chiark / gitweb /
logind: check whether newly created session is active
[elogind.git] / src / logind-dbus.c
index f7ec1485ac3fddbed84f5c40a423c59c8fbfb2f9..050eb7160edde03b4a7b075208b2c53c5357ab57 100644 (file)
 
 #include <errno.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "logind.h"
 #include "dbus-common.h"
+#include "strv.h"
 
 #define BUS_MANAGER_INTERFACE                                           \
         " <interface name=\"org.freedesktop.login1.Manager\">\n"        \
@@ -40,7 +42,7 @@
         "   <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n"        \
         "  </method>\n"                                                 \
         "  <method name=\"ListSessions\">\n"                            \
-        "   <arg name=\"users\" type=\"a(sussso)\" direction=\"out\"/>\n" \
+        "   <arg name=\"sessions\" type=\"a(susso)\" direction=\"out\"/>\n" \
         "  </method>\n"                                                 \
         "  <method name=\"ListUsers\">\n"                               \
         "   <arg name=\"users\" type=\"a(uso)\" direction=\"out\"/>\n"  \
         "  <method name=\"CreateSession\">\n"                           \
         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
         "   <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n"       \
+        "   <arg name=\"sevice\" type=\"s\" direction=\"in\"/>\n"       \
         "   <arg name=\"type\" type=\"s\" direction=\"in\"/>\n"         \
         "   <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
+        "   <arg name=\"vtnr\" type=\"u\" direction=\"in\"/>\n"         \
         "   <arg name=\"tty\" type=\"s\" direction=\"in\"/>\n"          \
         "   <arg name=\"display\" type=\"s\" direction=\"in\"/>\n"      \
         "   <arg name=\"remote\" type=\"b\" direction=\"in\"/>\n"       \
         "   <arg name=\"remote_host\" type=\"s\" direction=\"in\"/>\n"  \
         "   <arg name=\"controllers\" type=\"as\" direction=\"in\"/>\n" \
         "   <arg name=\"reset_controllers\" type=\"as\" direction=\"in\"/>\n" \
-        "   <arg name=\"kill_processes\" type=\"as\" direction=\"in\"/>\n" \
+        "   <arg name=\"kill_processes\" type=\"b\" direction=\"in\"/>\n" \
         "   <arg name=\"id\" type=\"s\" direction=\"out\"/>\n"          \
         "   <arg name=\"path\" type=\"o\" direction=\"out\"/>\n"        \
+        "   <arg name=\"runtime_path\" type=\"o\" direction=\"out\"/>\n" \
         "   <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n"          \
         "  </method>\n"                                                 \
         "  <method name=\"ActivateSession\">\n"                         \
 
 static int bus_manager_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
         Manager *m = data;
-        bool b;
+        dbus_bool_t b;
 
         assert(i);
         assert(property);
         assert(m);
 
-        b = manager_get_idle_hint(m, NULL);
+        b = manager_get_idle_hint(m, NULL) > 0;
         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
                 return -ENOMEM;
 
@@ -161,6 +166,364 @@ static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *pr
         return 0;
 }
 
+static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMessage **_reply) {
+        Session *session = NULL;
+        User *user = NULL;
+        const char *type, *seat, *tty, *display, *remote_user, *remote_host, *service;
+        uint32_t uid, leader, audit_id = 0;
+        dbus_bool_t remote, kill_processes;
+        char **controllers = NULL, **reset_controllers = NULL;
+        SessionType t;
+        Seat *s;
+        DBusMessageIter iter;
+        int r;
+        char *id = NULL, *p;
+        uint32_t vtnr = 0;
+        int pipe_fds[2] = { -1, -1 };
+        DBusMessage *reply = NULL;
+        bool b;
+
+        assert(m);
+        assert(message);
+        assert(_reply);
+
+        if (!dbus_message_iter_init(message, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &uid);
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &leader);
+
+        if (leader <= 0 ||
+            !dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &service);
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &type);
+        t = session_type_from_string(type);
+
+        if (t < 0 ||
+            !dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &seat);
+
+        if (isempty(seat))
+                s = NULL;
+        else {
+                s = hashmap_get(m->seats, seat);
+                if (!s)
+                        return -ENOENT;
+        }
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &vtnr);
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &tty);
+
+        if (tty_is_vc(tty)) {
+                int v;
+
+                if (!s)
+                        s = m->vtconsole;
+                else if (s != m->vtconsole)
+                        return -EINVAL;
+
+                v = vtnr_from_tty(tty);
+
+                if (v <= 0)
+                        return v < 0 ? v : -EINVAL;
+
+                if (vtnr <= 0)
+                        vtnr = (uint32_t) v;
+                else if (vtnr != (uint32_t) v)
+                        return -EINVAL;
+
+        } else if (!isempty(tty) && seat_is_vtconsole(s))
+                return -EINVAL;
+
+        if (s) {
+                if (seat_is_vtconsole(s)) {
+                        if (vtnr <= 0 || vtnr > 63)
+                                return -EINVAL;
+                } else {
+                        if (vtnr > 0)
+                                return -EINVAL;
+                }
+        }
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &display);
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &remote);
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &remote_user);
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &remote_host);
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        r = bus_parse_strv_iter(&iter, &controllers);
+        if (r < 0)
+                return -EINVAL;
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
+                r = -EINVAL;
+                goto fail;
+        }
+
+        r = bus_parse_strv_iter(&iter, &reset_controllers);
+        if (r < 0)
+                goto fail;
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
+                r = -EINVAL;
+                goto fail;
+        }
+
+        dbus_message_iter_get_basic(&iter, &kill_processes);
+
+        r = manager_add_user_by_uid(m, uid, &user);
+        if (r < 0)
+                goto fail;
+
+        audit_session_from_pid(leader, &audit_id);
+
+        if (audit_id > 0) {
+                asprintf(&id, "%lu", (unsigned long) audit_id);
+
+                if (!id) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+
+                session = hashmap_get(m->sessions, id);
+
+                if (session) {
+
+                        /* Session already exists, client is probably
+                         * something like "su" which changes uid but
+                         * is still the same audit session */
+
+                        reply = dbus_message_new_method_return(message);
+                        if (!reply) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        /* Create a throw-away fd */
+                        if (pipe(pipe_fds) < 0) {
+                                r = -errno;
+                                goto fail;
+                        }
+
+                        close_nointr_nofail(pipe_fds[0]);
+                        pipe_fds[0] = -1;
+
+                        p = session_bus_path(session);
+                        if (!p) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        b = dbus_message_append_args(
+                                        reply,
+                                        DBUS_TYPE_STRING, &session->id,
+                                        DBUS_TYPE_OBJECT_PATH, &p,
+                                        DBUS_TYPE_STRING, &session->user->runtime_path,
+                                        DBUS_TYPE_UNIX_FD, &pipe_fds[1],
+                                        DBUS_TYPE_INVALID);
+                        free(p);
+
+                        if (!b) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        close_nointr_nofail(pipe_fds[1]);
+                        *_reply = reply;
+
+                        return 0;
+                }
+
+        } else {
+                do {
+                        free(id);
+                        asprintf(&id, "c%lu", ++m->session_counter);
+
+                        if (!id) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                } while (hashmap_get(m->sessions, id));
+        }
+
+        r = manager_add_session(m, user, id, &session);
+        free(id);
+        if (r < 0)
+                goto fail;
+
+        session->leader = leader;
+        session->audit_id = audit_id;
+        session->type = t;
+        session->remote = remote;
+        session->controllers = controllers;
+        session->reset_controllers = reset_controllers;
+        session->kill_processes = kill_processes;
+        session->vtnr = vtnr;
+
+        controllers = reset_controllers = NULL;
+
+        if (!isempty(tty)) {
+                session->tty = strdup(tty);
+                if (!session->tty) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
+
+        if (!isempty(display)) {
+                session->display = strdup(display);
+                if (!session->display) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
+
+        if (!isempty(remote_user)) {
+                session->remote_user = strdup(remote_user);
+                if (!session->remote_user) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
+
+        if (!isempty(remote_host)) {
+                session->remote_host = strdup(remote_host);
+                if (!session->remote_host) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
+
+        if (!isempty(service)) {
+                session->service = strdup(service);
+                if (!session->service) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
+
+        if (pipe(pipe_fds) < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = session_set_pipe_fd(session, pipe_fds[0]);
+        if (r < 0)
+                goto fail;
+        pipe_fds[0] = -1;
+
+        if (s) {
+                r = seat_attach_session(s, session);
+                if (r < 0)
+                        goto fail;
+        }
+
+        r = session_start(session);
+        if (r < 0)
+                goto fail;
+
+        reply = dbus_message_new_method_return(message);
+        if (!reply) {
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        p = session_bus_path(session);
+        if (!p) {
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        b = dbus_message_append_args(
+                        reply,
+                        DBUS_TYPE_STRING, &session->id,
+                        DBUS_TYPE_OBJECT_PATH, &p,
+                        DBUS_TYPE_STRING, &session->user->runtime_path,
+                        DBUS_TYPE_UNIX_FD, &pipe_fds[1],
+                        DBUS_TYPE_INVALID);
+        free(p);
+
+        if (!b) {
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        close_nointr_nofail(pipe_fds[1]);
+        *_reply = reply;
+
+        return 0;
+
+fail:
+        strv_free(controllers);
+        strv_free(reset_controllers);
+
+        if (session)
+                session_add_to_gc_queue(session);
+
+        if (user)
+                user_add_to_gc_queue(user);
+
+        close_pipe(pipe_fds);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        return r;
+}
+
 static DBusHandlerResult manager_message_handler(
                 DBusConnection *connection,
                 DBusMessage *message,
@@ -171,7 +534,6 @@ static DBusHandlerResult manager_message_handler(
         const BusProperty properties[] = {
                 { "org.freedesktop.login1.Manager", "ControlGroupHierarchy",  bus_property_append_string,   "s",  m->cgroup_path          },
                 { "org.freedesktop.login1.Manager", "Controllers",            bus_property_append_strv,     "as", m->controllers          },
-                { "org.freedesktop.login1.Manager", "ResetControllers",       bus_property_append_strv,     "as", m->reset_controllers    },
                 { "org.freedesktop.login1.Manager", "NAutoVTs",               bus_property_append_unsigned, "u",  &m->n_autovts           },
                 { "org.freedesktop.login1.Manager", "KillOnlyUsers",          bus_property_append_strv,     "as", m->kill_only_users      },
                 { "org.freedesktop.login1.Manager", "KillExcludeUsers",       bus_property_append_strv,     "as", m->kill_exclude_users   },
@@ -294,6 +656,146 @@ static DBusHandlerResult manager_message_handler(
                 if (!b)
                         goto oom;
 
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSessions")) {
+                char *p;
+                Session *session;
+                Iterator i;
+                DBusMessageIter iter, sub;
+                const char *empty = "";
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+
+                dbus_message_iter_init_append(reply, &iter);
+
+                if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(susso)", &sub))
+                        goto oom;
+
+                HASHMAP_FOREACH(session, m->sessions, i) {
+                        DBusMessageIter sub2;
+                        uint32_t uid;
+
+                        if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
+                                goto oom;
+
+                        uid = session->user->uid;
+
+                        p = session_bus_path(session);
+                        if (!p)
+                                goto oom;
+
+                        if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->user->name) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, session->seat ? (const char**) &session->seat->id : &empty) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
+                                free(p);
+                                goto oom;
+                        }
+
+                        free(p);
+
+                        if (!dbus_message_iter_close_container(&sub, &sub2))
+                                goto oom;
+                }
+
+                if (!dbus_message_iter_close_container(&iter, &sub))
+                        goto oom;
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
+                char *p;
+                User *user;
+                Iterator i;
+                DBusMessageIter iter, sub;
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+
+                dbus_message_iter_init_append(reply, &iter);
+
+                if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(uso)", &sub))
+                        goto oom;
+
+                HASHMAP_FOREACH(user, m->users, i) {
+                        DBusMessageIter sub2;
+                        uint32_t uid;
+
+                        if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
+                                goto oom;
+
+                        uid = user->uid;
+
+                        p = user_bus_path(user);
+                        if (!p)
+                                goto oom;
+
+                        if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &user->name) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
+                                free(p);
+                                goto oom;
+                        }
+
+                        free(p);
+
+                        if (!dbus_message_iter_close_container(&sub, &sub2))
+                                goto oom;
+                }
+
+                if (!dbus_message_iter_close_container(&iter, &sub))
+                        goto oom;
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
+                char *p;
+                Seat *seat;
+                Iterator i;
+                DBusMessageIter iter, sub;
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+
+                dbus_message_iter_init_append(reply, &iter);
+
+                if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(so)", &sub))
+                        goto oom;
+
+                HASHMAP_FOREACH(seat, m->seats, i) {
+                        DBusMessageIter sub2;
+
+                        if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
+                                goto oom;
+
+                        p = seat_bus_path(seat);
+                        if (!p)
+                                goto oom;
+
+                        if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &seat->id) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
+                                free(p);
+                                goto oom;
+                        }
+
+                        free(p);
+
+                        if (!dbus_message_iter_close_container(&sub, &sub2))
+                                goto oom;
+                }
+
+                if (!dbus_message_iter_close_container(&iter, &sub))
+                        goto oom;
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
+
+                r = bus_manager_create_session(m, message, &reply);
+                if (r == -ENOMEM)
+                        goto oom;
+
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
+
         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
                 const char *name;
                 Session *session;
@@ -473,3 +975,55 @@ oom:
 const DBusObjectPathVTable bus_manager_vtable = {
         .message_function = manager_message_handler
 };
+
+DBusHandlerResult bus_message_filter(
+                DBusConnection *connection,
+                DBusMessage *message,
+                void *userdata) {
+
+        Manager *m = userdata;
+        DBusError error;
+
+        assert(m);
+        assert(connection);
+        assert(message);
+
+        dbus_error_init(&error);
+
+        if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
+                const char *cgroup;
+
+                if (!dbus_message_get_args(message, &error,
+                                           DBUS_TYPE_STRING, &cgroup,
+                                           DBUS_TYPE_INVALID))
+                        log_error("Failed to parse Released message: %s", bus_error_message(&error));
+                else
+                        manager_cgroup_notify_empty(m, cgroup);
+        }
+
+        dbus_error_free(&error);
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+int manager_send_changed(Manager *manager, const char *properties) {
+        DBusMessage *m;
+        int r = -ENOMEM;
+
+        assert(manager);
+
+        m = bus_properties_changed_new("/org/freedesktop/login1", "org.freedesktop.login1.Manager", properties);
+        if (!m)
+                goto finish;
+
+        if (!dbus_connection_send(manager->bus, m, NULL))
+                goto finish;
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        return r;
+}