From 2bd6fbf20ebb055d4871ffb8eedefa7d7aa60ee8 Mon Sep 17 00:00:00 2001 From: sghctoma Date: Thu, 30 Aug 2018 11:15:39 +0200 Subject: [PATCH 1/4] Fix that major(st_rdev) have no meaning on FreeBSD The major device number does not indicate the device type on FreeBSD, and AFAIK the only way to differentiate between DRM, input, and other devices is checking the fd path. This commit implements that. The drmDropmaster and drmSetmaster calls are necessary, because the implicit drop (that should occur when the DRM fd is closed) seems not to be working in some scenarios (e.g. if you have a tmux session running - maybe the fd is retained somehow by tmux?). This is a problem, because once you exit the compositor, you can't start it (or any other program that wants to be DRM master) again until you close all your tmux sessions. --- backend/session/direct-freebsd.c | 42 +++++++++++++++++++++++++++----- backend/session/direct-ipc.c | 21 ++++++++++++++-- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/backend/session/direct-freebsd.c b/backend/session/direct-freebsd.c index 63e1be01..1ef76f7a 100644 --- a/backend/session/direct-freebsd.c +++ b/backend/session/direct-freebsd.c @@ -40,12 +40,6 @@ static int direct_session_open(struct wlr_session *base, const char *path) { return fd; } - struct stat st; - if (fstat(fd, &st) < 0) { - close(fd); - return -errno; - } - return fd; } @@ -59,6 +53,20 @@ static void direct_session_close(struct wlr_session *base, int fd) { return; } + char *name; + name = devname(st.st_rdev, S_IFCHR); + if (name == NULL) { + wlr_log_errno(WLR_ERROR, "Failed to get device name"); + close(fd); + return; + } + + if (strncmp(name, "drm/", 4) == 0) { + direct_ipc_dropmaster(session->sock, fd); + } else if (strncmp(name, "input/event", 11)) { + ioctl(fd, EVIOCREVOKE, 0); + } + close(fd); } @@ -79,6 +87,8 @@ static void direct_session_destroy(struct wlr_session *base) { ioctl(session->tty_fd, KDSETMODE, KD_TEXT); ioctl(session->tty_fd, VT_SETMODE, &mode); + ioctl(session->tty_fd, VT_ACTIVATE, 1); + if (errno) { wlr_log(WLR_ERROR, "Failed to restore tty"); } @@ -97,9 +107,29 @@ static int vt_handler(int signo, void *data) { if (session->base.active) { session->base.active = false; wlr_signal_emit_safe(&session->base.session_signal, session); + + char *name; + struct wlr_device *dev; + wl_list_for_each(dev, &session->base.devices, link) { + name = devname(dev->dev, S_IFCHR); + if (name != NULL && strncmp(name, "drm/", 4) == 0) { + direct_ipc_dropmaster(session->sock, dev->fd); + } + } + ioctl(session->tty_fd, VT_RELDISP, 1); } else { ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ); + + char *name; + struct wlr_device *dev; + wl_list_for_each(dev, &session->base.devices, link) { + name = devname(dev->dev, S_IFCHR); + if (name != NULL && strncmp(name, "drm/", 4) == 0) { + direct_ipc_setmaster(session->sock, dev->fd); + } + } + session->base.active = true; wlr_signal_emit_safe(&session->base.session_signal, session); } diff --git a/backend/session/direct-ipc.c b/backend/session/direct-ipc.c index 5fdb95ac..99ffcb1b 100644 --- a/backend/session/direct-ipc.c +++ b/backend/session/direct-ipc.c @@ -142,6 +142,7 @@ static void communicate(int sock) { goto error; } +#ifndef __FreeBSD__ struct stat st; if (fstat(fd, &st) < 0) { ret = errno; @@ -157,6 +158,19 @@ static void communicate(int sock) { if (maj == DRM_MAJOR && drmSetMaster(fd)) { ret = errno; } +#else + if (strncmp(msg.path, "/dev/drm/", 9) && + strncmp(msg.path, "/dev/input/event", 16)) { + + ret = ENOTSUP; + goto error; + } + + if (strncmp(msg.path, "/dev/drm/", 9) == 0 && drmSetMaster(fd)) { + ret = errno; + } +#endif + error: send_msg(sock, ret ? -1 : fd, &ret, sizeof(ret)); if (fd >= 0) { @@ -193,8 +207,11 @@ int direct_ipc_open(int sock, const char *path) { send_msg(sock, -1, &msg, sizeof(msg)); - int fd, err; - recv_msg(sock, &fd, &err, sizeof(err)); + int fd, err, ret; + int retry = 0; + do { + ret = recv_msg(sock, &fd, &err, sizeof(err)); + } while (ret == 0 && retry++ < 3); return err ? -err : fd; } From d948bffd3e7b283870dcbd7f1a5dd2aaea1950ec Mon Sep 17 00:00:00 2001 From: sghctoma Date: Sun, 2 Sep 2018 20:01:13 +0200 Subject: [PATCH 2/4] Activate last active VT after compositor exit The VT the compositor was started from was not activated after exiting the compositor, which resulted in arriving on a blank VT. This commit fixes that by introducing a new field in direct_session struct that stores the last active VT so that it can be activated in direct_session_destroy. --- backend/session/direct-freebsd.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/backend/session/direct-freebsd.c b/backend/session/direct-freebsd.c index 1ef76f7a..7784296b 100644 --- a/backend/session/direct-freebsd.c +++ b/backend/session/direct-freebsd.c @@ -23,6 +23,7 @@ const struct session_impl session_direct; struct direct_session { struct wlr_session base; int tty_fd; + int old_tty; int old_kbmode; int sock; pid_t child; @@ -87,7 +88,7 @@ static void direct_session_destroy(struct wlr_session *base) { ioctl(session->tty_fd, KDSETMODE, KD_TEXT); ioctl(session->tty_fd, VT_SETMODE, &mode); - ioctl(session->tty_fd, VT_ACTIVATE, 1); + ioctl(session->tty_fd, VT_ACTIVATE, session->old_tty); if (errno) { wlr_log(WLR_ERROR, "Failed to restore tty"); @@ -138,11 +139,15 @@ static int vt_handler(int signo, void *data) { } static bool setup_tty(struct direct_session *session, struct wl_display *display) { - int fd = -1, tty = -1, tty0_fd = -1; + int fd = -1, tty = -1, tty0_fd = -1, old_tty = 1; if ((tty0_fd = open("/dev/ttyv0", O_RDWR | O_CLOEXEC)) < 0) { wlr_log_errno(WLR_ERROR, "Could not open /dev/ttyv0 to find a free vt"); goto error; } + if (ioctl(tty0_fd, VT_GETACTIVE, &old_tty) != 0) { + wlr_log_errno(WLR_ERROR, "Could not get active vt"); + goto error; + } if (ioctl(tty0_fd, VT_OPENQRY, &tty) != 0) { wlr_log_errno(WLR_ERROR, "Could not find a free vt"); goto error; @@ -198,13 +203,16 @@ static bool setup_tty(struct direct_session *session, struct wl_display *display session->base.vtnr = tty; session->tty_fd = fd; + session->old_tty = old_tty; session->old_kbmode = old_kbmode; return true; error: - // Drop back to tty 1, better than hanging in a useless blank console - ioctl(fd, VT_ACTIVATE, 1); + // In case we could not get the last active one, drop back to tty 1, + // better than hanging in a useless blank console. Otherwise activate the + // last active. + ioctl(fd, VT_ACTIVATE, old_tty); close(fd); return false; } From 7a5d3c4d3bf8c2f21fcec65182f1563f4c857408 Mon Sep 17 00:00:00 2001 From: sghctoma Date: Fri, 7 Sep 2018 16:37:25 +0200 Subject: [PATCH 3/4] Implement device type discovery using ioctl calls This commit implements device type discovery by calling two ioctls (DRM_IOCTL_VERSION and EVIOCGVERSION) on the device. These iocts are specific to drm and input devices respectively, therefore we can determine the device type based on which one returns an error. --- backend/session/direct-freebsd.c | 34 +++++++++----------------------- backend/session/direct-ipc.c | 10 ++++++---- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/backend/session/direct-freebsd.c b/backend/session/direct-freebsd.c index 7784296b..cecfb0c1 100644 --- a/backend/session/direct-freebsd.c +++ b/backend/session/direct-freebsd.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "backend/session/direct-ipc.h" #include "util/signal.h" @@ -47,24 +48,11 @@ static int direct_session_open(struct wlr_session *base, const char *path) { static void direct_session_close(struct wlr_session *base, int fd) { struct direct_session *session = wl_container_of(base, session, base); - struct stat st; - if (fstat(fd, &st) < 0) { - wlr_log_errno(WLR_ERROR, "Stat failed"); - close(fd); - return; - } - - char *name; - name = devname(st.st_rdev, S_IFCHR); - if (name == NULL) { - wlr_log_errno(WLR_ERROR, "Failed to get device name"); - close(fd); - return; - } - - if (strncmp(name, "drm/", 4) == 0) { + int ev; + struct drm_version dv = {0}; + if (ioctl(fd, DRM_IOCTL_VERSION, &dv) == 0) { direct_ipc_dropmaster(session->sock, fd); - } else if (strncmp(name, "input/event", 11)) { + } else if (ioctl(fd, EVIOCGVERSION, &ev) == 0) { ioctl(fd, EVIOCREVOKE, 0); } @@ -104,16 +92,15 @@ static void direct_session_destroy(struct wlr_session *base) { static int vt_handler(int signo, void *data) { struct direct_session *session = data; + struct drm_version dv = {0}; + struct wlr_device *dev; if (session->base.active) { session->base.active = false; wlr_signal_emit_safe(&session->base.session_signal, session); - char *name; - struct wlr_device *dev; wl_list_for_each(dev, &session->base.devices, link) { - name = devname(dev->dev, S_IFCHR); - if (name != NULL && strncmp(name, "drm/", 4) == 0) { + if (ioctl(dev->fd, DRM_IOCTL_VERSION, &dv) == 0) { direct_ipc_dropmaster(session->sock, dev->fd); } } @@ -122,11 +109,8 @@ static int vt_handler(int signo, void *data) { } else { ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ); - char *name; - struct wlr_device *dev; wl_list_for_each(dev, &session->base.devices, link) { - name = devname(dev->dev, S_IFCHR); - if (name != NULL && strncmp(name, "drm/", 4) == 0) { + if (ioctl(dev->fd, DRM_IOCTL_VERSION, &dv) == 0) { direct_ipc_setmaster(session->sock, dev->fd); } } diff --git a/backend/session/direct-ipc.c b/backend/session/direct-ipc.c index 99ffcb1b..302b639e 100644 --- a/backend/session/direct-ipc.c +++ b/backend/session/direct-ipc.c @@ -3,6 +3,7 @@ #define __BSD_VISIBLE 1 #define INPUT_MAJOR 0 #endif +#include #include #include #include @@ -159,14 +160,15 @@ static void communicate(int sock) { ret = errno; } #else - if (strncmp(msg.path, "/dev/drm/", 9) && - strncmp(msg.path, "/dev/input/event", 16)) { - + int ev; + struct drm_version dv = {0}; + if (ioctl(fd, EVIOCGVERSION, &ev) == -1 && + ioctl(fd, DRM_IOCTL_VERSION, &dv) == -1) { ret = ENOTSUP; goto error; } - if (strncmp(msg.path, "/dev/drm/", 9) == 0 && drmSetMaster(fd)) { + if (dv.version_major != 0 && drmSetMaster(fd)) { ret = errno; } #endif From ba5df0d21d5838ce4fe616c2e20719ccc8a4315c Mon Sep 17 00:00:00 2001 From: sghctoma Date: Fri, 7 Sep 2018 17:44:48 +0200 Subject: [PATCH 4/4] Fix build failure on non-FreeBSD systems Accidentally included the FreeBSD-specific dev/evdev/input.h file on other systems too. This commit fixes that. --- backend/session/direct-ipc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/session/direct-ipc.c b/backend/session/direct-ipc.c index 302b639e..3abce46a 100644 --- a/backend/session/direct-ipc.c +++ b/backend/session/direct-ipc.c @@ -1,9 +1,8 @@ #define _POSIX_C_SOURCE 200809L #ifdef __FreeBSD__ #define __BSD_VISIBLE 1 -#define INPUT_MAJOR 0 -#endif #include +#endif #include #include #include