From 8af9597bdb2f17e0220c90299a322cfff3c4195f Mon Sep 17 00:00:00 2001 From: Sven Eden Date: Mon, 28 Nov 2016 04:53:40 +0100 Subject: [PATCH] Prep v220: Use new cgroups functions 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. --- src/libelogind/sd-bus/bus-creds.c | 47 +- src/login/71-seat.rules.in | 4 +- src/login/loginctl.c | 34 +- src/login/logind-action.c | 164 +---- src/login/logind-core.c | 64 +- src/login/logind-dbus.c | 957 ++++++++++++++++++++++++------ src/login/logind-gperf.gperf | 8 +- src/login/logind-seat.c | 32 + src/login/logind-session-dbus.c | 8 +- src/login/logind-session.c | 128 ++-- src/login/logind-session.h | 2 +- src/login/logind-user.c | 306 +++++++--- src/login/logind-user.h | 6 +- src/login/logind.c | 200 +++++-- src/login/logind.conf | 10 +- src/login/logind.h | 57 +- src/shared/rm-rf.c | 9 +- 17 files changed, 1449 insertions(+), 587 deletions(-) diff --git a/src/libelogind/sd-bus/bus-creds.c b/src/libelogind/sd-bus/bus-creds.c index 153b05859..1c365b7fc 100644 --- a/src/libelogind/sd-bus/bus-creds.c +++ b/src/libelogind/sd-bus/bus-creds.c @@ -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) { + int r; + 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); - 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) { + int r; + 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); - 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) { + int r; + 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); - 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; @@ -504,7 +537,7 @@ _public_ int sd_bus_creds_get_owner_uid(sd_bus_creds *c, uid_t *uid) { 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) { diff --git a/src/login/71-seat.rules.in b/src/login/71-seat.rules.in index d5aeacd7a..de55c9a4e 100644 --- a/src/login/71-seat.rules.in +++ b/src/login/71-seat.rules.in @@ -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}=="", \ - 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}" -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" diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 31400d962..c44209fe0 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -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 bool arg_ignore_inhibitors = false; +#if 0 static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; +#endif // 0 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); } -#endif + +#endif // 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); -#endif + +#endif // 0 } return 0; @@ -1195,9 +1198,6 @@ static int check_inhibitors(sd_bus *bus, const char *verb, const char *inhibit_w assert(bus); - if (arg_ignore_inhibitors) - 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" - " -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" @@ -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" +#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" +#endif // 0 "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" - " 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" @@ -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 }, - { "ignore-inhibitors", no_argument, NULL, 'i' }, +#if 0 { "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); - 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) { @@ -1549,7 +1551,7 @@ static int parse_argv(int argc, char *argv[]) { 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); @@ -1558,21 +1560,13 @@ static int parse_argv(int argc, char *argv[]) { break; case 'o': -#if 0 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; - - case 'i': - arg_ignore_inhibitors = true; - break; - +#endif // 0 case ARG_NO_PAGER: arg_no_pager = true; break; diff --git a/src/login/logind-action.c b/src/login/logind-action.c index 7a57e1ba2..f635fb1b6 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -20,20 +20,12 @@ ***/ #include -#include -#include - -#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 "special.h" #include "sleep-config.h" -#include "bus-error.h" #include "bus-util.h" +#include "bus-error.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..." }; + 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; @@ -151,7 +153,7 @@ int manager_handle_action( 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; @@ -160,144 +162,6 @@ int manager_handle_action( 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", diff --git a/src/login/logind-core.c b/src/login/logind-core.c index d5af8dcdc..440c32aa2 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -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) { - _cleanup_free_ char *session_name = NULL; + _cleanup_free_ char *unit = NULL; 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; - r = cg_pid_get_session(pid, &session_name); + r = cg_pid_get_unit(pid, &unit); if (r < 0) return 0; - s = hashmap_get(m->sessions, session_name); + s = hashmap_get(m->session_units, unit); 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) { - Session *s; + _cleanup_free_ char *unit = NULL; + User *u; 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; } @@ -429,6 +437,46 @@ static int vt_is_busy(unsigned int vtnr) { 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; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index c5ac94360..1f5cf865b 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -38,7 +38,12 @@ #include "bus-common-errors.h" #include "udev-util.h" #include "selinux-util.h" +#include "efivars.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; @@ -209,16 +214,42 @@ static int property_get_preparing( return sd_bus_message_append(reply, "b", b); } +static int property_get_scheduled_shutdown( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + int r; + + assert(bus); + assert(reply); + assert(m); + + r = sd_bus_message_open_container(reply, 'r', "st"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "st", m->scheduled_shutdown_type, m->scheduled_shutdown_timeout); + if (r < 0) + return r; + + return sd_bus_message_close_container(reply); +} + static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction); -static int method_get_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { +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; - assert(bus); 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); } -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; - assert(bus); 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); } -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; - assert(bus); 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); } -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; - assert(bus); 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); } -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; - assert(bus); 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); } -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; - assert(bus); 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; - 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; - assert(bus); 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; - 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; - assert(bus); 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; - 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; + assert(message); + assert(m); + 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; - 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; @@ -533,7 +560,6 @@ static int method_create_session(sd_bus *bus, sd_bus_message *message, void *use SessionClass c; int r; - assert(bus); 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); - /* 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; @@ -815,13 +835,12 @@ fail: 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; - assert(bus); 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; - session_add_to_gc_queue(session); - 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; - assert(bus); 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; - 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; - assert(bus); 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); } -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; - assert(bus); 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; - 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; - assert(bus); 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); } -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; - assert(bus); 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; - 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; - assert(bus); 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; - 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; - assert(bus); 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; - 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; - assert(bus); 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; - 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; - assert(bus); 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; - 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; @@ -1062,7 +1070,6 @@ static int method_set_user_linger(sd_bus *bus, sd_bus_message *message, void *us uint32_t uid; int interactive; - assert(bus); assert(message); assert(m); @@ -1242,12 +1249,11 @@ static int flush_devices(Manager *m) { 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; - assert(bus); 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); } -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; - assert(bus); assert(message); assert(m); @@ -1335,36 +1340,31 @@ static int have_multiple_sessions( static int bus_manager_log_shutdown( Manager *m, InhibitWhat w, - HandleAction action) { + const char *unit_name) { const char *p, *q; assert(m); + assert(unit_name); 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"; - break; - case HANDLE_HALT: + } else if (streq(unit_name, SPECIAL_HALT_TARGET)) { 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"; - break; - case HANDLE_KEXEC: + } else if (streq(unit_name, SPECIAL_KEXEC_TARGET)) { p = "MESSAGE=System is rebooting with kexec."; q = "SHUTDOWN=kexec"; - break; - default: + } else { p = "MESSAGE=System is shutting down."; q = NULL; - break; } 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, - HandleAction action, + const char *unit_name, 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); + 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; - /* 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; } +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, - HandleAction action) { + const char *unit_name) { + + int r; + usec_t timeout_val; 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; @@ -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, - HandleAction action, + const char *unit_name, InhibitWhat w, sd_bus_error *error) { @@ -1492,8 +1575,10 @@ int bus_manager_shutdown_or_sleep_now_or_later( int r; assert(m); + assert(unit_name); 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); @@ -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 */ - 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 */ - r = execute_shutdown_or_sleep(m, w, action, error); + r = execute_shutdown_or_sleep(m, w, unit_name, error); return r; } -static int method_do_shutdown_or_sleep( +static int verify_shutdown_creds( Manager *m, sd_bus_message *message, - HandleAction sleep_action, InhibitWhat w, + bool interactive, 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; - int interactive, r; uid_t uid; + int r; 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) @@ -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); - 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; @@ -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 */ } - 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; @@ -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 */ } - 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; @@ -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 */ } - 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); } -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, - 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, - method_poweroff, 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, - HANDLE_REBOOT, + SPECIAL_REBOOT_TARGET, INHIBIT_SHUTDOWN, "org.freedesktop.login1.reboot", "org.freedesktop.login1.reboot-multiple-sessions", "org.freedesktop.login1.reboot-ignore-inhibit", NULL, - method_reboot, 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, - HANDLE_SUSPEND, + SPECIAL_SUSPEND_TARGET, INHIBIT_SLEEP, "org.freedesktop.login1.suspend", "org.freedesktop.login1.suspend-multiple-sessions", "org.freedesktop.login1.suspend-ignore-inhibit", "suspend", - method_suspend, 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, - HANDLE_HIBERNATE, + SPECIAL_HIBERNATE_TARGET, INHIBIT_SLEEP, "org.freedesktop.login1.hibernate", "org.freedesktop.login1.hibernate-multiple-sessions", "org.freedesktop.login1.hibernate-ignore-inhibit", "hibernate", - method_hibernate, 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, - 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", - method_hybrid_sleep, 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) { - 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; @@ -1739,7 +2072,7 @@ static int method_can_shutdown_or_sleep( } 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; @@ -1755,7 +2088,7 @@ static int method_can_shutdown_or_sleep( /* 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; @@ -1770,7 +2103,7 @@ static int method_can_shutdown_or_sleep( 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( @@ -1783,7 +2116,7 @@ static int method_can_poweroff(sd_bus *bus, sd_bus_message *message, void *userd 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( @@ -1796,7 +2129,7 @@ static int method_can_reboot(sd_bus *bus, sd_bus_message *message, void *userdat 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( @@ -1809,7 +2142,7 @@ static int method_can_suspend(sd_bus *bus, sd_bus_message *message, void *userda 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( @@ -1822,7 +2155,7 @@ static int method_can_hibernate(sd_bus *bus, sd_bus_message *message, void *user 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( @@ -1835,7 +2168,100 @@ static int method_can_hybrid_sleep(sd_bus *bus, sd_bus_message *message, void *u 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; @@ -1848,7 +2274,6 @@ static int method_inhibit(sd_bus *bus, sd_bus_message *message, void *userdata, uid_t uid; int r; - assert(bus); assert(message); assert(m); @@ -1951,9 +2376,14 @@ fail: const sd_bus_vtable manager_vtable[] = { SD_BUS_VTABLE_START(0), + SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", NULL, NULL, offsetof(Manager, enable_wall_messages), 0), + SD_BUS_WRITABLE_PROPERTY("WallMessage", "s", NULL, NULL, offsetof(Manager, wall_message), 0), + + SD_BUS_PROPERTY("NAutoVTs", "u", NULL, offsetof(Manager, n_autovts), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST), 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), @@ -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("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), @@ -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("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), @@ -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("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), @@ -2020,56 +2455,228 @@ const sd_bus_vtable manager_vtable[] = { 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; - 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; + } - /* 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) { - 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 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( diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index 9705dd5b1..9218d098e 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -14,6 +14,8 @@ struct ConfigPerfItem; %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) @@ -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) -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) diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c index 5c15ff3a8..495ec50be 100644 --- a/src/login/logind-seat.c +++ b/src/login/logind-seat.c @@ -178,6 +178,34 @@ static int vt_allocate(unsigned int vtnr) { 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; @@ -328,6 +356,7 @@ int seat_active_vt_changed(Seat *s, unsigned int vtnr) { } r = seat_set_active(s, new_active); + manager_spawn_autovt(s->manager, vtnr); return r; } @@ -385,6 +414,9 @@ int seat_start(Seat *s) { LOG_MESSAGE("New seat %s.", s->id), NULL); + /* Initialize VT magic stuff */ + seat_preallocate_vts(s); + /* Read current VT */ seat_read_active_vt(s); diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index d51a6e4bb..563153e2d 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -701,6 +701,9 @@ int session_send_create_reply(Session *s, sd_bus_error *error) { 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; @@ -711,10 +714,9 @@ int session_send_create_reply(Session *s, sd_bus_error *error) { 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); - user_save(s->user); p = session_bus_path(s); if (!p) diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 350a597d1..39eab7183 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -36,12 +36,12 @@ #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" +#define RELEASE_USEC (20*USEC_PER_SEC) + 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); } + 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); @@ -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->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); @@ -305,6 +317,8 @@ int session_load(Session *s) { 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, @@ -479,21 +493,42 @@ int session_activate(Session *s) { return 0; } -static int session_start_cgroup(Session *s) { +static int session_start_scope(Session *s) { 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; } @@ -513,7 +548,8 @@ int session_start(Session *s) { if (r < 0) return r; - r = session_start_cgroup(s); + /* Create cgroup */ + r = session_start_scope(s); if (r < 0) return r; @@ -554,16 +590,31 @@ int session_start(Session *s) { 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; + char *job = NULL; int r; assert(s); + if (!s->scope) + return 0; + 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 0; @@ -586,7 +637,7 @@ int session_stop(Session *s, bool force) { session_remove_fifo(s); /* Kill cgroup */ - r = session_stop_cgroup(s, force); + r = session_stop_scope(s, force); s->stopping = true; @@ -648,6 +699,16 @@ int session_finalize(Session *s) { 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); @@ -657,10 +718,11 @@ int session_release(Session *s) { 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) { @@ -865,7 +927,10 @@ bool session_check_gc(Session *s, bool drop_not_started) { 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; @@ -888,7 +953,7 @@ SessionState session_get_state(Session *s) { 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)) @@ -900,23 +965,10 @@ SessionState session_get_state(Session *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) { diff --git a/src/login/logind-session.h b/src/login/logind-session.h index dffe5a9aa..854f30fbd 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -89,8 +89,8 @@ struct Session { char *service; char *desktop; - /* Always NULL. */ char *scope; + char *scope_job; Seat *seat; unsigned int vtnr; diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 2ea2067b2..71bff9672 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -26,6 +26,7 @@ #include "util.h" #include "mkdir.h" +#include "rm-rf.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 "smack-util.h" -#include "label.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; @@ -82,6 +83,19 @@ void user_free(User *u) { 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)); @@ -91,7 +105,7 @@ void user_free(User *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; @@ -99,6 +113,9 @@ static int user_save_internal(User *u) { 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; @@ -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->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); @@ -231,15 +258,6 @@ finish: 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; @@ -249,6 +267,10 @@ int user_load(User *u) { 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, @@ -298,10 +320,10 @@ static int user_mkdir_runtime_path(User *u) { } 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; - (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); @@ -329,10 +351,6 @@ static int user_mkdir_runtime_path(User *u) { 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; @@ -349,6 +367,72 @@ fail: 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; @@ -364,11 +448,15 @@ int user_start(User *u) { 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); @@ -383,6 +471,50 @@ int user_start(User *u) { 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; @@ -391,7 +523,7 @@ static int user_remove_runtime_path(User *u) { 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); @@ -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); - 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); @@ -429,6 +561,16 @@ int user_stop(User *u, bool force) { 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); @@ -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; - dual_timestamp ts = DUAL_TIMESTAMP_NULL; + dual_timestamp ts = { 0, 0 }; 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 (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; } @@ -556,6 +704,9 @@ UserState user_get_state(User *u) { if (u->stopping) return USER_CLOSING; + if (u->slice_job || u->service_job) + return USER_OPENING; + if (u->sessions) { bool all_closing = true; @@ -579,87 +730,62 @@ UserState user_get_state(User *u) { } int user_kill(User *u, int signo) { - Session *s; - int res = 0; - 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) { - 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. */ - log_debug("Electing new display for user %s", u->name); 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; - } - 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] = { diff --git a/src/login/logind-user.h b/src/login/logind-user.h index 902bb52aa..722247806 100644 --- a/src/login/logind-user.h +++ b/src/login/logind-user.h @@ -47,12 +47,12 @@ struct User { 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_job; + char *slice_job; + Session *display; dual_timestamp timestamp; diff --git a/src/login/logind.c b/src/login/logind.c index cd0b67119..bcc268be8 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -30,11 +30,14 @@ #include "conf-parser.h" #include "bus-util.h" #include "bus-error.h" -#include "udev-util.h" -#include "signal-util.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; @@ -43,7 +46,10 @@ Manager *manager_new(void) { 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; @@ -67,9 +73,13 @@ Manager *manager_new(void) { 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); - 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); @@ -93,7 +103,7 @@ fail: return NULL; } -void manager_free(Manager *m) { +static void manager_free(Manager *m) { 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->user_units); + hashmap_free(m->session_units); + 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); @@ -153,14 +170,23 @@ void manager_free(Manager *m) { if (m->udev) udev_unref(m->udev); + if (m->unlink_nologin) + unlink("/run/nologin"); + bus_verify_polkit_async_registry_free(m->polkit_registry); sd_bus_unref(m->bus); sd_event_unref(m->event); + safe_close(m->reserve_vt_fd); + 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); } @@ -538,31 +564,25 @@ static int manager_dispatch_console(sd_event_source *s, int fd, uint32_t revents 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); - r = sd_bus_message_read(message, "s", &cgroup); - if (r < 0) { - bus_log_parse_error(r); + if (m->reserve_vt <= 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; } @@ -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"); - 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"); @@ -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"); + 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"); @@ -707,8 +787,13 @@ static int manager_connect_console(Manager *m) { 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) @@ -794,10 +879,30 @@ static int manager_connect_udev(Manager *m) { 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; } -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; @@ -908,7 +1013,7 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us return 0; } -int manager_startup(Manager *m) { +static int manager_startup(Manager *m) { int r; Seat *seat; Session *session; @@ -971,6 +1076,9 @@ int manager_startup(Manager *m) { /* 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); @@ -992,14 +1100,12 @@ int manager_startup(Manager *m) { return 0; } -int manager_run(Manager *m) { +static int manager_run(Manager *m) { int r; assert(m); for (;;) { - usec_t us = (uint64_t) -1; - 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); - 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; } @@ -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. */ - 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/machines", 0755); m = manager_new(); if (!m) { @@ -1078,7 +1170,7 @@ int main(int argc, char *argv[]) { 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" @@ -1086,7 +1178,7 @@ int main(int argc, char *argv[]) { 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, diff --git a/src/login/logind.conf b/src/login/logind.conf index 04038bad2..6df6f04c7 100644 --- a/src/login/logind.conf +++ b/src/login/logind.conf @@ -12,6 +12,8 @@ # See logind.conf(5) for details. [Login] +#NAutoVTs=6 +#ReserveVT=6 #KillUserProcesses=no #KillOnlyUsers= #KillExcludeUsers=root @@ -30,11 +32,3 @@ #IdleActionSec=30min #RuntimeDirectorySize=10% #RemoveIPC=yes - -[Sleep] -#SuspendState=mem standby freeze -#SuspendMode= -#HibernateState=disk -#HibernateMode=platform shutdown -#HybridSleepState=disk -#HybridSleepMode=suspend platform shutdown diff --git a/src/login/logind.h b/src/login/logind.h index 6644ac086..cd226f55f 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -65,6 +65,11 @@ struct Manager { int console_active_fd; + unsigned n_autovts; + + unsigned reserve_vt; + int reserve_vt_fd; + Seat *seat0; char **kill_only_users, **kill_exclude_users; @@ -73,6 +78,9 @@ struct Manager { 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, @@ -80,10 +88,26 @@ struct Manager { 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 */ - 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; @@ -111,9 +135,6 @@ struct Manager { size_t runtime_dir_size; }; -Manager *manager_new(void); -void manager_free(Manager *m); - int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device); int manager_add_button(Manager *m, const char *name, Button **_button); int manager_add_seat(Manager *m, const char *id, Seat **_seat); @@ -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_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); @@ -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); -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[]; -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); @@ -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_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); diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c index bafd483be..d70e959d8 100644 --- a/src/shared/rm-rf.c +++ b/src/shared/rm-rf.c @@ -21,7 +21,6 @@ #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) { @@ -115,7 +114,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { safe_close(subdir_fd); continue; } - +#if 0 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; } } - +#endif // /* 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; } - +#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); @@ -187,7 +186,7 @@ int rm_rf(const char *path, RemoveFlags flags) { /* 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) { -- 2.30.2