xref: /openbsd/usr.bin/tmux/server.c (revision 3d40d63a)
1*3d40d63aSnicm /* $OpenBSD: server.c,v 1.208 2025/01/01 15:17:36 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
498ca8272Snicm  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5311827fbSnicm  *
6311827fbSnicm  * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm  * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm  * copyright notice and this permission notice appear in all copies.
9311827fbSnicm  *
10311827fbSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm  */
18311827fbSnicm 
19311827fbSnicm #include <sys/types.h>
20311827fbSnicm #include <sys/ioctl.h>
21311827fbSnicm #include <sys/socket.h>
22311827fbSnicm #include <sys/stat.h>
23311827fbSnicm #include <sys/un.h>
24311827fbSnicm #include <sys/wait.h>
25311827fbSnicm 
26311827fbSnicm #include <errno.h>
277724d0b0Snicm #include <event.h>
28311827fbSnicm #include <fcntl.h>
29a575a2dfSnicm #include <paths.h>
30311827fbSnicm #include <signal.h>
31311827fbSnicm #include <stdio.h>
32311827fbSnicm #include <stdlib.h>
33311827fbSnicm #include <string.h>
34311827fbSnicm #include <termios.h>
35311827fbSnicm #include <time.h>
36311827fbSnicm #include <unistd.h>
37311827fbSnicm 
38311827fbSnicm #include "tmux.h"
39311827fbSnicm 
40311827fbSnicm /*
41311827fbSnicm  * Main server functions.
42311827fbSnicm  */
43311827fbSnicm 
44311827fbSnicm struct clients		 clients;
45311827fbSnicm 
465b8ac713Snicm struct tmuxproc		*server_proc;
47432eaf59Snicm static int		 server_fd = -1;
48b3e4eeb8Snicm static uint64_t		 server_client_flags;
499883b791Snicm static int		 server_exit;
509883b791Snicm static struct event	 server_ev_accept;
5197887b4cSnicm static struct event	 server_ev_tidy;
52b7fa064bSnicm 
537519eda3Snicm struct cmd_find_state	 marked_pane;
542986c025Snicm 
550687354cSnicm static u_int		 message_next;
560687354cSnicm struct message_list	 message_log;
570687354cSnicm 
58a711f92aSnicm time_t			 current_time;
59a711f92aSnicm 
609883b791Snicm static int	server_loop(void);
619883b791Snicm static void	server_send_exit(void);
629883b791Snicm static void	server_accept(int, short, void *);
639883b791Snicm static void	server_signal(int);
649883b791Snicm static void	server_child_signal(void);
659883b791Snicm static void	server_child_exited(pid_t, int);
669883b791Snicm static void	server_child_stopped(pid_t, int);
67b7fa064bSnicm 
682986c025Snicm /* Set marked pane. */
692986c025Snicm void
server_set_marked(struct session * s,struct winlink * wl,struct window_pane * wp)702986c025Snicm server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
712986c025Snicm {
7293e732aaSnicm 	cmd_find_clear_state(&marked_pane, 0);
737519eda3Snicm 	marked_pane.s = s;
747519eda3Snicm 	marked_pane.wl = wl;
757519eda3Snicm 	marked_pane.w = wl->window;
767519eda3Snicm 	marked_pane.wp = wp;
772986c025Snicm }
782986c025Snicm 
792986c025Snicm /* Clear marked pane. */
802986c025Snicm void
server_clear_marked(void)812986c025Snicm server_clear_marked(void)
822986c025Snicm {
8393e732aaSnicm 	cmd_find_clear_state(&marked_pane, 0);
842986c025Snicm }
852986c025Snicm 
862986c025Snicm /* Is this the marked pane? */
872986c025Snicm int
server_is_marked(struct session * s,struct winlink * wl,struct window_pane * wp)882986c025Snicm server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
892986c025Snicm {
902986c025Snicm 	if (s == NULL || wl == NULL || wp == NULL)
912986c025Snicm 		return (0);
927519eda3Snicm 	if (marked_pane.s != s || marked_pane.wl != wl)
932986c025Snicm 		return (0);
947519eda3Snicm 	if (marked_pane.wp != wp)
952986c025Snicm 		return (0);
962986c025Snicm 	return (server_check_marked());
972986c025Snicm }
982986c025Snicm 
992986c025Snicm /* Check if the marked pane is still valid. */
1002986c025Snicm int
server_check_marked(void)1012986c025Snicm server_check_marked(void)
1022986c025Snicm {
1037519eda3Snicm 	return (cmd_find_valid_state(&marked_pane));
1042986c025Snicm }
1052986c025Snicm 
10689654e7cSnicm /* Create server socket. */
107ab0a70a7Snicm int
server_create_socket(uint64_t flags,char ** cause)108ab0a70a7Snicm server_create_socket(uint64_t flags, char **cause)
109311827fbSnicm {
11089654e7cSnicm 	struct sockaddr_un	sa;
11189654e7cSnicm 	size_t			size;
11289654e7cSnicm 	mode_t			mask;
113039206c6Snicm 	int			fd, saved_errno;
11489654e7cSnicm 
11589654e7cSnicm 	memset(&sa, 0, sizeof sa);
11689654e7cSnicm 	sa.sun_family = AF_UNIX;
11789654e7cSnicm 	size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
11889654e7cSnicm 	if (size >= sizeof sa.sun_path) {
11989654e7cSnicm 		errno = ENAMETOOLONG;
120039206c6Snicm 		goto fail;
12189654e7cSnicm 	}
12289654e7cSnicm 	unlink(sa.sun_path);
12389654e7cSnicm 
12489654e7cSnicm 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
125039206c6Snicm 		goto fail;
12689654e7cSnicm 
127b6d7e0c7Snicm 	if (flags & CLIENT_DEFAULTSOCKET)
1287394227cSnicm 		mask = umask(S_IXUSR|S_IXGRP|S_IRWXO);
129b6d7e0c7Snicm 	else
130b6d7e0c7Snicm 		mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
131039206c6Snicm 	if (bind(fd, (struct sockaddr *)&sa, sizeof sa) == -1) {
132039206c6Snicm 		saved_errno = errno;
133fb807b18Snicm 		close(fd);
134039206c6Snicm 		errno = saved_errno;
135039206c6Snicm 		goto fail;
136fb807b18Snicm 	}
13789654e7cSnicm 	umask(mask);
13889654e7cSnicm 
139fb807b18Snicm 	if (listen(fd, 128) == -1) {
140039206c6Snicm 		saved_errno = errno;
141fb807b18Snicm 		close(fd);
142039206c6Snicm 		errno = saved_errno;
143039206c6Snicm 		goto fail;
144fb807b18Snicm 	}
145a0ee4254Snicm 	setblocking(fd, 0);
146311827fbSnicm 
14789654e7cSnicm 	return (fd);
148039206c6Snicm 
149039206c6Snicm fail:
150039206c6Snicm 	if (cause != NULL) {
151039206c6Snicm 		xasprintf(cause, "error creating %s (%s)", socket_path,
152039206c6Snicm 		    strerror(errno));
153039206c6Snicm 	}
154039206c6Snicm 	return (-1);
155039206c6Snicm }
156039206c6Snicm 
15797887b4cSnicm /* Tidy up every hour. */
15897887b4cSnicm static void
server_tidy_event(__unused int fd,__unused short events,__unused void * data)15997887b4cSnicm server_tidy_event(__unused int fd, __unused short events, __unused void *data)
16097887b4cSnicm {
16197887b4cSnicm     struct timeval	tv = { .tv_sec = 3600 };
16297887b4cSnicm     uint64_t		t = get_timer();
16397887b4cSnicm 
16497887b4cSnicm     format_tidy_jobs();
16597887b4cSnicm 
1662bd728e0Snicm     log_debug("%s: took %llu milliseconds", __func__,
1672bd728e0Snicm         (unsigned long long)(get_timer() - t));
16897887b4cSnicm     evtimer_add(&server_ev_tidy, &tv);
16997887b4cSnicm }
17097887b4cSnicm 
171311827fbSnicm /* Fork new server. */
172311827fbSnicm int
server_start(struct tmuxproc * client,uint64_t flags,struct event_base * base,int lockfd,char * lockfile)173ab0a70a7Snicm server_start(struct tmuxproc *client, uint64_t flags, struct event_base *base,
174b6d7e0c7Snicm     int lockfd, char *lockfile)
175311827fbSnicm {
176ab26eabfSnicm 	int		 fd;
177189d1393Snicm 	sigset_t	 set, oldset;
17810e1651aSnicm 	struct client	*c = NULL;
179039206c6Snicm 	char		*cause = NULL;
18097887b4cSnicm 	struct timeval	 tv = { .tv_sec = 3600 };
181311827fbSnicm 
182189d1393Snicm 	sigfillset(&set);
183189d1393Snicm 	sigprocmask(SIG_BLOCK, &set, &oldset);
1849e0637eaSnicm 
1859e0637eaSnicm 	if (~flags & CLIENT_NOFORK) {
186ab26eabfSnicm 		if (proc_fork_and_daemon(&fd) != 0) {
187189d1393Snicm 			sigprocmask(SIG_SETMASK, &oldset, NULL);
188ab26eabfSnicm 			return (fd);
189311827fbSnicm 		}
1909e0637eaSnicm 	}
19189b52a5bSnicm 	proc_clear_signals(client, 0);
192ab26eabfSnicm 	server_client_flags = flags;
1939e0637eaSnicm 
194c37a9299Snicm 	if (event_reinit(base) != 0)
195c37a9299Snicm 		fatalx("event_reinit failed");
196c37a9299Snicm 	server_proc = proc_start("server");
1979e0637eaSnicm 
198c37a9299Snicm 	proc_set_signals(server_proc, server_signal);
199189d1393Snicm 	sigprocmask(SIG_SETMASK, &oldset, NULL);
200c37a9299Snicm 
20179c9b201Snicm 	if (log_get_level() > 1)
2026d2e9b46Snicm 		tty_create_log();
203cb19d99cSnicm 	if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec "
204cb19d99cSnicm 	    "tty ps", NULL) != 0)
2054e7a7adeSnicm 		fatal("pledge failed");
2064e7a7adeSnicm 
2079265d1acSnicm 	input_key_build();
208*3d40d63aSnicm 	utf8_update_width_cache();
209ae69181dSnicm 	RB_INIT(&windows);
2103e0f2533Snicm 	RB_INIT(&all_window_panes);
21182873134Snicm 	TAILQ_INIT(&clients);
21259996dc3Snicm 	RB_INIT(&sessions);
213311827fbSnicm 	key_bindings_init();
2140687354cSnicm 	TAILQ_INIT(&message_log);
2158c1ade38Snicm 	gettimeofday(&start_time, NULL);
216311827fbSnicm 
217b6d7e0c7Snicm 	server_fd = server_create_socket(flags, &cause);
218039206c6Snicm 	if (server_fd != -1)
219d53cf33dSnicm 		server_update_socket();
2209e0637eaSnicm 	if (~flags & CLIENT_NOFORK)
221ab26eabfSnicm 		c = server_client_create(fd);
2229e0637eaSnicm 	else
2239e0637eaSnicm 		options_set_number(global_options, "exit-empty", 0);
224311827fbSnicm 
2255c705c36Snicm 	if (lockfd >= 0) {
226549634dfSnicm 		unlink(lockfile);
2277d053cf9Snicm 		free(lockfile);
228549634dfSnicm 		close(lockfd);
2295c705c36Snicm 	}
230549634dfSnicm 
231039206c6Snicm 	if (cause != NULL) {
23210e1651aSnicm 		if (c != NULL) {
23387d557e0Snicm 			c->exit_message = cause;
234039206c6Snicm 			c->flags |= CLIENT_EXIT;
235b43857e5Snicm 		} else {
236b43857e5Snicm 			fprintf(stderr, "%s\n", cause);
237b43857e5Snicm 			exit(1);
238b43857e5Snicm 		}
23910e1651aSnicm 	}
2407724d0b0Snicm 
24197887b4cSnicm 	evtimer_set(&server_ev_tidy, server_tidy_event, NULL);
24297887b4cSnicm 	evtimer_add(&server_ev_tidy, &tv);
24397887b4cSnicm 
2448ab000fcSnicm 	server_acl_init();
2458ab000fcSnicm 
246432eaf59Snicm 	server_add_accept(0);
2475b8ac713Snicm 	proc_loop(server_proc, server_loop);
2481a432fefSnicm 
2499430fe9eSnicm 	job_kill_all();
250179ef399Snicm 	status_prompt_save_history();
2519430fe9eSnicm 
2527724d0b0Snicm 	exit(0);
253311827fbSnicm }
254311827fbSnicm 
2555b8ac713Snicm /* Server loop callback. */
2569883b791Snicm static int
server_loop(void)2577724d0b0Snicm server_loop(void)
258311827fbSnicm {
2595b8ac713Snicm 	struct client	*c;
260765b9a58Snicm 	u_int		 items;
261765b9a58Snicm 
262a711f92aSnicm 	current_time = time(NULL);
263a711f92aSnicm 
264765b9a58Snicm 	do {
265765b9a58Snicm 		items = cmdq_next(NULL);
2666a2dc005Snicm 		TAILQ_FOREACH(c, &clients, entry) {
2676a2dc005Snicm 			if (c->flags & CLIENT_IDENTIFIED)
268765b9a58Snicm 				items += cmdq_next(c);
2696a2dc005Snicm 		}
270765b9a58Snicm 	} while (items != 0);
271311827fbSnicm 
27289654e7cSnicm 	server_client_loop();
27330da262fSnicm 
2740d4a1e1fSnicm 	if (!options_get_number(global_options, "exit-empty") && !server_exit)
2750d4a1e1fSnicm 		return (0);
2760d4a1e1fSnicm 
277d89252e5Snicm 	if (!options_get_number(global_options, "exit-unattached")) {
27859996dc3Snicm 		if (!RB_EMPTY(&sessions))
27930da262fSnicm 			return (0);
28030da262fSnicm 	}
2817d5939e2Snicm 
28282873134Snicm 	TAILQ_FOREACH(c, &clients, entry) {
28382873134Snicm 		if (c->session != NULL)
2847d5939e2Snicm 			return (0);
2857d5939e2Snicm 	}
2867d5939e2Snicm 
2877d5939e2Snicm 	/*
2887d5939e2Snicm 	 * No attached clients therefore want to exit - flush any waiting
2897d5939e2Snicm 	 * clients but don't actually exit until they've gone.
2907d5939e2Snicm 	 */
2917d5939e2Snicm 	cmd_wait_for_flush();
29282873134Snicm 	if (!TAILQ_EMPTY(&clients))
29330da262fSnicm 		return (0);
2947d5939e2Snicm 
2959430fe9eSnicm 	if (job_still_running())
29649505a58Snicm 		return (0);
29749505a58Snicm 
29830da262fSnicm 	return (1);
299311827fbSnicm }
300311827fbSnicm 
3014d9325ceSnicm /* Exit the server by killing all clients and windows. */
3029883b791Snicm static void
server_send_exit(void)3034d9325ceSnicm server_send_exit(void)
304311827fbSnicm {
30582873134Snicm 	struct client	*c, *c1;
30682873134Snicm 	struct session	*s, *s1;
307311827fbSnicm 
3087d5939e2Snicm 	cmd_wait_for_flush();
3097d5939e2Snicm 
31082873134Snicm 	TAILQ_FOREACH_SAFE(c, &clients, entry, c1) {
3115b8ac713Snicm 		if (c->flags & CLIENT_SUSPENDED)
3127724d0b0Snicm 			server_client_lost(c);
31349505a58Snicm 		else {
314a34cf9c8Snicm 			c->flags |= CLIENT_EXIT;
315a34cf9c8Snicm 			c->exit_type = CLIENT_EXIT_SHUTDOWN;
31649505a58Snicm 		}
3177724d0b0Snicm 		c->session = NULL;
3188148c7feSnicm 	}
319311827fbSnicm 
32082873134Snicm 	RB_FOREACH_SAFE(s, sessions, &sessions, s1)
321c26c4f79Snicm 		session_destroy(s, 1, __func__);
322311827fbSnicm }
323311827fbSnicm 
3247724d0b0Snicm /* Update socket execute permissions based on whether sessions are attached. */
32537dfd443Snicm void
server_update_socket(void)3267724d0b0Snicm server_update_socket(void)
3277724d0b0Snicm {
3287724d0b0Snicm 	struct session	*s;
3297724d0b0Snicm 	static int	 last = -1;
3306d9987f5Snicm 	int		 n, mode;
3316d9987f5Snicm 	struct stat      sb;
3327724d0b0Snicm 
3337724d0b0Snicm 	n = 0;
33459996dc3Snicm 	RB_FOREACH(s, sessions, &sessions) {
335647c5c18Snicm 		if (s->attached != 0) {
3367724d0b0Snicm 			n++;
3377724d0b0Snicm 			break;
3387724d0b0Snicm 		}
3397724d0b0Snicm 	}
3407724d0b0Snicm 
3417724d0b0Snicm 	if (n != last) {
3427724d0b0Snicm 		last = n;
3436d9987f5Snicm 
3446d9987f5Snicm 		if (stat(socket_path, &sb) != 0)
3456d9987f5Snicm 			return;
346d61e0664Ssemarie 		mode = sb.st_mode & ACCESSPERMS;
3476d9987f5Snicm 		if (n != 0) {
3486d9987f5Snicm 			if (mode & S_IRUSR)
3496d9987f5Snicm 				mode |= S_IXUSR;
3506d9987f5Snicm 			if (mode & S_IRGRP)
3516d9987f5Snicm 				mode |= S_IXGRP;
3526d9987f5Snicm 			if (mode & S_IROTH)
3536d9987f5Snicm 				mode |= S_IXOTH;
3546d9987f5Snicm 		} else
3556d9987f5Snicm 			mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
3566d9987f5Snicm 		chmod(socket_path, mode);
3577724d0b0Snicm 	}
3587724d0b0Snicm }
3597724d0b0Snicm 
3607724d0b0Snicm /* Callback for server socket. */
3619883b791Snicm static void
server_accept(int fd,short events,__unused void * data)362d0e2e7f1Snicm server_accept(int fd, short events, __unused void *data)
3637724d0b0Snicm {
3647724d0b0Snicm 	struct sockaddr_storage	 sa;
3657724d0b0Snicm 	socklen_t		 slen = sizeof sa;
3667724d0b0Snicm 	int			 newfd;
3678ab000fcSnicm 	struct client		*c;
3687724d0b0Snicm 
369a06e463cSnicm 	server_add_accept(0);
3707724d0b0Snicm 	if (!(events & EV_READ))
3717724d0b0Snicm 		return;
3727724d0b0Snicm 
3737724d0b0Snicm 	newfd = accept(fd, (struct sockaddr *) &sa, &slen);
3747724d0b0Snicm 	if (newfd == -1) {
3757724d0b0Snicm 		if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
3767724d0b0Snicm 			return;
377a06e463cSnicm 		if (errno == ENFILE || errno == EMFILE) {
378a06e463cSnicm 			/* Delete and don't try again for 1 second. */
379a06e463cSnicm 			server_add_accept(1);
380a06e463cSnicm 			return;
381a06e463cSnicm 		}
3827724d0b0Snicm 		fatal("accept failed");
3837724d0b0Snicm 	}
3848ab000fcSnicm 
3854d9325ceSnicm 	if (server_exit) {
3867724d0b0Snicm 		close(newfd);
3877724d0b0Snicm 		return;
3887724d0b0Snicm 	}
3898ab000fcSnicm 	c = server_client_create(newfd);
3908ab000fcSnicm 	if (!server_acl_join(c)) {
3918ab000fcSnicm 		c->exit_message = xstrdup("access not allowed");
3928ab000fcSnicm 		c->flags |= CLIENT_EXIT;
3938ab000fcSnicm 	}
3947724d0b0Snicm }
3957724d0b0Snicm 
396a06e463cSnicm /*
397a06e463cSnicm  * Add accept event. If timeout is nonzero, add as a timeout instead of a read
398a06e463cSnicm  * event - used to backoff when running out of file descriptors.
399a06e463cSnicm  */
400a06e463cSnicm void
server_add_accept(int timeout)401a06e463cSnicm server_add_accept(int timeout)
402a06e463cSnicm {
403a06e463cSnicm 	struct timeval tv = { timeout, 0 };
404a06e463cSnicm 
405432eaf59Snicm 	if (server_fd == -1)
406432eaf59Snicm 		return;
407432eaf59Snicm 
408a06e463cSnicm 	if (event_initialized(&server_ev_accept))
409a06e463cSnicm 		event_del(&server_ev_accept);
410a06e463cSnicm 
411a06e463cSnicm 	if (timeout == 0) {
4125b8ac713Snicm 		event_set(&server_ev_accept, server_fd, EV_READ, server_accept,
4135b8ac713Snicm 		    NULL);
414a06e463cSnicm 		event_add(&server_ev_accept, NULL);
415a06e463cSnicm 	} else {
4165b8ac713Snicm 		event_set(&server_ev_accept, server_fd, EV_TIMEOUT,
4175b8ac713Snicm 		    server_accept, NULL);
418a06e463cSnicm 		event_add(&server_ev_accept, &tv);
419a06e463cSnicm 	}
420a06e463cSnicm }
421a06e463cSnicm 
4227724d0b0Snicm /* Signal handler. */
4239883b791Snicm static void
server_signal(int sig)4245b8ac713Snicm server_signal(int sig)
4257724d0b0Snicm {
426d53cf33dSnicm 	int	fd;
4275235cd7fSnicm 
428bc4816c6Snicm 	log_debug("%s: %s", __func__, strsignal(sig));
4297724d0b0Snicm 	switch (sig) {
4309e0637eaSnicm 	case SIGINT:
4317724d0b0Snicm 	case SIGTERM:
4324d9325ceSnicm 		server_exit = 1;
4334d9325ceSnicm 		server_send_exit();
4347724d0b0Snicm 		break;
4357724d0b0Snicm 	case SIGCHLD:
4367724d0b0Snicm 		server_child_signal();
4377724d0b0Snicm 		break;
4387724d0b0Snicm 	case SIGUSR1:
4397724d0b0Snicm 		event_del(&server_ev_accept);
440b6d7e0c7Snicm 		fd = server_create_socket(server_client_flags, NULL);
441d53cf33dSnicm 		if (fd != -1) {
4427724d0b0Snicm 			close(server_fd);
443d53cf33dSnicm 			server_fd = fd;
444d53cf33dSnicm 			server_update_socket();
445d53cf33dSnicm 		}
446a06e463cSnicm 		server_add_accept(0);
4477724d0b0Snicm 		break;
44879c9b201Snicm 	case SIGUSR2:
44979c9b201Snicm 		proc_toggle_log(server_proc);
45079c9b201Snicm 		break;
4517724d0b0Snicm 	}
4527724d0b0Snicm }
4537724d0b0Snicm 
4547724d0b0Snicm /* Handle SIGCHLD. */
4559883b791Snicm static void
server_child_signal(void)4567724d0b0Snicm server_child_signal(void)
4577724d0b0Snicm {
4587724d0b0Snicm 	int	 status;
4597724d0b0Snicm 	pid_t	 pid;
4607724d0b0Snicm 
4617724d0b0Snicm 	for (;;) {
4627724d0b0Snicm 		switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
4637724d0b0Snicm 		case -1:
4647724d0b0Snicm 			if (errno == ECHILD)
4657724d0b0Snicm 				return;
4667724d0b0Snicm 			fatal("waitpid failed");
4677724d0b0Snicm 		case 0:
4687724d0b0Snicm 			return;
4697724d0b0Snicm 		}
4707724d0b0Snicm 		if (WIFSTOPPED(status))
4717724d0b0Snicm 			server_child_stopped(pid, status);
472a236b623Snicm 		else if (WIFEXITED(status) || WIFSIGNALED(status))
4737724d0b0Snicm 			server_child_exited(pid, status);
4747724d0b0Snicm 	}
4757724d0b0Snicm }
4767724d0b0Snicm 
4777724d0b0Snicm /* Handle exited children. */
4789883b791Snicm static void
server_child_exited(pid_t pid,int status)4797724d0b0Snicm server_child_exited(pid_t pid, int status)
480311827fbSnicm {
481ae69181dSnicm 	struct window		*w, *w1;
48289654e7cSnicm 	struct window_pane	*wp;
4837724d0b0Snicm 
484ae69181dSnicm 	RB_FOREACH_SAFE(w, windows, &windows, w1) {
4857724d0b0Snicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
4867724d0b0Snicm 			if (wp->pid == pid) {
4873e459475Snicm 				wp->status = status;
488514fd64dSnicm 				wp->flags |= PANE_STATUSREADY;
489f35e5f52Snicm 
490f35e5f52Snicm 				log_debug("%%%u exited", wp->id);
491f35e5f52Snicm 				wp->flags |= PANE_EXITED;
492f35e5f52Snicm 
493f35e5f52Snicm 				if (window_pane_destroy_ready(wp))
4944fc586aaSnicm 					server_destroy_pane(wp, 1);
4957c6e570cSnicm 				break;
4967724d0b0Snicm 			}
4977724d0b0Snicm 		}
4987724d0b0Snicm 	}
4999430fe9eSnicm 	job_check_died(pid, status);
5007724d0b0Snicm }
5017724d0b0Snicm 
5027724d0b0Snicm /* Handle stopped children. */
5039883b791Snicm static void
server_child_stopped(pid_t pid,int status)5047724d0b0Snicm server_child_stopped(pid_t pid, int status)
5057724d0b0Snicm {
5067724d0b0Snicm 	struct window		*w;
5077724d0b0Snicm 	struct window_pane	*wp;
5087724d0b0Snicm 
5097724d0b0Snicm 	if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
5107724d0b0Snicm 		return;
5117724d0b0Snicm 
512ae69181dSnicm 	RB_FOREACH(w, windows, &windows) {
5137724d0b0Snicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
5147724d0b0Snicm 			if (wp->pid == pid) {
5157724d0b0Snicm 				if (killpg(pid, SIGCONT) != 0)
5167724d0b0Snicm 					kill(pid, SIGCONT);
5177724d0b0Snicm 			}
5187724d0b0Snicm 		}
5197724d0b0Snicm 	}
520a6c9106fSnicm 	job_check_died(pid, status);
5217724d0b0Snicm }
5220687354cSnicm 
5230687354cSnicm /* Add to message log. */
5240687354cSnicm void
server_add_message(const char * fmt,...)5250687354cSnicm server_add_message(const char *fmt, ...)
5260687354cSnicm {
5270687354cSnicm 	struct message_entry	*msg, *msg1;
5280687354cSnicm 	char			*s;
5290687354cSnicm 	va_list			 ap;
5300687354cSnicm 	u_int			 limit;
5310687354cSnicm 
5320687354cSnicm 	va_start(ap, fmt);
5330687354cSnicm 	xvasprintf(&s, fmt, ap);
5340687354cSnicm 	va_end(ap);
5350687354cSnicm 
5360687354cSnicm 	log_debug("message: %s", s);
5370687354cSnicm 
5380687354cSnicm 	msg = xcalloc(1, sizeof *msg);
5390687354cSnicm 	gettimeofday(&msg->msg_time, NULL);
5400687354cSnicm 	msg->msg_num = message_next++;
5410687354cSnicm 	msg->msg = s;
5420687354cSnicm 	TAILQ_INSERT_TAIL(&message_log, msg, entry);
5430687354cSnicm 
5440687354cSnicm 	limit = options_get_number(global_options, "message-limit");
5450687354cSnicm 	TAILQ_FOREACH_SAFE(msg, &message_log, entry, msg1) {
5460687354cSnicm 		if (msg->msg_num + limit >= message_next)
5470687354cSnicm 			break;
5480687354cSnicm 		free(msg->msg);
5490687354cSnicm 		TAILQ_REMOVE(&message_log, msg, entry);
5500687354cSnicm 		free(msg);
5510687354cSnicm 	}
5520687354cSnicm }
553