Changeset 36873 in vbox for trunk/src/VBox/HostServices/GuestControl/testcase
- Timestamp:
- Apr 28, 2011 8:55:09 AM (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp
r33806 r36873 6 6 7 7 /* 8 * Copyright (C) 201 0Oracle Corporation8 * Copyright (C) 2011 Oracle Corporation 9 9 * 10 10 * This file is part of VirtualBox Open Source Edition (OSE), as … … 21 21 *******************************************************************************/ 22 22 #include <VBox/HostServices/GuestControlSvc.h> 23 #include <iprt/alloca.h>24 23 #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 24 #include <iprt/stream.h> 39 #include <iprt/t hread.h>25 #include <iprt/test.h> 40 26 41 27 #include "../gctrl.h" 42 28 29 /******************************************************************************* 30 * Global Variables * 31 *******************************************************************************/ 32 static RTTEST g_hTest = NIL_RTTEST; 33 43 34 using namespace guestControl; 44 35 45 extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *pTable); 46 char g_szImageName[RTPATH_MAX]; 47 48 /** Prototypes. */ 49 int guestExecSendStdOut(VBOXHGCMSVCFNTABLE *pTable, 50 const char *pszStdOut, uint32_t cbStdOut); 36 extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable); 51 37 52 38 /** Simple call handle structure for the guest call completion callback */ 53 39 struct VBOXHGCMCALLHANDLE_TYPEDEF 54 40 { 55 /** Where to store the result code */41 /** Where to store the result code. */ 56 42 int32_t rc; 57 43 }; 58 44 59 /**60 * For buffering process input supplied by the client.61 */62 typedef struct TXSEXECSTDINBUF63 {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. */78 typedef TXSEXECSTDINBUF *PTXSEXECSTDINBUF;79 80 45 /** Call completion callback for guest calls. */ 81 46 static void callComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc) … … 86 51 /** 87 52 * Initialise the HGCM service table as much as we need to start the 88 * service 53 * service. 54 * 55 * @return IPRT status. 89 56 * @param pTable the table to initialise 90 57 */ 91 voidinitTable(VBOXHGCMSVCFNTABLE *pTable, VBOXHGCMSVCHELPERS *pHelpers)58 int initTable(VBOXHGCMSVCFNTABLE *pTable, VBOXHGCMSVCHELPERS *pHelpers) 92 59 { 93 60 pTable->cbSize = sizeof (VBOXHGCMSVCFNTABLE); … … 95 62 pHelpers->pfnCallComplete = callComplete; 96 63 pTable->pHelpers = pHelpers; 97 } 64 65 return VINF_SUCCESS; 66 } 67 68 typedef struct CMDHOST 69 { 70 /** The HGCM command to execute. */ 71 int cmd; 72 /** Number of parameters. */ 73 int num_parms; 74 /** The actual parameters. */ 75 const PVBOXHGCMSVCPARM parms; 76 /** Flag indicating whether we need a connected client for this command. */ 77 bool fNeedsClient; 78 /** The desired return value from the host. */ 79 int rc; 80 } CMDHOST, *PCMDHOST; 81 82 typedef struct CMDCLIENT 83 { 84 /** The client's ID. */ 85 int client_id; 86 /** The HGCM command to execute. */ 87 int cmd; 88 /** Number of parameters. */ 89 int num_parms; 90 /** The actual parameters. */ 91 const PVBOXHGCMSVCPARM parms; 92 /** The desired return value from the host. */ 93 int rc; 94 } CMDCLIENT, *PCMDCLIENT; 98 95 99 96 /** 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 */ 113 static 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); 97 * Tests the HOST_EXEC_CMD function. 98 * @returns iprt status value to indicate whether the test went as expected. 99 * @note prints its own diagnostic information to stdout. 100 */ 101 static int testHostCmd(const VBOXHGCMSVCFNTABLE *pTable, const PCMDHOST pCmd, uint32_t uNumTests) 102 { 103 int rc = VINF_SUCCESS; 104 if (!VALID_PTR(pTable->pfnHostCall)) 105 { 106 RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Invalid pfnHostCall() pointer\n"); 107 rc = VERR_INVALID_POINTER; 135 108 } 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 */157 static 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 */189 static 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 109 if (RT_SUCCESS(rc)) 195 110 { 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 */ 211 static 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) 111 for (unsigned i = 0; (i < uNumTests) && RT_SUCCESS(rc); i++) 219 112 { 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 */ 248 static 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 */ 320 static 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 329 static 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) 113 RTTestPrintf(g_hTest, RTTESTLVL_INFO, "Testing #%u (cmd: %d, num_parms: %d, parms: 0x%p\n", 114 i, pCmd[i].cmd, pCmd[i].num_parms, pCmd[i].parms); 115 116 if (pCmd[i].fNeedsClient) 374 117 { 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; 118 int client_rc = pTable->pfnConnect(pTable->pvService, 1000 /* Client ID */, NULL /* pvClient */); 119 if (RT_FAILURE(client_rc)) 120 rc = client_rc; 394 121 } 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 543 int 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); 122 590 123 if (RT_SUCCESS(rc)) 591 124 { 592 RTHANDLE hStdOut;593 PRTHANDLE phStdOut;594 RTPIPE hStdOutR;595 rc = guestExecSetupPipe(1 /* stdout */, &hStdOut, &phStdOut, &hStdOutR);596 if ( RT_SUCCESS(rc))125 int host_rc = pTable->pfnHostCall(pTable->pvService, 126 pCmd[i].cmd, 127 pCmd[i].num_parms, 128 pCmd[i].parms); 129 if (host_rc != pCmd[i].rc) 597 130 { 598 RTHANDLE hStdErr; 599 PRTHANDLE phStdErr; 600 RTPIPE hStdErrR; 601 rc = guestExecSetupPipe(2 /* stderr */, &hStdErr, &phStdErr, &hStdErrR); 131 RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Host call test #%u returned with rc=%Rrc instead of rc=%Rrc\n", 132 i, host_rc, pCmd[i].rc); 133 rc = host_rc; 602 134 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); 135 rc = VERR_INVALID_PARAMETER; 659 136 } 660 RTPipeClose(hStdInW); 661 RTHandleClose(phStdIn); 662 } 663 } 664 RTEnvDestroy(hEnv); 665 } 666 667 return rc; 668 } 669 670 int 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) 137 138 if (pCmd[i].fNeedsClient) 718 139 { 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 } 140 int client_rc = pTable->pfnDisconnect(pTable->pvService, 1000 /* Client ID */, NULL /* pvClient */); 141 if (RT_SUCCESS(rc)) 142 rc = client_rc; 733 143 } 734 if (RT_SUCCESS(rc))735 {736 /* stdin */737 rc = paParms[7].getString(&pcBuf, &cbLen);738 char *pszStdIn = RTStrDup(pcBuf);739 if ( pszStdIn740 && RT_SUCCESS(rc))741 {742 /* stdout */743 rc = paParms[8].getString(&pcBuf, &cbLen);744 char *pszStdOut = RTStrDup(pcBuf);745 if ( pszStdOut746 && RT_SUCCESS(rc))747 {748 /* stderr */749 rc = paParms[9].getString(&pcBuf, &cbLen);750 char *pszStdErr = RTStrDup(pcBuf);751 if ( pszStdErr752 && RT_SUCCESS(rc))753 {754 /* user */755 rc = paParms[10].getString(&pcBuf, &cbLen);756 char *pszUser = RTStrDup(pcBuf);757 if ( pszUser758 && 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. */800 int 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 == NULL808 || 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. */829 int 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 passing835 * 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 else845 {846 uint32_t uCmd;847 848 /*849 * Parameter 0 *always* specifies the actual command the host wants850 * 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 144 } 870 145 } … … 873 148 } 874 149 875 /** This represents the execution service thread in VBoxService. */ 876 static 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. */ 150 static int testHost(const VBOXHGCMSVCFNTABLE *pTable) 151 { 152 RTTestSub(g_hTest, "Testing host commands ..."); 153 154 static VBOXHGCMSVCPARM s_aParms[1]; 155 s_aParms[0].setUInt32(1000 /* Context ID */); 156 157 static CMDHOST s_aCmdHostAll[] = 158 { 159 /** No client connected, invalid command. */ 160 { 1024 /* Not existing command */, 0, 0, false, VERR_NOT_SUPPORTED }, 161 { -1 /* Invalid command */, 0, 0, false, VERR_NOT_SUPPORTED }, 162 { HOST_CANCEL_PENDING_WAITS, 1024, 0, false, VERR_NOT_FOUND }, 163 { HOST_CANCEL_PENDING_WAITS, 0, &s_aParms[0], false, VERR_NOT_FOUND }, 164 165 /** No client connected, valid command. */ 166 { HOST_CANCEL_PENDING_WAITS, 0, 0, false, VERR_NOT_FOUND }, 167 168 /** Client connected. */ 169 { 1024 /* Not existing command */, 0, 0, true, VERR_NOT_SUPPORTED }, 170 { -1 /* Invalid command */, 0, 0, true, VERR_NOT_SUPPORTED }, 171 172 /** Client connected, valid parameters given. */ 173 { HOST_CANCEL_PENDING_WAITS, 0, 0, true, VINF_SUCCESS }, 174 { HOST_CANCEL_PENDING_WAITS, 1024, &s_aParms[0], true, VINF_SUCCESS }, 175 { HOST_CANCEL_PENDING_WAITS, 0, &s_aParms[0], true, VINF_SUCCESS}, 176 177 /** Client connected, invalid parameters given. */ 178 { HOST_CANCEL_PENDING_WAITS, 1024, 0, true, VERR_INVALID_POINTER }, 179 { HOST_CANCEL_PENDING_WAITS, 1, 0, true, VERR_INVALID_POINTER }, 180 { HOST_CANCEL_PENDING_WAITS, -1, 0, true, VERR_INVALID_POINTER }, 181 182 /** Client connected, parameters given. */ 183 { HOST_CANCEL_PENDING_WAITS, 1, &s_aParms[0], true, VINF_SUCCESS }, 184 { HOST_EXEC_CMD, 1, &s_aParms[0], true, VINF_SUCCESS }, 185 { HOST_EXEC_SET_INPUT, 1, &s_aParms[0], true, VINF_SUCCESS }, 186 { HOST_EXEC_GET_OUTPUT, 1, &s_aParms[0], true, VINF_SUCCESS } 187 }; 188 189 int rc = testHostCmd(pTable, &s_aCmdHostAll[0], RT_ELEMENTS(s_aCmdHostAll)); 190 RTTestSubDone(g_hTest); 191 return rc; 192 } 193 194 static int testClient(const VBOXHGCMSVCFNTABLE *pTable) 195 { 196 RTTestSub(g_hTest, "Testing client commands ..."); 197 198 int rc = pTable->pfnConnect(pTable->pvService, 1 /* Client ID */, NULL /* pvClient */); 199 if (RT_SUCCESS(rc)) 200 { 201 VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; 202 203 /* No commands from host yet. */ 204 static VBOXHGCMSVCPARM s_aParmsGuest[8]; 205 pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, 206 GUEST_GET_HOST_MSG, 2, &s_aParmsGuest[0]); 207 RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VINF_SUCCESS, callHandle.rc); 208 209 /* Host: Add a dummy command. */ 210 static VBOXHGCMSVCPARM s_aParmsHost[8]; 211 s_aParmsHost[0].setUInt32(1000 /* Context ID */); 212 s_aParmsHost[1].setString("foo.bar"); 213 214 rc = pTable->pfnHostCall(pTable->pvService, HOST_EXEC_CMD, 2, &s_aParmsHost[0]); 215 RTTEST_CHECK_RC_RET(g_hTest, rc, VINF_SUCCESS, rc); 216 217 /* Client: Get host command with a invalid parameter count specified. */ 218 pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, 219 GUEST_GET_HOST_MSG, 1024, &s_aParmsGuest[0]); 220 RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_INVALID_PARAMETER, callHandle.rc); 221 pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, 222 GUEST_GET_HOST_MSG, -1, &s_aParmsGuest[0]); 223 RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_INVALID_PARAMETER, callHandle.rc); 224 pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, 225 GUEST_GET_HOST_MSG, -1, NULL); 226 RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_INVALID_PARAMETER, callHandle.rc); 227 pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, 228 GUEST_GET_HOST_MSG, 16, NULL); 229 RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_INVALID_PARAMETER, callHandle.rc); 230 231 /* Client: Get host command with a too small HGCM array. */ 232 pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, 233 GUEST_GET_HOST_MSG, 0, &s_aParmsGuest[0]); 234 RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_TOO_MUCH_DATA, callHandle.rc); 235 pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, 236 GUEST_GET_HOST_MSG, 1, &s_aParmsGuest[0]); 237 RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_TOO_MUCH_DATA, callHandle.rc); 238 239 /* Client: Get host command without an allocated buffer. */ 240 pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, 241 GUEST_GET_HOST_MSG, 2, &s_aParmsGuest[0]); 242 RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_BUFFER_OVERFLOW, callHandle.rc); 243 244 /* Client: Get host command, this time with a valid buffer. */ 245 char szBuf[16]; 246 s_aParmsGuest[1].setPointer(szBuf, sizeof(szBuf)); 247 pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, 248 GUEST_GET_HOST_MSG, 2, &s_aParmsGuest[0]); 249 RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VINF_SUCCESS, callHandle.rc); 250 251 /* Client: Now make sure there's no host message left anymore. */ 252 pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, 253 GUEST_GET_HOST_MSG, 2, &s_aParmsGuest[0]); 254 RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VINF_SUCCESS, callHandle.rc); 255 256 /* Client: Disconnect again. */ 257 int rc2 = pTable->pfnDisconnect(pTable->pvService, 1000 /* Client ID */, NULL /* pvClient */); 258 if (RT_SUCCESS(rc)) 259 rc = rc2; 892 260 } 261 262 RTTestSubDone(g_hTest); 893 263 return rc; 894 264 } 895 265 896 int 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 939 int 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 */ 955 int 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 1037 int 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 1052 RTEXITCODE 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 */ 1096 static 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 266 /* 267 * Set environment variable "IPRT_TEST_MAX_LEVEL=all" to get more debug output! 268 */ 1122 269 int main(int argc, char **argv) 1123 270 { 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 271 RTEXITCODE rcExit = RTTestInitAndCreate("tstGuestControlSvc", &g_hTest); 272 if (rcExit != RTEXITCODE_SUCCESS) 273 return rcExit; 274 RTTestBanner(g_hTest); 275 276 /* Some host info. */ 277 RTTestIPrintf(RTTESTLVL_ALWAYS, "sizeof(void*)=%d\n", sizeof(void*)); 278 279 /* Do the tests. */ 1132 280 VBOXHGCMSVCFNTABLE svcTable; 1133 281 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 } 282 RTTEST_CHECK_RC_RET(g_hTest, initTable(&svcTable, &svcHelpers), VINF_SUCCESS, 1); 283 284 do 285 { 286 RTTESTI_CHECK_RC_BREAK(VBoxHGCMSvcLoad(&svcTable), VINF_SUCCESS); 287 288 RTTESTI_CHECK_RC_BREAK(testHost(&svcTable), VINF_SUCCESS); 289 290 RTTESTI_CHECK_RC_BREAK(svcTable.pfnUnload(svcTable.pvService), VINF_SUCCESS); 291 292 RTTESTI_CHECK_RC_BREAK(VBoxHGCMSvcLoad(&svcTable), VINF_SUCCESS); 293 294 RTTESTI_CHECK_RC_BREAK(testClient(&svcTable), VINF_SUCCESS); 295 296 RTTESTI_CHECK_RC_BREAK(svcTable.pfnUnload(svcTable.pvService), VINF_SUCCESS); 297 298 } while (0); 299 300 return RTTestSummaryAndDestroy(g_hTest); 301 } 302
Note:
See TracChangeset
for help on using the changeset viewer.