VirtualBox

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

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

VBoxService: Renamed VBoxServiceControlThread.cpp -> VBoxServiceControlProcess.cpp.

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