VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExec.cpp@ 36174

Last change on this file since 36174 was 36174, checked in by vboxsync, 14 years ago

Logging adjustments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 74.1 KB
Line 
1/* $Id: VBoxServiceControlExec.cpp 36174 2011-03-04 14:46:08Z vboxsync $ */
2/** @file
3 * VBoxServiceControlExec - Utility functions for process execution.
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/assert.h>
23#include <iprt/crc.h>
24#include <iprt/ctype.h>
25#include <iprt/env.h>
26#include <iprt/file.h>
27#include <iprt/getopt.h>
28#include <iprt/handle.h>
29#include <iprt/mem.h>
30#include <iprt/path.h>
31#include <iprt/param.h>
32#include <iprt/pipe.h>
33#include <iprt/poll.h>
34#include <iprt/process.h>
35#include <iprt/string.h>
36#include <iprt/semaphore.h>
37#include <iprt/stream.h>
38#include <iprt/thread.h>
39#include <VBox/version.h>
40#include <VBox/VBoxGuestLib.h>
41#include <VBox/HostServices/GuestControlSvc.h>
42#include "VBoxServiceInternal.h"
43#include "VBoxServiceUtils.h"
44
45using namespace guestControl;
46
47extern RTLISTNODE g_GuestControlExecThreads;
48
49static int VBoxServiceControlExecPipeInit(PVBOXSERVICECTRLEXECPIPEBUF pBuf, bool fNeedNotificationPipe);
50static int VBoxServiceControlExecPipeBufRead(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
51 uint8_t *pbBuffer, uint32_t cbBuffer, uint32_t *pcbToRead);
52static int VBoxServiceControlExecPipeBufWrite(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
53 uint8_t *pbData, uint32_t cbData, bool fPendingClose, uint32_t *pcbWritten);
54static bool VBoxServiceControlExecPipeBufIsEnabled(PVBOXSERVICECTRLEXECPIPEBUF pBuf);
55static int VBoxServiceControlExecPipeBufSetStatus(PVBOXSERVICECTRLEXECPIPEBUF pBuf, bool fEnabled);
56static void VBoxServiceControlExecPipeBufDestroy(PVBOXSERVICECTRLEXECPIPEBUF pBuf);
57
58
59/**
60 * Handle an error event on standard input.
61 *
62 * @returns IPRT status code.
63 * @param hPollSet The polling set.
64 * @param fPollEvt The event mask returned by RTPollNoResume.
65 * @param phStdInW The standard input pipe handle.
66 * @param pStdInBuf The standard input buffer.
67 */
68static int VBoxServiceControlExecProcHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
69 PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf)
70{
71 int rc = RTCritSectEnter(&pStdInBuf->CritSect);
72 if (RT_SUCCESS(rc))
73 {
74 int rc2 = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
75 /* Don't assert if writable handle is not in poll set anymore. */
76 if ( RT_FAILURE(rc2)
77 && rc2 != VERR_POLL_HANDLE_ID_NOT_FOUND)
78 {
79 AssertRC(rc2);
80 }
81
82 rc2 = RTPipeClose(*phStdInW);
83 AssertRC(rc2);
84 *phStdInW = NIL_RTPIPE;
85
86 /* Mark the stdin buffer as dead; we're not using it anymore. */
87 pStdInBuf->fEnabled = false;
88
89 rc2 = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_ERROR);
90 AssertRC(rc2);
91
92 rc2 = RTCritSectLeave(&pStdInBuf->CritSect);
93 if (RT_SUCCESS(rc))
94 rc = rc2;
95 }
96 return rc;
97}
98
99
100/**
101 * Try write some more data to the standard input of the child.
102 *
103 * @returns IPRT status code.
104 * @retval VINF_TRY_AGAIN if there is still data left in the buffer.
105 *
106 * @param hPollSet The polling set.
107 * @param pStdInBuf The standard input buffer.
108 * @param hStdInW The standard input pipe.
109 * @param pfClose Pointer to a flag whether the pipe needs to be closed afterwards.
110 */
111static int VBoxServiceControlExecProcWriteStdIn(RTPOLLSET hPollSet, PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf, RTPIPE hStdInW,
112 size_t *pcbWritten, bool *pfClose)
113{
114 AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
115 AssertPtrReturn(pfClose, VERR_INVALID_PARAMETER);
116
117 int rc = RTCritSectEnter(&pStdInBuf->CritSect);
118 if (RT_SUCCESS(rc))
119 {
120 Assert(pStdInBuf->cbSize >= pStdInBuf->cbOffset);
121 size_t cbToWrite = pStdInBuf->cbSize - pStdInBuf->cbOffset;
122 cbToWrite = RT_MIN(cbToWrite, _1M);
123 *pfClose = false;
124 if ( pStdInBuf->fEnabled
125 && cbToWrite)
126 {
127 rc = RTPipeWrite(hStdInW, &pStdInBuf->pbData[pStdInBuf->cbOffset], cbToWrite, pcbWritten);
128 if (RT_SUCCESS(rc))
129 {
130 pStdInBuf->fNeedNotification = true;
131 if (rc != VINF_TRY_AGAIN)
132 pStdInBuf->cbOffset += *pcbWritten;
133
134 /* Did somebody tell us that we should come to an end,
135 * e.g. no more data coming in? */
136 if (pStdInBuf->fPendingClose)
137 {
138 /* When we wrote out all data in the buffer we
139 * can finally shutdown. */
140 if (pStdInBuf->cbSize == pStdInBuf->cbOffset)
141 {
142 *pfClose = true;
143 }
144 else if (pStdInBuf->fNeedNotification)
145 {
146 /* Still data to push out - so we need another
147 * poll round! Write something into the notification pipe. */
148 size_t cbWrittenIgnore;
149 int rc2 = RTPipeWrite(pStdInBuf->hNotificationPipeW, "i", 1, &cbWrittenIgnore);
150
151 /* Disable notification until it is set again on successful write. */
152 pStdInBuf->fNeedNotification = !RT_SUCCESS(rc2);
153 }
154 }
155 }
156 else
157 {
158 *pcbWritten = 0;
159 pStdInBuf->fEnabled = pStdInBuf->fEnabled;
160 }
161#ifdef DEBUG
162 VBoxServiceVerbose(1, "ControlExec: Written StdIn: cbOffset=%u, pcbWritten=%u, rc=%Rrc, cbAlloc=%u, cbSize=%u\n",
163 pStdInBuf->cbOffset, *pcbWritten, rc,
164 pStdInBuf->cbAllocated, pStdInBuf->cbSize);
165#endif
166 }
167 else
168 {
169 *pcbWritten = 0;
170 pStdInBuf->fNeedNotification = pStdInBuf->fEnabled;
171 }
172
173 if ( !*pcbWritten
174 && pStdInBuf->fEnabled)
175 {
176 /*
177 * Nothing else left to write now? Remove the writable event from the poll set
178 * to not trigger too high CPU loads.
179 */
180 rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
181 AssertRC(rc);
182 }
183
184 int rc2 = RTCritSectLeave(&pStdInBuf->CritSect);
185 if (RT_SUCCESS(rc))
186 rc = rc2;
187 }
188 return rc;
189}
190
191
192/**
193 * Handle an event indicating we can write to the standard input pipe of the
194 * child process.
195 *
196 * @returns IPRT status code.
197 * @param hPollSet The polling set.
198 * @param fPollEvt The event mask returned by RTPollNoResume.
199 * @param phStdInW The standard input pipe.
200 * @param pStdInBuf The standard input buffer.
201 * @param pcbWritten Where to return the number of bytes written.
202 */
203static int VBoxServiceControlExecProcHandleStdInWritableEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
204 PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf, size_t *pcbWritten)
205{
206 AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
207 int rc;
208 if (!(fPollEvt & RTPOLL_EVT_ERROR))
209 {
210 bool fClose;
211 rc = VBoxServiceControlExecProcWriteStdIn(hPollSet,
212 pStdInBuf, *phStdInW,
213 pcbWritten, &fClose);
214 if (rc == VINF_TRY_AGAIN)
215 rc = VINF_SUCCESS;
216 if (RT_FAILURE(rc))
217 {
218 if ( rc == VERR_BAD_PIPE
219 || rc == VERR_BROKEN_PIPE)
220 {
221 rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
222 AssertRC(rc);
223 }
224 else
225 {
226 /** @todo Do we need to do something about this error condition? */
227 AssertRC(rc);
228 }
229 }
230 else if (fClose)
231 {
232 /* If the pipe needs to be closed, do so. */
233 rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
234 }
235 }
236 else
237 {
238 *pcbWritten = 0;
239 rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
240 }
241 return rc;
242}
243
244
245/**
246 * Handle a transport event or successful pfnPollIn() call.
247 *
248 * @returns IPRT status code from client send.
249 * @retval VINF_EOF indicates ABORT command.
250 *
251 * @param hPollSet The polling set.
252 * @param fPollEvt The event mask returned by RTPollNoResume.
253 * @param idPollHnd The handle ID.
254 * @param hStdInW The standard input pipe.
255 * @param pStdInBuf The standard input buffer.
256 */
257static int VBoxServiceControlExecProcHandleTransportEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, uint32_t idPollHnd,
258 PRTPIPE phStdInW, PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf)
259{
260 return 0; //RTPollSetAddPipe(hPollSet, *phStdInW, RTPOLL_EVT_WRITE, 4 /*TXSEXECHNDID_STDIN_WRITABLE*/);
261}
262
263
264/**
265 * Handle pending output data or error on standard out, standard error or the
266 * test pipe.
267 *
268 * @returns IPRT status code from client send.
269 * @param pThread The thread specific data.
270 * @param hPollSet The polling set.
271 * @param fPollEvt The event mask returned by RTPollNoResume.
272 * @param phPipeR The pipe handle.
273 * @param pu32Crc The current CRC-32 of the stream. (In/Out)
274 * @param uHandleId The handle ID.
275 *
276 * @todo Put the last 4 parameters into a struct!
277 */
278static int VBoxServiceControlExecProcHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phPipeR,
279 uint32_t uHandleId, PVBOXSERVICECTRLEXECPIPEBUF pStdOutBuf)
280{
281#ifdef DEBUG
282 VBoxServiceVerbose(4, "ControlExec: HandleOutputEvent: fPollEvt=%#x\n", fPollEvt);
283#endif
284
285 /*
286 * Try drain the pipe before acting on any errors.
287 */
288 int rc = VINF_SUCCESS;
289 size_t cbRead;
290 uint8_t abBuf[_64K];
291
292 int rc2 = RTPipeRead(*phPipeR, abBuf, sizeof(abBuf), &cbRead);
293 if (RT_SUCCESS(rc2) && cbRead)
294 {
295#if 0
296 /* Only used for "real-time" stdout/stderr data; gets sent immediately (later)! */
297 rc = VbglR3GuestCtrlExecSendOut(pThread->uClientID, pThread->uContextID,
298 pData->uPID, uHandleId, 0 /* u32Flags */,
299 abBuf, cbRead);
300 if (RT_FAILURE(rc))
301 {
302 VBoxServiceError("ControlExec: Error while sending real-time output data, rc=%Rrc, cbRead=%u, CID=%u, PID=%u\n",
303 rc, cbRead, pThread->uClientID, pData->uPID);
304 }
305 else
306 {
307#endif
308 uint32_t cbWritten;
309 rc = VBoxServiceControlExecPipeBufWrite(pStdOutBuf, abBuf,
310 cbRead, false /* Pending close */, &cbWritten);
311 if (RT_SUCCESS(rc))
312 {
313 Assert(cbRead == cbWritten);
314 /* Make sure we go another poll round in case there was too much data
315 for the buffer to hold. */
316 fPollEvt &= RTPOLL_EVT_ERROR;
317 }
318#if 0
319 }
320#endif
321 }
322 else if (RT_FAILURE(rc2))
323 {
324 fPollEvt |= RTPOLL_EVT_ERROR;
325 AssertMsg(rc2 == VERR_BROKEN_PIPE, ("%Rrc\n", rc));
326 }
327
328 /*
329 * If an error was signalled, close reading stdout/stderr pipe.
330 */
331 if (fPollEvt & RTPOLL_EVT_ERROR)
332 {
333 rc2 = RTPollSetRemove(hPollSet, uHandleId);
334 AssertRC(rc2);
335
336 rc2 = RTPipeClose(*phPipeR);
337 AssertRC(rc2);
338 *phPipeR = NIL_RTPIPE;
339 }
340 return rc;
341}
342
343
344int VBoxServiceControlExecProcHandleStdInputNotify(RTPOLLSET hPollSet,
345 PRTPIPE phNotificationPipeR, PRTPIPE phInputPipeW)
346{
347#ifdef DEBUG
348 VBoxServiceVerbose(4, "ControlExec: HandleStdInputNotify\n");
349#endif
350 /* Drain the notification pipe. */
351 uint8_t abBuf[8];
352 size_t cbIgnore;
353 int rc = RTPipeRead(*phNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
354 if (RT_SUCCESS(rc))
355 {
356 /*
357 * When the writable handle previously was removed from the poll set we need to add
358 * it here again so that writable events from the started procecss get handled correctly.
359 */
360 RTHANDLE hWritableIgnored;
361 rc = RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE, &hWritableIgnored);
362 if (rc == VERR_POLL_HANDLE_ID_NOT_FOUND)
363 rc = RTPollSetAddPipe(hPollSet, *phInputPipeW, RTPOLL_EVT_WRITE, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
364 }
365 return rc;
366}
367
368
369/**
370 * Execution loop which (usually) runs in a dedicated per-started-process thread and
371 * handles all pipe input/output and signalling stuff.
372 *
373 * @return IPRT status code.
374 * @param pThread The process' thread handle.
375 * @param hProcess The actual process handle.
376 * @param cMsTimeout Time limit (in ms) of the process' life time.
377 * @param hPollSet The poll set to use.
378 * @param hStdInW Handle to the process' stdin write end.
379 * @param hStdOutR Handle to the process' stdout read end.
380 * @param hStdErrR Handle to the process' stderr read end.
381 */
382static int VBoxServiceControlExecProcLoop(PVBOXSERVICECTRLTHREAD pThread,
383 RTPROCESS hProcess, RTMSINTERVAL cMsTimeout, RTPOLLSET hPollSet,
384 PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR)
385{
386 AssertPtrReturn(phStdInW, VERR_INVALID_PARAMETER);
387 AssertPtrReturn(phStdOutR, VERR_INVALID_PARAMETER);
388 AssertPtrReturn(phStdErrR, VERR_INVALID_PARAMETER);
389
390 int rc;
391 int rc2;
392 uint64_t const MsStart = RTTimeMilliTS();
393 RTPROCSTATUS ProcessStatus = { 254, RTPROCEXITREASON_ABEND };
394 bool fProcessAlive = true;
395 bool fProcessTimedOut = false;
396 uint64_t MsProcessKilled = UINT64_MAX;
397 RTMSINTERVAL const cMsPollBase = *phStdInW != NIL_RTPIPE
398 ? 100 /* Need to poll for input. */
399 : 1000; /* Need only poll for process exit and aborts. */
400 RTMSINTERVAL cMsPollCur = 0;
401
402 AssertPtr(pThread);
403 Assert(pThread->enmType == kVBoxServiceCtrlThreadDataExec);
404 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
405 AssertPtr(pData);
406
407 /* Assign PID to thread data. */
408 pData->uPID = hProcess;
409
410 /*
411 * Before entering the loop, tell the host that we've started the guest
412 * and that it's now OK to send input to the process.
413 */
414 VBoxServiceVerbose(3, "ControlExec: Process started: PID=%u, CID=%u, User=%s\n",
415 pData->uPID, pThread->uContextID, pData->pszUser);
416 rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
417 pData->uPID, PROC_STS_STARTED, 0 /* u32Flags */,
418 NULL /* pvData */, 0 /* cbData */);
419
420 /*
421 * Process input, output, the test pipe and client requests.
422 */
423 while ( RT_SUCCESS(rc)
424 && RT_UNLIKELY(!pThread->fShutdown))
425 {
426 /*
427 * Wait/Process all pending events.
428 */
429 uint32_t idPollHnd;
430 uint32_t fPollEvt;
431 rc2 = RTPollNoResume(hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
432 if (pThread->fShutdown)
433 continue;
434
435 cMsPollCur = 0; /* No rest until we've checked everything. */
436
437 if (RT_SUCCESS(rc2))
438 {
439 VBoxServiceVerbose(4, "ControlExec: RTPollNoResume idPollHnd=%u\n", idPollHnd);
440 switch (idPollHnd)
441 {
442 case VBOXSERVICECTRLPIPEID_STDIN_ERROR:
443 rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, &pData->stdIn);
444 break;
445
446 case VBOXSERVICECTRLPIPEID_STDIN_INPUT_NOTIFY:
447 rc = VBoxServiceControlExecProcHandleStdInputNotify(hPollSet,
448 &pData->stdIn.hNotificationPipeR, &pData->pipeStdInW);
449 AssertRC(rc);
450 /* Fall through. */
451 case VBOXSERVICECTRLPIPEID_STDIN_WRITABLE:
452 {
453 size_t cbWritten;
454 rc = VBoxServiceControlExecProcHandleStdInWritableEvent(hPollSet, fPollEvt, phStdInW,
455 &pData->stdIn, &cbWritten);
456 break;
457 }
458
459 case VBOXSERVICECTRLPIPEID_STDOUT:
460 rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt, phStdOutR,
461 VBOXSERVICECTRLPIPEID_STDOUT, &pData->stdOut);
462 break;
463
464 case VBOXSERVICECTRLPIPEID_STDERR:
465 rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt, phStdErrR,
466 VBOXSERVICECTRLPIPEID_STDERR, &pData->stdOut);
467 break;
468
469 default:
470 AssertMsgFailed(("idPollHnd=%u fPollEvt=%#x\n", idPollHnd, fPollEvt));
471 break;
472 }
473 if (RT_FAILURE(rc) || rc == VINF_EOF)
474 break; /* Abort command, or client dead or something. */
475 continue;
476 }
477
478 /*
479 * Check for process death.
480 */
481 if (fProcessAlive)
482 {
483 rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
484 if (RT_SUCCESS_NP(rc2))
485 {
486 fProcessAlive = false;
487 continue;
488 }
489 if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
490 continue;
491 if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
492 {
493 fProcessAlive = false;
494 ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
495 ProcessStatus.iStatus = 255;
496 AssertFailed();
497 }
498 else
499 AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
500 }
501
502 /*
503 * If the process has terminated, we're should head out.
504 */
505 if (!fProcessAlive)
506 break;
507
508 /*
509 * Check for timed out, killing the process.
510 */
511 uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
512 if (cMsTimeout != RT_INDEFINITE_WAIT)
513 {
514 uint64_t u64Now = RTTimeMilliTS();
515 uint64_t cMsElapsed = u64Now - MsStart;
516 if (cMsElapsed >= cMsTimeout)
517 {
518 VBoxServiceVerbose(3, "ControlExec: Process timed out (%ums elapsed > %ums timeout), killing ...", cMsElapsed, cMsTimeout);
519
520 fProcessTimedOut = true;
521 if ( MsProcessKilled == UINT64_MAX
522 || u64Now - MsProcessKilled > 1000)
523 {
524 if (u64Now - MsProcessKilled > 20*60*1000)
525 break; /* Give up after 20 mins. */
526 RTProcTerminate(hProcess);
527 MsProcessKilled = u64Now;
528 continue;
529 }
530 cMilliesLeft = 10000;
531 }
532 else
533 cMilliesLeft = cMsTimeout - (uint32_t)cMsElapsed;
534 }
535
536 /* Reset the polling interval since we've done all pending work. */
537 cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft;
538
539 /*
540 * Need to exit?
541 */
542 if (pThread->fShutdown)
543 break;
544 }
545
546 /*
547 * Try kill the process if it's still alive at this point.
548 */
549 if (fProcessAlive)
550 {
551 if (MsProcessKilled == UINT64_MAX)
552 {
553 VBoxServiceVerbose(3, "ControlExec: Process (PID=%u) is still alive and not killed yet\n",
554 pData->uPID);
555
556 MsProcessKilled = RTTimeMilliTS();
557 RTProcTerminate(hProcess);
558 RTThreadSleep(500);
559 }
560
561 for (size_t i = 0; i < 10; i++)
562 {
563 VBoxServiceVerbose(4, "ControlExec: Kill attempt %d/10: Waiting for process (PID=%u) exit ...\n",
564 i + 1, pData->uPID);
565 rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
566 if (RT_SUCCESS(rc2))
567 {
568 VBoxServiceVerbose(4, "ControlExec: Kill attempt %d/10: Process (PID=%u) exited\n",
569 i + 1, pData->uPID);
570 fProcessAlive = false;
571 break;
572 }
573 if (i >= 5)
574 {
575 VBoxServiceVerbose(4, "ControlExec: Kill attempt %d/10: Try to terminate (PID=%u) ...\n",
576 i + 1, pData->uPID);
577 RTProcTerminate(hProcess);
578 }
579 RTThreadSleep(i >= 5 ? 2000 : 500);
580 }
581
582 if (fProcessAlive)
583 VBoxServiceVerbose(3, "ControlExec: Process (PID=%u) could not be killed\n", pData->uPID);
584 }
585
586 /*
587 * If we don't have a client problem (RT_FAILURE(rc) we'll reply to the
588 * clients exec packet now.
589 */
590 if (RT_SUCCESS(rc))
591 {
592 VBoxServiceControlExecPipeBufSetStatus(&pData->stdIn, false /* Disabled */);
593 VBoxServiceControlExecPipeBufSetStatus(&pData->stdOut, false /* Disabled */);
594 VBoxServiceControlExecPipeBufSetStatus(&pData->stdErr, false /* Disabled */);
595
596 /* Since the process is not alive anymore, destroy its local
597 * stdin pipe buffer - it's not used anymore and can eat up quite
598 * a bit of memory. */
599 VBoxServiceControlExecPipeBufDestroy(&pData->stdIn);
600
601 uint32_t uStatus = PROC_STS_UNDEFINED;
602 uint32_t uFlags = 0;
603
604 if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX)
605 {
606 VBoxServiceVerbose(3, "ControlExec: Process timed out and got killed\n");
607 uStatus = PROC_STS_TOK;
608 }
609 else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX)
610 {
611 VBoxServiceVerbose(3, "ControlExec: Process timed out and did *not* get killed\n");
612 uStatus = PROC_STS_TOA;
613 }
614 else if (pThread->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
615 {
616 VBoxServiceVerbose(3, "ControlExec: Process got terminated because system/service is about to shutdown\n");
617 uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */
618 uFlags = pData->uFlags; /* Return handed-in execution flags back to the host. */
619 }
620 else if (fProcessAlive)
621 {
622 VBoxServiceError("ControlExec: Process is alive when it should not!\n");
623 }
624 else if (MsProcessKilled != UINT64_MAX)
625 {
626 VBoxServiceError("ControlExec: Process has been killed when it should not!\n");
627 }
628 else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
629 {
630 VBoxServiceVerbose(3, "ControlExec: Process ended with RTPROCEXITREASON_NORMAL\n");
631
632 uStatus = PROC_STS_TEN;
633 uFlags = ProcessStatus.iStatus;
634 }
635 else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
636 {
637 VBoxServiceVerbose(3, "ControlExec: Process ended with RTPROCEXITREASON_SIGNAL\n");
638
639 uStatus = PROC_STS_TES;
640 uFlags = ProcessStatus.iStatus;
641 }
642 else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
643 {
644 VBoxServiceVerbose(3, "ControlExec: Process ended with RTPROCEXITREASON_ABEND\n");
645
646 uStatus = PROC_STS_TEA;
647 uFlags = ProcessStatus.iStatus;
648 }
649 else
650 {
651 VBoxServiceError("ControlExec: Process has reached an undefined status!\n");
652 }
653
654 VBoxServiceVerbose(3, "ControlExec: Process ended: PID=%u, CID=%u, Status=%u, Flags=%u\n",
655 pData->uPID, pThread->uContextID, uStatus, uFlags);
656 rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
657 pData->uPID, uStatus, uFlags,
658 NULL /* pvData */, 0 /* cbData */);
659 VBoxServiceVerbose(3, "ControlExec: Process loop ended with rc=%Rrc\n", rc);
660 }
661 else
662 VBoxServiceError("ControlExec: Process loop failed with rc=%Rrc\n", rc);
663 return rc;
664}
665
666
667/**
668 * Sets up the redirection / pipe / nothing for one of the standard handles.
669 *
670 * @returns IPRT status code. No client replies made.
671 * @param fd Which standard handle it is (0 == stdin, 1 ==
672 * stdout, 2 == stderr).
673 * @param ph The generic handle that @a pph may be set
674 * pointing to. Always set.
675 * @param pph Pointer to the RTProcCreateExec argument.
676 * Always set.
677 * @param phPipe Where to return the end of the pipe that we
678 * should service. Always set.
679 */
680static int VBoxServiceControlExecSetupPipe(int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
681{
682 AssertPtrReturn(ph, VERR_INVALID_PARAMETER);
683 AssertPtrReturn(pph, VERR_INVALID_PARAMETER);
684 AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
685
686 ph->enmType = RTHANDLETYPE_PIPE;
687 ph->u.hPipe = NIL_RTPIPE;
688 *pph = NULL;
689 *phPipe = NIL_RTPIPE;
690
691 int rc;
692
693 /*
694 * Setup a pipe for forwarding to/from the client.
695 * The ph union struct will be filled with a pipe read/write handle
696 * to represent the "other" end to phPipe.
697 */
698 if (fd == 0) /* stdin? */
699 {
700 /* Connect a wrtie pipe specified by phPipe to stdin. */
701 rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
702 }
703 else /* stdout or stderr? */
704 {
705 /* Connect a read pipe specified by phPipe to stdout or stderr. */
706 rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
707 }
708 if (RT_FAILURE(rc))
709 return rc;
710 ph->enmType = RTHANDLETYPE_PIPE;
711 *pph = ph;
712
713 return rc;
714}
715
716
717/**
718 * Initializes a pipe buffer.
719 *
720 * @returns IPRT status code.
721 * @param pBuf The pipe buffer to initialize.
722 * @param fNeedNotificationPipe Whether the buffer needs a notification
723 * pipe or not.
724 */
725static int VBoxServiceControlExecPipeBufInit(PVBOXSERVICECTRLEXECPIPEBUF pBuf, bool fNeedNotificationPipe)
726{
727 AssertPtrReturn(pBuf, VERR_INVALID_PARAMETER);
728
729 /** @todo Add allocation size as function parameter! */
730 pBuf->pbData = (uint8_t *)RTMemAlloc(_64K); /* Start with a 64k buffer. */
731 AssertReturn(pBuf->pbData, VERR_NO_MEMORY);
732 pBuf->cbAllocated = _64K;
733 pBuf->cbSize = 0;
734 pBuf->cbOffset = 0;
735 pBuf->fEnabled = true;
736 pBuf->fPendingClose = false;
737 pBuf->fNeedNotification = fNeedNotificationPipe;
738 pBuf->hNotificationPipeW = NIL_RTPIPE;
739 pBuf->hNotificationPipeR = NIL_RTPIPE;
740 pBuf->hEventSem = NIL_RTSEMEVENT;
741
742 int rc = RTSemEventCreate(&pBuf->hEventSem);
743 if (RT_SUCCESS(rc))
744 {
745 rc = RTCritSectInit(&pBuf->CritSect);
746 if (RT_SUCCESS(rc) && fNeedNotificationPipe)
747 {
748 rc = RTPipeCreate(&pBuf->hNotificationPipeR, &pBuf->hNotificationPipeW, 0);
749 if (RT_FAILURE(rc))
750 RTCritSectDelete(&pBuf->CritSect);
751 }
752 }
753 return rc;
754}
755
756
757/**
758 * Reads out data from a specififed pipe buffer.
759 *
760 * @return IPRT status code.
761 * @param pBuf Pointer to pipe buffer to read the data from.
762 * @param pbBuffer Pointer to buffer to store the read out data.
763 * @param cbBuffer Size (in bytes) of the buffer where to store the data.
764 * @param pcbToRead Pointer to desired amount (in bytes) of data to read,
765 * will reflect the actual amount read on return.
766 */
767static int VBoxServiceControlExecPipeBufRead(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
768 uint8_t *pbBuffer, uint32_t cbBuffer, uint32_t *pcbToRead)
769{
770 AssertPtrReturn(pBuf, VERR_INVALID_PARAMETER);
771 AssertPtrReturn(pbBuffer, VERR_INVALID_PARAMETER);
772 AssertReturn(cbBuffer, VERR_INVALID_PARAMETER);
773 AssertPtrReturn(pcbToRead, VERR_INVALID_PARAMETER);
774
775 int rc = RTCritSectEnter(&pBuf->CritSect);
776 if (RT_SUCCESS(rc))
777 {
778 Assert(pBuf->cbSize >= pBuf->cbOffset);
779 if (*pcbToRead > pBuf->cbSize - pBuf->cbOffset)
780 *pcbToRead = pBuf->cbSize - pBuf->cbOffset;
781
782 if (*pcbToRead > cbBuffer)
783 *pcbToRead = cbBuffer;
784
785 if (*pcbToRead > 0)
786 {
787 memcpy(pbBuffer, pBuf->pbData + pBuf->cbOffset, *pcbToRead);
788 pBuf->cbOffset += *pcbToRead;
789
790 RTSemEventSignal(pBuf->hEventSem);
791 }
792 else
793 {
794 pbBuffer = NULL;
795 *pcbToRead = 0;
796 }
797 rc = RTCritSectLeave(&pBuf->CritSect);
798 }
799 return rc;
800}
801
802
803/**
804 * Writes data into a specififed pipe buffer.
805 *
806 * @return IPRT status code.
807 * @param pBuf Pointer to pipe buffer to write data into.
808 * @param pbData Pointer to byte data to write.
809 * @param cbData Data size (in bytes) to write.
810 * @param fPendingClose Needs the pipe (buffer) to be closed next time we have the chance to?
811 * @param pcbWritten Pointer to where the amount of written bytes get stored. Optional.
812 */
813static int VBoxServiceControlExecPipeBufWrite(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
814 uint8_t *pbData, uint32_t cbData, bool fPendingClose,
815 uint32_t *pcbWritten)
816{
817 AssertPtrReturn(pBuf, VERR_INVALID_PARAMETER);
818 AssertPtrReturn(pbData, VERR_INVALID_PARAMETER);
819
820 int rc;
821 if (pBuf->fEnabled)
822 {
823 rc = RTCritSectEnter(&pBuf->CritSect);
824 if (RT_SUCCESS(rc))
825 {
826 /* Rewind the buffer if it's empty. */
827 size_t cbInBuf = pBuf->cbSize - pBuf->cbOffset;
828 bool const fAddToSet = cbInBuf == 0;
829 if (fAddToSet)
830 pBuf->cbSize = pBuf->cbOffset = 0;
831
832 /* Try and see if we can simply append the data. */
833 if (cbData + pBuf->cbSize <= pBuf->cbAllocated)
834 {
835 memcpy(&pBuf->pbData[pBuf->cbSize], pbData, cbData);
836 pBuf->cbSize += cbData;
837 }
838 else
839 {
840 /* Move any buffered data to the front. */
841 cbInBuf = pBuf->cbSize - pBuf->cbOffset;
842 if (cbInBuf == 0)
843 pBuf->cbSize = pBuf->cbOffset = 0;
844 else if (pBuf->cbOffset) /* Do we have something to move? */
845 {
846 memmove(pBuf->pbData, &pBuf->pbData[pBuf->cbOffset], cbInBuf);
847 pBuf->cbSize = cbInBuf;
848 pBuf->cbOffset = 0;
849 }
850
851 /* Do we need to grow the buffer? */
852 if (cbData + pBuf->cbSize > pBuf->cbAllocated)
853 {
854 size_t cbAlloc = pBuf->cbSize + cbData;
855 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
856 void *pvNew = RTMemRealloc(pBuf->pbData, cbAlloc);
857 if (pvNew)
858 {
859 pBuf->pbData = (uint8_t *)pvNew;
860 pBuf->cbAllocated = cbAlloc;
861 }
862 else
863 rc = VERR_NO_MEMORY;
864 }
865
866 /* Finally, copy the data. */
867 if (RT_SUCCESS(rc))
868 {
869 if (cbData + pBuf->cbSize <= pBuf->cbAllocated)
870 {
871 memcpy(&pBuf->pbData[pBuf->cbSize], pbData, cbData);
872 pBuf->cbSize += cbData;
873 }
874 else
875 rc = VERR_BUFFER_OVERFLOW;
876 }
877 }
878
879 if (RT_SUCCESS(rc))
880 {
881 /*
882 * Was this the final read/write to do on this buffer? Then close it
883 * next time we have the chance to.
884 */
885 if (fPendingClose)
886 pBuf->fPendingClose = fPendingClose;
887
888 /*
889 * Wake up the thread servicing the process so it can feed it
890 * (if we have a notification helper pipe).
891 */
892 if (pBuf->fNeedNotification)
893 {
894 size_t cbWritten;
895 int rc2 = RTPipeWrite(pBuf->hNotificationPipeW, "i", 1, &cbWritten);
896
897 /* Disable notification until it is set again on successful write. */
898 pBuf->fNeedNotification = !RT_SUCCESS(rc2);
899 }
900
901 /* Report back written bytes (if wanted). */
902 if (pcbWritten)
903 *pcbWritten = cbData;
904
905 RTSemEventSignal(pBuf->hEventSem);
906 }
907 int rc2 = RTCritSectLeave(&pBuf->CritSect);
908 if (RT_SUCCESS(rc))
909 rc = rc2;
910 }
911 }
912 else
913 rc = VERR_BAD_PIPE;
914 return rc;
915}
916
917
918/**
919 * Returns whether a pipe buffer is active or not.
920 *
921 * @return bool True if pipe buffer is active, false if not.
922 * @param pBuf The pipe buffer.
923 */
924static bool VBoxServiceControlExecPipeBufIsEnabled(PVBOXSERVICECTRLEXECPIPEBUF pBuf)
925{
926 AssertPtrReturn(pBuf, VERR_INVALID_PARAMETER);
927
928 bool fEnabled = false;
929 if (RT_SUCCESS(RTCritSectEnter(&pBuf->CritSect)))
930 {
931 fEnabled = pBuf->fEnabled;
932 RTCritSectLeave(&pBuf->CritSect);
933 }
934 return fEnabled;
935}
936
937
938/**
939 * Sets the current status (enabled/disabled) of a pipe buffer.
940 *
941 * @return IPRT status code.
942 * @param pBuf The pipe buffer.
943 * @param fEnabled Pipe buffer status to set.
944 */
945static int VBoxServiceControlExecPipeBufSetStatus(PVBOXSERVICECTRLEXECPIPEBUF pBuf, bool fEnabled)
946{
947 AssertPtrReturn(pBuf, VERR_INVALID_PARAMETER);
948
949 int rc = RTCritSectEnter(&pBuf->CritSect);
950 if (RT_SUCCESS(rc))
951 {
952 pBuf->fEnabled = fEnabled;
953 /* Let waiter know that something has changed ... */
954 if (pBuf->hEventSem)
955 RTSemEventSignal(pBuf->hEventSem);
956 rc = RTCritSectLeave(&pBuf->CritSect);
957 }
958 return rc;
959}
960
961
962/**
963 * Deletes a pipe buffer.
964 * Note: Not thread safe -- only call this when nobody is relying on the
965 * data anymore!
966 *
967 * @param pBuf The pipe buffer.
968 */
969static void VBoxServiceControlExecPipeBufDestroy(PVBOXSERVICECTRLEXECPIPEBUF pBuf)
970{
971 AssertPtr(pBuf);
972 if (pBuf->pbData)
973 {
974 RTMemFree(pBuf->pbData);
975 pBuf->pbData = NULL;
976 pBuf->cbAllocated = 0;
977 pBuf->cbSize = 0;
978 pBuf->cbOffset = 0;
979 }
980
981 RTPipeClose(pBuf->hNotificationPipeR);
982 pBuf->hNotificationPipeR = NIL_RTPIPE;
983 RTPipeClose(pBuf->hNotificationPipeW);
984 pBuf->hNotificationPipeW = NIL_RTPIPE;
985
986 RTSemEventDestroy(pBuf->hEventSem);
987 RTCritSectDelete(&pBuf->CritSect);
988}
989
990
991/**
992 * Allocates and gives back a thread data struct which then can be used by the worker thread.
993 * Needs to be freed with VBoxServiceControlExecDestroyThreadData().
994 *
995 * @return IPRT status code.
996 * @param pThread The thread's handle to allocate the data for.
997 * @param u32ContextID The context ID bound to this request / command.
998 * @param pszCmd Full qualified path of process to start (without arguments).
999 * @param uFlags Process execution flags.
1000 * @param pszArgs String of arguments to pass to the process to start.
1001 * @param uNumArgs Number of arguments specified in pszArgs.
1002 * @param pszEnv String of environment variables ("FOO=BAR") to pass to the process
1003 * to start.
1004 * @param cbEnv Size (in bytes) of environment variables.
1005 * @param uNumEnvVars Number of environment variables specified in pszEnv.
1006 * @param pszUser User name (account) to start the process under.
1007 * @param pszPassword Password of specified user name (account).
1008 * @param uTimeLimitMS Time limit (in ms) of the process' life time.
1009 */
1010static int VBoxServiceControlExecAllocateThreadData(PVBOXSERVICECTRLTHREAD pThread,
1011 uint32_t u32ContextID,
1012 const char *pszCmd, uint32_t uFlags,
1013 const char *pszArgs, uint32_t uNumArgs,
1014 const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
1015 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS)
1016{
1017 AssertPtr(pThread);
1018
1019 /* General stuff. */
1020 pThread->Node.pPrev = NULL;
1021 pThread->Node.pNext = NULL;
1022
1023 pThread->fShutdown = false;
1024 pThread->fStarted = false;
1025 pThread->fStopped = false;
1026
1027 pThread->uContextID = u32ContextID;
1028 /* ClientID will be assigned when thread is started! */
1029
1030 /* Specific stuff. */
1031 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREADDATAEXEC));
1032 if (pData == NULL)
1033 return VERR_NO_MEMORY;
1034
1035 pData->uPID = 0; /* Don't have a PID yet. */
1036 pData->pszCmd = RTStrDup(pszCmd);
1037 pData->uFlags = uFlags;
1038 pData->uNumEnvVars = 0;
1039 pData->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
1040
1041 /* Prepare argument list. */
1042 int rc = RTGetOptArgvFromString(&pData->papszArgs, (int*)&pData->uNumArgs,
1043 (uNumArgs > 0) ? pszArgs : "", NULL);
1044 /* Did we get the same result? */
1045 Assert(uNumArgs == pData->uNumArgs);
1046
1047 if (RT_SUCCESS(rc))
1048 {
1049 /* Prepare environment list. */
1050 if (uNumEnvVars)
1051 {
1052 pData->papszEnv = (char **)RTMemAlloc(uNumEnvVars * sizeof(char*));
1053 AssertPtr(pData->papszEnv);
1054 pData->uNumEnvVars = uNumEnvVars;
1055
1056 const char *pszCur = pszEnv;
1057 uint32_t i = 0;
1058 uint32_t cbLen = 0;
1059 while (cbLen < cbEnv)
1060 {
1061 /* sanity check */
1062 if (i >= uNumEnvVars)
1063 {
1064 rc = VERR_INVALID_PARAMETER;
1065 break;
1066 }
1067 int cbStr = RTStrAPrintf(&pData->papszEnv[i++], "%s", pszCur);
1068 if (cbStr < 0)
1069 {
1070 rc = VERR_NO_STR_MEMORY;
1071 break;
1072 }
1073 pszCur += cbStr + 1; /* Skip terminating '\0' */
1074 cbLen += cbStr + 1; /* Skip terminating '\0' */
1075 }
1076 }
1077
1078 pData->pszUser = RTStrDup(pszUser);
1079 pData->pszPassword = RTStrDup(pszPassword);
1080 pData->uTimeLimitMS = uTimeLimitMS;
1081
1082 /* Adjust time limit value. */
1083 pData->uTimeLimitMS = ( uTimeLimitMS == UINT32_MAX
1084 || uTimeLimitMS == 0)
1085 ? RT_INDEFINITE_WAIT : uTimeLimitMS;
1086
1087 /* Init buffers. */
1088 rc = VBoxServiceControlExecPipeBufInit(&pData->stdOut, false /*fNeedNotificationPipe*/);
1089 if (RT_SUCCESS(rc))
1090 {
1091 rc = VBoxServiceControlExecPipeBufInit(&pData->stdErr, false /*fNeedNotificationPipe*/);
1092 if (RT_SUCCESS(rc))
1093 rc = VBoxServiceControlExecPipeBufInit(&pData->stdIn, true /*fNeedNotificationPipe*/);
1094 }
1095 }
1096
1097 if (RT_FAILURE(rc))
1098 {
1099 VBoxServiceControlExecDestroyThreadData(pData);
1100 }
1101 else
1102 {
1103 pThread->enmType = kVBoxServiceCtrlThreadDataExec;
1104 pThread->pvData = pData;
1105 }
1106 return rc;
1107}
1108
1109
1110/**
1111 * Frees an allocated thread data structure along with all its allocated parameters.
1112 *
1113 * @param pData Pointer to thread data to free.
1114 */
1115void VBoxServiceControlExecDestroyThreadData(PVBOXSERVICECTRLTHREADDATAEXEC pData)
1116{
1117 if (pData)
1118 {
1119 RTStrFree(pData->pszCmd);
1120 if (pData->uNumEnvVars)
1121 {
1122 for (uint32_t i = 0; i < pData->uNumEnvVars; i++)
1123 RTStrFree(pData->papszEnv[i]);
1124 RTMemFree(pData->papszEnv);
1125 }
1126 RTGetOptArgvFree(pData->papszArgs);
1127 RTStrFree(pData->pszUser);
1128 RTStrFree(pData->pszPassword);
1129
1130 VBoxServiceControlExecPipeBufDestroy(&pData->stdOut);
1131 VBoxServiceControlExecPipeBufDestroy(&pData->stdErr);
1132 VBoxServiceControlExecPipeBufDestroy(&pData->stdIn);
1133
1134 RTMemFree(pData);
1135 pData = NULL;
1136 }
1137}
1138
1139
1140/**
1141 * Expands a file name / path to its real content. This only works on Windows
1142 * for now (e.g. translating "%TEMP%\foo.exe" to "C:\Windows\Temp" when starting
1143 * with system / administrative rights).
1144 *
1145 * @return IPRT status code.
1146 * @param pszPath Path to resolve.
1147 * @param pszExpanded Pointer to string to store the resolved path in.
1148 * @param cbExpanded Size (in bytes) of string to store the resolved path.
1149 */
1150static int VBoxServiceControlExecMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded)
1151{
1152 int rc = VINF_SUCCESS;
1153#ifdef RT_OS_WINDOWS
1154 if (!ExpandEnvironmentStrings(pszPath, pszExpanded, cbExpanded))
1155 rc = RTErrConvertFromWin32(GetLastError());
1156#else
1157 /* No expansion for non-Windows yet. */
1158 rc = RTStrCopy(pszExpanded, cbExpanded, pszPath);
1159#endif
1160#ifdef DEBUG
1161 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecMakeFullPath: %s -> %s\n",
1162 pszPath, pszExpanded);
1163#endif
1164 return rc;
1165}
1166
1167
1168/**
1169 * Resolves the full path of a specified executable name. This function also
1170 * resolves internal VBoxService tools to its appropriate executable path + name.
1171 *
1172 * @return IPRT status code.
1173 * @param pszFileName File name to resovle.
1174 * @param pszResolved Pointer to a string where the resolved file name will be stored.
1175 * @param cbResolved Size (in bytes) of resolved file name string.
1176 */
1177static int VBoxServiceControlExecResolveExecutable(const char *pszFileName, char *pszResolved, size_t cbResolved)
1178{
1179 int rc = VINF_SUCCESS;
1180
1181 /* Search the path of our executable. */
1182 char szVBoxService[RTPATH_MAX];
1183 if (RTProcGetExecutablePath(szVBoxService, sizeof(szVBoxService)))
1184 {
1185 char *pszExecResolved = NULL;
1186 if ( (g_pszProgName && RTStrICmp(pszFileName, g_pszProgName) == 0)
1187 || !RTStrICmp(pszFileName, VBOXSERVICE_NAME))
1188 {
1189 /* We just want to execute VBoxService (no toolbox). */
1190 pszExecResolved = RTStrDup(szVBoxService);
1191 }
1192#ifdef VBOXSERVICE_TOOLBOX
1193 else if (RTStrStr(pszFileName, "vbox_") == pszFileName)
1194 {
1195 /* We want to use the internal toolbox (all internal
1196 * tools are starting with "vbox_" (e.g. "vbox_cat"). */
1197 pszExecResolved = RTStrDup(szVBoxService);
1198 }
1199#endif
1200 else /* Nothing to resolve, copy original. */
1201 pszExecResolved = RTStrDup(pszFileName);
1202 AssertPtr(pszExecResolved);
1203
1204 rc = VBoxServiceControlExecMakeFullPath(pszExecResolved, pszResolved, cbResolved);
1205#ifdef DEBUG
1206 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecResolveExecutable: %s -> %s\n",
1207 pszFileName, pszResolved);
1208#endif
1209 RTStrFree(pszExecResolved);
1210 }
1211 return rc;
1212}
1213
1214
1215#ifdef VBOXSERVICE_TOOLBOX
1216/**
1217 * Constructs the argv command line of a VBoxService program
1218 * by first appending the full path of VBoxService along with the given
1219 * tool name (e.g. "vbox_cat") + the tool's actual command line parameters.
1220 *
1221 * @return IPRT status code.
1222 * @param pszFileName File name (full path) of this process.
1223 * @param papszArgs Original argv command line from the host.
1224 * @param ppapszArgv Pointer to a pointer with the new argv command line.
1225 * Needs to be freed with RTGetOptArgvFree.
1226 */
1227static int VBoxServiceControlExecPrepareArgv(const char *pszFileName,
1228 const char * const *papszArgs, char ***ppapszArgv)
1229{
1230 AssertPtrReturn(pszFileName, VERR_INVALID_PARAMETER);
1231 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
1232 AssertPtrReturn(ppapszArgv, VERR_INVALID_PARAMETER);
1233
1234 char *pszArgs;
1235 int rc = RTGetOptArgvToString(&pszArgs, papszArgs,
1236 RTGETOPTARGV_CNV_QUOTE_MS_CRT); /* RTGETOPTARGV_CNV_QUOTE_BOURNE_SH */
1237 if ( RT_SUCCESS(rc)
1238 && pszArgs)
1239 {
1240 /*
1241 * Construct the new command line by appending the actual
1242 * tool name to new process' command line.
1243 */
1244 char szArgsExp[RTPATH_MAX];
1245 rc = VBoxServiceControlExecMakeFullPath(pszArgs, szArgsExp, sizeof(szArgsExp));
1246 if (RT_SUCCESS(rc))
1247 {
1248 char *pszNewArgs;
1249 if (RTStrAPrintf(&pszNewArgs, "%s %s", pszFileName, szArgsExp))
1250 {
1251#ifdef DEBUG
1252 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecPrepareArgv: %s\n",
1253 pszNewArgs);
1254#endif
1255 int iNumArgsIgnored;
1256 rc = RTGetOptArgvFromString(ppapszArgv, &iNumArgsIgnored,
1257 pszNewArgs, NULL /* Use standard separators. */);
1258 RTStrFree(pszNewArgs);
1259 }
1260 }
1261 RTStrFree(pszArgs);
1262 }
1263 else /* No arguments given, just use the resolved file name as argv[0]. */
1264 {
1265 int iNumArgsIgnored;
1266 rc = RTGetOptArgvFromString(ppapszArgv, &iNumArgsIgnored,
1267 pszFileName, NULL /* Use standard separators. */);
1268 }
1269 return rc;
1270}
1271#endif
1272
1273
1274/**
1275 * Helper function to create/start a process on the guest.
1276 *
1277 * @return IPRT status code.
1278 * @param pszExec Full qualified path of process to start (without arguments).
1279 * @param papszArgs Pointer to array of command line arguments.
1280 * @param hEnv Handle to environment block to use.
1281 * @param fFlags Process execution flags.
1282 * @param phStdIn Handle for the process' stdin pipe.
1283 * @param phStdOut Handle for the process' stdout pipe.
1284 * @param phStdErr Handle for the process' stderr pipe.
1285 * @param pszAsUser User name (account) to start the process under.
1286 * @param pszPassword Password of the specified user.
1287 * @param phProcess Pointer which will receive the process handle after
1288 * successful process start.
1289 */
1290static int VBoxServiceControlExecCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
1291 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
1292 const char *pszPassword, PRTPROCESS phProcess)
1293{
1294 AssertPtrReturn(pszExec, VERR_INVALID_PARAMETER);
1295 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
1296 AssertPtrReturn(phProcess, VERR_INVALID_PARAMETER);
1297
1298 int rc = VINF_SUCCESS;
1299 char szExecExp[RTPATH_MAX];
1300#ifdef RT_OS_WINDOWS
1301 /*
1302 * If sysprep should be executed do this in the context of VBoxService, which
1303 * (usually, if started by SCM) has administrator rights. Because of that a UI
1304 * won't be shown (doesn't have a desktop).
1305 */
1306 if (RTStrICmp(pszExec, "sysprep") == 0)
1307 {
1308 /* Use a predefined sysprep path as default. */
1309 char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe";
1310
1311 /*
1312 * On Windows Vista (and up) sysprep is located in "system32\\sysprep\\sysprep.exe",
1313 * so detect the OS and use a different path.
1314 */
1315 OSVERSIONINFOEX OSInfoEx;
1316 RT_ZERO(OSInfoEx);
1317 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
1318 if ( GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
1319 && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
1320 && OSInfoEx.dwMajorVersion >= 6 /* Vista or later */)
1321 {
1322 rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL);
1323 if (RT_SUCCESS(rc))
1324 rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\sysprep\\sysprep.exe");
1325 }
1326
1327 if (RT_SUCCESS(rc))
1328 {
1329 char **papszArgsExp;
1330 rc = VBoxServiceControlExecPrepareArgv(szSysprepCmd, papszArgs, &papszArgsExp);
1331 if (RT_SUCCESS(rc))
1332 {
1333 rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */,
1334 phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */,
1335 NULL /* pszPassword */, phProcess);
1336 }
1337 RTGetOptArgvFree(papszArgsExp);
1338 }
1339 return rc;
1340 }
1341#endif /* RT_OS_WINDOWS */
1342
1343 /*
1344 * Do the environment variables expansion on executable and arguments.
1345 */
1346 rc = VBoxServiceControlExecResolveExecutable(pszExec, szExecExp, sizeof(szExecExp));
1347 if (RT_SUCCESS(rc))
1348 {
1349 char **papszArgsExp;
1350 rc = VBoxServiceControlExecPrepareArgv(szExecExp, papszArgs, &papszArgsExp);
1351 if (RT_SUCCESS(rc))
1352 {
1353 uint32_t uProcFlags = 0;
1354 if (fFlags)
1355 {
1356 /* Process Main flag "ExecuteProcessFlag_Hidden". */
1357 if (fFlags & RT_BIT(2))
1358 uProcFlags = RTPROC_FLAGS_HIDDEN;
1359 }
1360
1361 /* If no user name specified run with current credentials (e.g.
1362 * full service/system rights). This is prohibited via official Main API!
1363 *
1364 * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication
1365 * code (at least on Windows) for running processes as different users
1366 * started from our system service. */
1367 if (strlen(pszAsUser))
1368 uProcFlags |= RTPROC_FLAGS_SERVICE;
1369
1370 /* Do normal execution. */
1371 rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags,
1372 phStdIn, phStdOut, phStdErr,
1373 strlen(pszAsUser) ? pszAsUser : NULL,
1374 strlen(pszPassword) ? pszPassword : NULL,
1375 phProcess);
1376 RTGetOptArgvFree(papszArgsExp);
1377 }
1378 }
1379 return rc;
1380}
1381
1382/**
1383 * The actual worker routine (lopp) for a started guest process.
1384 *
1385 * @return IPRT status code.
1386 * @param PVBOXSERVICECTRLTHREAD Thread data associated with a started process.
1387 */
1388static DECLCALLBACK(int) VBoxServiceControlExecProcessWorker(PVBOXSERVICECTRLTHREAD pThread)
1389{
1390 AssertPtr(pThread);
1391 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
1392 AssertPtr(pData);
1393
1394 VBoxServiceVerbose(3, "ControlExec: Thread of process \"%s\" started\n", pData->pszCmd);
1395
1396 int rc = VbglR3GuestCtrlConnect(&pThread->uClientID);
1397 if (RT_FAILURE(rc))
1398 {
1399 VBoxServiceError("ControlExec: Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc);
1400 RTThreadUserSignal(RTThreadSelf());
1401 return rc;
1402 }
1403
1404 bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
1405
1406 /*
1407 * Create the environment.
1408 */
1409 RTENV hEnv;
1410 rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
1411 if (RT_SUCCESS(rc))
1412 {
1413 size_t i;
1414 for (i = 0; i < pData->uNumEnvVars && pData->papszEnv; i++)
1415 {
1416 rc = RTEnvPutEx(hEnv, pData->papszEnv[i]);
1417 if (RT_FAILURE(rc))
1418 break;
1419 }
1420 if (RT_SUCCESS(rc))
1421 {
1422 /*
1423 * Setup the redirection of the standard stuff.
1424 */
1425 /** @todo consider supporting: gcc stuff.c >file 2>&1. */
1426 RTHANDLE hStdIn;
1427 PRTHANDLE phStdIn;
1428 rc = VBoxServiceControlExecSetupPipe(0 /*STDIN_FILENO*/, &hStdIn, &phStdIn, &pData->pipeStdInW);
1429 if (RT_SUCCESS(rc))
1430 {
1431 RTHANDLE hStdOut;
1432 PRTHANDLE phStdOut;
1433 RTPIPE hStdOutR;
1434 rc = VBoxServiceControlExecSetupPipe(1 /*STDOUT_FILENO*/, &hStdOut, &phStdOut, &hStdOutR);
1435 if (RT_SUCCESS(rc))
1436 {
1437 RTHANDLE hStdErr;
1438 PRTHANDLE phStdErr;
1439 RTPIPE hStdErrR;
1440 rc = VBoxServiceControlExecSetupPipe(2 /*STDERR_FILENO*/, &hStdErr, &phStdErr, &hStdErrR);
1441 if (RT_SUCCESS(rc))
1442 {
1443 /*
1444 * Create a poll set for the pipes and let the
1445 * transport layer add stuff to it as well.
1446 */
1447 RTPOLLSET hPollSet;
1448 rc = RTPollSetCreate(&hPollSet);
1449 if (RT_SUCCESS(rc))
1450 {
1451 rc = RTPollSetAddPipe(hPollSet, pData->pipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN_ERROR);
1452 if (RT_SUCCESS(rc))
1453 rc = RTPollSetAddPipe(hPollSet, hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDOUT);
1454 if (RT_SUCCESS(rc))
1455 rc = RTPollSetAddPipe(hPollSet, hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDERR);
1456 if (RT_SUCCESS(rc))
1457 rc = RTPollSetAddPipe(hPollSet, pData->pipeStdInW, RTPOLL_EVT_WRITE, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
1458 if (RT_SUCCESS(rc))
1459 rc = RTPollSetAddPipe(hPollSet, pData->stdIn.hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_STDIN_INPUT_NOTIFY);
1460 if (RT_SUCCESS(rc))
1461 {
1462 RTPROCESS hProcess;
1463 rc = VBoxServiceControlExecCreateProcess(pData->pszCmd, pData->papszArgs, hEnv, pData->uFlags,
1464 phStdIn, phStdOut, phStdErr,
1465 pData->pszUser, pData->pszPassword,
1466 &hProcess);
1467
1468 /*
1469 * Tell the control thread that it can continue
1470 * spawning services. This needs to be done after the new
1471 * process has been started because otherwise signal handling
1472 * on (Open) Solaris does not work correctly (see #5068).
1473 */
1474 int rc2 = RTThreadUserSignal(RTThreadSelf());
1475 if (RT_FAILURE(rc2))
1476 rc = rc2;
1477 fSignalled = true;
1478
1479 if (RT_SUCCESS(rc))
1480 {
1481 /*
1482 * Close the child ends of any pipes and redirected files.
1483 */
1484 rc2 = RTHandleClose(phStdIn); AssertRC(rc2);
1485 phStdIn = NULL;
1486 rc2 = RTHandleClose(phStdOut); AssertRC(rc2);
1487 phStdOut = NULL;
1488 rc2 = RTHandleClose(phStdErr); AssertRC(rc2);
1489 phStdErr = NULL;
1490
1491 /* Enter the process loop. */
1492 rc = VBoxServiceControlExecProcLoop(pThread,
1493 hProcess, pData->uTimeLimitMS, hPollSet,
1494 &pData->pipeStdInW, &hStdOutR, &hStdErrR);
1495
1496 /*
1497 * The handles that are no longer in the set have
1498 * been closed by the above call in order to prevent
1499 * the guest from getting stuck accessing them.
1500 * So, NIL the handles to avoid closing them again.
1501 */
1502 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 0 /* stdin */, NULL)))
1503 pData->pipeStdInW = NIL_RTPIPE;
1504 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 1 /* stdout */, NULL)))
1505 hStdOutR = NIL_RTPIPE;
1506 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 2 /* stderr */, NULL)))
1507 hStdErrR = NIL_RTPIPE;
1508 }
1509 else /* Something went wrong; report error! */
1510 {
1511 VBoxServiceError("ControlExec: Could not start process '%s' (CID: %u)! Error: %Rrc\n",
1512 pData->pszCmd, pThread->uContextID, rc);
1513
1514 rc2 = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, pData->uPID,
1515 PROC_STS_ERROR, rc,
1516 NULL /* pvData */, 0 /* cbData */);
1517 if (RT_FAILURE(rc2))
1518 VBoxServiceError("ControlExec: Could not report process start error! Error: %Rrc (process error %Rrc)\n",
1519 rc2, rc);
1520 }
1521 }
1522 RTPollSetDestroy(hPollSet);
1523 }
1524 RTPipeClose(hStdErrR);
1525 RTHandleClose(phStdErr);
1526 }
1527 RTPipeClose(hStdOutR);
1528 RTHandleClose(phStdOut);
1529 }
1530 RTPipeClose(pData->pipeStdInW);
1531 RTHandleClose(phStdIn);
1532 }
1533 }
1534 RTEnvDestroy(hEnv);
1535 }
1536
1537 VbglR3GuestCtrlDisconnect(pThread->uClientID);
1538 VBoxServiceVerbose(3, "ControlExec: Thread of process \"%s\" (PID: %u) ended with rc=%Rrc\n",
1539 pData->pszCmd, pData->uPID, rc);
1540
1541 /*
1542 * If something went wrong signal the user event so that others don't wait
1543 * forever on this thread.
1544 */
1545 if (RT_FAILURE(rc) && !fSignalled)
1546 RTThreadUserSignal(RTThreadSelf());
1547 return rc;
1548}
1549
1550
1551/**
1552 * Finds a (formerly) started process given by its PID.
1553 *
1554 * @return PVBOXSERVICECTRLTHREAD Process structure if found, otherwise NULL.
1555 * @param uPID PID to search for.
1556 */
1557static PVBOXSERVICECTRLTHREAD VBoxServiceControlExecFindProcess(uint32_t uPID)
1558{
1559 PVBOXSERVICECTRLTHREAD pNode;
1560 bool fFound = false;
1561 RTListForEach(&g_GuestControlExecThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
1562 {
1563 if ( pNode->fStarted
1564 && pNode->enmType == kVBoxServiceCtrlThreadDataExec)
1565 {
1566 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
1567 if (pData && pData->uPID == uPID)
1568 {
1569 return pNode;
1570 }
1571 }
1572 }
1573 return NULL;
1574}
1575
1576
1577/**
1578 * Thread main routine for a started process.
1579 *
1580 * @return IPRT status code.
1581 * @param RTTHREAD Pointer to the thread's data.
1582 * @param void* User-supplied argument pointer.
1583 *
1584 */
1585static DECLCALLBACK(int) VBoxServiceControlExecThread(RTTHREAD ThreadSelf, void *pvUser)
1586{
1587 PVBOXSERVICECTRLTHREAD pThread = (VBOXSERVICECTRLTHREAD*)pvUser;
1588 AssertPtr(pThread);
1589 return VBoxServiceControlExecProcessWorker(pThread);
1590}
1591
1592
1593/**
1594 * Executes (starts) a process on the guest. This causes a new thread to be created
1595 * so that this function will not block the overall program execution.
1596 *
1597 * @return IPRT status code.
1598 * @param uContextID Context ID to associate the process to start with.
1599 * @param pszCmd Full qualified path of process to start (without arguments).
1600 * @param uFlags Process execution flags.
1601 * @param pszArgs String of arguments to pass to the process to start.
1602 * @param uNumArgs Number of arguments specified in pszArgs.
1603 * @param pszEnv String of environment variables ("FOO=BAR") to pass to the process
1604 * to start.
1605 * @param cbEnv Size (in bytes) of environment variables.
1606 * @param uNumEnvVars Number of environment variables specified in pszEnv.
1607 * @param pszUser User name (account) to start the process under.
1608 * @param pszPassword Password of specified user name (account).
1609 * @param uTimeLimitMS Time limit (in ms) of the process' life time.
1610 */
1611int VBoxServiceControlExecProcess(uint32_t uContextID, const char *pszCmd, uint32_t uFlags,
1612 const char *pszArgs, uint32_t uNumArgs,
1613 const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
1614 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS)
1615{
1616 PVBOXSERVICECTRLTHREAD pThread = (PVBOXSERVICECTRLTHREAD)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREAD));
1617
1618 int rc;
1619 if (pThread)
1620 {
1621 rc = VBoxServiceControlExecAllocateThreadData(pThread,
1622 uContextID,
1623 pszCmd, uFlags,
1624 pszArgs, uNumArgs,
1625 pszEnv, cbEnv, uNumEnvVars,
1626 pszUser, pszPassword,
1627 uTimeLimitMS);
1628 if (RT_SUCCESS(rc))
1629 {
1630 rc = RTThreadCreate(&pThread->Thread, VBoxServiceControlExecThread,
1631 (void *)(PVBOXSERVICECTRLTHREAD*)pThread, 0,
1632 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "Exec");
1633 if (RT_FAILURE(rc))
1634 {
1635 VBoxServiceError("ControlExec: RTThreadCreate failed, rc=%Rrc\n, pThread=%p\n",
1636 rc, pThread);
1637 }
1638 else
1639 {
1640 VBoxServiceVerbose(4, "ControlExec: Waiting for thread to initialize ...\n");
1641
1642 /* Wait for the thread to initialize. */
1643 RTThreadUserWait(pThread->Thread, 60 * 1000);
1644 if (pThread->fShutdown)
1645 {
1646 VBoxServiceError("ControlExec: Thread for process \"%s\" failed to start!\n", pszCmd);
1647 rc = VERR_GENERAL_FAILURE;
1648 }
1649 else
1650 {
1651 pThread->fStarted = true;
1652 /*rc =*/ RTListAppend(&g_GuestControlExecThreads, &pThread->Node);
1653 }
1654 }
1655
1656 if (RT_FAILURE(rc))
1657 VBoxServiceControlExecDestroyThreadData((PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData);
1658 }
1659 if (RT_FAILURE(rc))
1660 RTMemFree(pThread);
1661 }
1662 else
1663 rc = VERR_NO_MEMORY;
1664 return rc;
1665}
1666
1667
1668/**
1669 * Handles starting processes on the guest.
1670 *
1671 * @returns IPRT status code.
1672 * @param u32ClientId The HGCM client session ID.
1673 * @param uNumParms The number of parameters the host is offering.
1674 */
1675int VBoxServiceControlExecHandleCmdStartProcess(uint32_t u32ClientId, uint32_t uNumParms)
1676{
1677 uint32_t uContextID;
1678 char szCmd[_1K];
1679 uint32_t uFlags;
1680 char szArgs[_1K];
1681 uint32_t uNumArgs;
1682 char szEnv[_64K];
1683 uint32_t cbEnv = sizeof(szEnv);
1684 uint32_t uNumEnvVars;
1685 char szUser[128];
1686 char szPassword[128];
1687 uint32_t uTimeLimitMS;
1688
1689#if 0 /* for valgrind */
1690 RT_ZERO(szCmd);
1691 RT_ZERO(szArgs);
1692 RT_ZERO(szEnv);
1693 RT_ZERO(szUser);
1694 RT_ZERO(szPassword);
1695#endif
1696
1697 if (uNumParms != 11)
1698 return VERR_INVALID_PARAMETER;
1699
1700 int rc = VbglR3GuestCtrlExecGetHostCmd(u32ClientId,
1701 uNumParms,
1702 &uContextID,
1703 /* Command */
1704 szCmd, sizeof(szCmd),
1705 /* Flags */
1706 &uFlags,
1707 /* Arguments */
1708 szArgs, sizeof(szArgs), &uNumArgs,
1709 /* Environment */
1710 szEnv, &cbEnv, &uNumEnvVars,
1711 /* Credentials */
1712 szUser, sizeof(szUser),
1713 szPassword, sizeof(szPassword),
1714 /* Timelimit */
1715 &uTimeLimitMS);
1716#ifdef DEBUG
1717 VBoxServiceVerbose(3, "ControlExec: Start process szCmd=%s, uFlags=%u, szArgs=%s, szEnv=%s, szUser=%s, szPW=%s, uTimeout=%u\n",
1718 szCmd, uFlags, uNumArgs ? szArgs : "<None>", uNumEnvVars ? szEnv : "<None>", szUser, szPassword, uTimeLimitMS);
1719#endif
1720 if (RT_SUCCESS(rc))
1721 {
1722 rc = VBoxServiceControlExecProcess(uContextID, szCmd, uFlags, szArgs, uNumArgs,
1723 szEnv, cbEnv, uNumEnvVars,
1724 szUser, szPassword, uTimeLimitMS);
1725 }
1726 else
1727 VBoxServiceError("ControlExec: Failed to retrieve exec start command! Error: %Rrc\n", rc);
1728 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdStartProcess returned with %Rrc\n", rc);
1729 return rc;
1730}
1731
1732
1733/**
1734 * Handles input for a started process by copying the received data into its
1735 * stdin pipe.
1736 *
1737 * @returns IPRT status code.
1738 * @param u32ClientId The HGCM client session ID.
1739 * @param uNumParms The number of parameters the host is offering.
1740 * @param cMaxBufSize The maximum buffer size for retrieving the input data.
1741 */
1742int VBoxServiceControlExecHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize)
1743{
1744 uint32_t uContextID;
1745 uint32_t uPID;
1746 uint32_t uFlags;
1747 uint32_t cbSize;
1748
1749 AssertReturn(RT_IS_POWER_OF_TWO(cbMaxBufSize), VERR_INVALID_PARAMETER);
1750 uint8_t *pabBuffer = (uint8_t*)RTMemAlloc(cbMaxBufSize);
1751 AssertPtrReturn(pabBuffer, VERR_NO_MEMORY);
1752
1753 /*
1754 * Ask the host for the input data.
1755 */
1756 int rc = VbglR3GuestCtrlExecGetHostCmdInput(u32ClientId, uNumParms,
1757 &uContextID, &uPID, &uFlags,
1758 pabBuffer, cbMaxBufSize, &cbSize);
1759 if (RT_FAILURE(rc))
1760 {
1761 VBoxServiceError("ControlExec: Failed to retrieve exec input command! Error: %Rrc\n", rc);
1762 }
1763 else if (cbSize > cbMaxBufSize)
1764 {
1765 VBoxServiceError("ControlExec: Maximum input buffer size is too small! cbSize=%u, cbMaxBufSize=%u\n",
1766 cbSize, cbMaxBufSize);
1767 rc = VERR_INVALID_PARAMETER;
1768 }
1769 else
1770 {
1771 /*
1772 * Resolve the PID.
1773 */
1774 VBoxServiceVerbose(4, "ControlExec: Input (PID %u) received: cbSize=%u\n", uPID, cbSize);
1775
1776 PVBOXSERVICECTRLTHREAD pNode = VBoxServiceControlExecFindProcess(uPID);
1777 if (pNode)
1778 {
1779 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
1780 AssertPtr(pData);
1781
1782 /*
1783 * Is this the last input block we need to deliver? Then let the pipe know ...
1784 */
1785 bool fPendingClose = false;
1786 if (uFlags & INPUT_FLAG_EOF)
1787 {
1788 fPendingClose = true;
1789 VBoxServiceVerbose(4, "ControlExec: Got last input block (PID %u) ...\n", uPID);
1790 }
1791
1792 /*
1793 * Feed the data to the pipe.
1794 */
1795 uint32_t cbWritten;
1796 rc = VBoxServiceControlExecPipeBufWrite(&pData->stdIn, pabBuffer,
1797 cbSize, fPendingClose, &cbWritten);
1798#ifdef DEBUG
1799 VBoxServiceVerbose(4, "ControlExec: Written to StdIn buffer (PID %u): rc=%Rrc, uFlags=0x%x, cbAlloc=%u, cbSize=%u, cbOffset=%u\n",
1800 uPID, rc, uFlags,
1801 pData->stdIn.cbAllocated, pData->stdIn.cbSize, pData->stdIn.cbOffset);
1802#endif
1803 uint32_t uStatus = INPUT_STS_UNDEFINED;
1804 if (RT_SUCCESS(rc))
1805 {
1806 if (cbWritten || !cbSize) /* Did we write something or was there anything to write at all? */
1807 {
1808 uStatus = INPUT_STS_WRITTEN;
1809 uFlags = 0;
1810 }
1811 }
1812 else
1813 {
1814 if (rc == VERR_BAD_PIPE)
1815 uStatus = INPUT_STS_TERMINATED;
1816 else if (rc == VERR_BUFFER_OVERFLOW)
1817 uStatus = INPUT_STS_OVERFLOW;
1818 else
1819 {
1820 uStatus = INPUT_STS_ERROR;
1821 uFlags = rc;
1822 }
1823 }
1824
1825 if (uStatus > INPUT_STS_UNDEFINED)
1826 {
1827 /* Note: Since the context ID is unique the request *has* to be completed here,
1828 * regardless whether we got data or not! Otherwise the progress object
1829 * on the host never will get completed! */
1830 rc = VbglR3GuestCtrlExecReportStatusIn(u32ClientId, uContextID, uPID,
1831 uStatus, uFlags, (uint32_t)cbWritten);
1832 }
1833 }
1834 else
1835 rc = VERR_NOT_FOUND; /* PID not found! */
1836 }
1837 RTMemFree(pabBuffer);
1838 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdSetInput returned with %Rrc\n", rc);
1839 return rc;
1840}
1841
1842
1843/**
1844 * Handles the guest control output command.
1845 *
1846 * @return IPRT status code.
1847 * @param u32ClientId idClient The HGCM client session ID.
1848 * @param uNumParms cParms The number of parameters the host is
1849 * offering.
1850 */
1851int VBoxServiceControlExecHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms)
1852{
1853 uint32_t uContextID;
1854 uint32_t uPID;
1855 uint32_t uHandleID;
1856 uint32_t uFlags;
1857
1858 int rc = VbglR3GuestCtrlExecGetHostCmdOutput(u32ClientId, uNumParms,
1859 &uContextID, &uPID, &uHandleID, &uFlags);
1860 if (RT_SUCCESS(rc))
1861 {
1862 PVBOXSERVICECTRLTHREAD pNode = VBoxServiceControlExecFindProcess(uPID);
1863 if (pNode)
1864 {
1865 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
1866 AssertPtr(pData);
1867
1868 const uint32_t cbSize = _1M;
1869 uint32_t cbRead = cbSize;
1870 uint8_t *pBuf = (uint8_t*)RTMemAlloc(cbSize);
1871 if (pBuf)
1872 {
1873 /* If the stdout pipe buffer is enabled (that is, still could be filled by a running
1874 * process) wait for the signal to arrive so that we don't return without any actual
1875 * data read. */
1876 if (VBoxServiceControlExecPipeBufIsEnabled(&pData->stdOut))
1877 {
1878 VBoxServiceVerbose(4, "ControlExec: Waiting for output data becoming ready ...\n");
1879 rc = RTSemEventWait(pData->stdOut.hEventSem, RT_INDEFINITE_WAIT);
1880 }
1881 if (RT_SUCCESS(rc))
1882 {
1883 /** @todo Use uHandleID to distinguish between stdout/stderr! */
1884 rc = VBoxServiceControlExecPipeBufRead(&pData->stdOut, pBuf, cbSize, &cbRead);
1885 if (RT_SUCCESS(rc))
1886 {
1887 /* Note: Since the context ID is unique the request *has* to be completed here,
1888 * regardless whether we got data or not! Otherwise the progress object
1889 * on the host never will get completed! */
1890 /* cbRead now contains actual size. */
1891 rc = VbglR3GuestCtrlExecSendOut(u32ClientId, uContextID, uPID, 0 /* Handle ID */, 0 /* Flags */,
1892 pBuf, cbRead);
1893 }
1894 }
1895 RTMemFree(pBuf);
1896 }
1897 else
1898 rc = VERR_NO_MEMORY;
1899 }
1900 else
1901 rc = VERR_NOT_FOUND; /* PID not found! */
1902 }
1903 else
1904 VBoxServiceError("ControlExec: Failed to retrieve exec output command! Error: %Rrc\n", rc);
1905 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdGetOutput returned with %Rrc\n", rc);
1906 return rc;
1907}
1908
Note: See TracBrowser for help on using the repository browser.

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