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(&current_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 						    &current_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