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