xref: /openbsd/usr.bin/tmux/server.c (revision 130af69d)
1*130af69dSnicm /* $OpenBSD: server.c,v 1.19 2009/08/11 19:32:25 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);
113311827fbSnicm }
114311827fbSnicm 
115311827fbSnicm /* Find client index. */
116311827fbSnicm int
117311827fbSnicm server_client_index(struct client *c)
118311827fbSnicm {
119311827fbSnicm 	u_int	i;
120311827fbSnicm 
121311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
122311827fbSnicm 		if (c == ARRAY_ITEM(&clients, i))
123311827fbSnicm 			return (i);
124311827fbSnicm 	}
125311827fbSnicm 	return (-1);
126311827fbSnicm }
127311827fbSnicm 
128311827fbSnicm /* Fork new server. */
129311827fbSnicm int
130311827fbSnicm server_start(char *path)
131311827fbSnicm {
1320cd9638eSnicm 	struct client	*c;
1330cd9638eSnicm 	int		 pair[2], srv_fd;
134311827fbSnicm 	char		*cause;
135311827fbSnicm 	char		 rpathbuf[MAXPATHLEN];
136311827fbSnicm 
137311827fbSnicm 	/* The first client is special and gets a socketpair; create it. */
138311827fbSnicm 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
139311827fbSnicm 		fatal("socketpair failed");
140311827fbSnicm 
141311827fbSnicm 	switch (fork()) {
142311827fbSnicm 	case -1:
143311827fbSnicm 		fatal("fork failed");
144311827fbSnicm 	case 0:
145311827fbSnicm 		break;
146311827fbSnicm 	default:
147311827fbSnicm 		close(pair[1]);
148311827fbSnicm 		return (pair[0]);
149311827fbSnicm 	}
150311827fbSnicm 	close(pair[0]);
151311827fbSnicm 
152311827fbSnicm 	/*
153311827fbSnicm 	 * Must daemonise before loading configuration as the PID changes so
154311827fbSnicm 	 * $TMUX would be wrong for sessions created in the config file.
155311827fbSnicm 	 */
1560cd9638eSnicm 	if (daemon(1, 0) != 0)
157311827fbSnicm 		fatal("daemon failed");
158311827fbSnicm 
1590cd9638eSnicm 	logfile("server");
1600cd9638eSnicm 	log_debug("server started, pid %ld", (long) getpid());
1610cd9638eSnicm 
162311827fbSnicm 	ARRAY_INIT(&windows);
163311827fbSnicm 	ARRAY_INIT(&clients);
164311827fbSnicm 	ARRAY_INIT(&sessions);
16568895571Snicm 	mode_key_init_trees();
166311827fbSnicm 	key_bindings_init();
167311827fbSnicm 	utf8_build();
168311827fbSnicm 
169311827fbSnicm 	server_locked = 0;
170311827fbSnicm 	server_password = NULL;
171311827fbSnicm 	server_activity = time(NULL);
172311827fbSnicm 
173311827fbSnicm 	start_time = time(NULL);
174311827fbSnicm 	socket_path = path;
175311827fbSnicm 
176311827fbSnicm 	if (realpath(socket_path, rpathbuf) == NULL)
177311827fbSnicm 		strlcpy(rpathbuf, socket_path, sizeof rpathbuf);
1780cd9638eSnicm 	log_debug("socket path %s", socket_path);
179311827fbSnicm 	setproctitle("server (%s)", rpathbuf);
180311827fbSnicm 
181311827fbSnicm 	srv_fd = server_create_socket();
182311827fbSnicm 	server_create_client(pair[1]);
183311827fbSnicm 
1840cd9638eSnicm 	if (access(SYSTEM_CFG, R_OK) != 0) {
1850cd9638eSnicm 		if (errno != ENOENT) {
1860cd9638eSnicm 			xasprintf(
1870cd9638eSnicm 			    &cause, "%s: %s", strerror(errno), SYSTEM_CFG);
1880cd9638eSnicm 			goto error;
1890cd9638eSnicm 		}
1900cd9638eSnicm 	} else if (load_cfg(SYSTEM_CFG, &cause) != 0)
1910cd9638eSnicm 		goto error;
1920cd9638eSnicm 	if (cfg_file != NULL && load_cfg(cfg_file, &cause) != 0)
1930cd9638eSnicm 		goto error;
1940cd9638eSnicm 
1950cd9638eSnicm 	exit(server_main(srv_fd));
1960cd9638eSnicm 
1970cd9638eSnicm error:
1980cd9638eSnicm 	/* Write the error and shutdown the server. */
1990cd9638eSnicm 	c = ARRAY_FIRST(&clients);
2000cd9638eSnicm 
2010cd9638eSnicm 	server_write_error(c, cause);
2020cd9638eSnicm 	xfree(cause);
2030cd9638eSnicm 
2040cd9638eSnicm 	server_shutdown();
2050cd9638eSnicm 	c->flags |= CLIENT_BAD;
2060cd9638eSnicm 
207311827fbSnicm 	exit(server_main(srv_fd));
208311827fbSnicm }
209311827fbSnicm 
210311827fbSnicm /* Create server socket. */
211311827fbSnicm int
212311827fbSnicm server_create_socket(void)
213311827fbSnicm {
214311827fbSnicm 	struct sockaddr_un	sa;
215311827fbSnicm 	size_t			size;
216311827fbSnicm 	mode_t			mask;
217311827fbSnicm 	int			fd, mode;
218311827fbSnicm 
219311827fbSnicm 	memset(&sa, 0, sizeof sa);
220311827fbSnicm 	sa.sun_family = AF_UNIX;
221311827fbSnicm 	size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
222311827fbSnicm 	if (size >= sizeof sa.sun_path) {
223311827fbSnicm 		errno = ENAMETOOLONG;
224311827fbSnicm 		fatal("socket failed");
225311827fbSnicm 	}
226311827fbSnicm 	unlink(sa.sun_path);
227311827fbSnicm 
228311827fbSnicm 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
229311827fbSnicm 		fatal("socket failed");
230311827fbSnicm 
231311827fbSnicm 	mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
232311827fbSnicm 	if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1)
233311827fbSnicm 		fatal("bind failed");
234311827fbSnicm 	umask(mask);
235311827fbSnicm 
236311827fbSnicm 	if (listen(fd, 16) == -1)
237311827fbSnicm 		fatal("listen failed");
238311827fbSnicm 
239311827fbSnicm 	if ((mode = fcntl(fd, F_GETFL)) == -1)
240311827fbSnicm 		fatal("fcntl failed");
241311827fbSnicm 	if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
242311827fbSnicm 		fatal("fcntl failed");
243311827fbSnicm 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
244311827fbSnicm 		fatal("fcntl failed");
245311827fbSnicm 
246311827fbSnicm 	return (fd);
247311827fbSnicm }
248311827fbSnicm 
249311827fbSnicm /* Main server loop. */
250311827fbSnicm int
251311827fbSnicm server_main(int srv_fd)
252311827fbSnicm {
253311827fbSnicm 	struct window	*w;
254311827fbSnicm 	struct pollfd	*pfds, *pfd;
255311827fbSnicm 	int		 nfds, xtimeout;
256311827fbSnicm 	u_int		 i, n;
257311827fbSnicm 	time_t		 now, last;
258311827fbSnicm 
259311827fbSnicm 	siginit();
260311827fbSnicm 
261311827fbSnicm 	last = time(NULL);
262311827fbSnicm 
263311827fbSnicm 	pfds = NULL;
264311827fbSnicm 	for (;;) {
265311827fbSnicm 		/* If sigterm, kill all windows and clients. */
266311827fbSnicm 		if (sigterm)
267311827fbSnicm 			server_shutdown();
268311827fbSnicm 
269311827fbSnicm 		/* Handle child exit. */
270311827fbSnicm 		if (sigchld) {
271311827fbSnicm 			server_child_signal();
272311827fbSnicm 			sigchld = 0;
273311827fbSnicm 		}
274311827fbSnicm 
275311827fbSnicm 		/* Recreate socket on SIGUSR1. */
276311827fbSnicm 		if (sigusr1) {
277311827fbSnicm 			close(srv_fd);
278311827fbSnicm 			srv_fd = server_create_socket();
279311827fbSnicm 			sigusr1 = 0;
280311827fbSnicm 		}
281311827fbSnicm 
282311827fbSnicm 		/* Initialise pollfd array. */
283311827fbSnicm 		nfds = 1;
284311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
285311827fbSnicm 			w = ARRAY_ITEM(&windows, i);
286311827fbSnicm 			if (w != NULL)
287311827fbSnicm 				nfds += window_count_panes(w);
288311827fbSnicm 		}
289311827fbSnicm 		nfds += ARRAY_LENGTH(&clients) * 2;
290311827fbSnicm 		pfds = xrealloc(pfds, nfds, sizeof *pfds);
291311827fbSnicm 		memset(pfds, 0, nfds * sizeof *pfds);
292311827fbSnicm 		pfd = pfds;
293311827fbSnicm 
294311827fbSnicm 		/* Fill server socket. */
295311827fbSnicm 		pfd->fd = srv_fd;
296311827fbSnicm 		pfd->events = POLLIN;
297311827fbSnicm 		pfd++;
298311827fbSnicm 
299311827fbSnicm 		/* Fill window and client sockets. */
300311827fbSnicm 		server_fill_windows(&pfd);
301311827fbSnicm 		server_fill_clients(&pfd);
302311827fbSnicm 
303311827fbSnicm 		/* Update socket permissions. */
304311827fbSnicm 		xtimeout = INFTIM;
305311827fbSnicm 		if (sigterm || server_update_socket() != 0)
306311827fbSnicm 			xtimeout = POLL_TIMEOUT;
307311827fbSnicm 
308311827fbSnicm 		/* Do the poll. */
3099b1fc963Snicm 		if (poll(pfds, nfds, xtimeout) == -1) {
310311827fbSnicm 			if (errno == EAGAIN || errno == EINTR)
311311827fbSnicm 				continue;
312311827fbSnicm 			fatal("poll failed");
313311827fbSnicm 		}
314311827fbSnicm 		pfd = pfds;
315311827fbSnicm 
316311827fbSnicm 		/* Handle server socket. */
317311827fbSnicm 		if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP))
318311827fbSnicm 			fatalx("lost server socket");
319311827fbSnicm 		if (pfd->revents & POLLIN) {
320311827fbSnicm 			server_accept_client(srv_fd);
321311827fbSnicm 			continue;
322311827fbSnicm 		}
323311827fbSnicm 		pfd++;
324311827fbSnicm 
325311827fbSnicm 		/* Call second-based timers. */
326311827fbSnicm 		now = time(NULL);
327311827fbSnicm 		if (now != last) {
328311827fbSnicm 			last = now;
329311827fbSnicm 			server_second_timers();
330311827fbSnicm 		}
331311827fbSnicm 
332311827fbSnicm 		/* Set window names. */
333311827fbSnicm 		set_window_names();
334311827fbSnicm 
335311827fbSnicm 		/*
336311827fbSnicm 		 * Handle window and client sockets. Clients can create
337311827fbSnicm 		 * windows, so windows must come first to avoid messing up by
338311827fbSnicm 		 * increasing the array size.
339311827fbSnicm 		 */
340311827fbSnicm 		server_handle_windows(&pfd);
341311827fbSnicm 		server_handle_clients(&pfd);
342311827fbSnicm 
3430476b68eSnicm 		/* Collect any unset key bindings. */
3440476b68eSnicm 		key_bindings_clean();
3450476b68eSnicm 
346311827fbSnicm 		/*
347311827fbSnicm 		 * If we have no sessions and clients left, let's get out
348311827fbSnicm 		 * of here...
349311827fbSnicm 		 */
350311827fbSnicm 		n = 0;
351311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
352311827fbSnicm 			if (ARRAY_ITEM(&sessions, i) != NULL)
353311827fbSnicm 				n++;
354311827fbSnicm 		}
355311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
356311827fbSnicm 			if (ARRAY_ITEM(&clients, i) != NULL)
357311827fbSnicm 				n++;
358311827fbSnicm 		}
359311827fbSnicm 		if (n == 0)
360311827fbSnicm 			break;
361311827fbSnicm 	}
362311827fbSnicm 	if (pfds != NULL)
363311827fbSnicm 		xfree(pfds);
364311827fbSnicm 
365311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
366311827fbSnicm 		if (ARRAY_ITEM(&sessions, i) != NULL)
367311827fbSnicm 			session_destroy(ARRAY_ITEM(&sessions, i));
368311827fbSnicm 	}
369311827fbSnicm 	ARRAY_FREE(&sessions);
370311827fbSnicm 
371311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
372311827fbSnicm 		if (ARRAY_ITEM(&clients, i) != NULL)
373311827fbSnicm 			server_lost_client(ARRAY_ITEM(&clients, i));
374311827fbSnicm 	}
375311827fbSnicm 	ARRAY_FREE(&clients);
376311827fbSnicm 
37768895571Snicm 	mode_key_free_trees();
378311827fbSnicm 	key_bindings_free();
379311827fbSnicm 
380311827fbSnicm 	close(srv_fd);
381311827fbSnicm 
382311827fbSnicm 	unlink(socket_path);
383311827fbSnicm 	xfree(socket_path);
384311827fbSnicm 
385eaecedb2Snicm 	options_free(&global_s_options);
386eaecedb2Snicm 	options_free(&global_w_options);
387311827fbSnicm 	if (server_password != NULL)
388311827fbSnicm 		xfree(server_password);
389311827fbSnicm 
390311827fbSnicm 	return (0);
391311827fbSnicm }
392311827fbSnicm 
393311827fbSnicm /* Kill all clients. */
394311827fbSnicm void
395311827fbSnicm server_shutdown(void)
396311827fbSnicm {
397311827fbSnicm 	struct session	*s;
398311827fbSnicm 	struct client	*c;
399311827fbSnicm 	u_int		 i, j;
400311827fbSnicm 
401311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
402311827fbSnicm 		s = ARRAY_ITEM(&sessions, i);
403311827fbSnicm 		for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
404311827fbSnicm 			c = ARRAY_ITEM(&clients, j);
405311827fbSnicm 			if (c != NULL && c->session == s) {
406311827fbSnicm 				s = NULL;
407311827fbSnicm 				break;
408311827fbSnicm 			}
409311827fbSnicm 		}
410311827fbSnicm 		if (s != NULL)
411311827fbSnicm 			session_destroy(s);
412311827fbSnicm 	}
413311827fbSnicm 
414311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
415311827fbSnicm 		c = ARRAY_ITEM(&clients, i);
4160cd9638eSnicm 		if (c != NULL) {
4170cd9638eSnicm 			if (c->flags & CLIENT_BAD)
4180cd9638eSnicm 				server_lost_client(c);
4190cd9638eSnicm 			else
420311827fbSnicm 				server_write_client(c, MSG_SHUTDOWN, NULL, 0);
4210cd9638eSnicm 			c->flags |= CLIENT_BAD;
4220cd9638eSnicm 		}
423311827fbSnicm 	}
424311827fbSnicm }
425311827fbSnicm 
426311827fbSnicm /* Handle SIGCHLD. */
427311827fbSnicm void
428311827fbSnicm server_child_signal(void)
429311827fbSnicm {
430311827fbSnicm 	struct window		*w;
431311827fbSnicm 	struct window_pane	*wp;
432311827fbSnicm 	int		 	 status;
433311827fbSnicm 	pid_t		 	 pid;
434311827fbSnicm 	u_int		 	 i;
435311827fbSnicm 
436311827fbSnicm 	for (;;) {
437311827fbSnicm 		switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
438311827fbSnicm 		case -1:
439311827fbSnicm 			if (errno == ECHILD)
440311827fbSnicm 				return;
441311827fbSnicm 			fatal("waitpid");
442311827fbSnicm 		case 0:
443311827fbSnicm 			return;
444311827fbSnicm 		}
445311827fbSnicm 		if (!WIFSTOPPED(status))
446311827fbSnicm 			continue;
447311827fbSnicm 		if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
448311827fbSnicm 			continue;
449311827fbSnicm 
450311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
451311827fbSnicm 			w = ARRAY_ITEM(&windows, i);
452311827fbSnicm 			if (w == NULL)
453311827fbSnicm 				continue;
454311827fbSnicm 			TAILQ_FOREACH(wp, &w->panes, entry) {
455311827fbSnicm 				if (wp->pid == pid) {
456311827fbSnicm 					if (killpg(pid, SIGCONT) != 0)
457311827fbSnicm 						kill(pid, SIGCONT);
458311827fbSnicm 				}
459311827fbSnicm 			}
460311827fbSnicm 		}
461311827fbSnicm 	}
462311827fbSnicm }
463311827fbSnicm 
464311827fbSnicm /* Fill window pollfds. */
465311827fbSnicm void
466311827fbSnicm server_fill_windows(struct pollfd **pfd)
467311827fbSnicm {
468311827fbSnicm 	struct window		*w;
469311827fbSnicm 	struct window_pane	*wp;
470311827fbSnicm 	u_int		 	 i;
471311827fbSnicm 
472311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
473311827fbSnicm 		w = ARRAY_ITEM(&windows, i);
474311827fbSnicm 		if (w == NULL)
475311827fbSnicm 			continue;
476311827fbSnicm 
477311827fbSnicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
478311827fbSnicm 			(*pfd)->fd = wp->fd;
479311827fbSnicm 			if (wp->fd != -1) {
480311827fbSnicm 				(*pfd)->events = POLLIN;
481311827fbSnicm 				if (BUFFER_USED(wp->out) > 0)
482311827fbSnicm 					(*pfd)->events |= POLLOUT;
483311827fbSnicm 			}
484311827fbSnicm 			(*pfd)++;
485311827fbSnicm 		}
486311827fbSnicm 	}
487311827fbSnicm }
488311827fbSnicm 
489311827fbSnicm /* Handle window pollfds. */
490311827fbSnicm void
491311827fbSnicm server_handle_windows(struct pollfd **pfd)
492311827fbSnicm {
493311827fbSnicm 	struct window		*w;
494311827fbSnicm 	struct window_pane	*wp;
495311827fbSnicm 	u_int		 	 i;
496311827fbSnicm 
497311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
498311827fbSnicm 		w = ARRAY_ITEM(&windows, i);
499311827fbSnicm 		if (w == NULL)
500311827fbSnicm 			continue;
501311827fbSnicm 
502311827fbSnicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
503311827fbSnicm 			if (wp->fd != -1) {
504311827fbSnicm 				if (buffer_poll(*pfd, wp->in, wp->out) != 0) {
505311827fbSnicm 					close(wp->fd);
506311827fbSnicm 					wp->fd = -1;
507311827fbSnicm 				} else
508311827fbSnicm 					server_handle_window(w, wp);
509311827fbSnicm 			}
510311827fbSnicm 			(*pfd)++;
511311827fbSnicm 		}
512311827fbSnicm 
513311827fbSnicm 		server_check_window(w);
514311827fbSnicm 	}
515311827fbSnicm }
516311827fbSnicm 
517311827fbSnicm /* Check for general redraw on client. */
518311827fbSnicm void
519311827fbSnicm server_check_redraw(struct client *c)
520311827fbSnicm {
521311827fbSnicm 	struct session		*s;
522311827fbSnicm 	struct window_pane	*wp;
523311827fbSnicm 	char		 	 title[512];
524311827fbSnicm 	int		 	 flags, redraw;
525311827fbSnicm 
526311827fbSnicm 	if (c == NULL || c->session == NULL)
527311827fbSnicm 		return;
528311827fbSnicm 	s = c->session;
529311827fbSnicm 
530311827fbSnicm 	flags = c->tty.flags & TTY_FREEZE;
531311827fbSnicm 	c->tty.flags &= ~TTY_FREEZE;
532311827fbSnicm 
533311827fbSnicm 	if (options_get_number(&s->options, "set-titles")) {
534311827fbSnicm 		xsnprintf(title, sizeof title, "%s:%u:%s - \"%s\"",
535311827fbSnicm 		    s->name, s->curw->idx, s->curw->window->name,
536311827fbSnicm 		    s->curw->window->active->screen->title);
537311827fbSnicm 		if (c->title == NULL || strcmp(title, c->title) != 0) {
538311827fbSnicm 			if (c->title != NULL)
539311827fbSnicm 				xfree(c->title);
540311827fbSnicm 			c->title = xstrdup(title);
541311827fbSnicm 			tty_set_title(&c->tty, c->title);
542311827fbSnicm 		}
543311827fbSnicm 	}
544311827fbSnicm 
545311827fbSnicm 	if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
546311827fbSnicm 		if (c->message_string != NULL)
547311827fbSnicm 			redraw = status_message_redraw(c);
548311827fbSnicm 		else if (c->prompt_string != NULL)
549311827fbSnicm 			redraw = status_prompt_redraw(c);
550311827fbSnicm 		else
551311827fbSnicm 			redraw = status_redraw(c);
552311827fbSnicm 		if (!redraw)
553311827fbSnicm 			c->flags &= ~CLIENT_STATUS;
554311827fbSnicm 	}
555311827fbSnicm 
556311827fbSnicm 	if (c->flags & CLIENT_REDRAW) {
557311827fbSnicm 		if (server_locked)
558311827fbSnicm 			server_redraw_locked(c);
559311827fbSnicm 		else
56079fc95a8Snicm  			screen_redraw_screen(c, 0);
561311827fbSnicm 		c->flags &= ~CLIENT_STATUS;
562311827fbSnicm 	} else {
563311827fbSnicm 		TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
564311827fbSnicm 			if (wp->flags & PANE_REDRAW)
565311827fbSnicm 				screen_redraw_pane(c, wp);
566311827fbSnicm 		}
567311827fbSnicm 	}
568311827fbSnicm 
569311827fbSnicm 	if (c->flags & CLIENT_STATUS)
57079fc95a8Snicm 		screen_redraw_screen(c, 1);
571311827fbSnicm 
572311827fbSnicm 	c->tty.flags |= flags;
573311827fbSnicm 
574311827fbSnicm 	c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS);
575311827fbSnicm }
576311827fbSnicm 
577311827fbSnicm /* Redraw client when locked. */
578311827fbSnicm void
579311827fbSnicm server_redraw_locked(struct client *c)
580311827fbSnicm {
581311827fbSnicm 	struct screen_write_ctx	ctx;
582311827fbSnicm 	struct screen		screen;
583d658fe3cSnicm 	struct grid_cell	gc;
584311827fbSnicm 	u_int			colour, xx, yy, i;
585311827fbSnicm 	int    			style;
586311827fbSnicm 
587311827fbSnicm 	xx = c->tty.sx;
588311827fbSnicm 	yy = c->tty.sy - 1;
589311827fbSnicm 	if (xx == 0 || yy == 0)
590311827fbSnicm 		return;
591eaecedb2Snicm 	colour = options_get_number(&global_w_options, "clock-mode-colour");
592eaecedb2Snicm 	style = options_get_number(&global_w_options, "clock-mode-style");
593311827fbSnicm 
594d658fe3cSnicm 	memcpy(&gc, &grid_default_cell, sizeof gc);
595d658fe3cSnicm 	gc.fg = colour;
596d658fe3cSnicm 	gc.attr |= GRID_ATTR_BRIGHT;
597d658fe3cSnicm 
598311827fbSnicm 	screen_init(&screen, xx, yy, 0);
599311827fbSnicm 
600311827fbSnicm 	screen_write_start(&ctx, NULL, &screen);
601311827fbSnicm 	clock_draw(&ctx, colour, style);
602d658fe3cSnicm 
603d658fe3cSnicm 	if (password_failures != 0) {
604d658fe3cSnicm 		screen_write_cursormove(&ctx, 0, 0);
605d658fe3cSnicm 		screen_write_puts(
606d658fe3cSnicm 		    &ctx, &gc, "%u failed attempts", password_failures);
607d658fe3cSnicm 	}
608d658fe3cSnicm 
609311827fbSnicm 	screen_write_stop(&ctx);
610311827fbSnicm 
611311827fbSnicm 	for (i = 0; i < screen_size_y(&screen); i++)
612311827fbSnicm 		tty_draw_line(&c->tty, &screen, i, 0, 0);
61379fc95a8Snicm 	screen_redraw_screen(c, 1);
614311827fbSnicm 
615311827fbSnicm 	screen_free(&screen);
616311827fbSnicm }
617311827fbSnicm 
618311827fbSnicm /* Check for timers on client. */
619311827fbSnicm void
620311827fbSnicm server_check_timers(struct client *c)
621311827fbSnicm {
622311827fbSnicm 	struct session	*s;
623311827fbSnicm 	struct timeval	 tv;
624311827fbSnicm 	u_int		 interval;
625311827fbSnicm 
626311827fbSnicm 	if (c == NULL || c->session == NULL)
627311827fbSnicm 		return;
628311827fbSnicm 	s = c->session;
629311827fbSnicm 
630311827fbSnicm 	if (gettimeofday(&tv, NULL) != 0)
631311827fbSnicm 		fatal("gettimeofday");
632311827fbSnicm 
633311827fbSnicm 	if (c->message_string != NULL && timercmp(&tv, &c->message_timer, >))
634311827fbSnicm 		status_message_clear(c);
635311827fbSnicm 
636311827fbSnicm 	if (c->message_string != NULL || c->prompt_string != NULL) {
637311827fbSnicm 		/*
638311827fbSnicm 		 * Don't need timed redraw for messages/prompts so bail now.
639311827fbSnicm 		 * The status timer isn't reset when they are redrawn anyway.
640311827fbSnicm 		 */
641311827fbSnicm 		return;
642311827fbSnicm 	}
643311827fbSnicm 	if (!options_get_number(&s->options, "status"))
644311827fbSnicm 		return;
645311827fbSnicm 
646311827fbSnicm 	/* Check timer; resolution is only a second so don't be too clever. */
647311827fbSnicm 	interval = options_get_number(&s->options, "status-interval");
648311827fbSnicm 	if (interval == 0)
649311827fbSnicm 		return;
650311827fbSnicm 	if (tv.tv_sec < c->status_timer.tv_sec ||
651311827fbSnicm 	    ((u_int) tv.tv_sec) - c->status_timer.tv_sec >= interval)
652311827fbSnicm 		c->flags |= CLIENT_STATUS;
653311827fbSnicm }
654311827fbSnicm 
655311827fbSnicm /* Fill client pollfds. */
656311827fbSnicm void
657311827fbSnicm server_fill_clients(struct pollfd **pfd)
658311827fbSnicm {
659311827fbSnicm 	struct client		*c;
660311827fbSnicm 	struct window		*w;
661311827fbSnicm 	struct window_pane	*wp;
662311827fbSnicm 	u_int		 	 i;
663311827fbSnicm 
664311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
665311827fbSnicm 		c = ARRAY_ITEM(&clients, i);
666311827fbSnicm 
667311827fbSnicm 		server_check_timers(c);
668311827fbSnicm 		server_check_redraw(c);
669311827fbSnicm 
670311827fbSnicm 		if (c == NULL)
671311827fbSnicm 			(*pfd)->fd = -1;
672311827fbSnicm 		else {
673fd234c13Snicm 			(*pfd)->fd = c->ibuf.fd;
6740cd9638eSnicm 			if (!(c->flags & CLIENT_BAD))
675fd234c13Snicm 				(*pfd)->events |= POLLIN;
676fd234c13Snicm 			if (c->ibuf.w.queued > 0)
677311827fbSnicm 				(*pfd)->events |= POLLOUT;
678311827fbSnicm 		}
679311827fbSnicm 		(*pfd)++;
680311827fbSnicm 
681311827fbSnicm 		if (c == NULL || c->flags & CLIENT_SUSPENDED ||
682311827fbSnicm 		    c->tty.fd == -1 || c->session == NULL)
683311827fbSnicm 			(*pfd)->fd = -1;
684311827fbSnicm 		else {
685311827fbSnicm 			(*pfd)->fd = c->tty.fd;
686311827fbSnicm 			(*pfd)->events = POLLIN;
687311827fbSnicm 			if (BUFFER_USED(c->tty.out) > 0)
688311827fbSnicm 				(*pfd)->events |= POLLOUT;
689311827fbSnicm 		}
690311827fbSnicm 		(*pfd)++;
691311827fbSnicm 	}
692311827fbSnicm 
693311827fbSnicm 	/*
694311827fbSnicm 	 * Clear any window redraw flags (will have been redrawn as part of
695311827fbSnicm 	 * client).
696311827fbSnicm 	 */
697311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
698311827fbSnicm 		w = ARRAY_ITEM(&windows, i);
699311827fbSnicm 		if (w == NULL)
700311827fbSnicm 			continue;
701311827fbSnicm 
702311827fbSnicm 		w->flags &= ~WINDOW_REDRAW;
703311827fbSnicm 		TAILQ_FOREACH(wp, &w->panes, entry)
704311827fbSnicm 			wp->flags &= ~PANE_REDRAW;
705311827fbSnicm 	}
706311827fbSnicm }
707311827fbSnicm 
708311827fbSnicm /* Handle client pollfds. */
709311827fbSnicm void
710311827fbSnicm server_handle_clients(struct pollfd **pfd)
711311827fbSnicm {
712311827fbSnicm 	struct client	*c;
713311827fbSnicm 	u_int		 i;
714311827fbSnicm 
715311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
716311827fbSnicm 		c = ARRAY_ITEM(&clients, i);
717311827fbSnicm 
718311827fbSnicm 		if (c != NULL) {
719fd234c13Snicm 			if ((*pfd)->revents & (POLLERR|POLLNVAL|POLLHUP)) {
720311827fbSnicm 				server_lost_client(c);
721311827fbSnicm 				(*pfd) += 2;
722311827fbSnicm 				continue;
723fd234c13Snicm 			}
724fd234c13Snicm 
725fd234c13Snicm 			if ((*pfd)->revents & POLLOUT) {
726fd234c13Snicm 				if (msgbuf_write(&c->ibuf.w) < 0) {
7270cd9638eSnicm 					server_lost_client(c);
7280cd9638eSnicm 					(*pfd) += 2;
7290cd9638eSnicm 					continue;
730fd234c13Snicm 				}
731fd234c13Snicm 			}
732fd234c13Snicm 
733fd234c13Snicm 			if (c->flags & CLIENT_BAD) {
734fd234c13Snicm 				if (c->ibuf.w.queued == 0)
735fd234c13Snicm 					server_lost_client(c);
736fd234c13Snicm 				(*pfd) += 2;
737fd234c13Snicm 				continue;
738fd234c13Snicm 			} else if ((*pfd)->revents & POLLIN) {
739fd234c13Snicm 				if (server_msg_dispatch(c) != 0) {
740fd234c13Snicm 					server_lost_client(c);
741fd234c13Snicm 					(*pfd) += 2;
742fd234c13Snicm 					continue;
743fd234c13Snicm 				}
744fd234c13Snicm 			}
745311827fbSnicm 		}
746311827fbSnicm 		(*pfd)++;
747311827fbSnicm 
748311827fbSnicm 		if (c != NULL && !(c->flags & CLIENT_SUSPENDED) &&
749311827fbSnicm 		    c->tty.fd != -1 && c->session != NULL) {
750311827fbSnicm 			if (buffer_poll(*pfd, c->tty.in, c->tty.out) != 0)
751311827fbSnicm 				server_lost_client(c);
752311827fbSnicm 			else
753311827fbSnicm 				server_handle_client(c);
754311827fbSnicm 		}
755311827fbSnicm 		(*pfd)++;
756311827fbSnicm 	}
757311827fbSnicm }
758311827fbSnicm 
759311827fbSnicm /* accept(2) and create new client. */
760980b7663Snicm void
761311827fbSnicm server_accept_client(int srv_fd)
762311827fbSnicm {
763311827fbSnicm 	struct sockaddr_storage	sa;
764311827fbSnicm 	socklen_t		slen = sizeof sa;
765311827fbSnicm 	int			fd;
766311827fbSnicm 
767311827fbSnicm 	fd = accept(srv_fd, (struct sockaddr *) &sa, &slen);
768311827fbSnicm 	if (fd == -1) {
769311827fbSnicm 		if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
770980b7663Snicm 			return;
771311827fbSnicm 		fatal("accept failed");
772311827fbSnicm 	}
773311827fbSnicm 	if (sigterm) {
774311827fbSnicm 		close(fd);
775980b7663Snicm 		return;
776311827fbSnicm 	}
777980b7663Snicm 	server_create_client(fd);
778311827fbSnicm }
779311827fbSnicm 
780311827fbSnicm /* Input data from client. */
781311827fbSnicm void
782311827fbSnicm server_handle_client(struct client *c)
783311827fbSnicm {
784311827fbSnicm 	struct window_pane	*wp;
785311827fbSnicm 	struct screen		*s;
786311827fbSnicm 	struct timeval	 	 tv;
787311827fbSnicm 	struct key_binding	*bd;
788311827fbSnicm 	int		 	 key, prefix, status, xtimeout;
789311827fbSnicm 	int			 mode;
790311827fbSnicm 	u_char			 mouse[3];
791311827fbSnicm 
792311827fbSnicm 	xtimeout = options_get_number(&c->session->options, "repeat-time");
793311827fbSnicm 	if (xtimeout != 0 && c->flags & CLIENT_REPEAT) {
794311827fbSnicm 		if (gettimeofday(&tv, NULL) != 0)
795311827fbSnicm 			fatal("gettimeofday");
796311827fbSnicm 		if (timercmp(&tv, &c->repeat_timer, >))
797311827fbSnicm 			c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
798311827fbSnicm 	}
799311827fbSnicm 
800311827fbSnicm 	/* Process keys. */
801311827fbSnicm 	prefix = options_get_number(&c->session->options, "prefix");
802311827fbSnicm 	while (tty_keys_next(&c->tty, &key, mouse) == 0) {
803311827fbSnicm 		server_activity = time(NULL);
804311827fbSnicm 
805311827fbSnicm 		if (c->session == NULL)
806311827fbSnicm 			return;
807311827fbSnicm 		wp = c->session->curw->window->active;	/* could die */
808311827fbSnicm 
809311827fbSnicm 		status_message_clear(c);
810311827fbSnicm 		if (c->prompt_string != NULL) {
811311827fbSnicm 			status_prompt_key(c, key);
812311827fbSnicm 			continue;
813311827fbSnicm 		}
814311827fbSnicm 		if (server_locked)
815311827fbSnicm 			continue;
816311827fbSnicm 
817311827fbSnicm 		/* Check for mouse keys. */
818311827fbSnicm 		if (key == KEYC_MOUSE) {
819311827fbSnicm 			window_pane_mouse(wp, c, mouse[0], mouse[1], mouse[2]);
820311827fbSnicm 			continue;
821311827fbSnicm 		}
822311827fbSnicm 
823311827fbSnicm 		/* No previous prefix key. */
824311827fbSnicm 		if (!(c->flags & CLIENT_PREFIX)) {
825311827fbSnicm 			if (key == prefix)
826311827fbSnicm 				c->flags |= CLIENT_PREFIX;
827ad4696b5Snicm 			else {
828ad4696b5Snicm 				/* Try as a non-prefix key binding. */
829ad4696b5Snicm 				if ((bd = key_bindings_lookup(key)) == NULL)
830311827fbSnicm 					window_pane_key(wp, c, key);
831ad4696b5Snicm 				else
832ad4696b5Snicm 					key_bindings_dispatch(bd, c);
833ad4696b5Snicm 			}
834311827fbSnicm 			continue;
835311827fbSnicm 		}
836311827fbSnicm 
837311827fbSnicm 		/* Prefix key already pressed. Reset prefix and lookup key. */
838311827fbSnicm 		c->flags &= ~CLIENT_PREFIX;
839ad4696b5Snicm 		if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) {
840311827fbSnicm 			/* If repeating, treat this as a key, else ignore. */
841311827fbSnicm 			if (c->flags & CLIENT_REPEAT) {
842311827fbSnicm 				c->flags &= ~CLIENT_REPEAT;
843311827fbSnicm 				if (key == prefix)
844311827fbSnicm 					c->flags |= CLIENT_PREFIX;
845311827fbSnicm 				else
846311827fbSnicm 					window_pane_key(wp, c, key);
847311827fbSnicm 			}
848311827fbSnicm 			continue;
849311827fbSnicm 		}
850311827fbSnicm 
851311827fbSnicm 		/* If already repeating, but this key can't repeat, skip it. */
852311827fbSnicm 		if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
853311827fbSnicm 			c->flags &= ~CLIENT_REPEAT;
854311827fbSnicm 			if (key == prefix)
855311827fbSnicm 				c->flags |= CLIENT_PREFIX;
856311827fbSnicm 			else
857311827fbSnicm 				window_pane_key(wp, c, key);
858311827fbSnicm 			continue;
859311827fbSnicm 		}
860311827fbSnicm 
861311827fbSnicm 		/* If this key can repeat, reset the repeat flags and timer. */
862311827fbSnicm 		if (xtimeout != 0 && bd->can_repeat) {
863311827fbSnicm 			c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
864311827fbSnicm 
865311827fbSnicm 			tv.tv_sec = xtimeout / 1000;
866311827fbSnicm 			tv.tv_usec = (xtimeout % 1000) * 1000L;
867311827fbSnicm 			if (gettimeofday(&c->repeat_timer, NULL) != 0)
868311827fbSnicm 				fatal("gettimeofday");
869311827fbSnicm 			timeradd(&c->repeat_timer, &tv, &c->repeat_timer);
870311827fbSnicm 		}
871311827fbSnicm 
872311827fbSnicm 		/* Dispatch the command. */
873311827fbSnicm 		key_bindings_dispatch(bd, c);
874311827fbSnicm 	}
875311827fbSnicm 	if (c->session == NULL)
876311827fbSnicm 		return;
877311827fbSnicm 	wp = c->session->curw->window->active;	/* could die - do each loop */
878311827fbSnicm 	s = wp->screen;
879311827fbSnicm 
880311827fbSnicm 	/* Ensure cursor position and mode settings. */
881311827fbSnicm 	status = options_get_number(&c->session->options, "status");
882dde47eafSnicm 	tty_region(&c->tty, 0, c->tty.sy - 1, 0);
883af9e4c5dSnicm 	if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
884af9e4c5dSnicm 		tty_cursor(&c->tty, 0, 0, 0, 0);
885af9e4c5dSnicm 	else
886311827fbSnicm 		tty_cursor(&c->tty, s->cx, s->cy, wp->xoff, wp->yoff);
887311827fbSnicm 
888311827fbSnicm 	mode = s->mode;
889311827fbSnicm 	if (server_locked)
890311827fbSnicm 		mode &= ~TTY_NOCURSOR;
891311827fbSnicm 	tty_update_mode(&c->tty, mode);
892311827fbSnicm }
893311827fbSnicm 
894311827fbSnicm /* Lost a client. */
895311827fbSnicm void
896311827fbSnicm server_lost_client(struct client *c)
897311827fbSnicm {
898311827fbSnicm 	u_int	i;
899311827fbSnicm 
900311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
901311827fbSnicm 		if (ARRAY_ITEM(&clients, i) == c)
902311827fbSnicm 			ARRAY_SET(&clients, i, NULL);
903311827fbSnicm 	}
904311827fbSnicm 
905*130af69dSnicm 	tty_free(&c->tty);
906311827fbSnicm 
907311827fbSnicm 	screen_free(&c->status);
908311827fbSnicm 
909311827fbSnicm 	if (c->title != NULL)
910311827fbSnicm 		xfree(c->title);
911311827fbSnicm 
912311827fbSnicm 	if (c->message_string != NULL)
913311827fbSnicm 		xfree(c->message_string);
914311827fbSnicm 
915311827fbSnicm 	if (c->prompt_string != NULL)
916311827fbSnicm 		xfree(c->prompt_string);
917311827fbSnicm 	if (c->prompt_buffer != NULL)
918311827fbSnicm 		xfree(c->prompt_buffer);
919311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++)
920311827fbSnicm 		xfree(ARRAY_ITEM(&c->prompt_hdata, i));
921311827fbSnicm 	ARRAY_FREE(&c->prompt_hdata);
922311827fbSnicm 
923311827fbSnicm 	if (c->cwd != NULL)
924311827fbSnicm 		xfree(c->cwd);
925311827fbSnicm 
926fd234c13Snicm 	close(c->ibuf.fd);
927fd234c13Snicm 	imsg_clear(&c->ibuf);
928311827fbSnicm 	xfree(c);
929311827fbSnicm 
930311827fbSnicm 	recalculate_sizes();
931311827fbSnicm }
932311827fbSnicm 
933311827fbSnicm /* Handle window data. */
934311827fbSnicm void
935311827fbSnicm server_handle_window(struct window *w, struct window_pane *wp)
936311827fbSnicm {
937311827fbSnicm 	struct session	*s;
938311827fbSnicm 	u_int		 i;
939311827fbSnicm 	int		 update;
940311827fbSnicm 
941311827fbSnicm 	window_pane_parse(wp);
942311827fbSnicm 
943311827fbSnicm 	if ((w->flags & (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT)) == 0)
944311827fbSnicm 		return;
945311827fbSnicm 
946311827fbSnicm 	update = 0;
947311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
948311827fbSnicm 		s = ARRAY_ITEM(&sessions, i);
949311827fbSnicm 		if (s == NULL || !session_has(s, w))
950311827fbSnicm 			continue;
951311827fbSnicm 
9520903c7b9Snicm 		update += server_check_window_bell(s, w);
953311827fbSnicm 		update += server_check_window_activity(s, w);
954311827fbSnicm 		update += server_check_window_content(s, w, wp);
955311827fbSnicm 	}
956311827fbSnicm 	if (update)
957311827fbSnicm 		server_status_window(w);
958311827fbSnicm 
959311827fbSnicm 	w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT);
960311827fbSnicm }
961311827fbSnicm 
962311827fbSnicm int
9630903c7b9Snicm server_check_window_bell(struct session *s, struct window *w)
964311827fbSnicm {
965311827fbSnicm 	struct client	*c;
966311827fbSnicm 	u_int		 i;
9670903c7b9Snicm 	int		 action, visual;
968311827fbSnicm 
969311827fbSnicm 	if (!(w->flags & WINDOW_BELL))
970311827fbSnicm 		return (0);
971311827fbSnicm 	if (session_alert_has_window(s, w, WINDOW_BELL))
972311827fbSnicm 		return (0);
973311827fbSnicm 	session_alert_add(s, w, WINDOW_BELL);
974311827fbSnicm 
975311827fbSnicm 	action = options_get_number(&s->options, "bell-action");
976311827fbSnicm 	switch (action) {
977311827fbSnicm 	case BELL_ANY:
978311827fbSnicm 		if (s->flags & SESSION_UNATTACHED)
979311827fbSnicm 			break;
9800903c7b9Snicm 		visual = options_get_number(&s->options, "visual-bell");
981311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
982311827fbSnicm 			c = ARRAY_ITEM(&clients, i);
9830903c7b9Snicm 			if (c == NULL || c->session != s)
9840903c7b9Snicm 				continue;
9850903c7b9Snicm 			if (!visual) {
986311827fbSnicm 				tty_putcode(&c->tty, TTYC_BEL);
9870903c7b9Snicm 				continue;
9880903c7b9Snicm 			}
9890903c7b9Snicm  			if (c->session->curw->window == w) {
9900903c7b9Snicm 				status_message_set(c, "Bell in current window");
9910903c7b9Snicm 				continue;
9920903c7b9Snicm 			}
9930903c7b9Snicm 			status_message_set(c, "Bell in window %u",
9940903c7b9Snicm 			    winlink_find_by_window(&s->windows, w)->idx);
995311827fbSnicm 		}
996311827fbSnicm 		break;
997311827fbSnicm 	case BELL_CURRENT:
9980903c7b9Snicm 		if (s->flags & SESSION_UNATTACHED)
999311827fbSnicm 			break;
10000903c7b9Snicm 		visual = options_get_number(&s->options, "visual-bell");
1001311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1002311827fbSnicm 			c = ARRAY_ITEM(&clients, i);
10030903c7b9Snicm 			if (c == NULL || c->session != s)
10040903c7b9Snicm 				continue;
10050903c7b9Snicm  			if (c->session->curw->window != w)
10060903c7b9Snicm 				continue;
10070903c7b9Snicm 			if (!visual) {
1008311827fbSnicm 				tty_putcode(&c->tty, TTYC_BEL);
10090903c7b9Snicm 				continue;
10100903c7b9Snicm 			}
10110903c7b9Snicm 			status_message_set(c, "Bell in current window");
1012311827fbSnicm 		}
1013311827fbSnicm 		break;
1014311827fbSnicm 	}
1015311827fbSnicm 	return (1);
1016311827fbSnicm }
1017311827fbSnicm 
1018311827fbSnicm int
1019311827fbSnicm server_check_window_activity(struct session *s, struct window *w)
1020311827fbSnicm {
10210903c7b9Snicm 	struct client	*c;
10220903c7b9Snicm 	u_int		 i;
10230903c7b9Snicm 
1024311827fbSnicm 	if (!(w->flags & WINDOW_ACTIVITY))
1025311827fbSnicm 		return (0);
10260903c7b9Snicm 
1027311827fbSnicm 	if (!options_get_number(&w->options, "monitor-activity"))
1028311827fbSnicm 		return (0);
10290903c7b9Snicm 
1030311827fbSnicm 	if (session_alert_has_window(s, w, WINDOW_ACTIVITY))
1031311827fbSnicm 		return (0);
10320903c7b9Snicm 	if (s->curw->window == w)
10330903c7b9Snicm 		return (0);
10340903c7b9Snicm 
1035311827fbSnicm 	session_alert_add(s, w, WINDOW_ACTIVITY);
10360903c7b9Snicm 	if (s->flags & SESSION_UNATTACHED)
10370903c7b9Snicm 		return (0);
10380903c7b9Snicm  	if (options_get_number(&s->options, "visual-activity")) {
10390903c7b9Snicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
10400903c7b9Snicm 			c = ARRAY_ITEM(&clients, i);
10410903c7b9Snicm 			if (c == NULL || c->session != s)
10420903c7b9Snicm 				continue;
10430903c7b9Snicm 			status_message_set(c, "Activity in window %u",
10440903c7b9Snicm 			    winlink_find_by_window(&s->windows, w)->idx);
10450903c7b9Snicm 		}
10460903c7b9Snicm 	}
10470903c7b9Snicm 
1048311827fbSnicm 	return (1);
1049311827fbSnicm }
1050311827fbSnicm 
1051311827fbSnicm int
1052311827fbSnicm server_check_window_content(
1053311827fbSnicm     struct session *s, struct window *w, struct window_pane *wp)
1054311827fbSnicm {
10550903c7b9Snicm 	struct client	*c;
10560903c7b9Snicm 	u_int		 i;
1057311827fbSnicm 	char		*found, *ptr;
1058311827fbSnicm 
10590903c7b9Snicm 	if (!(w->flags & WINDOW_ACTIVITY))	/* activity for new content */
1060311827fbSnicm 		return (0);
10610903c7b9Snicm 
10620903c7b9Snicm 	ptr = options_get_string(&w->options, "monitor-content");
10630903c7b9Snicm 	if (ptr == NULL || *ptr == '\0')
1064311827fbSnicm 		return (0);
10650903c7b9Snicm 
1066311827fbSnicm 	if (session_alert_has_window(s, w, WINDOW_CONTENT))
1067311827fbSnicm 		return (0);
10680903c7b9Snicm 	if (s->curw->window == w)
10690903c7b9Snicm 		return (0);
10700903c7b9Snicm 
1071570a3589Snicm 	if ((found = window_pane_search(wp, ptr, NULL)) == NULL)
1072311827fbSnicm 		return (0);
1073311827fbSnicm     	xfree(found);
10740903c7b9Snicm 
10750903c7b9Snicm 	session_alert_add(s, w, WINDOW_CONTENT);
10760903c7b9Snicm 	if (s->flags & SESSION_UNATTACHED)
10770903c7b9Snicm 		return (0);
10780903c7b9Snicm  	if (options_get_number(&s->options, "visual-content")) {
10790903c7b9Snicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
10800903c7b9Snicm 			c = ARRAY_ITEM(&clients, i);
10810903c7b9Snicm 			if (c == NULL || c->session != s)
10820903c7b9Snicm 				continue;
10830903c7b9Snicm 			status_message_set(c, "Content in window %u",
10840903c7b9Snicm 			    winlink_find_by_window(&s->windows, w)->idx);
10850903c7b9Snicm 		}
10860903c7b9Snicm 	}
10870903c7b9Snicm 
1088311827fbSnicm 	return (1);
1089311827fbSnicm }
1090311827fbSnicm 
1091c752cdacSnicm /* Check if window still exists. */
1092311827fbSnicm void
1093311827fbSnicm server_check_window(struct window *w)
1094311827fbSnicm {
1095311827fbSnicm 	struct window_pane	*wp, *wq;
1096311827fbSnicm 	struct client		*c;
1097311827fbSnicm 	struct session		*s;
1098311827fbSnicm 	struct winlink		*wl;
1099311827fbSnicm 	u_int		 	 i, j;
1100311827fbSnicm 	int		 	 destroyed, flag;
1101311827fbSnicm 
1102311827fbSnicm 	flag = options_get_number(&w->options, "remain-on-exit");
1103311827fbSnicm 
1104311827fbSnicm 	destroyed = 1;
1105311827fbSnicm 
1106311827fbSnicm 	wp = TAILQ_FIRST(&w->panes);
1107311827fbSnicm 	while (wp != NULL) {
1108311827fbSnicm 		wq = TAILQ_NEXT(wp, entry);
1109c752cdacSnicm 		/*
1110c752cdacSnicm 		 * If the pane has died and the remain-on-exit flag is not set,
1111c752cdacSnicm 		 * remove the pane; otherwise, if the flag is set, don't allow
1112c752cdacSnicm 		 * the window to be destroyed (or it'll close when the last
1113c752cdacSnicm 		 * pane dies).
1114c752cdacSnicm 		 */
1115c752cdacSnicm 		if (wp->fd == -1 && !flag) {
1116af9e4c5dSnicm 			layout_close_pane(wp);
1117311827fbSnicm 			window_remove_pane(w, wp);
1118311827fbSnicm 			server_redraw_window(w);
1119c752cdacSnicm 		} else
1120c752cdacSnicm 			destroyed = 0;
1121311827fbSnicm 		wp = wq;
1122311827fbSnicm 	}
1123311827fbSnicm 
1124311827fbSnicm 	if (!destroyed)
1125311827fbSnicm 		return;
1126311827fbSnicm 
1127311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1128311827fbSnicm 		s = ARRAY_ITEM(&sessions, i);
1129311827fbSnicm 		if (s == NULL)
1130311827fbSnicm 			continue;
1131311827fbSnicm 		if (!session_has(s, w))
1132311827fbSnicm 			continue;
1133311827fbSnicm 
1134311827fbSnicm 	restart:
1135311827fbSnicm 		/* Detach window and either redraw or kill clients. */
1136311827fbSnicm 		RB_FOREACH(wl, winlinks, &s->windows) {
1137311827fbSnicm 			if (wl->window != w)
1138311827fbSnicm 				continue;
1139311827fbSnicm 			destroyed = session_detach(s, wl);
1140311827fbSnicm 			for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
1141311827fbSnicm 				c = ARRAY_ITEM(&clients, j);
1142311827fbSnicm 				if (c == NULL || c->session != s)
1143311827fbSnicm 					continue;
1144311827fbSnicm 				if (!destroyed) {
1145311827fbSnicm 					server_redraw_client(c);
1146311827fbSnicm 					continue;
1147311827fbSnicm 				}
1148311827fbSnicm 				c->session = NULL;
1149311827fbSnicm 				server_write_client(c, MSG_EXIT, NULL, 0);
1150311827fbSnicm 			}
1151311827fbSnicm 			/* If the session was destroyed, bail now. */
1152311827fbSnicm 			if (destroyed)
1153311827fbSnicm 				break;
1154311827fbSnicm 			goto restart;
1155311827fbSnicm 		}
1156311827fbSnicm 	}
1157311827fbSnicm 
1158311827fbSnicm 	recalculate_sizes();
1159311827fbSnicm }
1160311827fbSnicm 
1161311827fbSnicm /* Call any once-per-second timers. */
1162311827fbSnicm void
1163311827fbSnicm server_second_timers(void)
1164311827fbSnicm {
1165311827fbSnicm 	struct window		*w;
1166311827fbSnicm 	struct window_pane	*wp;
1167311827fbSnicm 	u_int		 	 i;
1168311827fbSnicm 	int			 xtimeout;
1169311827fbSnicm 	struct tm	 	 now, then;
1170311827fbSnicm 	static time_t	 	 last_t = 0;
1171311827fbSnicm 	time_t		 	 t;
1172311827fbSnicm 
1173311827fbSnicm 	t = time(NULL);
1174eaecedb2Snicm 	xtimeout = options_get_number(&global_s_options, "lock-after-time");
1175311827fbSnicm 	if (xtimeout > 0 && t > server_activity + xtimeout)
1176311827fbSnicm 		server_lock();
1177311827fbSnicm 
1178311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
1179311827fbSnicm 		w = ARRAY_ITEM(&windows, i);
1180311827fbSnicm 		if (w == NULL)
1181311827fbSnicm 			continue;
1182311827fbSnicm 
1183311827fbSnicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
1184311827fbSnicm 			if (wp->mode != NULL && wp->mode->timer != NULL)
1185311827fbSnicm 				wp->mode->timer(wp);
1186311827fbSnicm 		}
1187311827fbSnicm 	}
1188311827fbSnicm 
1189311827fbSnicm 	/* Check for a minute having passed. */
1190311827fbSnicm 	gmtime_r(&t, &now);
1191311827fbSnicm 	gmtime_r(&last_t, &then);
1192311827fbSnicm 	if (now.tm_min == then.tm_min)
1193311827fbSnicm 		return;
1194311827fbSnicm 	last_t = t;
1195311827fbSnicm 
1196311827fbSnicm 	/* If locked, redraw all clients. */
1197311827fbSnicm 	if (server_locked) {
1198311827fbSnicm 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1199311827fbSnicm 			if (ARRAY_ITEM(&clients, i) != NULL)
1200311827fbSnicm 				server_redraw_client(ARRAY_ITEM(&clients, i));
1201311827fbSnicm 		}
1202311827fbSnicm 	}
1203311827fbSnicm }
1204311827fbSnicm 
1205311827fbSnicm /* Update socket execute permissions based on whether sessions are attached. */
1206311827fbSnicm int
1207311827fbSnicm server_update_socket(void)
1208311827fbSnicm {
1209311827fbSnicm 	struct session	*s;
1210311827fbSnicm 	u_int		 i;
1211311827fbSnicm 	static int	 last = -1;
1212311827fbSnicm 	int		 n;
1213311827fbSnicm 
1214311827fbSnicm 	n = 0;
1215311827fbSnicm 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1216311827fbSnicm 		s = ARRAY_ITEM(&sessions, i);
1217311827fbSnicm 		if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
1218311827fbSnicm 			n++;
1219311827fbSnicm 			break;
1220311827fbSnicm 		}
1221311827fbSnicm 	}
1222311827fbSnicm 
1223311827fbSnicm 	if (n != last) {
1224311827fbSnicm 		last = n;
1225311827fbSnicm 		if (n != 0)
1226311827fbSnicm 			chmod(socket_path, S_IRWXU);
1227311827fbSnicm 		else
1228311827fbSnicm 			chmod(socket_path, S_IRUSR|S_IWUSR);
1229311827fbSnicm 	}
1230311827fbSnicm 
1231311827fbSnicm 	return (n);
1232311827fbSnicm }
1233