VirtualBox

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

Last change on this file since 45604 was 45604, checked in by vboxsync, 12 years ago

VBoxService/GuestCtrl: Added asynchronous request handling, more fixes for testcases.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 70.7 KB
Line 
1/* $Id: VBoxServiceControlProcess.cpp 45604 2013-04-18 12:21:20Z vboxsync $ */
2/** @file
3 * VBoxServiceControlThread - Guest process handling.
4 */
5
6/*
7 * Copyright (C) 2012-2013 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
43using namespace guestControl;
44
45/*******************************************************************************
46* Internal Functions *
47*******************************************************************************/
48static int gstcntlProcessAssignPID(PVBOXSERVICECTRLPROCESS pThread, uint32_t uPID);
49static int gstcntlProcessRequestCancel(PVBOXSERVICECTRLREQUEST pThread);
50static int gstcntlProcessSetupPipe(const char *pszHowTo, int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe);
51
52/**
53 * Initialies the passed in thread data structure with the parameters given.
54 *
55 * @return IPRT status code.
56 * @param pProcess Process to initialize.
57 * @param pSession Guest session the process is bound to.
58 * @param pStartupInfo Startup information.
59 * @param u32ContextID The context ID bound to this request / command.
60 */
61static int gstcntlProcessInit(PVBOXSERVICECTRLPROCESS pProcess,
62 const PVBOXSERVICECTRLSESSION pSession,
63 const PVBOXSERVICECTRLPROCSTARTUPINFO pStartupInfo,
64 uint32_t u32ContextID)
65{
66 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
67 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
68 AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER);
69
70 /* General stuff. */
71 pProcess->pSession = pSession;
72 pProcess->pAnchor = NULL;
73 pProcess->Node.pPrev = NULL;
74 pProcess->Node.pNext = NULL;
75
76 pProcess->fShutdown = false;
77 pProcess->fStarted = false;
78 pProcess->fStopped = false;
79
80 pProcess->uContextID = u32ContextID;
81 /* ClientID will be assigned when thread is started; every guest
82 * process has its own client ID to detect crashes on a per-guest-process
83 * level. */
84
85 int rc = RTCritSectInit(&pProcess->CritSect);
86 if (RT_FAILURE(rc))
87 return rc;
88
89 pProcess->uPID = 0; /* Don't have a PID yet. */
90 pProcess->pRequest = NULL; /* No request assigned yet. */
91
92 /* Copy over startup info. */
93 memcpy(&pProcess->StartupInfo, pStartupInfo, sizeof(VBOXSERVICECTRLPROCSTARTUPINFO));
94
95 /* Adjust timeout value. */
96 if ( pProcess->StartupInfo.uTimeLimitMS == UINT32_MAX
97 || pProcess->StartupInfo.uTimeLimitMS == 0)
98 pProcess->StartupInfo.uTimeLimitMS = RT_INDEFINITE_WAIT;
99
100 if (RT_FAILURE(rc)) /* Clean up on failure. */
101 GstCntlProcessFree(pProcess);
102 return rc;
103}
104
105
106/**
107 * Frees a guest process.
108 *
109 * @return IPRT status code.
110 * @param pProcess Guest process to free.
111 */
112int GstCntlProcessFree(PVBOXSERVICECTRLPROCESS pProcess)
113{
114 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
115
116 VBoxServiceVerbose(3, "[PID %RU32]: Freeing ...\n",
117 pProcess->uPID);
118
119
120 /*
121 * Destroy other thread data.
122 */
123 if (RTCritSectIsInitialized(&pProcess->CritSect))
124 RTCritSectDelete(&pProcess->CritSect);
125
126 /*
127 * Destroy thread structure as final step.
128 */
129 RTMemFree(pProcess);
130 pProcess = NULL;
131
132 return VINF_SUCCESS;
133}
134
135
136/**
137 * Signals a guest process thread that we want it to shut down in
138 * a gentle way.
139 *
140 * @return IPRT status code.
141 * @param pProcess Process to stop.
142 */
143int GstCntlProcessStop(PVBOXSERVICECTRLPROCESS pProcess)
144{
145 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
146
147 VBoxServiceVerbose(3, "[PID %RU32]: Stopping ...\n",
148 pProcess->uPID);
149
150 int rc = RTCritSectEnter(&pProcess->CritSect);
151 if (RT_SUCCESS(rc))
152 {
153 if (pProcess->pRequest)
154 {
155 rc = gstcntlProcessRequestCancel(pProcess->pRequest);
156 if (RT_FAILURE(rc))
157 VBoxServiceError("[PID %RU32]: Cancelling current request failed, rc=%Rrc\n",
158 pProcess->uPID, rc);
159 }
160
161 /* Do *not* set pThread->fShutdown or other stuff here!
162 * The guest thread loop will do that as soon as it processes the quit message. */
163
164 PVBOXSERVICECTRLREQUEST pRequest;
165 rc = GstCntlProcessRequestAlloc(&pRequest, VBOXSERVICECTRLREQUEST_PROC_TERM);
166 if (RT_SUCCESS(rc))
167 {
168 rc = GstCntlProcessPerform(pProcess, pRequest,
169 true /* Async */);
170 if (RT_FAILURE(rc))
171 VBoxServiceVerbose(3, "[PID %RU32]: Sending termination request failed with rc=%Rrc\n",
172 pProcess->uPID, rc);
173 /* Deletion of pRequest will be done on request completion. */
174 }
175
176 int rc2 = RTCritSectLeave(&pProcess->CritSect);
177 AssertRC(rc2);
178 }
179
180 return rc;
181}
182
183
184/**
185 * Releases (unlocks) a previously locked guest process.
186 *
187 * @param pProcess Process to unlock.
188 */
189void GstCntlProcessRelease(const PVBOXSERVICECTRLPROCESS pProcess)
190{
191 AssertPtrReturnVoid(pProcess);
192
193 int rc = RTCritSectLeave(&pProcess->CritSect);
194 AssertRC(rc);
195}
196
197
198/**
199 * Wait for a guest process thread to shut down.
200 * Note: Caller is responsible for locking!
201 *
202 * @return IPRT status code.
203 * @param pProcess Process to wait shutting down for.
204 * @param RTMSINTERVAL Timeout in ms to wait for shutdown.
205 * @param pRc Where to store the thread's return code. Optional.
206 */
207int GstCntlProcessWait(const PVBOXSERVICECTRLPROCESS pProcess,
208 RTMSINTERVAL msTimeout, int *pRc)
209{
210 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
211 /* pRc is optional. */
212
213 AssertMsgReturn(ASMAtomicReadBool(&pProcess->fStarted),
214 ("Tried to wait on guest process=%p which has not been started yet\n",
215 pProcess), VERR_INVALID_PARAMETER);
216
217 /* Guest process already has been stopped, no need to wait. */
218 if (ASMAtomicReadBool(&pProcess->fStopped))
219 return VINF_SUCCESS;
220
221 VBoxServiceVerbose(2, "[PID %RU32]: Waiting for shutdown (%RU32ms) ...\n",
222 pProcess->uPID, msTimeout);
223
224 /* Wait a bit ... */
225 int rcThread;
226 Assert(pProcess->Thread != NIL_RTTHREAD);
227 int rc = RTThreadWait(pProcess->Thread, msTimeout, &rcThread);
228 if (RT_FAILURE(rc))
229 {
230 VBoxServiceError("[PID %RU32]: Waiting for shutting down thread returned error rc=%Rrc\n",
231 pProcess->uPID, rc);
232 }
233 else
234 {
235 VBoxServiceVerbose(3, "[PID %RU32]: Thread reported exit code=%Rrc\n",
236 pProcess->uPID, rcThread);
237 if (pRc)
238 *pRc = rcThread;
239 }
240
241 return rc;
242}
243
244
245/**
246 * Closes the stdin pipe of a guest process.
247 *
248 * @return IPRT status code.
249 * @param hPollSet The polling set.
250 * @param phStdInW The standard input pipe handle.
251 */
252static int gstcntlProcessCloseStdIn(RTPOLLSET hPollSet, PRTPIPE phStdInW)
253{
254 AssertPtrReturn(phStdInW, VERR_INVALID_POINTER);
255
256 int rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN);
257 if (rc != VERR_POLL_HANDLE_ID_NOT_FOUND)
258 AssertRC(rc);
259
260 if (*phStdInW != NIL_RTPIPE)
261 {
262 rc = RTPipeClose(*phStdInW);
263 AssertRC(rc);
264 *phStdInW = NIL_RTPIPE;
265 }
266
267 return rc;
268}
269
270
271/**
272 * Handle an error event on standard input.
273 *
274 * @return IPRT status code.
275 * @param hPollSet The polling set.
276 * @param fPollEvt The event mask returned by RTPollNoResume.
277 * @param phStdInW The standard input pipe handle.
278 */
279static int gstcntlProcessHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW)
280{
281 NOREF(fPollEvt);
282
283 return gstcntlProcessCloseStdIn(hPollSet, phStdInW);
284}
285
286
287/**
288 * Handle pending output data or error on standard out or standard error.
289 *
290 * @returns IPRT status code from client send.
291 * @param hPollSet The polling set.
292 * @param fPollEvt The event mask returned by RTPollNoResume.
293 * @param phPipeR The pipe handle.
294 * @param idPollHnd The pipe ID to handle.
295 *
296 */
297static int gstcntlProcessHandleOutputError(RTPOLLSET hPollSet, uint32_t fPollEvt,
298 PRTPIPE phPipeR, uint32_t idPollHnd)
299{
300 AssertPtrReturn(phPipeR, VERR_INVALID_POINTER);
301
302#ifdef DEBUG
303 VBoxServiceVerbose(4, "gstcntlProcessHandleOutputError: fPollEvt=0x%x, idPollHnd=%u\n",
304 fPollEvt, idPollHnd);
305#endif
306
307 /* Remove pipe from poll set. */
308 int rc2 = RTPollSetRemove(hPollSet, idPollHnd);
309 AssertMsg(RT_SUCCESS(rc2) || rc2 == VERR_POLL_HANDLE_ID_NOT_FOUND, ("%Rrc\n", rc2));
310
311 bool fClosePipe = true; /* By default close the pipe. */
312
313 /* Check if there's remaining data to read from the pipe. */
314 size_t cbReadable;
315 rc2 = RTPipeQueryReadable(*phPipeR, &cbReadable);
316 if ( RT_SUCCESS(rc2)
317 && cbReadable)
318 {
319 VBoxServiceVerbose(3, "gstcntlProcessHandleOutputError: idPollHnd=%RU32 has %zu bytes left, vetoing close\n",
320 idPollHnd, cbReadable);
321
322 /* Veto closing the pipe yet because there's still stuff to read
323 * from the pipe. This can happen on UNIX-y systems where on
324 * error/hangup there still can be data to be read out. */
325 fClosePipe = false;
326 }
327 else
328 VBoxServiceVerbose(3, "gstcntlProcessHandleOutputError: idPollHnd=%RU32 will be closed\n",
329 idPollHnd);
330
331 if ( *phPipeR != NIL_RTPIPE
332 && fClosePipe)
333 {
334 rc2 = RTPipeClose(*phPipeR);
335 AssertRC(rc2);
336 *phPipeR = NIL_RTPIPE;
337 }
338
339 return VINF_SUCCESS;
340}
341
342
343/**
344 * Handle pending output data or error on standard out or standard error.
345 *
346 * @returns IPRT status code from client send.
347 * @param hPollSet The polling set.
348 * @param fPollEvt The event mask returned by RTPollNoResume.
349 * @param phPipeR The pipe handle.
350 * @param idPollHnd The pipe ID to handle.
351 *
352 */
353static int gstcntlProcessHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt,
354 PRTPIPE phPipeR, uint32_t idPollHnd)
355{
356#if 0
357 VBoxServiceVerbose(4, "GstCntlProcessHandleOutputEvent: fPollEvt=0x%x, idPollHnd=%u\n",
358 fPollEvt, idPollHnd);
359#endif
360
361 int rc = VINF_SUCCESS;
362
363#ifdef DEBUG
364 size_t cbReadable;
365 rc = RTPipeQueryReadable(*phPipeR, &cbReadable);
366 if ( RT_SUCCESS(rc)
367 && cbReadable)
368 {
369 VBoxServiceVerbose(4, "gstcntlProcessHandleOutputEvent: cbReadable=%ld\n",
370 cbReadable);
371 }
372#endif
373
374#if 0
375 //if (fPollEvt & RTPOLL_EVT_READ)
376 {
377 size_t cbRead = 0;
378 uint8_t byData[_64K];
379 rc = RTPipeRead(*phPipeR,
380 byData, sizeof(byData), &cbRead);
381 VBoxServiceVerbose(4, "GstCntlProcessHandleOutputEvent cbRead=%u, rc=%Rrc\n",
382 cbRead, rc);
383
384 /* Make sure we go another poll round in case there was too much data
385 for the buffer to hold. */
386 fPollEvt &= RTPOLL_EVT_ERROR;
387 }
388#endif
389
390 if (fPollEvt & RTPOLL_EVT_ERROR)
391 rc = gstcntlProcessHandleOutputError(hPollSet, fPollEvt,
392 phPipeR, idPollHnd);
393 return rc;
394}
395
396
397/**
398 * Completes the given request. After returning pRequest won't be valid
399 * anymore!
400 *
401 * @return IPRT status code.
402 * @param pRequest Pointer to request to signal.
403 * @param rc rc to set request result to.
404 */
405static int gstcntlProcessRequestComplete(PVBOXSERVICECTRLREQUEST pRequest, int rc)
406{
407 AssertPtrReturn(pRequest, VERR_INVALID_POINTER);
408
409 int rc2;
410 if (!pRequest->fAsync)
411 {
412 /* Assign overall result. */
413 pRequest->rc = rc;
414
415#ifdef DEBUG_andy
416 VBoxServiceVerbose(4, "Handled req=%RU32, CID=%RU32, rc=%Rrc, cbData=%RU32, pvData=%p\n",
417 pRequest->enmType, pRequest->uCID, pRequest->rc,
418 pRequest->cbData, pRequest->pvData);
419#endif
420 /* Signal waiters. */
421 rc2 = RTSemEventMultiSignal(pRequest->Event);
422 AssertRC(rc2);
423
424 pRequest = NULL;
425 }
426 else
427 {
428#ifdef DEBUG_andy
429 VBoxServiceVerbose(4, "Deleting async req=%RU32, CID=%RU32, rc=%Rrc, cbData=%RU32, pvData=%p\n",
430 pRequest->enmType, pRequest->uCID, pRequest->rc,
431 pRequest->cbData, pRequest->pvData);
432#endif
433 GstCntlProcessRequestFree(pRequest);
434 rc2 = VINF_SUCCESS;
435 }
436
437 return rc2;
438}
439
440
441static int gstcntlProcessRequestHandle(RTPOLLSET hPollSet, uint32_t fPollEvt,
442 PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR,
443 PVBOXSERVICECTRLPROCESS pProcess, PVBOXSERVICECTRLREQUEST pRequest)
444{
445 AssertPtrReturn(phStdInW, VERR_INVALID_POINTER);
446 AssertPtrReturn(phStdOutR, VERR_INVALID_POINTER);
447 AssertPtrReturn(phStdErrR, VERR_INVALID_POINTER);
448 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
449 AssertPtrReturn(pRequest, VERR_INVALID_POINTER);
450
451 VBoxServiceVerbose(4, "[PID %RU32]: Handling pRequest=%p\n",
452 pProcess->uPID, pRequest);
453
454 /* Drain the notification pipe. */
455 uint8_t abBuf[8];
456 size_t cbIgnore;
457 int rc = RTPipeRead(pProcess->hNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
458 if (RT_FAILURE(rc))
459 VBoxServiceError("Draining IPC notification pipe failed with rc=%Rrc\n", rc);
460
461 bool fDefer = false; /* Whether the request completion should be deferred or not. */
462 int rcReq = VINF_SUCCESS; /* Actual request result. */
463
464 switch (pRequest->enmType)
465 {
466 case VBOXSERVICECTRLREQUEST_PROC_STDIN:
467 case VBOXSERVICECTRLREQUEST_PROC_STDIN_EOF:
468 {
469 size_t cbWritten = 0;
470 if (pRequest->cbData)
471 {
472 AssertPtrReturn(pRequest->pvData, VERR_INVALID_POINTER);
473 if (*phStdInW != NIL_RTPIPE)
474 {
475 rcReq = RTPipeWrite(*phStdInW,
476 pRequest->pvData, pRequest->cbData, &cbWritten);
477 }
478 else
479 rcReq = VINF_EOF;
480 }
481
482 /*
483 * If this is the last write + we have really have written all data
484 * we need to close the stdin pipe on our end and remove it from
485 * the poll set.
486 */
487 if ( pRequest->enmType == VBOXSERVICECTRLREQUEST_PROC_STDIN_EOF
488 && pRequest->cbData == cbWritten)
489 {
490 rc = gstcntlProcessCloseStdIn(hPollSet, phStdInW);
491 }
492
493 /* Report back actual data written (if any). */
494 pRequest->cbData = cbWritten;
495 break;
496 }
497
498 case VBOXSERVICECTRLREQUEST_PROC_STDOUT:
499 case VBOXSERVICECTRLREQUEST_PROC_STDERR:
500 {
501 AssertPtrReturn(pRequest->pvData, VERR_INVALID_POINTER);
502 AssertReturn(pRequest->cbData, VERR_INVALID_PARAMETER);
503
504 PRTPIPE pPipeR = pRequest->enmType == VBOXSERVICECTRLREQUEST_PROC_STDERR
505 ? phStdErrR : phStdOutR;
506 AssertPtr(pPipeR);
507
508 size_t cbRead = 0;
509 if (*pPipeR != NIL_RTPIPE)
510 {
511 rcReq = RTPipeRead(*pPipeR,
512 pRequest->pvData, pRequest->cbData, &cbRead);
513 if (RT_FAILURE(rcReq))
514 {
515 RTPollSetRemove(hPollSet, pRequest->enmType == VBOXSERVICECTRLREQUEST_PROC_STDERR
516 ? VBOXSERVICECTRLPIPEID_STDERR : VBOXSERVICECTRLPIPEID_STDOUT);
517 RTPipeClose(*pPipeR);
518 *pPipeR = NIL_RTPIPE;
519 if (rcReq == VERR_BROKEN_PIPE)
520 rcReq = VINF_EOF;
521 }
522 }
523 else
524 rcReq = VINF_EOF;
525
526 /* Report back actual data read (if any). */
527 pRequest->cbData = cbRead;
528 break;
529 }
530
531 case VBOXSERVICECTRLREQUEST_PROC_TERM:
532 ASMAtomicXchgBool(&pProcess->fShutdown, true);
533 fDefer = true;
534 break;
535
536 default:
537 rcReq = VERR_NOT_IMPLEMENTED;
538 break;
539 }
540
541 if ( RT_FAILURE(rc)
542 || !fDefer)
543 {
544 rc = gstcntlProcessRequestComplete(pRequest,
545 RT_SUCCESS(rc) ? rcReq : rc);
546 }
547 else /* Completing the request defered. */
548 rc = VINF_AIO_TASK_PENDING; /** @todo Find an own rc! */
549
550 return rc;
551}
552
553
554/**
555 * Execution loop which runs in a dedicated per-started-process thread and
556 * handles all pipe input/output and signalling stuff.
557 *
558 * @return IPRT status code.
559 * @param pProcess The guest process to handle.
560 * @param hProcess The actual process handle.
561 * @param cMsTimeout Time limit (in ms) of the process' life time.
562 * @param hPollSet The poll set to use.
563 * @param hStdInW Handle to the process' stdin write end.
564 * @param hStdOutR Handle to the process' stdout read end.
565 * @param hStdErrR Handle to the process' stderr read end.
566 */
567static int gstcntlProcessProcLoop(PVBOXSERVICECTRLPROCESS pProcess,
568 RTPROCESS hProcess, RTPOLLSET hPollSet,
569 PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR)
570{
571 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
572 AssertPtrReturn(phStdInW, VERR_INVALID_PARAMETER);
573 /* Rest is optional. */
574
575 int rc;
576 int rc2;
577 uint64_t const uMsStart = RTTimeMilliTS();
578 RTPROCSTATUS ProcessStatus = { 254, RTPROCEXITREASON_ABEND };
579 bool fProcessAlive = true;
580 bool fProcessTimedOut = false;
581 uint64_t MsProcessKilled = UINT64_MAX;
582 RTMSINTERVAL const cMsPollBase = *phStdInW != NIL_RTPIPE
583 ? 100 /* Need to poll for input. */
584 : 1000; /* Need only poll for process exit and aborts. */
585 RTMSINTERVAL cMsPollCur = 0;
586
587 /*
588 * Assign PID to thread data.
589 * Also check if there already was a thread with the same PID and shut it down -- otherwise
590 * the first (stale) entry will be found and we get really weird results!
591 */
592 rc = gstcntlProcessAssignPID(pProcess, hProcess);
593 if (RT_FAILURE(rc))
594 {
595 VBoxServiceError("Unable to assign PID=%u, to new thread, rc=%Rrc\n",
596 hProcess, rc);
597 return rc;
598 }
599
600 /*
601 * Before entering the loop, tell the host that we've started the guest
602 * and that it's now OK to send input to the process.
603 */
604 VBoxServiceVerbose(2, "[PID %RU32]: Process \"%s\" started, CID=%u, User=%s, cMsTimeout=%RU32\n",
605 pProcess->uPID, pProcess->StartupInfo.szCmd, pProcess->uContextID,
606 pProcess->StartupInfo.szUser, pProcess->StartupInfo.uTimeLimitMS);
607 VBGLR3GUESTCTRLCMDCTX ctx = { pProcess->uClientID, pProcess->uContextID };
608 rc = VbglR3GuestCtrlProcCbStatus(&ctx,
609 pProcess->uPID, PROC_STS_STARTED, 0 /* u32Flags */,
610 NULL /* pvData */, 0 /* cbData */);
611
612 /*
613 * Process input, output, the test pipe and client requests.
614 */
615 PVBOXSERVICECTRLREQUEST pReq = NULL;
616 while ( RT_SUCCESS(rc)
617 && RT_UNLIKELY(!pProcess->fShutdown))
618 {
619 /*
620 * Wait/Process all pending events.
621 */
622 uint32_t idPollHnd;
623 uint32_t fPollEvt;
624 rc2 = RTPollNoResume(hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
625 if (pProcess->fShutdown)
626 continue;
627
628 cMsPollCur = 0; /* No rest until we've checked everything. */
629
630 if (RT_SUCCESS(rc2))
631 {
632 switch (idPollHnd)
633 {
634 case VBOXSERVICECTRLPIPEID_STDIN:
635 rc = gstcntlProcessHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW);
636 break;
637
638 case VBOXSERVICECTRLPIPEID_STDOUT:
639 rc = gstcntlProcessHandleOutputEvent(hPollSet, fPollEvt,
640 phStdOutR, idPollHnd);
641 break;
642 case VBOXSERVICECTRLPIPEID_STDERR:
643 rc = gstcntlProcessHandleOutputEvent(hPollSet, fPollEvt,
644 phStdErrR, idPollHnd);
645 break;
646
647 case VBOXSERVICECTRLPIPEID_IPC_NOTIFY:
648 pReq = pProcess->pRequest; /** @todo Implement request queue. */
649 rc = gstcntlProcessRequestHandle(hPollSet, fPollEvt,
650 phStdInW, phStdOutR, phStdErrR,
651 pProcess, pReq);
652 if (rc == VINF_AIO_TASK_PENDING)
653 VBoxServiceVerbose(4, "[PID %RU32]: pRequest=%p will be handled deferred\n",
654 pProcess->uPID, pReq);
655 break;
656
657 default:
658 AssertMsgFailed(("Unknown idPollHnd=%RU32\n", idPollHnd));
659 break;
660 }
661
662 if (RT_FAILURE(rc) || rc == VINF_EOF)
663 break; /* Abort command, or client dead or something. */
664 }
665#ifdef DEBUG_andy
666 VBoxServiceVerbose(4, "[PID %RU32]: Polling done, pollRc=%Rrc, pollCnt=%u, idPollHnd=%RU32, rc=%Rrc, fProcessAlive=%RTbool, fShutdown=%RTbool\n",
667 pProcess->uPID, rc2, RTPollSetGetCount(hPollSet), idPollHnd, rc, fProcessAlive, pProcess->fShutdown);
668#endif
669 if (RT_UNLIKELY(pProcess->fShutdown))
670 break; /* We were asked to shutdown. */
671
672 /*
673 * Check for process death.
674 */
675 if (fProcessAlive)
676 {
677 rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
678#ifdef DEBUG_andy
679 VBoxServiceVerbose(4, "[PID %RU32]: RTProcWaitNoResume=%Rrc, stdOut=%s, stdErrR=%s\n",
680 pProcess->uPID, rc2,
681 *phStdOutR == NIL_RTPIPE ? "closed" : "open",
682 *phStdErrR == NIL_RTPIPE ? "closed" : "open");
683#endif
684 if (RT_SUCCESS_NP(rc2))
685 {
686 fProcessAlive = false;
687 continue;
688 }
689 if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
690 continue;
691 if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
692 {
693 fProcessAlive = false;
694 ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
695 ProcessStatus.iStatus = 255;
696 AssertFailed();
697 }
698 else
699 AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
700 }
701
702 /*
703 * If the process has terminated and all output has been consumed,
704 * we should be heading out.
705 */
706 if ( !fProcessAlive
707 && *phStdOutR == NIL_RTPIPE
708 && *phStdErrR == NIL_RTPIPE)
709 break;
710
711 /*
712 * Check for timed out, killing the process.
713 */
714 uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
715 if (pProcess->StartupInfo.uTimeLimitMS != RT_INDEFINITE_WAIT)
716 {
717 uint64_t u64Now = RTTimeMilliTS();
718 uint64_t cMsElapsed = u64Now - uMsStart;
719 if (cMsElapsed >= pProcess->StartupInfo.uTimeLimitMS)
720 {
721 VBoxServiceVerbose(3, "[PID %RU32]: Timed out (%RU32ms elapsed > %RU32ms timeout), killing ...\n",
722 pProcess->uPID, cMsElapsed, pProcess->StartupInfo.uTimeLimitMS);
723
724 fProcessTimedOut = true;
725 if ( MsProcessKilled == UINT64_MAX
726 || u64Now - MsProcessKilled > 1000)
727 {
728 if (u64Now - MsProcessKilled > 20*60*1000)
729 break; /* Give up after 20 mins. */
730 RTProcTerminate(hProcess);
731 MsProcessKilled = u64Now;
732 continue;
733 }
734 cMilliesLeft = 10000;
735 }
736 else
737 cMilliesLeft = pProcess->StartupInfo.uTimeLimitMS - (uint32_t)cMsElapsed;
738 }
739
740 /* Reset the polling interval since we've done all pending work. */
741 cMsPollCur = fProcessAlive
742 ? cMsPollBase
743 : RT_MS_1MIN;
744 if (cMilliesLeft < cMsPollCur)
745 cMsPollCur = cMilliesLeft;
746 }
747
748 rc2 = RTCritSectEnter(&pProcess->CritSect);
749 if (RT_SUCCESS(rc2))
750 {
751 ASMAtomicXchgBool(&pProcess->fShutdown, true);
752
753 rc2 = RTCritSectLeave(&pProcess->CritSect);
754 AssertRC(rc2);
755 }
756
757 /*
758 * Try kill the process if it's still alive at this point.
759 */
760 if (fProcessAlive)
761 {
762 if (MsProcessKilled == UINT64_MAX)
763 {
764 VBoxServiceVerbose(3, "[PID %RU32]: Is still alive and not killed yet\n",
765 pProcess->uPID);
766
767 MsProcessKilled = RTTimeMilliTS();
768 RTProcTerminate(hProcess);
769 RTThreadSleep(500);
770 }
771
772 for (size_t i = 0; i < 10; i++)
773 {
774 VBoxServiceVerbose(4, "[PID %RU32]: Kill attempt %d/10: Waiting to exit ...\n",
775 pProcess->uPID, i + 1);
776 rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
777 if (RT_SUCCESS(rc2))
778 {
779 VBoxServiceVerbose(4, "[PID %RU32]: Kill attempt %d/10: Exited\n",
780 pProcess->uPID, i + 1);
781 fProcessAlive = false;
782 break;
783 }
784 if (i >= 5)
785 {
786 VBoxServiceVerbose(4, "[PID %RU32]: Kill attempt %d/10: Trying to terminate ...\n",
787 pProcess->uPID, i + 1);
788 RTProcTerminate(hProcess);
789 }
790 RTThreadSleep(i >= 5 ? 2000 : 500);
791 }
792
793 if (fProcessAlive)
794 VBoxServiceVerbose(3, "[PID %RU32]: Could not be killed\n", pProcess->uPID);
795
796 if ( pReq /* Handle deferred termination request. */
797 && pReq->enmType == VBOXSERVICECTRLREQUEST_PROC_TERM)
798 {
799 rc2 = gstcntlProcessRequestComplete(pReq,
800 fProcessAlive ? VINF_SUCCESS : VERR_PROCESS_RUNNING);
801 AssertRC(rc2);
802 }
803 else if (pReq)
804 AssertMsgFailed(("Unable to handle unknown deferred request (type: %RU32)\n", pReq->enmType));
805 }
806
807 /*
808 * If we don't have a client problem (RT_FAILURE(rc)) we'll reply to the
809 * clients exec packet now.
810 */
811 if (RT_SUCCESS(rc))
812 {
813 uint32_t uStatus = PROC_STS_UNDEFINED;
814 uint32_t uFlags = 0;
815
816 if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX)
817 {
818 VBoxServiceVerbose(3, "[PID %RU32]: Timed out and got killed\n",
819 pProcess->uPID);
820 uStatus = PROC_STS_TOK;
821 }
822 else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX)
823 {
824 VBoxServiceVerbose(3, "[PID %RU32]: Timed out and did *not* get killed\n",
825 pProcess->uPID);
826 uStatus = PROC_STS_TOA;
827 }
828 else if (pProcess->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
829 {
830 VBoxServiceVerbose(3, "[PID %RU32]: Got terminated because system/service is about to shutdown\n",
831 pProcess->uPID);
832 uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */
833 uFlags = pProcess->StartupInfo.uFlags; /* Return handed-in execution flags back to the host. */
834 }
835 else if (fProcessAlive)
836 {
837 VBoxServiceError("[PID %RU32]: Is alive when it should not!\n",
838 pProcess->uPID);
839 }
840 else if (MsProcessKilled != UINT64_MAX)
841 {
842 VBoxServiceError("[PID %RU32]: Has been killed when it should not!\n",
843 pProcess->uPID);
844 }
845 else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
846 {
847 VBoxServiceVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_NORMAL (Exit code: %u)\n",
848 pProcess->uPID, ProcessStatus.iStatus);
849
850 uStatus = PROC_STS_TEN;
851 uFlags = ProcessStatus.iStatus;
852 }
853 else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
854 {
855 VBoxServiceVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_SIGNAL (Signal: %u)\n",
856 pProcess->uPID, ProcessStatus.iStatus);
857
858 uStatus = PROC_STS_TES;
859 uFlags = ProcessStatus.iStatus;
860 }
861 else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
862 {
863 /* ProcessStatus.iStatus will be undefined. */
864 VBoxServiceVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_ABEND\n",
865 pProcess->uPID);
866
867 uStatus = PROC_STS_TEA;
868 uFlags = ProcessStatus.iStatus;
869 }
870 else
871 VBoxServiceVerbose(1, "[PID %RU32]: Handling process status %u not implemented\n",
872 pProcess->uPID, ProcessStatus.enmReason);
873
874 VBoxServiceVerbose(2, "[PID %RU32]: Ended, ClientID=%u, CID=%u, Status=%u, Flags=0x%x\n",
875 pProcess->uPID, pProcess->uClientID, pProcess->uContextID, uStatus, uFlags);
876
877 if (!(pProcess->StartupInfo.uFlags & EXECUTEPROCESSFLAG_WAIT_START))
878 {
879 VBGLR3GUESTCTRLCMDCTX ctx = { pProcess->uClientID, pProcess->uContextID };
880 rc2 = VbglR3GuestCtrlProcCbStatus(&ctx,
881 pProcess->uPID, uStatus, uFlags,
882 NULL /* pvData */, 0 /* cbData */);
883 if (RT_FAILURE(rc2))
884 VBoxServiceError("[PID %RU32]: Error reporting final status to host; rc=%Rrc\n",
885 pProcess->uPID, rc2);
886 }
887 else
888 VBoxServiceVerbose(3, "[PID %RU32]: Was started detached, no final status sent to host\n",
889 pProcess->uPID);
890 }
891
892 VBoxServiceVerbose(3, "[PID %RU32]: Process loop ended with rc=%Rrc\n",
893 pProcess->uPID, rc);
894 return rc;
895}
896
897
898/**
899 * Initializes a pipe's handle and pipe object.
900 *
901 * @return IPRT status code.
902 * @param ph The pipe's handle to initialize.
903 * @param phPipe The pipe's object to initialize.
904 */
905static int gstcntlProcessInitPipe(PRTHANDLE ph, PRTPIPE phPipe)
906{
907 AssertPtrReturn(ph, VERR_INVALID_PARAMETER);
908 AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
909
910 ph->enmType = RTHANDLETYPE_PIPE;
911 ph->u.hPipe = NIL_RTPIPE;
912 *phPipe = NIL_RTPIPE;
913
914 return VINF_SUCCESS;
915}
916
917
918/**
919 * Allocates a guest thread request with the specified request data.
920 *
921 * @return IPRT status code.
922 * @param ppReq Pointer that will receive the newly allocated request.
923 * Must be freed later with GstCntlProcessRequestFree().
924 * @param enmType Request type.
925 * @param pvData Payload data, based on request type.
926 * @param cbData Size of payload data (in bytes).
927 * @param uCID Context ID to which this request belongs to.
928 */
929int GstCntlProcessRequestAllocEx(PVBOXSERVICECTRLREQUEST *ppReq,
930 VBOXSERVICECTRLREQUESTTYPE enmType,
931 void *pvData,
932 size_t cbData,
933 uint32_t uCID)
934{
935 AssertPtrReturn(ppReq, VERR_INVALID_POINTER);
936
937 PVBOXSERVICECTRLREQUEST pReq =
938 (PVBOXSERVICECTRLREQUEST)RTMemAlloc(sizeof(VBOXSERVICECTRLREQUEST));
939 AssertPtrReturn(pReq, VERR_NO_MEMORY);
940
941 RT_ZERO(*pReq);
942 pReq->fAsync = false;
943 pReq->enmType = enmType;
944 pReq->uCID = uCID;
945 pReq->cbData = cbData;
946 pReq->pvData = pvData;
947
948 /* Set request result to some defined state in case
949 * it got cancelled. */
950 pReq->rc = VERR_CANCELLED;
951
952 int rc = RTSemEventMultiCreate(&pReq->Event);
953 AssertRC(rc);
954
955 if (RT_SUCCESS(rc))
956 {
957 *ppReq = pReq;
958 return VINF_SUCCESS;
959 }
960
961 RTMemFree(pReq);
962 return rc;
963}
964
965
966/**
967 * Allocates a guest thread request with the specified request data.
968 *
969 * @return IPRT status code.
970 * @param ppReq Pointer that will receive the newly allocated request.
971 * Must be freed later with GstCntlProcessRequestFree().
972 * @param enmType Request type.
973 */
974int GstCntlProcessRequestAlloc(PVBOXSERVICECTRLREQUEST *ppReq,
975 VBOXSERVICECTRLREQUESTTYPE enmType)
976{
977 return GstCntlProcessRequestAllocEx(ppReq, enmType,
978 NULL /* pvData */, 0 /* cbData */,
979 0 /* ContextID */);
980}
981
982
983/**
984 * Cancels a previously fired off guest process request.
985 * Note: Caller is responsible for locking!
986 *
987 * @return IPRT status code.
988 * @param pReq Request to cancel.
989 */
990static int gstcntlProcessRequestCancel(PVBOXSERVICECTRLREQUEST pReq)
991{
992 if (!pReq) /* Silently skip non-initialized requests. */
993 return VINF_SUCCESS;
994
995 VBoxServiceVerbose(4, "Cancelling request=0x%p\n", pReq);
996
997 return RTSemEventMultiSignal(pReq->Event);
998}
999
1000
1001/**
1002 * Frees a formerly allocated guest thread request.
1003 *
1004 * @return IPRT status code.
1005 * @param pReq Request to free.
1006 */
1007void GstCntlProcessRequestFree(PVBOXSERVICECTRLREQUEST pReq)
1008{
1009 AssertPtrReturnVoid(pReq);
1010
1011 VBoxServiceVerbose(4, "Freeing request=0x%p (event=%RTsem)\n",
1012 pReq, &pReq->Event);
1013
1014 int rc = RTSemEventMultiDestroy(pReq->Event);
1015 AssertRC(rc);
1016
1017 RTMemFree(pReq);
1018 pReq = NULL;
1019}
1020
1021
1022/**
1023 * Waits for a guest thread's event to get triggered.
1024 *
1025 * @return IPRT status code.
1026 * @param pReq Request to wait for.
1027 */
1028int GstCntlProcessRequestWait(PVBOXSERVICECTRLREQUEST pReq)
1029{
1030 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
1031
1032 /* Wait on the request to get completed (or we are asked to abort/shutdown). */
1033 int rc = RTSemEventMultiWait(pReq->Event, RT_INDEFINITE_WAIT);
1034 if (RT_SUCCESS(rc))
1035 {
1036 VBoxServiceVerbose(4, "Performed request with rc=%Rrc, cbData=%u\n",
1037 pReq->rc, pReq->cbData);
1038
1039 /* Give back overall request result. */
1040 rc = pReq->rc;
1041 }
1042 else
1043 VBoxServiceError("Waiting for request failed, rc=%Rrc\n", rc);
1044
1045 return rc;
1046}
1047
1048
1049/**
1050 * Sets up the redirection / pipe / nothing for one of the standard handles.
1051 *
1052 * @returns IPRT status code. No client replies made.
1053 * @param pszHowTo How to set up this standard handle.
1054 * @param fd Which standard handle it is (0 == stdin, 1 ==
1055 * stdout, 2 == stderr).
1056 * @param ph The generic handle that @a pph may be set
1057 * pointing to. Always set.
1058 * @param pph Pointer to the RTProcCreateExec argument.
1059 * Always set.
1060 * @param phPipe Where to return the end of the pipe that we
1061 * should service.
1062 */
1063static int gstcntlProcessSetupPipe(const char *pszHowTo, int fd,
1064 PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
1065{
1066 AssertPtrReturn(ph, VERR_INVALID_POINTER);
1067 AssertPtrReturn(pph, VERR_INVALID_POINTER);
1068 AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
1069
1070 int rc;
1071
1072 ph->enmType = RTHANDLETYPE_PIPE;
1073 ph->u.hPipe = NIL_RTPIPE;
1074 *pph = NULL;
1075 *phPipe = NIL_RTPIPE;
1076
1077 if (!strcmp(pszHowTo, "|"))
1078 {
1079 /*
1080 * Setup a pipe for forwarding to/from the client.
1081 * The ph union struct will be filled with a pipe read/write handle
1082 * to represent the "other" end to phPipe.
1083 */
1084 if (fd == 0) /* stdin? */
1085 {
1086 /* Connect a wrtie pipe specified by phPipe to stdin. */
1087 rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
1088 }
1089 else /* stdout or stderr? */
1090 {
1091 /* Connect a read pipe specified by phPipe to stdout or stderr. */
1092 rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
1093 }
1094
1095 if (RT_FAILURE(rc))
1096 return rc;
1097
1098 ph->enmType = RTHANDLETYPE_PIPE;
1099 *pph = ph;
1100 }
1101 else if (!strcmp(pszHowTo, "/dev/null"))
1102 {
1103 /*
1104 * Redirect to/from /dev/null.
1105 */
1106 RTFILE hFile;
1107 rc = RTFileOpenBitBucket(&hFile, fd == 0 ? RTFILE_O_READ : RTFILE_O_WRITE);
1108 if (RT_FAILURE(rc))
1109 return rc;
1110
1111 ph->enmType = RTHANDLETYPE_FILE;
1112 ph->u.hFile = hFile;
1113 *pph = ph;
1114 }
1115 else /* Add other piping stuff here. */
1116 rc = VINF_SUCCESS; /* Same as parent (us). */
1117
1118 return rc;
1119}
1120
1121
1122/**
1123 * Expands a file name / path to its real content. This only works on Windows
1124 * for now (e.g. translating "%TEMP%\foo.exe" to "C:\Windows\Temp" when starting
1125 * with system / administrative rights).
1126 *
1127 * @return IPRT status code.
1128 * @param pszPath Path to resolve.
1129 * @param pszExpanded Pointer to string to store the resolved path in.
1130 * @param cbExpanded Size (in bytes) of string to store the resolved path.
1131 */
1132static int gstcntlProcessMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded)
1133{
1134 int rc = VINF_SUCCESS;
1135#ifdef RT_OS_WINDOWS
1136 if (!ExpandEnvironmentStrings(pszPath, pszExpanded, cbExpanded))
1137 rc = RTErrConvertFromWin32(GetLastError());
1138#else
1139 /* No expansion for non-Windows yet. */
1140 rc = RTStrCopy(pszExpanded, cbExpanded, pszPath);
1141#endif
1142#ifdef DEBUG
1143 VBoxServiceVerbose(3, "VBoxServiceControlExecMakeFullPath: %s -> %s\n",
1144 pszPath, pszExpanded);
1145#endif
1146 return rc;
1147}
1148
1149
1150/**
1151 * Resolves the full path of a specified executable name. This function also
1152 * resolves internal VBoxService tools to its appropriate executable path + name if
1153 * VBOXSERVICE_NAME is specified as pszFileName.
1154 *
1155 * @return IPRT status code.
1156 * @param pszFileName File name to resolve.
1157 * @param pszResolved Pointer to a string where the resolved file name will be stored.
1158 * @param cbResolved Size (in bytes) of resolved file name string.
1159 */
1160static int gstcntlProcessResolveExecutable(const char *pszFileName,
1161 char *pszResolved, size_t cbResolved)
1162{
1163 AssertPtrReturn(pszFileName, VERR_INVALID_POINTER);
1164 AssertPtrReturn(pszResolved, VERR_INVALID_POINTER);
1165 AssertReturn(cbResolved, VERR_INVALID_PARAMETER);
1166
1167 int rc = VINF_SUCCESS;
1168
1169 char szPathToResolve[RTPATH_MAX];
1170 if ( (g_pszProgName && (RTStrICmp(pszFileName, g_pszProgName) == 0))
1171 || !RTStrICmp(pszFileName, VBOXSERVICE_NAME))
1172 {
1173 /* Resolve executable name of this process. */
1174 if (!RTProcGetExecutablePath(szPathToResolve, sizeof(szPathToResolve)))
1175 rc = VERR_FILE_NOT_FOUND;
1176 }
1177 else
1178 {
1179 /* Take the raw argument to resolve. */
1180 rc = RTStrCopy(szPathToResolve, sizeof(szPathToResolve), pszFileName);
1181 }
1182
1183 if (RT_SUCCESS(rc))
1184 {
1185 rc = gstcntlProcessMakeFullPath(szPathToResolve, pszResolved, cbResolved);
1186 if (RT_SUCCESS(rc))
1187 VBoxServiceVerbose(3, "Looked up executable: %s -> %s\n",
1188 pszFileName, pszResolved);
1189 }
1190
1191 if (RT_FAILURE(rc))
1192 VBoxServiceError("Failed to lookup executable \"%s\" with rc=%Rrc\n",
1193 pszFileName, rc);
1194 return rc;
1195}
1196
1197
1198/**
1199 * Constructs the argv command line by resolving environment variables
1200 * and relative paths.
1201 *
1202 * @return IPRT status code.
1203 * @param pszArgv0 First argument (argv0), either original or modified version. Optional.
1204 * @param papszArgs Original argv command line from the host, starting at argv[1].
1205 * @param ppapszArgv Pointer to a pointer with the new argv command line.
1206 * Needs to be freed with RTGetOptArgvFree.
1207 */
1208static int gstcntlProcessAllocateArgv(const char *pszArgv0,
1209 const char * const *papszArgs,
1210 bool fExpandArgs, char ***ppapszArgv)
1211{
1212 AssertPtrReturn(ppapszArgv, VERR_INVALID_POINTER);
1213
1214 VBoxServiceVerbose(3, "GstCntlProcessPrepareArgv: pszArgv0=%p, papszArgs=%p, fExpandArgs=%RTbool, ppapszArgv=%p\n",
1215 pszArgv0, papszArgs, fExpandArgs, ppapszArgv);
1216
1217 int rc = VINF_SUCCESS;
1218 uint32_t cArgs;
1219 for (cArgs = 0; papszArgs[cArgs]; cArgs++)
1220 {
1221 if (cArgs >= UINT32_MAX - 2)
1222 return VERR_BUFFER_OVERFLOW;
1223 }
1224
1225 /* Allocate new argv vector (adding + 2 for argv0 + termination). */
1226 size_t cbSize = (cArgs + 2) * sizeof(char*);
1227 char **papszNewArgv = (char**)RTMemAlloc(cbSize);
1228 if (!papszNewArgv)
1229 return VERR_NO_MEMORY;
1230
1231#ifdef DEBUG
1232 VBoxServiceVerbose(3, "GstCntlProcessAllocateArgv: cbSize=%RU32, cArgs=%RU32\n",
1233 cbSize, cArgs);
1234#endif
1235
1236 size_t i = 0; /* Keep the argument counter in scope for cleaning up on failure. */
1237
1238 rc = RTStrDupEx(&papszNewArgv[0], pszArgv0);
1239 if (RT_SUCCESS(rc))
1240 {
1241 for (; i < cArgs; i++)
1242 {
1243 char *pszArg;
1244#if 0 /* Arguments expansion -- untested. */
1245 if (fExpandArgs)
1246 {
1247 /* According to MSDN the limit on older Windows version is 32K, whereas
1248 * Vista+ there are no limits anymore. We still stick to 4K. */
1249 char szExpanded[_4K];
1250# ifdef RT_OS_WINDOWS
1251 if (!ExpandEnvironmentStrings(papszArgs[i], szExpanded, sizeof(szExpanded)))
1252 rc = RTErrConvertFromWin32(GetLastError());
1253# else
1254 /* No expansion for non-Windows yet. */
1255 rc = RTStrCopy(papszArgs[i], sizeof(szExpanded), szExpanded);
1256# endif
1257 if (RT_SUCCESS(rc))
1258 rc = RTStrDupEx(&pszArg, szExpanded);
1259 }
1260 else
1261#endif
1262 rc = RTStrDupEx(&pszArg, papszArgs[i]);
1263
1264 if (RT_FAILURE(rc))
1265 break;
1266
1267 papszNewArgv[i + 1] = pszArg;
1268 }
1269
1270 if (RT_SUCCESS(rc))
1271 {
1272 /* Terminate array. */
1273 papszNewArgv[cArgs + 1] = NULL;
1274
1275 *ppapszArgv = papszNewArgv;
1276 }
1277 }
1278
1279 if (RT_FAILURE(rc))
1280 {
1281 for (i; i > 0; i--)
1282 RTStrFree(papszNewArgv[i]);
1283 RTMemFree(papszNewArgv);
1284 }
1285
1286 return rc;
1287}
1288
1289
1290/**
1291 * Assigns a valid PID to a guest control thread and also checks if there already was
1292 * another (stale) guest process which was using that PID before and destroys it.
1293 *
1294 * @return IPRT status code.
1295 * @param pThread Thread to assign PID to.
1296 * @param uPID PID to assign to the specified guest control execution thread.
1297 */
1298int gstcntlProcessAssignPID(PVBOXSERVICECTRLPROCESS pThread, uint32_t uPID)
1299{
1300 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
1301 AssertReturn(uPID, VERR_INVALID_PARAMETER);
1302
1303 AssertPtr(pThread->pSession);
1304 int rc = RTCritSectEnter(&pThread->pSession->CritSect);
1305 if (RT_SUCCESS(rc))
1306 {
1307 /* Search old threads using the desired PID and shut them down completely -- it's
1308 * not used anymore. */
1309 PVBOXSERVICECTRLPROCESS pThreadCur;
1310 bool fTryAgain = false;
1311 do
1312 {
1313 RTListForEach(&pThread->pSession->lstProcessesActive, pThreadCur, VBOXSERVICECTRLPROCESS, Node)
1314 {
1315 if (pThreadCur->uPID == uPID)
1316 {
1317 Assert(pThreadCur != pThread); /* can't happen */
1318 uint32_t uTriedPID = uPID;
1319 uPID += 391939;
1320 VBoxServiceVerbose(2, "PID %RU32 was used before, trying again with %u ...\n",
1321 uTriedPID, uPID);
1322 fTryAgain = true;
1323 break;
1324 }
1325 }
1326 } while (fTryAgain);
1327
1328 /* Assign PID to current thread. */
1329 pThread->uPID = uPID;
1330
1331 rc = RTCritSectLeave(&pThread->pSession->CritSect);
1332 AssertRC(rc);
1333 }
1334
1335 return rc;
1336}
1337
1338
1339void gstcntlProcessFreeArgv(char **papszArgv)
1340{
1341 if (papszArgv)
1342 {
1343 size_t i = 0;
1344 while (papszArgv[i])
1345 RTStrFree(papszArgv[i++]);
1346 RTMemFree(papszArgv);
1347 }
1348}
1349
1350
1351/**
1352 * Helper function to create/start a process on the guest.
1353 *
1354 * @return IPRT status code.
1355 * @param pszExec Full qualified path of process to start (without arguments).
1356 * @param papszArgs Pointer to array of command line arguments.
1357 * @param hEnv Handle to environment block to use.
1358 * @param fFlags Process execution flags.
1359 * @param phStdIn Handle for the process' stdin pipe.
1360 * @param phStdOut Handle for the process' stdout pipe.
1361 * @param phStdErr Handle for the process' stderr pipe.
1362 * @param pszAsUser User name (account) to start the process under.
1363 * @param pszPassword Password of the specified user.
1364 * @param phProcess Pointer which will receive the process handle after
1365 * successful process start.
1366 */
1367static int gstcntlProcessCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
1368 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
1369 const char *pszPassword, PRTPROCESS phProcess)
1370{
1371 AssertPtrReturn(pszExec, VERR_INVALID_PARAMETER);
1372 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
1373 AssertPtrReturn(phProcess, VERR_INVALID_PARAMETER);
1374
1375 int rc = VINF_SUCCESS;
1376 char szExecExp[RTPATH_MAX];
1377
1378 /* Do we need to expand environment variables in arguments? */
1379 bool fExpandArgs = (fFlags & EXECUTEPROCESSFLAG_EXPAND_ARGUMENTS) ? true : false;
1380
1381#ifdef RT_OS_WINDOWS
1382 /*
1383 * If sysprep should be executed do this in the context of VBoxService, which
1384 * (usually, if started by SCM) has administrator rights. Because of that a UI
1385 * won't be shown (doesn't have a desktop).
1386 */
1387 if (!RTStrICmp(pszExec, "sysprep"))
1388 {
1389 /* Use a predefined sysprep path as default. */
1390 char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe";
1391
1392 /*
1393 * On Windows Vista (and up) sysprep is located in "system32\\sysprep\\sysprep.exe",
1394 * so detect the OS and use a different path.
1395 */
1396 OSVERSIONINFOEX OSInfoEx;
1397 RT_ZERO(OSInfoEx);
1398 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
1399 if ( GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
1400 && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
1401 && OSInfoEx.dwMajorVersion >= 6 /* Vista or later */)
1402 {
1403 rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL);
1404 if (RT_SUCCESS(rc))
1405 rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\sysprep\\sysprep.exe");
1406 }
1407
1408 if (RT_SUCCESS(rc))
1409 {
1410 char **papszArgsExp;
1411 rc = gstcntlProcessAllocateArgv(szSysprepCmd /* argv0 */, papszArgs,
1412 fExpandArgs, &papszArgsExp);
1413 if (RT_SUCCESS(rc))
1414 {
1415 rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */,
1416 phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */,
1417 NULL /* pszPassword */, phProcess);
1418 gstcntlProcessFreeArgv(papszArgsExp);
1419 }
1420 }
1421
1422 if (RT_FAILURE(rc))
1423 VBoxServiceVerbose(3, "Starting sysprep returned rc=%Rrc\n", rc);
1424
1425 return rc;
1426 }
1427#endif /* RT_OS_WINDOWS */
1428
1429#ifdef VBOXSERVICE_TOOLBOX
1430 if (RTStrStr(pszExec, "vbox_") == pszExec)
1431 {
1432 /* We want to use the internal toolbox (all internal
1433 * tools are starting with "vbox_" (e.g. "vbox_cat"). */
1434 rc = gstcntlProcessResolveExecutable(VBOXSERVICE_NAME, szExecExp, sizeof(szExecExp));
1435 }
1436 else
1437 {
1438#endif
1439 /*
1440 * Do the environment variables expansion on executable and arguments.
1441 */
1442 rc = gstcntlProcessResolveExecutable(pszExec, szExecExp, sizeof(szExecExp));
1443#ifdef VBOXSERVICE_TOOLBOX
1444 }
1445#endif
1446 if (RT_SUCCESS(rc))
1447 {
1448 char **papszArgsExp;
1449 rc = gstcntlProcessAllocateArgv(pszExec /* Always use the unmodified executable name as argv0. */,
1450 papszArgs /* Append the rest of the argument vector (if any). */,
1451 fExpandArgs, &papszArgsExp);
1452 if (RT_FAILURE(rc))
1453 {
1454 /* Don't print any arguments -- may contain passwords or other sensible data! */
1455 VBoxServiceError("Could not prepare arguments, rc=%Rrc\n", rc);
1456 }
1457 else
1458 {
1459 uint32_t uProcFlags = 0;
1460 if (fFlags)
1461 {
1462 if (fFlags & EXECUTEPROCESSFLAG_HIDDEN)
1463 uProcFlags |= RTPROC_FLAGS_HIDDEN;
1464 if (fFlags & EXECUTEPROCESSFLAG_NO_PROFILE)
1465 uProcFlags |= RTPROC_FLAGS_NO_PROFILE;
1466 }
1467
1468 /* If no user name specified run with current credentials (e.g.
1469 * full service/system rights). This is prohibited via official Main API!
1470 *
1471 * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication
1472 * code (at least on Windows) for running processes as different users
1473 * started from our system service. */
1474 if (pszAsUser && *pszAsUser)
1475 uProcFlags |= RTPROC_FLAGS_SERVICE;
1476#ifdef DEBUG
1477 VBoxServiceVerbose(3, "Command: %s\n", szExecExp);
1478 for (size_t i = 0; papszArgsExp[i]; i++)
1479 VBoxServiceVerbose(3, "\targv[%ld]: %s\n", i, papszArgsExp[i]);
1480#endif
1481 VBoxServiceVerbose(3, "Starting process \"%s\" ...\n", szExecExp);
1482
1483 /* Do normal execution. */
1484 rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags,
1485 phStdIn, phStdOut, phStdErr,
1486 pszAsUser && *pszAsUser ? pszAsUser : NULL,
1487 pszPassword && *pszPassword ? pszPassword : NULL,
1488 phProcess);
1489
1490 VBoxServiceVerbose(3, "Starting process \"%s\" returned rc=%Rrc\n",
1491 szExecExp, rc);
1492
1493 gstcntlProcessFreeArgv(papszArgsExp);
1494 }
1495 }
1496 return rc;
1497}
1498
1499/**
1500 * The actual worker routine (loop) for a started guest process.
1501 *
1502 * @return IPRT status code.
1503 * @param PVBOXSERVICECTRLPROCESS Guest process.
1504 */
1505static int gstcntlProcessProcessWorker(PVBOXSERVICECTRLPROCESS pProcess)
1506{
1507 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1508 VBoxServiceVerbose(3, "Thread of process pThread=0x%p = \"%s\" started\n",
1509 pProcess, pProcess->StartupInfo.szCmd);
1510
1511 int rc = GstCntlSessionListSet(pProcess->pSession,
1512 pProcess, VBOXSERVICECTRLTHREADLIST_RUNNING);
1513 AssertRC(rc);
1514
1515 rc = VbglR3GuestCtrlConnect(&pProcess->uClientID);
1516 if (RT_FAILURE(rc))
1517 {
1518 VBoxServiceError("Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc);
1519 RTThreadUserSignal(RTThreadSelf());
1520 return rc;
1521 }
1522 VBoxServiceVerbose(3, "Guest process \"%s\" got client ID=%u, flags=0x%x\n",
1523 pProcess->StartupInfo.szCmd, pProcess->uClientID, pProcess->StartupInfo.uFlags);
1524
1525 bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
1526
1527 /*
1528 * Prepare argument list.
1529 */
1530 char **papszArgs;
1531 uint32_t uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
1532 rc = RTGetOptArgvFromString(&papszArgs, (int*)&uNumArgs,
1533 (pProcess->StartupInfo.uNumArgs > 0) ? pProcess->StartupInfo.szArgs : "", NULL);
1534 /* Did we get the same result? */
1535 Assert(pProcess->StartupInfo.uNumArgs == uNumArgs);
1536
1537 /*
1538 * Prepare environment variables list.
1539 */
1540 char **papszEnv;
1541 uint32_t uNumEnvVars = 0; /* Initialize in case of failing ... */
1542 if (RT_SUCCESS(rc))
1543 {
1544 /* Prepare environment list. */
1545 if (pProcess->StartupInfo.uNumEnvVars)
1546 {
1547 papszEnv = (char **)RTMemAlloc(pProcess->StartupInfo.uNumEnvVars * sizeof(char*));
1548 AssertPtr(papszEnv);
1549 uNumEnvVars = pProcess->StartupInfo.uNumEnvVars;
1550
1551 const char *pszCur = pProcess->StartupInfo.szEnv;
1552 uint32_t i = 0;
1553 uint32_t cbLen = 0;
1554 while (cbLen < pProcess->StartupInfo.cbEnv)
1555 {
1556 /* sanity check */
1557 if (i >= pProcess->StartupInfo.uNumEnvVars)
1558 {
1559 rc = VERR_INVALID_PARAMETER;
1560 break;
1561 }
1562 int cbStr = RTStrAPrintf(&papszEnv[i++], "%s", pszCur);
1563 if (cbStr < 0)
1564 {
1565 rc = VERR_NO_STR_MEMORY;
1566 break;
1567 }
1568 pszCur += cbStr + 1; /* Skip terminating '\0' */
1569 cbLen += cbStr + 1; /* Skip terminating '\0' */
1570 }
1571 Assert(i == pProcess->StartupInfo.uNumEnvVars);
1572 }
1573 }
1574
1575 /*
1576 * Create the environment.
1577 */
1578 RTENV hEnv;
1579 rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
1580 if (RT_SUCCESS(rc))
1581 {
1582 size_t i;
1583 for (i = 0; i < uNumEnvVars && papszEnv; i++)
1584 {
1585 rc = RTEnvPutEx(hEnv, papszEnv[i]);
1586 if (RT_FAILURE(rc))
1587 break;
1588 }
1589 if (RT_SUCCESS(rc))
1590 {
1591 /*
1592 * Setup the redirection of the standard stuff.
1593 */
1594 /** @todo consider supporting: gcc stuff.c >file 2>&1. */
1595 RTHANDLE hStdIn;
1596 PRTHANDLE phStdIn;
1597 rc = gstcntlProcessSetupPipe("|", 0 /*STDIN_FILENO*/,
1598 &hStdIn, &phStdIn, &pProcess->pipeStdInW);
1599 if (RT_SUCCESS(rc))
1600 {
1601 RTHANDLE hStdOut;
1602 PRTHANDLE phStdOut;
1603 RTPIPE pipeStdOutR;
1604 rc = gstcntlProcessSetupPipe( (pProcess->StartupInfo.uFlags & EXECUTEPROCESSFLAG_WAIT_STDOUT)
1605 ? "|" : "/dev/null",
1606 1 /*STDOUT_FILENO*/,
1607 &hStdOut, &phStdOut, &pipeStdOutR);
1608 if (RT_SUCCESS(rc))
1609 {
1610 RTHANDLE hStdErr;
1611 PRTHANDLE phStdErr;
1612 RTPIPE pipeStdErrR;
1613 rc = gstcntlProcessSetupPipe( (pProcess->StartupInfo.uFlags & EXECUTEPROCESSFLAG_WAIT_STDERR)
1614 ? "|" : "/dev/null",
1615 2 /*STDERR_FILENO*/,
1616 &hStdErr, &phStdErr, &pipeStdErrR);
1617 if (RT_SUCCESS(rc))
1618 {
1619 /*
1620 * Create a poll set for the pipes and let the
1621 * transport layer add stuff to it as well.
1622 */
1623 RTPOLLSET hPollSet;
1624 rc = RTPollSetCreate(&hPollSet);
1625 if (RT_SUCCESS(rc))
1626 {
1627 uint32_t uFlags = RTPOLL_EVT_ERROR;
1628#if 0
1629 /* Add reading event to pollset to get some more information. */
1630 uFlags |= RTPOLL_EVT_READ;
1631#endif
1632 /* Stdin. */
1633 if (RT_SUCCESS(rc))
1634 rc = RTPollSetAddPipe(hPollSet, pProcess->pipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN);
1635 /* Stdout. */
1636 if (RT_SUCCESS(rc))
1637 rc = RTPollSetAddPipe(hPollSet, pipeStdOutR, uFlags, VBOXSERVICECTRLPIPEID_STDOUT);
1638 /* Stderr. */
1639 if (RT_SUCCESS(rc))
1640 rc = RTPollSetAddPipe(hPollSet, pipeStdErrR, uFlags, VBOXSERVICECTRLPIPEID_STDERR);
1641 /* IPC notification pipe. */
1642 if (RT_SUCCESS(rc))
1643 rc = RTPipeCreate(&pProcess->hNotificationPipeR, &pProcess->hNotificationPipeW, 0 /* Flags */);
1644 if (RT_SUCCESS(rc))
1645 rc = RTPollSetAddPipe(hPollSet, pProcess->hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_IPC_NOTIFY);
1646
1647 if (RT_SUCCESS(rc))
1648 {
1649 AssertPtr(pProcess->pSession);
1650 bool fNeedsImpersonation = !(pProcess->pSession->uFlags & VBOXSERVICECTRLSESSION_FLAG_FORK);
1651
1652 RTPROCESS hProcess;
1653 rc = gstcntlProcessCreateProcess(pProcess->StartupInfo.szCmd, papszArgs, hEnv, pProcess->StartupInfo.uFlags,
1654 phStdIn, phStdOut, phStdErr,
1655 fNeedsImpersonation ? pProcess->StartupInfo.szUser : NULL,
1656 fNeedsImpersonation ? pProcess->StartupInfo.szPassword : NULL,
1657 &hProcess);
1658 if (RT_FAILURE(rc))
1659 VBoxServiceError("Error starting process, rc=%Rrc\n", rc);
1660 /*
1661 * Tell the session thread that it can continue
1662 * spawning guest processes. This needs to be done after the new
1663 * process has been started because otherwise signal handling
1664 * on (Open) Solaris does not work correctly (see @bugref{5068}).
1665 */
1666 int rc2 = RTThreadUserSignal(RTThreadSelf());
1667 if (RT_SUCCESS(rc))
1668 rc = rc2;
1669 fSignalled = true;
1670
1671 if (RT_SUCCESS(rc))
1672 {
1673 /*
1674 * Close the child ends of any pipes and redirected files.
1675 */
1676 rc2 = RTHandleClose(phStdIn); AssertRC(rc2);
1677 phStdIn = NULL;
1678 rc2 = RTHandleClose(phStdOut); AssertRC(rc2);
1679 phStdOut = NULL;
1680 rc2 = RTHandleClose(phStdErr); AssertRC(rc2);
1681 phStdErr = NULL;
1682
1683 /* Enter the process loop. */
1684 rc = gstcntlProcessProcLoop(pProcess, hProcess, hPollSet,
1685 &pProcess->pipeStdInW, &pipeStdOutR, &pipeStdErrR);
1686
1687 /*
1688 * The handles that are no longer in the set have
1689 * been closed by the above call in order to prevent
1690 * the guest from getting stuck accessing them.
1691 * So, NIL the handles to avoid closing them again.
1692 */
1693 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_IPC_NOTIFY, NULL)))
1694 {
1695 pProcess->hNotificationPipeR = NIL_RTPIPE;
1696 pProcess->hNotificationPipeW = NIL_RTPIPE;
1697 }
1698 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDERR, NULL)))
1699 pipeStdErrR = NIL_RTPIPE;
1700 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDOUT, NULL)))
1701 pipeStdOutR = NIL_RTPIPE;
1702 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDIN, NULL)))
1703 pProcess->pipeStdInW = NIL_RTPIPE;
1704 }
1705 }
1706 RTPollSetDestroy(hPollSet);
1707
1708 RTPipeClose(pProcess->hNotificationPipeR);
1709 pProcess->hNotificationPipeR = NIL_RTPIPE;
1710 RTPipeClose(pProcess->hNotificationPipeW);
1711 pProcess->hNotificationPipeW = NIL_RTPIPE;
1712 }
1713 RTPipeClose(pipeStdErrR);
1714 pipeStdErrR = NIL_RTPIPE;
1715 RTHandleClose(phStdErr);
1716 if (phStdErr)
1717 RTHandleClose(phStdErr);
1718 }
1719 RTPipeClose(pipeStdOutR);
1720 pipeStdOutR = NIL_RTPIPE;
1721 RTHandleClose(&hStdOut);
1722 if (phStdOut)
1723 RTHandleClose(phStdOut);
1724 }
1725 RTPipeClose(pProcess->pipeStdInW);
1726 pProcess->pipeStdInW = NIL_RTPIPE;
1727 RTHandleClose(phStdIn);
1728 }
1729 }
1730 RTEnvDestroy(hEnv);
1731 }
1732
1733 /* Move thread to stopped thread list. */
1734 /*int rc2 = GstCntlSessionListSet(pProcess->pSession,
1735 pProcess, VBOXSERVICECTRLTHREADLIST_STOPPED);
1736 AssertRC(rc2);*/
1737
1738 if (pProcess->uClientID)
1739 {
1740 if (RT_FAILURE(rc))
1741 {
1742 VBGLR3GUESTCTRLCMDCTX ctx = { pProcess->uClientID, pProcess->uContextID };
1743 int rc2 = VbglR3GuestCtrlProcCbStatus(&ctx,
1744 pProcess->uPID, PROC_STS_ERROR, rc,
1745 NULL /* pvData */, 0 /* cbData */);
1746 if (RT_FAILURE(rc2))
1747 VBoxServiceError("Could not report process failure error; rc=%Rrc (process error %Rrc)\n",
1748 rc2, rc);
1749 }
1750
1751 /* Disconnect this client from the guest control service. This also cancels all
1752 * outstanding host requests. */
1753 VBoxServiceVerbose(3, "[PID %RU32]: Disconnecting (client ID=%u) ...\n",
1754 pProcess->uPID, pProcess->uClientID);
1755 VbglR3GuestCtrlDisconnect(pProcess->uClientID);
1756 pProcess->uClientID = 0;
1757 }
1758
1759 /* Free argument + environment variable lists. */
1760 if (uNumEnvVars)
1761 {
1762 for (uint32_t i = 0; i < uNumEnvVars; i++)
1763 RTStrFree(papszEnv[i]);
1764 RTMemFree(papszEnv);
1765 }
1766 if (uNumArgs)
1767 RTGetOptArgvFree(papszArgs);
1768
1769 /* Update stopped status. */
1770 ASMAtomicXchgBool(&pProcess->fStopped, true);
1771
1772 /*
1773 * If something went wrong signal the user event so that others don't wait
1774 * forever on this thread.
1775 */
1776 if (RT_FAILURE(rc) && !fSignalled)
1777 RTThreadUserSignal(RTThreadSelf());
1778
1779 VBoxServiceVerbose(3, "[PID %RU32]: Thread of process \"%s\" ended with rc=%Rrc\n",
1780 pProcess->uPID, pProcess->StartupInfo.szCmd, rc);
1781 return rc;
1782}
1783
1784
1785/**
1786 * Thread main routine for a started process.
1787 *
1788 * @return IPRT status code.
1789 * @param RTTHREAD Pointer to the thread's data.
1790 * @param void* User-supplied argument pointer.
1791 *
1792 */
1793static DECLCALLBACK(int) gstcntlProcessThread(RTTHREAD ThreadSelf, void *pvUser)
1794{
1795 PVBOXSERVICECTRLPROCESS pProcess = (VBOXSERVICECTRLPROCESS*)pvUser;
1796 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1797 return gstcntlProcessProcessWorker(pProcess);
1798}
1799
1800
1801/**
1802 * Executes (starts) a process on the guest. This causes a new thread to be created
1803 * so that this function will not block the overall program execution.
1804 *
1805 * @return IPRT status code.
1806 * @param pSession Guest session.
1807 * @param pStartupInfo Startup info.
1808 * @param uContextID Context ID to associate the process to start with.
1809
1810 */
1811int GstCntlProcessStart(const PVBOXSERVICECTRLSESSION pSession,
1812 const PVBOXSERVICECTRLPROCSTARTUPINFO pStartupInfo,
1813 uint32_t uContextID)
1814{
1815 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1816 AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER);
1817
1818 /*
1819 * Allocate new thread data and assign it to our thread list.
1820 */
1821 PVBOXSERVICECTRLPROCESS pProcess = (PVBOXSERVICECTRLPROCESS)RTMemAlloc(sizeof(VBOXSERVICECTRLPROCESS));
1822 if (!pProcess)
1823 return VERR_NO_MEMORY;
1824
1825 int rc = gstcntlProcessInit(pProcess, pSession, pStartupInfo, uContextID);
1826 if (RT_SUCCESS(rc))
1827 {
1828 static uint32_t s_uCtrlExecThread = 0;
1829 if (s_uCtrlExecThread++ == UINT32_MAX)
1830 s_uCtrlExecThread = 0; /* Wrap around to not let IPRT freak out. */
1831 rc = RTThreadCreateF(&pProcess->Thread, gstcntlProcessThread,
1832 pProcess /*pvUser*/, 0 /*cbStack*/,
1833 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "gctl%u", s_uCtrlExecThread);
1834 if (RT_FAILURE(rc))
1835 {
1836 VBoxServiceError("Creating thread for guest process failed: rc=%Rrc, pProcess=%p\n",
1837 rc, pProcess);
1838 }
1839 else
1840 {
1841 VBoxServiceVerbose(4, "Waiting for thread to initialize ...\n");
1842
1843 /* Wait for the thread to initialize. */
1844 rc = RTThreadUserWait(pProcess->Thread, 60 * 1000 /* 60 seconds max. */);
1845 AssertRC(rc);
1846 if ( ASMAtomicReadBool(&pProcess->fShutdown)
1847 || RT_FAILURE(rc))
1848 {
1849 VBoxServiceError("Thread for process \"%s\" failed to start, rc=%Rrc\n",
1850 pStartupInfo->szCmd, rc);
1851 }
1852 else
1853 {
1854 ASMAtomicXchgBool(&pProcess->fStarted, true);
1855 }
1856 }
1857 }
1858
1859 if (RT_FAILURE(rc))
1860 GstCntlProcessFree(pProcess);
1861
1862 return rc;
1863}
1864
1865
1866/**
1867 * Performs a request to a specific (formerly started) guest process and waits
1868 * for its response.
1869 * Note: Caller is responsible for locking!
1870 *
1871 * @return IPRT status code.
1872 * @param pProcess Guest process to perform operation on.
1873 * @param pRequest Pointer to request to perform.
1874 */
1875int GstCntlProcessPerform(PVBOXSERVICECTRLPROCESS pProcess,
1876 PVBOXSERVICECTRLREQUEST pRequest,
1877 bool fAsync)
1878{
1879 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1880 AssertPtrReturn(pRequest, VERR_INVALID_POINTER);
1881 AssertReturn(pRequest->enmType > VBOXSERVICECTRLREQUEST_UNKNOWN, VERR_INVALID_PARAMETER);
1882 /* Rest in pRequest is optional (based on the request type). */
1883
1884 int rc = VINF_SUCCESS;
1885
1886 AssertMsgReturn(pProcess->pRequest == NULL,
1887 ("Another request still is in progress (%p)\n", pProcess->pRequest),
1888 VERR_ACCESS_DENIED);
1889
1890 if (ASMAtomicReadBool(&pProcess->fShutdown))
1891 {
1892 rc = VERR_CANCELLED;
1893 }
1894 else
1895 {
1896 VBoxServiceVerbose(3, "[PID %RU32]: Sending pRequest=%p\n",
1897 pProcess->uPID, pRequest);
1898
1899 /* Set request structure pointer. */
1900 pProcess->pRequest = pRequest;
1901
1902 /** @todo To speed up simultaneous guest process handling we could add a worker threads
1903 * or queue in order to wait for the request to happen. Later. */
1904 /* Wake up guest thread by sending a wakeup byte to the notification pipe so
1905 * that RTPoll unblocks (returns) and we then can do our requested operation. */
1906 Assert(pProcess->hNotificationPipeW != NIL_RTPIPE);
1907 size_t cbWritten = 0;
1908 if (RT_SUCCESS(rc))
1909 rc = RTPipeWrite(pProcess->hNotificationPipeW, "i", 1, &cbWritten);
1910
1911 if (RT_SUCCESS(rc))
1912 {
1913 Assert(cbWritten);
1914 if (!fAsync)
1915 {
1916 VBoxServiceVerbose(3, "[PID %RU32]: Waiting for response on pRequest=%p, enmType=%u, pvData=0x%p, cbData=%u\n",
1917 pProcess->uPID, pRequest, pRequest->enmType, pRequest->pvData, pRequest->cbData);
1918
1919 rc = GstCntlProcessRequestWait(pRequest);
1920 if (RT_SUCCESS(rc))
1921 pProcess->pRequest = NULL;
1922 }
1923 }
1924 }
1925
1926 VBoxServiceVerbose(3, "[PID %RU32]: Performed pRequest=%p, enmType=%u, uCID=%u, pvData=0x%p, cbData=%u, rc=%Rrc\n",
1927 pProcess->uPID, pRequest, pRequest->enmType, pRequest->uCID, pRequest->pvData, pRequest->cbData, rc);
1928 return rc;
1929}
1930
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