VirtualBox

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

Last change on this file since 28087 was 27976, checked in by vboxsync, 15 years ago

*: scm cleans up whitespace and adds a new line at the end of ApplianceimplPrivate.h.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette