VirtualBox

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

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

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 73.7 KB
Line 
1/* $Id: VBoxServiceControlExec.cpp 35954 2011-02-14 10:33:26Z 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#ifdef RT_OS_WINDOWS
1300 /*
1301 * If sysprep should be executed do this in the context of VBoxService, which
1302 * (usually, if started by SCM) has administrator rights. Because of that a UI
1303 * won't be shown (doesn't have a desktop).
1304 */
1305 if (RTStrICmp(pszExec, "sysprep") == 0)
1306 {
1307 /* Get the predefined path of sysprep.exe (depending on Windows OS). */
1308 char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe";
1309 OSVERSIONINFOEX OSInfoEx;
1310 RT_ZERO(OSInfoEx);
1311 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
1312 if ( GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
1313 && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
1314 && OSInfoEx.dwMajorVersion >= 6 /* Vista or later */)
1315 {
1316 rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL);
1317 if (RT_SUCCESS(rc))
1318 rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\sysprep\\sysprep.exe");
1319 }
1320 rc = RTProcCreateEx(szSysprepCmd, papszArgs, hEnv, 0 /* fFlags */,
1321 phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */,
1322 NULL /* pszPassword */, phProcess);
1323 return rc;
1324 }
1325#endif /* RT_OS_WINDOWS */
1326
1327 /*
1328 * Do the environment variables expansion on executable and arguments.
1329 */
1330 char szExecExp[RTPATH_MAX];
1331 rc = VBoxServiceControlExecResolveExecutable(pszExec, szExecExp, sizeof(szExecExp));
1332 if (RT_SUCCESS(rc))
1333 {
1334 char **papszArgsExp;
1335 rc = VBoxServiceControlExecPrepareArgv(szExecExp, papszArgs, &papszArgsExp);
1336 if (RT_SUCCESS(rc))
1337 {
1338 uint32_t uProcFlags = 0;
1339 if (fFlags)
1340 {
1341 /* Process Main flag "ExecuteProcessFlag_Hidden". */
1342 if (fFlags & RT_BIT(2))
1343 uProcFlags = RTPROC_FLAGS_HIDDEN;
1344 }
1345
1346 /* If no user name specified run with current credentials (e.g.
1347 * full service/system rights). This is prohibited via official Main API!
1348 *
1349 * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication
1350 * code (at least on Windows) for running processes as different users
1351 * started from our system service. */
1352 if (strlen(pszAsUser))
1353 uProcFlags |= RTPROC_FLAGS_SERVICE;
1354
1355 /* Do normal execution. */
1356 rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags,
1357 phStdIn, phStdOut, phStdErr,
1358 strlen(pszAsUser) ? pszAsUser : NULL,
1359 strlen(pszPassword) ? pszPassword : NULL,
1360 phProcess);
1361 }
1362 RTGetOptArgvFree(papszArgsExp);
1363 }
1364 return rc;
1365}
1366
1367/**
1368 * The actual worker routine (lopp) for a started guest process.
1369 *
1370 * @return IPRT status code.
1371 * @param PVBOXSERVICECTRLTHREAD Thread data associated with a started process.
1372 */
1373static DECLCALLBACK(int) VBoxServiceControlExecProcessWorker(PVBOXSERVICECTRLTHREAD pThread)
1374{
1375 AssertPtr(pThread);
1376 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
1377 AssertPtr(pData);
1378
1379 VBoxServiceVerbose(3, "ControlExec: Thread of process \"%s\" started\n", pData->pszCmd);
1380
1381 int rc = VbglR3GuestCtrlConnect(&pThread->uClientID);
1382 if (RT_FAILURE(rc))
1383 {
1384 VBoxServiceError("ControlExec: Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc);
1385 RTThreadUserSignal(RTThreadSelf());
1386 return rc;
1387 }
1388
1389 bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
1390
1391 /*
1392 * Create the environment.
1393 */
1394 RTENV hEnv;
1395 rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
1396 if (RT_SUCCESS(rc))
1397 {
1398 size_t i;
1399 for (i = 0; i < pData->uNumEnvVars && pData->papszEnv; i++)
1400 {
1401 rc = RTEnvPutEx(hEnv, pData->papszEnv[i]);
1402 if (RT_FAILURE(rc))
1403 break;
1404 }
1405 if (RT_SUCCESS(rc))
1406 {
1407 /*
1408 * Setup the redirection of the standard stuff.
1409 */
1410 /** @todo consider supporting: gcc stuff.c >file 2>&1. */
1411 RTHANDLE hStdIn;
1412 PRTHANDLE phStdIn;
1413 rc = VBoxServiceControlExecSetupPipe(0 /*STDIN_FILENO*/, &hStdIn, &phStdIn, &pData->pipeStdInW);
1414 if (RT_SUCCESS(rc))
1415 {
1416 RTHANDLE hStdOut;
1417 PRTHANDLE phStdOut;
1418 RTPIPE hStdOutR;
1419 rc = VBoxServiceControlExecSetupPipe(1 /*STDOUT_FILENO*/, &hStdOut, &phStdOut, &hStdOutR);
1420 if (RT_SUCCESS(rc))
1421 {
1422 RTHANDLE hStdErr;
1423 PRTHANDLE phStdErr;
1424 RTPIPE hStdErrR;
1425 rc = VBoxServiceControlExecSetupPipe(2 /*STDERR_FILENO*/, &hStdErr, &phStdErr, &hStdErrR);
1426 if (RT_SUCCESS(rc))
1427 {
1428 /*
1429 * Create a poll set for the pipes and let the
1430 * transport layer add stuff to it as well.
1431 */
1432 RTPOLLSET hPollSet;
1433 rc = RTPollSetCreate(&hPollSet);
1434 if (RT_SUCCESS(rc))
1435 {
1436 rc = RTPollSetAddPipe(hPollSet, pData->pipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN_ERROR);
1437 if (RT_SUCCESS(rc))
1438 rc = RTPollSetAddPipe(hPollSet, hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDOUT);
1439 if (RT_SUCCESS(rc))
1440 rc = RTPollSetAddPipe(hPollSet, hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDERR);
1441 if (RT_SUCCESS(rc))
1442 rc = RTPollSetAddPipe(hPollSet, pData->pipeStdInW, RTPOLL_EVT_WRITE, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
1443 if (RT_SUCCESS(rc))
1444 rc = RTPollSetAddPipe(hPollSet, pData->stdIn.hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_STDIN_INPUT_NOTIFY);
1445 if (RT_SUCCESS(rc))
1446 {
1447 RTPROCESS hProcess;
1448 rc = VBoxServiceControlExecCreateProcess(pData->pszCmd, pData->papszArgs, hEnv, pData->uFlags,
1449 phStdIn, phStdOut, phStdErr,
1450 pData->pszUser, pData->pszPassword,
1451 &hProcess);
1452
1453 /*
1454 * Tell the control thread that it can continue
1455 * spawning services. This needs to be done after the new
1456 * process has been started because otherwise signal handling
1457 * on (Open) Solaris does not work correctly (see #5068).
1458 */
1459 int rc2 = RTThreadUserSignal(RTThreadSelf());
1460 if (RT_FAILURE(rc2))
1461 rc = rc2;
1462 fSignalled = true;
1463
1464 if (RT_SUCCESS(rc))
1465 {
1466 /*
1467 * Close the child ends of any pipes and redirected files.
1468 */
1469 rc2 = RTHandleClose(phStdIn); AssertRC(rc2);
1470 phStdIn = NULL;
1471 rc2 = RTHandleClose(phStdOut); AssertRC(rc2);
1472 phStdOut = NULL;
1473 rc2 = RTHandleClose(phStdErr); AssertRC(rc2);
1474 phStdErr = NULL;
1475
1476 /* Enter the process loop. */
1477 rc = VBoxServiceControlExecProcLoop(pThread,
1478 hProcess, pData->uTimeLimitMS, hPollSet,
1479 &pData->pipeStdInW, &hStdOutR, &hStdErrR);
1480
1481 /*
1482 * The handles that are no longer in the set have
1483 * been closed by the above call in order to prevent
1484 * the guest from getting stuck accessing them.
1485 * So, NIL the handles to avoid closing them again.
1486 */
1487 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 0 /* stdin */, NULL)))
1488 pData->pipeStdInW = NIL_RTPIPE;
1489 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 1 /* stdout */, NULL)))
1490 hStdOutR = NIL_RTPIPE;
1491 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 2 /* stderr */, NULL)))
1492 hStdErrR = NIL_RTPIPE;
1493 }
1494 else /* Something went wrong; report error! */
1495 {
1496 VBoxServiceError("ControlExec: Could not start process '%s' (CID: %u)! Error: %Rrc\n",
1497 pData->pszCmd, pThread->uContextID, rc);
1498
1499 rc2 = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, pData->uPID,
1500 PROC_STS_ERROR, rc,
1501 NULL /* pvData */, 0 /* cbData */);
1502 if (RT_FAILURE(rc2))
1503 VBoxServiceError("ControlExec: Could not report process start error! Error: %Rrc (process error %Rrc)\n",
1504 rc2, rc);
1505 }
1506 }
1507 RTPollSetDestroy(hPollSet);
1508 }
1509 RTPipeClose(hStdErrR);
1510 RTHandleClose(phStdErr);
1511 }
1512 RTPipeClose(hStdOutR);
1513 RTHandleClose(phStdOut);
1514 }
1515 RTPipeClose(pData->pipeStdInW);
1516 RTHandleClose(phStdIn);
1517 }
1518 }
1519 RTEnvDestroy(hEnv);
1520 }
1521
1522 VbglR3GuestCtrlDisconnect(pThread->uClientID);
1523 VBoxServiceVerbose(3, "ControlExec: Thread of process \"%s\" (PID: %u) ended with rc=%Rrc\n",
1524 pData->pszCmd, pData->uPID, rc);
1525
1526 /*
1527 * If something went wrong signal the user event so that others don't wait
1528 * forever on this thread.
1529 */
1530 if (RT_FAILURE(rc) && !fSignalled)
1531 RTThreadUserSignal(RTThreadSelf());
1532 return rc;
1533}
1534
1535
1536/**
1537 * Finds a (formerly) started process given by its PID.
1538 *
1539 * @return PVBOXSERVICECTRLTHREAD Process structure if found, otherwise NULL.
1540 * @param uPID PID to search for.
1541 */
1542static PVBOXSERVICECTRLTHREAD VBoxServiceControlExecFindProcess(uint32_t uPID)
1543{
1544 PVBOXSERVICECTRLTHREAD pNode;
1545 bool fFound = false;
1546 RTListForEach(&g_GuestControlExecThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
1547 {
1548 if ( pNode->fStarted
1549 && pNode->enmType == kVBoxServiceCtrlThreadDataExec)
1550 {
1551 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
1552 if (pData && pData->uPID == uPID)
1553 {
1554 return pNode;
1555 }
1556 }
1557 }
1558 return NULL;
1559}
1560
1561
1562/**
1563 * Thread main routine for a started process.
1564 *
1565 * @return IPRT status code.
1566 * @param RTTHREAD Pointer to the thread's data.
1567 * @param void* User-supplied argument pointer.
1568 *
1569 */
1570static DECLCALLBACK(int) VBoxServiceControlExecThread(RTTHREAD ThreadSelf, void *pvUser)
1571{
1572 PVBOXSERVICECTRLTHREAD pThread = (VBOXSERVICECTRLTHREAD*)pvUser;
1573 AssertPtr(pThread);
1574 return VBoxServiceControlExecProcessWorker(pThread);
1575}
1576
1577
1578/**
1579 * Executes (starts) a process on the guest. This causes a new thread to be created
1580 * so that this function will not block the overall program execution.
1581 *
1582 * @return IPRT status code.
1583 * @param uContextID Context ID to associate the process to start with.
1584 * @param pszCmd Full qualified path of process to start (without arguments).
1585 * @param uFlags Process execution flags.
1586 * @param pszArgs String of arguments to pass to the process to start.
1587 * @param uNumArgs Number of arguments specified in pszArgs.
1588 * @param pszEnv String of environment variables ("FOO=BAR") to pass to the process
1589 * to start.
1590 * @param cbEnv Size (in bytes) of environment variables.
1591 * @param uNumEnvVars Number of environment variables specified in pszEnv.
1592 * @param pszUser User name (account) to start the process under.
1593 * @param pszPassword Password of specified user name (account).
1594 * @param uTimeLimitMS Time limit (in ms) of the process' life time.
1595 */
1596int VBoxServiceControlExecProcess(uint32_t uContextID, const char *pszCmd, uint32_t uFlags,
1597 const char *pszArgs, uint32_t uNumArgs,
1598 const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
1599 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS)
1600{
1601 PVBOXSERVICECTRLTHREAD pThread = (PVBOXSERVICECTRLTHREAD)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREAD));
1602
1603 int rc;
1604 if (pThread)
1605 {
1606 rc = VBoxServiceControlExecAllocateThreadData(pThread,
1607 uContextID,
1608 pszCmd, uFlags,
1609 pszArgs, uNumArgs,
1610 pszEnv, cbEnv, uNumEnvVars,
1611 pszUser, pszPassword,
1612 uTimeLimitMS);
1613 if (RT_SUCCESS(rc))
1614 {
1615 rc = RTThreadCreate(&pThread->Thread, VBoxServiceControlExecThread,
1616 (void *)(PVBOXSERVICECTRLTHREAD*)pThread, 0,
1617 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "Exec");
1618 if (RT_FAILURE(rc))
1619 {
1620 VBoxServiceError("ControlExec: RTThreadCreate failed, rc=%Rrc\n, pThread=%p\n",
1621 rc, pThread);
1622 }
1623 else
1624 {
1625 VBoxServiceVerbose(4, "ControlExec: Waiting for thread to initialize ...\n");
1626
1627 /* Wait for the thread to initialize. */
1628 RTThreadUserWait(pThread->Thread, 60 * 1000);
1629 if (pThread->fShutdown)
1630 {
1631 VBoxServiceError("ControlExec: Thread for process \"%s\" failed to start!\n", pszCmd);
1632 rc = VERR_GENERAL_FAILURE;
1633 }
1634 else
1635 {
1636 pThread->fStarted = true;
1637 /*rc =*/ RTListAppend(&g_GuestControlExecThreads, &pThread->Node);
1638 }
1639 }
1640
1641 if (RT_FAILURE(rc))
1642 VBoxServiceControlExecDestroyThreadData((PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData);
1643 }
1644 if (RT_FAILURE(rc))
1645 RTMemFree(pThread);
1646 }
1647 else
1648 rc = VERR_NO_MEMORY;
1649 return rc;
1650}
1651
1652
1653/**
1654 * Handles starting processes on the guest.
1655 *
1656 * @returns IPRT status code.
1657 * @param u32ClientId The HGCM client session ID.
1658 * @param uNumParms The number of parameters the host is offering.
1659 */
1660int VBoxServiceControlExecHandleCmdStartProcess(uint32_t u32ClientId, uint32_t uNumParms)
1661{
1662 uint32_t uContextID;
1663 char szCmd[_1K];
1664 uint32_t uFlags;
1665 char szArgs[_1K];
1666 uint32_t uNumArgs;
1667 char szEnv[_64K];
1668 uint32_t cbEnv = sizeof(szEnv);
1669 uint32_t uNumEnvVars;
1670 char szUser[128];
1671 char szPassword[128];
1672 uint32_t uTimeLimitMS;
1673
1674#if 0 /* for valgrind */
1675 RT_ZERO(szCmd);
1676 RT_ZERO(szArgs);
1677 RT_ZERO(szEnv);
1678 RT_ZERO(szUser);
1679 RT_ZERO(szPassword);
1680#endif
1681
1682 if (uNumParms != 11)
1683 return VERR_INVALID_PARAMETER;
1684
1685 int rc = VbglR3GuestCtrlExecGetHostCmd(u32ClientId,
1686 uNumParms,
1687 &uContextID,
1688 /* Command */
1689 szCmd, sizeof(szCmd),
1690 /* Flags */
1691 &uFlags,
1692 /* Arguments */
1693 szArgs, sizeof(szArgs), &uNumArgs,
1694 /* Environment */
1695 szEnv, &cbEnv, &uNumEnvVars,
1696 /* Credentials */
1697 szUser, sizeof(szUser),
1698 szPassword, sizeof(szPassword),
1699 /* Timelimit */
1700 &uTimeLimitMS);
1701#ifdef DEBUG
1702 VBoxServiceVerbose(3, "ControlExec: Start process szCmd=%s, uFlags=%u, szArgs=%s, szEnv=%s, szUser=%s, szPW=%s, uTimeout=%u\n",
1703 szCmd, uFlags, uNumArgs ? szArgs : "<None>", uNumEnvVars ? szEnv : "<None>", szUser, szPassword, uTimeLimitMS);
1704#endif
1705 if (RT_SUCCESS(rc))
1706 {
1707 rc = VBoxServiceControlExecProcess(uContextID, szCmd, uFlags, szArgs, uNumArgs,
1708 szEnv, cbEnv, uNumEnvVars,
1709 szUser, szPassword, uTimeLimitMS);
1710 }
1711 else
1712 VBoxServiceError("ControlExec: Failed to retrieve exec start command! Error: %Rrc\n", rc);
1713 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdStartProcess returned with %Rrc\n", rc);
1714 return rc;
1715}
1716
1717
1718/**
1719 * Handles input for a started process by copying the received data into its
1720 * stdin pipe.
1721 *
1722 * @returns IPRT status code.
1723 * @param u32ClientId The HGCM client session ID.
1724 * @param uNumParms The number of parameters the host is offering.
1725 * @param cMaxBufSize The maximum buffer size for retrieving the input data.
1726 */
1727int VBoxServiceControlExecHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize)
1728{
1729 uint32_t uContextID;
1730 uint32_t uPID;
1731 uint32_t uFlags;
1732 uint32_t cbSize;
1733
1734 AssertReturn(RT_IS_POWER_OF_TWO(cbMaxBufSize), VERR_INVALID_PARAMETER);
1735 uint8_t *pabBuffer = (uint8_t*)RTMemAlloc(cbMaxBufSize);
1736 AssertPtrReturn(pabBuffer, VERR_NO_MEMORY);
1737
1738 /*
1739 * Ask the host for the input data.
1740 */
1741 int rc = VbglR3GuestCtrlExecGetHostCmdInput(u32ClientId, uNumParms,
1742 &uContextID, &uPID, &uFlags,
1743 pabBuffer, cbMaxBufSize, &cbSize);
1744 if (RT_FAILURE(rc))
1745 {
1746 VBoxServiceError("ControlExec: Failed to retrieve exec input command! Error: %Rrc\n", rc);
1747 }
1748 else if (cbSize > cbMaxBufSize)
1749 {
1750 VBoxServiceError("ControlExec: Maximum input buffer size is too small! cbSize=%u, cbMaxBufSize=%u\n",
1751 cbSize, cbMaxBufSize);
1752 rc = VERR_INVALID_PARAMETER;
1753 }
1754 else
1755 {
1756 /*
1757 * Resolve the PID.
1758 */
1759#ifdef DEBUG
1760 VBoxServiceVerbose(4, "ControlExec: Input (PID %u) received: cbSize=%u\n", uPID, cbSize);
1761#endif
1762 PVBOXSERVICECTRLTHREAD pNode = VBoxServiceControlExecFindProcess(uPID);
1763 if (pNode)
1764 {
1765 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
1766 AssertPtr(pData);
1767
1768 /*
1769 * Is this the last input block we need to deliver? Then let the pipe know ...
1770 */
1771 bool fPendingClose = false;
1772 if (uFlags & INPUT_FLAG_EOF)
1773 fPendingClose = true;
1774#ifdef DEBUG
1775 if (fPendingClose)
1776 VBoxServiceVerbose(4, "ControlExec: Got last input block ...\n");
1777#endif
1778 /*
1779 * Feed the data to the pipe.
1780 */
1781 uint32_t cbWritten;
1782 rc = VBoxServiceControlExecPipeBufWrite(&pData->stdIn, pabBuffer,
1783 cbSize, fPendingClose, &cbWritten);
1784#ifdef DEBUG
1785 VBoxServiceVerbose(4, "ControlExec: Written to StdIn buffer (PID %u): rc=%Rrc, uFlags=0x%x, cbAlloc=%u, cbSize=%u, cbOffset=%u\n",
1786 uPID, rc, uFlags,
1787 pData->stdIn.cbAllocated, pData->stdIn.cbSize, pData->stdIn.cbOffset);
1788#endif
1789 uint32_t uStatus = INPUT_STS_UNDEFINED;
1790 if (RT_SUCCESS(rc))
1791 {
1792 if (cbWritten || !cbSize) /* Did we write something or was there anything to write at all? */
1793 {
1794 uStatus = INPUT_STS_WRITTEN;
1795 uFlags = 0;
1796 }
1797 }
1798 else
1799 {
1800 if (rc == VERR_BAD_PIPE)
1801 uStatus = INPUT_STS_TERMINATED;
1802 else if (rc == VERR_BUFFER_OVERFLOW)
1803 uStatus = INPUT_STS_OVERFLOW;
1804 else
1805 {
1806 uStatus = INPUT_STS_ERROR;
1807 uFlags = rc;
1808 }
1809 }
1810
1811 if (uStatus > INPUT_STS_UNDEFINED)
1812 {
1813 /* Note: Since the context ID is unique the request *has* to be completed here,
1814 * regardless whether we got data or not! Otherwise the progress object
1815 * on the host never will get completed! */
1816 rc = VbglR3GuestCtrlExecReportStatusIn(u32ClientId, uContextID, uPID,
1817 uStatus, uFlags, (uint32_t)cbWritten);
1818 }
1819 }
1820 else
1821 rc = VERR_NOT_FOUND; /* PID not found! */
1822 }
1823 RTMemFree(pabBuffer);
1824 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdSetInput returned with %Rrc\n", rc);
1825 return rc;
1826}
1827
1828
1829/**
1830 * Handles the guest control output command.
1831 *
1832 * @return IPRT status code.
1833 * @param u32ClientId idClient The HGCM client session ID.
1834 * @param uNumParms cParms The number of parameters the host is
1835 * offering.
1836 */
1837int VBoxServiceControlExecHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms)
1838{
1839 uint32_t uContextID;
1840 uint32_t uPID;
1841 uint32_t uHandleID;
1842 uint32_t uFlags;
1843
1844 int rc = VbglR3GuestCtrlExecGetHostCmdOutput(u32ClientId, uNumParms,
1845 &uContextID, &uPID, &uHandleID, &uFlags);
1846 if (RT_SUCCESS(rc))
1847 {
1848 PVBOXSERVICECTRLTHREAD pNode = VBoxServiceControlExecFindProcess(uPID);
1849 if (pNode)
1850 {
1851 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
1852 AssertPtr(pData);
1853
1854 const uint32_t cbSize = _1M;
1855 uint32_t cbRead = cbSize;
1856 uint8_t *pBuf = (uint8_t*)RTMemAlloc(cbSize);
1857 if (pBuf)
1858 {
1859 /* If the stdout pipe buffer is enabled (that is, still could be filled by a running
1860 * process) wait for the signal to arrive so that we don't return without any actual
1861 * data read. */
1862 if (VBoxServiceControlExecPipeBufIsEnabled(&pData->stdOut))
1863 {
1864 VBoxServiceVerbose(4, "ControlExec: Waiting for output data becoming ready ...\n");
1865 rc = RTSemEventWait(pData->stdOut.hEventSem, RT_INDEFINITE_WAIT);
1866 }
1867 if (RT_SUCCESS(rc))
1868 {
1869 /** @todo Use uHandleID to distinguish between stdout/stderr! */
1870 rc = VBoxServiceControlExecPipeBufRead(&pData->stdOut, pBuf, cbSize, &cbRead);
1871 if (RT_SUCCESS(rc))
1872 {
1873 /* Note: Since the context ID is unique the request *has* to be completed here,
1874 * regardless whether we got data or not! Otherwise the progress object
1875 * on the host never will get completed! */
1876 /* cbRead now contains actual size. */
1877 rc = VbglR3GuestCtrlExecSendOut(u32ClientId, uContextID, uPID, 0 /* Handle ID */, 0 /* Flags */,
1878 pBuf, cbRead);
1879 }
1880 }
1881 RTMemFree(pBuf);
1882 }
1883 else
1884 rc = VERR_NO_MEMORY;
1885 }
1886 else
1887 rc = VERR_NOT_FOUND; /* PID not found! */
1888 }
1889 else
1890 VBoxServiceError("ControlExec: Failed to retrieve exec output command! Error: %Rrc\n", rc);
1891 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdGetOutput returned with %Rrc\n", rc);
1892 return rc;
1893}
1894
Note: See TracBrowser for help on using the repository browser.

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