Changeset 39279 in vbox
- Timestamp:
- Nov 11, 2011 5:50:19 PM (13 years ago)
- Location:
- trunk/src/VBox/Additions/common/VBoxService
- Files:
-
- 3 deleted
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Additions/common/VBoxService/Makefile.kmk
r38133 r39279 69 69 VBoxService_SOURCES += \ 70 70 VBoxServiceControl.cpp \ 71 VBoxServiceControlExec.cpp \ 72 VBoxServiceControlExecThread.cpp \ 73 VBoxServicePipeBuf.cpp 71 VBoxServiceControlExecThread.cpp 74 72 endif 75 73 -
trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp
r38587 r39279 39 39 /** The control interval (milliseconds). */ 40 40 uint32_t g_ControlInterval = 0; 41 /** The semaphore we're blocking o n. */42 static RTSEMEVENTMULTIg_hControlEvent = NIL_RTSEMEVENTMULTI;41 /** The semaphore we're blocking our main control thread on. */ 42 RTSEMEVENTMULTI g_hControlEvent = NIL_RTSEMEVENTMULTI; 43 43 /** The guest control service client ID. */ 44 44 static uint32_t g_GuestControlSvcClientID = 0; 45 45 /** How many started guest processes are kept into memory for supplying 46 * information to the host. Default is 5 processes. If 0 is specified,46 * information to the host. Default is 25 processes. If 0 is specified, 47 47 * the maximum number of processes is unlimited. */ 48 uint32_t g_GuestControlProcsMaxKept = 5;48 uint32_t g_GuestControlProcsMaxKept = 25; 49 49 /** List of guest control threads. */ 50 50 RTLISTNODE g_GuestControlThreads; 51 51 /** Critical section protecting g_GuestControlExecThreads. */ 52 52 RTCRITSECT g_GuestControlThreadsCritSect; 53 54 static int VBoxServiceControlStartAllowed(bool *pbAllowed); 53 55 54 56 /** @copydoc VBOXSERVICE::pfnPreInit */ … … 191 193 192 194 case HOST_EXEC_CMD: 193 rc = VBoxServiceControl ExecHandleCmdStartProcess(g_GuestControlSvcClientID, uNumParms);195 rc = VBoxServiceControlHandleCmdStartProc(g_GuestControlSvcClientID, uNumParms); 194 196 break; 195 197 196 198 case HOST_EXEC_SET_INPUT: 197 199 /** @todo Make buffer size configurable via guest properties/argv! */ 198 rc = VBoxServiceControl ExecHandleCmdSetInput(g_GuestControlSvcClientID, uNumParms, _1M /* Buffer size */);200 rc = VBoxServiceControlHandleCmdSetInput(g_GuestControlSvcClientID, uNumParms, _1M /* Buffer size */); 199 201 break; 200 202 201 203 case HOST_EXEC_GET_OUTPUT: 202 rc = VBoxServiceControl ExecHandleCmdGetOutput(g_GuestControlSvcClientID, uNumParms);204 rc = VBoxServiceControlHandleCmdGetOutput(g_GuestControlSvcClientID, uNumParms); 203 205 break; 204 206 … … 231 233 232 234 235 /** 236 * Handles starting processes on the guest. 237 * 238 * @returns IPRT status code. 239 * @param u32ClientId The HGCM client session ID. 240 * @param uNumParms The number of parameters the host is offering. 241 */ 242 int VBoxServiceControlHandleCmdStartProc(uint32_t uClientID, uint32_t uNumParms) 243 { 244 uint32_t uContextID; 245 char szCmd[_1K]; 246 uint32_t uFlags; 247 char szArgs[_1K]; 248 uint32_t uNumArgs; 249 char szEnv[_64K]; 250 uint32_t cbEnv = sizeof(szEnv); 251 uint32_t uNumEnvVars; 252 char szUser[128]; 253 char szPassword[128]; 254 uint32_t uTimeLimitMS; 255 256 #if 0 /* for valgrind */ 257 RT_ZERO(szCmd); 258 RT_ZERO(szArgs); 259 RT_ZERO(szEnv); 260 RT_ZERO(szUser); 261 RT_ZERO(szPassword); 262 #endif 263 264 if (uNumParms != 11) 265 return VERR_INVALID_PARAMETER; 266 267 int rc = VbglR3GuestCtrlExecGetHostCmd(uClientID, 268 uNumParms, 269 &uContextID, 270 /* Command */ 271 szCmd, sizeof(szCmd), 272 /* Flags */ 273 &uFlags, 274 /* Arguments */ 275 szArgs, sizeof(szArgs), &uNumArgs, 276 /* Environment */ 277 szEnv, &cbEnv, &uNumEnvVars, 278 /* Credentials */ 279 szUser, sizeof(szUser), 280 szPassword, sizeof(szPassword), 281 /* Timelimit */ 282 &uTimeLimitMS); 283 if (RT_SUCCESS(rc)) 284 { 285 #ifdef DEBUG 286 VBoxServiceVerbose(3, "ControlExec: Start process szCmd=%s, uFlags=%u, szArgs=%s, szEnv=%s, szUser=%s, szPW=%s, uTimeout=%u\n", 287 szCmd, uFlags, uNumArgs ? szArgs : "<None>", uNumEnvVars ? szEnv : "<None>", szUser, szPassword, uTimeLimitMS); 288 #endif 289 bool fAllowed = false; 290 int rc = VBoxServiceControlStartAllowed(&fAllowed); 291 if (RT_FAILURE(rc)) 292 VBoxServiceError("ControlExec: Error determining whether process can be started or not, rc=%Rrc\n", rc); 293 294 if ( RT_SUCCESS(rc) 295 && fAllowed) 296 { 297 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect); 298 if (RT_SUCCESS(rc)) 299 { 300 /** @todo Put the following params into a struct! */ 301 RTLISTNODE *pThreadNode; 302 rc = VBoxServiceControlThreadStart(uClientID, uContextID, 303 szCmd, uFlags, szArgs, uNumArgs, 304 szEnv, cbEnv, uNumEnvVars, 305 szUser, szPassword, uTimeLimitMS, 306 &pThreadNode); 307 if (RT_SUCCESS(rc)) 308 { 309 /* Insert thread node into thread list. */ 310 /*rc =*/ RTListAppend(&g_GuestControlThreads, pThreadNode); 311 } 312 313 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect); 314 if (RT_SUCCESS(rc)) 315 rc = rc2; 316 } 317 } 318 else /* Process start is not allowed due to policy settings. */ 319 { 320 /* Tell the host. */ 321 rc = VbglR3GuestCtrlExecReportStatus(uClientID, uContextID, 0 /* PID, invalid. */, 322 PROC_STS_ERROR, VERR_MAX_PROCS_REACHED, 323 NULL /* pvData */, 0 /* cbData */); 324 } 325 } 326 else 327 VBoxServiceError("ControlExec: Failed to retrieve exec start command! Error: %Rrc\n", rc); 328 return rc; 329 } 330 331 332 /** 333 * Gets output from stdout/stderr of a specified guest process. 334 * 335 * @return IPRT status code. 336 * @param uPID PID of process to retrieve the output from. 337 * @param uHandleId Stream ID (stdout = 0, stderr = 2) to get the output from. 338 * @param uTimeout Timeout (in ms) to wait for output becoming available. 339 * @param pvBuf Pointer to a pre-allocated buffer to store the output. 340 * @param cbBuf Size (in bytes) of the pre-allocated buffer. 341 * @param pcbRead Pointer to number of bytes read. Optional. 342 */ 343 int VBoxServiceControlExecGetOutput(uint32_t uPID, uint32_t uHandleId, uint32_t uTimeout, 344 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead) 345 { 346 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 347 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); 348 /* pcbRead is optional. */ 349 350 int rc = VINF_SUCCESS; 351 352 VBOXSERVICECTRLREQUEST ctrlRequest; 353 ctrlRequest.cbData = cbBuf; 354 ctrlRequest.pvData = (uint8_t*)pvBuf; 355 356 switch (uHandleId) 357 { 358 case OUTPUT_HANDLE_ID_STDERR: 359 ctrlRequest.enmType = VBOXSERVICECTRLREQUEST_STDERR_READ; 360 break; 361 362 case OUTPUT_HANDLE_ID_STDOUT: 363 case OUTPUT_HANDLE_ID_STDOUT_DEPRECATED: 364 ctrlRequest.enmType = VBOXSERVICECTRLREQUEST_STDOUT_READ; 365 break; 366 367 default: 368 rc = VERR_INVALID_PARAMETER; 369 break; 370 } 371 372 if (RT_SUCCESS(rc)) 373 rc = VBoxServiceControlExecThreadPerform(uPID, &ctrlRequest); 374 375 if (RT_SUCCESS(rc)) 376 { 377 if (pcbRead) 378 *pcbRead = ctrlRequest.cbData; 379 } 380 381 return rc; 382 } 383 384 385 /** 386 * Injects input to a specified running process. 387 * 388 * @return IPRT status code. 389 * @param uPID PID of process to set the input for. 390 * @param fPendingClose Flag indicating whether this is the last input block sent to the process. 391 * @param pvBuf Pointer to a buffer containing the actual input data. 392 * @param cbBuf Size (in bytes) of the input buffer data. 393 * @param pcbWritten Pointer to number of bytes written to the process. Optional. 394 */ 395 int VBoxServiceControlSetInput(uint32_t uPID, bool fPendingClose, 396 void *pvBuf, uint32_t cbBuf, 397 uint32_t *pcbWritten) 398 { 399 AssertPtrReturn(pvBuf, VERR_INVALID_PARAMETER); 400 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); 401 /* pcbWritten is optional. */ 402 403 int rc = VINF_SUCCESS; 404 405 VBOXSERVICECTRLREQUEST ctrlRequest; 406 ctrlRequest.cbData = cbBuf; 407 ctrlRequest.pvData = pvBuf; 408 ctrlRequest.enmType = fPendingClose 409 ? VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF : VBOXSERVICECTRLREQUEST_STDIN_WRITE; 410 if (RT_SUCCESS(rc)) 411 rc = VBoxServiceControlExecThreadPerform(uPID, &ctrlRequest); 412 413 if (RT_SUCCESS(rc)) 414 { 415 if (pcbWritten) 416 *pcbWritten = ctrlRequest.cbData; 417 } 418 419 return rc; 420 } 421 422 423 /** 424 * Handles input for a started process by copying the received data into its 425 * stdin pipe. 426 * 427 * @returns IPRT status code. 428 * @param u32ClientId The HGCM client session ID. 429 * @param uNumParms The number of parameters the host is offering. 430 * @param cMaxBufSize The maximum buffer size for retrieving the input data. 431 */ 432 int VBoxServiceControlHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize) 433 { 434 uint32_t uContextID; 435 uint32_t uPID; 436 uint32_t uFlags; 437 uint32_t cbSize; 438 439 AssertReturn(RT_IS_POWER_OF_TWO(cbMaxBufSize), VERR_INVALID_PARAMETER); 440 uint8_t *pabBuffer = (uint8_t*)RTMemAlloc(cbMaxBufSize); 441 AssertPtrReturn(pabBuffer, VERR_NO_MEMORY); 442 443 uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status sent back to the host. */ 444 uint32_t cbWritten = 0; /* Number of bytes written to the guest. */ 445 446 /* 447 * Ask the host for the input data. 448 */ 449 int rc = VbglR3GuestCtrlExecGetHostCmdInput(u32ClientId, uNumParms, 450 &uContextID, &uPID, &uFlags, 451 pabBuffer, cbMaxBufSize, &cbSize); 452 if (RT_FAILURE(rc)) 453 { 454 VBoxServiceError("ControlExec: [PID %u]: Failed to retrieve exec input command! Error: %Rrc\n", 455 uPID, rc); 456 } 457 else if (cbSize > cbMaxBufSize) 458 { 459 VBoxServiceError("ControlExec: [PID %u]: Too much input received! cbSize=%u, cbMaxBufSize=%u\n", 460 uPID, cbSize, cbMaxBufSize); 461 rc = VERR_INVALID_PARAMETER; 462 } 463 else 464 { 465 /* 466 * Is this the last input block we need to deliver? Then let the pipe know ... 467 */ 468 bool fPendingClose = false; 469 if (uFlags & INPUT_FLAG_EOF) 470 { 471 fPendingClose = true; 472 VBoxServiceVerbose(4, "ControlExec: [PID %u]: Got last input block of size %u ...\n", 473 uPID, cbSize); 474 } 475 476 rc = VBoxServiceControlSetInput(uPID, fPendingClose, pabBuffer, 477 cbSize, &cbWritten); 478 VBoxServiceVerbose(4, "ControlExec: [PID %u]: Written input, rc=%Rrc, uFlags=0x%x, fPendingClose=%d, cbSize=%u, cbWritten=%u\n", 479 uPID, rc, uFlags, fPendingClose, cbSize, cbWritten); 480 if (RT_SUCCESS(rc)) 481 { 482 if (cbWritten || !cbSize) /* Did we write something or was there anything to write at all? */ 483 { 484 uStatus = INPUT_STS_WRITTEN; 485 uFlags = 0; 486 } 487 } 488 else 489 { 490 if (rc == VERR_BAD_PIPE) 491 uStatus = INPUT_STS_TERMINATED; 492 else if (rc == VERR_BUFFER_OVERFLOW) 493 uStatus = INPUT_STS_OVERFLOW; 494 } 495 } 496 RTMemFree(pabBuffer); 497 498 /* 499 * If there was an error and we did not set the host status 500 * yet, then do it now. 501 */ 502 if ( RT_FAILURE(rc) 503 && uStatus == INPUT_STS_UNDEFINED) 504 { 505 uStatus = INPUT_STS_ERROR; 506 uFlags = rc; 507 } 508 Assert(uStatus > INPUT_STS_UNDEFINED); 509 510 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Input processed, CID=%u, uStatus=%u, uFlags=0x%x, cbWritten=%u\n", 511 uPID, uContextID, uStatus, uFlags, cbWritten); 512 513 /* Note: Since the context ID is unique the request *has* to be completed here, 514 * regardless whether we got data or not! Otherwise the progress object 515 * on the host never will get completed! */ 516 rc = VbglR3GuestCtrlExecReportStatusIn(u32ClientId, uContextID, uPID, 517 uStatus, uFlags, (uint32_t)cbWritten); 518 519 if (RT_FAILURE(rc)) 520 VBoxServiceError("ControlExec: [PID %u]: Failed to report input status! Error: %Rrc\n", 521 uPID, rc); 522 return rc; 523 } 524 525 526 /** 527 * Handles the guest control output command. 528 * 529 * @return IPRT status code. 530 * @param u32ClientId idClient The HGCM client session ID. 531 * @param uNumParms cParms The number of parameters the host is 532 * offering. 533 */ 534 int VBoxServiceControlHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms) 535 { 536 uint32_t uContextID; 537 uint32_t uPID; 538 uint32_t uHandleID; 539 uint32_t uFlags; 540 541 int rc = VbglR3GuestCtrlExecGetHostCmdOutput(u32ClientId, uNumParms, 542 &uContextID, &uPID, &uHandleID, &uFlags); 543 if (RT_SUCCESS(rc)) 544 { 545 uint32_t cbRead = 0; 546 uint8_t *pBuf = (uint8_t*)RTMemAlloc(_64K); 547 if (pBuf) 548 { 549 rc = VBoxServiceControlExecGetOutput(uPID, uHandleID, RT_INDEFINITE_WAIT /* Timeout */, 550 pBuf, _64K /* cbSize */, &cbRead); 551 if (RT_SUCCESS(rc)) 552 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Got output, CID=%u, cbRead=%u, uHandle=%u, uFlags=%u\n", 553 uPID, uContextID, cbRead, uHandleID, uFlags); 554 else 555 VBoxServiceError("ControlExec: [PID %u]: Failed to retrieve output, CID=%u, uHandle=%u, rc=%Rrc\n", 556 uPID, uContextID, uHandleID, rc); 557 /* Note: Since the context ID is unique the request *has* to be completed here, 558 * regardless whether we got data or not! Otherwise the progress object 559 * on the host never will get completed! */ 560 /* cbRead now contains actual size. */ 561 int rc2 = VbglR3GuestCtrlExecSendOut(u32ClientId, uContextID, uPID, uHandleID, uFlags, 562 pBuf, cbRead); 563 if (RT_SUCCESS(rc)) 564 rc = rc2; 565 RTMemFree(pBuf); 566 } 567 else 568 rc = VERR_NO_MEMORY; 569 } 570 571 if (RT_FAILURE(rc)) 572 VBoxServiceError("ControlExec: [PID %u]: Error handling output command! Error: %Rrc\n", 573 uPID, rc); 574 return rc; 575 } 576 577 233 578 /** @copydoc VBOXSERVICE::pfnStop */ 234 579 static DECLCALLBACK(void) VBoxServiceControlStop(void) … … 236 581 VBoxServiceVerbose(3, "Control: Stopping ...\n"); 237 582 238 /** @todo Later, figure what to do if we're in RTProcWait(). it's a very583 /** @todo Later, figure what to do if we're in RTProcWait(). It's a very 239 584 * annoying call since doesn't support timeouts in the posix world. */ 240 585 RTSemEventMultiSignal(g_hControlEvent); … … 253 598 254 599 255 void VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread)256 {257 AssertPtrReturnVoid(pThread);258 ASMAtomicXchgBool(&pThread->fShutdown, true);259 }260 261 262 int VBoxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread)263 {264 AssertPtrReturn(pThread, VERR_INVALID_POINTER);265 int rc = VINF_SUCCESS;266 if ( pThread->Thread != NIL_RTTHREAD267 && !pThread->fShutdown) /* Only shutdown threads which aren't yet. */268 {269 /* Wait a bit ... */270 rc = RTThreadWait(pThread->Thread, 30 * 1000 /* Wait 30 seconds max. */, NULL);271 }272 return rc;273 }274 275 276 600 static void VBoxServiceControlDestroyThreads(void) 277 601 { … … 282 606 { 283 607 /* Signal all threads that we want to shutdown. */ 284 PVBOXSERVICECTRLTHREAD p Node;285 RTListForEach(&g_GuestControlThreads, p Node, VBOXSERVICECTRLTHREAD, Node)286 VBoxServiceControlThreadSignalShutdown(p Node);608 PVBOXSERVICECTRLTHREAD pThread; 609 RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node) 610 VBoxServiceControlThreadSignalShutdown(pThread); 287 611 288 612 /* Wait for threads to shutdown. */ 289 RTListForEach(&g_GuestControlThreads, p Node, VBOXSERVICECTRLTHREAD, Node)290 { 291 int rc2 = VBoxServiceControl ThreadWaitForShutdown(pNode);613 RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node) 614 { 615 int rc2 = VBoxServiceControlExecThreadShutdown(pThread); 292 616 if (RT_FAILURE(rc2)) 293 VBoxServiceError("Control: Thread failed to stop; rc2=%Rrc\n", rc2); 294 295 /* Destroy thread specific data. */ 296 switch (pNode->enmType) 297 { 298 case kVBoxServiceCtrlThreadDataExec: 299 VBoxServiceControlExecThreadDataDestroy((PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData); 300 break; 301 302 default: 303 break; 304 } 617 VBoxServiceError("Control: Guest process thread failed to stop; rc2=%Rrc\n", rc2); 305 618 } 306 619 307 620 /* Finally destroy thread list. */ 308 p Node= RTListGetFirst(&g_GuestControlThreads, VBOXSERVICECTRLTHREAD, Node);309 while (p Node)310 { 311 PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&p Node->Node, VBOXSERVICECTRLTHREAD, Node);312 bool fLast = RTListNodeIsLast(&g_GuestControlThreads, &p Node->Node);313 314 RTListNodeRemove(&pNode->Node);315 RTMemFree(pNode);621 pThread = RTListGetFirst(&g_GuestControlThreads, VBOXSERVICECTRLTHREAD, Node); 622 while (pThread) 623 { 624 PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pThread->Node, VBOXSERVICECTRLTHREAD, Node); 625 bool fLast = RTListNodeIsLast(&g_GuestControlThreads, &pThread->Node); 626 627 VBoxServiceControlRemoveThread(pThread); 628 VBoxServiceControlExecThreadDestroy(pThread); 316 629 317 630 if (fLast) 318 631 break; 319 632 320 p Node= pNext;633 pThread = pNext; 321 634 } 322 635 … … 325 638 rc = rc2; 326 639 } 640 327 641 RTCritSectDelete(&g_GuestControlThreadsCritSect); 328 642 } … … 343 657 RTSemEventMultiDestroy(g_hControlEvent); 344 658 g_hControlEvent = NIL_RTSEMEVENTMULTI; 659 } 660 } 661 662 663 static int VBoxServiceControlStartAllowed(bool *pbAllowed) 664 { 665 AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER); 666 667 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect); 668 if (RT_SUCCESS(rc)) 669 { 670 /* 671 * Check if we're respecting our memory policy by checking 672 * how many guest processes are started and served already. 673 */ 674 bool fLimitReached = false; 675 if (g_GuestControlProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */ 676 { 677 uint32_t uProcsRunning = 0; 678 uint32_t uProcsStopped = 0; 679 PVBOXSERVICECTRLTHREAD pThread; 680 RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node) 681 { 682 // THREAD LOCKING!! 683 Assert(pThread->fStarted != pThread->fStopped); 684 if (pThread->fStarted) 685 uProcsRunning++; 686 else if (pThread->fStopped) 687 uProcsStopped++; 688 else 689 AssertMsgFailed(("ControlExec: Guest process neither started nor stopped!?\n")); 690 } 691 692 VBoxServiceVerbose(2, "ControlExec: Maximum served guest processes set to %u, running=%u, stopped=%u\n", 693 g_GuestControlProcsMaxKept, uProcsRunning, uProcsStopped); 694 695 int32_t iProcsLeft = (g_GuestControlProcsMaxKept - uProcsRunning - 1); 696 if (iProcsLeft < 0) 697 { 698 VBoxServiceVerbose(3, "ControlExec: Maximum running guest processes reached (%u)\n", 699 g_GuestControlProcsMaxKept); 700 fLimitReached = true; 701 } 702 } 703 704 *pbAllowed = !fLimitReached; 705 706 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect); 707 if (RT_SUCCESS(rc)) 708 rc = rc2; 709 } 710 711 return rc; 712 } 713 714 715 /** 716 * Finds a (formerly) started process given by its PID. 717 * 718 * @return PVBOXSERVICECTRLTHREAD Process structure if found, otherwise NULL. 719 * @param uPID PID to search for. 720 */ 721 const PVBOXSERVICECTRLTHREAD VBoxServiceControlGetThreadByPID(uint32_t uPID) 722 { 723 PVBOXSERVICECTRLTHREAD pNode = NULL; 724 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect); 725 if (RT_SUCCESS(rc)) 726 { 727 PVBOXSERVICECTRLTHREAD pNodeCur; 728 RTListForEach(&g_GuestControlThreads, pNodeCur, VBOXSERVICECTRLTHREAD, Node) 729 { 730 if (pNodeCur->uPID == uPID) 731 { 732 pNode = pNodeCur; 733 break; 734 } 735 } 736 737 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect); 738 if (RT_SUCCESS(rc)) 739 rc = rc2; 740 } 741 742 return pNode; 743 } 744 745 746 void VBoxServiceControlRemoveThread(PVBOXSERVICECTRLTHREAD pThread) 747 { 748 if (!pThread) 749 return; 750 751 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect); 752 if (RT_SUCCESS(rc)) 753 { 754 RTListNodeRemove(&pThread->Node); 755 756 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect); 757 if (RT_SUCCESS(rc)) 758 rc = rc2; 345 759 } 346 760 } … … 357 771 "Host-driven Guest Control", 358 772 /* pszUsage. */ 359 " [--control-interval <ms>] [--control-procs-max-kept <x>]" 773 " [--control-interval <ms>] [--control-procs-max-kept <x>]\n" 774 " [--control-procs-mem-std[in|out|err] <KB>]" 360 775 , 361 776 /* pszOptions. */ … … 364 779 " --control-procs-max-kept\n" 365 780 " Specifies how many started guest processes are\n" 366 " kept into memory to work with. \n"781 " kept into memory to work with. Default is 25.\n" 367 782 , 368 783 /* methods */ -
trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.cpp
r38870 r39279 1 1 /* $Id$ */ 2 2 /** @file 3 * VBoxServiceControlExecThread - Thread for an executed guest process.3 * VBoxServiceControlExecThread - Thread for every started guest process. 4 4 */ 5 5 … … 22 22 #include <iprt/asm.h> 23 23 #include <iprt/assert.h> 24 #include <iprt/env.h> 24 25 #include <iprt/getopt.h> 26 #include <iprt/handle.h> 25 27 #include <iprt/mem.h> 28 #include <iprt/path.h> 26 29 #include <iprt/pipe.h> 30 #include <iprt/poll.h> 31 #include <iprt/process.h> 27 32 #include <iprt/semaphore.h> 28 33 #include <iprt/string.h> 29 34 #include <iprt/thread.h> 35 36 #include <VBox/VBoxGuestLib.h> 30 37 #include <VBox/HostServices/GuestControlSvc.h> 31 38 32 #include "VBoxService PipeBuf.h"39 #include "VBoxServiceInternal.h" 33 40 #include "VBoxServiceControlExecThread.h" 34 41 35 extern uint32_t g_GuestControlProcsMaxKept;36 extern RTLISTNODE g_GuestControlThreads; 37 extern RTCRITSECT g_GuestControlThreadsCritSect; 38 39 PVBOXSERVICECTRLTHREAD vboxServiceControlExecThreadGetByPID(uint32_t uPID);40 int VBoxServiceControlExecThreadShutdown(const PVBOXSERVICECTRLTHREAD pThread);42 using namespace guestControl; 43 44 /* Internal functions. */ 45 int vboxServiceControlExecThreadAssignPID(PVBOXSERVICECTRLTHREAD pData, uint32_t uPID); 46 void vboxServiceControlExecThreadFree(PVBOXSERVICECTRLTHREAD pData); 47 int vboxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread); 41 48 42 49 /** … … 73 80 74 81 pThread->fShutdown = false; 75 pThread->fStarted = false;76 pThread->fStopped = false;82 pThread->fStarted = false; 83 pThread->fStopped = false; 77 84 78 85 pThread->uContextID = u32ContextID; 79 86 /* ClientID will be assigned when thread is started! */ 80 87 81 /* Specific stuff. */ 82 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREADDATAEXEC)); 83 if (pData == NULL) 84 return VERR_NO_MEMORY; 85 86 pData->uPID = 0; /* Don't have a PID yet. */ 87 pData->pszCmd = RTStrDup(pszCmd); 88 pData->uFlags = uFlags; 89 pData->uNumEnvVars = 0; 90 pData->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */ 88 int rc = RTCritSectInit(&pThread->CritSect); 89 if (RT_FAILURE(rc)) 90 return rc; 91 92 rc = RTSemEventMultiCreate(&pThread->RequestEvent); 93 AssertRCReturn(rc, rc); 94 95 pThread->uPID = 0; /* Don't have a PID yet. */ 96 pThread->pszCmd = RTStrDup(pszCmd); 97 pThread->uFlags = uFlags; 98 pThread->uTimeLimitMS = ( uTimeLimitMS == UINT32_MAX 99 || uTimeLimitMS == 0) 100 ? RT_INDEFINITE_WAIT : uTimeLimitMS; 91 101 92 102 /* Prepare argument list. */ 93 int rc = RTGetOptArgvFromString(&pData->papszArgs, (int*)&pData->uNumArgs, 94 (uNumArgs > 0) ? pszArgs : "", NULL); 103 pThread->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */ 104 rc = RTGetOptArgvFromString(&pThread->papszArgs, (int*)&pThread->uNumArgs, 105 (uNumArgs > 0) ? pszArgs : "", NULL); 95 106 /* Did we get the same result? */ 96 Assert(uNumArgs == p Data->uNumArgs);107 Assert(uNumArgs == pThread->uNumArgs); 97 108 98 109 if (RT_SUCCESS(rc)) 99 110 { 100 111 /* Prepare environment list. */ 112 pThread->uNumEnvVars = 0; 101 113 if (uNumEnvVars) 102 114 { 103 p Data->papszEnv = (char **)RTMemAlloc(uNumEnvVars * sizeof(char*));104 AssertPtr(p Data->papszEnv);105 p Data->uNumEnvVars = uNumEnvVars;115 pThread->papszEnv = (char **)RTMemAlloc(uNumEnvVars * sizeof(char*)); 116 AssertPtr(pThread->papszEnv); 117 pThread->uNumEnvVars = uNumEnvVars; 106 118 107 119 const char *pszCur = pszEnv; … … 116 128 break; 117 129 } 118 int cbStr = RTStrAPrintf(&p Data->papszEnv[i++], "%s", pszCur);130 int cbStr = RTStrAPrintf(&pThread->papszEnv[i++], "%s", pszCur); 119 131 if (cbStr < 0) 120 132 { … … 127 139 } 128 140 129 pData->pszUser = RTStrDup(pszUser); 130 pData->pszPassword = RTStrDup(pszPassword); 131 pData->uTimeLimitMS = uTimeLimitMS; 132 133 /* Adjust time limit value. */ 134 pData->uTimeLimitMS = ( uTimeLimitMS == UINT32_MAX 135 || uTimeLimitMS == 0) 136 ? RT_INDEFINITE_WAIT : uTimeLimitMS; 137 138 /* Init buffers. */ 139 rc = VBoxServicePipeBufInit(&pData->stdOut, VBOXSERVICECTRLPIPEID_STDOUT, 140 false /*fNeedNotificationPipe*/); 141 /* User management. */ 142 pThread->pszUser = RTStrDup(pszUser); 143 pThread->pszPassword = RTStrDup(pszPassword); 144 } 145 146 if (RT_FAILURE(rc)) /* Clean up on failure. */ 147 vboxServiceControlExecThreadFree(pThread); 148 return rc; 149 } 150 151 152 /** 153 * TODO 154 * 155 * @param pThread 156 */ 157 void VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread) 158 { 159 AssertPtrReturnVoid(pThread); 160 161 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Signalling shutdown ...\n", 162 pThread->uPID); 163 164 /* First, set the shutdown flag. */ 165 ASMAtomicXchgBool(&pThread->fShutdown, true); 166 167 VBOXSERVICECTRLREQUEST ctrlRequest; 168 RT_ZERO(ctrlRequest); 169 ctrlRequest.enmType = VBOXSERVICECTRLREQUEST_QUIT; 170 171 int rc = VBoxServiceControlExecThreadPerform(pThread->uPID, &ctrlRequest); 172 if (RT_FAILURE(rc)) 173 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Sending quit request failed with rc=%Rrc\n", 174 pThread->uPID, rc); 175 } 176 177 178 static int vboxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread) 179 { 180 AssertPtrReturn(pThread, VERR_INVALID_POINTER); 181 int rc = VINF_SUCCESS; 182 if ( pThread->Thread != NIL_RTTHREAD 183 && !pThread->fShutdown) /* Only shutdown threads which aren't yet. */ 184 { 185 /* Wait a bit ... */ 186 rc = RTThreadWait(pThread->Thread, 30 * 1000 /* Wait 30 seconds max. */, NULL); 187 } 188 return rc; 189 } 190 191 192 /** 193 * Frees an allocated thread data structure along with all its allocated parameters. 194 * 195 * @param pThread Pointer to thread data to free. 196 */ 197 static void vboxServiceControlExecThreadFree(const PVBOXSERVICECTRLTHREAD pThread) 198 { 199 if (pThread) 200 { 201 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Freeing thread data ...\n", 202 pThread->uPID); 203 204 RTStrFree(pThread->pszCmd); 205 if (pThread->uNumEnvVars) 206 { 207 for (uint32_t i = 0; i < pThread->uNumEnvVars; i++) 208 RTStrFree(pThread->papszEnv[i]); 209 RTMemFree(pThread->papszEnv); 210 } 211 RTGetOptArgvFree(pThread->papszArgs); 212 RTStrFree(pThread->pszUser); 213 RTStrFree(pThread->pszPassword); 214 } 215 } 216 217 218 /** 219 * Closes the stdin pipe of a guest process. 220 * 221 * @return IPRT status code. 222 * @param hPollSet The polling set. 223 * @param phStdInW The standard input pipe handle. 224 */ 225 static int VBoxServiceControlExecProcCloseStdIn(RTPOLLSET hPollSet, PRTPIPE phStdInW) 226 { 227 int rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN); 228 if (rc != VERR_POLL_HANDLE_ID_NOT_FOUND) 229 AssertRC(rc); 230 231 rc = RTPipeClose(*phStdInW); 232 AssertRC(rc); 233 *phStdInW = NIL_RTPIPE; 234 235 return rc; 236 } 237 238 239 /** 240 * Handle an error event on standard input. 241 * 242 * @return IPRT status code. 243 * @param hPollSet The polling set. 244 * @param fPollEvt The event mask returned by RTPollNoResume. 245 * @param phStdInW The standard input pipe handle. 246 */ 247 static int VBoxServiceControlExecProcHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW) 248 { 249 NOREF(fPollEvt); 250 251 return VBoxServiceControlExecProcCloseStdIn(hPollSet, phStdInW); 252 } 253 254 255 /** 256 * Handle pending output data or error on standard out or standard error. 257 * 258 * @returns IPRT status code from client send. 259 * @param hPollSet The polling set. 260 * @param fPollEvt The event mask returned by RTPollNoResume. 261 * @param phPipeR The pipe handle. 262 * @param idPollHnd The pipe ID to handle. 263 * 264 */ 265 static int VBoxServiceControlExecProcHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, 266 PRTPIPE phPipeR, uint32_t idPollHnd) 267 { 268 int rc = VINF_SUCCESS; 269 270 #if 0 271 if (fPollEvt & RTPOLL_EVT_READ) 272 { 273 /** @todo Later: Notify the host about the read operation! */ 274 275 /* Make sure we go another poll round in case there was too much data 276 for the buffer to hold. */ 277 fPollEvt &= RTPOLL_EVT_ERROR; 278 279 /* Remove read event from poll set and just poll for errors from now on. 280 * This is necessary for doing a RTPipeRead. */ 281 rc = RTPollSetEventsChange(hPollSet, idPollHnd, RTPOLL_EVT_ERROR); 282 AssertRC(rc); 283 } 284 #endif 285 286 #ifdef DEBUG_andy 287 VBoxServiceVerbose(4, "ControlExec: HandleOutputEvent fPollEvt=0x%x, idPollHnd=%u\n", 288 fPollEvt, idPollHnd); 289 #endif 290 291 /* 292 * If an error was raised signalled 293 */ 294 if (fPollEvt & RTPOLL_EVT_ERROR) 295 { 296 rc = RTPollSetRemove(hPollSet, idPollHnd); 297 AssertRC(rc); 298 299 rc = RTPipeClose(*phPipeR); 300 AssertRC(rc); 301 *phPipeR = NIL_RTPIPE; 302 } 303 304 return rc; 305 } 306 307 308 int VBoxServiceControlExecProcHandleIPCNotify(RTPOLLSET hPollSet, uint32_t fPollEvt, 309 PRTPIPE phNotificationPipeR) 310 { 311 #ifdef DEBUG_andy 312 VBoxServiceVerbose(4, "ControlExec: HandleIPCNotify\n"); 313 #endif 314 /* Drain the notification pipe. */ 315 uint8_t abBuf[8]; 316 size_t cbIgnore; 317 int rc = RTPipeRead(*phNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore); 318 if (RT_FAILURE(rc)) 319 VBoxServiceError("ControlExec: Draining IPC notification pipe failed with rc=%Rrc\n", rc); 320 return rc; 321 } 322 323 324 static int VBoxServiceControlExecHandleIPCRequest(RTPOLLSET hPollSet, uint32_t fPollEvt, 325 PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR, 326 PVBOXSERVICECTRLREQUEST pRequest) 327 { 328 #ifdef DEBUG_andy 329 VBoxServiceVerbose(4, "ControlExec: HandleIPCRequest\n"); 330 #endif 331 332 AssertPtrReturn(phStdInW, VERR_INVALID_POINTER); 333 AssertPtrReturn(phStdOutR, VERR_INVALID_POINTER); 334 AssertPtrReturn(phStdErrR, VERR_INVALID_POINTER); 335 AssertPtrReturn(pRequest, VERR_INVALID_POINTER); 336 337 int rc = VINF_SUCCESS; 338 int rcReq = VINF_SUCCESS; /* Actual request result. */ 339 340 switch (pRequest->enmType) 341 { 342 case VBOXSERVICECTRLREQUEST_QUIT: /* Main control asked us to quit. */ 343 /** @todo Check for some conditions to check to 344 * veto quitting. */ 345 pRequest->rc = VINF_SUCCESS; 346 break; 347 348 case VBOXSERVICECTRLREQUEST_STDIN_WRITE: 349 /* Fall through is intentional. */ 350 case VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF: 351 { 352 AssertPtrReturn(pRequest->pvData, VERR_INVALID_POINTER); 353 AssertReturn(pRequest->cbData, VERR_INVALID_PARAMETER); 354 355 size_t cbWritten = 0; 356 if (*phStdInW != NIL_RTPIPE) 357 { 358 rcReq = RTPipeWrite(*phStdInW, 359 pRequest->pvData, pRequest->cbData, &cbWritten); 360 } 361 else 362 rcReq = VINF_EOF; 363 364 /* If this is the last write we need to close the stdin pipe on our 365 * end and remove it from the poll set. */ 366 if (VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF == pRequest->enmType) 367 rc = VBoxServiceControlExecProcCloseStdIn(hPollSet, phStdInW); 368 369 /* Reqport back actual data written (if any). */ 370 pRequest->cbData = cbWritten; 371 break; 372 } 373 374 case VBOXSERVICECTRLREQUEST_STDOUT_READ: 375 /* Fall through is intentional. */ 376 case VBOXSERVICECTRLREQUEST_STDERR_READ: 377 { 378 AssertPtrReturn(pRequest->pvData, VERR_INVALID_POINTER); 379 AssertReturn(pRequest->cbData, VERR_INVALID_PARAMETER); 380 381 PRTPIPE pPipeR = pRequest->enmType == VBOXSERVICECTRLREQUEST_STDERR_READ 382 ? phStdErrR : phStdOutR; 383 AssertPtr(pPipeR); 384 385 size_t cbRead = 0; 386 if (*pPipeR != NIL_RTPIPE) 387 { 388 rcReq = RTPipeRead(*pPipeR, 389 pRequest->pvData, pRequest->cbData, &cbRead); 390 if (rcReq == VERR_BROKEN_PIPE) 391 rcReq = VINF_EOF; 392 } 393 else 394 rcReq = VINF_EOF; 395 396 /* Report back actual data read (if any). */ 397 pRequest->cbData = cbRead; 398 break; 399 } 400 401 default: 402 rcReq = VERR_INVALID_PARAMETER; 403 break; 404 } 405 406 pRequest->rc = RT_SUCCESS(rc) 407 ? rcReq : rc; 408 409 VBoxServiceVerbose(4, "ControlExec: Handled IPC request with rcReq=%Rrc, enmType=%u, cbData=%u\n", 410 rcReq, pRequest->enmType, pRequest->cbData); 411 return rc; 412 } 413 414 415 /** 416 * Execution loop which runs in a dedicated per-started-process thread and 417 * handles all pipe input/output and signalling stuff. 418 * 419 * @return IPRT status code. 420 * @param pThread The process' thread handle. 421 * @param hProcess The actual process handle. 422 * @param cMsTimeout Time limit (in ms) of the process' life time. 423 * @param hPollSet The poll set to use. 424 * @param hStdInW Handle to the process' stdin write end. 425 * @param hStdOutR Handle to the process' stdout read end. 426 * @param hStdErrR Handle to the process' stderr read end. 427 */ 428 static int VBoxServiceControlExecProcLoop(PVBOXSERVICECTRLTHREAD pThread, 429 RTPROCESS hProcess, RTMSINTERVAL cMsTimeout, RTPOLLSET hPollSet, 430 PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR) 431 { 432 AssertPtrReturn(pThread, VERR_INVALID_POINTER); 433 AssertPtrReturn(phStdInW, VERR_INVALID_PARAMETER); 434 AssertPtrReturn(phStdOutR, VERR_INVALID_PARAMETER); 435 AssertPtrReturn(phStdErrR, VERR_INVALID_PARAMETER); 436 437 int rc; 438 int rc2; 439 uint64_t const MsStart = RTTimeMilliTS(); 440 RTPROCSTATUS ProcessStatus = { 254, RTPROCEXITREASON_ABEND }; 441 bool fProcessAlive = true; 442 bool fProcessTimedOut = false; 443 uint64_t MsProcessKilled = UINT64_MAX; 444 RTMSINTERVAL const cMsPollBase = *phStdInW != NIL_RTPIPE 445 ? 100 /* Need to poll for input. */ 446 : 1000; /* Need only poll for process exit and aborts. */ 447 RTMSINTERVAL cMsPollCur = 0; 448 449 /* 450 * Assign PID to thread data. 451 * Also check if there already was a thread with the same PID and shut it down -- otherwise 452 * the first (stale) entry will be found and we get really weird results! 453 */ 454 rc = vboxServiceControlExecThreadAssignPID(pThread, hProcess); 455 if (RT_FAILURE(rc)) 456 { 457 VBoxServiceError("ControlExec: Unable to assign PID to new thread, rc=%Rrc\n", rc); 458 return rc; 459 } 460 461 /* 462 * Before entering the loop, tell the host that we've started the guest 463 * and that it's now OK to send input to the process. 464 */ 465 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Process started, CID=%u, User=%s\n", 466 pThread->uPID, pThread->uContextID, pThread->pszUser); 467 rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, 468 pThread->uPID, PROC_STS_STARTED, 0 /* u32Flags */, 469 NULL /* pvData */, 0 /* cbData */); 470 471 /* 472 * Process input, output, the test pipe and client requests. 473 */ 474 while ( RT_SUCCESS(rc) 475 && RT_UNLIKELY(!pThread->fShutdown)) 476 { 477 /* 478 * Wait/Process all pending events. 479 */ 480 uint32_t idPollHnd; 481 uint32_t fPollEvt; 482 rc2 = RTPollNoResume(hPollSet, /*cMsPollCur*/ RT_INDEFINITE_WAIT, &fPollEvt, &idPollHnd); 483 if (pThread->fShutdown) 484 continue; 485 486 cMsPollCur = 0; /* No rest until we've checked everything. */ 487 488 if (RT_SUCCESS(rc2)) 489 { 490 /*VBoxServiceVerbose(4, "ControlExec: [PID %u}: RTPollNoResume idPollHnd=%u\n", 491 pThread->uPID, idPollHnd);*/ 492 switch (idPollHnd) 493 { 494 case VBOXSERVICECTRLPIPEID_STDIN: 495 rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW); 496 break; 497 498 case VBOXSERVICECTRLPIPEID_STDOUT: 499 rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt, 500 phStdOutR, idPollHnd); 501 break; 502 503 case VBOXSERVICECTRLPIPEID_STDERR: 504 rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt, 505 phStdErrR, idPollHnd); 506 break; 507 508 case VBOXSERVICECTRLPIPEID_IPC_NOTIFY: 509 rc = VBoxServiceControlExecProcHandleIPCNotify(hPollSet, fPollEvt, 510 &pThread->hNotificationPipeR); 511 /* Fall through is intentional. */ 512 default: 513 /* Handle IPC requests. */ 514 if (RT_SUCCESS(rc)) 515 { 516 rc = VBoxServiceControlExecHandleIPCRequest(hPollSet, fPollEvt, 517 phStdInW, phStdOutR, phStdErrR, 518 pThread->pRequest); 519 } 520 521 /* If we were asked to terminate do so ... */ 522 if (pThread->pRequest->enmType == VBOXSERVICECTRLREQUEST_QUIT) 523 rc = VINF_EOF; 524 525 /* In any case, regardless of the result, we notify 526 * the main guest control to unblock it. */ 527 int rc2 = RTSemEventMultiSignal(pThread->RequestEvent); 528 AssertRC(rc2); 529 /* No access to pRequest here anymore -- could be out of scope 530 * or modified already! */ 531 break; 532 } 533 if (RT_FAILURE(rc) || rc == VINF_EOF) 534 break; /* Abort command, or client dead or something. */ 535 536 /* Only notification pipe left? Then there's nothing to poll for anymore really. 537 * Bail out .. */ 538 if (RTPollSetGetCount(hPollSet) > 1) 539 continue; 540 } 541 542 /* 543 * Check for process death. 544 */ 545 if (fProcessAlive) 546 { 547 rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus); 548 if (RT_SUCCESS_NP(rc2)) 549 { 550 fProcessAlive = false; 551 continue; 552 } 553 if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED)) 554 continue; 555 if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND)) 556 { 557 fProcessAlive = false; 558 ProcessStatus.enmReason = RTPROCEXITREASON_ABEND; 559 ProcessStatus.iStatus = 255; 560 AssertFailed(); 561 } 562 else 563 AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2)); 564 } 565 566 /* 567 * If the process has terminated, we're should head out. 568 */ 569 if (!fProcessAlive) 570 break; 571 572 /* 573 * Check for timed out, killing the process. 574 */ 575 uint32_t cMilliesLeft = RT_INDEFINITE_WAIT; 576 if (cMsTimeout != RT_INDEFINITE_WAIT) 577 { 578 uint64_t u64Now = RTTimeMilliTS(); 579 uint64_t cMsElapsed = u64Now - MsStart; 580 if (cMsElapsed >= cMsTimeout) 581 { 582 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Timed out (%ums elapsed > %ums timeout), killing ...", 583 pThread->uPID, cMsElapsed, cMsTimeout); 584 585 fProcessTimedOut = true; 586 if ( MsProcessKilled == UINT64_MAX 587 || u64Now - MsProcessKilled > 1000) 588 { 589 if (u64Now - MsProcessKilled > 20*60*1000) 590 break; /* Give up after 20 mins. */ 591 RTProcTerminate(hProcess); 592 MsProcessKilled = u64Now; 593 continue; 594 } 595 cMilliesLeft = 10000; 596 } 597 else 598 cMilliesLeft = cMsTimeout - (uint32_t)cMsElapsed; 599 } 600 601 /* Reset the polling interval since we've done all pending work. */ 602 cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft; 603 604 /* 605 * Need to exit? 606 */ 607 if (pThread->fShutdown) 608 break; 609 } 610 611 /* 612 * Try kill the process if it's still alive at this point. 613 */ 614 if (fProcessAlive) 615 { 616 if (MsProcessKilled == UINT64_MAX) 617 { 618 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Is still alive and not killed yet\n", 619 pThread->uPID); 620 621 MsProcessKilled = RTTimeMilliTS(); 622 RTProcTerminate(hProcess); 623 RTThreadSleep(500); 624 } 625 626 for (size_t i = 0; i < 10; i++) 627 { 628 VBoxServiceVerbose(4, "ControlExec: [PID %u]: Kill attempt %d/10: Waiting to exit ...\n", 629 pThread->uPID, i + 1); 630 rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus); 631 if (RT_SUCCESS(rc2)) 632 { 633 VBoxServiceVerbose(4, "ControlExec: [PID %u]: Kill attempt %d/10: Exited\n", 634 pThread->uPID, i + 1); 635 fProcessAlive = false; 636 break; 637 } 638 if (i >= 5) 639 { 640 VBoxServiceVerbose(4, "ControlExec: [PID %u]: Kill attempt %d/10: Trying to terminate ...\n", 641 pThread->uPID, i + 1); 642 RTProcTerminate(hProcess); 643 } 644 RTThreadSleep(i >= 5 ? 2000 : 500); 645 } 646 647 if (fProcessAlive) 648 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Could not be killed\n", pThread->uPID); 649 } 650 651 /* 652 * If we don't have a client problem (RT_FAILURE(rc)) we'll reply to the 653 * clients exec packet now. 654 */ 655 if (RT_SUCCESS(rc)) 656 { 657 /* Mark this thread as stopped and do some action required for stopping ... */ 658 //VBoxServiceControlExecThreadCleanup(pThread); 659 660 uint32_t uStatus = PROC_STS_UNDEFINED; 661 uint32_t uFlags = 0; 662 663 if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX) 664 { 665 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Timed out and got killed\n", 666 pThread->uPID); 667 uStatus = PROC_STS_TOK; 668 } 669 else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX) 670 { 671 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Timed out and did *not* get killed\n", 672 pThread->uPID); 673 uStatus = PROC_STS_TOA; 674 } 675 else if (pThread->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX)) 676 { 677 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Got terminated because system/service is about to shutdown\n", 678 pThread->uPID); 679 uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */ 680 uFlags = pThread->uFlags; /* Return handed-in execution flags back to the host. */ 681 } 682 else if (fProcessAlive) 683 { 684 VBoxServiceError("ControlExec: [PID %u]: Is alive when it should not!\n", 685 pThread->uPID); 686 } 687 else if (MsProcessKilled != UINT64_MAX) 688 { 689 VBoxServiceError("ControlExec: [PID %u]: Has been killed when it should not!\n", 690 pThread->uPID); 691 } 692 else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL) 693 { 694 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended with RTPROCEXITREASON_NORMAL (%u)\n", 695 pThread->uPID, ProcessStatus.iStatus); 696 697 uStatus = PROC_STS_TEN; 698 uFlags = ProcessStatus.iStatus; 699 } 700 else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL) 701 { 702 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended with RTPROCEXITREASON_SIGNAL (%u)\n", 703 pThread->uPID, ProcessStatus.iStatus); 704 705 uStatus = PROC_STS_TES; 706 uFlags = ProcessStatus.iStatus; 707 } 708 else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND) 709 { 710 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended with RTPROCEXITREASON_ABEND (%u)\n", 711 pThread->uPID, ProcessStatus.iStatus); 712 713 uStatus = PROC_STS_TEA; 714 uFlags = ProcessStatus.iStatus; 715 } 716 else 717 VBoxServiceVerbose(1, "ControlExec: [PID %u]: Handling process status %u not implemented\n", 718 pThread->uPID, ProcessStatus.enmReason); 719 720 rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, 721 pThread->uPID, uStatus, uFlags, 722 NULL /* pvData */, 0 /* cbData */); 723 if (RT_FAILURE(rc)) 724 VBoxServiceError("ControlExec: [PID %u]: Error reporting final status to host; rc=%Rrc\n", 725 pThread->uPID, rc); 726 727 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended, CID=%u, Status=%u, Flags=%u\n", 728 pThread->uPID, pThread->uContextID, uStatus, uFlags); 729 730 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Process loop ended with rc=%Rrc\n", 731 pThread->uPID, rc); 732 } 733 else 734 VBoxServiceError("ControlExec: [PID %u]: Loop failed with rc=%Rrc\n", 735 pThread->uPID, rc); 736 return rc; 737 } 738 739 740 /** 741 * Sets up the redirection / pipe / nothing for one of the standard handles. 742 * 743 * @returns IPRT status code. No client replies made. 744 * @param fd Which standard handle it is (0 == stdin, 1 == 745 * stdout, 2 == stderr). 746 * @param ph The generic handle that @a pph may be set 747 * pointing to. Always set. 748 * @param pph Pointer to the RTProcCreateExec argument. 749 * Always set. 750 * @param phPipe Where to return the end of the pipe that we 751 * should service. Always set. 752 */ 753 static int VBoxServiceControlExecSetupPipe(int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe) 754 { 755 AssertPtrReturn(ph, VERR_INVALID_PARAMETER); 756 AssertPtrReturn(pph, VERR_INVALID_PARAMETER); 757 AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER); 758 759 ph->enmType = RTHANDLETYPE_PIPE; 760 ph->u.hPipe = NIL_RTPIPE; 761 *pph = NULL; 762 *phPipe = NIL_RTPIPE; 763 764 int rc; 765 766 /* 767 * Setup a pipe for forwarding to/from the client. 768 * The ph union struct will be filled with a pipe read/write handle 769 * to represent the "other" end to phPipe. 770 */ 771 if (fd == 0) /* stdin? */ 772 { 773 /* Connect a wrtie pipe specified by phPipe to stdin. */ 774 rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ); 775 } 776 else /* stdout or stderr? */ 777 { 778 /* Connect a read pipe specified by phPipe to stdout or stderr. */ 779 rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE); 780 } 781 if (RT_FAILURE(rc)) 782 return rc; 783 ph->enmType = RTHANDLETYPE_PIPE; 784 *pph = ph; 785 786 return rc; 787 } 788 789 790 /** 791 * Expands a file name / path to its real content. This only works on Windows 792 * for now (e.g. translating "%TEMP%\foo.exe" to "C:\Windows\Temp" when starting 793 * with system / administrative rights). 794 * 795 * @return IPRT status code. 796 * @param pszPath Path to resolve. 797 * @param pszExpanded Pointer to string to store the resolved path in. 798 * @param cbExpanded Size (in bytes) of string to store the resolved path. 799 */ 800 static int VBoxServiceControlExecMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded) 801 { 802 int rc = VINF_SUCCESS; 803 #ifdef RT_OS_WINDOWS 804 if (!ExpandEnvironmentStrings(pszPath, pszExpanded, cbExpanded)) 805 rc = RTErrConvertFromWin32(GetLastError()); 806 #else 807 /* No expansion for non-Windows yet. */ 808 rc = RTStrCopy(pszExpanded, cbExpanded, pszPath); 809 #endif 810 #ifdef DEBUG 811 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecMakeFullPath: %s -> %s\n", 812 pszPath, pszExpanded); 813 #endif 814 return rc; 815 } 816 817 818 /** 819 * Resolves the full path of a specified executable name. This function also 820 * resolves internal VBoxService tools to its appropriate executable path + name. 821 * 822 * @return IPRT status code. 823 * @param pszFileName File name to resovle. 824 * @param pszResolved Pointer to a string where the resolved file name will be stored. 825 * @param cbResolved Size (in bytes) of resolved file name string. 826 */ 827 static int VBoxServiceControlExecResolveExecutable(const char *pszFileName, char *pszResolved, size_t cbResolved) 828 { 829 int rc = VINF_SUCCESS; 830 831 /* Search the path of our executable. */ 832 char szVBoxService[RTPATH_MAX]; 833 if (RTProcGetExecutablePath(szVBoxService, sizeof(szVBoxService))) 834 { 835 char *pszExecResolved = NULL; 836 if ( (g_pszProgName && RTStrICmp(pszFileName, g_pszProgName) == 0) 837 || !RTStrICmp(pszFileName, VBOXSERVICE_NAME)) 838 { 839 /* We just want to execute VBoxService (no toolbox). */ 840 pszExecResolved = RTStrDup(szVBoxService); 841 } 842 else /* Nothing to resolve, copy original. */ 843 pszExecResolved = RTStrDup(pszFileName); 844 AssertPtr(pszExecResolved); 845 846 rc = VBoxServiceControlExecMakeFullPath(pszExecResolved, pszResolved, cbResolved); 847 #ifdef DEBUG 848 VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecResolveExecutable: %s -> %s\n", 849 pszFileName, pszResolved); 850 #endif 851 RTStrFree(pszExecResolved); 852 } 853 return rc; 854 } 855 856 857 /** 858 * Constructs the argv command line by resolving environment variables 859 * and relative paths. 860 * 861 * @return IPRT status code. 862 * @param pszArgv0 First argument (argv0), either original or modified version. 863 * @param papszArgs Original argv command line from the host, starting at argv[1]. 864 * @param ppapszArgv Pointer to a pointer with the new argv command line. 865 * Needs to be freed with RTGetOptArgvFree. 866 */ 867 static int VBoxServiceControlExecPrepareArgv(const char *pszArgv0, 868 const char * const *papszArgs, char ***ppapszArgv) 869 { 870 /** @todo RTGetOptArgvToString converts to MSC quoted string, while 871 * RTGetOptArgvFromString takes bourne shell according to the docs... 872 * Actually, converting to and from here is a very roundabout way of prepending 873 * an entry (pszFilename) to an array (*ppapszArgv). */ 874 int rc = VINF_SUCCESS; 875 char *pszNewArgs = NULL; 876 if (pszArgv0) 877 rc = RTStrAAppend(&pszNewArgs, pszArgv0); 878 if ( RT_SUCCESS(rc) 879 && papszArgs) 880 881 { 882 char *pszArgs; 883 rc = RTGetOptArgvToString(&pszArgs, papszArgs, 884 RTGETOPTARGV_CNV_QUOTE_MS_CRT); /* RTGETOPTARGV_CNV_QUOTE_BOURNE_SH */ 141 885 if (RT_SUCCESS(rc)) 142 886 { 143 rc = VBoxServicePipeBufInit(&pData->stdErr, VBOXSERVICECTRLPIPEID_STDERR, 144 false /*fNeedNotificationPipe*/); 887 rc = RTStrAAppend(&pszNewArgs, " "); 145 888 if (RT_SUCCESS(rc)) 146 rc = VBoxServicePipeBufInit(&pData->stdIn, VBOXSERVICECTRLPIPEID_STDIN, 147 true /*fNeedNotificationPipe*/); 889 rc = RTStrAAppend(&pszNewArgs, pszArgs); 890 } 891 } 892 893 if (RT_SUCCESS(rc)) 894 { 895 int iNumArgsIgnored; 896 rc = RTGetOptArgvFromString(ppapszArgv, &iNumArgsIgnored, 897 pszNewArgs ? pszNewArgs : "", NULL /* Use standard separators. */); 898 } 899 900 if (pszNewArgs) 901 RTStrFree(pszNewArgs); 902 return rc; 903 } 904 905 906 /** 907 * Helper function to create/start a process on the guest. 908 * 909 * @return IPRT status code. 910 * @param pszExec Full qualified path of process to start (without arguments). 911 * @param papszArgs Pointer to array of command line arguments. 912 * @param hEnv Handle to environment block to use. 913 * @param fFlags Process execution flags. 914 * @param phStdIn Handle for the process' stdin pipe. 915 * @param phStdOut Handle for the process' stdout pipe. 916 * @param phStdErr Handle for the process' stderr pipe. 917 * @param pszAsUser User name (account) to start the process under. 918 * @param pszPassword Password of the specified user. 919 * @param phProcess Pointer which will receive the process handle after 920 * successful process start. 921 */ 922 static int VBoxServiceControlExecCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags, 923 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser, 924 const char *pszPassword, PRTPROCESS phProcess) 925 { 926 AssertPtrReturn(pszExec, VERR_INVALID_PARAMETER); 927 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER); 928 AssertPtrReturn(phProcess, VERR_INVALID_PARAMETER); 929 930 int rc = VINF_SUCCESS; 931 char szExecExp[RTPATH_MAX]; 932 #ifdef RT_OS_WINDOWS 933 /* 934 * If sysprep should be executed do this in the context of VBoxService, which 935 * (usually, if started by SCM) has administrator rights. Because of that a UI 936 * won't be shown (doesn't have a desktop). 937 */ 938 if (RTStrICmp(pszExec, "sysprep") == 0) 939 { 940 /* Use a predefined sysprep path as default. */ 941 char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe"; 942 943 /* 944 * On Windows Vista (and up) sysprep is located in "system32\\sysprep\\sysprep.exe", 945 * so detect the OS and use a different path. 946 */ 947 OSVERSIONINFOEX OSInfoEx; 948 RT_ZERO(OSInfoEx); 949 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); 950 if ( GetVersionEx((LPOSVERSIONINFO) &OSInfoEx) 951 && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT 952 && OSInfoEx.dwMajorVersion >= 6 /* Vista or later */) 953 { 954 rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL); 955 if (RT_SUCCESS(rc)) 956 rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\sysprep\\sysprep.exe"); 148 957 } 149 958 150 959 if (RT_SUCCESS(rc)) 151 960 { 152 pThread->enmType = kVBoxServiceCtrlThreadDataExec; 153 pThread->pvData = pData; 154 } 155 } 156 961 char **papszArgsExp; 962 rc = VBoxServiceControlExecPrepareArgv(szSysprepCmd /* argv0 */, papszArgs, &papszArgsExp); 963 if (RT_SUCCESS(rc)) 964 { 965 rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */, 966 phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */, 967 NULL /* pszPassword */, phProcess); 968 } 969 RTGetOptArgvFree(papszArgsExp); 970 } 971 return rc; 972 } 973 #endif /* RT_OS_WINDOWS */ 974 975 #ifdef VBOXSERVICE_TOOLBOX 976 if (RTStrStr(pszExec, "vbox_") == pszExec) 977 { 978 /* We want to use the internal toolbox (all internal 979 * tools are starting with "vbox_" (e.g. "vbox_cat"). */ 980 rc = VBoxServiceControlExecResolveExecutable(VBOXSERVICE_NAME, szExecExp, sizeof(szExecExp)); 981 } 982 else 983 { 984 #endif 985 /* 986 * Do the environment variables expansion on executable and arguments. 987 */ 988 rc = VBoxServiceControlExecResolveExecutable(pszExec, szExecExp, sizeof(szExecExp)); 989 #ifdef VBOXSERVICE_TOOLBOX 990 } 991 #endif 992 if (RT_SUCCESS(rc)) 993 { 994 char **papszArgsExp; 995 rc = VBoxServiceControlExecPrepareArgv(pszExec /* Always use the unmodified executable name as argv0. */, 996 papszArgs /* Append the rest of the argument vector (if any). */, &papszArgsExp); 997 if (RT_SUCCESS(rc)) 998 { 999 uint32_t uProcFlags = 0; 1000 if (fFlags) 1001 { 1002 /* Process Main flag "ExecuteProcessFlag_Hidden". */ 1003 if (fFlags & RT_BIT(2)) 1004 uProcFlags |= RTPROC_FLAGS_HIDDEN; 1005 /* Process Main flag "ExecuteProcessFlag_NoProfile". */ 1006 if (fFlags & RT_BIT(3)) 1007 uProcFlags |= RTPROC_FLAGS_NO_PROFILE; 1008 } 1009 1010 /* If no user name specified run with current credentials (e.g. 1011 * full service/system rights). This is prohibited via official Main API! 1012 * 1013 * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication 1014 * code (at least on Windows) for running processes as different users 1015 * started from our system service. */ 1016 if (*pszAsUser) 1017 uProcFlags |= RTPROC_FLAGS_SERVICE; 1018 #ifdef DEBUG 1019 VBoxServiceVerbose(3, "ControlExec: Command: %s\n", szExecExp); 1020 for (size_t i = 0; papszArgsExp[i]; i++) 1021 VBoxServiceVerbose(3, "ControlExec:\targv[%ld]: %s\n", i, papszArgsExp[i]); 1022 #endif 1023 /* Do normal execution. */ 1024 rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags, 1025 phStdIn, phStdOut, phStdErr, 1026 *pszAsUser ? pszAsUser : NULL, 1027 *pszPassword ? pszPassword : NULL, 1028 phProcess); 1029 RTGetOptArgvFree(papszArgsExp); 1030 } 1031 } 1032 return rc; 1033 } 1034 1035 /** 1036 * The actual worker routine (lopp) for a started guest process. 1037 * 1038 * @return IPRT status code. 1039 * @param PVBOXSERVICECTRLTHREAD Thread data associated with a started process. 1040 */ 1041 static DECLCALLBACK(int) VBoxServiceControlExecProcessWorker(PVBOXSERVICECTRLTHREAD pThread) 1042 { 1043 AssertPtrReturn(pThread, VERR_INVALID_POINTER); 1044 VBoxServiceVerbose(3, "ControlExec: Thread of process \"%s\" started\n", pThread->pszCmd); 1045 1046 int rc = VbglR3GuestCtrlConnect(&pThread->uClientID); 157 1047 if (RT_FAILURE(rc)) 158 VBoxServiceControlExecThreadDataDestroy(pData); 1048 { 1049 VBoxServiceError("ControlExec: Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc); 1050 RTThreadUserSignal(RTThreadSelf()); 1051 return rc; 1052 } 1053 1054 bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */ 1055 1056 /* 1057 * Create the environment. 1058 */ 1059 RTENV hEnv; 1060 rc = RTEnvClone(&hEnv, RTENV_DEFAULT); 1061 if (RT_SUCCESS(rc)) 1062 { 1063 size_t i; 1064 for (i = 0; i < pThread->uNumEnvVars && pThread->papszEnv; i++) 1065 { 1066 rc = RTEnvPutEx(hEnv, pThread->papszEnv[i]); 1067 if (RT_FAILURE(rc)) 1068 break; 1069 } 1070 if (RT_SUCCESS(rc)) 1071 { 1072 /* 1073 * Setup the redirection of the standard stuff. 1074 */ 1075 /** @todo consider supporting: gcc stuff.c >file 2>&1. */ 1076 RTHANDLE hStdIn; 1077 PRTHANDLE phStdIn; 1078 rc = VBoxServiceControlExecSetupPipe(0 /*STDIN_FILENO*/, &hStdIn, &phStdIn, &pThread->pipeStdInW); 1079 if (RT_SUCCESS(rc)) 1080 { 1081 RTHANDLE hStdOut; 1082 PRTHANDLE phStdOut; 1083 RTPIPE hStdOutR; 1084 rc = VBoxServiceControlExecSetupPipe(1 /*STDOUT_FILENO*/, &hStdOut, &phStdOut, &hStdOutR); 1085 if (RT_SUCCESS(rc)) 1086 { 1087 RTHANDLE hStdErr; 1088 PRTHANDLE phStdErr; 1089 RTPIPE hStdErrR; 1090 rc = VBoxServiceControlExecSetupPipe(2 /*STDERR_FILENO*/, &hStdErr, &phStdErr, &hStdErrR); 1091 if (RT_SUCCESS(rc)) 1092 { 1093 /* 1094 * Create a poll set for the pipes and let the 1095 * transport layer add stuff to it as well. 1096 */ 1097 RTPOLLSET hPollSet; 1098 rc = RTPollSetCreate(&hPollSet); 1099 if (RT_SUCCESS(rc)) 1100 { 1101 /* Stdin. */ 1102 if (RT_SUCCESS(rc)) 1103 rc = RTPollSetAddPipe(hPollSet, pThread->pipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN); 1104 /* Stdout. */ 1105 if (RT_SUCCESS(rc)) 1106 rc = RTPollSetAddPipe(hPollSet, hStdOutR, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDOUT); 1107 /* Stderr. */ 1108 if (RT_SUCCESS(rc)) 1109 rc = RTPollSetAddPipe(hPollSet, hStdErrR, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDERR); 1110 /* IPC notification pipe. */ 1111 if (RT_SUCCESS(rc)) 1112 rc = RTPipeCreate(&pThread->hNotificationPipeR, &pThread->hNotificationPipeW, 0 /* Flags */); 1113 if (RT_SUCCESS(rc)) 1114 rc = RTPollSetAddPipe(hPollSet, pThread->hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_IPC_NOTIFY); 1115 1116 if (RT_SUCCESS(rc)) 1117 { 1118 RTPROCESS hProcess; 1119 rc = VBoxServiceControlExecCreateProcess(pThread->pszCmd, pThread->papszArgs, hEnv, pThread->uFlags, 1120 phStdIn, phStdOut, phStdErr, 1121 pThread->pszUser, pThread->pszPassword, 1122 &hProcess); 1123 if (RT_FAILURE(rc)) 1124 VBoxServiceError("ControlExec: Error starting process, rc=%Rrc\n", rc); 1125 /* 1126 * Tell the control thread that it can continue 1127 * spawning services. This needs to be done after the new 1128 * process has been started because otherwise signal handling 1129 * on (Open) Solaris does not work correctly (see #5068). 1130 */ 1131 int rc2 = RTThreadUserSignal(RTThreadSelf()); 1132 if (RT_FAILURE(rc2)) 1133 rc = rc2; 1134 fSignalled = true; 1135 1136 if (RT_SUCCESS(rc)) 1137 { 1138 /* 1139 * Close the child ends of any pipes and redirected files. 1140 */ 1141 rc2 = RTHandleClose(phStdIn); AssertRC(rc2); 1142 phStdIn = NULL; 1143 rc2 = RTHandleClose(phStdOut); AssertRC(rc2); 1144 phStdOut = NULL; 1145 rc2 = RTHandleClose(phStdErr); AssertRC(rc2); 1146 phStdErr = NULL; 1147 1148 /* Enter the process loop. */ 1149 rc = VBoxServiceControlExecProcLoop(pThread, 1150 hProcess, pThread->uTimeLimitMS, hPollSet, 1151 &pThread->pipeStdInW, &hStdOutR, &hStdErrR); 1152 1153 /* Before cleaning up everything else, remove the thread from our thread list. */ 1154 VBoxServiceControlRemoveThread(pThread); 1155 1156 /* 1157 * The handles that are no longer in the set have 1158 * been closed by the above call in order to prevent 1159 * the guest from getting stuck accessing them. 1160 * So, NIL the handles to avoid closing them again. 1161 */ 1162 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_IPC_NOTIFY, NULL))) 1163 { 1164 pThread->hNotificationPipeR = NIL_RTPIPE; 1165 pThread->hNotificationPipeW = NIL_RTPIPE; 1166 } 1167 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDERR, NULL))) 1168 hStdErrR = NIL_RTPIPE; 1169 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDOUT, NULL))) 1170 hStdOutR = NIL_RTPIPE; 1171 if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDIN, NULL))) 1172 pThread->pipeStdInW = NIL_RTPIPE; 1173 } 1174 else /* Something went wrong; report error! */ 1175 { 1176 VBoxServiceError("ControlExec: Could not start process '%s' (CID: %u)! Error: %Rrc\n", 1177 pThread->pszCmd, pThread->uContextID, rc); 1178 1179 rc2 = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, pThread->uPID, 1180 PROC_STS_ERROR, rc, 1181 NULL /* pvData */, 0 /* cbData */); 1182 if (RT_FAILURE(rc2)) 1183 VBoxServiceError("ControlExec: Could not report process start error! Error: %Rrc (process error %Rrc)\n", 1184 rc2, rc); 1185 } 1186 } 1187 RTPollSetDestroy(hPollSet); 1188 1189 RTPipeClose(pThread->hNotificationPipeR); 1190 pThread->hNotificationPipeR = NIL_RTPIPE; 1191 RTPipeClose(pThread->hNotificationPipeW); 1192 pThread->hNotificationPipeW = NIL_RTPIPE; 1193 } 1194 RTPipeClose(hStdErrR); 1195 hStdErrR = NIL_RTPIPE; 1196 RTHandleClose(phStdErr); 1197 } 1198 RTPipeClose(hStdOutR); 1199 hStdOutR = NIL_RTPIPE; 1200 RTHandleClose(phStdOut); 1201 } 1202 RTPipeClose(pThread->pipeStdInW); 1203 RTHandleClose(phStdIn); 1204 } 1205 } 1206 RTEnvDestroy(hEnv); 1207 } 1208 1209 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Thread of process \"%s\" ended with rc=%Rrc\n", 1210 pThread->uPID, pThread->pszCmd, rc); 1211 1212 /* Disconnect from guest control service. */ 1213 VbglR3GuestCtrlDisconnect(pThread->uClientID); 1214 pThread->uClientID = 0; 1215 1216 ASMAtomicXchgBool(&pThread->fStarted, false); 1217 ASMAtomicXchgBool(&pThread->fStopped, true); 1218 1219 int rc2 = VBoxServiceControlExecThreadShutdown(pThread); 1220 if (RT_SUCCESS(rc2)) 1221 VBoxServiceControlExecThreadDestroy(pThread); 1222 1223 /* 1224 * If something went wrong signal the user event so that others don't wait 1225 * forever on this thread. 1226 */ 1227 if (RT_FAILURE(rc) && !fSignalled) 1228 RTThreadUserSignal(RTThreadSelf()); 1229 1230 return rc; 1231 } 1232 1233 1234 /** 1235 * Thread main routine for a started process. 1236 * 1237 * @return IPRT status code. 1238 * @param RTTHREAD Pointer to the thread's data. 1239 * @param void* User-supplied argument pointer. 1240 * 1241 */ 1242 static DECLCALLBACK(int) VBoxServiceControlExecThread(RTTHREAD ThreadSelf, void *pvUser) 1243 { 1244 PVBOXSERVICECTRLTHREAD pThread = (VBOXSERVICECTRLTHREAD*)pvUser; 1245 AssertPtrReturn(pThread, VERR_INVALID_POINTER); 1246 return VBoxServiceControlExecProcessWorker(pThread); 1247 } 1248 1249 1250 /** 1251 * Executes (starts) a process on the guest. This causes a new thread to be created 1252 * so that this function will not block the overall program execution. 1253 * 1254 * @return IPRT status code. 1255 * @param uClientID Client ID for accessing host service. 1256 * @param uContextID Context ID to associate the process to start with. 1257 * @param pszCmd Full qualified path of process to start (without arguments). 1258 * @param uFlags Process execution flags. 1259 * @param pszArgs String of arguments to pass to the process to start. 1260 * @param uNumArgs Number of arguments specified in pszArgs. 1261 * @param pszEnv String of environment variables ("FOO=BAR") to pass to the process 1262 * to start. 1263 * @param cbEnv Size (in bytes) of environment variables. 1264 * @param uNumEnvVars Number of environment variables specified in pszEnv. 1265 * @param pszUser User name (account) to start the process under. 1266 * @param pszPassword Password of specified user name (account). 1267 * @param uTimeLimitMS Time limit (in ms) of the process' life time. 1268 * @param ppNode The thread's list node to insert into the global thread list 1269 * on success. 1270 */ 1271 int VBoxServiceControlThreadStart(uint32_t uClientID, uint32_t uContextID, 1272 const char *pszCmd, uint32_t uFlags, 1273 const char *pszArgs, uint32_t uNumArgs, 1274 const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars, 1275 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS, 1276 PRTLISTNODE *ppNode) 1277 { 1278 AssertPtrReturn(ppNode, VERR_INVALID_POINTER); 1279 1280 /* 1281 * Allocate new thread data and assign it to our thread list. 1282 */ 1283 PVBOXSERVICECTRLTHREAD pThread = (PVBOXSERVICECTRLTHREAD)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREAD)); 1284 if (!pThread) 1285 return VERR_NO_MEMORY; 1286 1287 int rc = VBoxServiceControlExecThreadAlloc(pThread, 1288 uContextID, 1289 pszCmd, uFlags, 1290 pszArgs, uNumArgs, 1291 pszEnv, cbEnv, uNumEnvVars, 1292 pszUser, pszPassword, 1293 uTimeLimitMS); 1294 if (RT_SUCCESS(rc)) 1295 { 1296 static uint32_t uCtrlExecThread = 0; 1297 char szThreadName[32]; 1298 if (!RTStrPrintf(szThreadName, sizeof(szThreadName), "controlexec%ld", uCtrlExecThread++)) 1299 AssertMsgFailed(("Unable to create unique control exec thread name!\n")); 1300 1301 rc = RTThreadCreate(&pThread->Thread, VBoxServiceControlExecThread, 1302 (void *)(PVBOXSERVICECTRLTHREAD*)pThread, 0, 1303 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, szThreadName); 1304 if (RT_FAILURE(rc)) 1305 { 1306 VBoxServiceError("ControlExec: RTThreadCreate failed, rc=%Rrc\n, pThread=%p\n", 1307 rc, pThread); 1308 } 1309 else 1310 { 1311 VBoxServiceVerbose(4, "ControlExec: Waiting for thread to initialize ...\n"); 1312 1313 /* Wait for the thread to initialize. */ 1314 RTThreadUserWait(pThread->Thread, 60 * 1000 /* 60 seconds max. */); 1315 if (pThread->fShutdown) 1316 { 1317 VBoxServiceError("ControlExec: Thread for process \"%s\" failed to start!\n", pszCmd); 1318 rc = VERR_GENERAL_FAILURE; 1319 } 1320 else 1321 { 1322 pThread->fStarted = true; 1323 1324 /* Return the thread's node. */ 1325 *ppNode = &pThread->Node; 1326 } 1327 } 1328 } 1329 1330 if (RT_FAILURE(rc)) 1331 RTMemFree(pThread); 1332 159 1333 return rc; 160 1334 } … … 169 1343 * @param uPID PID to assign to the specified guest control execution thread. 170 1344 */ 171 int VBoxServiceControlExecThreadAssignPID(PVBOXSERVICECTRLTHREADDATAEXEC pData, uint32_t uPID)172 { 173 AssertPtrReturn(p Data, VERR_INVALID_POINTER);1345 int vboxServiceControlExecThreadAssignPID(PVBOXSERVICECTRLTHREAD pThread, uint32_t uPID) 1346 { 1347 AssertPtrReturn(pThread, VERR_INVALID_POINTER); 174 1348 AssertReturn(uPID, VERR_INVALID_PARAMETER); 175 1349 176 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect); 177 if (RT_SUCCESS(rc)) 178 { 179 /* Search an old thread using the desired PID and shut it down completely -- it's 180 * not used anymore. */ 181 PVBOXSERVICECTRLTHREAD pOldNode = vboxServiceControlExecThreadGetByPID(uPID); 182 if ( pOldNode 183 && pOldNode->pvData != pData) 184 { 185 PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pOldNode->Node, VBOXSERVICECTRLTHREAD, Node); 186 187 VBoxServiceVerbose(3, "ControlExec: PID %u was used before, shutting down stale exec thread ...\n", 188 uPID); 189 AssertPtr(pOldNode->pvData); 190 rc = VBoxServiceControlExecThreadShutdown(pOldNode); 191 if (RT_FAILURE(rc)) 1350 int rc = VINF_SUCCESS; 1351 1352 /* Search an old thread using the desired PID and shut it down completely -- it's 1353 * not used anymore. */ 1354 PVBOXSERVICECTRLTHREAD pOldThread = VBoxServiceControlGetThreadByPID(uPID); 1355 if ( pOldThread 1356 && pOldThread != pThread) 1357 { 1358 PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pOldThread->Node, VBOXSERVICECTRLTHREAD, Node); 1359 1360 VBoxServiceVerbose(3, "ControlExec: PID %u was used before, shutting down stale exec thread ...\n", 1361 uPID); 1362 rc = VBoxServiceControlExecThreadShutdown(pOldThread); 1363 if (RT_FAILURE(rc)) 1364 { 1365 VBoxServiceVerbose(3, "ControlExec: Unable to shut down stale exec thread, rc=%Rrc\n", rc); 1366 /* Keep going. */ 1367 } 1368 } 1369 1370 /* Assign PID to current thread. */ 1371 pThread->uPID = uPID; 1372 1373 return rc; 1374 } 1375 1376 1377 /** 1378 * TODO 1379 * 1380 * @return IPRT status code. 1381 * @return int 1382 * @param uPID 1383 * @param pRequest 1384 */ 1385 int VBoxServiceControlExecThreadPerform(uint32_t uPID, PVBOXSERVICECTRLREQUEST pRequest) 1386 { 1387 AssertPtrReturn(pRequest, VERR_INVALID_POINTER); 1388 AssertReturn(pRequest->enmType > VBOXSERVICECTRLREQUEST_UNKNOWN, VERR_INVALID_PARAMETER); 1389 /* Rest in pRequest is optional (based on the request type). */ 1390 1391 VBoxServiceVerbose(4, "ControlExec: [PID %u]: Performing enmType=%u, pvData=0x%p, cbData=%u ...\n", 1392 uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData); 1393 1394 int rc; 1395 const PVBOXSERVICECTRLTHREAD pThread = VBoxServiceControlGetThreadByPID(uPID); 1396 if (pThread) 1397 { 1398 rc = RTCritSectEnter(&pThread->CritSect); 1399 if (RT_SUCCESS(rc)) 1400 { 1401 /* Set request structure pointer. */ 1402 pThread->pRequest = pRequest; 1403 1404 /** @todo To speed up simultaneous guest process handling we could add a worker threads in order 1405 * to wait for the request to happen. Later. */ 1406 1407 /* Wake up guest thrad by sending a wakeup byte to the notification pipe so 1408 * that RTPoll unblocks (returns) and we then can do our requested operation. */ 1409 if (pThread->hNotificationPipeW == NIL_RTPIPE) 1410 rc = VERR_BROKEN_PIPE; 1411 size_t cbWritten; 1412 if (RT_SUCCESS(rc)) 1413 rc = RTPipeWrite(pThread->hNotificationPipeW, "i", 1, &cbWritten); 1414 if (RT_SUCCESS(rc) && cbWritten) 192 1415 { 193 VBoxServiceVerbose(3, "ControlExec: Unable to shut down stale exec thread, rc=%Rrc\n", rc); 194 /* Keep going. */ 195 } 196 197 RTListNodeRemove(&pOldNode->Node); 198 RTMemFree(pOldNode); 199 } 200 201 /* Assign PID to current thread. */ 202 pData->uPID = uPID; 203 VBoxServicePipeBufSetPID(&pData->stdIn, pData->uPID); 204 VBoxServicePipeBufSetPID(&pData->stdOut, pData->uPID); 205 VBoxServicePipeBufSetPID(&pData->stdErr, pData->uPID); 206 207 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect); 208 if (RT_SUCCESS(rc)) 209 rc = rc2; 210 } 211 212 return rc; 213 } 214 215 216 /** 217 * Frees an allocated thread data structure along with all its allocated parameters. 218 * 219 * @param pData Pointer to thread data to free. 220 */ 221 void VBoxServiceControlExecThreadDataDestroy(PVBOXSERVICECTRLTHREADDATAEXEC pData) 222 { 223 if (pData) 224 { 225 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Destroying thread data ...\n", 226 pData->uPID); 227 228 RTStrFree(pData->pszCmd); 229 if (pData->uNumEnvVars) 230 { 231 for (uint32_t i = 0; i < pData->uNumEnvVars; i++) 232 RTStrFree(pData->papszEnv[i]); 233 RTMemFree(pData->papszEnv); 234 } 235 RTGetOptArgvFree(pData->papszArgs); 236 RTStrFree(pData->pszUser); 237 RTStrFree(pData->pszPassword); 238 239 VBoxServicePipeBufDestroy(&pData->stdOut); 240 VBoxServicePipeBufDestroy(&pData->stdErr); 241 VBoxServicePipeBufDestroy(&pData->stdIn); 242 243 RTMemFree(pData); 244 pData = NULL; 245 } 246 } 247 248 249 /** 250 * Finds a (formerly) started process given by its PID. 251 * Internal function, does not do locking -- this must be done from the caller function! 252 * 253 * @return PVBOXSERVICECTRLTHREAD Process structure if found, otherwise NULL. 254 * @param uPID PID to search for. 255 */ 256 PVBOXSERVICECTRLTHREAD vboxServiceControlExecThreadGetByPID(uint32_t uPID) 257 { 258 PVBOXSERVICECTRLTHREAD pNode = NULL; 259 RTListForEach(&g_GuestControlThreads, pNode, VBOXSERVICECTRLTHREAD, Node) 260 { 261 if (pNode->enmType == kVBoxServiceCtrlThreadDataExec) 262 { 263 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData; 264 if (pData && pData->uPID == uPID) 265 return pNode; 266 } 267 } 268 return NULL; 269 } 270 271 272 /** 273 * Injects input to a specified running process. 274 * 275 * @return IPRT status code. 276 * @param uPID PID of process to set the input for. 277 * @param fPendingClose Flag indicating whether this is the last input block sent to the process. 278 * @param pBuf Pointer to a buffer containing the actual input data. 279 * @param cbSize Size (in bytes) of the input buffer data. 280 * @param pcbWritten Pointer to number of bytes written to the process. Optional. 281 */ 282 int VBoxServiceControlExecThreadSetInput(uint32_t uPID, bool fPendingClose, uint8_t *pBuf, 283 uint32_t cbSize, uint32_t *pcbWritten) 284 { 285 AssertPtrReturn(pBuf, VERR_INVALID_PARAMETER); 286 287 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect); 288 if (RT_SUCCESS(rc)) 289 { 290 PVBOXSERVICECTRLTHREAD pNode = vboxServiceControlExecThreadGetByPID(uPID); 291 if (pNode) 292 { 293 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData; 294 AssertPtr(pData); 295 296 if (VBoxServicePipeBufIsEnabled(&pData->stdIn)) 297 { 298 /* 299 * Feed the data to the pipe. 300 */ 301 uint32_t cbWritten; 302 rc = VBoxServicePipeBufWriteToBuf(&pData->stdIn, pBuf, 303 cbSize, fPendingClose, &cbWritten); 304 if (pcbWritten) 305 *pcbWritten = cbWritten; 306 } 307 else 308 { 309 /* If input buffer is not enabled anymore we cannot handle that data ... */ 310 rc = VERR_BAD_PIPE; 311 } 312 } 313 else 314 rc = VERR_NOT_FOUND; /* PID not found! */ 315 RTCritSectLeave(&g_GuestControlThreadsCritSect); 316 } 317 return rc; 318 } 319 320 321 /** 322 * Gets output from stdout/stderr of a specified process. 323 * 324 * @return IPRT status code. 325 * @param uPID PID of process to retrieve the output from. 326 * @param uHandleId Stream ID (stdout = 0, stderr = 2) to get the output from. 327 * @param uTimeout Timeout (in ms) to wait for output becoming available. 328 * @param pBuf Pointer to a pre-allocated buffer to store the output. 329 * @param cbSize Size (in bytes) of the pre-allocated buffer. 330 * @param pcbRead Pointer to number of bytes read. Optional. 331 */ 332 int VBoxServiceControlExecThreadGetOutput(uint32_t uPID, uint32_t uHandleId, uint32_t uTimeout, 333 uint8_t *pBuf, uint32_t cbSize, uint32_t *pcbRead) 334 { 335 AssertPtrReturn(pBuf, VERR_INVALID_POINTER); 336 AssertReturn(cbSize, VERR_INVALID_PARAMETER); 337 338 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect); 339 if (RT_SUCCESS(rc)) 340 { 341 const PVBOXSERVICECTRLTHREAD pThread = vboxServiceControlExecThreadGetByPID(uPID); 342 if (pThread) 343 { 344 const PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData; 345 AssertPtr(pData); 346 347 PVBOXSERVICECTRLEXECPIPEBUF pPipeBuf = NULL; 348 switch (uHandleId) 349 { 350 case OUTPUT_HANDLE_ID_STDERR: 351 pPipeBuf = &pData->stdErr; 352 break; 353 354 case OUTPUT_HANDLE_ID_STDOUT: 355 case OUTPUT_HANDLE_ID_STDOUT_DEPRECATED: 356 pPipeBuf = &pData->stdOut; 357 break; 358 359 default: 360 rc = VERR_NOT_FOUND; /* Handle ID not found! */ 361 break; 362 } 363 364 if (RT_SUCCESS(rc)) 365 { 366 AssertPtr(pPipeBuf); 367 368 #ifdef DEBUG_andy 369 VBoxServiceVerbose(4, "ControlExec: [PID %u]: Getting output from pipe buffer %u ...\n", 370 uPID, pPipeBuf->uPipeId); 371 #endif 372 /* If the stdout pipe buffer is enabled (that is, still could be filled by a running 373 * process) wait for the signal to arrive so that we don't return without any actual 374 * data read. */ 375 bool fEnabled = VBoxServicePipeBufIsEnabled(pPipeBuf); 376 if (fEnabled) 377 { 378 #ifdef DEBUG_andy 379 VBoxServiceVerbose(4, "ControlExec: [PID %u]: Waiting for pipe buffer %u (%ums)\n", 380 uPID, pPipeBuf->uPipeId, uTimeout); 381 #endif 382 rc = VBoxServicePipeBufWaitForEvent(pPipeBuf, uTimeout); 383 } 1416 /* Wait on the request to get completed (or we are asked to abort/shutdown). */ 1417 rc = RTSemEventMultiWait(pThread->RequestEvent, RT_INDEFINITE_WAIT); 384 1418 if (RT_SUCCESS(rc)) 385 1419 { 386 uint32_t cbRead = cbSize; /* Read as much as possible. */ 387 rc = VBoxServicePipeBufRead(pPipeBuf, pBuf, cbSize, &cbRead); 388 if (RT_SUCCESS(rc)) 389 { 390 if ( !cbRead 391 && fEnabled) 392 { 393 AssertReleaseMsg(!VBoxServicePipeBufIsEnabled(pPipeBuf), 394 ("[PID %u]: Waited (%ums) for active pipe buffer %u (%u size, %u bytes left), but nothing read!\n", 395 uPID, uTimeout, pPipeBuf->uPipeId, pPipeBuf->cbSize, pPipeBuf->cbSize - pPipeBuf->cbOffset)); 396 } 397 if (pcbRead) 398 *pcbRead = cbRead; 399 } 400 else 401 VBoxServiceError("ControlExec: [PID %u]: Unable to read from pipe buffer %u, rc=%Rrc\n", 402 uPID, pPipeBuf->uPipeId, rc); 1420 VBoxServiceVerbose(4, "ControlExec: [PID %u]: Performed with rc=%Rrc, cbData=%u\n", 1421 uPID, pRequest->rc, pRequest->cbData); 1422 1423 /* Give back overall request result. */ 1424 rc = pRequest->rc; 1425 1426 /* Reset the semaphore. */ 1427 int rc2 = RTSemEventMultiReset(pThread->RequestEvent); 1428 if (RT_FAILURE(rc2)) 1429 VBoxServiceError("ControlExec: Unable to reset request event, rc=%Rrc\n", rc2); 403 1430 } 404 1431 } 405 } 406 else 407 rc = VERR_NOT_FOUND; /* PID not found! */ 408 409 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect); 1432 1433 int rc2 = RTCritSectLeave(&pThread->CritSect); 1434 AssertRCReturn(rc2, rc2); 1435 } 1436 } 1437 else /* PID not found! */ 1438 rc = VERR_NOT_FOUND; 1439 1440 VBoxServiceVerbose(4, "ControlExec: [PID %u]: Performed enmType=%u, pvData=0x%p, cbData=%u with rc=%Rrc\n", 1441 uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData, rc); 1442 return rc; 1443 } 1444 1445 1446 /** 1447 * Removes the guest thread from the global guest thread list and finally 1448 * destroys it. Does not do locking, must be done by the caller! 1449 * 1450 * @param pThread Thread to destroy. 1451 */ 1452 void VBoxServiceControlExecThreadDestroy(PVBOXSERVICECTRLTHREAD pThread) 1453 { 1454 if (!pThread) 1455 return; 1456 1457 if (RTCritSectIsInitialized(&pThread->CritSect)) 1458 RTCritSectDelete(&pThread->CritSect); 1459 1460 /* Destroy thread structure as final step. */ 1461 RTMemFree(pThread); 1462 pThread = NULL; 1463 } 1464 1465 1466 /** 1467 * Shuts down a guest thread. 1468 * Does not do locking, must be done by the caller! 1469 * 1470 * @return IPRT status code. 1471 * @param pThread Thread to shut down. 1472 */ 1473 int VBoxServiceControlExecThreadShutdown(PVBOXSERVICECTRLTHREAD pThread) 1474 { 1475 AssertPtrReturn(pThread, VERR_INVALID_POINTER); 1476 1477 if (ASMAtomicReadBool(&pThread->fShutdown)) /* Already shut down. */ 1478 return VINF_SUCCESS; 1479 1480 /* First, signal shut down. */ 1481 VBoxServiceControlThreadSignalShutdown(pThread); 1482 1483 /* 1484 * Wait for thread to shutdown. 1485 */ 1486 VBoxServiceVerbose(2, "ControlExec: [PID %u]: Waitng for shutting down ...\n", 1487 pThread->uPID); 1488 int rc; 1489 for (int i = 0; i < 3; i++) 1490 { 1491 rc = vboxServiceControlThreadWaitForShutdown(pThread); 410 1492 if (RT_SUCCESS(rc)) 411 rc = rc2; 412 } 413 return rc; 414 } 415 416 417 int VBoxServiceControlExecThreadRemove(uint32_t uPID) 418 { 419 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect); 420 if (RT_SUCCESS(rc)) 421 { 422 PVBOXSERVICECTRLTHREAD pThread = vboxServiceControlExecThreadGetByPID(uPID); 423 if (pThread) 424 { 425 Assert(pThread->fStarted != pThread->fStopped); 426 if (pThread->fStopped) /* Only shut down stopped threads. */ 427 { 428 VBoxServiceVerbose(4, "ControlExec: [PID %u]: Removing thread ... \n", 429 uPID); 430 431 rc = VBoxServiceControlExecThreadShutdown(pThread); 432 433 RTListNodeRemove(&pThread->Node); 434 RTMemFree(pThread); 435 } 436 } 437 else 438 rc = VERR_NOT_FOUND; 439 440 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect); 441 if (RT_SUCCESS(rc)) 442 rc = rc2; 443 } 444 445 return rc; 446 } 447 448 /* Does not do locking, must be done by the caller! */ 449 int VBoxServiceControlExecThreadShutdown(const PVBOXSERVICECTRLTHREAD pThread) 450 { 451 AssertPtrReturn(pThread, VERR_INVALID_POINTER); 452 453 if (pThread->enmType == kVBoxServiceCtrlThreadDataExec) 454 { 455 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData; 456 if (!pData) /* Already destroyed execution data. */ 457 return VINF_SUCCESS; 458 if (pThread->fStarted) 459 { 460 VBoxServiceVerbose(2, "ControlExec: [PID %u]: Shutting down a still running thread without stopping is not possible!\n", 461 pData->uPID); 462 return VERR_INVALID_PARAMETER; 463 } 464 465 VBoxServiceVerbose(2, "ControlExec: [PID %u]: Shutting down, will not be served anymore\n", 466 pData->uPID); 467 VBoxServiceControlExecThreadDataDestroy(pData); 468 } 469 470 VBoxServiceControlThreadSignalShutdown(pThread); 471 return VBoxServiceControlThreadWaitForShutdown(pThread); 472 } 473 474 475 int VBoxServiceControlExecThreadStartAllowed(bool *pbAllowed) 476 { 477 AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER); 478 479 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect); 480 if (RT_SUCCESS(rc)) 481 { 482 /* 483 * Check if we're respecting our memory policy by checking 484 * how many guest processes are started and served already. 485 */ 486 bool fLimitReached = false; 487 if (g_GuestControlProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */ 488 { 489 /** @todo Put running/stopped (+ memory alloc) stats into global struct! */ 490 uint32_t uProcsRunning = 0; 491 uint32_t uProcsStopped = 0; 492 PVBOXSERVICECTRLTHREAD pNode; 493 RTListForEach(&g_GuestControlThreads, pNode, VBOXSERVICECTRLTHREAD, Node) 494 { 495 if (pNode->enmType == kVBoxServiceCtrlThreadDataExec) 496 { 497 Assert(pNode->fStarted != pNode->fStopped); 498 if (pNode->fStarted) 499 uProcsRunning++; 500 else if (pNode->fStopped) 501 uProcsStopped++; 502 else 503 AssertMsgFailed(("Process neither started nor stopped!?\n")); 504 } 505 } 506 507 VBoxServiceVerbose(2, "ControlExec: Maximum served guest processes set to %u, running=%u, stopped=%u\n", 508 g_GuestControlProcsMaxKept, uProcsRunning, uProcsStopped); 509 510 int32_t iProcsLeft = (g_GuestControlProcsMaxKept - uProcsRunning - 1); 511 if (iProcsLeft < 0) 512 { 513 VBoxServiceVerbose(3, "ControlExec: Maximum running guest processes reached (%u)\n", 514 g_GuestControlProcsMaxKept); 515 fLimitReached = true; 516 } 517 else if (uProcsStopped > (uint32_t)iProcsLeft) 518 { 519 uint32_t uProcsToKill = uProcsStopped - iProcsLeft; 520 Assert(uProcsToKill); 521 VBoxServiceVerbose(3, "ControlExec: Shutting down %ld stopped guest processes\n", uProcsToKill); 522 523 RTListForEach(&g_GuestControlThreads, pNode, VBOXSERVICECTRLTHREAD, Node) 524 { 525 if ( pNode->enmType == kVBoxServiceCtrlThreadDataExec 526 && pNode->fStopped) 527 { 528 PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pNode->Node, VBOXSERVICECTRLTHREAD, Node); 529 530 int rc2 = VBoxServiceControlExecThreadShutdown(pNode); 531 if (RT_FAILURE(rc2)) 532 { 533 VBoxServiceError("ControlExec: Unable to shut down thread due to policy, rc=%Rrc\n", rc2); 534 if (RT_SUCCESS(rc)) 535 rc = rc2; 536 /* Keep going. */ 537 } 538 539 RTListNodeRemove(&pNode->Node); 540 RTMemFree(pNode); 541 pNode = pNext; 542 543 Assert(uProcsToKill); 544 uProcsToKill--; 545 if (!uProcsToKill) 546 break; 547 } 548 } 549 Assert(uProcsToKill == 0); 550 } 551 } 552 553 *pbAllowed = !fLimitReached; 554 555 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect); 556 if (RT_SUCCESS(rc)) 557 rc = rc2; 558 } 559 560 return rc; 561 } 562 563 564 /** 565 * Marks an guest execution thread as stopped and cleans up its internal pipe buffers. 566 * 567 * @param pThread Pointer to guest execution thread. 568 */ 569 void VBoxServiceControlExecThreadStop(const PVBOXSERVICECTRLTHREAD pThread) 570 { 571 AssertPtr(pThread); 572 573 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect); 574 if (RT_SUCCESS(rc)) 575 { 576 if (pThread->fStarted) 577 { 578 const PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData; 579 if (pData) 580 { 581 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Marking as stopped\n", pData->uPID); 582 583 VBoxServicePipeBufSetStatus(&pData->stdIn, false /* Disabled */); 584 VBoxServicePipeBufSetStatus(&pData->stdOut, false /* Disabled */); 585 VBoxServicePipeBufSetStatus(&pData->stdErr, false /* Disabled */); 586 587 /* Since the process is not alive anymore, destroy its local 588 * stdin pipe buffer - it's not used anymore and can eat up quite 589 * a bit of memory. */ 590 VBoxServicePipeBufDestroy(&pData->stdIn); 591 } 592 593 /* Mark as stopped. */ 594 ASMAtomicXchgBool(&pThread->fStarted, false); 595 ASMAtomicXchgBool(&pThread->fStopped, true); 596 } 597 598 RTCritSectLeave(&g_GuestControlThreadsCritSect); 599 } 600 } 601 1493 break; 1494 1495 VBoxServiceError("ControlExec: [PID %u]: Attempt #%d: Waiting for shutdown failed with rc=%Rrc ...\n", 1496 pThread->uPID, i + 1, rc); 1497 RTThreadSleep(500); /* Wait a bit before next try. */ 1498 } 1499 1500 /* Do not destroy critical section here! */ 1501 1502 /* 1503 * Destroy thread-specific data. 1504 */ 1505 vboxServiceControlExecThreadFree(pThread); 1506 1507 return rc; 1508 } 1509 -
trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.h
r38587 r39279 21 21 #include "VBoxServiceInternal.h" 22 22 23 int VBoxServiceControlExecThreadAlloc(PVBOXSERVICECTRLTHREAD pThread, 24 uint32_t u32ContextID, 25 const char *pszCmd, uint32_t uFlags, 26 const char *pszArgs, uint32_t uNumArgs, 27 const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars, 28 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS); 29 int VBoxServiceControlExecThreadAssignPID(PVBOXSERVICECTRLTHREADDATAEXEC pData, uint32_t uPID); 30 void VBoxServiceControlExecThreadDataDestroy(PVBOXSERVICECTRLTHREADDATAEXEC pData); 31 int VBoxServiceControlExecThreadGetOutput(uint32_t uPID, uint32_t uHandleId, uint32_t uTimeout, 32 uint8_t *pBuf, uint32_t cbSize, uint32_t *pcbRead); 33 int VBoxServiceControlExecThreadRemove(uint32_t uPID); 34 int VBoxServiceControlExecThreadSetInput(uint32_t uPID, bool fPendingClose, uint8_t *pBuf, 35 uint32_t cbSize, uint32_t *pcbWritten); 36 int VBoxServiceControlExecThreadStartAllowed(bool *pbAllowed); 37 void VBoxServiceControlExecThreadStop(const PVBOXSERVICECTRLTHREAD pThread); 23 void VBoxServiceControlExecThreadDestroy(PVBOXSERVICECTRLTHREAD pThread); 24 int VBoxServiceControlExecThreadPerform(uint32_t uPID, PVBOXSERVICECTRLREQUEST pRequest); 25 int VBoxServiceControlExecThreadShutdown(const PVBOXSERVICECTRLTHREAD pThread); 26 38 27 #endif /* !___VBoxServiceControlExecThread_h */ 39 28 -
trunk/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h
r38633 r39279 108 108 109 109 #ifdef VBOX_WITH_GUEST_CONTROL 110 typedef enum VBOXSERVICECTRLTHREADDATATYPE 111 { 112 kVBoxServiceCtrlThreadDataUnknown = 0, 113 kVBoxServiceCtrlThreadDataExec 114 } VBOXSERVICECTRLTHREADDATATYPE; 115 110 /** 111 * Pipe IDs for handling the guest process poll set. 112 */ 116 113 typedef enum VBOXSERVICECTRLPIPEID 117 114 { 118 VBOXSERVICECTRLPIPEID_STDIN = 0, 119 VBOXSERVICECTRLPIPEID_STDIN_ERROR, 120 VBOXSERVICECTRLPIPEID_STDIN_WRITABLE, 121 VBOXSERVICECTRLPIPEID_STDIN_INPUT_NOTIFY, 122 VBOXSERVICECTRLPIPEID_STDOUT, 123 VBOXSERVICECTRLPIPEID_STDERR 115 VBOXSERVICECTRLPIPEID_UNKNOWN = 0, 116 VBOXSERVICECTRLPIPEID_STDIN = 10, 117 VBOXSERVICECTRLPIPEID_STDIN_WRITABLE = 11, 118 /** Pipe for reading from guest process' stdout. */ 119 VBOXSERVICECTRLPIPEID_STDOUT = 40, 120 /** Pipe for reading from guest process' stderr. */ 121 VBOXSERVICECTRLPIPEID_STDERR = 50, 122 /** Notification pipe for waking up the guest process 123 * control thread. */ 124 VBOXSERVICECTRLPIPEID_IPC_NOTIFY = 100 124 125 } VBOXSERVICECTRLPIPEID; 125 126 126 127 /** 127 * Structure for holding buffered pipe data. 128 */ 129 typedef struct 130 { 131 /** The PID the pipe is assigned to. */ 132 uint32_t uPID; 133 /** The pipe's Id of enum VBOXSERVICECTRLPIPEID. */ 134 uint8_t uPipeId; 135 /** The data buffer. */ 136 uint8_t *pbData; 137 /** The amount of allocated buffer space. */ 138 uint32_t cbAllocated; 139 /** The actual used/occupied buffer space. */ 140 uint32_t cbSize; 141 /** Helper variable for keeping track of what 142 * already was processed and what not. */ 143 uint32_t cbOffset; 144 /** Critical section protecting this buffer structure. */ 145 RTCRITSECT CritSect; 146 /** Flag indicating whether this pipe buffer accepts new 147 * data to be written to or not. If not enabled, already 148 * (allocated) buffered data still can be read out. */ 149 bool fEnabled; 150 /** Set if it's necessary to write to the notification pipe. */ 151 bool fNeedNotification; 152 /** Set if the pipe needs to be closed after the next read/write. */ 153 bool fPendingClose; 154 /** The notification pipe associated with this buffer. 155 * This is NIL_RTPIPE for output pipes. */ 156 RTPIPE hNotificationPipeW; 157 /** The other end of hNotificationPipeW. */ 158 RTPIPE hNotificationPipeR; 159 /** The event semaphore for getting notified whether something 160 * has changed, e.g. written to this buffer or enabled/disabled it. */ 161 RTSEMEVENT hEventSem; 162 } VBOXSERVICECTRLEXECPIPEBUF; 163 /** Pointer to buffered pipe data. */ 164 typedef VBOXSERVICECTRLEXECPIPEBUF *PVBOXSERVICECTRLEXECPIPEBUF; 165 166 /** 167 * Structure for holding guest exection relevant data. 168 */ 169 typedef struct 170 { 171 uint32_t uPID; 172 char *pszCmd; 173 uint32_t uFlags; 174 char **papszArgs; 175 uint32_t uNumArgs; 176 char **papszEnv; 177 uint32_t uNumEnvVars; 178 char *pszUser; 179 char *pszPassword; 180 uint32_t uTimeLimitMS; 181 182 RTPIPE pipeStdInW; 183 VBOXSERVICECTRLEXECPIPEBUF stdIn; 184 VBOXSERVICECTRLEXECPIPEBUF stdOut; 185 VBOXSERVICECTRLEXECPIPEBUF stdErr; 186 187 } VBOXSERVICECTRLTHREADDATAEXEC; 188 /** Pointer to thread data. */ 189 typedef VBOXSERVICECTRLTHREADDATAEXEC *PVBOXSERVICECTRLTHREADDATAEXEC; 190 191 /* Structure for holding thread relevant data. */ 128 * Request types to perform on a started guest process. 129 */ 130 typedef enum VBOXSERVICECTRLREQUESTTYPE 131 { 132 /** Unknown request. */ 133 VBOXSERVICECTRLREQUEST_UNKNOWN = 0, 134 /** Main control thread asked used to quit. */ 135 VBOXSERVICECTRLREQUEST_QUIT = 1, 136 /** Performs reading from stdout. */ 137 VBOXSERVICECTRLREQUEST_STDOUT_READ = 50, 138 /** Performs reading from stderr. */ 139 VBOXSERVICECTRLREQUEST_STDERR_READ = 60, 140 /** Performs writing to stdin. */ 141 VBOXSERVICECTRLREQUEST_STDIN_WRITE = 70, 142 /** Same as VBOXSERVICECTRLREQUEST_STDIN_WRITE, but 143 * marks the end of input. */ 144 VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF = 71, 145 /** Kill process. 146 * @todo Implement this! */ 147 VBOXSERVICECTRLREQUEST_KILL = 90, 148 /** Gently ask process to terminate. 149 * @todo Implement this! */ 150 VBOXSERVICECTRLREQUEST_HANGUP = 91 151 } VBOXSERVICECTRLREQUESTTYPE; 152 153 /** 154 * Structure to perform a request on a started guest 155 * process. Needed for letting the main guest control thread 156 * to communicate (and wait) for a certain operation which 157 * will be done in context of the started guest process thread. 158 */ 159 typedef struct VBOXSERVICECTRLREQUEST 160 { 161 /** The request type to handle. */ 162 VBOXSERVICECTRLREQUESTTYPE enmType; 163 /** On input, this contains the (maximum) amount 164 * of buffered data to read or write. On output, 165 * this show the actual amount of data read/written. */ 166 size_t cbData; 167 /** The provided, allocated data buffer for input/output. */ 168 void *pvData; 169 /** The overall result of the operation. */ 170 int rc; 171 } VBOXSERVICECTRLREQUEST; 172 /** Pointer to request. */ 173 typedef VBOXSERVICECTRLREQUEST *PVBOXSERVICECTRLREQUEST; 174 175 /** 176 * Structure for holding data for one (started) guest process. 177 */ 192 178 typedef struct VBOXSERVICECTRLTHREAD 193 179 { … … 206 192 /** Context ID. */ 207 193 uint32_t uContextID; 208 /** Type of thread. See VBOXSERVICECTRLTHREADDATATYPE for more info. */ 209 VBOXSERVICECTRLTHREADDATATYPE enmType; 210 /** Pointer to actual thread data, depending on enmType. */ 211 void *pvData; 194 /** Critical section for thread-safe use. */ 195 RTCRITSECT CritSect; 196 197 /** @todo Document me! */ 198 uint32_t uPID; 199 char *pszCmd; 200 uint32_t uFlags; 201 char **papszArgs; 202 uint32_t uNumArgs; 203 char **papszEnv; 204 uint32_t uNumEnvVars; 205 /** Name of specified user account to run the 206 * guest process under. */ 207 char *pszUser; 208 /** Password of specified user account. */ 209 char *pszPassword; 210 /** Overall time limit (in ms) that the guest process 211 * is allowed to run. 0 for indefinite time. */ 212 uint32_t uTimeLimitMS; 213 RTSEMEVENTMULTI RequestEvent; 214 /** Pointer to the current IPC request being 215 * processed. */ 216 PVBOXSERVICECTRLREQUEST pRequest; 217 /** StdIn pipe for addressing writes to the 218 * guest process' stdin.*/ 219 RTPIPE pipeStdInW; 220 /** The notification pipe associated with this guest process. 221 * This is NIL_RTPIPE for output pipes. */ 222 RTPIPE hNotificationPipeW; 223 /** The other end of hNotificationPipeW. */ 224 RTPIPE hNotificationPipeR; 212 225 } VBOXSERVICECTRLTHREAD; 213 226 /** Pointer to thread data. */ … … 308 321 309 322 #ifdef VBOX_WITH_GUEST_CONTROL 310 extern void VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread); 311 extern int VBoxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread); 312 313 extern int VBoxServiceControlExecHandleCmdStartProcess(uint32_t u32ClientId, uint32_t uNumParms); 314 extern int VBoxServiceControlExecHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize); 315 extern int VBoxServiceControlExecHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms); 316 extern int VBoxServiceControlExecProcess(uint32_t uClientID, uint32_t uContext, 323 /* Guest control functions. */ 324 extern int VBoxServiceControlHandleCmdStartProc(uint32_t u32ClientId, uint32_t uNumParms); 325 extern int VBoxServiceControlHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize); 326 extern int VBoxServiceControlHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms); 327 extern const PVBOXSERVICECTRLTHREAD VBoxServiceControlGetThreadByPID(uint32_t uPID); 328 extern void VBoxServiceControlRemoveThread(PVBOXSERVICECTRLTHREAD pThread); 329 /* Guest process functions. */ 330 extern int VBoxServiceControlThreadStart(uint32_t uClientID, uint32_t uContext, 317 331 const char *pszCmd, uint32_t uFlags, 318 332 const char *pszArgs, uint32_t uNumArgs, 319 333 const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars, 320 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS); 321 extern void VBoxServiceControlExecThreadDataDestroy(PVBOXSERVICECTRLTHREADDATAEXEC pThread); 334 const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS, 335 PRTLISTNODE *ppNode); 336 extern void VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread); 337 extern int VBoxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread); 322 338 #endif /* VBOX_WITH_GUEST_CONTROL */ 323 339 -
trunk/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp
r39106 r39279 674 674 { szSysDir, "VBoxCredProv.dll" }, 675 675 676 /* On 64-bit we don't yet have the OpenGL DLLs in native format. 677 So just enumerate the 32-bit files in the SYSWOW directory. */ 676 /* Handy-to-know stuff, mostly patched stuff. */ 677 { szSysDir, "libWine.dll" }, 678 { szSysDir, "wined3d.dll" }, 679 { szSysDir, "d3d8.dll" }, 680 { szSysDir, "d3d9.dll" }, 681 682 /* Direct3D. */ 683 { szSysDir, "VBoxDispD3D.dll" }, 684 { szSysDir, "VBoxD3D8.dll" }, 685 { szSysDir, "VBoxD3D9.dll" }, 686 687 /* OpenGL. */ 688 { szSysDir, "VBoxOGLarrayspu.dll" }, 689 { szSysDir, "VBoxOGLcrutil.dll" }, 690 { szSysDir, "VBoxOGLerrorspu.dll" }, 691 { szSysDir, "VBoxOGLpackspu.dll" }, 692 { szSysDir, "VBoxOGLpassthroughspu.dll" }, 693 { szSysDir, "VBoxOGLfeedbackspu.dll" }, 694 { szSysDir, "VBoxOGL.dll" }, 695 696 /* On 64-bit OSes we also have our OpenGL + D3D DLLs in 32-bit, 697 so enumerate these files in the SYSWOW directory. */ 678 698 # ifdef RT_ARCH_AMD64 679 699 { szSysWowDir, "VBoxOGLarrayspu.dll" }, … … 684 704 { szSysWowDir, "VBoxOGLfeedbackspu.dll" }, 685 705 { szSysWowDir, "VBoxOGL.dll" }, 686 # else /* !RT_ARCH_AMD64 */ 687 { szSysDir, "VBoxOGLarrayspu.dll" }, 688 { szSysDir, "VBoxOGLcrutil.dll" }, 689 { szSysDir, "VBoxOGLerrorspu.dll" }, 690 { szSysDir, "VBoxOGLpackspu.dll" }, 691 { szSysDir, "VBoxOGLpassthroughspu.dll" }, 692 { szSysDir, "VBoxOGLfeedbackspu.dll" }, 693 { szSysDir, "VBoxOGL.dll" }, 694 # endif /* !RT_ARCH_AMD64 */ 695 706 707 { szSysWowDir, "VBoxDispD3D.dll" }, 708 # endif /* RT_ARCH_AMD64 */ 709 710 /* Installed drivers. */ 696 711 { szDriversDir, "VBoxGuest.sys" }, 697 712 { szDriversDir, "VBoxMouse.sys" }, 698 713 { szDriversDir, "VBoxSF.sys" }, 699 714 { szDriversDir, "VBoxVideo.sys" }, 715 { szDriversDir, "VBoxVideoWddm.sys" }, 700 716 }; 701 717
Note:
See TracChangeset
for help on using the changeset viewer.