4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license from the top-level
9 * OPENSOLARIS.LICENSE or <http://opensource.org/licenses/CDDL-1.0>.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each file
14 * and include the License file from the top-level OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
24 * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
30 #include <libzfs.h> /* FIXME: Replace with libzfs_core. */
36 #include <sys/zfs_ioctl.h>
44 #include "zed_strings.h"
47 * Open the libzfs interface.
50 zed_event_init(struct zed_conf *zcp)
53 zed_log_die("Failed zed_event_init: %s", strerror(EINVAL));
55 zcp->zfs_hdl = libzfs_init();
57 zed_log_die("Failed to initialize libzfs");
59 zcp->zevent_fd = open(ZFS_DEV, O_RDWR);
60 if (zcp->zevent_fd < 0)
61 zed_log_die("Failed to open \"%s\": %s",
62 ZFS_DEV, strerror(errno));
66 * Close the libzfs interface.
69 zed_event_fini(struct zed_conf *zcp)
72 zed_log_die("Failed zed_event_fini: %s", strerror(EINVAL));
74 if (zcp->zevent_fd >= 0) {
75 if (close(zcp->zevent_fd) < 0)
76 zed_log_msg(LOG_WARNING, "Failed to close \"%s\": %s",
77 ZFS_DEV, strerror(errno));
82 libzfs_fini(zcp->zfs_hdl);
88 * Seek to the event specified by [saved_eid] and [saved_etime].
89 * This protects against processing a given event more than once.
90 * Return 0 upon a successful seek to the specified event, or -1 otherwise.
91 * A zevent is considered to be uniquely specified by its (eid,time) tuple.
92 * The unsigned 64b eid is set to 1 when the kernel module is loaded, and
93 * incremented by 1 for each new event. Since the state file can persist
94 * across a kernel module reload, the time must be checked to ensure a match.
97 zed_event_seek(struct zed_conf *zcp, uint64_t saved_eid, int64_t saved_etime[])
109 zed_log_msg(LOG_ERR, "Failed to seek zevent: %s",
115 while ((eid < saved_eid) && !found) {
116 rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped,
117 ZEVENT_NONBLOCK, zcp->zevent_fd);
119 if ((rv != 0) || !nvl)
123 zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
125 * FIXME: Increase max size of event nvlist in
126 * /sys/module/zfs/parameters/zfs_zevent_len_max ?
129 if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
130 zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
131 } else if (nvlist_lookup_int64_array(nvl, "time",
132 &etime, &nelem) != 0) {
133 zed_log_msg(LOG_WARNING,
134 "Failed to lookup zevent time (eid=%llu)", eid);
135 } else if (nelem != 2) {
136 zed_log_msg(LOG_WARNING,
137 "Failed to lookup zevent time (eid=%llu, nelem=%u)",
139 } else if ((eid != saved_eid) ||
140 (etime[0] != saved_etime[0]) ||
141 (etime[1] != saved_etime[1])) {
148 if (!found && (saved_eid > 0)) {
149 if (zpool_events_seek(zcp->zfs_hdl, ZEVENT_SEEK_START,
151 zed_log_msg(LOG_WARNING, "Failed to seek to eid=0");
155 zed_log_msg(LOG_NOTICE, "Processing events since eid=%llu", eid);
156 return (found ? 0 : -1);
160 _zed_event_convert_int8_array(char *buf, int buflen, nvpair_t *nvp)
170 (void) nvpair_value_int8_array(nvp, &i8p, &nelem);
171 for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
172 n = snprintf(p, buflen, "%d ", i8p[i]);
173 if ((n < 0) || (n >= buflen)) {
187 _zed_event_convert_uint8_array(char *buf, int buflen, nvpair_t *nvp)
197 (void) nvpair_value_uint8_array(nvp, &u8p, &nelem);
198 for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
199 n = snprintf(p, buflen, "%u ", u8p[i]);
200 if ((n < 0) || (n >= buflen)) {
214 _zed_event_convert_int16_array(char *buf, int buflen, nvpair_t *nvp)
224 (void) nvpair_value_int16_array(nvp, &i16p, &nelem);
225 for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
226 n = snprintf(p, buflen, "%d ", i16p[i]);
227 if ((n < 0) || (n >= buflen)) {
241 _zed_event_convert_uint16_array(char *buf, int buflen, nvpair_t *nvp)
251 (void) nvpair_value_uint16_array(nvp, &u16p, &nelem);
252 for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
253 n = snprintf(p, buflen, "%u ", u16p[i]);
254 if ((n < 0) || (n >= buflen)) {
268 _zed_event_convert_int32_array(char *buf, int buflen, nvpair_t *nvp)
278 (void) nvpair_value_int32_array(nvp, &i32p, &nelem);
279 for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
280 n = snprintf(p, buflen, "%d ", i32p[i]);
281 if ((n < 0) || (n >= buflen)) {
295 _zed_event_convert_uint32_array(char *buf, int buflen, nvpair_t *nvp)
305 (void) nvpair_value_uint32_array(nvp, &u32p, &nelem);
306 for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
307 n = snprintf(p, buflen, "%u ", u32p[i]);
308 if ((n < 0) || (n >= buflen)) {
322 _zed_event_convert_int64_array(char *buf, int buflen, nvpair_t *nvp)
332 (void) nvpair_value_int64_array(nvp, &i64p, &nelem);
333 for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
334 n = snprintf(p, buflen, "%lld ", (u_longlong_t) i64p[i]);
335 if ((n < 0) || (n >= buflen)) {
349 _zed_event_convert_uint64_array(char *buf, int buflen, nvpair_t *nvp,
360 (void) nvpair_value_uint64_array(nvp, &u64p, &nelem);
361 for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
362 n = snprintf(p, buflen, fmt, (u_longlong_t) u64p[i]);
363 if ((n < 0) || (n >= buflen)) {
377 _zed_event_convert_string_array(char *buf, int buflen, nvpair_t *nvp)
387 (void) nvpair_value_string_array(nvp, &strp, &nelem);
388 for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
389 n = snprintf(p, buflen, "%s ", strp[i] ? strp[i] : "<NULL>");
390 if ((n < 0) || (n >= buflen)) {
404 * Return non-zero if nvpair [name] should be formatted in hex; o/w, return 0.
407 _zed_event_value_is_hex(const char *name)
409 const char *hex_suffix[] = {
420 for (pp = hex_suffix; *pp; pp++) {
421 p = strstr(name, *pp);
422 if (p && strlen(p) == strlen(*pp))
429 * Convert the nvpair [nvp] to a string which is added to the environment
430 * of the child process.
431 * Return 0 on success, -1 on error.
432 * FIXME: Refactor with cmd/zpool/zpool_main.c:zpool_do_events_nvprint()?
435 _zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp)
457 name = nvpair_name(nvp);
458 type = nvpair_type(nvp);
459 buflen = sizeof (buf);
461 /* Copy NAME prefix for ZED zevent namespace. */
462 n = strlcpy(buf, ZEVENT_VAR_PREFIX, sizeof (buf));
463 if (n >= sizeof (buf)) {
464 zed_log_msg(LOG_WARNING,
465 "Failed to convert nvpair \"%s\" for eid=%llu: %s",
466 name, eid, "Exceeded buffer size");
472 /* Convert NAME to alphanumeric uppercase. */
473 for (q = name; *q && (buflen > 0); q++) {
474 *p++ = isalnum(*q) ? toupper(*q) : '_';
478 /* Separate NAME from VALUE. */
487 case DATA_TYPE_BOOLEAN:
488 n = snprintf(p, buflen, "%s", "1");
490 case DATA_TYPE_BOOLEAN_VALUE:
491 (void) nvpair_value_boolean_value(nvp, &b);
492 n = snprintf(p, buflen, "%s", b ? "1" : "0");
495 (void) nvpair_value_byte(nvp, &i8);
496 n = snprintf(p, buflen, "%d", i8);
499 (void) nvpair_value_int8(nvp, (int8_t *) &i8);
500 n = snprintf(p, buflen, "%d", i8);
502 case DATA_TYPE_UINT8:
503 (void) nvpair_value_uint8(nvp, &i8);
504 n = snprintf(p, buflen, "%u", i8);
506 case DATA_TYPE_INT16:
507 (void) nvpair_value_int16(nvp, (int16_t *) &i16);
508 n = snprintf(p, buflen, "%d", i16);
510 case DATA_TYPE_UINT16:
511 (void) nvpair_value_uint16(nvp, &i16);
512 n = snprintf(p, buflen, "%u", i16);
514 case DATA_TYPE_INT32:
515 (void) nvpair_value_int32(nvp, (int32_t *) &i32);
516 n = snprintf(p, buflen, "%d", i32);
518 case DATA_TYPE_UINT32:
519 (void) nvpair_value_uint32(nvp, &i32);
520 n = snprintf(p, buflen, "%u", i32);
522 case DATA_TYPE_INT64:
523 (void) nvpair_value_int64(nvp, (int64_t *) &i64);
524 n = snprintf(p, buflen, "%lld", (longlong_t) i64);
526 case DATA_TYPE_UINT64:
527 (void) nvpair_value_uint64(nvp, &i64);
528 fmt = _zed_event_value_is_hex(name) ? "0x%.16llX" : "%llu";
529 n = snprintf(p, buflen, fmt, (u_longlong_t) i64);
531 case DATA_TYPE_DOUBLE:
532 (void) nvpair_value_double(nvp, &d);
533 n = snprintf(p, buflen, "%g", d);
535 case DATA_TYPE_HRTIME:
536 (void) nvpair_value_hrtime(nvp, (hrtime_t *) &i64);
537 n = snprintf(p, buflen, "%llu", (u_longlong_t) i64);
539 case DATA_TYPE_NVLIST:
541 n = snprintf(p, buflen, "%s", "_NOT_IMPLEMENTED_");
543 case DATA_TYPE_STRING:
544 (void) nvpair_value_string(nvp, &str);
545 n = snprintf(p, buflen, "%s", (str ? str : "<NULL>"));
547 case DATA_TYPE_BOOLEAN_ARRAY:
549 n = snprintf(p, buflen, "%s", "_NOT_IMPLEMENTED_");
551 case DATA_TYPE_BYTE_ARRAY:
553 n = snprintf(p, buflen, "%s", "_NOT_IMPLEMENTED_");
555 case DATA_TYPE_INT8_ARRAY:
556 n = _zed_event_convert_int8_array(p, buflen, nvp);
558 case DATA_TYPE_UINT8_ARRAY:
559 n = _zed_event_convert_uint8_array(p, buflen, nvp);
561 case DATA_TYPE_INT16_ARRAY:
562 n = _zed_event_convert_int16_array(p, buflen, nvp);
564 case DATA_TYPE_UINT16_ARRAY:
565 n = _zed_event_convert_uint16_array(p, buflen, nvp);
567 case DATA_TYPE_INT32_ARRAY:
568 n = _zed_event_convert_int32_array(p, buflen, nvp);
570 case DATA_TYPE_UINT32_ARRAY:
571 n = _zed_event_convert_uint32_array(p, buflen, nvp);
573 case DATA_TYPE_INT64_ARRAY:
574 n = _zed_event_convert_int64_array(p, buflen, nvp);
576 case DATA_TYPE_UINT64_ARRAY:
577 fmt = _zed_event_value_is_hex(name) ? "0x%.16llX " : "%llu ";
578 n = _zed_event_convert_uint64_array(p, buflen, nvp, fmt);
580 case DATA_TYPE_STRING_ARRAY:
581 n = _zed_event_convert_string_array(p, buflen, nvp);
583 case DATA_TYPE_NVLIST_ARRAY:
585 n = snprintf(p, buflen, "%s", "_NOT_IMPLEMENTED_");
588 zed_log_msg(LOG_WARNING,
589 "Failed to convert nvpair \"%s\" for eid=%llu: "
590 "Unrecognized type=%u", name, eid, (unsigned int) type);
593 if ((n < 0) || (n >= sizeof (buf))) {
594 zed_log_msg(LOG_WARNING,
595 "Failed to convert nvpair \"%s\" for eid=%llu: %s",
596 name, eid, "Exceeded buffer size");
599 if (zed_strings_add(zsp, buf) < 0) {
600 zed_log_msg(LOG_WARNING,
601 "Failed to convert nvpair \"%s\" for eid=%llu: %s",
602 name, eid, strerror(ENOMEM));
608 * Add the environment variable specified by the format string [fmt].
611 _zed_event_add_var(uint64_t eid, zed_strings_t *zsp, const char *fmt, ...)
622 va_start(vargs, fmt);
623 n = vsnprintf(buf, sizeof (buf), fmt, vargs);
625 p = strchr(buf, '=');
626 namelen = (p) ? p - buf : strlen(buf);
628 if ((n < 0) || (n >= sizeof (buf))) {
629 zed_log_msg(LOG_WARNING, "Failed to add %.*s for eid=%llu: %s",
630 namelen, buf, eid, "Exceeded buffer size");
632 zed_log_msg(LOG_WARNING, "Failed to add %.*s for eid=%llu: %s",
633 namelen, buf, eid, "Missing assignment");
634 } else if (zed_strings_add(zsp, buf) < 0) {
635 zed_log_msg(LOG_WARNING, "Failed to add %.*s for eid=%llu: %s",
636 namelen, buf, eid, strerror(ENOMEM));
641 * Restrict various environment variables to safe and sane values
642 * when constructing the environment for the child process.
643 * Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1.
646 _zed_event_add_env_restrict(uint64_t eid, zed_strings_t *zsp)
648 const char *env_restrict[] = {
650 "PATH=" _PATH_STDPATH,
651 "ZDB=" SBINDIR "/zdb",
652 "ZED=" SBINDIR "/zed",
653 "ZFS=" SBINDIR "/zfs",
654 "ZINJECT=" SBINDIR "/zinject",
655 "ZPOOL=" SBINDIR "/zpool",
656 "ZFS_ALIAS=" ZFS_META_ALIAS,
657 "ZFS_VERSION=" ZFS_META_VERSION,
658 "ZFS_RELEASE=" ZFS_META_RELEASE,
665 for (pp = env_restrict; *pp; pp++) {
666 _zed_event_add_var(eid, zsp, "%s", *pp);
671 * Preserve specified variables from the parent environment
672 * when constructing the environment for the child process.
673 * Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1.
676 _zed_event_add_env_preserve(uint64_t eid, zed_strings_t *zsp)
678 const char *env_preserve[] = {
687 for (pp = env_preserve; *pp; pp++) {
688 if ((p = getenv(*pp)))
689 _zed_event_add_var(eid, zsp, "%s=%s", *pp, p);
694 * Compute the "subclass" by removing the first 3 components of [class]
695 * (which seem to always be either "ereport.fs.zfs" or "resource.fs.zfs").
696 * Return a pointer inside the string [class], or NULL if insufficient
700 _zed_event_get_subclass(const char *class)
709 for (i = 0; i < 3; i++) {
719 * Convert the zevent time from a 2-element array of 64b integers
720 * into a more convenient form:
721 * TIME_SECS is the second component of the time.
722 * TIME_NSECS is the nanosecond component of the time.
723 * TIME_STRING is an almost-RFC3339-compliant string representation.
726 _zed_event_add_time_strings(uint64_t eid, zed_strings_t *zsp, int64_t etime[])
732 assert(etime != NULL);
734 _zed_event_add_var(eid, zsp, "%s%s=%lld",
735 ZEVENT_VAR_PREFIX, "TIME_SECS", (long long int) etime[0]);
736 _zed_event_add_var(eid, zsp, "%s%s=%lld",
737 ZEVENT_VAR_PREFIX, "TIME_NSECS", (long long int) etime[1]);
739 if (!(stp = localtime((const time_t *) &etime[0]))) {
740 zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",
741 ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "localtime error");
742 } else if (!strftime(buf, sizeof (buf), "%Y-%m-%d %H:%M:%S%z", stp)) {
743 zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",
744 ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "strftime error");
746 _zed_event_add_var(eid, zsp, "%s%s=%s",
747 ZEVENT_VAR_PREFIX, "TIME_STRING", buf);
752 * Service the next zevent, blocking until one is available.
755 zed_event_service(struct zed_conf *zcp)
765 const char *subclass;
770 zed_log_msg(LOG_ERR, "Failed to service zevent: %s",
774 rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONE,
777 if ((rv != 0) || !nvl)
781 zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
783 * FIXME: Increase max size of event nvlist in
784 * /sys/module/zfs/parameters/zfs_zevent_len_max ?
787 if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
788 zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
789 } else if (nvlist_lookup_int64_array(
790 nvl, "time", &etime, &nelem) != 0) {
791 zed_log_msg(LOG_WARNING,
792 "Failed to lookup zevent time (eid=%llu)", eid);
793 } else if (nelem != 2) {
794 zed_log_msg(LOG_WARNING,
795 "Failed to lookup zevent time (eid=%llu, nelem=%u)",
797 } else if (nvlist_lookup_string(nvl, "class", &class) != 0) {
798 zed_log_msg(LOG_WARNING,
799 "Failed to lookup zevent class (eid=%llu)", eid);
801 zsp = zed_strings_create();
804 while ((nvp = nvlist_next_nvpair(nvl, nvp)))
805 _zed_event_add_nvpair(eid, zsp, nvp);
807 _zed_event_add_env_restrict(eid, zsp);
808 _zed_event_add_env_preserve(eid, zsp);
810 _zed_event_add_var(eid, zsp, "%s%s=%d",
811 ZED_VAR_PREFIX, "PID", (int) getpid());
812 _zed_event_add_var(eid, zsp, "%s%s=%s",
813 ZED_VAR_PREFIX, "SCRIPT_DIR", zcp->script_dir);
815 subclass = _zed_event_get_subclass(class);
816 _zed_event_add_var(eid, zsp, "%s%s=%s",
817 ZEVENT_VAR_PREFIX, "SUBCLASS",
818 (subclass ? subclass : class));
819 _zed_event_add_time_strings(eid, zsp, etime);
821 zed_exec_process(eid, class, subclass,
822 zcp->script_dir, zcp->scripts, zsp, zcp->zevent_fd);
824 zed_conf_write_state(zcp, eid, etime);
826 zed_strings_destroy(zsp);