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
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.
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]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
33 #include <sys/param.h>
34 #include <sys/systeminfo.h>
35 #include <sys/sysevent/eventdefs.h>
36 #include <sys/sysevent/dr.h>
38 #include <libnvpair.h>
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.
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
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().
72 * See help() for an example of how pcidr can be specified in a sysevent.conf
77 * plugin search paths (searched in order specified);
78 * macros begin MACRO_BEGTOK and end with MACRO_ENDTOK;
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
84 * NOTE: if plugin search algorithm is changed starting with find_plugin(),
85 * please update documentation here.
88 * SI_PLATFORM = cmd of same name in sysinfo(2)
89 * SI_MACHINE = cmd of same name in sysinfo(2)
91 #define MACRO_BEGTOK "${"
92 #define MACRO_ENDTOK "}"
93 #define SI_MACRO "SI_"
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
,
100 static int plugin_paths_len
= sizeof (plugin_paths
) / sizeof (plugin_paths
[0]);
103 static nvlist_t
*nvlistp
= NULL
; /* attribute list */
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);
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 *);
125 /* since the handler is not public, we don't expose its usage normally */
128 "%s [-h] [-s] [-v <level>] [-l <log_file>] <attributes>\n"
131 " -s turn OFF messages to the syslog (use syslog by default)\n"
133 " -v verbose mode; <level> range is %d..%d; default is %d\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"
140 " whitespace seperated strings of <name>=<value> pairs\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"
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"
154 prg
, MIN_DLVL
, MAX_DLVL
, dlvl
,
155 prg
, MAX_DLVL
, /* Example 1 */
156 prg
, DWARN
); /* Example 2 */
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_*()
167 nvadd(nvlist_t
*listp
, char *name
, char *value
, data_type_t type
)
173 case DATA_TYPE_STRING
:
174 rv
= nvlist_add_string(listp
, name
, value
);
176 dprint(DDEBUG
, "%s: nvlist_add_string() failed: "
177 "name = %s, value = %s, rv = %d\n",
178 fn
, name
, value
, rv
);
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()
187 dprint(DDEBUG
, "%s: unsupported type: name = %s, value = %s, "
188 "type = 0x%x\n", fn
, name
, value
, (int)type
);
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
201 * return: allocated nvlist on success, exits otherwise
203 * recognized names will have predetermined types, while all others will have
204 * values of type string
207 parse_argv_attr(int argc
, char **argv
, int *argip
)
209 char *fn
= "parse_argv_attr";
211 nvlist_t
*attrlistp
= NULL
;
212 char *eqp
, *name
, *value
;
215 assert(*argip
< argc
);
217 rv
= nvlist_alloc(&attrlistp
, NV_UNIQUE_NAME_TYPE
, 0);
219 dprint(DDEBUG
, "%s: nvlist_alloc() failed: rv = %d\n", fn
, rv
);
223 for (i
= *argip
; i
< argc
; i
++) {
224 eqp
= strchr(argv
[i
], '=');
231 if (*name
== '\0' || *value
== '\0')
234 if (pcidr_name2type(name
, &type
) != 0)
235 type
= DATA_TYPE_STRING
;
237 rv
= nvadd(attrlistp
, name
, value
, type
);
239 dprint(DDEBUG
, "%s: nvadd() failed: attribute \"%s\", "
240 "value = %s, type = %d, rv = %d\n",
241 fn
, name
, value
, (int)type
, rv
);
254 dprint(DDEBUG
, "%s: bad attribute argv[%d]: \"%s\"\n", fn
, i
, argv
[i
]);
256 nvlist_free(attrlistp
);
264 } si_cmd_nametab
[] = {
265 SI_PLATFORM
, "SI_PLATFORM",
266 SI_MACHINE
, "SI_MACHINE",
268 static int si_cmd_nametab_len
=
269 sizeof (si_cmd_nametab
) / sizeof (si_cmd_nametab
[0]);
272 si_name2cmd(char *name
)
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
);
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
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
296 static macro_list_t
*
297 parse_macros(char *const str
, int *lenp
)
302 int i
, begtok_len
, endtok_len
;
304 begtok_len
= strlen(MACRO_BEGTOK
);
305 endtok_len
= strlen(MACRO_ENDTOK
);
307 /* count all occurrences */
308 for (beg
= str
, i
= 0; beg
!= NULL
; i
++) {
309 beg
= strstr(beg
, MACRO_BEGTOK
);
312 end
= strstr(beg
+ begtok_len
, MACRO_ENDTOK
);
315 beg
= end
+ endtok_len
;
321 lp
= pcidr_malloc(sizeof (macro_list_t
) * i
);
323 for (beg
= str
, i
= 0; i
< *lenp
; i
++) {
324 beg
= strstr(beg
, MACRO_BEGTOK
);
326 end
= strstr(beg
+ begtok_len
, MACRO_ENDTOK
);
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
);
334 lp
[i
].end
= (end
+ endtok_len
) - 1;
336 beg
= end
+ endtok_len
;
343 free_macros(macro_list_t
*lp
, int len
)
347 for (i
= 0; i
< len
; i
++)
354 * evaluates any macros in <opath> and returns allocated string on success;
358 parse_path(char *const opath
)
360 char *fn
= "parse_path";
361 char buf
[MAXPATHLEN
+ 1];
362 int bufsize
= sizeof (buf
) / sizeof (buf
[0]);
364 int sibufsize
= sizeof (sibuf
) / sizeof (sibuf
[0]);
366 char *path
, *pathp
, *pathend
;
367 int rv
, i
, lplen
, si_cmd
, pathlen
, okmacro
, si_macro_len
;
371 * make a copy so we can modify it for easier parsing;
372 * lp members will refer to the copy
374 path
= strdup(opath
);
375 lp
= parse_macros(path
, &lplen
);
380 si_macro_len
= strlen(SI_MACRO
);
381 pathlen
= strlen(path
);
382 pathend
= &path
[pathlen
- 1];
385 for (i
= 0; i
< lplen
; i
++) {
387 sz
= strlcat(buf
, pathp
, bufsize
);
388 assert(sz
< bufsize
);
391 if (strncmp(lp
[i
].name
, SI_MACRO
, si_macro_len
) == 0) {
392 si_cmd
= si_name2cmd(lp
[i
].name
);
395 rv
= sysinfo(si_cmd
, sibuf
, sibufsize
);
397 dprint(DDEBUG
, "%s: sysinfo cmd %d failed: "
398 "errno = %d\n", fn
, si_cmd
, errno
);
402 sz
= strlcat(buf
, sibuf
, bufsize
);
403 assert(sz
< bufsize
);
406 /* check for unrecognized macros */
408 pathp
= lp
[i
].end
+ 1;
412 if (pathp
< pathend
) {
413 sz
= strlcat(buf
, pathp
, bufsize
);
414 assert(sz
< bufsize
);
417 free_macros(lp
, lplen
);
420 return (strdup(buf
));
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
432 find_plugin(nvlist_t
*attrlistp
)
434 char *fn
= "find_plugin";
439 for (i
= 0; i
< plugin_paths_len
; i
++) {
440 path
= parse_path(plugin_paths
[i
]);
442 dprint(DDEBUG
, "%s: error parsing path %s\n", fn
,
447 rv
= stat(path
, &statbuf
);
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 "
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)
469 do_plugin(char *path
, nvlist_t
*attrlistp
, pcidr_opt_t
*optp
)
471 char *fn
= "do_plugin";
477 dlh
= dlopen(path
, RTLD_LAZY
| RTLD_GLOBAL
);
479 dprint(DDEBUG
, "%s: dlopen() failed: %s\n", fn
, dlerror());
484 if (sigfillset(&set
) != 0) {
485 dprint(DDEBUG
, "%s: sigfillset() failed: errno = %d\n", fn
,
490 if (sigprocmask(SIG_BLOCK
, &set
, &oset
) != 0) {
491 dprint(DDEBUG
, "%s: blocking signals with sigprocmask() "
492 "failed: errno = %d\n", fn
, errno
);
497 fp
= (pcidr_plugin_t
)dlsym(dlh
, PCIDR_PLUGIN_SYMSTR
);
499 dprint(DDEBUG
, "%s: dlsym() failed: %s\n", fn
, dlerror());
503 rv
= fp(attrlistp
, optp
);
505 dprint(DDEBUG
, "%s: %s() failed: rv = %d\n", fn
,
506 PCIDR_PLUGIN_SYMSTR
, rv
);
508 if (sigprocmask(SIG_SETMASK
, &oset
, NULL
) != 0) {
509 dprint(DDEBUG
, "%s: unblocking signals with sigprocmask() "
510 "failed: errno = %d\n", fn
, errno
);
527 nvlist_free(nvlistp
);
537 main(int argc
, char **argv
)
540 char *dfile
= NULL
, *plugin_path
= NULL
;
542 pcidr_opt_t plugin_opt
;
546 extern int optind
, optopt
;
550 assert(MIN_DLVL
== 0);
552 assert(MIN_DLVL
== DNONE
);
553 assert(MAX_DLVL
== dpritab_len
- 1);
555 (void) atexit(exiter
);
560 openlog(prg
, LOG_PID
| LOG_CONS
, LOG_DAEMON
);
570 while ((c
= getopt(argc
, argv
, optstr
)) != -1) {
586 dprint(DWARN
, "bad option: %c\n", optopt
);
592 * [ -l ] do file option first so we can still get msgs if -s is used
595 if (strcmp(dfile
, "-") == 0) {
596 /* ignore if stdout is not open/valid */
598 if (stdout
!= NULL
&&
599 fstat(fileno(stdout
), &statbuf
) == 0)
602 dfp
= fopen(dfile
, "a");
604 dprint(DWARN
, "cannot open %s: %s\n",
605 dfile
, strerror(errno
));
612 if (dlvl
< MIN_DLVL
|| dlvl
> MAX_DLVL
) {
613 dprint(DWARN
, "bad arg for -v: %d\n", dlvl
);
619 dprint(DWARN
, "missing attribute arguments\n");
623 nvlistp
= parse_argv_attr(argc
, argv
, &argi
);
624 if (nvlistp
== NULL
) {
625 dprint(DWARN
, "attribute parsing error\n");
629 (void) memset(&plugin_opt
, 0, sizeof (plugin_opt
));
630 plugin_opt
.logopt
.dlvl
= dlvl
;
631 plugin_opt
.logopt
.prg
= prg
;
632 plugin_opt
.logopt
.dfp
= dfp
;
633 plugin_opt
.logopt
.dsys
= dsys
;
635 dprint(DINFO
, "=== sysevent attributes ========================\n");
636 pcidr_print_attrlist(DINFO
, nvlistp
, NULL
);
637 dprint(DINFO
, "================================================\n");
639 plugin_path
= find_plugin(nvlistp
);
640 if (plugin_path
== NULL
) {
641 dprint(DWARN
, "cannot find plugin\n");
644 dprint(DINFO
, "using plugin: %s\n\n", plugin_path
);
646 rv
= do_plugin(plugin_path
, nvlistp
, &plugin_opt
);
648 dprint(DWARN
, "plugin %s failed\n", plugin_path
);
650 if (plugin_path
!= NULL
)