[systemd-commits] 9 commits - .gitignore Makefile.am TODO man/logind.conf.xml man/systemctl.xml man/systemd-inhibit.xml man/systemd.special.xml src/core src/login src/shared src/sleep src/systemctl units/.gitignore units/hibernate.service.in units/hibernate.target units/sleep.target units/suspend.service.in units/suspend.target

Lennart Poettering lennart at kemper.freedesktop.org
Tue May 8 16:36:16 PDT 2012


 .gitignore                                 |    1 
 Makefile.am                                |   18 -
 TODO                                       |   14 
 man/logind.conf.xml                        |   11 
 man/systemctl.xml                          |   10 
 man/systemd-inhibit.xml                    |    8 
 man/systemd.special.xml                    |   34 +
 src/core/dbus-manager.c                    |   52 ++
 src/core/main.c                            |  194 +++++++----
 src/core/manager.c                         |    3 
 src/core/manager.h                         |    4 
 src/core/shutdown.c                        |    6 
 src/core/special.h                         |    2 
 src/login/inhibit.c                        |    6 
 src/login/logind-dbus.c                    |  505 +++++++++++++++++++----------
 src/login/logind-inhibit.c                 |   16 
 src/login/logind-inhibit.h                 |    8 
 src/login/logind.c                         |    6 
 src/login/logind.h                         |    7 
 src/login/org.freedesktop.login1.conf      |   16 
 src/login/org.freedesktop.login1.policy.in |   84 ++++
 src/login/test-inhibit.c                   |   14 
 src/shared/path-util.h                     |    1 
 src/shared/util.c                          |   51 ++
 src/shared/util.h                          |    2 
 src/sleep/Makefile                         |    1 
 src/sleep/sleep.c                          |   83 ++++
 src/systemctl/systemctl.c                  |   28 +
 units/.gitignore                           |    2 
 units/hibernate.service.in                 |   16 
 units/hibernate.target                     |   14 
 units/sleep.target                         |   13 
 units/suspend.service.in                   |   16 
 units/suspend.target                       |   14 
 34 files changed, 951 insertions(+), 309 deletions(-)

New commits:
commit 07f74a7ebde5acf098ab72dc49a3371731ffb476
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed May 9 01:26:30 2012 +0200

    update TODO

diff --git a/TODO b/TODO
index 47a890d..d2bb326 100644
--- a/TODO
+++ b/TODO
@@ -22,6 +22,11 @@ Bugfixes:
 * properly handle .mount unit state tracking when two mount points are stacked one on top of another on the exact same mount point.
 
 Features:
+
+* expose switch-root in systemctl
+
+* remove old root in switch-root logic
+
 * improve !/proc/*/loginuid situation: make /proc/*/loginuid less dependent on CONFIG_AUDIT,
   or use the users cgroup information when /proc/*/loginuid is not available.
 

commit d4d046e3fdf198be0d329ea79b30f56ae6bbb4d7
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed May 9 01:25:52 2012 +0200

    util: a few updates for rm_rf()

diff --git a/src/shared/util.c b/src/shared/util.c
index 5f06c4b..c9899fb 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -3139,7 +3139,8 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
         /* This returns the first error we run into, but nevertheless
          * tries to go on */
 
-        if (!(d = fdopendir(fd))) {
+        d = fdopendir(fd);
+        if (!d) {
                 close_nointr_nofail(fd);
 
                 return errno == ENOENT ? 0 : -errno;
@@ -3150,9 +3151,9 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
                 bool is_dir, keep_around = false;
                 int r;
 
-                if ((r = readdir_r(d, &buf, &de)) != 0) {
-                        if (ret == 0)
-                                ret = -r;
+                r = readdir_r(d, &buf, &de);
+                if (r != 0 && ret == 0) {
+                        ret = -r;
                         break;
                 }
 
@@ -3199,17 +3200,16 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
                 if (is_dir) {
                         int subdir_fd;
 
-                        subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+                        subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
                         if (subdir_fd < 0) {
                                 if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                                 continue;
                         }
 
-                        if ((r = rm_rf_children(subdir_fd, only_dirs, honour_sticky)) < 0) {
-                                if (ret == 0)
-                                        ret = r;
-                        }
+                        r = rm_rf_children(subdir_fd, only_dirs, honour_sticky);
+                        if (r < 0 && ret == 0)
+                                ret = r;
 
                         if (!keep_around)
                                 if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
@@ -3237,13 +3237,14 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
 
         assert(path);
 
-        if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) {
+        fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+        if (fd < 0) {
 
                 if (errno != ENOTDIR)
                         return -errno;
 
                 if (delete_root && !only_dirs)
-                        if (unlink(path) < 0)
+                        if (unlink(path) < 0 && errno != ENOENT)
                                 return -errno;
 
                 return 0;

commit 664f88a7e653918942b858e3f387be2ebc9ebf03
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed May 9 01:24:50 2012 +0200

    manager: introduce SwitchRoot bus call for initrd/main transition

diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index bdc9192..2e6bc3d 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -32,6 +32,7 @@
 #include "install.h"
 #include "watchdog.h"
 #include "hwclock.h"
+#include "path-util.h"
 
 #define BUS_MANAGER_INTERFACE_BEGIN                                     \
         " <interface name=\"org.freedesktop.systemd1.Manager\">\n"
@@ -126,6 +127,10 @@
         "  <method name=\"PowerOff\"/>\n"                               \
         "  <method name=\"Halt\"/>\n"                                   \
         "  <method name=\"KExec\"/>\n"                                  \
+        "  <method name=\"SwitchRoot\">\n"                              \
+        "   <arg name=\"new_root\" type=\"s\" direction=\"in\"/>\n"     \
+        "   <arg name=\"init\" type=\"s\" direction=\"in\"/>\n"         \
+        "  </method>\n"                                                 \
         "  <method name=\"SetEnvironment\">\n"                          \
         "   <arg name=\"names\" type=\"as\" direction=\"in\"/>\n"       \
         "  </method>\n"                                                 \
@@ -1177,6 +1182,53 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
 
                 m->exit_code = MANAGER_KEXEC;
 
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SwitchRoot")) {
+                const char *switch_root, *switch_root_init;
+                char *u, *v;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_STRING, &switch_root,
+                                    DBUS_TYPE_STRING, &switch_root_init,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+                if (path_equal(switch_root, "/") || !is_path(switch_root))
+                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
+
+                if (!isempty(switch_root_init) && !is_path(switch_root_init))
+                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
+
+                if (m->running_as != MANAGER_SYSTEM) {
+                        dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Switching root is only supported for system managers.");
+                        return bus_send_error_reply(connection, message, &error, -ENOTSUP);
+                }
+
+                u = strdup(switch_root);
+                if (!u)
+                        goto oom;
+
+                if (!isempty(switch_root_init)) {
+                        v = strdup(switch_root_init);
+                        if (!v) {
+                                free(u);
+                                goto oom;
+                        }
+                } else
+                        v = NULL;
+
+                free(m->switch_root);
+                free(m->switch_root_init);
+                m->switch_root = u;
+                m->switch_root_init = v;
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+
+                m->exit_code = MANAGER_SWITCH_ROOT;
+
         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
                 char **l = NULL, **e = NULL;
 
diff --git a/src/core/main.c b/src/core/main.c
index 5878099..d7ce8ab 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -32,6 +32,7 @@
 #include <sys/wait.h>
 #include <fcntl.h>
 #include <sys/prctl.h>
+#include <sys/mount.h>
 
 #include "manager.h"
 #include "log.h"
@@ -47,6 +48,7 @@
 #include "def.h"
 #include "virt.h"
 #include "watchdog.h"
+#include "path-util.h"
 
 #include "mount-setup.h"
 #include "loopback-setup.h"
@@ -1165,6 +1167,36 @@ static void test_cgroups(void) {
         sleep(10);
 }
 
+static int do_switch_root(const char *switch_root) {
+        int r;
+
+        if (path_equal(switch_root, "/"))
+                return 0;
+
+        if (chdir(switch_root) < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        if (mount(switch_root, "/", NULL, MS_MOVE, NULL) < 0) {
+                r = -errno;
+                chdir("/");
+                goto fail;
+        }
+
+        if (chroot(".") < 0)
+                log_warning("Failed to change root, ignoring: %s", strerror(-r));
+
+        /* FIXME: remove old root */
+
+        return 0;
+
+fail:
+        log_error("Failed to switch root, ignoring: %s", strerror(-r));
+
+        return r;
+}
+
 int main(int argc, char *argv[]) {
         Manager *m = NULL;
         int r, retval = EXIT_FAILURE;
@@ -1179,6 +1211,7 @@ int main(int argc, char *argv[]) {
         int j;
         bool loaded_policy = false;
         bool arm_reboot_watchdog = false;
+        char *switch_root = NULL, *switch_root_init = NULL;
 
 #ifdef HAVE_SYSV_COMPAT
         if (getpid() != 1 && strstr(program_invocation_short_name, "init")) {
@@ -1549,6 +1582,7 @@ int main(int argc, char *argv[]) {
                         break;
 
                 case MANAGER_REEXECUTE:
+
                         if (prepare_reexecute(m, &serialization, &fds) < 0)
                                 goto finish;
 
@@ -1556,6 +1590,20 @@ int main(int argc, char *argv[]) {
                         log_notice("Reexecuting.");
                         goto finish;
 
+                case MANAGER_SWITCH_ROOT:
+                        /* Steal the switch root parameters */
+                        switch_root = m->switch_root;
+                        switch_root_init = m->switch_root_init;
+                        m->switch_root = m->switch_root_init = NULL;
+
+                        if (!switch_root_init)
+                                if (prepare_reexecute(m, &serialization, &fds) < 0)
+                                        goto finish;
+
+                        reexecute = true;
+                        log_notice("Switching root.");
+                        goto finish;
+
                 case MANAGER_REBOOT:
                 case MANAGER_POWEROFF:
                 case MANAGER_HALT:
@@ -1588,66 +1636,66 @@ finish:
         free_join_controllers();
 
         dbus_shutdown();
-
         label_finish();
 
         if (reexecute) {
-                const char *args[15];
-                unsigned i = 0;
-                char sfd[16];
+                const char **args;
+                unsigned i;
 
-                assert(serialization);
-                assert(fds);
+                /* Close and disarm the watchdog, so that the new
+                 * instance can reinitialize it, but doesn't get
+                 * rebooted while we do that */
+                watchdog_close(true);
 
-                args[i++] = SYSTEMD_BINARY_PATH;
+                if (switch_root)
+                        do_switch_root(switch_root);
 
-                args[i++] = "--log-level";
-                args[i++] = log_level_to_string(log_get_max_level());
+                args = newa(const char*, MAX(5, argc+1));
 
-                args[i++] = "--log-target";
-                args[i++] = log_target_to_string(log_get_target());
+                if (!switch_root_init) {
+                        char sfd[16];
 
-                if (arg_running_as == MANAGER_SYSTEM)
-                        args[i++] = "--system";
-                else
-                        args[i++] = "--user";
+                        /* First try to spawn ourselves with the right
+                         * path, and with full serialization. We do
+                         * this only if the user didn't specify an
+                         * explicit init to spawn. */
 
-                if (arg_dump_core)
-                        args[i++] = "--dump-core";
+                        assert(serialization);
+                        assert(fds);
 
-                if (arg_crash_shell)
-                        args[i++] = "--crash-shell";
+                        snprintf(sfd, sizeof(sfd), "%i", fileno(serialization));
+                        char_array_0(sfd);
 
-                if (arg_confirm_spawn)
-                        args[i++] = "--confirm-spawn";
+                        i = 0;
+                        args[i++] = SYSTEMD_BINARY_PATH;
+                        args[i++] = arg_running_as == MANAGER_SYSTEM ? "--system" : "--user";
+                        args[i++] = "--deserialize";
+                        args[i++] = sfd;
+                        args[i++] = NULL;
 
-                if (arg_show_status)
-                        args[i++] = "--show-status=1";
-                else
-                        args[i++] = "--show-status=0";
+                        assert(i <= ELEMENTSOF(args));
+                        execv(args[0], (char* const*) args);
+                }
 
-#ifdef HAVE_SYSV_COMPAT
-                if (arg_sysv_console)
-                        args[i++] = "--sysv-console=1";
-                else
-                        args[i++] = "--sysv-console=0";
-#endif
+                /* Try the fallback, if there is any, without any
+                 * serialization. We pass the original argv[] and
+                 * envp[]. (Well, modulo the ordering changes due to
+                 * getopt() in argv[], and some cleanups in envp[],
+                 * but let's hope that doesn't matter.) */
 
-                snprintf(sfd, sizeof(sfd), "%i", fileno(serialization));
-                char_array_0(sfd);
+                if (serialization)
+                        fclose(serialization);
 
-                args[i++] = "--deserialize";
-                args[i++] = sfd;
+                if (fds)
+                        fdset_free(fds);
 
+                i = 0;
+                args[i++] = switch_root_init ? switch_root_init : "/sbin/init";
+                for (j = 1; j < argc; j++)
+                        args[i++] = argv[j];
                 args[i++] = NULL;
 
                 assert(i <= ELEMENTSOF(args));
-
-                /* Close and disarm the watchdog, so that the new
-                 * instance can reinitialize it, but doesn't get
-                 * rebooted while we do that */
-                watchdog_close(true);
-
                 execv(args[0], (char* const*) args);
 
                 log_error("Failed to reexecute: %m");
diff --git a/src/core/manager.c b/src/core/manager.c
index c6baf22..bd86f89 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -522,6 +522,9 @@ void manager_free(Manager *m) {
 
         close_pipe(m->idle_pipe);
 
+        free(m->switch_root);
+        free(m->switch_root_init);
+
         free(m);
 }
 
diff --git a/src/core/manager.h b/src/core/manager.h
index 6d1f5d8..92dc75d 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -45,6 +45,7 @@ typedef enum ManagerExitCode {
         MANAGER_POWEROFF,
         MANAGER_HALT,
         MANAGER_KEXEC,
+        MANAGER_SWITCH_ROOT,
         _MANAGER_EXIT_CODE_MAX,
         _MANAGER_EXIT_CODE_INVALID = -1
 } ManagerExitCode;
@@ -233,6 +234,9 @@ struct Manager {
 
         /* Type=idle pipes */
         int idle_pipe[2];
+
+        char *switch_root;
+        char *switch_root_init;
 };
 
 int manager_new(ManagerRunningAs running_as, Manager **m);

commit 14753f341937b75b244261f36807330456fa33a8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue May 8 23:37:25 2012 +0200

    path-util: there is no function path_parent()

diff --git a/src/shared/path-util.h b/src/shared/path-util.h
index 181b981..a441783 100644
--- a/src/shared/path-util.h
+++ b/src/shared/path-util.h
@@ -31,7 +31,6 @@ int path_get_parent(const char *path, char **parent);
 bool path_is_absolute(const char *p);
 char *path_make_absolute(const char *p, const char *prefix);
 char *path_make_absolute_cwd(const char *p);
-int path_parent(const char *path, char **parent);
 char *path_kill_slashes(char *path);
 bool path_startswith(const char *path, const char *prefix);
 bool path_equal(const char *a, const char *b);

commit a5d87bf0eaf718afe62fb0b18c2f526c6e4375fc
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue May 8 23:36:55 2012 +0200

    main: simplify+unify logic for parsing runtime booleans

diff --git a/src/core/main.c b/src/core/main.c
index fe4522e..5878099 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -760,9 +760,9 @@ static int parse_argv(int argc, char *argv[]) {
                 { "test",                     no_argument,       NULL, ARG_TEST                     },
                 { "help",                     no_argument,       NULL, 'h'                          },
                 { "dump-configuration-items", no_argument,       NULL, ARG_DUMP_CONFIGURATION_ITEMS },
-                { "dump-core",                no_argument,       NULL, ARG_DUMP_CORE                },
-                { "crash-shell",              no_argument,       NULL, ARG_CRASH_SHELL              },
-                { "confirm-spawn",            no_argument,       NULL, ARG_CONFIRM_SPAWN            },
+                { "dump-core",                optional_argument, NULL, ARG_DUMP_CORE                },
+                { "crash-shell",              optional_argument, NULL, ARG_CRASH_SHELL              },
+                { "confirm-spawn",            optional_argument, NULL, ARG_CONFIRM_SPAWN            },
                 { "show-status",              optional_argument, NULL, ARG_SHOW_STATUS              },
 #ifdef HAVE_SYSV_COMPAT
                 { "sysv-console",             optional_argument, NULL, ARG_SYSV_CONSOLE             },
@@ -871,39 +871,49 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_DUMP_CORE:
-                        arg_dump_core = true;
+                        r = optarg ? parse_boolean(optarg) : 1;
+                        if (r < 0) {
+                                log_error("Failed to parse dump core boolean %s.", optarg);
+                                return r;
+                        }
+                        arg_dump_core = r;
                         break;
 
                 case ARG_CRASH_SHELL:
-                        arg_crash_shell = true;
+                        r = optarg ? parse_boolean(optarg) : 1;
+                        if (r < 0) {
+                                log_error("Failed to parse crash shell boolean %s.", optarg);
+                                return r;
+                        }
+                        arg_crash_shell = r;
                         break;
 
                 case ARG_CONFIRM_SPAWN:
-                        arg_confirm_spawn = true;
+                        r = optarg ? parse_boolean(optarg) : 1;
+                        if (r < 0) {
+                                log_error("Failed to parse confirm spawn boolean %s.", optarg);
+                                return r;
+                        }
+                        arg_confirm_spawn = r;
                         break;
 
                 case ARG_SHOW_STATUS:
-
-                        if (optarg) {
-                                if ((r = parse_boolean(optarg)) < 0) {
-                                        log_error("Failed to show status boolean %s.", optarg);
-                                        return r;
-                                }
-                                arg_show_status = r;
-                        } else
-                                arg_show_status = true;
+                        r = optarg ? parse_boolean(optarg) : 1;
+                        if (r < 0) {
+                                log_error("Failed to parse show status boolean %s.", optarg);
+                                return r;
+                        }
+                        arg_show_status = r;
                         break;
+
 #ifdef HAVE_SYSV_COMPAT
                 case ARG_SYSV_CONSOLE:
-
-                        if (optarg) {
-                                if ((r = parse_boolean(optarg)) < 0) {
-                                        log_error("Failed to SysV console boolean %s.", optarg);
-                                        return r;
-                                }
-                                arg_sysv_console = r;
-                        } else
-                                arg_sysv_console = true;
+                        r = optarg ? parse_boolean(optarg) : 1;
+                        if (r < 0) {
+                                log_error("Failed to parse SysV console boolean %s.", optarg);
+                                return r;
+                        }
+                        arg_sysv_console = r;
                         break;
 #endif
 
@@ -1014,9 +1024,9 @@ static int help(void) {
                "     --unit=UNIT                 Set default unit\n"
                "     --system                    Run a system instance, even if PID != 1\n"
                "     --user                      Run a user instance\n"
-               "     --dump-core                 Dump core on crash\n"
-               "     --crash-shell               Run shell on crash\n"
-               "     --confirm-spawn             Ask for confirmation when spawning processes\n"
+               "     --dump-core[=0|1]           Dump core on crash\n"
+               "     --crash-shell[=0|1]         Run shell on crash\n"
+               "     --confirm-spawn[=0|1]       Ask for confirmation when spawning processes\n"
                "     --show-status[=0|1]         Show status updates on the console during bootup\n"
 #ifdef HAVE_SYSV_COMPAT
                "     --sysv-console[=0|1]        Connect output of SysV scripts to console\n"

commit d889a2069a87e4617b32ddbdeace5a53a12c699d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue May 8 19:02:25 2012 +0200

    logind: implement suspend/hibernate calls with inhibition logic

diff --git a/TODO b/TODO
index aeba2b5..47a890d 100644
--- a/TODO
+++ b/TODO
@@ -36,7 +36,7 @@ Features:
 * automount: implement expire
 
 * logind: auto-suspend, auto-shutdown:
-        IdleAction=(none|suspend|hibernate|poweroff)
+        IdleAction=(none|suspend|opportunistic|hibernate|poweroff)
         IdleActionDelay=...
         SessionIdleMode=(explicit|ignore|login)
         ForceShutdown=(yes|no)
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 9fc3a14..e089eeb 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -135,12 +135,24 @@
         "  <method name=\"Reboot\">\n"                                  \
         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
         "  </method>\n"                                                 \
+        "  <method name=\"Suspend\">\n"                                 \
+        "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
+        "  </method>\n"                                                 \
+        "  <method name=\"Hibernate\">\n"                               \
+        "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
+        "  </method>\n"                                                 \
         "  <method name=\"CanPowerOff\">\n"                             \
         "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
         "  </method>\n"                                                 \
         "  <method name=\"CanReboot\">\n"                               \
         "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
         "  </method>\n"                                                 \
+        "  <method name=\"CanSuspend\">\n"                              \
+        "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
+        "  </method>\n"                                                 \
+        "  <method name=\"CanHibernate\">\n"                            \
+        "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
+        "  </method>\n"                                                 \
         "  <method name=\"Inhibit\">\n"                                 \
         "   <arg name=\"what\" type=\"s\" direction=\"in\"/>\n"         \
         "   <arg name=\"who\" type=\"s\" direction=\"in\"/>\n"          \
@@ -178,6 +190,9 @@
         "  <signal name=\"PrepareForShutdown\">\n"                      \
         "   <arg name=\"active\" type=\"b\"/>\n"                        \
         "  </signal>\n"                                                 \
+        "  <signal name=\"PrepareForSleep\">\n"                         \
+        "   <arg name=\"active\" type=\"b\"/>\n"                        \
+        "  </signal>\n"                                                 \
         "  <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
         "  <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
         "  <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
@@ -937,12 +952,12 @@ static int have_multiple_sessions(
         return false;
 }
 
-static int send_start_unit(DBusConnection *connection, const char *name, DBusError *error) {
+static int send_start_unit(DBusConnection *connection, const char *unit_name, DBusError *error) {
         DBusMessage *message, *reply;
         const char *mode = "replace";
 
         assert(connection);
-        assert(name);
+        assert(unit_name);
 
         message = dbus_message_new_method_call(
                         "org.freedesktop.systemd1",
@@ -953,7 +968,7 @@ static int send_start_unit(DBusConnection *connection, const char *name, DBusErr
                 return -ENOMEM;
 
         if (!dbus_message_append_args(message,
-                                      DBUS_TYPE_STRING, &name,
+                                      DBUS_TYPE_STRING, &unit_name,
                                       DBUS_TYPE_STRING, &mode,
                                       DBUS_TYPE_INVALID)) {
                 dbus_message_unref(message);
@@ -970,14 +985,22 @@ static int send_start_unit(DBusConnection *connection, const char *name, DBusErr
         return 0;
 }
 
-static int send_prepare_for_shutdown(Manager *m, bool _active) {
+static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) {
+        static const char * const signal_name[_INHIBIT_WHAT_MAX] = {
+                [INHIBIT_SHUTDOWN] = "PrepareForShutdown",
+                [INHIBIT_SLEEP] = "PrepareForSleep"
+        };
+
         dbus_bool_t active = _active;
         DBusMessage *message;
         int r = 0;
 
         assert(m);
+        assert(w >= 0);
+        assert(w < _INHIBIT_WHAT_MAX);
+        assert(signal_name[w]);
 
-        message = dbus_message_new_signal("/org/freedesktop/login1", "org.freedesktop.login1.Manager", "PrepareForShutdown");
+        message = dbus_message_new_signal("/org/freedesktop/login1", "org.freedesktop.login1.Manager", signal_name[w]);
         if (!message)
                 return -ENOMEM;
 
@@ -989,21 +1012,222 @@ static int send_prepare_for_shutdown(Manager *m, bool _active) {
         return r;
 }
 
-static int delay_shutdown(Manager *m, const char *name) {
+static int delay_shutdown_or_sleep(Manager *m, InhibitWhat w, const char *unit_name) {
         assert(m);
+        assert(w >= 0);
+        assert(w < _INHIBIT_WHAT_MAX);
 
-        if (!m->delayed_shutdown) {
-                /* Tell everybody to prepare for shutdown */
-                send_prepare_for_shutdown(m, true);
+        /* Tell everybody to prepare for shutdown/sleep */
+        send_prepare_for(m, w, true);
 
-                /* Update timestamp for timeout */
-                m->delayed_shutdown_timestamp = now(CLOCK_MONOTONIC);
-        }
+        /* Update timestamp for timeout */
+        if (!m->delayed_unit)
+                m->delayed_timestamp = now(CLOCK_MONOTONIC);
 
         /* Remember what we want to do, possibly overriding what kind
-         * of shutdown we previously queued. */
-        m->delayed_shutdown = name;
+         * of unit we previously queued. */
+        m->delayed_unit = unit_name;
+        m->delayed_what = w;
+
+        return 0;
+}
+
+static int bus_manager_can_shutdown_or_sleep(
+                Manager *m,
+                DBusConnection *connection,
+                DBusMessage *message,
+                InhibitWhat w,
+                const char *action,
+                const char *action_multiple_sessions,
+                const char *action_ignore_inhibit,
+                const char *sleep_type,
+                DBusError *error,
+                DBusMessage **_reply) {
+
+        bool multiple_sessions, challenge, blocked, b;
+        const char *result;
+        DBusMessage *reply = NULL;
+        int r;
+
+        assert(m);
+        assert(connection);
+        assert(message);
+        assert(w >= 0);
+        assert(w <= _INHIBIT_WHAT_MAX);
+        assert(action);
+        assert(action_multiple_sessions);
+        assert(action_ignore_inhibit);
+        assert(error);
+        assert(_reply);
+
+        if (sleep_type) {
+                r = can_sleep(sleep_type);
+                if (r < 0)
+                        return r;
+
+                result = "na";
+                goto finish;
+        }
+
+        r = have_multiple_sessions(connection, m, message, error);
+        if (r < 0)
+                return r;
+
+        multiple_sessions = r > 0;
+        blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL);
+
+        if (multiple_sessions) {
+                r = verify_polkit(connection, message, action_multiple_sessions, false, &challenge, error);
+                if (r < 0)
+                        return r;
+
+                if (r > 0)
+                        result = "yes";
+                        else if (challenge)
+                                result = "challenge";
+                        else
+                                result = "no";
+                }
+
+        if (blocked) {
+                r = verify_polkit(connection, message, action_ignore_inhibit, false, &challenge, error);
+                if (r < 0)
+                        return r;
+
+                if (r > 0 && !result)
+                        result = "yes";
+                else if (challenge && (!result || streq(result, "yes")))
+                        result = "challenge";
+                else
+                        result = "no";
+        }
+
+        if (!multiple_sessions && !blocked) {
+                /* If neither inhibit nor multiple sessions
+                 * apply then just check the normal policy */
+
+                r = verify_polkit(connection, message, action, false, &challenge, error);
+                if (r < 0)
+                        return r;
+
+                if (r > 0)
+                        result = "yes";
+                else if (challenge)
+                        result = "challenge";
+                else
+                        result = "no";
+        }
+
+finish:
+        reply = dbus_message_new_method_return(message);
+        if (!reply)
+                return -ENOMEM;
+
+        b = dbus_message_append_args(
+                        reply,
+                        DBUS_TYPE_STRING, &result,
+                        DBUS_TYPE_INVALID);
+        if (!b) {
+                dbus_message_unref(reply);
+                return -ENOMEM;
+        }
+
+        *_reply = reply;
+        return 0;
+}
+
+static int bus_manager_do_shutdown_or_sleep(
+                Manager *m,
+                DBusConnection *connection,
+                DBusMessage *message,
+                const char *unit_name,
+                InhibitWhat w,
+                const char *action,
+                const char *action_multiple_sessions,
+                const char *action_ignore_inhibit,
+                const char *sleep_type,
+                DBusError *error,
+                DBusMessage **_reply) {
+
+        dbus_bool_t interactive;
+        bool multiple_sessions, blocked, delayed;
+        DBusMessage *reply = NULL;
+        int r;
+
+        assert(m);
+        assert(connection);
+        assert(message);
+        assert(unit_name);
+        assert(w >= 0);
+        assert(w <= _INHIBIT_WHAT_MAX);
+        assert(action);
+        assert(action_multiple_sessions);
+        assert(action_ignore_inhibit);
+        assert(error);
+        assert(_reply);
+
+        if (!dbus_message_get_args(
+                            message,
+                            error,
+                            DBUS_TYPE_BOOLEAN, &interactive,
+                            DBUS_TYPE_INVALID))
+                return -EINVAL;
+
+        if (sleep_type) {
+                r = can_sleep(sleep_type);
+                if (r < 0)
+                        return r;
+
+                if (r == 0)
+                        return -ENOTSUP;
+        }
+
+        r = have_multiple_sessions(connection, m, message, error);
+        if (r < 0)
+                return r;
+
+        multiple_sessions = r > 0;
+        blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL);
+
+        if (multiple_sessions) {
+                r = verify_polkit(connection, message, action_multiple_sessions, interactive, NULL, error);
+                if (r < 0)
+                        return r;
+        }
+
+        if (blocked) {
+                r = verify_polkit(connection, message, action_ignore_inhibit, interactive, NULL, error);
+                if (r < 0)
+                        return r;
+        }
+
+        if (!multiple_sessions && !blocked) {
+                r = verify_polkit(connection, message, action, interactive, NULL, error);
+                if (r < 0)
+                        return r;
+        }
+
+        delayed =
+                m->inhibit_delay_max > 0 &&
+                manager_is_inhibited(m, w, INHIBIT_DELAY, NULL);
+
+        if (delayed) {
+                /* Shutdown is delayed, keep in mind what we
+                 * want to do, and start a timeout */
+                r = delay_shutdown_or_sleep(m, w, unit_name);
+        } else
+                /* Shutdown is not delayed, execute it
+                 * immediately */
+                r = send_start_unit(connection, unit_name, error);
+
+        if (r < 0)
+                return r;
+
+        reply = dbus_message_new_method_return(message);
+        if (!reply)
+                return -ENOMEM;
 
+        *_reply = reply;
         return 0;
 }
 
@@ -1731,158 +1955,104 @@ static DBusHandlerResult manager_message_handler(
                 if (!reply)
                         goto oom;
 
-        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff") ||
-                   dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) {
-                dbus_bool_t interactive;
-                bool multiple_sessions, blocked, delayed;
-                const char *name, *action;
-
-                if (!dbus_message_get_args(
-                                    message,
-                                    &error,
-                                    DBUS_TYPE_BOOLEAN, &interactive,
-                                    DBUS_TYPE_INVALID))
-                        return bus_send_error_reply(connection, message, &error, -EINVAL);
-
-                r = have_multiple_sessions(connection, m, message, &error);
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff")) {
+
+                r = bus_manager_do_shutdown_or_sleep(
+                                m, connection, message,
+                                SPECIAL_POWEROFF_TARGET,
+                                INHIBIT_SHUTDOWN,
+                                "org.freedesktop.login1.power-off",
+                                "org.freedesktop.login1.power-off-multiple-sessions",
+                                "org.freedesktop.login1.power-off-ignore-inhibit",
+                                NULL,
+                                &error, &reply);
                 if (r < 0)
                         return bus_send_error_reply(connection, message, &error, r);
-
-                multiple_sessions = r > 0;
-                blocked = manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL);
-
-                if (multiple_sessions) {
-                        action = streq(dbus_message_get_member(message), "PowerOff") ?
-                                "org.freedesktop.login1.power-off-multiple-sessions" :
-                                "org.freedesktop.login1.reboot-multiple-sessions";
-
-                        r = verify_polkit(connection, message, action, interactive, NULL, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
-                }
-
-                if (blocked) {
-                        action = streq(dbus_message_get_member(message), "PowerOff") ?
-                                "org.freedesktop.login1.power-off-ignore-inhibit" :
-                                "org.freedesktop.login1.reboot-ignore-inhibit";
-
-                        r = verify_polkit(connection, message, action, interactive, NULL, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
-                }
-
-                if (!multiple_sessions && !blocked) {
-                        action = streq(dbus_message_get_member(message), "PowerOff") ?
-                                "org.freedesktop.login1.power-off" :
-                                "org.freedesktop.login1.reboot";
-
-                        r = verify_polkit(connection, message, action, interactive, NULL, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
-                }
-
-                name = streq(dbus_message_get_member(message), "PowerOff") ?
-                        SPECIAL_POWEROFF_TARGET : SPECIAL_REBOOT_TARGET;
-
-                delayed =
-                        m->inhibit_delay_max > 0 &&
-                        manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_DELAY, NULL);
-
-                if (delayed) {
-                        /* Shutdown is delayed, keep in mind what we
-                         * want to do, and start a timeout */
-                        r = delay_shutdown(m, name);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, NULL, r);
-                } else {
-                        /* Shutdown is not delayed, execute it
-                         * immediately */
-                        r = send_start_unit(connection, name, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
-                }
-
-                reply = dbus_message_new_method_return(message);
-                if (!reply)
-                        goto oom;
-
-        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanPowerOff") ||
-                   dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanReboot")) {
-
-                bool multiple_sessions, challenge, inhibit, b;
-                const char *action, *result;
-
-                r = have_multiple_sessions(connection, m, message, &error);
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) {
+                r = bus_manager_do_shutdown_or_sleep(
+                                m, connection, message,
+                                SPECIAL_REBOOT_TARGET,
+                                INHIBIT_SHUTDOWN,
+                                "org.freedesktop.login1.reboot",
+                                "org.freedesktop.login1.reboot-multiple-sessions",
+                                "org.freedesktop.login1.reboot-ignore-inhibit",
+                                NULL,
+                                &error, &reply);
                 if (r < 0)
                         return bus_send_error_reply(connection, message, &error, r);
 
-                multiple_sessions = r > 0;
-                inhibit = manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL);
-
-                if (multiple_sessions) {
-                        action = streq(dbus_message_get_member(message), "CanPowerOff") ?
-                                "org.freedesktop.login1.power-off-multiple-sessions" :
-                                "org.freedesktop.login1.reboot-multiple-sessions";
-
-                        r = verify_polkit(connection, message, action, false, &challenge, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
-
-                        if (r > 0)
-                                result = "yes";
-                        else if (challenge)
-                                result = "challenge";
-                        else
-                                result = "no";
-                }
-
-                if (inhibit) {
-                        action = streq(dbus_message_get_member(message), "CanPowerOff") ?
-                                "org.freedesktop.login1.power-off-ignore-inhibit" :
-                                "org.freedesktop.login1.reboot-ignore-inhibit";
-
-                        r = verify_polkit(connection, message, action, false, &challenge, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
-
-                        if (r > 0 && !result)
-                                result = "yes";
-                        else if (challenge && (!result || streq(result, "yes")))
-                                result = "challenge";
-                        else
-                                result = "no";
-                }
-
-                if (!multiple_sessions && !inhibit) {
-                        /* If neither inhibit nor multiple sessions
-                         * apply then just check the normal policy */
-
-                        action = streq(dbus_message_get_member(message), "CanPowerOff") ?
-                                "org.freedesktop.login1.power-off" :
-                                "org.freedesktop.login1.reboot";
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Suspend")) {
+                r = bus_manager_do_shutdown_or_sleep(
+                                m, connection, message,
+                                SPECIAL_SUSPEND_TARGET,
+                                INHIBIT_SLEEP,
+                                "org.freedesktop.login1.suspend",
+                                "org.freedesktop.login1.suspend-multiple-sessions",
+                                "org.freedesktop.login1.suspend-ignore-inhibit",
+                                "mem",
+                                &error, &reply);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Hibernate")) {
+                r = bus_manager_do_shutdown_or_sleep(
+                                m, connection, message,
+                                SPECIAL_HIBERNATE_TARGET,
+                                INHIBIT_SLEEP,
+                                "org.freedesktop.login1.hibernate",
+                                "org.freedesktop.login1.hibernate-multiple-sessions",
+                                "org.freedesktop.login1.hibernate-ignore-inhibit",
+                                "disk",
+                                &error, &reply);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
 
-                        r = verify_polkit(connection, message, action, false, &challenge, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanPowerOff")) {
 
-                        if (r > 0)
-                                result = "yes";
-                        else if (challenge)
-                                result = "challenge";
-                        else
-                                result = "no";
-                }
+                r = bus_manager_can_shutdown_or_sleep(
+                                m, connection, message,
+                                INHIBIT_SHUTDOWN,
+                                "org.freedesktop.login1.power-off",
+                                "org.freedesktop.login1.power-off-multiple-sessions",
+                                "org.freedesktop.login1.power-off-ignore-inhibit",
+                                NULL,
+                                &error, &reply);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanReboot")) {
+                r = bus_manager_can_shutdown_or_sleep(
+                                m, connection, message,
+                                INHIBIT_SHUTDOWN,
+                                "org.freedesktop.login1.reboot",
+                                "org.freedesktop.login1.reboot-multiple-sessions",
+                                "org.freedesktop.login1.reboot-ignore-inhibit",
+                                NULL,
+                                &error, &reply);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
 
-                reply = dbus_message_new_method_return(message);
-                if (!reply)
-                        goto oom;
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanSuspend")) {
+                r = bus_manager_can_shutdown_or_sleep(
+                                m, connection, message,
+                                INHIBIT_SLEEP,
+                                "org.freedesktop.login1.suspend",
+                                "org.freedesktop.login1.suspend-multiple-sessions",
+                                "org.freedesktop.login1.suspend-ignore-inhibit",
+                                "mem",
+                                &error, &reply);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
 
-                b = dbus_message_append_args(
-                                reply,
-                                DBUS_TYPE_STRING, &result,
-                                DBUS_TYPE_INVALID);
-                if (!b)
-                        goto oom;
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanHibernate")) {
+                r = bus_manager_can_shutdown_or_sleep(
+                                m, connection, message,
+                                INHIBIT_SLEEP,
+                                "org.freedesktop.login1.hibernate",
+                                "org.freedesktop.login1.hibernate-multiple-sessions",
+                                "org.freedesktop.login1.hibernate-ignore-inhibit",
+                                "disk",
+                                &error, &reply);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
 
         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
                 char *introspection = NULL;
@@ -2029,38 +2199,39 @@ finish:
         return r;
 }
 
-int manager_dispatch_delayed_shutdown(Manager *manager) {
-        const char *name;
+int manager_dispatch_delayed(Manager *manager) {
+        const char *unit_name;
         DBusError error;
         bool delayed;
         int r;
 
         assert(manager);
 
-        if (!manager->delayed_shutdown)
+        if (!manager->delayed_unit)
                 return 0;
 
         /* Continue delay? */
         delayed =
-                manager->delayed_shutdown_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC) &&
-                manager_is_inhibited(manager, INHIBIT_SHUTDOWN, INHIBIT_DELAY, NULL);
+                manager->delayed_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC) &&
+                manager_is_inhibited(manager, manager->delayed_what, INHIBIT_DELAY, NULL);
         if (delayed)
                 return 0;
 
         /* Reset delay data */
-        name = manager->delayed_shutdown;
-        manager->delayed_shutdown = NULL;
+        unit_name = manager->delayed_unit;
+        manager->delayed_unit = NULL;
 
         /* Actually do the shutdown */
         dbus_error_init(&error);
-        r = send_start_unit(manager->bus, name, &error);
+        r = send_start_unit(manager->bus, unit_name, &error);
         if (r < 0) {
-                log_warning("Failed to send delayed shutdown message: %s", bus_error_message_or_strerror(&error, -r));
+                log_warning("Failed to send delayed message: %s", bus_error_message_or_strerror(&error, -r));
+                dbus_error_free(&error);
                 return r;
         }
 
         /* Tell people about it */
-        send_prepare_for_shutdown(manager, false);
+        send_prepare_for(manager, manager->delayed_what, false);
 
         return 1;
 }
diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c
index 2007ec7..512fc07 100644
--- a/src/login/logind-inhibit.c
+++ b/src/login/logind-inhibit.c
@@ -163,7 +163,7 @@ int inhibitor_start(Inhibitor *i) {
 
         i->started = true;
 
-        manager_send_changed(i->manager, "Inhibited\0");
+        manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited\0" : "DelayInhibited\0");
 
         return 0;
 }
@@ -182,7 +182,7 @@ int inhibitor_stop(Inhibitor *i) {
 
         i->started = false;
 
-        manager_send_changed(i->manager, "Inhibited\0");
+        manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited\0" : "DelayInhibited\0");
 
         return 0;
 }
diff --git a/src/login/logind-inhibit.h b/src/login/logind-inhibit.h
index 6364b00..4377f00 100644
--- a/src/login/logind-inhibit.h
+++ b/src/login/logind-inhibit.h
@@ -23,11 +23,10 @@
 ***/
 
 typedef struct Inhibitor Inhibitor;
+typedef enum InhibitWhat InhibitWhat;
 
 #include "list.h"
 #include "util.h"
-#include "logind.h"
-#include "logind-seat.h"
 
 typedef enum InhibitWhat {
         INHIBIT_SHUTDOWN = 1,
@@ -44,6 +43,9 @@ typedef enum InhibitMode {
         _INHIBIT_MODE_INVALID = -1
 } InhibitMode;
 
+#include "logind.h"
+#include "logind-seat.h"
+
 struct Inhibitor {
         Manager *manager;
 
diff --git a/src/login/logind.c b/src/login/logind.c
index 5860bfc..8d4f733 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -1269,7 +1269,7 @@ int manager_run(Manager *m) {
 
                 manager_gc(m, true);
 
-                if (manager_dispatch_delayed_shutdown(m) > 0)
+                if (manager_dispatch_delayed(m) > 0)
                         continue;
 
                 if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE)
@@ -1277,11 +1277,11 @@ int manager_run(Manager *m) {
 
                 manager_gc(m, true);
 
-                if (m->delayed_shutdown) {
+                if (m->delayed_unit) {
                         usec_t x, y;
 
                         x = now(CLOCK_MONOTONIC);
-                        y = m->delayed_shutdown_timestamp + m->inhibit_delay_max;
+                        y = m->delayed_timestamp + m->inhibit_delay_max;
 
                         msec = x >= y ? 0 : (int) ((y - x) / USEC_PER_MSEC);
                 }
diff --git a/src/login/logind.h b/src/login/logind.h
index 2c05452..2433221 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -85,8 +85,9 @@ struct Manager {
         /* If a shutdown was delayed due to a inhibitor this contains
            the unit name we are supposed to start after the delay is
            over */
-        const char *delayed_shutdown;
-        usec_t delayed_shutdown_timestamp;
+        const char *delayed_unit;
+        InhibitWhat delayed_what;
+        usec_t delayed_timestamp;
 
         usec_t inhibit_delay_max;
 };
@@ -140,7 +141,7 @@ DBusHandlerResult bus_message_filter(DBusConnection *c, DBusMessage *message, vo
 
 int manager_send_changed(Manager *manager, const char *properties);
 
-int manager_dispatch_delayed_shutdown(Manager *manager);
+int manager_dispatch_delayed(Manager *manager);
 
 /* gperf lookup function */
 const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length);
diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf
index 0f70b37..5860fd9 100644
--- a/src/login/org.freedesktop.login1.conf
+++ b/src/login/org.freedesktop.login1.conf
@@ -94,6 +94,14 @@
 
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Manager"
+                       send_member="Suspend"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="Hibernate"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
                        send_member="CanPowerOff"/>
 
                 <allow send_destination="org.freedesktop.login1"
@@ -102,6 +110,14 @@
 
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Manager"
+                       send_member="CanSuspend"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="CanHibernate"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
                        send_member="AttachDevice"/>
 
                 <allow send_destination="org.freedesktop.login1"
diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in
index 76ed2be..8422645 100644
--- a/src/login/org.freedesktop.login1.policy.in
+++ b/src/login/org.freedesktop.login1.policy.in
@@ -48,7 +48,7 @@
 
         <action id="org.freedesktop.login1.attach-device">
                 <_description>Allow attaching devices to seats</_description>
-                <_message>Authentication is required to allow attaching a device to a seat.</_message>
+                <_message>Authentication is required for attaching a device to a seat.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
@@ -58,7 +58,7 @@
 
         <action id="org.freedesktop.login1.flush-devices">
                 <_description>Flush device to seat attachments</_description>
-                <_message>Authentication is required to allow resetting how devices are attached to seats.</_message>
+                <_message>Authentication is required for resetting how devices are attached to seats.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
@@ -68,7 +68,7 @@
 
         <action id="org.freedesktop.login1.power-off">
                 <_description>Power off the system</_description>
-                <_message>Authentication is required to allow powering off the system.</_message>
+                <_message>Authentication is required for powering off the system.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
@@ -77,8 +77,8 @@
         </action>
 
         <action id="org.freedesktop.login1.power-off-multiple-sessions">
-                <_description>Power off the system when other users are logged in</_description>
-                <_message>Authentication is required to allow powering off the system while other users are logged in.</_message>
+                <_description>Power off the system while other users are logged in</_description>
+                <_message>Authentication is required for powering off the system while other users are logged in.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
@@ -87,8 +87,8 @@
         </action>
 
         <action id="org.freedesktop.login1.power-off-ignore-inhibit">
-                <_description>Power off the system when an application asked to inhibit it</_description>
-                <_message>Authentication is required to allow powering off the system while an application asked to inhibit it.</_message>
+                <_description>Power off the system while an application asked to inhibit it</_description>
+                <_message>Authentication is required for powering off the system while an application asked to inhibit it.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
@@ -98,7 +98,7 @@
 
         <action id="org.freedesktop.login1.reboot">
                 <_description>Reboot the system</_description>
-                <_message>Authentication is required to allow rebooting the system.</_message>
+                <_message>Authentication is required for rebooting the system.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
@@ -107,8 +107,8 @@
         </action>
 
         <action id="org.freedesktop.login1.reboot-multiple-sessions">
-                <_description>Reboot the system when other users are logged in</_description>
-                <_message>Authentication is required to allow rebooting the system while other users are logged in.</_message>
+                <_description>Reboot the system while other users are logged in</_description>
+                <_message>Authentication is required for rebooting the system while other users are logged in.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
@@ -117,8 +117,68 @@
         </action>
 
         <action id="org.freedesktop.login1.reboot-ignore-inhibit">
-                <_description>Reboot the system when an application asked to inhibit it</_description>
-                <_message>Authentication is required to allow rebooting the system while an application asked to inhibit it.</_message>
+                <_description>Reboot the system while an application asked to inhibit it</_description>
+                <_message>Authentication is required for rebooting the system while an application asked to inhibit it.</_message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.login1.suspend">
+                <_description>Suspend the system</_description>
+                <_message>Authentication is required for suspending the system.</_message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>yes</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.login1.suspend-multiple-sessions">
+                <_description>Suspend the system while other users are logged in</_description>
+                <_message>Authentication is required for suspending the system while other users are logged in.</_message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.login1.suspend-ignore-inhibit">
+                <_description>Suspend the system while an application asked to inhibit it</_description>
+                <_message>Authentication is required for suspending the system while an application asked to inhibit it.</_message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.login1.hibernate">
+                <_description>Hibernate the system</_description>
+                <_message>Authentication is required for hibernating the system.</_message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>yes</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.login1.hibernate-multiple-sessions">
+                <_description>Hibernate the system while other users are logged in</_description>
+                <_message>Authentication is required for hibernating the system while other users are logged in.</_message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.login1.hibernate-ignore-inhibit">
+                <_description>Hibernate the system while an application asked to inhibit it</_description>
+                <_message>Authentication is required for hibernating the system while an application asked to inhibit it.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
diff --git a/src/shared/util.c b/src/shared/util.c
index d8d3f1a..5f06c4b 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -5590,3 +5590,28 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
 
         return r;
 }
+
+int can_sleep(const char *type) {
+        char *p, *w, *state;
+        size_t l, k;
+        bool found = false;
+        int r;
+
+        assert(type);
+
+        r = read_one_line_file("/sys/power/state", &p);
+        if (r < 0)
+                return r == -ENOENT ? 0 : r;
+
+        k = strlen(type);
+
+        FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
+                if (l == k && strncmp(w, type, l) == 0) {
+                        found = true;
+                        break;
+                }
+        }
+
+        free(p);
+        return found;
+}
diff --git a/src/shared/util.h b/src/shared/util.h
index 59a69a8..272ab48 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -505,4 +505,6 @@ int setrlimit_closest(int resource, const struct rlimit *rlim);
 
 int getenv_for_pid(pid_t pid, const char *field, char **_value);
 
+int can_sleep(const char *type);
+
 #endif
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 762b5be..4708a35 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -1750,6 +1750,14 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) {
                 method = "PowerOff";
                 break;
 
+        case ACTION_SUSPEND:
+                method = "Suspend";
+                break;
+
+        case ACTION_HIBERNATE:
+                method = "Hibernate";
+                break;
+
         default:
                 return -EINVAL;
         }
@@ -1839,7 +1847,9 @@ static int start_special(DBusConnection *bus, char **args) {
         /* first try logind, to allow authentication with polkit */
         if (geteuid() != 0 &&
             (a == ACTION_POWEROFF ||
-             a == ACTION_REBOOT)) {
+             a == ACTION_REBOOT ||
+             a == ACTION_SUSPEND ||
+             a == ACTION_HIBERNATE)) {
                 r = reboot_with_logind(bus, a);
                 if (r >= 0)
                         return r;

commit 6edd7d0a09171ea5ae8e01b7b1cbcb0bdfbfeb16
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat May 5 02:06:58 2012 +0200

    sleep: implement suspend/hibernate as first class targets

diff --git a/.gitignore b/.gitignore
index 7f22255..e61f1cd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+/systemd-sleep
 /systemd-inhibit
 /systemd-remount-fs
 /build-aux
diff --git a/Makefile.am b/Makefile.am
index dbcfeb3..002a86a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -67,6 +67,7 @@ usergeneratordir=$(pkglibexecdir)/user-generators
 pkgincludedir=$(includedir)/systemd
 systemgeneratordir=$(rootlibexecdir)/system-generators
 systemshutdowndir=$(rootlibexecdir)/system-shutdown
+systemsleepdir=$(rootlibexecdir)/system-sleep
 systemunitdir=$(rootprefix)/lib/systemd/system
 udevlibexecdir=$(rootprefix)/lib/udev
 udevhomedir = $(libexecdir)/udev
@@ -117,6 +118,7 @@ AM_CPPFLAGS = \
 	-DSYSTEMD_CGROUP_AGENT_PATH=\"$(rootlibexecdir)/systemd-cgroups-agent\" \
 	-DSYSTEMD_BINARY_PATH=\"$(rootlibexecdir)/systemd\" \
 	-DSYSTEMD_SHUTDOWN_BINARY_PATH=\"$(rootlibexecdir)/systemd-shutdown\" \
+	-DSYSTEMD_SLEEP_BINARY_PATH=\"$(rootlibexecdir)/systemd-sleep\" \
 	-DSYSTEMCTL_BINARY_PATH=\"$(rootbindir)/systemctl\" \
 	-DSYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH=\"$(rootbindir)/systemd-tty-ask-password-agent\" \
 	-DSYSTEMD_STDIO_BRIDGE_BINARY_PATH=\"$(bindir)/systemd-stdio-bridge\" \
@@ -230,7 +232,8 @@ rootlibexec_PROGRAMS = \
 	systemd-fsck \
 	systemd-timestamp \
 	systemd-ac-power \
-	systemd-sysctl
+	systemd-sysctl \
+	systemd-sleep
 
 systemgenerator_PROGRAMS = \
 	systemd-getty-generator
@@ -266,6 +269,7 @@ dist_systemunit_DATA = \
 	units/nss-lookup.target \
 	units/nss-user-lookup.target \
 	units/mail-transfer-agent.target \
+	units/hibernate.target \
 	units/http-daemon.target \
 	units/poweroff.target \
 	units/reboot.target \
@@ -276,7 +280,9 @@ dist_systemunit_DATA = \
 	units/final.target \
 	units/umount.target \
 	units/sigpwr.target \
+	units/sleep.target \
 	units/sockets.target \
+	units/suspend.target \
 	units/swap.target \
 	units/systemd-initctl.socket \
 	units/systemd-shutdownd.socket \
@@ -318,12 +324,14 @@ nodist_systemunit_DATA = \
 	units/systemd-sysctl.service \
 	units/halt.service \
 	units/emergency.service \
+	units/hibernate.service \
 	units/poweroff.service \
 	units/reboot.service \
 	units/kexec.service \
 	units/fsck at .service \
 	units/fsck-root.service \
 	units/rescue.service \
+	units/suspend.service \
 	units/user at .service \
 	units/systemd-udev.service \
 	units/systemd-udev-trigger.service \
@@ -1063,6 +1071,13 @@ systemd_sysctl_LDADD = \
 	libsystemd-shared.la
 
 # ------------------------------------------------------------------------------
+systemd_sleep_SOURCES = \
+	src/sleep/sleep.c
+
+systemd_sleep_LDADD = \
+	libsystemd-shared.la
+
+# ------------------------------------------------------------------------------
 systemd_fsck_SOURCES = \
 	src/fsck/fsck.c
 
@@ -3027,6 +3042,7 @@ systemd-install-data-hook:
 		$(DESTDIR)$(prefix)/lib/sysctl.d \
 		$(DESTDIR)$(sysconfdir)/sysctl.d \
 		$(DESTDIR)$(systemshutdowndir) \
+		$(DESTDIR)$(systemsleepdir) \
 		$(DESTDIR)$(systemgeneratordir) \
 		$(DESTDIR)$(usergeneratordir)
 	$(MKDIR_P) -m 0755 \
diff --git a/TODO b/TODO
index cd7aecf..aeba2b5 100644
--- a/TODO
+++ b/TODO
@@ -53,9 +53,6 @@ Features:
 
 * ExecOnFailure=/usr/bin/foo
 
-* logind: add "mode" flag to poweroff/suspend inhibit logic so that we can
-  support both "inhibit" and "delay" mode.
-
 * fedora: make sshd and pam_loginuid work in nspawn containers
 
 * fix utmp for console logins in containers
@@ -72,8 +69,6 @@ Features:
 
 * journald: allow forwarding of log data to specific TTY instea dof console
 
-* suspend/hibernate/hybrid support, auto-suspend logic with idle hint
-
 * add RequiredBy to [Install]
 
 * udev: move to LGPL
diff --git a/man/systemctl.xml b/man/systemctl.xml
index dd0ff78..9e113eb 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -1149,6 +1149,16 @@
                                 <option>--user</option> option) and
                                 will fail otherwise.</para></listitem>
                         </varlistentry>
+                        <varlistentry>
+                                <term><command>suspend</command></term>
+
+                                <listitem><para>Suspend the system.</para></listitem>
+                        </varlistentry>
+                        <varlistentry>
+                                <term><command>hibernate</command></term>
+
+                                <listitem><para>Hibernate the system.</para></listitem>
+                        </varlistentry>
                 </variablelist>
 
         </refsect1>
diff --git a/man/systemd.special.xml b/man/systemd.special.xml
index 39c3802..984e998 100644
--- a/man/systemd.special.xml
+++ b/man/systemd.special.xml
@@ -58,6 +58,7 @@
                 <filename>exit.service</filename>,
                 <filename>final.service</filename>,
                 <filename>graphical.target</filename>,
+                <filename>hibernate.target</filename>,
                 <filename>http-daemon.target</filename>,
                 <filename>halt.target</filename>,
                 <filename>kbrequest.target</filename>,
@@ -80,7 +81,9 @@
                 <filename>runlevel5.target</filename>,
                 <filename>shutdown.target</filename>,
                 <filename>sigpwr.target</filename>,
+                <filename>sleep.target</filename>,
                 <filename>sockets.target</filename>,
+                <filename>suspend.target</filename>,
                 <filename>swap.target</filename>,
                 <filename>sysinit.target</filename>,
                 <filename>syslog.service</filename>,
@@ -240,6 +243,15 @@
                                 </listitem>
                         </varlistentry>
                         <varlistentry>
+                                <term><filename>hibernate.target</filename></term>
+                                <listitem>
+                                        <para>A special target unit
+                                        for hibernating the
+                                        system. This pulls in
+                                        <filename>sleep.target</filename>.</para>
+                                </listitem>
+                        </varlistentry>
+                        <varlistentry>
                                 <term><filename>http-daemon.target</filename></term>
                                 <listitem>
                                         <para>A target for pulling in
@@ -591,6 +603,19 @@
                                 </listitem>
                         </varlistentry>
                         <varlistentry>
+                                <term><filename>sleep.target</filename></term>
+                                <listitem>
+                                        <para>A special target unit
+                                        that is pulled in by
+                                        <filename>suspend.target</filename>
+                                        and
+                                        <filename>hibernate.target</filename>
+                                        and may be used to hook units
+                                        into the sleep state
+                                        logic.</para>
+                                </listitem>
+                        </varlistentry>
+                        <varlistentry>
                                 <term><filename>sockets.target</filename></term>
                                 <listitem>
                                         <para>A special target unit
@@ -605,6 +630,15 @@
                                 </listitem>
                         </varlistentry>
                         <varlistentry>
+                                <term><filename>suspend.target</filename></term>
+                                <listitem>
+                                        <para>A special target unit
+                                        for suspending the
+                                        system. This pulls in
+                                        <filename>sleep.target</filename>.</para>
+                                </listitem>
+                        </varlistentry>
+                        <varlistentry>
                                 <term><filename>swap.target</filename></term>
                                 <listitem>
                                         <para>Similar to
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 2494bb8..a8dfe26 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -309,6 +309,7 @@ int main(int argc, char *argv[]) {
         unsigned retries;
         bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true;
         bool killed_everbody = false, in_container, use_watchdog = false;
+        char *arguments[3];
 
         log_parse_environment();
         log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */
@@ -442,7 +443,10 @@ int main(int argc, char *argv[]) {
         if (retries >= FINALIZE_ATTEMPTS)
                 log_error("Too many iterations, giving up.");
 
-        execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, NULL);
+        arguments[0] = NULL;
+        arguments[1] = argv[1];
+        arguments[2] = NULL;
+        execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments);
 
         /* If we are in a container, just exit, this will kill our
          * container for good. */
diff --git a/src/core/special.h b/src/core/special.h
index 2db4711..bc9b330 100644
--- a/src/core/special.h
+++ b/src/core/special.h
@@ -36,6 +36,8 @@
 #define SPECIAL_REBOOT_TARGET "reboot.target"
 #define SPECIAL_KEXEC_TARGET "kexec.target"
 #define SPECIAL_EXIT_TARGET "exit.target"
+#define SPECIAL_SUSPEND_TARGET "suspend.target"
+#define SPECIAL_HIBERNATE_TARGET "hibernate.target"
 
 /* Special boot targets */
 #define SPECIAL_RESCUE_TARGET "rescue.target"
diff --git a/src/shared/util.c b/src/shared/util.c
index 8a0b2a1..d8d3f1a 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -4106,8 +4106,7 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) {
                                 _argv[1] = NULL;
                                 argv = _argv;
                         } else
-                                if (!argv[0])
-                                        argv[0] = path;
+                                argv[0] = path;
 
                         execv(path, argv);
 
diff --git a/src/sleep/Makefile b/src/sleep/Makefile
new file mode 120000
index 0000000..d0b0e8e
--- /dev/null
+++ b/src/sleep/Makefile
@@ -0,0 +1 @@
+../Makefile
\ No newline at end of file
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
new file mode 100644
index 0000000..7062dc2
--- /dev/null
+++ b/src/sleep/sleep.c
@@ -0,0 +1,83 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  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/>.
+***/
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "log.h"
+#include "util.h"
+
+int main(int argc, char *argv[]) {
+        const char *verb;
+        char* arguments[4];
+        int r;
+        FILE *f;
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        if (argc != 2) {
+                log_error("Invalid number of arguments.");
+                r = -EINVAL;
+                goto finish;
+        }
+
+        if (streq(argv[1], "suspend"))
+                verb = "mem";
+        else if (streq(argv[1], "hibernate"))
+                verb = "disk";
+        else {
+                log_error("Unknown action '%s'.", argv[1]);
+                r = -EINVAL;
+                goto finish;
+        }
+
+        f = fopen("/sys/power/state", "we");
+        if (!f) {
+                log_error("Failed to open /sys/power/state: %m");
+                r = -errno;
+                goto finish;
+        }
+
+        arguments[0] = NULL;
+        arguments[1] = (char*) "pre";
+        arguments[2] = argv[1];
+        arguments[3] = NULL;
+        execute_directory(SYSTEMD_SLEEP_BINARY_PATH, NULL, arguments);
+
+        fputs(verb, f);
+        fputc('\n', f);
+        fflush(f);
+
+        r = ferror(f) ? -errno : 0;
+
+        arguments[1] = (char*) "post";
+        execute_directory(SYSTEMD_SLEEP_BINARY_PATH, NULL, arguments);
+
+        fclose(f);
+
+finish:
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+}
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index acede4e..762b5be 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -97,6 +97,8 @@ static enum action {
         ACTION_REBOOT,
         ACTION_KEXEC,
         ACTION_EXIT,
+        ACTION_SUSPEND,
+        ACTION_HIBERNATE,
         ACTION_RUNLEVEL2,
         ACTION_RUNLEVEL3,
         ACTION_RUNLEVEL4,
@@ -1605,6 +1607,10 @@ static enum action verb_to_action(const char *verb) {
                 return ACTION_DEFAULT;
         else if (streq(verb, "exit"))
                 return ACTION_EXIT;
+        else if (streq(verb, "suspend"))
+                return ACTION_SUSPEND;
+        else if (streq(verb, "hibernate"))
+                return ACTION_HIBERNATE;
         else
                 return ACTION_INVALID;
 }
@@ -1623,7 +1629,9 @@ static int start_unit(DBusConnection *bus, char **args) {
                 [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
                 [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET,
                 [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET,
-                [ACTION_EXIT] = SPECIAL_EXIT_TARGET
+                [ACTION_EXIT] = SPECIAL_EXIT_TARGET,
+                [ACTION_SUSPEND] = SPECIAL_SUSPEND_TARGET,
+                [ACTION_HIBERNATE] = SPECIAL_HIBERNATE_TARGET
         };
 
         int r, ret = 0;
@@ -4201,7 +4209,9 @@ static int systemctl_help(void) {
                "  poweroff                        Shut down and power-off the system\n"
                "  reboot                          Shut down and reboot the system\n"
                "  kexec                           Shut down and reboot the system with kexec\n"
-               "  exit                            Ask for user instance termination\n",
+               "  exit                            Request user instance exit\n"
+               "  suspend                         Suspend the system\n"
+               "  hibernate                       Hibernate the system\n",
                program_invocation_short_name);
 
         return 0;
@@ -5135,6 +5145,8 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 { "poweroff",              EQUAL, 1, start_special     },
                 { "reboot",                EQUAL, 1, start_special     },
                 { "kexec",                 EQUAL, 1, start_special     },
+                { "suspend",               EQUAL, 1, start_special     },
+                { "hibernate",             EQUAL, 1, start_special     },
                 { "default",               EQUAL, 1, start_special     },
                 { "rescue",                EQUAL, 1, start_special     },
                 { "emergency",             EQUAL, 1, start_special     },
diff --git a/units/.gitignore b/units/.gitignore
index 68c174f..dc5e1d4 100644
--- a/units/.gitignore
+++ b/units/.gitignore
@@ -1,3 +1,5 @@
+/hibernate.service
+/suspend.service
 /console-getty.service
 /systemd-journald.service
 user at .service
diff --git a/units/hibernate.service.in b/units/hibernate.service.in
new file mode 100644
index 0000000..6dba653
--- /dev/null
+++ b/units/hibernate.service.in
@@ -0,0 +1,16 @@
+#  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=Hibernate
+DefaultDependencies=no
+Requires=sleep.target
+After=sleep.target
+
+[Service]
+Type=oneshot
+ExecStart=@rootlibexecdir@/systemd-sleep hibernate
diff --git a/units/hibernate.target b/units/hibernate.target
new file mode 100644
index 0000000..05238a7
--- /dev/null
+++ b/units/hibernate.target
@@ -0,0 +1,14 @@
+#  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.
+
+# See systemd.special(7) for details
+
+[Unit]
+Description=Hibernate
+DefaultDependencies=no
+BindTo=hibernate.service
+After=hibernate.service
diff --git a/units/sleep.target b/units/sleep.target
new file mode 100644
index 0000000..9f4b247
--- /dev/null
+++ b/units/sleep.target
@@ -0,0 +1,13 @@
+#  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.
+
+# See systemd.special(7) for details
+
+[Unit]
+Description=Sleep
+DefaultDependencies=no
+RefuseManualStart=yes
diff --git a/units/suspend.service.in b/units/suspend.service.in
new file mode 100644
index 0000000..3cf819e
--- /dev/null
+++ b/units/suspend.service.in
@@ -0,0 +1,16 @@
+#  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=Suspend
+DefaultDependencies=no
+Requires=sleep.target
+After=sleep.target
+
+[Service]
+Type=oneshot
+ExecStart=@rootlibexecdir@/systemd-sleep suspend
diff --git a/units/suspend.target b/units/suspend.target
new file mode 100644
index 0000000..3ddb449
--- /dev/null
+++ b/units/suspend.target
@@ -0,0 +1,14 @@
+#  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.
+
+# See systemd.special(7) for details
+
+[Unit]
+Description=Suspend
+DefaultDependencies=no
+BindTo=suspend.service
+After=suspend.service

commit 4943c1c94ba751c98763f4232b4350481b22c90a
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat May 5 00:46:31 2012 +0200

    logind: use "sleep" as generic term for "suspend", "hibernate", and later on "hybrid suspend"

diff --git a/TODO b/TODO
index ebdc98d..cd7aecf 100644
--- a/TODO
+++ b/TODO
@@ -41,8 +41,6 @@ Features:
         SessionIdleMode=(explicit|ignore|login)
         ForceShutdown=(yes|no)
 
-* logind: use "sleep" as generic term for "suspend", "hibernate", ...
-
 * services which create their own subcgroups break cgroup-empty notification (needs to be fixed in the kernel)
 
 * don't delete /tmp/systemd-namespace-* before a process is gone down
diff --git a/man/logind.conf.xml b/man/logind.conf.xml
index deca1cd..166038b 100644
--- a/man/logind.conf.xml
+++ b/man/logind.conf.xml
@@ -151,11 +151,12 @@
                                 <term><varname>InhibitDelayMaxSec=</varname></term>
 
                                 <listitem><para>Specifies the maximum
-                                time a suspend or reboot is delayed
-                                due to an inhibitor lock of type
-                                <literal>delay</literal> being taken
-                                before it is ignored and the operation
-                                executed anyway. Defaults to
+                                time a system shutdown or sleep
+                                request is delayed due to an inhibitor
+                                lock of type <literal>delay</literal>
+                                being taken before it is ignored and
+                                the operation executed
+                                anyway. Defaults to
                                 5s.</para></listitem>
 
                         </varlistentry>
diff --git a/man/systemd-inhibit.xml b/man/systemd-inhibit.xml
index 3409590..c71a954 100644
--- a/man/systemd-inhibit.xml
+++ b/man/systemd-inhibit.xml
@@ -60,14 +60,14 @@
                 <title>Description</title>
 
                 <para><command>systemd-inhibit</command> may be used
-                to execute a program with a shutdown, suspend or idle
+                to execute a program with a shutdown, sleep or idle
                 inhibitor lock taken. The lock will be acquired before
                 the specified command line is executed and released
                 afterwards.</para>
 
                 <para>Inhibitor locks may be used to block or delay
-                suspend and shutdown requests from the user, as well
-                as automatic idle handling of the OS. This may be used
+                system sleep and shutdown requests from the user, as well
+                as automatic idle handling of the OS. This is useful
                 to avoid system suspends while an optical disc is
                 being recorded, or similar operations that should not
                 be interrupted.</para>
@@ -101,7 +101,7 @@
                                 separated list of one or more
                                 operations to inhibit:
                                 <literal>shutdown</literal>,
-                                <literal>suspend</literal>,
+                                <literal>sleep</literal>,
                                 <literal>idle</literal>, for
                                 inhibiting reboot/power-off/halt/kexec,
                                 suspending/hibernating, resp. the
diff --git a/src/login/inhibit.c b/src/login/inhibit.c
index a817c84..6f24a1b 100644
--- a/src/login/inhibit.c
+++ b/src/login/inhibit.c
@@ -31,7 +31,7 @@
 #include "build.h"
 #include "strv.h"
 
-static const char* arg_what = "idle:suspend:shutdown";
+static const char* arg_what = "idle:sleep:shutdown";
 static const char* arg_who = NULL;
 static const char* arg_why = "Unknown reason";
 static const char* arg_mode = "block";
@@ -182,11 +182,11 @@ finish:
 static int help(void) {
 
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
-               "Execute a process while inhibiting shutdown/suspend/idle.\n\n"
+               "Execute a process while inhibiting shutdown/sleep/idle.\n\n"
                "  -h --help               Show this help\n"
                "     --version            Show package version\n"
                "     --what=WHAT          Operations to inhibit, colon separated list of idle,\n"
-               "                          suspend, shutdown\n"
+               "                          sleep, shutdown\n"
                "     --who=STRING         A descriptive string who is inhibiting\n"
                "     --why=STRING         A descriptive string why is being inhibited\n"
                "     --mode=MODE          One of block or delay\n"
diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c
index b5b801e..2007ec7 100644
--- a/src/login/logind-inhibit.c
+++ b/src/login/logind-inhibit.c
@@ -376,12 +376,12 @@ const char *inhibit_what_to_string(InhibitWhat w) {
         static const char* const table[_INHIBIT_WHAT_MAX] = {
                 [0] = "",
                 [INHIBIT_SHUTDOWN] = "shutdown",
-                [INHIBIT_SUSPEND] = "suspend",
+                [INHIBIT_SLEEP] = "sleep",
                 [INHIBIT_IDLE] = "idle",
-                [INHIBIT_SHUTDOWN|INHIBIT_SUSPEND] = "shutdown:suspend",
+                [INHIBIT_SHUTDOWN|INHIBIT_SLEEP] = "shutdown:sleep",
                 [INHIBIT_SHUTDOWN|INHIBIT_IDLE] = "shutdown:idle",
-                [INHIBIT_SHUTDOWN|INHIBIT_SUSPEND|INHIBIT_IDLE] = "shutdown:suspend:idle",
-                [INHIBIT_SUSPEND|INHIBIT_IDLE] = "suspend:idle"
+                [INHIBIT_SHUTDOWN|INHIBIT_SLEEP|INHIBIT_IDLE] = "shutdown:sleep:idle",
+                [INHIBIT_SLEEP|INHIBIT_IDLE] = "sleep:idle"
         };
 
         if (w < 0 || w >= _INHIBIT_WHAT_MAX)
@@ -398,8 +398,8 @@ InhibitWhat inhibit_what_from_string(const char *s) {
         FOREACH_WORD_SEPARATOR(w, l, s, ":", state) {
                 if (l == 8 && strncmp(w, "shutdown", l) == 0)
                         what |= INHIBIT_SHUTDOWN;
-                else if (l == 7 && strncmp(w, "suspend", l) == 0)
-                        what |= INHIBIT_SUSPEND;
+                else if (l == 5 && strncmp(w, "sleep", l) == 0)
+                        what |= INHIBIT_SLEEP;
                 else if (l == 4 && strncmp(w, "idle", l) == 0)
                         what |= INHIBIT_IDLE;
                 else
diff --git a/src/login/logind-inhibit.h b/src/login/logind-inhibit.h
index 823af39..6364b00 100644
--- a/src/login/logind-inhibit.h
+++ b/src/login/logind-inhibit.h
@@ -31,7 +31,7 @@ typedef struct Inhibitor Inhibitor;
 
 typedef enum InhibitWhat {
         INHIBIT_SHUTDOWN = 1,
-        INHIBIT_SUSPEND = 2,
+        INHIBIT_SLEEP = 2,
         INHIBIT_IDLE = 4,
         _INHIBIT_WHAT_MAX = 8,
         _INHIBIT_WHAT_INVALID = -1
diff --git a/src/login/test-inhibit.c b/src/login/test-inhibit.c
index 634f1e1..7b6deff 100644
--- a/src/login/test-inhibit.c
+++ b/src/login/test-inhibit.c
@@ -119,7 +119,7 @@ int main(int argc, char*argv[]) {
 
         print_inhibitors(bus);
 
-        fd1 = inhibit(bus, "suspend");
+        fd1 = inhibit(bus, "sleep");
         assert(fd1 >= 0);
         print_inhibitors(bus);
 

commit dd5f0a96f734aafa00f6c3aea4003b7fdd278d81
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat May 5 00:39:24 2012 +0200

    logind: fix test-inhibit

diff --git a/src/login/test-inhibit.c b/src/login/test-inhibit.c
index c83e960..634f1e1 100644
--- a/src/login/test-inhibit.c
+++ b/src/login/test-inhibit.c
@@ -30,7 +30,7 @@
 static int inhibit(DBusConnection *bus, const char *what) {
         DBusMessage *m, *reply;
         DBusError error;
-        const char *who = "Test Tool", *reason = "Just because!";
+        const char *who = "Test Tool", *reason = "Just because!", *mode = "block";
         int fd;
 
         dbus_error_init(&error);
@@ -46,6 +46,7 @@ static int inhibit(DBusConnection *bus, const char *what) {
                                            DBUS_TYPE_STRING, &what,
                                            DBUS_TYPE_STRING, &who,
                                            DBUS_TYPE_STRING, &reason,
+                                           DBUS_TYPE_STRING, &mode,
                                            DBUS_TYPE_INVALID));
 
         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
@@ -83,19 +84,20 @@ static void print_inhibitors(DBusConnection *bus) {
         dbus_message_iter_recurse(&iter, &sub);
 
         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                const char *what, *who, *reason;
+                const char *what, *who, *why, *mode;
                 dbus_uint32_t uid, pid;
 
                 dbus_message_iter_recurse(&sub, &sub2);
 
                 assert_se(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &what, true) >= 0);
                 assert_se(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &who, true) >= 0);
-                assert_se(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &reason, true) >= 0);
+                assert_se(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &why, true) >= 0);
+                assert_se(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &mode, true) >= 0);
                 assert_se(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) >= 0);
                 assert_se(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, false) >= 0);
 
-                printf("what=<%s> who=<%s> reason=<%s> uid=<%lu> pid=<%lu>\n",
-                       what, who, reason, (unsigned long) uid, (unsigned long) pid);
+                printf("what=<%s> who=<%s> why=<%s> mode=<%s> uid=<%lu> pid=<%lu>\n",
+                       what, who, why, mode, (unsigned long) uid, (unsigned long) pid);
 
                 dbus_message_iter_next(&sub);
 



More information about the systemd-commits mailing list