VirtualBox

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

Last change on this file since 59131 was 59131, checked in by vboxsync, 9 years ago

VBoxServiceControlProcess.cpp: Logging.

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

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