VirtualBox

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

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

VBoxService/Guest Execution: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 57.4 KB
Line 
1/* $Id: VBoxServiceControlExec.cpp 33247 2010-10-20 10:01:48Z 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/**
51 * Handle an error event on standard input.
52 *
53 * @returns IPRT status code.
54 * @param hPollSet The polling set.
55 * @param fPollEvt The event mask returned by RTPollNoResume.
56 * @param phStdInW The standard input pipe handle.
57 * @param pStdInBuf The standard input buffer.
58 */
59static int VBoxServiceControlExecProcHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
60 PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf)
61{
62 int rc = RTCritSectEnter(&pStdInBuf->CritSect);
63 if (RT_SUCCESS(rc))
64 {
65 int rc2;
66 /* If there still was buffered data to write, remove the writable pipe as well. */
67 if (pStdInBuf->cbOffset < pStdInBuf->cbSize)
68 {
69 rc2 = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
70 AssertRC(rc2);
71 }
72
73 rc2 = RTPipeClose(*phStdInW);
74 AssertRC(rc2);
75 *phStdInW = NIL_RTPIPE;
76
77 /* Mark the stdin buffer as dead; we're not using it anymore. */
78 pStdInBuf->fAlive = false;
79
80 rc2 = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_ERROR);
81 AssertRC(rc2);
82
83 rc2 = RTCritSectLeave(&pStdInBuf->CritSect);
84 if (RT_SUCCESS(rc))
85 rc = rc2;
86 }
87 return rc;
88}
89
90
91/**
92 * Try write some more data to the standard input of the child.
93 *
94 * @returns IPRT status code.
95 * @retval VINF_TRY_AGAIN if there is still data left in the buffer.
96 *
97 * @param pStdInBuf The standard input buffer.
98 * @param hStdInW The standard input pipe.
99 * @param pfClose Pointer to a flag whether the pipe needs to be closed afterwards.
100 */
101static int VBoxServiceControlExecProcWriteStdIn(PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf, RTPIPE hStdInW,
102 size_t *pcbWritten, bool *pfClose)
103{
104 AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
105 AssertPtrReturn(pfClose, VERR_INVALID_PARAMETER);
106
107 int rc = RTCritSectEnter(&pStdInBuf->CritSect);
108 if (RT_SUCCESS(rc))
109 {
110 size_t cbToWrite = pStdInBuf->cbSize - pStdInBuf->cbOffset;
111 cbToWrite = RT_MIN(cbToWrite, _64K);
112 *pfClose = false;
113 if (cbToWrite && pStdInBuf->fAlive)
114 {
115 rc = RTPipeWrite(hStdInW, &pStdInBuf->pbData[pStdInBuf->cbOffset], cbToWrite, pcbWritten);
116 if (RT_SUCCESS(rc))
117 {
118 pStdInBuf->fNeedNotification = true;
119 if (rc == VINF_TRY_AGAIN)
120 {
121 //if (pStdInBuf->fNeedNotification)
122 }
123 else
124 {
125 pStdInBuf->cbOffset += *pcbWritten;
126 }
127
128 /* Did somebody tell us that we should come to an end,
129 * e.g. no more data coming in? */
130 if (pStdInBuf->fPendingClose)
131 {
132 /* When we wrote out all data in the buffer we
133 * can finally shutdown. */
134 if (pStdInBuf->cbSize == pStdInBuf->cbOffset)
135 {
136 *pfClose = true;
137 }
138 else if (pStdInBuf->fNeedNotification)
139 {
140 /* Still data to push out - so we need another
141 * poll round! Write something into the notification pipe. */
142 size_t cbWrittenIgnore;
143 int rc2 = RTPipeWrite(pStdInBuf->hNotificationPipeW, "i", 1, &cbWrittenIgnore);
144
145 /* Disable notification until it is set again on successful write. */
146 pStdInBuf->fNeedNotification = !RT_SUCCESS(rc2);
147 }
148 }
149 }
150 else
151 {
152 *pcbWritten = 0;
153 pStdInBuf->fAlive = pStdInBuf->fAlive;
154 }
155#ifdef DEBUG
156 VBoxServiceVerbose(1, "ControlExec: Written StdIn: cbOffset=%u, pcbWritten=%u, rc=%Rrc, cbAlloc=%u, cbSize=%u\n",
157 pStdInBuf->cbOffset, *pcbWritten, rc,
158 pStdInBuf->cbAllocated, pStdInBuf->cbSize);
159#endif
160 }
161 else
162 {
163 *pcbWritten = 0;
164 pStdInBuf->fNeedNotification = pStdInBuf->fAlive;
165 //rc = VERR_BAD_PIPE;
166 }
167 int rc2 = RTCritSectLeave(&pStdInBuf->CritSect);
168 if (RT_SUCCESS(rc))
169 rc = rc2;
170 }
171 return rc;
172}
173
174
175/**
176 * Handle an event indicating we can write to the standard input pipe of the
177 * child process.
178 *
179 * @returns IPRT status code.
180 * @param hPollSet The polling set.
181 * @param fPollEvt The event mask returned by RTPollNoResume.
182 * @param phStdInW The standard input pipe.
183 * @param pStdInBuf The standard input buffer.
184 * @param pcbWritten Where to return the number of bytes written.
185 */
186static int VBoxServiceControlExecProcHandleStdInWritableEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
187 PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf, size_t *pcbWritten)
188{
189 AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
190 int rc;
191 if (!(fPollEvt & RTPOLL_EVT_ERROR))
192 {
193 bool fClose;
194 rc = VBoxServiceControlExecProcWriteStdIn(pStdInBuf, *phStdInW, pcbWritten, &fClose);
195 if (rc == VINF_TRY_AGAIN)
196 rc = VINF_SUCCESS;
197 if (RT_FAILURE(rc))
198 {
199 if (rc == VERR_BAD_PIPE)
200 {
201 rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
202 AssertRC(rc);
203 }
204 else
205 {
206 /** @todo Do we need to do something about this error condition? */
207 AssertRC(rc);
208 }
209 }
210 else if (fClose)
211 {
212 /* If the pipe needs to be closed, do so. */
213 rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
214 }
215 }
216 else
217 {
218 *pcbWritten = 0;
219 rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
220 }
221 return rc;
222}
223
224
225/**
226 * Handle a transport event or successful pfnPollIn() call.
227 *
228 * @returns IPRT status code from client send.
229 * @retval VINF_EOF indicates ABORT command.
230 *
231 * @param hPollSet The polling set.
232 * @param fPollEvt The event mask returned by RTPollNoResume.
233 * @param idPollHnd The handle ID.
234 * @param hStdInW The standard input pipe.
235 * @param pStdInBuf The standard input buffer.
236 */
237static int VBoxServiceControlExecProcHandleTransportEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, uint32_t idPollHnd,
238 PRTPIPE phStdInW, PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf)
239{
240 return 0; //RTPollSetAddPipe(hPollSet, *phStdInW, RTPOLL_EVT_WRITE, 4 /*TXSEXECHNDID_STDIN_WRITABLE*/);
241}
242
243
244/**
245 * Handle pending output data or error on standard out, standard error or the
246 * test pipe.
247 *
248 * @returns IPRT status code from client send.
249 * @param pThread The thread specific data.
250 * @param hPollSet The polling set.
251 * @param fPollEvt The event mask returned by RTPollNoResume.
252 * @param phPipeR The pipe handle.
253 * @param pu32Crc The current CRC-32 of the stream. (In/Out)
254 * @param uHandleId The handle ID.
255 *
256 * @todo Put the last 4 parameters into a struct!
257 */
258static int VBoxServiceControlExecProcHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phPipeR,
259 uint32_t uHandleId, PVBOXSERVICECTRLEXECPIPEBUF pStdOutBuf)
260{
261#ifdef DEBUG
262 VBoxServiceVerbose(4, "ControlExec: HandleOutputEvent: fPollEvt=%#x\n", fPollEvt);
263#endif
264
265 /*
266 * Try drain the pipe before acting on any errors.
267 */
268 int rc = VINF_SUCCESS;
269 size_t cbRead;
270 uint8_t abBuf[_64K];
271
272 int rc2 = RTPipeRead(*phPipeR, abBuf, sizeof(abBuf), &cbRead);
273 if (RT_SUCCESS(rc2) && cbRead)
274 {
275#if 0
276 /* Only used for "real-time" stdout/stderr data; gets sent immediately (later)! */
277 rc = VbglR3GuestCtrlExecSendOut(pThread->uClientID, pThread->uContextID,
278 pData->uPID, uHandleId, 0 /* u32Flags */,
279 abBuf, cbRead);
280 if (RT_FAILURE(rc))
281 {
282 VBoxServiceError("ControlExec: Error while sending real-time output data, rc=%Rrc, cbRead=%u, CID=%u, PID=%u\n",
283 rc, cbRead, pThread->uClientID, pData->uPID);
284 }
285 else
286 {
287#endif
288 uint32_t cbWritten;
289 rc = VBoxServiceControlExecWritePipeBuffer(pStdOutBuf, abBuf,
290 cbRead, false /* Pending close */, &cbWritten);
291 if (RT_SUCCESS(rc))
292 {
293 Assert(cbRead == cbWritten);
294 /* Make sure we go another poll round in case there was too much data
295 for the buffer to hold. */
296 fPollEvt &= RTPOLL_EVT_ERROR;
297 }
298#if 0
299 }
300#endif
301 }
302 else if (RT_FAILURE(rc2))
303 {
304 fPollEvt |= RTPOLL_EVT_ERROR;
305 AssertMsg(rc2 == VERR_BROKEN_PIPE, ("%Rrc\n", rc));
306 }
307
308 /*
309 * If an error was signalled, close reading stdout/stderr pipe.
310 */
311 if (fPollEvt & RTPOLL_EVT_ERROR)
312 {
313 rc2 = RTPollSetRemove(hPollSet, uHandleId);
314 AssertRC(rc2);
315
316 rc2 = RTPipeClose(*phPipeR);
317 AssertRC(rc2);
318 *phPipeR = NIL_RTPIPE;
319 }
320 return rc;
321}
322
323
324/**
325 * TODO
326 *
327 * @return IPRT status code.
328 * @param pThread
329 * @param hProcess
330 * @param cMillies
331 * @param hPollSet
332 * @param hStdInW
333 * @param hStdOutR
334 * @param hStdErrR
335 */
336static int VBoxServiceControlExecProcLoop(PVBOXSERVICECTRLTHREAD pThread,
337 RTPROCESS hProcess, RTMSINTERVAL cMillies, RTPOLLSET hPollSet,
338 PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR)
339{
340 AssertPtrReturn(phStdInW, VERR_INVALID_PARAMETER);
341 AssertPtrReturn(phStdOutR, VERR_INVALID_PARAMETER);
342 AssertPtrReturn(phStdErrR, VERR_INVALID_PARAMETER);
343
344 int rc;
345 int rc2;
346 uint64_t const MsStart = RTTimeMilliTS();
347 RTPROCSTATUS ProcessStatus = { 254, RTPROCEXITREASON_ABEND };
348 bool fProcessAlive = true;
349 bool fProcessTimedOut = false;
350 uint64_t MsProcessKilled = UINT64_MAX;
351 RTMSINTERVAL const cMsPollBase = *phStdInW != NIL_RTPIPE
352 ? 100 /* Need to poll for input. */
353 : 1000; /* Need only poll for process exit and aborts. */
354 RTMSINTERVAL cMsPollCur = 0;
355
356 AssertPtr(pThread);
357 Assert(pThread->enmType == kVBoxServiceCtrlThreadDataExec);
358 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
359 AssertPtr(pData);
360
361 /* Assign PID to thread data. */
362 pData->uPID = hProcess;
363
364 /*
365 * Before entering the loop, tell the host that we've started the guest
366 * and that it's now OK to send input to the process.
367 */
368 VBoxServiceVerbose(3, "ControlExec: Process started: PID=%u, CID=%u, User=%s, PW=%s\n",
369 pData->uPID, pThread->uContextID, pData->pszUser, pData->pszPassword);
370 rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
371 pData->uPID, PROC_STS_STARTED, 0 /* u32Flags */,
372 NULL /* pvData */, 0 /* cbData */);
373
374 /*
375 * Process input, output, the test pipe and client requests.
376 */
377 while ( RT_SUCCESS(rc)
378 && RT_UNLIKELY(!pThread->fShutdown))
379 {
380 /*
381 * Wait/Process all pending events.
382 */
383 uint32_t idPollHnd;
384 uint32_t fPollEvt;
385 rc2 = RTPollNoResume(hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
386 if (pThread->fShutdown)
387 continue;
388
389 cMsPollCur = 0; /* No rest until we've checked everything. */
390
391 if (RT_SUCCESS(rc2))
392 {
393#ifdef DEBUG
394 VBoxServiceVerbose(4, "ControlExec: RTPollNoResume idPollHnd=%u\n", idPollHnd);
395#endif
396 switch (idPollHnd)
397 {
398 case VBOXSERVICECTRLPIPEID_STDIN_ERROR:
399 rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, &pData->stdIn);
400 break;
401
402 case VBOXSERVICECTRLPIPEID_STDIN_INPUT_NOTIFY:
403 {
404 /* Drain the notification pipe. */
405 uint8_t abBuf[8];
406 size_t cbIgnore;
407 RTPipeRead(pData->stdIn.hNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
408 }
409 /* Fall through. */
410 case VBOXSERVICECTRLPIPEID_STDIN_WRITABLE:
411 {
412 size_t cbWritten;
413 rc = VBoxServiceControlExecProcHandleStdInWritableEvent(hPollSet, fPollEvt, phStdInW,
414 &pData->stdIn, &cbWritten);
415 break;
416 }
417
418 case VBOXSERVICECTRLPIPEID_STDOUT:
419 rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt, phStdOutR,
420 VBOXSERVICECTRLPIPEID_STDOUT, &pData->stdOut);
421 break;
422
423 case VBOXSERVICECTRLPIPEID_STDERR:
424 rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt, phStdErrR,
425 VBOXSERVICECTRLPIPEID_STDERR, &pData->stdOut);
426 break;
427
428 default:
429 AssertMsgFailed(("idPollHnd=%u fPollEvt=%#x\n", idPollHnd, fPollEvt));
430 break;
431 }
432 if (RT_FAILURE(rc) || rc == VINF_EOF)
433 break; /* Abort command, or client dead or something. */
434 continue;
435 }
436
437 /*
438 * Check for process death.
439 */
440 if (fProcessAlive)
441 {
442 rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
443 if (RT_SUCCESS_NP(rc2))
444 {
445 fProcessAlive = false;
446 continue;
447 }
448 if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
449 continue;
450 if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
451 {
452 fProcessAlive = false;
453 ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
454 ProcessStatus.iStatus = 255;
455 AssertFailed();
456 }
457 else
458 AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
459 }
460
461 /*
462 * If the process has terminated, we're should head out.
463 */
464 if (!fProcessAlive)
465 break;
466
467 /*
468 * Check for timed out, killing the process.
469 */
470 uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
471 if (cMillies != RT_INDEFINITE_WAIT)
472 {
473 uint64_t u64Now = RTTimeMilliTS();
474 uint64_t cMsElapsed = u64Now - MsStart;
475 if (cMsElapsed >= cMillies)
476 {
477 VBoxServiceVerbose(3, "ControlExec: Process timed out (%ums elapsed > %ums timeout), killing ...", cMsElapsed, cMillies);
478
479 fProcessTimedOut = true;
480 if ( MsProcessKilled == UINT64_MAX
481 || u64Now - MsProcessKilled > 1000)
482 {
483 if (u64Now - MsProcessKilled > 20*60*1000)
484 break; /* give up after 20 mins */
485 RTProcTerminate(hProcess);
486 MsProcessKilled = u64Now;
487 continue;
488 }
489 cMilliesLeft = 10000;
490 }
491 else
492 cMilliesLeft = cMillies - (uint32_t)cMsElapsed;
493 }
494
495 /* Reset the polling interval since we've done all pending work. */
496 cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft;
497
498 /*
499 * Need to exit?
500 */
501 if (pThread->fShutdown)
502 break;
503 }
504
505 /*
506 * Try kill the process if it's still alive at this point.
507 */
508 if (fProcessAlive)
509 {
510 if (MsProcessKilled == UINT64_MAX)
511 {
512 VBoxServiceVerbose(3, "ControlExec: Process (PID=%u) is still alive and not killed yet\n",
513 pData->uPID);
514
515 MsProcessKilled = RTTimeMilliTS();
516 RTProcTerminate(hProcess);
517 RTThreadSleep(500);
518 }
519
520 for (size_t i = 0; i < 10; i++)
521 {
522 VBoxServiceVerbose(4, "ControlExec: Kill attempt %d/10: Waiting for process (PID=%u) exit ...\n",
523 i + 1, pData->uPID);
524 rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
525 if (RT_SUCCESS(rc2))
526 {
527 VBoxServiceVerbose(4, "ControlExec: Kill attempt %d/10: Process (PID=%u) exited\n",
528 i + 1, pData->uPID);
529 fProcessAlive = false;
530 break;
531 }
532 if (i >= 5)
533 {
534 VBoxServiceVerbose(4, "ControlExec: Kill attempt %d/10: Try to terminate (PID=%u) ...\n",
535 i + 1, pData->uPID);
536 RTProcTerminate(hProcess);
537 }
538 RTThreadSleep(i >= 5 ? 2000 : 500);
539 }
540
541 if (fProcessAlive)
542 VBoxServiceVerbose(3, "ControlExec: Process (PID=%u) could not be killed\n", pData->uPID);
543 }
544
545 /*
546 * If we don't have a client problem (RT_FAILURE(rc) we'll reply to the
547 * clients exec packet now.
548 */
549 if (RT_SUCCESS(rc))
550 {
551 /** WRONG CONTEXT ID!!!! */
552#if 0
553 rc = VbglR3GuestCtrlExecReportStatusIn(pThread->uClientID, pThread->uContextID, pData->uPID,
554 INPUT_STS_TERMINATED, 0 /* Flags */, 0);
555
556 rc = VbglR3GuestCtrlExecSendOut(pThread->uClientID, pThread->uContextID, pData->uPID,
557 0 /* Handle ID */, 0 /* Flags */, NULL, 0);
558#endif
559
560 uint32_t uStatus = PROC_STS_UNDEFINED;
561 uint32_t uFlags = 0;
562
563 if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX)
564 {
565 VBoxServiceVerbose(3, "ControlExec: Process timed out and got killed\n");
566 uStatus = PROC_STS_TOK;
567 }
568 else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX)
569 {
570 VBoxServiceVerbose(3, "ControlExec: Process timed out and did *not* get killed\n");
571 uStatus = PROC_STS_TOA;
572 }
573 else if (pThread->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
574 {
575 VBoxServiceVerbose(3, "ControlExec: Process got terminated because system/service is about to shutdown\n");
576 uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */
577 uFlags = pData->uFlags; /* Return handed-in execution flags back to the host. */
578 }
579 else if (fProcessAlive)
580 {
581 VBoxServiceError("ControlExec: Process is alive when it should not!\n");
582 }
583 else if (MsProcessKilled != UINT64_MAX)
584 {
585 VBoxServiceError("ControlExec: Process has been killed when it should not!\n");
586 }
587 else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
588 {
589 VBoxServiceVerbose(3, "ControlExec: Process ended with RTPROCEXITREASON_NORMAL\n");
590
591 uStatus = PROC_STS_TEN;
592 uFlags = ProcessStatus.iStatus;
593 }
594 else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
595 {
596 VBoxServiceVerbose(3, "ControlExec: Process ended with RTPROCEXITREASON_SIGNAL\n");
597
598 uStatus = PROC_STS_TES;
599 uFlags = ProcessStatus.iStatus;
600 }
601 else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
602 {
603 VBoxServiceVerbose(3, "ControlExec: Process ended with RTPROCEXITREASON_ABEND\n");
604
605 uStatus = PROC_STS_TEA;
606 uFlags = ProcessStatus.iStatus;
607 }
608 else
609 {
610 VBoxServiceError("ControlExec: Process has reached an undefined status!\n");
611 }
612
613 VBoxServiceVerbose(3, "ControlExec: Process ended: PID=%u, CID=%u, Status=%u, Flags=%u\n",
614 pData->uPID, pThread->uContextID, uStatus, uFlags);
615 rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
616 pData->uPID, uStatus, uFlags,
617 NULL /* pvData */, 0 /* cbData */);
618 VBoxServiceVerbose(3, "ControlExec: Process loop ended with rc=%Rrc\n", rc);
619 }
620 else
621 VBoxServiceError("ControlExec: Process loop failed with rc=%Rrc\n", rc);
622 return rc;
623}
624
625
626/**
627 * Sets up the redirection / pipe / nothing for one of the standard handles.
628 *
629 * @returns IPRT status code. No client replies made.
630 * @param fd Which standard handle it is (0 == stdin, 1 ==
631 * stdout, 2 == stderr).
632 * @param ph The generic handle that @a pph may be set
633 * pointing to. Always set.
634 * @param pph Pointer to the RTProcCreateExec argument.
635 * Always set.
636 * @param phPipe Where to return the end of the pipe that we
637 * should service. Always set.
638 */
639static int VBoxServiceControlExecSetupPipe(int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
640{
641 AssertPtr(ph);
642 AssertPtr(pph);
643 AssertPtr(phPipe);
644
645 ph->enmType = RTHANDLETYPE_PIPE;
646 ph->u.hPipe = NIL_RTPIPE;
647 *pph = NULL;
648 *phPipe = NIL_RTPIPE;
649
650 int rc;
651
652 /*
653 * Setup a pipe for forwarding to/from the client.
654 * The ph union struct will be filled with a pipe read/write handle
655 * to represent the "other" end to phPipe.
656 */
657 if (fd == 0) /* stdin? */
658 {
659 /* Connect a wrtie pipe specified by phPipe to stdin. */
660 rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
661 }
662 else /* stdout or stderr? */
663 {
664 /* Connect a read pipe specified by phPipe to stdout or stderr. */
665 rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
666 }
667 if (RT_FAILURE(rc))
668 return rc;
669 ph->enmType = RTHANDLETYPE_PIPE;
670 *pph = ph;
671
672 return rc;
673}
674
675
676/**
677 * Initializes a pipe buffer.
678 *
679 * @returns IPRT status code.
680 * @param pBuf The pipe buffer to initialize.
681 * @param fNeedNotificationPipe Whether the buffer needs a notification
682 * pipe or not.
683 */
684static int VBoxServiceControlExecInitPipeBuffer(PVBOXSERVICECTRLEXECPIPEBUF pBuf, bool fNeedNotificationPipe)
685{
686 AssertPtr(pBuf);
687
688 /** @todo Add allocation size as function parameter! */
689 pBuf->pbData = (uint8_t *)RTMemAlloc(_64K); /* Start with a 64k buffer. */
690 AssertReturn(pBuf->pbData, VERR_NO_MEMORY);
691 pBuf->cbAllocated = _64K;
692 pBuf->cbSize = 0;
693 pBuf->cbOffset = 0;
694 pBuf->fAlive = true;
695 pBuf->fPendingClose = false;
696 pBuf->fNeedNotification = fNeedNotificationPipe;
697 pBuf->hNotificationPipeW = NIL_RTPIPE;
698 pBuf->hNotificationPipeR = NIL_RTPIPE;
699
700 int rc = RTCritSectInit(&pBuf->CritSect);
701 if (RT_SUCCESS(rc) && fNeedNotificationPipe)
702 {
703 rc = RTPipeCreate(&pBuf->hNotificationPipeR, &pBuf->hNotificationPipeW, 0);
704 if (RT_FAILURE(rc))
705 RTCritSectDelete(&pBuf->CritSect);
706 }
707 return rc;
708}
709
710
711/**
712 * Deletes a pipe buffer.
713 *
714 * @param pBuf The pipe buffer.
715 */
716static void VBoxServiceControlExecDeletePipeBuffer(PVBOXSERVICECTRLEXECPIPEBUF pBuf)
717{
718 AssertPtr(pBuf);
719 RTMemFree(pBuf->pbData);
720 pBuf->pbData = NULL;
721 pBuf->cbAllocated = 0;
722 pBuf->cbSize = 0;
723 pBuf->cbOffset = 0;
724 pBuf->fAlive = false;
725
726 RTPipeClose(pBuf->hNotificationPipeR);
727 pBuf->hNotificationPipeR = NIL_RTPIPE;
728 RTPipeClose(pBuf->hNotificationPipeW);
729 pBuf->hNotificationPipeW = NIL_RTPIPE;
730 RTCritSectDelete(&pBuf->CritSect);
731}
732
733
734/**
735 * TODO
736 *
737 * @return IPRT status code.
738 * @param pBuf
739 * @param pbBuffer
740 * @param cbBuffer
741 * @param pcbToRead
742 */
743int VBoxServiceControlExecReadPipeBufferContent(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
744 uint8_t *pbBuffer, uint32_t cbBuffer, uint32_t *pcbToRead)
745{
746 AssertPtr(pBuf);
747 AssertPtr(pcbToRead);
748
749 int rc = RTCritSectEnter(&pBuf->CritSect);
750 if (RT_SUCCESS(rc))
751 {
752 Assert(pBuf->cbSize >= pBuf->cbOffset);
753 if (*pcbToRead > pBuf->cbSize - pBuf->cbOffset)
754 *pcbToRead = pBuf->cbSize - pBuf->cbOffset;
755
756 if (*pcbToRead > cbBuffer)
757 *pcbToRead = cbBuffer;
758
759 if (*pcbToRead > 0)
760 {
761 memcpy(pbBuffer, pBuf->pbData + pBuf->cbOffset, *pcbToRead);
762 pBuf->cbOffset += *pcbToRead;
763 }
764 else
765 {
766 pbBuffer = NULL;
767 *pcbToRead = 0;
768 }
769 rc = RTCritSectLeave(&pBuf->CritSect);
770 }
771 return rc;
772}
773
774
775/**
776 * TODO
777 *
778 * @return IPRT status code.
779 * @param pBuf
780 * @param pbData
781 * @param cbData
782 * @param fPendingClose
783 * @param pcbWritten
784 */
785int VBoxServiceControlExecWritePipeBuffer(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
786 uint8_t *pbData, uint32_t cbData, bool fPendingClose,
787 uint32_t *pcbWritten)
788{
789 AssertPtrReturn(pBuf, VERR_INVALID_PARAMETER);
790 AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
791
792 int rc = RTCritSectEnter(&pBuf->CritSect);
793 if (RT_SUCCESS(rc))
794 {
795 /* Rewind the buffer if it's empty. */
796 size_t cbInBuf = pBuf->cbSize - pBuf->cbOffset;
797 bool const fAddToSet = cbInBuf == 0;
798 if (fAddToSet)
799 pBuf->cbSize = pBuf->cbOffset = 0;
800
801 /* Try and see if we can simply append the data. */
802 if (cbData + pBuf->cbSize <= pBuf->cbAllocated)
803 {
804 memcpy(&pBuf->pbData[pBuf->cbSize], pbData, cbData);
805 pBuf->cbSize += cbData;
806 }
807 else
808 {
809 /* Move any buffered data to the front. */
810 cbInBuf = pBuf->cbSize - pBuf->cbOffset;
811 if (cbInBuf == 0)
812 pBuf->cbSize = pBuf->cbOffset = 0;
813 else if (pBuf->cbOffset) /* Do we have something to move? */
814 {
815 memmove(pBuf->pbData, &pBuf->pbData[pBuf->cbOffset], cbInBuf);
816 pBuf->cbSize = cbInBuf;
817 pBuf->cbOffset = 0;
818 }
819
820 /* Do we need to grow the buffer? */
821 if (cbData + pBuf->cbSize > pBuf->cbAllocated)
822 {
823 size_t cbAlloc = pBuf->cbSize + cbData;
824 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
825 void *pvNew = RTMemRealloc(pBuf->pbData, cbAlloc);
826 if (pvNew)
827 {
828 pBuf->pbData = (uint8_t *)pvNew;
829 pBuf->cbAllocated = cbAlloc;
830 }
831 else
832 rc = VERR_NO_MEMORY;
833 }
834
835 /* Finally, copy the data. */
836 if (RT_SUCCESS(rc))
837 {
838 if (cbData + pBuf->cbSize <= pBuf->cbAllocated)
839 {
840 memcpy(&pBuf->pbData[pBuf->cbSize], pbData, cbData);
841 pBuf->cbSize += cbData;
842 }
843 else
844 rc = VERR_BUFFER_OVERFLOW;
845 }
846 }
847
848 if (RT_SUCCESS(rc))
849 {
850 /* Report back written bytes. */
851 *pcbWritten = cbData;
852
853 /*
854 * Was this the final read/write to do on this buffer? The close it
855 * next time we have the chance to.
856 */
857 if (fPendingClose)
858 pBuf->fPendingClose = fPendingClose;
859
860 /*
861 * Wake up the thread servicing the process so it can feed it
862 * (if we have a notification helper pipe).
863 */
864 if (pBuf->fNeedNotification)
865 {
866 size_t cbWritten;
867 int rc2 = RTPipeWrite(pBuf->hNotificationPipeW, "i", 1, &cbWritten);
868
869 /* Disable notification until it is set again on successful write. */
870 pBuf->fNeedNotification = !RT_SUCCESS(rc2);
871 }
872 }
873 int rc2 = RTCritSectLeave(&pBuf->CritSect);
874 if (RT_SUCCESS(rc))
875 rc = rc2;
876 }
877 return rc;
878}
879
880
881/**
882 * Allocates and gives back a thread data struct which then can be used by the worker thread.
883 * Needs to be freed with VBoxServiceControlExecDestroyThreadData().
884 *
885 * @return IPRT status code.
886 * @param pThread
887 * @param u32ContextID
888 * @param pszCmd
889 * @param uFlags
890 * @param pszArgs
891 * @param uNumArgs
892 * @param pszEnv
893 * @param cbEnv
894 * @param uNumEnvVars
895 * @param pszUser
896 * @param pszPassword
897 * @param uTimeLimitMS
898 */
899int VBoxServiceControlExecAllocateThreadData(PVBOXSERVICECTRLTHREAD pThread,
900 uint32_t u32ContextID,
901 const char *pszCmd, uint32_t uFlags,
902 const char *pszArgs, uint32_t uNumArgs,
903 const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
904 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS)
905{
906 AssertPtr(pThread);
907
908 /* General stuff. */
909 pThread->Node.pPrev = NULL;
910 pThread->Node.pNext = NULL;
911
912 pThread->fShutdown = false;
913 pThread->fStarted = false;
914 pThread->fStopped = false;
915
916 pThread->uContextID = u32ContextID;
917 /* ClientID will be assigned when thread is started! */
918
919 /* Specific stuff. */
920 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREADDATAEXEC));
921 if (pData == NULL)
922 return VERR_NO_MEMORY;
923
924 pData->uPID = 0; /* Don't have a PID yet. */
925 pData->pszCmd = RTStrDup(pszCmd);
926 pData->uFlags = uFlags;
927 pData->uNumEnvVars = 0;
928 pData->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
929
930 /* Prepare argument list. */
931 int rc = RTGetOptArgvFromString(&pData->papszArgs, (int*)&pData->uNumArgs,
932 (uNumArgs > 0) ? pszArgs : "", NULL);
933 /* Did we get the same result? */
934 Assert(uNumArgs == pData->uNumArgs);
935
936 if (RT_SUCCESS(rc))
937 {
938 /* Prepare environment list. */
939 if (uNumEnvVars)
940 {
941 pData->papszEnv = (char**)RTMemAlloc(uNumEnvVars * sizeof(char*));
942 AssertPtr(pData->papszEnv);
943 pData->uNumEnvVars = uNumEnvVars;
944
945 const char *pcCur = pszEnv;
946 uint32_t i = 0;
947 uint32_t cbLen = 0;
948 while (cbLen < cbEnv)
949 {
950 /* sanity check */
951 if (i >= uNumEnvVars)
952 {
953 rc = VERR_INVALID_PARAMETER;
954 break;
955 }
956 int cbStr = RTStrAPrintf(&pData->papszEnv[i++], "%s", pcCur);
957 if (cbStr < 0)
958 {
959 rc = VERR_NO_MEMORY;
960 break;
961 }
962 cbLen += cbStr + 1; /* Skip terminating '\0' */
963 pcCur += cbStr + 1; /* Skip terminating '\0' */
964 }
965 }
966
967 pData->pszUser = RTStrDup(pszUser);
968 pData->pszPassword = RTStrDup(pszPassword);
969 pData->uTimeLimitMS = uTimeLimitMS;
970
971 /* Adjust time limit value. */
972 pData->uTimeLimitMS = ( uTimeLimitMS == UINT32_MAX
973 || uTimeLimitMS == 0)
974 ? RT_INDEFINITE_WAIT : uTimeLimitMS;
975
976 /* Init buffers. */
977 rc = VBoxServiceControlExecInitPipeBuffer(&pData->stdOut, false /*fNeedNotificationPipe*/);
978 if (RT_SUCCESS(rc))
979 {
980 rc = VBoxServiceControlExecInitPipeBuffer(&pData->stdErr, false /*fNeedNotificationPipe*/);
981 if (RT_SUCCESS(rc))
982 rc = VBoxServiceControlExecInitPipeBuffer(&pData->stdIn, true /*fNeedNotificationPipe*/);
983 }
984 }
985
986 if (RT_FAILURE(rc))
987 {
988 VBoxServiceControlExecDestroyThreadData(pData);
989 }
990 else
991 {
992 pThread->enmType = kVBoxServiceCtrlThreadDataExec;
993 pThread->pvData = pData;
994 }
995 return rc;
996}
997
998
999/**
1000 * Frees an allocated thread data structure along with all its allocated parameters.
1001 *
1002 * @param pData Pointer to thread data to free.
1003 */
1004void VBoxServiceControlExecDestroyThreadData(PVBOXSERVICECTRLTHREADDATAEXEC pData)
1005{
1006 if (pData)
1007 {
1008 RTStrFree(pData->pszCmd);
1009 if (pData->uNumEnvVars)
1010 {
1011 for (uint32_t i = 0; i < pData->uNumEnvVars; i++)
1012 RTStrFree(pData->papszEnv[i]);
1013 RTMemFree(pData->papszEnv);
1014 }
1015 RTGetOptArgvFree(pData->papszArgs);
1016 RTStrFree(pData->pszUser);
1017 RTStrFree(pData->pszPassword);
1018
1019 VBoxServiceControlExecDeletePipeBuffer(&pData->stdOut);
1020 VBoxServiceControlExecDeletePipeBuffer(&pData->stdErr);
1021 VBoxServiceControlExecDeletePipeBuffer(&pData->stdIn);
1022
1023 RTMemFree(pData);
1024 pData = NULL;
1025 }
1026}
1027
1028
1029/**
1030 * TODO
1031 *
1032 * @return IPRT status code.
1033 * @param pszExec
1034 * @param papszArgs
1035 * @param hEnv
1036 * @param fFlags
1037 * @param phStdIn
1038 * @param phStdOut
1039 * @param phStdErr
1040 * @param pszAsUser
1041 * @param pszPassword
1042 * @param phProcess
1043 */
1044int VBoxServiceControlExecCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
1045 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
1046 const char *pszPassword, PRTPROCESS phProcess)
1047{
1048 int rc = VINF_SUCCESS;
1049#ifdef RT_OS_WINDOWS
1050 /*
1051 * If sysprep should be executed do this in the context of VBoxService, which
1052 * (usually, if started by SCM) has administrator rights. Because of that a UI
1053 * won't be shown (doesn't have a desktop).
1054 */
1055 if (stricmp(pszExec, "sysprep") == 0)
1056 {
1057 /* Get the predefined path of sysprep.exe (depending on Windows OS). */
1058 char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe";
1059 OSVERSIONINFOEX OSInfoEx;
1060 RT_ZERO(OSInfoEx);
1061 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
1062 if ( GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
1063 && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
1064 && OSInfoEx.dwMajorVersion >= 6 /* Vista or later */)
1065 {
1066 rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL);
1067 if (RT_SUCCESS(rc))
1068 rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\sysprep\\sysprep.exe");
1069 }
1070 rc = RTProcCreateEx(szSysprepCmd, papszArgs, hEnv, 0 /* fFlags */,
1071 phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */,
1072 NULL /* pszPassword */, phProcess);
1073 }
1074 else
1075 {
1076#endif
1077 /* Do normal execution. */
1078 rc = RTProcCreateEx(pszExec, papszArgs, hEnv, fFlags,
1079 phStdIn, phStdOut, phStdErr, pszAsUser,
1080 pszPassword, phProcess);
1081#ifdef RT_OS_WINDOWS
1082 }
1083#endif
1084 return rc;
1085}
1086
1087/**
1088 * The actual worker routine (lopp) for a started guest process.
1089 *
1090 * @return IPRT status code.
1091 * @param PVBOXSERVICECTRLTHREAD Thread data associated with a started process.
1092 */
1093DECLCALLBACK(int) VBoxServiceControlExecProcessWorker(PVBOXSERVICECTRLTHREAD pThread)
1094{
1095 AssertPtr(pThread);
1096 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
1097 AssertPtr(pData);
1098
1099 VBoxServiceVerbose(3, "ControlExec: Thread of process \"%s\" started\n", pData->pszCmd);
1100
1101 int rc = VbglR3GuestCtrlConnect(&pThread->uClientID);
1102 if (RT_FAILURE(rc))
1103 {
1104 VBoxServiceError("ControlExec: Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc);
1105 RTThreadUserSignal(RTThreadSelf());
1106 return rc;
1107 }
1108
1109 bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
1110
1111 /*
1112 * Create the environment.
1113 */
1114 RTENV hEnv;
1115 rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
1116 if (RT_SUCCESS(rc))
1117 {
1118 size_t i;
1119 for (i = 0; i < pData->uNumEnvVars && pData->papszEnv; i++)
1120 {
1121 rc = RTEnvPutEx(hEnv, pData->papszEnv[i]);
1122 if (RT_FAILURE(rc))
1123 break;
1124 }
1125 if (RT_SUCCESS(rc))
1126 {
1127 /*
1128 * Setup the redirection of the standard stuff.
1129 */
1130 /** @todo consider supporting: gcc stuff.c >file 2>&1. */
1131 RTHANDLE hStdIn;
1132 PRTHANDLE phStdIn;
1133 rc = VBoxServiceControlExecSetupPipe(0 /*STDIN_FILENO*/, &hStdIn, &phStdIn, &pData->pipeStdInW);
1134 if (RT_SUCCESS(rc))
1135 {
1136 RTHANDLE hStdOut;
1137 PRTHANDLE phStdOut;
1138 RTPIPE hStdOutR;
1139 rc = VBoxServiceControlExecSetupPipe(1 /*STDOUT_FILENO*/, &hStdOut, &phStdOut, &hStdOutR);
1140 if (RT_SUCCESS(rc))
1141 {
1142 RTHANDLE hStdErr;
1143 PRTHANDLE phStdErr;
1144 RTPIPE hStdErrR;
1145 rc = VBoxServiceControlExecSetupPipe(2 /*STDERR_FILENO*/, &hStdErr, &phStdErr, &hStdErrR);
1146 if (RT_SUCCESS(rc))
1147 {
1148 /*
1149 * Create a poll set for the pipes and let the
1150 * transport layer add stuff to it as well.
1151 */
1152 RTPOLLSET hPollSet;
1153 rc = RTPollSetCreate(&hPollSet);
1154 if (RT_SUCCESS(rc))
1155 {
1156 rc = RTPollSetAddPipe(hPollSet, pData->pipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN_ERROR);
1157 if (RT_SUCCESS(rc))
1158 rc = RTPollSetAddPipe(hPollSet, hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDOUT);
1159 if (RT_SUCCESS(rc))
1160 rc = RTPollSetAddPipe(hPollSet, hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDERR);
1161 if (RT_SUCCESS(rc))
1162 rc = RTPollSetAddPipe(hPollSet, pData->pipeStdInW, RTPOLL_EVT_WRITE, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
1163 if (RT_SUCCESS(rc))
1164 rc = RTPollSetAddPipe(hPollSet, pData->stdIn.hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_STDIN_INPUT_NOTIFY);
1165 if (RT_SUCCESS(rc))
1166 {
1167 RTPROCESS hProcess;
1168 rc = VBoxServiceControlExecCreateProcess(pData->pszCmd, pData->papszArgs, hEnv, RTPROC_FLAGS_SERVICE,
1169 phStdIn, phStdOut, phStdErr,
1170 pData->pszUser, pData->pszPassword,
1171 &hProcess);
1172
1173 /*
1174 * Tell the control thread that it can continue
1175 * spawning services. This needs to be done after the new
1176 * process has been started because otherwise signal handling
1177 * on (Open) Solaris does not work correctly (see #5068).
1178 */
1179 int rc2 = RTThreadUserSignal(RTThreadSelf());
1180 if (RT_FAILURE(rc2))
1181 rc = rc2;
1182 fSignalled = true;
1183
1184 if (RT_SUCCESS(rc))
1185 {
1186 /*
1187 * Close the child ends of any pipes and redirected files.
1188 */
1189 rc2 = RTHandleClose(phStdIn); AssertRC(rc2);
1190 phStdIn = NULL;
1191 rc2 = RTHandleClose(phStdOut); AssertRC(rc2);
1192 phStdOut = NULL;
1193 rc2 = RTHandleClose(phStdErr); AssertRC(rc2);
1194 phStdErr = NULL;
1195
1196 /* Enter the process loop. */
1197 rc = VBoxServiceControlExecProcLoop(pThread,
1198 hProcess, pData->uTimeLimitMS, hPollSet,
1199 &pData->pipeStdInW, &hStdOutR, &hStdErrR);
1200
1201 /*
1202 * The handles that are no longer in the set have
1203 * been closed by the above call in order to prevent
1204 * the guest from getting stuck accessing them.
1205 * So, NIL the handles to avoid closing them again.
1206 */
1207 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 0 /* stdin */, NULL)))
1208 pData->pipeStdInW = NIL_RTPIPE;
1209 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 1 /* stdout */, NULL)))
1210 hStdOutR = NIL_RTPIPE;
1211 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 2 /* stderr */, NULL)))
1212 hStdErrR = NIL_RTPIPE;
1213 }
1214 else /* Something went wrong; report error! */
1215 {
1216 VBoxServiceError("ControlExec: Could not start process '%s' (CID: %u)! Error: %Rrc\n",
1217 pData->pszCmd, pThread->uContextID, rc);
1218
1219 rc2 = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, pData->uPID,
1220 PROC_STS_ERROR, rc,
1221 NULL /* pvData */, 0 /* cbData */);
1222 if (RT_FAILURE(rc2))
1223 VBoxServiceError("ControlExec: Could not report process start error! Error: %Rrc (process error %Rrc)\n",
1224 rc2, rc);
1225 }
1226 }
1227 RTPollSetDestroy(hPollSet);
1228 }
1229 RTPipeClose(hStdErrR);
1230 RTHandleClose(phStdErr);
1231 }
1232 RTPipeClose(hStdOutR);
1233 RTHandleClose(phStdOut);
1234 }
1235 RTPipeClose(pData->pipeStdInW);
1236 RTHandleClose(phStdIn);
1237 }
1238 }
1239 RTEnvDestroy(hEnv);
1240 }
1241
1242 VbglR3GuestCtrlDisconnect(pThread->uClientID);
1243 VBoxServiceVerbose(3, "ControlExec: Thread of process \"%s\" (PID: %u) ended with rc=%Rrc\n",
1244 pData->pszCmd, pData->uPID, rc);
1245
1246 /*
1247 * If something went wrong signal the user event so that others don't wait
1248 * forever on this thread.
1249 */
1250 if (RT_FAILURE(rc) && !fSignalled)
1251 RTThreadUserSignal(RTThreadSelf());
1252 return rc;
1253}
1254
1255
1256/**
1257 * Finds a (formerly) started process given by its PID.
1258 *
1259 * @return PVBOXSERVICECTRLTHREAD Process structure if found, otherwise NULL.
1260 * @param uPID PID to search for.
1261 */
1262PVBOXSERVICECTRLTHREAD VBoxServiceControlExecFindProcess(uint32_t uPID)
1263{
1264 PVBOXSERVICECTRLTHREAD pNode;
1265 bool fFound = false;
1266 RTListForEach(&g_GuestControlExecThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
1267 {
1268 if ( pNode->fStarted
1269 && pNode->enmType == kVBoxServiceCtrlThreadDataExec)
1270 {
1271 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
1272 if (pData && pData->uPID == uPID)
1273 {
1274 return pNode;
1275 }
1276 }
1277 }
1278 return NULL;
1279}
1280
1281
1282/**
1283 * Thread main routine for a started process.
1284 *
1285 * @return IPRT status code.
1286 * @param RTTHREAD Pointer to the thread's data.
1287 * @param void* User-supplied argument pointer.
1288 *
1289 */
1290static DECLCALLBACK(int) VBoxServiceControlExecThread(RTTHREAD ThreadSelf, void *pvUser)
1291{
1292 PVBOXSERVICECTRLTHREAD pThread = (VBOXSERVICECTRLTHREAD*)pvUser;
1293 AssertPtr(pThread);
1294 return VBoxServiceControlExecProcessWorker(pThread);
1295}
1296
1297
1298/**
1299 * TODO
1300 *
1301 * @return int
1302 * @param uContextID
1303 * @param pszCmd
1304 * @param uFlags
1305 * @param pszArgs
1306 * @param uNumArgs
1307 * @param pszEnv
1308 * @param cbEnv
1309 * @param uNumEnvVars
1310 * @param pszUser
1311 * @param pszPassword
1312 * @param uTimeLimitMS
1313 */
1314int VBoxServiceControlExecProcess(uint32_t uContextID, const char *pszCmd, uint32_t uFlags,
1315 const char *pszArgs, uint32_t uNumArgs,
1316 const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
1317 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS)
1318{
1319 PVBOXSERVICECTRLTHREAD pThread = (PVBOXSERVICECTRLTHREAD)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREAD));
1320
1321 int rc;
1322 if (pThread)
1323 {
1324 rc = VBoxServiceControlExecAllocateThreadData(pThread,
1325 uContextID,
1326 pszCmd, uFlags,
1327 pszArgs, uNumArgs,
1328 pszEnv, cbEnv, uNumEnvVars,
1329 pszUser, pszPassword,
1330 uTimeLimitMS);
1331 if (RT_SUCCESS(rc))
1332 {
1333 rc = RTThreadCreate(&pThread->Thread, VBoxServiceControlExecThread,
1334 (void *)(PVBOXSERVICECTRLTHREAD*)pThread, 0,
1335 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "Exec");
1336 if (RT_FAILURE(rc))
1337 {
1338 VBoxServiceError("ControlExec: RTThreadCreate failed, rc=%Rrc\n, pThread=%p\n",
1339 rc, pThread);
1340 }
1341 else
1342 {
1343 VBoxServiceVerbose(4, "ControlExec: Waiting for thread to initialize ...\n");
1344
1345 /* Wait for the thread to initialize. */
1346 RTThreadUserWait(pThread->Thread, 60 * 1000);
1347 if (pThread->fShutdown)
1348 {
1349 VBoxServiceError("ControlExec: Thread for process \"%s\" failed to start!\n", pszCmd);
1350 rc = VERR_GENERAL_FAILURE;
1351 }
1352 else
1353 {
1354 pThread->fStarted = true;
1355 /*rc =*/ RTListAppend(&g_GuestControlExecThreads, &pThread->Node);
1356 }
1357 }
1358
1359 if (RT_FAILURE(rc))
1360 VBoxServiceControlExecDestroyThreadData((PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData);
1361 }
1362 if (RT_FAILURE(rc))
1363 RTMemFree(pThread);
1364 }
1365 else
1366 rc = VERR_NO_MEMORY;
1367 return rc;
1368}
1369
1370
1371/**
1372 * TODO
1373 *
1374 * @return IPRT status code.
1375 * @param u32ClientId
1376 * @param uNumParms
1377 */
1378int VBoxServiceControlExecHandleCmdStartProcess(uint32_t u32ClientId, uint32_t uNumParms)
1379{
1380 uint32_t uContextID;
1381 char szCmd[_1K];
1382 uint32_t uFlags;
1383 char szArgs[_1K];
1384 uint32_t uNumArgs;
1385 char szEnv[_64K];
1386 uint32_t cbEnv = sizeof(szEnv);
1387 uint32_t uNumEnvVars;
1388 char szUser[128];
1389 char szPassword[128];
1390 uint32_t uTimeLimitMS;
1391
1392#if 0 /* for valgrind */
1393 RT_ZERO(szCmd);
1394 RT_ZERO(szArgs);
1395 RT_ZERO(szEnv);
1396 RT_ZERO(szUser);
1397 RT_ZERO(szPassword);
1398#endif
1399
1400 if (uNumParms != 11)
1401 return VERR_INVALID_PARAMETER;
1402
1403 int rc = VbglR3GuestCtrlExecGetHostCmd(u32ClientId,
1404 uNumParms,
1405 &uContextID,
1406 /* Command */
1407 szCmd, sizeof(szCmd),
1408 /* Flags */
1409 &uFlags,
1410 /* Arguments */
1411 szArgs, sizeof(szArgs), &uNumArgs,
1412 /* Environment */
1413 szEnv, &cbEnv, &uNumEnvVars,
1414 /* Credentials */
1415 szUser, sizeof(szUser),
1416 szPassword, sizeof(szPassword),
1417 /* Timelimit */
1418 &uTimeLimitMS);
1419 if (RT_FAILURE(rc))
1420 {
1421 VBoxServiceError("ControlExec: Failed to retrieve exec start command! Error: %Rrc\n", rc);
1422 }
1423 else
1424 {
1425 rc = VBoxServiceControlExecProcess(uContextID, szCmd, uFlags, szArgs, uNumArgs,
1426 szEnv, cbEnv, uNumEnvVars,
1427 szUser, szPassword, uTimeLimitMS);
1428 }
1429
1430 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdStartProcess returned with %Rrc\n", rc);
1431 return rc;
1432}
1433
1434
1435/**
1436 * Handles input for the started process by copying the received data into its
1437 * stdin pipe.
1438 *
1439 * @returns IPRT status code.
1440 * @param u32ClientId idClient The HGCM client session ID.
1441 * @param uNumParms cParms The number of parameters the host is
1442 * offering.
1443 */
1444int VBoxServiceControlExecHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms)
1445{
1446 uint32_t uContextID;
1447 uint32_t uPID;
1448 uint32_t uFlags;
1449 uint8_t abBuffer[_64K];
1450 uint32_t cbSize;
1451
1452 if (uNumParms != 5)
1453 return VERR_INVALID_PARAMETER;
1454
1455 /*
1456 * Ask the host for the input data.
1457 */
1458 int rc = VbglR3GuestCtrlExecGetHostCmdInput(u32ClientId, uNumParms,
1459 &uContextID, &uPID, &uFlags,
1460 abBuffer, sizeof(abBuffer), &cbSize);
1461 if (RT_FAILURE(rc))
1462 {
1463 VBoxServiceError("ControlExec: Failed to retrieve exec input command! Error: %Rrc\n", rc);
1464 }
1465 else if ( cbSize <= 0
1466 || cbSize > sizeof(abBuffer))
1467 {
1468 VBoxServiceError("ControlExec: Input size is invalid! cbSize=%u\n", cbSize);
1469 rc = VERR_INVALID_PARAMETER;
1470 }
1471 else
1472 {
1473 /*
1474 * Resolve the PID.
1475 */
1476#ifdef DEBUG
1477 VBoxServiceVerbose(4, "ControlExec: Input (PID %u) received: cbSize=%u\n", uPID, cbSize);
1478#endif
1479 PVBOXSERVICECTRLTHREAD pNode = VBoxServiceControlExecFindProcess(uPID);
1480 if (pNode)
1481 {
1482 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
1483 AssertPtr(pData);
1484
1485 /*
1486 * Is this the last input block we need to deliver? Then let the pipe know ...
1487 */
1488 bool fPendingClose = false;
1489 if (uFlags & INPUT_FLAG_EOF)
1490 fPendingClose = true;
1491#ifdef DEBUG
1492 if (fPendingClose)
1493 VBoxServiceVerbose(4, "ControlExec: Got last input block ...\n");
1494#endif
1495 /*
1496 * Feed the data to the pipe.
1497 */
1498 uint32_t cbWritten;
1499 rc = VBoxServiceControlExecWritePipeBuffer(&pData->stdIn, abBuffer, cbSize, fPendingClose, &cbWritten);
1500#ifdef DEBUG
1501 VBoxServiceVerbose(4, "ControlExec: Written to StdIn buffer (PID %u): rc=%Rrc, uFlags=0x%x, cbAlloc=%u, cbSize=%u, cbOffset=%u\n",
1502 uPID, rc, pData->stdIn.cbAllocated, pData->stdIn.cbSize,
1503 pData->stdIn.cbOffset);
1504#endif
1505 uint32_t uStatus = INPUT_STS_UNDEFINED;
1506 if (RT_SUCCESS(rc))
1507 {
1508 if (cbWritten) /* Did we write something? */
1509 {
1510 uStatus = INPUT_STS_WRITTEN;
1511 uFlags = 0;
1512 }
1513 }
1514 else
1515 {
1516 if (rc == VERR_BAD_PIPE)
1517 uStatus = INPUT_STS_TERMINATED;
1518 else if (rc == VERR_BUFFER_OVERFLOW)
1519 uStatus = INPUT_STS_OVERFLOW;
1520 else
1521 {
1522 uStatus = INPUT_STS_ERROR;
1523 uFlags = rc;
1524 }
1525 }
1526
1527 if (uStatus > INPUT_STS_UNDEFINED)
1528 {
1529 /* Note: Since the context ID is unique the request *has* to be completed here,
1530 * regardless whether we got data or not! Otherwise the progress object
1531 * on the host never will get completed! */
1532 rc = VbglR3GuestCtrlExecReportStatusIn(u32ClientId, uContextID, uPID,
1533 uStatus, uFlags, (uint32_t)cbWritten);
1534 }
1535 }
1536 else
1537 rc = VERR_NOT_FOUND; /* PID not found! */
1538 }
1539 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdSetInput returned with %Rrc\n", rc);
1540 return rc;
1541}
1542
1543
1544/**
1545 *
1546 *
1547 * @return IPRT status code.
1548 * @param u32ClientId
1549 * @param uNumParms
1550 */
1551int VBoxServiceControlExecHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms)
1552{
1553 uint32_t uContextID;
1554 uint32_t uPID;
1555 uint32_t uHandleID;
1556 uint32_t uFlags;
1557
1558 int rc = VbglR3GuestCtrlExecGetHostCmdOutput(u32ClientId, uNumParms,
1559 &uContextID, &uPID, &uHandleID, &uFlags);
1560 if (RT_FAILURE(rc))
1561 {
1562 VBoxServiceError("ControlExec: Failed to retrieve exec output command! Error: %Rrc\n", rc);
1563 }
1564 else
1565 {
1566 PVBOXSERVICECTRLTHREAD pNode = VBoxServiceControlExecFindProcess(uPID);
1567 if (pNode)
1568 {
1569 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
1570 AssertPtr(pData);
1571
1572 const uint32_t cbSize = _4K;
1573 uint32_t cbRead = cbSize;
1574 uint8_t *pBuf = (uint8_t*)RTMemAlloc(cbSize);
1575 if (pBuf)
1576 {
1577 /** @todo Use uHandleID to distinguish between stdout/stderr! */
1578 rc = VBoxServiceControlExecReadPipeBufferContent(&pData->stdOut, pBuf, cbSize, &cbRead);
1579 if (RT_SUCCESS(rc))
1580 {
1581 /* Note: Since the context ID is unique the request *has* to be completed here,
1582 * regardless whether we got data or not! Otherwise the progress object
1583 * on the host never will get completed! */
1584 /* cbRead now contains actual size. */
1585 rc = VbglR3GuestCtrlExecSendOut(u32ClientId, uContextID, uPID, 0 /* Handle ID */, 0 /* Flags */,
1586 pBuf, cbRead);
1587 }
1588 RTMemFree(pBuf);
1589 }
1590 else
1591 rc = VERR_NO_MEMORY;
1592 }
1593 else
1594 rc = VERR_NOT_FOUND; /* PID not found! */
1595 }
1596 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdGetOutput returned with %Rrc\n", rc);
1597 return rc;
1598}
1599
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