diff options
Diffstat (limited to 'seatd/server.c')
-rw-r--r-- | seatd/server.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/seatd/server.c b/seatd/server.c new file mode 100644 index 0000000..3db9b69 --- /dev/null +++ b/seatd/server.c @@ -0,0 +1,232 @@ +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <signal.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/un.h> +#include <unistd.h> + +#include "client.h" +#include "list.h" +#include "log.h" +#include "poller.h" +#include "seat.h" +#include "server.h" +#include "terminal.h" + +#define LISTEN_BACKLOG 16 + +static int server_handle_vt_acq(int signal, void *data); +static int server_handle_vt_rel(int signal, void *data); +static int server_handle_kill(int signal, void *data); + +struct server *server_create(void) { + struct poller *poller = poller_create(); + if (poller == NULL) { + log_error("could not create poller"); + return NULL; + } + struct server *server = calloc(1, sizeof(struct server)); + if (server == NULL) { + return NULL; + } + server->poller = poller; + + list_init(&server->seats); + + if (poller_add_signal(poller, SIGUSR1, server_handle_vt_rel, server) == NULL || + poller_add_signal(poller, SIGUSR2, server_handle_vt_acq, server) == NULL || + poller_add_signal(poller, SIGINT, server_handle_kill, server) == NULL || + poller_add_signal(poller, SIGTERM, server_handle_kill, server) == NULL) { + server_destroy(server); + return NULL; + } + + char *vtenv = getenv("SEATD_VTBOUND"); + + // TODO: create more seats: + struct seat *seat = seat_create("seat0", vtenv == NULL || strcmp(vtenv, "1") == 0); + if (seat == NULL) { + server_destroy(server); + return NULL; + } + + list_add(&server->seats, seat); + server->running = true; + return server; +} + +void server_destroy(struct server *server) { + assert(server); + for (size_t idx = 0; idx < server->seats.length; idx++) { + struct seat *seat = server->seats.items[idx]; + seat_destroy(seat); + } + list_free(&server->seats); + if (server->poller != NULL) { + poller_destroy(server->poller); + server->poller = NULL; + } + free(server); +} + +struct seat *server_get_seat(struct server *server, const char *seat_name) { + for (size_t idx = 0; idx < server->seats.length; idx++) { + struct seat *seat = server->seats.items[idx]; + if (strcmp(seat->seat_name, seat_name) == 0) { + return seat; + } + } + return NULL; +} + +static int server_handle_vt_acq(int signal, void *data) { + (void)signal; + struct server *server = data; + struct seat *seat = server_get_seat(server, "seat0"); + if (seat == NULL) { + return -1; + } + + seat_activate(seat); + return 0; +} + +static int server_handle_vt_rel(int signal, void *data) { + (void)signal; + struct server *server = data; + struct seat *seat = server_get_seat(server, "seat0"); + if (seat == NULL) { + return -1; + } + + seat_prepare_vt_switch(seat); + return 0; +} + +static int server_handle_kill(int signal, void *data) { + (void)signal; + struct server *server = data; + server->running = false; + return 0; +} + +static int set_nonblock(int fd) { + int flags; + if ((flags = fcntl(fd, F_GETFD)) == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { + log_errorf("could not set FD_CLOEXEC on socket: %s", strerror(errno)); + return -1; + } + if ((flags = fcntl(fd, F_GETFL)) == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + log_errorf("could not set O_NONBLOCK on socket: %s", strerror(errno)); + return -1; + } + return 0; +} + +static int server_handle_connection(int fd, uint32_t mask, void *data) { + struct server *server = data; + if (mask & (EVENT_ERROR | EVENT_HANGUP)) { + close(fd); + log_errorf("server socket recieved an error: %s", strerror(errno)); + exit(1); + } + + if (mask & EVENT_READABLE) { + int new_fd = accept(fd, NULL, NULL); + if (fd == -1) { + log_errorf("could not accept client connection: %s", strerror(errno)); + return 0; + } + + if (set_nonblock(new_fd) != 0) { + close(new_fd); + log_errorf("could not prepare new client socket: %s", strerror(errno)); + return 0; + } + + struct client *client = client_create(server, new_fd); + client->event_source = poller_add_fd(server->poller, new_fd, EVENT_READABLE, + client_handle_connection, client); + if (client->event_source == NULL) { + client_destroy(client); + log_errorf("could not add client socket to poller: %s", strerror(errno)); + return 0; + } + log_infof("new client connected (pid: %d, uid: %d, gid: %d)", client->pid, + client->uid, client->gid); + } + return 0; +} + +int server_add_client(struct server *server, int fd) { + if (set_nonblock(fd) != 0) { + close(fd); + log_errorf("could not prepare new client socket: %s", strerror(errno)); + return -1; + } + + struct client *client = client_create(server, fd); + client->event_source = + poller_add_fd(server->poller, fd, EVENT_READABLE, client_handle_connection, client); + if (client->event_source == NULL) { + client_destroy(client); + log_errorf("could not add client socket to poller: %s", strerror(errno)); + return -1; + } + return 0; +} + +int server_listen(struct server *server, const char *path) { + union { + struct sockaddr_un unix; + struct sockaddr generic; + } addr = {0}; + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + log_errorf("could not create socket: %s", strerror(errno)); + return -1; + } + if (set_nonblock(fd) == -1) { + close(fd); + log_errorf("could not prepare socket: %s", strerror(errno)); + return -1; + } + + addr.unix.sun_family = AF_UNIX; + strncpy(addr.unix.sun_path, path, sizeof addr.unix.sun_path - 1); + socklen_t size = offsetof(struct sockaddr_un, sun_path) + strlen(addr.unix.sun_path); + if (bind(fd, &addr.generic, size) == -1) { + log_errorf("could not bind socket: %s", strerror(errno)); + close(fd); + return -1; + } + if (listen(fd, LISTEN_BACKLOG) == -1) { + log_errorf("could not listen on socket: %s", strerror(errno)); + close(fd); + return -1; + } + struct group *videogrp = getgrnam("video"); + if (videogrp != NULL) { + if (chown(path, 0, videogrp->gr_gid) == -1) { + log_errorf("could not chown socket to video group: %s", strerror(errno)); + } else if (chmod(path, 0770) == -1) { + log_errorf("could not chmod socket: %s", strerror(errno)); + } + } else { + log_errorf("could not get video group: %s", strerror(errno)); + } + if (poller_add_fd(server->poller, fd, EVENT_READABLE, server_handle_connection, server) == + NULL) { + log_errorf("could not add socket to poller: %s", strerror(errno)); + close(fd); + return -1; + } + return 0; +} |