VirtualBox

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

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

VBoxService/GuestCtrl: Re-added (optional debug) dumping of guest process streams, now as a (forked) per-session option.

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