VirtualBox

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

Last change on this file since 28218 was 28218, checked in by vboxsync, 15 years ago

Guest Control: Update (HGCM low level callbacks, bugfixes).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.3 KB
Line 
1
2/* $Id: VBoxServiceControlExec.cpp 28218 2010-04-12 15:58:07Z vboxsync $ */
3/** @file
4 * VBoxServiceControlExec - Utility functions for process execution.
5 */
6
7/*
8 * Copyright (C) 2010 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#include <iprt/assert.h>
28#include <iprt/crc32.h>
29#include <iprt/ctype.h>
30#include <iprt/env.h>
31#include <iprt/file.h>
32#include <iprt/getopt.h>
33#include <iprt/handle.h>
34#include <iprt/mem.h>
35#include <iprt/path.h>
36#include <iprt/param.h>
37#include <iprt/pipe.h>
38#include <iprt/poll.h>
39#include <iprt/process.h>
40#include <iprt/string.h>
41#include <iprt/semaphore.h>
42#include <iprt/stream.h>
43#include <iprt/thread.h>
44#include <VBox/version.h>
45#include <VBox/VBoxGuestLib.h>
46#include <VBox/HostServices/GuestControlSvc.h>
47#include "VBoxServiceInternal.h"
48#include "VBoxServiceUtils.h"
49
50using namespace guestControl;
51
52/**
53 * Handle an error event on standard input.
54 *
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 void VBoxServiceControlExecProcHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
61 PVBOXSERVICECTRLSTDINBUF pStdInBuf)
62{
63 int rc2;
64 if (pStdInBuf->off < pStdInBuf->cb)
65 {
66 rc2 = RTPollSetRemove(hPollSet, 4 /*TXSEXECHNDID_STDIN_WRITABLE*/);
67 AssertRC(rc2);
68 }
69
70 rc2 = RTPollSetRemove(hPollSet, 0 /*TXSEXECHNDID_STDIN*/);
71 AssertRC(rc2);
72
73 rc2 = RTPipeClose(*phStdInW);
74 AssertRC(rc2);
75 *phStdInW = NIL_RTPIPE;
76
77 RTMemFree(pStdInBuf->pch);
78 pStdInBuf->pch = NULL;
79 pStdInBuf->off = 0;
80 pStdInBuf->cb = 0;
81 pStdInBuf->cbAllocated = 0;
82 pStdInBuf->fBitBucket = true;
83}
84
85
86/**
87 * Try write some more data to the standard input of the child.
88 *
89 * @returns IPRT status code.
90 * @param pStdInBuf The standard input buffer.
91 * @param hStdInW The standard input pipe.
92 */
93static int VBoxServiceControlExecProcWriteStdIn(PVBOXSERVICECTRLSTDINBUF pStdInBuf, RTPIPE hStdInW)
94{
95 size_t cbToWrite = pStdInBuf->cb - pStdInBuf->off;
96 size_t cbWritten;
97 int rc = RTPipeWrite(hStdInW, &pStdInBuf->pch[pStdInBuf->off], cbToWrite, &cbWritten);
98 if (RT_SUCCESS(rc))
99 {
100 Assert(cbWritten == cbToWrite);
101 pStdInBuf->off += cbWritten;
102 }
103 return rc;
104}
105
106
107/**
108 * Handle an event indicating we can write to the standard input pipe of the
109 * child process.
110 *
111 * @param hPollSet The polling set.
112 * @param fPollEvt The event mask returned by RTPollNoResume.
113 * @param phStdInW The standard input pipe.
114 * @param pStdInBuf The standard input buffer.
115 */
116static void VBoxServiceControlExecProcHandleStdInWritableEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
117 PVBOXSERVICECTRLSTDINBUF pStdInBuf)
118{
119 int rc;
120 if (!(fPollEvt & RTPOLL_EVT_ERROR))
121 {
122 rc = VBoxServiceControlExecProcWriteStdIn(pStdInBuf, *phStdInW);
123 if (RT_FAILURE(rc) && rc != VERR_BAD_PIPE)
124 {
125 /** @todo do we need to do something about this error condition? */
126 AssertRC(rc);
127 }
128
129 if (pStdInBuf->off < pStdInBuf->cb)
130 {
131 rc = RTPollSetRemove(hPollSet, 4 /*TXSEXECHNDID_STDIN_WRITABLE*/);
132 AssertRC(rc);
133 }
134 }
135 else
136 VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
137}
138
139
140/**
141 * Handle pending output data or error on standard out, standard error or the
142 * test pipe.
143 *
144 * @returns IPRT status code from client send.
145 * @param hPollSet The polling set.
146 * @param fPollEvt The event mask returned by RTPollNoResume.
147 * @param phPipeR The pipe handle.
148 * @param pu32Crc The current CRC-32 of the stream. (In/Out)
149 * @param uHandleId The handle ID.
150 * @param pszOpcode The opcode for the data upload.
151 *
152 * @todo Put the last 4 parameters into a struct!
153 */
154static int VBoxServiceControlExecProcHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phPipeR,
155 uint32_t *puCrc32 , uint32_t uHandleId)
156{
157 Log(("VBoxServiceControlExecProcHandleOutputEvent: fPollEvt=%#x\n", fPollEvt));
158
159 /*
160 * Try drain the pipe before acting on any errors.
161 */
162 int rc = VINF_SUCCESS;
163
164 char abBuf[_64K];
165 size_t cbRead;
166 int rc2 = RTPipeRead(*phPipeR, abBuf, sizeof(abBuf), &cbRead);
167 if (RT_SUCCESS(rc2) && cbRead)
168 {
169 Log(("Crc32=%#x ", *puCrc32));
170
171#if 1
172 abBuf[cbRead] = '\0';
173 RTPrintf("%s: %s\n", uHandleId == 1 ? "StdOut: " : "StdErr: ", abBuf);
174#endif
175
176 /**puCrc32 = RTCrc32Process(*puCrc32, abBuf, cbRead);
177 Log(("cbRead=%#x Crc32=%#x \n", cbRead, *puCrc32));
178 Pkt.uCrc32 = RTCrc32Finish(*puCrc32);*/
179 /* if (g_fDisplayOutput)
180 {
181 if (enmHndId == TXSEXECHNDID_STDOUT)
182 RTStrmPrintf(g_pStdErr, "%.*s", cbRead, Pkt.abBuf);
183 else if (enmHndId == TXSEXECHNDID_STDERR)
184 RTStrmPrintf(g_pStdErr, "%.*s", cbRead, Pkt.abBuf);
185 }
186
187 rc = txsReplyInternal(&Pkt.Hdr, pszOpcode, cbRead + sizeof(uint32_t));*/
188
189 /* Make sure we go another poll round in case there was too much data
190 for the buffer to hold. */
191 fPollEvt &= RTPOLL_EVT_ERROR;
192 }
193 else if (RT_FAILURE(rc2))
194 {
195 fPollEvt |= RTPOLL_EVT_ERROR;
196 AssertMsg(rc2 == VERR_BROKEN_PIPE, ("%Rrc\n", rc));
197 }
198
199 /*
200 * If an error was raised signalled,
201 */
202 if (fPollEvt & RTPOLL_EVT_ERROR)
203 {
204 rc2 = RTPollSetRemove(hPollSet, uHandleId);
205 AssertRC(rc2);
206
207 rc2 = RTPipeClose(*phPipeR);
208 AssertRC(rc2);
209 *phPipeR = NIL_RTPIPE;
210 }
211 return rc;
212}
213
214
215/**
216 * Handle a transport event or successful pfnPollIn() call.
217 *
218 * @returns IPRT status code from client send.
219 * @retval VINF_EOF indicates ABORT command.
220 *
221 * @param hPollSet The polling set.
222 * @param fPollEvt The event mask returned by RTPollNoResume.
223 * @param idPollHnd The handle ID.
224 * @param hStdInW The standard input pipe.
225 * @param pStdInBuf The standard input buffer.
226 */
227static int VBoxServiceControlExecProcHandleTransportEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, uint32_t idPollHnd,
228 PRTPIPE phStdInW, PVBOXSERVICECTRLSTDINBUF pStdInBuf)
229{
230
231 int rc = RTPollSetAddPipe(hPollSet, *phStdInW, RTPOLL_EVT_WRITE, 4 /*TXSEXECHNDID_STDIN_WRITABLE*/);
232
233 return rc;
234}
235
236
237static int VBoxServiceControlExecProcLoop(RTPROCESS hProcess, RTMSINTERVAL cMillies, RTPOLLSET hPollSet,
238 RTPIPE hStdInW, RTPIPE hStdOutR, RTPIPE hStdErrR)
239{
240 int rc;
241 int rc2;
242 VBOXSERVICECTRLSTDINBUF StdInBuf = { 0, 0, NULL, 0, hStdInW == NIL_RTPIPE, RTCrc32Start() };
243 uint32_t uStdOutCrc32 = RTCrc32Start();
244 uint32_t uStdErrCrc32 = uStdOutCrc32;
245 uint64_t const MsStart = RTTimeMilliTS();
246 RTPROCSTATUS ProcessStatus = { 254, RTPROCEXITREASON_ABEND };
247 bool fProcessAlive = true;
248 bool fProcessTimedOut = false;
249 uint64_t MsProcessKilled = UINT64_MAX;
250 bool const fHavePipes = hStdInW != NIL_RTPIPE
251 || hStdOutR != NIL_RTPIPE
252 || hStdErrR != NIL_RTPIPE;
253 RTMSINTERVAL const cMsPollBase = hStdInW != NIL_RTPIPE
254 ? 100 /* need to poll for input */
255 : 1000; /* need only poll for process exit and aborts */
256 RTMSINTERVAL cMsPollCur = 0;
257
258 /*
259 * Before entering the loop, tell the host that we've started the guest
260 * and that it's now OK to send input to the process.
261 */
262 rc = VINF_SUCCESS;
263
264 /*
265 * Process input, output, the test pipe and client requests.
266 */
267 while (RT_SUCCESS(rc))
268 {
269 /*
270 * Wait/Process all pending events.
271 */
272 uint32_t idPollHnd;
273 uint32_t fPollEvt;
274 rc2 = RTPollNoResume(hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
275
276 cMsPollCur = 0; /* no rest until we've checked everything. */
277
278 if (RT_SUCCESS(rc2))
279 {
280 switch (idPollHnd)
281 {
282 case 0 /* TXSEXECHNDID_STDIN */:
283 VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, &hStdInW, &StdInBuf);
284 break;
285
286 case 1 /* TXSEXECHNDID_STDOUT */:
287 rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt, &hStdOutR, &uStdOutCrc32, 1 /* TXSEXECHNDID_STDOUT */);
288 break;
289
290 case 2 /*TXSEXECHNDID_STDERR */:
291 rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt, &hStdErrR, &uStdErrCrc32, 2 /*TXSEXECHNDID_STDERR */);
292 break;
293
294 case 4 /* TXSEXECHNDID_STDIN_WRITABLE */:
295 VBoxServiceControlExecProcHandleStdInWritableEvent(hPollSet, fPollEvt, &hStdInW, &StdInBuf);
296 break;
297
298 default:
299 rc = VBoxServiceControlExecProcHandleTransportEvent(hPollSet, fPollEvt, idPollHnd, &hStdInW, &StdInBuf);
300 break;
301 }
302 if (RT_FAILURE(rc) || rc == VINF_EOF)
303 break; /* abort command, or client dead or something */
304 continue;
305 }
306
307 /*
308 * Check for incoming data.
309 */
310
311 /*
312 * Check for process death.
313 */
314 if (fProcessAlive)
315 {
316 rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
317 if (RT_SUCCESS_NP(rc2))
318 {
319 fProcessAlive = false;
320 continue;
321 }
322 if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
323 continue;
324 if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
325 {
326 fProcessAlive = false;
327 ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
328 ProcessStatus.iStatus = 255;
329 AssertFailed();
330 }
331 else
332 AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
333 }
334
335 /*
336 * If the process has terminated, we're should head out.
337 */
338 if (!fProcessAlive)
339 break;
340
341 /*
342 * Check for timed out, killing the process.
343 */
344 uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
345 if (cMillies != RT_INDEFINITE_WAIT)
346 {
347 uint64_t u64Now = RTTimeMilliTS();
348 uint64_t cMsElapsed = u64Now - MsStart;
349 if (cMsElapsed >= cMillies)
350 {
351 fProcessTimedOut = true;
352 if ( MsProcessKilled == UINT64_MAX
353 || u64Now - MsProcessKilled > 1000)
354 {
355 if (u64Now - MsProcessKilled > 20*60*1000)
356 break; /* give up after 20 mins */
357 RTProcTerminate(hProcess);
358 MsProcessKilled = u64Now;
359 continue;
360 }
361 cMilliesLeft = 10000;
362 }
363 else
364 cMilliesLeft = cMillies - (uint32_t)cMsElapsed;
365 }
366
367 /* Reset the polling interval since we've done all pending work. */
368 cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft;
369 }
370
371 /*
372 * Try kill the process if it's still alive at this point.
373 */
374 if (fProcessAlive)
375 {
376 if (MsProcessKilled == UINT64_MAX)
377 {
378 MsProcessKilled = RTTimeMilliTS();
379 RTProcTerminate(hProcess);
380 RTThreadSleep(500);
381 }
382
383 for (size_t i = 0; i < 10; i++)
384 {
385 rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
386 if (RT_SUCCESS(rc2))
387 {
388 fProcessAlive = false;
389 break;
390 }
391 if (i >= 5)
392 RTProcTerminate(hProcess);
393 RTThreadSleep(i >= 5 ? 2000 : 500);
394 }
395 }
396
397 /*
398 * If we don't have a client problem (RT_FAILURE(rc) we'll reply to the
399 * clients exec packet now.
400 */
401 if (RT_SUCCESS(rc))
402 {
403 if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX)
404 {
405
406 }
407 else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX)
408 {
409
410 }
411 /*else if (g_fTerminate && (fProcessAlive || MsProcessKilled != UINT64_MAX))
412 {
413
414 }*/
415 else if (fProcessAlive)
416 {
417
418 }
419 else if (MsProcessKilled != UINT64_MAX)
420 {
421
422 }
423 else if ( ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL
424 && ProcessStatus.iStatus == 0)
425 {
426
427 }
428 else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
429 {
430
431 }
432 else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
433 {
434
435 }
436 else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
437 {
438
439 }
440 else
441 {
442
443 }
444 }
445
446 RTMemFree(StdInBuf.pch);
447 return rc;
448}
449
450
451/**
452 * Sets up the redirection / pipe / nothing for one of the standard handles.
453 *
454 * @returns IPRT status code. No client replies made.
455 * @param pszHowTo How to set up this standard handle.
456 * @param fd Which standard handle it is (0 == stdin, 1 ==
457 * stdout, 2 == stderr).
458 * @param ph The generic handle that @a pph may be set
459 * pointing to. Always set.
460 * @param pph Pointer to the RTProcCreateExec argument.
461 * Always set.
462 * @param phPipe Where to return the end of the pipe that we
463 * should service. Always set.
464 */
465static int VBoxServiceControlExecSetupPipe(int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
466{
467 AssertPtr(ph);
468 AssertPtr(pph);
469 AssertPtr(phPipe);
470
471 ph->enmType = RTHANDLETYPE_PIPE;
472 ph->u.hPipe = NIL_RTPIPE;
473 *pph = NULL;
474 *phPipe = NIL_RTPIPE;
475
476 int rc;
477
478 /*
479 * Setup a pipe for forwarding to/from the client.
480 * The ph union struct will be filled with a pipe read/write handle
481 * to represent the "other" end to phPipe.
482 */
483 if (fd == 0) /* stdin? */
484 {
485 /* Connect a wrtie pipe specified by phPipe to stdin. */
486 rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
487 }
488 else /* stdout or stderr? */
489 {
490 /* Connect a read pipe specified by phPipe to stdout or stderr. */
491 rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
492 }
493 if (RT_FAILURE(rc))
494 return rc;
495 ph->enmType = RTHANDLETYPE_PIPE;
496 *pph = ph;
497
498 return rc;
499}
500
501/** Allocates and gives back a thread data struct which then can be used by the worker thread. */
502PVBOXSERVICECTRLTHREADDATA VBoxServiceControlExecAllocateThreadData(const char *pszCmd, uint32_t uFlags,
503 const char *pszArgs, uint32_t uNumArgs,
504 const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
505 const char *pszStdIn, const char *pszStdOut, const char *pszStdErr,
506 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS)
507{
508 PVBOXSERVICECTRLTHREADDATA pData = (PVBOXSERVICECTRLTHREADDATA)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREADDATA));
509 if (pData == NULL)
510 return NULL;
511
512 pData->pszCmd = RTStrDup(pszCmd);
513 pData->uFlags = uFlags;
514 pData->uNumEnvVars = 0;
515 pData->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
516
517 /* Prepare argument list. */
518 int rc = RTGetOptArgvFromString(&pData->papszArgs, (int*)&pData->uNumArgs,
519 (uNumArgs > 0) ? pszArgs : "", NULL);
520 /* Did we get the same result? */
521 Assert(uNumArgs == pData->uNumArgs);
522
523 if (RT_SUCCESS(rc))
524 {
525 /* Prepare environment list. */
526 if (uNumEnvVars)
527 {
528 pData->papszEnv = (char**)RTMemAlloc(uNumEnvVars * sizeof(char*));
529 AssertPtr(pData->papszEnv);
530 pData->uNumEnvVars = uNumEnvVars;
531
532 const char *pcCur = pszEnv;
533 uint32_t i = 0;
534 uint32_t cbLen = 0;
535 while (cbLen < cbEnv)
536 {
537 if (RTStrAPrintf(& pData->papszEnv[i++], "%s", pcCur) < 0)
538 {
539 rc = VERR_NO_MEMORY;
540 break;
541 }
542 cbLen += strlen(pcCur) + 1; /* Skip terminating zero. */
543 pcCur += cbLen;
544 }
545 }
546
547 pData->pszStdIn = RTStrDup(pszStdIn);
548 pData->pszStdOut = RTStrDup(pszStdOut);
549 pData->pszStdErr = RTStrDup(pszStdErr);
550 pData->pszUser = RTStrDup(pszUser);
551 pData->pszPassword = RTStrDup(pszPassword);
552 pData->uTimeLimitMS = uTimeLimitMS;
553 }
554
555 /* Adjust time limit value. */
556 pData->uTimeLimitMS = ( (uTimeLimitMS == UINT32_MAX)
557 || (uTimeLimitMS == 0)) ?
558 RT_INDEFINITE_WAIT : uTimeLimitMS;
559 return pData;
560}
561
562/** Frees an allocated thread data structure along with all its allocated parameters. */
563void VBoxServiceControlExecFreeThreadData(PVBOXSERVICECTRLTHREADDATA pData)
564{
565 AssertPtr(pData);
566 RTStrFree(pData->pszCmd);
567 if (pData->uNumEnvVars)
568 {
569 for (uint32_t i = 0; i < pData->uNumEnvVars; i++)
570 RTStrFree(pData->papszEnv[i]);
571 RTMemFree(pData->papszEnv);
572 }
573 RTGetOptArgvFree(pData->papszArgs);
574 RTStrFree(pData->pszStdIn);
575 RTStrFree(pData->pszStdOut);
576 RTStrFree(pData->pszStdErr);
577 RTStrFree(pData->pszUser);
578 RTStrFree(pData->pszPassword);
579
580 RTMemFree(pData);
581}
582
583DECLCALLBACK(int) VBoxServiceControlExecProcessWorker(PVBOXSERVICECTRLTHREADDATA pData)
584{
585 AssertPtr(pData);
586 AssertPtr(pData->papszArgs);
587 AssertPtr(pData->papszEnv);
588
589 /*
590 * Tell the control thread that it can continue
591 * spawning services.
592 */
593 RTThreadUserSignal(RTThreadSelf());
594 VBoxServiceVerbose(3, "Control: Thread of process \"%s\" started.", pData->pszCmd);
595
596 uint32_t u32ClientID;
597 int rc = VbglR3GuestCtrlConnect(&u32ClientID);
598 if (RT_SUCCESS(rc))
599 VBoxServiceVerbose(3, "Control: Thread client ID: %#x\n", u32ClientID);
600 else
601 {
602 VBoxServiceError("Control: Thread failed to connect to the guest control service! Error: %Rrc\n", rc);
603 return rc;
604 }
605
606 /*
607 * Create the environment.
608 */
609 RTENV hEnv;
610 rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
611 if (RT_SUCCESS(rc))
612 {
613 size_t i;
614 for (i = 0; i < pData->uNumEnvVars; i++)
615 {
616 rc = RTEnvPutEx(hEnv, pData->papszEnv[i]);
617 if (RT_FAILURE(rc))
618 break;
619 }
620 if (RT_SUCCESS(rc))
621 {
622 /*
623 * Setup the redirection of the standard stuff.
624 */
625 /** @todo consider supporting: gcc stuff.c >file 2>&1. */
626 RTHANDLE hStdIn;
627 PRTHANDLE phStdIn;
628 RTPIPE hStdInW;
629 rc = VBoxServiceControlExecSetupPipe(0 /* stdin */, &hStdIn, &phStdIn, &hStdInW);
630 if (RT_SUCCESS(rc))
631 {
632 RTHANDLE hStdOut;
633 PRTHANDLE phStdOut;
634 RTPIPE hStdOutR;
635 rc = VBoxServiceControlExecSetupPipe(1 /* stdout */, &hStdOut, &phStdOut, &hStdOutR);
636 if (RT_SUCCESS(rc))
637 {
638 RTHANDLE hStdErr;
639 PRTHANDLE phStdErr;
640 RTPIPE hStdErrR;
641 rc = VBoxServiceControlExecSetupPipe(2 /* stderr */, &hStdErr, &phStdErr, &hStdErrR);
642 if (RT_SUCCESS(rc))
643 {
644 /*
645 * Create a poll set for the pipes and let the
646 * transport layer add stuff to it as well.
647 */
648 RTPOLLSET hPollSet;
649 rc = RTPollSetCreate(&hPollSet);
650 if (RT_SUCCESS(rc))
651 {
652 rc = RTPollSetAddPipe(hPollSet, hStdInW, RTPOLL_EVT_ERROR, 0 /* TXSEXECHNDID_STDIN */);
653 if (RT_SUCCESS(rc))
654 rc = RTPollSetAddPipe(hPollSet, hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 1 /* TXSEXECHNDID_STDOUT */);
655 if (RT_SUCCESS(rc))
656 rc = RTPollSetAddPipe(hPollSet, hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 2 /* TXSEXECHNDID_TESTPIPE */);
657 if (RT_SUCCESS(rc))
658 {
659 RTPROCESS hProcess;
660 rc = RTProcCreateEx(pData->pszCmd, pData->papszArgs, hEnv, pData->uFlags,
661 phStdIn, phStdOut, phStdErr,
662 /*pszUsername, pszPassword,*/ NULL, NULL,
663 &hProcess);
664 if (RT_SUCCESS(rc))
665 {
666 VBoxServiceVerbose(3, "Control: Process \"%s\" started.\n", pData->pszCmd);
667 rc = VbglR3GuestCtrlExecReportStatus(u32ClientID, 123, PROC_STATUS_STARTED,
668 0 /* u32Flags */, NULL, 0);
669 /** @todo Dump a bit more info here. */
670
671 /*
672 * Close the child ends of any pipes and redirected files.
673 */
674 int rc2 = RTHandleClose(phStdIn); AssertRC(rc2);
675 phStdIn = NULL;
676 rc2 = RTHandleClose(phStdOut); AssertRC(rc2);
677 phStdOut = NULL;
678 rc2 = RTHandleClose(phStdErr); AssertRC(rc2);
679 phStdErr = NULL;
680
681 rc = VBoxServiceControlExecProcLoop(hProcess, pData->uTimeLimitMS, hPollSet,
682 hStdInW, hStdOutR, hStdErrR);
683 /*
684 * The handles that are no longer in the set have
685 * been closed by the above call in order to prevent
686 * the guest from getting stuck accessing them.
687 * So, NIL the handles to avoid closing them again.
688 */
689 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 0 /* stdin */, NULL)))
690 hStdInW = NIL_RTPIPE;
691 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 1 /* stdout */, NULL)))
692 hStdOutR = NIL_RTPIPE;
693 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 2 /* stderr */, NULL)))
694 hStdErrR = NIL_RTPIPE;
695 }
696 }
697 }
698 RTPipeClose(hStdErrR);
699 RTHandleClose(phStdErr);
700 }
701 RTPipeClose(hStdOutR);
702 RTHandleClose(phStdOut);
703 }
704 RTPipeClose(hStdInW);
705 RTHandleClose(phStdIn);
706 }
707 }
708 RTEnvDestroy(hEnv);
709 }
710
711 VbglR3GuestCtrlDisconnect(u32ClientID);
712 VBoxServiceVerbose(3, "Control: Thread of process \"%s\" ended with rc=%Rrc.\n", pData->pszCmd, rc);
713
714 /*
715 * Since we (hopefully) are the one ones that hold the thread data,
716 * destroy them now.
717 */
718 VBoxServiceControlExecFreeThreadData(pData);
719 return rc;
720}
721
722static DECLCALLBACK(int) VBoxServiceControlExecThread(RTTHREAD ThreadSelf, void *pvUser)
723{
724 PVBOXSERVICECTRLTHREADDATA pData = (PVBOXSERVICECTRLTHREADDATA)pvUser;
725 return VBoxServiceControlExecProcessWorker(pData);
726}
727
728int VBoxServiceControlExecProcess(const char *pszCmd, uint32_t uFlags,
729 const char *pszArgs, uint32_t uNumArgs,
730 const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
731 const char *pszStdIn, const char *pszStdOut, const char *pszStdErr,
732 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS)
733{
734 PVBOXSERVICECTRLTHREADDATA pThreadData =
735 VBoxServiceControlExecAllocateThreadData(pszCmd, uFlags,
736 pszArgs, uNumArgs,
737 pszEnv, cbEnv, uNumEnvVars,
738 pszStdIn, pszStdOut, pszStdErr,
739 pszUser, pszPassword,
740 uTimeLimitMS);
741 int rc = VINF_SUCCESS;
742 if (pThreadData)
743 {
744 rc = RTThreadCreate(NULL, VBoxServiceControlExecThread,
745 (void *)(PVBOXSERVICECTRLTHREADDATA)pThreadData, 0,
746 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "Exec");
747 if (RT_FAILURE(rc))
748 {
749 VBoxServiceError("Control: RTThreadCreate failed, rc=%Rrc\n, threadData=%p",
750 rc, pThreadData);
751 VBoxServiceControlExecFreeThreadData(pThreadData);
752 }
753 }
754 else
755 rc = VERR_NO_MEMORY;
756 return rc;
757}
758
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