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 <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 6416038816SMartin Matuska sa.sa_handler = SIG_IGN; 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"); 7816038816SMartin Matuska 7916038816SMartin Matuska (void) sigaddset(&sa.sa_mask, SIGCHLD); 8016038816SMartin Matuska if (pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL) < 0) 8116038816SMartin Matuska zed_log_die("Failed to block SIGCHLD"); 82eda14cbcSMatt Macy } 83eda14cbcSMatt Macy 84eda14cbcSMatt Macy /* 85eda14cbcSMatt Macy * Lock all current and future pages in the virtual memory address space. 86eda14cbcSMatt Macy * Access to locked pages will never be delayed by a page fault. 87eda14cbcSMatt Macy * 88eda14cbcSMatt Macy * EAGAIN is tested up to max_tries in case this is a transient error. 89eda14cbcSMatt Macy * 90eda14cbcSMatt Macy * Note that memory locks are not inherited by a child created via fork() 91eda14cbcSMatt Macy * and are automatically removed during an execve(). As such, this must 92eda14cbcSMatt Macy * be called after the daemon fork()s (when running in the background). 93eda14cbcSMatt Macy */ 94eda14cbcSMatt Macy static void 95eda14cbcSMatt Macy _lock_memory(void) 96eda14cbcSMatt Macy { 97eda14cbcSMatt Macy #if HAVE_MLOCKALL 98eda14cbcSMatt Macy int i = 0; 99eda14cbcSMatt Macy const int max_tries = 10; 100eda14cbcSMatt Macy 101eda14cbcSMatt Macy for (i = 0; i < max_tries; i++) { 102eda14cbcSMatt Macy if (mlockall(MCL_CURRENT | MCL_FUTURE) == 0) { 103eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "Locked all pages in memory"); 104eda14cbcSMatt Macy return; 105eda14cbcSMatt Macy } 106eda14cbcSMatt Macy if (errno != EAGAIN) 107eda14cbcSMatt Macy break; 108eda14cbcSMatt Macy } 109eda14cbcSMatt Macy zed_log_die("Failed to lock memory pages: %s", strerror(errno)); 110eda14cbcSMatt Macy 111eda14cbcSMatt Macy #else /* HAVE_MLOCKALL */ 112eda14cbcSMatt Macy zed_log_die("Failed to lock memory pages: mlockall() not supported"); 113eda14cbcSMatt Macy #endif /* HAVE_MLOCKALL */ 114eda14cbcSMatt Macy } 115eda14cbcSMatt Macy 116eda14cbcSMatt Macy /* 117eda14cbcSMatt Macy * Start daemonization of the process including the double fork(). 118eda14cbcSMatt Macy * 119eda14cbcSMatt Macy * The parent process will block here until _finish_daemonize() is called 120eda14cbcSMatt Macy * (in the grandchild process), at which point the parent process will exit. 121eda14cbcSMatt Macy * This prevents the parent process from exiting until initialization is 122eda14cbcSMatt Macy * complete. 123eda14cbcSMatt Macy */ 124eda14cbcSMatt Macy static void 125eda14cbcSMatt Macy _start_daemonize(void) 126eda14cbcSMatt Macy { 127eda14cbcSMatt Macy pid_t pid; 128eda14cbcSMatt Macy struct sigaction sa; 129eda14cbcSMatt Macy 130eda14cbcSMatt Macy /* Create pipe for communicating with child during daemonization. */ 131eda14cbcSMatt Macy zed_log_pipe_open(); 132eda14cbcSMatt Macy 133eda14cbcSMatt Macy /* Background process and ensure child is not process group leader. */ 134eda14cbcSMatt Macy pid = fork(); 135eda14cbcSMatt Macy if (pid < 0) { 136eda14cbcSMatt Macy zed_log_die("Failed to create child process: %s", 137eda14cbcSMatt Macy strerror(errno)); 138eda14cbcSMatt Macy } else if (pid > 0) { 139eda14cbcSMatt Macy 140eda14cbcSMatt Macy /* Close writes since parent will only read from pipe. */ 141eda14cbcSMatt Macy zed_log_pipe_close_writes(); 142eda14cbcSMatt Macy 143eda14cbcSMatt Macy /* Wait for notification that daemonization is complete. */ 144eda14cbcSMatt Macy zed_log_pipe_wait(); 145eda14cbcSMatt Macy 146eda14cbcSMatt Macy zed_log_pipe_close_reads(); 147eda14cbcSMatt Macy _exit(EXIT_SUCCESS); 148eda14cbcSMatt Macy } 149eda14cbcSMatt Macy 150eda14cbcSMatt Macy /* Close reads since child will only write to pipe. */ 151eda14cbcSMatt Macy zed_log_pipe_close_reads(); 152eda14cbcSMatt Macy 153eda14cbcSMatt Macy /* Create independent session and detach from terminal. */ 154eda14cbcSMatt Macy if (setsid() < 0) 155eda14cbcSMatt Macy zed_log_die("Failed to create new session: %s", 156eda14cbcSMatt Macy strerror(errno)); 157eda14cbcSMatt Macy 158eda14cbcSMatt Macy /* Prevent child from terminating on HUP when session leader exits. */ 159eda14cbcSMatt Macy if (sigemptyset(&sa.sa_mask) < 0) 160eda14cbcSMatt Macy zed_log_die("Failed to initialize sigset"); 161eda14cbcSMatt Macy 162eda14cbcSMatt Macy sa.sa_flags = 0; 163eda14cbcSMatt Macy sa.sa_handler = SIG_IGN; 164eda14cbcSMatt Macy 165eda14cbcSMatt Macy if (sigaction(SIGHUP, &sa, NULL) < 0) 166eda14cbcSMatt Macy zed_log_die("Failed to ignore SIGHUP"); 167eda14cbcSMatt Macy 168eda14cbcSMatt Macy /* Ensure process cannot re-acquire terminal. */ 169eda14cbcSMatt Macy pid = fork(); 170eda14cbcSMatt Macy if (pid < 0) { 171eda14cbcSMatt Macy zed_log_die("Failed to create grandchild process: %s", 172eda14cbcSMatt Macy strerror(errno)); 173eda14cbcSMatt Macy } else if (pid > 0) { 174eda14cbcSMatt Macy _exit(EXIT_SUCCESS); 175eda14cbcSMatt Macy } 176eda14cbcSMatt Macy } 177eda14cbcSMatt Macy 178eda14cbcSMatt Macy /* 179eda14cbcSMatt Macy * Finish daemonization of the process by closing stdin/stdout/stderr. 180eda14cbcSMatt Macy * 181eda14cbcSMatt Macy * This must be called at the end of initialization after all external 182eda14cbcSMatt Macy * communication channels are established and accessible. 183eda14cbcSMatt Macy */ 184eda14cbcSMatt Macy static void 185eda14cbcSMatt Macy _finish_daemonize(void) 186eda14cbcSMatt Macy { 187eda14cbcSMatt Macy int devnull; 188eda14cbcSMatt Macy 189eda14cbcSMatt Macy /* Preserve fd 0/1/2, but discard data to/from stdin/stdout/stderr. */ 190eda14cbcSMatt Macy devnull = open("/dev/null", O_RDWR); 191eda14cbcSMatt Macy if (devnull < 0) 192eda14cbcSMatt Macy zed_log_die("Failed to open /dev/null: %s", strerror(errno)); 193eda14cbcSMatt Macy 194eda14cbcSMatt Macy if (dup2(devnull, STDIN_FILENO) < 0) 195eda14cbcSMatt Macy zed_log_die("Failed to dup /dev/null onto stdin: %s", 196eda14cbcSMatt Macy strerror(errno)); 197eda14cbcSMatt Macy 198eda14cbcSMatt Macy if (dup2(devnull, STDOUT_FILENO) < 0) 199eda14cbcSMatt Macy zed_log_die("Failed to dup /dev/null onto stdout: %s", 200eda14cbcSMatt Macy strerror(errno)); 201eda14cbcSMatt Macy 202eda14cbcSMatt Macy if (dup2(devnull, STDERR_FILENO) < 0) 203eda14cbcSMatt Macy zed_log_die("Failed to dup /dev/null onto stderr: %s", 204eda14cbcSMatt Macy strerror(errno)); 205eda14cbcSMatt Macy 206eda14cbcSMatt Macy if ((devnull > STDERR_FILENO) && (close(devnull) < 0)) 207eda14cbcSMatt Macy zed_log_die("Failed to close /dev/null: %s", strerror(errno)); 208eda14cbcSMatt Macy 209eda14cbcSMatt Macy /* Notify parent that daemonization is complete. */ 210eda14cbcSMatt Macy zed_log_pipe_close_writes(); 211eda14cbcSMatt Macy } 212eda14cbcSMatt Macy 213eda14cbcSMatt Macy /* 214eda14cbcSMatt Macy * ZFS Event Daemon (ZED). 215eda14cbcSMatt Macy */ 216eda14cbcSMatt Macy int 217eda14cbcSMatt Macy main(int argc, char *argv[]) 218eda14cbcSMatt Macy { 21916038816SMartin Matuska struct zed_conf zcp; 220eda14cbcSMatt Macy uint64_t saved_eid; 221eda14cbcSMatt Macy int64_t saved_etime[2]; 222eda14cbcSMatt Macy 223eda14cbcSMatt Macy zed_log_init(argv[0]); 224eda14cbcSMatt Macy zed_log_stderr_open(LOG_NOTICE); 22516038816SMartin Matuska zed_conf_init(&zcp); 22616038816SMartin Matuska zed_conf_parse_opts(&zcp, argc, argv); 22716038816SMartin Matuska if (zcp.do_verbose) 228eda14cbcSMatt Macy zed_log_stderr_open(LOG_INFO); 229eda14cbcSMatt Macy 230eda14cbcSMatt Macy if (geteuid() != 0) 231eda14cbcSMatt Macy zed_log_die("Must be run as root"); 232eda14cbcSMatt Macy 233eda14cbcSMatt Macy zed_file_close_from(STDERR_FILENO + 1); 234eda14cbcSMatt Macy 235eda14cbcSMatt Macy (void) umask(0); 236eda14cbcSMatt Macy 237eda14cbcSMatt Macy if (chdir("/") < 0) 238eda14cbcSMatt Macy zed_log_die("Failed to change to root directory"); 239eda14cbcSMatt Macy 24016038816SMartin Matuska if (zed_conf_scan_dir(&zcp) < 0) 241eda14cbcSMatt Macy exit(EXIT_FAILURE); 242eda14cbcSMatt Macy 24316038816SMartin Matuska if (!zcp.do_foreground) { 244eda14cbcSMatt Macy _start_daemonize(); 245eda14cbcSMatt Macy zed_log_syslog_open(LOG_DAEMON); 246eda14cbcSMatt Macy } 247eda14cbcSMatt Macy _setup_sig_handlers(); 248eda14cbcSMatt Macy 24916038816SMartin Matuska if (zcp.do_memlock) 250eda14cbcSMatt Macy _lock_memory(); 251eda14cbcSMatt Macy 25216038816SMartin Matuska if ((zed_conf_write_pid(&zcp) < 0) && (!zcp.do_force)) 253eda14cbcSMatt Macy exit(EXIT_FAILURE); 254eda14cbcSMatt Macy 25516038816SMartin Matuska if (!zcp.do_foreground) 256eda14cbcSMatt Macy _finish_daemonize(); 257eda14cbcSMatt Macy 258eda14cbcSMatt Macy zed_log_msg(LOG_NOTICE, 259eda14cbcSMatt Macy "ZFS Event Daemon %s-%s (PID %d)", 260eda14cbcSMatt Macy ZFS_META_VERSION, ZFS_META_RELEASE, (int)getpid()); 261eda14cbcSMatt Macy 26216038816SMartin Matuska if (zed_conf_open_state(&zcp) < 0) 263eda14cbcSMatt Macy exit(EXIT_FAILURE); 264eda14cbcSMatt Macy 26516038816SMartin Matuska if (zed_conf_read_state(&zcp, &saved_eid, saved_etime) < 0) 266eda14cbcSMatt Macy exit(EXIT_FAILURE); 267eda14cbcSMatt Macy 268eda14cbcSMatt Macy idle: 269eda14cbcSMatt Macy /* 270eda14cbcSMatt Macy * If -I is specified, attempt to open /dev/zfs repeatedly until 271eda14cbcSMatt Macy * successful. 272eda14cbcSMatt Macy */ 273eda14cbcSMatt Macy do { 27416038816SMartin Matuska if (!zed_event_init(&zcp)) 275eda14cbcSMatt Macy break; 276eda14cbcSMatt Macy /* Wait for some time and try again. tunable? */ 277eda14cbcSMatt Macy sleep(30); 27816038816SMartin Matuska } while (!_got_exit && zcp.do_idle); 279eda14cbcSMatt Macy 280eda14cbcSMatt Macy if (_got_exit) 281eda14cbcSMatt Macy goto out; 282eda14cbcSMatt Macy 28316038816SMartin Matuska zed_event_seek(&zcp, saved_eid, saved_etime); 284eda14cbcSMatt Macy 285eda14cbcSMatt Macy while (!_got_exit) { 286eda14cbcSMatt Macy int rv; 287eda14cbcSMatt Macy if (_got_hup) { 288eda14cbcSMatt Macy _got_hup = 0; 28916038816SMartin Matuska (void) zed_conf_scan_dir(&zcp); 290eda14cbcSMatt Macy } 29116038816SMartin Matuska rv = zed_event_service(&zcp); 292eda14cbcSMatt Macy 293eda14cbcSMatt Macy /* ENODEV: When kernel module is unloaded (osx) */ 294*53b70c86SMartin Matuska if (rv != 0) 295eda14cbcSMatt Macy break; 296eda14cbcSMatt Macy } 297eda14cbcSMatt Macy 298eda14cbcSMatt Macy zed_log_msg(LOG_NOTICE, "Exiting"); 29916038816SMartin Matuska zed_event_fini(&zcp); 300eda14cbcSMatt Macy 30116038816SMartin Matuska if (zcp.do_idle && !_got_exit) 302eda14cbcSMatt Macy goto idle; 303eda14cbcSMatt Macy 304eda14cbcSMatt Macy out: 30516038816SMartin Matuska zed_conf_destroy(&zcp); 306eda14cbcSMatt Macy zed_log_fini(); 307eda14cbcSMatt Macy exit(EXIT_SUCCESS); 308eda14cbcSMatt Macy } 309