Changeset 39431 in vbox
- Timestamp:
- Nov 28, 2011 9:32:33 AM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp
r39418 r39431 453 453 } 454 454 455 /** 456 * Prints the desired guest output to a stream. 457 * 458 * @return IPRT status code. 459 * @param pGuest Pointer to IGuest interface. 460 * @param uPID PID of guest process to get the output from. 461 * @param fOutputFlags Output flags of type ProcessOutputFlag. 462 * @param cMsTimeout Timeout value (in ms) to wait for output. 463 */ 464 static int ctrlExecPrintOutput(IGuest *pGuest, ULONG uPID, 465 PRTSTREAM pStrmOutput, uint32_t fOutputFlags, 466 uint32_t cMsTimeout) 467 { 468 AssertPtrReturn(pGuest, VERR_INVALID_POINTER); 469 AssertReturn(uPID, VERR_INVALID_PARAMETER); 470 AssertPtrReturn(pStrmOutput, VERR_INVALID_POINTER); 471 472 SafeArray<BYTE> aOutputData; 473 ULONG cbOutputData = 0; 474 475 int vrc = VINF_SUCCESS; 476 HRESULT rc = pGuest->GetProcessOutput(uPID, fOutputFlags, 477 cMsTimeout, 478 _64K, ComSafeArrayAsOutParam(aOutputData)); 479 if (FAILED(rc)) 480 { 481 vrc = ctrlPrintError(pGuest, COM_IIDOF(IGuest)); 482 cbOutputData = 0; 483 } 484 else 485 { 486 cbOutputData = aOutputData.size(); 487 if (cbOutputData > 0) 488 { 489 BYTE *pBuf = aOutputData.raw(); 490 AssertPtr(pBuf); 491 pBuf[cbOutputData - 1] = 0; /* Properly terminate buffer. */ 492 493 /** @todo r=bird: Use a VFS I/O stream filter for doing this, it's a 494 * generic problem and the new VFS APIs will handle it more 495 * transparently. (requires writing dos2unix/unix2dos filters ofc) */ 496 497 /* 498 * If aOutputData is text data from the guest process' stdout or stderr, 499 * it has a platform dependent line ending. So standardize on 500 * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on 501 * Windows. Otherwise we end up with CR/CR/LF on Windows. 502 */ 503 504 char *pszBufUTF8; 505 vrc = RTStrCurrentCPToUtf8(&pszBufUTF8, (const char*)aOutputData.raw()); 506 if (RT_SUCCESS(vrc)) 507 { 508 cbOutputData = strlen(pszBufUTF8); 509 510 ULONG cbOutputDataPrint = cbOutputData; 511 for (char *s = pszBufUTF8, *d = s; 512 s - pszBufUTF8 < (ssize_t)cbOutputData; 513 s++, d++) 514 { 515 if (*s == '\r') 516 { 517 /* skip over CR, adjust destination */ 518 d--; 519 cbOutputDataPrint--; 520 } 521 else if (s != d) 522 *d = *s; 523 } 524 525 vrc = RTStrmWrite(pStrmOutput, pszBufUTF8, cbOutputDataPrint); 526 if (RT_FAILURE(vrc)) 527 RTMsgError("Unable to write output, rc=%Rrc\n", vrc); 528 529 RTStrFree(pszBufUTF8); 530 } 531 else 532 RTMsgError("Unable to convert output, rc=%Rrc\n", vrc); 533 } 534 } 535 536 return vrc; 537 } 538 539 /** 540 * Returns the remaining time (in ms) based on the start time and a set 541 * timeout value. Returns RT_INDEFINITE_WAIT if no timeout was specified. 542 * 543 * @return RTMSINTERVAL Time left (in ms). 544 * @param u64StartMs Start time (in ms). 545 * @param u32TimeoutMs Timeout value (in ms). 546 */ 547 inline RTMSINTERVAL ctrlExecGetRemainingTime(uint64_t u64StartMs, uint32_t u32TimeoutMs) 548 { 549 if (!u32TimeoutMs) /* If no timeout specified, wait forever. */ 550 return RT_INDEFINITE_WAIT; 551 552 uint64_t u64ElapsedMs = RTTimeMilliTS() - u64StartMs; 553 if (u64ElapsedMs >= u32TimeoutMs) 554 return 0; 555 556 return u32TimeoutMs - u64ElapsedMs; 557 } 558 455 559 /* <Missing docuemntation> */ 456 static int handleCtrlExecProgram(ComPtr<IGuest> guest, HandlerArg *pArg)560 static int handleCtrlExecProgram(ComPtr<IGuest> pGuest, HandlerArg *pArg) 457 561 { 458 562 AssertPtrReturn(pArg, VERR_INVALID_PARAMETER); … … 488 592 489 593 Utf8Str Utf8Cmd; 490 uint32_t fExecFlags = ExecuteProcessFlag_None; 491 uint32_t fOutputFlags = ProcessOutputFlag_None; 594 uint32_t fExecFlags = ExecuteProcessFlag_None; 492 595 com::SafeArray<IN_BSTR> args; 493 596 com::SafeArray<IN_BSTR> env; … … 498 601 bool fOutputBinary = false; 499 602 bool fWaitForExit = false; 500 bool fWaitForStdOut = false;501 603 bool fVerbose = false; 502 604 … … 571 673 case GETOPTDEF_EXEC_WAITFORSTDOUT: 572 674 fExecFlags |= ExecuteProcessFlag_WaitForStdOut; 573 fWaitForExit = fWaitForStdOut =true;675 fWaitForExit = true; 574 676 break; 575 677 576 678 case GETOPTDEF_EXEC_WAITFORSTDERR: 577 679 fExecFlags |= ExecuteProcessFlag_WaitForStdErr; 578 fWaitForExit = (fOutputFlags |= ProcessOutputFlag_StdErr) ? true : false;680 fWaitForExit = true; 579 681 break; 580 682 … … 622 724 ComPtr<IProgress> progress; 623 725 ULONG uPID = 0; 624 rc = guest->ExecuteProcess(Bstr(Utf8Cmd).raw(),726 rc = pGuest->ExecuteProcess(Bstr(Utf8Cmd).raw(), 625 727 fExecFlags, 626 728 ComSafeArrayAsInParam(args), … … 632 734 progress.asOutParam()); 633 735 if (FAILED(rc)) 634 return ctrlPrintError( guest, COM_IIDOF(IGuest));736 return ctrlPrintError(pGuest, COM_IIDOF(IGuest)); 635 737 636 738 if (fVerbose) … … 668 770 RTMsgError("Unable to set stdout's binary mode, rc=%Rrc\n", vrc); 669 771 772 PRTSTREAM pStream = g_pStdOut; /* StdOut by default. */ 773 AssertPtr(pStream); 774 670 775 /* Wait for process to exit ... */ 671 776 BOOL fCompleted = FALSE; … … 673 778 while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted)))) 674 779 { 675 SafeArray<BYTE> aOutputData; 676 ULONG cbOutputData = 0; 677 678 /* 679 * Some data left to output? 680 */ 681 if (fOutputFlags || fWaitForStdOut) 682 { 683 /** @todo r=bird: The timeout argument is bogus in several 684 * ways: 685 * 1. RT_MAX will evaluate the arguments twice, which may 686 * result in different values because RTTimeMilliTS() 687 * returns a higher value the 2nd time. Worst case: 688 * Imagine when RT_MAX calculates the remaining time 689 * out (first expansion) there is say 60 ms left. Then 690 * we're preempted and rescheduled after, say, 120 ms. 691 * We call RTTimeMilliTS() again and ends up with a 692 * value -60 ms, which translate to a UINT32_MAX - 59 693 * ms timeout. 694 * 695 * 2. When the period expires, we will wait forever since 696 * both 0 and -1 mean indefinite timeout with this API, 697 * at least that's one way of reading the main code. 698 * 699 * 3. There is a signed/unsigned ambiguity in the 700 * RT_MAX expression. The left hand side is signed 701 * integer (0), the right side is unsigned 64-bit. From 702 * what I can tell, the compiler will treat this as 703 * unsigned 64-bit and never return 0. 704 */ 705 rc = guest->GetProcessOutput(uPID, fOutputFlags, 706 RT_MAX(0, cMsTimeout - (RTTimeMilliTS() - u64StartMS)) /* Timeout in ms */, 707 _64K, ComSafeArrayAsOutParam(aOutputData)); 708 if (FAILED(rc)) 709 { 710 vrc = ctrlPrintError(guest, COM_IIDOF(IGuest)); 711 cbOutputData = 0; 712 } 713 else 714 { 715 cbOutputData = aOutputData.size(); 716 if (cbOutputData > 0) 717 { 718 BYTE *pBuf = aOutputData.raw(); 719 AssertPtr(pBuf); 720 pBuf[cbOutputData - 1] = 0; /* Properly terminate buffer. */ 721 722 /** @todo r=bird: Use a VFS I/O stream filter for doing this, it's a 723 * generic problem and the new VFS APIs will handle it more 724 * transparently. (requires writing dos2unix/unix2dos filters ofc) */ 725 726 /* 727 * If aOutputData is text data from the guest process' stdout or stderr, 728 * it has a platform dependent line ending. So standardize on 729 * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on 730 * Windows. Otherwise we end up with CR/CR/LF on Windows. 731 */ 732 733 char *pszBufUTF8; 734 vrc = RTStrCurrentCPToUtf8(&pszBufUTF8, (const char*)aOutputData.raw()); 735 if (RT_SUCCESS(vrc)) 736 { 737 cbOutputData = strlen(pszBufUTF8); 738 739 ULONG cbOutputDataPrint = cbOutputData; 740 for (char *s = pszBufUTF8, *d = s; 741 s - pszBufUTF8 < (ssize_t)cbOutputData; 742 s++, d++) 743 { 744 if (*s == '\r') 745 { 746 /* skip over CR, adjust destination */ 747 d--; 748 cbOutputDataPrint--; 749 } 750 else if (s != d) 751 *d = *s; 752 } 753 754 vrc = RTStrmWrite(g_pStdOut, pszBufUTF8, cbOutputDataPrint); 755 if (RT_FAILURE(vrc)) 756 RTMsgError("Unable to write output, rc=%Rrc\n", vrc); 757 758 RTStrFree(pszBufUTF8); 759 } 760 else 761 RTMsgError("Unable to convert output, rc=%Rrc\n", vrc); 762 } 763 } 764 } 765 766 /* No more output data left? */ 767 if (cbOutputData <= 0) 768 { 769 /* Only break out from process handling loop if we processed (displayed) 770 * all output data or if there simply never was output data and the process 771 * has been marked as complete. */ 772 if (fCompleted) 773 break; 780 /* Do we need to output stuff? */ 781 uint32_t cMsTimeLeft; 782 if (fExecFlags & ExecuteProcessFlag_WaitForStdOut) 783 { 784 cMsTimeLeft = ctrlExecGetRemainingTime(u64StartMS, cMsTimeout); 785 if (cMsTimeLeft) 786 vrc = ctrlExecPrintOutput(pGuest, uPID, 787 pStream, ProcessOutputFlag_None /* StdOut */, 788 cMsTimeLeft == RT_INDEFINITE_WAIT ? 0 : cMsTimeLeft); 789 } 790 791 if (fExecFlags & ExecuteProcessFlag_WaitForStdErr) 792 { 793 cMsTimeLeft = ctrlExecGetRemainingTime(u64StartMS, cMsTimeout); 794 if (cMsTimeLeft) 795 vrc = ctrlExecPrintOutput(pGuest, uPID, 796 pStream, ProcessOutputFlag_StdErr /* StdErr */, 797 cMsTimeLeft == RT_INDEFINITE_WAIT ? 0 : cMsTimeLeft); 774 798 } 775 799 … … 820 844 ExecuteProcessStatus_T retStatus; 821 845 ULONG uRetExitCode, uRetFlags; 822 rc = guest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);846 rc = pGuest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus); 823 847 if (SUCCEEDED(rc) && fVerbose) 824 848 RTPrintf("Exit code=%u (Status=%u [%s], Flags=%u)\n", uRetExitCode, retStatus, ctrlExecProcessStatusToText(retStatus), uRetFlags);
Note:
See TracChangeset
for help on using the changeset viewer.