shutdownd.c revision 4cfa2c999dea269ddc646bfeba6c7f1021a73843
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering This file is part of systemd.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering Copyright 2010 Lennart Poettering
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering systemd is free software; you can redistribute it and/or modify it
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering under the terms of the GNU General Public License as published by
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering the Free Software Foundation; either version 2 of the License, or
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering (at your option) any later version.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering systemd is distributed in the hope that it will be useful, but
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering General Public License for more details.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering You should have received a copy of the GNU General Public License
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringstatic int read_packet(int fd, struct shutdownd_command *_c) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if ((n = recvmsg(fd, &msghdr, MSG_DONTWAIT)) <= 0) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering control.cmsghdr.cmsg_level != SOL_SOCKET ||
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_warning("Received message without credentials. Ignoring.");
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_warning("Got request from unprivileged user. Ignoring.");
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (n != sizeof(c)) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_warning("Message has invalid size. Ignoring");
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringstatic void warn_wall(usec_t n, struct shutdownd_command *c) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering prefix = "The system is going down for system halt at ";
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering prefix = "The system is going down for power-off at ";
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering prefix = "The system is going down for reboot at ";
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (asprintf(&l, "%s%s%s%s!", c->wall_message, c->wall_message[0] ? "\n" : "",
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering prefix, format_timestamp(date, sizeof(date), c->elapse)) < 0)
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_error("Failed to allocate wall message");
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poetteringstatic usec_t when_wall(usec_t n, usec_t elapse) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering static const struct {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering { 10 * USEC_PER_MINUTE, USEC_PER_MINUTE },
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering { 3 * USEC_PER_HOUR, 30 * USEC_PER_MINUTE }
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek /* If the time is already passed, then don't announce */
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek for (i = 0; i < ELEMENTSOF(table); i++)
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek if (n + table[i].delay >= elapse) {
4b8268f843b0da1cfe1995d93a0b1f95faccc454Zbigniew Jędrzejewski-Szmek sub = ((left / table[i].interval) * table[i].interval);
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek sub = ((left / USEC_PER_HOUR) * USEC_PER_HOUR);
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek return elapse > sub ? elapse - sub : 1;
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmekstatic usec_t when_nologin(usec_t elapse) {
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmekint main(int argc, char *argv[]) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering bool exec_shutdown = false, unlink_nologin = false, failed = false;
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_error("This program should be invoked by init only.");
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek log_error("This program does not take arguments.");
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_error("Need exactly one file descriptor.");
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
d3b6d0c21ea5a0d15ec6dbd8b8d179138b7463bcZbigniew Jędrzejewski-Szmek for (i = 0; i < _FD_MAX; i++) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if ((pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) {
18cd5fe99f70a55a2d6f2303d6ee0624942695b1Zbigniew Jędrzejewski-Szmek log_error("timerfd_create(): %m");
18cd5fe99f70a55a2d6f2303d6ee0624942695b1Zbigniew Jędrzejewski-Szmek log_debug("systemd-shutdownd running as pid %lu", (unsigned long) getpid());
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering "STATUS=Processing requests...");
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek if (poll(pollfd, _FD_MAX, -1) < 0) {
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek if (pollfd[FD_SOCKET].revents) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if ((k = read_packet(pollfd[FD_SOCKET].fd, &c)) < 0)
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering else if (k > 0 && c.elapse > 0) {
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek char date[FORMAT_TIMESTAMP_MAX];
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek /* Send wall messages every so often */
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek timespec_store(&its.it_value, when_wall(n, c.elapse));
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek log_error("timerfd_settime(): %m");
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* Warn immediately if less than 15 minutes are left */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* Disallow logins 5 minutes prior to shutdown */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering timespec_store(&its.it_value, when_nologin(c.elapse));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* Shutdown after the specified time is reached */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering "STATUS=Shutting down at %s...",
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering format_timestamp(date, sizeof(date), c.elapse));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* Restart timer */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering timespec_store(&its.it_value, when_wall(n, c.elapse));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_info("Creating /run/nologin, blocking further logins...");
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek if ((e = write_one_line_file_atomic("/run/nologin", "System is going down.")) < 0)
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek log_error("Failed to create /run/nologin: %s", strerror(-e));
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek if (pollfd[FD_SHUTDOWN_TIMER].revents) {
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid());
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek for (i = 0; i < _FD_MAX; i++)
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek close_nointr_nofail(pollfd[i].fd);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek if (exec_shutdown && !c.dry_run) {
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek (c.warn_wall && c.wall_message[0]) ? c.wall_message :
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek (c.warn_wall ? NULL : "--no-wall"),
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek log_error("Failed to execute /sbin/shutdown: %m");
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek "STATUS=Exiting...");