VirtualBox

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

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

VBoxService/ControlExec: Update.

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