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