xref: /illumos-gate/usr/src/cmd/pcidr/pcidr.c (revision aab83bb8)
170025d76Sjohnny /*
270025d76Sjohnny  * CDDL HEADER START
370025d76Sjohnny  *
470025d76Sjohnny  * The contents of this file are subject to the terms of the
570025d76Sjohnny  * Common Development and Distribution License, Version 1.0 only
670025d76Sjohnny  * (the "License").  You may not use this file except in compliance
770025d76Sjohnny  * with the License.
870025d76Sjohnny  *
970025d76Sjohnny  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1070025d76Sjohnny  * or http://www.opensolaris.org/os/licensing.
1170025d76Sjohnny  * See the License for the specific language governing permissions
1270025d76Sjohnny  * and limitations under the License.
1370025d76Sjohnny  *
1470025d76Sjohnny  * When distributing Covered Code, include this CDDL HEADER in each
1570025d76Sjohnny  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1670025d76Sjohnny  * If applicable, add the following below this CDDL HEADER, with the
1770025d76Sjohnny  * fields enclosed by brackets "[]" replaced with your own identifying
1870025d76Sjohnny  * information: Portions Copyright [yyyy] [name of copyright owner]
1970025d76Sjohnny  *
2070025d76Sjohnny  * CDDL HEADER END
2170025d76Sjohnny  */
2270025d76Sjohnny /*
2370025d76Sjohnny  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
2470025d76Sjohnny  * Use is subject to license terms.
2570025d76Sjohnny  */
2670025d76Sjohnny 
2770025d76Sjohnny #include <stdio.h>
2870025d76Sjohnny #include <stdlib.h>
2970025d76Sjohnny #include <unistd.h>
3070025d76Sjohnny #include <strings.h>
3170025d76Sjohnny #include <string.h>
3270025d76Sjohnny #include <errno.h>
3370025d76Sjohnny #include <sys/param.h>
3470025d76Sjohnny #include <sys/systeminfo.h>
3570025d76Sjohnny #include <sys/sysevent/eventdefs.h>
3670025d76Sjohnny #include <sys/sysevent/dr.h>
3770025d76Sjohnny #include <syslog.h>
3870025d76Sjohnny #include <libnvpair.h>
3970025d76Sjohnny #include <stdarg.h>
4070025d76Sjohnny #include <assert.h>
4170025d76Sjohnny #include <sys/stat.h>
4270025d76Sjohnny #include <dlfcn.h>
4370025d76Sjohnny #include <signal.h>
4470025d76Sjohnny #include <pcidr.h>
4570025d76Sjohnny 
4670025d76Sjohnny /*
4770025d76Sjohnny  * pcidr takes in arguments of the form specified in the help() routine
4870025d76Sjohnny  * including a set of name=value pairs, then looks up a plugin (shared object)
4970025d76Sjohnny  * based on <plugin_paths> and however find_plugin() operates.  The entry
5070025d76Sjohnny  * point of the plugin is <PCIDR_PLUGIN_SYM> and has the type
5170025d76Sjohnny  * <pcidr_plugin_t>.  Plugins must use the <PCIDR_PLUGIN_PROTO> macro to
5270025d76Sjohnny  * define their entry point.
5370025d76Sjohnny  *
5470025d76Sjohnny  * The name=value arguments are intended to be used as a mechanism to pass
5570025d76Sjohnny  * arbitrary sysevent attributes using the macro expansion capability provided
5670025d76Sjohnny  * by the syseventd SLM processing sysevent.conf files (i.e. specifying
5770025d76Sjohnny  * "$attribute" arguments for the handler in a .conf file entry). They are
5870025d76Sjohnny  * converted into an nvlist_t (see libnvpair(3LIB)) by converting the values
5970025d76Sjohnny  * of recognized names into appropriate types using pcidr_name2type() and
6070025d76Sjohnny  * leaving all others as string types. Because pcidr is used as a sysevent.conf
6170025d76Sjohnny  * handler, the format of the value string for non-string attributes in each
6270025d76Sjohnny  * name=value argument must match that used by the syseventd macro capability
6370025d76Sjohnny  *
6470025d76Sjohnny  * The plugin will be passed this (nvlist_t *) along with a (pcidr_opt_t *) arg
6570025d76Sjohnny  * for other options.  While pcidr does some basic checking of arguments, it
6670025d76Sjohnny  * leaves any name=value check (after conversion) up to each plugin.  Note
6770025d76Sjohnny  * that pcidr_check_attrs() is used by the default plugin and can be used by
6870025d76Sjohnny  * any plugin that support the same or a superset of its attributes.  If the
6970025d76Sjohnny  * default plugin supports additional publishers, it should be updated in
7070025d76Sjohnny  * pcidr_check_attrs().
7170025d76Sjohnny  *
7270025d76Sjohnny  * See help() for an example of how pcidr can be specified in a sysevent.conf
7370025d76Sjohnny  * file.
7470025d76Sjohnny  */
7570025d76Sjohnny 
7670025d76Sjohnny /*
7770025d76Sjohnny  * plugin search paths (searched in order specified);
7870025d76Sjohnny  * macros begin MACRO_BEGTOK and end with MACRO_ENDTOK;
7970025d76Sjohnny  *
8070025d76Sjohnny  * be sure to update parse_path() and its support functions whenever macros
8170025d76Sjohnny  * are updated e.g. si_name2cmd(), as well as substring tokens (prefix or
8270025d76Sjohnny  * suffix) used to recognize different types of macros e.g. SI_MACRO
8370025d76Sjohnny  *
8470025d76Sjohnny  * NOTE: if plugin search algorithm is changed starting with find_plugin(),
8570025d76Sjohnny  * please update documentation here.
8670025d76Sjohnny  *
8770025d76Sjohnny  * macros:
8870025d76Sjohnny  * SI_PLATFORM = cmd of same name in sysinfo(2)
8970025d76Sjohnny  * SI_MACHINE = cmd of same name in sysinfo(2)
9070025d76Sjohnny  */
9170025d76Sjohnny #define	MACRO_BEGTOK	"${"
9270025d76Sjohnny #define	MACRO_ENDTOK	"}"
9370025d76Sjohnny #define	SI_MACRO	"SI_"
9470025d76Sjohnny 
9570025d76Sjohnny static char *plugin_paths[] = {
9670025d76Sjohnny 	"/usr/platform/${SI_PLATFORM}/lib/pci/" PCIDR_PLUGIN_NAME,
9770025d76Sjohnny 	"/usr/platform/${SI_MACHINE}/lib/pci/" PCIDR_PLUGIN_NAME,
9870025d76Sjohnny 	"/usr/lib/pci/" PCIDR_PLUGIN_NAME,
9970025d76Sjohnny };
10070025d76Sjohnny static int plugin_paths_len = sizeof (plugin_paths) / sizeof (plugin_paths[0]);
10170025d76Sjohnny 
10270025d76Sjohnny 
10370025d76Sjohnny static nvlist_t *nvlistp = NULL;	/* attribute list */
10470025d76Sjohnny 
10570025d76Sjohnny typedef struct {
10670025d76Sjohnny 	char *name;
10770025d76Sjohnny 	char *beg;
10870025d76Sjohnny 	char *end;
10970025d76Sjohnny } macro_list_t;
11070025d76Sjohnny static macro_list_t *parse_macros(char *const, int *);
11170025d76Sjohnny static void free_macros(macro_list_t *, int);
11270025d76Sjohnny static char *parse_path(char *const);
11370025d76Sjohnny static void help();
11470025d76Sjohnny static void exiter();
11570025d76Sjohnny static char *find_plugin(nvlist_t *);
11670025d76Sjohnny static int do_plugin(char *, nvlist_t *, pcidr_opt_t *);
11770025d76Sjohnny static int nvadd(nvlist_t *, char *, char *, data_type_t);
11870025d76Sjohnny static nvlist_t *parse_argv_attr(int, char **, int *);
11970025d76Sjohnny static int si_name2cmd(char *);
12070025d76Sjohnny 
12170025d76Sjohnny 
12270025d76Sjohnny static void
help()12370025d76Sjohnny help()
12470025d76Sjohnny {
12570025d76Sjohnny /* since the handler is not public, we don't expose its usage normally */
12670025d76Sjohnny #ifdef DEBUG
12770025d76Sjohnny 	(void) printf(
12870025d76Sjohnny "%s [-h] [-s] [-v <level>] [-l <log_file>] <attributes>\n"
12970025d76Sjohnny "	-h	help\n"
13070025d76Sjohnny "\n"
13170025d76Sjohnny "	-s	turn OFF messages to the syslog (use syslog by default)\n"
13270025d76Sjohnny "\n"
13370025d76Sjohnny "	-v	verbose mode; <level> range is %d..%d; default is %d\n"
13470025d76Sjohnny "\n"
13570025d76Sjohnny "	-l	also log messages to <log_file> (in addition to using\n"
13670025d76Sjohnny "		the syslog if that option is not disabled);\n"
13770025d76Sjohnny "		if <log_file> is '-', stdout is used\n"
13870025d76Sjohnny "\n"
13970025d76Sjohnny "	<attributes>\n"
14070025d76Sjohnny "		whitespace seperated strings of <name>=<value> pairs\n"
14170025d76Sjohnny "\n"
14270025d76Sjohnny "Example 1 (command line):\n"
14370025d76Sjohnny "	%s -s -v%d -l- \\\n"
14470025d76Sjohnny "		class=EC_dr subclass=ESC_dr_req publisher=pcie_pci \\\n"
14570025d76Sjohnny "		dr_request_type=dr_request_outgoing_resource \\\n"
14670025d76Sjohnny "		dr_ap_id=/devices/foo/bar\n"
14770025d76Sjohnny "\n"
14870025d76Sjohnny "Example 2 (/etc/sysevent/config/SUNW,sysevent.conf entry):\n"
14970025d76Sjohnny "	EC_dr ESC_dr_req SUNW pcie_pci - - - %s -v%d -l/tmp/log \\\n"
15070025d76Sjohnny "		class=$class subclass=$subclass publisher=$publisher \\\n"
15170025d76Sjohnny "		dr_request_type=$dr_request_type\\\n"
15270025d76Sjohnny "		dr_ap_id=$dr_ap_id\n"
15370025d76Sjohnny "\n",
15470025d76Sjohnny 	    prg, MIN_DLVL, MAX_DLVL, dlvl,
15570025d76Sjohnny 	    prg, MAX_DLVL, /* Example 1 */
15670025d76Sjohnny 	    prg, DWARN); /* Example 2 */
15770025d76Sjohnny #endif
15870025d76Sjohnny }
15970025d76Sjohnny 
16070025d76Sjohnny 
16170025d76Sjohnny /*
16270025d76Sjohnny  * will convert <value> from a string to the type indicated by <type>
16370025d76Sjohnny  * and will add it with <name> to nvlist_t <listp>; function returns the same
16470025d76Sjohnny  * value as nvlist_add_*()
16570025d76Sjohnny  */
16670025d76Sjohnny static int
nvadd(nvlist_t * listp,char * name,char * value,data_type_t type)16770025d76Sjohnny nvadd(nvlist_t *listp, char *name, char *value, data_type_t type)
16870025d76Sjohnny {
16970025d76Sjohnny 	char *fn = "nvadd";
17070025d76Sjohnny 	int rv = 0;
17170025d76Sjohnny 
17270025d76Sjohnny 	switch (type) {
17370025d76Sjohnny 	case DATA_TYPE_STRING:
17470025d76Sjohnny 		rv = nvlist_add_string(listp, name, value);
17570025d76Sjohnny 		if (rv != 0) {
17670025d76Sjohnny 			dprint(DDEBUG, "%s: nvlist_add_string() failed: "
17770025d76Sjohnny 			    "name = %s, value = %s, rv = %d\n",
17870025d76Sjohnny 			    fn, name, value, rv);
17970025d76Sjohnny 		}
18070025d76Sjohnny 		break;
18170025d76Sjohnny 	/*
18270025d76Sjohnny 	 * Conversion must support whatever string format syseventd uses for
18370025d76Sjohnny 	 * its .conf macros; in addition, minimum types supported must match
18470025d76Sjohnny 	 * those for pcidr_name2type()
18570025d76Sjohnny 	 */
18670025d76Sjohnny 	default:
18770025d76Sjohnny 		dprint(DDEBUG, "%s: unsupported type: name = %s, value = %s, "
18870025d76Sjohnny 		    "type = 0x%x\n", fn, name, value, (int)type);
18970025d76Sjohnny 		rv = EINVAL;
19070025d76Sjohnny 	}
19170025d76Sjohnny 
19270025d76Sjohnny 	return (rv);
19370025d76Sjohnny }
19470025d76Sjohnny 
19570025d76Sjohnny 
19670025d76Sjohnny /*
19770025d76Sjohnny  * argc: length of argv
19870025d76Sjohnny  * argv: each string starting from index <argip> has the format "name=value"
19970025d76Sjohnny  * argip: starting index in <argv>; also used to return ending index
20070025d76Sjohnny  *
20170025d76Sjohnny  * return: allocated nvlist on success, exits otherwise
20270025d76Sjohnny  *
20370025d76Sjohnny  * recognized names will have predetermined types, while all others will have
20470025d76Sjohnny  * values of type string
20570025d76Sjohnny  */
20670025d76Sjohnny static nvlist_t *
parse_argv_attr(int argc,char ** argv,int * argip)20770025d76Sjohnny parse_argv_attr(int argc, char **argv, int *argip)
20870025d76Sjohnny {
20970025d76Sjohnny 	char *fn = "parse_argv_attr";
21070025d76Sjohnny 	int rv, i;
21170025d76Sjohnny 	nvlist_t *attrlistp = NULL;
21270025d76Sjohnny 	char *eqp, *name, *value;
21370025d76Sjohnny 	data_type_t type;
21470025d76Sjohnny 
21570025d76Sjohnny 	assert(*argip < argc);
21670025d76Sjohnny 
21770025d76Sjohnny 	rv = nvlist_alloc(&attrlistp, NV_UNIQUE_NAME_TYPE, 0);
21870025d76Sjohnny 	if (rv != 0) {
21970025d76Sjohnny 		dprint(DDEBUG, "%s: nvlist_alloc() failed: rv = %d\n", fn, rv);
22070025d76Sjohnny 		goto ERR;
22170025d76Sjohnny 	}
22270025d76Sjohnny 
22370025d76Sjohnny 	for (i = *argip; i < argc; i++) {
22470025d76Sjohnny 		eqp = strchr(argv[i], '=');
22570025d76Sjohnny 		if (eqp == NULL)
22670025d76Sjohnny 			goto ERR_ARG;
22770025d76Sjohnny 		*eqp = '\0';
22870025d76Sjohnny 		name = argv[i];
22970025d76Sjohnny 		value = eqp;
23070025d76Sjohnny 		value++;
23170025d76Sjohnny 		if (*name == '\0' || *value == '\0')
23270025d76Sjohnny 			goto ERR_ARG;
23370025d76Sjohnny 
23470025d76Sjohnny 		if (pcidr_name2type(name, &type) != 0)
23570025d76Sjohnny 			type = DATA_TYPE_STRING;
23670025d76Sjohnny 
23770025d76Sjohnny 		rv = nvadd(attrlistp, name, value, type);
23870025d76Sjohnny 		if (rv != 0) {
23970025d76Sjohnny 			dprint(DDEBUG, "%s: nvadd() failed: attribute \"%s\", "
24070025d76Sjohnny 			    "value = %s, type = %d, rv = %d\n",
24170025d76Sjohnny 			    fn, name, value, (int)type, rv);
24270025d76Sjohnny 			goto ERR;
24370025d76Sjohnny 		}
24470025d76Sjohnny 		*eqp = '=';
24570025d76Sjohnny 	}
24670025d76Sjohnny 
24770025d76Sjohnny 	*argip = i;
24870025d76Sjohnny 	return (attrlistp);
24970025d76Sjohnny 
25070025d76Sjohnny 	/*NOTREACHED*/
25170025d76Sjohnny ERR_ARG:
25270025d76Sjohnny 	if (eqp != NULL)
25370025d76Sjohnny 		*eqp = '=';
25470025d76Sjohnny 	dprint(DDEBUG, "%s: bad attribute argv[%d]: \"%s\"\n", fn, i, argv[i]);
25570025d76Sjohnny ERR:
256*aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(attrlistp);
25770025d76Sjohnny 	return (NULL);
25870025d76Sjohnny }
25970025d76Sjohnny 
26070025d76Sjohnny 
26170025d76Sjohnny static struct {
26270025d76Sjohnny 	int cmd;
26370025d76Sjohnny 	char *name;
26470025d76Sjohnny } si_cmd_nametab[] = {
26570025d76Sjohnny 	SI_PLATFORM, "SI_PLATFORM",
26670025d76Sjohnny 	SI_MACHINE, "SI_MACHINE",
26770025d76Sjohnny };
26870025d76Sjohnny static int si_cmd_nametab_len =
26970025d76Sjohnny     sizeof (si_cmd_nametab) / sizeof (si_cmd_nametab[0]);
27070025d76Sjohnny 
27170025d76Sjohnny static int
si_name2cmd(char * name)27270025d76Sjohnny si_name2cmd(char *name)
27370025d76Sjohnny {
27470025d76Sjohnny 	int i;
27570025d76Sjohnny 
27670025d76Sjohnny 	for (i = 0; i < si_cmd_nametab_len; i++) {
27770025d76Sjohnny 		if (strcmp(name, si_cmd_nametab[i].name) == 0)
27870025d76Sjohnny 			return (si_cmd_nametab[i].cmd);
27970025d76Sjohnny 	}
28070025d76Sjohnny 	return (-1);
28170025d76Sjohnny }
28270025d76Sjohnny 
28370025d76Sjohnny 
28470025d76Sjohnny /*
28570025d76Sjohnny  * finds occurences of substrings surrounded (delimited) by MACRO_BEGTOK and
28670025d76Sjohnny  * MACRO_ENDTOK in <str>;
28770025d76Sjohnny  * returns an allocated array of macro_list_t whose length is
28870025d76Sjohnny  * returned through <lenp>; array entries will be in order of the occurrence;
28970025d76Sjohnny  * else returns NULL if none are found
29070025d76Sjohnny  *
29170025d76Sjohnny  * macro_list_t members:
29270025d76Sjohnny  *	char *name = allocated string containing name without macro delimiters
29370025d76Sjohnny  *	char *beg = location in <str> at _first char_ of MACRO_BEGTOK
29470025d76Sjohnny  *	char *end = location in <str> at _last char_ of MACRO_ENDTOK
29570025d76Sjohnny  */
29670025d76Sjohnny static macro_list_t *
parse_macros(char * const str,int * lenp)29770025d76Sjohnny parse_macros(char *const str, int *lenp)
29870025d76Sjohnny {
29970025d76Sjohnny 	char *beg, *end;
30070025d76Sjohnny 	macro_list_t *lp;
30170025d76Sjohnny 	size_t size;
30270025d76Sjohnny 	int i, begtok_len, endtok_len;
30370025d76Sjohnny 
30470025d76Sjohnny 	begtok_len = strlen(MACRO_BEGTOK);
30570025d76Sjohnny 	endtok_len = strlen(MACRO_ENDTOK);
30670025d76Sjohnny 
30770025d76Sjohnny 	/* count all occurrences */
30870025d76Sjohnny 	for (beg = str, i = 0; beg != NULL; i++) {
30970025d76Sjohnny 		beg = strstr(beg, MACRO_BEGTOK);
31070025d76Sjohnny 		if (beg == NULL)
31170025d76Sjohnny 			break;
31270025d76Sjohnny 		end = strstr(beg + begtok_len, MACRO_ENDTOK);
31370025d76Sjohnny 		if (end == NULL)
31470025d76Sjohnny 			break;
31570025d76Sjohnny 		beg = end + endtok_len;
31670025d76Sjohnny 	}
31770025d76Sjohnny 	if (i <= 0)
31870025d76Sjohnny 		return (NULL);
31970025d76Sjohnny 
32070025d76Sjohnny 	*lenp = i;
32170025d76Sjohnny 	lp = pcidr_malloc(sizeof (macro_list_t) * i);
32270025d76Sjohnny 
32370025d76Sjohnny 	for (beg = str, i = 0; i < *lenp; i++) {
32470025d76Sjohnny 		beg = strstr(beg, MACRO_BEGTOK);
32570025d76Sjohnny 		assert(beg != NULL);
32670025d76Sjohnny 		end = strstr(beg + begtok_len, MACRO_ENDTOK);
32770025d76Sjohnny 		assert(end != NULL);
32870025d76Sjohnny 
32970025d76Sjohnny 		size = (end - (beg + begtok_len)) + 1;
33070025d76Sjohnny 		lp[i].name = pcidr_malloc(size * sizeof (char));
33170025d76Sjohnny 		(void) strlcpy(lp[i].name, beg + begtok_len, size);
33270025d76Sjohnny 
33370025d76Sjohnny 		lp[i].beg = beg;
33470025d76Sjohnny 		lp[i].end = (end + endtok_len) - 1;
33570025d76Sjohnny 
33670025d76Sjohnny 		beg = end + endtok_len;
33770025d76Sjohnny 	}
33870025d76Sjohnny 
33970025d76Sjohnny 	return (lp);
34070025d76Sjohnny }
34170025d76Sjohnny 
34270025d76Sjohnny static void
free_macros(macro_list_t * lp,int len)34370025d76Sjohnny free_macros(macro_list_t *lp, int len)
34470025d76Sjohnny {
34570025d76Sjohnny 	int i;
34670025d76Sjohnny 
34770025d76Sjohnny 	for (i = 0; i < len; i++)
34870025d76Sjohnny 		free(lp[i].name);
34970025d76Sjohnny 	free(lp);
35070025d76Sjohnny }
35170025d76Sjohnny 
35270025d76Sjohnny 
35370025d76Sjohnny /*
35470025d76Sjohnny  * evaluates any macros in <opath> and returns allocated string on success;
35570025d76Sjohnny  * else NULL
35670025d76Sjohnny  */
35770025d76Sjohnny static char *
parse_path(char * const opath)35870025d76Sjohnny parse_path(char *const opath)
35970025d76Sjohnny {
36070025d76Sjohnny 	char *fn = "parse_path";
36170025d76Sjohnny 	char buf[MAXPATHLEN + 1];
36270025d76Sjohnny 	int bufsize = sizeof (buf) / sizeof (buf[0]);
36370025d76Sjohnny 	char sibuf[257];
36470025d76Sjohnny 	int sibufsize = sizeof (sibuf) / sizeof (sibuf[0]);
36570025d76Sjohnny 	macro_list_t *lp;
36670025d76Sjohnny 	char *path, *pathp, *pathend;
36770025d76Sjohnny 	int rv, i, lplen, si_cmd, pathlen, okmacro, si_macro_len;
36870025d76Sjohnny 	size_t sz;
36970025d76Sjohnny 
37070025d76Sjohnny 	/*
37170025d76Sjohnny 	 * make a copy so we can modify it for easier parsing;
37270025d76Sjohnny 	 * lp members will refer to the copy
37370025d76Sjohnny 	 */
37470025d76Sjohnny 	path = strdup(opath);
37570025d76Sjohnny 	lp = parse_macros(path, &lplen);
37670025d76Sjohnny 	if (lp == NULL)
37770025d76Sjohnny 		return (path);
37870025d76Sjohnny 
37970025d76Sjohnny 	rv = 0;
38070025d76Sjohnny 	si_macro_len = strlen(SI_MACRO);
38170025d76Sjohnny 	pathlen = strlen(path);
38270025d76Sjohnny 	pathend = &path[pathlen - 1];
38370025d76Sjohnny 	pathp = path;
38470025d76Sjohnny 	buf[0] = '\0';
38570025d76Sjohnny 	for (i = 0; i < lplen; i++) {
38670025d76Sjohnny 		lp[i].beg[0] = '\0';
38770025d76Sjohnny 		sz = strlcat(buf, pathp, bufsize);
38870025d76Sjohnny 		assert(sz < bufsize);
38970025d76Sjohnny 
39070025d76Sjohnny 		okmacro = 0;
39170025d76Sjohnny 		if (strncmp(lp[i].name, SI_MACRO, si_macro_len) == 0) {
39270025d76Sjohnny 			si_cmd = si_name2cmd(lp[i].name);
39370025d76Sjohnny 			assert(si_cmd >= 0);
39470025d76Sjohnny 
39570025d76Sjohnny 			rv = sysinfo(si_cmd, sibuf, sibufsize);
39670025d76Sjohnny 			if (rv < 0) {
39770025d76Sjohnny 				dprint(DDEBUG, "%s: sysinfo cmd %d failed: "
39870025d76Sjohnny 				    "errno = %d\n", fn, si_cmd, errno);
39970025d76Sjohnny 				goto OUT;
40070025d76Sjohnny 			}
40170025d76Sjohnny 
40270025d76Sjohnny 			sz = strlcat(buf, sibuf, bufsize);
40370025d76Sjohnny 			assert(sz < bufsize);
40470025d76Sjohnny 			okmacro = 1;
40570025d76Sjohnny 		}
40670025d76Sjohnny 		/* check for unrecognized macros */
40770025d76Sjohnny 		assert(okmacro);
40870025d76Sjohnny 		pathp = lp[i].end + 1;
40970025d76Sjohnny 	}
41070025d76Sjohnny 
41170025d76Sjohnny 	rv = 0;
41270025d76Sjohnny 	if (pathp < pathend) {
41370025d76Sjohnny 		sz = strlcat(buf, pathp, bufsize);
41470025d76Sjohnny 		assert(sz < bufsize);
41570025d76Sjohnny 	}
41670025d76Sjohnny OUT:
41770025d76Sjohnny 	free_macros(lp, lplen);
41870025d76Sjohnny 	free(path);
41970025d76Sjohnny 	if (rv == 0)
42070025d76Sjohnny 		return (strdup(buf));
42170025d76Sjohnny 	return (NULL);
42270025d76Sjohnny }
42370025d76Sjohnny 
42470025d76Sjohnny 
42570025d76Sjohnny /*
42670025d76Sjohnny  * returns allocated string containing plugin path which caller must free;
42770025d76Sjohnny  * else NULL;  <attrlistp> is for future use if attributes can be used to
42870025d76Sjohnny  * determin plugin
42970025d76Sjohnny  */
43070025d76Sjohnny /*ARGSUSED*/
43170025d76Sjohnny static char *
find_plugin(nvlist_t * attrlistp)43270025d76Sjohnny find_plugin(nvlist_t *attrlistp)
43370025d76Sjohnny {
43470025d76Sjohnny 	char *fn = "find_plugin";
43570025d76Sjohnny 	char *path = NULL;
43670025d76Sjohnny 	int i, rv;
43770025d76Sjohnny 	struct stat statbuf;
43870025d76Sjohnny 
43970025d76Sjohnny 	for (i = 0; i < plugin_paths_len; i++) {
44070025d76Sjohnny 		path = parse_path(plugin_paths[i]);
44170025d76Sjohnny 		if (path == NULL) {
44270025d76Sjohnny 			dprint(DDEBUG, "%s: error parsing path %s\n", fn,
44370025d76Sjohnny 			    path);
44470025d76Sjohnny 			return (NULL);
44570025d76Sjohnny 		}
44670025d76Sjohnny 
44770025d76Sjohnny 		rv = stat(path, &statbuf);
44870025d76Sjohnny 		if (rv < 0)
44970025d76Sjohnny 			dprint(DDEBUG, "%s: stat on %s failed: "
45070025d76Sjohnny 			    "errno = %d\n", fn, path, errno);
45170025d76Sjohnny 		else if ((statbuf.st_mode & S_IFMT) != S_IFREG)
45270025d76Sjohnny 			dprint(DDEBUG, "%s: %s is not a regular "
45370025d76Sjohnny 			    "file\n", fn, path);
45470025d76Sjohnny 		else
45570025d76Sjohnny 			return (path);
45670025d76Sjohnny 
45770025d76Sjohnny 		free(path);
45870025d76Sjohnny 	}
45970025d76Sjohnny 	return (NULL);
46070025d76Sjohnny }
46170025d76Sjohnny 
46270025d76Sjohnny 
46370025d76Sjohnny /*
46470025d76Sjohnny  * load plugin specified by <path> and pass the proceeding arguments
46570025d76Sjohnny  * to the plugin interface;  returns 0 on success (likewise for
46670025d76Sjohnny  * the plugin function)
46770025d76Sjohnny  */
46870025d76Sjohnny static int
do_plugin(char * path,nvlist_t * attrlistp,pcidr_opt_t * optp)46970025d76Sjohnny do_plugin(char *path, nvlist_t *attrlistp, pcidr_opt_t *optp)
47070025d76Sjohnny {
47170025d76Sjohnny 	char *fn = "do_plugin";
47270025d76Sjohnny 	int rv;
47370025d76Sjohnny 	void *dlh;
47470025d76Sjohnny 	sigset_t set, oset;
47570025d76Sjohnny 	pcidr_plugin_t fp;
47670025d76Sjohnny 
47770025d76Sjohnny 	dlh = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
47870025d76Sjohnny 	if (dlh == NULL) {
47970025d76Sjohnny 		dprint(DDEBUG, "%s: dlopen() failed: %s\n", fn, dlerror());
48070025d76Sjohnny 		rv = EINVAL;
48170025d76Sjohnny 		goto OUT;
48270025d76Sjohnny 	}
48370025d76Sjohnny 
48470025d76Sjohnny 	if (sigfillset(&set) != 0) {
48570025d76Sjohnny 		dprint(DDEBUG, "%s: sigfillset() failed: errno = %d\n", fn,
48670025d76Sjohnny 		    errno);
48770025d76Sjohnny 		rv = errno;
48870025d76Sjohnny 		goto OUT;
48970025d76Sjohnny 	}
49070025d76Sjohnny 	if (sigprocmask(SIG_BLOCK, &set, &oset) != 0) {
49170025d76Sjohnny 		dprint(DDEBUG, "%s: blocking signals with sigprocmask() "
49270025d76Sjohnny 		    "failed: errno = %d\n", fn, errno);
49370025d76Sjohnny 		rv = errno;
49470025d76Sjohnny 		goto OUT;
49570025d76Sjohnny 	}
49670025d76Sjohnny 
49770025d76Sjohnny 	fp = (pcidr_plugin_t)dlsym(dlh, PCIDR_PLUGIN_SYMSTR);
49870025d76Sjohnny 	if (fp == NULL)  {
49970025d76Sjohnny 		dprint(DDEBUG, "%s: dlsym() failed: %s\n", fn, dlerror());
50070025d76Sjohnny 		rv = EINVAL;
50170025d76Sjohnny 		goto OUT;
50270025d76Sjohnny 	}
50370025d76Sjohnny 	rv = fp(attrlistp, optp);
50470025d76Sjohnny 	if (rv != 0)
50570025d76Sjohnny 		dprint(DDEBUG, "%s: %s() failed: rv = %d\n", fn,
50670025d76Sjohnny 		    PCIDR_PLUGIN_SYMSTR, rv);
50770025d76Sjohnny 
50870025d76Sjohnny 	if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) {
50970025d76Sjohnny 		dprint(DDEBUG, "%s: unblocking signals with sigprocmask() "
51070025d76Sjohnny 		    "failed: errno = %d\n", fn, errno);
51170025d76Sjohnny 		rv = errno;
51270025d76Sjohnny 		goto OUT;
51370025d76Sjohnny 	}
51470025d76Sjohnny OUT:
51570025d76Sjohnny 	if (dlh != NULL)
51670025d76Sjohnny 		(void) dlclose(dlh);
51770025d76Sjohnny 	return (rv);
51870025d76Sjohnny }
51970025d76Sjohnny 
52070025d76Sjohnny 
52170025d76Sjohnny static void
exiter()52270025d76Sjohnny exiter()
52370025d76Sjohnny {
52470025d76Sjohnny 	extern FILE *dfp;
52570025d76Sjohnny 
52670025d76Sjohnny 	if (nvlistp != NULL)
52770025d76Sjohnny 		nvlist_free(nvlistp);
52870025d76Sjohnny 	if (dfp != NULL)
52970025d76Sjohnny 		(void) fclose(dfp);
53070025d76Sjohnny #ifdef DEBUG
53170025d76Sjohnny 	closelog();
53270025d76Sjohnny #endif
53370025d76Sjohnny }
53470025d76Sjohnny 
53570025d76Sjohnny 
53670025d76Sjohnny int
main(int argc,char ** argv)53770025d76Sjohnny main(int argc, char **argv)
53870025d76Sjohnny {
53970025d76Sjohnny 	int rv, argi;
54070025d76Sjohnny 	char *dfile = NULL, *plugin_path = NULL;
54170025d76Sjohnny 	struct stat statbuf;
54270025d76Sjohnny 	pcidr_opt_t plugin_opt;
54370025d76Sjohnny 	char *optstr = NULL;
54470025d76Sjohnny 
54570025d76Sjohnny 	extern char *optarg;
54670025d76Sjohnny 	extern int optind, optopt;
54770025d76Sjohnny 	int c;
54870025d76Sjohnny 
54970025d76Sjohnny 	/*CONSTCOND*/
55070025d76Sjohnny 	assert(MIN_DLVL == 0);
55170025d76Sjohnny 	/*CONSTCOND*/
55270025d76Sjohnny 	assert(MIN_DLVL == DNONE);
55370025d76Sjohnny 	assert(MAX_DLVL == dpritab_len - 1);
55470025d76Sjohnny 
55570025d76Sjohnny 	(void) atexit(exiter);
55670025d76Sjohnny 	prg = argv[0];
55770025d76Sjohnny 	dfp = NULL;
55870025d76Sjohnny 
55970025d76Sjohnny #ifdef DEBUG
56070025d76Sjohnny 	openlog(prg, LOG_PID | LOG_CONS, LOG_DAEMON);
56170025d76Sjohnny 	dlvl = DWARN;
56270025d76Sjohnny 	dsys = 1;
56370025d76Sjohnny 	optstr = "hsv:l:";
56470025d76Sjohnny #else
56570025d76Sjohnny 	dlvl = DNONE;
56670025d76Sjohnny 	dsys = 0;
56770025d76Sjohnny 	optstr = "sv:l:";
56870025d76Sjohnny #endif
56970025d76Sjohnny 
57070025d76Sjohnny 	while ((c = getopt(argc, argv, optstr)) != -1) {
57170025d76Sjohnny 		switch (c) {
57270025d76Sjohnny 		case 'h':
57370025d76Sjohnny 			help();
57470025d76Sjohnny 			exit(0);
57570025d76Sjohnny 			break;
57670025d76Sjohnny 		case 's':
57770025d76Sjohnny 			dsys = 0;
57870025d76Sjohnny 			break;
57970025d76Sjohnny 		case 'v':
58070025d76Sjohnny 			dlvl = atoi(optarg);
58170025d76Sjohnny 			break;
58270025d76Sjohnny 		case 'l':
58370025d76Sjohnny 			dfile = optarg;
58470025d76Sjohnny 			break;
58570025d76Sjohnny 		default:
58670025d76Sjohnny 			dprint(DWARN, "bad option: %c\n", optopt);
58770025d76Sjohnny 			return (EINVAL);
58870025d76Sjohnny 		}
58970025d76Sjohnny 	}
59070025d76Sjohnny 
59170025d76Sjohnny 	/*
59270025d76Sjohnny 	 * [ -l ] do file option first so we can still get msgs if -s is used
59370025d76Sjohnny 	 */
59470025d76Sjohnny 	if (dfile != NULL) {
59570025d76Sjohnny 		if (strcmp(dfile, "-") == 0) {
59670025d76Sjohnny 			/* ignore if stdout is not open/valid */
59770025d76Sjohnny 			dfp = NULL;
59870025d76Sjohnny 			if (stdout != NULL &&
59970025d76Sjohnny 			    fstat(fileno(stdout), &statbuf) == 0)
60070025d76Sjohnny 				dfp = stdout;
60170025d76Sjohnny 		} else {
60270025d76Sjohnny 			dfp = fopen(dfile, "a");
60370025d76Sjohnny 			if (dfp == NULL) {
60470025d76Sjohnny 				dprint(DWARN, "cannot open %s: %s\n",
60570025d76Sjohnny 				    dfile, strerror(errno));
60670025d76Sjohnny 				return (EINVAL);
60770025d76Sjohnny 			}
60870025d76Sjohnny 		}
60970025d76Sjohnny 	}
61070025d76Sjohnny 
61170025d76Sjohnny 	/* [ -v ] */
61270025d76Sjohnny 	if (dlvl < MIN_DLVL || dlvl > MAX_DLVL) {
61370025d76Sjohnny 		dprint(DWARN, "bad arg for -v: %d\n", dlvl);
61470025d76Sjohnny 		return (EINVAL);
61570025d76Sjohnny 	}
61670025d76Sjohnny 
61770025d76Sjohnny 	argi = optind;
61870025d76Sjohnny 	if (argi >= argc) {
61970025d76Sjohnny 		dprint(DWARN, "missing attribute arguments\n");
62070025d76Sjohnny 		return (EINVAL);
62170025d76Sjohnny 	}
62270025d76Sjohnny 
62370025d76Sjohnny 	nvlistp = parse_argv_attr(argc, argv, &argi);
62470025d76Sjohnny 	if (nvlistp == NULL) {
62570025d76Sjohnny 		dprint(DWARN, "attribute parsing error\n");
62670025d76Sjohnny 		return (EINVAL);
62770025d76Sjohnny 	}
62870025d76Sjohnny 
62970025d76Sjohnny 	(void) memset(&plugin_opt, 0, sizeof (plugin_opt));
63070025d76Sjohnny 	plugin_opt.logopt.dlvl = dlvl;
63170025d76Sjohnny 	plugin_opt.logopt.prg = prg;
632