VirtualBox

Changeset 35170 in vbox


Ignore:
Timestamp:
Dec 16, 2010 10:40:53 AM (14 years ago)
Author:
vboxsync
Message:

Main: Moved Guest Control code to own file for better separation.

Location:
trunk/src/VBox/Main
Files:
2 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/GuestCtrlImpl.cpp

    r35164 r35170  
    11/* $Id$ */
    2 
    32/** @file
    4  *
    5  * VirtualBox COM class implementation
     3 * VirtualBox COM class implementation: Guest
    64 */
    75
     
    4341#include <memory>
    4442
    45 // defines
    46 /////////////////////////////////////////////////////////////////////////////
    47 
    48 // constructor / destructor
    49 /////////////////////////////////////////////////////////////////////////////
    50 
    51 DEFINE_EMPTY_CTOR_DTOR (Guest)
    52 
    5343struct Guest::TaskGuest
    5444{
     
    215205        if (!RTFileExists(aTask->strSource.c_str()))
    216206        {
    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());
    224210        }
    225211        else
     
    782768#endif
    783769
    784 HRESULT Guest::FinalConstruct()
    785 {
    786     return S_OK;
    787 }
    788 
    789 void Guest::FinalRelease()
    790 {
    791     uninit ();
    792 }
    793 
    794770// public methods only for internal purposes
    795771/////////////////////////////////////////////////////////////////////////////
    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     else
    820         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     else
    827         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_CONTROL
    836     /* Init the context ID counter at 1000. */
    837     mNextContextID = 1000;
    838 #endif
    839 
    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_CONTROL
    852     /* Scope write lock as much as possible. */
    853     {
    854         /*
    855          * Cleanup must be done *before* AutoUninitSpan to cancel all
    856          * all outstanding waits in API functions (which hold AutoCaller
    857          * 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 #endif
    870 
    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 properties
    880 /////////////////////////////////////////////////////////////////////////////
    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 either
    928          * really don't have the Guest Additions version yet or the guest is running
    929          * older Guest Additions (< 3.2.0) which don't provide VMMDevReq_ReportGuestInfo2,
    930          * so get the version + revision from the (hopefully) provided guest properties
    931          * 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_OK
    944                 && !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 change
    949                  * 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         else
    964         {
    965             /* If getting the version + revision above fails or they simply aren't there
    966              * because of *really* old Guest Additions we only can report the interface
    967              * version to at least have something. */
    968             mData.mInterfaceVersion.cloneTo(aAdditionsVersion);
    969             /** @todo r=bird: hr is still indicating failure! */
    970         }
    971     }
    972     else
    973         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 critsect
    1046          * 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 critsect
    1083      * 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 device
    1130      * 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     else
    1157     {
    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 }
    1239772
    1240773#ifdef VBOX_WITH_GUEST_CONTROL
     
    29062439}
    29072440
    2908 // public methods only for internal purposes
    2909 /////////////////////////////////////////////////////////////////////////////
    2910 
    2911 /**
    2912  * Sets the general Guest Additions information like
    2913  * API (interface) version and OS type.  Gets called by
    2914  * vmmdevUpdateGuestInfo.
    2915  *
    2916  * @param aInterfaceVersion
    2917  * @param aOsType
    2918  */
    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 deprecated
    2928      * and will not be used anymore!  We might need it to at least report
    2929      * something as version number if *really* ancient Guest Additions are
    2930      * 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 they
    2936      * are assumed to be active or not.  Since newer Additions do report
    2937      * the Additions version *before* calling this function (by calling
    2938      * VMMDevReportGuestInfo2, VMMDevReportGuestStatus, VMMDevReportGuestInfo,
    2939      * in that order) we can tell apart old and new Additions here. Old
    2940      * Additions never would set VMMDevReportGuestInfo2 (which set mData.mAdditionsVersion)
    2941      * so they just rely on the aInterfaceVersion string (which gets set by
    2942      * VMMDevReportGuestInfo).
    2943      *
    2944      * So only mark the Additions as being active (run level = system) when we
    2945      * don't have the Additions version set.
    2946      */
    2947     if (mData.mAdditionsVersion.isEmpty())
    2948     {
    2949         if (aInterfaceVersion.isEmpty())
    2950             mData.mAdditionsRunLevel = AdditionsRunLevelType_None;
    2951         else
    2952             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 here
    2958      * 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 and
    2964      * mSupportsGraphics here and disabling/enabling it later according to
    2965      * 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 aAdditionsVersion
    2975  * @param aVersionName
    2976  */
    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 to
    2987          * 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 Facility
    2999  * @param Status
    3000  * @param ulFlags
    3001  */
    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_VBoxGuestDriver
    3013         || (   Facility == VBoxGuestStatusFacility_All
    3014             && (   Status   == VBoxGuestStatusCurrent_Inactive
    3015                 || Status   == VBoxGuestStatusCurrent_Disabled
    3016                )
    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 and
    3043  *                      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  
    11/* $Id$ */
    2 
    32/** @file
    4  *
    5  * VirtualBox COM class implementation
     3 * VirtualBox COM class implementation: Guest
    64 */
    75
     
    3432#endif
    3533#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>
    4134#include <VBox/pgm.h>
    42 
    43 #include <memory>
    4435
    4536// defines
     
    5041
    5142DEFINE_EMPTY_CTOR_DTOR (Guest)
    52 
    53 struct Guest::TaskGuest
    54 {
    55     enum TaskType
    56     {
    57         /** Copies a file to the guest. */
    58         CopyFile = 50,
    59 
    60         /** Update Guest Additions by directly copying the required installer
    61          *  off the .ISO file, transfer it to the guest and execute the installer
    62          *  with system privileges. */
    63         UpdateGuestAdditions = 100
    64     };
    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_CONTROL
    122         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 #endif
    133         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         && !fCanceled
    168         && 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_CONTROL
    194 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 functions
    204      * which get called here can do that.
    205      */
    206 
    207     HRESULT rc = S_OK;
    208 
    209     try
    210     {
    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 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());
    224         }
    225         else
    226         {
    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             else
    237             {
    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                 else
    247                 {
    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                     else
    276                     {
    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 the
    289                          * actual copying, start the guest part now.
    290                          */
    291                         rc = pGuest->ExecuteProcess(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
    292                                                       ExecuteProcessFlag_Hidden
    293                                                     | 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 RTFileRead
    319                              * could not resolve/map the native error code to an IPRT code, so just
    320                              * 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 functions
    411      * which get called here can do that.
    412      */
    413 
    414     HRESULT rc = S_OK;
    415     BOOL fCompleted;
    416     BOOL fCanceled;
    417 
    418     try
    419     {
    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                 else
    441                     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         else
    451             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         else
    467         {
    468             uint32_t cbOffset;
    469             size_t cbLength;
    470             vrc = RTIsoFsGetFileInfo(&iso, installerImage.c_str(), &cbOffset, &cbLength);
    471             if (   RT_SUCCESS(vrc)
    472                 && cbOffset
    473                 && 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             else
    482             {
    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) to
    522                      * copy over/pipe the data into a file on the guest (with
    523                      * system rights, no username/password specified).
    524                      */
    525                     rc = pGuest->executeProcessInternal(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
    526                                                           ExecuteProcessFlag_Hidden
    527                                                         | 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 caller
    537                          * 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 error
    540                          * lookup to figure out what went wrong. */
    541                         switch (vrc)
    542                         {
    543                             /* Guest execution service is not (yet) ready. This basically means that either VBoxService
    544                              * is not running (yet) or that the Guest Additions are too old (because VBoxService does not
    545                              * 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 guest
    553                              * 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                     else
    568                     {
    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 file
    581                              * 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 (   cbRead
    587                                     && 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_andy
    606                                     LogRel(("Copying Guest Additions (%u bytes left) ...\n", cbLength));
    607                                 #endif
    608                                     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_andy
    623                                     LogRel(("Copying Guest Additions (%u bytes written) ...\n", uBytesWritten));
    624                                 #endif
    625                                     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                     && !fCanceled
    652                    )
    653                )
    654             {
    655                 /*
    656                  * Installer was transferred successfully, so let's start it
    657                  * (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 context
    670                  * 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 this
    674                  * 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 status
    677                  * using a running VBoxTray instance via balloon messages in the
    678                  * Windows taskbar. */
    679                 installerArgs.push_back(Bstr("/post_installstatus").raw());
    680 
    681                 /*
    682                  * Start the just copied over installer with system rights
    683                  * in silent mode on the guest. Don't use the hidden flag since there
    684                  * 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                     else
    705                         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                             else
    740                             {
    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                         else
    757                         {
    758                             LogRel(("Guest Additions update was canceled by the user\n"));
    759                         }
    760                     }
    761                     else
    762                         rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
    763                 }
    764                 else
    765                     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 #endif
    78343
    78444HRESULT Guest::FinalConstruct()
     
    1238498}
    1239499
    1240 #ifdef VBOX_WITH_GUEST_CONTROL
    1241 /**
    1242  * Appends environment variables to the environment block.
    1243  *
    1244  * Each var=value pair is separated by the null character ('\\0').  The whole
    1245  * block will be stored in one blob and disassembled on the guest side later to
    1246  * fit into the HGCM param structure.
    1247  *
    1248  * @returns VBox status code.
    1249  *
    1250  * @param   pszEnvVar       The environment variable=value to append to the
    1251  *                          environment block.
    1252  * @param   ppvList         This is actually a pointer to a char pointer
    1253  *                          variable which keeps track of the environment block
    1254  *                          that we're constructing.
    1255  * @param   pcbList         Pointer to the variable holding the current size of
    1256  *                          the environment block.  (List is a misnomer, go
    1257  *                          ahead a be confused.)
    1258  * @param   pcEnvVars       Pointer to the variable holding count of variables
    1259  *                          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         else
    1272         {
    1273             memcpy(pvTmp + *pcbList, pszEnv, cchEnv);
    1274             pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */
    1275             *ppvList = (void **)pvTmp;
    1276         }
    1277     }
    1278     else
    1279     {
    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 commands
    1299  * from the guest. Acts as a dispatcher for the actual class instance.
    1300  *
    1301  * @returns VBox status code.
    1302  *
    1303  * @todo
    1304  *
    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 any
    1315      * changes to the object state.
    1316      */
    1317 #ifdef DEBUG_andy
    1318     LogFlowFunc(("pvExtension = %p, u32Function = %d, pvParms = %p, cbParms = %d\n",
    1319                  pvExtension, u32Function, pvParms, cbParms));
    1320 #endif
    1321     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 to
    1466                      * our progress object. This is helpful for waiters which rely on the success of our progress object
    1467                      * 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                     else
    1480                     {
    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         else
    1521             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 something
    1529                  * 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     else
    1567         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->cbData
    1593             && 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         else
    1603         {
    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         else
    1619         {
    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     else
    1630         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         else
    1663         {
    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     else
    1674         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.pProgress
    1719          && !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 the
    1725          * 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 (only
    1740              * 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 we
    1742              * have to abort here to make sure the host never hangs/gets stuck while waiting for the
    1743              * 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 object
    1758  * to the callback map. A callback is identified by a unique context ID which is used
    1759  * 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     do
    1775     {
    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     else
    1794     {
    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 0
    1804     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 #endif
    1814     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 parameters
    1824  *        in the API and have the same way of specifying infinite waits!  */
    1825 #ifndef VBOX_WITH_GUEST_CONTROL
    1826     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 #endif
    1845 }
    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 parameters
    1853  *        in the API and have the same way of specifying infinite waits!  */
    1854 #ifndef VBOX_WITH_GUEST_CONTROL
    1855     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     try
    1878     {
    1879         /*
    1880          * Create progress object.  Note that this is a multi operation
    1881          * 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 process
    1885          * started and exited normally. In any other case an error/exception
    1886          * 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 define
    1907          * 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 timeout
    1978                      * 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 timeout
    1980                      * but let the started process perform lengthly operations then.
    1981                      */
    1982                     if (aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)
    1983                         paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
    1984                     else
    1985                         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 meanwhile
    1991                          * 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                     else
    2006                         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 process
    2017                  * has been started (or something went wrong). This is necessary to
    2018                  * 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 already
    2054                                  * 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 the
    2063                                      * 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 something
    2089                      * 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                         else
    2115                         {
    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                             else
    2120                                 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_CONTROL
    2175     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     try
    2195     {
    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         else
    2212             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() above
    2220              * is only local and is used to determine whether the operation finished
    2221              * 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 meanwhile
    2266                      * 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 process
    2288                  * has been started (or something went wrong). This is necessary to
    2289                  * 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                         else
    2329                         {
    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 #endif
    2365 }
    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 parameters
    2370  *        in the API and have the same way of specifying infinite waits!  */
    2371 #ifndef VBOX_WITH_GUEST_CONTROL
    2372     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     try
    2388     {
    2389         /*
    2390          * Create progress object.
    2391          * This progress object, compared to the one in executeProgress() above
    2392          * is only local and is used to determine whether the operation finished
    2393          * 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 meanwhile
    2436                  * 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 process
    2458              * has been started (or something went wrong). This is necessary to
    2459              * 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                         else
    2499                             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                     else
    2527                     {
    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 #endif
    2568 }
    2569 
    2570 STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ULONG *aStatus)
    2571 {
    2572 #ifndef VBOX_WITH_GUEST_CONTROL
    2573     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     try
    2583     {
    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         else
    2594             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 #endif
    2603 }
    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_CONTROL
    2610     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     try
    2638     {
    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_CONTROL
    2686     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 #endif
    2703 }
    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_CONTROL
    2711     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 to
    2732      *       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     try
    2738     {
    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             else
    2823                 AssertReleaseMsgFailed(("Directory creation neither completed nor canceled!?"));
    2824 
    2825             if (SUCCEEDED(rc))
    2826                 progressCreate->notifyComplete(S_OK);
    2827             else
    2828                 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_CONTROL
    2848     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     try
    2869     {
    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 path
    2884          * 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 
    2908500// public methods only for internal purposes
    2909501/////////////////////////////////////////////////////////////////////////////
  • trunk/src/VBox/Main/Makefile.kmk

    r34963 r35170  
    642642        ConsoleVRDPServer.cpp \
    643643        GuestImpl.cpp \
     644        GuestCtrlImpl.cpp \
    644645        KeyboardImpl.cpp \
    645646        MouseImpl.cpp \
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette