VirtualBox

source: vbox/trunk/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp@ 35766

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

ExtPack changes, related IPRT changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.4 KB
Line 
1/* $Id: tstGuestControlSvc.cpp 33806 2010-11-05 17:20:15Z vboxsync $ */
2/** @file
3 *
4 * Testcase for the guest control service.
5 */
6
7/*
8 * Copyright (C) 2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <VBox/HostServices/GuestControlSvc.h>
23#include <iprt/alloca.h>
24#include <iprt/initterm.h>
25#include <iprt/crc.h>
26#include <iprt/ctype.h>
27#include <iprt/env.h>
28#include <iprt/file.h>
29#include <iprt/getopt.h>
30#include <iprt/handle.h>
31#include <iprt/mem.h>
32#include <iprt/message.h>
33#include <iprt/param.h>
34#include <iprt/path.h>
35#include <iprt/pipe.h>
36#include <iprt/poll.h>
37#include <iprt/process.h>
38#include <iprt/stream.h>
39#include <iprt/thread.h>
40
41#include "../gctrl.h"
42
43using namespace guestControl;
44
45extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *pTable);
46char g_szImageName[RTPATH_MAX];
47
48/** Prototypes. */
49int guestExecSendStdOut(VBOXHGCMSVCFNTABLE *pTable,
50 const char *pszStdOut, uint32_t cbStdOut);
51
52/** Simple call handle structure for the guest call completion callback */
53struct VBOXHGCMCALLHANDLE_TYPEDEF
54{
55 /** Where to store the result code */
56 int32_t rc;
57};
58
59/**
60 * For buffering process input supplied by the client.
61 */
62typedef struct TXSEXECSTDINBUF
63{
64 /** The mount of buffered data. */
65 size_t cb;
66 /** The current data offset. */
67 size_t off;
68 /** The data buffer. */
69 char *pch;
70 /** The amount of allocated buffer space. */
71 size_t cbAllocated;
72 /** Send further input into the bit bucket (stdin is dead). */
73 bool fBitBucket;
74 /** The CRC-32 for standard input (received part). */
75 uint32_t uCrc32;
76} TXSEXECSTDINBUF;
77/** Pointer to a standard input buffer. */
78typedef TXSEXECSTDINBUF *PTXSEXECSTDINBUF;
79
80/** Call completion callback for guest calls. */
81static void callComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc)
82{
83 callHandle->rc = rc;
84}
85
86/**
87 * Initialise the HGCM service table as much as we need to start the
88 * service
89 * @param pTable the table to initialise
90 */
91void initTable(VBOXHGCMSVCFNTABLE *pTable, VBOXHGCMSVCHELPERS *pHelpers)
92{
93 pTable->cbSize = sizeof (VBOXHGCMSVCFNTABLE);
94 pTable->u32Version = VBOX_HGCM_SVC_VERSION;
95 pHelpers->pfnCallComplete = callComplete;
96 pTable->pHelpers = pHelpers;
97}
98
99/**
100 * Sets up the redirection / pipe / nothing for one of the standard handles.
101 *
102 * @returns IPRT status code. No client replies made.
103 * @param pszHowTo How to set up this standard handle.
104 * @param fd Which standard handle it is (0 == stdin, 1 ==
105 * stdout, 2 == stderr).
106 * @param ph The generic handle that @a pph may be set
107 * pointing to. Always set.
108 * @param pph Pointer to the RTProcCreateExec argument.
109 * Always set.
110 * @param phPipe Where to return the end of the pipe that we
111 * should service. Always set.
112 */
113static int guestExecSetupPipe(int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
114{
115 AssertPtr(ph);
116 AssertPtr(pph);
117 AssertPtr(phPipe);
118
119 ph->enmType = RTHANDLETYPE_PIPE;
120 ph->u.hPipe = NIL_RTPIPE;
121 *pph = NULL;
122 *phPipe = NIL_RTPIPE;
123
124 int rc;
125
126 /*
127 * Setup a pipe for forwarding to/from the client.
128 * The ph union struct will be filled with a pipe read/write handle
129 * to represent the "other" end to phPipe.
130 */
131 if (fd == 0) /* stdin? */
132 {
133 /* Connect a wrtie pipe specified by phPipe to stdin. */
134 rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
135 }
136 else /* stdout or stderr? */
137 {
138 /* Connect a read pipe specified by phPipe to stdout or stderr. */
139 rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
140 }
141 if (RT_FAILURE(rc))
142 return rc;
143 ph->enmType = RTHANDLETYPE_PIPE;
144 *pph = ph;
145
146 return rc;
147}
148
149/**
150 * Handle an error event on standard input.
151 *
152 * @param hPollSet The polling set.
153 * @param fPollEvt The event mask returned by RTPollNoResume.
154 * @param phStdInW The standard input pipe handle.
155 * @param pStdInBuf The standard input buffer.
156 */
157static void guestExecProcHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
158 PTXSEXECSTDINBUF pStdInBuf)
159{
160 int rc2;
161 if (pStdInBuf->off < pStdInBuf->cb)
162 {
163 rc2 = RTPollSetRemove(hPollSet, 4 /*TXSEXECHNDID_STDIN_WRITABLE*/);
164 AssertRC(rc2);
165 }
166
167 rc2 = RTPollSetRemove(hPollSet, 0 /*TXSEXECHNDID_STDIN*/);
168 AssertRC(rc2);
169
170 rc2 = RTPipeClose(*phStdInW);
171 AssertRC(rc2);
172 *phStdInW = NIL_RTPIPE;
173
174 RTMemFree(pStdInBuf->pch);
175 pStdInBuf->pch = NULL;
176 pStdInBuf->off = 0;
177 pStdInBuf->cb = 0;
178 pStdInBuf->cbAllocated = 0;
179 pStdInBuf->fBitBucket = true;
180}
181
182/**
183 * Try write some more data to the standard input of the child.
184 *
185 * @returns IPRT status code.
186 * @param pStdInBuf The standard input buffer.
187 * @param hStdInW The standard input pipe.
188 */
189static int guestExecProcWriteStdIn(PTXSEXECSTDINBUF pStdInBuf, RTPIPE hStdInW)
190{
191 size_t cbToWrite = pStdInBuf->cb - pStdInBuf->off;
192 size_t cbWritten;
193 int rc = RTPipeWrite(hStdInW, &pStdInBuf->pch[pStdInBuf->off], cbToWrite, &cbWritten);
194 if (RT_SUCCESS(rc))
195 {
196 Assert(cbWritten == cbToWrite);
197 pStdInBuf->off += cbWritten;
198 }
199 return rc;
200}
201
202/**
203 * Handle an event indicating we can write to the standard input pipe of the
204 * child process.
205 *
206 * @param hPollSet The polling set.
207 * @param fPollEvt The event mask returned by RTPollNoResume.
208 * @param phStdInW The standard input pipe.
209 * @param pStdInBuf The standard input buffer.
210 */
211static void guestExecProcHandleStdInWritableEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
212 PTXSEXECSTDINBUF pStdInBuf)
213{
214 int rc;
215 if (!(fPollEvt & RTPOLL_EVT_ERROR))
216 {
217 rc = guestExecProcWriteStdIn(pStdInBuf, *phStdInW);
218 if (RT_FAILURE(rc) && rc != VERR_BAD_PIPE)
219 {
220 /** @todo do we need to do something about this error condition? */
221 AssertRC(rc);
222 }
223
224 if (pStdInBuf->off < pStdInBuf->cb)
225 {
226 rc = RTPollSetRemove(hPollSet, 4 /*TXSEXECHNDID_STDIN_WRITABLE*/);
227 AssertRC(rc);
228 }
229 }
230 else
231 guestExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
232}
233
234/**
235 * Handle pending output data or error on standard out, standard error or the
236 * test pipe.
237 *
238 * @returns IPRT status code from client send.
239 * @param hPollSet The polling set.
240 * @param fPollEvt The event mask returned by RTPollNoResume.
241 * @param phPipeR The pipe handle.
242 * @param pu32Crc The current CRC-32 of the stream. (In/Out)
243 * @param uHandleId The handle ID.
244 * @param pszOpcode The opcode for the data upload.
245 *
246 * @todo Put the last 4 parameters into a struct!
247 */
248static int guestExecProcHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phPipeR,
249 uint32_t *puCrc32 , uint32_t uHandleId)
250{
251 Log(("guestExecProcHandleOutputEvent: fPollEvt=%#x\n", fPollEvt));
252
253 /*
254 * Try drain the pipe before acting on any errors.
255 */
256 int rc = VINF_SUCCESS;
257
258 char abBuf[_64K];
259 size_t cbRead;
260 int rc2 = RTPipeRead(*phPipeR, abBuf, sizeof(abBuf), &cbRead);
261 if (RT_SUCCESS(rc2) && cbRead)
262 {
263 Log(("Crc32=%#x ", *puCrc32));
264
265#if 1
266 abBuf[cbRead] = '\0';
267 RTPrintf("%s: %s\n", uHandleId == 1 ? "StdOut: " : "StdErr: ", abBuf);
268#endif
269
270 /**puCrc32 = RTCrc32Process(*puCrc32, abBuf, cbRead);
271 Log(("cbRead=%#x Crc32=%#x \n", cbRead, *puCrc32));
272 Pkt.uCrc32 = RTCrc32Finish(*puCrc32);*/
273 /* if (g_fDisplayOutput)
274 {
275 if (enmHndId == TXSEXECHNDID_STDOUT)
276 RTStrmPrintf(g_pStdErr, "%.*s", cbRead, Pkt.abBuf);
277 else if (enmHndId == TXSEXECHNDID_STDERR)
278 RTStrmPrintf(g_pStdErr, "%.*s", cbRead, Pkt.abBuf);
279 }
280
281 rc = txsReplyInternal(&Pkt.Hdr, pszOpcode, cbRead + sizeof(uint32_t));*/
282
283 /* Make sure we go another poll round in case there was too much data
284 for the buffer to hold. */
285 fPollEvt &= RTPOLL_EVT_ERROR;
286 }
287 else if (RT_FAILURE(rc2))
288 {
289 fPollEvt |= RTPOLL_EVT_ERROR;
290 AssertMsg(rc2 == VERR_BROKEN_PIPE, ("%Rrc\n", rc));
291 }
292
293 /*
294 * If an error was raised signalled,
295 */
296 if (fPollEvt & RTPOLL_EVT_ERROR)
297 {
298 rc2 = RTPollSetRemove(hPollSet, uHandleId);
299 AssertRC(rc2);
300
301 rc2 = RTPipeClose(*phPipeR);
302 AssertRC(rc2);
303 *phPipeR = NIL_RTPIPE;
304 }
305 return rc;
306}
307
308/**
309 * Handle a transport event or successful pfnPollIn() call.
310 *
311 * @returns IPRT status code from client send.
312 * @retval VINF_EOF indicates ABORT command.
313 *
314 * @param hPollSet The polling set.
315 * @param fPollEvt The event mask returned by RTPollNoResume.
316 * @param idPollHnd The handle ID.
317 * @param hStdInW The standard input pipe.
318 * @param pStdInBuf The standard input buffer.
319 */
320static int guestExecProcHandleTransportEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, uint32_t idPollHnd,
321 PRTPIPE phStdInW, PTXSEXECSTDINBUF pStdInBuf)
322{
323
324 int rc = RTPollSetAddPipe(hPollSet, *phStdInW, RTPOLL_EVT_WRITE, 4 /*TXSEXECHNDID_STDIN_WRITABLE*/);
325
326 return rc;
327}
328
329static int guestExecProcLoop(VBOXHGCMSVCFNTABLE *pTable,
330 RTPROCESS hProcess, RTMSINTERVAL cMillies, RTPOLLSET hPollSet,
331 RTPIPE hStdInW, RTPIPE hStdOutR, RTPIPE hStdErrR)
332{
333 int rc;
334 int rc2;
335 TXSEXECSTDINBUF StdInBuf = { 0, 0, NULL, 0, hStdInW == NIL_RTPIPE, RTCrc32Start() };
336 uint32_t uStdOutCrc32 = RTCrc32Start();
337 uint32_t uStdErrCrc32 = uStdOutCrc32;
338 uint64_t const MsStart = RTTimeMilliTS();
339 RTPROCSTATUS ProcessStatus = { 254, RTPROCEXITREASON_ABEND };
340 bool fProcessAlive = true;
341 bool fProcessTimedOut = false;
342 uint64_t MsProcessKilled = UINT64_MAX;
343 bool const fHavePipes = hStdInW != NIL_RTPIPE
344 || hStdOutR != NIL_RTPIPE
345 || hStdErrR != NIL_RTPIPE;
346 RTMSINTERVAL const cMsPollBase = hStdInW != NIL_RTPIPE
347 ? 100 /* need to poll for input */
348 : 1000; /* need only poll for process exit and aborts */
349 RTMSINTERVAL cMsPollCur = 0;
350
351 /*
352 * Before entering the loop, tell the host that we've started the guest
353 * and that it's now OK to send input to the process.
354 */
355 rc = VINF_SUCCESS;
356
357 /*
358 * Process input, output, the test pipe and client requests.
359 */
360 while (RT_SUCCESS(rc))
361 {
362 /*
363 * Wait/Process all pending events.
364 */
365 uint32_t idPollHnd;
366 uint32_t fPollEvt;
367 rc2 = RTPollNoResume(hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
368
369 cMsPollCur = 0; /* no rest until we've checked everything. */
370
371 if (RT_SUCCESS(rc2))
372 {
373 switch (idPollHnd)
374 {
375 case 0 /* TXSEXECHNDID_STDIN */:
376 guestExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, &hStdInW, &StdInBuf);
377 break;
378
379 case 1 /* TXSEXECHNDID_STDOUT */:
380 rc = guestExecProcHandleOutputEvent(hPollSet, fPollEvt, &hStdOutR, &uStdOutCrc32, 1 /* TXSEXECHNDID_STDOUT */);
381 break;
382
383 case 2 /*TXSEXECHNDID_STDERR */:
384 rc = guestExecProcHandleOutputEvent(hPollSet, fPollEvt, &hStdErrR, &uStdErrCrc32, 2 /*TXSEXECHNDID_STDERR */);
385 break;
386
387 case 4 /* TXSEXECHNDID_STDIN_WRITABLE */:
388 guestExecProcHandleStdInWritableEvent(hPollSet, fPollEvt, &hStdInW, &StdInBuf);
389 break;
390
391 default:
392 rc = guestExecProcHandleTransportEvent(hPollSet, fPollEvt, idPollHnd, &hStdInW, &StdInBuf);
393 break;
394 }
395 if (RT_FAILURE(rc) || rc == VINF_EOF)
396 break; /* abort command, or client dead or something */
397 continue;
398 }
399
400 /*
401 * Check for incoming data.
402 */
403
404 /*
405 * Check for process death.
406 */
407 if (fProcessAlive)
408 {
409 rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
410 if (RT_SUCCESS_NP(rc2))
411 {
412 fProcessAlive = false;
413 continue;
414 }
415 if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
416 continue;
417 if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
418 {
419 fProcessAlive = false;
420 ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
421 ProcessStatus.iStatus = 255;
422 AssertFailed();
423 }
424 else
425 AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
426 }
427
428 /*
429 * If the process has terminated, we're should head out.
430 */
431 if (!fProcessAlive)
432 break;
433
434 /*
435 * Check for timed out, killing the process.
436 */
437 uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
438 if (cMillies != RT_INDEFINITE_WAIT)
439 {
440 uint64_t u64Now = RTTimeMilliTS();
441 uint64_t cMsElapsed = u64Now - MsStart;
442 if (cMsElapsed >= cMillies)
443 {
444 fProcessTimedOut = true;
445 if ( MsProcessKilled == UINT64_MAX
446 || u64Now - MsProcessKilled > 1000)
447 {
448 if (u64Now - MsProcessKilled > 20*60*1000)
449 break; /* give up after 20 mins */
450 RTProcTerminate(hProcess);
451 MsProcessKilled = u64Now;
452 continue;
453 }
454 cMilliesLeft = 10000;
455 }
456 else
457 cMilliesLeft = cMillies - (uint32_t)cMsElapsed;
458 }
459
460 /* Reset the polling interval since we've done all pending work. */
461 cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft;
462 }
463
464 /*
465 * Try kill the process if it's still alive at this point.
466 */
467 if (fProcessAlive)
468 {
469 if (MsProcessKilled == UINT64_MAX)
470 {
471 MsProcessKilled = RTTimeMilliTS();
472 RTProcTerminate(hProcess);
473 RTThreadSleep(500);
474 }
475
476 for (size_t i = 0; i < 10; i++)
477 {
478 rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
479 if (RT_SUCCESS(rc2))
480 {
481 fProcessAlive = false;
482 break;
483 }
484 if (i >= 5)
485 RTProcTerminate(hProcess);
486 RTThreadSleep(i >= 5 ? 2000 : 500);
487 }
488 }
489
490 /*
491 * If we don't have a client problem (RT_FAILURE(rc) we'll reply to the
492 * clients exec packet now.
493 */
494 if (RT_SUCCESS(rc))
495 {
496 if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX)
497 {
498
499 }
500 else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX)
501 {
502
503 }
504 /*else if (g_fTerminate && (fProcessAlive || MsProcessKilled != UINT64_MAX))
505 {
506
507 }*/
508 else if (fProcessAlive)
509 {
510
511 }
512 else if (MsProcessKilled != UINT64_MAX)
513 {
514
515 }
516 else if ( ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL
517 && ProcessStatus.iStatus == 0)
518 {
519
520 }
521 else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
522 {
523
524 }
525 else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
526 {
527
528 }
529 else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
530 {
531
532 }
533 else
534 {
535
536 }
537 }
538
539 RTMemFree(StdInBuf.pch);
540 return rc;
541}
542
543int guestExecProcess(VBOXHGCMSVCFNTABLE *pTable,
544 uint32_t fFlags, const char *pszExecName,
545 uint32_t cArgs, const char * const *papszArgs,
546 uint32_t cEnvVars, const char * const *papszEnv,
547 const char *pszStdIn, const char *pszStdOut, const char *pszStdErr,
548 const char *pszUsername, const char *pszPassword, RTMSINTERVAL cMillies)
549{
550#if 0
551 /* Print some info */
552 RTPrintf("Flags: %u\n"
553 "# Args: %u\n"
554 "# Env: %u\n"
555 "StdIn: %s, StdOut: %s, StdErr: %s\n"
556 "User: %s, Timelimit: %u\n",
557 fFlags, cArgs, cEnvVars,
558 pszStdIn, pszStdOut, pszStdErr,
559 pszUsername ? pszUsername : "<None>", cMillies);
560 for (uint32_t i=0; i<cArgs; i++)
561 RTPrintf("Arg %u: %s\n", i, papszArgs[i]);
562 for (uint32_t i=0; i<cEnvVars; i++)
563 RTPrintf("Env %u: %s\n", i, papszEnv[i]);
564#endif
565
566 /*
567 * Create the environment.
568 */
569 RTENV hEnv;
570 int rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
571 if (RT_SUCCESS(rc))
572 {
573 size_t i;
574 for (i = 0; i < cEnvVars; i++)
575 {
576 rc = RTEnvPutEx(hEnv, papszEnv[i]);
577 if (RT_FAILURE(rc))
578 break;
579 }
580 if (RT_SUCCESS(rc))
581 {
582 /*
583 * Setup the redirection of the standard stuff.
584 */
585 /** @todo consider supporting: gcc stuff.c >file 2>&1. */
586 RTHANDLE hStdIn;
587 PRTHANDLE phStdIn;
588 RTPIPE hStdInW;
589 rc = guestExecSetupPipe(0 /* stdin */, &hStdIn, &phStdIn, &hStdInW);
590 if (RT_SUCCESS(rc))
591 {
592 RTHANDLE hStdOut;
593 PRTHANDLE phStdOut;
594 RTPIPE hStdOutR;
595 rc = guestExecSetupPipe(1 /* stdout */, &hStdOut, &phStdOut, &hStdOutR);
596 if (RT_SUCCESS(rc))
597 {
598 RTHANDLE hStdErr;
599 PRTHANDLE phStdErr;
600 RTPIPE hStdErrR;
601 rc = guestExecSetupPipe(2 /* stderr */, &hStdErr, &phStdErr, &hStdErrR);
602 if (RT_SUCCESS(rc))
603 {
604 /*
605 * Create a poll set for the pipes and let the
606 * transport layer add stuff to it as well.
607 */
608 RTPOLLSET hPollSet;
609 rc = RTPollSetCreate(&hPollSet);
610 if (RT_SUCCESS(rc))
611 {
612 rc = RTPollSetAddPipe(hPollSet, hStdInW, RTPOLL_EVT_ERROR, 0 /* TXSEXECHNDID_STDIN */);
613 if (RT_SUCCESS(rc))
614 rc = RTPollSetAddPipe(hPollSet, hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 1 /* TXSEXECHNDID_STDOUT */);
615 if (RT_SUCCESS(rc))
616 rc = RTPollSetAddPipe(hPollSet, hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 2 /* TXSEXECHNDID_TESTPIPE */);
617 if (RT_SUCCESS(rc))
618 {
619 RTPROCESS hProcess;
620 rc = RTProcCreateEx(pszExecName, papszArgs, hEnv, 0 /*fFlags*/,
621 phStdIn, phStdOut, phStdErr,
622 /*pszUsername, pszPassword,*/ NULL, NULL,
623 &hProcess);
624 if (RT_SUCCESS(rc))
625 {
626 /*
627 * Close the child ends of any pipes and redirected files.
628 */
629 int rc2 = RTHandleClose(phStdIn); AssertRC(rc2);
630 phStdIn = NULL;
631 rc2 = RTHandleClose(phStdOut); AssertRC(rc2);
632 phStdOut = NULL;
633 rc2 = RTHandleClose(phStdErr); AssertRC(rc2);
634 phStdErr = NULL;
635
636 rc = guestExecProcLoop(pTable,
637 hProcess, cMillies, hPollSet,
638 hStdInW, hStdOutR, hStdErrR);
639 /*
640 * The handles that are no longer in the set have
641 * been closed by the above call in order to prevent
642 * the guest from getting stuck accessing them.
643 * So, NIL the handles to avoid closing them again.
644 */
645 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 0 /* stdin */, NULL)))
646 hStdInW = NIL_RTPIPE;
647 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 1 /* stdout */, NULL)))
648 hStdOutR = NIL_RTPIPE;
649 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 2 /* stderr */, NULL)))
650 hStdErrR = NIL_RTPIPE;
651 }
652 }
653 }
654 RTPipeClose(hStdErrR);
655 RTHandleClose(phStdErr);
656 }
657 RTPipeClose(hStdOutR);
658 RTHandleClose(phStdOut);
659 }
660 RTPipeClose(hStdInW);
661 RTHandleClose(phStdIn);
662 }
663 }
664 RTEnvDestroy(hEnv);
665 }
666
667 return rc;
668}
669
670int guestExecHandleCmdExecute(VBOXHGCMSVCFNTABLE *pTable, PVBOXHGCMSVCPARM paParms, uint32_t cParms)
671{
672 char *pcBuf;
673 uint32_t cbLen;
674
675 if(cParms < 13)
676 return VERR_INVALID_PARAMETER;
677
678 /* flags */
679 uint32_t uFlags;
680 int rc = paParms[1].getUInt32(&uFlags);
681 if (RT_SUCCESS(rc))
682 {
683 /* cmd */
684 rc = paParms[2].getString(&pcBuf, &cbLen);
685 char *pszExecName = RTStrDup(pcBuf);
686
687 /* arguments */
688 if ( pszExecName
689 && RT_SUCCESS(rc))
690 {
691 /* argc */
692 uint32_t uArgc;
693 paParms[3].getUInt32(&uArgc);
694
695 /* argv */
696 char *pcData;
697 paParms[4].getBuffer((void**)&pcData, &cbLen);
698 AssertPtr(pcData);
699
700 char **ppaArg;
701 int iArgs;
702 rc = RTGetOptArgvFromString(&ppaArg, &iArgs, pcData, NULL);
703 Assert(uArgc == iArgs);
704
705 /* environment */
706 if (RT_SUCCESS(rc))
707 {
708 /* envc */
709 uint32_t uEnvc;
710 paParms[5].getUInt32(&uEnvc);
711
712 /* envv */
713 paParms[6].getBuffer((void**)&pcData, &cbLen);
714 AssertPtr(pcData);
715
716 char **ppaEnv = NULL;
717 if (uEnvc)
718 {
719 ppaEnv = (char**)RTMemAlloc(uEnvc * sizeof(char*));
720 AssertPtr(ppaEnv);
721
722 char *pcCur = pcData;
723 uint32_t i = 0;
724 while (pcCur < pcData + cbLen)
725 {
726 if (RTStrAPrintf(&ppaEnv[i++], "%s", pcCur) < 0)
727 {
728 rc = VERR_NO_MEMORY;
729 break;
730 }
731 pcCur += strlen(pcCur) + 1; /* Skip terminating zero. */
732 }
733 }
734 if (RT_SUCCESS(rc))
735 {
736 /* stdin */
737 rc = paParms[7].getString(&pcBuf, &cbLen);
738 char *pszStdIn = RTStrDup(pcBuf);
739 if ( pszStdIn
740 && RT_SUCCESS(rc))
741 {
742 /* stdout */
743 rc = paParms[8].getString(&pcBuf, &cbLen);
744 char *pszStdOut = RTStrDup(pcBuf);
745 if ( pszStdOut
746 && RT_SUCCESS(rc))
747 {
748 /* stderr */
749 rc = paParms[9].getString(&pcBuf, &cbLen);
750 char *pszStdErr = RTStrDup(pcBuf);
751 if ( pszStdErr
752 && RT_SUCCESS(rc))
753 {
754 /* user */
755 rc = paParms[10].getString(&pcBuf, &cbLen);
756 char *pszUser = RTStrDup(pcBuf);
757 if ( pszUser
758 && RT_SUCCESS(rc))
759 {
760 /* password */
761 rc = paParms[11].getString(&pcBuf, &cbLen);
762 char *pszPassword = RTStrDup(pcBuf);
763 if (RT_SUCCESS(rc))
764 {
765 /* time to wait (0 = max, no time limit) */
766 RTMSINTERVAL msTimeLimit;
767 rc = paParms[12].getUInt32(&msTimeLimit);
768 if (RT_SUCCESS(rc))
769 {
770 rc = guestExecProcess(pTable,
771 uFlags, pszExecName,
772 uArgc, ppaArg,
773 uEnvc, ppaEnv,
774 pszStdIn, pszStdOut, pszStdIn,
775 pszUser, pszPassword,
776 msTimeLimit == UINT32_MAX ? RT_INDEFINITE_WAIT : msTimeLimit);
777 }
778 }
779 RTStrFree(pszUser);
780 }
781 RTStrFree(pszStdErr);
782 }
783 RTStrFree(pszStdOut);
784 }
785 RTStrFree(pszStdIn);
786 }
787 for (uint32_t i=0; i<uEnvc; i++)
788 RTStrFree(ppaEnv[i]);
789 RTMemFree(ppaEnv);
790 }
791 RTGetOptArgvFree(ppaArg);
792 }
793 RTStrFree(pszExecName);
794 }
795 }
796 return rc;
797}
798
799/** Sends the current stdout (stderr later?) data to the host. */
800int guestExecSendStdOut(VBOXHGCMSVCFNTABLE *pTable,
801 const char *pszStdOut, uint32_t cbStdOut)
802{
803 VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
804 int command = GUEST_EXEC_SEND_STDOUT;
805 VBOXHGCMSVCPARM paParms[3];
806
807 if ( pszStdOut == NULL
808 || cbStdOut == 0)
809 {
810 return VERR_INVALID_PARAMETER;
811 }
812
813 paParms[0].setUInt32(123 /* @todo PID. */);
814 paParms[1].setUInt32(1 /* @todo Pipe ID. */);
815 paParms[2].setPointer((void*)pszStdOut, cbStdOut + 1);
816
817 pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, command,
818 3, paParms);
819 int rc = VINF_SUCCESS;
820 if (RT_FAILURE(callHandle.rc))
821 {
822 RTPrintf("guestSendStdOut failed with callHandle.rc=%Rrc\n", callHandle.rc);
823 rc = callHandle.rc;
824 }
825 return rc;
826}
827
828/** Gets a new message (command) from the host and does the appropriate action. */
829int guestGetHostMsg(VBOXHGCMSVCFNTABLE *pTable)
830{
831 VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
832 int command = GUEST_GET_HOST_MSG;
833 VBOXHGCMSVCPARM paParms[13];
834 /* Work around silly constant issues - we ought to allow passing
835 * constant strings in the hgcm parameters. */
836 pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, command,
837 13, paParms);
838 int rc;
839 if (RT_FAILURE(callHandle.rc))
840 {
841 RTPrintf("guestGetHostMsg failed with callHandle.rc=%Rrc\n", callHandle.rc);
842 rc = callHandle.rc;
843 }
844 else
845 {
846 uint32_t uCmd;
847
848 /*
849 * Parameter 0 *always* specifies the actual command the host wants
850 * to execute.
851 */
852 rc = paParms[0].getUInt32(&uCmd);
853 if (RT_SUCCESS(rc))
854 {
855 switch(uCmd)
856 {
857 case GETHOSTMSG_EXEC_CMD:
858 rc = guestExecHandleCmdExecute(pTable, paParms, 13);
859 break;
860
861 case GETHOSTMSG_EXEC_STDIN:
862 //rc = guestExecHandleCmdStdIn(pTable, paParms, 12);
863 break;
864
865 default:
866 RTPrintf("guestGetHostMsg: Invalid host command: %u\n", uCmd);
867 rc = VERR_INVALID_PARAMETER;
868 break;
869 }
870 }
871 }
872 return rc;
873}
874
875/** This represents the execution service thread in VBoxService. */
876static DECLCALLBACK(int) guestThread(RTTHREAD Thread, void *pvUser)
877{
878 VBOXHGCMSVCFNTABLE *pTable = (VBOXHGCMSVCFNTABLE*)pvUser;
879 AssertPtr(pTable);
880
881 int rc = guestGetHostMsg(pTable);
882 if(RT_FAILURE(rc))
883 return rc;
884
885 /* We don't remove the current request from the host queue yet,
886 * so don't try to get a new host message more than once atm. */
887
888 for(;;) /* Run forever atm. */
889 {
890 RTThreadSleep(1);
891 /** @tdo Add graceful shutdown here. */
892 }
893 return rc;
894}
895
896int hostExecCmd(VBOXHGCMSVCFNTABLE *pTable,
897 uint32_t u32Flags,
898 const char *pszCmd,
899 uint32_t u32Argc,
900 const void *pvArgs,
901 uint32_t cbArgs,
902 uint32_t u32Envc,
903 const void *pvEnv,
904 uint32_t cbEnv,
905 const char *pszStdin,
906 const char *pszStdOut,
907 const char *pszStdErr,
908 const char *pszUser,
909 const char *pszPassword,
910 uint32_t u32TimeLimit)
911{
912 int command = HOST_EXEC_CMD;
913 VBOXHGCMSVCPARM paParms[13];
914
915 /** @todo Put some assert macros here! */
916 paParms[0].setUInt32(HOST_EXEC_CMD);
917 paParms[1].setUInt32(u32Flags);
918 paParms[2].setPointer((void*)pszCmd, (uint32_t)strlen(pszCmd) + 1);
919 paParms[3].setUInt32(u32Argc);
920 paParms[4].setPointer((void*)pvArgs, cbArgs);
921 paParms[5].setUInt32(u32Envc);
922 paParms[6].setPointer((void*)pvEnv, cbEnv);
923 paParms[7].setPointer((void*)pszStdin, (uint32_t)strlen(pszStdin) + 1);
924 paParms[8].setPointer((void*)pszStdOut, (uint32_t)strlen(pszStdOut) + 1);
925 paParms[9].setPointer((void*)pszStdErr, (uint32_t)strlen(pszStdErr) + 1);
926 paParms[10].setPointer((void*)pszUser, (uint32_t)strlen(pszUser) + 1);
927 paParms[11].setPointer((void*)pszPassword, (uint32_t)strlen(pszPassword) + 1);
928 paParms[12].setUInt32(u32TimeLimit);
929
930 int rc = pTable->pfnHostCall(pTable->pvService, command,
931 13, paParms);
932 if (RT_FAILURE(rc))
933 {
934 RTPrintf("hostDoExecute() call failed with rc=%Rrc\n", rc);
935 }
936 return rc;
937}
938
939int hostExecGetStatus(VBOXHGCMSVCFNTABLE *pTable)
940{
941 int command = HOST_EXEC_GET_STATUS;
942 VBOXHGCMSVCPARM paParms[13];
943
944 /** @todo Put some assert macros here! */
945 paParms[0].setUInt32(0 /* @todo 0=Execute */);
946
947 return 0;
948}
949
950/**
951 * Test the EXECUTE function.
952 * @returns iprt status value to indicate whether the test went as expected.
953 * @note prints its own diagnostic information to stdout.
954 */
955int testExecute(VBOXHGCMSVCFNTABLE *pTable)
956{
957 RTPrintf("Testing the EXECUTE call.\n");
958 if (!VALID_PTR(pTable->pfnHostCall))
959 {
960 RTPrintf("Invalid pfnHostCall() pointer\n");
961 return VERR_INVALID_POINTER;
962 }
963
964 int rc = VINF_SUCCESS;
965 /*
966 * The host code (= later Main?) will run in this thread,
967 * while the client (guest) code will run in another one (= VBoxService in guest).
968 */
969 RTTHREAD threadGuest;
970 rc = RTThreadCreate(&threadGuest, guestThread, pTable /* Save call table. */,
971 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "GUEST");
972
973 /* This is the host code. */
974 if(RT_SUCCESS(rc))
975 {
976 void *pvArgs;
977 uint32_t cArgs;
978 uint32_t cbArgs;
979
980 void *pvEnv = NULL;
981 uint32_t cEnv;
982 uint32_t cbEnv;
983
984 char szCmdLine[_1K];
985
986 /* Test stdout*/
987 RTStrPrintf(szCmdLine, sizeof(szCmdLine),
988 "%s --test-stdout", g_szImageName); /* Include image name as argv[0]. */
989 rc = gctrlPrepareExecArgv(szCmdLine, &pvArgs, &cbArgs, &cArgs);
990 if (RT_SUCCESS(rc))
991 {
992 rc = gctrlAddToExecEnvv("VBOX_LOG=all", &pvEnv, &cbEnv, &cEnv);
993 if (RT_SUCCESS(rc))
994 rc = gctrlAddToExecEnvv("VBOX_LOG_DEST=file=c:\\baz\\barfoo.txt", &pvEnv, &cbEnv, &cEnv);
995 if (RT_SUCCESS(rc))
996 rc = gctrlAddToExecEnvv("HOME=iN-WoNdeRlAnd", &pvEnv, &cbEnv, &cEnv);
997 if (RT_SUCCESS(rc))
998 {
999 rc = hostExecCmd(pTable,
1000 456123,
1001 g_szImageName,
1002 cArgs,
1003 pvArgs,
1004 cbArgs,
1005 cEnv,
1006 pvEnv,
1007 cbEnv,
1008 "|",
1009 "|",
1010 "|",
1011 "vbox",
1012 "password",
1013 UINT32_MAX);
1014 RTMemFree(pvEnv);
1015 }
1016 RTMemFree(pvArgs);
1017 }
1018 if (RT_SUCCESS(rc))
1019 {
1020 for(;;)
1021 {
1022 RTThreadSleep(1);
1023 }
1024 }
1025
1026 /* Wait for guest thread to finish. */
1027 int rc2;
1028 rc = RTThreadWait(threadGuest, RT_INDEFINITE_WAIT, &rc2);
1029 if (RT_FAILURE(rc))
1030 RTPrintf("RTThreadWait failed, rc=%Rrc\n", rc);
1031 else if (RT_FAILURE(rc2))
1032 RTPrintf("Thread failed, rc2=%Rrc\n", rc2);
1033 }
1034 return rc;
1035}
1036
1037int selfTestExecStdOut(int h, int argc, char **argv)
1038{
1039 RTStrmPrintf(g_pStdOut, "This is the first text to StdOut!\n");
1040 RTStrmPrintf(g_pStdErr, "Hum, this really should be ignored because it's stderr! :-/\n");
1041 /*for (int i=0; i<10; i++)
1042 {
1043 RTStrmPrintf(g_pStdOut, "This is the i=%d to StdOut! Waiting ...\n", i+1);
1044 RTStrmPrintf(g_pStdErr, "This is the i=%d to StdErr!\n", i+1);
1045 RTThreadSleep(1000);
1046
1047 }*/
1048 RTThreadSleep(5000);
1049 return VINF_SUCCESS;
1050}
1051
1052RTEXITCODE selfTestExecStd(int h, int argc, char **argv)
1053{
1054 int rc = VINF_SUCCESS;
1055
1056#if 0
1057 /* Dump arguments and environment. */
1058 RTENV env;
1059 rc = RTEnvCreate(&env);
1060 if (RT_SUCCESS(rc))
1061 {
1062 const char *const *pcEnv = RTEnvGetExecEnvP(env);
1063 RTPrintf("Environment: %s\n" , pcEnv);
1064 rc = RTEnvDestroy(env);
1065 }
1066#endif
1067
1068 /* Do the test(s) based on the handle number(s). */
1069 switch (h)
1070 {
1071 case 0: /* stdin */
1072 break;
1073
1074 case 1: /* stdout */
1075 rc = selfTestExecStdOut(h, argc, argv);
1076 break;
1077
1078 case 2: /* stderr */
1079 break;
1080
1081 case 3: /* all std* */
1082 break;
1083 }
1084
1085 return RTEXITCODE_SUCCESS;
1086}
1087
1088/**
1089 * Parses the arguments.
1090 *
1091 * @returns Exit code. Exit if this is non-zero or @a *pfExit is set.
1092 * @param argc The number of arguments.
1093 * @param argv The argument vector.
1094 * @param pfExit For indicating exit when the exit code is zero.
1095 */
1096static RTEXITCODE parseArgv(int argc, char **argv, bool *pfExit)
1097{
1098#if 1
1099 RTPrintf("ArgC: %d\n", argc);
1100 for (int i=0; i<argc; i++)
1101 RTPrintf("ArgV: %s\n", argv[i]);
1102#endif
1103
1104 if (argc == 2)
1105 {
1106 *pfExit = true;
1107 if (!strcmp(argv[1], "--test-stdin"))
1108 return selfTestExecStd(0 /* stdin */, argc, argv);
1109 else if (!strcmp(argv[1], "--test-stdout"))
1110 return selfTestExecStd(1 /* stdout */, argc, argv);
1111 else if (!strcmp(argv[1], "--test-stderr"))
1112 return selfTestExecStd(2 /* stderr */, argc, argv);
1113 else if (!strcmp(argv[1], "--test-all"))
1114 return selfTestExecStd(3 /* all */, argc, argv);
1115
1116 RTPrintf("Unknown test! Exit.\n");
1117 return RTEXITCODE_FAILURE;
1118 }
1119 return RTEXITCODE_SUCCESS;
1120}
1121
1122int main(int argc, char **argv)
1123{
1124 int rc = RTR3Init();
1125 if (RT_FAILURE(rc))
1126 return RTMsgInitFailure(rc);
1127
1128 /* Save image name for later use. */
1129 if (!RTProcGetExecutablePath(g_szImageName, sizeof(g_szImageName)))
1130 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcGetExecutablePath failed\n");
1131
1132 VBOXHGCMSVCFNTABLE svcTable;
1133 VBOXHGCMSVCHELPERS svcHelpers;
1134
1135 initTable(&svcTable, &svcHelpers);
1136
1137 RTPrintf("PID: %u\n", RTProcSelf ());
1138
1139 bool fExit = false;
1140 rc = parseArgv(argc, argv, &fExit);
1141 if (rc || fExit)
1142 return rc;
1143
1144 /* The function is inside the service, not HGCM. */
1145 if (RT_FAILURE(VBoxHGCMSvcLoad(&svcTable)))
1146 {
1147 RTPrintf("Failed to start the HGCM service.\n");
1148 return 1;
1149 }
1150 if (RT_FAILURE(testExecute(&svcTable)))
1151 return 1;
1152 RTPrintf("tstGuestControlSvc: SUCCEEDED.\n");
1153 return 0;
1154}
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