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