VirtualBox

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

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

Guest Control: Build fix, memory cleanup.

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