VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp@ 39418

Last change on this file since 39418 was 39418, checked in by vboxsync, 13 years ago

GuestCtrl: Added support for explicitly waiting on stdout/stderr, bugfixes, logging adjustments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.8 KB
Line 
1/* $Id: VBoxServiceControlThread.cpp 39418 2011-11-25 10:11:06Z vboxsync $ */
2/** @file
3 * VBoxServiceControlExecThread - Thread for every started guest process.
4 */
5
6/*
7 * Copyright (C) 2011 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/getopt.h>
26#include <iprt/handle.h>
27#include <iprt/mem.h>
28#include <iprt/path.h>
29#include <iprt/pipe.h>
30#include <iprt/poll.h>
31#include <iprt/process.h>
32#include <iprt/semaphore.h>
33#include <iprt/string.h>
34#include <iprt/thread.h>
35
36#include <VBox/VBoxGuestLib.h>
37#include <VBox/HostServices/GuestControlSvc.h>
38
39#include "VBoxServiceInternal.h"
40
41using namespace guestControl;
42
43/* Internal functions. */
44static int vboxServiceControlThreadFree(PVBOXSERVICECTRLTHREAD pThread);
45
46/**
47 * Allocates and gives back a thread data struct which then can be used by the worker thread.
48 * Needs to be freed with VBoxServiceControlExecDestroyThreadData().
49 *
50 * @return IPRT status code.
51 * @param pThread The thread's handle to allocate the data for.
52 * @param u32ContextID The context ID bound to this request / command.
53 * @param pszCmd Full qualified path of process to start (without arguments).
54 * @param uFlags Process execution flags.
55 * @param pszArgs String of arguments to pass to the process to start.
56 * @param uNumArgs Number of arguments specified in pszArgs.
57 * @param pszEnv String of environment variables ("FOO=BAR") to pass to the process
58 * to start.
59 * @param cbEnv Size (in bytes) of environment variables.
60 * @param uNumEnvVars Number of environment variables specified in pszEnv.
61 * @param pszUser User name (account) to start the process under.
62 * @param pszPassword Password of specified user name (account).
63 * @param uTimeLimitMS Time limit (in ms) of the process' life time.
64 */
65int VBoxServiceControlExecThreadAlloc(PVBOXSERVICECTRLTHREAD pThread,
66 uint32_t u32ContextID,
67 const char *pszCmd, uint32_t uFlags,
68 const char *pszArgs, uint32_t uNumArgs,
69 const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
70 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS)
71{
72 AssertPtr(pThread);
73
74 /* General stuff. */
75 pThread->Node.pPrev = NULL;
76 pThread->Node.pNext = NULL;
77
78 pThread->fShutdown = false;
79 pThread->fStarted = false;
80 pThread->fStopped = false;
81
82 pThread->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(&pThread->CritSect);
88 if (RT_FAILURE(rc))
89 return rc;
90
91 rc = RTSemEventMultiCreate(&pThread->RequestEvent);
92 AssertRCReturn(rc, rc);
93
94 pThread->uPID = 0; /* Don't have a PID yet. */
95 pThread->pszCmd = RTStrDup(pszCmd);
96 pThread->uFlags = uFlags;
97 pThread->uTimeLimitMS = ( uTimeLimitMS == UINT32_MAX
98 || uTimeLimitMS == 0)
99 ? RT_INDEFINITE_WAIT : uTimeLimitMS;
100
101 /* Prepare argument list. */
102 pThread->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
103 rc = RTGetOptArgvFromString(&pThread->papszArgs, (int*)&pThread->uNumArgs,
104 (uNumArgs > 0) ? pszArgs : "", NULL);
105 /* Did we get the same result? */
106 Assert(uNumArgs == pThread->uNumArgs);
107
108 if (RT_SUCCESS(rc))
109 {
110 /* Prepare environment list. */
111 pThread->uNumEnvVars = 0;
112 if (uNumEnvVars)
113 {
114 pThread->papszEnv = (char **)RTMemAlloc(uNumEnvVars * sizeof(char*));
115 AssertPtr(pThread->papszEnv);
116 pThread->uNumEnvVars = uNumEnvVars;
117
118 const char *pszCur = pszEnv;
119 uint32_t i = 0;
120 uint32_t cbLen = 0;
121 while (cbLen < cbEnv)
122 {
123 /* sanity check */
124 if (i >= uNumEnvVars)
125 {
126 rc = VERR_INVALID_PARAMETER;
127 break;
128 }
129 int cbStr = RTStrAPrintf(&pThread->papszEnv[i++], "%s", pszCur);
130 if (cbStr < 0)
131 {
132 rc = VERR_NO_STR_MEMORY;
133 break;
134 }
135 pszCur += cbStr + 1; /* Skip terminating '\0' */
136 cbLen += cbStr + 1; /* Skip terminating '\0' */
137 }
138 }
139
140 /* User management. */
141 pThread->pszUser = RTStrDup(pszUser);
142 AssertPtr(pThread->pszUser);
143 pThread->pszPassword = RTStrDup(pszPassword);
144 AssertPtr(pThread->pszPassword);
145 }
146
147 if (RT_FAILURE(rc)) /* Clean up on failure. */
148 vboxServiceControlThreadFree(pThread);
149 return rc;
150}
151
152
153/**
154 * Frees a guest thread.
155 *
156 * @return IPRT status code.
157 * @param pThread Thread to shut down.
158 */
159static int vboxServiceControlThreadFree(PVBOXSERVICECTRLTHREAD pThread)
160{
161 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
162
163 VBoxServiceVerbose(1, "ControlThread: [PID %u]: Shutting down ...\n",
164 pThread->uPID);
165
166 /* Signal the request event to unblock potential waiters. */
167 int rc = RTSemEventMultiSignal(pThread->RequestEvent);
168 if (RT_FAILURE(rc))
169 VBoxServiceError("ControlThread: [PID %u]: Signalling request event failed, rc=%Rrc\n",
170 pThread->uPID, rc);
171
172 rc = RTCritSectEnter(&pThread->CritSect);
173 if (RT_SUCCESS(rc))
174 {
175 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Freeing thread data ...\n",
176 pThread->uPID);
177
178 RTStrFree(pThread->pszCmd);
179 if (pThread->uNumEnvVars)
180 {
181 for (uint32_t i = 0; i < pThread->uNumEnvVars; i++)
182 RTStrFree(pThread->papszEnv[i]);
183 RTMemFree(pThread->papszEnv);
184 }
185 RTGetOptArgvFree(pThread->papszArgs);
186 RTStrFree(pThread->pszUser);
187 RTStrFree(pThread->pszPassword);
188
189 rc = RTSemEventMultiDestroy(pThread->RequestEvent);
190 AssertRC(rc);
191
192 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Cleaning up ...\n",
193 pThread->uPID);
194
195 /* Set stopped status. */
196 ASMAtomicXchgBool(&pThread->fStopped, true);
197
198 rc = RTCritSectLeave(&pThread->CritSect);
199 AssertRC(rc);
200 }
201
202 /*
203 * Destroy other thread data.
204 */
205 if (RTCritSectIsInitialized(&pThread->CritSect))
206 RTCritSectDelete(&pThread->CritSect);
207
208 /*
209 * Destroy thread structure as final step.
210 */
211 RTMemFree(pThread);
212 pThread = NULL;
213
214 return rc;
215}
216
217
218/**
219 * Signals a guest process thread that we want it to shut down in
220 * a gentle way.
221 *
222 * @return IPRT status code.
223 * @param pThread Thread to shut down.
224 */
225int VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread)
226{
227 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
228
229 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Signalling shutdown ...\n",
230 pThread->uPID);
231
232 /* Do *not* set pThread->fShutdown or other stuff here!
233 * The guest thread loop will do that as soon as it processes the quit message. */
234
235 VBOXSERVICECTRLREQUEST ctrlRequest;
236 RT_ZERO(ctrlRequest);
237 ctrlRequest.enmType = VBOXSERVICECTRLREQUEST_QUIT;
238
239 int rc = VBoxServiceControlThreadPerform(pThread->uPID, &ctrlRequest);
240 if (RT_FAILURE(rc))
241 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Sending quit request failed with rc=%Rrc\n",
242 pThread->uPID, rc);
243 return rc;
244}
245
246
247/**
248 * Wait for a guest process thread to shut down.
249 *
250 * @return IPRT status code.
251 * @param pThread Thread to wait shutting down for.
252 * @param RTMSINTERVAL Timeout in ms to wait for shutdown.
253 */
254int VBoxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread,
255 RTMSINTERVAL msTimeout)
256{
257 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
258 int rc = VINF_SUCCESS;
259 if ( pThread->Thread != NIL_RTTHREAD
260 && !ASMAtomicReadBool(&pThread->fStopped)) /* Only shutdown threads which aren't yet. */
261 {
262 VBoxServiceVerbose(2, "ControlThread: [PID %u]: Waiting for shutdown ...\n",
263 pThread->uPID);
264
265 /* Wait a bit ... */
266 int rcThread;
267 rc = RTThreadWait(pThread->Thread, msTimeout, &rcThread);
268 if (RT_FAILURE(rcThread))
269 {
270 VBoxServiceError("ControlThread: [PID %u]: Shutdown returned error rc=%Rrc\n",
271 pThread->uPID, rcThread);
272 if (RT_SUCCESS(rc))
273 rc = rcThread;
274 }
275 }
276 return rc;
277}
278
279
280/**
281 * Returns the guest process thread's current status.
282 *
283 * @return VBOXSERVICECTRLTHREADSTATUS
284 * @param pThread Thread to determine current status for.
285 */
286VBOXSERVICECTRLTHREADSTATUS VBoxServiceControlThreadGetStatus(const PVBOXSERVICECTRLTHREAD pThread)
287{
288 AssertPtrReturn(pThread, VBOXSERVICECTRLTHREADSTATUS_UNKNOWN);
289
290 int rc = RTCritSectEnter(&pThread->CritSect);
291 if (RT_SUCCESS(rc))
292 {
293 Assert(pThread->fStarted != pThread->fStopped);
294
295 VBOXSERVICECTRLTHREADSTATUS enmStatus = VBOXSERVICECTRLTHREADSTATUS_UNKNOWN;
296 /** @todo Add more logic here. */
297 /** @todo Remove fStarted/fStopped and just use this VBOXSERVICECTRLTHREADSTATUS. */
298 if (pThread->fStarted)
299 enmStatus = VBOXSERVICECTRLTHREADSTATUS_STARTED;
300 else if (pThread->fStopped)
301 enmStatus = VBOXSERVICECTRLTHREADSTATUS_STOPPED;
302 else
303 AssertMsgFailed(("ControlThread: Uknown thread status (0x%x)\n"));
304
305 rc = RTCritSectLeave(&pThread->CritSect);
306 AssertRC(rc);
307
308 return enmStatus;
309 }
310
311 return VBOXSERVICECTRLTHREADSTATUS_UNKNOWN;
312}
313
314
315/**
316 * Closes the stdin pipe of a guest process.
317 *
318 * @return IPRT status code.
319 * @param hPollSet The polling set.
320 * @param phStdInW The standard input pipe handle.
321 */
322static int VBoxServiceControlThreadCloseStdIn(RTPOLLSET hPollSet, PRTPIPE phStdInW)
323{
324 int rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN);
325 if (rc != VERR_POLL_HANDLE_ID_NOT_FOUND)
326 AssertRC(rc);
327
328 rc = RTPipeClose(*phStdInW);
329 AssertRC(rc);
330 *phStdInW = NIL_RTPIPE;
331
332 return rc;
333}
334
335
336/**
337 * Handle an error event on standard input.
338 *
339 * @return IPRT status code.
340 * @param hPollSet The polling set.
341 * @param fPollEvt The event mask returned by RTPollNoResume.
342 * @param phStdInW The standard input pipe handle.
343 */
344static int VBoxServiceControlThreadHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW)
345{
346 NOREF(fPollEvt);
347
348 return VBoxServiceControlThreadCloseStdIn(hPollSet, phStdInW);
349}
350
351
352/**
353 * Handle pending output data or error on standard out or standard error.
354 *
355 * @returns IPRT status code from client send.
356 * @param hPollSet The polling set.
357 * @param fPollEvt The event mask returned by RTPollNoResume.
358 * @param phPipeR The pipe handle.
359 * @param idPollHnd The pipe ID to handle.
360 *
361 */
362static int VBoxServiceControlThreadHandleOutputError(RTPOLLSET hPollSet, uint32_t fPollEvt,
363 PRTPIPE phPipeR, uint32_t idPollHnd)
364{
365 int rc = VINF_SUCCESS;
366
367 rc = RTPollSetRemove(hPollSet, idPollHnd);
368 AssertRC(rc);
369
370 rc = RTPipeClose(*phPipeR);
371 AssertRC(rc);
372 *phPipeR = NIL_RTPIPE;
373
374 return rc;
375}
376
377
378/**
379 * Handle pending output data or error on standard out or standard error.
380 *
381 * @returns IPRT status code from client send.
382 * @param hPollSet The polling set.
383 * @param fPollEvt The event mask returned by RTPollNoResume.
384 * @param phPipeR The pipe handle.
385 * @param idPollHnd The pipe ID to handle.
386 *
387 */
388static int VBoxServiceControlThreadHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt,
389 PRTPIPE phPipeR, uint32_t idPollHnd)
390{
391 int rc = VINF_SUCCESS;
392 if (fPollEvt == RTPOLL_EVT_READ)
393 {
394 char abBuf[_64K];
395 size_t cbRead;
396 int rc = RTPipeRead(*phPipeR, abBuf, sizeof(abBuf), &cbRead);
397 if (RT_SUCCESS(rc) && cbRead)
398 {
399#ifdef DEBUG
400 VBoxServiceVerbose(4, "ControlThread: Drain idPollHnd=%u, abBuf=0x%p, cbRead=%u\n",
401 idPollHnd, abBuf, cbRead);
402#endif
403 /* Goes to bit bucket ... */
404
405 /* Make sure we go another poll round in case there was too much data
406 for the buffer to hold. */
407 fPollEvt &= RTPOLL_EVT_ERROR;
408 }
409 else if (RT_FAILURE(rc))
410 {
411 fPollEvt |= RTPOLL_EVT_ERROR;
412 AssertMsg(rc == VERR_BROKEN_PIPE, ("%Rrc\n", rc));
413 }
414 }
415
416 if (fPollEvt & RTPOLL_EVT_ERROR)
417 rc = VBoxServiceControlThreadHandleOutputError(hPollSet, fPollEvt,
418 phPipeR, idPollHnd);
419 return rc;
420}
421
422
423static int VBoxServiceControlThreadHandleRequest(RTPOLLSET hPollSet, uint32_t fPollEvt,
424 PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR,
425 PVBOXSERVICECTRLTHREAD pThread)
426{
427#ifdef DEBUG_andy
428 VBoxServiceVerbose(4, "ControlThread: HandleIPCRequest\n");
429#endif
430
431 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
432 AssertPtrReturn(phStdInW, VERR_INVALID_POINTER);
433 AssertPtrReturn(phStdOutR, VERR_INVALID_POINTER);
434 AssertPtrReturn(phStdErrR, VERR_INVALID_POINTER);
435
436 /*int rc = RTCritSectEnter(&pThread->CritSect);
437 if (RT_SUCCESS(rc))
438 {*/
439 /* Drain the notification pipe. */
440 uint8_t abBuf[8];
441 size_t cbIgnore;
442 rc = RTPipeRead(pThread->hNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
443 if (RT_FAILURE(rc))
444 VBoxServiceError("ControlThread: Draining IPC notification pipe failed with rc=%Rrc\n", rc);
445
446 int rcReq = VINF_SUCCESS; /* Actual request result. */
447
448 PVBOXSERVICECTRLREQUEST pRequest = pThread->pRequest;
449 AssertPtr(pRequest);
450
451 switch (pRequest->enmType)
452 {
453 case VBOXSERVICECTRLREQUEST_QUIT: /* Main control asked us to quit. */
454 {
455 /** @todo Check for some conditions to check to
456 * veto quitting. */
457 pThread->fShutdown = true;
458 rcReq = VINF_SUCCESS;
459 break;
460 }
461
462 case VBOXSERVICECTRLREQUEST_STDIN_WRITE:
463 /* Fall through is intentional. */
464 case VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF:
465 {
466 AssertPtrReturn(pRequest->pvData, VERR_INVALID_POINTER);
467 AssertReturn(pRequest->cbData, VERR_INVALID_PARAMETER);
468
469 size_t cbWritten = 0;
470 if (*phStdInW != NIL_RTPIPE)
471 {
472 rcReq = RTPipeWrite(*phStdInW,
473 pRequest->pvData, pRequest->cbData, &cbWritten);
474 }
475 else
476 rcReq = VINF_EOF;
477
478 /* If this is the last write we need to close the stdin pipe on our
479 * end and remove it from the poll set. */
480 if (VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF == pRequest->enmType)
481 rc = VBoxServiceControlThreadCloseStdIn(hPollSet, phStdInW);
482
483 /* Reqport back actual data written (if any). */
484 pRequest->cbData = cbWritten;
485 break;
486 }
487
488 case VBOXSERVICECTRLREQUEST_STDOUT_READ:
489 /* Fall through is intentional. */
490 case VBOXSERVICECTRLREQUEST_STDERR_READ:
491 {
492 AssertPtrReturn(pRequest->pvData, VERR_INVALID_POINTER);
493 AssertReturn(pRequest->cbData, VERR_INVALID_PARAMETER);
494
495 PRTPIPE pPipeR = pRequest->enmType == VBOXSERVICECTRLREQUEST_STDERR_READ
496 ? phStdErrR : phStdOutR;
497 AssertPtr(pPipeR);
498
499 size_t cbRead = 0;
500 if (*pPipeR != NIL_RTPIPE)
501 {
502 rcReq = RTPipeRead(*pPipeR,
503 pRequest->pvData, pRequest->cbData, &cbRead);
504 if (rcReq == VERR_BROKEN_PIPE)
505 rcReq = VINF_EOF;
506 }
507 else
508 rcReq = VINF_EOF;
509
510 /* Report back actual data read (if any). */
511 pRequest->cbData = cbRead;
512 break;
513 }
514
515 default:
516 rcReq = VERR_NOT_IMPLEMENTED;
517 break;
518 }
519
520 /* Assign overall result. */
521 pRequest->rc = RT_SUCCESS(rc)
522 ? rcReq : rc;
523
524 VBoxServiceVerbose(2, "ControlThread: [PID %u]: Handled req=%u, CID=%u, rcReq=%Rrc, cbData=%u\n",
525 pThread->uPID, pRequest->enmType, pRequest->uCID, rcReq, pRequest->cbData);
526
527 /* In any case, regardless of the result, we notify
528 * the main guest control to unblock it. */
529 int rc2 = RTSemEventMultiSignal(pThread->RequestEvent);
530 AssertRC(rc2);
531 /* No access to pRequest here anymore -- could be out of scope
532 * or modified already! */
533
534 /*rc2 = RTCritSectLeave(&pThread->CritSect);
535 AssertRC(rc2);
536 }*/
537
538 return rc;
539}
540
541
542/**
543 * Execution loop which runs in a dedicated per-started-process thread and
544 * handles all pipe input/output and signalling stuff.
545 *
546 * @return IPRT status code.
547 * @param pThread The process' thread handle.
548 * @param hProcess The actual process handle.
549 * @param cMsTimeout Time limit (in ms) of the process' life time.
550 * @param hPollSet The poll set to use.
551 * @param hStdInW Handle to the process' stdin write end.
552 * @param hStdOutR Handle to the process' stdout read end.
553 * @param hStdErrR Handle to the process' stderr read end.
554 */
555static int VBoxServiceControlThreadProcLoop(PVBOXSERVICECTRLTHREAD pThread,
556 RTPROCESS hProcess, RTMSINTERVAL cMsTimeout, RTPOLLSET hPollSet,
557 PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR)
558{
559 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
560 AssertPtrReturn(phStdInW, VERR_INVALID_PARAMETER);
561 /* Rest is optional. */
562
563 int rc;
564 int rc2;
565 uint64_t const MsStart = RTTimeMilliTS();
566 RTPROCSTATUS ProcessStatus = { 254, RTPROCEXITREASON_ABEND };
567 bool fProcessAlive = true;
568 bool fProcessTimedOut = false;
569 uint64_t MsProcessKilled = UINT64_MAX;
570 RTMSINTERVAL const cMsPollBase = *phStdInW != NIL_RTPIPE
571 ? 100 /* Need to poll for input. */
572 : 1000; /* Need only poll for process exit and aborts. */
573 RTMSINTERVAL cMsPollCur = 0;
574
575 /*
576 * Assign PID to thread data.
577 * Also check if there already was a thread with the same PID and shut it down -- otherwise
578 * the first (stale) entry will be found and we get really weird results!
579 */
580 rc = VBoxServiceControlAssignPID(pThread, hProcess);
581 if (RT_FAILURE(rc))
582 {
583 VBoxServiceError("ControlThread: Unable to assign PID=%u, to new thread, rc=%Rrc\n",
584 hProcess, rc);
585 return rc;
586 }
587
588 /*
589 * Before entering the loop, tell the host that we've started the guest
590 * and that it's now OK to send input to the process.
591 */
592 VBoxServiceVerbose(2, "ControlThread: [PID %u]: Process \"%s\" started, CID=%u, User=%s\n",
593 pThread->uPID, pThread->pszCmd, pThread->uContextID, pThread->pszUser);
594 rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
595 pThread->uPID, PROC_STS_STARTED, 0 /* u32Flags */,
596 NULL /* pvData */, 0 /* cbData */);
597
598 /*
599 * Process input, output, the test pipe and client requests.
600 */
601 while ( RT_SUCCESS(rc)
602 && RT_UNLIKELY(!pThread->fShutdown))
603 {
604 /*
605 * Wait/Process all pending events.
606 */
607 uint32_t idPollHnd;
608 uint32_t fPollEvt;
609 rc2 = RTPollNoResume(hPollSet, /*cMsPollCur*/ RT_INDEFINITE_WAIT, &fPollEvt, &idPollHnd);
610 if (pThread->fShutdown)
611 continue;
612
613 cMsPollCur = 0; /* No rest until we've checked everything. */
614
615 if (RT_SUCCESS(rc2))
616 {
617 /*VBoxServiceVerbose(4, "ControlThread: [PID %u}: RTPollNoResume idPollHnd=%u\n",
618 pThread->uPID, idPollHnd);*/
619 switch (idPollHnd)
620 {
621 case VBOXSERVICECTRLPIPEID_STDIN:
622 rc = VBoxServiceControlThreadHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW);
623 break;
624
625 case VBOXSERVICECTRLPIPEID_STDOUT:
626 rc = VBoxServiceControlThreadHandleOutputEvent(hPollSet, fPollEvt,
627 phStdOutR, idPollHnd);
628 break;
629
630 case VBOXSERVICECTRLPIPEID_STDERR:
631 rc = VBoxServiceControlThreadHandleOutputEvent(hPollSet, fPollEvt,
632 phStdErrR, idPollHnd);
633 break;
634
635 case VBOXSERVICECTRLPIPEID_IPC_NOTIFY:
636 rc = VBoxServiceControlThreadHandleRequest(hPollSet, fPollEvt,
637 phStdInW, phStdOutR, phStdErrR, pThread);
638 break;
639 }
640
641 if (RT_FAILURE(rc) || rc == VINF_EOF)
642 break; /* Abort command, or client dead or something. */
643
644 if (RT_UNLIKELY(pThread->fShutdown))
645 break; /* We were asked to shutdown. */
646
647 /* Do we have more to poll for (e.g. is there (still) something in our pollset
648 * like stdout, stderr or stdin)? If we only have our notification pipe left we
649 * need to bail out. */
650 if (RTPollSetGetCount(hPollSet) > 1)
651 continue;
652 }
653
654 /*
655 * Check for process death.
656 */
657 if (fProcessAlive)
658 {
659 rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
660 if (RT_SUCCESS_NP(rc2))
661 {
662 fProcessAlive = false;
663 continue;
664 }
665 if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
666 continue;
667 if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
668 {
669 fProcessAlive = false;
670 ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
671 ProcessStatus.iStatus = 255;
672 AssertFailed();
673 }
674 else
675 AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
676 }
677
678 /*
679 * If the process has terminated, we're should head out.
680 */
681 if (!fProcessAlive)
682 break;
683
684 /*
685 * Check for timed out, killing the process.
686 */
687 uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
688 if (cMsTimeout != RT_INDEFINITE_WAIT)
689 {
690 uint64_t u64Now = RTTimeMilliTS();
691 uint64_t cMsElapsed = u64Now - MsStart;
692 if (cMsElapsed >= cMsTimeout)
693 {
694 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Timed out (%ums elapsed > %ums timeout), killing ...",
695 pThread->uPID, cMsElapsed, cMsTimeout);
696
697 fProcessTimedOut = true;
698 if ( MsProcessKilled == UINT64_MAX
699 || u64Now - MsProcessKilled > 1000)
700 {
701 if (u64Now - MsProcessKilled > 20*60*1000)
702 break; /* Give up after 20 mins. */
703 RTProcTerminate(hProcess);
704 MsProcessKilled = u64Now;
705 continue;
706 }
707 cMilliesLeft = 10000;
708 }
709 else
710 cMilliesLeft = cMsTimeout - (uint32_t)cMsElapsed;
711 }
712
713 /* Reset the polling interval since we've done all pending work. */
714 cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft;
715
716 /*
717 * Need to exit?
718 */
719 if (pThread->fShutdown)
720 break;
721 }
722
723 /*
724 * Try kill the process if it's still alive at this point.
725 */
726 if (fProcessAlive)
727 {
728 if (MsProcessKilled == UINT64_MAX)
729 {
730 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Is still alive and not killed yet\n",
731 pThread->uPID);
732
733 MsProcessKilled = RTTimeMilliTS();
734 RTProcTerminate(hProcess);
735 RTThreadSleep(500);
736 }
737
738 for (size_t i = 0; i < 10; i++)
739 {
740 VBoxServiceVerbose(4, "ControlThread: [PID %u]: Kill attempt %d/10: Waiting to exit ...\n",
741 pThread->uPID, i + 1);
742 rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
743 if (RT_SUCCESS(rc2))
744 {
745 VBoxServiceVerbose(4, "ControlThread: [PID %u]: Kill attempt %d/10: Exited\n",
746 pThread->uPID, i + 1);
747 fProcessAlive = false;
748 break;
749 }
750 if (i >= 5)
751 {
752 VBoxServiceVerbose(4, "ControlThread: [PID %u]: Kill attempt %d/10: Trying to terminate ...\n",
753 pThread->uPID, i + 1);
754 RTProcTerminate(hProcess);
755 }
756 RTThreadSleep(i >= 5 ? 2000 : 500);
757 }
758
759 if (fProcessAlive)
760 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Could not be killed\n", pThread->uPID);
761 }
762
763 /*
764 * If we don't have a client problem (RT_FAILURE(rc)) we'll reply to the
765 * clients exec packet now.
766 */
767 if (RT_SUCCESS(rc))
768 {
769 uint32_t uStatus = PROC_STS_UNDEFINED;
770 uint32_t uFlags = 0;
771
772 if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX)
773 {
774 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Timed out and got killed\n",
775 pThread->uPID);
776 uStatus = PROC_STS_TOK;
777 }
778 else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX)
779 {
780 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Timed out and did *not* get killed\n",
781 pThread->uPID);
782 uStatus = PROC_STS_TOA;
783 }
784 else if (pThread->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
785 {
786 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Got terminated because system/service is about to shutdown\n",
787 pThread->uPID);
788 uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */
789 uFlags = pThread->uFlags; /* Return handed-in execution flags back to the host. */
790 }
791 else if (fProcessAlive)
792 {
793 VBoxServiceError("ControlThread: [PID %u]: Is alive when it should not!\n",
794 pThread->uPID);
795 }
796 else if (MsProcessKilled != UINT64_MAX)
797 {
798 VBoxServiceError("ControlThread: [PID %u]: Has been killed when it should not!\n",
799 pThread->uPID);
800 }
801 else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
802 {
803 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Ended with RTPROCEXITREASON_NORMAL (%u)\n",
804 pThread->uPID, ProcessStatus.iStatus);
805
806 uStatus = PROC_STS_TEN;
807 uFlags = ProcessStatus.iStatus;
808 }
809 else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
810 {
811 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Ended with RTPROCEXITREASON_SIGNAL (%u)\n",
812 pThread->uPID, ProcessStatus.iStatus);
813
814 uStatus = PROC_STS_TES;
815 uFlags = ProcessStatus.iStatus;
816 }
817 else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
818 {
819 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Ended with RTPROCEXITREASON_ABEND (%u)\n",
820 pThread->uPID, ProcessStatus.iStatus);
821
822 uStatus = PROC_STS_TEA;
823 uFlags = ProcessStatus.iStatus;
824 }
825 else
826 VBoxServiceVerbose(1, "ControlThread: [PID %u]: Handling process status %u not implemented\n",
827 pThread->uPID, ProcessStatus.enmReason);
828
829 VBoxServiceVerbose(2, "ControlThread: [PID %u]: Ended, ClientID=%u, CID=%u, Status=%u, Flags=0x%x\n",
830 pThread->uPID, pThread->uClientID, pThread->uContextID, uStatus, uFlags);
831 rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
832 pThread->uPID, uStatus, uFlags,
833 NULL /* pvData */, 0 /* cbData */);
834 if (RT_FAILURE(rc))
835 VBoxServiceError("ControlThread: [PID %u]: Error reporting final status to host; rc=%Rrc\n",
836 pThread->uPID, rc);
837
838 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Process loop ended with rc=%Rrc\n",
839 pThread->uPID, rc);
840 }
841 else
842 VBoxServiceError("ControlThread: [PID %u]: Loop failed with rc=%Rrc\n",
843 pThread->uPID, rc);
844 return rc;
845}
846
847
848static int vboxServiceControlThreadInitPipe(PRTHANDLE ph, PRTPIPE phPipe)
849{
850 AssertPtrReturn(ph, VERR_INVALID_PARAMETER);
851 AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
852
853 ph->enmType = RTHANDLETYPE_PIPE;
854 ph->u.hPipe = NIL_RTPIPE;
855 *phPipe = NIL_RTPIPE;
856
857 return VINF_SUCCESS;
858}
859
860
861/**
862 * Sets up the redirection / pipe / nothing for one of the standard handles.
863 *
864 * @returns IPRT status code. No client replies made.
865 * @param fd Which standard handle it is (0 == stdin, 1 ==
866 * stdout, 2 == stderr).
867 * @param ph The generic handle that @a pph may be set
868 * pointing to. Always set.
869 * @param pph Pointer to the RTProcCreateExec argument.
870 * Always set.
871 * @param phPipe Where to return the end of the pipe that we
872 * should service. Always set.
873 */
874static int VBoxServiceControlThreadSetupPipe(int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
875{
876 AssertPtrReturn(ph, VERR_INVALID_PARAMETER);
877 AssertPtrReturn(pph, VERR_INVALID_PARAMETER);
878 AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
879
880 int rc;
881
882 /*
883 * Setup a pipe for forwarding to/from the client.
884 * The ph union struct will be filled with a pipe read/write handle
885 * to represent the "other" end to phPipe.
886 */
887 if (fd == 0) /* stdin? */
888 {
889 /* Connect a wrtie pipe specified by phPipe to stdin. */
890 rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
891 }
892 else /* stdout or stderr? */
893 {
894 /* Connect a read pipe specified by phPipe to stdout or stderr. */
895 rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
896 }
897 if (RT_FAILURE(rc))
898 return rc;
899 ph->enmType = RTHANDLETYPE_PIPE;
900 *pph = ph;
901
902 return rc;
903}
904
905
906/**
907 * Expands a file name / path to its real content. This only works on Windows
908 * for now (e.g. translating "%TEMP%\foo.exe" to "C:\Windows\Temp" when starting
909 * with system / administrative rights).
910 *
911 * @return IPRT status code.
912 * @param pszPath Path to resolve.
913 * @param pszExpanded Pointer to string to store the resolved path in.
914 * @param cbExpanded Size (in bytes) of string to store the resolved path.
915 */
916static int VBoxServiceControlThreadMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded)
917{
918 int rc = VINF_SUCCESS;
919#ifdef RT_OS_WINDOWS
920 if (!ExpandEnvironmentStrings(pszPath, pszExpanded, cbExpanded))
921 rc = RTErrConvertFromWin32(GetLastError());
922#else
923 /* No expansion for non-Windows yet. */
924 rc = RTStrCopy(pszExpanded, cbExpanded, pszPath);
925#endif
926#ifdef DEBUG
927 VBoxServiceVerbose(3, "ControlThread: VBoxServiceControlExecMakeFullPath: %s -> %s\n",
928 pszPath, pszExpanded);
929#endif
930 return rc;
931}
932
933
934/**
935 * Resolves the full path of a specified executable name. This function also
936 * resolves internal VBoxService tools to its appropriate executable path + name.
937 *
938 * @return IPRT status code.
939 * @param pszFileName File name to resovle.
940 * @param pszResolved Pointer to a string where the resolved file name will be stored.
941 * @param cbResolved Size (in bytes) of resolved file name string.
942 */
943static int VBoxServiceControlThreadResolveExecutable(const char *pszFileName, char *pszResolved, size_t cbResolved)
944{
945 int rc = VINF_SUCCESS;
946
947 /* Search the path of our executable. */
948 char szVBoxService[RTPATH_MAX];
949 if (RTProcGetExecutablePath(szVBoxService, sizeof(szVBoxService)))
950 {
951 char *pszExecResolved = NULL;
952 if ( (g_pszProgName && RTStrICmp(pszFileName, g_pszProgName) == 0)
953 || !RTStrICmp(pszFileName, VBOXSERVICE_NAME))
954 {
955 /* We just want to execute VBoxService (no toolbox). */
956 pszExecResolved = RTStrDup(szVBoxService);
957 }
958 else /* Nothing to resolve, copy original. */
959 pszExecResolved = RTStrDup(pszFileName);
960 AssertPtr(pszExecResolved);
961
962 rc = VBoxServiceControlThreadMakeFullPath(pszExecResolved, pszResolved, cbResolved);
963#ifdef DEBUG
964 VBoxServiceVerbose(3, "ControlThread: VBoxServiceControlExecResolveExecutable: %s -> %s\n",
965 pszFileName, pszResolved);
966#endif
967 RTStrFree(pszExecResolved);
968 }
969 return rc;
970}
971
972
973/**
974 * Constructs the argv command line by resolving environment variables
975 * and relative paths.
976 *
977 * @return IPRT status code.
978 * @param pszArgv0 First argument (argv0), either original or modified version.
979 * @param papszArgs Original argv command line from the host, starting at argv[1].
980 * @param ppapszArgv Pointer to a pointer with the new argv command line.
981 * Needs to be freed with RTGetOptArgvFree.
982 */
983static int VBoxServiceControlThreadPrepareArgv(const char *pszArgv0,
984 const char * const *papszArgs, char ***ppapszArgv)
985{
986/** @todo RTGetOptArgvToString converts to MSC quoted string, while
987 * RTGetOptArgvFromString takes bourne shell according to the docs...
988 * Actually, converting to and from here is a very roundabout way of prepending
989 * an entry (pszFilename) to an array (*ppapszArgv). */
990 int rc = VINF_SUCCESS;
991 char *pszNewArgs = NULL;
992 if (pszArgv0)
993 rc = RTStrAAppend(&pszNewArgs, pszArgv0);
994 if ( RT_SUCCESS(rc)
995 && papszArgs)
996
997 {
998 char *pszArgs;
999 rc = RTGetOptArgvToString(&pszArgs, papszArgs,
1000 RTGETOPTARGV_CNV_QUOTE_MS_CRT); /* RTGETOPTARGV_CNV_QUOTE_BOURNE_SH */
1001 if (RT_SUCCESS(rc))
1002 {
1003 rc = RTStrAAppend(&pszNewArgs, " ");
1004 if (RT_SUCCESS(rc))
1005 rc = RTStrAAppend(&pszNewArgs, pszArgs);
1006 }
1007 }
1008
1009 if (RT_SUCCESS(rc))
1010 {
1011 int iNumArgsIgnored;
1012 rc = RTGetOptArgvFromString(ppapszArgv, &iNumArgsIgnored,
1013 pszNewArgs ? pszNewArgs : "", NULL /* Use standard separators. */);
1014 }
1015
1016 if (pszNewArgs)
1017 RTStrFree(pszNewArgs);
1018 return rc;
1019}
1020
1021
1022/**
1023 * Helper function to create/start a process on the guest.
1024 *
1025 * @return IPRT status code.
1026 * @param pszExec Full qualified path of process to start (without arguments).
1027 * @param papszArgs Pointer to array of command line arguments.
1028 * @param hEnv Handle to environment block to use.
1029 * @param fFlags Process execution flags.
1030 * @param phStdIn Handle for the process' stdin pipe.
1031 * @param phStdOut Handle for the process' stdout pipe.
1032 * @param phStdErr Handle for the process' stderr pipe.
1033 * @param pszAsUser User name (account) to start the process under.
1034 * @param pszPassword Password of the specified user.
1035 * @param phProcess Pointer which will receive the process handle after
1036 * successful process start.
1037 */
1038static int VBoxServiceControlThreadCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
1039 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
1040 const char *pszPassword, PRTPROCESS phProcess)
1041{
1042 AssertPtrReturn(pszExec, VERR_INVALID_PARAMETER);
1043 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
1044 AssertPtrReturn(phProcess, VERR_INVALID_PARAMETER);
1045
1046 int rc = VINF_SUCCESS;
1047 char szExecExp[RTPATH_MAX];
1048#ifdef RT_OS_WINDOWS
1049 /*
1050 * If sysprep should be executed do this in the context of VBoxService, which
1051 * (usually, if started by SCM) has administrator rights. Because of that a UI
1052 * won't be shown (doesn't have a desktop).
1053 */
1054 if (RTStrICmp(pszExec, "sysprep") == 0)
1055 {
1056 /* Use a predefined sysprep path as default. */
1057 char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe";
1058
1059 /*
1060 * On Windows Vista (and up) sysprep is located in "system32\\sysprep\\sysprep.exe",
1061 * so detect the OS and use a different path.
1062 */
1063 OSVERSIONINFOEX OSInfoEx;
1064 RT_ZERO(OSInfoEx);
1065 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
1066 if ( GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
1067 && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
1068 && OSInfoEx.dwMajorVersion >= 6 /* Vista or later */)
1069 {
1070 rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL);
1071 if (RT_SUCCESS(rc))
1072 rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\sysprep\\sysprep.exe");
1073 }
1074
1075 if (RT_SUCCESS(rc))
1076 {
1077 char **papszArgsExp;
1078 rc = VBoxServiceControlThreadPrepareArgv(szSysprepCmd /* argv0 */, papszArgs, &papszArgsExp);
1079 if (RT_SUCCESS(rc))
1080 {
1081 rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */,
1082 phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */,
1083 NULL /* pszPassword */, phProcess);
1084 }
1085 RTGetOptArgvFree(papszArgsExp);
1086 }
1087 return rc;
1088 }
1089#endif /* RT_OS_WINDOWS */
1090
1091#ifdef VBOXSERVICE_TOOLBOX
1092 if (RTStrStr(pszExec, "vbox_") == pszExec)
1093 {
1094 /* We want to use the internal toolbox (all internal
1095 * tools are starting with "vbox_" (e.g. "vbox_cat"). */
1096 rc = VBoxServiceControlThreadResolveExecutable(VBOXSERVICE_NAME, szExecExp, sizeof(szExecExp));
1097 }
1098 else
1099 {
1100#endif
1101 /*
1102 * Do the environment variables expansion on executable and arguments.
1103 */
1104 rc = VBoxServiceControlThreadResolveExecutable(pszExec, szExecExp, sizeof(szExecExp));
1105#ifdef VBOXSERVICE_TOOLBOX
1106 }
1107#endif
1108 if (RT_SUCCESS(rc))
1109 {
1110 char **papszArgsExp;
1111 rc = VBoxServiceControlThreadPrepareArgv(pszExec /* Always use the unmodified executable name as argv0. */,
1112 papszArgs /* Append the rest of the argument vector (if any). */, &papszArgsExp);
1113 if (RT_SUCCESS(rc))
1114 {
1115 uint32_t uProcFlags = 0;
1116 if (fFlags)
1117 {
1118 if (fFlags & EXECUTEPROCESSFLAG_HIDDEN)
1119 uProcFlags |= RTPROC_FLAGS_HIDDEN;
1120 if (fFlags & EXECUTEPROCESSFLAG_NO_PROFILE)
1121 uProcFlags |= RTPROC_FLAGS_NO_PROFILE;
1122 }
1123
1124 /* If no user name specified run with current credentials (e.g.
1125 * full service/system rights). This is prohibited via official Main API!
1126 *
1127 * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication
1128 * code (at least on Windows) for running processes as different users
1129 * started from our system service. */
1130 if (*pszAsUser)
1131 uProcFlags |= RTPROC_FLAGS_SERVICE;
1132#ifdef DEBUG
1133 VBoxServiceVerbose(3, "ControlThread: Command: %s\n", szExecExp);
1134 for (size_t i = 0; papszArgsExp[i]; i++)
1135 VBoxServiceVerbose(3, "ControlThread:\targv[%ld]: %s\n", i, papszArgsExp[i]);
1136#endif
1137 /* Do normal execution. */
1138 rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags,
1139 phStdIn, phStdOut, phStdErr,
1140 *pszAsUser ? pszAsUser : NULL,
1141 *pszPassword ? pszPassword : NULL,
1142 phProcess);
1143 RTGetOptArgvFree(papszArgsExp);
1144 }
1145 }
1146 return rc;
1147}
1148
1149/**
1150 * The actual worker routine (lopp) for a started guest process.
1151 *
1152 * @return IPRT status code.
1153 * @param PVBOXSERVICECTRLTHREAD Thread data associated with a started process.
1154 */
1155static DECLCALLBACK(int) VBoxServiceControlThreadProcessWorker(PVBOXSERVICECTRLTHREAD pThread)
1156{
1157 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
1158 VBoxServiceVerbose(3, "ControlThread: Thread of process \"%s\" started\n", pThread->pszCmd);
1159
1160 int rc = VbglR3GuestCtrlConnect(&pThread->uClientID);
1161 if (RT_FAILURE(rc))
1162 {
1163 VBoxServiceError("ControlThread: Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc);
1164 RTThreadUserSignal(RTThreadSelf());
1165 return rc;
1166 }
1167 VBoxServiceVerbose(3, "ControlThread: Guest process \"%s\" got client ID=%u, flags=0x%x\n",
1168 pThread->pszCmd, pThread->uClientID, pThread->uFlags);
1169
1170 bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
1171
1172 /*
1173 * Create the environment.
1174 */
1175 RTENV hEnv;
1176 rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
1177 if (RT_SUCCESS(rc))
1178 {
1179 size_t i;
1180 for (i = 0; i < pThread->uNumEnvVars && pThread->papszEnv; i++)
1181 {
1182 rc = RTEnvPutEx(hEnv, pThread->papszEnv[i]);
1183 if (RT_FAILURE(rc))
1184 break;
1185 }
1186 if (RT_SUCCESS(rc))
1187 {
1188 /*
1189 * Setup the redirection of the standard stuff.
1190 */
1191 /** @todo consider supporting: gcc stuff.c >file 2>&1. */
1192 RTHANDLE hStdIn;
1193 PRTHANDLE phStdIn;
1194 rc = VBoxServiceControlThreadSetupPipe(0 /*STDIN_FILENO*/, &hStdIn, &phStdIn, &pThread->pipeStdInW);
1195 if (RT_SUCCESS(rc))
1196 {
1197 RTHANDLE hStdOut;
1198 PRTHANDLE phStdOut;
1199 RTPIPE hStdOutR;
1200 rc = VBoxServiceControlThreadSetupPipe(1 /*STDOUT_FILENO*/, &hStdOut, &phStdOut, &hStdOutR);
1201 if (RT_SUCCESS(rc))
1202 {
1203 RTHANDLE hStdErr;
1204 PRTHANDLE phStdErr;
1205 RTPIPE hStdErrR;
1206 rc = VBoxServiceControlThreadSetupPipe(2 /*STDERR_FILENO*/, &hStdErr, &phStdErr, &hStdErrR);
1207 if (RT_SUCCESS(rc))
1208 {
1209 /*
1210 * Create a poll set for the pipes and let the
1211 * transport layer add stuff to it as well.
1212 */
1213 RTPOLLSET hPollSet;
1214 rc = RTPollSetCreate(&hPollSet);
1215 if (RT_SUCCESS(rc))
1216 {
1217 /* Stdin. */
1218 if (RT_SUCCESS(rc))
1219 rc = RTPollSetAddPipe(hPollSet, pThread->pipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN);
1220 /* Stdout. */
1221 if (RT_SUCCESS(rc))
1222 {
1223 uint32_t uFlags = RTPOLL_EVT_ERROR;
1224 if (!(pThread->uFlags & EXECUTEPROCESSFLAG_WAIT_STDOUT))
1225 {
1226 uFlags |= RTPOLL_EVT_READ;
1227 VBoxServiceVerbose(3, "ControlThread: Host is not interested in getting stdout for \"%s\", poll flags=0x%x\n",
1228 pThread->pszCmd, uFlags);
1229 }
1230 rc = RTPollSetAddPipe(hPollSet, hStdOutR, uFlags, VBOXSERVICECTRLPIPEID_STDOUT);
1231 }
1232 /* Stderr. */
1233 if (RT_SUCCESS(rc))
1234 {
1235 uint32_t uFlags = RTPOLL_EVT_ERROR;
1236 if (!(pThread->uFlags & EXECUTEPROCESSFLAG_WAIT_STDERR))
1237 {
1238 uFlags |= RTPOLL_EVT_READ;
1239 VBoxServiceVerbose(3, "ControlThread: Host is not interested in getting stderr for \"%s\", poll flags=0x%x\n",
1240 pThread->pszCmd, uFlags);
1241 }
1242 rc = RTPollSetAddPipe(hPollSet, hStdErrR, uFlags, VBOXSERVICECTRLPIPEID_STDERR);
1243 }
1244 /* IPC notification pipe. */
1245 if (RT_SUCCESS(rc))
1246 rc = RTPipeCreate(&pThread->hNotificationPipeR, &pThread->hNotificationPipeW, 0 /* Flags */);
1247 if (RT_SUCCESS(rc))
1248 rc = RTPollSetAddPipe(hPollSet, pThread->hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_IPC_NOTIFY);
1249
1250 if (RT_SUCCESS(rc))
1251 {
1252 RTPROCESS hProcess;
1253 rc = VBoxServiceControlThreadCreateProcess(pThread->pszCmd, pThread->papszArgs, hEnv, pThread->uFlags,
1254 phStdIn, phStdOut, phStdErr,
1255 pThread->pszUser, pThread->pszPassword,
1256 &hProcess);
1257 if (RT_FAILURE(rc))
1258 VBoxServiceError("ControlThread: Error starting process, rc=%Rrc\n", rc);
1259 /*
1260 * Tell the control thread that it can continue
1261 * spawning services. This needs to be done after the new
1262 * process has been started because otherwise signal handling
1263 * on (Open) Solaris does not work correctly (see #5068).
1264 */
1265 int rc2 = RTThreadUserSignal(RTThreadSelf());
1266 if (RT_FAILURE(rc2))
1267 rc = rc2;
1268 fSignalled = true;
1269
1270 if (RT_SUCCESS(rc))
1271 {
1272 /*
1273 * Close the child ends of any pipes and redirected files.
1274 */
1275 rc2 = RTHandleClose(phStdIn); AssertRC(rc2);
1276 phStdIn = NULL;
1277 rc2 = RTHandleClose(phStdOut); AssertRC(rc2);
1278 phStdOut = NULL;
1279 rc2 = RTHandleClose(phStdErr); AssertRC(rc2);
1280 phStdErr = NULL;
1281
1282 /* Enter the process loop. */
1283 rc = VBoxServiceControlThreadProcLoop(pThread,
1284 hProcess, pThread->uTimeLimitMS, hPollSet,
1285 &pThread->pipeStdInW, &hStdOutR, &hStdErrR);
1286 /*
1287 * The handles that are no longer in the set have
1288 * been closed by the above call in order to prevent
1289 * the guest from getting stuck accessing them.
1290 * So, NIL the handles to avoid closing them again.
1291 */
1292 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_IPC_NOTIFY, NULL)))
1293 {
1294 pThread->hNotificationPipeR = NIL_RTPIPE;
1295 pThread->hNotificationPipeW = NIL_RTPIPE;
1296 }
1297 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDERR, NULL)))
1298 hStdErrR = NIL_RTPIPE;
1299 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDOUT, NULL)))
1300 hStdOutR = NIL_RTPIPE;
1301 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDIN, NULL)))
1302 pThread->pipeStdInW = NIL_RTPIPE;
1303 }
1304 else /* Something went wrong; report error! */
1305 {
1306 VBoxServiceError("ControlThread: Could not start process '%s' (CID: %u)! Error: %Rrc\n",
1307 pThread->pszCmd, pThread->uContextID, rc);
1308
1309 rc2 = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, pThread->uPID,
1310 PROC_STS_ERROR, rc,
1311 NULL /* pvData */, 0 /* cbData */);
1312 if (RT_FAILURE(rc2))
1313 VBoxServiceError("ControlThread: Could not report process start error! Error: %Rrc (process error %Rrc)\n",
1314 rc2, rc);
1315 }
1316 }
1317 RTPollSetDestroy(hPollSet);
1318
1319 RTPipeClose(pThread->hNotificationPipeR);
1320 pThread->hNotificationPipeR = NIL_RTPIPE;
1321 RTPipeClose(pThread->hNotificationPipeW);
1322 pThread->hNotificationPipeW = NIL_RTPIPE;
1323 }
1324 RTPipeClose(hStdErrR);
1325 hStdErrR = NIL_RTPIPE;
1326 RTHandleClose(phStdErr);
1327 }
1328 RTPipeClose(hStdOutR);
1329 hStdOutR = NIL_RTPIPE;
1330 RTHandleClose(phStdOut);
1331 }
1332 RTPipeClose(pThread->pipeStdInW);
1333 RTHandleClose(phStdIn);
1334 }
1335 }
1336 RTEnvDestroy(hEnv);
1337 }
1338
1339 if (pThread->uClientID)
1340 {
1341 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Cancelling pending waits (client ID=%u)\n",
1342 pThread->uPID, pThread->uClientID);
1343 int rc2 = VbglR3GuestCtrlCancelPendingWaits(pThread->uClientID);
1344 if (RT_FAILURE(rc2))
1345 {
1346 VBoxServiceError("ControlThread: [PID %u]: Cancelling pending waits failed; rc=%Rrc\n",
1347 pThread->uPID, rc2);
1348 if (RT_SUCCESS(rc))
1349 rc = rc2;
1350 }
1351
1352 /* Disconnect from guest control service. */
1353 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Disconnecting (client ID=%u) ...\n",
1354 pThread->uPID, pThread->uClientID);
1355 VbglR3GuestCtrlDisconnect(pThread->uClientID);
1356 pThread->uClientID = 0;
1357 }
1358
1359 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Thread of process \"%s\" ended with rc=%Rrc\n",
1360 pThread->uPID, pThread->pszCmd, rc);
1361
1362 ASMAtomicXchgBool(&pThread->fStarted, false);
1363
1364 /*
1365 * If something went wrong signal the user event so that others don't wait
1366 * forever on this thread.
1367 */
1368 if (RT_FAILURE(rc) && !fSignalled)
1369 RTThreadUserSignal(RTThreadSelf());
1370
1371 /*
1372 * Remove thread from global thread list. After this it's safe to shutdown
1373 * and deallocate this thread.
1374 */
1375 VBoxServiceControlRemoveThread(pThread);
1376
1377 int rc2 = vboxServiceControlThreadFree(pThread);
1378 if (RT_SUCCESS(rc))
1379 rc = rc2;
1380
1381 return rc;
1382}
1383
1384
1385/**
1386 * Thread main routine for a started process.
1387 *
1388 * @return IPRT status code.
1389 * @param RTTHREAD Pointer to the thread's data.
1390 * @param void* User-supplied argument pointer.
1391 *
1392 */
1393static DECLCALLBACK(int) VBoxServiceControlThread(RTTHREAD ThreadSelf, void *pvUser)
1394{
1395 PVBOXSERVICECTRLTHREAD pThread = (VBOXSERVICECTRLTHREAD*)pvUser;
1396 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
1397 return VBoxServiceControlThreadProcessWorker(pThread);
1398}
1399
1400
1401/**
1402 * Executes (starts) a process on the guest. This causes a new thread to be created
1403 * so that this function will not block the overall program execution.
1404 *
1405 * @return IPRT status code.
1406 * @param uClientID Client ID for accessing host service.
1407 * @param uContextID Context ID to associate the process to start with.
1408 * @param pszCmd Full qualified path of process to start (without arguments).
1409 * @param uFlags Process execution flags.
1410 * @param pszArgs String of arguments to pass to the process to start.
1411 * @param uNumArgs Number of arguments specified in pszArgs.
1412 * @param pszEnv String of environment variables ("FOO=BAR") to pass to the process
1413 * to start.
1414 * @param cbEnv Size (in bytes) of environment variables.
1415 * @param uNumEnvVars Number of environment variables specified in pszEnv.
1416 * @param pszUser User name (account) to start the process under.
1417 * @param pszPassword Password of specified user name (account).
1418 * @param uTimeLimitMS Time limit (in ms) of the process' life time.
1419 * @param ppNode The thread's list node to insert into the global thread list
1420 * on success.
1421 */
1422int VBoxServiceControlThreadStart(uint32_t uClientID, uint32_t uContextID,
1423 const char *pszCmd, uint32_t uFlags,
1424 const char *pszArgs, uint32_t uNumArgs,
1425 const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
1426 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS,
1427 PRTLISTNODE *ppNode)
1428{
1429 AssertPtrReturn(ppNode, VERR_INVALID_POINTER);
1430
1431 /*
1432 * Allocate new thread data and assign it to our thread list.
1433 */
1434 PVBOXSERVICECTRLTHREAD pThread = (PVBOXSERVICECTRLTHREAD)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREAD));
1435 if (!pThread)
1436 return VERR_NO_MEMORY;
1437
1438 int rc = VBoxServiceControlExecThreadAlloc(pThread,
1439 uContextID,
1440 pszCmd, uFlags,
1441 pszArgs, uNumArgs,
1442 pszEnv, cbEnv, uNumEnvVars,
1443 pszUser, pszPassword,
1444 uTimeLimitMS);
1445 if (RT_SUCCESS(rc))
1446 {
1447 static uint32_t uCtrlExecThread = 0;
1448 char szThreadName[32];
1449 if (!RTStrPrintf(szThreadName, sizeof(szThreadName), "controlexec%ld", uCtrlExecThread++))
1450 AssertMsgFailed(("Unable to create unique control exec thread name!\n"));
1451
1452 rc = RTThreadCreate(&pThread->Thread, VBoxServiceControlThread,
1453 (void *)(PVBOXSERVICECTRLTHREAD*)pThread, 0,
1454 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, szThreadName);
1455 if (RT_FAILURE(rc))
1456 {
1457 VBoxServiceError("ControlThread: RTThreadCreate failed, rc=%Rrc\n, pThread=%p\n",
1458 rc, pThread);
1459 }
1460 else
1461 {
1462 VBoxServiceVerbose(4, "ControlThread: Waiting for thread to initialize ...\n");
1463
1464 /* Wait for the thread to initialize. */
1465 RTThreadUserWait(pThread->Thread, 60 * 1000 /* 60 seconds max. */);
1466 if (ASMAtomicReadBool(&pThread->fShutdown))
1467 {
1468 VBoxServiceError("ControlThread: Thread for process \"%s\" failed to start!\n", pszCmd);
1469 rc = VERR_GENERAL_FAILURE;
1470 }
1471 else
1472 {
1473 pThread->fStarted = true;
1474
1475 /* Return the thread's node. */
1476 *ppNode = &pThread->Node;
1477 }
1478 }
1479 }
1480
1481 if (RT_FAILURE(rc))
1482 RTMemFree(pThread);
1483
1484 return rc;
1485}
1486
1487
1488/**
1489 * Performs a request to a specific (formerly started) guest process and waits
1490 * for its response.
1491 *
1492 * @return IPRT status code.
1493 * @param uPID PID of guest process to perform a request to.
1494 * @param pRequest Pointer to request to perform.
1495 */
1496int VBoxServiceControlThreadPerform(uint32_t uPID, PVBOXSERVICECTRLREQUEST pRequest)
1497{
1498 AssertPtrReturn(pRequest, VERR_INVALID_POINTER);
1499 AssertReturn(pRequest->enmType > VBOXSERVICECTRLREQUEST_UNKNOWN, VERR_INVALID_PARAMETER);
1500 /* Rest in pRequest is optional (based on the request type). */
1501
1502 int rc = VINF_SUCCESS;
1503 const PVBOXSERVICECTRLTHREAD pThread = VBoxServiceControlGetThreadLocked(uPID);
1504 if (pThread)
1505 {
1506 /* Set request result to some defined state in case
1507 * it got cancelled. */
1508 pRequest->rc = VERR_CANCELLED;
1509
1510 /* Set request structure pointer. */
1511 pThread->pRequest = pRequest;
1512
1513 /** @todo To speed up simultaneous guest process handling we could add a worker threads
1514 * or queue in order to wait for the request to happen. Later. */
1515
1516 /* Wake up guest thrad by sending a wakeup byte to the notification pipe so
1517 * that RTPoll unblocks (returns) and we then can do our requested operation. */
1518 if (pThread->hNotificationPipeW == NIL_RTPIPE)
1519 rc = VERR_BROKEN_PIPE;
1520 size_t cbWritten;
1521 if (RT_SUCCESS(rc))
1522 rc = RTPipeWrite(pThread->hNotificationPipeW, "i", 1, &cbWritten);
1523
1524 if ( RT_SUCCESS(rc)
1525 && cbWritten)
1526 {
1527 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Waiting for response on enmType=%u, pvData=0x%p, cbData=%u\n",
1528 uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData);
1529 /* Wait on the request to get completed (or we are asked to abort/shutdown). */
1530 rc = RTSemEventMultiWait(pThread->RequestEvent, RT_INDEFINITE_WAIT);
1531 if (RT_SUCCESS(rc))
1532 {
1533 VBoxServiceVerbose(4, "ControlThread: [PID %u]: Performed with rc=%Rrc, cbData=%u\n",
1534 uPID, pRequest->rc, pRequest->cbData);
1535
1536 /* Give back overall request result. */
1537 rc = pRequest->rc;
1538
1539 /* Reset the semaphore. */
1540 int rc2 = RTSemEventMultiReset(pThread->RequestEvent);
1541 if (RT_FAILURE(rc2))
1542 VBoxServiceError("ControlThread: [PID %u]: Unable to reset request event, rc=%Rrc\n",
1543 uPID, rc2);
1544 }
1545 else
1546 VBoxServiceError("ControlThread: [PID %u]: Wait failed, rc=%Rrc\n",
1547 uPID, rc);
1548 }
1549
1550 VBoxServiceControlThreadUnlock(pThread);
1551 }
1552 else /* PID not found! */
1553 rc = VERR_NOT_FOUND;
1554
1555 VBoxServiceVerbose(3, "ControlThread: [PID %u]: Performed enmType=%u, uCID=%u, pvData=0x%p, cbData=%u, rc=%Rrc\n",
1556 uPID, pRequest->enmType, pRequest->uCID, pRequest->pvData, pRequest->cbData, rc);
1557 return rc;
1558}
1559
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