VBoxServiceControlExecThread.cpp revision e90377a22a76ec3a527b71b4fceacc0ae83889ac
/* $Id$ */
/** @file
* VBoxServiceControlExecThread - Thread for an executed guest process.
*/
/*
* Copyright (C) 2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/semaphore.h>
#include "VBoxServicePipeBuf.h"
#include "VBoxServiceControlExecThread.h"
extern uint32_t g_GuestControlProcsMaxKept;
extern RTLISTNODE g_GuestControlThreads;
/**
* Allocates and gives back a thread data struct which then can be used by the worker thread.
* Needs to be freed with VBoxServiceControlExecDestroyThreadData().
*
* @return IPRT status code.
* @param pThread The thread's handle to allocate the data for.
* @param u32ContextID The context ID bound to this request / command.
* @param pszCmd Full qualified path of process to start (without arguments).
* @param uFlags Process execution flags.
* @param pszArgs String of arguments to pass to the process to start.
* @param uNumArgs Number of arguments specified in pszArgs.
* @param pszEnv String of environment variables ("FOO=BAR") to pass to the process
* to start.
* @param cbEnv Size (in bytes) of environment variables.
* @param uNumEnvVars Number of environment variables specified in pszEnv.
* @param pszUser User name (account) to start the process under.
* @param pszPassword Password of specified user name (account).
* @param uTimeLimitMS Time limit (in ms) of the process' life time.
*/
{
/* General stuff. */
/* ClientID will be assigned when thread is started! */
/* Specific stuff. */
PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREADDATAEXEC));
return VERR_NO_MEMORY;
pData->uNumEnvVars = 0;
/* Prepare argument list. */
/* Did we get the same result? */
if (RT_SUCCESS(rc))
{
/* Prepare environment list. */
if (uNumEnvVars)
{
uint32_t i = 0;
{
/* sanity check */
if (i >= uNumEnvVars)
{
break;
}
if (cbStr < 0)
{
break;
}
}
}
/* Adjust time limit value. */
|| uTimeLimitMS == 0)
/* Init buffers. */
false /*fNeedNotificationPipe*/);
if (RT_SUCCESS(rc))
{
false /*fNeedNotificationPipe*/);
if (RT_SUCCESS(rc))
true /*fNeedNotificationPipe*/);
}
if (RT_SUCCESS(rc))
{
}
}
if (RT_FAILURE(rc))
return rc;
}
/**
* Applies the policies to all guest execution threads.
*
* @return IPRT status code.
*/
{
if (RT_SUCCESS(rc))
{
/*
* Check if we're respecting our memory policy by checking
* how many guest processes are started and served already.
*/
if (g_GuestControlProcsMaxKept) /* If we allow unlimited processes, take a shortcut. */
{
{
uNumProcs++;
}
/*
* Do we serve more guest processes than we should? Kill the oldest n ones (=beginning
* at the list).
*/
uint32_t uProcsToKill = 0;
if (uProcsToKill)
{
VBoxServiceVerbose(2, "Control: Maximum guest processes set to %u, running %u, killing %u oldest one(s) ...\n",
{
if (RT_FAILURE(rc))
{
/* Keep going. */
}
/* Did we fullfill our rate? Then stop killing innocent guest processes. */
if (--uProcsToKill == 0)
break;
}
}
}
if (RT_SUCCESS(rc))
}
return rc;
}
/**
* Assigns a valid PID to a guest control thread and also checks if there already was
* another (stale) guest process which was using that PID before and destroys it.
*
* @return IPRT status code.
* @param pData Pointer to guest control execution thread data.
* @param uPID PID to assign to the specified guest control execution thread.
*/
{
if (RT_SUCCESS(rc))
{
/* Search an old thread using the desired PID and shut it down completely -- it's
* not used anymore. */
if ( pOldNode
{
uPID);
if (RT_FAILURE(rc))
{
/* Keep going. */
}
}
/* Assign PID to current thread. */
if (RT_SUCCESS(rc))
}
return rc;
}
/**
* Frees an allocated thread data structure along with all its allocated parameters.
*
* @param pData Pointer to thread data to free.
*/
{
if (pData)
{
if (pData->uNumEnvVars)
{
}
}
}
/**
* Finds a (formerly) started process given by its PID.
* Internal function, does not do locking -- this must be done from the caller function!
*
* @return PVBOXSERVICECTRLTHREAD Process structure if found, otherwise NULL.
* @param uPID PID to search for.
*/
{
{
{
return pNode;
}
}
return NULL;
}
/**
* Injects input to a specified running process.
*
* @return IPRT status code.
* @param uPID PID of process to set the input for.
* @param fPendingClose Flag indicating whether this is the last input block sent to the process.
* @param pBuf Pointer to a buffer containing the actual input data.
* @param cbSize Size (in bytes) of the input buffer data.
* @param pcbWritten Pointer to number of bytes written to the process. Optional.
*/
{
if (RT_SUCCESS(rc))
{
if (pNode)
{
{
/*
* Feed the data to the pipe.
*/
if (pcbWritten)
*pcbWritten = cbWritten;
}
else
{
/* If input buffer is not enabled anymore we cannot handle that data ... */
rc = VERR_BAD_PIPE;
}
}
else
}
return rc;
}
/**
*
* @return IPRT status code.
* @param uPID PID of process to retrieve the output from.
* @param uHandleId Stream ID (stdout = 0, stderr = 2) to get the output from.
* @param uTimeout Timeout (in ms) to wait for output becoming available.
* @param pBuf Pointer to a pre-allocated buffer to store the output.
* @param cbSize Size (in bytes) of the pre-allocated buffer.
* @param pcbRead Pointer to number of bytes read. Optional.
*/
{
if (RT_SUCCESS(rc))
{
if (pNode)
{
switch (uHandleId)
{
case OUTPUT_HANDLE_ID_STDERR: /* StdErr */
break;
case OUTPUT_HANDLE_ID_STDOUT: /* StdOut */
break;
default:
break;
}
if (!pPipeBuf)
return VERR_INVALID_PARAMETER;
#ifdef DEBUG_andy
#endif
/* If the stdout pipe buffer is enabled (that is, still could be filled by a running
* process) wait for the signal to arrive so that we don't return without any actual
* data read. */
if (fEnabled)
{
#ifdef DEBUG_andy
#endif
}
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
AssertMsgFailed(("Waited for pipe buffer %u, but nothing read!\n",
if (pcbRead)
}
else
VBoxServiceError("ControlExec: [PID %u]: Unable to read from pipe buffer %u, rc=%Rrc\n",
}
}
else
if (RT_SUCCESS(rc))
}
return rc;
}
{
{
if (!pData)
return VINF_SUCCESS;
}
}