/*
**==============================================================================
**
** 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
#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 casat error from 'void*' to 'MI_Main' */
#if defined(_MSC_VER)
# pragma warning(disable : 4055)
#endif
#define T MI_T
/*
**=============================================================================
**
** Local defintions
**
**=============================================================================
*/
typedef MI_Module* (*MI_Main)(MI_Server* server);
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 MI_Char** 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;
}
*systemName = buf;
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, "GetPath") == 0)
return (void*)&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 */
};
typedef struct _Library Library;
struct _Provider
{
struct _Provider* next;
struct _Provider* prev;
const MI_ClassDecl* classDecl;
void* self;
/* number of outstanding requests */
AtomicInt refCounter;
/* time when last outstanding request was handled */
MI_Uint64 idleSince;
/* indicator if Provider refused idle-unload */
MI_Boolean refusedUnload;
/* pointer to lib object */
Library* lib;
/* indications support */
Context ctxIndications;
};
struct _Library
{
struct _Library* next;
struct _Library* prev;
char libraryName[MAX_PATH_SIZE];
void* handle;
const MI_Module* module;
MI_Module_Self* self;
struct _Provider* head;
struct _Provider* tail;
ProvMgr* provmgr;
};
static Library* _OpenLibrary(
ProvMgr* self,
const char* libraryName)
{
Library* p;
/* Search cache first */
for (p = self->head; p; p = p->next)
{
if (strcmp(p->libraryName, libraryName) == 0)
{
return p;
}
}
/* Allocate new libray object */
p = (Library*)calloc(1, sizeof(Library));
if (!p)
return NULL;
/* Library.refs */
p->provmgr = self;
/* Open the library */
{
char path[MAX_PATH_SIZE];
Lib_Format(path, self->providerDir, libraryName);
p->handle = Lib_Open(path);
if (!p->handle)
{
/* ATTN: one more attempt */
p->handle = Lib_Open(libraryName);
}
if (!p->handle)
{
free(p);
return NULL;
}
}
/* Lib_Open.libraryName */
Strlcpy(p->libraryName, libraryName, sizeof(p->libraryName));
/* Invoke MI_Main() entry point */
{
MI_Main statikMain;
/* Lookup symbol */
{
void* ptr = Lib_Sym(p->handle, "MI_Main");
statikMain = (MI_Main)ptr;
if (!statikMain)
{
free(p);
return NULL;
}
}
/* Call MI_Main */
{
p->module = (*statikMain)(&_server);
}
}
/* Invoke the module initialize function */
if (p->module->Load)
{
Context ctx;
Context_Init(&ctx, 0);
(p->module->Load)(&p->self, (MI_Context*)&ctx);
Context_Destroy(&ctx);
}
/* Add library to the list */
List_Prepend(
(ListElem**)&self->head,
(ListElem**)&self->tail,
(ListElem*)p);
return p;
}
static Provider* _OpenProvider(
Library* self,
const MI_Char* className)
{
Provider* p;
/* Search cache first */
for (p = self->head; p; p = p->next)
{
if (Zcasecmp(p->classDecl->name, className) == 0)
{
AtomicInc(&p->refCounter);
return p;
}
}
/* New Provider */
p = (Provider*)calloc(1, sizeof(Provider));
if (!p)
{
return NULL;
}
/* Provider.refs */
p->refCounter = 1;
p->lib = self;
/* Provider.classDecl */
{
p->classDecl = SchemaDecl_FindClassDecl(self->module->schemaDecl,
className);
if (!p->classDecl)
{
free(p);
return NULL;
}
}
/* Initialize context - used for indication providers */
Context_Init(&p->ctxIndications, 0);
p->ctxIndications.chainType = CTX_TYPE_IND_NOTINITIALIZED;
/* Call provider Load() method */
if (p->classDecl->providerFT->Load)
{
Context ctx;
MI_Result r = MI_RESULT_OK;
Context_Init(&ctx, p);
ctx.result = &r;
(*p->classDecl->providerFT->Load)(&p->self, self->self, &ctx.base);
DEBUG_ASSERT(ctx.magic == (MI_Uint32)-1);
if (MI_RESULT_OK != r)
{
LOGW((T("failed to call provider's load with result %d; class: %s"), (int)r, className));
free(p);
return NULL;
}
}
/* Prepend to list */
List_Prepend(
(ListElem**)&self->head,
(ListElem**)&self->tail,
(ListElem*)p);
return p;
}
static MI_Result _GetProviderByClassName(
ProvMgr* self,
const char* libraryName,
MI_ConstString cn,
Provider** provOut
)
{
Library* lib;
Provider* prov;
/* Open the library */
{
lib = _OpenLibrary(self, libraryName);
if (!lib)
{
LOGW_CHAR(("failed to open provider library: %s", libraryName));
return MI_RESULT_FAILED;
}
}
/* Open the provider */
{
prov = _OpenProvider(lib, cn);
if (!prov)
{
LOGW_CHAR(("failed to open the provider %s for class %s",
libraryName, 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_Instance* inst;
MI_Result r;
MI_UNUSED(sd);
/* Allocate the instance for the provider */
inst = (MI_Instance*)Batch_GetClear(batch, cd->size);
if (!inst)
{
LOGF((T("allocation failed")));
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);
if (r != MI_RESULT_OK)
{
LOGW((T("instance conversion failed: %s, err %d"), cd->name, r));
return r;
}
*instOut = inst;
return MI_RESULT_OK;
}
static MI_Result _HandleGetInstanceReq(
ProvMgr* self,
const char* libraryName,
GetInstanceReq* msg,
Provider** prov)
{
MI_Instance* inst;
MI_Result r;
const MI_Char* className;
/* Get classname from instance */
r = __MI_Instance_GetClassName(msg->instanceName, &className);
if (r != MI_RESULT_OK)
return r;
/* find provider */
r = _GetProviderByClassName(self, libraryName, className, prov);
if ( MI_RESULT_OK != r )
return r;
/* Allocate the instance for the provider */
r = _Instance_InitConvert_FromBatch(
msg->base.batch,
(*prov)->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instanceName,
MI_TRUE,
MI_FALSE,
&inst );
if (MI_RESULT_OK != r)
return r;
#if 0
/* Print instance */
Instance_Print(inst, stdout, 0);
#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.batch, sizeof(Context));;
Context_Init(ctx, (*prov));
ctx->request = &msg->base;
/* _PostInstance() filters by this if not null */
ctx->instanceName = inst;
/* message will be freed in context release*/
Message_AddRef(&msg->base);
/* 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.batch, sizeof(Context));;
Context_Init(ctx, (*prov));
ctx->request = &msg->base;
/* message will be freed in context release*/
Message_AddRef(&msg->base);
/* Invoke provider */
(*(*prov)->classDecl->providerFT->GetInstance)(
(*prov)->self,
&ctx->base,
msg->nameSpace,
className,
inst,
NULL);
}
return MI_RESULT_OK;
}
static MI_Result _HandleCreateInstanceReq(
ProvMgr* self,
const char* libraryName,
CreateInstanceReq* msg,
Provider** prov)
{
MI_Instance* inst;
MI_Result r;
const MI_Char* className;
/* Get classname from instance */
r = __MI_Instance_GetClassName(msg->instance, &className);
if (r != MI_RESULT_OK)
return r;
/* find provider */
r = _GetProviderByClassName(self, libraryName, className, prov);
if ( MI_RESULT_OK != r )
return r;
/* Allocate the instance for the provider */
r = _Instance_InitConvert_FromBatch(
msg->base.batch,
(*prov)->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instance,
MI_FALSE,
MI_TRUE,
&inst );
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.batch,
sizeof(Context));;
Context_Init(ctx, (*prov));
ctx->request = &msg->base;
/* message will be freed in context release*/
Message_AddRef(&msg->base);
(*(*prov)->classDecl->providerFT->CreateInstance)((*prov)->self, &ctx->base,
msg->nameSpace, className, inst);
}
return MI_RESULT_OK;
}
static MI_Result _HandleModifyInstanceReq(
ProvMgr* self,
const char* libraryName,
ModifyInstanceReq* msg,
Provider** prov)
{
MI_Instance* inst;
MI_Result r;
const MI_Char* className;
/* Get classname from instance */
r = __MI_Instance_GetClassName(msg->instance, &className);
if (r != MI_RESULT_OK)
return r;
/* find provider */
r = _GetProviderByClassName(self, libraryName, className, prov);
if ( MI_RESULT_OK != r )
return r;
/* Allocate the instance for the provider */
r = _Instance_InitConvert_FromBatch(
msg->base.batch,
(*prov)->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instance,
MI_FALSE,
MI_FALSE,
&inst );
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.batch,
sizeof(Context));;
Context_Init(ctx, (*prov));
ctx->request = &msg->base;
/* message will be freed in context release*/
Message_AddRef(&msg->base);
(*(*prov)->classDecl->providerFT->ModifyInstance)((*prov)->self, &ctx->base,
msg->nameSpace, className, inst, NULL);
}
return MI_RESULT_OK;
}
static MI_Result _HandleDeleteInstanceReq(
ProvMgr* self,
const char* libraryName,
DeleteInstanceReq* msg,
Provider** prov)
{
MI_Instance* inst;
MI_Result r;
const MI_Char* className;
/* Get classname from instance */
r = __MI_Instance_GetClassName(msg->instanceName, &className);
if (r != MI_RESULT_OK)
return r;
/* find provider */
r = _GetProviderByClassName(self, libraryName, className, prov);
if ( MI_RESULT_OK != r )
return r;
/* Allocate the instance for the provider */
r = _Instance_InitConvert_FromBatch(
msg->base.batch,
(*prov)->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instanceName,
MI_TRUE,
MI_FALSE,
&inst);
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.batch,
sizeof(Context));;
Context_Init(ctx, (*prov));
ctx->request = &msg->base;
/* message will be freed in context release*/
Message_AddRef(&msg->base);
(*(*prov)->classDecl->providerFT->DeleteInstance)((*prov)->self, &ctx->base,
msg->nameSpace, className, inst);
}
return MI_RESULT_OK;
}
static MI_Result _HandleSubscribeReq(
ProvMgr* self,
const char* libraryName,
SubscribeReq* msg,
Provider** prov)
{
MI_Result r;
/* find provider */
r = _GetProviderByClassName( self, libraryName, msg->className, prov );
if ( MI_RESULT_OK != r )
return r;
/* Invoke provider */
if (!(*prov)->classDecl->providerFT->EnableIndications || !(*prov)->classDecl->providerFT->Subscribe)
return MI_RESULT_INVALID_CLASS;
if ((*prov)->ctxIndications.chainType == CTX_TYPE_IND_NOTINITIALIZED)
{
/* have to initialize provider first */
(*prov)->ctxIndications.chainType = CTX_TYPE_IND_READY;
(*prov)->classDecl->providerFT->EnableIndications((*prov)->self,
&(*prov)->ctxIndications.base, msg->nameSpace, msg->className);
}
if ((*prov)->ctxIndications.chainType == CTX_TYPE_IND_READY)
{
/* provider is ready for subscriptions */
Context* ctx = (Context*)Batch_GetClear(msg->base.batch, sizeof(Context));
Context_Init(ctx, (*prov));
ctx->request = &msg->base;
/* message will be freed in context release*/
Message_AddRef(&msg->base);
(*(*prov)->classDecl->providerFT->Subscribe)((*prov)->self, &ctx->base,
msg->nameSpace, msg->className, 0, __bookmark, msg->subscriptionID,
__subscriptionSelfPtr);
}
else
return MI_RESULT_FAILED; /* unexpected state */
return MI_RESULT_OK;
}
static MI_Result _HandleInvokeReq(
ProvMgr* self,
const char* libraryName,
InvokeReq* msg,
Provider** prov)
{
MI_Instance* inst = 0;
MI_Instance* instParams = 0;
MI_Result r;
MI_ConstString cn = 0;
MI_MethodDecl* md = 0;
/* 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, libraryName, cn, 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.batch,
(*prov)->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instance,
MI_TRUE,
MI_FALSE,
&inst );
if (MI_RESULT_OK != r)
return r;
}
if (msg->instanceParams)
{
/* paramters (if any) */
r = _Instance_InitConvert_FromBatch(
msg->base.batch,
(const MI_ClassDecl*)md,
(*prov)->lib->module->schemaDecl,
msg->instanceParams,
MI_FALSE,
MI_TRUE,
&instParams );
if (MI_RESULT_OK != r)
return r;
}
#if 0
/* Print instance */
Instance_Print(inst, stdout, 0);
#endif
/* Invoke provider */
if (!md->function)
return MI_RESULT_INVALID_CLASS;
{
Context* ctx = (Context*)Batch_GetClear(msg->base.batch, sizeof(Context));;
Context_Init(ctx, (*prov));
ctx->request = &msg->base;
/* message will be freed in context release*/
Message_AddRef(&msg->base);
/* call get first if fn is non-static */
/*if (inst && (*prov)->classDecl->providerFT->GetInstance)
{
ctx->chainType = 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(
ProvMgr* self,
const char* libraryName,
EnumerateInstancesReq* msg,
Provider** prov)
{
Context* ctx;
MI_Result r;
/* find provider */
r = _GetProviderByClassName( self, libraryName, msg->className, prov );
if ( MI_RESULT_OK != r )
return r;
/* Validate WQL query (if any) against provider's class declaration */
if (msg->wql)
{
if (WQL_Validate(msg->wql, (*prov)->classDecl) != 0)
{
LOGW((T("query validation failed: %s"), 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.batch, sizeof(Context));;
if (!ctx)
{
LOGF((T("allocation failed")));
return MI_RESULT_FAILED;
}
Context_Init(ctx, (*prov));
ctx->request = &msg->base;
}
LOGD((T("enumerate instances of %s"), msg->className));
/* message will be freed in context release */
Message_AddRef(&msg->base);
(*(*prov)->classDecl->providerFT->EnumerateInstances)(
(*prov)->self, &ctx->base,
msg->nameSpace, msg->className, NULL, MI_FALSE, NULL);
return MI_RESULT_OK;
}
static MI_Result _HandleAssociatorsOfReq(
ProvMgr* self,
const char* libraryName,
AssociatorsOfReq* msg,
Provider** prov)
{
Context* ctx;
MI_Result r;
MI_Instance* inst = 0;
/* find provider */
r = _GetProviderByClassName( self, libraryName, msg->className, 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, libraryName, ((Instance*) msg->instance)->classDecl->name, &provInst );
if ( MI_RESULT_OK != r )
return r;
r = _Instance_InitConvert_FromBatch(
msg->base.batch,
provInst->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instance,
MI_TRUE,
MI_FALSE,
&inst );
Provider_Release(provInst);
if (MI_RESULT_OK != r)
return r;
}
ctx = (Context*)Batch_GetClear(
msg->base.batch, sizeof(Context));;
if (!ctx)
{
LOGF((T("allocation failed")));
return MI_RESULT_FAILED;
}
Context_Init(ctx, (*prov));
ctx->request = &msg->base;
/* message will be freed in context release */
Message_AddRef(&msg->base);
(*(*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(
ProvMgr* self,
const char* libraryName,
ReferencesOfReq* msg,
Provider** prov)
{
Context* ctx;
MI_Result r;
MI_Instance* inst = 0;
/* find provider */
r = _GetProviderByClassName( self, libraryName, msg->className, 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,
libraryName,
((Instance*) msg->instance)->classDecl->name,
&provInst );
if ( MI_RESULT_OK != r )
return r;
r = _Instance_InitConvert_FromBatch(
msg->base.batch,
provInst->classDecl,
(*prov)->lib->module->schemaDecl,
msg->instance,
MI_TRUE,
MI_FALSE,
&inst );
Provider_Release(provInst);
if (MI_RESULT_OK != r)
return r;
}
ctx = (Context*)Batch_GetClear(
msg->base.batch, sizeof(Context));;
if (!ctx)
{
LOGF((T("allocation failed")));
return MI_RESULT_FAILED;
}
Context_Init(ctx, (*prov));
ctx->request = &msg->base;
/* message will be freed in context release */
Message_AddRef(&msg->base);
(*(*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; )
{
MI_Uint64 provFireAtTime = p->idleSince + self->idleTimeoutUsec;
p_next = p->next;
/* unload if 'force' option passed or provider is idle long enough */
if (!idleOnly ||
(!p->refusedUnload && 0 == p->refCounter && provFireAtTime <= currentTimeUsec))
{
LOGD((T("Unloading provider %s"), p->classDecl->name));
/* Call provider unload() method */
if (p->classDecl->providerFT->Unload)
{
Context ctx;
Context_Init(&ctx, 0);
(*p->classDecl->providerFT->Unload)(p->self, &ctx.base);
DEBUG_ASSERT(ctx.magic == (MI_Uint32)-1);
}
Context_Destroy(&p->ctxIndications);
if (p->refCounter != 0)
{
/* Error condition - unloading active rpovider! */
LOGE((T("Unloading active provider %s, with ref counter %d"), p->classDecl->name, (int)p->refCounter));
LOGE_CHAR(("Unloading active provider for lib %s, with ref counter %d", lib->libraryName, (int)p->refCounter));
// ATTN: _exit is a good option here, since provider's behavior maybe undefined
_exit(1);
}
List_Remove(
(ListElem**)&lib->head,
(ListElem**)&lib->tail,
(ListElem*)p);
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;
}
}
static void _UnloadAllLibraries(
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;
_UnloadAllProviders(self,p,idleOnly,currentTimeUsec,nextFireAtTime);
/* Unload libraries that have no loaded providers */
if (!p->head)
{
/* Invoke the module un-initialize function */
if (p->module->Unload)
{
Context ctx;
Context_Init(&ctx, 0);
(p->module->Unload)(p->self, (MI_Context*)&ctx);
Context_Destroy(&ctx);
}
Lib_Close(p->handle);
LOGD_CHAR(("Unloading lib %s", p->libraryName));
List_Remove(
(ListElem**)&self->head,
(ListElem**)&self->tail,
(ListElem*)p);
free(p);
}
p = p_next;
}
}
/*
Timeout handler: unloads idle providers and libraris,
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 */
//LOGI((T("Unloading idle providers")));
_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;
}
/* If all libraries are gone, notify caller */
if (!self->head && self->idleCallback)
(*self->idleCallback)(self,self->idleCallbackData);
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 = MI_ULL(90) * MI_ULL(1000000);
/* 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);
return MI_RESULT_OK;
}
MI_Result ProvMgr_Destroy(
ProvMgr* self)
{
if (!self)
return MI_RESULT_INVALID_PARAMETER;
/* release opened libraries */
_UnloadAllLibraries(self, MI_FALSE, 0, NULL);
memset(self, -1, sizeof(ProvMgr));
return MI_RESULT_OK;
}
/*
Routes incoming message to appropriate
message handler based on message tag
*/
MI_Result ProvMgr_PostMessage(
ProvMgr* self,
const char* libraryName,
Message* msg)
{
Provider* prov = 0;
MI_Result r = MI_RESULT_INVALID_PARAMETER;
/* Check parameters */
if (!self || !msg)
return MI_RESULT_INVALID_PARAMETER;
/* Dispatch the message */
switch (msg->tag)
{
case GetInstanceReqTag:
{
r = _HandleGetInstanceReq(self, libraryName,
(GetInstanceReq*)msg, &prov);
break;
}
case CreateInstanceReqTag:
{
r = _HandleCreateInstanceReq(self, libraryName,
(CreateInstanceReq*)msg, &prov);
break;
}
case ModifyInstanceReqTag:
{
r = _HandleModifyInstanceReq(self, libraryName,
(ModifyInstanceReq*)msg, &prov);
break;
}
case DeleteInstanceReqTag:
{
r = _HandleDeleteInstanceReq(self, libraryName,
(DeleteInstanceReq*)msg, &prov);
break;
}
case InvokeReqTag:
{
r = _HandleInvokeReq(self, libraryName,
(InvokeReq*)msg, &prov);
break;
}
case EnumerateInstancesReqTag:
{
r = _HandleEnumerateInstancesReq(self, libraryName,
(EnumerateInstancesReq*)msg, &prov);
break;
}
case AssociatorsOfReqTag:
{
r = _HandleAssociatorsOfReq(self, libraryName,
(AssociatorsOfReq*)msg, &prov);
break;
}
case ReferencesOfReqTag:
{
r = _HandleReferencesOfReq(self, libraryName,
(ReferencesOfReq*)msg, &prov);
break;
}
case SubscribeReqTag:
{
r = _HandleSubscribeReq(self, libraryName,
(SubscribeReq*)msg, &prov);
break;
}
default:
break;
}
Provider_Release(prov);
return r;
}
/*
Increments provider's ref-counter
*/
void Provider_Addref(Provider* provider)
{
if (provider)
AtomicInc(&provider->refCounter);
}
/*
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 && AtomicDec(&provider->refCounter))
{
//LOGD((T("Releasing provider %s"), provider->classDecl->name));
if (!provider->refusedUnload)
{
/* Provider becomes idle */
if (MI_RESULT_OK != Time_Now(&provider->idleSince))
provider->idleSince = ~ (MI_Uint64)0;
//LOGD((T("Setting idle-since to ") UINT64_FMT_T T(" for provider %s"), provider->idleSince, provider->classDecl->name));
/* Set timer if it's first idle provider */
if (TIME_NEVER == provider->lib->provmgr->timeoutHandler.fireTimeoutAt)
{
if (MI_RESULT_OK == Time_Now(&provider->lib->provmgr->timeoutHandler.fireTimeoutAt))
{
provider->lib->provmgr->timeoutHandler.fireTimeoutAt += provider->lib->provmgr->idleTimeoutUsec;
//LOGD((T("Setting fire-at to ") UINT64_FMT_T, provider->lib->provmgr->timeoutHandler.fireTimeoutAt));
/* wakeup main thread */
Selector_Wakeup(provider->lib->provmgr->selector);
}
}
}
}
}
void Provider_SetRefuseUnloadFlag(Provider* provider, MI_Boolean flag)
{
provider->refusedUnload = flag;
}
void Provider_NewInstanceCreated(
Provider* provider,
Message* msg)
{
Selector_NewInstanceCreated(provider->lib->provmgr->selector, msg);
}