VirtualBox

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

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

GuestCtrl/VBoxService: Added active/inactive guest process thread handling, put process starting parameters into a struct, shortcut for handling 0-byte write requests.

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