xref: /netbsd-src/external/bsd/tmux/dist/server.c (revision 890b6d91a44b7fcb2dfbcbc1e93463086e462d2d)
15494e770Schristos /* $OpenBSD$ */
2698d5317Sjmmv 
3698d5317Sjmmv /*
4ed4e6cd4Schristos  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5698d5317Sjmmv  *
6698d5317Sjmmv  * Permission to use, copy, modify, and distribute this software for any
7698d5317Sjmmv  * purpose with or without fee is hereby granted, provided that the above
8698d5317Sjmmv  * copyright notice and this permission notice appear in all copies.
9698d5317Sjmmv  *
10698d5317Sjmmv  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11698d5317Sjmmv  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12698d5317Sjmmv  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13698d5317Sjmmv  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14698d5317Sjmmv  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15698d5317Sjmmv  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16698d5317Sjmmv  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17698d5317Sjmmv  */
18698d5317Sjmmv 
19698d5317Sjmmv #include <sys/types.h>
20698d5317Sjmmv #include <sys/ioctl.h>
21698d5317Sjmmv #include <sys/socket.h>
22698d5317Sjmmv #include <sys/stat.h>
23698d5317Sjmmv #include <sys/un.h>
24698d5317Sjmmv #include <sys/wait.h>
25698d5317Sjmmv 
26698d5317Sjmmv #include <errno.h>
27698d5317Sjmmv #include <fcntl.h>
28698d5317Sjmmv #include <signal.h>
29698d5317Sjmmv #include <stdio.h>
30698d5317Sjmmv #include <stdlib.h>
31698d5317Sjmmv #include <string.h>
32698d5317Sjmmv #include <termios.h>
33698d5317Sjmmv #include <time.h>
34698d5317Sjmmv #include <unistd.h>
35698d5317Sjmmv 
36698d5317Sjmmv #include "tmux.h"
37698d5317Sjmmv 
38698d5317Sjmmv /*
39698d5317Sjmmv  * Main server functions.
40698d5317Sjmmv  */
41698d5317Sjmmv 
42698d5317Sjmmv struct clients		 clients;
43698d5317Sjmmv 
44ed4e6cd4Schristos struct tmuxproc		*server_proc;
456483eba0Schristos static int		 server_fd = -1;
469fb66d81Schristos static uint64_t		 server_client_flags;
474e179ddaSchristos static int		 server_exit;
484e179ddaSchristos static struct event	 server_ev_accept;
499fb66d81Schristos static struct event	 server_ev_tidy;
50698d5317Sjmmv 
51ed4e6cd4Schristos struct cmd_find_state	 marked_pane;
52d530c4d0Sjmmv 
539fb66d81Schristos static u_int		 message_next;
549fb66d81Schristos struct message_list	 message_log;
559fb66d81Schristos 
56f844e94eSwiz time_t			 current_time;
57f844e94eSwiz 
584e179ddaSchristos static int	server_loop(void);
594e179ddaSchristos static void	server_send_exit(void);
604e179ddaSchristos static void	server_accept(int, short, void *);
614e179ddaSchristos static void	server_signal(int);
624e179ddaSchristos static void	server_child_signal(void);
634e179ddaSchristos static void	server_child_exited(pid_t, int);
644e179ddaSchristos static void	server_child_stopped(pid_t, int);
655494e770Schristos 
665494e770Schristos /* Set marked pane. */
675494e770Schristos void
685494e770Schristos server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
695494e770Schristos {
70c9ad075bSchristos 	cmd_find_clear_state(&marked_pane, 0);
71ed4e6cd4Schristos 	marked_pane.s = s;
72ed4e6cd4Schristos 	marked_pane.wl = wl;
73ed4e6cd4Schristos 	marked_pane.w = wl->window;
74ed4e6cd4Schristos 	marked_pane.wp = wp;
755494e770Schristos }
765494e770Schristos 
775494e770Schristos /* Clear marked pane. */
785494e770Schristos void
795494e770Schristos server_clear_marked(void)
805494e770Schristos {
81c9ad075bSchristos 	cmd_find_clear_state(&marked_pane, 0);
825494e770Schristos }
835494e770Schristos 
845494e770Schristos /* Is this the marked pane? */
855494e770Schristos int
865494e770Schristos server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
875494e770Schristos {
885494e770Schristos 	if (s == NULL || wl == NULL || wp == NULL)
895494e770Schristos 		return (0);
90ed4e6cd4Schristos 	if (marked_pane.s != s || marked_pane.wl != wl)
915494e770Schristos 		return (0);
92ed4e6cd4Schristos 	if (marked_pane.wp != wp)
935494e770Schristos 		return (0);
945494e770Schristos 	return (server_check_marked());
955494e770Schristos }
965494e770Schristos 
975494e770Schristos /* Check if the marked pane is still valid. */
985494e770Schristos int
995494e770Schristos server_check_marked(void)
1005494e770Schristos {
101ed4e6cd4Schristos 	return (cmd_find_valid_state(&marked_pane));
1025494e770Schristos }
103698d5317Sjmmv 
104698d5317Sjmmv /* Create server socket. */
10546548964Swiz int
106*890b6d91Swiz server_create_socket(uint64_t flags, char **cause)
107698d5317Sjmmv {
108698d5317Sjmmv 	struct sockaddr_un	sa;
109698d5317Sjmmv 	size_t			size;
110698d5317Sjmmv 	mode_t			mask;
1118f3b9483Schristos 	int			fd, saved_errno;
112698d5317Sjmmv 
113698d5317Sjmmv 	memset(&sa, 0, sizeof sa);
114698d5317Sjmmv 	sa.sun_family = AF_UNIX;
115698d5317Sjmmv 	size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
116698d5317Sjmmv 	if (size >= sizeof sa.sun_path) {
117698d5317Sjmmv 		errno = ENAMETOOLONG;
1188f3b9483Schristos 		goto fail;
119698d5317Sjmmv 	}
120698d5317Sjmmv 	unlink(sa.sun_path);
121698d5317Sjmmv 
122698d5317Sjmmv 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
1238f3b9483Schristos 		goto fail;
124698d5317Sjmmv 
1259fb66d81Schristos 	if (flags & CLIENT_DEFAULTSOCKET)
126698d5317Sjmmv 		mask = umask(S_IXUSR|S_IXGRP|S_IRWXO);
1279fb66d81Schristos 	else
1289fb66d81Schristos 		mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
1298f3b9483Schristos 	if (bind(fd, (struct sockaddr *)&sa, sizeof sa) == -1) {
1308f3b9483Schristos 		saved_errno = errno;
131c9ad075bSchristos 		close(fd);
1328f3b9483Schristos 		errno = saved_errno;
1338f3b9483Schristos 		goto fail;
134c9ad075bSchristos 	}
135698d5317Sjmmv 	umask(mask);
136698d5317Sjmmv 
137c9ad075bSchristos 	if (listen(fd, 128) == -1) {
1388f3b9483Schristos 		saved_errno = errno;
139c9ad075bSchristos 		close(fd);
1408f3b9483Schristos 		errno = saved_errno;
1418f3b9483Schristos 		goto fail;
142c9ad075bSchristos 	}
143d530c4d0Sjmmv 	setblocking(fd, 0);
144698d5317Sjmmv 
145698d5317Sjmmv 	return (fd);
1468f3b9483Schristos 
1478f3b9483Schristos fail:
1488f3b9483Schristos 	if (cause != NULL) {
1498f3b9483Schristos 		xasprintf(cause, "error creating %s (%s)", socket_path,
1508f3b9483Schristos 		    strerror(errno));
1518f3b9483Schristos 	}
1528f3b9483Schristos 	return (-1);
1538f3b9483Schristos }
1548f3b9483Schristos 
1559fb66d81Schristos /* Tidy up every hour. */
1569fb66d81Schristos static void
1579fb66d81Schristos server_tidy_event(__unused int fd, __unused short events, __unused void *data)
1589fb66d81Schristos {
1599fb66d81Schristos     struct timeval	tv = { .tv_sec = 3600 };
1609fb66d81Schristos     uint64_t		t = get_timer();
1619fb66d81Schristos 
1629fb66d81Schristos     format_tidy_jobs();
1639fb66d81Schristos 
1649fb66d81Schristos #ifdef HAVE_MALLOC_TRIM
1659fb66d81Schristos     malloc_trim(0);
1669fb66d81Schristos #endif
1679fb66d81Schristos 
168e271dbb8Schristos     log_debug("%s: took %ju milliseconds", __func__, (uintmax_t)get_timer() - t);
1699fb66d81Schristos     evtimer_add(&server_ev_tidy, &tv);
1709fb66d81Schristos }
1719fb66d81Schristos 
172698d5317Sjmmv /* Fork new server. */
173698d5317Sjmmv int
174*890b6d91Swiz server_start(struct tmuxproc *client, uint64_t flags, struct event_base *base,
1759fb66d81Schristos     int lockfd, char *lockfile)
176698d5317Sjmmv {
1779fb66d81Schristos 	int		 fd;
178c9ad075bSchristos 	sigset_t	 set, oldset;
1799fb66d81Schristos 	struct client	*c = NULL;
1808f3b9483Schristos 	char		*cause = NULL;
1819fb66d81Schristos 	struct timeval	 tv = { .tv_sec = 3600 };
182698d5317Sjmmv 
183c9ad075bSchristos 	sigfillset(&set);
184c9ad075bSchristos 	sigprocmask(SIG_BLOCK, &set, &oldset);
1859fb66d81Schristos 
1869fb66d81Schristos 	if (~flags & CLIENT_NOFORK) {
1879fb66d81Schristos 		if (proc_fork_and_daemon(&fd) != 0) {
188c9ad075bSchristos 			sigprocmask(SIG_SETMASK, &oldset, NULL);
1899fb66d81Schristos 			return (fd);
190698d5317Sjmmv 		}
1919fb66d81Schristos 	}
192c9ad075bSchristos 	proc_clear_signals(client, 0);
1939fb66d81Schristos 	server_client_flags = flags;
1949fb66d81Schristos 
195c9ad075bSchristos 	if (event_reinit(base) != 0)
196c9ad075bSchristos 		fatalx("event_reinit failed");
197c9ad075bSchristos 	server_proc = proc_start("server");
1989fb66d81Schristos 
199c9ad075bSchristos 	proc_set_signals(server_proc, server_signal);
200c9ad075bSchristos 	sigprocmask(SIG_SETMASK, &oldset, NULL);
201698d5317Sjmmv 
202c9ad075bSchristos 	if (log_get_level() > 1)
203ed4e6cd4Schristos 		tty_create_log();
204ed4e6cd4Schristos 	if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec "
205ed4e6cd4Schristos 	    "tty ps", NULL) != 0)
206ed4e6cd4Schristos 		fatal("pledge failed");
207698d5317Sjmmv 
2089fb66d81Schristos 	input_key_build();
2095494e770Schristos 	RB_INIT(&windows);
210d530c4d0Sjmmv 	RB_INIT(&all_window_panes);
2115494e770Schristos 	TAILQ_INIT(&clients);
212698d5317Sjmmv 	RB_INIT(&sessions);
213698d5317Sjmmv 	key_bindings_init();
2149fb66d81Schristos 	TAILQ_INIT(&message_log);
215ed4e6cd4Schristos 	gettimeofday(&start_time, NULL);
216698d5317Sjmmv 
21746548964Swiz #ifdef HAVE_SYSTEMD
21846548964Swiz 	server_fd = systemd_create_socket(flags, &cause);
21946548964Swiz #else
2209fb66d81Schristos 	server_fd = server_create_socket(flags, &cause);
22146548964Swiz #endif
2228f3b9483Schristos 	if (server_fd != -1)
2235494e770Schristos 		server_update_socket();
2249fb66d81Schristos 	if (~flags & CLIENT_NOFORK)
2259fb66d81Schristos 		c = server_client_create(fd);
2269fb66d81Schristos 	else
2279fb66d81Schristos 		options_set_number(global_options, "exit-empty", 0);
228698d5317Sjmmv 
229ed4e6cd4Schristos 	if (lockfd >= 0) {
230928fc495Schristos 		unlink(lockfile);
231928fc495Schristos 		free(lockfile);
232928fc495Schristos 		close(lockfd);
233ed4e6cd4Schristos 	}
234698d5317Sjmmv 
2358f3b9483Schristos 	if (cause != NULL) {
2369fb66d81Schristos 		if (c != NULL) {
23746548964Swiz 			c->exit_message = cause;
2388f3b9483Schristos 			c->flags |= CLIENT_EXIT;
23946548964Swiz 		} else {
24046548964Swiz 			fprintf(stderr, "%s\n", cause);
24146548964Swiz 			exit(1);
2428f3b9483Schristos 		}
2439fb66d81Schristos 	}
2449fb66d81Schristos 
2459fb66d81Schristos 	evtimer_set(&server_ev_tidy, server_tidy_event, NULL);
2469fb66d81Schristos 	evtimer_add(&server_ev_tidy, &tv);
2478f3b9483Schristos 
24846548964Swiz 	server_acl_init();
24946548964Swiz 
250928fc495Schristos 	server_add_accept(0);
251ed4e6cd4Schristos 	proc_loop(server_proc, server_loop);
252c9ad075bSchristos 
253ef36e747Schristos 	job_kill_all();
2545494e770Schristos 	status_prompt_save_history();
255ef36e747Schristos 
256698d5317Sjmmv 	exit(0);
257698d5317Sjmmv }
258698d5317Sjmmv 
259ed4e6cd4Schristos /* Server loop callback. */
2604e179ddaSchristos static int
261ed4e6cd4Schristos server_loop(void)
262698d5317Sjmmv {
2635494e770Schristos 	struct client	*c;
2644e179ddaSchristos 	u_int		 items;
2654e179ddaSchristos 
266f844e94eSwiz 	current_time = time(NULL);
267f844e94eSwiz 
2684e179ddaSchristos 	do {
2694e179ddaSchristos 		items = cmdq_next(NULL);
2704e179ddaSchristos 		TAILQ_FOREACH(c, &clients, entry) {
2714e179ddaSchristos 			if (c->flags & CLIENT_IDENTIFIED)
2724e179ddaSchristos 				items += cmdq_next(c);
2734e179ddaSchristos 		}
2744e179ddaSchristos 	} while (items != 0);
275698d5317Sjmmv 
276ed4e6cd4Schristos 	server_client_loop();
277ed4e6cd4Schristos 
2788f3b9483Schristos 	if (!options_get_number(global_options, "exit-empty") && !server_exit)
2798f3b9483Schristos 		return (0);
2808f3b9483Schristos 
281ed4e6cd4Schristos 	if (!options_get_number(global_options, "exit-unattached")) {
282698d5317Sjmmv 		if (!RB_EMPTY(&sessions))
283698d5317Sjmmv 			return (0);
284698d5317Sjmmv 	}
2855494e770Schristos 
2865494e770Schristos 	TAILQ_FOREACH(c, &clients, entry) {
2875494e770Schristos 		if (c->session != NULL)
288698d5317Sjmmv 			return (0);
289698d5317Sjmmv 	}
2905494e770Schristos 
2915494e770Schristos 	/*
2925494e770Schristos 	 * No attached clients therefore want to exit - flush any waiting
2935494e770Schristos 	 * clients but don't actually exit until they've gone.
2945494e770Schristos 	 */
2955494e770Schristos 	cmd_wait_for_flush();
2965494e770Schristos 	if (!TAILQ_EMPTY(&clients))
2975494e770Schristos 		return (0);
2985494e770Schristos 
299ef36e747Schristos 	if (job_still_running())
3008f3b9483Schristos 		return (0);
3018f3b9483Schristos 
302698d5317Sjmmv 	return (1);
303698d5317Sjmmv }
304698d5317Sjmmv 
305ed4e6cd4Schristos /* Exit the server by killing all clients and windows. */
3064e179ddaSchristos static void
307ed4e6cd4Schristos server_send_exit(void)
308698d5317Sjmmv {
3095494e770Schristos 	struct client	*c, *c1;
3105494e770Schristos 	struct session	*s, *s1;
311698d5317Sjmmv 
3125494e770Schristos 	cmd_wait_for_flush();
3135494e770Schristos 
3145494e770Schristos 	TAILQ_FOREACH_SAFE(c, &clients, entry, c1) {
315ed4e6cd4Schristos 		if (c->flags & CLIENT_SUSPENDED)
316698d5317Sjmmv 			server_client_lost(c);
3178f3b9483Schristos 		else {
3189fb66d81Schristos 			c->flags |= CLIENT_EXIT;
3199fb66d81Schristos 			c->exit_type = CLIENT_EXIT_SHUTDOWN;
3208f3b9483Schristos 		}
321698d5317Sjmmv 		c->session = NULL;
322698d5317Sjmmv 	}
323698d5317Sjmmv 
3245494e770Schristos 	RB_FOREACH_SAFE(s, sessions, &sessions, s1)
3256483eba0Schristos 		session_destroy(s, 1, __func__);
326698d5317Sjmmv }
327698d5317Sjmmv 
328698d5317Sjmmv /* Update socket execute permissions based on whether sessions are attached. */
329698d5317Sjmmv void
330698d5317Sjmmv server_update_socket(void)
331698d5317Sjmmv {
332698d5317Sjmmv 	struct session	*s;
333698d5317Sjmmv 	static int	 last = -1;
334698d5317Sjmmv 	int		 n, mode;
335698d5317Sjmmv 	struct stat      sb;
336698d5317Sjmmv 
337698d5317Sjmmv 	n = 0;
338698d5317Sjmmv 	RB_FOREACH(s, sessions, &sessions) {
339ef36e747Schristos 		if (s->attached != 0) {
340698d5317Sjmmv 			n++;
341698d5317Sjmmv 			break;
342698d5317Sjmmv 		}
343698d5317Sjmmv 	}
344698d5317Sjmmv 
345698d5317Sjmmv 	if (n != last) {
346698d5317Sjmmv 		last = n;
347698d5317Sjmmv 
348698d5317Sjmmv 		if (stat(socket_path, &sb) != 0)
349698d5317Sjmmv 			return;
3504e179ddaSchristos 		mode = sb.st_mode & ACCESSPERMS;
351698d5317Sjmmv 		if (n != 0) {
352698d5317Sjmmv 			if (mode & S_IRUSR)
353698d5317Sjmmv 				mode |= S_IXUSR;
354698d5317Sjmmv 			if (mode & S_IRGRP)
355698d5317Sjmmv 				mode |= S_IXGRP;
356698d5317Sjmmv 			if (mode & S_IROTH)
357698d5317Sjmmv 				mode |= S_IXOTH;
358698d5317Sjmmv 		} else
359698d5317Sjmmv 			mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
360698d5317Sjmmv 		chmod(socket_path, mode);
361698d5317Sjmmv 	}
362698d5317Sjmmv }
363698d5317Sjmmv 
364698d5317Sjmmv /* Callback for server socket. */
3654e179ddaSchristos static void
366ed4e6cd4Schristos server_accept(int fd, short events, __unused void *data)
367698d5317Sjmmv {
368698d5317Sjmmv 	struct sockaddr_storage	 sa;
369698d5317Sjmmv 	socklen_t		 slen = sizeof sa;
370698d5317Sjmmv 	int			 newfd;
37146548964Swiz 	struct client		*c;
372698d5317Sjmmv 
373928fc495Schristos 	server_add_accept(0);
374698d5317Sjmmv 	if (!(events & EV_READ))
375698d5317Sjmmv 		return;
376698d5317Sjmmv 
377698d5317Sjmmv 	newfd = accept(fd, (struct sockaddr *) &sa, &slen);
378698d5317Sjmmv 	if (newfd == -1) {
379698d5317Sjmmv 		if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
380698d5317Sjmmv 			return;
381928fc495Schristos 		if (errno == ENFILE || errno == EMFILE) {
382928fc495Schristos 			/* Delete and don't try again for 1 second. */
383928fc495Schristos 			server_add_accept(1);
384928fc495Schristos 			return;
385928fc495Schristos 		}
386698d5317Sjmmv 		fatal("accept failed");
387698d5317Sjmmv 	}
38846548964Swiz 
389ed4e6cd4Schristos 	if (server_exit) {
390698d5317Sjmmv 		close(newfd);
391698d5317Sjmmv 		return;
392698d5317Sjmmv 	}
39346548964Swiz 	c = server_client_create(newfd);
39446548964Swiz 	if (!server_acl_join(c)) {
39546548964Swiz 		c->exit_message = xstrdup("access not allowed");
39646548964Swiz 		c->flags |= CLIENT_EXIT;
39746548964Swiz 	}
398698d5317Sjmmv }
399698d5317Sjmmv 
400928fc495Schristos /*
401928fc495Schristos  * Add accept event. If timeout is nonzero, add as a timeout instead of a read
402928fc495Schristos  * event - used to backoff when running out of file descriptors.
403928fc495Schristos  */
404928fc495Schristos void
405928fc495Schristos server_add_accept(int timeout)
406928fc495Schristos {
407928fc495Schristos 	struct timeval tv = { timeout, 0 };
408928fc495Schristos 
4096483eba0Schristos 	if (server_fd == -1)
4106483eba0Schristos 		return;
4116483eba0Schristos 
412928fc495Schristos 	if (event_initialized(&server_ev_accept))
413928fc495Schristos 		event_del(&server_ev_accept);
414928fc495Schristos 
415928fc495Schristos 	if (timeout == 0) {
416ed4e6cd4Schristos 		event_set(&server_ev_accept, server_fd, EV_READ, server_accept,
417ed4e6cd4Schristos 		    NULL);
418928fc495Schristos 		event_add(&server_ev_accept, NULL);
419928fc495Schristos 	} else {
420ed4e6cd4Schristos 		event_set(&server_ev_accept, server_fd, EV_TIMEOUT,
421ed4e6cd4Schristos 		    server_accept, NULL);
422928fc495Schristos 		event_add(&server_ev_accept, &tv);
423928fc495Schristos 	}
424928fc495Schristos }
425928fc495Schristos 
426698d5317Sjmmv /* Signal handler. */
4274e179ddaSchristos static void
428ed4e6cd4Schristos server_signal(int sig)
429698d5317Sjmmv {
4305494e770Schristos 	int	fd;
4315494e770Schristos 
432c9ad075bSchristos 	log_debug("%s: %s", __func__, strsignal(sig));
433698d5317Sjmmv 	switch (sig) {
4349fb66d81Schristos 	case SIGINT:
435698d5317Sjmmv 	case SIGTERM:
436ed4e6cd4Schristos 		server_exit = 1;
437ed4e6cd4Schristos 		server_send_exit();
438698d5317Sjmmv 		break;
439698d5317Sjmmv 	case SIGCHLD:
440698d5317Sjmmv 		server_child_signal();
441698d5317Sjmmv 		break;
442698d5317Sjmmv 	case SIGUSR1:
443698d5317Sjmmv 		event_del(&server_ev_accept);
4449fb66d81Schristos 		fd = server_create_socket(server_client_flags, NULL);
4455494e770Schristos 		if (fd != -1) {
446698d5317Sjmmv 			close(server_fd);
4475494e770Schristos 			server_fd = fd;
4485494e770Schristos 			server_update_socket();
4495494e770Schristos 		}
450928fc495Schristos 		server_add_accept(0);
451698d5317Sjmmv 		break;
452c9ad075bSchristos 	case SIGUSR2:
453c9ad075bSchristos 		proc_toggle_log(server_proc);
454c9ad075bSchristos 		break;
455698d5317Sjmmv 	}
456698d5317Sjmmv }
457698d5317Sjmmv 
458698d5317Sjmmv /* Handle SIGCHLD. */
4594e179ddaSchristos static void
460698d5317Sjmmv server_child_signal(void)
461698d5317Sjmmv {
462698d5317Sjmmv 	int	 status;
463698d5317Sjmmv 	pid_t	 pid;
464698d5317Sjmmv 
465698d5317Sjmmv 	for (;;) {
466698d5317Sjmmv 		switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
467698d5317Sjmmv 		case -1:
468698d5317Sjmmv 			if (errno == ECHILD)
469698d5317Sjmmv 				return;
470698d5317Sjmmv 			fatal("waitpid failed");
471698d5317Sjmmv 		case 0:
472698d5317Sjmmv 			return;
473698d5317Sjmmv 		}
474698d5317Sjmmv 		if (WIFSTOPPED(status))
475698d5317Sjmmv 			server_child_stopped(pid, status);
476698d5317Sjmmv 		else if (WIFEXITED(status) || WIFSIGNALED(status))
477698d5317Sjmmv 			server_child_exited(pid, status);
478698d5317Sjmmv 	}
479698d5317Sjmmv }
480698d5317Sjmmv 
481698d5317Sjmmv /* Handle exited children. */
4824e179ddaSchristos static void
483698d5317Sjmmv server_child_exited(pid_t pid, int status)
484698d5317Sjmmv {
4855494e770Schristos 	struct window		*w, *w1;
486698d5317Sjmmv 	struct window_pane	*wp;
487698d5317Sjmmv 
4885494e770Schristos 	RB_FOREACH_SAFE(w, windows, &windows, w1) {
489698d5317Sjmmv 		TAILQ_FOREACH(wp, &w->panes, entry) {
490698d5317Sjmmv 			if (wp->pid == pid) {
4915494e770Schristos 				wp->status = status;
4928f3b9483Schristos 				wp->flags |= PANE_STATUSREADY;
493c9ad075bSchristos 
494c9ad075bSchristos 				log_debug("%%%u exited", wp->id);
495c9ad075bSchristos 				wp->flags |= PANE_EXITED;
496c9ad075bSchristos 
497c9ad075bSchristos 				if (window_pane_destroy_ready(wp))
498ed4e6cd4Schristos 					server_destroy_pane(wp, 1);
499698d5317Sjmmv 				break;
500698d5317Sjmmv 			}
501698d5317Sjmmv 		}
502698d5317Sjmmv 	}
503ef36e747Schristos 	job_check_died(pid, status);
504698d5317Sjmmv }
505698d5317Sjmmv 
506698d5317Sjmmv /* Handle stopped children. */
5074e179ddaSchristos static void
508698d5317Sjmmv server_child_stopped(pid_t pid, int status)
509698d5317Sjmmv {
510698d5317Sjmmv 	struct window		*w;
511698d5317Sjmmv 	struct window_pane	*wp;
512698d5317Sjmmv 
513698d5317Sjmmv 	if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
514698d5317Sjmmv 		return;
515698d5317Sjmmv 
5165494e770Schristos 	RB_FOREACH(w, windows, &windows) {
517698d5317Sjmmv 		TAILQ_FOREACH(wp, &w->panes, entry) {
518698d5317Sjmmv 			if (wp->pid == pid) {
519698d5317Sjmmv 				if (killpg(pid, SIGCONT) != 0)
520698d5317Sjmmv 					kill(pid, SIGCONT);
521698d5317Sjmmv 			}
522698d5317Sjmmv 		}
523698d5317Sjmmv 	}
5249fb66d81Schristos 	job_check_died(pid, status);
5259fb66d81Schristos }
5269fb66d81Schristos 
5279fb66d81Schristos /* Add to message log. */
5289fb66d81Schristos void
5299fb66d81Schristos server_add_message(const char *fmt, ...)
5309fb66d81Schristos {
5319fb66d81Schristos 	struct message_entry	*msg, *msg1;
5329fb66d81Schristos 	char			*s;
5339fb66d81Schristos 	va_list			 ap;
5349fb66d81Schristos 	u_int			 limit;
5359fb66d81Schristos 
5369fb66d81Schristos 	va_start(ap, fmt);
5379fb66d81Schristos 	xvasprintf(&s, fmt, ap);
5389fb66d81Schristos 	va_end(ap);
5399fb66d81Schristos 
5409fb66d81Schristos 	log_debug("message: %s", s);
5419fb66d81Schristos 
5429fb66d81Schristos 	msg = xcalloc(1, sizeof *msg);
5439fb66d81Schristos 	gettimeofday(&msg->msg_time, NULL);
5449fb66d81Schristos 	msg->msg_num = message_next++;
5459fb66d81Schristos 	msg->msg = s;
5469fb66d81Schristos 	TAILQ_INSERT_TAIL(&message_log, msg, entry);
5479fb66d81Schristos 
5489fb66d81Schristos 	limit = options_get_number(global_options, "message-limit");
5499fb66d81Schristos 	TAILQ_FOREACH_SAFE(msg, &message_log, entry, msg1) {
5509fb66d81Schristos 		if (msg->msg_num + limit >= message_next)
5519fb66d81Schristos 			break;
5529fb66d81Schristos 		free(msg->msg);
5539fb66d81Schristos 		TAILQ_REMOVE(&message_log, msg, entry);
5549fb66d81Schristos 		free(msg);
5559fb66d81Schristos 	}
556698d5317Sjmmv }
557