chiark / gitweb /
Prep v220: Use new cgroups functions
authorSven Eden <yamakuzure@gmx.net>
Mon, 28 Nov 2016 03:53:40 +0000 (04:53 +0100)
committerSven Eden <yamakuzure@gmx.net>
Tue, 14 Mar 2017 09:17:13 +0000 (10:17 +0100)
Prep v220: Update logind and loginctl to upstream version.
Prep v220: src/shared/rm-rf.c does not need to be able to handle btrfs subvolumes for elogind.

17 files changed:
src/libelogind/sd-bus/bus-creds.c
src/login/71-seat.rules.in
src/login/loginctl.c
src/login/logind-action.c
src/login/logind-core.c
src/login/logind-dbus.c
src/login/logind-gperf.gperf
src/login/logind-seat.c
src/login/logind-session-dbus.c
src/login/logind-session.c
src/login/logind-session.h
src/login/logind-user.c
src/login/logind-user.h
src/login/logind.c
src/login/logind.conf
src/login/logind.h
src/shared/rm-rf.c

index 153b058593eef6f754e7b7280d07c1ba49514cc3..1c365b7fcd54960da7dfc2e1cae6504882cbe304 100644 (file)
@@ -387,6 +387,8 @@ _public_ int sd_bus_creds_get_cgroup(sd_bus_creds *c, const char **ret) {
 }
 
 _public_ int sd_bus_creds_get_unit(sd_bus_creds *c, const char **ret) {
 }
 
 _public_ int sd_bus_creds_get_unit(sd_bus_creds *c, const char **ret) {
+        int r;
+
         assert_return(c, -EINVAL);
         assert_return(ret, -EINVAL);
 
         assert_return(c, -EINVAL);
         assert_return(ret, -EINVAL);
 
@@ -395,14 +397,25 @@ _public_ int sd_bus_creds_get_unit(sd_bus_creds *c, const char **ret) {
 
         assert(c->cgroup);
 
 
         assert(c->cgroup);
 
-        if (!c->unit)
-                return -ESRCH;
+        if (!c->unit) {
+                const char *shifted;
+
+                r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
+                if (r < 0)
+                        return r;
+
+                r = cg_path_get_unit(shifted, (char**) &c->unit);
+                if (r < 0)
+                        return r;
+        }
 
         *ret = c->unit;
         return 0;
 }
 
 _public_ int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **ret) {
 
         *ret = c->unit;
         return 0;
 }
 
 _public_ int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **ret) {
+        int r;
+
         assert_return(c, -EINVAL);
         assert_return(ret, -EINVAL);
 
         assert_return(c, -EINVAL);
         assert_return(ret, -EINVAL);
 
@@ -411,14 +424,25 @@ _public_ int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **ret) {
 
         assert(c->cgroup);
 
 
         assert(c->cgroup);
 
-        if (!c->user_unit)
-                return -ESRCH;
+        if (!c->user_unit) {
+                const char *shifted;
+
+                r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
+                if (r < 0)
+                        return r;
+
+                r = cg_path_get_user_unit(shifted, (char**) &c->user_unit);
+                if (r < 0)
+                        return r;
+        }
 
         *ret = c->user_unit;
         return 0;
 }
 
 _public_ int sd_bus_creds_get_slice(sd_bus_creds *c, const char **ret) {
 
         *ret = c->user_unit;
         return 0;
 }
 
 _public_ int sd_bus_creds_get_slice(sd_bus_creds *c, const char **ret) {
+        int r;
+
         assert_return(c, -EINVAL);
         assert_return(ret, -EINVAL);
 
         assert_return(c, -EINVAL);
         assert_return(ret, -EINVAL);
 
@@ -427,8 +451,17 @@ _public_ int sd_bus_creds_get_slice(sd_bus_creds *c, const char **ret) {
 
         assert(c->cgroup);
 
 
         assert(c->cgroup);
 
-        if (!c->slice)
-                return -ESRCH;
+        if (!c->slice) {
+                const char *shifted;
+
+                r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
+                if (r < 0)
+                        return r;
+
+                r = cg_path_get_slice(shifted, (char**) &c->slice);
+                if (r < 0)
+                        return r;
+        }
 
         *ret = c->slice;
         return 0;
 
         *ret = c->slice;
         return 0;
@@ -504,7 +537,7 @@ _public_ int sd_bus_creds_get_owner_uid(sd_bus_creds *c, uid_t *uid) {
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        return -ESRCH;
+        return cg_path_get_owner_uid(shifted, uid);
 }
 
 _public_ int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline) {
 }
 
 _public_ int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline) {
index d5aeacd7a4c72b8994e4d6fc1a0f3256005354e6..de55c9a4ec2a2056f3fb3efdb43f2197171214fe 100644 (file)
@@ -44,11 +44,11 @@ SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{idProduct}=="401a", ATTR{product}
 SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{idProduct}=="401a", ATTR{product}=="mimo inc", \
                   ATTR{../idVendor}=="058f", ATTR{../idProduct}=="6254", \
                   ENV{ID_AVOID_LOOP}=="", \
 SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{idProduct}=="401a", ATTR{product}=="mimo inc", \
                   ATTR{../idVendor}=="058f", ATTR{../idProduct}=="6254", \
                   ENV{ID_AVOID_LOOP}=="", \
-                  RUN+="@udevbindir@/udevadm trigger --parent-match=%p/.."
+                  RUN+="@rootbindir@/udevadm trigger --parent-match=%p/.."
 
 TAG=="seat", ENV{ID_PATH}=="", IMPORT{builtin}="path_id"
 TAG=="seat", ENV{ID_FOR_SEAT}=="", ENV{ID_PATH_TAG}!="", ENV{ID_FOR_SEAT}="$env{SUBSYSTEM}-$env{ID_PATH_TAG}"
 
 
 TAG=="seat", ENV{ID_PATH}=="", IMPORT{builtin}="path_id"
 TAG=="seat", ENV{ID_FOR_SEAT}=="", ENV{ID_PATH_TAG}!="", ENV{ID_FOR_SEAT}="$env{SUBSYSTEM}-$env{ID_PATH_TAG}"
 
-SUBSYSTEM=="input", ATTR{name}=="Wiebetech LLC Wiebetech", RUN+="@bindir@/loginctl lock-sessions"
+SUBSYSTEM=="input", ATTR{name}=="Wiebetech LLC Wiebetech", RUN+="@rootbindir@/loginctl lock-sessions"
 
 LABEL="seat_end"
 
 LABEL="seat_end"
index 31400d9629e3f3d29234f5c5efb6fe5e478543fc..c44209fe04106e7affd54d868af026be000b75ea 100644 (file)
@@ -56,9 +56,10 @@ static int arg_signal = SIGTERM;
 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
 static char *arg_host = NULL;
 static bool arg_ask_password = true;
 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
 static char *arg_host = NULL;
 static bool arg_ask_password = true;
-static bool arg_ignore_inhibitors = false;
+#if 0
 static unsigned arg_lines = 10;
 static OutputMode arg_output = OUTPUT_SHORT;
 static unsigned arg_lines = 10;
 static OutputMode arg_output = OUTPUT_SHORT;
+#endif // 0
 
 static void pager_open_if_enabled(void) {
 
 
 static void pager_open_if_enabled(void) {
 
@@ -552,7 +553,8 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li
                                         true,
                                         NULL);
                 }
                                         true,
                                         NULL);
                 }
-#endif
+
+#endif // 0
         }
 
         return 0;
         }
 
         return 0;
@@ -632,7 +634,8 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
                                 SD_JOURNAL_LOCAL_ONLY,
                                 true,
                                 NULL);
                                 SD_JOURNAL_LOCAL_ONLY,
                                 true,
                                 NULL);
-#endif
+
+#endif // 0
         }
 
         return 0;
         }
 
         return 0;
@@ -1195,9 +1198,6 @@ static int check_inhibitors(sd_bus *bus, const char *verb, const char *inhibit_w
 
         assert(bus);
 
 
         assert(bus);
 
-        if (arg_ignore_inhibitors)
-                return 0;
-
         if (geteuid() == 0)
                 return 0;
 
         if (geteuid() == 0)
                 return 0;
 
@@ -1435,7 +1435,6 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --no-pager            Do not pipe output into a pager\n"
                "     --no-legend           Do not show the headers and footers\n"
                "     --no-ask-password     Don't prompt for password\n"
                "     --no-pager            Do not pipe output into a pager\n"
                "     --no-legend           Do not show the headers and footers\n"
                "     --no-ask-password     Don't prompt for password\n"
-               "  -i --ignore-inhibitors   Ignore inhibitors when suspending or shutting down\n"
                "  -H --host=[USER@]HOST    Operate on remote host\n"
                "  -M --machine=CONTAINER   Operate on local container\n"
                "  -p --property=NAME       Show only properties by this name\n"
                "  -H --host=[USER@]HOST    Operate on remote host\n"
                "  -M --machine=CONTAINER   Operate on local container\n"
                "  -p --property=NAME       Show only properties by this name\n"
@@ -1443,9 +1442,11 @@ static int help(int argc, char *argv[], void *userdata) {
                "  -l --full                Do not ellipsize output\n"
                "     --kill-who=WHO        Who to send signal to\n"
                "  -s --signal=SIGNAL       Which signal to send\n"
                "  -l --full                Do not ellipsize output\n"
                "     --kill-who=WHO        Who to send signal to\n"
                "  -s --signal=SIGNAL       Which signal to send\n"
+#if 0
                "  -n --lines=INTEGER       Number of journal entries to show\n"
                "  -o --output=STRING       Change journal output mode (short, short-monotonic,\n"
                "                           verbose, export, json, json-pretty, json-sse, cat)\n\n"
                "  -n --lines=INTEGER       Number of journal entries to show\n"
                "  -o --output=STRING       Change journal output mode (short, short-monotonic,\n"
                "                           verbose, export, json, json-pretty, json-sse, cat)\n\n"
+#endif // 0
                "Session Commands:\n"
                "  list-sessions            List sessions\n"
                "  session-status [ID...]   Show session status\n"
                "Session Commands:\n"
                "  list-sessions            List sessions\n"
                "  session-status [ID...]   Show session status\n"
@@ -1471,7 +1472,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "  show-seat [NAME...]      Show properties of seats or the manager\n"
                "  attach NAME DEVICE...    Attach one or more devices to a seat\n"
                "  flush-devices            Flush all device associations\n"
                "  show-seat [NAME...]      Show properties of seats or the manager\n"
                "  attach NAME DEVICE...    Attach one or more devices to a seat\n"
                "  flush-devices            Flush all device associations\n"
-               "  terminate-seat NAME...   Terminate all sessions on one or more seats\n\n"
+               "  terminate-seat NAME...   Terminate all sessions on one or more seats\n"
                "System Commands:\n"
                "  poweroff                 Turn off the machine\n"
                "  reboot                   Reboot the machine\n"
                "System Commands:\n"
                "  poweroff                 Turn off the machine\n"
                "  reboot                   Reboot the machine\n"
@@ -1506,9 +1507,10 @@ static int parse_argv(int argc, char *argv[]) {
                 { "host",            required_argument, NULL, 'H'                 },
                 { "machine",         required_argument, NULL, 'M'                 },
                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
                 { "host",            required_argument, NULL, 'H'                 },
                 { "machine",         required_argument, NULL, 'M'                 },
                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
-                { "ignore-inhibitors", no_argument,     NULL, 'i'                 },
+#if 0
                 { "lines",           required_argument, NULL, 'n'                 },
                 { "output",          required_argument, NULL, 'o'                 },
                 { "lines",           required_argument, NULL, 'n'                 },
                 { "output",          required_argument, NULL, 'o'                 },
+#endif //
                 {}
         };
 
                 {}
         };
 
@@ -1517,7 +1519,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:i", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
 
                 switch (c) {
 
 
                 switch (c) {
 
@@ -1549,7 +1551,7 @@ static int parse_argv(int argc, char *argv[]) {
                 case 'l':
                         arg_full = true;
                         break;
                 case 'l':
                         arg_full = true;
                         break;
-
+#if 0
                 case 'n':
                         if (safe_atou(optarg, &arg_lines) < 0) {
                                 log_error("Failed to parse lines '%s'", optarg);
                 case 'n':
                         if (safe_atou(optarg, &arg_lines) < 0) {
                                 log_error("Failed to parse lines '%s'", optarg);
@@ -1558,21 +1560,13 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'o':
                         break;
 
                 case 'o':
-#if 0
                         arg_output = output_mode_from_string(optarg);
                         arg_output = output_mode_from_string(optarg);
-#else
-                        arg_output = -1;
-#endif
                         if (arg_output < 0) {
                                 log_error("Unknown output '%s'.", optarg);
                                 return -EINVAL;
                         }
                         break;
                         if (arg_output < 0) {
                                 log_error("Unknown output '%s'.", optarg);
                                 return -EINVAL;
                         }
                         break;
-
-                case 'i':
-                        arg_ignore_inhibitors = true;
-                        break;
-
+#endif // 0
                 case ARG_NO_PAGER:
                         arg_no_pager = true;
                         break;
                 case ARG_NO_PAGER:
                         arg_no_pager = true;
                         break;
index 7a57e1ba222ff63dead57fc6aa02f95e6b808dfd..f635fb1b632039a459d4f0de245279a7d6e03faa 100644 (file)
 ***/
 
 #include <unistd.h>
 ***/
 
 #include <unistd.h>
-#include <stdio.h>
-#include <errno.h>
-
-#include "sd-messages.h"
-#include "log.h"
-#include "util.h"
-#include "strv.h"
-#include "fileio.h"
-#include "build.h"
-#include "def.h"
+
 #include "conf-parser.h"
 #include "conf-parser.h"
+#include "special.h"
 #include "sleep-config.h"
 #include "sleep-config.h"
-#include "bus-error.h"
 #include "bus-util.h"
 #include "bus-util.h"
+#include "bus-error.h"
 #include "logind-action.h"
 #include "formats-util.h"
 #include "process-util.h"
 #include "logind-action.h"
 #include "formats-util.h"
 #include "process-util.h"
@@ -56,6 +48,16 @@ int manager_handle_action(
                 [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending..."
         };
 
                 [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending..."
         };
 
+        static const char * const target_table[_HANDLE_ACTION_MAX] = {
+                [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET,
+                [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET,
+                [HANDLE_HALT] = SPECIAL_HALT_TARGET,
+                [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET,
+                [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET,
+                [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
+                [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET
+        };
+
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         InhibitWhat inhibit_operation;
         Inhibitor *offending = NULL;
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         InhibitWhat inhibit_operation;
         Inhibitor *offending = NULL;
@@ -151,7 +153,7 @@ int manager_handle_action(
 
         log_info("%s", message_table[handle]);
 
 
         log_info("%s", message_table[handle]);
 
-        r = bus_manager_shutdown_or_sleep_now_or_later(m, handle, inhibit_operation, &error);
+        r = bus_manager_shutdown_or_sleep_now_or_later(m, target_table[handle], inhibit_operation, &error);
         if (r < 0) {
                 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
                 return r;
         if (r < 0) {
                 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
                 return r;
@@ -160,144 +162,6 @@ int manager_handle_action(
         return 1;
 }
 
         return 1;
 }
 
-static int run_helper(const char *helper) {
-        int pid = fork();
-        if (pid < 0) {
-                return log_error_errno(errno, "Failed to fork: %m");
-        }
-
-        if (pid == 0) {
-                /* Child */
-
-                close_all_fds(NULL, 0);
-
-                execlp(helper, helper, NULL);
-                log_error_errno(errno, "Failed to execute %s: %m", helper);
-                _exit(EXIT_FAILURE);
-        }
-
-        return wait_for_terminate_and_warn(helper, pid, true);
-}
-
-static int write_mode(char **modes) {
-        int r = 0;
-        char **mode;
-
-        STRV_FOREACH(mode, modes) {
-                int k;
-
-                k = write_string_file("/sys/power/disk", *mode);
-                if (k == 0)
-                        return 0;
-
-                log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m",
-                                *mode);
-                if (r == 0)
-                        r = k;
-        }
-
-        if (r < 0)
-                log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");
-
-        return r;
-}
-
-static int write_state(FILE **f, char **states) {
-        char **state;
-        int r = 0;
-
-        STRV_FOREACH(state, states) {
-                int k;
-
-                k = write_string_stream(*f, *state);
-                if (k == 0)
-                        return 0;
-                log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m",
-                                *state);
-                if (r == 0)
-                        r = k;
-
-                fclose(*f);
-                *f = fopen("/sys/power/state", "we");
-                if (!*f)
-                        return log_error_errno(errno, "Failed to open /sys/power/state: %m");
-        }
-
-        return r;
-}
-
-static int do_sleep(const char *arg_verb) {
-        _cleanup_strv_free_ char **modes = NULL, **states = NULL;
-        char *arguments[] = {
-                NULL,
-                (char*) "pre",
-                (char*) arg_verb,
-                NULL
-        };
-        static const char* const dirs[] = { SYSTEM_SLEEP_PATH, NULL};
-        int r;
-        _cleanup_fclose_ FILE *f = NULL;
-
-        r = parse_sleep_config(arg_verb, &modes, &states);
-        if (r < 0)
-                return r;
-
-        /* This file is opened first, so that if we hit an error,
-         * we can abort before modifying any state. */
-        f = fopen("/sys/power/state", "we");
-        if (!f)
-                return log_error_errno(errno, "Failed to open /sys/power/state: %m");
-
-        /* Configure the hibernation mode */
-        r = write_mode(modes);
-        if (r < 0)
-                return r;
-
-        execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
-
-        log_struct(LOG_INFO,
-                   LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START),
-                   LOG_MESSAGE("Suspending system..."),
-                   "SLEEP=%s", arg_verb,
-                   NULL);
-
-        r = write_state(&f, states);
-        if (r < 0)
-                return r;
-
-        log_struct(LOG_INFO,
-                   LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP),
-                   LOG_MESSAGE("System resumed."),
-                   "SLEEP=%s", arg_verb,
-                   NULL);
-
-        arguments[1] = (char*) "post";
-        execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
-
-        return r;
-}
-
-int shutdown_or_sleep(HandleAction action) {
-        switch (action) {
-        case HANDLE_POWEROFF:
-                return run_helper(HALT);
-        case HANDLE_REBOOT:
-                return run_helper(REBOOT);
-        case HANDLE_HALT:
-                return run_helper(HALT);
-        case HANDLE_KEXEC:
-                return run_helper(KEXEC);
-        case HANDLE_SUSPEND:
-                return do_sleep("suspend");
-        case HANDLE_HIBERNATE:
-                return do_sleep("hibernate");
-        case HANDLE_HYBRID_SLEEP:
-                return do_sleep("hybrid-sleep");
-        default:
-                return -EINVAL;
-        }
-}
-
 static const char* const handle_action_table[_HANDLE_ACTION_MAX] = {
         [HANDLE_IGNORE] = "ignore",
         [HANDLE_POWEROFF] = "poweroff",
 static const char* const handle_action_table[_HANDLE_ACTION_MAX] = {
         [HANDLE_IGNORE] = "ignore",
         [HANDLE_POWEROFF] = "poweroff",
index d5af8dcdc91701b53e1c2fe30ea703b6137c42d4..440c32aa2cd2284e042f41bb2e834e0a2af0d241 100644 (file)
@@ -312,7 +312,7 @@ int manager_process_button_device(Manager *m, struct udev_device *d) {
 }
 
 int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
 }
 
 int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
-        _cleanup_free_ char *session_name = NULL;
+        _cleanup_free_ char *unit = NULL;
         Session *s;
         int r;
 
         Session *s;
         int r;
 
@@ -322,11 +322,11 @@ int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
         if (pid < 1)
                 return -EINVAL;
 
         if (pid < 1)
                 return -EINVAL;
 
-        r = cg_pid_get_session(pid, &session_name);
+        r = cg_pid_get_unit(pid, &unit);
         if (r < 0)
                 return 0;
 
         if (r < 0)
                 return 0;
 
-        s = hashmap_get(m->sessions, session_name);
+        s = hashmap_get(m->session_units, unit);
         if (!s)
                 return 0;
 
         if (!s)
                 return 0;
 
@@ -335,17 +335,25 @@ int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
 }
 
 int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
 }
 
 int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
-        Session *s;
+        _cleanup_free_ char *unit = NULL;
+        User *u;
         int r;
 
         assert(m);
         assert(user);
 
         int r;
 
         assert(m);
         assert(user);
 
-        r = manager_get_session_by_pid (m, pid, &s);
-        if (r <= 0)
-                return r;
+        if (pid < 1)
+                return -EINVAL;
+
+        r = cg_pid_get_slice(pid, &unit);
+        if (r < 0)
+                return 0;
+
+        u = hashmap_get(m->user_units, unit);
+        if (!u)
+                return 0;
 
 
-        *user = s->user;
+        *user = u;
         return 1;
 }
 
         return 1;
 }
 
@@ -429,6 +437,46 @@ static int vt_is_busy(unsigned int vtnr) {
         return r;
 }
 
         return r;
 }
 
+int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)];
+        int r;
+
+        assert(m);
+        assert(vtnr >= 1);
+
+        if (vtnr > m->n_autovts &&
+            vtnr != m->reserve_vt)
+                return 0;
+
+        if (vtnr != m->reserve_vt) {
+                /* If this is the reserved TTY, we'll start the getty
+                 * on it in any case, but otherwise only if it is not
+                 * busy. */
+
+                r = vt_is_busy(vtnr);
+                if (r < 0)
+                        return r;
+                else if (r > 0)
+                        return -EBUSY;
+        }
+
+        snprintf(name, sizeof(name), "autovt@tty%u.service", vtnr);
+        r = sd_bus_call_method(
+                        m->bus,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "StartUnit",
+                        &error,
+                        NULL,
+                        "ss", name, "fail");
+        if (r < 0)
+                log_error("Failed to start %s: %s", name, bus_error_message(&error, r));
+
+        return r;
+}
+
 bool manager_is_docked(Manager *m) {
         Iterator i;
         Button *b;
 bool manager_is_docked(Manager *m) {
         Iterator i;
         Button *b;
index c5ac94360d2782609bd3b2237b26364817d4d115..1f5cf865b1440824cd5680053ca4a9c99efe487f 100644 (file)
 #include "bus-common-errors.h"
 #include "udev-util.h"
 #include "selinux-util.h"
 #include "bus-common-errors.h"
 #include "udev-util.h"
 #include "selinux-util.h"
+#include "efivars.h"
 #include "logind.h"
 #include "logind.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "utmp-wtmp.h"
 
 int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) {
         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
 
 int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) {
         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
@@ -209,16 +214,42 @@ static int property_get_preparing(
         return sd_bus_message_append(reply, "b", b);
 }
 
         return sd_bus_message_append(reply, "b", b);
 }
 
+static int property_get_scheduled_shutdown(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Manager *m = userdata;
+        int r;
+
+        assert(bus);
+        assert(reply);
+        assert(m);
+
+        r = sd_bus_message_open_container(reply, 'r', "st");
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(reply, "st", m->scheduled_shutdown_type, m->scheduled_shutdown_timeout);
+        if (r < 0)
+                return r;
+
+        return sd_bus_message_close_container(reply);
+}
+
 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction);
 
 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction);
 
-static int method_get_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_free_ char *p = NULL;
         Manager *m = userdata;
         const char *name;
         Session *session;
         int r;
 
         _cleanup_free_ char *p = NULL;
         Manager *m = userdata;
         const char *name;
         Session *session;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -237,14 +268,13 @@ static int method_get_session(sd_bus *bus, sd_bus_message *message, void *userda
         return sd_bus_reply_method_return(message, "o", p);
 }
 
         return sd_bus_reply_method_return(message, "o", p);
 }
 
-static int method_get_session_by_pid(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_session_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_free_ char *p = NULL;
         Session *session = NULL;
         Manager *m = userdata;
         pid_t pid;
         int r;
 
         _cleanup_free_ char *p = NULL;
         Session *session = NULL;
         Manager *m = userdata;
         pid_t pid;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -274,14 +304,13 @@ static int method_get_session_by_pid(sd_bus *bus, sd_bus_message *message, void
         return sd_bus_reply_method_return(message, "o", p);
 }
 
         return sd_bus_reply_method_return(message, "o", p);
 }
 
-static int method_get_user(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_free_ char *p = NULL;
         Manager *m = userdata;
         uint32_t uid;
         User *user;
         int r;
 
         _cleanup_free_ char *p = NULL;
         Manager *m = userdata;
         uint32_t uid;
         User *user;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -300,14 +329,13 @@ static int method_get_user(sd_bus *bus, sd_bus_message *message, void *userdata,
         return sd_bus_reply_method_return(message, "o", p);
 }
 
         return sd_bus_reply_method_return(message, "o", p);
 }
 
-static int method_get_user_by_pid(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_user_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_free_ char *p = NULL;
         Manager *m = userdata;
         User *user = NULL;
         pid_t pid;
         int r;
 
         _cleanup_free_ char *p = NULL;
         Manager *m = userdata;
         User *user = NULL;
         pid_t pid;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -336,14 +364,13 @@ static int method_get_user_by_pid(sd_bus *bus, sd_bus_message *message, void *us
         return sd_bus_reply_method_return(message, "o", p);
 }
 
         return sd_bus_reply_method_return(message, "o", p);
 }
 
-static int method_get_seat(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_free_ char *p = NULL;
         Manager *m = userdata;
         const char *name;
         Seat *seat;
         int r;
 
         _cleanup_free_ char *p = NULL;
         Manager *m = userdata;
         const char *name;
         Seat *seat;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -362,14 +389,13 @@ static int method_get_seat(sd_bus *bus, sd_bus_message *message, void *userdata,
         return sd_bus_reply_method_return(message, "o", p);
 }
 
         return sd_bus_reply_method_return(message, "o", p);
 }
 
-static int method_list_sessions(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_list_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         Manager *m = userdata;
         Session *session;
         Iterator i;
         int r;
 
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         Manager *m = userdata;
         Session *session;
         Iterator i;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -402,17 +428,16 @@ static int method_list_sessions(sd_bus *bus, sd_bus_message *message, void *user
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        return sd_bus_send(bus, reply, NULL);
+        return sd_bus_send(NULL, reply, NULL);
 }
 
 }
 
-static int method_list_users(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_list_users(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         Manager *m = userdata;
         User *user;
         Iterator i;
         int r;
 
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         Manager *m = userdata;
         User *user;
         Iterator i;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -443,17 +468,16 @@ static int method_list_users(sd_bus *bus, sd_bus_message *message, void *userdat
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        return sd_bus_send(bus, reply, NULL);
+        return sd_bus_send(NULL, reply, NULL);
 }
 
 }
 
-static int method_list_seats(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_list_seats(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         Manager *m = userdata;
         Seat *seat;
         Iterator i;
         int r;
 
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         Manager *m = userdata;
         Seat *seat;
         Iterator i;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -481,16 +505,19 @@ static int method_list_seats(sd_bus *bus, sd_bus_message *message, void *userdat
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        return sd_bus_send(bus, reply, NULL);
+        return sd_bus_send(NULL, reply, NULL);
 }
 
 }
 
-static int method_list_inhibitors(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         Manager *m = userdata;
         Inhibitor *inhibitor;
         Iterator i;
         int r;
 
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         Manager *m = userdata;
         Inhibitor *inhibitor;
         Iterator i;
         int r;
 
+        assert(message);
+        assert(m);
+
         r = sd_bus_message_new_method_return(message, &reply);
         if (r < 0)
                 return r;
         r = sd_bus_message_new_method_return(message, &reply);
         if (r < 0)
                 return r;
@@ -516,10 +543,10 @@ static int method_list_inhibitors(sd_bus *bus, sd_bus_message *message, void *us
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        return sd_bus_send(bus, reply, NULL);
+        return sd_bus_send(NULL, reply, NULL);
 }
 
 }
 
-static int method_create_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_create_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop;
         uint32_t uid, leader, audit_id = 0;
         _cleanup_free_ char *id = NULL;
         const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop;
         uint32_t uid, leader, audit_id = 0;
         _cleanup_free_ char *id = NULL;
@@ -533,7 +560,6 @@ static int method_create_session(sd_bus *bus, sd_bus_message *message, void *use
         SessionClass c;
         int r;
 
         SessionClass c;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -793,15 +819,9 @@ static int method_create_session(sd_bus *bus, sd_bus_message *message, void *use
 
         session->create_message = sd_bus_message_ref(message);
 
 
         session->create_message = sd_bus_message_ref(message);
 
-        /* Here upstream systemd starts cgroups and the user systemd,
-           and arranges to reply asynchronously.  We reply
-           directly.  */
-
-        r = session_send_create_reply(session, NULL);
-        if (r < 0)
-                goto fail;
-
-        session_save(session);
+        /* Now, let's wait until the slice unit and stuff got
+         * created. We send the reply back from
+         * session_send_create_reply(). */
 
         return 1;
 
 
         return 1;
 
@@ -815,13 +835,12 @@ fail:
         return r;
 }
 
         return r;
 }
 
-static int method_release_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_release_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
         Session *session;
         const char *name;
         int r;
 
         Manager *m = userdata;
         Session *session;
         const char *name;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -837,18 +856,15 @@ static int method_release_session(sd_bus *bus, sd_bus_message *message, void *us
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        session_add_to_gc_queue(session);
-
         return sd_bus_reply_method_return(message, NULL);
 }
 
         return sd_bus_reply_method_return(message, NULL);
 }
 
-static int method_activate_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
         Session *session;
         const char *name;
         int r;
 
         Manager *m = userdata;
         Session *session;
         const char *name;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -860,17 +876,16 @@ static int method_activate_session(sd_bus *bus, sd_bus_message *message, void *u
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        return bus_session_method_activate(bus, message, session, error);
+        return bus_session_method_activate(message, session, error);
 }
 
 }
 
-static int method_activate_session_on_seat(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_activate_session_on_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         const char *session_name, *seat_name;
         Manager *m = userdata;
         Session *session;
         Seat *seat;
         int r;
 
         const char *session_name, *seat_name;
         Manager *m = userdata;
         Session *session;
         Seat *seat;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -899,13 +914,12 @@ static int method_activate_session_on_seat(sd_bus *bus, sd_bus_message *message,
         return sd_bus_reply_method_return(message, NULL);
 }
 
         return sd_bus_reply_method_return(message, NULL);
 }
 
-static int method_lock_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_lock_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
         Session *session;
         const char *name;
         int r;
 
         Manager *m = userdata;
         Session *session;
         const char *name;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -917,14 +931,13 @@ static int method_lock_session(sd_bus *bus, sd_bus_message *message, void *userd
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        return bus_session_method_lock(bus, message, session, error);
+        return bus_session_method_lock(message, session, error);
 }
 
 }
 
-static int method_lock_sessions(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_lock_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
         int r;
 
         Manager *m = userdata;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -948,13 +961,12 @@ static int method_lock_sessions(sd_bus *bus, sd_bus_message *message, void *user
         return sd_bus_reply_method_return(message, NULL);
 }
 
         return sd_bus_reply_method_return(message, NULL);
 }
 
-static int method_kill_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_kill_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         const char *name;
         Manager *m = userdata;
         Session *session;
         int r;
 
         const char *name;
         Manager *m = userdata;
         Session *session;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -966,16 +978,15 @@ static int method_kill_session(sd_bus *bus, sd_bus_message *message, void *userd
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        return bus_session_method_kill(bus, message, session, error);
+        return bus_session_method_kill(message, session, error);
 }
 
 }
 
-static int method_kill_user(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_kill_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
         uint32_t uid;
         User *user;
         int r;
 
         Manager *m = userdata;
         uint32_t uid;
         User *user;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -987,16 +998,15 @@ static int method_kill_user(sd_bus *bus, sd_bus_message *message, void *userdata
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        return bus_user_method_kill(bus, message, user, error);
+        return bus_user_method_kill(message, user, error);
 }
 
 }
 
-static int method_terminate_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_terminate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
         const char *name;
         Session *session;
         int r;
 
         Manager *m = userdata;
         const char *name;
         Session *session;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -1008,16 +1018,15 @@ static int method_terminate_session(sd_bus *bus, sd_bus_message *message, void *
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        return bus_session_method_terminate(bus, message, session, error);
+        return bus_session_method_terminate(message, session, error);
 }
 
 }
 
-static int method_terminate_user(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_terminate_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
         uint32_t uid;
         User *user;
         int r;
 
         Manager *m = userdata;
         uint32_t uid;
         User *user;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -1029,16 +1038,15 @@ static int method_terminate_user(sd_bus *bus, sd_bus_message *message, void *use
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        return bus_user_method_terminate(bus, message, user, error);
+        return bus_user_method_terminate(message, user, error);
 }
 
 }
 
-static int method_terminate_seat(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_terminate_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
         const char *name;
         Seat *seat;
         int r;
 
         Manager *m = userdata;
         const char *name;
         Seat *seat;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -1050,10 +1058,10 @@ static int method_terminate_seat(sd_bus *bus, sd_bus_message *message, void *use
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        return bus_seat_method_terminate(bus, message, seat, error);
+        return bus_seat_method_terminate(message, seat, error);
 }
 
 }
 
-static int method_set_user_linger(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_free_ char *cc = NULL;
         Manager *m = userdata;
         int b, r;
         _cleanup_free_ char *cc = NULL;
         Manager *m = userdata;
         int b, r;
@@ -1062,7 +1070,6 @@ static int method_set_user_linger(sd_bus *bus, sd_bus_message *message, void *us
         uint32_t uid;
         int interactive;
 
         uint32_t uid;
         int interactive;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -1242,12 +1249,11 @@ static int flush_devices(Manager *m) {
         return trigger_device(m, NULL);
 }
 
         return trigger_device(m, NULL);
 }
 
-static int method_attach_device(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_attach_device(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         const char *sysfs, *seat;
         Manager *m = userdata;
         int interactive, r;
 
         const char *sysfs, *seat;
         Manager *m = userdata;
         int interactive, r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -1281,11 +1287,10 @@ static int method_attach_device(sd_bus *bus, sd_bus_message *message, void *user
         return sd_bus_reply_method_return(message, NULL);
 }
 
         return sd_bus_reply_method_return(message, NULL);
 }
 
-static int method_flush_devices(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
         int interactive, r;
 
         Manager *m = userdata;
         int interactive, r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -1335,36 +1340,31 @@ static int have_multiple_sessions(
 static int bus_manager_log_shutdown(
                 Manager *m,
                 InhibitWhat w,
 static int bus_manager_log_shutdown(
                 Manager *m,
                 InhibitWhat w,
-                HandleAction action) {
+                const char *unit_name) {
 
         const char *p, *q;
 
         assert(m);
 
         const char *p, *q;
 
         assert(m);
+        assert(unit_name);
 
         if (w != INHIBIT_SHUTDOWN)
                 return 0;
 
 
         if (w != INHIBIT_SHUTDOWN)
                 return 0;
 
-        switch (action) {
-        case HANDLE_POWEROFF:
+        if (streq(unit_name, SPECIAL_POWEROFF_TARGET)) {
                 p = "MESSAGE=System is powering down.";
                 q = "SHUTDOWN=power-off";
                 p = "MESSAGE=System is powering down.";
                 q = "SHUTDOWN=power-off";
-                break;
-        case HANDLE_HALT:
+        } else if (streq(unit_name, SPECIAL_HALT_TARGET)) {
                 p = "MESSAGE=System is halting.";
                 q = "SHUTDOWN=halt";
                 p = "MESSAGE=System is halting.";
                 q = "SHUTDOWN=halt";
-                break;
-        case HANDLE_REBOOT:
+        } else if (streq(unit_name, SPECIAL_REBOOT_TARGET)) {
                 p = "MESSAGE=System is rebooting.";
                 q = "SHUTDOWN=reboot";
                 p = "MESSAGE=System is rebooting.";
                 q = "SHUTDOWN=reboot";
-                break;
-        case HANDLE_KEXEC:
+        } else if (streq(unit_name, SPECIAL_KEXEC_TARGET)) {
                 p = "MESSAGE=System is rebooting with kexec.";
                 q = "SHUTDOWN=kexec";
                 p = "MESSAGE=System is rebooting with kexec.";
                 q = "SHUTDOWN=kexec";
-                break;
-        default:
+        } else {
                 p = "MESSAGE=System is shutting down.";
                 q = NULL;
                 p = "MESSAGE=System is shutting down.";
                 q = NULL;
-                break;
         }
 
         return log_struct(LOG_NOTICE,
         }
 
         return log_struct(LOG_NOTICE,
@@ -1422,39 +1422,122 @@ int manager_set_lid_switch_ignore(Manager *m, usec_t until) {
 static int execute_shutdown_or_sleep(
                 Manager *m,
                 InhibitWhat w,
 static int execute_shutdown_or_sleep(
                 Manager *m,
                 InhibitWhat w,
-                HandleAction action,
+                const char *unit_name,
                 sd_bus_error *error) {
                 sd_bus_error *error) {
+
+        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        const char *p;
+        char *c;
         int r;
 
         assert(m);
         assert(w >= 0);
         assert(w < _INHIBIT_WHAT_MAX);
         int r;
 
         assert(m);
         assert(w >= 0);
         assert(w < _INHIBIT_WHAT_MAX);
+        assert(unit_name);
 
 
-        bus_manager_log_shutdown(m, w, action);
+        bus_manager_log_shutdown(m, w, unit_name);
 
 
-        /* FIXME: here do the thing.  */
+        r = sd_bus_call_method(
+                        m->bus,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "StartUnit",
+                        error,
+                        &reply,
+                        "ss", unit_name, "replace-irreversibly");
+        if (r < 0)
+                return r;
 
 
-        r = shutdown_or_sleep(action);
+        r = sd_bus_message_read(reply, "o", &p);
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        /* Make sure the lid switch is ignored for a while (?) */
+        c = strdup(p);
+        if (!c)
+                return -ENOMEM;
+
+        m->action_unit = unit_name;
+        free(m->action_job);
+        m->action_job = c;
+        m->action_what = w;
+
+        /* Make sure the lid switch is ignored for a while */
         manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + m->holdoff_timeout_usec);
 
         return 0;
 }
 
         manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + m->holdoff_timeout_usec);
 
         return 0;
 }
 
+static int manager_inhibit_timeout_handler(
+                        sd_event_source *s,
+                        uint64_t usec,
+                        void *userdata) {
+
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        Inhibitor *offending = NULL;
+        Manager *manager = userdata;
+        int r;
+
+        assert(manager);
+        assert(manager->inhibit_timeout_source == s);
+
+        if (manager->action_what == 0 || manager->action_job)
+                return 0;
+
+        if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
+                _cleanup_free_ char *comm = NULL, *u = NULL;
+
+                (void) get_process_comm(offending->pid, &comm);
+                u = uid_to_name(offending->uid);
+
+                log_notice("Delay lock is active (UID "UID_FMT"/%s, PID "PID_FMT"/%s) but inhibitor timeout is reached.",
+                           offending->uid, strna(u),
+                           offending->pid, strna(comm));
+        }
+
+        /* Actually do the operation */
+        r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error);
+        if (r < 0) {
+                log_warning("Failed to send delayed message: %s", bus_error_message(&error, r));
+
+                manager->action_unit = NULL;
+                manager->action_what = 0;
+        }
+
+        return 0;
+}
+
 static int delay_shutdown_or_sleep(
                 Manager *m,
                 InhibitWhat w,
 static int delay_shutdown_or_sleep(
                 Manager *m,
                 InhibitWhat w,
-                HandleAction action) {
+                const char *unit_name) {
+
+        int r;
+        usec_t timeout_val;
 
         assert(m);
         assert(w >= 0);
         assert(w < _INHIBIT_WHAT_MAX);
 
         assert(m);
         assert(w >= 0);
         assert(w < _INHIBIT_WHAT_MAX);
+        assert(unit_name);
+
+        timeout_val = now(CLOCK_MONOTONIC) + m->inhibit_delay_max;
+
+        if (m->inhibit_timeout_source) {
+                r = sd_event_source_set_time(m->inhibit_timeout_source, timeout_val);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_source_set_time() failed: %m");
+
+                r = sd_event_source_set_enabled(m->inhibit_timeout_source, SD_EVENT_ONESHOT);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_source_set_enabled() failed: %m");
+        } else {
+                r = sd_event_add_time(m->event, &m->inhibit_timeout_source, CLOCK_MONOTONIC,
+                                      timeout_val, 0, manager_inhibit_timeout_handler, m);
+                if (r < 0)
+                        return r;
+        }
 
 
-        m->action_timestamp = now(CLOCK_MONOTONIC);
-        m->pending_action = action;
+        m->action_unit = unit_name;
         m->action_what = w;
 
         return 0;
         m->action_what = w;
 
         return 0;
@@ -1484,7 +1567,7 @@ static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) {
 
 int bus_manager_shutdown_or_sleep_now_or_later(
                 Manager *m,
 
 int bus_manager_shutdown_or_sleep_now_or_later(
                 Manager *m,
-                HandleAction action,
+                const char *unit_name,
                 InhibitWhat w,
                 sd_bus_error *error) {
 
                 InhibitWhat w,
                 sd_bus_error *error) {
 
@@ -1492,8 +1575,10 @@ int bus_manager_shutdown_or_sleep_now_or_later(
         int r;
 
         assert(m);
         int r;
 
         assert(m);
+        assert(unit_name);
         assert(w >= 0);
         assert(w <= _INHIBIT_WHAT_MAX);
         assert(w >= 0);
         assert(w <= _INHIBIT_WHAT_MAX);
+        assert(!m->action_job);
 
         /* Tell everybody to prepare for shutdown/sleep */
         send_prepare_for(m, w, true);
 
         /* Tell everybody to prepare for shutdown/sleep */
         send_prepare_for(m, w, true);
@@ -1505,57 +1590,34 @@ int bus_manager_shutdown_or_sleep_now_or_later(
         if (delayed)
                 /* Shutdown is delayed, keep in mind what we
                  * want to do, and start a timeout */
         if (delayed)
                 /* Shutdown is delayed, keep in mind what we
                  * want to do, and start a timeout */
-                r = delay_shutdown_or_sleep(m, w, action);
+                r = delay_shutdown_or_sleep(m, w, unit_name);
         else
                 /* Shutdown is not delayed, execute it
                  * immediately */
         else
                 /* Shutdown is not delayed, execute it
                  * immediately */
-                r = execute_shutdown_or_sleep(m, w, action, error);
+                r = execute_shutdown_or_sleep(m, w, unit_name, error);
 
         return r;
 }
 
 
         return r;
 }
 
-static int method_do_shutdown_or_sleep(
+static int verify_shutdown_creds(
                 Manager *m,
                 sd_bus_message *message,
                 Manager *m,
                 sd_bus_message *message,
-                HandleAction sleep_action,
                 InhibitWhat w,
                 InhibitWhat w,
+                bool interactive,
                 const char *action,
                 const char *action_multiple_sessions,
                 const char *action_ignore_inhibit,
                 const char *action,
                 const char *action_multiple_sessions,
                 const char *action_ignore_inhibit,
-                const char *sleep_verb,
-                sd_bus_message_handler_t method,
                 sd_bus_error *error) {
 
         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
         bool multiple_sessions, blocked;
                 sd_bus_error *error) {
 
         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
         bool multiple_sessions, blocked;
-        int interactive, r;
         uid_t uid;
         uid_t uid;
+        int r;
 
         assert(m);
         assert(message);
         assert(w >= 0);
         assert(w <= _INHIBIT_WHAT_MAX);
 
         assert(m);
         assert(message);
         assert(w >= 0);
         assert(w <= _INHIBIT_WHAT_MAX);
-        assert(action);
-        assert(action_multiple_sessions);
-        assert(action_ignore_inhibit);
-        assert(method);
-
-        r = sd_bus_message_read(message, "b", &interactive);
-        if (r < 0)
-                return r;
-
-        /* Don't allow multiple jobs being executed at the same time */
-        if (m->action_what)
-                return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress");
-
-        if (sleep_verb) {
-                r = can_sleep(sleep_verb);
-                if (r < 0)
-                        return r;
-
-                if (r == 0)
-                        return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported");
-        }
 
         r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
         if (r < 0)
 
         r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
         if (r < 0)
@@ -1572,7 +1634,7 @@ static int method_do_shutdown_or_sleep(
         multiple_sessions = r > 0;
         blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
 
         multiple_sessions = r > 0;
         blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
 
-        if (multiple_sessions) {
+        if (multiple_sessions && action_multiple_sessions) {
                 r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, interactive, UID_INVALID, &m->polkit_registry, error);
                 if (r < 0)
                         return r;
                 r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, interactive, UID_INVALID, &m->polkit_registry, error);
                 if (r < 0)
                         return r;
@@ -1580,7 +1642,7 @@ static int method_do_shutdown_or_sleep(
                         return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
         }
 
                         return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
         }
 
-        if (blocked) {
+        if (blocked && action_ignore_inhibit) {
                 r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, interactive, UID_INVALID, &m->polkit_registry, error);
                 if (r < 0)
                         return r;
                 r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, interactive, UID_INVALID, &m->polkit_registry, error);
                 if (r < 0)
                         return r;
@@ -1588,7 +1650,7 @@ static int method_do_shutdown_or_sleep(
                         return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
         }
 
                         return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
         }
 
-        if (!multiple_sessions && !blocked) {
+        if (!multiple_sessions && !blocked && action) {
                 r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, interactive, UID_INVALID, &m->polkit_registry, error);
                 if (r < 0)
                         return r;
                 r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, interactive, UID_INVALID, &m->polkit_registry, error);
                 if (r < 0)
                         return r;
@@ -1596,85 +1658,356 @@ static int method_do_shutdown_or_sleep(
                         return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
         }
 
                         return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
         }
 
-        r = bus_manager_shutdown_or_sleep_now_or_later(m, sleep_action, w, error);
+        return 0;
+}
+
+static int method_do_shutdown_or_sleep(
+                Manager *m,
+                sd_bus_message *message,
+                const char *unit_name,
+                InhibitWhat w,
+                const char *action,
+                const char *action_multiple_sessions,
+                const char *action_ignore_inhibit,
+                const char *sleep_verb,
+                sd_bus_error *error) {
+
+        int interactive, r;
+
+        assert(m);
+        assert(message);
+        assert(unit_name);
+        assert(w >= 0);
+        assert(w <= _INHIBIT_WHAT_MAX);
+
+        r = sd_bus_message_read(message, "b", &interactive);
+        if (r < 0)
+                return r;
+
+        /* Don't allow multiple jobs being executed at the same time */
+        if (m->action_what)
+                return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress");
+
+        if (sleep_verb) {
+                r = can_sleep(sleep_verb);
+                if (r < 0)
+                        return r;
+
+                if (r == 0)
+                        return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported");
+        }
+
+        r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions,
+                                  action_ignore_inhibit, error);
+        if (r != 0)
+                return r;
+
+        r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error);
         if (r < 0)
                 return r;
 
         return sd_bus_reply_method_return(message, NULL);
 }
 
         if (r < 0)
                 return r;
 
         return sd_bus_reply_method_return(message, NULL);
 }
 
-static int method_poweroff(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
         return method_do_shutdown_or_sleep(
                         m, message,
         Manager *m = userdata;
 
         return method_do_shutdown_or_sleep(
                         m, message,
-                        HANDLE_POWEROFF,
+                        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,
                         INHIBIT_SHUTDOWN,
                         "org.freedesktop.login1.power-off",
                         "org.freedesktop.login1.power-off-multiple-sessions",
                         "org.freedesktop.login1.power-off-ignore-inhibit",
                         NULL,
-                        method_poweroff,
                         error);
 }
 
                         error);
 }
 
-static int method_reboot(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
         return method_do_shutdown_or_sleep(
                         m, message,
         Manager *m = userdata;
 
         return method_do_shutdown_or_sleep(
                         m, message,
-                        HANDLE_REBOOT,
+                        SPECIAL_REBOOT_TARGET,
                         INHIBIT_SHUTDOWN,
                         "org.freedesktop.login1.reboot",
                         "org.freedesktop.login1.reboot-multiple-sessions",
                         "org.freedesktop.login1.reboot-ignore-inhibit",
                         NULL,
                         INHIBIT_SHUTDOWN,
                         "org.freedesktop.login1.reboot",
                         "org.freedesktop.login1.reboot-multiple-sessions",
                         "org.freedesktop.login1.reboot-ignore-inhibit",
                         NULL,
-                        method_reboot,
                         error);
 }
 
                         error);
 }
 
-static int method_suspend(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
         return method_do_shutdown_or_sleep(
                         m, message,
         Manager *m = userdata;
 
         return method_do_shutdown_or_sleep(
                         m, message,
-                        HANDLE_SUSPEND,
+                        SPECIAL_SUSPEND_TARGET,
                         INHIBIT_SLEEP,
                         "org.freedesktop.login1.suspend",
                         "org.freedesktop.login1.suspend-multiple-sessions",
                         "org.freedesktop.login1.suspend-ignore-inhibit",
                         "suspend",
                         INHIBIT_SLEEP,
                         "org.freedesktop.login1.suspend",
                         "org.freedesktop.login1.suspend-multiple-sessions",
                         "org.freedesktop.login1.suspend-ignore-inhibit",
                         "suspend",
-                        method_suspend,
                         error);
 }
 
                         error);
 }
 
-static int method_hibernate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int nologin_timeout_handler(
+                        sd_event_source *s,
+                        uint64_t usec,
+                        void *userdata) {
+
+        Manager *m = userdata;
+        int r;
+
+        log_info("Creating /run/nologin, blocking further logins...");
+
+        r = write_string_file_atomic("/run/nologin", "System is going down.");
+        if (r < 0)
+                log_error_errno(r, "Failed to create /run/nologin: %m");
+        else
+                m->unlink_nologin = true;
+
+        return 0;
+}
+
+static int update_schedule_file(Manager *m) {
+
+        int r;
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_free_ char *t = NULL, *temp_path = NULL;
+
+        assert(m);
+
+        r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create shutdown subdirectory: %m");
+
+        t = cescape(m->wall_message);
+        if (!t)
+                return log_oom();
+
+        r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
+        if (r < 0)
+                return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m");
+
+        (void) fchmod(fileno(f), 0644);
+
+        fprintf(f,
+                "USEC="USEC_FMT"\n"
+                "WARN_WALL=%i\n"
+                "MODE=%s\n",
+                m->scheduled_shutdown_timeout,
+                m->enable_wall_messages,
+                m->scheduled_shutdown_type);
+
+        if (!isempty(m->wall_message))
+                fprintf(f, "WALL_MESSAGE=%s\n", t);
+
+        (void) fflush_and_check(f);
+
+        if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
+                log_error_errno(errno, "Failed to write information about scheduled shutdowns: %m");
+                r = -errno;
+
+                (void) unlink(temp_path);
+                (void) unlink("/run/systemd/shutdown/scheduled");
+        }
+
+        return r;
+}
+
+static int manager_scheduled_shutdown_handler(
+                        sd_event_source *s,
+                        uint64_t usec,
+                        void *userdata) {
+
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        Manager *m = userdata;
+        const char *target;
+        int r;
+
+        assert(m);
+
+        if (isempty(m->scheduled_shutdown_type))
+                return 0;
+
+        if (streq(m->scheduled_shutdown_type, "halt"))
+                target = SPECIAL_HALT_TARGET;
+        else if (streq(m->scheduled_shutdown_type, "poweroff"))
+                target = SPECIAL_POWEROFF_TARGET;
+        else
+                target = SPECIAL_REBOOT_TARGET;
+
+        r = execute_shutdown_or_sleep(m, 0, target, &error);
+        if (r < 0)
+                return log_error_errno(r, "Unable to execute transition to %s: %m", target);
+
+        return 0;
+}
+
+static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = userdata;
+        _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+        const char *action_multiple_sessions = NULL;
+        const char *action_ignore_inhibit = NULL;
+        const char *action = NULL;
+        uint64_t elapse;
+        char *type;
+        int r;
+
+        assert(m);
+        assert(message);
+
+        r = sd_bus_message_read(message, "st", &type, &elapse);
+        if (r < 0)
+                return r;
+
+        if (streq(type, "reboot")) {
+                action = "org.freedesktop.login1.reboot";
+                action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions";
+                action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit";
+        } else if (streq(type, "halt")) {
+                action = "org.freedesktop.login1.halt";
+                action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions";
+                action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit";
+        } else if (streq(type, "poweroff")) {
+                action = "org.freedesktop.login1.poweroff";
+                action_multiple_sessions = "org.freedesktop.login1.poweroff-multiple-sessions";
+                action_ignore_inhibit = "org.freedesktop.login1.poweroff-ignore-inhibit";
+        } else
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
+
+        r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, false,
+                                  action, action_multiple_sessions, action_ignore_inhibit, error);
+        if (r != 0)
+                return r;
+
+        if (m->scheduled_shutdown_timeout_source) {
+                r = sd_event_source_set_time(m->scheduled_shutdown_timeout_source, elapse);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_source_set_time() failed: %m");
+
+                r = sd_event_source_set_enabled(m->scheduled_shutdown_timeout_source, SD_EVENT_ONESHOT);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_source_set_enabled() failed: %m");
+        } else {
+                r = sd_event_add_time(m->event, &m->scheduled_shutdown_timeout_source,
+                                      CLOCK_REALTIME, elapse, 0, manager_scheduled_shutdown_handler, m);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_add_time() failed: %m");
+        }
+
+        r = free_and_strdup(&m->scheduled_shutdown_type, type);
+        if (r < 0) {
+                m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+                return log_oom();
+        }
+
+        if (m->nologin_timeout_source) {
+                r = sd_event_source_set_time(m->nologin_timeout_source, elapse);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_source_set_time() failed: %m");
+
+                r = sd_event_source_set_enabled(m->nologin_timeout_source, SD_EVENT_ONESHOT);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_source_set_enabled() failed: %m");
+        } else {
+                r = sd_event_add_time(m->event, &m->nologin_timeout_source,
+                                      CLOCK_REALTIME, elapse - 5 * USEC_PER_MINUTE, 0, nologin_timeout_handler, m);
+                if (r < 0)
+                        return log_error_errno(r, "sd_event_add_time() failed: %m");
+        }
+
+        m->scheduled_shutdown_timeout = elapse;
+
+        r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
+        if (r >= 0) {
+                const char *tty;
+
+                (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid);
+                (void) sd_bus_creds_get_tty(creds, &tty);
+
+                r = free_and_strdup(&m->scheduled_shutdown_tty, tty);
+                if (r < 0) {
+                        m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+                        return log_oom();
+                }
+        }
+
+        r = manager_setup_wall_message_timer(m);
+        if (r < 0)
+                return r;
+
+        if (!isempty(type)) {
+                r = update_schedule_file(m);
+                if (r < 0)
+                        return r;
+        } else
+                (void) unlink("/run/systemd/shutdown/scheduled");
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = userdata;
+        bool cancelled;
+
+        assert(m);
+        assert(message);
+
+        cancelled = m->scheduled_shutdown_type != NULL;
+
+        m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+        m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source);
+        m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source);
+        free(m->scheduled_shutdown_type);
+        m->scheduled_shutdown_type = NULL;
+        m->scheduled_shutdown_timeout = 0;
+
+        if (cancelled) {
+                _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+                const char *tty = NULL;
+                uid_t uid = 0;
+                int r;
+
+                r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
+                if (r >= 0) {
+                        (void) sd_bus_creds_get_uid(creds, &uid);
+                        (void) sd_bus_creds_get_tty(creds, &tty);
+                }
+
+                utmp_wall("The system shutdown has been cancelled",
+                          lookup_uid(uid), tty, logind_wall_tty_filter, m);
+        }
+
+        return sd_bus_reply_method_return(message, "b", cancelled);
+}
+
+static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
         return method_do_shutdown_or_sleep(
                         m, message,
         Manager *m = userdata;
 
         return method_do_shutdown_or_sleep(
                         m, message,
-                        HANDLE_HIBERNATE,
+                        SPECIAL_HIBERNATE_TARGET,
                         INHIBIT_SLEEP,
                         "org.freedesktop.login1.hibernate",
                         "org.freedesktop.login1.hibernate-multiple-sessions",
                         "org.freedesktop.login1.hibernate-ignore-inhibit",
                         "hibernate",
                         INHIBIT_SLEEP,
                         "org.freedesktop.login1.hibernate",
                         "org.freedesktop.login1.hibernate-multiple-sessions",
                         "org.freedesktop.login1.hibernate-ignore-inhibit",
                         "hibernate",
-                        method_hibernate,
                         error);
 }
 
                         error);
 }
 
-static int method_hybrid_sleep(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
         return method_do_shutdown_or_sleep(
                         m, message,
         Manager *m = userdata;
 
         return method_do_shutdown_or_sleep(
                         m, message,
-                        HANDLE_HYBRID_SLEEP,
+                        SPECIAL_HYBRID_SLEEP_TARGET,
                         INHIBIT_SLEEP,
                         "org.freedesktop.login1.hibernate",
                         "org.freedesktop.login1.hibernate-multiple-sessions",
                         "org.freedesktop.login1.hibernate-ignore-inhibit",
                         "hybrid-sleep",
                         INHIBIT_SLEEP,
                         "org.freedesktop.login1.hibernate",
                         "org.freedesktop.login1.hibernate-multiple-sessions",
                         "org.freedesktop.login1.hibernate-ignore-inhibit",
                         "hybrid-sleep",
-                        method_hybrid_sleep,
                         error);
 }
 
                         error);
 }
 
@@ -1726,7 +2059,7 @@ static int method_can_shutdown_or_sleep(
         blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
 
         if (multiple_sessions) {
         blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
 
         if (multiple_sessions) {
-                r = bus_verify_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, false, UID_INVALID, &challenge, error);
+                r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, UID_INVALID, &challenge, error);
                 if (r < 0)
                         return r;
 
                 if (r < 0)
                         return r;
 
@@ -1739,7 +2072,7 @@ static int method_can_shutdown_or_sleep(
         }
 
         if (blocked) {
         }
 
         if (blocked) {
-                r = bus_verify_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, false, UID_INVALID, &challenge, error);
+                r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, UID_INVALID, &challenge, error);
                 if (r < 0)
                         return r;
 
                 if (r < 0)
                         return r;
 
@@ -1755,7 +2088,7 @@ static int method_can_shutdown_or_sleep(
                 /* If neither inhibit nor multiple sessions
                  * apply then just check the normal policy */
 
                 /* If neither inhibit nor multiple sessions
                  * apply then just check the normal policy */
 
-                r = bus_verify_polkit(message, CAP_SYS_BOOT, action, false, UID_INVALID, &challenge, error);
+                r = bus_test_polkit(message, CAP_SYS_BOOT, action, UID_INVALID, &challenge, error);
                 if (r < 0)
                         return r;
 
                 if (r < 0)
                         return r;
 
@@ -1770,7 +2103,7 @@ static int method_can_shutdown_or_sleep(
         return sd_bus_reply_method_return(message, "s", result);
 }
 
         return sd_bus_reply_method_return(message, "s", result);
 }
 
-static int method_can_poweroff(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_can_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
         return method_can_shutdown_or_sleep(
         Manager *m = userdata;
 
         return method_can_shutdown_or_sleep(
@@ -1783,7 +2116,7 @@ static int method_can_poweroff(sd_bus *bus, sd_bus_message *message, void *userd
                         error);
 }
 
                         error);
 }
 
-static int method_can_reboot(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_can_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
         return method_can_shutdown_or_sleep(
         Manager *m = userdata;
 
         return method_can_shutdown_or_sleep(
@@ -1796,7 +2129,7 @@ static int method_can_reboot(sd_bus *bus, sd_bus_message *message, void *userdat
                         error);
 }
 
                         error);
 }
 
-static int method_can_suspend(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_can_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
         return method_can_shutdown_or_sleep(
         Manager *m = userdata;
 
         return method_can_shutdown_or_sleep(
@@ -1809,7 +2142,7 @@ static int method_can_suspend(sd_bus *bus, sd_bus_message *message, void *userda
                         error);
 }
 
                         error);
 }
 
-static int method_can_hibernate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_can_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
         return method_can_shutdown_or_sleep(
         Manager *m = userdata;
 
         return method_can_shutdown_or_sleep(
@@ -1822,7 +2155,7 @@ static int method_can_hibernate(sd_bus *bus, sd_bus_message *message, void *user
                         error);
 }
 
                         error);
 }
 
-static int method_can_hybrid_sleep(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
         return method_can_shutdown_or_sleep(
         Manager *m = userdata;
 
         return method_can_shutdown_or_sleep(
@@ -1835,7 +2168,100 @@ static int method_can_hybrid_sleep(sd_bus *bus, sd_bus_message *message, void *u
                         error);
 }
 
                         error);
 }
 
-static int method_inhibit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int property_get_reboot_to_firmware_setup(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+        int r;
+
+        assert(bus);
+        assert(reply);
+        assert(userdata);
+
+        r = efi_get_reboot_to_firmware();
+        if (r < 0 && r != -EOPNOTSUPP)
+                return r;
+
+        return sd_bus_message_append(reply, "b", r > 0);
+}
+
+static int method_set_reboot_to_firmware_setup(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        int b, r;
+        Manager *m = userdata;
+
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_read(message, "b", &b);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_polkit_async(message,
+                                    CAP_SYS_ADMIN,
+                                    "org.freedesktop.login1.set-reboot-to-firmware-setup",
+                                    false,
+                                    UID_INVALID,
+                                    &m->polkit_registry,
+                                    error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+        r = efi_set_reboot_to_firmware(b);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_can_reboot_to_firmware_setup(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        int r;
+        bool challenge;
+        const char *result;
+        Manager *m = userdata;
+
+        assert(message);
+        assert(m);
+
+        r = efi_reboot_to_firmware_supported();
+        if (r == -EOPNOTSUPP)
+                return sd_bus_reply_method_return(message, "s", "na");
+        else if (r < 0)
+                return r;
+
+        r = bus_test_polkit(message,
+                            CAP_SYS_ADMIN,
+                            "org.freedesktop.login1.set-reboot-to-firmware-setup",
+                            UID_INVALID,
+                            &challenge,
+                            error);
+        if (r < 0)
+                return r;
+
+        if (r > 0)
+                result = "yes";
+        else if (challenge)
+                result = "challenge";
+        else
+                result = "no";
+
+        return sd_bus_reply_method_return(message, "s", result);
+}
+
+static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
         const char *who, *why, *what, *mode;
         _cleanup_free_ char *id = NULL;
         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
         const char *who, *why, *what, *mode;
         _cleanup_free_ char *id = NULL;
@@ -1848,7 +2274,6 @@ static int method_inhibit(sd_bus *bus, sd_bus_message *message, void *userdata,
         uid_t uid;
         int r;
 
         uid_t uid;
         int r;
 
-        assert(bus);
         assert(message);
         assert(m);
 
         assert(message);
         assert(m);
 
@@ -1951,9 +2376,14 @@ fail:
 const sd_bus_vtable manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
 const sd_bus_vtable manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
+        SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", NULL, NULL, offsetof(Manager, enable_wall_messages), 0),
+        SD_BUS_WRITABLE_PROPERTY("WallMessage", "s", NULL, NULL, offsetof(Manager, wall_message), 0),
+
+        SD_BUS_PROPERTY("NAutoVTs", "u", NULL, offsetof(Manager, n_autovts), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -1970,6 +2400,7 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_PROPERTY("IdleActionUSec", "t", NULL, offsetof(Manager, idle_action_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("PreparingForShutdown", "b", property_get_preparing, 0, 0),
         SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0),
         SD_BUS_PROPERTY("IdleActionUSec", "t", NULL, offsetof(Manager, idle_action_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("PreparingForShutdown", "b", property_get_preparing, 0, 0),
         SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0),
+        SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0),
 
         SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
 
         SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -1999,6 +2430,8 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -2007,6 +2440,8 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
 
         SD_BUS_SIGNAL("SessionNew", "so", 0),
         SD_BUS_SIGNAL("SessionRemoved", "so", 0),
 
         SD_BUS_SIGNAL("SessionNew", "so", 0),
         SD_BUS_SIGNAL("SessionRemoved", "so", 0),
@@ -2020,56 +2455,228 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_VTABLE_END
 };
 
         SD_BUS_VTABLE_END
 };
 
-int manager_send_changed(Manager *manager, const char *property, ...) {
-        char **l;
+static int session_jobs_reply(Session *s, const char *unit, const char *result) {
+        int r = 0;
 
 
-        assert(manager);
+        assert(s);
+        assert(unit);
 
 
-        l = strv_from_stdarg_alloca(property);
+        if (!s->started)
+                return r;
 
 
-        return sd_bus_emit_properties_changed_strv(
-                        manager->bus,
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        l);
+        if (streq(result, "done"))
+                r = session_send_create_reply(s, NULL);
+        else {
+                _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
+
+                sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
+                r = session_send_create_reply(s, &e);
+        }
+
+        return r;
 }
 
 }
 
-int manager_dispatch_delayed(Manager *manager) {
-        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-        Inhibitor *offending = NULL;
+int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        const char *path, *result, *unit;
+        Manager *m = userdata;
+        Session *session;
+        uint32_t id;
+        User *user;
         int r;
 
         int r;
 
-        assert(manager);
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result);
+        if (r < 0) {
+                bus_log_parse_error(r);
+                return r;
+        }
+
+        if (m->action_job && streq(m->action_job, path)) {
+                log_info("Operation finished.");
 
 
-        if (manager->action_what == 0)
+                /* Tell people that they now may take a lock again */
+                send_prepare_for(m, m->action_what, false);
+
+                free(m->action_job);
+                m->action_job = NULL;
+                m->action_unit = NULL;
+                m->action_what = 0;
                 return 0;
                 return 0;
+        }
 
 
-        /* Continue delay? */
-        if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
-                _cleanup_free_ char *comm = NULL, *u = NULL;
+        session = hashmap_get(m->session_units, unit);
+        if (session) {
 
 
-                get_process_comm(offending->pid, &comm);
-                u = uid_to_name(offending->uid);
+                if (streq_ptr(path, session->scope_job)) {
+                        free(session->scope_job);
+                        session->scope_job = NULL;
+                }
 
 
-                if (manager->action_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC))
-                        return 0;
+                session_jobs_reply(session, unit, result);
 
 
-                log_info("Delay lock is active (UID "UID_FMT"/%s, PID "PID_FMT"/%s) but inhibitor timeout is reached.",
-                         offending->uid, strna(u),
-                         offending->pid, strna(comm));
+                session_save(session);
+                session_add_to_gc_queue(session);
         }
 
         }
 
-        /* Actually do the operation */
-        r = execute_shutdown_or_sleep(manager, manager->action_what, manager->pending_action, &error);
+        user = hashmap_get(m->user_units, unit);
+        if (user) {
+
+                if (streq_ptr(path, user->service_job)) {
+                        free(user->service_job);
+                        user->service_job = NULL;
+                }
+
+                if (streq_ptr(path, user->slice_job)) {
+                        free(user->slice_job);
+                        user->slice_job = NULL;
+                }
+
+                LIST_FOREACH(sessions_by_user, session, user->sessions) {
+                        session_jobs_reply(session, unit, result);
+                }
+
+                user_save(user);
+                user_add_to_gc_queue(user);
+        }
+
+        return 0;
+}
+
+int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        const char *path, *unit;
+        Manager *m = userdata;
+        Session *session;
+        User *user;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_read(message, "so", &unit, &path);
         if (r < 0) {
         if (r < 0) {
-                log_warning("Failed to send delayed message: %s", bus_error_message(&error, r));
+                bus_log_parse_error(r);
+                return r;
+        }
 
 
-                manager->pending_action = HANDLE_IGNORE;
-                manager->action_what = 0;
+        session = hashmap_get(m->session_units, unit);
+        if (session)
+                session_add_to_gc_queue(session);
+
+        user = hashmap_get(m->user_units, unit);
+        if (user)
+                user_add_to_gc_queue(user);
+
+        return 0;
+}
+
+int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_free_ char *unit = NULL;
+        Manager *m = userdata;
+        const char *path;
+        Session *session;
+        User *user;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        path = sd_bus_message_get_path(message);
+        if (!path)
+                return 0;
+
+        r = unit_name_from_dbus_path(path, &unit);
+        if (r == -EINVAL) /* not a unit */
+                return 0;
+        if (r < 0)
+                return r;
+
+        session = hashmap_get(m->session_units, unit);
+        if (session)
+                session_add_to_gc_queue(session);
+
+        user = hashmap_get(m->user_units, unit);
+        if (user)
+                user_add_to_gc_queue(user);
+
+        return 0;
+}
+
+int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = userdata;
+        Session *session;
+        Iterator i;
+        int b, r;
+
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_read(message, "b", &b);
+        if (r < 0) {
+                bus_log_parse_error(r);
                 return r;
         }
 
                 return r;
         }
 
-        return 1;
+        if (b)
+                return 0;
+
+        /* systemd finished reloading, let's recheck all our sessions */
+        log_debug("System manager has been reloaded, rechecking sessions...");
+
+        HASHMAP_FOREACH(session, m->sessions, i)
+                session_add_to_gc_queue(session);
+
+        return 0;
+}
+
+int match_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        const char *name, *old, *new;
+        Manager *m = userdata;
+        Session *session;
+        Iterator i;
+        int r;
+        char *key;
+
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_read(message, "sss", &name, &old, &new);
+        if (r < 0) {
+                bus_log_parse_error(r);
+                return r;
+        }
+
+        if (isempty(old) || !isempty(new))
+                return 0;
+
+        key = set_remove(m->busnames, (char*) old);
+        if (!key)
+                return 0;
+
+        /* Drop all controllers owned by this name */
+
+        free(key);
+
+        HASHMAP_FOREACH(session, m->sessions, i)
+                if (session_is_controller(session, old))
+                        session_drop_controller(session);
+
+        return 0;
+}
+
+int manager_send_changed(Manager *manager, const char *property, ...) {
+        char **l;
+
+        assert(manager);
+
+        l = strv_from_stdarg_alloca(property);
+
+        return sd_bus_emit_properties_changed_strv(
+                        manager->bus,
+                        "/org/freedesktop/login1",
+                        "org.freedesktop.login1.Manager",
+                        l);
 }
 
 int manager_start_scope(
 }
 
 int manager_start_scope(
index 9705dd5b13a2324d75838045f2d21fd50eafbf8c..9218d098e024aa9f87b5fa1750247fd5ea229209 100644 (file)
@@ -14,6 +14,8 @@ struct ConfigPerfItem;
 %struct-type
 %includes
 %%
 %struct-type
 %includes
 %%
+Login.NAutoVTs,                    config_parse_unsigned,      0, offsetof(Manager, n_autovts)
+Login.ReserveVT,                   config_parse_unsigned,      0, offsetof(Manager, reserve_vt)
 Login.KillUserProcesses,           config_parse_bool,          0, offsetof(Manager, kill_user_processes)
 Login.KillOnlyUsers,               config_parse_strv,          0, offsetof(Manager, kill_only_users)
 Login.KillExcludeUsers,            config_parse_strv,          0, offsetof(Manager, kill_exclude_users)
 Login.KillUserProcesses,           config_parse_bool,          0, offsetof(Manager, kill_user_processes)
 Login.KillOnlyUsers,               config_parse_strv,          0, offsetof(Manager, kill_only_users)
 Login.KillExcludeUsers,            config_parse_strv,          0, offsetof(Manager, kill_exclude_users)
@@ -32,9 +34,3 @@ Login.IdleAction,                  config_parse_handle_action, 0, offsetof(Manag
 Login.IdleActionSec,               config_parse_sec,           0, offsetof(Manager, idle_action_usec)
 Login.RuntimeDirectorySize,        config_parse_tmpfs_size,    0, offsetof(Manager, runtime_dir_size)
 Login.RemoveIPC,                   config_parse_bool,          0, offsetof(Manager, remove_ipc)
 Login.IdleActionSec,               config_parse_sec,           0, offsetof(Manager, idle_action_usec)
 Login.RuntimeDirectorySize,        config_parse_tmpfs_size,    0, offsetof(Manager, runtime_dir_size)
 Login.RemoveIPC,                   config_parse_bool,          0, offsetof(Manager, remove_ipc)
-Sleep.SuspendMode,                 config_parse_strv,          0, offsetof(Manager, suspend_mode)
-Sleep.SuspendState,                config_parse_strv,          0, offsetof(Manager, suspend_state)
-Sleep.HibernateMode,               config_parse_strv,          0, offsetof(Manager, hibernate_mode)
-Sleep.HibernateState,              config_parse_strv,          0, offsetof(Manager, hibernate_state)
-Sleep.HybridSleepMode,             config_parse_strv,          0, offsetof(Manager, hybrid_sleep_mode)
-Sleep.HybridSleepState,            config_parse_strv,          0, offsetof(Manager, hybrid_sleep_state)
index 5c15ff3a893db509719ee93800c8bce7e632ecc9..495ec50be06d21e1da86505f5c394b01c78b53af 100644 (file)
@@ -178,6 +178,34 @@ static int vt_allocate(unsigned int vtnr) {
         return 0;
 }
 
         return 0;
 }
 
+int seat_preallocate_vts(Seat *s) {
+        int r = 0;
+        unsigned i;
+
+        assert(s);
+        assert(s->manager);
+
+        log_debug("Preallocating VTs...");
+
+        if (s->manager->n_autovts <= 0)
+                return 0;
+
+        if (!seat_has_vts(s))
+                return 0;
+
+        for (i = 1; i <= s->manager->n_autovts; i++) {
+                int q;
+
+                q = vt_allocate(i);
+                if (q < 0) {
+                        log_error_errno(q, "Failed to preallocate VT %u: %m", i);
+                        r = q;
+                }
+        }
+
+        return r;
+}
+
 int seat_apply_acls(Seat *s, Session *old_active) {
         int r;
 
 int seat_apply_acls(Seat *s, Session *old_active) {
         int r;
 
@@ -328,6 +356,7 @@ int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
         }
 
         r = seat_set_active(s, new_active);
         }
 
         r = seat_set_active(s, new_active);
+        manager_spawn_autovt(s->manager, vtnr);
 
         return r;
 }
 
         return r;
 }
@@ -385,6 +414,9 @@ int seat_start(Seat *s) {
                    LOG_MESSAGE("New seat %s.", s->id),
                    NULL);
 
                    LOG_MESSAGE("New seat %s.", s->id),
                    NULL);
 
+        /* Initialize VT magic stuff */
+        seat_preallocate_vts(s);
+
         /* Read current VT */
         seat_read_active_vt(s);
 
         /* Read current VT */
         seat_read_active_vt(s);
 
index d51a6e4bb97aa266a7686696c9b571c157b47822..563153e2d92b64644f35e3c587cbd4da43a71bd3 100644 (file)
@@ -701,6 +701,9 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
         if (!s->create_message)
                 return 0;
 
         if (!s->create_message)
                 return 0;
 
+        if (!sd_bus_error_is_set(error) && (s->scope_job || s->user->service_job))
+                return 0;
+
         c = s->create_message;
         s->create_message = NULL;
 
         c = s->create_message;
         s->create_message = NULL;
 
@@ -711,10 +714,9 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
         if (fifo_fd < 0)
                 return fifo_fd;
 
         if (fifo_fd < 0)
                 return fifo_fd;
 
-        /* Update the session and user state files before we notify
-         * the client about the result. */
+        /* Update the session state file before we notify the client
+         * about the result. */
         session_save(s);
         session_save(s);
-        user_save(s->user);
 
         p = session_bus_path(s);
         if (!p)
 
         p = session_bus_path(s);
         if (!p)
index 350a597d10351caf95e46dc8a0701e8e6d43eaed..39eab71833c8fccc8067a1df349e527ba35179ae 100644 (file)
 #include "audit.h"
 #include "bus-util.h"
 #include "bus-error.h"
 #include "audit.h"
 #include "bus-util.h"
 #include "bus-error.h"
-#include "cgroup-util.h"
-#include "def.h"
 #include "logind-session.h"
 #include "formats-util.h"
 #include "terminal-util.h"
 
 #include "logind-session.h"
 #include "formats-util.h"
 #include "terminal-util.h"
 
+#define RELEASE_USEC (20*USEC_PER_SEC)
+
 static void session_remove_fifo(Session *s);
 
 Session* session_new(Manager *m, const char *id) {
 static void session_remove_fifo(Session *s);
 
 Session* session_new(Manager *m, const char *id) {
@@ -120,6 +120,13 @@ void session_free(Session *s) {
                 LIST_REMOVE(sessions_by_seat, s->seat->sessions, s);
         }
 
                 LIST_REMOVE(sessions_by_seat, s->seat->sessions, s);
         }
 
+        if (s->scope) {
+                hashmap_remove(s->manager->session_units, s->scope);
+                free(s->scope);
+        }
+
+        free(s->scope_job);
+
         sd_bus_message_unref(s->create_message);
 
         free(s->tty);
         sd_bus_message_unref(s->create_message);
 
         free(s->tty);
@@ -187,6 +194,11 @@ int session_save(Session *s) {
         if (s->class >= 0)
                 fprintf(f, "CLASS=%s\n", session_class_to_string(s->class));
 
         if (s->class >= 0)
                 fprintf(f, "CLASS=%s\n", session_class_to_string(s->class));
 
+        if (s->scope)
+                fprintf(f, "SCOPE=%s\n", s->scope);
+        if (s->scope_job)
+                fprintf(f, "SCOPE_JOB=%s\n", s->scope_job);
+
         if (s->fifo_path)
                 fprintf(f, "FIFO=%s\n", s->fifo_path);
 
         if (s->fifo_path)
                 fprintf(f, "FIFO=%s\n", s->fifo_path);
 
@@ -305,6 +317,8 @@ int session_load(Session *s) {
 
         r = parse_env_file(s->state_file, NEWLINE,
                            "REMOTE",         &remote,
 
         r = parse_env_file(s->state_file, NEWLINE,
                            "REMOTE",         &remote,
+                           "SCOPE",          &s->scope,
+                           "SCOPE_JOB",      &s->scope_job,
                            "FIFO",           &s->fifo_path,
                            "SEAT",           &seat,
                            "TTY",            &s->tty,
                            "FIFO",           &s->fifo_path,
                            "SEAT",           &seat,
                            "TTY",            &s->tty,
@@ -479,21 +493,42 @@ int session_activate(Session *s) {
         return 0;
 }
 
         return 0;
 }
 
-static int session_start_cgroup(Session *s) {
+static int session_start_scope(Session *s) {
         int r;
 
         assert(s);
         assert(s->user);
         int r;
 
         assert(s);
         assert(s->user);
-        assert(s->leader > 0);
+        assert(s->user->slice);
+
+        if (!s->scope) {
+                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+                _cleanup_free_ char *description = NULL;
+                char *scope, *job = NULL;
+
+                description = strjoin("Session ", s->id, " of user ", s->user->name, NULL);
+                if (!description)
+                        return log_oom();
+
+                scope = strjoin("session-", s->id, ".scope", NULL);
+                if (!scope)
+                        return log_oom();
+
+                r = manager_start_scope(s->manager, scope, s->leader, s->user->slice, description, "systemd-logind.service", "systemd-user-sessions.service", &error, &job);
+                if (r < 0) {
+                        log_error("Failed to start session scope %s: %s %s",
+                                  scope, bus_error_message(&error, r), error.name);
+                        free(scope);
+                        return r;
+                } else {
+                        s->scope = scope;
 
 
-        /* First, create our own group */
-        r = cg_create(SYSTEMD_CGROUP_CONTROLLER, s->id);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create cgroup %s: %m", s->id);
+                        free(s->scope_job);
+                        s->scope_job = job;
+                }
+        }
 
 
-        r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, s->id, s->leader);
-        if (r < 0)
-                log_warning_errno(r, "Failed to attach PID %d to cgroup %s: %m", s->leader, s->id);
+        if (s->scope)
+                hashmap_put(s->manager->session_units, s->scope, s);
 
         return 0;
 }
 
         return 0;
 }
@@ -513,7 +548,8 @@ int session_start(Session *s) {
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        r = session_start_cgroup(s);
+        /* Create cgroup */
+        r = session_start_scope(s);
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
@@ -554,16 +590,31 @@ int session_start(Session *s) {
         return 0;
 }
 
         return 0;
 }
 
-static int session_stop_cgroup(Session *s, bool force) {
+static int session_stop_scope(Session *s, bool force) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        char *job = NULL;
         int r;
 
         assert(s);
 
         int r;
 
         assert(s);
 
+        if (!s->scope)
+                return 0;
+
         if (force || manager_shall_kill(s->manager, s->user->name)) {
         if (force || manager_shall_kill(s->manager, s->user->name)) {
-                r = session_kill(s, KILL_ALL, SIGTERM);
-                if (r < 0)
+                r = manager_stop_unit(s->manager, s->scope, &error, &job);
+                if (r < 0) {
+                        log_error("Failed to stop session scope: %s", bus_error_message(&error, r));
+                        return r;
+                }
+
+                free(s->scope_job);
+                s->scope_job = job;
+        } else {
+                r = manager_abandon_scope(s->manager, s->scope, &error);
+                if (r < 0) {
+                        log_error("Failed to abandon session scope: %s", bus_error_message(&error, r));
                         return r;
                         return r;
+                }
         }
 
         return 0;
         }
 
         return 0;
@@ -586,7 +637,7 @@ int session_stop(Session *s, bool force) {
         session_remove_fifo(s);
 
         /* Kill cgroup */
         session_remove_fifo(s);
 
         /* Kill cgroup */
-        r = session_stop_cgroup(s, force);
+        r = session_stop_scope(s, force);
 
         s->stopping = true;
 
 
         s->stopping = true;
 
@@ -648,6 +699,16 @@ int session_finalize(Session *s) {
         return r;
 }
 
         return r;
 }
 
+static int release_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) {
+        Session *s = userdata;
+
+        assert(es);
+        assert(s);
+
+        session_stop(s, false);
+        return 0;
+}
+
 int session_release(Session *s) {
         assert(s);
 
 int session_release(Session *s) {
         assert(s);
 
@@ -657,10 +718,11 @@ int session_release(Session *s) {
         if (s->timer_event_source)
                 return 0;
 
         if (s->timer_event_source)
                 return 0;
 
-        /* In systemd, session release is triggered by user jobs
-           dying.  In elogind we don't have that so go ahead and stop
-           now.  */
-        return session_stop(s, false);
+        return sd_event_add_time(s->manager->event,
+                                 &s->timer_event_source,
+                                 CLOCK_MONOTONIC,
+                                 now(CLOCK_MONOTONIC) + RELEASE_USEC, 0,
+                                 release_timeout_callback, s);
 }
 
 bool session_is_active(Session *s) {
 }
 
 bool session_is_active(Session *s) {
@@ -865,7 +927,10 @@ bool session_check_gc(Session *s, bool drop_not_started) {
                         return true;
         }
 
                         return true;
         }
 
-        if (cg_is_empty_recursive (SYSTEMD_CGROUP_CONTROLLER, s->id, false) > 0)
+        if (s->scope_job && manager_job_is_active(s->manager, s->scope_job))
+                return true;
+
+        if (s->scope && manager_unit_is_active(s->manager, s->scope))
                 return true;
 
         return false;
                 return true;
 
         return false;
@@ -888,7 +953,7 @@ SessionState session_get_state(Session *s) {
         if (s->stopping || s->timer_event_source)
                 return SESSION_CLOSING;
 
         if (s->stopping || s->timer_event_source)
                 return SESSION_CLOSING;
 
-        if (s->fifo_fd < 0)
+        if (s->scope_job || s->fifo_fd < 0)
                 return SESSION_OPENING;
 
         if (session_is_active(s))
                 return SESSION_OPENING;
 
         if (session_is_active(s))
@@ -900,23 +965,10 @@ SessionState session_get_state(Session *s) {
 int session_kill(Session *s, KillWho who, int signo) {
         assert(s);
 
 int session_kill(Session *s, KillWho who, int signo) {
         assert(s);
 
-        if (who == KILL_LEADER) {
-                if (s->leader <= 0)
-                        return -ESRCH;
-
-                /* FIXME: verify that leader is in cgroup?  */
+        if (!s->scope)
+                return -ESRCH;
 
 
-                if (kill(s->leader, signo) < 0) {
-                        return log_error_errno(errno, "Failed to kill process leader %d for session %s: %m", s->leader, s->id);
-                }
-                return 0;
-        } else {
-                bool sigcont = false;
-                bool ignore_self = true;
-                bool rem = true;
-                return cg_kill_recursive (SYSTEMD_CGROUP_CONTROLLER, s->id, signo,
-                                          sigcont, ignore_self, rem, NULL);
-        }
+        return manager_kill_unit(s->manager, s->scope, who, signo, NULL);
 }
 
 static int session_open_vt(Session *s) {
 }
 
 static int session_open_vt(Session *s) {
index dffe5a9aa18d19d5ace6d4c1def7d29206fac3a2..854f30fbd15841ec93a0b74c4eb3816bef15e96a 100644 (file)
@@ -89,8 +89,8 @@ struct Session {
         char *service;
         char *desktop;
 
         char *service;
         char *desktop;
 
-        /* Always NULL.  */
         char *scope;
         char *scope;
+        char *scope_job;
 
         Seat *seat;
         unsigned int vtnr;
 
         Seat *seat;
         unsigned int vtnr;
index 2ea2067b2cecbaaf0359c442e82f026f62af40f9..71bff96728fd1b733b1ccd654e729103549b74d2 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "util.h"
 #include "mkdir.h"
 
 #include "util.h"
 #include "mkdir.h"
+#include "rm-rf.h"
 #include "hashmap.h"
 #include "fileio.h"
 #include "path-util.h"
 #include "hashmap.h"
 #include "fileio.h"
 #include "path-util.h"
@@ -35,9 +36,9 @@
 #include "bus-error.h"
 #include "conf-parser.h"
 #include "clean-ipc.h"
 #include "bus-error.h"
 #include "conf-parser.h"
 #include "clean-ipc.h"
-#include "smack-util.h"
-#include "label.h"
 #include "logind-user.h"
 #include "logind-user.h"
+#include "smack-util.h"
+#include "formats-util.h"
 
 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
         User *u;
 
 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
         User *u;
@@ -82,6 +83,19 @@ void user_free(User *u) {
         while (u->sessions)
                 session_free(u->sessions);
 
         while (u->sessions)
                 session_free(u->sessions);
 
+        if (u->slice) {
+                hashmap_remove(u->manager->user_units, u->slice);
+                free(u->slice);
+        }
+
+        if (u->service) {
+                hashmap_remove(u->manager->user_units, u->service);
+                free(u->service);
+        }
+
+        free(u->slice_job);
+        free(u->service_job);
+
         free(u->runtime_path);
 
         hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
         free(u->runtime_path);
 
         hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
@@ -91,7 +105,7 @@ void user_free(User *u) {
         free(u);
 }
 
         free(u);
 }
 
-static int user_save_internal(User *u) {
+int user_save(User *u) {
         _cleanup_free_ char *temp_path = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
         _cleanup_free_ char *temp_path = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
@@ -99,6 +113,9 @@ static int user_save_internal(User *u) {
         assert(u);
         assert(u->state_file);
 
         assert(u);
         assert(u->state_file);
 
+        if (!u->started)
+                return 0;
+
         r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
         if (r < 0)
                 goto finish;
         r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
         if (r < 0)
                 goto finish;
@@ -119,6 +136,16 @@ static int user_save_internal(User *u) {
         if (u->runtime_path)
                 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
 
         if (u->runtime_path)
                 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
 
+        if (u->service)
+                fprintf(f, "SERVICE=%s\n", u->service);
+        if (u->service_job)
+                fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
+
+        if (u->slice)
+                fprintf(f, "SLICE=%s\n", u->slice);
+        if (u->slice_job)
+                fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
+
         if (u->display)
                 fprintf(f, "DISPLAY=%s\n", u->display->id);
 
         if (u->display)
                 fprintf(f, "DISPLAY=%s\n", u->display->id);
 
@@ -231,15 +258,6 @@ finish:
         return r;
 }
 
         return r;
 }
 
-int user_save(User *u) {
-        assert(u);
-
-        if (!u->started)
-                return 0;
-
-        return user_save_internal (u);
-}
-
 int user_load(User *u) {
         _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
         Session *s = NULL;
 int user_load(User *u) {
         _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
         Session *s = NULL;
@@ -249,6 +267,10 @@ int user_load(User *u) {
 
         r = parse_env_file(u->state_file, NEWLINE,
                            "RUNTIME",     &u->runtime_path,
 
         r = parse_env_file(u->state_file, NEWLINE,
                            "RUNTIME",     &u->runtime_path,
+                           "SERVICE",     &u->service,
+                           "SERVICE_JOB", &u->service_job,
+                           "SLICE",       &u->slice,
+                           "SLICE_JOB",   &u->slice_job,
                            "DISPLAY",     &display,
                            "REALTIME",    &realtime,
                            "MONOTONIC",   &monotonic,
                            "DISPLAY",     &display,
                            "REALTIME",    &realtime,
                            "MONOTONIC",   &monotonic,
@@ -298,10 +320,10 @@ static int user_mkdir_runtime_path(User *u) {
         } else
                 p = u->runtime_path;
 
         } else
                 p = u->runtime_path;
 
-        if (path_is_mount_point(p, 0) <= 0) {
+        if (path_is_mount_point(p, false) <= 0) {
                 _cleanup_free_ char *t = NULL;
 
                 _cleanup_free_ char *t = NULL;
 
-                (void) mkdir_label(p, 0700);
+                (void) mkdir(p, 0700);
 
                 if (mac_smack_use())
                         r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
 
                 if (mac_smack_use())
                         r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
@@ -329,10 +351,6 @@ static int user_mkdir_runtime_path(User *u) {
                                 goto fail;
                         }
                 }
                                 goto fail;
                         }
                 }
-
-                r = label_fix(p, false, false);
-                if (r < 0)
-                        log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", p);
         }
 
         u->runtime_path = p;
         }
 
         u->runtime_path = p;
@@ -349,6 +367,72 @@ fail:
         return r;
 }
 
         return r;
 }
 
+static int user_start_slice(User *u) {
+        char *job;
+        int r;
+
+        assert(u);
+
+        if (!u->slice) {
+                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+                char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice;
+                sprintf(lu, UID_FMT, u->uid);
+
+                r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &slice);
+                if (r < 0)
+                        return r;
+
+                r = manager_start_unit(u->manager, slice, &error, &job);
+                if (r < 0) {
+                        log_error("Failed to start user slice: %s", bus_error_message(&error, r));
+                        free(slice);
+                } else {
+                        u->slice = slice;
+
+                        free(u->slice_job);
+                        u->slice_job = job;
+                }
+        }
+
+        if (u->slice)
+                hashmap_put(u->manager->user_units, u->slice, u);
+
+        return 0;
+}
+
+static int user_start_service(User *u) {
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        char *job;
+        int r;
+
+        assert(u);
+
+        if (!u->service) {
+                char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
+                sprintf(lu, UID_FMT, u->uid);
+
+                r = unit_name_build("user", lu, ".service", &service);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to build service name: %m");
+
+                r = manager_start_unit(u->manager, service, &error, &job);
+                if (r < 0) {
+                        log_error("Failed to start user service: %s", bus_error_message(&error, r));
+                        free(service);
+                } else {
+                        u->service = service;
+
+                        free(u->service_job);
+                        u->service_job = job;
+                }
+        }
+
+        if (u->service)
+                hashmap_put(u->manager->user_units, u->service, u);
+
+        return 0;
+}
+
 int user_start(User *u) {
         int r;
 
 int user_start(User *u) {
         int r;
 
@@ -364,11 +448,15 @@ int user_start(User *u) {
         if (r < 0)
                 return r;
 
         if (r < 0)
                 return r;
 
-        /* Save the user data so far, because pam_systemd will read the
-         * XDG_RUNTIME_DIR out of it while starting up systemd --user.
-         * We need to do user_save_internal() because we have not
-         * "officially" started yet. */
-        user_save_internal(u);
+        /* Create cgroup */
+        r = user_start_slice(u);
+        if (r < 0)
+                return r;
+
+        /* Spawn user systemd */
+        r = user_start_service(u);
+        if (r < 0)
+                return r;
 
         if (!dual_timestamp_is_set(&u->timestamp))
                 dual_timestamp_get(&u->timestamp);
 
         if (!dual_timestamp_is_set(&u->timestamp))
                 dual_timestamp_get(&u->timestamp);
@@ -383,6 +471,50 @@ int user_start(User *u) {
         return 0;
 }
 
         return 0;
 }
 
+static int user_stop_slice(User *u) {
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        char *job;
+        int r;
+
+        assert(u);
+
+        if (!u->slice)
+                return 0;
+
+        r = manager_stop_unit(u->manager, u->slice, &error, &job);
+        if (r < 0) {
+                log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
+                return r;
+        }
+
+        free(u->slice_job);
+        u->slice_job = job;
+
+        return r;
+}
+
+static int user_stop_service(User *u) {
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        char *job;
+        int r;
+
+        assert(u);
+
+        if (!u->service)
+                return 0;
+
+        r = manager_stop_unit(u->manager, u->service, &error, &job);
+        if (r < 0) {
+                log_error("Failed to stop user service: %s", bus_error_message(&error, r));
+                return r;
+        }
+
+        free(u->service_job);
+        u->service_job = job;
+
+        return r;
+}
+
 static int user_remove_runtime_path(User *u) {
         int r;
 
 static int user_remove_runtime_path(User *u) {
         int r;
 
@@ -391,7 +523,7 @@ static int user_remove_runtime_path(User *u) {
         if (!u->runtime_path)
                 return 0;
 
         if (!u->runtime_path)
                 return 0;
 
-        r = rm_rf(u->runtime_path, false, false, false);
+        r = rm_rf(u->runtime_path, 0);
         if (r < 0)
                 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
 
         if (r < 0)
                 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
 
@@ -402,7 +534,7 @@ static int user_remove_runtime_path(User *u) {
         if (r < 0 && errno != EINVAL && errno != ENOENT)
                 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
 
         if (r < 0 && errno != EINVAL && errno != ENOENT)
                 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
 
-        r = rm_rf(u->runtime_path, false, true, false);
+        r = rm_rf(u->runtime_path, REMOVE_ROOT);
         if (r < 0)
                 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
 
         if (r < 0)
                 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
 
@@ -429,6 +561,16 @@ int user_stop(User *u, bool force) {
                         r = k;
         }
 
                         r = k;
         }
 
+        /* Kill systemd */
+        k = user_stop_service(u);
+        if (k < 0)
+                r = k;
+
+        /* Kill cgroup */
+        k = user_stop_slice(u);
+        if (k < 0)
+                r = k;
+
         u->stopping = true;
 
         user_save(u);
         u->stopping = true;
 
         user_save(u);
@@ -477,7 +619,7 @@ int user_finalize(User *u) {
 int user_get_idle_hint(User *u, dual_timestamp *t) {
         Session *s;
         bool idle_hint = true;
 int user_get_idle_hint(User *u, dual_timestamp *t) {
         Session *s;
         bool idle_hint = true;
-        dual_timestamp ts = DUAL_TIMESTAMP_NULL;
+        dual_timestamp ts = { 0, 0 };
 
         assert(u);
 
 
         assert(u);
 
@@ -535,6 +677,12 @@ bool user_check_gc(User *u, bool drop_not_started) {
         if (user_check_linger_file(u) > 0)
                 return true;
 
         if (user_check_linger_file(u) > 0)
                 return true;
 
+        if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
+                return true;
+
+        if (u->service_job && manager_job_is_active(u->manager, u->service_job))
+                return true;
+
         return false;
 }
 
         return false;
 }
 
@@ -556,6 +704,9 @@ UserState user_get_state(User *u) {
         if (u->stopping)
                 return USER_CLOSING;
 
         if (u->stopping)
                 return USER_CLOSING;
 
+        if (u->slice_job || u->service_job)
+                return USER_OPENING;
+
         if (u->sessions) {
                 bool all_closing = true;
 
         if (u->sessions) {
                 bool all_closing = true;
 
@@ -579,87 +730,62 @@ UserState user_get_state(User *u) {
 }
 
 int user_kill(User *u, int signo) {
 }
 
 int user_kill(User *u, int signo) {
-        Session *s;
-        int res = 0;
-
         assert(u);
 
         assert(u);
 
-        LIST_FOREACH(sessions_by_user, s, u->sessions) {
-                int r = session_kill(s, KILL_ALL, signo);
-                if (res == 0 && r < 0)
-                        res = r;
-        }
-
-        return res;
-}
-
-static bool elect_display_filter(Session *s) {
-        /* Return true if the session is a candidate for the user’s â€˜primary
-         * session’ or â€˜display’. */
-        assert(s);
-
-        return (s->class == SESSION_USER && !s->stopping);
-}
-
-static int elect_display_compare(Session *s1, Session *s2) {
-        /* Indexed by SessionType. Lower numbers mean more preferred. */
-        const int type_ranks[_SESSION_TYPE_MAX] = {
-                [SESSION_UNSPECIFIED] = 0,
-                [SESSION_TTY] = -2,
-                [SESSION_X11] = -3,
-                [SESSION_WAYLAND] = -3,
-                [SESSION_MIR] = -3,
-                [SESSION_WEB] = -1,
-        };
-
-        /* Calculate the partial order relationship between s1 and s2,
-         * returning < 0 if s1 is preferred as the user’s â€˜primary session’,
-         * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
-         * is preferred.
-         *
-         * s1 or s2 may be NULL. */
-        if (!s1 && !s2)
-                return 0;
-
-        if ((s1 == NULL) != (s2 == NULL))
-                return (s1 == NULL) - (s2 == NULL);
-
-        if (s1->stopping != s2->stopping)
-                return s1->stopping - s2->stopping;
-
-        if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
-                return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
-
-        if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
-                return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
+        if (!u->slice)
+                return -ESRCH;
 
 
-        if (s1->type != s2->type)
-                return type_ranks[s1->type] - type_ranks[s2->type];
-
-        return 0;
+        return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
 }
 
 void user_elect_display(User *u) {
 }
 
 void user_elect_display(User *u) {
-        Session *s;
+        Session *graphical = NULL, *text = NULL, *other = NULL, *s;
 
         assert(u);
 
         /* This elects a primary session for each user, which we call
          * the "display". We try to keep the assignment stable, but we
          * "upgrade" to better choices. */
 
         assert(u);
 
         /* This elects a primary session for each user, which we call
          * the "display". We try to keep the assignment stable, but we
          * "upgrade" to better choices. */
-        log_debug("Electing new display for user %s", u->name);
 
         LIST_FOREACH(sessions_by_user, s, u->sessions) {
 
         LIST_FOREACH(sessions_by_user, s, u->sessions) {
-                if (!elect_display_filter(s)) {
-                        log_debug("Ignoring session %s", s->id);
+
+                if (s->class != SESSION_USER)
                         continue;
                         continue;
-                }
 
 
-                if (elect_display_compare(s, u->display) < 0) {
-                        log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
-                        u->display = s;
-                }
+                if (s->stopping)
+                        continue;
+
+                if (SESSION_TYPE_IS_GRAPHICAL(s->type))
+                        graphical = s;
+                else if (s->type == SESSION_TTY)
+                        text = s;
+                else
+                        other = s;
+        }
+
+        if (graphical &&
+            (!u->display ||
+             u->display->class != SESSION_USER ||
+             u->display->stopping ||
+             !SESSION_TYPE_IS_GRAPHICAL(u->display->type))) {
+                u->display = graphical;
+                return;
         }
         }
+
+        if (text &&
+            (!u->display ||
+             u->display->class != SESSION_USER ||
+             u->display->stopping ||
+             u->display->type != SESSION_TTY)) {
+                u->display = text;
+                return;
+        }
+
+        if (other &&
+            (!u->display ||
+             u->display->class != SESSION_USER ||
+             u->display->stopping))
+                u->display = other;
 }
 
 static const char* const user_state_table[_USER_STATE_MAX] = {
 }
 
 static const char* const user_state_table[_USER_STATE_MAX] = {
index 902bb52aa0ce13ac1b0d04a24cac92f6ed9a041a..722247806bbb5592fa67e4d3c42e770b208bc7e8 100644 (file)
@@ -47,12 +47,12 @@ struct User {
         char *state_file;
         char *runtime_path;
 
         char *state_file;
         char *runtime_path;
 
-        /* These are always NULL, and here just for logind-user-dbus.c
-           to easily provide a NULL value for the user's service and
-           slice properties. */
         char *service;
         char *slice;
 
         char *service;
         char *slice;
 
+        char *service_job;
+        char *slice_job;
+
         Session *display;
 
         dual_timestamp timestamp;
         Session *display;
 
         dual_timestamp timestamp;
index cd0b67119b45b6114fdf1709c0bd55136ed7d297..bcc268be8cbf62011ac9ffc99bff57808c297cab 100644 (file)
 #include "conf-parser.h"
 #include "bus-util.h"
 #include "bus-error.h"
 #include "conf-parser.h"
 #include "bus-util.h"
 #include "bus-error.h"
-#include "udev-util.h"
-#include "signal-util.h"
 #include "logind.h"
 #include "logind.h"
+#include "udev-util.h"
+#include "formats-util.h"
+#include "label.h"
 
 
-Manager *manager_new(void) {
+static void manager_free(Manager *m);
+
+static Manager *manager_new(void) {
         Manager *m;
         int r;
 
         Manager *m;
         int r;
 
@@ -43,7 +46,10 @@ Manager *manager_new(void) {
                 return NULL;
 
         m->console_active_fd = -1;
                 return NULL;
 
         m->console_active_fd = -1;
+        m->reserve_vt_fd = -1;
 
 
+        m->n_autovts = 6;
+        m->reserve_vt = 6;
         m->remove_ipc = true;
         m->inhibit_delay_max = 5 * USEC_PER_SEC;
         m->handle_power_key = HANDLE_POWEROFF;
         m->remove_ipc = true;
         m->inhibit_delay_max = 5 * USEC_PER_SEC;
         m->handle_power_key = HANDLE_POWEROFF;
@@ -67,9 +73,13 @@ Manager *manager_new(void) {
         m->inhibitors = hashmap_new(&string_hash_ops);
         m->buttons = hashmap_new(&string_hash_ops);
 
         m->inhibitors = hashmap_new(&string_hash_ops);
         m->buttons = hashmap_new(&string_hash_ops);
 
+        m->user_units = hashmap_new(&string_hash_ops);
+        m->session_units = hashmap_new(&string_hash_ops);
+
         m->busnames = set_new(&string_hash_ops);
 
         m->busnames = set_new(&string_hash_ops);
 
-        if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->busnames)
+        if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->busnames ||
+            !m->user_units || !m->session_units)
                 goto fail;
 
         m->kill_exclude_users = strv_new("root", NULL);
                 goto fail;
 
         m->kill_exclude_users = strv_new("root", NULL);
@@ -93,7 +103,7 @@ fail:
         return NULL;
 }
 
         return NULL;
 }
 
-void manager_free(Manager *m) {
+static void manager_free(Manager *m) {
         Session *session;
         User *u;
         Device *d;
         Session *session;
         User *u;
         Device *d;
@@ -128,9 +138,16 @@ void manager_free(Manager *m) {
         hashmap_free(m->inhibitors);
         hashmap_free(m->buttons);
 
         hashmap_free(m->inhibitors);
         hashmap_free(m->buttons);
 
+        hashmap_free(m->user_units);
+        hashmap_free(m->session_units);
+
         set_free_free(m->busnames);
 
         sd_event_source_unref(m->idle_action_event_source);
         set_free_free(m->busnames);
 
         sd_event_source_unref(m->idle_action_event_source);
+        sd_event_source_unref(m->inhibit_timeout_source);
+        sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+        sd_event_source_unref(m->nologin_timeout_source);
+        sd_event_source_unref(m->wall_message_timeout_source);
 
         sd_event_source_unref(m->console_active_event_source);
         sd_event_source_unref(m->udev_seat_event_source);
 
         sd_event_source_unref(m->console_active_event_source);
         sd_event_source_unref(m->udev_seat_event_source);
@@ -153,14 +170,23 @@ void manager_free(Manager *m) {
         if (m->udev)
                 udev_unref(m->udev);
 
         if (m->udev)
                 udev_unref(m->udev);
 
+        if (m->unlink_nologin)
+                unlink("/run/nologin");
+
         bus_verify_polkit_async_registry_free(m->polkit_registry);
 
         sd_bus_unref(m->bus);
         sd_event_unref(m->event);
 
         bus_verify_polkit_async_registry_free(m->polkit_registry);
 
         sd_bus_unref(m->bus);
         sd_event_unref(m->event);
 
+        safe_close(m->reserve_vt_fd);
+
         strv_free(m->kill_only_users);
         strv_free(m->kill_exclude_users);
 
         strv_free(m->kill_only_users);
         strv_free(m->kill_exclude_users);
 
+        free(m->scheduled_shutdown_type);
+        free(m->scheduled_shutdown_tty);
+        free(m->wall_message);
+        free(m->action_job);
         free(m);
 }
 
         free(m);
 }
 
@@ -538,31 +564,25 @@ static int manager_dispatch_console(sd_event_source *s, int fd, uint32_t revents
         return 0;
 }
 
         return 0;
 }
 
-static int signal_agent_released(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        Manager *m = userdata;
-        Session *s;
-        const char *cgroup;
-        int r;
+static int manager_reserve_vt(Manager *m) {
+        _cleanup_free_ char *p = NULL;
 
 
-        assert(bus);
-        assert(message);
         assert(m);
 
         assert(m);
 
-        r = sd_bus_message_read(message, "s", &cgroup);
-        if (r < 0) {
-                bus_log_parse_error(r);
+        if (m->reserve_vt <= 0)
                 return 0;
                 return 0;
-        }
 
 
-        s = hashmap_get(m->sessions, cgroup);
+        if (asprintf(&p, "/dev/tty%u", m->reserve_vt) < 0)
+                return log_oom();
 
 
-        if (!s) {
-                log_warning("Session not found: %s", cgroup);
-                return 0;
-        }
+        m->reserve_vt_fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
+        if (m->reserve_vt_fd < 0) {
 
 
-        session_finalize(s);
-        session_free(s);
+                /* Don't complain on VT-less systems */
+                if (errno != ENOENT)
+                        log_warning_errno(errno, "Failed to pin reserved VT: %m");
+                return -errno;
+        }
 
         return 0;
 }
 
         return 0;
 }
@@ -582,13 +602,6 @@ static int manager_connect_bus(Manager *m) {
         if (r < 0)
                 return log_error_errno(r, "Failed to add manager object vtable: %m");
 
         if (r < 0)
                 return log_error_errno(r, "Failed to add manager object vtable: %m");
 
-        r = sd_bus_add_match(m->bus, NULL,
-                             "type='signal',"
-                             "interface='org.freedesktop.systemd1.Agent',"
-                             "member='Released',"
-                             "path='/org/freedesktop/systemd1/agent'",
-                             signal_agent_released, m);
-
         r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/seat", "org.freedesktop.login1.Seat", seat_vtable, seat_object_find, m);
         if (r < 0)
                 return log_error_errno(r, "Failed to add seat object vtable: %m");
         r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/seat", "org.freedesktop.login1.Seat", seat_vtable, seat_object_find, m);
         if (r < 0)
                 return log_error_errno(r, "Failed to add seat object vtable: %m");
@@ -613,6 +626,73 @@ static int manager_connect_bus(Manager *m) {
         if (r < 0)
                 return log_error_errno(r, "Failed to add user enumerator: %m");
 
         if (r < 0)
                 return log_error_errno(r, "Failed to add user enumerator: %m");
 
+        r = sd_bus_add_match(m->bus,
+                             NULL,
+                             "type='signal',"
+                             "sender='org.freedesktop.DBus',"
+                             "interface='org.freedesktop.DBus',"
+                             "member='NameOwnerChanged',"
+                             "path='/org/freedesktop/DBus'",
+                             match_name_owner_changed, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add match for NameOwnerChanged: %m");
+
+        r = sd_bus_add_match(m->bus,
+                             NULL,
+                             "type='signal',"
+                             "sender='org.freedesktop.systemd1',"
+                             "interface='org.freedesktop.systemd1.Manager',"
+                             "member='JobRemoved',"
+                             "path='/org/freedesktop/systemd1'",
+                             match_job_removed, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add match for JobRemoved: %m");
+
+        r = sd_bus_add_match(m->bus,
+                             NULL,
+                             "type='signal',"
+                             "sender='org.freedesktop.systemd1',"
+                             "interface='org.freedesktop.systemd1.Manager',"
+                             "member='UnitRemoved',"
+                             "path='/org/freedesktop/systemd1'",
+                             match_unit_removed, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add match for UnitRemoved: %m");
+
+        r = sd_bus_add_match(m->bus,
+                             NULL,
+                             "type='signal',"
+                             "sender='org.freedesktop.systemd1',"
+                             "interface='org.freedesktop.DBus.Properties',"
+                             "member='PropertiesChanged'",
+                             match_properties_changed, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add match for PropertiesChanged: %m");
+
+        r = sd_bus_add_match(m->bus,
+                             NULL,
+                             "type='signal',"
+                             "sender='org.freedesktop.systemd1',"
+                             "interface='org.freedesktop.systemd1.Manager',"
+                             "member='Reloading',"
+                             "path='/org/freedesktop/systemd1'",
+                             match_reloading, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add match for Reloading: %m");
+
+        r = sd_bus_call_method(
+                        m->bus,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "Subscribe",
+                        &error,
+                        NULL, NULL);
+        if (r < 0) {
+                log_error("Failed to enable subscription: %s", bus_error_message(&error, r));
+                return r;
+        }
+
         r = sd_bus_request_name(m->bus, "org.freedesktop.login1", 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to register name: %m");
         r = sd_bus_request_name(m->bus, "org.freedesktop.login1", 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to register name: %m");
@@ -707,8 +787,13 @@ static int manager_connect_console(Manager *m) {
                 return -EINVAL;
         }
 
                 return -EINVAL;
         }
 
-        assert_se(ignore_signals(SIGRTMIN + 1, -1) >= 0);
-        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN, -1) >= 0);
+        r = ignore_signals(SIGRTMIN + 1, -1);
+        if (r < 0)
+                return log_error_errno(r, "Cannot ignore SIGRTMIN + 1: %m");
+
+        r = sigprocmask_many(SIG_BLOCK, SIGRTMIN, -1);
+        if (r < 0)
+                return log_error_errno(r, "Cannot block SIGRTMIN: %m");
 
         r = sd_event_add_signal(m->event, NULL, SIGRTMIN, manager_vt_switch, m);
         if (r < 0)
 
         r = sd_event_add_signal(m->event, NULL, SIGRTMIN, manager_vt_switch, m);
         if (r < 0)
@@ -794,10 +879,30 @@ static int manager_connect_udev(Manager *m) {
                         return r;
         }
 
                         return r;
         }
 
+        /* Don't bother watching VCSA devices, if nobody cares */
+        if (m->n_autovts > 0 && m->console_active_fd >= 0) {
+
+                m->udev_vcsa_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
+                if (!m->udev_vcsa_monitor)
+                        return -ENOMEM;
+
+                r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_vcsa_monitor, "vc", NULL);
+                if (r < 0)
+                        return r;
+
+                r = udev_monitor_enable_receiving(m->udev_vcsa_monitor);
+                if (r < 0)
+                        return r;
+
+                r = sd_event_add_io(m->event, &m->udev_vcsa_event_source, udev_monitor_get_fd(m->udev_vcsa_monitor), EPOLLIN, manager_dispatch_vcsa_udev, m);
+                if (r < 0)
+                        return r;
+        }
+
         return 0;
 }
 
         return 0;
 }
 
-void manager_gc(Manager *m, bool drop_not_started) {
+static void manager_gc(Manager *m, bool drop_not_started) {
         Seat *seat;
         Session *session;
         User *user;
         Seat *seat;
         Session *session;
         User *user;
@@ -908,7 +1013,7 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us
         return 0;
 }
 
         return 0;
 }
 
-int manager_startup(Manager *m) {
+static int manager_startup(Manager *m) {
         int r;
         Seat *seat;
         Session *session;
         int r;
         Seat *seat;
         Session *session;
@@ -971,6 +1076,9 @@ int manager_startup(Manager *m) {
         /* Remove stale objects before we start them */
         manager_gc(m, false);
 
         /* Remove stale objects before we start them */
         manager_gc(m, false);
 
+        /* Reserve the special reserved VT */
+        manager_reserve_vt(m);
+
         /* And start everything */
         HASHMAP_FOREACH(seat, m->seats, i)
                 seat_start(seat);
         /* And start everything */
         HASHMAP_FOREACH(seat, m->seats, i)
                 seat_start(seat);
@@ -992,14 +1100,12 @@ int manager_startup(Manager *m) {
         return 0;
 }
 
         return 0;
 }
 
-int manager_run(Manager *m) {
+static int manager_run(Manager *m) {
         int r;
 
         assert(m);
 
         for (;;) {
         int r;
 
         assert(m);
 
         for (;;) {
-                usec_t us = (uint64_t) -1;
-
                 r = sd_event_get_state(m->event);
                 if (r < 0)
                         return r;
                 r = sd_event_get_state(m->event);
                 if (r < 0)
                         return r;
@@ -1008,19 +1114,7 @@ int manager_run(Manager *m) {
 
                 manager_gc(m, true);
 
 
                 manager_gc(m, true);
 
-                if (manager_dispatch_delayed(m) > 0)
-                        continue;
-
-                if (m->action_what != 0) {
-                        usec_t x, y;
-
-                        x = now(CLOCK_MONOTONIC);
-                        y = m->action_timestamp + m->inhibit_delay_max;
-
-                        us = x >= y ? 0 : y - x;
-                }
-
-                r = sd_event_run(m->event, us);
+                r = sd_event_run(m->event, (uint64_t) -1);
                 if (r < 0)
                         return r;
         }
                 if (r < 0)
                         return r;
         }
@@ -1058,11 +1152,9 @@ int main(int argc, char *argv[]) {
          * existence of /run/systemd/seats/ to determine whether
          * logind is available, so please always make sure this check
          * stays in. */
          * existence of /run/systemd/seats/ to determine whether
          * logind is available, so please always make sure this check
          * stays in. */
-        mkdir_label("/run/systemd", 0755);
         mkdir_label("/run/systemd/seats", 0755);
         mkdir_label("/run/systemd/users", 0755);
         mkdir_label("/run/systemd/sessions", 0755);
         mkdir_label("/run/systemd/seats", 0755);
         mkdir_label("/run/systemd/users", 0755);
         mkdir_label("/run/systemd/sessions", 0755);
-        mkdir_label("/run/systemd/machines", 0755);
 
         m = manager_new();
         if (!m) {
 
         m = manager_new();
         if (!m) {
@@ -1078,7 +1170,7 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
                 goto finish;
         }
 
-        log_debug("logind running as pid "PID_FMT, getpid());
+        log_debug("systemd-logind running as pid "PID_FMT, getpid());
 
         sd_notify(false,
                   "READY=1\n"
 
         sd_notify(false,
                   "READY=1\n"
@@ -1086,7 +1178,7 @@ int main(int argc, char *argv[]) {
 
         r = manager_run(m);
 
 
         r = manager_run(m);
 
-        log_debug("logind stopped as pid "PID_FMT, getpid());
+        log_debug("systemd-logind stopped as pid "PID_FMT, getpid());
 
 finish:
         sd_notify(false,
 
 finish:
         sd_notify(false,
index 04038bad204604cceb0b4405481ec26cb183f8fc..6df6f04c775b125b0e9f02198a842e0374c3b5d9 100644 (file)
@@ -12,6 +12,8 @@
 # See logind.conf(5) for details.
 
 [Login]
 # See logind.conf(5) for details.
 
 [Login]
+#NAutoVTs=6
+#ReserveVT=6
 #KillUserProcesses=no
 #KillOnlyUsers=
 #KillExcludeUsers=root
 #KillUserProcesses=no
 #KillOnlyUsers=
 #KillExcludeUsers=root
 #IdleActionSec=30min
 #RuntimeDirectorySize=10%
 #RemoveIPC=yes
 #IdleActionSec=30min
 #RuntimeDirectorySize=10%
 #RemoveIPC=yes
-
-[Sleep]
-#SuspendState=mem standby freeze
-#SuspendMode=
-#HibernateState=disk
-#HibernateMode=platform shutdown
-#HybridSleepState=disk
-#HybridSleepMode=suspend platform shutdown
index 6644ac08688c9629aab80712c7db60fa3b549d25..cd226f55fc8cc5502b5256817aaa548114885716 100644 (file)
@@ -65,6 +65,11 @@ struct Manager {
 
         int console_active_fd;
 
 
         int console_active_fd;
 
+        unsigned n_autovts;
+
+        unsigned reserve_vt;
+        int reserve_vt_fd;
+
         Seat *seat0;
 
         char **kill_only_users, **kill_exclude_users;
         Seat *seat0;
 
         char **kill_only_users, **kill_exclude_users;
@@ -73,6 +78,9 @@ struct Manager {
         unsigned long session_counter;
         unsigned long inhibit_counter;
 
         unsigned long session_counter;
         unsigned long inhibit_counter;
 
+        Hashmap *session_units;
+        Hashmap *user_units;
+
         usec_t inhibit_delay_max;
 
         /* If an action is currently being executed or is delayed,
         usec_t inhibit_delay_max;
 
         /* If an action is currently being executed or is delayed,
@@ -80,10 +88,26 @@ struct Manager {
         InhibitWhat action_what;
 
         /* If a shutdown/suspend was delayed due to a inhibitor this
         InhibitWhat action_what;
 
         /* If a shutdown/suspend was delayed due to a inhibitor this
-           contains the action we are supposed to perform after the
+           contains the unit name we are supposed to start after the
            delay is over */
            delay is over */
-        HandleAction pending_action;
-        usec_t action_timestamp;
+        const char *action_unit;
+
+        /* If a shutdown/suspend is currently executed, then this is
+         * the job of it */
+        char *action_job;
+        sd_event_source *inhibit_timeout_source;
+
+        char *scheduled_shutdown_type;
+        usec_t scheduled_shutdown_timeout;
+        sd_event_source *scheduled_shutdown_timeout_source;
+        uid_t scheduled_shutdown_uid;
+        char *scheduled_shutdown_tty;
+        sd_event_source *nologin_timeout_source;
+        bool unlink_nologin;
+
+        char *wall_message;
+        unsigned enable_wall_messages;
+        sd_event_source *wall_message_timeout_source;
 
         sd_event_source *idle_action_event_source;
         usec_t idle_action_usec;
 
         sd_event_source *idle_action_event_source;
         usec_t idle_action_usec;
@@ -111,9 +135,6 @@ struct Manager {
         size_t runtime_dir_size;
 };
 
         size_t runtime_dir_size;
 };
 
-Manager *manager_new(void);
-void manager_free(Manager *m);
-
 int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device);
 int manager_add_button(Manager *m, const char *name, Button **_button);
 int manager_add_seat(Manager *m, const char *id, Seat **_seat);
 int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device);
 int manager_add_button(Manager *m, const char *name, Button **_button);
 int manager_add_seat(Manager *m, const char *id, Seat **_seat);
@@ -126,10 +147,7 @@ int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor);
 int manager_process_seat_device(Manager *m, struct udev_device *d);
 int manager_process_button_device(Manager *m, struct udev_device *d);
 
 int manager_process_seat_device(Manager *m, struct udev_device *d);
 int manager_process_button_device(Manager *m, struct udev_device *d);
 
-int manager_startup(Manager *m);
-int manager_run(Manager *m);
-
-void manager_gc(Manager *m, bool drop_not_started);
+int manager_spawn_autovt(Manager *m, unsigned int vtnr);
 
 bool manager_shall_kill(Manager *m, const char *user);
 
 
 bool manager_shall_kill(Manager *m, const char *user);
 
@@ -138,16 +156,21 @@ int manager_get_idle_hint(Manager *m, dual_timestamp *t);
 int manager_get_user_by_pid(Manager *m, pid_t pid, User **user);
 int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session);
 
 int manager_get_user_by_pid(Manager *m, pid_t pid, User **user);
 int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session);
 
-bool manager_is_docked_or_external_displays(Manager *m);
+bool manager_is_docked(Manager *m);
+int manager_count_displays(Manager *m);
+bool manager_is_docked_or_multiple_displays(Manager *m);
 
 extern const sd_bus_vtable manager_vtable[];
 
 
 extern const sd_bus_vtable manager_vtable[];
 
-int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, HandleAction action, InhibitWhat w, sd_bus_error *error);
-int shutdown_or_sleep(HandleAction action);
+int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error);
 
 
-int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
+int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name, InhibitWhat w, sd_bus_error *error);
 
 
-int manager_dispatch_delayed(Manager *manager);
+int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
 
 int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_error *error, char **job);
 int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
 
 int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_error *error, char **job);
 int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
@@ -171,5 +194,5 @@ int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const ch
 int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret);
 int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret);
 
 int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret);
 int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret);
 
-
-int manager_dispatch_delayed(Manager *manager, bool timeout);
\ No newline at end of file
+int manager_setup_wall_message_timer(Manager *m);
+bool logind_wall_tty_filter(const char *tty, void *userdata);
index bafd483be2bbf39f42d09257b03decbeb5bef9fc..d70e959d86011952f0b6d563fe6b635bddda2ec3 100644 (file)
@@ -21,7 +21,6 @@
 
 #include "util.h"
 #include "path-util.h"
 
 #include "util.h"
 #include "path-util.h"
-#include "btrfs-util.h"
 #include "rm-rf.h"
 
 int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
 #include "rm-rf.h"
 
 int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
@@ -115,7 +114,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
                                 safe_close(subdir_fd);
                                 continue;
                         }
                                 safe_close(subdir_fd);
                                 continue;
                         }
-
+#if 0
                         if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) {
 
                                 /* This could be a subvolume, try to remove it */
                         if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) {
 
                                 /* This could be a subvolume, try to remove it */
@@ -139,7 +138,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
                                         continue;
                                 }
                         }
                                         continue;
                                 }
                         }
-
+#endif //
                         /* We pass REMOVE_PHYSICAL here, to avoid
                          * doing the fstatfs() to check the file
                          * system type again for each directory */
                         /* We pass REMOVE_PHYSICAL here, to avoid
                          * doing the fstatfs() to check the file
                          * system type again for each directory */
@@ -175,7 +174,7 @@ int rm_rf(const char *path, RemoveFlags flags) {
                 log_error("Attempted to remove entire root file system, and we can't allow that.");
                 return -EPERM;
         }
                 log_error("Attempted to remove entire root file system, and we can't allow that.");
                 return -EPERM;
         }
-
+#if 0
         if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) {
                 /* Try to remove as subvolume first */
                 r = btrfs_subvol_remove(path, true);
         if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) {
                 /* Try to remove as subvolume first */
                 r = btrfs_subvol_remove(path, true);
@@ -187,7 +186,7 @@ int rm_rf(const char *path, RemoveFlags flags) {
 
                 /* Not btrfs or not a subvolume */
         }
 
                 /* Not btrfs or not a subvolume */
         }
-
+#endif // 0
         fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
         if (fd < 0) {
 
         fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
         if (fd < 0) {