VirtualBox

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

Last change on this file since 55549 was 55535, checked in by vboxsync, 10 years ago

Main,VBoxManage,VBoxShell,ValidationKit: Changed the IGuestSession:createProcess[Ex] interfaces to take argv[0] as input separate from the executable name. This is not yet implemented on the guest side.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 81.5 KB
Line 
1/* $Id: VBoxServiceControlProcess.cpp 55535 2015-04-30 02:13:56Z 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 if (rc == VERR_INTERRUPTED)
528 rc = VINF_SUCCESS; /* SIGCHLD send by quick childs! */
529 if (RT_FAILURE(rc))
530 VBoxServiceError("[PID %RU32]: Error reporting starting status to host, rc=%Rrc\n",
531 pProcess->uPID, rc);
532
533 /*
534 * Process input, output, the test pipe and client requests.
535 */
536 while ( RT_SUCCESS(rc)
537 && RT_UNLIKELY(!pProcess->fShutdown))
538 {
539 /*
540 * Wait/Process all pending events.
541 */
542 uint32_t idPollHnd;
543 uint32_t fPollEvt;
544 rc2 = RTPollNoResume(pProcess->hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
545 if (pProcess->fShutdown)
546 continue;
547
548 cMsPollCur = 0; /* No rest until we've checked everything. */
549
550 if (RT_SUCCESS(rc2))
551 {
552 switch (idPollHnd)
553 {
554 case VBOXSERVICECTRLPIPEID_STDIN:
555 rc = gstcntlProcessPollsetOnInput(pProcess, fPollEvt,
556 &pProcess->hPipeStdInW);
557 break;
558
559 case VBOXSERVICECTRLPIPEID_STDOUT:
560 rc = gstcntlProcessPollsetOnOutput(pProcess, fPollEvt,
561 &pProcess->hPipeStdOutR, idPollHnd);
562 break;
563
564 case VBOXSERVICECTRLPIPEID_STDERR:
565 rc = gstcntlProcessPollsetOnOutput(pProcess, fPollEvt,
566 &pProcess->hPipeStdOutR, idPollHnd);
567 break;
568
569 case VBOXSERVICECTRLPIPEID_IPC_NOTIFY:
570#ifdef DEBUG_andy
571 VBoxServiceVerbose(4, "[PID %RU32]: IPC notify\n", pProcess->uPID);
572#endif
573 rc2 = gstcntlProcessLock(pProcess);
574 if (RT_SUCCESS(rc2))
575 {
576 /* Drain the notification pipe. */
577 uint8_t abBuf[8];
578 size_t cbIgnore;
579 rc2 = RTPipeRead(pProcess->hNotificationPipeR,
580 abBuf, sizeof(abBuf), &cbIgnore);
581 if (RT_FAILURE(rc2))
582 VBoxServiceError("Draining IPC notification pipe failed with rc=%Rrc\n", rc2);
583
584 /* Process all pending requests. */
585 VBoxServiceVerbose(4, "[PID %RU32]: Processing pending requests ...\n",
586 pProcess->uPID);
587 Assert(pProcess->hReqQueue != NIL_RTREQQUEUE);
588 rc2 = RTReqQueueProcess(pProcess->hReqQueue,
589 0 /* Only process all pending requests, don't wait for new ones */);
590 if ( RT_FAILURE(rc2)
591 && rc2 != VERR_TIMEOUT)
592 VBoxServiceError("Processing requests failed with with rc=%Rrc\n", rc2);
593
594 int rc3 = gstcntlProcessUnlock(pProcess);
595 AssertRC(rc3);
596#ifdef DEBUG
597 VBoxServiceVerbose(4, "[PID %RU32]: Processing pending requests done, rc=%Rrc\n",
598 pProcess->uPID, rc2);
599#endif
600 }
601
602 break;
603
604 default:
605 AssertMsgFailed(("Unknown idPollHnd=%RU32\n", idPollHnd));
606 break;
607 }
608
609 if (RT_FAILURE(rc) || rc == VINF_EOF)
610 break; /* Abort command, or client dead or something. */
611 }
612#if 0
613 VBoxServiceVerbose(4, "[PID %RU32]: Polling done, pollRc=%Rrc, pollCnt=%RU32, idPollHnd=%s, rc=%Rrc, fProcessAlive=%RTbool, fShutdown=%RTbool\n",
614 pProcess->uPID, rc2, RTPollSetGetCount(hPollSet), gstcntlProcessPollHandleToString(idPollHnd), rc, fProcessAlive, pProcess->fShutdown);
615 VBoxServiceVerbose(4, "[PID %RU32]: stdOut=%s, stdErrR=%s\n",
616 pProcess->uPID,
617 *phStdOutR == NIL_RTPIPE ? "closed" : "open",
618 *phStdErrR == NIL_RTPIPE ? "closed" : "open");
619#endif
620 if (RT_UNLIKELY(pProcess->fShutdown))
621 break; /* We were asked to shutdown. */
622
623 /*
624 * Check for process death.
625 */
626 if (fProcessAlive)
627 {
628 rc2 = RTProcWaitNoResume(pProcess->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
629#if 0
630 VBoxServiceVerbose(4, "[PID %RU32]: RTProcWaitNoResume=%Rrc\n",
631 pProcess->uPID, rc2);
632#endif
633 if (RT_SUCCESS_NP(rc2))
634 {
635 fProcessAlive = false;
636 /* Note: Don't bail out here yet. First check in the next block below
637 * if all needed pipe outputs have been consumed. */
638 }
639 else
640 {
641 if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
642 continue;
643 if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
644 {
645 fProcessAlive = false;
646 ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
647 ProcessStatus.iStatus = 255;
648 AssertFailed();
649 }
650 else
651 AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
652 }
653 }
654
655 /*
656 * If the process has terminated and all output has been consumed,
657 * we should be heading out.
658 */
659 if (!fProcessAlive)
660 {
661 if ( fProcessTimedOut
662 || ( pProcess->hPipeStdOutR == NIL_RTPIPE
663 && pProcess->hPipeStdErrR == NIL_RTPIPE)
664 )
665 {
666 break;
667 }
668 }
669
670 /*
671 * Check for timed out, killing the process.
672 */
673 uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
674 if ( pProcess->StartupInfo.uTimeLimitMS != RT_INDEFINITE_WAIT
675 && pProcess->StartupInfo.uTimeLimitMS != 0)
676 {
677 uint64_t u64Now = RTTimeMilliTS();
678 uint64_t cMsElapsed = u64Now - uMsStart;
679 if (cMsElapsed >= pProcess->StartupInfo.uTimeLimitMS)
680 {
681 fProcessTimedOut = true;
682 if ( MsProcessKilled == UINT64_MAX
683 || u64Now - MsProcessKilled > 1000)
684 {
685 if (u64Now - MsProcessKilled > 20*60*1000)
686 break; /* Give up after 20 mins. */
687
688 VBoxServiceVerbose(3, "[PID %RU32]: Timed out (%RU64ms elapsed > %RU32ms timeout), killing ...\n",
689 pProcess->uPID, cMsElapsed, pProcess->StartupInfo.uTimeLimitMS);
690
691 rc2 = RTProcTerminate(pProcess->hProcess);
692 VBoxServiceVerbose(3, "[PID %RU32]: Killing process resulted in rc=%Rrc\n",
693 pProcess->uPID, rc2);
694 MsProcessKilled = u64Now;
695 continue;
696 }
697 cMilliesLeft = 10000;
698 }
699 else
700 cMilliesLeft = pProcess->StartupInfo.uTimeLimitMS - (uint32_t)cMsElapsed;
701 }
702
703 /* Reset the polling interval since we've done all pending work. */
704 cMsPollCur = fProcessAlive
705 ? cMsPollBase
706 : RT_MS_1MIN;
707 if (cMilliesLeft < cMsPollCur)
708 cMsPollCur = cMilliesLeft;
709 }
710
711 VBoxServiceVerbose(3, "[PID %RU32]: Loop ended: rc=%Rrc, fShutdown=%RTbool, fProcessAlive=%RTbool, fProcessTimedOut=%RTbool, MsProcessKilled=%RU64\n",
712 pProcess->uPID, rc, pProcess->fShutdown, fProcessAlive, fProcessTimedOut, MsProcessKilled, MsProcessKilled);
713 VBoxServiceVerbose(3, "[PID %RU32]: *phStdOutR=%s, *phStdErrR=%s\n",
714 pProcess->uPID,
715 pProcess->hPipeStdOutR == NIL_RTPIPE ? "closed" : "open",
716 pProcess->hPipeStdErrR == NIL_RTPIPE ? "closed" : "open");
717
718 /* Signal that this thread is in progress of shutting down. */
719 ASMAtomicXchgBool(&pProcess->fShutdown, true);
720
721 /*
722 * Try killing the process if it's still alive at this point.
723 */
724 if (fProcessAlive)
725 {
726 if (MsProcessKilled == UINT64_MAX)
727 {
728 VBoxServiceVerbose(2, "[PID %RU32]: Is still alive and not killed yet\n",
729 pProcess->uPID);
730
731 MsProcessKilled = RTTimeMilliTS();
732 rc2 = RTProcTerminate(pProcess->hProcess);
733 if (rc2 == VERR_NOT_FOUND)
734 {
735 fProcessAlive = false;
736 }
737 else if (RT_FAILURE(rc2))
738 VBoxServiceError("PID %RU32]: Killing process failed with rc=%Rrc\n",
739 pProcess->uPID, rc2);
740 RTThreadSleep(500);
741 }
742
743 for (int i = 0; i < 10 && fProcessAlive; i++)
744 {
745 VBoxServiceVerbose(4, "[PID %RU32]: Kill attempt %d/10: Waiting to exit ...\n",
746 pProcess->uPID, i + 1);
747 rc2 = RTProcWait(pProcess->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
748 if (RT_SUCCESS(rc2))
749 {
750 VBoxServiceVerbose(4, "[PID %RU32]: Kill attempt %d/10: Exited\n",
751 pProcess->uPID, i + 1);
752 fProcessAlive = false;
753 break;
754 }
755 if (i >= 5)
756 {
757 VBoxServiceVerbose(4, "[PID %RU32]: Kill attempt %d/10: Trying to terminate ...\n",
758 pProcess->uPID, i + 1);
759 rc2 = RTProcTerminate(pProcess->hProcess);
760 if ( RT_FAILURE(rc)
761 && rc2 != VERR_NOT_FOUND)
762 VBoxServiceError("PID %RU32]: Killing process failed with rc=%Rrc\n",
763 pProcess->uPID, rc2);
764 }
765 RTThreadSleep(i >= 5 ? 2000 : 500);
766 }
767
768 if (fProcessAlive)
769 VBoxServiceError("[PID %RU32]: Could not be killed\n", pProcess->uPID);
770 }
771
772 /*
773 * Shutdown procedure:
774 * - Set the pProcess->fShutdown indicator to let others know we're
775 * not accepting any new requests anymore.
776 * - After setting the indicator, try to process all outstanding
777 * requests to make sure they're getting delivered.
778 *
779 * Note: After removing the process from the session's list it's not
780 * even possible for the session anymore to control what's
781 * happening to this thread, so be careful and don't mess it up.
782 */
783
784 rc2 = gstcntlProcessLock(pProcess);
785 if (RT_SUCCESS(rc2))
786 {
787 VBoxServiceVerbose(3, "[PID %RU32]: Processing outstanding requests ...\n",
788 pProcess->uPID);
789
790 /* Process all pending requests (but don't wait for new ones). */
791 Assert(pProcess->hReqQueue != NIL_RTREQQUEUE);
792 rc2 = RTReqQueueProcess(pProcess->hReqQueue, 0 /* No timeout */);
793 if ( RT_FAILURE(rc2)
794 && rc2 != VERR_TIMEOUT)
795 VBoxServiceError("[PID %RU32]: Processing outstanding requests failed with with rc=%Rrc\n",
796 pProcess->uPID, rc2);
797
798 VBoxServiceVerbose(3, "[PID %RU32]: Processing outstanding requests done, rc=%Rrc\n",
799 pProcess->uPID, rc2);
800
801 rc2 = gstcntlProcessUnlock(pProcess);
802 AssertRC(rc2);
803 }
804
805 /*
806 * If we don't have a client problem (RT_FAILURE(rc)) we'll reply to the
807 * clients exec packet now.
808 */
809 if (RT_SUCCESS(rc))
810 {
811 uint32_t uStatus = PROC_STS_UNDEFINED;
812 uint32_t uFlags = 0;
813
814 if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX)
815 {
816 VBoxServiceVerbose(3, "[PID %RU32]: Timed out and got killed\n",
817 pProcess->uPID);
818 uStatus = PROC_STS_TOK;
819 }
820 else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX)
821 {
822 VBoxServiceVerbose(3, "[PID %RU32]: Timed out and did *not* get killed\n",
823 pProcess->uPID);
824 uStatus = PROC_STS_TOA;
825 }
826 else if (pProcess->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
827 {
828 VBoxServiceVerbose(3, "[PID %RU32]: Got terminated because system/service is about to shutdown\n",
829 pProcess->uPID);
830 uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */
831 uFlags = pProcess->StartupInfo.uFlags; /* Return handed-in execution flags back to the host. */
832 }
833 else if (fProcessAlive)
834 {
835 VBoxServiceError("[PID %RU32]: Is alive when it should not!\n",
836 pProcess->uPID);
837 }
838 else if (MsProcessKilled != UINT64_MAX)
839 {
840 VBoxServiceError("[PID %RU32]: Has been killed when it should not!\n",
841 pProcess->uPID);
842 }
843 else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
844 {
845 VBoxServiceVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_NORMAL (Exit code: %d)\n",
846 pProcess->uPID, ProcessStatus.iStatus);
847
848 uStatus = PROC_STS_TEN;
849 uFlags = ProcessStatus.iStatus;
850 }
851 else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
852 {
853 VBoxServiceVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_SIGNAL (Signal: %u)\n",
854 pProcess->uPID, ProcessStatus.iStatus);
855
856 uStatus = PROC_STS_TES;
857 uFlags = ProcessStatus.iStatus;
858 }
859 else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
860 {
861 /* ProcessStatus.iStatus will be undefined. */
862 VBoxServiceVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_ABEND\n",
863 pProcess->uPID);
864
865 uStatus = PROC_STS_TEA;
866 uFlags = ProcessStatus.iStatus;
867 }
868 else
869 VBoxServiceVerbose(1, "[PID %RU32]: Handling process status %u not implemented\n",
870 pProcess->uPID, ProcessStatus.enmReason);
871
872 VBoxServiceVerbose(2, "[PID %RU32]: Ended, ClientID=%u, CID=%u, Status=%u, Flags=0x%x\n",
873 pProcess->uPID, pProcess->uClientID, pProcess->uContextID, uStatus, uFlags);
874
875 VBGLR3GUESTCTRLCMDCTX ctxEnd = { pProcess->uClientID, pProcess->uContextID };
876 rc2 = VbglR3GuestCtrlProcCbStatus(&ctxEnd,
877 pProcess->uPID, uStatus, uFlags,
878 NULL /* pvData */, 0 /* cbData */);
879 if ( RT_FAILURE(rc2)
880 && rc2 == VERR_NOT_FOUND)
881 VBoxServiceError("[PID %RU32]: Error reporting final status to host; rc=%Rrc\n",
882 pProcess->uPID, rc2);
883 }
884
885 VBoxServiceVerbose(3, "[PID %RU32]: Process loop returned with rc=%Rrc\n",
886 pProcess->uPID, rc);
887 return rc;
888}
889
890
891/**
892 * Initializes a pipe's handle and pipe object.
893 *
894 * @return IPRT status code.
895 * @param ph The pipe's handle to initialize.
896 * @param phPipe The pipe's object to initialize.
897 */
898static int gstcntlProcessInitPipe(PRTHANDLE ph, PRTPIPE phPipe)
899{
900 AssertPtrReturn(ph, VERR_INVALID_PARAMETER);
901 AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
902
903 ph->enmType = RTHANDLETYPE_PIPE;
904 ph->u.hPipe = NIL_RTPIPE;
905 *phPipe = NIL_RTPIPE;
906
907 return VINF_SUCCESS;
908}
909
910
911/**
912 * Sets up the redirection / pipe / nothing for one of the standard handles.
913 *
914 * @returns IPRT status code. No client replies made.
915 * @param pszHowTo How to set up this standard handle.
916 * @param fd Which standard handle it is (0 == stdin, 1 ==
917 * stdout, 2 == stderr).
918 * @param ph The generic handle that @a pph may be set
919 * pointing to. Always set.
920 * @param pph Pointer to the RTProcCreateExec argument.
921 * Always set.
922 * @param phPipe Where to return the end of the pipe that we
923 * should service.
924 */
925static int gstcntlProcessSetupPipe(const char *pszHowTo, int fd,
926 PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
927{
928 AssertPtrReturn(ph, VERR_INVALID_POINTER);
929 AssertPtrReturn(pph, VERR_INVALID_POINTER);
930 AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
931
932 int rc;
933
934 ph->enmType = RTHANDLETYPE_PIPE;
935 ph->u.hPipe = NIL_RTPIPE;
936 *pph = NULL;
937 *phPipe = NIL_RTPIPE;
938
939 if (!strcmp(pszHowTo, "|"))
940 {
941 /*
942 * Setup a pipe for forwarding to/from the client.
943 * The ph union struct will be filled with a pipe read/write handle
944 * to represent the "other" end to phPipe.
945 */
946 if (fd == 0) /* stdin? */
947 {
948 /* Connect a wrtie pipe specified by phPipe to stdin. */
949 rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
950 }
951 else /* stdout or stderr? */
952 {
953 /* Connect a read pipe specified by phPipe to stdout or stderr. */
954 rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
955 }
956
957 if (RT_FAILURE(rc))
958 return rc;
959
960 ph->enmType = RTHANDLETYPE_PIPE;
961 *pph = ph;
962 }
963 else if (!strcmp(pszHowTo, "/dev/null"))
964 {
965 /*
966 * Redirect to/from /dev/null.
967 */
968 RTFILE hFile;
969 rc = RTFileOpenBitBucket(&hFile, fd == 0 ? RTFILE_O_READ : RTFILE_O_WRITE);
970 if (RT_FAILURE(rc))
971 return rc;
972
973 ph->enmType = RTHANDLETYPE_FILE;
974 ph->u.hFile = hFile;
975 *pph = ph;
976 }
977 else /* Add other piping stuff here. */
978 rc = VINF_SUCCESS; /* Same as parent (us). */
979
980 return rc;
981}
982
983
984/**
985 * Expands a file name / path to its real content. This only works on Windows
986 * for now (e.g. translating "%TEMP%\foo.exe" to "C:\Windows\Temp" when starting
987 * with system / administrative rights).
988 *
989 * @return IPRT status code.
990 * @param pszPath Path to resolve.
991 * @param pszExpanded Pointer to string to store the resolved path in.
992 * @param cbExpanded Size (in bytes) of string to store the resolved path.
993 */
994static int gstcntlProcessMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded)
995{
996 int rc = VINF_SUCCESS;
997#ifdef RT_OS_WINDOWS
998 if (!ExpandEnvironmentStrings(pszPath, pszExpanded, cbExpanded))
999 rc = RTErrConvertFromWin32(GetLastError());
1000#else
1001 /* No expansion for non-Windows yet. */
1002 rc = RTStrCopy(pszExpanded, cbExpanded, pszPath);
1003#endif
1004#ifdef DEBUG
1005 VBoxServiceVerbose(3, "VBoxServiceControlExecMakeFullPath: %s -> %s\n",
1006 pszPath, pszExpanded);
1007#endif
1008 return rc;
1009}
1010
1011
1012/**
1013 * Resolves the full path of a specified executable name. This function also
1014 * resolves internal VBoxService tools to its appropriate executable path + name if
1015 * VBOXSERVICE_NAME is specified as pszFileName.
1016 *
1017 * @return IPRT status code.
1018 * @param pszFileName File name to resolve.
1019 * @param pszResolved Pointer to a string where the resolved file name will be stored.
1020 * @param cbResolved Size (in bytes) of resolved file name string.
1021 */
1022static int gstcntlProcessResolveExecutable(const char *pszFileName,
1023 char *pszResolved, size_t cbResolved)
1024{
1025 AssertPtrReturn(pszFileName, VERR_INVALID_POINTER);
1026 AssertPtrReturn(pszResolved, VERR_INVALID_POINTER);
1027 AssertReturn(cbResolved, VERR_INVALID_PARAMETER);
1028
1029 int rc = VINF_SUCCESS;
1030
1031 char szPathToResolve[RTPATH_MAX];
1032 if ( (g_pszProgName && (RTStrICmp(pszFileName, g_pszProgName) == 0))
1033 || !RTStrICmp(pszFileName, VBOXSERVICE_NAME))
1034 {
1035 /* Resolve executable name of this process. */
1036 if (!RTProcGetExecutablePath(szPathToResolve, sizeof(szPathToResolve)))
1037 rc = VERR_FILE_NOT_FOUND;
1038 }
1039 else
1040 {
1041 /* Take the raw argument to resolve. */
1042 rc = RTStrCopy(szPathToResolve, sizeof(szPathToResolve), pszFileName);
1043 }
1044
1045 if (RT_SUCCESS(rc))
1046 {
1047 rc = gstcntlProcessMakeFullPath(szPathToResolve, pszResolved, cbResolved);
1048 if (RT_SUCCESS(rc))
1049 VBoxServiceVerbose(3, "Looked up executable: %s -> %s\n",
1050 pszFileName, pszResolved);
1051 }
1052
1053 if (RT_FAILURE(rc))
1054 VBoxServiceError("Failed to lookup executable \"%s\" with rc=%Rrc\n",
1055 pszFileName, rc);
1056 return rc;
1057}
1058
1059
1060/**
1061 * Constructs the argv command line by resolving environment variables
1062 * and relative paths.
1063 *
1064 * @return IPRT status code.
1065 * @param pszArgv0 First argument (argv0), either original or modified version. Optional.
1066 * @param papszArgs Original argv command line from the host, starting at argv[1].
1067 * @param fFlags The process creation flags pass to us from the host.
1068 * @param ppapszArgv Pointer to a pointer with the new argv command line.
1069 * Needs to be freed with RTGetOptArgvFree.
1070 */
1071static int gstcntlProcessAllocateArgv(const char *pszArgv0, const char * const *papszArgs, uint32_t fFlags,
1072 char ***ppapszArgv)
1073{
1074 AssertPtrReturn(ppapszArgv, VERR_INVALID_POINTER);
1075
1076 VBoxServiceVerbose(3, "GstCntlProcessPrepareArgv: pszArgv0=%p, papszArgs=%p, fFlags=%#x, ppapszArgv=%p\n",
1077 pszArgv0, papszArgs, fFlags, ppapszArgv);
1078
1079 int rc = VINF_SUCCESS;
1080 uint32_t cArgs;
1081 for (cArgs = 0; papszArgs[cArgs]; cArgs++)
1082 {
1083 if (cArgs >= UINT32_MAX - 2)
1084 return VERR_BUFFER_OVERFLOW;
1085 }
1086
1087 /* Allocate new argv vector (adding + 2 for argv0 + termination). */
1088 size_t cbSize = (cArgs + 2) * sizeof(char*);
1089 char **papszNewArgv = (char**)RTMemAlloc(cbSize);
1090 if (!papszNewArgv)
1091 return VERR_NO_MEMORY;
1092
1093#ifdef DEBUG
1094 VBoxServiceVerbose(3, "GstCntlProcessAllocateArgv: cbSize=%RU32, cArgs=%RU32\n",
1095 cbSize, cArgs);
1096#endif
1097
1098
1099 /* HACK ALERT! Since we still don't allow the user to really specify the first
1100 argument separately from the executable image, we have to fudge
1101 a little in the unquoted argument case to deal with executables
1102 containing spaces. */
1103 /** @todo Fix the stupid host/guest protocol so the user can do this for us! */
1104 if ( !(fFlags & EXECUTEPROCESSFLAG_UNQUOTED_ARGS)
1105 || !strpbrk(pszArgv0, " \t\n\r")
1106 || pszArgv0[0] == '"')
1107 rc = RTStrDupEx(&papszNewArgv[0], pszArgv0);
1108 else
1109 {
1110 size_t cchArgv0 = strlen(pszArgv0);
1111 rc = RTStrAllocEx(&papszNewArgv[0], 1 + cchArgv0 + 1 + 1);
1112 if (RT_SUCCESS(rc))
1113 {
1114 char *pszDst = papszNewArgv[0];
1115 *pszDst++ = '"';
1116 memcpy(pszDst, pszArgv0, cchArgv0);
1117 pszDst += cchArgv0;
1118 *pszDst++ = '"';
1119 *pszDst = '\0';
1120 }
1121 }
1122 if (RT_SUCCESS(rc))
1123 {
1124 size_t i;
1125 for (i = 0; i < cArgs; i++)
1126 {
1127 char *pszArg;
1128#if 0 /* Arguments expansion -- untested. */
1129 if (fFlags & EXECUTEPROCESSFLAG_EXPAND_ARGUMENTS)
1130 {
1131/** @todo r=bird: If you want this, we need a generic implementation, preferably in RTEnv or somewhere like that. The marking
1132 * up of the variables must be the same on all platforms. */
1133 /* According to MSDN the limit on older Windows version is 32K, whereas
1134 * Vista+ there are no limits anymore. We still stick to 4K. */
1135 char szExpanded[_4K];
1136# ifdef RT_OS_WINDOWS
1137 if (!ExpandEnvironmentStrings(papszArgs[i], szExpanded, sizeof(szExpanded)))
1138 rc = RTErrConvertFromWin32(GetLastError());
1139# else
1140 /* No expansion for non-Windows yet. */
1141 rc = RTStrCopy(papszArgs[i], sizeof(szExpanded), szExpanded);
1142# endif
1143 if (RT_SUCCESS(rc))
1144 rc = RTStrDupEx(&pszArg, szExpanded);
1145 }
1146 else
1147#endif
1148 rc = RTStrDupEx(&pszArg, papszArgs[i]);
1149
1150 if (RT_FAILURE(rc))
1151 break;
1152
1153 papszNewArgv[i + 1] = pszArg;
1154 }
1155
1156 if (RT_SUCCESS(rc))
1157 {
1158 /* Terminate array. */
1159 papszNewArgv[cArgs + 1] = NULL;
1160
1161 *ppapszArgv = papszNewArgv;
1162 return VINF_SUCCESS;
1163 }
1164
1165 /* Failed, bail out. */
1166 for (; i > 0; i--)
1167 RTStrFree(papszNewArgv[i]);
1168 }
1169 RTMemFree(papszNewArgv);
1170 return rc;
1171}
1172
1173
1174/**
1175 * Assigns a valid PID to a guest control thread and also checks if there already was
1176 * another (stale) guest process which was using that PID before and destroys it.
1177 *
1178 * @return IPRT status code.
1179 * @param pProcess Process to assign PID to.
1180 * @param uPID PID to assign to the specified guest control execution thread.
1181 */
1182int gstcntlProcessAssignPID(PVBOXSERVICECTRLPROCESS pProcess, uint32_t uPID)
1183{
1184 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1185 AssertReturn(uPID, VERR_INVALID_PARAMETER);
1186
1187 AssertPtr(pProcess->pSession);
1188 int rc = RTCritSectEnter(&pProcess->pSession->CritSect);
1189 if (RT_SUCCESS(rc))
1190 {
1191 /* Search old threads using the desired PID and shut them down completely -- it's
1192 * not used anymore. */
1193 PVBOXSERVICECTRLPROCESS pProcessCur;
1194 bool fTryAgain;
1195 do
1196 {
1197 fTryAgain = false;
1198 RTListForEach(&pProcess->pSession->lstProcesses, pProcessCur, VBOXSERVICECTRLPROCESS, Node)
1199 {
1200 if (pProcessCur->uPID == uPID)
1201 {
1202 Assert(pProcessCur != pProcess); /* can't happen */
1203 uint32_t uTriedPID = uPID;
1204 uPID += 391939;
1205 VBoxServiceVerbose(2, "PID %RU32 was used before (process %p), trying again with %RU32 ...\n",
1206 uTriedPID, pProcessCur, uPID);
1207 fTryAgain = true;
1208 break;
1209 }
1210 }
1211 } while (fTryAgain);
1212
1213 /* Assign PID to current thread. */
1214 pProcess->uPID = uPID;
1215
1216 rc = RTCritSectLeave(&pProcess->pSession->CritSect);
1217 AssertRC(rc);
1218 }
1219
1220 return rc;
1221}
1222
1223
1224void gstcntlProcessFreeArgv(char **papszArgv)
1225{
1226 if (papszArgv)
1227 {
1228 size_t i = 0;
1229 while (papszArgv[i])
1230 RTStrFree(papszArgv[i++]);
1231 RTMemFree(papszArgv);
1232 }
1233}
1234
1235
1236/**
1237 * Helper function to create/start a process on the guest.
1238 *
1239 * @return IPRT status code.
1240 * @param pszExec Full qualified path of process to start (without arguments).
1241 * @param papszArgs Pointer to array of command line arguments.
1242 * @param hEnv Handle to environment block to use.
1243 * @param fFlags Process execution flags.
1244 * @param phStdIn Handle for the process' stdin pipe.
1245 * @param phStdOut Handle for the process' stdout pipe.
1246 * @param phStdErr Handle for the process' stderr pipe.
1247 * @param pszAsUser User name (account) to start the process under.
1248 * @param pszPassword Password of the specified user.
1249 * @param phProcess Pointer which will receive the process handle after
1250 * successful process start.
1251 */
1252static int gstcntlProcessCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
1253 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
1254 const char *pszPassword, PRTPROCESS phProcess)
1255{
1256 AssertPtrReturn(pszExec, VERR_INVALID_PARAMETER);
1257 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
1258 AssertPtrReturn(phProcess, VERR_INVALID_PARAMETER);
1259
1260 int rc = VINF_SUCCESS;
1261 char szExecExp[RTPATH_MAX];
1262
1263#ifdef RT_OS_WINDOWS
1264 /*
1265 * If sysprep should be executed do this in the context of VBoxService, which
1266 * (usually, if started by SCM) has administrator rights. Because of that a UI
1267 * won't be shown (doesn't have a desktop).
1268 */
1269 if (!RTStrICmp(pszExec, "sysprep"))
1270 {
1271 /* Use a predefined sysprep path as default. */
1272 char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe";
1273 /** @todo Check digital signature of file above before executing it? */
1274
1275 /*
1276 * On Windows Vista (and up) sysprep is located in "system32\\Sysprep\\sysprep.exe",
1277 * so detect the OS and use a different path.
1278 */
1279 OSVERSIONINFOEX OSInfoEx;
1280 RT_ZERO(OSInfoEx);
1281 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
1282 BOOL fRet = GetVersionEx((LPOSVERSIONINFO) &OSInfoEx);
1283 if ( fRet
1284 && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
1285 && OSInfoEx.dwMajorVersion >= 6 /* Vista or later */)
1286 {
1287 rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL);
1288#ifndef RT_ARCH_AMD64
1289 /* Don't execute 64-bit sysprep from a 32-bit service host! */
1290 char szSysWow64[RTPATH_MAX];
1291 if (RTStrPrintf(szSysWow64, sizeof(szSysWow64), "%s", szSysprepCmd))
1292 {
1293 rc = RTPathAppend(szSysWow64, sizeof(szSysWow64), "SysWow64");
1294 AssertRC(rc);
1295 }
1296 if ( RT_SUCCESS(rc)
1297 && RTPathExists(szSysWow64))
1298 VBoxServiceVerbose(0, "Warning: This service is 32-bit; could not execute sysprep on 64-bit OS!\n");
1299#endif
1300 if (RT_SUCCESS(rc))
1301 rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\Sysprep\\sysprep.exe");
1302 if (RT_SUCCESS(rc))
1303 RTPathChangeToDosSlashes(szSysprepCmd, false /* No forcing necessary */);
1304
1305 if (RT_FAILURE(rc))
1306 VBoxServiceError("Failed to detect sysrep location, rc=%Rrc\n", rc);
1307 }
1308 else if (!fRet)
1309 VBoxServiceError("Failed to retrieve OS information, last error=%ld\n", GetLastError());
1310
1311 VBoxServiceVerbose(3, "Sysprep executable is: %s\n", szSysprepCmd);
1312
1313 if (RT_SUCCESS(rc))
1314 {
1315 char **papszArgsExp;
1316 rc = gstcntlProcessAllocateArgv(szSysprepCmd /* argv0 */, papszArgs, fFlags, &papszArgsExp);
1317 if (RT_SUCCESS(rc))
1318 {
1319 /* As we don't specify credentials for the sysprep process, it will
1320 * run under behalf of the account VBoxService was started under, most
1321 * likely local system. */
1322 rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */,
1323 phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */,
1324 NULL /* pszPassword */, phProcess);
1325 gstcntlProcessFreeArgv(papszArgsExp);
1326 }
1327 }
1328
1329 if (RT_FAILURE(rc))
1330 VBoxServiceVerbose(3, "Starting sysprep returned rc=%Rrc\n", rc);
1331
1332 return rc;
1333 }
1334#endif /* RT_OS_WINDOWS */
1335
1336#ifdef VBOXSERVICE_TOOLBOX
1337 if (RTStrStr(pszExec, "vbox_") == pszExec)
1338 {
1339 /* We want to use the internal toolbox (all internal
1340 * tools are starting with "vbox_" (e.g. "vbox_cat"). */
1341 rc = gstcntlProcessResolveExecutable(VBOXSERVICE_NAME, szExecExp, sizeof(szExecExp));
1342 }
1343 else
1344 {
1345#endif
1346 /*
1347 * Do the environment variables expansion on executable and arguments.
1348 */
1349 rc = gstcntlProcessResolveExecutable(pszExec, szExecExp, sizeof(szExecExp));
1350#ifdef VBOXSERVICE_TOOLBOX
1351 }
1352#endif
1353 if (RT_SUCCESS(rc))
1354 {
1355 char **papszArgsExp;
1356 /** @todo r-bird: pszExec != argv[0]! When are you going to get that?!? How many
1357 * times does this need to be pointed out? HOST/GUEST INTERFACE IS MISDESIGNED! */
1358 rc = gstcntlProcessAllocateArgv(pszExec /* Always use the unmodified executable name as argv0. */,
1359 papszArgs /* Append the rest of the argument vector (if any). */,
1360 fFlags, &papszArgsExp);
1361 if (RT_FAILURE(rc))
1362 {
1363 /* Don't print any arguments -- may contain passwords or other sensible data! */
1364 VBoxServiceError("Could not prepare arguments, rc=%Rrc\n", rc);
1365 }
1366 else
1367 {
1368 uint32_t uProcFlags = 0;
1369 if (fFlags)
1370 {
1371 if (fFlags & EXECUTEPROCESSFLAG_HIDDEN)
1372 uProcFlags |= RTPROC_FLAGS_HIDDEN;
1373 if (fFlags & EXECUTEPROCESSFLAG_NO_PROFILE)
1374 uProcFlags |= RTPROC_FLAGS_NO_PROFILE;
1375 if (fFlags & EXECUTEPROCESSFLAG_UNQUOTED_ARGS)
1376 uProcFlags |= RTPROC_FLAGS_UNQUOTED_ARGS;
1377 }
1378
1379 /* If no user name specified run with current credentials (e.g.
1380 * full service/system rights). This is prohibited via official Main API!
1381 *
1382 * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication
1383 * code (at least on Windows) for running processes as different users
1384 * started from our system service. */
1385 if (pszAsUser && *pszAsUser)
1386 uProcFlags |= RTPROC_FLAGS_SERVICE;
1387#ifdef DEBUG
1388 VBoxServiceVerbose(3, "Command: %s\n", szExecExp);
1389 for (size_t i = 0; papszArgsExp[i]; i++)
1390 VBoxServiceVerbose(3, "\targv[%ld]: %s\n", i, papszArgsExp[i]);
1391#endif
1392 VBoxServiceVerbose(3, "Starting process \"%s\" ...\n", szExecExp);
1393
1394 /* Do normal execution. */
1395 rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags,
1396 phStdIn, phStdOut, phStdErr,
1397 pszAsUser && *pszAsUser ? pszAsUser : NULL,
1398 pszPassword && *pszPassword ? pszPassword : NULL,
1399 phProcess);
1400
1401 VBoxServiceVerbose(3, "Starting process \"%s\" returned rc=%Rrc\n",
1402 szExecExp, rc);
1403
1404 gstcntlProcessFreeArgv(papszArgsExp);
1405 }
1406 }
1407 return rc;
1408}
1409
1410
1411#ifdef DEBUG
1412static int gstcntlProcessDumpToFile(const char *pszFileName, void *pvBuf, size_t cbBuf)
1413{
1414 AssertPtrReturn(pszFileName, VERR_INVALID_POINTER);
1415 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1416
1417 if (!cbBuf)
1418 return VINF_SUCCESS;
1419
1420 char szFile[RTPATH_MAX];
1421
1422 int rc = RTPathTemp(szFile, sizeof(szFile));
1423 if (RT_SUCCESS(rc))
1424 rc = RTPathAppend(szFile, sizeof(szFile), pszFileName);
1425
1426 if (RT_SUCCESS(rc))
1427 {
1428 VBoxServiceVerbose(4, "Dumping %ld bytes to \"%s\"\n", cbBuf, szFile);
1429
1430 RTFILE fh;
1431 rc = RTFileOpen(&fh, szFile, RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
1432 if (RT_SUCCESS(rc))
1433 {
1434 rc = RTFileWrite(fh, pvBuf, cbBuf, NULL /* pcbWritten */);
1435 RTFileClose(fh);
1436 }
1437 }
1438
1439 return rc;
1440}
1441#endif
1442
1443
1444/**
1445 * The actual worker routine (loop) for a started guest process.
1446 *
1447 * @return IPRT status code.
1448 * @param PVBOXSERVICECTRLPROCESS Guest process.
1449 */
1450static int gstcntlProcessProcessWorker(PVBOXSERVICECTRLPROCESS pProcess)
1451{
1452 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1453 VBoxServiceVerbose(3, "Thread of process pThread=0x%p = \"%s\" started\n",
1454 pProcess, pProcess->StartupInfo.szCmd);
1455
1456 int rc = VbglR3GuestCtrlConnect(&pProcess->uClientID);
1457 if (RT_FAILURE(rc))
1458 {
1459 VBoxServiceError("Process thread \"%s\" (%p) failed to connect to the guest control service, rc=%Rrc\n",
1460 pProcess->StartupInfo.szCmd, pProcess, rc);
1461 RTThreadUserSignal(RTThreadSelf());
1462 return rc;
1463 }
1464
1465 VBoxServiceVerbose(3, "Guest process \"%s\" got client ID=%u, flags=0x%x\n",
1466 pProcess->StartupInfo.szCmd, pProcess->uClientID, pProcess->StartupInfo.uFlags);
1467
1468 /* The process thread is not interested in receiving any commands;
1469 * tell the host service. */
1470 rc = VbglR3GuestCtrlMsgFilterSet(pProcess->uClientID, 0 /* Skip all */,
1471 0 /* Filter mask to add */, 0 /* Filter mask to remove */);
1472 if (RT_FAILURE(rc))
1473 {
1474 VBoxServiceError("Unable to set message filter, rc=%Rrc\n", rc);
1475 /* Non-critical. */
1476 }
1477
1478 rc = GstCntlSessionProcessAdd(pProcess->pSession, pProcess);
1479 if (RT_FAILURE(rc))
1480 {
1481 VBoxServiceError("Errorwhile adding guest process \"%s\" (%p) to session process list, rc=%Rrc\n",
1482 pProcess->StartupInfo.szCmd, pProcess, rc);
1483 RTThreadUserSignal(RTThreadSelf());
1484 return rc;
1485 }
1486
1487 bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
1488
1489 /*
1490 * Prepare argument list.
1491 */
1492 char **papszArgs;
1493 uint32_t uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
1494 rc = RTGetOptArgvFromString(&papszArgs, (int*)&uNumArgs,
1495 (pProcess->StartupInfo.uNumArgs > 0) ? pProcess->StartupInfo.szArgs : "", NULL);
1496 /* Did we get the same result? */
1497 Assert(pProcess->StartupInfo.uNumArgs == uNumArgs);
1498
1499 /*
1500 * Prepare environment variables list.
1501 */
1502/** @todo r=bird: you don't need to prepare this, do you? Why don't you replace
1503 * the brilliant RTStrAPrintf call with RTEnvPutEx and drop the papszEnv related code? */
1504 char **papszEnv = NULL;
1505 uint32_t uNumEnvVars = 0; /* Initialize in case of failing ... */
1506 if (RT_SUCCESS(rc))
1507 {
1508 /* Prepare environment list. */
1509 if (pProcess->StartupInfo.uNumEnvVars)
1510 {
1511 papszEnv = (char **)RTMemAlloc(pProcess->StartupInfo.uNumEnvVars * sizeof(char*));
1512 AssertPtr(papszEnv);
1513 uNumEnvVars = pProcess->StartupInfo.uNumEnvVars;
1514
1515 const char *pszCur = pProcess->StartupInfo.szEnv;
1516 uint32_t i = 0;
1517 uint32_t cbLen = 0;
1518 while (cbLen < pProcess->StartupInfo.cbEnv)
1519 {
1520 /* sanity check */
1521 if (i >= pProcess->StartupInfo.uNumEnvVars)
1522 {
1523 rc = VERR_INVALID_PARAMETER;
1524 break;
1525 }
1526 int cbStr = RTStrAPrintf(&papszEnv[i++], "%s", pszCur);
1527 if (cbStr < 0)
1528 {
1529 rc = VERR_NO_STR_MEMORY;
1530 break;
1531 }
1532 pszCur += cbStr + 1; /* Skip terminating '\0' */
1533 cbLen += cbStr + 1; /* Skip terminating '\0' */
1534 }
1535 Assert(i == pProcess->StartupInfo.uNumEnvVars);
1536 }
1537 }
1538
1539 /*
1540 * Create the environment.
1541 */
1542 RTENV hEnv;
1543 if (RT_SUCCESS(rc))
1544 rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
1545 if (RT_SUCCESS(rc))
1546 {
1547 size_t i;
1548 for (i = 0; i < uNumEnvVars && papszEnv; i++)
1549 {
1550 rc = RTEnvPutEx(hEnv, papszEnv[i]);
1551 if (RT_FAILURE(rc))
1552 break;
1553 }
1554 if (RT_SUCCESS(rc))
1555 {
1556 /*
1557 * Setup the redirection of the standard stuff.
1558 */
1559 /** @todo consider supporting: gcc stuff.c >file 2>&1. */
1560 RTHANDLE hStdIn;
1561 PRTHANDLE phStdIn;
1562 rc = gstcntlProcessSetupPipe("|", 0 /*STDIN_FILENO*/,
1563 &hStdIn, &phStdIn, &pProcess->hPipeStdInW);
1564 if (RT_SUCCESS(rc))
1565 {
1566 RTHANDLE hStdOut;
1567 PRTHANDLE phStdOut;
1568 rc = gstcntlProcessSetupPipe( (pProcess->StartupInfo.uFlags & EXECUTEPROCESSFLAG_WAIT_STDOUT)
1569 ? "|" : "/dev/null",
1570 1 /*STDOUT_FILENO*/,
1571 &hStdOut, &phStdOut, &pProcess->hPipeStdOutR);
1572 if (RT_SUCCESS(rc))
1573 {
1574 RTHANDLE hStdErr;
1575 PRTHANDLE phStdErr;
1576 rc = gstcntlProcessSetupPipe( (pProcess->StartupInfo.uFlags & EXECUTEPROCESSFLAG_WAIT_STDERR)
1577 ? "|" : "/dev/null",
1578 2 /*STDERR_FILENO*/,
1579 &hStdErr, &phStdErr, &pProcess->hPipeStdErrR);
1580 if (RT_SUCCESS(rc))
1581 {
1582 /*
1583 * Create a poll set for the pipes and let the
1584 * transport layer add stuff to it as well.
1585 */
1586 rc = RTPollSetCreate(&pProcess->hPollSet);
1587 if (RT_SUCCESS(rc))
1588 {
1589 uint32_t uFlags = RTPOLL_EVT_ERROR;
1590#if 0
1591 /* Add reading event to pollset to get some more information. */
1592 uFlags |= RTPOLL_EVT_READ;
1593#endif
1594 /* Stdin. */
1595 if (RT_SUCCESS(rc))
1596 rc = RTPollSetAddPipe(pProcess->hPollSet,
1597 pProcess->hPipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN);
1598 /* Stdout. */
1599 if (RT_SUCCESS(rc))
1600 rc = RTPollSetAddPipe(pProcess->hPollSet,
1601 pProcess->hPipeStdOutR, uFlags, VBOXSERVICECTRLPIPEID_STDOUT);
1602 /* Stderr. */
1603 if (RT_SUCCESS(rc))
1604 rc = RTPollSetAddPipe(pProcess->hPollSet,
1605 pProcess->hPipeStdErrR, uFlags, VBOXSERVICECTRLPIPEID_STDERR);
1606 /* IPC notification pipe. */
1607 if (RT_SUCCESS(rc))
1608 rc = RTPipeCreate(&pProcess->hNotificationPipeR, &pProcess->hNotificationPipeW, 0 /* Flags */);
1609 if (RT_SUCCESS(rc))
1610 rc = RTPollSetAddPipe(pProcess->hPollSet,
1611 pProcess->hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_IPC_NOTIFY);
1612 if (RT_SUCCESS(rc))
1613 {
1614 AssertPtr(pProcess->pSession);
1615 bool fNeedsImpersonation = !(pProcess->pSession->uFlags & VBOXSERVICECTRLSESSION_FLAG_FORK);
1616
1617 rc = gstcntlProcessCreateProcess(pProcess->StartupInfo.szCmd, papszArgs, hEnv,
1618 pProcess->StartupInfo.uFlags,
1619 phStdIn, phStdOut, phStdErr,
1620 fNeedsImpersonation ? pProcess->StartupInfo.szUser : NULL,
1621 fNeedsImpersonation ? pProcess->StartupInfo.szPassword : NULL,
1622 &pProcess->hProcess);
1623 if (RT_FAILURE(rc))
1624 VBoxServiceError("Error starting process, rc=%Rrc\n", rc);
1625 /*
1626 * Tell the session thread that it can continue
1627 * spawning guest processes. This needs to be done after the new
1628 * process has been started because otherwise signal handling
1629 * on (Open) Solaris does not work correctly (see @bugref{5068}).
1630 */
1631 int rc2 = RTThreadUserSignal(RTThreadSelf());
1632 if (RT_SUCCESS(rc))
1633 rc = rc2;
1634 fSignalled = true;
1635
1636 if (RT_SUCCESS(rc))
1637 {
1638 /*
1639 * Close the child ends of any pipes and redirected files.
1640 */
1641 rc2 = RTHandleClose(phStdIn); AssertRC(rc2);
1642 phStdIn = NULL;
1643 rc2 = RTHandleClose(phStdOut); AssertRC(rc2);
1644 phStdOut = NULL;
1645 rc2 = RTHandleClose(phStdErr); AssertRC(rc2);
1646 phStdErr = NULL;
1647
1648 /* Enter the process main loop. */
1649 rc = gstcntlProcessProcLoop(pProcess);
1650
1651 /*
1652 * The handles that are no longer in the set have
1653 * been closed by the above call in order to prevent
1654 * the guest from getting stuck accessing them.
1655 * So, NIL the handles to avoid closing them again.
1656 */
1657 if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet,
1658 VBOXSERVICECTRLPIPEID_IPC_NOTIFY, NULL)))
1659 {
1660 pProcess->hNotificationPipeR = NIL_RTPIPE;
1661 pProcess->hNotificationPipeW = NIL_RTPIPE;
1662 }
1663 if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet,
1664 VBOXSERVICECTRLPIPEID_STDERR, NULL)))
1665 pProcess->hPipeStdErrR = NIL_RTPIPE;
1666 if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet,
1667 VBOXSERVICECTRLPIPEID_STDOUT, NULL)))
1668 pProcess->hPipeStdOutR = NIL_RTPIPE;
1669 if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet,
1670 VBOXSERVICECTRLPIPEID_STDIN, NULL)))
1671 pProcess->hPipeStdInW = NIL_RTPIPE;
1672 }
1673 }
1674 RTPollSetDestroy(pProcess->hPollSet);
1675
1676 RTPipeClose(pProcess->hNotificationPipeR);
1677 pProcess->hNotificationPipeR = NIL_RTPIPE;
1678 RTPipeClose(pProcess->hNotificationPipeW);
1679 pProcess->hNotificationPipeW = NIL_RTPIPE;
1680 }
1681 RTPipeClose(pProcess->hPipeStdErrR);
1682 pProcess->hPipeStdErrR = NIL_RTPIPE;
1683 RTHandleClose(phStdErr);
1684 if (phStdErr)
1685 RTHandleClose(phStdErr);
1686 }
1687 RTPipeClose(pProcess->hPipeStdOutR);
1688 pProcess->hPipeStdOutR = NIL_RTPIPE;
1689 RTHandleClose(&hStdOut);
1690 if (phStdOut)
1691 RTHandleClose(phStdOut);
1692 }
1693 RTPipeClose(pProcess->hPipeStdInW);
1694 pProcess->hPipeStdInW = NIL_RTPIPE;
1695 RTHandleClose(phStdIn);
1696 }
1697 }
1698 RTEnvDestroy(hEnv);
1699 }
1700
1701 if (pProcess->uClientID)
1702 {
1703 if (RT_FAILURE(rc))
1704 {
1705 VBGLR3GUESTCTRLCMDCTX ctx = { pProcess->uClientID, pProcess->uContextID };
1706 int rc2 = VbglR3GuestCtrlProcCbStatus(&ctx,
1707 pProcess->uPID, PROC_STS_ERROR, rc,
1708 NULL /* pvData */, 0 /* cbData */);
1709 if ( RT_FAILURE(rc2)
1710 && rc2 != VERR_NOT_FOUND)
1711 VBoxServiceError("[PID %RU32]: Could not report process failure error; rc=%Rrc (process error %Rrc)\n",
1712 pProcess->uPID, rc2, rc);
1713 }
1714
1715 /* Disconnect this client from the guest control service. This also cancels all
1716 * outstanding host requests. */
1717 VBoxServiceVerbose(3, "[PID %RU32]: Disconnecting (client ID=%u) ...\n",
1718 pProcess->uPID, pProcess->uClientID);
1719 VbglR3GuestCtrlDisconnect(pProcess->uClientID);
1720 pProcess->uClientID = 0;
1721 }
1722
1723 /* Free argument + environment variable lists. */
1724 if (uNumEnvVars)
1725 {
1726 for (uint32_t i = 0; i < uNumEnvVars; i++)
1727 RTStrFree(papszEnv[i]);
1728 RTMemFree(papszEnv);
1729 }
1730 if (uNumArgs)
1731 RTGetOptArgvFree(papszArgs);
1732
1733 /*
1734 * If something went wrong signal the user event so that others don't wait
1735 * forever on this thread.
1736 */
1737 if (RT_FAILURE(rc) && !fSignalled)
1738 RTThreadUserSignal(RTThreadSelf());
1739
1740 VBoxServiceVerbose(3, "[PID %RU32]: Thread of process \"%s\" ended with rc=%Rrc\n",
1741 pProcess->uPID, pProcess->StartupInfo.szCmd, rc);
1742
1743 /* Finally, update stopped status. */
1744 ASMAtomicXchgBool(&pProcess->fStopped, true);
1745
1746 return rc;
1747}
1748
1749
1750static int gstcntlProcessLock(PVBOXSERVICECTRLPROCESS pProcess)
1751{
1752 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1753 int rc = RTCritSectEnter(&pProcess->CritSect);
1754 AssertRC(rc);
1755 return rc;
1756}
1757
1758
1759/**
1760 * Thread main routine for a started process.
1761 *
1762 * @return IPRT status code.
1763 * @param RTTHREAD Pointer to the thread's data.
1764 * @param void* User-supplied argument pointer.
1765 *
1766 */
1767static DECLCALLBACK(int) gstcntlProcessThread(RTTHREAD ThreadSelf, void *pvUser)
1768{
1769 PVBOXSERVICECTRLPROCESS pProcess = (VBOXSERVICECTRLPROCESS*)pvUser;
1770 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1771 return gstcntlProcessProcessWorker(pProcess);
1772}
1773
1774
1775static int gstcntlProcessUnlock(PVBOXSERVICECTRLPROCESS pProcess)
1776{
1777 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1778 int rc = RTCritSectLeave(&pProcess->CritSect);
1779 AssertRC(rc);
1780 return rc;
1781}
1782
1783
1784/**
1785 * Executes (starts) a process on the guest. This causes a new thread to be created
1786 * so that this function will not block the overall program execution.
1787 *
1788 * @return IPRT status code.
1789 * @param pSession Guest session.
1790 * @param pStartupInfo Startup info.
1791 * @param uContextID Context ID to associate the process to start with.
1792
1793 */
1794int GstCntlProcessStart(const PVBOXSERVICECTRLSESSION pSession,
1795 const PVBOXSERVICECTRLPROCSTARTUPINFO pStartupInfo,
1796 uint32_t uContextID)
1797{
1798 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1799 AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER);
1800
1801 /*
1802 * Allocate new thread data and assign it to our thread list.
1803 */
1804 PVBOXSERVICECTRLPROCESS pProcess = (PVBOXSERVICECTRLPROCESS)RTMemAlloc(sizeof(VBOXSERVICECTRLPROCESS));
1805 if (!pProcess)
1806 return VERR_NO_MEMORY;
1807
1808 int rc = gstcntlProcessInit(pProcess, pSession, pStartupInfo, uContextID);
1809 if (RT_SUCCESS(rc))
1810 {
1811 static uint32_t s_uCtrlExecThread = 0;
1812 if (s_uCtrlExecThread++ == UINT32_MAX)
1813 s_uCtrlExecThread = 0; /* Wrap around to not let IPRT freak out. */
1814 rc = RTThreadCreateF(&pProcess->Thread, gstcntlProcessThread,
1815 pProcess /*pvUser*/, 0 /*cbStack*/,
1816 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "gctl%u", s_uCtrlExecThread);
1817 if (RT_FAILURE(rc))
1818 {
1819 VBoxServiceError("Creating thread for guest process \"%s\" failed: rc=%Rrc, pProcess=%p\n",
1820 pStartupInfo->szCmd, rc, pProcess);
1821
1822 GstCntlProcessFree(pProcess);
1823 }
1824 else
1825 {
1826 VBoxServiceVerbose(4, "Waiting for thread to initialize ...\n");
1827
1828 /* Wait for the thread to initialize. */
1829 rc = RTThreadUserWait(pProcess->Thread, 60 * 1000 /* 60 seconds max. */);
1830 AssertRC(rc);
1831 if ( ASMAtomicReadBool(&pProcess->fShutdown)
1832 || RT_FAILURE(rc))
1833 {
1834 VBoxServiceError("Thread for process \"%s\" failed to start, rc=%Rrc\n",
1835 pStartupInfo->szCmd, rc);
1836
1837 GstCntlProcessFree(pProcess);
1838 }
1839 else
1840 {
1841 ASMAtomicXchgBool(&pProcess->fStarted, true);
1842 }
1843 }
1844 }
1845
1846 return rc;
1847}
1848
1849
1850static DECLCALLBACK(int) gstcntlProcessOnInput(PVBOXSERVICECTRLPROCESS pThis,
1851 const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
1852 bool fPendingClose, void *pvBuf, uint32_t cbBuf)
1853{
1854 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1855 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1856
1857 int rc;
1858
1859 size_t cbWritten = 0;
1860 if (pvBuf && cbBuf)
1861 {
1862 if (pThis->hPipeStdInW != NIL_RTPIPE)
1863 {
1864 rc = RTPipeWrite(pThis->hPipeStdInW,
1865 pvBuf, cbBuf, &cbWritten);
1866 }
1867 else
1868 rc = VINF_EOF;
1869 }
1870 else
1871 rc = VERR_INVALID_PARAMETER;
1872
1873 /*
1874 * If this is the last write + we have really have written all data
1875 * we need to close the stdin pipe on our end and remove it from
1876 * the poll set.
1877 */
1878 if ( fPendingClose
1879 && (cbBuf == cbWritten))
1880 {
1881 int rc2 = gstcntlProcessPollsetCloseInput(pThis, &pThis->hPipeStdInW);
1882 if (RT_SUCCESS(rc))
1883 rc = rc2;
1884 }
1885
1886 uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status to send back to the host. */
1887 uint32_t uFlags = 0; /* No flags at the moment. */
1888 if (RT_SUCCESS(rc))
1889 {
1890 VBoxServiceVerbose(4, "[PID %RU32]: Written %RU32 bytes input, CID=%RU32, fPendingClose=%RTbool\n",
1891 pThis->uPID, cbWritten, pHostCtx->uContextID, fPendingClose);
1892 uStatus = INPUT_STS_WRITTEN;
1893 }
1894 else
1895 {
1896 if (rc == VERR_BAD_PIPE)
1897 uStatus = INPUT_STS_TERMINATED;
1898 else if (rc == VERR_BUFFER_OVERFLOW)
1899 uStatus = INPUT_STS_OVERFLOW;
1900 /* else undefined */
1901 }
1902
1903 /*
1904 * If there was an error and we did not set the host status
1905 * yet, then do it now.
1906 */
1907 if ( RT_FAILURE(rc)
1908 && uStatus == INPUT_STS_UNDEFINED)
1909 {
1910 uStatus = INPUT_STS_ERROR;
1911 uFlags = rc;
1912 }
1913 Assert(uStatus > INPUT_STS_UNDEFINED);
1914
1915#ifdef DEBUG
1916
1917#endif
1918 int rc2 = VbglR3GuestCtrlProcCbStatusInput(pHostCtx, pThis->uPID,
1919 uStatus, uFlags, (uint32_t)cbWritten);
1920 if (RT_SUCCESS(rc))
1921 rc = rc2;
1922
1923#ifdef DEBUG
1924 VBoxServiceVerbose(3, "[PID %RU32]: gstcntlProcessOnInput returned with rc=%Rrc\n",
1925 pThis->uPID, rc);
1926#endif
1927 return VINF_SUCCESS; /** @todo Return rc here as soon as RTReqQueue todos are fixed. */
1928}
1929
1930
1931static DECLCALLBACK(int) gstcntlProcessOnOutput(PVBOXSERVICECTRLPROCESS pThis,
1932 const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
1933 uint32_t uHandle, uint32_t cbToRead, uint32_t uFlags)
1934{
1935 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1936 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1937
1938 const PVBOXSERVICECTRLSESSION pSession = pThis->pSession;
1939 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1940
1941 int rc;
1942
1943 uint32_t cbBuf = cbToRead;
1944 uint8_t *pvBuf = (uint8_t *)RTMemAlloc(cbBuf);
1945 if (pvBuf)
1946 {
1947 PRTPIPE phPipe = uHandle == OUTPUT_HANDLE_ID_STDOUT
1948 ? &pThis->hPipeStdOutR
1949 : &pThis->hPipeStdErrR;
1950 AssertPtr(phPipe);
1951
1952 size_t cbRead = 0;
1953 if (*phPipe != NIL_RTPIPE)
1954 {
1955 rc = RTPipeRead(*phPipe, pvBuf, cbBuf, &cbRead);
1956 if (RT_FAILURE(rc))
1957 {
1958 RTPollSetRemove(pThis->hPollSet, uHandle == OUTPUT_HANDLE_ID_STDERR
1959 ? VBOXSERVICECTRLPIPEID_STDERR : VBOXSERVICECTRLPIPEID_STDOUT);
1960 RTPipeClose(*phPipe);
1961 *phPipe = NIL_RTPIPE;
1962 if (rc == VERR_BROKEN_PIPE)
1963 rc = VINF_EOF;
1964 }
1965 }
1966 else
1967 rc = VINF_EOF;
1968
1969#ifdef DEBUG
1970 if (RT_SUCCESS(rc))
1971 {
1972 if ( pSession->uFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT
1973 && ( uHandle == OUTPUT_HANDLE_ID_STDOUT
1974 || uHandle == OUTPUT_HANDLE_ID_STDOUT_DEPRECATED)
1975 )
1976 {
1977 char szDumpFile[RTPATH_MAX];
1978 if (!RTStrPrintf(szDumpFile, sizeof(szDumpFile), "VBoxService_Session%RU32_PID%RU32_StdOut.txt",
1979 pSession->StartupInfo.uSessionID, pThis->uPID)) rc = VERR_BUFFER_UNDERFLOW;
1980 if (RT_SUCCESS(rc))
1981 rc = gstcntlProcessDumpToFile(szDumpFile, pvBuf, cbRead);
1982 AssertRC(rc);
1983 }
1984 else if ( pSession->uFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR
1985 && uHandle == OUTPUT_HANDLE_ID_STDERR)
1986 {
1987 char szDumpFile[RTPATH_MAX];
1988 if (!RTStrPrintf(szDumpFile, sizeof(szDumpFile), "VBoxService_Session%RU32_PID%RU32_StdErr.txt",
1989 pSession->StartupInfo.uSessionID, pThis->uPID))
1990 rc = VERR_BUFFER_UNDERFLOW;
1991 if (RT_SUCCESS(rc))
1992 rc = gstcntlProcessDumpToFile(szDumpFile, pvBuf, cbRead);
1993 AssertRC(rc);
1994 }
1995 }
1996#endif
1997
1998 if (RT_SUCCESS(rc))
1999 {
2000#ifdef DEBUG
2001 VBoxServiceVerbose(3, "[PID %RU32]: Read %RU32 bytes output: uHandle=%RU32, CID=%RU32, uFlags=%x\n",
2002 pThis->uPID, cbRead, uHandle, pHostCtx->uContextID, uFlags);
2003#endif
2004 /** Note: Don't convert/touch/modify/whatever the output data here! This might be binary
2005 * data which the host needs to work with -- so just pass through all data unfiltered! */
2006
2007 /* Note: Since the context ID is unique the request *has* to be completed here,
2008 * regardless whether we got data or not! Otherwise the waiting events
2009 * on the host never will get completed! */
2010 rc = VbglR3GuestCtrlProcCbOutput(pHostCtx, pThis->uPID, uHandle, uFlags,
2011 pvBuf, cbRead);
2012 if ( RT_FAILURE(rc)
2013 && rc == VERR_NOT_FOUND) /* Not critical if guest PID is not found on the host (anymore). */
2014 rc = VINF_SUCCESS;
2015 }
2016
2017 RTMemFree(pvBuf);
2018 }
2019 else
2020 rc = VERR_NO_MEMORY;
2021
2022#ifdef DEBUG
2023 VBoxServiceVerbose(3, "[PID %RU32]: Reading output returned with rc=%Rrc\n",
2024 pThis->uPID, rc);
2025#endif
2026 return VINF_SUCCESS; /** @todo Return rc here as soon as RTReqQueue todos are fixed. */
2027}
2028
2029
2030static DECLCALLBACK(int) gstcntlProcessOnTerm(PVBOXSERVICECTRLPROCESS pThis)
2031{
2032 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2033
2034 if (!ASMAtomicXchgBool(&pThis->fShutdown, true))
2035 {
2036 VBoxServiceVerbose(3, "[PID %RU32]: Setting shutdown flag ...\n",
2037 pThis->uPID);
2038 }
2039
2040 return VINF_SUCCESS; /** @todo Return rc here as soon as RTReqQueue todos are fixed. */
2041}
2042
2043
2044int gstcntlProcessRequestExV(PVBOXSERVICECTRLPROCESS pProcess, const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
2045 bool fAsync, RTMSINTERVAL uTimeoutMS, PRTREQ pReq, PFNRT pfnFunction,
2046 unsigned cArgs, va_list Args)
2047{
2048 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2049 /* pHostCtx is optional. */
2050 AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
2051 if (!fAsync)
2052 AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
2053
2054 int rc = gstcntlProcessLock(pProcess);
2055 if (RT_SUCCESS(rc))
2056 {
2057#ifdef DEBUG
2058 VBoxServiceVerbose(3, "[PID %RU32]: gstcntlProcessRequestExV fAsync=%RTbool, uTimeoutMS=%RU32, cArgs=%u\n",
2059 pProcess->uPID, fAsync, uTimeoutMS, cArgs);
2060#endif
2061 uint32_t uFlags = RTREQFLAGS_IPRT_STATUS;
2062 if (fAsync)
2063 {
2064 Assert(uTimeoutMS == 0);
2065 uFlags |= RTREQFLAGS_NO_WAIT;
2066 }
2067
2068 rc = RTReqQueueCallV(pProcess->hReqQueue, &pReq, uTimeoutMS, uFlags,
2069 pfnFunction, cArgs, Args);
2070 if (RT_SUCCESS(rc))
2071 {
2072 /* Wake up the process' notification pipe to get
2073 * the request being processed. */
2074 Assert(pProcess->hNotificationPipeW != NIL_RTPIPE);
2075 size_t cbWritten = 0;
2076 rc = RTPipeWrite(pProcess->hNotificationPipeW, "i", 1, &cbWritten);
2077 if ( RT_SUCCESS(rc)
2078 && cbWritten != 1)
2079 {
2080 VBoxServiceError("[PID %RU32]: Notification pipe got %zu bytes instead of 1\n",
2081 pProcess->uPID, cbWritten);
2082 }
2083 else if (RT_UNLIKELY(RT_FAILURE(rc)))
2084 VBoxServiceError("[PID %RU32]: Writing to notification pipe failed, rc=%Rrc\n",
2085 pProcess->uPID, rc);
2086 }
2087 else
2088 VBoxServiceError("[PID %RU32]: RTReqQueueCallV failed, rc=%Rrc\n",
2089 pProcess->uPID, rc);
2090
2091 int rc2 = gstcntlProcessUnlock(pProcess);
2092 if (RT_SUCCESS(rc))
2093 rc = rc2;
2094 }
2095
2096#ifdef DEBUG
2097 VBoxServiceVerbose(3, "[PID %RU32]: gstcntlProcessRequestExV returned rc=%Rrc\n",
2098 pProcess->uPID, rc);
2099#endif
2100 return rc;
2101}
2102
2103
2104int gstcntlProcessRequestAsync(PVBOXSERVICECTRLPROCESS pProcess, const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
2105 PFNRT pfnFunction, unsigned cArgs, ...)
2106{
2107 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2108 /* pHostCtx is optional. */
2109 AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
2110
2111 va_list va;
2112 va_start(va, cArgs);
2113 int rc = gstcntlProcessRequestExV(pProcess, pHostCtx, true /* fAsync */, 0 /* uTimeoutMS */,
2114 NULL /* pReq */, pfnFunction, cArgs, va);
2115 va_end(va);
2116
2117 return rc;
2118}
2119
2120
2121int gstcntlProcessRequestWait(PVBOXSERVICECTRLPROCESS pProcess, const PVBGLR3GUESTCTRLCMDCTX pHostCtx,
2122 RTMSINTERVAL uTimeoutMS, PRTREQ pReq, PFNRT pfnFunction, unsigned cArgs, ...)
2123{
2124 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2125 /* pHostCtx is optional. */
2126 AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
2127
2128 va_list va;
2129 va_start(va, cArgs);
2130 int rc = gstcntlProcessRequestExV(pProcess, pHostCtx, false /* fAsync */, uTimeoutMS,
2131 pReq, pfnFunction, cArgs, va);
2132 va_end(va);
2133
2134 return rc;
2135}
2136
2137
2138int GstCntlProcessHandleInput(PVBOXSERVICECTRLPROCESS pProcess, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
2139 bool fPendingClose, void *pvBuf, uint32_t cbBuf)
2140{
2141 if (!ASMAtomicReadBool(&pProcess->fShutdown))
2142 return gstcntlProcessRequestAsync(pProcess, pHostCtx, (PFNRT)gstcntlProcessOnInput,
2143 5 /* cArgs */, pProcess, pHostCtx, fPendingClose, pvBuf, cbBuf);
2144
2145 return gstcntlProcessOnInput(pProcess, pHostCtx, fPendingClose, pvBuf, cbBuf);
2146}
2147
2148
2149int GstCntlProcessHandleOutput(PVBOXSERVICECTRLPROCESS pProcess, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
2150 uint32_t uHandle, uint32_t cbToRead, uint32_t uFlags)
2151{
2152 if (!ASMAtomicReadBool(&pProcess->fShutdown))
2153 return gstcntlProcessRequestAsync(pProcess, pHostCtx, (PFNRT)gstcntlProcessOnOutput,
2154 5 /* cArgs */, pProcess, pHostCtx, uHandle, cbToRead, uFlags);
2155
2156 return gstcntlProcessOnOutput(pProcess, pHostCtx, uHandle, cbToRead, uFlags);
2157}
2158
2159
2160int GstCntlProcessHandleTerm(PVBOXSERVICECTRLPROCESS pProcess)
2161{
2162 if (!ASMAtomicReadBool(&pProcess->fShutdown))
2163 return gstcntlProcessRequestAsync(pProcess, NULL /* pHostCtx */, (PFNRT)gstcntlProcessOnTerm,
2164 1 /* cArgs */, pProcess);
2165
2166 return gstcntlProcessOnTerm(pProcess);
2167}
2168
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