Compare commits
No commits in common. "obsd" and "0.2.0" have entirely different histories.
51 changed files with 1512 additions and 2675 deletions
|
@ -3,26 +3,31 @@ packages:
|
|||
- meson
|
||||
- linux-headers
|
||||
- clang
|
||||
- clang-extra-tools
|
||||
- clang-analyzer
|
||||
- scdoc
|
||||
sources:
|
||||
- https://git.sr.ht/~kennylevinsen/seatd
|
||||
tasks:
|
||||
- prepare: |
|
||||
meson -Dlibseat-seatd=enabled -Dlibseat-builtin=enabled -Dlibseat-logind=disabled -Dexamples=enabled build seatd
|
||||
cd seatd
|
||||
meson build -Dseatd=enabled -Dbuiltin=enabled -Dlogind=disabled
|
||||
- build: |
|
||||
cd seatd
|
||||
ninja -C build
|
||||
sudo ninja -C build install
|
||||
- unittest: |
|
||||
cd seatd
|
||||
ninja -C build test
|
||||
- scan-build: |
|
||||
cd seatd
|
||||
ninja -C build scan-build
|
||||
[ -z "$(ls -A build/meson-logs/scanbuild/ 2>/dev/null)" ]
|
||||
- smoketest: |
|
||||
timeout -s KILL 30s sudo ./build/seatd-launch -l debug -- ./build/simpletest /dev/dri/card0
|
||||
cd seatd
|
||||
timeout -s KILL 30s ./.builds/smoketest-seatd.sh
|
||||
- smoketest-builtin: |
|
||||
timeout -s KILL 30s sudo LIBSEAT_BACKEND=builtin ./build/simpletest /dev/dri/card0
|
||||
cd seatd
|
||||
timeout -s KILL 30s ./.builds/smoketest-builtin.sh
|
||||
- check-format: |
|
||||
cd seatd
|
||||
ninja -C build clang-format
|
||||
git -C seatd diff --exit-code
|
||||
git diff --exit-code
|
||||
|
|
|
@ -9,22 +9,24 @@ sources:
|
|||
- https://git.sr.ht/~kennylevinsen/seatd
|
||||
tasks:
|
||||
- prepare: |
|
||||
meson -Db_sanitize=address -Dlibseat-logind=auto -Dlibseat-seatd=enabled -Dlibseat-builtin=enabled -Dexamples=enabled build seatd
|
||||
cd seatd
|
||||
meson -Dlogind=enabled -Dseatd=enabled -Dbuiltin=enabled build
|
||||
- build: |
|
||||
cd seatd
|
||||
ninja -C build
|
||||
sudo ninja -C build install
|
||||
- unittest: |
|
||||
cd seatd
|
||||
ninja -C build test
|
||||
- scan-build: |
|
||||
cd seatd
|
||||
ninja -C build scan-build
|
||||
[ -z "$(ls -A build/meson-logs/scanbuild/ 2>/dev/null)" ]
|
||||
- smoketest: |
|
||||
timeout -s KILL 30s sudo ./build/seatd-launch -l debug -- ./build/simpletest /dev/input/event0
|
||||
cd seatd
|
||||
rm -rf build
|
||||
meson -Db_sanitize=address -Dlogind=enabled -Dseatd=enabled -Dbuiltin=enabled build
|
||||
ninja -C build
|
||||
timeout -s KILL 30s ./.builds/smoketest-seatd.sh
|
||||
- smoketest-builtin: |
|
||||
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
|
||||
cd seatd
|
||||
timeout -s KILL 30s ./.builds/smoketest-builtin.sh
|
||||
|
|
|
@ -5,16 +5,17 @@ sources:
|
|||
- https://git.sr.ht/~kennylevinsen/seatd
|
||||
tasks:
|
||||
- prepare: |
|
||||
meson -Dlibseat-seatd=enabled -Dlibseat-builtin=enabled -Dlibseat-logind=disabled build seatd
|
||||
cd seatd
|
||||
meson build -Dseatd=enabled -Dbuiltin=enabled -Dlogind=disabled
|
||||
- build: |
|
||||
cd seatd
|
||||
ninja -C build
|
||||
- unittest: |
|
||||
cd seatd
|
||||
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 KILL 30s sudo ./build/seatd-launch -l debug -- ./build/simpletest /dev/input/event0
|
||||
cd seatd
|
||||
timeout -s KILL 30s ./.builds/smoketest-seatd.sh
|
||||
- smoketest-builtin: |
|
||||
timeout -s KILL 30s sudo LIBSEAT_BACKEND=builtin ./build/simpletest /dev/input/event0
|
||||
cd seatd
|
||||
timeout -s KILL 30s ./.builds/smoketest-builtin.sh
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
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
|
30
.builds/smoketest-builtin.sh
Executable file
30
.builds/smoketest-builtin.sh
Executable file
|
@ -0,0 +1,30 @@
|
|||
#!/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"
|
58
.builds/smoketest-seatd.sh
Executable file
58
.builds/smoketest-seatd.sh
Executable file
|
@ -0,0 +1,58 @@
|
|||
#!/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: ForContinuationAndIndentation
|
||||
UseTab: Always
|
||||
ColumnLimit: 100
|
||||
|
||||
AlignConsecutiveMacros: true
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
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, and has experimental NetBSD support.
|
||||
Currently supports Linux and FreeBSD. Alpha
|
||||
|
||||
## What is seat management?
|
||||
|
||||
|
@ -12,7 +12,7 @@ Seat management takes care of mediating access to shared devices (graphics, inpu
|
|||
|
||||
### seatd
|
||||
|
||||
A seat management daemon, that does everything it needs to do. Nothing more, nothing less. Depends only on libc.
|
||||
A seat management deamon, 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.
|
||||
|
||||
## How to discuss
|
||||
## I want more
|
||||
|
||||
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).
|
||||
Go to #kennylevinsen @ chat.freenode.net to discuss, or use [~kennylevinsen/public-inbox@lists.sr.ht](https://lists.sr.ht/~kennylevinsen/public-inbox).
|
||||
|
|
|
@ -284,14 +284,15 @@ int connection_get(struct connection *connection, void *dst, size_t count) {
|
|||
return count;
|
||||
}
|
||||
|
||||
int connection_get_fd(struct connection *connection, int *fd) {
|
||||
if (sizeof(int) > connection_buffer_size(&connection->fds_in)) {
|
||||
int connection_get_fd(struct connection *connection) {
|
||||
int fd;
|
||||
if (sizeof fd > connection_buffer_size(&connection->fds_in)) {
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
}
|
||||
connection_buffer_copy(&connection->fds_in, fd, sizeof(int));
|
||||
connection_buffer_consume(&connection->fds_in, sizeof(int));
|
||||
return 0;
|
||||
connection_buffer_copy(&connection->fds_in, &fd, sizeof fd);
|
||||
connection_buffer_consume(&connection->fds_in, sizeof fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
void connection_close_fds(struct connection *connection) {
|
||||
|
|
44
common/drm.c
44
common/drm.c
|
@ -2,16 +2,19 @@
|
|||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/sysmacros.h>
|
||||
#endif
|
||||
|
||||
#include "drm.h"
|
||||
|
||||
// From libdrm
|
||||
#define DRM_IOCTL_BASE 'd'
|
||||
#define DRM_IO(nr) _IO(DRM_IOCTL_BASE, nr)
|
||||
#define DRM_IOCTL_BASE 'd'
|
||||
#define DRM_IO(nr) _IO(DRM_IOCTL_BASE, nr)
|
||||
#define DRM_IOCTL_SET_MASTER DRM_IO(0x1e)
|
||||
#define DRM_IOCTL_DROP_MASTER DRM_IO(0x1f)
|
||||
|
||||
#define STRLEN(s) ((sizeof(s) / sizeof(s[0])) - 1)
|
||||
#define STR_HAS_PREFIX(prefix, s) (strncmp(prefix, s, STRLEN(prefix)) == 0)
|
||||
#define STRLEN(s) ((sizeof(s) / sizeof(s[0])) - 1)
|
||||
|
||||
int drm_set_master(int fd) {
|
||||
return ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
|
||||
|
@ -21,21 +24,24 @@ int drm_drop_master(int fd) {
|
|||
return ioctl(fd, DRM_IOCTL_DROP_MASTER, 0);
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
int path_is_drm(const char *path) {
|
||||
if (STR_HAS_PREFIX("/dev/dri/", path))
|
||||
return 1;
|
||||
return 0;
|
||||
static int path_is_drm_card(const char *path) {
|
||||
static const char prefix[] = "/dev/dri/card";
|
||||
static const int prefixlen = STRLEN(prefix);
|
||||
return strncmp(prefix, path, prefixlen) == 0;
|
||||
}
|
||||
#elif defined(__FreeBSD__)
|
||||
int path_is_drm(const char *path) {
|
||||
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;
|
||||
|
||||
static int path_is_drm_render(const char *path) {
|
||||
static const char prefix[] = "/dev/dri/renderD";
|
||||
static const int prefixlen = STRLEN(prefix);
|
||||
return strncmp(prefix, path, prefixlen) == 0;
|
||||
}
|
||||
|
||||
int path_is_drm(const char *path) {
|
||||
return path_is_drm_card(path) || path_is_drm_render(path);
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
int dev_is_drm(dev_t device) {
|
||||
return major(device) == 226;
|
||||
}
|
||||
#else
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
|
|
|
@ -9,13 +9,14 @@
|
|||
#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);
|
||||
|
@ -25,15 +26,9 @@ int path_is_evdev(const char *path) {
|
|||
int evdev_revoke(int fd) {
|
||||
return ioctl(fd, EVIOCREVOKE, NULL);
|
||||
}
|
||||
#elif defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
int path_is_evdev(const char *path) {
|
||||
(void)path;
|
||||
return 0;
|
||||
|
||||
#if defined(__linux__)
|
||||
int dev_is_evdev(dev_t device) {
|
||||
return major(device) == INPUT_MAJOR;
|
||||
}
|
||||
int evdev_revoke(int fd) {
|
||||
(void)fd;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
|
|
|
@ -32,15 +32,3 @@ bool linked_list_empty(struct linked_list *list) {
|
|||
assert(list->prev != NULL && list->next != NULL);
|
||||
return list->next == list;
|
||||
}
|
||||
|
||||
void linked_list_take(struct linked_list *target, struct linked_list *source) {
|
||||
if (linked_list_empty(source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
source->next->prev = target;
|
||||
source->prev->next = target->next;
|
||||
target->next->prev = source->prev;
|
||||
target->next = source->next;
|
||||
linked_list_init(source);
|
||||
}
|
||||
|
|
85
common/list.c
Normal file
85
common/list.c
Normal file
|
@ -0,0 +1,85 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
void list_init(struct list *list) {
|
||||
list->capacity = 10;
|
||||
list->length = 0;
|
||||
list->items = malloc(sizeof(void *) * list->capacity);
|
||||
}
|
||||
|
||||
static void list_resize(struct list *list) {
|
||||
if (list->length == list->capacity) {
|
||||
list->capacity *= 2;
|
||||
list->items = realloc(list->items, sizeof(void *) * list->capacity);
|
||||
}
|
||||
}
|
||||
|
||||
void list_free(struct list *list) {
|
||||
list->capacity = 0;
|
||||
list->length = 0;
|
||||
free(list->items);
|
||||
}
|
||||
|
||||
void list_add(struct list *list, void *item) {
|
||||
list_resize(list);
|
||||
list->items[list->length++] = item;
|
||||
}
|
||||
|
||||
void list_insert(struct list *list, size_t index, void *item) {
|
||||
list_resize(list);
|
||||
memmove(&list->items[index + 1], &list->items[index],
|
||||
sizeof(void *) * (list->length - index));
|
||||
list->length++;
|
||||
list->items[index] = item;
|
||||
}
|
||||
|
||||
void list_del(struct list *list, size_t index) {
|
||||
list->length--;
|
||||
memmove(&list->items[index], &list->items[index + 1],
|
||||
sizeof(void *) * (list->length - index));
|
||||
}
|
||||
|
||||
size_t list_find(struct list *list, const void *item) {
|
||||
for (size_t i = 0; i < list->length; i++) {
|
||||
if (list->items[i] == item) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void list_concat(struct list *list, const struct list *source) {
|
||||
if (list->length + source->length > list->capacity) {
|
||||
while (list->length + source->length > list->capacity) {
|
||||
list->capacity *= 2;
|
||||
}
|
||||
list->items = realloc(list->items, sizeof(void *) * list->capacity);
|
||||
}
|
||||
memmove(&list->items[list->length], source->items, sizeof(void *) * (source->length));
|
||||
list->length += source->length;
|
||||
}
|
||||
|
||||
void list_truncate(struct list *list) {
|
||||
list->length = 0;
|
||||
}
|
||||
|
||||
void *list_pop_front(struct list *list) {
|
||||
if (list->length == 0) {
|
||||
return NULL;
|
||||
}
|
||||
void *item = list->items[0];
|
||||
list_del(list, 0);
|
||||
return item;
|
||||
}
|
||||
|
||||
void *list_pop_back(struct list *list) {
|
||||
if (list->length == 0) {
|
||||
return NULL;
|
||||
}
|
||||
void *item = list->items[list->length - 1];
|
||||
list->length -= 1;
|
||||
return item;
|
||||
}
|
|
@ -13,25 +13,14 @@
|
|||
#include <linux/vt.h>
|
||||
#define K_ENABLE K_UNICODE
|
||||
#define K_DISABLE K_OFF
|
||||
#define FRSIG 0
|
||||
#define FRSIG 0
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <sys/consio.h>
|
||||
#include <sys/kbio.h>
|
||||
#include <termios.h>
|
||||
#define K_ENABLE K_XLATE
|
||||
#define K_DISABLE K_RAW
|
||||
#define FRSIG SIGIO
|
||||
#elif defined(__NetBSD__)
|
||||
#include <dev/wscons/wsdisplay_usl_io.h>
|
||||
#define K_ENABLE K_XLATE
|
||||
#define K_DISABLE K_RAW
|
||||
#define FRSIG 0 // unimplemented
|
||||
#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
|
||||
#define FRSIG SIGIO
|
||||
#else
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
|
@ -49,38 +38,6 @@ static int get_tty_path(int tty, char path[static TTYPATHLEN]) {
|
|||
const size_t prefix_len = sizeof(prefix) - 1;
|
||||
strcpy(path, prefix);
|
||||
|
||||
// The FreeBSD VT_GETACTIVE is implemented in the kernel as follows:
|
||||
//
|
||||
// static int
|
||||
// vtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data,
|
||||
// struct thread *td)
|
||||
// {
|
||||
// struct vt_window *vw = tm->tm_softc;
|
||||
// struct vt_device *vd = vw->vw_device;
|
||||
// ...
|
||||
// switch (cmd) {
|
||||
// ...
|
||||
// case VT_GETACTIVE:
|
||||
// *(int *)data = vd->vd_curwindow->vw_number + 1;
|
||||
// return (0);
|
||||
// ...
|
||||
// }
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// The side-effect here being that the returned VT number is one
|
||||
// greater than the internal VT number. The internal number is what is
|
||||
// used to number the TTY device, while the external number is what we
|
||||
// use in e.g. VT switching.
|
||||
//
|
||||
// We subtract one from the requested TTY number to compensate. If the
|
||||
// user asked for TTY 0 (which is special on Linux), we just give them
|
||||
// the first tty.
|
||||
|
||||
if (tty > 0) {
|
||||
tty--;
|
||||
}
|
||||
|
||||
// The FreeBSD tty name is constructed in the kernel as follows:
|
||||
//
|
||||
// static void
|
||||
|
@ -145,53 +102,31 @@ 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));
|
||||
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));
|
||||
log_errorf("could not open target tty: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int terminal_current_vt(int fd) {
|
||||
#if defined(__linux__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#if defined(__linux__)
|
||||
struct vt_stat st;
|
||||
int res = ioctl(fd, VT_GETSTATE, &st);
|
||||
close(fd);
|
||||
if (res == -1) {
|
||||
log_errorf("Could not retrieve VT state: %s", strerror(errno));
|
||||
log_errorf("could not retrieve VT state: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return st.v_active;
|
||||
|
@ -200,22 +135,46 @@ int terminal_current_vt(int fd) {
|
|||
int res = ioctl(fd, VT_GETACTIVE, &vt);
|
||||
close(fd);
|
||||
if (res == -1) {
|
||||
log_errorf("Could not retrieve VT state: %s", strerror(errno));
|
||||
log_errorf("could not retrieve VT state: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vt == -1) {
|
||||
log_errorf("Invalid VT: %d", vt);
|
||||
// The FreeBSD VT_GETACTIVE is implemented in the kernel as follows:
|
||||
//
|
||||
// static int
|
||||
// vtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data,
|
||||
// struct thread *td)
|
||||
// {
|
||||
// struct vt_window *vw = tm->tm_softc;
|
||||
// struct vt_device *vd = vw->vw_device;
|
||||
// ...
|
||||
// switch (cmd) {
|
||||
// ...
|
||||
// case VT_GETACTIVE:
|
||||
// *(int *)data = vd->vd_curwindow->vw_number + 1;
|
||||
// return (0);
|
||||
// ...
|
||||
// }
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// The side-effect here being that the returned VT number is one
|
||||
// greater than the internal VT number, which is what is used for e.g.
|
||||
// numbering the associated VT. To simplify things, we subtract one
|
||||
// from the returned VT number before returning it.
|
||||
|
||||
if (vt < 1) {
|
||||
log_errorf("invalid vt: %d", vt);
|
||||
return -1;
|
||||
}
|
||||
return vt;
|
||||
return vt - 1;
|
||||
#else
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
}
|
||||
|
||||
int terminal_set_process_switching(int fd, bool enable) {
|
||||
log_debugf("Setting process switching to %d", enable);
|
||||
log_debug("setting process switching");
|
||||
struct vt_mode mode = {
|
||||
.mode = enable ? VT_PROCESS : VT_AUTO,
|
||||
.waitv = 0,
|
||||
|
@ -225,37 +184,26 @@ int terminal_set_process_switching(int fd, bool enable) {
|
|||
};
|
||||
|
||||
if (ioctl(fd, VT_SETMODE, &mode) == -1) {
|
||||
log_errorf("Could not set VT mode to %s process switching: %s",
|
||||
enable ? "enable" : "disable", strerror(errno));
|
||||
log_errorf("could not set VT mode: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int terminal_switch_vt(int fd, int vt) {
|
||||
log_debugf("Switching to VT %d", vt);
|
||||
log_debugf("switching to vt %d", vt);
|
||||
if (ioctl(fd, VT_ACTIVATE, vt) == -1) {
|
||||
log_errorf("Could not activate VT %d: %s", vt, strerror(errno));
|
||||
log_errorf("could not activate VT: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int terminal_ack_release(int fd) {
|
||||
log_debug("Acking VT release");
|
||||
if (ioctl(fd, VT_RELDISP, 1) == -1) {
|
||||
log_errorf("Could not ack VT release: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int terminal_ack_acquire(int fd) {
|
||||
log_debug("Acking VT acquire");
|
||||
int terminal_ack_switch(int fd) {
|
||||
log_debug("acking vt switch");
|
||||
if (ioctl(fd, VT_RELDISP, VT_ACKACQ) == -1) {
|
||||
log_errorf("Could not ack VT acquire: %s", strerror(errno));
|
||||
log_errorf("could not ack VT switch: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -263,25 +211,15 @@ 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__
|
||||
log_debugf("setting KD keyboard state to %d", enable);
|
||||
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));
|
||||
log_errorf("could not set KD keyboard mode: %s", 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) {
|
||||
log_errorf("Could not set get terminal mode: %s", strerror(errno));
|
||||
log_errorf("could not set get terminal mode: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (enable) {
|
||||
|
@ -290,8 +228,7 @@ int terminal_set_keyboard(int fd, bool enable) {
|
|||
cfmakeraw(&tios);
|
||||
}
|
||||
if (tcsetattr(fd, TCSAFLUSH, &tios) == -1) {
|
||||
log_errorf("Could not set terminal mode to %s: %s", enable ? "sane" : "raw",
|
||||
strerror(errno));
|
||||
log_errorf("could not set terminal mode: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
@ -299,20 +236,10 @@ 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__
|
||||
log_debugf("setting KD graphics state to %d", enable);
|
||||
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));
|
||||
log_errorf("could not set KD graphics mode: %s", 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;
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
#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,11 +1,9 @@
|
|||
[Unit]
|
||||
Description=Seat management daemon
|
||||
Documentation=man:seatd(1)
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# Specify the group you'd like to grant access to seatd
|
||||
ExecStart=seatd -g seat
|
||||
ExecStart=seatd -g video
|
||||
Restart=always
|
||||
RestartSec=1
|
||||
|
||||
|
|
|
@ -43,11 +43,7 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
while (active == 0) {
|
||||
fprintf(stderr, "waiting for activation...\n");
|
||||
if (libseat_dispatch(backend, -1) == -1) {
|
||||
libseat_close_seat(backend);
|
||||
fprintf(stderr, "libseat_dispatch() failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
libseat_dispatch(backend, -1);
|
||||
}
|
||||
fprintf(stderr, "active!\n");
|
||||
|
||||
|
@ -61,8 +57,8 @@ int main(int argc, char *argv[]) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
libseat_close_device(backend, device);
|
||||
close(fd);
|
||||
libseat_close_device(backend, device);
|
||||
libseat_close_seat(backend);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ struct named_backend {
|
|||
};
|
||||
|
||||
struct seat_impl {
|
||||
struct libseat *(*open_seat)(const struct libseat_seat_listener *listener, void *data);
|
||||
struct libseat *(*open_seat)(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);
|
||||
|
|
|
@ -10,14 +10,6 @@
|
|||
|
||||
struct server;
|
||||
|
||||
enum client_state {
|
||||
CLIENT_NEW,
|
||||
CLIENT_ACTIVE,
|
||||
CLIENT_PENDING_DISABLE,
|
||||
CLIENT_DISABLED,
|
||||
CLIENT_CLOSED
|
||||
};
|
||||
|
||||
struct client {
|
||||
struct linked_list link; // seat::clients
|
||||
struct server *server;
|
||||
|
@ -29,16 +21,18 @@ struct client {
|
|||
gid_t gid;
|
||||
|
||||
struct seat *seat;
|
||||
int session;
|
||||
enum client_state state;
|
||||
int seat_vt;
|
||||
bool pending_disable;
|
||||
|
||||
struct linked_list devices;
|
||||
};
|
||||
|
||||
struct client *client_create(struct server *server, int client_fd);
|
||||
void client_kill(struct client *client);
|
||||
void client_destroy(struct client *client);
|
||||
|
||||
int client_handle_connection(int fd, uint32_t mask, void *data);
|
||||
int client_get_session(const struct client *client);
|
||||
int client_send_enable_seat(struct client *client);
|
||||
int client_send_disable_seat(struct client *client);
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ int connection_put_fd(struct connection *connection, int fd);
|
|||
|
||||
size_t connection_pending(struct connection *connection);
|
||||
int connection_get(struct connection *connection, void *dst, size_t count);
|
||||
int connection_get_fd(struct connection *connection, int *fd);
|
||||
int connection_get_fd(struct connection *connection);
|
||||
void connection_restore(struct connection *connection, size_t count);
|
||||
|
||||
void connection_close_fds(struct connection *connection);
|
||||
|
|
|
@ -5,4 +5,9 @@ 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,4 +4,9 @@
|
|||
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,8 +1,6 @@
|
|||
#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.
|
||||
|
@ -47,17 +45,15 @@ struct libseat_seat_listener {
|
|||
* The available backends, if enabled at compile-time, are: seatd, logind and
|
||||
* builtin.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* 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(const struct libseat_seat_listener *listener, void *userdata);
|
||||
struct libseat *libseat_open_seat(struct libseat_seat_listener *listener, void *userdata);
|
||||
|
||||
/*
|
||||
* Disables a seat, used in response to a disable_seat event. After disabling
|
||||
|
@ -135,7 +131,7 @@ int libseat_get_fd(struct libseat *seat);
|
|||
* milliseconds that might occur.
|
||||
*
|
||||
* Returns a positive number signifying processed internal messages on success.
|
||||
* Returns 0 if no messages were processed. Returns -1 and sets errno on error.
|
||||
* Returns 0-if no messages were processed. Returns -1 and sets errno on error.
|
||||
*/
|
||||
int libseat_dispatch(struct libseat *seat, int timeout);
|
||||
|
||||
|
|
|
@ -12,6 +12,5 @@ void linked_list_init(struct linked_list *list);
|
|||
void linked_list_insert(struct linked_list *list, struct linked_list *elem);
|
||||
void linked_list_remove(struct linked_list *elem);
|
||||
bool linked_list_empty(struct linked_list *list);
|
||||
void linked_list_take(struct linked_list *target, struct linked_list *source);
|
||||
|
||||
#endif
|
||||
|
|
23
include/list.h
Normal file
23
include/list.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef _SEATD_LIST_H
|
||||
#define _SEATD_LIST_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct list {
|
||||
size_t capacity;
|
||||
size_t length;
|
||||
void **items;
|
||||
};
|
||||
|
||||
void list_init(struct list *);
|
||||
void list_free(struct list *list);
|
||||
void list_add(struct list *list, void *item);
|
||||
void list_insert(struct list *list, size_t index, void *item);
|
||||
void list_del(struct list *list, size_t index);
|
||||
void list_concat(struct list *list, const struct list *source);
|
||||
void list_truncate(struct list *list);
|
||||
void *list_pop_front(struct list *list);
|
||||
void *list_pop_back(struct list *list);
|
||||
size_t list_find(struct list *list, const void *item);
|
||||
|
||||
#endif
|
|
@ -17,20 +17,31 @@
|
|||
#define __FILENAME__ __FILE__
|
||||
#endif
|
||||
|
||||
#define log_infof(fmt, ...) \
|
||||
_logf(LIBSEAT_LOG_LEVEL_INFO, "[%s:%d] " fmt, __FILENAME__, __LINE__, __VA_ARGS__)
|
||||
#define log_infof(fmt, ...) \
|
||||
_logf(LIBSEAT_LOG_LEVEL_INFO, "[%s:%d] %s: " fmt, __FILENAME__, __LINE__, __func__, \
|
||||
__VA_ARGS__)
|
||||
|
||||
#define log_info(str) _logf(LIBSEAT_LOG_LEVEL_INFO, "[%s:%d] %s", __FILENAME__, __LINE__, str)
|
||||
#define log_info(str) \
|
||||
_logf(LIBSEAT_LOG_LEVEL_INFO, "[%s:%d] %s: %s", __FILENAME__, __LINE__, __func__, str)
|
||||
|
||||
#define log_errorf(fmt, ...) \
|
||||
_logf(LIBSEAT_LOG_LEVEL_ERROR, "[%s:%d] " fmt, __FILENAME__, __LINE__, __VA_ARGS__)
|
||||
#define log_errorf(fmt, ...) \
|
||||
_logf(LIBSEAT_LOG_LEVEL_ERROR, "[%s:%d] %s: " fmt, __FILENAME__, __LINE__, __func__, \
|
||||
__VA_ARGS__)
|
||||
|
||||
#define log_error(str) _logf(LIBSEAT_LOG_LEVEL_ERROR, "[%s:%d] %s", __FILENAME__, __LINE__, str)
|
||||
#define log_error(str) \
|
||||
_logf(LIBSEAT_LOG_LEVEL_ERROR, "[%s:%d] %s: %s", __FILENAME__, __LINE__, __func__, str)
|
||||
|
||||
#define log_debugf(fmt, ...) \
|
||||
_logf(LIBSEAT_LOG_LEVEL_DEBUG, "[%s:%d] " fmt, __FILENAME__, __LINE__, __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define log_debugf(fmt, ...) \
|
||||
_logf(LIBSEAT_LOG_LEVEL_DEBUG, "[%s:%d] %s: " fmt, __FILENAME__, __LINE__, __func__, \
|
||||
__VA_ARGS__)
|
||||
|
||||
#define log_debug(str) _logf(LIBSEAT_LOG_LEVEL_DEBUG, "[%s:%d] %s", __FILENAME__, __LINE__, str)
|
||||
#define log_debug(str) \
|
||||
_logf(LIBSEAT_LOG_LEVEL_DEBUG, "[%s:%d] %s: %s", __FILENAME__, __LINE__, __func__, str)
|
||||
#else
|
||||
#define log_debugf(fmt, ...)
|
||||
#define log_debug(str)
|
||||
#endif
|
||||
|
||||
void log_init(void);
|
||||
void _logf(enum libseat_log_level level, const char *fmt, ...) ATTRIB_PRINTF(2, 3);
|
||||
|
|
|
@ -4,7 +4,11 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "linked_list.h"
|
||||
#include "list.h"
|
||||
|
||||
struct poller;
|
||||
struct event_source_fd;
|
||||
struct event_source_signal;
|
||||
|
||||
/*
|
||||
* These are the event types available from the poller.
|
||||
|
@ -14,16 +18,6 @@
|
|||
#define EVENT_ERROR 0x8
|
||||
#define EVENT_HANGUP 0x10
|
||||
|
||||
/**
|
||||
* The fd poller class. This must be created by poller_add_fd.
|
||||
*/
|
||||
struct event_source_fd;
|
||||
|
||||
/*
|
||||
* The signal poller class. This must be created by poller_add_signal.
|
||||
*/
|
||||
struct event_source_signal;
|
||||
|
||||
/**
|
||||
* The callback type used by event_source_fd, passed to poller_add_fd.
|
||||
*/
|
||||
|
@ -37,6 +31,21 @@ struct event_source_fd_impl {
|
|||
int (*destroy)(struct event_source_fd *event_source);
|
||||
};
|
||||
|
||||
/**
|
||||
* The fd poller class. This must be created by poller_add_fd.
|
||||
*/
|
||||
struct event_source_fd {
|
||||
const struct event_source_fd_impl *impl;
|
||||
event_source_fd_func_t func;
|
||||
|
||||
int fd;
|
||||
uint32_t mask;
|
||||
void *data;
|
||||
|
||||
struct poller *poller;
|
||||
bool killed;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the event_source_fd from the poller and frees the structure.
|
||||
*/
|
||||
|
@ -59,25 +68,26 @@ struct event_source_signal_impl {
|
|||
int (*destroy)(struct event_source_signal *event_source);
|
||||
};
|
||||
|
||||
/*
|
||||
* The signal poller class. This must be created by poller_add_signal.
|
||||
*/
|
||||
struct event_source_signal {
|
||||
const struct event_source_signal_impl *impl;
|
||||
event_source_signal_func_t func;
|
||||
|
||||
int signal;
|
||||
void *data;
|
||||
|
||||
struct poller *poller;
|
||||
bool raised;
|
||||
bool killed;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the event_source_siganl from the poller and frees the structure.
|
||||
*/
|
||||
int event_source_signal_destroy(struct event_source_signal *event_source);
|
||||
|
||||
/**
|
||||
* The poller base class. This must be created by poller_create.
|
||||
*/
|
||||
struct poller {
|
||||
struct linked_list signals;
|
||||
struct linked_list fds;
|
||||
|
||||
int signal_fds[2];
|
||||
struct pollfd *pollfds;
|
||||
size_t pollfds_len;
|
||||
size_t fd_event_sources;
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
/**
|
||||
* The interface that a poll backend must implement.
|
||||
*/
|
||||
|
@ -93,11 +103,30 @@ struct poll_impl {
|
|||
int (*poll)(struct poller *);
|
||||
};
|
||||
|
||||
/**
|
||||
* The poller base class. This must be created by poller_create.
|
||||
*/
|
||||
struct poller {
|
||||
struct list signals;
|
||||
struct list new_signals;
|
||||
struct list fds;
|
||||
struct list new_fds;
|
||||
|
||||
struct pollfd *pollfds;
|
||||
size_t pollfds_len;
|
||||
bool dirty;
|
||||
bool inpoll;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a poller with the best available polling backend. This poller must
|
||||
* be torn down with poller_destroy when it is no longer needed.
|
||||
*/
|
||||
/**
|
||||
* Initializes the poller. The poller must be torn down with poller_finish when
|
||||
* it is no longer needed.
|
||||
*/
|
||||
int poller_init(struct poller *poller);
|
||||
void poller_init(struct poller *poller);
|
||||
|
||||
/**
|
||||
* De-initializes the poller. This destroys all remaining event sources and
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#ifndef _SEATD_CONSTANTS_H
|
||||
#define _SEATD_CONSTANTS_H
|
||||
|
||||
#define MAX_PATH_LEN 256
|
||||
#define MAX_SEAT_LEN 64
|
||||
#define MAX_PATH_LEN 256
|
||||
#define MAX_SEAT_LEN 64
|
||||
#define MAX_SEAT_DEVICES 128
|
||||
#define MAX_SESSION_LEN 64
|
||||
#define MAX_SESSION_LEN 64
|
||||
|
||||
#define CLIENT_EVENT(opcode) (opcode)
|
||||
#define SERVER_EVENT(opcode) ((opcode) + (1 << 15))
|
||||
|
@ -15,7 +15,6 @@
|
|||
#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)
|
||||
|
@ -23,8 +22,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)
|
||||
#define SERVER_ERROR SERVER_EVENT(0x7FFF)
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -53,7 +51,10 @@ struct proto_server_seat_opened {
|
|||
|
||||
struct proto_server_device_opened {
|
||||
int device_id;
|
||||
// One fd in auxillary data
|
||||
};
|
||||
|
||||
struct proto_server_device_closed {
|
||||
int device_id;
|
||||
};
|
||||
|
||||
struct proto_server_error {
|
||||
|
|
|
@ -13,7 +13,6 @@ enum seat_device_type {
|
|||
SEAT_DEVICE_TYPE_NORMAL,
|
||||
SEAT_DEVICE_TYPE_EVDEV,
|
||||
SEAT_DEVICE_TYPE_DRM,
|
||||
SEAT_DEVICE_TYPE_WSCONS,
|
||||
};
|
||||
|
||||
struct seat_device {
|
||||
|
@ -27,15 +26,15 @@ struct seat_device {
|
|||
};
|
||||
|
||||
struct seat {
|
||||
struct linked_list link; // server::seats
|
||||
char *seat_name;
|
||||
struct linked_list clients;
|
||||
struct client *active_client;
|
||||
struct client *next_client;
|
||||
|
||||
bool vt_bound;
|
||||
int cur_vt;
|
||||
int session_cnt;
|
||||
bool vt_pending_ack;
|
||||
int next_vt;
|
||||
int curttyfd;
|
||||
};
|
||||
|
||||
struct seat *seat_create(const char *name, bool vt_bound);
|
||||
|
@ -44,6 +43,7 @@ void seat_destroy(struct seat *seat);
|
|||
int seat_add_client(struct seat *seat, struct client *client);
|
||||
int seat_remove_client(struct client *client);
|
||||
int seat_open_client(struct seat *seat, struct client *client);
|
||||
int seat_close_client(struct client *client);
|
||||
int seat_ack_disable_client(struct client *client);
|
||||
|
||||
struct seat_device *seat_open_device(struct client *client, const char *path);
|
||||
|
@ -51,7 +51,7 @@ int seat_close_device(struct client *client, struct seat_device *seat_device);
|
|||
struct seat_device *seat_find_device(struct client *client, int device_id);
|
||||
|
||||
int seat_set_next_session(struct client *client, int session);
|
||||
int seat_vt_activate(struct seat *seat);
|
||||
int seat_vt_release(struct seat *seat);
|
||||
int seat_activate(struct seat *seat);
|
||||
int seat_prepare_vt_switch(struct seat *seat);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "linked_list.h"
|
||||
#include "list.h"
|
||||
#include "poller.h"
|
||||
|
||||
struct client;
|
||||
|
@ -12,8 +12,7 @@ struct server {
|
|||
bool running;
|
||||
struct poller poller;
|
||||
|
||||
struct linked_list seats;
|
||||
struct linked_list idle_clients;
|
||||
struct list seats;
|
||||
};
|
||||
|
||||
int server_init(struct server *server);
|
||||
|
|
|
@ -8,8 +8,7 @@ int terminal_open(int vt);
|
|||
int terminal_set_process_switching(int fd, bool enable);
|
||||
int terminal_current_vt(int fd);
|
||||
int terminal_switch_vt(int fd, int vt);
|
||||
int terminal_ack_release(int fd);
|
||||
int terminal_ack_acquire(int fd);
|
||||
int terminal_ack_switch(int fd);
|
||||
int terminal_set_keyboard(int fd, bool enable);
|
||||
int terminal_set_graphics(int fd, bool enable);
|
||||
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
#ifndef _TEST_H
|
||||
#define _TEST_H
|
||||
|
||||
char *__curtestname = "<none>";
|
||||
|
||||
#define test_run(func) \
|
||||
do { \
|
||||
char *orig = __curtestname; \
|
||||
__curtestname = #func; \
|
||||
func(); \
|
||||
__curtestname = orig; \
|
||||
} while (0)
|
||||
|
||||
#define test_assert(cond) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
fprintf(stderr, "%s:%d: %s: test_assert failed: %s\n", __FILE__, __LINE__, \
|
||||
__curtestname, #cond); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
|
@ -1,6 +0,0 @@
|
|||
#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_LIBELOGIND)
|
||||
#if defined(HAVE_ELOGIND)
|
||||
#include <elogind/sd-bus.h>
|
||||
#include <elogind/sd-login.h>
|
||||
#elif defined(HAVE_LIBSYSTEMD)
|
||||
#elif defined(HAVE_SYSTEMD)
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <systemd/sd-login.h>
|
||||
#else
|
||||
|
@ -28,17 +28,9 @@
|
|||
#include "libseat.h"
|
||||
#include "log.h"
|
||||
|
||||
static int dev_major_is_drm(unsigned int dev_major) {
|
||||
return dev_major == 226;
|
||||
}
|
||||
|
||||
static int dev_is_drm(dev_t device) {
|
||||
return dev_major_is_drm(major(device));
|
||||
}
|
||||
|
||||
struct backend_logind {
|
||||
struct libseat base;
|
||||
const struct libseat_seat_listener *seat_listener;
|
||||
struct libseat_seat_listener *seat_listener;
|
||||
void *seat_listener_data;
|
||||
|
||||
sd_bus *bus;
|
||||
|
@ -47,6 +39,7 @@ struct backend_logind {
|
|||
char *path;
|
||||
char *seat_path;
|
||||
|
||||
bool can_graphical;
|
||||
bool active;
|
||||
bool initial_setup;
|
||||
int has_drm;
|
||||
|
@ -54,7 +47,6 @@ struct backend_logind {
|
|||
|
||||
const struct seat_impl logind_impl;
|
||||
static struct backend_logind *backend_logind_from_libseat_backend(struct libseat *base);
|
||||
static void release_control(struct backend_logind *backend);
|
||||
|
||||
static void destroy(struct backend_logind *backend) {
|
||||
assert(backend);
|
||||
|
@ -70,52 +62,10 @@ static void destroy(struct backend_logind *backend) {
|
|||
|
||||
static int close_seat(struct libseat *base) {
|
||||
struct backend_logind *backend = backend_logind_from_libseat_backend(base);
|
||||
release_control(backend);
|
||||
destroy(backend);
|
||||
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);
|
||||
|
||||
|
@ -162,11 +112,9 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -182,6 +130,7 @@ static int close_device(struct libseat *base, int device_id) {
|
|||
struct stat st = {0};
|
||||
if (fstat(fd, &st) < 0) {
|
||||
log_errorf("Could not stat fd %d", fd);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (dev_is_drm(st.st_rdev)) {
|
||||
|
@ -189,6 +138,7 @@ static int close_device(struct libseat *base, int device_id) {
|
|||
log_debugf("DRM device closed, current total: %d", session->has_drm);
|
||||
assert(session->has_drm >= 0);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
@ -201,8 +151,8 @@ 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;
|
||||
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static int switch_session(struct libseat *base, int s) {
|
||||
|
@ -215,7 +165,8 @@ static int switch_session(struct libseat *base, int s) {
|
|||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
int ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", session->seat_path,
|
||||
int ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||
"/org/freedesktop/login1/seat/seat0",
|
||||
"org.freedesktop.login1.Seat", "SwitchTo", &error, &msg, "u",
|
||||
(uint32_t)s);
|
||||
if (ret < 0) {
|
||||
|
@ -224,8 +175,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;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static int disable_seat(struct libseat *base) {
|
||||
|
@ -235,13 +185,7 @@ static int disable_seat(struct libseat *base) {
|
|||
|
||||
static int get_fd(struct libseat *base) {
|
||||
struct backend_logind *backend = backend_logind_from_libseat_backend(base);
|
||||
int fd = sd_bus_get_fd(backend->bus);
|
||||
if (fd >= 0) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
errno = -fd;
|
||||
return -1;
|
||||
return sd_bus_get_fd(backend->bus);
|
||||
}
|
||||
|
||||
static int poll_connection(struct backend_logind *backend, int timeout) {
|
||||
|
@ -265,7 +209,7 @@ static int poll_connection(struct backend_logind *backend, int timeout) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dispatch_and_execute(struct libseat *base, int timeout) {
|
||||
static int dispatch_background(struct libseat *base, int timeout) {
|
||||
struct backend_logind *backend = backend_logind_from_libseat_backend(base);
|
||||
if (backend->initial_setup) {
|
||||
backend->initial_setup = false;
|
||||
|
@ -292,12 +236,15 @@ static int dispatch_and_execute(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;
|
||||
}
|
||||
|
||||
|
@ -306,11 +253,10 @@ static struct backend_logind *backend_logind_from_libseat_backend(struct libseat
|
|||
return (struct backend_logind *)base;
|
||||
}
|
||||
|
||||
static int session_activate(struct backend_logind *session) {
|
||||
static bool 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) {
|
||||
|
@ -319,26 +265,10 @@ static int session_activate(struct backend_logind *session) {
|
|||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
return ret;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
static bool take_control(struct backend_logind *session) {
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
|
@ -351,22 +281,7 @@ static int take_control(struct backend_logind *session) {
|
|||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void release_control(struct backend_logind *session) {
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
int ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", session->path,
|
||||
"org.freedesktop.login1.Session", "ReleaseControl", &error,
|
||||
&msg, "");
|
||||
if (ret < 0) {
|
||||
log_errorf("Could not release control of session: %s", error.message);
|
||||
}
|
||||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static void set_active(struct backend_logind *backend, bool active) {
|
||||
|
@ -395,7 +310,7 @@ static int pause_device(sd_bus_message *msg, void *userdata, sd_bus_error *ret_e
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (dev_major_is_drm(major) && strcmp(type, "gone") != 0) {
|
||||
if (dev_is_drm(makedev(major, minor)) && strcmp(type, "gone") != 0) {
|
||||
log_debugf("DRM device paused: %s", type);
|
||||
assert(session->has_drm > 0);
|
||||
set_active(session, false);
|
||||
|
@ -427,7 +342,7 @@ static int resume_device(sd_bus_message *msg, void *userdata, sd_bus_error *ret_
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (dev_major_is_drm(major)) {
|
||||
if (dev_is_drm(makedev(major, minor))) {
|
||||
log_debug("DRM device resumed");
|
||||
assert(session->has_drm > 0);
|
||||
set_active(session, true);
|
||||
|
@ -472,7 +387,8 @@ static int properties_changed(sd_bus_message *msg, void *userdata, sd_bus_error
|
|||
goto error;
|
||||
}
|
||||
|
||||
if (is_session && strcmp(s, "Active") == 0) {
|
||||
if ((is_session && strcmp(s, "Active") == 0) ||
|
||||
(is_seat && strcmp(s, "CanGraphical"))) {
|
||||
int ret;
|
||||
ret = sd_bus_message_enter_container(msg, 'v', "b");
|
||||
if (ret < 0) {
|
||||
|
@ -486,7 +402,11 @@ static int properties_changed(sd_bus_message *msg, void *userdata, sd_bus_error
|
|||
}
|
||||
|
||||
log_debugf("%s state changed: %d", s, value);
|
||||
set_active(session, value);
|
||||
if (is_session) {
|
||||
set_active(session, value);
|
||||
} else {
|
||||
session->can_graphical = value;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
sd_bus_message_skip(msg, "{sv}");
|
||||
|
@ -510,10 +430,12 @@ static int properties_changed(sd_bus_message *msg, void *userdata, sd_bus_error
|
|||
// PropertiesChanged arg 3: changed properties without values
|
||||
sd_bus_message_enter_container(msg, 'a', "s");
|
||||
while ((ret = sd_bus_message_read_basic(msg, 's', &s)) > 0) {
|
||||
if (is_session && strcmp(s, "Active") == 0) {
|
||||
if ((is_session && strcmp(s, "Active") == 0) ||
|
||||
(is_seat && strcmp(s, "CanGraphical"))) {
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
const char *obj = "org.freedesktop.login1.Session";
|
||||
const char *field = "Active";
|
||||
const char *obj = is_session ? "org.freedesktop.login1.Session"
|
||||
: "org.freedesktop.login1.Seat";
|
||||
const char *field = is_session ? "Active" : "CanGraphical";
|
||||
bool value;
|
||||
ret = sd_bus_get_property_trivial(session->bus, "org.freedesktop.login1",
|
||||
session->path, obj, field, &error, 'b',
|
||||
|
@ -524,7 +446,11 @@ static int properties_changed(sd_bus_message *msg, void *userdata, sd_bus_error
|
|||
}
|
||||
|
||||
log_debugf("%s state changed: %d", field, value);
|
||||
set_active(session, value);
|
||||
if (is_session) {
|
||||
set_active(session, value);
|
||||
} else {
|
||||
session->can_graphical = value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -537,7 +463,7 @@ error:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int add_signal_matches(struct backend_logind *backend) {
|
||||
static bool 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";
|
||||
|
@ -547,34 +473,34 @@ static int 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 ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
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 ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
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 ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
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 ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int find_session_path(struct backend_logind *session) {
|
||||
static bool find_session_path(struct backend_logind *session) {
|
||||
int ret;
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
@ -599,10 +525,10 @@ out:
|
|||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
|
||||
return ret;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static int find_seat_path(struct backend_logind *session) {
|
||||
static bool find_seat_path(struct backend_logind *session) {
|
||||
int ret;
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
@ -627,10 +553,10 @@ out:
|
|||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
|
||||
return ret;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static int get_display_session(char **session_id) {
|
||||
static bool get_display_session(char **session_id) {
|
||||
assert(session_id != NULL);
|
||||
char *xdg_session_id = getenv("XDG_SESSION_ID");
|
||||
int ret;
|
||||
|
@ -663,12 +589,12 @@ static int get_display_session(char **session_id) {
|
|||
|
||||
success:
|
||||
assert(*session_id != NULL);
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
error:
|
||||
free(*session_id);
|
||||
*session_id = NULL;
|
||||
return ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int set_type(struct backend_logind *backend, const char *type) {
|
||||
|
@ -687,19 +613,17 @@ static int set_type(struct backend_logind *backend, const char *type) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct libseat *logind_open_seat(const struct libseat_seat_listener *listener, void *data) {
|
||||
static struct libseat *logind_open_seat(struct libseat_seat_listener *listener, void *data) {
|
||||
struct backend_logind *backend = calloc(1, sizeof(struct backend_logind));
|
||||
if (backend == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ret;
|
||||
ret = get_display_session(&backend->id);
|
||||
if (ret < 0) {
|
||||
if (!get_display_session(&backend->id)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = sd_session_get_seat(backend->id, &backend->seat);
|
||||
int ret = sd_session_get_seat(backend->id, &backend->seat);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
@ -709,34 +633,32 @@ static struct libseat *logind_open_seat(const struct libseat_seat_listener *list
|
|||
goto error;
|
||||
}
|
||||
|
||||
ret = find_session_path(backend);
|
||||
if (ret < 0) {
|
||||
if (!find_session_path(backend)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = find_seat_path(backend);
|
||||
if (ret < 0) {
|
||||
if (!find_seat_path(backend)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = add_signal_matches(backend);
|
||||
if (ret < 0) {
|
||||
if (!add_signal_matches(backend)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = session_activate(backend);
|
||||
if (ret < 0) {
|
||||
if (!session_activate(backend)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = session_check_active(backend);
|
||||
if (ret < 0) {
|
||||
if (!take_control(backend)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = take_control(backend);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
backend->can_graphical = sd_seat_can_graphical(backend->seat);
|
||||
while (!backend->can_graphical) {
|
||||
if (poll_connection(backend, -1) == -1) {
|
||||
log_errorf("Could not poll connection: %s", strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
const char *env = getenv("XDG_SESSION_TYPE");
|
||||
|
@ -745,16 +667,17 @@ static struct libseat *logind_open_seat(const struct libseat_seat_listener *list
|
|||
}
|
||||
|
||||
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;
|
||||
if (backend != NULL) {
|
||||
destroy(backend);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -767,5 +690,5 @@ const struct seat_impl logind_impl = {
|
|||
.close_device = close_device,
|
||||
.switch_session = switch_session,
|
||||
.get_fd = get_fd,
|
||||
.dispatch = dispatch_and_execute,
|
||||
.dispatch = dispatch_background,
|
||||
};
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
#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,38 +33,45 @@ struct pending_event {
|
|||
struct backend_seatd {
|
||||
struct libseat base;
|
||||
struct connection connection;
|
||||
const struct libseat_seat_listener *seat_listener;
|
||||
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 | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
log_errorf("Could not create socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
const char *path = getenv("SEATD_SOCK");
|
||||
if (set_nonblock(fd) == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
char *path = getenv("SEATD_SOCK");
|
||||
if (path == NULL) {
|
||||
path = SEATD_DEFAULTPATH;
|
||||
path = "/run/seatd.sock";
|
||||
}
|
||||
addr.unix.sun_family = AF_UNIX;
|
||||
strncpy(addr.unix.sun_path, path, sizeof addr.unix.sun_path - 1);
|
||||
strncpy(addr.unix.sun_path, path, sizeof addr.unix.sun_path);
|
||||
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;
|
||||
};
|
||||
|
@ -81,87 +88,35 @@ static struct backend_seatd *backend_seatd_from_libseat_backend(struct libseat *
|
|||
return (struct backend_seatd *)base;
|
||||
}
|
||||
|
||||
static void cleanup(struct backend_seatd *backend) {
|
||||
if (backend->connection.fd != -1) {
|
||||
close(backend->connection.fd);
|
||||
backend->connection.fd = -1;
|
||||
}
|
||||
connection_close_fds(&backend->connection);
|
||||
while (!linked_list_empty(&backend->pending_events)) {
|
||||
struct pending_event *ev = (struct pending_event *)backend->pending_events.next;
|
||||
linked_list_remove(&ev->link);
|
||||
free(ev);
|
||||
static void handle_enable_seat(struct backend_seatd *backend) {
|
||||
log_info("Enabling seat");
|
||||
if (backend->seat_listener != NULL && backend->seat_listener->enable_seat != NULL) {
|
||||
backend->seat_listener->enable_seat(&backend->base, backend->seat_listener_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy(struct backend_seatd *backend) {
|
||||
cleanup(backend);
|
||||
free(backend);
|
||||
}
|
||||
|
||||
static void set_error(struct backend_seatd *backend) {
|
||||
if (backend->error) {
|
||||
return;
|
||||
static void handle_disable_seat(struct backend_seatd *backend) {
|
||||
log_info("Disabling seat");
|
||||
if (backend->seat_listener != NULL && backend->seat_listener->disable_seat != NULL) {
|
||||
backend->seat_listener->disable_seat(&backend->base, backend->seat_listener_data);
|
||||
}
|
||||
|
||||
backend->error = true;
|
||||
cleanup(backend);
|
||||
}
|
||||
|
||||
static inline int conn_put(struct backend_seatd *backend, const void *data, const size_t data_len) {
|
||||
if (connection_put(&backend->connection, data, data_len) == -1) {
|
||||
log_errorf("Could not send request: %s", strerror(errno));
|
||||
set_error(backend);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int conn_flush(struct backend_seatd *backend) {
|
||||
if (connection_flush(&backend->connection) == -1) {
|
||||
log_errorf("Could not flush connection: %s", strerror(errno));
|
||||
set_error(backend);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int conn_get(struct backend_seatd *backend, void *target, const size_t target_len) {
|
||||
if (connection_get(&backend->connection, target, target_len) == -1) {
|
||||
log_error("Invalid message: insufficient data received");
|
||||
set_error(backend);
|
||||
errno = EBADMSG;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int conn_get_fd(struct backend_seatd *backend, int *fd) {
|
||||
if (connection_get_fd(&backend->connection, fd) == -1) {
|
||||
log_error("Invalid message: insufficient data received");
|
||||
set_error(backend);
|
||||
errno = EBADMSG;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t read_header(struct backend_seatd *backend, uint16_t expected_opcode,
|
||||
size_t expected_size, bool variable) {
|
||||
static size_t read_header(struct connection *connection, uint16_t expected_opcode) {
|
||||
struct proto_header header;
|
||||
if (conn_get(backend, &header, sizeof header) == -1) {
|
||||
set_error(backend);
|
||||
if (connection_get(connection, &header, sizeof header) == -1) {
|
||||
log_error("Received invalid message: header too short");
|
||||
return SIZE_MAX;
|
||||
}
|
||||
if (header.opcode != expected_opcode) {
|
||||
connection_restore(connection, sizeof header);
|
||||
struct proto_server_error msg;
|
||||
if (header.opcode != SERVER_ERROR) {
|
||||
log_errorf("Unexpected response: expected opcode %d, received opcode %d",
|
||||
log_errorf("Received invalid message: expected opcode %d, received opcode %d",
|
||||
expected_opcode, header.opcode);
|
||||
set_error(backend);
|
||||
errno = EBADMSG;
|
||||
} else if (header.size != sizeof msg || conn_get(backend, &msg, sizeof msg) == -1) {
|
||||
set_error(backend);
|
||||
} else if (connection_get(connection, &msg, sizeof msg) == -1) {
|
||||
log_error("Received invalid message");
|
||||
errno = EBADMSG;
|
||||
} else {
|
||||
errno = msg.error_code;
|
||||
|
@ -169,20 +124,12 @@ static size_t read_header(struct backend_seatd *backend, uint16_t expected_opcod
|
|||
return SIZE_MAX;
|
||||
}
|
||||
|
||||
if ((!variable && header.size != expected_size) || (variable && header.size < expected_size)) {
|
||||
log_errorf("Invalid message: does not match expected size: variable: %d, header.size: %d, expected size: %zd",
|
||||
variable, header.size, expected_size);
|
||||
set_error(backend);
|
||||
errno = EBADMSG;
|
||||
return SIZE_MAX;
|
||||
}
|
||||
return header.size;
|
||||
}
|
||||
|
||||
static int queue_event(struct backend_seatd *backend, int opcode) {
|
||||
struct pending_event *ev = calloc(1, sizeof(struct pending_event));
|
||||
if (ev == NULL) {
|
||||
log_errorf("Allocation failed: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -191,11 +138,15 @@ static int queue_event(struct backend_seatd *backend, int opcode) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
static void execute_events(struct backend_seatd *backend) {
|
||||
struct linked_list list = {
|
||||
.next = backend->pending_events.next,
|
||||
.prev = backend->pending_events.prev,
|
||||
};
|
||||
list.next->prev = &list;
|
||||
list.prev->next = &list;
|
||||
|
||||
linked_list_init(&backend->pending_events);
|
||||
while (!linked_list_empty(&list)) {
|
||||
struct pending_event *ev = (struct pending_event *)list.next;
|
||||
int opcode = ev->opcode;
|
||||
|
@ -204,22 +155,15 @@ static int execute_events(struct backend_seatd *backend) {
|
|||
|
||||
switch (opcode) {
|
||||
case SERVER_DISABLE_SEAT:
|
||||
log_info("Disabling seat");
|
||||
backend->seat_listener->disable_seat(&backend->base,
|
||||
backend->seat_listener_data);
|
||||
handle_disable_seat(backend);
|
||||
break;
|
||||
case SERVER_ENABLE_SEAT:
|
||||
log_info("Enabling seat");
|
||||
backend->seat_listener->enable_seat(&backend->base,
|
||||
backend->seat_listener_data);
|
||||
handle_enable_seat(backend);
|
||||
break;
|
||||
default:
|
||||
log_errorf("Invalid opcode: %d", opcode);
|
||||
abort();
|
||||
}
|
||||
executed++;
|
||||
}
|
||||
return executed;
|
||||
}
|
||||
|
||||
static int dispatch_pending(struct backend_seatd *backend, int *opcode) {
|
||||
|
@ -228,16 +172,9 @@ 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) {
|
||||
set_error(backend);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
@ -253,15 +190,6 @@ 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,
|
||||
|
@ -292,23 +220,20 @@ static int poll_connection(struct backend_seatd *backend, int timeout) {
|
|||
}
|
||||
|
||||
static int dispatch(struct backend_seatd *backend) {
|
||||
if (conn_flush(backend) == -1) {
|
||||
if (connection_flush(&backend->connection) == -1) {
|
||||
log_errorf("Could not flush connection: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
while (true) {
|
||||
int opcode = 0;
|
||||
if (dispatch_pending(backend, &opcode) == -1) {
|
||||
log_errorf("Could not dispatch pending messages: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (opcode != 0) {
|
||||
break;
|
||||
}
|
||||
int opcode = 0, res = 0;
|
||||
while ((res = dispatch_pending(backend, &opcode)) == 0 && opcode == 0) {
|
||||
if (poll_connection(backend, -1) == -1) {
|
||||
log_errorf("Could not poll connection: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (res == -1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -317,52 +242,52 @@ static int get_fd(struct libseat *base) {
|
|||
return backend->connection.fd;
|
||||
}
|
||||
|
||||
static int dispatch_and_execute(struct libseat *base, int timeout) {
|
||||
static int dispatch_background(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) {
|
||||
// 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 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.
|
||||
int read = 0;
|
||||
if (predispatch > 0 || timeout == 0) {
|
||||
if (timeout == 0) {
|
||||
read = connection_read(&backend->connection);
|
||||
} else {
|
||||
read = poll_connection(backend, timeout);
|
||||
}
|
||||
|
||||
if (read == 0) {
|
||||
return predispatch;
|
||||
if (read > 0) {
|
||||
dispatched += dispatch_pending(backend, NULL);
|
||||
} else if (read == -1 && errno != EAGAIN) {
|
||||
log_errorf("Could not read from connection: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
int postdispatch = dispatch_pending_and_execute(backend);
|
||||
if (postdispatch == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return predispatch + postdispatch;
|
||||
execute_events(backend);
|
||||
return dispatched;
|
||||
}
|
||||
|
||||
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);
|
||||
static void destroy(struct backend_seatd *backend) {
|
||||
if (backend->connection.fd != -1) {
|
||||
close(backend->connection.fd);
|
||||
backend->connection.fd = -1;
|
||||
}
|
||||
connection_close_fds(&backend->connection);
|
||||
while (!linked_list_empty(&backend->pending_events)) {
|
||||
struct pending_event *ev = (struct pending_event *)backend->pending_events.next;
|
||||
linked_list_remove(&ev->link);
|
||||
free(ev);
|
||||
}
|
||||
free(backend);
|
||||
}
|
||||
|
||||
static struct libseat *_open_seat(struct libseat_seat_listener *listener, void *data, int fd) {
|
||||
struct backend_seatd *backend = calloc(1, sizeof(struct backend_seatd));
|
||||
if (backend == NULL) {
|
||||
log_errorf("Allocation failed: %s", strerror(errno));
|
||||
goto alloc_error;
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
backend->seat_listener = listener;
|
||||
backend->seat_listener_data = data;
|
||||
backend->connection.fd = fd;
|
||||
|
@ -373,36 +298,46 @@ static struct libseat *_open_seat(const struct libseat_seat_listener *listener,
|
|||
.opcode = CLIENT_OPEN_SEAT,
|
||||
.size = 0,
|
||||
};
|
||||
if (conn_put(backend, &header, sizeof header) == -1 || dispatch(backend) == -1) {
|
||||
goto backend_error;
|
||||
|
||||
if (connection_put(&backend->connection, &header, sizeof header) == -1 ||
|
||||
dispatch(backend) == -1) {
|
||||
destroy(backend);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t size = read_header(&backend->connection, SERVER_SEAT_OPENED);
|
||||
if (size == SIZE_MAX) {
|
||||
destroy(backend);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct proto_server_seat_opened rmsg;
|
||||
size_t size = read_header(backend, SERVER_SEAT_OPENED, sizeof rmsg, true);
|
||||
if (size == SIZE_MAX || conn_get(backend, &rmsg, sizeof rmsg) == -1) {
|
||||
goto backend_error;
|
||||
}
|
||||
if (rmsg.seat_name_len != size - sizeof rmsg) {
|
||||
log_errorf("Invalid message: seat_name_len does not match remaining message size (%d != %zd)",
|
||||
rmsg.seat_name_len, size);
|
||||
errno = EBADMSG;
|
||||
goto backend_error;
|
||||
}
|
||||
if (conn_get(backend, backend->seat_name, rmsg.seat_name_len) == -1) {
|
||||
goto backend_error;
|
||||
if (sizeof rmsg > size) {
|
||||
goto badmsg_error;
|
||||
}
|
||||
|
||||
execute_events(backend);
|
||||
if (connection_get(&backend->connection, &rmsg, sizeof rmsg) == -1) {
|
||||
goto badmsg_error;
|
||||
};
|
||||
|
||||
if (sizeof rmsg + rmsg.seat_name_len > size ||
|
||||
rmsg.seat_name_len >= sizeof backend->seat_name) {
|
||||
goto badmsg_error;
|
||||
}
|
||||
|
||||
if (connection_get(&backend->connection, backend->seat_name, rmsg.seat_name_len) == -1) {
|
||||
goto badmsg_error;
|
||||
};
|
||||
|
||||
return &backend->base;
|
||||
|
||||
backend_error:
|
||||
destroy(backend);
|
||||
alloc_error:
|
||||
close(fd);
|
||||
badmsg_error:
|
||||
log_error("Received invalid message");
|
||||
errno = EBADMSG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct libseat *open_seat(const struct libseat_seat_listener *listener, void *data) {
|
||||
static struct libseat *open_seat(struct libseat_seat_listener *listener, void *data) {
|
||||
int fd = seatd_connect();
|
||||
if (fd == -1) {
|
||||
return NULL;
|
||||
|
@ -418,22 +353,21 @@ static int close_seat(struct libseat *base) {
|
|||
.opcode = CLIENT_CLOSE_SEAT,
|
||||
.size = 0,
|
||||
};
|
||||
if (conn_put(backend, &header, sizeof header) == -1 || dispatch(backend) == -1) {
|
||||
goto error;
|
||||
|
||||
if (connection_put(&backend->connection, &header, sizeof header) == -1 ||
|
||||
dispatch(backend) == -1) {
|
||||
destroy(backend);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read_header(backend, SERVER_SEAT_CLOSED, 0, false) == SIZE_MAX) {
|
||||
goto error;
|
||||
size_t size = read_header(&backend->connection, SERVER_SEAT_CLOSED);
|
||||
if (size == SIZE_MAX) {
|
||||
destroy(backend);
|
||||
return -1;
|
||||
}
|
||||
|
||||
execute_events(backend);
|
||||
destroy(backend);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
execute_events(backend);
|
||||
destroy(backend);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *seat_name(struct libseat *base) {
|
||||
|
@ -441,42 +375,9 @@ 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) {
|
||||
errno = ENOTCONN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t pathlen = strlen(path) + 1;
|
||||
if (pathlen > MAX_PATH_LEN) {
|
||||
errno = EINVAL;
|
||||
|
@ -490,32 +391,42 @@ static int open_device(struct libseat *base, const char *path, int *fd) {
|
|||
.opcode = CLIENT_OPEN_DEVICE,
|
||||
.size = sizeof msg + pathlen,
|
||||
};
|
||||
if (conn_put(backend, &header, sizeof header) == -1 ||
|
||||
conn_put(backend, &msg, sizeof msg) == -1 || conn_put(backend, path, pathlen) == -1 ||
|
||||
dispatch(backend) == -1) {
|
||||
goto error;
|
||||
|
||||
if (connection_put(&backend->connection, &header, sizeof header) == -1 ||
|
||||
connection_put(&backend->connection, &msg, sizeof msg) == -1 ||
|
||||
connection_put(&backend->connection, path, pathlen) == -1 || dispatch(backend) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t size = read_header(&backend->connection, SERVER_DEVICE_OPENED);
|
||||
if (size == SIZE_MAX) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct proto_server_device_opened rmsg;
|
||||
if (read_header(backend, SERVER_DEVICE_OPENED, sizeof rmsg, false) == SIZE_MAX ||
|
||||
conn_get(backend, &rmsg, sizeof rmsg) == -1 || conn_get_fd(backend, fd)) {
|
||||
goto error;
|
||||
if (sizeof rmsg > size) {
|
||||
goto badmsg_error;
|
||||
}
|
||||
if (connection_get(&backend->connection, &rmsg, sizeof rmsg) == -1) {
|
||||
goto badmsg_error;
|
||||
}
|
||||
|
||||
check_pending_events(backend);
|
||||
int received_fd = connection_get_fd(&backend->connection);
|
||||
if (received_fd == -1) {
|
||||
goto badmsg_error;
|
||||
}
|
||||
|
||||
*fd = received_fd;
|
||||
return rmsg.device_id;
|
||||
|
||||
error:
|
||||
check_pending_events(backend);
|
||||
badmsg_error:
|
||||
log_error("Received invalid message");
|
||||
errno = EBADMSG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int close_device(struct libseat *base, int device_id) {
|
||||
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
|
||||
if (backend->error) {
|
||||
errno = ENOTCONN;
|
||||
return -1;
|
||||
}
|
||||
if (device_id < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
|
@ -528,29 +439,38 @@ static int close_device(struct libseat *base, int device_id) {
|
|||
.opcode = CLIENT_CLOSE_DEVICE,
|
||||
.size = sizeof msg,
|
||||
};
|
||||
if (conn_put(backend, &header, sizeof header) == -1 ||
|
||||
conn_put(backend, &msg, sizeof msg) == -1 || dispatch(backend) == -1) {
|
||||
goto error;
|
||||
|
||||
if (connection_put(&backend->connection, &header, sizeof header) == -1 ||
|
||||
connection_put(&backend->connection, &msg, sizeof msg) == -1 || dispatch(backend) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read_header(backend, SERVER_DEVICE_CLOSED, 0, false) == SIZE_MAX) {
|
||||
goto error;
|
||||
size_t size = read_header(&backend->connection, SERVER_DEVICE_CLOSED);
|
||||
if (size == SIZE_MAX) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct proto_server_device_closed rmsg;
|
||||
if (sizeof rmsg > size) {
|
||||
goto badmsg_error;
|
||||
}
|
||||
if (connection_get(&backend->connection, &rmsg, sizeof rmsg) == -1) {
|
||||
goto badmsg_error;
|
||||
}
|
||||
if (rmsg.device_id != device_id) {
|
||||
goto badmsg_error;
|
||||
}
|
||||
|
||||
check_pending_events(backend);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
check_pending_events(backend);
|
||||
badmsg_error:
|
||||
log_error("Received invalid message");
|
||||
errno = EBADMSG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int switch_session(struct libseat *base, int session) {
|
||||
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
|
||||
if (backend->error) {
|
||||
errno = ENOTCONN;
|
||||
return -1;
|
||||
}
|
||||
if (session < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -562,8 +482,10 @@ static int switch_session(struct libseat *base, int session) {
|
|||
.opcode = CLIENT_SWITCH_SESSION,
|
||||
.size = sizeof msg,
|
||||
};
|
||||
if (conn_put(backend, &header, sizeof header) == -1 ||
|
||||
conn_put(backend, &msg, sizeof msg) == -1 || conn_flush(backend) == -1) {
|
||||
|
||||
if (connection_put(&backend->connection, &header, sizeof header) == -1 ||
|
||||
connection_put(&backend->connection, &msg, sizeof msg) == -1 ||
|
||||
connection_flush(&backend->connection) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -572,15 +494,13 @@ static int switch_session(struct libseat *base, int session) {
|
|||
|
||||
static int disable_seat(struct libseat *base) {
|
||||
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
|
||||
if (backend->error) {
|
||||
errno = ENOTCONN;
|
||||
return -1;
|
||||
}
|
||||
struct proto_header header = {
|
||||
.opcode = CLIENT_DISABLE_SEAT,
|
||||
.size = 0,
|
||||
};
|
||||
if (conn_put(backend, &header, sizeof header) == -1 || conn_flush(backend) == -1) {
|
||||
|
||||
if (connection_put(&backend->connection, &header, sizeof header) == -1 ||
|
||||
connection_flush(&backend->connection) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -596,14 +516,33 @@ const struct seat_impl seatd_impl = {
|
|||
.close_device = close_device,
|
||||
.switch_session = switch_session,
|
||||
.get_fd = get_fd,
|
||||
.dispatch = dispatch_and_execute,
|
||||
.dispatch = dispatch_background,
|
||||
};
|
||||
|
||||
#ifdef BUILTIN_ENABLED
|
||||
#include <signal.h>
|
||||
|
||||
static struct libseat *builtin_open_seat(const struct libseat_seat_listener *listener, void *data) {
|
||||
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) {
|
||||
int fds[2];
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, fds) == -1) {
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
|
||||
log_errorf("Could not create socket pair: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
@ -615,7 +554,6 @@ static struct libseat *builtin_open_seat(const struct libseat_seat_listener *lis
|
|||
close(fds[1]);
|
||||
return NULL;
|
||||
} else if (pid == 0) {
|
||||
close(fds[1]);
|
||||
int fd = fds[0];
|
||||
int res = 0;
|
||||
struct server server = {0};
|
||||
|
@ -630,22 +568,20 @@ static struct libseat *builtin_open_seat(const struct libseat_seat_listener *lis
|
|||
res = 1;
|
||||
goto server_error;
|
||||
}
|
||||
log_info("Started embedded seatd");
|
||||
set_deathsig(SIGTERM);
|
||||
while (server.running) {
|
||||
if (poller_poll(&server.poller) == -1) {
|
||||
log_errorf("Could not poll server socket: %s", strerror(errno));
|
||||
res = 1;
|
||||
break;
|
||||
goto server_error;
|
||||
}
|
||||
}
|
||||
server_error:
|
||||
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);
|
||||
}
|
||||
|
@ -660,6 +596,6 @@ const struct seat_impl builtin_impl = {
|
|||
.close_device = close_device,
|
||||
.switch_session = switch_session,
|
||||
.get_fd = get_fd,
|
||||
.dispatch = dispatch_and_execute,
|
||||
.dispatch = dispatch_background,
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
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
|
||||
|
@ -25,8 +24,6 @@ static const struct named_backend impls[] = {
|
|||
#ifdef BUILTIN_ENABLED
|
||||
{"builtin", &builtin_impl},
|
||||
#endif
|
||||
{"noop", &noop_impl},
|
||||
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
|
@ -34,8 +31,8 @@ static const struct named_backend impls[] = {
|
|||
#error At least one backend must be enabled
|
||||
#endif
|
||||
|
||||
struct libseat *libseat_open_seat(const struct libseat_seat_listener *listener, void *data) {
|
||||
if (listener == NULL || listener->enable_seat == NULL || listener->disable_seat == NULL) {
|
||||
struct libseat *libseat_open_seat(struct libseat_seat_listener *listener, void *data) {
|
||||
if (listener == NULL) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -43,42 +40,22 @@ struct libseat *libseat_open_seat(const struct libseat_seat_listener *listener,
|
|||
log_init();
|
||||
|
||||
char *backend_type = getenv("LIBSEAT_BACKEND");
|
||||
if (backend_type != NULL) {
|
||||
const struct named_backend *iter = impls;
|
||||
while (iter->backend != NULL && strcmp(backend_type, iter->name) != 0) {
|
||||
iter++;
|
||||
}
|
||||
if (iter == NULL || iter->backend == NULL) {
|
||||
log_errorf("No backend matched name '%s'", backend_type);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
struct libseat *backend = iter->backend->open_seat(listener, data);
|
||||
if (backend == NULL) {
|
||||
log_errorf("Backend '%s' failed to open seat: %s", iter->name,
|
||||
strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
log_infof("Seat opened with backend '%s'", iter->name);
|
||||
return backend;
|
||||
}
|
||||
|
||||
struct libseat *backend = NULL;
|
||||
for (const struct named_backend *iter = impls; iter->backend != NULL; iter++) {
|
||||
if (iter->backend == &noop_impl) {
|
||||
if (backend_type != NULL && strcmp(backend_type, iter->name) != 0) {
|
||||
continue;
|
||||
}
|
||||
log_infof("Trying backend '%s'", iter->name);
|
||||
backend = iter->backend->open_seat(listener, data);
|
||||
if (backend != NULL) {
|
||||
log_infof("Seat opened with backend '%s'", iter->name);
|
||||
return backend;
|
||||
break;
|
||||
}
|
||||
log_infof("Backend '%s' failed to open seat, skipping", iter->name);
|
||||
}
|
||||
|
||||
log_error("No backend was able to open a seat");
|
||||
errno = ENOSYS;
|
||||
return NULL;
|
||||
if (backend == NULL) {
|
||||
errno = ENOSYS;
|
||||
}
|
||||
return backend;
|
||||
}
|
||||
|
||||
int libseat_disable_seat(struct libseat *seat) {
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
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,20 +13,14 @@ 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.
|
||||
|
||||
*-l <loglevel>*
|
||||
Log-level to use. Must be one of debug, info, error or silent. Defaults
|
||||
to error.
|
||||
*-s <path>*
|
||||
Where to create the seatd socket. Defaults to `/run/seatd.sock`.
|
||||
|
||||
*-v*
|
||||
Show the version number and quit.
|
||||
|
@ -39,19 +33,23 @@ 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
|
||||
|
||||
*SEATD_VTBOUND*
|
||||
If set to "0", the seat will not be bound to a VT.
|
||||
[[ *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"
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
The libseat library, *<libseat.h>*, *seatd-launch*(1)
|
||||
The libseat library, *<libseat.h>*
|
||||
|
||||
# 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.
|
||||
Maintained by Kenny Levinsen <contact@kl.wtf>. For more information about seatd
|
||||
development, see https://sr.ht/~kennylevinsen/seatd.
|
||||
|
|
196
meson.build
196
meson.build
|
@ -1,9 +1,9 @@
|
|||
project(
|
||||
'seatd',
|
||||
'c',
|
||||
version: '0.8.0',
|
||||
version: '0.2.0',
|
||||
license: 'MIT',
|
||||
meson_version: '>=0.60.0',
|
||||
meson_version: '>=0.53.0',
|
||||
default_options: [
|
||||
'c_std=c11',
|
||||
'warning_level=3',
|
||||
|
@ -11,34 +11,7 @@ project(
|
|||
],
|
||||
)
|
||||
|
||||
# Bump whenever ABI-breaking changes occur.
|
||||
libseat_soversion = 1
|
||||
|
||||
defaultpath = get_option('defaultpath')
|
||||
if defaultpath == ''
|
||||
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',
|
||||
|
@ -52,28 +25,21 @@ add_project_arguments(cc.get_supported_arguments(
|
|||
'-Wno-unknown-warning-option',
|
||||
'-Wno-unused-command-line-argument',
|
||||
'-Wvla',
|
||||
]),
|
||||
language: 'c',
|
||||
)
|
||||
|
||||
add_project_arguments(cc.get_supported_link_arguments(
|
||||
[
|
||||
'-Wl,--exclude-libs=ALL',
|
||||
]),
|
||||
'-D_XOPEN_SOURCE=700',
|
||||
'-D__BSD_VISIBLE',
|
||||
'-DSEATD_VERSION="@0@"'.format(meson.project_version()),
|
||||
],
|
||||
language: 'c',
|
||||
)
|
||||
|
||||
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')
|
||||
add_project_arguments('-D_FORTIFY_SOURCE=2', language: 'c')
|
||||
endif
|
||||
|
||||
# Hacks
|
||||
source_root = meson.current_source_dir().split('/')
|
||||
build_root = meson.global_build_root().split('/')
|
||||
build_root = meson.build_root().split('/')
|
||||
relative_dir_parts = []
|
||||
i = 0
|
||||
in_prefix = true
|
||||
|
@ -94,6 +60,11 @@ 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',
|
||||
|
@ -110,59 +81,46 @@ private_deps = []
|
|||
server_files = [
|
||||
'common/log.c',
|
||||
'common/linked_list.c',
|
||||
'common/list.c',
|
||||
'common/terminal.c',
|
||||
'common/connection.c',
|
||||
'common/evdev.c',
|
||||
'common/drm.c',
|
||||
'common/wscons.c',
|
||||
'seatd/poller.c',
|
||||
'seatd/seat.c',
|
||||
'seatd/client.c',
|
||||
'seatd/server.c',
|
||||
]
|
||||
|
||||
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
|
||||
if get_option('seatd').enabled()
|
||||
private_files += 'libseat/backend/seatd.c'
|
||||
add_project_arguments('-DSEATD_ENABLED=1', language: 'c')
|
||||
endif
|
||||
|
||||
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('lib@0@'.format(get_option('libseat-logind')))
|
||||
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()
|
||||
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
|
||||
add_project_arguments('-DHAVE_ELOGIND=1', language: 'c')
|
||||
logind_provider = 'elogind'
|
||||
else
|
||||
logind = dependency('libsystemd')
|
||||
add_project_arguments('-DHAVE_SYSTEMD=1', language: 'c')
|
||||
logind_provider = 'systemd'
|
||||
endif
|
||||
|
||||
private_files += [
|
||||
'libseat/backend/logind.c',
|
||||
'common/drm.c',
|
||||
]
|
||||
private_deps += logind
|
||||
endif
|
||||
|
||||
# needed for cross-compilation
|
||||
# realtime = meson.get_compiler('c').find_library('rt')
|
||||
# private_deps += realtime
|
||||
|
||||
if with_builtin
|
||||
libseat_c_args += '-DBUILTIN_ENABLED=1'
|
||||
if get_option('builtin').enabled()
|
||||
add_project_arguments('-DBUILTIN_ENABLED=1', language: 'c')
|
||||
private_files += server_files
|
||||
endif
|
||||
|
||||
|
@ -171,67 +129,40 @@ 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', 'libseat/backend/noop.c' ],
|
||||
soversion: '@0@'.format(libseat_soversion),
|
||||
[ 'libseat/libseat.c' ],
|
||||
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,
|
||||
)
|
||||
|
||||
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
|
||||
if get_option('server').enabled()
|
||||
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'],
|
||||
|
@ -241,29 +172,25 @@ if get_option('examples') == 'enabled'
|
|||
)
|
||||
endif
|
||||
|
||||
tests = {
|
||||
'linked_list': ['common/linked_list.c'],
|
||||
'connection': ['common/connection.c'],
|
||||
'poller': ['common/linked_list.c', 'seatd/poller.c'],
|
||||
}
|
||||
test(
|
||||
'linked_list',
|
||||
executable(
|
||||
'linked_list_test',
|
||||
['common/linked_list.c', 'tests/linked_list.c'],
|
||||
include_directories: [include_directories('.', 'include')],
|
||||
)
|
||||
)
|
||||
|
||||
foreach name, value : tests
|
||||
test(name, executable(
|
||||
'@0@_test'.format(name),
|
||||
['tests/@0@.c'.format(name), value],
|
||||
include_directories: [include_directories('.', 'include')]))
|
||||
endforeach
|
||||
|
||||
if with_server
|
||||
scdoc = dependency('scdoc', required: get_option('man-pages'), version: '>= 1.9.7', native: true)
|
||||
else
|
||||
scdoc = disabler()
|
||||
endif
|
||||
scdoc = dependency('scdoc', required: get_option('man-pages'), version: '>= 1.9.7')
|
||||
|
||||
if scdoc.found()
|
||||
sh = find_program('sh')
|
||||
|
||||
man_pages = ['seatd.1.scd']
|
||||
|
||||
mandir = get_option('mandir')
|
||||
|
||||
foreach src : ['seatd.1.scd', 'seatd-launch.1.scd']
|
||||
foreach src : man_pages
|
||||
topic = src.split('.')[0]
|
||||
section = src.split('.')[1]
|
||||
output = '@0@.@1@'.format(topic, section)
|
||||
|
@ -272,9 +199,9 @@ if scdoc.found()
|
|||
output,
|
||||
input: 'man/' + src,
|
||||
output: output,
|
||||
command: scdoc.get_variable(pkgconfig: 'scdoc'),
|
||||
feed: true,
|
||||
capture: true,
|
||||
command: [
|
||||
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc.get_pkgconfig_variable('scdoc'), output)
|
||||
],
|
||||
install: true,
|
||||
install_dir: '@0@/man@1@'.format(mandir, section)
|
||||
)
|
||||
|
@ -282,9 +209,8 @@ if scdoc.found()
|
|||
endif
|
||||
|
||||
summary({
|
||||
'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)
|
||||
'seatd': get_option('seatd').enabled() ? 1 : 0,
|
||||
'builtin': get_option('builtin').enabled() ? 1 : 0,
|
||||
'systemd': logind_provider == 'systemd' ? 1 : 0,
|
||||
'elogind': logind_provider == 'elogind' ? 1 : 0,
|
||||
})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
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('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('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)')
|
||||
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
#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;
|
||||
}
|
216
seatd/client.c
216
seatd/client.c
|
@ -14,10 +14,6 @@
|
|||
#include <sys/un.h>
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
#include <sys/un.h>
|
||||
#endif
|
||||
|
||||
#include "client.h"
|
||||
#include "linked_list.h"
|
||||
#include "log.h"
|
||||
|
@ -38,56 +34,18 @@ 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) {
|
||||
if (getsockopt(fd, SOL_SOCKET, LOCAL_PEERCRED, &cred, &len) == -1) {
|
||||
return -1;
|
||||
}
|
||||
#if __FreeBSD_version >= 1300030 || (__FreeBSD_version >= 1202506 && __FreeBSD_version < 1300000)
|
||||
*pid = cred.cr_pid;
|
||||
#else
|
||||
*pid = -1;
|
||||
#endif
|
||||
*uid = cred.cr_uid;
|
||||
*gid = cred.cr_ngroups > 0 ? cred.cr_groups[0] : (gid_t)-1;
|
||||
*gid = cred.cr_ngroups > 0 ? cred.cr_groups[0] : -1;
|
||||
return 0;
|
||||
#else
|
||||
#error Unsupported platform
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -108,37 +66,37 @@ struct client *client_create(struct server *server, int client_fd) {
|
|||
client->uid = uid;
|
||||
client->gid = gid;
|
||||
client->pid = pid;
|
||||
client->session = -1;
|
||||
client->server = server;
|
||||
client->connection.fd = client_fd;
|
||||
client->state = CLIENT_NEW;
|
||||
linked_list_init(&client->devices);
|
||||
linked_list_insert(&server->idle_clients, &client->link);
|
||||
return client;
|
||||
}
|
||||
|
||||
void client_kill(struct client *client) {
|
||||
assert(client);
|
||||
if (client->connection.fd != -1) {
|
||||
shutdown(client->connection.fd, SHUT_RDWR);
|
||||
};
|
||||
if (client->seat != NULL) {
|
||||
seat_remove_client(client);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
client->connection.fd = -1;
|
||||
}
|
||||
linked_list_remove(&client->link);
|
||||
if (client->seat != NULL) {
|
||||
// This should also close and remove all devices
|
||||
seat_remove_client(client);
|
||||
}
|
||||
if (client->event_source != NULL) {
|
||||
event_source_fd_destroy(client->event_source);
|
||||
client->event_source = NULL;
|
||||
}
|
||||
if (client->connection.fd != -1) {
|
||||
close(client->connection.fd);
|
||||
client->connection.fd = -1;
|
||||
}
|
||||
connection_close_fds(&client->connection);
|
||||
assert(linked_list_empty(&client->devices));
|
||||
free(client);
|
||||
|
@ -164,8 +122,8 @@ static int client_send_error(struct client *client, int error_code) {
|
|||
};
|
||||
|
||||
if (connection_put(&client->connection, &errheader, sizeof errheader) == -1 ||
|
||||
connection_put(&client->connection, &errmsg, sizeof errmsg) == -1) {
|
||||
log_errorf("Could not send error to client: %s", strerror(errno));
|
||||
connection_put(&client->connection, &errmsg, sizeof errmsg)) {
|
||||
log_error("could not send error to client");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -180,22 +138,20 @@ static char *client_get_seat_name(struct client *client) {
|
|||
static int handle_open_seat(struct client *client) {
|
||||
char *seat_name = client_get_seat_name(client);
|
||||
if (seat_name == NULL) {
|
||||
log_error("Could not get name of target seat");
|
||||
log_error("could not get name of target seat");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct seat *seat = server_get_seat(client->server, seat_name);
|
||||
if (seat == NULL) {
|
||||
log_errorf("Could not find seat named %s", seat_name);
|
||||
log_error("unable to find seat by name");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (seat_add_client(seat, client) == -1) {
|
||||
log_errorf("Could not add client to target seat: %s", strerror(errno));
|
||||
log_errorf("unable to add client to target seat: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
linked_list_remove(&client->link);
|
||||
linked_list_insert(&seat->clients, &client->link);
|
||||
|
||||
size_t seat_name_len = strlen(seat_name);
|
||||
|
||||
|
@ -210,7 +166,7 @@ static int handle_open_seat(struct client *client) {
|
|||
if (connection_put(&client->connection, &header, sizeof header) == -1 ||
|
||||
connection_put(&client->connection, &rmsg, sizeof rmsg) == -1 ||
|
||||
connection_put(&client->connection, seat_name, seat_name_len) == -1) {
|
||||
log_errorf("Could not write response: %s", strerror(errno));
|
||||
log_errorf("unable to write response: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -220,16 +176,14 @@ static int handle_open_seat(struct client *client) {
|
|||
|
||||
static int handle_close_seat(struct client *client) {
|
||||
if (client->seat == NULL) {
|
||||
log_error("Protocol error: no seat associated with client");
|
||||
log_error("protocol error: no seat associated with client");
|
||||
return -1;
|
||||
}
|
||||
|
||||
linked_list_remove(&client->link);
|
||||
if (seat_remove_client(client) == -1) {
|
||||
log_error("Could not remove client from seat");
|
||||
log_error("unable to remove client from seat");
|
||||
return -1;
|
||||
}
|
||||
linked_list_insert(&client->server->idle_clients, &client->link);
|
||||
|
||||
struct proto_header header = {
|
||||
.opcode = SERVER_SEAT_CLOSED,
|
||||
|
@ -237,7 +191,7 @@ static int handle_close_seat(struct client *client) {
|
|||
};
|
||||
|
||||
if (connection_put(&client->connection, &header, sizeof header) == -1) {
|
||||
log_errorf("Could not write response: %s", strerror(errno));
|
||||
log_errorf("unable to write response: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -246,25 +200,25 @@ static int handle_close_seat(struct client *client) {
|
|||
|
||||
static int handle_open_device(struct client *client, char *path) {
|
||||
if (client->seat == NULL) {
|
||||
log_error("Protocol error: no seat associated with client");
|
||||
log_error("protocol error: no seat associated with client");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct seat_device *device = seat_open_device(client, path);
|
||||
if (device == NULL) {
|
||||
log_errorf("Could not open device: %s", strerror(errno));
|
||||
log_errorf("could not open device: %s", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
int dupfd = dup(device->fd);
|
||||
if (dupfd == -1) {
|
||||
log_errorf("Could not dup fd: %s", strerror(errno));
|
||||
log_errorf("could not dup fd: %s", strerror(errno));
|
||||
seat_close_device(client, device);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (connection_put_fd(&client->connection, dupfd) == -1) {
|
||||
log_errorf("Could not queue fd for sending: %s", strerror(errno));
|
||||
log_errorf("unable to queue fd for sending: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -277,8 +231,8 @@ static int handle_open_device(struct client *client, char *path) {
|
|||
};
|
||||
|
||||
if (connection_put(&client->connection, &header, sizeof header) == -1 ||
|
||||
connection_put(&client->connection, &msg, sizeof msg) == -1) {
|
||||
log_errorf("Could not write response: %s", strerror(errno));
|
||||
connection_put(&client->connection, &msg, sizeof msg)) {
|
||||
log_errorf("unable to write response: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -290,29 +244,33 @@ fail:
|
|||
|
||||
static int handle_close_device(struct client *client, int device_id) {
|
||||
if (client->seat == NULL) {
|
||||
log_error("Protocol error: no seat associated with client");
|
||||
log_error("protocol error: no seat associated with client");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct seat_device *device = seat_find_device(client, device_id);
|
||||
if (device == NULL) {
|
||||
log_error("No such device");
|
||||
log_error("no such device");
|
||||
errno = EBADF;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (seat_close_device(client, device) == -1) {
|
||||
log_errorf("Could not close device: %s", strerror(errno));
|
||||
log_errorf("could not close device: %s", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
struct proto_server_device_closed msg = {
|
||||
.device_id = device_id,
|
||||
};
|
||||
struct proto_header header = {
|
||||
.opcode = SERVER_DEVICE_CLOSED,
|
||||
.size = 0,
|
||||
.size = sizeof msg,
|
||||
};
|
||||
|
||||
if (connection_put(&client->connection, &header, sizeof header) == -1) {
|
||||
log_errorf("Could not write response: %s", strerror(errno));
|
||||
if (connection_put(&client->connection, &header, sizeof header) == -1 ||
|
||||
connection_put(&client->connection, &msg, sizeof msg)) {
|
||||
log_errorf("unable to write response: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -324,10 +282,10 @@ fail:
|
|||
|
||||
static int handle_switch_session(struct client *client, int session) {
|
||||
if (client->seat == NULL) {
|
||||
log_error("Protocol error: no seat associated with client");
|
||||
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;
|
||||
}
|
||||
|
@ -340,10 +298,17 @@ error:
|
|||
|
||||
static int handle_disable_seat(struct client *client) {
|
||||
if (client->seat == NULL) {
|
||||
log_error("Protocol error: no seat associated with client");
|
||||
log_error("protocol error: no seat associated with client");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct seat *seat = client->seat;
|
||||
if (seat->active_client != client) {
|
||||
log_info("client is not currently active");
|
||||
errno = EPERM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (seat_ack_disable_client(client) == -1) {
|
||||
goto error;
|
||||
}
|
||||
|
@ -354,27 +319,12 @@ 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) {
|
||||
log_error("Protocol error: invalid open_seat message");
|
||||
log_error("protocol error: invalid open_seat message");
|
||||
return -1;
|
||||
}
|
||||
res = handle_open_seat(client);
|
||||
|
@ -382,7 +332,7 @@ static int client_handle_opcode(struct client *client, uint16_t opcode, size_t s
|
|||
}
|
||||
case CLIENT_CLOSE_SEAT: {
|
||||
if (size != 0) {
|
||||
log_error("Protocol error: invalid close_seat message");
|
||||
log_error("protocol error: invalid close_seat message");
|
||||
return -1;
|
||||
}
|
||||
res = handle_close_seat(client);
|
||||
|
@ -393,11 +343,11 @@ static int client_handle_opcode(struct client *client, uint16_t opcode, size_t s
|
|||
struct proto_client_open_device msg;
|
||||
if (sizeof msg > size || connection_get(&client->connection, &msg, sizeof msg) == -1 ||
|
||||
sizeof msg + msg.path_len > size || msg.path_len > MAX_PATH_LEN) {
|
||||
log_error("Protocol error: invalid open_device message");
|
||||
log_error("protocol error: invalid open_device message");
|
||||
return -1;
|
||||
}
|
||||
if (connection_get(&client->connection, path, msg.path_len) == -1) {
|
||||
log_error("Protocol error: invalid open_device message");
|
||||
log_error("protocol error: invalid open_device message");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -407,7 +357,7 @@ static int client_handle_opcode(struct client *client, uint16_t opcode, size_t s
|
|||
case CLIENT_CLOSE_DEVICE: {
|
||||
struct proto_client_close_device msg;
|
||||
if (sizeof msg > size || connection_get(&client->connection, &msg, sizeof msg) == -1) {
|
||||
log_error("Protocol error: invalid close_device message");
|
||||
log_error("protocol error: invalid close_device message");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -417,7 +367,7 @@ static int client_handle_opcode(struct client *client, uint16_t opcode, size_t s
|
|||
case CLIENT_SWITCH_SESSION: {
|
||||
struct proto_client_switch_session msg;
|
||||
if (sizeof msg > size || connection_get(&client->connection, &msg, sizeof msg) == -1) {
|
||||
log_error("Protocol error: invalid switch_session message");
|
||||
log_error("protocol error: invalid switch_session message");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -426,22 +376,14 @@ static int client_handle_opcode(struct client *client, uint16_t opcode, size_t s
|
|||
}
|
||||
case CLIENT_DISABLE_SEAT: {
|
||||
if (size != 0) {
|
||||
log_error("Protocol error: invalid disable_seat message");
|
||||
log_error("protocol error: invalid disable_seat message");
|
||||
return -1;
|
||||
}
|
||||
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);
|
||||
log_errorf("protocol error: unknown opcode: %d", opcode);
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
|
@ -458,7 +400,7 @@ int client_send_disable_seat(struct client *client) {
|
|||
};
|
||||
if (connection_put(&client->connection, &header, sizeof header) == -1 ||
|
||||
connection_flush(&client->connection) == -1) {
|
||||
log_errorf("Could not send event: %s", strerror(errno));
|
||||
log_error("unable to send event");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -471,7 +413,7 @@ int client_send_enable_seat(struct client *client) {
|
|||
};
|
||||
if (connection_put(&client->connection, &header, sizeof header) == -1 ||
|
||||
connection_flush(&client->connection) == -1) {
|
||||
log_errorf("Could not send event: %s", strerror(errno));
|
||||
log_error("unable to send event");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -482,18 +424,18 @@ int client_handle_connection(int fd, uint32_t mask, void *data) {
|
|||
|
||||
struct client *client = data;
|
||||
if (mask & EVENT_ERROR) {
|
||||
log_error("Connection error");
|
||||
log_error("connection error");
|
||||
goto fail;
|
||||
}
|
||||
if (mask & EVENT_HANGUP) {
|
||||
log_info("Client disconnected");
|
||||
log_info("client disconnected");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (mask & EVENT_WRITABLE) {
|
||||
int len = connection_flush(&client->connection);
|
||||
if (len == -1 && errno != EAGAIN) {
|
||||
log_errorf("Could not flush client connection: %s", strerror(errno));
|
||||
log_error("could not flush client connection");
|
||||
goto fail;
|
||||
} else if (len >= 0) {
|
||||
event_source_fd_update(client->event_source, EVENT_READABLE);
|
||||
|
@ -502,18 +444,8 @@ int client_handle_connection(int fd, uint32_t mask, void *data) {
|
|||
|
||||
if (mask & EVENT_READABLE) {
|
||||
int len = connection_read(&client->connection);
|
||||
if (len == -1 && errno != EAGAIN) {
|
||||
log_errorf("Could not read client connection: %s", strerror(errno));
|
||||
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
|
||||
if (len == 0 || (len == -1 && errno != EAGAIN)) {
|
||||
log_error("could not read client connection");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -535,3 +467,15 @@ fail:
|
|||
client_destroy(client);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int client_get_session(const struct client *client) {
|
||||
if (client->seat == NULL || client->seat->active_client != client) {
|
||||
return -1;
|
||||
}
|
||||
if (client->seat->vt_bound) {
|
||||
return client->seat_vt;
|
||||
}
|
||||
// TODO: Store some session sequence
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
|
455
seatd/poller.c
455
seatd/poller.c
|
@ -1,105 +1,41 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "linked_list.h"
|
||||
#include "list.h"
|
||||
#include "poller.h"
|
||||
|
||||
struct event_source_fd {
|
||||
struct linked_list link; // poller::fds
|
||||
const struct event_source_fd_impl *impl;
|
||||
event_source_fd_func_t func;
|
||||
|
||||
int fd;
|
||||
uint32_t mask;
|
||||
void *data;
|
||||
|
||||
struct poller *poller;
|
||||
bool killed;
|
||||
ssize_t pollfd_idx;
|
||||
};
|
||||
|
||||
struct event_source_signal {
|
||||
struct linked_list link; // poller::signals
|
||||
const struct event_source_signal_impl *impl;
|
||||
event_source_signal_func_t func;
|
||||
|
||||
int signal;
|
||||
void *data;
|
||||
|
||||
struct poller *poller;
|
||||
bool raised;
|
||||
bool killed;
|
||||
};
|
||||
|
||||
/* Used for signal handling */
|
||||
struct poller *global_poller = NULL;
|
||||
|
||||
static void signal_handler(int sig) {
|
||||
if (global_poller == NULL) {
|
||||
return;
|
||||
}
|
||||
for (struct linked_list *elem = global_poller->signals.next;
|
||||
elem != &global_poller->signals; elem = elem->next) {
|
||||
struct event_source_signal *bps = (struct event_source_signal *)elem;
|
||||
if (bps->signal == sig) {
|
||||
bps->raised = true;
|
||||
}
|
||||
}
|
||||
int saved_errno = errno;
|
||||
if (write(global_poller->signal_fds[1], "\0", 1) == -1 && errno != EAGAIN) {
|
||||
// This is unfortunate.
|
||||
}
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
static int set_nonblock(int fd) {
|
||||
int flags;
|
||||
if ((flags = fcntl(fd, F_GETFL)) == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int poller_init(struct poller *poller) {
|
||||
void poller_init(struct poller *poller) {
|
||||
assert(global_poller == NULL);
|
||||
|
||||
if (pipe(poller->signal_fds) == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (set_nonblock(poller->signal_fds[0]) == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (set_nonblock(poller->signal_fds[1]) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
linked_list_init(&poller->fds);
|
||||
linked_list_init(&poller->signals);
|
||||
poller->pollfds = NULL;
|
||||
poller->pollfds_len = 0;
|
||||
poller->dirty = true;
|
||||
poller->fd_event_sources = 1;
|
||||
list_init(&poller->fds);
|
||||
list_init(&poller->new_fds);
|
||||
list_init(&poller->signals);
|
||||
list_init(&poller->new_signals);
|
||||
global_poller = poller;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int poller_finish(struct poller *poller) {
|
||||
while (!linked_list_empty(&poller->fds)) {
|
||||
struct event_source_fd *bpfd = (struct event_source_fd *)poller->fds.next;
|
||||
linked_list_remove(&bpfd->link);
|
||||
for (size_t idx = 0; idx < poller->fds.length; idx++) {
|
||||
struct event_source_fd *bpfd = poller->fds.items[idx];
|
||||
free(bpfd);
|
||||
}
|
||||
while (!linked_list_empty(&poller->signals)) {
|
||||
struct event_source_signal *bps = (struct event_source_signal *)poller->signals.next;
|
||||
list_free(&poller->fds);
|
||||
for (size_t idx = 0; idx < poller->new_fds.length; idx++) {
|
||||
struct event_source_fd *bpfd = poller->new_fds.items[idx];
|
||||
free(bpfd);
|
||||
}
|
||||
list_free(&poller->new_fds);
|
||||
for (size_t idx = 0; idx < poller->signals.length; idx++) {
|
||||
struct event_source_signal *bps = poller->signals.items[idx];
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = SIG_DFL;
|
||||
|
@ -107,96 +43,22 @@ int poller_finish(struct poller *poller) {
|
|||
sa.sa_flags = 0;
|
||||
sigaction(bps->signal, &sa, NULL);
|
||||
|
||||
linked_list_remove(&bps->link);
|
||||
free(bps);
|
||||
}
|
||||
free(poller->pollfds);
|
||||
global_poller = NULL;
|
||||
return 0;
|
||||
}
|
||||
list_free(&poller->signals);
|
||||
for (size_t idx = 0; idx < poller->new_signals.length; idx++) {
|
||||
struct event_source_signal *bps = poller->new_signals.items[idx];
|
||||
|
||||
struct event_source_fd *poller_add_fd(struct poller *poller, int fd, uint32_t mask,
|
||||
event_source_fd_func_t func, void *data) {
|
||||
struct event_source_fd *bpfd = calloc(1, sizeof(struct event_source_fd));
|
||||
if (bpfd == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
bpfd->fd = fd;
|
||||
bpfd->mask = mask;
|
||||
bpfd->data = data;
|
||||
bpfd->func = func;
|
||||
bpfd->poller = poller;
|
||||
bpfd->pollfd_idx = -1;
|
||||
poller->fd_event_sources += 1;
|
||||
poller->dirty = true;
|
||||
linked_list_insert(&poller->fds, &bpfd->link);
|
||||
return (struct event_source_fd *)bpfd;
|
||||
}
|
||||
|
||||
int event_source_fd_destroy(struct event_source_fd *event_source) {
|
||||
struct event_source_fd *bpfd = (struct event_source_fd *)event_source;
|
||||
struct poller *poller = bpfd->poller;
|
||||
poller->fd_event_sources -= 1;
|
||||
poller->dirty = true;
|
||||
bpfd->killed = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int event_source_fd_update(struct event_source_fd *event_source, uint32_t mask) {
|
||||
struct event_source_fd *bpfd = (struct event_source_fd *)event_source;
|
||||
struct poller *poller = bpfd->poller;
|
||||
event_source->mask = mask;
|
||||
poller->dirty = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct event_source_signal *poller_add_signal(struct poller *poller, int signal,
|
||||
event_source_signal_func_t func, void *data) {
|
||||
|
||||
struct event_source_signal *bps = calloc(1, sizeof(struct event_source_signal));
|
||||
if (bps == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bps->signal = signal;
|
||||
bps->data = data;
|
||||
bps->func = func;
|
||||
bps->poller = poller;
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = &signal_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sigaction(signal, &sa, NULL);
|
||||
|
||||
linked_list_insert(&poller->signals, &bps->link);
|
||||
|
||||
return (struct event_source_signal *)bps;
|
||||
}
|
||||
|
||||
int event_source_signal_destroy(struct event_source_signal *event_source) {
|
||||
struct event_source_signal *bps = (struct event_source_signal *)event_source;
|
||||
struct poller *poller = bps->poller;
|
||||
|
||||
int refcnt = 0;
|
||||
for (struct linked_list *elem = poller->signals.next; elem != &poller->signals;
|
||||
elem = elem->next) {
|
||||
struct event_source_signal *b = (struct event_source_signal *)elem;
|
||||
if (b->signal == bps->signal && !b->killed) {
|
||||
refcnt++;
|
||||
}
|
||||
}
|
||||
|
||||
if (refcnt == 0) {
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = SIG_DFL;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sigaction(bps->signal, &sa, NULL);
|
||||
}
|
||||
|
||||
poller->dirty = true;
|
||||
bps->killed = true;
|
||||
free(bps);
|
||||
}
|
||||
list_free(&poller->new_signals);
|
||||
free(poller->pollfds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -228,100 +90,227 @@ static uint32_t poll_mask_to_event_mask(int poll_mask) {
|
|||
return event_mask;
|
||||
}
|
||||
|
||||
static int regenerate(struct poller *poller) {
|
||||
if (poller->fd_event_sources > poller->pollfds_len) {
|
||||
struct pollfd *fds =
|
||||
realloc(poller->pollfds, poller->fd_event_sources * sizeof(struct pollfd));
|
||||
static int regenerate_pollfds(struct poller *poller) {
|
||||
if (poller->pollfds_len != poller->fds.length) {
|
||||
struct pollfd *fds = calloc(poller->fds.length, sizeof(struct pollfd));
|
||||
if (fds == NULL) {
|
||||
return -1;
|
||||
}
|
||||
free(poller->pollfds);
|
||||
poller->pollfds = fds;
|
||||
poller->pollfds_len = poller->fd_event_sources;
|
||||
poller->pollfds_len = poller->fds.length;
|
||||
}
|
||||
|
||||
size_t idx = 0;
|
||||
poller->pollfds[idx++] = (struct pollfd){
|
||||
.fd = poller->signal_fds[0],
|
||||
.events = POLLIN,
|
||||
};
|
||||
|
||||
for (struct linked_list *elem = poller->fds.next; elem != &poller->fds; elem = elem->next) {
|
||||
struct event_source_fd *bpfd = (struct event_source_fd *)elem;
|
||||
if (bpfd->killed) {
|
||||
elem = elem->prev;
|
||||
linked_list_remove(&bpfd->link);
|
||||
free(bpfd);
|
||||
} else {
|
||||
bpfd->pollfd_idx = idx++;
|
||||
assert(bpfd->pollfd_idx < (ssize_t)poller->pollfds_len);
|
||||
poller->pollfds[bpfd->pollfd_idx] = (struct pollfd){
|
||||
.fd = bpfd->fd,
|
||||
.events = event_mask_to_poll_mask(bpfd->mask),
|
||||
};
|
||||
}
|
||||
}
|
||||
assert(idx == poller->fd_event_sources);
|
||||
|
||||
for (struct linked_list *elem = poller->signals.next; elem != &poller->signals;
|
||||
elem = elem->next) {
|
||||
struct event_source_signal *bps = (struct event_source_signal *)elem;
|
||||
if (bps->killed) {
|
||||
elem = elem->prev;
|
||||
linked_list_remove(&bps->link);
|
||||
free(bps);
|
||||
}
|
||||
for (size_t idx = 0; idx < poller->fds.length; idx++) {
|
||||
struct event_source_fd *bpfd = poller->fds.items[idx];
|
||||
poller->pollfds[idx] = (struct pollfd){
|
||||
.fd = bpfd->fd,
|
||||
.events = event_mask_to_poll_mask(bpfd->mask),
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dispatch(struct poller *poller) {
|
||||
if ((poller->pollfds[0].revents & POLLIN) != 0) {
|
||||
char garbage[8];
|
||||
while (read(poller->signal_fds[0], &garbage, sizeof garbage) != -1) {
|
||||
}
|
||||
struct event_source_fd *poller_add_fd(struct poller *poller, int fd, uint32_t mask,
|
||||
event_source_fd_func_t func, void *data) {
|
||||
struct event_source_fd *bpfd = calloc(1, sizeof(struct event_source_fd));
|
||||
if (bpfd == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
bpfd->fd = fd;
|
||||
bpfd->mask = mask;
|
||||
bpfd->data = data;
|
||||
bpfd->func = func;
|
||||
bpfd->poller = poller;
|
||||
poller->dirty = true;
|
||||
if (poller->inpoll) {
|
||||
list_add(&poller->new_fds, bpfd);
|
||||
} else {
|
||||
list_add(&poller->fds, bpfd);
|
||||
regenerate_pollfds(poller);
|
||||
}
|
||||
return (struct event_source_fd *)bpfd;
|
||||
}
|
||||
|
||||
for (struct linked_list *elem = poller->signals.next; elem != &poller->signals;
|
||||
elem = elem->next) {
|
||||
struct event_source_signal *bps = (struct event_source_signal *)elem;
|
||||
if (!bps->raised || bps->killed) {
|
||||
continue;
|
||||
}
|
||||
bps->func(bps->signal, bps->data);
|
||||
bps->raised = false;
|
||||
int event_source_fd_destroy(struct event_source_fd *event_source) {
|
||||
struct event_source_fd *bpfd = (struct event_source_fd *)event_source;
|
||||
struct poller *poller = bpfd->poller;
|
||||
int idx = list_find(&poller->fds, event_source);
|
||||
if (idx == -1) {
|
||||
return -1;
|
||||
}
|
||||
poller->dirty = true;
|
||||
if (poller->inpoll) {
|
||||
bpfd->killed = true;
|
||||
} else {
|
||||
list_del(&poller->fds, idx);
|
||||
free(bpfd);
|
||||
regenerate_pollfds(poller);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int event_source_fd_update(struct event_source_fd *event_source, uint32_t mask) {
|
||||
struct event_source_fd *bpfd = (struct event_source_fd *)event_source;
|
||||
struct poller *poller = bpfd->poller;
|
||||
event_source->mask = mask;
|
||||
|
||||
poller->dirty = true;
|
||||
if (!poller->inpoll) {
|
||||
regenerate_pollfds(poller);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void signal_handler(int sig) {
|
||||
if (global_poller == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t idx = 0; idx < global_poller->signals.length; idx++) {
|
||||
struct event_source_signal *bps = global_poller->signals.items[idx];
|
||||
if (bps->signal == sig) {
|
||||
bps->raised = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct event_source_signal *poller_add_signal(struct poller *poller, int signal,
|
||||
event_source_signal_func_t func, void *data) {
|
||||
|
||||
struct event_source_signal *bps = calloc(1, sizeof(struct event_source_signal));
|
||||
if (bps == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int refcnt = 0;
|
||||
for (size_t idx = 0; idx < poller->signals.length; idx++) {
|
||||
struct event_source_signal *bps = poller->signals.items[idx];
|
||||
if (bps->signal == signal) {
|
||||
refcnt++;
|
||||
}
|
||||
}
|
||||
|
||||
for (struct linked_list *elem = poller->fds.next; elem != &poller->fds; elem = elem->next) {
|
||||
struct event_source_fd *bpfd = (struct event_source_fd *)elem;
|
||||
if (bpfd->pollfd_idx == -1 || bpfd->killed) {
|
||||
continue;
|
||||
}
|
||||
assert(bpfd->pollfd_idx < (ssize_t)poller->pollfds_len);
|
||||
short revents = poller->pollfds[bpfd->pollfd_idx].revents;
|
||||
if (revents == 0) {
|
||||
continue;
|
||||
}
|
||||
bpfd->func(poller->pollfds[bpfd->pollfd_idx].fd, poll_mask_to_event_mask(revents),
|
||||
bpfd->data);
|
||||
bps->signal = signal;
|
||||
bps->data = data;
|
||||
bps->func = func;
|
||||
bps->poller = poller;
|
||||
|
||||
if (refcnt == 0) {
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = &signal_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sigaction(signal, &sa, NULL);
|
||||
}
|
||||
|
||||
if (poller->inpoll) {
|
||||
list_add(&poller->new_signals, bps);
|
||||
} else {
|
||||
list_add(&poller->signals, bps);
|
||||
}
|
||||
|
||||
return (struct event_source_signal *)bps;
|
||||
}
|
||||
|
||||
int event_source_signal_destroy(struct event_source_signal *event_source) {
|
||||
struct event_source_signal *bps = (struct event_source_signal *)event_source;
|
||||
struct poller *poller = bps->poller;
|
||||
|
||||
int idx = list_find(&poller->signals, event_source);
|
||||
if (idx == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int refcnt = 0;
|
||||
for (size_t idx = 0; idx < poller->signals.length; idx++) {
|
||||
struct event_source_signal *b = poller->signals.items[idx];
|
||||
if (b->signal == bps->signal) {
|
||||
refcnt++;
|
||||
}
|
||||
}
|
||||
|
||||
if (refcnt == 0) {
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = SIG_DFL;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sigaction(bps->signal, &sa, NULL);
|
||||
}
|
||||
|
||||
if (poller->inpoll) {
|
||||
bps->killed = true;
|
||||
} else {
|
||||
list_del(&poller->signals, idx);
|
||||
free(bps);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int poller_poll(struct poller *poller) {
|
||||
if (poller->dirty) {
|
||||
if (regenerate(poller) == -1) {
|
||||
return -1;
|
||||
if (poll(poller->pollfds, poller->fds.length, -1) == -1 && errno != EINTR) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
poller->inpoll = true;
|
||||
|
||||
for (size_t idx = 0; idx < poller->fds.length; idx++) {
|
||||
short revents = poller->pollfds[idx].revents;
|
||||
if (revents == 0) {
|
||||
continue;
|
||||
}
|
||||
struct event_source_fd *bpfd = poller->fds.items[idx];
|
||||
bpfd->func(poller->pollfds[idx].fd, poll_mask_to_event_mask(revents), bpfd->data);
|
||||
}
|
||||
|
||||
for (size_t idx = 0; idx < poller->signals.length; idx++) {
|
||||
struct event_source_signal *bps = poller->signals.items[idx];
|
||||
if (!bps->raised) {
|
||||
continue;
|
||||
}
|
||||
bps->func(bps->signal, bps->data);
|
||||
bps->raised = false;
|
||||
}
|
||||
|
||||
poller->inpoll = false;
|
||||
|
||||
for (size_t idx = 0; idx < poller->fds.length; idx++) {
|
||||
struct event_source_fd *bpfd = poller->fds.items[idx];
|
||||
if (!bpfd->killed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list_del(&poller->fds, idx);
|
||||
free(bpfd);
|
||||
idx--;
|
||||
}
|
||||
|
||||
for (size_t idx = 0; idx < poller->signals.length; idx++) {
|
||||
struct event_source_signal *bps = poller->signals.items[idx];
|
||||
if (!bps->killed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list_del(&poller->signals, idx);
|
||||
free(bps);
|
||||
idx--;
|
||||
}
|
||||
|
||||
if (poller->new_fds.length > 0) {
|
||||
list_concat(&poller->fds, &poller->new_fds);
|
||||
list_truncate(&poller->new_fds);
|
||||
}
|
||||
|
||||
if (poller->new_signals.length > 0) {
|
||||
list_concat(&poller->signals, &poller->new_signals);
|
||||
list_truncate(&poller->new_signals);
|
||||
}
|
||||
|
||||
if (poller->dirty) {
|
||||
regenerate_pollfds(poller);
|
||||
poller->dirty = false;
|
||||
}
|
||||
|
||||
while (poll(poller->pollfds, poller->fd_event_sources, -1) == -1) {
|
||||
if (errno != EINTR) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(poller);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
631
seatd/seat.c
631
seatd/seat.c
|
@ -17,10 +17,6 @@
|
|||
#include "protocol.h"
|
||||
#include "seat.h"
|
||||
#include "terminal.h"
|
||||
#include "wscons.h"
|
||||
|
||||
static int seat_close_client(struct client *client);
|
||||
static int vt_close(int vt);
|
||||
|
||||
struct seat *seat_create(const char *seat_name, bool vt_bound) {
|
||||
struct seat *seat = calloc(1, sizeof(struct seat));
|
||||
|
@ -29,17 +25,13 @@ struct seat *seat_create(const char *seat_name, bool vt_bound) {
|
|||
}
|
||||
linked_list_init(&seat->clients);
|
||||
seat->vt_bound = vt_bound;
|
||||
seat->curttyfd = -1;
|
||||
seat->seat_name = strdup(seat_name);
|
||||
seat->cur_vt = 0;
|
||||
if (seat->seat_name == NULL) {
|
||||
free(seat);
|
||||
return NULL;
|
||||
}
|
||||
if (vt_bound) {
|
||||
log_infof("Created VT-bound seat %s", seat_name);
|
||||
} else {
|
||||
log_infof("Created seat %s", seat_name);
|
||||
}
|
||||
log_debugf("created seat '%s' (vt_bound: %d)", seat_name, vt_bound);
|
||||
return seat;
|
||||
}
|
||||
|
||||
|
@ -47,129 +39,34 @@ void seat_destroy(struct seat *seat) {
|
|||
assert(seat);
|
||||
while (!linked_list_empty(&seat->clients)) {
|
||||
struct client *client = (struct client *)seat->clients.next;
|
||||
// This will cause the client to remove itself from the seat
|
||||
assert(client->seat == seat);
|
||||
client_destroy(client);
|
||||
client_kill(client);
|
||||
}
|
||||
linked_list_remove(&seat->link);
|
||||
assert(seat->curttyfd == -1);
|
||||
|
||||
free(seat->seat_name);
|
||||
free(seat);
|
||||
}
|
||||
|
||||
static void seat_update_vt(struct seat *seat) {
|
||||
int tty0fd = terminal_open(0);
|
||||
if (tty0fd == -1) {
|
||||
log_errorf("Could not open tty0 to update VT: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
seat->cur_vt = terminal_current_vt(tty0fd);
|
||||
close(tty0fd);
|
||||
}
|
||||
|
||||
static int vt_open(int vt) {
|
||||
assert(vt != -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(ttyfd, true);
|
||||
terminal_set_keyboard(ttyfd, false);
|
||||
terminal_set_graphics(ttyfd, true);
|
||||
close(ttyfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
terminal_set_process_switching(ttyfd, true);
|
||||
terminal_set_graphics(ttyfd, false);
|
||||
terminal_set_keyboard(ttyfd, true);
|
||||
close(ttyfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vt_switch(struct seat *seat, int vt) {
|
||||
int ttyfd = terminal_open(seat->cur_vt);
|
||||
if (ttyfd == -1) {
|
||||
log_errorf("Could not open terminal to switch to VT %d: %s", vt, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
terminal_set_process_switching(ttyfd, true);
|
||||
terminal_switch_vt(ttyfd, vt);
|
||||
close(ttyfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (release) {
|
||||
terminal_ack_release(tty0fd);
|
||||
} else {
|
||||
terminal_ack_acquire(tty0fd);
|
||||
}
|
||||
close(tty0fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int seat_add_client(struct seat *seat, struct client *client) {
|
||||
assert(seat);
|
||||
assert(client);
|
||||
|
||||
if (client->seat != NULL) {
|
||||
log_error("Could not add client: client is already a member of a seat");
|
||||
errno = EBUSY;
|
||||
log_error("cannot add client: client is already a member of a seat");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
if (seat->vt_bound && seat->active_client != NULL) {
|
||||
log_error("cannot add client: seat is vt_bound and an active client already exists");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (client->session != -1) {
|
||||
log_error("Could not add client: client cannot be reused");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (seat->vt_bound) {
|
||||
seat_update_vt(seat);
|
||||
if (seat->cur_vt == -1) {
|
||||
log_error("Could not determine VT for 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++;
|
||||
}
|
||||
|
||||
client->seat = seat;
|
||||
log_infof("Added client %d to %s", client->session, seat->seat_name);
|
||||
|
||||
linked_list_insert(&seat->clients, &client->link);
|
||||
log_debug("added client");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -178,6 +75,10 @@ int seat_remove_client(struct client *client) {
|
|||
assert(client->seat);
|
||||
|
||||
struct seat *seat = client->seat;
|
||||
|
||||
// We must first remove the client to avoid reactivation
|
||||
linked_list_remove(&client->link);
|
||||
|
||||
if (seat->next_client == client) {
|
||||
seat->next_client = NULL;
|
||||
}
|
||||
|
@ -187,10 +88,12 @@ int seat_remove_client(struct client *client) {
|
|||
seat_close_device(client, device);
|
||||
}
|
||||
|
||||
seat_close_client(client);
|
||||
if (seat->active_client == client) {
|
||||
seat_close_client(client);
|
||||
}
|
||||
|
||||
client->seat = NULL;
|
||||
log_infof("Removed client %d from %s", client->session, seat->seat_name);
|
||||
log_debug("removed client");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -217,18 +120,19 @@ struct seat_device *seat_open_device(struct client *client, const char *path) {
|
|||
assert(strlen(path) > 0);
|
||||
struct seat *seat = client->seat;
|
||||
|
||||
log_debugf("Opening device %s for client %d on %s", path, client->session, seat->seat_name);
|
||||
|
||||
if (client->state != CLIENT_ACTIVE) {
|
||||
log_error("Could open device: client is not active");
|
||||
if (client != seat->active_client) {
|
||||
errno = EPERM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (client->pending_disable) {
|
||||
errno = EPERM;
|
||||
return NULL;
|
||||
}
|
||||
assert(seat->active_client == client);
|
||||
|
||||
char sanitized_path[PATH_MAX];
|
||||
if (realpath(path, sanitized_path) == NULL) {
|
||||
log_errorf("Could not canonicalize path %s: %s", path, strerror(errno));
|
||||
log_errorf("invalid path '%s': %s", path, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -237,65 +141,61 @@ 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);
|
||||
log_errorf("invalid path '%s'", sanitized_path);
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int device_id = 1;
|
||||
size_t device_count = 0;
|
||||
struct seat_device *device = NULL;
|
||||
for (struct linked_list *elem = client->devices.next; elem != &client->devices;
|
||||
elem = elem->next) {
|
||||
struct seat_device *old_device = (struct seat_device *)elem;
|
||||
struct seat_device *device = (struct seat_device *)elem;
|
||||
|
||||
if (strcmp(old_device->path, sanitized_path) == 0) {
|
||||
old_device->ref_cnt++;
|
||||
device = old_device;
|
||||
goto done;
|
||||
// If the device already exists, increase the ref count and
|
||||
// return it.
|
||||
if (strcmp(device->path, path) == 0) {
|
||||
device->ref_cnt++;
|
||||
return device;
|
||||
}
|
||||
|
||||
if (old_device->device_id >= device_id) {
|
||||
device_id = old_device->device_id + 1;
|
||||
// If the device has a higher id, up our device id
|
||||
if (device->device_id >= device_id) {
|
||||
device_id = device->device_id + 1;
|
||||
}
|
||||
device_count++;
|
||||
}
|
||||
|
||||
if (device_count >= MAX_SEAT_DEVICES) {
|
||||
log_error("Client exceeded max seat devices");
|
||||
log_error("max seat devices exceeded");
|
||||
errno = EMFILE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fd = open(sanitized_path, O_RDWR | O_NOCTTY | O_NOFOLLOW | O_CLOEXEC | O_NONBLOCK);
|
||||
if (fd == -1) {
|
||||
log_errorf("Could not open file: %s", strerror(errno));
|
||||
log_errorf("could not open file: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SEAT_DEVICE_TYPE_DRM:
|
||||
if (drm_set_master(fd) == -1) {
|
||||
log_errorf("Could not make device fd drm master: %s", strerror(errno));
|
||||
log_debugf("drm_set_master failed: %s", strerror(errno));
|
||||
}
|
||||
break;
|
||||
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");
|
||||
log_error("invalid seat device type");
|
||||
abort();
|
||||
}
|
||||
|
||||
device = calloc(1, sizeof(struct seat_device));
|
||||
struct seat_device *device = calloc(1, sizeof(struct seat_device));
|
||||
if (device == NULL) {
|
||||
log_errorf("Allocation failed: %s", strerror(errno));
|
||||
log_errorf("could not alloc device for '%s': %s", sanitized_path, strerror(errno));
|
||||
close(fd);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
|
@ -303,25 +203,67 @@ struct seat_device *seat_open_device(struct client *client, const char *path) {
|
|||
|
||||
device->path = strdup(sanitized_path);
|
||||
if (device->path == NULL) {
|
||||
log_errorf("Allocation failed: %s", strerror(errno));
|
||||
log_errorf("could not dup path for '%s': %s", sanitized_path, strerror(errno));
|
||||
close(fd);
|
||||
free(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
log_debugf("seat: %p, client: %p, path: '%s', device_id: %d", (void *)seat, (void *)client,
|
||||
path, device_id);
|
||||
|
||||
device->ref_cnt = 1;
|
||||
device->type = type;
|
||||
device->fd = fd;
|
||||
device->device_id = device_id;
|
||||
device->active = true;
|
||||
linked_list_insert(&client->devices, &device->link);
|
||||
|
||||
done:
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
static int seat_deactivate_device(struct seat_device *seat_device) {
|
||||
int seat_close_device(struct client *client, struct seat_device *seat_device) {
|
||||
assert(client);
|
||||
assert(client->seat);
|
||||
assert(seat_device && seat_device->fd != -1);
|
||||
|
||||
log_debugf("seat: %p, client: %p, path: '%s', device_id: %d", (void *)client->seat,
|
||||
(void *)client, seat_device->path, seat_device->device_id);
|
||||
|
||||
seat_device->ref_cnt--;
|
||||
if (seat_device->ref_cnt > 0) {
|
||||
// We still have more references to this device, so leave it be.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The ref count hit zero, so destroy the device
|
||||
linked_list_remove(&seat_device->link);
|
||||
if (seat_device->active && seat_device->fd != -1) {
|
||||
switch (seat_device->type) {
|
||||
case SEAT_DEVICE_TYPE_DRM:
|
||||
if (drm_drop_master(seat_device->fd) == -1) {
|
||||
log_debugf("drm_drop_master failed: %s", strerror(errno));
|
||||
}
|
||||
break;
|
||||
case SEAT_DEVICE_TYPE_EVDEV:
|
||||
if (evdev_revoke(seat_device->fd) == -1) {
|
||||
log_debugf("evdev_revoke failed: %s", strerror(errno));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_error("invalid seat device type");
|
||||
abort();
|
||||
}
|
||||
close(seat_device->fd);
|
||||
seat_device->fd = -1;
|
||||
}
|
||||
free(seat_device->path);
|
||||
free(seat_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seat_deactivate_device(struct client *client, struct seat_device *seat_device) {
|
||||
assert(client);
|
||||
assert(client->seat);
|
||||
assert(seat_device && seat_device->fd > 0);
|
||||
|
||||
if (!seat_device->active) {
|
||||
|
@ -330,50 +272,24 @@ static int seat_deactivate_device(struct seat_device *seat_device) {
|
|||
switch (seat_device->type) {
|
||||
case SEAT_DEVICE_TYPE_DRM:
|
||||
if (drm_drop_master(seat_device->fd) == -1) {
|
||||
log_errorf("Could not revoke drm master on device fd: %s", strerror(errno));
|
||||
log_debugf("drm_drop_master failed: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case SEAT_DEVICE_TYPE_EVDEV:
|
||||
if (evdev_revoke(seat_device->fd) == -1) {
|
||||
log_errorf("Could not revoke evdev on device fd: %s", strerror(errno));
|
||||
log_debugf("evdev_revoke failed: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case SEAT_DEVICE_TYPE_WSCONS:
|
||||
// Nothing to do here
|
||||
break;
|
||||
default:
|
||||
log_error("Invalid seat device type");
|
||||
log_error("invalid seat device type");
|
||||
abort();
|
||||
}
|
||||
seat_device->active = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int seat_close_device(struct client *client, struct seat_device *seat_device) {
|
||||
assert(client);
|
||||
assert(client->seat);
|
||||
assert(seat_device && seat_device->fd != -1);
|
||||
|
||||
log_debugf("Closing device %s for client %d on %s", seat_device->path, client->session,
|
||||
client->seat->seat_name);
|
||||
|
||||
seat_device->ref_cnt--;
|
||||
if (seat_device->ref_cnt > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
linked_list_remove(&seat_device->link);
|
||||
if (seat_device->fd != -1) {
|
||||
seat_deactivate_device(seat_device);
|
||||
close(seat_device->fd);
|
||||
}
|
||||
free(seat_device->path);
|
||||
free(seat_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seat_activate_device(struct client *client, struct seat_device *seat_device) {
|
||||
assert(client);
|
||||
assert(client->seat);
|
||||
|
@ -385,145 +301,102 @@ static int seat_activate_device(struct client *client, struct seat_device *seat_
|
|||
switch (seat_device->type) {
|
||||
case SEAT_DEVICE_TYPE_DRM:
|
||||
if (drm_set_master(seat_device->fd) == -1) {
|
||||
log_errorf("Could not make device fd drm master: %s", strerror(errno));
|
||||
log_debugf("drmset_master failed: %s", strerror(errno));
|
||||
}
|
||||
seat_device->active = true;
|
||||
break;
|
||||
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");
|
||||
log_error("invalid seat device type");
|
||||
abort();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seat_activate(struct seat *seat) {
|
||||
assert(seat);
|
||||
|
||||
if (seat->active_client != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct client *next_client = NULL;
|
||||
if (seat->next_client != NULL) {
|
||||
log_debugf("Activating next queued client on %s", seat->seat_name);
|
||||
next_client = seat->next_client;
|
||||
seat->next_client = NULL;
|
||||
} else if (linked_list_empty(&seat->clients)) {
|
||||
log_infof("No clients on %s to activate", seat->seat_name);
|
||||
return -1;
|
||||
} else if (seat->vt_bound && seat->cur_vt == -1) {
|
||||
return -1;
|
||||
} else if (seat->vt_bound) {
|
||||
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_debugf("Activating client belonging to VT %d", seat->cur_vt);
|
||||
next_client = client;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
log_infof("No clients belonging to VT %d to activate", seat->cur_vt);
|
||||
return -1;
|
||||
} else {
|
||||
log_debugf("Activating first client on %s", seat->seat_name);
|
||||
next_client = (struct client *)seat->clients.next;
|
||||
}
|
||||
|
||||
done:
|
||||
return seat_open_client(seat, next_client);
|
||||
}
|
||||
|
||||
int seat_open_client(struct seat *seat, struct client *client) {
|
||||
assert(seat);
|
||||
assert(client);
|
||||
|
||||
if (client->state != CLIENT_NEW && client->state != CLIENT_DISABLED) {
|
||||
log_error("Could not enable client: client is not new or disabled");
|
||||
errno = EALREADY;
|
||||
return -1;
|
||||
if (seat->vt_bound && client->seat_vt == 0) {
|
||||
int tty0fd = terminal_open(0);
|
||||
if (tty0fd == -1) {
|
||||
log_errorf("unable to open tty0: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
client->seat_vt = terminal_current_vt(tty0fd);
|
||||
close(tty0fd);
|
||||
if (client->seat_vt == -1) {
|
||||
log_errorf("unable to get current VT for client: %s", strerror(errno));
|
||||
client->seat_vt = 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (seat->active_client != NULL) {
|
||||
log_error("Could not enable client: seat already has an active client");
|
||||
log_error("client already active");
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (seat->vt_bound && vt_open(client->session) == -1) {
|
||||
log_error("Could not open VT for client");
|
||||
goto error;
|
||||
assert(seat->curttyfd == -1);
|
||||
|
||||
if (seat->vt_bound) {
|
||||
int ttyfd = terminal_open(client->seat_vt);
|
||||
if (ttyfd == -1) {
|
||||
log_errorf("unable to open tty for vt %d: %s", client->seat_vt,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
terminal_set_process_switching(ttyfd, true);
|
||||
terminal_set_keyboard(ttyfd, false);
|
||||
terminal_set_graphics(ttyfd, true);
|
||||
seat->curttyfd = ttyfd;
|
||||
}
|
||||
|
||||
for (struct linked_list *elem = client->devices.next; elem != &client->devices;
|
||||
elem = elem->next) {
|
||||
struct seat_device *device = (struct seat_device *)elem;
|
||||
if (seat_activate_device(client, device) == -1) {
|
||||
log_errorf("Could not activate %s: %s", device->path, strerror(errno));
|
||||
log_errorf("unable to activate '%s': %s", device->path, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
client->state = CLIENT_ACTIVE;
|
||||
seat->active_client = client;
|
||||
if (client_send_enable_seat(client) == -1) {
|
||||
log_error("Could not send enable signal to client");
|
||||
goto error;
|
||||
log_error("could not send enable signal");
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_infof("Opened client %d on %s", client->session, seat->seat_name);
|
||||
log_info("client successfully enabled");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (seat->vt_bound) {
|
||||
vt_close(seat->cur_vt);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int seat_close_client(struct client *client) {
|
||||
int seat_close_client(struct client *client) {
|
||||
assert(client);
|
||||
assert(client->seat);
|
||||
|
||||
struct seat *seat = client->seat;
|
||||
|
||||
if (seat->active_client != client) {
|
||||
log_error("client not active");
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!linked_list_empty(&client->devices)) {
|
||||
struct seat_device *device = (struct seat_device *)client->devices.next;
|
||||
if (seat_close_device(client, device) == -1) {
|
||||
log_errorf("Could not close %s: %s", device->path, strerror(errno));
|
||||
log_errorf("unable to close '%s': %s", device->path, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
bool was_current = seat->active_client == client;
|
||||
if (was_current) {
|
||||
seat->active_client = NULL;
|
||||
seat_activate(seat);
|
||||
}
|
||||
|
||||
if (seat->vt_bound) {
|
||||
if (was_current && seat->active_client == NULL) {
|
||||
// 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->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(client->session);
|
||||
}
|
||||
}
|
||||
|
||||
client->state = CLIENT_CLOSED;
|
||||
log_infof("Closed client %d on %s", client->session, seat->seat_name);
|
||||
|
||||
client->pending_disable = false;
|
||||
seat->active_client = NULL;
|
||||
seat_activate(seat);
|
||||
log_debug("closed client");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -533,12 +406,11 @@ static int seat_disable_client(struct client *client) {
|
|||
|
||||
struct seat *seat = client->seat;
|
||||
|
||||
if (client->state != CLIENT_ACTIVE) {
|
||||
log_error("Could not disable client: client is not active");
|
||||
if (seat->active_client != client) {
|
||||
log_error("client not active");
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
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
|
||||
|
@ -547,18 +419,18 @@ static int seat_disable_client(struct client *client) {
|
|||
for (struct linked_list *elem = client->devices.next; elem != &client->devices;
|
||||
elem = elem->next) {
|
||||
struct seat_device *device = (struct seat_device *)elem;
|
||||
if (seat_deactivate_device(device) == -1) {
|
||||
log_errorf("Could not deactivate %s: %s", device->path, strerror(errno));
|
||||
if (seat_deactivate_device(client, device) == -1) {
|
||||
log_errorf("unable to deactivate '%s': %s", device->path, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
client->state = CLIENT_PENDING_DISABLE;
|
||||
client->pending_disable = true;
|
||||
if (client_send_disable_seat(seat->active_client) == -1) {
|
||||
log_error("Could not send disable event");
|
||||
log_error("could not send disable event");
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_infof("Disabling client %d on %s", client->session, seat->seat_name);
|
||||
log_debug("disabling client");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -567,25 +439,17 @@ int seat_ack_disable_client(struct client *client) {
|
|||
assert(client->seat);
|
||||
|
||||
struct seat *seat = client->seat;
|
||||
if (client->state != CLIENT_PENDING_DISABLE) {
|
||||
log_error("Could not ack disable: client is not pending disable");
|
||||
|
||||
if (seat->active_client != client || !client->pending_disable) {
|
||||
log_error("client not active or not pending disable");
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
|
||||
client->state = CLIENT_DISABLED;
|
||||
log_infof("Disabled client %d on %s", client->session, seat->seat_name);
|
||||
|
||||
if (seat->active_client != client) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
client->pending_disable = false;
|
||||
seat->active_client = NULL;
|
||||
seat_activate(seat);
|
||||
|
||||
// If we're VT-bound, we've either de-activated a client on a foreign
|
||||
// VT, in which case we need to do nothing, or disabled the current VT,
|
||||
// in which case seat_activate would just immediately re-enable it.
|
||||
log_debug("disabled client");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -595,89 +459,186 @@ int seat_set_next_session(struct client *client, int session) {
|
|||
|
||||
struct seat *seat = client->seat;
|
||||
|
||||
if (client->state != CLIENT_ACTIVE) {
|
||||
log_error("Could not set next session: client is not active");
|
||||
if (seat->active_client != client || client->pending_disable) {
|
||||
log_error("client not active or pending disable");
|
||||
errno = EPERM;
|
||||
return -1;
|
||||
}
|
||||
assert(seat->active_client == client);
|
||||
|
||||
if (session == client_get_session(client)) {
|
||||
log_info("requested session is already active");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if the session number is valid
|
||||
if (session <= 0) {
|
||||
log_errorf("Could not set next session: invalid session value %d", session);
|
||||
log_errorf("invalid session value: %d", session);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (session == client->session) {
|
||||
log_info("Could not set next session: requested session is already active");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (seat->next_client != NULL) {
|
||||
log_info("Could not set next session: switch is already queued");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (seat->vt_bound) {
|
||||
log_infof("Switching from VT %d to VT %d", seat->cur_vt, session);
|
||||
if (vt_switch(seat, session) == -1) {
|
||||
log_error("Could not switch VT");
|
||||
return -1;
|
||||
}
|
||||
// Check if a switch is already queued
|
||||
if (seat->next_vt > 0 || seat->next_client != NULL) {
|
||||
log_info("switch is already queued");
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct client *target = NULL;
|
||||
|
||||
for (struct linked_list *elem = seat->clients.next; elem != &seat->clients;
|
||||
elem = elem->next) {
|
||||
struct client *c = (struct client *)elem;
|
||||
if (c->session == session) {
|
||||
if (client_get_session(c) == session) {
|
||||
target = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target == NULL) {
|
||||
log_error("Could not set next session: no such client");
|
||||
if (target != NULL) {
|
||||
log_info("queuing switch to different client");
|
||||
seat->next_client = target;
|
||||
seat->next_vt = 0;
|
||||
} else if (seat->vt_bound) {
|
||||
log_info("queuing switch to different VT");
|
||||
seat->next_vt = session;
|
||||
seat->next_client = NULL;
|
||||
} else {
|
||||
log_error("no valid switch available");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_infof("Queuing switch to client %d on %s", session, seat->seat_name);
|
||||
seat->next_client = target;
|
||||
seat_disable_client(seat->active_client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int seat_vt_activate(struct seat *seat) {
|
||||
int seat_activate(struct seat *seat) {
|
||||
assert(seat);
|
||||
if (!seat->vt_bound) {
|
||||
log_debug("VT activation on non VT-bound seat, ignoring");
|
||||
return -1;
|
||||
}
|
||||
seat_update_vt(seat);
|
||||
log_debug("Activating VT");
|
||||
vt_ack(seat, false);
|
||||
if (seat->active_client == NULL) {
|
||||
seat_activate(seat);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int seat_vt_release(struct seat *seat) {
|
||||
assert(seat);
|
||||
if (!seat->vt_bound) {
|
||||
log_debug("VT release request on non VT-bound seat, ignoring");
|
||||
return -1;
|
||||
}
|
||||
seat_update_vt(seat);
|
||||
|
||||
log_debug("Releasing VT");
|
||||
// We already have an active client!
|
||||
if (seat->active_client != NULL) {
|
||||
seat_disable_client(seat->active_client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
vt_ack(seat, true);
|
||||
seat->cur_vt = -1;
|
||||
int vt = -1;
|
||||
if (seat->vt_bound) {
|
||||
int ttyfd = terminal_open(0);
|
||||
if (ttyfd == -1) {
|
||||
log_errorf("unable to open tty0: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If we need to ack a switch, do that
|
||||
if (seat->vt_pending_ack) {
|
||||
log_info("acking pending VT switch");
|
||||
seat->vt_pending_ack = false;
|
||||
if (seat->curttyfd != -1) {
|
||||
terminal_set_process_switching(seat->curttyfd, true);
|
||||
terminal_set_keyboard(seat->curttyfd, true);
|
||||
terminal_set_graphics(seat->curttyfd, false);
|
||||
close(seat->curttyfd);
|
||||
seat->curttyfd = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If we're asked to do a simple VT switch, do that
|
||||
if (seat->next_vt > 0) {
|
||||
log_info("executing VT switch");
|
||||
if (seat->curttyfd != -1) {
|
||||
terminal_set_process_switching(seat->curttyfd, true);
|
||||
terminal_set_keyboard(seat->curttyfd, true);
|
||||
terminal_set_graphics(seat->curttyfd, false);
|
||||
close(seat->curttyfd);
|
||||
seat->curttyfd = -1;
|
||||
}
|
||||
terminal_switch_vt(ttyfd, seat->next_vt);
|
||||
seat->next_vt = 0;
|
||||
close(ttyfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We'll need the VT below
|
||||
vt = terminal_current_vt(ttyfd);
|
||||
if (vt == -1) {
|
||||
log_errorf("unable to get vt: %s", strerror(errno));
|
||||
close(ttyfd);
|
||||
return -1;
|
||||
}
|
||||
close(ttyfd);
|
||||
}
|
||||
|
||||
// Try to pick a client for activation
|
||||
struct client *next_client = NULL;
|
||||
if (seat->next_client != NULL) {
|
||||
// A specific client has been requested, use it
|
||||
next_client = seat->next_client;
|
||||
seat->next_client = NULL;
|
||||
} else if (!linked_list_empty(&seat->clients) && seat->vt_bound) {
|
||||
// No client is requested, try to find an applicable one
|
||||
for (struct linked_list *elem = seat->clients.next; elem != &seat->clients;
|
||||
elem = elem->next) {
|
||||
struct client *client = (struct client *)elem;
|
||||
if (client->seat_vt == vt) {
|
||||
next_client = client;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (!linked_list_empty(&seat->clients)) {
|
||||
next_client = (struct client *)seat->clients.next;
|
||||
}
|
||||
|
||||
if (next_client == NULL) {
|
||||
// No suitable client found
|
||||
log_info("no client suitable for activation");
|
||||
if (seat->vt_bound && seat->curttyfd != -1) {
|
||||
terminal_set_process_switching(seat->curttyfd, false);
|
||||
terminal_set_keyboard(seat->curttyfd, true);
|
||||
terminal_set_graphics(seat->curttyfd, false);
|
||||
close(seat->curttyfd);
|
||||
seat->curttyfd = -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_info("activating next client");
|
||||
if (seat->vt_bound && next_client->seat_vt != vt) {
|
||||
int ttyfd = terminal_open(0);
|
||||
if (ttyfd == -1) {
|
||||
log_errorf("unable to open tty0: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
terminal_switch_vt(ttyfd, next_client->seat_vt);
|
||||
close(ttyfd);
|
||||
}
|
||||
|
||||
return seat_open_client(seat, next_client);
|
||||
}
|
||||
|
||||
int seat_prepare_vt_switch(struct seat *seat) {
|
||||
assert(seat);
|
||||
|
||||
if (seat->active_client == NULL) {
|
||||
log_info("no active client, performing switch immediately");
|
||||
int tty0fd = terminal_open(0);
|
||||
if (tty0fd == -1) {
|
||||
log_errorf("unable to open tty0: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
terminal_ack_switch(tty0fd);
|
||||
close(tty0fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (seat->vt_pending_ack) {
|
||||
log_info("impatient user, killing session to force pending switch");
|
||||
seat_close_client(seat->active_client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug("delaying VT switch acknowledgement");
|
||||
|
||||
seat->vt_pending_ack = true;
|
||||
seat_disable_client(seat->active_client);
|
||||
return 0;
|
||||
}
|
||||
|
|
158
seatd/seatd.c
158
seatd/seatd.c
|
@ -17,14 +17,14 @@
|
|||
|
||||
#define LISTEN_BACKLOG 16
|
||||
|
||||
static int open_socket(const char *path, int uid, int gid) {
|
||||
static int open_socket(char *path, int uid, int gid) {
|
||||
union {
|
||||
struct sockaddr_un unix;
|
||||
struct sockaddr generic;
|
||||
} addr = {{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));
|
||||
log_errorf("could not create socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -32,61 +32,59 @@ static int open_socket(const char *path, int uid, int gid) {
|
|||
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 (bind(fd, &addr.generic, size) == -1) {
|
||||
log_errorf("Could not bind socket: %s", strerror(errno));
|
||||
goto error;
|
||||
log_errorf("could not bind socket: %s", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (listen(fd, LISTEN_BACKLOG) == -1) {
|
||||
log_errorf("Could not listen on socket: %s", strerror(errno));
|
||||
goto error;
|
||||
log_errorf("could not listen on socket: %s", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
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,
|
||||
log_errorf("could not chown socket to uid %d, gid %d: %s", uid, gid,
|
||||
strerror(errno));
|
||||
goto error;
|
||||
} else if (chmod(path, 0770) == -1) {
|
||||
log_errorf("could not chmod socket: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
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"
|
||||
" -l <loglevel> Log-level, one of debug, info, error or silent\n"
|
||||
" -s <path> Where to create the seatd socket\n"
|
||||
" -v Show the version number\n"
|
||||
"\n";
|
||||
|
||||
int c;
|
||||
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) {
|
||||
int uid = 0, gid = 0;
|
||||
char *socket_path = getenv("SEATD_SOCK");
|
||||
while ((c = getopt(argc, argv, "vhs:g:u:")) != -1) {
|
||||
switch (c) {
|
||||
case 'n':
|
||||
readiness = atoi(optarg);
|
||||
if (readiness < 0) {
|
||||
fprintf(stderr, "Invalid readiness fd: %s\n", optarg);
|
||||
return 1;
|
||||
}
|
||||
case 's':
|
||||
socket_path = optarg;
|
||||
break;
|
||||
case 'u': {
|
||||
if (!chown_socket) {
|
||||
fprintf(stderr, "-u/-g and -z are mutually exclusive\n");
|
||||
return 1;
|
||||
}
|
||||
struct passwd *pw = getpwnam(optarg);
|
||||
if (pw == NULL) {
|
||||
fprintf(stderr, "Could not find user by name '%s'.\n", optarg);
|
||||
|
@ -97,10 +95,6 @@ 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);
|
||||
|
@ -110,31 +104,6 @@ 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;
|
||||
|
@ -149,72 +118,45 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
log_init();
|
||||
libseat_set_log_level(level);
|
||||
|
||||
struct stat st;
|
||||
if (lstat(SEATD_DEFAULTPATH, &st) == 0) {
|
||||
if (!S_ISSOCK(st.st_mode)) {
|
||||
log_errorf("Non-socket file found at socket path %s, refusing to start",
|
||||
SEATD_DEFAULTPATH);
|
||||
return 1;
|
||||
} else if (!unlink_existing_socket) {
|
||||
log_errorf("Socket file found at socket path %s, refusing to start",
|
||||
SEATD_DEFAULTPATH);
|
||||
return 1;
|
||||
} else {
|
||||
// We only do this if the socket path is not user specified
|
||||
log_infof("Removing leftover socket at %s", SEATD_DEFAULTPATH);
|
||||
if (unlink(SEATD_DEFAULTPATH) == -1) {
|
||||
log_errorf("Could not remove leftover socket: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (socket_path == NULL) {
|
||||
socket_path = "/run/seatd.sock";
|
||||
struct stat st;
|
||||
if (stat(socket_path, &st) == 0) {
|
||||
log_info("removing leftover seatd socket");
|
||||
unlink(socket_path);
|
||||
}
|
||||
}
|
||||
|
||||
struct server server = {0};
|
||||
if (server_init(&server) == -1) {
|
||||
log_errorf("server_init failed: %s", strerror(errno));
|
||||
log_errorf("server_create failed: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ret = 1;
|
||||
int socket_fd = open_socket(SEATD_DEFAULTPATH, uid, gid);
|
||||
int socket_fd = open_socket(socket_path, uid, gid);
|
||||
if (socket_fd == -1) {
|
||||
log_error("Could not create server socket");
|
||||
goto error_server;
|
||||
log_errorf("could not create server socket: %s", strerror(errno));
|
||||
server_finish(&server);
|
||||
return 1;
|
||||
}
|
||||
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));
|
||||
log_errorf("could not add socket to poller: %s", strerror(errno));
|
||||
close(socket_fd);
|
||||
goto error_socket;
|
||||
server_finish(&server);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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));
|
||||
goto error_socket;
|
||||
log_errorf("poller failed: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
unlink(socket_path);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "client.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
#include "poller.h"
|
||||
#include "seat.h"
|
||||
|
@ -23,13 +24,9 @@ static int server_handle_vt_rel(int signal, void *data);
|
|||
static int server_handle_kill(int signal, void *data);
|
||||
|
||||
int server_init(struct server *server) {
|
||||
if (poller_init(&server->poller) == -1) {
|
||||
log_errorf("could not initialize poller: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
poller_init(&server->poller);
|
||||
|
||||
linked_list_init(&server->seats);
|
||||
linked_list_init(&server->idle_clients);
|
||||
list_init(&server->seats);
|
||||
|
||||
if (poller_add_signal(&server->poller, SIGUSR1, server_handle_vt_rel, server) == NULL ||
|
||||
poller_add_signal(&server->poller, SIGUSR2, server_handle_vt_acq, server) == NULL ||
|
||||
|
@ -48,28 +45,24 @@ int server_init(struct server *server) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
linked_list_insert(&server->seats, &seat->link);
|
||||
list_add(&server->seats, seat);
|
||||
server->running = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void server_finish(struct server *server) {
|
||||
assert(server);
|
||||
while (!linked_list_empty(&server->idle_clients)) {
|
||||
struct client *client = (struct client *)server->idle_clients.next;
|
||||
client_destroy(client);
|
||||
}
|
||||
while (!linked_list_empty(&server->seats)) {
|
||||
struct seat *seat = (struct seat *)server->seats.next;
|
||||
for (size_t idx = 0; idx < server->seats.length; idx++) {
|
||||
struct seat *seat = server->seats.items[idx];
|
||||
seat_destroy(seat);
|
||||
}
|
||||
list_free(&server->seats);
|
||||
poller_finish(&server->poller);
|
||||
}
|
||||
|
||||
struct seat *server_get_seat(struct server *server, const char *seat_name) {
|
||||
for (struct linked_list *elem = server->seats.next; elem != &server->seats;
|
||||
elem = elem->next) {
|
||||
struct seat *seat = (struct seat *)elem;
|
||||
for (size_t idx = 0; idx < server->seats.length; idx++) {
|
||||
struct seat *seat = server->seats.items[idx];
|
||||
if (strcmp(seat->seat_name, seat_name) == 0) {
|
||||
return seat;
|
||||
}
|
||||
|
@ -85,7 +78,7 @@ static int server_handle_vt_acq(int signal, void *data) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
seat_vt_activate(seat);
|
||||
seat_activate(seat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -97,7 +90,7 @@ static int server_handle_vt_rel(int signal, void *data) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
seat_vt_release(seat);
|
||||
seat_prepare_vt_switch(seat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -111,11 +104,11 @@ static int server_handle_kill(int signal, void *data) {
|
|||
static int set_nonblock(int fd) {
|
||||
int flags;
|
||||
if ((flags = fcntl(fd, F_GETFD)) == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
|
||||
log_errorf("Could not set FD_CLOEXEC on socket: %s", strerror(errno));
|
||||
log_errorf("could not set FD_CLOEXEC on socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if ((flags = fcntl(fd, F_GETFL)) == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||
log_errorf("Could not set O_NONBLOCK on socket: %s", strerror(errno));
|
||||
log_errorf("could not set O_NONBLOCK on socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -123,26 +116,20 @@ static int set_nonblock(int fd) {
|
|||
|
||||
int server_add_client(struct server *server, int fd) {
|
||||
if (set_nonblock(fd) != 0) {
|
||||
log_errorf("Could not prepare new client socket: %s", strerror(errno));
|
||||
close(fd);
|
||||
log_errorf("could not prepare new client socket: %s", strerror(errno));
|
||||
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) {
|
||||
log_errorf("Could not add client socket to poller: %s", strerror(errno));
|
||||
client_destroy(client);
|
||||
log_errorf("could not add client socket to poller: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
log_infof("New client connected (pid: %d, uid: %d, gid: %d)", client->pid, client->uid,
|
||||
log_infof("new client connected (pid: %d, uid: %d, gid: %d)", client->pid, client->uid,
|
||||
client->gid);
|
||||
return 0;
|
||||
}
|
||||
|
@ -152,14 +139,14 @@ 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_error("Server socket received an error");
|
||||
log_errorf("server socket recieved an error: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mask & EVENT_READABLE) {
|
||||
int new_fd = accept(fd, NULL, NULL);
|
||||
if (fd == -1) {
|
||||
log_errorf("Could not accept client connection: %s", strerror(errno));
|
||||
log_errorf("could not accept client connection: %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "connection.h"
|
||||
#include "test.h"
|
||||
|
||||
static void test_send_one_byte(void) {
|
||||
int fds[2];
|
||||
socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||
struct connection c1 = {.fd = fds[0]};
|
||||
struct connection c2 = {.fd = fds[1]};
|
||||
|
||||
char in = 85, out = 0;
|
||||
|
||||
test_assert(connection_put(&c1, &in, sizeof in) == 0);
|
||||
test_assert(connection_flush(&c1) == sizeof in);
|
||||
|
||||
test_assert(connection_read(&c2) == sizeof out);
|
||||
test_assert(connection_pending(&c2) == sizeof out);
|
||||
test_assert(connection_get(&c2, &out, sizeof out) == sizeof out);
|
||||
test_assert(out == in);
|
||||
|
||||
test_assert(connection_pending(&c2) == 0);
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
static void test_short_read(void) {
|
||||
int fds[2];
|
||||
socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||
struct connection c1 = {.fd = fds[0]};
|
||||
struct connection c2 = {.fd = fds[1]};
|
||||
|
||||
char in = 85, out = 0;
|
||||
int out_large = 0;
|
||||
|
||||
test_assert(connection_put(&c1, &in, sizeof in) == 0);
|
||||
test_assert(connection_flush(&c1) > 0);
|
||||
|
||||
test_assert(connection_read(&c2) > 0);
|
||||
|
||||
test_assert(connection_pending(&c2) == sizeof out);
|
||||
test_assert(connection_get(&c2, &out, sizeof out_large) == -1);
|
||||
test_assert(errno == EAGAIN);
|
||||
|
||||
test_assert(connection_pending(&c2) == sizeof out);
|
||||
test_assert(connection_get(&c2, &out, sizeof out) == sizeof out);
|
||||
|
||||
test_assert(out == in);
|
||||
|
||||
test_assert(connection_pending(&c2) == 0);
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
static void test_long_write(void) {
|
||||
int fds[2];
|
||||
socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||
struct connection c1 = {.fd = fds[0]};
|
||||
struct connection c2 = {.fd = fds[1]};
|
||||
|
||||
char in[CONNECTION_BUFFER_SIZE + 1];
|
||||
memset(in, 0, sizeof in);
|
||||
|
||||
test_assert(connection_put(&c1, &in, sizeof in) == -1);
|
||||
test_assert(errno = EAGAIN);
|
||||
|
||||
test_assert(connection_read(&c2) == -1 && errno == EAGAIN);
|
||||
test_assert(connection_pending(&c2) == 0);
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
static void test_send_one_int(void) {
|
||||
int fds[2];
|
||||
socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||
struct connection c1 = {.fd = fds[0]};
|
||||
struct connection c2 = {.fd = fds[1]};
|
||||
|
||||
int in = 0xDEADBEEF, out = 0;
|
||||
|
||||
test_assert(connection_put(&c1, &in, sizeof in) == 0);
|
||||
test_assert(connection_flush(&c1) == sizeof in);
|
||||
|
||||
test_assert(connection_read(&c2) == sizeof out);
|
||||
test_assert(connection_pending(&c2) == sizeof out);
|
||||
test_assert(connection_get(&c2, &out, sizeof out) == sizeof out);
|
||||
|
||||
test_assert(out == in);
|
||||
|
||||
test_assert(connection_pending(&c2) == 0);
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
static void test_restore(void) {
|
||||
int fds[2];
|
||||
socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||
struct connection c1 = {.fd = fds[0]};
|
||||
struct connection c2 = {.fd = fds[1]};
|
||||
|
||||
int in = 0xDEADBEEF, out = 0;
|
||||
|
||||
test_assert(connection_put(&c1, &in, sizeof in) == 0);
|
||||
test_assert(connection_flush(&c1) == sizeof in);
|
||||
|
||||
test_assert(connection_read(&c2) == sizeof out);
|
||||
test_assert(connection_pending(&c2) == sizeof out);
|
||||
test_assert(connection_get(&c2, &out, sizeof out) == sizeof out);
|
||||
|
||||
test_assert(out == in);
|
||||
|
||||
test_assert(connection_pending(&c2) == 0);
|
||||
|
||||
connection_restore(&c2, sizeof out);
|
||||
test_assert(connection_pending(&c2) == sizeof out);
|
||||
test_assert(connection_get(&c2, &out, sizeof out) == sizeof out);
|
||||
|
||||
test_assert(out == in);
|
||||
|
||||
test_assert(connection_pending(&c2) == 0);
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
static void test_send_variable_sequence(void) {
|
||||
int fds[2];
|
||||
socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||
struct connection c1 = {.fd = fds[0]};
|
||||
struct connection c2 = {.fd = fds[1]};
|
||||
|
||||
char in1 = 85, out1 = 0;
|
||||
int in2 = 0xDEADBEEF, out2 = 0;
|
||||
uint64_t in3 = 0xCAFEDEADBEEF, out3 = 0;
|
||||
char in4 = 85, out4 = 0;
|
||||
|
||||
test_assert(connection_put(&c1, &in1, sizeof in1) == 0);
|
||||
test_assert(connection_put(&c1, &in2, sizeof in2) == 0);
|
||||
test_assert(connection_put(&c1, &in3, sizeof in3) == 0);
|
||||
test_assert(connection_put(&c1, &in4, sizeof in4) == 0);
|
||||
test_assert(connection_flush(&c1) > 0);
|
||||
|
||||
test_assert(connection_read(&c2) > 0);
|
||||
test_assert(connection_pending(&c2) > 0);
|
||||
test_assert(connection_get(&c2, &out1, sizeof out1) == sizeof out1);
|
||||
test_assert(connection_get(&c2, &out2, sizeof out2) == sizeof out2);
|
||||
test_assert(connection_get(&c2, &out3, sizeof out3) == sizeof out3);
|
||||
test_assert(connection_get(&c2, &out4, sizeof out4) == sizeof out4);
|
||||
|
||||
test_assert(out1 == in1);
|
||||
test_assert(out2 == in2);
|
||||
test_assert(out3 == in3);
|
||||
test_assert(out4 == in4);
|
||||
|
||||
test_assert(connection_pending(&c2) == 0);
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
test_run(test_send_one_byte);
|
||||
test_run(test_short_read);
|
||||
test_run(test_long_write);
|
||||
test_run(test_send_one_int);
|
||||
test_run(test_restore);
|
||||
test_run(test_send_variable_sequence);
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "linked_list.h"
|
||||
#include "test.h"
|
||||
|
||||
struct list_elem {
|
||||
struct linked_list link;
|
||||
|
@ -15,10 +15,10 @@ static void test_linked_list_init(void) {
|
|||
linked_list_init(&list);
|
||||
|
||||
// Both next and prev should point to self
|
||||
test_assert(list.next == &list && list.prev == &list);
|
||||
assert(list.next == &list && list.prev == &list);
|
||||
|
||||
// The list should be empty
|
||||
test_assert(linked_list_empty(&list));
|
||||
assert(linked_list_empty(&list));
|
||||
}
|
||||
|
||||
static void test_linked_list_single_insert(void) {
|
||||
|
@ -29,14 +29,14 @@ static void test_linked_list_single_insert(void) {
|
|||
linked_list_insert(&list, &elem1.link);
|
||||
|
||||
// Both next and prev on list should point to the elem
|
||||
test_assert(list.next == &elem1.link && list.prev == &elem1.link);
|
||||
assert(list.next == &elem1.link && list.prev == &elem1.link);
|
||||
|
||||
// Both next and prev on elem should point to the list
|
||||
test_assert(elem1.link.next == &list && elem1.link.prev == &list);
|
||||
assert(elem1.link.next == &list && elem1.link.prev == &list);
|
||||
|
||||
// The list and element should not be empty
|
||||
test_assert(!linked_list_empty(&list));
|
||||
test_assert(!linked_list_empty(&elem1.link));
|
||||
assert(!linked_list_empty(&list));
|
||||
assert(!linked_list_empty(&elem1.link));
|
||||
}
|
||||
|
||||
static void test_linked_list_single_remove(void) {
|
||||
|
@ -48,13 +48,13 @@ static void test_linked_list_single_remove(void) {
|
|||
linked_list_remove(&elem1.link);
|
||||
|
||||
// Both next and prev on elem be NULL
|
||||
test_assert(elem1.link.next == NULL && elem1.link.prev == NULL);
|
||||
assert(elem1.link.next == NULL && elem1.link.prev == NULL);
|
||||
|
||||
// Both next and prev should point to self
|
||||
test_assert(list.next == &list && list.prev == &list);
|
||||
assert(list.next == &list && list.prev == &list);
|
||||
|
||||
// The list should be empty
|
||||
test_assert(linked_list_empty(&list));
|
||||
assert(linked_list_empty(&list));
|
||||
}
|
||||
|
||||
static void test_linked_list_alternate_remove(void) {
|
||||
|
@ -66,13 +66,13 @@ static void test_linked_list_alternate_remove(void) {
|
|||
linked_list_remove(&list);
|
||||
|
||||
// Both next and prev on list be NULL
|
||||
test_assert(list.next == NULL && list.prev == NULL);
|
||||
assert(list.next == NULL && list.prev == NULL);
|
||||
|
||||
// Both next and prev should point to self
|
||||
test_assert(elem1.link.next == &elem1.link && elem1.link.prev == &elem1.link);
|
||||
assert(elem1.link.next == &elem1.link && elem1.link.prev == &elem1.link);
|
||||
|
||||
// The elem should be empty
|
||||
test_assert(linked_list_empty(&elem1.link));
|
||||
assert(linked_list_empty(&elem1.link));
|
||||
}
|
||||
|
||||
static void test_linked_list_sequential_remove(void) {
|
||||
|
@ -85,35 +85,35 @@ static void test_linked_list_sequential_remove(void) {
|
|||
linked_list_insert(&elem2.link, &elem3.link);
|
||||
|
||||
// The order should now be list→elem1→elem2→elem3→list
|
||||
test_assert(list.next == &elem1.link && list.prev == &elem3.link);
|
||||
test_assert(elem1.link.next == &elem2.link && elem1.link.prev == &list);
|
||||
test_assert(elem2.link.next == &elem3.link && elem2.link.prev == &elem1.link);
|
||||
test_assert(elem3.link.next == &list && elem3.link.prev == &elem2.link);
|
||||
assert(list.next == &elem1.link && list.prev == &elem3.link);
|
||||
assert(elem1.link.next == &elem2.link && elem1.link.prev == &list);
|
||||
assert(elem2.link.next == &elem3.link && elem2.link.prev == &elem1.link);
|
||||
assert(elem3.link.next == &list && elem3.link.prev == &elem2.link);
|
||||
|
||||
linked_list_remove(list.next);
|
||||
|
||||
// The order should now be list→elem2→elem3→list
|
||||
test_assert(list.next == &elem2.link && list.prev == &elem3.link);
|
||||
test_assert(elem2.link.next == &elem3.link && elem2.link.prev == &list);
|
||||
test_assert(elem3.link.next == &list && elem3.link.prev == &elem2.link);
|
||||
test_assert(elem1.link.next == NULL && elem1.link.prev == NULL);
|
||||
assert(list.next == &elem2.link && list.prev == &elem3.link);
|
||||
assert(elem2.link.next == &elem3.link && elem2.link.prev == &list);
|
||||
assert(elem3.link.next == &list && elem3.link.prev == &elem2.link);
|
||||
assert(elem1.link.next == NULL && elem1.link.prev == NULL);
|
||||
|
||||
linked_list_remove(list.next);
|
||||
|
||||
// The order should now be list→elem3→list
|
||||
test_assert(list.next == &elem3.link && list.prev == &elem3.link);
|
||||
test_assert(elem3.link.next == &list && elem3.link.prev == &list);
|
||||
test_assert(elem1.link.next == NULL && elem1.link.prev == NULL);
|
||||
test_assert(elem2.link.next == NULL && elem2.link.prev == NULL);
|
||||
assert(list.next == &elem3.link && list.prev == &elem3.link);
|
||||
assert(elem3.link.next == &list && elem3.link.prev == &list);
|
||||
assert(elem1.link.next == NULL && elem1.link.prev == NULL);
|
||||
assert(elem2.link.next == NULL && elem2.link.prev == NULL);
|
||||
|
||||
linked_list_remove(list.next);
|
||||
|
||||
// The list should now be empty
|
||||
test_assert(elem1.link.next == NULL && elem1.link.prev == NULL);
|
||||
test_assert(elem2.link.next == NULL && elem2.link.prev == NULL);
|
||||
test_assert(elem3.link.next == NULL && elem3.link.prev == NULL);
|
||||
test_assert(list.next == &list && list.prev == &list);
|
||||
test_assert(linked_list_empty(&list));
|
||||
assert(elem1.link.next == NULL && elem1.link.prev == NULL);
|
||||
assert(elem2.link.next == NULL && elem2.link.prev == NULL);
|
||||
assert(elem3.link.next == NULL && elem3.link.prev == NULL);
|
||||
assert(list.next == &list && list.prev == &list);
|
||||
assert(linked_list_empty(&list));
|
||||
}
|
||||
|
||||
static void test_linked_list_insert_after(void) {
|
||||
|
@ -126,10 +126,10 @@ static void test_linked_list_insert_after(void) {
|
|||
linked_list_insert(&elem1.link, &elem2.link);
|
||||
|
||||
// The order should now be list→elem1→elem2→elem3→list
|
||||
test_assert(list.next == &elem1.link && list.prev == &elem3.link);
|
||||
test_assert(elem1.link.next == &elem2.link && elem1.link.prev == &list);
|
||||
test_assert(elem2.link.next == &elem3.link && elem2.link.prev == &elem1.link);
|
||||
test_assert(elem3.link.next == &list && elem3.link.prev == &elem2.link);
|
||||
assert(list.next == &elem1.link && list.prev == &elem3.link);
|
||||
assert(elem1.link.next == &elem2.link && elem1.link.prev == &list);
|
||||
assert(elem2.link.next == &elem3.link && elem2.link.prev == &elem1.link);
|
||||
assert(elem3.link.next == &list && elem3.link.prev == &elem2.link);
|
||||
}
|
||||
|
||||
static void test_linked_list_remove_loop(void) {
|
||||
|
@ -147,13 +147,13 @@ static void test_linked_list_remove_loop(void) {
|
|||
linked_list_remove(&elem->link);
|
||||
cnt++;
|
||||
}
|
||||
test_assert(cnt == 3);
|
||||
assert(cnt == 3);
|
||||
|
||||
// Link should now be empty, and next and prev on all elements hsould be NULL
|
||||
test_assert(linked_list_empty(&list));
|
||||
test_assert(elem1.link.next == NULL && elem1.link.prev == NULL);
|
||||
test_assert(elem2.link.next == NULL && elem2.link.prev == NULL);
|
||||
test_assert(elem3.link.next == NULL && elem3.link.prev == NULL);
|
||||
assert(linked_list_empty(&list));
|
||||
assert(elem1.link.next == NULL && elem1.link.prev == NULL);
|
||||
assert(elem2.link.next == NULL && elem2.link.prev == NULL);
|
||||
assert(elem3.link.next == NULL && elem3.link.prev == NULL);
|
||||
}
|
||||
|
||||
static void test_linked_list_manual_iterate(void) {
|
||||
|
@ -170,15 +170,15 @@ static void test_linked_list_manual_iterate(void) {
|
|||
struct list_elem *ptr = NULL;
|
||||
|
||||
ptr = (struct list_elem *)list.next;
|
||||
test_assert(strcmp("elem1", ptr->content) == 0);
|
||||
assert(strcmp("elem1", ptr->content) == 0);
|
||||
|
||||
ptr = (struct list_elem *)ptr->link.next;
|
||||
test_assert(strcmp("elem2", ptr->content) == 0);
|
||||
assert(strcmp("elem2", ptr->content) == 0);
|
||||
|
||||
ptr = (struct list_elem *)ptr->link.next;
|
||||
test_assert(strcmp("elem3", ptr->content) == 0);
|
||||
assert(strcmp("elem3", ptr->content) == 0);
|
||||
|
||||
test_assert(ptr->link.next == &list);
|
||||
assert(ptr->link.next == &list);
|
||||
}
|
||||
|
||||
static void test_linked_list_loop_iterate(void) {
|
||||
|
@ -195,97 +195,25 @@ static void test_linked_list_loop_iterate(void) {
|
|||
size_t cnt = 0;
|
||||
for (struct linked_list *ptr = list.next; ptr != &list; ptr = ptr->next) {
|
||||
struct list_elem *elem = (struct list_elem *)ptr;
|
||||
test_assert(strcmp("elem", elem->content) == 0);
|
||||
assert(strcmp("elem", elem->content) == 0);
|
||||
cnt++;
|
||||
}
|
||||
test_assert(cnt == 3);
|
||||
}
|
||||
|
||||
static void test_linked_list_take_empty(void) {
|
||||
struct linked_list list1, list2;
|
||||
linked_list_init(&list1);
|
||||
linked_list_init(&list2);
|
||||
|
||||
linked_list_take(&list2, &list1);
|
||||
|
||||
test_assert(linked_list_empty(&list1));
|
||||
test_assert(linked_list_empty(&list2));
|
||||
}
|
||||
|
||||
static void test_linked_list_take_single(void) {
|
||||
struct linked_list list1, list2;
|
||||
linked_list_init(&list1);
|
||||
linked_list_init(&list2);
|
||||
|
||||
struct list_elem elem1 = {{0}, NULL};
|
||||
linked_list_insert(&list1, &elem1.link);
|
||||
|
||||
linked_list_take(&list2, &list1);
|
||||
|
||||
test_assert(linked_list_empty(&list1));
|
||||
test_assert(list2.next == &elem1.link && list2.prev == &elem1.link);
|
||||
test_assert(elem1.link.next == &list2 && elem1.link.prev == &list2);
|
||||
}
|
||||
|
||||
static void test_linked_list_take_many(void) {
|
||||
struct linked_list list1, list2;
|
||||
linked_list_init(&list1);
|
||||
linked_list_init(&list2);
|
||||
|
||||
struct list_elem elem1 = {{0}, NULL};
|
||||
struct list_elem elem2 = {{0}, NULL};
|
||||
linked_list_insert(&list1, &elem2.link);
|
||||
linked_list_insert(&list1, &elem1.link);
|
||||
|
||||
linked_list_take(&list2, &list1);
|
||||
|
||||
test_assert(linked_list_empty(&list1));
|
||||
test_assert(list2.next == &elem1.link && list2.prev == &elem2.link);
|
||||
test_assert(elem1.link.next == &elem2.link && elem1.link.prev == &list2);
|
||||
test_assert(elem2.link.next == &list2 && elem2.link.prev == &elem1.link);
|
||||
}
|
||||
|
||||
static void test_linked_list_take_concat(void) {
|
||||
struct linked_list list1, list2;
|
||||
linked_list_init(&list1);
|
||||
linked_list_init(&list2);
|
||||
|
||||
struct list_elem elem1 = {{0}, NULL};
|
||||
struct list_elem elem2 = {{0}, NULL};
|
||||
struct list_elem elem3 = {{0}, NULL};
|
||||
struct list_elem elem4 = {{0}, NULL};
|
||||
linked_list_insert(&list1, &elem2.link);
|
||||
linked_list_insert(&list1, &elem1.link);
|
||||
linked_list_insert(&list2, &elem4.link);
|
||||
linked_list_insert(&list2, &elem3.link);
|
||||
|
||||
linked_list_take(&list2, &list1);
|
||||
|
||||
test_assert(linked_list_empty(&list1));
|
||||
test_assert(list2.next == &elem1.link && list2.prev == &elem4.link);
|
||||
test_assert(elem1.link.next == &elem2.link && elem1.link.prev == &list2);
|
||||
test_assert(elem2.link.next == &elem3.link && elem2.link.prev == &elem1.link);
|
||||
test_assert(elem3.link.next == &elem4.link && elem3.link.prev == &elem2.link);
|
||||
test_assert(elem4.link.next == &list2 && elem4.link.prev == &elem3.link);
|
||||
assert(cnt == 3);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
test_run(test_linked_list_init);
|
||||
test_run(test_linked_list_single_insert);
|
||||
test_run(test_linked_list_single_remove);
|
||||
test_run(test_linked_list_alternate_remove);
|
||||
test_run(test_linked_list_sequential_remove);
|
||||
test_run(test_linked_list_insert_after);
|
||||
test_run(test_linked_list_remove_loop);
|
||||
test_run(test_linked_list_manual_iterate);
|
||||
test_run(test_linked_list_loop_iterate);
|
||||
test_run(test_linked_list_take_empty);
|
||||
test_run(test_linked_list_take_single);
|
||||
test_run(test_linked_list_take_many);
|
||||
test_run(test_linked_list_take_concat);
|
||||
test_linked_list_init();
|
||||
test_linked_list_single_insert();
|
||||
test_linked_list_single_remove();
|
||||
test_linked_list_alternate_remove();
|
||||
test_linked_list_sequential_remove();
|
||||
test_linked_list_insert_after();
|
||||
test_linked_list_remove_loop();
|
||||
test_linked_list_manual_iterate();
|
||||
test_linked_list_loop_iterate();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
195
tests/poller.c
195
tests/poller.c
|
@ -1,195 +0,0 @@
|
|||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "poller.h"
|
||||
#include "test.h"
|
||||
|
||||
static void test_poller_init(void) {
|
||||
struct poller poller;
|
||||
test_assert(poller_init(&poller) == 0);
|
||||
poller_finish(&poller);
|
||||
}
|
||||
|
||||
struct test_fd {
|
||||
int fd;
|
||||
uint32_t events;
|
||||
};
|
||||
|
||||
static int test_fd_event(int fd, uint32_t mask, void *data) {
|
||||
struct test_fd *d = data;
|
||||
d->fd = fd;
|
||||
d->events = mask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_poller_single_fd(void) {
|
||||
struct poller poller;
|
||||
test_assert(poller_init(&poller) == 0);
|
||||
|
||||
int fds[2];
|
||||
test_assert(pipe(fds) == 0);
|
||||
|
||||
struct test_fd evd;
|
||||
struct event_source_fd *ev =
|
||||
poller_add_fd(&poller, fds[0], EVENT_READABLE, test_fd_event, &evd);
|
||||
test_assert(ev != NULL);
|
||||
|
||||
evd.fd = 0;
|
||||
evd.events = 0;
|
||||
test_assert(write(fds[1], "\0", 1) == 1);
|
||||
test_assert(poller_poll(&poller) == 0);
|
||||
test_assert(evd.fd == fds[0]);
|
||||
test_assert(evd.events == EVENT_READABLE);
|
||||
|
||||
evd.fd = 0;
|
||||
evd.events = 0;
|
||||
test_assert(write(fds[1], "\0", 1) == 1);
|
||||
test_assert(poller_poll(&poller) == 0);
|
||||
test_assert(evd.fd == fds[0]);
|
||||
test_assert(evd.events == EVENT_READABLE);
|
||||
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
poller_finish(&poller);
|
||||
}
|
||||
|
||||
static void test_poller_multi_fd(void) {
|
||||
struct poller poller;
|
||||
test_assert(poller_init(&poller) == 0);
|
||||
|
||||
char dummy[8];
|
||||
int fdsa[2], fdsb[2];
|
||||
test_assert(pipe(fdsa) == 0);
|
||||
test_assert(pipe(fdsb) == 0);
|
||||
|
||||
struct test_fd evd1, evd2;
|
||||
struct event_source_fd *ev1 =
|
||||
poller_add_fd(&poller, fdsa[0], EVENT_READABLE, test_fd_event, &evd1);
|
||||
struct event_source_fd *ev2 =
|
||||
poller_add_fd(&poller, fdsb[0], EVENT_READABLE, test_fd_event, &evd2);
|
||||
test_assert(ev1 != NULL);
|
||||
test_assert(ev2 != NULL);
|
||||
|
||||
evd1.fd = evd2.fd = 0;
|
||||
evd1.events = evd2.events = 0;
|
||||
test_assert(write(fdsa[1], "\0", 1) == 1);
|
||||
test_assert(poller_poll(&poller) == 0);
|
||||
test_assert(read(fdsa[0], &dummy, sizeof dummy) == 1);
|
||||
test_assert(evd1.fd == fdsa[0]);
|
||||
test_assert(evd1.events == EVENT_READABLE);
|
||||
test_assert(evd2.fd == 0);
|
||||
test_assert(evd2.events == 0);
|
||||
|
||||
evd1.fd = evd2.fd = 0;
|
||||
evd1.events = evd2.events = 0;
|
||||
test_assert(write(fdsb[1], "\0", 1) == 1);
|
||||
test_assert(poller_poll(&poller) == 0);
|
||||
test_assert(read(fdsb[0], &dummy, sizeof dummy) == 1);
|
||||
test_assert(evd1.fd == 0);
|
||||
test_assert(evd1.events == 0);
|
||||
test_assert(evd2.fd == fdsb[0]);
|
||||
test_assert(evd2.events == EVENT_READABLE);
|
||||
|
||||
evd1.fd = evd2.fd = 0;
|
||||
evd1.events = evd2.events = 0;
|
||||
test_assert(write(fdsa[1], "\0", 1) == 1);
|
||||
test_assert(write(fdsb[1], "\0", 1) == 1);
|
||||
test_assert(poller_poll(&poller) == 0);
|
||||
test_assert(read(fdsa[0], &dummy, sizeof dummy) == 1);
|
||||
test_assert(read(fdsb[0], &dummy, sizeof dummy) == 1);
|
||||
test_assert(evd1.fd == fdsa[0]);
|
||||
test_assert(evd1.events == EVENT_READABLE);
|
||||
test_assert(evd2.fd == fdsb[0]);
|
||||
test_assert(evd2.events == EVENT_READABLE);
|
||||
|
||||
close(fdsa[0]);
|
||||
close(fdsa[1]);
|
||||
close(fdsb[0]);
|
||||
close(fdsb[1]);
|
||||
poller_finish(&poller);
|
||||
}
|
||||
|
||||
struct test_signal {
|
||||
int signal;
|
||||
};
|
||||
|
||||
static int test_signal_event(int signal, void *data) {
|
||||
struct test_signal *d = data;
|
||||
d->signal = signal;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#define SIGRTMIN SIGUSR1
|
||||
#endif
|
||||
|
||||
static void test_poller_single_signal(void) {
|
||||
struct poller poller;
|
||||
test_assert(poller_init(&poller) == 0);
|
||||
|
||||
struct test_signal evd;
|
||||
struct event_source_signal *ev =
|
||||
poller_add_signal(&poller, SIGRTMIN, test_signal_event, &evd);
|
||||
test_assert(ev != NULL);
|
||||
|
||||
evd.signal = 0;
|
||||
test_assert(kill(getpid(), SIGRTMIN) == 0);
|
||||
test_assert(poller_poll(&poller) == 0);
|
||||
test_assert(evd.signal == SIGRTMIN);
|
||||
|
||||
evd.signal = 0;
|
||||
test_assert(kill(getpid(), SIGRTMIN) == 0);
|
||||
test_assert(poller_poll(&poller) == 0);
|
||||
test_assert(evd.signal == SIGRTMIN);
|
||||
|
||||
poller_finish(&poller);
|
||||
}
|
||||
|
||||
static void test_poller_multi_signal(void) {
|
||||
struct poller poller;
|
||||
test_assert(poller_init(&poller) == 0);
|
||||
|
||||
struct test_signal evd1, evd2;
|
||||
struct event_source_signal *ev1 =
|
||||
poller_add_signal(&poller, SIGRTMIN, test_signal_event, &evd1);
|
||||
struct event_source_signal *ev2 =
|
||||
poller_add_signal(&poller, SIGRTMIN + 1, test_signal_event, &evd2);
|
||||
test_assert(ev1 != NULL);
|
||||
test_assert(ev2 != NULL);
|
||||
|
||||
evd1.signal = evd2.signal = 0;
|
||||
test_assert(kill(getpid(), SIGRTMIN) == 0);
|
||||
test_assert(poller_poll(&poller) == 0);
|
||||
test_assert(evd1.signal == SIGRTMIN);
|
||||
test_assert(evd2.signal == 0);
|
||||
|
||||
evd1.signal = evd2.signal = 0;
|
||||
test_assert(kill(getpid(), SIGRTMIN + 1) == 0);
|
||||
test_assert(poller_poll(&poller) == 0);
|
||||
test_assert(evd1.signal == 0);
|
||||
test_assert(evd2.signal == SIGRTMIN + 1);
|
||||
|
||||
evd1.signal = evd2.signal = 0;
|
||||
test_assert(kill(getpid(), SIGRTMIN) == 0);
|
||||
test_assert(kill(getpid(), SIGRTMIN + 1) == 0);
|
||||
test_assert(poller_poll(&poller) == 0);
|
||||
test_assert(evd1.signal == SIGRTMIN);
|
||||
test_assert(evd2.signal == SIGRTMIN + 1);
|
||||
|
||||
poller_finish(&poller);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
test_run(test_poller_init);
|
||||
test_run(test_poller_single_fd);
|
||||
test_run(test_poller_multi_fd);
|
||||
test_run(test_poller_single_signal);
|
||||
test_run(test_poller_multi_signal);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue