aboutsummaryrefslogtreecommitdiff
path: root/cmd/zed/zed_event.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/zed/zed_event.c')
-rw-r--r--cmd/zed/zed_event.c965
1 files changed, 965 insertions, 0 deletions
diff --git a/cmd/zed/zed_event.c b/cmd/zed/zed_event.c
new file mode 100644
index 000000000000..1c5d00e297ff
--- /dev/null
+++ b/cmd/zed/zed_event.c
@@ -0,0 +1,965 @@
+/*
+ * This file is part of the ZFS Event Daemon (ZED)
+ * for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
+ * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
+ * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
+ * Refer to the ZoL git commit log for authoritative copyright attribution.
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License Version 1.0 (CDDL-1.0).
+ * You can obtain a copy of the license from the top-level file
+ * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
+ * You may not use this file except in compliance with the license.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libzfs.h> /* FIXME: Replace with libzfs_core. */
+#include <paths.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/zfs_ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/fm/fs/zfs.h>
+#include "zed.h"
+#include "zed_conf.h"
+#include "zed_disk_event.h"
+#include "zed_event.h"
+#include "zed_exec.h"
+#include "zed_file.h"
+#include "zed_log.h"
+#include "zed_strings.h"
+
+#include "agents/zfs_agents.h"
+
+#define MAXBUF 4096
+
+/*
+ * Open the libzfs interface.
+ */
+int
+zed_event_init(struct zed_conf *zcp)
+{
+ if (!zcp)
+ zed_log_die("Failed zed_event_init: %s", strerror(EINVAL));
+
+ zcp->zfs_hdl = libzfs_init();
+ if (!zcp->zfs_hdl) {
+ if (zcp->do_idle)
+ return (-1);
+ zed_log_die("Failed to initialize libzfs");
+ }
+
+ zcp->zevent_fd = open(ZFS_DEV, O_RDWR);
+ if (zcp->zevent_fd < 0) {
+ if (zcp->do_idle)
+ return (-1);
+ zed_log_die("Failed to open \"%s\": %s",
+ ZFS_DEV, strerror(errno));
+ }
+
+ zfs_agent_init(zcp->zfs_hdl);
+
+ if (zed_disk_event_init() != 0) {
+ if (zcp->do_idle)
+ return (-1);
+ zed_log_die("Failed to initialize disk events");
+ }
+
+ return (0);
+}
+
+/*
+ * Close the libzfs interface.
+ */
+void
+zed_event_fini(struct zed_conf *zcp)
+{
+ if (!zcp)
+ zed_log_die("Failed zed_event_fini: %s", strerror(EINVAL));
+
+ zed_disk_event_fini();
+ zfs_agent_fini();
+
+ if (zcp->zevent_fd >= 0) {
+ if (close(zcp->zevent_fd) < 0)
+ zed_log_msg(LOG_WARNING, "Failed to close \"%s\": %s",
+ ZFS_DEV, strerror(errno));
+
+ zcp->zevent_fd = -1;
+ }
+ if (zcp->zfs_hdl) {
+ libzfs_fini(zcp->zfs_hdl);
+ zcp->zfs_hdl = NULL;
+ }
+}
+
+/*
+ * Seek to the event specified by [saved_eid] and [saved_etime].
+ * This protects against processing a given event more than once.
+ * Return 0 upon a successful seek to the specified event, or -1 otherwise.
+ *
+ * A zevent is considered to be uniquely specified by its (eid,time) tuple.
+ * The unsigned 64b eid is set to 1 when the kernel module is loaded, and
+ * incremented by 1 for each new event. Since the state file can persist
+ * across a kernel module reload, the time must be checked to ensure a match.
+ */
+int
+zed_event_seek(struct zed_conf *zcp, uint64_t saved_eid, int64_t saved_etime[])
+{
+ uint64_t eid;
+ int found;
+ nvlist_t *nvl;
+ int n_dropped;
+ int64_t *etime;
+ uint_t nelem;
+ int rv;
+
+ if (!zcp) {
+ errno = EINVAL;
+ zed_log_msg(LOG_ERR, "Failed to seek zevent: %s",
+ strerror(errno));
+ return (-1);
+ }
+ eid = 0;
+ found = 0;
+ while ((eid < saved_eid) && !found) {
+ rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped,
+ ZEVENT_NONBLOCK, zcp->zevent_fd);
+
+ if ((rv != 0) || !nvl)
+ break;
+
+ if (n_dropped > 0) {
+ zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
+ /*
+ * FIXME: Increase max size of event nvlist in
+ * /sys/module/zfs/parameters/zfs_zevent_len_max ?
+ */
+ }
+ if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
+ zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
+ } else if (nvlist_lookup_int64_array(nvl, "time",
+ &etime, &nelem) != 0) {
+ zed_log_msg(LOG_WARNING,
+ "Failed to lookup zevent time (eid=%llu)", eid);
+ } else if (nelem != 2) {
+ zed_log_msg(LOG_WARNING,
+ "Failed to lookup zevent time (eid=%llu, nelem=%u)",
+ eid, nelem);
+ } else if ((eid != saved_eid) ||
+ (etime[0] != saved_etime[0]) ||
+ (etime[1] != saved_etime[1])) {
+ /* no-op */
+ } else {
+ found = 1;
+ }
+ free(nvl);
+ }
+ if (!found && (saved_eid > 0)) {
+ if (zpool_events_seek(zcp->zfs_hdl, ZEVENT_SEEK_START,
+ zcp->zevent_fd) < 0)
+ zed_log_msg(LOG_WARNING, "Failed to seek to eid=0");
+ else
+ eid = 0;
+ }
+ zed_log_msg(LOG_NOTICE, "Processing events since eid=%llu", eid);
+ return (found ? 0 : -1);
+}
+
+/*
+ * Return non-zero if nvpair [name] should be formatted in hex; o/w, return 0.
+ */
+static int
+_zed_event_value_is_hex(const char *name)
+{
+ const char *hex_suffix[] = {
+ "_guid",
+ "_guids",
+ NULL
+ };
+ const char **pp;
+ char *p;
+
+ if (!name)
+ return (0);
+
+ for (pp = hex_suffix; *pp; pp++) {
+ p = strstr(name, *pp);
+ if (p && strlen(p) == strlen(*pp))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Add an environment variable for [eid] to the container [zsp].
+ *
+ * The variable name is the concatenation of [prefix] and [name] converted to
+ * uppercase with non-alphanumeric characters converted to underscores;
+ * [prefix] is optional, and [name] must begin with an alphabetic character.
+ * If the converted variable name already exists within the container [zsp],
+ * its existing value will be replaced with the new value.
+ *
+ * The variable value is specified by the format string [fmt].
+ *
+ * Returns 0 on success, and -1 on error (with errno set).
+ *
+ * All environment variables in [zsp] should be added through this function.
+ */
+static int
+_zed_event_add_var(uint64_t eid, zed_strings_t *zsp,
+ const char *prefix, const char *name, const char *fmt, ...)
+{
+ char keybuf[MAXBUF];
+ char valbuf[MAXBUF];
+ char *dstp;
+ const char *srcp;
+ const char *lastp;
+ int n;
+ int buflen;
+ va_list vargs;
+
+ assert(zsp != NULL);
+ assert(fmt != NULL);
+
+ if (!name) {
+ errno = EINVAL;
+ zed_log_msg(LOG_WARNING,
+ "Failed to add variable for eid=%llu: Name is empty", eid);
+ return (-1);
+ } else if (!isalpha(name[0])) {
+ errno = EINVAL;
+ zed_log_msg(LOG_WARNING,
+ "Failed to add variable for eid=%llu: "
+ "Name \"%s\" is invalid", eid, name);
+ return (-1);
+ }
+ /*
+ * Construct the string key by converting PREFIX (if present) and NAME.
+ */
+ dstp = keybuf;
+ lastp = keybuf + sizeof (keybuf);
+ if (prefix) {
+ for (srcp = prefix; *srcp && (dstp < lastp); srcp++)
+ *dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_';
+ }
+ for (srcp = name; *srcp && (dstp < lastp); srcp++)
+ *dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_';
+
+ if (dstp == lastp) {
+ errno = ENAMETOOLONG;
+ zed_log_msg(LOG_WARNING,
+ "Failed to add variable for eid=%llu: Name too long", eid);
+ return (-1);
+ }
+ *dstp = '\0';
+ /*
+ * Construct the string specified by "[PREFIX][NAME]=[FMT]".
+ */
+ dstp = valbuf;
+ buflen = sizeof (valbuf);
+ n = strlcpy(dstp, keybuf, buflen);
+ if (n >= sizeof (valbuf)) {
+ errno = EMSGSIZE;
+ zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
+ keybuf, eid, "Exceeded buffer size");
+ return (-1);
+ }
+ dstp += n;
+ buflen -= n;
+
+ *dstp++ = '=';
+ buflen--;
+
+ if (buflen <= 0) {
+ errno = EMSGSIZE;
+ zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
+ keybuf, eid, "Exceeded buffer size");
+ return (-1);
+ }
+
+ va_start(vargs, fmt);
+ n = vsnprintf(dstp, buflen, fmt, vargs);
+ va_end(vargs);
+
+ if ((n < 0) || (n >= buflen)) {
+ errno = EMSGSIZE;
+ zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
+ keybuf, eid, "Exceeded buffer size");
+ return (-1);
+ } else if (zed_strings_add(zsp, keybuf, valbuf) < 0) {
+ zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
+ keybuf, eid, strerror(errno));
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+_zed_event_add_array_err(uint64_t eid, const char *name)
+{
+ errno = EMSGSIZE;
+ zed_log_msg(LOG_WARNING,
+ "Failed to convert nvpair \"%s\" for eid=%llu: "
+ "Exceeded buffer size", name, eid);
+ return (-1);
+}
+
+static int
+_zed_event_add_int8_array(uint64_t eid, zed_strings_t *zsp,
+ const char *prefix, nvpair_t *nvp)
+{
+ char buf[MAXBUF];
+ int buflen = sizeof (buf);
+ const char *name;
+ int8_t *i8p;
+ uint_t nelem;
+ uint_t i;
+ char *p;
+ int n;
+
+ assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT8_ARRAY));
+
+ name = nvpair_name(nvp);
+ (void) nvpair_value_int8_array(nvp, &i8p, &nelem);
+ for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
+ n = snprintf(p, buflen, "%d ", i8p[i]);
+ if ((n < 0) || (n >= buflen))
+ return (_zed_event_add_array_err(eid, name));
+ p += n;
+ buflen -= n;
+ }
+ if (nelem > 0)
+ *--p = '\0';
+
+ return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
+}
+
+static int
+_zed_event_add_uint8_array(uint64_t eid, zed_strings_t *zsp,
+ const char *prefix, nvpair_t *nvp)
+{
+ char buf[MAXBUF];
+ int buflen = sizeof (buf);
+ const char *name;
+ uint8_t *u8p;
+ uint_t nelem;
+ uint_t i;
+ char *p;
+ int n;
+
+ assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT8_ARRAY));
+
+ name = nvpair_name(nvp);
+ (void) nvpair_value_uint8_array(nvp, &u8p, &nelem);
+ for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
+ n = snprintf(p, buflen, "%u ", u8p[i]);
+ if ((n < 0) || (n >= buflen))
+ return (_zed_event_add_array_err(eid, name));
+ p += n;
+ buflen -= n;
+ }
+ if (nelem > 0)
+ *--p = '\0';
+
+ return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
+}
+
+static int
+_zed_event_add_int16_array(uint64_t eid, zed_strings_t *zsp,
+ const char *prefix, nvpair_t *nvp)
+{
+ char buf[MAXBUF];
+ int buflen = sizeof (buf);
+ const char *name;
+ int16_t *i16p;
+ uint_t nelem;
+ uint_t i;
+ char *p;
+ int n;
+
+ assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT16_ARRAY));
+
+ name = nvpair_name(nvp);
+ (void) nvpair_value_int16_array(nvp, &i16p, &nelem);
+ for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
+ n = snprintf(p, buflen, "%d ", i16p[i]);
+ if ((n < 0) || (n >= buflen))
+ return (_zed_event_add_array_err(eid, name));
+ p += n;
+ buflen -= n;
+ }
+ if (nelem > 0)
+ *--p = '\0';
+
+ return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
+}
+
+static int
+_zed_event_add_uint16_array(uint64_t eid, zed_strings_t *zsp,
+ const char *prefix, nvpair_t *nvp)
+{
+ char buf[MAXBUF];
+ int buflen = sizeof (buf);
+ const char *name;
+ uint16_t *u16p;
+ uint_t nelem;
+ uint_t i;
+ char *p;
+ int n;
+
+ assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT16_ARRAY));
+
+ name = nvpair_name(nvp);
+ (void) nvpair_value_uint16_array(nvp, &u16p, &nelem);
+ for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
+ n = snprintf(p, buflen, "%u ", u16p[i]);
+ if ((n < 0) || (n >= buflen))
+ return (_zed_event_add_array_err(eid, name));
+ p += n;
+ buflen -= n;
+ }
+ if (nelem > 0)
+ *--p = '\0';
+
+ return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
+}
+
+static int
+_zed_event_add_int32_array(uint64_t eid, zed_strings_t *zsp,
+ const char *prefix, nvpair_t *nvp)
+{
+ char buf[MAXBUF];
+ int buflen = sizeof (buf);
+ const char *name;
+ int32_t *i32p;
+ uint_t nelem;
+ uint_t i;
+ char *p;
+ int n;
+
+ assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT32_ARRAY));
+
+ name = nvpair_name(nvp);
+ (void) nvpair_value_int32_array(nvp, &i32p, &nelem);
+ for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
+ n = snprintf(p, buflen, "%d ", i32p[i]);
+ if ((n < 0) || (n >= buflen))
+ return (_zed_event_add_array_err(eid, name));
+ p += n;
+ buflen -= n;
+ }
+ if (nelem > 0)
+ *--p = '\0';
+
+ return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
+}
+
+static int
+_zed_event_add_uint32_array(uint64_t eid, zed_strings_t *zsp,
+ const char *prefix, nvpair_t *nvp)
+{
+ char buf[MAXBUF];
+ int buflen = sizeof (buf);
+ const char *name;
+ uint32_t *u32p;
+ uint_t nelem;
+ uint_t i;
+ char *p;
+ int n;
+
+ assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT32_ARRAY));
+
+ name = nvpair_name(nvp);
+ (void) nvpair_value_uint32_array(nvp, &u32p, &nelem);
+ for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
+ n = snprintf(p, buflen, "%u ", u32p[i]);
+ if ((n < 0) || (n >= buflen))
+ return (_zed_event_add_array_err(eid, name));
+ p += n;
+ buflen -= n;
+ }
+ if (nelem > 0)
+ *--p = '\0';
+
+ return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
+}
+
+static int
+_zed_event_add_int64_array(uint64_t eid, zed_strings_t *zsp,
+ const char *prefix, nvpair_t *nvp)
+{
+ char buf[MAXBUF];
+ int buflen = sizeof (buf);
+ const char *name;
+ int64_t *i64p;
+ uint_t nelem;
+ uint_t i;
+ char *p;
+ int n;
+
+ assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT64_ARRAY));
+
+ name = nvpair_name(nvp);
+ (void) nvpair_value_int64_array(nvp, &i64p, &nelem);
+ for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
+ n = snprintf(p, buflen, "%lld ", (u_longlong_t)i64p[i]);
+ if ((n < 0) || (n >= buflen))
+ return (_zed_event_add_array_err(eid, name));
+ p += n;
+ buflen -= n;
+ }
+ if (nelem > 0)
+ *--p = '\0';
+
+ return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
+}
+
+static int
+_zed_event_add_uint64_array(uint64_t eid, zed_strings_t *zsp,
+ const char *prefix, nvpair_t *nvp)
+{
+ char buf[MAXBUF];
+ int buflen = sizeof (buf);
+ const char *name;
+ const char *fmt;
+ uint64_t *u64p;
+ uint_t nelem;
+ uint_t i;
+ char *p;
+ int n;
+
+ assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY));
+
+ name = nvpair_name(nvp);
+ fmt = _zed_event_value_is_hex(name) ? "0x%.16llX " : "%llu ";
+ (void) nvpair_value_uint64_array(nvp, &u64p, &nelem);
+ for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
+ n = snprintf(p, buflen, fmt, (u_longlong_t)u64p[i]);
+ if ((n < 0) || (n >= buflen))
+ return (_zed_event_add_array_err(eid, name));
+ p += n;
+ buflen -= n;
+ }
+ if (nelem > 0)
+ *--p = '\0';
+
+ return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
+}
+
+static int
+_zed_event_add_string_array(uint64_t eid, zed_strings_t *zsp,
+ const char *prefix, nvpair_t *nvp)
+{
+ char buf[MAXBUF];
+ int buflen = sizeof (buf);
+ const char *name;
+ char **strp;
+ uint_t nelem;
+ uint_t i;
+ char *p;
+ int n;
+
+ assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY));
+
+ name = nvpair_name(nvp);
+ (void) nvpair_value_string_array(nvp, &strp, &nelem);
+ for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
+ n = snprintf(p, buflen, "%s ", strp[i] ? strp[i] : "<NULL>");
+ if ((n < 0) || (n >= buflen))
+ return (_zed_event_add_array_err(eid, name));
+ p += n;
+ buflen -= n;
+ }
+ if (nelem > 0)
+ *--p = '\0';
+
+ return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
+}
+
+/*
+ * Convert the nvpair [nvp] to a string which is added to the environment
+ * of the child process.
+ * Return 0 on success, -1 on error.
+ *
+ * FIXME: Refactor with cmd/zpool/zpool_main.c:zpool_do_events_nvprint()?
+ */
+static void
+_zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp)
+{
+ const char *name;
+ data_type_t type;
+ const char *prefix = ZEVENT_VAR_PREFIX;
+ boolean_t b;
+ double d;
+ uint8_t i8;
+ uint16_t i16;
+ uint32_t i32;
+ uint64_t i64;
+ char *str;
+
+ assert(zsp != NULL);
+ assert(nvp != NULL);
+
+ name = nvpair_name(nvp);
+ type = nvpair_type(nvp);
+
+ switch (type) {
+ case DATA_TYPE_BOOLEAN:
+ _zed_event_add_var(eid, zsp, prefix, name, "%s", "1");
+ break;
+ case DATA_TYPE_BOOLEAN_VALUE:
+ (void) nvpair_value_boolean_value(nvp, &b);
+ _zed_event_add_var(eid, zsp, prefix, name, "%s", b ? "1" : "0");
+ break;
+ case DATA_TYPE_BYTE:
+ (void) nvpair_value_byte(nvp, &i8);
+ _zed_event_add_var(eid, zsp, prefix, name, "%d", i8);
+ break;
+ case DATA_TYPE_INT8:
+ (void) nvpair_value_int8(nvp, (int8_t *)&i8);
+ _zed_event_add_var(eid, zsp, prefix, name, "%d", i8);
+ break;
+ case DATA_TYPE_UINT8:
+ (void) nvpair_value_uint8(nvp, &i8);
+ _zed_event_add_var(eid, zsp, prefix, name, "%u", i8);
+ break;
+ case DATA_TYPE_INT16:
+ (void) nvpair_value_int16(nvp, (int16_t *)&i16);
+ _zed_event_add_var(eid, zsp, prefix, name, "%d", i16);
+ break;
+ case DATA_TYPE_UINT16:
+ (void) nvpair_value_uint16(nvp, &i16);
+ _zed_event_add_var(eid, zsp, prefix, name, "%u", i16);
+ break;
+ case DATA_TYPE_INT32:
+ (void) nvpair_value_int32(nvp, (int32_t *)&i32);
+ _zed_event_add_var(eid, zsp, prefix, name, "%d", i32);
+ break;
+ case DATA_TYPE_UINT32:
+ (void) nvpair_value_uint32(nvp, &i32);
+ _zed_event_add_var(eid, zsp, prefix, name, "%u", i32);
+ break;
+ case DATA_TYPE_INT64:
+ (void) nvpair_value_int64(nvp, (int64_t *)&i64);
+ _zed_event_add_var(eid, zsp, prefix, name,
+ "%lld", (longlong_t)i64);
+ break;
+ case DATA_TYPE_UINT64:
+ (void) nvpair_value_uint64(nvp, &i64);
+ _zed_event_add_var(eid, zsp, prefix, name,
+ (_zed_event_value_is_hex(name) ? "0x%.16llX" : "%llu"),
+ (u_longlong_t)i64);
+ /*
+ * shadow readable strings for vdev state pairs
+ */
+ if (strcmp(name, FM_EREPORT_PAYLOAD_ZFS_VDEV_STATE) == 0 ||
+ strcmp(name, FM_EREPORT_PAYLOAD_ZFS_VDEV_LASTSTATE) == 0) {
+ char alt[32];
+
+ (void) snprintf(alt, sizeof (alt), "%s_str", name);
+ _zed_event_add_var(eid, zsp, prefix, alt, "%s",
+ zpool_state_to_name(i64, VDEV_AUX_NONE));
+ } else
+ /*
+ * shadow readable strings for pool state
+ */
+ if (strcmp(name, FM_EREPORT_PAYLOAD_ZFS_POOL_STATE) == 0) {
+ char alt[32];
+
+ (void) snprintf(alt, sizeof (alt), "%s_str", name);
+ _zed_event_add_var(eid, zsp, prefix, alt, "%s",
+ zpool_pool_state_to_name(i64));
+ }
+ break;
+ case DATA_TYPE_DOUBLE:
+ (void) nvpair_value_double(nvp, &d);
+ _zed_event_add_var(eid, zsp, prefix, name, "%g", d);
+ break;
+ case DATA_TYPE_HRTIME:
+ (void) nvpair_value_hrtime(nvp, (hrtime_t *)&i64);
+ _zed_event_add_var(eid, zsp, prefix, name,
+ "%llu", (u_longlong_t)i64);
+ break;
+ case DATA_TYPE_NVLIST:
+ _zed_event_add_var(eid, zsp, prefix, name,
+ "%s", "_NOT_IMPLEMENTED_"); /* FIXME */
+ break;
+ case DATA_TYPE_STRING:
+ (void) nvpair_value_string(nvp, &str);
+ _zed_event_add_var(eid, zsp, prefix, name,
+ "%s", (str ? str : "<NULL>"));
+ break;
+ case DATA_TYPE_BOOLEAN_ARRAY:
+ _zed_event_add_var(eid, zsp, prefix, name,
+ "%s", "_NOT_IMPLEMENTED_"); /* FIXME */
+ break;
+ case DATA_TYPE_BYTE_ARRAY:
+ _zed_event_add_var(eid, zsp, prefix, name,
+ "%s", "_NOT_IMPLEMENTED_"); /* FIXME */
+ break;
+ case DATA_TYPE_INT8_ARRAY:
+ _zed_event_add_int8_array(eid, zsp, prefix, nvp);
+ break;
+ case DATA_TYPE_UINT8_ARRAY:
+ _zed_event_add_uint8_array(eid, zsp, prefix, nvp);
+ break;
+ case DATA_TYPE_INT16_ARRAY:
+ _zed_event_add_int16_array(eid, zsp, prefix, nvp);
+ break;
+ case DATA_TYPE_UINT16_ARRAY:
+ _zed_event_add_uint16_array(eid, zsp, prefix, nvp);
+ break;
+ case DATA_TYPE_INT32_ARRAY:
+ _zed_event_add_int32_array(eid, zsp, prefix, nvp);
+ break;
+ case DATA_TYPE_UINT32_ARRAY:
+ _zed_event_add_uint32_array(eid, zsp, prefix, nvp);
+ break;
+ case DATA_TYPE_INT64_ARRAY:
+ _zed_event_add_int64_array(eid, zsp, prefix, nvp);
+ break;
+ case DATA_TYPE_UINT64_ARRAY:
+ _zed_event_add_uint64_array(eid, zsp, prefix, nvp);
+ break;
+ case DATA_TYPE_STRING_ARRAY:
+ _zed_event_add_string_array(eid, zsp, prefix, nvp);
+ break;
+ case DATA_TYPE_NVLIST_ARRAY:
+ _zed_event_add_var(eid, zsp, prefix, name,
+ "%s", "_NOT_IMPLEMENTED_"); /* FIXME */
+ break;
+ default:
+ errno = EINVAL;
+ zed_log_msg(LOG_WARNING,
+ "Failed to convert nvpair \"%s\" for eid=%llu: "
+ "Unrecognized type=%u", name, eid, (unsigned int) type);
+ break;
+ }
+}
+
+/*
+ * Restrict various environment variables to safe and sane values
+ * when constructing the environment for the child process, unless
+ * we're running with a custom $PATH (like under the ZFS test suite).
+ *
+ * Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1.
+ */
+static void
+_zed_event_add_env_restrict(uint64_t eid, zed_strings_t *zsp,
+ const char *path)
+{
+ const char *env_restrict[][2] = {
+ { "IFS", " \t\n" },
+ { "PATH", _PATH_STDPATH },
+ { "ZDB", SBINDIR "/zdb" },
+ { "ZED", SBINDIR "/zed" },
+ { "ZFS", SBINDIR "/zfs" },
+ { "ZINJECT", SBINDIR "/zinject" },
+ { "ZPOOL", SBINDIR "/zpool" },
+ { "ZFS_ALIAS", ZFS_META_ALIAS },
+ { "ZFS_VERSION", ZFS_META_VERSION },
+ { "ZFS_RELEASE", ZFS_META_RELEASE },
+ { NULL, NULL }
+ };
+
+ /*
+ * If we have a custom $PATH, use the default ZFS binary locations
+ * instead of the hard-coded ones.
+ */
+ const char *env_path[][2] = {
+ { "IFS", " \t\n" },
+ { "PATH", NULL }, /* $PATH copied in later on */
+ { "ZDB", "zdb" },
+ { "ZED", "zed" },
+ { "ZFS", "zfs" },
+ { "ZINJECT", "zinject" },
+ { "ZPOOL", "zpool" },
+ { "ZFS_ALIAS", ZFS_META_ALIAS },
+ { "ZFS_VERSION", ZFS_META_VERSION },
+ { "ZFS_RELEASE", ZFS_META_RELEASE },
+ { NULL, NULL }
+ };
+ const char *(*pa)[2];
+
+ assert(zsp != NULL);
+
+ pa = path != NULL ? env_path : env_restrict;
+
+ for (; *(*pa); pa++) {
+ /* Use our custom $PATH if we have one */
+ if (path != NULL && strcmp((*pa)[0], "PATH") == 0)
+ (*pa)[1] = path;
+
+ _zed_event_add_var(eid, zsp, NULL, (*pa)[0], "%s", (*pa)[1]);
+ }
+}
+
+/*
+ * Preserve specified variables from the parent environment
+ * when constructing the environment for the child process.
+ *
+ * Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1.
+ */
+static void
+_zed_event_add_env_preserve(uint64_t eid, zed_strings_t *zsp)
+{
+ const char *env_preserve[] = {
+ "TZ",
+ NULL
+ };
+ const char **keyp;
+ const char *val;
+
+ assert(zsp != NULL);
+
+ for (keyp = env_preserve; *keyp; keyp++) {
+ if ((val = getenv(*keyp)))
+ _zed_event_add_var(eid, zsp, NULL, *keyp, "%s", val);
+ }
+}
+
+/*
+ * Compute the "subclass" by removing the first 3 components of [class]
+ * (which will always be of the form "*.fs.zfs"). Return a pointer inside
+ * the string [class], or NULL if insufficient components exist.
+ */
+static const char *
+_zed_event_get_subclass(const char *class)
+{
+ const char *p;
+ int i;
+
+ if (!class)
+ return (NULL);
+
+ p = class;
+ for (i = 0; i < 3; i++) {
+ p = strchr(p, '.');
+ if (!p)
+ break;
+ p++;
+ }
+ return (p);
+}
+
+/*
+ * Convert the zevent time from a 2-element array of 64b integers
+ * into a more convenient form:
+ * - TIME_SECS is the second component of the time.
+ * - TIME_NSECS is the nanosecond component of the time.
+ * - TIME_STRING is an almost-RFC3339-compliant string representation.
+ */
+static void
+_zed_event_add_time_strings(uint64_t eid, zed_strings_t *zsp, int64_t etime[])
+{
+ struct tm *stp;
+ char buf[32];
+
+ assert(zsp != NULL);
+ assert(etime != NULL);
+
+ _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_SECS",
+ "%lld", (long long int) etime[0]);
+ _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_NSECS",
+ "%lld", (long long int) etime[1]);
+
+ if (!(stp = localtime((const time_t *) &etime[0]))) {
+ zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",
+ ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "localtime error");
+ } else if (!strftime(buf, sizeof (buf), "%Y-%m-%d %H:%M:%S%z", stp)) {
+ zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",
+ ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "strftime error");
+ } else {
+ _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_STRING",
+ "%s", buf);
+ }
+}
+
+/*
+ * Service the next zevent, blocking until one is available.
+ */
+int
+zed_event_service(struct zed_conf *zcp)
+{
+ nvlist_t *nvl;
+ nvpair_t *nvp;
+ int n_dropped;
+ zed_strings_t *zsp;
+ uint64_t eid;
+ int64_t *etime;
+ uint_t nelem;
+ char *class;
+ const char *subclass;
+ int rv;
+
+ if (!zcp) {
+ errno = EINVAL;
+ zed_log_msg(LOG_ERR, "Failed to service zevent: %s",
+ strerror(errno));
+ return (EINVAL);
+ }
+ rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONE,
+ zcp->zevent_fd);
+
+ if ((rv != 0) || !nvl)
+ return (errno);
+
+ if (n_dropped > 0) {
+ zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
+ /*
+ * FIXME: Increase max size of event nvlist in
+ * /sys/module/zfs/parameters/zfs_zevent_len_max ?
+ */
+ }
+ if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
+ zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
+ } else if (nvlist_lookup_int64_array(
+ nvl, "time", &etime, &nelem) != 0) {
+ zed_log_msg(LOG_WARNING,
+ "Failed to lookup zevent time (eid=%llu)", eid);
+ } else if (nelem != 2) {
+ zed_log_msg(LOG_WARNING,
+ "Failed to lookup zevent time (eid=%llu, nelem=%u)",
+ eid, nelem);
+ } else if (nvlist_lookup_string(nvl, "class", &class) != 0) {
+ zed_log_msg(LOG_WARNING,
+ "Failed to lookup zevent class (eid=%llu)", eid);
+ } else {
+ /* let internal modules see this event first */
+ zfs_agent_post_event(class, NULL, nvl);
+
+ zsp = zed_strings_create();
+
+ nvp = NULL;
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)))
+ _zed_event_add_nvpair(eid, zsp, nvp);
+
+ _zed_event_add_env_restrict(eid, zsp, zcp->path);
+ _zed_event_add_env_preserve(eid, zsp);
+
+ _zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "PID",
+ "%d", (int)getpid());
+ _zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "ZEDLET_DIR",
+ "%s", zcp->zedlet_dir);
+ subclass = _zed_event_get_subclass(class);
+ _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "SUBCLASS",
+ "%s", (subclass ? subclass : class));
+
+ _zed_event_add_time_strings(eid, zsp, etime);
+
+ zed_exec_process(eid, class, subclass,
+ zcp->zedlet_dir, zcp->zedlets, zsp, zcp->zevent_fd);
+
+ zed_conf_write_state(zcp, eid, etime);
+
+ zed_strings_destroy(zsp);
+ }
+ nvlist_free(nvl);
+ return (0);
+}