diff options
Diffstat (limited to 'cmd/zed/zed_event.c')
-rw-r--r-- | cmd/zed/zed_event.c | 965 |
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); +} |