chiark / gitweb /
loginctl: implement missing kill verb
authorLennart Poettering <lennart@poettering.net>
Wed, 13 Jul 2011 17:58:35 +0000 (19:58 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 13 Jul 2011 17:59:28 +0000 (19:59 +0200)
src/loginctl.c
src/logind-dbus.c
src/logind-session-dbus.c
src/logind-session.c
src/logind-session.h
src/logind-user-dbus.c
src/logind-user.c
src/logind-user.h

index bb5be908f6cc008eaf2cf5e77e4c3cd40b1c33b8..829213e3d0c97f3d821ad60104ce31a888bd8499 100644 (file)
@@ -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;
index ec39d56d9e2ce13f7ace722998f7cc3509da4d11..16dbd3664ca2eb9b0cd65306d423f336dd4e4aef 100644 (file)
         "  <method name=\"UnlockSession\">\n"                           \
         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
         "  </method>\n"                                                 \
+        "  <method name=\"KillSession\">\n"                             \
+        "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
+        "   <arg name=\"who\" type=\"s\"/>\n"                           \
+        "   <arg name=\"signal\" type=\"s\"/>\n"                        \
+        "  </method>\n"                                                 \
+        "  <method name=\"KillUser\">\n"                                \
+        "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
+        "   <arg name=\"signal\" type=\"s\"/>\n"                        \
+        "  </method>\n"                                                 \
         "  <method name=\"TerminateSession\">\n"                        \
         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
         "  </method>\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;
index 5fe391cca493f8c2eee404c41e039a08bcdbd4c7..dc0ef5b3d6336c9370d7ed2f31dc09876231aebb 100644 (file)
         "  <method name=\"SetIdleHint\">\n"                             \
         "   <arg name=\"b\" type=\"b\"/>\n"                             \
         "  </method>\n"                                                 \
+        "  <method name=\"Kill\">\n"                                    \
+        "   <arg name=\"who\" type=\"s\"/>\n"                           \
+        "   <arg name=\"signal\" type=\"s\"/>\n"                        \
+        "  </method>\n"                                                 \
         "  <property name=\"Id\" type=\"s\" access=\"read\"/>\n"        \
         "  <property name=\"User\" type=\"(uo)\" access=\"read\"/>\n"   \
         "  <property name=\"Name\" type=\"s\" access=\"read\"/>\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);
 
index ab4de66ca4d8c4bc90eb521a72029476296a0a82..201cd394ec34e258eadeeab3be8e85888b24c11c 100644 (file)
@@ -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);
index d9f44ef0e04d8cd93973a26370bcc3374901d497..8e394ac0d8f698eaa32a22b3d6d931fb06e1f223 100644 (file)
@@ -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
index 7263d1b757265ecc2504224a94524625b0a71a3f..3673a28bd4ceb6c7eeb64427b731536d8f32422b 100644 (file)
@@ -29,6 +29,9 @@
 #define BUS_USER_INTERFACE \
         " <interface name=\"org.freedesktop.login1.User\">\n"           \
         "  <method name=\"Terminate\"/>\n"                              \
+        "  <method name=\"Kill\">\n"                                    \
+        "   <arg name=\"signal\" type=\"s\"/>\n"                        \
+        "  </method>\n"                                                 \
         "  <property name=\"UID\" type=\"u\" access=\"read\"/>\n"       \
         "  <property name=\"GID\" type=\"u\" access=\"read\"/>\n"       \
         "  <property name=\"Name\" type=\"s\" access=\"read\"/>\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);
 
index dacf148f7829e603c2224447408054e8cb052a58..1655a9a46b76b7b9d4a372dd8a7fd3f58433c854 100644 (file)
@@ -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",
index 8a8d5ede4098ebd9f2aa14bf3999faff3b016061..db9a5f6a3411153307a2e428c19fb8089e2ad90c 100644 (file)
@@ -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);