VirtualBox

Changeset 37589 in vbox


Ignore:
Timestamp:
Jun 22, 2011 1:20:06 PM (14 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
72433
Message:

Main/GuestCtrl: Major overhaul of internal guest control handling, refactored code, don't use iterators as parameters, minimize locking.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/process.h

    r37447 r37589  
    199199#define RTPROC_FLAGS_SAME_CONTRACT          RT_BIT(3)
    200200/** Do not load user profile data when executing a process. This bit
    201  *  at the moment only is used on Windows. */
     201 *  at the moment only is valid on Windows. */
    202202#define RTPROC_FLAGS_NO_PROFILE             RT_BIT(4)
    203203/** @}  */
  • trunk/src/VBox/Main/include/GuestImpl.h

    r37375 r37589  
    130130# ifdef VBOX_WITH_GUEST_CONTROL
    131131    /** Static callback for handling guest notifications. */
    132     static DECLCALLBACK(int) doGuestCtrlNotification(void *pvExtension, uint32_t u32Function, void *pvParms, uint32_t cbParms);
     132    static DECLCALLBACK(int) notifyCtrlDispatcher(void *pvExtension, uint32_t u32Function, void *pvParms, uint32_t cbParms);
    133133# endif
    134134    static HRESULT setErrorStatic(HRESULT aResultCode,
     
    146146    HRESULT taskUpdateGuestAdditions(TaskGuest *aTask);
    147147
    148     // Internal callback context handling.
    149     struct CallbackContext
     148    // Internal guest callback representation.
     149    typedef struct VBOXGUESTCTRL_CALLBACK
    150150    {
    151151        eVBoxGuestCtrlCallbackType  mType;
     
    156156        /** Pointer to user-supplied IProgress. */
    157157        ComObjPtr<Progress>         pProgress;
    158     };
    159     /*
    160      * The map key is the context ID.
    161      */
    162     typedef std::map< uint32_t, CallbackContext > CallbackMap;
    163     typedef std::map< uint32_t, CallbackContext >::iterator CallbackMapIter;
    164     typedef std::map< uint32_t, CallbackContext >::const_iterator CallbackMapIterConst;
    165 
    166     struct GuestProcess
    167     {
    168         ExecuteProcessStatus_T      mStatus;
    169         uint32_t                    mFlags;
    170         uint32_t                    mExitCode;
    171     };
    172     /*
    173      * The map key is the PID (process identifier).
    174      */
    175     typedef std::map< uint32_t, GuestProcess > GuestProcessMap;
    176     typedef std::map< uint32_t, GuestProcess >::iterator GuestProcessMapIter;
    177     typedef std::map< uint32_t, GuestProcess >::const_iterator GuestProcessMapIterConst;
    178 
    179     int directoryEntryAppend(const char *pszPath, PRTLISTNODE pList);
    180     int directoryRead(const char *pszDirectory, const char *pszFilter, ULONG uFlags, ULONG *pcObjects, PRTLISTNODE pList);
    181 
    182     int prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnv);
    183     /** Handler for guest execution control notifications. */
     158    } VBOXGUESTCTRL_CALLBACK, *PVBOXGUESTCTRL_CALLBACK;
     159    typedef std::map< uint32_t, VBOXGUESTCTRL_CALLBACK > CallbackMap;
     160    typedef std::map< uint32_t, VBOXGUESTCTRL_CALLBACK >::iterator CallbackMapIter;
     161    typedef std::map< uint32_t, VBOXGUESTCTRL_CALLBACK >::const_iterator CallbackMapIterConst;
     162
     163    int callbackAdd(const PVBOXGUESTCTRL_CALLBACK pCallbackData, uint32_t *puContextID);
     164    void callbackDestroy(uint32_t uContextID);
     165    bool callbackExists(uint32_t uContextID);
     166    void callbackFreeUserData(void *pvData);
     167    int callbackGetUserData(uint32_t uContextID, eVBoxGuestCtrlCallbackType *pEnmType, void **ppvData, size_t *pcbData);
     168    void* callbackGetUserDataMutableRaw(uint32_t uContextID, size_t *pcbData);
     169    bool callbackIsCanceled(uint32_t uContextID);
     170    bool callbackIsComplete(uint32_t uContextID);
     171    int callbackMoveForward(uint32_t uContextID, const char *pszMessage);
     172    int callbackNotifyEx(uint32_t uContextID, int iRC, const char *pszMessage);
     173    int callbackNotifyComplete(uint32_t uContextID);
     174    int callbackNotifyAllForPID(uint32_t uPID, int iRC, const char *pszMessage);
     175    int callbackWaitForCompletion(uint32_t uContextID, LONG lStage, LONG lTimeout);
     176
    184177    int notifyCtrlClientDisconnected(uint32_t u32Function, PCALLBACKDATACLIENTDISCONNECTED pData);
    185178    int notifyCtrlExecStatus(uint32_t u32Function, PCALLBACKDATAEXECSTATUS pData);
    186179    int notifyCtrlExecOut(uint32_t u32Function, PCALLBACKDATAEXECOUT pData);
    187180    int notifyCtrlExecInStatus(uint32_t u32Function, PCALLBACKDATAEXECINSTATUS pData);
    188     CallbackMapIter getCtrlCallbackContextByID(uint32_t u32ContextID);
    189     GuestProcessMapIter getProcessByPID(uint32_t u32PID);
    190     void notifyCtrlCallbackContext(Guest::CallbackMapIter it, const char *pszText);
    191     void destroyCtrlCallbackContext(CallbackMapIter it);
    192     uint32_t addCtrlCallbackContext(eVBoxGuestCtrlCallbackType enmType, void *pvData, uint32_t cbData, Progress* pProgress);
     181
     182    // Internal guest process representation.
     183    typedef struct VBOXGUESTCTRL_PROCESS
     184    {
     185        ExecuteProcessStatus_T      mStatus;
     186        uint32_t                    mFlags;
     187        uint32_t                    mExitCode;
     188    } VBOXGUESTCTRL_PROCESS, *PVBOXGUESTCTRL_PROCESS;
     189    typedef std::map< uint32_t, VBOXGUESTCTRL_PROCESS > GuestProcessMap;
     190    typedef std::map< uint32_t, VBOXGUESTCTRL_PROCESS >::iterator GuestProcessMapIter;
     191    typedef std::map< uint32_t, VBOXGUESTCTRL_PROCESS >::const_iterator GuestProcessMapIterConst;
     192
     193    int processAdd(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags);
     194    int processGetByPID(uint32_t u32PID, PVBOXGUESTCTRL_PROCESS pProcess);
     195    int processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags);
     196
     197    // Utility functions.
     198    int directoryEntryAppend(const char *pszPath, PRTLISTNODE pList);
     199    int directoryRead(const char *pszDirectory, const char *pszFilter, ULONG uFlags, ULONG *pcObjects, PRTLISTNODE pList);
     200    int prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnv);
     201
     202    /*
     203     * Handler for guest execution control notifications.
     204     */
     205    HRESULT handleErrorCompletion(int rc);
     206    HRESULT handleErrorHGCM(int rc);
     207
    193208    HRESULT waitForProcessStatusChange(ULONG uPID, ExecuteProcessStatus_T *pRetStatus, ULONG *puRetExitCode, ULONG uTimeoutMS);
     209    HRESULT executeProcessResult(const char *pszCommand, const char *pszUser, ULONG ulTimeout, PCALLBACKDATAEXECSTATUS pExecStatus, ULONG *puPID);
    194210# endif
    195211
     
    220236    /** General extension callback for guest control. */
    221237    HGCMSVCEXTHANDLE  mhExtCtrl;
    222 
     238    /** Next upcoming context ID. */
    223239    volatile uint32_t mNextContextID;
    224     CallbackMap mCallbackMap;
    225     GuestProcessMap mGuestProcessMap;
     240    CallbackMap       mCallbackMap;
     241    GuestProcessMap   mGuestProcessMap;
    226242# endif
    227243};
  • trunk/src/VBox/Main/src-client/ConsoleImpl2.cpp

    r37471 r37589  
    24242424
    24252425                    parm.setUInt32(!useHostClipboard());
    2426                    
     2426
    24272427                    pVMMDev->hgcmHostCall("VBoxSharedClipboard", VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, 1, &parm);
    24282428
     
    47674767        HGCMSVCEXTHANDLE hDummy;
    47684768        rc = HGCMHostRegisterServiceExtension(&hDummy, "VBoxGuestControlSvc",
    4769                                               &Guest::doGuestCtrlNotification,
     4769                                              &Guest::notifyCtrlDispatcher,
    47704770                                              pConsole->getGuest());
    47714771        if (RT_FAILURE(rc))
  • trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp

    r37447 r37589  
    11971197
    11981198/**
     1199 * Adds a callback with a user provided data block and an optional progress object
     1200 * to the callback map. A callback is identified by a unique context ID which is used
     1201 * to identify a callback from the guest side.
     1202 *
     1203 * @return  IPRT status code.
     1204 * @param   puContextID
     1205 * @param   pCallbackData
     1206 */
     1207int Guest::callbackAdd(const PVBOXGUESTCTRL_CALLBACK pCallbackData, uint32_t *puContextID)
     1208{
     1209    AssertPtrReturn(pCallbackData, VERR_INVALID_PARAMETER);
     1210    AssertPtrReturn(puContextID, VERR_INVALID_PARAMETER);
     1211
     1212    int rc;
     1213
     1214    /* Create a new context ID and assign it. */
     1215    uint32_t uNewContextID = 0;
     1216    for (;;)
     1217    {
     1218        /* Create a new context ID ... */
     1219        uNewContextID = ASMAtomicIncU32(&mNextContextID);
     1220        if (uNewContextID == UINT32_MAX)
     1221            ASMAtomicUoWriteU32(&mNextContextID, 1000);
     1222        /* Is the context ID already used?  Try next ID ... */
     1223        if (!callbackExists(uNewContextID))
     1224        {
     1225            /* Callback with context ID was not found. This means
     1226             * we can use this context ID for our new callback we want
     1227             * to add below. */
     1228            rc = VINF_SUCCESS;
     1229            break;
     1230        }
     1231    }
     1232
     1233    if (RT_SUCCESS(rc))
     1234    {
     1235        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     1236
     1237        /* Add callback with new context ID to our callback map. */
     1238        mCallbackMap[uNewContextID] = *pCallbackData;
     1239        Assert(mCallbackMap.size());
     1240
     1241        /* Report back new context ID. */
     1242        *puContextID = uNewContextID;
     1243    }
     1244
     1245    return rc;
     1246}
     1247
     1248/**
     1249 * Does not do locking!
     1250 *
     1251 * @param   uContextID
     1252 */
     1253void Guest::callbackDestroy(uint32_t uContextID)
     1254{
     1255    AssertReturnVoid(uContextID);
     1256
     1257    LogFlowFunc(("Destroying callback with CID=%u ...\n", uContextID));
     1258
     1259    /* Notify callback (if necessary). */
     1260    int rc = callbackNotifyEx(uContextID, VERR_CANCELLED,
     1261                              Guest::tr("VM is shutting down, canceling uncompleted guest requests ..."));
     1262    AssertRC(rc);
     1263
     1264    CallbackMapIter it = mCallbackMap.find(uContextID);
     1265    if (it != mCallbackMap.end())
     1266    {
     1267        if (it->second.pvData)
     1268        {
     1269            callbackFreeUserData(it->second.pvData);
     1270            it->second.cbData = 0;
     1271        }
     1272
     1273        /* Remove callback context (not used anymore). */
     1274        mCallbackMap.erase(it);
     1275    }
     1276}
     1277
     1278bool Guest::callbackExists(uint32_t uContextID)
     1279{
     1280    AssertReturn(uContextID, false);
     1281
     1282    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     1283
     1284    CallbackMapIter it = mCallbackMap.find(uContextID);
     1285    return (it == mCallbackMap.end()) ? false : true;
     1286}
     1287
     1288void Guest::callbackFreeUserData(void *pvData)
     1289{
     1290    if (pvData)
     1291    {
     1292        RTMemFree(pvData);
     1293        pvData = NULL;
     1294    }
     1295}
     1296
     1297int Guest::callbackGetUserData(uint32_t uContextID, eVBoxGuestCtrlCallbackType *pEnmType,
     1298                               void **ppvData, size_t *pcbData)
     1299{
     1300    AssertReturn(uContextID, VERR_INVALID_PARAMETER);
     1301    /* pEnmType is optional. */
     1302    AssertPtrReturn(ppvData, VERR_INVALID_PARAMETER);
     1303    /* pcbData is optional. */
     1304
     1305    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     1306
     1307    CallbackMapIterConst it = mCallbackMap.find(uContextID);
     1308    if (it != mCallbackMap.end())
     1309    {
     1310        if (pEnmType)
     1311            *pEnmType = it->second.mType;
     1312
     1313        void *pvData = RTMemAlloc(it->second.cbData);
     1314        AssertPtrReturn(pvData, VERR_NO_MEMORY);
     1315        memcpy(pvData, it->second.pvData, it->second.cbData);
     1316        *ppvData = pvData;
     1317
     1318        if (pcbData)
     1319            *pcbData = it->second.cbData;
     1320
     1321        return VINF_SUCCESS;
     1322    }
     1323
     1324    return VERR_NOT_FOUND;
     1325}
     1326
     1327/* Does not do locking! Caller has to take care of it because the caller needs to
     1328 * modify the data ...*/
     1329void* Guest::callbackGetUserDataMutableRaw(uint32_t uContextID, size_t *pcbData)
     1330{
     1331    AssertReturn(uContextID, NULL);
     1332    /* pcbData is optional. */
     1333
     1334    CallbackMapIterConst it = mCallbackMap.find(uContextID);
     1335    if (it != mCallbackMap.end())
     1336    {
     1337        if (pcbData)
     1338            *pcbData = it->second.cbData;
     1339        return it->second.pvData;
     1340    }
     1341
     1342    return NULL;
     1343}
     1344
     1345bool Guest::callbackIsCanceled(uint32_t uContextID)
     1346{
     1347    AssertReturn(uContextID, true);
     1348
     1349    Progress *pProgress = NULL;
     1350    {
     1351        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     1352
     1353        CallbackMapIterConst it = mCallbackMap.find(uContextID);
     1354        if (it != mCallbackMap.end())
     1355            pProgress = it->second.pProgress;
     1356    }
     1357
     1358    if (pProgress)
     1359    {
     1360        BOOL fCanceled = FALSE;
     1361        HRESULT hRC = pProgress->COMGETTER(Canceled)(&fCanceled);
     1362        if (   SUCCEEDED(hRC)
     1363            && !fCanceled)
     1364        {
     1365            return false;
     1366        }
     1367    }
     1368
     1369    return true; /* No progress / error means canceled. */
     1370}
     1371
     1372bool Guest::callbackIsComplete(uint32_t uContextID)
     1373{
     1374    AssertReturn(uContextID, true);
     1375
     1376    Progress *pProgress = NULL;
     1377    {
     1378        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     1379
     1380        CallbackMapIterConst it = mCallbackMap.find(uContextID);
     1381        if (it != mCallbackMap.end())
     1382            pProgress = it->second.pProgress;
     1383    }
     1384
     1385    if (pProgress)
     1386    {
     1387        BOOL fCompleted = FALSE;
     1388        HRESULT hRC = pProgress->COMGETTER(Completed)(&fCompleted);
     1389        if (   SUCCEEDED(hRC)
     1390            && fCompleted)
     1391        {
     1392            return true;
     1393        }
     1394    }
     1395
     1396    return false;
     1397}
     1398
     1399int Guest::callbackMoveForward(uint32_t uContextID, const char *pszMessage)
     1400{
     1401    AssertReturn(uContextID, VERR_INVALID_PARAMETER);
     1402    AssertPtrReturn(pszMessage, VERR_INVALID_PARAMETER);
     1403
     1404    Progress *pProgress = NULL;
     1405    {
     1406        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     1407
     1408        CallbackMapIterConst it = mCallbackMap.find(uContextID);
     1409        if (it != mCallbackMap.end())
     1410            pProgress = it->second.pProgress;
     1411    }
     1412
     1413    if (pProgress)
     1414    {
     1415        HRESULT hr = pProgress->SetNextOperation(Bstr(pszMessage).raw(), 1 /* Weight */);
     1416        if (FAILED(hr))
     1417            return VERR_CANCELLED;
     1418
     1419        return VINF_SUCCESS;
     1420    }
     1421
     1422    return VERR_NOT_FOUND;
     1423}
     1424
     1425/**
     1426 * TODO
     1427 *
     1428 * @return  IPRT status code.
     1429 * @param   uContextID
     1430 * @param   iRC
     1431 * @param   pszMessage
     1432 */
     1433int Guest::callbackNotifyEx(uint32_t uContextID, int iRC, const char *pszMessage)
     1434{
     1435    AssertReturn(uContextID, VERR_INVALID_PARAMETER);
     1436
     1437    LogFlowFunc(("Notifying callback with CID=%u, iRC=%d, pszMsg=%s ...\n",
     1438                 uContextID, iRC, pszMessage ? pszMessage : "<No message given>"));
     1439
     1440    Progress *pProgress = NULL;
     1441    {
     1442        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     1443
     1444        CallbackMapIterConst it = mCallbackMap.find(uContextID);
     1445        if (it != mCallbackMap.end())
     1446            pProgress = it->second.pProgress;
     1447    }
     1448
     1449#if 0
     1450    BOOL fCanceled = FALSE;
     1451    HRESULT hRC = pProgress->COMGETTER(Canceled)(&fCanceled);
     1452    if (   SUCCEEDED(hRC)
     1453        && fCanceled)
     1454    {
     1455        /* If progress already canceled do nothing here. */
     1456        return VINF_SUCCESS;
     1457    }
     1458#endif
     1459
     1460    if (pProgress)
     1461    {
     1462        /*
     1463         * Assume we didn't complete to make sure we clean up even if the
     1464         * following call fails.
     1465         */
     1466        BOOL fCompleted = FALSE;
     1467        HRESULT hRC = pProgress->COMGETTER(Completed)(&fCompleted);
     1468        if (   SUCCEEDED(hRC)
     1469            && !fCompleted)
     1470        {
     1471            /*
     1472             * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only
     1473             * cancel won't work!). This could happen if the client thread (e.g. VBoxService, thread of a spawned process)
     1474             * is disconnecting without having the chance to sending a status message before, so we
     1475             * have to abort here to make sure the host never hangs/gets stuck while waiting for the
     1476             * progress object to become signalled.
     1477             */
     1478            if (   RT_SUCCESS(iRC)
     1479                && !pszMessage)
     1480            {
     1481                hRC = pProgress->notifyComplete(S_OK);
     1482            }
     1483            else
     1484            {
     1485                AssertPtrReturn(pszMessage, VERR_INVALID_PARAMETER);
     1486                hRC = pProgress->notifyComplete(VBOX_E_IPRT_ERROR /* Must not be S_OK. */,
     1487                                                COM_IIDOF(IGuest),
     1488                                                Guest::getStaticComponentName(),
     1489                                                pszMessage);
     1490            }
     1491        }
     1492        ComAssertComRC(hRC);
     1493
     1494        /*
     1495         * Do *not* NULL pProgress here, because waiting function like executeProcess()
     1496         * will still rely on this object for checking whether they have to give up!
     1497         */
     1498    }
     1499    /* If pProgress is not found (anymore) that's fine.
     1500     * Might be destroyed already. */
     1501    return S_OK;
     1502}
     1503
     1504/**
     1505 * TODO
     1506 *
     1507 * @return  IPRT status code.
     1508 * @param   uPID
     1509 * @param   iRC
     1510 * @param   pszMessage
     1511 */
     1512int Guest::callbackNotifyAllForPID(uint32_t uPID, int iRC, const char *pszMessage)
     1513{
     1514    AssertReturn(uPID, VERR_INVALID_PARAMETER);
     1515
     1516    int vrc = VINF_SUCCESS;
     1517
     1518    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     1519
     1520    CallbackMapIter it;
     1521    for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
     1522    {
     1523        switch (it->second.mType)
     1524        {
     1525            case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:
     1526                break;
     1527
     1528            /* When waiting for process output while the process is destroyed,
     1529             * make sure we also destroy the actual waiting operation (internal progress object)
     1530             * in order to not block the caller. */
     1531            case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
     1532            {
     1533                PCALLBACKDATAEXECOUT pItData = (PCALLBACKDATAEXECOUT)it->second.pvData;
     1534                AssertPtr(pItData);
     1535                if (pItData->u32PID == uPID)
     1536                    vrc = callbackNotifyEx(it->first, iRC, pszMessage);
     1537                break;
     1538            }
     1539
     1540            /* When waiting for injecting process input while the process is destroyed,
     1541             * make sure we also destroy the actual waiting operation (internal progress object)
     1542             * in order to not block the caller. */
     1543            case VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS:
     1544            {
     1545                PCALLBACKDATAEXECINSTATUS pItData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;
     1546                AssertPtr(pItData);
     1547                if (pItData->u32PID == uPID)
     1548                    vrc = callbackNotifyEx(it->first, iRC, pszMessage);
     1549                break;
     1550            }
     1551
     1552            default:
     1553                vrc = VERR_INVALID_PARAMETER;
     1554                AssertMsgFailed(("Unknown callback type %d, iRC=%d, message=%s\n",
     1555                                 it->second.mType, iRC, pszMessage ? pszMessage : "<No message given>"));
     1556                break;
     1557        }
     1558
     1559        if (RT_FAILURE(vrc))
     1560            break;
     1561    }
     1562
     1563    return vrc;
     1564}
     1565
     1566int Guest::callbackNotifyComplete(uint32_t uContextID)
     1567{
     1568    return callbackNotifyEx(uContextID, S_OK, NULL /* No message */);
     1569}
     1570
     1571int Guest::callbackWaitForCompletion(uint32_t uContextID, LONG lStage, LONG lTimeout)
     1572{
     1573    AssertReturn(uContextID, VERR_INVALID_PARAMETER);
     1574
     1575    /*
     1576     * Wait for the HGCM low level callback until the process
     1577     * has been started (or something went wrong). This is necessary to
     1578     * get the PID.
     1579     */
     1580
     1581    int vrc = VINF_SUCCESS;
     1582    Progress *pProgress = NULL;
     1583    {
     1584        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     1585
     1586        CallbackMapIterConst it = mCallbackMap.find(uContextID);
     1587        if (it != mCallbackMap.end())
     1588            pProgress = it->second.pProgress;
     1589        else
     1590            vrc = VERR_NOT_FOUND;
     1591    }
     1592
     1593    if (RT_SUCCESS(vrc))
     1594    {
     1595        HRESULT rc;
     1596        if (lStage < 0)
     1597            rc = pProgress->WaitForCompletion(lTimeout);
     1598        else
     1599            rc = pProgress->WaitForOperationCompletion((ULONG)lStage, lTimeout);
     1600        if (SUCCEEDED(rc))
     1601        {
     1602            if (!callbackIsComplete(uContextID))
     1603                vrc = callbackIsCanceled(uContextID)
     1604                    ? VERR_CANCELLED : VINF_SUCCESS;
     1605        }
     1606        else
     1607            vrc = VERR_TIMEOUT;
     1608    }
     1609
     1610    return vrc;
     1611}
     1612
     1613/**
    11991614 * Static callback function for receiving updates on guest control commands
    12001615 * from the guest. Acts as a dispatcher for the actual class instance.
     
    12051620 *
    12061621 */
    1207 DECLCALLBACK(int) Guest::doGuestCtrlNotification(void    *pvExtension,
    1208                                                  uint32_t u32Function,
    1209                                                  void    *pvParms,
    1210                                                  uint32_t cbParms)
     1622DECLCALLBACK(int) Guest::notifyCtrlDispatcher(void    *pvExtension,
     1623                                              uint32_t u32Function,
     1624                                              void    *pvParms,
     1625                                              uint32_t cbParms)
    12111626{
    12121627    using namespace guestControl;
     
    12861701
    12871702/* Function for handling the execution start/termination notification. */
     1703/* Callback can be called several times. */
    12881704int Guest::notifyCtrlExecStatus(uint32_t                u32Function,
    12891705                                PCALLBACKDATAEXECSTATUS pData)
    12901706{
     1707    AssertReturn(u32Function, VERR_INVALID_PARAMETER);
     1708    AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
     1709
     1710    uint32_t uContextID = pData->hdr.u32ContextID;
     1711
     1712    /* Scope write locks as much as possible. */
     1713    {
     1714        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     1715
     1716        PCALLBACKDATAEXECSTATUS pCallbackData =
     1717            (PCALLBACKDATAEXECSTATUS)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
     1718        if (pCallbackData)
     1719        {
     1720            pCallbackData->u32PID = pData->u32PID;
     1721            pCallbackData->u32Status = pData->u32Status;
     1722            pCallbackData->u32Flags = pData->u32Flags;
     1723            /** @todo Copy void* buffer contents? */
     1724        }
     1725        else
     1726            AssertReleaseMsgFailed(("Process status (PID=%u) does not have allocated callback data!\n",
     1727                                    pData->u32PID));
     1728    }
     1729
    12911730    int vrc = VINF_SUCCESS;
    1292 
    1293     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1294 
    1295     AssertPtr(pData);
    1296     CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
    1297 
    1298     /* Callback can be called several times. */
    1299     if (it != mCallbackMap.end())
    1300     {
    1301         PCALLBACKDATAEXECSTATUS pCBData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;
    1302         AssertPtr(pCBData);
    1303 
    1304         pCBData->u32PID = pData->u32PID;
    1305         pCBData->u32Status = pData->u32Status;
    1306         pCBData->u32Flags = pData->u32Flags;
    1307         /** @todo Copy void* buffer contents! */
    1308 
    1309         Utf8Str errMsg;
    1310 
    1311         /* Was progress canceled before? */
    1312         BOOL fCanceled;
    1313         ComAssert(!it->second.pProgress.isNull());
    1314         if (   SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled))
    1315             && !fCanceled)
    1316         {
    1317             /* Do progress handling. */
    1318             HRESULT hr;
    1319             switch (pData->u32Status)
     1731    Utf8Str errMsg;
     1732
     1733    /* Was progress canceled before? */
     1734    bool fCbCanceled = callbackIsCanceled(uContextID);
     1735    if (!fCbCanceled)
     1736    {
     1737        /* Do progress handling. */
     1738        switch (pData->u32Status)
     1739        {
     1740            case PROC_STS_STARTED:
     1741                vrc = callbackMoveForward(uContextID, Guest::tr("Waiting for process to exit ..."));
     1742                LogRel(("Guest process (PID %u) started\n", pData->u32PID)); /** @todo Add process name */
     1743                break;
     1744
     1745            case PROC_STS_TEN: /* Terminated normally. */
     1746                vrc = callbackNotifyComplete(uContextID);
     1747                LogRel(("Guest process (PID %u) exited normally\n", pData->u32PID)); /** @todo Add process name */
     1748                break;
     1749
     1750            case PROC_STS_TEA: /* Terminated abnormally. */
     1751                LogRel(("Guest process (PID %u) terminated abnormally with exit code = %u\n",
     1752                        pData->u32PID, pData->u32Flags)); /** @todo Add process name */
     1753                errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),
     1754                                    pData->u32Flags);
     1755                break;
     1756
     1757            case PROC_STS_TES: /* Terminated through signal. */
     1758                LogRel(("Guest process (PID %u) terminated through signal with exit code = %u\n",
     1759                        pData->u32PID, pData->u32Flags)); /** @todo Add process name */
     1760                errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),
     1761                                    pData->u32Flags);
     1762                break;
     1763
     1764            case PROC_STS_TOK:
     1765                LogRel(("Guest process (PID %u) timed out and was killed\n", pData->u32PID)); /** @todo Add process name */
     1766                errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));
     1767                break;
     1768
     1769            case PROC_STS_TOA:
     1770                LogRel(("Guest process (PID %u) timed out and could not be killed\n", pData->u32PID)); /** @todo Add process name */
     1771                errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));
     1772                break;
     1773
     1774            case PROC_STS_DWN:
     1775                LogRel(("Guest process (PID %u) killed because system is shutting down\n", pData->u32PID)); /** @todo Add process name */
     1776                /*
     1777                 * If u32Flags has ExecuteProcessFlag_IgnoreOrphanedProcesses set, we don't report an error to
     1778                 * our progress object. This is helpful for waiters which rely on the success of our progress object
     1779                 * even if the executed process was killed because the system/VBoxService is shutting down.
     1780                 *
     1781                 * In this case u32Flags contains the actual execution flags reached in via Guest::ExecuteProcess().
     1782                 */
     1783                if (pData->u32Flags & ExecuteProcessFlag_IgnoreOrphanedProcesses)
     1784                {
     1785                    vrc = callbackNotifyComplete(uContextID);
     1786                }
     1787                else
     1788                    errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down"));
     1789                break;
     1790
     1791            case PROC_STS_ERROR:
     1792                LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",
     1793                        pData->u32PID, pData->u32Flags)); /** @todo Add process name */
     1794                errMsg = Utf8StrFmt(Guest::tr("Process execution failed with rc=%Rrc"), pData->u32Flags);
     1795                break;
     1796
     1797            default:
     1798                vrc = VERR_INVALID_PARAMETER;
     1799                break;
     1800        }
     1801
     1802        /* Handle process map. */
     1803        /** @todo What happens on/deal with PID reuse? */
     1804        /** @todo How to deal with multiple updates at once? */
     1805        if (pData->u32PID)
     1806        {
     1807            VBOXGUESTCTRL_PROCESS process;
     1808            vrc = processGetByPID(pData->u32PID, &process);
     1809            if (vrc == VERR_NOT_FOUND)
    13201810            {
    1321                 case PROC_STS_STARTED:
    1322                     LogRel(("Guest process (PID %u) started\n", pCBData->u32PID)); /** @todo Add process name */
    1323                     hr = it->second.pProgress->SetNextOperation(Bstr(tr("Waiting for process to exit ...")).raw(), 1 /* Weight */);
    1324                     AssertComRC(hr);
    1325                     break;
    1326 
    1327                 case PROC_STS_TEN: /* Terminated normally. */
    1328                     LogRel(("Guest process (PID %u) exited normally\n", pCBData->u32PID)); /** @todo Add process name */
    1329                     if (!it->second.pProgress->getCompleted())
    1330                     {
    1331                         hr = it->second.pProgress->notifyComplete(S_OK);
    1332                         AssertComRC(hr);
    1333 
    1334                         LogFlowFunc(("Process (CID=%u, status=%u) terminated successfully\n",
    1335                                      pData->hdr.u32ContextID, pData->u32Status));
    1336                     }
    1337                     break;
    1338 
    1339                 case PROC_STS_TEA: /* Terminated abnormally. */
    1340                     LogRel(("Guest process (PID %u) terminated abnormally with exit code = %u\n",
    1341                             pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
    1342                     errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),
    1343                                         pCBData->u32Flags);
    1344                     break;
    1345 
    1346                 case PROC_STS_TES: /* Terminated through signal. */
    1347                     LogRel(("Guest process (PID %u) terminated through signal with exit code = %u\n",
    1348                             pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
    1349                     errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),
    1350                                         pCBData->u32Flags);
    1351                     break;
    1352 
    1353                 case PROC_STS_TOK:
    1354                     LogRel(("Guest process (PID %u) timed out and was killed\n", pCBData->u32PID)); /** @todo Add process name */
    1355                     errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));
    1356                     break;
    1357 
    1358                 case PROC_STS_TOA:
    1359                     LogRel(("Guest process (PID %u) timed out and could not be killed\n", pCBData->u32PID)); /** @todo Add process name */
    1360                     errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));
    1361                     break;
    1362 
    1363                 case PROC_STS_DWN:
    1364                     LogRel(("Guest process (PID %u) killed because system is shutting down\n", pCBData->u32PID)); /** @todo Add process name */
    1365                     /*
    1366                      * If u32Flags has ExecuteProcessFlag_IgnoreOrphanedProcesses set, we don't report an error to
    1367                      * our progress object. This is helpful for waiters which rely on the success of our progress object
    1368                      * even if the executed process was killed because the system/VBoxService is shutting down.
    1369                      *
    1370                      * In this case u32Flags contains the actual execution flags reached in via Guest::ExecuteProcess().
    1371                      */
    1372                     if (pData->u32Flags & ExecuteProcessFlag_IgnoreOrphanedProcesses)
    1373                     {
    1374                         if (!it->second.pProgress->getCompleted())
    1375                         {
    1376                             hr = it->second.pProgress->notifyComplete(S_OK);
    1377                             AssertComRC(hr);
    1378                         }
    1379                     }
    1380                     else
    1381                     {
    1382                         errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down"));
    1383                     }
    1384                     break;
    1385 
    1386                 case PROC_STS_ERROR:
    1387                     LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",
    1388                             pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
    1389                     errMsg = Utf8StrFmt(Guest::tr("Process execution failed with rc=%Rrc"), pCBData->u32Flags);
    1390                     break;
    1391 
    1392                 default:
    1393                     vrc = VERR_INVALID_PARAMETER;
    1394                     break;
     1811                /* Not found, add to map. */
     1812                vrc = processAdd(pData->u32PID,
     1813                                 (ExecuteProcessStatus_T)pData->u32Status,
     1814                                 pData->u32Flags /* Contains exit code. */,
     1815                                 0 /*Flags. */);
     1816                AssertRC(vrc);
    13951817            }
    1396 
    1397             /* Handle process map. */
    1398             /** @todo What happens on/deal with PID reuse? */
    1399             /** @todo How to deal with multiple updates at once? */
    1400             if (pCBData->u32PID > 0)
     1818            else if (RT_SUCCESS(vrc))
    14011819            {
    1402                 GuestProcessMapIter it_proc = getProcessByPID(pCBData->u32PID);
    1403                 if (it_proc == mGuestProcessMap.end())
    1404                 {
    1405                     /* Not found, add to map. */
    1406                     GuestProcess newProcess;
    1407                     newProcess.mStatus = (ExecuteProcessStatus_T)pCBData->u32Status;
    1408                     newProcess.mExitCode = pCBData->u32Flags; /* Contains exit code. */
    1409                     newProcess.mFlags = 0;
    1410 
    1411                     mGuestProcessMap[pCBData->u32PID] = newProcess;
    1412                 }
    1413                 else /* Update map. */
    1414                 {
    1415                     it_proc->second.mStatus = (ExecuteProcessStatus_T)pCBData->u32Status;
    1416                     it_proc->second.mExitCode = pCBData->u32Flags; /* Contains exit code. */
    1417                     it_proc->second.mFlags = 0;
    1418                 }
     1820                /* Process found, update process map. */
     1821                vrc = processSetStatus(pData->u32PID,
     1822                                       (ExecuteProcessStatus_T)pData->u32Status,
     1823                                       pData->u32Flags /* Contains exit code. */,
     1824                                       0 /*Flags. */);
     1825                AssertRC(vrc);
    14191826            }
    1420         }
    1421         else
    1422             errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));
    1423 
    1424         if (!it->second.pProgress->getCompleted())
    1425         {
    1426             if (   errMsg.length()
    1427                 || fCanceled) /* If canceled we have to report E_FAIL! */
     1827            else
     1828                AssertReleaseMsgFailed(("Process was neither found nor absent!?\n"));
     1829        }
     1830    }
     1831    else
     1832        errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));
     1833
     1834    if (!callbackIsComplete(uContextID))
     1835    {
     1836        if (   errMsg.length()
     1837            || fCbCanceled) /* If canceled we have to report E_FAIL! */
     1838        {
     1839            /* Notify all callbacks which are still waiting on something
     1840             * which is related to the current PID. */
     1841            if (pData->u32PID)
    14281842            {
    1429                 /* Destroy all callbacks which are still waiting on something
    1430                  * which is related to the current PID. */
    1431                 CallbackMapIter it2;
    1432                 for (it2 = mCallbackMap.begin(); it2 != mCallbackMap.end(); it2++)
    1433                 {
    1434                     switch (it2->second.mType)
    1435                     {
    1436                         case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:
    1437                             break;
    1438 
    1439                         /* When waiting for process output while the process is destroyed,
    1440                          * make sure we also destroy the actual waiting operation (internal progress object)
    1441                          * in order to not block the caller. */
    1442                         case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
    1443                         {
    1444                             PCALLBACKDATAEXECOUT pItData = (PCALLBACKDATAEXECOUT)it2->second.pvData;
    1445                             AssertPtr(pItData);
    1446                             if (pItData->u32PID == pCBData->u32PID)
    1447                                 notifyCtrlCallbackContext(it2, errMsg.c_str());
    1448                             break;
    1449                         }
    1450 
    1451                         /* When waiting for injecting process input while the process is destroyed,
    1452                          * make sure we also destroy the actual waiting operation (internal progress object)
    1453                          * in order to not block the caller. */
    1454                         case VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS:
    1455                         {
    1456                             PCALLBACKDATAEXECINSTATUS pItData = (PCALLBACKDATAEXECINSTATUS)it2->second.pvData;
    1457                             AssertPtr(pItData);
    1458                             if (pItData->u32PID == pCBData->u32PID)
    1459                                 notifyCtrlCallbackContext(it2, errMsg.c_str());
    1460                             break;
    1461                         }
    1462 
    1463                         default:
    1464                             AssertMsgFailed(("Unknown callback type %d\n", it2->second.mType));
    1465                             break;
    1466                     }
    1467                 }
    1468 
    1469                 /* Let the caller know what went wrong ... */
    1470                 notifyCtrlCallbackContext(it, errMsg.c_str());
    1471 
    1472                 LogFlowFunc(("Process (CID=%u, status=%u) reported error: %s\n",
    1473                              pData->hdr.u32ContextID, pData->u32Status, errMsg.c_str()));
     1843                vrc = callbackNotifyAllForPID(pData->u32PID, VERR_GENERAL_FAILURE, errMsg.c_str());
     1844                if (RT_FAILURE(vrc))
     1845                    LogFlowFunc(("Failed to notify other callbacks for PID=%u\n",
     1846                                 pData->u32PID));
    14741847            }
    1475         }
    1476     }
    1477     else
    1478         LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
     1848
     1849            /* Let the caller know what went wrong ... */
     1850            int rc2 = callbackNotifyEx(uContextID, VERR_GENERAL_FAILURE, errMsg.c_str());
     1851            if (RT_FAILURE(rc2))
     1852            {
     1853                LogFlowFunc(("Failed to notify callback CID=%u for PID=%u\n",
     1854                             uContextID, pData->u32PID));
     1855
     1856                if (RT_SUCCESS(vrc))
     1857                    vrc = rc2;
     1858            }
     1859            LogFlowFunc(("Process (CID=%u, status=%u) reported error: %s\n",
     1860                         uContextID, pData->u32Status, errMsg.c_str()));
     1861        }
     1862    }
    14791863    LogFlowFunc(("Returned with rc=%Rrc\n", vrc));
    14801864    return vrc;
     
    14851869                             PCALLBACKDATAEXECOUT pData)
    14861870{
    1487     int rc = VINF_SUCCESS;
    1488 
    1489     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1490 
    1491     AssertPtr(pData);
    1492     CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
    1493     if (it != mCallbackMap.end())
    1494     {
    1495         PCALLBACKDATAEXECOUT pCBData = (PCALLBACKDATAEXECOUT)it->second.pvData;
    1496         AssertPtr(pCBData);
    1497 
    1498         pCBData->u32PID = pData->u32PID;
    1499         pCBData->u32HandleId = pData->u32HandleId;
    1500         pCBData->u32Flags = pData->u32Flags;
    1501 
    1502         /* Make sure we really got something! */
    1503         if (   pData->cbData
    1504             && pData->pvData)
    1505         {
    1506             /* Allocate data buffer and copy it */
    1507             pCBData->pvData = RTMemAlloc(pData->cbData);
    1508             pCBData->cbData = pData->cbData;
    1509 
    1510             AssertReturn(pCBData->pvData, VERR_NO_MEMORY);
    1511             memcpy(pCBData->pvData, pData->pvData, pData->cbData);
     1871    AssertReturn(u32Function, VERR_INVALID_PARAMETER);
     1872    AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
     1873
     1874    uint32_t uContextID = pData->hdr.u32ContextID;
     1875    Assert(uContextID);
     1876
     1877    /* Scope write locks as much as possible. */
     1878    {
     1879        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     1880
     1881        PCALLBACKDATAEXECOUT pCallbackData =
     1882            (PCALLBACKDATAEXECOUT)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
     1883        if (pCallbackData)
     1884        {
     1885            pCallbackData->u32PID = pData->u32PID;
     1886            pCallbackData->u32HandleId = pData->u32HandleId;
     1887            pCallbackData->u32Flags = pData->u32Flags;
     1888
     1889            /* Make sure we really got something! */
     1890            if (   pData->cbData
     1891                && pData->pvData)
     1892            {
     1893                callbackFreeUserData(pCallbackData->pvData);
     1894
     1895                /* Allocate data buffer and copy it */
     1896                pCallbackData->pvData = RTMemAlloc(pData->cbData);
     1897                pCallbackData->cbData = pData->cbData;
     1898
     1899                AssertReturn(pCallbackData->pvData, VERR_NO_MEMORY);
     1900                memcpy(pCallbackData->pvData, pData->pvData, pData->cbData);
     1901            }
     1902            else /* Nothing received ... */
     1903            {
     1904                pCallbackData->pvData = NULL;
     1905                pCallbackData->cbData = 0;
     1906            }
    15121907        }
    15131908        else
    1514         {
    1515             pCBData->pvData = NULL;
    1516             pCBData->cbData = 0;
    1517         }
    1518 
    1519         /* Was progress canceled before? */
    1520         BOOL fCanceled;
    1521         ComAssert(!it->second.pProgress.isNull());
    1522         if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && fCanceled)
    1523         {
    1524             it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
    1525                                                  COM_IIDOF(IGuest),
    1526                                                  Guest::getStaticComponentName(),
    1527                                                  Guest::tr("The output operation was canceled"));
    1528         }
    1529         else
    1530         {
    1531             BOOL fCompleted;
    1532             if (   SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
    1533                 && !fCompleted)
    1534             {
    1535                 /* If we previously got completed notification, don't trigger again. */
    1536                 it->second.pProgress->notifyComplete(S_OK);
    1537             }
    1538         }
     1909            AssertReleaseMsgFailed(("Process output status (PID=%u) does not have allocated callback data!\n",
     1910                                    pData->u32PID));
     1911    }
     1912
     1913    int vrc;
     1914    if (callbackIsCanceled(pData->u32PID))
     1915    {
     1916        vrc = callbackNotifyEx(uContextID, VERR_CANCELLED,
     1917                               Guest::tr("The output operation was canceled"));
    15391918    }
    15401919    else
    1541         LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
    1542     return rc;
     1920        vrc = callbackNotifyComplete(uContextID);
     1921
     1922    return vrc;
    15431923}
    15441924
     
    15471927                                  PCALLBACKDATAEXECINSTATUS pData)
    15481928{
    1549     int rc = VINF_SUCCESS;
    1550 
    1551     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1552 
    1553     AssertPtr(pData);
    1554     CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
    1555     if (it != mCallbackMap.end())
    1556     {
    1557         PCALLBACKDATAEXECINSTATUS pCBData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;
    1558         AssertPtr(pCBData);
    1559 
    1560         /* Save bytes processed. */
    1561         pCBData->cbProcessed = pData->cbProcessed;
    1562         pCBData->u32Status = pData->u32Status;
    1563         pCBData->u32Flags = pData->u32Flags;
    1564         pCBData->u32PID = pData->u32PID;
    1565 
    1566         /* Only trigger completion once. */
    1567         BOOL fCompleted;
    1568         if (   SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
    1569             && !fCompleted)
    1570         {
    1571             it->second.pProgress->notifyComplete(S_OK);
    1572         }
    1573     }
    1574     else
    1575         LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
    1576     return rc;
     1929    AssertReturn(u32Function, VERR_INVALID_PARAMETER);
     1930    AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
     1931
     1932    uint32_t uContextID = pData->hdr.u32ContextID;
     1933    Assert(uContextID);
     1934
     1935    /* Scope write locks as much as possible. */
     1936    {
     1937        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     1938
     1939        PCALLBACKDATAEXECINSTATUS pCallbackData =
     1940            (PCALLBACKDATAEXECINSTATUS)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
     1941        if (pCallbackData)
     1942        {
     1943            /* Save bytes processed. */
     1944            pCallbackData->cbProcessed = pData->cbProcessed;
     1945            pCallbackData->u32Status = pData->u32Status;
     1946            pCallbackData->u32Flags = pData->u32Flags;
     1947            pCallbackData->u32PID = pData->u32PID;
     1948        }
     1949        else
     1950            AssertReleaseMsgFailed(("Process input status (PID=%u) does not have allocated callback data!\n",
     1951                                    pData->u32PID));
     1952    }
     1953
     1954    return callbackNotifyComplete(uContextID);
    15771955}
    15781956
     
    15801958                                        PCALLBACKDATACLIENTDISCONNECTED pData)
    15811959{
    1582     int rc = VINF_SUCCESS;
     1960    /* u32Function is 0. */
     1961    AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
     1962
     1963    uint32_t uContextID = pData->hdr.u32ContextID;
     1964    Assert(uContextID);
     1965
     1966    return callbackNotifyEx(uContextID, S_OK,
     1967                            Guest::tr("Client disconnected"));
     1968}
     1969
     1970int Guest::processAdd(uint32_t u32PID, ExecuteProcessStatus_T enmStatus,
     1971                      uint32_t uExitCode, uint32_t uFlags)
     1972{
     1973    AssertReturn(u32PID, VERR_INVALID_PARAMETER);
    15831974
    15841975    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1585     CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
    1586     if (it != mCallbackMap.end())
    1587     {
    1588         LogFlowFunc(("Client with CID=%u disconnected\n", it->first));
    1589         notifyCtrlCallbackContext(it, Guest::tr("Client disconnected"));
    1590     }
    1591     return rc;
    1592 }
    1593 
    1594 Guest::CallbackMapIter Guest::getCtrlCallbackContextByID(uint32_t u32ContextID)
    1595 {
     1976
     1977    GuestProcessMapIterConst it = mGuestProcessMap.find(u32PID);
     1978    if (it == mGuestProcessMap.end())
     1979    {
     1980        VBOXGUESTCTRL_PROCESS process;
     1981
     1982        process.mStatus = enmStatus;
     1983        process.mExitCode = uExitCode;
     1984        process.mFlags = uFlags;
     1985
     1986        mGuestProcessMap[u32PID] = process;
     1987
     1988        return VINF_SUCCESS;
     1989    }
     1990
     1991    return VERR_ALREADY_EXISTS;
     1992}
     1993
     1994int Guest::processGetByPID(uint32_t u32PID, PVBOXGUESTCTRL_PROCESS pProcess)
     1995{
     1996    AssertReturn(u32PID, VERR_INVALID_PARAMETER);
     1997    AssertPtrReturn(pProcess, VERR_INVALID_PARAMETER);
     1998
    15961999    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1597     return mCallbackMap.find(u32ContextID);
    1598 }
    1599 
    1600 Guest::GuestProcessMapIter Guest::getProcessByPID(uint32_t u32PID)
    1601 {
     2000
     2001    GuestProcessMapIterConst it = mGuestProcessMap.find(u32PID);
     2002    if (it != mGuestProcessMap.end())
     2003    {
     2004        pProcess->mStatus = it->second.mStatus;
     2005        pProcess->mExitCode = it->second.mExitCode;
     2006        pProcess->mFlags = it->second.mFlags;
     2007
     2008        return VINF_SUCCESS;
     2009    }
     2010
     2011    return VERR_NOT_FOUND;
     2012}
     2013
     2014int Guest::processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags)
     2015{
     2016    AssertReturn(u32PID, VERR_INVALID_PARAMETER);
     2017
    16022018    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1603     return mGuestProcessMap.find(u32PID);
    1604 }
    1605 
    1606 /* No locking here; */
    1607 void Guest::destroyCtrlCallbackContext(Guest::CallbackMapIter it)
    1608 {
    1609     LogFlowFunc(("Destroying callback with CID=%u ...\n", it->first));
    1610 
    1611     if (it->second.pvData)
    1612     {
    1613         RTMemFree(it->second.pvData);
    1614         it->second.pvData = NULL;
    1615         it->second.cbData = 0;
    1616     }
    1617 
    1618     /* Remove callback context (not used anymore). */
    1619     mCallbackMap.erase(it);
    1620 }
    1621 
    1622 /* No locking here; */
    1623 void Guest::notifyCtrlCallbackContext(Guest::CallbackMapIter it, const char *pszText)
    1624 {
    1625     AssertPtr(pszText);
    1626     LogFlowFunc(("Handling callback with CID=%u ...\n", it->first));
    1627 
    1628     /* Notify outstanding waits for progress ... */
    1629     if (    it->second.pProgress
    1630          && !it->second.pProgress.isNull())
    1631     {
    1632         LogFlowFunc(("Notifying progress for CID=%u (Reason: %s) ...\n",
    1633                      it->first, pszText));
    1634 
    1635         /*
    1636          * Assume we didn't complete to make sure we clean up even if the
    1637          * following call fails.
    1638          */
    1639         BOOL fCompleted = FALSE;
    1640         it->second.pProgress->COMGETTER(Completed)(&fCompleted);
    1641         if (!fCompleted)
    1642         {
    1643             /*
    1644              * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only
    1645              * cancel won't work!). This could happen if the client thread (e.g. VBoxService, thread of a spawned process)
    1646              * is disconnecting without having the chance to sending a status message before, so we
    1647              * have to abort here to make sure the host never hangs/gets stuck while waiting for the
    1648              * progress object to become signalled.
    1649              */
    1650             it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
    1651                                                  COM_IIDOF(IGuest),
    1652                                                  Guest::getStaticComponentName(),
    1653                                                  pszText);
    1654         }
    1655         /*
    1656          * Do *not* NULL pProgress here, because waiting function like executeProcess()
    1657          * will still rely on this object for checking whether they have to give up!
    1658          */
    1659     }
    1660 }
    1661 
    1662 /* Adds a callback with a user provided data block and an optional progress object
    1663  * to the callback map. A callback is identified by a unique context ID which is used
    1664  * to identify a callback from the guest side. */
    1665 uint32_t Guest::addCtrlCallbackContext(eVBoxGuestCtrlCallbackType enmType, void *pvData, uint32_t cbData, Progress *pProgress)
    1666 {
    1667     AssertPtr(pProgress);
    1668 
    1669     /** @todo Put this stuff into a constructor! */
    1670     CallbackContext context;
    1671     context.mType = enmType;
    1672     context.pvData = pvData;
    1673     context.cbData = cbData;
    1674     context.pProgress = pProgress;
    1675 
    1676     /* Create a new context ID and assign it. */
    1677     CallbackMapIter it;
    1678     uint32_t uNewContext = 0;
    1679     do
    1680     {
    1681         /* Create a new context ID ... */
    1682         uNewContext = ASMAtomicIncU32(&mNextContextID);
    1683         if (uNewContext == UINT32_MAX)
    1684             ASMAtomicUoWriteU32(&mNextContextID, 1000);
    1685         /* Is the context ID already used? */
    1686         it = getCtrlCallbackContextByID(uNewContext);
    1687     } while(it != mCallbackMap.end());
    1688 
    1689     uint32_t nCallbacks = 0;
    1690     if (   it == mCallbackMap.end()
    1691         && uNewContext > 0)
    1692     {
    1693         /* We apparently got an unused context ID, let's use it! */
    1694         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1695         mCallbackMap[uNewContext] = context;
    1696         nCallbacks = mCallbackMap.size();
    1697     }
     2019
     2020    GuestProcessMapIter it = mGuestProcessMap.find(u32PID);
     2021    if (it != mGuestProcessMap.end())
     2022    {
     2023        it->second.mStatus = enmStatus;
     2024        it->second.mExitCode = uExitCode;
     2025        it->second.mFlags = uFlags;
     2026
     2027        return VINF_SUCCESS;
     2028    }
     2029
     2030    return VERR_NOT_FOUND;
     2031}
     2032
     2033HRESULT Guest::handleErrorCompletion(int rc)
     2034{
     2035    HRESULT hRC;
     2036    if (rc == VERR_NOT_FOUND)
     2037        hRC = setErrorNoLog(VBOX_E_VM_ERROR,
     2038                            tr("VMM device is not available (is the VM running?)"));
     2039    else if (rc == VERR_CANCELLED)
     2040        hRC = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2041                            tr("Process execution has been canceled"));
     2042    else if (rc == VERR_TIMEOUT)
     2043        hRC= setErrorNoLog(VBOX_E_IPRT_ERROR,
     2044                            tr("The guest did not respond within time"));
    16982045    else
    1699     {
    1700         /* Should never happen ... */
    1701         {
    1702             AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1703             nCallbacks = mCallbackMap.size();
    1704         }
    1705         AssertReleaseMsg(uNewContext, ("No free context ID found! uNewContext=%u, nCallbacks=%u", uNewContext, nCallbacks));
    1706     }
    1707 
    1708 #if 0
    1709     if (nCallbacks > 256) /* Don't let the container size get too big! */
    1710     {
    1711         Guest::CallbackListIter it = mCallbackList.begin();
    1712         destroyCtrlCallbackContext(it);
    1713         {
    1714             AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    1715             mCallbackList.erase(it);
    1716         }
    1717     }
    1718 #endif
    1719     return uNewContext;
     2046        hRC = setErrorNoLog(E_UNEXPECTED,
     2047                            tr("Waiting for completion failed with error %Rrc"), rc);
     2048    return hRC;
     2049}
     2050
     2051HRESULT Guest::handleErrorHGCM(int rc)
     2052{
     2053    HRESULT hRC;
     2054    if (rc == VERR_INVALID_VM_HANDLE)
     2055        hRC = setErrorNoLog(VBOX_E_VM_ERROR,
     2056                            tr("VMM device is not available (is the VM running?)"));
     2057    else if (rc == VERR_NOT_FOUND)
     2058        hRC = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2059                            tr("The guest execution service is not ready (yet)"));
     2060    else if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
     2061        hRC= setErrorNoLog(VBOX_E_IPRT_ERROR,
     2062                            tr("The guest execution service is not available"));
     2063    else /* HGCM call went wrong. */
     2064        hRC = setErrorNoLog(E_UNEXPECTED,
     2065                            tr("The HGCM call failed with error %Rrc"), rc);
     2066    return hRC;
    17202067}
    17212068
     
    17512098    } while(*pRetStatus == ExecuteProcessStatus_Started && SUCCEEDED(hRC));
    17522099    return hRC;
     2100}
     2101
     2102HRESULT Guest::executeProcessResult(const char *pszCommand, const char *pszUser, ULONG ulTimeout,
     2103                                    PCALLBACKDATAEXECSTATUS pExecStatus, ULONG *puPID)
     2104{
     2105    AssertPtrReturn(pExecStatus, E_INVALIDARG);
     2106    AssertPtrReturn(puPID, E_INVALIDARG);
     2107
     2108    HRESULT rc = S_OK;
     2109
     2110    /* Did we get some status? */
     2111    switch (pExecStatus->u32Status)
     2112    {
     2113        case PROC_STS_STARTED:
     2114            /* Process is (still) running; get PID. */
     2115            *puPID = pExecStatus->u32PID;
     2116            break;
     2117
     2118        /* In any other case the process either already
     2119         * terminated or something else went wrong, so no PID ... */
     2120        case PROC_STS_TEN: /* Terminated normally. */
     2121        case PROC_STS_TEA: /* Terminated abnormally. */
     2122        case PROC_STS_TES: /* Terminated through signal. */
     2123        case PROC_STS_TOK:
     2124        case PROC_STS_TOA:
     2125        case PROC_STS_DWN:
     2126            /*
     2127             * Process (already) ended, but we want to get the
     2128             * PID anyway to retrieve the output in a later call.
     2129             */
     2130            *puPID = pExecStatus->u32PID;
     2131            break;
     2132
     2133        case PROC_STS_ERROR:
     2134            {
     2135                int vrc = pExecStatus->u32Flags; /* u32Flags member contains IPRT error code. */
     2136                if (vrc == VERR_FILE_NOT_FOUND) /* This is the most likely error. */
     2137                    rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2138                                       tr("The file '%s' was not found on guest"), pszCommand);
     2139                else if (vrc == VERR_PATH_NOT_FOUND)
     2140                    rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2141                                       tr("The path to file '%s' was not found on guest"), pszCommand);
     2142                else if (vrc == VERR_BAD_EXE_FORMAT)
     2143                    rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2144                                       tr("The file '%s' is not an executable format on guest"), pszCommand);
     2145                else if (vrc == VERR_AUTHENTICATION_FAILURE)
     2146                    rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2147                                       tr("The specified user '%s' was not able to logon on guest"), pszUser);
     2148                else if (vrc == VERR_TIMEOUT)
     2149                    rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2150                                       tr("The guest did not respond within time (%ums)"), ulTimeout);
     2151                else if (vrc == VERR_CANCELLED)
     2152                    rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2153                                       tr("The execution operation was canceled"));
     2154                else if (vrc == VERR_PERMISSION_DENIED)
     2155                    rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2156                                       tr("Invalid user/password credentials"));
     2157                else
     2158                {
     2159                    if (pExecStatus && pExecStatus->u32Status == PROC_STS_ERROR)
     2160                        rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2161                                           tr("Process could not be started: %Rrc"), pExecStatus->u32Flags);
     2162                    else
     2163                        rc = setErrorNoLog(E_UNEXPECTED,
     2164                                           tr("The service call failed with error %Rrc"), vrc);
     2165                }
     2166            }
     2167            break;
     2168
     2169        case PROC_STS_UNDEFINED: /* . */
     2170            rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2171                               tr("The operation did not complete within time"));
     2172            break;
     2173
     2174        default:
     2175            AssertReleaseMsgFailed(("Process (PID %u) reported back an undefined state!\n",
     2176                                    pExecStatus->u32PID));
     2177            rc = E_UNEXPECTED;
     2178            break;
     2179    }
     2180
     2181    return rc;
    17532182}
    17542183#endif /* VBOX_WITH_GUEST_CONTROL */
     
    18252254         * occurred.
    18262255         */
    1827         ComObjPtr <Progress> progress;
    1828         rc = progress.createObject();
     2256        ComObjPtr <Progress> pProgress;
     2257        rc = pProgress.createObject();
    18292258        if (SUCCEEDED(rc))
    18302259        {
    1831             rc = progress->init(static_cast<IGuest*>(this),
    1832                                 Bstr(tr("Executing process")).raw(),
    1833                                 TRUE,
    1834                                 2,                                          /* Number of operations. */
    1835                                 Bstr(tr("Starting process ...")).raw());    /* Description of first stage. */
     2260            rc = pProgress->init(static_cast<IGuest*>(this),
     2261                                 Bstr(tr("Executing process")).raw(),
     2262                                 TRUE,
     2263                                 2,                                          /* Number of operations. */
     2264                                 Bstr(tr("Starting process ...")).raw());    /* Description of first stage. */
    18362265        }
    18372266        ComAssertComRC(rc);
     
    18932322                if (RT_SUCCESS(vrc))
    18942323                {
    1895                     PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));
    1896                     AssertReturn(pData, VBOX_E_IPRT_ERROR);
    1897                     RT_ZERO(*pData);
    1898                     uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START,
    1899                                                         pData, sizeof(CALLBACKDATAEXECSTATUS), progress);
    1900                     Assert(uContextID > 0);
    1901 
    1902                     VBOXHGCMSVCPARM paParms[15];
    1903                     int i = 0;
    1904                     paParms[i++].setUInt32(uContextID);
    1905                     paParms[i++].setPointer((void*)Utf8Command.c_str(), (uint32_t)Utf8Command.length() + 1);
    1906                     paParms[i++].setUInt32(aFlags);
    1907                     paParms[i++].setUInt32(uNumArgs);
    1908                     paParms[i++].setPointer((void*)pszArgs, cbArgs);
    1909                     paParms[i++].setUInt32(uNumEnv);
    1910                     paParms[i++].setUInt32(cbEnv);
    1911                     paParms[i++].setPointer((void*)pvEnv, cbEnv);
    1912                     paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);
    1913                     paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);
    1914 
    1915                     /*
    1916                      * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
    1917                      * until the process was started - the process itself then gets an infinite timeout for execution.
    1918                      * This is handy when we want to start a process inside a worker thread within a certain timeout
    1919                      * but let the started process perform lengthly operations then.
    1920                      */
    1921                     if (aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)
    1922                         paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
    1923                     else
    1924                         paParms[i++].setUInt32(aTimeoutMS);
    1925 
    1926                     VMMDev *pVMMDev = NULL;
     2324                    /* Allocate payload. */
     2325                    PCALLBACKDATAEXECSTATUS pStatus = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));
     2326                    AssertReturn(pStatus, VBOX_E_IPRT_ERROR);
     2327                    RT_ZERO(*pStatus);
     2328
     2329                    /* Create callback. */
     2330                    VBOXGUESTCTRL_CALLBACK callback;
     2331                    callback.mType  = VBOXGUESTCTRLCALLBACKTYPE_EXEC_START;
     2332                    callback.cbData = sizeof(CALLBACKDATAEXECSTATUS);
     2333                    callback.pvData = pStatus;
     2334                    callback.pProgress = pProgress;
     2335
     2336                    vrc = callbackAdd(&callback, &uContextID);
     2337                    if (RT_SUCCESS(vrc))
    19272338                    {
    1928                         /* Make sure mParent is valid, so set the read lock while using.
    1929                          * Do not keep this lock while doing the actual call, because in the meanwhile
    1930                          * another thread could request a write lock which would be a bad idea ... */
    1931                         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1932 
    1933                         /* Forward the information to the VMM device. */
    1934                         AssertPtr(mParent);
    1935                         pVMMDev = mParent->getVMMDev();
     2339                        VBOXHGCMSVCPARM paParms[15];
     2340                        int i = 0;
     2341                        paParms[i++].setUInt32(uContextID);
     2342                        paParms[i++].setPointer((void*)Utf8Command.c_str(), (uint32_t)Utf8Command.length() + 1);
     2343                        paParms[i++].setUInt32(aFlags);
     2344                        paParms[i++].setUInt32(uNumArgs);
     2345                        paParms[i++].setPointer((void*)pszArgs, cbArgs);
     2346                        paParms[i++].setUInt32(uNumEnv);
     2347                        paParms[i++].setUInt32(cbEnv);
     2348                        paParms[i++].setPointer((void*)pvEnv, cbEnv);
     2349                        paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);
     2350                        paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);
     2351
     2352                        /*
     2353                         * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
     2354                         * until the process was started - the process itself then gets an infinite timeout for execution.
     2355                         * This is handy when we want to start a process inside a worker thread within a certain timeout
     2356                         * but let the started process perform lengthly operations then.
     2357                         */
     2358                        if (aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)
     2359                            paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
     2360                        else
     2361                            paParms[i++].setUInt32(aTimeoutMS);
     2362
     2363                        VMMDev *pVMMDev = NULL;
     2364                        {
     2365                            /* Make sure mParent is valid, so set the read lock while using.
     2366                             * Do not keep this lock while doing the actual call, because in the meanwhile
     2367                             * another thread could request a write lock which would be a bad idea ... */
     2368                            AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     2369
     2370                            /* Forward the information to the VMM device. */
     2371                            AssertPtr(mParent);
     2372                            pVMMDev = mParent->getVMMDev();
     2373                        }
     2374
     2375                        if (pVMMDev)
     2376                        {
     2377                            LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
     2378                            vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
     2379                                                       i, paParms);
     2380                        }
     2381                        else
     2382                            vrc = VERR_INVALID_VM_HANDLE;
    19362383                    }
    1937 
    1938                     if (pVMMDev)
    1939                     {
    1940                         LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
    1941                         vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
    1942                                                    i, paParms);
    1943                     }
    1944                     else
    1945                         vrc = VERR_INVALID_VM_HANDLE;
    19462384                    RTMemFree(pvEnv);
    19472385                }
    19482386                RTStrFree(pszArgs);
    19492387            }
     2388
    19502389            if (RT_SUCCESS(vrc))
    19512390            {
     
    19572396                 * get the PID.
    19582397                 */
    1959                 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
    1960                 BOOL fCanceled = FALSE;
    1961                 if (it != mCallbackMap.end())
     2398
     2399                PCALLBACKDATAEXECSTATUS pExecStatus = NULL;
     2400
     2401                 /*
     2402                 * Wait for the first stage (=0) to complete (that is starting the process).
     2403                 */
     2404                vrc = callbackWaitForCompletion(uContextID, 0 /* Stage */, aTimeoutMS);
     2405                if (RT_SUCCESS(vrc))
    19622406                {
    1963                     ComAssert(!it->second.pProgress.isNull());
    1964 
    1965                     /*
    1966                      * Wait for the first stage (=0) to complete (that is starting the process).
    1967                      */
    1968                     PCALLBACKDATAEXECSTATUS pData = NULL;
    1969                     rc = it->second.pProgress->WaitForOperationCompletion(0, aTimeoutMS);
    1970                     if (SUCCEEDED(rc))
     2407                    vrc = callbackGetUserData(uContextID, NULL /* We know the type. */,
     2408                                              (void**)&pExecStatus, NULL /* Don't need the size. */);
     2409                    if (RT_SUCCESS(vrc))
    19712410                    {
    1972                         /* Was the operation canceled by one of the parties? */
    1973                         rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
    1974                         if (FAILED(rc)) throw rc;
    1975 
    1976                         if (!fCanceled)
    1977                         {
    1978                             AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    1979 
    1980                             pData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;
    1981                             Assert(it->second.cbData == sizeof(CALLBACKDATAEXECSTATUS));
    1982                             AssertPtr(pData);
    1983 
    1984                             /* Did we get some status? */
    1985                             switch (pData->u32Status)
    1986                             {
    1987                                 case PROC_STS_STARTED:
    1988                                     /* Process is (still) running; get PID. */
    1989                                     *aPID = pData->u32PID;
    1990                                     break;
    1991 
    1992                                 /* In any other case the process either already
    1993                                  * terminated or something else went wrong, so no PID ... */
    1994                                 case PROC_STS_TEN: /* Terminated normally. */
    1995                                 case PROC_STS_TEA: /* Terminated abnormally. */
    1996                                 case PROC_STS_TES: /* Terminated through signal. */
    1997                                 case PROC_STS_TOK:
    1998                                 case PROC_STS_TOA:
    1999                                 case PROC_STS_DWN:
    2000                                     /*
    2001                                      * Process (already) ended, but we want to get the
    2002                                      * PID anyway to retrieve the output in a later call.
    2003                                      */
    2004                                     *aPID = pData->u32PID;
    2005                                     break;
    2006 
    2007                                 case PROC_STS_ERROR:
    2008                                     vrc = pData->u32Flags; /* u32Flags member contains IPRT error code. */
    2009                                     break;
    2010 
    2011                                 case PROC_STS_UNDEFINED:
    2012                                     vrc = VERR_TIMEOUT;    /* Operation did not complete within time. */
    2013                                     break;
    2014 
    2015                                 default:
    2016                                     vrc = VERR_INVALID_PARAMETER; /* Unknown status, should never happen! */
    2017                                     break;
    2018                             }
    2019                         }
    2020                         else /* Operation was canceled. */
    2021                             vrc = VERR_CANCELLED;
     2411                        rc = executeProcessResult(Utf8Command.c_str(), Utf8UserName.c_str(), aTimeoutMS,
     2412                                                  pExecStatus, aPID);
     2413                        callbackFreeUserData(pExecStatus);
    20222414                    }
    2023                     else /* Operation did not complete within time. */
    2024                         vrc = VERR_TIMEOUT;
    2025 
    2026                     /*
    2027                      * Do *not* remove the callback yet - we might wait with the IProgress object on something
    2028                      * else (like end of process) ...
    2029                      */
    2030                     if (RT_FAILURE(vrc))
     2415                    else
    20312416                    {
    2032                         if (vrc == VERR_FILE_NOT_FOUND) /* This is the most likely error. */
    2033                             rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
    2034                                                tr("The file '%s' was not found on guest"), Utf8Command.c_str());
    2035                         else if (vrc == VERR_PATH_NOT_FOUND)
    2036                             rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
    2037                                                tr("The path to file '%s' was not found on guest"), Utf8Command.c_str());
    2038                         else if (vrc == VERR_BAD_EXE_FORMAT)
    2039                             rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
    2040                                                tr("The file '%s' is not an executable format on guest"), Utf8Command.c_str());
    2041                         else if (vrc == VERR_AUTHENTICATION_FAILURE)
    2042                             rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
    2043                                                tr("The specified user '%s' was not able to logon on guest"), Utf8UserName.c_str());
    2044                         else if (vrc == VERR_TIMEOUT)
    2045                             rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
    2046                                                tr("The guest did not respond within time (%ums)"), aTimeoutMS);
    2047                         else if (vrc == VERR_CANCELLED)
    2048                             rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
    2049                                                tr("The execution operation was canceled"));
    2050                         else if (vrc == VERR_PERMISSION_DENIED)
    2051                             rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
    2052                                                tr("Invalid user/password credentials"));
    2053                         else
    2054                         {
    2055                             if (pData && pData->u32Status == PROC_STS_ERROR)
    2056                                 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
    2057                                                    tr("Process could not be started: %Rrc"), pData->u32Flags);
    2058                             else
    2059                                 rc = setErrorNoLog(E_UNEXPECTED,
    2060                                                    tr("The service call failed with error %Rrc"), vrc);
    2061                         }
    2062                     }
    2063                     else /* Execution went fine. */
    2064                     {
    2065                         /* Return the progress to the caller. */
    2066                         progress.queryInterfaceTo(aProgress);
     2417                        rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2418                                           tr("Unable to retrieve process execution status data"));
    20672419                    }
    20682420                }
    2069                 else /* Callback context not found; should never happen! */
    2070                     AssertMsg(it != mCallbackMap.end(), ("Callback context with ID %u not found!", uContextID));
     2421                else
     2422                    rc = handleErrorCompletion(vrc);
     2423
     2424                /*
     2425                 * Do *not* remove the callback yet - we might wait with the IProgress object on something
     2426                 * else (like end of process) ...
     2427                 */
    20712428            }
    2072             else /* HGCM related error codes .*/
    2073             {
    2074                 if (vrc == VERR_INVALID_VM_HANDLE)
    2075                     rc = setErrorNoLog(VBOX_E_VM_ERROR,
    2076                                        tr("VMM device is not available (is the VM running?)"));
    2077                 else if (vrc == VERR_NOT_FOUND)
    2078                     rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
    2079                                        tr("The guest execution service is not ready"));
    2080                 else if (vrc == VERR_HGCM_SERVICE_NOT_FOUND)
    2081                     rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
    2082                                        tr("The guest execution service is not available"));
    2083                 else /* HGCM call went wrong. */
    2084                     rc = setErrorNoLog(E_UNEXPECTED,
    2085                                        tr("The HGCM call failed with error %Rrc"), vrc);
    2086             }
     2429            else
     2430                rc = handleErrorHGCM(vrc);
    20872431
    20882432            for (unsigned i = 0; i < uNumArgs; i++)
     
    20912435        }
    20922436
    2093         if (RT_FAILURE(vrc))
     2437        if (SUCCEEDED(rc))
     2438        {
     2439            /* Return the progress to the caller. */
     2440            pProgress.queryInterfaceTo(aProgress);
     2441        }
     2442        else
    20942443        {
    20952444            if (!pRC) /* Skip logging internal calls. */
     
    21332482    try
    21342483    {
    2135         /* Init. */
    2136         *aBytesWritten = 0;
    2137 
    2138         {
    2139             /* Take read lock to prevent races. */
    2140             AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2141 
    2142             /* Search for existing PID. */
    2143             GuestProcessMapIterConst itProc = getProcessByPID(aPID);
    2144             if (itProc != mGuestProcessMap.end())
    2145             {
    2146                 /* PID exists; check if process is still running. */
    2147                 if (itProc->second.mStatus != ExecuteProcessStatus_Started)
    2148                     rc = setError(VBOX_E_IPRT_ERROR,
    2149                                   Guest::tr("Cannot inject input to not running process (PID %u)"), aPID);
    2150             }
    2151             else
     2484        VBOXGUESTCTRL_PROCESS process;
     2485        int vrc = processGetByPID(aPID, &process);
     2486        if (RT_SUCCESS(vrc))
     2487        {
     2488            /* PID exists; check if process is still running. */
     2489            if (process.mStatus != ExecuteProcessStatus_Started)
    21522490                rc = setError(VBOX_E_IPRT_ERROR,
    2153                               Guest::tr("Cannot inject input to non-existent process (PID %u)"), aPID);
    2154         }
     2491                              Guest::tr("Cannot inject input to not running process (PID %u)"), aPID);
     2492        }
     2493        else
     2494            rc = setError(VBOX_E_IPRT_ERROR,
     2495                          Guest::tr("Cannot inject input to non-existent process (PID %u)"), aPID);
    21552496
    21562497        if (SUCCEEDED(rc))
    21572498        {
     2499            uint32_t uContextID = 0;
     2500
    21582501            /*
    21592502             * Create progress object.
     
    21772520                aTimeoutMS = UINT32_MAX;
    21782521
    2179             PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECINSTATUS));
    2180             if (NULL == pData) throw rc;
    2181             AssertReturn(pData, VBOX_E_IPRT_ERROR);
    2182             RT_ZERO(*pData);
     2522            /* Construct callback data. */
     2523            VBOXGUESTCTRL_CALLBACK callback;
     2524            callback.mType  = VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS;
     2525            callback.cbData = sizeof(CALLBACKDATAEXECINSTATUS);
     2526
     2527            PCALLBACKDATAEXECINSTATUS pStatus = (PCALLBACKDATAEXECINSTATUS)RTMemAlloc(callback.cbData);
     2528            AssertReturn(pStatus, VBOX_E_IPRT_ERROR);
     2529            RT_ZERO(*pStatus);
    21832530
    21842531            /* Save PID + output flags for later use. */
    2185             pData->u32PID = aPID;
    2186             pData->u32Flags = aFlags;
    2187 
    2188             /* Add job to callback contexts. */
    2189             uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS,
    2190                                                          pData, sizeof(CALLBACKDATAEXECINSTATUS), pProgress);
    2191             Assert(uContextID > 0);
    2192 
    2193             com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(aData));
    2194             uint32_t cbSize = sfaData.size();
    2195 
    2196             VBOXHGCMSVCPARM paParms[6];
    2197             int i = 0;
    2198             paParms[i++].setUInt32(uContextID);
    2199             paParms[i++].setUInt32(aPID);
    2200             paParms[i++].setUInt32(aFlags);
    2201             paParms[i++].setPointer(sfaData.raw(), cbSize);
    2202             paParms[i++].setUInt32(cbSize);
    2203 
    2204             int vrc = VINF_SUCCESS;
    2205 
     2532            pStatus->u32PID = aPID;
     2533            pStatus->u32Flags = aFlags;
     2534
     2535            callback.pvData = pStatus;
     2536            callback.pProgress = pProgress;
     2537
     2538            /* Add the callback. */
     2539            vrc = callbackAdd(&callback, &uContextID);
     2540            if (RT_SUCCESS(vrc))
    22062541            {
     2542                com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(aData));
     2543                uint32_t cbSize = sfaData.size();
     2544
     2545                VBOXHGCMSVCPARM paParms[6];
     2546                int i = 0;
     2547                paParms[i++].setUInt32(uContextID);
     2548                paParms[i++].setUInt32(aPID);
     2549                paParms[i++].setUInt32(aFlags);
     2550                paParms[i++].setPointer(sfaData.raw(), cbSize);
     2551                paParms[i++].setUInt32(cbSize);
     2552
     2553                {
     2554                    VMMDev *pVMMDev = NULL;
     2555                    {
     2556                        /* Make sure mParent is valid, so set the read lock while using.
     2557                         * Do not keep this lock while doing the actual call, because in the meanwhile
     2558                         * another thread could request a write lock which would be a bad idea ... */
     2559                        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     2560
     2561                        /* Forward the information to the VMM device. */
     2562                        AssertPtr(mParent);
     2563                        pVMMDev = mParent->getVMMDev();
     2564                    }
     2565
     2566                    if (pVMMDev)
     2567                    {
     2568                        LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
     2569                        vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_SET_INPUT,
     2570                                                   i, paParms);
     2571                    }
     2572                }
     2573            }
     2574
     2575            if (RT_SUCCESS(vrc))
     2576            {
     2577                LogFlowFunc(("Waiting for HGCM callback ...\n"));
     2578
     2579                /*
     2580                 * Wait for the HGCM low level callback until the process
     2581                 * has been started (or something went wrong). This is necessary to
     2582                 * get the PID.
     2583                 */
     2584
     2585                PCALLBACKDATAEXECINSTATUS pExecStatusIn = NULL;
     2586
     2587                 /*
     2588                 * Wait for the first stage (=0) to complete (that is starting the process).
     2589                 */
     2590                vrc = callbackWaitForCompletion(uContextID, 0 /* Stage */, aTimeoutMS);
     2591                if (RT_SUCCESS(vrc))
     2592                {
     2593                    vrc = callbackGetUserData(uContextID, NULL /* We know the type. */,
     2594                                              (void**)&pExecStatusIn, NULL /* Don't need the size. */);
     2595                    if (RT_SUCCESS(vrc))
     2596                    {
     2597                        switch (pExecStatusIn->u32Status)
     2598                        {
     2599                            case INPUT_STS_WRITTEN:
     2600                                *aBytesWritten = pExecStatusIn->cbProcessed;
     2601                                break;
     2602
     2603                            default:
     2604                                rc = setError(VBOX_E_IPRT_ERROR,
     2605                                              tr("Client error %u while processing input data"), pExecStatusIn->u32Status);
     2606                                break;
     2607                        }
     2608
     2609                        callbackFreeUserData(pExecStatusIn);
     2610                    }
     2611                    else
     2612                    {
     2613                        rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2614                                           tr("Unable to retrieve process input status data"));
     2615                    }
     2616                }
     2617                else
     2618                    rc = handleErrorCompletion(vrc);
     2619            }
     2620            else
     2621                rc = handleErrorHGCM(vrc);
     2622
     2623            if (SUCCEEDED(rc))
     2624            {
     2625                /* Nothing to do here yet. */
     2626            }
     2627
     2628            /* The callback isn't needed anymore -- just was kept locally. */
     2629            callbackDestroy(uContextID);
     2630
     2631            /* Cleanup. */
     2632            if (!pProgress.isNull())
     2633                pProgress->uninit();
     2634            pProgress.setNull();
     2635        }
     2636    }
     2637    catch (std::bad_alloc &)
     2638    {
     2639        rc = E_OUTOFMEMORY;
     2640    }
     2641    return rc;
     2642#endif
     2643}
     2644
     2645STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
     2646{
     2647/** @todo r=bird: Eventually we should clean up all the timeout parameters
     2648 *        in the API and have the same way of specifying infinite waits!  */
     2649#ifndef VBOX_WITH_GUEST_CONTROL
     2650    ReturnComNotImplemented();
     2651#else  /* VBOX_WITH_GUEST_CONTROL */
     2652    using namespace guestControl;
     2653
     2654    CheckComArgExpr(aPID, aPID > 0);
     2655    if (aSize < 0)
     2656        return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
     2657    if (aSize == 0)
     2658        return setError(E_INVALIDARG, tr("The size (%lld) is zero"), aSize);
     2659    if (aFlags)
     2660    {
     2661        if (!(aFlags & ProcessOutputFlag_StdErr))
     2662        {
     2663            return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
     2664        }
     2665    }
     2666
     2667    AutoCaller autoCaller(this);
     2668    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     2669
     2670    HRESULT rc = S_OK;
     2671
     2672    try
     2673    {
     2674        VBOXGUESTCTRL_PROCESS process;
     2675        int vrc = processGetByPID(aPID, &process);
     2676        if (RT_FAILURE(vrc))
     2677            rc = setError(VBOX_E_IPRT_ERROR,
     2678                          Guest::tr("Cannot get output from non-existent process (PID %u)"), aPID);
     2679
     2680        if (SUCCEEDED(rc))
     2681        {
     2682            uint32_t uContextID = 0;
     2683
     2684            /*
     2685             * Create progress object.
     2686             * This progress object, compared to the one in executeProgress() above,
     2687             * is only single-stage local and is used to determine whether the operation
     2688             * finished or got canceled.
     2689             */
     2690            ComObjPtr <Progress> pProgress;
     2691            rc = pProgress.createObject();
     2692            if (SUCCEEDED(rc))
     2693            {
     2694                rc = pProgress->init(static_cast<IGuest*>(this),
     2695                                     Bstr(tr("Setting input for process")).raw(),
     2696                                     TRUE /* Cancelable */);
     2697            }
     2698            if (FAILED(rc)) throw rc;
     2699            ComAssert(!pProgress.isNull());
     2700
     2701            /* Adjust timeout. */
     2702            if (aTimeoutMS == 0)
     2703                aTimeoutMS = UINT32_MAX;
     2704
     2705            /* Set handle ID. */
     2706            uint32_t uHandleID = OUTPUT_HANDLE_ID_STDOUT; /* Default */
     2707            if (aFlags & ProcessOutputFlag_StdErr)
     2708                uHandleID = OUTPUT_HANDLE_ID_STDERR;
     2709
     2710            /* Construct callback data. */
     2711            VBOXGUESTCTRL_CALLBACK callback;
     2712            callback.mType  = VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT;
     2713            callback.cbData = sizeof(CALLBACKDATAEXECOUT);
     2714
     2715            PCALLBACKDATAEXECOUT pStatus = (PCALLBACKDATAEXECOUT)RTMemAlloc(callback.cbData);
     2716            AssertReturn(pStatus, VBOX_E_IPRT_ERROR);
     2717            RT_ZERO(*pStatus);
     2718
     2719            /* Save PID + output flags for later use. */
     2720            pStatus->u32PID = aPID;
     2721            pStatus->u32Flags = aFlags;
     2722
     2723            callback.pvData = pStatus;
     2724            callback.pProgress = pProgress;
     2725
     2726            /* Add the callback. */
     2727            vrc = callbackAdd(&callback, &uContextID);
     2728            if (RT_SUCCESS(vrc))
     2729            {
     2730                VBOXHGCMSVCPARM paParms[5];
     2731                int i = 0;
     2732                paParms[i++].setUInt32(uContextID);
     2733                paParms[i++].setUInt32(aPID);
     2734                paParms[i++].setUInt32(uHandleID);
     2735                paParms[i++].setUInt32(0 /* Flags, none set yet */);
     2736
    22072737                VMMDev *pVMMDev = NULL;
    22082738                {
     
    22202750                {
    22212751                    LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
    2222                     vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_SET_INPUT,
     2752                    vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,
    22232753                                               i, paParms);
    22242754                }
     
    22272757            if (RT_SUCCESS(vrc))
    22282758            {
    2229                 LogFlowFunc(("Waiting for HGCM callback ...\n"));
     2759                LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
    22302760
    22312761                /*
     
    22342764                 * get the PID.
    22352765                 */
    2236                 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
    2237                 BOOL fCanceled = FALSE;
    2238                 if (it != mCallbackMap.end())
     2766
     2767                PCALLBACKDATAEXECOUT pExecOut = NULL;
     2768
     2769                 /*
     2770                 * Wait for the first stage (=0) to complete (that is starting the process).
     2771                 */
     2772                vrc = callbackWaitForCompletion(uContextID, 0 /* Stage */, aTimeoutMS);
     2773                if (RT_SUCCESS(vrc))
    22392774                {
    2240                     ComAssert(!it->second.pProgress.isNull());
    2241 
    2242                     /* Wait until operation completed. */
    2243                     rc = it->second.pProgress->WaitForCompletion(aTimeoutMS);
    2244                     if (FAILED(rc)) throw rc;
    2245 
    2246                     /* Was the operation canceled by one of the parties? */
    2247                     rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
    2248                     if (FAILED(rc)) throw rc;
    2249 
    2250                     if (!fCanceled)
     2775                    vrc = callbackGetUserData(uContextID, NULL /* We know the type. */,
     2776                                              (void**)&pExecOut, NULL /* Don't need the size. */);
     2777                    if (RT_SUCCESS(vrc))
    22512778                    {
    2252                         BOOL fCompleted;
    2253                         if (   SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
    2254                             && fCompleted)
     2779                        com::SafeArray<BYTE> outputData((size_t)aSize);
     2780
     2781                        if (pExecOut->cbData)
    22552782                        {
    2256                             AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2257 
    2258                             PCALLBACKDATAEXECINSTATUS pStatusData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;
    2259                             AssertPtr(pStatusData);
    2260                             Assert(it->second.cbData == sizeof(CALLBACKDATAEXECINSTATUS));
    2261 
    2262                             switch (pStatusData->u32Status)
    2263                             {
    2264                                 case INPUT_STS_WRITTEN:
    2265                                     *aBytesWritten = pStatusData->cbProcessed;
    2266                                     break;
    2267 
    2268                                 default:
    2269                                     rc = setError(VBOX_E_IPRT_ERROR,
    2270                                                   tr("Client error %u while processing input data"), pStatusData->u32Status);
    2271                                     break;
    2272                             }
     2783                            /* Do we need to resize the array? */
     2784                            if (pExecOut->cbData > aSize)
     2785                                outputData.resize(pExecOut->cbData);
     2786
     2787                            /* Fill output in supplied out buffer. */
     2788                            memcpy(outputData.raw(), pExecOut->pvData, pExecOut->cbData);
     2789                            outputData.resize(pExecOut->cbData); /* Shrink to fit actual buffer size. */
    22732790                        }
    22742791                        else
    2275                             rc = setError(VBOX_E_IPRT_ERROR,
    2276                                           tr("The input operation was not acknowledged from guest within time (%ums)"), aTimeoutMS);
     2792                        {
     2793                            /* No data within specified timeout available. */
     2794                            outputData.resize(0);
     2795                        }
     2796
     2797                        /* Detach output buffer to output argument. */
     2798                        outputData.detachTo(ComSafeArrayOutArg(aData));
     2799
     2800                        callbackFreeUserData(pExecOut);
    22772801                    }
    22782802                    else
    2279                         rc = setError(VBOX_E_IPRT_ERROR,
    2280                                       tr("The input operation was canceled by the guest"));
    22812803                    {
    2282                         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2283                         /* Destroy locally used progress object. */
    2284                         destroyCtrlCallbackContext(it);
     2804                        rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
     2805                                           tr("Unable to retrieve process output data"));
    22852806                    }
    22862807                }
    2287                 else /* PID lookup failed. */
    2288                     rc = setError(VBOX_E_IPRT_ERROR,
    2289                                   tr("Process (PID %u) not found"), aPID);
     2808                else
     2809                    rc = handleErrorCompletion(vrc);
    22902810            }
    2291             else /* HGCM operation failed. */
    2292                 rc = setError(E_UNEXPECTED,
    2293                               tr("The HGCM call failed (%Rrc)"), vrc);
     2811            else
     2812                rc = handleErrorHGCM(vrc);
     2813
     2814            if (SUCCEEDED(rc))
     2815            {
     2816
     2817            }
     2818
     2819            /* The callback isn't needed anymore -- just was kept locally. */
     2820            callbackDestroy(uContextID);
    22942821
    22952822            /* Cleanup. */
     
    22982825            pProgress.setNull();
    22992826        }
    2300     }
    2301     catch (std::bad_alloc &)
    2302     {
    2303         rc = E_OUTOFMEMORY;
    2304     }
    2305     return rc;
    2306 #endif
    2307 }
    2308 
    2309 STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
    2310 {
    2311 /** @todo r=bird: Eventually we should clean up all the timeout parameters
    2312  *        in the API and have the same way of specifying infinite waits!  */
    2313 #ifndef VBOX_WITH_GUEST_CONTROL
    2314     ReturnComNotImplemented();
    2315 #else  /* VBOX_WITH_GUEST_CONTROL */
    2316     using namespace guestControl;
    2317 
    2318     CheckComArgExpr(aPID, aPID > 0);
    2319     if (aSize < 0)
    2320         return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
    2321     if (aFlags)
    2322     {
    2323         if (!(aFlags & ProcessOutputFlag_StdErr))
    2324         {
    2325             return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
    2326         }
    2327     }
    2328 
    2329     AutoCaller autoCaller(this);
    2330     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2331 
    2332     HRESULT rc = S_OK;
    2333 
    2334     try
    2335     {
    2336         /*
    2337          * Create progress object.
    2338          * This progress object, compared to the one in executeProgress() above
    2339          * is only local and is used to determine whether the operation finished
    2340          * or got canceled.
    2341          */
    2342         ComObjPtr <Progress> progress;
    2343         rc = progress.createObject();
    2344         if (SUCCEEDED(rc))
    2345         {
    2346             rc = progress->init(static_cast<IGuest*>(this),
    2347                                 Bstr(tr("Getting output of process")).raw(),
    2348                                 TRUE /* Cancelable */);
    2349         }
    2350         if (FAILED(rc)) return rc;
    2351 
    2352         /* Adjust timeout. */
    2353         if (aTimeoutMS == 0)
    2354             aTimeoutMS = UINT32_MAX;
    2355         /* Set handle ID. */
    2356         uint32_t uHandleID = OUTPUT_HANDLE_ID_STDOUT; /* Default */
    2357         if (aFlags & ProcessOutputFlag_StdErr)
    2358             uHandleID = OUTPUT_HANDLE_ID_STDERR;
    2359 
    2360         /* Search for existing PID. */
    2361         PCALLBACKDATAEXECOUT pData = (CALLBACKDATAEXECOUT*)RTMemAlloc(sizeof(CALLBACKDATAEXECOUT));
    2362         AssertReturn(pData, VBOX_E_IPRT_ERROR);
    2363         RT_ZERO(*pData);
    2364         /* Save PID + output flags for later use. */
    2365         pData->u32PID = aPID;
    2366         pData->u32Flags = aFlags;
    2367         /* Add job to callback contexts. */
    2368         uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT,
    2369                                                      pData, sizeof(CALLBACKDATAEXECOUT), progress);
    2370         Assert(uContextID > 0);
    2371 
    2372         VBOXHGCMSVCPARM paParms[5];
    2373         int i = 0;
    2374         paParms[i++].setUInt32(uContextID);
    2375         paParms[i++].setUInt32(aPID);
    2376         paParms[i++].setUInt32(uHandleID);
    2377         paParms[i++].setUInt32(0 /* Flags, none set yet */);
    2378 
    2379         com::SafeArray<BYTE> outputData((size_t)aSize);
    2380         int vrc = VINF_SUCCESS;
    2381 
    2382         {
    2383             VMMDev *pVMMDev = NULL;
    2384             {
    2385                 /* Make sure mParent is valid, so set the read lock while using.
    2386                  * Do not keep this lock while doing the actual call, because in the meanwhile
    2387                  * another thread could request a write lock which would be a bad idea ... */
    2388                 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2389 
    2390                 /* Forward the information to the VMM device. */
    2391                 AssertPtr(mParent);
    2392                 pVMMDev = mParent->getVMMDev();
    2393             }
    2394 
    2395             if (pVMMDev)
    2396             {
    2397                 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
    2398                 vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,
    2399                                            i, paParms);
    2400             }
    2401         }
    2402 
    2403         if (RT_SUCCESS(vrc))
    2404         {
    2405             LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
    2406 
    2407             /*
    2408              * Wait for the HGCM low level callback until the process
    2409              * has been started (or something went wrong). This is necessary to
    2410              * get the PID.
    2411              */
    2412             CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
    2413             BOOL fCanceled = FALSE;
    2414             if (it != mCallbackMap.end())
    2415             {
    2416                 ComAssert(!it->second.pProgress.isNull());
    2417 
    2418                 /* Wait until operation completed. */
    2419                 rc = it->second.pProgress->WaitForCompletion(aTimeoutMS);
    2420                 if (FAILED(rc)) throw rc;
    2421 
    2422                 /* Was the operation canceled by one of the parties? */
    2423                 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
    2424                 if (FAILED(rc)) throw rc;
    2425 
    2426                 if (!fCanceled)
    2427                 {
    2428                     BOOL fCompleted;
    2429                     if (   SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
    2430                         && fCompleted)
    2431                     {
    2432                         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2433 
    2434                         /* Did we get some output? */
    2435                         pData = (PCALLBACKDATAEXECOUT)it->second.pvData;
    2436                         Assert(it->second.cbData == sizeof(CALLBACKDATAEXECOUT));
    2437                         AssertPtr(pData);
    2438 
    2439                         if (pData->cbData)
    2440                         {
    2441                             /* Do we need to resize the array? */
    2442                             if (pData->cbData > aSize)
    2443                                 outputData.resize(pData->cbData);
    2444 
    2445                             /* Fill output in supplied out buffer. */
    2446                             memcpy(outputData.raw(), pData->pvData, pData->cbData);
    2447                             outputData.resize(pData->cbData); /* Shrink to fit actual buffer size. */
    2448                         }
    2449                         else
    2450                         {
    2451                             /* No data within specified timeout available. Use a special
    2452                              * error so that we can gently handle that case a bit below. */
    2453                             vrc = VERR_NO_DATA;
    2454                         }
    2455                     }
    2456                     else /* If callback not called within time ... well, that's a timeout! */
    2457                         vrc = VERR_TIMEOUT;
    2458                 }
    2459                 else /* Operation was canceled. */
    2460                 {
    2461                     vrc = VERR_CANCELLED;
    2462                 }
    2463 
    2464                 if (RT_FAILURE(vrc))
    2465                 {
    2466                     if (vrc == VERR_NO_DATA)
    2467                     {
    2468                         /* If there was no output data then this is no error we want
    2469                          * to report to COM. The caller just gets back a size of 0 (zero). */
    2470                         rc = S_OK;
    2471                     }
    2472                     else if (vrc == VERR_TIMEOUT)
    2473                     {
    2474                         rc = setError(VBOX_E_IPRT_ERROR,
    2475                                       tr("The guest did not output within time (%ums)"), aTimeoutMS);
    2476                     }
    2477                     else if (vrc == VERR_CANCELLED)
    2478                     {
    2479                         rc = setError(VBOX_E_IPRT_ERROR,
    2480                                       tr("The output operation was canceled"));
    2481                     }
    2482                     else
    2483                     {
    2484                         rc = setError(E_UNEXPECTED,
    2485                                       tr("The service call failed with error %Rrc"), vrc);
    2486                     }
    2487                 }
    2488 
    2489                 {
    2490                     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2491                     /* Destroy locally used progress object. */
    2492                     destroyCtrlCallbackContext(it);
    2493                 }
    2494             }
    2495             else /* PID lookup failed. */
    2496                 rc = setError(VBOX_E_IPRT_ERROR,
    2497                               tr("Process (PID %u) not found!"), aPID);
    2498         }
    2499         else /* HGCM operation failed. */
    2500             rc = setError(E_UNEXPECTED,
    2501                           tr("The HGCM call failed with error %Rrc"), vrc);
    2502 
    2503         /* Cleanup. */
    2504         progress->uninit();
    2505         progress.setNull();
    2506 
    2507         /* If something failed (or there simply was no data, indicated by VERR_NO_DATA,
    2508          * we return an empty array so that the frontend knows when to give up. */
    2509         if (RT_FAILURE(vrc) || FAILED(rc))
    2510             outputData.resize(0);
    2511         outputData.detachTo(ComSafeArrayOutArg(aData));
    25122827    }
    25132828    catch (std::bad_alloc &)
     
    25352850    try
    25362851    {
    2537         AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2538 
    2539         GuestProcessMapIterConst it = getProcessByPID(aPID);
    2540         if (it != mGuestProcessMap.end())
    2541         {
    2542             *aExitCode = it->second.mExitCode;
    2543             *aFlags = it->second.mFlags;
    2544             *aStatus = it->second.mStatus;
     2852        VBOXGUESTCTRL_PROCESS process;
     2853        int vrc = processGetByPID(aPID, &process);
     2854        if (RT_SUCCESS(vrc))
     2855        {
     2856            *aExitCode = process.mExitCode;
     2857            *aFlags = process.mFlags;
     2858            *aStatus = process.mStatus;
    25452859        }
    25462860        else
     
    28363150                              tr("Guest directory creation was aborted"));
    28373151            else
    2838                 AssertReleaseMsgFailed(("Guest directory creation neither completed nor canceled!?"));
     3152                AssertReleaseMsgFailed(("Guest directory creation neither completed nor canceled!?\n"));
    28393153        }
    28403154    }
     
    28563170    using namespace guestControl;
    28573171
    2858     CheckComArgStrNotEmptyOrNull(aDirectory);
    2859     CheckComArgStrNotEmptyOrNull(aUserName);
    2860     CheckComArgStrNotEmptyOrNull(aPassword);
    2861     CheckComArgNotNull(aHandle);
    2862 
    2863     AutoCaller autoCaller(this);
    2864     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2865 
    2866     /* Validate flags. */
    2867     if (aFlags != DirectoryOpenFlag_None)
    2868         return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
    2869 
    2870     HRESULT rc = S_OK;
    2871     try
    2872     {
    2873         Utf8Str Utf8Dir(aDirectory);
    2874         Utf8Str UtfFilter(aFilter);
    2875         Utf8Str Utf8UserName(aUserName);
    2876         Utf8Str Utf8Password(aPassword);
    2877 
    2878         /*
    2879          * Create progress object.
    2880          * This progress object, compared to the one in executeProgress() above
    2881          * is only local and is used to determine whether the operation finished
    2882          * or got canceled.
    2883          */
    2884         ComObjPtr <Progress> progress;
    2885         rc = progress.createObject();
    2886         if (SUCCEEDED(rc))
    2887         {
    2888             rc = progress->init(static_cast<IGuest*>(this),
    2889                                 Bstr(tr("Getting output of process")).raw(),
    2890                                 TRUE /* Cancelable */);
    2891         }
    2892         if (FAILED(rc)) return rc;
    2893 
    2894         PCALLBACKDATADIROPEN pData = (PCALLBACKDATADIROPEN)RTMemAlloc(sizeof(CALLBACKDATADIROPEN));
    2895         AssertReturn(pData, VBOX_E_IPRT_ERROR);
    2896         RT_ZERO(*pData);
    2897         uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START,
    2898                                                      pData, sizeof(CALLBACKDATADIROPEN), progress);
    2899         Assert(uContextID > 0);
    2900 
    2901         VBOXHGCMSVCPARM paParms[8];
    2902         int i = 0;
    2903         paParms[i++].setUInt32(uContextID);
    2904         paParms[i++].setPointer((void*)Utf8Dir.c_str(), (uint32_t)Utf8Dir.length() + 1);
    2905         paParms[i++].setPointer((void*)UtfFilter.c_str(), (uint32_t)UtfFilter.length() + 1);
    2906         paParms[i++].setUInt32(0 /* Flags, none set yet */);
    2907         paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);
    2908         paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);
    2909 
    2910         int vrc = VINF_SUCCESS;
    2911         {
    2912             VMMDev *pVMMDev = NULL;
    2913             {
    2914                 /* Make sure mParent is valid, so set the read lock while using.
    2915                  * Do not keep this lock while doing the actual call, because in the meanwhile
    2916                  * another thread could request a write lock which would be a bad idea ... */
    2917                 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2918 
    2919                 /* Forward the information to the VMM device. */
    2920                 AssertPtr(mParent);
    2921                 pVMMDev = mParent->getVMMDev();
    2922             }
    2923 
    2924             if (pVMMDev)
    2925             {
    2926                 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
    2927                 vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_DIR_OPEN,
    2928                                             i, paParms);
    2929             }
    2930         }
    2931 
    2932         if (RT_SUCCESS(vrc))
    2933         {
    2934             /*
    2935              * Wait for the HGCM low level callback until the process
    2936              * has been started (or something went wrong). This is necessary to
    2937              * get the PID.
    2938              */
    2939             CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
    2940             Assert(it != mCallbackMap.end());
    2941             ComAssert(!it->second.pProgress.isNull());
    2942 
    2943             BOOL fCanceled = FALSE;
    2944 
    2945             /* Wait until operation completed. */
    2946             rc = it->second.pProgress->WaitForCompletion(10 * 1000);
    2947             if (FAILED(rc)) throw rc;
    2948 
    2949             /* Was the operation canceled by one of the parties? */
    2950             rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
    2951             if (FAILED(rc)) throw rc;
    2952 
    2953             if (!fCanceled)
    2954             {
    2955                 BOOL fCompleted;
    2956                 if (   SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
    2957                     && fCompleted)
    2958                 {
    2959                     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    2960 
    2961                     /* Did we get some output? */
    2962                     pData = (PCALLBACKDATADIROPEN)it->second.pvData;
    2963                     Assert(it->second.cbData == sizeof(PCALLBACKDATADIROPEN));
    2964                     AssertPtr(pData);
    2965                 }
    2966                 else /* If callback not called within time ... well, that's a timeout! */
    2967                     vrc = VERR_TIMEOUT;
    2968             }
    2969             else /* Operation was canceled. */
    2970             {
    2971                 vrc = VERR_CANCELLED;
    2972             }
    2973 
    2974             if (RT_FAILURE(vrc))
    2975             {
    2976                 if (vrc == VERR_NO_DATA)
    2977                 {
    2978                     /* If there was no output data then this is no error we want
    2979                      * to report to COM. The caller just gets back a size of 0 (zero). */
    2980                     rc = S_OK;
    2981                 }
    2982                 else if (vrc == VERR_TIMEOUT)
    2983                 {
    2984                     rc = setError(VBOX_E_IPRT_ERROR,
    2985                                   tr("The guest did not output within time"));
    2986                 }
    2987                 else if (vrc == VERR_CANCELLED)
    2988                 {
    2989                     rc = setError(VBOX_E_IPRT_ERROR,
    2990                                   tr("The output operation was canceled"));
    2991                 }
    2992                 else
    2993                 {
    2994                     rc = setError(E_UNEXPECTED,
    2995                                   tr("The service call failed with error %Rrc"), vrc);
    2996                 }
    2997             }
    2998 
    2999             {
    3000                 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3001                 /* Destroy locally used progress object. */
    3002                 destroyCtrlCallbackContext(it);
    3003             }
    3004         }
    3005         else /* HGCM operation failed. */
    3006             rc = setError(E_UNEXPECTED,
    3007                           tr("The HGCM call failed with error %Rrc"), vrc);
    3008 
    3009         /* Cleanup. */
    3010         progress->uninit();
    3011         progress.setNull();
    3012     }
    3013     catch (std::bad_alloc &)
    3014     {
    3015         rc = E_OUTOFMEMORY;
    3016     }
    3017     return rc;
     3172    return VBOX_E_NOT_SUPPORTED;
    30183173#endif
    30193174}
  • trunk/src/VBox/Main/src-client/GuestImpl.cpp

    r36891 r37589  
    123123        CallbackMapIter it;
    124124        for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
    125             destroyCtrlCallbackContext(it);
     125            callbackDestroy(it->first);
    126126
    127127        /* Clear process map. */
Note: See TracChangeset for help on using the changeset viewer.

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