1 mike 1.1 /*
2 **==============================================================================
3 **
4 ** Open Management Infrastructure (OMI)
5 **
6 ** Copyright (c) Microsoft Corporation
7 **
8 ** Licensed under the Apache License, Version 2.0 (the "License"); you may not
9 ** use this file except in compliance with the License. You may obtain a copy
10 ** of the License at
11 **
12 ** http://www.apache.org/licenses/LICENSE-2.0
13 **
14 ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
16 ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
17 ** MERCHANTABLITY OR NON-INFRINGEMENT.
18 **
19 ** See the Apache 2 License for the specific language governing permissions
20 ** and limitations under the License.
21 **
22 mike 1.1 **==============================================================================
23 */
24
25 #include <common.h>
26 #include <base/args.h>
27 #include <string>
28 #include <vector>
29 #include <base/lib.h>
30 #include <unistd.h>
31 #include <base/getopt.h>
32 #include <base/paths.h>
33 #include <base/dir.h>
34 #include <base/io.h>
35 #include <base/file.h>
36 #include <base/time.h>
37 #include <dlfcn.h>
38 #include <unistd.h>
39 #include <sys/wait.h>
40 #include <omiclient/client.h>
41
42 using namespace std;
43 mike 1.1
44 static const char USAGE[] = "\
45 Usage: %s [OPTIONS] PROVIDERLIBRARY\n\
46 \n\
47 OVERVIEW:\n\
48 This program generates a provider registration file (.reg) from\n\
49 information read from a provider library.\n\
50 \n\
51 OPTIONS:\n\
52 -h, --help Print this help message.\n\
53 -v, --version Print version information.\n\
54 -n, --namespace NAME Register provider for this namespace (option\n\
55 may be repeated to specify multiple\n\
56 nsDirs).\n\
57 -l, --link Mode for developers. Instead of copying library file\n\
58 link is created in lib directory.\n\
59 -o, --hosting HOSTING Use given hosting mode (@requestor@,@inproc@,<user>).\n\
60 \n\
61 EXAMPLES:\n\
62 The following generates a provider registration file for the provider\n\
63 contained in 'libDogProvider.so'. It then copies the .reg file and\n\
64 mike 1.1 the provider to the installed locations.\n\
65 \n\
66 $ omireg -n interop -n root/cimv2 libDogProvider.so\n\
67 \n\
68 ";
69
70 using namespace std;
71
72 static const char* arg0;
73
74 //==============================================================================
75 //
76 // err()
77 //
78 // Writes a formatted error message to standard error (preceded by argv[0])
79 // and then exists.
80 //
81 //==============================================================================
82
83 PRINTF_FORMAT(1, 2)
84 static void FUNCTION_NEVER_RETURNS err(const char* fmt, ...)
85 mike 1.1 {
86 fprintf(stderr, "%s: ", arg0);
87
88 va_list ap;
89 va_start(ap, fmt);
90 vfprintf(stderr, fmt, ap);
91 va_end(ap);
92
93 fputc('\n', stderr);
94 exit(1);
95 }
96
97 //==============================================================================
98 //
99 // GetCommandLineOptions()
100 //
101 // This function processes command line options.
102 //
103 //==============================================================================
104
105 struct Options
106 mike 1.1 {
107 bool help;
108 bool devmode;
109 string hosting;
110 vector<string> nsDirs;
111
112 Options() : help(false), devmode(false)
113 {
114 }
115 };
116
117 static void GetCommandLineDestDirOption(
118 int* argc_,
119 const char* argv[])
120 {
121 int argc = *argc_;
122 int i;
123 const char* destdir = NULL;
124
125 for (i = 1; i < argc; )
126 {
127 mike 1.1 if (strcmp(argv[i], "--destdir") == 0)
128 {
129 if (i + 1 == argc)
130 err("missing argument for --destdir option");
131
132 destdir = argv[i+1];
133 memmove((char*)&argv[i], (char*)&argv[i+2],
134 sizeof(char*) * (argc-i-1));
135 argc -= 2;
136 }
137 else if (strncmp(argv[i], "--destdir=", 10) == 0)
138 {
139 destdir = argv[i] + 10;
140 memmove((char*)&argv[i], (char*)&argv[i+1],
141 sizeof(char*) * (argc-i));
142
143 argc -= 1;
144 }
145 else
146 i++;
147 }
148 mike 1.1
149 if (destdir)
150 {
151 if (SetPath(ID_DESTDIR, destdir) != 0)
152 err("failed to set destdir");
153 }
154
155 *argc_ = argc;
156 }
157
158 static void GetCommandLineOptions(
159 int& argc,
160 const char**& argv,
161 Options& options)
162 {
163 GetOptState state = GETOPTSTATE_INITIALIZER;
164 const char* opts[] =
165 {
166 "-h",
167 "--help",
168 "-v",
169 mike 1.1 "--version",
170 "-n:",
171 "--namespace:",
172 "-l",
173 "--link",
174 "-o:",
175 "--hosting:",
176 NULL
177 };
178
179 /* For each argument */
180 for (;;)
181 {
182 int r = GetOpt(&argc, (const char**)argv, opts, &state);
183
184 if (r == 1)
185 break;
186
187 if (r == -1)
188 err("%s", state.err);
189
190 mike 1.1 if (strcmp(state.opt, "-h") == 0 ||
191 strcmp(state.opt, "--help") == 0)
192 {
193 options.help = true;
194 }
195 else if (strcmp(state.opt, "-v") == 0 ||
196 strcmp(state.opt, "--version") == 0)
197 {
198 printf("%s: %s\n", arg0,
199 CONFIG_PRODUCT "-" CONFIG_VERSION " - " CONFIG_DATE);
200 exit(0);
201 }
202 else if (strcmp(state.opt, "-l") == 0 ||
203 strcmp(state.opt, "--link") == 0)
204 {
205 options.devmode = true;
206 }
207 else if (strcmp(state.opt, "-n") == 0 ||
208 strcmp(state.opt, "--namespace") == 0)
209 {
210 string ns = state.arg;
211 mike 1.1
212 for (size_t i = 0; i < ns.size(); i++)
213 {
214 if (ns[i] == '/')
215 ns[i] = '#';
216 }
217
218 ns = string(GetPath(ID_REGISTERDIR)) + string("/") + ns;
219 options.nsDirs.push_back(ns);
220 }
221 else if (strcmp(state.opt, "-o") == 0 ||
222 strcmp(state.opt, "--hosting") == 0)
223 {
224 options.hosting = state.arg;
225 }
226 }
227 }
228
229 typedef MI_Module* (*MainProc)(MI_Server* server);
230
231 static string BaseName(const string& str)
232 mike 1.1 {
233 const char* start = strrchr(str.c_str(), '/');
234
235 if (start)
236 start++;
237 else
238 start = str.c_str();
239
240 return string(start);
241 }
242
243 static string BaseLibName(const string& str)
244 {
245 const char* start = strrchr(str.c_str(), '/');
246
247 if (start)
248 start++;
249 else
250 start = str.c_str();
251
252 if (strncmp(start, "lib", 3) == 0)
253 mike 1.1 start += 3;
254
255 const char* p = start;
256
257 while (*p && *p != '.')
258 p++;
259
260 return string(start, p - start);
261 }
262
263 static void PrintClassPath(FILE* os, const MI_ClassDecl* cd)
264 {
265 for (const MI_ClassDecl* p = cd; p; p = p->superClassDecl)
266 {
267 if (p != cd)
268 fprintf(os, ":");
269 fprintf(os, "%s", p->name);
270 }
271 }
272
273 static const MI_ClassDecl* FindClassDecl(
274 mike 1.1 const MI_SchemaDecl* sd,
275 const char* className)
276 {
277 for (MI_Uint32 i = 0; i < sd->numClassDecls; i++)
278 {
279 const MI_ClassDecl* p = sd->classDecls[i];
280
281 if (strcmp(p->name, className) == 0)
282 return p;
283 }
284
285 return 0;
286 }
287
288 static void GenClassLine(
289 FILE* os,
290 const MI_SchemaDecl* sd,
291 const MI_ClassDecl* cd)
292 {
293 // Print the class path: CLASS1.CLASS2.CLASS3
294 fprintf(os, "CLASS=");
295 mike 1.1 PrintClassPath(os, cd);
296
297 // Print the association classes:
298 if (cd->flags & MI_FLAG_ASSOCIATION)
299 {
300 fprintf(os, "{");
301
302 MI_Uint32 n = 0;
303
304 for (MI_Uint32 i = 0; i < cd->numProperties; i++)
305 {
306 const MI_PropertyDecl* pd = cd->properties[i];
307
308 if (pd->type == MI_REFERENCE && pd->className)
309 {
310 const MI_ClassDecl* rcd = FindClassDecl(sd, pd->className);
311
312 if (!rcd)
313 err("failed to find class: %s", pd->className);
314
315 if (n != 0)
316 mike 1.1 fprintf(os, ",");
317 PrintClassPath(os, rcd);
318 n++;
319 }
320 }
321
322 fprintf(os, "}");
323 }
324
325 fprintf(os, "\n");
326 }
327
328 static MI_Module* LoadModule(const char* path)
329 {
330 const char FUNC[] = "MI_Main";
331
332 // Load the library:
333 void* handle = Lib_Open(path);
334 if (!handle)
335 {
336 char* msg = Lib_Err();
337 mike 1.1 err("failed to load library: %s\n", msg);
338 Lib_Free(msg);
339 }
340
341 // Load the MI_Main() entry point:
342 void* sym = Lib_Sym(handle, FUNC);
343 if (!sym)
344 {
345 err("failed to find symbol '%s' in library '%s'\n", FUNC, path);
346 }
347
348 // Call MI_Main() to get MI_Module object.
349 MainProc main = (MainProc)sym;
350 MI_Module* module = (*main)(NULL);
351 if (!module)
352 {
353 err("%s:%s() failed", path, FUNC);
354 }
355
356 // Check character size:
357 if (module->charSize != sizeof(MI_Char))
358 mike 1.1 {
359 err("provider char size (%u) does not match %s char size (%u)",
360 module->charSize, arg0, (int)sizeof(MI_Char));
361 }
362
363 return module;
364 }
365
366 static void GenRegFile(
367 FILE* os,
368 int argc,
369 const char** argv,
370 const string& baseName,
371 MI_Module* module,
372 const Options& opts)
373 {
374 // Get schemaDecl:
375 MI_SchemaDecl* sd = module->schemaDecl;
376 if (!sd)
377 {
378 err("MI_Module.schemaDecl is null");
379 mike 1.1 }
380
381 // Generate header line.
382 fprintf(os, "# ");
383 for (int i = 0; i < argc; i++)
384 {
385 string arg;
386
387 if (i == 0)
388 arg = BaseName(argv[i]);
389 else
390 arg = argv[i];
391
392 fprintf(os, "%s", arg.c_str());
393 if (i + 1 != argc)
394 fprintf(os, " ");
395 }
396 fprintf(os, "\n");
397
398 // Write library name:
399 fprintf(os, "LIBRARY=%s\n", baseName.c_str());
400 mike 1.1
401 // Hosting
402 if (!opts.hosting.empty())
403 {
404 fprintf(os, "HOSTING=%s\n", opts.hosting.c_str());
405 }
406
407 // Find providers:
408 for (MI_Uint32 i = 0; i < sd->numClassDecls; i++)
409 {
410 const MI_ClassDecl* cd = sd->classDecls[i];
411
412 if (!cd)
413 err("null classDecl element");
414
415 // If it has a provider, print the class line.
416 if (cd->providerFT)
417 {
418 GenClassLine(os, sd, cd);
419 }
420 }
421 mike 1.1 }
422
423 static bool Inhale(const char* path, vector<char>& data)
424 {
425 FILE* is = Fopen(path, "r");
426
427 if (!is)
428 return false;
429
430 size_t n;
431 char buf[4096];
432
433 while ((n = fread(buf, 1, sizeof(buf), is)) != 0)
434 {
435 data.insert(data.end(), buf, buf + n);
436 }
437
438 data.push_back('\0');
439
440 fclose(is);
441
442 mike 1.1 return true;
443 }
444
445 static void _RefreshServer()
446 {
447 // check if server is running
448 mi::Client cl;
449 const MI_Uint64 TIMEOUT = 500 * 1000;
450
451 if (cl.Connect(GetPath(ID_SOCKETFILE), MI_T(""), MI_T(""), TIMEOUT))
452 {
453 // reload server's config
454 pid_t child = fork();
455 if (!child)
456 {
457 execl(GetPath(ID_SERVERPROGRAM), GetPath(ID_SERVERPROGRAM), "-r",
458 NULL );
459 exit(1); // never get here... if everything is ok
460 }
461 else if (child > 0)
462 {
463 mike 1.1 /* wait for child */
464 int s;
465 waitpid(child, &s, 0);
466 }
467
468 // wait until it restarts
469 cl.NoOp(TIMEOUT);
470
471 // wait for server to restart
472 for ( int i = 0; i < 30; i++ )
473 {
474 if (cl.Connect(GetPath(ID_SOCKETFILE), MI_T(""), MI_T(""), TIMEOUT))
475 break;
476
477 Time_Sleep(50);
478 }
479 }
480 }
481
482 int main(int argc, const char** argv)
483 {
484 mike 1.1 arg0 = argv[0];
485
486 // Get --destdir command-line option.
487 GetCommandLineDestDirOption(&argc, argv);
488
489 // Get command-line options.
490 Options opts;
491 GetCommandLineOptions(argc, argv, opts);
492
493 if (opts.nsDirs.size() == 0)
494 {
495 string ns = CONFIG_NAMESPACE;
496
497 for (size_t i = 0; i < ns.size(); i++)
498 {
499 if (ns[i] == '/')
500 ns[i] = '#';
501 }
502
503 ns = string(GetPath(ID_REGISTERDIR)) + string("/") + ns;
504 opts.nsDirs.push_back(ns);
505 mike 1.1 }
506
507 // Check arguments.
508 if (opts.help)
509 {
510 fprintf(stderr, USAGE, arg0);
511 exit(1);
512 }
513
514 // Check number of arguments.
515 if (argc != 2)
516 {
517 fprintf(stderr, "Usage: %s PROVIDERLIBRARY\n", arg0);
518 fprintf(stderr, "Try '%s --help' for help\n\n", arg0);
519 exit(1);
520 }
521
522 // Check that namespace directories are writable.
523 for (size_t i = 0; i < opts.nsDirs.size(); i++)
524 {
525 const string path = opts.nsDirs[i];
526 mike 1.1
527 if (!Isdir(path.c_str()))
528 err("no such namespace directory: %s", path.c_str());
529
530 if (access(path.c_str(), W_OK) != 0)
531 err("namespace directory is not writable: %s", path.c_str());
532 }
533
534 #ifdef CONFIG_OS_HPUX
535 // HP is the only platform that locks loaded binary files
536 // so if provider is already loaded unlink/copy will fail
537 // force server to unload all providers before copying
538 _RefreshServer();
539 #endif
540
541 // Copy library to provider directory (avoid copy if file is identical
542 // since the path could be the same).
543 {
544 vector<char> data1;
545
546 if (!Inhale(argv[1], data1))
547 mike 1.1 err("cannot read provider library: %s", argv[1]);
548
549 string path = GetPath(ID_PROVIDERDIR);
550 path += "/";
551 path += Basename(argv[1]);
552
553 if (opts.devmode)
554 {
555 unlink(path.c_str());
556 if (symlink(argv[1], path.c_str()) != 0)
557 err("failed to symlink '%s' to '%s'", argv[1], path.c_str());
558 }
559 else
560 {
561 if (File_Copy(argv[1], path.c_str()) != 0)
562 err("failed to copy '%s' to '%s'", argv[1], path.c_str());
563
564 // set mod explicitly
565 // w by owner only; RX for all.
566 chmod(path.c_str(),
567 S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
568 mike 1.1
569 printf("Created %s\n", path.c_str());
570 }
571 }
572
573 // Load module:
574 MI_Module* module = LoadModule(argv[1]);
575 if (!module)
576 err("failed to load provider library: %s", argv[1]);
577
578 // Generate .reg file in each namespace directory:
579 for (size_t i = 0; i < opts.nsDirs.size(); i++)
580 {
581 // Open output file (using basename of library):
582 string baseLibName = BaseLibName(argv[1]);
583 string regFileName = baseLibName + ".reg";
584
585 string path = opts.nsDirs[i];
586 path += "/";
587 path += regFileName;
588
589 mike 1.1 FILE* os = fopen(path.c_str(), "wb");
590
591 if (!os)
592 err("failed to open: %s", path.c_str());
593
594 // Generate the registration file.
595 GenRegFile(os, argc, argv, baseLibName, module, opts);
596
597 printf("Created %s\n", path.c_str());
598
599 fclose(os);
600 }
601
602 // ask server to re-read configuration
603 _RefreshServer();
604
605 return 0;
606 }
|