VirtualBox

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

Last change on this file since 106061 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette