diff options
author | mnussbaum <michaelnussbaum08@gmail.com> | 2019-02-01 18:56:31 -0800 |
---|---|---|
committer | mnnussbaum <michaelnussbaum08@gmail.com> | 2019-02-03 18:45:32 -0800 |
commit | c138da233b618ab5d42e4810f1ad7f2e687f2a59 (patch) | |
tree | 1441337adac6927b62bb9262be71b103f7e0dc92 /backend | |
parent | 28f11aec3106c5d0528d1835055c82c4d571c286 (diff) |
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.
Diffstat (limited to 'backend')
-rw-r--r-- | backend/session/logind.c | 121 |
1 files 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; } |