Changeset 35170 in vbox
- Timestamp:
- Dec 16, 2010 10:40:53 AM (14 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 2 edited
- 1 copied
-
GuestCtrlImpl.cpp (copied) (copied from trunk/src/VBox/Main/GuestImpl.cpp ) (5 diffs)
-
GuestImpl.cpp (modified) (4 diffs)
-
Makefile.kmk (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/GuestCtrlImpl.cpp
r35164 r35170 1 1 /* $Id$ */ 2 3 2 /** @file 4 * 5 * VirtualBox COM class implementation 3 * VirtualBox COM class implementation: Guest 6 4 */ 7 5 … … 43 41 #include <memory> 44 42 45 // defines46 /////////////////////////////////////////////////////////////////////////////47 48 // constructor / destructor49 /////////////////////////////////////////////////////////////////////////////50 51 DEFINE_EMPTY_CTOR_DTOR (Guest)52 53 43 struct Guest::TaskGuest 54 44 { … … 215 205 if (!RTFileExists(aTask->strSource.c_str())) 216 206 { 217 /* Since this task runs in another thread than the main Guest object 218 * we cannot rely on notifyComplete's internal lookup - so do this ourselves. */ 219 rc = VBOX_E_IPRT_ERROR; 220 aTask->progress->notifyComplete(rc, 221 COM_IIDOF(IGuest), 222 Guest::getStaticComponentName(), 223 Guest::tr("Source file \"%s\" does not exist"), aTask->strSource.c_str()); 207 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress, 208 Guest::tr("Source file \"%s\" does not exist"), 209 aTask->strSource.c_str()); 224 210 } 225 211 else … … 782 768 #endif 783 769 784 HRESULT Guest::FinalConstruct()785 {786 return S_OK;787 }788 789 void Guest::FinalRelease()790 {791 uninit ();792 }793 794 770 // public methods only for internal purposes 795 771 ///////////////////////////////////////////////////////////////////////////// 796 797 /**798 * Initializes the guest object.799 */800 HRESULT Guest::init(Console *aParent)801 {802 LogFlowThisFunc(("aParent=%p\n", aParent));803 804 ComAssertRet(aParent, E_INVALIDARG);805 806 /* Enclose the state transition NotReady->InInit->Ready */807 AutoInitSpan autoInitSpan(this);808 AssertReturn(autoInitSpan.isOk(), E_FAIL);809 810 unconst(mParent) = aParent;811 812 /* Confirm a successful initialization when it's the case */813 autoInitSpan.setSucceeded();814 815 ULONG aMemoryBalloonSize;816 HRESULT ret = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize);817 if (ret == S_OK)818 mMemoryBalloonSize = aMemoryBalloonSize;819 else820 mMemoryBalloonSize = 0; /* Default is no ballooning */821 822 BOOL fPageFusionEnabled;823 ret = mParent->machine()->COMGETTER(PageFusionEnabled)(&fPageFusionEnabled);824 if (ret == S_OK)825 mfPageFusionEnabled = fPageFusionEnabled;826 else827 mfPageFusionEnabled = false; /* Default is no page fusion*/828 829 mStatUpdateInterval = 0; /* Default is not to report guest statistics at all */830 831 /* Clear statistics. */832 for (unsigned i = 0 ; i < GUESTSTATTYPE_MAX; i++)833 mCurrentGuestStat[i] = 0;834 835 #ifdef VBOX_WITH_GUEST_CONTROL836 /* Init the context ID counter at 1000. */837 mNextContextID = 1000;838 #endif839 840 return S_OK;841 }842 843 /**844 * Uninitializes the instance and sets the ready flag to FALSE.845 * Called either from FinalRelease() or by the parent when it gets destroyed.846 */847 void Guest::uninit()848 {849 LogFlowThisFunc(("\n"));850 851 #ifdef VBOX_WITH_GUEST_CONTROL852 /* Scope write lock as much as possible. */853 {854 /*855 * Cleanup must be done *before* AutoUninitSpan to cancel all856 * all outstanding waits in API functions (which hold AutoCaller857 * ref counts).858 */859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);860 861 /* Clean up callback data. */862 CallbackMapIter it;863 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)864 destroyCtrlCallbackContext(it);865 866 /* Clear process map. */867 mGuestProcessMap.clear();868 }869 #endif870 871 /* Enclose the state transition Ready->InUninit->NotReady */872 AutoUninitSpan autoUninitSpan(this);873 if (autoUninitSpan.uninitDone())874 return;875 876 unconst(mParent) = NULL;877 }878 879 // IGuest properties880 /////////////////////////////////////////////////////////////////////////////881 882 STDMETHODIMP Guest::COMGETTER(OSTypeId) (BSTR *aOSTypeId)883 {884 CheckComArgOutPointerValid(aOSTypeId);885 886 AutoCaller autoCaller(this);887 if (FAILED(autoCaller.rc())) return autoCaller.rc();888 889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);890 891 /* Redirect the call to IMachine if no additions are installed. */892 if (mData.mAdditionsVersion.isEmpty())893 return mParent->machine()->COMGETTER(OSTypeId)(aOSTypeId);894 895 mData.mOSTypeId.cloneTo(aOSTypeId);896 897 return S_OK;898 }899 900 STDMETHODIMP Guest::COMGETTER(AdditionsRunLevel) (AdditionsRunLevelType_T *aRunLevel)901 {902 AutoCaller autoCaller(this);903 if (FAILED(autoCaller.rc())) return autoCaller.rc();904 905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);906 907 *aRunLevel = mData.mAdditionsRunLevel;908 909 return S_OK;910 }911 912 STDMETHODIMP Guest::COMGETTER(AdditionsVersion) (BSTR *aAdditionsVersion)913 {914 CheckComArgOutPointerValid(aAdditionsVersion);915 916 AutoCaller autoCaller(this);917 if (FAILED(autoCaller.rc())) return autoCaller.rc();918 919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);920 921 HRESULT hr = S_OK;922 if ( mData.mAdditionsVersion.isEmpty()923 /* Only try alternative way if GA are active! */924 && mData.mAdditionsRunLevel > AdditionsRunLevelType_None)925 {926 /*927 * If we got back an empty string from GetAdditionsVersion() we either928 * really don't have the Guest Additions version yet or the guest is running929 * older Guest Additions (< 3.2.0) which don't provide VMMDevReq_ReportGuestInfo2,930 * so get the version + revision from the (hopefully) provided guest properties931 * instead.932 */933 Bstr addVersion;934 LONG64 u64Timestamp;935 Bstr flags;936 hr = mParent->machine()->GetGuestProperty(Bstr("/VirtualBox/GuestAdd/Version").raw(),937 addVersion.asOutParam(), &u64Timestamp, flags.asOutParam());938 if (hr == S_OK)939 {940 Bstr addRevision;941 hr = mParent->machine()->GetGuestProperty(Bstr("/VirtualBox/GuestAdd/Revision").raw(),942 addRevision.asOutParam(), &u64Timestamp, flags.asOutParam());943 if ( hr == S_OK944 && !addVersion.isEmpty()945 && !addRevision.isEmpty())946 {947 /* Some Guest Additions versions had interchanged version + revision values,948 * so check if the version value at least has a dot to identify it and change949 * both values to reflect the right content. */950 if (!Utf8Str(addVersion).contains("."))951 {952 Bstr addTemp = addVersion;953 addVersion = addRevision;954 addRevision = addTemp;955 }956 957 Bstr additionsVersion = BstrFmt("%ls r%ls",958 addVersion.raw(), addRevision.raw());959 additionsVersion.cloneTo(aAdditionsVersion);960 }961 /** @todo r=bird: else: Should not return failure! */962 }963 else964 {965 /* If getting the version + revision above fails or they simply aren't there966 * because of *really* old Guest Additions we only can report the interface967 * version to at least have something. */968 mData.mInterfaceVersion.cloneTo(aAdditionsVersion);969 /** @todo r=bird: hr is still indicating failure! */970 }971 }972 else973 mData.mAdditionsVersion.cloneTo(aAdditionsVersion);974 975 return hr;976 }977 978 STDMETHODIMP Guest::COMGETTER(SupportsSeamless) (BOOL *aSupportsSeamless)979 {980 CheckComArgOutPointerValid(aSupportsSeamless);981 982 AutoCaller autoCaller(this);983 if (FAILED(autoCaller.rc())) return autoCaller.rc();984 985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);986 987 *aSupportsSeamless = mData.mSupportsSeamless;988 989 return S_OK;990 }991 992 STDMETHODIMP Guest::COMGETTER(SupportsGraphics) (BOOL *aSupportsGraphics)993 {994 CheckComArgOutPointerValid(aSupportsGraphics);995 996 AutoCaller autoCaller(this);997 if (FAILED(autoCaller.rc())) return autoCaller.rc();998 999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1000 1001 *aSupportsGraphics = mData.mSupportsGraphics;1002 1003 return S_OK;1004 }1005 1006 BOOL Guest::isPageFusionEnabled()1007 {1008 AutoCaller autoCaller(this);1009 if (FAILED(autoCaller.rc())) return false;1010 1011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1012 1013 return mfPageFusionEnabled;1014 }1015 1016 STDMETHODIMP Guest::COMGETTER(MemoryBalloonSize) (ULONG *aMemoryBalloonSize)1017 {1018 CheckComArgOutPointerValid(aMemoryBalloonSize);1019 1020 AutoCaller autoCaller(this);1021 if (FAILED(autoCaller.rc())) return autoCaller.rc();1022 1023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1024 1025 *aMemoryBalloonSize = mMemoryBalloonSize;1026 1027 return S_OK;1028 }1029 1030 STDMETHODIMP Guest::COMSETTER(MemoryBalloonSize) (ULONG aMemoryBalloonSize)1031 {1032 AutoCaller autoCaller(this);1033 if (FAILED(autoCaller.rc())) return autoCaller.rc();1034 1035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);1036 1037 /* We must be 100% sure that IMachine::COMSETTER(MemoryBalloonSize)1038 * does not call us back in any way! */1039 HRESULT ret = mParent->machine()->COMSETTER(MemoryBalloonSize)(aMemoryBalloonSize);1040 if (ret == S_OK)1041 {1042 mMemoryBalloonSize = aMemoryBalloonSize;1043 /* forward the information to the VMM device */1044 VMMDev *pVMMDev = mParent->getVMMDev();1045 /* MUST release all locks before calling VMM device as its critsect1046 * has higher lock order than anything in Main. */1047 alock.release();1048 if (pVMMDev)1049 {1050 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();1051 if (pVMMDevPort)1052 pVMMDevPort->pfnSetMemoryBalloon(pVMMDevPort, aMemoryBalloonSize);1053 }1054 }1055 1056 return ret;1057 }1058 1059 STDMETHODIMP Guest::COMGETTER(StatisticsUpdateInterval)(ULONG *aUpdateInterval)1060 {1061 CheckComArgOutPointerValid(aUpdateInterval);1062 1063 AutoCaller autoCaller(this);1064 if (FAILED(autoCaller.rc())) return autoCaller.rc();1065 1066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1067 1068 *aUpdateInterval = mStatUpdateInterval;1069 return S_OK;1070 }1071 1072 STDMETHODIMP Guest::COMSETTER(StatisticsUpdateInterval)(ULONG aUpdateInterval)1073 {1074 AutoCaller autoCaller(this);1075 if (FAILED(autoCaller.rc())) return autoCaller.rc();1076 1077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);1078 1079 mStatUpdateInterval = aUpdateInterval;1080 /* forward the information to the VMM device */1081 VMMDev *pVMMDev = mParent->getVMMDev();1082 /* MUST release all locks before calling VMM device as its critsect1083 * has higher lock order than anything in Main. */1084 alock.release();1085 if (pVMMDev)1086 {1087 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();1088 if (pVMMDevPort)1089 pVMMDevPort->pfnSetStatisticsInterval(pVMMDevPort, aUpdateInterval);1090 }1091 1092 return S_OK;1093 }1094 1095 STDMETHODIMP Guest::InternalGetStatistics(ULONG *aCpuUser, ULONG *aCpuKernel, ULONG *aCpuIdle,1096 ULONG *aMemTotal, ULONG *aMemFree, ULONG *aMemBalloon, ULONG *aMemShared,1097 ULONG *aMemCache, ULONG *aPageTotal,1098 ULONG *aMemAllocTotal, ULONG *aMemFreeTotal, ULONG *aMemBalloonTotal, ULONG *aMemSharedTotal)1099 {1100 CheckComArgOutPointerValid(aCpuUser);1101 CheckComArgOutPointerValid(aCpuKernel);1102 CheckComArgOutPointerValid(aCpuIdle);1103 CheckComArgOutPointerValid(aMemTotal);1104 CheckComArgOutPointerValid(aMemFree);1105 CheckComArgOutPointerValid(aMemBalloon);1106 CheckComArgOutPointerValid(aMemShared);1107 CheckComArgOutPointerValid(aMemCache);1108 CheckComArgOutPointerValid(aPageTotal);1109 CheckComArgOutPointerValid(aMemAllocTotal);1110 CheckComArgOutPointerValid(aMemFreeTotal);1111 CheckComArgOutPointerValid(aMemBalloonTotal);1112 CheckComArgOutPointerValid(aMemSharedTotal);1113 1114 AutoCaller autoCaller(this);1115 if (FAILED(autoCaller.rc())) return autoCaller.rc();1116 1117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1118 1119 *aCpuUser = mCurrentGuestStat[GUESTSTATTYPE_CPUUSER];1120 *aCpuKernel = mCurrentGuestStat[GUESTSTATTYPE_CPUKERNEL];1121 *aCpuIdle = mCurrentGuestStat[GUESTSTATTYPE_CPUIDLE];1122 *aMemTotal = mCurrentGuestStat[GUESTSTATTYPE_MEMTOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */1123 *aMemFree = mCurrentGuestStat[GUESTSTATTYPE_MEMFREE] * (_4K/_1K); /* page (4K) -> 1KB units */1124 *aMemBalloon = mCurrentGuestStat[GUESTSTATTYPE_MEMBALLOON] * (_4K/_1K); /* page (4K) -> 1KB units */1125 *aMemCache = mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K); /* page (4K) -> 1KB units */1126 *aPageTotal = mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */1127 1128 /* MUST release all locks before calling any PGM statistics queries,1129 * as they are executed by EMT and that might deadlock us by VMM device1130 * activity which waits for the Guest object lock. */1131 alock.release();1132 Console::SafeVMPtr pVM (mParent);1133 if (pVM.isOk())1134 {1135 uint64_t uFreeTotal, uAllocTotal, uBalloonedTotal, uSharedTotal;1136 *aMemFreeTotal = 0;1137 int rc = PGMR3QueryVMMMemoryStats(pVM.raw(), &uAllocTotal, &uFreeTotal, &uBalloonedTotal, &uSharedTotal);1138 AssertRC(rc);1139 if (rc == VINF_SUCCESS)1140 {1141 *aMemAllocTotal = (ULONG)(uAllocTotal / _1K); /* bytes -> KB */1142 *aMemFreeTotal = (ULONG)(uFreeTotal / _1K);1143 *aMemBalloonTotal = (ULONG)(uBalloonedTotal / _1K);1144 *aMemSharedTotal = (ULONG)(uSharedTotal / _1K);1145 }1146 1147 /* Query the missing per-VM memory statistics. */1148 *aMemShared = 0;1149 uint64_t uTotalMem, uPrivateMem, uSharedMem, uZeroMem;1150 rc = PGMR3QueryMemoryStats(pVM.raw(), &uTotalMem, &uPrivateMem, &uSharedMem, &uZeroMem);1151 if (rc == VINF_SUCCESS)1152 {1153 *aMemShared = (ULONG)(uSharedMem / _1K);1154 }1155 }1156 else1157 {1158 *aMemFreeTotal = 0;1159 *aMemShared = 0;1160 }1161 1162 return S_OK;1163 }1164 1165 HRESULT Guest::setStatistic(ULONG aCpuId, GUESTSTATTYPE enmType, ULONG aVal)1166 {1167 AutoCaller autoCaller(this);1168 if (FAILED(autoCaller.rc())) return autoCaller.rc();1169 1170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);1171 1172 if (enmType >= GUESTSTATTYPE_MAX)1173 return E_INVALIDARG;1174 1175 mCurrentGuestStat[enmType] = aVal;1176 return S_OK;1177 }1178 1179 STDMETHODIMP Guest::GetAdditionsStatus(AdditionsRunLevelType_T aLevel, BOOL *aActive)1180 {1181 AutoCaller autoCaller(this);1182 if (FAILED(autoCaller.rc())) return autoCaller.rc();1183 1184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1185 1186 HRESULT rc = S_OK;1187 switch (aLevel)1188 {1189 case AdditionsRunLevelType_System:1190 *aActive = (mData.mAdditionsRunLevel > AdditionsRunLevelType_None);1191 break;1192 1193 case AdditionsRunLevelType_Userland:1194 *aActive = (mData.mAdditionsRunLevel >= AdditionsRunLevelType_Userland);1195 break;1196 1197 case AdditionsRunLevelType_Desktop:1198 *aActive = (mData.mAdditionsRunLevel >= AdditionsRunLevelType_Desktop);1199 break;1200 1201 default:1202 rc = setError(VBOX_E_NOT_SUPPORTED,1203 tr("Invalid status level defined: %u"), aLevel);1204 break;1205 }1206 1207 return rc;1208 }1209 1210 STDMETHODIMP Guest::SetCredentials(IN_BSTR aUserName, IN_BSTR aPassword,1211 IN_BSTR aDomain, BOOL aAllowInteractiveLogon)1212 {1213 AutoCaller autoCaller(this);1214 if (FAILED(autoCaller.rc())) return autoCaller.rc();1215 1216 /* forward the information to the VMM device */1217 VMMDev *pVMMDev = mParent->getVMMDev();1218 if (pVMMDev)1219 {1220 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();1221 if (pVMMDevPort)1222 {1223 uint32_t u32Flags = VMMDEV_SETCREDENTIALS_GUESTLOGON;1224 if (!aAllowInteractiveLogon)1225 u32Flags = VMMDEV_SETCREDENTIALS_NOLOCALLOGON;1226 1227 pVMMDevPort->pfnSetCredentials(pVMMDevPort,1228 Utf8Str(aUserName).c_str(),1229 Utf8Str(aPassword).c_str(),1230 Utf8Str(aDomain).c_str(),1231 u32Flags);1232 return S_OK;1233 }1234 }1235 1236 return setError(VBOX_E_VM_ERROR,1237 tr("VMM device is not available (is the VM running?)"));1238 }1239 772 1240 773 #ifdef VBOX_WITH_GUEST_CONTROL … … 2906 2439 } 2907 2440 2908 // public methods only for internal purposes2909 /////////////////////////////////////////////////////////////////////////////2910 2911 /**2912 * Sets the general Guest Additions information like2913 * API (interface) version and OS type. Gets called by2914 * vmmdevUpdateGuestInfo.2915 *2916 * @param aInterfaceVersion2917 * @param aOsType2918 */2919 void Guest::setAdditionsInfo(Bstr aInterfaceVersion, VBOXOSTYPE aOsType)2920 {2921 AutoCaller autoCaller(this);2922 AssertComRCReturnVoid(autoCaller.rc());2923 2924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);2925 2926 /*2927 * Note: The Guest Additions API (interface) version is deprecated2928 * and will not be used anymore! We might need it to at least report2929 * something as version number if *really* ancient Guest Additions are2930 * installed (without the guest version + revision properties having set).2931 */2932 mData.mInterfaceVersion = aInterfaceVersion;2933 2934 /*2935 * Older Additions rely on the Additions API version whether they2936 * are assumed to be active or not. Since newer Additions do report2937 * the Additions version *before* calling this function (by calling2938 * VMMDevReportGuestInfo2, VMMDevReportGuestStatus, VMMDevReportGuestInfo,2939 * in that order) we can tell apart old and new Additions here. Old2940 * Additions never would set VMMDevReportGuestInfo2 (which set mData.mAdditionsVersion)2941 * so they just rely on the aInterfaceVersion string (which gets set by2942 * VMMDevReportGuestInfo).2943 *2944 * So only mark the Additions as being active (run level = system) when we2945 * don't have the Additions version set.2946 */2947 if (mData.mAdditionsVersion.isEmpty())2948 {2949 if (aInterfaceVersion.isEmpty())2950 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;2951 else2952 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;2953 }2954 2955 /*2956 * Older Additions didn't have this finer grained capability bit,2957 * so enable it by default. Newer Additions will not enable this here2958 * and use the setSupportedFeatures function instead.2959 */2960 mData.mSupportsGraphics = mData.mAdditionsRunLevel > AdditionsRunLevelType_None;2961 2962 /*2963 * Note! There is a race going on between setting mAdditionsRunLevel and2964 * mSupportsGraphics here and disabling/enabling it later according to2965 * its real status when using new(er) Guest Additions.2966 */2967 mData.mOSTypeId = Global::OSTypeId (aOsType);2968 }2969 2970 /**2971 * Sets the Guest Additions version information details.2972 * Gets called by vmmdevUpdateGuestInfo2.2973 *2974 * @param aAdditionsVersion2975 * @param aVersionName2976 */2977 void Guest::setAdditionsInfo2(Bstr aAdditionsVersion, Bstr aVersionName, Bstr aRevision)2978 {2979 AutoCaller autoCaller(this);2980 AssertComRCReturnVoid(autoCaller.rc());2981 2982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);2983 2984 if (!aVersionName.isEmpty())2985 /*2986 * aVersionName could be "x.y.z_BETA1_FOOBAR", so append revision manually to2987 * become "x.y.z_BETA1_FOOBAR r12345".2988 */2989 mData.mAdditionsVersion = BstrFmt("%ls r%ls", aVersionName.raw(), aRevision.raw());2990 else /* aAdditionsVersion is in x.y.zr12345 format. */2991 mData.mAdditionsVersion = aAdditionsVersion;2992 }2993 2994 /**2995 * Sets the status of a certain Guest Additions facility.2996 * Gets called by vmmdevUpdateGuestStatus.2997 *2998 * @param Facility2999 * @param Status3000 * @param ulFlags3001 */3002 void Guest::setAdditionsStatus(VBoxGuestStatusFacility Facility, VBoxGuestStatusCurrent Status, ULONG ulFlags)3003 {3004 AutoCaller autoCaller(this);3005 AssertComRCReturnVoid(autoCaller.rc());3006 3007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);3008 3009 uint32_t uCurFacility = Facility + (Status == VBoxGuestStatusCurrent_Active ? 0 : -1);3010 3011 /* First check for disabled status. */3012 if ( Facility < VBoxGuestStatusFacility_VBoxGuestDriver3013 || ( Facility == VBoxGuestStatusFacility_All3014 && ( Status == VBoxGuestStatusCurrent_Inactive3015 || Status == VBoxGuestStatusCurrent_Disabled3016 )3017 )3018 )3019 {3020 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;3021 }3022 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxTray)3023 {3024 mData.mAdditionsRunLevel = AdditionsRunLevelType_Desktop;3025 }3026 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxService)3027 {3028 mData.mAdditionsRunLevel = AdditionsRunLevelType_Userland;3029 }3030 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxGuestDriver)3031 {3032 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;3033 }3034 else /* Should never happen! */3035 AssertMsgFailed(("Invalid facility status/run level detected! uCurFacility=%ld\n", uCurFacility));3036 }3037 3038 /**3039 * Sets the supported features (and whether they are active or not).3040 *3041 * @param fCaps Guest capability bit mask (VMMDEV_GUEST_SUPPORTS_XXX).3042 * @param fActive No idea what this is supposed to be, it's always 0 and3043 * not references by this method.3044 */3045 void Guest::setSupportedFeatures(uint32_t fCaps, uint32_t fActive)3046 {3047 AutoCaller autoCaller(this);3048 AssertComRCReturnVoid(autoCaller.rc());3049 3050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);3051 3052 mData.mSupportsSeamless = (fCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS);3053 /** @todo Add VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING */3054 mData.mSupportsGraphics = (fCaps & VMMDEV_GUEST_SUPPORTS_GRAPHICS);3055 }3056 /* vi: set tabstop=4 shiftwidth=4 expandtab: */ -
trunk/src/VBox/Main/GuestImpl.cpp
r35129 r35170 1 1 /* $Id$ */ 2 3 2 /** @file 4 * 5 * VirtualBox COM class implementation 3 * VirtualBox COM class implementation: Guest 6 4 */ 7 5 … … 34 32 #endif 35 33 #include <iprt/cpp/utils.h> 36 #include <iprt/file.h>37 #include <iprt/getopt.h>38 #include <iprt/isofs.h>39 #include <iprt/list.h>40 #include <iprt/path.h>41 34 #include <VBox/pgm.h> 42 43 #include <memory>44 35 45 36 // defines … … 50 41 51 42 DEFINE_EMPTY_CTOR_DTOR (Guest) 52 53 struct Guest::TaskGuest54 {55 enum TaskType56 {57 /** Copies a file to the guest. */58 CopyFile = 50,59 60 /** Update Guest Additions by directly copying the required installer61 * off the .ISO file, transfer it to the guest and execute the installer62 * with system privileges. */63 UpdateGuestAdditions = 10064 };65 66 TaskGuest(TaskType aTaskType, Guest *aThat, Progress *aProgress)67 : taskType(aTaskType),68 pGuest(aThat),69 progress(aProgress),70 rc(S_OK)71 {}72 ~TaskGuest() {}73 74 int startThread();75 static int taskThread(RTTHREAD aThread, void *pvUser);76 static int uploadProgress(unsigned uPercent, void *pvUser);77 78 static HRESULT setProgressErrorInfo(HRESULT hr, ComObjPtr<Progress> pProgress, const char * pszText, ...);79 static HRESULT setProgressErrorInfo(HRESULT hr, ComObjPtr<Progress> pProgress, ComObjPtr<Guest> pGuest);80 81 TaskType taskType;82 Guest *pGuest;83 ComObjPtr<Progress> progress;84 HRESULT rc;85 86 /* Task data. */87 Utf8Str strSource;88 Utf8Str strDest;89 Utf8Str strUserName;90 Utf8Str strPassword;91 ULONG uFlags;92 };93 94 int Guest::TaskGuest::startThread()95 {96 int vrc = RTThreadCreate(NULL, Guest::TaskGuest::taskThread, this,97 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,98 "Guest::Task");99 100 if (RT_FAILURE(vrc))101 return Guest::setErrorStatic(E_FAIL, Utf8StrFmt("Could not create taskThreadGuest (%Rrc)\n", vrc));102 103 return vrc;104 }105 106 /* static */107 DECLCALLBACK(int) Guest::TaskGuest::taskThread(RTTHREAD /* aThread */, void *pvUser)108 {109 std::auto_ptr<TaskGuest> task(static_cast<TaskGuest*>(pvUser));110 AssertReturn(task.get(), VERR_GENERAL_FAILURE);111 112 Guest *pGuest = task->pGuest;113 114 LogFlowFuncEnter();115 LogFlowFunc(("Guest %p\n", pGuest));116 117 HRESULT rc = S_OK;118 119 switch (task->taskType)120 {121 #ifdef VBOX_WITH_GUEST_CONTROL122 case TaskGuest::CopyFile:123 {124 rc = pGuest->taskCopyFile(task.get());125 break;126 }127 case TaskGuest::UpdateGuestAdditions:128 {129 rc = pGuest->taskUpdateGuestAdditions(task.get());130 break;131 }132 #endif133 default:134 AssertMsgFailed(("Invalid task type %u specified!\n", task->taskType));135 break;136 }137 138 LogFlowFunc(("rc=%Rhrc\n", rc));139 LogFlowFuncLeave();140 141 return VINF_SUCCESS;142 }143 144 /* static */145 int Guest::TaskGuest::uploadProgress(unsigned uPercent, void *pvUser)146 {147 Guest::TaskGuest *pTask = *(Guest::TaskGuest**)pvUser;148 149 if (pTask &&150 !pTask->progress.isNull())151 {152 BOOL fCanceled;153 pTask->progress->COMGETTER(Canceled)(&fCanceled);154 if (fCanceled)155 return -1;156 pTask->progress->SetCurrentOperationProgress(uPercent);157 }158 return VINF_SUCCESS;159 }160 161 /* static */162 HRESULT Guest::TaskGuest::setProgressErrorInfo(HRESULT hr, ComObjPtr<Progress> pProgress, const char *pszText, ...)163 {164 BOOL fCanceled;165 BOOL fCompleted;166 if ( SUCCEEDED(pProgress->COMGETTER(Canceled(&fCanceled)))167 && !fCanceled168 && SUCCEEDED(pProgress->COMGETTER(Completed(&fCompleted)))169 && !fCompleted)170 {171 va_list va;172 va_start(va, pszText);173 HRESULT hr2 = pProgress->notifyCompleteV(hr,174 COM_IIDOF(IGuest),175 Guest::getStaticComponentName(),176 pszText,177 va);178 va_end(va);179 if (hr2 == S_OK) /* If unable to retrieve error, return input error. */180 hr2 = hr;181 return hr2;182 }183 return S_OK;184 }185 186 /* static */187 HRESULT Guest::TaskGuest::setProgressErrorInfo(HRESULT hr, ComObjPtr<Progress> pProgress, ComObjPtr<Guest> pGuest)188 {189 return setProgressErrorInfo(hr, pProgress,190 Utf8Str(com::ErrorInfo((IGuest*)pGuest, COM_IIDOF(IGuest)).getText()).c_str());191 }192 193 #ifdef VBOX_WITH_GUEST_CONTROL194 HRESULT Guest::taskCopyFile(TaskGuest *aTask)195 {196 LogFlowFuncEnter();197 198 AutoCaller autoCaller(this);199 if (FAILED(autoCaller.rc())) return autoCaller.rc();200 201 /*202 * Do *not* take a write lock here since we don't (and won't)203 * touch any class-specific data (of IGuest) here - only the member functions204 * which get called here can do that.205 */206 207 HRESULT rc = S_OK;208 209 try210 {211 Guest *pGuest = aTask->pGuest;212 AssertPtr(pGuest);213 214 /* Does our source file exist? */215 if (!RTFileExists(aTask->strSource.c_str()))216 {217 /* Since this task runs in another thread than the main Guest object218 * we cannot rely on notifyComplete's internal lookup - so do this ourselves. */219 rc = VBOX_E_IPRT_ERROR;220 aTask->progress->notifyComplete(rc,221 COM_IIDOF(IGuest),222 Guest::getStaticComponentName(),223 Guest::tr("Source file \"%s\" does not exist"), aTask->strSource.c_str());224 }225 else226 {227 RTFILE fileSource;228 int vrc = RTFileOpen(&fileSource, aTask->strSource.c_str(),229 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);230 if (RT_FAILURE(vrc))231 {232 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,233 Guest::tr("Could not open source file \"%s\" for reading (%Rrc)"),234 aTask->strSource.c_str(), vrc);235 }236 else237 {238 uint64_t cbSize;239 vrc = RTFileGetSize(fileSource, &cbSize);240 if (RT_FAILURE(vrc))241 {242 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,243 Guest::tr("Could not query file size of \"%s\" (%Rrc)"),244 aTask->strSource.c_str(), vrc);245 }246 else247 {248 com::SafeArray<IN_BSTR> args;249 com::SafeArray<IN_BSTR> env;250 251 /*252 * Prepare tool command line.253 */254 char szOutput[RTPATH_MAX];255 if (RTStrPrintf(szOutput, sizeof(szOutput), "--output=%s", aTask->strDest.c_str()) <= sizeof(szOutput) - 1)256 {257 /*258 * Normalize path slashes, based on the detected guest.259 */260 Utf8Str osType = mData.mOSTypeId;261 if ( osType.contains("Microsoft", Utf8Str::CaseInsensitive)262 || osType.contains("Windows", Utf8Str::CaseInsensitive))263 {264 /* We have a Windows guest. */265 RTPathChangeToDosSlashes(szOutput, true /* Force conversion. */);266 }267 else /* ... or something which isn't from Redmond ... */268 {269 RTPathChangeToUnixSlashes(szOutput, true /* Force conversion. */);270 }271 272 args.push_back(Bstr(VBOXSERVICE_TOOL_CAT).raw()); /* The actual (internal) tool to use (as argv[0]). */273 args.push_back(Bstr(szOutput).raw()); /* We want to write a file ... */274 }275 else276 {277 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,278 Guest::tr("Error preparing command line"));279 }280 281 ComPtr<IProgress> execProgress;282 ULONG uPID;283 if (SUCCEEDED(rc))284 {285 LogRel(("Copying file \"%s\" to guest \"%s\" (%u bytes) ...\n",286 aTask->strSource.c_str(), aTask->strDest.c_str(), cbSize));287 /*288 * Okay, since we gathered all stuff we need until now to start the289 * actual copying, start the guest part now.290 */291 rc = pGuest->ExecuteProcess(Bstr(VBOXSERVICE_TOOL_CAT).raw(),292 ExecuteProcessFlag_Hidden293 | ExecuteProcessFlag_WaitForProcessStartOnly,294 ComSafeArrayAsInParam(args),295 ComSafeArrayAsInParam(env),296 Bstr(aTask->strUserName).raw(),297 Bstr(aTask->strPassword).raw(),298 5 * 1000 /* Wait 5s for getting the process started. */,299 &uPID, execProgress.asOutParam());300 if (FAILED(rc))301 rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);302 }303 304 if (SUCCEEDED(rc))305 {306 BOOL fCompleted = FALSE;307 BOOL fCanceled = FALSE;308 309 size_t cbToRead = cbSize;310 size_t cbTransfered = 0;311 size_t cbRead;312 SafeArray<BYTE> aInputData(_1M);313 while ( SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted)))314 && !fCompleted)315 {316 vrc = RTFileRead(fileSource, (uint8_t*)aInputData.raw(), RT_MIN(cbToRead, _1M), &cbRead);317 /*318 * Some other error occured? There might be a chance that RTFileRead319 * could not resolve/map the native error code to an IPRT code, so just320 * print a generic error.321 */322 if (RT_FAILURE(vrc))323 {324 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,325 Guest::tr("Could not read from file \"%s\" (%Rrc)"),326 aTask->strSource.c_str(), vrc);327 break;328 }329 330 /* Resize buffer to reflect amount we just have read. */331 if (cbRead > 0)332 aInputData.resize(cbRead);333 334 ULONG uFlags = ProcessInputFlag_None;335 /* Did we reach the end of the content we want to transfer (last chunk)? */336 if ( (cbRead < _1M)337 /* ... or does the user want to cancel? */338 || ( SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))339 && fCanceled)340 )341 {342 uFlags |= ProcessInputFlag_EndOfFile;343 }344 345 /* Transfer the current chunk ... */346 ULONG uBytesWritten;347 rc = pGuest->SetProcessInput(uPID, uFlags,348 10 * 1000 /* Wait 10s for getting the input data transfered. */,349 ComSafeArrayAsInParam(aInputData), &uBytesWritten);350 if (FAILED(rc))351 {352 rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);353 break;354 }355 356 Assert(cbRead <= cbToRead);357 Assert(cbToRead >= cbRead);358 cbToRead -= cbRead;359 360 cbTransfered += uBytesWritten;361 Assert(cbTransfered <= cbSize);362 aTask->progress->SetCurrentOperationProgress(cbTransfered / (cbSize / 100));363 364 /* End of file reached? */365 if (cbToRead == 0)366 break;367 368 /* Progress canceled by Main API? */369 if ( SUCCEEDED(execProgress->COMGETTER(Canceled(&fCanceled)))370 && fCanceled)371 {372 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,373 Guest::tr("Copy operation of file \"%s\" was canceled on guest side"),374 aTask->strSource.c_str());375 break;376 }377 }378 379 if (SUCCEEDED(rc))380 aTask->progress->notifyComplete(S_OK);381 }382 }383 RTFileClose(fileSource);384 }385 }386 }387 catch (HRESULT aRC)388 {389 rc = aRC;390 }391 392 /* Clean up */393 aTask->rc = rc;394 395 LogFlowFunc(("rc=%Rhrc\n", rc));396 LogFlowFuncLeave();397 398 return VINF_SUCCESS;399 }400 401 HRESULT Guest::taskUpdateGuestAdditions(TaskGuest *aTask)402 {403 LogFlowFuncEnter();404 405 AutoCaller autoCaller(this);406 if (FAILED(autoCaller.rc())) return autoCaller.rc();407 408 /*409 * Do *not* take a write lock here since we don't (and won't)410 * touch any class-specific data (of IGuest) here - only the member functions411 * which get called here can do that.412 */413 414 HRESULT rc = S_OK;415 BOOL fCompleted;416 BOOL fCanceled;417 418 try419 {420 Guest *pGuest = aTask->pGuest;421 AssertPtr(pGuest);422 423 aTask->progress->SetCurrentOperationProgress(10);424 425 /*426 * Determine guest OS type and the required installer image.427 * At the moment only Windows guests are supported.428 */429 Utf8Str installerImage;430 Bstr osTypeId;431 if ( SUCCEEDED(pGuest->COMGETTER(OSTypeId(osTypeId.asOutParam())))432 && !osTypeId.isEmpty())433 {434 Utf8Str osTypeIdUtf8(osTypeId); /* Needed for .contains(). */435 if ( osTypeIdUtf8.contains("Microsoft", Utf8Str::CaseInsensitive)436 || osTypeIdUtf8.contains("Windows", Utf8Str::CaseInsensitive))437 {438 if (osTypeIdUtf8.contains("64", Utf8Str::CaseInsensitive))439 installerImage = "VBOXWINDOWSADDITIONS_AMD64.EXE";440 else441 installerImage = "VBOXWINDOWSADDITIONS_X86.EXE";442 /* Since the installers are located in the root directory,443 * no further path processing needs to be done (yet). */444 }445 else /* Everything else is not supported (yet). */446 throw TaskGuest::setProgressErrorInfo(VBOX_E_NOT_SUPPORTED, aTask->progress,447 Guest::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),448 osTypeIdUtf8.c_str());449 }450 else451 throw TaskGuest::setProgressErrorInfo(VBOX_E_NOT_SUPPORTED, aTask->progress,452 Guest::tr("Could not detected guest OS type/version, please update manually"));453 Assert(!installerImage.isEmpty());454 455 /*456 * Try to open the .ISO file and locate the specified installer.457 */458 RTISOFSFILE iso;459 int vrc = RTIsoFsOpen(&iso, aTask->strSource.c_str());460 if (RT_FAILURE(vrc))461 {462 rc = TaskGuest::setProgressErrorInfo(VBOX_E_FILE_ERROR, aTask->progress,463 Guest::tr("Invalid installation medium detected: \"%s\""),464 aTask->strSource.c_str());465 }466 else467 {468 uint32_t cbOffset;469 size_t cbLength;470 vrc = RTIsoFsGetFileInfo(&iso, installerImage.c_str(), &cbOffset, &cbLength);471 if ( RT_SUCCESS(vrc)472 && cbOffset473 && cbLength)474 {475 vrc = RTFileSeek(iso.file, cbOffset, RTFILE_SEEK_BEGIN, NULL);476 if (RT_FAILURE(vrc))477 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,478 Guest::tr("Could not seek to setup file on installation medium \"%s\" (%Rrc)"),479 aTask->strSource.c_str(), vrc);480 }481 else482 {483 switch (vrc)484 {485 case VERR_FILE_NOT_FOUND:486 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,487 Guest::tr("Setup file was not found on installation medium \"%s\""),488 aTask->strSource.c_str());489 break;490 491 default:492 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,493 Guest::tr("An unknown error (%Rrc) occured while retrieving information of setup file on installation medium \"%s\""),494 vrc, aTask->strSource.c_str());495 break;496 }497 }498 499 /* Specify the ouput path on the guest side. */500 Utf8Str strInstallerPath = "%TEMP%\\VBoxWindowsAdditions.exe";501 502 if (RT_SUCCESS(vrc))503 {504 /* Okay, we're ready to start our copy routine on the guest! */505 aTask->progress->SetCurrentOperationProgress(15);506 507 /* Prepare command line args. */508 com::SafeArray<IN_BSTR> args;509 com::SafeArray<IN_BSTR> env;510 511 args.push_back(Bstr(VBOXSERVICE_TOOL_CAT).raw()); /* The actual (internal) tool to use (as argv[0]). */512 args.push_back(Bstr("--output").raw()); /* We want to write a file ... */513 args.push_back(Bstr(strInstallerPath.c_str()).raw()); /* ... with this path. */514 515 if (SUCCEEDED(rc))516 {517 ComPtr<IProgress> progressCat;518 ULONG uPID;519 520 /*521 * Start built-in "vbox_cat" tool (inside VBoxService) to522 * copy over/pipe the data into a file on the guest (with523 * system rights, no username/password specified).524 */525 rc = pGuest->executeProcessInternal(Bstr(VBOXSERVICE_TOOL_CAT).raw(),526 ExecuteProcessFlag_Hidden527 | ExecuteProcessFlag_WaitForProcessStartOnly,528 ComSafeArrayAsInParam(args),529 ComSafeArrayAsInParam(env),530 Bstr("").raw() /* Username. */,531 Bstr("").raw() /* Password */,532 5 * 1000 /* Wait 5s for getting the process started. */,533 &uPID, progressCat.asOutParam(), &vrc);534 if (FAILED(rc))535 {536 /* Errors which return VBOX_E_NOT_SUPPORTED can be safely skipped by the caller537 * to silently fall back to "normal" (old) .ISO mounting. */538 539 /* Due to a very limited COM error range we use vrc for a more detailed error540 * lookup to figure out what went wrong. */541 switch (vrc)542 {543 /* Guest execution service is not (yet) ready. This basically means that either VBoxService544 * is not running (yet) or that the Guest Additions are too old (because VBoxService does not545 * support the guest execution feature in this version). */546 case VERR_NOT_FOUND:547 LogRel(("Guest Additions seem not to be installed yet\n"));548 rc = TaskGuest::setProgressErrorInfo(VBOX_E_NOT_SUPPORTED, aTask->progress,549 Guest::tr("Guest Additions seem not to be installed or are not ready to update yet"));550 break;551 552 /* Getting back a VERR_INVALID_PARAMETER indicates that the installed Guest Additions are supporting the guest553 * execution but not the built-in "vbox_cat" tool of VBoxService (< 4.0). */554 case VERR_INVALID_PARAMETER:555 LogRel(("Guest Additions are installed but don't supported automatic updating\n"));556 rc = TaskGuest::setProgressErrorInfo(VBOX_E_NOT_SUPPORTED, aTask->progress,557 Guest::tr("Installed Guest Additions do not support automatic updating"));558 break;559 560 default:561 rc = TaskGuest::setProgressErrorInfo(E_FAIL, aTask->progress,562 Guest::tr("Error copying Guest Additions setup file to guest path \"%s\" (%Rrc)"),563 strInstallerPath.c_str(), vrc);564 break;565 }566 }567 else568 {569 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", aTask->strSource.c_str()));570 LogRel(("Copying Guest Additions installer \"%s\" to \"%s\" on guest ...\n",571 installerImage.c_str(), strInstallerPath.c_str()));572 aTask->progress->SetCurrentOperationProgress(20);573 574 /* Wait for process to exit ... */575 SafeArray<BYTE> aInputData(_1M);576 while ( SUCCEEDED(progressCat->COMGETTER(Completed(&fCompleted)))577 && !fCompleted)578 {579 size_t cbRead;580 /* cbLength contains remaining bytes of our installer file581 * opened above to read. */582 size_t cbToRead = RT_MIN(cbLength, _1M);583 if (cbToRead)584 {585 vrc = RTFileRead(iso.file, (uint8_t*)aInputData.raw(), cbToRead, &cbRead);586 if ( cbRead587 && RT_SUCCESS(vrc))588 {589 /* Resize buffer to reflect amount we just have read. */590 if (cbRead > 0)591 aInputData.resize(cbRead);592 593 /* Did we reach the end of the content we want to transfer (last chunk)? */594 ULONG uFlags = ProcessInputFlag_None;595 if ( (cbRead < _1M)596 /* ... or does the user want to cancel? */597 || ( SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))598 && fCanceled)599 )600 {601 uFlags |= ProcessInputFlag_EndOfFile;602 }603 604 /* Transfer the current chunk ... */605 #ifdef DEBUG_andy606 LogRel(("Copying Guest Additions (%u bytes left) ...\n", cbLength));607 #endif608 ULONG uBytesWritten;609 rc = pGuest->SetProcessInput(uPID, uFlags,610 10 * 1000 /* Wait 10s for getting the input data transfered. */,611 ComSafeArrayAsInParam(aInputData), &uBytesWritten);612 if (FAILED(rc))613 {614 rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);615 break;616 }617 618 /* If task was canceled above also cancel the process execution. */619 if (fCanceled)620 progressCat->Cancel();621 622 #ifdef DEBUG_andy623 LogRel(("Copying Guest Additions (%u bytes written) ...\n", uBytesWritten));624 #endif625 Assert(cbLength >= uBytesWritten);626 cbLength -= uBytesWritten;627 }628 else if (RT_FAILURE(vrc))629 {630 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,631 Guest::tr("Error while reading setup file \"%s\" (To read: %u, Size: %u) from installation medium (%Rrc)"),632 installerImage.c_str(), cbToRead, cbLength, vrc);633 }634 }635 636 /* Internal progress canceled? */637 if ( SUCCEEDED(progressCat->COMGETTER(Canceled(&fCanceled)))638 && fCanceled)639 {640 aTask->progress->Cancel();641 break;642 }643 }644 }645 }646 }647 RTIsoFsClose(&iso);648 649 if ( SUCCEEDED(rc)650 && ( SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))651 && !fCanceled652 )653 )654 {655 /*656 * Installer was transferred successfully, so let's start it657 * (with system rights).658 */659 LogRel(("Preparing to execute Guest Additions update ...\n"));660 aTask->progress->SetCurrentOperationProgress(66);661 662 /* Prepare command line args for installer. */663 com::SafeArray<IN_BSTR> installerArgs;664 com::SafeArray<IN_BSTR> installerEnv;665 666 /** @todo Only Windows! */667 installerArgs.push_back(Bstr(strInstallerPath).raw()); /* The actual (internal) installer image (as argv[0]). */668 /* Note that starting at Windows Vista the lovely session 0 separation applies:669 * This means that if we run an application with the profile/security context670 * of VBoxService (system rights!) we're not able to show any UI. */671 installerArgs.push_back(Bstr("/S").raw()); /* We want to install in silent mode. */672 installerArgs.push_back(Bstr("/l").raw()); /* ... and logging enabled. */673 /* Don't quit VBoxService during upgrade because it still is used for this674 * piece of code we're in right now (that is, here!) ... */675 installerArgs.push_back(Bstr("/no_vboxservice_exit").raw());676 /* Tell the installer to report its current installation status677 * using a running VBoxTray instance via balloon messages in the678 * Windows taskbar. */679 installerArgs.push_back(Bstr("/post_installstatus").raw());680 681 /*682 * Start the just copied over installer with system rights683 * in silent mode on the guest. Don't use the hidden flag since there684 * may be pop ups the user has to process.685 */686 ComPtr<IProgress> progressInstaller;687 ULONG uPID;688 rc = pGuest->executeProcessInternal(Bstr(strInstallerPath).raw(),689 ExecuteProcessFlag_WaitForProcessStartOnly,690 ComSafeArrayAsInParam(installerArgs),691 ComSafeArrayAsInParam(installerEnv),692 Bstr("").raw() /* Username */,693 Bstr("").raw() /* Password */,694 10 * 1000 /* Wait 10s for getting the process started */,695 &uPID, progressInstaller.asOutParam(), &vrc);696 if (SUCCEEDED(rc))697 {698 LogRel(("Guest Additions update is running ...\n"));699 700 /* If the caller does not want to wait for out guest update process to end,701 * complete the progress object now so that the caller can do other work. */702 if (aTask->uFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)703 aTask->progress->notifyComplete(S_OK);704 else705 aTask->progress->SetCurrentOperationProgress(70);706 707 /* Wait until the Guest Additions installer finishes ... */708 while ( SUCCEEDED(progressInstaller->COMGETTER(Completed(&fCompleted)))709 && !fCompleted)710 {711 if ( SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))712 && fCanceled)713 {714 progressInstaller->Cancel();715 break;716 }717 /* Progress canceled by Main API? */718 if ( SUCCEEDED(progressInstaller->COMGETTER(Canceled(&fCanceled)))719 && fCanceled)720 {721 break;722 }723 RTThreadSleep(100);724 }725 726 ULONG uRetStatus, uRetExitCode, uRetFlags;727 rc = pGuest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &uRetStatus);728 if (SUCCEEDED(rc))729 {730 if (fCompleted)731 {732 if (uRetExitCode == 0)733 {734 LogRel(("Guest Additions update successful!\n"));735 if ( SUCCEEDED(aTask->progress->COMGETTER(Completed(&fCompleted)))736 && !fCompleted)737 aTask->progress->notifyComplete(S_OK);738 }739 else740 {741 LogRel(("Guest Additions update failed (Exit code=%u, Status=%u, Flags=%u)\n",742 uRetExitCode, uRetStatus, uRetFlags));743 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,744 Guest::tr("Guest Additions update failed with exit code=%u (status=%u, flags=%u)"),745 uRetExitCode, uRetStatus, uRetFlags);746 }747 }748 else if ( SUCCEEDED(progressInstaller->COMGETTER(Canceled(&fCanceled)))749 && fCanceled)750 {751 LogRel(("Guest Additions update was canceled\n"));752 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,753 Guest::tr("Guest Additions update was canceled by the guest with exit code=%u (status=%u, flags=%u)"),754 uRetExitCode, uRetStatus, uRetFlags);755 }756 else757 {758 LogRel(("Guest Additions update was canceled by the user\n"));759 }760 }761 else762 rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);763 }764 else765 rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);766 }767 }768 }769 catch (HRESULT aRC)770 {771 rc = aRC;772 }773 774 /* Clean up */775 aTask->rc = rc;776 777 LogFlowFunc(("rc=%Rhrc\n", rc));778 LogFlowFuncLeave();779 780 return VINF_SUCCESS;781 }782 #endif783 43 784 44 HRESULT Guest::FinalConstruct() … … 1238 498 } 1239 499 1240 #ifdef VBOX_WITH_GUEST_CONTROL1241 /**1242 * Appends environment variables to the environment block.1243 *1244 * Each var=value pair is separated by the null character ('\\0'). The whole1245 * block will be stored in one blob and disassembled on the guest side later to1246 * fit into the HGCM param structure.1247 *1248 * @returns VBox status code.1249 *1250 * @param pszEnvVar The environment variable=value to append to the1251 * environment block.1252 * @param ppvList This is actually a pointer to a char pointer1253 * variable which keeps track of the environment block1254 * that we're constructing.1255 * @param pcbList Pointer to the variable holding the current size of1256 * the environment block. (List is a misnomer, go1257 * ahead a be confused.)1258 * @param pcEnvVars Pointer to the variable holding count of variables1259 * stored in the environment block.1260 */1261 int Guest::prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnvVars)1262 {1263 int rc = VINF_SUCCESS;1264 uint32_t cchEnv = strlen(pszEnv); Assert(cchEnv >= 2);1265 if (*ppvList)1266 {1267 uint32_t cbNewLen = *pcbList + cchEnv + 1; /* Include zero termination. */1268 char *pvTmp = (char *)RTMemRealloc(*ppvList, cbNewLen);1269 if (pvTmp == NULL)1270 rc = VERR_NO_MEMORY;1271 else1272 {1273 memcpy(pvTmp + *pcbList, pszEnv, cchEnv);1274 pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */1275 *ppvList = (void **)pvTmp;1276 }1277 }1278 else1279 {1280 char *pszTmp;1281 if (RTStrAPrintf(&pszTmp, "%s", pszEnv) >= 0)1282 {1283 *ppvList = (void **)pszTmp;1284 /* Reset counters. */1285 *pcEnvVars = 0;1286 *pcbList = 0;1287 }1288 }1289 if (RT_SUCCESS(rc))1290 {1291 *pcbList += cchEnv + 1; /* Include zero termination. */1292 *pcEnvVars += 1; /* Increase env variable count. */1293 }1294 return rc;1295 }1296 1297 /**1298 * Static callback function for receiving updates on guest control commands1299 * from the guest. Acts as a dispatcher for the actual class instance.1300 *1301 * @returns VBox status code.1302 *1303 * @todo1304 *1305 */1306 DECLCALLBACK(int) Guest::doGuestCtrlNotification(void *pvExtension,1307 uint32_t u32Function,1308 void *pvParms,1309 uint32_t cbParms)1310 {1311 using namespace guestControl;1312 1313 /*1314 * No locking, as this is purely a notification which does not make any1315 * changes to the object state.1316 */1317 #ifdef DEBUG_andy1318 LogFlowFunc(("pvExtension = %p, u32Function = %d, pvParms = %p, cbParms = %d\n",1319 pvExtension, u32Function, pvParms, cbParms));1320 #endif1321 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest *>(pvExtension);1322 1323 int rc = VINF_SUCCESS;1324 switch (u32Function)1325 {1326 case GUEST_DISCONNECTED:1327 {1328 //LogFlowFunc(("GUEST_DISCONNECTED\n"));1329 1330 PCALLBACKDATACLIENTDISCONNECTED pCBData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvParms);1331 AssertPtr(pCBData);1332 AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbParms, VERR_INVALID_PARAMETER);1333 AssertReturn(CALLBACKDATAMAGICCLIENTDISCONNECTED == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);1334 1335 rc = pGuest->notifyCtrlClientDisconnected(u32Function, pCBData);1336 break;1337 }1338 1339 case GUEST_EXEC_SEND_STATUS:1340 {1341 //LogFlowFunc(("GUEST_EXEC_SEND_STATUS\n"));1342 1343 PCALLBACKDATAEXECSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvParms);1344 AssertPtr(pCBData);1345 AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbParms, VERR_INVALID_PARAMETER);1346 AssertReturn(CALLBACKDATAMAGICEXECSTATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);1347 1348 rc = pGuest->notifyCtrlExecStatus(u32Function, pCBData);1349 break;1350 }1351 1352 case GUEST_EXEC_SEND_OUTPUT:1353 {1354 //LogFlowFunc(("GUEST_EXEC_SEND_OUTPUT\n"));1355 1356 PCALLBACKDATAEXECOUT pCBData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvParms);1357 AssertPtr(pCBData);1358 AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbParms, VERR_INVALID_PARAMETER);1359 AssertReturn(CALLBACKDATAMAGICEXECOUT == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);1360 1361 rc = pGuest->notifyCtrlExecOut(u32Function, pCBData);1362 break;1363 }1364 1365 case GUEST_EXEC_SEND_INPUT_STATUS:1366 {1367 //LogFlowFunc(("GUEST_EXEC_SEND_INPUT_STATUS\n"));1368 1369 PCALLBACKDATAEXECINSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECINSTATUS>(pvParms);1370 AssertPtr(pCBData);1371 AssertReturn(sizeof(CALLBACKDATAEXECINSTATUS) == cbParms, VERR_INVALID_PARAMETER);1372 AssertReturn(CALLBACKDATAMAGICEXECINSTATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);1373 1374 rc = pGuest->notifyCtrlExecInStatus(u32Function, pCBData);1375 break;1376 }1377 1378 default:1379 AssertMsgFailed(("Unknown guest control notification received, u32Function=%u\n", u32Function));1380 rc = VERR_INVALID_PARAMETER;1381 break;1382 }1383 return rc;1384 }1385 1386 /* Function for handling the execution start/termination notification. */1387 int Guest::notifyCtrlExecStatus(uint32_t u32Function,1388 PCALLBACKDATAEXECSTATUS pData)1389 {1390 int vrc = VINF_SUCCESS;1391 1392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);1393 1394 AssertPtr(pData);1395 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);1396 1397 /* Callback can be called several times. */1398 if (it != mCallbackMap.end())1399 {1400 PCALLBACKDATAEXECSTATUS pCBData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;1401 AssertPtr(pCBData);1402 1403 pCBData->u32PID = pData->u32PID;1404 pCBData->u32Status = pData->u32Status;1405 pCBData->u32Flags = pData->u32Flags;1406 /** @todo Copy void* buffer contents! */1407 1408 Utf8Str errMsg;1409 1410 /* Was progress canceled before? */1411 BOOL fCanceled;1412 ComAssert(!it->second.pProgress.isNull());1413 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled))1414 && !fCanceled)1415 {1416 /* Do progress handling. */1417 HRESULT hr;1418 switch (pData->u32Status)1419 {1420 case PROC_STS_STARTED:1421 LogRel(("Guest process (PID %u) started\n", pCBData->u32PID)); /** @todo Add process name */1422 hr = it->second.pProgress->SetNextOperation(Bstr(tr("Waiting for process to exit ...")).raw(), 1 /* Weight */);1423 AssertComRC(hr);1424 break;1425 1426 case PROC_STS_TEN: /* Terminated normally. */1427 LogRel(("Guest process (PID %u) exited normally\n", pCBData->u32PID)); /** @todo Add process name */1428 if (!it->second.pProgress->getCompleted())1429 {1430 hr = it->second.pProgress->notifyComplete(S_OK);1431 AssertComRC(hr);1432 1433 LogFlowFunc(("Process (CID=%u, status=%u) terminated successfully\n",1434 pData->hdr.u32ContextID, pData->u32Status));1435 }1436 break;1437 1438 case PROC_STS_TEA: /* Terminated abnormally. */1439 LogRel(("Guest process (PID %u) terminated abnormally with exit code = %u\n",1440 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */1441 errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),1442 pCBData->u32Flags);1443 break;1444 1445 case PROC_STS_TES: /* Terminated through signal. */1446 LogRel(("Guest process (PID %u) terminated through signal with exit code = %u\n",1447 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */1448 errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),1449 pCBData->u32Flags);1450 break;1451 1452 case PROC_STS_TOK:1453 LogRel(("Guest process (PID %u) timed out and was killed\n", pCBData->u32PID)); /** @todo Add process name */1454 errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));1455 break;1456 1457 case PROC_STS_TOA:1458 LogRel(("Guest process (PID %u) timed out and could not be killed\n", pCBData->u32PID)); /** @todo Add process name */1459 errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));1460 break;1461 1462 case PROC_STS_DWN:1463 LogRel(("Guest process (PID %u) killed because system is shutting down\n", pCBData->u32PID)); /** @todo Add process name */1464 /*1465 * If u32Flags has ExecuteProcessFlag_IgnoreOrphanedProcesses set, we don't report an error to1466 * our progress object. This is helpful for waiters which rely on the success of our progress object1467 * even if the executed process was killed because the system/VBoxService is shutting down.1468 *1469 * In this case u32Flags contains the actual execution flags reached in via Guest::ExecuteProcess().1470 */1471 if (pData->u32Flags & ExecuteProcessFlag_IgnoreOrphanedProcesses)1472 {1473 if (!it->second.pProgress->getCompleted())1474 {1475 hr = it->second.pProgress->notifyComplete(S_OK);1476 AssertComRC(hr);1477 }1478 }1479 else1480 {1481 errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down"));1482 }1483 break;1484 1485 case PROC_STS_ERROR:1486 LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",1487 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */1488 errMsg = Utf8StrFmt(Guest::tr("Process execution failed with rc=%Rrc"), pCBData->u32Flags);1489 break;1490 1491 default:1492 vrc = VERR_INVALID_PARAMETER;1493 break;1494 }1495 1496 /* Handle process map. */1497 /** @todo What happens on/deal with PID reuse? */1498 /** @todo How to deal with multiple updates at once? */1499 if (pCBData->u32PID > 0)1500 {1501 GuestProcessMapIter it_proc = getProcessByPID(pCBData->u32PID);1502 if (it_proc == mGuestProcessMap.end())1503 {1504 /* Not found, add to map. */1505 GuestProcess newProcess;1506 newProcess.mStatus = pCBData->u32Status;1507 newProcess.mExitCode = pCBData->u32Flags; /* Contains exit code. */1508 newProcess.mFlags = 0;1509 1510 mGuestProcessMap[pCBData->u32PID] = newProcess;1511 }1512 else /* Update map. */1513 {1514 it_proc->second.mStatus = pCBData->u32Status;1515 it_proc->second.mExitCode = pCBData->u32Flags; /* Contains exit code. */1516 it_proc->second.mFlags = 0;1517 }1518 }1519 }1520 else1521 errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));1522 1523 if (!it->second.pProgress->getCompleted())1524 {1525 if ( errMsg.length()1526 || fCanceled) /* If canceled we have to report E_FAIL! */1527 {1528 /* Destroy all callbacks which are still waiting on something1529 * which is related to the current PID. */1530 CallbackMapIter it2;1531 for (it2 = mCallbackMap.begin(); it2 != mCallbackMap.end(); it2++)1532 {1533 switch (it2->second.mType)1534 {1535 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:1536 break;1537 1538 /* When waiting for process output while the process is destroyed,1539 * make sure we also destroy the actual waiting operation (internal progress object)1540 * in order to not block the caller. */1541 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:1542 {1543 PCALLBACKDATAEXECOUT pItData = (CALLBACKDATAEXECOUT*)it2->second.pvData;1544 AssertPtr(pItData);1545 if (pItData->u32PID == pCBData->u32PID)1546 destroyCtrlCallbackContext(it2);1547 break;1548 }1549 1550 default:1551 AssertMsgFailed(("Unknown callback type %d\n", it2->second.mType));1552 break;1553 }1554 }1555 1556 HRESULT hr2 = it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,1557 COM_IIDOF(IGuest),1558 Guest::getStaticComponentName(),1559 "%s", errMsg.c_str());1560 AssertComRC(hr2);1561 LogFlowFunc(("Process (CID=%u, status=%u) reported error: %s\n",1562 pData->hdr.u32ContextID, pData->u32Status, errMsg.c_str()));1563 }1564 }1565 }1566 else1567 LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));1568 LogFlowFunc(("Returned with rc=%Rrc\n", vrc));1569 return vrc;1570 }1571 1572 /* Function for handling the execution output notification. */1573 int Guest::notifyCtrlExecOut(uint32_t u32Function,1574 PCALLBACKDATAEXECOUT pData)1575 {1576 int rc = VINF_SUCCESS;1577 1578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);1579 1580 AssertPtr(pData);1581 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);1582 if (it != mCallbackMap.end())1583 {1584 PCALLBACKDATAEXECOUT pCBData = (PCALLBACKDATAEXECOUT)it->second.pvData;1585 AssertPtr(pCBData);1586 1587 pCBData->u32PID = pData->u32PID;1588 pCBData->u32HandleId = pData->u32HandleId;1589 pCBData->u32Flags = pData->u32Flags;1590 1591 /* Make sure we really got something! */1592 if ( pData->cbData1593 && pData->pvData)1594 {1595 /* Allocate data buffer and copy it */1596 pCBData->pvData = RTMemAlloc(pData->cbData);1597 pCBData->cbData = pData->cbData;1598 1599 AssertReturn(pCBData->pvData, VERR_NO_MEMORY);1600 memcpy(pCBData->pvData, pData->pvData, pData->cbData);1601 }1602 else1603 {1604 pCBData->pvData = NULL;1605 pCBData->cbData = 0;1606 }1607 1608 /* Was progress canceled before? */1609 BOOL fCanceled;1610 ComAssert(!it->second.pProgress.isNull());1611 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && fCanceled)1612 {1613 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,1614 COM_IIDOF(IGuest),1615 Guest::getStaticComponentName(),1616 Guest::tr("The output operation was canceled"));1617 }1618 else1619 {1620 BOOL fCompleted;1621 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))1622 && !fCompleted)1623 {1624 /* If we previously got completed notification, don't trigger again. */1625 it->second.pProgress->notifyComplete(S_OK);1626 }1627 }1628 }1629 else1630 LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));1631 return rc;1632 }1633 1634 /* Function for handling the execution input status notification. */1635 int Guest::notifyCtrlExecInStatus(uint32_t u32Function,1636 PCALLBACKDATAEXECINSTATUS pData)1637 {1638 int rc = VINF_SUCCESS;1639 1640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);1641 1642 AssertPtr(pData);1643 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);1644 if (it != mCallbackMap.end())1645 {1646 PCALLBACKDATAEXECINSTATUS pCBData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;1647 AssertPtr(pCBData);1648 1649 /* Save bytes processed. */1650 pCBData->cbProcessed = pData->cbProcessed;1651 1652 /* Was progress canceled before? */1653 BOOL fCanceled;1654 ComAssert(!it->second.pProgress.isNull());1655 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && fCanceled)1656 {1657 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,1658 COM_IIDOF(IGuest),1659 Guest::getStaticComponentName(),1660 Guest::tr("The input operation was canceled"));1661 }1662 else1663 {1664 BOOL fCompleted;1665 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))1666 && !fCompleted)1667 {1668 /* If we previously got completed notification, don't trigger again. */1669 it->second.pProgress->notifyComplete(S_OK);1670 }1671 }1672 }1673 else1674 LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));1675 return rc;1676 }1677 1678 int Guest::notifyCtrlClientDisconnected(uint32_t u32Function,1679 PCALLBACKDATACLIENTDISCONNECTED pData)1680 {1681 int rc = VINF_SUCCESS;1682 1683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);1684 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);1685 if (it != mCallbackMap.end())1686 {1687 LogFlowFunc(("Client with CID=%u disconnected\n", it->first));1688 destroyCtrlCallbackContext(it);1689 }1690 return rc;1691 }1692 1693 Guest::CallbackMapIter Guest::getCtrlCallbackContextByID(uint32_t u32ContextID)1694 {1695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1696 return mCallbackMap.find(u32ContextID);1697 }1698 1699 Guest::GuestProcessMapIter Guest::getProcessByPID(uint32_t u32PID)1700 {1701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1702 return mGuestProcessMap.find(u32PID);1703 }1704 1705 /* No locking here; */1706 void Guest::destroyCtrlCallbackContext(Guest::CallbackMapIter it)1707 {1708 LogFlowFunc(("Destroying callback with CID=%u ...\n", it->first));1709 1710 if (it->second.pvData)1711 {1712 RTMemFree(it->second.pvData);1713 it->second.pvData = NULL;1714 it->second.cbData = 0;1715 }1716 1717 /* Notify outstanding waits for progress ... */1718 if ( it->second.pProgress1719 && !it->second.pProgress.isNull())1720 {1721 LogFlowFunc(("Handling progress for CID=%u ...\n", it->first));1722 1723 /*1724 * Assume we didn't complete to make sure we clean up even if the1725 * following call fails.1726 */1727 BOOL fCompleted = FALSE;1728 it->second.pProgress->COMGETTER(Completed)(&fCompleted);1729 if (!fCompleted)1730 {1731 LogFlowFunc(("Progress of CID=%u *not* completed, cancelling ...\n", it->first));1732 1733 /* Only cancel if not canceled before! */1734 BOOL fCanceled;1735 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && !fCanceled)1736 it->second.pProgress->Cancel();1737 1738 /*1739 * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only1740 * cancel won't work!). This could happen if the client thread (e.g. VBoxService, thread of a spawned process)1741 * is disconnecting without having the chance to sending a status message before, so we1742 * have to abort here to make sure the host never hangs/gets stuck while waiting for the1743 * progress object to become signalled.1744 */1745 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,1746 COM_IIDOF(IGuest),1747 Guest::getStaticComponentName(),1748 Guest::tr("The operation was canceled because client is shutting down"));1749 }1750 /*1751 * Do *not* NULL pProgress here, because waiting function like executeProcess()1752 * will still rely on this object for checking whether they have to give up!1753 */1754 }1755 }1756 1757 /* Adds a callback with a user provided data block and an optional progress object1758 * to the callback map. A callback is identified by a unique context ID which is used1759 * to identify a callback from the guest side. */1760 uint32_t Guest::addCtrlCallbackContext(eVBoxGuestCtrlCallbackType enmType, void *pvData, uint32_t cbData, Progress *pProgress)1761 {1762 AssertPtr(pProgress);1763 1764 /** @todo Put this stuff into a constructor! */1765 CallbackContext context;1766 context.mType = enmType;1767 context.pvData = pvData;1768 context.cbData = cbData;1769 context.pProgress = pProgress;1770 1771 /* Create a new context ID and assign it. */1772 CallbackMapIter it;1773 uint32_t uNewContext = 0;1774 do1775 {1776 /* Create a new context ID ... */1777 uNewContext = ASMAtomicIncU32(&mNextContextID);1778 if (uNewContext == UINT32_MAX)1779 ASMAtomicUoWriteU32(&mNextContextID, 1000);1780 /* Is the context ID already used? */1781 it = getCtrlCallbackContextByID(uNewContext);1782 } while(it != mCallbackMap.end());1783 1784 uint32_t nCallbacks = 0;1785 if ( it == mCallbackMap.end()1786 && uNewContext > 0)1787 {1788 /* We apparently got an unused context ID, let's use it! */1789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);1790 mCallbackMap[uNewContext] = context;1791 nCallbacks = mCallbackMap.size();1792 }1793 else1794 {1795 /* Should never happen ... */1796 {1797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1798 nCallbacks = mCallbackMap.size();1799 }1800 AssertReleaseMsg(uNewContext, ("No free context ID found! uNewContext=%u, nCallbacks=%u", uNewContext, nCallbacks));1801 }1802 1803 #if 01804 if (nCallbacks > 256) /* Don't let the container size get too big! */1805 {1806 Guest::CallbackListIter it = mCallbackList.begin();1807 destroyCtrlCallbackContext(it);1808 {1809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);1810 mCallbackList.erase(it);1811 }1812 }1813 #endif1814 return uNewContext;1815 }1816 #endif /* VBOX_WITH_GUEST_CONTROL */1817 1818 STDMETHODIMP Guest::ExecuteProcess(IN_BSTR aCommand, ULONG aFlags,1819 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),1820 IN_BSTR aUserName, IN_BSTR aPassword,1821 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress)1822 {1823 /** @todo r=bird: Eventually we should clean up all the timeout parameters1824 * in the API and have the same way of specifying infinite waits! */1825 #ifndef VBOX_WITH_GUEST_CONTROL1826 ReturnComNotImplemented();1827 #else /* VBOX_WITH_GUEST_CONTROL */1828 using namespace guestControl;1829 1830 CheckComArgStrNotEmptyOrNull(aCommand);1831 CheckComArgOutPointerValid(aPID);1832 CheckComArgOutPointerValid(aProgress);1833 1834 /* Do not allow anonymous executions (with system rights). */1835 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))1836 return setError(E_INVALIDARG, tr("No user name specified"));1837 1838 LogRel(("Executing guest process \"%s\" as user \"%s\" ...\n",1839 Utf8Str(aCommand).c_str(), Utf8Str(aUserName).c_str()));1840 1841 return executeProcessInternal(aCommand, aFlags, ComSafeArrayInArg(aArguments),1842 ComSafeArrayInArg(aEnvironment),1843 aUserName, aPassword, aTimeoutMS, aPID, aProgress, NULL /* rc */);1844 #endif1845 }1846 1847 HRESULT Guest::executeProcessInternal(IN_BSTR aCommand, ULONG aFlags,1848 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),1849 IN_BSTR aUserName, IN_BSTR aPassword,1850 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress, int *pRC)1851 {1852 /** @todo r=bird: Eventually we should clean up all the timeout parameters1853 * in the API and have the same way of specifying infinite waits! */1854 #ifndef VBOX_WITH_GUEST_CONTROL1855 ReturnComNotImplemented();1856 #else /* VBOX_WITH_GUEST_CONTROL */1857 using namespace guestControl;1858 1859 AutoCaller autoCaller(this);1860 if (FAILED(autoCaller.rc())) return autoCaller.rc();1861 1862 /* Validate flags. */1863 if (aFlags != ExecuteProcessFlag_None)1864 {1865 if ( !(aFlags & ExecuteProcessFlag_IgnoreOrphanedProcesses)1866 && !(aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)1867 && !(aFlags & ExecuteProcessFlag_Hidden))1868 {1869 if (pRC)1870 *pRC = VERR_INVALID_PARAMETER;1871 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);1872 }1873 }1874 1875 HRESULT rc = S_OK;1876 1877 try1878 {1879 /*1880 * Create progress object. Note that this is a multi operation1881 * object to perform the following steps:1882 * - Operation 1 (0): Create/start process.1883 * - Operation 2 (1): Wait for process to exit.1884 * If this progress completed successfully (S_OK), the process1885 * started and exited normally. In any other case an error/exception1886 * occurred.1887 */1888 ComObjPtr <Progress> progress;1889 rc = progress.createObject();1890 if (SUCCEEDED(rc))1891 {1892 rc = progress->init(static_cast<IGuest*>(this),1893 Bstr(tr("Executing process")).raw(),1894 TRUE,1895 2, /* Number of operations. */1896 Bstr(tr("Starting process ...")).raw()); /* Description of first stage. */1897 }1898 ComAssertComRC(rc);1899 1900 /*1901 * Prepare process execution.1902 */1903 int vrc = VINF_SUCCESS;1904 Utf8Str Utf8Command(aCommand);1905 1906 /* Adjust timeout. If set to 0, we define1907 * an infinite timeout. */1908 if (aTimeoutMS == 0)1909 aTimeoutMS = UINT32_MAX;1910 1911 /* Prepare arguments. */1912 char **papszArgv = NULL;1913 uint32_t uNumArgs = 0;1914 if (aArguments > 0)1915 {1916 com::SafeArray<IN_BSTR> args(ComSafeArrayInArg(aArguments));1917 uNumArgs = args.size();1918 papszArgv = (char**)RTMemAlloc(sizeof(char*) * (uNumArgs + 1));1919 AssertReturn(papszArgv, E_OUTOFMEMORY);1920 for (unsigned i = 0; RT_SUCCESS(vrc) && i < uNumArgs; i++)1921 vrc = RTUtf16ToUtf8(args[i], &papszArgv[i]);1922 papszArgv[uNumArgs] = NULL;1923 }1924 1925 Utf8Str Utf8UserName(aUserName);1926 Utf8Str Utf8Password(aPassword);1927 if (RT_SUCCESS(vrc))1928 {1929 uint32_t uContextID = 0;1930 1931 char *pszArgs = NULL;1932 if (uNumArgs > 0)1933 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, 0);1934 if (RT_SUCCESS(vrc))1935 {1936 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */1937 1938 /* Prepare environment. */1939 void *pvEnv = NULL;1940 uint32_t uNumEnv = 0;1941 uint32_t cbEnv = 0;1942 if (aEnvironment > 0)1943 {1944 com::SafeArray<IN_BSTR> env(ComSafeArrayInArg(aEnvironment));1945 1946 for (unsigned i = 0; i < env.size(); i++)1947 {1948 vrc = prepareExecuteEnv(Utf8Str(env[i]).c_str(), &pvEnv, &cbEnv, &uNumEnv);1949 if (RT_FAILURE(vrc))1950 break;1951 }1952 }1953 1954 if (RT_SUCCESS(vrc))1955 {1956 PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));1957 AssertReturn(pData, VBOX_E_IPRT_ERROR);1958 RT_ZERO(*pData);1959 uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START,1960 pData, sizeof(CALLBACKDATAEXECSTATUS), progress);1961 Assert(uContextID > 0);1962 1963 VBOXHGCMSVCPARM paParms[15];1964 int i = 0;1965 paParms[i++].setUInt32(uContextID);1966 paParms[i++].setPointer((void*)Utf8Command.c_str(), (uint32_t)Utf8Command.length() + 1);1967 paParms[i++].setUInt32(aFlags);1968 paParms[i++].setUInt32(uNumArgs);1969 paParms[i++].setPointer((void*)pszArgs, cbArgs);1970 paParms[i++].setUInt32(uNumEnv);1971 paParms[i++].setUInt32(cbEnv);1972 paParms[i++].setPointer((void*)pvEnv, cbEnv);1973 paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);1974 paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);1975 1976 /*1977 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout1978 * until the process was started - the process itself then gets an infinite timeout for execution.1979 * This is handy when we want to start a process inside a worker thread within a certain timeout1980 * but let the started process perform lengthly operations then.1981 */1982 if (aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)1983 paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);1984 else1985 paParms[i++].setUInt32(aTimeoutMS);1986 1987 VMMDev *vmmDev;1988 {1989 /* Make sure mParent is valid, so set the read lock while using.1990 * Do not keep this lock while doing the actual call, because in the meanwhile1991 * another thread could request a write lock which would be a bad idea ... */1992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1993 1994 /* Forward the information to the VMM device. */1995 AssertPtr(mParent);1996 vmmDev = mParent->getVMMDev();1997 }1998 1999 if (vmmDev)2000 {2001 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));2002 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,2003 i, paParms);2004 }2005 else2006 vrc = VERR_INVALID_VM_HANDLE;2007 RTMemFree(pvEnv);2008 }2009 RTStrFree(pszArgs);2010 }2011 if (RT_SUCCESS(vrc))2012 {2013 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));2014 2015 /*2016 * Wait for the HGCM low level callback until the process2017 * has been started (or something went wrong). This is necessary to2018 * get the PID.2019 */2020 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);2021 BOOL fCanceled = FALSE;2022 if (it != mCallbackMap.end())2023 {2024 ComAssert(!it->second.pProgress.isNull());2025 2026 /*2027 * Wait for the first stage (=0) to complete (that is starting the process).2028 */2029 PCALLBACKDATAEXECSTATUS pData = NULL;2030 rc = it->second.pProgress->WaitForOperationCompletion(0, aTimeoutMS);2031 if (SUCCEEDED(rc))2032 {2033 /* Was the operation canceled by one of the parties? */2034 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);2035 if (FAILED(rc)) throw rc;2036 2037 if (!fCanceled)2038 {2039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2040 2041 pData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;2042 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECSTATUS));2043 AssertPtr(pData);2044 2045 /* Did we get some status? */2046 switch (pData->u32Status)2047 {2048 case PROC_STS_STARTED:2049 /* Process is (still) running; get PID. */2050 *aPID = pData->u32PID;2051 break;2052 2053 /* In any other case the process either already2054 * terminated or something else went wrong, so no PID ... */2055 case PROC_STS_TEN: /* Terminated normally. */2056 case PROC_STS_TEA: /* Terminated abnormally. */2057 case PROC_STS_TES: /* Terminated through signal. */2058 case PROC_STS_TOK:2059 case PROC_STS_TOA:2060 case PROC_STS_DWN:2061 /*2062 * Process (already) ended, but we want to get the2063 * PID anyway to retrieve the output in a later call.2064 */2065 *aPID = pData->u32PID;2066 break;2067 2068 case PROC_STS_ERROR:2069 vrc = pData->u32Flags; /* u32Flags member contains IPRT error code. */2070 break;2071 2072 case PROC_STS_UNDEFINED:2073 vrc = VERR_TIMEOUT; /* Operation did not complete within time. */2074 break;2075 2076 default:2077 vrc = VERR_INVALID_PARAMETER; /* Unknown status, should never happen! */2078 break;2079 }2080 }2081 else /* Operation was canceled. */2082 vrc = VERR_CANCELLED;2083 }2084 else /* Operation did not complete within time. */2085 vrc = VERR_TIMEOUT;2086 2087 /*2088 * Do *not* remove the callback yet - we might wait with the IProgress object on something2089 * else (like end of process) ...2090 */2091 if (RT_FAILURE(vrc))2092 {2093 if (vrc == VERR_FILE_NOT_FOUND) /* This is the most likely error. */2094 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,2095 tr("The file '%s' was not found on guest"), Utf8Command.c_str());2096 else if (vrc == VERR_PATH_NOT_FOUND)2097 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,2098 tr("The path to file '%s' was not found on guest"), Utf8Command.c_str());2099 else if (vrc == VERR_BAD_EXE_FORMAT)2100 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,2101 tr("The file '%s' is not an executable format on guest"), Utf8Command.c_str());2102 else if (vrc == VERR_AUTHENTICATION_FAILURE)2103 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,2104 tr("The specified user '%s' was not able to logon on guest"), Utf8UserName.c_str());2105 else if (vrc == VERR_TIMEOUT)2106 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,2107 tr("The guest did not respond within time (%ums)"), aTimeoutMS);2108 else if (vrc == VERR_CANCELLED)2109 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,2110 tr("The execution operation was canceled"));2111 else if (vrc == VERR_PERMISSION_DENIED)2112 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,2113 tr("Invalid user/password credentials"));2114 else2115 {2116 if (pData && pData->u32Status == PROC_STS_ERROR)2117 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,2118 tr("Process could not be started: %Rrc"), pData->u32Flags);2119 else2120 rc = setErrorNoLog(E_UNEXPECTED,2121 tr("The service call failed with error %Rrc"), vrc);2122 }2123 }2124 else /* Execution went fine. */2125 {2126 /* Return the progress to the caller. */2127 progress.queryInterfaceTo(aProgress);2128 }2129 }2130 else /* Callback context not found; should never happen! */2131 AssertMsg(it != mCallbackMap.end(), ("Callback context with ID %u not found!", uContextID));2132 }2133 else /* HGCM related error codes .*/2134 {2135 if (vrc == VERR_INVALID_VM_HANDLE)2136 rc = setErrorNoLog(VBOX_E_VM_ERROR,2137 tr("VMM device is not available (is the VM running?)"));2138 else if (vrc == VERR_NOT_FOUND)2139 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,2140 tr("The guest execution service is not ready"));2141 else if (vrc == VERR_HGCM_SERVICE_NOT_FOUND)2142 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,2143 tr("The guest execution service is not available"));2144 else /* HGCM call went wrong. */2145 rc = setErrorNoLog(E_UNEXPECTED,2146 tr("The HGCM call failed with error %Rrc"), vrc);2147 }2148 2149 for (unsigned i = 0; i < uNumArgs; i++)2150 RTMemFree(papszArgv[i]);2151 RTMemFree(papszArgv);2152 }2153 2154 if (RT_FAILURE(vrc))2155 {2156 if (!pRC) /* Skip logging internal calls. */2157 LogRel(("Executing guest process \"%s\" as user \"%s\" failed with %Rrc\n",2158 Utf8Command.c_str(), Utf8UserName.c_str(), vrc));2159 }2160 2161 if (pRC)2162 *pRC = vrc;2163 }2164 catch (std::bad_alloc &)2165 {2166 rc = E_OUTOFMEMORY;2167 }2168 return rc;2169 #endif /* VBOX_WITH_GUEST_CONTROL */2170 }2171 2172 STDMETHODIMP Guest::SetProcessInput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, ComSafeArrayIn(BYTE, aData), ULONG *aBytesWritten)2173 {2174 #ifndef VBOX_WITH_GUEST_CONTROL2175 ReturnComNotImplemented();2176 #else /* VBOX_WITH_GUEST_CONTROL */2177 using namespace guestControl;2178 2179 CheckComArgExpr(aPID, aPID > 0);2180 CheckComArgOutPointerValid(aBytesWritten);2181 2182 /* Validate flags. */2183 if (aFlags)2184 {2185 if (!(aFlags & ProcessInputFlag_EndOfFile))2186 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);2187 }2188 2189 AutoCaller autoCaller(this);2190 if (FAILED(autoCaller.rc())) return autoCaller.rc();2191 2192 HRESULT rc = S_OK;2193 2194 try2195 {2196 /* Init. */2197 *aBytesWritten = 0;2198 2199 /* Search for existing PID. */2200 GuestProcessMapIterConst itProc = getProcessByPID(aPID);2201 if (itProc != mGuestProcessMap.end())2202 {2203 /* PID exists; check if process is still running. */2204 if (itProc->second.mStatus != PROC_STS_STARTED)2205 {2206 rc = setError(VBOX_E_IPRT_ERROR,2207 tr("Process (PID %u) does not run anymore! Status: %ld, Flags: %u, Exit Code: %u"),2208 aPID, itProc->second.mStatus, itProc->second.mFlags, itProc->second.mExitCode);2209 }2210 }2211 else2212 rc = setError(VBOX_E_IPRT_ERROR,2213 tr("Process (PID %u) not found!"), aPID);2214 2215 if (SUCCEEDED(rc))2216 {2217 /*2218 * Create progress object.2219 * This progress object, compared to the one in executeProgress() above2220 * is only local and is used to determine whether the operation finished2221 * or got canceled.2222 */2223 ComObjPtr <Progress> progress;2224 rc = progress.createObject();2225 if (SUCCEEDED(rc))2226 {2227 rc = progress->init(static_cast<IGuest*>(this),2228 Bstr(tr("Setting input for process")).raw(),2229 TRUE /* Cancelable */);2230 }2231 if (FAILED(rc)) return rc;2232 2233 /* Adjust timeout. */2234 if (aTimeoutMS == 0)2235 aTimeoutMS = UINT32_MAX;2236 2237 PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECINSTATUS));2238 AssertReturn(pData, VBOX_E_IPRT_ERROR);2239 RT_ZERO(*pData);2240 /* Save PID + output flags for later use. */2241 pData->u32PID = aPID;2242 pData->u32Flags = aFlags;2243 /* Add job to callback contexts. */2244 uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS,2245 pData, sizeof(CALLBACKDATAEXECINSTATUS), progress);2246 Assert(uContextID > 0);2247 2248 com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(aData));2249 uint32_t cbSize = sfaData.size();2250 2251 VBOXHGCMSVCPARM paParms[6];2252 int i = 0;2253 paParms[i++].setUInt32(uContextID);2254 paParms[i++].setUInt32(aPID);2255 paParms[i++].setUInt32(aFlags);2256 paParms[i++].setPointer(sfaData.raw(), cbSize);2257 paParms[i++].setUInt32(cbSize);2258 2259 int vrc = VINF_SUCCESS;2260 2261 {2262 VMMDev *vmmDev;2263 {2264 /* Make sure mParent is valid, so set the read lock while using.2265 * Do not keep this lock while doing the actual call, because in the meanwhile2266 * another thread could request a write lock which would be a bad idea ... */2267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2268 2269 /* Forward the information to the VMM device. */2270 AssertPtr(mParent);2271 vmmDev = mParent->getVMMDev();2272 }2273 2274 if (vmmDev)2275 {2276 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));2277 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_SET_INPUT,2278 i, paParms);2279 }2280 }2281 2282 if (RT_SUCCESS(vrc))2283 {2284 LogFlowFunc(("Waiting for HGCM callback ...\n"));2285 2286 /*2287 * Wait for the HGCM low level callback until the process2288 * has been started (or something went wrong). This is necessary to2289 * get the PID.2290 */2291 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);2292 BOOL fCanceled = FALSE;2293 if (it != mCallbackMap.end())2294 {2295 ComAssert(!it->second.pProgress.isNull());2296 2297 /* Wait until operation completed. */2298 rc = it->second.pProgress->WaitForCompletion(aTimeoutMS);2299 if (FAILED(rc)) throw rc;2300 2301 /* Was the operation canceled by one of the parties? */2302 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);2303 if (FAILED(rc)) throw rc;2304 2305 if (!fCanceled)2306 {2307 BOOL fCompleted;2308 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))2309 && fCompleted)2310 {2311 PCALLBACKDATAEXECINSTATUS pStatusData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;2312 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECINSTATUS));2313 AssertPtr(pStatusData);2314 2315 *aBytesWritten = pStatusData->cbProcessed;2316 }2317 }2318 else /* Operation was canceled. */2319 vrc = VERR_CANCELLED;2320 2321 if (RT_FAILURE(vrc))2322 {2323 if (vrc == VERR_CANCELLED)2324 {2325 rc = setError(VBOX_E_IPRT_ERROR,2326 tr("The input operation was canceled"));2327 }2328 else2329 {2330 rc = setError(E_UNEXPECTED,2331 tr("The service call failed with error %Rrc"), vrc);2332 }2333 }2334 2335 {2336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);2337 /*2338 * Destroy locally used progress object.2339 */2340 destroyCtrlCallbackContext(it);2341 }2342 2343 /* Remove callback context (not used anymore). */2344 mCallbackMap.erase(it);2345 }2346 else /* PID lookup failed. */2347 rc = setError(VBOX_E_IPRT_ERROR,2348 tr("Process (PID %u) not found!"), aPID);2349 }2350 else /* HGCM operation failed. */2351 rc = setError(E_UNEXPECTED,2352 tr("The HGCM call failed with error %Rrc"), vrc);2353 2354 /* Cleanup. */2355 progress->uninit();2356 progress.setNull();2357 }2358 }2359 catch (std::bad_alloc &)2360 {2361 rc = E_OUTOFMEMORY;2362 }2363 return rc;2364 #endif2365 }2366 2367 STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))2368 {2369 /** @todo r=bird: Eventually we should clean up all the timeout parameters2370 * in the API and have the same way of specifying infinite waits! */2371 #ifndef VBOX_WITH_GUEST_CONTROL2372 ReturnComNotImplemented();2373 #else /* VBOX_WITH_GUEST_CONTROL */2374 using namespace guestControl;2375 2376 CheckComArgExpr(aPID, aPID > 0);2377 if (aSize < 0)2378 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);2379 if (aFlags != 0) /* Flags are not supported at the moment. */2380 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);2381 2382 AutoCaller autoCaller(this);2383 if (FAILED(autoCaller.rc())) return autoCaller.rc();2384 2385 HRESULT rc = S_OK;2386 2387 try2388 {2389 /*2390 * Create progress object.2391 * This progress object, compared to the one in executeProgress() above2392 * is only local and is used to determine whether the operation finished2393 * or got canceled.2394 */2395 ComObjPtr <Progress> progress;2396 rc = progress.createObject();2397 if (SUCCEEDED(rc))2398 {2399 rc = progress->init(static_cast<IGuest*>(this),2400 Bstr(tr("Getting output of process")).raw(),2401 TRUE);2402 }2403 if (FAILED(rc)) return rc;2404 2405 /* Adjust timeout. */2406 if (aTimeoutMS == 0)2407 aTimeoutMS = UINT32_MAX;2408 2409 /* Search for existing PID. */2410 PCALLBACKDATAEXECOUT pData = (CALLBACKDATAEXECOUT*)RTMemAlloc(sizeof(CALLBACKDATAEXECOUT));2411 AssertReturn(pData, VBOX_E_IPRT_ERROR);2412 RT_ZERO(*pData);2413 /* Save PID + output flags for later use. */2414 pData->u32PID = aPID;2415 pData->u32Flags = aFlags;2416 /* Add job to callback contexts. */2417 uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT,2418 pData, sizeof(CALLBACKDATAEXECOUT), progress);2419 Assert(uContextID > 0);2420 2421 com::SafeArray<BYTE> outputData((size_t)aSize);2422 2423 VBOXHGCMSVCPARM paParms[5];2424 int i = 0;2425 paParms[i++].setUInt32(uContextID);2426 paParms[i++].setUInt32(aPID);2427 paParms[i++].setUInt32(aFlags); /** @todo Should represent stdout and/or stderr. */2428 2429 int vrc = VINF_SUCCESS;2430 2431 {2432 VMMDev *vmmDev;2433 {2434 /* Make sure mParent is valid, so set the read lock while using.2435 * Do not keep this lock while doing the actual call, because in the meanwhile2436 * another thread could request a write lock which would be a bad idea ... */2437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2438 2439 /* Forward the information to the VMM device. */2440 AssertPtr(mParent);2441 vmmDev = mParent->getVMMDev();2442 }2443 2444 if (vmmDev)2445 {2446 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));2447 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,2448 i, paParms);2449 }2450 }2451 2452 if (RT_SUCCESS(vrc))2453 {2454 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));2455 2456 /*2457 * Wait for the HGCM low level callback until the process2458 * has been started (or something went wrong). This is necessary to2459 * get the PID.2460 */2461 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);2462 BOOL fCanceled = FALSE;2463 if (it != mCallbackMap.end())2464 {2465 ComAssert(!it->second.pProgress.isNull());2466 2467 /* Wait until operation completed. */2468 rc = it->second.pProgress->WaitForCompletion(aTimeoutMS);2469 if (FAILED(rc)) throw rc;2470 2471 /* Was the operation canceled by one of the parties? */2472 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);2473 if (FAILED(rc)) throw rc;2474 2475 if (!fCanceled)2476 {2477 BOOL fCompleted;2478 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))2479 && fCompleted)2480 {2481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2482 2483 /* Did we get some output? */2484 pData = (PCALLBACKDATAEXECOUT)it->second.pvData;2485 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECOUT));2486 AssertPtr(pData);2487 2488 if (pData->cbData)2489 {2490 /* Do we need to resize the array? */2491 if (pData->cbData > aSize)2492 outputData.resize(pData->cbData);2493 2494 /* Fill output in supplied out buffer. */2495 memcpy(outputData.raw(), pData->pvData, pData->cbData);2496 outputData.resize(pData->cbData); /* Shrink to fit actual buffer size. */2497 }2498 else2499 vrc = VERR_NO_DATA; /* This is not an error we want to report to COM. */2500 }2501 else /* If callback not called within time ... well, that's a timeout! */2502 vrc = VERR_TIMEOUT;2503 }2504 else /* Operation was canceled. */2505 {2506 vrc = VERR_CANCELLED;2507 }2508 2509 if (RT_FAILURE(vrc))2510 {2511 if (vrc == VERR_NO_DATA)2512 {2513 /* This is not an error we want to report to COM. */2514 rc = S_OK;2515 }2516 else if (vrc == VERR_TIMEOUT)2517 {2518 rc = setError(VBOX_E_IPRT_ERROR,2519 tr("The guest did not output within time (%ums)"), aTimeoutMS);2520 }2521 else if (vrc == VERR_CANCELLED)2522 {2523 rc = setError(VBOX_E_IPRT_ERROR,2524 tr("The output operation was canceled"));2525 }2526 else2527 {2528 rc = setError(E_UNEXPECTED,2529 tr("The service call failed with error %Rrc"), vrc);2530 }2531 }2532 2533 {2534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);2535 /*2536 * Destroy locally used progress object.2537 */2538 destroyCtrlCallbackContext(it);2539 }2540 2541 /* Remove callback context (not used anymore). */2542 mCallbackMap.erase(it);2543 }2544 else /* PID lookup failed. */2545 rc = setError(VBOX_E_IPRT_ERROR,2546 tr("Process (PID %u) not found!"), aPID);2547 }2548 else /* HGCM operation failed. */2549 rc = setError(E_UNEXPECTED,2550 tr("The HGCM call failed with error %Rrc"), vrc);2551 2552 /* Cleanup. */2553 progress->uninit();2554 progress.setNull();2555 2556 /* If something failed (or there simply was no data, indicated by VERR_NO_DATA,2557 * we return an empty array so that the frontend knows when to give up. */2558 if (RT_FAILURE(vrc) || FAILED(rc))2559 outputData.resize(0);2560 outputData.detachTo(ComSafeArrayOutArg(aData));2561 }2562 catch (std::bad_alloc &)2563 {2564 rc = E_OUTOFMEMORY;2565 }2566 return rc;2567 #endif2568 }2569 2570 STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ULONG *aStatus)2571 {2572 #ifndef VBOX_WITH_GUEST_CONTROL2573 ReturnComNotImplemented();2574 #else /* VBOX_WITH_GUEST_CONTROL */2575 using namespace guestControl;2576 2577 AutoCaller autoCaller(this);2578 if (FAILED(autoCaller.rc())) return autoCaller.rc();2579 2580 HRESULT rc = S_OK;2581 2582 try2583 {2584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2585 2586 GuestProcessMapIterConst it = getProcessByPID(aPID);2587 if (it != mGuestProcessMap.end())2588 {2589 *aExitCode = it->second.mExitCode;2590 *aFlags = it->second.mFlags;2591 *aStatus = it->second.mStatus;2592 }2593 else2594 rc = setError(VBOX_E_IPRT_ERROR,2595 tr("Process (PID %u) not found!"), aPID);2596 }2597 catch (std::bad_alloc &)2598 {2599 rc = E_OUTOFMEMORY;2600 }2601 return rc;2602 #endif2603 }2604 2605 STDMETHODIMP Guest::CopyToGuest(IN_BSTR aSource, IN_BSTR aDest,2606 IN_BSTR aUserName, IN_BSTR aPassword,2607 ULONG aFlags, IProgress **aProgress)2608 {2609 #ifndef VBOX_WITH_GUEST_CONTROL2610 ReturnComNotImplemented();2611 #else /* VBOX_WITH_GUEST_CONTROL */2612 CheckComArgStrNotEmptyOrNull(aSource);2613 CheckComArgStrNotEmptyOrNull(aDest);2614 CheckComArgStrNotEmptyOrNull(aUserName);2615 CheckComArgStrNotEmptyOrNull(aPassword);2616 CheckComArgOutPointerValid(aProgress);2617 2618 AutoCaller autoCaller(this);2619 if (FAILED(autoCaller.rc())) return autoCaller.rc();2620 2621 /* Validate flags. */2622 if (aFlags != CopyFileFlag_None)2623 {2624 if ( !(aFlags & CopyFileFlag_Recursive)2625 && !(aFlags & CopyFileFlag_Update)2626 && !(aFlags & CopyFileFlag_FollowLinks))2627 {2628 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);2629 }2630 }2631 2632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2633 2634 HRESULT rc = S_OK;2635 2636 ComObjPtr<Progress> progress;2637 try2638 {2639 /* Create the progress object. */2640 progress.createObject();2641 2642 rc = progress->init(static_cast<IGuest*>(this),2643 Bstr(tr("Copying file")).raw(),2644 TRUE /* aCancelable */);2645 if (FAILED(rc)) throw rc;2646 2647 /* Initialize our worker task. */2648 TaskGuest *pTask = new TaskGuest(TaskGuest::CopyFile, this, progress);2649 AssertPtr(pTask);2650 std::auto_ptr<TaskGuest> task(pTask);2651 2652 /* Assign data - aSource is the source file on the host,2653 * aDest reflects the full path on the guest. */2654 task->strSource = (Utf8Str(aSource));2655 task->strDest = (Utf8Str(aDest));2656 task->strUserName = (Utf8Str(aUserName));2657 task->strPassword = (Utf8Str(aPassword));2658 task->uFlags = aFlags;2659 2660 rc = task->startThread();2661 if (FAILED(rc)) throw rc;2662 2663 /* Don't destruct on success. */2664 task.release();2665 }2666 catch (HRESULT aRC)2667 {2668 rc = aRC;2669 }2670 2671 if (SUCCEEDED(rc))2672 {2673 /* Return progress to the caller. */2674 progress.queryInterfaceTo(aProgress);2675 }2676 return rc;2677 #endif /* VBOX_WITH_GUEST_CONTROL */2678 }2679 2680 STDMETHODIMP Guest::CreateDirectory(IN_BSTR aDirectory,2681 IN_BSTR aUserName, IN_BSTR aPassword,2682 ULONG aMode, ULONG aFlags,2683 IProgress **aProgress)2684 {2685 #ifndef VBOX_WITH_GUEST_CONTROL2686 ReturnComNotImplemented();2687 #else /* VBOX_WITH_GUEST_CONTROL */2688 using namespace guestControl;2689 2690 CheckComArgStrNotEmptyOrNull(aDirectory);2691 2692 /* Do not allow anonymous executions (with system rights). */2693 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))2694 return setError(E_INVALIDARG, tr("No user name specified"));2695 2696 LogRel(("Creating guest directory \"%s\" as user \"%s\" ...\n",2697 Utf8Str(aDirectory).c_str(), Utf8Str(aUserName).c_str()));2698 2699 return createDirectoryInternal(aDirectory,2700 aUserName, aPassword,2701 aMode, aFlags, aProgress, NULL /* rc */);2702 #endif2703 }2704 2705 HRESULT Guest::createDirectoryInternal(IN_BSTR aDirectory,2706 IN_BSTR aUserName, IN_BSTR aPassword,2707 ULONG aMode, ULONG aFlags,2708 IProgress **aProgress, int *pRC)2709 {2710 #ifndef VBOX_WITH_GUEST_CONTROL2711 ReturnComNotImplemented();2712 #else /* VBOX_WITH_GUEST_CONTROL */2713 using namespace guestControl;2714 2715 CheckComArgStrNotEmptyOrNull(aDirectory);2716 CheckComArgOutPointerValid(aProgress);2717 2718 AutoCaller autoCaller(this);2719 if (FAILED(autoCaller.rc())) return autoCaller.rc();2720 2721 /* Validate flags. */2722 if (aFlags != CreateDirectoryFlag_None)2723 {2724 if (!(aFlags & CreateDirectoryFlag_Parents))2725 {2726 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);2727 }2728 }2729 2730 /**2731 * @todo We return a progress object because we maybe later want to2732 * process more than one directory (or somewhat lengthly operations)2733 * that require having a progress object provided to the caller.2734 */2735 2736 HRESULT rc = S_OK;2737 try2738 {2739 Utf8Str Utf8Directory(aDirectory);2740 Utf8Str Utf8UserName(aUserName);2741 Utf8Str Utf8Password(aPassword);2742 2743 com::SafeArray<IN_BSTR> args;2744 com::SafeArray<IN_BSTR> env;2745 2746 /*2747 * Prepare tool command line.2748 */2749 args.push_back(Bstr(VBOXSERVICE_TOOL_MKDIR).raw()); /* The actual (internal) tool to use (as argv[0]). */2750 if (aFlags & CreateDirectoryFlag_Parents)2751 args.push_back(Bstr("--parents").raw()); /* We also want to create the parent directories. */2752 if (aMode > 0)2753 {2754 args.push_back(Bstr("--mode").raw()); /* Set the creation mode. */2755 2756 char szMode[16];2757 RTStrPrintf(szMode, sizeof(szMode), "%o", aMode);2758 args.push_back(Bstr(szMode).raw());2759 }2760 args.push_back(Bstr(Utf8Directory).raw()); /* The directory we want to create. */2761 2762 /*2763 * Execute guest process.2764 */2765 ComPtr<IProgress> progressExec;2766 ULONG uPID;2767 if (SUCCEEDED(rc))2768 {2769 rc = ExecuteProcess(Bstr(VBOXSERVICE_TOOL_MKDIR).raw(),2770 ExecuteProcessFlag_Hidden,2771 ComSafeArrayAsInParam(args),2772 ComSafeArrayAsInParam(env),2773 Bstr(Utf8UserName).raw(),2774 Bstr(Utf8Password).raw(),2775 5 * 1000 /* Wait 5s for getting the process started. */,2776 &uPID, progressExec.asOutParam());2777 }2778 2779 if (SUCCEEDED(rc))2780 {2781 /* Wait for process to exit ... */2782 BOOL fCompleted = FALSE;2783 BOOL fCanceled = FALSE;2784 2785 while ( SUCCEEDED(progressExec->COMGETTER(Completed(&fCompleted)))2786 && !fCompleted)2787 {2788 /* Progress canceled by Main API? */2789 if ( SUCCEEDED(progressExec->COMGETTER(Canceled(&fCanceled)))2790 && fCanceled)2791 {2792 break;2793 }2794 }2795 2796 ComObjPtr<Progress> progressCreate;2797 rc = progressCreate.createObject();2798 if (SUCCEEDED(rc))2799 {2800 rc = progressCreate->init(static_cast<IGuest*>(this),2801 Bstr(tr("Creating directory")).raw(),2802 TRUE);2803 }2804 if (FAILED(rc)) return rc;2805 2806 if (fCompleted)2807 {2808 ULONG uRetStatus, uRetExitCode, uRetFlags;2809 if (SUCCEEDED(rc))2810 {2811 rc = GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &uRetStatus);2812 if (SUCCEEDED(rc) && uRetExitCode != 0)2813 {2814 rc = setError(VBOX_E_IPRT_ERROR,2815 tr("Error while creating directory"));2816 }2817 }2818 }2819 else if (fCanceled)2820 rc = setError(VBOX_E_IPRT_ERROR,2821 tr("Directory creation was aborted"));2822 else2823 AssertReleaseMsgFailed(("Directory creation neither completed nor canceled!?"));2824 2825 if (SUCCEEDED(rc))2826 progressCreate->notifyComplete(S_OK);2827 else2828 progressCreate->notifyComplete(VBOX_E_IPRT_ERROR,2829 COM_IIDOF(IGuest),2830 Guest::getStaticComponentName(),2831 Guest::tr("Error while executing creation command"));2832 2833 /* Return the progress to the caller. */2834 progressCreate.queryInterfaceTo(aProgress);2835 }2836 }2837 catch (std::bad_alloc &)2838 {2839 rc = E_OUTOFMEMORY;2840 }2841 return rc;2842 #endif /* VBOX_WITH_GUEST_CONTROL */2843 }2844 2845 STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ULONG aFlags, IProgress **aProgress)2846 {2847 #ifndef VBOX_WITH_GUEST_CONTROL2848 ReturnComNotImplemented();2849 #else /* VBOX_WITH_GUEST_CONTROL */2850 CheckComArgStrNotEmptyOrNull(aSource);2851 CheckComArgOutPointerValid(aProgress);2852 2853 AutoCaller autoCaller(this);2854 if (FAILED(autoCaller.rc())) return autoCaller.rc();2855 2856 /* Validate flags. */2857 if (aFlags)2858 {2859 if (!(aFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))2860 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);2861 }2862 2863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2864 2865 HRESULT rc = S_OK;2866 2867 ComObjPtr<Progress> progress;2868 try2869 {2870 /* Create the progress object. */2871 progress.createObject();2872 2873 rc = progress->init(static_cast<IGuest*>(this),2874 Bstr(tr("Updating Guest Additions")).raw(),2875 TRUE /* aCancelable */);2876 if (FAILED(rc)) throw rc;2877 2878 /* Initialize our worker task. */2879 TaskGuest *pTask = new TaskGuest(TaskGuest::UpdateGuestAdditions, this, progress);2880 AssertPtr(pTask);2881 std::auto_ptr<TaskGuest> task(pTask);2882 2883 /* Assign data - in that case aSource is the full path2884 * to the Guest Additions .ISO we want to mount. */2885 task->strSource = (Utf8Str(aSource));2886 task->uFlags = aFlags;2887 2888 rc = task->startThread();2889 if (FAILED(rc)) throw rc;2890 2891 /* Don't destruct on success. */2892 task.release();2893 }2894 catch (HRESULT aRC)2895 {2896 rc = aRC;2897 }2898 2899 if (SUCCEEDED(rc))2900 {2901 /* Return progress to the caller. */2902 progress.queryInterfaceTo(aProgress);2903 }2904 return rc;2905 #endif /* VBOX_WITH_GUEST_CONTROL */2906 }2907 2908 500 // public methods only for internal purposes 2909 501 ///////////////////////////////////////////////////////////////////////////// -
trunk/src/VBox/Main/Makefile.kmk
r34963 r35170 642 642 ConsoleVRDPServer.cpp \ 643 643 GuestImpl.cpp \ 644 GuestCtrlImpl.cpp \ 644 645 KeyboardImpl.cpp \ 645 646 MouseImpl.cpp \
Note:
See TracChangeset
for help on using the changeset viewer.

