Path: blob/main/sys/contrib/openzfs/cmd/zed/zed_event.c
48380 views
// SPDX-License-Identifier: CDDL-1.01/*2* This file is part of the ZFS Event Daemon (ZED).3*4* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).5* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.6* Refer to the OpenZFS git commit log for authoritative copyright attribution.7*8* The contents of this file are subject to the terms of the9* Common Development and Distribution License Version 1.0 (CDDL-1.0).10* You can obtain a copy of the license from the top-level file11* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.12* You may not use this file except in compliance with the license.13*/1415#include <ctype.h>16#include <errno.h>17#include <fcntl.h>18#include <libzfs_core.h>19#include <paths.h>20#include <stdarg.h>21#include <stdio.h>22#include <stdlib.h>23#include <string.h>24#include <sys/zfs_ioctl.h>25#include <time.h>26#include <unistd.h>27#include <sys/fm/fs/zfs.h>28#include "zed.h"29#include "zed_conf.h"30#include "zed_disk_event.h"31#include "zed_event.h"32#include "zed_exec.h"33#include "zed_file.h"34#include "zed_log.h"35#include "zed_strings.h"3637#include "agents/zfs_agents.h"38#include <libzutil.h>3940#define MAXBUF 40964142static int max_zevent_buf_len = 1 << 20;4344/*45* Open the libzfs interface.46*/47int48zed_event_init(struct zed_conf *zcp)49{50if (!zcp)51zed_log_die("Failed zed_event_init: %s", strerror(EINVAL));5253zcp->zfs_hdl = libzfs_init();54if (!zcp->zfs_hdl) {55if (zcp->do_idle)56return (-1);57zed_log_die("Failed to initialize libzfs");58}5960zcp->zevent_fd = open(ZFS_DEV, O_RDWR | O_CLOEXEC);61if (zcp->zevent_fd < 0) {62if (zcp->do_idle)63return (-1);64zed_log_die("Failed to open \"%s\": %s",65ZFS_DEV, strerror(errno));66}6768zfs_agent_init(zcp->zfs_hdl);6970if (zed_disk_event_init() != 0) {71if (zcp->do_idle)72return (-1);73zed_log_die("Failed to initialize disk events");74}7576if (zcp->max_zevent_buf_len != 0)77max_zevent_buf_len = zcp->max_zevent_buf_len;7879return (0);80}8182/*83* Close the libzfs interface.84*/85void86zed_event_fini(struct zed_conf *zcp)87{88if (!zcp)89zed_log_die("Failed zed_event_fini: %s", strerror(EINVAL));9091zed_disk_event_fini();92zfs_agent_fini();9394if (zcp->zevent_fd >= 0) {95if (close(zcp->zevent_fd) < 0)96zed_log_msg(LOG_WARNING, "Failed to close \"%s\": %s",97ZFS_DEV, strerror(errno));9899zcp->zevent_fd = -1;100}101if (zcp->zfs_hdl) {102libzfs_fini(zcp->zfs_hdl);103zcp->zfs_hdl = NULL;104}105106zed_exec_fini();107}108109static void110_bump_event_queue_length(void)111{112int zzlm, wr;113char qlen_buf[12] = {0}; /* parameter is int => max "-2147483647\n" */114long int qlen, orig_qlen;115116zzlm = open("/sys/module/zfs/parameters/zfs_zevent_len_max", O_RDWR);117if (zzlm < 0)118goto done;119120if (read(zzlm, qlen_buf, sizeof (qlen_buf)) < 0)121goto done;122qlen_buf[sizeof (qlen_buf) - 1] = '\0';123124errno = 0;125orig_qlen = qlen = strtol(qlen_buf, NULL, 10);126if (errno == ERANGE)127goto done;128129if (qlen <= 0)130qlen = 512; /* default zfs_zevent_len_max value */131else132qlen *= 2;133134/*135* Don't consume all of kernel memory with event logs if something136* goes wrong.137*/138if (qlen > max_zevent_buf_len)139qlen = max_zevent_buf_len;140if (qlen == orig_qlen)141goto done;142wr = snprintf(qlen_buf, sizeof (qlen_buf), "%ld", qlen);143if (wr >= sizeof (qlen_buf)) {144wr = sizeof (qlen_buf) - 1;145zed_log_msg(LOG_WARNING, "Truncation in %s()", __func__);146}147148if (pwrite(zzlm, qlen_buf, wr + 1, 0) < 0)149goto done;150151zed_log_msg(LOG_WARNING, "Bumping queue length to %ld", qlen);152153done:154if (zzlm > -1)155(void) close(zzlm);156}157158/*159* Seek to the event specified by [saved_eid] and [saved_etime].160* This protects against processing a given event more than once.161* Return 0 upon a successful seek to the specified event, or -1 otherwise.162*163* A zevent is considered to be uniquely specified by its (eid,time) tuple.164* The unsigned 64b eid is set to 1 when the kernel module is loaded, and165* incremented by 1 for each new event. Since the state file can persist166* across a kernel module reload, the time must be checked to ensure a match.167*/168int169zed_event_seek(struct zed_conf *zcp, uint64_t saved_eid, int64_t saved_etime[])170{171uint64_t eid;172int found;173nvlist_t *nvl;174int n_dropped;175int64_t *etime;176uint_t nelem;177int rv;178179if (!zcp) {180errno = EINVAL;181zed_log_msg(LOG_ERR, "Failed to seek zevent: %s",182strerror(errno));183return (-1);184}185eid = 0;186found = 0;187while ((eid < saved_eid) && !found) {188rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped,189ZEVENT_NONBLOCK, zcp->zevent_fd);190191if ((rv != 0) || !nvl)192break;193194if (n_dropped > 0) {195zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);196_bump_event_queue_length();197}198if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {199zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");200} else if (nvlist_lookup_int64_array(nvl, "time",201&etime, &nelem) != 0) {202zed_log_msg(LOG_WARNING,203"Failed to lookup zevent time (eid=%llu)", eid);204} else if (nelem != 2) {205zed_log_msg(LOG_WARNING,206"Failed to lookup zevent time (eid=%llu, nelem=%u)",207eid, nelem);208} else if ((eid != saved_eid) ||209(etime[0] != saved_etime[0]) ||210(etime[1] != saved_etime[1])) {211/* no-op */212} else {213found = 1;214}215free(nvl);216}217if (!found && (saved_eid > 0)) {218if (zpool_events_seek(zcp->zfs_hdl, ZEVENT_SEEK_START,219zcp->zevent_fd) < 0)220zed_log_msg(LOG_WARNING, "Failed to seek to eid=0");221else222eid = 0;223}224zed_log_msg(LOG_NOTICE, "Processing events since eid=%llu", eid);225return (found ? 0 : -1);226}227228/*229* Return non-zero if nvpair [name] should be formatted in hex; o/w, return 0.230*/231static int232_zed_event_value_is_hex(const char *name)233{234const char *hex_suffix[] = {235"_guid",236"_guids",237NULL238};239const char **pp;240char *p;241242if (!name)243return (0);244245for (pp = hex_suffix; *pp; pp++) {246p = strstr(name, *pp);247if (p && strlen(p) == strlen(*pp))248return (1);249}250return (0);251}252253/*254* Add an environment variable for [eid] to the container [zsp].255*256* The variable name is the concatenation of [prefix] and [name] converted to257* uppercase with non-alphanumeric characters converted to underscores;258* [prefix] is optional, and [name] must begin with an alphabetic character.259* If the converted variable name already exists within the container [zsp],260* its existing value will be replaced with the new value.261*262* The variable value is specified by the format string [fmt].263*264* Returns 0 on success, and -1 on error (with errno set).265*266* All environment variables in [zsp] should be added through this function.267*/268static __attribute__((format(printf, 5, 6))) int269_zed_event_add_var(uint64_t eid, zed_strings_t *zsp,270const char *prefix, const char *name, const char *fmt, ...)271{272char keybuf[MAXBUF];273char valbuf[MAXBUF];274char *dstp;275const char *srcp;276const char *lastp;277int n;278int buflen;279va_list vargs;280281assert(zsp != NULL);282assert(fmt != NULL);283284if (!name) {285errno = EINVAL;286zed_log_msg(LOG_WARNING,287"Failed to add variable for eid=%llu: Name is empty", eid);288return (-1);289} else if (!isalpha(name[0])) {290errno = EINVAL;291zed_log_msg(LOG_WARNING,292"Failed to add variable for eid=%llu: "293"Name \"%s\" is invalid", eid, name);294return (-1);295}296/*297* Construct the string key by converting PREFIX (if present) and NAME.298*/299dstp = keybuf;300lastp = keybuf + sizeof (keybuf);301if (prefix) {302for (srcp = prefix; *srcp && (dstp < lastp); srcp++)303*dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_';304}305for (srcp = name; *srcp && (dstp < lastp); srcp++)306*dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_';307308if (dstp == lastp) {309errno = ENAMETOOLONG;310zed_log_msg(LOG_WARNING,311"Failed to add variable for eid=%llu: Name too long", eid);312return (-1);313}314*dstp = '\0';315/*316* Construct the string specified by "[PREFIX][NAME]=[FMT]".317*/318dstp = valbuf;319buflen = sizeof (valbuf);320n = strlcpy(dstp, keybuf, buflen);321if (n >= sizeof (valbuf)) {322errno = EMSGSIZE;323zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",324keybuf, eid, "Exceeded buffer size");325return (-1);326}327dstp += n;328buflen -= n;329330*dstp++ = '=';331buflen--;332333if (buflen <= 0) {334errno = EMSGSIZE;335zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",336keybuf, eid, "Exceeded buffer size");337return (-1);338}339340va_start(vargs, fmt);341n = vsnprintf(dstp, buflen, fmt, vargs);342va_end(vargs);343344if ((n < 0) || (n >= buflen)) {345errno = EMSGSIZE;346zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",347keybuf, eid, "Exceeded buffer size");348return (-1);349} else if (zed_strings_add(zsp, keybuf, valbuf) < 0) {350zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",351keybuf, eid, strerror(errno));352return (-1);353}354return (0);355}356357static int358_zed_event_add_array_err(uint64_t eid, const char *name)359{360errno = EMSGSIZE;361zed_log_msg(LOG_WARNING,362"Failed to convert nvpair \"%s\" for eid=%llu: "363"Exceeded buffer size", name, eid);364return (-1);365}366367static int368_zed_event_add_int8_array(uint64_t eid, zed_strings_t *zsp,369const char *prefix, nvpair_t *nvp)370{371char buf[MAXBUF];372int buflen = sizeof (buf);373const char *name;374int8_t *i8p;375uint_t nelem;376uint_t i;377char *p;378int n;379380assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT8_ARRAY));381382name = nvpair_name(nvp);383(void) nvpair_value_int8_array(nvp, &i8p, &nelem);384for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {385n = snprintf(p, buflen, "%d ", i8p[i]);386if ((n < 0) || (n >= buflen))387return (_zed_event_add_array_err(eid, name));388p += n;389buflen -= n;390}391if (nelem > 0)392*--p = '\0';393394return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));395}396397static int398_zed_event_add_uint8_array(uint64_t eid, zed_strings_t *zsp,399const char *prefix, nvpair_t *nvp)400{401char buf[MAXBUF];402int buflen = sizeof (buf);403const char *name;404uint8_t *u8p;405uint_t nelem;406uint_t i;407char *p;408int n;409410assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT8_ARRAY));411412name = nvpair_name(nvp);413(void) nvpair_value_uint8_array(nvp, &u8p, &nelem);414for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {415n = snprintf(p, buflen, "%u ", u8p[i]);416if ((n < 0) || (n >= buflen))417return (_zed_event_add_array_err(eid, name));418p += n;419buflen -= n;420}421if (nelem > 0)422*--p = '\0';423424return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));425}426427static int428_zed_event_add_int16_array(uint64_t eid, zed_strings_t *zsp,429const char *prefix, nvpair_t *nvp)430{431char buf[MAXBUF];432int buflen = sizeof (buf);433const char *name;434int16_t *i16p;435uint_t nelem;436uint_t i;437char *p;438int n;439440assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT16_ARRAY));441442name = nvpair_name(nvp);443(void) nvpair_value_int16_array(nvp, &i16p, &nelem);444for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {445n = snprintf(p, buflen, "%d ", i16p[i]);446if ((n < 0) || (n >= buflen))447return (_zed_event_add_array_err(eid, name));448p += n;449buflen -= n;450}451if (nelem > 0)452*--p = '\0';453454return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));455}456457static int458_zed_event_add_uint16_array(uint64_t eid, zed_strings_t *zsp,459const char *prefix, nvpair_t *nvp)460{461char buf[MAXBUF];462int buflen = sizeof (buf);463const char *name;464uint16_t *u16p;465uint_t nelem;466uint_t i;467char *p;468int n;469470assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT16_ARRAY));471472name = nvpair_name(nvp);473(void) nvpair_value_uint16_array(nvp, &u16p, &nelem);474for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {475n = snprintf(p, buflen, "%u ", u16p[i]);476if ((n < 0) || (n >= buflen))477return (_zed_event_add_array_err(eid, name));478p += n;479buflen -= n;480}481if (nelem > 0)482*--p = '\0';483484return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));485}486487static int488_zed_event_add_int32_array(uint64_t eid, zed_strings_t *zsp,489const char *prefix, nvpair_t *nvp)490{491char buf[MAXBUF];492int buflen = sizeof (buf);493const char *name;494int32_t *i32p;495uint_t nelem;496uint_t i;497char *p;498int n;499500assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT32_ARRAY));501502name = nvpair_name(nvp);503(void) nvpair_value_int32_array(nvp, &i32p, &nelem);504for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {505n = snprintf(p, buflen, "%d ", i32p[i]);506if ((n < 0) || (n >= buflen))507return (_zed_event_add_array_err(eid, name));508p += n;509buflen -= n;510}511if (nelem > 0)512*--p = '\0';513514return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));515}516517static int518_zed_event_add_uint32_array(uint64_t eid, zed_strings_t *zsp,519const char *prefix, nvpair_t *nvp)520{521char buf[MAXBUF];522int buflen = sizeof (buf);523const char *name;524uint32_t *u32p;525uint_t nelem;526uint_t i;527char *p;528int n;529530assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT32_ARRAY));531532name = nvpair_name(nvp);533(void) nvpair_value_uint32_array(nvp, &u32p, &nelem);534for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {535n = snprintf(p, buflen, "%u ", u32p[i]);536if ((n < 0) || (n >= buflen))537return (_zed_event_add_array_err(eid, name));538p += n;539buflen -= n;540}541if (nelem > 0)542*--p = '\0';543544return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));545}546547static int548_zed_event_add_int64_array(uint64_t eid, zed_strings_t *zsp,549const char *prefix, nvpair_t *nvp)550{551char buf[MAXBUF];552int buflen = sizeof (buf);553const char *name;554int64_t *i64p;555uint_t nelem;556uint_t i;557char *p;558int n;559560assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT64_ARRAY));561562name = nvpair_name(nvp);563(void) nvpair_value_int64_array(nvp, &i64p, &nelem);564for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {565n = snprintf(p, buflen, "%lld ", (u_longlong_t)i64p[i]);566if ((n < 0) || (n >= buflen))567return (_zed_event_add_array_err(eid, name));568p += n;569buflen -= n;570}571if (nelem > 0)572*--p = '\0';573574return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));575}576577static int578_zed_event_add_uint64_array(uint64_t eid, zed_strings_t *zsp,579const char *prefix, nvpair_t *nvp)580{581char buf[MAXBUF];582int buflen = sizeof (buf);583const char *name;584const char *fmt;585uint64_t *u64p;586uint_t nelem;587uint_t i;588char *p;589int n;590591assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY));592593name = nvpair_name(nvp);594fmt = _zed_event_value_is_hex(name) ? "0x%.16llX " : "%llu ";595(void) nvpair_value_uint64_array(nvp, &u64p, &nelem);596for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {597n = snprintf(p, buflen, fmt, (u_longlong_t)u64p[i]);598if ((n < 0) || (n >= buflen))599return (_zed_event_add_array_err(eid, name));600p += n;601buflen -= n;602}603if (nelem > 0)604*--p = '\0';605606return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));607}608609static int610_zed_event_add_string_array(uint64_t eid, zed_strings_t *zsp,611const char *prefix, nvpair_t *nvp)612{613char buf[MAXBUF];614int buflen = sizeof (buf);615const char *name;616const char **strp;617uint_t nelem;618uint_t i;619char *p;620int n;621622assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY));623624name = nvpair_name(nvp);625(void) nvpair_value_string_array(nvp, &strp, &nelem);626for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {627n = snprintf(p, buflen, "%s ", strp[i] ? strp[i] : "<NULL>");628if ((n < 0) || (n >= buflen))629return (_zed_event_add_array_err(eid, name));630p += n;631buflen -= n;632}633if (nelem > 0)634*--p = '\0';635636return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));637}638639/*640* Convert the nvpair [nvp] to a string which is added to the environment641* of the child process.642* Return 0 on success, -1 on error.643*/644static void645_zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp)646{647const char *name;648data_type_t type;649const char *prefix = ZEVENT_VAR_PREFIX;650boolean_t b;651double d;652uint8_t i8;653uint16_t i16;654uint32_t i32;655uint64_t i64;656const char *str;657658assert(zsp != NULL);659assert(nvp != NULL);660661name = nvpair_name(nvp);662type = nvpair_type(nvp);663664switch (type) {665case DATA_TYPE_BOOLEAN:666_zed_event_add_var(eid, zsp, prefix, name, "%s", "1");667break;668case DATA_TYPE_BOOLEAN_VALUE:669(void) nvpair_value_boolean_value(nvp, &b);670_zed_event_add_var(eid, zsp, prefix, name, "%s", b ? "1" : "0");671break;672case DATA_TYPE_BYTE:673(void) nvpair_value_byte(nvp, &i8);674_zed_event_add_var(eid, zsp, prefix, name, "%d", i8);675break;676case DATA_TYPE_INT8:677(void) nvpair_value_int8(nvp, (int8_t *)&i8);678_zed_event_add_var(eid, zsp, prefix, name, "%d", i8);679break;680case DATA_TYPE_UINT8:681(void) nvpair_value_uint8(nvp, &i8);682_zed_event_add_var(eid, zsp, prefix, name, "%u", i8);683break;684case DATA_TYPE_INT16:685(void) nvpair_value_int16(nvp, (int16_t *)&i16);686_zed_event_add_var(eid, zsp, prefix, name, "%d", i16);687break;688case DATA_TYPE_UINT16:689(void) nvpair_value_uint16(nvp, &i16);690_zed_event_add_var(eid, zsp, prefix, name, "%u", i16);691break;692case DATA_TYPE_INT32:693(void) nvpair_value_int32(nvp, (int32_t *)&i32);694_zed_event_add_var(eid, zsp, prefix, name, "%d", i32);695break;696case DATA_TYPE_UINT32:697(void) nvpair_value_uint32(nvp, &i32);698_zed_event_add_var(eid, zsp, prefix, name, "%u", i32);699break;700case DATA_TYPE_INT64:701(void) nvpair_value_int64(nvp, (int64_t *)&i64);702_zed_event_add_var(eid, zsp, prefix, name,703"%lld", (longlong_t)i64);704break;705case DATA_TYPE_UINT64:706(void) nvpair_value_uint64(nvp, &i64);707_zed_event_add_var(eid, zsp, prefix, name,708(_zed_event_value_is_hex(name) ? "0x%.16llX" : "%llu"),709(u_longlong_t)i64);710/*711* shadow readable strings for vdev state pairs712*/713if (strcmp(name, FM_EREPORT_PAYLOAD_ZFS_VDEV_STATE) == 0 ||714strcmp(name, FM_EREPORT_PAYLOAD_ZFS_VDEV_LASTSTATE) == 0) {715char alt[32];716717(void) snprintf(alt, sizeof (alt), "%s_str", name);718_zed_event_add_var(eid, zsp, prefix, alt, "%s",719zpool_state_to_name(i64, VDEV_AUX_NONE));720} else721/*722* shadow readable strings for pool state723*/724if (strcmp(name, FM_EREPORT_PAYLOAD_ZFS_POOL_STATE) == 0) {725char alt[32];726727(void) snprintf(alt, sizeof (alt), "%s_str", name);728_zed_event_add_var(eid, zsp, prefix, alt, "%s",729zpool_pool_state_to_name(i64));730}731break;732case DATA_TYPE_DOUBLE:733(void) nvpair_value_double(nvp, &d);734_zed_event_add_var(eid, zsp, prefix, name, "%g", d);735break;736case DATA_TYPE_HRTIME:737(void) nvpair_value_hrtime(nvp, (hrtime_t *)&i64);738_zed_event_add_var(eid, zsp, prefix, name,739"%llu", (u_longlong_t)i64);740break;741case DATA_TYPE_STRING:742(void) nvpair_value_string(nvp, &str);743_zed_event_add_var(eid, zsp, prefix, name,744"%s", (str ? str : "<NULL>"));745break;746case DATA_TYPE_INT8_ARRAY:747_zed_event_add_int8_array(eid, zsp, prefix, nvp);748break;749case DATA_TYPE_UINT8_ARRAY:750_zed_event_add_uint8_array(eid, zsp, prefix, nvp);751break;752case DATA_TYPE_INT16_ARRAY:753_zed_event_add_int16_array(eid, zsp, prefix, nvp);754break;755case DATA_TYPE_UINT16_ARRAY:756_zed_event_add_uint16_array(eid, zsp, prefix, nvp);757break;758case DATA_TYPE_INT32_ARRAY:759_zed_event_add_int32_array(eid, zsp, prefix, nvp);760break;761case DATA_TYPE_UINT32_ARRAY:762_zed_event_add_uint32_array(eid, zsp, prefix, nvp);763break;764case DATA_TYPE_INT64_ARRAY:765_zed_event_add_int64_array(eid, zsp, prefix, nvp);766break;767case DATA_TYPE_UINT64_ARRAY:768_zed_event_add_uint64_array(eid, zsp, prefix, nvp);769break;770case DATA_TYPE_STRING_ARRAY:771_zed_event_add_string_array(eid, zsp, prefix, nvp);772break;773case DATA_TYPE_NVLIST:774case DATA_TYPE_BOOLEAN_ARRAY:775case DATA_TYPE_BYTE_ARRAY:776case DATA_TYPE_NVLIST_ARRAY:777_zed_event_add_var(eid, zsp, prefix, name, "_NOT_IMPLEMENTED_");778break;779default:780errno = EINVAL;781zed_log_msg(LOG_WARNING,782"Failed to convert nvpair \"%s\" for eid=%llu: "783"Unrecognized type=%u", name, eid, (unsigned int) type);784break;785}786}787788/*789* Restrict various environment variables to safe and sane values790* when constructing the environment for the child process, unless791* we're running with a custom $PATH (like under the ZFS test suite).792*793* Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1.794*/795static void796_zed_event_add_env_restrict(uint64_t eid, zed_strings_t *zsp,797const char *path)798{799const char *env_restrict[][2] = {800{ "IFS", " \t\n" },801{ "PATH", _PATH_STDPATH },802{ "ZDB", SBINDIR "/zdb" },803{ "ZED", SBINDIR "/zed" },804{ "ZFS", SBINDIR "/zfs" },805{ "ZINJECT", SBINDIR "/zinject" },806{ "ZPOOL", SBINDIR "/zpool" },807{ "ZFS_ALIAS", ZFS_META_ALIAS },808{ "ZFS_VERSION", ZFS_META_VERSION },809{ "ZFS_RELEASE", ZFS_META_RELEASE },810{ NULL, NULL }811};812813/*814* If we have a custom $PATH, use the default ZFS binary locations815* instead of the hard-coded ones.816*/817const char *env_path[][2] = {818{ "IFS", " \t\n" },819{ "PATH", NULL }, /* $PATH copied in later on */820{ "ZDB", "zdb" },821{ "ZED", "zed" },822{ "ZFS", "zfs" },823{ "ZINJECT", "zinject" },824{ "ZPOOL", "zpool" },825{ "ZFS_ALIAS", ZFS_META_ALIAS },826{ "ZFS_VERSION", ZFS_META_VERSION },827{ "ZFS_RELEASE", ZFS_META_RELEASE },828{ NULL, NULL }829};830const char *(*pa)[2];831832assert(zsp != NULL);833834pa = path != NULL ? env_path : env_restrict;835836for (; *(*pa); pa++) {837/* Use our custom $PATH if we have one */838if (path != NULL && strcmp((*pa)[0], "PATH") == 0)839(*pa)[1] = path;840841_zed_event_add_var(eid, zsp, NULL, (*pa)[0], "%s", (*pa)[1]);842}843}844845/*846* Preserve specified variables from the parent environment847* when constructing the environment for the child process.848*849* Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1.850*/851static void852_zed_event_add_env_preserve(uint64_t eid, zed_strings_t *zsp)853{854const char *env_preserve[] = {855"TZ",856NULL857};858const char **keyp;859const char *val;860861assert(zsp != NULL);862863for (keyp = env_preserve; *keyp; keyp++) {864if ((val = getenv(*keyp)))865_zed_event_add_var(eid, zsp, NULL, *keyp, "%s", val);866}867}868869/*870* Compute the "subclass" by removing the first 3 components of [class]871* (which will always be of the form "*.fs.zfs"). Return a pointer inside872* the string [class], or NULL if insufficient components exist.873*/874static const char *875_zed_event_get_subclass(const char *class)876{877const char *p;878int i;879880if (!class)881return (NULL);882883p = class;884for (i = 0; i < 3; i++) {885p = strchr(p, '.');886if (!p)887break;888p++;889}890return (p);891}892893/*894* Convert the zevent time from a 2-element array of 64b integers895* into a more convenient form:896* - TIME_SECS is the second component of the time.897* - TIME_NSECS is the nanosecond component of the time.898* - TIME_STRING is an almost-RFC3339-compliant string representation.899*/900static void901_zed_event_add_time_strings(uint64_t eid, zed_strings_t *zsp, int64_t etime[])902{903struct tm stp;904char buf[32];905906assert(zsp != NULL);907assert(etime != NULL);908909_zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_SECS",910"%" PRId64, etime[0]);911_zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_NSECS",912"%" PRId64, etime[1]);913914if (!localtime_r((const time_t *) &etime[0], &stp)) {915zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",916ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "localtime error");917} else if (!strftime(buf, sizeof (buf), "%Y-%m-%d %H:%M:%S%z", &stp)) {918zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",919ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "strftime error");920} else {921_zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_STRING",922"%s", buf);923}924}925926927static void928_zed_event_update_enc_sysfs_path(nvlist_t *nvl)929{930const char *vdev_path;931932if (nvlist_lookup_string(nvl, FM_EREPORT_PAYLOAD_ZFS_VDEV_PATH,933&vdev_path) != 0) {934return; /* some other kind of event, ignore it */935}936937if (vdev_path == NULL) {938return;939}940941update_vdev_config_dev_sysfs_path(nvl, vdev_path,942FM_EREPORT_PAYLOAD_ZFS_VDEV_ENC_SYSFS_PATH);943}944945/*946* Service the next zevent, blocking until one is available.947*/948int949zed_event_service(struct zed_conf *zcp)950{951nvlist_t *nvl;952nvpair_t *nvp;953int n_dropped;954zed_strings_t *zsp;955uint64_t eid;956int64_t *etime;957uint_t nelem;958const char *class;959const char *subclass;960int rv;961962if (!zcp) {963errno = EINVAL;964zed_log_msg(LOG_ERR, "Failed to service zevent: %s",965strerror(errno));966return (EINVAL);967}968rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONE,969zcp->zevent_fd);970971if ((rv != 0) || !nvl)972return (errno);973974if (n_dropped > 0) {975zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);976_bump_event_queue_length();977}978if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {979zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");980} else if (nvlist_lookup_int64_array(981nvl, "time", &etime, &nelem) != 0) {982zed_log_msg(LOG_WARNING,983"Failed to lookup zevent time (eid=%llu)", eid);984} else if (nelem != 2) {985zed_log_msg(LOG_WARNING,986"Failed to lookup zevent time (eid=%llu, nelem=%u)",987eid, nelem);988} else if (nvlist_lookup_string(nvl, "class", &class) != 0) {989zed_log_msg(LOG_WARNING,990"Failed to lookup zevent class (eid=%llu)", eid);991} else {992/*993* Special case: If we can dynamically detect an enclosure sysfs994* path, then use that value rather than the one stored in the995* vd->vdev_enc_sysfs_path. There have been rare cases where996* vd->vdev_enc_sysfs_path becomes outdated. However, there997* will be other times when we can not dynamically detect the998* sysfs path (like if a disk disappears) and have to rely on999* the old value for things like turning on the fault LED.1000*/1001_zed_event_update_enc_sysfs_path(nvl);10021003/* let internal modules see this event first */1004zfs_agent_post_event(class, NULL, nvl);10051006zsp = zed_strings_create();10071008nvp = NULL;1009while ((nvp = nvlist_next_nvpair(nvl, nvp)))1010_zed_event_add_nvpair(eid, zsp, nvp);10111012_zed_event_add_env_restrict(eid, zsp, zcp->path);1013_zed_event_add_env_preserve(eid, zsp);10141015_zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "PID",1016"%d", (int)getpid());1017_zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "ZEDLET_DIR",1018"%s", zcp->zedlet_dir);1019subclass = _zed_event_get_subclass(class);1020_zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "SUBCLASS",1021"%s", (subclass ? subclass : class));10221023_zed_event_add_time_strings(eid, zsp, etime);10241025zed_exec_process(eid, class, subclass, zcp, zsp);10261027zed_conf_write_state(zcp, eid, etime);10281029zed_strings_destroy(zsp);1030}1031nvlist_free(nvl);1032return (0);1033}103410351036