diff --git a/.builds/alpine.yml b/.builds/alpine.yml index bb8904a..7e59203 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -20,7 +20,7 @@ tasks: ninja -C build scan-build [ -z "$(ls -A build/meson-logs/scanbuild/ 2>/dev/null)" ] - smoketest: | - timeout -s KILL 30s sudo SEATD_LOGLEVEL=debug ./build/seatd-launch ./build/simpletest /dev/dri/card0 + timeout -s KILL 30s sudo ./build/seatd-launch -l debug -- ./build/simpletest /dev/dri/card0 - smoketest-builtin: | timeout -s KILL 30s sudo LIBSEAT_BACKEND=builtin ./build/simpletest /dev/dri/card0 - check-format: | diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index da300c8..185d232 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -19,7 +19,7 @@ tasks: ninja -C build scan-build [ -z "$(ls -A build/meson-logs/scanbuild/ 2>/dev/null)" ] - smoketest: | - timeout -s KILL 30s sudo SEATD_LOGLEVEL=debug ./build/seatd-launch ./build/simpletest /dev/input/event0 + timeout -s KILL 30s sudo ./build/seatd-launch -l debug -- ./build/simpletest /dev/input/event0 - smoketest-builtin: | timeout -s KILL 30s sudo LIBSEAT_BACKEND=builtin ./build/simpletest /dev/input/event0 - smoketest-logind: | diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index b4bc7da..c8f9618 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -15,6 +15,6 @@ tasks: 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 KILL 30s sudo SEATD_LOGLEVEL=debug ./build/seatd-launch ./build/simpletest /dev/input/event0 + timeout -s KILL 30s sudo ./build/seatd-launch -l debug -- ./build/simpletest /dev/input/event0 - smoketest-builtin: | timeout -s KILL 30s sudo LIBSEAT_BACKEND=builtin ./build/simpletest /dev/input/event0 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 diff --git a/README.md b/README.md index 9aa9e99..b8a705f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A minimal seat management daemon, and a universal seat management library. -Currently supports Linux and FreeBSD. +Currently supports Linux and FreeBSD, and has experimental NetBSD support. ## What is seat management? @@ -43,4 +43,4 @@ Instead of giving user shell developers more work, libseat aims to make supporti ## How to discuss -Go to #kennylevinsen @ irc.libera.chat to discuss, or use [~kennylevinsen/seatd-devel@lists.sr.ht](https://lists.sr.ht/~kennylevinsen/seatd-devel). +Go to [#kennylevinsen @ irc.libera.chat](ircs://irc.libera.chat/#kennylevinsen) to discuss, or use [~kennylevinsen/seatd-devel@lists.sr.ht](https://lists.sr.ht/~kennylevinsen/seatd-devel). diff --git a/common/drm.c b/common/drm.c index 9591dc0..2fbadd5 100644 --- a/common/drm.c +++ b/common/drm.c @@ -2,10 +2,6 @@ #include #include -#if defined(__linux__) -#include -#endif - #include "drm.h" // From libdrm @@ -14,7 +10,8 @@ #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) +#define STRLEN(s) ((sizeof(s) / sizeof(s[0])) - 1) +#define STR_HAS_PREFIX(prefix, s) (strncmp(prefix, s, STRLEN(prefix)) == 0) int drm_set_master(int fd) { return ioctl(fd, DRM_IOCTL_SET_MASTER, 0); @@ -24,21 +21,20 @@ int drm_drop_master(int fd) { return ioctl(fd, DRM_IOCTL_DROP_MASTER, 0); } -#if defined(__linux__) +#if defined(__linux__) || defined(__NetBSD__) || defined(__OpenBSD__) 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) == 226; + if (STR_HAS_PREFIX("/dev/dri/", path)) + return 1; + 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; + if (STR_HAS_PREFIX("/dev/dri/", path)) + return 1; + /* Some drivers have /dev/dri/X symlinked to /dev/drm/X */ + if (STR_HAS_PREFIX("/dev/drm/", path)) + return 1; + return 0; } #else #error Unsupported platform diff --git a/common/evdev.c b/common/evdev.c index 4aff9bc..a77bf36 100644 --- a/common/evdev.c +++ b/common/evdev.c @@ -9,14 +9,13 @@ #include #elif defined(__FreeBSD__) #include -#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); @@ -26,9 +25,15 @@ int path_is_evdev(const char *path) { int evdev_revoke(int fd) { return ioctl(fd, EVIOCREVOKE, NULL); } - -#if defined(__linux__) -int dev_is_evdev(dev_t device) { - return major(device) == INPUT_MAJOR; +#elif defined(__NetBSD__) || defined(__OpenBSD__) +int path_is_evdev(const char *path) { + (void)path; + return 0; } +int evdev_revoke(int fd) { + (void)fd; + return 0; +} +#else +#error Unsupported platform #endif diff --git a/common/terminal.c b/common/terminal.c index 0c3466f..5aac2dd 100644 --- a/common/terminal.c +++ b/common/terminal.c @@ -21,6 +21,17 @@ #define K_ENABLE K_XLATE #define K_DISABLE K_RAW #define FRSIG SIGIO +#elif defined(__NetBSD__) +#include +#define K_ENABLE K_XLATE +#define K_DISABLE K_RAW +#define FRSIG 0 // unimplemented +#elif defined(__OpenBSD__) +#include +#include +#define K_ENABLE K_XLATE +#define K_DISABLE K_RAW +#define FRSIG SIGIO #else #error Unsupported platform #endif @@ -134,16 +145,38 @@ 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; +} +#elif defined(__OpenBSD__) +static int get_tty_path(int tty, char path[static TTYPATHLEN]) { + assert(tty >= 0); + if (snprintf(path, TTYPATHLEN, "/dev/ttyC%d", tty) == -1) { + return -1; + } + return 0; +} #else #error Unsupported platform #endif int terminal_open(int vt) { char path[TTYPATHLEN]; + log_debugf("terminal_open vt %d", vt); +#ifdef __OpenBSD__ + if (vt > 0) + vt--; +#endif if (get_tty_path(vt, path) == -1) { log_errorf("Could not generate tty path: %s", strerror(errno)); return -1; } + log_debugf("terminal_open path %s", path); int fd = open(path, O_RDWR | O_NOCTTY); if (fd == -1) { log_errorf("Could not open target tty: %s", strerror(errno)); @@ -153,7 +186,7 @@ int terminal_open(int vt) { } int terminal_current_vt(int fd) { -#if defined(__linux__) +#if defined(__linux__) || defined(__NetBSD__) || defined(__OpenBSD__) struct vt_stat st; int res = ioctl(fd, VT_GETSTATE, &st); close(fd); @@ -231,11 +264,20 @@ int terminal_ack_acquire(int fd) { int terminal_set_keyboard(int fd, bool enable) { log_debugf("Setting KD keyboard state to %d", enable); +#ifndef __OpenBSD1__ 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; } +#else + int mode = enable ? WSKBD_RAW : WSKBD_TRANSLATED; + if (ioctl(fd, WSKBDIO_SETMODE, &mode) == -1) { + log_errorf("Could not set keyboard mode to %s: %s", + enable ? "translated" : "raw", strerror(errno)); + return -1; + } +#endif #if defined(__FreeBSD__) struct termios tios; if (tcgetattr(fd, &tios) == -1) { @@ -258,10 +300,19 @@ int terminal_set_keyboard(int fd, bool enable) { int terminal_set_graphics(int fd, bool enable) { log_debugf("Setting KD graphics state to %d", enable); +#ifndef __OpenBSD1__ if (ioctl(fd, KDSETMODE, enable ? KD_GRAPHICS : KD_TEXT) == -1) { log_errorf("Could not set KD graphics mode to %s: %s", enable ? "graphics" : "text", strerror(errno)); return -1; } +#else + int mode = enable ? WSDISPLAYIO_MODE_MAPPED : WSDISPLAYIO_MODE_EMUL; + if (ioctl(fd, WSDISPLAYIO_SMODE, &mode) == -1) { + log_errorf("Could not set graphics mode to %s: %s", + enable ? "mapped" : "emul", strerror(errno)); + return -1; + } +#endif return 0; } diff --git a/common/wscons.c b/common/wscons.c new file mode 100644 index 0000000..121074c --- /dev/null +++ b/common/wscons.c @@ -0,0 +1,27 @@ +#include +#include + +#if defined(__NetBSD__) +#include +#include +#endif + +#include "wscons.h" + +#define STRLEN(s) ((sizeof(s) / sizeof(s[0])) - 1) + +#if defined(__NetBSD__) || defined(__OpenBSD__) +int path_is_wscons(const char *path) { + static const char wskbd[] = "/dev/wskbd"; + static const char wsmouse[] = "/dev/wsmouse"; + static const char wsmux[] = "/dev/wsmux"; + return strncmp(path, wskbd, STRLEN(wskbd)) == 0 || + strncmp(path, wsmouse, STRLEN(wsmouse)) == 0 || + strncmp(path, wsmux, STRLEN(wsmouse)) == 0; +} +#else +int path_is_wscons(const char *path) { + (void)path; + return 0; +} +#endif diff --git a/include/drm.h b/include/drm.h index 8a7fb10..a8a5461 100644 --- a/include/drm.h +++ b/include/drm.h @@ -5,9 +5,4 @@ int drm_set_master(int fd); int drm_drop_master(int fd); int path_is_drm(const char *path); -#if defined(__linux__) -#include -int dev_is_drm(dev_t device); -#endif - #endif diff --git a/include/evdev.h b/include/evdev.h index 6ebd943..5e6cfbf 100644 --- a/include/evdev.h +++ b/include/evdev.h @@ -4,9 +4,4 @@ int evdev_revoke(int fd); int path_is_evdev(const char *path); -#if defined(__linux__) -#include -int dev_is_evdev(dev_t device); -#endif - #endif diff --git a/include/seat.h b/include/seat.h index cc243b6..ff857e7 100644 --- a/include/seat.h +++ b/include/seat.h @@ -13,6 +13,7 @@ enum seat_device_type { SEAT_DEVICE_TYPE_NORMAL, SEAT_DEVICE_TYPE_EVDEV, SEAT_DEVICE_TYPE_DRM, + SEAT_DEVICE_TYPE_WSCONS, }; struct seat_device { diff --git a/include/wscons.h b/include/wscons.h new file mode 100644 index 0000000..bf0c10c --- /dev/null +++ b/include/wscons.h @@ -0,0 +1,6 @@ +#ifndef _SEATD_WSCONS_H +#define _SEATD_WSCONS_H + +int path_is_wscons(const char *path); + +#endif diff --git a/libseat/backend/logind.c b/libseat/backend/logind.c index 5a10c75..2589e2f 100644 --- a/libseat/backend/logind.c +++ b/libseat/backend/logind.c @@ -13,10 +13,10 @@ #include #include -#if defined(HAVE_ELOGIND) +#if defined(HAVE_LIBELOGIND) #include #include -#elif defined(HAVE_SYSTEMD) +#elif defined(HAVE_LIBSYSTEMD) #include #include #else @@ -28,6 +28,14 @@ #include "libseat.h" #include "log.h" +static int dev_major_is_drm(unsigned int dev_major) { + return dev_major == 226; +} + +static int dev_is_drm(dev_t device) { + return dev_major_is_drm(major(device)); +} + struct backend_logind { struct libseat base; const struct libseat_seat_listener *seat_listener; @@ -41,7 +49,6 @@ struct backend_logind { bool active; bool initial_setup; - bool awaiting_pong; int has_drm; }; @@ -70,13 +77,12 @@ static int close_seat(struct libseat *base) { static int ping_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { (void)ret_error; - struct backend_logind *session = userdata; + (void)userdata; if (sd_bus_message_is_method_error(m, NULL)) { const sd_bus_error *error = sd_bus_message_get_error(m); log_errorf("Ping failed: %s: %s", error->name, error->message); return -1; } - session->awaiting_pong = false; return 0; } @@ -91,14 +97,15 @@ static int send_ping(struct backend_logind *backend) { } static void check_pending_events(struct backend_logind *backend) { - if (sd_bus_get_events(backend->bus) <= 0) { - return; - } - if (backend->awaiting_pong) { + uint64_t queued_read, queued_write; + sd_bus_get_n_queued_read(backend->bus, &queued_read); + sd_bus_get_n_queued_write(backend->bus, &queued_write); + + if (queued_read == 0 && queued_write == 0) { return; } - // We have events pending execution, so a dispatch is required. + // The sd_bus instance has queued data, so a dispatch is required. // However, we likely already drained our socket, so there will not be // anything to read. Instead, send a ping request to logind so that the // user will be woken up by its response. @@ -107,7 +114,6 @@ static void check_pending_events(struct backend_logind *backend) { log_errorf("Could not send ping message: %s", strerror(-ret)); return; } - backend->awaiting_pong = true; } static int open_device(struct libseat *base, const char *path, int *fd) { @@ -286,6 +292,7 @@ static int dispatch_and_execute(struct libseat *base, int timeout) { total_dispatched += dispatched; } } + check_pending_events(backend); return total_dispatched; } @@ -388,7 +395,7 @@ static int pause_device(sd_bus_message *msg, void *userdata, sd_bus_error *ret_e return 0; } - if (dev_is_drm(makedev(major, minor)) && strcmp(type, "gone") != 0) { + if (dev_major_is_drm(major) && strcmp(type, "gone") != 0) { log_debugf("DRM device paused: %s", type); assert(session->has_drm > 0); set_active(session, false); @@ -420,7 +427,7 @@ static int resume_device(sd_bus_message *msg, void *userdata, sd_bus_error *ret_ return 0; } - if (dev_is_drm(makedev(major, minor))) { + if (dev_major_is_drm(major)) { log_debug("DRM device resumed"); assert(session->has_drm > 0); set_active(session, true); @@ -742,6 +749,7 @@ static struct libseat *logind_open_seat(const struct libseat_seat_listener *list backend->seat_listener_data = data; backend->base.impl = &logind_impl; + check_pending_events(backend); return &backend->base; error: diff --git a/libseat/backend/noop.c b/libseat/backend/noop.c index 7436c48..18f26c3 100644 --- a/libseat/backend/noop.c +++ b/libseat/backend/noop.c @@ -46,13 +46,13 @@ static int disable_seat(struct libseat *base) { static const char *seat_name(struct libseat *base) { (void)base; - return "noop"; + return "seat0"; } static int open_device(struct libseat *base, const char *path, int *fd) { (void)base; - int tmpfd = open(path, O_RDWR | O_CLOEXEC); + int tmpfd = open(path, O_RDWR | O_NOCTTY | O_NOFOLLOW | O_CLOEXEC | O_NONBLOCK); if (tmpfd < 0) { log_errorf("Failed to open device: %s", strerror(errno)); return -1; @@ -109,12 +109,13 @@ static struct libseat *noop_open_seat(const struct libseat_seat_listener *listen return NULL; } - if (socketpair(AF_UNIX, SOCK_STREAM, 0, backend->sockets) != 0) { + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, backend->sockets) != 0) { log_errorf("socketpair() failed: %s", strerror(errno)); free(backend); return NULL; } + backend->initial_setup = true; backend->seat_listener = listener; backend->seat_listener_data = data; backend->base.impl = &noop_impl; diff --git a/libseat/backend/seatd.c b/libseat/backend/seatd.c index 26308d1..abcd2a5 100644 --- a/libseat/backend/seatd.c +++ b/libseat/backend/seatd.c @@ -42,32 +42,16 @@ struct backend_seatd { char seat_name[MAX_SEAT_LEN]; }; -static int set_nonblock(int fd) { - int flags; - if ((flags = fcntl(fd, F_GETFD)) == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { - return -1; - } - if ((flags = fcntl(fd, F_GETFL)) == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { - return -1; - } - return 0; -} - static int seatd_connect(void) { union { struct sockaddr_un unix; struct sockaddr generic; } addr = {{0}}; - int fd = socket(AF_UNIX, SOCK_STREAM, 0); + int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); if (fd == -1) { log_errorf("Could not create socket: %s", strerror(errno)); return -1; } - if (set_nonblock(fd) == -1) { - log_errorf("Could not make socket non-blocking: %s", strerror(errno)); - close(fd); - return -1; - } const char *path = getenv("SEATD_SOCK"); if (path == NULL) { path = SEATD_DEFAULTPATH; @@ -616,29 +600,10 @@ const struct seat_impl seatd_impl = { }; #ifdef BUILTIN_ENABLED -#include - -static int set_deathsig(int signal); - -#if defined(__linux__) -#include - -static int set_deathsig(int signal) { - return prctl(PR_SET_PDEATHSIG, signal); -} -#elif defined(__FreeBSD__) -#include - -static int set_deathsig(int signal) { - return procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &signal); -} -#else -#error Unsupported platform -#endif static struct libseat *builtin_open_seat(const struct libseat_seat_listener *listener, void *data) { int fds[2]; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) { + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, fds) == -1) { log_errorf("Could not create socket pair: %s", strerror(errno)); return NULL; } @@ -650,6 +615,7 @@ static struct libseat *builtin_open_seat(const struct libseat_seat_listener *lis close(fds[1]); return NULL; } else if (pid == 0) { + close(fds[1]); int fd = fds[0]; int res = 0; struct server server = {0}; @@ -664,7 +630,7 @@ static struct libseat *builtin_open_seat(const struct libseat_seat_listener *lis res = 1; goto server_error; } - set_deathsig(SIGTERM); + log_info("Started embedded seatd"); while (server.running) { if (poller_poll(&server.poller) == -1) { log_errorf("Could not poll server socket: %s", strerror(errno)); @@ -676,8 +642,10 @@ static struct libseat *builtin_open_seat(const struct libseat_seat_listener *lis server_finish(&server); error: close(fd); + log_info("Stopped embedded seatd"); exit(res); } else { + close(fds[0]); int fd = fds[1]; return _open_seat(listener, data, fd); } diff --git a/man/seatd-launch.1.scd b/man/seatd-launch.1.scd index 9234203..73d302d 100644 --- a/man/seatd-launch.1.scd +++ b/man/seatd-launch.1.scd @@ -10,12 +10,13 @@ seatd-launch - Start a process with its own seatd instance # OPTIONS +*-l * + Log-level to pass to seatd. See *seatd*(1) for information about + available log-levels. + *-h* Show help message and quit. -*-s * - Where to create the seatd socket. Defaults to a unique file path. - *-v* Show the version number and quit. diff --git a/man/seatd.1.scd b/man/seatd.1.scd index de95843..92f8e4b 100644 --- a/man/seatd.1.scd +++ b/man/seatd.1.scd @@ -13,7 +13,7 @@ seatd - A seat management daemon *-h* Show help message and quit. -*-n* +*-n * FD to notify readiness on. A single newline will be written and the fd closed when seatd is ready to serve requests. This is compatible with s6's notification protocol. @@ -24,8 +24,9 @@ seatd - A seat management daemon *-g * Group to own the seatd socket. -*-s * - Where to create the seatd socket. Defaults to `/run/seatd.sock`. +*-l * + Log-level to use. Must be one of debug, info, error or silent. Defaults + to error. *-v* Show the version number and quit. @@ -38,17 +39,12 @@ such as displays and input devices in a multi-session, multi-seat environment. seatd operates over a UNIX domain socket, with *libseat* providing the client-side of the protocol. +The location of the socket for seatd is set at compile-time. + # ENVIRONMENT -[[ *VARIABLE* -:[ *VALUES* -:< *DESCRIPTION* -| SEATD_SOCK -: File path -: Informs libseat of the socket location, needed if it differs from `/run/seatd.sock` -| SEATD_LOGLEVEL -: silent, error, info, debug -: Sets the seatd log level. Defaults to "error" +*SEATD_VTBOUND* + If set to "0", the seat will not be bound to a VT. # SEE ALSO diff --git a/meson.build b/meson.build index d8fd25d..fcd85de 100644 --- a/meson.build +++ b/meson.build @@ -1,9 +1,9 @@ project( 'seatd', 'c', - version: '0.6.3', + version: '0.8.0', license: 'MIT', - meson_version: '>=0.56.0', + meson_version: '>=0.60.0', default_options: [ 'c_std=c11', 'warning_level=3', @@ -22,9 +22,22 @@ if defaultpath == '' endif endif -seatdpath = '@0@/@1@/seatd'.format(get_option('prefix'), get_option('bindir')) +seatdpath = get_option('prefix') / get_option('bindir') / 'seatd' cc = meson.get_compiler('c') + +add_project_arguments( + [ + '-D_XOPEN_SOURCE=700', + '-D_BSD_SOURCE', + '-D_NETBSD_SOURCE', + '-DSEATD_VERSION="@0@"'.format(meson.project_version()), + '-DSEATD_DEFAULTPATH="@0@"'.format(defaultpath), + '-DSEATD_INSTALLPATH="@0@"'.format(seatdpath), + ], + language: 'c', +) + add_project_arguments(cc.get_supported_arguments( [ '-Wundef', @@ -39,12 +52,13 @@ add_project_arguments(cc.get_supported_arguments( '-Wno-unknown-warning-option', '-Wno-unused-command-line-argument', '-Wvla', + ]), + language: 'c', +) + +add_project_arguments(cc.get_supported_link_arguments( + [ '-Wl,--exclude-libs=ALL', - '-D_XOPEN_SOURCE=700', - '-D__BSD_VISIBLE', - '-DSEATD_VERSION="@0@"'.format(meson.project_version()), - '-DSEATD_DEFAULTPATH="@0@"'.format(defaultpath), - '-DSEATD_INSTALLPATH="@0@"'.format(seatdpath), ]), language: 'c', ) @@ -59,7 +73,7 @@ endif # Hacks source_root = meson.current_source_dir().split('/') -build_root = meson.build_root().split('/') +build_root = meson.global_build_root().split('/') relative_dir_parts = [] i = 0 in_prefix = true @@ -100,6 +114,7 @@ server_files = [ 'common/connection.c', 'common/evdev.c', 'common/drm.c', + 'common/wscons.c', 'seatd/poller.c', 'seatd/seat.c', 'seatd/client.c', @@ -114,31 +129,26 @@ if with_seatd or with_builtin private_files += 'libseat/backend/seatd.c' endif +libseat_c_args = ['-DLIBSEAT=1'] + if with_seatd - add_project_arguments('-DSEATD_ENABLED=1', language: 'c') + libseat_c_args += '-DSEATD_ENABLED=1' endif logind = disabler() -logind_provider = '' if get_option('libseat-logind') != 'disabled' if get_option('libseat-logind') == 'auto' and get_option('auto_features').disabled() # Disable logind elif get_option('libseat-logind') == 'auto' assert(get_option('auto_features').auto(), '-Dlibseat-logind must be set to systemd or elogind since auto_features != auto') - foreach logind_provider : ['elogind', 'systemd'] - logind = dependency('lib@0@'.format(logind_provider), required: false) - if logind.found() - break - endif - endforeach + logind = dependency(['libelogind', 'libsystemd'], required: false) else - logind_provider = get_option('libseat-logind') - logind = dependency('lib@0@'.format(logind_provider)) + logind = dependency('lib@0@'.format(get_option('libseat-logind'))) endif if logind.found() - add_project_arguments('-DLOGIND_ENABLED=1', language: 'c') - add_project_arguments('-DHAVE_@0@=1'.format(logind_provider.to_upper()), language: 'c') + libseat_c_args += '-DLOGIND_ENABLED=1' + libseat_c_args += '-DHAVE_@0@=1'.format(logind.name().to_upper()) private_files += [ 'libseat/backend/logind.c', 'common/drm.c', @@ -148,11 +158,11 @@ if get_option('libseat-logind') != 'disabled' endif # needed for cross-compilation -realtime = meson.get_compiler('c').find_library('rt') -private_deps += realtime +# realtime = meson.get_compiler('c').find_library('rt') +# private_deps += realtime if with_builtin - add_project_arguments('-DBUILTIN_ENABLED=1', language: 'c') + libseat_c_args += '-DBUILTIN_ENABLED=1' private_files += server_files endif @@ -161,6 +171,7 @@ private_lib = static_library( private_files, dependencies: private_deps, include_directories: [include_directories('.', 'include')], + c_args: libseat_c_args, ) symbols_file = 'libseat/libseat.syms' @@ -168,12 +179,13 @@ symbols_flag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), lib = library( 'seat', # This results in the library being called 'libseat' [ 'libseat/libseat.c', 'libseat/backend/noop.c' ], - soversion: libseat_soversion, + soversion: '@0@'.format(libseat_soversion), link_with: private_lib, include_directories: [include_directories('.', 'include')], install: true, link_args: symbols_flag, link_depends: symbols_file, + c_args: libseat_c_args, ) install_headers('include/libseat.h') @@ -200,20 +212,22 @@ libseat = declare_dependency( variables: libseat_vars, ) +meson.override_dependency('libseat', libseat) + if with_server executable( 'seatd', [ server_files, 'seatd/seatd.c' ], include_directories: [include_directories('.', 'include')], install: true, - dependencies: [realtime], +# dependencies: [realtime], ) executable( 'seatd-launch', [ 'seatd-launch/seatd-launch.c' ], include_directories: [include_directories('.', 'include')], install: true, - dependencies: [realtime], +# dependencies: [realtime], ) endif @@ -247,7 +261,6 @@ else endif if scdoc.found() - scdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true) mandir = get_option('mandir') foreach src : ['seatd.1.scd', 'seatd-launch.1.scd'] @@ -259,9 +272,9 @@ if scdoc.found() output, input: 'man/' + src, output: output, - command: [ - 'sh', '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.full_path(), output) - ], + command: scdoc.get_variable(pkgconfig: 'scdoc'), + feed: true, + capture: true, install: true, install_dir: '@0@/man@1@'.format(mandir, section) ) @@ -271,7 +284,7 @@ endif summary({ 'libseat-seatd': with_seatd, 'libseat-builtin': with_builtin, - 'libseat-systemd': logind.found() and logind_provider == 'systemd', - 'libseat-elogind': logind.found() and logind_provider == 'elogind', + 'libseat-systemd': logind.found() and logind.name() == 'libsystemd', + 'libseat-elogind': logind.found() and logind.name() == 'libelogind', 'server': with_server, }, bool_yn: true) diff --git a/seatd-launch/seatd-launch.c b/seatd-launch/seatd-launch.c index 69ca86a..65d4f33 100644 --- a/seatd-launch/seatd-launch.c +++ b/seatd-launch/seatd-launch.c @@ -11,21 +11,20 @@ #include int main(int argc, char *argv[]) { - (void)argc; - const char *usage = "Usage: seatd-launch [options] [--] command\n" "\n" + " -l Log-level to pass to seatd\n" " -h Show this help message\n" - " -s Where to create the seatd socket\n" " -v Show the version number\n" "\n"; int c; - char *sockpath = NULL; - while ((c = getopt(argc, argv, "vhs:")) != -1) { + char loglevel[16] = "info"; + while ((c = getopt(argc, argv, "vhl:")) != -1) { switch (c) { - case 's': - sockpath = optarg; + case 'l': + strncpy(loglevel, optarg, sizeof loglevel); + loglevel[sizeof loglevel - 1] = '\0'; break; case 'v': printf("seatd-launch version %s\n", SEATD_VERSION); @@ -34,7 +33,7 @@ int main(int argc, char *argv[]) { printf("%s", usage); return 0; case '?': - fprintf(stderr, "Try '%s -h' for more information.\n", argv[0]); + fprintf(stderr, "Try 'seatd-launch -h' for more information.\n"); return 1; default: abort(); @@ -47,44 +46,30 @@ int main(int argc, char *argv[]) { } char **command = &argv[optind]; - char sockbuf[256]; - if (sockpath == NULL) { - sprintf(sockbuf, "/tmp/seatd.%d.sock", getpid()); - sockpath = sockbuf; - } - - unlink(sockpath); - - int fds[2]; - if (pipe(fds) == -1) { + int readiness_pipe[2]; + if (pipe(readiness_pipe) == -1) { perror("Could not create pipe"); goto error; } + // Start seatd pid_t seatd_child = fork(); if (seatd_child == -1) { perror("Could not fork seatd process"); goto error; } else if (seatd_child == 0) { - close(fds[0]); + close(readiness_pipe[0]); char pipebuf[16] = {0}; - snprintf(pipebuf, sizeof pipebuf, "%d", fds[1]); + snprintf(pipebuf, sizeof pipebuf, "%d", readiness_pipe[1]); - char *env[2] = {NULL, NULL}; - char loglevelbuf[32] = {0}; - char *cur_loglevel = getenv("SEATD_LOGLEVEL"); - if (cur_loglevel != NULL) { - snprintf(loglevelbuf, sizeof loglevelbuf, "SEATD_LOGLEVEL=%s", cur_loglevel); - env[0] = loglevelbuf; - } - - char *command[] = {"seatd", "-n", pipebuf, "-s", sockpath, NULL}; + char *env[1] = {NULL}; + char *command[] = {"seatd", "-n", pipebuf, "-l", loglevel, "-z", NULL}; execve(SEATD_INSTALLPATH, command, env); perror("Could not start seatd"); _exit(1); } - close(fds[1]); + close(readiness_pipe[1]); // Wait for seatd to be ready char buf[1] = {0}; @@ -99,7 +84,7 @@ int main(int argc, char *argv[]) { } struct pollfd fd = { - .fd = fds[0], + .fd = readiness_pipe[0], .events = POLLIN, }; @@ -114,7 +99,7 @@ int main(int argc, char *argv[]) { } if (fd.revents & POLLIN) { - ssize_t n = read(fds[0], buf, 1); + ssize_t n = read(readiness_pipe[0], buf, 1); if (n == -1 && errno != EINTR) { perror("Could not read from pipe"); goto error_seatd; @@ -123,17 +108,17 @@ int main(int argc, char *argv[]) { } } } - close(fds[0]); + close(readiness_pipe[0]); uid_t uid = getuid(); gid_t gid = getgid(); // Restrict access to the socket to just us - if (chown(sockpath, uid, gid) == -1) { + if (chown(SEATD_DEFAULTPATH, uid, gid) == -1) { perror("Could not chown seatd socket"); goto error_seatd; } - if (chmod(sockpath, 0700) == -1) { + if (chmod(SEATD_DEFAULTPATH, 0700) == -1) { perror("Could not chmod socket"); goto error_seatd; } @@ -153,7 +138,7 @@ int main(int argc, char *argv[]) { perror("Could not fork target process"); goto error_seatd; } else if (child == 0) { - setenv("SEATD_SOCK", sockpath, 1); + setenv("SEATD_SOCK", SEATD_DEFAULTPATH, 1); execvp(command[0], command); perror("Could not start target"); _exit(1); @@ -170,9 +155,6 @@ int main(int argc, char *argv[]) { } } - if (unlink(sockpath) != 0) { - perror("Could not unlink socket"); - } if (kill(seatd_child, SIGTERM) != 0) { perror("Could not kill seatd"); } @@ -186,7 +168,6 @@ int main(int argc, char *argv[]) { } error_seatd: - unlink(sockpath); kill(seatd_child, SIGTERM); error: return 1; diff --git a/seatd/client.c b/seatd/client.c index 220c5d3..481704e 100644 --- a/seatd/client.c +++ b/seatd/client.c @@ -14,6 +14,10 @@ #include #endif +#if defined(__NetBSD__) +#include +#endif + #include "client.h" #include "linked_list.h" #include "log.h" @@ -34,6 +38,40 @@ 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(__OpenBSD__) + struct sockpeercred peercred; + socklen_t len = sizeof(peercred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &peercred, &len) == -1) { + // assume builtin backend + if (errno == EINVAL) { + *pid = getpid(); + *uid = getuid(); + *gid = getgid(); + return 0; + } + return -1; + } + *pid = peercred.pid; + *uid = peercred.uid; + *gid = peercred.gid; + return 0; #elif defined(__FreeBSD__) struct xucred cred; socklen_t len = sizeof cred; @@ -49,7 +87,7 @@ static int get_peer(int fd, pid_t *pid, uid_t *uid, gid_t *gid) { *gid = cred.cr_ngroups > 0 ? cred.cr_groups[0] : (gid_t)-1; return 0; #else - return -1; +#error Unsupported platform #endif } @@ -81,6 +119,13 @@ struct client *client_create(struct server *server, int client_fd) { void client_destroy(struct client *client) { assert(client); + +#ifdef LIBSEAT + // The built-in backend version of seatd should terminate once its only + // client disconnects. + client->server->running = false; +#endif + client->server = NULL; if (client->connection.fd != -1) { close(client->connection.fd); @@ -282,7 +327,7 @@ static int handle_switch_session(struct client *client, int session) { log_error("Protocol error: no seat associated with client"); return -1; } - + log_debugf("handle_switch_session %d", session); if (seat_set_next_session(client, session) == -1) { goto error; } @@ -325,6 +370,7 @@ static int handle_ping(struct client *client) { static int client_handle_opcode(struct client *client, uint16_t opcode, size_t size) { int res = 0; + log_debugf("client_handle_opcode: %d\n", opcode); switch (opcode) { case CLIENT_OPEN_SEAT: { if (size != 0) { @@ -461,7 +507,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; } diff --git a/seatd/seat.c b/seatd/seat.c index 354273f..8820992 100644 --- a/seatd/seat.c +++ b/seatd/seat.c @@ -17,6 +17,7 @@ #include "protocol.h" #include "seat.h" #include "terminal.h" +#include "wscons.h" static int seat_close_client(struct client *client); static int vt_close(int vt); @@ -86,8 +87,8 @@ static int vt_close(int vt) { return -1; } terminal_set_process_switching(ttyfd, true); - terminal_set_keyboard(ttyfd, true); terminal_set_graphics(ttyfd, false); + terminal_set_keyboard(ttyfd, true); close(ttyfd); return 0; } @@ -106,6 +107,7 @@ static int vt_switch(struct seat *seat, int vt) { static int vt_ack(struct seat *seat, bool release) { int tty0fd = terminal_open(seat->cur_vt); + log_debugf("vt_ack VT %d %d\n", seat->cur_vt, release); if (tty0fd == -1) { log_errorf("Could not open tty0 to ack VT signal: %s", strerror(errno)); return -1; @@ -235,6 +237,8 @@ struct seat_device *seat_open_device(struct client *client, const char *path) { type = SEAT_DEVICE_TYPE_EVDEV; } else if (path_is_drm(sanitized_path)) { type = SEAT_DEVICE_TYPE_DRM; + } else if (path_is_wscons(sanitized_path)) { + type = SEAT_DEVICE_TYPE_WSCONS; } else { log_errorf("%s is not a supported device type ", sanitized_path); errno = ENOENT; @@ -281,6 +285,9 @@ struct seat_device *seat_open_device(struct client *client, const char *path) { case SEAT_DEVICE_TYPE_EVDEV: // Nothing to do here break; + case SEAT_DEVICE_TYPE_WSCONS: + // Nothing to do here + break; default: log_error("Invalid seat device type"); abort(); @@ -333,6 +340,9 @@ static int seat_deactivate_device(struct seat_device *seat_device) { return -1; } break; + case SEAT_DEVICE_TYPE_WSCONS: + // Nothing to do here + break; default: log_error("Invalid seat device type"); abort(); @@ -382,6 +392,9 @@ static int seat_activate_device(struct client *client, struct seat_device *seat_ case SEAT_DEVICE_TYPE_EVDEV: errno = EINVAL; return -1; + case SEAT_DEVICE_TYPE_WSCONS: + // Nothing to do here + break; default: log_error("Invalid seat device type"); abort(); @@ -525,7 +538,7 @@ static int seat_disable_client(struct client *client) { errno = EBUSY; return -1; } - assert(seat->active_client = client); + assert(seat->active_client == client); // We *deactivate* all remaining fds. These may later be reactivated. // The reason we cannot just close them is that certain device fds, such diff --git a/seatd/seatd.c b/seatd/seatd.c index 053d44b..f88e6c9 100644 --- a/seatd/seatd.c +++ b/seatd/seatd.c @@ -40,15 +40,15 @@ static int open_socket(const char *path, int uid, int gid) { goto error; } if (uid != -1 || gid != -1) { + if (chmod(path, 0770) == -1) { + log_errorf("Could not chmod socket: %s", strerror(errno)); + goto error; + } if (chown(path, uid, gid) == -1) { log_errorf("Could not chown socket to uid %d, gid %d: %s", uid, gid, strerror(errno)); goto error; } - if (chmod(path, 0770) == -1) { - log_errorf("Could not chmod socket: %s", strerror(errno)); - goto error; - } } return fd; error: @@ -57,35 +57,23 @@ error: } int main(int argc, char *argv[]) { - char *loglevel = getenv("SEATD_LOGLEVEL"); - enum libseat_log_level level = LIBSEAT_LOG_LEVEL_ERROR; - if (loglevel != NULL) { - if (strcmp(loglevel, "silent") == 0) { - level = LIBSEAT_LOG_LEVEL_SILENT; - } else if (strcmp(loglevel, "info") == 0) { - level = LIBSEAT_LOG_LEVEL_INFO; - } else if (strcmp(loglevel, "debug") == 0) { - level = LIBSEAT_LOG_LEVEL_DEBUG; - } - } - log_init(); - libseat_set_log_level(level); - const char *usage = "Usage: seatd [options]\n" "\n" " -h Show this help message\n" - " -n FD to notify readiness on\n" + " -n FD to notify readiness on\n" " -u User to own the seatd socket\n" " -g Group to own the seatd socket\n" - " -s Where to create the seatd socket\n" + " -l Log-level, one of debug, info, error or silent\n" " -v Show the version number\n" "\n"; int c; int uid = -1, gid = -1; int readiness = -1; - const char *socket_path = getenv("SEATD_SOCK"); - while ((c = getopt(argc, argv, "vhn:s:g:u:")) != -1) { + bool unlink_existing_socket = true; + bool chown_socket = true; + enum libseat_log_level level = LIBSEAT_LOG_LEVEL_INFO; + while ((c = getopt(argc, argv, "vhn:g:u:l:z")) != -1) { switch (c) { case 'n': readiness = atoi(optarg); @@ -94,10 +82,11 @@ int main(int argc, char *argv[]) { return 1; } break; - case 's': - socket_path = optarg; - break; case 'u': { + if (!chown_socket) { + fprintf(stderr, "-u/-g and -z are mutually exclusive\n"); + return 1; + } struct passwd *pw = getpwnam(optarg); if (pw == NULL) { fprintf(stderr, "Could not find user by name '%s'.\n", optarg); @@ -108,6 +97,10 @@ int main(int argc, char *argv[]) { break; } case 'g': { + if (!chown_socket) { + fprintf(stderr, "-u/-g and -z are mutually exclusive\n"); + return 1; + } struct group *gr = getgrnam(optarg); if (gr == NULL) { fprintf(stderr, "Could not find group by name '%s'.\n", optarg); @@ -117,6 +110,31 @@ int main(int argc, char *argv[]) { } break; } + case 'l': + if (strcmp(optarg, "debug") == 0) { + level = LIBSEAT_LOG_LEVEL_DEBUG; + } else if (strcmp(optarg, "info") == 0) { + level = LIBSEAT_LOG_LEVEL_INFO; + } else if (strcmp(optarg, "error") == 0) { + level = LIBSEAT_LOG_LEVEL_ERROR; + } else if (strcmp(optarg, "silent") == 0) { + level = LIBSEAT_LOG_LEVEL_SILENT; + } else { + fprintf(stderr, "Invalid loglevel: %s\n", optarg); + return 1; + } + break; + case 'z': + // Running under seatd-launch. We do not unlink files + // to protect against multiple instances, and + // seatd-launch takes care of ownership. + if (uid != -1 || gid != -1) { + fprintf(stderr, "-u/-g and -z are mutually exclusive\n"); + return 1; + } + unlink_existing_socket = false; + chown_socket = false; + break; case 'v': printf("seatd version %s\n", SEATD_VERSION); return 0; @@ -131,33 +149,46 @@ int main(int argc, char *argv[]) { } } - if (socket_path == NULL) { - socket_path = SEATD_DEFAULTPATH; - struct stat st; - if (stat(socket_path, &st) == 0) { - log_info("Removing leftover seatd socket"); - unlink(socket_path); + log_init(); + libseat_set_log_level(level); + + struct stat st; + if (lstat(SEATD_DEFAULTPATH, &st) == 0) { + if (!S_ISSOCK(st.st_mode)) { + log_errorf("Non-socket file found at socket path %s, refusing to start", + SEATD_DEFAULTPATH); + return 1; + } else if (!unlink_existing_socket) { + log_errorf("Socket file found at socket path %s, refusing to start", + SEATD_DEFAULTPATH); + return 1; + } else { + // We only do this if the socket path is not user specified + log_infof("Removing leftover socket at %s", SEATD_DEFAULTPATH); + if (unlink(SEATD_DEFAULTPATH) == -1) { + log_errorf("Could not remove leftover socket: %s", strerror(errno)); + return 1; + } } } struct server server = {0}; if (server_init(&server) == -1) { - log_errorf("server_create failed: %s", strerror(errno)); + log_errorf("server_init failed: %s", strerror(errno)); return 1; } - int socket_fd = open_socket(socket_path, uid, gid); + int ret = 1; + int socket_fd = open_socket(SEATD_DEFAULTPATH, uid, gid); if (socket_fd == -1) { log_error("Could not create server socket"); - server_finish(&server); - return 1; + goto error_server; } if (poller_add_fd(&server.poller, socket_fd, EVENT_READABLE, server_handle_connection, &server) == NULL) { log_errorf("Could not add socket to poller: %s", strerror(errno)); close(socket_fd); - server_finish(&server); - return 1; + goto error_socket; } log_info("seatd started"); @@ -172,12 +203,18 @@ int main(int argc, char *argv[]) { while (server.running) { if (poller_poll(&server.poller) == -1) { log_errorf("Poller failed: %s", strerror(errno)); - return 1; + goto error_socket; } } + ret = 0; + +error_socket: + if (unlink(SEATD_DEFAULTPATH) == -1) { + log_errorf("Could not remove socket: %s", strerror(errno)); + } +error_server: server_finish(&server); - unlink(socket_path); log_info("seatd stopped"); - return 0; + return ret; } diff --git a/seatd/server.c b/seatd/server.c index 0a08f50..47d6e91 100644 --- a/seatd/server.c +++ b/seatd/server.c @@ -123,12 +123,18 @@ static int set_nonblock(int fd) { int server_add_client(struct server *server, int fd) { if (set_nonblock(fd) != 0) { - close(fd); log_errorf("Could not prepare new client socket: %s", strerror(errno)); + close(fd); return -1; } struct client *client = client_create(server, fd); + if (client == NULL) { + log_errorf("Could not create client: %s", strerror(errno)); + close(fd); + return -1; + } + client->event_source = poller_add_fd(&server->poller, fd, EVENT_READABLE, client_handle_connection, client); if (client->event_source == NULL) { @@ -146,7 +152,7 @@ int server_handle_connection(int fd, uint32_t mask, void *data) { if (mask & (EVENT_ERROR | EVENT_HANGUP)) { shutdown(fd, SHUT_RDWR); server->running = false; - log_errorf("Server socket received an error: %s", strerror(errno)); + log_error("Server socket received an error"); return -1; } diff --git a/tests/poller.c b/tests/poller.c index 382d9d2..dd36c7d 100644 --- a/tests/poller.c +++ b/tests/poller.c @@ -123,6 +123,10 @@ static int test_signal_event(int signal, void *data) { return 0; } +#ifdef __OpenBSD__ +#define SIGRTMIN SIGUSR1 +#endif + static void test_poller_single_signal(void) { struct poller poller; test_assert(poller_init(&poller) == 0);