VirtualBox

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

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

Guest Control: Update (VBoxService).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.2 KB
Line 
1
2/* $Id: VBoxServiceControlExec.cpp 27926 2010-04-01 08:58:39Z 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
501int VBoxServiceControlExecProcess(PVBOXSERVICECTRLPROCDATA pExecData,
502 const char * const *papszArgs,
503 const char * const *papszEnv)
504{
505 AssertPtr(pExecData);
506
507 /*
508 * Create the environment.
509 */
510 RTENV hEnv;
511 int rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
512 if (RT_SUCCESS(rc))
513 {
514 size_t i;
515 for (i = 0; i < pExecData->uNumEnvVars; i++)
516 {
517 // @todo rc = RTEnvPutEx(hEnv, pExecData->[i]);
518 if (RT_FAILURE(rc))
519 break;
520 }
521 if (RT_SUCCESS(rc))
522 {
523 /*
524 * Setup the redirection of the standard stuff.
525 */
526 /** @todo consider supporting: gcc stuff.c >file 2>&1. */
527 RTHANDLE hStdIn;
528 PRTHANDLE phStdIn;
529 RTPIPE hStdInW;
530 rc = VBoxServiceControlExecSetupPipe(0 /* stdin */, &hStdIn, &phStdIn, &hStdInW);
531 if (RT_SUCCESS(rc))
532 {
533 RTHANDLE hStdOut;
534 PRTHANDLE phStdOut;
535 RTPIPE hStdOutR;
536 rc = VBoxServiceControlExecSetupPipe(1 /* stdout */, &hStdOut, &phStdOut, &hStdOutR);
537 if (RT_SUCCESS(rc))
538 {
539 RTHANDLE hStdErr;
540 PRTHANDLE phStdErr;
541 RTPIPE hStdErrR;
542 rc = VBoxServiceControlExecSetupPipe(2 /* stderr */, &hStdErr, &phStdErr, &hStdErrR);
543 if (RT_SUCCESS(rc))
544 {
545 /*
546 * Create a poll set for the pipes and let the
547 * transport layer add stuff to it as well.
548 */
549 RTPOLLSET hPollSet;
550 rc = RTPollSetCreate(&hPollSet);
551 if (RT_SUCCESS(rc))
552 {
553 rc = RTPollSetAddPipe(hPollSet, hStdInW, RTPOLL_EVT_ERROR, 0 /* TXSEXECHNDID_STDIN */);
554 if (RT_SUCCESS(rc))
555 rc = RTPollSetAddPipe(hPollSet, hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 1 /* TXSEXECHNDID_STDOUT */);
556 if (RT_SUCCESS(rc))
557 rc = RTPollSetAddPipe(hPollSet, hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 2 /* TXSEXECHNDID_TESTPIPE */);
558 if (RT_SUCCESS(rc))
559 {
560 RTPROCESS hProcess;
561 rc = RTProcCreateEx(pExecData->szCmd, papszArgs, hEnv, 0 /*fFlags*/,
562 phStdIn, phStdOut, phStdErr,
563 /*pszUsername, pszPassword,*/ NULL, NULL,
564 &hProcess);
565 if (RT_SUCCESS(rc))
566 {
567 /*
568 * Close the child ends of any pipes and redirected files.
569 */
570 int rc2 = RTHandleClose(phStdIn); AssertRC(rc2);
571 phStdIn = NULL;
572 rc2 = RTHandleClose(phStdOut); AssertRC(rc2);
573 phStdOut = NULL;
574 rc2 = RTHandleClose(phStdErr); AssertRC(rc2);
575 phStdErr = NULL;
576
577 rc = VBoxServiceControlExecProcLoop(hProcess, pExecData->uTimeLimitMS, hPollSet,
578 hStdInW, hStdOutR, hStdErrR);
579 /*
580 * The handles that are no longer in the set have
581 * been closed by the above call in order to prevent
582 * the guest from getting stuck accessing them.
583 * So, NIL the handles to avoid closing them again.
584 */
585 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 0 /* stdin */, NULL)))
586 hStdInW = NIL_RTPIPE;
587 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 1 /* stdout */, NULL)))
588 hStdOutR = NIL_RTPIPE;
589 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 2 /* stderr */, NULL)))
590 hStdErrR = NIL_RTPIPE;
591 }
592 }
593 }
594 RTPipeClose(hStdErrR);
595 RTHandleClose(phStdErr);
596 }
597 RTPipeClose(hStdOutR);
598 RTHandleClose(phStdOut);
599 }
600 RTPipeClose(hStdInW);
601 RTHandleClose(phStdIn);
602 }
603 }
604 RTEnvDestroy(hEnv);
605 }
606 return rc;
607}
608
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