X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flogin%2Floginctl.c;h=16d80ab57078cebecf10086d786a3692ebf6e78c;hb=6f5035f2db6ab7b4beae46c44b95e6a8b6d956b5;hp=c62ae326fb0210cd98d0c1d4e517a25373b5f86f;hpb=86e1f46f92ff4c447ea32a6892ea394c6e6ef541;p=elogind.git diff --git a/src/login/loginctl.c b/src/login/loginctl.c index c62ae326f..16d80ab57 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" @@ -53,6 +53,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; @@ -493,7 +494,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 +510,7 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li true, NULL); } +#endif } return 0; @@ -577,7 +579,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,6 +592,7 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) SD_JOURNAL_LOCAL_ONLY, true, NULL); +#endif } finish: @@ -846,6 +849,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 +857,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 +907,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 +927,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 +938,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 +991,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 +1028,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 +1057,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 +1085,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 +1152,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 +1403,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 +1418,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 +1429,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 +1439,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 +1474,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 +1485,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 +1526,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 +1592,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 +1602,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 +1612,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 +1641,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: