2020-07-31 00:22:18 +02:00
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <poll.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "backend.h"
|
|
|
|
#include "connection.h"
|
|
|
|
#include "libseat.h"
|
2020-08-03 02:32:33 +02:00
|
|
|
#include "linked_list.h"
|
2020-07-31 00:22:18 +02:00
|
|
|
#include "log.h"
|
|
|
|
#include "protocol.h"
|
|
|
|
|
|
|
|
#ifdef BUILTIN_ENABLED
|
|
|
|
#include "poller.h"
|
|
|
|
#include "server.h"
|
|
|
|
#endif
|
|
|
|
|
2020-08-05 23:33:01 +02:00
|
|
|
const struct seat_impl seatd_impl;
|
|
|
|
const struct seat_impl builtin_impl;
|
2020-07-31 00:22:18 +02:00
|
|
|
|
|
|
|
struct pending_event {
|
2020-08-03 02:32:33 +02:00
|
|
|
struct linked_list link; // backend_seat::link
|
2020-07-31 00:22:18 +02:00
|
|
|
int opcode;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct backend_seatd {
|
|
|
|
struct libseat base;
|
|
|
|
struct connection connection;
|
2021-08-14 09:03:23 +00:00
|
|
|
const struct libseat_seat_listener *seat_listener;
|
2020-07-31 00:22:18 +02:00
|
|
|
void *seat_listener_data;
|
2020-08-03 02:32:33 +02:00
|
|
|
struct linked_list pending_events;
|
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, 6fa82930d0c5660eea3102989c765dc864514e36 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-20 23:43:10 +02:00
|
|
|
bool awaiting_pong;
|
2020-08-29 22:45:01 +02:00
|
|
|
bool error;
|
2020-07-31 00:22:18 +02:00
|
|
|
|
|
|
|
char seat_name[MAX_SEAT_LEN];
|
|
|
|
};
|
|
|
|
|
|
|
|
static int seatd_connect(void) {
|
|
|
|
union {
|
|
|
|
struct sockaddr_un unix;
|
|
|
|
struct sockaddr generic;
|
2020-08-01 00:31:59 +00:00
|
|
|
} addr = {{0}};
|
2022-02-09 23:21:18 +01:00
|
|
|
int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
2020-07-31 00:22:18 +02:00
|
|
|
if (fd == -1) {
|
2020-08-29 20:31:51 +02:00
|
|
|
log_errorf("Could not create socket: %s", strerror(errno));
|
2020-07-31 00:22:18 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2020-09-22 01:12:33 +02:00
|
|
|
const char *path = getenv("SEATD_SOCK");
|
2020-07-31 00:22:18 +02:00
|
|
|
if (path == NULL) {
|
2020-09-22 01:12:33 +02:00
|
|
|
path = SEATD_DEFAULTPATH;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
addr.unix.sun_family = AF_UNIX;
|
2020-09-22 00:53:01 +02:00
|
|
|
strncpy(addr.unix.sun_path, path, sizeof addr.unix.sun_path - 1);
|
2020-07-31 00:22:18 +02:00
|
|
|
socklen_t size = offsetof(struct sockaddr_un, sun_path) + strlen(addr.unix.sun_path);
|
|
|
|
if (connect(fd, &addr.generic, size) == -1) {
|
2021-04-23 14:13:58 +00:00
|
|
|
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));
|
|
|
|
}
|
2020-07-31 00:22:18 +02:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
};
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct backend_seatd *backend_seatd_from_libseat_backend(struct libseat *base) {
|
|
|
|
assert(base);
|
|
|
|
#ifdef BUILTIN_ENABLED
|
|
|
|
assert(base->impl == &seatd_impl || base->impl == &builtin_impl);
|
|
|
|
#else
|
|
|
|
assert(base->impl == &seatd_impl);
|
|
|
|
#endif
|
|
|
|
return (struct backend_seatd *)base;
|
|
|
|
}
|
|
|
|
|
2020-08-29 22:45:01 +02:00
|
|
|
static void cleanup(struct backend_seatd *backend) {
|
|
|
|
if (backend->connection.fd != -1) {
|
|
|
|
close(backend->connection.fd);
|
|
|
|
backend->connection.fd = -1;
|
|
|
|
}
|
|
|
|
connection_close_fds(&backend->connection);
|
|
|
|
while (!linked_list_empty(&backend->pending_events)) {
|
|
|
|
struct pending_event *ev = (struct pending_event *)backend->pending_events.next;
|
|
|
|
linked_list_remove(&ev->link);
|
|
|
|
free(ev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void destroy(struct backend_seatd *backend) {
|
|
|
|
cleanup(backend);
|
|
|
|
free(backend);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_error(struct backend_seatd *backend) {
|
|
|
|
if (backend->error) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
backend->error = true;
|
|
|
|
cleanup(backend);
|
|
|
|
}
|
|
|
|
|
2020-08-29 20:31:51 +02:00
|
|
|
static inline int conn_put(struct backend_seatd *backend, const void *data, const size_t data_len) {
|
|
|
|
if (connection_put(&backend->connection, data, data_len) == -1) {
|
|
|
|
log_errorf("Could not send request: %s", strerror(errno));
|
2020-08-29 22:45:01 +02:00
|
|
|
set_error(backend);
|
2020-08-29 20:31:51 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int conn_flush(struct backend_seatd *backend) {
|
|
|
|
if (connection_flush(&backend->connection) == -1) {
|
|
|
|
log_errorf("Could not flush connection: %s", strerror(errno));
|
2020-08-29 22:45:01 +02:00
|
|
|
set_error(backend);
|
2020-08-29 20:31:51 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int conn_get(struct backend_seatd *backend, void *target, const size_t target_len) {
|
|
|
|
if (connection_get(&backend->connection, target, target_len) == -1) {
|
|
|
|
log_error("Invalid message: insufficient data received");
|
2020-08-29 22:45:01 +02:00
|
|
|
set_error(backend);
|
2020-08-29 20:31:51 +02:00
|
|
|
errno = EBADMSG;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int conn_get_fd(struct backend_seatd *backend, int *fd) {
|
|
|
|
if (connection_get_fd(&backend->connection, fd) == -1) {
|
|
|
|
log_error("Invalid message: insufficient data received");
|
2020-08-29 22:45:01 +02:00
|
|
|
set_error(backend);
|
2020-08-29 20:31:51 +02:00
|
|
|
errno = EBADMSG;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t read_header(struct backend_seatd *backend, uint16_t expected_opcode,
|
|
|
|
size_t expected_size, bool variable) {
|
2020-07-31 00:22:18 +02:00
|
|
|
struct proto_header header;
|
2020-08-29 20:31:51 +02:00
|
|
|
if (conn_get(backend, &header, sizeof header) == -1) {
|
2020-08-29 22:45:01 +02:00
|
|
|
set_error(backend);
|
2020-07-31 00:22:18 +02:00
|
|
|
return SIZE_MAX;
|
|
|
|
}
|
|
|
|
if (header.opcode != expected_opcode) {
|
2020-08-28 22:40:42 +02:00
|
|
|
struct proto_server_error msg;
|
|
|
|
if (header.opcode != SERVER_ERROR) {
|
2020-08-29 20:31:51 +02:00
|
|
|
log_errorf("Unexpected response: expected opcode %d, received opcode %d",
|
2020-08-28 22:40:42 +02:00
|
|
|
expected_opcode, header.opcode);
|
2020-08-29 22:45:01 +02:00
|
|
|
set_error(backend);
|
2020-08-28 22:40:42 +02:00
|
|
|
errno = EBADMSG;
|
2020-09-07 23:33:50 +02:00
|
|
|
} else if (header.size != sizeof msg || conn_get(backend, &msg, sizeof msg) == -1) {
|
2020-08-29 22:45:01 +02:00
|
|
|
set_error(backend);
|
2020-08-28 22:40:42 +02:00
|
|
|
errno = EBADMSG;
|
|
|
|
} else {
|
|
|
|
errno = msg.error_code;
|
|
|
|
}
|
2020-07-31 00:22:18 +02:00
|
|
|
return SIZE_MAX;
|
|
|
|
}
|
|
|
|
|
2020-08-29 20:31:51 +02:00
|
|
|
if ((!variable && header.size != expected_size) || (variable && header.size < expected_size)) {
|
|
|
|
log_errorf("Invalid message: does not match expected size: variable: %d, header.size: %d, expected size: %zd",
|
|
|
|
variable, header.size, expected_size);
|
2020-08-29 22:45:01 +02:00
|
|
|
set_error(backend);
|
2020-08-29 20:31:51 +02:00
|
|
|
errno = EBADMSG;
|
|
|
|
return SIZE_MAX;
|
|
|
|
}
|
2020-07-31 00:22:18 +02:00
|
|
|
return header.size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int queue_event(struct backend_seatd *backend, int opcode) {
|
|
|
|
struct pending_event *ev = calloc(1, sizeof(struct pending_event));
|
|
|
|
if (ev == NULL) {
|
2020-08-29 20:31:51 +02:00
|
|
|
log_errorf("Allocation failed: %s", strerror(errno));
|
2020-07-31 00:22:18 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ev->opcode = opcode;
|
2020-08-03 02:32:33 +02:00
|
|
|
linked_list_insert(&backend->pending_events, &ev->link);
|
2020-07-31 00:22:18 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-07-08 23:41:09 +02:00
|
|
|
static int execute_events(struct backend_seatd *backend) {
|
2020-08-30 00:05:19 +02:00
|
|
|
struct linked_list list;
|
2020-08-31 02:26:47 +02:00
|
|
|
linked_list_init(&list);
|
2020-08-30 00:05:19 +02:00
|
|
|
linked_list_take(&list, &backend->pending_events);
|
2021-07-08 23:41:09 +02:00
|
|
|
int executed = 0;
|
2020-08-03 02:32:33 +02:00
|
|
|
while (!linked_list_empty(&list)) {
|
|
|
|
struct pending_event *ev = (struct pending_event *)list.next;
|
2020-07-31 00:22:18 +02:00
|
|
|
int opcode = ev->opcode;
|
2020-08-03 02:32:33 +02:00
|
|
|
linked_list_remove(&ev->link);
|
2020-07-31 00:22:18 +02:00
|
|
|
free(ev);
|
|
|
|
|
|
|
|
switch (opcode) {
|
|
|
|
case SERVER_DISABLE_SEAT:
|
2020-08-29 20:27:02 +02:00
|
|
|
log_info("Disabling seat");
|
|
|
|
backend->seat_listener->disable_seat(&backend->base,
|
|
|
|
backend->seat_listener_data);
|
2020-07-31 00:22:18 +02:00
|
|
|
break;
|
|
|
|
case SERVER_ENABLE_SEAT:
|
2020-08-29 20:27:02 +02:00
|
|
|
log_info("Enabling seat");
|
|
|
|
backend->seat_listener->enable_seat(&backend->base,
|
|
|
|
backend->seat_listener_data);
|
2020-07-31 00:22:18 +02:00
|
|
|
break;
|
|
|
|
default:
|
2020-08-29 20:27:02 +02:00
|
|
|
log_errorf("Invalid opcode: %d", opcode);
|
2020-07-31 00:22:18 +02:00
|
|
|
abort();
|
|
|
|
}
|
2021-07-08 23:41:09 +02:00
|
|
|
executed++;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
2021-07-08 23:41:09 +02:00
|
|
|
return executed;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int dispatch_pending(struct backend_seatd *backend, int *opcode) {
|
|
|
|
int packets = 0;
|
|
|
|
struct proto_header header;
|
|
|
|
while (connection_get(&backend->connection, &header, sizeof header) != -1) {
|
|
|
|
packets++;
|
|
|
|
switch (header.opcode) {
|
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, 6fa82930d0c5660eea3102989c765dc864514e36 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-20 23:43:10 +02:00
|
|
|
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;
|
2020-07-31 00:22:18 +02:00
|
|
|
case SERVER_DISABLE_SEAT:
|
|
|
|
case SERVER_ENABLE_SEAT:
|
2020-08-28 22:40:42 +02:00
|
|
|
if (queue_event(backend, header.opcode) == -1) {
|
2020-08-29 22:45:01 +02:00
|
|
|
set_error(backend);
|
2020-08-28 22:40:42 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2020-07-31 00:22:18 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (opcode != NULL &&
|
|
|
|
connection_pending(&backend->connection) >= header.size) {
|
|
|
|
*opcode = header.opcode;
|
|
|
|
}
|
|
|
|
connection_restore(&backend->connection, sizeof header);
|
|
|
|
return packets;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return packets;
|
|
|
|
}
|
|
|
|
|
2021-07-09 00:09:14 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-07-31 00:22:18 +02:00
|
|
|
static int poll_connection(struct backend_seatd *backend, int timeout) {
|
|
|
|
struct pollfd fd = {
|
|
|
|
.fd = backend->connection.fd,
|
|
|
|
.events = POLLIN,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (poll(&fd, 1, timeout) == -1) {
|
|
|
|
return (errno == EAGAIN || errno == EINTR) ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fd.revents & (POLLERR | POLLHUP)) {
|
|
|
|
errno = EPIPE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int len = 0;
|
|
|
|
if (fd.revents & POLLIN) {
|
|
|
|
len = connection_read(&backend->connection);
|
2020-08-28 22:40:42 +02:00
|
|
|
if (len == 0) {
|
|
|
|
errno = EIO;
|
|
|
|
return -1;
|
|
|
|
} else if (len == -1 && errno != EAGAIN) {
|
2020-07-31 00:22:18 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dispatch(struct backend_seatd *backend) {
|
2020-08-29 20:31:51 +02:00
|
|
|
if (conn_flush(backend) == -1) {
|
2020-07-31 00:22:18 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2020-09-19 21:33:55 +02:00
|
|
|
while (true) {
|
|
|
|
int opcode = 0;
|
|
|
|
if (dispatch_pending(backend, &opcode) == -1) {
|
|
|
|
log_errorf("Could not dispatch pending messages: %s", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (opcode != 0) {
|
|
|
|
break;
|
|
|
|
}
|
2020-07-31 00:22:18 +02:00
|
|
|
if (poll_connection(backend, -1) == -1) {
|
2020-08-28 22:40:42 +02:00
|
|
|
log_errorf("Could not poll connection: %s", strerror(errno));
|
2020-07-31 00:22:18 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2020-08-28 22:40:42 +02:00
|
|
|
return 0;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_fd(struct libseat *base) {
|
|
|
|
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
|
|
|
|
return backend->connection.fd;
|
|
|
|
}
|
|
|
|
|
2021-07-09 00:16:10 +02:00
|
|
|
static int dispatch_and_execute(struct libseat *base, int timeout) {
|
2020-07-31 00:22:18 +02:00
|
|
|
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
|
2020-08-29 22:45:01 +02:00
|
|
|
if (backend->error) {
|
|
|
|
errno = ENOTCONN;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-07-09 00:09:14 +02:00
|
|
|
int predispatch = dispatch_pending_and_execute(backend);
|
|
|
|
if (predispatch == -1) {
|
|
|
|
return -1;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
2021-07-09 00:09:14 +02:00
|
|
|
|
|
|
|
// 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.
|
2020-07-31 00:22:18 +02:00
|
|
|
int read = 0;
|
2021-08-06 01:09:57 +02:00
|
|
|
if (predispatch > 0 || timeout == 0) {
|
2020-07-31 00:22:18 +02:00
|
|
|
read = connection_read(&backend->connection);
|
|
|
|
} else {
|
|
|
|
read = poll_connection(backend, timeout);
|
|
|
|
}
|
2021-07-09 00:09:14 +02:00
|
|
|
|
|
|
|
if (read == 0) {
|
|
|
|
return predispatch;
|
2020-07-31 00:22:18 +02:00
|
|
|
} else if (read == -1 && errno != EAGAIN) {
|
2020-08-28 22:40:42 +02:00
|
|
|
log_errorf("Could not read from connection: %s", strerror(errno));
|
2020-07-31 00:22:18 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-07-09 00:09:14 +02:00
|
|
|
int postdispatch = dispatch_pending_and_execute(backend);
|
|
|
|
if (postdispatch == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return predispatch + postdispatch;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
2021-08-14 09:03:23 +00:00
|
|
|
static struct libseat *_open_seat(const struct libseat_seat_listener *listener, void *data, int fd) {
|
2020-08-29 20:27:02 +02:00
|
|
|
assert(listener != NULL);
|
|
|
|
assert(listener->enable_seat != NULL && listener->disable_seat != NULL);
|
2020-07-31 00:22:18 +02:00
|
|
|
struct backend_seatd *backend = calloc(1, sizeof(struct backend_seatd));
|
|
|
|
if (backend == NULL) {
|
2020-08-29 20:31:51 +02:00
|
|
|
log_errorf("Allocation failed: %s", strerror(errno));
|
|
|
|
goto alloc_error;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
2020-08-29 20:31:51 +02:00
|
|
|
|
2020-07-31 00:22:18 +02:00
|
|
|
backend->seat_listener = listener;
|
|
|
|
backend->seat_listener_data = data;
|
|
|
|
backend->connection.fd = fd;
|
|
|
|
backend->base.impl = &seatd_impl;
|
2020-08-03 02:32:33 +02:00
|
|
|
linked_list_init(&backend->pending_events);
|
2020-07-31 00:22:18 +02:00
|
|
|
|
|
|
|
struct proto_header header = {
|
|
|
|
.opcode = CLIENT_OPEN_SEAT,
|
|
|
|
.size = 0,
|
|
|
|
};
|
2020-08-29 20:31:51 +02:00
|
|
|
if (conn_put(backend, &header, sizeof header) == -1 || dispatch(backend) == -1) {
|
|
|
|
goto backend_error;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
struct proto_server_seat_opened rmsg;
|
2020-08-29 20:31:51 +02:00
|
|
|
size_t size = read_header(backend, SERVER_SEAT_OPENED, sizeof rmsg, true);
|
|
|
|
if (size == SIZE_MAX || conn_get(backend, &rmsg, sizeof rmsg) == -1) {
|
|
|
|
goto backend_error;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
2020-08-29 20:31:51 +02:00
|
|
|
if (rmsg.seat_name_len != size - sizeof rmsg) {
|
|
|
|
log_errorf("Invalid message: seat_name_len does not match remaining message size (%d != %zd)",
|
|
|
|
rmsg.seat_name_len, size);
|
|
|
|
errno = EBADMSG;
|
|
|
|
goto backend_error;
|
|
|
|
}
|
|
|
|
if (conn_get(backend, backend->seat_name, rmsg.seat_name_len) == -1) {
|
|
|
|
goto backend_error;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
2020-09-19 21:58:09 +02:00
|
|
|
execute_events(backend);
|
2020-07-31 00:22:18 +02:00
|
|
|
return &backend->base;
|
2020-08-28 22:40:42 +02:00
|
|
|
|
2020-08-29 20:31:51 +02:00
|
|
|
backend_error:
|
|
|
|
destroy(backend);
|
|
|
|
alloc_error:
|
|
|
|
close(fd);
|
2020-08-28 22:40:42 +02:00
|
|
|
return NULL;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
2021-08-14 09:03:23 +00:00
|
|
|
static struct libseat *open_seat(const struct libseat_seat_listener *listener, void *data) {
|
2020-07-31 00:22:18 +02:00
|
|
|
int fd = seatd_connect();
|
|
|
|
if (fd == -1) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return _open_seat(listener, data, fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int close_seat(struct libseat *base) {
|
|
|
|
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
|
|
|
|
|
|
|
|
struct proto_header header = {
|
|
|
|
.opcode = CLIENT_CLOSE_SEAT,
|
|
|
|
.size = 0,
|
|
|
|
};
|
2020-08-29 20:31:51 +02:00
|
|
|
if (conn_put(backend, &header, sizeof header) == -1 || dispatch(backend) == -1) {
|
|
|
|
goto error;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
2020-08-29 20:31:51 +02:00
|
|
|
if (read_header(backend, SERVER_SEAT_CLOSED, 0, false) == SIZE_MAX) {
|
|
|
|
goto error;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
2020-09-19 21:58:09 +02:00
|
|
|
execute_events(backend);
|
2020-07-31 00:22:18 +02:00
|
|
|
destroy(backend);
|
|
|
|
return 0;
|
2020-08-29 20:31:51 +02:00
|
|
|
|
|
|
|
error:
|
2020-09-19 21:58:09 +02:00
|
|
|
execute_events(backend);
|
2020-08-29 20:31:51 +02:00
|
|
|
destroy(backend);
|
|
|
|
return -1;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char *seat_name(struct libseat *base) {
|
|
|
|
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
|
|
|
|
return backend->seat_name;
|
|
|
|
}
|
|
|
|
|
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, 6fa82930d0c5660eea3102989c765dc864514e36 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-20 23:43:10 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-07-31 00:22:18 +02:00
|
|
|
static int open_device(struct libseat *base, const char *path, int *fd) {
|
|
|
|
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
|
2020-08-29 22:45:01 +02:00
|
|
|
if (backend->error) {
|
|
|
|
errno = ENOTCONN;
|
|
|
|
return -1;
|
|
|
|
}
|
2020-07-31 00:22:18 +02:00
|
|
|
size_t pathlen = strlen(path) + 1;
|
|
|
|
if (pathlen > MAX_PATH_LEN) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct proto_client_open_device msg = {
|
|
|
|
.path_len = (uint16_t)pathlen,
|
|
|
|
};
|
|
|
|
struct proto_header header = {
|
|
|
|
.opcode = CLIENT_OPEN_DEVICE,
|
|
|
|
.size = sizeof msg + pathlen,
|
|
|
|
};
|
2020-08-29 20:31:51 +02:00
|
|
|
if (conn_put(backend, &header, sizeof header) == -1 ||
|
|
|
|
conn_put(backend, &msg, sizeof msg) == -1 || conn_put(backend, path, pathlen) == -1 ||
|
|
|
|
dispatch(backend) == -1) {
|
2020-09-19 21:58:09 +02:00
|
|
|
goto error;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
struct proto_server_device_opened rmsg;
|
2020-08-29 20:31:51 +02:00
|
|
|
if (read_header(backend, SERVER_DEVICE_OPENED, sizeof rmsg, false) == SIZE_MAX ||
|
|
|
|
conn_get(backend, &rmsg, sizeof rmsg) == -1 || conn_get_fd(backend, fd)) {
|
2020-09-19 21:58:09 +02:00
|
|
|
goto error;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
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, 6fa82930d0c5660eea3102989c765dc864514e36 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-20 23:43:10 +02:00
|
|
|
check_pending_events(backend);
|
2020-07-31 00:22:18 +02:00
|
|
|
return rmsg.device_id;
|
2020-09-19 21:58:09 +02:00
|
|
|
|
|
|
|
error:
|
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, 6fa82930d0c5660eea3102989c765dc864514e36 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-20 23:43:10 +02:00
|
|
|
check_pending_events(backend);
|
2020-09-19 21:58:09 +02:00
|
|
|
return -1;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int close_device(struct libseat *base, int device_id) {
|
|
|
|
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
|
2020-08-29 22:45:01 +02:00
|
|
|
if (backend->error) {
|
|
|
|
errno = ENOTCONN;
|
|
|
|
return -1;
|
|
|
|
}
|
2020-07-31 00:22:18 +02:00
|
|
|
if (device_id < 0) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct proto_client_close_device msg = {
|
|
|
|
.device_id = device_id,
|
|
|
|
};
|
|
|
|
struct proto_header header = {
|
|
|
|
.opcode = CLIENT_CLOSE_DEVICE,
|
|
|
|
.size = sizeof msg,
|
|
|
|
};
|
2020-08-29 20:31:51 +02:00
|
|
|
if (conn_put(backend, &header, sizeof header) == -1 ||
|
|
|
|
conn_put(backend, &msg, sizeof msg) == -1 || dispatch(backend) == -1) {
|
2020-09-19 21:58:09 +02:00
|
|
|
goto error;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
2020-08-30 03:26:32 +02:00
|
|
|
if (read_header(backend, SERVER_DEVICE_CLOSED, 0, false) == SIZE_MAX) {
|
2020-09-19 21:58:09 +02:00
|
|
|
goto error;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
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, 6fa82930d0c5660eea3102989c765dc864514e36 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-20 23:43:10 +02:00
|
|
|
check_pending_events(backend);
|
2020-07-31 00:22:18 +02:00
|
|
|
return 0;
|
2020-09-19 21:58:09 +02:00
|
|
|
|
|
|
|
error:
|
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, 6fa82930d0c5660eea3102989c765dc864514e36 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-20 23:43:10 +02:00
|
|
|
check_pending_events(backend);
|
2020-09-19 21:58:09 +02:00
|
|
|
return -1;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int switch_session(struct libseat *base, int session) {
|
|
|
|
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
|
2020-08-29 22:45:01 +02:00
|
|
|
if (backend->error) {
|
|
|
|
errno = ENOTCONN;
|
|
|
|
return -1;
|
|
|
|
}
|
2020-07-31 00:22:18 +02:00
|
|
|
if (session < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct proto_client_switch_session msg = {
|
|
|
|
.session = session,
|
|
|
|
};
|
|
|
|
struct proto_header header = {
|
|
|
|
.opcode = CLIENT_SWITCH_SESSION,
|
|
|
|
.size = sizeof msg,
|
|
|
|
};
|
2020-08-29 20:31:51 +02:00
|
|
|
if (conn_put(backend, &header, sizeof header) == -1 ||
|
|
|
|
conn_put(backend, &msg, sizeof msg) == -1 || conn_flush(backend) == -1) {
|
2020-07-31 00:22:18 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int disable_seat(struct libseat *base) {
|
|
|
|
struct backend_seatd *backend = backend_seatd_from_libseat_backend(base);
|
2020-08-29 22:45:01 +02:00
|
|
|
if (backend->error) {
|
|
|
|
errno = ENOTCONN;
|
|
|
|
return -1;
|
|
|
|
}
|
2020-07-31 00:22:18 +02:00
|
|
|
struct proto_header header = {
|
|
|
|
.opcode = CLIENT_DISABLE_SEAT,
|
|
|
|
.size = 0,
|
|
|
|
};
|
2020-08-29 20:31:51 +02:00
|
|
|
if (conn_put(backend, &header, sizeof header) == -1 || conn_flush(backend) == -1) {
|
2020-07-31 00:22:18 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-05 23:33:01 +02:00
|
|
|
const struct seat_impl seatd_impl = {
|
2020-07-31 00:22:18 +02:00
|
|
|
.open_seat = 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,
|
2021-07-09 00:16:10 +02:00
|
|
|
.dispatch = dispatch_and_execute,
|
2020-07-31 00:22:18 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef BUILTIN_ENABLED
|
2020-08-01 16:53:34 +02:00
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
static int set_deathsig(int signal);
|
|
|
|
|
|
|
|
#if defined(__linux__)
|
|
|
|
#include <sys/prctl.h>
|
|
|
|
|
|
|
|
static int set_deathsig(int signal) {
|
|
|
|
return prctl(PR_SET_PDEATHSIG, signal);
|
|
|
|
}
|
|
|
|
#elif defined(__FreeBSD__)
|
|
|
|
#include <sys/procctl.h>
|
|
|
|
|
|
|
|
static int set_deathsig(int signal) {
|
|
|
|
return procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &signal);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#error Unsupported platform
|
|
|
|
#endif
|
|
|
|
|
2021-08-15 14:31:50 +02:00
|
|
|
static struct libseat *builtin_open_seat(const struct libseat_seat_listener *listener, void *data) {
|
2020-07-31 00:22:18 +02:00
|
|
|
int fds[2];
|
2022-02-09 23:21:18 +01:00
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, fds) == -1) {
|
2020-08-28 22:40:42 +02:00
|
|
|
log_errorf("Could not create socket pair: %s", strerror(errno));
|
2020-07-31 00:22:18 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pid_t pid = fork();
|
|
|
|
if (pid == -1) {
|
2020-08-28 22:40:42 +02:00
|
|
|
log_errorf("Could not fork: %s", strerror(errno));
|
2020-07-31 00:22:18 +02:00
|
|
|
close(fds[0]);
|
|
|
|
close(fds[1]);
|
|
|
|
return NULL;
|
|
|
|
} else if (pid == 0) {
|
2022-03-03 14:41:52 +01:00
|
|
|
close(fds[1]);
|
2020-07-31 00:22:18 +02:00
|
|
|
int fd = fds[0];
|
2020-08-03 01:26:31 +02:00
|
|
|
int res = 0;
|
|
|
|
struct server server = {0};
|
|
|
|
if (server_init(&server) == -1) {
|
2020-08-28 22:40:42 +02:00
|
|
|
log_errorf("Could not init embedded seatd server: %s", strerror(errno));
|
2020-08-03 01:26:31 +02:00
|
|
|
res = 1;
|
|
|
|
goto error;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
2020-08-03 01:26:31 +02:00
|
|
|
if (server_add_client(&server, fd) == -1) {
|
2020-08-28 22:40:42 +02:00
|
|
|
log_errorf("Could not add client to embedded seatd server: %s",
|
|
|
|
strerror(errno));
|
2020-08-03 01:26:31 +02:00
|
|
|
res = 1;
|
|
|
|
goto server_error;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
2020-08-01 16:53:34 +02:00
|
|
|
set_deathsig(SIGTERM);
|
2020-08-03 01:26:31 +02:00
|
|
|
while (server.running) {
|
|
|
|
if (poller_poll(&server.poller) == -1) {
|
2020-08-28 22:40:42 +02:00
|
|
|
log_errorf("Could not poll server socket: %s", strerror(errno));
|
2020-08-03 01:26:31 +02:00
|
|
|
res = 1;
|
2020-08-31 22:44:02 +02:00
|
|
|
break;
|
2020-07-31 00:22:18 +02:00
|
|
|
}
|
|
|
|
}
|
2020-08-03 01:26:31 +02:00
|
|
|
server_error:
|
|
|
|
server_finish(&server);
|
|
|
|
error:
|
2020-07-31 00:22:18 +02:00
|
|
|
close(fd);
|
2020-08-03 01:26:31 +02:00
|
|
|
exit(res);
|
2020-07-31 00:22:18 +02:00
|
|
|
} else {
|
2022-03-03 14:41:52 +01:00
|
|
|
close(fds[0]);
|
2020-07-31 00:22:18 +02:00
|
|
|
int fd = fds[1];
|
|
|
|
return _open_seat(listener, data, fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-05 23:33:01 +02:00
|
|
|
const struct seat_impl builtin_impl = {
|
2020-07-31 00:22:18 +02:00
|
|
|
.open_seat = builtin_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,
|
2021-07-09 00:16:10 +02:00
|
|
|
.dispatch = dispatch_and_execute,
|
2020-07-31 00:22:18 +02:00
|
|
|
};
|
|
|
|
#endif
|