VirtualBox

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

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

Guest Execution: more review stuff.

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