VirtualBox

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

Last change on this file since 31952 was 31952, checked in by vboxsync, 14 years ago

Additions/VBoxService: sanity check

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