in src/extensions/update_manifest_handlers/steps_handler/src/steps_handler.cpp [1260:1468]
static ADUC_Result StepsHandler_IsInstalled(const tagADUC_WorkflowData* workflowData)
{
ADUC_Result result{ ADUC_Result_Failure };
ADUC_WorkflowHandle handle = workflowData->WorkflowHandle;
ADUC_WorkflowHandle stepHandle = nullptr;
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();
Log_Debug("Evaluating is-installed state of the workflow (level %d, step %d).", workflowLevel, workflowStep);
int 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)
{
// To top-level step, or if component enumerator is not registered, we will assume that this reference update is
// interned for the host device and will set selectedComponentCount to 1 to iterate through
// every step once.
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. Returning 'installed' to skip this step.
const char* msg = "Optional step (no matching components)";
Log_Debug(msg);
result = { ADUC_Result_IsInstalled_Installed };
// 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_Download_Skipped_NoMatchingComponents.
ADUC_Result currentResult = workflow_get_result(handle);
if (IsAducResultCodeFailure(currentResult.ResultCode))
{
ADUC_Result newResult = { ADUC_Result_Download_Skipped_NoMatchingComponents };
workflow_set_result(handle, newResult);
workflow_set_result_details(handle, msg);
}
goto done;
}
}
// For each selected component, check whether the update has been installed.
for (size_t iCom = 0, stepsCount = workflow_get_children_count(handle); iCom < selectedComponentsCount; iCom++)
{
serializedComponentString = CreateComponentSerializedString(selectedComponentsArray, iCom);
// For each step (child workflow), invoke IsInstalled().
for (size_t i = 0; i < stepsCount; i++)
{
if (IsStepsHandlerExtraDebugLogsEnabled())
{
Log_Debug(
"Evaluating 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 child step #%lu due to missing (child) workflow data.";
Log_Error(errorFmt, i);
result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_ISINSTALLED_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 child step #%lu", 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_Debug("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 child step #%lu (handler :%s)";
Log_Error(errorFmt, i, stepUpdateType);
workflow_set_result_details(handle, errorFmt, i, stepUpdateType == nullptr ? "NULL" : stepUpdateType);
goto done;
}
// If this step is already installed, skip to the next one.
try
{
result = contentHandler->IsInstalled(&stepWorkflow);
// If step is already installed, we should make sure that the current step's result is set correctly.
if (result.ResultCode == ADUC_Result_IsInstalled_Installed)
{
// Note: the step's workflow result will be reported to the IoT Hub when the workflow is finished.
// If the step is 'Installed', its workflow result should not be 'Failure'.
// We're setting the result code to ADUC_Result_Install_Skipped_UpdateAlreadyInstalled here
// to avoid potential confusion when customer viewing the twin data.
ADUC_Result stepWorkflowResult = workflow_get_result(stepWorkflow.WorkflowHandle);
if (stepWorkflowResult.ResultCode == ADUC_Result_Failure
|| stepWorkflowResult.ResultCode == ADUC_Result_Failure_Cancelled)
{
ADUC_Result stepResultCode;
stepResultCode.ResultCode = ADUC_Result_Install_Skipped_UpdateAlreadyInstalled;
stepResultCode.ExtendedResultCode = 0;
workflow_set_result(stepWorkflow.WorkflowHandle, stepResultCode);
}
}
}
catch (...)
{
// Cannot determine whether the step is installed, so, let's assume that it's not install.
result.ResultCode = ADUC_Result_IsInstalled_NotInstalled;
result.ExtendedResultCode = 0;
}
if (IsAducResultCodeFailure(result.ResultCode)
|| result.ResultCode == ADUC_Result_IsInstalled_NotInstalled)
{
Log_Info(
"Workflow lvl %d, step #%d, child step #%lu, component #%d is not installed.",
workflowLevel,
workflowStep,
i,
iCom);
// We can stop here if we found one component that not installed.
goto done;
}
} // steps
} // components
result.ResultCode = ADUC_Result_IsInstalled_Installed;
result.ExtendedResultCode = 0;
{
// This is a good opportunity to set the workflow state to indicates that
// the current component is up-to-date with its goal state.
// We do this by setting workflow result code to ADUC_Result_Apply_Success.
ADUC_Result currentResult = workflow_get_result(handle);
if (IsAducResultCodeFailure(currentResult.ResultCode))
{
ADUC_Result newResult;
newResult.ResultCode = ADUC_Result_Apply_Success;
newResult.ExtendedResultCode = 0;
workflow_set_result(handle, newResult);
}
}
done:
json_free_serialized_string(serializedComponentString);
workflow_free_string(workFolder);
Log_Debug("Workflow lvl %d step #%d is-installed state %d", workflowLevel, workflowStep, result.ResultCode);
return result;
}