}
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) {
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;
"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"
" 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;
{ "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;
" <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" \
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;
" <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" \
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);
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;
log_info("Removed session %s.", s->id);
/* Kill cgroup */
- k = session_kill_cgroup(s);
+ k = session_terminate_cgroup(s);
if (k < 0)
r = k;
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",
};
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);
_SESSION_TYPE_INVALID = -1
} SessionType;
+typedef enum KillWho {
+ KILL_LEADER,
+ KILL_ALL,
+ _KILL_WHO_MAX,
+ _KILL_WHO_INVALID = -1
+} KillWho;
+
struct Session {
Manager *manager;
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);
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
#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" \
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);
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;
r = k;
/* Kill cgroup */
- k = user_kill_cgroup(u);
+ k = user_terminate_cgroup(u);
if (k < 0)
r = k;
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",
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);