static int wall_message_timeout_handler( sd_event_source *s, uint64_t usec, void *userdata) { Manager *m = userdata; usec_t n, next; int r; assert(m); assert(s == m->wall_message_timeout_source); n = now(CLOCK_REALTIME); r = warn_wall(m, n); if (r == 0) return 0; next = when_wall(n, m->scheduled_shutdown_timeout); if (next > 0) { r = sd_event_source_set_time(s, n + next); if (r < 0) return log_error_errno(r, "sd_event_source_set_time() failed. %m"); r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); if (r < 0) return log_error_errno(r, "sd_event_source_set_enabled() failed. %m"); } return 0; }
int manager_setup_wall_message_timer(Manager *m) { usec_t n, elapse; int r; assert(m); n = now(CLOCK_REALTIME); elapse = m->scheduled_shutdown_timeout; /* wall message handling */ if (isempty(m->scheduled_shutdown_type)) { warn_wall(m, n); return 0; } if (elapse < n) return 0; /* Warn immediately if less than 15 minutes are left */ if (elapse - n < 15 * USEC_PER_MINUTE) { r = warn_wall(m, n); if (r == 0) return 0; } elapse = when_wall(n, elapse); if (elapse == 0) return 0; if (m->wall_message_timeout_source) { r = sd_event_source_set_time(m->wall_message_timeout_source, n + elapse); if (r < 0) return log_error_errno(r, "sd_event_source_set_time() failed. %m"); r = sd_event_source_set_enabled(m->wall_message_timeout_source, SD_EVENT_ONESHOT); if (r < 0) return log_error_errno(r, "sd_event_source_set_enabled() failed. %m"); } else { r = sd_event_add_time(m->event, &m->wall_message_timeout_source, CLOCK_REALTIME, n + elapse, 0, wall_message_timeout_handler, m); if (r < 0) return log_error_errno(r, "sd_event_add_time() failed. %m"); } return 0; }
int main(int argc, char *argv[]) { enum { FD_SOCKET, FD_WALL_TIMER, FD_NOLOGIN_TIMER, FD_SHUTDOWN_TIMER, _FD_MAX }; int r = EXIT_FAILURE, n_fds; int one = 1; struct shutdownd_command c; struct pollfd pollfd[_FD_MAX]; bool exec_shutdown = false, unlink_nologin = false, failed = false; unsigned i; if (getppid() != 1) { log_error("This program should be invoked by init only."); return EXIT_FAILURE; } if (argc > 1) { log_error("This program does not take arguments."); return EXIT_FAILURE; } log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); log_parse_environment(); log_open(); if ((n_fds = sd_listen_fds(true)) < 0) { log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); return EXIT_FAILURE; } if (n_fds != 1) { log_error("Need exactly one file descriptor."); return EXIT_FAILURE; } if (setsockopt(SD_LISTEN_FDS_START, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { log_error("SO_PASSCRED failed: %m"); return EXIT_FAILURE; } zero(c); zero(pollfd); pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START; pollfd[FD_SOCKET].events = POLLIN; for (i = 0; i < _FD_MAX; i++) { if (i == FD_SOCKET) continue; pollfd[i].events = POLLIN; if ((pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) { log_error("timerfd_create(): %m"); failed = true; } } if (failed) goto finish; log_debug("systemd-shutdownd running as pid %lu", (unsigned long) getpid()); sd_notify(false, "READY=1\n" "STATUS=Processing requests..."); do { int k; usec_t n; if (poll(pollfd, _FD_MAX, -1) < 0) { if (errno == EAGAIN || errno == EINTR) continue; log_error("poll(): %m"); goto finish; } n = now(CLOCK_REALTIME); if (pollfd[FD_SOCKET].revents) { if ((k = read_packet(pollfd[FD_SOCKET].fd, &c)) < 0) goto finish; else if (k > 0 && c.elapse > 0) { struct itimerspec its; char date[FORMAT_TIMESTAMP_MAX]; if (c.warn_wall) { /* Send wall messages every so often */ zero(its); timespec_store(&its.it_value, when_wall(n, c.elapse)); if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { log_error("timerfd_settime(): %m"); goto finish; } /* Warn immediately if less than 15 minutes are left */ if (n < c.elapse && n + 15*USEC_PER_MINUTE >= c.elapse) warn_wall(n, &c); } /* Disallow logins 5 minutes prior to shutdown */ zero(its); timespec_store(&its.it_value, when_nologin(c.elapse)); if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { log_error("timerfd_settime(): %m"); goto finish; } /* Shutdown after the specified time is reached */ zero(its); timespec_store(&its.it_value, c.elapse); if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { log_error("timerfd_settime(): %m"); goto finish; } sd_notifyf(false, "STATUS=Shutting down at %s...", format_timestamp(date, sizeof(date), c.elapse)); } } if (pollfd[FD_WALL_TIMER].revents) { struct itimerspec its; warn_wall(n, &c); flush_fd(pollfd[FD_WALL_TIMER].fd); /* Restart timer */ zero(its); timespec_store(&its.it_value, when_wall(n, c.elapse)); if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { log_error("timerfd_settime(): %m"); goto finish; } } if (pollfd[FD_NOLOGIN_TIMER].revents) { int e; log_info("Creating /run/nologin, blocking further logins..."); if ((e = write_one_line_file("/run/nologin", "System is going down.")) < 0) log_error("Failed to create /run/nologin: %s", strerror(-e)); else unlink_nologin = true; flush_fd(pollfd[FD_NOLOGIN_TIMER].fd); } if (pollfd[FD_SHUTDOWN_TIMER].revents) { exec_shutdown = true; goto finish; } } while (c.elapse > 0); r = EXIT_SUCCESS; log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid()); finish: for (i = 0; i < _FD_MAX; i++) if (pollfd[i].fd >= 0) close_nointr_nofail(pollfd[i].fd); if (unlink_nologin) unlink("/run/nologin"); if (exec_shutdown) { char sw[3]; sw[0] = '-'; sw[1] = c.mode; sw[2] = 0; execl(SYSTEMCTL_BINARY_PATH, "shutdown", sw, "now", (c.warn_wall && c.wall_message[0]) ? c.wall_message : (c.warn_wall ? NULL : "--no-wall"), NULL); log_error("Failed to execute /sbin/shutdown: %m"); } sd_notify(false, "STATUS=Exiting..."); return r; }