xref: /openbsd/usr.bin/tmux/server.c (revision b7fa064b)
1*b7fa064bSnicm /* $OpenBSD: server.c,v 1.45 2009/10/10 09:31:39 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
4311827fbSnicm  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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>
27311827fbSnicm #include <fcntl.h>
28a575a2dfSnicm #include <paths.h>
29311827fbSnicm #include <signal.h>
30311827fbSnicm #include <stdio.h>
31311827fbSnicm #include <stdlib.h>
32311827fbSnicm #include <string.h>
33311827fbSnicm #include <syslog.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 /* Client list. */
45311827fbSnicm struct clients	 clients;
467255ff90Snicm struct clients	 dead_clients;
47311827fbSnicm 
48*b7fa064bSnicm /* Mapping of a pollfd to an fd independent of its position in the array. */
49*b7fa064bSnicm struct poll_item {
50*b7fa064bSnicm 	struct pollfd	 pfd;
51*b7fa064bSnicm 
52*b7fa064bSnicm 	RB_ENTRY(poll_item) entry;
53*b7fa064bSnicm };
54*b7fa064bSnicm RB_HEAD(poll_items, poll_item) poll_items;
55*b7fa064bSnicm 
56*b7fa064bSnicm int		 server_poll_cmp(struct poll_item *, struct poll_item *);
57*b7fa064bSnicm struct pollfd	*server_poll_lookup(int);
58*b7fa064bSnicm void		 server_poll_add(int, int);
59*b7fa064bSnicm struct pollfd	*server_poll_flatten(int *);
60*b7fa064bSnicm void		 server_poll_parse(struct pollfd *);
61*b7fa064bSnicm void		 server_poll_reset(void);
62*b7fa064bSnicm RB_PROTOTYPE(poll_items, poll_item, entry, server_poll_cmp);
63*b7fa064bSnicm RB_GENERATE(poll_items, poll_item, entry, server_poll_cmp);
64*b7fa064bSnicm 
65980b7663Snicm void		 server_create_client(int);
66311827fbSnicm int		 server_create_socket(void);
67311827fbSnicm int		 server_main(int);
68311827fbSnicm void		 server_shutdown(void);
6930da262fSnicm int		 server_should_shutdown(void);
70311827fbSnicm void		 server_child_signal(void);
71*b7fa064bSnicm void		 server_fill_windows(void);
72*b7fa064bSnicm void		 server_handle_windows(void);
73*b7fa064bSnicm void		 server_fill_clients(void);
74*b7fa064bSnicm void		 server_handle_clients(void);
75980b7663Snicm void		 server_accept_client(int);
76311827fbSnicm void		 server_handle_client(struct client *);
77311827fbSnicm void		 server_handle_window(struct window *, struct window_pane *);
780903c7b9Snicm int		 server_check_window_bell(struct session *, struct window *);
79311827fbSnicm int		 server_check_window_activity(struct session *,
80311827fbSnicm 		      struct window *);
81311827fbSnicm int		 server_check_window_content(struct session *, struct window *,
82311827fbSnicm 		      struct window_pane *);
837255ff90Snicm void		 server_clean_dead(void);
84311827fbSnicm void		 server_lost_client(struct client *);
85311827fbSnicm void	 	 server_check_window(struct window *);
86311827fbSnicm void		 server_check_redraw(struct client *);
8795b31741Snicm void		 server_set_title(struct client *);
88311827fbSnicm void		 server_check_timers(struct client *);
89311827fbSnicm void		 server_second_timers(void);
90311827fbSnicm int		 server_update_socket(void);
91311827fbSnicm 
92*b7fa064bSnicm int
93*b7fa064bSnicm server_poll_cmp(struct poll_item *pitem1, struct poll_item *pitem2)
94*b7fa064bSnicm {
95*b7fa064bSnicm 	return (pitem1->pfd.fd - pitem2->pfd.fd);
96*b7fa064bSnicm }
97*b7fa064bSnicm 
98*b7fa064bSnicm struct pollfd *
99*b7fa064bSnicm server_poll_lookup(int fd)
100*b7fa064bSnicm {
101*b7fa064bSnicm 	struct poll_item	pitem;
102*b7fa064bSnicm 
103*b7fa064bSnicm 	pitem.pfd.fd = fd;
104*b7fa064bSnicm 	return (&RB_FIND(poll_items, &poll_items, &pitem)->pfd);
105*b7fa064bSnicm }
106*b7fa064bSnicm 
107*b7fa064bSnicm void
108*b7fa064bSnicm server_poll_add(int fd, int events)
109*b7fa064bSnicm {
110*b7fa064bSnicm 	struct poll_item	*pitem;
111*b7fa064bSnicm 
112*b7fa064bSnicm 	pitem = xmalloc(sizeof *pitem);
113*b7fa064bSnicm 	pitem->pfd.fd = fd;
114*b7fa064bSnicm 	pitem->pfd.events = events;
115*b7fa064bSnicm 	RB_INSERT(poll_items, &poll_items, pitem);
116*b7fa064bSnicm }
117*b7fa064bSnicm 
118*b7fa064bSnicm struct pollfd *
119*b7fa064bSnicm server_poll_flatten(int *nfds)
120*b7fa064bSnicm {
121*b7fa064bSnicm 	struct poll_item	*pitem;
122*b7fa064bSnicm 	struct pollfd		*pfds;
123*b7fa064bSnicm 
124*b7fa064bSnicm 	pfds = NULL;
125*b7fa064bSnicm 	*nfds = 0;
126*b7fa064bSnicm 	RB_FOREACH(pitem, poll_items, &poll_items) {
127*b7fa064bSnicm 		pfds = xrealloc(pfds, (*nfds) + 1, sizeof *pfds);
128*b7fa064bSnicm 		pfds[*nfds].fd = pitem->pfd.fd;
129*b7fa064bSnicm 		pfds[*nfds].events = pitem->pfd.events;
130*b7fa064bSnicm 		(*nfds)++;
131*b7fa064bSnicm 	}
132*b7fa064bSnicm 	return (pfds);
133*b7fa064bSnicm }
134*b7fa064bSnicm 
135*b7fa064bSnicm void
136*b7fa064bSnicm server_poll_parse(struct pollfd *pfds)
137*b7fa064bSnicm {
138*b7fa064bSnicm 	struct poll_item	*pitem;
139*b7fa064bSnicm 	int			 nfds;
140*b7fa064bSnicm 
141*b7fa064bSnicm 	nfds = 0;
142*b7fa064bSnicm 	RB_FOREACH(pitem, poll_items, &poll_items) {
143*b7fa064bSnicm 		pitem->pfd.revents = pfds[nfds].revents;
144*b7fa064bSnicm 		nfds++;
145*b7fa064bSnicm 	}
146*b7fa064bSnicm 	xfree(pfds);
147*b7fa064bSnicm }
148*b7fa064bSnicm 
149*b7fa064bSnicm void
150*b7fa064bSnicm server_poll_reset(void)
151*b7fa064bSnicm {
152*b7fa064bSnicm 	struct poll_item	*pitem;
153*b7fa064bSnicm 
154*b7fa064bSnicm 	while (!RB_EMPTY(&poll_items)) {
155*b7fa064bSnicm 		pitem = RB_ROOT(&poll_items);
156*b7fa064bSnicm 		RB_REMOVE(poll_items, &poll_items, pitem);
157*b7fa064bSnicm 		xfree(pitem);
158*b7fa064bSnicm 	}
159*b7fa064bSnicm }
160*b7fa064bSnicm 
161311827fbSnicm /* Create a new client. */
162980b7663Snicm void
163311827fbSnicm server_create_client(int fd)
164311827fbSnicm {
165311827fbSnicm 	struct client	*c;
166311827fbSnicm 	int		 mode;
167311827fbSnicm 	u_int		 i;
168311827fbSnicm 
169311827fbSnicm 	if ((mode = fcntl(fd, F_GETFL)) == -1)
170311827fbSnicm 		fatal("fcntl failed");
171311827fbSnicm 	if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
172311827fbSnicm 		fatal("fcntl failed");
173311827fbSnicm 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
174311827fbSnicm 		fatal("fcntl failed");
175311827fbSnicm 
176311827fbSnicm 	c = xcalloc(1, sizeof *c);
1777255ff90Snicm 	c->references = 0;
178fd234c13Snicm 	imsg_init(&c->ibuf, fd);
179311827fbSnicm 
18060ea6095Snicm 	if (gettimeofday(&c->tv, NULL) != 0)
18160ea6095Snicm 		fatal("gettimeofday failed");
18260ea6095Snicm 
183311827fbSnicm 	ARRAY_INIT(&c->prompt_hdata);
184311827fbSnicm 
185311827fbSnicm 	c->tty.fd = -1;
186311827fbSnicm 	c->title = NULL;
187311827fbSnicm 
188311827fbSnicm 	c->session = NULL;
189311827fbSnicm 	c->tty.sx = 80;
19088081898Snicm 	c->tty.sy = 24;
191311827fbSnicm 	screen_init(&c->status, c->tty.sx, 1, 0);
192311827fbSnicm 
193311827fbSnicm 	c->message_string = NULL;
194311827fbSnicm 
195311827fbSnicm 	c->prompt_string = NULL;
196311827fbSnicm 	c->prompt_buffer = NULL;
197311827fbSnicm 	c->prompt_index = 0;
198311827fbSnicm 
199311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
200311827fbSnicm 		if (ARRAY_ITEM(&clients, i) == NULL) {
201311827fbSnicm 			ARRAY_SET(&clients, i, c);
202980b7663Snicm 			return;
203311827fbSnicm 		}
204311827fbSnicm 	}
205311827fbSnicm 	ARRAY_ADD(&clients, c);
2060bb39cd5Snicm 	log_debug("new client %d", fd);
207311827fbSnicm }
208311827fbSnicm 
209311827fbSnicm /* Fork new server. */
210311827fbSnicm int
211311827fbSnicm server_start(char *path)
212311827fbSnicm {
2130cd9638eSnicm 	struct client	*c;
2140cd9638eSnicm 	int		 pair[2], srv_fd;
215311827fbSnicm 	char		*cause;
216311827fbSnicm 	char		 rpathbuf[MAXPATHLEN];
217311827fbSnicm 
218311827fbSnicm 	/* The first client is special and gets a socketpair; create it. */
219311827fbSnicm 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
220311827fbSnicm 		fatal("socketpair failed");
221311827fbSnicm 
222311827fbSnicm 	switch (fork()) {
223311827fbSnicm 	case -1:
224311827fbSnicm 		fatal("fork failed");
225311827fbSnicm 	case 0:
226311827fbSnicm 		break;
227311827fbSnicm 	default:
228311827fbSnicm 		close(pair[1]);
229311827fbSnicm 		return (pair[0]);
230311827fbSnicm 	}
231311827fbSnicm 	close(pair[0]);
232311827fbSnicm 
233311827fbSnicm 	/*
234311827fbSnicm 	 * Must daemonise before loading configuration as the PID changes so
235311827fbSnicm 	 * $TMUX would be wrong for sessions created in the config file.
236311827fbSnicm 	 */
2370cd9638eSnicm 	if (daemon(1, 0) != 0)
238311827fbSnicm 		fatal("daemon failed");
239311827fbSnicm 
2400cd9638eSnicm 	logfile("server");
2410cd9638eSnicm 	log_debug("server started, pid %ld", (long) getpid());
2420cd9638eSnicm 
243311827fbSnicm 	ARRAY_INIT(&windows);
244311827fbSnicm 	ARRAY_INIT(&clients);
2457255ff90Snicm 	ARRAY_INIT(&dead_clients);
246311827fbSnicm 	ARRAY_INIT(&sessions);
2477255ff90Snicm 	ARRAY_INIT(&dead_sessions);
24868895571Snicm 	mode_key_init_trees();
249311827fbSnicm 	key_bindings_init();
250311827fbSnicm 	utf8_build();
251311827fbSnicm 
252311827fbSnicm 	server_activity = time(NULL);
253311827fbSnicm 
254311827fbSnicm 	start_time = time(NULL);
255311827fbSnicm 	socket_path = path;
256311827fbSnicm 
257311827fbSnicm 	if (realpath(socket_path, rpathbuf) == NULL)
258311827fbSnicm 		strlcpy(rpathbuf, socket_path, sizeof rpathbuf);
2590cd9638eSnicm 	log_debug("socket path %s", socket_path);
260311827fbSnicm 	setproctitle("server (%s)", rpathbuf);
261311827fbSnicm 
262311827fbSnicm 	srv_fd = server_create_socket();
263311827fbSnicm 	server_create_client(pair[1]);
264311827fbSnicm 
2650cd9638eSnicm 	if (access(SYSTEM_CFG, R_OK) != 0) {
2660cd9638eSnicm 		if (errno != ENOENT) {
2670cd9638eSnicm 			xasprintf(
2680cd9638eSnicm 			    &cause, "%s: %s", strerror(errno), SYSTEM_CFG);
2690cd9638eSnicm 			goto error;
2700cd9638eSnicm 		}
271c0a52a07Snicm 	} else if (load_cfg(SYSTEM_CFG, NULL, &cause) != 0)
2720cd9638eSnicm 		goto error;
273c0a52a07Snicm 	if (cfg_file != NULL && load_cfg(cfg_file, NULL, &cause) != 0)
2740cd9638eSnicm 		goto error;
2750cd9638eSnicm 
2760cd9638eSnicm 	exit(server_main(srv_fd));
2770cd9638eSnicm 
2780cd9638eSnicm error:
2790cd9638eSnicm 	/* Write the error and shutdown the server. */
2800cd9638eSnicm 	c = ARRAY_FIRST(&clients);
2810cd9638eSnicm 
2820cd9638eSnicm 	server_write_error(c, cause);
2830cd9638eSnicm 	xfree(cause);
2840cd9638eSnicm 
2855879d031Snicm 	sigterm = 1;
2860cd9638eSnicm 	server_shutdown();
2870cd9638eSnicm 
288311827fbSnicm 	exit(server_main(srv_fd));
289311827fbSnicm }
290311827fbSnicm 
291311827fbSnicm /* Create server socket. */
292311827fbSnicm int
293311827fbSnicm server_create_socket(void)
294311827fbSnicm {
295311827fbSnicm 	struct sockaddr_un	sa;
296311827fbSnicm 	size_t			size;
297311827fbSnicm 	mode_t			mask;
298311827fbSnicm 	int			fd, mode;
299311827fbSnicm 
300311827fbSnicm 	memset(&sa, 0, sizeof sa);
301311827fbSnicm 	sa.sun_family = AF_UNIX;
302311827fbSnicm 	size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
303311827fbSnicm 	if (size >= sizeof sa.sun_path) {
304311827fbSnicm 		errno = ENAMETOOLONG;
305311827fbSnicm 		fatal("socket failed");
306311827fbSnicm 	}
307311827fbSnicm 	unlink(sa.sun_path);
308311827fbSnicm 
309311827fbSnicm 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
310311827fbSnicm 		fatal("socket failed");
311311827fbSnicm 
312311827fbSnicm 	mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
313311827fbSnicm 	if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1)
314311827fbSnicm 		fatal("bind failed");
315311827fbSnicm 	umask(mask);
316311827fbSnicm 
317311827fbSnicm 	if (listen(fd, 16) == -1)
318311827fbSnicm 		fatal("listen failed");
319311827fbSnicm 
320311827fbSnicm 	if ((mode = fcntl(fd, F_GETFL)) == -1)
321311827fbSnicm 		fatal("fcntl failed");
322311827fbSnicm 	if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
323311827fbSnicm 		fatal("fcntl failed");
324311827fbSnicm 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
325311827fbSnicm 		fatal("fcntl failed");
326311827fbSnicm 
327311827fbSnicm 	return (fd);
328311827fbSnicm }
329311827fbSnicm 
330311827fbSnicm /* Main server loop. */
331311827fbSnicm int
332311827fbSnicm server_main(int srv_fd)
333311827fbSnicm {
334311827fbSnicm 	struct pollfd	*pfds, *pfd;
335311827fbSnicm 	int		 nfds, xtimeout;
33630da262fSnicm 	u_int		 i;
337311827fbSnicm 	time_t		 now, last;
338311827fbSnicm 
339311827fbSnicm 	siginit();
3400bb39cd5Snicm 	log_debug("server socket is %d", srv_fd);
341311827fbSnicm 
342311827fbSnicm 	last = time(NULL);
343311827fbSnicm 
344311827fbSnicm 	pfds = NULL;
345311827fbSnicm 	for (;;) {
346311827fbSnicm 		/* If sigterm, kill all windows and clients. */
347311827fbSnicm 		if (sigterm)
348311827fbSnicm 			server_shutdown();
349311827fbSnicm 
35030da262fSnicm 		/* Stop if no sessions or clients left. */
35130da262fSnicm 		if (server_should_shutdown())
35230da262fSnicm 			break;
35330da262fSnicm 
354311827fbSnicm 		/* Handle child exit. */
355311827fbSnicm 		if (sigchld) {
356311827fbSnicm 			server_child_signal();
357311827fbSnicm 			sigchld = 0;
358311827fbSnicm 		}
359311827fbSnicm 
360311827fbSnicm 		/* Recreate socket on SIGUSR1. */
361311827fbSnicm 		if (sigusr1) {
362311827fbSnicm 			close(srv_fd);
363311827fbSnicm 			srv_fd = server_create_socket();
364311827fbSnicm 			sigusr1 = 0;
365311827fbSnicm 		}
366311827fbSnicm 
367*b7fa064bSnicm 		/* Initialise pollfd array and add server socket. */
368*b7fa064bSnicm 		server_poll_reset();
369*b7fa064bSnicm 		server_poll_add(srv_fd, POLLIN);
370311827fbSnicm 
371311827fbSnicm 		/* Fill window and client sockets. */
372*b7fa064bSnicm 		server_fill_windows();
373*b7fa064bSnicm 		server_fill_clients();
374311827fbSnicm 
375311827fbSnicm 		/* Update socket permissions. */
376311827fbSnicm 		xtimeout = INFTIM;
3775879d031Snicm 		if (server_update_socket() != 0)
378311827fbSnicm 			xtimeout = POLL_TIMEOUT;
379311827fbSnicm 
380311827fbSnicm 		/* Do the poll. */
381*b7fa064bSnicm 		pfds = server_poll_flatten(&nfds);
382*b7fa064bSnicm 		log_debug("polling %d", nfds);
3839b1fc963Snicm 		if (poll(pfds, nfds, xtimeout) == -1) {
384311827fbSnicm 			if (errno == EAGAIN || errno == EINTR)
385311827fbSnicm 				continue;
386311827fbSnicm 			fatal("poll failed");
387311827fbSnicm 		}
388*b7fa064bSnicm 		server_poll_parse(pfds);
389311827fbSnicm 
390311827fbSnicm 		/* Handle server socket. */
391*b7fa064bSnicm 		pfd = server_poll_lookup(srv_fd);
392311827fbSnicm 		if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP))
393311827fbSnicm 			fatalx("lost server socket");
394311827fbSnicm 		if (pfd->revents & POLLIN) {
395311827fbSnicm 			server_accept_client(srv_fd);
396311827fbSnicm 			continue;
397311827fbSnicm 		}
398311827fbSnicm 
399311827fbSnicm 		/* Call second-based timers. */
400311827fbSnicm 		now = time(NULL);
401311827fbSnicm 		if (now != last) {
402311827fbSnicm 			last = now;
403311827fbSnicm 			server_second_timers();
404311827fbSnicm 		}
405311827fbSnicm 
406311827fbSnicm 		/* Set window names. */
407311827fbSnicm 		set_window_names();
408311827fbSnicm 
409311827fbSnicm 		/*
410311827fbSnicm 		 * Handle window and client sockets. Clients can create
411311827fbSnicm 		 * windows, so windows must come first to avoid messing up by
412311827fbSnicm 		 * increasing the array size.
413311827fbSnicm 		 */
414*b7fa064bSnicm 		server_handle_windows();
415*b7fa064bSnicm 		server_handle_clients();
416311827fbSnicm 
4170476b68eSnicm 		/* Collect any unset key bindings. */
4180476b68eSnicm 		key_bindings_clean();
4190476b68eSnicm 
4207255ff90Snicm 		/* Collect dead clients and sessions. */
4217255ff90Snicm 		server_clean_dead();
422311827fbSnicm 	}
423*b7fa064bSnicm 	server_poll_reset();
424311827fbSnicm 
425311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
426311827fbSnicm 		if (ARRAY_ITEM(&sessions, i) != NULL)
427311827fbSnicm 			session_destroy(ARRAY_ITEM(&sessions, i));
428311827fbSnicm 	}
429311827fbSnicm 	ARRAY_FREE(&sessions);
430311827fbSnicm 
431311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
432311827fbSnicm 		if (ARRAY_ITEM(&clients, i) != NULL)
433311827fbSnicm 			server_lost_client(ARRAY_ITEM(&clients, i));
434311827fbSnicm 	}
435311827fbSnicm 	ARRAY_FREE(&clients);
436311827fbSnicm 
43768895571Snicm 	mode_key_free_trees();
438311827fbSnicm 	key_bindings_free();
439311827fbSnicm 
440311827fbSnicm 	close(srv_fd);
441311827fbSnicm 
442311827fbSnicm 	unlink(socket_path);
443311827fbSnicm 	xfree(socket_path);
444311827fbSnicm 
445eaecedb2Snicm 	options_free(&global_s_options);
446eaecedb2Snicm 	options_free(&global_w_options);
447311827fbSnicm 
448311827fbSnicm 	return (0);
449311827fbSnicm }
450311827fbSnicm 
451311827fbSnicm /* Kill all clients. */
452311827fbSnicm void
453311827fbSnicm server_shutdown(void)
454311827fbSnicm {
455311827fbSnicm 	struct session	*s;
456311827fbSnicm 	struct client	*c;
457311827fbSnicm 	u_int		 i, j;
458311827fbSnicm 
45930da262fSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
46030da262fSnicm 		c = ARRAY_ITEM(&clients, i);
46130da262fSnicm 		if (c != NULL) {
46230da262fSnicm 			if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED))
46330da262fSnicm 				server_lost_client(c);
46430da262fSnicm 			else
46530da262fSnicm 				server_write_client(c, MSG_SHUTDOWN, NULL, 0);
46630da262fSnicm 		}
46730da262fSnicm 	}
46830da262fSnicm 
469311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
470311827fbSnicm 		s = ARRAY_ITEM(&sessions, i);
471311827fbSnicm 		for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
472311827fbSnicm 			c = ARRAY_ITEM(&clients, j);
473311827fbSnicm 			if (c != NULL && c->session == s) {
474311827fbSnicm 				s = NULL;
475311827fbSnicm 				break;
476311827fbSnicm 			}
477311827fbSnicm 		}
478311827fbSnicm 		if (s != NULL)
479311827fbSnicm 			session_destroy(s);
480311827fbSnicm 	}
48130da262fSnicm }
482311827fbSnicm 
48330da262fSnicm /* Check if the server should be shutting down (no more clients or windows). */
48430da262fSnicm int
48530da262fSnicm server_should_shutdown(void)
48630da262fSnicm {
48730da262fSnicm 	u_int	i;
48830da262fSnicm 
48930da262fSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
49030da262fSnicm 		if (ARRAY_ITEM(&sessions, i) != NULL)
49130da262fSnicm 			return (0);
49230da262fSnicm 	}
493311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
49430da262fSnicm 		if (ARRAY_ITEM(&clients, i) != NULL)
49530da262fSnicm 			return (0);
4960cd9638eSnicm 	}
49730da262fSnicm 	return (1);
498311827fbSnicm }
499311827fbSnicm 
500311827fbSnicm /* Handle SIGCHLD. */
501311827fbSnicm void
502311827fbSnicm server_child_signal(void)
503311827fbSnicm {
504311827fbSnicm 	struct window		*w;
505311827fbSnicm 	struct window_pane	*wp;
506311827fbSnicm 	int		 	 status;
507311827fbSnicm 	pid_t		 	 pid;
508311827fbSnicm 	u_int		 	 i;
509311827fbSnicm 
510311827fbSnicm 	for (;;) {
511311827fbSnicm 		switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
512311827fbSnicm 		case -1:
513311827fbSnicm 			if (errno == ECHILD)
514311827fbSnicm 				return;
51585d997dcSnicm 			fatal("waitpid failed");
516311827fbSnicm 		case 0:
517311827fbSnicm 			return;
518311827fbSnicm 		}
519311827fbSnicm 		if (!WIFSTOPPED(status))
520311827fbSnicm 			continue;
521311827fbSnicm 		if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
522311827fbSnicm 			continue;
523311827fbSnicm 
524311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
525311827fbSnicm 			w = ARRAY_ITEM(&windows, i);
526311827fbSnicm 			if (w == NULL)
527311827fbSnicm 				continue;
528311827fbSnicm 			TAILQ_FOREACH(wp, &w->panes, entry) {
529311827fbSnicm 				if (wp->pid == pid) {
530311827fbSnicm 					if (killpg(pid, SIGCONT) != 0)
531311827fbSnicm 						kill(pid, SIGCONT);
532311827fbSnicm 				}
533311827fbSnicm 			}
534311827fbSnicm 		}
535311827fbSnicm 	}
536311827fbSnicm }
537311827fbSnicm 
538311827fbSnicm /* Fill window pollfds. */
539311827fbSnicm void
540*b7fa064bSnicm server_fill_windows(void)
541311827fbSnicm {
542311827fbSnicm 	struct window		*w;
543311827fbSnicm 	struct window_pane	*wp;
544311827fbSnicm 	u_int		 	 i;
545*b7fa064bSnicm 	int			 events;
546311827fbSnicm 
547311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
548311827fbSnicm 		w = ARRAY_ITEM(&windows, i);
549311827fbSnicm 		if (w == NULL)
550311827fbSnicm 			continue;
551311827fbSnicm 
552311827fbSnicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
553*b7fa064bSnicm 			if (wp->fd == -1)
554*b7fa064bSnicm 				continue;
555*b7fa064bSnicm 			events = POLLIN;
556311827fbSnicm 			if (BUFFER_USED(wp->out) > 0)
557*b7fa064bSnicm 				events |= POLLOUT;
558*b7fa064bSnicm 			server_poll_add(wp->fd, events);
559311827fbSnicm 		}
560311827fbSnicm 	}
561311827fbSnicm }
562311827fbSnicm 
563311827fbSnicm /* Handle window pollfds. */
564311827fbSnicm void
565*b7fa064bSnicm server_handle_windows(void)
566311827fbSnicm {
567311827fbSnicm 	struct window		*w;
568311827fbSnicm 	struct window_pane	*wp;
569*b7fa064bSnicm 	struct pollfd		*pfd;
570311827fbSnicm 	u_int		 	 i;
571311827fbSnicm 
572311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
573311827fbSnicm 		w = ARRAY_ITEM(&windows, i);
574311827fbSnicm 		if (w == NULL)
575311827fbSnicm 			continue;
576311827fbSnicm 
577311827fbSnicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
578*b7fa064bSnicm 			if (wp->fd == -1)
579*b7fa064bSnicm 				continue;
580*b7fa064bSnicm 			if ((pfd = server_poll_lookup(wp->fd)) == NULL)
581*b7fa064bSnicm 				continue;
582*b7fa064bSnicm 			if (buffer_poll(pfd, wp->in, wp->out) != 0) {
583311827fbSnicm 				close(wp->fd);
584311827fbSnicm 				wp->fd = -1;
585311827fbSnicm 			} else
586311827fbSnicm 				server_handle_window(w, wp);
587311827fbSnicm 		}
588311827fbSnicm 
589311827fbSnicm 		server_check_window(w);
590311827fbSnicm 	}
591311827fbSnicm }
592311827fbSnicm 
593311827fbSnicm /* Check for general redraw on client. */
594311827fbSnicm void
595311827fbSnicm server_check_redraw(struct client *c)
596311827fbSnicm {
597311827fbSnicm 	struct session		*s;
598311827fbSnicm 	struct window_pane	*wp;
599311827fbSnicm 	int		 	 flags, redraw;
600311827fbSnicm 
601311827fbSnicm 	if (c == NULL || c->session == NULL)
602311827fbSnicm 		return;
603311827fbSnicm 	s = c->session;
604311827fbSnicm 
605311827fbSnicm 	flags = c->tty.flags & TTY_FREEZE;
606311827fbSnicm 	c->tty.flags &= ~TTY_FREEZE;
607311827fbSnicm 
608311827fbSnicm 	if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
60995b31741Snicm 		if (options_get_number(&s->options, "set-titles"))
61095b31741Snicm 			server_set_title(c);
61195b31741Snicm 
612311827fbSnicm 		if (c->message_string != NULL)
613311827fbSnicm 			redraw = status_message_redraw(c);
614311827fbSnicm 		else if (c->prompt_string != NULL)
615311827fbSnicm 			redraw = status_prompt_redraw(c);
616311827fbSnicm 		else
617311827fbSnicm 			redraw = status_redraw(c);
618311827fbSnicm 		if (!redraw)
619311827fbSnicm 			c->flags &= ~CLIENT_STATUS;
620311827fbSnicm 	}
621311827fbSnicm 
622311827fbSnicm 	if (c->flags & CLIENT_REDRAW) {
62379fc95a8Snicm 		screen_redraw_screen(c, 0);
624311827fbSnicm 		c->flags &= ~CLIENT_STATUS;
625311827fbSnicm 	} else {
626311827fbSnicm 		TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
627311827fbSnicm 			if (wp->flags & PANE_REDRAW)
628311827fbSnicm 				screen_redraw_pane(c, wp);
629311827fbSnicm 		}
630311827fbSnicm 	}
631311827fbSnicm 
632311827fbSnicm 	if (c->flags & CLIENT_STATUS)
63379fc95a8Snicm 		screen_redraw_screen(c, 1);
634311827fbSnicm 
635311827fbSnicm 	c->tty.flags |= flags;
636311827fbSnicm 
637311827fbSnicm 	c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS);
638311827fbSnicm }
639311827fbSnicm 
64095b31741Snicm /* Set client title. */
64195b31741Snicm void
64295b31741Snicm server_set_title(struct client *c)
64395b31741Snicm {
64495b31741Snicm 	struct session	*s = c->session;
64595b31741Snicm 	const char	*template;
64695b31741Snicm 	char		*title;
64795b31741Snicm 
64895b31741Snicm 	template = options_get_string(&s->options, "set-titles-string");
64995b31741Snicm 
65095b31741Snicm 	title = status_replace(c->session, template, time(NULL));
65195b31741Snicm 	if (c->title == NULL || strcmp(title, c->title) != 0) {
65295b31741Snicm 		if (c->title != NULL)
65395b31741Snicm 			xfree(c->title);
65495b31741Snicm 		c->title = xstrdup(title);
65595b31741Snicm 		tty_set_title(&c->tty, c->title);
65695b31741Snicm 	}
65795b31741Snicm 	xfree(title);
65895b31741Snicm }
65995b31741Snicm 
660311827fbSnicm /* Check for timers on client. */
661311827fbSnicm void
662311827fbSnicm server_check_timers(struct client *c)
663311827fbSnicm {
664311827fbSnicm 	struct session	*s;
665311827fbSnicm 	struct timeval	 tv;
666311827fbSnicm 	u_int		 interval;
667311827fbSnicm 
668311827fbSnicm 	if (c == NULL || c->session == NULL)
669311827fbSnicm 		return;
670311827fbSnicm 	s = c->session;
671311827fbSnicm 
672311827fbSnicm 	if (gettimeofday(&tv, NULL) != 0)
67385d997dcSnicm 		fatal("gettimeofday failed");
674311827fbSnicm 
675669c539cSnicm 	if (c->flags & CLIENT_IDENTIFY && timercmp(&tv, &c->identify_timer, >))
676669c539cSnicm 		server_clear_identify(c);
677669c539cSnicm 
678311827fbSnicm 	if (c->message_string != NULL && timercmp(&tv, &c->message_timer, >))
679311827fbSnicm 		status_message_clear(c);
680311827fbSnicm 
681311827fbSnicm 	if (c->message_string != NULL || c->prompt_string != NULL) {
682311827fbSnicm 		/*
683311827fbSnicm 		 * Don't need timed redraw for messages/prompts so bail now.
684311827fbSnicm 		 * The status timer isn't reset when they are redrawn anyway.
685311827fbSnicm 		 */
686311827fbSnicm 		return;
687311827fbSnicm 	}
688311827fbSnicm 	if (!options_get_number(&s->options, "status"))
689311827fbSnicm 		return;
690311827fbSnicm 
691311827fbSnicm 	/* Check timer; resolution is only a second so don't be too clever. */
692311827fbSnicm 	interval = options_get_number(&s->options, "status-interval");
693311827fbSnicm 	if (interval == 0)
694311827fbSnicm 		return;
695311827fbSnicm 	if (tv.tv_sec < c->status_timer.tv_sec ||
696311827fbSnicm 	    ((u_int) tv.tv_sec) - c->status_timer.tv_sec >= interval)
697311827fbSnicm 		c->flags |= CLIENT_STATUS;
698311827fbSnicm }
699311827fbSnicm 
700311827fbSnicm /* Fill client pollfds. */
701311827fbSnicm void
702*b7fa064bSnicm server_fill_clients(void)
703311827fbSnicm {
704311827fbSnicm 	struct client		*c;
705311827fbSnicm 	struct window		*w;
706311827fbSnicm 	struct window_pane	*wp;
707311827fbSnicm 	u_int		 	 i;
708*b7fa064bSnicm 	int			 events;
709311827fbSnicm 
710311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
711311827fbSnicm 		c = ARRAY_ITEM(&clients, i);
712311827fbSnicm 
713311827fbSnicm 		server_check_timers(c);
714311827fbSnicm 		server_check_redraw(c);
715311827fbSnicm 
716*b7fa064bSnicm 		if (c != NULL) {
717*b7fa064bSnicm 			events = 0;
7180cd9638eSnicm 			if (!(c->flags & CLIENT_BAD))
719*b7fa064bSnicm 				events |= POLLIN;
720fd234c13Snicm 			if (c->ibuf.w.queued > 0)
721*b7fa064bSnicm 				events |= POLLOUT;
722*b7fa064bSnicm 			server_poll_add(c->ibuf.fd, events);
723311827fbSnicm 		}
724311827fbSnicm 
725*b7fa064bSnicm 		if (c != NULL && !(c->flags & CLIENT_SUSPENDED) &&
726*b7fa064bSnicm 		    c->tty.fd != -1 && c->session != NULL) {
727*b7fa064bSnicm 			events = POLLIN;
728311827fbSnicm 			if (BUFFER_USED(c->tty.out) > 0)
729*b7fa064bSnicm 				events |= POLLOUT;
730*b7fa064bSnicm 			server_poll_add(c->tty.fd, events);
731311827fbSnicm 		}
732311827fbSnicm 	}
733311827fbSnicm 
734311827fbSnicm 	/*
735311827fbSnicm 	 * Clear any window redraw flags (will have been redrawn as part of
736311827fbSnicm 	 * client).
737311827fbSnicm 	 */
738311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
739311827fbSnicm 		w = ARRAY_ITEM(&windows, i);
740311827fbSnicm 		if (w == NULL)
741311827fbSnicm 			continue;
742311827fbSnicm 
743311827fbSnicm 		w->flags &= ~WINDOW_REDRAW;
744311827fbSnicm 		TAILQ_FOREACH(wp, &w->panes, entry)
745311827fbSnicm 			wp->flags &= ~PANE_REDRAW;
746311827fbSnicm 	}
747311827fbSnicm }
748311827fbSnicm 
749311827fbSnicm /* Handle client pollfds. */
750311827fbSnicm void
751*b7fa064bSnicm server_handle_clients(void)
752311827fbSnicm {
753311827fbSnicm 	struct client	*c;
754*b7fa064bSnicm 	struct pollfd	*pfd;
755311827fbSnicm 	u_int		 i;
756311827fbSnicm 
757311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
758311827fbSnicm 		c = ARRAY_ITEM(&clients, i);
759311827fbSnicm 
760311827fbSnicm 		if (c != NULL) {
761*b7fa064bSnicm 			if ((pfd = server_poll_lookup(c->ibuf.fd)) == NULL)
762*b7fa064bSnicm 				continue;
763*b7fa064bSnicm 			if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP)) {
764311827fbSnicm 				server_lost_client(c);
765311827fbSnicm 				continue;
766fd234c13Snicm 			}
767fd234c13Snicm 
768*b7fa064bSnicm 			if (pfd->revents & POLLOUT) {
769fd234c13Snicm 				if (msgbuf_write(&c->ibuf.w) < 0) {
7700cd9638eSnicm 					server_lost_client(c);
7710cd9638eSnicm 					continue;
772fd234c13Snicm 				}
773fd234c13Snicm 			}
774fd234c13Snicm 
775fd234c13Snicm 			if (c->flags & CLIENT_BAD) {
776fd234c13Snicm 				if (c->ibuf.w.queued == 0)
777fd234c13Snicm 					server_lost_client(c);
778fd234c13Snicm 				continue;
779*b7fa064bSnicm 			} else if (pfd->revents & POLLIN) {
780fd234c13Snicm 				if (server_msg_dispatch(c) != 0) {
781fd234c13Snicm 					server_lost_client(c);
782fd234c13Snicm 					continue;
783fd234c13Snicm 				}
784fd234c13Snicm 			}
785311827fbSnicm 		}
786311827fbSnicm 
787311827fbSnicm 		if (c != NULL && !(c->flags & CLIENT_SUSPENDED) &&
788311827fbSnicm 		    c->tty.fd != -1 && c->session != NULL) {
789*b7fa064bSnicm 			if ((pfd = server_poll_lookup(c->tty.fd)) == NULL)
790*b7fa064bSnicm 				continue;
791*b7fa064bSnicm 			if (buffer_poll(pfd, c->tty.in, c->tty.out) != 0)
792311827fbSnicm 				server_lost_client(c);
793311827fbSnicm 			else
794311827fbSnicm 				server_handle_client(c);
795311827fbSnicm 		}
796311827fbSnicm 	}
797311827fbSnicm }
798311827fbSnicm 
799311827fbSnicm /* accept(2) and create new client. */
800980b7663Snicm void
801311827fbSnicm server_accept_client(int srv_fd)
802311827fbSnicm {
803311827fbSnicm 	struct sockaddr_storage	sa;
804311827fbSnicm 	socklen_t		slen = sizeof sa;
805311827fbSnicm 	int			fd;
806311827fbSnicm 
807311827fbSnicm 	fd = accept(srv_fd, (struct sockaddr *) &sa, &slen);
808311827fbSnicm 	if (fd == -1) {
809311827fbSnicm 		if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
810980b7663Snicm 			return;
811311827fbSnicm 		fatal("accept failed");
812311827fbSnicm 	}
813311827fbSnicm 	if (sigterm) {
814311827fbSnicm 		close(fd);
815980b7663Snicm 		return;
816311827fbSnicm 	}
817980b7663Snicm 	server_create_client(fd);
818311827fbSnicm }
819311827fbSnicm 
820311827fbSnicm /* Input data from client. */
821311827fbSnicm void
822311827fbSnicm server_handle_client(struct client *c)
823311827fbSnicm {
824e086383eSnicm 	struct window		*w;
825311827fbSnicm 	struct window_pane	*wp;
826311827fbSnicm 	struct screen		*s;
827311827fbSnicm 	struct timeval	 	 tv;
828311827fbSnicm 	struct key_binding	*bd;
829dfab4feaSnicm 	struct keylist		*keylist;
830dfab4feaSnicm 	int		 	 key, status, xtimeout, mode, isprefix;
831dfab4feaSnicm 	u_int			 i;
832311827fbSnicm 	u_char			 mouse[3];
833311827fbSnicm 
834311827fbSnicm 	xtimeout = options_get_number(&c->session->options, "repeat-time");
835311827fbSnicm 	if (xtimeout != 0 && c->flags & CLIENT_REPEAT) {
836311827fbSnicm 		if (gettimeofday(&tv, NULL) != 0)
83785d997dcSnicm 			fatal("gettimeofday failed");
838311827fbSnicm 		if (timercmp(&tv, &c->repeat_timer, >))
839311827fbSnicm 			c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
840311827fbSnicm 	}
841311827fbSnicm 
842311827fbSnicm 	/* Process keys. */
843dfab4feaSnicm 	keylist = options_get_data(&c->session->options, "prefix");
844311827fbSnicm 	while (tty_keys_next(&c->tty, &key, mouse) == 0) {
845311827fbSnicm 		server_activity = time(NULL);
846311827fbSnicm 
847311827fbSnicm 		if (c->session == NULL)
848311827fbSnicm 			return;
849e086383eSnicm 		w = c->session->curw->window;
850e086383eSnicm 		wp = w->active;	/* could die */
851e086383eSnicm 
852e086383eSnicm 		/* Special case: number keys jump to pane in identify mode. */
853e086383eSnicm 		if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
854e086383eSnicm 			wp = window_pane_at_index(w, key - '0');
855e086383eSnicm 			if (wp != NULL && window_pane_visible(wp))
856e086383eSnicm 				window_set_active_pane(w, wp);
857e086383eSnicm 			server_clear_identify(c);
858e086383eSnicm 			continue;
859e086383eSnicm 		}
860311827fbSnicm 
861311827fbSnicm 		status_message_clear(c);
862669c539cSnicm 		server_clear_identify(c);
863311827fbSnicm 		if (c->prompt_string != NULL) {
864311827fbSnicm 			status_prompt_key(c, key);
865311827fbSnicm 			continue;
866311827fbSnicm 		}
867311827fbSnicm 
868311827fbSnicm 		/* Check for mouse keys. */
869311827fbSnicm 		if (key == KEYC_MOUSE) {
870311827fbSnicm 			window_pane_mouse(wp, c, mouse[0], mouse[1], mouse[2]);
871311827fbSnicm 			continue;
872311827fbSnicm 		}
873311827fbSnicm 
874dfab4feaSnicm 		/* Is this a prefix key? */
875dfab4feaSnicm 		isprefix = 0;
876dfab4feaSnicm 		for (i = 0; i < ARRAY_LENGTH(keylist); i++) {
877dfab4feaSnicm 			if (key == ARRAY_ITEM(keylist, i)) {
878dfab4feaSnicm 				isprefix = 1;
879dfab4feaSnicm 				break;
880dfab4feaSnicm 			}
881dfab4feaSnicm 		}
882dfab4feaSnicm 
883311827fbSnicm 		/* No previous prefix key. */
884311827fbSnicm 		if (!(c->flags & CLIENT_PREFIX)) {
885dfab4feaSnicm 			if (isprefix)
886311827fbSnicm 				c->flags |= CLIENT_PREFIX;
887ad4696b5Snicm 			else {
888ad4696b5Snicm 				/* Try as a non-prefix key binding. */
889ad4696b5Snicm 				if ((bd = key_bindings_lookup(key)) == NULL)
890311827fbSnicm 					window_pane_key(wp, c, key);
891ad4696b5Snicm 				else
892ad4696b5Snicm 					key_bindings_dispatch(bd, c);
893ad4696b5Snicm 			}
894311827fbSnicm 			continue;
895311827fbSnicm 		}
896311827fbSnicm 
897311827fbSnicm 		/* Prefix key already pressed. Reset prefix and lookup key. */
898311827fbSnicm 		c->flags &= ~CLIENT_PREFIX;
899ad4696b5Snicm 		if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) {
900311827fbSnicm 			/* If repeating, treat this as a key, else ignore. */
901311827fbSnicm 			if (c->flags & CLIENT_REPEAT) {
902311827fbSnicm 				c->flags &= ~CLIENT_REPEAT;
903dfab4feaSnicm 				if (isprefix)
904311827fbSnicm 					c->flags |= CLIENT_PREFIX;
905311827fbSnicm 				else
906311827fbSnicm 					window_pane_key(wp, c, key);
907311827fbSnicm 			}
908311827fbSnicm 			continue;
909311827fbSnicm 		}
910311827fbSnicm 
911311827fbSnicm 		/* If already repeating, but this key can't repeat, skip it. */
912311827fbSnicm 		if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
913311827fbSnicm 			c->flags &= ~CLIENT_REPEAT;
914dfab4feaSnicm 			if (isprefix)
915311827fbSnicm 				c->flags |= CLIENT_PREFIX;
916311827fbSnicm 			else
917311827fbSnicm 				window_pane_key(wp, c, key);
918311827fbSnicm 			continue;
919311827fbSnicm 		}
920311827fbSnicm 
921311827fbSnicm 		/* If this key can repeat, reset the repeat flags and timer. */
922311827fbSnicm 		if (xtimeout != 0 && bd->can_repeat) {
923311827fbSnicm 			c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
924311827fbSnicm 
925311827fbSnicm 			tv.tv_sec = xtimeout / 1000;
926311827fbSnicm 			tv.tv_usec = (xtimeout % 1000) * 1000L;
927311827fbSnicm 			if (gettimeofday(&c->repeat_timer, NULL) != 0)
92885d997dcSnicm 				fatal("gettimeofday failed");
929311827fbSnicm 			timeradd(&c->repeat_timer, &tv, &c->repeat_timer);
930311827fbSnicm 		}
931311827fbSnicm 
932311827fbSnicm 		/* Dispatch the command. */
933311827fbSnicm 		key_bindings_dispatch(bd, c);
934311827fbSnicm 	}
935311827fbSnicm 	if (c->session == NULL)
936311827fbSnicm 		return;
937311827fbSnicm 	wp = c->session->curw->window->active;	/* could die - do each loop */
938311827fbSnicm 	s = wp->screen;
939311827fbSnicm 
94045400b51Snicm 	/*
94145400b51Snicm 	 * Update cursor position and mode settings. The scroll region and
94245400b51Snicm 	 * attributes are cleared across poll(2) as this is the most likely
94345400b51Snicm 	 * time a user may interrupt tmux, for example with ~^Z in ssh(1). This
94445400b51Snicm 	 * is a compromise between excessive resets and likelihood of an
94545400b51Snicm 	 * interrupt.
94645400b51Snicm 	 *
94745400b51Snicm 	 * tty_region/tty_reset/tty_update_mode already take care of not
94845400b51Snicm 	 * resetting things that are already in their default state.
94945400b51Snicm 	 */
950311827fbSnicm 	status = options_get_number(&c->session->options, "status");
951dde47eafSnicm 	tty_region(&c->tty, 0, c->tty.sy - 1, 0);
952af9e4c5dSnicm 	if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
953af9e4c5dSnicm 		tty_cursor(&c->tty, 0, 0, 0, 0);
954af9e4c5dSnicm 	else
955311827fbSnicm 		tty_cursor(&c->tty, s->cx, s->cy, wp->xoff, wp->yoff);
956311827fbSnicm 
957311827fbSnicm 	mode = s->mode;
958311827fbSnicm 	tty_update_mode(&c->tty, mode);
95945400b51Snicm 	tty_reset(&c->tty);
960311827fbSnicm }
961311827fbSnicm 
962311827fbSnicm /* Lost a client. */
963311827fbSnicm void
964311827fbSnicm server_lost_client(struct client *c)
965311827fbSnicm {
966311827fbSnicm 	u_int	i;
967311827fbSnicm 
968311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
969311827fbSnicm 		if (ARRAY_ITEM(&clients, i) == c)
970311827fbSnicm 			ARRAY_SET(&clients, i, NULL);
971311827fbSnicm 	}
9720bb39cd5Snicm 	log_debug("lost client %d", c->ibuf.fd);
973311827fbSnicm 
974f369cd7fSnicm 	/*
975f369cd7fSnicm 	 * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
976f369cd7fSnicm 	 * and tty_free might close an unrelated fd.
977f369cd7fSnicm 	 */
978f369cd7fSnicm 	if (c->flags & CLIENT_TERMINAL)
979130af69dSnicm 		tty_free(&c->tty);
980311827fbSnicm 
981311827fbSnicm 	screen_free(&c->status);
982311827fbSnicm 
983311827fbSnicm 	if (c->title != NULL)
984311827fbSnicm 		xfree(c->title);
985311827fbSnicm 
986311827fbSnicm 	if (c->message_string != NULL)
987311827fbSnicm 		xfree(c->message_string);
988311827fbSnicm 
989311827fbSnicm 	if (c->prompt_string != NULL)
990311827fbSnicm 		xfree(c->prompt_string);
991311827fbSnicm 	if (c->prompt_buffer != NULL)
992311827fbSnicm 		xfree(c->prompt_buffer);
993311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++)
994311827fbSnicm 		xfree(ARRAY_ITEM(&c->prompt_hdata, i));
995311827fbSnicm 	ARRAY_FREE(&c->prompt_hdata);
996311827fbSnicm 
997311827fbSnicm 	if (c->cwd != NULL)
998311827fbSnicm 		xfree(c->cwd);
999311827fbSnicm 
1000fd234c13Snicm 	close(c->ibuf.fd);
1001fd234c13Snicm 	imsg_clear(&c->ibuf);
10027255ff90Snicm 
10037255ff90Snicm 	for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
10047255ff90Snicm 		if (ARRAY_ITEM(&dead_clients, i) == NULL) {
10057255ff90Snicm 			ARRAY_SET(&dead_clients, i, c);
10067255ff90Snicm 			break;
10077255ff90Snicm 		}
10087255ff90Snicm 	}
10097255ff90Snicm 	if (i == ARRAY_LENGTH(&dead_clients))
10107255ff90Snicm 		ARRAY_ADD(&dead_clients, c);
10117255ff90Snicm 	c->flags |= CLIENT_DEAD;
1012311827fbSnicm 
1013311827fbSnicm 	recalculate_sizes();
1014311827fbSnicm }
1015311827fbSnicm 
10167255ff90Snicm /* Free dead, unreferenced clients and sessions. */
10177255ff90Snicm void
10187255ff90Snicm server_clean_dead(void)
10197255ff90Snicm {
10207255ff90Snicm 	struct session	*s;
10217255ff90Snicm 	struct client	*c;
10227255ff90Snicm 	u_int		 i;
10237255ff90Snicm 
10247255ff90Snicm 	for (i = 0; i < ARRAY_LENGTH(&dead_sessions); i++) {
10257255ff90Snicm 		s = ARRAY_ITEM(&dead_sessions, i);
10267255ff90Snicm 		if (s == NULL || s->references != 0)
10277255ff90Snicm 			continue;
10287255ff90Snicm 		ARRAY_SET(&dead_sessions, i, NULL);
10297255ff90Snicm 		xfree(s);
10307255ff90Snicm 	}
10317255ff90Snicm 
10327255ff90Snicm 	for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
10337255ff90Snicm 		c = ARRAY_ITEM(&dead_clients, i);
10347255ff90Snicm 		if (c == NULL || c->references != 0)
10357255ff90Snicm 			continue;
10367255ff90Snicm 		ARRAY_SET(&dead_clients, i, NULL);
10377255ff90Snicm 		xfree(c);
10387255ff90Snicm 	}
10397255ff90Snicm }
10407255ff90Snicm 
1041311827fbSnicm /* Handle window data. */
1042311827fbSnicm void
1043311827fbSnicm server_handle_window(struct window *w, struct window_pane *wp)
1044311827fbSnicm {
1045311827fbSnicm 	struct session	*s;
1046311827fbSnicm 	u_int		 i;
1047311827fbSnicm 	int		 update;
1048311827fbSnicm 
1049311827fbSnicm 	window_pane_parse(wp);
1050311827fbSnicm 
1051311827fbSnicm 	if ((w->flags & (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT)) == 0)
1052311827fbSnicm 		return;
1053311827fbSnicm 
1054311827fbSnicm 	update = 0;
1055311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1056311827fbSnicm 		s = ARRAY_ITEM(&sessions, i);
1057311827fbSnicm 		if (s == NULL || !session_has(s, w))
1058311827fbSnicm 			continue;
1059311827fbSnicm 
10600903c7b9Snicm 		update += server_check_window_bell(s, w);
1061311827fbSnicm 		update += server_check_window_activity(s, w);
1062311827fbSnicm 		update += server_check_window_content(s, w, wp);
1063311827fbSnicm 	}
1064311827fbSnicm 	if (update)
1065311827fbSnicm 		server_status_window(w);
1066311827fbSnicm 
1067311827fbSnicm 	w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT);
1068311827fbSnicm }
1069311827fbSnicm 
1070311827fbSnicm int
10710903c7b9Snicm server_check_window_bell(struct session *s, struct window *w)
1072311827fbSnicm {
1073311827fbSnicm 	struct client	*c;
1074311827fbSnicm 	u_int		 i;
10750903c7b9Snicm 	int		 action, visual;
1076311827fbSnicm 
1077311827fbSnicm 	if (!(w->flags & WINDOW_BELL))
1078311827fbSnicm 		return (0);
1079311827fbSnicm 	if (session_alert_has_window(s, w, WINDOW_BELL))
1080311827fbSnicm 		return (0);
1081311827fbSnicm 	session_alert_add(s, w, WINDOW_BELL);
1082311827fbSnicm 
1083311827fbSnicm 	action = options_get_number(&s->options, "bell-action");
1084311827fbSnicm 	switch (action) {
1085311827fbSnicm 	case BELL_ANY:
1086311827fbSnicm 		if (s->flags & SESSION_UNATTACHED)
1087311827fbSnicm 			break;
10880903c7b9Snicm 		visual = options_get_number(&s->options, "visual-bell");
1089311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1090311827fbSnicm 			c = ARRAY_ITEM(&clients, i);
10910903c7b9Snicm 			if (c == NULL || c->session != s)
10920903c7b9Snicm 				continue;
10930903c7b9Snicm 			if (!visual) {
1094311827fbSnicm 				tty_putcode(&c->tty, TTYC_BEL);
10950903c7b9Snicm 				continue;
10960903c7b9Snicm 			}
10970903c7b9Snicm  			if (c->session->curw->window == w) {
10980903c7b9Snicm 				status_message_set(c, "Bell in current window");
10990903c7b9Snicm 				continue;
11000903c7b9Snicm 			}
11010903c7b9Snicm 			status_message_set(c, "Bell in window %u",
11020903c7b9Snicm 			    winlink_find_by_window(&s->windows, w)->idx);
1103311827fbSnicm 		}
1104311827fbSnicm 		break;
1105311827fbSnicm 	case BELL_CURRENT:
11060903c7b9Snicm 		if (s->flags & SESSION_UNATTACHED)
1107311827fbSnicm 			break;
11080903c7b9Snicm 		visual = options_get_number(&s->options, "visual-bell");
1109311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1110311827fbSnicm 			c = ARRAY_ITEM(&clients, i);
11110903c7b9Snicm 			if (c == NULL || c->session != s)
11120903c7b9Snicm 				continue;
11130903c7b9Snicm  			if (c->session->curw->window != w)
11140903c7b9Snicm 				continue;
11150903c7b9Snicm 			if (!visual) {
1116311827fbSnicm 				tty_putcode(&c->tty, TTYC_BEL);
11170903c7b9Snicm 				continue;
11180903c7b9Snicm 			}
11190903c7b9Snicm 			status_message_set(c, "Bell in current window");
1120311827fbSnicm 		}
1121311827fbSnicm 		break;
1122311827fbSnicm 	}
1123311827fbSnicm 	return (1);
1124311827fbSnicm }
1125311827fbSnicm 
1126311827fbSnicm int
1127311827fbSnicm server_check_window_activity(struct session *s, struct window *w)
1128311827fbSnicm {
11290903c7b9Snicm 	struct client	*c;
11300903c7b9Snicm 	u_int		 i;
11310903c7b9Snicm 
1132311827fbSnicm 	if (!(w->flags & WINDOW_ACTIVITY))
1133311827fbSnicm 		return (0);
11340903c7b9Snicm 
1135311827fbSnicm 	if (!options_get_number(&w->options, "monitor-activity"))
1136311827fbSnicm 		return (0);
11370903c7b9Snicm 
1138311827fbSnicm 	if (session_alert_has_window(s, w, WINDOW_ACTIVITY))
1139311827fbSnicm 		return (0);
11400903c7b9Snicm 	if (s->curw->window == w)
11410903c7b9Snicm 		return (0);
11420903c7b9Snicm 
1143311827fbSnicm 	session_alert_add(s, w, WINDOW_ACTIVITY);
11440903c7b9Snicm 	if (s->flags & SESSION_UNATTACHED)
11450903c7b9Snicm 		return (0);
11460903c7b9Snicm  	if (options_get_number(&s->options, "visual-activity")) {
11470903c7b9Snicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
11480903c7b9Snicm 			c = ARRAY_ITEM(&clients, i);
11490903c7b9Snicm 			if (c == NULL || c->session != s)
11500903c7b9Snicm 				continue;
11510903c7b9Snicm 			status_message_set(c, "Activity in window %u",
11520903c7b9Snicm 			    winlink_find_by_window(&s->windows, w)->idx);
11530903c7b9Snicm 		}
11540903c7b9Snicm 	}
11550903c7b9Snicm 
1156311827fbSnicm 	return (1);
1157311827fbSnicm }
1158311827fbSnicm 
1159311827fbSnicm int
1160311827fbSnicm server_check_window_content(
1161311827fbSnicm     struct session *s, struct window *w, struct window_pane *wp)
1162311827fbSnicm {
11630903c7b9Snicm 	struct client	*c;
11640903c7b9Snicm 	u_int		 i;
1165311827fbSnicm 	char		*found, *ptr;
1166311827fbSnicm 
11670903c7b9Snicm 	if (!(w->flags & WINDOW_ACTIVITY))	/* activity for new content */
1168311827fbSnicm 		return (0);
11690903c7b9Snicm 
11700903c7b9Snicm 	ptr = options_get_string(&w->options, "monitor-content");
11710903c7b9Snicm 	if (ptr == NULL || *ptr == '\0')
1172311827fbSnicm 		return (0);
11730903c7b9Snicm 
1174311827fbSnicm 	if (session_alert_has_window(s, w, WINDOW_CONTENT))
1175311827fbSnicm 		return (0);
11760903c7b9Snicm 	if (s->curw->window == w)
11770903c7b9Snicm 		return (0);
11780903c7b9Snicm 
1179570a3589Snicm 	if ((found = window_pane_search(wp, ptr, NULL)) == NULL)
1180311827fbSnicm 		return (0);
1181311827fbSnicm     	xfree(found);
11820903c7b9Snicm 
11830903c7b9Snicm 	session_alert_add(s, w, WINDOW_CONTENT);
11840903c7b9Snicm 	if (s->flags & SESSION_UNATTACHED)
11850903c7b9Snicm 		return (0);
11860903c7b9Snicm  	if (options_get_number(&s->options, "visual-content")) {
11870903c7b9Snicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
11880903c7b9Snicm 			c = ARRAY_ITEM(&clients, i);
11890903c7b9Snicm 			if (c == NULL || c->session != s)
11900903c7b9Snicm 				continue;
11910903c7b9Snicm 			status_message_set(c, "Content in window %u",
11920903c7b9Snicm 			    winlink_find_by_window(&s->windows, w)->idx);
11930903c7b9Snicm 		}
11940903c7b9Snicm 	}
11950903c7b9Snicm 
1196311827fbSnicm 	return (1);
1197311827fbSnicm }
1198311827fbSnicm 
1199c752cdacSnicm /* Check if window still exists. */
1200311827fbSnicm void
1201311827fbSnicm server_check_window(struct window *w)
1202311827fbSnicm {
1203311827fbSnicm 	struct window_pane	*wp, *wq;
1204e0b5b8e5Snicm 	struct options		*oo = &w->options;
1205311827fbSnicm 	struct session		*s;
1206311827fbSnicm 	struct winlink		*wl;
1207fafcfb1dSnicm 	u_int		 	 i;
1208e0b5b8e5Snicm 	int		 	 destroyed;
1209311827fbSnicm 
1210311827fbSnicm 	destroyed = 1;
1211311827fbSnicm 
1212311827fbSnicm 	wp = TAILQ_FIRST(&w->panes);
1213311827fbSnicm 	while (wp != NULL) {
1214311827fbSnicm 		wq = TAILQ_NEXT(wp, entry);
1215c752cdacSnicm 		/*
1216c752cdacSnicm 		 * If the pane has died and the remain-on-exit flag is not set,
1217c752cdacSnicm 		 * remove the pane; otherwise, if the flag is set, don't allow
1218c752cdacSnicm 		 * the window to be destroyed (or it'll close when the last
1219c752cdacSnicm 		 * pane dies).
1220c752cdacSnicm 		 */
12212be3d294Snicm 		if (wp->fd == -1 && !options_get_number(oo, "remain-on-exit")) {
1222af9e4c5dSnicm 			layout_close_pane(wp);
1223311827fbSnicm 			window_remove_pane(w, wp);
1224311827fbSnicm 			server_redraw_window(w);
1225c752cdacSnicm 		} else
1226c752cdacSnicm 			destroyed = 0;
1227311827fbSnicm 		wp = wq;
1228311827fbSnicm 	}
1229311827fbSnicm 
1230311827fbSnicm 	if (!destroyed)
1231311827fbSnicm 		return;
1232311827fbSnicm 
1233311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1234311827fbSnicm 		s = ARRAY_ITEM(&sessions, i);
1235311827fbSnicm 		if (s == NULL)
1236311827fbSnicm 			continue;
1237311827fbSnicm 		if (!session_has(s, w))
1238311827fbSnicm 			continue;
1239311827fbSnicm 
1240311827fbSnicm 	restart:
1241311827fbSnicm 		/* Detach window and either redraw or kill clients. */
1242311827fbSnicm 		RB_FOREACH(wl, winlinks, &s->windows) {
1243311827fbSnicm 			if (wl->window != w)
1244311827fbSnicm 				continue;
12452d9a0fc7Snicm 			if (session_detach(s, wl)) {
12462d9a0fc7Snicm 				server_destroy_session(s);
1247311827fbSnicm 				break;
12482d9a0fc7Snicm 			}
12492d9a0fc7Snicm 			server_redraw_session(s);
1250311827fbSnicm 			goto restart;
1251311827fbSnicm 		}
1252311827fbSnicm 	}
1253311827fbSnicm 
1254311827fbSnicm 	recalculate_sizes();
1255311827fbSnicm }
1256311827fbSnicm 
1257311827fbSnicm /* Call any once-per-second timers. */
1258311827fbSnicm void
1259311827fbSnicm server_second_timers(void)
1260311827fbSnicm {
1261311827fbSnicm 	struct window		*w;
1262311827fbSnicm 	struct window_pane	*wp;
1263311827fbSnicm 	u_int		 	 i;
1264311827fbSnicm 	int			 xtimeout;
1265311827fbSnicm 	time_t		 	 t;
1266311827fbSnicm 
1267311827fbSnicm 	t = time(NULL);
12681b552934Snicm 
1269eaecedb2Snicm 	xtimeout = options_get_number(&global_s_options, "lock-after-time");
1270fd523813Snicm 	if (xtimeout > 0 && t > server_activity + xtimeout) {
1271311827fbSnicm 		server_lock();
1272fd523813Snicm 		recalculate_sizes();
1273fd523813Snicm 	}
1274311827fbSnicm 
1275311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
1276311827fbSnicm 		w = ARRAY_ITEM(&windows, i);
1277311827fbSnicm 		if (w == NULL)
1278311827fbSnicm 			continue;
1279311827fbSnicm 
1280311827fbSnicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
1281311827fbSnicm 			if (wp->mode != NULL && wp->mode->timer != NULL)
1282311827fbSnicm 				wp->mode->timer(wp);
1283311827fbSnicm 		}
1284311827fbSnicm 	}
1285311827fbSnicm }
1286311827fbSnicm 
1287311827fbSnicm /* Update socket execute permissions based on whether sessions are attached. */
1288311827fbSnicm int
1289311827fbSnicm server_update_socket(void)
1290311827fbSnicm {
1291311827fbSnicm 	struct session	*s;
1292311827fbSnicm 	u_int		 i;
1293311827fbSnicm 	static int	 last = -1;
1294311827fbSnicm 	int		 n;
1295311827fbSnicm 
1296311827fbSnicm 	n = 0;
1297311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1298311827fbSnicm 		s = ARRAY_ITEM(&sessions, i);
1299311827fbSnicm 		if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
1300311827fbSnicm 			n++;
1301311827fbSnicm 			break;
1302311827fbSnicm 		}
1303311827fbSnicm 	}
1304311827fbSnicm 
1305311827fbSnicm 	if (n != last) {
1306311827fbSnicm 		last = n;
1307311827fbSnicm 		if (n != 0)
1308311827fbSnicm 			chmod(socket_path, S_IRWXU);
1309311827fbSnicm 		else
1310311827fbSnicm 			chmod(socket_path, S_IRUSR|S_IWUSR);
1311311827fbSnicm 	}
1312311827fbSnicm 
1313311827fbSnicm 	return (n);
1314311827fbSnicm }
1315