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