From c138da233b618ab5d42e4810f1ad7f2e687f2a59 Mon Sep 17 00:00:00 2001 From: mnussbaum Date: Fri, 1 Feb 2019 18:56:31 -0800 Subject: [PATCH] Allow compositors to run as systemd user units When a wlroots compositor runs as a systemd user unit there is no session associated with the compositor process. Instead we need to attach to an active and graphical user session. This change first looks for an available session for the process, and if there isn't one falls back to display in the oldest available graphical session. This work was modeled after a similar change to mutter - https://gitlab.gnome.org/GNOME/mutter/merge_requests/150. --- backend/session/logind.c | 121 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 3 deletions(-) diff --git a/backend/session/logind.c b/backend/session/logind.c index 5bde16c2..0bacfbcd 100644 --- a/backend/session/logind.c +++ b/backend/session/logind.c @@ -487,6 +487,123 @@ static int dbus_event(int fd, uint32_t mask, void *data) { return 1; } +static bool contains_str(const char *needle, const char **haystack) { + for (int i = 0; haystack[i]; i++) { + if (strcmp(haystack[i], needle) == 0) { + return true; + } + } + + return false; +} + +static bool get_greeter_session(char **session_id) { + char *class = NULL; + char **user_sessions = NULL; + int user_session_count = sd_uid_get_sessions(getuid(), 1, &user_sessions); + + if (user_session_count < 0) { + wlr_log(WLR_ERROR, "Failed to get sessions"); + goto out; + } + + if (user_session_count == 0) { + wlr_log(WLR_ERROR, "User has no sessions"); + goto out; + } + + for (int i = 0; i < user_session_count; ++i) { + int ret = sd_session_get_class(user_sessions[i], &class); + if (ret < 0) { + continue; + } + + if (strcmp(class, "greeter") == 0) { + *session_id = strdup(user_sessions[i]); + goto out; + } + } + +out: + free(class); + for (int i = 0; i < user_session_count; ++i) { + free(user_sessions[i]); + } + free(user_sessions); + + return *session_id != NULL; +} + +static bool get_display_session(char **session_id) { + assert(session_id != NULL); + int ret; + + // If there's a session active for the current process then just use that + ret = sd_pid_get_session(getpid(), session_id); + if (ret == 0) { + return true; + } + + char *type = NULL; + char *state = NULL; + + // Find any active sessions for the user if the process isn't part of an + // active session itself + ret = sd_uid_get_display(getuid(), session_id); + if (ret < 0 && ret != -ENODATA) { + wlr_log(WLR_ERROR, "Failed to get display: %s", strerror(-ret)); + goto error; + } + + if (ret != 0 && !get_greeter_session(session_id)) { + wlr_log(WLR_ERROR, "Couldn't find an active session or a greeter session"); + goto error; + } + + assert(*session_id != NULL); + + // Check that the available session is graphical + ret = sd_session_get_type(*session_id, &type); + if (ret < 0) { + wlr_log(WLR_ERROR, "Couldn't get a type for session '%s': %s", + *session_id, strerror(-ret)); + goto error; + } + + const char *graphical_session_types[] = {"wayland", "x11", "mir", NULL}; + if (!contains_str(type, graphical_session_types)) { + wlr_log(WLR_ERROR, "Session '%s' isn't a graphical session (type: '%s')", + *session_id, type); + goto error; + } + + // Check that the session is active + ret = sd_session_get_state(*session_id, &state); + if (ret < 0) { + wlr_log(WLR_ERROR, "Couldn't get state for session '%s': %s", + *session_id, strerror(-ret)); + goto error; + } + + const char *active_states[] = {"active", "online", NULL}; + if (!contains_str(state, active_states)) { + wlr_log(WLR_ERROR, "Session '%s' is not active", *session_id); + goto error; + } + + free(type); + free(state); + return true; + +error: + free(type); + free(state); + free(*session_id); + *session_id = NULL; + + return false; +} + static struct wlr_session *logind_session_create(struct wl_display *disp) { int ret; struct logind_session *session = calloc(1, sizeof(*session)); @@ -495,9 +612,7 @@ static struct wlr_session *logind_session_create(struct wl_display *disp) { return NULL; } - ret = sd_pid_get_session(getpid(), &session->id); - if (ret < 0) { - wlr_log(WLR_ERROR, "Failed to get session id: %s", strerror(-ret)); + if (!get_display_session(&session->id)) { goto error; }