New session interface.
This commit is contained in:
		
							parent
							
								
									e446a5300b
								
							
						
					
					
						commit
						762ac7f4c0
					
				|  | @ -51,8 +51,7 @@ find_package(DRM REQUIRED) | ||||||
| find_package(GBM REQUIRED) | find_package(GBM REQUIRED) | ||||||
| find_package(LibInput REQUIRED) | find_package(LibInput REQUIRED) | ||||||
| find_package(Udev REQUIRED) | find_package(Udev REQUIRED) | ||||||
| find_package(Dbus) | find_package(Systemd) | ||||||
| find_package(Systemd REQUIRED) |  | ||||||
| 
 | 
 | ||||||
| include(Wayland) | include(Wayland) | ||||||
| include(Manpage) | include(Manpage) | ||||||
|  | @ -62,5 +61,6 @@ include_directories(include) | ||||||
| add_subdirectory(backend) | add_subdirectory(backend) | ||||||
| add_subdirectory(common) | add_subdirectory(common) | ||||||
| add_subdirectory(wayland) | add_subdirectory(wayland) | ||||||
|  | add_subdirectory(session) | ||||||
| 
 | 
 | ||||||
| add_subdirectory(example) | add_subdirectory(example) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,20 @@ | ||||||
|  | #ifndef WLR_SESSION_INTERFACE_H | ||||||
|  | #define WLR_SESSION_INTERFACE_H | ||||||
|  | 
 | ||||||
|  | struct wlr_session; | ||||||
|  | 
 | ||||||
|  | struct session_interface { | ||||||
|  | 	struct wlr_session *(*start)(void); | ||||||
|  | 	void (*finish)(struct wlr_session *session); | ||||||
|  | 	int (*open)(struct wlr_session *restrict session, | ||||||
|  | 		const char *restrict path); | ||||||
|  | 	void (*close)(struct wlr_session *session, int fd); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct wlr_session { | ||||||
|  | 	struct session_interface iface; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | extern const struct session_interface session_logind_iface; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -0,0 +1,12 @@ | ||||||
|  | #ifndef WLR_SESSION_H | ||||||
|  | #define WLR_SESSION_H | ||||||
|  | 
 | ||||||
|  | struct wlr_session; | ||||||
|  | 
 | ||||||
|  | struct wlr_session *wlr_session_start(void); | ||||||
|  | void wlr_session_finish(struct wlr_session *session); | ||||||
|  | int wlr_session_open_file(struct wlr_session *restrict session, | ||||||
|  | 	const char *restrict path); | ||||||
|  | void wlr_session_close_file(struct wlr_session *session, int fd); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -0,0 +1,24 @@ | ||||||
|  | include_directories( | ||||||
|  | 	${WAYLAND_INCLUDE_DIR} | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | set(sources | ||||||
|  |     session.c | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | set(libs | ||||||
|  |     wlr-common | ||||||
|  |     ${WAYLAND_LIBRARIES} | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | if (SYSTEMD_FOUND) | ||||||
|  |     add_definitions(${SYSTEMD_DEFINITIONS}) | ||||||
|  |     include_directories(${SYSTEMD_INCLUDE_DIRS}) | ||||||
|  | 
 | ||||||
|  |     add_definitions(-DHAS_SYSTEMD) | ||||||
|  |     list(APPEND sources logind.c) | ||||||
|  |     list(APPEND libs ${SYSTEMD_LIBRARIES}) | ||||||
|  | endif () | ||||||
|  | 
 | ||||||
|  | add_library(wlr-session ${sources}) | ||||||
|  | target_link_libraries(wlr-session ${libs}) | ||||||
|  | @ -0,0 +1,220 @@ | ||||||
|  | #define _POSIX_C_SOURCE 200809L | ||||||
|  | 
 | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <systemd/sd-bus.h> | ||||||
|  | #include <systemd/sd-login.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <sys/sysmacros.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <wayland-server.h> | ||||||
|  | 
 | ||||||
|  | #include "session/interface.h" | ||||||
|  | #include "common/log.h" | ||||||
|  | 
 | ||||||
|  | struct logind_session { | ||||||
|  | 	struct wlr_session base; | ||||||
|  | 
 | ||||||
|  | 	sd_bus *bus; | ||||||
|  | 
 | ||||||
|  | 	char *id; | ||||||
|  | 	char *path; | ||||||
|  | 	char *seat; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int logind_take_device(struct wlr_session *restrict base, | ||||||
|  | 	const char *restrict path) { | ||||||
|  | 
 | ||||||
|  | 	struct logind_session *session = wl_container_of(base, session, base); | ||||||
|  | 
 | ||||||
|  | 	int ret; | ||||||
|  | 	int fd = -1; | ||||||
|  | 	sd_bus_message *msg = NULL; | ||||||
|  | 	sd_bus_error error = SD_BUS_ERROR_NULL; | ||||||
|  | 
 | ||||||
|  | 	struct stat st; | ||||||
|  | 	if (stat(path, &st) < 0) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to stat '%s'", path); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", | ||||||
|  | 		session->path, "org.freedesktop.login1.Session", "TakeDevice", | ||||||
|  | 		&error, &msg, "uu", major(st.st_rdev), minor(st.st_rdev)); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to take device '%s': %s", path, error.message); | ||||||
|  | 		goto error; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	int paused = 0; | ||||||
|  | 	ret = sd_bus_message_read(msg, "hb", &fd, &paused); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to parse DBus response for '%s': %s", | ||||||
|  | 			path, strerror(-ret)); | ||||||
|  | 		goto error; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// The original fd seem to be closed when the message is freed
 | ||||||
|  | 	// so we just clone it.
 | ||||||
|  | 	fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); | ||||||
|  | 	if (fd == -1) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to clone file descriptor for '%s': %s", | ||||||
|  | 			path, strerror(errno)); | ||||||
|  | 		goto error; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | error: | ||||||
|  | 	sd_bus_error_free(&error); | ||||||
|  | 	sd_bus_message_unref(msg); | ||||||
|  | 	return fd; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void logind_release_device(struct wlr_session *base, int fd) { | ||||||
|  | 	struct logind_session *session = wl_container_of(base, session, base); | ||||||
|  | 
 | ||||||
|  | 	int ret; | ||||||
|  | 	sd_bus_message *msg = NULL; | ||||||
|  | 	sd_bus_error error = SD_BUS_ERROR_NULL; | ||||||
|  | 
 | ||||||
|  | 	struct stat st; | ||||||
|  | 	if (fstat(fd, &st) < 0) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to stat device '%d'", fd); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", | ||||||
|  | 		session->path, "org.freedesktop.login1.Session", "ReleaseDevice", | ||||||
|  | 		&error, &msg, "uu", major(st.st_rdev), minor(st.st_rdev)); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to release device '%d'", fd); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sd_bus_error_free(&error); | ||||||
|  | 	sd_bus_message_unref(msg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool session_activate(struct logind_session *session) { | ||||||
|  | 	int ret; | ||||||
|  | 	sd_bus_message *msg = NULL; | ||||||
|  | 	sd_bus_error error = SD_BUS_ERROR_NULL; | ||||||
|  | 
 | ||||||
|  | 	ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", | ||||||
|  | 		session->path, "org.freedesktop.login1.Session", "Activate", | ||||||
|  | 		&error, &msg, ""); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to activate session"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sd_bus_error_free(&error); | ||||||
|  | 	sd_bus_message_unref(msg); | ||||||
|  | 	return ret >= 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool take_control(struct logind_session *session) { | ||||||
|  | 	int ret; | ||||||
|  | 	sd_bus_message *msg = NULL; | ||||||
|  | 	sd_bus_error error = SD_BUS_ERROR_NULL; | ||||||
|  | 
 | ||||||
|  | 	ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", | ||||||
|  | 		session->path, "org.freedesktop.login1.Session", "TakeControl", | ||||||
|  | 		&error, &msg, "b", false); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to take control of session"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sd_bus_error_free(&error); | ||||||
|  | 	sd_bus_message_unref(msg); | ||||||
|  | 	return ret >= 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void release_control(struct logind_session *session) { | ||||||
|  | 	int ret; | ||||||
|  | 	sd_bus_message *msg = NULL; | ||||||
|  | 	sd_bus_error error = SD_BUS_ERROR_NULL; | ||||||
|  | 
 | ||||||
|  | 	ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", | ||||||
|  | 		session->path, "org.freedesktop.login1.Session", "ReleaseControl", | ||||||
|  | 		&error, &msg, ""); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to release control of session"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sd_bus_error_free(&error); | ||||||
|  | 	sd_bus_message_unref(msg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void logind_session_finish(struct wlr_session *base) { | ||||||
|  | 	struct logind_session *session = wl_container_of(base, session, base); | ||||||
|  | 
 | ||||||
|  | 	release_control(session); | ||||||
|  | 
 | ||||||
|  | 	sd_bus_unref(session->bus); | ||||||
|  | 	free(session->id); | ||||||
|  | 	free(session->path); | ||||||
|  | 	free(session->seat); | ||||||
|  | 	free(session); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct wlr_session *logind_session_start(void) { | ||||||
|  | 	int ret; | ||||||
|  | 	struct logind_session *session = calloc(1, sizeof(*session)); | ||||||
|  | 	if (!session) { | ||||||
|  | 		wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno)); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ret = sd_pid_get_session(getpid(), &session->id); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to get session id: %s", strerror(-ret)); | ||||||
|  | 		goto error; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ret = sd_session_get_seat(session->id, &session->seat); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to get seat id: %s", strerror(-ret)); | ||||||
|  | 		goto error; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	const char *fmt = "/org/freedesktop/login1/session/%s"; | ||||||
|  | 	int len = snprintf(NULL, 0, fmt, session->id); | ||||||
|  | 
 | ||||||
|  | 	session->path = malloc(len + 1); | ||||||
|  | 	if (!session->path) | ||||||
|  | 		goto error; | ||||||
|  | 
 | ||||||
|  | 	sprintf(session->path, fmt, session->id); | ||||||
|  | 
 | ||||||
|  | 	ret = sd_bus_default_system(&session->bus); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to open DBus connection: %s", strerror(-ret)); | ||||||
|  | 		goto error; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!session_activate(session)) | ||||||
|  | 		goto error_bus; | ||||||
|  | 
 | ||||||
|  | 	if (!take_control(session)) | ||||||
|  | 		goto error_bus; | ||||||
|  | 
 | ||||||
|  | 	return &session->base; | ||||||
|  | 
 | ||||||
|  | error_bus: | ||||||
|  | 	sd_bus_unref(session->bus); | ||||||
|  | 
 | ||||||
|  | error: | ||||||
|  | 	free(session->path); | ||||||
|  | 	free(session->id); | ||||||
|  | 	free(session->seat); | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const struct session_interface session_logind_iface = { | ||||||
|  | 	.start = logind_session_start, | ||||||
|  | 	.finish = logind_session_finish, | ||||||
|  | 	.open = logind_take_device, | ||||||
|  | 	.close = logind_release_device, | ||||||
|  | }; | ||||||
|  | @ -0,0 +1,38 @@ | ||||||
|  | #include <stddef.h> | ||||||
|  | 
 | ||||||
|  | #include <wlr/session.h> | ||||||
|  | #include "session/interface.h" | ||||||
|  | 
 | ||||||
|  | static const struct session_interface *ifaces[] = { | ||||||
|  | #ifdef HAS_SYSTEMD | ||||||
|  | 	&session_logind_iface, | ||||||
|  | #endif | ||||||
|  | 	NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct wlr_session *wlr_session_start(void) { | ||||||
|  | 	const struct session_interface **iter; | ||||||
|  | 
 | ||||||
|  | 	for (iter = ifaces; *iter; ++iter) { | ||||||
|  | 		struct wlr_session *session = (*iter)->start(); | ||||||
|  | 		if (session) { | ||||||
|  | 			return session; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void wlr_session_finish(struct wlr_session *session) { | ||||||
|  | 	session->iface.finish(session); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | int wlr_session_open_file(struct wlr_session *restrict session, | ||||||
|  | 	const char *restrict path) { | ||||||
|  | 
 | ||||||
|  | 	return session->iface.open(session, path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void wlr_session_close_file(struct wlr_session *session, int fd) { | ||||||
|  | 	session->iface.close(session, fd); | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue