VirtualBox

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

最後變更 在這個檔案從92682是 92668,由 vboxsync 提交於 3 年 前

VBoxService: Added missing error handling for UPN formatting in vgsvcGstCtrlProcessCreateProcess. [build fix]

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

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