xref: /freebsd/sys/contrib/openzfs/cmd/zed/zed_conf.c (revision 1323ec57)
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 
52 	if (!(zcp->pid_file = strdup(ZED_PID_FILE)) ||
53 	    !(zcp->zedlet_dir = strdup(ZED_ZEDLET_DIR)) ||
54 	    !(zcp->state_file = strdup(ZED_STATE_FILE)))
55 		zed_log_die("Failed to create conf: %s", strerror(errno));
56 }
57 
58 /*
59  * Destroy the configuration [zcp].
60  *
61  * Note: zfs_hdl & zevent_fd are destroyed via zed_event_fini().
62  */
63 void
64 zed_conf_destroy(struct zed_conf *zcp)
65 {
66 	if (zcp->state_fd >= 0) {
67 		if (close(zcp->state_fd) < 0)
68 			zed_log_msg(LOG_WARNING,
69 			    "Failed to close state file \"%s\": %s",
70 			    zcp->state_file, strerror(errno));
71 		zcp->state_fd = -1;
72 	}
73 	if (zcp->pid_file) {
74 		if ((unlink(zcp->pid_file) < 0) && (errno != ENOENT))
75 			zed_log_msg(LOG_WARNING,
76 			    "Failed to remove PID file \"%s\": %s",
77 			    zcp->pid_file, strerror(errno));
78 	}
79 	if (zcp->pid_fd >= 0) {
80 		if (close(zcp->pid_fd) < 0)
81 			zed_log_msg(LOG_WARNING,
82 			    "Failed to close PID file \"%s\": %s",
83 			    zcp->pid_file, strerror(errno));
84 		zcp->pid_fd = -1;
85 	}
86 	if (zcp->pid_file) {
87 		free(zcp->pid_file);
88 		zcp->pid_file = NULL;
89 	}
90 	if (zcp->zedlet_dir) {
91 		free(zcp->zedlet_dir);
92 		zcp->zedlet_dir = NULL;
93 	}
94 	if (zcp->state_file) {
95 		free(zcp->state_file);
96 		zcp->state_file = NULL;
97 	}
98 	if (zcp->zedlets) {
99 		zed_strings_destroy(zcp->zedlets);
100 		zcp->zedlets = NULL;
101 	}
102 }
103 
104 /*
105  * Display command-line help and exit.
106  *
107  * If [got_err] is 0, output to stdout and exit normally;
108  * otherwise, output to stderr and exit with a failure status.
109  */
110 static void
111 _zed_conf_display_help(const char *prog, boolean_t got_err)
112 {
113 	struct opt { const char *o, *d, *v; };
114 
115 	FILE *fp = got_err ? stderr : stdout;
116 
117 	struct opt *oo;
118 	struct opt iopts[] = {
119 		{ .o = "-h", .d = "Display help" },
120 		{ .o = "-L", .d = "Display license information" },
121 		{ .o = "-V", .d = "Display version information" },
122 		{},
123 	};
124 	struct opt nopts[] = {
125 		{ .o = "-v", .d = "Be verbose" },
126 		{ .o = "-f", .d = "Force daemon to run" },
127 		{ .o = "-F", .d = "Run daemon in the foreground" },
128 		{ .o = "-I",
129 		    .d = "Idle daemon until kernel module is (re)loaded" },
130 		{ .o = "-M", .d = "Lock all pages in memory" },
131 		{ .o = "-P", .d = "$PATH for ZED to use (only used by ZTS)" },
132 		{ .o = "-Z", .d = "Zero state file" },
133 		{},
134 	};
135 	struct opt vopts[] = {
136 		{ .o = "-d DIR", .d = "Read enabled ZEDLETs from DIR.",
137 		    .v = ZED_ZEDLET_DIR },
138 		{ .o = "-p FILE", .d = "Write daemon's PID to FILE.",
139 		    .v = ZED_PID_FILE },
140 		{ .o = "-s FILE", .d = "Write daemon's state to FILE.",
141 		    .v = ZED_STATE_FILE },
142 		{ .o = "-j JOBS", .d = "Start at most JOBS at once.",
143 		    .v = "16" },
144 		{},
145 	};
146 
147 	fprintf(fp, "Usage: %s [OPTION]...\n", (prog ? prog : "zed"));
148 	fprintf(fp, "\n");
149 	for (oo = iopts; oo->o; ++oo)
150 		fprintf(fp, "    %*s %s\n", -8, oo->o, oo->d);
151 	fprintf(fp, "\n");
152 	for (oo = nopts; oo->o; ++oo)
153 		fprintf(fp, "    %*s %s\n", -8, oo->o, oo->d);
154 	fprintf(fp, "\n");
155 	for (oo = vopts; oo->o; ++oo)
156 		fprintf(fp, "    %*s %s [%s]\n", -8, oo->o, oo->d, oo->v);
157 	fprintf(fp, "\n");
158 
159 	exit(got_err ? EXIT_FAILURE : EXIT_SUCCESS);
160 }
161 
162 /*
163  * Display license information to stdout and exit.
164  */
165 static void
166 _zed_conf_display_license(void)
167 {
168 	printf(
169 	    "The ZFS Event Daemon (ZED) is distributed under the terms of the\n"
170 	    "  Common Development and Distribution License (CDDL-1.0)\n"
171 	    "  <http://opensource.org/licenses/CDDL-1.0>.\n"
172 	    "\n"
173 	    "Developed at Lawrence Livermore National Laboratory"
174 	    " (LLNL-CODE-403049).\n"
175 	    "\n");
176 
177 	exit(EXIT_SUCCESS);
178 }
179 
180 /*
181  * Display version information to stdout and exit.
182  */
183 static void
184 _zed_conf_display_version(void)
185 {
186 	printf("%s-%s-%s\n",
187 	    ZFS_META_NAME, ZFS_META_VERSION, ZFS_META_RELEASE);
188 
189 	exit(EXIT_SUCCESS);
190 }
191 
192 /*
193  * Copy the [path] string to the [resultp] ptr.
194  * If [path] is not an absolute path, prefix it with the current working dir.
195  * If [resultp] is non-null, free its existing string before assignment.
196  */
197 static void
198 _zed_conf_parse_path(char **resultp, const char *path)
199 {
200 	char buf[PATH_MAX];
201 
202 	assert(resultp != NULL);
203 	assert(path != NULL);
204 
205 	if (*resultp)
206 		free(*resultp);
207 
208 	if (path[0] == '/') {
209 		*resultp = strdup(path);
210 	} else {
211 		if (!getcwd(buf, sizeof (buf)))
212 			zed_log_die("Failed to get current working dir: %s",
213 			    strerror(errno));
214 
215 		if (strlcat(buf, "/", sizeof (buf)) >= sizeof (buf) ||
216 		    strlcat(buf, path, sizeof (buf)) >= sizeof (buf))
217 			zed_log_die("Failed to copy path: %s",
218 			    strerror(ENAMETOOLONG));
219 
220 		*resultp = strdup(buf);
221 	}
222 
223 	if (!*resultp)
224 		zed_log_die("Failed to copy path: %s", strerror(ENOMEM));
225 }
226 
227 /*
228  * Parse the command-line options into the configuration [zcp].
229  */
230 void
231 zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
232 {
233 	const char * const opts = ":hLVd:p:P:s:vfFMZIj:";
234 	int opt;
235 	unsigned long raw;
236 
237 	if (!zcp || !argv || !argv[0])
238 		zed_log_die("Failed to parse options: Internal error");
239 
240 	opterr = 0;			/* suppress default getopt err msgs */
241 
242 	while ((opt = getopt(argc, argv, opts)) != -1) {
243 		switch (opt) {
244 		case 'h':
245 			_zed_conf_display_help(argv[0], B_FALSE);
246 			break;
247 		case 'L':
248 			_zed_conf_display_license();
249 			break;
250 		case 'V':
251 			_zed_conf_display_version();
252 			break;
253 		case 'd':
254 			_zed_conf_parse_path(&zcp->zedlet_dir, optarg);
255 			break;
256 		case 'I':
257 			zcp->do_idle = 1;
258 			break;
259 		case 'p':
260 			_zed_conf_parse_path(&zcp->pid_file, optarg);
261 			break;
262 		case 'P':
263 			_zed_conf_parse_path(&zcp->path, optarg);
264 			break;
265 		case 's':
266 			_zed_conf_parse_path(&zcp->state_file, optarg);
267 			break;
268 		case 'v':
269 			zcp->do_verbose = 1;
270 			break;
271 		case 'f':
272 			zcp->do_force = 1;
273 			break;
274 		case 'F':
275 			zcp->do_foreground = 1;
276 			break;
277 		case 'M':
278 			zcp->do_memlock = 1;
279 			break;
280 		case 'Z':
281 			zcp->do_zero = 1;
282 			break;
283 		case 'j':
284 			errno = 0;
285 			raw = strtoul(optarg, NULL, 0);
286 			if (errno == ERANGE || raw > INT16_MAX) {
287 				zed_log_die("%lu is too many jobs", raw);
288 			} if (raw == 0) {
289 				zed_log_die("0 jobs makes no sense");
290 			} else {
291 				zcp->max_jobs = raw;
292 			}
293 			break;
294 		case '?':
295 		default:
296 			if (optopt == '?')
297 				_zed_conf_display_help(argv[0], B_FALSE);
298 
299 			fprintf(stderr, "%s: Invalid option '-%c'\n\n",
300 			    argv[0], optopt);
301 			_zed_conf_display_help(argv[0], B_TRUE);
302 			break;
303 		}
304 	}
305 }
306 
307 /*
308  * Scan the [zcp] zedlet_dir for files to exec based on the event class.
309  * Files must be executable by user, but not writable by group or other.
310  * Dotfiles are ignored.
311  *
312  * Return 0 on success with an updated set of zedlets,
313  * or -1 on error with errno set.
314  */
315 int
316 zed_conf_scan_dir(struct zed_conf *zcp)
317 {
318 	zed_strings_t *zedlets;
319 	DIR *dirp;
320 	struct dirent *direntp;
321 	char pathname[PATH_MAX];
322 	struct stat st;
323 	int n;
324 
325 	if (!zcp) {
326 		errno = EINVAL;
327 		zed_log_msg(LOG_ERR, "Failed to scan zedlet dir: %s",
328 		    strerror(errno));
329 		return (-1);
330 	}
331 	zedlets = zed_strings_create();
332 	if (!zedlets) {
333 		errno = ENOMEM;
334 		zed_log_msg(LOG_WARNING, "Failed to scan dir \"%s\": %s",
335 		    zcp->zedlet_dir, strerror(errno));
336 		return (-1);
337 	}
338 	dirp = opendir(zcp->zedlet_dir);
339 	if (!dirp) {
340 		int errno_bak = errno;
341 		zed_log_msg(LOG_WARNING, "Failed to open dir \"%s\": %s",
342 		    zcp->zedlet_dir, strerror(errno));
343 		zed_strings_destroy(zedlets);
344 		errno = errno_bak;
345 		return (-1);
346 	}
347 	while ((direntp = readdir(dirp))) {
348 		if (direntp->d_name[0] == '.')
349 			continue;
350 
351 		n = snprintf(pathname, sizeof (pathname),
352 		    "%s/%s", zcp->zedlet_dir, direntp->d_name);
353 		if ((n < 0) || (n >= sizeof (pathname))) {
354 			zed_log_msg(LOG_WARNING, "Failed to stat \"%s\": %s",
355 			    direntp->d_name, strerror(ENAMETOOLONG));
356 			continue;
357 		}
358 		if (stat(pathname, &st) < 0) {
359 			zed_log_msg(LOG_WARNING, "Failed to stat \"%s\": %s",
360 			    pathname, strerror(errno));
361 			continue;
362 		}
363 		if (!S_ISREG(st.st_mode)) {
364 			zed_log_msg(LOG_INFO,
365 			    "Ignoring \"%s\": not a regular file",
366 			    direntp->d_name);
367 			continue;
368 		}
369 		if ((st.st_uid != 0) && !zcp->do_force) {
370 			zed_log_msg(LOG_NOTICE,
371 			    "Ignoring \"%s\": not owned by root",
372 			    direntp->d_name);
373 			continue;
374 		}
375 		if (!(st.st_mode & S_IXUSR)) {
376 			zed_log_msg(LOG_INFO,
377 			    "Ignoring \"%s\": not executable by user",
378 			    direntp->d_name);
379 			continue;
380 		}
381 		if ((st.st_mode & S_IWGRP) && !zcp->do_force) {
382 			zed_log_msg(LOG_NOTICE,
383 			    "Ignoring \"%s\": writable by group",
384 			    direntp->d_name);
385 			continue;
386 		}
387 		if ((st.st_mode & S_IWOTH) && !zcp->do_force) {
388 			zed_log_msg(LOG_NOTICE,
389 			    "Ignoring \"%s\": writable by other",
390 			    direntp->d_name);
391 			continue;
392 		}
393 		if (zed_strings_add(zedlets, NULL, direntp->d_name) < 0) {
394 			zed_log_msg(LOG_WARNING,
395 			    "Failed to register \"%s\": %s",
396 			    direntp->d_name, strerror(errno));
397 			continue;
398 		}
399 		if (zcp->do_verbose)
400 			zed_log_msg(LOG_INFO,
401 			    "Registered zedlet \"%s\"", direntp->d_name);
402 	}
403 	if (closedir(dirp) < 0) {
404 		int errno_bak = errno;
405 		zed_log_msg(LOG_WARNING, "Failed to close dir \"%s\": %s",
406 		    zcp->zedlet_dir, strerror(errno));
407 		zed_strings_destroy(zedlets);
408 		errno = errno_bak;
409 		return (-1);
410 	}
411 	if (zcp->zedlets)
412 		zed_strings_destroy(zcp->zedlets);
413 
414 	zcp->zedlets = zedlets;
415 	return (0);
416 }
417 
418 /*
419  * Write the PID file specified in [zcp].
420  * Return 0 on success, -1 on error.
421  *
422  * This must be called after fork()ing to become a daemon (so the correct PID
423  * is recorded), but before daemonization is complete and the parent process
424  * exits (for synchronization with systemd).
425  */
426 int
427 zed_conf_write_pid(struct zed_conf *zcp)
428 {
429 	char buf[PATH_MAX];
430 	int n;
431 	char *p;
432 	mode_t mask;
433 	int rv;
434 
435 	if (!zcp || !zcp->pid_file) {
436 		errno = EINVAL;
437 		zed_log_msg(LOG_ERR, "Failed to create PID file: %s",
438 		    strerror(errno));
439 		return (-1);
440 	}
441 	assert(zcp->pid_fd == -1);
442 	/*
443 	 * Create PID file directory if needed.
444 	 */
445 	n = strlcpy(buf, zcp->pid_file, sizeof (buf));
446 	if (n >= sizeof (buf)) {
447 		errno = ENAMETOOLONG;
448 		zed_log_msg(LOG_ERR, "Failed to create PID file: %s",
449 		    strerror(errno));
450 		goto err;
451 	}
452 	p = strrchr(buf, '/');
453 	if (p)
454 		*p = '\0';
455 
456 	if ((mkdirp(buf, 0755) < 0) && (errno != EEXIST)) {
457 		zed_log_msg(LOG_ERR, "Failed to create directory \"%s\": %s",
458 		    buf, strerror(errno));
459 		goto err;
460 	}
461 	/*
462 	 * Obtain PID file lock.
463 	 */
464 	mask = umask(0);
465 	umask(mask | 022);
466 	zcp->pid_fd = open(zcp->pid_file, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
467 	umask(mask);
468 	if (zcp->pid_fd < 0) {
469 		zed_log_msg(LOG_ERR, "Failed to open PID file \"%s\": %s",
470 		    zcp->pid_file, strerror(errno));
471 		goto err;
472 	}
473 	rv = zed_file_lock(zcp->pid_fd);
474 	if (rv < 0) {
475 		zed_log_msg(LOG_ERR, "Failed to lock PID file \"%s\": %s",
476 		    zcp->pid_file, strerror(errno));
477 		goto err;
478 	} else if (rv > 0) {
479 		pid_t pid = zed_file_is_locked(zcp->pid_fd);
480 		if (pid < 0) {
481 			zed_log_msg(LOG_ERR,
482 			    "Failed to test lock on PID file \"%s\"",
483 			    zcp->pid_file);
484 		} else if (pid > 0) {
485 			zed_log_msg(LOG_ERR,
486 			    "Found PID %d bound to PID file \"%s\"",
487 			    pid, zcp->pid_file);
488 		} else {
489 			zed_log_msg(LOG_ERR,
490 			    "Inconsistent lock state on PID file \"%s\"",
491 			    zcp->pid_file);
492 		}
493 		goto err;
494 	}
495 	/*
496 	 * Write PID file.
497 	 */
498 	n = snprintf(buf, sizeof (buf), "%d\n", (int)getpid());
499 	if ((n < 0) || (n >= sizeof (buf))) {
500 		errno = ERANGE;
501 		zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s",
502 		    zcp->pid_file, strerror(errno));
503 	} else if (write(zcp->pid_fd, buf, n) != n) {
504 		zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s",
505 		    zcp->pid_file, strerror(errno));
506 	} else if (fdatasync(zcp->pid_fd) < 0) {
507 		zed_log_msg(LOG_ERR, "Failed to sync PID file \"%s\": %s",
508 		    zcp->pid_file, strerror(errno));
509 	} else {
510 		return (0);
511 	}
512 
513 err:
514 	if (zcp->pid_fd >= 0) {
515 		(void) close(zcp->pid_fd);
516 		zcp->pid_fd = -1;
517 	}
518 	return (-1);
519 }
520 
521 /*
522  * Open and lock the [zcp] state_file.
523  * Return 0 on success, -1 on error.
524  *
525  * FIXME: Move state information into kernel.
526  */
527 int
528 zed_conf_open_state(struct zed_conf *zcp)
529 {
530 	char dirbuf[PATH_MAX];
531 	int n;
532 	char *p;
533 	int rv;
534 
535 	if (!zcp || !zcp->state_file) {
536 		errno = EINVAL;
537 		zed_log_msg(LOG_ERR, "Failed to open state file: %s",
538 		    strerror(errno));
539 		return (-1);
540 	}
541 	n = strlcpy(dirbuf, zcp->state_file, sizeof (dirbuf));
542 	if (n >= sizeof (dirbuf)) {
543 		errno = ENAMETOOLONG;
544 		zed_log_msg(LOG_WARNING, "Failed to open state file: %s",
545 		    strerror(errno));
546 		return (-1);
547 	}
548 	p = strrchr(dirbuf, '/');
549 	if (p)
550 		*p = '\0';
551 
552 	if ((mkdirp(dirbuf, 0755) < 0) && (errno != EEXIST)) {
553 		zed_log_msg(LOG_WARNING,
554 		    "Failed to create directory \"%s\": %s",
555 		    dirbuf, strerror(errno));
556 		return (-1);
557 	}
558 	if (zcp->state_fd >= 0) {
559 		if (close(zcp->state_fd) < 0) {
560 			zed_log_msg(LOG_WARNING,
561 			    "Failed to close state file \"%s\": %s",
562 			    zcp->state_file, strerror(errno));
563 			return (-1);
564 		}
565 	}
566 	if (zcp->do_zero)
567 		(void) unlink(zcp->state_file);
568 
569 	zcp->state_fd = open(zcp->state_file,
570 	    O_RDWR | O_CREAT | O_CLOEXEC, 0644);
571 	if (zcp->state_fd < 0) {
572 		zed_log_msg(LOG_WARNING, "Failed to open state file \"%s\": %s",
573 		    zcp->state_file, strerror(errno));
574 		return (-1);
575 	}
576 	rv = zed_file_lock(zcp->state_fd);
577 	if (rv < 0) {
578 		zed_log_msg(LOG_WARNING, "Failed to lock state file \"%s\": %s",
579 		    zcp->state_file, strerror(errno));
580 		return (-1);
581 	}
582 	if (rv > 0) {
583 		pid_t pid = zed_file_is_locked(zcp->state_fd);
584 		if (pid < 0) {
585 			zed_log_msg(LOG_WARNING,
586 			    "Failed to test lock on state file \"%s\"",
587 			    zcp->state_file);
588 		} else if (pid > 0) {
589 			zed_log_msg(LOG_WARNING,
590 			    "Found PID %d bound to state file \"%s\"",
591 			    pid, zcp->state_file);
592 		} else {
593 			zed_log_msg(LOG_WARNING,
594 			    "Inconsistent lock state on state file \"%s\"",
595 			    zcp->state_file);
596 		}
597 		return (-1);
598 	}
599 	return (0);
600 }
601 
602 /*
603  * Read the opened [zcp] state_file to obtain the eid & etime of the last event
604  * processed.  Write the state from the last event to the [eidp] & [etime] args
605  * passed by reference.  Note that etime[] is an array of size 2.
606  * Return 0 on success, -1 on error.
607  */
608 int
609 zed_conf_read_state(struct zed_conf *zcp, uint64_t *eidp, int64_t etime[])
610 {
611 	ssize_t len;
612 	struct iovec iov[3];
613 	ssize_t n;
614 
615 	if (!zcp || !eidp || !etime) {
616 		errno = EINVAL;
617 		zed_log_msg(LOG_ERR,
618 		    "Failed to read state file: %s", strerror(errno));
619 		return (-1);
620 	}
621 	if (lseek(zcp->state_fd, 0, SEEK_SET) == (off_t)-1) {
622 		zed_log_msg(LOG_WARNING,
623 		    "Failed to reposition state file offset: %s",
624 		    strerror(errno));
625 		return (-1);
626 	}
627 	len = 0;
628 	iov[0].iov_base = eidp;
629 	len += iov[0].iov_len = sizeof (*eidp);
630 	iov[1].iov_base = &etime[0];
631 	len += iov[1].iov_len = sizeof (etime[0]);
632 	iov[2].iov_base = &etime[1];
633 	len += iov[2].iov_len = sizeof (etime[1]);
634 
635 	n = readv(zcp->state_fd, iov, 3);
636 	if (n == 0) {
637 		*eidp = 0;
638 	} else if (n < 0) {
639 		zed_log_msg(LOG_WARNING,
640 		    "Failed to read state file \"%s\": %s",
641 		    zcp->state_file, strerror(errno));
642 		return (-1);
643 	} else if (n != len) {
644 		errno = EIO;
645 		zed_log_msg(LOG_WARNING,
646 		    "Failed to read state file \"%s\": Read %d of %d bytes",
647 		    zcp->state_file, n, len);
648 		return (-1);
649 	}
650 	return (0);
651 }
652 
653 /*
654  * Write the [eid] & [etime] of the last processed event to the opened
655  * [zcp] state_file.  Note that etime[] is an array of size 2.
656  * Return 0 on success, -1 on error.
657  */
658 int
659 zed_conf_write_state(struct zed_conf *zcp, uint64_t eid, int64_t etime[])
660 {
661 	ssize_t len;
662 	struct iovec iov[3];
663 	ssize_t n;
664 
665 	if (!zcp) {
666 		errno = EINVAL;
667 		zed_log_msg(LOG_ERR,
668 		    "Failed to write state file: %s", strerror(errno));
669 		return (-1);
670 	}
671 	if (lseek(zcp->state_fd, 0, SEEK_SET) == (off_t)-1) {
672 		zed_log_msg(LOG_WARNING,
673 		    "Failed to reposition state file offset: %s",
674 		    strerror(errno));
675 		return (-1);
676 	}
677 	len = 0;
678 	iov[0].iov_base = &eid;
679 	len += iov[0].iov_len = sizeof (eid);
680 	iov[1].iov_base = &etime[0];
681 	len += iov[1].iov_len = sizeof (etime[0]);
682 	iov[2].iov_base = &etime[1];
683 	len += iov[2].iov_len = sizeof (etime[1]);
684 
685 	n = writev(zcp->state_fd, iov, 3);
686 	if (n < 0) {
687 		zed_log_msg(LOG_WARNING,
688 		    "Failed to write state file \"%s\": %s",
689 		    zcp->state_file, strerror(errno));
690 		return (-1);
691 	}
692 	if (n != len) {
693 		errno = EIO;
694 		zed_log_msg(LOG_WARNING,
695 		    "Failed to write state file \"%s\": Wrote %d of %d bytes",
696 		    zcp->state_file, n, len);
697 		return (-1);
698 	}
699 	if (fdatasync(zcp->state_fd) < 0) {
700 		zed_log_msg(LOG_WARNING,
701 		    "Failed to sync state file \"%s\": %s",
702 		    zcp->state_file, strerror(errno));
703 		return (-1);
704 	}
705 	return (0);
706 }
707