Changeset 37589 in vbox
- Timestamp:
- Jun 22, 2011 1:20:06 PM (14 years ago)
- svn:sync-xref-src-repo-rev:
- 72433
- Location:
- trunk
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/iprt/process.h
r37447 r37589 199 199 #define RTPROC_FLAGS_SAME_CONTRACT RT_BIT(3) 200 200 /** 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. */ 202 202 #define RTPROC_FLAGS_NO_PROFILE RT_BIT(4) 203 203 /** @} */ -
trunk/src/VBox/Main/include/GuestImpl.h
r37375 r37589 130 130 # ifdef VBOX_WITH_GUEST_CONTROL 131 131 /** 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); 133 133 # endif 134 134 static HRESULT setErrorStatic(HRESULT aResultCode, … … 146 146 HRESULT taskUpdateGuestAdditions(TaskGuest *aTask); 147 147 148 // Internal callback context handling.149 struct CallbackContext148 // Internal guest callback representation. 149 typedef struct VBOXGUESTCTRL_CALLBACK 150 150 { 151 151 eVBoxGuestCtrlCallbackType mType; … … 156 156 /** Pointer to user-supplied IProgress. */ 157 157 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 184 177 int notifyCtrlClientDisconnected(uint32_t u32Function, PCALLBACKDATACLIENTDISCONNECTED pData); 185 178 int notifyCtrlExecStatus(uint32_t u32Function, PCALLBACKDATAEXECSTATUS pData); 186 179 int notifyCtrlExecOut(uint32_t u32Function, PCALLBACKDATAEXECOUT pData); 187 180 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 193 208 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); 194 210 # endif 195 211 … … 220 236 /** General extension callback for guest control. */ 221 237 HGCMSVCEXTHANDLE mhExtCtrl; 222 238 /** Next upcoming context ID. */ 223 239 volatile uint32_t mNextContextID; 224 CallbackMap mCallbackMap;225 GuestProcessMap mGuestProcessMap;240 CallbackMap mCallbackMap; 241 GuestProcessMap mGuestProcessMap; 226 242 # endif 227 243 }; -
trunk/src/VBox/Main/src-client/ConsoleImpl2.cpp
r37471 r37589 2424 2424 2425 2425 parm.setUInt32(!useHostClipboard()); 2426 2426 2427 2427 pVMMDev->hgcmHostCall("VBoxSharedClipboard", VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, 1, &parm); 2428 2428 … … 4767 4767 HGCMSVCEXTHANDLE hDummy; 4768 4768 rc = HGCMHostRegisterServiceExtension(&hDummy, "VBoxGuestControlSvc", 4769 &Guest:: doGuestCtrlNotification,4769 &Guest::notifyCtrlDispatcher, 4770 4770 pConsole->getGuest()); 4771 4771 if (RT_FAILURE(rc)) -
trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp
r37447 r37589 1197 1197 1198 1198 /** 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 */ 1207 int 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 */ 1253 void 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 1278 bool 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 1288 void Guest::callbackFreeUserData(void *pvData) 1289 { 1290 if (pvData) 1291 { 1292 RTMemFree(pvData); 1293 pvData = NULL; 1294 } 1295 } 1296 1297 int 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 ...*/ 1329 void* 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 1345 bool 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 1372 bool 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 1399 int 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 */ 1433 int 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 */ 1512 int 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 1566 int Guest::callbackNotifyComplete(uint32_t uContextID) 1567 { 1568 return callbackNotifyEx(uContextID, S_OK, NULL /* No message */); 1569 } 1570 1571 int 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 /** 1199 1614 * Static callback function for receiving updates on guest control commands 1200 1615 * from the guest. Acts as a dispatcher for the actual class instance. … … 1205 1620 * 1206 1621 */ 1207 DECLCALLBACK(int) Guest:: doGuestCtrlNotification(void *pvExtension,1208 1209 1210 1622 DECLCALLBACK(int) Guest::notifyCtrlDispatcher(void *pvExtension, 1623 uint32_t u32Function, 1624 void *pvParms, 1625 uint32_t cbParms) 1211 1626 { 1212 1627 using namespace guestControl; … … 1286 1701 1287 1702 /* Function for handling the execution start/termination notification. */ 1703 /* Callback can be called several times. */ 1288 1704 int Guest::notifyCtrlExecStatus(uint32_t u32Function, 1289 1705 PCALLBACKDATAEXECSTATUS pData) 1290 1706 { 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 1291 1730 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) 1320 1810 { 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); 1395 1817 } 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)) 1401 1819 { 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); 1419 1826 } 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) 1428 1842 { 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)); 1474 1847 } 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 } 1479 1863 LogFlowFunc(("Returned with rc=%Rrc\n", vrc)); 1480 1864 return vrc; … … 1485 1869 PCALLBACKDATAEXECOUT pData) 1486 1870 { 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 } 1512 1907 } 1513 1908 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")); 1539 1918 } 1540 1919 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; 1543 1923 } 1544 1924 … … 1547 1927 PCALLBACKDATAEXECINSTATUS pData) 1548 1928 { 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); 1577 1955 } 1578 1956 … … 1580 1958 PCALLBACKDATACLIENTDISCONNECTED pData) 1581 1959 { 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 1970 int Guest::processAdd(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, 1971 uint32_t uExitCode, uint32_t uFlags) 1972 { 1973 AssertReturn(u32PID, VERR_INVALID_PARAMETER); 1583 1974 1584 1975 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 1994 int Guest::processGetByPID(uint32_t u32PID, PVBOXGUESTCTRL_PROCESS pProcess) 1995 { 1996 AssertReturn(u32PID, VERR_INVALID_PARAMETER); 1997 AssertPtrReturn(pProcess, VERR_INVALID_PARAMETER); 1998 1596 1999 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 2014 int Guest::processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags) 2015 { 2016 AssertReturn(u32PID, VERR_INVALID_PARAMETER); 2017 1602 2018 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 2033 HRESULT 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")); 1698 2045 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 2051 HRESULT 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; 1720 2067 } 1721 2068 … … 1751 2098 } while(*pRetStatus == ExecuteProcessStatus_Started && SUCCEEDED(hRC)); 1752 2099 return hRC; 2100 } 2101 2102 HRESULT 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; 1753 2182 } 1754 2183 #endif /* VBOX_WITH_GUEST_CONTROL */ … … 1825 2254 * occurred. 1826 2255 */ 1827 ComObjPtr <Progress> p rogress;1828 rc = p rogress.createObject();2256 ComObjPtr <Progress> pProgress; 2257 rc = pProgress.createObject(); 1829 2258 if (SUCCEEDED(rc)) 1830 2259 { 1831 rc = p rogress->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. */ 1836 2265 } 1837 2266 ComAssertComRC(rc); … … 1893 2322 if (RT_SUCCESS(vrc)) 1894 2323 { 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)) 1927 2338 { 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; 1936 2383 } 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 else1945 vrc = VERR_INVALID_VM_HANDLE;1946 2384 RTMemFree(pvEnv); 1947 2385 } 1948 2386 RTStrFree(pszArgs); 1949 2387 } 2388 1950 2389 if (RT_SUCCESS(vrc)) 1951 2390 { … … 1957 2396 * get the PID. 1958 2397 */ 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)) 1962 2406 { 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)) 1971 2410 { 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); 2022 2414 } 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 2031 2416 { 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")); 2067 2419 } 2068 2420 } 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 */ 2071 2428 } 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); 2087 2431 2088 2432 for (unsigned i = 0; i < uNumArgs; i++) … … 2091 2435 } 2092 2436 2093 if (RT_FAILURE(vrc)) 2437 if (SUCCEEDED(rc)) 2438 { 2439 /* Return the progress to the caller. */ 2440 pProgress.queryInterfaceTo(aProgress); 2441 } 2442 else 2094 2443 { 2095 2444 if (!pRC) /* Skip logging internal calls. */ … … 2133 2482 try 2134 2483 { 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) 2152 2490 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); 2155 2496 2156 2497 if (SUCCEEDED(rc)) 2157 2498 { 2499 uint32_t uContextID = 0; 2500 2158 2501 /* 2159 2502 * Create progress object. … … 2177 2520 aTimeoutMS = UINT32_MAX; 2178 2521 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); 2183 2530 2184 2531 /* 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)) 2206 2541 { 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 2645 STDMETHODIMP 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 2207 2737 VMMDev *pVMMDev = NULL; 2208 2738 { … … 2220 2750 { 2221 2751 LogFlowFunc(("hgcmHostCall numParms=%d\n", i)); 2222 vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_ SET_INPUT,2752 vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT, 2223 2753 i, paParms); 2224 2754 } … … 2227 2757 if (RT_SUCCESS(vrc)) 2228 2758 { 2229 LogFlowFunc(("Waiting for HGCM callback ...\n"));2759 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS)); 2230 2760 2231 2761 /* … … 2234 2764 * get the PID. 2235 2765 */ 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)) 2239 2774 { 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)) 2251 2778 { 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) 2255 2782 { 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. */ 2273 2790 } 2274 2791 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); 2277 2801 } 2278 2802 else 2279 rc = setError(VBOX_E_IPRT_ERROR,2280 tr("The input operation was canceled by the guest"));2281 2803 { 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")); 2285 2806 } 2286 2807 } 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); 2290 2810 } 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); 2294 2821 2295 2822 /* Cleanup. */ … … 2298 2825 pProgress.setNull(); 2299 2826 } 2300 }2301 catch (std::bad_alloc &)2302 {2303 rc = E_OUTOFMEMORY;2304 }2305 return rc;2306 #endif2307 }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 parameters2312 * in the API and have the same way of specifying infinite waits! */2313 #ifndef VBOX_WITH_GUEST_CONTROL2314 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 try2335 {2336 /*2337 * Create progress object.2338 * This progress object, compared to the one in executeProgress() above2339 * is only local and is used to determine whether the operation finished2340 * 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 meanwhile2387 * 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 process2409 * has been started (or something went wrong). This is necessary to2410 * 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 else2450 {2451 /* No data within specified timeout available. Use a special2452 * 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 want2469 * 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 else2483 {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));2512 2827 } 2513 2828 catch (std::bad_alloc &) … … 2535 2850 try 2536 2851 { 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; 2545 2859 } 2546 2860 else … … 2836 3150 tr("Guest directory creation was aborted")); 2837 3151 else 2838 AssertReleaseMsgFailed(("Guest directory creation neither completed nor canceled!? "));3152 AssertReleaseMsgFailed(("Guest directory creation neither completed nor canceled!?\n")); 2839 3153 } 2840 3154 } … … 2856 3170 using namespace guestControl; 2857 3171 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; 3018 3173 #endif 3019 3174 } -
trunk/src/VBox/Main/src-client/GuestImpl.cpp
r36891 r37589 123 123 CallbackMapIter it; 124 124 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++) 125 destroyCtrlCallbackContext(it);125 callbackDestroy(it->first); 126 126 127 127 /* Clear process map. */
Note:
See TracChangeset
for help on using the changeset viewer.