~kennylevinsen/seatd-devel

Initial netbsd support v1 APPLIED

illiliti: 6
 Initial netbsd support
 ci: Add NetBSD
 common/evdev: Decouple os-specific functions
 common/terminal: Decouple os-specific functions
 common/drm: Decouple os-specific functions
 Rename evdev to input

 36 files changed, 501 insertions(+), 305 deletions(-)
Implementation and CI part of this patch merged, thanks!
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~kennylevinsen/seatd-devel/patches/29975/mbox | git am -3
Learn more about email & git

[PATCH 1/6] Initial netbsd support Export this patch

---
 common/drm.c      | 15 +++++++++++++++
 common/evdev.c    | 23 +++++++++++++++++++++++
 common/terminal.c | 18 ++++++++++++++++--
 include/drm.h     |  2 +-
 include/evdev.h   |  2 +-
 meson.build       |  1 +
 seatd/client.c    | 27 +++++++++++++++++++++++++++
 7 files changed, 84 insertions(+), 4 deletions(-)

diff --git a/common/drm.c b/common/drm.c
index 9591dc0..0d8096a 100644
--- a/common/drm.c
+++ b/common/drm.c
@@ -6,6 +6,11 @@
#include <sys/sysmacros.h>
#endif

#if defined(__NetBSD__)
#include <stdlib.h>
#include <sys/stat.h>
#endif

#include "drm.h"

// From libdrm
@@ -40,6 +45,16 @@ int path_is_drm(const char *path) {
	static const int prefixlen = STRLEN(prefix);
	return strncmp(prefix, path, prefixlen) == 0;
}
#elif defined(__NetBSD__)
int path_is_drm(const char *path) {
	static const char prefix[] = "/dev/dri/";
	static const int prefixlen = STRLEN(prefix);
	return strncmp(prefix, path, prefixlen) == 0;
}

int dev_is_drm(dev_t device) {
	return major(device) == getdevmajor("drm", S_IFCHR);
}
#else
#error Unsupported platform
#endif
diff --git a/common/evdev.c b/common/evdev.c
index 4aff9bc..40acf29 100644
--- a/common/evdev.c
+++ b/common/evdev.c
@@ -9,6 +9,9 @@
#include <sys/sysmacros.h>
#elif defined(__FreeBSD__)
#include <dev/evdev/input.h>
#elif defined(__NetBSD__)
#include <stdlib.h>
#include <sys/stat.h>
#else
#error Unsupported platform
#endif
@@ -17,6 +20,7 @@

#define STRLEN(s) ((sizeof(s) / sizeof(s[0])) - 1)

#if defined(__linux__) || defined(__FreeBSD__)
int path_is_evdev(const char *path) {
	static const char prefix[] = "/dev/input/event";
	static const size_t prefixlen = STRLEN(prefix);
@@ -26,9 +30,28 @@ int path_is_evdev(const char *path) {
int evdev_revoke(int fd) {
	return ioctl(fd, EVIOCREVOKE, NULL);
}
#endif

#if defined(__linux__)
int dev_is_evdev(dev_t device) {
	return major(device) == INPUT_MAJOR;
}
#elif defined(__NetBSD__)
int dev_is_evdev(dev_t device) {
	return major(device) == getdevmajor("wskbd", S_IFCHR) ||
		major(device) == getdevmajor("wsmouse", S_IFCHR) ||
		major(device) == getdevmajor("wsmux", S_IFCHR);
}
int path_is_evdev(const char *path) {
	const char *wskbd = "/dev/wskbd";
	const char *wsmouse = "/dev/wsmouse";
	const char *wsmux = "/dev/wsmux";
	return strncmp(path, wskbd, STRLEN(wskbd)) == 0 ||
		strncmp(path, wsmouse, STRLEN(wsmouse)) == 0 ||
		strncmp(path, wsmux, STRLEN(wsmouse)) == 0;
}
int evdev_revoke(int fd) {
	(void)fd;
	return 0;
}
#endif
diff --git a/common/terminal.c b/common/terminal.c
index 0c3466f..183c9bd 100644
--- a/common/terminal.c
+++ b/common/terminal.c
@@ -21,6 +21,11 @@
#define K_ENABLE  K_XLATE
#define K_DISABLE K_RAW
#define FRSIG     SIGIO
#elif defined(__NetBSD__)
#include <dev/wscons/wsdisplay_usl_io.h>
#define K_ENABLE  K_XLATE
#define K_DISABLE K_RAW
#define FRSIG     0 // unimplemented
#else
#error Unsupported platform
#endif
@@ -134,6 +139,14 @@ static int get_tty_path(int tty, char path[static TTYPATHLEN]) {
	}
	return 0;
}
#elif defined(__NetBSD__)
static int get_tty_path(int tty, char path[static TTYPATHLEN]) {
	assert(tty >= 0);
	if (snprintf(path, TTYPATHLEN, "/dev/ttyE%d", tty) == -1) {
		return -1;
	}
	return 0;
}
#else
#error Unsupported platform
#endif
@@ -153,7 +166,7 @@ int terminal_open(int vt) {
}

int terminal_current_vt(int fd) {
#if defined(__linux__)
#if defined(__linux__) || defined(__NetBSD__)
	struct vt_stat st;
	int res = ioctl(fd, VT_GETSTATE, &st);
	close(fd);
@@ -231,12 +244,13 @@ int terminal_ack_acquire(int fd) {

int terminal_set_keyboard(int fd, bool enable) {
	log_debugf("Setting KD keyboard state to %d", enable);
#if defined(__linux__) || defined(__NetBSD__)
	if (ioctl(fd, KDSKBMODE, enable ? K_ENABLE : K_DISABLE) == -1) {
		log_errorf("Could not set KD keyboard mode to %s: %s",
			   enable ? "enabled" : "disabled", strerror(errno));
		return -1;
	}
#if defined(__FreeBSD__)
#elif defined(__FreeBSD__)
	struct termios tios;
	if (tcgetattr(fd, &tios) == -1) {
		log_errorf("Could not set get terminal mode: %s", strerror(errno));
diff --git a/include/drm.h b/include/drm.h
index 8a7fb10..c8c3eeb 100644
--- a/include/drm.h
+++ b/include/drm.h
@@ -5,7 +5,7 @@ int drm_set_master(int fd);
int drm_drop_master(int fd);
int path_is_drm(const char *path);

#if defined(__linux__)
#if defined(__linux__) || defined(__NetBSD__)
#include <sys/types.h>
int dev_is_drm(dev_t device);
#endif
diff --git a/include/evdev.h b/include/evdev.h
index 6ebd943..da57828 100644
--- a/include/evdev.h
+++ b/include/evdev.h
@@ -4,7 +4,7 @@
int evdev_revoke(int fd);
int path_is_evdev(const char *path);

#if defined(__linux__)
#if defined(__linux__) || defined(__NetBSD__)
#include <sys/types.h>
int dev_is_evdev(dev_t device);
#endif
diff --git a/meson.build b/meson.build
index e6e583d..0f267a6 100644
--- a/meson.build
+++ b/meson.build
@@ -30,6 +30,7 @@ add_project_arguments(
	[
		'-D_XOPEN_SOURCE=700',
		'-D__BSD_VISIBLE',
		'-D_NETBSD_SOURCE',
		'-DSEATD_VERSION="@0@"'.format(meson.project_version()),
		'-DSEATD_DEFAULTPATH="@0@"'.format(defaultpath),
		'-DSEATD_INSTALLPATH="@0@"'.format(seatdpath),
diff --git a/seatd/client.c b/seatd/client.c
index 6b6f3b3..0097792 100644
--- a/seatd/client.c
+++ b/seatd/client.c
@@ -14,6 +14,10 @@
#include <sys/un.h>
#endif

#if defined(__NetBSD__)
#include <sys/un.h>
#endif

#include "client.h"
#include "linked_list.h"
#include "log.h"
@@ -34,6 +38,23 @@ static int get_peer(int fd, pid_t *pid, uid_t *uid, gid_t *gid) {
	*uid = cred.uid;
	*gid = cred.gid;
	return 0;
#elif defined(__NetBSD__)
	struct unpcbid cred;
	socklen_t len = sizeof cred;
	if (getsockopt(fd, 0, LOCAL_PEEREID, &cred, &len) == -1) {
		// assume builtin backend
		if (errno == EINVAL) {
			*pid = getpid();
			*uid = getuid();
			*gid = getgid();
			return 0;
		}
		return -1;
	}
	*pid = cred.unp_pid;
	*uid = cred.unp_euid;
	*gid = cred.unp_egid;
	return 0;
#elif defined(__FreeBSD__)
	struct xucred cred;
	socklen_t len = sizeof cred;
@@ -468,7 +489,13 @@ int client_handle_connection(int fd, uint32_t mask, void *data) {
			goto fail;
		}
		if (len == 0) {
// https://man.netbsd.org/poll.2
// Sockets produce POLLIN rather than POLLHUP when the remote end is closed.
#if defined(__NetBSD__)
			log_info("Client disconnected");
#else
			log_error("Could not read client connection: zero-length read");
#endif
			goto fail;
		}

-- 
2.35.1
Would you mind if I started out by merging implementation + CI, leaving 
the reorganization part for later?

[PATCH 2/6] ci: Add NetBSD Export this patch

---
 .builds/netbsd.yml | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 .builds/netbsd.yml

diff --git a/.builds/netbsd.yml b/.builds/netbsd.yml
new file mode 100644
index 0000000..a676138
--- /dev/null
+++ b/.builds/netbsd.yml
@@ -0,0 +1,24 @@
image: netbsd/latest
packages:
   - meson
sources:
   - https://git.sr.ht/~kennylevinsen/seatd
tasks:
   - wscons: |
      echo 'wscons=YES' | sudo tee -a /etc/rc.conf
      sudo /etc/rc.d/wscons start
      sudo /etc/rc.d/ttys restart
   - prepare: |
      meson -Dlibseat-seatd=enabled -Dlibseat-builtin=enabled -Dlibseat-logind=disabled build seatd
   - build: |
      ninja -C build
   - unittest: |
      ninja -C build test
   - smoketest: |
      rm -rf build
      meson -Db_lundef=false -Db_sanitize=address -Dlibseat-seatd=enabled -Dlibseat-builtin=enabled -Dexamples=enabled -Dlibseat-logind=disabled build seatd
      ninja -C build
      sudo ninja -C build install
      timeout -s SIGKILL 30s sudo SEATD_LOGLEVEL=debug ./build/seatd-launch ./build/simpletest /dev/wskbd
   - smoketest-builtin: |
      timeout -s SIGKILL 30s sudo LIBSEAT_BACKEND=builtin ./build/simpletest /dev/wskbd
-- 
2.35.1

[PATCH 3/6] common/evdev: Decouple os-specific functions Export this patch

---
 common/evdev.c         | 55 ++++++++----------------------------------
 common/evdev_freebsd.c | 18 ++++++++++++++
 common/evdev_linux.c   | 18 ++++++++++++++
 common/evdev_netbsd.c  | 15 ++++++++++++
 include/evdev.h        |  6 ++---
 meson.build            | 10 ++++++++
 6 files changed, 73 insertions(+), 49 deletions(-)
 create mode 100644 common/evdev_freebsd.c
 create mode 100644 common/evdev_linux.c
 create mode 100644 common/evdev_netbsd.c

diff --git a/common/evdev.c b/common/evdev.c
index 40acf29..51d3a7f 100644
--- a/common/evdev.c
+++ b/common/evdev.c
@@ -1,57 +1,22 @@
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <stddef.h>

#if defined(__linux__)
#include <linux/input.h>
#include <linux/major.h>
#include <sys/sysmacros.h>
#elif defined(__FreeBSD__)
#include <dev/evdev/input.h>
#elif defined(__NetBSD__)
#include <stdlib.h>
#include <sys/stat.h>
#if defined(__linux__) || defined(__FreeBSD__)
static const char *inputs[] = { "/dev/input/event", NULL };
#elif defined(__OpenBSD__) || defined(__NetBSD__)
static const char *inputs[] = { "/dev/wskbd", "/dev/wsmux", "/dev/wsmouse", NULL };
#else
#error Unsupported platform
#endif

#include "evdev.h"

#define STRLEN(s) ((sizeof(s) / sizeof(s[0])) - 1)

#if defined(__linux__) || defined(__FreeBSD__)
int path_is_evdev(const char *path) {
	static const char prefix[] = "/dev/input/event";
	static const size_t prefixlen = STRLEN(prefix);
	return strncmp(prefix, path, prefixlen) == 0;
}

int evdev_revoke(int fd) {
	return ioctl(fd, EVIOCREVOKE, NULL);
}
#endif
	for (size_t i = 0; inputs[i]; i++) {
		if (strncmp(path, inputs[i], strlen(inputs[i])) == 0) {
			return 1;
		}
	}

#if defined(__linux__)
int dev_is_evdev(dev_t device) {
	return major(device) == INPUT_MAJOR;
}
#elif defined(__NetBSD__)
int dev_is_evdev(dev_t device) {
	return major(device) == getdevmajor("wskbd", S_IFCHR) ||
		major(device) == getdevmajor("wsmouse", S_IFCHR) ||
		major(device) == getdevmajor("wsmux", S_IFCHR);
}
int path_is_evdev(const char *path) {
	const char *wskbd = "/dev/wskbd";
	const char *wsmouse = "/dev/wsmouse";
	const char *wsmux = "/dev/wsmux";
	return strncmp(path, wskbd, STRLEN(wskbd)) == 0 ||
		strncmp(path, wsmouse, STRLEN(wsmouse)) == 0 ||
		strncmp(path, wsmux, STRLEN(wsmouse)) == 0;
}
int evdev_revoke(int fd) {
	(void)fd;
	return 0;
}
#endif
diff --git a/common/evdev_freebsd.c b/common/evdev_freebsd.c
new file mode 100644
index 0000000..c2dcd02
--- /dev/null
+++ b/common/evdev_freebsd.c
@@ -0,0 +1,18 @@
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <dev/evdev/input.h>

#include "evdev.h"

int evdev_revoke(int fd) {
	return ioctl(fd, EVIOCREVOKE, NULL);
}

int dev_is_evdev(dev_t device) {
	char devname[SPECNAMELEN];
	// devname_r always succeds
	devname_r(device, S_IFCHR, devname ,sizeof(devname));
	return strncmp(devname, "event/", 6) == 0;
}
diff --git a/common/evdev_linux.c b/common/evdev_linux.c
new file mode 100644
index 0000000..2c4a15b
--- /dev/null
+++ b/common/evdev_linux.c
@@ -0,0 +1,18 @@
#include <stddef.h>
#include <linux/input.h>
#include <linux/major.h>
#include <sys/sysmacros.h>

#include "evdev.h"

#ifndef INPUT_MAJOR
#define INPUT_MAJOR 13
#endif

int evdev_revoke(int fd) {
	return ioctl(fd, EVIOCREVOKE, NULL);
}

int dev_is_evdev(dev_t device) {
	return major(device) == INPUT_MAJOR;
}
diff --git a/common/evdev_netbsd.c b/common/evdev_netbsd.c
new file mode 100644
index 0000000..f98298a
--- /dev/null
+++ b/common/evdev_netbsd.c
@@ -0,0 +1,15 @@
#include <stdlib.h>
#include <sys/stat.h>

#include "evdev.h"

int evdev_revoke(int fd) {
	(void)fd;
	return 0;
}

int dev_is_evdev(dev_t device) {
	return major(device) == getdevmajor("wskbd", S_IFCHR) ||
		major(device) == getdevmajor("wsmouse", S_IFCHR) ||
		major(device) == getdevmajor("wsmux", S_IFCHR);
}
diff --git a/include/evdev.h b/include/evdev.h
index da57828..d9eb503 100644
--- a/include/evdev.h
+++ b/include/evdev.h
@@ -1,12 +1,10 @@
#include <sys/types.h>

#ifndef _SEATD_EVDEV_H
#define _SEATD_EVDEV_H

int evdev_revoke(int fd);
int path_is_evdev(const char *path);

#if defined(__linux__) || defined(__NetBSD__)
#include <sys/types.h>
int dev_is_evdev(dev_t device);
#endif

#endif
diff --git a/meson.build b/meson.build
index 0f267a6..21f1095 100644
--- a/meson.build
+++ b/meson.build
@@ -120,6 +120,16 @@ server_files = [
	'seatd/server.c',
]

if host_machine.system() == 'netbsd'
  server_files += 'common/evdev_netbsd.c'
elif host_machine.system() == 'linux'
  server_files += 'common/evdev_linux.c'
elif host_machine.system() == 'freebsd'
  server_files += 'common/evdev_freebsd.c'
else
  error('Unsupported platform')
endif

with_seatd = get_option('libseat-seatd') == 'enabled'
with_builtin = get_option('libseat-builtin') == 'enabled'
with_server = get_option('server') == 'enabled'
-- 
2.35.1

[PATCH 4/6] common/terminal: Decouple os-specific functions Export this patch

---
 common/terminal.c         | 180 +-------------------------------------
 common/terminal_freebsd.c | 129 +++++++++++++++++++++++++++
 common/terminal_linux.c   |  33 +++++++
 common/terminal_netbsd.c  |  33 +++++++
 include/terminal.h        |  29 ++++++
 meson.build               |   3 +
 6 files changed, 231 insertions(+), 176 deletions(-)
 create mode 100644 common/terminal_freebsd.c
 create mode 100644 common/terminal_linux.c
 create mode 100644 common/terminal_netbsd.c

diff --git a/common/terminal.c b/common/terminal.c
index 183c9bd..c1998c0 100644
--- a/common/terminal.c
+++ b/common/terminal.c
@@ -1,156 +1,14 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>

#if defined(__linux__)
#include <linux/kd.h>
#include <linux/vt.h>
#define K_ENABLE  K_UNICODE
#define K_DISABLE K_OFF
#define FRSIG     0
#elif defined(__FreeBSD__)
#include <sys/consio.h>
#include <sys/kbio.h>
#include <termios.h>
#define K_ENABLE  K_XLATE
#define K_DISABLE K_RAW
#define FRSIG     SIGIO
#elif defined(__NetBSD__)
#include <dev/wscons/wsdisplay_usl_io.h>
#define K_ENABLE  K_XLATE
#define K_DISABLE K_RAW
#define FRSIG     0 // unimplemented
#else
#error Unsupported platform
#endif
#include <limits.h>

#include "log.h"
#include "terminal.h"

#define TTYPATHLEN 16

#if defined(__FreeBSD__)
static int get_tty_path(int tty, char path[static TTYPATHLEN]) {
	assert(tty >= 0);

	const char prefix[] = "/dev/ttyv";
	const size_t prefix_len = sizeof(prefix) - 1;
	strcpy(path, prefix);

	// The FreeBSD VT_GETACTIVE is implemented in the kernel as follows:
	//
	//	static int
	//	vtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data,
	// 		struct thread *td)
	//	{
	//		struct vt_window *vw = tm->tm_softc;
	//		struct vt_device *vd = vw->vw_device;
	//		...
	//		switch (cmd) {
	//		...
	//		case VT_GETACTIVE:
	//			*(int *)data = vd->vd_curwindow->vw_number + 1;
	//			return (0);
	//		...
	//		}
	//		...
	//	}
	//
	// The side-effect here being that the returned VT number is one
	// greater than the internal VT number. The internal number is what is
	// used to number the TTY device, while the external number is what we
	// use in e.g. VT switching.
	//
	// We subtract one from the requested TTY number to compensate. If the
	// user asked for TTY 0 (which is special on Linux), we just give them
	// the first tty.

	if (tty > 0) {
		tty--;
	}

	// The FreeBSD tty name is constructed in the kernel as follows:
	//
	//	static void
	//	vtterm_cnprobe(struct terminal *tm, struct consdev *cp)
	//	{
	//		...
	//		struct vt_window *vw = tm->tm_softc;
	//		...
	//		sprintf(cp->cn_name, "ttyv%r", VT_UNIT(vw));
	//		...
	//	}
	//
	// With %r being a FreeBSD-internal radix formatter (seemingly set to
	// base 32), and VT_UNIT expanding to the following to extract the
	// internal VT number (which is one less than the external VT number):
	//
	//	((vw)->vw_device->vd_unit * VT_MAXWINDOWS + (vw)->vw_number)
	//
	// As the %r formatter is kernel-internal, we implement the base 32
	// encoding ourselves below.

	size_t offset = prefix_len;
	if (tty == 0) {
		path[offset++] = '0';
		path[offset++] = '\0';
		return 0;
	}

	const int base = 32;
	for (int remaining = tty; remaining > 0; remaining /= base, offset++) {
		// Return early if the buffer is too small.
		if (offset + 1 >= TTYPATHLEN) {
			errno = ENOMEM;
			return -1;
		}

		const int value = remaining % base;
		if (value >= 10) {
			path[offset] = 'a' + value - 10;
		} else {
			path[offset] = '0' + value;
		}
	}

	const size_t num_len = offset - prefix_len;
	for (size_t i = 0; i < num_len / 2; i++) {
		const size_t p1 = prefix_len + i;
		const size_t p2 = offset - 1 - i;
		const char tmp = path[p1];
		path[p1] = path[p2];
		path[p2] = tmp;
	}

	path[offset++] = '\0';
	return 0;
}
#elif defined(__linux__)
static int get_tty_path(int tty, char path[static TTYPATHLEN]) {
	assert(tty >= 0);
	if (snprintf(path, TTYPATHLEN, "/dev/tty%d", tty) == -1) {
		return -1;
	}
	return 0;
}
#elif defined(__NetBSD__)
static int get_tty_path(int tty, char path[static TTYPATHLEN]) {
	assert(tty >= 0);
	if (snprintf(path, TTYPATHLEN, "/dev/ttyE%d", tty) == -1) {
		return -1;
	}
	return 0;
}
#else
#error Unsupported platform
#endif

int terminal_open(int vt) {
	char path[TTYPATHLEN];
	if (get_tty_path(vt, path) == -1) {
@@ -166,18 +24,8 @@ int terminal_open(int vt) {
}

int terminal_current_vt(int fd) {
#if defined(__linux__) || defined(__NetBSD__)
	struct vt_stat st;
	int res = ioctl(fd, VT_GETSTATE, &st);
	close(fd);
	if (res == -1) {
		log_errorf("Could not retrieve VT state: %s", strerror(errno));
		return -1;
	}
	return st.v_active;
#elif defined(__FreeBSD__)
	int vt;
	int res = ioctl(fd, VT_GETACTIVE, &vt);
	int res = get_current_vt(fd, &vt);
	close(fd);
	if (res == -1) {
		log_errorf("Could not retrieve VT state: %s", strerror(errno));
@@ -189,9 +37,6 @@ int terminal_current_vt(int fd) {
		return -1;
	}
	return vt;
#else
#error Unsupported platform
#endif
}

int terminal_set_process_switching(int fd, bool enable) {
@@ -244,29 +89,12 @@ int terminal_ack_acquire(int fd) {

int terminal_set_keyboard(int fd, bool enable) {
	log_debugf("Setting KD keyboard state to %d", enable);
#if defined(__linux__) || defined(__NetBSD__)
	if (ioctl(fd, KDSKBMODE, enable ? K_ENABLE : K_DISABLE) == -1) {

	if (set_keyboard_mode(fd, enable) == -1) {
		log_errorf("Could not set KD keyboard mode to %s: %s",
			   enable ? "enabled" : "disabled", strerror(errno));
		return -1;
	}
#elif defined(__FreeBSD__)
	struct termios tios;
	if (tcgetattr(fd, &tios) == -1) {
		log_errorf("Could not set get terminal mode: %s", strerror(errno));
		return -1;
	}
	if (enable) {
		cfmakesane(&tios);
	} else {
		cfmakeraw(&tios);
	}
	if (tcsetattr(fd, TCSAFLUSH, &tios) == -1) {
		log_errorf("Could not set terminal mode to %s: %s", enable ? "sane" : "raw",
			   strerror(errno));
		return -1;
	}
#endif
	return 0;
}

diff --git a/common/terminal_freebsd.c b/common/terminal_freebsd.c
new file mode 100644
index 0000000..6137ed3
--- /dev/null
+++ b/common/terminal_freebsd.c
@@ -0,0 +1,129 @@
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <limits.h>
#include <sys/consio.h>
#include <sys/kbio.h>
#include <termios.h>

#include "terminal.h"

int get_tty_path(int tty, char path[static TTYPATHLEN]) {
	assert(tty >= 0);

	const char prefix[] = "/dev/ttyv";
	const size_t prefix_len = sizeof(prefix) - 1;
	strcpy(path, prefix);

	// The FreeBSD VT_GETACTIVE is implemented in the kernel as follows:
	//
	//	static int
	//	vtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data,
	// 		struct thread *td)
	//	{
	//		struct vt_window *vw = tm->tm_softc;
	//		struct vt_device *vd = vw->vw_device;
	//		...
	//		switch (cmd) {
	//		...
	//		case VT_GETACTIVE:
	//			*(int *)data = vd->vd_curwindow->vw_number + 1;
	//			return (0);
	//		...
	//		}
	//		...
	//	}
	//
	// The side-effect here being that the returned VT number is one
	// greater than the internal VT number. The internal number is what is
	// used to number the TTY device, while the external number is what we
	// use in e.g. VT switching.
	//
	// We subtract one from the requested TTY number to compensate. If the
	// user asked for TTY 0 (which is special on Linux), we just give them
	// the first tty.

	if (tty > 0) {
		tty--;
	}

	// The FreeBSD tty name is constructed in the kernel as follows:
	//
	//	static void
	//	vtterm_cnprobe(struct terminal *tm, struct consdev *cp)
	//	{
	//		...
	//		struct vt_window *vw = tm->tm_softc;
	//		...
	//		sprintf(cp->cn_name, "ttyv%r", VT_UNIT(vw));
	//		...
	//	}
	//
	// With %r being a FreeBSD-internal radix formatter (seemingly set to
	// base 32), and VT_UNIT expanding to the following to extract the
	// internal VT number (which is one less than the external VT number):
	//
	//	((vw)->vw_device->vd_unit * VT_MAXWINDOWS + (vw)->vw_number)
	//
	// As the %r formatter is kernel-internal, we implement the base 32
	// encoding ourselves below.

	size_t offset = prefix_len;
	if (tty == 0) {
		path[offset++] = '0';
		path[offset++] = '\0';
		return 0;
	}

	const int base = 32;
	for (int remaining = tty; remaining > 0; remaining /= base, offset++) {
		// Return early if the buffer is too small.
		if (offset + 1 >= TTYPATHLEN) {
			errno = ENOMEM;
			return -1;
		}

		const int value = remaining % base;
		if (value >= 10) {
			path[offset] = 'a' + value - 10;
		} else {
			path[offset] = '0' + value;
		}
	}

	const size_t num_len = offset - prefix_len;
	for (size_t i = 0; i < num_len / 2; i++) {
		const size_t p1 = prefix_len + i;
		const size_t p2 = offset - 1 - i;
		const char tmp = path[p1];
		path[p1] = path[p2];
		path[p2] = tmp;
	}

	path[offset++] = '\0';
	return 0;
}

int get_current_vt(int fd, int *vt) {
	int res = ioctl(fd, VT_GETACTIVE, vt);
	int save_errno = errno;
	close(fd);
	errno = save_errno;
	return res;
}

int set_keyboard_mode(int fd, bool enable) {
	struct termios tios;
	if (tcgetattr(fd, &tios) == -1) {
		return -1;
	}
	if (enable) {
		cfmakesane(&tios);
	} else {
		cfmakeraw(&tios);
	}
	return tcsetattr(fd, TCSAFLUSH, &tios);
}
diff --git a/common/terminal_linux.c b/common/terminal_linux.c
new file mode 100644
index 0000000..1c679cb
--- /dev/null
+++ b/common/terminal_linux.c
@@ -0,0 +1,33 @@
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <limits.h>

#include "terminal.h"

int get_tty_path(int tty, char path[static TTYPATHLEN]) {
	assert(tty >= 0);
	snprintf(path, TTYPATHLEN, "/dev/tty%d", tty);
	return 0;
}

int get_current_vt(int fd, int *vt) {
	struct vt_stat st;
	int res = ioctl(fd, VT_GETSTATE, &st);
	int save_errno = errno;
	close(fd);
	errno = save_errno;
	if (res == -1) {
		return -1;
	}
	*vt = st.v_active;
	return 0;
}

int set_keyboard_mode(int fd, bool enable) {
	return ioctl(fd, KDSKBMODE, enable ? K_UNICODE : K_OFF);
}
diff --git a/common/terminal_netbsd.c b/common/terminal_netbsd.c
new file mode 100644
index 0000000..0c8cca2
--- /dev/null
+++ b/common/terminal_netbsd.c
@@ -0,0 +1,33 @@
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <limits.h>
#include <dev/wscons/wsdisplay_usl_io.h>

#include "terminal.h"

int get_tty_path(int tty, char path[static TTYPATHLEN]) {
	assert(tty >= 0);
	snprintf(path, TTYPATHLEN, "/dev/ttyE%d", tty);
	return 0;
}

int get_current_vt(int fd, int *vt) {
	struct vt_stat st;
	int res = ioctl(fd, VT_GETSTATE, &st);
	int save_errno = errno;
	close(fd);
	errno = save_errno;
	if (res == -1) {
		return -1;
	}
	*vt = st.v_active;
	return 0;
}

int set_keyboard_mode(int fd, bool enable) {
	return ioctl(fd, KDSKBMODE, enable ? K_XLATE : K_RAW);
}
diff --git a/include/terminal.h b/include/terminal.h
index fb46ce3..31ff8c6 100644
--- a/include/terminal.h
+++ b/include/terminal.h
@@ -2,6 +2,30 @@
#define _SEATD_TERMINAL_H

#include <stdbool.h>
#include <limits.h>
#include <signal.h>

#ifndef TTY_NAME_MAX
#define TTY_NAME_MAX (16 + 1)
#endif

#define TTYPATHLEN ((sizeof("/dev/") - 1) + TTY_NAME_MAX)

#if defined(__linux__)
#include <linux/kd.h>
#include <linux/vt.h>
#define FRSIG 0
#elif defined(__FreeBSD__)
#include <sys/consio.h>
#include <sys/kbio.h>
#include <termios.h>
#define FRSIG SIGIO
#elif defined(__NetBSD__)
#include <dev/wscons/wsdisplay_usl_io.h>
#define FRSIG 0
#else
#error Unsupported platform
#endif

int terminal_open(int vt);

@@ -13,4 +37,9 @@ int terminal_ack_acquire(int fd);
int terminal_set_keyboard(int fd, bool enable);
int terminal_set_graphics(int fd, bool enable);

// internal
int get_tty_path(int tty, char path[static TTYPATHLEN]);
int get_current_vt(int fd, int *vt);
int set_keyboard_mode(int fd, bool enable);

#endif
diff --git a/meson.build b/meson.build
index 21f1095..49dd49b 100644
--- a/meson.build
+++ b/meson.build
@@ -122,10 +122,13 @@ server_files = [

if host_machine.system() == 'netbsd'
  server_files += 'common/evdev_netbsd.c'
  server_files += 'common/terminal_netbsd.c'
elif host_machine.system() == 'linux'
  server_files += 'common/evdev_linux.c'
  server_files += 'common/terminal_linux.c'
elif host_machine.system() == 'freebsd'
  server_files += 'common/evdev_freebsd.c'
  server_files += 'common/terminal_freebsd.c'
else
  error('Unsupported platform')
endif
-- 
2.35.1

[PATCH 5/6] common/drm: Decouple os-specific functions Export this patch

---
 common/drm.c         | 53 +++++++++++++-------------------------------
 common/drm_freebsd.c | 13 +++++++++++
 common/drm_linux.c   | 12 ++++++++++
 common/drm_netbsd.c  |  8 +++++++
 include/drm.h        |  6 ++---
 meson.build          |  3 +++
 6 files changed, 54 insertions(+), 41 deletions(-)
 create mode 100644 common/drm_freebsd.c
 create mode 100644 common/drm_linux.c
 create mode 100644 common/drm_netbsd.c

diff --git a/common/drm.c b/common/drm.c
index 0d8096a..dbe24e8 100644
--- a/common/drm.c
+++ b/common/drm.c
@@ -1,26 +1,25 @@
#include <stddef.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>

#if defined(__linux__)
#include <sys/sysmacros.h>
#endif
#include "drm.h"

#if defined(__NetBSD__)
#include <stdlib.h>
#include <sys/stat.h>
#if defined(__linux__) || defined(__NetBSD__)
static const char *drms[] = { "/dev/dri/", NULL };
#elif defined(__OpenBSD__)
static const char *drms[] = { "/dev/drm", NULL };
#elif defined(__FreeBSD__)
static const char *drms[] = { "/dev/dri/", "/dev/drm/", NULL };
#else
#error Unsupported platform
#endif

#include "drm.h"

// From libdrm
#define DRM_IOCTL_BASE        'd'
#define DRM_IO(nr)            _IO(DRM_IOCTL_BASE, nr)
#define DRM_IOCTL_SET_MASTER  DRM_IO(0x1e)
#define DRM_IOCTL_DROP_MASTER DRM_IO(0x1f)

#define STRLEN(s) ((sizeof(s) / sizeof(s[0])) - 1)

int drm_set_master(int fd) {
	return ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
}
@@ -29,32 +28,12 @@ int drm_drop_master(int fd) {
	return ioctl(fd, DRM_IOCTL_DROP_MASTER, 0);
}

#if defined(__linux__)
int path_is_drm(const char *path) {
	static const char prefix[] = "/dev/dri/";
	static const int prefixlen = STRLEN(prefix);
	return strncmp(prefix, path, prefixlen) == 0;
}
	for (size_t i = 0; drms[i]; i++) {
		if (strncmp(path, drms[i], strlen(drms[i])) == 0) {
			return 1;
		}
	}

int dev_is_drm(dev_t device) {
	return major(device) == 226;
	return 0;
}
#elif defined(__FreeBSD__)
int path_is_drm(const char *path) {
	static const char prefix[] = "/dev/drm/";
	static const int prefixlen = STRLEN(prefix);
	return strncmp(prefix, path, prefixlen) == 0;
}
#elif defined(__NetBSD__)
int path_is_drm(const char *path) {
	static const char prefix[] = "/dev/dri/";
	static const int prefixlen = STRLEN(prefix);
	return strncmp(prefix, path, prefixlen) == 0;
}

int dev_is_drm(dev_t device) {
	return major(device) == getdevmajor("drm", S_IFCHR);
}
#else
#error Unsupported platform
#endif
diff --git a/common/drm_freebsd.c b/common/drm_freebsd.c
new file mode 100644
index 0000000..aa5b710
--- /dev/null
+++ b/common/drm_freebsd.c
@@ -0,0 +1,13 @@
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/param.h>

#include "drm.h"

int dev_is_drm(dev_t device) {
	char devname[SPECNAMELEN];
	// devname_r always succeds
	devname_r(device, S_IFCHR, devname, sizeof(devname));
	return strncmp(devname, "dri/", 4) == 0 || strncmp(devname, "drm/", 4) == 0;
}
diff --git a/common/drm_linux.c b/common/drm_linux.c
new file mode 100644
index 0000000..c6f9c79
--- /dev/null
+++ b/common/drm_linux.c
@@ -0,0 +1,12 @@
#include <linux/major.h>
#include <sys/sysmacros.h>

#include "drm.h"

#ifndef DRM_MAJOR
#define DRM_MAJOR 226
#endif

int dev_is_drm(dev_t device) {
	return major(device) == DRM_MAJOR;
}
diff --git a/common/drm_netbsd.c b/common/drm_netbsd.c
new file mode 100644
index 0000000..911c381
--- /dev/null
+++ b/common/drm_netbsd.c
@@ -0,0 +1,8 @@
#include <stdlib.h>
#include <sys/stat.h>

#include "drm.h"

int dev_is_drm(dev_t device) {
	return major(device) == getdevmajor("drm", S_IFCHR);
}
diff --git a/include/drm.h b/include/drm.h
index c8c3eeb..7d72bf2 100644
--- a/include/drm.h
+++ b/include/drm.h
@@ -1,13 +1,11 @@
#include <sys/types.h>

#ifndef _SEATD_DRM_H
#define _SEATD_DRM_H

int drm_set_master(int fd);
int drm_drop_master(int fd);
int path_is_drm(const char *path);

#if defined(__linux__) || defined(__NetBSD__)
#include <sys/types.h>
int dev_is_drm(dev_t device);
#endif

#endif
diff --git a/meson.build b/meson.build
index 49dd49b..f64d20a 100644
--- a/meson.build
+++ b/meson.build
@@ -121,12 +121,15 @@ server_files = [
]

if host_machine.system() == 'netbsd'
  server_files += 'common/drm_netbsd.c'
  server_files += 'common/evdev_netbsd.c'
  server_files += 'common/terminal_netbsd.c'
elif host_machine.system() == 'linux'
  server_files += 'common/drm_linux.c'
  server_files += 'common/evdev_linux.c'
  server_files += 'common/terminal_linux.c'
elif host_machine.system() == 'freebsd'
  server_files += 'common/drm_freebsd.c'
  server_files += 'common/evdev_freebsd.c'
  server_files += 'common/terminal_freebsd.c'
else
-- 
2.35.1

[PATCH 6/6] Rename evdev to input Export this patch

---
 common/{evdev.c => input.c}                 |  4 ++--
 common/{evdev_freebsd.c => input_freebsd.c} |  6 +++---
 common/{evdev_linux.c => input_linux.c}     |  6 +++---
 common/{evdev_netbsd.c => input_netbsd.c}   |  6 +++---
 include/evdev.h                             | 10 ----------
 include/input.h                             | 10 ++++++++++
 include/libseat.h                           |  2 +-
 include/seat.h                              |  2 +-
 meson.build                                 |  8 ++++----
 seatd/seat.c                                | 16 ++++++++--------
 10 files changed, 35 insertions(+), 35 deletions(-)
 rename common/{evdev.c => input.c} (88%)
 rename common/{evdev_freebsd.c => input_freebsd.c} (80%)
 rename common/{evdev_linux.c => input_linux.c} (74%)
 rename common/{evdev_netbsd.c => input_netbsd.c} (74%)
 delete mode 100644 include/evdev.h
 create mode 100644 include/input.h

diff --git a/common/evdev.c b/common/input.c
similarity index 88%
rename from common/evdev.c
rename to common/input.c
index 51d3a7f..f102f26 100644
--- a/common/evdev.c
+++ b/common/input.c
@@ -9,9 +9,9 @@ static const char *inputs[] = { "/dev/wskbd", "/dev/wsmux", "/dev/wsmouse", NULL
#error Unsupported platform
#endif

#include "evdev.h"
#include "input.h"

int path_is_evdev(const char *path) {
int path_is_input(const char *path) {
	for (size_t i = 0; inputs[i]; i++) {
		if (strncmp(path, inputs[i], strlen(inputs[i])) == 0) {
			return 1;
diff --git a/common/evdev_freebsd.c b/common/input_freebsd.c
similarity index 80%
rename from common/evdev_freebsd.c
rename to common/input_freebsd.c
index c2dcd02..be8309c 100644
--- a/common/evdev_freebsd.c
+++ b/common/input_freebsd.c
@@ -4,13 +4,13 @@
#include <sys/param.h>
#include <dev/evdev/input.h>

#include "evdev.h"
#include "input.h"

int evdev_revoke(int fd) {
int input_revoke(int fd) {
	return ioctl(fd, EVIOCREVOKE, NULL);
}

int dev_is_evdev(dev_t device) {
int dev_is_input(dev_t device) {
	char devname[SPECNAMELEN];
	// devname_r always succeds
	devname_r(device, S_IFCHR, devname ,sizeof(devname));
diff --git a/common/evdev_linux.c b/common/input_linux.c
similarity index 74%
rename from common/evdev_linux.c
rename to common/input_linux.c
index 2c4a15b..2264cd0 100644
--- a/common/evdev_linux.c
+++ b/common/input_linux.c
@@ -3,16 +3,16 @@
#include <linux/major.h>
#include <sys/sysmacros.h>

#include "evdev.h"
#include "input.h"

#ifndef INPUT_MAJOR
#define INPUT_MAJOR 13
#endif

int evdev_revoke(int fd) {
int input_revoke(int fd) {
	return ioctl(fd, EVIOCREVOKE, NULL);
}

int dev_is_evdev(dev_t device) {
int dev_is_input(dev_t device) {
	return major(device) == INPUT_MAJOR;
}
diff --git a/common/evdev_netbsd.c b/common/input_netbsd.c
similarity index 74%
rename from common/evdev_netbsd.c
rename to common/input_netbsd.c
index f98298a..0c761ae 100644
--- a/common/evdev_netbsd.c
+++ b/common/input_netbsd.c
@@ -1,14 +1,14 @@
#include <stdlib.h>
#include <sys/stat.h>

#include "evdev.h"
#include "input.h"

int evdev_revoke(int fd) {
int input_revoke(int fd) {
	(void)fd;
	return 0;
}

int dev_is_evdev(dev_t device) {
int dev_is_input(dev_t device) {
	return major(device) == getdevmajor("wskbd", S_IFCHR) ||
		major(device) == getdevmajor("wsmouse", S_IFCHR) ||
		major(device) == getdevmajor("wsmux", S_IFCHR);
diff --git a/include/evdev.h b/include/evdev.h
deleted file mode 100644
index d9eb503..0000000
--- a/include/evdev.h
@@ -1,10 +0,0 @@
#include <sys/types.h>

#ifndef _SEATD_EVDEV_H
#define _SEATD_EVDEV_H

int evdev_revoke(int fd);
int path_is_evdev(const char *path);
int dev_is_evdev(dev_t device);

#endif
diff --git a/include/input.h b/include/input.h
new file mode 100644
index 0000000..146f587
--- /dev/null
+++ b/include/input.h
@@ -0,0 +1,10 @@
#include <sys/types.h>

#ifndef _SEATD_INPUT_H
#define _SEATD_INPUT_H

int input_revoke(int fd);
int path_is_input(const char *path);
int dev_is_input(dev_t device);

#endif
diff --git a/include/libseat.h b/include/libseat.h
index 385acd9..60c9f8d 100644
--- a/include/libseat.h
+++ b/include/libseat.h
@@ -80,7 +80,7 @@ int libseat_close_seat(struct libseat *seat);
 * the specified pointer.
 *
 * This will only succeed if the seat is active and the device is of a type
 * permitted for opening on the backend, such as drm and evdev.
 * permitted for opening on the backend, such as drm and input.
 *
 * The device may be revoked in some situations, such as in situations where a
 * seat session switch is being forced.
diff --git a/include/seat.h b/include/seat.h
index cc243b6..ca9dd3e 100644
--- a/include/seat.h
+++ b/include/seat.h
@@ -11,7 +11,7 @@ struct client;

enum seat_device_type {
	SEAT_DEVICE_TYPE_NORMAL,
	SEAT_DEVICE_TYPE_EVDEV,
	SEAT_DEVICE_TYPE_INPUT,
	SEAT_DEVICE_TYPE_DRM,
};

diff --git a/meson.build b/meson.build
index f64d20a..83fc21a 100644
--- a/meson.build
+++ b/meson.build
@@ -112,7 +112,7 @@ server_files = [
	'common/linked_list.c',
	'common/terminal.c',
	'common/connection.c',
	'common/evdev.c',
	'common/input.c',
	'common/drm.c',
	'seatd/poller.c',
	'seatd/seat.c',
@@ -122,15 +122,15 @@ server_files = [

if host_machine.system() == 'netbsd'
  server_files += 'common/drm_netbsd.c'
  server_files += 'common/evdev_netbsd.c'
  server_files += 'common/input_netbsd.c'
  server_files += 'common/terminal_netbsd.c'
elif host_machine.system() == 'linux'
  server_files += 'common/drm_linux.c'
  server_files += 'common/evdev_linux.c'
  server_files += 'common/input_linux.c'
  server_files += 'common/terminal_linux.c'
elif host_machine.system() == 'freebsd'
  server_files += 'common/drm_freebsd.c'
  server_files += 'common/evdev_freebsd.c'
  server_files += 'common/input_freebsd.c'
  server_files += 'common/terminal_freebsd.c'
else
  error('Unsupported platform')
diff --git a/seatd/seat.c b/seatd/seat.c
index 354273f..545339d 100644
--- a/seatd/seat.c
+++ b/seatd/seat.c
@@ -11,7 +11,7 @@

#include "client.h"
#include "drm.h"
#include "evdev.h"
#include "input.h"
#include "linked_list.h"
#include "log.h"
#include "protocol.h"
@@ -231,8 +231,8 @@ struct seat_device *seat_open_device(struct client *client, const char *path) {
	}

	enum seat_device_type type;
	if (path_is_evdev(sanitized_path)) {
		type = SEAT_DEVICE_TYPE_EVDEV;
	if (path_is_input(sanitized_path)) {
		type = SEAT_DEVICE_TYPE_INPUT;
	} else if (path_is_drm(sanitized_path)) {
		type = SEAT_DEVICE_TYPE_DRM;
	} else {
@@ -278,7 +278,7 @@ struct seat_device *seat_open_device(struct client *client, const char *path) {
			log_errorf("Could not make device fd drm master: %s", strerror(errno));
		}
		break;
	case SEAT_DEVICE_TYPE_EVDEV:
	case SEAT_DEVICE_TYPE_INPUT:
		// Nothing to do here
		break;
	default:
@@ -327,9 +327,9 @@ static int seat_deactivate_device(struct seat_device *seat_device) {
			return -1;
		}
		break;
	case SEAT_DEVICE_TYPE_EVDEV:
		if (evdev_revoke(seat_device->fd) == -1) {
			log_errorf("Could not revoke evdev on device fd: %s", strerror(errno));
	case SEAT_DEVICE_TYPE_INPUT:
		if (input_revoke(seat_device->fd) == -1) {
			log_errorf("Could not revoke input on device fd: %s", strerror(errno));
			return -1;
		}
		break;
@@ -379,7 +379,7 @@ static int seat_activate_device(struct client *client, struct seat_device *seat_
		}
		seat_device->active = true;
		break;
	case SEAT_DEVICE_TYPE_EVDEV:
	case SEAT_DEVICE_TYPE_INPUT:
		errno = EINVAL;
		return -1;
	default:
-- 
2.35.1