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