VirtualBox

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

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

GuestCtrl: Implemented using (public) VirtualBox events instead of own callback mechanisms. Bugfixes for testcases (still work in progress).

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

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