#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <errno.h>
#include <sys/param.h>
#include <sys/systeminfo.h>
#include <sys/sysevent/eventdefs.h>
#include <sys/sysevent/dr.h>
#include <syslog.h>
#include <libnvpair.h>
#include <stdarg.h>
#include <assert.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include <signal.h>
#include <pcidr.h>
#define MACRO_BEGTOK "${"
#define MACRO_ENDTOK "}"
#define SI_MACRO "SI_"
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,
};
static int plugin_paths_len = sizeof (plugin_paths) / sizeof (plugin_paths[0]);
static nvlist_t *nvlistp = NULL;
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 int do_plugin(char *, nvlist_t *, pcidr_opt_t *);
static int nvadd(nvlist_t *, char *, char *, data_type_t);
static nvlist_t *parse_argv_attr(int, char **, int *);
static int si_name2cmd(char *);
static void
help()
{
#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"
" dr_ap_id=/devices/foo/bar\n"
"\n"
"Example 2 (/etc/sysevent/config/SUNW,sysevent.conf entry):\n"
" EC_dr ESC_dr_req SUNW pcie_pci - - - %s -v%d -l/tmp/log \\\n"
" class=$class subclass=$subclass publisher=$publisher \\\n"
" dr_request_type=$dr_request_type\\\n"
" dr_ap_id=$dr_ap_id\n"
"\n",
prg, MIN_DLVL, MAX_DLVL, dlvl,
prg, MAX_DLVL,
prg, DWARN);
#endif
}
static int
nvadd(nvlist_t *listp, char *name, char *value, data_type_t type)
{
char *fn = "nvadd";
int rv = 0;
switch (type) {
case DATA_TYPE_STRING:
rv = nvlist_add_string(listp, name, value);
if (rv != 0) {
dprint(DDEBUG, "%s: nvlist_add_string() failed: "
"name = %s, value = %s, rv = %d\n",
fn, name, value, rv);
}
break;
default:
dprint(DDEBUG, "%s: unsupported type: name = %s, value = %s, "
"type = 0x%x\n", fn, name, value, (int)type);
rv = EINVAL;
}
return (rv);
}
static nvlist_t *
parse_argv_attr(int argc, char **argv, int *argip)
{
char *fn = "parse_argv_attr";
int rv, i;
nvlist_t *attrlistp = NULL;
char *eqp, *name, *value;
data_type_t type;
assert(*argip < argc);
rv = nvlist_alloc(&attrlistp, NV_UNIQUE_NAME_TYPE, 0);
if (rv != 0) {
dprint(DDEBUG, "%s: nvlist_alloc() failed: rv = %d\n", fn, rv);
goto ERR;
}
for (i = *argip; i < argc; i++) {
eqp = strchr(argv[i], '=');
if (eqp == NULL)
goto ERR_ARG;
*eqp = '\0';
name = argv[i];
value = eqp;
value++;
if (*name == '\0' || *value == '\0')
goto ERR_ARG;
if (pcidr_name2type(name, &type) != 0)
type = DATA_TYPE_STRING;
rv = nvadd(attrlistp, name, value, type);
if (rv != 0) {
dprint(DDEBUG, "%s: nvadd() failed: attribute \"%s\", "
"value = %s, type = %d, rv = %d\n",
fn, name, value, (int)type, rv);
goto ERR;
}
*eqp = '=';
}
*argip = i;
return (attrlistp);
ERR_ARG:
if (eqp != NULL)
*eqp = '=';
dprint(DDEBUG, "%s: bad attribute argv[%d]: \"%s\"\n", fn, i, argv[i]);
ERR:
nvlist_free(attrlistp);
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
si_name2cmd(char *name)
{
int i;
for (i = 0; i < si_cmd_nametab_len; i++) {
if (strcmp(name, si_cmd_nametab[i].name) == 0)
return (si_cmd_nametab[i].cmd);
}
return (-1);
}
static macro_list_t *
parse_macros(char *const str, int *lenp)
{
char *beg, *end;
macro_list_t *lp;
size_t size;
int i, begtok_len, endtok_len;
begtok_len = strlen(MACRO_BEGTOK);
endtok_len = strlen(MACRO_ENDTOK);
for (beg = str, i = 0; beg != NULL; i++) {
beg = strstr(beg, MACRO_BEGTOK);
if (beg == NULL)
break;
end = strstr(beg + begtok_len, MACRO_ENDTOK);
if (end == NULL)
break;
beg = end + endtok_len;
}
if (i <= 0)
return (NULL);
*lenp = i;
lp = pcidr_malloc(sizeof (macro_list_t) * i);
for (beg = str, i = 0; i < *lenp; i++) {
beg = strstr(beg, MACRO_BEGTOK);
assert(beg != NULL);
end = strstr(beg + begtok_len, MACRO_ENDTOK);
assert(end != NULL);
size = (end - (beg + begtok_len)) + 1;
lp[i].name = pcidr_malloc(size * sizeof (char));
(void) strlcpy(lp[i].name, beg + begtok_len, size);
lp[i].beg = beg;
lp[i].end = (end + endtok_len) - 1;
beg = end + endtok_len;
}
return (lp);
}
static void
free_macros(macro_list_t *lp, int len)
{
int i;
for (i = 0; i < len; i++)
free(lp[i].name);
free(lp);
}
static char *
parse_path(char *const opath)
{
char *fn = "parse_path";
char buf[MAXPATHLEN + 1];
int bufsize = sizeof (buf) / sizeof (buf[0]);
char sibuf[257];
int sibufsize = sizeof (sibuf) / sizeof (sibuf[0]);
macro_list_t *lp;
char *path, *pathp, *pathend;
int rv, i, lplen, si_cmd, pathlen, okmacro, si_macro_len;
size_t sz;
path = strdup(opath);
lp = parse_macros(path, &lplen);
if (lp == NULL)
return (path);
rv = 0;
si_macro_len = strlen(SI_MACRO);
pathlen = strlen(path);
pathend = &path[pathlen - 1];
pathp = path;
buf[0] = '\0';
for (i = 0; i < lplen; i++) {
lp[i].beg[0] = '\0';
sz = strlcat(buf, pathp, bufsize);
assert(sz < bufsize);
okmacro = 0;
if (strncmp(lp[i].name, SI_MACRO, si_macro_len) == 0) {
si_cmd = si_name2cmd(lp[i].name);
assert(si_cmd >= 0);
rv = sysinfo(si_cmd, sibuf, sibufsize);
if (rv < 0) {
dprint(DDEBUG, "%s: sysinfo cmd %d failed: "
"errno = %d\n", fn, si_cmd, errno);
goto OUT;
}
sz = strlcat(buf, sibuf, bufsize);
assert(sz < bufsize);
okmacro = 1;
}
assert(okmacro);
pathp = lp[i].end + 1;
}
rv = 0;
if (pathp < pathend) {
sz = strlcat(buf, pathp, bufsize);
assert(sz < bufsize);
}
OUT:
free_macros(lp, lplen);
free(path);
if (rv == 0)
return (strdup(buf));
return (NULL);
}
static char *
find_plugin(nvlist_t *attrlistp)
{
char *fn = "find_plugin";
char *path = NULL;
int i, rv;
struct stat statbuf;
for (i = 0; i < plugin_paths_len; i++) {
path = parse_path(plugin_paths[i]);
if (path == NULL) {
dprint(DDEBUG, "%s: error parsing path %s\n", fn,
path);
return (NULL);
}
rv = stat(path, &statbuf);
if (rv < 0)
dprint(DDEBUG, "%s: stat on %s failed: "
"errno = %d\n", fn, path, errno);
else if ((statbuf.st_mode & S_IFMT) != S_IFREG)
dprint(DDEBUG, "%s: %s is not a regular "
"file\n", fn, path);
else
return (path);
free(path);
}
return (NULL);
}
static int
do_plugin(char *path, nvlist_t *attrlistp, pcidr_opt_t *optp)
{
char *fn = "do_plugin";
int rv;
void *dlh;
sigset_t set, oset;
pcidr_plugin_t fp;
dlh = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
if (dlh == NULL) {
dprint(DDEBUG, "%s: dlopen() failed: %s\n", fn, dlerror());
rv = EINVAL;
goto OUT;
}
if (sigfillset(&set) != 0) {
dprint(DDEBUG, "%s: sigfillset() failed: errno = %d\n", fn,
errno);
rv = errno;
goto OUT;
}
if (sigprocmask(SIG_BLOCK, &set, &oset) != 0) {
dprint(DDEBUG, "%s: blocking signals with sigprocmask() "
"failed: errno = %d\n", fn, errno);
rv = errno;
goto OUT;
}
fp = (pcidr_plugin_t)dlsym(dlh, PCIDR_PLUGIN_SYMSTR);
if (fp == NULL) {
dprint(DDEBUG, "%s: dlsym() failed: %s\n", fn, dlerror());
rv = EINVAL;
goto OUT;
}
rv = fp(attrlistp, optp);
if (rv != 0)
dprint(DDEBUG, "%s: %s() failed: rv = %d\n", fn,
PCIDR_PLUGIN_SYMSTR, rv);
if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) {
dprint(DDEBUG, "%s: unblocking signals with sigprocmask() "
"failed: errno = %d\n", fn, errno);
rv = errno;
goto OUT;
}
OUT:
if (dlh != NULL)
(void) dlclose(dlh);
return (rv);
}
static void
exiter()
{
extern FILE *dfp;
if (nvlistp != NULL)
nvlist_free(nvlistp);
if (dfp != NULL)
(void) fclose(dfp);
#ifdef DEBUG
closelog();
#endif
}
int
main(int argc, char **argv)
{
int rv, argi;
char *dfile = NULL, *plugin_path = NULL;
struct stat statbuf;
pcidr_opt_t plugin_opt;
char *optstr = NULL;
extern char *optarg;
extern int optind, optopt;
int c;
assert(MIN_DLVL == 0);
assert(MIN_DLVL == DNONE);
assert(MAX_DLVL == dpritab_len - 1);
(void) atexit(exiter);
prg = argv[0];
dfp = NULL;
#ifdef DEBUG
openlog(prg, LOG_PID | LOG_CONS, LOG_DAEMON);
dlvl = DWARN;
dsys = 1;
optstr = "hsv:l:";
#else
dlvl = DNONE;
dsys = 0;
optstr = "sv:l:";
#endif
while ((c = getopt(argc, argv, optstr)) != -1) {
switch (c) {
case 'h':
help();
exit(0);
break;
case 's':
dsys = 0;
break;
case 'v':
dlvl = atoi(optarg);
break;
case 'l':
dfile = optarg;
break;
default:
dprint(DWARN, "bad option: %c\n", optopt);
return (EINVAL);
}
}
if (dfile != NULL) {
if (strcmp(dfile, "-") == 0) {
dfp = NULL;
if (fstat(fileno(stdout), &statbuf) == 0)
dfp = stdout;
} else {
dfp = fopen(dfile, "a");
if (dfp == NULL) {
dprint(DWARN, "cannot open %s: %s\n",
dfile, strerror(errno));
return (EINVAL);
}
}
}
if (dlvl < MIN_DLVL || dlvl > MAX_DLVL) {
dprint(DWARN, "bad arg for -v: %d\n", dlvl);
return (EINVAL);
}
argi = optind;
if (argi >= argc) {
dprint(DWARN, "missing attribute arguments\n");
return (EINVAL);
}
nvlistp = parse_argv_attr(argc, argv, &argi);
if (nvlistp == NULL) {
dprint(DWARN, "attribute parsing error\n");
return (EINVAL);
}
(void) memset(&plugin_opt, 0, sizeof (plugin_opt));
plugin_opt.logopt.dlvl = dlvl;
plugin_opt.logopt.prg = prg;
plugin_opt.logopt.dfp = dfp;
plugin_opt.logopt.dsys = dsys;
dprint(DINFO, "=== sysevent attributes ========================\n");
pcidr_print_attrlist(DINFO, nvlistp, NULL);
dprint(DINFO, "================================================\n");
plugin_path = find_plugin(nvlistp);
if (plugin_path == NULL) {
dprint(DWARN, "cannot find plugin\n");
return (EINVAL);
}
dprint(DINFO, "using plugin: %s\n\n", plugin_path);
rv = do_plugin(plugin_path, nvlistp, &plugin_opt);
if (rv != 0) {
dprint(DWARN, "plugin %s failed\n", plugin_path);
}
if (plugin_path != NULL)
free(plugin_path);
return (rv);
}