summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2011-07-13 19:58:35 +0200
committerLennart Poettering <lennart@poettering.net>2011-07-13 19:59:28 +0200
commitde07ab16c6b919cead26c9a5209a362127ff6142 (patch)
tree9b4d4f674ae5fa607fb8516be541d16dcca9e9c8
parenta17204af0e950be7a5199db62ef400814e29aa3c (diff)
loginctl: implement missing kill verb
-rw-r--r--src/loginctl.c149
-rw-r--r--src/logind-dbus.c76
-rw-r--r--src/logind-session-dbus.c36
-rw-r--r--src/logind-session.c53
-rw-r--r--src/logind-session.h11
-rw-r--r--src/logind-user-dbus.c24
-rw-r--r--src/logind-user.c29
-rw-r--r--src/logind-user.h1
8 files changed, 365 insertions, 14 deletions
diff --git a/src/loginctl.c b/src/loginctl.c
index bb5be908f6..829213e3d0 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 ec39d56d9e..16dbd3664c 100644
--- a/src/logind-dbus.c
+++ b/src/logind-dbus.c
@@ -81,6 +81,15 @@
" <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;
diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c
index 5fe391cca4..dc0ef5b3d6 100644
--- a/src/logind-session-dbus.c
+++ b/src/logind-session-dbus.c
@@ -36,6 +36,10 @@
" <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);
diff --git a/src/logind-session.c b/src/logind-session.c
index ab4de66ca4..201cd394ec 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 d9f44ef0e0..8e394ac0d8 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 7263d1b757..3673a28bd4 100644
--- a/src/logind-user-dbus.c
+++ b/src/logind-user-dbus.c
@@ -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);
diff --git a/src/logind-user.c b/src/logind-user.c
index dacf148f78..1655a9a46b 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 8a8d5ede40..db9a5f6a34 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);