/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <errno.h>
#include <sys/systeminfo.h>
#include <syslog.h>
#include <libnvpair.h>
#include <stdarg.h>
#include <assert.h>
#include <dlfcn.h>
#include <signal.h>
#include <pcidr.h>
/*
* pcidr takes in arguments of the form specified in the help() routine
* including a set of name=value pairs, then looks up a plugin (shared object)
* based on <plugin_paths> and however find_plugin() operates. The entry
* point of the plugin is <PCIDR_PLUGIN_SYM> and has the type
* <pcidr_plugin_t>. Plugins must use the <PCIDR_PLUGIN_PROTO> macro to
* define their entry point.
*
* The name=value arguments are intended to be used as a mechanism to pass
* arbitrary sysevent attributes using the macro expansion capability provided
* by the syseventd SLM processing sysevent.conf files (i.e. specifying
* "$attribute" arguments for the handler in a .conf file entry). They are
* converted into an nvlist_t (see libnvpair(3LIB)) by converting the values
* of recognized names into appropriate types using pcidr_name2type() and
* leaving all others as string types. Because pcidr is used as a sysevent.conf
* handler, the format of the value string for non-string attributes in each
* name=value argument must match that used by the syseventd macro capability
*
* The plugin will be passed this (nvlist_t *) along with a (pcidr_opt_t *) arg
* for other options. While pcidr does some basic checking of arguments, it
* leaves any name=value check (after conversion) up to each plugin. Note
* that pcidr_check_attrs() is used by the default plugin and can be used by
* any plugin that support the same or a superset of its attributes. If the
* default plugin supports additional publishers, it should be updated in
* pcidr_check_attrs().
*
* See help() for an example of how pcidr can be specified in a sysevent.conf
* file.
*/
/*
* plugin search paths (searched in order specified);
* macros begin MACRO_BEGTOK and end with MACRO_ENDTOK;
*
* be sure to update parse_path() and its support functions whenever macros
* are updated e.g. si_name2cmd(), as well as substring tokens (prefix or
* suffix) used to recognize different types of macros e.g. SI_MACRO
*
* NOTE: if plugin search algorithm is changed starting with find_plugin(),
* please update documentation here.
*
* macros:
* SI_PLATFORM = cmd of same name in sysinfo(2)
* SI_MACHINE = cmd of same name in sysinfo(2)
*/
static char *plugin_paths[] = {
"/usr/platform/${SI_PLATFORM}/lib/pci/" PCIDR_PLUGIN_NAME,
"/usr/platform/${SI_MACHINE}/lib/pci/" PCIDR_PLUGIN_NAME,
"/usr/lib/pci/" PCIDR_PLUGIN_NAME,
};
typedef struct {
char *name;
char *beg;
char *end;
} macro_list_t;
static macro_list_t *parse_macros(char *const, int *);
static void free_macros(macro_list_t *, int);
static char *parse_path(char *const);
static void help();
static void exiter();
static char *find_plugin(nvlist_t *);
static nvlist_t *parse_argv_attr(int, char **, int *);
static int si_name2cmd(char *);
static void
help()
{
/* since the handler is not public, we don't expose its usage normally */
#ifdef DEBUG
(void) printf(
"%s [-h] [-s] [-v <level>] [-l <log_file>] <attributes>\n"
" -h help\n"
"\n"
" -s turn OFF messages to the syslog (use syslog by default)\n"
"\n"
" -v verbose mode; <level> range is %d..%d; default is %d\n"
"\n"
" -l also log messages to <log_file> (in addition to using\n"
" the syslog if that option is not disabled);\n"
" if <log_file> is '-', stdout is used\n"
"\n"
" <attributes>\n"
" whitespace seperated strings of <name>=<value> pairs\n"
"\n"
"Example 1 (command line):\n"
" %s -s -v%d -l- \\\n"
" class=EC_dr subclass=ESC_dr_req publisher=pcie_pci \\\n"
" dr_request_type=dr_request_outgoing_resource \\\n"
"\n"
"Example 2 (/etc/sysevent/config/SUNW,sysevent.conf entry):\n"
" class=$class subclass=$subclass publisher=$publisher \\\n"
" dr_request_type=$dr_request_type\\\n"
" dr_ap_id=$dr_ap_id\n"
"\n",
#endif
}
/*
* will convert <value> from a string to the type indicated by <type>
* and will add it with <name> to nvlist_t <listp>; function returns the same
* value as nvlist_add_*()
*/
static int
{
int rv = 0;
switch (type) {
case DATA_TYPE_STRING:
if (rv != 0) {
"name = %s, value = %s, rv = %d\n",
}
break;
/*
* Conversion must support whatever string format syseventd uses for
* its .conf macros; in addition, minimum types supported must match
* those for pcidr_name2type()
*/
default:
}
return (rv);
}
/*
* argc: length of argv
* argv: each string starting from index <argip> has the format "name=value"
* argip: starting index in <argv>; also used to return ending index
*
* return: allocated nvlist on success, exits otherwise
*
* recognized names will have predetermined types, while all others will have
* values of type string
*/
static nvlist_t *
{
int rv, i;
if (rv != 0) {
goto ERR;
}
goto ERR_ARG;
*eqp = '\0';
value++;
goto ERR_ARG;
if (rv != 0) {
"value = %s, type = %d, rv = %d\n",
goto ERR;
}
*eqp = '=';
}
*argip = i;
return (attrlistp);
/*NOTREACHED*/
*eqp = '=';
ERR:
return (NULL);
}
static struct {
int cmd;
char *name;
} si_cmd_nametab[] = {
SI_PLATFORM, "SI_PLATFORM",
SI_MACHINE, "SI_MACHINE",
};
static int si_cmd_nametab_len =
sizeof (si_cmd_nametab) / sizeof (si_cmd_nametab[0]);
static int
{
int i;
for (i = 0; i < si_cmd_nametab_len; i++) {
return (si_cmd_nametab[i].cmd);
}
return (-1);
}
/*
* finds occurences of substrings surrounded (delimited) by MACRO_BEGTOK and
* MACRO_ENDTOK in <str>;
* returns an allocated array of macro_list_t whose length is
* returned through <lenp>; array entries will be in order of the occurrence;
* else returns NULL if none are found
*
* macro_list_t members:
* char *name = allocated string containing name without macro delimiters
* char *beg = location in <str> at _first char_ of MACRO_BEGTOK
* char *end = location in <str> at _last char_ of MACRO_ENDTOK
*/
static macro_list_t *
{
/* count all occurrences */
break;
break;
}
if (i <= 0)
return (NULL);
*lenp = i;
}
return (lp);
}
static void
{
int i;
for (i = 0; i < len; i++)
}
/*
* evaluates any macros in <opath> and returns allocated string on success;
* else NULL
*/
static char *
{
/*
* make a copy so we can modify it for easier parsing;
* lp members will refer to the copy
*/
return (path);
rv = 0;
buf[0] = '\0';
for (i = 0; i < lplen; i++) {
okmacro = 0;
if (rv < 0) {
goto OUT;
}
okmacro = 1;
}
/* check for unrecognized macros */
}
rv = 0;
}
OUT:
if (rv == 0)
return (NULL);
}
/*
* returns allocated string containing plugin path which caller must free;
* else NULL; <attrlistp> is for future use if attributes can be used to
* determin plugin
*/
/*ARGSUSED*/
static char *
{
int i, rv;
for (i = 0; i < plugin_paths_len; i++) {
path);
return (NULL);
}
if (rv < 0)
else
return (path);
}
return (NULL);
}
/*
* load plugin specified by <path> and pass the proceeding arguments
* to the plugin interface; returns 0 on success (likewise for
* the plugin function)
*/
static int
{
int rv;
void *dlh;
goto OUT;
}
if (sigfillset(&set) != 0) {
errno);
goto OUT;
}
goto OUT;
}
goto OUT;
}
if (rv != 0)
goto OUT;
}
OUT:
return (rv);
}
static void
exiter()
{
#ifdef DEBUG
closelog();
#endif
}
int
{
extern char *optarg;
int c;
/*CONSTCOND*/
/*CONSTCOND*/
#ifdef DEBUG
dsys = 1;
optstr = "hsv:l:";
#else
dsys = 0;
optstr = "sv:l:";
#endif
switch (c) {
case 'h':
help();
exit(0);
break;
case 's':
dsys = 0;
break;
case 'v':
break;
case 'l':
break;
default:
return (EINVAL);
}
}
/*
* [ -l ] do file option first so we can still get msgs if -s is used
*/
} else {
return (EINVAL);
}
}
}
/* [ -v ] */
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
if (plugin_path == NULL) {
return (EINVAL);
}
if (rv != 0) {
}
if (plugin_path != NULL)
return (rv);
}