VirtualBox

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

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

VBoxService/GuestCopy: Fixed copying of small and 0-byte files. Refactored per-process threading code into own module, added thread locking/access.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.6 KB
Line 
1/* $Id: VBoxServiceControlExec.cpp 36548 2011-04-05 09:27:33Z 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/stream.h>
37#include <iprt/thread.h>
38#include <VBox/version.h>
39#include <VBox/VBoxGuestLib.h>
40#include <VBox/HostServices/GuestControlSvc.h>
41
42#include "VBoxServiceInternal.h"
43#include "VBoxServiceUtils.h"
44#include "VBoxServicePipeBuf.h"
45#include "VBoxServiceControlExecThread.h"
46
47using namespace guestControl;
48
49extern RTLISTNODE g_GuestControlExecThreads;
50extern RTCRITSECT g_GuestControlExecThreadsCritSect;
51
52
53/**
54 * Handle an error event on standard input.
55 *
56 * @returns IPRT status code.
57 * @param hPollSet The polling set.
58 * @param fPollEvt The event mask returned by RTPollNoResume.
59 * @param phStdInW The standard input pipe handle.
60 * @param pStdInBuf The standard input buffer.
61 */
62static int VBoxServiceControlExecProcHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
63 PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf)
64{
65 int rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
66 /* Don't assert if writable handle is not in poll set anymore. */
67 if ( RT_FAILURE(rc)
68 && rc != VERR_POLL_HANDLE_ID_NOT_FOUND)
69 {
70 AssertRC(rc);
71 }
72
73 /* Close writable stdin pipe. */
74 rc = RTPipeClose(*phStdInW);
75 AssertRC(rc);
76 *phStdInW = NIL_RTPIPE;
77
78 /* Mark the stdin buffer as dead; we're not using it anymore. */
79 rc = VBoxServicePipeBufSetStatus(pStdInBuf, false /* Disabled */);
80 AssertRC(rc);
81
82 /* Remove stdin error handle from set. */
83 rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_ERROR);
84 /* Don't assert if writable handle is not in poll set anymore. */
85 if ( RT_FAILURE(rc)
86 && rc != VERR_POLL_HANDLE_ID_NOT_FOUND)
87 {
88 AssertRC(rc);
89 }
90 else
91 rc = VINF_SUCCESS;
92
93 return rc;
94}
95
96
97/**
98 * Try write some more data to the standard input of the child.
99 *
100 * @returns IPRT status code.
101 * @retval VINF_TRY_AGAIN if there is still data left in the buffer.
102 *
103 * @param hPollSet The polling set.
104 * @param pStdInBuf The standard input buffer.
105 * @param hStdInW The standard input pipe.
106 * @param pfClose Pointer to a flag whether the pipe needs to be closed afterwards.
107 */
108static int VBoxServiceControlExecProcWriteStdIn(RTPOLLSET hPollSet, PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf, RTPIPE hStdInW,
109 size_t *pcbWritten, bool *pfClose)
110{
111 AssertPtrReturn(pStdInBuf, VERR_INVALID_PARAMETER);
112 AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
113 AssertPtrReturn(pfClose, VERR_INVALID_PARAMETER);
114
115 size_t cbLeft;
116 int rc = VBoxServicePipeBufWriteToPipe(pStdInBuf, hStdInW, pcbWritten, &cbLeft);
117
118 /* If we have written all data which is in the buffer set the close flag. */
119 *pfClose = (cbLeft == 0) && VBoxServicePipeBufIsClosing(pStdInBuf);
120
121 if ( !*pcbWritten
122 && VBoxServicePipeBufIsEnabled(pStdInBuf))
123 {
124 /*
125 * Nothing else left to write now? Remove the writable event from the poll set
126 * to not trigger too high CPU loads.
127 */
128 int rc2 = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
129 AssertRC(rc2);
130 }
131
132 VBoxServiceVerbose(3, "VBoxServiceControlExecProcWriteStdIn: Written=%u, Left=%u, rc=%Rrc\n",
133 *pcbWritten, cbLeft, rc);
134 return rc;
135}
136
137
138/**
139 * Handle an event indicating we can write to the standard input pipe of the
140 * child process.
141 *
142 * @returns IPRT status code.
143 * @param hPollSet The polling set.
144 * @param fPollEvt The event mask returned by RTPollNoResume.
145 * @param phStdInW The standard input pipe.
146 * @param pStdInBuf The standard input buffer.
147 * @param pcbWritten Where to return the number of bytes written.
148 */
149static int VBoxServiceControlExecProcHandleStdInWritableEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
150 PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf, size_t *pcbWritten)
151{
152 AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
153 int rc;
154 if (!(fPollEvt & RTPOLL_EVT_ERROR))
155 {
156 bool fClose;
157 rc = VBoxServiceControlExecProcWriteStdIn(hPollSet,
158 pStdInBuf, *phStdInW,
159 pcbWritten, &fClose);
160 if ( rc == VINF_TRY_AGAIN
161 || rc == VERR_MORE_DATA)
162 rc = VINF_SUCCESS;
163 if (RT_FAILURE(rc))
164 {
165 if ( rc == VERR_BAD_PIPE
166 || rc == VERR_BROKEN_PIPE)
167 {
168 rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
169 AssertRC(rc);
170 }
171 else
172 {
173 /** @todo Do we need to do something about this error condition? */
174 AssertRC(rc);
175 }
176 }
177 else if (fClose)
178 {
179 /* If the pipe needs to be closed, do so. */
180 rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
181 }
182 }
183 else
184 {
185 *pcbWritten = 0;
186 rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
187 }
188 return rc;
189}
190
191
192/**
193 * Handle pending output data or error on standard out, standard error or the
194 * test pipe.
195 *
196 * @returns IPRT status code from client send.
197 * @param pThread The thread specific data.
198 * @param hPollSet The polling set.
199 * @param fPollEvt The event mask returned by RTPollNoResume.
200 * @param phPipeR The pipe handle.
201 * @param pu32Crc The current CRC-32 of the stream. (In/Out)
202 * @param uHandleId The handle ID.
203 *
204 * @todo Put the last 4 parameters into a struct!
205 */
206static int VBoxServiceControlExecProcHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phPipeR,
207 uint32_t uHandleId, PVBOXSERVICECTRLEXECPIPEBUF pStdOutBuf)
208{
209#ifdef DEBUG
210 VBoxServiceVerbose(4, "ControlExec: HandleOutputEvent: fPollEvt=%#x\n", fPollEvt);
211#endif
212
213 /*
214 * Try drain the pipe before acting on any errors.
215 */
216 int rc = VINF_SUCCESS;
217 size_t cbRead;
218 uint8_t abBuf[_64K];
219
220 int rc2 = RTPipeRead(*phPipeR, abBuf, sizeof(abBuf), &cbRead);
221 if (RT_SUCCESS(rc2) && cbRead)
222 {
223 uint32_t cbWritten;
224 rc = VBoxServicePipeBufWriteToBuf(pStdOutBuf, abBuf,
225 cbRead, false /* Pending close */, &cbWritten);
226 if (RT_SUCCESS(rc))
227 {
228 Assert(cbRead == cbWritten);
229 /* Make sure we go another poll round in case there was too much data
230 for the buffer to hold. */
231 fPollEvt &= RTPOLL_EVT_ERROR;
232 }
233 }
234 else if (RT_FAILURE(rc2))
235 {
236 fPollEvt |= RTPOLL_EVT_ERROR;
237 AssertMsg(rc2 == VERR_BROKEN_PIPE, ("%Rrc\n", rc));
238 }
239
240 /*
241 * If an error was signalled, close reading stdout/stderr pipe.
242 */
243 if (fPollEvt & RTPOLL_EVT_ERROR)
244 {
245 rc2 = RTPollSetRemove(hPollSet, uHandleId);
246 AssertRC(rc2);
247
248 rc2 = RTPipeClose(*phPipeR);
249 AssertRC(rc2);
250 *phPipeR = NIL_RTPIPE;
251 }
252 return rc;
253}
254
255
256int VBoxServiceControlExecProcHandleStdInputNotify(RTPOLLSET hPollSet,
257 PRTPIPE phNotificationPipeR, PRTPIPE phInputPipeW)
258{
259#ifdef DEBUG
260 VBoxServiceVerbose(4, "ControlExec: HandleStdInputNotify\n");
261#endif
262 /* Drain the notification pipe. */
263 uint8_t abBuf[8];
264 size_t cbIgnore;
265 int rc = RTPipeRead(*phNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
266 if (RT_SUCCESS(rc))
267 {
268 /*
269 * When the writable handle previously was removed from the poll set we need to add
270 * it here again so that writable events from the started procecss get handled correctly.
271 */
272 RTHANDLE hWritableIgnored;
273 rc = RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE, &hWritableIgnored);
274 if (rc == VERR_POLL_HANDLE_ID_NOT_FOUND)
275 rc = RTPollSetAddPipe(hPollSet, *phInputPipeW, RTPOLL_EVT_WRITE, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
276 }
277 return rc;
278}
279
280
281/**
282 * Execution loop which runs in a dedicated per-started-process thread and
283 * handles all pipe input/output and signalling stuff.
284 *
285 * @return IPRT status code.
286 * @param pThread The process' thread handle.
287 * @param hProcess The actual process handle.
288 * @param cMsTimeout Time limit (in ms) of the process' life time.
289 * @param hPollSet The poll set to use.
290 * @param hStdInW Handle to the process' stdin write end.
291 * @param hStdOutR Handle to the process' stdout read end.
292 * @param hStdErrR Handle to the process' stderr read end.
293 */
294static int VBoxServiceControlExecProcLoop(PVBOXSERVICECTRLTHREAD pThread,
295 RTPROCESS hProcess, RTMSINTERVAL cMsTimeout, RTPOLLSET hPollSet,
296 PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR)
297{
298 AssertPtrReturn(phStdInW, VERR_INVALID_PARAMETER);
299 AssertPtrReturn(phStdOutR, VERR_INVALID_PARAMETER);
300 AssertPtrReturn(phStdErrR, VERR_INVALID_PARAMETER);
301
302 int rc;
303 int rc2;
304 uint64_t const MsStart = RTTimeMilliTS();
305 RTPROCSTATUS ProcessStatus = { 254, RTPROCEXITREASON_ABEND };
306 bool fProcessAlive = true;
307 bool fProcessTimedOut = false;
308 uint64_t MsProcessKilled = UINT64_MAX;
309 RTMSINTERVAL const cMsPollBase = *phStdInW != NIL_RTPIPE
310 ? 100 /* Need to poll for input. */
311 : 1000; /* Need only poll for process exit and aborts. */
312 RTMSINTERVAL cMsPollCur = 0;
313
314 AssertPtr(pThread);
315 Assert(pThread->enmType == kVBoxServiceCtrlThreadDataExec);
316 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
317 AssertPtr(pData);
318
319 /* Assign PID to thread data. */
320 pData->uPID = hProcess;
321
322 /*
323 * Before entering the loop, tell the host that we've started the guest
324 * and that it's now OK to send input to the process.
325 */
326 VBoxServiceVerbose(3, "ControlExec: Process started: PID=%u, CID=%u, User=%s\n",
327 pData->uPID, pThread->uContextID, pData->pszUser);
328 rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
329 pData->uPID, PROC_STS_STARTED, 0 /* u32Flags */,
330 NULL /* pvData */, 0 /* cbData */);
331
332 /*
333 * Process input, output, the test pipe and client requests.
334 */
335 while ( RT_SUCCESS(rc)
336 && RT_UNLIKELY(!pThread->fShutdown))
337 {
338 /*
339 * Wait/Process all pending events.
340 */
341 uint32_t idPollHnd;
342 uint32_t fPollEvt;
343 rc2 = RTPollNoResume(hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
344 if (pThread->fShutdown)
345 continue;
346
347 cMsPollCur = 0; /* No rest until we've checked everything. */
348
349 if (RT_SUCCESS(rc2))
350 {
351 VBoxServiceVerbose(4, "ControlExec: RTPollNoResume idPollHnd=%u\n", idPollHnd);
352 switch (idPollHnd)
353 {
354 case VBOXSERVICECTRLPIPEID_STDIN_ERROR:
355 rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, &pData->stdIn);
356 break;
357
358 case VBOXSERVICECTRLPIPEID_STDIN_INPUT_NOTIFY:
359 rc = VBoxServiceControlExecProcHandleStdInputNotify(hPollSet,
360 &pData->stdIn.hNotificationPipeR, &pData->pipeStdInW);
361 AssertRC(rc);
362 /* Fall through. */
363 case VBOXSERVICECTRLPIPEID_STDIN_WRITABLE:
364 {
365 size_t cbWritten;
366 rc = VBoxServiceControlExecProcHandleStdInWritableEvent(hPollSet, fPollEvt, phStdInW,
367 &pData->stdIn, &cbWritten);
368 break;
369 }
370
371 case VBOXSERVICECTRLPIPEID_STDOUT:
372 rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt, phStdOutR,
373 VBOXSERVICECTRLPIPEID_STDOUT, &pData->stdOut);
374 break;
375
376 case VBOXSERVICECTRLPIPEID_STDERR:
377 rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt, phStdErrR,
378 VBOXSERVICECTRLPIPEID_STDERR, &pData->stdOut);
379 break;
380
381 default:
382 AssertMsgFailed(("idPollHnd=%u fPollEvt=%#x\n", idPollHnd, fPollEvt));
383 break;
384 }
385 if (RT_FAILURE(rc) || rc == VINF_EOF)
386 break; /* Abort command, or client dead or something. */
387 continue;
388 }
389
390 /*
391 * Check for process death.
392 */
393 if (fProcessAlive)
394 {
395 rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
396 if (RT_SUCCESS_NP(rc2))
397 {
398 fProcessAlive = false;
399 continue;
400 }
401 if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
402 continue;
403 if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
404 {
405 fProcessAlive = false;
406 ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
407 ProcessStatus.iStatus = 255;
408 AssertFailed();
409 }
410 else
411 AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
412 }
413
414 /*
415 * If the process has terminated, we're should head out.
416 */
417 if (!fProcessAlive)
418 break;
419
420 /*
421 * Check for timed out, killing the process.
422 */
423 uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
424 if (cMsTimeout != RT_INDEFINITE_WAIT)
425 {
426 uint64_t u64Now = RTTimeMilliTS();
427 uint64_t cMsElapsed = u64Now - MsStart;
428 if (cMsElapsed >= cMsTimeout)
429 {
430 VBoxServiceVerbose(3, "ControlExec: Process timed out (%ums elapsed > %ums timeout), killing ...", cMsElapsed, cMsTimeout);
431
432 fProcessTimedOut = true;
433 if ( MsProcessKilled == UINT64_MAX
434 || u64Now - MsProcessKilled > 1000)
435 {
436 if (u64Now - MsProcessKilled > 20*60*1000)
437 break; /* Give up after 20 mins. */
438 RTProcTerminate(hProcess);
439 MsProcessKilled = u64Now;
440 continue;
441 }
442 cMilliesLeft = 10000;
443 }
444 else
445 cMilliesLeft = cMsTimeout - (uint32_t)cMsElapsed;
446 }
447
448 /* Reset the polling interval since we've done all pending work. */
449 cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft;
450
451 /*
452 * Need to exit?
453 */
454 if (pThread->fShutdown)
455 break;
456 }
457
458 /*
459 * Try kill the process if it's still alive at this point.
460 */
461 if (fProcessAlive)
462 {
463 if (MsProcessKilled == UINT64_MAX)
464 {
465 VBoxServiceVerbose(3, "ControlExec: Process (PID=%u) is still alive and not killed yet\n",
466 pData->uPID);
467
468 MsProcessKilled = RTTimeMilliTS();
469 RTProcTerminate(hProcess);
470 RTThreadSleep(500);
471 }
472
473 for (size_t i = 0; i < 10; i++)
474 {
475 VBoxServiceVerbose(4, "ControlExec: Kill attempt %d/10: Waiting for process (PID=%u) exit ...\n",
476 i + 1, pData->uPID);
477 rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
478 if (RT_SUCCESS(rc2))
479 {
480 VBoxServiceVerbose(4, "ControlExec: Kill attempt %d/10: Process (PID=%u) exited\n",
481 i + 1, pData->uPID);
482 fProcessAlive = false;
483 break;
484 }
485 if (i >= 5)
486 {
487 VBoxServiceVerbose(4, "ControlExec: Kill attempt %d/10: Try to terminate (PID=%u) ...\n",
488 i + 1, pData->uPID);
489 RTProcTerminate(hProcess);
490 }
491 RTThreadSleep(i >= 5 ? 2000 : 500);
492 }
493
494 if (fProcessAlive)
495 VBoxServiceVerbose(3, "ControlExec: Process (PID=%u) could not be killed\n", pData->uPID);
496 }
497
498 /*
499 * If we don't have a client problem (RT_FAILURE(rc) we'll reply to the
500 * clients exec packet now.
501 */
502 if (RT_SUCCESS(rc))
503 {
504 VBoxServicePipeBufSetStatus(&pData->stdIn, false /* Disabled */);
505 VBoxServicePipeBufSetStatus(&pData->stdOut, false /* Disabled */);
506 VBoxServicePipeBufSetStatus(&pData->stdErr, false /* Disabled */);
507
508 /* Since the process is not alive anymore, destroy its local
509 * stdin pipe buffer - it's not used anymore and can eat up quite
510 * a bit of memory. */
511 VBoxServicePipeBufDestroy(&pData->stdIn);
512
513 uint32_t uStatus = PROC_STS_UNDEFINED;
514 uint32_t uFlags = 0;
515
516 if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX)
517 {
518 VBoxServiceVerbose(3, "ControlExec: Process timed out and got killed\n");
519 uStatus = PROC_STS_TOK;
520 }
521 else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX)
522 {
523 VBoxServiceVerbose(3, "ControlExec: Process timed out and did *not* get killed\n");
524 uStatus = PROC_STS_TOA;
525 }
526 else if (pThread->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
527 {
528 VBoxServiceVerbose(3, "ControlExec: Process got terminated because system/service is about to shutdown\n");
529 uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */
530 uFlags = pData->uFlags; /* Return handed-in execution flags back to the host. */
531 }
532 else if (fProcessAlive)
533 {
534 VBoxServiceError("ControlExec: Process is alive when it should not!\n");
535 }
536 else if (MsProcessKilled != UINT64_MAX)
537 {
538 VBoxServiceError("ControlExec: Process has been killed when it should not!\n");
539 }
540 else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
541 {
542 VBoxServiceVerbose(3, "ControlExec: Process ended with RTPROCEXITREASON_NORMAL (%u)\n",
543 ProcessStatus.iStatus);
544
545 uStatus = PROC_STS_TEN;
546 uFlags = ProcessStatus.iStatus;
547 }
548 else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
549 {
550 VBoxServiceVerbose(3, "ControlExec: Process ended with RTPROCEXITREASON_SIGNAL (%u)\n",
551 ProcessStatus.iStatus);
552
553 uStatus = PROC_STS_TES;
554 uFlags = ProcessStatus.iStatus;
555 }
556 else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
557 {
558 VBoxServiceVerbose(3, "ControlExec: Process ended with RTPROCEXITREASON_ABEND (%u)\n",
559 ProcessStatus.iStatus);
560
561 uStatus = PROC_STS_TEA;
562 uFlags = ProcessStatus.iStatus;
563 }
564 else
565 {
566 VBoxServiceError("ControlExec: Process has reached an undefined status!\n");
567 }
568
569 VBoxServiceVerbose(3, "ControlExec: Process ended: PID=%u, CID=%u, Status=%u, Flags=%u\n",
570 pData->uPID, pThread->uContextID, uStatus, uFlags);
571 rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
572 pData->uPID, uStatus, uFlags,
573 NULL /* pvData */, 0 /* cbData */);
574 VBoxServiceVerbose(3, "ControlExec: Process loop ended with rc=%Rrc\n", rc);
575 }
576 else
577 VBoxServiceError("ControlExec: Process loop failed with rc=%Rrc\n", rc);
578 return rc;
579}
580
581
582/**
583 * Sets up the redirection / pipe / nothing for one of the standard handles.
584 *
585 * @returns IPRT status code. No client replies made.
586 * @param fd Which standard handle it is (0 == stdin, 1 ==
587 * stdout, 2 == stderr).
588 * @param ph The generic handle that @a pph may be set
589 * pointing to. Always set.
590 * @param pph Pointer to the RTProcCreateExec argument.
591 * Always set.
592 * @param phPipe Where to return the end of the pipe that we
593 * should service. Always set.
594 */
595static int VBoxServiceControlExecSetupPipe(int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
596{
597 AssertPtrReturn(ph, VERR_INVALID_PARAMETER);
598 AssertPtrReturn(pph, VERR_INVALID_PARAMETER);
599 AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
600
601 ph->enmType = RTHANDLETYPE_PIPE;
602 ph->u.hPipe = NIL_RTPIPE;
603 *pph = NULL;
604 *phPipe = NIL_RTPIPE;
605
606 int rc;
607
608 /*
609 * Setup a pipe for forwarding to/from the client.
610 * The ph union struct will be filled with a pipe read/write handle
611 * to represent the "other" end to phPipe.
612 */
613 if (fd == 0) /* stdin? */
614 {
615 /* Connect a wrtie pipe specified by phPipe to stdin. */
616 rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
617 }
618 else /* stdout or stderr? */
619 {
620 /* Connect a read pipe specified by phPipe to stdout or stderr. */
621 rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
622 }
623 if (RT_FAILURE(rc))
624 return rc;
625 ph->enmType = RTHANDLETYPE_PIPE;
626 *pph = ph;
627
628 return rc;
629}
630
631
632/**
633 * Expands a file name / path to its real content. This only works on Windows
634 * for now (e.g. translating "%TEMP%\foo.exe" to "C:\Windows\Temp" when starting
635 * with system / administrative rights).
636 *
637 * @return IPRT status code.
638 * @param pszPath Path to resolve.
639 * @param pszExpanded Pointer to string to store the resolved path in.
640 * @param cbExpanded Size (in bytes) of string to store the resolved path.
641 */
642static int VBoxServiceControlExecMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded)
643{
644 int rc = VINF_SUCCESS;
645#ifdef RT_OS_WINDOWS
646 if (!ExpandEnvironmentStrings(pszPath, pszExpanded, cbExpanded))
647 rc = RTErrConvertFromWin32(GetLastError());
648#else
649 /* No expansion for non-Windows yet. */
650 rc = RTStrCopy(pszExpanded, cbExpanded, pszPath);
651#endif
652#ifdef DEBUG
653 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecMakeFullPath: %s -> %s\n",
654 pszPath, pszExpanded);
655#endif
656 return rc;
657}
658
659
660/**
661 * Resolves the full path of a specified executable name. This function also
662 * resolves internal VBoxService tools to its appropriate executable path + name.
663 *
664 * @return IPRT status code.
665 * @param pszFileName File name to resovle.
666 * @param pszResolved Pointer to a string where the resolved file name will be stored.
667 * @param cbResolved Size (in bytes) of resolved file name string.
668 */
669static int VBoxServiceControlExecResolveExecutable(const char *pszFileName, char *pszResolved, size_t cbResolved)
670{
671 int rc = VINF_SUCCESS;
672
673 /* Search the path of our executable. */
674 char szVBoxService[RTPATH_MAX];
675 if (RTProcGetExecutablePath(szVBoxService, sizeof(szVBoxService)))
676 {
677 char *pszExecResolved = NULL;
678 if ( (g_pszProgName && RTStrICmp(pszFileName, g_pszProgName) == 0)
679 || !RTStrICmp(pszFileName, VBOXSERVICE_NAME))
680 {
681 /* We just want to execute VBoxService (no toolbox). */
682 pszExecResolved = RTStrDup(szVBoxService);
683 }
684#ifdef VBOXSERVICE_TOOLBOX
685 else if (RTStrStr(pszFileName, "vbox_") == pszFileName)
686 {
687 /* We want to use the internal toolbox (all internal
688 * tools are starting with "vbox_" (e.g. "vbox_cat"). */
689 pszExecResolved = RTStrDup(szVBoxService);
690 }
691#endif
692 else /* Nothing to resolve, copy original. */
693 pszExecResolved = RTStrDup(pszFileName);
694 AssertPtr(pszExecResolved);
695
696 rc = VBoxServiceControlExecMakeFullPath(pszExecResolved, pszResolved, cbResolved);
697#ifdef DEBUG
698 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecResolveExecutable: %s -> %s\n",
699 pszFileName, pszResolved);
700#endif
701 RTStrFree(pszExecResolved);
702 }
703 return rc;
704}
705
706
707#ifdef VBOXSERVICE_TOOLBOX
708/**
709 * Constructs the argv command line of a VBoxService program
710 * by first appending the full path of VBoxService along with the given
711 * tool name (e.g. "vbox_cat") + the tool's actual command line parameters.
712 *
713 * @return IPRT status code.
714 * @param pszFileName File name (full path) of this process.
715 * @param papszArgs Original argv command line from the host.
716 * @param ppapszArgv Pointer to a pointer with the new argv command line.
717 * Needs to be freed with RTGetOptArgvFree.
718 */
719static int VBoxServiceControlExecPrepareArgv(const char *pszFileName,
720 const char * const *papszArgs, char ***ppapszArgv)
721{
722 AssertPtrReturn(pszFileName, VERR_INVALID_PARAMETER);
723 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
724 AssertPtrReturn(ppapszArgv, VERR_INVALID_PARAMETER);
725
726/** @todo r=bird: Obvious misdesign: argv[0] does NOT have to be the same as
727 * the full path to the executable file name!! I thought we went thru
728 * all that when you did the VBoxService toolbox stuff, i.e. how busybox
729 * works? */
730
731/** @todo RTGetOptArgvToString converts to MSC quoted string, while
732 * RTGetOptArgvFromString takes bourne shell according to the docs...
733 * Actually, converting to and from here is a very roundabout way of prepending
734 * an entry (pszFilename) to an array (*ppapszArgv). */
735 char *pszArgs;
736 int rc = RTGetOptArgvToString(&pszArgs, papszArgs,
737 RTGETOPTARGV_CNV_QUOTE_MS_CRT); /* RTGETOPTARGV_CNV_QUOTE_BOURNE_SH */
738 if ( RT_SUCCESS(rc)
739 && pszArgs) /**< @todo pszArg will never be NULL on a successfull return. Perhaps *pszArgs was meant? */
740 {
741 /*
742 * Construct the new command line by appending the actual
743 * tool name to new process' command line.
744 */
745 char szArgsExp[RTPATH_MAX];
746 rc = VBoxServiceControlExecMakeFullPath(pszArgs, szArgsExp, sizeof(szArgsExp));
747 if (RT_SUCCESS(rc))
748 {
749 char *pszNewArgs;
750 if (RTStrAPrintf(&pszNewArgs, "%s %s", pszFileName, szArgsExp))
751 {
752#ifdef DEBUG
753 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecPrepareArgv: %s\n",
754 pszNewArgs);
755#endif
756 int iNumArgsIgnored;
757 rc = RTGetOptArgvFromString(ppapszArgv, &iNumArgsIgnored,
758 pszNewArgs, NULL /* Use standard separators. */);
759 RTStrFree(pszNewArgs);
760 }
761 }
762 RTStrFree(pszArgs);
763 }
764 else /* No arguments given, just use the resolved file name as argv[0]. */
765 {
766 int iNumArgsIgnored;
767 rc = RTGetOptArgvFromString(ppapszArgv, &iNumArgsIgnored,
768 pszFileName, NULL /* Use standard separators. */);
769 }
770 return rc;
771}
772#endif
773
774
775/**
776 * Helper function to create/start a process on the guest.
777 *
778 * @return IPRT status code.
779 * @param pszExec Full qualified path of process to start (without arguments).
780 * @param papszArgs Pointer to array of command line arguments.
781 * @param hEnv Handle to environment block to use.
782 * @param fFlags Process execution flags.
783 * @param phStdIn Handle for the process' stdin pipe.
784 * @param phStdOut Handle for the process' stdout pipe.
785 * @param phStdErr Handle for the process' stderr pipe.
786 * @param pszAsUser User name (account) to start the process under.
787 * @param pszPassword Password of the specified user.
788 * @param phProcess Pointer which will receive the process handle after
789 * successful process start.
790 */
791static int VBoxServiceControlExecCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
792 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
793 const char *pszPassword, PRTPROCESS phProcess)
794{
795 AssertPtrReturn(pszExec, VERR_INVALID_PARAMETER);
796 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
797 AssertPtrReturn(phProcess, VERR_INVALID_PARAMETER);
798
799 int rc = VINF_SUCCESS;
800 char szExecExp[RTPATH_MAX];
801#ifdef RT_OS_WINDOWS
802 /*
803 * If sysprep should be executed do this in the context of VBoxService, which
804 * (usually, if started by SCM) has administrator rights. Because of that a UI
805 * won't be shown (doesn't have a desktop).
806 */
807 if (RTStrICmp(pszExec, "sysprep") == 0)
808 {
809 /* Use a predefined sysprep path as default. */
810 char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe";
811
812 /*
813 * On Windows Vista (and up) sysprep is located in "system32\\sysprep\\sysprep.exe",
814 * so detect the OS and use a different path.
815 */
816 OSVERSIONINFOEX OSInfoEx;
817 RT_ZERO(OSInfoEx);
818 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
819 if ( GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
820 && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
821 && OSInfoEx.dwMajorVersion >= 6 /* Vista or later */)
822 {
823 rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL);
824 if (RT_SUCCESS(rc))
825 rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\sysprep\\sysprep.exe");
826 }
827
828 if (RT_SUCCESS(rc))
829 {
830 char **papszArgsExp;
831 rc = VBoxServiceControlExecPrepareArgv(szSysprepCmd, papszArgs, &papszArgsExp);
832 if (RT_SUCCESS(rc))
833 {
834 rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */,
835 phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */,
836 NULL /* pszPassword */, phProcess);
837 }
838 RTGetOptArgvFree(papszArgsExp);
839 }
840 return rc;
841 }
842#endif /* RT_OS_WINDOWS */
843
844 /*
845 * Do the environment variables expansion on executable and arguments.
846 */
847 rc = VBoxServiceControlExecResolveExecutable(pszExec, szExecExp, sizeof(szExecExp));
848 if (RT_SUCCESS(rc))
849 {
850 char **papszArgsExp;
851 rc = VBoxServiceControlExecPrepareArgv(szExecExp, papszArgs, &papszArgsExp);
852 if (RT_SUCCESS(rc))
853 {
854 uint32_t uProcFlags = 0;
855 if (fFlags)
856 {
857 /* Process Main flag "ExecuteProcessFlag_Hidden". */
858 if (fFlags & RT_BIT(2))
859 uProcFlags = RTPROC_FLAGS_HIDDEN;
860 }
861
862 /* If no user name specified run with current credentials (e.g.
863 * full service/system rights). This is prohibited via official Main API!
864 *
865 * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication
866 * code (at least on Windows) for running processes as different users
867 * started from our system service. */
868 if (*pszAsUser)
869 uProcFlags |= RTPROC_FLAGS_SERVICE;
870#ifdef DEBUG
871 char *pszCmdLine;
872 rc = RTGetOptArgvToString(&pszCmdLine, papszArgsExp, 0 /* Default */);
873 AssertRC(rc);
874 VBoxServiceVerbose(3, "ControlExec: Executing: %s %s\n",
875 szExecExp, pszCmdLine);
876 RTStrFree(pszCmdLine);
877#endif
878 /* Do normal execution. */
879 rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags,
880 phStdIn, phStdOut, phStdErr,
881 *pszAsUser ? pszAsUser : NULL,
882 *pszPassword ? pszPassword : NULL,
883 phProcess);
884 RTGetOptArgvFree(papszArgsExp);
885 }
886 }
887 return rc;
888}
889
890/**
891 * The actual worker routine (lopp) for a started guest process.
892 *
893 * @return IPRT status code.
894 * @param PVBOXSERVICECTRLTHREAD Thread data associated with a started process.
895 */
896static DECLCALLBACK(int) VBoxServiceControlExecProcessWorker(PVBOXSERVICECTRLTHREAD pThread)
897{
898 AssertPtr(pThread);
899 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
900 AssertPtr(pData);
901
902 VBoxServiceVerbose(3, "ControlExec: Thread of process \"%s\" started\n", pData->pszCmd);
903
904 int rc = VbglR3GuestCtrlConnect(&pThread->uClientID);
905 if (RT_FAILURE(rc))
906 {
907 VBoxServiceError("ControlExec: Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc);
908 RTThreadUserSignal(RTThreadSelf());
909 return rc;
910 }
911
912 bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
913
914 /*
915 * Create the environment.
916 */
917 RTENV hEnv;
918 rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
919 if (RT_SUCCESS(rc))
920 {
921 size_t i;
922 for (i = 0; i < pData->uNumEnvVars && pData->papszEnv; i++)
923 {
924 rc = RTEnvPutEx(hEnv, pData->papszEnv[i]);
925 if (RT_FAILURE(rc))
926 break;
927 }
928 if (RT_SUCCESS(rc))
929 {
930 /*
931 * Setup the redirection of the standard stuff.
932 */
933 /** @todo consider supporting: gcc stuff.c >file 2>&1. */
934 RTHANDLE hStdIn;
935 PRTHANDLE phStdIn;
936 rc = VBoxServiceControlExecSetupPipe(0 /*STDIN_FILENO*/, &hStdIn, &phStdIn, &pData->pipeStdInW);
937 if (RT_SUCCESS(rc))
938 {
939 RTHANDLE hStdOut;
940 PRTHANDLE phStdOut;
941 RTPIPE hStdOutR;
942 rc = VBoxServiceControlExecSetupPipe(1 /*STDOUT_FILENO*/, &hStdOut, &phStdOut, &hStdOutR);
943 if (RT_SUCCESS(rc))
944 {
945 RTHANDLE hStdErr;
946 PRTHANDLE phStdErr;
947 RTPIPE hStdErrR;
948 rc = VBoxServiceControlExecSetupPipe(2 /*STDERR_FILENO*/, &hStdErr, &phStdErr, &hStdErrR);
949 if (RT_SUCCESS(rc))
950 {
951 /*
952 * Create a poll set for the pipes and let the
953 * transport layer add stuff to it as well.
954 */
955 RTPOLLSET hPollSet;
956 rc = RTPollSetCreate(&hPollSet);
957 if (RT_SUCCESS(rc))
958 {
959 rc = RTPollSetAddPipe(hPollSet, pData->pipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN_ERROR);
960 if (RT_SUCCESS(rc))
961 rc = RTPollSetAddPipe(hPollSet, hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDOUT);
962 if (RT_SUCCESS(rc))
963 rc = RTPollSetAddPipe(hPollSet, hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDERR);
964 if (RT_SUCCESS(rc))
965 rc = RTPollSetAddPipe(hPollSet, pData->pipeStdInW, RTPOLL_EVT_WRITE, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
966 if (RT_SUCCESS(rc))
967 rc = RTPollSetAddPipe(hPollSet, pData->stdIn.hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_STDIN_INPUT_NOTIFY);
968 if (RT_SUCCESS(rc))
969 {
970 RTPROCESS hProcess;
971 rc = VBoxServiceControlExecCreateProcess(pData->pszCmd, pData->papszArgs, hEnv, pData->uFlags,
972 phStdIn, phStdOut, phStdErr,
973 pData->pszUser, pData->pszPassword,
974 &hProcess);
975 if (RT_FAILURE(rc))
976 VBoxServiceError("ControlExec: Error starting process, rc=%Rrc\n", rc);
977 /*
978 * Tell the control thread that it can continue
979 * spawning services. This needs to be done after the new
980 * process has been started because otherwise signal handling
981 * on (Open) Solaris does not work correctly (see #5068).
982 */
983 int rc2 = RTThreadUserSignal(RTThreadSelf());
984 if (RT_FAILURE(rc2))
985 rc = rc2;
986 fSignalled = true;
987
988 if (RT_SUCCESS(rc))
989 {
990 /*
991 * Close the child ends of any pipes and redirected files.
992 */
993 rc2 = RTHandleClose(phStdIn); AssertRC(rc2);
994 phStdIn = NULL;
995 rc2 = RTHandleClose(phStdOut); AssertRC(rc2);
996 phStdOut = NULL;
997 rc2 = RTHandleClose(phStdErr); AssertRC(rc2);
998 phStdErr = NULL;
999
1000 /* Enter the process loop. */
1001 rc = VBoxServiceControlExecProcLoop(pThread,
1002 hProcess, pData->uTimeLimitMS, hPollSet,
1003 &pData->pipeStdInW, &hStdOutR, &hStdErrR);
1004
1005 /*
1006 * The handles that are no longer in the set have
1007 * been closed by the above call in order to prevent
1008 * the guest from getting stuck accessing them.
1009 * So, NIL the handles to avoid closing them again.
1010 */
1011 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 0 /* stdin */, NULL)))
1012 pData->pipeStdInW = NIL_RTPIPE;
1013 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 1 /* stdout */, NULL)))
1014 hStdOutR = NIL_RTPIPE;
1015 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 2 /* stderr */, NULL)))
1016 hStdErrR = NIL_RTPIPE;
1017 }
1018 else /* Something went wrong; report error! */
1019 {
1020 VBoxServiceError("ControlExec: Could not start process '%s' (CID: %u)! Error: %Rrc\n",
1021 pData->pszCmd, pThread->uContextID, rc);
1022
1023 rc2 = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, pData->uPID,
1024 PROC_STS_ERROR, rc,
1025 NULL /* pvData */, 0 /* cbData */);
1026 if (RT_FAILURE(rc2))
1027 VBoxServiceError("ControlExec: Could not report process start error! Error: %Rrc (process error %Rrc)\n",
1028 rc2, rc);
1029 }
1030 }
1031 RTPollSetDestroy(hPollSet);
1032 }
1033 RTPipeClose(hStdErrR);
1034 RTHandleClose(phStdErr);
1035 }
1036 RTPipeClose(hStdOutR);
1037 RTHandleClose(phStdOut);
1038 }
1039 RTPipeClose(pData->pipeStdInW);
1040 RTHandleClose(phStdIn);
1041 }
1042 }
1043 RTEnvDestroy(hEnv);
1044 }
1045
1046 VbglR3GuestCtrlDisconnect(pThread->uClientID);
1047 VBoxServiceVerbose(3, "ControlExec: Thread of process \"%s\" (PID: %u) ended with rc=%Rrc\n",
1048 pData->pszCmd, pData->uPID, rc);
1049
1050 /*
1051 * If something went wrong signal the user event so that others don't wait
1052 * forever on this thread.
1053 */
1054 if (RT_FAILURE(rc) && !fSignalled)
1055 RTThreadUserSignal(RTThreadSelf());
1056 return rc;
1057}
1058
1059
1060/**
1061 * Thread main routine for a started process.
1062 *
1063 * @return IPRT status code.
1064 * @param RTTHREAD Pointer to the thread's data.
1065 * @param void* User-supplied argument pointer.
1066 *
1067 */
1068static DECLCALLBACK(int) VBoxServiceControlExecThread(RTTHREAD ThreadSelf, void *pvUser)
1069{
1070 PVBOXSERVICECTRLTHREAD pThread = (VBOXSERVICECTRLTHREAD*)pvUser;
1071 AssertPtr(pThread);
1072 return VBoxServiceControlExecProcessWorker(pThread);
1073}
1074
1075
1076/**
1077 * Executes (starts) a process on the guest. This causes a new thread to be created
1078 * so that this function will not block the overall program execution.
1079 *
1080 * @return IPRT status code.
1081 * @param uContextID Context ID to associate the process to start with.
1082 * @param pszCmd Full qualified path of process to start (without arguments).
1083 * @param uFlags Process execution flags.
1084 * @param pszArgs String of arguments to pass to the process to start.
1085 * @param uNumArgs Number of arguments specified in pszArgs.
1086 * @param pszEnv String of environment variables ("FOO=BAR") to pass to the process
1087 * to start.
1088 * @param cbEnv Size (in bytes) of environment variables.
1089 * @param uNumEnvVars Number of environment variables specified in pszEnv.
1090 * @param pszUser User name (account) to start the process under.
1091 * @param pszPassword Password of specified user name (account).
1092 * @param uTimeLimitMS Time limit (in ms) of the process' life time.
1093 */
1094int VBoxServiceControlExecProcess(uint32_t uContextID, const char *pszCmd, uint32_t uFlags,
1095 const char *pszArgs, uint32_t uNumArgs,
1096 const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
1097 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS)
1098{
1099 int rc;
1100
1101 PVBOXSERVICECTRLTHREAD pThread = (PVBOXSERVICECTRLTHREAD)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREAD));
1102 if (pThread)
1103 {
1104 rc = VBoxServiceControlExecThreadAlloc(pThread,
1105 uContextID,
1106 pszCmd, uFlags,
1107 pszArgs, uNumArgs,
1108 pszEnv, cbEnv, uNumEnvVars,
1109 pszUser, pszPassword,
1110 uTimeLimitMS);
1111 if (RT_SUCCESS(rc))
1112 {
1113 rc = RTThreadCreate(&pThread->Thread, VBoxServiceControlExecThread,
1114 (void *)(PVBOXSERVICECTRLTHREAD*)pThread, 0,
1115 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "Exec");
1116 if (RT_FAILURE(rc))
1117 {
1118 VBoxServiceError("ControlExec: RTThreadCreate failed, rc=%Rrc\n, pThread=%p\n",
1119 rc, pThread);
1120 }
1121 else
1122 {
1123 VBoxServiceVerbose(4, "ControlExec: Waiting for thread to initialize ...\n");
1124
1125 /* Wait for the thread to initialize. */
1126 RTThreadUserWait(pThread->Thread, 60 * 1000);
1127 if (pThread->fShutdown)
1128 {
1129 VBoxServiceError("ControlExec: Thread for process \"%s\" failed to start!\n", pszCmd);
1130 rc = VERR_GENERAL_FAILURE;
1131 }
1132 else
1133 {
1134 pThread->fStarted = true;
1135 /*rc =*/ RTListAppend(&g_GuestControlExecThreads, &pThread->Node);
1136 }
1137 }
1138
1139 if (RT_FAILURE(rc))
1140 VBoxServiceControlExecThreadDestroy((PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData);
1141 }
1142 if (RT_FAILURE(rc))
1143 RTMemFree(pThread);
1144 }
1145 else
1146 rc = VERR_NO_MEMORY;
1147 return rc;
1148}
1149
1150
1151/**
1152 * Handles starting processes on the guest.
1153 *
1154 * @returns IPRT status code.
1155 * @param u32ClientId The HGCM client session ID.
1156 * @param uNumParms The number of parameters the host is offering.
1157 */
1158int VBoxServiceControlExecHandleCmdStartProcess(uint32_t u32ClientId, uint32_t uNumParms)
1159{
1160 uint32_t uContextID;
1161 char szCmd[_1K];
1162 uint32_t uFlags;
1163 char szArgs[_1K];
1164 uint32_t uNumArgs;
1165 char szEnv[_64K];
1166 uint32_t cbEnv = sizeof(szEnv);
1167 uint32_t uNumEnvVars;
1168 char szUser[128];
1169 char szPassword[128];
1170 uint32_t uTimeLimitMS;
1171
1172#if 0 /* for valgrind */
1173 RT_ZERO(szCmd);
1174 RT_ZERO(szArgs);
1175 RT_ZERO(szEnv);
1176 RT_ZERO(szUser);
1177 RT_ZERO(szPassword);
1178#endif
1179
1180 if (uNumParms != 11)
1181 return VERR_INVALID_PARAMETER;
1182
1183 int rc = VbglR3GuestCtrlExecGetHostCmd(u32ClientId,
1184 uNumParms,
1185 &uContextID,
1186 /* Command */
1187 szCmd, sizeof(szCmd),
1188 /* Flags */
1189 &uFlags,
1190 /* Arguments */
1191 szArgs, sizeof(szArgs), &uNumArgs,
1192 /* Environment */
1193 szEnv, &cbEnv, &uNumEnvVars,
1194 /* Credentials */
1195 szUser, sizeof(szUser),
1196 szPassword, sizeof(szPassword),
1197 /* Timelimit */
1198 &uTimeLimitMS);
1199#ifdef DEBUG
1200 VBoxServiceVerbose(3, "ControlExec: Start process szCmd=%s, uFlags=%u, szArgs=%s, szEnv=%s, szUser=%s, szPW=%s, uTimeout=%u\n",
1201 szCmd, uFlags, uNumArgs ? szArgs : "<None>", uNumEnvVars ? szEnv : "<None>", szUser, szPassword, uTimeLimitMS);
1202#endif
1203 if (RT_SUCCESS(rc))
1204 {
1205 rc = VBoxServiceControlExecProcess(uContextID, szCmd, uFlags, szArgs, uNumArgs,
1206 szEnv, cbEnv, uNumEnvVars,
1207 szUser, szPassword, uTimeLimitMS);
1208 }
1209 else
1210 VBoxServiceError("ControlExec: Failed to retrieve exec start command! Error: %Rrc\n", rc);
1211 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdStartProcess returned with %Rrc\n", rc);
1212 return rc;
1213}
1214
1215
1216/**
1217 * Handles input for a started process by copying the received data into its
1218 * stdin pipe.
1219 *
1220 * @returns IPRT status code.
1221 * @param u32ClientId The HGCM client session ID.
1222 * @param uNumParms The number of parameters the host is offering.
1223 * @param cMaxBufSize The maximum buffer size for retrieving the input data.
1224 */
1225int VBoxServiceControlExecHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize)
1226{
1227 uint32_t uContextID;
1228 uint32_t uPID;
1229 uint32_t uFlags;
1230 uint32_t cbSize;
1231
1232 AssertReturn(RT_IS_POWER_OF_TWO(cbMaxBufSize), VERR_INVALID_PARAMETER);
1233 uint8_t *pabBuffer = (uint8_t*)RTMemAlloc(cbMaxBufSize);
1234 AssertPtrReturn(pabBuffer, VERR_NO_MEMORY);
1235
1236 uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status sent back to the host. */
1237 uint32_t cbWritten = 0; /* Number of bytes written to the guest. */
1238
1239 /*
1240 * Ask the host for the input data.
1241 */
1242 int rc = VbglR3GuestCtrlExecGetHostCmdInput(u32ClientId, uNumParms,
1243 &uContextID, &uPID, &uFlags,
1244 pabBuffer, cbMaxBufSize, &cbSize);
1245 if (RT_FAILURE(rc))
1246 {
1247 VBoxServiceError("ControlExec: Failed to retrieve exec input command! Error: %Rrc\n", rc);
1248 }
1249 else if (cbSize > cbMaxBufSize)
1250 {
1251 VBoxServiceError("ControlExec: Maximum input buffer size is too small! cbSize=%u, cbMaxBufSize=%u\n",
1252 cbSize, cbMaxBufSize);
1253 rc = VERR_INVALID_PARAMETER;
1254 }
1255 else
1256 {
1257 /*
1258 * Is this the last input block we need to deliver? Then let the pipe know ...
1259 */
1260 bool fPendingClose = false;
1261 if (uFlags & INPUT_FLAG_EOF)
1262 {
1263 fPendingClose = true;
1264 VBoxServiceVerbose(4, "ControlExec: Got last input block (PID %u) of size %u ...\n", uPID, cbSize);
1265 }
1266
1267 rc = VBoxServiceControlExecThreadSetInput(uPID, fPendingClose, pabBuffer,
1268 cbSize, &cbWritten);
1269 VBoxServiceVerbose(4, "ControlExec: Written input (PID %u): rc=%Rrc, uFlags=0x%x, fPendingClose=%d, cbSize=%u, cbWritten=%u\n",
1270 uPID, rc, uFlags, fPendingClose, cbSize, cbWritten);
1271 if (RT_SUCCESS(rc))
1272 {
1273 if (cbWritten || !cbSize) /* Did we write something or was there anything to write at all? */
1274 {
1275 uStatus = INPUT_STS_WRITTEN;
1276 uFlags = 0;
1277 }
1278 }
1279 else
1280 {
1281 if (rc == VERR_BAD_PIPE)
1282 uStatus = INPUT_STS_TERMINATED;
1283 else if (rc == VERR_BUFFER_OVERFLOW)
1284 uStatus = INPUT_STS_OVERFLOW;
1285 }
1286 }
1287 RTMemFree(pabBuffer);
1288
1289 /*
1290 * If there was an error and we did not set the host status
1291 * yet, then do it now.
1292 */
1293 if ( RT_FAILURE(rc)
1294 && uStatus == INPUT_STS_UNDEFINED)
1295 {
1296 uStatus = INPUT_STS_ERROR;
1297 uFlags = rc;
1298 }
1299 Assert(uStatus > INPUT_STS_UNDEFINED);
1300
1301 VBoxServiceVerbose(3, "ControlExec: Input processed (PID %u), Status=%u, Flags=0x%x, cbWritten=%u\n",
1302 uPID, uStatus, uFlags, cbWritten);
1303
1304 /* Note: Since the context ID is unique the request *has* to be completed here,
1305 * regardless whether we got data or not! Otherwise the progress object
1306 * on the host never will get completed! */
1307 rc = VbglR3GuestCtrlExecReportStatusIn(u32ClientId, uContextID, uPID,
1308 uStatus, uFlags, (uint32_t)cbWritten);
1309
1310 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdSetInput returned with %Rrc\n", rc);
1311 return rc;
1312}
1313
1314
1315/**
1316 * Handles the guest control output command.
1317 *
1318 * @return IPRT status code.
1319 * @param u32ClientId idClient The HGCM client session ID.
1320 * @param uNumParms cParms The number of parameters the host is
1321 * offering.
1322 */
1323int VBoxServiceControlExecHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms)
1324{
1325 uint32_t uContextID;
1326 uint32_t uPID;
1327 uint32_t uHandleID;
1328 uint32_t uFlags;
1329
1330 int rc = VbglR3GuestCtrlExecGetHostCmdOutput(u32ClientId, uNumParms,
1331 &uContextID, &uPID, &uHandleID, &uFlags);
1332 if (RT_SUCCESS(rc))
1333 {
1334 uint32_t cbRead;
1335 uint8_t *pBuf = (uint8_t*)RTMemAlloc(_64K);
1336 if (pBuf)
1337 {
1338 rc = VBoxServiceControlExecThreadGetOutput(uPID, uHandleID, RT_INDEFINITE_WAIT /* Timeout */,
1339 pBuf, _64K /* cbSize */, &cbRead);
1340 if (RT_SUCCESS(rc))
1341 {
1342 /* Note: Since the context ID is unique the request *has* to be completed here,
1343 * regardless whether we got data or not! Otherwise the progress object
1344 * on the host never will get completed! */
1345 /* cbRead now contains actual size. */
1346 rc = VbglR3GuestCtrlExecSendOut(u32ClientId, uContextID, uPID, uHandleID, 0 /* Flags */,
1347 pBuf, cbRead);
1348 }
1349 RTMemFree(pBuf);
1350 }
1351 else
1352 rc = VERR_NO_MEMORY;
1353 }
1354 else
1355 VBoxServiceError("ControlExec: Failed to retrieve exec output command! Error: %Rrc\n", rc);
1356 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdGetOutput returned with %Rrc\n", rc);
1357 return rc;
1358}
1359
Note: See TracBrowser for help on using the repository browser.

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