diff options
-rw-r--r-- | src/login/logind-dbus.c | 8 | ||||
-rw-r--r-- | src/login/logind-session-dbus.c | 42 | ||||
-rw-r--r-- | src/login/logind-session.c | 48 | ||||
-rw-r--r-- | src/login/logind-session.h | 6 | ||||
-rw-r--r-- | src/login/logind.c | 10 |
5 files changed, 114 insertions, 0 deletions
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 5decf8114c..113a2b73b6 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -2473,8 +2473,16 @@ DBusHandlerResult bus_message_filter( goto finish; } + /* drop all controllers owned by this name */ if (*old && !*new && (key = hashmap_remove(m->busnames, old))) { + Session *session; + Iterator i; + free(key); + + HASHMAP_FOREACH(session, m->sessions, i) + if (session_is_controller(session, old)) + session_drop_controller(session); } } diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index 2cc4d8587b..35bf4480cb 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -40,6 +40,10 @@ " <arg name=\"who\" type=\"s\"/>\n" \ " <arg name=\"signal\" type=\"s\"/>\n" \ " </method>\n" \ + " <method name=\"TakeControl\"/>\n" \ + " <arg name=\"force\" type=\"b\"/>\n" \ + " </method>\n" \ + " <method name=\"ReleaseControl\"/>\n" \ " <signal name=\"Lock\"/>\n" \ " <signal name=\"Unlock\"/>\n" \ " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \ @@ -366,6 +370,44 @@ static DBusHandlerResult session_message_dispatch( if (!reply) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "TakeControl")) { + dbus_bool_t force; + unsigned long ul; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_BOOLEAN, &force, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error); + if (ul == (unsigned long) -1) + return bus_send_error_reply(connection, message, &error, -EIO); + + if (ul != 0 && (force || ul != s->user->uid)) + return bus_send_error_reply(connection, message, NULL, -EPERM); + + r = session_set_controller(s, bus_message_get_sender_with_fallback(message), force); + 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.Session", "ReleaseControl")) { + const char *sender = bus_message_get_sender_with_fallback(message); + + if (!session_is_controller(s, sender)) + return bus_send_error_reply(connection, message, NULL, -EPERM); + + session_drop_controller(s); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else { const BusBoundProperties bps[] = { { "org.freedesktop.login1.Session", bus_login_session_properties, s }, diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 2d22a68b6e..fe5fa27be1 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -73,6 +73,8 @@ void session_free(Session *s) { if (s->in_gc_queue) LIST_REMOVE(Session, gc_queue, s->manager->session_gc_queue, s); + session_drop_controller(s); + if (s->user) { LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s); @@ -918,6 +920,52 @@ int session_kill(Session *s, KillWho who, int signo) { return manager_kill_unit(s->manager, s->scope, who, signo, NULL); } +bool session_is_controller(Session *s, const char *sender) +{ + assert(s); + + return streq_ptr(s->controller, sender); +} + +int session_set_controller(Session *s, const char *sender, bool force) { + char *t; + int r; + + assert(s); + assert(sender); + + if (session_is_controller(s, sender)) + return 0; + if (s->controller && !force) + return -EBUSY; + + t = strdup(sender); + if (!t) + return -ENOMEM; + + r = manager_watch_busname(s->manager, sender); + if (r) { + free(t); + return r; + } + + session_drop_controller(s); + + s->controller = t; + return 0; +} + +void session_drop_controller(Session *s) { + assert(s); + + if (!s->controller) + return; + + manager_drop_busname(s->manager, s->controller); + free(s->controller); + s->controller = NULL; +} + static const char* const session_state_table[_SESSION_STATE_MAX] = { [SESSION_OPENING] = "opening", [SESSION_ONLINE] = "online", diff --git a/src/login/logind-session.h b/src/login/logind-session.h index 9cf64850be..839fb230dc 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -106,6 +106,8 @@ struct Session { DBusMessage *create_message; + char *controller; + LIST_FIELDS(Session, sessions_by_user); LIST_FIELDS(Session, sessions_by_seat); @@ -154,3 +156,7 @@ SessionClass session_class_from_string(const char *s) _pure_; const char *kill_who_to_string(KillWho k) _const_; KillWho kill_who_from_string(const char *s) _pure_; + +bool session_is_controller(Session *s, const char *sender); +int session_set_controller(Session *s, const char *sender, bool force); +void session_drop_controller(Session *s); diff --git a/src/login/logind.c b/src/login/logind.c index 89e4eeea16..c99c2844e8 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -391,11 +391,21 @@ int manager_watch_busname(Manager *m, const char *name) { } void manager_drop_busname(Manager *m, const char *name) { + Session *session; + Iterator i; char *key; assert(m); assert(name); + if (!hashmap_get(m->busnames, name)) + return; + + /* keep it if the name still owns a controller */ + HASHMAP_FOREACH(session, m->sessions, i) + if (session_is_controller(session, name)) + return; + key = hashmap_remove(m->busnames, name); if (key) free(key); |