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