VBoxServiceControlExecThread.cpp revision 68a95b86122c4c00bce18f0905444b1d15ff5935
5b281ba489ca18f0380d7efc7a5108b606cce449vboxsync * VBoxServiceControlExecThread - Thread for every started guest process.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Copyright (C) 2011 Oracle Corporation
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * available from http://www.virtualbox.org. This file is free software;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * you can redistribute it and/or modify it under the terms of the GNU
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * General Public License (GPL) as published by the Free Software
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/*******************************************************************************
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync* Header Files *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync*******************************************************************************/
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncusing namespace guestControl;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/* Internal functions. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncstatic int vboxServiceControlThreadAssignPID(PVBOXSERVICECTRLTHREAD pData, uint32_t uPID);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncstatic void vboxServiceControlThreadFree(PVBOXSERVICECTRLTHREAD pData);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncstatic int vboxServiceControlThreadShutdown(PVBOXSERVICECTRLTHREAD pThread);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Allocates and gives back a thread data struct which then can be used by the worker thread.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Needs to be freed with VBoxServiceControlExecDestroyThreadData().
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @return IPRT status code.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pThread The thread's handle to allocate the data for.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param u32ContextID The context ID bound to this request / command.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pszCmd Full qualified path of process to start (without arguments).
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param uFlags Process execution flags.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pszArgs String of arguments to pass to the process to start.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param uNumArgs Number of arguments specified in pszArgs.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pszEnv String of environment variables ("FOO=BAR") to pass to the process
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * to start.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param cbEnv Size (in bytes) of environment variables.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param uNumEnvVars Number of environment variables specified in pszEnv.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pszUser User name (account) to start the process under.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pszPassword Password of specified user name (account).
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param uTimeLimitMS Time limit (in ms) of the process' life time.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncint VBoxServiceControlExecThreadAlloc(PVBOXSERVICECTRLTHREAD pThread,
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /* General stuff. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /* ClientID will be assigned when thread is started; every guest
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * process has its own client ID to detect crashes on a per-guest-process
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * level. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync rc = RTSemEventMultiCreate(&pThread->RequestEvent);
0f77dc54d7ec617480988ccdfcd080f480e79698vboxsync pThread->uTimeLimitMS = ( uTimeLimitMS == UINT32_MAX
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /* Prepare argument list. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync pThread->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
004d34f82d0e8a4d58a65cc50eacf298bc6eb956vboxsync rc = RTGetOptArgvFromString(&pThread->papszArgs, (int*)&pThread->uNumArgs,
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /* Did we get the same result? */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /* Prepare environment list. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync pThread->papszEnv = (char **)RTMemAlloc(uNumEnvVars * sizeof(char*));
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /* sanity check */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync int cbStr = RTStrAPrintf(&pThread->papszEnv[i++], "%s", pszCur);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /* User management. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Signals a guest process thread that we want it to shut down in
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * a gentle way.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @return IPRT status code.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pThread Thread to shut down.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncint VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync VBoxServiceVerbose(3, "ControlThead: [PID %u]: Signalling shutdown ...\n",
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /* Do *not* set pThread->fShutdown or other stuff here!
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * The guest thread loop will do that as soon as it processes the quit message. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync int rc = VBoxServiceControlThreadPerform(pThread->uPID, &ctrlRequest);
004d34f82d0e8a4d58a65cc50eacf298bc6eb956vboxsync VBoxServiceVerbose(3, "ControlThead: [PID %u]: Sending quit request failed with rc=%Rrc\n",
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Wait for a guest process thread to shut down.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @return IPRT status code.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pThread Thread to wait shutting down for.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param RTMSINTERVAL Timeout in ms to wait for shutdown.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncint VBoxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread,
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync && !ASMAtomicReadBool(&pThread->fStopped)) /* Only shutdown threads which aren't yet. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync VBoxServiceVerbose(3, "ControlThead: [PID %u]: Waiting for shutdown ...\n",
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /* Wait a bit ... */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync rc = RTThreadWait(pThread->Thread, msTimeout, &rcThread);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync VBoxServiceError("ControlThead: [PID %u]: Shutdown returned error rc=%Rrc\n",
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Frees an allocated thread data structure along with all its allocated parameters.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pThread Pointer to thread data to free.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncstatic void vboxServiceControlThreadFree(const PVBOXSERVICECTRLTHREAD pThread)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync VBoxServiceVerbose(3, "ControlThead: [PID %u]: Freeing thread data ...\n",
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync for (uint32_t i = 0; i < pThread->uNumEnvVars; i++)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync int rc2 = RTSemEventMultiDestroy(pThread->RequestEvent);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Closes the stdin pipe of a guest process.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @return IPRT status code.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param hPollSet The polling set.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param phStdInW The standard input pipe handle.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncstatic int VBoxServiceControlThreadCloseStdIn(RTPOLLSET hPollSet, PRTPIPE phStdInW)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync int rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN);
static int VBoxServiceControlThreadHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW)
#ifdef DEBUG_andy
return rc;
#ifdef DEBUG_andy
return rc;
int rc;
int rc2;
bool fProcessAlive = true;
bool fProcessTimedOut = false;
return rc;
switch (idPollHnd)
if (fProcessAlive)
fProcessAlive = false;
fProcessAlive = false;
AssertFailed();
if (!fProcessAlive)
VBoxServiceVerbose(3, "ControlThead: [PID %u]: Timed out (%ums elapsed > %ums timeout), killing ...",
fProcessTimedOut = true;
if (fProcessAlive)
fProcessAlive = false;
if (fProcessAlive)
VBoxServiceVerbose(3, "ControlThead: [PID %u]: Got terminated because system/service is about to shutdown\n",
else if (fProcessAlive)
VBoxServiceVerbose(3, "ControlThead: [PID %u]: Sending final status, ClientID=%u, CID=%u, Status=%u, Flags=0x%x\n",
return rc;
int rc;
return rc;
return rc;
static int VBoxServiceControlThreadMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded)
#ifdef RT_OS_WINDOWS
#ifdef DEBUG
return rc;
static int VBoxServiceControlThreadResolveExecutable(const char *pszFileName, char *pszResolved, size_t cbResolved)
#ifdef DEBUG
return rc;
if (pszArgv0)
&& papszArgs)
char *pszArgs;
int iNumArgsIgnored;
if (pszNewArgs)
return rc;
static int VBoxServiceControlThreadCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
#ifdef RT_OS_WINDOWS
char **papszArgsExp;
return rc;
#ifdef VBOXSERVICE_TOOLBOX
#ifdef VBOXSERVICE_TOOLBOX
char **papszArgsExp;
rc = VBoxServiceControlThreadPrepareArgv(pszExec /* Always use the unmodified executable name as argv0. */,
if (fFlags)
if (*pszAsUser)
#ifdef DEBUG
return rc;
VBoxServiceError("ControlThead: Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc);
return rc;
size_t i;
/** @todo consider supporting: gcc stuff.c >file 2>&1. */
rc = VBoxServiceControlThreadSetupPipe(0 /*STDIN_FILENO*/, &hStdIn, &phStdIn, &pThread->pipeStdInW);
rc = RTPollSetAddPipe(hPollSet, pThread->pipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN);
rc = RTPollSetAddPipe(hPollSet, pThread->hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_IPC_NOTIFY);
rc = VBoxServiceControlThreadCreateProcess(pThread->pszCmd, pThread->papszArgs, hEnv, pThread->uFlags,
&hProcess);
fSignalled = true;
VBoxServiceError("ControlThead: Could not report process start error! Error: %Rrc (process error %Rrc)\n",
return rc;
if (!pThread)
return VERR_NO_MEMORY;
return rc;
if ( pOldThread
VBoxServiceVerbose(3, "ControlThead: PID %u was used before, shutting down stale exec thread ...\n",
uPID);
return rc;
int rc;
if ( pThread
VBoxServiceVerbose(4, "ControlThead: [PID %u]: Waiting for response on enmType=%u, pvData=0x%p, cbData=%u\n",
VBoxServiceVerbose(4, "ControlThead: Performed PID=%u, enmType=%u, pvData=0x%p, cbData=%u with rc=%Rrc\n",
return rc;
return rc;