VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlProcess.cpp

Last change on this file was 99120, checked in by vboxsync, 14 months ago

Guest Control: Added ability of specifying an optional current working directory to started guest processes. This needs Guest Additions which support this. bugref:8053

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 86.6 KB
RevLine 
[36548]1/* $Id: VBoxServiceControlProcess.cpp 99120 2023-03-22 17:30:14Z vboxsync $ */
2/** @file
[44863]3 * VBoxServiceControlThread - Guest process handling.
[36548]4 */
5
6/*
[98103]7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
[36548]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[36548]26 */
27
28
[57358]29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
[38180]32#include <iprt/asm.h>
[36548]33#include <iprt/assert.h>
[39279]34#include <iprt/env.h>
[39434]35#include <iprt/file.h>
[36548]36#include <iprt/getopt.h>
[39279]37#include <iprt/handle.h>
[36548]38#include <iprt/mem.h>
[39279]39#include <iprt/path.h>
[36548]40#include <iprt/pipe.h>
[39279]41#include <iprt/poll.h>
42#include <iprt/process.h>
[36548]43#include <iprt/semaphore.h>
44#include <iprt/string.h>
[83400]45#include <iprt/string.h>
[96502]46#include <iprt/system.h>
[39279]47#include <iprt/thread.h>
[36548]48
[39279]49#include <VBox/VBoxGuestLib.h>
[36887]50#include <VBox/HostServices/GuestControlSvc.h>
51
[39279]52#include "VBoxServiceInternal.h"
[44863]53#include "VBoxServiceControl.h"
[60622]54#include "VBoxServiceToolBox.h"
[36548]55
[39279]56using namespace guestControl;
[36548]57
[57358]58
59/*********************************************************************************************************************************
60* Internal Functions *
61*********************************************************************************************************************************/
[58029]62static int vgsvcGstCtrlProcessAssignPID(PVBOXSERVICECTRLPROCESS pThread, uint32_t uPID);
63static int vgsvcGstCtrlProcessLock(PVBOXSERVICECTRLPROCESS pProcess);
64static int vgsvcGstCtrlProcessSetupPipe(const char *pszHowTo, int fd, PRTHANDLE ph, PRTHANDLE *pph,
65 PRTPIPE phPipe);
66static int vgsvcGstCtrlProcessUnlock(PVBOXSERVICECTRLPROCESS pProcess);
[47545]67/* Request handlers. */
[58029]68static DECLCALLBACK(int) vgsvcGstCtrlProcessOnInput(PVBOXSERVICECTRLPROCESS pThis, const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
69 bool fPendingClose, void *pvBuf, uint32_t cbBuf);
70static DECLCALLBACK(int) vgsvcGstCtrlProcessOnOutput(PVBOXSERVICECTRLPROCESS pThis, const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
71 uint32_t uHandle, uint32_t cbToRead, uint32_t uFlags);
[36548]72
[58029]73
[84147]74
75/**
[39427]76 * Initialies the passed in thread data structure with the parameters given.
[36548]77 *
78 * @return IPRT status code.
[45010]79 * @param pProcess Process to initialize.
80 * @param pSession Guest session the process is bound to.
81 * @param pStartupInfo Startup information.
[36548]82 * @param u32ContextID The context ID bound to this request / command.
83 */
[58029]84static int vgsvcGstCtrlProcessInit(PVBOXSERVICECTRLPROCESS pProcess,
85 const PVBOXSERVICECTRLSESSION pSession,
[84215]86 const PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo,
[58029]87 uint32_t u32ContextID)
[36548]88{
[39906]89 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
[45010]90 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
91 AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER);
[36548]92
93 /* General stuff. */
[47545]94 pProcess->hProcess = NIL_RTPROCESS;
[45010]95 pProcess->pSession = pSession;
96 pProcess->Node.pPrev = NULL;
97 pProcess->Node.pNext = NULL;
[36548]98
[47545]99 pProcess->fShutdown = false;
100 pProcess->fStarted = false;
101 pProcess->fStopped = false;
[36548]102
[47545]103 pProcess->uPID = 0; /* Don't have a PID yet. */
104 pProcess->cRefs = 0;
105 /*
106 * Use the initial context ID we got for starting
107 * the process to report back its status with the
108 * same context ID.
109 */
[45010]110 pProcess->uContextID = u32ContextID;
[47545]111 /*
112 * Note: pProcess->ClientID will be assigned when thread is started;
113 * every guest process has its own client ID to detect crashes on
114 * a per-guest-process level.
115 */
[36548]116
[45010]117 int rc = RTCritSectInit(&pProcess->CritSect);
[39279]118 if (RT_FAILURE(rc))
119 return rc;
[36548]120
[47545]121 pProcess->hPollSet = NIL_RTPOLLSET;
122 pProcess->hPipeStdInW = NIL_RTPIPE;
123 pProcess->hPipeStdOutR = NIL_RTPIPE;
124 pProcess->hPipeStdErrR = NIL_RTPIPE;
125 pProcess->hNotificationPipeW = NIL_RTPIPE;
126 pProcess->hNotificationPipeR = NIL_RTPIPE;
[39279]127
[47545]128 rc = RTReqQueueCreate(&pProcess->hReqQueue);
129 AssertReleaseRC(rc);
130
[84147]131 /* Duplicate startup info. */
[84215]132 pProcess->pStartupInfo = VbglR3GuestCtrlProcStartupInfoDup(pStartupInfo);
[84147]133 AssertPtrReturn(pProcess->pStartupInfo, VERR_NO_MEMORY);
[36548]134
[45415]135 /* Adjust timeout value. */
[84147]136 if ( pProcess->pStartupInfo->uTimeLimitMS == UINT32_MAX
137 || pProcess->pStartupInfo->uTimeLimitMS == 0)
138 pProcess->pStartupInfo->uTimeLimitMS = RT_INDEFINITE_WAIT;
[36548]139
[39279]140 if (RT_FAILURE(rc)) /* Clean up on failure. */
[58029]141 VGSvcGstCtrlProcessFree(pProcess);
[39279]142 return rc;
143}
[36548]144
[39279]145
146/**
[47545]147 * Frees a guest process. On success, pProcess will be
148 * free'd and thus won't be available anymore.
[39418]149 *
150 * @return IPRT status code.
[45010]151 * @param pProcess Guest process to free.
[83286]152 * The pointer will not be valid anymore after return.
[39418]153 */
[58029]154int VGSvcGstCtrlProcessFree(PVBOXSERVICECTRLPROCESS pProcess)
[39418]155{
[45010]156 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
[39418]157
[83286]158 int rc = RTCritSectEnter(&pProcess->CritSect);
159 if (RT_SUCCESS(rc))
160 {
161 VGSvcVerbose(3, "[PID %RU32]: Freeing (cRefs=%RU32)...\n", pProcess->uPID, pProcess->cRefs);
[39418]162
[83286]163 AssertReturn(pProcess->cRefs == 0, VERR_WRONG_ORDER);
164 AssertReturn(pProcess->fStopped, VERR_WRONG_ORDER);
165 AssertReturn(pProcess->fShutdown, VERR_WRONG_ORDER);
[39418]166
[84215]167 VbglR3GuestCtrlProcStartupInfoFree(pProcess->pStartupInfo);
[84147]168 pProcess->pStartupInfo = NULL;
169
[83286]170 /*
171 * Destroy other thread data.
172 */
[83287]173 rc = RTPollSetDestroy(pProcess->hPollSet);
[83286]174 AssertRC(rc);
[47545]175
[83286]176 rc = RTReqQueueDestroy(pProcess->hReqQueue);
177 AssertRC(rc);
[47545]178
[83286]179 rc = RTPipeClose(pProcess->hNotificationPipeR);
180 AssertRC(rc);
181 rc = RTPipeClose(pProcess->hNotificationPipeW);
182 AssertRC(rc);
[39418]183
[83286]184 rc = RTPipeClose(pProcess->hPipeStdInW);
185 AssertRC(rc);
186 rc = RTPipeClose(pProcess->hPipeStdErrR);
187 AssertRC(rc);
188 rc = RTPipeClose(pProcess->hPipeStdOutR);
189 AssertRC(rc);
190
191 rc = RTCritSectLeave(&pProcess->CritSect);
192 AssertRC(rc);
193
194 RTCritSectDelete(&pProcess->CritSect);
195
196 /*
197 * Destroy thread structure as final step.
198 */
199 RTMemFree(pProcess);
200 pProcess = NULL;
201 }
202
203 return rc;
[39418]204}
205
206
207/**
[39300]208 * Signals a guest process thread that we want it to shut down in
209 * a gentle way.
[39279]210 *
[39300]211 * @return IPRT status code.
[45415]212 * @param pProcess Process to stop.
[39279]213 */
[58029]214int VGSvcGstCtrlProcessStop(PVBOXSERVICECTRLPROCESS pProcess)
[39279]215{
[45415]216 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
[39279]217
[58029]218 VGSvcVerbose(3, "[PID %RU32]: Stopping ...\n", pProcess->uPID);
[39279]219
[45697]220 /* Do *not* set pThread->fShutdown or other stuff here!
[47545]221 * The guest thread loop will clean up itself. */
[45697]222
[58029]223 return VGSvcGstCtrlProcessHandleTerm(pProcess);
[39279]224}
225
226
[39300]227/**
[47545]228 * Releases a previously acquired guest process (decreases the refcount).
[44963]229 *
[83286]230 * @param pProcess Process to release.
[44963]231 */
[58029]232void VGSvcGstCtrlProcessRelease(PVBOXSERVICECTRLPROCESS pProcess)
[44963]233{
[45415]234 AssertPtrReturnVoid(pProcess);
[44963]235
[83286]236 int rc2 = RTCritSectEnter(&pProcess->CritSect);
237 if (RT_SUCCESS(rc2))
[45697]238 {
[83286]239 AssertReturnVoid(pProcess->cRefs);
[45697]240 pProcess->cRefs--;
[47545]241
[83286]242 VGSvcVerbose(3, "[PID %RU32]: cRefs=%RU32, fShutdown=%RTbool, fStopped=%RTbool\n",
243 pProcess->uPID, pProcess->cRefs, pProcess->fShutdown, pProcess->fStopped);
244
245 rc2 = RTCritSectLeave(&pProcess->CritSect);
246 AssertRC(rc2);
[45697]247 }
[44963]248}
249
250
251/**
[39300]252 * Wait for a guest process thread to shut down.
253 *
254 * @return IPRT status code.
[45415]255 * @param pProcess Process to wait shutting down for.
[58089]256 * @param msTimeout Timeout in ms to wait for shutdown.
[58029]257 * @param prc Where to store the thread's return code.
258 * Optional.
[39300]259 */
[58029]260int VGSvcGstCtrlProcessWait(const PVBOXSERVICECTRLPROCESS pProcess, RTMSINTERVAL msTimeout, int *prc)
[39279]261{
[45415]262 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
[58029]263 AssertPtrNullReturn(prc, VERR_INVALID_POINTER);
[40682]264
[58029]265 int rc = vgsvcGstCtrlProcessLock(pProcess);
[84873]266 if (RT_SUCCESS(rc))
[47545]267 {
[84873]268 if (RTThreadGetState(pProcess->Thread) != RTTHREADSTATE_INVALID) /* Is there a thread we can wait for? */
269 {
270 VGSvcVerbose(2, "[PID %RU32]: Waiting for shutdown (%RU32ms) ...\n", pProcess->uPID, msTimeout);
[45604]271
[84873]272 AssertMsgReturn(pProcess->fStarted,
273 ("Tried to wait on guest process=%p (PID %RU32) which has not been started yet\n",
274 pProcess, pProcess->uPID), VERR_INVALID_PARAMETER);
[45604]275
[84873]276 /* Unlock process before waiting. */
277 rc = vgsvcGstCtrlProcessUnlock(pProcess);
278 AssertRC(rc);
[83286]279
[84873]280 /* Do the actual waiting. */
281 int rcThread;
282 Assert(pProcess->Thread != NIL_RTTHREAD);
283 rc = RTThreadWait(pProcess->Thread, msTimeout, &rcThread);
[83286]284
[84873]285 int rc2 = vgsvcGstCtrlProcessLock(pProcess);
286 AssertRC(rc2);
[83286]287
[84873]288 if (RT_SUCCESS(rc))
289 {
290 pProcess->Thread = NIL_RTTHREAD;
291 VGSvcVerbose(3, "[PID %RU32]: Thread shutdown complete, thread rc=%Rrc\n", pProcess->uPID, rcThread);
292 if (prc)
293 *prc = rcThread;
294 }
[47545]295 }
296
[84873]297 int rc2 = vgsvcGstCtrlProcessUnlock(pProcess);
[83286]298 AssertRC(rc2);
[39279]299 }
[45415]300
[84873]301 if (RT_FAILURE(rc))
302 VGSvcError("[PID %RU32]: Waiting for shutting down thread returned error rc=%Rrc\n", pProcess->uPID, rc);
303
[58029]304 VGSvcVerbose(3, "[PID %RU32]: Waiting resulted in rc=%Rrc\n", pProcess->uPID, rc);
[39279]305 return rc;
306}
307
308
309/**
310 * Closes the stdin pipe of a guest process.
311 *
312 * @return IPRT status code.
[58089]313 * @param pProcess The process which input pipe we close.
[39279]314 * @param phStdInW The standard input pipe handle.
315 */
[58029]316static int vgsvcGstCtrlProcessPollsetCloseInput(PVBOXSERVICECTRLPROCESS pProcess, PRTPIPE phStdInW)
[39279]317{
[47545]318 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
[39462]319 AssertPtrReturn(phStdInW, VERR_INVALID_POINTER);
320
[47545]321 int rc = RTPollSetRemove(pProcess->hPollSet, VBOXSERVICECTRLPIPEID_STDIN);
[39279]322 if (rc != VERR_POLL_HANDLE_ID_NOT_FOUND)
323 AssertRC(rc);
324
[39462]325 if (*phStdInW != NIL_RTPIPE)
326 {
327 rc = RTPipeClose(*phStdInW);
328 AssertRC(rc);
329 *phStdInW = NIL_RTPIPE;
330 }
[39279]331
332 return rc;
333}
334
335
[62882]336#ifdef DEBUG
[58029]337/**
338 * Names a poll handle ID.
339 *
340 * @returns Pointer to read-only string.
341 * @param idPollHnd What to name.
342 */
343static const char *vgsvcGstCtrlProcessPollHandleToString(uint32_t idPollHnd)
[45697]344{
345 switch (idPollHnd)
346 {
347 case VBOXSERVICECTRLPIPEID_UNKNOWN:
348 return "unknown";
349 case VBOXSERVICECTRLPIPEID_STDIN:
350 return "stdin";
351 case VBOXSERVICECTRLPIPEID_STDIN_WRITABLE:
352 return "stdin_writable";
353 case VBOXSERVICECTRLPIPEID_STDOUT:
354 return "stdout";
355 case VBOXSERVICECTRLPIPEID_STDERR:
356 return "stderr";
357 case VBOXSERVICECTRLPIPEID_IPC_NOTIFY:
358 return "ipc_notify";
359 default:
[58029]360 return "unknown";
[45697]361 }
362}
[62882]363#endif /* DEBUG */
[45697]364
365
[39279]366/**
367 * Handle an error event on standard input.
368 *
369 * @return IPRT status code.
[47545]370 * @param pProcess Process to handle pollset for.
[39279]371 * @param fPollEvt The event mask returned by RTPollNoResume.
372 * @param phStdInW The standard input pipe handle.
373 */
[58029]374static int vgsvcGstCtrlProcessPollsetOnInput(PVBOXSERVICECTRLPROCESS pProcess, uint32_t fPollEvt, PRTPIPE phStdInW)
[39279]375{
[47545]376 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
377
[39279]378 NOREF(fPollEvt);
379
[58029]380 return vgsvcGstCtrlProcessPollsetCloseInput(pProcess, phStdInW);
[39279]381}
382
383
384/**
385 * Handle pending output data or error on standard out or standard error.
386 *
387 * @returns IPRT status code from client send.
[47545]388 * @param pProcess Process to handle pollset for.
[39279]389 * @param fPollEvt The event mask returned by RTPollNoResume.
390 * @param phPipeR The pipe handle.
391 * @param idPollHnd The pipe ID to handle.
392 */
[58029]393static int vgsvcGstCtrlProcessHandleOutputError(PVBOXSERVICECTRLPROCESS pProcess,
394 uint32_t fPollEvt, PRTPIPE phPipeR, uint32_t idPollHnd)
[39279]395{
[62850]396 RT_NOREF1(fPollEvt);
[47545]397 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
[39279]398
[47545]399 if (!phPipeR)
400 return VINF_SUCCESS;
401
[39843]402#ifdef DEBUG
[58029]403 VGSvcVerbose(4, "[PID %RU32]: Output error: idPollHnd=%s, fPollEvt=0x%x\n",
404 pProcess->uPID, vgsvcGstCtrlProcessPollHandleToString(idPollHnd), fPollEvt);
[39843]405#endif
[39279]406
[39843]407 /* Remove pipe from poll set. */
[47545]408 int rc2 = RTPollSetRemove(pProcess->hPollSet, idPollHnd);
[39843]409 AssertMsg(RT_SUCCESS(rc2) || rc2 == VERR_POLL_HANDLE_ID_NOT_FOUND, ("%Rrc\n", rc2));
[39279]410
[39843]411 bool fClosePipe = true; /* By default close the pipe. */
412
413 /* Check if there's remaining data to read from the pipe. */
[47545]414 if (*phPipeR != NIL_RTPIPE)
[39843]415 {
[47545]416 size_t cbReadable;
417 rc2 = RTPipeQueryReadable(*phPipeR, &cbReadable);
418 if ( RT_SUCCESS(rc2)
419 && cbReadable)
420 {
421#ifdef DEBUG
[58029]422 VGSvcVerbose(3, "[PID %RU32]: idPollHnd=%s has %zu bytes left, vetoing close\n",
423 pProcess->uPID, vgsvcGstCtrlProcessPollHandleToString(idPollHnd), cbReadable);
[47545]424#endif
425 /* Veto closing the pipe yet because there's still stuff to read
426 * from the pipe. This can happen on UNIX-y systems where on
427 * error/hangup there still can be data to be read out. */
428 fClosePipe = false;
429 }
[39843]430 }
[47545]431#ifdef DEBUG
[39843]432 else
[58029]433 VGSvcVerbose(3, "[PID %RU32]: idPollHnd=%s will be closed\n",
434 pProcess->uPID, vgsvcGstCtrlProcessPollHandleToString(idPollHnd));
[47545]435#endif
[39843]436
437 if ( *phPipeR != NIL_RTPIPE
438 && fClosePipe)
439 {
440 rc2 = RTPipeClose(*phPipeR);
441 AssertRC(rc2);
442 *phPipeR = NIL_RTPIPE;
443 }
444
445 return VINF_SUCCESS;
[39418]446}
[39279]447
448
[39418]449/**
450 * Handle pending output data or error on standard out or standard error.
451 *
452 * @returns IPRT status code from client send.
[47545]453 * @param pProcess Process to handle pollset for.
[39418]454 * @param fPollEvt The event mask returned by RTPollNoResume.
455 * @param phPipeR The pipe handle.
456 * @param idPollHnd The pipe ID to handle.
457 *
458 */
[58029]459static int vgsvcGstCtrlProcessPollsetOnOutput(PVBOXSERVICECTRLPROCESS pProcess,
460 uint32_t fPollEvt, PRTPIPE phPipeR, uint32_t idPollHnd)
[39418]461{
[47545]462 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
463
464#ifdef DEBUG
[58029]465 VGSvcVerbose(4, "[PID %RU32]: Output event phPipeR=%p, idPollHnd=%s, fPollEvt=0x%x\n",
466 pProcess->uPID, phPipeR, vgsvcGstCtrlProcessPollHandleToString(idPollHnd), fPollEvt);
[39843]467#endif
468
[47545]469 if (!phPipeR)
470 return VINF_SUCCESS;
471
[39418]472 int rc = VINF_SUCCESS;
[39434]473
[39843]474#ifdef DEBUG
[47545]475 if (*phPipeR != NIL_RTPIPE)
[39279]476 {
[47545]477 size_t cbReadable;
478 rc = RTPipeQueryReadable(*phPipeR, &cbReadable);
479 if ( RT_SUCCESS(rc)
480 && cbReadable)
481 {
[58029]482 VGSvcVerbose(4, "[PID %RU32]: Output event cbReadable=%zu\n", pProcess->uPID, cbReadable);
[47545]483 }
[39843]484 }
485#endif
[39279]486
[39843]487#if 0
[47545]488 /* Push output to the host. */
489 if (fPollEvt & RTPOLL_EVT_READ)
[39843]490 {
491 size_t cbRead = 0;
492 uint8_t byData[_64K];
[58029]493 rc = RTPipeRead(*phPipeR, byData, sizeof(byData), &cbRead);
494 VGSvcVerbose(4, "VGSvcGstCtrlProcessHandleOutputEvent cbRead=%u, rc=%Rrc\n", cbRead, rc);
[39843]495
[39434]496 /* Make sure we go another poll round in case there was too much data
497 for the buffer to hold. */
498 fPollEvt &= RTPOLL_EVT_ERROR;
[39279]499 }
[39843]500#endif
[39279]501
[39418]502 if (fPollEvt & RTPOLL_EVT_ERROR)
[58029]503 rc = vgsvcGstCtrlProcessHandleOutputError(pProcess, fPollEvt, phPipeR, idPollHnd);
[39279]504 return rc;
505}
506
507
[44863]508/**
[39279]509 * Execution loop which runs in a dedicated per-started-process thread and
510 * handles all pipe input/output and signalling stuff.
[38157]511 *
512 * @return IPRT status code.
[45415]513 * @param pProcess The guest process to handle.
[38157]514 */
[58029]515static int vgsvcGstCtrlProcessProcLoop(PVBOXSERVICECTRLPROCESS pProcess)
[38113]516{
[45415]517 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
[38113]518
[39279]519 int rc;
520 int rc2;
[45415]521 uint64_t const uMsStart = RTTimeMilliTS();
[39279]522 RTPROCSTATUS ProcessStatus = { 254, RTPROCEXITREASON_ABEND };
523 bool fProcessAlive = true;
524 bool fProcessTimedOut = false;
525 uint64_t MsProcessKilled = UINT64_MAX;
[47545]526 RTMSINTERVAL const cMsPollBase = pProcess->hPipeStdInW != NIL_RTPIPE
[39279]527 ? 100 /* Need to poll for input. */
528 : 1000; /* Need only poll for process exit and aborts. */
529 RTMSINTERVAL cMsPollCur = 0;
530
531 /*
532 * Assign PID to thread data.
533 * Also check if there already was a thread with the same PID and shut it down -- otherwise
534 * the first (stale) entry will be found and we get really weird results!
535 */
[58029]536 rc = vgsvcGstCtrlProcessAssignPID(pProcess, pProcess->hProcess /* Opaque PID handle */);
[39279]537 if (RT_FAILURE(rc))
[38113]538 {
[58029]539 VGSvcError("Unable to assign PID=%u, to new thread, rc=%Rrc\n", pProcess->hProcess, rc);
[39279]540 return rc;
541 }
542
543 /*
544 * Before entering the loop, tell the host that we've started the guest
545 * and that it's now OK to send input to the process.
546 */
[58029]547 VGSvcVerbose(2, "[PID %RU32]: Process '%s' started, CID=%u, User=%s, cMsTimeout=%RU32\n",
[84147]548 pProcess->uPID, pProcess->pStartupInfo->pszCmd, pProcess->uContextID,
549 pProcess->pStartupInfo->pszUser, pProcess->pStartupInfo->uTimeLimitMS);
[84238]550 VBGLR3GUESTCTRLCMDCTX ctxStart = { g_idControlSvcClient, pProcess->uContextID, 0 /* uProtocol */, 0 /* uNumParms */ };
[45697]551 rc = VbglR3GuestCtrlProcCbStatus(&ctxStart,
[45415]552 pProcess->uPID, PROC_STS_STARTED, 0 /* u32Flags */,
[44863]553 NULL /* pvData */, 0 /* cbData */);
[51205]554 if (rc == VERR_INTERRUPTED)
555 rc = VINF_SUCCESS; /* SIGCHLD send by quick childs! */
[49349]556 if (RT_FAILURE(rc))
[58029]557 VGSvcError("[PID %RU32]: Error reporting starting status to host, rc=%Rrc\n", pProcess->uPID, rc);
[39279]558
559 /*
560 * Process input, output, the test pipe and client requests.
561 */
562 while ( RT_SUCCESS(rc)
[45415]563 && RT_UNLIKELY(!pProcess->fShutdown))
[39279]564 {
565 /*
566 * Wait/Process all pending events.
567 */
568 uint32_t idPollHnd;
569 uint32_t fPollEvt;
[47545]570 rc2 = RTPollNoResume(pProcess->hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
[45415]571 if (pProcess->fShutdown)
[39279]572 continue;
573
574 cMsPollCur = 0; /* No rest until we've checked everything. */
575
576 if (RT_SUCCESS(rc2))
[38113]577 {
[39279]578 switch (idPollHnd)
579 {
580 case VBOXSERVICECTRLPIPEID_STDIN:
[58029]581 rc = vgsvcGstCtrlProcessPollsetOnInput(pProcess, fPollEvt, &pProcess->hPipeStdInW);
[39279]582 break;
[38157]583
[39279]584 case VBOXSERVICECTRLPIPEID_STDOUT:
[58029]585 rc = vgsvcGstCtrlProcessPollsetOnOutput(pProcess, fPollEvt, &pProcess->hPipeStdOutR, idPollHnd);
[39279]586 break;
[45697]587
[39279]588 case VBOXSERVICECTRLPIPEID_STDERR:
[83272]589 rc = vgsvcGstCtrlProcessPollsetOnOutput(pProcess, fPollEvt, &pProcess->hPipeStdErrR, idPollHnd);
[39279]590 break;
591
592 case VBOXSERVICECTRLPIPEID_IPC_NOTIFY:
[45697]593#ifdef DEBUG_andy
[58029]594 VGSvcVerbose(4, "[PID %RU32]: IPC notify\n", pProcess->uPID);
[45697]595#endif
[58029]596 rc2 = vgsvcGstCtrlProcessLock(pProcess);
[47545]597 if (RT_SUCCESS(rc2))
[45697]598 {
[47545]599 /* Drain the notification pipe. */
600 uint8_t abBuf[8];
601 size_t cbIgnore;
[58029]602 rc2 = RTPipeRead(pProcess->hNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
[47545]603 if (RT_FAILURE(rc2))
[58029]604 VGSvcError("Draining IPC notification pipe failed with rc=%Rrc\n", rc2);
[49349]605
606 /* Process all pending requests. */
[58029]607 VGSvcVerbose(4, "[PID %RU32]: Processing pending requests ...\n", pProcess->uPID);
[47545]608 Assert(pProcess->hReqQueue != NIL_RTREQQUEUE);
609 rc2 = RTReqQueueProcess(pProcess->hReqQueue,
610 0 /* Only process all pending requests, don't wait for new ones */);
611 if ( RT_FAILURE(rc2)
612 && rc2 != VERR_TIMEOUT)
[58029]613 VGSvcError("Processing requests failed with with rc=%Rrc\n", rc2);
[45697]614
[58029]615 int rc3 = vgsvcGstCtrlProcessUnlock(pProcess);
[47545]616 AssertRC(rc3);
617#ifdef DEBUG
[58029]618 VGSvcVerbose(4, "[PID %RU32]: Processing pending requests done, rc=%Rrc\n", pProcess->uPID, rc2);
[47545]619#endif
[45697]620 }
[47545]621
[39279]622 break;
[43981]623
624 default:
625 AssertMsgFailed(("Unknown idPollHnd=%RU32\n", idPollHnd));
626 break;
[39279]627 }
[39300]628
[39279]629 if (RT_FAILURE(rc) || rc == VINF_EOF)
630 break; /* Abort command, or client dead or something. */
[45415]631 }
[47545]632#if 0
[58029]633 VGSvcVerbose(4, "[PID %RU32]: Polling done, pollRc=%Rrc, pollCnt=%RU32, idPollHnd=%s, rc=%Rrc, fProcessAlive=%RTbool, fShutdown=%RTbool\n",
634 pProcess->uPID, rc2, RTPollSetGetCount(hPollSet), vgsvcGstCtrlProcessPollHandleToString(idPollHnd), rc, fProcessAlive, pProcess->fShutdown);
635 VGSvcVerbose(4, "[PID %RU32]: stdOut=%s, stdErrR=%s\n",
636 pProcess->uPID,
637 *phStdOutR == NIL_RTPIPE ? "closed" : "open",
638 *phStdErrR == NIL_RTPIPE ? "closed" : "open");
[45415]639#endif
640 if (RT_UNLIKELY(pProcess->fShutdown))
641 break; /* We were asked to shutdown. */
[39300]642
[39279]643 /*
644 * Check for process death.
645 */
646 if (fProcessAlive)
647 {
[47545]648 rc2 = RTProcWaitNoResume(pProcess->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
[39279]649 if (RT_SUCCESS_NP(rc2))
[38157]650 {
[39279]651 fProcessAlive = false;
[47475]652 /* Note: Don't bail out here yet. First check in the next block below
653 * if all needed pipe outputs have been consumed. */
[38157]654 }
[47475]655 else
[39279]656 {
[47475]657 if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
658 continue;
659 if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
660 {
661 fProcessAlive = false;
662 ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
663 ProcessStatus.iStatus = 255;
664 AssertFailed();
665 }
666 else
667 AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
[39279]668 }
669 }
[38157]670
[39279]671 /*
[39843]672 * If the process has terminated and all output has been consumed,
673 * we should be heading out.
[39279]674 */
[47475]675 if (!fProcessAlive)
[45697]676 {
[47475]677 if ( fProcessTimedOut
[47545]678 || ( pProcess->hPipeStdOutR == NIL_RTPIPE
679 && pProcess->hPipeStdErrR == NIL_RTPIPE)
[47475]680 )
681 {
[59131]682 VGSvcVerbose(3, "[PID %RU32]: RTProcWaitNoResume=%Rrc\n", pProcess->uPID, rc2);
[47475]683 break;
684 }
[45697]685 }
[39279]686
687 /*
688 * Check for timed out, killing the process.
689 */
690 uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
[84147]691 if ( pProcess->pStartupInfo->uTimeLimitMS != RT_INDEFINITE_WAIT
692 && pProcess->pStartupInfo->uTimeLimitMS != 0)
[39279]693 {
694 uint64_t u64Now = RTTimeMilliTS();
[45415]695 uint64_t cMsElapsed = u64Now - uMsStart;
[84147]696 if (cMsElapsed >= pProcess->pStartupInfo->uTimeLimitMS)
[39279]697 {
698 fProcessTimedOut = true;
699 if ( MsProcessKilled == UINT64_MAX
700 || u64Now - MsProcessKilled > 1000)
701 {
702 if (u64Now - MsProcessKilled > 20*60*1000)
703 break; /* Give up after 20 mins. */
[47545]704
[58029]705 VGSvcVerbose(3, "[PID %RU32]: Timed out (%RU64ms elapsed > %RU32ms timeout), killing ...\n",
[84147]706 pProcess->uPID, cMsElapsed, pProcess->pStartupInfo->uTimeLimitMS);
[47545]707
708 rc2 = RTProcTerminate(pProcess->hProcess);
[58029]709 VGSvcVerbose(3, "[PID %RU32]: Killing process resulted in rc=%Rrc\n",
710 pProcess->uPID, rc2);
[39279]711 MsProcessKilled = u64Now;
712 continue;
713 }
714 cMilliesLeft = 10000;
715 }
716 else
[84147]717 cMilliesLeft = pProcess->pStartupInfo->uTimeLimitMS - (uint32_t)cMsElapsed;
[38113]718 }
719
[39279]720 /* Reset the polling interval since we've done all pending work. */
[39843]721 cMsPollCur = fProcessAlive
722 ? cMsPollBase
723 : RT_MS_1MIN;
724 if (cMilliesLeft < cMsPollCur)
725 cMsPollCur = cMilliesLeft;
[38113]726 }
727
[75858]728 VGSvcVerbose(3, "[PID %RU32]: Loop ended: rc=%Rrc, fShutdown=%RTbool, fProcessAlive=%RTbool, fProcessTimedOut=%RTbool, MsProcessKilled=%RU64 (%RX64)\n",
[58029]729 pProcess->uPID, rc, pProcess->fShutdown, fProcessAlive, fProcessTimedOut, MsProcessKilled, MsProcessKilled);
730 VGSvcVerbose(3, "[PID %RU32]: *phStdOutR=%s, *phStdErrR=%s\n",
731 pProcess->uPID,
732 pProcess->hPipeStdOutR == NIL_RTPIPE ? "closed" : "open",
733 pProcess->hPipeStdErrR == NIL_RTPIPE ? "closed" : "open");
[39843]734
[45697]735 /* Signal that this thread is in progress of shutting down. */
[58029]736 ASMAtomicWriteBool(&pProcess->fShutdown, true);
[39843]737
[39279]738 /*
[47545]739 * Try killing the process if it's still alive at this point.
[39279]740 */
741 if (fProcessAlive)
742 {
743 if (MsProcessKilled == UINT64_MAX)
744 {
[58029]745 VGSvcVerbose(2, "[PID %RU32]: Is still alive and not killed yet\n", pProcess->uPID);
[39279]746
747 MsProcessKilled = RTTimeMilliTS();
[47545]748 rc2 = RTProcTerminate(pProcess->hProcess);
749 if (rc2 == VERR_NOT_FOUND)
750 {
751 fProcessAlive = false;
752 }
753 else if (RT_FAILURE(rc2))
[59520]754 VGSvcError("[PID %RU32]: Killing process failed with rc=%Rrc\n", pProcess->uPID, rc2);
[39279]755 RTThreadSleep(500);
756 }
757
[47545]758 for (int i = 0; i < 10 && fProcessAlive; i++)
[39279]759 {
[58029]760 VGSvcVerbose(4, "[PID %RU32]: Kill attempt %d/10: Waiting to exit ...\n", pProcess->uPID, i + 1);
[47545]761 rc2 = RTProcWait(pProcess->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
[39279]762 if (RT_SUCCESS(rc2))
763 {
[58029]764 VGSvcVerbose(4, "[PID %RU32]: Kill attempt %d/10: Exited\n", pProcess->uPID, i + 1);
[39279]765 fProcessAlive = false;
766 break;
767 }
768 if (i >= 5)
769 {
[58029]770 VGSvcVerbose(4, "[PID %RU32]: Kill attempt %d/10: Trying to terminate ...\n", pProcess->uPID, i + 1);
[47545]771 rc2 = RTProcTerminate(pProcess->hProcess);
772 if ( RT_FAILURE(rc)
773 && rc2 != VERR_NOT_FOUND)
[58029]774 VGSvcError("PID %RU32]: Killing process failed with rc=%Rrc\n",
[47545]775 pProcess->uPID, rc2);
[39279]776 }
777 RTThreadSleep(i >= 5 ? 2000 : 500);
778 }
779
780 if (fProcessAlive)
[58029]781 VGSvcError("[PID %RU32]: Could not be killed\n", pProcess->uPID);
[45697]782 }
[44863]783
[47545]784 /*
785 * Shutdown procedure:
786 * - Set the pProcess->fShutdown indicator to let others know we're
787 * not accepting any new requests anymore.
788 * - After setting the indicator, try to process all outstanding
789 * requests to make sure they're getting delivered.
790 *
791 * Note: After removing the process from the session's list it's not
792 * even possible for the session anymore to control what's
793 * happening to this thread, so be careful and don't mess it up.
794 */
795
[58029]796 rc2 = vgsvcGstCtrlProcessLock(pProcess);
[45697]797 if (RT_SUCCESS(rc2))
798 {
[58029]799 VGSvcVerbose(3, "[PID %RU32]: Processing outstanding requests ...\n", pProcess->uPID);
[45697]800
[47545]801 /* Process all pending requests (but don't wait for new ones). */
802 Assert(pProcess->hReqQueue != NIL_RTREQQUEUE);
803 rc2 = RTReqQueueProcess(pProcess->hReqQueue, 0 /* No timeout */);
804 if ( RT_FAILURE(rc2)
805 && rc2 != VERR_TIMEOUT)
[58029]806 VGSvcError("[PID %RU32]: Processing outstanding requests failed with with rc=%Rrc\n", pProcess->uPID, rc2);
[45697]807
[58029]808 VGSvcVerbose(3, "[PID %RU32]: Processing outstanding requests done, rc=%Rrc\n", pProcess->uPID, rc2);
[47545]809
[58029]810 rc2 = vgsvcGstCtrlProcessUnlock(pProcess);
[45697]811 AssertRC(rc2);
[39279]812 }
813
814 /*
815 * If we don't have a client problem (RT_FAILURE(rc)) we'll reply to the
816 * clients exec packet now.
817 */
818 if (RT_SUCCESS(rc))
819 {
820 uint32_t uStatus = PROC_STS_UNDEFINED;
[58029]821 uint32_t fFlags = 0;
[39279]822
823 if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX)
824 {
[58029]825 VGSvcVerbose(3, "[PID %RU32]: Timed out and got killed\n", pProcess->uPID);
[39279]826 uStatus = PROC_STS_TOK;
827 }
828 else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX)
829 {
[58029]830 VGSvcVerbose(3, "[PID %RU32]: Timed out and did *not* get killed\n", pProcess->uPID);
[39279]831 uStatus = PROC_STS_TOA;
832 }
[45415]833 else if (pProcess->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
[39279]834 {
[58029]835 VGSvcVerbose(3, "[PID %RU32]: Got terminated because system/service is about to shutdown\n", pProcess->uPID);
[39279]836 uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */
[84147]837 fFlags = pProcess->pStartupInfo->fFlags; /* Return handed-in execution flags back to the host. */
[39279]838 }
839 else if (fProcessAlive)
[58029]840 VGSvcError("[PID %RU32]: Is alive when it should not!\n", pProcess->uPID);
[39279]841 else if (MsProcessKilled != UINT64_MAX)
[58029]842 VGSvcError("[PID %RU32]: Has been killed when it should not!\n", pProcess->uPID);
[39279]843 else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
844 {
[58029]845 VGSvcVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_NORMAL (Exit code: %d)\n",
846 pProcess->uPID, ProcessStatus.iStatus);
[39279]847 uStatus = PROC_STS_TEN;
[58029]848 fFlags = ProcessStatus.iStatus;
[39279]849 }
850 else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
851 {
[58029]852 VGSvcVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_SIGNAL (Signal: %u)\n",
853 pProcess->uPID, ProcessStatus.iStatus);
[39279]854 uStatus = PROC_STS_TES;
[58029]855 fFlags = ProcessStatus.iStatus;
[39279]856 }
857 else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
858 {
[39843]859 /* ProcessStatus.iStatus will be undefined. */
[58029]860 VGSvcVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_ABEND\n", pProcess->uPID);
[39279]861 uStatus = PROC_STS_TEA;
[58029]862 fFlags = ProcessStatus.iStatus;
[39279]863 }
864 else
[58029]865 VGSvcVerbose(1, "[PID %RU32]: Handling process status %u not implemented\n", pProcess->uPID, ProcessStatus.enmReason);
[84238]866 VBGLR3GUESTCTRLCMDCTX ctxEnd = { g_idControlSvcClient, pProcess->uContextID, 0 /* uProtocol */, 0 /* uNumParms */ };
[58029]867 VGSvcVerbose(2, "[PID %RU32]: Ended, ClientID=%u, CID=%u, Status=%u, Flags=0x%x\n",
[75858]868 pProcess->uPID, ctxEnd.uClientID, pProcess->uContextID, uStatus, fFlags);
[39279]869
[58029]870 rc2 = VbglR3GuestCtrlProcCbStatus(&ctxEnd, pProcess->uPID, uStatus, fFlags, NULL /* pvData */, 0 /* cbData */);
[47545]871 if ( RT_FAILURE(rc2)
872 && rc2 == VERR_NOT_FOUND)
[58029]873 VGSvcError("[PID %RU32]: Error reporting final status to host; rc=%Rrc\n", pProcess->uPID, rc2);
[45604]874 }
[43981]875
[58029]876 VGSvcVerbose(3, "[PID %RU32]: Process loop returned with rc=%Rrc\n", pProcess->uPID, rc);
[38113]877 return rc;
878}
879
880
[62882]881#if 0 /* unused */
[39843]882/**
883 * Initializes a pipe's handle and pipe object.
884 *
885 * @return IPRT status code.
886 * @param ph The pipe's handle to initialize.
887 * @param phPipe The pipe's object to initialize.
888 */
[58029]889static int vgsvcGstCtrlProcessInitPipe(PRTHANDLE ph, PRTPIPE phPipe)
[39418]890{
891 AssertPtrReturn(ph, VERR_INVALID_PARAMETER);
892 AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
893
894 ph->enmType = RTHANDLETYPE_PIPE;
895 ph->u.hPipe = NIL_RTPIPE;
896 *phPipe = NIL_RTPIPE;
897
898 return VINF_SUCCESS;
899}
[62882]900#endif
[39418]901
902
[36548]903/**
[39279]904 * Sets up the redirection / pipe / nothing for one of the standard handles.
[38157]905 *
[39279]906 * @returns IPRT status code. No client replies made.
[39434]907 * @param pszHowTo How to set up this standard handle.
[39279]908 * @param fd Which standard handle it is (0 == stdin, 1 ==
909 * stdout, 2 == stderr).
910 * @param ph The generic handle that @a pph may be set
911 * pointing to. Always set.
912 * @param pph Pointer to the RTProcCreateExec argument.
913 * Always set.
914 * @param phPipe Where to return the end of the pipe that we
[39434]915 * should service.
[38157]916 */
[58029]917static int vgsvcGstCtrlProcessSetupPipe(const char *pszHowTo, int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
[38157]918{
[39434]919 AssertPtrReturn(ph, VERR_INVALID_POINTER);
920 AssertPtrReturn(pph, VERR_INVALID_POINTER);
[39843]921 AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
[39279]922
923 int rc;
924
[39434]925 ph->enmType = RTHANDLETYPE_PIPE;
926 ph->u.hPipe = NIL_RTPIPE;
927 *pph = NULL;
928 *phPipe = NIL_RTPIPE;
929
930 if (!strcmp(pszHowTo, "|"))
[38157]931 {
[39434]932 /*
933 * Setup a pipe for forwarding to/from the client.
934 * The ph union struct will be filled with a pipe read/write handle
935 * to represent the "other" end to phPipe.
936 */
937 if (fd == 0) /* stdin? */
938 {
939 /* Connect a wrtie pipe specified by phPipe to stdin. */
940 rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
941 }
[58029]942 else /* stdout or stderr. */
[39434]943 {
944 /* Connect a read pipe specified by phPipe to stdout or stderr. */
945 rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
946 }
947
948 if (RT_FAILURE(rc))
949 return rc;
950
951 ph->enmType = RTHANDLETYPE_PIPE;
952 *pph = ph;
[39279]953 }
[39434]954 else if (!strcmp(pszHowTo, "/dev/null"))
[39279]955 {
[39434]956 /*
957 * Redirect to/from /dev/null.
958 */
959 RTFILE hFile;
960 rc = RTFileOpenBitBucket(&hFile, fd == 0 ? RTFILE_O_READ : RTFILE_O_WRITE);
961 if (RT_FAILURE(rc))
962 return rc;
963
964 ph->enmType = RTHANDLETYPE_FILE;
965 ph->u.hFile = hFile;
966 *pph = ph;
[39279]967 }
[39434]968 else /* Add other piping stuff here. */
[39843]969 rc = VINF_SUCCESS; /* Same as parent (us). */
[38157]970
[39279]971 return rc;
972}
973
974
975/**
[92660]976 * Expands a file name / path to its real content.
[39279]977 *
[92660]978 * ~~This only works on Windows for now (e.g. translating "%TEMP%\foo.exe" to
979 * "C:\Windows\Temp" when starting with system / administrative rights).~~ See
980 * todo in code.
981 *
[39279]982 * @return IPRT status code.
983 * @param pszPath Path to resolve.
984 * @param pszExpanded Pointer to string to store the resolved path in.
985 * @param cbExpanded Size (in bytes) of string to store the resolved path.
986 */
[58029]987static int vgsvcGstCtrlProcessMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded)
[39279]988{
[58029]989/** @todo r=bird: This feature shall be made optional, i.e. require a
990 * flag to be passed down. Further, it shall work on the environment
991 * block of the new process (i.e. include env changes passed down from
992 * the caller). I would also suggest using the unix variable expansion
993 * syntax, not the DOS one.
994 *
995 * Since this currently not available on non-windows guests, I suggest
996 * we disable it until such a time as it is implemented correctly. */
[92659]997#if 0 /*def RT_OS_WINDOWS - see above. Don't know why this wasn't disabled before 7.0, didn't see the @todo yet? */
998 int rc = VINF_SUCCESS;
[58029]999 if (!ExpandEnvironmentStrings(pszPath, pszExpanded, (DWORD)cbExpanded))
[39279]1000 rc = RTErrConvertFromWin32(GetLastError());
1001#else
[92659]1002 /* There is no expansion anywhere yet, see above @todo. */
1003 int rc = RTStrCopy(pszExpanded, cbExpanded, pszPath);
[39279]1004#endif
1005#ifdef DEBUG
[58031]1006 VGSvcVerbose(3, "vgsvcGstCtrlProcessMakeFullPath: %s -> %s\n", pszPath, pszExpanded);
[39279]1007#endif
1008 return rc;
1009}
1010
1011
1012/**
[92658]1013 * Resolves the full path of a specified executable name.
[39279]1014 *
[92658]1015 * This function also resolves internal VBoxService tools to its appropriate
1016 * executable path + name if VBOXSERVICE_NAME is specified as pszFilename.
1017 *
[39279]1018 * @return IPRT status code.
[92658]1019 * @param pszFilename File name to resolve.
[39279]1020 * @param pszResolved Pointer to a string where the resolved file name will be stored.
1021 * @param cbResolved Size (in bytes) of resolved file name string.
1022 */
[92658]1023static int vgsvcGstCtrlProcessResolveExecutable(const char *pszFilename, char *pszResolved, size_t cbResolved)
[39279]1024{
[92658]1025 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
[40531]1026 AssertPtrReturn(pszResolved, VERR_INVALID_POINTER);
1027 AssertReturn(cbResolved, VERR_INVALID_PARAMETER);
1028
[92658]1029 const char * const pszOrgFilename = pszFilename;
1030 if ( RTStrICmp(pszFilename, g_pszProgName) == 0
1031 || RTStrICmp(pszFilename, VBOXSERVICE_NAME) == 0)
1032 pszFilename = RTProcExecutablePath();
[39279]1033
[92658]1034 int rc = vgsvcGstCtrlProcessMakeFullPath(pszFilename, pszResolved, cbResolved);
[40531]1035 if (RT_SUCCESS(rc))
[92658]1036 VGSvcVerbose(3, "Looked up executable: %s -> %s\n", pszOrgFilename, pszResolved);
[39279]1037 return rc;
[38157]1038}
1039
1040
1041/**
[39279]1042 * Constructs the argv command line by resolving environment variables
1043 * and relative paths.
[36548]1044 *
[39279]1045 * @return IPRT status code.
[83508]1046 * @param pszArgv0 First argument (argv0), either original or modified version.
[39279]1047 * @param papszArgs Original argv command line from the host, starting at argv[1].
[55531]1048 * @param fFlags The process creation flags pass to us from the host.
[92662]1049 * @param fExecutingSelf Set if we're executing the VBoxService executable
1050 * and should inject the --utf8-argv trick.
[39279]1051 * @param ppapszArgv Pointer to a pointer with the new argv command line.
1052 * Needs to be freed with RTGetOptArgvFree.
[36548]1053 */
[58029]1054static int vgsvcGstCtrlProcessAllocateArgv(const char *pszArgv0, const char * const *papszArgs, uint32_t fFlags,
[92662]1055 bool fExecutingSelf, char ***ppapszArgv)
[36548]1056{
[92662]1057 VGSvcVerbose(3, "VGSvcGstCtrlProcessPrepareArgv: pszArgv0=%p, papszArgs=%p, fFlags=%#x, fExecutingSelf=%d, ppapszArgv=%p\n",
1058 pszArgv0, papszArgs, fFlags, fExecutingSelf, ppapszArgv);
[43049]1059
[83508]1060 AssertPtrReturn(pszArgv0, VERR_INVALID_POINTER);
1061 AssertPtrReturn(ppapszArgv, VERR_INVALID_POINTER);
[92707]1062 AssertReturn(!(fFlags & GUEST_PROC_CREATE_FLAG_EXPAND_ARGUMENTS), VERR_INVALID_FLAGS); /** @todo implement me */
[83508]1063
[92662]1064#ifndef VBOXSERVICE_ARG1_UTF8_ARGV
1065 fExecutingSelf = false;
1066#endif
1067
1068 /* Count arguments: */
[39279]1069 int rc = VINF_SUCCESS;
[42997]1070 uint32_t cArgs;
1071 for (cArgs = 0; papszArgs[cArgs]; cArgs++)
1072 {
[43196]1073 if (cArgs >= UINT32_MAX - 2)
[42997]1074 return VERR_BUFFER_OVERFLOW;
1075 }
[39279]1076
[43049]1077 /* Allocate new argv vector (adding + 2 for argv0 + termination). */
[92662]1078 size_t cbSize = (fExecutingSelf + cArgs + 2) * sizeof(char *);
[58029]1079 char **papszNewArgv = (char **)RTMemAlloc(cbSize);
[42997]1080 if (!papszNewArgv)
1081 return VERR_NO_MEMORY;
1082
[83508]1083 VGSvcVerbose(3, "VGSvcGstCtrlProcessAllocateArgv: pszArgv0 = '%s', cArgs=%RU32, cbSize=%zu\n", pszArgv0, cArgs, cbSize);
[83419]1084#ifdef DEBUG /* Never log this stuff in release mode! */
[83508]1085 if (cArgs)
1086 {
1087 for (uint32_t i = 0; i < cArgs; i++)
1088 VGSvcVerbose(3, "VGSvcGstCtrlProcessAllocateArgv: papszArgs[%RU32] = '%s'\n", i, papszArgs[i]);
1089 }
[42997]1090#endif
1091
[92663]1092 /* HACK ALERT! Older hosts (< VBox 6.1.x) did not allow the user to really specify
1093 the first argument separately from the executable image, so we have
1094 to fudge a little in the unquoted argument case to deal with executables
1095 containing spaces. Windows only, as RTPROC_FLAGS_UNQUOTED_ARGS is
1096 ignored on all other hosts. */
1097#ifdef RT_OS_WINDOWS
[92707]1098 if ( (fFlags & GUEST_PROC_CREATE_FLAG_UNQUOTED_ARGS)
[92663]1099 && strpbrk(pszArgv0, " \t\n\r")
1100 && pszArgv0[0] == '"')
[83508]1101 {
[55531]1102 size_t cchArgv0 = strlen(pszArgv0);
[83508]1103 AssertReturn(cchArgv0, VERR_INVALID_PARAMETER); /* Paranoia. */
[55531]1104 rc = RTStrAllocEx(&papszNewArgv[0], 1 + cchArgv0 + 1 + 1);
1105 if (RT_SUCCESS(rc))
1106 {
1107 char *pszDst = papszNewArgv[0];
1108 *pszDst++ = '"';
1109 memcpy(pszDst, pszArgv0, cchArgv0);
1110 pszDst += cchArgv0;
1111 *pszDst++ = '"';
1112 *pszDst = '\0';
1113 }
1114 }
[92663]1115 else
1116#endif
1117 rc = RTStrDupEx(&papszNewArgv[0], pszArgv0);
[42997]1118 if (RT_SUCCESS(rc))
[36548]1119 {
[92662]1120 size_t iDst = 1;
1121
1122#ifdef VBOXSERVICE_ARG1_UTF8_ARGV
1123 /* Insert --utf8-argv as the first argument if executing the VBoxService binary. */
1124 if (fExecutingSelf)
[42921]1125 {
[92662]1126 rc = RTStrDupEx(&papszNewArgv[iDst], VBOXSERVICE_ARG1_UTF8_ARGV);
1127 if (RT_SUCCESS(rc))
1128 iDst++;
1129 }
1130#endif
1131 /* Copy over the other arguments. */
1132 if (RT_SUCCESS(rc))
1133 for (size_t iSrc = 0; iSrc < cArgs; iSrc++)
1134 {
[42997]1135#if 0 /* Arguments expansion -- untested. */
[92707]1136 if (fFlags & GUEST_PROC_CREATE_FLAG_EXPAND_ARGUMENTS)
[92662]1137 {
[55531]1138/** @todo r=bird: If you want this, we need a generic implementation, preferably in RTEnv or somewhere like that. The marking
1139 * up of the variables must be the same on all platforms. */
[92662]1140 /* According to MSDN the limit on older Windows version is 32K, whereas
1141 * Vista+ there are no limits anymore. We still stick to 4K. */
1142 char szExpanded[_4K];
[42997]1143# ifdef RT_OS_WINDOWS
[92662]1144 if (!ExpandEnvironmentStrings(papszArgs[i], szExpanded, sizeof(szExpanded)))
1145 rc = RTErrConvertFromWin32(GetLastError());
[42997]1146# else
[92662]1147 /* No expansion for non-Windows yet. */
1148 rc = RTStrCopy(papszArgs[i], sizeof(szExpanded), szExpanded);
[42997]1149# endif
[92662]1150 if (RT_SUCCESS(rc))
1151 rc = RTStrDupEx(&pszArg, szExpanded);
1152 }
1153 else
1154#endif
1155 rc = RTStrDupEx(&papszNewArgv[iDst], papszArgs[iSrc]);
[42997]1156 if (RT_SUCCESS(rc))
[92662]1157 iDst++;
1158 else
1159 break;
[42997]1160 }
[42921]1161
[39279]1162 if (RT_SUCCESS(rc))
[36548]1163 {
[42997]1164 /* Terminate array. */
[92662]1165 papszNewArgv[iDst] = NULL;
[42921]1166
[42997]1167 *ppapszArgv = papszNewArgv;
[55531]1168 return VINF_SUCCESS;
[36548]1169 }
[39279]1170
[55531]1171 /* Failed, bail out. */
[92662]1172 while (iDst-- > 0)
1173 RTStrFree(papszNewArgv[iDst]);
[39279]1174 }
[55531]1175 RTMemFree(papszNewArgv);
[39279]1176 return rc;
[36548]1177}
1178
1179
[44963]1180/**
1181 * Assigns a valid PID to a guest control thread and also checks if there already was
1182 * another (stale) guest process which was using that PID before and destroys it.
1183 *
1184 * @return IPRT status code.
[47481]1185 * @param pProcess Process to assign PID to.
[44963]1186 * @param uPID PID to assign to the specified guest control execution thread.
1187 */
[58029]1188static int vgsvcGstCtrlProcessAssignPID(PVBOXSERVICECTRLPROCESS pProcess, uint32_t uPID)
[44963]1189{
[47481]1190 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
[44963]1191 AssertReturn(uPID, VERR_INVALID_PARAMETER);
1192
[47481]1193 AssertPtr(pProcess->pSession);
1194 int rc = RTCritSectEnter(&pProcess->pSession->CritSect);
[44963]1195 if (RT_SUCCESS(rc))
1196 {
1197 /* Search old threads using the desired PID and shut them down completely -- it's
1198 * not used anymore. */
[47481]1199 bool fTryAgain;
[44963]1200 do
1201 {
[47481]1202 fTryAgain = false;
[64766]1203 PVBOXSERVICECTRLPROCESS pProcessCur;
[47545]1204 RTListForEach(&pProcess->pSession->lstProcesses, pProcessCur, VBOXSERVICECTRLPROCESS, Node)
[44963]1205 {
[47481]1206 if (pProcessCur->uPID == uPID)
[44963]1207 {
[47481]1208 Assert(pProcessCur != pProcess); /* can't happen */
[44963]1209 uint32_t uTriedPID = uPID;
1210 uPID += 391939;
[58029]1211 VGSvcVerbose(2, "PID %RU32 was used before (process %p), trying again with %RU32 ...\n",
[47481]1212 uTriedPID, pProcessCur, uPID);
[44963]1213 fTryAgain = true;
1214 break;
1215 }
1216 }
1217 } while (fTryAgain);
1218
1219 /* Assign PID to current thread. */
[47481]1220 pProcess->uPID = uPID;
[44963]1221
[47481]1222 rc = RTCritSectLeave(&pProcess->pSession->CritSect);
[44963]1223 AssertRC(rc);
1224 }
1225
1226 return rc;
1227}
1228
1229
[58029]1230static void vgsvcGstCtrlProcessFreeArgv(char **papszArgv)
[42997]1231{
1232 if (papszArgv)
1233 {
1234 size_t i = 0;
1235 while (papszArgv[i])
[43000]1236 RTStrFree(papszArgv[i++]);
[42997]1237 RTMemFree(papszArgv);
1238 }
1239}
1240
1241
[36548]1242/**
[39279]1243 * Helper function to create/start a process on the guest.
[36548]1244 *
1245 * @return IPRT status code.
[39279]1246 * @param pszExec Full qualified path of process to start (without arguments).
1247 * @param papszArgs Pointer to array of command line arguments.
1248 * @param hEnv Handle to environment block to use.
1249 * @param fFlags Process execution flags.
1250 * @param phStdIn Handle for the process' stdin pipe.
1251 * @param phStdOut Handle for the process' stdout pipe.
1252 * @param phStdErr Handle for the process' stderr pipe.
1253 * @param pszAsUser User name (account) to start the process under.
1254 * @param pszPassword Password of the specified user.
[59134]1255 * @param pszDomain Domain to use for authentication.
[99120]1256 * @param pszCwd Current working directory to use for the created process.
1257 * Set to NULL if not being used.
[39279]1258 * @param phProcess Pointer which will receive the process handle after
1259 * successful process start.
[36548]1260 */
[58029]1261static int vgsvcGstCtrlProcessCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
[59517]1262 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr,
1263 const char *pszAsUser, const char *pszPassword, const char *pszDomain,
[99120]1264 const char *pszCwd, PRTPROCESS phProcess)
[36548]1265{
[62882]1266#ifndef RT_OS_WINDOWS
1267 RT_NOREF1(pszDomain);
1268#endif
[39279]1269 AssertPtrReturn(pszExec, VERR_INVALID_PARAMETER);
1270 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
[59517]1271 /* phStdIn is optional. */
1272 /* phStdOut is optional. */
1273 /* phStdErr is optional. */
1274 /* pszPassword is optional. */
1275 /* pszDomain is optional. */
[99120]1276 /* pszCwd is optional. */
[39279]1277 AssertPtrReturn(phProcess, VERR_INVALID_PARAMETER);
[36548]1278
[39279]1279 int rc = VINF_SUCCESS;
1280 char szExecExp[RTPATH_MAX];
[42921]1281
[59517]1282#ifdef DEBUG
1283 /* Never log this in release mode! */
1284 VGSvcVerbose(4, "pszUser=%s, pszPassword=%s, pszDomain=%s\n", pszAsUser, pszPassword, pszDomain);
1285#endif
1286
[39279]1287#ifdef RT_OS_WINDOWS
1288 /*
1289 * If sysprep should be executed do this in the context of VBoxService, which
1290 * (usually, if started by SCM) has administrator rights. Because of that a UI
1291 * won't be shown (doesn't have a desktop).
1292 */
[40531]1293 if (!RTStrICmp(pszExec, "sysprep"))
[36548]1294 {
[39279]1295 /* Use a predefined sysprep path as default. */
1296 char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe";
[46513]1297 /** @todo Check digital signature of file above before executing it? */
[39279]1298
1299 /*
[46513]1300 * On Windows Vista (and up) sysprep is located in "system32\\Sysprep\\sysprep.exe",
[39279]1301 * so detect the OS and use a different path.
1302 */
[96502]1303 if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6,0,0) /* Vista and later */)
[36548]1304 {
[39279]1305 rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL);
[46513]1306#ifndef RT_ARCH_AMD64
1307 /* Don't execute 64-bit sysprep from a 32-bit service host! */
1308 char szSysWow64[RTPATH_MAX];
1309 if (RTStrPrintf(szSysWow64, sizeof(szSysWow64), "%s", szSysprepCmd))
1310 {
1311 rc = RTPathAppend(szSysWow64, sizeof(szSysWow64), "SysWow64");
1312 AssertRC(rc);
1313 }
1314 if ( RT_SUCCESS(rc)
1315 && RTPathExists(szSysWow64))
[58029]1316 VGSvcVerbose(0, "Warning: This service is 32-bit; could not execute sysprep on 64-bit OS!\n");
[46513]1317#endif
[39279]1318 if (RT_SUCCESS(rc))
[46513]1319 rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\Sysprep\\sysprep.exe");
1320 if (RT_SUCCESS(rc))
1321 RTPathChangeToDosSlashes(szSysprepCmd, false /* No forcing necessary */);
1322
1323 if (RT_FAILURE(rc))
[58029]1324 VGSvcError("Failed to detect sysrep location, rc=%Rrc\n", rc);
[39279]1325 }
[36548]1326
[58029]1327 VGSvcVerbose(3, "Sysprep executable is: %s\n", szSysprepCmd);
[46513]1328
[39279]1329 if (RT_SUCCESS(rc))
1330 {
1331 char **papszArgsExp;
[92662]1332 rc = vgsvcGstCtrlProcessAllocateArgv(szSysprepCmd /* argv0 */, papszArgs, fFlags,
1333 false /*fExecutingSelf*/, &papszArgsExp);
[39279]1334 if (RT_SUCCESS(rc))
[36548]1335 {
[46513]1336 /* As we don't specify credentials for the sysprep process, it will
1337 * run under behalf of the account VBoxService was started under, most
1338 * likely local system. */
[39279]1339 rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */,
1340 phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */,
[80569]1341 NULL /* pszPassword */, NULL, phProcess);
[58029]1342 vgsvcGstCtrlProcessFreeArgv(papszArgsExp);
[36548]1343 }
[39279]1344 }
[40531]1345
1346 if (RT_FAILURE(rc))
[58029]1347 VGSvcVerbose(3, "Starting sysprep returned rc=%Rrc\n", rc);
[40531]1348
[39279]1349 return rc;
1350 }
1351#endif /* RT_OS_WINDOWS */
1352
[92662]1353 bool fExecutingSelf = false;
[60583]1354#ifdef VBOX_WITH_VBOXSERVICE_TOOLBOX
[92666]1355 /* The "vbox_" prefix is reserved for the toolbox (vbox_cat, vbox_mkdir,
1356 et al.) and we will replace pszExec with the full VBoxService path instead. */
1357 if (RTStrStartsWith(pszExec, "vbox_"))
[39279]1358 {
[92662]1359 fExecutingSelf = true;
[58029]1360 rc = vgsvcGstCtrlProcessResolveExecutable(VBOXSERVICE_NAME, szExecExp, sizeof(szExecExp));
[39279]1361 }
1362 else
1363 {
1364#endif
1365 /*
1366 * Do the environment variables expansion on executable and arguments.
1367 */
[58029]1368 rc = vgsvcGstCtrlProcessResolveExecutable(pszExec, szExecExp, sizeof(szExecExp));
[60583]1369#ifdef VBOX_WITH_VBOXSERVICE_TOOLBOX
[39279]1370 }
1371#endif
1372 if (RT_SUCCESS(rc))
1373 {
[92661]1374 /*
[83419]1375 * This one is a bit tricky to also support older hosts:
1376 *
1377 * - If the host does not provide a dedicated argv[0] (< VBox 6.1.x), we use the
[83508]1378 * unmodified executable name (pszExec) as the (default) argv[0]. This is wrong, but we can't do
[83419]1379 * much about it. The rest (argv[1,2,n]) then gets set starting at papszArgs[0].
1380 *
1381 * - Newer hosts (>= VBox 6.1.x) provide a correct argv[0] independently of the actual
1382 * executable name though, so actually use argv[0] *and* argv[1,2,n] as intended.
1383 */
1384 const bool fHasArgv0 = RT_BOOL(g_fControlHostFeatures0 & VBOX_GUESTCTRL_HF_0_PROCESS_ARGV0);
1385
[83508]1386 const char *pcszArgv0 = (fHasArgv0 && papszArgs[0]) ? papszArgs[0] : pszExec;
1387 AssertPtrReturn(pcszArgv0, VERR_INVALID_POINTER); /* Paranoia. */
1388
[83609]1389 const uint32_t uArgvIdx = pcszArgv0 == papszArgs[0] ? 1 : 0;
[83508]1390
[83602]1391 VGSvcVerbose(3, "vgsvcGstCtrlProcessCreateProcess: fHasArgv0=%RTbool, pcszArgv0=%p, uArgvIdx=%RU32, "
1392 "g_fControlHostFeatures0=%#x\n",
1393 fHasArgv0, pcszArgv0, uArgvIdx, g_fControlHostFeatures0);
[83508]1394
[39279]1395 char **papszArgsExp;
[92662]1396 rc = vgsvcGstCtrlProcessAllocateArgv(pcszArgv0, &papszArgs[uArgvIdx], fFlags, fExecutingSelf, &papszArgsExp);
[40531]1397 if (RT_FAILURE(rc))
[39279]1398 {
[40531]1399 /* Don't print any arguments -- may contain passwords or other sensible data! */
[58029]1400 VGSvcError("Could not prepare arguments, rc=%Rrc\n", rc);
[40531]1401 }
1402 else
1403 {
[92662]1404 uint32_t fProcCreateFlags = 0;
1405 if (fExecutingSelf)
1406 fProcCreateFlags |= VBOXSERVICE_PROC_F_UTF8_ARGV;
[39279]1407 if (fFlags)
[36548]1408 {
[92707]1409 if (fFlags & GUEST_PROC_CREATE_FLAG_HIDDEN)
[92662]1410 fProcCreateFlags |= RTPROC_FLAGS_HIDDEN;
[92707]1411 if (fFlags & GUEST_PROC_CREATE_FLAG_PROFILE)
[92662]1412 fProcCreateFlags |= RTPROC_FLAGS_PROFILE;
[92707]1413 if (fFlags & GUEST_PROC_CREATE_FLAG_UNQUOTED_ARGS)
[92662]1414 fProcCreateFlags |= RTPROC_FLAGS_UNQUOTED_ARGS;
[36548]1415 }
[99120]1416 if (pszCwd && *pszCwd)
1417 fProcCreateFlags |= RTPROC_FLAGS_CWD;
1418 else
1419 pszCwd = NULL;
[39279]1420
1421 /* If no user name specified run with current credentials (e.g.
1422 * full service/system rights). This is prohibited via official Main API!
1423 *
1424 * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication
1425 * code (at least on Windows) for running processes as different users
1426 * started from our system service. */
[45010]1427 if (pszAsUser && *pszAsUser)
[92662]1428 fProcCreateFlags |= RTPROC_FLAGS_SERVICE;
[39279]1429#ifdef DEBUG
[58029]1430 VGSvcVerbose(3, "Command: %s\n", szExecExp);
[39279]1431 for (size_t i = 0; papszArgsExp[i]; i++)
[92662]1432 VGSvcVerbose(3, " argv[%zu]: %s\n", i, papszArgsExp[i]);
[39279]1433#endif
[58029]1434 VGSvcVerbose(3, "Starting process '%s' ...\n", szExecExp);
[40531]1435
[59134]1436#ifdef RT_OS_WINDOWS
1437 /* If a domain name is given, construct an UPN (User Principle Name) with
1438 * the domain name built-in, e.g. "joedoe@example.com". */
1439 char *pszUserUPN = NULL;
[92667]1440 if (pszDomain && *pszDomain != '\0')
[59134]1441 {
[92668]1442 pszAsUser = pszUserUPN = RTStrAPrintf2("%s@%s", pszAsUser, pszDomain);
[92667]1443 if (pszAsUser)
1444 VGSvcVerbose(3, "Using UPN: %s\n", pszAsUser);
1445 else
1446 rc = VERR_NO_STR_MEMORY;
[59134]1447 }
[92667]1448 if (RT_SUCCESS(rc))
[59134]1449#endif
[92667]1450 {
1451 /* Do normal execution. */
1452 rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, fProcCreateFlags,
1453 phStdIn, phStdOut, phStdErr,
1454 pszAsUser,
1455 pszPassword && *pszPassword ? pszPassword : NULL,
[99120]1456 (void *)pszCwd /*pvExtraData*/,
[92667]1457 phProcess);
[59134]1458
1459#ifdef RT_OS_WINDOWS
1460 RTStrFree(pszUserUPN);
1461#endif
[92667]1462 VGSvcVerbose(3, "Starting process '%s' returned rc=%Rrc\n", szExecExp, rc);
1463 }
[58029]1464 vgsvcGstCtrlProcessFreeArgv(papszArgsExp);
[36548]1465 }
1466 }
1467 return rc;
1468}
1469
[47551]1470
1471#ifdef DEBUG
[83400]1472/**
1473 * Dumps content to a file in the OS temporary directory.
1474 *
1475 * @returns VBox status code.
1476 * @param pvBuf Buffer of content to dump.
1477 * @param cbBuf Size (in bytes) of content to dump.
1478 * @param pszFileNmFmt Pointer to the file name format string, @see pg_rt_str_format.
1479 * @param ... The format argument.
1480 */
1481static int vgsvcGstCtrlProcessDbgDumpToFileF(const void *pvBuf, size_t cbBuf, const char *pszFileNmFmt, ...)
[47551]1482{
[83400]1483 AssertPtrReturn(pszFileNmFmt, VERR_INVALID_POINTER);
[47551]1484 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1485
1486 if (!cbBuf)
1487 return VINF_SUCCESS;
1488
[83400]1489 va_list va;
1490 va_start(va, pszFileNmFmt);
[47551]1491
[83400]1492 char *pszFileName = NULL;
1493 const int cchFileName = RTStrAPrintfV(&pszFileName, pszFileNmFmt, va);
1494
1495 va_end(va);
1496
1497 if (!cchFileName)
1498 return VERR_NO_MEMORY;
1499
1500 char szPathFileAbs[RTPATH_MAX];
1501 int rc = RTPathTemp(szPathFileAbs, sizeof(szPathFileAbs));
[47551]1502 if (RT_SUCCESS(rc))
[83400]1503 rc = RTPathAppend(szPathFileAbs, sizeof(szPathFileAbs), pszFileName);
[47551]1504
[83400]1505 RTStrFree(pszFileName);
1506
[47551]1507 if (RT_SUCCESS(rc))
1508 {
[83400]1509 VGSvcVerbose(4, "Dumping %zu bytes to '%s'\n", cbBuf, szPathFileAbs);
[47551]1510
1511 RTFILE fh;
[83400]1512 rc = RTFileOpen(&fh, szPathFileAbs, RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
[47551]1513 if (RT_SUCCESS(rc))
1514 {
1515 rc = RTFileWrite(fh, pvBuf, cbBuf, NULL /* pcbWritten */);
1516 RTFileClose(fh);
1517 }
1518 }
1519
1520 return rc;
1521}
[58029]1522#endif /* DEBUG */
[47551]1523
1524
[36548]1525/**
[39427]1526 * The actual worker routine (loop) for a started guest process.
[36548]1527 *
1528 * @return IPRT status code.
[58089]1529 * @param pProcess The process we're servicing and monitoring.
[36548]1530 */
[58029]1531static int vgsvcGstCtrlProcessProcessWorker(PVBOXSERVICECTRLPROCESS pProcess)
[36548]1532{
[45010]1533 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
[84147]1534 VGSvcVerbose(3, "Thread of process pThread=0x%p = '%s' started\n", pProcess, pProcess->pStartupInfo->pszCmd);
[36548]1535
[84147]1536 VGSvcVerbose(3, "Guest process '%s', flags=0x%x\n", pProcess->pStartupInfo->pszCmd, pProcess->pStartupInfo->fFlags);
[39906]1537
[75858]1538 int rc = VGSvcGstCtrlSessionProcessAdd(pProcess->pSession, pProcess);
[39279]1539 if (RT_FAILURE(rc))
1540 {
[83286]1541 VGSvcError("Error while adding guest process '%s' (%p) to session process list, rc=%Rrc\n",
[84147]1542 pProcess->pStartupInfo->pszCmd, pProcess, rc);
[39279]1543 RTThreadUserSignal(RTThreadSelf());
1544 return rc;
1545 }
[47545]1546
[39279]1547 bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
1548
1549 /*
[45415]1550 * Prepare argument list.
1551 */
[83419]1552 VGSvcVerbose(3, "vgsvcGstCtrlProcessProcessWorker: fHostFeatures0 = %#x\n", g_fControlHostFeatures0);
[84147]1553 VGSvcVerbose(3, "vgsvcGstCtrlProcessProcessWorker: StartupInfo.szCmd = '%s'\n", pProcess->pStartupInfo->pszCmd);
[99120]1554 VGSvcVerbose(3, "vgsvcGstCtrlProcessProcessWorker: StartupInfo.szCwd = '%s'\n", pProcess->pStartupInfo->pszCwd
1555 ? pProcess->pStartupInfo->pszCwd : "<None>");
[84147]1556 VGSvcVerbose(3, "vgsvcGstCtrlProcessProcessWorker: StartupInfo.uNumArgs = '%RU32'\n", pProcess->pStartupInfo->cArgs);
[83419]1557#ifdef DEBUG /* Never log this stuff in release mode! */
[84147]1558 VGSvcVerbose(3, "vgsvcGstCtrlProcessProcessWorker: StartupInfo.szArgs = '%s'\n", pProcess->pStartupInfo->pszArgs);
[83419]1559#endif
1560
[45415]1561 char **papszArgs;
[70390]1562 int cArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
1563 rc = RTGetOptArgvFromString(&papszArgs, &cArgs,
[84147]1564 pProcess->pStartupInfo->cArgs > 0 ? pProcess->pStartupInfo->pszArgs : "",
[55671]1565 RTGETOPTARGV_CNV_QUOTE_BOURNE_SH, NULL);
[45415]1566
[83609]1567 VGSvcVerbose(3, "vgsvcGstCtrlProcessProcessWorker: cArgs = %d\n", cArgs);
[83420]1568#ifdef VBOX_STRICT
[83609]1569 for (int i = 0; i < cArgs; i++)
1570 VGSvcVerbose(3, "vgsvcGstCtrlProcessProcessWorker: papszArgs[%d] = '%s'\n", i, papszArgs[i] ? papszArgs[i] : "<NULL>");
1571
[83508]1572 const bool fHasArgv0 = RT_BOOL(g_fControlHostFeatures0 & VBOX_GUESTCTRL_HF_0_PROCESS_ARGV0); RT_NOREF(fHasArgv0);
[83609]1573 const int cArgsToCheck = cArgs + (fHasArgv0 ? 0 : 1);
[83419]1574
1575 /* Did we get the same result?
1576 * Take into account that we might not have supplied a (correct) argv[0] from the host. */
[84147]1577 AssertMsg((int)pProcess->pStartupInfo->cArgs == cArgsToCheck,
[83508]1578 ("rc=%Rrc, StartupInfo.uNumArgs=%RU32 != cArgsToCheck=%d, cArgs=%d, fHostFeatures0=%#x\n",
[84147]1579 rc, pProcess->pStartupInfo->cArgs, cArgsToCheck, cArgs, g_fControlHostFeatures0));
[83420]1580#endif
1581
[45415]1582 /*
[83269]1583 * Create the environment.
[45415]1584 */
[84147]1585 uint32_t const cbEnv = pProcess->pStartupInfo->cbEnv;
[45415]1586 if (RT_SUCCESS(rc))
[92707]1587 AssertStmt( cbEnv <= GUEST_PROC_MAX_ENV_LEN
[84147]1588 || pProcess->pStartupInfo->cEnvVars == 0,
[84140]1589 rc = VERR_INVALID_PARAMETER);
1590 if (RT_SUCCESS(rc))
[45415]1591 {
[83269]1592 RTENV hEnv;
1593 rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
1594 if (RT_SUCCESS(rc))
[45415]1595 {
[83269]1596 VGSvcVerbose(3, "Additional environment variables: %RU32 (%RU32 bytes)\n",
[84147]1597 pProcess->pStartupInfo->cEnvVars, cbEnv);
[45415]1598
[84147]1599 if ( pProcess->pStartupInfo->cEnvVars
[84140]1600 && cbEnv > 0)
[45415]1601 {
[84140]1602 size_t offCur = 0;
1603 while (offCur < cbEnv)
[45415]1604 {
[84147]1605 const char * const pszCur = &pProcess->pStartupInfo->pszEnv[offCur];
[84140]1606 size_t const cchCur = RTStrNLen(pszCur, cbEnv - offCur);
1607 AssertBreakStmt(cchCur < cbEnv - offCur, rc = VERR_INVALID_PARAMETER);
[83269]1608 VGSvcVerbose(3, "Setting environment variable: '%s'\n", pszCur);
1609 rc = RTEnvPutEx(hEnv, pszCur);
[84140]1610 if (RT_SUCCESS(rc))
1611 offCur += cchCur + 1;
1612 else
[83269]1613 {
1614 VGSvcError("Setting environment variable '%s' failed: %Rrc\n", pszCur, rc);
1615 break;
1616 }
[45415]1617 }
1618 }
1619
[36548]1620 if (RT_SUCCESS(rc))
1621 {
[62850]1622 /*
1623 * Setup the redirection of the standard stuff.
1624 */
1625 /** @todo consider supporting: gcc stuff.c >file 2>&1. */
1626 RTHANDLE hStdIn;
1627 PRTHANDLE phStdIn;
1628 rc = vgsvcGstCtrlProcessSetupPipe("|", 0 /*STDIN_FILENO*/,
1629 &hStdIn, &phStdIn, &pProcess->hPipeStdInW);
[36548]1630 if (RT_SUCCESS(rc))
1631 {
[62850]1632 RTHANDLE hStdOut;
1633 PRTHANDLE phStdOut;
[92707]1634 rc = vgsvcGstCtrlProcessSetupPipe( (pProcess->pStartupInfo->fFlags & GUEST_PROC_CREATE_FLAG_WAIT_STDOUT)
[44248]1635 ? "|" : "/dev/null",
[62850]1636 1 /*STDOUT_FILENO*/,
1637 &hStdOut, &phStdOut, &pProcess->hPipeStdOutR);
[38870]1638 if (RT_SUCCESS(rc))
[38587]1639 {
[62850]1640 RTHANDLE hStdErr;
1641 PRTHANDLE phStdErr;
[92707]1642 rc = vgsvcGstCtrlProcessSetupPipe( (pProcess->pStartupInfo->fFlags & GUEST_PROC_CREATE_FLAG_WAIT_STDERR)
[62850]1643 ? "|" : "/dev/null",
1644 2 /*STDERR_FILENO*/,
1645 &hStdErr, &phStdErr, &pProcess->hPipeStdErrR);
[39279]1646 if (RT_SUCCESS(rc))
[38870]1647 {
[62850]1648 /*
1649 * Create a poll set for the pipes and let the
1650 * transport layer add stuff to it as well.
1651 */
1652 rc = RTPollSetCreate(&pProcess->hPollSet);
[39279]1653 if (RT_SUCCESS(rc))
1654 {
[62850]1655 uint32_t uFlags = RTPOLL_EVT_ERROR;
[75858]1656#if 0
[62850]1657 /* Add reading event to pollset to get some more information. */
1658 uFlags |= RTPOLL_EVT_READ;
[75858]1659#endif
[62850]1660 /* Stdin. */
[39843]1661 if (RT_SUCCESS(rc))
[62850]1662 rc = RTPollSetAddPipe(pProcess->hPollSet,
1663 pProcess->hPipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN);
1664 /* Stdout. */
[39279]1665 if (RT_SUCCESS(rc))
[62850]1666 rc = RTPollSetAddPipe(pProcess->hPollSet,
1667 pProcess->hPipeStdOutR, uFlags, VBOXSERVICECTRLPIPEID_STDOUT);
1668 /* Stderr. */
1669 if (RT_SUCCESS(rc))
1670 rc = RTPollSetAddPipe(pProcess->hPollSet,
1671 pProcess->hPipeStdErrR, uFlags, VBOXSERVICECTRLPIPEID_STDERR);
1672 /* IPC notification pipe. */
1673 if (RT_SUCCESS(rc))
1674 rc = RTPipeCreate(&pProcess->hNotificationPipeR, &pProcess->hNotificationPipeW, 0 /* Flags */);
1675 if (RT_SUCCESS(rc))
1676 rc = RTPollSetAddPipe(pProcess->hPollSet,
1677 pProcess->hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_IPC_NOTIFY);
1678 if (RT_SUCCESS(rc))
[39279]1679 {
[62850]1680 AssertPtr(pProcess->pSession);
1681 bool fNeedsImpersonation = !(pProcess->pSession->fFlags & VBOXSERVICECTRLSESSION_FLAG_SPAWN);
1682
[84147]1683 rc = vgsvcGstCtrlProcessCreateProcess(pProcess->pStartupInfo->pszCmd, papszArgs, hEnv,
1684 pProcess->pStartupInfo->fFlags,
[62850]1685 phStdIn, phStdOut, phStdErr,
[84147]1686 fNeedsImpersonation ? pProcess->pStartupInfo->pszUser : NULL,
1687 fNeedsImpersonation ? pProcess->pStartupInfo->pszPassword : NULL,
1688 fNeedsImpersonation ? pProcess->pStartupInfo->pszDomain : NULL,
[99120]1689 pProcess->pStartupInfo->pszCwd, &pProcess->hProcess);
[62850]1690 if (RT_FAILURE(rc))
1691 VGSvcError("Error starting process, rc=%Rrc\n", rc);
[39279]1692 /*
[62850]1693 * Tell the session thread that it can continue
1694 * spawning guest processes. This needs to be done after the new
1695 * process has been started because otherwise signal handling
1696 * on (Open) Solaris does not work correctly (see @bugref{5068}).
[39279]1697 */
[62850]1698 int rc2 = RTThreadUserSignal(RTThreadSelf());
1699 if (RT_SUCCESS(rc))
1700 rc = rc2;
1701 fSignalled = true;
[39279]1702
[62850]1703 if (RT_SUCCESS(rc))
1704 {
1705 /*
1706 * Close the child ends of any pipes and redirected files.
1707 */
1708 rc2 = RTHandleClose(phStdIn); AssertRC(rc2);
1709 phStdIn = NULL;
1710 rc2 = RTHandleClose(phStdOut); AssertRC(rc2);
1711 phStdOut = NULL;
1712 rc2 = RTHandleClose(phStdErr); AssertRC(rc2);
1713 phStdErr = NULL;
[39906]1714
[62850]1715 /* Enter the process main loop. */
1716 rc = vgsvcGstCtrlProcessProcLoop(pProcess);
1717
1718 /*
1719 * The handles that are no longer in the set have
1720 * been closed by the above call in order to prevent
1721 * the guest from getting stuck accessing them.
1722 * So, NIL the handles to avoid closing them again.
1723 */
1724 if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet,
1725 VBOXSERVICECTRLPIPEID_IPC_NOTIFY, NULL)))
1726 pProcess->hNotificationPipeW = NIL_RTPIPE;
1727 if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet,
1728 VBOXSERVICECTRLPIPEID_STDERR, NULL)))
1729 pProcess->hPipeStdErrR = NIL_RTPIPE;
1730 if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet,
1731 VBOXSERVICECTRLPIPEID_STDOUT, NULL)))
1732 pProcess->hPipeStdOutR = NIL_RTPIPE;
1733 if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet,
1734 VBOXSERVICECTRLPIPEID_STDIN, NULL)))
1735 pProcess->hPipeStdInW = NIL_RTPIPE;
[39279]1736 }
1737 }
[62850]1738 RTPollSetDestroy(pProcess->hPollSet);
[83286]1739 pProcess->hPollSet = NIL_RTPOLLSET;
[62850]1740
1741 RTPipeClose(pProcess->hNotificationPipeR);
1742 pProcess->hNotificationPipeR = NIL_RTPIPE;
1743 RTPipeClose(pProcess->hNotificationPipeW);
1744 pProcess->hNotificationPipeW = NIL_RTPIPE;
[39279]1745 }
[62850]1746 RTPipeClose(pProcess->hPipeStdErrR);
1747 pProcess->hPipeStdErrR = NIL_RTPIPE;
[83286]1748 RTHandleClose(&hStdErr);
[62850]1749 if (phStdErr)
1750 RTHandleClose(phStdErr);
[38870]1751 }
[62850]1752 RTPipeClose(pProcess->hPipeStdOutR);
1753 pProcess->hPipeStdOutR = NIL_RTPIPE;
1754 RTHandleClose(&hStdOut);
1755 if (phStdOut)
1756 RTHandleClose(phStdOut);
[38587]1757 }
[62850]1758 RTPipeClose(pProcess->hPipeStdInW);
1759 pProcess->hPipeStdInW = NIL_RTPIPE;
[83286]1760 RTHandleClose(&hStdIn);
1761 if (phStdIn)
1762 RTHandleClose(phStdIn);
[36548]1763 }
1764 }
[62850]1765 RTEnvDestroy(hEnv);
[36548]1766 }
[39279]1767 }
[36749]1768
[75858]1769 if (RT_FAILURE(rc))
[39300]1770 {
[84238]1771 VBGLR3GUESTCTRLCMDCTX ctx = { g_idControlSvcClient, pProcess->uContextID, 0 /* uProtocol */, 0 /* uNumParms */ };
[75858]1772 int rc2 = VbglR3GuestCtrlProcCbStatus(&ctx,
1773 pProcess->uPID, PROC_STS_ERROR, rc,
1774 NULL /* pvData */, 0 /* cbData */);
1775 if ( RT_FAILURE(rc2)
1776 && rc2 != VERR_NOT_FOUND)
1777 VGSvcError("[PID %RU32]: Could not report process failure error; rc=%Rrc (process error %Rrc)\n",
1778 pProcess->uPID, rc2, rc);
[39300]1779 }
1780
[83286]1781 /* Update stopped status. */
1782 ASMAtomicWriteBool(&pProcess->fStopped, true);
1783
[70390]1784 if (cArgs)
[45415]1785 RTGetOptArgvFree(papszArgs);
1786
[39279]1787 /*
1788 * If something went wrong signal the user event so that others don't wait
1789 * forever on this thread.
1790 */
[83286]1791 if ( RT_FAILURE(rc)
1792 && !fSignalled)
1793 {
[39279]1794 RTThreadUserSignal(RTThreadSelf());
[83286]1795 }
[39279]1796
[83286]1797 /* Set shut down flag in case we've forgotten it. */
[70390]1798 ASMAtomicWriteBool(&pProcess->fShutdown, true);
[47545]1799
[83286]1800 VGSvcVerbose(3, "[PID %RU32]: Thread of process '%s' ended with rc=%Rrc (fSignalled=%RTbool)\n",
[84147]1801 pProcess->uPID, pProcess->pStartupInfo->pszCmd, rc, fSignalled);
[83286]1802
[36548]1803 return rc;
1804}
1805
[38180]1806
[58029]1807static int vgsvcGstCtrlProcessLock(PVBOXSERVICECTRLPROCESS pProcess)
[45697]1808{
1809 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1810 int rc = RTCritSectEnter(&pProcess->CritSect);
1811 AssertRC(rc);
1812 return rc;
1813}
1814
1815
[39279]1816/**
1817 * Thread main routine for a started process.
1818 *
1819 * @return IPRT status code.
[58089]1820 * @param hThreadSelf The thread handle.
1821 * @param pvUser Pointer to a VBOXSERVICECTRLPROCESS structure.
[39279]1822 *
1823 */
[58089]1824static DECLCALLBACK(int) vgsvcGstCtrlProcessThread(RTTHREAD hThreadSelf, void *pvUser)
[38587]1825{
[62850]1826 RT_NOREF1(hThreadSelf);
[58089]1827 PVBOXSERVICECTRLPROCESS pProcess = (PVBOXSERVICECTRLPROCESS)pvUser;
[45010]1828 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
[58029]1829 return vgsvcGstCtrlProcessProcessWorker(pProcess);
[39279]1830}
1831
1832
[58029]1833static int vgsvcGstCtrlProcessUnlock(PVBOXSERVICECTRLPROCESS pProcess)
[45697]1834{
1835 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1836 int rc = RTCritSectLeave(&pProcess->CritSect);
1837 AssertRC(rc);
1838 return rc;
1839}
1840
1841
[39279]1842/**
1843 * Executes (starts) a process on the guest. This causes a new thread to be created
1844 * so that this function will not block the overall program execution.
1845 *
1846 * @return IPRT status code.
[45010]1847 * @param pSession Guest session.
1848 * @param pStartupInfo Startup info.
[39279]1849 * @param uContextID Context ID to associate the process to start with.
1850 */
[58029]1851int VGSvcGstCtrlProcessStart(const PVBOXSERVICECTRLSESSION pSession,
[84215]1852 const PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo, uint32_t uContextID)
[39279]1853{
[45010]1854 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1855 AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER);
[39279]1856
1857 /*
1858 * Allocate new thread data and assign it to our thread list.
1859 */
[45010]1860 PVBOXSERVICECTRLPROCESS pProcess = (PVBOXSERVICECTRLPROCESS)RTMemAlloc(sizeof(VBOXSERVICECTRLPROCESS));
1861 if (!pProcess)
[39279]1862 return VERR_NO_MEMORY;
1863
[58029]1864 int rc = vgsvcGstCtrlProcessInit(pProcess, pSession, pStartupInfo, uContextID);
[38587]1865 if (RT_SUCCESS(rc))
1866 {
[39427]1867 static uint32_t s_uCtrlExecThread = 0;
[58029]1868 rc = RTThreadCreateF(&pProcess->Thread, vgsvcGstCtrlProcessThread,
[45010]1869 pProcess /*pvUser*/, 0 /*cbStack*/,
[83399]1870 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "gctl%RU32", s_uCtrlExecThread++);
[39279]1871 if (RT_FAILURE(rc))
[38587]1872 {
[58029]1873 VGSvcError("Creating thread for guest process '%s' failed: rc=%Rrc, pProcess=%p\n",
[84147]1874 pStartupInfo->pszCmd, rc, pProcess);
[47545]1875
[84881]1876 /* Process has not been added to the session's process list yet, so skip VGSvcGstCtrlSessionProcessRemove() here. */
[58029]1877 VGSvcGstCtrlProcessFree(pProcess);
[39279]1878 }
1879 else
1880 {
[58029]1881 VGSvcVerbose(4, "Waiting for thread to initialize ...\n");
[39279]1882
1883 /* Wait for the thread to initialize. */
[45010]1884 rc = RTThreadUserWait(pProcess->Thread, 60 * 1000 /* 60 seconds max. */);
[39906]1885 AssertRC(rc);
[45010]1886 if ( ASMAtomicReadBool(&pProcess->fShutdown)
[70390]1887 || ASMAtomicReadBool(&pProcess->fStopped)
[39906]1888 || RT_FAILURE(rc))
[38587]1889 {
[84147]1890 VGSvcError("Thread for process '%s' failed to start, rc=%Rrc\n", pStartupInfo->pszCmd, rc);
[70390]1891 int rc2 = RTThreadWait(pProcess->Thread, RT_MS_1SEC * 30, NULL);
1892 if (RT_SUCCESS(rc2))
1893 pProcess->Thread = NIL_RTTHREAD;
[84881]1894
1895 VGSvcGstCtrlSessionProcessRemove(pSession, pProcess);
[58029]1896 VGSvcGstCtrlProcessFree(pProcess);
[39279]1897 }
1898 else
1899 {
[45010]1900 ASMAtomicXchgBool(&pProcess->fStarted, true);
[38587]1901 }
1902 }
1903 }
1904
1905 return rc;
1906}
1907
[39279]1908
[58029]1909static DECLCALLBACK(int) vgsvcGstCtrlProcessOnInput(PVBOXSERVICECTRLPROCESS pThis,
1910 const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
1911 bool fPendingClose, void *pvBuf, uint32_t cbBuf)
[38180]1912{
[47545]1913 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1914 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
[38587]1915
[47545]1916 int rc;
[44963]1917
[47545]1918 size_t cbWritten = 0;
1919 if (pvBuf && cbBuf)
[38180]1920 {
[47545]1921 if (pThis->hPipeStdInW != NIL_RTPIPE)
[58029]1922 rc = RTPipeWrite(pThis->hPipeStdInW, pvBuf, cbBuf, &cbWritten);
[47545]1923 else
1924 rc = VINF_EOF;
[44963]1925 }
1926 else
[47545]1927 rc = VERR_INVALID_PARAMETER;
1928
1929 /*
1930 * If this is the last write + we have really have written all data
1931 * we need to close the stdin pipe on our end and remove it from
1932 * the poll set.
1933 */
1934 if ( fPendingClose
[58029]1935 && cbBuf == cbWritten)
[44963]1936 {
[58029]1937 int rc2 = vgsvcGstCtrlProcessPollsetCloseInput(pThis, &pThis->hPipeStdInW);
[45697]1938 if (RT_SUCCESS(rc))
[47545]1939 rc = rc2;
1940 }
[45415]1941
[47545]1942 uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status to send back to the host. */
[58029]1943 uint32_t fFlags = 0; /* No flags at the moment. */
[47545]1944 if (RT_SUCCESS(rc))
1945 {
[58029]1946 VGSvcVerbose(4, "[PID %RU32]: Written %RU32 bytes input, CID=%RU32, fPendingClose=%RTbool\n",
1947 pThis->uPID, cbWritten, pHostCtx->uContextID, fPendingClose);
[47545]1948 uStatus = INPUT_STS_WRITTEN;
1949 }
1950 else
1951 {
1952 if (rc == VERR_BAD_PIPE)
1953 uStatus = INPUT_STS_TERMINATED;
1954 else if (rc == VERR_BUFFER_OVERFLOW)
1955 uStatus = INPUT_STS_OVERFLOW;
1956 /* else undefined */
1957 }
[38180]1958
[47545]1959 /*
1960 * If there was an error and we did not set the host status
1961 * yet, then do it now.
1962 */
1963 if ( RT_FAILURE(rc)
1964 && uStatus == INPUT_STS_UNDEFINED)
1965 {
1966 uStatus = INPUT_STS_ERROR;
[58029]1967 fFlags = rc; /* funny thing to call a "flag"... */
[47545]1968 }
1969 Assert(uStatus > INPUT_STS_UNDEFINED);
[38180]1970
[58029]1971 int rc2 = VbglR3GuestCtrlProcCbStatusInput(pHostCtx, pThis->uPID, uStatus, fFlags, (uint32_t)cbWritten);
[47545]1972 if (RT_SUCCESS(rc))
1973 rc = rc2;
[38180]1974
[47545]1975#ifdef DEBUG
[58029]1976 VGSvcVerbose(3, "[PID %RU32]: vgsvcGstCtrlProcessOnInput returned with rc=%Rrc\n", pThis->uPID, rc);
[47545]1977#endif
[92690]1978 return rc;
[47545]1979}
[45697]1980
[47545]1981
[58029]1982static DECLCALLBACK(int) vgsvcGstCtrlProcessOnOutput(PVBOXSERVICECTRLPROCESS pThis,
1983 const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
1984 uint32_t uHandle, uint32_t cbToRead, uint32_t fFlags)
[47545]1985{
1986 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1987 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1988
1989 const PVBOXSERVICECTRLSESSION pSession = pThis->pSession;
1990 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1991
1992 int rc;
1993
1994 uint32_t cbBuf = cbToRead;
1995 uint8_t *pvBuf = (uint8_t *)RTMemAlloc(cbBuf);
1996 if (pvBuf)
1997 {
[92707]1998 PRTPIPE phPipe = uHandle == GUEST_PROC_OUT_H_STDOUT
[58029]1999 ? &pThis->hPipeStdOutR
2000 : &pThis->hPipeStdErrR;
[47545]2001 AssertPtr(phPipe);
2002
2003 size_t cbRead = 0;
2004 if (*phPipe != NIL_RTPIPE)
2005 {
2006 rc = RTPipeRead(*phPipe, pvBuf, cbBuf, &cbRead);
2007 if (RT_FAILURE(rc))
2008 {
[92707]2009 RTPollSetRemove(pThis->hPollSet, uHandle == GUEST_PROC_OUT_H_STDERR
[47545]2010 ? VBOXSERVICECTRLPIPEID_STDERR : VBOXSERVICECTRLPIPEID_STDOUT);
2011 RTPipeClose(*phPipe);
2012 *phPipe = NIL_RTPIPE;
2013 if (rc == VERR_BROKEN_PIPE)
2014 rc = VINF_EOF;
[45604]2015 }
[47545]2016 }
2017 else
2018 rc = VINF_EOF;
[45697]2019
[47551]2020#ifdef DEBUG
[47545]2021 if (RT_SUCCESS(rc))
2022 {
[57659]2023 if ( pSession->fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT
[92707]2024 && ( uHandle == GUEST_PROC_OUT_H_STDOUT
2025 || uHandle == GUEST_PROC_OUT_H_STDOUT_DEPRECATED)
[47551]2026 )
[45697]2027 {
[83400]2028 rc = vgsvcGstCtrlProcessDbgDumpToFileF(pvBuf, cbRead, "VBoxService_Session%RU32_PID%RU32_StdOut.txt",
2029 pSession->StartupInfo.uSessionID, pThis->uPID);
[47545]2030 AssertRC(rc);
[45697]2031 }
[57659]2032 else if ( pSession->fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR
[92707]2033 && uHandle == GUEST_PROC_OUT_H_STDERR)
[47545]2034 {
[83400]2035 rc = vgsvcGstCtrlProcessDbgDumpToFileF(pvBuf, cbRead, "VBoxService_Session%RU32_PID%RU32_StdErr.txt",
2036 pSession->StartupInfo.uSessionID, pThis->uPID);
[47545]2037 AssertRC(rc);
2038 }
2039 }
2040#endif
[45697]2041
[47545]2042 if (RT_SUCCESS(rc))
2043 {
2044#ifdef DEBUG
[58029]2045 VGSvcVerbose(3, "[PID %RU32]: Read %RU32 bytes output: uHandle=%RU32, CID=%RU32, fFlags=%x\n",
2046 pThis->uPID, cbRead, uHandle, pHostCtx->uContextID, fFlags);
[47545]2047#endif
2048 /** Note: Don't convert/touch/modify/whatever the output data here! This might be binary
2049 * data which the host needs to work with -- so just pass through all data unfiltered! */
2050
2051 /* Note: Since the context ID is unique the request *has* to be completed here,
2052 * regardless whether we got data or not! Otherwise the waiting events
2053 * on the host never will get completed! */
[58029]2054 Assert((uint32_t)cbRead == cbRead);
2055 rc = VbglR3GuestCtrlProcCbOutput(pHostCtx, pThis->uPID, uHandle, fFlags, pvBuf, (uint32_t)cbRead);
[47545]2056 if ( RT_FAILURE(rc)
2057 && rc == VERR_NOT_FOUND) /* Not critical if guest PID is not found on the host (anymore). */
2058 rc = VINF_SUCCESS;
[38180]2059 }
[47545]2060
2061 RTMemFree(pvBuf);
[38180]2062 }
[47545]2063 else
2064 rc = VERR_NO_MEMORY;
[38180]2065
[47545]2066#ifdef DEBUG
[58029]2067 VGSvcVerbose(3, "[PID %RU32]: Reading output returned with rc=%Rrc\n", pThis->uPID, rc);
[47545]2068#endif
[92690]2069 return rc;
[47545]2070}
2071
2072
[58029]2073static DECLCALLBACK(int) vgsvcGstCtrlProcessOnTerm(PVBOXSERVICECTRLPROCESS pThis)
[47545]2074{
2075 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2076
2077 if (!ASMAtomicXchgBool(&pThis->fShutdown, true))
[58029]2078 VGSvcVerbose(3, "[PID %RU32]: Setting shutdown flag ...\n", pThis->uPID);
[47545]2079
[92690]2080 return VINF_SUCCESS;
[47545]2081}
2082
2083
[58029]2084static int vgsvcGstCtrlProcessRequestExV(PVBOXSERVICECTRLPROCESS pProcess, const PVBGLR3GUESTCTRLCMDCTX pHostCtx, bool fAsync,
[88812]2085 RTMSINTERVAL uTimeoutMS, PFNRT pfnFunction, unsigned cArgs, va_list Args)
[47545]2086{
[62850]2087 RT_NOREF1(pHostCtx);
[47545]2088 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2089 /* pHostCtx is optional. */
2090 AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
2091 if (!fAsync)
2092 AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
2093
[58029]2094 int rc = vgsvcGstCtrlProcessLock(pProcess);
[47545]2095 if (RT_SUCCESS(rc))
2096 {
2097#ifdef DEBUG
[58029]2098 VGSvcVerbose(3, "[PID %RU32]: vgsvcGstCtrlProcessRequestExV fAsync=%RTbool, uTimeoutMS=%RU32, cArgs=%u\n",
2099 pProcess->uPID, fAsync, uTimeoutMS, cArgs);
[47545]2100#endif
[58029]2101 uint32_t fFlags = RTREQFLAGS_IPRT_STATUS;
[47545]2102 if (fAsync)
2103 {
2104 Assert(uTimeoutMS == 0);
[58029]2105 fFlags |= RTREQFLAGS_NO_WAIT;
[47545]2106 }
2107
[88812]2108 PRTREQ hReq = NIL_RTREQ;
2109 rc = RTReqQueueCallV(pProcess->hReqQueue, &hReq, uTimeoutMS, fFlags, pfnFunction, cArgs, Args);
2110 RTReqRelease(hReq);
[47545]2111 if (RT_SUCCESS(rc))
2112 {
2113 /* Wake up the process' notification pipe to get
2114 * the request being processed. */
[70390]2115 Assert(pProcess->hNotificationPipeW != NIL_RTPIPE || pProcess->fShutdown /* latter in case of race */);
[47545]2116 size_t cbWritten = 0;
2117 rc = RTPipeWrite(pProcess->hNotificationPipeW, "i", 1, &cbWritten);
2118 if ( RT_SUCCESS(rc)
2119 && cbWritten != 1)
2120 {
[58029]2121 VGSvcError("[PID %RU32]: Notification pipe got %zu bytes instead of 1\n",
[47545]2122 pProcess->uPID, cbWritten);
2123 }
2124 else if (RT_UNLIKELY(RT_FAILURE(rc)))
[58029]2125 VGSvcError("[PID %RU32]: Writing to notification pipe failed, rc=%Rrc\n",
[47545]2126 pProcess->uPID, rc);
2127 }
2128 else
[58029]2129 VGSvcError("[PID %RU32]: RTReqQueueCallV failed, rc=%Rrc\n",
[47545]2130 pProcess->uPID, rc);
2131
[58029]2132 int rc2 = vgsvcGstCtrlProcessUnlock(pProcess);
[47545]2133 if (RT_SUCCESS(rc))
2134 rc = rc2;
2135 }
2136
2137#ifdef DEBUG
[58029]2138 VGSvcVerbose(3, "[PID %RU32]: vgsvcGstCtrlProcessRequestExV returned rc=%Rrc\n", pProcess->uPID, rc);
[47545]2139#endif
[38180]2140 return rc;
2141}
2142
[47545]2143
[58029]2144static int vgsvcGstCtrlProcessRequestAsync(PVBOXSERVICECTRLPROCESS pProcess, const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
2145 PFNRT pfnFunction, unsigned cArgs, ...)
[47545]2146{
2147 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2148 /* pHostCtx is optional. */
2149 AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
2150
2151 va_list va;
2152 va_start(va, cArgs);
[58029]2153 int rc = vgsvcGstCtrlProcessRequestExV(pProcess, pHostCtx, true /* fAsync */, 0 /* uTimeoutMS */,
[88812]2154 pfnFunction, cArgs, va);
[47545]2155 va_end(va);
2156
2157 return rc;
2158}
2159
2160
[58029]2161#if 0 /* unused */
2162static int vgsvcGstCtrlProcessRequestWait(PVBOXSERVICECTRLPROCESS pProcess, const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
[88812]2163 RTMSINTERVAL uTimeoutMS, PFNRT pfnFunction, unsigned cArgs, ...)
[47545]2164{
2165 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2166 /* pHostCtx is optional. */
2167 AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
2168
2169 va_list va;
2170 va_start(va, cArgs);
[58029]2171 int rc = vgsvcGstCtrlProcessRequestExV(pProcess, pHostCtx, false /* fAsync */, uTimeoutMS,
[88812]2172 pfnFunction, cArgs, va);
[47545]2173 va_end(va);
2174
2175 return rc;
2176}
[58029]2177#endif
[47545]2178
2179
[58029]2180int VGSvcGstCtrlProcessHandleInput(PVBOXSERVICECTRLPROCESS pProcess, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
2181 bool fPendingClose, void *pvBuf, uint32_t cbBuf)
[47545]2182{
[70390]2183 if (!ASMAtomicReadBool(&pProcess->fShutdown) && !ASMAtomicReadBool(&pProcess->fStopped))
[58029]2184 return vgsvcGstCtrlProcessRequestAsync(pProcess, pHostCtx, (PFNRT)vgsvcGstCtrlProcessOnInput,
2185 5 /* cArgs */, pProcess, pHostCtx, fPendingClose, pvBuf, cbBuf);
[47545]2186
[58029]2187 return vgsvcGstCtrlProcessOnInput(pProcess, pHostCtx, fPendingClose, pvBuf, cbBuf);
[47545]2188}
2189
2190
[58029]2191int VGSvcGstCtrlProcessHandleOutput(PVBOXSERVICECTRLPROCESS pProcess, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
2192 uint32_t uHandle, uint32_t cbToRead, uint32_t fFlags)
[47545]2193{
[70390]2194 if (!ASMAtomicReadBool(&pProcess->fShutdown) && !ASMAtomicReadBool(&pProcess->fStopped))
[58029]2195 return vgsvcGstCtrlProcessRequestAsync(pProcess, pHostCtx, (PFNRT)vgsvcGstCtrlProcessOnOutput,
2196 5 /* cArgs */, pProcess, pHostCtx, uHandle, cbToRead, fFlags);
[47545]2197
[58029]2198 return vgsvcGstCtrlProcessOnOutput(pProcess, pHostCtx, uHandle, cbToRead, fFlags);
[47545]2199}
2200
2201
[58029]2202int VGSvcGstCtrlProcessHandleTerm(PVBOXSERVICECTRLPROCESS pProcess)
[47545]2203{
[70390]2204 if (!ASMAtomicReadBool(&pProcess->fShutdown) && !ASMAtomicReadBool(&pProcess->fStopped))
[58029]2205 return vgsvcGstCtrlProcessRequestAsync(pProcess, NULL /* pHostCtx */, (PFNRT)vgsvcGstCtrlProcessOnTerm,
2206 1 /* cArgs */, pProcess);
[47545]2207
[58029]2208 return vgsvcGstCtrlProcessOnTerm(pProcess);
[47545]2209}
2210
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use