xref: /openbsd/usr.bin/tmux/server.c (revision f369cd7f)
1*f369cd7fSnicm /* $OpenBSD: server.c,v 1.25 2009/08/31 11:37:27 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;
46311827fbSnicm 
47980b7663Snicm void		 server_create_client(int);
48311827fbSnicm int		 server_create_socket(void);
49311827fbSnicm int		 server_main(int);
50311827fbSnicm void		 server_shutdown(void);
51311827fbSnicm void		 server_child_signal(void);
52311827fbSnicm void		 server_fill_windows(struct pollfd **);
53311827fbSnicm void		 server_handle_windows(struct pollfd **);
54311827fbSnicm void		 server_fill_clients(struct pollfd **);
55311827fbSnicm void		 server_handle_clients(struct pollfd **);
56980b7663Snicm void		 server_accept_client(int);
57311827fbSnicm void		 server_handle_client(struct client *);
58311827fbSnicm void		 server_handle_window(struct window *, struct window_pane *);
590903c7b9Snicm int		 server_check_window_bell(struct session *, struct window *);
60311827fbSnicm int		 server_check_window_activity(struct session *,
61311827fbSnicm 		      struct window *);
62311827fbSnicm int		 server_check_window_content(struct session *, struct window *,
63311827fbSnicm 		      struct window_pane *);
64311827fbSnicm void		 server_lost_client(struct client *);
65311827fbSnicm void	 	 server_check_window(struct window *);
66311827fbSnicm void		 server_check_redraw(struct client *);
67311827fbSnicm void		 server_redraw_locked(struct client *);
68311827fbSnicm void		 server_check_timers(struct client *);
69311827fbSnicm void		 server_second_timers(void);
70311827fbSnicm int		 server_update_socket(void);
71311827fbSnicm 
72311827fbSnicm /* Create a new client. */
73980b7663Snicm void
74311827fbSnicm server_create_client(int fd)
75311827fbSnicm {
76311827fbSnicm 	struct client	*c;
77311827fbSnicm 	int		 mode;
78311827fbSnicm 	u_int		 i;
79311827fbSnicm 
80311827fbSnicm 	if ((mode = fcntl(fd, F_GETFL)) == -1)
81311827fbSnicm 		fatal("fcntl failed");
82311827fbSnicm 	if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
83311827fbSnicm 		fatal("fcntl failed");
84311827fbSnicm 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
85311827fbSnicm 		fatal("fcntl failed");
86311827fbSnicm 
87311827fbSnicm 	c = xcalloc(1, sizeof *c);
88fd234c13Snicm 	imsg_init(&c->ibuf, fd);
89311827fbSnicm 
90311827fbSnicm 	ARRAY_INIT(&c->prompt_hdata);
91311827fbSnicm 
92311827fbSnicm 	c->tty.fd = -1;
93311827fbSnicm 	c->title = NULL;
94311827fbSnicm 
95311827fbSnicm 	c->session = NULL;
96311827fbSnicm 	c->tty.sx = 80;
97311827fbSnicm 	c->tty.sy = 25;
98311827fbSnicm 	screen_init(&c->status, c->tty.sx, 1, 0);
99311827fbSnicm 
100311827fbSnicm 	c->message_string = NULL;
101311827fbSnicm 
102311827fbSnicm 	c->prompt_string = NULL;
103311827fbSnicm 	c->prompt_buffer = NULL;
104311827fbSnicm 	c->prompt_index = 0;
105311827fbSnicm 
106311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
107311827fbSnicm 		if (ARRAY_ITEM(&clients, i) == NULL) {
108311827fbSnicm 			ARRAY_SET(&clients, i, c);
109980b7663Snicm 			return;
110311827fbSnicm 		}
111311827fbSnicm 	}
112311827fbSnicm 	ARRAY_ADD(&clients, c);
1130bb39cd5Snicm 	log_debug("new client %d", fd);
114311827fbSnicm }
115311827fbSnicm 
116311827fbSnicm /* Find client index. */
117311827fbSnicm int
118311827fbSnicm server_client_index(struct client *c)
119311827fbSnicm {
120311827fbSnicm 	u_int	i;
121311827fbSnicm 
122311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
123311827fbSnicm 		if (c == ARRAY_ITEM(&clients, i))
124311827fbSnicm 			return (i);
125311827fbSnicm 	}
126311827fbSnicm 	return (-1);
127311827fbSnicm }
128311827fbSnicm 
129311827fbSnicm /* Fork new server. */
130311827fbSnicm int
131311827fbSnicm server_start(char *path)
132311827fbSnicm {
1330cd9638eSnicm 	struct client	*c;
1340cd9638eSnicm 	int		 pair[2], srv_fd;
135311827fbSnicm 	char		*cause;
136311827fbSnicm 	char		 rpathbuf[MAXPATHLEN];
137311827fbSnicm 
138311827fbSnicm 	/* The first client is special and gets a socketpair; create it. */
139311827fbSnicm 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
140311827fbSnicm 		fatal("socketpair failed");
141311827fbSnicm 
142311827fbSnicm 	switch (fork()) {
143311827fbSnicm 	case -1:
144311827fbSnicm 		fatal("fork failed");
145311827fbSnicm 	case 0:
146311827fbSnicm 		break;
147311827fbSnicm 	default:
148311827fbSnicm 		close(pair[1]);
149311827fbSnicm 		return (pair[0]);
150311827fbSnicm 	}
151311827fbSnicm 	close(pair[0]);
152311827fbSnicm 
153311827fbSnicm 	/*
154311827fbSnicm 	 * Must daemonise before loading configuration as the PID changes so
155311827fbSnicm 	 * $TMUX would be wrong for sessions created in the config file.
156311827fbSnicm 	 */
1570cd9638eSnicm 	if (daemon(1, 0) != 0)
158311827fbSnicm 		fatal("daemon failed");
159311827fbSnicm 
1600cd9638eSnicm 	logfile("server");
1610cd9638eSnicm 	log_debug("server started, pid %ld", (long) getpid());
1620cd9638eSnicm 
163311827fbSnicm 	ARRAY_INIT(&windows);
164311827fbSnicm 	ARRAY_INIT(&clients);
165311827fbSnicm 	ARRAY_INIT(&sessions);
16668895571Snicm 	mode_key_init_trees();
167311827fbSnicm 	key_bindings_init();
168311827fbSnicm 	utf8_build();
169311827fbSnicm 
170311827fbSnicm 	server_locked = 0;
171311827fbSnicm 	server_password = NULL;
172311827fbSnicm 	server_activity = time(NULL);
173311827fbSnicm 
174311827fbSnicm 	start_time = time(NULL);
175311827fbSnicm 	socket_path = path;
176311827fbSnicm 
177311827fbSnicm 	if (realpath(socket_path, rpathbuf) == NULL)
178311827fbSnicm 		strlcpy(rpathbuf, socket_path, sizeof rpathbuf);
1790cd9638eSnicm 	log_debug("socket path %s", socket_path);
180311827fbSnicm 	setproctitle("server (%s)", rpathbuf);
181311827fbSnicm 
182311827fbSnicm 	srv_fd = server_create_socket();
183311827fbSnicm 	server_create_client(pair[1]);
184311827fbSnicm 
1850cd9638eSnicm 	if (access(SYSTEM_CFG, R_OK) != 0) {
1860cd9638eSnicm 		if (errno != ENOENT) {
1870cd9638eSnicm 			xasprintf(
1880cd9638eSnicm 			    &cause, "%s: %s", strerror(errno), SYSTEM_CFG);
1890cd9638eSnicm 			goto error;
1900cd9638eSnicm 		}
191c0a52a07Snicm 	} else if (load_cfg(SYSTEM_CFG, NULL, &cause) != 0)
1920cd9638eSnicm 		goto error;
193c0a52a07Snicm 	if (cfg_file != NULL && load_cfg(cfg_file, NULL, &cause) != 0)
1940cd9638eSnicm 		goto error;
1950cd9638eSnicm 
1960cd9638eSnicm 	exit(server_main(srv_fd));
1970cd9638eSnicm 
1980cd9638eSnicm error:
1990cd9638eSnicm 	/* Write the error and shutdown the server. */
2000cd9638eSnicm 	c = ARRAY_FIRST(&clients);
2010cd9638eSnicm 
2020cd9638eSnicm 	server_write_error(c, cause);
2030cd9638eSnicm 	xfree(cause);
2040cd9638eSnicm 
2050cd9638eSnicm 	server_shutdown();
2060cd9638eSnicm 	c->flags |= CLIENT_BAD;
2070cd9638eSnicm 
208311827fbSnicm 	exit(server_main(srv_fd));
209311827fbSnicm }
210311827fbSnicm 
211311827fbSnicm /* Create server socket. */
212311827fbSnicm int
213311827fbSnicm server_create_socket(void)
214311827fbSnicm {
215311827fbSnicm 	struct sockaddr_un	sa;
216311827fbSnicm 	size_t			size;
217311827fbSnicm 	mode_t			mask;
218311827fbSnicm 	int			fd, mode;
219311827fbSnicm 
220311827fbSnicm 	memset(&sa, 0, sizeof sa);
221311827fbSnicm 	sa.sun_family = AF_UNIX;
222311827fbSnicm 	size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
223311827fbSnicm 	if (size >= sizeof sa.sun_path) {
224311827fbSnicm 		errno = ENAMETOOLONG;
225311827fbSnicm 		fatal("socket failed");
226311827fbSnicm 	}
227311827fbSnicm 	unlink(sa.sun_path);
228311827fbSnicm 
229311827fbSnicm 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
230311827fbSnicm 		fatal("socket failed");
231311827fbSnicm 
232311827fbSnicm 	mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
233311827fbSnicm 	if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1)
234311827fbSnicm 		fatal("bind failed");
235311827fbSnicm 	umask(mask);
236311827fbSnicm 
237311827fbSnicm 	if (listen(fd, 16) == -1)
238311827fbSnicm 		fatal("listen failed");
239311827fbSnicm 
240311827fbSnicm 	if ((mode = fcntl(fd, F_GETFL)) == -1)
241311827fbSnicm 		fatal("fcntl failed");
242311827fbSnicm 	if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
243311827fbSnicm 		fatal("fcntl failed");
244311827fbSnicm 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
245311827fbSnicm 		fatal("fcntl failed");
246311827fbSnicm 
247311827fbSnicm 	return (fd);
248311827fbSnicm }
249311827fbSnicm 
250311827fbSnicm /* Main server loop. */
251311827fbSnicm int
252311827fbSnicm server_main(int srv_fd)
253311827fbSnicm {
254311827fbSnicm 	struct window	*w;
255311827fbSnicm 	struct pollfd	*pfds, *pfd;
256311827fbSnicm 	int		 nfds, xtimeout;
257311827fbSnicm 	u_int		 i, n;
258311827fbSnicm 	time_t		 now, last;
259311827fbSnicm 
260311827fbSnicm 	siginit();
2610bb39cd5Snicm 	log_debug("server socket is %d", srv_fd);
262311827fbSnicm 
263311827fbSnicm 	last = time(NULL);
264311827fbSnicm 
265311827fbSnicm 	pfds = NULL;
266311827fbSnicm 	for (;;) {
267311827fbSnicm 		/* If sigterm, kill all windows and clients. */
268311827fbSnicm 		if (sigterm)
269311827fbSnicm 			server_shutdown();
270311827fbSnicm 
271311827fbSnicm 		/* Handle child exit. */
272311827fbSnicm 		if (sigchld) {
273311827fbSnicm 			server_child_signal();
274311827fbSnicm 			sigchld = 0;
275311827fbSnicm 		}
276311827fbSnicm 
277311827fbSnicm 		/* Recreate socket on SIGUSR1. */
278311827fbSnicm 		if (sigusr1) {
279311827fbSnicm 			close(srv_fd);
280311827fbSnicm 			srv_fd = server_create_socket();
281311827fbSnicm 			sigusr1 = 0;
282311827fbSnicm 		}
283311827fbSnicm 
284311827fbSnicm 		/* Initialise pollfd array. */
285311827fbSnicm 		nfds = 1;
286311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
287311827fbSnicm 			w = ARRAY_ITEM(&windows, i);
288311827fbSnicm 			if (w != NULL)
289311827fbSnicm 				nfds += window_count_panes(w);
290311827fbSnicm 		}
291311827fbSnicm 		nfds += ARRAY_LENGTH(&clients) * 2;
292311827fbSnicm 		pfds = xrealloc(pfds, nfds, sizeof *pfds);
293311827fbSnicm 		memset(pfds, 0, nfds * sizeof *pfds);
294311827fbSnicm 		pfd = pfds;
295311827fbSnicm 
296311827fbSnicm 		/* Fill server socket. */
297311827fbSnicm 		pfd->fd = srv_fd;
298311827fbSnicm 		pfd->events = POLLIN;
299311827fbSnicm 		pfd++;
300311827fbSnicm 
301311827fbSnicm 		/* Fill window and client sockets. */
302311827fbSnicm 		server_fill_windows(&pfd);
303311827fbSnicm 		server_fill_clients(&pfd);
304311827fbSnicm 
305311827fbSnicm 		/* Update socket permissions. */
306311827fbSnicm 		xtimeout = INFTIM;
307311827fbSnicm 		if (sigterm || server_update_socket() != 0)
308311827fbSnicm 			xtimeout = POLL_TIMEOUT;
309311827fbSnicm 
310311827fbSnicm 		/* Do the poll. */
3119b1fc963Snicm 		if (poll(pfds, nfds, xtimeout) == -1) {
312311827fbSnicm 			if (errno == EAGAIN || errno == EINTR)
313311827fbSnicm 				continue;
314311827fbSnicm 			fatal("poll failed");
315311827fbSnicm 		}
316311827fbSnicm 		pfd = pfds;
317311827fbSnicm 
318311827fbSnicm 		/* Handle server socket. */
319311827fbSnicm 		if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP))
320311827fbSnicm 			fatalx("lost server socket");
321311827fbSnicm 		if (pfd->revents & POLLIN) {
322311827fbSnicm 			server_accept_client(srv_fd);
323311827fbSnicm 			continue;
324311827fbSnicm 		}
325311827fbSnicm 		pfd++;
326311827fbSnicm 
327311827fbSnicm 		/* Call second-based timers. */
328311827fbSnicm 		now = time(NULL);
329311827fbSnicm 		if (now != last) {
330311827fbSnicm 			last = now;
331311827fbSnicm 			server_second_timers();
332311827fbSnicm 		}
333311827fbSnicm 
334311827fbSnicm 		/* Set window names. */
335311827fbSnicm 		set_window_names();
336311827fbSnicm 
337311827fbSnicm 		/*
338311827fbSnicm 		 * Handle window and client sockets. Clients can create
339311827fbSnicm 		 * windows, so windows must come first to avoid messing up by
340311827fbSnicm 		 * increasing the array size.
341311827fbSnicm 		 */
342311827fbSnicm 		server_handle_windows(&pfd);
343311827fbSnicm 		server_handle_clients(&pfd);
344311827fbSnicm 
3450476b68eSnicm 		/* Collect any unset key bindings. */
3460476b68eSnicm 		key_bindings_clean();
3470476b68eSnicm 
348311827fbSnicm 		/*
349311827fbSnicm 		 * If we have no sessions and clients left, let's get out
350311827fbSnicm 		 * of here...
351311827fbSnicm 		 */
352311827fbSnicm 		n = 0;
353311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
354311827fbSnicm 			if (ARRAY_ITEM(&sessions, i) != NULL)
355311827fbSnicm 				n++;
356311827fbSnicm 		}
357311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
358311827fbSnicm 			if (ARRAY_ITEM(&clients, i) != NULL)
359311827fbSnicm 				n++;
360311827fbSnicm 		}
361311827fbSnicm 		if (n == 0)
362311827fbSnicm 			break;
363311827fbSnicm 	}
364311827fbSnicm 	if (pfds != NULL)
365311827fbSnicm 		xfree(pfds);
366311827fbSnicm 
367311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
368311827fbSnicm 		if (ARRAY_ITEM(&sessions, i) != NULL)
369311827fbSnicm 			session_destroy(ARRAY_ITEM(&sessions, i));
370311827fbSnicm 	}
371311827fbSnicm 	ARRAY_FREE(&sessions);
372311827fbSnicm 
373311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
374311827fbSnicm 		if (ARRAY_ITEM(&clients, i) != NULL)
375311827fbSnicm 			server_lost_client(ARRAY_ITEM(&clients, i));
376311827fbSnicm 	}
377311827fbSnicm 	ARRAY_FREE(&clients);
378311827fbSnicm 
37968895571Snicm 	mode_key_free_trees();
380311827fbSnicm 	key_bindings_free();
381311827fbSnicm 
382311827fbSnicm 	close(srv_fd);
383311827fbSnicm 
384311827fbSnicm 	unlink(socket_path);
385311827fbSnicm 	xfree(socket_path);
386311827fbSnicm 
387eaecedb2Snicm 	options_free(&global_s_options);
388eaecedb2Snicm 	options_free(&global_w_options);
389311827fbSnicm 	if (server_password != NULL)
390311827fbSnicm 		xfree(server_password);
391311827fbSnicm 
392311827fbSnicm 	return (0);
393311827fbSnicm }
394311827fbSnicm 
395311827fbSnicm /* Kill all clients. */
396311827fbSnicm void
397311827fbSnicm server_shutdown(void)
398311827fbSnicm {
399311827fbSnicm 	struct session	*s;
400311827fbSnicm 	struct client	*c;
401311827fbSnicm 	u_int		 i, j;
402311827fbSnicm 
403311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
404311827fbSnicm 		s = ARRAY_ITEM(&sessions, i);
405311827fbSnicm 		for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
406311827fbSnicm 			c = ARRAY_ITEM(&clients, j);
407311827fbSnicm 			if (c != NULL && c->session == s) {
408311827fbSnicm 				s = NULL;
409311827fbSnicm 				break;
410311827fbSnicm 			}
411311827fbSnicm 		}
412311827fbSnicm 		if (s != NULL)
413311827fbSnicm 			session_destroy(s);
414311827fbSnicm 	}
415311827fbSnicm 
416311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
417311827fbSnicm 		c = ARRAY_ITEM(&clients, i);
4180cd9638eSnicm 		if (c != NULL) {
4190cd9638eSnicm 			if (c->flags & CLIENT_BAD)
4200cd9638eSnicm 				server_lost_client(c);
4210cd9638eSnicm 			else
422311827fbSnicm 				server_write_client(c, MSG_SHUTDOWN, NULL, 0);
4230cd9638eSnicm 			c->flags |= CLIENT_BAD;
4240cd9638eSnicm 		}
425311827fbSnicm 	}
426311827fbSnicm }
427311827fbSnicm 
428311827fbSnicm /* Handle SIGCHLD. */
429311827fbSnicm void
430311827fbSnicm server_child_signal(void)
431311827fbSnicm {
432311827fbSnicm 	struct window		*w;
433311827fbSnicm 	struct window_pane	*wp;
434311827fbSnicm 	int		 	 status;
435311827fbSnicm 	pid_t		 	 pid;
436311827fbSnicm 	u_int		 	 i;
437311827fbSnicm 
438311827fbSnicm 	for (;;) {
439311827fbSnicm 		switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
440311827fbSnicm 		case -1:
441311827fbSnicm 			if (errno == ECHILD)
442311827fbSnicm 				return;
443311827fbSnicm 			fatal("waitpid");
444311827fbSnicm 		case 0:
445311827fbSnicm 			return;
446311827fbSnicm 		}
447311827fbSnicm 		if (!WIFSTOPPED(status))
448311827fbSnicm 			continue;
449311827fbSnicm 		if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
450311827fbSnicm 			continue;
451311827fbSnicm 
452311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
453311827fbSnicm 			w = ARRAY_ITEM(&windows, i);
454311827fbSnicm 			if (w == NULL)
455311827fbSnicm 				continue;
456311827fbSnicm 			TAILQ_FOREACH(wp, &w->panes, entry) {
457311827fbSnicm 				if (wp->pid == pid) {
458311827fbSnicm 					if (killpg(pid, SIGCONT) != 0)
459311827fbSnicm 						kill(pid, SIGCONT);
460311827fbSnicm 				}
461311827fbSnicm 			}
462311827fbSnicm 		}
463311827fbSnicm 	}
464311827fbSnicm }
465311827fbSnicm 
466311827fbSnicm /* Fill window pollfds. */
467311827fbSnicm void
468311827fbSnicm server_fill_windows(struct pollfd **pfd)
469311827fbSnicm {
470311827fbSnicm 	struct window		*w;
471311827fbSnicm 	struct window_pane	*wp;
472311827fbSnicm 	u_int		 	 i;
473311827fbSnicm 
474311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
475311827fbSnicm 		w = ARRAY_ITEM(&windows, i);
476311827fbSnicm 		if (w == NULL)
477311827fbSnicm 			continue;
478311827fbSnicm 
479311827fbSnicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
480311827fbSnicm 			(*pfd)->fd = wp->fd;
481311827fbSnicm 			if (wp->fd != -1) {
482311827fbSnicm 				(*pfd)->events = POLLIN;
483311827fbSnicm 				if (BUFFER_USED(wp->out) > 0)
484311827fbSnicm 					(*pfd)->events |= POLLOUT;
485311827fbSnicm 			}
486311827fbSnicm 			(*pfd)++;
487311827fbSnicm 		}
488311827fbSnicm 	}
489311827fbSnicm }
490311827fbSnicm 
491311827fbSnicm /* Handle window pollfds. */
492311827fbSnicm void
493311827fbSnicm server_handle_windows(struct pollfd **pfd)
494311827fbSnicm {
495311827fbSnicm 	struct window		*w;
496311827fbSnicm 	struct window_pane	*wp;
497311827fbSnicm 	u_int		 	 i;
498311827fbSnicm 
499311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
500311827fbSnicm 		w = ARRAY_ITEM(&windows, i);
501311827fbSnicm 		if (w == NULL)
502311827fbSnicm 			continue;
503311827fbSnicm 
504311827fbSnicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
505311827fbSnicm 			if (wp->fd != -1) {
506311827fbSnicm 				if (buffer_poll(*pfd, wp->in, wp->out) != 0) {
507311827fbSnicm 					close(wp->fd);
508311827fbSnicm 					wp->fd = -1;
509311827fbSnicm 				} else
510311827fbSnicm 					server_handle_window(w, wp);
511311827fbSnicm 			}
512311827fbSnicm 			(*pfd)++;
513311827fbSnicm 		}
514311827fbSnicm 
515311827fbSnicm 		server_check_window(w);
516311827fbSnicm 	}
517311827fbSnicm }
518311827fbSnicm 
519311827fbSnicm /* Check for general redraw on client. */
520311827fbSnicm void
521311827fbSnicm server_check_redraw(struct client *c)
522311827fbSnicm {
523311827fbSnicm 	struct session		*s;
524311827fbSnicm 	struct window_pane	*wp;
525311827fbSnicm 	char		 	 title[512];
526311827fbSnicm 	int		 	 flags, redraw;
527311827fbSnicm 
528311827fbSnicm 	if (c == NULL || c->session == NULL)
529311827fbSnicm 		return;
530311827fbSnicm 	s = c->session;
531311827fbSnicm 
532311827fbSnicm 	flags = c->tty.flags & TTY_FREEZE;
533311827fbSnicm 	c->tty.flags &= ~TTY_FREEZE;
534311827fbSnicm 
535311827fbSnicm 	if (options_get_number(&s->options, "set-titles")) {
536311827fbSnicm 		xsnprintf(title, sizeof title, "%s:%u:%s - \"%s\"",
537311827fbSnicm 		    s->name, s->curw->idx, s->curw->window->name,
538311827fbSnicm 		    s->curw->window->active->screen->title);
539311827fbSnicm 		if (c->title == NULL || strcmp(title, c->title) != 0) {
540311827fbSnicm 			if (c->title != NULL)
541311827fbSnicm 				xfree(c->title);
542311827fbSnicm 			c->title = xstrdup(title);
543311827fbSnicm 			tty_set_title(&c->tty, c->title);
544311827fbSnicm 		}
545311827fbSnicm 	}
546311827fbSnicm 
547311827fbSnicm 	if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
548311827fbSnicm 		if (c->message_string != NULL)
549311827fbSnicm 			redraw = status_message_redraw(c);
550311827fbSnicm 		else if (c->prompt_string != NULL)
551311827fbSnicm 			redraw = status_prompt_redraw(c);
552311827fbSnicm 		else
553311827fbSnicm 			redraw = status_redraw(c);
554311827fbSnicm 		if (!redraw)
555311827fbSnicm 			c->flags &= ~CLIENT_STATUS;
556311827fbSnicm 	}
557311827fbSnicm 
558311827fbSnicm 	if (c->flags & CLIENT_REDRAW) {
559311827fbSnicm 		if (server_locked)
560311827fbSnicm 			server_redraw_locked(c);
561311827fbSnicm 		else
56279fc95a8Snicm  			screen_redraw_screen(c, 0);
563311827fbSnicm 		c->flags &= ~CLIENT_STATUS;
564311827fbSnicm 	} else {
565311827fbSnicm 		TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
566311827fbSnicm 			if (wp->flags & PANE_REDRAW)
567311827fbSnicm 				screen_redraw_pane(c, wp);
568311827fbSnicm 		}
569311827fbSnicm 	}
570311827fbSnicm 
571311827fbSnicm 	if (c->flags & CLIENT_STATUS)
57279fc95a8Snicm 		screen_redraw_screen(c, 1);
573311827fbSnicm 
574311827fbSnicm 	c->tty.flags |= flags;
575311827fbSnicm 
576311827fbSnicm 	c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS);
577311827fbSnicm }
578311827fbSnicm 
579311827fbSnicm /* Redraw client when locked. */
580311827fbSnicm void
581311827fbSnicm server_redraw_locked(struct client *c)
582311827fbSnicm {
583311827fbSnicm 	struct screen_write_ctx	ctx;
584311827fbSnicm 	struct screen		screen;
585d658fe3cSnicm 	struct grid_cell	gc;
586311827fbSnicm 	u_int			colour, xx, yy, i;
587311827fbSnicm 	int    			style;
588311827fbSnicm 
589311827fbSnicm 	xx = c->tty.sx;
590311827fbSnicm 	yy = c->tty.sy - 1;
591311827fbSnicm 	if (xx == 0 || yy == 0)
592311827fbSnicm 		return;
593eaecedb2Snicm 	colour = options_get_number(&global_w_options, "clock-mode-colour");
594eaecedb2Snicm 	style = options_get_number(&global_w_options, "clock-mode-style");
595311827fbSnicm 
596d658fe3cSnicm 	memcpy(&gc, &grid_default_cell, sizeof gc);
597d658fe3cSnicm 	gc.fg = colour;
598d658fe3cSnicm 	gc.attr |= GRID_ATTR_BRIGHT;
599d658fe3cSnicm 
600311827fbSnicm 	screen_init(&screen, xx, yy, 0);
601311827fbSnicm 
602311827fbSnicm 	screen_write_start(&ctx, NULL, &screen);
603311827fbSnicm 	clock_draw(&ctx, colour, style);
604d658fe3cSnicm 
605d658fe3cSnicm 	if (password_failures != 0) {
606d658fe3cSnicm 		screen_write_cursormove(&ctx, 0, 0);
607d658fe3cSnicm 		screen_write_puts(
608d658fe3cSnicm 		    &ctx, &gc, "%u failed attempts", password_failures);
609d658fe3cSnicm 	}
610d658fe3cSnicm 
611311827fbSnicm 	screen_write_stop(&ctx);
612311827fbSnicm 
613311827fbSnicm 	for (i = 0; i < screen_size_y(&screen); i++)
614311827fbSnicm 		tty_draw_line(&c->tty, &screen, i, 0, 0);
61579fc95a8Snicm 	screen_redraw_screen(c, 1);
616311827fbSnicm 
617311827fbSnicm 	screen_free(&screen);
618311827fbSnicm }
619311827fbSnicm 
620311827fbSnicm /* Check for timers on client. */
621311827fbSnicm void
622311827fbSnicm server_check_timers(struct client *c)
623311827fbSnicm {
624311827fbSnicm 	struct session	*s;
625311827fbSnicm 	struct timeval	 tv;
626311827fbSnicm 	u_int		 interval;
627311827fbSnicm 
628311827fbSnicm 	if (c == NULL || c->session == NULL)
629311827fbSnicm 		return;
630311827fbSnicm 	s = c->session;
631311827fbSnicm 
632311827fbSnicm 	if (gettimeofday(&tv, NULL) != 0)
633311827fbSnicm 		fatal("gettimeofday");
634311827fbSnicm 
635311827fbSnicm 	if (c->message_string != NULL && timercmp(&tv, &c->message_timer, >))
636311827fbSnicm 		status_message_clear(c);
637311827fbSnicm 
638311827fbSnicm 	if (c->message_string != NULL || c->prompt_string != NULL) {
639311827fbSnicm 		/*
640311827fbSnicm 		 * Don't need timed redraw for messages/prompts so bail now.
641311827fbSnicm 		 * The status timer isn't reset when they are redrawn anyway.
642311827fbSnicm 		 */
643311827fbSnicm 		return;
644311827fbSnicm 	}
645311827fbSnicm 	if (!options_get_number(&s->options, "status"))
646311827fbSnicm 		return;
647311827fbSnicm 
648311827fbSnicm 	/* Check timer; resolution is only a second so don't be too clever. */
649311827fbSnicm 	interval = options_get_number(&s->options, "status-interval");
650311827fbSnicm 	if (interval == 0)
651311827fbSnicm 		return;
652311827fbSnicm 	if (tv.tv_sec < c->status_timer.tv_sec ||
653311827fbSnicm 	    ((u_int) tv.tv_sec) - c->status_timer.tv_sec >= interval)
654311827fbSnicm 		c->flags |= CLIENT_STATUS;
655311827fbSnicm }
656311827fbSnicm 
657311827fbSnicm /* Fill client pollfds. */
658311827fbSnicm void
659311827fbSnicm server_fill_clients(struct pollfd **pfd)
660311827fbSnicm {
661311827fbSnicm 	struct client		*c;
662311827fbSnicm 	struct window		*w;
663311827fbSnicm 	struct window_pane	*wp;
664311827fbSnicm 	u_int		 	 i;
665311827fbSnicm 
666311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
667311827fbSnicm 		c = ARRAY_ITEM(&clients, i);
668311827fbSnicm 
669311827fbSnicm 		server_check_timers(c);
670311827fbSnicm 		server_check_redraw(c);
671311827fbSnicm 
672311827fbSnicm 		if (c == NULL)
673311827fbSnicm 			(*pfd)->fd = -1;
674311827fbSnicm 		else {
675fd234c13Snicm 			(*pfd)->fd = c->ibuf.fd;
6760cd9638eSnicm 			if (!(c->flags & CLIENT_BAD))
677fd234c13Snicm 				(*pfd)->events |= POLLIN;
678fd234c13Snicm 			if (c->ibuf.w.queued > 0)
679311827fbSnicm 				(*pfd)->events |= POLLOUT;
680311827fbSnicm 		}
681311827fbSnicm 		(*pfd)++;
682311827fbSnicm 
683311827fbSnicm 		if (c == NULL || c->flags & CLIENT_SUSPENDED ||
684311827fbSnicm 		    c->tty.fd == -1 || c->session == NULL)
685311827fbSnicm 			(*pfd)->fd = -1;
686311827fbSnicm 		else {
687311827fbSnicm 			(*pfd)->fd = c->tty.fd;
688311827fbSnicm 			(*pfd)->events = POLLIN;
689311827fbSnicm 			if (BUFFER_USED(c->tty.out) > 0)
690311827fbSnicm 				(*pfd)->events |= POLLOUT;
691311827fbSnicm 		}
692311827fbSnicm 		(*pfd)++;
693311827fbSnicm 	}
694311827fbSnicm 
695311827fbSnicm 	/*
696311827fbSnicm 	 * Clear any window redraw flags (will have been redrawn as part of
697311827fbSnicm 	 * client).
698311827fbSnicm 	 */
699311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
700311827fbSnicm 		w = ARRAY_ITEM(&windows, i);
701311827fbSnicm 		if (w == NULL)
702311827fbSnicm 			continue;
703311827fbSnicm 
704311827fbSnicm 		w->flags &= ~WINDOW_REDRAW;
705311827fbSnicm 		TAILQ_FOREACH(wp, &w->panes, entry)
706311827fbSnicm 			wp->flags &= ~PANE_REDRAW;
707311827fbSnicm 	}
708311827fbSnicm }
709311827fbSnicm 
710311827fbSnicm /* Handle client pollfds. */
711311827fbSnicm void
712311827fbSnicm server_handle_clients(struct pollfd **pfd)
713311827fbSnicm {
714311827fbSnicm 	struct client	*c;
715311827fbSnicm 	u_int		 i;
716311827fbSnicm 
717311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
718311827fbSnicm 		c = ARRAY_ITEM(&clients, i);
719311827fbSnicm 
720311827fbSnicm 		if (c != NULL) {
721fd234c13Snicm 			if ((*pfd)->revents & (POLLERR|POLLNVAL|POLLHUP)) {
722311827fbSnicm 				server_lost_client(c);
723311827fbSnicm 				(*pfd) += 2;
724311827fbSnicm 				continue;
725fd234c13Snicm 			}
726fd234c13Snicm 
727fd234c13Snicm 			if ((*pfd)->revents & POLLOUT) {
728fd234c13Snicm 				if (msgbuf_write(&c->ibuf.w) < 0) {
7290cd9638eSnicm 					server_lost_client(c);
7300cd9638eSnicm 					(*pfd) += 2;
7310cd9638eSnicm 					continue;
732fd234c13Snicm 				}
733fd234c13Snicm 			}
734fd234c13Snicm 
735fd234c13Snicm 			if (c->flags & CLIENT_BAD) {
736fd234c13Snicm 				if (c->ibuf.w.queued == 0)
737fd234c13Snicm 					server_lost_client(c);
738fd234c13Snicm 				(*pfd) += 2;
739fd234c13Snicm 				continue;
740fd234c13Snicm 			} else if ((*pfd)->revents & POLLIN) {
741fd234c13Snicm 				if (server_msg_dispatch(c) != 0) {
742fd234c13Snicm 					server_lost_client(c);
743fd234c13Snicm 					(*pfd) += 2;
744fd234c13Snicm 					continue;
745fd234c13Snicm 				}
746fd234c13Snicm 			}
747311827fbSnicm 		}
748311827fbSnicm 		(*pfd)++;
749311827fbSnicm 
750311827fbSnicm 		if (c != NULL && !(c->flags & CLIENT_SUSPENDED) &&
751311827fbSnicm 		    c->tty.fd != -1 && c->session != NULL) {
752311827fbSnicm 			if (buffer_poll(*pfd, c->tty.in, c->tty.out) != 0)
753311827fbSnicm 				server_lost_client(c);
754311827fbSnicm 			else
755311827fbSnicm 				server_handle_client(c);
756311827fbSnicm 		}
757311827fbSnicm 		(*pfd)++;
758311827fbSnicm 	}
759311827fbSnicm }
760311827fbSnicm 
761311827fbSnicm /* accept(2) and create new client. */
762980b7663Snicm void
763311827fbSnicm server_accept_client(int srv_fd)
764311827fbSnicm {
765311827fbSnicm 	struct sockaddr_storage	sa;
766311827fbSnicm 	socklen_t		slen = sizeof sa;
767311827fbSnicm 	int			fd;
768311827fbSnicm 
769311827fbSnicm 	fd = accept(srv_fd, (struct sockaddr *) &sa, &slen);
770311827fbSnicm 	if (fd == -1) {
771311827fbSnicm 		if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
772980b7663Snicm 			return;
773311827fbSnicm 		fatal("accept failed");
774311827fbSnicm 	}
775311827fbSnicm 	if (sigterm) {
776311827fbSnicm 		close(fd);
777980b7663Snicm 		return;
778311827fbSnicm 	}
779980b7663Snicm 	server_create_client(fd);
780311827fbSnicm }
781311827fbSnicm 
782311827fbSnicm /* Input data from client. */
783311827fbSnicm void
784311827fbSnicm server_handle_client(struct client *c)
785311827fbSnicm {
786311827fbSnicm 	struct window_pane	*wp;
787311827fbSnicm 	struct screen		*s;
788311827fbSnicm 	struct timeval	 	 tv;
789311827fbSnicm 	struct key_binding	*bd;
790311827fbSnicm 	int		 	 key, prefix, status, xtimeout;
791311827fbSnicm 	int			 mode;
792311827fbSnicm 	u_char			 mouse[3];
793311827fbSnicm 
794311827fbSnicm 	xtimeout = options_get_number(&c->session->options, "repeat-time");
795311827fbSnicm 	if (xtimeout != 0 && c->flags & CLIENT_REPEAT) {
796311827fbSnicm 		if (gettimeofday(&tv, NULL) != 0)
797311827fbSnicm 			fatal("gettimeofday");
798311827fbSnicm 		if (timercmp(&tv, &c->repeat_timer, >))
799311827fbSnicm 			c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
800311827fbSnicm 	}
801311827fbSnicm 
802311827fbSnicm 	/* Process keys. */
803311827fbSnicm 	prefix = options_get_number(&c->session->options, "prefix");
804311827fbSnicm 	while (tty_keys_next(&c->tty, &key, mouse) == 0) {
805311827fbSnicm 		server_activity = time(NULL);
806311827fbSnicm 
807311827fbSnicm 		if (c->session == NULL)
808311827fbSnicm 			return;
809311827fbSnicm 		wp = c->session->curw->window->active;	/* could die */
810311827fbSnicm 
811311827fbSnicm 		status_message_clear(c);
812311827fbSnicm 		if (c->prompt_string != NULL) {
813311827fbSnicm 			status_prompt_key(c, key);
814311827fbSnicm 			continue;
815311827fbSnicm 		}
816311827fbSnicm 		if (server_locked)
817311827fbSnicm 			continue;
818311827fbSnicm 
819311827fbSnicm 		/* Check for mouse keys. */
820311827fbSnicm 		if (key == KEYC_MOUSE) {
821311827fbSnicm 			window_pane_mouse(wp, c, mouse[0], mouse[1], mouse[2]);
822311827fbSnicm 			continue;
823311827fbSnicm 		}
824311827fbSnicm 
825311827fbSnicm 		/* No previous prefix key. */
826311827fbSnicm 		if (!(c->flags & CLIENT_PREFIX)) {
827311827fbSnicm 			if (key == prefix)
828311827fbSnicm 				c->flags |= CLIENT_PREFIX;
829ad4696b5Snicm 			else {
830ad4696b5Snicm 				/* Try as a non-prefix key binding. */
831ad4696b5Snicm 				if ((bd = key_bindings_lookup(key)) == NULL)
832311827fbSnicm 					window_pane_key(wp, c, key);
833ad4696b5Snicm 				else
834ad4696b5Snicm 					key_bindings_dispatch(bd, c);
835ad4696b5Snicm 			}
836311827fbSnicm 			continue;
837311827fbSnicm 		}
838311827fbSnicm 
839311827fbSnicm 		/* Prefix key already pressed. Reset prefix and lookup key. */
840311827fbSnicm 		c->flags &= ~CLIENT_PREFIX;
841ad4696b5Snicm 		if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) {
842311827fbSnicm 			/* If repeating, treat this as a key, else ignore. */
843311827fbSnicm 			if (c->flags & CLIENT_REPEAT) {
844311827fbSnicm 				c->flags &= ~CLIENT_REPEAT;
845311827fbSnicm 				if (key == prefix)
846311827fbSnicm 					c->flags |= CLIENT_PREFIX;
847311827fbSnicm 				else
848311827fbSnicm 					window_pane_key(wp, c, key);
849311827fbSnicm 			}
850311827fbSnicm 			continue;
851311827fbSnicm 		}
852311827fbSnicm 
853311827fbSnicm 		/* If already repeating, but this key can't repeat, skip it. */
854311827fbSnicm 		if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
855311827fbSnicm 			c->flags &= ~CLIENT_REPEAT;
856311827fbSnicm 			if (key == prefix)
857311827fbSnicm 				c->flags |= CLIENT_PREFIX;
858311827fbSnicm 			else
859311827fbSnicm 				window_pane_key(wp, c, key);
860311827fbSnicm 			continue;
861311827fbSnicm 		}
862311827fbSnicm 
863311827fbSnicm 		/* If this key can repeat, reset the repeat flags and timer. */
864311827fbSnicm 		if (xtimeout != 0 && bd->can_repeat) {
865311827fbSnicm 			c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
866311827fbSnicm 
867311827fbSnicm 			tv.tv_sec = xtimeout / 1000;
868311827fbSnicm 			tv.tv_usec = (xtimeout % 1000) * 1000L;
869311827fbSnicm 			if (gettimeofday(&c->repeat_timer, NULL) != 0)
870311827fbSnicm 				fatal("gettimeofday");
871311827fbSnicm 			timeradd(&c->repeat_timer, &tv, &c->repeat_timer);
872311827fbSnicm 		}
873311827fbSnicm 
874311827fbSnicm 		/* Dispatch the command. */
875311827fbSnicm 		key_bindings_dispatch(bd, c);
876311827fbSnicm 	}
877311827fbSnicm 	if (c->session == NULL)
878311827fbSnicm 		return;
879311827fbSnicm 	wp = c->session->curw->window->active;	/* could die - do each loop */
880311827fbSnicm 	s = wp->screen;
881311827fbSnicm 
88245400b51Snicm 	/*
88345400b51Snicm 	 * Update cursor position and mode settings. The scroll region and
88445400b51Snicm 	 * attributes are cleared across poll(2) as this is the most likely
88545400b51Snicm 	 * time a user may interrupt tmux, for example with ~^Z in ssh(1). This
88645400b51Snicm 	 * is a compromise between excessive resets and likelihood of an
88745400b51Snicm 	 * interrupt.
88845400b51Snicm 	 *
88945400b51Snicm 	 * tty_region/tty_reset/tty_update_mode already take care of not
89045400b51Snicm 	 * resetting things that are already in their default state.
89145400b51Snicm 	 */
892311827fbSnicm 	status = options_get_number(&c->session->options, "status");
893dde47eafSnicm 	tty_region(&c->tty, 0, c->tty.sy - 1, 0);
894af9e4c5dSnicm 	if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
895af9e4c5dSnicm 		tty_cursor(&c->tty, 0, 0, 0, 0);
896af9e4c5dSnicm 	else
897311827fbSnicm 		tty_cursor(&c->tty, s->cx, s->cy, wp->xoff, wp->yoff);
898311827fbSnicm 
899311827fbSnicm 	mode = s->mode;
900311827fbSnicm 	if (server_locked)
901311827fbSnicm 		mode &= ~TTY_NOCURSOR;
902311827fbSnicm 	tty_update_mode(&c->tty, mode);
90345400b51Snicm 	tty_reset(&c->tty);
904311827fbSnicm }
905311827fbSnicm 
906311827fbSnicm /* Lost a client. */
907311827fbSnicm void
908311827fbSnicm server_lost_client(struct client *c)
909311827fbSnicm {
910311827fbSnicm 	u_int	i;
911311827fbSnicm 
912311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
913311827fbSnicm 		if (ARRAY_ITEM(&clients, i) == c)
914311827fbSnicm 			ARRAY_SET(&clients, i, NULL);
915311827fbSnicm 	}
9160bb39cd5Snicm 	log_debug("lost client %d", c->ibuf.fd);
917311827fbSnicm 
918*f369cd7fSnicm 	/*
919*f369cd7fSnicm 	 * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
920*f369cd7fSnicm 	 * and tty_free might close an unrelated fd.
921*f369cd7fSnicm 	 */
922*f369cd7fSnicm 	if (c->flags & CLIENT_TERMINAL)
923130af69dSnicm 		tty_free(&c->tty);
924311827fbSnicm 
925311827fbSnicm 	screen_free(&c->status);
926311827fbSnicm 
927311827fbSnicm 	if (c->title != NULL)
928311827fbSnicm 		xfree(c->title);
929311827fbSnicm 
930311827fbSnicm 	if (c->message_string != NULL)
931311827fbSnicm 		xfree(c->message_string);
932311827fbSnicm 
933311827fbSnicm 	if (c->prompt_string != NULL)
934311827fbSnicm 		xfree(c->prompt_string);
935311827fbSnicm 	if (c->prompt_buffer != NULL)
936311827fbSnicm 		xfree(c->prompt_buffer);
937311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++)
938311827fbSnicm 		xfree(ARRAY_ITEM(&c->prompt_hdata, i));
939311827fbSnicm 	ARRAY_FREE(&c->prompt_hdata);
940311827fbSnicm 
941311827fbSnicm 	if (c->cwd != NULL)
942311827fbSnicm 		xfree(c->cwd);
943311827fbSnicm 
944fd234c13Snicm 	close(c->ibuf.fd);
945fd234c13Snicm 	imsg_clear(&c->ibuf);
946311827fbSnicm 	xfree(c);
947311827fbSnicm 
948311827fbSnicm 	recalculate_sizes();
949311827fbSnicm }
950311827fbSnicm 
951311827fbSnicm /* Handle window data. */
952311827fbSnicm void
953311827fbSnicm server_handle_window(struct window *w, struct window_pane *wp)
954311827fbSnicm {
955311827fbSnicm 	struct session	*s;
956311827fbSnicm 	u_int		 i;
957311827fbSnicm 	int		 update;
958311827fbSnicm 
959311827fbSnicm 	window_pane_parse(wp);
960311827fbSnicm 
961311827fbSnicm 	if ((w->flags & (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT)) == 0)
962311827fbSnicm 		return;
963311827fbSnicm 
964311827fbSnicm 	update = 0;
965311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
966311827fbSnicm 		s = ARRAY_ITEM(&sessions, i);
967311827fbSnicm 		if (s == NULL || !session_has(s, w))
968311827fbSnicm 			continue;
969311827fbSnicm 
9700903c7b9Snicm 		update += server_check_window_bell(s, w);
971311827fbSnicm 		update += server_check_window_activity(s, w);
972311827fbSnicm 		update += server_check_window_content(s, w, wp);
973311827fbSnicm 	}
974311827fbSnicm 	if (update)
975311827fbSnicm 		server_status_window(w);
976311827fbSnicm 
977311827fbSnicm 	w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT);
978311827fbSnicm }
979311827fbSnicm 
980311827fbSnicm int
9810903c7b9Snicm server_check_window_bell(struct session *s, struct window *w)
982311827fbSnicm {
983311827fbSnicm 	struct client	*c;
984311827fbSnicm 	u_int		 i;
9850903c7b9Snicm 	int		 action, visual;
986311827fbSnicm 
987311827fbSnicm 	if (!(w->flags & WINDOW_BELL))
988311827fbSnicm 		return (0);
989311827fbSnicm 	if (session_alert_has_window(s, w, WINDOW_BELL))
990311827fbSnicm 		return (0);
991311827fbSnicm 	session_alert_add(s, w, WINDOW_BELL);
992311827fbSnicm 
993311827fbSnicm 	action = options_get_number(&s->options, "bell-action");
994311827fbSnicm 	switch (action) {
995311827fbSnicm 	case BELL_ANY:
996311827fbSnicm 		if (s->flags & SESSION_UNATTACHED)
997311827fbSnicm 			break;
9980903c7b9Snicm 		visual = options_get_number(&s->options, "visual-bell");
999311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1000311827fbSnicm 			c = ARRAY_ITEM(&clients, i);
10010903c7b9Snicm 			if (c == NULL || c->session != s)
10020903c7b9Snicm 				continue;
10030903c7b9Snicm 			if (!visual) {
1004311827fbSnicm 				tty_putcode(&c->tty, TTYC_BEL);
10050903c7b9Snicm 				continue;
10060903c7b9Snicm 			}
10070903c7b9Snicm  			if (c->session->curw->window == w) {
10080903c7b9Snicm 				status_message_set(c, "Bell in current window");
10090903c7b9Snicm 				continue;
10100903c7b9Snicm 			}
10110903c7b9Snicm 			status_message_set(c, "Bell in window %u",
10120903c7b9Snicm 			    winlink_find_by_window(&s->windows, w)->idx);
1013311827fbSnicm 		}
1014311827fbSnicm 		break;
1015311827fbSnicm 	case BELL_CURRENT:
10160903c7b9Snicm 		if (s->flags & SESSION_UNATTACHED)
1017311827fbSnicm 			break;
10180903c7b9Snicm 		visual = options_get_number(&s->options, "visual-bell");
1019311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1020311827fbSnicm 			c = ARRAY_ITEM(&clients, i);
10210903c7b9Snicm 			if (c == NULL || c->session != s)
10220903c7b9Snicm 				continue;
10230903c7b9Snicm  			if (c->session->curw->window != w)
10240903c7b9Snicm 				continue;
10250903c7b9Snicm 			if (!visual) {
1026311827fbSnicm 				tty_putcode(&c->tty, TTYC_BEL);
10270903c7b9Snicm 				continue;
10280903c7b9Snicm 			}
10290903c7b9Snicm 			status_message_set(c, "Bell in current window");
1030311827fbSnicm 		}
1031311827fbSnicm 		break;
1032311827fbSnicm 	}
1033311827fbSnicm 	return (1);
1034311827fbSnicm }
1035311827fbSnicm 
1036311827fbSnicm int
1037311827fbSnicm server_check_window_activity(struct session *s, struct window *w)
1038311827fbSnicm {
10390903c7b9Snicm 	struct client	*c;
10400903c7b9Snicm 	u_int		 i;
10410903c7b9Snicm 
1042311827fbSnicm 	if (!(w->flags & WINDOW_ACTIVITY))
1043311827fbSnicm 		return (0);
10440903c7b9Snicm 
1045311827fbSnicm 	if (!options_get_number(&w->options, "monitor-activity"))
1046311827fbSnicm 		return (0);
10470903c7b9Snicm 
1048311827fbSnicm 	if (session_alert_has_window(s, w, WINDOW_ACTIVITY))
1049311827fbSnicm 		return (0);
10500903c7b9Snicm 	if (s->curw->window == w)
10510903c7b9Snicm 		return (0);
10520903c7b9Snicm 
1053311827fbSnicm 	session_alert_add(s, w, WINDOW_ACTIVITY);
10540903c7b9Snicm 	if (s->flags & SESSION_UNATTACHED)
10550903c7b9Snicm 		return (0);
10560903c7b9Snicm  	if (options_get_number(&s->options, "visual-activity")) {
10570903c7b9Snicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
10580903c7b9Snicm 			c = ARRAY_ITEM(&clients, i);
10590903c7b9Snicm 			if (c == NULL || c->session != s)
10600903c7b9Snicm 				continue;
10610903c7b9Snicm 			status_message_set(c, "Activity in window %u",
10620903c7b9Snicm 			    winlink_find_by_window(&s->windows, w)->idx);
10630903c7b9Snicm 		}
10640903c7b9Snicm 	}
10650903c7b9Snicm 
1066311827fbSnicm 	return (1);
1067311827fbSnicm }
1068311827fbSnicm 
1069311827fbSnicm int
1070311827fbSnicm server_check_window_content(
1071311827fbSnicm     struct session *s, struct window *w, struct window_pane *wp)
1072311827fbSnicm {
10730903c7b9Snicm 	struct client	*c;
10740903c7b9Snicm 	u_int		 i;
1075311827fbSnicm 	char		*found, *ptr;
1076311827fbSnicm 
10770903c7b9Snicm 	if (!(w->flags & WINDOW_ACTIVITY))	/* activity for new content */
1078311827fbSnicm 		return (0);
10790903c7b9Snicm 
10800903c7b9Snicm 	ptr = options_get_string(&w->options, "monitor-content");
10810903c7b9Snicm 	if (ptr == NULL || *ptr == '\0')
1082311827fbSnicm 		return (0);
10830903c7b9Snicm 
1084311827fbSnicm 	if (session_alert_has_window(s, w, WINDOW_CONTENT))
1085311827fbSnicm 		return (0);
10860903c7b9Snicm 	if (s->curw->window == w)
10870903c7b9Snicm 		return (0);
10880903c7b9Snicm 
1089570a3589Snicm 	if ((found = window_pane_search(wp, ptr, NULL)) == NULL)
1090311827fbSnicm 		return (0);
1091311827fbSnicm     	xfree(found);
10920903c7b9Snicm 
10930903c7b9Snicm 	session_alert_add(s, w, WINDOW_CONTENT);
10940903c7b9Snicm 	if (s->flags & SESSION_UNATTACHED)
10950903c7b9Snicm 		return (0);
10960903c7b9Snicm  	if (options_get_number(&s->options, "visual-content")) {
10970903c7b9Snicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
10980903c7b9Snicm 			c = ARRAY_ITEM(&clients, i);
10990903c7b9Snicm 			if (c == NULL || c->session != s)
11000903c7b9Snicm 				continue;
11010903c7b9Snicm 			status_message_set(c, "Content in window %u",
11020903c7b9Snicm 			    winlink_find_by_window(&s->windows, w)->idx);
11030903c7b9Snicm 		}
11040903c7b9Snicm 	}
11050903c7b9Snicm 
1106311827fbSnicm 	return (1);
1107311827fbSnicm }
1108311827fbSnicm 
1109c752cdacSnicm /* Check if window still exists. */
1110311827fbSnicm void
1111311827fbSnicm server_check_window(struct window *w)
1112311827fbSnicm {
1113311827fbSnicm 	struct window_pane	*wp, *wq;
1114e0b5b8e5Snicm 	struct options		*oo = &w->options;
1115311827fbSnicm 	struct client		*c;
1116311827fbSnicm 	struct session		*s;
1117311827fbSnicm 	struct winlink		*wl;
1118311827fbSnicm 	u_int		 	 i, j;
1119e0b5b8e5Snicm 	int		 	 destroyed;
1120311827fbSnicm 
1121311827fbSnicm 	destroyed = 1;
1122311827fbSnicm 
1123311827fbSnicm 	wp = TAILQ_FIRST(&w->panes);
1124311827fbSnicm 	while (wp != NULL) {
1125311827fbSnicm 		wq = TAILQ_NEXT(wp, entry);
1126c752cdacSnicm 		/*
1127c752cdacSnicm 		 * If the pane has died and the remain-on-exit flag is not set,
1128c752cdacSnicm 		 * remove the pane; otherwise, if the flag is set, don't allow
1129c752cdacSnicm 		 * the window to be destroyed (or it'll close when the last
1130c752cdacSnicm 		 * pane dies).
1131c752cdacSnicm 		 */
11322be3d294Snicm 		if (wp->fd == -1 && !options_get_number(oo, "remain-on-exit")) {
1133af9e4c5dSnicm 			layout_close_pane(wp);
1134311827fbSnicm 			window_remove_pane(w, wp);
1135311827fbSnicm 			server_redraw_window(w);
1136c752cdacSnicm 		} else
1137c752cdacSnicm 			destroyed = 0;
1138311827fbSnicm 		wp = wq;
1139311827fbSnicm 	}
1140311827fbSnicm 
1141311827fbSnicm 	if (!destroyed)
1142311827fbSnicm 		return;
1143311827fbSnicm 
1144311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1145311827fbSnicm 		s = ARRAY_ITEM(&sessions, i);
1146311827fbSnicm 		if (s == NULL)
1147311827fbSnicm 			continue;
1148311827fbSnicm 		if (!session_has(s, w))
1149311827fbSnicm 			continue;
1150311827fbSnicm 
1151311827fbSnicm 	restart:
1152311827fbSnicm 		/* Detach window and either redraw or kill clients. */
1153311827fbSnicm 		RB_FOREACH(wl, winlinks, &s->windows) {
1154311827fbSnicm 			if (wl->window != w)
1155311827fbSnicm 				continue;
1156311827fbSnicm 			destroyed = session_detach(s, wl);
1157311827fbSnicm 			for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
1158311827fbSnicm 				c = ARRAY_ITEM(&clients, j);
1159311827fbSnicm 				if (c == NULL || c->session != s)
1160311827fbSnicm 					continue;
1161311827fbSnicm 				if (!destroyed) {
1162311827fbSnicm 					server_redraw_client(c);
1163311827fbSnicm 					continue;
1164311827fbSnicm 				}
1165311827fbSnicm 				c->session = NULL;
1166311827fbSnicm 				server_write_client(c, MSG_EXIT, NULL, 0);
1167311827fbSnicm 			}
1168311827fbSnicm 			/* If the session was destroyed, bail now. */
1169311827fbSnicm 			if (destroyed)
1170311827fbSnicm 				break;
1171311827fbSnicm 			goto restart;
1172311827fbSnicm 		}
1173311827fbSnicm 	}
1174311827fbSnicm 
1175311827fbSnicm 	recalculate_sizes();
1176311827fbSnicm }
1177311827fbSnicm 
1178311827fbSnicm /* Call any once-per-second timers. */
1179311827fbSnicm void
1180311827fbSnicm server_second_timers(void)
1181311827fbSnicm {
1182311827fbSnicm 	struct window		*w;
1183311827fbSnicm 	struct window_pane	*wp;
1184311827fbSnicm 	u_int		 	 i;
1185311827fbSnicm 	int			 xtimeout;
1186311827fbSnicm 	struct tm	 	 now, then;
1187311827fbSnicm 	static time_t	 	 last_t = 0;
1188311827fbSnicm 	time_t		 	 t;
1189311827fbSnicm 
1190311827fbSnicm 	t = time(NULL);
1191eaecedb2Snicm 	xtimeout = options_get_number(&global_s_options, "lock-after-time");
1192311827fbSnicm 	if (xtimeout > 0 && t > server_activity + xtimeout)
1193311827fbSnicm 		server_lock();
1194311827fbSnicm 
1195311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
1196311827fbSnicm 		w = ARRAY_ITEM(&windows, i);
1197311827fbSnicm 		if (w == NULL)
1198311827fbSnicm 			continue;
1199311827fbSnicm 
1200311827fbSnicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
1201311827fbSnicm 			if (wp->mode != NULL && wp->mode->timer != NULL)
1202311827fbSnicm 				wp->mode->timer(wp);
1203311827fbSnicm 		}
1204311827fbSnicm 	}
1205311827fbSnicm 
1206311827fbSnicm 	/* Check for a minute having passed. */
1207311827fbSnicm 	gmtime_r(&t, &now);
1208311827fbSnicm 	gmtime_r(&last_t, &then);
1209311827fbSnicm 	if (now.tm_min == then.tm_min)
1210311827fbSnicm 		return;
1211311827fbSnicm 	last_t = t;
1212311827fbSnicm 
1213311827fbSnicm 	/* If locked, redraw all clients. */
1214311827fbSnicm 	if (server_locked) {
1215311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1216311827fbSnicm 			if (ARRAY_ITEM(&clients, i) != NULL)
1217311827fbSnicm 				server_redraw_client(ARRAY_ITEM(&clients, i));
1218311827fbSnicm 		}
1219311827fbSnicm 	}
1220311827fbSnicm }
1221311827fbSnicm 
1222311827fbSnicm /* Update socket execute permissions based on whether sessions are attached. */
1223311827fbSnicm int
1224311827fbSnicm server_update_socket(void)
1225311827fbSnicm {
1226311827fbSnicm 	struct session	*s;
1227311827fbSnicm 	u_int		 i;
1228311827fbSnicm 	static int	 last = -1;
1229311827fbSnicm 	int		 n;
1230311827fbSnicm 
1231311827fbSnicm 	n = 0;
1232311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1233311827fbSnicm 		s = ARRAY_ITEM(&sessions, i);
1234311827fbSnicm 		if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
1235311827fbSnicm 			n++;
1236311827fbSnicm 			break;
1237311827fbSnicm 		}
1238311827fbSnicm 	}
1239311827fbSnicm 
1240311827fbSnicm 	if (n != last) {
1241311827fbSnicm 		last = n;
1242311827fbSnicm 		if (n != 0)
1243311827fbSnicm 			chmod(socket_path, S_IRWXU);
1244311827fbSnicm 		else
1245311827fbSnicm 			chmod(socket_path, S_IRUSR|S_IWUSR);
1246311827fbSnicm 	}
1247311827fbSnicm 
1248311827fbSnicm 	return (n);
1249311827fbSnicm }
1250