GuestCtrlImpl.cpp revision b5b4bbfe55ea08e985580bf9139eb116ccbdd1db
* @param ppvList This is actually a pointer to a char pointer * variable which keeps track of the environment block * that we're constructing. * @param pcbList Pointer to the variable holding the current size of * the environment block. (List is a misnomer, go * @param pcEnvVars Pointer to the variable holding count of variables * stored in the environment block. *
pcEnvVars +=
1;
/* Increase env variable count. */ * Adds a callback with a user provided data block and an optional progress object * to the callback map. A callback is identified by a unique context ID which is used * to identify a callback from the guest side. * @return IPRT status code. /* puContextID is optional. */ /* Create a new context ID and assign it. */ /* Create a new context ID ... */ /* Is the context ID already used? Try next ID ... */ /* Callback with context ID was not found. This means * we can use this context ID for our new callback we want break;
/* Don't try too hard. */ /* Add callback with new context ID to our callback map. */ /* Report back new context ID. */ * Destroys the formerly allocated callback data. The callback then * needs to get removed from the callback map via callbackRemove(). * Removes a callback from the callback map. * Checks whether a callback with the given context ID * @return bool True, if callback exists, false if not. * @param uContextID Context ID to check. /* pEnmType is optional. */ /* ppvData is optional. */ /* pcbData is optional. */ /* Does not do locking! Caller has to take care of it because the caller needs to /* pcbData is optional. */ /* Everything else is optional. */ return true;
/* No progress / error means canceled. */ * Notifies a specified callback about its final status. * @return IPRT status code. LogFlowFunc((
"Checking whether callback (CID=%u) needs notification iRC=%Rrc, pszMsg=%s\n",
/* If progress already canceled do nothing here. */ * Assume we didn't complete to make sure we clean up even if the LogFlowFunc((
"Notifying callback with CID=%u, iRC=%Rrc, pszMsg=%s\n",
* To get waitForCompletion completed (unblocked) we have to notify it if necessary (only * cancel won't work!). This could happen if the client thread (e.g. VBoxService, thread of a spawned process) * is disconnecting without having the chance to sending a status message before, so we * have to abort here to make sure the host never hangs/gets stuck while waiting for the * progress object to become signalled. LogFlowFunc((
"Notified callback with CID=%u returned %Rhrc (0x%x)\n",
* Do *not* NULL pProgress here, because waiting function like executeProcess() * will still rely on this object for checking whether they have to give up! /* If pProgress is not found (anymore) that's fine. * Might be destroyed already. */ * @return IPRT status code. /* When waiting for process output while the process is destroyed, * make sure we also destroy the actual waiting operation (internal progress object) * in order to not block the caller. */ /* When waiting for injecting process input while the process is destroyed, * make sure we also destroy the actual waiting operation (internal progress object) * in order to not block the caller. */ * Waits for a callback (using its context ID) to complete. * @return IPRT status code. * @param uContextID Context ID to wait for. * @param lStage Stage to wait for. Specify -1 if no staging is present/required. * Specifying a stage is only needed if there's a multi operation progress * @param lTimeout Timeout (in ms) to wait for. * Wait for the HGCM low level callback until the process * has been started (or something went wrong). This is necessary to LogFlowFunc((
"Waiting for callback completion (CID=%u, Stage=%RI32, timeout=%RI32ms) ...\n",
LogFlowFunc((
"Callback (CID=%u) completed with rc=%Rrc\n",
* Static callback function for receiving updates on guest control commands * from the guest. Acts as a dispatcher for the actual class instance. * @returns VBox status code. * No locking, as this is purely a notification which does not make any * changes to the object state. LogFlowFunc((
"pvExtension=%p, u32Function=%d, pvParms=%p, cbParms=%d\n",
//LogFlowFunc(("GUEST_DISCONNECTED\n")); //LogFlowFunc(("GUEST_EXEC_SEND_STATUS\n")); //LogFlowFunc(("GUEST_EXEC_SEND_OUTPUT\n")); //LogFlowFunc(("GUEST_EXEC_SEND_INPUT_STATUS\n")); /* Callback can be called several times. */ /* Scope write locks as much as possible. */ LogFlowFunc((
"Execution status (CID=%u, pData=0x%p)\n",
/** @todo Copy void* buffer contents? */ /* If pCallbackData is NULL this might be an old request for which no user data * might exist anymore. */ /* Was progress canceled before? */ /* Handle process map. This needs to be done first in order to have a valid * map in case some callback gets notified a bit below. */ /* Note: PIDs never get removed here in case the guest process signalled its * end; instead the next call of GetProcessStatus() will remove the PID * from the process map after we got the process' final (exit) status. * See waitpid() for an example. */ /* Just reach through flags. */ /* Interprete u32Flags as the guest process' exit code. */ /* Do progress handling. */ LogRel((
"Guest process (PID %u) started\n",
pData->
u32PID));
/** @todo Add process name */ LogRel((
"Guest process (PID %u) exited normally\n",
pData->
u32PID));
/** @todo Add process name */ LogRel((
"Guest process (PID %u) terminated abnormally with exit code = %u\n",
LogRel((
"Guest process (PID %u) terminated through signal with exit code = %u\n",
LogRel((
"Guest process (PID %u) timed out and was killed\n",
pData->
u32PID));
/** @todo Add process name */ LogRel((
"Guest process (PID %u) timed out and could not be killed\n",
pData->
u32PID));
/** @todo Add process name */ LogRel((
"Guest process (PID %u) killed because system is shutting down\n",
pData->
u32PID));
/** @todo Add process name */ * If u32Flags has ExecuteProcessFlag_IgnoreOrphanedProcesses set, we don't report an error to * our progress object. This is helpful for waiters which rely on the success of our progress object * even if the executed process was killed because the system/VBoxService is shutting down. * In this case u32Flags contains the actual execution flags reached in via Guest::ExecuteProcess(). LogRel((
"Guest process (PID %u) could not be started because of rc=%Rrc\n",
LogRel((
"Guest process could not be started because maximum number of parallel guest processes has been reached\n"));
LogRel((
"Guest process could not be started because of rc=%Rrc\n",
/* Do we need to handle the callback error? */ /* Notify all callbacks which are still waiting on something * which is related to the current PID. */ LogFlowFunc((
"Failed to notify other callbacks for PID=%u\n",
/* Let the caller know what went wrong ... */ LogFlowFunc((
"Failed to notify callback CID=%u for PID=%u\n",
LogFlowFunc((
"Process (CID=%u, status=%u) reported: %s\n",
LogFlowFunc((
"Returned with rc=%Rrc, rcCallback=%Rrc\n",
/* Function for handling the execution output notification. */ /* Scope write locks as much as possible. */ /* Make sure we really got something! */ /* Allocate data buffer and copy it */ else /* Nothing received ... */ /* If pCallbackData is NULL this might be an old request for which no user data * might exist anymore. */ Guest::
tr(
"The output operation was canceled"));
/* Function for handling the execution input status notification. */ /* Scope write locks as much as possible. */ /* Save bytes processed. */ /* If pCallbackData is NULL this might be an old request for which no user data * might exist anymore. */ * Gets guest process information. Removes the process from the map * @return IPRT status code. * @param u32PID PID of process to get status for. * @param pProcess Where to store the process information. Optional. * @param fRemove Flag indicating whether to remove the * process from the map when process marked a * remove it from the map. */ tr(
"VMM device is not available (is the VM running?)"));
tr(
"Process execution has been canceled"));
tr(
"The guest did not respond within time"));
tr(
"Waiting for completion failed with error %Rrc"),
rc);
tr(
"VMM device is not available (is the VM running?)"));
tr(
"The guest execution service is not ready (yet)"));
tr(
"The guest execution service is not available"));
else /* HGCM call went wrong. */ tr(
"The HGCM call failed with error %Rrc"),
rc);
#
endif /* VBOX_WITH_GUEST_CONTROL *//** @todo r=bird: Eventually we should clean up all the timeout parameters * in the API and have the same way of specifying infinite waits! */ #
else /* VBOX_WITH_GUEST_CONTROL */ /* Do not allow anonymous executions (with system rights). */ LogRel((
"Executing guest process \"%s\" as user \"%s\" ...\n",
* Executes and waits for an internal tool (that is, a tool which is integrated into * VBoxService, beginning with "vbox_" (e.g. "vbox_ls")) to finish its operation. * @param aTool Name of tool to execute. * @param aDescription Friendly description of the operation. * @param aFlags Execution flags. * @param aUsername Username to execute tool under. * @param aPassword The user's password. * @param uFlagsToAdd ExecuteProcessFlag flags to add to the execution operation. * @param aProgress Pointer which receives the tool's progress object. Optional. * @param aPID Pointer which receives the tool's PID. Optional. /* Return the progress to the caller. */ /* Did we get some status? */ /* Process is (still) running; get PID. */ /* In any other case the process either already * terminated or something else went wrong, so no PID ... */ * Process (already) ended, but we want to get the * PID anyway to retrieve the output in a later call. tr(
"The path to file '%s' was not found on guest"),
pszCommand);
tr(
"The file '%s' is not an executable format on guest"),
pszCommand);
tr(
"The specified user '%s' was not able to logon on guest"),
pszUser);
tr(
"The guest did not respond within time (%ums)"),
ulTimeout);
tr(
"The execution operation was canceled"));
tr(
"Concurrent guest process limit is reached"));
tr(
"The service call failed with error %Rrc"),
vrc);
tr(
"The operation did not complete within time"));
/** @todo Add more stuff! */ * Tries to drain the guest's output and fill it into * a guest process stream object for later usage. * @todo What's about specifying stderr? * @return IPRT status code. * @param aPID PID of process to get the output from. * @param aFlags Which stream to drain (stdout or stderr). * @param stream Reference to guest process stream to fill. 0
/* Infinite timeout */,
continue;
/* Try one more time. */ else /* No more output and/or error! */ true /* Remove from table */);
* Tries to retrieve the next stream block of a given stream and * drains the process output only as much as needed to get this next * @return IPRT status code. LogFlowFunc((
"Getting next stream block of PID=%u, Flags=%u; cbStrmSize=%u, cbStrmOff=%u\n",
LogFlowFunc((
"Parsing block rc=%Rrc, strmBlockCnt=%ld\n",
0
/* Infinite timeout */,
/* Reset found pairs to not break out too early and let all the new * data to be parsed as well. */ continue;
/* Try one more time. */ /* No more output to drain from stream. */ /* We haved drained the stream as much as we can and reached the * end of our stream buffer -- that means that there simply is no * stream block anymore, which is ok. */ LogFlowFunc((
"Returned with strmBlockCnt=%ld, cPairs=%ld, rc=%Rrc\n",
* Tries to get the next upcoming value block from a started guest process * by first draining its output and then processing the received guest stream. * @return IPRT status code. * @param ulPID PID of process to get/parse the output from. * @param stream Reference to process stream object to use. * @param streamBlock Reference that receives the next stream block data. * Gets output from a formerly started guest process, tries to parse all of its guest * stream (as long as data is available) and returns a stream object which can contain * multiple stream blocks (which in turn then can contain key=value pairs). * @param ulPID PID of process to get/parse the output from. * @param streamObjects Reference to a guest stream object structure for * storing the parsed data. /* Try to parse the stream output we gathered until now. If we still need more * data the parsing routine will tell us and we just do another poll round. */ tr(
"Error while parsing guest output (%Rrc)"),
rc);
* Waits for a fomerly started guest process to exit using its progress * object and returns its final status. Returns E_ABORT if guest process * @return IPRT status code. * @param uPID PID of guest process to wait for. * @param pProgress Progress object to wait for. * @param uTimeoutMS Timeout (in ms) for waiting; use 0 for * @param pRetStatus Pointer where to store the final process * @param puRetExitCode Pointer where to store the final process tr(
"Waiting for guest process to end failed (%Rhrc)"),
* Does the actual guest process execution, internal function. * @param aCommand Command line to execute. * @param aFlags Execution flags. * @param Username Username to execute the process with. * @param aPassword The user's password. * @param aTimeoutMS Timeout (in ms) to wait for the execution operation. * @param aPID Pointer that receives the guest process' PID. * @param aProgress Pointer that receives the guest process' progress object. * @param pRC Pointer that receives the internal IPRT return code. Optional. /** @todo r=bird: Eventually we should clean up all the timeout parameters * in the API and have the same way of specifying infinite waits! */ * Create progress object. Note that this is a multi operation * object to perform the following steps: * - Operation 2 (1): Wait for process to exit. * If this progress completed successfully (S_OK), the process 2,
/* Number of operations. */ Bstr(
tr(
"Starting process ...")).
raw());
/* Description of first stage. */ * Prepare process execution. /* Adjust timeout. If set to 0, we define * an infinite timeout. */ /* Prepare environment. */ for (
unsigned i = 0; i <
env.
size(); i++)
* If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout * until the process was started - the process itself then gets an infinite timeout for execution. * This is handy when we want to start a process inside a worker thread within a certain timeout * but let the started process perform lengthly operations then. /* Make sure mParent is valid, so set the read lock while using. * Do not keep this lock while doing the actual call, because in the meanwhile * another thread could request a write lock which would be a bad idea ... */ /* Forward the information to the VMM device. */ * Wait for the HGCM low level callback until the process * has been started (or something went wrong). This is necessary to * Wait for the first stage (=0) to complete (that is starting the process). tr(
"Unable to retrieve process execution status data"));
* Do *not* remove the callback yet - we might wait with the IProgress object on something * else (like end of process) ... /* Return the progress to the caller. */ if (!
pRC)
/* Skip logging internal calls. */ LogRel((
"Executing guest process \"%s\" as user \"%s\" failed with %Rrc\n",
#
endif /* VBOX_WITH_GUEST_CONTROL */#
else /* VBOX_WITH_GUEST_CONTROL */ /* PID exists; check if process is still running. */ Guest::
tr(
"Cannot inject input to not running process (PID %u)"),
aPID);
Guest::
tr(
"Cannot inject input to non-existent process (PID %u)"),
aPID);
* Create progress object. * This progress object, compared to the one in executeProgress() above, * is only single-stage local and is used to determine whether the operation * finished or got canceled. /* Save PID + output flags for later use. */ /* Make sure mParent is valid, so set the read lock while using. * Do not keep this lock while doing the actual call, because in the meanwhile * another thread could request a write lock which would be a bad idea ... */ /* Forward the information to the VMM device. */ * Wait for getting back the input response from the guest. tr(
"Client reported error %Rrc while processing input data"),
tr(
"Client terminated while processing input data"));
tr(
"Client reported buffer overflow while processing input data"));
/*AssertReleaseMsgFailed(("Client reported unknown input error, status=%u, flags=%u\n", pExecStatusIn->u32Status, pExecStatusIn->u32Flags));*/ tr(
"Unable to retrieve process input status data"));
/* The callback isn't needed anymore -- just was kept locally. */ #
else /* VBOX_WITH_GUEST_CONTROL *//** @todo r=bird: Eventually we should clean up all the timeout parameters * in the API and have the same way of specifying infinite waits! */ #
else /* VBOX_WITH_GUEST_CONTROL */ Guest::
tr(
"Guest process (PID %u) does not exist"),
aPID);
/* If the process is still in the process table but does not run anymore * don't remove it but report back an appropriate error. */ Guest::
tr(
"Guest process (PID %u) does not run anymore"),
aPID);
* Create progress object. * This progress object, compared to the one in executeProgress() above, * is only single-stage local and is used to determine whether the operation * finished or got canceled. Bstr(
tr(
"Getting output for guest process")).
raw(),
/** @todo Use a buffer for next iteration if returned data is too big * aSize is bogus -- will be ignored atm! */ /* Save PID + output flags for later use. */ /* Make sure mParent is valid, so set the read lock while using. * Do not keep this lock while doing the actual call, because in the meanwhile * another thread could request a write lock which would be a bad idea ... */ /* Forward the information to the VMM device. */ * Wait for the HGCM low level callback until the process * has been started (or something went wrong). This is necessary to * Wait for the first output callback notification to arrive. /* Do we need to resize the array? */ /* Fill output in supplied out buffer. */ /* No data within specified timeout available. */ /* Detach output buffer to output argument. */ tr(
"Unable to retrieve process output data (%Rrc)"),
vrc);
/* The callback isn't needed anymore -- just was kept locally. */ #
else /* VBOX_WITH_GUEST_CONTROL */ true /* Remove when terminated */);
tr(
"Process (PID %u) not found!"),
aPID);
#
else /* VBOX_WITH_GUEST_CONTROL */ /* Do not allow anonymous executions (with system rights). */ /* Create the progress object. */ Bstr(
tr(
"Copying file from guest to host")).
raw(),
/* Initialize our worker task. */ /* Assign data - aSource is the source file on the host, * aDest reflects the full path on the guest. */ /* Don't destruct on success. */ /* Return progress to the caller. */ #
endif /* VBOX_WITH_GUEST_CONTROL */#
else /* VBOX_WITH_GUEST_CONTROL */ /* Do not allow anonymous executions (with system rights). */ /* Create the progress object. */ Bstr(
tr(
"Copying file from host to guest")).
raw(),
/* Initialize our worker task. */ /* Assign data - aSource is the source file on the host, * aDest reflects the full path on the guest. */ /* Don't destruct on success. */ /* Return progress to the caller. */ #
endif /* VBOX_WITH_GUEST_CONTROL */#
else /* VBOX_WITH_GUEST_CONTROL */ /* Create the progress object. */ /* Initialize our worker task. */ /* Assign data - in that case aSource is the full path * to the Guest Additions .ISO we want to mount. */ /* Don't destruct on success. */ /* Return progress to the caller. */ #
endif /* VBOX_WITH_GUEST_CONTROL */