138363Swpaul/*
238363Swpaul * This file is part of the ZFS Event Daemon (ZED).
338363Swpaul *
438363Swpaul * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
538363Swpaul * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
638363Swpaul * Refer to the OpenZFS git commit log for authoritative copyright attribution.
738363Swpaul *
838363Swpaul * The contents of this file are subject to the terms of the
938363Swpaul * Common Development and Distribution License Version 1.0 (CDDL-1.0).
1038363Swpaul * You can obtain a copy of the license from the top-level file
1138363Swpaul * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
1238363Swpaul * You may not use this file except in compliance with the license.
1338363Swpaul */
1438363Swpaul
1538363Swpaul#include <ctype.h>
1638363Swpaul#include <errno.h>
1738363Swpaul#include <fcntl.h>
1838363Swpaul#include <libzfs_core.h>
1938363Swpaul#include <paths.h>
2038363Swpaul#include <stdarg.h>
2138363Swpaul#include <stdio.h>
2238363Swpaul#include <stdlib.h>
2338363Swpaul#include <string.h>
2438363Swpaul#include <sys/zfs_ioctl.h>
2538363Swpaul#include <time.h>
2638363Swpaul#include <unistd.h>
2738363Swpaul#include <sys/fm/fs/zfs.h>
2838363Swpaul#include "zed.h"
2938363Swpaul#include "zed_conf.h"
3038363Swpaul#include "zed_disk_event.h"
3138363Swpaul#include "zed_event.h"
3246514Swpaul#include "zed_exec.h"
3338363Swpaul#include "zed_file.h"
3438363Swpaul#include "zed_log.h"
3538363Swpaul#include "zed_strings.h"
3638363Swpaul
3738363Swpaul#include "agents/zfs_agents.h"
3838363Swpaul#include <libzutil.h>
3938363Swpaul
4038363Swpaul#define	MAXBUF	4096
4138363Swpaul
4238363Swpaulstatic int max_zevent_buf_len = 1 << 20;
4338363Swpaul
4438363Swpaul/*
4538363Swpaul * Open the libzfs interface.
4638363Swpaul */
4738363Swpaulint
4838363Swpaulzed_event_init(struct zed_conf *zcp)
4938363Swpaul{
5038363Swpaul	if (!zcp)
5138363Swpaul		zed_log_die("Failed zed_event_init: %s", strerror(EINVAL));
5238363Swpaul
5338363Swpaul	zcp->zfs_hdl = libzfs_init();
5438363Swpaul	if (!zcp->zfs_hdl) {
5538363Swpaul		if (zcp->do_idle)
5638363Swpaul			return (-1);
5738363Swpaul		zed_log_die("Failed to initialize libzfs");
5838363Swpaul	}
5938363Swpaul
6038363Swpaul	zcp->zevent_fd = open(ZFS_DEV, O_RDWR | O_CLOEXEC);
6138363Swpaul	if (zcp->zevent_fd < 0) {
6238363Swpaul		if (zcp->do_idle)
6338363Swpaul			return (-1);
6438363Swpaul		zed_log_die("Failed to open \"%s\": %s",
6538363Swpaul		    ZFS_DEV, strerror(errno));
6638363Swpaul	}
6738363Swpaul
6838363Swpaul	zfs_agent_init(zcp->zfs_hdl);
6938363Swpaul
7038363Swpaul	if (zed_disk_event_init() != 0) {
7138363Swpaul		if (zcp->do_idle)
7238363Swpaul			return (-1);
7338363Swpaul		zed_log_die("Failed to initialize disk events");
7438363Swpaul	}
7538363Swpaul
7638363Swpaul	if (zcp->max_zevent_buf_len != 0)
7738363Swpaul		max_zevent_buf_len = zcp->max_zevent_buf_len;
7838363Swpaul
7938363Swpaul	return (0);
8038363Swpaul}
8138363Swpaul
8238363Swpaul/*
8338363Swpaul * Close the libzfs interface.
8438363Swpaul */
8538363Swpaulvoid
8638363Swpaulzed_event_fini(struct zed_conf *zcp)
8738363Swpaul{
8838363Swpaul	if (!zcp)
8938363Swpaul		zed_log_die("Failed zed_event_fini: %s", strerror(EINVAL));
9038363Swpaul
9138363Swpaul	zed_disk_event_fini();
9238363Swpaul	zfs_agent_fini();
9338363Swpaul
9438363Swpaul	if (zcp->zevent_fd >= 0) {
9538363Swpaul		if (close(zcp->zevent_fd) < 0)
9638363Swpaul			zed_log_msg(LOG_WARNING, "Failed to close \"%s\": %s",
9738363Swpaul			    ZFS_DEV, strerror(errno));
9838363Swpaul
9938363Swpaul		zcp->zevent_fd = -1;
10038363Swpaul	}
10138363Swpaul	if (zcp->zfs_hdl) {
10238363Swpaul		libzfs_fini(zcp->zfs_hdl);
10338363Swpaul		zcp->zfs_hdl = NULL;
10438363Swpaul	}
10538363Swpaul
10638363Swpaul	zed_exec_fini();
10738363Swpaul}
10838363Swpaul
10938363Swpaulstatic void
11038363Swpaul_bump_event_queue_length(void)
11138363Swpaul{
11238363Swpaul	int zzlm = -1, wr;
11338363Swpaul	char qlen_buf[12] = {0}; /* parameter is int => max "-2147483647\n" */
11438363Swpaul	long int qlen, orig_qlen;
11538363Swpaul
11638363Swpaul	zzlm = open("/sys/module/zfs/parameters/zfs_zevent_len_max", O_RDWR);
11738363Swpaul	if (zzlm < 0)
11838363Swpaul		goto done;
11938363Swpaul
12038363Swpaul	if (read(zzlm, qlen_buf, sizeof (qlen_buf)) < 0)
12138363Swpaul		goto done;
12238363Swpaul	qlen_buf[sizeof (qlen_buf) - 1] = '\0';
12338363Swpaul
12438363Swpaul	errno = 0;
12538363Swpaul	orig_qlen = qlen = strtol(qlen_buf, NULL, 10);
12638363Swpaul	if (errno == ERANGE)
12738363Swpaul		goto done;
12838363Swpaul
12938363Swpaul	if (qlen <= 0)
13038363Swpaul		qlen = 512; /* default zfs_zevent_len_max value */
13138363Swpaul	else
13238363Swpaul		qlen *= 2;
13338363Swpaul
13438363Swpaul	/*
13538363Swpaul	 * Don't consume all of kernel memory with event logs if something
13638363Swpaul	 * goes wrong.
13738363Swpaul	 */
13838363Swpaul	if (qlen > max_zevent_buf_len)
13938363Swpaul		qlen = max_zevent_buf_len;
14038363Swpaul	if (qlen == orig_qlen)
14138363Swpaul		goto done;
14238363Swpaul	wr = snprintf(qlen_buf, sizeof (qlen_buf), "%ld", qlen);
14338363Swpaul	if (wr >= sizeof (qlen_buf)) {
14438363Swpaul		wr = sizeof (qlen_buf) - 1;
14538363Swpaul		zed_log_msg(LOG_WARNING, "Truncation in %s()", __func__);
14638363Swpaul	}
14738363Swpaul
14838363Swpaul	if (pwrite(zzlm, qlen_buf, wr + 1, 0) < 0)
14938363Swpaul		goto done;
15038363Swpaul
15138363Swpaul	zed_log_msg(LOG_WARNING, "Bumping queue length to %ld", qlen);
15238363Swpaul
15338363Swpauldone:
15438363Swpaul	if (zzlm > -1)
15538363Swpaul		(void) close(zzlm);
15638363Swpaul}
15738363Swpaul
15838363Swpaul/*
15938363Swpaul * Seek to the event specified by [saved_eid] and [saved_etime].
16038363Swpaul * This protects against processing a given event more than once.
16138363Swpaul * Return 0 upon a successful seek to the specified event, or -1 otherwise.
16238363Swpaul *
16338363Swpaul * A zevent is considered to be uniquely specified by its (eid,time) tuple.
16438363Swpaul * The unsigned 64b eid is set to 1 when the kernel module is loaded, and
16538363Swpaul * incremented by 1 for each new event.  Since the state file can persist
16638363Swpaul * across a kernel module reload, the time must be checked to ensure a match.
16738363Swpaul */
16838363Swpaulint
16938363Swpaulzed_event_seek(struct zed_conf *zcp, uint64_t saved_eid, int64_t saved_etime[])
17038363Swpaul{
17138363Swpaul	uint64_t eid;
17238363Swpaul	int found;
17338363Swpaul	nvlist_t *nvl;
17438526Swpaul	int n_dropped;
17538526Swpaul	int64_t *etime;
17638526Swpaul	uint_t nelem;
17738526Swpaul	int rv;
17838526Swpaul
17938526Swpaul	if (!zcp) {
18038526Swpaul		errno = EINVAL;
18138363Swpaul		zed_log_msg(LOG_ERR, "Failed to seek zevent: %s",
18238363Swpaul		    strerror(errno));
18338363Swpaul		return (-1);
18438363Swpaul	}
18538363Swpaul	eid = 0;
18638363Swpaul	found = 0;
18738363Swpaul	while ((eid < saved_eid) && !found) {
18838363Swpaul		rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped,
18938363Swpaul		    ZEVENT_NONBLOCK, zcp->zevent_fd);
19038363Swpaul
19138363Swpaul		if ((rv != 0) || !nvl)
19238363Swpaul			break;
19338363Swpaul
19438363Swpaul		if (n_dropped > 0) {
19538363Swpaul			zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
19638363Swpaul			_bump_event_queue_length();
19738363Swpaul		}
19838363Swpaul		if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
19938363Swpaul			zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
20038363Swpaul		} else if (nvlist_lookup_int64_array(nvl, "time",
20138363Swpaul		    &etime, &nelem) != 0) {
20238363Swpaul			zed_log_msg(LOG_WARNING,
20338363Swpaul			    "Failed to lookup zevent time (eid=%llu)", eid);
20438363Swpaul		} else if (nelem != 2) {
20538363Swpaul			zed_log_msg(LOG_WARNING,
20638363Swpaul			    "Failed to lookup zevent time (eid=%llu, nelem=%u)",
20738363Swpaul			    eid, nelem);
20838363Swpaul		} else if ((eid != saved_eid) ||
20938363Swpaul		    (etime[0] != saved_etime[0]) ||
21038363Swpaul		    (etime[1] != saved_etime[1])) {
21138363Swpaul			/* no-op */
21238363Swpaul		} else {
21338363Swpaul			found = 1;
21438363Swpaul		}
21538363Swpaul		free(nvl);
21638363Swpaul	}
21738363Swpaul	if (!found && (saved_eid > 0)) {
21838363Swpaul		if (zpool_events_seek(zcp->zfs_hdl, ZEVENT_SEEK_START,
21938363Swpaul		    zcp->zevent_fd) < 0)
22038363Swpaul			zed_log_msg(LOG_WARNING, "Failed to seek to eid=0");
22138363Swpaul		else
22238363Swpaul			eid = 0;
22338363Swpaul	}
22438363Swpaul	zed_log_msg(LOG_NOTICE, "Processing events since eid=%llu", eid);
22538363Swpaul	return (found ? 0 : -1);
22638363Swpaul}
22738363Swpaul
22838363Swpaul/*
22938363Swpaul * Return non-zero if nvpair [name] should be formatted in hex; o/w, return 0.
23038363Swpaul */
23138363Swpaulstatic int
23238363Swpaul_zed_event_value_is_hex(const char *name)
23338363Swpaul{
23438363Swpaul	const char *hex_suffix[] = {
23538363Swpaul		"_guid",
23638363Swpaul		"_guids",
23738363Swpaul		NULL
23838363Swpaul	};
23938363Swpaul	const char **pp;
24038363Swpaul	char *p;
24138363Swpaul
24238363Swpaul	if (!name)
24338363Swpaul		return (0);
24438363Swpaul
24538363Swpaul	for (pp = hex_suffix; *pp; pp++) {
24638363Swpaul		p = strstr(name, *pp);
24738363Swpaul		if (p && strlen(p) == strlen(*pp))
24838363Swpaul			return (1);
24938363Swpaul	}
25038363Swpaul	return (0);
25138363Swpaul}
25238363Swpaul
25338363Swpaul/*
25438363Swpaul * Add an environment variable for [eid] to the container [zsp].
25538363Swpaul *
25638363Swpaul * The variable name is the concatenation of [prefix] and [name] converted to
25738363Swpaul * uppercase with non-alphanumeric characters converted to underscores;
25838363Swpaul * [prefix] is optional, and [name] must begin with an alphabetic character.
25938363Swpaul * If the converted variable name already exists within the container [zsp],
26038363Swpaul * its existing value will be replaced with the new value.
26138363Swpaul *
26238363Swpaul * The variable value is specified by the format string [fmt].
26338363Swpaul *
26438363Swpaul * Returns 0 on success, and -1 on error (with errno set).
26538363Swpaul *
26638363Swpaul * All environment variables in [zsp] should be added through this function.
26738363Swpaul */
26838363Swpaulstatic __attribute__((format(printf, 5, 6))) int
26938363Swpaul_zed_event_add_var(uint64_t eid, zed_strings_t *zsp,
27038363Swpaul    const char *prefix, const char *name, const char *fmt, ...)
27138363Swpaul{
27238363Swpaul	char keybuf[MAXBUF];
27338363Swpaul	char valbuf[MAXBUF];
27438363Swpaul	char *dstp;
27538363Swpaul	const char *srcp;
27638363Swpaul	const char *lastp;
27738363Swpaul	int n;
27838363Swpaul	int buflen;
27938363Swpaul	va_list vargs;
28038363Swpaul
28138363Swpaul	assert(zsp != NULL);
28238363Swpaul	assert(fmt != NULL);
28338363Swpaul
28438363Swpaul	if (!name) {
28538363Swpaul		errno = EINVAL;
28638363Swpaul		zed_log_msg(LOG_WARNING,
28738363Swpaul		    "Failed to add variable for eid=%llu: Name is empty", eid);
28838363Swpaul		return (-1);
28938363Swpaul	} else if (!isalpha(name[0])) {
29038363Swpaul		errno = EINVAL;
29138363Swpaul		zed_log_msg(LOG_WARNING,
29238363Swpaul		    "Failed to add variable for eid=%llu: "
29338363Swpaul		    "Name \"%s\" is invalid", eid, name);
29438363Swpaul		return (-1);
29538363Swpaul	}
29638363Swpaul	/*
29738363Swpaul	 * Construct the string key by converting PREFIX (if present) and NAME.
29838363Swpaul	 */
29938363Swpaul	dstp = keybuf;
30038363Swpaul	lastp = keybuf + sizeof (keybuf);
30138363Swpaul	if (prefix) {
30238363Swpaul		for (srcp = prefix; *srcp && (dstp < lastp); srcp++)
30338363Swpaul			*dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_';
30438363Swpaul	}
30538363Swpaul	for (srcp = name; *srcp && (dstp < lastp); srcp++)
30638363Swpaul		*dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_';
30738363Swpaul
30838363Swpaul	if (dstp == lastp) {
30938363Swpaul		errno = ENAMETOOLONG;
31038363Swpaul		zed_log_msg(LOG_WARNING,
31138363Swpaul		    "Failed to add variable for eid=%llu: Name too long", eid);
31238363Swpaul		return (-1);
31338363Swpaul	}
31438363Swpaul	*dstp = '\0';
31538363Swpaul	/*
31638363Swpaul	 * Construct the string specified by "[PREFIX][NAME]=[FMT]".
31738363Swpaul	 */
31838363Swpaul	dstp = valbuf;
31938363Swpaul	buflen = sizeof (valbuf);
32038363Swpaul	n = strlcpy(dstp, keybuf, buflen);
32138363Swpaul	if (n >= sizeof (valbuf)) {
32238363Swpaul		errno = EMSGSIZE;
32338363Swpaul		zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
32438363Swpaul		    keybuf, eid, "Exceeded buffer size");
32538363Swpaul		return (-1);
32638363Swpaul	}
32738363Swpaul	dstp += n;
32838363Swpaul	buflen -= n;
32938363Swpaul
33038363Swpaul	*dstp++ = '=';
33138363Swpaul	buflen--;
33238363Swpaul
33338363Swpaul	if (buflen <= 0) {
33438363Swpaul		errno = EMSGSIZE;
33538363Swpaul		zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
33638363Swpaul		    keybuf, eid, "Exceeded buffer size");
33738363Swpaul		return (-1);
33838363Swpaul	}
33938363Swpaul
34038363Swpaul	va_start(vargs, fmt);
34138363Swpaul	n = vsnprintf(dstp, buflen, fmt, vargs);
34238363Swpaul	va_end(vargs);
34338363Swpaul
34438363Swpaul	if ((n < 0) || (n >= buflen)) {
34538363Swpaul		errno = EMSGSIZE;
34638363Swpaul		zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
34738363Swpaul		    keybuf, eid, "Exceeded buffer size");
34838363Swpaul		return (-1);
34938363Swpaul	} else if (zed_strings_add(zsp, keybuf, valbuf) < 0) {
35038363Swpaul		zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
35138363Swpaul		    keybuf, eid, strerror(errno));
35238363Swpaul		return (-1);
35338363Swpaul	}
35438363Swpaul	return (0);
35538363Swpaul}
35638363Swpaul
35738363Swpaulstatic int
35838363Swpaul_zed_event_add_array_err(uint64_t eid, const char *name)
35938363Swpaul{
36038363Swpaul	errno = EMSGSIZE;
36138363Swpaul	zed_log_msg(LOG_WARNING,
36238363Swpaul	    "Failed to convert nvpair \"%s\" for eid=%llu: "
36338363Swpaul	    "Exceeded buffer size", name, eid);
36440588Swpaul	return (-1);
36538363Swpaul}
36638363Swpaul
36738363Swpaulstatic int
36838363Swpaul_zed_event_add_int8_array(uint64_t eid, zed_strings_t *zsp,
36938363Swpaul    const char *prefix, nvpair_t *nvp)
37038363Swpaul{
37138363Swpaul	char buf[MAXBUF];
37238363Swpaul	int buflen = sizeof (buf);
37338363Swpaul	const char *name;
37438363Swpaul	int8_t *i8p;
37538363Swpaul	uint_t nelem;
37638363Swpaul	uint_t i;
37738363Swpaul	char *p;
37838363Swpaul	int n;
37938363Swpaul
38038363Swpaul	assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT8_ARRAY));
38138363Swpaul
38238363Swpaul	name = nvpair_name(nvp);
38338363Swpaul	(void) nvpair_value_int8_array(nvp, &i8p, &nelem);
38438363Swpaul	for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
38538363Swpaul		n = snprintf(p, buflen, "%d ", i8p[i]);
38638363Swpaul		if ((n < 0) || (n >= buflen))
38738363Swpaul			return (_zed_event_add_array_err(eid, name));
38838363Swpaul		p += n;
38938363Swpaul		buflen -= n;
39038363Swpaul	}
39138363Swpaul	if (nelem > 0)
39238363Swpaul		*--p = '\0';
39338363Swpaul
39438363Swpaul	return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
39538363Swpaul}
39638363Swpaul
39738363Swpaulstatic int
39838363Swpaul_zed_event_add_uint8_array(uint64_t eid, zed_strings_t *zsp,
39938363Swpaul    const char *prefix, nvpair_t *nvp)
40038363Swpaul{
40138363Swpaul	char buf[MAXBUF];
40238363Swpaul	int buflen = sizeof (buf);
40338363Swpaul	const char *name;
40438363Swpaul	uint8_t *u8p;
40538363Swpaul	uint_t nelem;
40638363Swpaul	uint_t i;
40738363Swpaul	char *p;
40838363Swpaul	int n;
40938363Swpaul
41038363Swpaul	assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT8_ARRAY));
41138363Swpaul
41238363Swpaul	name = nvpair_name(nvp);
41338363Swpaul	(void) nvpair_value_uint8_array(nvp, &u8p, &nelem);
41438363Swpaul	for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
41538363Swpaul		n = snprintf(p, buflen, "%u ", u8p[i]);
41638363Swpaul		if ((n < 0) || (n >= buflen))
41738363Swpaul			return (_zed_event_add_array_err(eid, name));
41838363Swpaul		p += n;
41938363Swpaul		buflen -= n;
42038363Swpaul	}
42138363Swpaul	if (nelem > 0)
42238363Swpaul		*--p = '\0';
42338363Swpaul
42438363Swpaul	return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
42538363Swpaul}
42638363Swpaul
42738363Swpaulstatic int
42838363Swpaul_zed_event_add_int16_array(uint64_t eid, zed_strings_t *zsp,
42938363Swpaul    const char *prefix, nvpair_t *nvp)
43038363Swpaul{
43138363Swpaul	char buf[MAXBUF];
43238363Swpaul	int buflen = sizeof (buf);
43338363Swpaul	const char *name;
43438363Swpaul	int16_t *i16p;
43538363Swpaul	uint_t nelem;
43638363Swpaul	uint_t i;
43738363Swpaul	char *p;
43838363Swpaul	int n;
43938363Swpaul
44046204Swpaul	assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT16_ARRAY));
44146204Swpaul
44238363Swpaul	name = nvpair_name(nvp);
44338363Swpaul	(void) nvpair_value_int16_array(nvp, &i16p, &nelem);
44438363Swpaul	for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
44538363Swpaul		n = snprintf(p, buflen, "%d ", i16p[i]);
44638363Swpaul		if ((n < 0) || (n >= buflen))
44738363Swpaul			return (_zed_event_add_array_err(eid, name));
44838363Swpaul		p += n;
44938363Swpaul		buflen -= n;
45038363Swpaul	}
45138363Swpaul	if (nelem > 0)
45238363Swpaul		*--p = '\0';
45338363Swpaul
45438363Swpaul	return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
45538363Swpaul}
45638363Swpaul
45738363Swpaulstatic int
45838363Swpaul_zed_event_add_uint16_array(uint64_t eid, zed_strings_t *zsp,
45938363Swpaul    const char *prefix, nvpair_t *nvp)
46038363Swpaul{
46138363Swpaul	char buf[MAXBUF];
46238363Swpaul	int buflen = sizeof (buf);
46338363Swpaul	const char *name;
46438363Swpaul	uint16_t *u16p;
46538363Swpaul	uint_t nelem;
46638363Swpaul	uint_t i;
46738363Swpaul	char *p;
46838363Swpaul	int n;
46938363Swpaul
47038363Swpaul	assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT16_ARRAY));
47138363Swpaul
47238363Swpaul	name = nvpair_name(nvp);
47338363Swpaul	(void) nvpair_value_uint16_array(nvp, &u16p, &nelem);
47438363Swpaul	for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
47538363Swpaul		n = snprintf(p, buflen, "%u ", u16p[i]);
47638363Swpaul		if ((n < 0) || (n >= buflen))
47738363Swpaul			return (_zed_event_add_array_err(eid, name));
47838363Swpaul		p += n;
47938363Swpaul		buflen -= n;
48038363Swpaul	}
48138363Swpaul	if (nelem > 0)
48238363Swpaul		*--p = '\0';
48338363Swpaul
48438363Swpaul	return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
48538363Swpaul}
48638363Swpaul
48738363Swpaulstatic int
48838363Swpaul_zed_event_add_int32_array(uint64_t eid, zed_strings_t *zsp,
48938363Swpaul    const char *prefix, nvpair_t *nvp)
49038363Swpaul{
49138363Swpaul	char buf[MAXBUF];
49238363Swpaul	int buflen = sizeof (buf);
49338363Swpaul	const char *name;
49438363Swpaul	int32_t *i32p;
49538363Swpaul	uint_t nelem;
49638363Swpaul	uint_t i;
49738363Swpaul	char *p;
49838363Swpaul	int n;
49938363Swpaul
50038363Swpaul	assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT32_ARRAY));
50138363Swpaul
50238363Swpaul	name = nvpair_name(nvp);
50338363Swpaul	(void) nvpair_value_int32_array(nvp, &i32p, &nelem);
50438363Swpaul	for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
50538363Swpaul		n = snprintf(p, buflen, "%d ", i32p[i]);
50638363Swpaul		if ((n < 0) || (n >= buflen))
50738363Swpaul			return (_zed_event_add_array_err(eid, name));
50838363Swpaul		p += n;
50938363Swpaul		buflen -= n;
51038363Swpaul	}
51138363Swpaul	if (nelem > 0)
51238363Swpaul		*--p = '\0';
51338363Swpaul
51438363Swpaul	return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
51538363Swpaul}
51638363Swpaul
51738363Swpaulstatic int
51838363Swpaul_zed_event_add_uint32_array(uint64_t eid, zed_strings_t *zsp,
51938363Swpaul    const char *prefix, nvpair_t *nvp)
52038363Swpaul{
52138363Swpaul	char buf[MAXBUF];
52238363Swpaul	int buflen = sizeof (buf);
52338363Swpaul	const char *name;
52438363Swpaul	uint32_t *u32p;
52538363Swpaul	uint_t nelem;
52638363Swpaul	uint_t i;
52738363Swpaul	char *p;
52838363Swpaul	int n;
52938363Swpaul
53038363Swpaul	assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT32_ARRAY));
53138363Swpaul
53238363Swpaul	name = nvpair_name(nvp);
53338363Swpaul	(void) nvpair_value_uint32_array(nvp, &u32p, &nelem);
53438363Swpaul	for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
53538363Swpaul		n = snprintf(p, buflen, "%u ", u32p[i]);
53638363Swpaul		if ((n < 0) || (n >= buflen))
53738363Swpaul			return (_zed_event_add_array_err(eid, name));
53838363Swpaul		p += n;
53938363Swpaul		buflen -= n;
54038363Swpaul	}
54138363Swpaul	if (nelem > 0)
54245062Swpaul		*--p = '\0';
54345062Swpaul
54438363Swpaul	return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
54538363Swpaul}
54638363Swpaul
54738363Swpaulstatic int
54838363Swpaul_zed_event_add_int64_array(uint64_t eid, zed_strings_t *zsp,
54938363Swpaul    const char *prefix, nvpair_t *nvp)
55038363Swpaul{
55138363Swpaul	char buf[MAXBUF];
55238363Swpaul	int buflen = sizeof (buf);
55338363Swpaul	const char *name;
55438363Swpaul	int64_t *i64p;
55538363Swpaul	uint_t nelem;
55646514Swpaul	uint_t i;
55738363Swpaul	char *p;
55838363Swpaul	int n;
55938363Swpaul
56038363Swpaul	assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT64_ARRAY));
56138363Swpaul
56238363Swpaul	name = nvpair_name(nvp);
56338363Swpaul	(void) nvpair_value_int64_array(nvp, &i64p, &nelem);
56438363Swpaul	for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
56538363Swpaul		n = snprintf(p, buflen, "%lld ", (u_longlong_t)i64p[i]);
56638363Swpaul		if ((n < 0) || (n >= buflen))
56738363Swpaul			return (_zed_event_add_array_err(eid, name));
56838363Swpaul		p += n;
56938363Swpaul		buflen -= n;
57038363Swpaul	}
57138363Swpaul	if (nelem > 0)
57238363Swpaul		*--p = '\0';
57338363Swpaul
57438363Swpaul	return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
57538363Swpaul}
57638363Swpaul
57738363Swpaulstatic int
57838363Swpaul_zed_event_add_uint64_array(uint64_t eid, zed_strings_t *zsp,
57938363Swpaul    const char *prefix, nvpair_t *nvp)
58038363Swpaul{
58138363Swpaul	char buf[MAXBUF];
58238363Swpaul	int buflen = sizeof (buf);
58338363Swpaul	const char *name;
58438363Swpaul	const char *fmt;
58538363Swpaul	uint64_t *u64p;
58638363Swpaul	uint_t nelem;
58738363Swpaul	uint_t i;
58838363Swpaul	char *p;
58945062Swpaul	int n;
59038363Swpaul
59145062Swpaul	assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY));
59238363Swpaul
59345062Swpaul	name = nvpair_name(nvp);
59438363Swpaul	fmt = _zed_event_value_is_hex(name) ? "0x%.16llX " : "%llu ";
59545062Swpaul	(void) nvpair_value_uint64_array(nvp, &u64p, &nelem);
59645062Swpaul	for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
59745062Swpaul		n = snprintf(p, buflen, fmt, (u_longlong_t)u64p[i]);
59845062Swpaul		if ((n < 0) || (n >= buflen))
59945062Swpaul			return (_zed_event_add_array_err(eid, name));
60045062Swpaul		p += n;
60138363Swpaul		buflen -= n;
60238363Swpaul	}
60338363Swpaul	if (nelem > 0)
60438363Swpaul		*--p = '\0';
60538363Swpaul
60638363Swpaul	return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
60738363Swpaul}
60838363Swpaul
60938363Swpaulstatic int
61038363Swpaul_zed_event_add_string_array(uint64_t eid, zed_strings_t *zsp,
61138363Swpaul    const char *prefix, nvpair_t *nvp)
61238363Swpaul{
61338363Swpaul	char buf[MAXBUF];
61438363Swpaul	int buflen = sizeof (buf);
61538363Swpaul	const char *name;
61638363Swpaul	const char **strp;
61738363Swpaul	uint_t nelem;
61838363Swpaul	uint_t i;
61938363Swpaul	char *p;
62046204Swpaul	int n;
62146204Swpaul
62246204Swpaul	assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY));
62345693Swpaul
62446204Swpaul	name = nvpair_name(nvp);
62538363Swpaul	(void) nvpair_value_string_array(nvp, &strp, &nelem);
62645601Swpaul	for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
62740097Swpaul		n = snprintf(p, buflen, "%s ", strp[i] ? strp[i] : "<NULL>");
62846204Swpaul		if ((n < 0) || (n >= buflen))
62945629Swpaul			return (_zed_event_add_array_err(eid, name));
63038363Swpaul		p += n;
63138363Swpaul		buflen -= n;
63238363Swpaul	}
63338363Swpaul	if (nelem > 0)
63438363Swpaul		*--p = '\0';
63538363Swpaul
63638363Swpaul	return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
63738363Swpaul}
63838363Swpaul
63938363Swpaul/*
64038363Swpaul * Convert the nvpair [nvp] to a string which is added to the environment
64138363Swpaul * of the child process.
64238363Swpaul * Return 0 on success, -1 on error.
64338363Swpaul */
64438363Swpaulstatic void
64538363Swpaul_zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp)
64638363Swpaul{
64738363Swpaul	const char *name;
64838363Swpaul	data_type_t type;
64938363Swpaul	const char *prefix = ZEVENT_VAR_PREFIX;
65038363Swpaul	boolean_t b;
65138363Swpaul	double d;
65238363Swpaul	uint8_t i8;
65338363Swpaul	uint16_t i16;
65438363Swpaul	uint32_t i32;
65538363Swpaul	uint64_t i64;
65638363Swpaul	const char *str;
65738363Swpaul
65838363Swpaul	assert(zsp != NULL);
65938363Swpaul	assert(nvp != NULL);
66038363Swpaul
66138363Swpaul	name = nvpair_name(nvp);
66238363Swpaul	type = nvpair_type(nvp);
66338363Swpaul
66438363Swpaul	switch (type) {
66538363Swpaul	case DATA_TYPE_BOOLEAN:
66638363Swpaul		_zed_event_add_var(eid, zsp, prefix, name, "%s", "1");
66738363Swpaul		break;
66838363Swpaul	case DATA_TYPE_BOOLEAN_VALUE:
66938363Swpaul		(void) nvpair_value_boolean_value(nvp, &b);
67038363Swpaul		_zed_event_add_var(eid, zsp, prefix, name, "%s", b ? "1" : "0");
67138363Swpaul		break;
67238363Swpaul	case DATA_TYPE_BYTE:
67338363Swpaul		(void) nvpair_value_byte(nvp, &i8);
67438363Swpaul		_zed_event_add_var(eid, zsp, prefix, name, "%d", i8);
67538363Swpaul		break;
67638363Swpaul	case DATA_TYPE_INT8:
67738363Swpaul		(void) nvpair_value_int8(nvp, (int8_t *)&i8);
67838363Swpaul		_zed_event_add_var(eid, zsp, prefix, name, "%d", i8);
67938363Swpaul		break;
68038363Swpaul	case DATA_TYPE_UINT8:
68138363Swpaul		(void) nvpair_value_uint8(nvp, &i8);
68238363Swpaul		_zed_event_add_var(eid, zsp, prefix, name, "%u", i8);
68338363Swpaul		break;
68438363Swpaul	case DATA_TYPE_INT16:
68538363Swpaul		(void) nvpair_value_int16(nvp, (int16_t *)&i16);
68638363Swpaul		_zed_event_add_var(eid, zsp, prefix, name, "%d", i16);
68738363Swpaul		break;
68838363Swpaul	case DATA_TYPE_UINT16:
68938363Swpaul		(void) nvpair_value_uint16(nvp, &i16);
69038363Swpaul		_zed_event_add_var(eid, zsp, prefix, name, "%u", i16);
69138363Swpaul		break;
69238363Swpaul	case DATA_TYPE_INT32:
69338363Swpaul		(void) nvpair_value_int32(nvp, (int32_t *)&i32);
69438363Swpaul		_zed_event_add_var(eid, zsp, prefix, name, "%d", i32);
69538363Swpaul		break;
69638363Swpaul	case DATA_TYPE_UINT32:
69738363Swpaul		(void) nvpair_value_uint32(nvp, &i32);
69838363Swpaul		_zed_event_add_var(eid, zsp, prefix, name, "%u", i32);
69938363Swpaul		break;
70038363Swpaul	case DATA_TYPE_INT64:
70138363Swpaul		(void) nvpair_value_int64(nvp, (int64_t *)&i64);
70238363Swpaul		_zed_event_add_var(eid, zsp, prefix, name,
70338363Swpaul		    "%lld", (longlong_t)i64);
70438363Swpaul		break;
70538363Swpaul	case DATA_TYPE_UINT64:
70638363Swpaul		(void) nvpair_value_uint64(nvp, &i64);
70738363Swpaul		_zed_event_add_var(eid, zsp, prefix, name,
70838363Swpaul		    (_zed_event_value_is_hex(name) ? "0x%.16llX" : "%llu"),
70938363Swpaul		    (u_longlong_t)i64);
71038363Swpaul		/*
71138363Swpaul		 * shadow readable strings for vdev state pairs
71238363Swpaul		 */
71338363Swpaul		if (strcmp(name, FM_EREPORT_PAYLOAD_ZFS_VDEV_STATE) == 0 ||
71438363Swpaul		    strcmp(name, FM_EREPORT_PAYLOAD_ZFS_VDEV_LASTSTATE) == 0) {
71538363Swpaul			char alt[32];
71638363Swpaul
71738363Swpaul			(void) snprintf(alt, sizeof (alt), "%s_str", name);
71838363Swpaul			_zed_event_add_var(eid, zsp, prefix, alt, "%s",
71938363Swpaul			    zpool_state_to_name(i64, VDEV_AUX_NONE));
72038363Swpaul		} else
72138363Swpaul		/*
72238363Swpaul		 * shadow readable strings for pool state
72338363Swpaul		 */
72438363Swpaul		if (strcmp(name, FM_EREPORT_PAYLOAD_ZFS_POOL_STATE) == 0) {
72538363Swpaul			char alt[32];
72638363Swpaul
72738363Swpaul			(void) snprintf(alt, sizeof (alt), "%s_str", name);
72838363Swpaul			_zed_event_add_var(eid, zsp, prefix, alt, "%s",
72938363Swpaul			    zpool_pool_state_to_name(i64));
73038363Swpaul		}
73138363Swpaul		break;
73238363Swpaul	case DATA_TYPE_DOUBLE:
73338363Swpaul		(void) nvpair_value_double(nvp, &d);
73438363Swpaul		_zed_event_add_var(eid, zsp, prefix, name, "%g", d);
73538363Swpaul		break;
73638363Swpaul	case DATA_TYPE_HRTIME:
73738363Swpaul		(void) nvpair_value_hrtime(nvp, (hrtime_t *)&i64);
73838363Swpaul		_zed_event_add_var(eid, zsp, prefix, name,
73938363Swpaul		    "%llu", (u_longlong_t)i64);
74038363Swpaul		break;
74138363Swpaul	case DATA_TYPE_STRING:
74238363Swpaul		(void) nvpair_value_string(nvp, &str);
74338363Swpaul		_zed_event_add_var(eid, zsp, prefix, name,
74438363Swpaul		    "%s", (str ? str : "<NULL>"));
74538363Swpaul		break;
74638363Swpaul	case DATA_TYPE_INT8_ARRAY:
74738363Swpaul		_zed_event_add_int8_array(eid, zsp, prefix, nvp);
74838363Swpaul		break;
74938363Swpaul	case DATA_TYPE_UINT8_ARRAY:
75038363Swpaul		_zed_event_add_uint8_array(eid, zsp, prefix, nvp);
75138363Swpaul		break;
75238363Swpaul	case DATA_TYPE_INT16_ARRAY:
75338363Swpaul		_zed_event_add_int16_array(eid, zsp, prefix, nvp);
75438363Swpaul		break;
75538363Swpaul	case DATA_TYPE_UINT16_ARRAY:
75638363Swpaul		_zed_event_add_uint16_array(eid, zsp, prefix, nvp);
75738363Swpaul		break;
75838363Swpaul	case DATA_TYPE_INT32_ARRAY:
75938363Swpaul		_zed_event_add_int32_array(eid, zsp, prefix, nvp);
76038363Swpaul		break;
76138363Swpaul	case DATA_TYPE_UINT32_ARRAY:
76238363Swpaul		_zed_event_add_uint32_array(eid, zsp, prefix, nvp);
76338363Swpaul		break;
76438363Swpaul	case DATA_TYPE_INT64_ARRAY:
76538363Swpaul		_zed_event_add_int64_array(eid, zsp, prefix, nvp);
76638363Swpaul		break;
76738363Swpaul	case DATA_TYPE_UINT64_ARRAY:
76838363Swpaul		_zed_event_add_uint64_array(eid, zsp, prefix, nvp);
76938363Swpaul		break;
77038363Swpaul	case DATA_TYPE_STRING_ARRAY:
77138363Swpaul		_zed_event_add_string_array(eid, zsp, prefix, nvp);
77238363Swpaul		break;
77338363Swpaul	case DATA_TYPE_NVLIST:
77438363Swpaul	case DATA_TYPE_BOOLEAN_ARRAY:
77538363Swpaul	case DATA_TYPE_BYTE_ARRAY:
77638363Swpaul	case DATA_TYPE_NVLIST_ARRAY:
77738363Swpaul		_zed_event_add_var(eid, zsp, prefix, name, "_NOT_IMPLEMENTED_");
77838363Swpaul		break;
77938363Swpaul	default:
78038363Swpaul		errno = EINVAL;
78138363Swpaul		zed_log_msg(LOG_WARNING,
78238363Swpaul		    "Failed to convert nvpair \"%s\" for eid=%llu: "
78338363Swpaul		    "Unrecognized type=%u", name, eid, (unsigned int) type);
78438363Swpaul		break;
78538363Swpaul	}
78638363Swpaul}
78738363Swpaul
78838363Swpaul/*
78938363Swpaul * Restrict various environment variables to safe and sane values
79038363Swpaul * when constructing the environment for the child process, unless
79138363Swpaul * we're running with a custom $PATH (like under the ZFS test suite).
79238363Swpaul *
79338363Swpaul * Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1.
79438363Swpaul */
79538363Swpaulstatic void
79638363Swpaul_zed_event_add_env_restrict(uint64_t eid, zed_strings_t *zsp,
79738363Swpaul    const char *path)
79838363Swpaul{
79938363Swpaul	const char *env_restrict[][2] = {
80038363Swpaul		{ "IFS",		" \t\n" },
80138363Swpaul		{ "PATH",		_PATH_STDPATH },
80238363Swpaul		{ "ZDB",		SBINDIR "/zdb" },
80338363Swpaul		{ "ZED",		SBINDIR "/zed" },
80438363Swpaul		{ "ZFS",		SBINDIR "/zfs" },
80538363Swpaul		{ "ZINJECT",		SBINDIR "/zinject" },
80638363Swpaul		{ "ZPOOL",		SBINDIR "/zpool" },
80738363Swpaul		{ "ZFS_ALIAS",		ZFS_META_ALIAS },
80838363Swpaul		{ "ZFS_VERSION",	ZFS_META_VERSION },
80938363Swpaul		{ "ZFS_RELEASE",	ZFS_META_RELEASE },
81038363Swpaul		{ NULL,			NULL }
81138363Swpaul	};
81245062Swpaul
81345062Swpaul	/*
81445062Swpaul	 * If we have a custom $PATH, use the default ZFS binary locations
81545062Swpaul	 * instead of the hard-coded ones.
81645062Swpaul	 */
81745062Swpaul	const char *env_path[][2] = {
81846204Swpaul		{ "IFS",		" \t\n" },
81946204Swpaul		{ "PATH",		NULL }, /* $PATH copied in later on */
82046204Swpaul		{ "ZDB",		"zdb" },
82146204Swpaul		{ "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	};
830	const char *(*pa)[2];
831
832	assert(zsp != NULL);
833
834	pa = path != NULL ? env_path : env_restrict;
835
836	for (; *(*pa); pa++) {
837		/* Use our custom $PATH if we have one */
838		if (path != NULL && strcmp((*pa)[0], "PATH") == 0)
839			(*pa)[1] = path;
840
841		_zed_event_add_var(eid, zsp, NULL, (*pa)[0], "%s", (*pa)[1]);
842	}
843}
844
845/*
846 * Preserve specified variables from the parent environment
847 * when constructing the environment for the child process.
848 *
849 * Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1.
850 */
851static void
852_zed_event_add_env_preserve(uint64_t eid, zed_strings_t *zsp)
853{
854	const char *env_preserve[] = {
855		"TZ",
856		NULL
857	};
858	const char **keyp;
859	const char *val;
860
861	assert(zsp != NULL);
862
863	for (keyp = env_preserve; *keyp; keyp++) {
864		if ((val = getenv(*keyp)))
865			_zed_event_add_var(eid, zsp, NULL, *keyp, "%s", val);
866	}
867}
868
869/*
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 inside
872 * the string [class], or NULL if insufficient components exist.
873 */
874static const char *
875_zed_event_get_subclass(const char *class)
876{
877	const char *p;
878	int i;
879
880	if (!class)
881		return (NULL);
882
883	p = class;
884	for (i = 0; i < 3; i++) {
885		p = strchr(p, '.');
886		if (!p)
887			break;
888		p++;
889	}
890	return (p);
891}
892
893/*
894 * Convert the zevent time from a 2-element array of 64b integers
895 * 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 void
901_zed_event_add_time_strings(uint64_t eid, zed_strings_t *zsp, int64_t etime[])
902{
903	struct tm stp;
904	char buf[32];
905
906	assert(zsp != NULL);
907	assert(etime != NULL);
908
909	_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]);
913
914	if (!localtime_r((const time_t *) &etime[0], &stp)) {
915		zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",
916		    ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "localtime error");
917	} else if (!strftime(buf, sizeof (buf), "%Y-%m-%d %H:%M:%S%z", &stp)) {
918		zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",
919		    ZEVENT_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}
925
926
927static void
928_zed_event_update_enc_sysfs_path(nvlist_t *nvl)
929{
930	const char *vdev_path;
931
932	if (nvlist_lookup_string(nvl, FM_EREPORT_PAYLOAD_ZFS_VDEV_PATH,
933	    &vdev_path) != 0) {
934		return; /* some other kind of event, ignore it */
935	}
936
937	if (vdev_path == NULL) {
938		return;
939	}
940
941	update_vdev_config_dev_sysfs_path(nvl, vdev_path,
942	    FM_EREPORT_PAYLOAD_ZFS_VDEV_ENC_SYSFS_PATH);
943}
944
945/*
946 * Service the next zevent, blocking until one is available.
947 */
948int
949zed_event_service(struct zed_conf *zcp)
950{
951	nvlist_t *nvl;
952	nvpair_t *nvp;
953	int n_dropped;
954	zed_strings_t *zsp;
955	uint64_t eid;
956	int64_t *etime;
957	uint_t nelem;
958	const char *class;
959	const char *subclass;
960	int rv;
961
962	if (!zcp) {
963		errno = EINVAL;
964		zed_log_msg(LOG_ERR, "Failed to service zevent: %s",
965		    strerror(errno));
966		return (EINVAL);
967	}
968	rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONE,
969	    zcp->zevent_fd);
970
971	if ((rv != 0) || !nvl)
972		return (errno);
973
974	if (n_dropped > 0) {
975		zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
976		_bump_event_queue_length();
977	}
978	if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
979		zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
980	} else if (nvlist_lookup_int64_array(
981	    nvl, "time", &etime, &nelem) != 0) {
982		zed_log_msg(LOG_WARNING,
983		    "Failed to lookup zevent time (eid=%llu)", eid);
984	} else if (nelem != 2) {
985		zed_log_msg(LOG_WARNING,
986		    "Failed to lookup zevent time (eid=%llu, nelem=%u)",
987		    eid, nelem);
988	} else if (nvlist_lookup_string(nvl, "class", &class) != 0) {
989		zed_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 sysfs
994		 * path, then use that value rather than the one stored in the
995		 * vd->vdev_enc_sysfs_path.  There have been rare cases where
996		 * vd->vdev_enc_sysfs_path becomes outdated.  However, there
997		 * will be other times when we can not dynamically detect the
998		 * sysfs path (like if a disk disappears) and have to rely on
999		 * the old value for things like turning on the fault LED.
1000		 */
1001		_zed_event_update_enc_sysfs_path(nvl);
1002
1003		/* let internal modules see this event first */
1004		zfs_agent_post_event(class, NULL, nvl);
1005
1006		zsp = zed_strings_create();
1007
1008		nvp = NULL;
1009		while ((nvp = nvlist_next_nvpair(nvl, nvp)))
1010			_zed_event_add_nvpair(eid, zsp, nvp);
1011
1012		_zed_event_add_env_restrict(eid, zsp, zcp->path);
1013		_zed_event_add_env_preserve(eid, zsp);
1014
1015		_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);
1019		subclass = _zed_event_get_subclass(class);
1020		_zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "SUBCLASS",
1021		    "%s", (subclass ? subclass : class));
1022
1023		_zed_event_add_time_strings(eid, zsp, etime);
1024
1025		zed_exec_process(eid, class, subclass, zcp, zsp);
1026
1027		zed_conf_write_state(zcp, eid, etime);
1028
1029		zed_strings_destroy(zsp);
1030	}
1031	nvlist_free(nvl);
1032	return (0);
1033}
1034