VirtualBox

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

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

Guest Control/Main/VBoxService: Update on locking, bugfixes.

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