1 /*
2 * Copyright © 2019 Manuel Stoeckl
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26 #include "main.h"
27
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <inttypes.h>
31 #include <poll.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/socket.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <sys/un.h>
39 #include <sys/wait.h>
40 #include <unistd.h>
41
conntoken_header(const struct main_config * config,bool reconnectable,bool update)42 static inline uint32_t conntoken_header(const struct main_config *config,
43 bool reconnectable, bool update)
44 {
45 uint32_t header = (WAYPIPE_PROTOCOL_VERSION << 16) | CONN_FIXED_BIT;
46 header |= (update ? CONN_UPDATE_BIT : 0);
47 header |= (reconnectable ? CONN_RECONNECTABLE_BIT : 0);
48 // TODO: stop compile gating the 'COMP' enum entries
49 #ifdef HAS_LZ4
50 header |= (config->compression == COMP_LZ4 ? CONN_LZ4_COMPRESSION : 0);
51 #endif
52 #ifdef HAS_ZSTD
53 header |= (config->compression == COMP_ZSTD ? CONN_ZSTD_COMPRESSION
54 : 0);
55 #endif
56 if (config->compression == COMP_NONE) {
57 header |= CONN_NO_COMPRESSION;
58 }
59 if (config->video_if_possible) {
60 header |= (config->video_fmt == VIDEO_H264 ? CONN_H264_VIDEO
61 : 0);
62 header |= (config->video_fmt == VIDEO_VP9 ? CONN_VP9_VIDEO : 0);
63 } else {
64 header |= CONN_NO_VIDEO;
65 }
66 #ifdef HAS_DMABUF
67 header |= (config->no_gpu ? CONN_NO_DMABUF_SUPPORT : 0);
68 #else
69 header |= CONN_NO_DMABUF_SUPPORT;
70 #endif
71 return header;
72 }
73
74 /** Fill the key for a token using random data with a very low accidental
75 * collision probability. Whatever data was in the key before will be shuffled
76 * in.*/
fill_random_key(struct connection_token * token)77 static void fill_random_key(struct connection_token *token)
78 {
79 token->key[0] *= 13;
80 token->key[1] *= 17;
81 token->key[2] *= 29;
82
83 struct timespec tp;
84 clock_gettime(CLOCK_REALTIME, &tp);
85 token->key[0] += (uint32_t)getpid();
86 token->key[1] += 1 + (uint32_t)tp.tv_sec;
87 token->key[2] += 2 + (uint32_t)tp.tv_nsec;
88
89 int devrand = open("/dev/urandom", O_RDONLY);
90 if (devrand != -1) {
91 uint32_t tmp[3];
92 errno = 0;
93 (void)read(devrand, tmp, sizeof(tmp));
94 checked_close(devrand);
95 token->key[0] ^= tmp[0];
96 token->key[1] ^= tmp[1];
97 token->key[2] ^= tmp[2];
98 }
99 }
100
read_sockaddr(int control_pipe,struct sockaddr_un * sockaddr)101 static int read_sockaddr(int control_pipe, struct sockaddr_un *sockaddr)
102 {
103 /* It is unlikely that a signal would interrupt a read of a ~100 byte
104 * sockaddr; and if used properly, the control pipe should never be
105 * sent much more data than that */
106 char path[4096];
107 ssize_t amt = read(control_pipe, path, sizeof(path) - 1);
108 if (amt == -1) {
109 wp_error("Failed to read from control pipe: %s",
110 strerror(errno));
111 return -1;
112 }
113 path[amt] = '\0';
114 if (strlen(path) >= sizeof(sockaddr->sun_path)) {
115 wp_error("Socket path read from control pipe is too long (%zu bytes, expected <= %zu): %s",
116 strlen(path), sizeof(sockaddr->sun_path) - 1,
117 path);
118 return -1;
119 }
120 strcpy(sockaddr->sun_path, path);
121 return 0;
122 }
123
run_single_server_reconnector(int control_pipe,int linkfd,const struct connection_token * flagged_token)124 static int run_single_server_reconnector(int control_pipe, int linkfd,
125 const struct connection_token *flagged_token)
126 {
127 int retcode = EXIT_SUCCESS;
128 while (!shutdown_flag) {
129 struct pollfd pf[2];
130 pf[0].fd = control_pipe;
131 pf[0].events = POLLIN;
132 pf[0].revents = 0;
133 pf[1].fd = linkfd;
134 pf[1].events = 0;
135 pf[1].revents = 0;
136
137 int r = poll(pf, 2, -1);
138 if (r == -1 && errno == EINTR) {
139 continue;
140 } else if (r == -1) {
141 retcode = EXIT_FAILURE;
142 break;
143 } else if (r == 0) {
144 // Nothing to read
145 continue;
146 }
147
148 if (pf[1].revents & POLLHUP) {
149 /* Hang up, main thread has closed its link */
150 break;
151 }
152 if (pf[0].revents & POLLIN) {
153 struct sockaddr_un new_sockaddr;
154 if (read_sockaddr(control_pipe, &new_sockaddr) == -1) {
155 retcode = EXIT_FAILURE;
156 break;
157 }
158
159 int new_conn = connect_to_socket(&new_sockaddr);
160 if (new_conn == -1) {
161 wp_error("Socket path \"%s\" was invalid: %s",
162 new_sockaddr.sun_path,
163 strerror(errno));
164 /* Socket path was invalid */
165 continue;
166 }
167
168 if (write(new_conn, flagged_token,
169 sizeof(*flagged_token)) !=
170 sizeof(*flagged_token)) {
171 wp_error("Failed to write to new connection: %s",
172 strerror(errno));
173 checked_close(new_conn);
174 continue;
175 }
176
177 if (send_one_fd(linkfd, new_conn) == -1) {
178 wp_error("Failed to send new connection to subprocess: %s",
179 strerror(errno));
180 }
181 checked_close(new_conn);
182 }
183 }
184 checked_close(control_pipe);
185 checked_close(linkfd);
186 return retcode;
187 }
188
run_single_server(int control_pipe,const struct sockaddr_un * socket_addr,bool unlink_at_end,int server_link,const struct main_config * config)189 static int run_single_server(int control_pipe,
190 const struct sockaddr_un *socket_addr, bool unlink_at_end,
191 int server_link, const struct main_config *config)
192 {
193 int chanfd = connect_to_socket(socket_addr);
194 if (chanfd == -1) {
195 goto fail_srv;
196 }
197 /* Only unlink the socket if it actually was a socket */
198 if (unlink_at_end) {
199 unlink(socket_addr->sun_path);
200 }
201 bool reconnectable = control_pipe != -1;
202
203 struct connection_token token;
204 memset(&token, 0, sizeof(token));
205 fill_random_key(&token);
206 token.header = conntoken_header(config, reconnectable, false);
207 wp_debug("Connection token header: %08" PRIx32, token.header);
208 if (write(chanfd, &token, sizeof(token)) != sizeof(token)) {
209 wp_error("Failed to write connection token to socket");
210 goto fail_cfd;
211 }
212
213 int linkfds[2] = {-1, -1};
214 if (control_pipe != -1) {
215 if (socketpair(AF_UNIX, SOCK_STREAM, 0, linkfds) == -1) {
216 wp_error("Failed to create socketpair: %s",
217 strerror(errno));
218 goto fail_cfd;
219 }
220
221 pid_t reco_pid = fork();
222 if (reco_pid == -1) {
223 wp_error("Fork failure: %s", strerror(errno));
224 checked_close(linkfds[0]);
225 checked_close(linkfds[1]);
226 goto fail_cfd;
227 } else if (reco_pid == 0) {
228 checked_close(chanfd);
229 checked_close(linkfds[0]);
230 checked_close(server_link);
231
232 /* Further uses of the token will be to reconnect */
233 token.header |= CONN_UPDATE_BIT;
234 int rc = run_single_server_reconnector(
235 control_pipe, linkfds[1], &token);
236 exit(rc);
237 }
238 checked_close(control_pipe);
239 checked_close(linkfds[1]);
240 }
241
242 int ret = main_interface_loop(
243 chanfd, server_link, linkfds[0], config, false);
244 return ret;
245
246 fail_cfd:
247 checked_close(chanfd);
248 fail_srv:
249 checked_close(server_link);
250 return EXIT_FAILURE;
251 }
252
handle_new_server_connection(const struct sockaddr_un * current_sockaddr,int control_pipe,int wdisplay_socket,int appfd,struct conn_map * connmap,const struct main_config * config,const struct connection_token * new_token)253 static int handle_new_server_connection(
254 const struct sockaddr_un *current_sockaddr, int control_pipe,
255 int wdisplay_socket, int appfd, struct conn_map *connmap,
256 const struct main_config *config,
257 const struct connection_token *new_token)
258 {
259 bool reconnectable = control_pipe != -1;
260 if (reconnectable && buf_ensure_size(connmap->count + 1,
261 sizeof(struct conn_addr),
262 &connmap->size,
263 (void **)&connmap->data) == -1) {
264 wp_error("Failed to allocate memory to track new connection");
265 goto fail_appfd;
266 }
267
268 int chanfd = connect_to_socket(current_sockaddr);
269 if (chanfd == -1) {
270 goto fail_appfd;
271 }
272 if (write(chanfd, new_token, sizeof(*new_token)) !=
273 sizeof(*new_token)) {
274 wp_error("Failed to write connection token: %s",
275 strerror(errno));
276 goto fail_chanfd;
277 }
278
279 int linksocks[2] = {-1, -1};
280 if (reconnectable) {
281 if (socketpair(AF_UNIX, SOCK_STREAM, 0, linksocks) == -1) {
282 wp_error("Socketpair for process link failed: %s",
283 strerror(errno));
284 goto fail_chanfd;
285 }
286 }
287
288 pid_t npid = fork();
289 if (npid == 0) {
290 // Run forked process, with the only shared state being the
291 // new channel socket
292 checked_close(wdisplay_socket);
293 if (reconnectable) {
294 checked_close(control_pipe);
295 checked_close(linksocks[0]);
296 }
297 for (int i = 0; i < connmap->count; i++) {
298 if (connmap->data[i].linkfd != -1) {
299 checked_close(connmap->data[i].linkfd);
300 }
301 }
302 int rc = main_interface_loop(
303 chanfd, appfd, linksocks[1], config, false);
304 check_unclosed_fds();
305 exit(rc);
306 } else if (npid == -1) {
307 wp_error("Fork failure: %s", strerror(errno));
308 if (reconnectable) {
309 checked_close(linksocks[0]);
310 checked_close(linksocks[1]);
311 }
312 goto fail_chanfd;
313 }
314
315 // This process no longer needs the application connection
316 checked_close(chanfd);
317 checked_close(appfd);
318 if (reconnectable) {
319 checked_close(linksocks[1]);
320
321 connmap->data[connmap->count++] = (struct conn_addr){
322 .token = *new_token,
323 .pid = npid,
324 .linkfd = linksocks[0],
325 };
326 }
327
328 return 0;
329 fail_chanfd:
330 checked_close(chanfd);
331 fail_appfd:
332 checked_close(appfd);
333 return -1;
334 }
335
update_connections(struct sockaddr_un * current_sockaddr,const struct sockaddr_un * new_sockaddr,struct conn_map * connmap,bool unlink_at_end)336 static int update_connections(struct sockaddr_un *current_sockaddr,
337 const struct sockaddr_un *new_sockaddr,
338 struct conn_map *connmap, bool unlink_at_end)
339 {
340 /* TODO: what happens if there's a partial failure? */
341 for (int i = 0; i < connmap->count; i++) {
342 int chanfd = connect_to_socket(new_sockaddr);
343 if (chanfd == -1) {
344 wp_error("Failed to connect to socket at \"%s\": %s",
345 new_sockaddr->sun_path,
346 strerror(errno));
347 return -1;
348 }
349 struct connection_token flagged_token = connmap->data[i].token;
350 flagged_token.header |= CONN_UPDATE_BIT;
351 if (write(chanfd, &flagged_token, sizeof(flagged_token)) !=
352 sizeof(flagged_token)) {
353 wp_error("Failed to write token to replacement connection: %s",
354 strerror(errno));
355 checked_close(chanfd);
356 return -1;
357 }
358
359 if (send_one_fd(connmap->data[i].linkfd, chanfd) == -1) {
360 // TODO: what happens if data has changed?
361 checked_close(chanfd);
362 return -1;
363 }
364 checked_close(chanfd);
365 }
366 /* If switching connections succeeded, adopt the new socket */
367 if (unlink_at_end && strcmp(current_sockaddr->sun_path,
368 new_sockaddr->sun_path)) {
369 unlink(current_sockaddr->sun_path);
370 }
371 *current_sockaddr = *new_sockaddr;
372 return 0;
373 }
374
run_multi_server(int control_pipe,const struct sockaddr_un * socket_addr,bool unlink_at_end,int wdisplay_socket,const struct main_config * config,pid_t * child_pid)375 static int run_multi_server(int control_pipe,
376 const struct sockaddr_un *socket_addr, bool unlink_at_end,
377 int wdisplay_socket, const struct main_config *config,
378 pid_t *child_pid)
379 {
380 struct conn_map connmap = {.data = NULL, .count = 0, .size = 0};
381 struct sockaddr_un current_sockaddr = *socket_addr;
382
383 struct pollfd pfs[2];
384 pfs[0].fd = wdisplay_socket;
385 pfs[0].events = POLLIN;
386 pfs[0].revents = 0;
387 pfs[1].fd = control_pipe;
388 pfs[1].events = POLLIN;
389 pfs[1].revents = 0;
390 int retcode = EXIT_SUCCESS;
391 struct connection_token token;
392 memset(&token, 0, sizeof(token));
393 token.header = conntoken_header(config, control_pipe != -1, false);
394 wp_debug("Connection token header: %08" PRIx32, token.header);
395 while (!shutdown_flag) {
396 int status = -1;
397 if (wait_for_pid_and_clean(
398 child_pid, &status, WNOHANG, &connmap)) {
399 wp_debug("Child program has died, exiting");
400 retcode = WEXITSTATUS(status);
401 break;
402 }
403
404 int r = poll(pfs, 1 + (control_pipe != -1), -1);
405 if (r == -1) {
406 if (errno == EINTR) {
407 // If SIGCHLD, we will check the child.
408 // If SIGINT, the loop ends
409 continue;
410 }
411 wp_error("Poll failed: %s", strerror(errno));
412 retcode = EXIT_FAILURE;
413 break;
414 } else if (r == 0) {
415 continue;
416 }
417 if (pfs[1].revents & POLLIN) {
418 struct sockaddr_un new_sockaddr;
419 if (read_sockaddr(control_pipe, &new_sockaddr) == -1) {
420
421 } else {
422 update_connections(¤t_sockaddr,
423 &new_sockaddr, &connmap,
424 unlink_at_end);
425 }
426 }
427
428 if (pfs[0].revents & POLLIN) {
429 int appfd = accept(wdisplay_socket, NULL, NULL);
430 if (appfd == -1) {
431 if (errno == EAGAIN || errno == EWOULDBLOCK) {
432 // The wakeup may have been
433 // spurious
434 continue;
435 }
436 wp_error("Connection failure: %s",
437 strerror(errno));
438 retcode = EXIT_FAILURE;
439 break;
440 } else {
441 wp_debug("New connection to server");
442 fill_random_key(&token);
443 if (handle_new_server_connection(
444 ¤t_sockaddr,
445 control_pipe,
446 wdisplay_socket, appfd,
447 &connmap, config,
448 &token) == -1) {
449 retcode = EXIT_FAILURE;
450 break;
451 }
452 }
453 }
454 }
455 if (unlink_at_end) {
456 unlink(current_sockaddr.sun_path);
457 }
458 checked_close(wdisplay_socket);
459 if (control_pipe != -1) {
460 checked_close(control_pipe);
461 }
462
463 for (int i = 0; i < connmap.count; i++) {
464 checked_close(connmap.data[i].linkfd);
465 }
466 free(connmap.data);
467 return retcode;
468 }
469
470 /* requires >=256 byte shell/shellname buffers */
setup_login_shell_command(char shell[static256],char shellname[static256],bool login_shell)471 static void setup_login_shell_command(char shell[static 256],
472 char shellname[static 256], bool login_shell)
473 {
474 strcpy(shellname, "-sh");
475 strcpy(shell, "/bin/sh");
476
477 // Select the preferred shell on the system
478 char *shell_env = getenv("SHELL");
479 if (!shell_env) {
480 return;
481 }
482 int len = (int)strlen(shell_env);
483 if (len >= 254) {
484 wp_error("Environment variable $SHELL is too long at %d bytes, falling back to %s",
485 len, shell);
486 return;
487 }
488 strcpy(shell, shell_env);
489 if (login_shell) {
490 /* Create a login shell. The convention for this is to prefix
491 * the name of the shell with a single hyphen */
492 int start = len;
493 for (; start-- > 0;) {
494 if (shell[start] == '/') {
495 start++;
496 break;
497 }
498 }
499 shellname[0] = '-';
500 strcpy(shellname + 1, shell + start);
501 } else {
502 strcpy(shellname, shell);
503 }
504 }
505
run_server(const struct sockaddr_un * socket_addr,const char * wayland_display,const char * control_path,const struct main_config * config,bool oneshot,bool unlink_at_end,char * const app_argv[],bool login_shell_if_backup)506 int run_server(const struct sockaddr_un *socket_addr,
507 const char *wayland_display, const char *control_path,
508 const struct main_config *config, bool oneshot,
509 bool unlink_at_end, char *const app_argv[],
510 bool login_shell_if_backup)
511 {
512 wp_debug("I'm a server connecting on %s, running: %s",
513 socket_addr->sun_path, app_argv[0]);
514 wp_debug("version: %s", WAYPIPE_VERSION);
515
516 struct sockaddr_un display_path;
517 memset(&display_path, 0, sizeof(display_path));
518 if (!oneshot) {
519 if (wayland_display[0] == '/') {
520 if (strlen(wayland_display) >=
521 sizeof(display_path.sun_path)) {
522 wp_error("Absolute path '%s' specified for WAYLAND_DISPLAY is too long (%zu bytes > %zu)",
523 wayland_display,
524 strlen(wayland_display),
525 sizeof(display_path.sun_path) -
526 1);
527 return EXIT_FAILURE;
528 }
529 strcpy(display_path.sun_path, wayland_display);
530 } else {
531 const char *xdg_dir = getenv("XDG_RUNTIME_DIR");
532 if (!xdg_dir) {
533 wp_error("Env. var XDG_RUNTIME_DIR not available, cannot place display socket for WAYLAND_DISPLAY=\"%s\"",
534 wayland_display);
535 return EXIT_FAILURE;
536 }
537 if (strlen(xdg_dir) + 1 + strlen(wayland_display) >=
538 sizeof(display_path.sun_path)) {
539 wp_error("Path '%s/%s' specified for WAYLAND_DISPLAY is too long (%zu + 1 + %zu bytes > %zu)",
540 xdg_dir, wayland_display,
541 strlen(xdg_dir),
542 strlen(wayland_display),
543 sizeof(display_path.sun_path) -
544 1);
545 return EXIT_FAILURE;
546 }
547 multi_strcat(display_path.sun_path,
548 sizeof(display_path.sun_path), xdg_dir,
549 "/", wayland_display, NULL);
550 }
551 }
552
553 // Setup connection to program
554 int wayland_socket = -1, server_link = -1, wdisplay_socket = -1;
555 if (oneshot) {
556 int csockpair[2];
557 if (socketpair(AF_UNIX, SOCK_STREAM, 0, csockpair) == -1) {
558 wp_error("Socketpair failed: %s", strerror(errno));
559 return EXIT_FAILURE;
560 }
561 wayland_socket = csockpair[1];
562 server_link = csockpair[0];
563 } else {
564 // Bind a socket for WAYLAND_DISPLAY, and listen
565 int nmaxclients = 128;
566 wdisplay_socket = setup_nb_socket(&display_path, nmaxclients);
567 if (wdisplay_socket == -1) {
568 // Error messages already made
569 return EXIT_FAILURE;
570 }
571 }
572
573 // Launch program
574 pid_t pid = fork();
575 if (pid == -1) {
576 wp_error("Fork failure: %s", strerror(errno));
577 if (!oneshot) {
578 unlink(display_path.sun_path);
579 }
580 return EXIT_FAILURE;
581 } else if (pid == 0) {
582 if (oneshot) {
583 char bufs2[16];
584 sprintf(bufs2, "%d", wayland_socket);
585
586 // Provide the other socket in the pair to child
587 // application
588 unsetenv("WAYLAND_DISPLAY");
589 setenv("WAYLAND_SOCKET", bufs2, 1);
590 checked_close(server_link);
591 } else {
592 // Since Wayland 1.15, absolute paths are supported in
593 // WAYLAND_DISPLAY
594 unsetenv("WAYLAND_SOCKET");
595 setenv("WAYLAND_DISPLAY", wayland_display, 1);
596 checked_close(wdisplay_socket);
597 }
598
599 const char *application = app_argv[0];
600 char shell[256];
601 char shellname[256];
602 char *shellcmd[2] = {shellname, NULL};
603 if (!application) {
604 setup_login_shell_command(shell, shellname,
605 login_shell_if_backup);
606 application = shell;
607 app_argv = shellcmd;
608 }
609
610 execvp(application, app_argv);
611 wp_error("Failed to execvp \'%s\': %s", application,
612 strerror(errno));
613 return EXIT_FAILURE;
614 }
615 if (oneshot) {
616 // We no longer need to see this side
617 checked_close(wayland_socket);
618 }
619
620 int control_pipe = -1;
621 if (control_path) {
622 if (mkfifo(control_path, 0644) == -1) {
623 wp_error("Failed to make a control FIFO at %s: %s",
624 control_path, strerror(errno));
625 } else {
626 /* To prevent getting POLLHUP spam after the first user
627 * closes this pipe, open both read and write ends of
628 * the named pipe */
629 control_pipe = open(control_path, O_RDWR | O_NONBLOCK);
630 if (control_pipe == -1) {
631 wp_error("Failed to open created FIFO for reading: %s",
632 control_path, strerror(errno));
633 }
634 }
635 }
636
637 int retcode = EXIT_SUCCESS;
638 /* These functions will close server_link, wdisplay_socket, and
639 * control_pipe */
640 if (oneshot) {
641 retcode = run_single_server(control_pipe, socket_addr,
642 unlink_at_end, server_link, config);
643 } else {
644 retcode = run_multi_server(control_pipe, socket_addr,
645 unlink_at_end, wdisplay_socket, config, &pid);
646 }
647 if (control_pipe != -1) {
648 unlink(control_path);
649 }
650 if (!oneshot) {
651 unlink(display_path.sun_path);
652 }
653
654 // Wait for child processes to exit
655 wp_debug("Waiting for child handlers and program");
656
657 int status = -1;
658 if (wait_for_pid_and_clean(
659 &pid, &status, shutdown_flag ? WNOHANG : 0, NULL)) {
660 wp_debug("Child program has died, exiting");
661 retcode = WEXITSTATUS(status);
662 }
663 wp_debug("Program ended");
664 return retcode;
665 }
666