VirtualBox

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

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

Guest Control/VBoxService: Addressed some todos.

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