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