X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flogin%2Floginctl.c;h=543f14063cbc1e68476484f2b66c3697822b0245;hb=bf2098e9e4837573d84dd4949b9c28a7372c93b9;hp=c62ae326fb0210cd98d0c1d4e517a25373b5f86f;hpb=86e1f46f92ff4c447ea32a6892ea394c6e6ef541;p=elogind.git diff --git a/src/login/loginctl.c b/src/login/loginctl.c index c62ae326f..543f14063 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -23,10 +23,10 @@ #include #include #include -#include #include #include "sd-bus.h" +#include "sd-login.h" #include "bus-util.h" #include "bus-error.h" #include "log.h" @@ -37,11 +37,15 @@ #include "strv.h" #include "unit-name.h" #include "sysfs-show.h" -#include "logs-show.h" +// #include "logs-show.h" #include "cgroup-show.h" #include "cgroup-util.h" #include "spawn-polkit-agent.h" #include "verbs.h" +#include "process-util.h" +#include "terminal-util.h" +#include "signal-util.h" +#include "logind-action.h" static char **arg_property = NULL; static bool arg_all = false; @@ -53,8 +57,22 @@ static int arg_signal = SIGTERM; static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static char *arg_host = NULL; static bool arg_ask_password = true; +static bool arg_ignore_inhibitors = false; +#if 0 static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; +#endif // 0 +static enum action { + _ACTION_INVALID, + ACTION_POWEROFF, + ACTION_REBOOT, + ACTION_SUSPEND, + ACTION_HIBERNATE, + ACTION_HYBRID_SLEEP, + ACTION_CANCEL_SHUTDOWN, + _ACTION_MAX +} arg_action; + static void pager_open_if_enabled(void) { @@ -261,7 +279,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit if (isempty(cgroup)) return 0; - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0) + if (cg_is_empty_recursive(ELOGIND_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) return 0; c = columns(); @@ -270,47 +288,86 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit else c = 0; - show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags()); + show_cgroup_and_extra(ELOGIND_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags()); return 0; } typedef struct SessionStatusInfo { - const char *id; + char *id; uid_t uid; - const char *name; + char *name; struct dual_timestamp timestamp; unsigned int vtnr; - const char *seat; - const char *tty; - const char *display; + char *seat; + char *tty; + char *display; bool remote; - const char *remote_host; - const char *remote_user; - const char *service; + char *remote_host; + char *remote_user; + char *service; pid_t leader; - const char *type; - const char *class; - const char *state; - const char *scope; - const char *desktop; + char *type; + char *class; + char *state; + char *scope; + char *desktop; } SessionStatusInfo; typedef struct UserStatusInfo { uid_t uid; - const char *name; + char *name; struct dual_timestamp timestamp; - const char *state; + char *state; char **sessions; - const char *display; - const char *slice; + char *display; + char *slice; } UserStatusInfo; typedef struct SeatStatusInfo { - const char *id; - const char *active_session; + char *id; + char *active_session; char **sessions; } SeatStatusInfo; +static void session_status_info_clear(SessionStatusInfo *info) { + if (info) { + free(info->id); + free(info->name); + free(info->seat); + free(info->tty); + free(info->display); + free(info->remote_host); + free(info->remote_user); + free(info->service); + free(info->type); + free(info->class); + free(info->state); + free(info->scope); + free(info->desktop); + zero(*info); + } +} + +static void user_status_info_clear(UserStatusInfo *info) { + if (info) { + free(info->name); + free(info->state); + strv_free(info->sessions); + free(info->display); + free(info->slice); + zero(*info); + } +} + +static void seat_status_info_clear(SeatStatusInfo *info) { + if (info) { + free(info->id); + free(info->active_session); + strv_free(info->sessions); + zero(*info); + } +} + static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { const char *contents; int r; @@ -331,11 +388,9 @@ static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_mess if (r < 0) return r; - free(*p); - *p = strdup(s); - - if (!*p) - return -ENOMEM; + r = free_and_strdup(p, s); + if (r < 0) + return r; } else { r = sd_bus_message_read_basic(m, contents[0], userdata); if (r < 0) @@ -402,7 +457,7 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; char since2[FORMAT_TIMESTAMP_MAX], *s2; - SessionStatusInfo i = {}; + _cleanup_(session_status_info_clear) SessionStatusInfo i = {}; int r; r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); @@ -493,7 +548,7 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li if (i.scope) { printf("\t Unit: %s\n", i.scope); show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader); - +#if 0 if (arg_transport == BUS_TRANSPORT_LOCAL) { show_journal_by_unit( @@ -509,6 +564,8 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li true, NULL); } + +#endif // 0 } return 0; @@ -530,14 +587,12 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; char since2[FORMAT_TIMESTAMP_MAX], *s2; - UserStatusInfo i = {}; + _cleanup_(user_status_info_clear) UserStatusInfo i = {}; int r; r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); - if (r < 0) { - log_error_errno(r, "Could not get properties: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Could not get properties: %m"); if (*new_line) printf("\n"); @@ -577,7 +632,7 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) if (i.slice) { printf("\t Unit: %s\n", i.slice); show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0); - +#if 0 show_journal_by_unit( stdout, i.slice, @@ -590,12 +645,11 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) SD_JOURNAL_LOCAL_ONLY, true, NULL); - } -finish: - strv_free(i.sessions); +#endif // 0 + } - return r; + return 0; } static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) { @@ -607,14 +661,12 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {} }; - SeatStatusInfo i = {}; + _cleanup_(seat_status_info_clear) SeatStatusInfo i = {}; int r; r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); - if (r < 0) { - log_error_errno(r, "Could not get properties: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Could not get properties: %m"); if (*new_line) printf("\n"); @@ -651,10 +703,7 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) show_sysfs(i.id, "\t\t ", c); } -finish: - strv_free(i.sessions); - - return r; + return 0; } static int show_properties(sd_bus *bus, const char *path, bool *new_line) { @@ -846,6 +895,7 @@ static int show_seat(int argc, char *argv[], void *userdata) { static int activate(int argc, char *argv[], void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; + char *short_argv[3]; int r, i; assert(bus); @@ -853,9 +903,22 @@ static int activate(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(); + if (argc < 2) { + /* No argument? Let's convert this into the empty + * session name, which the calls will then resolve to + * the caller's session. */ + + short_argv[0] = argv[0]; + short_argv[1] = (char*) ""; + short_argv[2] = NULL; + + argv = short_argv; + argc = 2; + } + for (i = 1; i < argc; i++) { - r = sd_bus_call_method ( + r = sd_bus_call_method( bus, "org.freedesktop.login1", "/org/freedesktop/login1", @@ -890,7 +953,7 @@ static int kill_session(int argc, char *argv[], void *userdata) { for (i = 1; i < argc; i++) { - r = sd_bus_call_method ( + r = sd_bus_call_method( bus, "org.freedesktop.login1", "/org/freedesktop/login1", @@ -910,6 +973,7 @@ static int kill_session(int argc, char *argv[], void *userdata) { static int enable_linger(int argc, char *argv[], void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; + char* short_argv[3]; bool b; int r, i; @@ -920,14 +984,26 @@ static int enable_linger(int argc, char *argv[], void *userdata) { b = streq(argv[0], "enable-linger"); + if (argc < 2) { + short_argv[0] = argv[0]; + short_argv[1] = (char*) ""; + short_argv[2] = NULL; + argv = short_argv; + argc = 2; + } + for (i = 1; i < argc; i++) { uid_t uid; - r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); - if (r < 0) - return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); + if (isempty(argv[i])) + uid = UID_INVALID; + else { + r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); + } - r = sd_bus_call_method ( + r = sd_bus_call_method( bus, "org.freedesktop.login1", "/org/freedesktop/login1", @@ -961,7 +1037,7 @@ static int terminate_user(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); - r = sd_bus_call_method ( + r = sd_bus_call_method( bus, "org.freedesktop.login1", "/org/freedesktop/login1", @@ -998,7 +1074,7 @@ static int kill_user(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); - r = sd_bus_call_method ( + r = sd_bus_call_method( bus, "org.freedesktop.login1", "/org/freedesktop/login1", @@ -1027,7 +1103,7 @@ static int attach(int argc, char *argv[], void *userdata) { for (i = 2; i < argc; i++) { - r = sd_bus_call_method ( + r = sd_bus_call_method( bus, "org.freedesktop.login1", "/org/freedesktop/login1", @@ -1055,7 +1131,7 @@ static int flush_devices(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(); - r = sd_bus_call_method ( + r = sd_bus_call_method( bus, "org.freedesktop.login1", "/org/freedesktop/login1", @@ -1122,6 +1198,254 @@ static int terminate_seat(int argc, char *argv[], void *userdata) { return 0; } +/* Ask elogind, which might grant access to unprivileged users + * through PolicyKit */ +static int reboot_with_logind(sd_bus *bus, enum action a) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + const char *method; + int r; + static const char *table[_ACTION_MAX] = { + [ACTION_REBOOT] = "The system is going down for reboot NOW!", + [ACTION_POWEROFF] = "The system is going down for power-off NOW!", + [ACTION_CANCEL_SHUTDOWN] = "The system shutdown has been cancelled NOW!" + }; + + if (!bus) + return -EIO; + + polkit_agent_open_if_enabled(); + + switch (a) { + + case ACTION_POWEROFF: + method = "PowerOff"; + break; + + case ACTION_REBOOT: + method = "Reboot"; + break; + + case ACTION_SUSPEND: + method = "Suspend"; + break; + + case ACTION_HIBERNATE: + method = "Hibernate"; + break; + + case ACTION_HYBRID_SLEEP: + method = "HybridSleep"; + break; + + case ACTION_CANCEL_SHUTDOWN: + method = "CancelScheduledShutdown"; + break; + + default: + return -EINVAL; + } + + if (table[a]) { + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetWallMessage", + &error, + NULL, + "sb", + table[a], + true); + + if (r < 0) { + log_warning_errno(r, "Failed to set wall message, ignoring: %s", + bus_error_message(&error, r)); + sd_bus_error_free(&error); + } + } + + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + method, + &error, + NULL, + "b", arg_ask_password); + if (r < 0) + log_error("Failed to execute operation: %s", bus_error_message(&error, r)); + + return r; +} + +static const struct { + HandleAction action; + const char* verb; +} action_table[_ACTION_MAX] = { + [ACTION_POWEROFF] = { HANDLE_POWEROFF, "poweroff", }, + [ACTION_REBOOT] = { HANDLE_REBOOT, "reboot", }, + [ACTION_SUSPEND] = { HANDLE_SUSPEND, "suspend", }, + [ACTION_HIBERNATE] = { HANDLE_HIBERNATE, "hibernate", }, + [ACTION_HYBRID_SLEEP] = { HANDLE_HYBRID_SLEEP, "hybrid-sleep" }, +}; + +static enum action verb_to_action(const char *verb) { + enum action i; + + for (i = _ACTION_INVALID; i < _ACTION_MAX; i++) + if (streq_ptr(action_table[i].verb, verb)) + return i; + + return _ACTION_INVALID; +} + +static int check_inhibitors(sd_bus *bus, enum action a) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_strv_free_ char **sessions = NULL; + const char *what, *who, *why, *mode; + uint32_t uid, pid; + unsigned c = 0; + char **s; + int r; + + if (!bus) + return 0; + + if (arg_ignore_inhibitors) + return 0; + + if (geteuid() == 0) + return 0; + + if (!on_tty()) + return 0; + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListInhibitors", + NULL, + &reply, + NULL); + if (r < 0) + /* If logind is not around, then there are no inhibitors... */ + return 0; + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) { + _cleanup_free_ char *comm = NULL, *user = NULL; + _cleanup_strv_free_ char **sv = NULL; + + if (!streq(mode, "block")) + continue; + + sv = strv_split(what, ":"); + if (!sv) + return log_oom(); + + if ((pid_t) pid < 0) + return log_error_errno(ERANGE, "Bad PID %"PRIu32": %m", pid); + + if (!strv_contains(sv, + a == ACTION_POWEROFF || + a == ACTION_REBOOT ? "shutdown" : "sleep")) + continue; + + get_process_comm(pid, &comm); + user = uid_to_name(uid); + + log_warning("Operation inhibited by \"%s\" (PID "PID_FMT" \"%s\", user %s), reason is \"%s\".", + who, (pid_t) pid, strna(comm), strna(user), why); + + c++; + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + /* Check for current sessions */ + sd_get_sessions(&sessions); + STRV_FOREACH(s, sessions) { + _cleanup_free_ char *type = NULL, *tty = NULL, *seat = NULL, *user = NULL, *service = NULL, *class = NULL; + + if (sd_session_get_uid(*s, &uid) < 0 || uid == getuid()) + continue; + + if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user")) + continue; + + if (sd_session_get_type(*s, &type) < 0 || (!streq(type, "x11") && !streq(type, "tty"))) + continue; + + sd_session_get_tty(*s, &tty); + sd_session_get_seat(*s, &seat); + sd_session_get_service(*s, &service); + user = uid_to_name(uid); + + log_warning("User %s is logged in on %s.", strna(user), isempty(tty) ? (isempty(seat) ? strna(service) : seat) : tty); + c++; + } + + if (c <= 0) + return 0; + + log_error("Please retry operation after closing inhibitors and logging out other users.\n" + "Alternatively, ignore inhibitors and users with 'loginctl %s -i'.", + action_table[a].verb); + + return -EPERM; +} + +static int start_special(int argc, char *argv[], void *userdata) { + sd_bus *bus = userdata; + enum action a; + int r; + + assert(argv); + + a = verb_to_action(argv[0]); + + r = check_inhibitors(bus, a); + if (r < 0) + return r; + + /* Now power off actions in chroot environments */ + if ((a == ACTION_POWEROFF || + a == ACTION_REBOOT) && + (running_in_chroot() > 0) ) { + log_info("Running in chroot, ignoring request."); + return 0; + } + + /* Switch to cancel shutdown, if a shutdown action was requested, + and the option to cancel it was set: */ + if ((a == ACTION_POWEROFF || + a == ACTION_REBOOT) && + (arg_action == ACTION_CANCEL_SHUTDOWN)) + return reboot_with_logind(bus, arg_action); + + /* Otherwise perform requested action */ + if (a == ACTION_POWEROFF || + a == ACTION_REBOOT || + a == ACTION_SUSPEND || + a == ACTION_HIBERNATE || + a == ACTION_HYBRID_SLEEP) + return reboot_with_logind(bus, a); + + return -EOPNOTSUPP; +} + static int help(int argc, char *argv[], void *userdata) { printf("%s [OPTIONS...] {COMMAND} ...\n\n" @@ -1138,16 +1462,20 @@ static int help(int argc, char *argv[], void *userdata) { " -l --full Do not ellipsize output\n" " --kill-who=WHO Who to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" +#if 0 " -n --lines=INTEGER Number of journal entries to show\n" " -o --output=STRING Change journal output mode (short, short-monotonic,\n" " verbose, export, json, json-pretty, json-sse, cat)\n\n" +#endif // 0 + " -c Cancel a pending shutdown\n" + " -i --ignore-inhibitors When shutting down or sleeping, ignore inhibitors\n" "Session Commands:\n" " list-sessions List sessions\n" " session-status [ID...] Show session status\n" " show-session [ID...] Show properties of sessions or the manager\n" - " activate ID Activate a session\n" - " lock-session ID... Screen lock one or more sessions\n" - " unlock-session ID... Screen unlock one or more sessions\n" + " activate [ID] Activate a session\n" + " lock-session [ID...] Screen lock one or more sessions\n" + " unlock-session [ID...] Screen unlock one or more sessions\n" " lock-sessions Screen lock all current sessions\n" " unlock-sessions Screen unlock all current sessions\n" " terminate-session ID... Terminate one or more sessions\n" @@ -1156,8 +1484,8 @@ static int help(int argc, char *argv[], void *userdata) { " list-users List users\n" " user-status [USER...] Show user status\n" " show-user [USER...] Show properties of users or the manager\n" - " enable-linger USER... Enable linger state of one or more users\n" - " disable-linger USER... Disable linger state of one or more users\n" + " enable-linger [USER...] Enable linger state of one or more users\n" + " disable-linger [USER...] Disable linger state of one or more users\n" " terminate-user USER... Terminate all sessions of one or more users\n" " kill-user USER... Send signal to processes of a user\n\n" "Seat Commands:\n" @@ -1167,6 +1495,12 @@ static int help(int argc, char *argv[], void *userdata) { " attach NAME DEVICE... Attach one or more devices to a seat\n" " flush-devices Flush all device associations\n" " terminate-seat NAME... Terminate all sessions on one or more seats\n" + "System Commands:\n" + " poweroff Turn off the machine\n" + " reboot Reboot the machine\n" + " suspend Suspend the machine to memory\n" + " hibernate Suspend the machine to disk\n" + " hybrid-sleep Suspend the machine to memory and disk\n" , program_invocation_short_name); return 0; @@ -1195,8 +1529,11 @@ static int parse_argv(int argc, char *argv[]) { { "host", required_argument, NULL, 'H' }, { "machine", required_argument, NULL, 'M' }, { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, +#if 0 { "lines", required_argument, NULL, 'n' }, { "output", required_argument, NULL, 'o' }, +#endif // + { "ignore-inhibitors", no_argument, NULL, 'i' }, {} }; @@ -1205,7 +1542,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hp:als:H:M:ci", options, NULL)) >= 0) switch (c) { @@ -1237,7 +1574,7 @@ static int parse_argv(int argc, char *argv[]) { case 'l': arg_full = true; break; - +#if 0 case 'n': if (safe_atou(optarg, &arg_lines) < 0) { log_error("Failed to parse lines '%s'", optarg); @@ -1252,7 +1589,7 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } break; - +#endif // 0 case ARG_NO_PAGER: arg_no_pager = true; break; @@ -1287,6 +1624,14 @@ static int parse_argv(int argc, char *argv[]) { arg_host = optarg; break; + case 'c': + arg_action = ACTION_CANCEL_SHUTDOWN; + break; + + case 'i': + arg_ignore_inhibitors = true; + break; + case '?': return -EINVAL; @@ -1304,9 +1649,9 @@ static int loginctl_main(int argc, char *argv[], sd_bus *bus) { { "list-sessions", VERB_ANY, 1, VERB_DEFAULT, list_sessions }, { "session-status", VERB_ANY, VERB_ANY, 0, show_session }, { "show-session", VERB_ANY, VERB_ANY, 0, show_session }, - { "activate", 2, 2, 0, activate }, - { "lock-session", 2, VERB_ANY, 0, activate }, - { "unlock-session", 2, VERB_ANY, 0, activate }, + { "activate", VERB_ANY, 2, 0, activate }, + { "lock-session", VERB_ANY, VERB_ANY, 0, activate }, + { "unlock-session", VERB_ANY, VERB_ANY, 0, activate }, { "lock-sessions", VERB_ANY, 1, 0, lock_sessions }, { "unlock-sessions", VERB_ANY, 1, 0, lock_sessions }, { "terminate-session", 2, VERB_ANY, 0, activate }, @@ -1314,8 +1659,8 @@ static int loginctl_main(int argc, char *argv[], sd_bus *bus) { { "list-users", VERB_ANY, 1, 0, list_users }, { "user-status", VERB_ANY, VERB_ANY, 0, show_user }, { "show-user", VERB_ANY, VERB_ANY, 0, show_user }, - { "enable-linger", 2, VERB_ANY, 0, enable_linger }, - { "disable-linger", 2, VERB_ANY, 0, enable_linger }, + { "enable-linger", VERB_ANY, VERB_ANY, 0, enable_linger }, + { "disable-linger", VERB_ANY, VERB_ANY, 0, enable_linger }, { "terminate-user", 2, VERB_ANY, 0, terminate_user }, { "kill-user", 2, VERB_ANY, 0, kill_user }, { "list-seats", VERB_ANY, 1, 0, list_seats }, @@ -1324,6 +1669,12 @@ static int loginctl_main(int argc, char *argv[], sd_bus *bus) { { "attach", 3, VERB_ANY, 0, attach }, { "flush-devices", VERB_ANY, 1, 0, flush_devices }, { "terminate-seat", 2, VERB_ANY, 0, terminate_seat }, + { "poweroff", VERB_ANY, 1, 0, start_special }, + { "reboot", VERB_ANY, 1, 0, start_special }, + { "suspend", VERB_ANY, 1, 0, start_special }, + { "hibernate", VERB_ANY, 1, 0, start_special }, + { "hybrid-sleep", VERB_ANY, 1, 0, start_special }, + { "cancel-shutdown", VERB_ANY, 1, 0, start_special }, {} }; @@ -1331,7 +1682,7 @@ static int loginctl_main(int argc, char *argv[], sd_bus *bus) { } int main(int argc, char *argv[]) { - _cleanup_bus_close_unref_ sd_bus *bus = NULL; + _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL; int r; setlocale(LC_ALL, ""); @@ -1348,6 +1699,8 @@ int main(int argc, char *argv[]) { goto finish; } + sd_bus_set_allow_interactive_authorization(bus, arg_ask_password); + r = loginctl_main(argc, argv, bus); finish: