VirtualBox

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

Last change on this file since 96663 was 96502, checked in by vboxsync, 3 years ago

Add/VBoxService: Replaced GetVersionEx calls with RTSystemGetNtVersion, so we don't need the IPRT fallback code for ancient NT versions that doesn't have that API. Checked out the KB970910 description against what we're doing with the API, and found that it shouldn't affect us, so RDP detection can run on all systems that have a WTSQuerySessionInformation API. Corrected QueryFullProcessImageNameW/GetModuleFileNameExW calls. bugref:10162

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

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