VirtualBox

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

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

Additions/common/VBoxServiceControlExec: Solaris build fix.

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