Collaborative ASCII art https://ascii.town
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

511 lines
13KB

  1. /* Copyright (C) 2017 C. McEnroe <june@causal.agency>
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU Affero General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <sys/types.h>
  17. #include <err.h>
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <signal.h>
  21. #include <stdbool.h>
  22. #include <stdint.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <sys/event.h>
  26. #include <sys/mman.h>
  27. #include <sys/socket.h>
  28. #include <sys/time.h>
  29. #include <sys/un.h>
  30. #include <sysexits.h>
  31. #include <time.h>
  32. #include <unistd.h>
  33. #ifdef __FreeBSD__
  34. #include <libutil.h>
  35. #include <sys/capsicum.h>
  36. #endif
  37. #include "torus.h"
  38. static struct Tile *tiles;
  39. static void tilesMap(const char *path) {
  40. int fd = open(path, O_CREAT | O_RDWR, 0644);
  41. if (fd < 0) err(EX_CANTCREAT, "%s", path);
  42. int error = ftruncate(fd, TilesSize);
  43. if (error) err(EX_IOERR, "%s", path);
  44. tiles = mmap(NULL, TilesSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  45. if (tiles == MAP_FAILED) err(EX_OSERR, "mmap");
  46. close(fd);
  47. error = madvise(tiles, TilesSize, MADV_RANDOM);
  48. if (error) err(EX_OSERR, "madvise");
  49. #ifdef MADV_NOCORE
  50. error = madvise(tiles, TilesSize, MADV_NOCORE);
  51. if (error) err(EX_OSERR, "madvise");
  52. #endif
  53. }
  54. static struct Tile *tileGet(uint32_t tileX, uint32_t tileY) {
  55. struct Tile *tile = &tiles[tileY * TileRows + tileX];
  56. if (!tile->createTime) {
  57. memset(tile->cells, ' ', CellsSize);
  58. memset(tile->colors, ColorWhite, CellsSize);
  59. tile->createTime = time(NULL);
  60. }
  61. return tile;
  62. }
  63. static struct Tile *tileAccess(uint32_t tileX, uint32_t tileY) {
  64. struct Tile *tile = tileGet(tileX, tileY);
  65. tile->accessTime = time(NULL);
  66. tile->accessCount++;
  67. return tile;
  68. }
  69. static struct Tile *tileModify(uint32_t tileX, uint32_t tileY) {
  70. struct Tile *tile = tileGet(tileX, tileY);
  71. tile->modifyTime = time(NULL);
  72. tile->modifyCount++;
  73. return tile;
  74. }
  75. static struct Client {
  76. int fd;
  77. uint32_t tileX;
  78. uint32_t tileY;
  79. uint8_t cellX;
  80. uint8_t cellY;
  81. struct Client *prev;
  82. struct Client *next;
  83. } *clientHead;
  84. static struct Client *clientAdd(int fd) {
  85. struct Client *client = malloc(sizeof(*client));
  86. if (!client) err(EX_OSERR, "malloc");
  87. client->fd = fd;
  88. client->tileX = TileInitX;
  89. client->tileY = TileInitY;
  90. client->cellX = CellInitX;
  91. client->cellY = CellInitY;
  92. client->prev = NULL;
  93. if (clientHead) {
  94. clientHead->prev = client;
  95. client->next = clientHead;
  96. } else {
  97. client->next = NULL;
  98. }
  99. clientHead = client;
  100. return client;
  101. }
  102. static bool clientSend(const struct Client *client, struct ServerMessage msg) {
  103. ssize_t size = send(client->fd, &msg, sizeof(msg), 0);
  104. if (size < 0) return false;
  105. if (msg.type == ServerTile) {
  106. struct Tile *tile = tileAccess(client->tileX, client->tileY);
  107. size = send(client->fd, tile, sizeof(*tile), 0);
  108. if (size < 0) return false;
  109. }
  110. return true;
  111. }
  112. static void clientCast(const struct Client *origin, struct ServerMessage msg) {
  113. for (struct Client *client = clientHead; client; client = client->next) {
  114. if (client == origin) continue;
  115. if (client->tileX != origin->tileX) continue;
  116. if (client->tileY != origin->tileY) continue;
  117. clientSend(client, msg);
  118. }
  119. }
  120. static void clientRemove(struct Client *client) {
  121. if (client->prev) client->prev->next = client->next;
  122. if (client->next) client->next->prev = client->prev;
  123. if (clientHead == client) clientHead = client->next;
  124. struct ServerMessage msg = {
  125. .type = ServerCursor,
  126. .cursor = {
  127. .oldCellX = client->cellX, .oldCellY = client->cellY,
  128. .newCellX = CursorNone, .newCellY = CursorNone,
  129. },
  130. };
  131. clientCast(client, msg);
  132. close(client->fd);
  133. free(client);
  134. }
  135. static bool clientCursors(const struct Client *client) {
  136. struct ServerMessage msg = {
  137. .type = ServerCursor,
  138. .cursor = { .oldCellX = CursorNone, .oldCellY = CursorNone },
  139. };
  140. for (struct Client *friend = clientHead; friend; friend = friend->next) {
  141. if (friend == client) continue;
  142. if (friend->tileX != client->tileX) continue;
  143. if (friend->tileY != client->tileY) continue;
  144. msg.cursor.newCellX = friend->cellX;
  145. msg.cursor.newCellY = friend->cellY;
  146. if (!clientSend(client, msg)) return false;
  147. }
  148. return true;
  149. }
  150. static bool clientUpdate(struct Client *client, const struct Client *old) {
  151. struct ServerMessage msg = {
  152. .type = ServerMove,
  153. .move = { .cellX = client->cellX, .cellY = client->cellY },
  154. };
  155. if (!clientSend(client, msg)) return false;
  156. if (client->tileX != old->tileX || client->tileY != old->tileY) {
  157. msg.type = ServerTile;
  158. if (!clientSend(client, msg)) return false;
  159. if (!clientCursors(client)) return false;
  160. msg = (struct ServerMessage) {
  161. .type = ServerCursor,
  162. .cursor = {
  163. .oldCellX = old->cellX, .oldCellY = old->cellY,
  164. .newCellX = CursorNone, .newCellY = CursorNone,
  165. },
  166. };
  167. clientCast(old, msg);
  168. msg = (struct ServerMessage) {
  169. .type = ServerCursor,
  170. .cursor = {
  171. .oldCellX = CursorNone, .oldCellY = CursorNone,
  172. .newCellX = client->cellX, .newCellY = client->cellY,
  173. },
  174. };
  175. clientCast(client, msg);
  176. } else {
  177. msg = (struct ServerMessage) {
  178. .type = ServerCursor,
  179. .cursor = {
  180. .oldCellX = old->cellX, .oldCellY = old->cellY,
  181. .newCellX = client->cellX, .newCellY = client->cellY,
  182. },
  183. };
  184. clientCast(client, msg);
  185. }
  186. return true;
  187. }
  188. static bool clientMove(struct Client *client, int8_t dx, int8_t dy) {
  189. struct Client old = *client;
  190. if (dx > CellCols - client->cellX) dx = CellCols - client->cellX;
  191. if (dx < -client->cellX - 1) dx = -client->cellX - 1;
  192. if (dy > CellRows - client->cellY) dy = CellRows - client->cellY;
  193. if (dy < -client->cellY - 1) dy = -client->cellY - 1;
  194. client->cellX += dx;
  195. client->cellY += dy;
  196. if (client->cellX == CellCols) {
  197. client->tileX++;
  198. client->cellX = 0;
  199. }
  200. if (client->cellX == UINT8_MAX) {
  201. client->tileX--;
  202. client->cellX = CellCols - 1;
  203. }
  204. if (client->cellY == CellRows) {
  205. client->tileY++;
  206. client->cellY = 0;
  207. }
  208. if (client->cellY == UINT8_MAX) {
  209. client->tileY--;
  210. client->cellY = CellRows - 1;
  211. }
  212. if (client->tileX == TileCols) client->tileX = 0;
  213. if (client->tileX == UINT32_MAX) client->tileX = TileCols - 1;
  214. if (client->tileY == TileRows) client->tileY = 0;
  215. if (client->tileY == UINT32_MAX) client->tileY = TileRows - 1;
  216. assert(client->cellX < CellCols);
  217. assert(client->cellY < CellRows);
  218. assert(client->tileX < TileCols);
  219. assert(client->tileY < TileRows);
  220. return clientUpdate(client, &old);
  221. }
  222. static bool clientFlip(struct Client *client) {
  223. struct Client old = *client;
  224. client->tileX = (client->tileX + TileCols / 2) % TileCols;
  225. client->tileY = (client->tileY + TileRows / 2) % TileRows;
  226. return clientUpdate(client, &old);
  227. }
  228. static bool clientPut(const struct Client *client, uint8_t color, uint8_t cell) {
  229. struct Tile *tile = tileModify(client->tileX, client->tileY);
  230. tile->colors[client->cellY][client->cellX] = color;
  231. tile->cells[client->cellY][client->cellX] = cell;
  232. struct ServerMessage msg = {
  233. .type = ServerPut,
  234. .put = {
  235. .cellX = client->cellX,
  236. .cellY = client->cellY,
  237. .color = color,
  238. .cell = cell,
  239. },
  240. };
  241. bool success = clientSend(client, msg);
  242. clientCast(client, msg);
  243. return success;
  244. }
  245. static bool clientMap(const struct Client *client) {
  246. int32_t mapY = (int32_t)client->tileY - MapRows / 2;
  247. int32_t mapX = (int32_t)client->tileX - MapCols / 2;
  248. time_t now = time(NULL);
  249. struct Map map = {
  250. .now = now,
  251. .min = {
  252. .createTime = now,
  253. .modifyTime = now,
  254. .accessTime = now,
  255. .modifyCount = UINT32_MAX,
  256. .accessCount = UINT32_MAX,
  257. },
  258. };
  259. for (int32_t y = 0; y < MapRows; ++y) {
  260. for (int32_t x = 0; x < MapCols; ++x) {
  261. uint32_t tileY = ((mapY + y) % TileRows + TileRows) % TileRows;
  262. uint32_t tileX = ((mapX + x) % TileCols + TileCols) % TileCols;
  263. struct Meta meta = tileMeta(&tiles[tileY * TileRows + tileX]);
  264. if (meta.createTime > 1) {
  265. if (meta.createTime < map.min.createTime) {
  266. map.min.createTime = meta.createTime;
  267. }
  268. if (meta.createTime > map.max.createTime) {
  269. map.max.createTime = meta.createTime;
  270. }
  271. }
  272. if (meta.modifyTime) {
  273. if (meta.modifyTime < map.min.modifyTime) {
  274. map.min.modifyTime = meta.modifyTime;
  275. }
  276. if (meta.modifyTime > map.max.modifyTime) {
  277. map.max.modifyTime = meta.modifyTime;
  278. }
  279. }
  280. if (meta.accessTime) {
  281. if (meta.accessTime < map.min.accessTime) {
  282. map.min.accessTime = meta.accessTime;
  283. }
  284. if (meta.accessTime > map.max.accessTime) {
  285. map.max.accessTime = meta.accessTime;
  286. }
  287. }
  288. if (meta.modifyCount < map.min.modifyCount) {
  289. map.min.modifyCount = meta.modifyCount;
  290. }
  291. if (meta.modifyCount > map.max.modifyCount) {
  292. map.max.modifyCount = meta.modifyCount;
  293. }
  294. if (meta.accessCount < map.min.accessCount) {
  295. map.min.accessCount = meta.accessCount;
  296. }
  297. if (meta.accessCount > map.max.accessCount) {
  298. map.max.accessCount = meta.accessCount;
  299. }
  300. map.meta[y][x] = meta;
  301. }
  302. }
  303. struct ServerMessage msg = { .type = ServerMap };
  304. if (!clientSend(client, msg)) return false;
  305. if (0 > send(client->fd, &map, sizeof(map), 0)) return false;
  306. return true;
  307. }
  308. static bool clientTele(struct Client *client, uint8_t port) {
  309. if (port >= ARRAY_LEN(Ports)) return false;
  310. struct Client old = *client;
  311. client->tileX = Ports[port].tileX;
  312. client->tileY = Ports[port].tileY;
  313. client->cellX = CellInitX;
  314. client->cellY = CellInitY;
  315. return clientUpdate(client, &old);
  316. }
  317. int main(int argc, char *argv[]) {
  318. int error;
  319. const char *dataPath = DefaultDataPath;
  320. const char *sockPath = DefaultSockPath;
  321. const char *pidPath = NULL;
  322. int opt;
  323. while (0 < (opt = getopt(argc, argv, "d:p:s:"))) {
  324. switch (opt) {
  325. break; case 'd': dataPath = optarg;
  326. break; case 'p': pidPath = optarg;
  327. break; case 's': sockPath = optarg;
  328. break; default: return EX_USAGE;
  329. }
  330. }
  331. #ifdef __FreeBSD__
  332. struct pidfh *pid = NULL;
  333. if (pidPath) {
  334. pid = pidfile_open(pidPath, 0600, NULL);
  335. if (!pid) err(EX_CANTCREAT, "%s", pidPath);
  336. }
  337. #endif
  338. tilesMap(dataPath);
  339. int server = socket(PF_LOCAL, SOCK_STREAM, 0);
  340. if (server < 0) err(EX_OSERR, "socket");
  341. error = unlink(sockPath);
  342. if (error && errno != ENOENT) err(EX_IOERR, "%s", sockPath);
  343. struct sockaddr_un addr = { .sun_family = AF_LOCAL };
  344. strlcpy(addr.sun_path, sockPath, sizeof(addr.sun_path));
  345. error = bind(server, (struct sockaddr *)&addr, SUN_LEN(&addr));
  346. if (error) err(EX_CANTCREAT, "%s", sockPath);
  347. #ifdef __FreeBSD__
  348. error = cap_enter();
  349. if (error) err(EX_OSERR, "cap_enter");
  350. cap_rights_t rights;
  351. cap_rights_init(
  352. &rights,
  353. CAP_LISTEN, CAP_ACCEPT, CAP_EVENT,
  354. CAP_READ, CAP_WRITE, CAP_SETSOCKOPT
  355. );
  356. error = cap_rights_limit(server, &rights);
  357. if (error) err(EX_OSERR, "cap_rights_limit");
  358. if (pid) {
  359. cap_rights_init(&rights, CAP_PWRITE, CAP_FSTAT, CAP_FTRUNCATE);
  360. error = cap_rights_limit(pidfile_fileno(pid), &rights);
  361. if (error) err(EX_OSERR, "cap_rights_limit");
  362. // FIXME: daemon(3) can't chdir or open /dev/null in capability mode.
  363. error = daemon(0, 0);
  364. if (error) err(EX_OSERR, "daemon");
  365. pidfile_write(pid);
  366. }
  367. #endif
  368. error = listen(server, 0);
  369. if (error) err(EX_OSERR, "listen");
  370. int kq = kqueue();
  371. if (kq < 0) err(EX_OSERR, "kqueue");
  372. struct kevent event;
  373. EV_SET(&event, server, EVFILT_READ, EV_ADD, 0, 0, 0);
  374. int nevents = kevent(kq, &event, 1, NULL, 0, NULL);
  375. if (nevents < 0) err(EX_OSERR, "kevent");
  376. for (;;) {
  377. nevents = kevent(kq, NULL, 0, &event, 1, NULL);
  378. if (nevents < 0) err(EX_IOERR, "kevent");
  379. if (!event.udata) {
  380. int fd = accept(server, NULL, NULL);
  381. if (fd < 0) err(EX_IOERR, "accept");
  382. fcntl(fd, F_SETFL, O_NONBLOCK);
  383. int on = 1;
  384. error = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));
  385. if (error) err(EX_IOERR, "setsockopt");
  386. int size = 2 * sizeof(struct Tile);
  387. error = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
  388. if (error) err(EX_IOERR, "setsockopt");
  389. struct Client *client = clientAdd(fd);
  390. EV_SET(&event, fd, EVFILT_READ, EV_ADD, 0, 0, client);
  391. nevents = kevent(kq, &event, 1, NULL, 0, NULL);
  392. if (nevents < 0) err(EX_IOERR, "kevent");
  393. struct ServerMessage msg = { .type = ServerTile };
  394. bool success = clientSend(client, msg)
  395. && clientMove(client, 0, 0)
  396. && clientCursors(client);
  397. if (!success) clientRemove(client);
  398. continue;
  399. }
  400. struct Client *client = (struct Client *)event.udata;
  401. if (event.flags & EV_EOF) {
  402. clientRemove(client);
  403. continue;
  404. }
  405. struct ClientMessage msg;
  406. ssize_t size = recv(client->fd, &msg, sizeof(msg), 0);
  407. if (size != sizeof(msg)) {
  408. clientRemove(client);
  409. continue;
  410. }
  411. bool success = false;
  412. switch (msg.type) {
  413. break; case ClientMove: {
  414. success = clientMove(client, msg.move.dx, msg.move.dy);
  415. }
  416. break; case ClientFlip: {
  417. success = clientFlip(client);
  418. }
  419. break; case ClientPut: {
  420. success = clientPut(client, msg.put.color, msg.put.cell);
  421. }
  422. break; case ClientMap: {
  423. success = clientMap(client);
  424. }
  425. break; case ClientTele: {
  426. success = clientTele(client, msg.port);
  427. }
  428. }
  429. if (!success) clientRemove(client);
  430. }
  431. }