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