VirtualBox

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

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

Build fix.

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