diff options
author | Gavin Maltby <gavin.maltby@oracle.com> | 2010-07-30 17:04:17 +1000 |
---|---|---|
committer | Gavin Maltby <gavin.maltby@oracle.com> | 2010-07-30 17:04:17 +1000 |
commit | f6e214c7418f43af38bd8c3a557e3d0a1d311cfa (patch) | |
tree | 0f0e4cee5ead68ee30660107f9eccf7cd9e72c2e /usr/src/lib/fm/libfmevent/common/fmev_publish.c | |
parent | 265a964d7aa43c47170d21d2f01bcf873d7fd79d (diff) | |
download | illumos-gate-f6e214c7418f43af38bd8c3a557e3d0a1d311cfa.tar.gz |
PSARC/2009/617 Software Events Notification Parameters CLI
PSARC/2009/618 snmp-notify: SNMP Notification Daemon for Software Events
PSARC/2009/619 smtp-notify: Email Notification Daemon for Software Events
PSARC/2010/225 fmd for non-global Solaris zones
PSARC/2010/226 Solaris Instance UUID
PSARC/2010/227 nvlist_nvflag(3NVPAIR)
PSARC/2010/228 libfmevent additions
PSARC/2010/257 sysevent_evc_setpropnvl and sysevent_evc_getpropnvl
PSARC/2010/265 FMRI and FMA Event Stabilty, 'ireport' category 1 event class, and the 'sw' FMRI scheme
PSARC/2010/278 FMA/SMF integration: instance state transitions
PSARC/2010/279 Modelling panics within FMA
PSARC/2010/290 logadm.conf upgrade
6392476 fmdump needs to pretty-print
6393375 userland ereport/ireport event generation interfaces
6445732 Add email notification agent for FMA and software events
6804168 RFE: Allow an efficient means to monitor SMF services status changes
6866661 scf_values_destroy(3SCF) will segfault if is passed NULL
6884709 Add snmp notification agent for FMA and software events
6884712 Add private interface to tap into libfmd_msg macro expansion capabilities
6897919 fmd to run in a non-global zone
6897937 fmd use of non-private doors is not safe
6900081 add a UUID to Solaris kernel image for use in crashdump identification
6914884 model panic events as a defect diagnosis in FMA
6944862 fmd_case_open_uuid, fmd_case_uuisresolved, fmd_nvl_create_defect
6944866 log legacy sysevents in fmd
6944867 enumerate svc scheme in topo
6944868 software-diagnosis and software-response fmd modules
6944870 model SMF maintenance state as a defect diagnosis in FMA
6944876 savecore runs in foreground for systems with zfs root and dedicated dump
6965796 Implement notification parameters for SMF state transitions and FMA events
6968287 SUN-FM-MIB.mib needs to be updated to reflect Oracle information
6972331 logadm.conf upgrade PSARC/2010/290
Diffstat (limited to 'usr/src/lib/fm/libfmevent/common/fmev_publish.c')
-rw-r--r-- | usr/src/lib/fm/libfmevent/common/fmev_publish.c | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/usr/src/lib/fm/libfmevent/common/fmev_publish.c b/usr/src/lib/fm/libfmevent/common/fmev_publish.c new file mode 100644 index 0000000000..f3dc01ed11 --- /dev/null +++ b/usr/src/lib/fm/libfmevent/common/fmev_publish.c @@ -0,0 +1,536 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 + * or http://www.opensolaris.org/os/licensing. + * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +/* + * Simple-minded raw event publication from user context. See extensive + * comments in libfmevent.h. These interfaces remain Project Private - + * they have to evolve before rollout to Public levels. + * + * Events are dispatched synchronously using the GPEC sysevent mechanism. + * The caller context must therefore be one in which a sysevent_evc_publish + * (and possibly sysevent_evc_bind if not already bound) is safe. We will + * also allocate and manipulate nvlists. + * + * Since we use GPEC, which has no least privilege awareness, these interfaces + * will only work for would-be producers running as root. + * + * There is no event rate throttling applied, so we rely on producers + * to throttle themselves. A future refinement should apply mandatory + * but tuneable throttling on a per-producer basis. In this first version + * the only throttle is the publication event queue depth - we'll drop + * events when the queue is full. + * + * We can publish over four channels, for privileged/non-privileged and + * high/low priority. Since only privileged producers will work now + * (see above) we hardcode priv == B_TRUE and so only two channels are + * actually used, separating higher and lower value streams from privileged + * producers. + */ + +#include <stdarg.h> +#include <unistd.h> +#include <stdlib.h> +#include <atomic.h> +#include <errno.h> +#include <pthread.h> +#include <strings.h> + +#include "fmev_impl.h" + +static struct { + const char *name; /* channel name */ + evchan_t *binding; /* GPEC binding, once bound */ + const uint32_t flags; /* flags to use in binding */ +} chaninfo[] = { + { FMEV_CHAN_USER_NOPRIV_LV, NULL, 0 }, + { FMEV_CHAN_USER_NOPRIV_HV, NULL, 0 }, + { FMEV_CHAN_USER_PRIV_LV, NULL, EVCH_HOLD_PEND_INDEF }, + { FMEV_CHAN_USER_PRIV_HV, NULL, EVCH_HOLD_PEND_INDEF} +}; + +#define CHANIDX(priv, pri) (2 * ((priv) != 0) + (pri == FMEV_HIPRI)) + +#define CHAN_NAME(priv, pri) (chaninfo[CHANIDX(priv, pri)].name) +#define CHAN_BINDING(priv, pri) (chaninfo[CHANIDX(priv, pri)].binding) +#define CHAN_FLAGS(priv, pri) (chaninfo[CHANIDX(priv, pri)].flags) + +/* + * Called after fork in the new child. We clear the cached event + * channel bindings which are only valid in the process that created + * them. + */ +static void +clear_bindings(void) +{ + int i; + + for (i = 0; i < sizeof (chaninfo) / sizeof chaninfo[0]; i++) + chaninfo[i].binding = NULL; +} + +#pragma init(_fmev_publish_init) + +static void +_fmev_publish_init(void) +{ + (void) pthread_atfork(NULL, NULL, clear_bindings); +} + +static evchan_t * +bind_channel(boolean_t priv, fmev_pri_t pri) +{ + evchan_t **evcpp = &CHAN_BINDING(priv, pri); + evchan_t *evc; + + if (*evcpp != NULL) + return (*evcpp); + + if (sysevent_evc_bind(CHAN_NAME(priv, pri), &evc, + EVCH_CREAT | CHAN_FLAGS(priv, pri)) != 0) + return (NULL); + + if (atomic_cas_ptr(evcpp, NULL, evc) != NULL) + (void) sysevent_evc_unbind(evc); + + return (*evcpp); +} + +static fmev_err_t +vrfy_ruleset(const char *ruleset) +{ + if (ruleset != NULL && + strnlen(ruleset, FMEV_MAX_RULESET_LEN) == FMEV_MAX_RULESET_LEN) + return (FMEVERR_STRING2BIG); + + return (FMEV_OK); + +} + +static fmev_err_t +vrfy_class(const char *class) +{ + if (class == NULL || *class == '\0') + return (FMEVERR_API); + + if (strnlen(class, FMEV_PUB_MAXCLASSLEN) == FMEV_PUB_MAXCLASSLEN) + return (FMEVERR_STRING2BIG); + + return (FMEV_OK); +} + +static fmev_err_t +vrfy_subclass(const char *subclass) +{ + if (subclass == NULL || *subclass == '\0') + return (FMEVERR_API); + + if (strnlen(subclass, FMEV_PUB_MAXSUBCLASSLEN) == + FMEV_PUB_MAXSUBCLASSLEN) + return (FMEVERR_STRING2BIG); + + return (FMEV_OK); +} + +static fmev_err_t +vrfy_pri(fmev_pri_t pri) +{ + return (pri == FMEV_LOPRI || pri == FMEV_HIPRI ? + FMEV_OK : FMEVERR_API); +} + +const char * +fmev_pri_string(fmev_pri_t pri) +{ + static const char *pristr[] = { "low", "high" }; + + if (vrfy_pri(pri) != FMEV_OK) + return (NULL); + + return (pristr[pri - FMEV_LOPRI]); +} + +static fmev_err_t +vrfy(const char **rulesetp, const char **classp, const char **subclassp, + fmev_pri_t *prip) +{ + fmev_err_t rc = FMEV_OK; + + if (rulesetp && (rc = vrfy_ruleset(*rulesetp)) != FMEV_OK) + return (rc); + + if (classp && (rc = vrfy_class(*classp)) != FMEV_OK || + subclassp && (rc = vrfy_subclass(*subclassp)) != FMEV_OK || + prip && (rc = vrfy_pri(*prip)) != FMEV_OK) + return (rc); + + return (FMEV_OK); +} + +uint_t fmev_va2nvl_maxtuples = 100; + +fmev_err_t +va2nvl(nvlist_t **nvlp, va_list ap, uint_t ntuples) +{ + nvlist_t *nvl = NULL; + uint_t processed = 0; + char *name; + + if (ntuples == 0) + return (FMEVERR_INTERNAL); + + if ((name = va_arg(ap, char *)) == NULL || name == FMEV_ARG_TERM) + return (FMEVERR_VARARGS_MALFORMED); + + if (ntuples > fmev_va2nvl_maxtuples) + return (FMEVERR_VARARGS_TOOLONG); + + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) + return (FMEVERR_ALLOC); + + while (name != NULL && name != FMEV_ARG_TERM && processed <= ntuples) { + data_type_t type; + int err, nelem; + + type = va_arg(ap, data_type_t); + + switch (type) { + case DATA_TYPE_BYTE: + err = nvlist_add_byte(nvl, name, + va_arg(ap, uint_t)); + break; + case DATA_TYPE_BYTE_ARRAY: + nelem = va_arg(ap, int); + err = nvlist_add_byte_array(nvl, name, + va_arg(ap, uchar_t *), nelem); + break; + case DATA_TYPE_BOOLEAN_VALUE: + err = nvlist_add_boolean_value(nvl, name, + va_arg(ap, boolean_t)); + break; + case DATA_TYPE_BOOLEAN_ARRAY: + nelem = va_arg(ap, int); + err = nvlist_add_boolean_array(nvl, name, + va_arg(ap, boolean_t *), nelem); + break; + case DATA_TYPE_INT8: + err = nvlist_add_int8(nvl, name, + va_arg(ap, int)); + break; + case DATA_TYPE_INT8_ARRAY: + nelem = va_arg(ap, int); + err = nvlist_add_int8_array(nvl, name, + va_arg(ap, int8_t *), nelem); + break; + case DATA_TYPE_UINT8: + err = nvlist_add_uint8(nvl, name, + va_arg(ap, uint_t)); + break; + case DATA_TYPE_UINT8_ARRAY: + nelem = va_arg(ap, int); + err = nvlist_add_uint8_array(nvl, name, + va_arg(ap, uint8_t *), nelem); + break; + case DATA_TYPE_INT16: + err = nvlist_add_int16(nvl, name, + va_arg(ap, int)); + break; + case DATA_TYPE_INT16_ARRAY: + nelem = va_arg(ap, int); + err = nvlist_add_int16_array(nvl, name, + va_arg(ap, int16_t *), nelem); + break; + case DATA_TYPE_UINT16: + err = nvlist_add_uint16(nvl, name, + va_arg(ap, uint_t)); + break; + case DATA_TYPE_UINT16_ARRAY: + nelem = va_arg(ap, int); + err = nvlist_add_uint16_array(nvl, name, + va_arg(ap, uint16_t *), nelem); + break; + case DATA_TYPE_INT32: + err = nvlist_add_int32(nvl, name, + va_arg(ap, int32_t)); + break; + case DATA_TYPE_INT32_ARRAY: + nelem = va_arg(ap, int); + err = nvlist_add_int32_array(nvl, name, + va_arg(ap, int32_t *), nelem); + break; + case DATA_TYPE_UINT32: + err = nvlist_add_uint32(nvl, name, + va_arg(ap, uint32_t)); + break; + case DATA_TYPE_UINT32_ARRAY: + nelem = va_arg(ap, int); + err = nvlist_add_uint32_array(nvl, name, + va_arg(ap, uint32_t *), nelem); + break; + case DATA_TYPE_INT64: + err = nvlist_add_int64(nvl, name, + va_arg(ap, int64_t)); + break; + case DATA_TYPE_INT64_ARRAY: + nelem = va_arg(ap, int); + err = nvlist_add_int64_array(nvl, name, + va_arg(ap, int64_t *), nelem); + break; + case DATA_TYPE_UINT64: + err = nvlist_add_uint64(nvl, name, + va_arg(ap, uint64_t)); + break; + case DATA_TYPE_UINT64_ARRAY: + nelem = va_arg(ap, int); + err = nvlist_add_uint64_array(nvl, name, + va_arg(ap, uint64_t *), nelem); + break; + case DATA_TYPE_STRING: + err = nvlist_add_string(nvl, name, + va_arg(ap, char *)); + break; + case DATA_TYPE_STRING_ARRAY: + nelem = va_arg(ap, int); + err = nvlist_add_string_array(nvl, name, + va_arg(ap, char **), nelem); + break; + case DATA_TYPE_NVLIST: + err = nvlist_add_nvlist(nvl, name, + va_arg(ap, nvlist_t *)); + break; + case DATA_TYPE_NVLIST_ARRAY: + nelem = va_arg(ap, int); + err = nvlist_add_nvlist_array(nvl, name, + va_arg(ap, nvlist_t **), nelem); + break; + case DATA_TYPE_HRTIME: + err = nvlist_add_hrtime(nvl, name, + va_arg(ap, hrtime_t)); + break; + case DATA_TYPE_DOUBLE: + err = nvlist_add_double(nvl, name, + va_arg(ap, double)); + break; + default: + err = EINVAL; + } + + if (err) + break; /* terminate on first error */ + + processed++; + name = va_arg(ap, char *); + } + + if (name != FMEV_ARG_TERM || processed != ntuples) { + *nvlp = NULL; + nvlist_free(nvl); + return (FMEVERR_VARARGS_MALFORMED); + } + + *nvlp = nvl; + return (FMEV_SUCCESS); +} + +static fmev_err_t +do_publish(const char *file, const char *func, int64_t line, + const char *ruleset, const char *class, const char *subclass, + fmev_pri_t pri, nvlist_t *nvl, uint_t ntuples, va_list ap) +{ + fmev_err_t rc = FMEVERR_INTERNAL; + boolean_t priv = B_TRUE; + nvlist_t *tmpnvl = NULL; + nvlist_t *pub; + evchan_t *evc; + + if (nvl) { + ASSERT(ntuples == 0); + + /* + * Enforce NV_UNIQUE_NAME + */ + if ((nvlist_nvflag(nvl) & NV_UNIQUE_NAME) != NV_UNIQUE_NAME) + return (FMEVERR_NVLIST); + + pub = nvl; + + } else if (ntuples != 0) { + fmev_err_t err; + + err = va2nvl(&tmpnvl, ap, ntuples); + if (err != FMEV_SUCCESS) + return (err); + + pub = tmpnvl; + } else { + /* + * Even if the caller has no tuples to publish (just an event + * class and subclass), we are going to add some detector + * information so we need some nvlist. + */ + if (nvlist_alloc(&tmpnvl, NV_UNIQUE_NAME, 0) != 0) + return (FMEVERR_ALLOC); + + pub = tmpnvl; + } + + evc = bind_channel(priv, pri); + + if (evc == NULL) { + rc = FMEVERR_INTERNAL; + goto done; + } + + + /* + * Add detector information + */ + if (file && nvlist_add_string(pub, "__fmev_file", file) != 0 || + func && nvlist_add_string(pub, "__fmev_func", func) != 0 || + line != -1 && nvlist_add_int64(pub, "__fmev_line", line) != 0 || + nvlist_add_int32(pub, "__fmev_pid", getpid()) != 0 || + nvlist_add_string(pub, "__fmev_execname", getexecname()) != 0) { + rc = FMEVERR_ALLOC; + goto done; + } + + if (ruleset == NULL) + ruleset = FMEV_RULESET_DEFAULT; + + /* + * We abuse the GPEC publication arguments as follows: + * + * GPEC argument Our usage + * -------------------- ----------------- + * const char *class Raw class + * const char *subclass Raw subclass + * const char *vendor Ruleset name + * const char *pub_name Unused + * nvlist_t *attr_list Event attributes + */ + rc = (sysevent_evc_publish(evc, class, subclass, ruleset, "", + pub, EVCH_NOSLEEP) == 0) ? FMEV_SUCCESS : FMEVERR_TRANSPORT; + +done: + /* Free a passed in nvlist iff success */ + if (nvl && rc == FMEV_SUCCESS) + nvlist_free(nvl); + + if (tmpnvl) + nvlist_free(tmpnvl); + + return (rc); +} + +fmev_err_t +_i_fmev_publish_nvl( + const char *file, const char *func, int64_t line, + const char *ruleset, const char *class, const char *subclass, + fmev_pri_t pri, nvlist_t *attr) +{ + fmev_err_t rc; + + if ((rc = vrfy(&ruleset, &class, &subclass, &pri)) != FMEV_OK) + return (rc); /* any attr not freed */ + + return (do_publish(file, func, line, + ruleset, class, subclass, + pri, attr, 0, NULL)); /* any attr freed iff success */ +} + +fmev_err_t +_i_fmev_publish( + const char *file, const char *func, int64_t line, + const char *ruleset, const char *class, const char *subclass, + fmev_pri_t pri, + uint_t ntuples, ...) +{ + va_list ap; + fmev_err_t rc; + + if ((rc = vrfy(&ruleset, &class, &subclass, &pri)) != FMEV_OK) + return (rc); + + if (ntuples != 0) + va_start(ap, ntuples); + + rc = do_publish(file, func, line, + ruleset, class, subclass, + pri, NULL, ntuples, ap); + + if (ntuples != 0) + va_end(ap); + + return (rc); +} + + +#pragma weak fmev_publish = _fmev_publish +#pragma weak fmev_rspublish = _fmev_rspublish + +static fmev_err_t +_fmev_publish(const char *class, const char *subclass, fmev_pri_t pri, + uint_t ntuples, ...) +{ + fmev_err_t rc; + va_list ap; + + if ((rc = vrfy(NULL, &class, &subclass, &pri)) != FMEV_OK) + return (rc); + + if (ntuples != 0) + va_start(ap, ntuples); + + rc = do_publish(NULL, NULL, -1, + FMEV_RULESET_DEFAULT, class, subclass, + pri, NULL, ntuples, ap); + + if (ntuples != 0) + va_end(ap); + + return (rc); +} + +static fmev_err_t +_fmev_rspublish(const char *ruleset, const char *class, const char *subclass, + fmev_pri_t pri, uint_t ntuples, ...) +{ + fmev_err_t rc; + va_list ap; + + if ((rc = vrfy(&ruleset, &class, &subclass, &pri)) != FMEV_OK) + return (rc); + + if (ntuples != 0) + va_start(ap, ntuples); + + rc = do_publish(NULL, NULL, -1, + ruleset, class, subclass, + pri, NULL, ntuples, ap); + + if (ntuples != 0) + va_end(ap); + + return (rc); +} |