mirror of
https://github.com/hyprwm/wlroots-hyprland.git
synced 2024-11-30 08:25:58 +01:00
fa05d3cde6
When starting a compositor that's using the "direct" session backend, wlroots needs to handle calls to `drmSetMaster()` and `drmDropMaster()`. As both calls used to require `CAP_SYS_ADMIN`, wlroots thus simply refused starting in case the process doesn't enjoy evelated privileges. Permission rules have changed since linux.git commit 45bc3d26c95a (drm: rework SET_MASTER and DROP_MASTER perm handling, 2020-03-19). As a result, starting with Linux v5.8, both ioctls will now also succeed if the process is currently or has been the DRM master. And as the first process to open render nodes will become the DRM master automatically, this effectively means that process elevation is not strictly required in all setups anymore. So let's drop the `geteuid() != 0` permission check to allow those new rules to do their magic.
237 lines
4.6 KiB
C
237 lines
4.6 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
#ifdef __FreeBSD__
|
|
#define __BSD_VISIBLE 1
|
|
#include <linux/input.h>
|
|
#endif
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
#include <wlr/config.h>
|
|
#include <wlr/util/log.h>
|
|
#include <xf86drm.h>
|
|
#ifdef __linux__
|
|
#include <sys/sysmacros.h>
|
|
#include <linux/major.h>
|
|
#endif
|
|
#include "backend/session/direct-ipc.h"
|
|
|
|
enum { DRM_MAJOR = 226 };
|
|
|
|
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)),
|
|
};
|
|
memcpy(CMSG_DATA(cmsg), &fd, sizeof(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);
|
|
if (cmsg) {
|
|
memcpy(fd_out, CMSG_DATA(cmsg), sizeof(*fd_out));
|
|
} else {
|
|
*fd_out = -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, &drm_fd, &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;
|
|
}
|
|
|
|
#ifndef __FreeBSD__
|
|
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 && drmSetMaster(fd)) {
|
|
ret = errno;
|
|
}
|
|
#else
|
|
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 (dv.version_major != 0 && drmSetMaster(fd)) {
|
|
ret = errno;
|
|
}
|
|
#endif
|
|
|
|
error:
|
|
send_msg(sock, ret ? -1 : fd, &ret, sizeof(ret));
|
|
if (fd >= 0) {
|
|
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;
|
|
|
|
case MSG_END:
|
|
running = false;
|
|
send_msg(sock, -1, NULL, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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, ret;
|
|
int retry = 0;
|
|
do {
|
|
ret = recv_msg(sock, &fd, &err, sizeof(err));
|
|
} while (ret == 0 && retry++ < 3);
|
|
|
|
return err ? -err : fd;
|
|
}
|
|
|
|
void direct_ipc_setmaster(int sock, int fd) {
|
|
struct msg msg = { .type = MSG_SETMASTER };
|
|
|
|
send_msg(sock, fd, &msg, sizeof(msg));
|
|
recv_msg(sock, NULL, NULL, 0);
|
|
}
|
|
|
|
void direct_ipc_dropmaster(int sock, int fd) {
|
|
struct msg msg = { .type = MSG_DROPMASTER };
|
|
|
|
send_msg(sock, fd, &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_init(pid_t *pid_out) {
|
|
int sock[2];
|
|
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) < 0) {
|
|
wlr_log_errno(WLR_ERROR, "Failed to create socket pair");
|
|
return -1;
|
|
}
|
|
|
|
pid_t pid = fork();
|
|
if (pid < 0) {
|
|
wlr_log_errno(WLR_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];
|
|
}
|