X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flogin%2Floginctl.c;h=a47563b506921fd095a8b1a9beafaedeabcc39d5;hp=c62ae326fb0210cd98d0c1d4e517a25373b5f86f;hb=801daa75024fb177df1535f8e16a450bc68619fd;hpb=86e1f46f92ff4c447ea32a6892ea394c6e6ef541 diff --git a/src/login/loginctl.c b/src/login/loginctl.c index c62ae326f..a47563b50 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" @@ -42,6 +42,9 @@ #include "cgroup-util.h" #include "spawn-polkit-agent.h" #include "verbs.h" +#include "process-util.h" +#include "terminal-util.h" +#include "signal-util.h" static char **arg_property = NULL; static bool arg_all = false; @@ -53,6 +56,7 @@ 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; static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; @@ -275,42 +279,81 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit } 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; @@ -402,7 +445,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 +536,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 +552,7 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li true, NULL); } +#endif } return 0; @@ -530,14 +574,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 +619,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 +632,10 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) SD_JOURNAL_LOCAL_ONLY, true, NULL); +#endif } -finish: - strv_free(i.sessions); - - return r; + return 0; } static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) { @@ -607,14 +647,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 +689,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 +881,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 +889,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 +939,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 +959,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 +970,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 +1023,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 +1060,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 +1089,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 +1117,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 +1184,248 @@ static int terminate_seat(int argc, char *argv[], void *userdata) { return 0; } +static int check_inhibitors(sd_bus *bus, const char *verb, const char *inhibit_what) { + _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; + + assert(bus); + + 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 (!strv_contains(sv, inhibit_what)) + 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, 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.\nAlternatively, ignore inhibitors and users with 'loginctl %s -i'.", verb); + + return -EPERM; +} + +static int poweroff(int argc, char *argv[], void *userdata) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + sd_bus *bus = userdata; + int r; + + assert(bus); + + r = check_inhibitors(bus, "poweroff", "shutdown"); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "PowerOff", + &error, + NULL, + "b", arg_ask_password); + if (r < 0) + log_error("Failed to power off: %s", bus_error_message(&error, r)); + + return r; +} + +static int reboot(int argc, char *argv[], void *userdata) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + sd_bus *bus = userdata; + int r; + + assert(bus); + + r = check_inhibitors(bus, "reboot", "shutdown"); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "Reboot", + &error, + NULL, + "b", arg_ask_password); + if (r < 0) + log_error("Failed to reboot: %s", bus_error_message(&error, r)); + + return r; +} + +static int suspend(int argc, char *argv[], void *userdata) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + sd_bus *bus = userdata; + int r; + + assert(bus); + + r = check_inhibitors(bus, "suspend", "sleep"); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "Suspend", + &error, + NULL, + "b", arg_ask_password); + if (r < 0) + log_error("Failed to suspend: %s", bus_error_message(&error, r)); + + return r; +} + +static int hibernate(int argc, char *argv[], void *userdata) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + sd_bus *bus = userdata; + int r; + + assert(bus); + + r = check_inhibitors(bus, "hibernate", "sleep"); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "Hibernate", + &error, + NULL, + "b", arg_ask_password); + if (r < 0) + log_error("Failed to hibernate: %s", bus_error_message(&error, r)); + + return r; +} + +static int hybrid_sleep(int argc, char *argv[], void *userdata) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + sd_bus *bus = userdata; + int r; + + assert(bus); + + r = check_inhibitors(bus, "hybrid-sleep", "sleep"); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "HybridSleep", + &error, + NULL, + "b", arg_ask_password); + if (r < 0) + log_error("Failed to hybrid sleep: %s", bus_error_message(&error, r)); + + return r; +} + static int help(int argc, char *argv[], void *userdata) { printf("%s [OPTIONS...] {COMMAND} ...\n\n" @@ -1131,6 +1435,7 @@ static int help(int argc, char *argv[], void *userdata) { " --no-pager Do not pipe output into a pager\n" " --no-legend Do not show the headers and footers\n" " --no-ask-password Don't prompt for password\n" + " -i --ignore-inhibitors Ignore inhibitors when suspending or shutting down\n" " -H --host=[USER@]HOST Operate on remote host\n" " -M --machine=CONTAINER Operate on local container\n" " -p --property=NAME Show only properties by this name\n" @@ -1145,9 +1450,9 @@ static int help(int argc, char *argv[], void *userdata) { " 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 +1461,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" @@ -1166,7 +1471,13 @@ static int help(int argc, char *argv[], void *userdata) { " show-seat [NAME...] Show properties of seats or the manager\n" " attach NAME DEVICE... Attach one or more devices to a seat\n" " flush-devices Flush all device associations\n" - " terminate-seat NAME... Terminate all sessions on one or more seats\n" + " terminate-seat NAME... Terminate all sessions on one or more seats\n\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,6 +1506,7 @@ static int parse_argv(int argc, char *argv[]) { { "host", required_argument, NULL, 'H' }, { "machine", required_argument, NULL, 'M' }, { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "ignore-inhibitors", no_argument, NULL, 'i' }, { "lines", required_argument, NULL, 'n' }, { "output", required_argument, NULL, 'o' }, {} @@ -1205,7 +1517,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:n:o:i", options, NULL)) >= 0) switch (c) { @@ -1246,13 +1558,21 @@ static int parse_argv(int argc, char *argv[]) { break; case 'o': +#if 0 arg_output = output_mode_from_string(optarg); +#else + arg_output = -1; +#endif if (arg_output < 0) { log_error("Unknown output '%s'.", optarg); return -EINVAL; } break; + case 'i': + arg_ignore_inhibitors = true; + break; + case ARG_NO_PAGER: arg_no_pager = true; break; @@ -1304,9 +1624,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 +1634,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 +1644,11 @@ 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, poweroff }, + { "reboot", VERB_ANY, 1, 0, reboot }, + { "suspend", VERB_ANY, 1, 0, suspend }, + { "hibernate", VERB_ANY, 1, 0, hibernate }, + { "hybrid-sleep", VERB_ANY, 1, 0, hybrid_sleep }, {} }; @@ -1348,6 +1673,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: