Compare commits
128 commits
Author | SHA1 | Date | |
---|---|---|---|
b4462cb033 | |||
cc09e26976 | |||
![]() |
0746edbeae | ||
![]() |
3e9ef69f14 | ||
a8aee6fa70 | |||
4b2ecdf936 | |||
f2ff233c26 | |||
dbaa859f28 | |||
![]() |
1bd042e5b0 | ||
![]() |
56720a6275 | ||
![]() |
e5b018def8 | ||
![]() |
3e0d510b2c | ||
![]() |
207e2a5936 | ||
![]() |
9b8b6e0bf8 | ||
![]() |
14355639f8 | ||
![]() |
a803ba0502 | ||
![]() |
6888653a8d | ||
![]() |
2842f0e2b1 | ||
![]() |
85d0bf5943 | ||
![]() |
8f8c9558e6 | ||
![]() |
0462e9331d | ||
![]() |
684dd61945 | ||
![]() |
d5539dead8 | ||
![]() |
845256009b | ||
![]() |
a5f9a2a2c8 | ||
![]() |
4ad48cb305 | ||
![]() |
1990f9b034 | ||
![]() |
bb0efb65b3 | ||
![]() |
ce6a6b7d2e | ||
![]() |
8dc6a50d88 | ||
![]() |
795cf169e7 | ||
![]() |
46c83972fe | ||
![]() |
abcecbb53b | ||
![]() |
ae42d05513 | ||
![]() |
0d6bdf4f01 | ||
![]() |
466efea49b | ||
![]() |
9bbdf0f0b8 | ||
![]() |
3eb0db57bb | ||
![]() |
ed90ed62cd | ||
![]() |
a44476ce65 | ||
![]() |
10658dc543 | ||
![]() |
32d06482d3 | ||
![]() |
0864f6a3ac | ||
![]() |
157ce68565 | ||
![]() |
f128359332 | ||
![]() |
b47c79d731 | ||
![]() |
96a5de8859 | ||
![]() |
936ff9dbea | ||
![]() |
f381e22955 | ||
![]() |
d92fa01f88 | ||
![]() |
69cf5c36e0 | ||
![]() |
88529f0856 | ||
![]() |
cb7a94378b | ||
![]() |
88db55f606 | ||
![]() |
e35c9cd02e | ||
![]() |
ec0d6565bb | ||
![]() |
8e5c00e7c8 | ||
![]() |
d2193b45ff | ||
![]() |
262ccef84e | ||
![]() |
1c376ca9b1 | ||
![]() |
e2baadc230 | ||
![]() |
db08fb921f | ||
![]() |
2eee9aa445 | ||
![]() |
0f20175752 | ||
![]() |
ebf512c2bf | ||
![]() |
907b75de1a | ||
![]() |
4091ba2c07 | ||
![]() |
66becee6da | ||
![]() |
4e3b7b3bb6 | ||
![]() |
fe600eac2b | ||
![]() |
8c85c46d2d | ||
![]() |
483dbf76fa | ||
![]() |
d5c1a7811b | ||
![]() |
da59bea775 | ||
![]() |
3ad9164a89 | ||
![]() |
d1c6bb9a15 | ||
![]() |
e7343ca96f | ||
![]() |
17cdbe0ad2 | ||
![]() |
60c370d4ec | ||
![]() |
29a6832ca0 | ||
![]() |
df3f307b8e | ||
![]() |
15b0972bd3 | ||
![]() |
038c30f9b1 | ||
![]() |
d78859bc9a | ||
![]() |
d9ae4c3010 | ||
![]() |
6e7a1db32d | ||
![]() |
166feaea33 | ||
![]() |
309650aa4d | ||
![]() |
2cfc56d5ed | ||
![]() |
48727a0b6b | ||
![]() |
369af8f9e4 | ||
![]() |
3a843745c2 | ||
![]() |
f2a614dcd3 | ||
![]() |
7d06b34ee2 | ||
![]() |
978dec42b0 | ||
![]() |
1e98727ae9 | ||
![]() |
c8b3a22d4e | ||
![]() |
312d6906ae | ||
![]() |
d03e9d1c35 | ||
![]() |
6444da6093 | ||
![]() |
5923e0edc9 | ||
![]() |
7a6d12ff7a | ||
![]() |
2204db5531 | ||
![]() |
bff09d8859 | ||
![]() |
fc97206df9 | ||
![]() |
355cc9c944 | ||
![]() |
36f54adc2c | ||
![]() |
5535c2c3b1 | ||
![]() |
81ff0a09a9 | ||
![]() |
5884a6003a | ||
![]() |
753c5276cf | ||
![]() |
ee40913810 | ||
![]() |
392da918e6 | ||
![]() |
385cc0039d | ||
![]() |
3ce4c57814 | ||
![]() |
50da164ddc | ||
![]() |
f9ba8b57bc | ||
![]() |
fa2700126f | ||
![]() |
9a7824b7c3 | ||
![]() |
5ad91ae9da | ||
![]() |
0d855a28f2 | ||
![]() |
34f55a3e24 | ||
![]() |
a9865adb5f | ||
![]() |
745d662920 | ||
![]() |
1f457b1df8 | ||
![]() |
4e65e1bf47 | ||
![]() |
75cb20e891 | ||
![]() |
e802d381a1 |
36 changed files with 1130 additions and 416 deletions
|
@ -10,18 +10,19 @@ sources:
|
|||
- https://git.sr.ht/~kennylevinsen/seatd
|
||||
tasks:
|
||||
- prepare: |
|
||||
meson -Dseatd=enabled -Dbuiltin=enabled -Dlogind=disabled build seatd
|
||||
meson -Dlibseat-seatd=enabled -Dlibseat-builtin=enabled -Dlibseat-logind=disabled -Dexamples=enabled build seatd
|
||||
- build: |
|
||||
ninja -C build
|
||||
sudo ninja -C build install
|
||||
- unittest: |
|
||||
ninja -C build test
|
||||
- scan-build: |
|
||||
ninja -C build scan-build
|
||||
[ -z "$(ls -A build/meson-logs/scanbuild/ 2>/dev/null)" ]
|
||||
- smoketest: |
|
||||
timeout -s KILL 30s ./seatd/.builds/smoketest-seatd.sh
|
||||
timeout -s KILL 30s sudo ./build/seatd-launch -l debug -- ./build/simpletest /dev/dri/card0
|
||||
- smoketest-builtin: |
|
||||
timeout -s KILL 30s ./seatd/.builds/smoketest-builtin.sh
|
||||
timeout -s KILL 30s sudo LIBSEAT_BACKEND=builtin ./build/simpletest /dev/dri/card0
|
||||
- check-format: |
|
||||
ninja -C build clang-format
|
||||
git -C seatd diff --exit-code
|
||||
|
|
|
@ -9,15 +9,22 @@ sources:
|
|||
- https://git.sr.ht/~kennylevinsen/seatd
|
||||
tasks:
|
||||
- prepare: |
|
||||
meson -Db_sanitize=address -Dlogind=enabled -Dseatd=enabled -Dbuiltin=enabled build seatd
|
||||
meson -Db_sanitize=address -Dlibseat-logind=auto -Dlibseat-seatd=enabled -Dlibseat-builtin=enabled -Dexamples=enabled build seatd
|
||||
- build: |
|
||||
ninja -C build
|
||||
sudo ninja -C build install
|
||||
- unittest: |
|
||||
ninja -C build test
|
||||
- scan-build: |
|
||||
ninja -C build scan-build
|
||||
[ -z "$(ls -A build/meson-logs/scanbuild/ 2>/dev/null)" ]
|
||||
- smoketest: |
|
||||
timeout -s KILL 30s ./seatd/.builds/smoketest-seatd.sh
|
||||
timeout -s KILL 30s sudo ./build/seatd-launch -l debug -- ./build/simpletest /dev/input/event0
|
||||
- smoketest-builtin: |
|
||||
timeout -s KILL 30s ./seatd/.builds/smoketest-builtin.sh
|
||||
timeout -s KILL 30s sudo LIBSEAT_BACKEND=builtin ./build/simpletest /dev/input/event0
|
||||
- smoketest-logind: |
|
||||
# Turn off systemd-logind and patch our session to be tied to seat0 on VT 6
|
||||
sudo systemctl stop systemd-logind
|
||||
echo -e "ACTIVE=$XDG_SESSION_ID\nACTIVE_UID=$UID\nSESSIONS=$XDG_SESSION_ID\nUIDS=$UID\n" | sudo tee -a /run/systemd/seats/seat0 > /dev/null
|
||||
echo -e "SEAT=seat0\nVTNR=6\n" | sudo tee -a /run/systemd/sessions/$XDG_SESSION_ID > /dev/null
|
||||
timeout -s KILL 30s sudo LIBSEAT_BACKEND=logind ./build/simpletest /dev/input/event0
|
||||
|
|
|
@ -5,15 +5,16 @@ sources:
|
|||
- https://git.sr.ht/~kennylevinsen/seatd
|
||||
tasks:
|
||||
- prepare: |
|
||||
meson -Dseatd=enabled -Dbuiltin=enabled -Dlogind=disabled build seatd
|
||||
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 -Dseatd=enabled -Dbuiltin=enabled -Dlogind=disabled build seatd
|
||||
meson -Db_lundef=false -Db_sanitize=address -Dlibseat-seatd=enabled -Dlibseat-builtin=enabled -Dexamples=enabled -Dlibseat-logind=disabled build seatd
|
||||
ninja -C build
|
||||
timeout -s KILL 30s ./seatd/.builds/smoketest-seatd.sh
|
||||
sudo ninja -C build install
|
||||
timeout -s KILL 30s sudo ./build/seatd-launch -l debug -- ./build/simpletest /dev/input/event0
|
||||
- smoketest-builtin: |
|
||||
timeout -s KILL 30s ./seatd/.builds/smoketest-builtin.sh
|
||||
timeout -s KILL 30s sudo LIBSEAT_BACKEND=builtin ./build/simpletest /dev/input/event0
|
||||
|
|
24
.builds/netbsd.yml
Normal file
24
.builds/netbsd.yml
Normal file
|
@ -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
|
|
@ -1,30 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Devices that exist on sr.ht
|
||||
if [ -e "/dev/input/event0" ]
|
||||
then
|
||||
file="/dev/input/event0"
|
||||
elif [ -e "/dev/dri/card0" ]
|
||||
then
|
||||
file="/dev/dri/card0"
|
||||
else
|
||||
echo "No useful device file found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#
|
||||
# Run simpletest a few times
|
||||
#
|
||||
cnt=0
|
||||
while [ "$cnt" -lt 5 ]
|
||||
do
|
||||
echo "Simpletest run $cnt"
|
||||
if ! sudo LIBSEAT_BACKEND=builtin LIBSEAT_LOGLEVEL=debug SEATD_SOCK=./seatd.sock ./build/simpletest $file
|
||||
then
|
||||
echo "Simpletest failed"
|
||||
exit $res
|
||||
fi
|
||||
cnt=$((cnt+1))
|
||||
done
|
||||
|
||||
echo "smoketest-builtin completed"
|
|
@ -1,58 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Start seatd
|
||||
#
|
||||
[ -f seatd.sock ] && sudo rm seatd.sock
|
||||
sudo SEATD_LOGLEVEL=debug SEATD_SOCK=./seatd.sock ./build/seatd &
|
||||
|
||||
# seatd is started in the background, so wait for it to come alive
|
||||
cnt=0
|
||||
while ! [ -e ./seatd.sock ] && [ "$cnt" -lt 10 ]
|
||||
do
|
||||
sleep 0.1
|
||||
cnt=$((cnt+1))
|
||||
done
|
||||
|
||||
if ! [ -e ./seatd.sock ]
|
||||
then
|
||||
echo "seatd socket not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sudo chmod 777 ./seatd.sock
|
||||
|
||||
# Devices that exist on sr.ht
|
||||
if [ -e "/dev/input/event0" ]
|
||||
then
|
||||
file="/dev/input/event0"
|
||||
elif [ -e "/dev/dri/card0" ]
|
||||
then
|
||||
file="/dev/dri/card0"
|
||||
else
|
||||
echo "No useful device file found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#
|
||||
# Run simpletest a few times
|
||||
#
|
||||
cnt=0
|
||||
while [ "$cnt" -lt 5 ]
|
||||
do
|
||||
echo "Simpletest run $cnt"
|
||||
if ! LIBSEAT_LOGLEVEL=debug SEATD_SOCK=./seatd.sock ./build/simpletest $file
|
||||
then
|
||||
echo "Simpletest failed"
|
||||
sudo killall seatd
|
||||
exit 1
|
||||
fi
|
||||
cnt=$((cnt+1))
|
||||
done
|
||||
|
||||
#
|
||||
# Wait for it to shut down
|
||||
#
|
||||
sudo killall seatd 2>/dev/null
|
||||
|
||||
echo "smoketest-seatd completed"
|
|
@ -2,7 +2,7 @@
|
|||
IndentWidth: 8
|
||||
TabWidth: 8
|
||||
ContinuationIndentWidth: 8
|
||||
UseTab: Always
|
||||
UseTab: ForContinuationAndIndentation
|
||||
ColumnLimit: 100
|
||||
|
||||
AlignConsecutiveMacros: true
|
||||
|
|
8
.editorconfig
Normal file
8
.editorconfig
Normal file
|
@ -0,0 +1,8 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = tab
|
||||
indent_size = 8
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
A minimal seat management daemon, and a universal seat management library.
|
||||
|
||||
Currently supports Linux and FreeBSD. Alpha
|
||||
Currently supports Linux and FreeBSD, and has experimental NetBSD support.
|
||||
|
||||
## What is seat management?
|
||||
|
||||
|
@ -12,7 +12,7 @@ Seat management takes care of mediating access to shared devices (graphics, inpu
|
|||
|
||||
### seatd
|
||||
|
||||
A seat management deamon, that does everything it needs to do. Nothing more, nothing less. Depends only on libc.
|
||||
A seat management daemon, that does everything it needs to do. Nothing more, nothing less. Depends only on libc.
|
||||
|
||||
### libseat
|
||||
|
||||
|
@ -41,6 +41,6 @@ Why spend time isolating logind and keeping up with upstream when we could inste
|
|||
|
||||
Instead of giving user shell developers more work, libseat aims to make supporting seatd less work than what they're currently implementing. This is done by taking care of all the seat management needs with multiple backends, providing not only seatd support, but replacing the existing logind and direct seat management implementations.
|
||||
|
||||
## I want more
|
||||
## How to discuss
|
||||
|
||||
Go to #kennylevinsen @ chat.freenode.net 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).
|
||||
|
|
26
common/drm.c
26
common/drm.c
|
@ -2,10 +2,6 @@
|
|||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/sysmacros.h>
|
||||
#endif
|
||||
|
||||
#include "drm.h"
|
||||
|
||||
// From libdrm
|
||||
|
@ -15,6 +11,7 @@
|
|||
#define DRM_IOCTL_DROP_MASTER DRM_IO(0x1f)
|
||||
|
||||
#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
|
||||
|
|
|
@ -9,14 +9,13 @@
|
|||
#include <sys/sysmacros.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <dev/evdev/input.h>
|
||||
#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
|
||||
|
|
|
@ -21,6 +21,17 @@
|
|||
#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
|
||||
#elif defined(__OpenBSD__)
|
||||
#include <dev/wscons/wsconsio.h>
|
||||
#include <dev/wscons/wsdisplay_usl_io.h>
|
||||
#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;
|
||||
}
|
||||
|
|
27
common/wscons.c
Normal file
27
common/wscons.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#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
|
|
@ -1,9 +1,11 @@
|
|||
[Unit]
|
||||
Description=Seat management daemon
|
||||
Documentation=man:seatd(1)
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=seatd -g video
|
||||
# Specify the group you'd like to grant access to seatd
|
||||
ExecStart=seatd -g seat
|
||||
Restart=always
|
||||
RestartSec=1
|
||||
|
||||
|
|
|
@ -43,7 +43,11 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
while (active == 0) {
|
||||
fprintf(stderr, "waiting for activation...\n");
|
||||
libseat_dispatch(backend, -1);
|
||||
if (libseat_dispatch(backend, -1) == -1) {
|
||||
libseat_close_seat(backend);
|
||||
fprintf(stderr, "libseat_dispatch() failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "active!\n");
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ struct named_backend {
|
|||
};
|
||||
|
||||
struct seat_impl {
|
||||
struct libseat *(*open_seat)(struct libseat_seat_listener *listener, void *data);
|
||||
struct libseat *(*open_seat)(const struct libseat_seat_listener *listener, void *data);
|
||||
int (*disable_seat)(struct libseat *seat);
|
||||
int (*close_seat)(struct libseat *seat);
|
||||
const char *(*seat_name)(struct libseat *seat);
|
||||
|
|
|
@ -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 <sys/types.h>
|
||||
int dev_is_drm(dev_t device);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,9 +4,4 @@
|
|||
int evdev_revoke(int fd);
|
||||
int path_is_evdev(const char *path);
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/types.h>
|
||||
int dev_is_evdev(dev_t device);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef _LIBSEAT_H
|
||||
#define _LIBSEAT_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
/*
|
||||
* An opaque struct containing an opened seat, created by libseat_open_seat and
|
||||
* destroyed by libseat_close_seat.
|
||||
|
@ -45,15 +47,17 @@ struct libseat_seat_listener {
|
|||
* The available backends, if enabled at compile-time, are: seatd, logind and
|
||||
* builtin.
|
||||
*
|
||||
* To use builtin, the process must have CAP_SYS_ADMIN or be root at the time
|
||||
* of the call. These privileges can be dropped at any point after the call.
|
||||
* To use builtin, the process must have permission to open and use the seat's
|
||||
* devices at the time of the call. In the case of DRM devices, this includes
|
||||
* permission for drmSetMaster(3). These privileges can be dropped at any
|
||||
* point after the call.
|
||||
*
|
||||
* The returned pointer must be destroyed with libseat_close_seat.
|
||||
*
|
||||
* Returns a pointer to an opaque libseat struct on success. Returns NULL and
|
||||
* sets errno on error.
|
||||
*/
|
||||
struct libseat *libseat_open_seat(struct libseat_seat_listener *listener, void *userdata);
|
||||
struct libseat *libseat_open_seat(const struct libseat_seat_listener *listener, void *userdata);
|
||||
|
||||
/*
|
||||
* Disables a seat, used in response to a disable_seat event. After disabling
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#define CLIENT_CLOSE_DEVICE CLIENT_EVENT(4)
|
||||
#define CLIENT_DISABLE_SEAT CLIENT_EVENT(5)
|
||||
#define CLIENT_SWITCH_SESSION CLIENT_EVENT(6)
|
||||
#define CLIENT_PING CLIENT_EVENT(7)
|
||||
|
||||
#define SERVER_SEAT_OPENED SERVER_EVENT(1)
|
||||
#define SERVER_SEAT_CLOSED SERVER_EVENT(2)
|
||||
|
@ -22,6 +23,7 @@
|
|||
#define SERVER_DEVICE_CLOSED SERVER_EVENT(4)
|
||||
#define SERVER_DISABLE_SEAT SERVER_EVENT(5)
|
||||
#define SERVER_ENABLE_SEAT SERVER_EVENT(6)
|
||||
#define SERVER_PONG SERVER_EVENT(7)
|
||||
#define SERVER_ERROR SERVER_EVENT(0x7FFF)
|
||||
|
||||
#include <stdint.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 {
|
||||
|
@ -33,7 +34,6 @@ struct seat {
|
|||
struct client *next_client;
|
||||
|
||||
bool vt_bound;
|
||||
int cur_ttyfd;
|
||||
int cur_vt;
|
||||
int session_cnt;
|
||||
};
|
||||
|
|
6
include/wscons.h
Normal file
6
include/wscons.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#ifndef _SEATD_WSCONS_H
|
||||
#define _SEATD_WSCONS_H
|
||||
|
||||
int path_is_wscons(const char *path);
|
||||
|
||||
#endif
|
|
@ -13,10 +13,10 @@
|
|||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(HAVE_ELOGIND)
|
||||
#if defined(HAVE_LIBELOGIND)
|
||||
#include <elogind/sd-bus.h>
|
||||
#include <elogind/sd-login.h>
|
||||
#elif defined(HAVE_SYSTEMD)
|
||||
#elif defined(HAVE_LIBSYSTEMD)
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <systemd/sd-login.h>
|
||||
#else
|
||||
|
@ -28,9 +28,17 @@
|
|||
#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;
|
||||
struct libseat_seat_listener *seat_listener;
|
||||
const struct libseat_seat_listener *seat_listener;
|
||||
void *seat_listener_data;
|
||||
|
||||
sd_bus *bus;
|
||||
|
@ -67,6 +75,47 @@ static int close_seat(struct libseat *base) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ping_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
|
||||
(void)ret_error;
|
||||
(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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_ping(struct backend_logind *backend) {
|
||||
int ret = sd_bus_call_method_async(backend->bus, NULL, "org.freedesktop.login1",
|
||||
"/org/freedesktop/login1", "org.freedesktop.DBus.Peer",
|
||||
"Ping", ping_handler, backend, "");
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_pending_events(struct backend_logind *backend) {
|
||||
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;
|
||||
}
|
||||
|
||||
// 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.
|
||||
int ret = send_ping(backend);
|
||||
if (ret < 0) {
|
||||
log_errorf("Could not send ping message: %s", strerror(-ret));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int open_device(struct libseat *base, const char *path, int *fd) {
|
||||
struct backend_logind *session = backend_logind_from_libseat_backend(base);
|
||||
|
||||
|
@ -113,9 +162,11 @@ static int open_device(struct libseat *base, const char *path, int *fd) {
|
|||
}
|
||||
|
||||
*fd = tmpfd;
|
||||
|
||||
out:
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
check_pending_events(session);
|
||||
return tmpfd;
|
||||
}
|
||||
|
||||
|
@ -150,7 +201,7 @@ static int close_device(struct libseat *base, int device_id) {
|
|||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
|
||||
check_pending_events(session);
|
||||
return ret < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
|
@ -173,6 +224,7 @@ static int switch_session(struct libseat *base, int s) {
|
|||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
check_pending_events(session);
|
||||
return ret < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
|
@ -213,7 +265,7 @@ static int poll_connection(struct backend_logind *backend, int timeout) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dispatch_background(struct libseat *base, int timeout) {
|
||||
static int dispatch_and_execute(struct libseat *base, int timeout) {
|
||||
struct backend_logind *backend = backend_logind_from_libseat_backend(base);
|
||||
if (backend->initial_setup) {
|
||||
backend->initial_setup = false;
|
||||
|
@ -240,15 +292,12 @@ static int dispatch_background(struct libseat *base, int timeout) {
|
|||
total_dispatched += dispatched;
|
||||
}
|
||||
}
|
||||
check_pending_events(backend);
|
||||
return total_dispatched;
|
||||
}
|
||||
|
||||
static const char *seat_name(struct libseat *base) {
|
||||
struct backend_logind *backend = backend_logind_from_libseat_backend(base);
|
||||
|
||||
if (backend->seat == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return backend->seat;
|
||||
}
|
||||
|
||||
|
@ -257,10 +306,11 @@ static struct backend_logind *backend_logind_from_libseat_backend(struct libseat
|
|||
return (struct backend_logind *)base;
|
||||
}
|
||||
|
||||
static bool session_activate(struct backend_logind *session) {
|
||||
static int session_activate(struct backend_logind *session) {
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
// Note: the Activate call might not make the session active immediately
|
||||
int ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", session->path,
|
||||
"org.freedesktop.login1.Session", "Activate", &error, &msg, "");
|
||||
if (ret < 0) {
|
||||
|
@ -269,10 +319,26 @@ static bool session_activate(struct backend_logind *session) {
|
|||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
return ret >= 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool take_control(struct backend_logind *session) {
|
||||
static int session_check_active(struct backend_logind *session) {
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
int active = 0;
|
||||
int ret = sd_bus_get_property_trivial(session->bus, "org.freedesktop.login1", session->path,
|
||||
"org.freedesktop.login1.Session", "Active", &error,
|
||||
'b', &active);
|
||||
if (ret < 0) {
|
||||
log_errorf("Could not check if session is active: %s", error.message);
|
||||
} else {
|
||||
session->active = (bool)active;
|
||||
}
|
||||
|
||||
sd_bus_error_free(&error);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int take_control(struct backend_logind *session) {
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
|
@ -285,7 +351,7 @@ static bool take_control(struct backend_logind *session) {
|
|||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
return ret >= 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void release_control(struct backend_logind *session) {
|
||||
|
@ -329,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);
|
||||
|
@ -361,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);
|
||||
|
@ -471,7 +537,7 @@ error:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool add_signal_matches(struct backend_logind *backend) {
|
||||
static int add_signal_matches(struct backend_logind *backend) {
|
||||
static const char *logind = "org.freedesktop.login1";
|
||||
static const char *session_interface = "org.freedesktop.login1.Session";
|
||||
static const char *property_interface = "org.freedesktop.DBus.Properties";
|
||||
|
@ -481,34 +547,34 @@ static bool add_signal_matches(struct backend_logind *backend) {
|
|||
"PauseDevice", pause_device, backend);
|
||||
if (ret < 0) {
|
||||
log_errorf("Could not add D-Bus match: %s", strerror(-ret));
|
||||
return false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sd_bus_match_signal(backend->bus, NULL, logind, backend->path, session_interface,
|
||||
"ResumeDevice", resume_device, backend);
|
||||
if (ret < 0) {
|
||||
log_errorf("Could not add D-Bus match: %s", strerror(-ret));
|
||||
return false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sd_bus_match_signal(backend->bus, NULL, logind, backend->path, property_interface,
|
||||
"PropertiesChanged", properties_changed, backend);
|
||||
if (ret < 0) {
|
||||
log_errorf("Could not add D-Bus match: %s", strerror(-ret));
|
||||
return false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sd_bus_match_signal(backend->bus, NULL, logind, backend->seat_path, property_interface,
|
||||
"PropertiesChanged", properties_changed, backend);
|
||||
if (ret < 0) {
|
||||
log_errorf("Could not add D-Bus match: %s", strerror(-ret));
|
||||
return false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool find_session_path(struct backend_logind *session) {
|
||||
static int find_session_path(struct backend_logind *session) {
|
||||
int ret;
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
@ -533,10 +599,10 @@ out:
|
|||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
|
||||
return ret >= 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool find_seat_path(struct backend_logind *session) {
|
||||
static int find_seat_path(struct backend_logind *session) {
|
||||
int ret;
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
@ -561,10 +627,10 @@ out:
|
|||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
|
||||
return ret >= 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool get_display_session(char **session_id) {
|
||||
static int get_display_session(char **session_id) {
|
||||
assert(session_id != NULL);
|
||||
char *xdg_session_id = getenv("XDG_SESSION_ID");
|
||||
int ret;
|
||||
|
@ -597,12 +663,12 @@ static bool get_display_session(char **session_id) {
|
|||
|
||||
success:
|
||||
assert(*session_id != NULL);
|
||||
return true;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
free(*session_id);
|
||||
*session_id = NULL;
|
||||
return false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_type(struct backend_logind *backend, const char *type) {
|
||||
|
@ -621,17 +687,19 @@ static int set_type(struct backend_logind *backend, const char *type) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct libseat *logind_open_seat(struct libseat_seat_listener *listener, void *data) {
|
||||
static struct libseat *logind_open_seat(const struct libseat_seat_listener *listener, void *data) {
|
||||
struct backend_logind *backend = calloc(1, sizeof(struct backend_logind));
|
||||
if (backend == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!get_display_session(&backend->id)) {
|
||||
int ret;
|
||||
ret = get_display_session(&backend->id);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
int ret = sd_session_get_seat(backend->id, &backend->seat);
|
||||
ret = sd_session_get_seat(backend->id, &backend->seat);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
@ -641,23 +709,33 @@ static struct libseat *logind_open_seat(struct libseat_seat_listener *listener,
|
|||
goto error;
|
||||
}
|
||||
|
||||
if (!find_session_path(backend)) {
|
||||
ret = find_session_path(backend);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!find_seat_path(backend)) {
|
||||
ret = find_seat_path(backend);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!add_signal_matches(backend)) {
|
||||
ret = add_signal_matches(backend);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!session_activate(backend)) {
|
||||
ret = session_activate(backend);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!take_control(backend)) {
|
||||
ret = session_check_active(backend);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = take_control(backend);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
@ -667,15 +745,16 @@ static struct libseat *logind_open_seat(struct libseat_seat_listener *listener,
|
|||
}
|
||||
|
||||
backend->initial_setup = true;
|
||||
backend->active = true;
|
||||
backend->seat_listener = listener;
|
||||
backend->seat_listener_data = data;
|
||||
backend->base.impl = &logind_impl;
|
||||
|
||||
check_pending_events(backend);
|
||||
return &backend->base;
|
||||
|
||||
error:
|
||||
destroy(backend);
|
||||
errno = -ret;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -688,5 +767,5 @@ const struct seat_impl logind_impl = {
|
|||
.close_device = close_device,
|
||||
.switch_session = switch_session,
|
||||
.get_fd = get_fd,
|
||||
.dispatch = dispatch_background,
|
||||
.dispatch = dispatch_and_execute,
|
||||
};
|
||||
|
|
136
libseat/backend/noop.c
Normal file
136
libseat/backend/noop.c
Normal file
|
@ -0,0 +1,136 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "backend.h"
|
||||
#include "log.h"
|
||||
|
||||
struct backend_noop {
|
||||
struct libseat base;
|
||||
const struct libseat_seat_listener *seat_listener;
|
||||
void *seat_listener_data;
|
||||
|
||||
bool initial_setup;
|
||||
int sockets[2];
|
||||
};
|
||||
|
||||
extern const struct seat_impl noop_impl;
|
||||
|
||||
static struct backend_noop *backend_noop_from_libseat_backend(struct libseat *base) {
|
||||
assert(base->impl == &noop_impl);
|
||||
return (struct backend_noop *)base;
|
||||
}
|
||||
|
||||
static void destroy(struct backend_noop *backend) {
|
||||
close(backend->sockets[0]);
|
||||
close(backend->sockets[1]);
|
||||
free(backend);
|
||||
}
|
||||
|
||||
static int close_seat(struct libseat *base) {
|
||||
struct backend_noop *backend = backend_noop_from_libseat_backend(base);
|
||||
destroy(backend);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disable_seat(struct libseat *base) {
|
||||
(void)base;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *seat_name(struct libseat *base) {
|
||||
(void)base;
|
||||
return "seat0";
|
||||
}
|
||||
|
||||
static int open_device(struct libseat *base, const char *path, int *fd) {
|
||||
(void)base;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
*fd = tmpfd;
|
||||
return tmpfd;
|
||||
}
|
||||
|
||||
static int close_device(struct libseat *base, int device_id) {
|
||||
(void)base;
|
||||
(void)device_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int switch_session(struct libseat *base, int s) {
|
||||
(void)base;
|
||||
(void)s;
|
||||
log_errorf("No-op backend cannot switch to session %d", s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int get_fd(struct libseat *base) {
|
||||
struct backend_noop *backend = backend_noop_from_libseat_backend(base);
|
||||
return backend->sockets[0];
|
||||
}
|
||||
|
||||
static int dispatch_background(struct libseat *base, int timeout) {
|
||||
struct backend_noop *backend = backend_noop_from_libseat_backend(base);
|
||||
|
||||
if (backend->initial_setup) {
|
||||
backend->initial_setup = false;
|
||||
backend->seat_listener->enable_seat(&backend->base, backend->seat_listener_data);
|
||||
}
|
||||
|
||||
struct pollfd fd = {
|
||||
.fd = backend->sockets[0],
|
||||
.events = POLLIN,
|
||||
};
|
||||
if (poll(&fd, 1, timeout) < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct libseat *noop_open_seat(const struct libseat_seat_listener *listener, void *data) {
|
||||
struct backend_noop *backend = calloc(1, sizeof(struct backend_noop));
|
||||
if (backend == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return &backend->base;
|
||||
}
|
||||
|
||||
const struct seat_impl noop_impl = {
|
||||
.open_seat = noop_open_seat,
|
||||
.disable_seat = disable_seat,
|
||||
.close_seat = close_seat,
|
||||
.seat_name = seat_name,
|
||||
.open_device = open_device,
|
||||
.close_device = close_device,
|
||||
.switch_session = switch_session,
|
||||
.get_fd = get_fd,
|
||||
.dispatch = dispatch_background,
|
||||
};
|
|
@ -33,40 +33,25 @@ struct pending_event {
|
|||
struct backend_seatd {
|
||||
struct libseat base;
|
||||
struct connection connection;
|
||||
struct libseat_seat_listener *seat_listener;
|
||||
const struct libseat_seat_listener *seat_listener;
|
||||
void *seat_listener_data;
|
||||
struct linked_list pending_events;
|
||||
bool awaiting_pong;
|
||||
bool error;
|
||||
|
||||
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;
|
||||
|
@ -75,7 +60,11 @@ static int seatd_connect(void) {
|
|||
strncpy(addr.unix.sun_path, path, sizeof addr.unix.sun_path - 1);
|
||||
socklen_t size = offsetof(struct sockaddr_un, sun_path) + strlen(addr.unix.sun_path);
|
||||
if (connect(fd, &addr.generic, size) == -1) {
|
||||
if (errno == ENOENT) {
|
||||
log_infof("Could not connect to socket %s: %s", path, strerror(errno));
|
||||
} else {
|
||||
log_errorf("Could not connect to socket %s: %s", path, strerror(errno));
|
||||
}
|
||||
close(fd);
|
||||
return -1;
|
||||
};
|
||||
|
@ -202,10 +191,11 @@ static int queue_event(struct backend_seatd *backend, int opcode) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void execute_events(struct backend_seatd *backend) {
|
||||
static int execute_events(struct backend_seatd *backend) {
|
||||
struct linked_list list;
|
||||
linked_list_init(&list);
|
||||
linked_list_take(&list, &backend->pending_events);
|
||||
int executed = 0;
|
||||
while (!linked_list_empty(&list)) {
|
||||
struct pending_event *ev = (struct pending_event *)list.next;
|
||||
int opcode = ev->opcode;
|
||||
|
@ -227,7 +217,9 @@ static void execute_events(struct backend_seatd *backend) {
|
|||
log_errorf("Invalid opcode: %d", opcode);
|
||||
abort();
|
||||
}
|
||||
executed++;
|
||||
}
|
||||
return executed;
|
||||
}
|
||||
|
||||
static int dispatch_pending(struct backend_seatd *backend, int *opcode) {
|
||||
|
@ -236,6 +228,12 @@ static int dispatch_pending(struct backend_seatd *backend, int *opcode) {
|
|||
while (connection_get(&backend->connection, &header, sizeof header) != -1) {
|
||||
packets++;
|
||||
switch (header.opcode) {
|
||||
case SERVER_PONG:
|
||||
// We care about whether or not the answer has been
|
||||
// read from the connection, so handle it here instead
|
||||
// of pushing it to the pending event list.
|
||||
backend->awaiting_pong = false;
|
||||
break;
|
||||
case SERVER_DISABLE_SEAT:
|
||||
case SERVER_ENABLE_SEAT:
|
||||
if (queue_event(backend, header.opcode) == -1) {
|
||||
|
@ -255,6 +253,15 @@ static int dispatch_pending(struct backend_seatd *backend, int *opcode) {
|
|||
return packets;
|
||||
}
|
||||
|
||||
static int dispatch_pending_and_execute(struct backend_seatd *backend) {
|
||||
int dispatched = dispatch_pending(backend, NULL);
|
||||
if (dispatched == -1) {
|
||||
return -1;
|
||||
}
|
||||
dispatched += execute_events(backend);
|
||||
return dispatched;
|
||||
}
|
||||
|
||||
static int poll_connection(struct backend_seatd *backend, int timeout) {
|
||||
struct pollfd fd = {
|
||||
.fd = backend->connection.fd,
|
||||
|
@ -310,38 +317,44 @@ static int get_fd(struct libseat *base) {
|
|||
return backend->connection.fd;
|
||||
}
|
||||
|
||||
static int dispatch_background(struct libseat *base, int timeout) {
|
||||
static int dispatch_and_execute(struct libseat *base, int timeout) {
|
||||
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
|
||||
if (backend->error) {
|
||||
errno = ENOTCONN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dispatched = dispatch_pending(backend, NULL);
|
||||
if (dispatched > 0) {
|
||||
int predispatch = dispatch_pending_and_execute(backend);
|
||||
if (predispatch == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We don't want to block if we dispatched something, as the
|
||||
// caller might be waiting for the result. However, we'd also
|
||||
// like to read anything pending.
|
||||
timeout = 0;
|
||||
}
|
||||
int read = 0;
|
||||
if (timeout == 0) {
|
||||
if (predispatch > 0 || timeout == 0) {
|
||||
read = connection_read(&backend->connection);
|
||||
} else {
|
||||
read = poll_connection(backend, timeout);
|
||||
}
|
||||
if (read > 0) {
|
||||
dispatched += dispatch_pending(backend, NULL);
|
||||
|
||||
if (read == 0) {
|
||||
return predispatch;
|
||||
} else if (read == -1 && errno != EAGAIN) {
|
||||
log_errorf("Could not read from connection: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
execute_events(backend);
|
||||
return dispatched;
|
||||
int postdispatch = dispatch_pending_and_execute(backend);
|
||||
if (postdispatch == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct libseat *_open_seat(struct libseat_seat_listener *listener, void *data, int fd) {
|
||||
return predispatch + postdispatch;
|
||||
}
|
||||
|
||||
static struct libseat *_open_seat(const struct libseat_seat_listener *listener, void *data, int fd) {
|
||||
assert(listener != NULL);
|
||||
assert(listener->enable_seat != NULL && listener->disable_seat != NULL);
|
||||
struct backend_seatd *backend = calloc(1, sizeof(struct backend_seatd));
|
||||
|
@ -389,7 +402,7 @@ alloc_error:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static struct libseat *open_seat(struct libseat_seat_listener *listener, void *data) {
|
||||
static struct libseat *open_seat(const struct libseat_seat_listener *listener, void *data) {
|
||||
int fd = seatd_connect();
|
||||
if (fd == -1) {
|
||||
return NULL;
|
||||
|
@ -428,6 +441,36 @@ static const char *seat_name(struct libseat *base) {
|
|||
return backend->seat_name;
|
||||
}
|
||||
|
||||
static int send_ping(struct backend_seatd *backend) {
|
||||
struct proto_header header = {
|
||||
.opcode = CLIENT_PING,
|
||||
.size = 0,
|
||||
};
|
||||
if (conn_put(backend, &header, sizeof header) == -1 || conn_flush(backend) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_pending_events(struct backend_seatd *backend) {
|
||||
if (linked_list_empty(&backend->pending_events)) {
|
||||
return;
|
||||
}
|
||||
if (backend->awaiting_pong) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We have events pending execution, 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 seatd, so that the
|
||||
// user will be woken up by its response.
|
||||
if (send_ping(backend) == -1) {
|
||||
log_errorf("Could not send ping request: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
backend->awaiting_pong = true;
|
||||
}
|
||||
|
||||
static int open_device(struct libseat *base, const char *path, int *fd) {
|
||||
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
|
||||
if (backend->error) {
|
||||
|
@ -459,11 +502,11 @@ static int open_device(struct libseat *base, const char *path, int *fd) {
|
|||
goto error;
|
||||
}
|
||||
|
||||
execute_events(backend);
|
||||
check_pending_events(backend);
|
||||
return rmsg.device_id;
|
||||
|
||||
error:
|
||||
execute_events(backend);
|
||||
check_pending_events(backend);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -494,11 +537,11 @@ static int close_device(struct libseat *base, int device_id) {
|
|||
goto error;
|
||||
}
|
||||
|
||||
execute_events(backend);
|
||||
check_pending_events(backend);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
execute_events(backend);
|
||||
check_pending_events(backend);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -553,42 +596,18 @@ const struct seat_impl seatd_impl = {
|
|||
.close_device = close_device,
|
||||
.switch_session = switch_session,
|
||||
.get_fd = get_fd,
|
||||
.dispatch = dispatch_background,
|
||||
.dispatch = dispatch_and_execute,
|
||||
};
|
||||
|
||||
#ifdef BUILTIN_ENABLED
|
||||
#include <signal.h>
|
||||
|
||||
static int set_deathsig(int signal);
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/prctl.h>
|
||||
|
||||
static int set_deathsig(int signal) {
|
||||
return prctl(PR_SET_PDEATHSIG, signal);
|
||||
}
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <sys/procctl.h>
|
||||
|
||||
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(struct libseat_seat_listener *listener, void *data) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (geteuid() != 0) {
|
||||
log_error("Built-in seatd instance requires root privileges");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
log_errorf("Could not fork: %s", strerror(errno));
|
||||
|
@ -596,6 +615,7 @@ static struct libseat *builtin_open_seat(struct libseat_seat_listener *listener,
|
|||
close(fds[1]);
|
||||
return NULL;
|
||||
} else if (pid == 0) {
|
||||
close(fds[1]);
|
||||
int fd = fds[0];
|
||||
int res = 0;
|
||||
struct server server = {0};
|
||||
|
@ -610,7 +630,7 @@ static struct libseat *builtin_open_seat(struct libseat_seat_listener *listener,
|
|||
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));
|
||||
|
@ -622,8 +642,10 @@ static struct libseat *builtin_open_seat(struct libseat_seat_listener *listener,
|
|||
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);
|
||||
}
|
||||
|
@ -638,6 +660,6 @@ const struct seat_impl builtin_impl = {
|
|||
.close_device = close_device,
|
||||
.switch_session = switch_session,
|
||||
.get_fd = get_fd,
|
||||
.dispatch = dispatch_background,
|
||||
.dispatch = dispatch_and_execute,
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
extern const struct seat_impl seatd_impl;
|
||||
extern const struct seat_impl logind_impl;
|
||||
extern const struct seat_impl builtin_impl;
|
||||
extern const struct seat_impl noop_impl;
|
||||
|
||||
static const struct named_backend impls[] = {
|
||||
#ifdef SEATD_ENABLED
|
||||
|
@ -24,6 +25,8 @@ static const struct named_backend impls[] = {
|
|||
#ifdef BUILTIN_ENABLED
|
||||
{"builtin", &builtin_impl},
|
||||
#endif
|
||||
{"noop", &noop_impl},
|
||||
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
|
@ -31,7 +34,7 @@ static const struct named_backend impls[] = {
|
|||
#error At least one backend must be enabled
|
||||
#endif
|
||||
|
||||
struct libseat *libseat_open_seat(struct libseat_seat_listener *listener, void *data) {
|
||||
struct libseat *libseat_open_seat(const struct libseat_seat_listener *listener, void *data) {
|
||||
if (listener == NULL || listener->enable_seat == NULL || listener->disable_seat == NULL) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
|
@ -62,6 +65,9 @@ struct libseat *libseat_open_seat(struct libseat_seat_listener *listener, void *
|
|||
|
||||
struct libseat *backend = NULL;
|
||||
for (const struct named_backend *iter = impls; iter->backend != NULL; iter++) {
|
||||
if (iter->backend == &noop_impl) {
|
||||
continue;
|
||||
}
|
||||
backend = iter->backend->open_seat(listener, data);
|
||||
if (backend != NULL) {
|
||||
log_infof("Seat opened with backend '%s'", iter->name);
|
||||
|
|
58
man/seatd-launch.1.scd
Normal file
58
man/seatd-launch.1.scd
Normal file
|
@ -0,0 +1,58 @@
|
|||
seatd-launch(1)
|
||||
|
||||
# NAME
|
||||
|
||||
seatd-launch - Start a process with its own seatd instance
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
*seatd-launch* [options] [--] command
|
||||
|
||||
# OPTIONS
|
||||
|
||||
*-l <loglevel>*
|
||||
Log-level to pass to seatd. See *seatd*(1) for information about
|
||||
available log-levels.
|
||||
|
||||
*-h*
|
||||
Show help message and quit.
|
||||
|
||||
*-v*
|
||||
Show the version number and quit.
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
seatd-launch starts a seatd instance with a dedicated socket path, waits for it
|
||||
to be ready, and starts the specified command with SEATD_SOCK set
|
||||
appropriately. Once the specified command terminates, the seatd instance is
|
||||
also terminated.
|
||||
|
||||
seatd requires root privileges to perform its tasks. This can be achieved
|
||||
through SUID of seatd-launch or by running seatd-launch as root. seatd-launch
|
||||
will drop privileges from the effective user to the real user before running
|
||||
the specified command. If the real user is root, this is simply a noop. You
|
||||
should only run seatd-launch as root if you intend for the specified command to
|
||||
run as root as well.
|
||||
|
||||
seatd-launch serves a similar purpose to the libseat "builtin" backend, but is
|
||||
superior to it for two reasons:
|
||||
. The specified command never runs as root
|
||||
. The standard seatd executable and libseat backend is used
|
||||
|
||||
# EXIT STATUS
|
||||
|
||||
seatd-launch exits with the status of its child. When the child terminates on
|
||||
a signal _N_, seatd-launch exits with the status 128 + _N_.
|
||||
|
||||
If seatd-launch fails because of another error, it exits with a non-zero
|
||||
status.
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
The libseat library, *<libseat.h>*, *seatd*(1)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
Maintained by Kenny Levinsen <contact@kl.wtf>, who is assisted by other
|
||||
open-source contributors. For more information about seatd development, see
|
||||
https://sr.ht/~kennylevinsen/seatd.
|
|
@ -13,14 +13,20 @@ seatd - A seat management daemon
|
|||
*-h*
|
||||
Show help message and quit.
|
||||
|
||||
*-n <fd>*
|
||||
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.
|
||||
|
||||
*-u <user>*
|
||||
User to own the seatd socket.
|
||||
|
||||
*-g <group>*
|
||||
Group to own the seatd socket.
|
||||
|
||||
*-s <path>*
|
||||
Where to create the seatd socket. Defaults to `/run/seatd.sock`.
|
||||
*-l <loglevel>*
|
||||
Log-level to use. Must be one of debug, info, error or silent. Defaults
|
||||
to error.
|
||||
|
||||
*-v*
|
||||
Show the version number and quit.
|
||||
|
@ -33,21 +39,16 @@ 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
|
||||
|
||||
The libseat library, *<libseat.h>*
|
||||
The libseat library, *<libseat.h>*, *seatd-launch*(1)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
|
|
153
meson.build
153
meson.build
|
@ -1,9 +1,9 @@
|
|||
project(
|
||||
'seatd',
|
||||
'c',
|
||||
version: '0.5.0',
|
||||
version: '0.8.0',
|
||||
license: 'MIT',
|
||||
meson_version: '>=0.53.0',
|
||||
meson_version: '>=0.60.0',
|
||||
default_options: [
|
||||
'c_std=c11',
|
||||
'warning_level=3',
|
||||
|
@ -16,15 +16,29 @@ libseat_soversion = 1
|
|||
|
||||
defaultpath = get_option('defaultpath')
|
||||
if defaultpath == ''
|
||||
system = target_machine.system()
|
||||
if system == 'linux'
|
||||
defaultpath = '/run/seatd.sock'
|
||||
else
|
||||
defaultpath = '/var/run/seatd.sock'
|
||||
if target_machine.system() == 'linux'
|
||||
defaultpath = '/run/seatd.sock'
|
||||
endif
|
||||
endif
|
||||
|
||||
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',
|
||||
'-Wunused',
|
||||
|
@ -38,12 +52,14 @@ add_project_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)
|
||||
],
|
||||
]),
|
||||
language: 'c',
|
||||
)
|
||||
|
||||
|
@ -51,9 +67,13 @@ if ['debugoptimized', 'release', 'minsize'].contains(get_option('buildtype'))
|
|||
add_project_arguments('-D_FORTIFY_SOURCE=2', language: 'c')
|
||||
endif
|
||||
|
||||
if get_option('buildtype').startswith('debug')
|
||||
add_project_arguments('-DDEBUG', language : 'c')
|
||||
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
|
||||
|
@ -74,11 +94,6 @@ foreach p : source_root
|
|||
i += 1
|
||||
endforeach
|
||||
|
||||
if get_option('buildtype').startswith('debug')
|
||||
add_project_arguments('-DDEBUG', language : 'c')
|
||||
endif
|
||||
|
||||
|
||||
add_project_arguments(
|
||||
'-DREL_SRC_DIR="@0@"'.format(join_paths(relative_dir_parts) + '/'),
|
||||
language: 'c',
|
||||
|
@ -99,41 +114,55 @@ server_files = [
|
|||
'common/connection.c',
|
||||
'common/evdev.c',
|
||||
'common/drm.c',
|
||||
'common/wscons.c',
|
||||
'seatd/poller.c',
|
||||
'seatd/seat.c',
|
||||
'seatd/client.c',
|
||||
'seatd/server.c',
|
||||
]
|
||||
|
||||
if get_option('seatd').enabled()
|
||||
with_seatd = get_option('libseat-seatd') == 'enabled'
|
||||
with_builtin = get_option('libseat-builtin') == 'enabled'
|
||||
with_server = get_option('server') == 'enabled'
|
||||
|
||||
if with_seatd or with_builtin
|
||||
private_files += 'libseat/backend/seatd.c'
|
||||
add_project_arguments('-DSEATD_ENABLED=1', language: 'c')
|
||||
endif
|
||||
|
||||
logind_provider = ''
|
||||
if get_option('logind').enabled()
|
||||
# Check for libelogind first, as elogind may provide a libsystemd wrapper
|
||||
# which can cause issues.
|
||||
logind = dependency('libelogind', required: false)
|
||||
add_project_arguments('-DLOGIND_ENABLED=1', language: 'c')
|
||||
if logind.found()
|
||||
add_project_arguments('-DHAVE_ELOGIND=1', language: 'c')
|
||||
logind_provider = 'elogind'
|
||||
libseat_c_args = ['-DLIBSEAT=1']
|
||||
|
||||
if with_seatd
|
||||
libseat_c_args += '-DSEATD_ENABLED=1'
|
||||
endif
|
||||
|
||||
logind = disabler()
|
||||
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')
|
||||
logind = dependency(['libelogind', 'libsystemd'], required: false)
|
||||
else
|
||||
logind = dependency('libsystemd')
|
||||
add_project_arguments('-DHAVE_SYSTEMD=1', language: 'c')
|
||||
logind_provider = 'systemd'
|
||||
logind = dependency('lib@0@'.format(get_option('libseat-logind')))
|
||||
endif
|
||||
|
||||
if logind.found()
|
||||
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',
|
||||
]
|
||||
private_deps += logind
|
||||
endif
|
||||
endif
|
||||
|
||||
if get_option('builtin').enabled()
|
||||
add_project_arguments('-DBUILTIN_ENABLED=1', language: 'c')
|
||||
# needed for cross-compilation
|
||||
# realtime = meson.get_compiler('c').find_library('rt')
|
||||
# private_deps += realtime
|
||||
|
||||
if with_builtin
|
||||
libseat_c_args += '-DBUILTIN_ENABLED=1'
|
||||
private_files += server_files
|
||||
endif
|
||||
|
||||
|
@ -142,41 +171,67 @@ private_lib = static_library(
|
|||
private_files,
|
||||
dependencies: private_deps,
|
||||
include_directories: [include_directories('.', 'include')],
|
||||
c_args: libseat_c_args,
|
||||
)
|
||||
|
||||
symbols_file = 'libseat/libseat.syms'
|
||||
symbols_flag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), symbols_file)
|
||||
lib = library(
|
||||
'seat', # This results in the library being called 'libseat'
|
||||
[ 'libseat/libseat.c' ],
|
||||
soversion: libseat_soversion,
|
||||
[ 'libseat/libseat.c', 'libseat/backend/noop.c' ],
|
||||
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')
|
||||
|
||||
libseat_vars = {
|
||||
'have_seatd': with_seatd.to_string(),
|
||||
'have_logind': logind.found().to_string(),
|
||||
'have_builtin': with_builtin.to_string(),
|
||||
}
|
||||
|
||||
pkgconfig = import('pkgconfig')
|
||||
pkgconfig.generate(lib,
|
||||
version: meson.project_version(),
|
||||
filebase: 'libseat',
|
||||
name: 'libseat',
|
||||
description: 'Seat management library',
|
||||
variables: libseat_vars,
|
||||
)
|
||||
|
||||
if get_option('server').enabled()
|
||||
libseat = declare_dependency(
|
||||
link_with: lib,
|
||||
dependencies: private_deps,
|
||||
include_directories: include_directories('include', is_system: true),
|
||||
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],
|
||||
)
|
||||
executable(
|
||||
'seatd-launch',
|
||||
[ 'seatd-launch/seatd-launch.c' ],
|
||||
include_directories: [include_directories('.', 'include')],
|
||||
install: true,
|
||||
# dependencies: [realtime],
|
||||
)
|
||||
endif
|
||||
|
||||
if get_option('examples').enabled()
|
||||
if get_option('examples') == 'enabled'
|
||||
executable(
|
||||
'simpletest',
|
||||
['examples/simpletest/main.c'],
|
||||
|
@ -199,21 +254,16 @@ foreach name, value : tests
|
|||
include_directories: [include_directories('.', 'include')]))
|
||||
endforeach
|
||||
|
||||
if get_option('server').enabled()
|
||||
if with_server
|
||||
scdoc = dependency('scdoc', required: get_option('man-pages'), version: '>= 1.9.7', native: true)
|
||||
else
|
||||
scdoc = disabler()
|
||||
endif
|
||||
|
||||
if scdoc.found()
|
||||
sh = find_program('sh', native: true)
|
||||
scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true)
|
||||
|
||||
man_pages = ['seatd.1.scd']
|
||||
|
||||
mandir = get_option('mandir')
|
||||
|
||||
foreach src : man_pages
|
||||
foreach src : ['seatd.1.scd', 'seatd-launch.1.scd']
|
||||
topic = src.split('.')[0]
|
||||
section = src.split('.')[1]
|
||||
output = '@0@.@1@'.format(topic, section)
|
||||
|
@ -222,9 +272,9 @@ if scdoc.found()
|
|||
output,
|
||||
input: 'man/' + src,
|
||||
output: output,
|
||||
command: [
|
||||
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
|
||||
],
|
||||
command: scdoc.get_variable(pkgconfig: 'scdoc'),
|
||||
feed: true,
|
||||
capture: true,
|
||||
install: true,
|
||||
install_dir: '@0@/man@1@'.format(mandir, section)
|
||||
)
|
||||
|
@ -232,8 +282,9 @@ if scdoc.found()
|
|||
endif
|
||||
|
||||
summary({
|
||||
'seatd': get_option('seatd').enabled(),
|
||||
'builtin': get_option('builtin').enabled(),
|
||||
'systemd': logind_provider == 'systemd',
|
||||
'elogind': logind_provider == 'elogind',
|
||||
'libseat-seatd': with_seatd,
|
||||
'libseat-builtin': with_builtin,
|
||||
'libseat-systemd': logind.found() and logind.name() == 'libsystemd',
|
||||
'libseat-elogind': logind.found() and logind.name() == 'libelogind',
|
||||
'server': with_server,
|
||||
}, bool_yn: true)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
option('logind', type: 'feature', value: 'disabled', description: 'logind support')
|
||||
option('seatd', type: 'feature', value: 'enabled', description: 'seatd support')
|
||||
option('builtin', type: 'feature', value: 'disabled', description: 'builtin seatd server')
|
||||
option('server', type: 'feature', value: 'enabled', description: 'seatd server')
|
||||
option('examples', type: 'feature', value: 'enabled', description: 'libseat example programs')
|
||||
option('libseat-logind', type: 'combo', choices: ['auto', 'disabled', 'elogind', 'systemd'], value: 'auto', description: 'logind support for libseat')
|
||||
option('libseat-seatd', type: 'combo', choices: ['enabled', 'disabled'], value: 'enabled', description: 'seatd support for libseat')
|
||||
option('libseat-builtin', type: 'combo', choices: ['enabled', 'disabled'], value: 'disabled', description: 'built-in seatd server for libseat')
|
||||
option('server', type: 'combo', choices: ['enabled', 'disabled'], value: 'enabled', description: 'seatd server')
|
||||
option('examples', type: 'combo', choices: ['enabled', 'disabled'], value: 'disabled', description: 'libseat example programs')
|
||||
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
|
||||
option('defaultpath', type: 'string', value: '', description: 'Default location for seatd socket (empty for default)')
|
||||
|
|
174
seatd-launch/seatd-launch.c
Normal file
174
seatd-launch/seatd-launch.c
Normal file
|
@ -0,0 +1,174 @@
|
|||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const char *usage = "Usage: seatd-launch [options] [--] command\n"
|
||||
"\n"
|
||||
" -l <loglevel> Log-level to pass to seatd\n"
|
||||
" -h Show this help message\n"
|
||||
" -v Show the version number\n"
|
||||
"\n";
|
||||
|
||||
int c;
|
||||
char loglevel[16] = "info";
|
||||
while ((c = getopt(argc, argv, "vhl:")) != -1) {
|
||||
switch (c) {
|
||||
case 'l':
|
||||
strncpy(loglevel, optarg, sizeof loglevel);
|
||||
loglevel[sizeof loglevel - 1] = '\0';
|
||||
break;
|
||||
case 'v':
|
||||
printf("seatd-launch version %s\n", SEATD_VERSION);
|
||||
return 0;
|
||||
case 'h':
|
||||
printf("%s", usage);
|
||||
return 0;
|
||||
case '?':
|
||||
fprintf(stderr, "Try 'seatd-launch -h' for more information.\n");
|
||||
return 1;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, "A command must be specified\n\n%s", usage);
|
||||
return 1;
|
||||
}
|
||||
char **command = &argv[optind];
|
||||
|
||||
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(readiness_pipe[0]);
|
||||
|
||||
char pipebuf[16] = {0};
|
||||
snprintf(pipebuf, sizeof pipebuf, "%d", readiness_pipe[1]);
|
||||
|
||||
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(readiness_pipe[1]);
|
||||
|
||||
// Wait for seatd to be ready
|
||||
char buf[1] = {0};
|
||||
while (true) {
|
||||
pid_t p = waitpid(seatd_child, NULL, WNOHANG);
|
||||
if (p == seatd_child) {
|
||||
fprintf(stderr, "seatd exited prematurely\n");
|
||||
goto error_seatd;
|
||||
} else if (p == -1 && (errno != EINTR && errno != ECHILD)) {
|
||||
perror("Could not wait for seatd process");
|
||||
goto error_seatd;
|
||||
}
|
||||
|
||||
struct pollfd fd = {
|
||||
.fd = readiness_pipe[0],
|
||||
.events = POLLIN,
|
||||
};
|
||||
|
||||
// We poll with timeout to avoid a racing on a blocking read
|
||||
if (poll(&fd, 1, 1000) == -1) {
|
||||
if (errno == EAGAIN || errno == EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
perror("Could not poll notification fd");
|
||||
goto error_seatd;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd.revents & POLLIN) {
|
||||
ssize_t n = read(readiness_pipe[0], buf, 1);
|
||||
if (n == -1 && errno != EINTR) {
|
||||
perror("Could not read from pipe");
|
||||
goto error_seatd;
|
||||
} else if (n > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
close(readiness_pipe[0]);
|
||||
|
||||
uid_t uid = getuid();
|
||||
gid_t gid = getgid();
|
||||
|
||||
// Restrict access to the socket to just us
|
||||
if (chown(SEATD_DEFAULTPATH, uid, gid) == -1) {
|
||||
perror("Could not chown seatd socket");
|
||||
goto error_seatd;
|
||||
}
|
||||
if (chmod(SEATD_DEFAULTPATH, 0700) == -1) {
|
||||
perror("Could not chmod socket");
|
||||
goto error_seatd;
|
||||
}
|
||||
|
||||
// Drop privileges
|
||||
if (setgid(gid) == -1) {
|
||||
perror("Could not set gid to drop privileges");
|
||||
goto error_seatd;
|
||||
}
|
||||
if (setuid(uid) == -1) {
|
||||
perror("Could not set uid to drop privileges");
|
||||
goto error_seatd;
|
||||
}
|
||||
|
||||
pid_t child = fork();
|
||||
if (child == -1) {
|
||||
perror("Could not fork target process");
|
||||
goto error_seatd;
|
||||
} else if (child == 0) {
|
||||
setenv("SEATD_SOCK", SEATD_DEFAULTPATH, 1);
|
||||
execvp(command[0], command);
|
||||
perror("Could not start target");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
while (true) {
|
||||
pid_t p = waitpid(child, &status, 0);
|
||||
if (p == child) {
|
||||
break;
|
||||
} else if (p == -1 && errno != EINTR) {
|
||||
perror("Could not wait for target process");
|
||||
goto error_seatd;
|
||||
}
|
||||
}
|
||||
|
||||
if (kill(seatd_child, SIGTERM) != 0) {
|
||||
perror("Could not kill seatd");
|
||||
}
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
return WEXITSTATUS(status);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
return 128 + WTERMSIG(status);
|
||||
} else {
|
||||
abort(); // unreachable
|
||||
}
|
||||
|
||||
error_seatd:
|
||||
kill(seatd_child, SIGTERM);
|
||||
error:
|
||||
return 1;
|
||||
}
|
|
@ -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,13 +38,47 @@ 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;
|
||||
if (getsockopt(fd, 0, LOCAL_PEERCRED, &cred, &len) == -1) {
|
||||
return -1;
|
||||
}
|
||||
#if __FreeBSD_version >= 1300030
|
||||
#if __FreeBSD_version >= 1300030 || (__FreeBSD_version >= 1202506 && __FreeBSD_version < 1300000)
|
||||
*pid = cred.cr_pid;
|
||||
#else
|
||||
*pid = -1;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -309,8 +354,23 @@ error:
|
|||
return client_send_error(client, errno);
|
||||
}
|
||||
|
||||
static int handle_ping(struct client *client) {
|
||||
struct proto_header header = {
|
||||
.opcode = SERVER_PONG,
|
||||
.size = 0,
|
||||
};
|
||||
|
||||
if (connection_put(&client->connection, &header, sizeof header) == -1) {
|
||||
log_errorf("Could not write response: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -372,6 +432,14 @@ static int client_handle_opcode(struct client *client, uint16_t opcode, size_t s
|
|||
res = handle_disable_seat(client);
|
||||
break;
|
||||
}
|
||||
case CLIENT_PING: {
|
||||
if (size != 0) {
|
||||
log_error("Protocol error: invalid ping message");
|
||||
return -1;
|
||||
}
|
||||
res = handle_ping(client);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log_errorf("Protocol error: unknown opcode: %d", opcode);
|
||||
res = -1;
|
||||
|
@ -439,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;
|
||||
}
|
||||
|
||||
|
|
80
seatd/seat.c
80
seatd/seat.c
|
@ -17,9 +17,10 @@
|
|||
#include "protocol.h"
|
||||
#include "seat.h"
|
||||
#include "terminal.h"
|
||||
#include "wscons.h"
|
||||
|
||||
static int seat_close_client(struct client *client);
|
||||
static void vt_close(struct seat *seat);
|
||||
static int vt_close(int vt);
|
||||
|
||||
struct seat *seat_create(const char *seat_name, bool vt_bound) {
|
||||
struct seat *seat = calloc(1, sizeof(struct seat));
|
||||
|
@ -30,7 +31,6 @@ struct seat *seat_create(const char *seat_name, bool vt_bound) {
|
|||
seat->vt_bound = vt_bound;
|
||||
seat->seat_name = strdup(seat_name);
|
||||
seat->cur_vt = 0;
|
||||
seat->cur_ttyfd = -1;
|
||||
if (seat->seat_name == NULL) {
|
||||
free(seat);
|
||||
return NULL;
|
||||
|
@ -50,7 +50,6 @@ void seat_destroy(struct seat *seat) {
|
|||
assert(client->seat == seat);
|
||||
client_destroy(client);
|
||||
}
|
||||
vt_close(seat);
|
||||
linked_list_remove(&seat->link);
|
||||
free(seat->seat_name);
|
||||
free(seat);
|
||||
|
@ -66,47 +65,30 @@ static void seat_update_vt(struct seat *seat) {
|
|||
close(tty0fd);
|
||||
}
|
||||
|
||||
static int vt_open(struct seat *seat, int vt) {
|
||||
static int vt_open(int vt) {
|
||||
assert(vt != -1);
|
||||
if (seat->cur_ttyfd != -1) {
|
||||
terminal_set_process_switching(seat->cur_ttyfd, true);
|
||||
close(seat->cur_ttyfd);
|
||||
}
|
||||
seat->cur_ttyfd = terminal_open(vt);
|
||||
if (seat->cur_ttyfd == -1) {
|
||||
int ttyfd = terminal_open(vt);
|
||||
if (ttyfd == -1) {
|
||||
log_errorf("Could not open terminal for VT %d: %s", vt, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
terminal_set_process_switching(seat->cur_ttyfd, true);
|
||||
terminal_set_keyboard(seat->cur_ttyfd, false);
|
||||
terminal_set_graphics(seat->cur_ttyfd, true);
|
||||
terminal_set_process_switching(ttyfd, true);
|
||||
terminal_set_keyboard(ttyfd, false);
|
||||
terminal_set_graphics(ttyfd, true);
|
||||
close(ttyfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vt_close_fd(int fd) {
|
||||
terminal_set_process_switching(fd, true);
|
||||
terminal_set_keyboard(fd, true);
|
||||
terminal_set_graphics(fd, false);
|
||||
}
|
||||
|
||||
static void vt_close(struct seat *seat) {
|
||||
if (seat->cur_ttyfd == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
vt_close_fd(seat->cur_ttyfd);
|
||||
close(seat->cur_ttyfd);
|
||||
seat->cur_ttyfd = -1;
|
||||
}
|
||||
|
||||
static int vt_close_num(int vt) {
|
||||
static int vt_close(int vt) {
|
||||
int ttyfd = terminal_open(vt);
|
||||
if (ttyfd == -1) {
|
||||
log_errorf("Could not open terminal to clean up VT %d: %s", vt, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
vt_close_fd(ttyfd);
|
||||
terminal_set_process_switching(ttyfd, true);
|
||||
terminal_set_graphics(ttyfd, false);
|
||||
terminal_set_keyboard(ttyfd, true);
|
||||
close(ttyfd);
|
||||
return 0;
|
||||
}
|
||||
|
@ -125,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;
|
||||
|
@ -148,7 +131,8 @@ int seat_add_client(struct seat *seat, struct client *client) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (seat->vt_bound && seat->active_client != NULL) {
|
||||
if (seat->vt_bound && seat->active_client != NULL &&
|
||||
seat->active_client->state != CLIENT_PENDING_DISABLE) {
|
||||
log_error("Could not add client: seat is VT-bound and has an active client");
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
|
@ -167,6 +151,17 @@ int seat_add_client(struct seat *seat, struct client *client) {
|
|||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (seat->active_client != NULL) {
|
||||
for (struct linked_list *elem = seat->clients.next; elem != &seat->clients;
|
||||
elem = elem->next) {
|
||||
struct client *client = (struct client *)elem;
|
||||
if (client->session == seat->cur_vt) {
|
||||
log_error("Could not add client: seat is VT-bound and already has pending client");
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
client->session = seat->cur_vt;
|
||||
} else {
|
||||
client->session = seat->session_cnt++;
|
||||
|
@ -242,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;
|
||||
|
@ -288,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();
|
||||
|
@ -340,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();
|
||||
|
@ -389,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();
|
||||
|
@ -452,7 +458,7 @@ int seat_open_client(struct seat *seat, struct client *client) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (seat->vt_bound && vt_open(seat, client->session) == -1) {
|
||||
if (seat->vt_bound && vt_open(client->session) == -1) {
|
||||
log_error("Could not open VT for client");
|
||||
goto error;
|
||||
}
|
||||
|
@ -477,7 +483,7 @@ int seat_open_client(struct seat *seat, struct client *client) {
|
|||
|
||||
error:
|
||||
if (seat->vt_bound) {
|
||||
vt_close(seat);
|
||||
vt_close(seat->cur_vt);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -506,12 +512,12 @@ static int seat_close_client(struct client *client) {
|
|||
// This client was current, but there were no clients
|
||||
// waiting to take this VT, so clean it up.
|
||||
log_debug("Closing active VT");
|
||||
vt_close(seat);
|
||||
vt_close(seat->cur_vt);
|
||||
} else if (!was_current && client->state != CLIENT_CLOSED) {
|
||||
// This client was not current, but as the client was
|
||||
// running, we need to clean up the VT.
|
||||
log_debug("Closing inactive VT");
|
||||
vt_close_num(client->session);
|
||||
vt_close(client->session);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -532,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
|
||||
|
|
145
seatd/seatd.c
145
seatd/seatd.c
|
@ -33,58 +33,60 @@ static int open_socket(const char *path, int uid, int gid) {
|
|||
socklen_t size = offsetof(struct sockaddr_un, sun_path) + strlen(addr.unix.sun_path);
|
||||
if (bind(fd, &addr.generic, size) == -1) {
|
||||
log_errorf("Could not bind socket: %s", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
goto error;
|
||||
}
|
||||
if (listen(fd, LISTEN_BACKLOG) == -1) {
|
||||
log_errorf("Could not listen on socket: %s", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
goto error;
|
||||
}
|
||||
if (uid != -1 || gid != -1) {
|
||||
if (chmod(path, 0770) == -1) {
|
||||
log_errorf("Could not chmod socket: %s", strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
if (uid != 0 || gid != 0) {
|
||||
if (chown(path, uid, gid) == -1) {
|
||||
log_errorf("Could not chown socket to uid %d, gid %d: %s", uid, gid,
|
||||
strerror(errno));
|
||||
} else if (chmod(path, 0770) == -1) {
|
||||
log_errorf("Could not chmod socket: %s", strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
return fd;
|
||||
error:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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> FD to notify readiness on\n"
|
||||
" -u <user> User to own the seatd socket\n"
|
||||
" -g <group> Group to own the seatd socket\n"
|
||||
" -s <path> Where to create the seatd socket\n"
|
||||
" -l <loglevel> Log-level, one of debug, info, error or silent\n"
|
||||
" -v Show the version number\n"
|
||||
"\n";
|
||||
|
||||
int c;
|
||||
int uid = 0, gid = 0;
|
||||
const char *socket_path = getenv("SEATD_SOCK");
|
||||
while ((c = getopt(argc, argv, "vhs:g:u:")) != -1) {
|
||||
int uid = -1, gid = -1;
|
||||
int readiness = -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 's':
|
||||
socket_path = optarg;
|
||||
case 'n':
|
||||
readiness = atoi(optarg);
|
||||
if (readiness < 0) {
|
||||
fprintf(stderr, "Invalid readiness fd: %s\n", optarg);
|
||||
return 1;
|
||||
}
|
||||
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);
|
||||
|
@ -95,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);
|
||||
|
@ -104,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;
|
||||
|
@ -118,46 +149,72 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
if (socket_path == NULL) {
|
||||
socket_path = SEATD_DEFAULTPATH;
|
||||
log_init();
|
||||
libseat_set_log_level(level);
|
||||
|
||||
struct stat st;
|
||||
if (stat(socket_path, &st) == 0) {
|
||||
log_info("Removing leftover seatd socket");
|
||||
unlink(socket_path);
|
||||
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_errorf("Could not create server socket: %s", strerror(errno));
|
||||
server_finish(&server);
|
||||
return 1;
|
||||
log_error("Could not create server socket");
|
||||
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");
|
||||
|
||||
if (readiness != -1) {
|
||||
if (write(readiness, "\n", 1) == -1) {
|
||||
log_errorf("Could not write readiness signal: %s\n", strerror(errno));
|
||||
}
|
||||
close(readiness);
|
||||
}
|
||||
|
||||
while (server.running) {
|
||||
if (poller_poll(&server.poller) == -1) {
|
||||
log_errorf("Poller failed: %s", strerror(errno));
|
||||
return 1;
|
||||
goto error_socket;
|
||||
}
|
||||
}
|
||||
|
||||
server_finish(&server);
|
||||
unlink(socket_path);
|
||||
log_info("seatd stopped");
|
||||
return 0;
|
||||
ret = 0;
|
||||
|
||||
error_socket:
|
||||
if (unlink(SEATD_DEFAULTPATH) == -1) {
|
||||
log_errorf("Could not remove socket: %s", strerror(errno));
|
||||
}
|
||||
error_server:
|
||||
server_finish(&server);
|
||||
log_info("seatd stopped");
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -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 recieved an error: %s", strerror(errno));
|
||||
log_error("Server socket received an error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue