From de07ab16c6b919cead26c9a5209a362127ff6142 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Jul 2011 19:58:35 +0200 Subject: [PATCH] loginctl: implement missing kill verb --- src/loginctl.c | 149 +++++++++++++++++++++++++++++++++++--- src/logind-dbus.c | 76 +++++++++++++++++++ src/logind-session-dbus.c | 36 +++++++++ src/logind-session.c | 53 +++++++++++++- src/logind-session.h | 11 +++ src/logind-user-dbus.c | 24 ++++++ src/logind-user.c | 29 +++++++- src/logind-user.h | 1 + 8 files changed, 365 insertions(+), 14 deletions(-) diff --git a/src/loginctl.c b/src/loginctl.c index bb5be908..829213e3 100644 --- a/src/loginctl.c +++ b/src/loginctl.c @@ -1208,7 +1208,63 @@ finish: } static int kill_session(DBusConnection *bus, char **args, unsigned n) { - return 0; + DBusMessage *m = NULL, *reply = NULL; + int ret = 0; + DBusError error; + unsigned i; + + assert(bus); + assert(args); + + dbus_error_init(&error); + + if (!arg_kill_who) + arg_kill_who = "all"; + + for (i = 1; i < n; i++) { + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "KillSession"); + if (!m) { + log_error("Could not allocate message."); + ret = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &args[i], + DBUS_TYPE_STRING, &arg_kill_who, + DBUS_TYPE_INT32, arg_signal, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + ret = -ENOMEM; + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(&error)); + ret = -EIO; + goto finish; + } + + dbus_message_unref(m); + dbus_message_unref(reply); + m = reply = NULL; + } + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return ret; } static int enable_linger(DBusConnection *bus, char **args, unsigned n) { @@ -1358,6 +1414,81 @@ finish: return ret; } +static int kill_user(DBusConnection *bus, char **args, unsigned n) { + DBusMessage *m = NULL, *reply = NULL; + int ret = 0; + DBusError error; + unsigned i; + + assert(bus); + assert(args); + + dbus_error_init(&error); + + if (!arg_kill_who) + arg_kill_who = "all"; + + for (i = 1; i < n; i++) { + uint32_t u; + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "KillUser"); + if (!m) { + log_error("Could not allocate message."); + ret = -ENOMEM; + goto finish; + } + + if (safe_atou32(args[i], &u) < 0) { + struct passwd *pw; + + errno = 0; + pw = getpwnam(args[i]); + if (!pw) { + ret = errno ? -errno : -ENOENT; + log_error("Failed to look up user %s: %s", args[i], strerror(-ret)); + goto finish; + } + + u = pw->pw_uid; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_UINT32, &u, + DBUS_TYPE_INT32, arg_signal, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + ret = -ENOMEM; + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(&error)); + ret = -EIO; + goto finish; + } + + dbus_message_unref(m); + dbus_message_unref(reply); + m = reply = NULL; + } + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return ret; +} + static int attach(DBusConnection *bus, char **args, unsigned n) { DBusMessage *m = NULL, *reply = NULL; int ret = 0; @@ -1537,7 +1668,7 @@ static int help(void) { "Commands:\n" " list-sessions List sessions\n" " session-status [ID...] Show session status\n" - " show-session [ID...] Show property of one or more sessions\n" + " show-session [ID...] Show properties of 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" @@ -1545,18 +1676,17 @@ static int help(void) { " kill-session [ID...] Send signal to processes of a session\n" " list-users List users\n" " user-status [USER...] Show user status\n" - " show-user [USER...] Show property of one or more users\n" + " show-user [USER...] Show properties 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" " list-seats List seats\n" " seat-status [NAME...] Show seat status\n" - " show-seat [NAME...] Show property of one or more seats\n" + " show-seat [NAME...] Show properties of one or more seats\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" - " kill-seat [NAME...] Send signal to processes of sessions on a seat\n", + " terminate-seat [NAME...] Terminate all sessions on one or more seats\n", program_invocation_short_name); return 0; @@ -1679,21 +1809,20 @@ static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "lock-session", MORE, 2, activate }, { "unlock-session", MORE, 2, activate }, { "terminate-session", MORE, 2, activate }, - { "kill-session", MORE, 2, kill_session }, /* missing */ + { "kill-session", MORE, 2, kill_session }, { "list-users", EQUAL, 1, list_users }, { "user-status", MORE, 2, show }, { "show-user", MORE, 1, show }, { "enable-linger", MORE, 2, enable_linger }, { "disable-linger", MORE, 2, enable_linger }, { "terminate-user", MORE, 2, terminate_user }, - { "kill-user", MORE, 2, kill_session }, /* missing */ + { "kill-user", MORE, 2, kill_user }, { "list-seats", EQUAL, 1, list_seats }, { "seat-status", MORE, 2, show }, { "show-seat", MORE, 1, show }, { "attach", MORE, 3, attach }, { "flush-devices", EQUAL, 1, flush_devices }, - { "terminate-seat", MORE, 2, terminate_seat }, /* missing */ - { "kill-seat", MORE, 2, kill_session }, /* missing */ + { "terminate-seat", MORE, 2, terminate_seat }, }; int left; diff --git a/src/logind-dbus.c b/src/logind-dbus.c index ec39d56d..16dbd366 100644 --- a/src/logind-dbus.c +++ b/src/logind-dbus.c @@ -81,6 +81,15 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -1009,6 +1018,73 @@ static DBusHandlerResult manager_message_handler( if (!reply) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillSession")) { + const char *swho; + int32_t signo; + KillWho who; + const char *name; + Session *session; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &swho, + DBUS_TYPE_INT32, &signo, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (isempty(swho)) + who = KILL_ALL; + else { + who = kill_who_from_string(swho); + if (who < 0) + return bus_send_error_reply(connection, message, &error, -EINVAL); + } + + if (signo <= 0 || signo >= _NSIG) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + session = hashmap_get(m->sessions, name); + if (!session) + return bus_send_error_reply(connection, message, &error, -ENOENT); + + r = session_kill(session, who, signo); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillUser")) { + uint32_t uid; + User *user; + int32_t signo; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_UINT32, &uid, + DBUS_TYPE_INT32, &signo, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (signo <= 0 || signo >= _NSIG) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid)); + if (!user) + return bus_send_error_reply(connection, message, &error, -ENOENT); + + r = user_kill(user, signo); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) { const char *name; Session *session; diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c index 5fe391cc..dc0ef5b3 100644 --- a/src/logind-session-dbus.c +++ b/src/logind-session-dbus.c @@ -36,6 +36,10 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -315,6 +319,38 @@ static DBusHandlerResult session_message_dispatch( if (!reply) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Kill")) { + const char *swho; + int32_t signo; + KillWho who; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &swho, + DBUS_TYPE_INT32, &signo, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (isempty(swho)) + who = KILL_ALL; + else { + who = kill_who_from_string(swho); + if (who < 0) + return bus_send_error_reply(connection, message, &error, -EINVAL); + } + + if (signo <= 0 || signo >= _NSIG) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + r = session_kill(s, who, signo); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); diff --git a/src/logind-session.c b/src/logind-session.c index ab4de66c..201cd394 100644 --- a/src/logind-session.c +++ b/src/logind-session.c @@ -588,7 +588,7 @@ static bool session_shall_kill(Session *s) { return strv_contains(s->manager->kill_only_users, s->user->name); } -static int session_kill_cgroup(Session *s) { +static int session_terminate_cgroup(Session *s) { int r; char **k; @@ -661,7 +661,7 @@ int session_stop(Session *s) { log_info("Removed session %s.", s->id); /* Kill cgroup */ - k = session_kill_cgroup(s); + k = session_terminate_cgroup(s); if (k < 0) r = k; @@ -886,6 +886,48 @@ void session_add_to_gc_queue(Session *s) { s->in_gc_queue = true; } +int session_kill(Session *s, KillWho who, int signo) { + int r = 0; + Set *pid_set = NULL; + + assert(s); + + if (!s->cgroup_path) + return -ESRCH; + + if (s->leader <= 0 && who == KILL_LEADER) + return -ESRCH; + + if (s->leader > 0) + if (kill(s->leader, signo) < 0) + r = -errno; + + if (who == KILL_ALL) { + int q; + + pid_set = set_new(trivial_hash_func, trivial_compare_func); + if (!pid_set) + return -ENOMEM; + + if (s->leader > 0) { + q = set_put(pid_set, LONG_TO_PTR(s->leader)); + if (q < 0) + r = q; + } + + q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, signo, false, true, false, pid_set); + if (q < 0) + if (q != -EAGAIN && q != -ESRCH && q != -ENOENT) + r = q; + } + +finish: + if (pid_set) + set_free(pid_set); + + return r; +} + static const char* const session_type_table[_SESSION_TYPE_MAX] = { [SESSION_TTY] = "tty", [SESSION_X11] = "x11", @@ -893,3 +935,10 @@ static const char* const session_type_table[_SESSION_TYPE_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType); + +static const char* const kill_who_table[_KILL_WHO_MAX] = { + [KILL_LEADER] = "leader", + [KILL_ALL] = "all" +}; + +DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); diff --git a/src/logind-session.h b/src/logind-session.h index d9f44ef0..8e394ac0 100644 --- a/src/logind-session.h +++ b/src/logind-session.h @@ -38,6 +38,13 @@ typedef enum SessionType { _SESSION_TYPE_INVALID = -1 } SessionType; +typedef enum KillWho { + KILL_LEADER, + KILL_ALL, + _KILL_WHO_MAX, + _KILL_WHO_INVALID = -1 +} KillWho; + struct Session { Manager *manager; @@ -98,6 +105,7 @@ int session_start(Session *s); int session_stop(Session *s); int session_save(Session *s); int session_load(Session *s); +int session_kill(Session *s, KillWho who, int signo); char *session_bus_path(Session *s); @@ -110,4 +118,7 @@ int session_send_lock(Session *s, bool lock); const char* session_type_to_string(SessionType t); SessionType session_type_from_string(const char *s); +const char *kill_who_to_string(KillWho k); +KillWho kill_who_from_string(const char *s); + #endif diff --git a/src/logind-user-dbus.c b/src/logind-user-dbus.c index 7263d1b7..3673a28b 100644 --- a/src/logind-user-dbus.c +++ b/src/logind-user-dbus.c @@ -29,6 +29,9 @@ #define BUS_USER_INTERFACE \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -250,6 +253,27 @@ static DBusHandlerResult user_message_dispatch( reply = dbus_message_new_method_return(message); if (!reply) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Kill")) { + int32_t signo; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_INT32, &signo, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (signo <= 0 || signo >= _NSIG) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + r = user_kill(u, signo); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); diff --git a/src/logind-user.c b/src/logind-user.c index dacf148f..1655a9a4 100644 --- a/src/logind-user.c +++ b/src/logind-user.c @@ -325,7 +325,7 @@ static int user_shall_kill(User *u) { return strv_contains(u->manager->kill_only_users, u->name); } -static int user_kill_cgroup(User *u) { +static int user_terminate_cgroup(User *u) { int r; char **k; @@ -401,7 +401,7 @@ int user_stop(User *u) { r = k; /* Kill cgroup */ - k = user_kill_cgroup(u); + k = user_terminate_cgroup(u); if (k < 0) r = k; @@ -515,6 +515,31 @@ UserState user_get_state(User *u) { return USER_ONLINE; } +int user_kill(User *u, int signo) { + int r = 0, q; + Set *pid_set = NULL; + + assert(u); + + if (!u->cgroup_path) + return -ESRCH; + + pid_set = set_new(trivial_hash_func, trivial_compare_func); + if (!pid_set) + return -ENOMEM; + + q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set); + if (q < 0) + if (q != -EAGAIN && q != -ESRCH && q != -ENOENT) + r = q; + +finish: + if (pid_set) + set_free(pid_set); + + return r; +} + static const char* const user_state_table[_USER_STATE_MAX] = { [USER_OFFLINE] = "offline", [USER_LINGERING] = "lingering", diff --git a/src/logind-user.h b/src/logind-user.h index 8a8d5ede..db9a5f6a 100644 --- a/src/logind-user.h +++ b/src/logind-user.h @@ -71,6 +71,7 @@ UserState user_get_state(User *u); int user_get_idle_hint(User *u, dual_timestamp *t); int user_save(User *u); int user_load(User *u); +int user_kill(User *u, int signo); char *user_bus_path(User *s); -- 2.39.5