(file) Return to omireg.cpp CVS log (file) (dir) Up to [OMI] / omi / omireg

File: [OMI] / omi / omireg / omireg.cpp (download)
Revision: 1.1.1.1 (vendor branch), Wed May 30 21:47:49 2012 UTC (12 years, 1 month ago) by mike
Branch: TOG
CVS Tags: OMI_1_0_2_Branch, OMI_1_0_2, OMI_1_0_1_PRE, OMI_1_0_1, OMI_1_0_0
Changes since 1.1: +0 -0 lines
Initial Import

/*
**==============================================================================
**
** 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 <common.h>
#include <base/args.h>
#include <string>
#include <vector>
#include <base/lib.h>
#include <unistd.h>
#include <base/getopt.h>
#include <base/paths.h>
#include <base/dir.h>
#include <base/io.h>
#include <base/file.h>
#include <base/time.h>
#include <dlfcn.h>
#include <unistd.h>
#include <sys/wait.h>
#include <omiclient/client.h>

using namespace std;

static const char USAGE[] = "\
Usage: %s [OPTIONS] PROVIDERLIBRARY\n\
\n\
OVERVIEW:\n\
    This program generates a provider registration file (.reg) from\n\
    information read from a provider library.\n\
\n\
OPTIONS:\n\
    -h, --help                  Print this help message.\n\
    -v, --version               Print version information.\n\
    -n, --namespace NAME        Register provider for this namespace (option\n\
                                may be repeated to specify multiple\n\
                                nsDirs).\n\
    -l, --link                  Mode for developers. Instead of copying library file\n\
                                link is created in lib directory.\n\
    -o, --hosting HOSTING       Use given hosting mode (@requestor@,@inproc@,<user>).\n\
\n\
EXAMPLES:\n\
    The following generates a provider registration file for the provider\n\
    contained in 'libDogProvider.so'. It then copies the .reg file and\n\
    the provider to the installed locations.\n\
\n\
        $ omireg -n interop -n root/cimv2 libDogProvider.so\n\
\n\
";

using namespace std;

static const char* arg0;

//==============================================================================
//
// err()
//
//     Writes a formatted error message to standard error (preceded by argv[0])
//     and then exists.
//
//==============================================================================

PRINTF_FORMAT(1, 2)
static void FUNCTION_NEVER_RETURNS err(const char* fmt, ...)
{
    fprintf(stderr, "%s: ", arg0);

    va_list ap;
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);

    fputc('\n', stderr);
    exit(1);
}

//==============================================================================
//
// GetCommandLineOptions()
//
//     This function processes command line options.
//
//==============================================================================

struct Options
{
    bool help;
    bool devmode;
    string  hosting;
    vector<string> nsDirs;

    Options() : help(false), devmode(false)
    {
    }
}; 

static void GetCommandLineDestDirOption(
    int* argc_,
    const char* argv[])
{
    int argc = *argc_;
    int i;
    const char* destdir = NULL;

    for (i = 1; i < argc; )
    {
        if (strcmp(argv[i], "--destdir") == 0)
        {
            if (i + 1 == argc)
                err("missing argument for --destdir option");

            destdir = argv[i+1];
            memmove((char*)&argv[i], (char*)&argv[i+2], 
                sizeof(char*) * (argc-i-1));
            argc -= 2;
        }
        else if (strncmp(argv[i], "--destdir=", 10) == 0)
        {
            destdir = argv[i] + 10;
            memmove((char*)&argv[i], (char*)&argv[i+1], 
                sizeof(char*) * (argc-i));

            argc -= 1;
        }
        else
            i++;
    }

    if (destdir)
    {
        if (SetPath(ID_DESTDIR, destdir) != 0)
            err("failed to set destdir");
    }

    *argc_ = argc;
}

static void GetCommandLineOptions( 
    int& argc, 
    const char**& argv, 
    Options& options)
{
    GetOptState state = GETOPTSTATE_INITIALIZER;
    const char* opts[] =
    {
        "-h",
        "--help",
        "-v",
        "--version",
        "-n:",
        "--namespace:",
        "-l",
        "--link",
        "-o:",
        "--hosting:",
        NULL
    };

    /* For each argument */
    for (;;)
    {
        int r = GetOpt(&argc, (const char**)argv, opts, &state);

        if (r == 1)
            break;

        if (r == -1)
            err("%s", state.err);

        if (strcmp(state.opt, "-h") == 0 || 
            strcmp(state.opt, "--help") == 0)
        {
            options.help = true;
        }
        else if (strcmp(state.opt, "-v") == 0 ||
                 strcmp(state.opt, "--version") == 0)
        {
            printf("%s: %s\n", arg0,
                CONFIG_PRODUCT "-" CONFIG_VERSION " - " CONFIG_DATE);
            exit(0);
        }
        else if (strcmp(state.opt, "-l") == 0 || 
            strcmp(state.opt, "--link") == 0)
        {
            options.devmode = true;
        }
        else if (strcmp(state.opt, "-n") == 0 || 
            strcmp(state.opt, "--namespace") == 0)
        {
            string ns = state.arg;

            for (size_t i = 0; i < ns.size(); i++)
            {
                if (ns[i] == '/')
                    ns[i] = '#';
            }

            ns = string(GetPath(ID_REGISTERDIR)) + string("/") + ns;
            options.nsDirs.push_back(ns);
        }
        else if (strcmp(state.opt, "-o") == 0 || 
            strcmp(state.opt, "--hosting") == 0)
        {
            options.hosting = state.arg;
        }
    }
}

typedef MI_Module* (*MainProc)(MI_Server* server);

static string BaseName(const string& str)
{
    const char* start = strrchr(str.c_str(), '/');

    if (start)
        start++;
    else
        start = str.c_str();

    return string(start);
}

static string BaseLibName(const string& str)
{
    const char* start = strrchr(str.c_str(), '/');

    if (start)
        start++;
    else
        start = str.c_str();

    if (strncmp(start, "lib", 3) == 0)
        start += 3;

    const char* p = start;

    while (*p && *p != '.')
        p++;

    return string(start, p - start);
}

static void PrintClassPath(FILE* os, const MI_ClassDecl* cd)
{
    for (const MI_ClassDecl* p = cd; p; p = p->superClassDecl)
    {
        if (p != cd)
            fprintf(os, ":");
        fprintf(os, "%s", p->name);
    }
}

static const MI_ClassDecl* FindClassDecl(
    const MI_SchemaDecl* sd, 
    const char* className)
{
    for (MI_Uint32 i = 0; i < sd->numClassDecls; i++)
    {
        const MI_ClassDecl* p = sd->classDecls[i];

        if (strcmp(p->name, className) == 0)
            return p;
    }

    return 0;
}

static void GenClassLine(
    FILE* os, 
    const MI_SchemaDecl* sd, 
    const MI_ClassDecl* cd)
{
    // Print the class path: CLASS1.CLASS2.CLASS3
    fprintf(os, "CLASS=");
    PrintClassPath(os, cd);

    // Print the association classes:
    if (cd->flags & MI_FLAG_ASSOCIATION)
    {
        fprintf(os, "{");

        MI_Uint32 n = 0;

        for (MI_Uint32 i = 0; i < cd->numProperties; i++)
        {
            const MI_PropertyDecl* pd = cd->properties[i];

            if (pd->type == MI_REFERENCE && pd->className)
            {
                const MI_ClassDecl* rcd = FindClassDecl(sd, pd->className);

                if (!rcd)
                    err("failed to find class: %s", pd->className);

                if (n != 0)
                    fprintf(os, ",");
                PrintClassPath(os, rcd);
                n++;
            }
        }

        fprintf(os, "}");
    }

    fprintf(os, "\n");
}

static MI_Module* LoadModule(const char* path)
{
    const char FUNC[] = "MI_Main";

    // Load the library:
    void* handle = Lib_Open(path);
    if (!handle)
    {
        char* msg = Lib_Err();
        err("failed to load library: %s\n", msg);
        Lib_Free(msg);
    }

    // Load the MI_Main() entry point:
    void* sym = Lib_Sym(handle, FUNC);
    if (!sym)
    {
        err("failed to find symbol '%s' in library '%s'\n", FUNC, path);
    }

    // Call MI_Main() to get MI_Module object.
    MainProc main = (MainProc)sym;
    MI_Module* module = (*main)(NULL);
    if (!module)
    {
        err("%s:%s() failed", path, FUNC);
    }

    // Check character size:
    if (module->charSize != sizeof(MI_Char))
    {
        err("provider char size (%u) does not match %s char size (%u)", 
            module->charSize, arg0, (int)sizeof(MI_Char));
    }

    return module;
}

static void GenRegFile(
    FILE* os, 
    int argc, 
    const char** argv, 
    const string& baseName,
    MI_Module* module,
    const Options& opts)
{
    // Get schemaDecl:
    MI_SchemaDecl* sd = module->schemaDecl;
    if (!sd)
    {
        err("MI_Module.schemaDecl is null");
    }

    // Generate header line.
    fprintf(os, "# ");
    for (int i = 0; i < argc; i++)
    {
        string arg;

        if (i == 0)
            arg = BaseName(argv[i]);
        else
            arg = argv[i];

        fprintf(os, "%s", arg.c_str());
        if (i + 1 != argc)
            fprintf(os, " ");
    }
    fprintf(os, "\n");

    // Write library name:
    fprintf(os, "LIBRARY=%s\n", baseName.c_str());

    // Hosting
    if (!opts.hosting.empty())
    {
        fprintf(os, "HOSTING=%s\n", opts.hosting.c_str());
    }

    // Find providers:
    for (MI_Uint32 i = 0; i < sd->numClassDecls; i++)
    {
        const MI_ClassDecl* cd = sd->classDecls[i];

        if (!cd)
            err("null classDecl element");

        // If it has a provider, print the class line.
        if (cd->providerFT)
        {
            GenClassLine(os, sd, cd);
        }
    }
}

static bool Inhale(const char* path, vector<char>& data)
{
    FILE* is = Fopen(path, "r");

    if (!is)
        return false;

    size_t n;
    char buf[4096];

    while ((n = fread(buf, 1, sizeof(buf), is)) != 0)
    {
        data.insert(data.end(), buf, buf + n);
    }

    data.push_back('\0');

    fclose(is);

    return true;
}

static void _RefreshServer()
{
    // check if server is running
    mi::Client cl;
    const MI_Uint64 TIMEOUT = 500 * 1000;

    if (cl.Connect(GetPath(ID_SOCKETFILE), MI_T(""), MI_T(""), TIMEOUT))
    {
        // reload server's config
        pid_t child = fork();
        if (!child)
        {
            execl(GetPath(ID_SERVERPROGRAM), GetPath(ID_SERVERPROGRAM), "-r",
                NULL );
            exit(1); // never get here... if everything is ok
        }
        else if (child > 0)
        {
            /* wait for child */
            int s;
            waitpid(child, &s, 0);
        }

        // wait until it restarts
        cl.NoOp(TIMEOUT);

        // wait for server to restart
        for ( int i = 0; i < 30; i++ )
        {
            if (cl.Connect(GetPath(ID_SOCKETFILE), MI_T(""), MI_T(""), TIMEOUT))
                break;

            Time_Sleep(50);
        }
    }
}

int main(int argc, const char** argv)
{
    arg0 = argv[0];

    // Get --destdir command-line option.
    GetCommandLineDestDirOption(&argc, argv);

    // Get command-line options.
    Options opts;
    GetCommandLineOptions(argc, argv, opts);

    if (opts.nsDirs.size() == 0)
    {
        string ns = CONFIG_NAMESPACE;

        for (size_t i = 0; i < ns.size(); i++)
        {
            if (ns[i] == '/')
                ns[i] = '#';
        }

        ns = string(GetPath(ID_REGISTERDIR)) + string("/") + ns;
        opts.nsDirs.push_back(ns);
    }

    // Check arguments.
    if (opts.help)
    {
        fprintf(stderr, USAGE, arg0);
        exit(1);
    }
    
    // Check number of arguments.
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s PROVIDERLIBRARY\n", arg0);
        fprintf(stderr, "Try '%s --help' for help\n\n", arg0);
        exit(1);
    }

    // Check that namespace directories are writable.
    for (size_t i = 0; i < opts.nsDirs.size(); i++)
    {
        const string path = opts.nsDirs[i];

        if (!Isdir(path.c_str()))
            err("no such namespace directory: %s", path.c_str());

        if (access(path.c_str(), W_OK) != 0)
            err("namespace directory is not writable: %s", path.c_str());
    }

#ifdef CONFIG_OS_HPUX
    // HP is the only platform that locks loaded binary files 
    // so if provider is already loaded unlink/copy will fail 
    // force server to unload all providers before copying
    _RefreshServer();
#endif

    // Copy library to provider directory (avoid copy if file is identical
    // since the path could be the same).
    {
        vector<char> data1;

        if (!Inhale(argv[1], data1))
            err("cannot read provider library: %s", argv[1]);

        string path = GetPath(ID_PROVIDERDIR);
        path += "/";
        path += Basename(argv[1]);

        if (opts.devmode)
        {
            unlink(path.c_str());
            if (symlink(argv[1], path.c_str()) != 0)
                err("failed to symlink '%s' to '%s'", argv[1], path.c_str());
        }
        else
        {
            if (File_Copy(argv[1], path.c_str()) != 0)
                err("failed to copy '%s' to '%s'", argv[1], path.c_str());

            // set mod explicitly
            // w by owner only; RX for all.
            chmod(path.c_str(), 
                S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);

            printf("Created %s\n", path.c_str());
        }
    }

    // Load module:
    MI_Module* module = LoadModule(argv[1]);
    if (!module)
        err("failed to load provider library: %s", argv[1]);

    // Generate .reg file in each namespace directory:
    for (size_t i = 0; i < opts.nsDirs.size(); i++)
    {
        // Open output file (using basename of library):
        string baseLibName = BaseLibName(argv[1]);
        string regFileName = baseLibName + ".reg";

        string path = opts.nsDirs[i];
        path += "/";
        path += regFileName;

        FILE* os = fopen(path.c_str(), "wb");

        if (!os)
            err("failed to open: %s", path.c_str());

        // Generate the registration file.
        GenRegFile(os, argc, argv, baseLibName, module, opts);

        printf("Created %s\n", path.c_str());

        fclose(os);
    }

    // ask server to re-read configuration
    _RefreshServer();

    return 0;
}

ViewCVS 0.9.2