(file) Return to provmgr.c CVS log (file) (dir) Up to [OMI] / omi / provmgr

File: [OMI] / omi / provmgr / provmgr.c (download)
Revision: 1.2, Mon Jun 25 18:51:02 2012 UTC (12 years ago) by mike
Branch: MAIN
CVS Tags: OMI_1_0_2
Changes since 1.1: +16 -1 lines
1.0.2 drop

/*
**==============================================================================
**
** 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 <base/log.h>
#include <base/strings.h>
#include <base/atomic.h>
#include <base/paths.h>
#include <base/time.h>
#include <wql/wql.h>

#if defined(CONFIG_POSIX)
# include <unistd.h>
#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;
    }

#if 0
    if (inst_in)
    {
        printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
        MI_Instance_Print(inst_in, stdout, 1);
        printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
    }
#endif

    /* 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);
}

ViewCVS 0.9.2