VirtualBox

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

Last change on this file since 44529 was 44248, checked in by vboxsync, 12 years ago

VBoxSerice/GuestCtrl: Renamed/shorted (static) function names.

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