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