Changeset 39418 in vbox for trunk/src/VBox/Main/src-client
- Timestamp:
- Nov 25, 2011 10:11:06 AM (13 years ago)
- svn:sync-xref-src-repo-rev:
- 75045
- Location:
- trunk/src/VBox/Main/src-client
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-client/GuestCtrlIO.cpp
r38439 r39418 318 318 } 319 319 320 uint32_t GuestProcessStream::GetSize() 321 { 322 return m_cbSize; 323 } 324 320 325 /** 321 326 * Tries to parse the next upcoming pair block within the internal -
trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp
r38627 r39418 162 162 AssertReturnVoid(uContextID); 163 163 164 LogFlowFunc(("Destroying callback with CID=%u ...\n", uContextID));165 166 /* Notify callback (if necessary). */167 int rc = callbackNotifyEx(uContextID, VERR_CANCELLED,168 Guest::tr("VM is shutting down, canceling uncompleted guest requests ..."));169 AssertRC(rc);170 171 164 CallbackMapIter it = mCallbackMap.find(uContextID); 172 165 if (it != mCallbackMap.end()) 173 166 { 167 LogFlowFunc(("Callback with CID=%u found\n", uContextID)); 174 168 if (it->second.pvData) 175 169 { 170 LogFlowFunc(("Destroying callback with CID=%u ...\n", uContextID)); 171 176 172 callbackFreeUserData(it->second.pvData); 177 173 it->second.cbData = 0; … … 385 381 386 382 /** 387 * TODO383 * Notifies a specified callback about its final status. 388 384 * 389 385 * @return IPRT status code. … … 395 391 { 396 392 AssertReturn(uContextID, VERR_INVALID_PARAMETER); 397 398 LogFlowFunc(("Notifying callback with CID=%u, iRC=%d, pszMsg=%s ...\n", 393 if (RT_FAILURE(iRC)) 394 AssertReturn(pszMessage, VERR_INVALID_PARAMETER); 395 396 LogFlowFunc(("Checking whether callback (CID=%u) needs notification iRC=%Rrc, pszMsg=%s\n", 399 397 uContextID, iRC, pszMessage ? pszMessage : "<No message given>")); 400 398 … … 430 428 && !fCompleted) 431 429 { 430 LogFlowFunc(("Notifying callback with CID=%u, iRC=%Rrc, pszMsg=%s\n", 431 uContextID, iRC, pszMessage ? pszMessage : "<No message given>")); 432 432 433 /* 433 434 * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only … … 437 438 * progress object to become signalled. 438 439 */ 439 if ( RT_SUCCESS(iRC) 440 && !pszMessage) 440 if (RT_SUCCESS(iRC)) 441 441 { 442 442 hRC = pProgress->notifyComplete(S_OK); … … 444 444 else 445 445 { 446 AssertPtrReturn(pszMessage, VERR_INVALID_PARAMETER); 446 447 447 hRC = pProgress->notifyComplete(VBOX_E_IPRT_ERROR /* Must not be S_OK. */, 448 448 COM_IIDOF(IGuest), … … 450 450 pszMessage); 451 451 } 452 } 453 ComAssertComRC(hRC); 452 453 LogFlowFunc(("Notified callback with CID=%u returned %Rhrc (0x%x)\n", 454 uContextID, hRC, hRC)); 455 } 456 else 457 LogFlowFunc(("Callback with CID=%u already notified\n", uContextID)); 454 458 455 459 /* … … 564 568 if (RT_SUCCESS(vrc)) 565 569 { 570 LogFlowFunc(("Waiting for callback completion (CID=%u, Stage=%RI32, timeout=%RI32ms) ...\n", 571 uContextID, lStage, lTimeout)); 566 572 HRESULT rc; 567 573 if (lStage < 0) … … 579 585 } 580 586 587 LogFlowFunc(("Callback (CID=%u) completed with rc=%Rrc\n", 588 uContextID, vrc)); 581 589 return vrc; 582 590 } … … 603 611 */ 604 612 #ifdef DEBUG_andy 605 LogFlowFunc(("pvExtension = %p, u32Function = %d, pvParms = %p, cbParms =%d\n",613 LogFlowFunc(("pvExtension=%p, u32Function=%d, pvParms=%p, cbParms=%d\n", 606 614 pvExtension, u32Function, pvParms, cbParms)); 607 615 #endif … … 686 694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 687 695 696 LogFlowFunc(("Execution status (CID=%u, pData=0x%p)\n", 697 uContextID, pData)); 698 688 699 PCALLBACKDATAEXECSTATUS pCallbackData = 689 700 (PCALLBACKDATAEXECSTATUS)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */); … … 695 706 /** @todo Copy void* buffer contents? */ 696 707 } 697 else698 AssertReleaseMsgFailed(("Process status (PID=%u, CID=%u) does not have allocated callback data!\n",699 pData->u32PID, uContextID));700 } 701 702 int vrc = VINF_SUCCESS;708 /* If pCallbackData is NULL this might be an old request for which no user data 709 * might exist anymore. */ 710 } 711 712 int vrc = VINF_SUCCESS; /* Function result. */ 713 int rcCallback = VINF_SUCCESS; /* Callback result. */ 703 714 Utf8Str errMsg; 704 715 705 716 /* Was progress canceled before? */ 706 bool fCbCanceled = callbackIsCanceled(uContextID); 707 if (!fCbCanceled) 708 { 717 bool fCanceled = callbackIsCanceled(uContextID); 718 if (!fCanceled) 719 { 720 /* Handle process map. This needs to be done first in order to have a valid 721 * map in case some callback gets notified a bit below. */ 722 723 /* Note: PIDs never get removed here in case the guest process signalled its 724 * end; instead the next call of GetProcessStatus() will remove the PID 725 * from the process map after we got the process' final (exit) status. 726 * See waitpid() for an example. */ 727 if (pData->u32PID) /* Only add/change a process if it has a valid PID (>0). */ 728 { 729 switch (pData->u32Status) 730 { 731 /* Interprete u32Flags as the guest process' exit code. */ 732 case PROC_STS_TES: 733 case PROC_STS_TOK: 734 vrc = processSetStatus(pData->u32PID, 735 (ExecuteProcessStatus_T)pData->u32Status, 736 pData->u32Flags /* Exit code. */, 0 /* Flags. */); 737 break; 738 /* Just reach through flags. */ 739 default: 740 vrc = processSetStatus(pData->u32PID, 741 (ExecuteProcessStatus_T)pData->u32Status, 742 0 /* Exit code. */, pData->u32Flags); 743 break; 744 } 745 } 746 709 747 /* Do progress handling. */ 710 748 switch (pData->u32Status) … … 725 763 errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"), 726 764 pData->u32Flags); 765 rcCallback = VERR_GENERAL_FAILURE; /** @todo */ 727 766 break; 728 767 … … 732 771 errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"), 733 772 pData->u32Flags); 773 rcCallback = VERR_GENERAL_FAILURE; /** @todo */ 734 774 break; 735 775 … … 737 777 LogRel(("Guest process (PID %u) timed out and was killed\n", pData->u32PID)); /** @todo Add process name */ 738 778 errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed")); 779 rcCallback = VERR_TIMEOUT; 739 780 break; 740 781 … … 742 783 LogRel(("Guest process (PID %u) timed out and could not be killed\n", pData->u32PID)); /** @todo Add process name */ 743 784 errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed")); 785 rcCallback = VERR_TIMEOUT; 744 786 break; 745 787 … … 758 800 } 759 801 else 802 { 760 803 errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down")); 804 rcCallback = VERR_CANCELLED; 805 } 761 806 break; 762 807 763 808 case PROC_STS_ERROR: 764 LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n", 765 pData->u32PID, pData->u32Flags)); /** @todo Add process name */ 809 if (pData->u32PID) 810 { 811 LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n", 812 pData->u32PID, pData->u32Flags)); /** @todo Add process name */ 813 } 814 else 815 { 816 switch (pData->u32Flags) 817 { 818 case VERR_MAX_PROCS_REACHED: 819 LogRel(("Guest process could not be started because maximum number of parallel guest processes has been reached\n")); 820 break; 821 822 default: 823 LogRel(("Guest process could not be started because of rc=%Rrc\n", 824 pData->u32Flags)); 825 } 826 827 } 766 828 errMsg = Utf8StrFmt(Guest::tr("Process execution failed with rc=%Rrc"), pData->u32Flags); 829 rcCallback = pData->u32Flags; /* Report back rc. */ 767 830 break; 768 831 … … 771 834 break; 772 835 } 773 774 /* Handle process map. */ 775 /** @todo What happens on/deal with PID reuse? */ 776 /** @todo How to deal with multiple updates at once? */ 836 } 837 else 838 { 839 errMsg = Utf8StrFmt(Guest::tr("Process execution canceled")); 840 rcCallback = VERR_CANCELLED; 841 } 842 843 /* Do we need to handle the callback error? */ 844 if (RT_FAILURE(rcCallback)) 845 { 846 AssertMsg(!errMsg.isEmpty(), ("Error message must not be empty!\n")); 847 848 /* Notify all callbacks which are still waiting on something 849 * which is related to the current PID. */ 777 850 if (pData->u32PID) 778 851 { 779 VBOXGUESTCTRL_PROCESS process; 780 vrc = processGetByPID(pData->u32PID, &process); 781 if (vrc == VERR_NOT_FOUND) 782 { 783 /* Not found, add to map. */ 784 vrc = processAdd(pData->u32PID, 785 (ExecuteProcessStatus_T)pData->u32Status, 786 pData->u32Flags /* Contains exit code. */, 787 0 /*Flags. */); 788 AssertRC(vrc); 789 } 790 else if (RT_SUCCESS(vrc)) 791 { 792 /* Process found, update process map. */ 793 vrc = processSetStatus(pData->u32PID, 794 (ExecuteProcessStatus_T)pData->u32Status, 795 pData->u32Flags /* Contains exit code. */, 796 0 /*Flags. */); 797 AssertRC(vrc); 798 } 799 else 800 AssertReleaseMsgFailed(("Process was neither found nor absent!?\n")); 801 } 802 } 803 else 804 errMsg = Utf8StrFmt(Guest::tr("Process execution canceled")); 805 806 if (!callbackIsComplete(uContextID)) 807 { 808 if ( errMsg.length() 809 || fCbCanceled) /* If canceled we have to report E_FAIL! */ 810 { 811 /* Notify all callbacks which are still waiting on something 812 * which is related to the current PID. */ 813 if (pData->u32PID) 814 { 815 vrc = callbackNotifyAllForPID(pData->u32PID, VERR_GENERAL_FAILURE, errMsg.c_str()); 816 if (RT_FAILURE(vrc)) 817 LogFlowFunc(("Failed to notify other callbacks for PID=%u\n", 818 pData->u32PID)); 819 } 820 821 /* Let the caller know what went wrong ... */ 822 int rc2 = callbackNotifyEx(uContextID, VERR_GENERAL_FAILURE, errMsg.c_str()); 852 int rc2 = callbackNotifyAllForPID(pData->u32PID, rcCallback, errMsg.c_str()); 823 853 if (RT_FAILURE(rc2)) 824 854 { 825 LogFlowFunc(("Failed to notify callback CID=%u for PID=%u\n", 826 uContextID, pData->u32PID)); 827 855 LogFlowFunc(("Failed to notify other callbacks for PID=%u\n", 856 pData->u32PID)); 828 857 if (RT_SUCCESS(vrc)) 829 858 vrc = rc2; 830 859 } 831 LogFlowFunc(("Process (CID=%u, status=%u) reported error: %s\n", 832 uContextID, pData->u32Status, errMsg.c_str())); 833 } 834 } 835 LogFlowFunc(("Returned with rc=%Rrc\n", vrc)); 860 } 861 862 /* Let the caller know what went wrong ... */ 863 int rc2 = callbackNotifyEx(uContextID, rcCallback, errMsg.c_str()); 864 if (RT_FAILURE(rc2)) 865 { 866 LogFlowFunc(("Failed to notify callback CID=%u for PID=%u\n", 867 uContextID, pData->u32PID)); 868 if (RT_SUCCESS(vrc)) 869 vrc = rc2; 870 } 871 LogFlowFunc(("Process (CID=%u, status=%u) reported: %s\n", 872 uContextID, pData->u32Status, errMsg.c_str())); 873 } 874 LogFlowFunc(("Returned with rc=%Rrc, rcCallback=%Rrc\n", 875 vrc, rcCallback)); 836 876 return vrc; 837 877 } … … 851 891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 852 892 893 LogFlowFunc(("Output status (CID=%u, pData=0x%p)\n", 894 uContextID, pData)); 895 853 896 PCALLBACKDATAEXECOUT pCallbackData = 854 897 (PCALLBACKDATAEXECOUT)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */); … … 878 921 } 879 922 } 880 else 881 AssertReleaseMsgFailed(("Process output status (PID=%u, CID=%u) does not have allocated callback data!\n", 882 pData->u32PID, uContextID)); 923 /* If pCallbackData is NULL this might be an old request for which no user data 924 * might exist anymore. */ 883 925 } 884 926 … … 909 951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 910 952 953 LogFlowFunc(("Input status (CID=%u, pData=0x%p)\n", 954 uContextID, pData)); 955 911 956 PCALLBACKDATAEXECINSTATUS pCallbackData = 912 957 (PCALLBACKDATAEXECINSTATUS)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */); … … 915 960 /* Save bytes processed. */ 916 961 pCallbackData->cbProcessed = pData->cbProcessed; 917 pCallbackData->u32Status = pData->u32Status; 918 pCallbackData->u32Flags = pData->u32Flags; 919 pCallbackData->u32PID = pData->u32PID; 920 } 921 else 922 AssertReleaseMsgFailed(("Process input status (PID=%u, CID=%u) does not have allocated callback data!\n", 923 pData->u32PID, uContextID)); 962 pCallbackData->u32Status = pData->u32Status; 963 pCallbackData->u32Flags = pData->u32Flags; 964 pCallbackData->u32PID = pData->u32PID; 965 } 966 /* If pCallbackData is NULL this might be an old request for which no user data 967 * might exist anymore. */ 924 968 } 925 969 … … 936 980 Assert(uContextID); 937 981 938 return callbackNotifyEx(uContextID, S_OK, 982 LogFlowFunc(("Client disconnected (CID=%u)\n,", uContextID)); 983 984 return callbackNotifyEx(uContextID, VERR_CANCELLED, 939 985 Guest::tr("Client disconnected")); 940 986 } 941 987 942 int Guest::processAdd(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, 943 uint32_t uExitCode, uint32_t uFlags) 988 /** 989 * Gets guest process information. Removes the process from the map 990 * after the process was marked as exited/terminated. 991 * 992 * @return IPRT status code. 993 * @param u32PID 994 * @param pProcess 995 */ 996 int Guest::processGetStatus(uint32_t u32PID, PVBOXGUESTCTRL_PROCESS pProcess) 944 997 { 945 998 AssertReturn(u32PID, VERR_INVALID_PARAMETER); 946 999 1000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1001 1002 GuestProcessMapIter it = mGuestProcessMap.find(u32PID); 1003 if (it != mGuestProcessMap.end()) 1004 { 1005 if (pProcess) 1006 { 1007 pProcess->mStatus = it->second.mStatus; 1008 pProcess->mExitCode = it->second.mExitCode; 1009 pProcess->mFlags = it->second.mFlags; 1010 } 1011 1012 /* If the is marked as stopped/terminated 1013 * remove it from the map. */ 1014 if (it->second.mStatus != ExecuteProcessStatus_Started) 1015 mGuestProcessMap.erase(it); 1016 1017 return VINF_SUCCESS; 1018 } 1019 1020 return VERR_NOT_FOUND; 1021 } 1022 1023 int Guest::processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags) 1024 { 1025 AssertReturn(u32PID, VERR_INVALID_PARAMETER); 1026 947 1027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 948 1028 949 GuestProcessMapIterConst it = mGuestProcessMap.find(u32PID); 950 if (it == mGuestProcessMap.end()) 1029 GuestProcessMapIter it = mGuestProcessMap.find(u32PID); 1030 if (it != mGuestProcessMap.end()) 1031 { 1032 it->second.mStatus = enmStatus; 1033 it->second.mExitCode = uExitCode; 1034 it->second.mFlags = uFlags; 1035 } 1036 else 951 1037 { 952 1038 VBOXGUESTCTRL_PROCESS process; … … 957 1043 958 1044 mGuestProcessMap[u32PID] = process; 959 960 return VINF_SUCCESS; 961 } 962 963 return VERR_ALREADY_EXISTS; 964 } 965 966 int Guest::processGetByPID(uint32_t u32PID, PVBOXGUESTCTRL_PROCESS pProcess) 967 { 968 AssertReturn(u32PID, VERR_INVALID_PARAMETER); 969 AssertPtrReturn(pProcess, VERR_INVALID_PARAMETER); 970 971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 972 973 GuestProcessMapIterConst it = mGuestProcessMap.find(u32PID); 974 if (it != mGuestProcessMap.end()) 975 { 976 pProcess->mStatus = it->second.mStatus; 977 pProcess->mExitCode = it->second.mExitCode; 978 pProcess->mFlags = it->second.mFlags; 979 980 return VINF_SUCCESS; 981 } 982 983 return VERR_NOT_FOUND; 984 } 985 986 int Guest::processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags) 987 { 988 AssertReturn(u32PID, VERR_INVALID_PARAMETER); 989 990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 991 992 GuestProcessMapIter it = mGuestProcessMap.find(u32PID); 993 if (it != mGuestProcessMap.end()) 994 { 995 it->second.mStatus = enmStatus; 996 it->second.mExitCode = uExitCode; 997 it->second.mFlags = uFlags; 998 999 return VINF_SUCCESS; 1000 } 1001 1002 return VERR_NOT_FOUND; 1045 } 1046 1047 return VINF_SUCCESS; 1003 1048 } 1004 1049 … … 1080 1125 * @param aUsername Username to execute tool under. 1081 1126 * @param aPassword The user's password. 1127 * @param uFlagsToAdd ExecuteProcessFlag flags to add to the execution operation. 1082 1128 * @param aProgress Pointer which receives the tool's progress object. Optional. 1083 1129 * @param aPID Pointer which receives the tool's PID. Optional. … … 1086 1132 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment), 1087 1133 IN_BSTR aUsername, IN_BSTR aPassword, 1134 ULONG uFlagsToAdd, 1135 GuestCtrlStreamObjects *pObjStdOut, GuestCtrlStreamObjects *pObjStdErr, 1088 1136 IProgress **aProgress, ULONG *aPID) 1089 1137 { … … 1091 1139 ULONG uPID; 1092 1140 1141 ULONG uFlags = ExecuteProcessFlag_Hidden 1142 | ExecuteProcessFlag_WaitForProcessStartOnly; 1143 if (uFlagsToAdd) 1144 uFlags |= uFlagsToAdd; 1145 1146 bool fWaitForOutput = false; 1147 if ( ( (uFlags & ExecuteProcessFlag_WaitForStdOut) 1148 && pObjStdOut) 1149 || ( (uFlags & ExecuteProcessFlag_WaitForStdErr) 1150 && pObjStdErr)) 1151 { 1152 fWaitForOutput = true; 1153 } 1154 1093 1155 HRESULT rc = ExecuteProcess(aTool, 1094 ExecuteProcessFlag_Hidden,1156 uFlags, 1095 1157 ComSafeArrayInArg(aArguments), 1096 1158 ComSafeArrayInArg(aEnvironment), 1097 1159 aUsername, aPassword, 1098 5 * 1000 /* Wait 5s for getting the process started. */,1160 0 /* No timeout. */, 1099 1161 &uPID, progressTool.asOutParam()); 1162 if ( SUCCEEDED(rc) 1163 && fWaitForOutput) 1164 { 1165 BOOL fCompleted; 1166 while ( SUCCEEDED(progressTool->COMGETTER(Completed)(&fCompleted)) 1167 && !fCompleted) 1168 { 1169 BOOL fCanceled; 1170 rc = progressTool->COMGETTER(Canceled)(&fCanceled); 1171 ComAssertRC(rc); 1172 if (fCanceled) 1173 { 1174 rc = setError(VBOX_E_IPRT_ERROR, 1175 tr("%s was cancelled"), Utf8Str(aDescription)); 1176 break; 1177 } 1178 1179 if ( (uFlags & ExecuteProcessFlag_WaitForStdOut) 1180 && pObjStdOut) 1181 { 1182 rc = executeStreamParse(uPID, ProcessOutputFlag_None /* StdOut */, *pObjStdOut); 1183 } 1184 1185 if ( (uFlags & ExecuteProcessFlag_WaitForStdErr) 1186 && pObjStdErr) 1187 { 1188 rc = executeStreamParse(uPID, ProcessOutputFlag_StdErr, *pObjStdErr); 1189 } 1190 1191 if (FAILED(rc)) 1192 break; 1193 } 1194 } 1195 1100 1196 if (SUCCEEDED(rc)) 1101 1197 { 1102 /* Wait for process to exit ... */ 1103 rc = progressTool->WaitForCompletion(-1); 1104 if (FAILED(rc)) return rc; 1105 1106 BOOL fCompleted = FALSE; 1107 BOOL fCanceled = FALSE; 1108 progressTool->COMGETTER(Completed)(&fCompleted); 1109 if (!fCompleted) 1110 progressTool->COMGETTER(Canceled)(&fCanceled); 1111 1112 if (fCompleted) 1113 { 1114 ExecuteProcessStatus_T retStatus; 1115 ULONG uRetExitCode, uRetFlags; 1116 if (SUCCEEDED(rc)) 1117 { 1118 rc = GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus); 1119 if (SUCCEEDED(rc)) 1120 { 1121 if (uRetExitCode != 0) /* Not equal 0 means some error occured. */ 1122 { 1123 /** @todo IPRT exit code to string! */ 1124 rc = setError(VBOX_E_IPRT_ERROR, 1125 tr("%s: Error %u occured"), 1126 Utf8Str(aDescription).c_str(), uRetExitCode); 1127 } 1128 else /* Return code 0, success. */ 1129 { 1130 if (aProgress) 1131 { 1132 /* Return the progress to the caller. */ 1133 progressTool.queryInterfaceTo(aProgress); 1134 } 1135 if (aPID) 1136 *aPID = uPID; 1137 } 1138 } 1139 } 1140 } 1141 else if (fCanceled) 1142 { 1143 rc = setError(VBOX_E_IPRT_ERROR, 1144 tr("%s was aborted"), aDescription); 1145 } 1146 else 1147 AssertReleaseMsgFailed(("%s: Operation neither completed nor canceled!?\n", 1148 Utf8Str(aDescription).c_str())); 1198 if (aProgress) 1199 { 1200 /* Return the progress to the caller. */ 1201 progressTool.queryInterfaceTo(aProgress); 1202 } 1203 1204 if (aPID) 1205 *aPID = uPID; 1149 1206 } 1150 1207 … … 1267 1324 1268 1325 /** 1269 * Tries to drain the guest's output (from stdout)and fill it into1326 * Tries to drain the guest's output and fill it into 1270 1327 * a guest process stream object for later usage. 1271 1328 * 1329 * @todo What's about specifying stderr? 1272 1330 * @return IPRT status code. 1273 1331 * @param aPID PID of process to get the output from. 1332 * @param aFlags Which stream to drain (stdout or stderr). 1274 1333 * @param stream Reference to guest process stream to fill. 1275 1334 */ 1276 int Guest::executeStreamDrain(ULONG aPID, GuestProcessStream &stream)1335 int Guest::executeStreamDrain(ULONG aPID, ULONG aFlags, GuestProcessStream &stream) 1277 1336 { 1278 1337 AssertReturn(aPID, VERR_INVALID_PARAMETER); 1279 1280 /** @todo Should we try to drain the stream harder? */1281 1338 1282 1339 int rc = VINF_SUCCESS; 1283 1340 for (;;) 1284 1341 { 1285 SafeArray<BYTE> aOutputData; 1286 HRESULT hr = GetProcessOutput(aPID, ProcessOutputFlag_None /* Stdout */, 1287 10 * 1000 /* Timeout in ms */, 1288 _64K, ComSafeArrayAsOutParam(aOutputData)); 1289 if ( SUCCEEDED(hr) 1290 && aOutputData.size()) 1291 { 1292 rc = stream.AddData(aOutputData.raw(), aOutputData.size()); 1293 if (RT_UNLIKELY(RT_FAILURE(rc))) 1342 SafeArray<BYTE> aData; 1343 HRESULT hr = getProcessOutputInternal(aPID, aFlags, 1344 0 /* Infinite timeout */, 1345 _64K, ComSafeArrayAsOutParam(aData), &rc); 1346 if (SUCCEEDED(hr)) 1347 { 1348 if (aData.size()) 1349 { 1350 rc = stream.AddData(aData.raw(), aData.size()); 1351 if (RT_UNLIKELY(RT_FAILURE(rc))) 1352 break; 1353 } 1354 1355 continue; /* Try one more time. */ 1356 } 1357 else /* No more output and/or error! */ 1358 { 1359 if (rc == VERR_NOT_FOUND) 1360 rc = VINF_SUCCESS; 1361 break; 1362 } 1363 } 1364 1365 return rc; 1366 } 1367 1368 /** 1369 * Tries to retrieve the next stream block of a given stream and 1370 * drains the process output only as much as needed to get this next 1371 * stream block. 1372 * 1373 * @return IPRT status code. 1374 * @param ulPID 1375 * @param ulFlags 1376 * @param stream 1377 * @param streamBlock 1378 */ 1379 int Guest::executeStreamGetNextBlock(ULONG ulPID, 1380 ULONG ulFlags, 1381 GuestProcessStream &stream, 1382 GuestProcessStreamBlock &streamBlock) 1383 { 1384 AssertReturn(!streamBlock.GetCount(), VERR_INVALID_PARAMETER); 1385 1386 bool fDrainStream = true; 1387 int rc; 1388 do 1389 { 1390 rc = stream.ParseBlock(streamBlock); 1391 if (RT_FAILURE(rc)) /* More data needed or empty buffer? */ 1392 { 1393 if (fDrainStream) 1394 { 1395 SafeArray<BYTE> aData; 1396 HRESULT hr = getProcessOutputInternal(ulPID, ulFlags, 1397 0 /* Infinite timeout */, 1398 _64K, ComSafeArrayAsOutParam(aData), &rc); 1399 if (SUCCEEDED(hr)) 1400 { 1401 if (aData.size()) 1402 { 1403 rc = stream.AddData(aData.raw(), aData.size()); 1404 if (RT_UNLIKELY(RT_FAILURE(rc))) 1405 break; 1406 } 1407 1408 continue; /* Try one more time. */ 1409 } 1410 else 1411 { 1412 /* No more output to drain from stream. */ 1413 if (rc == VERR_NOT_FOUND) 1414 { 1415 fDrainStream = false; 1416 continue; 1417 } 1418 1419 break; 1420 } 1421 } 1422 else 1423 { 1424 /* We haved drained the stream as much as we can and reached the 1425 * end of our stream buffer -- that means that there simply is no 1426 * stream block anymore, which is ok. */ 1427 if (rc == VERR_NO_DATA) 1428 rc = VINF_SUCCESS; 1294 1429 break; 1295 }1296 else /* No more output and/or error! */1297 break;1298 }1430 } 1431 } 1432 } 1433 while (!streamBlock.GetCount()); 1299 1434 1300 1435 return rc; … … 1306 1441 * 1307 1442 * @return IPRT status code. 1308 * @param aPID PID of process to get/parse the output from. 1443 * @param ulPID PID of process to get/parse the output from. 1444 * @param ulFlags ? 1309 1445 * @param stream Reference to process stream object to use. 1310 1446 * @param streamBlock Reference that receives the next stream block data. 1311 1447 * 1312 1448 */ 1313 int Guest::executeStreamGetNextBlock(ULONG aPID, GuestProcessStream &stream, 1314 GuestProcessStreamBlock &streamBlock) 1315 { 1316 int rc = executeStreamDrain(aPID, stream); 1317 if (RT_SUCCESS(rc)) 1318 { 1319 do 1320 { 1321 rc = stream.ParseBlock(streamBlock); 1322 if (streamBlock.GetCount()) 1323 break; /* We got a block, bail out! */ 1324 } while (RT_SUCCESS(rc)); 1325 1326 /* In case this was the last block, VERR_NO_DATA is returned. 1327 * Overwrite this to get a proper return value for the last block. */ 1328 if( streamBlock.GetCount() 1329 && rc == VERR_NO_DATA) 1330 { 1331 rc = VINF_SUCCESS; 1332 } 1333 } 1449 int Guest::executeStreamParseNextBlock(ULONG ulPID, 1450 ULONG ulFlags, 1451 GuestProcessStream &stream, 1452 GuestProcessStreamBlock &streamBlock) 1453 { 1454 AssertReturn(!streamBlock.GetCount(), VERR_INVALID_PARAMETER); 1455 1456 int rc; 1457 do 1458 { 1459 rc = stream.ParseBlock(streamBlock); 1460 if (RT_FAILURE(rc)) 1461 break; 1462 } 1463 while (!streamBlock.GetCount()); 1334 1464 1335 1465 return rc; … … 1342 1472 * 1343 1473 * @return HRESULT 1344 * @param aPID PID of process to get/parse the output from. 1474 * @param ulPID PID of process to get/parse the output from. 1475 * @param ulFlags ? 1345 1476 * @param streamObjects Reference to a guest stream object structure for 1346 1477 * storing the parsed data. 1347 1478 */ 1348 HRESULT Guest::executeStreamParse(ULONG aPID, GuestCtrlStreamObjects &streamObjects)1349 { 1350 GuestProcessStream guestStream;1351 HRESULT hr = executeStreamDrain(aPID, guestStream);1352 if ( SUCCEEDED(hr))1353 { 1354 for (;;)1479 HRESULT Guest::executeStreamParse(ULONG ulPID, ULONG ulFlags, GuestCtrlStreamObjects &streamObjects) 1480 { 1481 GuestProcessStream stream; 1482 int rc = executeStreamDrain(ulPID, ulFlags, stream); 1483 if (RT_SUCCESS(rc)) 1484 { 1485 do 1355 1486 { 1356 1487 /* Try to parse the stream output we gathered until now. If we still need more 1357 1488 * data the parsing routine will tell us and we just do another poll round. */ 1358 1489 GuestProcessStreamBlock curBlock; 1359 int vrc = guestStream.ParseBlock(curBlock); 1360 if (RT_SUCCESS(vrc)) 1361 { 1362 if (curBlock.GetCount()) 1363 { 1364 streamObjects.push_back(curBlock); 1365 } 1366 else 1367 break; /* No more data. */ 1368 } 1369 else /* Everything else would be an error! */ 1370 hr = setError(VBOX_E_IPRT_ERROR, 1371 tr("Error while parsing guest output (%Rrc)"), vrc); 1372 } 1373 } 1374 1375 /** @todo Add check if there now are any sream objects at all! */ 1376 1377 return hr; 1490 rc = executeStreamParseNextBlock(ulPID, ulFlags, stream, curBlock); 1491 if (RT_SUCCESS(rc)) 1492 streamObjects.push_back(curBlock); 1493 } while (RT_SUCCESS(rc)); 1494 1495 if (rc == VERR_NO_DATA) /* End of data reached. */ 1496 rc = VINF_SUCCESS; 1497 } 1498 1499 if (RT_FAILURE(rc)) 1500 return setError(VBOX_E_IPRT_ERROR, 1501 tr("Error while parsing guest output (%Rrc)"), rc); 1502 return rc; 1378 1503 } 1379 1504 … … 1461 1586 && !(aFlags & ExecuteProcessFlag_WaitForProcessStartOnly) 1462 1587 && !(aFlags & ExecuteProcessFlag_Hidden) 1463 && !(aFlags & ExecuteProcessFlag_NoProfile)) 1588 && !(aFlags & ExecuteProcessFlag_NoProfile) 1589 && !(aFlags & ExecuteProcessFlag_WaitForStdOut) 1590 && !(aFlags & ExecuteProcessFlag_WaitForStdErr)) 1464 1591 { 1465 1592 if (pRC) … … 1619 1746 if (RT_SUCCESS(vrc)) 1620 1747 { 1621 LogFlowFunc(("Waiting for HGCM callback (timeout=% dms) ...\n", aTimeoutMS));1748 LogFlowFunc(("Waiting for HGCM callback (timeout=%RI32ms) ...\n", aTimeoutMS)); 1622 1749 1623 1750 /* … … 1629 1756 PCALLBACKDATAEXECSTATUS pExecStatus = NULL; 1630 1757 1631 1758 /* 1632 1759 * Wait for the first stage (=0) to complete (that is starting the process). 1633 1760 */ … … 1713 1840 { 1714 1841 VBOXGUESTCTRL_PROCESS process; 1715 int vrc = processGet ByPID(aPID, &process);1842 int vrc = processGetStatus(aPID, &process); 1716 1843 if (RT_SUCCESS(vrc)) 1717 1844 { … … 1871 1998 STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData)) 1872 1999 { 2000 #ifndef VBOX_WITH_GUEST_CONTROL 2001 ReturnComNotImplemented(); 2002 #else /* VBOX_WITH_GUEST_CONTROL */ 2003 using namespace guestControl; 2004 2005 return getProcessOutputInternal(aPID, aFlags, aTimeoutMS, 2006 aSize, ComSafeArrayOutArg(aData), NULL /* rc */); 2007 #endif 2008 } 2009 2010 HRESULT Guest::getProcessOutputInternal(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, 2011 LONG64 aSize, ComSafeArrayOut(BYTE, aData), int *pRC) 2012 { 1873 2013 /** @todo r=bird: Eventually we should clean up all the timeout parameters 1874 2014 * in the API and have the same way of specifying infinite waits! */ … … 1898 2038 try 1899 2039 { 1900 VBOXGUESTCTRL_PROCESS proc ess;1901 int vrc = processGet ByPID(aPID, &process);2040 VBOXGUESTCTRL_PROCESS proc; 2041 int vrc = processGetStatus(aPID, &proc); 1902 2042 if (RT_FAILURE(vrc)) 2043 { 1903 2044 rc = setError(VBOX_E_IPRT_ERROR, 1904 Guest::tr(" Cannot get output from non-existent guest process (PID %u)"), aPID);1905 1906 if (SUCCEEDED(rc))2045 Guest::tr("Guest process (PID %u) does not exist"), aPID); 2046 } 2047 else 1907 2048 { 1908 2049 uint32_t uContextID = 0; … … 1979 2120 if (RT_SUCCESS(vrc)) 1980 2121 { 1981 LogFlowFunc(("Waiting for HGCM callback (timeout=% dms) ...\n", aTimeoutMS));2122 LogFlowFunc(("Waiting for HGCM callback (timeout=%RI32ms) ...\n", aTimeoutMS)); 1982 2123 1983 2124 /* … … 1987 2128 */ 1988 2129 1989 PCALLBACKDATAEXECOUT pExecOut = NULL;1990 1991 2130 /* 1992 2131 * Wait for the first output callback notification to arrive. 1993 2132 */ 1994 vrc = callbackWaitForCompletion(uContextID, -1 /* No staging */, aTimeoutMS);2133 vrc = callbackWaitForCompletion(uContextID, -1 /* No staging required */, aTimeoutMS); 1995 2134 if (RT_SUCCESS(vrc)) 1996 2135 { 2136 PCALLBACKDATAEXECOUT pExecOut = NULL; 1997 2137 vrc = callbackGetUserData(uContextID, NULL /* We know the type. */, 1998 2138 (void**)&pExecOut, NULL /* Don't need the size. */); … … 2042 2182 pProgress.setNull(); 2043 2183 } 2184 2185 if (pRC) 2186 *pRC = vrc; 2044 2187 } 2045 2188 catch (std::bad_alloc &) … … 2068 2211 { 2069 2212 VBOXGUESTCTRL_PROCESS process; 2070 int vrc = processGet ByPID(aPID, &process);2213 int vrc = processGetStatus(aPID, &process); 2071 2214 if (RT_SUCCESS(vrc)) 2072 2215 { -
trunk/src/VBox/Main/src-client/GuestCtrlImplDir.cpp
r39387 r39418 126 126 ComSafeArrayAsInParam(env), 127 127 aUsername, aPassword, 128 ExecuteProcessFlag_None, 129 NULL, NULL, 128 130 NULL /* Progress */, NULL /* PID */); 129 131 } … … 301 303 { 302 304 return executeStreamGetNextBlock(it->second.mPID, 305 ProcessOutputFlag_None /* StdOut */, 303 306 it->second.mStream, streamBlock); 304 307 } … … 405 408 406 409 ULONG uPID; 407 /** @todo Don't wait for tool to finish! Might take a lot of time! */ 410 /* We only start the directory listing and requesting stdout data but don't get its 411 * data here; this is done in sequential IGuest::DirectoryRead calls then. */ 408 412 hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_LS).raw(), Bstr("Opening directory").raw(), 409 413 ComSafeArrayAsInParam(args), 410 414 ComSafeArrayAsInParam(env), 411 415 aUsername, aPassword, 416 ExecuteProcessFlag_WaitForStdOut, 417 NULL, NULL, 412 418 NULL /* Progress */, &uPID); 413 419 if (SUCCEEDED(hr)) … … 477 483 */ 478 484 ULONG uPID; 485 GuestCtrlStreamObjects stdOut; 479 486 hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_STAT).raw(), Bstr("Querying directory information").raw(), 480 487 ComSafeArrayAsInParam(args), 481 488 ComSafeArrayAsInParam(env), 482 489 aUsername, aPassword, 490 ExecuteProcessFlag_WaitForStdOut, 491 &stdOut, NULL /* stdErr */, 483 492 NULL /* Progress */, &uPID); 484 493 if (SUCCEEDED(hr)) 485 494 { 486 GuestCtrlStreamObjects streamObjs; 487 hr = executeStreamParse(uPID, streamObjs); 488 if (SUCCEEDED(hr)) 495 int rc = VINF_SUCCESS; 496 497 Assert(stdOut.size()); 498 const char *pszFsType = stdOut[0].GetString("ftype"); 499 if (!pszFsType) /* Attribute missing? */ 500 rc = VERR_NOT_FOUND; 501 if ( RT_SUCCESS(rc) 502 && strcmp(pszFsType, "d")) /* Directory? */ 489 503 { 490 int rc = VINF_SUCCESS; 491 492 Assert(streamObjs.size()); 493 const char *pszFsType = streamObjs[0].GetString("ftype"); 494 if (!pszFsType) /* Attribute missing? */ 495 rc = VERR_NOT_FOUND; 496 if ( RT_SUCCESS(rc) 497 && strcmp(pszFsType, "d")) /* Directory? */ 498 { 499 rc = VERR_FILE_NOT_FOUND; 500 /* This is not critical for Main, so don't set hr -- 501 * we will take care of rc then. */ 502 } 503 if ( RT_SUCCESS(rc) 504 && aObjInfo) /* Do we want object details? */ 505 { 506 hr = executeStreamQueryFsObjInfo(aDirectory, streamObjs[0], 507 aObjInfo, enmAddAttribs); 508 } 509 510 if (pRC) 511 *pRC = rc; 504 rc = VERR_FILE_NOT_FOUND; 505 /* This is not critical for Main, so don't set hr -- 506 * we will take care of rc then. */ 512 507 } 508 if ( RT_SUCCESS(rc) 509 && aObjInfo) /* Do we want object details? */ 510 { 511 hr = executeStreamQueryFsObjInfo(aDirectory, stdOut[0], 512 aObjInfo, enmAddAttribs); 513 } 514 515 if (pRC) 516 *pRC = rc; 513 517 } 514 518 } … … 540 544 if (RT_SUCCESS(rc)) 541 545 { 542 ComObjPtr <GuestDirEntry> pDirEntry; 543 hr = pDirEntry.createObject(); 544 ComAssertComRC(hr); 545 546 Assert(streamBlock.GetCount()); 547 hr = pDirEntry->init(this, streamBlock); 548 if (SUCCEEDED(hr)) 546 if (streamBlock.GetCount()) 549 547 { 550 pDirEntry.queryInterfaceTo(aDirEntry); 548 ComObjPtr <GuestDirEntry> pDirEntry; 549 hr = pDirEntry.createObject(); 550 ComAssertComRC(hr); 551 552 Assert(streamBlock.GetCount()); 553 hr = pDirEntry->init(this, streamBlock); 554 if (SUCCEEDED(hr)) 555 { 556 pDirEntry.queryInterfaceTo(aDirEntry); 557 } 558 else 559 hr = setError(VBOX_E_IPRT_ERROR, 560 Guest::tr("Failed to init guest directory entry")); 551 561 } 552 562 else 553 hr = setError(VBOX_E_IPRT_ERROR, 554 Guest::tr("Failed to init guest directory entry")); 555 } 556 else if (rc == VERR_NO_DATA) 557 { 558 /* No more directory entries to read. That's fine. */ 559 hr = E_ABORT; /** @todo Find/define a better rc! */ 563 { 564 /* No more directory entries to read. That's fine. */ 565 hr = E_ABORT; /** @todo Find/define a better rc! */ 566 } 560 567 } 561 568 else -
trunk/src/VBox/Main/src-client/GuestCtrlImplFile.cpp
r38627 r39418 135 135 */ 136 136 ULONG uPID; 137 GuestCtrlStreamObjects stdOut; 137 138 hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_STAT).raw(), Bstr("Querying file information").raw(), 138 139 ComSafeArrayAsInParam(args), 139 140 ComSafeArrayAsInParam(env), 140 141 aUsername, aPassword, 142 ExecuteProcessFlag_WaitForStdOut, 143 &stdOut, NULL, 141 144 NULL /* Progress */, &uPID); 142 145 if (SUCCEEDED(hr)) 143 146 { 144 GuestCtrlStreamObjects streamObjs; 145 hr = executeStreamParse(uPID, streamObjs); 146 if (SUCCEEDED(hr)) 147 int rc = VINF_SUCCESS; 148 const char *pszFsType = stdOut[0].GetString("ftype"); 149 if (!pszFsType) /* Attribute missing? */ 150 rc = VERR_NOT_FOUND; 151 if ( RT_SUCCESS(rc) 152 && strcmp(pszFsType, "-")) /* Regular file? */ 147 153 { 148 int rc = VINF_SUCCESS; 149 const char *pszFsType = streamObjs[0].GetString("ftype"); 150 if (!pszFsType) /* Attribute missing? */ 151 rc = VERR_NOT_FOUND; 152 if ( RT_SUCCESS(rc) 153 && strcmp(pszFsType, "-")) /* Regular file? */ 154 { 155 rc = VERR_FILE_NOT_FOUND; 156 /* This is not critical for Main, so don't set hr -- 157 * we will take care of rc then. */ 158 } 159 if ( RT_SUCCESS(rc) 160 && aObjInfo) /* Do we want object details? */ 161 { 162 hr = executeStreamQueryFsObjInfo(aFile, streamObjs[0], 163 aObjInfo, enmAddAttribs); 164 } 165 166 if (pRC) 167 *pRC = rc; 154 rc = VERR_FILE_NOT_FOUND; 155 /* This is not critical for Main, so don't set hr -- 156 * we will take care of rc then. */ 168 157 } 158 if ( RT_SUCCESS(rc) 159 && aObjInfo) /* Do we want object details? */ 160 { 161 hr = executeStreamQueryFsObjInfo(aFile, stdOut[0], 162 aObjInfo, enmAddAttribs); 163 } 164 165 if (pRC) 166 *pRC = rc; 169 167 } 170 168 } -
trunk/src/VBox/Main/src-client/GuestCtrlImplTasks.cpp
r38627 r39418 522 522 * actual copying, start the guest part now. 523 523 */ 524 rc = pGuest->ExecuteProcess(Bstr(VBOXSERVICE_TOOL_CAT).raw(), 525 ExecuteProcessFlag_Hidden, 526 ComSafeArrayAsInParam(args), 527 ComSafeArrayAsInParam(env), 528 Bstr(aTask->strUserName).raw(), 529 Bstr(aTask->strPassword).raw(), 530 5 * 1000 /* Wait 5s for getting the process started. */, 531 &uPID, execProgress.asOutParam()); 524 rc = pGuest->executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_CAT).raw(), 525 Bstr("Copying file to host").raw(), 526 ComSafeArrayAsInParam(args), 527 ComSafeArrayAsInParam(env), 528 Bstr(aTask->strUserName).raw(), 529 Bstr(aTask->strPassword).raw(), 530 ExecuteProcessFlag_WaitForStdOut, 531 NULL, NULL, 532 execProgress.asOutParam(), &uPID); 532 533 if (FAILED(rc)) 533 534 rc = GuestTask::setProgressErrorInfo(rc, aTask->progress, pGuest); … … 551 552 size_t cbTransfered = 0; 552 553 SafeArray<BYTE> aOutputData(_64K); 553 while (SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted)))) 554 { 555 rc = this->GetProcessOutput(uPID, ProcessOutputFlag_None, 556 10 * 1000 /* Timeout in ms */, 557 _64K, ComSafeArrayAsOutParam(aOutputData)); 554 while ( SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted))) 555 && !fCompleted) 556 { 557 rc = pGuest->GetProcessOutput(uPID, ProcessOutputFlag_None /* StdOut */, 558 0 /* No timeout. */, 559 _64K, ComSafeArrayAsOutParam(aOutputData)); 558 560 if (SUCCEEDED(rc)) 559 561 { 560 if (!aOutputData.size()) 561 { 562 /* 563 * Only bitch about an unexpected end of a file when there already 564 * was data read from that file. If this was the very first read we can 565 * be (almost) sure that this file is not meant to be read by the specified user. 566 */ 567 if ( cbTransfered 568 && cbToRead) 562 if (aOutputData.size()) 563 { 564 vrc = RTFileWrite(hFileDest, aOutputData.raw(), aOutputData.size(), NULL /* No partial writes */); 565 if (RT_FAILURE(vrc)) 569 566 { 570 567 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress, 571 Guest::tr("Unexpected end of file \"%s\" (%u bytes left, %u bytes written)"), 572 aTask->strSource.c_str(), cbToRead, cbTransfered); 568 Guest::tr("Error writing to file \"%s\" (%u bytes left), rc=%Rrc"), 569 aTask->strSource.c_str(), cbToRead, vrc); 570 break; 573 571 } 574 break; 575 } 576 577 vrc = RTFileWrite(hFileDest, aOutputData.raw(), aOutputData.size(), NULL /* No partial writes */); 578 if (RT_FAILURE(vrc)) 579 { 580 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress, 581 Guest::tr("Error writing to file \"%s\" (%u bytes left), rc=%Rrc"), 582 aTask->strSource.c_str(), cbToRead, vrc); 583 break; 584 } 585 586 cbToRead -= aOutputData.size(); 587 cbTransfered += aOutputData.size(); 588 589 aTask->progress->SetCurrentOperationProgress(cbTransfered / (lFileSize / 100.0)); 572 573 cbToRead -= aOutputData.size(); 574 Assert(cbToRead >= 0); 575 cbTransfered += aOutputData.size(); 576 577 aTask->progress->SetCurrentOperationProgress(cbTransfered / (lFileSize / 100.0)); 578 } 579 580 /* Nothing read this time; try next round. */ 590 581 } 591 582 else … … 596 587 } 597 588 589 RTFileClose(hFileDest); 590 591 if ( cbTransfered 592 && (cbTransfered != lFileSize)) 593 { 594 /* 595 * Only bitch about an unexpected end of a file when there already 596 * was data read from that file. If this was the very first read we can 597 * be (almost) sure that this file is not meant to be read by the specified user. 598 */ 599 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress, 600 Guest::tr("Unexpected end of file \"%s\" (%u bytes total, %u bytes transferred)"), 601 aTask->strSource.c_str(), lFileSize, cbTransfered); 602 } 603 598 604 if (SUCCEEDED(rc)) 599 605 aTask->progress->notifyComplete(S_OK); 600 601 RTFileClose(hFileDest);602 606 } 603 607 } -
trunk/src/VBox/Main/src-client/GuestImpl.cpp
r39248 r39418 120 120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 121 121 122 /* Clean up callback data. */122 /* Notify left over callbacks that we are about to shutdown ... */ 123 123 CallbackMapIter it; 124 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++) 125 { 126 int rc2 = callbackNotifyEx(it->first, VERR_CANCELLED, 127 Guest::tr("VM is shutting down, canceling uncompleted guest requests ...")); 128 AssertRC(rc2); 129 } 130 131 /* Destroy left over callback data. */ 124 132 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++) 125 133 callbackDestroy(it->first);
Note:
See TracChangeset
for help on using the changeset viewer.