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