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
help()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
nvadd(nvlist_t * listp,char * name,char * value,data_type_t type)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 *
parse_argv_attr(int argc,char ** argv,int * argip)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
si_name2cmd(char * name)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 *
parse_macros(char * const str,int * lenp)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
free_macros(macro_list_t * lp,int len)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 *
parse_path(char * const opath)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 *
find_plugin(nvlist_t * attrlistp)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
do_plugin(char * path,nvlist_t * attrlistp,pcidr_opt_t * optp)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
exiter()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
main(int argc,char ** argv)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