in src/extensions/update_manifest_handlers/steps_handler/src/steps_handler.cpp [748:1132]
static ADUC_Result StepsHandler_Install(const tagADUC_WorkflowData* workflowData)
{
ADUC_Result result;
result.ResultCode = ADUC_Result_Failure;
result.ExtendedResultCode = 0;
ADUC_WorkflowHandle handle = workflowData->WorkflowHandle;
ADUC_WorkflowHandle stepHandle = nullptr;
const char* workflowId = workflow_peek_id(handle);
char* workFolder = workflow_get_workfolder(handle);
JSON_Array* selectedComponentsArray = nullptr;
int workflowLevel = workflow_get_level(handle);
int workflowStep = workflow_get_step_index(handle);
int selectedComponentsCount = 0;
char* serializedComponentString = nullptr;
bool isComponentsEnumeratorRegistered = ExtensionManager::IsComponentsEnumeratorRegistered();
int createResult = 0;
if (workflow_is_cancel_requested(handle))
{
Log_Info(
"Install task cancelled (level: %d, step:%d, wfid:%s, h_addr:0x%x).",
workflowLevel,
workflowStep,
workflowId,
handle);
result.ResultCode = ADUC_Result_Failure_Cancelled;
result.ExtendedResultCode = 0;
goto done;
}
Log_Debug(
"\n#\n#Install task begin (level: %d, step:%d, wfid:%s, h_addr:0x%x).",
workflowLevel,
workflowStep,
workflowId,
handle);
createResult = ADUC_SystemUtils_MkSandboxDirRecursive(workFolder);
if (createResult != 0)
{
Log_Error("Unable to create folder %s, error %d", workFolder, createResult);
result = { ADUC_Result_Failure, ADUC_ERC_STEPS_HANDLER_CREATE_SANDBOX_FAILURE };
goto done;
}
result = PrepareStepsWorkflowDataObject(handle);
if (IsAducResultCodeFailure(result.ResultCode))
{
workflow_set_result_details(handle, "Invalid steps workflow collection");
goto done;
}
if (workflowLevel == 0 || !isComponentsEnumeratorRegistered)
{
// If this is a top-level step or component enumerator is not registered, we will assume that this step is
// intended for the host device and will set selectedComponentCount to 1 to iterate through
// every step once without setting any component data on the workflow.
selectedComponentsCount = 1;
}
else
{
// This is a reference step (workflowLevel == 1), this intended for one or more components.
result = GetSelectedComponentsArray(handle, &selectedComponentsArray);
if (IsAducResultCodeFailure(result.ResultCode))
{
const char* fmt = "Missing selected components. workflow level %d, step %d";
Log_Error(fmt, workflowLevel, workflowStep);
workflow_set_result_details(handle, fmt, workflowLevel, workflowStep);
goto done;
}
selectedComponentsCount = static_cast<int>(json_array_get_count(selectedComponentsArray));
if (selectedComponentsCount == 0)
{
// If there's no matching component, we will consider this step 'optional' and this
// step is no-op.
const char* msg = "Optional step (no matching components)";
Log_Debug(msg);
result = { ADUC_Result_Install_Skipped_NoMatchingComponents };
// This is a good opportunity to set the workflow state to indicates that
// the current component is 'optional'.
// We do this by setting workflow result code to ADUC_Result_Install_Skipped_NoMatchingComponents
ADUC_Result currentResult = workflow_get_result(handle);
if (IsAducResultCodeFailure(currentResult.ResultCode))
{
ADUC_Result newResult;
newResult.ResultCode = ADUC_Result_Install_Skipped_NoMatchingComponents;
newResult.ExtendedResultCode = 0;
workflow_set_result(handle, newResult);
workflow_set_result_details(handle, msg);
}
}
}
// For each selected component, perform step's backup, install & apply phase, restore phase if needed, in order.
for (size_t iCom = 0, stepsCount = workflow_get_children_count(handle); iCom < selectedComponentsCount; iCom++)
{
serializedComponentString = CreateComponentSerializedString(selectedComponentsArray, iCom);
//
// For each step (child workflow), invoke backup, install and apply actions.
// if install or apply fails, invoke restore action.
//
for (size_t i = 0; i < stepsCount; i++)
{
if (IsStepsHandlerExtraDebugLogsEnabled())
{
Log_Debug(
"Perform install action of child step #%d on component #%d.\n#### Component ####\n%s\n###################\n",
i,
iCom,
serializedComponentString);
}
// Use a wrapper workflow to hold a stepHandle.
ADUC_WorkflowData stepWorkflow = {};
stepHandle = workflow_get_child(handle, i);
if (stepHandle == nullptr)
{
const char* errorFmt = "Cannot process step #%lu due to missing (child) workflow data.";
Log_Error(errorFmt, i);
result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_INSTALL_FAILURE_MISSING_CHILD_WORKFLOW;
workflow_set_result_details(handle, errorFmt, i);
goto done;
}
stepWorkflow.WorkflowHandle = stepHandle;
// For inline step - set current component info on the workflow.
if (serializedComponentString != nullptr && workflow_is_inline_step(handle, i))
{
if (!workflow_set_selected_components(stepHandle, serializedComponentString))
{
result.ResultCode = ADUC_Result_Failure;
result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_SET_SELECTED_COMPONENTS_FAILURE;
workflow_set_result_details(handle, "Cannot set target component(s) for step #%d", i);
goto done;
}
}
ContentHandler* contentHandler = nullptr;
const char* stepUpdateType = workflow_is_inline_step(handle, i)
? workflow_peek_update_manifest_step_handler(handle, i)
: DEFAULT_REF_STEP_HANDLER;
Log_Info("Loading handler for child step #%lu (handler: '%s')", i, stepUpdateType);
result = ExtensionManager::LoadUpdateContentHandlerExtension(stepUpdateType, &contentHandler);
if (IsAducResultCodeFailure(result.ResultCode))
{
const char* errorFmt = "Cannot load a handler for step #%lu (handler :%s)";
Log_Error(errorFmt, i, stepUpdateType);
workflow_set_result(stepHandle, result);
workflow_set_result_details(handle, errorFmt, i, stepUpdateType == nullptr ? "NULL" : stepUpdateType);
goto done;
}
// If this item is already installed, skip to the next one.
try
{
result = contentHandler->IsInstalled(&stepWorkflow);
}
catch (...)
{
// Cannot determine whether the step has been applied, so, we'll try to process the step.
result.ResultCode = ADUC_Result_IsInstalled_NotInstalled;
result.ExtendedResultCode = 0;
}
if (IsAducResultCodeSuccess(result.ResultCode) && result.ResultCode == ADUC_Result_IsInstalled_Installed)
{
result.ResultCode = ADUC_Result_Install_Skipped_UpdateAlreadyInstalled;
result.ExtendedResultCode = 0;
workflow_set_result(stepHandle, result);
workflow_set_result_details(handle, workflow_peek_result_details(stepHandle));
// Skipping 'backup', 'install' and 'apply'.
goto instanceDone;
}
//
// Perform 'backup' action before install.
//
try
{
result = contentHandler->Backup(&stepWorkflow);
}
catch (...)
{
result.ResultCode = ADUC_Result_Failure;
result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_INSTALL_UNKNOWN_EXCEPTION_BACKUP_CHILD_STEP;
goto done;
}
if (IsAducResultCodeFailure(result.ResultCode))
{
// Propagate item's resultDetails to parent.
workflow_set_result_details(handle, workflow_peek_result_details(stepHandle));
goto done;
}
//
// Perform 'install' action.
//
try
{
result = contentHandler->Install(&stepWorkflow);
}
catch (...)
{
Log_Error("The handler throws an exception inside Install().");
result.ResultCode = ADUC_Result_Failure;
result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_INSTALL_UNKNOWN_EXCEPTION_INSTALL_CHILD_STEP;
goto done;
}
// If the workflow interruption is required as part of the Install action,
// we must propagate that request to the wrapping workflow.
if (workflow_is_immediate_reboot_requested(stepHandle)
|| workflow_is_immediate_agent_restart_requested(stepHandle))
{
// Skip remaining tasks for this instance.
// And then skip remaining instance(s) if requested.
goto instanceDone;
}
// If any step reported that the update is already installed on the
// selected component, we will skip the 'apply' phase, and skip all
// remaining step(s).
switch (result.ResultCode)
{
case ADUC_Result_Install_Skipped_UpdateAlreadyInstalled:
case ADUC_Result_Install_Skipped_NoMatchingComponents:
goto instanceDone;
}
// If Install task failed, try to restore (best effort).
// The restore result is discarded, since install result is more important to customer.
if (IsAducResultCodeFailure(result.ResultCode))
{
// Propagate item's resultDetails to parent.
workflow_set_result_details(handle, workflow_peek_result_details(stepHandle));
// When install fails, invoke Restore action
try
{
// Try to restore from the install failure, but it shouldn't impact the result code.
// To know the restore result on each step, the corresponding Update Handler will need to
// implement proper logging and send it up through Diagnostics service.
contentHandler->Restore(&stepWorkflow);
}
catch (...)
{
Log_Warn("Unexpected error happened during restore action.");
}
goto done;
}
//
// Perform 'apply' action.
//
try
{
result = contentHandler->Apply(&stepWorkflow);
Log_Debug("Step's apply() return r:0x%x rc:0x%x", result.ResultCode, result.ExtendedResultCode);
}
catch (...)
{
Log_Error("The handler throws an exception inside Apply().");
result.ResultCode = ADUC_Result_Failure;
result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_INSTALL_UNKNOWN_EXCEPTION_APPLY_CHILD_STEP;
goto done;
}
if (IsAducResultCodeFailure(result.ResultCode))
{
// Propagate item's resultDetails to parent.
workflow_set_result_details(handle, workflow_peek_result_details(stepHandle));
// when apply fails, invoke restore action
try
{
Log_Info("Failed to install or apply. Try to restore now...");
// Try to restore from the apply failure, but it shouldn't impact the result code.
// To know the restore result on each step, the corresponding Update Handler will need to
// implement proper logging and send it up through Diagnostics service.
contentHandler->Restore(&stepWorkflow);
}
catch (...)
{
Log_Warn("Unexpected error happened during restore action.");
goto done;
}
}
instanceDone:
// If the workflow interruption is required as part of the Install action,
// we must propagate that request to the wrapping workflow.
if (workflow_is_immediate_reboot_requested(stepHandle))
{
workflow_request_immediate_reboot(handle);
// We must skip the remaining instance(s).
goto done;
}
if (workflow_is_immediate_agent_restart_requested(stepHandle))
{
// We must skip the remaining instance(s).
workflow_request_immediate_agent_restart(handle);
goto done;
}
if (workflow_is_reboot_requested(stepHandle))
{
// Continue with the remaining instance(s).
workflow_request_reboot(handle);
break;
}
if (workflow_is_agent_restart_requested(stepHandle))
{
// Continue with the remaining instance(s).
workflow_request_agent_restart(handle);
break;
}
workflow_set_result(stepHandle, result);
stepHandle = nullptr;
if (IsAducResultCodeFailure(result.ResultCode))
{
goto componentDone;
}
} // steps
componentDone:
json_free_serialized_string(serializedComponentString);
serializedComponentString = nullptr;
if (IsAducResultCodeFailure(result.ResultCode))
{
goto done;
}
}
if (workflow_is_cancel_requested(workflowData->WorkflowHandle))
{
result.ResultCode = ADUC_Result_Failure_Cancelled;
result.ExtendedResultCode = 0;
}
else
{
result.ResultCode = ADUC_Result_Install_Success;
result.ExtendedResultCode = 0;
}
done:
// NOTE: Do not free child workflow here, so that it can be reused in the next phase.
// Only free child handle when the workflow is done.
//
// Alternatively, we can persist child workflow state, to free up some memory, and
// load the state when needed in the next phase
workflow_set_result(handle, result);
if (IsAducResultCodeSuccess(result.ResultCode))
{
workflow_set_state(handle, ADUCITF_State_InstallSucceeded);
}
else
{
workflow_set_state(handle, ADUCITF_State_Failed);
}
json_free_serialized_string(serializedComponentString);
workflow_free_string(workFolder);
Log_Debug("Steps_Handler Install end (level %d).", workflowLevel);
return result;
}