loginctl: implement missing kill verb

This commit is contained in:
Lennart Poettering 2011-07-13 19:58:35 +02:00
parent a17204af0e
commit de07ab16c6
8 changed files with 365 additions and 14 deletions

View 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;

View File

@ -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;

View File

@ -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);

View 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);

View 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

View 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);

View 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",

View 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);