Path: blob/main/sys/contrib/openzfs/cmd/zed/zed.c
105585 views
// SPDX-License-Identifier: CDDL-1.01/*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 the9* Common Development and Distribution License Version 1.0 (CDDL-1.0).10* You can obtain a copy of the license from the top-level file11* "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*/1415#include <errno.h>16#include <fcntl.h>17#include <signal.h>18#include <stdio.h>19#include <stdlib.h>20#include <string.h>21#include <sys/mman.h>22#include <sys/stat.h>23#include <unistd.h>24#include "zed.h"25#include "zed_conf.h"26#include "zed_event.h"27#include "zed_file.h"28#include "zed_log.h"2930static volatile sig_atomic_t _got_exit = 0;31static volatile sig_atomic_t _got_hup = 0;3233/*34* Signal handler for SIGINT & SIGTERM.35*/36static void37_exit_handler(int signum)38{39(void) signum;40_got_exit = 1;41}4243/*44* Signal handler for SIGHUP.45*/46static void47_hup_handler(int signum)48{49(void) signum;50_got_hup = 1;51}5253/*54* Register signal handlers.55*/56static void57_setup_sig_handlers(void)58{59struct sigaction sa;6061if (sigemptyset(&sa.sa_mask) < 0)62zed_log_die("Failed to initialize sigset");6364sa.sa_flags = SA_RESTART;6566sa.sa_handler = SIG_IGN;67if (sigaction(SIGPIPE, &sa, NULL) < 0)68zed_log_die("Failed to ignore SIGPIPE");6970sa.sa_handler = _exit_handler;71if (sigaction(SIGINT, &sa, NULL) < 0)72zed_log_die("Failed to register SIGINT handler");7374if (sigaction(SIGTERM, &sa, NULL) < 0)75zed_log_die("Failed to register SIGTERM handler");7677sa.sa_handler = _hup_handler;78if (sigaction(SIGHUP, &sa, NULL) < 0)79zed_log_die("Failed to register SIGHUP handler");8081(void) sigaddset(&sa.sa_mask, SIGCHLD);82if (pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL) < 0)83zed_log_die("Failed to block SIGCHLD");84}8586/*87* Lock all current and future pages in the virtual memory address space.88* Access to locked pages will never be delayed by a page fault.89*90* EAGAIN is tested up to max_tries in case this is a transient error.91*92* Note that memory locks are not inherited by a child created via fork()93* and are automatically removed during an execve(). As such, this must94* be called after the daemon fork()s (when running in the background).95*/96static void97_lock_memory(void)98{99#if HAVE_MLOCKALL100int i = 0;101const int max_tries = 10;102103for (i = 0; i < max_tries; i++) {104if (mlockall(MCL_CURRENT | MCL_FUTURE) == 0) {105zed_log_msg(LOG_INFO, "Locked all pages in memory");106return;107}108if (errno != EAGAIN)109break;110}111zed_log_die("Failed to lock memory pages: %s", strerror(errno));112113#else /* HAVE_MLOCKALL */114zed_log_die("Failed to lock memory pages: mlockall() not supported");115#endif /* HAVE_MLOCKALL */116}117118/*119* Start daemonization of the process including the double fork().120*121* The parent process will block here until _finish_daemonize() is called122* (in the grandchild process), at which point the parent process will exit.123* This prevents the parent process from exiting until initialization is124* complete.125*/126static void127_start_daemonize(void)128{129pid_t pid;130struct sigaction sa;131132/* Create pipe for communicating with child during daemonization. */133zed_log_pipe_open();134135/* Background process and ensure child is not process group leader. */136pid = fork();137if (pid < 0) {138zed_log_die("Failed to create child process: %s",139strerror(errno));140} else if (pid > 0) {141142/* Close writes since parent will only read from pipe. */143zed_log_pipe_close_writes();144145/* Wait for notification that daemonization is complete. */146zed_log_pipe_wait();147148zed_log_pipe_close_reads();149_exit(EXIT_SUCCESS);150}151152/* Close reads since child will only write to pipe. */153zed_log_pipe_close_reads();154155/* Create independent session and detach from terminal. */156if (setsid() < 0)157zed_log_die("Failed to create new session: %s",158strerror(errno));159160/* Prevent child from terminating on HUP when session leader exits. */161if (sigemptyset(&sa.sa_mask) < 0)162zed_log_die("Failed to initialize sigset");163164sa.sa_flags = 0;165sa.sa_handler = SIG_IGN;166167if (sigaction(SIGHUP, &sa, NULL) < 0)168zed_log_die("Failed to ignore SIGHUP");169170/* Ensure process cannot re-acquire terminal. */171pid = fork();172if (pid < 0) {173zed_log_die("Failed to create grandchild process: %s",174strerror(errno));175} else if (pid > 0) {176_exit(EXIT_SUCCESS);177}178}179180/*181* Finish daemonization of the process by closing stdin/stdout/stderr.182*183* This must be called at the end of initialization after all external184* communication channels are established and accessible.185*/186static void187_finish_daemonize(void)188{189int devnull;190191/* Preserve fd 0/1/2, but discard data to/from stdin/stdout/stderr. */192devnull = open("/dev/null", O_RDWR);193if (devnull < 0)194zed_log_die("Failed to open /dev/null: %s", strerror(errno));195196if (dup2(devnull, STDIN_FILENO) < 0)197zed_log_die("Failed to dup /dev/null onto stdin: %s",198strerror(errno));199200if (dup2(devnull, STDOUT_FILENO) < 0)201zed_log_die("Failed to dup /dev/null onto stdout: %s",202strerror(errno));203204if (dup2(devnull, STDERR_FILENO) < 0)205zed_log_die("Failed to dup /dev/null onto stderr: %s",206strerror(errno));207208if ((devnull > STDERR_FILENO) && (close(devnull) < 0))209zed_log_die("Failed to close /dev/null: %s", strerror(errno));210211/* Notify parent that daemonization is complete. */212zed_log_pipe_close_writes();213}214215/*216* ZFS Event Daemon (ZED).217*/218int219main(int argc, char *argv[])220{221struct zed_conf zcp;222uint64_t saved_eid;223int64_t saved_etime[2];224225zed_log_init(argv[0]);226zed_log_stderr_open(LOG_NOTICE);227zed_conf_init(&zcp);228zed_conf_parse_opts(&zcp, argc, argv);229if (zcp.do_verbose)230zed_log_stderr_open(LOG_INFO);231232if (geteuid() != 0)233zed_log_die("Must be run as root");234235zed_file_close_from(STDERR_FILENO + 1);236237(void) umask(0);238239if (chdir("/") < 0)240zed_log_die("Failed to change to root directory");241242if (zed_conf_scan_dir(&zcp) < 0)243exit(EXIT_FAILURE);244245if (!zcp.do_foreground) {246_start_daemonize();247zed_log_syslog_open(LOG_DAEMON);248}249_setup_sig_handlers();250251if (zcp.do_memlock)252_lock_memory();253254if ((zed_conf_write_pid(&zcp) < 0) && (!zcp.do_force))255exit(EXIT_FAILURE);256257if (!zcp.do_foreground)258_finish_daemonize();259260zed_log_msg(LOG_NOTICE,261"ZFS Event Daemon %s-%s (PID %d)",262ZFS_META_VERSION, ZFS_META_RELEASE, (int)getpid());263264if (zed_conf_open_state(&zcp) < 0)265exit(EXIT_FAILURE);266267if (zed_conf_read_state(&zcp, &saved_eid, saved_etime) < 0)268exit(EXIT_FAILURE);269270idle:271/*272* If -I is specified, attempt to open /dev/zfs repeatedly until273* successful.274*/275do {276if (!zed_event_init(&zcp))277break;278/* Wait for some time and try again. tunable? */279sleep(30);280} while (!_got_exit && zcp.do_idle);281282if (_got_exit)283goto out;284285zed_event_seek(&zcp, saved_eid, saved_etime);286287while (!_got_exit) {288int rv;289if (_got_hup) {290_got_hup = 0;291(void) zed_conf_scan_dir(&zcp);292}293rv = zed_event_service(&zcp);294295/* ENODEV: When kernel module is unloaded (osx) */296if (rv != 0)297break;298}299300zed_log_msg(LOG_NOTICE, "Exiting");301zed_event_fini(&zcp);302303if (zcp.do_idle && !_got_exit)304goto idle;305306out:307zed_conf_destroy(&zcp);308zed_log_fini();309exit(EXIT_SUCCESS);310}311312313