VirtualBox

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

Last change on this file since 72097 was 70390, checked in by vboxsync, 7 years ago

VBoxServiceControlProcess.cpp: Don't try send async requests to a thread that already quit because the process didn't even start (like file not found). Fixed confused documentation on hNotificationPipeW, leaving confused (but harmless) cleanup code following return from vgsvcGstCtrlProcessProcLoop().

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

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