1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <strings.h> 33 #include <string.h> 34 #include <errno.h> 35 #include <sys/param.h> 36 #include <sys/systeminfo.h> 37 #include <sys/sysevent/eventdefs.h> 38 #include <sys/sysevent/dr.h> 39 #include <syslog.h> 40 #include <libnvpair.h> 41 #include <stdarg.h> 42 #include <assert.h> 43 #include <sys/stat.h> 44 #include <dlfcn.h> 45 #include <signal.h> 46 #include <pcidr.h> 47 48 /* 49 * pcidr takes in arguments of the form specified in the help() routine 50 * including a set of name=value pairs, then looks up a plugin (shared object) 51 * based on <plugin_paths> and however find_plugin() operates. The entry 52 * point of the plugin is <PCIDR_PLUGIN_SYM> and has the type 53 * <pcidr_plugin_t>. Plugins must use the <PCIDR_PLUGIN_PROTO> macro to 54 * define their entry point. 55 * 56 * The name=value arguments are intended to be used as a mechanism to pass 57 * arbitrary sysevent attributes using the macro expansion capability provided 58 * by the syseventd SLM processing sysevent.conf files (i.e. specifying 59 * "$attribute" arguments for the handler in a .conf file entry). They are 60 * converted into an nvlist_t (see libnvpair(3LIB)) by converting the values 61 * of recognized names into appropriate types using pcidr_name2type() and 62 * leaving all others as string types. Because pcidr is used as a sysevent.conf 63 * handler, the format of the value string for non-string attributes in each 64 * name=value argument must match that used by the syseventd macro capability 65 * 66 * The plugin will be passed this (nvlist_t *) along with a (pcidr_opt_t *) arg 67 * for other options. While pcidr does some basic checking of arguments, it 68 * leaves any name=value check (after conversion) up to each plugin. Note 69 * that pcidr_check_attrs() is used by the default plugin and can be used by 70 * any plugin that support the same or a superset of its attributes. If the 71 * default plugin supports additional publishers, it should be updated in 72 * pcidr_check_attrs(). 73 * 74 * See help() for an example of how pcidr can be specified in a sysevent.conf 75 * file. 76 */ 77 78 /* 79 * plugin search paths (searched in order specified); 80 * macros begin MACRO_BEGTOK and end with MACRO_ENDTOK; 81 * 82 * be sure to update parse_path() and its support functions whenever macros 83 * are updated e.g. si_name2cmd(), as well as substring tokens (prefix or 84 * suffix) used to recognize different types of macros e.g. SI_MACRO 85 * 86 * NOTE: if plugin search algorithm is changed starting with find_plugin(), 87 * please update documentation here. 88 * 89 * macros: 90 * SI_PLATFORM = cmd of same name in sysinfo(2) 91 * SI_MACHINE = cmd of same name in sysinfo(2) 92 */ 93 #define MACRO_BEGTOK "${" 94 #define MACRO_ENDTOK "}" 95 #define SI_MACRO "SI_" 96 97 static char *plugin_paths[] = { 98 "/usr/platform/${SI_PLATFORM}/lib/pci/" PCIDR_PLUGIN_NAME, 99 "/usr/platform/${SI_MACHINE}/lib/pci/" PCIDR_PLUGIN_NAME, 100 "/usr/lib/pci/" PCIDR_PLUGIN_NAME, 101 }; 102 static int plugin_paths_len = sizeof (plugin_paths) / sizeof (plugin_paths[0]); 103 104 105 static nvlist_t *nvlistp = NULL; /* attribute list */ 106 107 typedef struct { 108 char *name; 109 char *beg; 110 char *end; 111 } macro_list_t; 112 static macro_list_t *parse_macros(char *const, int *); 113 static void free_macros(macro_list_t *, int); 114 static char *parse_path(char *const); 115 static void help(); 116 static void exiter(); 117 static char *find_plugin(nvlist_t *); 118 static int do_plugin(char *, nvlist_t *, pcidr_opt_t *); 119 static int nvadd(nvlist_t *, char *, char *, data_type_t); 120 static nvlist_t *parse_argv_attr(int, char **, int *); 121 static int si_name2cmd(char *); 122 123 124 static void 125 help() 126 { 127 /* since the handler is not public, we don't expose its usage normally */ 128 #ifdef DEBUG 129 (void) printf( 130 "%s [-h] [-s] [-v <level>] [-l <log_file>] <attributes>\n" 131 " -h help\n" 132 "\n" 133 " -s turn OFF messages to the syslog (use syslog by default)\n" 134 "\n" 135 " -v verbose mode; <level> range is %d..%d; default is %d\n" 136 "\n" 137 " -l also log messages to <log_file> (in addition to using\n" 138 " the syslog if that option is not disabled);\n" 139 " if <log_file> is '-', stdout is used\n" 140 "\n" 141 " <attributes>\n" 142 " whitespace seperated strings of <name>=<value> pairs\n" 143 "\n" 144 "Example 1 (command line):\n" 145 " %s -s -v%d -l- \\\n" 146 " class=EC_dr subclass=ESC_dr_req publisher=pcie_pci \\\n" 147 " dr_request_type=dr_request_outgoing_resource \\\n" 148 " dr_ap_id=/devices/foo/bar\n" 149 "\n" 150 "Example 2 (/etc/sysevent/config/SUNW,sysevent.conf entry):\n" 151 " EC_dr ESC_dr_req SUNW pcie_pci - - - %s -v%d -l/tmp/log \\\n" 152 " class=$class subclass=$subclass publisher=$publisher \\\n" 153 " dr_request_type=$dr_request_type\\\n" 154 " dr_ap_id=$dr_ap_id\n" 155 "\n", 156 prg, MIN_DLVL, MAX_DLVL, dlvl, 157 prg, MAX_DLVL, /* Example 1 */ 158 prg, DWARN); /* Example 2 */ 159 #endif 160 } 161 162 163 /* 164 * will convert <value> from a string to the type indicated by <type> 165 * and will add it with <name> to nvlist_t <listp>; function returns the same 166 * value as nvlist_add_*() 167 */ 168 static int 169 nvadd(nvlist_t *listp, char *name, char *value, data_type_t type) 170 { 171 char *fn = "nvadd"; 172 int rv = 0; 173 174 switch (type) { 175 case DATA_TYPE_STRING: 176 rv = nvlist_add_string(listp, name, value); 177 if (rv != 0) { 178 dprint(DDEBUG, "%s: nvlist_add_string() failed: " 179 "name = %s, value = %s, rv = %d\n", 180 fn, name, value, rv); 181 } 182 break; 183 /* 184 * Conversion must support whatever string format syseventd uses for 185 * its .conf macros; in addition, minimum types supported must match 186 * those for pcidr_name2type() 187 */ 188 default: 189 dprint(DDEBUG, "%s: unsupported type: name = %s, value = %s, " 190 "type = 0x%x\n", fn, name, value, (int)type); 191 rv = EINVAL; 192 } 193 194 return (rv); 195 } 196 197 198 /* 199 * argc: length of argv 200 * argv: each string starting from index <argip> has the format "name=value" 201 * argip: starting index in <argv>; also used to return ending index 202 * 203 * return: allocated nvlist on success, exits otherwise 204 * 205 * recognized names will have predetermined types, while all others will have 206 * values of type string 207 */ 208 static nvlist_t * 209 parse_argv_attr(int argc, char **argv, int *argip) 210 { 211 char *fn = "parse_argv_attr"; 212 int rv, i; 213 nvlist_t *attrlistp = NULL; 214 char *eqp, *name, *value; 215 data_type_t type; 216 217 assert(*argip < argc); 218 219 rv = nvlist_alloc(&attrlistp, NV_UNIQUE_NAME_TYPE, 0); 220 if (rv != 0) { 221 dprint(DDEBUG, "%s: nvlist_alloc() failed: rv = %d\n", fn, rv); 222 goto ERR; 223 } 224 225 for (i = *argip; i < argc; i++) { 226 eqp = strchr(argv[i], '='); 227 if (eqp == NULL) 228 goto ERR_ARG; 229 *eqp = '\0'; 230 name = argv[i]; 231 value = eqp; 232 value++; 233 if (*name == '\0' || *value == '\0') 234 goto ERR_ARG; 235 236 if (pcidr_name2type(name, &type) != 0) 237 type = DATA_TYPE_STRING; 238 239 rv = nvadd(attrlistp, name, value, type); 240 if (rv != 0) { 241 dprint(DDEBUG, "%s: nvadd() failed: attribute \"%s\", " 242 "value = %s, type = %d, rv = %d\n", 243 fn, name, value, (int)type, rv); 244 goto ERR; 245 } 246 *eqp = '='; 247 } 248 249 *argip = i; 250 return (attrlistp); 251 252 /*NOTREACHED*/ 253 ERR_ARG: 254 if (eqp != NULL) 255 *eqp = '='; 256 dprint(DDEBUG, "%s: bad attribute argv[%d]: \"%s\"\n", fn, i, argv[i]); 257 ERR: 258 if (attrlistp != NULL) 259 nvlist_free(attrlistp); 260 return (NULL); 261 } 262 263 264 static struct { 265 int cmd; 266 char *name; 267 } si_cmd_nametab[] = { 268 SI_PLATFORM, "SI_PLATFORM", 269 SI_MACHINE, "SI_MACHINE", 270 }; 271 static int si_cmd_nametab_len = 272 sizeof (si_cmd_nametab) / sizeof (si_cmd_nametab[0]); 273 274 static int 275 si_name2cmd(char *name) 276 { 277 int i; 278 279 for (i = 0; i < si_cmd_nametab_len; i++) { 280 if (strcmp(name, si_cmd_nametab[i].name) == 0) 281 return (si_cmd_nametab[i].cmd); 282 } 283 return (-1); 284 } 285 286 287 /* 288 * finds occurences of substrings surrounded (delimited) by MACRO_BEGTOK and 289 * MACRO_ENDTOK in <str>; 290 * returns an allocated array of macro_list_t whose length is 291 * returned through <lenp>; array entries will be in order of the occurrence; 292 * else returns NULL if none are found 293 * 294 * macro_list_t members: 295 * char *name = allocated string containing name without macro delimiters 296 * char *beg = location in <str> at _first char_ of MACRO_BEGTOK 297 * char *end = location in <str> at _last char_ of MACRO_ENDTOK 298 */ 299 static macro_list_t * 300 parse_macros(char *const str, int *lenp) 301 { 302 char *beg, *end; 303 macro_list_t *lp; 304 size_t size; 305 int i, begtok_len, endtok_len; 306 307 begtok_len = strlen(MACRO_BEGTOK); 308 endtok_len = strlen(MACRO_ENDTOK); 309 310 /* count all occurrences */ 311 for (beg = str, i = 0; beg != NULL; i++) { 312 beg = strstr(beg, MACRO_BEGTOK); 313 if (beg == NULL) 314 break; 315 end = strstr(beg + begtok_len, MACRO_ENDTOK); 316 if (end == NULL) 317 break; 318 beg = end + endtok_len; 319 } 320 if (i <= 0) 321 return (NULL); 322 323 *lenp = i; 324 lp = pcidr_malloc(sizeof (macro_list_t) * i); 325 326 for (beg = str, i = 0; i < *lenp; i++) { 327 beg = strstr(beg, MACRO_BEGTOK); 328 assert(beg != NULL); 329 end = strstr(beg + begtok_len, MACRO_ENDTOK); 330 assert(end != NULL); 331 332 size = (end - (beg + begtok_len)) + 1; 333 lp[i].name = pcidr_malloc(size * sizeof (char)); 334 (void) strlcpy(lp[i].name, beg + begtok_len, size); 335 336 lp[i].beg = beg; 337 lp[i].end = (end + endtok_len) - 1; 338 339 beg = end + endtok_len; 340 } 341 342 return (lp); 343 } 344 345 static void 346 free_macros(macro_list_t *lp, int len) 347 { 348 int i; 349 350 for (i = 0; i < len; i++) 351 free(lp[i].name); 352 free(lp); 353 } 354 355 356 /* 357 * evaluates any macros in <opath> and returns allocated string on success; 358 * else NULL 359 */ 360 static char * 361 parse_path(char *const opath) 362 { 363 char *fn = "parse_path"; 364 char buf[MAXPATHLEN + 1]; 365 int bufsize = sizeof (buf) / sizeof (buf[0]); 366 char sibuf[257]; 367 int sibufsize = sizeof (sibuf) / sizeof (sibuf[0]); 368 macro_list_t *lp; 369 char *path, *pathp, *pathend; 370 int rv, i, lplen, si_cmd, pathlen, okmacro, si_macro_len; 371 size_t sz; 372 373 /* 374 * make a copy so we can modify it for easier parsing; 375 * lp members will refer to the copy 376 */ 377 path = strdup(opath); 378 lp = parse_macros(path, &lplen); 379 if (lp == NULL) 380 return (path); 381 382 rv = 0; 383 si_macro_len = strlen(SI_MACRO); 384 pathlen = strlen(path); 385 pathend = &path[pathlen - 1]; 386 pathp = path; 387 buf[0] = '\0'; 388 for (i = 0; i < lplen; i++) { 389 lp[i].beg[0] = '\0'; 390 sz = strlcat(buf, pathp, bufsize); 391 assert(sz < bufsize); 392 393 okmacro = 0; 394 if (strncmp(lp[i].name, SI_MACRO, si_macro_len) == 0) { 395 si_cmd = si_name2cmd(lp[i].name); 396 assert(si_cmd >= 0); 397 398 rv = sysinfo(si_cmd, sibuf, sibufsize); 399 if (rv < 0) { 400 dprint(DDEBUG, "%s: sysinfo cmd %d failed: " 401 "errno = %d\n", fn, si_cmd, errno); 402 goto OUT; 403 } 404 405 sz = strlcat(buf, sibuf, bufsize); 406 assert(sz < bufsize); 407 okmacro = 1; 408 } 409 /* check for unrecognized macros */ 410 assert(okmacro); 411 pathp = lp[i].end + 1; 412 } 413 414 rv = 0; 415 if (pathp < pathend) { 416 sz = strlcat(buf, pathp, bufsize); 417 assert(sz < bufsize); 418 } 419 OUT: 420 free_macros(lp, lplen); 421 free(path); 422 if (rv == 0) 423 return (strdup(buf)); 424 return (NULL); 425 } 426 427 428 /* 429 * returns allocated string containing plugin path which caller must free; 430 * else NULL; <attrlistp> is for future use if attributes can be used to 431 * determin plugin 432 */ 433 /*ARGSUSED*/ 434 static char * 435 find_plugin(nvlist_t *attrlistp) 436 { 437 char *fn = "find_plugin"; 438 char *path = NULL; 439 int i, rv; 440 struct stat statbuf; 441 442 for (i = 0; i < plugin_paths_len; i++) { 443 path = parse_path(plugin_paths[i]); 444 if (path == NULL) { 445 dprint(DDEBUG, "%s: error parsing path %s\n", fn, 446 path); 447 return (NULL); 448 } 449 450 rv = stat(path, &statbuf); 451 if (rv < 0) 452 dprint(DDEBUG, "%s: stat on %s failed: " 453 "errno = %d\n", fn, path, errno); 454 else if ((statbuf.st_mode & S_IFMT) != S_IFREG) 455 dprint(DDEBUG, "%s: %s is not a regular " 456 "file\n", fn, path); 457 else 458 return (path); 459 460 free(path); 461 } 462 return (NULL); 463 } 464 465 466 /* 467 * load plugin specified by <path> and pass the proceeding arguments 468 * to the plugin interface; returns 0 on success (likewise for 469 * the plugin function) 470 */ 471 static int 472 do_plugin(char *path, nvlist_t *attrlistp, pcidr_opt_t *optp) 473 { 474 char *fn = "do_plugin"; 475 int rv; 476 void *dlh; 477 sigset_t set, oset; 478 pcidr_plugin_t fp; 479 480 dlh = dlopen(path, RTLD_LAZY | RTLD_GLOBAL); 481 if (dlh == NULL) { 482 dprint(DDEBUG, "%s: dlopen() failed: %s\n", fn, dlerror()); 483 rv = EINVAL; 484 goto OUT; 485 } 486 487 if (sigfillset(&set) != 0) { 488 dprint(DDEBUG, "%s: sigfillset() failed: errno = %d\n", fn, 489 errno); 490 rv = errno; 491 goto OUT; 492 } 493 if (sigprocmask(SIG_BLOCK, &set, &oset) != 0) { 494 dprint(DDEBUG, "%s: blocking signals with sigprocmask() " 495 "failed: errno = %d\n", fn, errno); 496 rv = errno; 497 goto OUT; 498 } 499 500 fp = (pcidr_plugin_t)dlsym(dlh, PCIDR_PLUGIN_SYMSTR); 501 if (fp == NULL) { 502 dprint(DDEBUG, "%s: dlsym() failed: %s\n", fn, dlerror()); 503 rv = EINVAL; 504 goto OUT; 505 } 506 rv = fp(attrlistp, optp); 507 if (rv != 0) 508 dprint(DDEBUG, "%s: %s() failed: rv = %d\n", fn, 509 PCIDR_PLUGIN_SYMSTR, rv); 510 511 if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) { 512 dprint(DDEBUG, "%s: unblocking signals with sigprocmask() " 513 "failed: errno = %d\n", fn, errno); 514 rv = errno; 515 goto OUT; 516 } 517 OUT: 518 if (dlh != NULL) 519 (void) dlclose(dlh); 520 return (rv); 521 } 522 523 524 static void 525 exiter() 526 { 527 extern FILE *dfp; 528 529 if (nvlistp != NULL) 530 nvlist_free(nvlistp); 531 if (dfp != NULL) 532 (void) fclose(dfp); 533 #ifdef DEBUG 534 closelog(); 535 #endif 536 } 537 538 539 int 540 main(int argc, char **argv) 541 { 542 int rv, argi; 543 char *dfile = NULL, *plugin_path = NULL; 544 struct stat statbuf; 545 pcidr_opt_t plugin_opt; 546 char *optstr = NULL; 547 548 extern char *optarg; 549 extern int optind, optopt; 550 int c; 551 552 /*CONSTCOND*/ 553 assert(MIN_DLVL == 0); 554 /*CONSTCOND*/ 555 assert(MIN_DLVL == DNONE); 556 assert(MAX_DLVL == dpritab_len - 1); 557 558 (void) atexit(exiter); 559 prg = argv[0]; 560 dfp = NULL; 561 562 #ifdef DEBUG 563 openlog(prg, LOG_PID | LOG_CONS, LOG_DAEMON); 564 dlvl = DWARN; 565 dsys = 1; 566 optstr = "hsv:l:"; 567 #else 568 dlvl = DNONE; 569 dsys = 0; 570 optstr = "sv:l:"; 571 #endif 572 573 while ((c = getopt(argc, argv, optstr)) != -1) { 574 switch (c) { 575 case 'h': 576 help(); 577 exit(0); 578 break; 579 case 's': 580 dsys = 0; 581 break; 582 case 'v': 583 dlvl = atoi(optarg); 584 break; 585 case 'l': 586 dfile = optarg; 587 break; 588 default: 589 dprint(DWARN, "bad option: %c\n", optopt); 590 return (EINVAL); 591 } 592 } 593 594 /* 595 * [ -l ] do file option first so we can still get msgs if -s is used 596 */ 597 if (dfile != NULL) { 598 if (strcmp(dfile, "-") == 0) { 599 /* ignore if stdout is not open/valid */ 600 dfp = NULL; 601 if (stdout != NULL && 602 fstat(fileno(stdout), &statbuf) == 0) 603 dfp = stdout; 604 } else { 605 dfp = fopen(dfile, "a"); 606 if (dfp == NULL) { 607 dprint(DWARN, "cannot open %s: %s\n", 608 dfile, strerror(errno)); 609 return (EINVAL); 610 } 611 } 612 } 613 614 /* [ -v ] */ 615 if (dlvl < MIN_DLVL || dlvl > MAX_DLVL) { 616 dprint(DWARN, "bad arg for -v: %d\n", dlvl); 617 return (EINVAL); 618 } 619 620 argi = optind; 621 if (argi >= argc) { 622 dprint(DWARN, "missing attribute arguments\n"); 623 return (EINVAL); 624 } 625 626 nvlistp = parse_argv_attr(argc, argv, &argi); 627 if (nvlistp == NULL) { 628 dprint(DWARN, "attribute parsing error\n"); 629 return (EINVAL); 630 } 631 632 (void) memset(&plugin_opt, 0, sizeof (plugin_opt)); 633 plugin_opt.logopt.dlvl = dlvl; 634 plugin_opt.logopt.prg = prg; 635 plugin_opt.logopt.dfp = dfp; 636 plugin_opt.logopt.dsys = dsys; 637 638 dprint(DINFO, "=== sysevent attributes ========================\n"); 639 pcidr_print_attrlist(DINFO, nvlistp, NULL); 640 dprint(DINFO, "================================================\n"); 641 642 plugin_path = find_plugin(nvlistp); 643 if (plugin_path == NULL) { 644 dprint(DWARN, "cannot find plugin\n"); 645 return (EINVAL); 646 } 647 dprint(DINFO, "using plugin: %s\n\n", plugin_path); 648 649 rv = do_plugin(plugin_path, nvlistp, &plugin_opt); 650 if (rv != 0) { 651 dprint(DWARN, "plugin %s failed\n", plugin_path); 652 } 653 if (plugin_path != NULL) 654 free(plugin_path); 655 return (rv); 656 } 657