]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - cmd/zed/zed_event.c
zed: allow limiting concurrent jobs
[FreeBSD/FreeBSD.git] / cmd / zed / zed_event.c
1 /*
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 ZoL git commit log for authoritative copyright attribution.
7  *
8  * The contents of this file are subject to the terms of the
9  * Common Development and Distribution License Version 1.0 (CDDL-1.0).
10  * You can obtain a copy of the license from the top-level file
11  * "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  */
14
15 #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"
36
37 #include "agents/zfs_agents.h"
38
39 #define MAXBUF  4096
40
41 /*
42  * Open the libzfs interface.
43  */
44 int
45 zed_event_init(struct zed_conf *zcp)
46 {
47         if (!zcp)
48                 zed_log_die("Failed zed_event_init: %s", strerror(EINVAL));
49
50         zcp->zfs_hdl = libzfs_init();
51         if (!zcp->zfs_hdl) {
52                 if (zcp->do_idle)
53                         return (-1);
54                 zed_log_die("Failed to initialize libzfs");
55         }
56
57         zcp->zevent_fd = open(ZFS_DEV, O_RDWR);
58         if (zcp->zevent_fd < 0) {
59                 if (zcp->do_idle)
60                         return (-1);
61                 zed_log_die("Failed to open \"%s\": %s",
62                     ZFS_DEV, strerror(errno));
63         }
64
65         zfs_agent_init(zcp->zfs_hdl);
66
67         if (zed_disk_event_init() != 0) {
68                 if (zcp->do_idle)
69                         return (-1);
70                 zed_log_die("Failed to initialize disk events");
71         }
72
73         return (0);
74 }
75
76 /*
77  * Close the libzfs interface.
78  */
79 void
80 zed_event_fini(struct zed_conf *zcp)
81 {
82         if (!zcp)
83                 zed_log_die("Failed zed_event_fini: %s", strerror(EINVAL));
84
85         zed_disk_event_fini();
86         zfs_agent_fini();
87
88         if (zcp->zevent_fd >= 0) {
89                 if (close(zcp->zevent_fd) < 0)
90                         zed_log_msg(LOG_WARNING, "Failed to close \"%s\": %s",
91                             ZFS_DEV, strerror(errno));
92
93                 zcp->zevent_fd = -1;
94         }
95         if (zcp->zfs_hdl) {
96                 libzfs_fini(zcp->zfs_hdl);
97                 zcp->zfs_hdl = NULL;
98         }
99
100         zed_exec_fini();
101 }
102
103 /*
104  * Seek to the event specified by [saved_eid] and [saved_etime].
105  * This protects against processing a given event more than once.
106  * Return 0 upon a successful seek to the specified event, or -1 otherwise.
107  *
108  * A zevent is considered to be uniquely specified by its (eid,time) tuple.
109  * The unsigned 64b eid is set to 1 when the kernel module is loaded, and
110  * incremented by 1 for each new event.  Since the state file can persist
111  * across a kernel module reload, the time must be checked to ensure a match.
112  */
113 int
114 zed_event_seek(struct zed_conf *zcp, uint64_t saved_eid, int64_t saved_etime[])
115 {
116         uint64_t eid;
117         int found;
118         nvlist_t *nvl;
119         int n_dropped;
120         int64_t *etime;
121         uint_t nelem;
122         int rv;
123
124         if (!zcp) {
125                 errno = EINVAL;
126                 zed_log_msg(LOG_ERR, "Failed to seek zevent: %s",
127                     strerror(errno));
128                 return (-1);
129         }
130         eid = 0;
131         found = 0;
132         while ((eid < saved_eid) && !found) {
133                 rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped,
134                     ZEVENT_NONBLOCK, zcp->zevent_fd);
135
136                 if ((rv != 0) || !nvl)
137                         break;
138
139                 if (n_dropped > 0) {
140                         zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
141                         /*
142                          * FIXME: Increase max size of event nvlist in
143                          *   /sys/module/zfs/parameters/zfs_zevent_len_max ?
144                          */
145                 }
146                 if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
147                         zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
148                 } else if (nvlist_lookup_int64_array(nvl, "time",
149                     &etime, &nelem) != 0) {
150                         zed_log_msg(LOG_WARNING,
151                             "Failed to lookup zevent time (eid=%llu)", eid);
152                 } else if (nelem != 2) {
153                         zed_log_msg(LOG_WARNING,
154                             "Failed to lookup zevent time (eid=%llu, nelem=%u)",
155                             eid, nelem);
156                 } else if ((eid != saved_eid) ||
157                     (etime[0] != saved_etime[0]) ||
158                     (etime[1] != saved_etime[1])) {
159                         /* no-op */
160                 } else {
161                         found = 1;
162                 }
163                 free(nvl);
164         }
165         if (!found && (saved_eid > 0)) {
166                 if (zpool_events_seek(zcp->zfs_hdl, ZEVENT_SEEK_START,
167                     zcp->zevent_fd) < 0)
168                         zed_log_msg(LOG_WARNING, "Failed to seek to eid=0");
169                 else
170                         eid = 0;
171         }
172         zed_log_msg(LOG_NOTICE, "Processing events since eid=%llu", eid);
173         return (found ? 0 : -1);
174 }
175
176 /*
177  * Return non-zero if nvpair [name] should be formatted in hex; o/w, return 0.
178  */
179 static int
180 _zed_event_value_is_hex(const char *name)
181 {
182         const char *hex_suffix[] = {
183                 "_guid",
184                 "_guids",
185                 NULL
186         };
187         const char **pp;
188         char *p;
189
190         if (!name)
191                 return (0);
192
193         for (pp = hex_suffix; *pp; pp++) {
194                 p = strstr(name, *pp);
195                 if (p && strlen(p) == strlen(*pp))
196                         return (1);
197         }
198         return (0);
199 }
200
201 /*
202  * Add an environment variable for [eid] to the container [zsp].
203  *
204  * The variable name is the concatenation of [prefix] and [name] converted to
205  * uppercase with non-alphanumeric characters converted to underscores;
206  * [prefix] is optional, and [name] must begin with an alphabetic character.
207  * If the converted variable name already exists within the container [zsp],
208  * its existing value will be replaced with the new value.
209  *
210  * The variable value is specified by the format string [fmt].
211  *
212  * Returns 0 on success, and -1 on error (with errno set).
213  *
214  * All environment variables in [zsp] should be added through this function.
215  */
216 static int
217 _zed_event_add_var(uint64_t eid, zed_strings_t *zsp,
218     const char *prefix, const char *name, const char *fmt, ...)
219 {
220         char keybuf[MAXBUF];
221         char valbuf[MAXBUF];
222         char *dstp;
223         const char *srcp;
224         const char *lastp;
225         int n;
226         int buflen;
227         va_list vargs;
228
229         assert(zsp != NULL);
230         assert(fmt != NULL);
231
232         if (!name) {
233                 errno = EINVAL;
234                 zed_log_msg(LOG_WARNING,
235                     "Failed to add variable for eid=%llu: Name is empty", eid);
236                 return (-1);
237         } else if (!isalpha(name[0])) {
238                 errno = EINVAL;
239                 zed_log_msg(LOG_WARNING,
240                     "Failed to add variable for eid=%llu: "
241                     "Name \"%s\" is invalid", eid, name);
242                 return (-1);
243         }
244         /*
245          * Construct the string key by converting PREFIX (if present) and NAME.
246          */
247         dstp = keybuf;
248         lastp = keybuf + sizeof (keybuf);
249         if (prefix) {
250                 for (srcp = prefix; *srcp && (dstp < lastp); srcp++)
251                         *dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_';
252         }
253         for (srcp = name; *srcp && (dstp < lastp); srcp++)
254                 *dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_';
255
256         if (dstp == lastp) {
257                 errno = ENAMETOOLONG;
258                 zed_log_msg(LOG_WARNING,
259                     "Failed to add variable for eid=%llu: Name too long", eid);
260                 return (-1);
261         }
262         *dstp = '\0';
263         /*
264          * Construct the string specified by "[PREFIX][NAME]=[FMT]".
265          */
266         dstp = valbuf;
267         buflen = sizeof (valbuf);
268         n = strlcpy(dstp, keybuf, buflen);
269         if (n >= sizeof (valbuf)) {
270                 errno = EMSGSIZE;
271                 zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
272                     keybuf, eid, "Exceeded buffer size");
273                 return (-1);
274         }
275         dstp += n;
276         buflen -= n;
277
278         *dstp++ = '=';
279         buflen--;
280
281         if (buflen <= 0) {
282                 errno = EMSGSIZE;
283                 zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
284                     keybuf, eid, "Exceeded buffer size");
285                 return (-1);
286         }
287
288         va_start(vargs, fmt);
289         n = vsnprintf(dstp, buflen, fmt, vargs);
290         va_end(vargs);
291
292         if ((n < 0) || (n >= buflen)) {
293                 errno = EMSGSIZE;
294                 zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
295                     keybuf, eid, "Exceeded buffer size");
296                 return (-1);
297         } else if (zed_strings_add(zsp, keybuf, valbuf) < 0) {
298                 zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
299                     keybuf, eid, strerror(errno));
300                 return (-1);
301         }
302         return (0);
303 }
304
305 static int
306 _zed_event_add_array_err(uint64_t eid, const char *name)
307 {
308         errno = EMSGSIZE;
309         zed_log_msg(LOG_WARNING,
310             "Failed to convert nvpair \"%s\" for eid=%llu: "
311             "Exceeded buffer size", name, eid);
312         return (-1);
313 }
314
315 static int
316 _zed_event_add_int8_array(uint64_t eid, zed_strings_t *zsp,
317     const char *prefix, nvpair_t *nvp)
318 {
319         char buf[MAXBUF];
320         int buflen = sizeof (buf);
321         const char *name;
322         int8_t *i8p;
323         uint_t nelem;
324         uint_t i;
325         char *p;
326         int n;
327
328         assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT8_ARRAY));
329
330         name = nvpair_name(nvp);
331         (void) nvpair_value_int8_array(nvp, &i8p, &nelem);
332         for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
333                 n = snprintf(p, buflen, "%d ", i8p[i]);
334                 if ((n < 0) || (n >= buflen))
335                         return (_zed_event_add_array_err(eid, name));
336                 p += n;
337                 buflen -= n;
338         }
339         if (nelem > 0)
340                 *--p = '\0';
341
342         return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
343 }
344
345 static int
346 _zed_event_add_uint8_array(uint64_t eid, zed_strings_t *zsp,
347     const char *prefix, nvpair_t *nvp)
348 {
349         char buf[MAXBUF];
350         int buflen = sizeof (buf);
351         const char *name;
352         uint8_t *u8p;
353         uint_t nelem;
354         uint_t i;
355         char *p;
356         int n;
357
358         assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT8_ARRAY));
359
360         name = nvpair_name(nvp);
361         (void) nvpair_value_uint8_array(nvp, &u8p, &nelem);
362         for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
363                 n = snprintf(p, buflen, "%u ", u8p[i]);
364                 if ((n < 0) || (n >= buflen))
365                         return (_zed_event_add_array_err(eid, name));
366                 p += n;
367                 buflen -= n;
368         }
369         if (nelem > 0)
370                 *--p = '\0';
371
372         return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
373 }
374
375 static int
376 _zed_event_add_int16_array(uint64_t eid, zed_strings_t *zsp,
377     const char *prefix, nvpair_t *nvp)
378 {
379         char buf[MAXBUF];
380         int buflen = sizeof (buf);
381         const char *name;
382         int16_t *i16p;
383         uint_t nelem;
384         uint_t i;
385         char *p;
386         int n;
387
388         assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT16_ARRAY));
389
390         name = nvpair_name(nvp);
391         (void) nvpair_value_int16_array(nvp, &i16p, &nelem);
392         for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
393                 n = snprintf(p, buflen, "%d ", i16p[i]);
394                 if ((n < 0) || (n >= buflen))
395                         return (_zed_event_add_array_err(eid, name));
396                 p += n;
397                 buflen -= n;
398         }
399         if (nelem > 0)
400                 *--p = '\0';
401
402         return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
403 }
404
405 static int
406 _zed_event_add_uint16_array(uint64_t eid, zed_strings_t *zsp,
407     const char *prefix, nvpair_t *nvp)
408 {
409         char buf[MAXBUF];
410         int buflen = sizeof (buf);
411         const char *name;
412         uint16_t *u16p;
413         uint_t nelem;
414         uint_t i;
415         char *p;
416         int n;
417
418         assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT16_ARRAY));
419
420         name = nvpair_name(nvp);
421         (void) nvpair_value_uint16_array(nvp, &u16p, &nelem);
422         for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
423                 n = snprintf(p, buflen, "%u ", u16p[i]);
424                 if ((n < 0) || (n >= buflen))
425                         return (_zed_event_add_array_err(eid, name));
426                 p += n;
427                 buflen -= n;
428         }
429         if (nelem > 0)
430                 *--p = '\0';
431
432         return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
433 }
434
435 static int
436 _zed_event_add_int32_array(uint64_t eid, zed_strings_t *zsp,
437     const char *prefix, nvpair_t *nvp)
438 {
439         char buf[MAXBUF];
440         int buflen = sizeof (buf);
441         const char *name;
442         int32_t *i32p;
443         uint_t nelem;
444         uint_t i;
445         char *p;
446         int n;
447
448         assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT32_ARRAY));
449
450         name = nvpair_name(nvp);
451         (void) nvpair_value_int32_array(nvp, &i32p, &nelem);
452         for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
453                 n = snprintf(p, buflen, "%d ", i32p[i]);
454                 if ((n < 0) || (n >= buflen))
455                         return (_zed_event_add_array_err(eid, name));
456                 p += n;
457                 buflen -= n;
458         }
459         if (nelem > 0)
460                 *--p = '\0';
461
462         return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
463 }
464
465 static int
466 _zed_event_add_uint32_array(uint64_t eid, zed_strings_t *zsp,
467     const char *prefix, nvpair_t *nvp)
468 {
469         char buf[MAXBUF];
470         int buflen = sizeof (buf);
471         const char *name;
472         uint32_t *u32p;
473         uint_t nelem;
474         uint_t i;
475         char *p;
476         int n;
477
478         assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT32_ARRAY));
479
480         name = nvpair_name(nvp);
481         (void) nvpair_value_uint32_array(nvp, &u32p, &nelem);
482         for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
483                 n = snprintf(p, buflen, "%u ", u32p[i]);
484                 if ((n < 0) || (n >= buflen))
485                         return (_zed_event_add_array_err(eid, name));
486                 p += n;
487                 buflen -= n;
488         }
489         if (nelem > 0)
490                 *--p = '\0';
491
492         return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
493 }
494
495 static int
496 _zed_event_add_int64_array(uint64_t eid, zed_strings_t *zsp,
497     const char *prefix, nvpair_t *nvp)
498 {
499         char buf[MAXBUF];
500         int buflen = sizeof (buf);
501         const char *name;
502         int64_t *i64p;
503         uint_t nelem;
504         uint_t i;
505         char *p;
506         int n;
507
508         assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT64_ARRAY));
509
510         name = nvpair_name(nvp);
511         (void) nvpair_value_int64_array(nvp, &i64p, &nelem);
512         for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
513                 n = snprintf(p, buflen, "%lld ", (u_longlong_t)i64p[i]);
514                 if ((n < 0) || (n >= buflen))
515                         return (_zed_event_add_array_err(eid, name));
516                 p += n;
517                 buflen -= n;
518         }
519         if (nelem > 0)
520                 *--p = '\0';
521
522         return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
523 }
524
525 static int
526 _zed_event_add_uint64_array(uint64_t eid, zed_strings_t *zsp,
527     const char *prefix, nvpair_t *nvp)
528 {
529         char buf[MAXBUF];
530         int buflen = sizeof (buf);
531         const char *name;
532         const char *fmt;
533         uint64_t *u64p;
534         uint_t nelem;
535         uint_t i;
536         char *p;
537         int n;
538
539         assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY));
540
541         name = nvpair_name(nvp);
542         fmt = _zed_event_value_is_hex(name) ? "0x%.16llX " : "%llu ";
543         (void) nvpair_value_uint64_array(nvp, &u64p, &nelem);
544         for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
545                 n = snprintf(p, buflen, fmt, (u_longlong_t)u64p[i]);
546                 if ((n < 0) || (n >= buflen))
547                         return (_zed_event_add_array_err(eid, name));
548                 p += n;
549                 buflen -= n;
550         }
551         if (nelem > 0)
552                 *--p = '\0';
553
554         return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
555 }
556
557 static int
558 _zed_event_add_string_array(uint64_t eid, zed_strings_t *zsp,
559     const char *prefix, nvpair_t *nvp)
560 {
561         char buf[MAXBUF];
562         int buflen = sizeof (buf);
563         const char *name;
564         char **strp;
565         uint_t nelem;
566         uint_t i;
567         char *p;
568         int n;
569
570         assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY));
571
572         name = nvpair_name(nvp);
573         (void) nvpair_value_string_array(nvp, &strp, &nelem);
574         for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
575                 n = snprintf(p, buflen, "%s ", strp[i] ? strp[i] : "<NULL>");
576                 if ((n < 0) || (n >= buflen))
577                         return (_zed_event_add_array_err(eid, name));
578                 p += n;
579                 buflen -= n;
580         }
581         if (nelem > 0)
582                 *--p = '\0';
583
584         return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
585 }
586
587 /*
588  * Convert the nvpair [nvp] to a string which is added to the environment
589  * of the child process.
590  * Return 0 on success, -1 on error.
591  *
592  * FIXME: Refactor with cmd/zpool/zpool_main.c:zpool_do_events_nvprint()?
593  */
594 static void
595 _zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp)
596 {
597         const char *name;
598         data_type_t type;
599         const char *prefix = ZEVENT_VAR_PREFIX;
600         boolean_t b;
601         double d;
602         uint8_t i8;
603         uint16_t i16;
604         uint32_t i32;
605         uint64_t i64;
606         char *str;
607
608         assert(zsp != NULL);
609         assert(nvp != NULL);
610
611         name = nvpair_name(nvp);
612         type = nvpair_type(nvp);
613
614         switch (type) {
615         case DATA_TYPE_BOOLEAN:
616                 _zed_event_add_var(eid, zsp, prefix, name, "%s", "1");
617                 break;
618         case DATA_TYPE_BOOLEAN_VALUE:
619                 (void) nvpair_value_boolean_value(nvp, &b);
620                 _zed_event_add_var(eid, zsp, prefix, name, "%s", b ? "1" : "0");
621                 break;
622         case DATA_TYPE_BYTE:
623                 (void) nvpair_value_byte(nvp, &i8);
624                 _zed_event_add_var(eid, zsp, prefix, name, "%d", i8);
625                 break;
626         case DATA_TYPE_INT8:
627                 (void) nvpair_value_int8(nvp, (int8_t *)&i8);
628                 _zed_event_add_var(eid, zsp, prefix, name, "%d", i8);
629                 break;
630         case DATA_TYPE_UINT8:
631                 (void) nvpair_value_uint8(nvp, &i8);
632                 _zed_event_add_var(eid, zsp, prefix, name, "%u", i8);
633                 break;
634         case DATA_TYPE_INT16:
635                 (void) nvpair_value_int16(nvp, (int16_t *)&i16);
636                 _zed_event_add_var(eid, zsp, prefix, name, "%d", i16);
637                 break;
638         case DATA_TYPE_UINT16:
639                 (void) nvpair_value_uint16(nvp, &i16);
640                 _zed_event_add_var(eid, zsp, prefix, name, "%u", i16);
641                 break;
642         case DATA_TYPE_INT32:
643                 (void) nvpair_value_int32(nvp, (int32_t *)&i32);
644                 _zed_event_add_var(eid, zsp, prefix, name, "%d", i32);
645                 break;
646         case DATA_TYPE_UINT32:
647                 (void) nvpair_value_uint32(nvp, &i32);
648                 _zed_event_add_var(eid, zsp, prefix, name, "%u", i32);
649                 break;
650         case DATA_TYPE_INT64:
651                 (void) nvpair_value_int64(nvp, (int64_t *)&i64);
652                 _zed_event_add_var(eid, zsp, prefix, name,
653                     "%lld", (longlong_t)i64);
654                 break;
655         case DATA_TYPE_UINT64:
656                 (void) nvpair_value_uint64(nvp, &i64);
657                 _zed_event_add_var(eid, zsp, prefix, name,
658                     (_zed_event_value_is_hex(name) ? "0x%.16llX" : "%llu"),
659                     (u_longlong_t)i64);
660                 /*
661                  * shadow readable strings for vdev state pairs
662                  */
663                 if (strcmp(name, FM_EREPORT_PAYLOAD_ZFS_VDEV_STATE) == 0 ||
664                     strcmp(name, FM_EREPORT_PAYLOAD_ZFS_VDEV_LASTSTATE) == 0) {
665                         char alt[32];
666
667                         (void) snprintf(alt, sizeof (alt), "%s_str", name);
668                         _zed_event_add_var(eid, zsp, prefix, alt, "%s",
669                             zpool_state_to_name(i64, VDEV_AUX_NONE));
670                 } else
671                 /*
672                  * shadow readable strings for pool state
673                  */
674                 if (strcmp(name, FM_EREPORT_PAYLOAD_ZFS_POOL_STATE) == 0) {
675                         char alt[32];
676
677                         (void) snprintf(alt, sizeof (alt), "%s_str", name);
678                         _zed_event_add_var(eid, zsp, prefix, alt, "%s",
679                             zpool_pool_state_to_name(i64));
680                 }
681                 break;
682         case DATA_TYPE_DOUBLE:
683                 (void) nvpair_value_double(nvp, &d);
684                 _zed_event_add_var(eid, zsp, prefix, name, "%g", d);
685                 break;
686         case DATA_TYPE_HRTIME:
687                 (void) nvpair_value_hrtime(nvp, (hrtime_t *)&i64);
688                 _zed_event_add_var(eid, zsp, prefix, name,
689                     "%llu", (u_longlong_t)i64);
690                 break;
691         case DATA_TYPE_NVLIST:
692                 _zed_event_add_var(eid, zsp, prefix, name,
693                     "%s", "_NOT_IMPLEMENTED_");                 /* FIXME */
694                 break;
695         case DATA_TYPE_STRING:
696                 (void) nvpair_value_string(nvp, &str);
697                 _zed_event_add_var(eid, zsp, prefix, name,
698                     "%s", (str ? str : "<NULL>"));
699                 break;
700         case DATA_TYPE_BOOLEAN_ARRAY:
701                 _zed_event_add_var(eid, zsp, prefix, name,
702                     "%s", "_NOT_IMPLEMENTED_");                 /* FIXME */
703                 break;
704         case DATA_TYPE_BYTE_ARRAY:
705                 _zed_event_add_var(eid, zsp, prefix, name,
706                     "%s", "_NOT_IMPLEMENTED_");                 /* FIXME */
707                 break;
708         case DATA_TYPE_INT8_ARRAY:
709                 _zed_event_add_int8_array(eid, zsp, prefix, nvp);
710                 break;
711         case DATA_TYPE_UINT8_ARRAY:
712                 _zed_event_add_uint8_array(eid, zsp, prefix, nvp);
713                 break;
714         case DATA_TYPE_INT16_ARRAY:
715                 _zed_event_add_int16_array(eid, zsp, prefix, nvp);
716                 break;
717         case DATA_TYPE_UINT16_ARRAY:
718                 _zed_event_add_uint16_array(eid, zsp, prefix, nvp);
719                 break;
720         case DATA_TYPE_INT32_ARRAY:
721                 _zed_event_add_int32_array(eid, zsp, prefix, nvp);
722                 break;
723         case DATA_TYPE_UINT32_ARRAY:
724                 _zed_event_add_uint32_array(eid, zsp, prefix, nvp);
725                 break;
726         case DATA_TYPE_INT64_ARRAY:
727                 _zed_event_add_int64_array(eid, zsp, prefix, nvp);
728                 break;
729         case DATA_TYPE_UINT64_ARRAY:
730                 _zed_event_add_uint64_array(eid, zsp, prefix, nvp);
731                 break;
732         case DATA_TYPE_STRING_ARRAY:
733                 _zed_event_add_string_array(eid, zsp, prefix, nvp);
734                 break;
735         case DATA_TYPE_NVLIST_ARRAY:
736                 _zed_event_add_var(eid, zsp, prefix, name,
737                     "%s", "_NOT_IMPLEMENTED_");                 /* FIXME */
738                 break;
739         default:
740                 errno = EINVAL;
741                 zed_log_msg(LOG_WARNING,
742                     "Failed to convert nvpair \"%s\" for eid=%llu: "
743                     "Unrecognized type=%u", name, eid, (unsigned int) type);
744                 break;
745         }
746 }
747
748 /*
749  * Restrict various environment variables to safe and sane values
750  * when constructing the environment for the child process, unless
751  * we're running with a custom $PATH (like under the ZFS test suite).
752  *
753  * Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1.
754  */
755 static void
756 _zed_event_add_env_restrict(uint64_t eid, zed_strings_t *zsp,
757     const char *path)
758 {
759         const char *env_restrict[][2] = {
760                 { "IFS",                " \t\n" },
761                 { "PATH",               _PATH_STDPATH },
762                 { "ZDB",                SBINDIR "/zdb" },
763                 { "ZED",                SBINDIR "/zed" },
764                 { "ZFS",                SBINDIR "/zfs" },
765                 { "ZINJECT",            SBINDIR "/zinject" },
766                 { "ZPOOL",              SBINDIR "/zpool" },
767                 { "ZFS_ALIAS",          ZFS_META_ALIAS },
768                 { "ZFS_VERSION",        ZFS_META_VERSION },
769                 { "ZFS_RELEASE",        ZFS_META_RELEASE },
770                 { NULL,                 NULL }
771         };
772
773         /*
774          * If we have a custom $PATH, use the default ZFS binary locations
775          * instead of the hard-coded ones.
776          */
777         const char *env_path[][2] = {
778                 { "IFS",                " \t\n" },
779                 { "PATH",               NULL }, /* $PATH copied in later on */
780                 { "ZDB",                "zdb" },
781                 { "ZED",                "zed" },
782                 { "ZFS",                "zfs" },
783                 { "ZINJECT",            "zinject" },
784                 { "ZPOOL",              "zpool" },
785                 { "ZFS_ALIAS",          ZFS_META_ALIAS },
786                 { "ZFS_VERSION",        ZFS_META_VERSION },
787                 { "ZFS_RELEASE",        ZFS_META_RELEASE },
788                 { NULL,                 NULL }
789         };
790         const char *(*pa)[2];
791
792         assert(zsp != NULL);
793
794         pa = path != NULL ? env_path : env_restrict;
795
796         for (; *(*pa); pa++) {
797                 /* Use our custom $PATH if we have one */
798                 if (path != NULL && strcmp((*pa)[0], "PATH") == 0)
799                         (*pa)[1] = path;
800
801                 _zed_event_add_var(eid, zsp, NULL, (*pa)[0], "%s", (*pa)[1]);
802         }
803 }
804
805 /*
806  * Preserve specified variables from the parent environment
807  * when constructing the environment for the child process.
808  *
809  * Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1.
810  */
811 static void
812 _zed_event_add_env_preserve(uint64_t eid, zed_strings_t *zsp)
813 {
814         const char *env_preserve[] = {
815                 "TZ",
816                 NULL
817         };
818         const char **keyp;
819         const char *val;
820
821         assert(zsp != NULL);
822
823         for (keyp = env_preserve; *keyp; keyp++) {
824                 if ((val = getenv(*keyp)))
825                         _zed_event_add_var(eid, zsp, NULL, *keyp, "%s", val);
826         }
827 }
828
829 /*
830  * Compute the "subclass" by removing the first 3 components of [class]
831  * (which will always be of the form "*.fs.zfs").  Return a pointer inside
832  * the string [class], or NULL if insufficient components exist.
833  */
834 static const char *
835 _zed_event_get_subclass(const char *class)
836 {
837         const char *p;
838         int i;
839
840         if (!class)
841                 return (NULL);
842
843         p = class;
844         for (i = 0; i < 3; i++) {
845                 p = strchr(p, '.');
846                 if (!p)
847                         break;
848                 p++;
849         }
850         return (p);
851 }
852
853 /*
854  * Convert the zevent time from a 2-element array of 64b integers
855  * into a more convenient form:
856  * - TIME_SECS is the second component of the time.
857  * - TIME_NSECS is the nanosecond component of the time.
858  * - TIME_STRING is an almost-RFC3339-compliant string representation.
859  */
860 static void
861 _zed_event_add_time_strings(uint64_t eid, zed_strings_t *zsp, int64_t etime[])
862 {
863         struct tm *stp;
864         char buf[32];
865
866         assert(zsp != NULL);
867         assert(etime != NULL);
868
869         _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_SECS",
870             "%lld", (long long int) etime[0]);
871         _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_NSECS",
872             "%lld", (long long int) etime[1]);
873
874         if (!(stp = localtime((const time_t *) &etime[0]))) {
875                 zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",
876                     ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "localtime error");
877         } else if (!strftime(buf, sizeof (buf), "%Y-%m-%d %H:%M:%S%z", stp)) {
878                 zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",
879                     ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "strftime error");
880         } else {
881                 _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_STRING",
882                     "%s", buf);
883         }
884 }
885
886 /*
887  * Service the next zevent, blocking until one is available.
888  */
889 int
890 zed_event_service(struct zed_conf *zcp)
891 {
892         nvlist_t *nvl;
893         nvpair_t *nvp;
894         int n_dropped;
895         zed_strings_t *zsp;
896         uint64_t eid;
897         int64_t *etime;
898         uint_t nelem;
899         char *class;
900         const char *subclass;
901         int rv;
902
903         if (!zcp) {
904                 errno = EINVAL;
905                 zed_log_msg(LOG_ERR, "Failed to service zevent: %s",
906                     strerror(errno));
907                 return (EINVAL);
908         }
909         rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONE,
910             zcp->zevent_fd);
911
912         if ((rv != 0) || !nvl)
913                 return (errno);
914
915         if (n_dropped > 0) {
916                 zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
917                 /*
918                  * FIXME: Increase max size of event nvlist in
919                  * /sys/module/zfs/parameters/zfs_zevent_len_max ?
920                  */
921         }
922         if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
923                 zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
924         } else if (nvlist_lookup_int64_array(
925             nvl, "time", &etime, &nelem) != 0) {
926                 zed_log_msg(LOG_WARNING,
927                     "Failed to lookup zevent time (eid=%llu)", eid);
928         } else if (nelem != 2) {
929                 zed_log_msg(LOG_WARNING,
930                     "Failed to lookup zevent time (eid=%llu, nelem=%u)",
931                     eid, nelem);
932         } else if (nvlist_lookup_string(nvl, "class", &class) != 0) {
933                 zed_log_msg(LOG_WARNING,
934                     "Failed to lookup zevent class (eid=%llu)", eid);
935         } else {
936                 /* let internal modules see this event first */
937                 zfs_agent_post_event(class, NULL, nvl);
938
939                 zsp = zed_strings_create();
940
941                 nvp = NULL;
942                 while ((nvp = nvlist_next_nvpair(nvl, nvp)))
943                         _zed_event_add_nvpair(eid, zsp, nvp);
944
945                 _zed_event_add_env_restrict(eid, zsp, zcp->path);
946                 _zed_event_add_env_preserve(eid, zsp);
947
948                 _zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "PID",
949                     "%d", (int)getpid());
950                 _zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "ZEDLET_DIR",
951                     "%s", zcp->zedlet_dir);
952                 subclass = _zed_event_get_subclass(class);
953                 _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "SUBCLASS",
954                     "%s", (subclass ? subclass : class));
955
956                 _zed_event_add_time_strings(eid, zsp, etime);
957
958                 zed_exec_process(eid, class, subclass, zcp, zsp);
959
960                 zed_conf_write_state(zcp, eid, etime);
961
962                 zed_strings_destroy(zsp);
963         }
964         nvlist_free(nvl);
965         return (0);
966 }