VirtualBox

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

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

VBoxService/GuestCtrl: Only close stdin when we really wrote all data of last block.

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

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