diff --git a/SOURCES/0611-Added-option-check-inhibitors-for-non-tty-usage.patch b/SOURCES/0611-Added-option-check-inhibitors-for-non-tty-usage.patch new file mode 100644 index 0000000..c913f65 --- /dev/null +++ b/SOURCES/0611-Added-option-check-inhibitors-for-non-tty-usage.patch @@ -0,0 +1,222 @@ +From f875436b93c6c5e83f46ab32429c977db4f0b10c Mon Sep 17 00:00:00 2001 +From: Felix Stupp +Date: Thu, 29 Oct 2020 12:48:48 +0100 +Subject: [PATCH] Added option --check-inhibitors for non-tty usage + +As described in #2680, systemctl did ignore inhibitors if it is not +attached to a tty to allow scripts to ignore inhibitors automatically. +This pull request preserves this behavior but allows scripts to +explicit check inhibitors if required. + +The new parameter '--check-inhibitors=yes' enables this feature. +The old parameter '-i'/'--ignore-inhibitors' was deprecated in favor +of '--check-inhibitors=no', the default behaviour can be specified +with '--check-inhibitors=auto'. +The new parameter is also described in the documentations and shell +completions found here. + +(cherry picked from commit b8ebe378b49a31549b8531d4b3177095ef385d55) + +Related: #1269726 +--- + man/systemctl.xml | 38 ++++++++++++++++++++---------- + shell-completion/bash/systemctl.in | 7 ++++-- + shell-completion/zsh/_systemctl.in | 9 ++++++- + src/systemctl/systemctl.c | 37 +++++++++++++++++++++-------- + 4 files changed, 65 insertions(+), 26 deletions(-) + +diff --git a/man/systemctl.xml b/man/systemctl.xml +index ed60a0739f..9f0f4d46ea 100644 +--- a/man/systemctl.xml ++++ b/man/systemctl.xml +@@ -323,23 +323,35 @@ + + + ++ ++ ++ ++ ++ When system shutdown or sleep state is request, this option controls how to deal with ++ inhibitor locks. It takes one of auto, yes or ++ no. Defaults to auto, which will behave like ++ yes for interactive invocations (i.e. from a TTY) and no ++ for non-interactive invocations. ++ yes will let the request respect inhibitor locks. ++ no will let the request ignore inhibitor locks. ++ ++ Applications can establish inhibitor locks to avoid that certain important operations ++ (such as CD burning or suchlike) are interrupted by system shutdown or a sleep state. Any user may ++ take these locks and privileged users may override these locks. ++ If any locks are taken, shutdown and sleep state requests will normally fail (unless privileged) ++ and a list of active locks is printed. ++ However, if no is specified or auto is specified on a ++ non-interactive requests, the established locks are ignored and not shown, and the operation ++ attempted anyway, possibly requiring additional privileges. ++ May be overriden by . ++ ++ ++ + + +- + + +- When system shutdown or a sleep state is requested, +- ignore inhibitor locks. Applications can establish inhibitor +- locks to avoid that certain important operations (such as CD +- burning or suchlike) are interrupted by system shutdown or a +- sleep state. Any user may take these locks and privileged +- users may override these locks. If any locks are taken, +- shutdown and sleep state requests will normally fail +- (regardless of whether privileged or not) and a list of active locks +- is printed. However, if +- is specified, the locks are ignored and not printed, and the +- operation attempted anyway, possibly requiring additional +- privileges. ++ Shortcut for . + + + +diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in +index ba51ae0d34..0c7afea57d 100644 +--- a/shell-completion/bash/systemctl.in ++++ b/shell-completion/bash/systemctl.in +@@ -111,9 +111,9 @@ _systemctl () { + [STANDALONE]='--all -a --reverse --after --before --defaults --force -f --full -l --global + --help -h --no-ask-password --no-block --no-legend --no-pager --no-reload --no-wall --now + --quiet -q --system --user --version --runtime --recursive -r --firmware-setup +- --show-types -i --ignore-inhibitors --plain --failed --value --fail --dry-run --wait' ++ --show-types --plain --failed --value --fail --dry-run --wait' + [ARG]='--host -H --kill-who --property -p --signal -s --type -t --state --job-mode --root +- --preset-mode -n --lines -o --output -M --machine --message' ++ --preset-mode -n --lines -o --output -M --machine --message --check-inhibitors' + ) + + if __contains_word "--user" ${COMP_WORDS[*]}; then +@@ -163,6 +163,9 @@ _systemctl () { + --machine|-M) + comps=$( __get_machines ) + ;; ++ --check-inhibitors) ++ comps='auto yes no' ++ ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +diff --git a/shell-completion/zsh/_systemctl.in b/shell-completion/zsh/_systemctl.in +index 9f576ed77d..b3c51cc843 100644 +--- a/shell-completion/zsh/_systemctl.in ++++ b/shell-completion/zsh/_systemctl.in +@@ -363,6 +363,13 @@ _job_modes() { + _values -s , "${_modes[@]}" + } + ++(( $+functions[_systemctl_check_inhibitors] )) || ++ _systemctl_check_inhibitors() { ++ local -a _modes ++ _modes=(auto yes no) ++ _values -s , "${_modes[@]}" ++ } ++ + # Build arguments for "systemctl" to be used in completion. + local -a _modes; _modes=("--user" "--system") + # Use the last mode (they are exclusive and the last one is used). +@@ -380,7 +387,7 @@ _arguments -s \ + '--before[Show units ordered before]' \ + {-l,--full}"[Don't ellipsize unit names on output]" \ + '--show-types[When showing sockets, show socket type]' \ +- {-i,--ignore-inhibitors}'[When executing a job, ignore jobs dependencies]' \ ++ '--check-inhibitors[Specify if inhibitors should be checked]:mode:_systemctl_check_inhibitors' \ + {-q,--quiet}'[Suppress output]' \ + '--no-block[Do not wait until operation finished]' \ + '--no-legend[Do not print a legend, i.e. the column headers and the footer with hints]' \ +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 8bec798373..8bcbf6bf4b 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -121,7 +121,7 @@ static bool arg_no_wall = false; + static bool arg_no_reload = false; + static bool arg_value = false; + static bool arg_show_types = false; +-static bool arg_ignore_inhibitors = false; ++static int arg_check_inhibitors = -1; + static bool arg_dry_run = false; + static bool arg_quiet = false; + static bool arg_full = false; +@@ -3313,17 +3313,19 @@ static int logind_check_inhibitors(enum action a) { + char **s; + int r; + +- if (arg_ignore_inhibitors || arg_force > 0) ++ if (arg_check_inhibitors == 0 || arg_force > 0) + return 0; + + if (arg_when > 0) + return 0; + +- if (geteuid() == 0) +- return 0; ++ if (arg_check_inhibitors < 0) { ++ if (geteuid() == 0) ++ return 0; + +- if (!on_tty()) +- return 0; ++ if (!on_tty()) ++ return 0; ++ } + + if (arg_transport != BUS_TRANSPORT_LOCAL) + return 0; +@@ -7237,8 +7239,10 @@ static void systemctl_help(void) { + " When enqueuing a unit job, show full transaction\n" + " --show-types When showing sockets, explicitly show their type\n" + " --value When showing properties, only print the value\n" +- " -i --ignore-inhibitors\n" +- " When shutting down or sleeping, ignore inhibitors\n" ++ " --check-inhibitors=MODE\n" ++ " Specify if checking inhibitors before shutting down,\n" ++ " sleeping or hibernating\n" ++ " -i Shortcut for --check-inhibitors=no\n" + " --kill-who=WHO Who to send signal to\n" + " -s --signal=SIGNAL Which signal to send\n" + " --now Start or stop unit in addition to enabling or disabling it\n" +@@ -7475,6 +7479,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { + ARG_REVERSE, + ARG_AFTER, + ARG_BEFORE, ++ ARG_CHECK_INHIBITORS, + ARG_DRY_RUN, + ARG_SHOW_TYPES, + ARG_IRREVERSIBLE, +@@ -7520,7 +7525,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { + { "fail", no_argument, NULL, ARG_FAIL }, /* compatibility only */ + { "irreversible", no_argument, NULL, ARG_IRREVERSIBLE }, /* compatibility only */ + { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, /* compatibility only */ +- { "ignore-inhibitors", no_argument, NULL, 'i' }, ++ { "ignore-inhibitors", no_argument, NULL, 'i' }, /* compatibility only */ ++ { "check-inhibitors", required_argument, NULL, ARG_CHECK_INHIBITORS }, + { "value", no_argument, NULL, ARG_VALUE }, + { "user", no_argument, NULL, ARG_USER }, + { "system", no_argument, NULL, ARG_SYSTEM }, +@@ -7813,7 +7819,18 @@ static int systemctl_parse_argv(int argc, char *argv[]) { + break; + + case 'i': +- arg_ignore_inhibitors = true; ++ arg_check_inhibitors = 0; ++ break; ++ ++ case ARG_CHECK_INHIBITORS: ++ if (streq(optarg, "auto")) ++ arg_check_inhibitors = -1; ++ else { ++ r = parse_boolean(optarg); ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse --check-inhibitors= argument: %s", optarg); ++ arg_check_inhibitors = r; ++ } + break; + + case ARG_PLAIN: diff --git a/SOURCES/0612-logind-Introduce-RebootWithFlags-and-others.patch b/SOURCES/0612-logind-Introduce-RebootWithFlags-and-others.patch new file mode 100644 index 0000000..fc9c119 --- /dev/null +++ b/SOURCES/0612-logind-Introduce-RebootWithFlags-and-others.patch @@ -0,0 +1,769 @@ +From d375206fe822903b16f3b9006ea47ffd938d88cb Mon Sep 17 00:00:00 2001 +From: Deepak Rawat +Date: Mon, 25 Jan 2021 09:14:08 -0800 +Subject: [PATCH] logind: Introduce RebootWithFlags and others + +Add new systemd-logind WithFlags version for Reboot and others. These +methods add a unit64 parameter, with which can send additional control flags. + +(cherry picked from commit 00971ea5164e2e7a5f2d7ffe12a566b62d282556) + +Related: #1269726 +--- + src/basic/login-util.h | 8 + + src/login/logind-dbus.c | 498 +++++++++++++++++++++++++++++++----- + src/systemctl/systemctl.c | 26 ++ + src/systemd/sd-bus-vtable.h | 19 +- + 4 files changed, 480 insertions(+), 71 deletions(-) + +diff --git a/src/basic/login-util.h b/src/basic/login-util.h +index e1e62e12b7..9832207458 100644 +--- a/src/basic/login-util.h ++++ b/src/basic/login-util.h +@@ -4,6 +4,14 @@ + #include + #include + ++#define SD_LOGIND_ROOT_CHECK_INHIBITORS (UINT64_C(1) << 0) ++ ++/* For internal use only */ ++#define SD_LOGIND_INTERACTIVE (UINT64_C(1) << 63) ++ ++#define SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC (SD_LOGIND_ROOT_CHECK_INHIBITORS) ++#define SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_ALL (SD_LOGIND_ROOT_CHECK_INHIBITORS|SD_LOGIND_INTERACTIVE) ++ + bool session_id_valid(const char *id); + + static inline bool logind_running(void) { +diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c +index 3f122fcbd9..0c43fbb3e0 100644 +--- a/src/login/logind-dbus.c ++++ b/src/login/logind-dbus.c +@@ -1698,14 +1698,14 @@ static int verify_shutdown_creds( + Manager *m, + sd_bus_message *message, + InhibitWhat w, +- bool interactive, + const char *action, + const char *action_multiple_sessions, + const char *action_ignore_inhibit, ++ uint64_t flags, + sd_bus_error *error) { + + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; +- bool multiple_sessions, blocked; ++ bool multiple_sessions, blocked, interactive; + uid_t uid; + int r; + +@@ -1728,6 +1728,7 @@ static int verify_shutdown_creds( + + multiple_sessions = r > 0; + blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); ++ interactive = flags & SD_LOGIND_INTERACTIVE; + + if (multiple_sessions && action_multiple_sessions) { + r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, interactive, UID_INVALID, &m->polkit_registry, error); +@@ -1737,12 +1738,19 @@ static int verify_shutdown_creds( + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + } + +- if (blocked && action_ignore_inhibit) { +- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, interactive, 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 */ ++ if (blocked) { ++ /* We don't check polkit for root here, because you can't be more privileged than root */ ++ if (uid == 0 && (flags & SD_LOGIND_ROOT_CHECK_INHIBITORS)) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, ++ "Access denied to root due to active block inhibitor"); ++ ++ if (action_ignore_inhibit) { ++ r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, interactive, 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 */ ++ } + } + + if (!multiple_sessions && !blocked && action) { +@@ -1765,9 +1773,11 @@ static int method_do_shutdown_or_sleep( + const char *action_multiple_sessions, + const char *action_ignore_inhibit, + const char *sleep_verb, ++ bool with_flags, + sd_bus_error *error) { + +- int interactive, r; ++ int interactive = false, r; ++ uint64_t flags = 0; + + assert(m); + assert(message); +@@ -1775,10 +1785,20 @@ static int method_do_shutdown_or_sleep( + assert(w >= 0); + assert(w <= _INHIBIT_WHAT_MAX); + +- r = sd_bus_message_read(message, "b", &interactive); ++ if (with_flags) ++ r = sd_bus_message_read(message, "t", &flags); ++ else ++ r = sd_bus_message_read(message, "b", &interactive); ++ + if (r < 0) + return r; + ++ if (with_flags && (flags & ~SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC)) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, ++ "Invalid flags parameter"); ++ ++ SET_FLAG(flags, SD_LOGIND_INTERACTIVE, interactive); ++ + /* 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"); +@@ -1795,8 +1815,8 @@ static int method_do_shutdown_or_sleep( + return r; + } + +- r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions, +- action_ignore_inhibit, error); ++ r = verify_shutdown_creds(m, message, w, action, action_multiple_sessions, ++ action_ignore_inhibit, flags, error); + if (r != 0) + return r; + +@@ -1818,6 +1838,7 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error + "org.freedesktop.login1.power-off-multiple-sessions", + "org.freedesktop.login1.power-off-ignore-inhibit", + NULL, ++ sd_bus_message_is_method_call(message, NULL, "PowerOffWithFlags"), + error); + } + +@@ -1832,6 +1853,7 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error * + "org.freedesktop.login1.reboot-multiple-sessions", + "org.freedesktop.login1.reboot-ignore-inhibit", + NULL, ++ sd_bus_message_is_method_call(message, NULL, "RebootWithFlags"), + error); + } + +@@ -1846,6 +1868,7 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er + "org.freedesktop.login1.halt-multiple-sessions", + "org.freedesktop.login1.halt-ignore-inhibit", + NULL, ++ sd_bus_message_is_method_call(message, NULL, "HaltWithFlags"), + error); + } + +@@ -1860,6 +1883,7 @@ static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error + "org.freedesktop.login1.suspend-multiple-sessions", + "org.freedesktop.login1.suspend-ignore-inhibit", + "suspend", ++ sd_bus_message_is_method_call(message, NULL, "SuspendWithFlags"), + error); + } + +@@ -1874,6 +1898,7 @@ static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_erro + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "hibernate", ++ sd_bus_message_is_method_call(message, NULL, "HibernateWithFlags"), + error); + } + +@@ -1888,6 +1913,7 @@ static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_e + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "hybrid-sleep", ++ sd_bus_message_is_method_call(message, NULL, "HybridSleepWithFlags"), + error); + } + +@@ -1902,6 +1928,7 @@ static int method_suspend_then_hibernate(sd_bus_message *message, void *userdata + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "hybrid-sleep", ++ sd_bus_message_is_method_call(message, NULL, "SuspendThenHibernateWithFlags"), + error); + } + +@@ -2089,8 +2116,8 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_ + } 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); ++ r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, action, action_multiple_sessions, ++ action_ignore_inhibit, 0, error); + if (r != 0) + return r; + +@@ -2683,60 +2710,395 @@ const sd_bus_vtable manager_vtable[] = { + SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_hashmap_size, offsetof(Manager, sessions), 0), + SD_BUS_PROPERTY("UserTasksMax", "t", property_get_compat_user_tasks_max, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + +- SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("GetUser", "u", "o", method_get_user, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("GetUserByPID", "u", "o", method_get_user_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("GetSeat", "s", "o", method_get_seat, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("ListSessions", NULL, "a(susso)", method_list_sessions, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("ListUsers", NULL, "a(uso)", method_list_users, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("ListSeats", NULL, "a(so)", method_list_seats, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("ListInhibitors", NULL, "a(ssssuu)", method_list_inhibitors, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("CreateSession", "uusssssussbssa(sv)", "soshusub", method_create_session, 0), +- SD_BUS_METHOD("ReleaseSession", "s", NULL, method_release_session, 0), +- SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("ActivateSessionOnSeat", "ss", NULL, method_activate_session_on_seat, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("LockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("UnlockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("LockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("UnlockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("KillSession", "ssi", NULL, method_kill_session, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("KillUser", "ui", NULL, method_kill_user, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("TerminateSession", "s", NULL, method_terminate_session, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("TerminateUser", "u", NULL, method_terminate_user, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("TerminateSeat", "s", NULL, method_terminate_seat, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("SetUserLinger", "ubb", NULL, method_set_user_linger, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("AttachDevice", "ssb", NULL, method_attach_device, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("FlushDevices", "b", NULL, method_flush_devices, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("Halt", "b", NULL, method_halt, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, 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("SuspendThenHibernate", "b", NULL, method_suspend_then_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("CanReboot", NULL, "s", method_can_reboot, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("CanHalt", NULL, "s", method_can_halt, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("CanSuspend", NULL, "s", method_can_suspend, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("CanSuspendThenHibernate", NULL, "s", method_can_suspend_then_hibernate, 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("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_METHOD("SetWallMessage", "sb", NULL, method_set_wall_message, SD_BUS_VTABLE_UNPRIVILEGED), +- +- SD_BUS_SIGNAL("SessionNew", "so", 0), +- SD_BUS_SIGNAL("SessionRemoved", "so", 0), +- SD_BUS_SIGNAL("UserNew", "uo", 0), +- SD_BUS_SIGNAL("UserRemoved", "uo", 0), +- SD_BUS_SIGNAL("SeatNew", "so", 0), +- SD_BUS_SIGNAL("SeatRemoved", "so", 0), +- SD_BUS_SIGNAL("PrepareForShutdown", "b", 0), +- SD_BUS_SIGNAL("PrepareForSleep", "b", 0), ++ SD_BUS_METHOD_WITH_NAMES("GetSession", ++ "s", ++ SD_BUS_PARAM(session_id), ++ "o", ++ SD_BUS_PARAM(object_path), ++ method_get_session, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("GetSessionByPID", ++ "u", ++ SD_BUS_PARAM(pid), ++ "o", ++ SD_BUS_PARAM(object_path), ++ method_get_session_by_pid, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("GetUser", ++ "u", ++ SD_BUS_PARAM(uid), ++ "o", ++ SD_BUS_PARAM(object_path), ++ method_get_user, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("GetUserByPID", ++ "u", ++ SD_BUS_PARAM(pid), ++ "o", ++ SD_BUS_PARAM(object_path), ++ method_get_user_by_pid, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("GetSeat", ++ "s", ++ SD_BUS_PARAM(seat_id), ++ "o", ++ SD_BUS_PARAM(object_path), ++ method_get_seat, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("ListSessions", ++ NULL,, ++ "a(susso)", ++ SD_BUS_PARAM(sessions), ++ method_list_sessions, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("ListUsers", ++ NULL,, ++ "a(uso)", ++ SD_BUS_PARAM(users), ++ method_list_users, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("ListSeats", ++ NULL,, ++ "a(so)", ++ SD_BUS_PARAM(seats), ++ method_list_seats, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("ListInhibitors", ++ NULL,, ++ "a(ssssuu)", ++ SD_BUS_PARAM(inhibitors), ++ method_list_inhibitors, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("CreateSession", ++ "uusssssussbssa(sv)", ++ SD_BUS_PARAM(uid) ++ SD_BUS_PARAM(pid) ++ SD_BUS_PARAM(service) ++ SD_BUS_PARAM(type) ++ SD_BUS_PARAM(class) ++ SD_BUS_PARAM(desktop) ++ SD_BUS_PARAM(seat_id) ++ SD_BUS_PARAM(vtnr) ++ SD_BUS_PARAM(tty) ++ SD_BUS_PARAM(display) ++ SD_BUS_PARAM(remote) ++ SD_BUS_PARAM(remote_user) ++ SD_BUS_PARAM(remote_host) ++ SD_BUS_PARAM(properties), ++ "soshusub", ++ SD_BUS_PARAM(session_id) ++ SD_BUS_PARAM(object_path) ++ SD_BUS_PARAM(runtime_path) ++ SD_BUS_PARAM(fifo_fd) ++ SD_BUS_PARAM(uid) ++ SD_BUS_PARAM(seat_id) ++ SD_BUS_PARAM(vtnr) ++ SD_BUS_PARAM(existing), ++ method_create_session, ++ 0), ++ SD_BUS_METHOD_WITH_NAMES("ReleaseSession", ++ "s", ++ SD_BUS_PARAM(session_id), ++ NULL,, ++ method_release_session, ++ 0), ++ SD_BUS_METHOD_WITH_NAMES("ActivateSession", ++ "s", ++ SD_BUS_PARAM(session_id), ++ NULL,, ++ method_activate_session, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("ActivateSessionOnSeat", ++ "ss", ++ SD_BUS_PARAM(session_id) ++ SD_BUS_PARAM(seat_id), ++ NULL,, ++ method_activate_session_on_seat, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("LockSession", ++ "s", ++ SD_BUS_PARAM(session_id), ++ NULL,, ++ method_lock_session, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("UnlockSession", ++ "s", ++ SD_BUS_PARAM(session_id), ++ NULL,, ++ method_lock_session, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("LockSessions", ++ NULL, ++ NULL, ++ method_lock_sessions, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("UnlockSessions", ++ NULL, ++ NULL, ++ method_lock_sessions, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("KillSession", ++ "ssi", ++ SD_BUS_PARAM(session_id) ++ SD_BUS_PARAM(who) ++ SD_BUS_PARAM(signal_number), ++ NULL,, ++ method_kill_session, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("KillUser", ++ "ui", ++ SD_BUS_PARAM(uid) ++ SD_BUS_PARAM(signal_number), ++ NULL,, ++ method_kill_user, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("TerminateSession", ++ "s", ++ SD_BUS_PARAM(session_id), ++ NULL,, ++ method_terminate_session, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("TerminateUser", ++ "u", ++ SD_BUS_PARAM(uid), ++ NULL,, ++ method_terminate_user, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("TerminateSeat", ++ "s", ++ SD_BUS_PARAM(seat_id), ++ NULL,, ++ method_terminate_seat, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("SetUserLinger", ++ "ubb", ++ SD_BUS_PARAM(uid) ++ SD_BUS_PARAM(enable) ++ SD_BUS_PARAM(interactive), ++ NULL,, ++ method_set_user_linger, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("AttachDevice", ++ "ssb", ++ SD_BUS_PARAM(seat_id) ++ SD_BUS_PARAM(sysfs_path) ++ SD_BUS_PARAM(interactive), ++ NULL,, ++ method_attach_device, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("FlushDevices", ++ "b", ++ SD_BUS_PARAM(interactive), ++ NULL,, ++ method_flush_devices, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("PowerOff", ++ "b", ++ SD_BUS_PARAM(interactive), ++ NULL,, ++ method_poweroff, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("PowerOffWithFlags", ++ "t", ++ SD_BUS_PARAM(flags), ++ NULL,, ++ method_poweroff, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("Reboot", ++ "b", ++ SD_BUS_PARAM(interactive), ++ NULL,, ++ method_reboot, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("RebootWithFlags", ++ "t", ++ SD_BUS_PARAM(flags), ++ NULL,, ++ method_reboot, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("Halt", ++ "b", ++ SD_BUS_PARAM(interactive), ++ NULL,, ++ method_halt, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("HaltWithFlags", ++ "t", ++ SD_BUS_PARAM(flags), ++ NULL,, ++ method_halt, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("Suspend", ++ "b", ++ SD_BUS_PARAM(interactive), ++ NULL,, ++ method_suspend, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("SuspendWithFlags", ++ "t", ++ SD_BUS_PARAM(flags), ++ NULL,, ++ method_suspend, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("Hibernate", ++ "b", ++ SD_BUS_PARAM(interactive), ++ NULL,, ++ method_hibernate, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("HibernateWithFlags", ++ "t", ++ SD_BUS_PARAM(flags), ++ NULL,, ++ method_hibernate, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("HybridSleep", ++ "b", ++ SD_BUS_PARAM(interactive), ++ NULL,, ++ method_hybrid_sleep, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("HybridSleepWithFlags", ++ "t", ++ SD_BUS_PARAM(flags), ++ NULL,, ++ method_hybrid_sleep, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("SuspendThenHibernate", ++ "b", ++ SD_BUS_PARAM(interactive), ++ NULL,, ++ method_suspend_then_hibernate, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("SuspendThenHibernateWithFlags", ++ "t", ++ SD_BUS_PARAM(flags), ++ NULL,, ++ method_suspend_then_hibernate, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("CanPowerOff", ++ NULL,, ++ "s", ++ SD_BUS_PARAM(result), ++ method_can_poweroff, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("CanReboot", ++ NULL,, ++ "s", ++ SD_BUS_PARAM(result), ++ method_can_reboot, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("CanHalt", ++ NULL,, ++ "s", ++ SD_BUS_PARAM(result), ++ method_can_halt, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("CanSuspend", ++ NULL,, ++ "s", ++ SD_BUS_PARAM(result), ++ method_can_suspend, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("CanHibernate", ++ NULL,, ++ "s", ++ SD_BUS_PARAM(result), ++ method_can_hibernate, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("CanHybridSleep", ++ NULL,, ++ "s", ++ SD_BUS_PARAM(result), ++ method_can_hybrid_sleep, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("CanSuspendThenHibernate", ++ NULL,, ++ "s", ++ SD_BUS_PARAM(result), ++ method_can_suspend_then_hibernate, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("ScheduleShutdown", ++ "st", ++ SD_BUS_PARAM(type) ++ SD_BUS_PARAM(usec), ++ NULL,, ++ method_schedule_shutdown, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("CancelScheduledShutdown", ++ NULL,, ++ "b", ++ SD_BUS_PARAM(cancelled), ++ method_cancel_scheduled_shutdown, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("Inhibit", ++ "ssss", ++ SD_BUS_PARAM(what) ++ SD_BUS_PARAM(who) ++ SD_BUS_PARAM(why) ++ SD_BUS_PARAM(mode), ++ "h", ++ SD_BUS_PARAM(pipe_fd), ++ method_inhibit, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("CanRebootToFirmwareSetup", ++ NULL,, ++ "s", ++ SD_BUS_PARAM(result), ++ method_can_reboot_to_firmware_setup, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("SetRebootToFirmwareSetup", ++ "b", ++ SD_BUS_PARAM(enable), ++ NULL,, ++ method_set_reboot_to_firmware_setup, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD_WITH_NAMES("SetWallMessage", ++ "sb", ++ SD_BUS_PARAM(wall_message) ++ SD_BUS_PARAM(enable), ++ NULL,, ++ method_set_wall_message, ++ SD_BUS_VTABLE_UNPRIVILEGED), ++ ++ SD_BUS_SIGNAL_WITH_NAMES("SessionNew", ++ "so", ++ SD_BUS_PARAM(session_id) ++ SD_BUS_PARAM(object_path), ++ 0), ++ SD_BUS_SIGNAL_WITH_NAMES("SessionRemoved", ++ "so", ++ SD_BUS_PARAM(session_id) ++ SD_BUS_PARAM(object_path), ++ 0), ++ SD_BUS_SIGNAL_WITH_NAMES("UserNew", ++ "uo", ++ SD_BUS_PARAM(uid) ++ SD_BUS_PARAM(object_path), ++ 0), ++ SD_BUS_SIGNAL_WITH_NAMES("UserRemoved", ++ "uo", ++ SD_BUS_PARAM(uid) ++ SD_BUS_PARAM(object_path), ++ 0), ++ SD_BUS_SIGNAL_WITH_NAMES("SeatNew", ++ "so", ++ SD_BUS_PARAM(seat_id) ++ SD_BUS_PARAM(object_path), ++ 0), ++ SD_BUS_SIGNAL_WITH_NAMES("SeatRemoved", ++ "so", ++ SD_BUS_PARAM(seat_id) ++ SD_BUS_PARAM(object_path), ++ 0), ++ SD_BUS_SIGNAL_WITH_NAMES("PrepareForShutdown", ++ "b", ++ SD_BUS_PARAM(start), ++ 0), ++ SD_BUS_SIGNAL_WITH_NAMES("PrepareForSleep", ++ "b", ++ SD_BUS_PARAM(start), ++ 0), + + SD_BUS_VTABLE_END + }; +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 8bcbf6bf4b..3dd7c1522f 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -50,6 +50,7 @@ + #include "list.h" + #include "locale-util.h" + #include "log.h" ++#include "login-util.h" + #include "logs-show.h" + #include "macro.h" + #include "mkdir.h" +@@ -3266,6 +3267,8 @@ static int logind_reboot(enum action a) { + }; + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; ++ const char *method_with_flags; ++ uint64_t flags = 0; + sd_bus *bus; + int r; + +@@ -3284,6 +3287,29 @@ static int logind_reboot(enum action a) { + if (arg_dry_run) + return 0; + ++ SET_FLAG(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS, arg_check_inhibitors > 0); ++ ++ method_with_flags = strjoina(actions[a].method, "WithFlags"); ++ ++ r = sd_bus_call_method( ++ bus, ++ "org.freedesktop.login1", ++ "/org/freedesktop/login1", ++ "org.freedesktop.login1.Manager", ++ method_with_flags, ++ &error, ++ NULL, ++ "t", flags); ++ if (r >= 0) ++ return 0; ++ ++ if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) ++ return log_error_errno(r, "Failed to %s via logind: %s", actions[a].description, bus_error_message(&error, r)); ++ ++ /* Fallback to original methods in case there is older version of systemd-logind */ ++ log_debug("Method %s not available: %s. Falling back to %s", method_with_flags, bus_error_message(&error, r), actions[a].method); ++ sd_bus_error_free(&error); ++ + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", +diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h +index 1268085498..8805e19477 100644 +--- a/src/systemd/sd-bus-vtable.h ++++ b/src/systemd/sd-bus-vtable.h +@@ -65,10 +65,12 @@ struct sd_bus_vtable { + const char *result; + sd_bus_message_handler_t handler; + size_t offset; ++ const char *names; + } method; + struct { + const char *member; + const char *signature; ++ const char *names; + } signal; + struct { + const char *member; +@@ -91,7 +93,10 @@ struct sd_bus_vtable { + }, \ + } + +-#define SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, _offset, _flags) \ ++/* helper macro to format method and signal parameters, one at a time */ ++#define SD_BUS_PARAM(x) #x "\0" ++ ++#define SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, _signature, _in_names, _result, _out_names, _handler, _offset, _flags) \ + { \ + .type = _SD_BUS_VTABLE_METHOD, \ + .flags = _flags, \ +@@ -102,13 +107,18 @@ struct sd_bus_vtable { + .result = _result, \ + .handler = _handler, \ + .offset = _offset, \ ++ .names = _in_names _out_names, \ + }, \ + }, \ + } ++#define SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, _offset, _flags) \ ++ SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, _signature, "", _result, "", _handler, _offset, _flags) ++#define SD_BUS_METHOD_WITH_NAMES(_member, _signature, _in_names, _result, _out_names, _handler, _flags) \ ++ SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, _signature, _in_names, _result, _out_names, _handler, 0, _flags) + #define SD_BUS_METHOD(_member, _signature, _result, _handler, _flags) \ +- SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, 0, _flags) ++ SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, _signature, "", _result, "", _handler, 0, _flags) + +-#define SD_BUS_SIGNAL(_member, _signature, _flags) \ ++#define SD_BUS_SIGNAL_WITH_NAMES(_member, _signature, _out_names, _flags) \ + { \ + .type = _SD_BUS_VTABLE_SIGNAL, \ + .flags = _flags, \ +@@ -116,9 +126,12 @@ struct sd_bus_vtable { + .signal = { \ + .member = _member, \ + .signature = _signature, \ ++ .names = _out_names, \ + }, \ + }, \ + } ++#define SD_BUS_SIGNAL(_member, _signature, _flags) \ ++ SD_BUS_SIGNAL_WITH_NAMES(_member, _signature, "", _flags) + + #define SD_BUS_PROPERTY(_member, _signature, _get, _offset, _flags) \ + { \ diff --git a/SOURCES/0613-logind-add-WithFlags-methods-to-policy.patch b/SOURCES/0613-logind-add-WithFlags-methods-to-policy.patch new file mode 100644 index 0000000..753c9d9 --- /dev/null +++ b/SOURCES/0613-logind-add-WithFlags-methods-to-policy.patch @@ -0,0 +1,85 @@ +From e1b18ab36b2457a4896e531f03713b198725c919 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 9 Mar 2021 09:03:58 +0100 +Subject: [PATCH] =?UTF-8?q?logind:=20add=20=E2=80=A6WithFlags=20methods=20?= + =?UTF-8?q?to=20policy?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Without this, privilege escalation through polkit does not work, because all +methods fail with permission errors. + +Forgotten in 8885fed4e3a52cf1bf105e42043203c485ed9d92. +Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1933335. + +(cherry picked from commit 2280db756eaff795091871feee8e47d4f6989a58) + +Related: #1269726 +--- + src/login/org.freedesktop.login1.conf | 28 +++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf +index f880f3e2da..dcde0c22c6 100644 +--- a/src/login/org.freedesktop.login1.conf ++++ b/src/login/org.freedesktop.login1.conf +@@ -130,30 +130,58 @@ + send_interface="org.freedesktop.login1.Manager" + send_member="PowerOff"/> + ++ ++ + + ++ ++ + + ++ ++ + + ++ ++ + + ++ ++ + + ++ ++ + + ++ ++ + diff --git a/SOURCES/0614-logind-simplify-flags-handling-a-bit.patch b/SOURCES/0614-logind-simplify-flags-handling-a-bit.patch new file mode 100644 index 0000000..fb61d3c --- /dev/null +++ b/SOURCES/0614-logind-simplify-flags-handling-a-bit.patch @@ -0,0 +1,72 @@ +From 6751217a032dd1a8e8ee324332f29786265f0ebe Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 2 Feb 2021 15:27:30 +0100 +Subject: [PATCH] logind: simplify flags handling a bit + +Let's split out the two codepaths a bit, and emphasize which ones it the +new-style and which the old-style codepath, and let's clearly convert +the params of the old-stye into the new style for further processing, so +that the old style path is brief and isolated. + +No change in behaviour. + +Follow-up for: 8885fed4e3a52cf1bf105e42043203c485ed9d92 + +(cherry picked from commit d3e99bc0c7f785dcf4e73cfed12f74002e73be5f) + +Related: #1269726 +--- + src/login/logind-dbus.c | 30 ++++++++++++++++++------------ + 1 file changed, 18 insertions(+), 12 deletions(-) + +diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c +index 0c43fbb3e0..ae9abc9bce 100644 +--- a/src/login/logind-dbus.c ++++ b/src/login/logind-dbus.c +@@ -1776,8 +1776,8 @@ static int method_do_shutdown_or_sleep( + bool with_flags, + sd_bus_error *error) { + +- int interactive = false, r; +- uint64_t flags = 0; ++ uint64_t flags; ++ int r; + + assert(m); + assert(message); +@@ -1785,19 +1785,25 @@ static int method_do_shutdown_or_sleep( + assert(w >= 0); + assert(w <= _INHIBIT_WHAT_MAX); + +- if (with_flags) ++ if (with_flags) { ++ /* New style method: with flags parameter (and interactive bool in the bus message header) */ + r = sd_bus_message_read(message, "t", &flags); +- else +- r = sd_bus_message_read(message, "b", &interactive); +- +- if (r < 0) +- return r; ++ if (r < 0) ++ return r; ++ if ((flags & ~SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC) != 0) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter"); ++ } else { ++ /* Old style method: no flags parameter, but interactive bool passed as boolean in ++ * payload. Let's convert this argument to the new-style flags parameter for our internal ++ * use. */ ++ int interactive; + +- if (with_flags && (flags & ~SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC)) +- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, +- "Invalid flags parameter"); ++ r = sd_bus_message_read(message, "b", &interactive); ++ if (r < 0) ++ return r; + +- SET_FLAG(flags, SD_LOGIND_INTERACTIVE, interactive); ++ flags = interactive ? SD_LOGIND_INTERACTIVE : 0; ++ } + + /* Don't allow multiple jobs being executed at the same time */ + if (m->action_what) diff --git a/SOURCES/0615-Update-link-to-RHEL-documentation.patch b/SOURCES/0615-Update-link-to-RHEL-documentation.patch new file mode 100644 index 0000000..922b826 --- /dev/null +++ b/SOURCES/0615-Update-link-to-RHEL-documentation.patch @@ -0,0 +1,25 @@ +From 48b893c770aed3214586d529ddaba14267818c33 Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Thu, 15 Jul 2021 10:35:08 +0200 +Subject: [PATCH] Update link to RHEL documentation + +RHEL-only + +Resolves: #1982584 +--- + man/systemctl.xml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/man/systemctl.xml b/man/systemctl.xml +index 9f0f4d46ea..a71e6c7c4f 100644 +--- a/man/systemctl.xml ++++ b/man/systemctl.xml +@@ -2017,7 +2017,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + For examples how to use systemctl in comparsion + with old service and chkconfig command please see: +- ++ + Managing System Services + + diff --git a/SOURCES/0616-Set-default-core-ulimit-to-0-but-keep-the-hard-limit.patch b/SOURCES/0616-Set-default-core-ulimit-to-0-but-keep-the-hard-limit.patch new file mode 100644 index 0000000..a1893a2 --- /dev/null +++ b/SOURCES/0616-Set-default-core-ulimit-to-0-but-keep-the-hard-limit.patch @@ -0,0 +1,29 @@ +From e2f5e8ccb27f48717b50339f58582d70ad5f59b1 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 3 Aug 2021 11:52:36 +0200 +Subject: [PATCH] Set default core ulimit to 0, but keep the hard limit + ulimited + +so users can change it later. + +Follow-up to 830bd662276ee117e65a4b3d541f77e8b172eafd. + +rhel-only +Resolves: #1905582 +--- + src/core/system.conf.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/system.conf.in b/src/core/system.conf.in +index b4d6dfa15a..84246c0e36 100644 +--- a/src/core/system.conf.in ++++ b/src/core/system.conf.in +@@ -52,7 +52,7 @@ + #DefaultLimitFSIZE= + #DefaultLimitDATA= + #DefaultLimitSTACK= +-DefaultLimitCORE=0 ++DefaultLimitCORE=0:infinity + #DefaultLimitRSS= + #DefaultLimitNOFILE= + #DefaultLimitAS= diff --git a/SOURCES/0617-shared-seccomp-util-address-family-filtering-is-brok.patch b/SOURCES/0617-shared-seccomp-util-address-family-filtering-is-brok.patch new file mode 100644 index 0000000..f3f13ad --- /dev/null +++ b/SOURCES/0617-shared-seccomp-util-address-family-filtering-is-brok.patch @@ -0,0 +1,76 @@ +From 3cf73fa4599116da350a0100378e749bbcbcad37 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 26 Nov 2020 11:23:54 +0100 +Subject: [PATCH] shared/seccomp-util: address family filtering is broken on + ppc + +This reverts the gist of da1921a5c396547261c8c7fcd94173346eb3b718 and +0d9fca76bb69e162265b2d25cb79f1890c0da31b (for ppc). + +Quoting #17559: +> libseccomp 2.5 added socket syscall multiplexing on ppc64(el): +> https://github.com/seccomp/libseccomp/pull/229 +> +> Like with i386, s390 and s390x this breaks socket argument filtering, so +> RestrictAddressFamilies doesn't work. +> +> This causes the unit test to fail: +> /* test_restrict_address_families */ +> Operating on architecture: ppc +> Failed to install socket family rules for architecture ppc, skipping: Operation canceled +> Operating on architecture: ppc64 +> Failed to add socket() rule for architecture ppc64, skipping: Invalid argument +> Operating on architecture: ppc64-le +> Failed to add socket() rule for architecture ppc64-le, skipping: Invalid argument +> Assertion 'fd < 0' failed at src/test/test-seccomp.c:424, function test_restrict_address_families(). Aborting. +> +> The socket filters can't be added so `socket(AF_UNIX, SOCK_DGRAM, 0);` still +> works, triggering the assertion. + +Fixes #17559. + +(cherry picked from commit d5923e38bc0e6cf9d7620ed5f1f8606fe7fe1168) + +Resolves: #1982650 +--- + src/shared/seccomp-util.c | 6 +++--- + src/test/test-seccomp.c | 2 +- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c +index e903512d45..c57c409433 100644 +--- a/src/shared/seccomp-util.c ++++ b/src/shared/seccomp-util.c +@@ -1251,9 +1251,6 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist) { + case SCMP_ARCH_X32: + case SCMP_ARCH_ARM: + case SCMP_ARCH_AARCH64: +- case SCMP_ARCH_PPC: +- case SCMP_ARCH_PPC64: +- case SCMP_ARCH_PPC64LE: + case SCMP_ARCH_MIPSEL64N32: + case SCMP_ARCH_MIPS64N32: + case SCMP_ARCH_MIPSEL64: +@@ -1267,6 +1264,9 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist) { + case SCMP_ARCH_X86: + case SCMP_ARCH_MIPSEL: + case SCMP_ARCH_MIPS: ++ case SCMP_ARCH_PPC: ++ case SCMP_ARCH_PPC64: ++ case SCMP_ARCH_PPC64LE: + default: + /* These we either know we don't support (i.e. are the ones that do use socketcall()), or we + * don't know */ +diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c +index 009a2e1922..5eb1c78b8b 100644 +--- a/src/test/test-seccomp.c ++++ b/src/test/test-seccomp.c +@@ -25,7 +25,7 @@ + #include "util.h" + #include "virt.h" + +-#if SCMP_SYS(socket) < 0 || defined(__i386__) || defined(__s390x__) || defined(__s390__) ++#if SCMP_SYS(socket) < 0 || defined(__i386__) || defined(__s390x__) || defined(__s390__) || defined(__powerpc64__) || defined(__powerpc__) + /* On these archs, socket() is implemented via the socketcall() syscall multiplexer, + * and we can't restrict it hence via seccomp. */ + # define SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN 1 diff --git a/SOURCES/0618-logind-rework-Seat-Session-User-object-allocation-an.patch b/SOURCES/0618-logind-rework-Seat-Session-User-object-allocation-an.patch new file mode 100644 index 0000000..bddc651 --- /dev/null +++ b/SOURCES/0618-logind-rework-Seat-Session-User-object-allocation-an.patch @@ -0,0 +1,341 @@ +From 019b3a5d7530c51aa8f7f1e5f5cb5eb81113d4db Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 3 Aug 2018 18:53:09 +0200 +Subject: [PATCH] logind: rework Seat/Session/User object allocation and + freeing a bit + +Let's update things a bit to follow current practices: + +- User structure initialization rather than zero-initialized allocation + +- Always propagate proper errors from allocation functions + +- Use _cleanup_ for freeing objects when allocation fails half-way + +- Make destructors return NULL + +(cherry picked from commit 8c29a4570993105fecc12288596d2ee77c7f82b8) + +Related: #1642460 +--- + src/login/logind-core.c | 14 ++++++---- + src/login/logind-seat.c | 38 +++++++++++++++---------- + src/login/logind-seat.h | 6 ++-- + src/login/logind-session.c | 57 +++++++++++++++++++++----------------- + src/login/logind-session.h | 7 +++-- + src/login/logind-user.c | 21 +++++++------- + 6 files changed, 83 insertions(+), 60 deletions(-) + +diff --git a/src/login/logind-core.c b/src/login/logind-core.c +index cff5536ac0..f598bbaa1c 100644 +--- a/src/login/logind-core.c ++++ b/src/login/logind-core.c +@@ -88,15 +88,16 @@ int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_dev + + int manager_add_seat(Manager *m, const char *id, Seat **_seat) { + Seat *s; ++ int r; + + assert(m); + assert(id); + + s = hashmap_get(m->seats, id); + if (!s) { +- s = seat_new(m, id); +- if (!s) +- return -ENOMEM; ++ r = seat_new(&s, m, id); ++ if (r < 0) ++ return r; + } + + if (_seat) +@@ -107,15 +108,16 @@ int manager_add_seat(Manager *m, const char *id, Seat **_seat) { + + int manager_add_session(Manager *m, const char *id, Session **_session) { + Session *s; ++ int r; + + assert(m); + assert(id); + + s = hashmap_get(m->sessions, id); + if (!s) { +- s = session_new(m, id); +- if (!s) +- return -ENOMEM; ++ r = session_new(&s, m, id); ++ if (r < 0) ++ return r; + } + + if (_session) +diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c +index 63253db5bf..f68fc0ceaa 100644 +--- a/src/login/logind-seat.c ++++ b/src/login/logind-seat.c +@@ -21,33 +21,42 @@ + #include "terminal-util.h" + #include "util.h" + +-Seat *seat_new(Manager *m, const char *id) { +- Seat *s; ++int seat_new(Seat** ret, Manager *m, const char *id) { ++ _cleanup_(seat_freep) Seat *s = NULL; ++ int r; + ++ assert(ret); + assert(m); + assert(id); + +- s = new0(Seat, 1); ++ if (!seat_name_is_valid(id)) ++ return -EINVAL; ++ ++ s = new(Seat, 1); + if (!s) +- return NULL; ++ return -ENOMEM; ++ ++ *s = (Seat) { ++ .manager = m, ++ }; + + s->state_file = strappend("/run/systemd/seats/", id); + if (!s->state_file) +- return mfree(s); ++ return -ENOMEM; + + s->id = basename(s->state_file); +- s->manager = m; + +- if (hashmap_put(m->seats, s->id, s) < 0) { +- free(s->state_file); +- return mfree(s); +- } ++ r = hashmap_put(m->seats, s->id, s); ++ if (r < 0) ++ return r; + +- return s; ++ *ret = TAKE_PTR(s); ++ return 0; + } + +-void seat_free(Seat *s) { +- assert(s); ++Seat* seat_free(Seat *s) { ++ if (!s) ++ return NULL; + + if (s->in_gc_queue) + LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s); +@@ -64,7 +73,8 @@ void seat_free(Seat *s) { + + free(s->positions); + free(s->state_file); +- free(s); ++ ++ return mfree(s); + } + + int seat_save(Seat *s) { +diff --git a/src/login/logind-seat.h b/src/login/logind-seat.h +index 70878bbe52..51cd468e26 100644 +--- a/src/login/logind-seat.h ++++ b/src/login/logind-seat.h +@@ -27,8 +27,10 @@ struct Seat { + LIST_FIELDS(Seat, gc_queue); + }; + +-Seat *seat_new(Manager *m, const char *id); +-void seat_free(Seat *s); ++int seat_new(Seat **ret, Manager *m, const char *id); ++Seat* seat_free(Seat *s); ++ ++DEFINE_TRIVIAL_CLEANUP_FUNC(Seat *, seat_free); + + int seat_save(Seat *s); + int seat_load(Seat *s); +diff --git a/src/login/logind-session.c b/src/login/logind-session.c +index 69d5a10319..5621d59a41 100644 +--- a/src/login/logind-session.c ++++ b/src/login/logind-session.c +@@ -24,57 +24,61 @@ + #include "mkdir.h" + #include "parse-util.h" + #include "path-util.h" ++#include "process-util.h" + #include "string-table.h" + #include "terminal-util.h" + #include "user-util.h" + #include "util.h" +-#include "process-util.h" + + #define RELEASE_USEC (20*USEC_PER_SEC) + + static void session_remove_fifo(Session *s); + +-Session* session_new(Manager *m, const char *id) { +- Session *s; ++int session_new(Session **ret, Manager *m, const char *id) { ++ _cleanup_(session_freep) Session *s = NULL; ++ int r; + ++ assert(ret); + assert(m); + assert(id); +- assert(session_id_valid(id)); + +- s = new0(Session, 1); ++ if (!session_id_valid(id)) ++ return -EINVAL; ++ ++ s = new(Session, 1); + if (!s) +- return NULL; ++ return -ENOMEM; ++ ++ *s = (Session) { ++ .manager = m, ++ .fifo_fd = -1, ++ .vtfd = -1, ++ .audit_id = AUDIT_SESSION_INVALID, ++ }; + + s->state_file = strappend("/run/systemd/sessions/", id); + if (!s->state_file) +- return mfree(s); +- +- s->devices = hashmap_new(&devt_hash_ops); +- if (!s->devices) { +- free(s->state_file); +- return mfree(s); +- } ++ return -ENOMEM; + + s->id = basename(s->state_file); + +- if (hashmap_put(m->sessions, s->id, s) < 0) { +- hashmap_free(s->devices); +- free(s->state_file); +- return mfree(s); +- } ++ s->devices = hashmap_new(&devt_hash_ops); ++ if (!s->devices) ++ return -ENOMEM; + +- s->manager = m; +- s->fifo_fd = -1; +- s->vtfd = -1; +- s->audit_id = AUDIT_SESSION_INVALID; ++ r = hashmap_put(m->sessions, s->id, s); ++ if (r < 0) ++ return r; + +- return s; ++ *ret = TAKE_PTR(s); ++ return 0; + } + +-void session_free(Session *s) { ++Session* session_free(Session *s) { + SessionDevice *sd; + +- assert(s); ++ if (!s) ++ return NULL; + + if (s->in_gc_queue) + LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s); +@@ -126,7 +130,8 @@ void session_free(Session *s) { + hashmap_remove(s->manager->sessions, s->id); + + free(s->state_file); +- free(s); ++ ++ return mfree(s); + } + + void session_set_user(Session *s, User *u) { +diff --git a/src/login/logind-session.h b/src/login/logind-session.h +index 29ca399daf..572f2545c1 100644 +--- a/src/login/logind-session.h ++++ b/src/login/logind-session.h +@@ -109,8 +109,11 @@ struct Session { + LIST_FIELDS(Session, gc_queue); + }; + +-Session *session_new(Manager *m, const char *id); +-void session_free(Session *s); ++int session_new(Session **ret, Manager *m, const char *id); ++Session* session_free(Session *s); ++ ++DEFINE_TRIVIAL_CLEANUP_FUNC(Session *, session_free); ++ + void session_set_user(Session *s, User *u); + bool session_may_gc(Session *s, bool drop_not_started); + void session_add_to_gc_queue(Session *s); +diff --git a/src/login/logind-user.c b/src/login/logind-user.c +index 56b8066f12..60ccd62abb 100644 +--- a/src/login/logind-user.c ++++ b/src/login/logind-user.c +@@ -30,23 +30,24 @@ + #include "user-util.h" + #include "util.h" + +-int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) { ++int user_new(User **ret, Manager *m, uid_t uid, gid_t gid, const char *name) { + _cleanup_(user_freep) User *u = NULL; + char lu[DECIMAL_STR_MAX(uid_t) + 1]; + int r; + +- assert(out); ++ assert(ret); + assert(m); + assert(name); + +- u = new0(User, 1); ++ u = new(User, 1); + if (!u) + return -ENOMEM; + +- u->manager = m; +- u->uid = uid; +- u->gid = gid; +- xsprintf(lu, UID_FMT, uid); ++ *u = (User) { ++ .manager = m, ++ .uid = uid, ++ .gid = gid, ++ }; + + u->name = strdup(name); + if (!u->name) +@@ -58,6 +59,7 @@ int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) { + if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0) + return -ENOMEM; + ++ xsprintf(lu, UID_FMT, uid); + r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice); + if (r < 0) + return r; +@@ -78,8 +80,7 @@ int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) { + if (r < 0) + return r; + +- *out = TAKE_PTR(u); +- ++ *ret = TAKE_PTR(u); + return 0; + } + +@@ -272,7 +273,7 @@ int user_save(User *u) { + if (!u->started) + return 0; + +- return user_save_internal (u); ++ return user_save_internal(u); + } + + int user_load(User *u) { diff --git a/SOURCES/0619-logind-fix-serialization-deserialization-of-user-s-d.patch b/SOURCES/0619-logind-fix-serialization-deserialization-of-user-s-d.patch new file mode 100644 index 0000000..7639b30 --- /dev/null +++ b/SOURCES/0619-logind-fix-serialization-deserialization-of-user-s-d.patch @@ -0,0 +1,116 @@ +From 0314e68fe961cec941b1b0eb1cbcca4546cfdfdb Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 3 Aug 2018 19:04:35 +0200 +Subject: [PATCH] logind: fix serialization/deserialization of user's "display + session" + +Previously this was serialized as part of the user object. This didn't +work however, as we load users first, and sessions seconds and hence +referencing a session from the user load logic cannot work. + +Fix this by storing an IS_DISPLAY property along with each session, and +make the session with this set display session when it is loaded. + +(cherry picked from commit 1c8280fd47b6561d35b15b3b6d49bdeacf891bfd) + +Related: #1642460 +--- + src/login/logind-session.c | 18 +++++++++++++++++- + src/login/logind-user.c | 18 ++++-------------- + 2 files changed, 21 insertions(+), 15 deletions(-) + +diff --git a/src/login/logind-session.c b/src/login/logind-session.c +index 5621d59a41..0afb065b2b 100644 +--- a/src/login/logind-session.c ++++ b/src/login/logind-session.c +@@ -184,11 +184,13 @@ int session_save(Session *s) { + "UID="UID_FMT"\n" + "USER=%s\n" + "ACTIVE=%i\n" ++ "IS_DISPLAY=%i\n" + "STATE=%s\n" + "REMOTE=%i\n", + s->user->uid, + s->user->name, + session_is_active(s), ++ s->user->display == s, + session_state_to_string(session_get_state(s)), + s->remote); + +@@ -359,7 +361,8 @@ int session_load(Session *s) { + *monotonic = NULL, + *controller = NULL, + *active = NULL, +- *devices = NULL; ++ *devices = NULL, ++ *is_display = NULL; + + int k, r; + +@@ -389,6 +392,7 @@ int session_load(Session *s) { + "CONTROLLER", &controller, + "ACTIVE", &active, + "DEVICES", &devices, ++ "IS_DISPLAY", &is_display, + NULL); + + if (r < 0) +@@ -496,6 +500,18 @@ int session_load(Session *s) { + s->was_active = k; + } + ++ if (is_display) { ++ /* Note that when enumerating users are loaded before sessions, hence the display session to use is ++ * something we have to store along with the session and not the user, as in that case we couldn't ++ * apply it at the time we load the user. */ ++ ++ k = parse_boolean(is_display); ++ if (k < 0) ++ log_warning_errno(k, "Failed to parse IS_DISPLAY session property: %m"); ++ else if (k > 0) ++ s->user->display = s; ++ } ++ + if (controller) { + if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) { + session_set_controller(s, controller, false, false); +diff --git a/src/login/logind-user.c b/src/login/logind-user.c +index 60ccd62abb..17ed361411 100644 +--- a/src/login/logind-user.c ++++ b/src/login/logind-user.c +@@ -277,8 +277,7 @@ int user_save(User *u) { + } + + int user_load(User *u) { +- _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL; +- Session *s = NULL; ++ _cleanup_free_ char *realtime = NULL, *monotonic = NULL; + int r; + + assert(u); +@@ -286,22 +285,13 @@ int user_load(User *u) { + r = parse_env_file(NULL, u->state_file, NEWLINE, + "SERVICE_JOB", &u->service_job, + "SLICE_JOB", &u->slice_job, +- "DISPLAY", &display, + "REALTIME", &realtime, + "MONOTONIC", &monotonic, + NULL); +- if (r < 0) { +- if (r == -ENOENT) +- return 0; +- ++ if (r == -ENOENT) ++ return 0; ++ if (r < 0) + return log_error_errno(r, "Failed to read %s: %m", u->state_file); +- } +- +- if (display) +- s = hashmap_get(u->manager->sessions, display); +- +- if (s && s->display && display_is_local(s->display)) +- u->display = s; + + if (realtime) + timestamp_deserialize(realtime, &u->timestamp.realtime); diff --git a/SOURCES/0620-logind-turn-of-stdio-locking-when-writing-session-fi.patch b/SOURCES/0620-logind-turn-of-stdio-locking-when-writing-session-fi.patch new file mode 100644 index 0000000..93c7244 --- /dev/null +++ b/SOURCES/0620-logind-turn-of-stdio-locking-when-writing-session-fi.patch @@ -0,0 +1,39 @@ +From 3eab0f1b64477792bd01ca52c3eb26ce64c5c7ba Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 3 Aug 2018 20:18:55 +0200 +Subject: [PATCH] logind: turn of stdio locking when writing session files too + +This just copies what we already do for user and seat files to session +files. + +(cherry picked from commit 44176400138e18d9087e0864ca97041416a90d47) + +Related: #1642460 +--- + src/login/logind-session.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/login/logind-session.c b/src/login/logind-session.c +index 0afb065b2b..960a24d1a7 100644 +--- a/src/login/logind-session.c ++++ b/src/login/logind-session.c +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -175,9 +176,8 @@ int session_save(Session *s) { + if (r < 0) + goto fail; + +- assert(s->user); +- +- fchmod(fileno(f), 0644); ++ (void) __fsetlocking(f, FSETLOCKING_BYCALLER); ++ (void) fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n" diff --git a/SOURCES/0621-units-set-StopWhenUnneeded-for-the-user-slice-units-.patch b/SOURCES/0621-units-set-StopWhenUnneeded-for-the-user-slice-units-.patch new file mode 100644 index 0000000..4b9bb79 --- /dev/null +++ b/SOURCES/0621-units-set-StopWhenUnneeded-for-the-user-slice-units-.patch @@ -0,0 +1,27 @@ +From f94c1bbec9e2c3efcbafd61ea1fdf8dbc3245d1b Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 3 Aug 2018 20:19:38 +0200 +Subject: [PATCH] units: set StopWhenUnneeded= for the user slice units too + +We'd like them to go away, just like the user-runtime-dir@.service when +they aren't needed anymore. + +(cherry picked from commit 1007473b49b5aaeef0e53cd4a15f4ed8cf721926) + +Related: #1642460 +--- + units/user-.slice.d/10-defaults.conf | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/units/user-.slice.d/10-defaults.conf b/units/user-.slice.d/10-defaults.conf +index efc9d37c8e..1147e7aed9 100644 +--- a/units/user-.slice.d/10-defaults.conf ++++ b/units/user-.slice.d/10-defaults.conf +@@ -10,6 +10,7 @@ + [Unit] + Description=User Slice of UID %j + After=systemd-user-sessions.service ++StopWhenUnneeded=yes + + [Slice] + TasksMax=80% diff --git a/SOURCES/0622-units-improve-Description-string-a-bit.patch b/SOURCES/0622-units-improve-Description-string-a-bit.patch new file mode 100644 index 0000000..dff8053 --- /dev/null +++ b/SOURCES/0622-units-improve-Description-string-a-bit.patch @@ -0,0 +1,29 @@ +From 50a4e03d2da89df32f2f63eb56051d789508ae75 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 6 Aug 2018 18:15:07 +0200 +Subject: [PATCH] units: improve Description= string a bit + +Let's not use the word "wrapper", as it's not clear what that is, and in +some way any unit file is a "wrapper"... let's simply say that it's +about the runtime directory. + +(cherry picked from commit 14df094a51e87013d96ac697ae4f14593cbcad39) + +Related: #1642460 +--- + units/user-runtime-dir@.service.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/units/user-runtime-dir@.service.in b/units/user-runtime-dir@.service.in +index bfd6488d61..63db1cca6a 100644 +--- a/units/user-runtime-dir@.service.in ++++ b/units/user-runtime-dir@.service.in +@@ -8,7 +8,7 @@ + # (at your option) any later version. + + [Unit] +-Description=/run/user/%i mount wrapper ++Description=User runtime directory /run/user/%i + After=systemd-user-sessions.service + StopWhenUnneeded=yes + diff --git a/SOURCES/0623-logind-improve-logging-in-manager_connect_console.patch b/SOURCES/0623-logind-improve-logging-in-manager_connect_console.patch new file mode 100644 index 0000000..6a30f47 --- /dev/null +++ b/SOURCES/0623-logind-improve-logging-in-manager_connect_console.patch @@ -0,0 +1,77 @@ +From 9e9c6cbbdd60f4538cee041ffe3f9cd831c5de17 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 3 Aug 2018 20:21:27 +0200 +Subject: [PATCH] logind: improve logging in manager_connect_console() + +let's make sure we log about every failure + +Also, complain about systems where /dev/tty0 exists but +/sys/class/tty/tty0/active does not. Such systems (usually container +environments) are pretty broken as they mount something that is not a VC +to /dev/tty0 and they really shouldn't. + +Systems should either have a VC or not, but not badly fake one by +mounting things wildly. + +This just adds a warning message, as before we'll simply turn off VC +handling in this case. + +(cherry picked from commit 0b6d55cae9b8adc507fbea95d1b2874729a77386) + +Related: #1642460 +--- + src/login/logind.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/src/login/logind.c b/src/login/logind.c +index 52fcee933c..1b366cd55f 100644 +--- a/src/login/logind.c ++++ b/src/login/logind.c +@@ -815,28 +815,28 @@ static int manager_connect_console(Manager *m) { + assert(m); + assert(m->console_active_fd < 0); + +- /* On certain architectures (S390 and Xen, and containers), +- /dev/tty0 does not exist, so don't fail if we can't open +- it. */ ++ /* On certain systems (such as S390, Xen, and containers) /dev/tty0 does not exist (as there is no VC), so ++ * don't fail if we can't open it. */ ++ + if (access("/dev/tty0", F_OK) < 0) + return 0; + + m->console_active_fd = open("/sys/class/tty/tty0/active", O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (m->console_active_fd < 0) { + +- /* On some systems the device node /dev/tty0 may exist +- * even though /sys/class/tty/tty0 does not. */ +- if (errno == ENOENT) ++ /* On some systems /dev/tty0 may exist even though /sys/class/tty/tty0 does not. These are broken, but ++ * common. Let's complain but continue anyway. */ ++ if (errno == ENOENT) { ++ log_warning_errno(errno, "System has /dev/tty0 but not /sys/class/tty/tty0/active which is broken, ignoring: %m"); + return 0; ++ } + + return log_error_errno(errno, "Failed to open /sys/class/tty/tty0/active: %m"); + } + + r = sd_event_add_io(m->event, &m->console_active_event_source, m->console_active_fd, 0, manager_dispatch_console, m); +- if (r < 0) { +- log_error("Failed to watch foreground console"); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to watch foreground console: %m"); + + /* + * SIGRTMIN is used as global VT-release signal, SIGRTMIN + 1 is used +@@ -855,7 +855,7 @@ static int manager_connect_console(Manager *m) { + + r = sd_event_add_signal(m->event, NULL, SIGRTMIN, manager_vt_switch, m); + if (r < 0) +- return r; ++ return log_error_errno(r, "Failed to subscribe to signal: %m"); + + return 0; + } diff --git a/SOURCES/0624-logind-save-restore-User-object-s-stopping-field-dur.patch b/SOURCES/0624-logind-save-restore-User-object-s-stopping-field-dur.patch new file mode 100644 index 0000000..d640d09 --- /dev/null +++ b/SOURCES/0624-logind-save-restore-User-object-s-stopping-field-dur.patch @@ -0,0 +1,90 @@ +From 4703c08fe3a8bfa1bc9b893e8bde365b1cbeffd9 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 6 Aug 2018 18:14:11 +0200 +Subject: [PATCH] logind: save/restore User object's "stopping" field during + restarts + +Whether we are stopping or not is highly relevant, hence don't forget it +across restarts. + +(cherry picked from commit d865bc024bf28c17120d7322a81e9a99997a59f6) + +Related: #1642460 +--- + src/login/logind-user.c | 20 +++++++++++++++----- + src/login/logind-user.h | 5 +++-- + 2 files changed, 18 insertions(+), 7 deletions(-) + +diff --git a/src/login/logind-user.c b/src/login/logind-user.c +index 17ed361411..35b2ca5489 100644 +--- a/src/login/logind-user.c ++++ b/src/login/logind-user.c +@@ -136,9 +136,11 @@ static int user_save_internal(User *u) { + fprintf(f, + "# This is private data. Do not parse.\n" + "NAME=%s\n" +- "STATE=%s\n", ++ "STATE=%s\n" /* friendly user-facing state */ ++ "STOPPING=%s\n", /* low-level state */ + u->name, +- user_state_to_string(user_get_state(u))); ++ user_state_to_string(user_get_state(u)), ++ yes_no(u->stopping)); + + /* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */ + if (u->runtime_path) +@@ -277,14 +279,14 @@ int user_save(User *u) { + } + + int user_load(User *u) { +- _cleanup_free_ char *realtime = NULL, *monotonic = NULL; ++ _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *stopping = NULL; + int r; + + assert(u); + + r = parse_env_file(NULL, u->state_file, NEWLINE, + "SERVICE_JOB", &u->service_job, +- "SLICE_JOB", &u->slice_job, ++ "STOPPING", &stopping, + "REALTIME", &realtime, + "MONOTONIC", &monotonic, + NULL); +@@ -293,12 +295,20 @@ int user_load(User *u) { + if (r < 0) + return log_error_errno(r, "Failed to read %s: %m", u->state_file); + ++ if (stopping) { ++ r = parse_boolean(stopping); ++ if (r < 0) ++ log_debug_errno(r, "Failed to parse 'STOPPING' boolean: %s", stopping); ++ else ++ u->stopping = r; ++ } ++ + if (realtime) + timestamp_deserialize(realtime, &u->timestamp.realtime); + if (monotonic) + timestamp_deserialize(monotonic, &u->timestamp.monotonic); + +- return r; ++ return 0; + } + + static int user_start_service(User *u) { +diff --git a/src/login/logind-user.h b/src/login/logind-user.h +index eba2325284..03e020b870 100644 +--- a/src/login/logind-user.h ++++ b/src/login/logind-user.h +@@ -36,8 +36,9 @@ struct User { + dual_timestamp timestamp; + + bool in_gc_queue:1; +- bool started:1; +- bool stopping:1; ++ ++ bool started:1; /* Whenever the user being started, has been started or is being stopped again. */ ++ bool stopping:1; /* Whenever the user is being stopped or has been stopped. */ + + LIST_HEAD(Session, sessions); + LIST_FIELDS(User, gc_queue); diff --git a/SOURCES/0625-logind-correct-bad-clean-up-path.patch b/SOURCES/0625-logind-correct-bad-clean-up-path.patch new file mode 100644 index 0000000..90740cc --- /dev/null +++ b/SOURCES/0625-logind-correct-bad-clean-up-path.patch @@ -0,0 +1,25 @@ +From eebbeada76b0fa4e252ecf4e25b088733636fe89 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 6 Aug 2018 18:19:45 +0200 +Subject: [PATCH] logind: correct bad clean-up path + +(cherry picked from commit d88ffeeeefda4c3447223fd36f8e30f23c931e48) + +Related: #1642460 +--- + src/login/logind-dbus.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c +index ae9abc9bce..4b2c418453 100644 +--- a/src/login/logind-dbus.c ++++ b/src/login/logind-dbus.c +@@ -845,7 +845,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus + + r = sd_bus_message_enter_container(message, 'a', "(sv)"); + if (r < 0) +- return r; ++ goto fail; + + r = session_start(session, message); + if (r < 0) diff --git a/SOURCES/0626-logind-fix-bad-error-propagation.patch b/SOURCES/0626-logind-fix-bad-error-propagation.patch new file mode 100644 index 0000000..1a07e78 --- /dev/null +++ b/SOURCES/0626-logind-fix-bad-error-propagation.patch @@ -0,0 +1,25 @@ +From 7662d7c86d1fbb01693d4eb008fa27bf1e0030a9 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 6 Aug 2018 18:21:37 +0200 +Subject: [PATCH] logind: fix bad error propagation + +(cherry picked from commit cce08496e7353e3e9903b42695aba3f9d259b90a) + +Related: #1642460 +--- + src/login/logind-seat.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c +index f68fc0ceaa..9e4f009643 100644 +--- a/src/login/logind-seat.c ++++ b/src/login/logind-seat.c +@@ -175,7 +175,7 @@ static int vt_allocate(unsigned int vtnr) { + xsprintf(p, "/dev/tty%u", vtnr); + fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) +- return -errno; ++ return fd; + + return 0; + } diff --git a/SOURCES/0627-logind-never-elect-a-session-that-is-stopping-as-dis.patch b/SOURCES/0627-logind-never-elect-a-session-that-is-stopping-as-dis.patch new file mode 100644 index 0000000..145c425 --- /dev/null +++ b/SOURCES/0627-logind-never-elect-a-session-that-is-stopping-as-dis.patch @@ -0,0 +1,42 @@ +From 35f9a7f8f4e8917725349fe764706658c02537ca Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 6 Aug 2018 19:02:29 +0200 +Subject: [PATCH] logind: never elect a session that is stopping as display + +(cherry picked from commit 04857cd801022d9f9933efb484c6253572f09870) + +Related: #1642460 +--- + src/login/logind-user.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +diff --git a/src/login/logind-user.c b/src/login/logind-user.c +index 35b2ca5489..3e4c99bdbd 100644 +--- a/src/login/logind-user.c ++++ b/src/login/logind-user.c +@@ -613,11 +613,10 @@ int user_kill(User *u, int signo) { + } + + static bool elect_display_filter(Session *s) { +- /* Return true if the session is a candidate for the user’s ‘primary +- * session’ or ‘display’. */ ++ /* 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); ++ return s->class == SESSION_USER && s->started && !s->stopping; + } + + static int elect_display_compare(Session *s1, Session *s2) { +@@ -663,9 +662,8 @@ void user_elect_display(User *u) { + + 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. */ ++ /* 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) { diff --git a/SOURCES/0628-logind-introduce-little-helper-that-checks-whether-a.patch b/SOURCES/0628-logind-introduce-little-helper-that-checks-whether-a.patch new file mode 100644 index 0000000..d50750b --- /dev/null +++ b/SOURCES/0628-logind-introduce-little-helper-that-checks-whether-a.patch @@ -0,0 +1,60 @@ +From 83c49a5e54dffc3dfa85b79f6375cd0a42a4ff76 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 6 Aug 2018 19:34:39 +0200 +Subject: [PATCH] logind: introduce little helper that checks whether a session + is ready + +(cherry picked from commit b1951bc83ffbbb92ba4de7b9cba845421c2f35b1) + +Related: #1642460 +--- + src/login/logind-session-dbus.c | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c +index 88a2d33dc8..03585b7f8e 100644 +--- a/src/login/logind-session-dbus.c ++++ b/src/login/logind-session-dbus.c +@@ -704,6 +704,15 @@ int session_send_lock_all(Manager *m, bool lock) { + return r; + } + ++static bool session_ready(Session *s) { ++ assert(s); ++ ++ /* Returns true when the session is ready, i.e. all jobs we enqueued for it are done (regardless if successful or not) */ ++ ++ return !s->scope_job && ++ !s->user->service_job; ++} ++ + int session_send_create_reply(Session *s, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; + _cleanup_close_ int fifo_fd = -1; +@@ -711,14 +720,13 @@ int session_send_create_reply(Session *s, sd_bus_error *error) { + + assert(s); + +- /* This is called after the session scope and the user service +- * were successfully created, and finishes where ++ /* This is called after the session scope and the user service were successfully created, and finishes where + * bus_manager_create_session() left off. */ + + if (!s->create_message) + return 0; + +- if (!sd_bus_error_is_set(error) && (s->scope_job || s->user->service_job)) ++ if (!sd_bus_error_is_set(error) && !session_ready(s)) + return 0; + + c = s->create_message; +@@ -731,8 +739,7 @@ int session_send_create_reply(Session *s, sd_bus_error *error) { + if (fifo_fd < 0) + return fifo_fd; + +- /* Update the session state file before we notify the client +- * about the result. */ ++ /* Update the session state file before we notify the client about the result. */ + session_save(s); + + p = session_bus_path(s); diff --git a/SOURCES/0629-logind-propagate-session-stop-errors.patch b/SOURCES/0629-logind-propagate-session-stop-errors.patch new file mode 100644 index 0000000..feed8cb --- /dev/null +++ b/SOURCES/0629-logind-propagate-session-stop-errors.patch @@ -0,0 +1,40 @@ +From 31aa21a13f9b91486b1a95c5b73fa088af77fcb4 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 6 Aug 2018 19:35:44 +0200 +Subject: [PATCH] logind: propagate session stop errors + +Let's propagate errors from stopping sessions via seat_stop(). This is +similar to how we propagate such errors in user_stop() for all sessions +associated with a user. + +Note that we propagate these errors, but we don't abort the function. + +(cherry picked from commit e6958b7ea33813b085966ac25817a957c0dad7f9) + +Related: #1642460 +--- + src/login/logind-seat.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c +index 9e4f009643..96c34a6c9e 100644 +--- a/src/login/logind-seat.c ++++ b/src/login/logind-seat.c +@@ -431,7 +431,7 @@ int seat_start(Seat *s) { + } + + int seat_stop(Seat *s, bool force) { +- int r = 0; ++ int r; + + assert(s); + +@@ -441,7 +441,7 @@ int seat_stop(Seat *s, bool force) { + "SEAT_ID=%s", s->id, + LOG_MESSAGE("Removed seat %s.", s->id)); + +- seat_stop_sessions(s, force); ++ r = seat_stop_sessions(s, force); + + unlink(s->state_file); + seat_add_to_gc_queue(s); diff --git a/SOURCES/0630-logind-rework-how-we-manage-the-slice-and-user-runti.patch b/SOURCES/0630-logind-rework-how-we-manage-the-slice-and-user-runti.patch new file mode 100644 index 0000000..7880086 --- /dev/null +++ b/SOURCES/0630-logind-rework-how-we-manage-the-slice-and-user-runti.patch @@ -0,0 +1,618 @@ +From a05c1077911652954c8b9e82cfdc0fc643eca782 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 6 Aug 2018 21:44:45 +0200 +Subject: [PATCH] logind: rework how we manage the slice and + user-runtime-dir@.service unit for each user + +Instead of managing it explicitly, let's simplify things and rely on +regular Wants=/Requires= dependencies to pull in these units from +user@.service and the session scope, and StopWhenUneeded= to stop these +auxiliary units again. This way, they can be pulled in easily by +unrelated units too. + +This simplifies things quite a bit: for each session we now only need to +manage the session scope, and for each user the user@.service, the other +units are not something we need to manage anymore. + +This patch also makes sure that if user@.service of a user is masked we +will continue to work, and user-runtime-dir@.service will still be +correctly pulled in, as it is now a dependency of the scope unit. + +Fixes: #9461 +Replaces: #5546 +(cherry picked from commit 25a1ab4ed48b72e974f77a68dcbe3521014787bb) + +Related: #1642460 +--- + src/login/logind-dbus.c | 58 ++++++++-------- + src/login/logind-session.c | 64 ++++++++++-------- + src/login/logind-session.h | 2 +- + src/login/logind-user.c | 134 ++++++++++++++----------------------- + src/login/logind-user.h | 7 +- + src/login/logind.c | 2 +- + src/login/logind.h | 2 +- + 7 files changed, 123 insertions(+), 146 deletions(-) + +diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c +index 4b2c418453..7eba617fff 100644 +--- a/src/login/logind-dbus.c ++++ b/src/login/logind-dbus.c +@@ -847,7 +847,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus + if (r < 0) + goto fail; + +- r = session_start(session, message); ++ r = session_start(session, message, error); + if (r < 0) + goto fail; + +@@ -3110,24 +3110,20 @@ const sd_bus_vtable manager_vtable[] = { + }; + + static int session_jobs_reply(Session *s, const char *unit, const char *result) { +- int r = 0; +- + assert(s); + assert(unit); + + if (!s->started) +- return r; ++ return 0; + +- if (streq(result, "done")) +- r = session_send_create_reply(s, NULL); +- else { ++ if (result && !streq(result, "done")) { + _cleanup_(sd_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); ++ sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit '%s' failed with '%s'", unit, result); ++ return session_send_create_reply(s, &e); + } + +- return r; ++ return session_send_create_reply(s, NULL); + } + + int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { +@@ -3160,30 +3156,29 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err + } + + session = hashmap_get(m->session_units, unit); +- if (session && streq_ptr(path, session->scope_job)) { +- session->scope_job = mfree(session->scope_job); +- session_jobs_reply(session, unit, result); ++ if (session) { ++ if (streq_ptr(path, session->scope_job)) { ++ session->scope_job = mfree(session->scope_job); ++ (void) session_jobs_reply(session, unit, result); ++ ++ session_save(session); ++ user_save(session->user); ++ } + +- session_save(session); +- user_save(session->user); + session_add_to_gc_queue(session); + } + + user = hashmap_get(m->user_units, unit); +- if (user && +- (streq_ptr(path, user->service_job) || +- streq_ptr(path, user->slice_job))) { +- +- if (streq_ptr(path, user->service_job)) ++ if (user) { ++ if (streq_ptr(path, user->service_job)) { + user->service_job = mfree(user->service_job); + +- if (streq_ptr(path, user->slice_job)) +- user->slice_job = mfree(user->slice_job); ++ LIST_FOREACH(sessions_by_user, session, user->sessions) ++ (void) session_jobs_reply(session, unit, NULL /* don't propagate user service failures to the client */); + +- LIST_FOREACH(sessions_by_user, session, user->sessions) +- session_jobs_reply(session, unit, result); ++ user_save(user); ++ } + +- user_save(user); + user_add_to_gc_queue(user); + } + +@@ -3315,13 +3310,14 @@ int manager_start_scope( + pid_t pid, + const char *slice, + const char *description, +- const char *after, +- const char *after2, ++ char **wants, ++ char **after, + sd_bus_message *more_properties, + sd_bus_error *error, + char **job) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; ++ char **i; + int r; + + assert(manager); +@@ -3359,14 +3355,14 @@ int manager_start_scope( + return r; + } + +- if (!isempty(after)) { +- r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after); ++ STRV_FOREACH(i, wants) { ++ r = sd_bus_message_append(m, "(sv)", "Wants", "as", 1, *i); + if (r < 0) + return r; + } + +- if (!isempty(after2)) { +- r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2); ++ STRV_FOREACH(i, after) { ++ r = sd_bus_message_append(m, "(sv)", "After", "as", 1, *i); + if (r < 0) + return r; + } +diff --git a/src/login/logind-session.c b/src/login/logind-session.c +index 960a24d1a7..d56b48a732 100644 +--- a/src/login/logind-session.c ++++ b/src/login/logind-session.c +@@ -27,6 +27,7 @@ + #include "path-util.h" + #include "process-util.h" + #include "string-table.h" ++#include "strv.h" + #include "terminal-util.h" + #include "user-util.h" + #include "util.h" +@@ -560,17 +561,18 @@ int session_activate(Session *s) { + return 0; + } + +-static int session_start_scope(Session *s, sd_bus_message *properties) { ++static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_error *error) { + int r; + + assert(s); + assert(s->user); + + if (!s->scope) { +- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; +- char *scope, *job = NULL; ++ _cleanup_free_ char *scope = NULL; + const char *description; + ++ s->scope_job = mfree(s->scope_job); ++ + scope = strjoin("session-", s->id, ".scope"); + if (!scope) + return log_oom(); +@@ -583,21 +585,15 @@ static int session_start_scope(Session *s, sd_bus_message *properties) { + s->leader, + s->user->slice, + description, +- "systemd-logind.service", +- "systemd-user-sessions.service", ++ STRV_MAKE(s->user->runtime_dir_service, s->user->service), /* These two have StopWhenUnneeded= set, hence add a dep towards them */ ++ STRV_MAKE("systemd-logind.service", "systemd-user-sessions.service", s->user->runtime_dir_service, s->user->service), /* And order us after some more */ + properties, +- &error, +- &job); +- if (r < 0) { +- log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(&error, r)); +- free(scope); +- return r; +- } else { +- s->scope = scope; ++ error, ++ &s->scope_job); ++ if (r < 0) ++ return log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(error, r)); + +- free(s->scope_job); +- s->scope_job = job; +- } ++ s->scope = TAKE_PTR(scope); + } + + if (s->scope) +@@ -606,7 +602,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties) { + return 0; + } + +-int session_start(Session *s, sd_bus_message *properties) { ++int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) { + int r; + + assert(s); +@@ -614,6 +610,9 @@ int session_start(Session *s, sd_bus_message *properties) { + if (!s->user) + return -ESTALE; + ++ if (s->stopping) ++ return -EINVAL; ++ + if (s->started) + return 0; + +@@ -621,8 +620,7 @@ int session_start(Session *s, sd_bus_message *properties) { + if (r < 0) + return r; + +- /* Create cgroup */ +- r = session_start_scope(s, properties); ++ r = session_start_scope(s, properties, error); + if (r < 0) + return r; + +@@ -673,21 +671,24 @@ static int session_stop_scope(Session *s, bool force) { + * that is left in the scope is "left-over". Informing systemd about this has the benefit that it will log + * when killing any processes left after this point. */ + r = manager_abandon_scope(s->manager, s->scope, &error); +- if (r < 0) ++ if (r < 0) { + log_warning_errno(r, "Failed to abandon session scope, ignoring: %s", bus_error_message(&error, r)); ++ sd_bus_error_free(&error); ++ } ++ ++ s->scope_job = mfree(s->scope_job); + + /* Optionally, let's kill everything that's left now. */ + if (force || manager_shall_kill(s->manager, s->user->name)) { +- char *job = NULL; + +- r = manager_stop_unit(s->manager, s->scope, &error, &job); +- if (r < 0) +- return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r)); ++ r = manager_stop_unit(s->manager, s->scope, &error, &s->scope_job); ++ if (r < 0) { ++ if (force) ++ return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r)); + +- free(s->scope_job); +- s->scope_job = job; ++ log_warning_errno(r, "Failed to stop session scope, ignoring: %s", bus_error_message(&error, r)); ++ } + } else { +- s->scope_job = mfree(s->scope_job); + + /* With no killing, this session is allowed to persist in "closing" state indefinitely. + * Therefore session stop and session removal may be two distinct events. +@@ -707,8 +708,17 @@ int session_stop(Session *s, bool force) { + + assert(s); + ++ /* This is called whenever we begin with tearing down a session record. It's called in four cases: explicit API ++ * request via the bus (either directly for the session object or for the seat or user object this session ++ * belongs to; 'force' is true), or due to automatic GC (i.e. scope vanished; 'force' is false), or because the ++ * session FIFO saw an EOF ('force' is false), or because the release timer hit ('force' is false). */ ++ + if (!s->user) + return -ESTALE; ++ if (!s->started) ++ return 0; ++ if (s->stopping) ++ return 0; + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + +diff --git a/src/login/logind-session.h b/src/login/logind-session.h +index 572f2545c1..7d17d9a25f 100644 +--- a/src/login/logind-session.h ++++ b/src/login/logind-session.h +@@ -124,7 +124,7 @@ void session_set_idle_hint(Session *s, bool b); + int session_get_locked_hint(Session *s); + void session_set_locked_hint(Session *s, bool b); + int session_create_fifo(Session *s); +-int session_start(Session *s, sd_bus_message *properties); ++int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error); + int session_stop(Session *s, bool force); + int session_finalize(Session *s); + int session_release(Session *s); +diff --git a/src/login/logind-user.c b/src/login/logind-user.c +index 3e4c99bdbd..39fc76f4dc 100644 +--- a/src/login/logind-user.c ++++ b/src/login/logind-user.c +@@ -68,6 +68,10 @@ int user_new(User **ret, Manager *m, uid_t uid, gid_t gid, const char *name) { + if (r < 0) + return r; + ++ r = unit_name_build("user-runtime-dir", lu, ".service", &u->runtime_dir_service); ++ if (r < 0) ++ return r; ++ + r = hashmap_put(m->users, UID_TO_PTR(uid), u); + if (r < 0) + return r; +@@ -80,6 +84,10 @@ int user_new(User **ret, Manager *m, uid_t uid, gid_t gid, const char *name) { + if (r < 0) + return r; + ++ r = hashmap_put(m->user_units, u->runtime_dir_service, u); ++ if (r < 0) ++ return r; ++ + *ret = TAKE_PTR(u); + return 0; + } +@@ -97,15 +105,18 @@ User *user_free(User *u) { + if (u->service) + hashmap_remove_value(u->manager->user_units, u->service, u); + ++ if (u->runtime_dir_service) ++ hashmap_remove_value(u->manager->user_units, u->runtime_dir_service, u); ++ + if (u->slice) + hashmap_remove_value(u->manager->user_units, u->slice, u); + + hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u); + +- u->slice_job = mfree(u->slice_job); + u->service_job = mfree(u->service_job); + + u->service = mfree(u->service); ++ u->runtime_dir_service = mfree(u->runtime_dir_service); + u->slice = mfree(u->slice); + u->runtime_path = mfree(u->runtime_path); + u->state_file = mfree(u->state_file); +@@ -149,9 +160,6 @@ static int user_save_internal(User *u) { + if (u->service_job) + fprintf(f, "SERVICE_JOB=%s\n", u->service_job); + +- if (u->slice_job) +- fprintf(f, "SLICE_JOB=%s\n", u->slice_job); +- + if (u->display) + fprintf(f, "DISPLAY=%s\n", u->display->id); + +@@ -311,66 +319,46 @@ int user_load(User *u) { + return 0; + } + +-static int user_start_service(User *u) { ++static void user_start_service(User *u) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; +- char *job; + int r; + + assert(u); + ++ /* Start the service containing the "systemd --user" instance (user@.service). Note that we don't explicitly ++ * start the per-user slice or the systemd-runtime-dir@.service instance, as those are pulled in both by ++ * user@.service and the session scopes as dependencies. */ ++ + u->service_job = mfree(u->service_job); + +- r = manager_start_unit( +- u->manager, +- u->service, +- &error, +- &job); ++ r = manager_start_unit(u->manager, u->service, &error, &u->service_job); + if (r < 0) + /* we don't fail due to this, let's try to continue */ + log_full_errno(sd_bus_error_has_name(&error, BUS_ERROR_UNIT_MASKED) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to start user service '%s', ignoring: %s", u->service, bus_error_message(&error, r)); +- else +- u->service_job = job; +- +- return 0; + } + + int user_start(User *u) { +- int r; +- + assert(u); + + if (u->started && !u->stopping) + return 0; + +- /* +- * If u->stopping is set, the user is marked for removal and the slice +- * and service stop-jobs are queued. We have to clear that flag before +- * queing the start-jobs again. If they succeed, the user object can be +- * re-used just fine (pid1 takes care of job-ordering and proper +- * restart), but if they fail, we want to force another user_stop() so +- * possibly pending units are stopped. +- * Note that we don't clear u->started, as we have no clue what state +- * the user is in on failure here. Hence, we pretend the user is +- * running so it will be properly taken down by GC. However, we clearly +- * return an error from user_start() in that case, so no further +- * reference to the user is taken. +- */ ++ /* If u->stopping is set, the user is marked for removal and service stop-jobs are queued. We have to clear ++ * that flag before queing the start-jobs again. If they succeed, the user object can be re-used just fine ++ * (pid1 takes care of job-ordering and proper restart), but if they fail, we want to force another user_stop() ++ * so possibly pending units are stopped. */ + u->stopping = false; + + if (!u->started) + log_debug("Starting services for new user %s.", u->name); + +- /* 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. */ ++ /* 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); + +- /* Spawn user systemd */ +- r = user_start_service(u); +- if (r < 0) +- return r; ++ /* Start user@UID.service */ ++ user_start_service(u); + + if (!u->started) { + if (!dual_timestamp_is_set(&u->timestamp)) +@@ -385,68 +373,50 @@ int user_start(User *u) { + return 0; + } + +-static int user_stop_slice(User *u) { ++static void user_stop_service(User *u) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; +- char *job; + int r; + + assert(u); ++ assert(u->service); + +- 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; +- } ++ /* The reverse of user_start_service(). Note that we only stop user@UID.service here, and let StopWhenUnneeded= ++ * deal with the slice and the user-runtime-dir@.service instance. */ + +- free(u->slice_job); +- u->slice_job = job; +- +- return r; +-} +- +-static int user_stop_service(User *u) { +- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; +- char *job; +- int r; +- +- assert(u); +- +- 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; +- } ++ u->service_job = mfree(u->service_job); + +- free_and_replace(u->service_job, job); +- return r; ++ r = manager_stop_unit(u->manager, u->service, &error, &u->service_job); ++ if (r < 0) ++ log_warning_errno(r, "Failed to stop user service '%s', ignoring: %s", u->service, bus_error_message(&error, r)); + } + + int user_stop(User *u, bool force) { + Session *s; +- int r = 0, k; ++ int r = 0; + assert(u); + +- /* Stop jobs have already been queued */ +- if (u->stopping) { ++ /* This is called whenever we begin with tearing down a user record. It's called in two cases: explicit API ++ * request to do so via the bus (in which case 'force' is true) and automatically due to GC, if there's no ++ * session left pinning it (in which case 'force' is false). Note that this just initiates tearing down of the ++ * user, the User object will remain in memory until user_finalize() is called, see below. */ ++ ++ if (!u->started) ++ return 0; ++ ++ if (u->stopping) { /* Stop jobs have already been queued */ + user_save(u); +- return r; ++ return 0; + } + + LIST_FOREACH(sessions_by_user, s, u->sessions) { ++ int k; ++ + k = session_stop(s, force); + if (k < 0) + 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; ++ user_stop_service(u); + + u->stopping = true; + +@@ -461,6 +431,9 @@ int user_finalize(User *u) { + + assert(u); + ++ /* Called when the user is really ready to be freed, i.e. when all unit stop jobs and suchlike for it are ++ * done. This is called as a result of an earlier user_done() when all jobs are completed. */ ++ + if (u->started) + log_debug("User %s logged out.", u->name); + +@@ -554,9 +527,6 @@ bool user_may_gc(User *u, bool drop_not_started) { + if (user_check_linger_file(u) > 0) + return false; + +- if (u->slice_job && manager_job_is_active(u->manager, u->slice_job)) +- return false; +- + if (u->service_job && manager_job_is_active(u->manager, u->service_job)) + return false; + +@@ -581,7 +551,7 @@ UserState user_get_state(User *u) { + if (u->stopping) + return USER_CLOSING; + +- if (!u->started || u->slice_job || u->service_job) ++ if (!u->started || u->service_job) + return USER_OPENING; + + if (u->sessions) { +diff --git a/src/login/logind-user.h b/src/login/logind-user.h +index 03e020b870..5e1f7b813a 100644 +--- a/src/login/logind-user.h ++++ b/src/login/logind-user.h +@@ -25,11 +25,12 @@ struct User { + char *name; + char *state_file; + char *runtime_path; +- char *slice; +- char *service; ++ ++ char *slice; /* user-UID.slice */ ++ char *service; /* user@UID.service */ ++ char *runtime_dir_service; /* user-runtime-dir@UID.service */ + + char *service_job; +- char *slice_job; + + Session *display; + +diff --git a/src/login/logind.c b/src/login/logind.c +index 1b366cd55f..6c208c8e89 100644 +--- a/src/login/logind.c ++++ b/src/login/logind.c +@@ -1158,7 +1158,7 @@ static int manager_startup(Manager *m) { + user_start(user); + + HASHMAP_FOREACH(session, m->sessions, i) +- session_start(session, NULL); ++ (void) session_start(session, NULL, NULL); + + HASHMAP_FOREACH(inhibitor, m->inhibitors, i) + inhibitor_start(inhibitor); +diff --git a/src/login/logind.h b/src/login/logind.h +index a6ebc9e152..ae4d74076b 100644 +--- a/src/login/logind.h ++++ b/src/login/logind.h +@@ -161,7 +161,7 @@ int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name + + int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_; + +-int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_message *more_properties, sd_bus_error *error, char **job); ++int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, char **wants, char **after, sd_bus_message *more_properties, sd_bus_error *error, char **job); + int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); + int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); + int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error); diff --git a/SOURCES/0631-logind-optionally-keep-the-user-.service-instance-fo.patch b/SOURCES/0631-logind-optionally-keep-the-user-.service-instance-fo.patch new file mode 100644 index 0000000..63f6a56 --- /dev/null +++ b/SOURCES/0631-logind-optionally-keep-the-user-.service-instance-fo.patch @@ -0,0 +1,291 @@ +From 35455408393cb29a5e49fd769c4823b6a6f886b4 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 7 Aug 2018 11:02:00 +0200 +Subject: [PATCH] logind: optionally, keep the user@.service instance for + eached logged in user around for a while + +This should speed up rapid logout/login cycles a bit. + +By default this timeout is now set to 10s. + +Fixes: #8410 +Replaces: #4434 +(cherry picked from commit 9afe9efb9340588db553950727a2a9672dc3db24) + +Resolves: #1642460 +--- + man/logind.conf.xml | 11 +++++ + src/login/logind-core.c | 2 + + src/login/logind-dbus.c | 1 + + src/login/logind-gperf.gperf | 1 + + src/login/logind-session.c | 4 ++ + src/login/logind-user.c | 88 +++++++++++++++++++++++++++++++++--- + src/login/logind-user.h | 7 ++- + src/login/logind.h | 1 + + 8 files changed, 107 insertions(+), 8 deletions(-) + +diff --git a/man/logind.conf.xml b/man/logind.conf.xml +index 7d7e869a26..0cf8a7d1f2 100644 +--- a/man/logind.conf.xml ++++ b/man/logind.conf.xml +@@ -184,6 +184,17 @@ + 5. + + ++ ++ UserStopDelaySec= ++ ++ Specifies how long to keep the user record and per-user service ++ user@.service around for a user after they logged out fully. If set to zero, the per-user ++ service is terminated immediately when the last session of the user has ended. If this option is configured to ++ non-zero rapid logout/login cycles are sped up, as the user's service manager is not constantly restarted. If ++ set to infinity the per-user service for a user is never terminated again after first login, ++ and continues to run until system shutdown. Defaults to 10s. ++ ++ + + HandlePowerKey= + HandleSuspendKey= +diff --git a/src/login/logind-core.c b/src/login/logind-core.c +index f598bbaa1c..678c708df1 100644 +--- a/src/login/logind-core.c ++++ b/src/login/logind-core.c +@@ -27,6 +27,8 @@ void manager_reset_config(Manager *m) { + m->reserve_vt = 6; + m->remove_ipc = false; + m->inhibit_delay_max = 5 * USEC_PER_SEC; ++ m->user_stop_delay = 10 * USEC_PER_SEC; ++ + m->handle_power_key = HANDLE_POWEROFF; + m->handle_suspend_key = HANDLE_SUSPEND; + m->handle_hibernate_key = HANDLE_HIBERNATE; +diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c +index 7eba617fff..6586280269 100644 +--- a/src/login/logind-dbus.c ++++ b/src/login/logind-dbus.c +@@ -2695,6 +2695,7 @@ const sd_bus_vtable manager_vtable[] = { + SD_BUS_PROPERTY("BlockInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("UserStopDelayUSec", "t", NULL, offsetof(Manager, user_stop_delay), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HandlePowerKey", "s", property_get_handle_action, offsetof(Manager, handle_power_key), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HandleSuspendKey", "s", property_get_handle_action, offsetof(Manager, handle_suspend_key), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HandleHibernateKey", "s", property_get_handle_action, offsetof(Manager, handle_hibernate_key), SD_BUS_VTABLE_PROPERTY_CONST), +diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf +index c85339dcd3..8829ce7d85 100644 +--- a/src/login/logind-gperf.gperf ++++ b/src/login/logind-gperf.gperf +@@ -23,6 +23,7 @@ Login.KillUserProcesses, config_parse_bool, 0, offse + Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manager, kill_only_users) + Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users) + Login.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max) ++Login.UserStopDelaySec, config_parse_sec, 0, offsetof(Manager, user_stop_delay) + Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key) + Login.HandleSuspendKey, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key) + Login.HandleHibernateKey, config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key) +diff --git a/src/login/logind-session.c b/src/login/logind-session.c +index d56b48a732..dd4ac9482a 100644 +--- a/src/login/logind-session.c ++++ b/src/login/logind-session.c +@@ -101,6 +101,8 @@ Session* session_free(Session *s) { + + if (s->user->display == s) + s->user->display = NULL; ++ ++ user_update_last_session_timer(s->user); + } + + if (s->seat) { +@@ -142,6 +144,8 @@ void session_set_user(Session *s, User *u) { + + s->user = u; + LIST_PREPEND(sessions_by_user, u->sessions, s); ++ ++ user_update_last_session_timer(u); + } + + static void session_save_devices(Session *s, FILE *f) { +diff --git a/src/login/logind-user.c b/src/login/logind-user.c +index 39fc76f4dc..f23fcbe674 100644 +--- a/src/login/logind-user.c ++++ b/src/login/logind-user.c +@@ -47,6 +47,7 @@ int user_new(User **ret, Manager *m, uid_t uid, gid_t gid, const char *name) { + .manager = m, + .uid = uid, + .gid = gid, ++ .last_session_timestamp = USEC_INFINITY, + }; + + u->name = strdup(name); +@@ -113,6 +114,8 @@ User *user_free(User *u) { + + hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u); + ++ (void) sd_event_source_unref(u->timer_event_source); ++ + u->service_job = mfree(u->service_job); + + u->service = mfree(u->service); +@@ -170,6 +173,10 @@ static int user_save_internal(User *u) { + u->timestamp.realtime, + u->timestamp.monotonic); + ++ if (u->last_session_timestamp != USEC_INFINITY) ++ fprintf(f, "LAST_SESSION_TIMESTAMP=" USEC_FMT "\n", ++ u->last_session_timestamp); ++ + if (u->sessions) { + Session *i; + bool first; +@@ -287,16 +294,17 @@ int user_save(User *u) { + } + + int user_load(User *u) { +- _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *stopping = NULL; ++ _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *stopping = NULL, *last_session_timestamp = NULL; + int r; + + assert(u); + + r = parse_env_file(NULL, u->state_file, NEWLINE, +- "SERVICE_JOB", &u->service_job, +- "STOPPING", &stopping, +- "REALTIME", &realtime, +- "MONOTONIC", &monotonic, ++ "SERVICE_JOB", &u->service_job, ++ "STOPPING", &stopping, ++ "REALTIME", &realtime, ++ "MONOTONIC", &monotonic, ++ "LAST_SESSION_TIMESTAMP", &last_session_timestamp, + NULL); + if (r == -ENOENT) + return 0; +@@ -312,9 +320,11 @@ int user_load(User *u) { + } + + if (realtime) +- timestamp_deserialize(realtime, &u->timestamp.realtime); ++ (void) timestamp_deserialize(realtime, &u->timestamp.realtime); + if (monotonic) +- timestamp_deserialize(monotonic, &u->timestamp.monotonic); ++ (void) timestamp_deserialize(monotonic, &u->timestamp.monotonic); ++ if (last_session_timestamp) ++ (void) timestamp_deserialize(last_session_timestamp, &u->last_session_timestamp); + + return 0; + } +@@ -524,6 +534,17 @@ bool user_may_gc(User *u, bool drop_not_started) { + if (u->sessions) + return false; + ++ if (u->last_session_timestamp != USEC_INFINITY) { ++ /* All sessions have been closed. Let's see if we shall leave the user record around for a bit */ ++ ++ if (u->manager->user_stop_delay == USEC_INFINITY) ++ return false; /* Leave it around forever! */ ++ if (u->manager->user_stop_delay > 0 && ++ now(CLOCK_MONOTONIC) < usec_add(u->last_session_timestamp, u->manager->user_stop_delay)) ++ return false; /* Leave it around for a bit longer. */ ++ } ++ ++ /* Is this a user that shall stay around forever? */ + if (user_check_linger_file(u) > 0) + return false; + +@@ -649,6 +670,59 @@ void user_elect_display(User *u) { + } + } + ++static int user_stop_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) { ++ User *u = userdata; ++ ++ assert(u); ++ user_add_to_gc_queue(u); ++ ++ return 0; ++} ++ ++void user_update_last_session_timer(User *u) { ++ int r; ++ ++ assert(u); ++ ++ if (u->sessions) { ++ /* There are sessions, turn off the timer */ ++ u->last_session_timestamp = USEC_INFINITY; ++ u->timer_event_source = sd_event_source_unref(u->timer_event_source); ++ return; ++ } ++ ++ if (u->last_session_timestamp != USEC_INFINITY) ++ return; /* Timer already started */ ++ ++ u->last_session_timestamp = now(CLOCK_MONOTONIC); ++ ++ assert(!u->timer_event_source); ++ ++ if (u->manager->user_stop_delay == 0 || u->manager->user_stop_delay == USEC_INFINITY) ++ return; ++ ++ if (sd_event_get_state(u->manager->event) == SD_EVENT_FINISHED) { ++ log_debug("Not allocating user stop timeout, since we are already exiting."); ++ return; ++ } ++ ++ r = sd_event_add_time(u->manager->event, ++ &u->timer_event_source, ++ CLOCK_MONOTONIC, ++ usec_add(u->last_session_timestamp, u->manager->user_stop_delay), 0, ++ user_stop_timeout_callback, u); ++ if (r < 0) ++ log_warning_errno(r, "Failed to enqueue user stop event source, ignoring: %m"); ++ ++ if (DEBUG_LOGGING) { ++ char s[FORMAT_TIMESPAN_MAX]; ++ ++ log_debug("Last session of user '%s' logged out, terminating user context in %s.", ++ u->name, ++ format_timespan(s, sizeof(s), u->manager->user_stop_delay, USEC_PER_MSEC)); ++ } ++} ++ + static const char* const user_state_table[_USER_STATE_MAX] = { + [USER_OFFLINE] = "offline", + [USER_OPENING] = "opening", +diff --git a/src/login/logind-user.h b/src/login/logind-user.h +index 5e1f7b813a..e05646adc9 100644 +--- a/src/login/logind-user.h ++++ b/src/login/logind-user.h +@@ -34,7 +34,11 @@ struct User { + + Session *display; + +- dual_timestamp timestamp; ++ dual_timestamp timestamp; /* When this User object was 'started' the first time */ ++ usec_t last_session_timestamp; /* When the number of sessions of this user went from 1 to 0 the last time */ ++ ++ /* Set up when the last session of the user logs out */ ++ sd_event_source *timer_event_source; + + bool in_gc_queue:1; + +@@ -62,6 +66,7 @@ int user_load(User *u); + int user_kill(User *u, int signo); + int user_check_linger_file(User *u); + void user_elect_display(User *u); ++void user_update_last_session_timer(User *u); + + extern const sd_bus_vtable user_vtable[]; + int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); +diff --git a/src/login/logind.h b/src/login/logind.h +index ae4d74076b..7288dd7445 100644 +--- a/src/login/logind.h ++++ b/src/login/logind.h +@@ -62,6 +62,7 @@ struct Manager { + Hashmap *user_units; + + usec_t inhibit_delay_max; ++ usec_t user_stop_delay; + + /* If an action is currently being executed or is delayed, + * this is != 0 and encodes what is being done */ diff --git a/SOURCES/0632-logind-add-a-RequiresMountsFor-dependency-from-the-s.patch b/SOURCES/0632-logind-add-a-RequiresMountsFor-dependency-from-the-s.patch new file mode 100644 index 0000000..93f6a2c --- /dev/null +++ b/SOURCES/0632-logind-add-a-RequiresMountsFor-dependency-from-the-s.patch @@ -0,0 +1,210 @@ +From e9a187ea6abf1e7034ee3113355b282743a98f39 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 8 Aug 2018 15:27:49 +0200 +Subject: [PATCH] logind: add a RequiresMountsFor= dependency from the session + scope unit to the home directory of the user + +This is useful so that during shutdown scope units are always terminated +before the mounts necessary for the home directory. + +(Ideally we'd also add a similar dependency from the user@.service +instance to the home directory, but this isn't as easy as that service +is defined statically and not dynamically, and hence not easy to modify +dynamically, in particular when it comes to deps) + +(cherry picked from commit d5ac9d060267820aabdf9af509a54a1830b27b7d) + +Related: #1642460 +--- + src/login/logind-core.c | 24 ++++++++++++++++++------ + src/login/logind-dbus.c | 7 +++++++ + src/login/logind-session.c | 1 + + src/login/logind-user.c | 13 ++++++++++++- + src/login/logind-user.h | 3 ++- + src/login/logind.h | 4 ++-- + 6 files changed, 42 insertions(+), 10 deletions(-) + +diff --git a/src/login/logind-core.c b/src/login/logind-core.c +index 678c708df1..0ed812a2c8 100644 +--- a/src/login/logind-core.c ++++ b/src/login/logind-core.c +@@ -128,7 +128,14 @@ int manager_add_session(Manager *m, const char *id, Session **_session) { + return 0; + } + +-int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) { ++int manager_add_user( ++ Manager *m, ++ uid_t uid, ++ gid_t gid, ++ const char *name, ++ const char *home, ++ User **_user) { ++ + User *u; + int r; + +@@ -137,7 +144,7 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User ** + + u = hashmap_get(m->users, UID_TO_PTR(uid)); + if (!u) { +- r = user_new(&u, m, uid, gid, name); ++ r = user_new(&u, m, uid, gid, name, home); + if (r < 0) + return r; + } +@@ -148,7 +155,12 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User ** + return 0; + } + +-int manager_add_user_by_name(Manager *m, const char *name, User **_user) { ++int manager_add_user_by_name( ++ Manager *m, ++ const char *name, ++ User **_user) { ++ ++ const char *home = NULL; + uid_t uid; + gid_t gid; + int r; +@@ -156,11 +168,11 @@ int manager_add_user_by_name(Manager *m, const char *name, User **_user) { + assert(m); + assert(name); + +- r = get_user_creds(&name, &uid, &gid, NULL, NULL); ++ r = get_user_creds(&name, &uid, &gid, &home, NULL); + if (r < 0) + return r; + +- return manager_add_user(m, uid, gid, name, _user); ++ return manager_add_user(m, uid, gid, name, home, _user); + } + + int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) { +@@ -173,7 +185,7 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) { + if (!p) + return errno > 0 ? -errno : -ENOENT; + +- return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user); ++ return manager_add_user(m, uid, p->pw_gid, p->pw_name, p->pw_dir, _user); + } + + int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) { +diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c +index 6586280269..1bb152bc20 100644 +--- a/src/login/logind-dbus.c ++++ b/src/login/logind-dbus.c +@@ -3313,6 +3313,7 @@ int manager_start_scope( + const char *description, + char **wants, + char **after, ++ const char *requires_mounts_for, + sd_bus_message *more_properties, + sd_bus_error *error, + char **job) { +@@ -3368,6 +3369,12 @@ int manager_start_scope( + return r; + } + ++ if (!empty_or_root(requires_mounts_for)) { ++ r = sd_bus_message_append(m, "(sv)", "RequiresMountsFor", "as", 1, requires_mounts_for); ++ if (r < 0) ++ return r; ++ } ++ + /* Make sure that the session shells are terminated with SIGHUP since bash and friends tend to ignore + * SIGTERM */ + r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", true); +diff --git a/src/login/logind-session.c b/src/login/logind-session.c +index dd4ac9482a..e4c8bb36f6 100644 +--- a/src/login/logind-session.c ++++ b/src/login/logind-session.c +@@ -591,6 +591,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er + description, + STRV_MAKE(s->user->runtime_dir_service, s->user->service), /* These two have StopWhenUnneeded= set, hence add a dep towards them */ + STRV_MAKE("systemd-logind.service", "systemd-user-sessions.service", s->user->runtime_dir_service, s->user->service), /* And order us after some more */ ++ s->user->home, + properties, + error, + &s->scope_job); +diff --git a/src/login/logind-user.c b/src/login/logind-user.c +index f23fcbe674..70f5eb9d59 100644 +--- a/src/login/logind-user.c ++++ b/src/login/logind-user.c +@@ -30,7 +30,13 @@ + #include "user-util.h" + #include "util.h" + +-int user_new(User **ret, Manager *m, uid_t uid, gid_t gid, const char *name) { ++int user_new(User **ret, ++ Manager *m, ++ uid_t uid, ++ gid_t gid, ++ const char *name, ++ const char *home) { ++ + _cleanup_(user_freep) User *u = NULL; + char lu[DECIMAL_STR_MAX(uid_t) + 1]; + int r; +@@ -54,6 +60,10 @@ int user_new(User **ret, Manager *m, uid_t uid, gid_t gid, const char *name) { + if (!u->name) + return -ENOMEM; + ++ u->home = strdup(home); ++ if (!u->home) ++ return -ENOMEM; ++ + if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0) + return -ENOMEM; + +@@ -124,6 +134,7 @@ User *user_free(User *u) { + u->runtime_path = mfree(u->runtime_path); + u->state_file = mfree(u->state_file); + u->name = mfree(u->name); ++ u->home = mfree(u->home); + + return mfree(u); + } +diff --git a/src/login/logind-user.h b/src/login/logind-user.h +index e05646adc9..c41973e27d 100644 +--- a/src/login/logind-user.h ++++ b/src/login/logind-user.h +@@ -23,6 +23,7 @@ struct User { + uid_t uid; + gid_t gid; + char *name; ++ char *home; + char *state_file; + char *runtime_path; + +@@ -49,7 +50,7 @@ struct User { + LIST_FIELDS(User, gc_queue); + }; + +-int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name); ++int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name, const char *home); + User *user_free(User *u); + + DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free); +diff --git a/src/login/logind.h b/src/login/logind.h +index 7288dd7445..d29b01c75b 100644 +--- a/src/login/logind.h ++++ b/src/login/logind.h +@@ -129,7 +129,7 @@ int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_dev + int manager_add_button(Manager *m, const char *name, Button **_button); + int manager_add_seat(Manager *m, const char *id, Seat **_seat); + int manager_add_session(Manager *m, const char *id, Session **_session); +-int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user); ++int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, const char *home, User **_user); + int manager_add_user_by_name(Manager *m, const char *name, User **_user); + int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user); + int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor); +@@ -162,7 +162,7 @@ int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name + + int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_; + +-int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, char **wants, char **after, sd_bus_message *more_properties, sd_bus_error *error, char **job); ++int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, char **wants, char **after, const char *requires_mounts_for, sd_bus_message *more_properties, sd_bus_error *error, char **job); + int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); + int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); + int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error); diff --git a/SOURCES/0633-logind-improve-error-propagation-of-user_check_linge.patch b/SOURCES/0633-logind-improve-error-propagation-of-user_check_linge.patch new file mode 100644 index 0000000..9552207 --- /dev/null +++ b/SOURCES/0633-logind-improve-error-propagation-of-user_check_linge.patch @@ -0,0 +1,39 @@ +From 117ed6bd7aa71fc79599e1d37bdb4a94b3505a38 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 8 Aug 2018 16:03:11 +0200 +Subject: [PATCH] logind: improve error propagation of user_check_linger_file() + +Let's make this a bit prettier, and propagate unexpected access() errors +correctly. + +(The callers of this function will suppress them, but it's nicer of they +do that, rather than us doing that twice in both the callers and the +callees) + +(cherry picked from commit 6996df9b864981980f5b713dc5c7d506a7a4b9bf) + +Related: #1642460 +--- + src/login/logind-user.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/login/logind-user.c b/src/login/logind-user.c +index 70f5eb9d59..3fd28fc66c 100644 +--- a/src/login/logind-user.c ++++ b/src/login/logind-user.c +@@ -532,8 +532,14 @@ int user_check_linger_file(User *u) { + return -ENOMEM; + + p = strjoina("/var/lib/systemd/linger/", cc); ++ if (access(p, F_OK) < 0) { ++ if (errno != ENOENT) ++ return -errno; + +- return access(p, F_OK) >= 0; ++ return false; ++ } ++ ++ return true; + } + + bool user_may_gc(User *u, bool drop_not_started) { diff --git a/SOURCES/0634-logind-automatically-GC-lingering-users-for-who-now-.patch b/SOURCES/0634-logind-automatically-GC-lingering-users-for-who-now-.patch new file mode 100644 index 0000000..e299007 --- /dev/null +++ b/SOURCES/0634-logind-automatically-GC-lingering-users-for-who-now-.patch @@ -0,0 +1,82 @@ +From 89dd5e016a50da082e51129eecb3c5e04b8f0cf5 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 8 Aug 2018 16:04:40 +0200 +Subject: [PATCH] logind: automatically GC lingering users for who now + user@.service (nor slice, not runtime dir service) is running anymore + +This heavily borrows from @intelfx' PR #5546, but watches all three +units that are associated with a user now: the slice, the user@.service +and user-runtime-dir@.service. + +The logic and reasoning behind it is the same though: there's no value +in keeping lingering users around if all their three services are gone. + +Replaces: #5546 +Fixes: #4162 +(cherry picked from commit 4e5b605af202c770dfc8e3562d0f8d0440b2fe14) + +Related: #1642460 +--- + src/login/logind-user.c | 28 +++++++++++++++++++++++++--- + 1 file changed, 25 insertions(+), 3 deletions(-) + +diff --git a/src/login/logind-user.c b/src/login/logind-user.c +index 3fd28fc66c..bba3158d1a 100644 +--- a/src/login/logind-user.c ++++ b/src/login/logind-user.c +@@ -26,6 +26,7 @@ + #include "special.h" + #include "stdio-util.h" + #include "string-table.h" ++#include "strv.h" + #include "unit-name.h" + #include "user-util.h" + #include "util.h" +@@ -542,6 +543,25 @@ int user_check_linger_file(User *u) { + return true; + } + ++static bool user_unit_active(User *u) { ++ const char *i; ++ int r; ++ ++ assert(u->service); ++ assert(u->runtime_dir_service); ++ assert(u->slice); ++ ++ FOREACH_STRING(i, u->service, u->runtime_dir_service, u->slice) { ++ r = manager_unit_is_active(u->manager, i); ++ if (r < 0) ++ log_debug_errno(r, "Failed to determine whether unit '%s' is active, ignoring", u->service); ++ if (r != 0) ++ return true; ++ } ++ ++ return false; ++} ++ + bool user_may_gc(User *u, bool drop_not_started) { + assert(u); + +@@ -561,8 +581,10 @@ bool user_may_gc(User *u, bool drop_not_started) { + return false; /* Leave it around for a bit longer. */ + } + +- /* Is this a user that shall stay around forever? */ +- if (user_check_linger_file(u) > 0) ++ /* Is this a user that shall stay around forever ("linger")? Before we say "no" to GC'ing for lingering users, let's check ++ * if any of the three units that we maintain for this user is still around. If none of them is, ++ * there's no need to keep this user around even if lingering is enabled. */ ++ if (user_check_linger_file(u) > 0 && user_unit_active(u)) + return false; + + if (u->service_job && manager_job_is_active(u->manager, u->service_job)) +@@ -608,7 +630,7 @@ UserState user_get_state(User *u) { + return all_closing ? USER_CLOSING : USER_ONLINE; + } + +- if (user_check_linger_file(u) > 0) ++ if (user_check_linger_file(u) > 0 && user_unit_active(u)) + return USER_LINGERING; + + return USER_CLOSING; diff --git a/SOURCES/0635-pam_systemd-simplify-code-which-with-we-set-environm.patch b/SOURCES/0635-pam_systemd-simplify-code-which-with-we-set-environm.patch new file mode 100644 index 0000000..9eaff4c --- /dev/null +++ b/SOURCES/0635-pam_systemd-simplify-code-which-with-we-set-environm.patch @@ -0,0 +1,101 @@ +From 96887ddecd1e4c36d8a32411ed515ddaf0f3a0e3 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 20 Jul 2018 11:27:55 +0200 +Subject: [PATCH] pam_systemd: simplify code which with we set environment + variables + +Let's shorten things a bit by splitting out common code in a new +function. + +(cherry picked from commit d6baaa6978d3eb5b8e8497021c4ba576aee936a3) + +Related: #1642460 +--- + src/login/pam_systemd.c | 46 ++++++++++++++++++++++++----------------- + 1 file changed, 27 insertions(+), 19 deletions(-) + +diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c +index 78ddb7d398..b2b62540bb 100644 +--- a/src/login/pam_systemd.c ++++ b/src/login/pam_systemd.c +@@ -301,6 +301,24 @@ static const char* getenv_harder(pam_handle_t *handle, const char *key, const ch + return fallback; + } + ++static int update_environment(pam_handle_t *handle, const char *key, const char *value) { ++ int r; ++ ++ assert(handle); ++ assert(key); ++ ++ /* Updates the environment, but only if there's actually a value set. Also, log about errors */ ++ ++ if (isempty(value)) ++ return PAM_SUCCESS; ++ ++ r = pam_misc_setenv(handle, key, value, 0); ++ if (r != PAM_SUCCESS) ++ pam_syslog(handle, LOG_ERR, "Failed to set environment variable %s.", key); ++ ++ return r; ++} ++ + _public_ PAM_EXTERN int pam_sm_open_session( + pam_handle_t *handle, + int flags, +@@ -555,11 +573,9 @@ _public_ PAM_EXTERN int pam_sm_open_session( + "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u", + id, object_path, runtime_path, session_fd, seat, vtnr, original_uid); + +- r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0); +- if (r != PAM_SUCCESS) { +- pam_syslog(handle, LOG_ERR, "Failed to set session id."); ++ r = update_environment(handle, "XDG_SESSION_ID", id); ++ if (r != PAM_SUCCESS) + return r; +- } + + if (original_uid == pw->pw_uid) { + /* Don't set $XDG_RUNTIME_DIR if the user we now +@@ -568,34 +584,26 @@ _public_ PAM_EXTERN int pam_sm_open_session( + * in privileged apps clobbering the runtime directory + * unnecessarily. */ + +- r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0); +- if (r != PAM_SUCCESS) { +- pam_syslog(handle, LOG_ERR, "Failed to set runtime dir."); ++ r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path); ++ if (r != PAM_SUCCESS) + return r; +- } + + r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path); + if (r != PAM_SUCCESS) + return r; + } + +- if (!isempty(seat)) { +- r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0); +- if (r != PAM_SUCCESS) { +- pam_syslog(handle, LOG_ERR, "Failed to set seat."); +- return r; +- } +- } ++ r = update_environment(handle, "XDG_SEAT", seat); ++ if (r != PAM_SUCCESS) ++ return r; + + if (vtnr > 0) { + char buf[DECIMAL_STR_MAX(vtnr)]; + sprintf(buf, "%u", vtnr); + +- r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0); +- if (r != PAM_SUCCESS) { +- pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number."); ++ r = update_environment(handle, "XDG_VTNR", buf); ++ if (r != PAM_SUCCESS) + return r; +- } + } + + r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL); diff --git a/SOURCES/0636-logind-validate-run-user-1000-before-we-set-it.patch b/SOURCES/0636-logind-validate-run-user-1000-before-we-set-it.patch new file mode 100644 index 0000000..e485042 --- /dev/null +++ b/SOURCES/0636-logind-validate-run-user-1000-before-we-set-it.patch @@ -0,0 +1,89 @@ +From c8b74ac5cf508c7bcec92d197880043af1d2bad7 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 9 Oct 2018 22:23:41 +0200 +Subject: [PATCH] logind: validate /run/user/1000 before we set it + +Let's be safe than sorry, in particular as logind doesn't set it up +anymore, but user-runtime-dir@.service does, and logind doesn't really +track success of that. + +(cherry picked from commit b92171124819305985ed292cc472f6668a027425) + +Related: #1642460 +--- + src/login/pam_systemd.c | 48 +++++++++++++++++++++++++++++++++++------ + 1 file changed, 41 insertions(+), 7 deletions(-) + +diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c +index b2b62540bb..64e1b4d1bf 100644 +--- a/src/login/pam_systemd.c ++++ b/src/login/pam_systemd.c +@@ -319,6 +319,36 @@ static int update_environment(pam_handle_t *handle, const char *key, const char + return r; + } + ++static bool validate_runtime_directory(pam_handle_t *handle, const char *path, uid_t uid) { ++ struct stat st; ++ ++ assert(path); ++ ++ /* Just some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually set ++ * up properly for us. */ ++ ++ if (lstat(path, &st) < 0) { ++ pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror(errno)); ++ goto fail; ++ } ++ ++ if (!S_ISDIR(st.st_mode)) { ++ pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not actually a directory.", path); ++ goto fail; ++ } ++ ++ if (st.st_uid != uid) { ++ pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not owned by UID " UID_FMT ", as it should.", path, uid); ++ goto fail; ++ } ++ ++ return true; ++ ++fail: ++ pam_syslog(handle, LOG_WARNING, "Not setting $XDG_RUNTIME_DIR, as the directory is not in order."); ++ return false; ++} ++ + _public_ PAM_EXTERN int pam_sm_open_session( + pam_handle_t *handle, + int flags, +@@ -377,10 +407,12 @@ _public_ PAM_EXTERN int pam_sm_open_session( + if (asprintf(&rt, "/run/user/"UID_FMT, pw->pw_uid) < 0) + return PAM_BUF_ERR; + +- r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0); +- if (r != PAM_SUCCESS) { +- pam_syslog(handle, LOG_ERR, "Failed to set runtime dir."); +- return r; ++ if (validate_runtime_directory(handle, rt, pw->pw_uid)) { ++ r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0); ++ if (r != PAM_SUCCESS) { ++ pam_syslog(handle, LOG_ERR, "Failed to set runtime dir."); ++ return r; ++ } + } + + r = export_legacy_dbus_address(handle, pw->pw_uid, rt); +@@ -584,9 +616,11 @@ _public_ PAM_EXTERN int pam_sm_open_session( + * in privileged apps clobbering the runtime directory + * unnecessarily. */ + +- r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path); +- if (r != PAM_SUCCESS) +- return r; ++ if (validate_runtime_directory(handle, runtime_path, pw->pw_uid)) { ++ r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path); ++ if (r != PAM_SUCCESS) ++ return r; ++ } + + r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path); + if (r != PAM_SUCCESS) diff --git a/SPECS/systemd.spec b/SPECS/systemd.spec index b0a2787..bb99bcf 100644 --- a/SPECS/systemd.spec +++ b/SPECS/systemd.spec @@ -13,7 +13,7 @@ Name: systemd Url: http://www.freedesktop.org/wiki/Software/systemd Version: 239 -Release: 49%{?dist} +Release: 50%{?dist} # For a breakdown of the licensing, see README License: LGPLv2+ and MIT and GPLv2+ Summary: System and Service Manager @@ -660,6 +660,32 @@ Patch0607: 0607-ci-drop-forgotten-Travis-references.patch Patch0608: 0608-ci-run-unit-tests-on-CentOS-8-Stream-as-well.patch Patch0609: 0609-ci-add-missing-test-dependencies.patch Patch0610: 0610-meson-bump-timeout-for-test-udev-to-180s.patch +Patch0611: 0611-Added-option-check-inhibitors-for-non-tty-usage.patch +Patch0612: 0612-logind-Introduce-RebootWithFlags-and-others.patch +Patch0613: 0613-logind-add-WithFlags-methods-to-policy.patch +Patch0614: 0614-logind-simplify-flags-handling-a-bit.patch +Patch0615: 0615-Update-link-to-RHEL-documentation.patch +Patch0616: 0616-Set-default-core-ulimit-to-0-but-keep-the-hard-limit.patch +Patch0617: 0617-shared-seccomp-util-address-family-filtering-is-brok.patch +Patch0618: 0618-logind-rework-Seat-Session-User-object-allocation-an.patch +Patch0619: 0619-logind-fix-serialization-deserialization-of-user-s-d.patch +Patch0620: 0620-logind-turn-of-stdio-locking-when-writing-session-fi.patch +Patch0621: 0621-units-set-StopWhenUnneeded-for-the-user-slice-units-.patch +Patch0622: 0622-units-improve-Description-string-a-bit.patch +Patch0623: 0623-logind-improve-logging-in-manager_connect_console.patch +Patch0624: 0624-logind-save-restore-User-object-s-stopping-field-dur.patch +Patch0625: 0625-logind-correct-bad-clean-up-path.patch +Patch0626: 0626-logind-fix-bad-error-propagation.patch +Patch0627: 0627-logind-never-elect-a-session-that-is-stopping-as-dis.patch +Patch0628: 0628-logind-introduce-little-helper-that-checks-whether-a.patch +Patch0629: 0629-logind-propagate-session-stop-errors.patch +Patch0630: 0630-logind-rework-how-we-manage-the-slice-and-user-runti.patch +Patch0631: 0631-logind-optionally-keep-the-user-.service-instance-fo.patch +Patch0632: 0632-logind-add-a-RequiresMountsFor-dependency-from-the-s.patch +Patch0633: 0633-logind-improve-error-propagation-of-user_check_linge.patch +Patch0634: 0634-logind-automatically-GC-lingering-users-for-who-now-.patch +Patch0635: 0635-pam_systemd-simplify-code-which-with-we-set-environm.patch +Patch0636: 0636-logind-validate-run-user-1000-before-we-set-it.patch %ifarch %{ix86} x86_64 aarch64 @@ -1287,6 +1313,34 @@ fi %files tests -f .file-list-tests %changelog +* Fri Aug 27 2021 systemd maintenance team - 239-50 +- Added option --check-inhibitors for non-tty usage (#1269726) +- logind: Introduce RebootWithFlags and others (#1269726) +- logind: add …WithFlags methods to policy (#1269726) +- logind: simplify flags handling a bit (#1269726) +- Update link to RHEL documentation (#1982584) +- Set default core ulimit to 0, but keep the hard limit ulimited (#1905582) +- shared/seccomp-util: address family filtering is broken on ppc (#1982650) +- logind: rework Seat/Session/User object allocation and freeing a bit (#1642460) +- logind: fix serialization/deserialization of user's "display session" (#1642460) +- logind: turn of stdio locking when writing session files too (#1642460) +- units: set StopWhenUnneeded= for the user slice units too (#1642460) +- units: improve Description= string a bit (#1642460) +- logind: improve logging in manager_connect_console() (#1642460) +- logind: save/restore User object's "stopping" field during restarts (#1642460) +- logind: correct bad clean-up path (#1642460) +- logind: fix bad error propagation (#1642460) +- logind: never elect a session that is stopping as display (#1642460) +- logind: introduce little helper that checks whether a session is ready (#1642460) +- logind: propagate session stop errors (#1642460) +- logind: rework how we manage the slice and user-runtime-dir@.service unit for each user (#1642460) +- logind: optionally, keep the user@.service instance for eached logged in user around for a while (#1642460) +- logind: add a RequiresMountsFor= dependency from the session scope unit to the home directory of the user (#1642460) +- logind: improve error propagation of user_check_linger_file() (#1642460) +- logind: automatically GC lingering users for who now user@.service (nor slice, not runtime dir service) is running anymore (#1642460) +- pam_systemd: simplify code which with we set environment variables (#1642460) +- logind: validate /run/user/1000 before we set it (#1642460) + * Fri Jul 23 2021 systemd maintenance team - 239-49 - remove a left-over break (#1970860) - basic/unit-name: do not use strdupa() on a path (#1974700)