xref: /freebsd/sys/contrib/openzfs/cmd/zed/zed_conf.c (revision e0c4386e)
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 OpenZFS 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 <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"
34 
35 /*
36  * Initialise the configuration with default values.
37  */
38 void
39 zed_conf_init(struct zed_conf *zcp)
40 {
41 	memset(zcp, 0, sizeof (*zcp));
42 
43 	/* zcp->zfs_hdl opened in zed_event_init() */
44 	/* zcp->zedlets created in zed_conf_scan_dir() */
45 
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() */
49 
50 	zcp->max_jobs = 16;
51 	zcp->max_zevent_buf_len = 1 << 20;
52 
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));
57 }
58 
59 /*
60  * Destroy the configuration [zcp].
61  *
62  * Note: zfs_hdl & zevent_fd are destroyed via zed_event_fini().
63  */
64 void
65 zed_conf_destroy(struct zed_conf *zcp)
66 {
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;
73 	}
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));
79 	}
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;
86 	}
87 	if (zcp->pid_file) {
88 		free(zcp->pid_file);
89 		zcp->pid_file = NULL;
90 	}
91 	if (zcp->zedlet_dir) {
92 		free(zcp->zedlet_dir);
93 		zcp->zedlet_dir = NULL;
94 	}
95 	if (zcp->state_file) {
96 		free(zcp->state_file);
97 		zcp->state_file = NULL;
98 	}
99 	if (zcp->zedlets) {
100 		zed_strings_destroy(zcp->zedlets);
101 		zcp->zedlets = NULL;
102 	}
103 }
104 
105 /*
106  * Display command-line help and exit.
107  *
108  * If [got_err] is 0, output to stdout and exit normally;
109  * otherwise, output to stderr and exit with a failure status.
110  */
111 static void
112 _zed_conf_display_help(const char *prog, boolean_t got_err)
113 {
114 	struct opt { const char *o, *d, *v; };
115 
116 	FILE *fp = got_err ? stderr : stdout;
117 
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" },
123 		{},
124 	};
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" },
134 		{},
135 	};
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" },
147 		{},
148 	};
149 
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");
161 
162 	exit(got_err ? EXIT_FAILURE : EXIT_SUCCESS);
163 }
164 
165 /*
166  * Display license information to stdout and exit.
167  */
168 static void
169 _zed_conf_display_license(void)
170 {
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");
179 
180 	exit(EXIT_SUCCESS);
181 }
182 
183 /*
184  * Display version information to stdout and exit.
185  */
186 static void
187 _zed_conf_display_version(void)
188 {
189 	printf("%s-%s-%s\n",
190 	    ZFS_META_NAME, ZFS_META_VERSION, ZFS_META_RELEASE);
191 
192 	exit(EXIT_SUCCESS);
193 }
194 
195 /*
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.
199  */
200 static void
201 _zed_conf_parse_path(char **resultp, const char *path)
202 {
203 	char buf[PATH_MAX];
204 
205 	assert(resultp != NULL);
206 	assert(path != NULL);
207 
208 	if (*resultp)
209 		free(*resultp);
210 
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));
217 
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));
222 
223 		*resultp = strdup(buf);
224 	}
225 
226 	if (!*resultp)
227 		zed_log_die("Failed to copy path: %s", strerror(ENOMEM));
228 }
229 
230 /*
231  * Parse the command-line options into the configuration [zcp].
232  */
233 void
234 zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
235 {
236 	const char * const opts = ":hLVd:p:P:s:vfFMZIj:b:";
237 	int opt;
238 	unsigned long raw;
239 
240 	if (!zcp || !argv || !argv[0])
241 		zed_log_die("Failed to parse options: Internal error");
242 
243 	opterr = 0;			/* suppress default getopt err msgs */
244 
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;
295 			}
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;
306 			}
307 			break;
308 		case '?':
309 		default:
310 			if (optopt == '?')
311 				_zed_conf_display_help(argv[0], B_FALSE);
312 
313 			fprintf(stderr, "%s: Invalid option '-%c'\n\n",
314 			    argv[0], optopt);
315 			_zed_conf_display_help(argv[0], B_TRUE);
316 			break;
317 		}
318 	}
319 }
320 
321 /*
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.
325  *
326  * Return 0 on success with an updated set of zedlets,
327  * or -1 on error with errno set.
328  */
329 int
330 zed_conf_scan_dir(struct zed_conf *zcp)
331 {
332 	zed_strings_t *zedlets;
333 	DIR *dirp;
334 	struct dirent *direntp;
335 	char pathname[PATH_MAX];
336 	struct stat st;
337 	int n;
338 
339 	if (!zcp) {
340 		errno = EINVAL;
341 		zed_log_msg(LOG_ERR, "Failed to scan zedlet dir: %s",
342 		    strerror(errno));
343 		return (-1);
344 	}
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);
351 	}
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);
360 	}
361 	while ((direntp = readdir(dirp))) {
362 		if (direntp->d_name[0] == '.')
363 			continue;
364 
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;
371 		}
372 		if (stat(pathname, &st) < 0) {
373 			zed_log_msg(LOG_WARNING, "Failed to stat \"%s\": %s",
374 			    pathname, strerror(errno));
375 			continue;
376 		}
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;
382 		}
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;
388 		}
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;
394 		}
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;
400 		}
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;
406 		}
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;
412 		}
413 		if (zcp->do_verbose)
414 			zed_log_msg(LOG_INFO,
415 			    "Registered zedlet \"%s\"", direntp->d_name);
416 	}
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);
424 	}
425 	if (zcp->zedlets)
426 		zed_strings_destroy(zcp->zedlets);
427 
428 	zcp->zedlets = zedlets;
429 	return (0);
430 }
431 
432 /*
433  * Write the PID file specified in [zcp].
434  * Return 0 on success, -1 on error.
435  *
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).
439  */
440 int
441 zed_conf_write_pid(struct zed_conf *zcp)
442 {
443 	char buf[PATH_MAX];
444 	int n;
445 	char *p;
446 	mode_t mask;
447 	int rv;
448 
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);
454 	}
455 	assert(zcp->pid_fd == -1);
456 	/*
457 	 * Create PID file directory if needed.
458 	 */
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;
465 	}
466 	p = strrchr(buf, '/');
467 	if (p)
468 		*p = '\0';
469 
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;
474 	}
475 	/*
476 	 * Obtain PID file lock.
477 	 */
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;
486 	}
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);
506 		}
507 		goto err;
508 	}
509 	/*
510 	 * Write PID file.
511 	 */
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);
525 	}
526 
527 err:
528 	if (zcp->pid_fd >= 0) {
529 		(void) close(zcp->pid_fd);
530 		zcp->pid_fd = -1;
531 	}
532 	return (-1);
533 }
534 
535 /*
536  * Open and lock the [zcp] state_file.
537  * Return 0 on success, -1 on error.
538  *
539  * FIXME: Move state information into kernel.
540  */
541 int
542 zed_conf_open_state(struct zed_conf *zcp)
543 {
544 	char dirbuf[PATH_MAX];
545 	int n;
546 	char *p;
547 	int rv;
548 
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);
554 	}
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);
561 	}
562 	p = strrchr(dirbuf, '/');
563 	if (p)
564 		*p = '\0';
565 
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);
571 	}
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);
578 		}
579 	}
580 	if (zcp->do_zero)
581 		(void) unlink(zcp->state_file);
582 
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);
589 	}
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);
595 	}
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);
610 		}
611 		return (-1);
612 	}
613 	return (0);
614 }
615 
616 /*
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.
621  */
622 int
623 zed_conf_read_state(struct zed_conf *zcp, uint64_t *eidp, int64_t etime[])
624 {
625 	ssize_t len;
626 	struct iovec iov[3];
627 	ssize_t n;
628 
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);
634 	}
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);
640 	}
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]);
648 
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);
663 	}
664 	return (0);
665 }
666 
667 /*
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.
671  */
672 int
673 zed_conf_write_state(struct zed_conf *zcp, uint64_t eid, int64_t etime[])
674 {
675 	ssize_t len;
676 	struct iovec iov[3];
677 	ssize_t n;
678 
679 	if (!zcp) {
680 		errno = EINVAL;
681 		zed_log_msg(LOG_ERR,
682 		    "Failed to write state file: %s", strerror(errno));
683 		return (-1);
684 	}
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);
690 	}
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]);
698 
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);
705 	}
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);
712 	}
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);
718 	}
719 	return (0);
720 }
721