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.
 
 
 
 

507 lines
13 KiB

  1. #include "server.h"
  2. #include <string.h>
  3. #include <unistd.h>
  4. #include <errno.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <sys/epoll.h>
  8. #include <linux/un.h>
  9. #include <tllist.h>
  10. #define LOG_MODULE "server"
  11. #define LOG_ENABLE_DBG 0
  12. #include "log.h"
  13. #include "shm.h"
  14. #include "terminal.h"
  15. #include "wayland.h"
  16. #include "xmalloc.h"
  17. struct client;
  18. struct server {
  19. const struct config *conf;
  20. struct fdm *fdm;
  21. struct reaper *reaper;
  22. struct wayland *wayl;
  23. int fd;
  24. const char *sock_path;
  25. tll(struct client *) clients;
  26. };
  27. struct client {
  28. struct server *server;
  29. int fd;
  30. struct {
  31. uint8_t *data;
  32. size_t left;
  33. size_t idx;
  34. } buffer;
  35. struct config conf;
  36. struct terminal *term;
  37. };
  38. static void
  39. client_destroy(struct client *client)
  40. {
  41. if (client == NULL)
  42. return;
  43. if (client->term != NULL) {
  44. LOG_WARN("client FD=%d: terminal still alive", client->fd);
  45. term_destroy(client->term);
  46. }
  47. if (client->fd != -1) {
  48. LOG_DBG("client FD=%d: disconnected", client->fd);
  49. fdm_del(client->server->fdm, client->fd);
  50. }
  51. tll_foreach(client->server->clients, it) {
  52. if (it->item == client) {
  53. tll_remove(client->server->clients, it);
  54. break;
  55. }
  56. }
  57. free(client->buffer.data);
  58. /* TODO: clone server conf completely, so that we can just call
  59. * conf_destroy() here */
  60. free(client->conf.term);
  61. free(client->conf.title);
  62. free(client->conf.app_id);
  63. free(client);
  64. }
  65. static void
  66. client_send_exit_code(struct client *client, int exit_code)
  67. {
  68. if (client->fd == -1)
  69. return;
  70. if (write(client->fd, &exit_code, sizeof(exit_code)) != sizeof(exit_code))
  71. LOG_ERRNO("failed to write slave exit code to client");
  72. }
  73. static void
  74. term_shutdown_handler(void *data, int exit_code)
  75. {
  76. struct client *client = data;
  77. struct wl_shm *shm = client->server->wayl->shm;
  78. struct terminal *term = client->term;
  79. shm_purge(shm, shm_cookie_grid(term));
  80. shm_purge(shm, shm_cookie_search(term));
  81. for (enum csd_surface surf = 0; surf < CSD_SURF_COUNT; surf++)
  82. shm_purge(shm, shm_cookie_csd(term, surf));
  83. client_send_exit_code(client, exit_code);
  84. client->term = NULL;
  85. client_destroy(client);
  86. }
  87. static bool
  88. fdm_client(struct fdm *fdm, int fd, int events, void *data)
  89. {
  90. struct client *client = data;
  91. struct server *server = client->server;
  92. char **argv = NULL;
  93. int argc = 0;
  94. if (events & EPOLLHUP)
  95. goto shutdown;
  96. assert(events & EPOLLIN);
  97. if (client->term != NULL) {
  98. uint8_t dummy[128];
  99. ssize_t count = read(fd, dummy, sizeof(dummy));
  100. LOG_WARN("client unexpectedly sent %zd bytes", count);
  101. return true; /* TODO: shutdown instead? */
  102. }
  103. if (client->buffer.data == NULL) {
  104. /*
  105. * We haven't received any data yet - the first thing the
  106. * client sends is the total size of the initialization
  107. * data.
  108. */
  109. uint32_t total_len;
  110. ssize_t count = recv(fd, &total_len, sizeof(total_len), 0);
  111. if (count < 0) {
  112. LOG_ERRNO("failed to read total length");
  113. goto shutdown;
  114. }
  115. if (count != sizeof(total_len)) {
  116. LOG_ERR("client did not send setup packet size");
  117. goto shutdown;
  118. }
  119. const uint32_t max_size = 128 * 1024;
  120. if (total_len > max_size) {
  121. LOG_ERR("client wants to send too large setup packet (%u > %u)",
  122. total_len, max_size);
  123. goto shutdown;
  124. }
  125. LOG_DBG("total len: %u", total_len);
  126. client->buffer.data = xmalloc(total_len + 1);
  127. client->buffer.left = total_len;
  128. client->buffer.idx = 0;
  129. /* Prevent our strlen() calls to run outside */
  130. client->buffer.data[total_len] = '\0';
  131. return true; /* Let FDM trigger again when we have more data */
  132. }
  133. /* Keep filling our buffer of initialization data */
  134. ssize_t count = recv(
  135. fd, &client->buffer.data[client->buffer.idx], client->buffer.left, 0);
  136. if (count < 0) {
  137. LOG_ERRNO("failed to read");
  138. goto shutdown;
  139. }
  140. client->buffer.idx += count;
  141. client->buffer.left -= count;
  142. if (client->buffer.left > 0) {
  143. /* Not done yet */
  144. return true;
  145. }
  146. /* All initialization data received - time to instantiate a terminal! */
  147. assert(client->term == NULL);
  148. assert(client->buffer.data != NULL);
  149. assert(client->buffer.left == 0);
  150. /*
  151. * Parse the received buffer, verifying lengths etc
  152. */
  153. #define CHECK_BUF(sz) do { \
  154. if (p + (sz) > end) \
  155. goto shutdown; \
  156. } while (0)
  157. uint8_t *p = client->buffer.data;
  158. const uint8_t *end = &client->buffer.data[client->buffer.idx];
  159. uint16_t cwd_len;
  160. CHECK_BUF(sizeof(cwd_len));
  161. memcpy(&cwd_len, p, sizeof(cwd_len));
  162. p += sizeof(cwd_len);
  163. CHECK_BUF(cwd_len);
  164. const char *cwd = (const char *)p; p += cwd_len;
  165. LOG_DBG("CWD = %.*s", cwd_len, cwd);
  166. if (cwd_len != strlen(cwd) + 1) {
  167. LOG_ERR("CWD length mismatch: indicated = %u, actual = %zu",
  168. cwd_len - 1, strlen(cwd));
  169. goto shutdown;
  170. }
  171. uint16_t term_env_len;
  172. CHECK_BUF(sizeof(term_env_len));
  173. memcpy(&term_env_len, p, sizeof(term_env_len));
  174. p += sizeof(term_env_len);
  175. CHECK_BUF(term_env_len);
  176. const char *term_env = (const char *)p; p += term_env_len;
  177. LOG_DBG("TERM = %.*s", term_env_len, term_env);
  178. if (term_env_len != strlen(term_env) + 1) {
  179. LOG_ERR("TERM length mismatch: indicated = %u, actual = %zu",
  180. term_env_len - 1, strlen(term_env));
  181. goto shutdown;
  182. }
  183. uint16_t title_len;
  184. CHECK_BUF(sizeof(title_len));
  185. memcpy(&title_len, p, sizeof(title_len));
  186. p += sizeof(title_len);
  187. CHECK_BUF(title_len);
  188. const char *title = (const char *)p; p += title_len;
  189. LOG_DBG("app-id = %.*s", title_len, title);
  190. if (title_len != strlen(title) + 1) {
  191. LOG_ERR("title length mismatch: indicated = %u, actual = %zu",
  192. title_len - 1, strlen(title));
  193. goto shutdown;
  194. }
  195. uint16_t app_id_len;
  196. CHECK_BUF(sizeof(app_id_len));
  197. memcpy(&app_id_len, p, sizeof(app_id_len));
  198. p += sizeof(app_id_len);
  199. CHECK_BUF(app_id_len);
  200. const char *app_id = (const char *)p; p += app_id_len;
  201. LOG_DBG("app-id = %.*s", app_id_len, app_id);
  202. if (app_id_len != strlen(app_id) + 1) {
  203. LOG_ERR("app-id length mismatch: indicated = %u, actual = %zu",
  204. app_id_len - 1, strlen(app_id));
  205. goto shutdown;
  206. }
  207. CHECK_BUF(sizeof(uint8_t));
  208. const uint8_t maximized = *(const uint8_t *)p; p += sizeof(maximized);
  209. CHECK_BUF(sizeof(uint8_t));
  210. const uint8_t fullscreen = *(const uint8_t *)p; p += sizeof(fullscreen);
  211. CHECK_BUF(sizeof(uint8_t));
  212. const uint8_t hold = *(const uint8_t *)p; p += sizeof(hold);
  213. CHECK_BUF(sizeof(uint8_t));
  214. const uint8_t login_shell = *(const uint8_t *)p; p += sizeof(login_shell);
  215. CHECK_BUF(sizeof(argc));
  216. memcpy(&argc, p, sizeof(argc));
  217. p += sizeof(argc);
  218. argv = xcalloc(argc + 1, sizeof(argv[0]));
  219. LOG_DBG("argc = %d", argc);
  220. for (int i = 0; i < argc; i++) {
  221. uint16_t len;
  222. CHECK_BUF(sizeof(len));
  223. memcpy(&len, p, sizeof(len));
  224. p += sizeof(len);
  225. CHECK_BUF(len);
  226. argv[i] = (char *)p; p += strlen(argv[i]) + 1;
  227. LOG_DBG("argv[%d] = %s", i, argv[i]);
  228. if (len != strlen(argv[i]) + 1) {
  229. LOG_ERR("argv[%d] length mismatch: indicated = %u, actual = %zu",
  230. i, len - 1, strlen(argv[i]));
  231. goto shutdown;
  232. }
  233. }
  234. argv[argc] = NULL;
  235. #undef CHECK_BUF
  236. client->conf = *server->conf;
  237. client->conf.term = strlen(term_env) > 0
  238. ? xstrdup(term_env) : xstrdup(server->conf->term);
  239. client->conf.title = strlen(title) > 0
  240. ? xstrdup(title) : xstrdup(server->conf->title);
  241. client->conf.app_id = strlen(app_id) > 0
  242. ? xstrdup(app_id) : xstrdup(server->conf->app_id);
  243. client->conf.hold_at_exit = hold;
  244. client->conf.login_shell = login_shell;
  245. if (maximized)
  246. client->conf.startup_mode = STARTUP_MAXIMIZED;
  247. else if (fullscreen)
  248. client->conf.startup_mode = STARTUP_FULLSCREEN;
  249. client->term = term_init(
  250. &client->conf, server->fdm, server->reaper, server->wayl,
  251. "footclient", cwd, argc, argv, &term_shutdown_handler, client);
  252. if (client->term == NULL) {
  253. LOG_ERR("failed to instantiate new terminal");
  254. goto shutdown;
  255. }
  256. free(argv);
  257. return true;
  258. shutdown:
  259. LOG_DBG("client FD=%d: disconnected", client->fd);
  260. free(argv);
  261. fdm_del(fdm, fd);
  262. client->fd = -1;
  263. if (client->term != NULL && !client->term->is_shutting_down)
  264. term_shutdown(client->term);
  265. else
  266. client_destroy(client);
  267. return true;
  268. }
  269. static bool
  270. fdm_server(struct fdm *fdm, int fd, int events, void *data)
  271. {
  272. if (events & EPOLLHUP)
  273. return false;
  274. struct server *server = data;
  275. struct sockaddr_un addr;
  276. socklen_t addr_size = sizeof(addr);
  277. int client_fd = accept4(
  278. server->fd, (struct sockaddr *)&addr, &addr_size, SOCK_CLOEXEC | SOCK_NONBLOCK);
  279. if (client_fd == -1) {
  280. LOG_ERRNO("failed to accept client connection");
  281. return false;
  282. }
  283. struct client *client = xmalloc(sizeof(*client));
  284. *client = (struct client) {
  285. .server = server,
  286. .fd = client_fd,
  287. };
  288. if (!fdm_add(server->fdm, client_fd, EPOLLIN, &fdm_client, client)) {
  289. close(client_fd);
  290. free(client);
  291. return false;
  292. }
  293. LOG_DBG("client FD=%d: connected", client_fd);
  294. tll_push_back(server->clients, client);
  295. return true;
  296. }
  297. enum connect_status {CONNECT_ERR, CONNECT_FAIL, CONNECT_SUCCESS};
  298. static enum connect_status
  299. try_connect(const char *sock_path)
  300. {
  301. enum connect_status ret = CONNECT_ERR;
  302. int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
  303. if (fd == -1) {
  304. LOG_ERRNO("failed to create UNIX socket");
  305. goto err;
  306. }
  307. struct sockaddr_un addr = {.sun_family = AF_UNIX};
  308. strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
  309. switch (connect(fd, (struct sockaddr *)&addr, sizeof(addr))) {
  310. case 0:
  311. ret = CONNECT_SUCCESS;
  312. break;
  313. case -1:
  314. LOG_DBG("connect() failed: %s", strerror(errno));
  315. ret = CONNECT_FAIL;
  316. break;
  317. }
  318. err:
  319. if (fd != -1)
  320. close(fd);
  321. return ret;
  322. }
  323. struct server *
  324. server_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
  325. struct wayland *wayl)
  326. {
  327. int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
  328. if (fd == -1) {
  329. LOG_ERRNO("failed to create UNIX socket");
  330. return NULL;
  331. }
  332. struct server *server = NULL;
  333. const char *sock_path = conf->server_socket_path;
  334. switch (try_connect(sock_path)) {
  335. case CONNECT_FAIL:
  336. break;
  337. case CONNECT_SUCCESS:
  338. LOG_ERR("%s is already accepting connections; is 'foot --server' already running", sock_path);
  339. /* FALLTHROUGH */
  340. case CONNECT_ERR:
  341. goto err;
  342. }
  343. unlink(sock_path);
  344. struct sockaddr_un addr = {.sun_family = AF_UNIX};
  345. strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
  346. if (bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
  347. LOG_ERRNO("%s: failed to bind", addr.sun_path);
  348. goto err;
  349. }
  350. if (listen(fd, 0) < 0) {
  351. LOG_ERRNO("%s: failed to listen", addr.sun_path);
  352. goto err;
  353. }
  354. server = malloc(sizeof(*server));
  355. if (unlikely(server == NULL)) {
  356. LOG_ERRNO("malloc() failed");
  357. goto err;
  358. }
  359. *server = (struct server) {
  360. .conf = conf,
  361. .fdm = fdm,
  362. .reaper = reaper,
  363. .wayl = wayl,
  364. .fd = fd,
  365. .sock_path = sock_path,
  366. .clients = tll_init(),
  367. };
  368. if (!fdm_add(fdm, fd, EPOLLIN, &fdm_server, server))
  369. goto err;
  370. LOG_INFO("accepting connections on %s", sock_path);
  371. return server;
  372. err:
  373. free(server);
  374. if (fd != -1)
  375. close(fd);
  376. return NULL;
  377. }
  378. void
  379. server_destroy(struct server *server)
  380. {
  381. if (server == NULL)
  382. return;
  383. LOG_DBG("server destroy, %zu clients still alive",
  384. tll_length(server->clients));
  385. tll_foreach(server->clients, it) {
  386. client_send_exit_code(it->item, 1);
  387. client_destroy(it->item);
  388. }
  389. tll_free(server->clients);
  390. fdm_del(server->fdm, server->fd);
  391. if (server->sock_path != NULL)
  392. unlink(server->sock_path);
  393. free(server);
  394. }