1eda14cbcSMatt Macy /* 2*180f8225SMatt Macy * This file is part of the ZFS Event Daemon (ZED). 3*180f8225SMatt Macy * 4eda14cbcSMatt Macy * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). 5eda14cbcSMatt Macy * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. 6eda14cbcSMatt Macy * Refer to the ZoL 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 <errno.h> 16eda14cbcSMatt Macy #include <fcntl.h> 17eda14cbcSMatt Macy #include <signal.h> 18eda14cbcSMatt Macy #include <stdio.h> 19eda14cbcSMatt Macy #include <stdlib.h> 20eda14cbcSMatt Macy #include <string.h> 21eda14cbcSMatt Macy #include <sys/mman.h> 22eda14cbcSMatt Macy #include <sys/stat.h> 23eda14cbcSMatt Macy #include <unistd.h> 24eda14cbcSMatt Macy #include "zed.h" 25eda14cbcSMatt Macy #include "zed_conf.h" 26eda14cbcSMatt Macy #include "zed_event.h" 27eda14cbcSMatt Macy #include "zed_file.h" 28eda14cbcSMatt Macy #include "zed_log.h" 29eda14cbcSMatt Macy 30eda14cbcSMatt Macy static volatile sig_atomic_t _got_exit = 0; 31eda14cbcSMatt Macy static volatile sig_atomic_t _got_hup = 0; 32eda14cbcSMatt Macy 33eda14cbcSMatt Macy /* 34eda14cbcSMatt Macy * Signal handler for SIGINT & SIGTERM. 35eda14cbcSMatt Macy */ 36eda14cbcSMatt Macy static void 37eda14cbcSMatt Macy _exit_handler(int signum) 38eda14cbcSMatt Macy { 39eda14cbcSMatt Macy _got_exit = 1; 40eda14cbcSMatt Macy } 41eda14cbcSMatt Macy 42eda14cbcSMatt Macy /* 43eda14cbcSMatt Macy * Signal handler for SIGHUP. 44eda14cbcSMatt Macy */ 45eda14cbcSMatt Macy static void 46eda14cbcSMatt Macy _hup_handler(int signum) 47eda14cbcSMatt Macy { 48eda14cbcSMatt Macy _got_hup = 1; 49eda14cbcSMatt Macy } 50eda14cbcSMatt Macy 51eda14cbcSMatt Macy /* 52eda14cbcSMatt Macy * Register signal handlers. 53eda14cbcSMatt Macy */ 54eda14cbcSMatt Macy static void 55eda14cbcSMatt Macy _setup_sig_handlers(void) 56eda14cbcSMatt Macy { 57eda14cbcSMatt Macy struct sigaction sa; 58eda14cbcSMatt Macy 59eda14cbcSMatt Macy if (sigemptyset(&sa.sa_mask) < 0) 60eda14cbcSMatt Macy zed_log_die("Failed to initialize sigset"); 61eda14cbcSMatt Macy 62eda14cbcSMatt Macy sa.sa_flags = SA_RESTART; 63eda14cbcSMatt Macy sa.sa_handler = SIG_IGN; 64eda14cbcSMatt Macy 65eda14cbcSMatt Macy if (sigaction(SIGPIPE, &sa, NULL) < 0) 66eda14cbcSMatt Macy zed_log_die("Failed to ignore SIGPIPE"); 67eda14cbcSMatt Macy 68eda14cbcSMatt Macy sa.sa_handler = _exit_handler; 69eda14cbcSMatt Macy if (sigaction(SIGINT, &sa, NULL) < 0) 70eda14cbcSMatt Macy zed_log_die("Failed to register SIGINT handler"); 71eda14cbcSMatt Macy 72eda14cbcSMatt Macy if (sigaction(SIGTERM, &sa, NULL) < 0) 73eda14cbcSMatt Macy zed_log_die("Failed to register SIGTERM handler"); 74eda14cbcSMatt Macy 75eda14cbcSMatt Macy sa.sa_handler = _hup_handler; 76eda14cbcSMatt Macy if (sigaction(SIGHUP, &sa, NULL) < 0) 77eda14cbcSMatt Macy zed_log_die("Failed to register SIGHUP handler"); 78eda14cbcSMatt Macy } 79eda14cbcSMatt Macy 80eda14cbcSMatt Macy /* 81eda14cbcSMatt Macy * Lock all current and future pages in the virtual memory address space. 82eda14cbcSMatt Macy * Access to locked pages will never be delayed by a page fault. 83eda14cbcSMatt Macy * 84eda14cbcSMatt Macy * EAGAIN is tested up to max_tries in case this is a transient error. 85eda14cbcSMatt Macy * 86eda14cbcSMatt Macy * Note that memory locks are not inherited by a child created via fork() 87eda14cbcSMatt Macy * and are automatically removed during an execve(). As such, this must 88eda14cbcSMatt Macy * be called after the daemon fork()s (when running in the background). 89eda14cbcSMatt Macy */ 90eda14cbcSMatt Macy static void 91eda14cbcSMatt Macy _lock_memory(void) 92eda14cbcSMatt Macy { 93eda14cbcSMatt Macy #if HAVE_MLOCKALL 94eda14cbcSMatt Macy int i = 0; 95eda14cbcSMatt Macy const int max_tries = 10; 96eda14cbcSMatt Macy 97eda14cbcSMatt Macy for (i = 0; i < max_tries; i++) { 98eda14cbcSMatt Macy if (mlockall(MCL_CURRENT | MCL_FUTURE) == 0) { 99eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "Locked all pages in memory"); 100eda14cbcSMatt Macy return; 101eda14cbcSMatt Macy } 102eda14cbcSMatt Macy if (errno != EAGAIN) 103eda14cbcSMatt Macy break; 104eda14cbcSMatt Macy } 105eda14cbcSMatt Macy zed_log_die("Failed to lock memory pages: %s", strerror(errno)); 106eda14cbcSMatt Macy 107eda14cbcSMatt Macy #else /* HAVE_MLOCKALL */ 108eda14cbcSMatt Macy zed_log_die("Failed to lock memory pages: mlockall() not supported"); 109eda14cbcSMatt Macy #endif /* HAVE_MLOCKALL */ 110eda14cbcSMatt Macy } 111eda14cbcSMatt Macy 112eda14cbcSMatt Macy /* 113eda14cbcSMatt Macy * Start daemonization of the process including the double fork(). 114eda14cbcSMatt Macy * 115eda14cbcSMatt Macy * The parent process will block here until _finish_daemonize() is called 116eda14cbcSMatt Macy * (in the grandchild process), at which point the parent process will exit. 117eda14cbcSMatt Macy * This prevents the parent process from exiting until initialization is 118eda14cbcSMatt Macy * complete. 119eda14cbcSMatt Macy */ 120eda14cbcSMatt Macy static void 121eda14cbcSMatt Macy _start_daemonize(void) 122eda14cbcSMatt Macy { 123eda14cbcSMatt Macy pid_t pid; 124eda14cbcSMatt Macy struct sigaction sa; 125eda14cbcSMatt Macy 126eda14cbcSMatt Macy /* Create pipe for communicating with child during daemonization. */ 127eda14cbcSMatt Macy zed_log_pipe_open(); 128eda14cbcSMatt Macy 129eda14cbcSMatt Macy /* Background process and ensure child is not process group leader. */ 130eda14cbcSMatt Macy pid = fork(); 131eda14cbcSMatt Macy if (pid < 0) { 132eda14cbcSMatt Macy zed_log_die("Failed to create child process: %s", 133eda14cbcSMatt Macy strerror(errno)); 134eda14cbcSMatt Macy } else if (pid > 0) { 135eda14cbcSMatt Macy 136eda14cbcSMatt Macy /* Close writes since parent will only read from pipe. */ 137eda14cbcSMatt Macy zed_log_pipe_close_writes(); 138eda14cbcSMatt Macy 139eda14cbcSMatt Macy /* Wait for notification that daemonization is complete. */ 140eda14cbcSMatt Macy zed_log_pipe_wait(); 141eda14cbcSMatt Macy 142eda14cbcSMatt Macy zed_log_pipe_close_reads(); 143eda14cbcSMatt Macy _exit(EXIT_SUCCESS); 144eda14cbcSMatt Macy } 145eda14cbcSMatt Macy 146eda14cbcSMatt Macy /* Close reads since child will only write to pipe. */ 147eda14cbcSMatt Macy zed_log_pipe_close_reads(); 148eda14cbcSMatt Macy 149eda14cbcSMatt Macy /* Create independent session and detach from terminal. */ 150eda14cbcSMatt Macy if (setsid() < 0) 151eda14cbcSMatt Macy zed_log_die("Failed to create new session: %s", 152eda14cbcSMatt Macy strerror(errno)); 153eda14cbcSMatt Macy 154eda14cbcSMatt Macy /* Prevent child from terminating on HUP when session leader exits. */ 155eda14cbcSMatt Macy if (sigemptyset(&sa.sa_mask) < 0) 156eda14cbcSMatt Macy zed_log_die("Failed to initialize sigset"); 157eda14cbcSMatt Macy 158eda14cbcSMatt Macy sa.sa_flags = 0; 159eda14cbcSMatt Macy sa.sa_handler = SIG_IGN; 160eda14cbcSMatt Macy 161eda14cbcSMatt Macy if (sigaction(SIGHUP, &sa, NULL) < 0) 162eda14cbcSMatt Macy zed_log_die("Failed to ignore SIGHUP"); 163eda14cbcSMatt Macy 164eda14cbcSMatt Macy /* Ensure process cannot re-acquire terminal. */ 165eda14cbcSMatt Macy pid = fork(); 166eda14cbcSMatt Macy if (pid < 0) { 167eda14cbcSMatt Macy zed_log_die("Failed to create grandchild process: %s", 168eda14cbcSMatt Macy strerror(errno)); 169eda14cbcSMatt Macy } else if (pid > 0) { 170eda14cbcSMatt Macy _exit(EXIT_SUCCESS); 171eda14cbcSMatt Macy } 172eda14cbcSMatt Macy } 173eda14cbcSMatt Macy 174eda14cbcSMatt Macy /* 175eda14cbcSMatt Macy * Finish daemonization of the process by closing stdin/stdout/stderr. 176eda14cbcSMatt Macy * 177eda14cbcSMatt Macy * This must be called at the end of initialization after all external 178eda14cbcSMatt Macy * communication channels are established and accessible. 179eda14cbcSMatt Macy */ 180eda14cbcSMatt Macy static void 181eda14cbcSMatt Macy _finish_daemonize(void) 182eda14cbcSMatt Macy { 183eda14cbcSMatt Macy int devnull; 184eda14cbcSMatt Macy 185eda14cbcSMatt Macy /* Preserve fd 0/1/2, but discard data to/from stdin/stdout/stderr. */ 186eda14cbcSMatt Macy devnull = open("/dev/null", O_RDWR); 187eda14cbcSMatt Macy if (devnull < 0) 188eda14cbcSMatt Macy zed_log_die("Failed to open /dev/null: %s", strerror(errno)); 189eda14cbcSMatt Macy 190eda14cbcSMatt Macy if (dup2(devnull, STDIN_FILENO) < 0) 191eda14cbcSMatt Macy zed_log_die("Failed to dup /dev/null onto stdin: %s", 192eda14cbcSMatt Macy strerror(errno)); 193eda14cbcSMatt Macy 194eda14cbcSMatt Macy if (dup2(devnull, STDOUT_FILENO) < 0) 195eda14cbcSMatt Macy zed_log_die("Failed to dup /dev/null onto stdout: %s", 196eda14cbcSMatt Macy strerror(errno)); 197eda14cbcSMatt Macy 198eda14cbcSMatt Macy if (dup2(devnull, STDERR_FILENO) < 0) 199eda14cbcSMatt Macy zed_log_die("Failed to dup /dev/null onto stderr: %s", 200eda14cbcSMatt Macy strerror(errno)); 201eda14cbcSMatt Macy 202eda14cbcSMatt Macy if ((devnull > STDERR_FILENO) && (close(devnull) < 0)) 203eda14cbcSMatt Macy zed_log_die("Failed to close /dev/null: %s", strerror(errno)); 204eda14cbcSMatt Macy 205eda14cbcSMatt Macy /* Notify parent that daemonization is complete. */ 206eda14cbcSMatt Macy zed_log_pipe_close_writes(); 207eda14cbcSMatt Macy } 208eda14cbcSMatt Macy 209eda14cbcSMatt Macy /* 210eda14cbcSMatt Macy * ZFS Event Daemon (ZED). 211eda14cbcSMatt Macy */ 212eda14cbcSMatt Macy int 213eda14cbcSMatt Macy main(int argc, char *argv[]) 214eda14cbcSMatt Macy { 215eda14cbcSMatt Macy struct zed_conf *zcp; 216eda14cbcSMatt Macy uint64_t saved_eid; 217eda14cbcSMatt Macy int64_t saved_etime[2]; 218eda14cbcSMatt Macy 219eda14cbcSMatt Macy zed_log_init(argv[0]); 220eda14cbcSMatt Macy zed_log_stderr_open(LOG_NOTICE); 221eda14cbcSMatt Macy zcp = zed_conf_create(); 222eda14cbcSMatt Macy zed_conf_parse_opts(zcp, argc, argv); 223eda14cbcSMatt Macy if (zcp->do_verbose) 224eda14cbcSMatt Macy zed_log_stderr_open(LOG_INFO); 225eda14cbcSMatt Macy 226eda14cbcSMatt Macy if (geteuid() != 0) 227eda14cbcSMatt Macy zed_log_die("Must be run as root"); 228eda14cbcSMatt Macy 229eda14cbcSMatt Macy zed_conf_parse_file(zcp); 230eda14cbcSMatt Macy 231eda14cbcSMatt Macy zed_file_close_from(STDERR_FILENO + 1); 232eda14cbcSMatt Macy 233eda14cbcSMatt Macy (void) umask(0); 234eda14cbcSMatt Macy 235eda14cbcSMatt Macy if (chdir("/") < 0) 236eda14cbcSMatt Macy zed_log_die("Failed to change to root directory"); 237eda14cbcSMatt Macy 238eda14cbcSMatt Macy if (zed_conf_scan_dir(zcp) < 0) 239eda14cbcSMatt Macy exit(EXIT_FAILURE); 240eda14cbcSMatt Macy 241eda14cbcSMatt Macy if (!zcp->do_foreground) { 242eda14cbcSMatt Macy _start_daemonize(); 243eda14cbcSMatt Macy zed_log_syslog_open(LOG_DAEMON); 244eda14cbcSMatt Macy } 245eda14cbcSMatt Macy _setup_sig_handlers(); 246eda14cbcSMatt Macy 247eda14cbcSMatt Macy if (zcp->do_memlock) 248eda14cbcSMatt Macy _lock_memory(); 249eda14cbcSMatt Macy 250eda14cbcSMatt Macy if ((zed_conf_write_pid(zcp) < 0) && (!zcp->do_force)) 251eda14cbcSMatt Macy exit(EXIT_FAILURE); 252eda14cbcSMatt Macy 253eda14cbcSMatt Macy if (!zcp->do_foreground) 254eda14cbcSMatt Macy _finish_daemonize(); 255eda14cbcSMatt Macy 256eda14cbcSMatt Macy zed_log_msg(LOG_NOTICE, 257eda14cbcSMatt Macy "ZFS Event Daemon %s-%s (PID %d)", 258eda14cbcSMatt Macy ZFS_META_VERSION, ZFS_META_RELEASE, (int)getpid()); 259eda14cbcSMatt Macy 260eda14cbcSMatt Macy if (zed_conf_open_state(zcp) < 0) 261eda14cbcSMatt Macy exit(EXIT_FAILURE); 262eda14cbcSMatt Macy 263eda14cbcSMatt Macy if (zed_conf_read_state(zcp, &saved_eid, saved_etime) < 0) 264eda14cbcSMatt Macy exit(EXIT_FAILURE); 265eda14cbcSMatt Macy 266eda14cbcSMatt Macy idle: 267eda14cbcSMatt Macy /* 268eda14cbcSMatt Macy * If -I is specified, attempt to open /dev/zfs repeatedly until 269eda14cbcSMatt Macy * successful. 270eda14cbcSMatt Macy */ 271eda14cbcSMatt Macy do { 272eda14cbcSMatt Macy if (!zed_event_init(zcp)) 273eda14cbcSMatt Macy break; 274eda14cbcSMatt Macy /* Wait for some time and try again. tunable? */ 275eda14cbcSMatt Macy sleep(30); 276eda14cbcSMatt Macy } while (!_got_exit && zcp->do_idle); 277eda14cbcSMatt Macy 278eda14cbcSMatt Macy if (_got_exit) 279eda14cbcSMatt Macy goto out; 280eda14cbcSMatt Macy 281eda14cbcSMatt Macy zed_event_seek(zcp, saved_eid, saved_etime); 282eda14cbcSMatt Macy 283eda14cbcSMatt Macy while (!_got_exit) { 284eda14cbcSMatt Macy int rv; 285eda14cbcSMatt Macy if (_got_hup) { 286eda14cbcSMatt Macy _got_hup = 0; 287eda14cbcSMatt Macy (void) zed_conf_scan_dir(zcp); 288eda14cbcSMatt Macy } 289eda14cbcSMatt Macy rv = zed_event_service(zcp); 290eda14cbcSMatt Macy 291eda14cbcSMatt Macy /* ENODEV: When kernel module is unloaded (osx) */ 292eda14cbcSMatt Macy if (rv == ENODEV) 293eda14cbcSMatt Macy break; 294eda14cbcSMatt Macy } 295eda14cbcSMatt Macy 296eda14cbcSMatt Macy zed_log_msg(LOG_NOTICE, "Exiting"); 297eda14cbcSMatt Macy zed_event_fini(zcp); 298eda14cbcSMatt Macy 299eda14cbcSMatt Macy if (zcp->do_idle && !_got_exit) 300eda14cbcSMatt Macy goto idle; 301eda14cbcSMatt Macy 302eda14cbcSMatt Macy out: 303eda14cbcSMatt Macy zed_conf_destroy(zcp); 304eda14cbcSMatt Macy zed_log_fini(); 305eda14cbcSMatt Macy exit(EXIT_SUCCESS); 306eda14cbcSMatt Macy } 307