/*
**==============================================================================
**
** 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 "provmgr.h"
#include "context.h"
#include "AggregationContext.h"
#include "LifecycleContext.h"
#include "SubMgr.h"
#include "nioproc.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(CONFIG_POSIX)
# include
#endif
/* ATTN: libraryName is key (switch to module name) */
/* ATTN: implement module provider Unload() methods */
/* ATTN: implement propertySet */
/* Suppress cast error from 'void*' to 'MI_Main' */
#if defined(_MSC_VER)
# pragma warning(disable : 4055)
#endif
/*
**=============================================================================
**
** Local defintions
**
**=============================================================================
*/
extern MI_ContextFT __mi_contextFT;
static MI_Result MI_CALL _Server_GetVersion(MI_Uint32* version)
{
if (!version)
return MI_RESULT_INVALID_PARAMETER;
*version = MI_VERSION;
return MI_RESULT_OK;
}
static MI_Result MI_CALL _Server_GetSystemName(const ZChar** systemName)
{
#if defined(CONFIG_OS_WINDOWS)
*systemName = MI_T("unknown");
return MI_RESULT_OK;
#else
static char buf[256];
if (buf[0] == '\0')
{
if (gethostname(buf, sizeof(buf)) != 0)
return MI_RESULT_FAILED;
}
#if (MI_CHAR_TYPE == 1)
*systemName = buf;
#else
{
static ZChar wbuf[sizeof(buf)];
TcsStrlcpy(wbuf, buf, MI_COUNT(wbuf));
*systemName = wbuf;
}
#endif
return MI_RESULT_OK;
#endif
}
#if 0
static MI_ServerFT _serverFT =
{
_Server_GetVersion,
_Server_GetSystemName,
};
#endif
typedef struct InternalFT
{
ProvMgrFT provMgrFT;
MI_ServerFT serverFT;
}
InternalFT;
/* warning C4054: 'type cast': from function pointer to void* */
#if defined(_MSC_VER)
# pragma warning(disable : 4054)
#endif
static void* _FindSymbol(const char* name)
{
if (strcmp(name, "OMI_GetPath") == 0)
return (void*)&OMI_GetPath;
/* Not found */
return NULL;
}
static InternalFT _internalFT =
{
{ PROVMGRFT_MAGIC, _FindSymbol },
{
_Server_GetVersion,
_Server_GetSystemName
},
};
static MI_Server _server =
{
&_internalFT.serverFT,
&__mi_contextFT,
&__mi_instanceFT,
NULL, /* MI_PropertySetFT */
NULL, /* MI_FilterFT */
};
/*
* Try to find specific library and open it if not found,
* this function is not thread-safely operate the library list
*/
static Library* MI_CALL _OpenLibraryInternal(
ProvMgr* self,
_In_ const ProvRegEntry* proventry)
{
Library* p;
/* Search cache first */
for (p = self->head; p; p = p->next)
{
if (strcmp(p->libraryName, proventry->libraryName) == 0)
{
return p;
}
}
/* Allocate new libray object */
p = (Library*)PAL_Calloc(1, sizeof(Library));
if (!p)
return NULL;
/* Library.refs */
p->provmgr = self;
/* Open the library */
{
TChar path[PAL_MAX_PATH_SIZE];
path[0] = '\0';
Shlib_Format(path, self->providerDir, proventry->libraryName);
p->handle = Shlib_Open(path);
if (!p->handle)
{
TChar Tpath[PAL_MAX_PATH_SIZE];
if (TcsStrlcpy(Tpath, proventry->libraryName, PAL_MAX_PATH_SIZE) >= PAL_MAX_PATH_SIZE)
{
trace_SharedLib_CannotOpen(scs(proventry->libraryName));
PAL_Free(p);
return NULL;
}
trace_SharedLib_CannotOpenFirstTry(tcs(path), tcs(Shlib_Err()));
// here we are ignoring error from Shlib_Open on first attempt
NitsIgnoringError();
/* Try again */
p->handle = Shlib_Open(Tpath);
if (!p->handle)
{
trace_SharedLib_CannotOpenSecondTry(scs(proventry->libraryName), tcs(Shlib_Err()));
PAL_Free(p);
return NULL;
}
}
}
/* Lib_Open.libraryName */
Strlcpy(p->libraryName, proventry->libraryName, sizeof(p->libraryName));
p->instanceLifetimeContext = proventry->instanceLifetimeContext;
/* Invoke MI_Main() entry point */
{
MI_MainFunction statikMain;
/* Lookup symbol */
{
void* ptr = Shlib_Sym(p->handle, "MI_Main");
statikMain = (MI_MainFunction)ptr;
if (!statikMain)
{
PAL_Free(p);
trace_SharedLibrary_CannotFindSymbol(scs(proventry->libraryName), scs("MI_Main"));
return NULL;
}
}
/* Call MI_Main */
{
p->module = (*statikMain)(&_server);
if (!p->module)
{
PAL_Free(p);
trace_Provmgr_NullModulePointer(scs(proventry->libraryName), scs("MI_Main"));
return NULL;
}
if (p->module->version > MI_VERSION)
{
MI_Uint32 v = p->module->version;
PAL_Free(p);
trace_Provmgr_FailedToLoadProvider(scs(proventry->libraryName), MI_VERSION_GET_MAJOR(v), MI_VERSION_GET_MINOR(v), MI_VERSION_GET_REVISION(v), MI_MAJOR, MI_MINOR, MI_REVISION);
return NULL;
}
}
}
/* Invoke the module initialize function */
if (p->module->Load)
{
Context ctx;
MI_Result r = MI_RESULT_OK;
Context_Init(&ctx, NULL, NULL);
ctx.result = &r;
(p->module->Load)(&p->self, (MI_Context*)&ctx);
if (ctx.magic != 0xFFFFFFFF)
{
trace_ModuleLoad_FailedPostResult();
}
if (MI_RESULT_OK != r)
{
trace_FailedCallModuleLoad(r, scs(proventry->libraryName));
return NULL;
}
}
Lock_Init( &p->provlock );
/* Add library to the list */
List_Prepend(
(ListElem**)&self->head,
(ListElem**)&self->tail,
(ListElem*)p);
return p;
}
/*
* Try to find specific library and open it if not found,
* this function ensure thread-safe use of the library list
*/
static Library* MI_CALL _OpenLibrary(
ProvMgr* self,
_In_ const ProvRegEntry* proventry)
{
Library* lib;
Lock_Acquire( & self->liblock );
lib = _OpenLibraryInternal( self, proventry );
Lock_Release( & self->liblock );
return lib;
}
/*
* Try to find specific provider (class) and open it if not found,
* this function is NOT thread-safe
*/
static Provider* MI_CALL _OpenProviderInternal(
Library* self,
const ZChar* className,
Message* request)
{
Provider* p;
size_t psize = sizeof(Provider);
/* Search cache first */
for (p = self->head; p; p = p->next)
{
if (Tcscasecmp(p->classDecl->name, className) == 0)
{
Provider_Addref(p);
return p;
}
}
#ifndef DISABLE_INDICATION
/* Allocate SubscriptionManager along with Provider */
psize += sizeof( SubscriptionManager );
#endif
/* New Provider */
p = (Provider*)PAL_Calloc(1, psize);
if (!p)
{
LOGD_ALLOC_OOM;
return NULL;
}
/* Provider.refs */
p->refCounter = 1;
p->lib = self;
/* Provider.classDecl */
{
p->classDecl = SchemaDecl_FindClassDecl(self->module->schemaDecl,
className);
if (!p->classDecl || (request->tag != GetClassReqTag && !p->classDecl->providerFT))
{
PAL_Free(p);
return NULL;
}
}
#ifndef DISABLE_INDICATION
/*
* Following picture explains memory layout of provider,
* provider and submgr are sitting side by side, while
* provider.submgr sets to the submgr's memory address
*
* |---------|
* | Provider|
* | ... |
* | submgr* |---|
* |---------|<--|
* | submgr |
* | |
* |---------|
*/
p->subMgr = (SubscriptionManager*)(p + 1);
SubMgr_Init(p->subMgr, p);
#endif
if(p->classDecl->providerFT)
{
/* Call provider Load() method */
if (p->classDecl->providerFT->Load)
{
Context ctx;
MI_Result r = MI_RESULT_OK;
Context_Init(&ctx, p, NULL);
ctx.result = &r;
ctx.loadRequest = request;
Message_AddRef(ctx.loadRequest);
(*p->classDecl->providerFT->Load)(&p->self, self->self, &ctx.base);
if (ctx.magic != 0xFFFFFFFF)
{
trace_ProviderLoad_DidnotPostResult();
}
if (MI_RESULT_OK != r)
{
trace_FailedProviderLoad(r, tcs(className));
PAL_Free(p);
return NULL;
}
}
}
/* Prepend to list */
List_Prepend(
(ListElem**)&self->head,
(ListElem**)&self->tail,
(ListElem*)p);
return p;
}
/*
* Try to find specific provider (class) and open it if not found,
* this function is thread-safely operate the provider list
*/
static Provider* MI_CALL _OpenProvider(
Library* self,
const ZChar* className,
Message* request)
{
Provider* prov;
Lock_Acquire( & self->provlock );
prov = _OpenProviderInternal( self, className, request );
Lock_Release( & self->provlock );
return prov;
}
static MI_Result MI_CALL _GetProviderByClassName(
_In_ ProvMgr* self,
_In_ const ProvRegEntry* proventry,
_In_ MI_ConstString cn,
_In_ Message* request,
_Out_ Provider** provOut)
{
Library* lib;
Provider* prov;
trace_GetProviderByClassName(tcs(cn));
/* Open the library */
{
lib = _OpenLibrary(self, proventry);
if (!lib)
{
trace_OpenProviderLib_Failed(scs(proventry->libraryName));
return MI_RESULT_FAILED;
}
}
/* Open the provider */
{
prov = _OpenProvider(lib, cn, request);
if (!prov)
{
trace_OpenProvider_FailedForClass(proventry->libraryName, tcs(cn));
return MI_RESULT_FAILED;
}
}
*provOut = prov;
return MI_RESULT_OK;
}
static MI_Result _Instance_InitConvert_FromBatch(
Batch* batch,
const MI_ClassDecl* cd,
const MI_SchemaDecl* sd,
MI_Instance* inst_in,
MI_Boolean keys_only,
MI_Boolean allow_keyless_inst,
MI_Instance** instOut,
MI_Uint32 flags
)
{
MI_Instance* inst;
MI_Result r;
MI_UNUSED(sd);
/* Allocate the instance for the provider */
inst = (MI_Instance*)Batch_GetClear(batch, cd->size);
if (!inst)
{
trace_ProvMgr_AllocFailed();
return MI_RESULT_FAILED;
}
/* Convert instance name to provider's format (borrow storage) */
r = Instance_InitConvert(inst, cd, inst_in, keys_only, allow_keyless_inst, MI_FALSE,
batch, flags);
if (r != MI_RESULT_OK)
{
trace_InstanceConversionFailed(tcs(cd->name), r);
return r;
}
*instOut = inst;
return MI_RESULT_OK;
}
#ifndef DISABLE_INDICATION
/*
* Internal helper function for _Provider_InvokeSubscribe that expects all
* validation to be done beforehand.
*/
MI_Result _Provider_InvokeEnable(
_Inout_ Provider* provider,
_In_ SubscribeReq* msg )
{
SubscriptionManager* subMgr = provider->subMgr;
AggregationContext* aggrContext;
trace_EnablingIndicationsForClass(provider->classDecl->name, provider);
aggrContext = SubMgr_CreateAggrContext( subMgr );
if ( !aggrContext )
{
trace_AggregationContext_InitFailed();
return MI_RESULT_SERVER_LIMITS_EXCEEDED;
}
/*
* Set enabled to true, which will be set to false if
* posting result during EnableIndications call
*
*/
SubMgr_SetEnabled( subMgr, MI_TRUE );
(*provider->classDecl->providerFT->EnableIndications)(
provider->self,
&aggrContext->baseCtx.base,
msg->nameSpace,
msg->className);
if ( MI_FALSE == SubMgr_IsEnabled(subMgr) )
{
/*
* provider posted a final result during EnableIndications call,
* Clean up already occurred during Post handling, return here
*
*/
trace_ProviderEnableIndication_Failed();
return MI_RESULT_FAILED;
}
return MI_RESULT_OK;
}
/*
* Internal helper function for _Provider_RemoveSubscription that expects all
* validation to be done beforehand.
*/
_Use_decl_annotations_
MI_Result Provider_InvokeDisable(
Provider* provider)
{
MI_Result result = MI_RESULT_OK;
SubscriptionManager* subMgr;
MI_Boolean locked;
DEBUG_ASSERT( provider );
subMgr = provider->subMgr;
trace_Provider_InvokeDisable_Start( UintThreadID(), provider->classDecl->name, provider);
locked = SubMgr_AcquireEnableLock( subMgr, AcquireFromDisable );
if ( MI_TRUE == locked )
{
MI_Boolean wasEnabled = MI_FALSE;
if ( MI_TRUE == SubMgr_IsEnabled(subMgr) )
{
wasEnabled = MI_TRUE;
SubMgr_SetEnabled(subMgr, MI_FALSE);
}
if ( provider->classDecl->flags & MI_FLAG_INDICATION )
{
AggregationContext* aggrContext = SubMgr_RemoveAggrContext( subMgr );
if ( MI_FALSE == SubMgr_IsTerminating( subMgr ) )
{
DEBUG_ASSERT( (MI_TRUE == wasEnabled) ? (NULL != aggrContext) : (NULL == aggrContext) );
if ( aggrContext && ( MI_TRUE == wasEnabled ) )
{
provider->classDecl->providerFT->DisableIndications(
provider->self,
&aggrContext->baseCtx.base,
NULL, // TODO: Set this based on the initial SubscribeReq, but copy the value so its lifetime matches the context
provider->classDecl->name );
}
}
// else don't need to invoke DisableIndications
if ( aggrContext )
AggrContext_Delete( aggrContext );
}
else
{
// TODO: release lifecycle context
}
}
else
{
/* active subscription(s) added right before acquire the lock */
trace_Provider_InvokeDisable_AbandonSinceNewSubscriptionAdded( UintThreadID(), provider->classDecl->name, provider);
return MI_RESULT_OK;
}
/*
* Disabled the provider, reset flags,
* new subscribe request will success.
*/
SubMgr_SetTerminating( subMgr, MI_FALSE );
SubMgr_SetAllCancelledSafe( subMgr, MI_FALSE );
SubMgr_ReleaseEnableLock( subMgr );
trace_Provider_InvokeDisable_Complete( UintThreadID(), provider->classDecl->name, provider);
return result;
}
void _Provider_SubscribeFail(
_In_ SubscriptionContext* subscrContext,
_In_ SubscribeReq* msg,
_In_ MI_Result result )
{
// TODO: Replace this whole func with Cancel and propagate the result to the subscription context
PostResultMsg* finalmsg = PostResultMsg_NewAndSerialize(&msg->base.base, NULL, NULL, MI_RESULT_TYPE_MI, result);
if (finalmsg)
{
Strand_SchedulePost(&subscrContext->baseCtx.strand,&finalmsg->base);
PostResultMsg_Release(finalmsg);
/*
* subscribe failed and directly send final result to left side strand,
* thus context.c:Context_PostMessageLeft won't be called,
* so release the ref count added by SubscriptionContext.c:SubscrContext_Init
*/
SubMgrSubscription_Release( subscrContext->subscription );
}
else
{
trace_OutOfMemory();
Strand_ScheduleCancel(&subscrContext->baseCtx.strand);
}
}
_Use_decl_annotations_
void Provider_InvokeSubscribe(
Provider* provider,
SubscribeReq* msg,
SubscriptionContext* subscrContext )
{
MI_Result result = MI_RESULT_OK;
SubscriptionManager* subMgr;
MI_Boolean locked;
SubscriptionTargetType subType;
SubMgrSubscription* subscription = NULL;
DEBUG_ASSERT ( provider && subscrContext );
DEBUG_ASSERT( provider->subMgr );
STRAND_ASSERTONSTRAND(&subscrContext->baseCtx.strand);
if( subscrContext->baseCtx.strand.canceled )
{
// abort creating subscription
Strand_Leave( &subscrContext->baseCtx.strand );
return;
}
subMgr = provider->subMgr;
subType = (SubscriptionTargetType)msg->targetType;
do
{
trace_ProviderInvokeSubscribe_Begin(UintThreadID(), provider, msg, msg->base.base.tag, subscrContext );
/* To ensure enable/cancel thread safe */
locked = SubMgr_AcquireEnableLock( subMgr, AcquireFromSubscribe );
if ( MI_FALSE == locked )
{
result = MI_RESULT_FAILED;
Strand_Leave( &subscrContext->baseCtx.strand );
break;
}
/*
* Sanity checks of the request prior to initializing the context and
* completing the interaction open.
*
* These checks can return immediate failure without clean up
*/
if (SUBSCRIP_TARGET_DEFAULT == subType )
{
/*
* if class is not indication class, then indication manager has a bug
*/
DEBUG_ASSERT (provider->classDecl->flags & MI_FLAG_INDICATION);
/*
* if classdecl is not valid, then omireg tool has a bug
*/
DEBUG_ASSERT (
provider->classDecl->providerFT->EnableIndications &&
provider->classDecl->providerFT->Subscribe);
}
else if (SubscriptionTargetType_IsLifecycle( subType ))
{
// lifecycleCtx should have been initialized during provider Load().
if ( ! subMgr->lifecycleCtx )
{
trace_LifecycleSubscription_ContextNotInitialized(provider->classDecl->name);
result = MI_RESULT_FAILED;
break;
}
if ( ! LifeContext_IsTypeSupported(subMgr->lifecycleCtx, msg->targetType) )
{
trace_LifecycleSubscription_UnsupportedTargetType(
provider->classDecl->name, msg->targetType, subMgr->lifecycleCtx->supportedTypesFromProv);
result = MI_RESULT_NOT_SUPPORTED;
Strand_Leave( &subscrContext->baseCtx.strand );
break;
}
}
else
{
trace_InvokeSubscribeWithInvalidTargetType( subType );
result = MI_RESULT_FAILED;
Strand_Leave( &subscrContext->baseCtx.strand );
break;
}
subscription = subscrContext->subscription;
SubscriptionList_AddSubscription( &subMgr->subscrList, subscription );
/*
* Upon SubscriptionContext was initialized successfull,
* it hold one refcount of msg, which will be released
* inside _Context_Destory;
*
* SubscriptionContext was initialized successfully,
* following logic should report error through
* interaction interface then.
*/
/*
* Acquire post lock, so post indication and result from provider's thread
* will be blocked until Enable/Subscribe call finished
*
*/
SubMgrSubscription_Addref( subscription );
SubMgrSubscription_AcuquirePostLock( subscription );
/* Alert indication setup */
if (SUBSCRIP_TARGET_DEFAULT == msg->targetType )
{
SubMgr_SetEnableThread( subMgr );
Strand_Leave( &subscrContext->baseCtx.strand );
/*
* EnableIndications must be called for each provider
* prior to the first Subscribe call for alert indication
*/
if ( MI_FALSE == SubMgr_IsEnabled( subMgr ) )
{
result = _Provider_InvokeEnable( provider, msg );
if (MI_RESULT_OK != result)
{
trace_EnableIndication_Failed(provider->classDecl->name);
result = MI_RESULT_OK;
break;
}
}
DEBUG_ASSERT (SubMgr_IsEnabled( subMgr ));
SubMgrSubscription_SetState(subscription, SubscriptionState_Subscribed);
/*
* Invoke Subscribe with dummpy context
* provider cannot postinstance or indication to this context
*/
{
Context ctx;
MI_Result r = MI_RESULT_OK;
Subunsub_Context_Init(&ctx, &r, &msg->base);
(*provider->classDecl->providerFT->Subscribe)(
provider->self,
&ctx.base,
msg->nameSpace,
msg->className,
&subscrContext->subscription->filter->base,
msg->bookmark,
msg->subscriptionID,
(void**)&subscrContext->subself);
Context_Close(&ctx);
}
if (SubscriptionState_Subscribed == subscrContext->subscription->state)
{
trace_Provider_InvokeSubscribe(subscrContext->subscription);
/*
* Subscribe succeeded, send subscribe response,
* otherwise finalresult already sent within Subscribe call
*/
SubscrContext_SendSubscribeResponseMsg( subscrContext );
#if defined(_MSC_VER)
trace_SubscrForEvents_Succeeded_MSC(provider->classDecl->name, msg->subscriptionID);
#else
trace_SubscrForEvents_Succeeded(provider->classDecl->name, msg->subscriptionID);
#endif
result = MI_RESULT_OK;
break;
}
}
/*
* Lifecycle indication setup
*/
else if (SubscriptionTargetType_IsLifecycle( (SubscriptionTargetType)msg->targetType ))
{
if ( MI_FALSE == SubMgr_IsEnabled( subMgr ) )
SubMgr_SetEnabled( subMgr, MI_TRUE );
SubMgrSubscription_SetState( subscrContext->subscription, SubscriptionState_Subscribed );
LifeContext_UpdateSupportedTypes( subMgr->lifecycleCtx );
Strand_Leave( &subscrContext->baseCtx.strand );
SubscrContext_SendSubscribeResponseMsg( subscrContext );
#if defined(_MSC_VER)
trace_SubscrForLifecycle_Succeeded_MSC(provider->classDecl->name, msg->subscriptionID);
#else
trace_SubscrForLifecycle_Succeeded(provider->classDecl->name, msg->subscriptionID);
#endif
result = MI_RESULT_OK;
break;
}
}
while( 0 );
if ( subscription )
{
/*
* Release post lock, so indication and result
* posted from provider will go through
*/
SubMgrSubscription_ReleasePostLock( subscription );
SubMgrSubscription_Release( subscription );
}
/*
* Now release lock to allow Disable/other subscribe reqeuest to conitune
*/
if ( MI_TRUE == locked )
SubMgr_ReleaseEnableLock( subMgr );
trace_ProviderInvokeSubscribe_End(UintThreadID(), provider, result);
/*
* Once the SubscrContext has been initialized, the interactions is "Open"
* and must be shut down using the appropriate Strand calls. Those calls
* will handle clean up. This must return MI_RESULT_OK for that to happen.
*/
if ( result != MI_RESULT_OK )
{
_Provider_SubscribeFail( subscrContext, msg, result );
}
}
/*
* Invoke EnableIndication if not called yet;
* and invoke Subscribe call to provider;
*
* To ensure enable/disable thread-safe, and since
* OMI has single IO thread, this function has to be scheduled
* on separate thread for OMI.
* TODO: remove separate thread if have multi-IO threads implemented
*/
MI_Result _Provider_InvokeSubscribeWrapper(
_In_ Provider* provider,
_Inout_ InteractionOpenParams* interactionParams )
{
SubscriptionContext* subscrContext;
MI_Result result;
result = SubMgr_CreateSubscription( provider->subMgr, provider, interactionParams, &subscrContext );
if ( MI_RESULT_OK != result )
{
trace_FailedToAddSubscrMgr();
return result;
}
//#if defined(_MSC_VER)
// Strand_ScheduleAux( &(subscrContext->baseCtx.strand), CONTEXT_STRANDAUX_INVOKESUBSCRIBE );
//#else
result = Schedule_SubscribeRequest( provider, (SubscribeReq*)interactionParams->msg, subscrContext );
if( MI_RESULT_OK != result )
{
_Provider_SubscribeFail( subscrContext, (SubscribeReq*)interactionParams->msg, result );
/*
* schedule failed so CONTEXT_STRANDAUX_INVOKESUBSCRIBE won't be called,
* so release the ref count added by _SubMgrSubscription_New
*/
SubMgrSubscription_Release( subscrContext->subscription );
}
//#endif
return MI_RESULT_OK;
}
_Use_decl_annotations_
MI_Result Provider_RemoveSubscription(
Provider* provider,
MI_Uint64 subscriptionID)
{
SubMgrSubscription* subscription = NULL;
SubscriptionManager* subMgr;
MI_Result result = MI_RESULT_OK;
DEBUG_ASSERT (provider && provider->subMgr);
subMgr = provider->subMgr;
#if defined(_MSC_VER)
trace_RemovingSubscriptionForClass_MSC(subscriptionID, provider->classDecl->name);
#else
trace_RemovingSubscriptionForClass(subscriptionID, provider->classDecl->name);
#endif
subscription = SubMgr_GetSubscription( subMgr, subscriptionID );
if (NULL == subscription)
{
/* Not present or not found means that there is nothing to do. */
return MI_RESULT_OK;
}
SubMgrSubscription_SetState(subscription, SubscriptionState_Unsubscribed);
result = SubMgr_DeleteSubscription( subMgr, subscription);
/* check if the subscription list was empty or not */
if ( MI_TRUE == SubMgr_IsSubListEmpty( subMgr ) )
{
/*
* The specified subscription is the LAST one for its
* SubscriptionManager. Indications should be disabled for this
* provider. There is separate cleanup for Lifecycle and "normal"
* indications. SubscriptionManager will be cleaned up once the
* provider processes the Disable call.
*/
result = Provider_InvokeDisable ( provider );
}
return result;
}
_Use_decl_annotations_
MI_Result Provider_TerminateIndication(
Provider* provider,
MI_Result result,
const ZChar* errorMessage,
const MI_Instance* cimError )
{
SubscriptionManager* subMgr;
MI_Boolean locked;
DEBUG_ASSERT (provider && provider->subMgr);
trace_Provider_TerminateIndication_Start( UintThreadID(), provider->classDecl->name, provider);
Provider_Addref( provider );
subMgr = provider->subMgr;
/*
* SubMgr_AcquireEnableLock cancel all subscriptions if have any,
* internally it call Provider_InvokeDisable if no subscription;
* otherwise just return;
* Provider_InvokeDisable releases the aggregation context;
*/
locked = SubMgr_AcquireEnableLock( subMgr, AcquireFromTerminate );
DEBUG_ASSERT( MI_TRUE == locked );
SubMgr_ReleaseEnableLock( subMgr );
trace_Provider_TerminateIndication_Complete( UintThreadID(), provider->classDecl->name, provider);
Provider_Release( provider );
return MI_RESULT_OK;
}
#endif /* ifndef DISABLE_INDICATION */
static MI_Result _HandleGetInstanceReq(
_In_ ProvMgr* self,
_In_ const ProvRegEntry* proventry,
_Inout_ InteractionOpenParams* interactionParams,
_Out_ Provider** prov)
{
MI_Instance* inst;
MI_Result r;
const ZChar* className;
GetInstanceReq* msg = (GetInstanceReq*)interactionParams->msg;
/* Get classname from instance */
r = __MI_Instance_GetClassName(msg->instanceName, &className);
if (r != MI_RESULT_OK)
return r;
/* find provider */
r = _GetProviderByClassName(
self,
proventry,
className,
&msg->base.base,
prov);
if ( MI_RESULT_OK != r )
return r;
/* Allocate the instance for the provider */
r = _Instance_InitConvert_FromBatch(
msg->base.base.batch,
(*prov)->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instanceName,
MI_TRUE,
MI_FALSE,
&inst,
msg->base.base.flags);
if (MI_RESULT_OK != r)
return r;
#if 0
/* Print instance */
Instance_Print(inst, stdout, 0, MI_FALSE, MI_FALSE);
#endif
/* If provider's GetInstance method null, use EnumerateInstances */
if ((*prov)->classDecl->providerFT->GetInstance == NULL)
{
Context* ctx;
if ((*prov)->classDecl->providerFT->EnumerateInstances == NULL)
return MI_RESULT_INVALID_CLASS;
/* Create context */
ctx = (Context*)Batch_GetClear(msg->base.base.batch, sizeof(Context));;
r = Context_Init(ctx, (*prov), interactionParams);
if( MI_RESULT_OK != r )
return r;
/* _PostInstance() filters by this if not null */
ctx->instanceName = inst;
/* Invoke provider */
(*(*prov)->classDecl->providerFT->EnumerateInstances)(
(*prov)->self,
&ctx->base,
msg->nameSpace,
className,
NULL, /* propertSet */
MI_FALSE, /* keysOnly */
NULL); /* filter */
}
else
{
Context* ctx;
/* Create context */
ctx = (Context*)Batch_GetClear(msg->base.base.batch, sizeof(Context));;
r = Context_Init(ctx, (*prov), interactionParams);
if( MI_RESULT_OK != r )
return r;
/* Invoke provider */
(*(*prov)->classDecl->providerFT->GetInstance)(
(*prov)->self,
&ctx->base,
msg->nameSpace,
className,
inst,
NULL);
}
return MI_RESULT_OK;
}
static void _PostClassToCallback(
Context* self,
InteractionOpenParams* params,
const MI_Class* schema)
{
MI_Result result = MI_RESULT_OK;
PostSchemaMsg* resp = PostSchemaMsg_New(self->request->base.operationId);
if (!resp)
{
trace_PostSchemaMsg_Failed();
result = MI_RESULT_FAILED;
goto End;
}
if (self->request->base.flags & WSMANFlag)
{
result = WSBuf_ClassToBuf(
schema,
self->request->base.flags,
resp->base.batch,
&resp->packedSchemaWsmanPtr,
&resp->packedSchemaWsmanSize);
trace_ProvMgr_PostingSchemaInWsmanToCallback();
if(result != MI_RESULT_OK)
{
trace_SchemaConversion_ToCimXmlFailed(result);
goto End;
}
resp->base.flags |= self->request->base.flags;
}
else
{
result = Instance_New(&resp->schemaInstance, schema->classDecl, resp->base.batch);
if (result != MI_RESULT_OK)
{
trace_SchemaConversion_ToInstanceFailed(result);
goto End;
}
result = InstanceToBatch(
resp->schemaInstance,
NULL,
NULL,
resp->base.batch,
&resp->packedSchemaInstancePtr,
&resp->packedSchemaInstanceSize);
if (result != MI_RESULT_OK)
{
trace_SchemaInstancePackaging_Failed(result);
goto End;
}
resp->base.flags |= BinaryProtocolFlag;
}
Context_CompleteOpen( self, params, MI_RESULT_OK );
Context_PostSchema( self, &resp->base);
{
PostResultMsg* msg = PostResultMsg_New( self->request->base.operationId );
if (msg)
{
Context_PostMessageLeft( self, &msg->base );
PostResultMsg_Release( msg );
}
}
Strand_ScheduleClose(&self->strand);
End:
if(resp)
{
PostSchemaMsg_Release(resp);
}
if( MI_RESULT_OK != result )
{
Context_CompleteOpen( self, params, result );
}
}
static MI_Result _HandleGetClassReq(
_In_ ProvMgr* self,
_In_ const ProvRegEntry* proventry,
_Inout_ InteractionOpenParams* interactionParams,
_Out_ Provider** prov)
{
MI_Result r;
MI_Class resultClass;
Context* ctx = NULL;
GetClassReq* msg = (GetClassReq*)interactionParams->msg;
trace_ProvMgr_GetClassReq(tcs(msg->className), tcs(msg->nameSpace));
memset(&resultClass, 0, sizeof(MI_Class));
/* find provider */
r = _GetProviderByClassName(
self,
proventry,
msg->className,
&msg->base.base,
prov);
if ( MI_RESULT_OK != r )
return r;
ctx = (Context*)Batch_GetClear(msg->base.base.batch,
sizeof(Context));;
r = Context_PartialInit(ctx, (*prov), interactionParams);
if( MI_RESULT_OK != r )
return r;
// by default serializer will use the function tables to access resultClass as a wrapper to MI_ClassDecl
// this place will be modified in future to fill in the appropriate extended function tables
// providing access to schema in form of instance of cimclass
Class_Construct(&resultClass, (*prov)->classDecl);
_PostClassToCallback(ctx, interactionParams, &resultClass);
return MI_RESULT_OK;
}
static MI_Result _HandleCreateInstanceReq(
_In_ ProvMgr* self,
_In_ const ProvRegEntry* proventry,
_Inout_ InteractionOpenParams* interactionParams,
_Out_ Provider** prov)
{
MI_Instance* inst;
MI_Result r;
const ZChar* className;
CreateInstanceReq* msg = (CreateInstanceReq*)interactionParams->msg;
/* Get classname from instance */
r = __MI_Instance_GetClassName(msg->instance, &className);
if (r != MI_RESULT_OK)
return r;
/* find provider */
r = _GetProviderByClassName(
self,
proventry,
className,
&msg->base.base,
prov);
if ( MI_RESULT_OK != r )
return r;
/* Allocate the instance for the provider */
r = _Instance_InitConvert_FromBatch(
msg->base.base.batch,
(*prov)->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instance,
MI_FALSE,
MI_TRUE,
&inst,
msg->base.base.flags);
if (MI_RESULT_OK != r)
return r;
/* Invoke provider */
if (!(*prov)->classDecl->providerFT->CreateInstance)
return MI_RESULT_INVALID_CLASS;
{
Context* ctx = (Context*)Batch_GetClear(msg->base.base.batch,
sizeof(Context));;
Context_Init(ctx, (*prov), interactionParams);
if( MI_RESULT_OK != r )
return r;
/* message will be freed in context release*/
(*(*prov)->classDecl->providerFT->CreateInstance)((*prov)->self, &ctx->base,
msg->nameSpace, className, inst);
}
return MI_RESULT_OK;
}
static MI_Result _HandleModifyInstanceReq(
_In_ ProvMgr* self,
_In_ const ProvRegEntry* proventry,
_Inout_ InteractionOpenParams* interactionParams,
_Out_ Provider** prov)
{
MI_Instance* inst;
MI_Result r;
const ZChar* className;
ModifyInstanceReq* msg = (ModifyInstanceReq*)interactionParams->msg;
/* Get classname from instance */
r = __MI_Instance_GetClassName(msg->instance, &className);
if (r != MI_RESULT_OK)
return r;
/* find provider */
r = _GetProviderByClassName(
self,
proventry,
className,
&msg->base.base,
prov);
if ( MI_RESULT_OK != r )
return r;
/* Allocate the instance for the provider */
r = _Instance_InitConvert_FromBatch(
msg->base.base.batch,
(*prov)->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instance,
MI_FALSE,
MI_FALSE,
&inst,
msg->base.base.flags);
if (MI_RESULT_OK != r)
return r;
/* Invoke provider */
if (!(*prov)->classDecl->providerFT->ModifyInstance)
return MI_RESULT_INVALID_CLASS;
{
Context* ctx = (Context*)Batch_GetClear(msg->base.base.batch,
sizeof(Context));;
r = Context_Init(ctx, (*prov), interactionParams);
if( MI_RESULT_OK != r )
return r;
/* Need to store instance in case we need to call GetInstance */
ctx->keyInstance = inst;
/* message will be freed in context release*/
(*(*prov)->classDecl->providerFT->ModifyInstance)((*prov)->self, &ctx->base,
msg->nameSpace, className, inst, NULL);
}
return MI_RESULT_OK;
}
static MI_Result _HandleDeleteInstanceReq(
_In_ ProvMgr* self,
_In_ const ProvRegEntry* proventry,
_Inout_ InteractionOpenParams* interactionParams,
_Out_ Provider** prov)
{
MI_Instance* inst;
MI_Result r;
const ZChar* className;
DeleteInstanceReq* msg = (DeleteInstanceReq*)interactionParams->msg;
/* Get classname from instance */
r = __MI_Instance_GetClassName(msg->instanceName, &className);
if (r != MI_RESULT_OK)
return r;
/* find provider */
r = _GetProviderByClassName(
self,
proventry,
className,
&msg->base.base,
prov);
if ( MI_RESULT_OK != r )
return r;
/* Allocate the instance for the provider */
r = _Instance_InitConvert_FromBatch(
msg->base.base.batch,
(*prov)->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instanceName,
MI_TRUE,
MI_FALSE,
&inst,
msg->base.base.flags);
if (MI_RESULT_OK != r)
return r;
/* Invoke provider */
if (!(*prov)->classDecl->providerFT->DeleteInstance)
return MI_RESULT_INVALID_CLASS;
{
Context* ctx = (Context*)Batch_GetClear(msg->base.base.batch,
sizeof(Context));;
r = Context_Init(ctx, (*prov), interactionParams);
if( MI_RESULT_OK != r )
return r;
/* message will be freed in context release*/
(*(*prov)->classDecl->providerFT->DeleteInstance)((*prov)->self, &ctx->base,
msg->nameSpace, className, inst);
}
return MI_RESULT_OK;
}
#ifndef DISABLE_INDICATION
/*
* Behaves conditionally depending on whether or not indications were
* compiled into the product.
*/
static MI_Result _HandleSubscribeReq(
_In_ ProvMgr* self,
_In_ const ProvRegEntry* proventry,
_Inout_ InteractionOpenParams* interactionParams,
_Out_ Provider** prov)
{
MI_Result r;
SubscribeReq* msg = (SubscribeReq*)interactionParams->msg;
*prov = NULL;
/* find provider */
r = _GetProviderByClassName(
self,
proventry,
msg->className,
&msg->base.base,
prov);
if ( MI_RESULT_OK != r )
{
trace_ProvMgr_ClassNotFound( tcs(msg->className) );
return r;
}
return _Provider_InvokeSubscribeWrapper( *prov, interactionParams );
}
#endif /* ifndef DISABLE_INDICATION */
static MI_Result _HandleInvokeReq(
_In_ ProvMgr* self,
_In_ const ProvRegEntry* proventry,
_Inout_ InteractionOpenParams* interactionParams,
_Out_ Provider** prov)
{
MI_Instance* inst = 0;
MI_Instance* instParams = 0;
MI_Result r;
MI_ConstString cn = 0;
MI_MethodDecl* md = 0;
InvokeReq* msg = (InvokeReq*)interactionParams->msg;
#if 0
MessagePrint(&msg->base, stdout);
#endif
/* parameter validation */
if (!msg || !msg->function)
return MI_RESULT_INVALID_PARAMETER;
if (msg->className)
cn = msg->className;
else if (msg->instance)
cn = ((Instance*) msg->instance)->classDecl->name;
if (!cn)
return MI_RESULT_INVALID_CLASS;
/* find provider */
r = _GetProviderByClassName(
self,
proventry,
cn,
&msg->base.base,
prov);
if ( MI_RESULT_OK != r )
return r;
/* find method declaration */
md = SchemaDecl_FindMethodDecl( (*prov)->classDecl, msg->function );
if (!md)
return MI_RESULT_FAILED;
/* if method is not static, instance must be provided */
if (!msg->instance && (md->flags & MI_FLAG_STATIC) != MI_FLAG_STATIC)
return MI_RESULT_INVALID_PARAMETER;
if (msg->instance)
{
r = _Instance_InitConvert_FromBatch(
msg->base.base.batch,
(*prov)->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instance,
MI_TRUE,
MI_FALSE,
&inst,
msg->base.base.flags);
if (MI_RESULT_OK != r)
return r;
}
if (msg->instanceParams)
{
/* paramters (if any) */
r = _Instance_InitConvert_FromBatch(
msg->base.base.batch,
(const MI_ClassDecl*)md,
(*prov)->lib->module->schemaDecl,
msg->instanceParams,
MI_FALSE,
MI_TRUE,
&instParams,
msg->base.base.flags);
if (MI_RESULT_OK != r)
return r;
}
#if 0
/* Print instance */
Instance_Print(inst, stdout, 0, 0, 0);
#endif
/* Invoke provider */
if (!md->function)
return MI_RESULT_INVALID_CLASS;
{
Context* ctx = (Context*)Batch_GetClear(msg->base.base.batch, sizeof(Context));
r = Context_Init(ctx, (*prov), interactionParams);
if( MI_RESULT_OK != r )
return r;
/* call get first if fn is non-static */
/*if (inst && (*prov)->classDecl->providerFT->GetInstance)
{
ctx->ctxType = CTX_TYPE_INVOKE_WITH_INSTANCE;
ctx->inst = inst;
ctx->instParams = instParams;
ctx->md = md;
ctx->prov_self = (*prov)->self;
(*(*prov)->classDecl->providerFT->GetInstance)((*prov)->self, &ctx->base,
__nameSpace, __className, inst, NULL);
}
else */ /* for static - call invoke directly */
(*md->function)((*prov)->self, &ctx->base,
msg->nameSpace, cn, msg->function, inst, instParams);
}
return MI_RESULT_OK;
}
static MI_Result _HandleEnumerateInstancesReq(
_In_ ProvMgr* self,
_In_ const ProvRegEntry* proventry,
_Inout_ InteractionOpenParams* interactionParams,
_Out_ Provider** prov)
{
Context* ctx;
MI_Result r;
EnumerateInstancesReq* msg = (EnumerateInstancesReq*)interactionParams->msg;
InstanceFilter* instanceFilter = NULL;
MI_Filter* filter = NULL;
#if 0
MessagePrint(&msg->base, stdout);
#endif
/* find provider */
r = _GetProviderByClassName(
self,
proventry,
msg->className,
&msg->base.base,
prov);
if ( MI_RESULT_OK != r )
return r;
DEBUG_ASSERT(msg->wql == NULL);
/* Get WQL query, if any */
if (msg->queryLanguage != NULL && msg->queryExpression != NULL)
{
/* Create filter then Get WQL query */
instanceFilter = InstanceFilter_New( &(msg->base.base) );
if (instanceFilter == NULL)
{
trace_InstanceFilter_AllocFailed();
return MI_RESULT_INVALID_QUERY;
}
filter = &(instanceFilter->base);
msg->wql = InstanceFilter_GetWQL(instanceFilter);
}
/* Validate WQL query (if any) against provider's class declaration */
if (msg->wql)
{
if (WQL_Validate(msg->wql, (*prov)->classDecl) != 0)
{
trace_QueryValidationFailed(tcs(msg->wql->text));
return MI_RESULT_INVALID_QUERY;
}
}
/* Invoke provider */
if (!(*prov)->classDecl->providerFT->EnumerateInstances)
return MI_RESULT_NOT_SUPPORTED;
/* Create the context object */
{
ctx = (Context*)Batch_GetClear(
msg->base.base.batch, sizeof(Context));;
if (!ctx)
{
trace_ProvMgr_AllocFailed();
return MI_RESULT_FAILED;
}
r = Context_Init(ctx, (*prov), interactionParams);
if( MI_RESULT_OK != r )
return r;
}
trace_ProvMgr_EnumerateInstancesOfClass( tcs(msg->className) );
(*(*prov)->classDecl->providerFT->EnumerateInstances)(
(*prov)->self, &ctx->base,
msg->nameSpace, msg->className, NULL, MI_FALSE, filter);
return MI_RESULT_OK;
}
static MI_Result MI_CALL _HandleAssociatorsOfReq(
_In_ ProvMgr* self,
_In_ const ProvRegEntry* proventry,
_Inout_ InteractionOpenParams* interactionParams,
_Out_ Provider** prov)
{
Context* ctx;
MI_Result r;
MI_Instance* inst = 0;
AssociationsOfReq* msg = (AssociationsOfReq*)interactionParams->msg;
/* find provider */
r = _GetProviderByClassName(
self,
proventry,
msg->className,
&msg->base.base,
prov);
if ( MI_RESULT_OK != r )
return r;
/* Invoke provider */
if (!(*prov)->classDecl->providerFT->AssociatorInstances)
return MI_RESULT_NOT_SUPPORTED;
if (!msg->instance)
return MI_RESULT_INVALID_PARAMETER;
{
Provider* provInst = 0;
r = _GetProviderByClassName(
self,
proventry,
((Instance*) msg->instance)->classDecl->name,
&msg->base.base,
&provInst );
if ( MI_RESULT_OK != r )
return r;
r = _Instance_InitConvert_FromBatch(
msg->base.base.batch,
provInst->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instance,
MI_TRUE,
MI_FALSE,
&inst,
msg->base.base.flags);
Provider_Release(provInst);
if (MI_RESULT_OK != r)
return r;
}
ctx = (Context*)Batch_GetClear(
msg->base.base.batch, sizeof(Context));;
if (!ctx)
{
trace_ProvMgr_AllocFailed();
return MI_RESULT_FAILED;
}
r = Context_Init(ctx, (*prov), interactionParams);
if( MI_RESULT_OK != r )
return r;
/* message will be freed in context release */
(*(*prov)->classDecl->providerFT->AssociatorInstances)(
(*prov)->self,
&ctx->base,
msg->nameSpace,
msg->className,
inst,
msg->resultClass,
msg->role,
msg->resultRole,
NULL, MI_FALSE, NULL);
return MI_RESULT_OK;
}
static MI_Result _HandleReferencesOfReq(
_In_ ProvMgr* self,
_In_ const ProvRegEntry* proventry,
_Inout_ InteractionOpenParams* interactionParams,
_Out_ Provider** prov)
{
Context* ctx;
MI_Result r;
MI_Instance* inst = 0;
AssociationsOfReq* msg = (AssociationsOfReq*)interactionParams->msg;
/* find provider */
r = _GetProviderByClassName(
self,
proventry,
msg->className,
&msg->base.base,
prov );
if ( MI_RESULT_OK != r )
return r;
/* Invoke provider */
if (!(*prov)->classDecl->providerFT->ReferenceInstances)
return MI_RESULT_NOT_SUPPORTED;
if (!msg->instance)
return MI_RESULT_INVALID_PARAMETER;
{
Provider* provInst = 0;
r = _GetProviderByClassName(
self,
proventry,
((Instance*) msg->instance)->classDecl->name,
&msg->base.base,
&provInst );
if ( MI_RESULT_OK != r )
return r;
r = _Instance_InitConvert_FromBatch(
msg->base.base.batch,
provInst->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instance,
MI_TRUE,
MI_FALSE,
&inst,
msg->base.base.flags);
Provider_Release(provInst);
if (MI_RESULT_OK != r)
return r;
}
ctx = (Context*)Batch_GetClear(
msg->base.base.batch, sizeof(Context));;
if (!ctx)
{
trace_ProvMgr_AllocFailed();
return MI_RESULT_FAILED;
}
r = Context_Init(ctx, (*prov), interactionParams);
if( MI_RESULT_OK != r )
return r;
/* message will be freed in context release */
(*(*prov)->classDecl->providerFT->ReferenceInstances)(
(*prov)->self,
&ctx->base,
msg->nameSpace,
msg->className,
inst,
msg->role,
NULL, MI_FALSE, NULL);
return MI_RESULT_OK;
}
static void _UnloadAllProviders(
ProvMgr* self,
Library* lib,
MI_Boolean idleOnly,
MI_Uint64 currentTimeUsec,
MI_Uint64* nextFireAtTime)
{
Provider* p, *p_next;
for (p = lib->head; p; )
{
/* ATTN: idleSince is sometimes 0 */
MI_Uint64 provFireAtTime;
if (p->idleSince != 0)
provFireAtTime = p->idleSince + self->idleTimeoutUsec;
else
provFireAtTime = ~((MI_Uint64)0);
p_next = p->next;
/* unload if 'force' option passed or provider is idle long enough */
if (!idleOnly ||
(!p->refusedUnload && 0 == p->refCounter && provFireAtTime <= currentTimeUsec))
{
trace_ProvMgr_UnloadingProvider( tcs(p->classDecl->name) );
#ifndef DISABLE_INDICATION
if (p->subMgr)
{
if (p->subMgr->lifecycleCtx)
{
LifeContext_Delete(p->subMgr->lifecycleCtx);
p->subMgr->lifecycleCtx = NULL;
}
}
#endif /* ifndef DISABLE_INDICATION */
/* Call provider unload() method */
if (p->classDecl->providerFT && p->classDecl->providerFT->Unload)
{
Context ctx;
Context_Init(&ctx, 0, NULL);
(*p->classDecl->providerFT->Unload)(p->self, &ctx.base);
DEBUG_ASSERT(ctx.magic == (MI_Uint32)-1);
}
if (p->refCounter != 0)
{
/* Error condition - unloading active rpovider! */
trace_UnloadingActiveProvider(
tcs(p->classDecl->name), (int)p->refCounter);
trace_UnloadingActiveProviderWithLib(
scs(lib->libraryName), (int)p->refCounter);
/* ATTN: _exit is a good option here, since provider's behavior maybe undefined */
trace_UnloadingActiveProvider_ServerExit(scs(lib->libraryName));
_exit(1);
}
List_Remove(
(ListElem**)&lib->head,
(ListElem**)&lib->tail,
(ListElem*)p);
Provider_Finalize( p );
PAL_Free(p);
}
else if (idleOnly && 0 == p->refCounter && nextFireAtTime)
{
/* re-calculate idle timeout */
if (provFireAtTime < *nextFireAtTime)
*nextFireAtTime = provFireAtTime;
}
/* move to next one */
p = p_next;
}
}
/*
* Unload all libraries *NOT* thread safely
*/
static void _UnloadAllLibrariesInternal(
ProvMgr* self,
MI_Boolean idleOnly,
MI_Uint64 currentTimeUsec,
MI_Uint64* nextFireAtTime)
{
Library* p, *p_next;
/* release all (or idle-only) opened libraries */
for (p = self->head; p; )
{
p_next = p->next;
Lock_Acquire( &p->provlock );
_UnloadAllProviders(self,p,idleOnly,currentTimeUsec,nextFireAtTime);
Lock_Release( &p->provlock );
/* Unload libraries that have no loaded providers */
if (!p->head)
{
/* Invoke the module un-initialize function */
if (p->module->Unload)
{
Context ctx;
MI_Result r = MI_RESULT_OK;
Context_Init(&ctx, NULL, NULL);
ctx.result = &r;
(p->module->Unload)(p->self, (MI_Context*)&ctx);
if (ctx.magic != 0xFFFFFFFF)
{
trace_LibraryUnload_DidnotPostResult();
}
if (MI_RESULT_OK != r)
{
trace_FailedCallLibraryUnload(r, scs(p->libraryName));
}
}
Shlib_Close(p->handle);
trace_ProvMgr_UnloadingLibrary( scs(p->libraryName) );
List_Remove(
(ListElem**)&self->head,
(ListElem**)&self->tail,
(ListElem*)p);
PAL_Free(p);
}
p = p_next;
}
}
/*
* Unload all libraries thread safely
*/
static void _UnloadAllLibraries(
ProvMgr* self,
MI_Boolean idleOnly,
MI_Uint64 currentTimeUsec,
MI_Uint64* nextFireAtTime)
{
Lock_Acquire( &self->liblock );
_UnloadAllLibrariesInternal( self, idleOnly, currentTimeUsec, nextFireAtTime );
// If all libraries are gone, notify caller
if (!self->head && self->idleCallback)
(*self->idleCallback)(self,self->idleCallbackData);
Lock_Release( &self->liblock );
}
/*
Timeout handler: unloads idle providers and libraries,
re-calculates new timeout (for next provider),
notifies server/agent if all libraries are unloaded
*/
static MI_Boolean _TimeoutCallback(
Selector* sel,
Handler* handler,
MI_Uint32 mask,
MI_Uint64 currentTimeUsec)
{
MI_UNUSED(sel);
if (mask & SELECTOR_TIMEOUT)
{
ProvMgr* self = (ProvMgr*)handler->data;
const MI_Uint64 u64max = ~ MI_ULL(0);
MI_Uint64 nextFireAtTime = u64max;
/* Unload all idle providers */
/* trace_UnloadingIdleProviders(); */
_UnloadAllLibraries(self, MI_TRUE, currentTimeUsec, &nextFireAtTime);
if (u64max != nextFireAtTime)
{
/* re-set timeout */
handler->fireTimeoutAt = nextFireAtTime;
}
else
{
/* disbale timeout, since no more idle providers */
handler->fireTimeoutAt = TIME_NEVER;
}
return MI_TRUE;
}
if (mask & (SELECTOR_REMOVE | SELECTOR_DESTROY))
{
/* ignore it */
}
return MI_TRUE;
}
/*
**=============================================================================
**
** Public defintions
**
**=============================================================================
*/
MI_Result ProvMgr_Init(
ProvMgr* self,
Selector* selector,
ProvMgrCallbackOnIdle idleCallback,
void* idleCallbackData,
const char* providerDir)
{
if (!self || !providerDir)
return MI_RESULT_INVALID_PARAMETER;
memset(self, 0, sizeof(ProvMgr));
Strlcpy(self->providerDir, providerDir, sizeof(self->providerDir)-1);
self->idleTimeoutUsec = PROVMGR_IDLE_TIMEOUT_USEC;
/* Add socket handler to catch timeout event */
self->timeoutHandler.sock = INVALID_SOCK;
self->timeoutHandler.data = self;
self->timeoutHandler.callback = _TimeoutCallback;
self->idleCallback = idleCallback;
self->idleCallbackData = idleCallbackData;
self->selector = selector;
Selector_AddHandler(selector, &self->timeoutHandler);
self->ioThreadId = Thread_ID(); /* IO thread always initializes for Linux */
Lock_Init( &self->liblock );
#ifndef DISABLE_INDICATION
RequestHandler_Init(&g_requesthandler);
#endif
return MI_RESULT_OK;
}
MI_Result ProvMgr_Destroy(
ProvMgr* self)
{
if (!self)
return MI_RESULT_INVALID_PARAMETER;
#ifndef DISABLE_INDICATION
RequestHandler_Finalize(&g_requesthandler);
#endif
/* release opened libraries */
_UnloadAllLibraries(self, MI_FALSE, 0, NULL);
/* If local session is initialized then we need to clean-up */
while (Atomic_Read(&self->localSessionInitialized) != 0)
{
if (Atomic_CompareAndSwap(&self->localSessionInitialized, 2, 1))
{
/* Initialized, so need to deinitialize */
MI_Session_Close(&self->localSession, NULL, NULL);
MI_Application_Close(&self->localApplication);
self->localSessionInitialized = 0;
/* Broadcast does a memory barrier so no need for assignment to be atomic */
CondLock_Broadcast((ptrdiff_t) &self->localSession);
}
else if (self->localSessionInitialized == 1)
{
/* Initializing for some reason so wait for it to complete */
ptrdiff_t init = self->localSessionInitialized;
while (init == 1)
{
CondLock_Wait((ptrdiff_t)&self->localSession, &self->localSessionInitialized, init, CONDLOCK_DEFAULT_SPINCOUNT);
init = self->localSessionInitialized;
}
}
}
memset(self, -1, sizeof(ProvMgr));
return MI_RESULT_OK;
}
/*
Routes incoming message to appropriate
message handler based on message tag
*/
MI_Result MI_CALL ProvMgr_NewRequest(
_In_ ProvMgr* self,
_In_ const ProvRegEntry* proventry,
_Inout_ InteractionOpenParams* params )
{
Provider* prov = 0;
MI_Result r = MI_RESULT_INVALID_PARAMETER;
/* Check parameters */
if( !self || !params || !params->msg || !params->interaction )
return MI_RESULT_INVALID_PARAMETER;
/* Dispatch the message */
switch( params->msg->tag )
{
case GetInstanceReqTag:
{
r = _HandleGetInstanceReq(self, proventry, params, &prov);
break;
}
case GetClassReqTag:
{
r = _HandleGetClassReq(self, proventry, params, &prov);
break;
}
case CreateInstanceReqTag:
{
r = _HandleCreateInstanceReq(self, proventry, params, &prov);
break;
}
case ModifyInstanceReqTag:
{
r = _HandleModifyInstanceReq(self, proventry, params, &prov);
break;
}
case DeleteInstanceReqTag:
{
r = _HandleDeleteInstanceReq(self, proventry, params, &prov);
break;
}
case InvokeReqTag:
{
r = _HandleInvokeReq(self, proventry, params, &prov);
break;
}
case EnumerateInstancesReqTag:
{
r = _HandleEnumerateInstancesReq(self, proventry, params, &prov);
break;
}
case AssociatorsOfReqTag:
{
r = _HandleAssociatorsOfReq(self, proventry, params, &prov);
break;
}
case ReferencesOfReqTag:
{
r = _HandleReferencesOfReq(self, proventry, params, &prov);
break;
}
#ifndef DISABLE_INDICATION
case SubscribeReqTag:
{
r = _HandleSubscribeReq(self, proventry, params, &prov);
break;
}
#endif
default:
trace_ProvMgr_NewRequest_UnsupportedMessage(
params->msg, MessageName(params->msg->tag));
break;
}
Provider_Release(prov); /* Releases hold from Provider_AddRef in _OpenProvider() */
return r;
}
/*
* Increments provider's ref-counter
*/
void Provider_Addref(Provider* provider)
{
if (provider)
Atomic_Inc(&provider->refCounter);
}
/*
* Finalize provider before free memory
*/
_Use_decl_annotations_
void Provider_Finalize(Provider* provider)
{
if (provider)
{
#ifndef DISABLE_INDICATION
DEBUG_ASSERT( provider->subMgr );
SubMgr_Finalize( provider->subMgr );
#endif
}
}
/*
* Decrements provider's ref-counter and
* marks provider as idle if ref-counter is 0 and
* 'refuse-unload' was not called
*/
void Provider_Release(Provider* provider)
{
if (provider && Atomic_Dec(&provider->refCounter) == 0)
{
if (!provider->refusedUnload)
{
/* Provider becomes idle */
if (PAL_TRUE != PAL_Time(&provider->idleSince))
provider->idleSince = ~ (MI_Uint64)0;
/* Set timer if it's first idle provider */
if (TIME_NEVER == provider->lib->provmgr->timeoutHandler.fireTimeoutAt)
{
if (PAL_TRUE == PAL_Time(&provider->lib->provmgr->timeoutHandler.fireTimeoutAt))
{
provider->lib->provmgr->timeoutHandler.fireTimeoutAt += provider->lib->provmgr->idleTimeoutUsec;
/* wakeup main thread */
Selector_Wakeup(provider->lib->provmgr->selector, MI_TRUE);
}
}
}
}
}
void Provider_SetRefuseUnloadFlag(Provider* provider, MI_Boolean flag)
{
provider->refusedUnload = flag;
}
MI_Result ProvMgr_GetLocalSesson(
_Inout_ ProvMgr* self,
_Out_ MI_Session *localSession)
{
#if defined(DISABLE_LOCALSESSION)
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(localSession);
return MI_RESULT_NOT_SUPPORTED;
#else /* defined(DISABLE_LOCALSESSION) */
while (Atomic_Read(&self->localSessionInitialized) != 2)
{
if (Atomic_CompareAndSwap(&self->localSessionInitialized, 0, 1) == 0)
{
/* Need to initialize */
MI_Result miResult;
miResult = MI_Application_Initialize(0, MI_T("OMI Local Session"), NULL, &self->localApplication);
if (miResult == MI_RESULT_OK)
{
miResult = MI_Application_NewSession(&self->localApplication, NULL, NULL, NULL, NULL, NULL, &self->localSession);
if (miResult == MI_RESULT_OK)
{
/* Succeeded, so mark as initialized */
*localSession = self->localSession;
self->localSessionInitialized = 2;
}
else
{
/* Failed, so reset state */
MI_Application_Close(&self->localApplication);
self->localSessionInitialized = 0;
}
}
else
{
/* Failed so reset state */
self->localSessionInitialized = 0;
}
/* memory barrier as part of broadcast which is why state updates were not atomic */
CondLock_Broadcast((ptrdiff_t) &self->localSession);
return miResult;
}
else if (self->localSessionInitialized == 1)
{
/* Wait for the initialization on another thread to complete */
ptrdiff_t init = self->localSessionInitialized;
while (init == 1)
{
CondLock_Wait((ptrdiff_t)&self->localSession, &self->localSessionInitialized, init, CONDLOCK_DEFAULT_SPINCOUNT);
init = self->localSessionInitialized;
}
/* Need to try another trip around the loop to make sure it is initialized this time around */
}
}
*localSession = self->localSession;
return MI_RESULT_OK;
#endif /* defined(DISABLE_LOCALSESSION) */
}