/* **============================================================================== ** ** Open Management Infrastructure (OMI) ** ** Copyright (c) Microsoft Corporation ** ** Licensed under the Apache License, Version 2.0 (the "License"); you may not ** use this file except in compliance with the License. You may obtain a copy ** of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, ** MERCHANTABLITY OR NON-INFRINGEMENT. ** ** See the Apache 2 License for the specific language governing permissions ** and limitations under the License. ** **============================================================================== */ #include "LifecycleContext.h" #include "SubMgr.h" #include "CIM_InstCreation.h" #include "CIM_InstDeletion.h" #include "CIM_InstMethodCall.h" #include "CIM_InstModification.h" #include "CIM_InstRead.h" #include "indicationSchema.h" #include #include #include #include #include #include static const MI_Uint32 _LIFECYCLE_MAGIC = 0xFEEDCAFE; extern MI_CONST MI_ClassDecl CIM_InstCreation_rtti; extern MI_CONST MI_ClassDecl CIM_InstDeletion_rtti; extern MI_CONST MI_ClassDecl CIM_InstMethodCall_rtti; extern MI_CONST MI_ClassDecl CIM_InstModification_rtti; extern MI_CONST MI_ClassDecl CIM_InstRead_rtti; /* * This method takes advantage of the fact that the underlying layout of * properties within all concrete lifecycle indication classes is identical. * If the layout ever changes, this method must be updated (highly unlikely). */ MI_Result _SetCommonProperties( MI_Instance* instToFill, const MI_Instance* sourceInstance ) { MI_Result result = MI_RESULT_OK; MI_Value value; PAL_Datetime now; /* * Set CIM_Indication properties */ // TODO: Populate values correctly /* MI_ConstStringField IndicationIdentifier */ value.string = PAL_T("MSFT:IndicationIdentifier"); result = MI_Instance_SetElement( instToFill, PAL_T("IndicationIdentifier"), &value, MI_STRING, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_IndicationIdentifier(); return result; } /* MI_ConstStringAField CorrelatedIndications * / result = MI_Instance_SetElement( instToFill, PAL_T("CorrelatedIndications"), &value, MI_STRINGA, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_CorrelatedIndications(); return result; } */ /* MI_ConstDatetimeField IndicationTime */ if (0 == CPU_GetLocalTimestamp(&now) ) { memcpy( &value.datetime, &now, sizeof(MI_Datetime) ); result = MI_Instance_SetElement( instToFill, PAL_T("IndicationTime"), &value, MI_DATETIME, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_IndicationTime(); return result; } } /* MI_ConstUint16Field PerceivedSeverity * / result = MI_Instance_SetElement( instToFill, PAL_T("PerceivedSeverity"), &value, MI_UINT16, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_PerceivedSeverity(); return result; } / * MI_ConstStringField OtherSeverity * / result = MI_Instance_SetElement( instToFill, PAL_T("OtherSeverity"), &value, MI_STRING, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_OtherSeverity(); return result; } / * MI_ConstStringField IndicationFilterName * / result = MI_Instance_SetElement( instToFill, PAL_T("IndicationFilterName"), &value, MI_STRING, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_IndicationFilterName(); return result; } / * MI_ConstStringField SequenceContext * / result = MI_Instance_SetElement( instToFill, PAL_T("SequenceContext"), &value, MI_STRING, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_SequenceContext(); return result; } / * MI_ConstSint64Field SequenceNumber * / result = MI_Instance_SetElement( instToFill, PAL_T("SequenceNumber"), &value, MI_SINT64, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_SequenceNumber(); return result; } / * * Set CIM_InstIndication properties * / / * MI_ConstReferenceField SourceInstance */ if (sourceInstance) { value.reference = (MI_Instance*)sourceInstance; result = MI_Instance_SetElement( instToFill, PAL_T("SourceInstance"), &value, MI_INSTANCE, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_SourceInstance(); return result; } } /* MI_ConstStringField SourceInstanceModelPath * / result = MI_Instance_SetElement( instToFill, PAL_T("SourceInstanceModelPath"), &value, MI_STRING, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_SourceInstanceModelPath(); return result; } / * MI_ConstStringField SourceInstanceHost * / result = MI_Instance_SetElement( instToFill, PAL_T("SourceInstanceHost"), &value, MI_STRING, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_SourceInstanceHost(); return result; } */ return MI_RESULT_OK; } MI_Result _PostToAllSubscribers( _In_ SubscriptionManager* subMgr, _In_ MI_Instance* instanceToPost, _In_ MI_Uint32 postTargetType ) { MI_Boolean atLeastOneDelivered = MI_FALSE; SubMgrSubscriptionPtr* sublist; MI_Result r; size_t count = 0; size_t i; r = SubMgr_GetSubscriptionList(subMgr, &sublist, &count); if ( r != MI_RESULT_OK ) return r; /* For each subscription, use its context to PostInstance of instCreation. */ r = MI_RESULT_FAILED; for (i = 0; i < count; i++) { /* * This action is done through Context to: * 1. Conform to the existing API * 2. Avoid exposure of internal code via Context.h * 3. Reuse indication posting code * * This is a best-effort action. Intermediate failures will be * ignored. */ SubscriptionContext* ctx = sublist[i]->subscribeCtx; if (0 != (postTargetType & LifeContext_ConvertSupportedType( sublist[i]->msg->targetType )) ) { if (MI_RESULT_OK == (r = MI_Context_PostIndication( &ctx->baseCtx.base, instanceToPost, 0, NULL ) ) ) { atLeastOneDelivered = MI_TRUE; } } SubMgrSubscription_Release(sublist[i]); } return (atLeastOneDelivered ? MI_RESULT_OK : r); } /* * Groups all common indication preparation actions for re-use. This * function is targetted for Post handlers that set additional properties * on the indication prior to forwarding it. * * Note: The caller must call MI_Instance_Destruct on instanceToPost to ensure * proper cleanup. */ MI_Result _CommonIndicationPrep( _In_ MI_LifecycleIndicationContext* context, _In_ MI_Instance* indicationToPost, _In_ const MI_Instance* sourceInstance, _In_ const MI_ClassDecl* classDecl ) { MI_Result result = MI_RESULT_OK; LifecycleContext* lifeContext = (LifecycleContext*)context; SubscriptionManager* subMgr = lifeContext->provider->subMgr; /* Basic validity checks to prevent unnecessary work: don't create the * indication if there is no listener to receive it. */ if ( (MI_FALSE == SubMgr_IsEnabled ( subMgr )) || (MI_TRUE == SubMgr_IsSubListEmpty ( subMgr ))) { trace_LifecycleContext_Post_InvalidState(); return MI_RESULT_FAILED; } /* Create indication to post based on the input instance */ result = Instance_Construct( indicationToPost, classDecl, NULL ); if (MI_RESULT_OK != result) { trace_FailedToConstructInstance(tcs(classDecl->name)); return result; } result = _SetCommonProperties( indicationToPost, sourceInstance ); if (MI_RESULT_OK != result) { trace_FailedToSet_CommonProperties(tcs(classDecl->name)); return result; } return result; } /* * Wrapper for handling indications with no additional properties beyond * those from CIM_Indication and CIM_InstIndication. */ MI_Result _CommonPostSimple( _In_ MI_LifecycleIndicationContext* context, _In_ MI_Instance* indicationToPost, _In_ const MI_Instance* sourceInstance, _In_ const MI_ClassDecl* classDecl, _In_ MI_Uint32 postTargetType ) { MI_Result result = MI_RESULT_OK; if (!context || !sourceInstance || ((LifecycleContext*)context)->magic != _LIFECYCLE_MAGIC) { trace_NullInputParameterForClass(tcs(classDecl->name)); return MI_RESULT_INVALID_PARAMETER; } result = _CommonIndicationPrep( context, indicationToPost, sourceInstance, classDecl ); if (MI_RESULT_OK != result) { goto Lifecycle_CommonEnd; } result = _PostToAllSubscribers( ((LifecycleContext*)context)->provider->subMgr, indicationToPost, postTargetType ); Lifecycle_CommonEnd: if (indicationToPost->ft) { MI_Instance_Destruct( indicationToPost ); } return result; } MI_Result _PostCreate( _In_ MI_LifecycleIndicationContext* context, _In_ const MI_Instance* instance) { CIM_InstCreation instCreation; memset( &instCreation, 0, sizeof(CIM_InstCreation) ); return _CommonPostSimple( context, &instCreation.__instance, instance, &CIM_InstCreation_rtti, MI_LIFECYCLE_INDICATION_CREATE ); } MI_Result _PostModify( _In_ MI_LifecycleIndicationContext* context, _In_ const MI_Instance* originalInstance, _In_ const MI_Instance* instance) { MI_Result result = MI_RESULT_OK; CIM_InstModification instModification; MI_Value value; if (!context || !originalInstance || !instance || ((LifecycleContext*)context)->magic != _LIFECYCLE_MAGIC) { trace_NullInputParameterForClass(tcs(CIM_InstModification_rtti.name)); return MI_RESULT_INVALID_PARAMETER; } memset( &instModification, 0, sizeof(CIM_InstModification) ); result = _CommonIndicationPrep( context, &instModification.__instance, instance, &CIM_InstModification_rtti ); if (MI_RESULT_OK != result) { goto Lifecycle_ModifyEnd; } /* MI_ConstReferenceField PreviousInstance */ value.reference = (MI_Instance*)originalInstance; result = MI_Instance_SetElement( &instModification.__instance, PAL_T("PreviousInstance"), &value, MI_INSTANCE, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_PreviousInstance(); goto Lifecycle_ModifyEnd; } result = _PostToAllSubscribers( ((LifecycleContext*)context)->provider->subMgr, &instModification.__instance, MI_LIFECYCLE_INDICATION_MODIFY); Lifecycle_ModifyEnd: if (instModification.__instance.ft) { MI_Instance_Destruct( &instModification.__instance ); } return result; } MI_Result _PostDelete( _In_ MI_LifecycleIndicationContext* context, _In_ const MI_Instance* originalInstance) { CIM_InstDeletion instDeletion; memset( &instDeletion, 0, sizeof(CIM_InstDeletion) ); return _CommonPostSimple( context, &instDeletion.__instance, originalInstance, &CIM_InstDeletion_rtti, MI_LIFECYCLE_INDICATION_DELETE ); } MI_Result _PostRead( _In_ MI_LifecycleIndicationContext* context, _In_ const MI_Instance* instance) { CIM_InstRead instRead; memset( &instRead, 0, sizeof(CIM_InstRead) ); return _CommonPostSimple( context, &instRead.__instance, instance, &CIM_InstRead_rtti, MI_LIFECYCLE_INDICATION_READ ); } MI_Result _PostMethodCall( _In_ MI_LifecycleIndicationContext* context, _In_ const MI_Instance* instance, _In_z_ const MI_Char* methodName, _In_ MI_Boolean precall, _In_opt_ const MI_Instance* parameter, _In_opt_z_ const MI_Char* returnvalue) { MI_Result result = MI_RESULT_OK; CIM_InstMethodCall instMethodCall; MI_Value value; if (!context || !instance || !methodName || ((LifecycleContext*)context)->magic != _LIFECYCLE_MAGIC) { trace_NullInputParameterForClass(tcs(CIM_InstMethodCall_rtti.name)); return MI_RESULT_INVALID_PARAMETER; } memset( &instMethodCall, 0, sizeof(CIM_InstMethodCall) ); result = _CommonIndicationPrep( context, &instMethodCall.__instance, instance, &CIM_InstMethodCall_rtti ); if (MI_RESULT_OK != result) { goto Lifecycle_MethodCallEnd; } /* MI_ConstStringField MethodName */ value.string = (MI_Char*)methodName; result = MI_Instance_SetElement( &instMethodCall.__instance, PAL_T("MethodName"), &value, MI_STRING, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_MethodName(); goto Lifecycle_MethodCallEnd; } /* MI_ConstReferenceField MethodParameters */ value.reference = (MI_Instance*)parameter; result = MI_Instance_SetElement( &instMethodCall.__instance, PAL_T("MethodParameters"), &value, MI_INSTANCE, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_MethodParameters(); goto Lifecycle_MethodCallEnd; } /* MI_ConstStringField ReturnValue */ value.string = (MI_Char*)returnvalue; result = MI_Instance_SetElement( &instMethodCall.__instance, PAL_T("ReturnValue"), &value, MI_STRING, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_ReturnValue(); goto Lifecycle_MethodCallEnd; } /* MI_ConstBooleanField PreCall */ value.boolean = precall; result = MI_Instance_SetElement( &instMethodCall.__instance, PAL_T("PreCall"), &value, MI_BOOLEAN, 0 ); if (MI_RESULT_OK != result) { trace_FailedToSet_PreCall(); goto Lifecycle_MethodCallEnd; } result = _PostToAllSubscribers( ((LifecycleContext*)context)->provider->subMgr, &instMethodCall.__instance, MI_LIFECYCLE_INDICATION_METHODCALL); Lifecycle_MethodCallEnd: if (instMethodCall.__instance.ft) { MI_Instance_Destruct( &instMethodCall.__instance ); } return result; } MI_Result _PostResult( _In_ MI_LifecycleIndicationContext* context, _In_ MI_Result result) { LifecycleContext* lifeContext = (LifecycleContext*)context; if (!context || lifeContext->magic != _LIFECYCLE_MAGIC) { trace_NullInputParameter(); return MI_RESULT_INVALID_PARAMETER; } trace_DisablingLifeCycleIndicationsForClass(tcs(lifeContext->provider->classDecl->name)); return Provider_TerminateIndication( lifeContext->provider, result, NULL, NULL ); } // TODO: Only make this callable during provider load? MI_Result MI_CALL _SetSupportedTypes( _In_ MI_LifecycleIndicationContext* context, _In_ MI_Uint32 types) { LifecycleContext* lifeContext = (LifecycleContext*)context; if (!context || lifeContext->magic != _LIFECYCLE_MAGIC) { trace_NullInputParameter(); return MI_RESULT_INVALID_PARAMETER; } lifeContext->supportedTypesFromProv = types; return MI_RESULT_OK; } MI_Result _GetLifecycleIndicationTypes( _In_ MI_LifecycleIndicationContext* context, _Out_ MI_Uint32* types) { LifecycleContext* lifeContext = (LifecycleContext*)context; if (!context || !types || lifeContext->magic != _LIFECYCLE_MAGIC) { trace_NullInputParameter(); return MI_RESULT_INVALID_PARAMETER; } *types = lifeContext->supportedTypesFromProv; return MI_RESULT_OK; } MI_Result _RegisterLifecycleCallback( _In_ MI_LifecycleIndicationContext* context, _In_ MI_LifecycleIndicationCallback callback, _In_opt_ void* callbackData) { LifecycleContext* lifeContext = (LifecycleContext*)context; if (!context || !callback || lifeContext->magic != _LIFECYCLE_MAGIC) { trace_NullInputParameter(); return MI_RESULT_INVALID_PARAMETER; } lifeContext->lifecycleCallback = callback; lifeContext->callbackData = callbackData; return MI_RESULT_OK; } static MI_Result _ValidateContext(MI_LifecycleIndicationContext* self_) { LifecycleContext* lifeContext = (LifecycleContext*)self_; if (!self_ || lifeContext->magic != _LIFECYCLE_MAGIC) { trace_NullInputParameter(); return MI_RESULT_INVALID_PARAMETER; } return MI_RESULT_OK; } static MI_Result MI_CALL _ConstructInstance( MI_LifecycleIndicationContext* self_, const MI_ClassDecl* classDecl, MI_Instance* instance) { MI_Result r = _ValidateContext(self_); if (r == MI_RESULT_OK) return Instance_Construct(instance, classDecl, NULL); return r; } static MI_Result MI_CALL _ConstructParameters( MI_LifecycleIndicationContext* self_, const MI_MethodDecl* methodDecl, MI_Instance* instance) { MI_Result r = _ValidateContext(self_); if (r == MI_RESULT_OK) return Parameters_Init(instance, methodDecl, NULL); return r; } static MI_Result MI_CALL _NewInstance( MI_LifecycleIndicationContext* self_, const MI_ClassDecl* classDecl, MI_Instance** instance) { MI_Result r = _ValidateContext(self_); if (r == MI_RESULT_OK) return Instance_New(instance, classDecl, NULL); return r; } static MI_Result MI_CALL _NewDynamicInstance( MI_LifecycleIndicationContext* self_, const ZChar* className, MI_Uint32 flags, MI_Instance** instance) { MI_Result r = _ValidateContext(self_); if (r == MI_RESULT_OK) return Instance_NewDynamic(instance, className, flags, NULL); return r; } static MI_Result MI_CALL _NewParameters( MI_LifecycleIndicationContext* self_, const MI_MethodDecl* methodDecl, MI_Instance** instance) { return Parameters_New(instance, methodDecl, NULL); } MI_LifecycleIndicationContextFT __mi_lifecycle_contextFT = { _PostCreate, _PostModify, _PostDelete, _PostRead, _PostMethodCall, _PostResult, _ConstructInstance, _ConstructParameters, _NewInstance, _NewDynamicInstance, _NewParameters, _SetSupportedTypes, _GetLifecycleIndicationTypes, _RegisterLifecycleCallback }; LifecycleContext* LifeContext_New() { LifecycleContext* context = (LifecycleContext*)PAL_Calloc(1, sizeof(LifecycleContext)); if (!context) LOGD_ALLOC_OOM; return context; } _Use_decl_annotations_ void LifeContext_Delete( LifecycleContext* context ) { if (context) { memset(context, 0xFF, sizeof(LifecycleContext)); PAL_Free(context); } } _Use_decl_annotations_ void LifeContext_Init( LifecycleContext* context, Provider* provider ) { DEBUG_ASSERT ( provider && provider->subMgr ); context->base.ft = &__mi_lifecycle_contextFT; context->magic = _LIFECYCLE_MAGIC; context->provider = provider; context->supportedTypesFromProv = MI_LIFECYCLE_INDICATION_ALL; } /* * Converts SubscriptionTargetType enum values to bit flags that are compatible * with the MI API's type field. */ _Use_decl_annotations_ MI_Uint32 LifeContext_ConvertSupportedType( SubscriptionTargetType type ) { MI_Uint32 miType = MI_LIFECYCLE_INDICATION_NONE; switch(type) { case SUBSCRIP_TARGET_LIFECYCLE_CREATE: miType = MI_LIFECYCLE_INDICATION_CREATE; break; case SUBSCRIP_TARGET_LIFECYCLE_MODIFY: miType = MI_LIFECYCLE_INDICATION_MODIFY; break; case SUBSCRIP_TARGET_LIFECYCLE_DELETE: miType = MI_LIFECYCLE_INDICATION_DELETE; break; case SUBSCRIP_TARGET_LIFECYCLE_READ: miType = MI_LIFECYCLE_INDICATION_READ; break; case SUBSCRIP_TARGET_LIFECYCLE_METHODCALL: miType = MI_LIFECYCLE_INDICATION_METHODCALL; break; case SUBSCRIP_TARGET_LIFECYCLE_ALL: miType = MI_LIFECYCLE_INDICATION_ALL; break; /* Fallthorugh intentional */ case SUBSCRIP_TARGET_UNSUPPORTED: case SUBSCRIP_TARGET_DEFAULT: default: miType = MI_LIFECYCLE_INDICATION_NONE; } return miType; } /* * In this check, any requested bits that are not supported will get zeroed, * causing the comparison against the original value to fail. */ _Use_decl_annotations_ MI_Boolean LifeContext_IsTypeSupported( LifecycleContext* context, MI_Uint32 supportedTypeToCheck ) { MI_Uint32 convertedToMiType = LifeContext_ConvertSupportedType( supportedTypeToCheck ); if ( MI_LIFECYCLE_INDICATION_NONE == context->supportedTypesFromProv || MI_LIFECYCLE_INDICATION_NONE == convertedToMiType ) { return MI_FALSE; } if ( context->supportedTypesFromProv & convertedToMiType ) { return MI_TRUE; } else { return MI_FALSE; } } // TODO: return result here and check against the provider's advertised type. _Use_decl_annotations_ void LifeContext_UpdateSupportedTypes( LifecycleContext* context ) { MI_Uint32 indicationType = MI_LIFECYCLE_INDICATION_NONE; SubscriptionManager* subMgr = NULL; SubMgrSubscription* sub = NULL; if (NULL == context->lifecycleCallback) { return; } /* Iterate through the subscriptions and gather the various subscriptions * additively. */ subMgr = context->provider->subMgr; for (sub = (SubMgrSubscription*)subMgr->subscrList.head; sub; sub = sub->next) { indicationType |= LifeContext_ConvertSupportedType( sub->msg->targetType ); if (MI_LIFECYCLE_INDICATION_ALL == indicationType) { break; /* All bits are set. There is no point to continuing the search. */ } } if (indicationType != context->supportedTypesAggr) { /* A change in registration occurred, so notify the provider. */ context->supportedTypesAggr = indicationType; context->lifecycleCallback( indicationType, context->callbackData ); } }