From c16e3c46793b4fec5f76bf2a61b8877e5ba25e61 Mon Sep 17 00:00:00 2001 From: June McEnroe Date: Mon, 20 Sep 2021 16:32:59 -0400 Subject: Replace kqueue with poll, remove libutil dependency This makes the server much more portable. --- Makefile | 2 +- server.c | 331 +++++++++++++++++++++++++++++---------------------------------- torus.1 | 9 +- 3 files changed, 154 insertions(+), 188 deletions(-) diff --git a/Makefile b/Makefile index fbc2757..9a8ae76 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CHROOT_GROUP = ${CHROOT_USER} CFLAGS += -std=c11 -Wall -Wextra -Wpedantic LDFLAGS = -static -LDLIBS = -lcursesw -lutil -lz +LDLIBS = -lcursesw -lz -include config.mk diff --git a/server.c b/server.c index 9d1fe16..e61f99e 100644 --- a/server.c +++ b/server.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 June McEnroe +/* Copyright (C) 2017, 2021 June McEnroe * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -14,27 +14,25 @@ * along with this program. If not, see . */ -#include - +#include #include #include #include +#include #include -#include #include +#include #include #include -#include +#include #include #include -#include #include #include #include #include #ifdef __FreeBSD__ -#include #include #endif @@ -53,9 +51,6 @@ static void tilesMap(const char *path) { if (tiles == MAP_FAILED) err(EX_OSERR, "mmap"); close(fd); - error = madvise(tiles, TilesSize, MADV_RANDOM); - if (error) err(EX_OSERR, "madvise"); - #ifdef MADV_NOCORE error = madvise(tiles, TilesSize, MADV_NOCORE); if (error) err(EX_OSERR, "madvise"); @@ -72,7 +67,7 @@ static struct Tile *tileGet(uint32_t tileX, uint32_t tileY) { return tile; } -static struct Tile *tileAccess(uint32_t tileX, uint32_t tileY) { +static const struct Tile *tileAccess(uint32_t tileX, uint32_t tileY) { struct Tile *tile = tileGet(tileX, tileY); tile->accessTime = time(NULL); tile->accessCount++; @@ -86,110 +81,92 @@ static struct Tile *tileModify(uint32_t tileX, uint32_t tileY) { return tile; } +enum { ClientsCap = 256 }; static struct Client { int fd; - uint32_t tileX; uint32_t tileY; uint8_t cellX; uint8_t cellY; - - struct Client *prev; - struct Client *next; -} *clientHead; +} clients[ClientsCap]; +static size_t clientsLen; static struct Client *clientAdd(int fd) { - struct Client *client = malloc(sizeof(*client)); - if (!client) err(EX_OSERR, "malloc"); - + if (clientsLen == ClientsCap) return NULL; + struct Client *client = &clients[clientsLen++]; client->fd = fd; client->tileX = TileInitX; client->tileY = TileInitY; client->cellX = CellInitX; client->cellY = CellInitY; - - client->prev = NULL; - if (clientHead) { - clientHead->prev = client; - client->next = clientHead; - } else { - client->next = NULL; - } - clientHead = client; - return client; } -static bool clientSend(const struct Client *client, struct ServerMessage msg) { - ssize_t size = send(client->fd, &msg, sizeof(msg), 0); - if (size < 0) return false; - - if (msg.type == ServerTile) { - struct Tile *tile = tileAccess(client->tileX, client->tileY); - size = send(client->fd, tile, sizeof(*tile), 0); - if (size < 0) return false; +static int +clientSend(const struct Client *client, const struct ServerMessage *msg) { + ssize_t len = send(client->fd, msg, sizeof(*msg), 0); + if (len < 0) return -1; + if (msg->type == ServerTile) { + const struct Tile *tile = tileAccess(client->tileX, client->tileY); + len = send(client->fd, tile, sizeof(*tile), 0); + if (len < 0) return -1; } - - return true; + return 0; } -static void clientCast(const struct Client *origin, struct ServerMessage msg) { - for (struct Client *client = clientHead; client; client = client->next) { - if (client == origin) continue; - if (client->tileX != origin->tileX) continue; - if (client->tileY != origin->tileY) continue; - clientSend(client, msg); +static void +clientCast(const struct Client *source, const struct ServerMessage *msg) { + for (size_t i = 0; i < clientsLen; ++i) { + if (&clients[i] == source) continue; + if (clients[i].tileX != source->tileX) continue; + if (clients[i].tileY != source->tileY) continue; + clientSend(&clients[i], msg); } } -static void clientRemove(struct Client *client) { - if (client->prev) client->prev->next = client->next; - if (client->next) client->next->prev = client->prev; - if (clientHead == client) clientHead = client->next; - +static void clientRemove(size_t i) { + struct Client client = clients[i]; + clients[i] = clients[--clientsLen]; + close(client.fd); struct ServerMessage msg = { .type = ServerCursor, .cursor = { - .oldCellX = client->cellX, .oldCellY = client->cellY, - .newCellX = CursorNone, .newCellY = CursorNone, + .oldCellX = client.cellX, .oldCellY = client.cellY, + .newCellX = CursorNone, .newCellY = CursorNone, }, }; - clientCast(client, msg); - - close(client->fd); - free(client); + clientCast(&client, &msg); } -static bool clientCursors(const struct Client *client) { +static int clientCursors(const struct Client *client) { struct ServerMessage msg = { .type = ServerCursor, - .cursor = { .oldCellX = CursorNone, .oldCellY = CursorNone }, + .cursor.oldCellX = CursorNone, + .cursor.oldCellY = CursorNone, }; - - for (struct Client *friend = clientHead; friend; friend = friend->next) { - if (friend == client) continue; - if (friend->tileX != client->tileX) continue; - if (friend->tileY != client->tileY) continue; - - msg.cursor.newCellX = friend->cellX; - msg.cursor.newCellY = friend->cellY; - if (!clientSend(client, msg)) return false; + for (size_t i = 0; i < clientsLen; ++i) { + if (&clients[i] == client) continue; + if (clients[i].tileX != client->tileX) continue; + if (clients[i].tileY != client->tileY) continue; + msg.cursor.newCellX = clients[i].cellX; + msg.cursor.newCellY = clients[i].cellY; + if (clientSend(client, &msg) < 0) return -1; } - return true; + return 0; } -static bool clientUpdate(struct Client *client, const struct Client *old) { +static int clientUpdate(struct Client *new, const struct Client *old) { struct ServerMessage msg = { .type = ServerMove, - .move = { .cellX = client->cellX, .cellY = client->cellY }, + .move.cellX = new->cellX, + .move.cellY = new->cellY, }; - if (!clientSend(client, msg)) return false; + if (clientSend(new, &msg) < 0) return -1; - if (client->tileX != old->tileX || client->tileY != old->tileY) { + if (new->tileX != old->tileX || new->tileY != old->tileY) { msg.type = ServerTile; - if (!clientSend(client, msg)) return false; - - if (!clientCursors(client)) return false; + if (clientSend(new, &msg) < 0) return -1; + if (clientCursors(new) < 0) return -1; msg = (struct ServerMessage) { .type = ServerCursor, @@ -198,32 +175,31 @@ static bool clientUpdate(struct Client *client, const struct Client *old) { .newCellX = CursorNone, .newCellY = CursorNone, }, }; - clientCast(old, msg); + clientCast(old, &msg); msg = (struct ServerMessage) { .type = ServerCursor, .cursor = { - .oldCellX = CursorNone, .oldCellY = CursorNone, - .newCellX = client->cellX, .newCellY = client->cellY, + .oldCellX = CursorNone, .oldCellY = CursorNone, + .newCellX = new->cellX, .newCellY = new->cellY, }, }; - clientCast(client, msg); + clientCast(new, &msg); } else { msg = (struct ServerMessage) { .type = ServerCursor, .cursor = { - .oldCellX = old->cellX, .oldCellY = old->cellY, - .newCellX = client->cellX, .newCellY = client->cellY, + .oldCellX = old->cellX, .oldCellY = old->cellY, + .newCellX = new->cellX, .newCellY = new->cellY, }, }; - clientCast(client, msg); + clientCast(new, &msg); } - - return true; + return 0; } -static bool clientMove(struct Client *client, int8_t dx, int8_t dy) { +static int clientMove(struct Client *client, int8_t dx, int8_t dy) { struct Client old = *client; if (dx > CellCols - client->cellX) dx = CellCols - client->cellX; @@ -251,9 +227,9 @@ static bool clientMove(struct Client *client, int8_t dx, int8_t dy) { client->cellY = CellRows - 1; } - if (client->tileX == TileCols) client->tileX = 0; + if (client->tileX == TileCols) client->tileX = 0; if (client->tileX == UINT32_MAX) client->tileX = TileCols - 1; - if (client->tileY == TileRows) client->tileY = 0; + if (client->tileY == TileRows) client->tileY = 0; if (client->tileY == UINT32_MAX) client->tileY = TileRows - 1; assert(client->cellX < CellCols); @@ -264,18 +240,17 @@ static bool clientMove(struct Client *client, int8_t dx, int8_t dy) { return clientUpdate(client, &old); } -static bool clientFlip(struct Client *client) { +static int clientFlip(struct Client *client) { struct Client old = *client; client->tileX = (client->tileX + TileCols / 2) % TileCols; client->tileY = (client->tileY + TileRows / 2) % TileRows; return clientUpdate(client, &old); } -static bool clientPut(const struct Client *client, uint8_t color, uint8_t cell) { +static int clientPut(const struct Client *client, uint8_t color, uint8_t cell) { struct Tile *tile = tileModify(client->tileX, client->tileY); tile->colors[client->cellY][client->cellX] = color; tile->cells[client->cellY][client->cellX] = cell; - struct ServerMessage msg = { .type = ServerPut, .put = { @@ -285,12 +260,12 @@ static bool clientPut(const struct Client *client, uint8_t color, uint8_t cell) .cell = cell, }, }; - bool success = clientSend(client, msg); - clientCast(client, msg); - return success; + int error = clientSend(client, &msg); + clientCast(client, &msg); + return error; } -static bool clientMap(const struct Client *client) { +static int clientMap(const struct Client *client) { int32_t mapY = (int32_t)client->tileY - MapRows / 2; int32_t mapX = (int32_t)client->tileX - MapCols / 2; @@ -354,13 +329,13 @@ static bool clientMap(const struct Client *client) { } struct ServerMessage msg = { .type = ServerMap }; - if (!clientSend(client, msg)) return false; - if (0 > send(client->fd, &map, sizeof(map), 0)) return false; - return true; + if (clientSend(client, &msg) < 0) return -1; + if (send(client->fd, &map, sizeof(map), 0) < 0) return -1; + return 0; } -static bool clientTele(struct Client *client, uint8_t port) { - if (port >= ARRAY_LEN(Ports)) return false; +static int clientTele(struct Client *client, uint8_t port) { + if (port >= ARRAY_LEN(Ports)) return -1; struct Client old = *client; client->tileX = Ports[port].tileX; client->tileY = Ports[port].tileY; @@ -375,8 +350,8 @@ int main(int argc, char *argv[]) { const char *dataPath = DefaultDataPath; const char *sockPath = DefaultSockPath; const char *pidPath = NULL; - int opt; - while (0 < (opt = getopt(argc, argv, "d:p:s:"))) { + + for (int opt; 0 < (opt = getopt(argc, argv, "d:p:s:"));) { switch (opt) { break; case 'd': dataPath = optarg; break; case 'p': pidPath = optarg; @@ -385,27 +360,38 @@ int main(int argc, char *argv[]) { } } -#ifdef __FreeBSD__ - struct pidfh *pid = NULL; + int pidFile = -1; if (pidPath) { - pid = pidfile_open(pidPath, 0600, NULL); - if (!pid) err(EX_CANTCREAT, "%s", pidPath); + pidFile = open(pidPath, O_WRONLY | O_CREAT | O_CLOEXEC, 0600); + if (pidFile < 0) err(EX_CANTCREAT, "%s", pidPath); + + error = flock(pidFile, LOCK_EX | LOCK_NB); + if (error && errno != EWOULDBLOCK) err(EX_IOERR, "%s", pidPath); + if (error) errx(EX_CANTCREAT, "%s: file is locked", pidPath); + + error = ftruncate(pidFile, 0); + if (error) err(EX_IOERR, "%s", pidPath); } -#endif tilesMap(dataPath); - int server = socket(PF_LOCAL, SOCK_STREAM, 0); + int server = socket(PF_UNIX, SOCK_STREAM, 0); if (server < 0) err(EX_OSERR, "socket"); error = unlink(sockPath); - if (error && errno != ENOENT) err(EX_IOERR, "%s", sockPath); + if (error && errno != ENOENT) err(EX_CANTCREAT, "%s", sockPath); - struct sockaddr_un addr = { .sun_family = AF_LOCAL }; - strlcpy(addr.sun_path, sockPath, sizeof(addr.sun_path)); + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", sockPath); error = bind(server, (struct sockaddr *)&addr, SUN_LEN(&addr)); if (error) err(EX_CANTCREAT, "%s", sockPath); + if (pidPath) { + error = daemon(0, 0); + if (error) err(EX_OSERR, "daemon"); + dprintf(pidFile, "%ju", (uintmax_t)getpid()); + } + #ifdef __FreeBSD__ error = cap_enter(); if (error) err(EX_OSERR, "cap_enter"); @@ -418,93 +404,80 @@ int main(int argc, char *argv[]) { ); error = cap_rights_limit(server, &rights); if (error) err(EX_OSERR, "cap_rights_limit"); - - if (pid) { - cap_rights_init(&rights, CAP_PWRITE, CAP_FSTAT, CAP_FTRUNCATE); - error = cap_rights_limit(pidfile_fileno(pid), &rights); - if (error) err(EX_OSERR, "cap_rights_limit"); - - // FIXME: daemon(3) can't chdir or open /dev/null in capability mode. - error = daemon(0, 0); - if (error) err(EX_OSERR, "daemon"); - pidfile_write(pid); - } #endif - error = listen(server, 0); + error = listen(server, -1); if (error) err(EX_OSERR, "listen"); - int kq = kqueue(); - if (kq < 0) err(EX_OSERR, "kqueue"); + signal(SIGPIPE, SIG_IGN); + struct pollfd fds[1 + ClientsCap] = { + { .fd = server, .events = POLLIN }, + }; + for (;;) { + for (size_t i = 0; i < clientsLen; ++i) { + fds[1 + i].fd = clients[i].fd; + fds[1 + i].events = POLLIN; + } + int nfds = poll(fds, 1 + clientsLen, -1); + if (nfds < 0 && errno != EINTR) err(EX_IOERR, "poll"); + if (nfds < 0) continue; + + for (size_t i = clientsLen - 1; i < clientsLen; --i) { + if (!fds[1 + i].revents) continue; + if (fds[1 + i].revents & (POLLHUP | POLLERR)) { + clientRemove(i); + continue; + } - struct kevent event; - EV_SET(&event, server, EVFILT_READ, EV_ADD, 0, 0, 0); - int nevents = kevent(kq, &event, 1, NULL, 0, NULL); - if (nevents < 0) err(EX_OSERR, "kevent"); + struct ClientMessage msg; + ssize_t len = recv(clients[i].fd, &msg, sizeof(msg), 0); + if (len != sizeof(msg)) { + clientRemove(i); + continue; + } - for (;;) { - nevents = kevent(kq, NULL, 0, &event, 1, NULL); - if (nevents < 0) err(EX_IOERR, "kevent"); + switch (msg.type) { + break; case ClientMove: { + error = clientMove(&clients[i], msg.move.dx, msg.move.dy); + } + break; case ClientFlip: { + error = clientFlip(&clients[i]); + } + break; case ClientPut: { + error = clientPut(&clients[i], msg.put.color, msg.put.cell); + } + break; case ClientMap: { + error = clientMap(&clients[i]); + } + break; case ClientTele: { + error = clientTele(&clients[i], msg.port); + } + break; default: error = -1; + } + if (error) clientRemove(i); + } - if (!event.udata) { + if (fds[0].revents) { int fd = accept(server, NULL, NULL); if (fd < 0) err(EX_IOERR, "accept"); fcntl(fd, F_SETFL, O_NONBLOCK); - int on = 1; - error = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); - if (error) err(EX_IOERR, "setsockopt"); - int size = 2 * sizeof(struct Tile); error = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); if (error) err(EX_IOERR, "setsockopt"); struct Client *client = clientAdd(fd); - - EV_SET(&event, fd, EVFILT_READ, EV_ADD, 0, 0, client); - nevents = kevent(kq, &event, 1, NULL, 0, NULL); - if (nevents < 0) err(EX_IOERR, "kevent"); + if (!client) { + close(fd); + continue; + } struct ServerMessage msg = { .type = ServerTile }; - bool success = clientSend(client, msg) - && clientMove(client, 0, 0) - && clientCursors(client); - if (!success) clientRemove(client); - - continue; - } - - struct Client *client = (struct Client *)event.udata; - if (event.flags & EV_EOF) { - clientRemove(client); - continue; - } - - struct ClientMessage msg; - ssize_t size = recv(client->fd, &msg, sizeof(msg), 0); - if (size != sizeof(msg)) { - clientRemove(client); - continue; - } - - bool success = false; - switch (msg.type) { - break; case ClientMove: { - success = clientMove(client, msg.move.dx, msg.move.dy); - } - break; case ClientFlip: { - success = clientFlip(client); - } - break; case ClientPut: { - success = clientPut(client, msg.put.color, msg.put.cell); - } - break; case ClientMap: { - success = clientMap(client); - } - break; case ClientTele: { - success = clientTele(client, msg.port); - } + error = 0 + || clientSend(client, &msg) + || clientMove(client, 0, 0) + || clientCursors(client); + if (error) clientRemove(clientsLen - 1); } - if (!success) clientRemove(client); } } diff --git a/torus.1 b/torus.1 index addbda8..9bf4b52 100644 --- a/torus.1 +++ b/torus.1 @@ -1,4 +1,4 @@ -.Dd January 8, 2019 +.Dd September 20, 2021 .Dt torus 1 .Os "Causal Agency" . @@ -113,8 +113,6 @@ Run a FastCGI worker for use with .It Fl p Ar pidfile Daemonize and write PID to .Ar pidfile . -Only available on -.Fx . . .It Fl s Ar sock Set path to UNIX-domain socket. @@ -129,11 +127,6 @@ Set tile Y coordinate to render. .El . .Sh IMPLEMENTATION NOTES -This software targets -.Fx -and Darwin. -. -.Pp .Pa help.h contains tile data for the help page and can be generated from the first tile of -- cgit 1.4.1