From 60955addc0357cd80c9e86616ca8f2b2f80d1d31 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Mon, 3 Jul 2017 14:46:20 +1200 Subject: [PATCH 01/11] Added direct session support --- CMake/FindLibcap.cmake | 56 ++++++++++++++ CMakeLists.txt | 1 + session/CMakeLists.txt | 5 +- session/direct.c | 165 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 222 insertions(+), 5 deletions(-) create mode 100644 CMake/FindLibcap.cmake diff --git a/CMake/FindLibcap.cmake b/CMake/FindLibcap.cmake new file mode 100644 index 00000000..b34e5e37 --- /dev/null +++ b/CMake/FindLibcap.cmake @@ -0,0 +1,56 @@ +#.rst: +# FindLibcap +# ------- +# +# Find Libcap library +# +# Try to find Libcap library. The following values are defined +# +# :: +# +# Libcap_FOUND - True if Libcap is available +# Libcap_INCLUDE_DIRS - Include directories for Libcap +# Libcap_LIBRARIES - List of libraries for Libcap +# Libcap_DEFINITIONS - List of definitions for Libcap +# +# and also the following more fine grained variables +# +# :: +# +# Libcap_VERSION +# Libcap_VERSION_MAJOR +# Libcap_VERSION_MINOR +# +#============================================================================= +# Copyright (c) 2017 Jerzi Kaminsky +# +# Distributed under the OSI-approved BSD License (the "License"); +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= + +include(FeatureSummary) +set_package_properties(Libcap PROPERTIES + URL "https://www.kernel.org/pub/linux/libs/security/linux-privs/libcap2" + DESCRIPTION "Library for getting and setting POSIX.1e capabilities") + +find_package(PkgConfig) +pkg_check_modules(PC_CAP QUIET Libcap) +find_library(Libcap_LIBRARIES NAMES cap HINTS ${PC_CAP_LIBRARY_DIRS}) +find_path(Libcap_INCLUDE_DIRS sys/capability.h HINTS ${PC_CAP_INCLUDE_DIRS}) + +set(Libcap_VERSION ${PC_CAP_VERSION}) +string(REPLACE "." ";" VERSION_LIST "${PC_CAP_VERSION}") + +LIST(LENGTH VERSION_LIST n) +if (n EQUAL 2) + list(GET VERSION_LIST 0 Libcap_VERSION_MAJOR) + list(GET VERSION_LIST 1 Libcap_VERSION_MINOR) +endif () + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libcap DEFAULT_MSG Libcap_INCLUDE_DIRS Libcap_LIBRARIES) +mark_as_advanced(Libcap_INCLUDE_DIRS Libcap_LIBRARIES Libcap_DEFINITIONS + Libcap_VERSION Libcap_VERSION_MAJOR Libcap_VERSION_MICRO Libcap_VERSION_MINOR) diff --git a/CMakeLists.txt b/CMakeLists.txt index b15882ce..ba1cfba8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ find_package(GBM REQUIRED) find_package(LibInput REQUIRED) find_package(XKBCommon REQUIRED) find_package(Udev REQUIRED) +find_package(Libcap REQUIRED) find_package(Systemd) include(Wayland) diff --git a/session/CMakeLists.txt b/session/CMakeLists.txt index 23077c12..72ef9f56 100644 --- a/session/CMakeLists.txt +++ b/session/CMakeLists.txt @@ -1,5 +1,7 @@ include_directories( - ${WAYLAND_INCLUDE_DIR} + ${WAYLAND_INCLUDE_DIR} + ${DRM_INCLUDE_DIRS} + ${Libcap_INCLUDE_DIRS} ) set(sources @@ -10,6 +12,7 @@ set(sources set(libs wlr-util ${WAYLAND_LIBRARIES} + ${Libcap_LIBRARIES} ) if (SYSTEMD_FOUND) diff --git a/session/direct.c b/session/direct.c index 74f2faf1..e128658a 100644 --- a/session/direct.c +++ b/session/direct.c @@ -5,36 +5,174 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include #include +#ifndef KDSKBMUTE +#define KDSKBMUTE 0x4B51 +#endif + +enum { DRM_MAJOR = 226 }; + const struct session_impl session_direct; struct direct_session { struct wlr_session base; + int tty_fd; + int drm_fd; + int kb_mode; + + struct wl_event_source *vt_source; }; static int direct_session_open(struct wlr_session *restrict base, - const char *restrict path) { - return open(path, O_RDWR | O_CLOEXEC); + const char *restrict path) { + struct direct_session *session = wl_container_of(base, session, base); + int fd = open(path, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); + if (fd == -1) { + wlr_log_errno(L_ERROR, "%s", path); + return -errno; + } + + struct stat st; + if (fstat(fd, &st) == 0 && major(st.st_rdev) == DRM_MAJOR) { + session->drm_fd = fd; + drmSetMaster(fd); + } + + return fd; } static void direct_session_close(struct wlr_session *base, int fd) { + struct direct_session *session = wl_container_of(base, session, base); + + if (fd == session->drm_fd) { + drmDropMaster(fd); + session->drm_fd = -1; + } + close(fd); } static bool direct_change_vt(struct wlr_session *base, int vt) { - // TODO - return false; + struct direct_session *session = wl_container_of(base, session, base); + return ioctl(session->tty_fd, VT_ACTIVATE, vt) == 0; } static void direct_session_finish(struct wlr_session *base) { struct direct_session *session = wl_container_of(base, session, base); + struct vt_mode mode = { + .mode = VT_AUTO, + }; + if (ioctl(session->tty_fd, KDSKBMUTE, 0)) { + ioctl(session->tty_fd, KDSKBMODE, session->kb_mode); + } + ioctl(session->tty_fd, KDSETMODE, KD_TEXT); + ioctl(session->tty_fd, VT_SETMODE, &mode); + + wl_event_source_remove(session->vt_source); + close(session->tty_fd); free(session); } +static int vt_handler(int signo, void *data) { + struct direct_session *session = data; + + if (session->base.active) { + session->base.active = false; + wl_signal_emit(&session->base.session_signal, session); + drmDropMaster(session->drm_fd); + ioctl(session->tty_fd, VT_RELDISP, 1); + } else { + ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ); + drmSetMaster(session->drm_fd); + session->base.active = true; + wl_signal_emit(&session->base.session_signal, session); + } + + return 1; +} + +static bool setup_tty(struct direct_session *session, struct wl_display *display) { + session->tty_fd = dup(STDIN_FILENO); + + struct stat st; + if (fstat(session->tty_fd, &st) == -1 || major(st.st_rdev) != TTY_MAJOR || + minor(st.st_rdev) == 0) { + wlr_log(L_ERROR, "Not running from a virtual terminal"); + goto error; + } + + int ret; + + int kd_mode; + ret = ioctl(session->tty_fd, KDGETMODE, &kd_mode); + if (ret) { + wlr_log_errno(L_ERROR, "Failed to get tty mode"); + goto error; + } + + if (kd_mode != KD_TEXT) { + wlr_log(L_ERROR, + "tty already in graphics mode; is another display server running?"); + goto error; + } + + ioctl(session->tty_fd, VT_ACTIVATE, minor(st.st_rdev)); + ioctl(session->tty_fd, VT_WAITACTIVE, minor(st.st_rdev)); + + if (ioctl(session->tty_fd, KDGKBMODE, &session->kb_mode)) { + wlr_log_errno(L_ERROR, "Failed to read keyboard mode"); + goto error; + } + + if (ioctl(session->tty_fd, KDSKBMUTE, 1) && + ioctl(session->tty_fd, KDSKBMODE, K_OFF)) { + wlr_log_errno(L_ERROR, "Failed to set keyboard mode"); + goto error; + } + + if (ioctl(session->tty_fd, KDSETMODE, KD_GRAPHICS)) { + wlr_log_errno(L_ERROR, "Failed to get graphics mode on tty"); + goto error; + } + + struct vt_mode mode = { + .mode = VT_PROCESS, + .relsig = SIGUSR1, + .acqsig = SIGUSR1, + }; + + if (ioctl(session->tty_fd, VT_SETMODE, &mode) < 0) { + wlr_log(L_ERROR, "Failed to take control of tty"); + goto error; + } + + struct wl_event_loop *loop = wl_display_get_event_loop(display); + session->vt_source = wl_event_loop_add_signal(loop, SIGUSR1, + vt_handler, session); + if (!session->vt_source) { + goto error; + } + + return true; + +error: + close(session->tty_fd); + return false; +} + static struct wlr_session *direct_session_start(struct wl_display *disp) { struct direct_session *session = calloc(1, sizeof(*session)); if (!session) { @@ -42,12 +180,31 @@ static struct wlr_session *direct_session_start(struct wl_display *disp) { return NULL; } + cap_t cap = cap_get_proc(); + cap_flag_value_t val; + + if (!cap || cap_get_flag(cap, CAP_SYS_ADMIN, CAP_PERMITTED, &val) || val != CAP_SET) { + wlr_log(L_ERROR, "Do not have CAP_SYS_ADMIN; cannot become DRM master"); + cap_free(cap); + goto error_session; + } + + cap_free(cap); + + if (!setup_tty(session, disp)) { + goto error_session; + } + wlr_log(L_INFO, "Successfully loaded direct session"); session->base.impl = &session_direct; session->base.active = true; wl_signal_init(&session->base.session_signal); return &session->base; + +error_session: + free(session); + return NULL; } const struct session_impl session_direct = { From c2b54be74ac8cfbca5833214cc93e97350c0cf6f Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Mon, 3 Jul 2017 19:56:14 +1200 Subject: [PATCH 02/11] Minor fixes --- session/direct.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/session/direct.c b/session/direct.c index e128658a..f3be87b6 100644 --- a/session/direct.c +++ b/session/direct.c @@ -38,16 +38,26 @@ struct direct_session { static int direct_session_open(struct wlr_session *restrict base, const char *restrict path) { struct direct_session *session = wl_container_of(base, session, base); + + // These are the flags logind uses int fd = open(path, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); if (fd == -1) { - wlr_log_errno(L_ERROR, "%s", path); + wlr_log_errno(L_ERROR, "Cannot open %s", path); return -errno; } struct stat st; if (fstat(fd, &st) == 0 && major(st.st_rdev) == DRM_MAJOR) { + if (drmSetMaster(fd)) { + // Save errno, in case close() clobbers it + int e = errno; + wlr_log(L_ERROR, "Cannot become DRM master: %s%s", strerror(e), + e == EINVAL ? "; is another display server running?" : ""); + close(fd); + return -e; + } + session->drm_fd = fd; - drmSetMaster(fd); } return fd; @@ -105,6 +115,7 @@ static int vt_handler(int signo, void *data) { } static bool setup_tty(struct direct_session *session, struct wl_display *display) { + // TODO: Change this to accept any TTY, instead of just the current one session->tty_fd = dup(STDIN_FILENO); struct stat st; @@ -144,7 +155,7 @@ static bool setup_tty(struct direct_session *session, struct wl_display *display } if (ioctl(session->tty_fd, KDSETMODE, KD_GRAPHICS)) { - wlr_log_errno(L_ERROR, "Failed to get graphics mode on tty"); + wlr_log_errno(L_ERROR, "Failed to set graphics mode on tty"); goto error; } @@ -174,23 +185,23 @@ error: } static struct wlr_session *direct_session_start(struct wl_display *disp) { - struct direct_session *session = calloc(1, sizeof(*session)); - if (!session) { - wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno)); - return NULL; - } - cap_t cap = cap_get_proc(); cap_flag_value_t val; if (!cap || cap_get_flag(cap, CAP_SYS_ADMIN, CAP_PERMITTED, &val) || val != CAP_SET) { wlr_log(L_ERROR, "Do not have CAP_SYS_ADMIN; cannot become DRM master"); cap_free(cap); - goto error_session; + return NULL; } cap_free(cap); + struct direct_session *session = calloc(1, sizeof(*session)); + if (!session) { + wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno)); + return NULL; + } + if (!setup_tty(session, disp)) { goto error_session; } From 5c211e6195bb23dabfa7883b3050cf72db6f21ef Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Tue, 4 Jul 2017 00:15:09 +1200 Subject: [PATCH 03/11] Added forking --- session/direct.c | 205 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 186 insertions(+), 19 deletions(-) diff --git a/session/direct.c b/session/direct.c index f3be87b6..3615aec5 100644 --- a/session/direct.c +++ b/session/direct.c @@ -1,6 +1,8 @@ #define _POSIX_C_SOURCE 200809L #include #include +#include +#include #include #include #include @@ -10,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -31,32 +35,89 @@ struct direct_session { int tty_fd; int drm_fd; int kb_mode; + int sock; + pid_t child; struct wl_event_source *vt_source; }; +enum session_message_type { + SESSION_OPEN, + SESSION_SETMASTER, + SESSION_DROPMASTER, + SESSION_END, +}; + +struct session_message { + enum session_message_type type; + char path[60]; +}; + +static int send_message(int sock, enum session_message_type type, const char *path) { + struct session_message msg = { + .type = type, + }; + struct msghdr request = { + .msg_iov = &(struct iovec) { + .iov_base = &msg, + .iov_len = sizeof(msg), + }, + .msg_iovlen = 1, + }; + + if (path) { + snprintf(msg.path, sizeof(msg.path), "%s", path); + } + + sendmsg(sock, &request, 0); + + int err = 0, fd = -1; + char control[CMSG_SPACE(sizeof(fd))] = {0}; + struct msghdr reply = { + .msg_iov = &(struct iovec) { + .iov_base = &err, + .iov_len = sizeof(err), + }, + .msg_iovlen = 1, + .msg_control = control, + .msg_controllen = sizeof(control), + }; + + recvmsg(sock, &reply, 0); + + // The other types have no meaningful return value + if (type != SESSION_OPEN) { + return 0; + } + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&reply); + memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd)); + + return err ? -err : fd; +} + static int direct_session_open(struct wlr_session *restrict base, const char *restrict path) { struct direct_session *session = wl_container_of(base, session, base); - // These are the flags logind uses - int fd = open(path, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); - if (fd == -1) { - wlr_log_errno(L_ERROR, "Cannot open %s", path); + struct stat st; + if (stat(path, &st)) { return -errno; } - struct stat st; - if (fstat(fd, &st) == 0 && major(st.st_rdev) == DRM_MAJOR) { - if (drmSetMaster(fd)) { - // Save errno, in case close() clobbers it - int e = errno; - wlr_log(L_ERROR, "Cannot become DRM master: %s%s", strerror(e), - e == EINVAL ? "; is another display server running?" : ""); - close(fd); - return -e; - } + uint32_t maj = major(st.st_rdev); + if (maj != DRM_MAJOR && maj != INPUT_MAJOR) { + return -EINVAL; + } + int fd = send_message(session->sock, SESSION_OPEN, path); + if (fd < 0) { + wlr_log(L_ERROR, "Failed to open %s: %s%s", path, strerror(-fd), + fd == -EINVAL ? "; is another display server running?" : ""); + return fd; + } + + if (maj == DRM_MAJOR) { session->drm_fd = fd; } @@ -67,7 +128,7 @@ static void direct_session_close(struct wlr_session *base, int fd) { struct direct_session *session = wl_container_of(base, session, base); if (fd == session->drm_fd) { - drmDropMaster(fd); + send_message(session->sock, SESSION_DROPMASTER, NULL); session->drm_fd = -1; } @@ -91,6 +152,10 @@ static void direct_session_finish(struct wlr_session *base) { ioctl(session->tty_fd, KDSETMODE, KD_TEXT); ioctl(session->tty_fd, VT_SETMODE, &mode); + send_message(session->sock, SESSION_END, NULL); + close(session->sock); + wait(NULL); + wl_event_source_remove(session->vt_source); close(session->tty_fd); free(session); @@ -102,11 +167,11 @@ static int vt_handler(int signo, void *data) { if (session->base.active) { session->base.active = false; wl_signal_emit(&session->base.session_signal, session); - drmDropMaster(session->drm_fd); + send_message(session->sock, SESSION_DROPMASTER, NULL); ioctl(session->tty_fd, VT_RELDISP, 1); } else { ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ); - drmSetMaster(session->drm_fd); + send_message(session->sock, SESSION_SETMASTER, NULL); session->base.active = true; wl_signal_emit(&session->base.session_signal, session); } @@ -184,6 +249,78 @@ error: return false; } +static void communicate(int sock) { + struct session_message msg; + struct msghdr hdr = { + .msg_iov = &(struct iovec) { + .iov_base = &msg, + .iov_len = sizeof(msg), + }, + .msg_iovlen = 1, + }; + + int drm_fd = -1; + + while (recvmsg(sock, &hdr, 0) >= 0 || errno == EINTR) { + switch (msg.type) { + case SESSION_OPEN: + errno = 0; + // These are the flags that logind use + int fd = open(msg.path, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); + int e = errno; + + struct stat st; + if (fstat(fd, &st) >= 0 && major(st.st_rdev) == DRM_MAJOR) { + if (drmSetMaster(fd)) { + close(fd); + fd = -1; + e = errno; + } + + drm_fd = fd; + } + + char control[CMSG_SPACE(sizeof(fd))] = {0}; + struct msghdr reply = { + .msg_iov = &(struct iovec) { + .iov_base = &e, + .iov_len = sizeof(e), + }, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&reply); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + sendmsg(sock, &reply, 0); + break; + case SESSION_SETMASTER: + if (drm_fd != -1) { + drmSetMaster(drm_fd); + } + + sendmsg(sock, &(struct msghdr){0}, 0); + break; + case SESSION_DROPMASTER: + if (drm_fd != -1) { + drmDropMaster(drm_fd); + } + + sendmsg(sock, &(struct msghdr){0}, 0); + break; + case SESSION_END: + sendmsg(sock, &(struct msghdr){0}, 0); + return; + } + } + + +} + static struct wlr_session *direct_session_start(struct wl_display *disp) { cap_t cap = cap_get_proc(); cap_flag_value_t val; @@ -196,12 +333,36 @@ static struct wlr_session *direct_session_start(struct wl_display *disp) { cap_free(cap); + int sock[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) < 0) { + wlr_log_errno(L_ERROR, "Failed to create socket pair"); + return NULL; + } + + pid_t pid = fork(); + if (pid < 0) { + wlr_log_errno(L_ERROR, "Fork failed"); + goto error_sock; + } else if (pid == 0) { + close(sock[0]); + + communicate(sock[1]); + + _Exit(0); + } + + close(sock[1]); + sock[1] = -1; + struct direct_session *session = calloc(1, sizeof(*session)); if (!session) { - wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno)); - return NULL; + wlr_log_errno(L_ERROR, "Allocation failed"); + goto error_child; } + session->child = pid; + session->sock = sock[0]; + if (!setup_tty(session, disp)) { goto error_session; } @@ -215,6 +376,12 @@ static struct wlr_session *direct_session_start(struct wl_display *disp) { error_session: free(session); +error_child: + send_message(sock[0], SESSION_END, NULL); + wait(NULL); +error_sock: + close(sock[0]); + close(sock[1]); return NULL; } From dd40a42a99bc3f11efa3f080f368913f17e421e6 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Tue, 4 Jul 2017 00:29:03 +1200 Subject: [PATCH 04/11] Make libcap optional. --- CMakeLists.txt | 2 +- README.md | 1 + session/CMakeLists.txt | 10 ++++++++-- session/direct.c | 22 ++++++++++++++++++++-- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba1cfba8..d1276f8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,7 +50,7 @@ find_package(GBM REQUIRED) find_package(LibInput REQUIRED) find_package(XKBCommon REQUIRED) find_package(Udev REQUIRED) -find_package(Libcap REQUIRED) +find_package(Libcap) find_package(Systemd) include(Wayland) diff --git a/README.md b/README.md index 75d191d4..4dcfed0e 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Install dependencies: * libinput * udev * systemd (optional, for logind support) +* libcap (optional, for capability support) * asciidoc (optional, for man pages) Run these commands: diff --git a/session/CMakeLists.txt b/session/CMakeLists.txt index 72ef9f56..d79991ee 100644 --- a/session/CMakeLists.txt +++ b/session/CMakeLists.txt @@ -1,7 +1,6 @@ include_directories( ${WAYLAND_INCLUDE_DIR} ${DRM_INCLUDE_DIRS} - ${Libcap_INCLUDE_DIRS} ) set(sources @@ -12,7 +11,6 @@ set(sources set(libs wlr-util ${WAYLAND_LIBRARIES} - ${Libcap_LIBRARIES} ) if (SYSTEMD_FOUND) @@ -24,5 +22,13 @@ if (SYSTEMD_FOUND) list(APPEND libs ${SYSTEMD_LIBRARIES}) endif () +if (Libcap_FOUND) + add_definitions(${Libcap_DEFINITIONS}) + include_directories(${Libcap_INCLUDE_DIRS}) + + add_definitions(-DHAS_LIBCAP) + list(APPEND libs ${Libcap_LIBRARIES}) +endif () + add_library(wlr-session ${sources}) target_link_libraries(wlr-session ${libs}) diff --git a/session/direct.c b/session/direct.c index 3615aec5..e6c2440f 100644 --- a/session/direct.c +++ b/session/direct.c @@ -321,17 +321,35 @@ static void communicate(int sock) { } -static struct wlr_session *direct_session_start(struct wl_display *disp) { +#ifdef HAS_LIBCAP +static bool have_permissions(void) { cap_t cap = cap_get_proc(); cap_flag_value_t val; if (!cap || cap_get_flag(cap, CAP_SYS_ADMIN, CAP_PERMITTED, &val) || val != CAP_SET) { wlr_log(L_ERROR, "Do not have CAP_SYS_ADMIN; cannot become DRM master"); cap_free(cap); - return NULL; + return false; } cap_free(cap); + return true; +} +#else +static bool have_permissions(void) { + if (geteuid() != 0) { + wlr_log(L_ERROR, "Do not have root privileges; cannot become DRM master"); + return false; + } + + return true; +} +#endif + +static struct wlr_session *direct_session_start(struct wl_display *disp) { + if (!have_permissions()) { + return NULL; + } int sock[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) < 0) { From 2c27ed13934df0292df8eefea3a4cd1638785f0e Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Tue, 4 Jul 2017 12:50:54 +1200 Subject: [PATCH 05/11] include fixes --- session/direct.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/session/direct.c b/session/direct.c index e6c2440f..0bee21d3 100644 --- a/session/direct.c +++ b/session/direct.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -11,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -21,6 +19,9 @@ #include #include #include +#ifdef HAS_LIBCAP +#include +#endif #ifndef KDSKBMUTE #define KDSKBMUTE 0x4B51 From f413a67bb144cbd5ccb80c32f1ae31ca8ce9360e Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Sun, 9 Jul 2017 13:34:33 +1200 Subject: [PATCH 06/11] Removed KDSKBMUTE. This actually isn't supported by Linux. --- session/direct.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/session/direct.c b/session/direct.c index 0bee21d3..353a93bc 100644 --- a/session/direct.c +++ b/session/direct.c @@ -23,10 +23,6 @@ #include #endif -#ifndef KDSKBMUTE -#define KDSKBMUTE 0x4B51 -#endif - enum { DRM_MAJOR = 226 }; const struct session_impl session_direct; @@ -147,9 +143,7 @@ static void direct_session_finish(struct wlr_session *base) { .mode = VT_AUTO, }; - if (ioctl(session->tty_fd, KDSKBMUTE, 0)) { - ioctl(session->tty_fd, KDSKBMODE, session->kb_mode); - } + ioctl(session->tty_fd, KDSKBMODE, session->kb_mode); ioctl(session->tty_fd, KDSETMODE, KD_TEXT); ioctl(session->tty_fd, VT_SETMODE, &mode); @@ -214,8 +208,7 @@ static bool setup_tty(struct direct_session *session, struct wl_display *display goto error; } - if (ioctl(session->tty_fd, KDSKBMUTE, 1) && - ioctl(session->tty_fd, KDSKBMODE, K_OFF)) { + if (ioctl(session->tty_fd, KDSKBMODE, K_OFF)) { wlr_log_errno(L_ERROR, "Failed to set keyboard mode"); goto error; } From bd5bca52830e78364ab48efb0f6e4d0f1d9773db Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Sun, 9 Jul 2017 17:53:13 +1200 Subject: [PATCH 07/11] Added public fields to wlr_session --- include/wlr/session.h | 12 ++++---- include/wlr/session/interface.h | 5 ++-- session/direct.c | 50 +++++++++++++++++++++------------ session/logind.c | 29 ++++++++++--------- session/session.c | 6 ++-- 5 files changed, 59 insertions(+), 43 deletions(-) diff --git a/include/wlr/session.h b/include/wlr/session.h index 4a5b2174..52dbf8ca 100644 --- a/include/wlr/session.h +++ b/include/wlr/session.h @@ -9,16 +9,18 @@ struct session_impl; struct wlr_session { const struct session_impl *impl; - - bool active; struct wl_signal session_signal; + bool active; + + int drm_fd; + unsigned vtnr; + char seat[8]; }; struct wlr_session *wlr_session_start(struct wl_display *disp); void wlr_session_finish(struct wlr_session *session); -int wlr_session_open_file(struct wlr_session *restrict session, - const char *restrict path); +int wlr_session_open_file(struct wlr_session *session, const char *path); void wlr_session_close_file(struct wlr_session *session, int fd); -bool wlr_session_change_vt(struct wlr_session *session, int vt); +bool wlr_session_change_vt(struct wlr_session *session, unsigned vt); #endif diff --git a/include/wlr/session/interface.h b/include/wlr/session/interface.h index f75acfa4..4938110d 100644 --- a/include/wlr/session/interface.h +++ b/include/wlr/session/interface.h @@ -6,10 +6,9 @@ struct session_impl { struct wlr_session *(*start)(struct wl_display *disp); void (*finish)(struct wlr_session *session); - int (*open)(struct wlr_session *restrict session, - const char *restrict path); + int (*open)(struct wlr_session *session, const char *path); void (*close)(struct wlr_session *session, int fd); - bool (*change_vt)(struct wlr_session *session, int vt); + bool (*change_vt)(struct wlr_session *session, unsigned vt); }; #endif diff --git a/session/direct.c b/session/direct.c index 353a93bc..3932af68 100644 --- a/session/direct.c +++ b/session/direct.c @@ -30,8 +30,7 @@ const struct session_impl session_direct; struct direct_session { struct wlr_session base; int tty_fd; - int drm_fd; - int kb_mode; + int old_kbmode; int sock; pid_t child; @@ -93,8 +92,7 @@ static int send_message(int sock, enum session_message_type type, const char *pa return err ? -err : fd; } -static int direct_session_open(struct wlr_session *restrict base, - const char *restrict path) { +static int direct_session_open(struct wlr_session *base, const char *path) { struct direct_session *session = wl_container_of(base, session, base); struct stat st; @@ -115,7 +113,7 @@ static int direct_session_open(struct wlr_session *restrict base, } if (maj == DRM_MAJOR) { - session->drm_fd = fd; + session->base.drm_fd = fd; } return fd; @@ -124,17 +122,17 @@ static int direct_session_open(struct wlr_session *restrict base, static void direct_session_close(struct wlr_session *base, int fd) { struct direct_session *session = wl_container_of(base, session, base); - if (fd == session->drm_fd) { + if (fd == session->base.drm_fd) { send_message(session->sock, SESSION_DROPMASTER, NULL); - session->drm_fd = -1; + session->base.drm_fd = -1; } close(fd); } -static bool direct_change_vt(struct wlr_session *base, int vt) { +static bool direct_change_vt(struct wlr_session *base, unsigned vt) { struct direct_session *session = wl_container_of(base, session, base); - return ioctl(session->tty_fd, VT_ACTIVATE, vt) == 0; + return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0; } static void direct_session_finish(struct wlr_session *base) { @@ -143,10 +141,16 @@ static void direct_session_finish(struct wlr_session *base) { .mode = VT_AUTO, }; - ioctl(session->tty_fd, KDSKBMODE, session->kb_mode); + errno = 0; + + ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode); ioctl(session->tty_fd, KDSETMODE, KD_TEXT); ioctl(session->tty_fd, VT_SETMODE, &mode); + if (errno) { + wlr_log(L_ERROR, "Failed to restore tty"); + } + send_message(session->sock, SESSION_END, NULL); close(session->sock); wait(NULL); @@ -175,8 +179,11 @@ static int vt_handler(int signo, void *data) { } static bool setup_tty(struct direct_session *session, struct wl_display *display) { - // TODO: Change this to accept any TTY, instead of just the current one session->tty_fd = dup(STDIN_FILENO); + if (session->tty_fd == -1) { + wlr_log_errno(L_ERROR, "Cannot open tty"); + return false; + } struct stat st; if (fstat(session->tty_fd, &st) == -1 || major(st.st_rdev) != TTY_MAJOR || @@ -185,9 +192,10 @@ static bool setup_tty(struct direct_session *session, struct wl_display *display goto error; } - int ret; + int tty = minor(st.st_rdev); + int ret, kd_mode; + session->base.vtnr = tty; - int kd_mode; ret = ioctl(session->tty_fd, KDGETMODE, &kd_mode); if (ret) { wlr_log_errno(L_ERROR, "Failed to get tty mode"); @@ -200,10 +208,10 @@ static bool setup_tty(struct direct_session *session, struct wl_display *display goto error; } - ioctl(session->tty_fd, VT_ACTIVATE, minor(st.st_rdev)); - ioctl(session->tty_fd, VT_WAITACTIVE, minor(st.st_rdev)); + ioctl(session->tty_fd, VT_ACTIVATE, tty); + ioctl(session->tty_fd, VT_WAITACTIVE, tty); - if (ioctl(session->tty_fd, KDGKBMODE, &session->kb_mode)) { + if (ioctl(session->tty_fd, KDGKBMODE, &session->old_kbmode)) { wlr_log_errno(L_ERROR, "Failed to read keyboard mode"); goto error; } @@ -311,8 +319,6 @@ static void communicate(int sock) { return; } } - - } #ifdef HAS_LIBCAP @@ -379,8 +385,16 @@ static struct wlr_session *direct_session_start(struct wl_display *disp) { goto error_session; } + // XXX: Is it okay to trust the environment like this? + const char *seat = getenv("XDG_SEAT"); + if (!seat) { + seat = "seat0"; + } + wlr_log(L_INFO, "Successfully loaded direct session"); + snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); + session->base.drm_fd = -1; session->base.impl = &session_direct; session->base.active = true; wl_signal_init(&session->base.session_signal); diff --git a/session/logind.c b/session/logind.c index 6eb837ed..cfbdb4c7 100644 --- a/session/logind.c +++ b/session/logind.c @@ -26,13 +26,9 @@ struct logind_session { char *id; char *path; - char *seat; - - int drm_fd; }; -static int logind_take_device(struct wlr_session *restrict base, - const char *restrict path) { +static int logind_take_device(struct wlr_session *base, const char *path) { struct logind_session *session = wl_container_of(base, session, base); int ret; @@ -72,7 +68,7 @@ static int logind_take_device(struct wlr_session *restrict base, } if (major(st.st_rdev) == DRM_MAJOR) { - session->drm_fd = fd; + session->base.drm_fd = fd; } error: @@ -102,14 +98,14 @@ static void logind_release_device(struct wlr_session *base, int fd) { } if (major(st.st_rdev) == DRM_MAJOR) { - session->drm_fd = -1; + session->base.drm_fd = -1; } sd_bus_error_free(&error); sd_bus_message_unref(msg); } -static bool logind_change_vt(struct wlr_session *base, int vt) { +static bool logind_change_vt(struct wlr_session *base, unsigned vt) { struct logind_session *session = wl_container_of(base, session, base); int ret; @@ -187,7 +183,6 @@ static void logind_session_finish(struct wlr_session *base) { sd_bus_unref(session->bus); free(session->id); free(session->path); - free(session->seat); free(session); } @@ -242,7 +237,7 @@ static int resume_device(sd_bus_message *msg, void *userdata, sd_bus_error *ret_ } if (major == DRM_MAJOR) { - dup2(fd, session->drm_fd); + dup2(fd, session->base.drm_fd); session->base.active = true; wl_signal_emit(&session->base.session_signal, session); } @@ -305,11 +300,20 @@ static struct wlr_session *logind_session_start(struct wl_display *disp) { goto error; } - ret = sd_session_get_seat(session->id, &session->seat); + ret = sd_session_get_vt(session->id, &session->base.vtnr); + if (ret < 0) { + wlr_log(L_ERROR, "Session not running in virtual terminal"); + goto error; + } + + char *seat; + ret = sd_session_get_seat(session->id, &seat); if (ret < 0) { wlr_log(L_ERROR, "Failed to get seat id: %s", strerror(-ret)); goto error; } + snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); + free(seat); const char *fmt = "/org/freedesktop/login1/session/%s"; int len = snprintf(NULL, 0, fmt, session->id); @@ -345,7 +349,7 @@ static struct wlr_session *logind_session_start(struct wl_display *disp) { wlr_log(L_INFO, "Successfully loaded logind session"); - session->drm_fd = -1; + session->base.drm_fd = -1; session->base.impl = &session_logind; session->base.active = true; wl_signal_init(&session->base.session_signal); @@ -357,7 +361,6 @@ error_bus: error: free(session->path); free(session->id); - free(session->seat); return NULL; } diff --git a/session/session.c b/session/session.c index 9de93b1d..6f6aa2ea 100644 --- a/session/session.c +++ b/session/session.c @@ -33,9 +33,7 @@ void wlr_session_finish(struct wlr_session *session) { session->impl->finish(session); }; -int wlr_session_open_file(struct wlr_session *restrict session, - const char *restrict path) { - +int wlr_session_open_file(struct wlr_session *session, const char *path) { return session->impl->open(session, path); } @@ -43,6 +41,6 @@ void wlr_session_close_file(struct wlr_session *session, int fd) { session->impl->close(session, fd); } -bool wlr_session_change_vt(struct wlr_session *session, int vt) { +bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) { return session->impl->change_vt(session, vt); } From 2f2c8205d841a98649e9697ad5ba8cfe430ffaa7 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Sun, 9 Jul 2017 22:12:50 +1200 Subject: [PATCH 08/11] Moved IPC to its own file. --- include/session/direct-ipc.h | 12 ++ session/CMakeLists.txt | 1 + session/direct-ipc.c | 241 ++++++++++++++++++++++++++++++ session/direct.c | 274 +++++++---------------------------- 4 files changed, 303 insertions(+), 225 deletions(-) create mode 100644 include/session/direct-ipc.h create mode 100644 session/direct-ipc.c diff --git a/include/session/direct-ipc.h b/include/session/direct-ipc.h new file mode 100644 index 00000000..a8d9469d --- /dev/null +++ b/include/session/direct-ipc.h @@ -0,0 +1,12 @@ +#ifndef SESSION_DIRECT_IPC +#define SESSION_DIRECT_IPC + +#include + +int direct_ipc_open(int sock, const char *path); +void direct_ipc_setmaster(int sock); +void direct_ipc_dropmaster(int sock); +void direct_ipc_finish(int sock, pid_t pid); +int direct_ipc_start(pid_t *pid_out); + +#endif diff --git a/session/CMakeLists.txt b/session/CMakeLists.txt index d79991ee..bacd412f 100644 --- a/session/CMakeLists.txt +++ b/session/CMakeLists.txt @@ -6,6 +6,7 @@ include_directories( set(sources session.c direct.c + direct-ipc.c ) set(libs diff --git a/session/direct-ipc.c b/session/direct-ipc.c new file mode 100644 index 00000000..ef8de68c --- /dev/null +++ b/session/direct-ipc.c @@ -0,0 +1,241 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "session/direct-ipc.h" + +enum { DRM_MAJOR = 226 }; + +#ifdef HAS_LIBCAP +#include + +static bool have_permissions(void) { + cap_t cap = cap_get_proc(); + cap_flag_value_t val; + + if (!cap || cap_get_flag(cap, CAP_SYS_ADMIN, CAP_PERMITTED, &val) || val != CAP_SET) { + wlr_log(L_ERROR, "Do not have CAP_SYS_ADMIN; cannot become DRM master"); + cap_free(cap); + return false; + } + + cap_free(cap); + return true; +} +#else +static bool have_permissions(void) { + if (geteuid() != 0) { + wlr_log(L_ERROR, "Do not have root privileges; cannot become DRM master"); + return false; + } + + return true; +} +#endif + +static void send_msg(int sock, int fd, void *buf, size_t buf_len) { + char control[CMSG_SPACE(sizeof(fd))] = {0}; + struct iovec iovec = { .iov_base = buf, .iov_len = buf_len }; + struct msghdr msghdr = {0}; + + if (buf) { + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + } + + if (fd >= 0) { + msghdr.msg_control = &control; + msghdr.msg_controllen = sizeof(control); + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msghdr); + *cmsg = (struct cmsghdr) { + .cmsg_level = SOL_SOCKET, + .cmsg_type = SCM_RIGHTS, + .cmsg_len = CMSG_LEN(sizeof(fd)), + }; + *(int *)CMSG_DATA(cmsg) = fd; + } + + ssize_t ret; + do { + ret = sendmsg(sock, &msghdr, 0); + } while (ret < 0 && errno == EINTR); +} + +static ssize_t recv_msg(int sock, int *fd_out, void *buf, size_t buf_len) { + char control[CMSG_SPACE(sizeof(*fd_out))] = {0}; + struct iovec iovec = { .iov_base = buf, .iov_len = buf_len }; + struct msghdr msghdr = {0}; + + if (buf) { + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + } + + if (fd_out) { + msghdr.msg_control = &control; + msghdr.msg_controllen = sizeof(control); + } + + ssize_t ret; + do { + ret = recvmsg(sock, &msghdr, MSG_CMSG_CLOEXEC); + } while (ret < 0 && errno == EINTR); + + if (fd_out) { + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msghdr); + *fd_out = cmsg ? *(int *)CMSG_DATA(cmsg) : -1; + } + + return ret; +} + +enum msg_type { + MSG_OPEN, + MSG_SETMASTER, + MSG_DROPMASTER, + MSG_END, +}; + +struct msg { + enum msg_type type; + char path[256]; +}; + +static void communicate(int sock) { + struct msg msg; + int drm_fd = -1; + bool running = true; + + while (running && recv_msg(sock, NULL, &msg, sizeof(msg)) >= 0) { + switch (msg.type) { + case MSG_OPEN: + errno = 0; + + // These are the same flags that logind opens files with + int fd = open(msg.path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + int ret = errno; + if (fd == -1) { + goto error; + } + + struct stat st; + if (fstat(fd, &st) < 0) { + ret = errno; + goto error; + } + + uint32_t maj = major(st.st_rdev); + if (maj != INPUT_MAJOR && maj != DRM_MAJOR) { + ret = ENOTSUP; + goto error; + } + + if (maj == DRM_MAJOR) { + if (drmSetMaster(fd)) { + ret = errno; + } else { + drm_fd = fd; + } + } +error: + send_msg(sock, ret ? -1 : fd, &ret, sizeof(ret)); + + if (fd != drm_fd) { + close(fd); + } + + break; + + case MSG_SETMASTER: + drmSetMaster(drm_fd); + send_msg(sock, -1, NULL, 0); + break; + + case MSG_DROPMASTER: + drmDropMaster(drm_fd); + send_msg(sock, -1, NULL, 0); + break; + + case MSG_END: + running = false; + send_msg(sock, -1, NULL, 0); + break; + } + } + + close(drm_fd); + close(sock); +} + +int direct_ipc_open(int sock, const char *path) { + struct msg msg = { .type = MSG_OPEN }; + snprintf(msg.path, sizeof(msg.path), "%s", path); + + send_msg(sock, -1, &msg, sizeof(msg)); + + int fd, err; + recv_msg(sock, &fd, &err, sizeof(err)); + + return err ? -err : fd; +} + +void direct_ipc_setmaster(int sock) { + struct msg msg = { .type = MSG_SETMASTER }; + + send_msg(sock, -1, &msg, sizeof(msg)); + recv_msg(sock, NULL, NULL, 0); +} + +void direct_ipc_dropmaster(int sock) { + struct msg msg = { .type = MSG_DROPMASTER }; + + send_msg(sock, -1, &msg, sizeof(msg)); + recv_msg(sock, NULL, NULL, 0); +} + +void direct_ipc_finish(int sock, pid_t pid) { + struct msg msg = { .type = MSG_END }; + + send_msg(sock, -1, &msg, sizeof(msg)); + recv_msg(sock, NULL, NULL, 0); + + waitpid(pid, NULL, 0); +} + +int direct_ipc_start(pid_t *pid_out) { + if (!have_permissions()) { + return -1; + } + + int sock[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) < 0) { + wlr_log_errno(L_ERROR, "Failed to create socket pair"); + return -1; + } + + pid_t pid = fork(); + if (pid < 0) { + wlr_log_errno(L_ERROR, "Fork failed"); + close(sock[0]); + close(sock[1]); + return -1; + } else if (pid == 0) { + close(sock[0]); + communicate(sock[1]); + _Exit(0); + } + + close(sock[1]); + *pid_out = pid; + return sock[0]; +} diff --git a/session/direct.c b/session/direct.c index 3932af68..23ce1ee1 100644 --- a/session/direct.c +++ b/session/direct.c @@ -3,25 +3,19 @@ #include #include #include -#include -#include #include #include #include #include #include -#include -#include #include #include +#include #include #include -#include #include #include -#ifdef HAS_LIBCAP -#include -#endif +#include "session/direct-ipc.h" enum { DRM_MAJOR = 226 }; @@ -37,82 +31,23 @@ struct direct_session { struct wl_event_source *vt_source; }; -enum session_message_type { - SESSION_OPEN, - SESSION_SETMASTER, - SESSION_DROPMASTER, - SESSION_END, -}; - -struct session_message { - enum session_message_type type; - char path[60]; -}; - -static int send_message(int sock, enum session_message_type type, const char *path) { - struct session_message msg = { - .type = type, - }; - struct msghdr request = { - .msg_iov = &(struct iovec) { - .iov_base = &msg, - .iov_len = sizeof(msg), - }, - .msg_iovlen = 1, - }; - - if (path) { - snprintf(msg.path, sizeof(msg.path), "%s", path); - } - - sendmsg(sock, &request, 0); - - int err = 0, fd = -1; - char control[CMSG_SPACE(sizeof(fd))] = {0}; - struct msghdr reply = { - .msg_iov = &(struct iovec) { - .iov_base = &err, - .iov_len = sizeof(err), - }, - .msg_iovlen = 1, - .msg_control = control, - .msg_controllen = sizeof(control), - }; - - recvmsg(sock, &reply, 0); - - // The other types have no meaningful return value - if (type != SESSION_OPEN) { - return 0; - } - - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&reply); - memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd)); - - return err ? -err : fd; -} - static int direct_session_open(struct wlr_session *base, const char *path) { struct direct_session *session = wl_container_of(base, session, base); - struct stat st; - if (stat(path, &st)) { - return -errno; - } - - uint32_t maj = major(st.st_rdev); - if (maj != DRM_MAJOR && maj != INPUT_MAJOR) { - return -EINVAL; - } - - int fd = send_message(session->sock, SESSION_OPEN, path); + int fd = direct_ipc_open(session->sock, path); if (fd < 0) { wlr_log(L_ERROR, "Failed to open %s: %s%s", path, strerror(-fd), fd == -EINVAL ? "; is another display server running?" : ""); return fd; } - if (maj == DRM_MAJOR) { + struct stat st; + if (fstat(fd, &st) < 0) { + close(fd); + return -errno; + } + + if (major(st.st_rdev) == DRM_MAJOR) { session->base.drm_fd = fd; } @@ -122,9 +57,18 @@ 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); - if (fd == session->base.drm_fd) { - send_message(session->sock, SESSION_DROPMASTER, NULL); + struct stat st; + if (fstat(fd, &st) < 0) { + wlr_log_errno(L_ERROR, "Stat failed"); + close(fd); + return; + } + + if (major(st.st_rdev) == DRM_MAJOR) { + direct_ipc_dropmaster(session->sock); session->base.drm_fd = -1; + } else if (major(st.st_rdev) == INPUT_MAJOR) { + ioctl(fd, EVIOCREVOKE, 0); } close(fd); @@ -151,9 +95,8 @@ static void direct_session_finish(struct wlr_session *base) { wlr_log(L_ERROR, "Failed to restore tty"); } - send_message(session->sock, SESSION_END, NULL); + direct_ipc_finish(session->sock, session->child); close(session->sock); - wait(NULL); wl_event_source_remove(session->vt_source); close(session->tty_fd); @@ -166,11 +109,11 @@ static int vt_handler(int signo, void *data) { if (session->base.active) { session->base.active = false; wl_signal_emit(&session->base.session_signal, session); - send_message(session->sock, SESSION_DROPMASTER, NULL); + direct_ipc_dropmaster(session->sock); ioctl(session->tty_fd, VT_RELDISP, 1); } else { ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ); - send_message(session->sock, SESSION_SETMASTER, NULL); + direct_ipc_setmaster(session->sock); session->base.active = true; wl_signal_emit(&session->base.session_signal, session); } @@ -179,24 +122,22 @@ static int vt_handler(int signo, void *data) { } static bool setup_tty(struct direct_session *session, struct wl_display *display) { - session->tty_fd = dup(STDIN_FILENO); - if (session->tty_fd == -1) { + int fd = dup(STDIN_FILENO); + if (fd == -1) { wlr_log_errno(L_ERROR, "Cannot open tty"); return false; } struct stat st; - if (fstat(session->tty_fd, &st) == -1 || major(st.st_rdev) != TTY_MAJOR || - minor(st.st_rdev) == 0) { + if (fstat(fd, &st) == -1 || major(st.st_rdev) != TTY_MAJOR || minor(st.st_rdev) == 0) { wlr_log(L_ERROR, "Not running from a virtual terminal"); goto error; } int tty = minor(st.st_rdev); - int ret, kd_mode; - session->base.vtnr = tty; + int ret, kd_mode, old_kbmode; - ret = ioctl(session->tty_fd, KDGETMODE, &kd_mode); + ret = ioctl(fd, KDGETMODE, &kd_mode); if (ret) { wlr_log_errno(L_ERROR, "Failed to get tty mode"); goto error; @@ -208,20 +149,20 @@ static bool setup_tty(struct direct_session *session, struct wl_display *display goto error; } - ioctl(session->tty_fd, VT_ACTIVATE, tty); - ioctl(session->tty_fd, VT_WAITACTIVE, tty); + ioctl(fd, VT_ACTIVATE, tty); + ioctl(fd, VT_WAITACTIVE, tty); - if (ioctl(session->tty_fd, KDGKBMODE, &session->old_kbmode)) { + if (ioctl(fd, KDGKBMODE, &old_kbmode)) { wlr_log_errno(L_ERROR, "Failed to read keyboard mode"); goto error; } - if (ioctl(session->tty_fd, KDSKBMODE, K_OFF)) { + if (ioctl(fd, KDSKBMODE, K_OFF)) { wlr_log_errno(L_ERROR, "Failed to set keyboard mode"); goto error; } - if (ioctl(session->tty_fd, KDSETMODE, KD_GRAPHICS)) { + if (ioctl(fd, KDSETMODE, KD_GRAPHICS)) { wlr_log_errno(L_ERROR, "Failed to set graphics mode on tty"); goto error; } @@ -232,7 +173,7 @@ static bool setup_tty(struct direct_session *session, struct wl_display *display .acqsig = SIGUSR1, }; - if (ioctl(session->tty_fd, VT_SETMODE, &mode) < 0) { + if (ioctl(fd, VT_SETMODE, &mode) < 0) { wlr_log(L_ERROR, "Failed to take control of tty"); goto error; } @@ -244,145 +185,31 @@ static bool setup_tty(struct direct_session *session, struct wl_display *display goto error; } + session->base.vtnr = tty; + session->tty_fd = fd; + session->old_kbmode = old_kbmode; + return true; error: - close(session->tty_fd); + close(fd); return false; } -static void communicate(int sock) { - struct session_message msg; - struct msghdr hdr = { - .msg_iov = &(struct iovec) { - .iov_base = &msg, - .iov_len = sizeof(msg), - }, - .msg_iovlen = 1, - }; - - int drm_fd = -1; - - while (recvmsg(sock, &hdr, 0) >= 0 || errno == EINTR) { - switch (msg.type) { - case SESSION_OPEN: - errno = 0; - // These are the flags that logind use - int fd = open(msg.path, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); - int e = errno; - - struct stat st; - if (fstat(fd, &st) >= 0 && major(st.st_rdev) == DRM_MAJOR) { - if (drmSetMaster(fd)) { - close(fd); - fd = -1; - e = errno; - } - - drm_fd = fd; - } - - char control[CMSG_SPACE(sizeof(fd))] = {0}; - struct msghdr reply = { - .msg_iov = &(struct iovec) { - .iov_base = &e, - .iov_len = sizeof(e), - }, - .msg_iovlen = 1, - .msg_control = &control, - .msg_controllen = sizeof(control), - }; - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&reply); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); - memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); - - sendmsg(sock, &reply, 0); - break; - case SESSION_SETMASTER: - if (drm_fd != -1) { - drmSetMaster(drm_fd); - } - - sendmsg(sock, &(struct msghdr){0}, 0); - break; - case SESSION_DROPMASTER: - if (drm_fd != -1) { - drmDropMaster(drm_fd); - } - - sendmsg(sock, &(struct msghdr){0}, 0); - break; - case SESSION_END: - sendmsg(sock, &(struct msghdr){0}, 0); - return; - } - } -} - -#ifdef HAS_LIBCAP -static bool have_permissions(void) { - cap_t cap = cap_get_proc(); - cap_flag_value_t val; - - if (!cap || cap_get_flag(cap, CAP_SYS_ADMIN, CAP_PERMITTED, &val) || val != CAP_SET) { - wlr_log(L_ERROR, "Do not have CAP_SYS_ADMIN; cannot become DRM master"); - cap_free(cap); - return false; - } - - cap_free(cap); - return true; -} -#else -static bool have_permissions(void) { - if (geteuid() != 0) { - wlr_log(L_ERROR, "Do not have root privileges; cannot become DRM master"); - return false; - } - - return true; -} -#endif - static struct wlr_session *direct_session_start(struct wl_display *disp) { - if (!have_permissions()) { - return NULL; - } - - int sock[2]; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) < 0) { - wlr_log_errno(L_ERROR, "Failed to create socket pair"); - return NULL; - } - - pid_t pid = fork(); - if (pid < 0) { - wlr_log_errno(L_ERROR, "Fork failed"); - goto error_sock; - } else if (pid == 0) { - close(sock[0]); - - communicate(sock[1]); - - _Exit(0); - } - - close(sock[1]); - sock[1] = -1; - struct direct_session *session = calloc(1, sizeof(*session)); if (!session) { wlr_log_errno(L_ERROR, "Allocation failed"); - goto error_child; + return NULL; } - session->child = pid; - session->sock = sock[0]; + session->sock = direct_ipc_start(&session->child); + if (session->sock == -1) { + goto error_session; + } if (!setup_tty(session, disp)) { - goto error_session; + goto error_ipc; } // XXX: Is it okay to trust the environment like this? @@ -400,14 +227,11 @@ static struct wlr_session *direct_session_start(struct wl_display *disp) { wl_signal_init(&session->base.session_signal); return &session->base; +error_ipc: + direct_ipc_finish(session->sock, session->child); + close(session->sock); error_session: free(session); -error_child: - send_message(sock[0], SESSION_END, NULL); - wait(NULL); -error_sock: - close(sock[0]); - close(sock[1]); return NULL; } From 3779ef802ddf89067e1588077ed8c72b4674565a Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Sun, 9 Jul 2017 22:23:54 +1200 Subject: [PATCH 09/11] Stop remembering the drm fd in child. --- include/session/direct-ipc.h | 4 ++-- session/direct-ipc.c | 26 ++++++++++---------------- session/direct.c | 6 +++--- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/include/session/direct-ipc.h b/include/session/direct-ipc.h index a8d9469d..96504f04 100644 --- a/include/session/direct-ipc.h +++ b/include/session/direct-ipc.h @@ -4,8 +4,8 @@ #include int direct_ipc_open(int sock, const char *path); -void direct_ipc_setmaster(int sock); -void direct_ipc_dropmaster(int sock); +void direct_ipc_setmaster(int sock, int fd); +void direct_ipc_dropmaster(int sock, int fd); void direct_ipc_finish(int sock, pid_t pid); int direct_ipc_start(pid_t *pid_out); diff --git a/session/direct-ipc.c b/session/direct-ipc.c index ef8de68c..fbbde02a 100644 --- a/session/direct-ipc.c +++ b/session/direct-ipc.c @@ -116,7 +116,7 @@ static void communicate(int sock) { int drm_fd = -1; bool running = true; - while (running && recv_msg(sock, NULL, &msg, sizeof(msg)) >= 0) { + while (running && recv_msg(sock, &drm_fd, &msg, sizeof(msg)) >= 0) { switch (msg.type) { case MSG_OPEN: errno = 0; @@ -140,29 +140,24 @@ static void communicate(int sock) { goto error; } - if (maj == DRM_MAJOR) { - if (drmSetMaster(fd)) { - ret = errno; - } else { - drm_fd = fd; - } + if (maj == DRM_MAJOR && drmSetMaster(fd)) { + ret = errno; } error: send_msg(sock, ret ? -1 : fd, &ret, sizeof(ret)); - - if (fd != drm_fd) { - close(fd); - } + close(fd); break; case MSG_SETMASTER: drmSetMaster(drm_fd); + close(drm_fd); send_msg(sock, -1, NULL, 0); break; case MSG_DROPMASTER: drmDropMaster(drm_fd); + close(drm_fd); send_msg(sock, -1, NULL, 0); break; @@ -173,7 +168,6 @@ error: } } - close(drm_fd); close(sock); } @@ -189,17 +183,17 @@ int direct_ipc_open(int sock, const char *path) { return err ? -err : fd; } -void direct_ipc_setmaster(int sock) { +void direct_ipc_setmaster(int sock, int fd) { struct msg msg = { .type = MSG_SETMASTER }; - send_msg(sock, -1, &msg, sizeof(msg)); + send_msg(sock, fd, &msg, sizeof(msg)); recv_msg(sock, NULL, NULL, 0); } -void direct_ipc_dropmaster(int sock) { +void direct_ipc_dropmaster(int sock, int fd) { struct msg msg = { .type = MSG_DROPMASTER }; - send_msg(sock, -1, &msg, sizeof(msg)); + send_msg(sock, fd, &msg, sizeof(msg)); recv_msg(sock, NULL, NULL, 0); } diff --git a/session/direct.c b/session/direct.c index 23ce1ee1..1247663b 100644 --- a/session/direct.c +++ b/session/direct.c @@ -65,7 +65,7 @@ static void direct_session_close(struct wlr_session *base, int fd) { } if (major(st.st_rdev) == DRM_MAJOR) { - direct_ipc_dropmaster(session->sock); + direct_ipc_dropmaster(session->sock, session->base.drm_fd); session->base.drm_fd = -1; } else if (major(st.st_rdev) == INPUT_MAJOR) { ioctl(fd, EVIOCREVOKE, 0); @@ -109,11 +109,11 @@ static int vt_handler(int signo, void *data) { if (session->base.active) { session->base.active = false; wl_signal_emit(&session->base.session_signal, session); - direct_ipc_dropmaster(session->sock); + direct_ipc_dropmaster(session->sock, session->base.drm_fd); ioctl(session->tty_fd, VT_RELDISP, 1); } else { ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ); - direct_ipc_setmaster(session->sock); + direct_ipc_setmaster(session->sock, session->base.drm_fd); session->base.active = true; wl_signal_emit(&session->base.session_signal, session); } From 5b8ec107b9ca91ef8d81ce4e561605b808b6a03e Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Sun, 9 Jul 2017 23:02:41 +1200 Subject: [PATCH 10/11] Session documentation. --- include/wlr/session.h | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/include/wlr/session.h b/include/wlr/session.h index 52dbf8ca..7961e620 100644 --- a/include/wlr/session.h +++ b/include/wlr/session.h @@ -9,6 +9,10 @@ struct session_impl; struct wlr_session { const struct session_impl *impl; + /* + * Signal for when the session becomes active/inactive. + * It's called when we swap virtual terminal. + */ struct wl_signal session_signal; bool active; @@ -17,10 +21,45 @@ struct wlr_session { char seat[8]; }; +/* + * Opens a session, taking control of the current virtual terminal. + * This should not be called if another program is already in control + * of the terminal (Xorg, another Wayland compositor, etc.). + * + * If logind support is not enabled, you must have CAP_SYS_ADMIN or be root. + * It is safe to drop priviledges after this is called. + * + * Returns NULL on error. + */ struct wlr_session *wlr_session_start(struct wl_display *disp); + +/* + * Closes a previously opened session and restores the virtual terminal. + * You should call wlr_session_close_file on each files you opened + * with wlr_session_open_file before you call this. + */ void wlr_session_finish(struct wlr_session *session); + +/* + * Opens the file at path. + * This can only be used to open DRM or evdev (input) devices. + * + * When the session becomes inactive: + * - DRM files lose their DRM master status + * - evdev files become invalid and should be closed + * + * Returns -errno on error. + */ int wlr_session_open_file(struct wlr_session *session, const char *path); + +/* + * Closes a file previously opened with wlr_session_open_file. + */ void wlr_session_close_file(struct wlr_session *session, int fd); + +/* + * Changes the virtual terminal. + */ bool wlr_session_change_vt(struct wlr_session *session, unsigned vt); #endif From ceef12cb5a2a9347a1494332d320742c92fbd539 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Mon, 10 Jul 2017 13:46:15 +1200 Subject: [PATCH 11/11] Exit cleanly if we cannot open Wayland socket. --- examples/shared.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/examples/shared.c b/examples/shared.c index fbb6c559..6d225a85 100644 --- a/examples/shared.c +++ b/examples/shared.c @@ -466,6 +466,7 @@ void compositor_init(struct compositor_state *state) { struct wlr_backend *wlr = wlr_backend_autocreate( state->display, state->session); if (!wlr) { + wlr_session_finish(state->session); exit(1); } wl_signal_add(&wlr->events.input_add, &state->input_add); @@ -477,10 +478,19 @@ void compositor_init(struct compositor_state *state) { clock_gettime(CLOCK_MONOTONIC, &state->last_frame); const char *socket = wl_display_add_socket_auto(state->display); + if (!socket) { + wlr_log_errno(L_ERROR, "Unable to open wayland socket"); + wlr_backend_destroy(wlr); + wlr_session_finish(state->session); + exit(1); + } + wlr_log(L_INFO, "Running compositor on wayland display '%s'", socket); setenv("_WAYLAND_DISPLAY", socket, true); if (!wlr_backend_init(state->backend)) { wlr_log(L_ERROR, "Failed to initialize backend"); + wlr_backend_destroy(wlr); + wlr_session_finish(state->session); exit(1); } }