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.
25 #include <sys/types.h>
33 #include "zed_strings.h"
36 * Initialise the configuration with default values.
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() */
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().
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
));
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
));
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
;
100 zed_strings_destroy(zcp
->zedlets
);
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.
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
;
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" },
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.",
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.",
145 { .o
= "-b LEN", .d
= "Cap kernel event buffer at LEN entries.",
150 fprintf(fp
, "Usage: %s [OPTION]...\n", (prog
? prog
: "zed"));
152 for (oo
= iopts
; oo
->o
; ++oo
)
153 fprintf(fp
, " %*s %s\n", -8, oo
->o
, oo
->d
);
155 for (oo
= nopts
; oo
->o
; ++oo
)
156 fprintf(fp
, " %*s %s\n", -8, oo
->o
, oo
->d
);
158 for (oo
= vopts
; oo
->o
; ++oo
)
159 fprintf(fp
, " %*s %s [%s]\n", -8, oo
->o
, oo
->d
, oo
->v
);
162 exit(got_err
? EXIT_FAILURE
: EXIT_SUCCESS
);
166 * Display license information to stdout and exit.
169 _zed_conf_display_license(void)
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"
176 "Developed at Lawrence Livermore National Laboratory"
177 " (LLNL-CODE-403049).\n"
184 * Display version information to stdout and exit.
187 _zed_conf_display_version(void)
190 ZFS_META_NAME
, ZFS_META_VERSION
, ZFS_META_RELEASE
);
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.
201 _zed_conf_parse_path(char **resultp
, const char *path
)
205 assert(resultp
!= NULL
);
206 assert(path
!= NULL
);
211 if (path
[0] == '/') {
212 *resultp
= strdup(path
);
214 if (!getcwd(buf
, sizeof (buf
)))
215 zed_log_die("Failed to get current working dir: %s",
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
);
227 zed_log_die("Failed to copy path: %s", strerror(ENOMEM
));
231 * Parse the command-line options into the configuration [zcp].
234 zed_conf_parse_opts(struct zed_conf
*zcp
, int argc
, char **argv
)
236 const char * const opts
= ":hLVd:p:P:s:vfFMZIj:b:";
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) {
248 _zed_conf_display_help(argv
[0], B_FALSE
);
251 _zed_conf_display_license();
254 _zed_conf_display_version();
257 _zed_conf_parse_path(&zcp
->zedlet_dir
, optarg
);
263 _zed_conf_parse_path(&zcp
->pid_file
, optarg
);
266 _zed_conf_parse_path(&zcp
->path
, optarg
);
269 _zed_conf_parse_path(&zcp
->state_file
, optarg
);
278 zcp
->do_foreground
= 1;
288 raw
= strtoul(optarg
, NULL
, 0);
289 if (errno
== ERANGE
|| raw
> INT16_MAX
) {
290 zed_log_die("%lu is too many jobs", raw
);
292 zed_log_die("0 jobs makes no sense");
299 raw
= strtoul(optarg
, NULL
, 0);
300 if (errno
== ERANGE
|| raw
> INT32_MAX
) {
301 zed_log_die("%lu is too large", raw
);
303 zcp
->max_zevent_buf_len
= INT32_MAX
;
305 zcp
->max_zevent_buf_len
= raw
;
311 _zed_conf_display_help(argv
[0], B_FALSE
);
313 fprintf(stderr
, "%s: Invalid option '-%c'\n\n",
315 _zed_conf_display_help(argv
[0], B_TRUE
);
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
;
334 struct dirent
*direntp
;
335 char pathname
[PATH_MAX
];
341 zed_log_msg(LOG_ERR
, "Failed to scan zedlet dir: %s",
345 zedlets
= zed_strings_create();
348 zed_log_msg(LOG_WARNING
, "Failed to scan dir \"%s\": %s",
349 zcp
->zedlet_dir
, strerror(errno
));
352 dirp
= opendir(zcp
->zedlet_dir
);
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
);
361 while ((direntp
= readdir(dirp
))) {
362 if (direntp
->d_name
[0] == '.')
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
));
372 if (stat(pathname
, &st
) < 0) {
373 zed_log_msg(LOG_WARNING
, "Failed to stat \"%s\": %s",
374 pathname
, strerror(errno
));
377 if (!S_ISREG(st
.st_mode
)) {
378 zed_log_msg(LOG_INFO
,
379 "Ignoring \"%s\": not a regular file",
383 if ((st
.st_uid
!= 0) && !zcp
->do_force
) {
384 zed_log_msg(LOG_NOTICE
,
385 "Ignoring \"%s\": not owned by root",
389 if (!(st
.st_mode
& S_IXUSR
)) {
390 zed_log_msg(LOG_INFO
,
391 "Ignoring \"%s\": not executable by user",
395 if ((st
.st_mode
& S_IWGRP
) && !zcp
->do_force
) {
396 zed_log_msg(LOG_NOTICE
,
397 "Ignoring \"%s\": writable by group",
401 if ((st
.st_mode
& S_IWOTH
) && !zcp
->do_force
) {
402 zed_log_msg(LOG_NOTICE
,
403 "Ignoring \"%s\": writable by other",
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
));
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
);
426 zed_strings_destroy(zcp
->zedlets
);
428 zcp
->zedlets
= zedlets
;
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
)
449 if (!zcp
|| !zcp
->pid_file
) {
451 zed_log_msg(LOG_ERR
, "Failed to create PID file: %s",
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",
466 p
= strrchr(buf
, '/');
470 if ((mkdirp(buf
, 0755) < 0) && (errno
!= EEXIST
)) {
471 zed_log_msg(LOG_ERR
, "Failed to create directory \"%s\": %s",
472 buf
, strerror(errno
));
476 * Obtain PID file lock.
480 zcp
->pid_fd
= open(zcp
->pid_file
, O_RDWR
| O_CREAT
| O_CLOEXEC
, 0644);
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
));
487 rv
= zed_file_lock(zcp
->pid_fd
);
489 zed_log_msg(LOG_ERR
, "Failed to lock PID file \"%s\": %s",
490 zcp
->pid_file
, strerror(errno
));
493 pid_t pid
= zed_file_is_locked(zcp
->pid_fd
);
496 "Failed to test lock on PID file \"%s\"",
498 } else if (pid
> 0) {
500 "Found PID %d bound to PID file \"%s\"",
504 "Inconsistent lock state on PID file \"%s\"",
512 n
= snprintf(buf
, sizeof (buf
), "%d\n", (int)getpid());
513 if ((n
< 0) || (n
>= sizeof (buf
))) {
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
));
528 if (zcp
->pid_fd
>= 0) {
529 (void) close(zcp
->pid_fd
);
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
];
549 if (!zcp
|| !zcp
->state_file
) {
551 zed_log_msg(LOG_ERR
, "Failed to open state file: %s",
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",
562 p
= strrchr(dirbuf
, '/');
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
));
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
));
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
));
590 rv
= zed_file_lock(zcp
->state_fd
);
592 zed_log_msg(LOG_WARNING
, "Failed to lock state file \"%s\": %s",
593 zcp
->state_file
, strerror(errno
));
597 pid_t pid
= zed_file_is_locked(zcp
->state_fd
);
599 zed_log_msg(LOG_WARNING
,
600 "Failed to test lock on state file \"%s\"",
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
);
607 zed_log_msg(LOG_WARNING
,
608 "Inconsistent lock state on state file \"%s\"",
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
[])
629 if (!zcp
|| !eidp
|| !etime
) {
632 "Failed to read state file: %s", strerror(errno
));
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",
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);
653 zed_log_msg(LOG_WARNING
,
654 "Failed to read state file \"%s\": %s",
655 zcp
->state_file
, strerror(errno
));
657 } else if (n
!= len
) {
659 zed_log_msg(LOG_WARNING
,
660 "Failed to read state file \"%s\": Read %zd of %zd bytes",
661 zcp
->state_file
, n
, len
);
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
[])
682 "Failed to write state file: %s", strerror(errno
));
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",
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);
701 zed_log_msg(LOG_WARNING
,
702 "Failed to write state file \"%s\": %s",
703 zcp
->state_file
, strerror(errno
));
708 zed_log_msg(LOG_WARNING
,
709 "Failed to write state file \"%s\": Wrote %zd of %zd bytes",
710 zcp
->state_file
, n
, len
);
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
));