VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.cpp@ 39300

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

VBoxService/GuestCtrl: Overhauled internals, removed pipe double buffering, now using an IPC request/response system for communication (work in progress).

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