VirtualBox

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

Last change on this file since 38633 was 38633, checked in by vboxsync, 13 years ago

VBoxService: Fixed German Umlauts handling in toolbox, minimized/adjusted log locking.

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