Compare commits

...

128 commits
0.5.0 ... obsd

Author SHA1 Message Date
b4462cb033 attempt to support VT switches on OpenBSD 2024-07-10 15:19:41 +02:00
cc09e26976 Merge remote-tracking branch 'origin/master' into obsd 2023-11-08 12:00:16 +01:00
Adrien Demarez
0746edbeae seatd: fix small bug in assert 2023-10-24 11:59:51 +02:00
Kenny Levinsen
3e9ef69f14 Bump version to 0.8.0 2023-07-19 11:18:33 +02:00
a8aee6fa70 Add basic OpenBSD support
XXX more work needed to manage VT switches and proper
XXX fbtab(4) support to allow for non-root users
2023-07-05 16:57:27 +02:00
4b2ecdf936 Add basic OpenBSD support
XXX more work needed to manage VT switches and proper
XXX fbtab(4) support to allow for non-root users
2023-07-05 10:49:24 +02:00
f2ff233c26 No -lrt on OpenBSD
XXX This is crude, should add meson tests
2023-07-05 10:48:14 +02:00
dbaa859f28 define _BSD_SOURCE rather then __BSD_VISIBLE
__BSD_VISIBLE was set to 0, causing issues on OpenBSD
The proper way is to set _BSD_SOURCE
2023-07-05 10:47:06 +02:00
Jessica Clarke
1bd042e5b0 drm: Support drm-subtree drivers on FreeBSD
The drm-kmod drivers use linuxkpi and end up with /dev/drm being the
canonical path for the devices, but the drm-subtree drivers use drmkpi
which has them appear under /dev/dri like Linux. Thus, adapt path_is_drm
to recognise both on FreeBSD.
2023-05-24 16:43:21 +02:00
Anna (navi) Figueiredo Gomes
56720a6275 noop: Additional open flags for open(2)
Matching the functionallity by the seatd open call. O_NONBLOCK is
specially important for libseat, otherwise it hangs while trying to
drain all events from an input device fd.

Signed-off-by: Anna (navi) Figueiredo Gomes <navi@vlhl.dev>
2023-04-01 15:34:53 +02:00
Anna (navi) Figueiredo Gomes
e5b018def8 noop: Return seat0 as the seat name
wlroots' libinput backend expects the name of the seat to either match
ID_SEAT from udev, or in case ID_SEAT returns nothing, match seat0. As
noop has no seat switching, always returning seat0 as the session name
fixes that.

Signed-off-by: Anna (navi) Figueiredo Gomes <navi@vlhl.dev>
2023-04-01 15:34:53 +02:00
Alyssa Ross
3e0d510b2c meson: fix seatdpath with absolute bindir
Quoting the Meson documentation:

> Note that the value returned for built-in options that end in `dir`
> such as `bindir` and `libdir` is usually a path relative to (and
> inside) the `prefix` but you should not rely on that, as it can also
> be an absolute path [in some cases](Builtin-options.md#universal-options).
> [`install_dir` arguments](Installing.md) handle that as expected
> but if you need an absolute path, e.g. to use in a define etc.,
> you should use the path concatenation operator like this:
> `get_option('prefix') / get_option('localstatedir')`.
> Never manually join paths as if they were strings.

The concatenation of two absolute paths caused seatd-launch in Nixpkgs
to try to launch e.g.
/nix/store/43wyk9s2l2z8cparnshbf24d39vm5272-seatd-0.7.0//nix/store/j9a7k4qqwc3byyfmpqwg46shmh6g82yf-seatd-0.7.0-bin/bin/seatd.
2023-03-13 17:06:31 +01:00
Simon Ser
207e2a5936 man: add missing arg in -n syntax 2023-01-28 23:10:29 +01:00
Chia-I Wu
9b8b6e0bf8 noop: initialize initial_setup
Otherwise the enable_seat callback is never called.
2023-01-16 13:13:52 +01:00
Simon Ser
14355639f8 man: document SEATD_VTBOUND 2022-11-30 18:38:50 +01:00
Kenny Levinsen
a803ba0502 seatd-launch: Avoid argv[0] in help text 2022-05-23 22:03:38 +02:00
Kenny Levinsen
6888653a8d wscons: Fix STRLEN 2022-03-30 00:39:35 +02:00
Simon Ser
2842f0e2b1 seatd: refuse to compile with missing get_peer impl
Instead of using a stub, error out when get_peer isn't implemented
for the target platform. client_create relies on it.
2022-03-29 20:08:00 +02:00
Simon Ser
85d0bf5943 seatd: handle client_create failure
Failure to create the client causes a null pointer dereference.
2022-03-29 20:07:47 +02:00
Kenny Levinsen
8f8c9558e6 drm: Make dev_is_drm local to logind backend
This function is only used for logind, which is Linux-specific, but the
presence in common/drm.c suggested that it had to be portable.

Move it to the logind backend for now.
2022-03-29 10:54:27 +02:00
Kenny Levinsen
0462e9331d wscons: Move to its own device type
This reduces ifdefs and avoids overloading evdev as something it is not.
2022-03-29 10:54:27 +02:00
Kenny Levinsen
684dd61945 terminal: Revert FreeBSD behavior in set_keyboard
4ad48cb305 introduced support for NetBSD,
which modified a number of our ifdefs. In that process, FreeBSD was
accidentally excluded from an important code path that controls keyboard
usage on the kernel console.

Revert part of that change to restore FreeBSD behavior.
2022-03-29 10:03:30 +02:00
Kenny Levinsen
d5539dead8 meson: library soversion arg should be string
muon, a meson implementation in C, is more strict with its types and
revealed this discrepancy between meson behavior and documentation.
2022-03-28 16:28:00 +02:00
Kenny Levinsen
845256009b readme: Mention NetBSD 2022-03-23 20:29:09 +01:00
illiliti
a5f9a2a2c8 ci: Add NetBSD 2022-03-16 21:39:46 +01:00
illiliti
4ad48cb305 Initial netbsd support 2022-03-16 21:39:46 +01:00
Kenny Levinsen
1990f9b034 ci: Set loglevel argument to debug 2022-03-16 21:27:05 +01:00
Kenny Levinsen
bb0efb65b3 Bump version to 0.7.0 2022-03-03 17:52:50 +01:00
Kenny Levinsen
ce6a6b7d2e meson: Fix meson warnings 2022-03-03 17:52:26 +01:00
Kenny Levinsen
8dc6a50d88 builtin: Remove deathsig and log start/stop
Proper handling of client disconnect mean that we no longer need
deathsig handling.
2022-03-03 17:52:21 +01:00
Kenny Levinsen
795cf169e7 seatd: Shut down on client disconnect in builtin
If we're part of the libseat builtin backend, then we only have one
client. Shut down the server when this client disconnects.
2022-03-03 14:44:56 +01:00
Kenny Levinsen
46c83972fe builtin: Close other end of socketpair after fork
We will not get a socket hangup if we have duplicates socket fds in the
parent or child, so make sure we clean this up properly after fork.
2022-03-03 14:44:49 +01:00
Kenny Levinsen
abcecbb53b meson: Only set libseat defines for libseat itself
This allows us to distinguish between when we build seatd as part of the
builtin libseat backend, or when we build seatd on its own.
2022-03-03 14:40:40 +01:00
Kenny Levinsen
ae42d05513 seatd: Change default log-level to info 2022-02-26 22:56:42 +01:00
Kenny Levinsen
0d6bdf4f01 seatd: Remove runtime socket path configuration
Configurable socket paths exist mainly to facilitate multiple parallel
seatd instances. However, the only valid use-case for running multiple
instances of seatd is testing during development, which can just as well
be done by changing SEATD_DEFAULTPATH at compile-time for test builds.

Remove the command-line argument in seatd for runtime configuration of
socket path, hardcode the socket path in seatd-launch, and change seatd
unlink/chmod/chown code to not run when started by seatd-launch.

This means that seatd-launch will now fail to start seatd if another
seatd instance is already running. The unlink code still runs when seatd
is started normally to assist in system crash recovery, but this may be
removed later if we deem it unnecessary.
2022-02-26 22:25:27 +01:00
Kenny Levinsen
466efea49b seatd: Handle socket unlink errors
This ensures early failure and better error messages.
2022-02-26 19:37:49 +01:00
Kenny Levinsen
9bbdf0f0b8 seatd: Command-line argument for loglevel
SEATD_LOGLEVEL was used to set the loglevel despite already having
getopt in place. Remove the environment variable and make a command-line
argument for it instead.
2022-02-26 19:17:26 +01:00
Kenny Levinsen
3eb0db57bb seatd-launch: Minor readability improvements 2022-02-21 21:33:06 +01:00
Kenny Levinsen
ed90ed62cd seatd-launch: Use snprintf for socket path
We also reduce the size of the buffer from 256 bytes to a much more
reasonable 32 bytes.
2022-02-21 21:27:14 +01:00
Kenny Levinsen
a44476ce65 seatd: Fix usage rendering 2022-02-21 12:04:09 +01:00
Kenny Levinsen
10658dc543 seatd-launch: Remove socket path command line arg
This should not need to be configured, so remove the argument. If
downstream prefers a different folder, the location can be made
compile-time configurable like for seatd itself.
2022-02-21 12:02:31 +01:00
Kenny Levinsen
32d06482d3 seatd-launch: Do not unlink socket path
seatd cleans up after itself and takes care of stale sockets when
applicable, so seatd-launch should not replicate this functionality.
2022-02-21 12:02:31 +01:00
Kenny Levinsen
0864f6a3ac seatd: Ensure socket gets unlinked on error 2022-02-21 12:02:31 +01:00
Kenny Levinsen
157ce68565 seatd: Remove SOCK_PATH and improve cleanup
SOCK_PATH is made redundant by the -s command-line argument added in
a98e0c4ce9. Support was originally left
behind for short-term compatibility, but it should be fine to remove.

Previous socket cleanup is changed to run unconditionally. The cleanup
now fails if the existing file is not a socket.
2022-02-21 12:02:31 +01:00
Kenny Levinsen
f128359332 logind: Always send ping if data is queued
sd_bus_call_method may have read and queued our ping response, so we
cannot assume that a previous ping will make the socket readable.

Instead, always send a ping if read or write queues are not empty, even
if a ping has already been sent.
2022-02-15 13:51:49 +01:00
Kenny Levinsen
b47c79d731 libseat: Use SOCK_CLOEXEC and SOCK_NONBLOCK
This both simplifies our code and fixes an exec fd leak when using
builtin or noop backends.
2022-02-09 23:22:29 +01:00
Simon Ser
96a5de8859 readme: add irc:// link
This allows users to just click the link to join the IRC channel.
2021-12-16 10:40:53 +01:00
Simon Ser
936ff9dbea build: use meson.override_dependency
This allows downstream users to write:

    dependency('libseat', fallback: 'seatd')

instead of having to rely on the seatd/meson.build's variable name:

    dependency('libseat', fallback: ['seatd', 'libseat'])
2021-12-16 10:40:32 +01:00
Simon Ser
f381e22955 build: don't use sh for scdoc
Just use the built-in feed/capture Meson options instead.
2021-11-28 15:41:53 +01:00
Simon Ser
d92fa01f88 build: use list for logind dep
This reduces the boilerplate a bit. Use logind.name() instead of
having a separate source of truth. Requires adapting the checks a
bit because the dep name has a "lib" prefix.
2021-11-28 15:40:56 +01:00
Simon Ser
69cf5c36e0 build: don't use cc.get_supported_arguments for defines
If the compiler errors out on some -DXXX flag, then we're in
trouble. Avoid using cc.get_supported_arguments for defines we
require. Only use it for detecting support for warning flags.
2021-10-28 16:31:17 +02:00
Simon Ser
88529f0856 seatd: don't log errno on EVENT_ERROR
errno won't be set when poll returns EVENT_ERROR.
2021-10-24 00:55:32 +02:00
Simon Ser
cb7a94378b seatd: avoid overwriting errno in set_nonblock error handling
If close fails, it'll mess up errno, and log_errorf will print a
non-sensical value.
2021-10-24 00:52:08 +02:00
Kenny Levinsen
88db55f606 Bump version to 0.6.3 2021-10-19 21:39:17 +02:00
Kenny Levinsen
e35c9cd02e logind: Set userdata for ping_handler 2021-10-05 10:05:25 +02:00
Kenny Levinsen
ec0d6565bb ci: Add logind smoketest to arch 2021-09-22 23:06:59 +02:00
Kenny Levinsen
8e5c00e7c8 logind: Improve error handling in open_seat
errno was not being set by open_seat in most cases, leading to
simpletest possibly failing with "libseat_open_seat() failed: Success".
2021-09-22 23:04:40 +02:00
Simon Ser
d2193b45ff examples/simpletest: check for libseat_dispatch failures 2021-09-22 21:59:41 +02:00
Simon Ser
262ccef84e logind: check if session is active on startup
Up until now we assumed the session was always active on startup.
This might not be the case. Instead, read the current value of the
Active property.
2021-09-22 21:55:49 +02:00
Kenny Levinsen
1c376ca9b1 ci: Inline smoketest into build scripts
The smoketest script has gotten significantly simpler, and can simply be
inlined into the build scripts if we don't care about running the tests
twice.

This should fix CI for mailing list patches.
2021-09-21 13:11:55 +02:00
Kenny Levinsen
e2baadc230 clang-format: Fix alignment 2021-09-21 12:48:24 +02:00
Kenny Levinsen
db08fb921f logind: Send ping to wake us up later
sd_bus_call drains received messages into the receive queue, and peeks
for its own return value. It does not dispatch the receive queue.

As the socket is drained, the caller will not wake from a poll and have
no reason to dispatch libseat. This has gone unnoticed largely due to
logind sending an event for every device, making it unlikely that no
unread message will be left on the socket.

Like we have done for seatd, we fix this by sending a "ping" request to
logind if anything is left in our receive queue as reported by
sd_bus_get_events. The response to this will wake us up and ensure that
dispatch is called.
2021-09-21 11:18:18 +02:00
Kenny Levinsen
2eee9aa445 seatd: Implement ping request to wake up later
When device open or close messages are sent to seatd, libseat must read
messages from the socket until it sees the associated response message.
This means that it may drain enable/disable seat events from the socket,
queueing them internally for deferred processing.

As the socket is drained, the caller will not wake from a poll and have
no reason to dispatch libseat. To ensure that these messages would not
be left in the queue, 6fa82930d0 made it
so that open/close calls would execute all queued events just before
returning.

Unfortunately, this had the side-effect of having events fire from the
stack of libseat_open_device or libseat_close_device, which we now see
cause problems in compositors. Specifically, an issue has been observed
where libinput end up calling libseat_close_device, which in turn
dispatch a disable seat event that calls libinput_suspend. libinput does
not like this.

Instead, remove the execution from libseat_open_device and
libseat_close_device, and instead make a "ping" request to seatd if
events have been queued. The response to this will wake us up and ensure
that dispatch is called.
2021-09-21 11:18:18 +02:00
Kenny Levinsen
0f20175752 Bump version to 0.6.2 2021-09-16 01:07:42 +02:00
Kenny Levinsen
ebf512c2bf seatd-launch: Specify exact environment to seatd
The parent environment might contain nasty things. Create a new
environment for the seatd process containing only the environment
variables we care about. For now, that's only SEATD_LOGLEVEL.
2021-09-16 00:55:43 +02:00
Kenny Levinsen
907b75de1a seatd-launch: Use absolute path for seatd
We previously used execlp to execute seatd, which had the effect of
searching PATH for the executable. This allowed the caller to control
what executable was run, which is bad if SUID has been set.

Instead, expose the absolute install path for seatd from meason as a
define, and use that in a call to execv.
2021-09-16 00:46:49 +02:00
Kenny Levinsen
4091ba2c07 ci: Install seatd instead of manipulating PATH 2021-09-16 00:46:42 +02:00
Kenny Levinsen
66becee6da Bump version to 0.6.1 2021-09-14 11:58:56 +02:00
Simon Ser
4e3b7b3bb6 seatd-launch: print unlink/kill errors
Makes it easier to find out that something went wrong.
2021-09-13 13:40:00 +02:00
Simon Ser
fe600eac2b seatd-launch: exit with status >128 if child is signalled
Mimick shells and exit with a status >128 if our child has been
signalled. Exiting with 128 + signal number is what most shells do
(POSIX only requires them to exit with >128).
2021-09-13 13:39:20 +02:00
Kenny Levinsen
8c85c46d2d man/seatd-launch: Make mssage about root clearer 2021-09-12 12:06:05 +02:00
Kenny Levinsen
483dbf76fa seatd-launch: Use optind to find the command
The command indexing had not been updated afer the introduction of
getopt, so combining a command with flags would fail.

Add error handling for if no command was specified while we're at it.
2021-09-12 12:06:05 +02:00
Jan Beich
d5c1a7811b seatd-launch: respect PATH when looking for command
$ seatd-launch sway -c /dev/null
Could not start target: No such file or directory
2021-09-12 12:06:05 +02:00
Kenny Levinsen
da59bea775 man: Add seatd-launch(1) to SEE ALSO of seatd(1) 2021-09-11 15:55:26 +02:00
Kenny Levinsen
3ad9164a89 Bump version to 0.6.0 2021-09-11 14:02:44 +02:00
Kenny Levinsen
d1c6bb9a15 seatd-launch: Fix chmod error goto 2021-09-08 20:54:18 +02:00
Kenny Levinsen
e7343ca96f man: Add simple seatd-launch(1) page 2021-09-08 20:45:42 +02:00
Kenny Levinsen
17cdbe0ad2 seatd-launch: Set socket permissions directly
Instead of relying on seatd's user/group arguments, which require
turning our UID back into a username, just chmod/chown the socket
ourselves once seatd is ready.

We also reduce the permissions to just user access, instead of user and
group like seatd specifies.
2021-09-08 20:40:09 +02:00
Kenny Levinsen
60c370d4ec seat: Allow new clients when active is pending ack
New clients could only be added to a VT bound seat if there were no
"active" client, regardless of its actual state. This meant that if one
switched from an "active" VT to an "inactive" VT, the seat would be
blocked while the "active" client was in CLIENT_PENDING_DISABLE, causing
new clients to possibly fail should the old client take its time with
the ack.

Instead, allow new clients to also be added if there is an active client
whose state is CLIENT_PENDING_DISABLE, and there is no client with the
new VT as its session ID.
2021-09-07 22:24:24 +02:00
Simon Ser
29a6832ca0 Add .editorconfig
Allows text editor auto-configuration.
2021-09-02 20:41:47 +02:00
Kenny Levinsen
df3f307b8e seat: Avoid holding a tty fd
The kernel Secure Attention Key killer, triggered by SysRq+k, kills all
processes that hold an fd referencing the tty.

To avoid its attention, we stop storing the fd for the currently active
VT in seat state. This has the added benefit of simplifying state a bit.
2021-09-02 20:02:55 +02:00
Fabrice Fontaine
15b0972bd3 meson.build: fix build with gcc < 7
Test if arguments (e.g. -Wimplicit-fallthrough) is available before
using it as -Wimplicit-fallthrough has been added only since gcc 7.1 and
81fea426da
and so it will raise the following build failure with gcc < 7:

arm-none-linux-gnueabi-gcc: error: unrecognized command line option '-Wimplicit-fallthrough'

Fixes:
 - http://autobuild.buildroot.org/results/0ee6816a7cceebdafd07612677a594bdf68e0790

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2021-09-02 20:00:03 +02:00
Kenny Levinsen
038c30f9b1 libseat: Fix build of builtin backend
This was regressed by 166feaea33 which
missed the builtin backend when changing struct libseat_seat_listener to
being passed around as const.
2021-08-15 14:32:05 +02:00
Greg Depoire--Ferrer
d78859bc9a libseat: Update builtin backend root requirement documentation
The builtin backend no longer requires root, setuid or CAP_SYS_ADMIN.
This commit updates the documentation accordingly.
2021-08-15 14:03:23 +02:00
Greg Depoire--Ferrer
d9ae4c3010 Revert "libseat: Check euid before using builtin"
This reverts commit 1ae6c3b3dd.

A user might want to run the builtin server as non root, if they have
permission to use the devices.

The check was originally copied from wlroots's direct backend. It was reverted
in fa05d3cde68d with a detailed explanation of why root priviledges are not
always necessary to use the DRM device.
2021-08-15 14:03:23 +02:00
Kenny Levinsen
6e7a1db32d logind: Remove redundant null check 2021-08-15 13:34:26 +02:00
Simon Ser
166feaea33 Make libseat_seat_listener const
libseat will never write to that struct. Let's allow callers to
make it read-only.
2021-08-15 13:33:30 +02:00
Kenny Levinsen
309650aa4d seatd: Use path in chmod/chown operations
c8b3a22d4e snuck in a change which
converts chown/chmod to fchown/fchmod using the socket fd. This appears
to succeed under Linux, but fails with EINVAL on FreeBSD. As the error
handling in this area was flawed, CI failed to catch the regression.

Partially revert c8b3a22d4e to fix the
regression on FreeBSD.
2021-08-08 18:22:17 +02:00
Kenny Levinsen
2cfc56d5ed seatd: Improve socket permission error handling
chmod/chown errors were logged, but did not result in failure opening
the seatd socket. This meant that errors would not get caught by CI.
2021-08-08 18:21:28 +02:00
Kenny Levinsen
48727a0b6b seatd-launch: Command line argument support 2021-08-06 23:00:05 +02:00
Simon Ser
369af8f9e4 seatd-launch: check for getpwuid errors 2021-08-06 22:27:42 +02:00
Simon Ser
3a843745c2 seatd-launch: don't use gotos in child processes
While forked (child pid is zero), don't use gotos. These will
execute atexit functions and potentially mess up the stdlib.
Instead, use _exit.
2021-08-06 22:27:42 +02:00
Simon Ser
f2a614dcd3 seatd-launch: propagate child exit status
When the child process exits with a non-zero code or is killed,
return with a non-zero code as well.
2021-08-06 22:27:42 +02:00
Kenny Levinsen
7d06b34ee2 ci: Fix meson flags 2021-08-06 01:29:52 +02:00
Kenny Levinsen
978dec42b0 ci: Use seatd-launch 2021-08-06 01:29:52 +02:00
Kenny Levinsen
1e98727ae9 seatd-launch: Add seatd launch wrapper
This launch wrapper is used to conveniently start a new seatd instance,
wait for it to be ready, and launch a target application.
2021-08-06 01:29:52 +02:00
Kenny Levinsen
c8b3a22d4e seatd: Only set UID/GID when specified
The UID/GID defaulted to 0, which results in trying to chown to root
when a UID or GID isn't requested. Instead, deafult to -1 so that the
unspecified values are left intact.
2021-08-06 01:15:55 +02:00
Kenny Levinsen
312d6906ae seatd: s6-style readiness notification support
This adds the ability to specify the number of an fd that is inherited
by the process as open. Once seatd is read to serve requests, it will
write a single newline and close the fd.
2021-08-06 01:15:55 +02:00
Kenny Levinsen
d03e9d1c35 seatd: We shouldn't poll if predispatch > 0
This condition was accidentally botched as part of
5923e0edc9
2021-08-06 01:15:55 +02:00
Kenny Levinsen
6444da6093 libseat: Rename dispatch_background in backends
This name never made much sense. dispatch_and_execute is more
meaningful, especially when compared to the non-executing dispatch
function.
2021-07-09 00:16:10 +02:00
Kenny Levinsen
5923e0edc9 libseat/seatd: Add dispatch_pending_and_execute
This handler returns the number of dispatched or executed events, or -1
if dispatch_pending failed.

This helper is used to clean up dispatch_background, which now ensures
that all events are executed before we read or poll the connection, and
have improved error handling in the corner case where the second
dispatch_pending failed.
2021-07-09 00:09:14 +02:00
Kenny Levinsen
7a6d12ff7a libseat/seatd: Return executed events
Dispatch needs to report if something has happened, which includes
events executed from the queue as these could have lead to additional
dispatch and queuing.
2021-07-08 23:48:30 +02:00
Simon Ser
2204db5531 build: add prefix to libseat options
The option names are a little bit confusing, because it's not clear
which ones toggle libseat features, and which ones toggle seatd
features.

Add a "libseat-" prefix to libseat-specific features, to make it
more obvious that they only are about the library.
2021-07-06 22:11:43 +02:00
Simeon Schaub
bff09d8859 link with rt
Since seatd uses `clock_gettime`, this is needed when cross-compiling.
This came up in https://github.com/JuliaPackaging/Yggdrasil/pull/3193.
2021-06-20 22:36:33 +02:00
Kenny Levinsen
fc97206df9 readme: Update discuss section 2021-05-19 17:48:12 +02:00
Kenny Levinsen
355cc9c944 meson: Support building builtin without seatd
The builtin backend relies on the seatd backend implementation. When
builtin was enabled without seatd, compilation would fail due to the
implementation not being included.

Include the implementation if either seatd or builtin is enabled.
2021-04-25 20:20:52 +02:00
Simon Ser
36f54adc2c libseat/seatd: downgrade ENOENT log to info
The socket is expected not to be found if seatd is not running.
In general other backends will be attempted after seatd. There is
already an error message in case no backend can be started.
2021-04-25 19:15:24 +02:00
Kenny Levinsen
5535c2c3b1 contrib/systemd: Use a different group
"video" was used for convenience in the example, but a dedicated group
is preferable so that a user does not gain the ability to bypass the
seat manager and open devices directly.
2021-04-17 17:09:45 +02:00
Simon Ser
81ff0a09a9 build: set pkgconfig/dependency variables for features
This allows libseat users to e.g. advise people to chmod a+s the
executable if libseat is built with the builtin backend.

While bumping the Meson version, adjust the scdoc logic to avoid
the following warnings:

    WARNING: Project targeting '>=0.56.0' but tried to use feature deprecated since '0.56.0': Dependency.get_pkgconfig_variable. use Dependency.get_variable(pkgconfig : ...) instead
    WARNING: Project targeting '>=0.56.0' but tried to use feature deprecated since '0.55.0': ExternalProgram.path. use ExternalProgram.full_path() instead
2021-04-10 15:16:40 +02:00
Simon Ser
5884a6003a build: disable logind on -Dauto_features=disabled -Dlogind=auto
Setting auto_features=disabled is supposed to disable all optional
dependencies. Since we aren't using a feature option here, we need
to manually add logic to disable logind in this case.
2021-04-10 15:16:26 +02:00
Simon Ser
753c5276cf build: don't allow "auto" for seatd, builtin, server and examples
These features don't have any dependencies, so "auto" doesn't make
sense.
2021-04-08 23:07:35 +02:00
Simon Ser
ee40913810 build: don't explicitly search for sh
This removes the "Program sh found" line in the build logs, and
should not change anything else.
2021-04-08 23:07:35 +02:00
Simon Ser
392da918e6 build: fix logind feature summary when auto-detected
If -Dlogind=auto but systemd/elogind isn't available,
logind_provider would get set to the last item of the foreach loop.
This would incorrectly report "systemd: YES".
2021-04-08 23:07:35 +02:00
Simon Ser
385cc0039d build: add explicit logind provider option, auto-detect by default
Allow package maintainers to explicitly select a logind provider
by passing -Dlogind=systemd or -Dlogind=elogind. In case both are
available (e.g. for distributions which support both), this makes
it possible to gte deterministic behavior.

By default, auto-detect the logind provider. That way, users which
have systemd or elogind installed get the backend built by default.
2021-04-08 23:07:35 +02:00
Kenny Levinsen
3ce4c57814 meson: make 'logind' var always available 2021-04-08 23:07:35 +02:00
Kenny Levinsen
50da164ddc ci: Use 'auto' for arch linux logind 2021-04-08 23:07:35 +02:00
Kenny Levinsen
f9ba8b57bc Avoid a clang-format quirk
clang-format wants to put the terminating NULLs on the same line as the
noop backend when it doens't have any immediate non-NULL neighbors.

Add a newline to stop it.
2021-03-26 11:34:13 +01:00
Simon Ser
fa2700126f meson: declare libseat dependency
This can be used by parent projects when seatd is a subproject:

    libseat = dependency('libseat', fallback: ['seatd', 'libseat'])
2021-03-26 11:21:05 +01:00
Simon Ser
9a7824b7c3 Add no-op session
This is useful for headless testing, for instance with VKMS:

    modprobe vkms
    export WLR_DRM_DEVICES=/dev/dri/card1
    export WLR_BACKENDS=drm
    export LIBSEAT_BACKEND=noop
    sway

We don't need any of the VT handling in this case.
2021-03-26 11:11:26 +01:00
Jan Beich
5ad91ae9da client: enable cr_pid on FreeBSD >= 12.3
https://cgit.freebsd.org/src/commit/?id=925f44f33862908f9a2e72520a17af148c7d0db5
https://cgit.freebsd.org/src/commit/?id=2b61bda2c75f30f6eadd18fb891fd885e4c8d19d
2021-03-21 16:27:21 +01:00
Kenny Levinsen
0d855a28f2 readme: Remove alpha label 2021-03-18 15:45:40 +01:00
Kenny Levinsen
34f55a3e24 contrib: Add Documentation to systemd unit 2021-03-16 14:27:45 +01:00
Kenny Levinsen
a9865adb5f ci: Remove unnecessary env vars 2021-03-16 13:36:26 +01:00
Kenny Levinsen
745d662920 ci: Reduce test runs from 5 to 2 2021-03-16 13:36:26 +01:00
Kenny Levinsen
1f457b1df8 meson: Disable examples by default 2021-03-16 13:36:26 +01:00
Kenny Levinsen
4e65e1bf47 Correct minor misspellings 2021-03-16 13:03:57 +01:00
Kenny Levinsen
75cb20e891 meson: Minor cleanup 2021-03-16 13:03:57 +01:00
Kenny Levinsen
e802d381a1 meson: Fix logind backend auto mode 2021-03-16 13:03:57 +01:00
36 changed files with 1130 additions and 416 deletions

View file

@ -10,18 +10,19 @@ sources:
- https://git.sr.ht/~kennylevinsen/seatd - https://git.sr.ht/~kennylevinsen/seatd
tasks: tasks:
- prepare: | - prepare: |
meson -Dseatd=enabled -Dbuiltin=enabled -Dlogind=disabled build seatd meson -Dlibseat-seatd=enabled -Dlibseat-builtin=enabled -Dlibseat-logind=disabled -Dexamples=enabled build seatd
- build: | - build: |
ninja -C build ninja -C build
sudo ninja -C build install
- unittest: | - unittest: |
ninja -C build test ninja -C build test
- scan-build: | - scan-build: |
ninja -C build scan-build ninja -C build scan-build
[ -z "$(ls -A build/meson-logs/scanbuild/ 2>/dev/null)" ] [ -z "$(ls -A build/meson-logs/scanbuild/ 2>/dev/null)" ]
- smoketest: | - smoketest: |
timeout -s KILL 30s ./seatd/.builds/smoketest-seatd.sh timeout -s KILL 30s sudo ./build/seatd-launch -l debug -- ./build/simpletest /dev/dri/card0
- smoketest-builtin: | - smoketest-builtin: |
timeout -s KILL 30s ./seatd/.builds/smoketest-builtin.sh timeout -s KILL 30s sudo LIBSEAT_BACKEND=builtin ./build/simpletest /dev/dri/card0
- check-format: | - check-format: |
ninja -C build clang-format ninja -C build clang-format
git -C seatd diff --exit-code git -C seatd diff --exit-code

View file

@ -9,15 +9,22 @@ sources:
- https://git.sr.ht/~kennylevinsen/seatd - https://git.sr.ht/~kennylevinsen/seatd
tasks: tasks:
- prepare: | - prepare: |
meson -Db_sanitize=address -Dlogind=enabled -Dseatd=enabled -Dbuiltin=enabled build seatd meson -Db_sanitize=address -Dlibseat-logind=auto -Dlibseat-seatd=enabled -Dlibseat-builtin=enabled -Dexamples=enabled build seatd
- build: | - build: |
ninja -C build ninja -C build
sudo ninja -C build install
- unittest: | - unittest: |
ninja -C build test ninja -C build test
- scan-build: | - scan-build: |
ninja -C build scan-build ninja -C build scan-build
[ -z "$(ls -A build/meson-logs/scanbuild/ 2>/dev/null)" ] [ -z "$(ls -A build/meson-logs/scanbuild/ 2>/dev/null)" ]
- smoketest: | - smoketest: |
timeout -s KILL 30s ./seatd/.builds/smoketest-seatd.sh timeout -s KILL 30s sudo ./build/seatd-launch -l debug -- ./build/simpletest /dev/input/event0
- smoketest-builtin: | - smoketest-builtin: |
timeout -s KILL 30s ./seatd/.builds/smoketest-builtin.sh timeout -s KILL 30s sudo LIBSEAT_BACKEND=builtin ./build/simpletest /dev/input/event0
- smoketest-logind: |
# Turn off systemd-logind and patch our session to be tied to seat0 on VT 6
sudo systemctl stop systemd-logind
echo -e "ACTIVE=$XDG_SESSION_ID\nACTIVE_UID=$UID\nSESSIONS=$XDG_SESSION_ID\nUIDS=$UID\n" | sudo tee -a /run/systemd/seats/seat0 > /dev/null
echo -e "SEAT=seat0\nVTNR=6\n" | sudo tee -a /run/systemd/sessions/$XDG_SESSION_ID > /dev/null
timeout -s KILL 30s sudo LIBSEAT_BACKEND=logind ./build/simpletest /dev/input/event0

View file

@ -5,15 +5,16 @@ sources:
- https://git.sr.ht/~kennylevinsen/seatd - https://git.sr.ht/~kennylevinsen/seatd
tasks: tasks:
- prepare: | - prepare: |
meson -Dseatd=enabled -Dbuiltin=enabled -Dlogind=disabled build seatd meson -Dlibseat-seatd=enabled -Dlibseat-builtin=enabled -Dlibseat-logind=disabled build seatd
- build: | - build: |
ninja -C build ninja -C build
- unittest: | - unittest: |
ninja -C build test ninja -C build test
- smoketest: | - smoketest: |
rm -rf build rm -rf build
meson -Db_lundef=false -Db_sanitize=address -Dseatd=enabled -Dbuiltin=enabled -Dlogind=disabled build seatd meson -Db_lundef=false -Db_sanitize=address -Dlibseat-seatd=enabled -Dlibseat-builtin=enabled -Dexamples=enabled -Dlibseat-logind=disabled build seatd
ninja -C build ninja -C build
timeout -s KILL 30s ./seatd/.builds/smoketest-seatd.sh sudo ninja -C build install
timeout -s KILL 30s sudo ./build/seatd-launch -l debug -- ./build/simpletest /dev/input/event0
- smoketest-builtin: | - smoketest-builtin: |
timeout -s KILL 30s ./seatd/.builds/smoketest-builtin.sh timeout -s KILL 30s sudo LIBSEAT_BACKEND=builtin ./build/simpletest /dev/input/event0

24
.builds/netbsd.yml Normal file
View file

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

View file

@ -1,30 +0,0 @@
#!/bin/sh
# Devices that exist on sr.ht
if [ -e "/dev/input/event0" ]
then
file="/dev/input/event0"
elif [ -e "/dev/dri/card0" ]
then
file="/dev/dri/card0"
else
echo "No useful device file found"
exit 1
fi
#
# Run simpletest a few times
#
cnt=0
while [ "$cnt" -lt 5 ]
do
echo "Simpletest run $cnt"
if ! sudo LIBSEAT_BACKEND=builtin LIBSEAT_LOGLEVEL=debug SEATD_SOCK=./seatd.sock ./build/simpletest $file
then
echo "Simpletest failed"
exit $res
fi
cnt=$((cnt+1))
done
echo "smoketest-builtin completed"

View file

@ -1,58 +0,0 @@
#!/bin/sh
#
# Start seatd
#
[ -f seatd.sock ] && sudo rm seatd.sock
sudo SEATD_LOGLEVEL=debug SEATD_SOCK=./seatd.sock ./build/seatd &
# seatd is started in the background, so wait for it to come alive
cnt=0
while ! [ -e ./seatd.sock ] && [ "$cnt" -lt 10 ]
do
sleep 0.1
cnt=$((cnt+1))
done
if ! [ -e ./seatd.sock ]
then
echo "seatd socket not found"
exit 1
fi
sudo chmod 777 ./seatd.sock
# Devices that exist on sr.ht
if [ -e "/dev/input/event0" ]
then
file="/dev/input/event0"
elif [ -e "/dev/dri/card0" ]
then
file="/dev/dri/card0"
else
echo "No useful device file found"
exit 1
fi
#
# Run simpletest a few times
#
cnt=0
while [ "$cnt" -lt 5 ]
do
echo "Simpletest run $cnt"
if ! LIBSEAT_LOGLEVEL=debug SEATD_SOCK=./seatd.sock ./build/simpletest $file
then
echo "Simpletest failed"
sudo killall seatd
exit 1
fi
cnt=$((cnt+1))
done
#
# Wait for it to shut down
#
sudo killall seatd 2>/dev/null
echo "smoketest-seatd completed"

View file

@ -2,7 +2,7 @@
IndentWidth: 8 IndentWidth: 8
TabWidth: 8 TabWidth: 8
ContinuationIndentWidth: 8 ContinuationIndentWidth: 8
UseTab: Always UseTab: ForContinuationAndIndentation
ColumnLimit: 100 ColumnLimit: 100
AlignConsecutiveMacros: true AlignConsecutiveMacros: true

8
.editorconfig Normal file
View file

@ -0,0 +1,8 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = tab
indent_size = 8

View file

@ -2,7 +2,7 @@
A minimal seat management daemon, and a universal seat management library. A minimal seat management daemon, and a universal seat management library.
Currently supports Linux and FreeBSD. Alpha Currently supports Linux and FreeBSD, and has experimental NetBSD support.
## What is seat management? ## What is seat management?
@ -12,7 +12,7 @@ Seat management takes care of mediating access to shared devices (graphics, inpu
### seatd ### seatd
A seat management deamon, that does everything it needs to do. Nothing more, nothing less. Depends only on libc. A seat management daemon, that does everything it needs to do. Nothing more, nothing less. Depends only on libc.
### libseat ### 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. Instead of giving user shell developers more work, libseat aims to make supporting seatd less work than what they're currently implementing. This is done by taking care of all the seat management needs with multiple backends, providing not only seatd support, but replacing the existing logind and direct seat management implementations.
## I want more ## How to discuss
Go to #kennylevinsen @ chat.freenode.net to discuss, or use [~kennylevinsen/seatd-devel@lists.sr.ht](https://lists.sr.ht/~kennylevinsen/seatd-devel). Go to [#kennylevinsen @ irc.libera.chat](ircs://irc.libera.chat/#kennylevinsen) to discuss, or use [~kennylevinsen/seatd-devel@lists.sr.ht](https://lists.sr.ht/~kennylevinsen/seatd-devel).

View file

@ -2,19 +2,16 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/types.h> #include <sys/types.h>
#if defined(__linux__)
#include <sys/sysmacros.h>
#endif
#include "drm.h" #include "drm.h"
// From libdrm // From libdrm
#define DRM_IOCTL_BASE 'd' #define DRM_IOCTL_BASE 'd'
#define DRM_IO(nr) _IO(DRM_IOCTL_BASE, nr) #define DRM_IO(nr) _IO(DRM_IOCTL_BASE, nr)
#define DRM_IOCTL_SET_MASTER DRM_IO(0x1e) #define DRM_IOCTL_SET_MASTER DRM_IO(0x1e)
#define DRM_IOCTL_DROP_MASTER DRM_IO(0x1f) #define DRM_IOCTL_DROP_MASTER DRM_IO(0x1f)
#define STRLEN(s) ((sizeof(s) / sizeof(s[0])) - 1) #define STRLEN(s) ((sizeof(s) / sizeof(s[0])) - 1)
#define STR_HAS_PREFIX(prefix, s) (strncmp(prefix, s, STRLEN(prefix)) == 0)
int drm_set_master(int fd) { int drm_set_master(int fd) {
return ioctl(fd, DRM_IOCTL_SET_MASTER, 0); return ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
@ -24,21 +21,20 @@ int drm_drop_master(int fd) {
return ioctl(fd, DRM_IOCTL_DROP_MASTER, 0); return ioctl(fd, DRM_IOCTL_DROP_MASTER, 0);
} }
#if defined(__linux__) #if defined(__linux__) || defined(__NetBSD__) || defined(__OpenBSD__)
int path_is_drm(const char *path) { int path_is_drm(const char *path) {
static const char prefix[] = "/dev/dri/"; if (STR_HAS_PREFIX("/dev/dri/", path))
static const int prefixlen = STRLEN(prefix); return 1;
return strncmp(prefix, path, prefixlen) == 0; return 0;
}
int dev_is_drm(dev_t device) {
return major(device) == 226;
} }
#elif defined(__FreeBSD__) #elif defined(__FreeBSD__)
int path_is_drm(const char *path) { int path_is_drm(const char *path) {
static const char prefix[] = "/dev/drm/"; if (STR_HAS_PREFIX("/dev/dri/", path))
static const int prefixlen = STRLEN(prefix); return 1;
return strncmp(prefix, path, prefixlen) == 0; /* Some drivers have /dev/dri/X symlinked to /dev/drm/X */
if (STR_HAS_PREFIX("/dev/drm/", path))
return 1;
return 0;
} }
#else #else
#error Unsupported platform #error Unsupported platform

View file

@ -9,14 +9,13 @@
#include <sys/sysmacros.h> #include <sys/sysmacros.h>
#elif defined(__FreeBSD__) #elif defined(__FreeBSD__)
#include <dev/evdev/input.h> #include <dev/evdev/input.h>
#else
#error Unsupported platform
#endif #endif
#include "evdev.h" #include "evdev.h"
#define STRLEN(s) ((sizeof(s) / sizeof(s[0])) - 1) #define STRLEN(s) ((sizeof(s) / sizeof(s[0])) - 1)
#if defined(__linux__) || defined(__FreeBSD__)
int path_is_evdev(const char *path) { int path_is_evdev(const char *path) {
static const char prefix[] = "/dev/input/event"; static const char prefix[] = "/dev/input/event";
static const size_t prefixlen = STRLEN(prefix); static const size_t prefixlen = STRLEN(prefix);
@ -26,9 +25,15 @@ int path_is_evdev(const char *path) {
int evdev_revoke(int fd) { int evdev_revoke(int fd) {
return ioctl(fd, EVIOCREVOKE, NULL); return ioctl(fd, EVIOCREVOKE, NULL);
} }
#elif defined(__NetBSD__) || defined(__OpenBSD__)
#if defined(__linux__) int path_is_evdev(const char *path) {
int dev_is_evdev(dev_t device) { (void)path;
return major(device) == INPUT_MAJOR; return 0;
} }
int evdev_revoke(int fd) {
(void)fd;
return 0;
}
#else
#error Unsupported platform
#endif #endif

View file

@ -13,14 +13,25 @@
#include <linux/vt.h> #include <linux/vt.h>
#define K_ENABLE K_UNICODE #define K_ENABLE K_UNICODE
#define K_DISABLE K_OFF #define K_DISABLE K_OFF
#define FRSIG 0 #define FRSIG 0
#elif defined(__FreeBSD__) #elif defined(__FreeBSD__)
#include <sys/consio.h> #include <sys/consio.h>
#include <sys/kbio.h> #include <sys/kbio.h>
#include <termios.h> #include <termios.h>
#define K_ENABLE K_XLATE #define K_ENABLE K_XLATE
#define K_DISABLE K_RAW #define K_DISABLE K_RAW
#define FRSIG SIGIO #define FRSIG SIGIO
#elif defined(__NetBSD__)
#include <dev/wscons/wsdisplay_usl_io.h>
#define K_ENABLE K_XLATE
#define K_DISABLE K_RAW
#define FRSIG 0 // unimplemented
#elif defined(__OpenBSD__)
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplay_usl_io.h>
#define K_ENABLE K_XLATE
#define K_DISABLE K_RAW
#define FRSIG SIGIO
#else #else
#error Unsupported platform #error Unsupported platform
#endif #endif
@ -134,16 +145,38 @@ static int get_tty_path(int tty, char path[static TTYPATHLEN]) {
} }
return 0; 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 #else
#error Unsupported platform #error Unsupported platform
#endif #endif
int terminal_open(int vt) { int terminal_open(int vt) {
char path[TTYPATHLEN]; char path[TTYPATHLEN];
log_debugf("terminal_open vt %d", vt);
#ifdef __OpenBSD__
if (vt > 0)
vt--;
#endif
if (get_tty_path(vt, path) == -1) { 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; return -1;
} }
log_debugf("terminal_open path %s", path);
int fd = open(path, O_RDWR | O_NOCTTY); int fd = open(path, O_RDWR | O_NOCTTY);
if (fd == -1) { if (fd == -1) {
log_errorf("Could not open target tty: %s", strerror(errno)); log_errorf("Could not open target tty: %s", strerror(errno));
@ -153,7 +186,7 @@ int terminal_open(int vt) {
} }
int terminal_current_vt(int fd) { int terminal_current_vt(int fd) {
#if defined(__linux__) #if defined(__linux__) || defined(__NetBSD__) || defined(__OpenBSD__)
struct vt_stat st; struct vt_stat st;
int res = ioctl(fd, VT_GETSTATE, &st); int res = ioctl(fd, VT_GETSTATE, &st);
close(fd); close(fd);
@ -231,11 +264,20 @@ int terminal_ack_acquire(int fd) {
int terminal_set_keyboard(int fd, bool enable) { int terminal_set_keyboard(int fd, bool enable) {
log_debugf("Setting KD keyboard state to %d", enable); log_debugf("Setting KD keyboard state to %d", enable);
#ifndef __OpenBSD1__
if (ioctl(fd, KDSKBMODE, enable ? K_ENABLE : K_DISABLE) == -1) { if (ioctl(fd, KDSKBMODE, enable ? K_ENABLE : K_DISABLE) == -1) {
log_errorf("Could not set KD keyboard mode to %s: %s", log_errorf("Could not set KD keyboard mode to %s: %s",
enable ? "enabled" : "disabled", strerror(errno)); enable ? "enabled" : "disabled", strerror(errno));
return -1; 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__) #if defined(__FreeBSD__)
struct termios tios; struct termios tios;
if (tcgetattr(fd, &tios) == -1) { if (tcgetattr(fd, &tios) == -1) {
@ -258,10 +300,19 @@ int terminal_set_keyboard(int fd, bool enable) {
int terminal_set_graphics(int fd, bool enable) { int terminal_set_graphics(int fd, bool enable) {
log_debugf("Setting KD graphics state to %d", enable); log_debugf("Setting KD graphics state to %d", enable);
#ifndef __OpenBSD1__
if (ioctl(fd, KDSETMODE, enable ? KD_GRAPHICS : KD_TEXT) == -1) { if (ioctl(fd, KDSETMODE, enable ? KD_GRAPHICS : KD_TEXT) == -1) {
log_errorf("Could not set KD graphics mode to %s: %s", enable ? "graphics" : "text", log_errorf("Could not set KD graphics mode to %s: %s", enable ? "graphics" : "text",
strerror(errno)); strerror(errno));
return -1; 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; return 0;
} }

27
common/wscons.c Normal file
View file

@ -0,0 +1,27 @@
#include <stdlib.h>
#include <string.h>
#if defined(__NetBSD__)
#include <stdlib.h>
#include <sys/stat.h>
#endif
#include "wscons.h"
#define STRLEN(s) ((sizeof(s) / sizeof(s[0])) - 1)
#if defined(__NetBSD__) || defined(__OpenBSD__)
int path_is_wscons(const char *path) {
static const char wskbd[] = "/dev/wskbd";
static const char wsmouse[] = "/dev/wsmouse";
static const char wsmux[] = "/dev/wsmux";
return strncmp(path, wskbd, STRLEN(wskbd)) == 0 ||
strncmp(path, wsmouse, STRLEN(wsmouse)) == 0 ||
strncmp(path, wsmux, STRLEN(wsmouse)) == 0;
}
#else
int path_is_wscons(const char *path) {
(void)path;
return 0;
}
#endif

View file

@ -1,9 +1,11 @@
[Unit] [Unit]
Description=Seat management daemon Description=Seat management daemon
Documentation=man:seatd(1)
[Service] [Service]
Type=simple Type=simple
ExecStart=seatd -g video # Specify the group you'd like to grant access to seatd
ExecStart=seatd -g seat
Restart=always Restart=always
RestartSec=1 RestartSec=1

View file

@ -43,7 +43,11 @@ int main(int argc, char *argv[]) {
while (active == 0) { while (active == 0) {
fprintf(stderr, "waiting for activation...\n"); fprintf(stderr, "waiting for activation...\n");
libseat_dispatch(backend, -1); if (libseat_dispatch(backend, -1) == -1) {
libseat_close_seat(backend);
fprintf(stderr, "libseat_dispatch() failed: %s\n", strerror(errno));
return -1;
}
} }
fprintf(stderr, "active!\n"); fprintf(stderr, "active!\n");

View file

@ -16,7 +16,7 @@ struct named_backend {
}; };
struct seat_impl { struct seat_impl {
struct libseat *(*open_seat)(struct libseat_seat_listener *listener, void *data); struct libseat *(*open_seat)(const struct libseat_seat_listener *listener, void *data);
int (*disable_seat)(struct libseat *seat); int (*disable_seat)(struct libseat *seat);
int (*close_seat)(struct libseat *seat); int (*close_seat)(struct libseat *seat);
const char *(*seat_name)(struct libseat *seat); const char *(*seat_name)(struct libseat *seat);

View file

@ -5,9 +5,4 @@ int drm_set_master(int fd);
int drm_drop_master(int fd); int drm_drop_master(int fd);
int path_is_drm(const char *path); int path_is_drm(const char *path);
#if defined(__linux__)
#include <sys/types.h>
int dev_is_drm(dev_t device);
#endif
#endif #endif

View file

@ -4,9 +4,4 @@
int evdev_revoke(int fd); int evdev_revoke(int fd);
int path_is_evdev(const char *path); int path_is_evdev(const char *path);
#if defined(__linux__)
#include <sys/types.h>
int dev_is_evdev(dev_t device);
#endif
#endif #endif

View file

@ -1,6 +1,8 @@
#ifndef _LIBSEAT_H #ifndef _LIBSEAT_H
#define _LIBSEAT_H #define _LIBSEAT_H
#include <stdarg.h>
/* /*
* An opaque struct containing an opened seat, created by libseat_open_seat and * An opaque struct containing an opened seat, created by libseat_open_seat and
* destroyed by libseat_close_seat. * destroyed by libseat_close_seat.
@ -45,15 +47,17 @@ struct libseat_seat_listener {
* The available backends, if enabled at compile-time, are: seatd, logind and * The available backends, if enabled at compile-time, are: seatd, logind and
* builtin. * builtin.
* *
* To use builtin, the process must have CAP_SYS_ADMIN or be root at the time * To use builtin, the process must have permission to open and use the seat's
* of the call. These privileges can be dropped at any point after the call. * devices at the time of the call. In the case of DRM devices, this includes
* permission for drmSetMaster(3). These privileges can be dropped at any
* point after the call.
* *
* The returned pointer must be destroyed with libseat_close_seat. * The returned pointer must be destroyed with libseat_close_seat.
* *
* Returns a pointer to an opaque libseat struct on success. Returns NULL and * Returns a pointer to an opaque libseat struct on success. Returns NULL and
* sets errno on error. * sets errno on error.
*/ */
struct libseat *libseat_open_seat(struct libseat_seat_listener *listener, void *userdata); struct libseat *libseat_open_seat(const struct libseat_seat_listener *listener, void *userdata);
/* /*
* Disables a seat, used in response to a disable_seat event. After disabling * Disables a seat, used in response to a disable_seat event. After disabling

View file

@ -1,10 +1,10 @@
#ifndef _SEATD_CONSTANTS_H #ifndef _SEATD_CONSTANTS_H
#define _SEATD_CONSTANTS_H #define _SEATD_CONSTANTS_H
#define MAX_PATH_LEN 256 #define MAX_PATH_LEN 256
#define MAX_SEAT_LEN 64 #define MAX_SEAT_LEN 64
#define MAX_SEAT_DEVICES 128 #define MAX_SEAT_DEVICES 128
#define MAX_SESSION_LEN 64 #define MAX_SESSION_LEN 64
#define CLIENT_EVENT(opcode) (opcode) #define CLIENT_EVENT(opcode) (opcode)
#define SERVER_EVENT(opcode) ((opcode) + (1 << 15)) #define SERVER_EVENT(opcode) ((opcode) + (1 << 15))
@ -15,6 +15,7 @@
#define CLIENT_CLOSE_DEVICE CLIENT_EVENT(4) #define CLIENT_CLOSE_DEVICE CLIENT_EVENT(4)
#define CLIENT_DISABLE_SEAT CLIENT_EVENT(5) #define CLIENT_DISABLE_SEAT CLIENT_EVENT(5)
#define CLIENT_SWITCH_SESSION CLIENT_EVENT(6) #define CLIENT_SWITCH_SESSION CLIENT_EVENT(6)
#define CLIENT_PING CLIENT_EVENT(7)
#define SERVER_SEAT_OPENED SERVER_EVENT(1) #define SERVER_SEAT_OPENED SERVER_EVENT(1)
#define SERVER_SEAT_CLOSED SERVER_EVENT(2) #define SERVER_SEAT_CLOSED SERVER_EVENT(2)
@ -22,7 +23,8 @@
#define SERVER_DEVICE_CLOSED SERVER_EVENT(4) #define SERVER_DEVICE_CLOSED SERVER_EVENT(4)
#define SERVER_DISABLE_SEAT SERVER_EVENT(5) #define SERVER_DISABLE_SEAT SERVER_EVENT(5)
#define SERVER_ENABLE_SEAT SERVER_EVENT(6) #define SERVER_ENABLE_SEAT SERVER_EVENT(6)
#define SERVER_ERROR SERVER_EVENT(0x7FFF) #define SERVER_PONG SERVER_EVENT(7)
#define SERVER_ERROR SERVER_EVENT(0x7FFF)
#include <stdint.h> #include <stdint.h>

View file

@ -13,6 +13,7 @@ enum seat_device_type {
SEAT_DEVICE_TYPE_NORMAL, SEAT_DEVICE_TYPE_NORMAL,
SEAT_DEVICE_TYPE_EVDEV, SEAT_DEVICE_TYPE_EVDEV,
SEAT_DEVICE_TYPE_DRM, SEAT_DEVICE_TYPE_DRM,
SEAT_DEVICE_TYPE_WSCONS,
}; };
struct seat_device { struct seat_device {
@ -33,7 +34,6 @@ struct seat {
struct client *next_client; struct client *next_client;
bool vt_bound; bool vt_bound;
int cur_ttyfd;
int cur_vt; int cur_vt;
int session_cnt; int session_cnt;
}; };

6
include/wscons.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef _SEATD_WSCONS_H
#define _SEATD_WSCONS_H
int path_is_wscons(const char *path);
#endif

View file

@ -13,10 +13,10 @@
#include <sys/un.h> #include <sys/un.h>
#include <unistd.h> #include <unistd.h>
#if defined(HAVE_ELOGIND) #if defined(HAVE_LIBELOGIND)
#include <elogind/sd-bus.h> #include <elogind/sd-bus.h>
#include <elogind/sd-login.h> #include <elogind/sd-login.h>
#elif defined(HAVE_SYSTEMD) #elif defined(HAVE_LIBSYSTEMD)
#include <systemd/sd-bus.h> #include <systemd/sd-bus.h>
#include <systemd/sd-login.h> #include <systemd/sd-login.h>
#else #else
@ -28,9 +28,17 @@
#include "libseat.h" #include "libseat.h"
#include "log.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 backend_logind {
struct libseat base; struct libseat base;
struct libseat_seat_listener *seat_listener; const struct libseat_seat_listener *seat_listener;
void *seat_listener_data; void *seat_listener_data;
sd_bus *bus; sd_bus *bus;
@ -67,6 +75,47 @@ static int close_seat(struct libseat *base) {
return 0; 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) { static int open_device(struct libseat *base, const char *path, int *fd) {
struct backend_logind *session = backend_logind_from_libseat_backend(base); struct backend_logind *session = backend_logind_from_libseat_backend(base);
@ -113,9 +162,11 @@ static int open_device(struct libseat *base, const char *path, int *fd) {
} }
*fd = tmpfd; *fd = tmpfd;
out: out:
sd_bus_error_free(&error); sd_bus_error_free(&error);
sd_bus_message_unref(msg); sd_bus_message_unref(msg);
check_pending_events(session);
return tmpfd; return tmpfd;
} }
@ -150,7 +201,7 @@ static int close_device(struct libseat *base, int device_id) {
sd_bus_error_free(&error); sd_bus_error_free(&error);
sd_bus_message_unref(msg); sd_bus_message_unref(msg);
check_pending_events(session);
return ret < 0 ? -1 : 0; return ret < 0 ? -1 : 0;
} }
@ -173,6 +224,7 @@ static int switch_session(struct libseat *base, int s) {
sd_bus_error_free(&error); sd_bus_error_free(&error);
sd_bus_message_unref(msg); sd_bus_message_unref(msg);
check_pending_events(session);
return ret < 0 ? -1 : 0; return ret < 0 ? -1 : 0;
} }
@ -213,7 +265,7 @@ static int poll_connection(struct backend_logind *backend, int timeout) {
return 0; return 0;
} }
static int dispatch_background(struct libseat *base, int timeout) { static int dispatch_and_execute(struct libseat *base, int timeout) {
struct backend_logind *backend = backend_logind_from_libseat_backend(base); struct backend_logind *backend = backend_logind_from_libseat_backend(base);
if (backend->initial_setup) { if (backend->initial_setup) {
backend->initial_setup = false; backend->initial_setup = false;
@ -240,15 +292,12 @@ static int dispatch_background(struct libseat *base, int timeout) {
total_dispatched += dispatched; total_dispatched += dispatched;
} }
} }
check_pending_events(backend);
return total_dispatched; return total_dispatched;
} }
static const char *seat_name(struct libseat *base) { static const char *seat_name(struct libseat *base) {
struct backend_logind *backend = backend_logind_from_libseat_backend(base); struct backend_logind *backend = backend_logind_from_libseat_backend(base);
if (backend->seat == NULL) {
return NULL;
}
return backend->seat; return backend->seat;
} }
@ -257,10 +306,11 @@ static struct backend_logind *backend_logind_from_libseat_backend(struct libseat
return (struct backend_logind *)base; return (struct backend_logind *)base;
} }
static bool session_activate(struct backend_logind *session) { static int session_activate(struct backend_logind *session) {
sd_bus_message *msg = NULL; sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_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, int ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", session->path,
"org.freedesktop.login1.Session", "Activate", &error, &msg, ""); "org.freedesktop.login1.Session", "Activate", &error, &msg, "");
if (ret < 0) { if (ret < 0) {
@ -269,10 +319,26 @@ static bool session_activate(struct backend_logind *session) {
sd_bus_error_free(&error); sd_bus_error_free(&error);
sd_bus_message_unref(msg); sd_bus_message_unref(msg);
return ret >= 0; return ret;
} }
static bool take_control(struct backend_logind *session) { static int session_check_active(struct backend_logind *session) {
sd_bus_error error = SD_BUS_ERROR_NULL;
int active = 0;
int ret = sd_bus_get_property_trivial(session->bus, "org.freedesktop.login1", session->path,
"org.freedesktop.login1.Session", "Active", &error,
'b', &active);
if (ret < 0) {
log_errorf("Could not check if session is active: %s", error.message);
} else {
session->active = (bool)active;
}
sd_bus_error_free(&error);
return ret;
}
static int take_control(struct backend_logind *session) {
sd_bus_message *msg = NULL; sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus_error error = SD_BUS_ERROR_NULL;
@ -285,7 +351,7 @@ static bool take_control(struct backend_logind *session) {
sd_bus_error_free(&error); sd_bus_error_free(&error);
sd_bus_message_unref(msg); sd_bus_message_unref(msg);
return ret >= 0; return ret;
} }
static void release_control(struct backend_logind *session) { static void release_control(struct backend_logind *session) {
@ -329,7 +395,7 @@ static int pause_device(sd_bus_message *msg, void *userdata, sd_bus_error *ret_e
return 0; return 0;
} }
if (dev_is_drm(makedev(major, minor)) && strcmp(type, "gone") != 0) { if (dev_major_is_drm(major) && strcmp(type, "gone") != 0) {
log_debugf("DRM device paused: %s", type); log_debugf("DRM device paused: %s", type);
assert(session->has_drm > 0); assert(session->has_drm > 0);
set_active(session, false); set_active(session, false);
@ -361,7 +427,7 @@ static int resume_device(sd_bus_message *msg, void *userdata, sd_bus_error *ret_
return 0; return 0;
} }
if (dev_is_drm(makedev(major, minor))) { if (dev_major_is_drm(major)) {
log_debug("DRM device resumed"); log_debug("DRM device resumed");
assert(session->has_drm > 0); assert(session->has_drm > 0);
set_active(session, true); set_active(session, true);
@ -471,7 +537,7 @@ error:
return 0; return 0;
} }
static bool add_signal_matches(struct backend_logind *backend) { static int add_signal_matches(struct backend_logind *backend) {
static const char *logind = "org.freedesktop.login1"; static const char *logind = "org.freedesktop.login1";
static const char *session_interface = "org.freedesktop.login1.Session"; static const char *session_interface = "org.freedesktop.login1.Session";
static const char *property_interface = "org.freedesktop.DBus.Properties"; static const char *property_interface = "org.freedesktop.DBus.Properties";
@ -481,34 +547,34 @@ static bool add_signal_matches(struct backend_logind *backend) {
"PauseDevice", pause_device, backend); "PauseDevice", pause_device, backend);
if (ret < 0) { if (ret < 0) {
log_errorf("Could not add D-Bus match: %s", strerror(-ret)); log_errorf("Could not add D-Bus match: %s", strerror(-ret));
return false; return ret;
} }
ret = sd_bus_match_signal(backend->bus, NULL, logind, backend->path, session_interface, ret = sd_bus_match_signal(backend->bus, NULL, logind, backend->path, session_interface,
"ResumeDevice", resume_device, backend); "ResumeDevice", resume_device, backend);
if (ret < 0) { if (ret < 0) {
log_errorf("Could not add D-Bus match: %s", strerror(-ret)); log_errorf("Could not add D-Bus match: %s", strerror(-ret));
return false; return ret;
} }
ret = sd_bus_match_signal(backend->bus, NULL, logind, backend->path, property_interface, ret = sd_bus_match_signal(backend->bus, NULL, logind, backend->path, property_interface,
"PropertiesChanged", properties_changed, backend); "PropertiesChanged", properties_changed, backend);
if (ret < 0) { if (ret < 0) {
log_errorf("Could not add D-Bus match: %s", strerror(-ret)); log_errorf("Could not add D-Bus match: %s", strerror(-ret));
return false; return ret;
} }
ret = sd_bus_match_signal(backend->bus, NULL, logind, backend->seat_path, property_interface, ret = sd_bus_match_signal(backend->bus, NULL, logind, backend->seat_path, property_interface,
"PropertiesChanged", properties_changed, backend); "PropertiesChanged", properties_changed, backend);
if (ret < 0) { if (ret < 0) {
log_errorf("Could not add D-Bus match: %s", strerror(-ret)); log_errorf("Could not add D-Bus match: %s", strerror(-ret));
return false; return ret;
} }
return true; return 0;
} }
static bool find_session_path(struct backend_logind *session) { static int find_session_path(struct backend_logind *session) {
int ret; int ret;
sd_bus_message *msg = NULL; sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus_error error = SD_BUS_ERROR_NULL;
@ -533,10 +599,10 @@ out:
sd_bus_error_free(&error); sd_bus_error_free(&error);
sd_bus_message_unref(msg); sd_bus_message_unref(msg);
return ret >= 0; return ret;
} }
static bool find_seat_path(struct backend_logind *session) { static int find_seat_path(struct backend_logind *session) {
int ret; int ret;
sd_bus_message *msg = NULL; sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus_error error = SD_BUS_ERROR_NULL;
@ -561,10 +627,10 @@ out:
sd_bus_error_free(&error); sd_bus_error_free(&error);
sd_bus_message_unref(msg); sd_bus_message_unref(msg);
return ret >= 0; return ret;
} }
static bool get_display_session(char **session_id) { static int get_display_session(char **session_id) {
assert(session_id != NULL); assert(session_id != NULL);
char *xdg_session_id = getenv("XDG_SESSION_ID"); char *xdg_session_id = getenv("XDG_SESSION_ID");
int ret; int ret;
@ -597,12 +663,12 @@ static bool get_display_session(char **session_id) {
success: success:
assert(*session_id != NULL); assert(*session_id != NULL);
return true; return 0;
error: error:
free(*session_id); free(*session_id);
*session_id = NULL; *session_id = NULL;
return false; return ret;
} }
static int set_type(struct backend_logind *backend, const char *type) { static int set_type(struct backend_logind *backend, const char *type) {
@ -621,17 +687,19 @@ static int set_type(struct backend_logind *backend, const char *type) {
return ret; return ret;
} }
static struct libseat *logind_open_seat(struct libseat_seat_listener *listener, void *data) { static struct libseat *logind_open_seat(const struct libseat_seat_listener *listener, void *data) {
struct backend_logind *backend = calloc(1, sizeof(struct backend_logind)); struct backend_logind *backend = calloc(1, sizeof(struct backend_logind));
if (backend == NULL) { if (backend == NULL) {
return NULL; return NULL;
} }
if (!get_display_session(&backend->id)) { int ret;
ret = get_display_session(&backend->id);
if (ret < 0) {
goto error; goto error;
} }
int ret = sd_session_get_seat(backend->id, &backend->seat); ret = sd_session_get_seat(backend->id, &backend->seat);
if (ret < 0) { if (ret < 0) {
goto error; goto error;
} }
@ -641,23 +709,33 @@ static struct libseat *logind_open_seat(struct libseat_seat_listener *listener,
goto error; goto error;
} }
if (!find_session_path(backend)) { ret = find_session_path(backend);
if (ret < 0) {
goto error; goto error;
} }
if (!find_seat_path(backend)) { ret = find_seat_path(backend);
if (ret < 0) {
goto error; goto error;
} }
if (!add_signal_matches(backend)) { ret = add_signal_matches(backend);
if (ret < 0) {
goto error; goto error;
} }
if (!session_activate(backend)) { ret = session_activate(backend);
if (ret < 0) {
goto error; goto error;
} }
if (!take_control(backend)) { ret = session_check_active(backend);
if (ret < 0) {
goto error;
}
ret = take_control(backend);
if (ret < 0) {
goto error; goto error;
} }
@ -667,15 +745,16 @@ static struct libseat *logind_open_seat(struct libseat_seat_listener *listener,
} }
backend->initial_setup = true; backend->initial_setup = true;
backend->active = true;
backend->seat_listener = listener; backend->seat_listener = listener;
backend->seat_listener_data = data; backend->seat_listener_data = data;
backend->base.impl = &logind_impl; backend->base.impl = &logind_impl;
check_pending_events(backend);
return &backend->base; return &backend->base;
error: error:
destroy(backend); destroy(backend);
errno = -ret;
return NULL; return NULL;
} }
@ -688,5 +767,5 @@ const struct seat_impl logind_impl = {
.close_device = close_device, .close_device = close_device,
.switch_session = switch_session, .switch_session = switch_session,
.get_fd = get_fd, .get_fd = get_fd,
.dispatch = dispatch_background, .dispatch = dispatch_and_execute,
}; };

136
libseat/backend/noop.c Normal file
View file

@ -0,0 +1,136 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "backend.h"
#include "log.h"
struct backend_noop {
struct libseat base;
const struct libseat_seat_listener *seat_listener;
void *seat_listener_data;
bool initial_setup;
int sockets[2];
};
extern const struct seat_impl noop_impl;
static struct backend_noop *backend_noop_from_libseat_backend(struct libseat *base) {
assert(base->impl == &noop_impl);
return (struct backend_noop *)base;
}
static void destroy(struct backend_noop *backend) {
close(backend->sockets[0]);
close(backend->sockets[1]);
free(backend);
}
static int close_seat(struct libseat *base) {
struct backend_noop *backend = backend_noop_from_libseat_backend(base);
destroy(backend);
return 0;
}
static int disable_seat(struct libseat *base) {
(void)base;
return 0;
}
static const char *seat_name(struct libseat *base) {
(void)base;
return "seat0";
}
static int open_device(struct libseat *base, const char *path, int *fd) {
(void)base;
int tmpfd = open(path, O_RDWR | O_NOCTTY | O_NOFOLLOW | O_CLOEXEC | O_NONBLOCK);
if (tmpfd < 0) {
log_errorf("Failed to open device: %s", strerror(errno));
return -1;
}
*fd = tmpfd;
return tmpfd;
}
static int close_device(struct libseat *base, int device_id) {
(void)base;
(void)device_id;
return 0;
}
static int switch_session(struct libseat *base, int s) {
(void)base;
(void)s;
log_errorf("No-op backend cannot switch to session %d", s);
return -1;
}
static int get_fd(struct libseat *base) {
struct backend_noop *backend = backend_noop_from_libseat_backend(base);
return backend->sockets[0];
}
static int dispatch_background(struct libseat *base, int timeout) {
struct backend_noop *backend = backend_noop_from_libseat_backend(base);
if (backend->initial_setup) {
backend->initial_setup = false;
backend->seat_listener->enable_seat(&backend->base, backend->seat_listener_data);
}
struct pollfd fd = {
.fd = backend->sockets[0],
.events = POLLIN,
};
if (poll(&fd, 1, timeout) < 0) {
if (errno == EAGAIN || errno == EINTR) {
return 0;
} else {
return -1;
}
}
return 0;
}
static struct libseat *noop_open_seat(const struct libseat_seat_listener *listener, void *data) {
struct backend_noop *backend = calloc(1, sizeof(struct backend_noop));
if (backend == NULL) {
return NULL;
}
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, backend->sockets) != 0) {
log_errorf("socketpair() failed: %s", strerror(errno));
free(backend);
return NULL;
}
backend->initial_setup = true;
backend->seat_listener = listener;
backend->seat_listener_data = data;
backend->base.impl = &noop_impl;
return &backend->base;
}
const struct seat_impl noop_impl = {
.open_seat = noop_open_seat,
.disable_seat = disable_seat,
.close_seat = close_seat,
.seat_name = seat_name,
.open_device = open_device,
.close_device = close_device,
.switch_session = switch_session,
.get_fd = get_fd,
.dispatch = dispatch_background,
};

View file

@ -33,40 +33,25 @@ struct pending_event {
struct backend_seatd { struct backend_seatd {
struct libseat base; struct libseat base;
struct connection connection; struct connection connection;
struct libseat_seat_listener *seat_listener; const struct libseat_seat_listener *seat_listener;
void *seat_listener_data; void *seat_listener_data;
struct linked_list pending_events; struct linked_list pending_events;
bool awaiting_pong;
bool error; bool error;
char seat_name[MAX_SEAT_LEN]; 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) { static int seatd_connect(void) {
union { union {
struct sockaddr_un unix; struct sockaddr_un unix;
struct sockaddr generic; struct sockaddr generic;
} addr = {{0}}; } addr = {{0}};
int fd = socket(AF_UNIX, SOCK_STREAM, 0); int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (fd == -1) { if (fd == -1) {
log_errorf("Could not create socket: %s", strerror(errno)); log_errorf("Could not create socket: %s", strerror(errno));
return -1; return -1;
} }
if (set_nonblock(fd) == -1) {
log_errorf("Could not make socket non-blocking: %s", strerror(errno));
close(fd);
return -1;
}
const char *path = getenv("SEATD_SOCK"); const char *path = getenv("SEATD_SOCK");
if (path == NULL) { if (path == NULL) {
path = SEATD_DEFAULTPATH; path = SEATD_DEFAULTPATH;
@ -75,7 +60,11 @@ static int seatd_connect(void) {
strncpy(addr.unix.sun_path, path, sizeof addr.unix.sun_path - 1); 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); socklen_t size = offsetof(struct sockaddr_un, sun_path) + strlen(addr.unix.sun_path);
if (connect(fd, &addr.generic, size) == -1) { if (connect(fd, &addr.generic, size) == -1) {
log_errorf("Could not connect to socket %s: %s", path, strerror(errno)); 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); close(fd);
return -1; return -1;
}; };
@ -202,10 +191,11 @@ static int queue_event(struct backend_seatd *backend, int opcode) {
return 0; return 0;
} }
static void execute_events(struct backend_seatd *backend) { static int execute_events(struct backend_seatd *backend) {
struct linked_list list; struct linked_list list;
linked_list_init(&list); linked_list_init(&list);
linked_list_take(&list, &backend->pending_events); linked_list_take(&list, &backend->pending_events);
int executed = 0;
while (!linked_list_empty(&list)) { while (!linked_list_empty(&list)) {
struct pending_event *ev = (struct pending_event *)list.next; struct pending_event *ev = (struct pending_event *)list.next;
int opcode = ev->opcode; int opcode = ev->opcode;
@ -227,7 +217,9 @@ static void execute_events(struct backend_seatd *backend) {
log_errorf("Invalid opcode: %d", opcode); log_errorf("Invalid opcode: %d", opcode);
abort(); abort();
} }
executed++;
} }
return executed;
} }
static int dispatch_pending(struct backend_seatd *backend, int *opcode) { static int dispatch_pending(struct backend_seatd *backend, int *opcode) {
@ -236,6 +228,12 @@ static int dispatch_pending(struct backend_seatd *backend, int *opcode) {
while (connection_get(&backend->connection, &header, sizeof header) != -1) { while (connection_get(&backend->connection, &header, sizeof header) != -1) {
packets++; packets++;
switch (header.opcode) { 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_DISABLE_SEAT:
case SERVER_ENABLE_SEAT: case SERVER_ENABLE_SEAT:
if (queue_event(backend, header.opcode) == -1) { if (queue_event(backend, header.opcode) == -1) {
@ -255,6 +253,15 @@ static int dispatch_pending(struct backend_seatd *backend, int *opcode) {
return packets; 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) { static int poll_connection(struct backend_seatd *backend, int timeout) {
struct pollfd fd = { struct pollfd fd = {
.fd = backend->connection.fd, .fd = backend->connection.fd,
@ -310,38 +317,44 @@ static int get_fd(struct libseat *base) {
return backend->connection.fd; return backend->connection.fd;
} }
static int dispatch_background(struct libseat *base, int timeout) { static int dispatch_and_execute(struct libseat *base, int timeout) {
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base); struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
if (backend->error) { if (backend->error) {
errno = ENOTCONN; errno = ENOTCONN;
return -1; return -1;
} }
int dispatched = dispatch_pending(backend, NULL); int predispatch = dispatch_pending_and_execute(backend);
if (dispatched > 0) { if (predispatch == -1) {
// We don't want to block if we dispatched something, as the return -1;
// caller might be waiting for the result. However, we'd also
// like to read anything pending.
timeout = 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.
int read = 0; int read = 0;
if (timeout == 0) { if (predispatch > 0 || timeout == 0) {
read = connection_read(&backend->connection); read = connection_read(&backend->connection);
} else { } else {
read = poll_connection(backend, timeout); read = poll_connection(backend, timeout);
} }
if (read > 0) {
dispatched += dispatch_pending(backend, NULL); if (read == 0) {
return predispatch;
} else if (read == -1 && errno != EAGAIN) { } else if (read == -1 && errno != EAGAIN) {
log_errorf("Could not read from connection: %s", strerror(errno)); log_errorf("Could not read from connection: %s", strerror(errno));
return -1; return -1;
} }
execute_events(backend); int postdispatch = dispatch_pending_and_execute(backend);
return dispatched; if (postdispatch == -1) {
return -1;
}
return predispatch + postdispatch;
} }
static struct libseat *_open_seat(struct libseat_seat_listener *listener, void *data, int fd) { static struct libseat *_open_seat(const struct libseat_seat_listener *listener, void *data, int fd) {
assert(listener != NULL); assert(listener != NULL);
assert(listener->enable_seat != NULL && listener->disable_seat != NULL); assert(listener->enable_seat != NULL && listener->disable_seat != NULL);
struct backend_seatd *backend = calloc(1, sizeof(struct backend_seatd)); struct backend_seatd *backend = calloc(1, sizeof(struct backend_seatd));
@ -389,7 +402,7 @@ alloc_error:
return NULL; return NULL;
} }
static struct libseat *open_seat(struct libseat_seat_listener *listener, void *data) { static struct libseat *open_seat(const struct libseat_seat_listener *listener, void *data) {
int fd = seatd_connect(); int fd = seatd_connect();
if (fd == -1) { if (fd == -1) {
return NULL; return NULL;
@ -428,6 +441,36 @@ static const char *seat_name(struct libseat *base) {
return backend->seat_name; 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) { static int open_device(struct libseat *base, const char *path, int *fd) {
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base); struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
if (backend->error) { if (backend->error) {
@ -459,11 +502,11 @@ static int open_device(struct libseat *base, const char *path, int *fd) {
goto error; goto error;
} }
execute_events(backend); check_pending_events(backend);
return rmsg.device_id; return rmsg.device_id;
error: error:
execute_events(backend); check_pending_events(backend);
return -1; return -1;
} }
@ -494,11 +537,11 @@ static int close_device(struct libseat *base, int device_id) {
goto error; goto error;
} }
execute_events(backend); check_pending_events(backend);
return 0; return 0;
error: error:
execute_events(backend); check_pending_events(backend);
return -1; return -1;
} }
@ -553,42 +596,18 @@ const struct seat_impl seatd_impl = {
.close_device = close_device, .close_device = close_device,
.switch_session = switch_session, .switch_session = switch_session,
.get_fd = get_fd, .get_fd = get_fd,
.dispatch = dispatch_background, .dispatch = dispatch_and_execute,
}; };
#ifdef BUILTIN_ENABLED #ifdef BUILTIN_ENABLED
#include <signal.h>
static int set_deathsig(int signal); static struct libseat *builtin_open_seat(const struct libseat_seat_listener *listener, void *data) {
#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]; int fds[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) { if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, fds) == -1) {
log_errorf("Could not create socket pair: %s", strerror(errno)); log_errorf("Could not create socket pair: %s", strerror(errno));
return NULL; return NULL;
} }
if (geteuid() != 0) {
log_error("Built-in seatd instance requires root privileges");
return NULL;
}
pid_t pid = fork(); pid_t pid = fork();
if (pid == -1) { if (pid == -1) {
log_errorf("Could not fork: %s", strerror(errno)); log_errorf("Could not fork: %s", strerror(errno));
@ -596,6 +615,7 @@ static struct libseat *builtin_open_seat(struct libseat_seat_listener *listener,
close(fds[1]); close(fds[1]);
return NULL; return NULL;
} else if (pid == 0) { } else if (pid == 0) {
close(fds[1]);
int fd = fds[0]; int fd = fds[0];
int res = 0; int res = 0;
struct server server = {0}; struct server server = {0};
@ -610,7 +630,7 @@ static struct libseat *builtin_open_seat(struct libseat_seat_listener *listener,
res = 1; res = 1;
goto server_error; goto server_error;
} }
set_deathsig(SIGTERM); log_info("Started embedded seatd");
while (server.running) { while (server.running) {
if (poller_poll(&server.poller) == -1) { if (poller_poll(&server.poller) == -1) {
log_errorf("Could not poll server socket: %s", strerror(errno)); log_errorf("Could not poll server socket: %s", strerror(errno));
@ -622,8 +642,10 @@ static struct libseat *builtin_open_seat(struct libseat_seat_listener *listener,
server_finish(&server); server_finish(&server);
error: error:
close(fd); close(fd);
log_info("Stopped embedded seatd");
exit(res); exit(res);
} else { } else {
close(fds[0]);
int fd = fds[1]; int fd = fds[1];
return _open_seat(listener, data, fd); return _open_seat(listener, data, fd);
} }
@ -638,6 +660,6 @@ const struct seat_impl builtin_impl = {
.close_device = close_device, .close_device = close_device,
.switch_session = switch_session, .switch_session = switch_session,
.get_fd = get_fd, .get_fd = get_fd,
.dispatch = dispatch_background, .dispatch = dispatch_and_execute,
}; };
#endif #endif

View file

@ -13,6 +13,7 @@
extern const struct seat_impl seatd_impl; extern const struct seat_impl seatd_impl;
extern const struct seat_impl logind_impl; extern const struct seat_impl logind_impl;
extern const struct seat_impl builtin_impl; extern const struct seat_impl builtin_impl;
extern const struct seat_impl noop_impl;
static const struct named_backend impls[] = { static const struct named_backend impls[] = {
#ifdef SEATD_ENABLED #ifdef SEATD_ENABLED
@ -24,6 +25,8 @@ static const struct named_backend impls[] = {
#ifdef BUILTIN_ENABLED #ifdef BUILTIN_ENABLED
{"builtin", &builtin_impl}, {"builtin", &builtin_impl},
#endif #endif
{"noop", &noop_impl},
{NULL, NULL}, {NULL, NULL},
}; };
@ -31,7 +34,7 @@ static const struct named_backend impls[] = {
#error At least one backend must be enabled #error At least one backend must be enabled
#endif #endif
struct libseat *libseat_open_seat(struct libseat_seat_listener *listener, void *data) { struct libseat *libseat_open_seat(const struct libseat_seat_listener *listener, void *data) {
if (listener == NULL || listener->enable_seat == NULL || listener->disable_seat == NULL) { if (listener == NULL || listener->enable_seat == NULL || listener->disable_seat == NULL) {
errno = EINVAL; errno = EINVAL;
return NULL; return NULL;
@ -62,6 +65,9 @@ struct libseat *libseat_open_seat(struct libseat_seat_listener *listener, void *
struct libseat *backend = NULL; struct libseat *backend = NULL;
for (const struct named_backend *iter = impls; iter->backend != NULL; iter++) { for (const struct named_backend *iter = impls; iter->backend != NULL; iter++) {
if (iter->backend == &noop_impl) {
continue;
}
backend = iter->backend->open_seat(listener, data); backend = iter->backend->open_seat(listener, data);
if (backend != NULL) { if (backend != NULL) {
log_infof("Seat opened with backend '%s'", iter->name); log_infof("Seat opened with backend '%s'", iter->name);

58
man/seatd-launch.1.scd Normal file
View file

@ -0,0 +1,58 @@
seatd-launch(1)
# NAME
seatd-launch - Start a process with its own seatd instance
# SYNOPSIS
*seatd-launch* [options] [--] command
# OPTIONS
*-l <loglevel>*
Log-level to pass to seatd. See *seatd*(1) for information about
available log-levels.
*-h*
Show help message and quit.
*-v*
Show the version number and quit.
# DESCRIPTION
seatd-launch starts a seatd instance with a dedicated socket path, waits for it
to be ready, and starts the specified command with SEATD_SOCK set
appropriately. Once the specified command terminates, the seatd instance is
also terminated.
seatd requires root privileges to perform its tasks. This can be achieved
through SUID of seatd-launch or by running seatd-launch as root. seatd-launch
will drop privileges from the effective user to the real user before running
the specified command. If the real user is root, this is simply a noop. You
should only run seatd-launch as root if you intend for the specified command to
run as root as well.
seatd-launch serves a similar purpose to the libseat "builtin" backend, but is
superior to it for two reasons:
. The specified command never runs as root
. The standard seatd executable and libseat backend is used
# EXIT STATUS
seatd-launch exits with the status of its child. When the child terminates on
a signal _N_, seatd-launch exits with the status 128 + _N_.
If seatd-launch fails because of another error, it exits with a non-zero
status.
# SEE ALSO
The libseat library, *<libseat.h>*, *seatd*(1)
# AUTHORS
Maintained by Kenny Levinsen <contact@kl.wtf>, who is assisted by other
open-source contributors. For more information about seatd development, see
https://sr.ht/~kennylevinsen/seatd.

View file

@ -13,14 +13,20 @@ seatd - A seat management daemon
*-h* *-h*
Show help message and quit. 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>* *-u <user>*
User to own the seatd socket. User to own the seatd socket.
*-g <group>* *-g <group>*
Group to own the seatd socket. Group to own the seatd socket.
*-s <path>* *-l <loglevel>*
Where to create the seatd socket. Defaults to `/run/seatd.sock`. Log-level to use. Must be one of debug, info, error or silent. Defaults
to error.
*-v* *-v*
Show the version number and quit. Show the version number and quit.
@ -33,21 +39,16 @@ such as displays and input devices in a multi-session, multi-seat environment.
seatd operates over a UNIX domain socket, with *libseat* providing the seatd operates over a UNIX domain socket, with *libseat* providing the
client-side of the protocol. client-side of the protocol.
The location of the socket for seatd is set at compile-time.
# ENVIRONMENT # ENVIRONMENT
[[ *VARIABLE* *SEATD_VTBOUND*
:[ *VALUES* If set to "0", the seat will not be bound to a VT.
:< *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 # SEE ALSO
The libseat library, *<libseat.h>* The libseat library, *<libseat.h>*, *seatd-launch*(1)
# AUTHORS # AUTHORS

View file

@ -1,9 +1,9 @@
project( project(
'seatd', 'seatd',
'c', 'c',
version: '0.5.0', version: '0.8.0',
license: 'MIT', license: 'MIT',
meson_version: '>=0.53.0', meson_version: '>=0.60.0',
default_options: [ default_options: [
'c_std=c11', 'c_std=c11',
'warning_level=3', 'warning_level=3',
@ -16,15 +16,29 @@ libseat_soversion = 1
defaultpath = get_option('defaultpath') defaultpath = get_option('defaultpath')
if defaultpath == '' if defaultpath == ''
system = target_machine.system() defaultpath = '/var/run/seatd.sock'
if system == 'linux' if target_machine.system() == 'linux'
defaultpath = '/run/seatd.sock' defaultpath = '/run/seatd.sock'
else
defaultpath = '/var/run/seatd.sock'
endif endif
endif endif
seatdpath = get_option('prefix') / get_option('bindir') / 'seatd'
cc = meson.get_compiler('c')
add_project_arguments( 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', '-Wundef',
'-Wunused', '-Wunused',
@ -38,12 +52,14 @@ add_project_arguments(
'-Wno-unknown-warning-option', '-Wno-unknown-warning-option',
'-Wno-unused-command-line-argument', '-Wno-unused-command-line-argument',
'-Wvla', '-Wvla',
]),
language: 'c',
)
add_project_arguments(cc.get_supported_link_arguments(
[
'-Wl,--exclude-libs=ALL', '-Wl,--exclude-libs=ALL',
'-D_XOPEN_SOURCE=700', ]),
'-D__BSD_VISIBLE',
'-DSEATD_VERSION="@0@"'.format(meson.project_version()),
'-DSEATD_DEFAULTPATH="@0@"'.format(defaultpath)
],
language: 'c', language: 'c',
) )
@ -51,9 +67,13 @@ if ['debugoptimized', 'release', 'minsize'].contains(get_option('buildtype'))
add_project_arguments('-D_FORTIFY_SOURCE=2', language: 'c') add_project_arguments('-D_FORTIFY_SOURCE=2', language: 'c')
endif endif
if get_option('buildtype').startswith('debug')
add_project_arguments('-DDEBUG', language : 'c')
endif
# Hacks # Hacks
source_root = meson.current_source_dir().split('/') source_root = meson.current_source_dir().split('/')
build_root = meson.build_root().split('/') build_root = meson.global_build_root().split('/')
relative_dir_parts = [] relative_dir_parts = []
i = 0 i = 0
in_prefix = true in_prefix = true
@ -74,11 +94,6 @@ foreach p : source_root
i += 1 i += 1
endforeach endforeach
if get_option('buildtype').startswith('debug')
add_project_arguments('-DDEBUG', language : 'c')
endif
add_project_arguments( add_project_arguments(
'-DREL_SRC_DIR="@0@"'.format(join_paths(relative_dir_parts) + '/'), '-DREL_SRC_DIR="@0@"'.format(join_paths(relative_dir_parts) + '/'),
language: 'c', language: 'c',
@ -99,41 +114,55 @@ server_files = [
'common/connection.c', 'common/connection.c',
'common/evdev.c', 'common/evdev.c',
'common/drm.c', 'common/drm.c',
'common/wscons.c',
'seatd/poller.c', 'seatd/poller.c',
'seatd/seat.c', 'seatd/seat.c',
'seatd/client.c', 'seatd/client.c',
'seatd/server.c', 'seatd/server.c',
] ]
if get_option('seatd').enabled() with_seatd = get_option('libseat-seatd') == 'enabled'
with_builtin = get_option('libseat-builtin') == 'enabled'
with_server = get_option('server') == 'enabled'
if with_seatd or with_builtin
private_files += 'libseat/backend/seatd.c' private_files += 'libseat/backend/seatd.c'
add_project_arguments('-DSEATD_ENABLED=1', language: 'c')
endif endif
logind_provider = '' libseat_c_args = ['-DLIBSEAT=1']
if get_option('logind').enabled()
# Check for libelogind first, as elogind may provide a libsystemd wrapper if with_seatd
# which can cause issues. libseat_c_args += '-DSEATD_ENABLED=1'
logind = dependency('libelogind', required: false) endif
add_project_arguments('-DLOGIND_ENABLED=1', language: 'c')
if logind.found() logind = disabler()
add_project_arguments('-DHAVE_ELOGIND=1', language: 'c') if get_option('libseat-logind') != 'disabled'
logind_provider = 'elogind' 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 else
logind = dependency('libsystemd') logind = dependency('lib@0@'.format(get_option('libseat-logind')))
add_project_arguments('-DHAVE_SYSTEMD=1', language: 'c')
logind_provider = 'systemd'
endif endif
private_files += [ if logind.found()
'libseat/backend/logind.c', libseat_c_args += '-DLOGIND_ENABLED=1'
'common/drm.c', libseat_c_args += '-DHAVE_@0@=1'.format(logind.name().to_upper())
] private_files += [
private_deps += logind 'libseat/backend/logind.c',
'common/drm.c',
]
private_deps += logind
endif
endif endif
if get_option('builtin').enabled() # needed for cross-compilation
add_project_arguments('-DBUILTIN_ENABLED=1', language: 'c') # realtime = meson.get_compiler('c').find_library('rt')
# private_deps += realtime
if with_builtin
libseat_c_args += '-DBUILTIN_ENABLED=1'
private_files += server_files private_files += server_files
endif endif
@ -142,41 +171,67 @@ private_lib = static_library(
private_files, private_files,
dependencies: private_deps, dependencies: private_deps,
include_directories: [include_directories('.', 'include')], include_directories: [include_directories('.', 'include')],
c_args: libseat_c_args,
) )
symbols_file = 'libseat/libseat.syms' symbols_file = 'libseat/libseat.syms'
symbols_flag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), symbols_file) symbols_flag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), symbols_file)
lib = library( lib = library(
'seat', # This results in the library being called 'libseat' 'seat', # This results in the library being called 'libseat'
[ 'libseat/libseat.c' ], [ 'libseat/libseat.c', 'libseat/backend/noop.c' ],
soversion: libseat_soversion, soversion: '@0@'.format(libseat_soversion),
link_with: private_lib, link_with: private_lib,
include_directories: [include_directories('.', 'include')], include_directories: [include_directories('.', 'include')],
install: true, install: true,
link_args: symbols_flag, link_args: symbols_flag,
link_depends: symbols_file, link_depends: symbols_file,
c_args: libseat_c_args,
) )
install_headers('include/libseat.h') 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 = import('pkgconfig')
pkgconfig.generate(lib, pkgconfig.generate(lib,
version: meson.project_version(), version: meson.project_version(),
filebase: 'libseat', filebase: 'libseat',
name: 'libseat', name: 'libseat',
description: 'Seat management library', description: 'Seat management library',
variables: libseat_vars,
) )
if get_option('server').enabled() libseat = declare_dependency(
link_with: lib,
dependencies: private_deps,
include_directories: include_directories('include', is_system: true),
variables: libseat_vars,
)
meson.override_dependency('libseat', libseat)
if with_server
executable( executable(
'seatd', 'seatd',
[ server_files, 'seatd/seatd.c' ], [ server_files, 'seatd/seatd.c' ],
include_directories: [include_directories('.', 'include')], include_directories: [include_directories('.', 'include')],
install: true, install: true,
# dependencies: [realtime],
)
executable(
'seatd-launch',
[ 'seatd-launch/seatd-launch.c' ],
include_directories: [include_directories('.', 'include')],
install: true,
# dependencies: [realtime],
) )
endif endif
if get_option('examples').enabled() if get_option('examples') == 'enabled'
executable( executable(
'simpletest', 'simpletest',
['examples/simpletest/main.c'], ['examples/simpletest/main.c'],
@ -199,21 +254,16 @@ foreach name, value : tests
include_directories: [include_directories('.', 'include')])) include_directories: [include_directories('.', 'include')]))
endforeach endforeach
if get_option('server').enabled() if with_server
scdoc = dependency('scdoc', required: get_option('man-pages'), version: '>= 1.9.7', native: true) scdoc = dependency('scdoc', required: get_option('man-pages'), version: '>= 1.9.7', native: true)
else else
scdoc = disabler() scdoc = disabler()
endif endif
if scdoc.found() if scdoc.found()
sh = find_program('sh', native: true)
scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true)
man_pages = ['seatd.1.scd']
mandir = get_option('mandir') mandir = get_option('mandir')
foreach src : man_pages foreach src : ['seatd.1.scd', 'seatd-launch.1.scd']
topic = src.split('.')[0] topic = src.split('.')[0]
section = src.split('.')[1] section = src.split('.')[1]
output = '@0@.@1@'.format(topic, section) output = '@0@.@1@'.format(topic, section)
@ -222,9 +272,9 @@ if scdoc.found()
output, output,
input: 'man/' + src, input: 'man/' + src,
output: output, output: output,
command: [ command: scdoc.get_variable(pkgconfig: 'scdoc'),
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output) feed: true,
], capture: true,
install: true, install: true,
install_dir: '@0@/man@1@'.format(mandir, section) install_dir: '@0@/man@1@'.format(mandir, section)
) )
@ -232,8 +282,9 @@ if scdoc.found()
endif endif
summary({ summary({
'seatd': get_option('seatd').enabled(), 'libseat-seatd': with_seatd,
'builtin': get_option('builtin').enabled(), 'libseat-builtin': with_builtin,
'systemd': logind_provider == 'systemd', 'libseat-systemd': logind.found() and logind.name() == 'libsystemd',
'elogind': logind_provider == 'elogind', 'libseat-elogind': logind.found() and logind.name() == 'libelogind',
'server': with_server,
}, bool_yn: true) }, bool_yn: true)

View file

@ -1,7 +1,7 @@
option('logind', type: 'feature', value: 'disabled', description: 'logind support') option('libseat-logind', type: 'combo', choices: ['auto', 'disabled', 'elogind', 'systemd'], value: 'auto', description: 'logind support for libseat')
option('seatd', type: 'feature', value: 'enabled', description: 'seatd support') option('libseat-seatd', type: 'combo', choices: ['enabled', 'disabled'], value: 'enabled', description: 'seatd support for libseat')
option('builtin', type: 'feature', value: 'disabled', description: 'builtin seatd server') option('libseat-builtin', type: 'combo', choices: ['enabled', 'disabled'], value: 'disabled', description: 'built-in seatd server for libseat')
option('server', type: 'feature', value: 'enabled', description: 'seatd server') option('server', type: 'combo', choices: ['enabled', 'disabled'], value: 'enabled', description: 'seatd server')
option('examples', type: 'feature', value: 'enabled', description: 'libseat example programs') option('examples', type: 'combo', choices: ['enabled', 'disabled'], value: 'disabled', description: 'libseat example programs')
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') option('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)') option('defaultpath', type: 'string', value: '', description: 'Default location for seatd socket (empty for default)')

174
seatd-launch/seatd-launch.c Normal file
View file

@ -0,0 +1,174 @@
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
const char *usage = "Usage: seatd-launch [options] [--] command\n"
"\n"
" -l <loglevel> Log-level to pass to seatd\n"
" -h Show this help message\n"
" -v Show the version number\n"
"\n";
int c;
char loglevel[16] = "info";
while ((c = getopt(argc, argv, "vhl:")) != -1) {
switch (c) {
case 'l':
strncpy(loglevel, optarg, sizeof loglevel);
loglevel[sizeof loglevel - 1] = '\0';
break;
case 'v':
printf("seatd-launch version %s\n", SEATD_VERSION);
return 0;
case 'h':
printf("%s", usage);
return 0;
case '?':
fprintf(stderr, "Try 'seatd-launch -h' for more information.\n");
return 1;
default:
abort();
}
}
if (optind >= argc) {
fprintf(stderr, "A command must be specified\n\n%s", usage);
return 1;
}
char **command = &argv[optind];
int readiness_pipe[2];
if (pipe(readiness_pipe) == -1) {
perror("Could not create pipe");
goto error;
}
// Start seatd
pid_t seatd_child = fork();
if (seatd_child == -1) {
perror("Could not fork seatd process");
goto error;
} else if (seatd_child == 0) {
close(readiness_pipe[0]);
char pipebuf[16] = {0};
snprintf(pipebuf, sizeof pipebuf, "%d", readiness_pipe[1]);
char *env[1] = {NULL};
char *command[] = {"seatd", "-n", pipebuf, "-l", loglevel, "-z", NULL};
execve(SEATD_INSTALLPATH, command, env);
perror("Could not start seatd");
_exit(1);
}
close(readiness_pipe[1]);
// Wait for seatd to be ready
char buf[1] = {0};
while (true) {
pid_t p = waitpid(seatd_child, NULL, WNOHANG);
if (p == seatd_child) {
fprintf(stderr, "seatd exited prematurely\n");
goto error_seatd;
} else if (p == -1 && (errno != EINTR && errno != ECHILD)) {
perror("Could not wait for seatd process");
goto error_seatd;
}
struct pollfd fd = {
.fd = readiness_pipe[0],
.events = POLLIN,
};
// We poll with timeout to avoid a racing on a blocking read
if (poll(&fd, 1, 1000) == -1) {
if (errno == EAGAIN || errno == EINTR) {
continue;
} else {
perror("Could not poll notification fd");
goto error_seatd;
}
}
if (fd.revents & POLLIN) {
ssize_t n = read(readiness_pipe[0], buf, 1);
if (n == -1 && errno != EINTR) {
perror("Could not read from pipe");
goto error_seatd;
} else if (n > 0) {
break;
}
}
}
close(readiness_pipe[0]);
uid_t uid = getuid();
gid_t gid = getgid();
// Restrict access to the socket to just us
if (chown(SEATD_DEFAULTPATH, uid, gid) == -1) {
perror("Could not chown seatd socket");
goto error_seatd;
}
if (chmod(SEATD_DEFAULTPATH, 0700) == -1) {
perror("Could not chmod socket");
goto error_seatd;
}
// Drop privileges
if (setgid(gid) == -1) {
perror("Could not set gid to drop privileges");
goto error_seatd;
}
if (setuid(uid) == -1) {
perror("Could not set uid to drop privileges");
goto error_seatd;
}
pid_t child = fork();
if (child == -1) {
perror("Could not fork target process");
goto error_seatd;
} else if (child == 0) {
setenv("SEATD_SOCK", SEATD_DEFAULTPATH, 1);
execvp(command[0], command);
perror("Could not start target");
_exit(1);
}
int status = 0;
while (true) {
pid_t p = waitpid(child, &status, 0);
if (p == child) {
break;
} else if (p == -1 && errno != EINTR) {
perror("Could not wait for target process");
goto error_seatd;
}
}
if (kill(seatd_child, SIGTERM) != 0) {
perror("Could not kill seatd");
}
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
return 128 + WTERMSIG(status);
} else {
abort(); // unreachable
}
error_seatd:
kill(seatd_child, SIGTERM);
error:
return 1;
}

View file

@ -14,6 +14,10 @@
#include <sys/un.h> #include <sys/un.h>
#endif #endif
#if defined(__NetBSD__)
#include <sys/un.h>
#endif
#include "client.h" #include "client.h"
#include "linked_list.h" #include "linked_list.h"
#include "log.h" #include "log.h"
@ -34,13 +38,47 @@ static int get_peer(int fd, pid_t *pid, uid_t *uid, gid_t *gid) {
*uid = cred.uid; *uid = cred.uid;
*gid = cred.gid; *gid = cred.gid;
return 0; 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__) #elif defined(__FreeBSD__)
struct xucred cred; struct xucred cred;
socklen_t len = sizeof cred; socklen_t len = sizeof cred;
if (getsockopt(fd, 0, LOCAL_PEERCRED, &cred, &len) == -1) { if (getsockopt(fd, 0, LOCAL_PEERCRED, &cred, &len) == -1) {
return -1; return -1;
} }
#if __FreeBSD_version >= 1300030 #if __FreeBSD_version >= 1300030 || (__FreeBSD_version >= 1202506 && __FreeBSD_version < 1300000)
*pid = cred.cr_pid; *pid = cred.cr_pid;
#else #else
*pid = -1; *pid = -1;
@ -49,7 +87,7 @@ static int get_peer(int fd, pid_t *pid, uid_t *uid, gid_t *gid) {
*gid = cred.cr_ngroups > 0 ? cred.cr_groups[0] : (gid_t)-1; *gid = cred.cr_ngroups > 0 ? cred.cr_groups[0] : (gid_t)-1;
return 0; return 0;
#else #else
return -1; #error Unsupported platform
#endif #endif
} }
@ -81,6 +119,13 @@ struct client *client_create(struct server *server, int client_fd) {
void client_destroy(struct client *client) { void client_destroy(struct client *client) {
assert(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; client->server = NULL;
if (client->connection.fd != -1) { if (client->connection.fd != -1) {
close(client->connection.fd); close(client->connection.fd);
@ -282,7 +327,7 @@ static int handle_switch_session(struct client *client, int session) {
log_error("Protocol error: no seat associated with client"); log_error("Protocol error: no seat associated with client");
return -1; return -1;
} }
log_debugf("handle_switch_session %d", session);
if (seat_set_next_session(client, session) == -1) { if (seat_set_next_session(client, session) == -1) {
goto error; goto error;
} }
@ -309,8 +354,23 @@ error:
return client_send_error(client, errno); 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) { static int client_handle_opcode(struct client *client, uint16_t opcode, size_t size) {
int res = 0; int res = 0;
log_debugf("client_handle_opcode: %d\n", opcode);
switch (opcode) { switch (opcode) {
case CLIENT_OPEN_SEAT: { case CLIENT_OPEN_SEAT: {
if (size != 0) { if (size != 0) {
@ -372,6 +432,14 @@ static int client_handle_opcode(struct client *client, uint16_t opcode, size_t s
res = handle_disable_seat(client); res = handle_disable_seat(client);
break; break;
} }
case CLIENT_PING: {
if (size != 0) {
log_error("Protocol error: invalid ping message");
return -1;
}
res = handle_ping(client);
break;
}
default: default:
log_errorf("Protocol error: unknown opcode: %d", opcode); log_errorf("Protocol error: unknown opcode: %d", opcode);
res = -1; res = -1;
@ -439,7 +507,13 @@ int client_handle_connection(int fd, uint32_t mask, void *data) {
goto fail; goto fail;
} }
if (len == 0) { 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"); log_error("Could not read client connection: zero-length read");
#endif
goto fail; goto fail;
} }

View file

@ -17,9 +17,10 @@
#include "protocol.h" #include "protocol.h"
#include "seat.h" #include "seat.h"
#include "terminal.h" #include "terminal.h"
#include "wscons.h"
static int seat_close_client(struct client *client); static int seat_close_client(struct client *client);
static void vt_close(struct seat *seat); static int vt_close(int vt);
struct seat *seat_create(const char *seat_name, bool vt_bound) { struct seat *seat_create(const char *seat_name, bool vt_bound) {
struct seat *seat = calloc(1, sizeof(struct seat)); struct seat *seat = calloc(1, sizeof(struct seat));
@ -30,7 +31,6 @@ struct seat *seat_create(const char *seat_name, bool vt_bound) {
seat->vt_bound = vt_bound; seat->vt_bound = vt_bound;
seat->seat_name = strdup(seat_name); seat->seat_name = strdup(seat_name);
seat->cur_vt = 0; seat->cur_vt = 0;
seat->cur_ttyfd = -1;
if (seat->seat_name == NULL) { if (seat->seat_name == NULL) {
free(seat); free(seat);
return NULL; return NULL;
@ -50,7 +50,6 @@ void seat_destroy(struct seat *seat) {
assert(client->seat == seat); assert(client->seat == seat);
client_destroy(client); client_destroy(client);
} }
vt_close(seat);
linked_list_remove(&seat->link); linked_list_remove(&seat->link);
free(seat->seat_name); free(seat->seat_name);
free(seat); free(seat);
@ -66,47 +65,30 @@ static void seat_update_vt(struct seat *seat) {
close(tty0fd); close(tty0fd);
} }
static int vt_open(struct seat *seat, int vt) { static int vt_open(int vt) {
assert(vt != -1); assert(vt != -1);
if (seat->cur_ttyfd != -1) { int ttyfd = terminal_open(vt);
terminal_set_process_switching(seat->cur_ttyfd, true); if (ttyfd == -1) {
close(seat->cur_ttyfd);
}
seat->cur_ttyfd = terminal_open(vt);
if (seat->cur_ttyfd == -1) {
log_errorf("Could not open terminal for VT %d: %s", vt, strerror(errno)); log_errorf("Could not open terminal for VT %d: %s", vt, strerror(errno));
return -1; return -1;
} }
terminal_set_process_switching(seat->cur_ttyfd, true); terminal_set_process_switching(ttyfd, true);
terminal_set_keyboard(seat->cur_ttyfd, false); terminal_set_keyboard(ttyfd, false);
terminal_set_graphics(seat->cur_ttyfd, true); terminal_set_graphics(ttyfd, true);
close(ttyfd);
return 0; return 0;
} }
static void vt_close_fd(int fd) { static int vt_close(int vt) {
terminal_set_process_switching(fd, true);
terminal_set_keyboard(fd, true);
terminal_set_graphics(fd, false);
}
static void vt_close(struct seat *seat) {
if (seat->cur_ttyfd == -1) {
return;
}
vt_close_fd(seat->cur_ttyfd);
close(seat->cur_ttyfd);
seat->cur_ttyfd = -1;
}
static int vt_close_num(int vt) {
int ttyfd = terminal_open(vt); int ttyfd = terminal_open(vt);
if (ttyfd == -1) { if (ttyfd == -1) {
log_errorf("Could not open terminal to clean up VT %d: %s", vt, strerror(errno)); log_errorf("Could not open terminal to clean up VT %d: %s", vt, strerror(errno));
return -1; return -1;
} }
vt_close_fd(ttyfd); terminal_set_process_switching(ttyfd, true);
terminal_set_graphics(ttyfd, false);
terminal_set_keyboard(ttyfd, true);
close(ttyfd); close(ttyfd);
return 0; return 0;
} }
@ -125,6 +107,7 @@ static int vt_switch(struct seat *seat, int vt) {
static int vt_ack(struct seat *seat, bool release) { static int vt_ack(struct seat *seat, bool release) {
int tty0fd = terminal_open(seat->cur_vt); int tty0fd = terminal_open(seat->cur_vt);
log_debugf("vt_ack VT %d %d\n", seat->cur_vt, release);
if (tty0fd == -1) { if (tty0fd == -1) {
log_errorf("Could not open tty0 to ack VT signal: %s", strerror(errno)); log_errorf("Could not open tty0 to ack VT signal: %s", strerror(errno));
return -1; return -1;
@ -148,7 +131,8 @@ int seat_add_client(struct seat *seat, struct client *client) {
return -1; return -1;
} }
if (seat->vt_bound && seat->active_client != NULL) { if (seat->vt_bound && seat->active_client != NULL &&
seat->active_client->state != CLIENT_PENDING_DISABLE) {
log_error("Could not add client: seat is VT-bound and has an active client"); log_error("Could not add client: seat is VT-bound and has an active client");
errno = EBUSY; errno = EBUSY;
return -1; return -1;
@ -167,6 +151,17 @@ int seat_add_client(struct seat *seat, struct client *client) {
errno = EINVAL; errno = EINVAL;
return -1; 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; client->session = seat->cur_vt;
} else { } else {
client->session = seat->session_cnt++; client->session = seat->session_cnt++;
@ -242,6 +237,8 @@ struct seat_device *seat_open_device(struct client *client, const char *path) {
type = SEAT_DEVICE_TYPE_EVDEV; type = SEAT_DEVICE_TYPE_EVDEV;
} else if (path_is_drm(sanitized_path)) { } else if (path_is_drm(sanitized_path)) {
type = SEAT_DEVICE_TYPE_DRM; type = SEAT_DEVICE_TYPE_DRM;
} else if (path_is_wscons(sanitized_path)) {
type = SEAT_DEVICE_TYPE_WSCONS;
} else { } else {
log_errorf("%s is not a supported device type ", sanitized_path); log_errorf("%s is not a supported device type ", sanitized_path);
errno = ENOENT; errno = ENOENT;
@ -288,6 +285,9 @@ struct seat_device *seat_open_device(struct client *client, const char *path) {
case SEAT_DEVICE_TYPE_EVDEV: case SEAT_DEVICE_TYPE_EVDEV:
// Nothing to do here // Nothing to do here
break; break;
case SEAT_DEVICE_TYPE_WSCONS:
// Nothing to do here
break;
default: default:
log_error("Invalid seat device type"); log_error("Invalid seat device type");
abort(); abort();
@ -340,6 +340,9 @@ static int seat_deactivate_device(struct seat_device *seat_device) {
return -1; return -1;
} }
break; break;
case SEAT_DEVICE_TYPE_WSCONS:
// Nothing to do here
break;
default: default:
log_error("Invalid seat device type"); log_error("Invalid seat device type");
abort(); abort();
@ -389,6 +392,9 @@ static int seat_activate_device(struct client *client, struct seat_device *seat_
case SEAT_DEVICE_TYPE_EVDEV: case SEAT_DEVICE_TYPE_EVDEV:
errno = EINVAL; errno = EINVAL;
return -1; return -1;
case SEAT_DEVICE_TYPE_WSCONS:
// Nothing to do here
break;
default: default:
log_error("Invalid seat device type"); log_error("Invalid seat device type");
abort(); abort();
@ -452,7 +458,7 @@ int seat_open_client(struct seat *seat, struct client *client) {
return -1; return -1;
} }
if (seat->vt_bound && vt_open(seat, client->session) == -1) { if (seat->vt_bound && vt_open(client->session) == -1) {
log_error("Could not open VT for client"); log_error("Could not open VT for client");
goto error; goto error;
} }
@ -477,7 +483,7 @@ int seat_open_client(struct seat *seat, struct client *client) {
error: error:
if (seat->vt_bound) { if (seat->vt_bound) {
vt_close(seat); vt_close(seat->cur_vt);
} }
return -1; return -1;
} }
@ -506,12 +512,12 @@ static int seat_close_client(struct client *client) {
// This client was current, but there were no clients // This client was current, but there were no clients
// waiting to take this VT, so clean it up. // waiting to take this VT, so clean it up.
log_debug("Closing active VT"); log_debug("Closing active VT");
vt_close(seat); vt_close(seat->cur_vt);
} else if (!was_current && client->state != CLIENT_CLOSED) { } else if (!was_current && client->state != CLIENT_CLOSED) {
// This client was not current, but as the client was // This client was not current, but as the client was
// running, we need to clean up the VT. // running, we need to clean up the VT.
log_debug("Closing inactive VT"); log_debug("Closing inactive VT");
vt_close_num(client->session); vt_close(client->session);
} }
} }
@ -532,7 +538,7 @@ static int seat_disable_client(struct client *client) {
errno = EBUSY; errno = EBUSY;
return -1; return -1;
} }
assert(seat->active_client = client); assert(seat->active_client == client);
// We *deactivate* all remaining fds. These may later be reactivated. // We *deactivate* all remaining fds. These may later be reactivated.
// The reason we cannot just close them is that certain device fds, such // The reason we cannot just close them is that certain device fds, such

View file

@ -33,58 +33,60 @@ static int open_socket(const char *path, int uid, int gid) {
socklen_t size = offsetof(struct sockaddr_un, sun_path) + strlen(addr.unix.sun_path); socklen_t size = offsetof(struct sockaddr_un, sun_path) + strlen(addr.unix.sun_path);
if (bind(fd, &addr.generic, size) == -1) { if (bind(fd, &addr.generic, size) == -1) {
log_errorf("Could not bind socket: %s", strerror(errno)); log_errorf("Could not bind socket: %s", strerror(errno));
close(fd); goto error;
return -1;
} }
if (listen(fd, LISTEN_BACKLOG) == -1) { if (listen(fd, LISTEN_BACKLOG) == -1) {
log_errorf("Could not listen on socket: %s", strerror(errno)); log_errorf("Could not listen on socket: %s", strerror(errno));
close(fd); goto error;
return -1;
} }
if (uid != 0 || gid != 0) { if (uid != -1 || gid != -1) {
if (chmod(path, 0770) == -1) {
log_errorf("Could not chmod socket: %s", strerror(errno));
goto error;
}
if (chown(path, uid, gid) == -1) { 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)); strerror(errno));
} else if (chmod(path, 0770) == -1) { goto error;
log_errorf("Could not chmod socket: %s", strerror(errno));
} }
} }
return fd; return fd;
error:
close(fd);
return -1;
} }
int main(int argc, char *argv[]) { 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" const char *usage = "Usage: seatd [options]\n"
"\n" "\n"
" -h Show this help message\n" " -h Show this help message\n"
" -n <fd> FD to notify readiness on\n"
" -u <user> User to own the seatd socket\n" " -u <user> User to own the seatd socket\n"
" -g <group> Group to own the seatd socket\n" " -g <group> Group to own the seatd socket\n"
" -s <path> Where to create the seatd socket\n" " -l <loglevel> Log-level, one of debug, info, error or silent\n"
" -v Show the version number\n" " -v Show the version number\n"
"\n"; "\n";
int c; int c;
int uid = 0, gid = 0; int uid = -1, gid = -1;
const char *socket_path = getenv("SEATD_SOCK"); int readiness = -1;
while ((c = getopt(argc, argv, "vhs:g:u:")) != -1) { bool unlink_existing_socket = true;
bool chown_socket = true;
enum libseat_log_level level = LIBSEAT_LOG_LEVEL_INFO;
while ((c = getopt(argc, argv, "vhn:g:u:l:z")) != -1) {
switch (c) { switch (c) {
case 's': case 'n':
socket_path = optarg; readiness = atoi(optarg);
if (readiness < 0) {
fprintf(stderr, "Invalid readiness fd: %s\n", optarg);
return 1;
}
break; break;
case 'u': { case 'u': {
if (!chown_socket) {
fprintf(stderr, "-u/-g and -z are mutually exclusive\n");
return 1;
}
struct passwd *pw = getpwnam(optarg); struct passwd *pw = getpwnam(optarg);
if (pw == NULL) { if (pw == NULL) {
fprintf(stderr, "Could not find user by name '%s'.\n", optarg); fprintf(stderr, "Could not find user by name '%s'.\n", optarg);
@ -95,6 +97,10 @@ int main(int argc, char *argv[]) {
break; break;
} }
case 'g': { case 'g': {
if (!chown_socket) {
fprintf(stderr, "-u/-g and -z are mutually exclusive\n");
return 1;
}
struct group *gr = getgrnam(optarg); struct group *gr = getgrnam(optarg);
if (gr == NULL) { if (gr == NULL) {
fprintf(stderr, "Could not find group by name '%s'.\n", optarg); fprintf(stderr, "Could not find group by name '%s'.\n", optarg);
@ -104,6 +110,31 @@ int main(int argc, char *argv[]) {
} }
break; 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': case 'v':
printf("seatd version %s\n", SEATD_VERSION); printf("seatd version %s\n", SEATD_VERSION);
return 0; return 0;
@ -118,46 +149,72 @@ int main(int argc, char *argv[]) {
} }
} }
if (socket_path == NULL) { log_init();
socket_path = SEATD_DEFAULTPATH; libseat_set_log_level(level);
struct stat st;
if (stat(socket_path, &st) == 0) { struct stat st;
log_info("Removing leftover seatd socket"); if (lstat(SEATD_DEFAULTPATH, &st) == 0) {
unlink(socket_path); if (!S_ISSOCK(st.st_mode)) {
log_errorf("Non-socket file found at socket path %s, refusing to start",
SEATD_DEFAULTPATH);
return 1;
} else if (!unlink_existing_socket) {
log_errorf("Socket file found at socket path %s, refusing to start",
SEATD_DEFAULTPATH);
return 1;
} else {
// We only do this if the socket path is not user specified
log_infof("Removing leftover socket at %s", SEATD_DEFAULTPATH);
if (unlink(SEATD_DEFAULTPATH) == -1) {
log_errorf("Could not remove leftover socket: %s", strerror(errno));
return 1;
}
} }
} }
struct server server = {0}; struct server server = {0};
if (server_init(&server) == -1) { if (server_init(&server) == -1) {
log_errorf("server_create failed: %s", strerror(errno)); log_errorf("server_init failed: %s", strerror(errno));
return 1; return 1;
} }
int socket_fd = open_socket(socket_path, uid, gid); int ret = 1;
int socket_fd = open_socket(SEATD_DEFAULTPATH, uid, gid);
if (socket_fd == -1) { if (socket_fd == -1) {
log_errorf("Could not create server socket: %s", strerror(errno)); log_error("Could not create server socket");
server_finish(&server); goto error_server;
return 1;
} }
if (poller_add_fd(&server.poller, socket_fd, EVENT_READABLE, server_handle_connection, if (poller_add_fd(&server.poller, socket_fd, EVENT_READABLE, server_handle_connection,
&server) == NULL) { &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); close(socket_fd);
server_finish(&server); goto error_socket;
return 1;
} }
log_info("seatd started"); 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) { while (server.running) {
if (poller_poll(&server.poller) == -1) { if (poller_poll(&server.poller) == -1) {
log_errorf("Poller failed: %s", strerror(errno)); log_errorf("Poller failed: %s", strerror(errno));
return 1; goto error_socket;
} }
} }
ret = 0;
error_socket:
if (unlink(SEATD_DEFAULTPATH) == -1) {
log_errorf("Could not remove socket: %s", strerror(errno));
}
error_server:
server_finish(&server); server_finish(&server);
unlink(socket_path);
log_info("seatd stopped"); log_info("seatd stopped");
return 0; return ret;
} }

View file

@ -123,12 +123,18 @@ static int set_nonblock(int fd) {
int server_add_client(struct server *server, int fd) { int server_add_client(struct server *server, int fd) {
if (set_nonblock(fd) != 0) { if (set_nonblock(fd) != 0) {
close(fd);
log_errorf("Could not prepare new client socket: %s", strerror(errno)); log_errorf("Could not prepare new client socket: %s", strerror(errno));
close(fd);
return -1; return -1;
} }
struct client *client = client_create(server, fd); 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 = client->event_source =
poller_add_fd(&server->poller, fd, EVENT_READABLE, client_handle_connection, client); poller_add_fd(&server->poller, fd, EVENT_READABLE, client_handle_connection, client);
if (client->event_source == NULL) { if (client->event_source == NULL) {
@ -146,7 +152,7 @@ int server_handle_connection(int fd, uint32_t mask, void *data) {
if (mask & (EVENT_ERROR | EVENT_HANGUP)) { if (mask & (EVENT_ERROR | EVENT_HANGUP)) {
shutdown(fd, SHUT_RDWR); shutdown(fd, SHUT_RDWR);
server->running = false; server->running = false;
log_errorf("Server socket recieved an error: %s", strerror(errno)); log_error("Server socket received an error");
return -1; return -1;
} }

View file

@ -123,6 +123,10 @@ static int test_signal_event(int signal, void *data) {
return 0; return 0;
} }
#ifdef __OpenBSD__
#define SIGRTMIN SIGUSR1
#endif
static void test_poller_single_signal(void) { static void test_poller_single_signal(void) {
struct poller poller; struct poller poller;
test_assert(poller_init(&poller) == 0); test_assert(poller_init(&poller) == 0);