VirtualBox

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

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

scm copyright and license note update

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