MI_Result MergePartialConfigurations()

in LCM/dsc/engine/ConfigurationManager/LocalConfigManagerHelper.c [1179:1436]


MI_Result MergePartialConfigurations(_In_ LCMProviderContext *lcmContext,
        _In_ ModuleManager* moduleManager,
        _In_z_ const MI_Char* targetMofFile,
        _In_z_ const MI_Char* targetBaseDocumentMergedFile,
        _Outptr_result_maybenull_ MI_Instance** cimErrorDetails)
{
        MI_Result result;
        MI_Char *partialConfigDir = NULL;
        MI_Instance* metaConfigInstance = NULL;
        MI_Char* unexpandedPartialConfigFilePath = NULL;
        MI_Char* partialConfigFilePath = NULL;
        MI_Char* checksumFile = NULL;
        MI_InstanceA resourceInstanceArray = { 0 };
        Internal_Dir *dirHandle = NULL;
        MI_Application application = MI_APPLICATION_NULL;
        MI_Boolean applicationInited = MI_FALSE;
        MI_Serializer serializer = { 0 };
        MI_Value value;
        MI_Boolean errorOccured = MI_FALSE;
        MI_Boolean serializerInited = MI_FALSE;
        MI_Boolean isLocked = MI_FALSE;
        MI_Boolean newBaseDocumentIsPlaced = MI_FALSE; //Bool value marked to true if we added our new base doc instance
        Internal_DirEnt *dirEntry = NULL;
        MI_Instance * baseDocumentInstance = NULL;
        MI_Uint32 resultStatus = 0;

        /****************************** INITIALIZE EVERYTHING *******************************************/
        if (cimErrorDetails == NULL || targetMofFile == NULL || targetBaseDocumentMergedFile == NULL || moduleManager == NULL)
        {
                return MI_RESULT_INVALID_PARAMETER;
        }
        *cimErrorDetails = NULL;
        result = LoadModuleManager(moduleManager, cimErrorDetails);
        RETURN_RESULT_IF_FAILED(result);
        result = ExpandPath(CONFIGURATION_PARTIALCONFIG_STORE, &partialConfigDir, cimErrorDetails);
        RETURN_RESULT_IF_FAILED(result);

        if (File_ExistT(GetPendingConfigFileName()) != -1)
        {
                File_RemoveT(GetPendingConfigFileName());
        }
        if (File_ExistT(GetPartialConfigBaseDocumentInstanceFileName()) != -1)
        {
                File_RemoveT(GetPartialConfigBaseDocumentInstanceFileName());
        }

        result = MI_Application_Initialize(0, NULL, NULL, &application);
        GOTO_CLEANUP_AND_THROW_ERROR_IF_FAILED(result, result, ID_LCMHELPER_ERRORMERGING_PARTIALCONFIGS, cimErrorDetails, Exit)
                applicationInited = MI_TRUE;

        result = MI_Application_NewSerializer_Mof(&application, 0, MOFCODEC_FORMAT, &serializer);
        GOTO_CLEANUP_AND_THROW_ERROR_IF_FAILED(result, result, ID_LCMHELPER_ERRORMERGING_PARTIALCONFIGS, cimErrorDetails, Exit)
                serializerInited = MI_TRUE;

        //We know the directory exists for sure here so no need for checking for that.
        //Open directory and check for the first obtained file
        dirHandle = Internal_Dir_Open(partialConfigDir, NitsMakeCallSite(-3, NULL, NULL, 0));
        if (dirHandle == NULL || NitsShouldFault(NitsHere(), NitsAutomatic))
        {
                result = GetCimMIError(MI_RESULT_FAILED, cimErrorDetails, ID_MODMAN_FINDFIRST_FAILED);
                goto Exit;
        }
        dirEntry = Internal_Dir_Read(dirHandle, MOF_EXTENSION);
        //From now on until pending.mof is fully done, lock everything

        RecursiveLock_Acquire(&gExecutionLock);
        isLocked = MI_TRUE;
        result = GetMetaConfig((MSFT_DSCMetaConfiguration **) &metaConfigInstance);
        GOTO_CLEANUP_IF_FAILED(result, Exit);

        DSC_EventWriteLCMAboutToMergePartial();
        /****************************** LOGIC TO DESERIALIZE AND SERIALIZEs *******************************************/
        //For each file that is obtained, merge it up into the new pending.mof
        while (dirEntry != NULL)
        {
                /* Only process files*/
                if (!dirEntry->isDir)
                {

                        //Concatenate directory and filename
                        result = GetFullPath(partialConfigDir, dirEntry->name, &unexpandedPartialConfigFilePath, cimErrorDetails);
                        GOTO_CLEANUP_IF_FAILED(result, Exit);

                        result = ExpandPath(unexpandedPartialConfigFilePath, &partialConfigFilePath, cimErrorDetails);
                        DSC_free(unexpandedPartialConfigFilePath);
                        GOTO_CLEANUP_IF_FAILED(result, Exit);
                        //Validate the configuration - if its correctly placed - sometimes it could have been published first , n then run another set, and you still need to validate, if not valid, silently delete
                        result = ValidatePartialConfiguration(moduleManager, partialConfigFilePath, metaConfigInstance, cimErrorDetails);
                        if (result != MI_RESULT_OK)
                        {
                                if (*cimErrorDetails)
                                {//Output the warning with any possible underlying error that came with the above function calls
                                        DSC_WriteWarningFromError1Param((MI_Context*) lcmContext->context, cimErrorDetails, ID_LCM_PARTIALCONFIG_DELETINGFILE_WARNING, partialConfigFilePath);
                                        INSTANCE_DELETE_IF_NOT_NULL(*cimErrorDetails);
                                }
                                File_RemoveT(partialConfigFilePath); //Delete the file since its irrelevant
                               
                                ConcatStrings(&checksumFile, 0, partialConfigFilePath, CHECKSUM_EXTENSION);
                                File_RemoveT(checksumFile); //Delete the corresponding checksum file as well 
                                
                                dirEntry = Internal_Dir_Read(dirHandle, MOF_EXTENSION);
                                DSCFREE_IF_NOT_NULL(partialConfigFilePath);
                                DSCFREE_IF_NOT_NULL(checksumFile);
                                                                
                result = MI_RESULT_OK;
                                errorOccured = MI_TRUE;
                                continue; //Carry on to the next file
                        }
                        //Check if the required modules are present
                        
                        /*
                          result = DoPushDependencyCheck((MI_Char*) partialConfigFilePath, cimErrorDetails);
                        
                        if (result == MI_RESULT_OK)
                        {
                                result = moduleManager->ft->LoadInstanceDocumentFromLocation(moduleManager, VALIDATE_DOCUMENT_INSTANCE, partialConfigFilePath, cimErrorDetails, &resourceInstanceArray, &baseDocumentInstance);
                        }
                        */

                        result = moduleManager->ft->LoadInstanceDocumentFromLocation(moduleManager, VALIDATE_DOCUMENT_INSTANCE, partialConfigFilePath, cimErrorDetails, &resourceInstanceArray, &baseDocumentInstance);

                        //If any of the above operations failed, just skip the file and continue to the next one - give them a warning that you're doing this
                        if (result != MI_RESULT_OK)
                        {
                                if (*cimErrorDetails)
                                { //Output the warning with any possible underlying error that came with the above function calls
                                        DSC_WriteWarningFromError1Param((MI_Context*) lcmContext->context, cimErrorDetails, ID_LCM_PARTIALCONFIG_SKIPFILE_WARNING, partialConfigFilePath);
                                        INSTANCE_DELETE_IF_NOT_NULL(*cimErrorDetails);
                                }
                                dirEntry = Internal_Dir_Read(dirHandle, MOF_EXTENSION);
                                DSCFREE_IF_NOT_NULL(partialConfigFilePath);
                                result = MI_RESULT_OK;
                                errorOccured = MI_TRUE;
                                continue; //Carry on to the next file
                        }

                        //Get the partial configuration name from the document
                        result = DSC_MI_Instance_GetElement(baseDocumentInstance, OMI_ConfigurationDocument_Name, &value, NULL, NULL, NULL);
                        GOTO_CLEANUP_IF_FAILED(result, Exit);
                        DSC_EventWriteLCMMergingPartialConfiguration(value.string);

                        //For now its just a simple merge, put all of these deserialized stuff into another file and we'll be done, yay!
                        result = SerializeInstanceArrayToFile(&resourceInstanceArray, GetPendingConfigFileName(), cimErrorDetails, MI_T("ab"), MI_FALSE, &serializer);
                        GOTO_CLEANUP_IF_FAILED(result, Exit);
                        //Free the path for the partial config, to rellocate for the next one.
                        DSCFREE_IF_NOT_NULL(partialConfigFilePath);
                        result = SerializeSingleInstanceToFile(baseDocumentInstance, GetPartialConfigBaseDocumentInstanceFileName(), cimErrorDetails, MI_T("ab"), MI_FALSE, &serializer);
                        GOTO_CLEANUP_IF_FAILED(result, Exit);
                        //Set in the unique base document - happens one time only
                        if (!newBaseDocumentIsPlaced)
                        {
                                value.string = OMI_ConfigurationDocument_PartialConfigAuthor;
                                result = MI_Instance_SetElement(baseDocumentInstance, OMI_ConfigurationDocument_Author, &value, MI_STRING, 0);
                                GOTO_CLEANUP_IF_FAILED(result, Exit);
                                value.string = OMI_ConfigurationDocument_PartialConfigName;
                                result = MI_Instance_SetElement(baseDocumentInstance, OMI_ConfigurationDocument_Name, &value, MI_STRING, 0);
                                GOTO_CLEANUP_IF_FAILED(result, Exit);
                                result = SerializeSingleInstanceToFile(baseDocumentInstance, GetPendingConfigFileName(), cimErrorDetails, MI_T("ab"), MI_FALSE, &serializer);
                                GOTO_CLEANUP_IF_FAILED(result, Exit);
                                newBaseDocumentIsPlaced = MI_TRUE;
                        }
                        //We can't have multiple base document instances, so remove them from each (we'll add one at the end)
                        INSTANCE_DELETE_IF_NOT_NULL(baseDocumentInstance);
                        CleanUpInstanceCache(&resourceInstanceArray); //To clear old file's data                        
                }
                dirEntry = Internal_Dir_Read(dirHandle, MOF_EXTENSION);
        }//End of recursing directory files

        //Now check if the merged file is valid
        if (File_ExistT(GetPartialConfigBaseDocumentInstanceFileName()) != -1)
        {
                result = ValidatePartialConfigMergedFile(moduleManager, GetPendingConfigFileName(), cimErrorDetails);
        }
        else
        {
            if (errorOccured)
            {
                //This means there was an error and hence partial configurations couldn't be merged
                result = GetCimMIError(MI_RESULT_NOT_FOUND, cimErrorDetails, ID_PARTIALCONFIG_FAILEDPARTIALCONFIGS);
            }
            else
            {
                //That means there was no partial config file found that was valid, so throw error
                result = GetCimMIError(MI_RESULT_NOT_FOUND, cimErrorDetails, ID_PARTIALCONFIG_NOPARTIALCONFIGPRESENT);
            }
        }
        GOTO_CLEANUP_IF_FAILED(result, Exit);

        // we have a basic merged pending mof file, We will doing filtering to remove the ones that dependsOn is not satisfied
        result = moduleManager->ft->LoadInstanceDocumentFromLocation(moduleManager, 0, GetPendingConfigFileName(), cimErrorDetails, &resourceInstanceArray, &baseDocumentInstance);

        GOTO_CLEANUP_IF_FAILED(result, Exit);
        // clean up the temp file so the filtered configuration will be saved there
        if (File_ExistT(GetPendingConfigTmpFileName()) != -1)
        {
                File_RemoveT(GetPendingConfigTmpFileName());
        }
        if (File_ExistT(GetPartialConfigBaseDocumentInstanceTmpFileName()) != -1)
        {
                File_RemoveT(GetPartialConfigBaseDocumentInstanceTmpFileName());
        }

        lcmContext->serializer = &serializer;
        result = ProcessPartialConfigurations(lcmContext, moduleManager, 0, &resultStatus, FilterPartialConfigurations, metaConfigInstance, &resourceInstanceArray, cimErrorDetails);
        GOTO_CLEANUP_IF_FAILED(result, Exit);

        result = SerializeSingleInstanceToFile(baseDocumentInstance, GetPendingConfigTmpFileName(), cimErrorDetails, MI_T("ab"), MI_FALSE, &serializer);
        GOTO_CLEANUP_IF_FAILED(result, Exit);
        if (File_ExistT(GetPendingConfigFileName()) != -1)
        {
                File_RemoveT(GetPendingConfigFileName());
        }
        if (File_ExistT(GetPartialConfigBaseDocumentInstanceFileName()) != -1)
        {
                File_RemoveT(GetPartialConfigBaseDocumentInstanceFileName());
        }

        // If Current.mof exists, move Current.mof to Previous.mof
        if (File_ExistT(GetCurrentConfigFileName()) == 0)
        {
            result = CopyConfigAndRemoveSource(CONFIGURATION_LOCATION_CURRENT, CONFIGURATION_LOCATION_PREVIOUS, cimErrorDetails);
            GOTO_CLEANUP_IF_FAILED(result, Exit);
        }

        File_CopyT(GetPendingConfigTmpFileName(), GetPendingConfigFileName());
        File_CopyT(GetPartialConfigBaseDocumentInstanceTmpFileName(), GetPartialConfigBaseDocumentInstanceFileName());

        /****************************** CLEANUIP AND RETURN *******************************************/
Exit:
        DSCFREE_IF_NOT_NULL(partialConfigFilePath);
        DSCFREE_IF_NOT_NULL(partialConfigDir);
        INSTANCE_DELETE_IF_NOT_NULL(baseDocumentInstance);
        INSTANCE_DELETE_IF_NOT_NULL(metaConfigInstance);
        //Note: No need to clean partialConfigInstance variable as it points to a part of metaconfiguration
        if (resourceInstanceArray.data != NULL)
        {
                CleanUpInstanceCache(&resourceInstanceArray);
        }
        if (serializerInited)
        {
                MI_Serializer_Close(&serializer);
                serializerInited = MI_FALSE;
        }

        if (applicationInited)
        {
                MI_Application_Close(&application);
                applicationInited = MI_FALSE;
        }
        if (isLocked)
        {
                RecursiveLock_Release(&gExecutionLock);
                isLocked = MI_FALSE;
        }
        if (dirHandle != NULL)
                Internal_Dir_Close(dirHandle);
        return result;
}