VirtualBox

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

Last change on this file since 83287 was 83287, checked in by vboxsync, 5 years ago

VBoxService / Guest Control: Fixed guest process thread teardown / cleanup handling resulting in handle/thread leaks, separated functions more for cleaner structure. [build fix] bugref:9135

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