VirtualBox

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

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

VBoxService/GuestExec: Fixed file copy on Linux, refactored pipe buffer into own module.

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