ZTS: Add additional exceptions
[zfs.git] / cmd / zed / zed_conf.c
blob29de27c77c345ae22b1f41c3b227958932c2d1e6
1 /*
2 * This file is part of the ZFS Event Daemon (ZED).
4 * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
5 * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
6 * Refer to the OpenZFS git commit log for authoritative copyright attribution.
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.
15 #include <assert.h>
16 #include <ctype.h>
17 #include <dirent.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <libgen.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/uio.h>
28 #include <unistd.h>
29 #include "zed.h"
30 #include "zed_conf.h"
31 #include "zed_file.h"
32 #include "zed_log.h"
33 #include "zed_strings.h"
36 * Initialise the configuration with default values.
38 void
39 zed_conf_init(struct zed_conf *zcp)
41 memset(zcp, 0, sizeof (*zcp));
43 /* zcp->zfs_hdl opened in zed_event_init() */
44 /* zcp->zedlets created in zed_conf_scan_dir() */
46 zcp->pid_fd = -1; /* opened in zed_conf_write_pid() */
47 zcp->state_fd = -1; /* opened in zed_conf_open_state() */
48 zcp->zevent_fd = -1; /* opened in zed_event_init() */
50 zcp->max_jobs = 16;
51 zcp->max_zevent_buf_len = 1 << 20;
53 if (!(zcp->pid_file = strdup(ZED_PID_FILE)) ||
54 !(zcp->zedlet_dir = strdup(ZED_ZEDLET_DIR)) ||
55 !(zcp->state_file = strdup(ZED_STATE_FILE)))
56 zed_log_die("Failed to create conf: %s", strerror(errno));
60 * Destroy the configuration [zcp].
62 * Note: zfs_hdl & zevent_fd are destroyed via zed_event_fini().
64 void
65 zed_conf_destroy(struct zed_conf *zcp)
67 if (zcp->state_fd >= 0) {
68 if (close(zcp->state_fd) < 0)
69 zed_log_msg(LOG_WARNING,
70 "Failed to close state file \"%s\": %s",
71 zcp->state_file, strerror(errno));
72 zcp->state_fd = -1;
74 if (zcp->pid_file) {
75 if ((unlink(zcp->pid_file) < 0) && (errno != ENOENT))
76 zed_log_msg(LOG_WARNING,
77 "Failed to remove PID file \"%s\": %s",
78 zcp->pid_file, strerror(errno));
80 if (zcp->pid_fd >= 0) {
81 if (close(zcp->pid_fd) < 0)
82 zed_log_msg(LOG_WARNING,
83 "Failed to close PID file \"%s\": %s",
84 zcp->pid_file, strerror(errno));
85 zcp->pid_fd = -1;
87 if (zcp->pid_file) {
88 free(zcp->pid_file);
89 zcp->pid_file = NULL;
91 if (zcp->zedlet_dir) {
92 free(zcp->zedlet_dir);
93 zcp->zedlet_dir = NULL;
95 if (zcp->state_file) {
96 free(zcp->state_file);
97 zcp->state_file = NULL;
99 if (zcp->zedlets) {
100 zed_strings_destroy(zcp->zedlets);
101 zcp->zedlets = NULL;
106 * Display command-line help and exit.
108 * If [got_err] is 0, output to stdout and exit normally;
109 * otherwise, output to stderr and exit with a failure status.
111 static void
112 _zed_conf_display_help(const char *prog, boolean_t got_err)
114 struct opt { const char *o, *d, *v; };
116 FILE *fp = got_err ? stderr : stdout;
118 struct opt *oo;
119 struct opt iopts[] = {
120 { .o = "-h", .d = "Display help" },
121 { .o = "-L", .d = "Display license information" },
122 { .o = "-V", .d = "Display version information" },
125 struct opt nopts[] = {
126 { .o = "-v", .d = "Be verbose" },
127 { .o = "-f", .d = "Force daemon to run" },
128 { .o = "-F", .d = "Run daemon in the foreground" },
129 { .o = "-I",
130 .d = "Idle daemon until kernel module is (re)loaded" },
131 { .o = "-M", .d = "Lock all pages in memory" },
132 { .o = "-P", .d = "$PATH for ZED to use (only used by ZTS)" },
133 { .o = "-Z", .d = "Zero state file" },
136 struct opt vopts[] = {
137 { .o = "-d DIR", .d = "Read enabled ZEDLETs from DIR.",
138 .v = ZED_ZEDLET_DIR },
139 { .o = "-p FILE", .d = "Write daemon's PID to FILE.",
140 .v = ZED_PID_FILE },
141 { .o = "-s FILE", .d = "Write daemon's state to FILE.",
142 .v = ZED_STATE_FILE },
143 { .o = "-j JOBS", .d = "Start at most JOBS at once.",
144 .v = "16" },
145 { .o = "-b LEN", .d = "Cap kernel event buffer at LEN entries.",
146 .v = "1048576" },
150 fprintf(fp, "Usage: %s [OPTION]...\n", (prog ? prog : "zed"));
151 fprintf(fp, "\n");
152 for (oo = iopts; oo->o; ++oo)
153 fprintf(fp, " %*s %s\n", -8, oo->o, oo->d);
154 fprintf(fp, "\n");
155 for (oo = nopts; oo->o; ++oo)
156 fprintf(fp, " %*s %s\n", -8, oo->o, oo->d);
157 fprintf(fp, "\n");
158 for (oo = vopts; oo->o; ++oo)
159 fprintf(fp, " %*s %s [%s]\n", -8, oo->o, oo->d, oo->v);
160 fprintf(fp, "\n");
162 exit(got_err ? EXIT_FAILURE : EXIT_SUCCESS);
166 * Display license information to stdout and exit.
168 static void
169 _zed_conf_display_license(void)
171 printf(
172 "The ZFS Event Daemon (ZED) is distributed under the terms of the\n"
173 " Common Development and Distribution License (CDDL-1.0)\n"
174 " <http://opensource.org/licenses/CDDL-1.0>.\n"
175 "\n"
176 "Developed at Lawrence Livermore National Laboratory"
177 " (LLNL-CODE-403049).\n"
178 "\n");
180 exit(EXIT_SUCCESS);
184 * Display version information to stdout and exit.
186 static void
187 _zed_conf_display_version(void)
189 printf("%s-%s-%s\n",
190 ZFS_META_NAME, ZFS_META_VERSION, ZFS_META_RELEASE);
192 exit(EXIT_SUCCESS);
196 * Copy the [path] string to the [resultp] ptr.
197 * If [path] is not an absolute path, prefix it with the current working dir.
198 * If [resultp] is non-null, free its existing string before assignment.
200 static void
201 _zed_conf_parse_path(char **resultp, const char *path)
203 char buf[PATH_MAX];
205 assert(resultp != NULL);
206 assert(path != NULL);
208 if (*resultp)
209 free(*resultp);
211 if (path[0] == '/') {
212 *resultp = strdup(path);
213 } else {
214 if (!getcwd(buf, sizeof (buf)))
215 zed_log_die("Failed to get current working dir: %s",
216 strerror(errno));
218 if (strlcat(buf, "/", sizeof (buf)) >= sizeof (buf) ||
219 strlcat(buf, path, sizeof (buf)) >= sizeof (buf))
220 zed_log_die("Failed to copy path: %s",
221 strerror(ENAMETOOLONG));
223 *resultp = strdup(buf);
226 if (!*resultp)
227 zed_log_die("Failed to copy path: %s", strerror(ENOMEM));
231 * Parse the command-line options into the configuration [zcp].
233 void
234 zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
236 const char * const opts = ":hLVd:p:P:s:vfFMZIj:b:";
237 int opt;
238 unsigned long raw;
240 if (!zcp || !argv || !argv[0])
241 zed_log_die("Failed to parse options: Internal error");
243 opterr = 0; /* suppress default getopt err msgs */
245 while ((opt = getopt(argc, argv, opts)) != -1) {
246 switch (opt) {
247 case 'h':
248 _zed_conf_display_help(argv[0], B_FALSE);
249 break;
250 case 'L':
251 _zed_conf_display_license();
252 break;
253 case 'V':
254 _zed_conf_display_version();
255 break;
256 case 'd':
257 _zed_conf_parse_path(&zcp->zedlet_dir, optarg);
258 break;
259 case 'I':
260 zcp->do_idle = 1;
261 break;
262 case 'p':
263 _zed_conf_parse_path(&zcp->pid_file, optarg);
264 break;
265 case 'P':
266 _zed_conf_parse_path(&zcp->path, optarg);
267 break;
268 case 's':
269 _zed_conf_parse_path(&zcp->state_file, optarg);
270 break;
271 case 'v':
272 zcp->do_verbose = 1;
273 break;
274 case 'f':
275 zcp->do_force = 1;
276 break;
277 case 'F':
278 zcp->do_foreground = 1;
279 break;
280 case 'M':
281 zcp->do_memlock = 1;
282 break;
283 case 'Z':
284 zcp->do_zero = 1;
285 break;
286 case 'j':
287 errno = 0;
288 raw = strtoul(optarg, NULL, 0);
289 if (errno == ERANGE || raw > INT16_MAX) {
290 zed_log_die("%lu is too many jobs", raw);
291 } if (raw == 0) {
292 zed_log_die("0 jobs makes no sense");
293 } else {
294 zcp->max_jobs = raw;
296 break;
297 case 'b':
298 errno = 0;
299 raw = strtoul(optarg, NULL, 0);
300 if (errno == ERANGE || raw > INT32_MAX) {
301 zed_log_die("%lu is too large", raw);
302 } if (raw == 0) {
303 zcp->max_zevent_buf_len = INT32_MAX;
304 } else {
305 zcp->max_zevent_buf_len = raw;
307 break;
308 case '?':
309 default:
310 if (optopt == '?')
311 _zed_conf_display_help(argv[0], B_FALSE);
313 fprintf(stderr, "%s: Invalid option '-%c'\n\n",
314 argv[0], optopt);
315 _zed_conf_display_help(argv[0], B_TRUE);
316 break;
322 * Scan the [zcp] zedlet_dir for files to exec based on the event class.
323 * Files must be executable by user, but not writable by group or other.
324 * Dotfiles are ignored.
326 * Return 0 on success with an updated set of zedlets,
327 * or -1 on error with errno set.
330 zed_conf_scan_dir(struct zed_conf *zcp)
332 zed_strings_t *zedlets;
333 DIR *dirp;
334 struct dirent *direntp;
335 char pathname[PATH_MAX];
336 struct stat st;
337 int n;
339 if (!zcp) {
340 errno = EINVAL;
341 zed_log_msg(LOG_ERR, "Failed to scan zedlet dir: %s",
342 strerror(errno));
343 return (-1);
345 zedlets = zed_strings_create();
346 if (!zedlets) {
347 errno = ENOMEM;
348 zed_log_msg(LOG_WARNING, "Failed to scan dir \"%s\": %s",
349 zcp->zedlet_dir, strerror(errno));
350 return (-1);
352 dirp = opendir(zcp->zedlet_dir);
353 if (!dirp) {
354 int errno_bak = errno;
355 zed_log_msg(LOG_WARNING, "Failed to open dir \"%s\": %s",
356 zcp->zedlet_dir, strerror(errno));
357 zed_strings_destroy(zedlets);
358 errno = errno_bak;
359 return (-1);
361 while ((direntp = readdir(dirp))) {
362 if (direntp->d_name[0] == '.')
363 continue;
365 n = snprintf(pathname, sizeof (pathname),
366 "%s/%s", zcp->zedlet_dir, direntp->d_name);
367 if ((n < 0) || (n >= sizeof (pathname))) {
368 zed_log_msg(LOG_WARNING, "Failed to stat \"%s\": %s",
369 direntp->d_name, strerror(ENAMETOOLONG));
370 continue;
372 if (stat(pathname, &st) < 0) {
373 zed_log_msg(LOG_WARNING, "Failed to stat \"%s\": %s",
374 pathname, strerror(errno));
375 continue;
377 if (!S_ISREG(st.st_mode)) {
378 zed_log_msg(LOG_INFO,
379 "Ignoring \"%s\": not a regular file",
380 direntp->d_name);
381 continue;
383 if ((st.st_uid != 0) && !zcp->do_force) {
384 zed_log_msg(LOG_NOTICE,
385 "Ignoring \"%s\": not owned by root",
386 direntp->d_name);
387 continue;
389 if (!(st.st_mode & S_IXUSR)) {
390 zed_log_msg(LOG_INFO,
391 "Ignoring \"%s\": not executable by user",
392 direntp->d_name);
393 continue;
395 if ((st.st_mode & S_IWGRP) && !zcp->do_force) {
396 zed_log_msg(LOG_NOTICE,
397 "Ignoring \"%s\": writable by group",
398 direntp->d_name);
399 continue;
401 if ((st.st_mode & S_IWOTH) && !zcp->do_force) {
402 zed_log_msg(LOG_NOTICE,
403 "Ignoring \"%s\": writable by other",
404 direntp->d_name);
405 continue;
407 if (zed_strings_add(zedlets, NULL, direntp->d_name) < 0) {
408 zed_log_msg(LOG_WARNING,
409 "Failed to register \"%s\": %s",
410 direntp->d_name, strerror(errno));
411 continue;
413 if (zcp->do_verbose)
414 zed_log_msg(LOG_INFO,
415 "Registered zedlet \"%s\"", direntp->d_name);
417 if (closedir(dirp) < 0) {
418 int errno_bak = errno;
419 zed_log_msg(LOG_WARNING, "Failed to close dir \"%s\": %s",
420 zcp->zedlet_dir, strerror(errno));
421 zed_strings_destroy(zedlets);
422 errno = errno_bak;
423 return (-1);
425 if (zcp->zedlets)
426 zed_strings_destroy(zcp->zedlets);
428 zcp->zedlets = zedlets;
429 return (0);
433 * Write the PID file specified in [zcp].
434 * Return 0 on success, -1 on error.
436 * This must be called after fork()ing to become a daemon (so the correct PID
437 * is recorded), but before daemonization is complete and the parent process
438 * exits (for synchronization with systemd).
441 zed_conf_write_pid(struct zed_conf *zcp)
443 char buf[PATH_MAX];
444 int n;
445 char *p;
446 mode_t mask;
447 int rv;
449 if (!zcp || !zcp->pid_file) {
450 errno = EINVAL;
451 zed_log_msg(LOG_ERR, "Failed to create PID file: %s",
452 strerror(errno));
453 return (-1);
455 assert(zcp->pid_fd == -1);
457 * Create PID file directory if needed.
459 n = strlcpy(buf, zcp->pid_file, sizeof (buf));
460 if (n >= sizeof (buf)) {
461 errno = ENAMETOOLONG;
462 zed_log_msg(LOG_ERR, "Failed to create PID file: %s",
463 strerror(errno));
464 goto err;
466 p = strrchr(buf, '/');
467 if (p)
468 *p = '\0';
470 if ((mkdirp(buf, 0755) < 0) && (errno != EEXIST)) {
471 zed_log_msg(LOG_ERR, "Failed to create directory \"%s\": %s",
472 buf, strerror(errno));
473 goto err;
476 * Obtain PID file lock.
478 mask = umask(0);
479 umask(mask | 022);
480 zcp->pid_fd = open(zcp->pid_file, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
481 umask(mask);
482 if (zcp->pid_fd < 0) {
483 zed_log_msg(LOG_ERR, "Failed to open PID file \"%s\": %s",
484 zcp->pid_file, strerror(errno));
485 goto err;
487 rv = zed_file_lock(zcp->pid_fd);
488 if (rv < 0) {
489 zed_log_msg(LOG_ERR, "Failed to lock PID file \"%s\": %s",
490 zcp->pid_file, strerror(errno));
491 goto err;
492 } else if (rv > 0) {
493 pid_t pid = zed_file_is_locked(zcp->pid_fd);
494 if (pid < 0) {
495 zed_log_msg(LOG_ERR,
496 "Failed to test lock on PID file \"%s\"",
497 zcp->pid_file);
498 } else if (pid > 0) {
499 zed_log_msg(LOG_ERR,
500 "Found PID %d bound to PID file \"%s\"",
501 pid, zcp->pid_file);
502 } else {
503 zed_log_msg(LOG_ERR,
504 "Inconsistent lock state on PID file \"%s\"",
505 zcp->pid_file);
507 goto err;
510 * Write PID file.
512 n = snprintf(buf, sizeof (buf), "%d\n", (int)getpid());
513 if ((n < 0) || (n >= sizeof (buf))) {
514 errno = ERANGE;
515 zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s",
516 zcp->pid_file, strerror(errno));
517 } else if (write(zcp->pid_fd, buf, n) != n) {
518 zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s",
519 zcp->pid_file, strerror(errno));
520 } else if (fdatasync(zcp->pid_fd) < 0) {
521 zed_log_msg(LOG_ERR, "Failed to sync PID file \"%s\": %s",
522 zcp->pid_file, strerror(errno));
523 } else {
524 return (0);
527 err:
528 if (zcp->pid_fd >= 0) {
529 (void) close(zcp->pid_fd);
530 zcp->pid_fd = -1;
532 return (-1);
536 * Open and lock the [zcp] state_file.
537 * Return 0 on success, -1 on error.
539 * FIXME: Move state information into kernel.
542 zed_conf_open_state(struct zed_conf *zcp)
544 char dirbuf[PATH_MAX];
545 int n;
546 char *p;
547 int rv;
549 if (!zcp || !zcp->state_file) {
550 errno = EINVAL;
551 zed_log_msg(LOG_ERR, "Failed to open state file: %s",
552 strerror(errno));
553 return (-1);
555 n = strlcpy(dirbuf, zcp->state_file, sizeof (dirbuf));
556 if (n >= sizeof (dirbuf)) {
557 errno = ENAMETOOLONG;
558 zed_log_msg(LOG_WARNING, "Failed to open state file: %s",
559 strerror(errno));
560 return (-1);
562 p = strrchr(dirbuf, '/');
563 if (p)
564 *p = '\0';
566 if ((mkdirp(dirbuf, 0755) < 0) && (errno != EEXIST)) {
567 zed_log_msg(LOG_WARNING,
568 "Failed to create directory \"%s\": %s",
569 dirbuf, strerror(errno));
570 return (-1);
572 if (zcp->state_fd >= 0) {
573 if (close(zcp->state_fd) < 0) {
574 zed_log_msg(LOG_WARNING,
575 "Failed to close state file \"%s\": %s",
576 zcp->state_file, strerror(errno));
577 return (-1);
580 if (zcp->do_zero)
581 (void) unlink(zcp->state_file);
583 zcp->state_fd = open(zcp->state_file,
584 O_RDWR | O_CREAT | O_CLOEXEC, 0644);
585 if (zcp->state_fd < 0) {
586 zed_log_msg(LOG_WARNING, "Failed to open state file \"%s\": %s",
587 zcp->state_file, strerror(errno));
588 return (-1);
590 rv = zed_file_lock(zcp->state_fd);
591 if (rv < 0) {
592 zed_log_msg(LOG_WARNING, "Failed to lock state file \"%s\": %s",
593 zcp->state_file, strerror(errno));
594 return (-1);
596 if (rv > 0) {
597 pid_t pid = zed_file_is_locked(zcp->state_fd);
598 if (pid < 0) {
599 zed_log_msg(LOG_WARNING,
600 "Failed to test lock on state file \"%s\"",
601 zcp->state_file);
602 } else if (pid > 0) {
603 zed_log_msg(LOG_WARNING,
604 "Found PID %d bound to state file \"%s\"",
605 pid, zcp->state_file);
606 } else {
607 zed_log_msg(LOG_WARNING,
608 "Inconsistent lock state on state file \"%s\"",
609 zcp->state_file);
611 return (-1);
613 return (0);
617 * Read the opened [zcp] state_file to obtain the eid & etime of the last event
618 * processed. Write the state from the last event to the [eidp] & [etime] args
619 * passed by reference. Note that etime[] is an array of size 2.
620 * Return 0 on success, -1 on error.
623 zed_conf_read_state(struct zed_conf *zcp, uint64_t *eidp, int64_t etime[])
625 ssize_t len;
626 struct iovec iov[3];
627 ssize_t n;
629 if (!zcp || !eidp || !etime) {
630 errno = EINVAL;
631 zed_log_msg(LOG_ERR,
632 "Failed to read state file: %s", strerror(errno));
633 return (-1);
635 if (lseek(zcp->state_fd, 0, SEEK_SET) == (off_t)-1) {
636 zed_log_msg(LOG_WARNING,
637 "Failed to reposition state file offset: %s",
638 strerror(errno));
639 return (-1);
641 len = 0;
642 iov[0].iov_base = eidp;
643 len += iov[0].iov_len = sizeof (*eidp);
644 iov[1].iov_base = &etime[0];
645 len += iov[1].iov_len = sizeof (etime[0]);
646 iov[2].iov_base = &etime[1];
647 len += iov[2].iov_len = sizeof (etime[1]);
649 n = readv(zcp->state_fd, iov, 3);
650 if (n == 0) {
651 *eidp = 0;
652 } else if (n < 0) {
653 zed_log_msg(LOG_WARNING,
654 "Failed to read state file \"%s\": %s",
655 zcp->state_file, strerror(errno));
656 return (-1);
657 } else if (n != len) {
658 errno = EIO;
659 zed_log_msg(LOG_WARNING,
660 "Failed to read state file \"%s\": Read %zd of %zd bytes",
661 zcp->state_file, n, len);
662 return (-1);
664 return (0);
668 * Write the [eid] & [etime] of the last processed event to the opened
669 * [zcp] state_file. Note that etime[] is an array of size 2.
670 * Return 0 on success, -1 on error.
673 zed_conf_write_state(struct zed_conf *zcp, uint64_t eid, int64_t etime[])
675 ssize_t len;
676 struct iovec iov[3];
677 ssize_t n;
679 if (!zcp) {
680 errno = EINVAL;
681 zed_log_msg(LOG_ERR,
682 "Failed to write state file: %s", strerror(errno));
683 return (-1);
685 if (lseek(zcp->state_fd, 0, SEEK_SET) == (off_t)-1) {
686 zed_log_msg(LOG_WARNING,
687 "Failed to reposition state file offset: %s",
688 strerror(errno));
689 return (-1);
691 len = 0;
692 iov[0].iov_base = &eid;
693 len += iov[0].iov_len = sizeof (eid);
694 iov[1].iov_base = &etime[0];
695 len += iov[1].iov_len = sizeof (etime[0]);
696 iov[2].iov_base = &etime[1];
697 len += iov[2].iov_len = sizeof (etime[1]);
699 n = writev(zcp->state_fd, iov, 3);
700 if (n < 0) {
701 zed_log_msg(LOG_WARNING,
702 "Failed to write state file \"%s\": %s",
703 zcp->state_file, strerror(errno));
704 return (-1);
706 if (n != len) {
707 errno = EIO;
708 zed_log_msg(LOG_WARNING,
709 "Failed to write state file \"%s\": Wrote %zd of %zd bytes",
710 zcp->state_file, n, len);
711 return (-1);
713 if (fdatasync(zcp->state_fd) < 0) {
714 zed_log_msg(LOG_WARNING,
715 "Failed to sync state file \"%s\": %s",
716 zcp->state_file, strerror(errno));
717 return (-1);
719 return (0);