X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flogin%2Floginctl.c;h=bd7ec27da42500435537949aaaddc96b8c47d702;hp=ec102ae4c7c7e62e0d9ab2f82ceecf2d2250ef08;hb=f88c58a33748ad36866544b45e4fe06c506d4c31;hpb=2eec67acbb00593e414549a7e5b35eb7dd776b1b diff --git a/src/login/loginctl.c b/src/login/loginctl.c index ec102ae4c..bd7ec27da 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -26,6 +26,7 @@ #include #include "sd-bus.h" +#include "sd-login.h" #include "bus-util.h" #include "bus-error.h" #include "log.h" @@ -41,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; @@ -52,8 +56,10 @@ static int arg_signal = SIGTERM; static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static char *arg_host = NULL; static bool arg_ask_password = true; +#if 0 static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; +#endif // 0 static void pager_open_if_enabled(void) { @@ -260,7 +266,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, false) != 0 && leader <= 0) return 0; c = columns(); @@ -269,47 +275,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; @@ -401,7 +446,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); @@ -492,7 +537,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( @@ -508,6 +553,8 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li true, NULL); } + +#endif // 0 } return 0; @@ -529,14 +576,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"); @@ -576,7 +621,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, @@ -589,12 +634,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) { @@ -606,14 +650,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"); @@ -650,10 +692,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) { @@ -1148,6 +1187,245 @@ 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 (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" @@ -1164,9 +1442,11 @@ static int help(int argc, char *argv[], void *userdata) { " -l --full Do not ellipsize output\n" " --kill-who=WHO Who to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" +#if 0 " -n --lines=INTEGER Number of journal entries to show\n" " -o --output=STRING Change journal output mode (short, short-monotonic,\n" " verbose, export, json, json-pretty, json-sse, cat)\n\n" +#endif // 0 "Session Commands:\n" " list-sessions List sessions\n" " session-status [ID...] Show session status\n" @@ -1193,6 +1473,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; @@ -1221,8 +1507,10 @@ static int parse_argv(int argc, char *argv[]) { { "host", required_argument, NULL, 'H' }, { "machine", required_argument, NULL, 'M' }, { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, +#if 0 { "lines", required_argument, NULL, 'n' }, { "output", required_argument, NULL, 'o' }, +#endif // {} }; @@ -1231,7 +1519,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) switch (c) { @@ -1263,7 +1551,7 @@ static int parse_argv(int argc, char *argv[]) { case 'l': arg_full = true; break; - +#if 0 case 'n': if (safe_atou(optarg, &arg_lines) < 0) { log_error("Failed to parse lines '%s'", optarg); @@ -1278,7 +1566,7 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } break; - +#endif // 0 case ARG_NO_PAGER: arg_no_pager = true; break; @@ -1350,6 +1638,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 }, {} }; @@ -1357,7 +1650,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, "");