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