/* SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include #include "main.h" #include "signal-handler.h" #include "uring.h" #include "server.h" #include "server-config.h" #include "igmp.h" #include "announce.h" #include "idle.h" #include "ptimer.h" struct signal_ev { struct uring_task task; struct uring_task_buf tbuf; int pipe[2]; }; static void signal_delete() { assert_return(cfg->signal); uring_task_destroy(&cfg->signal->task); } static void signalfd_read(struct uring_task *task, int res) { struct signal_ev *signal = container_of(task, struct signal_ev, task); struct server *server, *stmp; static unsigned count = 0; siginfo_t *sia, *si; assert_return(task); assert_task_alive(DBG_SIG, task); if (res == 0 || res % sizeof(*si)) die("error in signalfd (%i vs %zu)", res, sizeof(*si)); sia = (siginfo_t *)(task->tbuf->buf); for (unsigned i = 0; i < res / sizeof(*si); i++) { si = &sia[i]; switch (si->si_signo) { case SIGUSR1: { debug(DBG_SIG, "Got a SIGUSR1"); if (si->si_code != SI_ASYNCNL || !si->si_ptr) { error("SIGUSR1: unexpected values in siginfo"); goto out; } server_cfg_async_dns_update(si->si_ptr); break; } case SIGUSR2: debug(DBG_SIG, "got a SIGUSR2"); dump_tree(); break; case SIGTERM: debug(DBG_SIG, "Got a SIGINT/SIGHUP"); verbose("got a signal to quit"); sd_notifyf(0, "STOPPING=1\nSTATUS=Received signal, exiting"); exit(EXIT_SUCCESS); break; case SIGINT: case SIGHUP: count++; if (count > 5) { dump_tree(); exit(EXIT_FAILURE); } verbose("got a signal to dump tree"); sd_notifyf(0, "STOPPING=1\nSTATUS=Received signal, exiting"); dump_tree(); signal_delete(); ptimer_delete(); igmp_delete(); announce_delete(); idle_delete(); server_cfg_monitor_delete(); list_for_each_entry_safe(server, stmp, &cfg->servers, list) server_delete(server); uring_delete(); return; default: error("got an unknown signal: %i", si->si_signo); break; } } out: uring_tbuf_read(&signal->task, signalfd_read); } static void hack_signal_handler(int signum, siginfo_t *si, _unused_ void *ucontext) { ssize_t r; assert_return(signum > 0 && si); r = write(cfg->signal->pipe[PIPE_WR], si, sizeof(*si)); if (r != sizeof(*si)) error("write: %zi\n", r); } static void signal_free(struct uring_task *task) { struct signal_ev *signal = container_of(task, struct signal_ev, task); assert_return(task); debug(DBG_SIG, "called"); close(signal->pipe[PIPE_WR]); cfg->signal = NULL; xfree(signal); } void signal_refdump() { assert_return(cfg->signal); uring_task_refdump(&cfg->signal->task); } void signal_init() { // sigset_t mask; struct signal_ev *signal; assert_return(!cfg->signal); signal = zmalloc(sizeof(*signal)); if (!signal) die("malloc: %m"); if (pipe2(signal->pipe, O_CLOEXEC) < 0) die("pipe2: %m"); /* sigfillset(&mask); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) die("sigprocmask: %m"); sfd = signalfd(-1, &mask, SFD_CLOEXEC); if (sfd < 0) die("signalfd: %m"); */ struct sigaction action; sigfillset(&action.sa_mask); action.sa_sigaction = hack_signal_handler; action.sa_flags = SA_SIGINFO; sigaction(SIGINT, &action, NULL); sigaction(SIGHUP, &action, NULL); sigaction(SIGTERM, &action, NULL); sigaction(SIGUSR1, &action, NULL); sigaction(SIGUSR2, &action, NULL); action.sa_handler = SIG_IGN; action.sa_flags = 0; sigaction(SIGPIPE, &action, NULL); debug(DBG_SIG, "using pipe fds %i -> %i", signal->pipe[PIPE_WR], signal->pipe[PIPE_RD]); uring_task_init(&signal->task, "signal", uring_parent(), signal_free); uring_task_set_fd(&signal->task, signal->pipe[PIPE_RD]); uring_task_set_buf(&signal->task, &signal->tbuf); cfg->signal = signal; uring_tbuf_read(&signal->task, signalfd_read); }