VirtualBox

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

Last change on this file since 47476 was 47476, checked in by vboxsync, 11 years ago

Format string: RU32 -> RU64.

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