[systemd-commits] 10 commits - .gitignore Makefile.am Makefile-man.am man/systemd-shutdownd.service.xml man/systemd.xml src/journal src/login src/shared src/shutdownd src/systemctl src/systemd src/tty-ask-password-agent units/.gitignore units/systemd-shutdownd.service.in units/systemd-shutdownd.socket

Daniel Mack zonque at kemper.freedesktop.org
Fri Apr 24 08:48:20 PDT 2015


 .gitignore                                          |    1 
 Makefile-man.am                                     |   12 
 Makefile.am                                         |   21 
 man/systemd-shutdownd.service.xml                   |   77 ---
 man/systemd.xml                                     |   10 
 src/journal/journald-wall.c                         |    2 
 src/login/logind-dbus.c                             |  449 ++++++++++++++++---
 src/login/logind-utmp.c                             |  182 +++++++
 src/login/logind.c                                  |   38 -
 src/login/logind.h                                  |   26 -
 src/shared/utmp-wtmp.c                              |   19 
 src/shared/utmp-wtmp.h                              |   15 
 src/shutdownd/Makefile                              |    1 
 src/shutdownd/shutdownd.c                           |  458 --------------------
 src/systemctl/systemctl.c                           |  164 ++++---
 src/systemd/sd-shutdown.h                           |  119 -----
 src/tty-ask-password-agent/tty-ask-password-agent.c |    4 
 units/.gitignore                                    |    1 
 units/systemd-shutdownd.service.in                  |   15 
 units/systemd-shutdownd.socket                      |   18 
 20 files changed, 727 insertions(+), 905 deletions(-)

New commits:
commit d6b07ef796abeaf3ab32c70e32be4857fef93481
Author: Daniel Mack <daniel at zonque.org>
Date:   Thu Apr 23 17:30:23 2015 +0200

    shutdownd: kill the old implementation
    
    Not that all functionality has been ported over to logind, the old
    implementation can be removed. There goes one of the oldest parts of
    the systemd code base.

diff --git a/.gitignore b/.gitignore
index 9da5122..d4edf99 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,7 +120,6 @@
 /systemd-rfkill
 /systemd-run
 /systemd-shutdown
-/systemd-shutdownd
 /systemd-sleep
 /systemd-socket-proxyd
 /systemd-stdio-bridge
diff --git a/Makefile-man.am b/Makefile-man.am
index 2f3e5f2..e902e5e 100644
--- a/Makefile-man.am
+++ b/Makefile-man.am
@@ -84,7 +84,6 @@ MANPAGES += \
 	man/systemd-path.1 \
 	man/systemd-remount-fs.service.8 \
 	man/systemd-run.1 \
-	man/systemd-shutdownd.service.8 \
 	man/systemd-sleep.conf.5 \
 	man/systemd-socket-proxyd.8 \
 	man/systemd-suspend.service.8 \
@@ -225,8 +224,6 @@ MANPAGES_ALIAS += \
 	man/systemd-reboot.service.8 \
 	man/systemd-remount-fs.8 \
 	man/systemd-shutdown.8 \
-	man/systemd-shutdownd.8 \
-	man/systemd-shutdownd.socket.8 \
 	man/systemd-sleep.8 \
 	man/systemd-sysctl.8 \
 	man/systemd-sysusers.service.8 \
@@ -339,8 +336,6 @@ man/systemd-poweroff.service.8: man/systemd-halt.service.8
 man/systemd-reboot.service.8: man/systemd-halt.service.8
 man/systemd-remount-fs.8: man/systemd-remount-fs.service.8
 man/systemd-shutdown.8: man/systemd-halt.service.8
-man/systemd-shutdownd.8: man/systemd-shutdownd.service.8
-man/systemd-shutdownd.socket.8: man/systemd-shutdownd.service.8
 man/systemd-sleep.8: man/systemd-suspend.service.8
 man/systemd-sysctl.8: man/systemd-sysctl.service.8
 man/systemd-sysusers.service.8: man/systemd-sysusers.8
@@ -651,12 +646,6 @@ man/systemd-remount-fs.html: man/systemd-remount-fs.service.html
 man/systemd-shutdown.html: man/systemd-halt.service.html
 	$(html-alias)
 
-man/systemd-shutdownd.html: man/systemd-shutdownd.service.html
-	$(html-alias)
-
-man/systemd-shutdownd.socket.html: man/systemd-shutdownd.service.html
-	$(html-alias)
-
 man/systemd-sleep.html: man/systemd-suspend.service.html
 	$(html-alias)
 
@@ -1808,7 +1797,6 @@ EXTRA_DIST += \
 	man/systemd-resolved.service.xml \
 	man/systemd-rfkill at .service.xml \
 	man/systemd-run.xml \
-	man/systemd-shutdownd.service.xml \
 	man/systemd-sleep.conf.xml \
 	man/systemd-socket-proxyd.xml \
 	man/systemd-suspend.service.xml \
diff --git a/Makefile.am b/Makefile.am
index e22b2e3..292cf91 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -394,7 +394,6 @@ rootlibexec_PROGRAMS = \
 	systemd \
 	systemd-cgroups-agent \
 	systemd-initctl \
-	systemd-shutdownd \
 	systemd-shutdown \
 	systemd-remount-fs \
 	systemd-reply-password \
@@ -504,7 +503,6 @@ dist_systemunit_DATA = \
 	units/x-.slice \
 	units/systemd-fsckd.socket \
 	units/systemd-initctl.socket \
-	units/systemd-shutdownd.socket \
 	units/syslog.socket \
 	units/dev-hugepages.mount \
 	units/dev-mqueue.mount \
@@ -540,7 +538,6 @@ nodist_systemunit_DATA = \
 	units/console-getty.service \
 	units/container-getty at .service \
 	units/systemd-initctl.service \
-	units/systemd-shutdownd.service \
 	units/systemd-remount-fs.service \
 	units/systemd-ask-password-wall.service \
 	units/systemd-ask-password-console.service \
@@ -594,7 +591,6 @@ EXTRA_DIST += \
 	units/container-getty at .service.m4.in \
 	units/rescue.service.in \
 	units/systemd-initctl.service.in \
-	units/systemd-shutdownd.service.in \
 	units/systemd-remount-fs.service.in \
 	units/systemd-update-utmp.service.in \
 	units/systemd-update-utmp-runlevel.service.in \
@@ -2163,18 +2159,6 @@ systemd_update_done_LDADD = \
 	libsystemd-shared.la
 
 # ------------------------------------------------------------------------------
-systemd_shutdownd_SOURCES = \
-	src/shutdownd/shutdownd.c
-
-systemd_shutdownd_LDADD = \
-	libsystemd-label.la \
-	libsystemd-internal.la \
-	libsystemd-shared.la
-
-dist_doc_DATA += \
-	src/systemd/sd-shutdown.h
-
-# ------------------------------------------------------------------------------
 systemd_shutdown_SOURCES = \
 	src/core/umount.c \
 	src/core/umount.h \
@@ -6519,7 +6503,6 @@ substitutions = \
        '|systemgidmax=$(SYSTEM_GID_MAX)|' \
        '|TTY_GID=$(TTY_GID)|' \
        '|systemsleepdir=$(systemsleepdir)|' \
-       '|systemshutdowndir=$(systemshutdowndir)|' \
        '|binfmtdir=$(binfmtdir)|' \
        '|modulesloaddir=$(modulesloaddir)|'
 
@@ -6676,8 +6659,7 @@ EXTRA_DIST += \
 	docs/var-log/README.in
 
 SOCKETS_TARGET_WANTS += \
-	systemd-initctl.socket \
-	systemd-shutdownd.socket
+	systemd-initctl.socket
 
 if HAVE_UTMP
 if HAVE_SYSV_COMPAT
diff --git a/man/systemd-shutdownd.service.xml b/man/systemd-shutdownd.service.xml
deleted file mode 100644
index 756949c..0000000
--- a/man/systemd-shutdownd.service.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version='1.0'?> <!--*-nxml-*-->
-<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
-
-<!--
-  This file is part of systemd.
-
-  Copyright 2012 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
--->
-
-<refentry id="systemd-shutdownd.service">
-
-  <refentryinfo>
-    <title>systemd-shutdownd.service</title>
-    <productname>systemd</productname>
-
-    <authorgroup>
-      <author>
-        <contrib>Developer</contrib>
-        <firstname>Lennart</firstname>
-        <surname>Poettering</surname>
-        <email>lennart at poettering.net</email>
-      </author>
-    </authorgroup>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>systemd-shutdownd.service</refentrytitle>
-    <manvolnum>8</manvolnum>
-  </refmeta>
-
-  <refnamediv>
-    <refname>systemd-shutdownd.service</refname>
-    <refname>systemd-shutdownd.socket</refname>
-    <refname>systemd-shutdownd</refname>
-    <refpurpose>Scheduled shutdown service</refpurpose>
-  </refnamediv>
-
-  <refsynopsisdiv>
-    <para><filename>systemd-shutdownd.service</filename></para>
-    <para><filename>systemd-shutdownd.socket</filename></para>
-    <para><filename>/usr/lib/systemd/systemd-shutdownd</filename></para>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>Description</title>
-
-    <para><filename>systemd-shutdownd.service</filename> is a system
-    service that implements scheduled shutdowns, as exposed by
-    <citerefentry><refentrytitle>shutdown</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
-    <filename>systemd-shutdownd.service</filename> is automatically
-    activated on request and terminates itself when it is
-    unused.</para>
-  </refsect1>
-
-  <refsect1>
-    <title>See Also</title>
-    <para>
-      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>shutdown</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-    </para>
-  </refsect1>
-
-</refentry>
diff --git a/man/systemd.xml b/man/systemd.xml
index d006b0b..4556d56 100644
--- a/man/systemd.xml
+++ b/man/systemd.xml
@@ -1039,16 +1039,6 @@
       </varlistentry>
 
       <varlistentry>
-        <term><filename>/run/systemd/shutdownd</filename></term>
-
-        <listitem><para>Used internally by the
-        <citerefentry><refentrytitle>shutdown</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-        tool to implement delayed shutdowns. This is an
-        <constant>AF_UNIX</constant> datagram
-        socket.</para></listitem>
-      </varlistentry>
-
-      <varlistentry>
         <term><filename>/run/systemd/private</filename></term>
 
         <listitem><para>Used internally as communication channel
diff --git a/src/shutdownd/Makefile b/src/shutdownd/Makefile
deleted file mode 120000
index d0b0e8e..0000000
--- a/src/shutdownd/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile
\ No newline at end of file
diff --git a/src/shutdownd/shutdownd.c b/src/shutdownd/shutdownd.c
deleted file mode 100644
index 8b857b5..0000000
--- a/src/shutdownd/shutdownd.c
+++ /dev/null
@@ -1,458 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/socket.h>
-#include <poll.h>
-#include <sys/timerfd.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stddef.h>
-
-#include "systemd/sd-daemon.h"
-#include "systemd/sd-shutdown.h"
-
-#include "log.h"
-#include "macro.h"
-#include "util.h"
-#include "utmp-wtmp.h"
-#include "mkdir.h"
-#include "fileio.h"
-#include "formats-util.h"
-
-union shutdown_buffer {
-        struct sd_shutdown_command command;
-        char space[offsetof(struct sd_shutdown_command, wall_message) + LINE_MAX];
-};
-
-static int read_packet(int fd, union shutdown_buffer *_b) {
-        struct ucred *ucred;
-        ssize_t n;
-
-        union shutdown_buffer b; /* We maintain our own copy here, in
-                                  * order not to corrupt the last message */
-        struct iovec iovec = {
-                .iov_base = &b,
-                .iov_len = sizeof(b) - 1,
-        };
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
-        } control = {};
-        struct msghdr msghdr = {
-                .msg_iov = &iovec,
-                .msg_iovlen = 1,
-                .msg_control = &control,
-                .msg_controllen = sizeof(control),
-        };
-
-        assert(fd >= 0);
-        assert(_b);
-
-        n = recvmsg(fd, &msghdr, MSG_DONTWAIT);
-        if (n < 0) {
-                if (errno == EAGAIN || errno == EINTR)
-                        return 0;
-
-                log_error_errno(errno, "recvmsg(): %m");
-                return -errno;
-        }
-
-        cmsg_close_all(&msghdr);
-
-        if (n == 0) {
-                log_error("Short read");
-                return -EIO;
-        }
-
-        if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
-            control.cmsghdr.cmsg_level != SOL_SOCKET ||
-            control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
-            control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
-                log_warning("Received message without credentials. Ignoring.");
-                return 0;
-        }
-
-        ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
-        if (ucred->uid != 0) {
-                log_warning("Got request from unprivileged user. Ignoring.");
-                return 0;
-        }
-
-        if ((size_t) n < offsetof(struct sd_shutdown_command, wall_message)) {
-                log_warning("Message has invalid size. Ignoring.");
-                return 0;
-        }
-
-        if (b.command.mode != SD_SHUTDOWN_NONE &&
-            b.command.mode != SD_SHUTDOWN_REBOOT &&
-            b.command.mode != SD_SHUTDOWN_POWEROFF &&
-            b.command.mode != SD_SHUTDOWN_HALT &&
-            b.command.mode != SD_SHUTDOWN_KEXEC) {
-                log_warning("Message has invalid mode. Ignoring.");
-                return 0;
-        }
-
-        b.space[n] = 0;
-
-        *_b = b;
-        return 1;
-}
-
-static void warn_wall(usec_t n, struct sd_shutdown_command *c) {
-        char date[FORMAT_TIMESTAMP_MAX];
-        const char *prefix;
-        _cleanup_free_ char *l = NULL;
-
-        assert(c);
-        assert(c->warn_wall);
-
-        if (n >= c->usec)
-                return;
-
-        if (c->mode == SD_SHUTDOWN_HALT)
-                prefix = "The system is going down for system halt at ";
-        else if (c->mode == SD_SHUTDOWN_POWEROFF)
-                prefix = "The system is going down for power-off at ";
-        else if (c->mode == SD_SHUTDOWN_REBOOT)
-                prefix = "The system is going down for reboot at ";
-        else if (c->mode == SD_SHUTDOWN_KEXEC)
-                prefix = "The system is going down for kexec reboot at ";
-        else if (c->mode == SD_SHUTDOWN_NONE)
-                prefix = "The system shutdown has been cancelled at ";
-        else
-                assert_not_reached("Unknown mode!");
-
-        if (asprintf(&l, "%s%s%s%s!", c->wall_message, c->wall_message[0] ? "\n" : "",
-                     prefix, format_timestamp(date, sizeof(date), c->usec)) >= 0)
-                utmp_wall(l, NULL, NULL, NULL, NULL);
-        else
-                log_error("Failed to allocate wall message");
-}
-
-_const_ static usec_t when_wall(usec_t n, usec_t elapse) {
-
-        static const struct {
-                usec_t delay;
-                usec_t interval;
-        } table[] = {
-                { 0,                    USEC_PER_MINUTE      },
-                { 10 * USEC_PER_MINUTE, 15 * USEC_PER_MINUTE },
-                { USEC_PER_HOUR,        30 * USEC_PER_MINUTE },
-                { 3 * USEC_PER_HOUR,    USEC_PER_HOUR        },
-        };
-
-        usec_t left, sub;
-        unsigned i = ELEMENTSOF(table) - 1;
-
-        /* If the time is already passed, then don't announce */
-        if (n >= elapse)
-                return 0;
-
-        left = elapse - n;
-        while (left < table[i].delay)
-                i--;
-        sub = (left / table[i].interval) * table[i].interval;
-
-        assert(sub < elapse);
-        return elapse - sub;
-}
-
-static usec_t when_nologin(usec_t elapse) {
-        return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
-}
-
-static const char *mode_to_string(enum sd_shutdown_mode m) {
-        switch (m) {
-        case SD_SHUTDOWN_REBOOT:
-                return "reboot";
-        case SD_SHUTDOWN_POWEROFF:
-                return "poweroff";
-        case SD_SHUTDOWN_HALT:
-                return "halt";
-        case SD_SHUTDOWN_KEXEC:
-                return "kexec";
-        default:
-                return NULL;
-        }
-}
-
-static int update_schedule_file(struct sd_shutdown_command *c) {
-        int r;
-        _cleanup_fclose_ FILE *f = NULL;
-        _cleanup_free_ char *t = NULL, *temp_path = NULL;
-
-        assert(c);
-
-        r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create shutdown subdirectory: %m");
-
-        t = cescape(c->wall_message);
-        if (!t)
-                return log_oom();
-
-        r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
-        if (r < 0)
-                return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m");
-
-        fchmod(fileno(f), 0644);
-
-        fprintf(f,
-                "USEC="USEC_FMT"\n"
-                "WARN_WALL=%i\n"
-                "MODE=%s\n",
-                c->usec,
-                c->warn_wall,
-                mode_to_string(c->mode));
-
-        if (c->dry_run)
-                fputs("DRY_RUN=1\n", f);
-
-        if (!isempty(t))
-                fprintf(f, "WALL_MESSAGE=%s\n", t);
-
-        fflush(f);
-
-        if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
-                log_error_errno(errno, "Failed to write information about scheduled shutdowns: %m");
-                r = -errno;
-
-                unlink(temp_path);
-                unlink("/run/systemd/shutdown/scheduled");
-        }
-
-        return r;
-}
-
-static bool scheduled(struct sd_shutdown_command *c) {
-        return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
-}
-
-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;
-        union shutdown_buffer b = {};
-        struct pollfd pollfd[_FD_MAX] = {};
-        bool exec_shutdown = false, unlink_nologin = 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_AUTO);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        n_fds = sd_listen_fds(true);
-        if (n_fds < 0) {
-                log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
-                return EXIT_FAILURE;
-        }
-
-        if (n_fds != 1) {
-                log_error("Need exactly one file descriptor.");
-                return EXIT_FAILURE;
-        }
-
-        pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
-        pollfd[FD_SOCKET].events = POLLIN;
-
-        for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
-                pollfd[i].events = POLLIN;
-                pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
-                if (pollfd[i].fd < 0) {
-                        log_error_errno(errno, "timerfd_create(): %m");
-                        goto finish;
-                }
-        }
-
-        log_debug("systemd-shutdownd running as pid "PID_FMT, getpid());
-
-        sd_notify(false,
-                  "READY=1\n"
-                  "STATUS=Processing requests...");
-
-        for (;;) {
-                int k;
-                usec_t n;
-
-                k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
-                if (k < 0) {
-
-                        if (errno == EAGAIN || errno == EINTR)
-                                continue;
-
-                        log_error_errno(errno, "poll(): %m");
-                        goto finish;
-                }
-
-                /* Exit on idle */
-                if (k == 0)
-                        break;
-
-                n = now(CLOCK_REALTIME);
-
-                if (pollfd[FD_SOCKET].revents) {
-
-                        k = read_packet(pollfd[FD_SOCKET].fd, &b);
-                        if (k < 0)
-                                goto finish;
-                        else if (k > 0) {
-                                struct itimerspec its;
-                                char date[FORMAT_TIMESTAMP_MAX];
-
-                                if (!scheduled(&b.command)) {
-                                        log_info("Shutdown canceled.");
-                                        if (b.command.warn_wall)
-                                                warn_wall(0, &b.command);
-                                        break;
-                                }
-
-                                zero(its);
-                                if (b.command.warn_wall) {
-                                        /* Send wall messages every so often */
-                                        timespec_store(&its.it_value, when_wall(n, b.command.usec));
-
-                                        /* Warn immediately if less than 15 minutes are left */
-                                        if (n < b.command.usec &&
-                                            n + 15*USEC_PER_MINUTE >= b.command.usec)
-                                                warn_wall(n, &b.command);
-                                }
-                                if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
-                                        log_error_errno(errno, "timerfd_settime(): %m");
-                                        goto finish;
-                                }
-
-                                /* Disallow logins 5 minutes prior to shutdown */
-                                zero(its);
-                                timespec_store(&its.it_value, when_nologin(b.command.usec));
-                                if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
-                                        log_error_errno(errno, "timerfd_settime(): %m");
-                                        goto finish;
-                                }
-
-                                /* Shutdown after the specified time is reached */
-                                zero(its);
-                                timespec_store(&its.it_value, b.command.usec);
-                                if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
-                                        log_error_errno(errno, "timerfd_settime(): %m");
-                                        goto finish;
-                                }
-
-                                update_schedule_file(&b.command);
-
-                                sd_notifyf(false,
-                                           "STATUS=Shutting down at %s (%s)...",
-                                           format_timestamp(date, sizeof(date), b.command.usec),
-                                           mode_to_string(b.command.mode));
-
-                                log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
-                        }
-                }
-
-                if (pollfd[FD_WALL_TIMER].revents) {
-                        struct itimerspec its = {};
-
-                        warn_wall(n, &b.command);
-                        flush_fd(pollfd[FD_WALL_TIMER].fd);
-
-                        /* Restart timer */
-                        timespec_store(&its.it_value, when_wall(n, b.command.usec));
-                        if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
-                                log_error_errno(errno, "timerfd_settime(): %m");
-                                goto finish;
-                        }
-                }
-
-                if (pollfd[FD_NOLOGIN_TIMER].revents) {
-                        int e;
-
-                        log_info("Creating /run/nologin, blocking further logins...");
-
-                        e = write_string_file_atomic("/run/nologin", "System is going down.");
-                        if (e < 0)
-                                log_error_errno(e, "Failed to create /run/nologin: %m");
-                        else
-                                unlink_nologin = true;
-
-                        flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
-                }
-
-                if (pollfd[FD_SHUTDOWN_TIMER].revents) {
-                        exec_shutdown = true;
-                        goto finish;
-                }
-        }
-
-        r = EXIT_SUCCESS;
-
-        log_debug("systemd-shutdownd stopped as pid "PID_FMT, getpid());
-
-finish:
-
-        for (i = 0; i < _FD_MAX; i++)
-                safe_close(pollfd[i].fd);
-
-        if (unlink_nologin)
-                unlink("/run/nologin");
-
-        unlink("/run/systemd/shutdown/scheduled");
-
-        if (exec_shutdown && !b.command.dry_run) {
-                char sw[3];
-
-                sw[0] = '-';
-                sw[1] = b.command.mode;
-                sw[2] = 0;
-
-                execl(SYSTEMCTL_BINARY_PATH,
-                      "shutdown",
-                      sw,
-                      "now",
-                      (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
-                      (b.command.warn_wall ? NULL : "--no-wall"),
-                      NULL);
-
-                log_error_errno(errno, "Failed to execute /sbin/shutdown: %m");
-        }
-
-        sd_notify(false,
-                  "STOPPING=\n"
-                  "STATUS=Exiting...");
-
-        return r;
-}
diff --git a/src/systemd/sd-shutdown.h b/src/systemd/sd-shutdown.h
deleted file mode 100644
index 9ff377f..0000000
--- a/src/systemd/sd-shutdown.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foosdshutdownhfoo
-#define foosdshutdownhfoo
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/* Interface for scheduling and cancelling timed shutdowns. */
-
-#include <inttypes.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _sd_packed_
-#  define _sd_packed_ __attribute__((packed))
-#endif
-
-typedef enum sd_shutdown_mode {
-        SD_SHUTDOWN_NONE = 0,
-        SD_SHUTDOWN_REBOOT = 'r',
-        SD_SHUTDOWN_POWEROFF = 'P',
-        SD_SHUTDOWN_HALT = 'H',
-        SD_SHUTDOWN_KEXEC = 'K'
-} sd_shutdown_mode_t;
-
-/* Calculate the size of the message as "offsetof(struct
- * sd_shutdown_command, wall_message) +
- * strlen(command.wall_message)" */
-struct sd_shutdown_command {
-        /* Microseconds after the epoch 1970 UTC */
-        uint64_t usec;
-
-        /* H, P, r, i.e. the switches usually passed to
-         * /usr/bin/shutdown to select whether to halt, power-off or
-         * reboot the machine */
-        sd_shutdown_mode_t mode:8;
-
-        /* If non-zero, don't actually shut down, just pretend */
-        unsigned dry_run:1;
-
-        /* If non-zero, send our wall message */
-        unsigned warn_wall:1;
-
-        /* The wall message to send around. Leave empty for the
-         * default wall message */
-        char wall_message[];
-} _sd_packed_;
-
-/* The scheme is very simple:
- *
- * To schedule a shutdown, simply fill in and send a single
- * AF_UNIX/SOCK_DGRAM datagram with the structure above suffixed with
- * the wall message to the socket /run/systemd/shutdownd (leave an
- * empty wall message for the default shutdown message). To calculate
- * the size of the message, use "offsetof(struct sd_shutdown_command,
- * wall_message) + strlen(command.wall_message)".
- *
- * To cancel a shutdown, do the same, but send a fully zeroed-out
- * structure.
- *
- * To be notified about scheduled shutdowns, create an inotify watch
- * on /run/shutdown/. Whenever a file called "scheduled" appears, a
- * shutdown is scheduled. If it is removed, it is canceled. If it is
- * replaced, the scheduled shutdown has been changed. The file contains
- * a simple, environment-like block that contains information about
- * the scheduled shutdown:
- *
- * USEC=
- * encodes the time for the shutdown in usecs since the epoch UTC,
- * formatted as a numeric string.
- *
- * WARN_WALL=
- * is 1 if a wall message shall be sent
- *
- * DRY_RUN=
- * is 1 if a dry-run shutdown is scheduled
- *
- * MODE=
- * is the shutdown mode, one of "poweroff", "reboot", "halt", "kexec"
- *
- * WALL_MESSAGE=
- * is the wall message to use, with all special characters escaped in C-style.
- *
- * Note that some fields might be missing if they do not apply.
- *
- * Note that the file is first written to a temporary file and then
- * renamed, in order to provide atomic properties for readers: if the
- * file exists under the name "scheduled", it is guaranteed to be fully
- * written. A reader should ignore all files in that directory by any
- * other name.
- *
- * Scheduled shutdowns are only accepted from privileged processes,
- * but anyone may watch the directory and the file in it.
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/units/.gitignore b/units/.gitignore
index ad469c1..d81d0c5 100644
--- a/units/.gitignore
+++ b/units/.gitignore
@@ -59,7 +59,6 @@
 /systemd-resolved.service.m4
 /systemd-hibernate-resume at .service
 /systemd-rfkill at .service
-/systemd-shutdownd.service
 /systemd-suspend.service
 /systemd-sysctl.service
 /systemd-sysusers.service
diff --git a/units/systemd-shutdownd.service.in b/units/systemd-shutdownd.service.in
deleted file mode 100644
index d951742..0000000
--- a/units/systemd-shutdownd.service.in
+++ /dev/null
@@ -1,15 +0,0 @@
-#  This file is part of systemd.
-#
-#  systemd is free software; you can redistribute it and/or modify it
-#  under the terms of the GNU Lesser General Public License as published by
-#  the Free Software Foundation; either version 2.1 of the License, or
-#  (at your option) any later version.
-
-[Unit]
-Description=Delayed Shutdown Service
-Documentation=man:systemd-shutdownd.service(8)
-DefaultDependencies=no
-
-[Service]
-ExecStart=@rootlibexecdir@/systemd-shutdownd
-NotifyAccess=all
diff --git a/units/systemd-shutdownd.socket b/units/systemd-shutdownd.socket
deleted file mode 100644
index 9421ce8..0000000
--- a/units/systemd-shutdownd.socket
+++ /dev/null
@@ -1,18 +0,0 @@
-#  This file is part of systemd.
-#
-#  systemd is free software; you can redistribute it and/or modify it
-#  under the terms of the GNU Lesser General Public License as published by
-#  the Free Software Foundation; either version 2.1 of the License, or
-#  (at your option) any later version.
-
-[Unit]
-Description=Delayed Shutdown Socket
-Documentation=man:systemd-shutdownd.service(8)
-DefaultDependencies=no
-Before=sockets.target
-
-[Socket]
-ListenDatagram=/run/systemd/shutdownd
-SocketMode=0600
-PassCredentials=yes
-PassSecurity=yes

commit f0efea232a582660b25ee992853c78cf825721b6
Author: Daniel Mack <daniel at zonque.org>
Date:   Thu Apr 23 16:03:02 2015 +0200

    systemctl: talk to logind for scheduled shutdowns
    
    Drop the code which communicates with shutdownd via its private socket,
    and use the functionality in logind instead.
    
    The code pathes which talk to logind have to create their own ad-hoc
    bus connection because by default, systemctl connects to systemd's
    private socket.

diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 38d15ff..1212673 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -34,7 +34,6 @@
 #include <stddef.h>
 
 #include "sd-daemon.h"
-#include "sd-shutdown.h"
 #include "sd-login.h"
 #include "sd-bus.h"
 #include "log.h"
@@ -7173,51 +7172,6 @@ found:
         return verb->dispatch(bus, argv + optind);
 }
 
-static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
-
-        struct sd_shutdown_command c = {
-                .usec = t,
-                .mode = mode,
-                .dry_run = dry_run,
-                .warn_wall = warn,
-        };
-
-        union sockaddr_union sockaddr = {
-                .un.sun_family = AF_UNIX,
-                .un.sun_path = "/run/systemd/shutdownd",
-        };
-
-        struct iovec iovec[2] = {{
-                 .iov_base = (char*) &c,
-                 .iov_len = offsetof(struct sd_shutdown_command, wall_message),
-        }};
-
-        struct msghdr msghdr = {
-                .msg_name = &sockaddr,
-                .msg_namelen = offsetof(struct sockaddr_un, sun_path)
-                               + strlen("/run/systemd/shutdownd"),
-                .msg_iov = iovec,
-                .msg_iovlen = 1,
-        };
-
-        _cleanup_close_ int fd;
-
-        fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
-        if (fd < 0)
-                return -errno;
-
-        if (!isempty(message)) {
-                iovec[1].iov_base = (char*) message;
-                iovec[1].iov_len = strlen(message);
-                msghdr.msg_iovlen++;
-        }
-
-        if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0)
-                return -errno;
-
-        return 0;
-}
-
 static int reload_with_fallback(sd_bus *bus) {
 
         if (bus) {
@@ -7325,23 +7279,68 @@ static int halt_main(sd_bus *bus) {
         }
 
         if (arg_when > 0) {
+                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+                _cleanup_bus_close_unref_ sd_bus *b = NULL;
                 _cleanup_free_ char *m;
 
+                if (avoid_bus()) {
+                        log_error("Unable to perform operation without bus connection.");
+                        return -ENOSYS;
+                }
+
+                r = sd_bus_open_system(&b);
+                if (r < 0)
+                        return log_error_errno(r, "Unable to open system bus: %m\n");
+
                 m = strv_join(arg_wall, " ");
                 if (!m)
                         return log_oom();
 
-                r = send_shutdownd(arg_when,
-                                   arg_action == ACTION_HALT     ? 'H' :
-                                   arg_action == ACTION_POWEROFF ? 'P' :
-                                   arg_action == ACTION_KEXEC    ? 'K' :
-                                                                   'r',
-                                   arg_dry,
-                                   !arg_no_wall,
-                                   m);
+                r = sd_bus_set_property(
+                                b,
+                                "org.freedesktop.login1",
+                                "/org/freedesktop/login1",
+                                "org.freedesktop.login1.Manager",
+                                "WallMessage",
+                                &error,
+                                "s", m);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to set WallMessage property in logind: %s",
+                                          bus_error_message(&error, r));
+                        sd_bus_error_free(&error);
+                }
+
+                r = sd_bus_set_property(
+                                b,
+                                "org.freedesktop.login1",
+                                "/org/freedesktop/login1",
+                                "org.freedesktop.login1.Manager",
+                                "EnableWallMessages",
+                                &error,
+                                "b", !arg_no_wall);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to set EnableWallMessages property in logind: %s",
+                                          bus_error_message(&error, r));
+                        sd_bus_error_free(&error);
+                }
 
+                r = sd_bus_call_method(
+                                b,
+                                "org.freedesktop.login1",
+                                "/org/freedesktop/login1",
+                                "org.freedesktop.login1.Manager",
+                                "ScheduleShutdown",
+                                &error,
+                                NULL,
+                                "st",
+                                arg_action == ACTION_HALT     ? "halt" :
+                                arg_action == ACTION_POWEROFF ? "poweroff" :
+                                arg_action == ACTION_KEXEC    ? "kexec" :
+                                                                "reboot",
+                                arg_when);
                 if (r < 0)
-                        log_warning_errno(r, "Failed to talk to shutdownd, proceeding with immediate shutdown: %m");
+                        log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s",
+                                          bus_error_message(&error, r));
                 else {
                         char date[FORMAT_TIMESTAMP_MAX];
 
@@ -7457,8 +7456,19 @@ int main(int argc, char*argv[]) {
                 break;
 
         case ACTION_CANCEL_SHUTDOWN: {
+                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+                _cleanup_bus_close_unref_ sd_bus *b = NULL;
                 _cleanup_free_ char *m = NULL;
 
+                if (avoid_bus()) {
+                        log_error("Unable to perform operation without bus connection.");
+                        return -ENOSYS;
+                }
+
+                r = sd_bus_open_system(&b);
+                if (r < 0)
+                        return log_error_errno(r, "Unable to open system bus: %m\n");
+
                 if (arg_wall) {
                         m = strv_join(arg_wall, " ");
                         if (!m) {
@@ -7467,9 +7477,45 @@ int main(int argc, char*argv[]) {
                         }
                 }
 
-                r = send_shutdownd(arg_when, SD_SHUTDOWN_NONE, false, !arg_no_wall, m);
+                r = sd_bus_set_property(
+                                b,
+                                "org.freedesktop.login1",
+                                "/org/freedesktop/login1",
+                                "org.freedesktop.login1.Manager",
+                                "WallMessage",
+                                &error,
+                                "s", arg_wall);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to set WallMessage property in logind: %s",
+                                          bus_error_message(&error, r));
+                        sd_bus_error_free(&error);
+                }
+
+                r = sd_bus_set_property(
+                                b,
+                                "org.freedesktop.login1",
+                                "/org/freedesktop/login1",
+                                "org.freedesktop.login1.Manager",
+                                "EnableWallMessages",
+                                &error,
+                                "b", !arg_no_wall);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to set EnableWallMessages property in logind: %s",
+                                          bus_error_message(&error, r));
+                        sd_bus_error_free(&error);
+                }
+
+                r = sd_bus_call_method(
+                                b,
+                                "org.freedesktop.login1",
+                                "/org/freedesktop/login1",
+                                "org.freedesktop.login1.Manager",
+                                "CancelScheduledShutdown",
+                                &error,
+                                NULL, NULL);
                 if (r < 0)
-                        log_warning_errno(r, "Failed to talk to shutdownd, shutdown hasn't been cancelled: %m");
+                        log_warning_errno(r, "Failed to talk to logind, shutdown hasn't been cancelled: %s",
+                                          bus_error_message(&error, r));
                 break;
         }
 

commit 867c37f6bb20886204679df9a43c973e2d7e44a5
Author: Daniel Mack <daniel at zonque.org>
Date:   Thu Apr 23 17:11:28 2015 +0200

    logind: add support for /run/nologin and /run/systemd/shutdown/scheduled
    
    Port over more code from shutdownd and teach logind to write /run/nologin at
    least 5 minutes before the system is going down, and
    /run/systemd/shutdown/scheduled when a shutdown is scheduled.

diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 80514f3..1eda3c2 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1770,6 +1770,71 @@ static int method_suspend(sd_bus *bus, sd_bus_message *message, void *userdata,
                         error);
 }
 
+static int nologin_timeout_handler(
+                        sd_event_source *s,
+                        uint64_t usec,
+                        void *userdata) {
+
+        Manager *m = userdata;
+        int r;
+
+        log_info("Creating /run/nologin, blocking further logins...");
+
+        r = write_string_file_atomic("/run/nologin", "System is going down.");
+        if (r < 0)
+                log_error_errno(r, "Failed to create /run/nologin: %m");
+        else
+                m->unlink_nologin = true;
+
+        return 0;
+}
+
+static int update_schedule_file(Manager *m) {
+
+        int r;
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_free_ char *t = NULL, *temp_path = NULL;
+
+        assert(m);
+
+        r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create shutdown subdirectory: %m");
+
+        t = cescape(m->wall_message);
+        if (!t)
+                return log_oom();
+
+        r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
+        if (r < 0)
+                return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m");
+
+        (void) fchmod(fileno(f), 0644);
+
+        fprintf(f,
+                "USEC="USEC_FMT"\n"
+                "WARN_WALL=%i\n"
+                "MODE=%s\n",
+                m->scheduled_shutdown_timeout,
+                m->enable_wall_messages,
+                m->scheduled_shutdown_type);
+
+        if (!isempty(m->wall_message))
+                fprintf(f, "WALL_MESSAGE=%s\n", t);
+
+        (void) fflush_and_check(f);
+
+        if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
+                log_error_errno(errno, "Failed to write information about scheduled shutdowns: %m");
+                r = -errno;
+
+                (void) unlink(temp_path);
+                (void) unlink("/run/systemd/shutdown/scheduled");
+        }
+
+        return r;
+}
+
 static int manager_scheduled_shutdown_handler(
                         sd_event_source *s,
                         uint64_t usec,
@@ -1857,6 +1922,21 @@ static int method_schedule_shutdown(sd_bus *bus, sd_bus_message *message, void *
                 return log_oom();
         }
 
+        if (m->nologin_timeout_source) {
+                r = sd_event_source_set_time(m->nologin_timeout_source, elapse);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_source_set_time() failed: %m\n");
+
+                r = sd_event_source_set_enabled(m->nologin_timeout_source, SD_EVENT_ONESHOT);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_source_set_enabled() failed: %m\n");
+        } else {
+                r = sd_event_add_time(m->event, &m->nologin_timeout_source,
+                                      CLOCK_REALTIME, elapse - 5 * USEC_PER_MINUTE, 0, nologin_timeout_handler, m);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_add_time() failed: %m\n");
+        }
+
         m->scheduled_shutdown_timeout = elapse;
 
         r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
@@ -1877,6 +1957,13 @@ static int method_schedule_shutdown(sd_bus *bus, sd_bus_message *message, void *
         if (r < 0)
                 return r;
 
+        if (!isempty(type)) {
+                r = update_schedule_file(m);
+                if (r < 0)
+                        return r;
+        } else
+                (void) unlink("/run/systemd/shutdown/scheduled");
+
         return sd_bus_reply_method_return(message, NULL);
 }
 
@@ -1891,6 +1978,7 @@ static int method_cancel_scheduled_shutdown(sd_bus *bus, sd_bus_message *message
 
         m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
         m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source);
+        m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source);
         free(m->scheduled_shutdown_type);
         m->scheduled_shutdown_type = NULL;
         m->scheduled_shutdown_timeout = 0;
diff --git a/src/login/logind.c b/src/login/logind.c
index 0d96bbd..7520f13 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -145,6 +145,7 @@ static void manager_free(Manager *m) {
         sd_event_source_unref(m->idle_action_event_source);
         sd_event_source_unref(m->inhibit_timeout_source);
         sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+        sd_event_source_unref(m->nologin_timeout_source);
         sd_event_source_unref(m->wall_message_timeout_source);
 
         sd_event_source_unref(m->console_active_event_source);
@@ -168,6 +169,9 @@ static void manager_free(Manager *m) {
         if (m->udev)
                 udev_unref(m->udev);
 
+        if (m->unlink_nologin)
+                unlink("/run/nologin");
+
         bus_verify_polkit_async_registry_free(m->polkit_registry);
 
         sd_bus_unref(m->bus);
diff --git a/src/login/logind.h b/src/login/logind.h
index 1c60b01..56c05b8 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -102,6 +102,8 @@ struct Manager {
         sd_event_source *scheduled_shutdown_timeout_source;
         uid_t scheduled_shutdown_uid;
         char *scheduled_shutdown_tty;
+        sd_event_source *nologin_timeout_source;
+        bool unlink_nologin;
 
         char *wall_message;
         unsigned enable_wall_messages;

commit e2fa5721c3ee5ea400b99a6463e8c1c257e20415
Author: Daniel Mack <daniel at zonque.org>
Date:   Wed Apr 22 17:20:42 2015 +0200

    logind: add code for UTMP wall messages
    
    Add a timer to print UTMP wall messages so that it repeatedly informs users
    about a scheduled shutdown:
    
     * every 1 minute with less than 10 minutes to go
     * every 15 minutes with less than 60 minutes to go
     * every 30 minutes with less than 180 minutes (3 hours) to go
     * every 60 minutes if more than that to go
    
    This functionality only active if the .EnableWallMessages DBus property
    is set to true. Also, a custom string can be added to the wall message,
    set through the WallMessagePrefix property.

diff --git a/Makefile.am b/Makefile.am
index f936534..e22b2e3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5987,6 +5987,7 @@ libsystemd_logind_core_la_SOURCES = \
 	src/login/logind-session-dbus.c \
 	src/login/logind-seat-dbus.c \
 	src/login/logind-user-dbus.c \
+	src/login/logind-utmp.c \
 	src/login/logind-acl.h
 
 libsystemd_logind_core_la_LIBADD = \
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 0fcf2cc..80514f3 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -43,6 +43,7 @@
 #include "formats-util.h"
 #include "process-util.h"
 #include "terminal-util.h"
+#include "utmp-wtmp.h"
 
 int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) {
         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
@@ -1800,6 +1801,7 @@ static int manager_scheduled_shutdown_handler(
 
 static int method_schedule_shutdown(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
+        _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
         const char *action_multiple_sessions = NULL;
         const char *action_ignore_inhibit = NULL;
         const char *action = NULL;
@@ -1857,6 +1859,24 @@ static int method_schedule_shutdown(sd_bus *bus, sd_bus_message *message, void *
 
         m->scheduled_shutdown_timeout = elapse;
 
+        r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
+        if (r >= 0) {
+                const char *tty;
+
+                (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid);
+                (void) sd_bus_creds_get_tty(creds, &tty);
+
+                r = free_and_strdup(&m->scheduled_shutdown_tty, tty);
+                if (r < 0) {
+                        m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+                        return log_oom();
+                }
+        }
+
+        r = manager_setup_wall_message_timer(m);
+        if (r < 0)
+                return r;
+
         return sd_bus_reply_method_return(message, NULL);
 }
 
@@ -1870,10 +1890,27 @@ static int method_cancel_scheduled_shutdown(sd_bus *bus, sd_bus_message *message
         cancelled = m->scheduled_shutdown_type != NULL;
 
         m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+        m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source);
         free(m->scheduled_shutdown_type);
         m->scheduled_shutdown_type = NULL;
         m->scheduled_shutdown_timeout = 0;
 
+        if (cancelled) {
+                _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+                const char *tty = NULL;
+                uid_t uid = 0;
+                int r;
+
+                r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
+                if (r >= 0) {
+                        (void) sd_bus_creds_get_uid(creds, &uid);
+                        (void) sd_bus_creds_get_tty(creds, &tty);
+                }
+
+                utmp_wall("The system shutdown has been cancelled",
+                          lookup_uid(uid), tty, logind_wall_tty_filter, m);
+        }
+
         return sd_bus_reply_method_return(message, "b", cancelled);
 }
 
@@ -2275,6 +2312,9 @@ fail:
 const sd_bus_vtable manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
+        SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", NULL, NULL, offsetof(Manager, enable_wall_messages), 0),
+        SD_BUS_WRITABLE_PROPERTY("WallMessage", "s", NULL, NULL, offsetof(Manager, wall_message), 0),
+
         SD_BUS_PROPERTY("NAutoVTs", "u", NULL, offsetof(Manager, n_autovts), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST),
diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c
new file mode 100644
index 0000000..9bbffe3
--- /dev/null
+++ b/src/login/logind-utmp.c
@@ -0,0 +1,182 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Daniel Mack
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include "sd-messages.h"
+#include "strv.h"
+#include "special.h"
+#include "unit-name.h"
+#include "audit.h"
+#include "bus-util.h"
+#include "bus-error.h"
+#include "bus-common-errors.h"
+#include "logind.h"
+#include "formats-util.h"
+#include "utmp-wtmp.h"
+
+_const_ static usec_t when_wall(usec_t n, usec_t elapse) {
+
+        usec_t left;
+        unsigned int i;
+        static const int wall_timers[] = {
+                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+                25, 40, 55, 70, 100, 130, 150, 180,
+        };
+
+        /* If the time is already passed, then don't announce */
+        if (n >= elapse)
+                return 0;
+
+        left = elapse - n;
+
+        for (i = 1; i < ELEMENTSOF(wall_timers); i++)
+                if (wall_timers[i] * USEC_PER_MINUTE >= left)
+                        return left - wall_timers[i-1] * USEC_PER_MINUTE;
+
+        return left % USEC_PER_HOUR;
+}
+
+bool logind_wall_tty_filter(const char *tty, void *userdata) {
+
+        Manager *m = userdata;
+
+        assert(m);
+
+        if (!startswith(tty, "/dev/"))
+                return true;
+
+        return !streq(tty + 5, m->scheduled_shutdown_tty);
+}
+
+static int warn_wall(Manager *m, usec_t n) {
+        char date[FORMAT_TIMESTAMP_MAX] = {};
+        _cleanup_free_ char *l = NULL;
+        usec_t left;
+        int r;
+
+        assert(m);
+
+        if (!m->enable_wall_messages)
+                return 0;
+
+        left = m->scheduled_shutdown_timeout > n;
+
+        r = asprintf(&l, "%s%sThe system is going down for %s %s%s!",
+                     strempty(m->wall_message),
+                     isempty(m->wall_message) ? "" : "\n",
+                     m->scheduled_shutdown_type,
+                     left ? "at " : "NOW",
+                     left ? format_timestamp(date, sizeof(date), m->scheduled_shutdown_timeout) : "");
+        if (r < 0) {
+                log_oom();
+                return 0;
+        }
+
+        utmp_wall(l, lookup_uid(m->scheduled_shutdown_uid),
+                  m->scheduled_shutdown_tty, logind_wall_tty_filter, m);
+
+        return 1;
+}
+
+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\n");
+
+                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\n");
+        }
+
+        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\n");
+
+                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\n");
+        } 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\n");
+        }
+
+        return 0;
+}
diff --git a/src/login/logind.c b/src/login/logind.c
index 3a4afdd..0d96bbd 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -145,6 +145,7 @@ static void manager_free(Manager *m) {
         sd_event_source_unref(m->idle_action_event_source);
         sd_event_source_unref(m->inhibit_timeout_source);
         sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+        sd_event_source_unref(m->wall_message_timeout_source);
 
         sd_event_source_unref(m->console_active_event_source);
         sd_event_source_unref(m->udev_seat_event_source);
@@ -178,6 +179,8 @@ static void manager_free(Manager *m) {
         strv_free(m->kill_exclude_users);
 
         free(m->scheduled_shutdown_type);
+        free(m->scheduled_shutdown_tty);
+        free(m->wall_message);
         free(m->action_job);
         free(m);
 }
diff --git a/src/login/logind.h b/src/login/logind.h
index cd2bdc0..1c60b01 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -100,6 +100,12 @@ struct Manager {
         char *scheduled_shutdown_type;
         usec_t scheduled_shutdown_timeout;
         sd_event_source *scheduled_shutdown_timeout_source;
+        uid_t scheduled_shutdown_uid;
+        char *scheduled_shutdown_tty;
+
+        char *wall_message;
+        unsigned enable_wall_messages;
+        sd_event_source *wall_message_timeout_source;
 
         sd_event_source *idle_action_event_source;
         usec_t idle_action_usec;
@@ -185,3 +191,6 @@ int config_parse_tmpfs_size(const char *unit, const char *filename, unsigned lin
 int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret);
 int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret);
 int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret);
+
+int manager_setup_wall_message_timer(Manager *m);
+bool logind_wall_tty_filter(const char *tty, void *userdata);

commit 99f710dde855f7ecb699ddac6ad77923c1f6bc85
Author: Daniel Mack <daniel at zonque.org>
Date:   Fri Apr 24 15:31:29 2015 +0200

    shared/utmp-wtmp: add parameter for origin tty and callback userdata
    
    Instead of looking up the tty from STDIN, let utmp_wall() take an argument
    to specify an origin tty for the wall message. Only if that argument is
    NULL do the STDIN lookup.
    
    Also add an void *userdata argument that is handed back to the callback
    function.

diff --git a/src/journal/journald-wall.c b/src/journal/journald-wall.c
index 5298e45..7863766 100644
--- a/src/journal/journald-wall.c
+++ b/src/journal/journald-wall.c
@@ -65,7 +65,7 @@ void server_forward_wall(
         } else
                 l = message;
 
-        r = utmp_wall(l, "systemd-journald", NULL);
+        r = utmp_wall(l, "systemd-journald", NULL, NULL, NULL);
         if (r < 0)
                 log_debug_errno(r, "Failed to send wall message: %m");
 }
diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c
index d6c4cc8..aaf249d 100644
--- a/src/shared/utmp-wtmp.c
+++ b/src/shared/utmp-wtmp.c
@@ -347,8 +347,14 @@ static int write_to_terminal(const char *tty, const char *message) {
         return 0;
 }
 
-int utmp_wall(const char *message, const char *username, bool (*match_tty)(const char *tty)) {
-        _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *tty = NULL;
+int utmp_wall(
+        const char *message,
+        const char *username,
+        const char *origin_tty,
+        bool (*match_tty)(const char *tty, void *userdata),
+        void *userdata) {
+
+        _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *stdin_tty = NULL;
         char date[FORMAT_TIMESTAMP_MAX];
         struct utmpx *u;
         int r;
@@ -362,14 +368,17 @@ int utmp_wall(const char *message, const char *username, bool (*match_tty)(const
                         return -ENOMEM;
         }
 
-        getttyname_harder(STDIN_FILENO, &tty);
+        if (!origin_tty) {
+                getttyname_harder(STDIN_FILENO, &stdin_tty);
+                origin_tty = stdin_tty;
+        }
 
         if (asprintf(&text,
                      "\a\r\n"
                      "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
                      "%s\r\n\r\n",
                      un ?: username, hn,
-                     tty ? " on " : "", strempty(tty),
+                     origin_tty ? " on " : "", strempty(origin_tty),
                      format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)),
                      message) < 0)
                 return -ENOMEM;
@@ -396,7 +405,7 @@ int utmp_wall(const char *message, const char *username, bool (*match_tty)(const
                         path = buf;
                 }
 
-                if (!match_tty || match_tty(path)) {
+                if (!match_tty || match_tty(path, userdata)) {
                         q = write_to_terminal(path, text);
                         if (q < 0)
                                 r = q;
diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h
index 87d004e..6ac2c7b 100644
--- a/src/shared/utmp-wtmp.h
+++ b/src/shared/utmp-wtmp.h
@@ -33,7 +33,12 @@ int utmp_put_runlevel(int runlevel, int previous);
 int utmp_put_dead_process(const char *id, pid_t pid, int code, int status);
 int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line);
 
-int utmp_wall(const char *message, const char *username, bool (*match_tty)(const char *tty));
+int utmp_wall(
+        const char *message,
+        const char *username,
+        const char *origin_tty,
+        bool (*match_tty)(const char *tty, void *userdata),
+        void *userdata);
 
 #else /* HAVE_UTMP */
 
@@ -55,8 +60,12 @@ static inline int utmp_put_dead_process(const char *id, pid_t pid, int code, int
 static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
         return 0;
 }
-static inline int utmp_wall(const char *message, const char *username,
-                bool (*match_tty)(const char *tty)) {
+static inline int utmp_wall(
+                const char *message,
+                const char *username,
+                const char *origin_tty,
+                bool (*match_tty)(const char *tty, void *userdata),
+                void *userdata);
         return 0;
 }
 
diff --git a/src/shutdownd/shutdownd.c b/src/shutdownd/shutdownd.c
index a05cddc..8b857b5 100644
--- a/src/shutdownd/shutdownd.c
+++ b/src/shutdownd/shutdownd.c
@@ -142,7 +142,7 @@ static void warn_wall(usec_t n, struct sd_shutdown_command *c) {
 
         if (asprintf(&l, "%s%s%s%s!", c->wall_message, c->wall_message[0] ? "\n" : "",
                      prefix, format_timestamp(date, sizeof(date), c->usec)) >= 0)
-                utmp_wall(l, NULL, NULL);
+                utmp_wall(l, NULL, NULL, NULL, NULL);
         else
                 log_error("Failed to allocate wall message");
 }
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 4e702fb..38d15ff 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -251,7 +251,7 @@ static void warn_wall(enum action a) {
                 }
 
                 if (*p) {
-                        utmp_wall(p, NULL, NULL);
+                        utmp_wall(p, NULL, NULL, NULL, NULL);
                         return;
                 }
         }
@@ -259,7 +259,7 @@ static void warn_wall(enum action a) {
         if (!table[a])
                 return;
 
-        utmp_wall(table[a], NULL, NULL);
+        utmp_wall(table[a], NULL, NULL, NULL, NULL);
 }
 
 static bool avoid_bus(void) {
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index 8cd6cab..c440170 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -381,7 +381,7 @@ static int wall_tty_block(void) {
         return fd;
 }
 
-static bool wall_tty_match(const char *path) {
+static bool wall_tty_match(const char *path, void *userdata) {
         int fd, r;
         struct stat st;
         _cleanup_free_ char *p = NULL;
@@ -455,7 +455,7 @@ static int show_passwords(void) {
                         r = q;
 
                 if (wall)
-                        utmp_wall(wall, NULL, wall_tty_match);
+                        utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
         }
 
         return r;

commit 8aaa023ae78f3cb28db3edd87f96b21486810b91
Author: Daniel Mack <daniel at zonque.org>
Date:   Mon Apr 20 15:27:15 2015 +0200

    logind: add .ScheduleShutdown and .CancelScheduledShutdown methods
    
    Add a method called ScheduleShutdown in org.freedesktop.login1.Manager
    which adds a timer to shut down the system at a later point in time.
    
    The first argument holds the type of the schedule that is about to
    happen, and must be one of 'reboot', 'halt' or 'poweroff'.
    
    The second argument specifies the absolute time, based on
    CLOCK_REALTIME in nanoseconds, at which the the operation should be
    executed.
    
    To cancel a previously scheduled shutdown, the CancelScheduledShutdown()
    can be called, which returns a bool, indicating whether a scheduled
    timeout was cancelled.
    
    Also add a new property called ScheduledShutdown which returns the
    equivalent to what was passed in via ScheduleShutdown, as '(st)' type.

diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index f638fe0..0fcf2cc 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -213,6 +213,33 @@ static int property_get_preparing(
         return sd_bus_message_append(reply, "b", b);
 }
 
+static int property_get_scheduled_shutdown(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Manager *m = userdata;
+        int r;
+
+        assert(bus);
+        assert(reply);
+        assert(m);
+
+        r = sd_bus_message_open_container(reply, 'r', "st");
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(reply, "st", m->scheduled_shutdown_type, m->scheduled_shutdown_timeout);
+        if (r < 0)
+                return r;
+
+        return sd_bus_message_close_container(reply);
+}
+
 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction);
 
 static int method_get_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -1742,6 +1769,114 @@ static int method_suspend(sd_bus *bus, sd_bus_message *message, void *userdata,
                         error);
 }
 
+static int manager_scheduled_shutdown_handler(
+                        sd_event_source *s,
+                        uint64_t usec,
+                        void *userdata) {
+
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        Manager *m = userdata;
+        const char *target;
+        int r;
+
+        assert(m);
+
+        if (isempty(m->scheduled_shutdown_type))
+                return 0;
+
+        if (streq(m->scheduled_shutdown_type, "halt"))
+                target = SPECIAL_HALT_TARGET;
+        else if (streq(m->scheduled_shutdown_type, "poweroff"))
+                target = SPECIAL_POWEROFF_TARGET;
+        else
+                target = SPECIAL_REBOOT_TARGET;
+
+        r = execute_shutdown_or_sleep(m, 0, target, &error);
+        if (r < 0)
+                return log_error_errno(r, "Unable to execute transition to %s: %m\n", target);
+
+        return 0;
+}
+
+static int method_schedule_shutdown(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = userdata;
+        const char *action_multiple_sessions = NULL;
+        const char *action_ignore_inhibit = NULL;
+        const char *action = NULL;
+        uint64_t elapse;
+        char *type;
+        int r;
+
+        assert(m);
+        assert(message);
+
+        r = sd_bus_message_read(message, "st", &type, &elapse);
+        if (r < 0)
+                return r;
+
+        if (streq(type, "reboot")) {
+                action = "org.freedesktop.login1.reboot";
+                action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions";
+                action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit";
+        } else if (streq(type, "halt")) {
+                action = "org.freedesktop.login1.halt";
+                action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions";
+                action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit";
+        } else if (streq(type, "poweroff")) {
+                action = "org.freedesktop.login1.poweroff";
+                action_multiple_sessions = "org.freedesktop.login1.poweroff-multiple-sessions";
+                action_ignore_inhibit = "org.freedesktop.login1.poweroff-ignore-inhibit";
+        } else
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
+
+        r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, false,
+                                  action, action_multiple_sessions, action_ignore_inhibit, error);
+        if (r != 0)
+                return r;
+
+        if (m->scheduled_shutdown_timeout_source) {
+                r = sd_event_source_set_time(m->scheduled_shutdown_timeout_source, elapse);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_source_set_time() failed: %m\n");
+
+                r = sd_event_source_set_enabled(m->scheduled_shutdown_timeout_source, SD_EVENT_ONESHOT);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_source_set_enabled() failed: %m\n");
+        } else {
+                r = sd_event_add_time(m->event, &m->scheduled_shutdown_timeout_source,
+                                      CLOCK_REALTIME, elapse, 0, manager_scheduled_shutdown_handler, m);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_add_time() failed: %m\n");
+        }
+
+        r = free_and_strdup(&m->scheduled_shutdown_type, type);
+        if (r < 0) {
+                m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+                return log_oom();
+        }
+
+        m->scheduled_shutdown_timeout = elapse;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_cancel_scheduled_shutdown(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = userdata;
+        bool cancelled;
+
+        assert(m);
+        assert(message);
+
+        cancelled = m->scheduled_shutdown_type != NULL;
+
+        m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+        free(m->scheduled_shutdown_type);
+        m->scheduled_shutdown_type = NULL;
+        m->scheduled_shutdown_timeout = 0;
+
+        return sd_bus_reply_method_return(message, "b", cancelled);
+}
+
 static int method_hibernate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
@@ -2161,6 +2296,7 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_PROPERTY("IdleActionUSec", "t", NULL, offsetof(Manager, idle_action_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("PreparingForShutdown", "b", property_get_preparing, 0, 0),
         SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0),
+        SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0),
 
         SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -2190,6 +2326,8 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/login/logind.c b/src/login/logind.c
index d8163ec..3a4afdd 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -144,6 +144,7 @@ static void manager_free(Manager *m) {
 
         sd_event_source_unref(m->idle_action_event_source);
         sd_event_source_unref(m->inhibit_timeout_source);
+        sd_event_source_unref(m->scheduled_shutdown_timeout_source);
 
         sd_event_source_unref(m->console_active_event_source);
         sd_event_source_unref(m->udev_seat_event_source);
@@ -176,6 +177,7 @@ static void manager_free(Manager *m) {
         strv_free(m->kill_only_users);
         strv_free(m->kill_exclude_users);
 
+        free(m->scheduled_shutdown_type);
         free(m->action_job);
         free(m);
 }
diff --git a/src/login/logind.h b/src/login/logind.h
index 2cb19ca..cd2bdc0 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -97,6 +97,10 @@ struct Manager {
         char *action_job;
         sd_event_source *inhibit_timeout_source;
 
+        char *scheduled_shutdown_type;
+        usec_t scheduled_shutdown_timeout;
+        sd_event_source *scheduled_shutdown_timeout_source;
+
         sd_event_source *idle_action_event_source;
         usec_t idle_action_usec;
         usec_t idle_action_not_before_usec;

commit b7aa9589e0bf37cd2682dee4528bd32a794d1363
Author: Daniel Mack <daniel at zonque.org>
Date:   Mon Apr 20 15:19:26 2015 +0200

    logind: factor out polkit checks
    
    Factor out the code to ask polkit for authorization from
    method_do_shutdown_or_sleep() into an own function called
    verify_shutdown_creds().
    
    This is needed in order to also use the same checks when shutdown
    operations are scheduled. For that, it's also necessary to allow
    NULL values for that action{,_multiple_sessions,_ignore_inhibit)
    arguments, which will suppress the call if no action string is
    passed.

diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index e8a92db..f638fe0 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1590,47 +1590,25 @@ int bus_manager_shutdown_or_sleep_now_or_later(
         return r;
 }
 
-static int method_do_shutdown_or_sleep(
+static int verify_shutdown_creds(
                 Manager *m,
                 sd_bus_message *message,
-                const char *unit_name,
                 InhibitWhat w,
+                bool interactive,
                 const char *action,
                 const char *action_multiple_sessions,
                 const char *action_ignore_inhibit,
-                const char *sleep_verb,
                 sd_bus_error *error) {
 
         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
         bool multiple_sessions, blocked;
-        int interactive, r;
         uid_t uid;
+        int r;
 
         assert(m);
         assert(message);
-        assert(unit_name);
         assert(w >= 0);
         assert(w <= _INHIBIT_WHAT_MAX);
-        assert(action);
-        assert(action_multiple_sessions);
-        assert(action_ignore_inhibit);
-
-        r = sd_bus_message_read(message, "b", &interactive);
-        if (r < 0)
-                return r;
-
-        /* Don't allow multiple jobs being executed at the same time */
-        if (m->action_what)
-                return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress");
-
-        if (sleep_verb) {
-                r = can_sleep(sleep_verb);
-                if (r < 0)
-                        return r;
-
-                if (r == 0)
-                        return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported");
-        }
 
         r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
         if (r < 0)
@@ -1647,7 +1625,7 @@ static int method_do_shutdown_or_sleep(
         multiple_sessions = r > 0;
         blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
 
-        if (multiple_sessions) {
+        if (multiple_sessions && action_multiple_sessions) {
                 r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, interactive, UID_INVALID, &m->polkit_registry, error);
                 if (r < 0)
                         return r;
@@ -1655,7 +1633,7 @@ static int method_do_shutdown_or_sleep(
                         return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
         }
 
-        if (blocked) {
+        if (blocked && action_ignore_inhibit) {
                 r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, interactive, UID_INVALID, &m->polkit_registry, error);
                 if (r < 0)
                         return r;
@@ -1663,7 +1641,7 @@ static int method_do_shutdown_or_sleep(
                         return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
         }
 
-        if (!multiple_sessions && !blocked) {
+        if (!multiple_sessions && !blocked && action) {
                 r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, interactive, UID_INVALID, &m->polkit_registry, error);
                 if (r < 0)
                         return r;
@@ -1671,6 +1649,50 @@ static int method_do_shutdown_or_sleep(
                         return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
         }
 
+        return 0;
+}
+
+static int method_do_shutdown_or_sleep(
+                Manager *m,
+                sd_bus_message *message,
+                const char *unit_name,
+                InhibitWhat w,
+                const char *action,
+                const char *action_multiple_sessions,
+                const char *action_ignore_inhibit,
+                const char *sleep_verb,
+                sd_bus_error *error) {
+
+        int interactive, r;
+
+        assert(m);
+        assert(message);
+        assert(unit_name);
+        assert(w >= 0);
+        assert(w <= _INHIBIT_WHAT_MAX);
+
+        r = sd_bus_message_read(message, "b", &interactive);
+        if (r < 0)
+                return r;
+
+        /* Don't allow multiple jobs being executed at the same time */
+        if (m->action_what)
+                return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress");
+
+        if (sleep_verb) {
+                r = can_sleep(sleep_verb);
+                if (r < 0)
+                        return r;
+
+                if (r == 0)
+                        return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported");
+        }
+
+        r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions,
+                                  action_ignore_inhibit, error);
+        if (r != 0)
+                return r;
+
         r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error);
         if (r < 0)
                 return r;

commit 905f0a39ae65010d5229c76b0c54d8a8a8eb56d5
Author: Daniel Mack <daniel at zonque.org>
Date:   Sat Apr 11 19:47:12 2015 +0200

    logind: make local functions static
    
    make manager_gc(), manager_startup(), manager_new(), manager_free()
    and manager_run() static, and kill their forward declarations.

diff --git a/src/login/logind.c b/src/login/logind.c
index ea7c6e7..d8163ec 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -34,7 +34,9 @@
 #include "udev-util.h"
 #include "formats-util.h"
 
-Manager *manager_new(void) {
+static void manager_free(Manager *m);
+
+static Manager *manager_new(void) {
         Manager *m;
         int r;
 
@@ -100,7 +102,7 @@ fail:
         return NULL;
 }
 
-void manager_free(Manager *m) {
+static void manager_free(Manager *m) {
         Session *session;
         User *u;
         Device *d;
@@ -890,7 +892,7 @@ static int manager_connect_udev(Manager *m) {
         return 0;
 }
 
-void manager_gc(Manager *m, bool drop_not_started) {
+static void manager_gc(Manager *m, bool drop_not_started) {
         Seat *seat;
         Session *session;
         User *user;
@@ -1001,7 +1003,7 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us
         return 0;
 }
 
-int manager_startup(Manager *m) {
+static int manager_startup(Manager *m) {
         int r;
         Seat *seat;
         Session *session;
@@ -1088,7 +1090,7 @@ int manager_startup(Manager *m) {
         return 0;
 }
 
-int manager_run(Manager *m) {
+static int manager_run(Manager *m) {
         int r;
 
         assert(m);
diff --git a/src/login/logind.h b/src/login/logind.h
index caf78f7..2cb19ca 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -123,9 +123,6 @@ struct Manager {
         size_t runtime_dir_size;
 };
 
-Manager *manager_new(void);
-void manager_free(Manager *m);
-
 int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device);
 int manager_add_button(Manager *m, const char *name, Button **_button);
 int manager_add_seat(Manager *m, const char *id, Seat **_seat);
@@ -138,12 +135,8 @@ int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor);
 int manager_process_seat_device(Manager *m, struct udev_device *d);
 int manager_process_button_device(Manager *m, struct udev_device *d);
 
-int manager_startup(Manager *m);
-int manager_run(Manager *m);
 int manager_spawn_autovt(Manager *m, unsigned int vtnr);
 
-void manager_gc(Manager *m, bool drop_not_started);
-
 bool manager_shall_kill(Manager *m, const char *user);
 
 int manager_get_idle_hint(Manager *m, dual_timestamp *t);

commit c0f32805086ff65d2905b0e3a9166d9ed6c2bc41
Author: Daniel Mack <daniel at zonque.org>
Date:   Sat Apr 11 18:44:51 2015 +0200

    logind: use sd_event timer source for inhibitor logic
    
    Instead of open-coding the delayed action and inhibit timeout logic,
    switch over to a real sd_event_source based implementation.
    
    This is not only easier to read but also allows us to add more timers
    in the future.

diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 037459b..e8a92db 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1459,17 +1459,75 @@ static int execute_shutdown_or_sleep(
         return 0;
 }
 
+static int manager_inhibit_timeout_handler(
+                        sd_event_source *s,
+                        uint64_t usec,
+                        void *userdata) {
+
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        Inhibitor *offending = NULL;
+        Manager *manager = userdata;
+        int r;
+
+        assert(manager);
+        assert(manager->inhibit_timeout_source == s);
+
+        if (manager->action_what == 0 || manager->action_job)
+                return 0;
+
+        if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
+                _cleanup_free_ char *comm = NULL, *u = NULL;
+
+                (void) get_process_comm(offending->pid, &comm);
+                u = uid_to_name(offending->uid);
+
+                log_notice("Delay lock is active (UID "UID_FMT"/%s, PID "PID_FMT"/%s) but inhibitor timeout is reached.",
+                           offending->uid, strna(u),
+                           offending->pid, strna(comm));
+        }
+
+        /* Actually do the operation */
+        r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error);
+        if (r < 0) {
+                log_warning("Failed to send delayed message: %s", bus_error_message(&error, r));
+
+                manager->action_unit = NULL;
+                manager->action_what = 0;
+        }
+
+        return 0;
+}
+
 static int delay_shutdown_or_sleep(
                 Manager *m,
                 InhibitWhat w,
                 const char *unit_name) {
 
+        int r;
+        usec_t timeout_val;
+
         assert(m);
         assert(w >= 0);
         assert(w < _INHIBIT_WHAT_MAX);
         assert(unit_name);
 
-        m->action_timestamp = now(CLOCK_MONOTONIC);
+        timeout_val = now(CLOCK_MONOTONIC) + m->inhibit_delay_max;
+
+        if (m->inhibit_timeout_source) {
+                r = sd_event_source_set_time(m->inhibit_timeout_source, timeout_val);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_source_set_time() failed: %m\n");
+
+                r = sd_event_source_set_enabled(m->inhibit_timeout_source, SD_EVENT_ONESHOT);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_source_set_enabled() failed: %m\n");
+        } else {
+                r = sd_event_add_time(m->event, &m->inhibit_timeout_source, CLOCK_MONOTONIC,
+                                      timeout_val, 0, manager_inhibit_timeout_handler, m);
+                if (r < 0)
+                        return r;
+        }
+
         m->action_unit = unit_name;
         m->action_what = w;
 
@@ -2358,44 +2416,6 @@ int manager_send_changed(Manager *manager, const char *property, ...) {
                         l);
 }
 
-int manager_dispatch_delayed(Manager *manager) {
-        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-        Inhibitor *offending = NULL;
-        int r;
-
-        assert(manager);
-
-        if (manager->action_what == 0 || manager->action_job)
-                return 0;
-
-        /* Continue delay? */
-        if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
-                _cleanup_free_ char *comm = NULL, *u = NULL;
-
-                get_process_comm(offending->pid, &comm);
-                u = uid_to_name(offending->uid);
-
-                if (manager->action_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC))
-                        return 0;
-
-                log_info("Delay lock is active (UID "UID_FMT"/%s, PID "PID_FMT"/%s) but inhibitor timeout is reached.",
-                         offending->uid, strna(u),
-                         offending->pid, strna(comm));
-        }
-
-        /* Actually do the operation */
-        r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error);
-        if (r < 0) {
-                log_warning("Failed to send delayed message: %s", bus_error_message(&error, r));
-
-                manager->action_unit = NULL;
-                manager->action_what = 0;
-                return r;
-        }
-
-        return 1;
-}
-
 int manager_start_scope(
                 Manager *manager,
                 const char *scope,
diff --git a/src/login/logind.c b/src/login/logind.c
index 707d528..ea7c6e7 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -141,6 +141,7 @@ void manager_free(Manager *m) {
         set_free_free(m->busnames);
 
         sd_event_source_unref(m->idle_action_event_source);
+        sd_event_source_unref(m->inhibit_timeout_source);
 
         sd_event_source_unref(m->console_active_event_source);
         sd_event_source_unref(m->udev_seat_event_source);
@@ -1093,8 +1094,6 @@ int manager_run(Manager *m) {
         assert(m);
 
         for (;;) {
-                usec_t us = (uint64_t) -1;
-
                 r = sd_event_get_state(m->event);
                 if (r < 0)
                         return r;
@@ -1103,19 +1102,7 @@ int manager_run(Manager *m) {
 
                 manager_gc(m, true);
 
-                if (manager_dispatch_delayed(m) > 0)
-                        continue;
-
-                if (m->action_what != 0 && !m->action_job) {
-                        usec_t x, y;
-
-                        x = now(CLOCK_MONOTONIC);
-                        y = m->action_timestamp + m->inhibit_delay_max;
-
-                        us = x >= y ? 0 : y - x;
-                }
-
-                r = sd_event_run(m->event, us);
+                r = sd_event_run(m->event, (uint64_t) -1);
                 if (r < 0)
                         return r;
         }
diff --git a/src/login/logind.h b/src/login/logind.h
index 4781688..caf78f7 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -95,7 +95,7 @@ struct Manager {
         /* If a shutdown/suspend is currently executed, then this is
          * the job of it */
         char *action_job;
-        usec_t action_timestamp;
+        sd_event_source *inhibit_timeout_source;
 
         sd_event_source *idle_action_event_source;
         usec_t idle_action_usec;
@@ -167,8 +167,6 @@ int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name
 
 int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
 
-int manager_dispatch_delayed(Manager *manager);
-
 int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_error *error, char **job);
 int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);

commit 3f61a7a6eb5ff7e5a14cd322897741969df6b723
Author: Daniel Mack <daniel at zonque.org>
Date:   Thu Apr 9 16:44:51 2015 +0200

    logind: drop unused argument from method_do_shutdown_or_sleep()

diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 76070a3..037459b 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1541,7 +1541,6 @@ static int method_do_shutdown_or_sleep(
                 const char *action_multiple_sessions,
                 const char *action_ignore_inhibit,
                 const char *sleep_verb,
-                sd_bus_message_handler_t method,
                 sd_bus_error *error) {
 
         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
@@ -1557,7 +1556,6 @@ static int method_do_shutdown_or_sleep(
         assert(action);
         assert(action_multiple_sessions);
         assert(action_ignore_inhibit);
-        assert(method);
 
         r = sd_bus_message_read(message, "b", &interactive);
         if (r < 0)
@@ -1633,7 +1631,6 @@ static int method_poweroff(sd_bus *bus, sd_bus_message *message, void *userdata,
                         "org.freedesktop.login1.power-off-multiple-sessions",
                         "org.freedesktop.login1.power-off-ignore-inhibit",
                         NULL,
-                        method_poweroff,
                         error);
 }
 
@@ -1648,7 +1645,6 @@ static int method_reboot(sd_bus *bus, sd_bus_message *message, void *userdata, s
                         "org.freedesktop.login1.reboot-multiple-sessions",
                         "org.freedesktop.login1.reboot-ignore-inhibit",
                         NULL,
-                        method_reboot,
                         error);
 }
 
@@ -1663,7 +1659,6 @@ static int method_suspend(sd_bus *bus, sd_bus_message *message, void *userdata,
                         "org.freedesktop.login1.suspend-multiple-sessions",
                         "org.freedesktop.login1.suspend-ignore-inhibit",
                         "suspend",
-                        method_suspend,
                         error);
 }
 
@@ -1678,7 +1673,6 @@ static int method_hibernate(sd_bus *bus, sd_bus_message *message, void *userdata
                         "org.freedesktop.login1.hibernate-multiple-sessions",
                         "org.freedesktop.login1.hibernate-ignore-inhibit",
                         "hibernate",
-                        method_hibernate,
                         error);
 }
 
@@ -1693,7 +1687,6 @@ static int method_hybrid_sleep(sd_bus *bus, sd_bus_message *message, void *userd
                         "org.freedesktop.login1.hibernate-multiple-sessions",
                         "org.freedesktop.login1.hibernate-ignore-inhibit",
                         "hybrid-sleep",
-                        method_hybrid_sleep,
                         error);
 }
 



More information about the systemd-commits mailing list