xref: /minix3/external/bsd/tmux/dist/server.c (revision 27852ebe53d5bf221cf5058cb7e858fa8fa8895e)
1*0a6a1f1dSLionel Sambuc /* Id */
2eda6f593SDavid van Moolenbroek 
3eda6f593SDavid van Moolenbroek /*
4eda6f593SDavid van Moolenbroek  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5eda6f593SDavid van Moolenbroek  *
6eda6f593SDavid van Moolenbroek  * Permission to use, copy, modify, and distribute this software for any
7eda6f593SDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
8eda6f593SDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
9eda6f593SDavid van Moolenbroek  *
10eda6f593SDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11eda6f593SDavid van Moolenbroek  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12eda6f593SDavid van Moolenbroek  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13eda6f593SDavid van Moolenbroek  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14eda6f593SDavid van Moolenbroek  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15eda6f593SDavid van Moolenbroek  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16eda6f593SDavid van Moolenbroek  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17eda6f593SDavid van Moolenbroek  */
18eda6f593SDavid van Moolenbroek 
19eda6f593SDavid van Moolenbroek #include <sys/types.h>
20eda6f593SDavid van Moolenbroek #include <sys/ioctl.h>
21eda6f593SDavid van Moolenbroek #include <sys/socket.h>
22eda6f593SDavid van Moolenbroek #include <sys/stat.h>
23eda6f593SDavid van Moolenbroek #include <sys/un.h>
24eda6f593SDavid van Moolenbroek #include <sys/wait.h>
25eda6f593SDavid van Moolenbroek 
26eda6f593SDavid van Moolenbroek #include <errno.h>
27eda6f593SDavid van Moolenbroek #include <event.h>
28eda6f593SDavid van Moolenbroek #include <fcntl.h>
29eda6f593SDavid van Moolenbroek #include <signal.h>
30eda6f593SDavid van Moolenbroek #include <stdio.h>
31eda6f593SDavid van Moolenbroek #include <stdlib.h>
32eda6f593SDavid van Moolenbroek #include <string.h>
33eda6f593SDavid van Moolenbroek #include <syslog.h>
34eda6f593SDavid van Moolenbroek #include <termios.h>
35eda6f593SDavid van Moolenbroek #include <time.h>
36eda6f593SDavid van Moolenbroek #include <unistd.h>
37eda6f593SDavid van Moolenbroek 
38eda6f593SDavid van Moolenbroek #include "tmux.h"
39eda6f593SDavid van Moolenbroek 
40eda6f593SDavid van Moolenbroek /*
41eda6f593SDavid van Moolenbroek  * Main server functions.
42eda6f593SDavid van Moolenbroek  */
43eda6f593SDavid van Moolenbroek 
44eda6f593SDavid van Moolenbroek /* Client list. */
45eda6f593SDavid van Moolenbroek struct clients	 clients;
46eda6f593SDavid van Moolenbroek struct clients	 dead_clients;
47eda6f593SDavid van Moolenbroek 
48eda6f593SDavid van Moolenbroek int		 server_fd;
49eda6f593SDavid van Moolenbroek int		 server_shutdown;
50eda6f593SDavid van Moolenbroek struct event	 server_ev_accept;
51eda6f593SDavid van Moolenbroek struct event	 server_ev_second;
52eda6f593SDavid van Moolenbroek 
53eda6f593SDavid van Moolenbroek struct paste_stack global_buffers;
54eda6f593SDavid van Moolenbroek 
55eda6f593SDavid van Moolenbroek int		 server_create_socket(void);
56eda6f593SDavid van Moolenbroek void		 server_loop(void);
57eda6f593SDavid van Moolenbroek int		 server_should_shutdown(void);
58eda6f593SDavid van Moolenbroek void		 server_send_shutdown(void);
59eda6f593SDavid van Moolenbroek void		 server_clean_dead(void);
60eda6f593SDavid van Moolenbroek void		 server_accept_callback(int, short, void *);
61eda6f593SDavid van Moolenbroek void		 server_signal_callback(int, short, void *);
62eda6f593SDavid van Moolenbroek void		 server_child_signal(void);
63eda6f593SDavid van Moolenbroek void		 server_child_exited(pid_t, int);
64eda6f593SDavid van Moolenbroek void		 server_child_stopped(pid_t, int);
65eda6f593SDavid van Moolenbroek void		 server_second_callback(int, short, void *);
66eda6f593SDavid van Moolenbroek void		 server_lock_server(void);
67eda6f593SDavid van Moolenbroek void		 server_lock_sessions(void);
68eda6f593SDavid van Moolenbroek 
69eda6f593SDavid van Moolenbroek /* Create server socket. */
70eda6f593SDavid van Moolenbroek int
server_create_socket(void)71eda6f593SDavid van Moolenbroek server_create_socket(void)
72eda6f593SDavid van Moolenbroek {
73eda6f593SDavid van Moolenbroek 	struct sockaddr_un	sa;
74eda6f593SDavid van Moolenbroek 	size_t			size;
75eda6f593SDavid van Moolenbroek 	mode_t			mask;
76eda6f593SDavid van Moolenbroek 	int			fd;
77eda6f593SDavid van Moolenbroek 
78eda6f593SDavid van Moolenbroek 	memset(&sa, 0, sizeof sa);
79eda6f593SDavid van Moolenbroek 	sa.sun_family = AF_UNIX;
80eda6f593SDavid van Moolenbroek 	size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
81eda6f593SDavid van Moolenbroek 	if (size >= sizeof sa.sun_path) {
82eda6f593SDavid van Moolenbroek 		errno = ENAMETOOLONG;
83eda6f593SDavid van Moolenbroek 		fatal("socket failed");
84eda6f593SDavid van Moolenbroek 	}
85eda6f593SDavid van Moolenbroek 	unlink(sa.sun_path);
86eda6f593SDavid van Moolenbroek 
87eda6f593SDavid van Moolenbroek 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
88eda6f593SDavid van Moolenbroek 		fatal("socket failed");
89eda6f593SDavid van Moolenbroek 
90eda6f593SDavid van Moolenbroek 	mask = umask(S_IXUSR|S_IXGRP|S_IRWXO);
91eda6f593SDavid van Moolenbroek 	if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1)
92eda6f593SDavid van Moolenbroek 		fatal("bind failed");
93eda6f593SDavid van Moolenbroek 	umask(mask);
94eda6f593SDavid van Moolenbroek 
95eda6f593SDavid van Moolenbroek 	if (listen(fd, 16) == -1)
96eda6f593SDavid van Moolenbroek 		fatal("listen failed");
97eda6f593SDavid van Moolenbroek 	setblocking(fd, 0);
98eda6f593SDavid van Moolenbroek 
99eda6f593SDavid van Moolenbroek 	server_update_socket();
100eda6f593SDavid van Moolenbroek 
101eda6f593SDavid van Moolenbroek 	return (fd);
102eda6f593SDavid van Moolenbroek }
103eda6f593SDavid van Moolenbroek 
104eda6f593SDavid van Moolenbroek /* Fork new server. */
105eda6f593SDavid van Moolenbroek int
server_start(int lockfd,char * lockfile)106*0a6a1f1dSLionel Sambuc server_start(int lockfd, char *lockfile)
107eda6f593SDavid van Moolenbroek {
108eda6f593SDavid van Moolenbroek 	int	 	 pair[2];
109eda6f593SDavid van Moolenbroek 	struct timeval	 tv;
110*0a6a1f1dSLionel Sambuc 	char		*cause;
111eda6f593SDavid van Moolenbroek 
112eda6f593SDavid van Moolenbroek 	/* The first client is special and gets a socketpair; create it. */
113eda6f593SDavid van Moolenbroek 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
114eda6f593SDavid van Moolenbroek 		fatal("socketpair failed");
115eda6f593SDavid van Moolenbroek 
116eda6f593SDavid van Moolenbroek 	switch (fork()) {
117eda6f593SDavid van Moolenbroek 	case -1:
118eda6f593SDavid van Moolenbroek 		fatal("fork failed");
119eda6f593SDavid van Moolenbroek 	case 0:
120eda6f593SDavid van Moolenbroek 		break;
121eda6f593SDavid van Moolenbroek 	default:
122eda6f593SDavid van Moolenbroek 		close(pair[1]);
123eda6f593SDavid van Moolenbroek 		return (pair[0]);
124eda6f593SDavid van Moolenbroek 	}
125eda6f593SDavid van Moolenbroek 	close(pair[0]);
126eda6f593SDavid van Moolenbroek 
127eda6f593SDavid van Moolenbroek 	/*
128eda6f593SDavid van Moolenbroek 	 * Must daemonise before loading configuration as the PID changes so
129eda6f593SDavid van Moolenbroek 	 * $TMUX would be wrong for sessions created in the config file.
130eda6f593SDavid van Moolenbroek 	 */
131eda6f593SDavid van Moolenbroek 	if (daemon(1, 0) != 0)
132eda6f593SDavid van Moolenbroek 		fatal("daemon failed");
133eda6f593SDavid van Moolenbroek 
134eda6f593SDavid van Moolenbroek 	/* event_init() was called in our parent, need to reinit. */
135eda6f593SDavid van Moolenbroek 	if (event_reinit(ev_base) != 0)
136eda6f593SDavid van Moolenbroek 		fatal("event_reinit failed");
137eda6f593SDavid van Moolenbroek 	clear_signals(0);
138eda6f593SDavid van Moolenbroek 
139eda6f593SDavid van Moolenbroek 	logfile("server");
140eda6f593SDavid van Moolenbroek 	log_debug("server started, pid %ld", (long) getpid());
141eda6f593SDavid van Moolenbroek 
142eda6f593SDavid van Moolenbroek 	ARRAY_INIT(&windows);
143eda6f593SDavid van Moolenbroek 	RB_INIT(&all_window_panes);
144eda6f593SDavid van Moolenbroek 	ARRAY_INIT(&clients);
145eda6f593SDavid van Moolenbroek 	ARRAY_INIT(&dead_clients);
146eda6f593SDavid van Moolenbroek 	RB_INIT(&sessions);
147eda6f593SDavid van Moolenbroek 	RB_INIT(&dead_sessions);
148eda6f593SDavid van Moolenbroek 	TAILQ_INIT(&session_groups);
149eda6f593SDavid van Moolenbroek 	ARRAY_INIT(&global_buffers);
150eda6f593SDavid van Moolenbroek 	mode_key_init_trees();
151eda6f593SDavid van Moolenbroek 	key_bindings_init();
152eda6f593SDavid van Moolenbroek 	utf8_build();
153eda6f593SDavid van Moolenbroek 
154eda6f593SDavid van Moolenbroek 	start_time = time(NULL);
155eda6f593SDavid van Moolenbroek 	log_debug("socket path %s", socket_path);
156eda6f593SDavid van Moolenbroek #ifdef HAVE_SETPROCTITLE
157eda6f593SDavid van Moolenbroek 	setproctitle("server (%s)", socket_path);
158eda6f593SDavid van Moolenbroek #endif
159eda6f593SDavid van Moolenbroek 
160eda6f593SDavid van Moolenbroek 	server_fd = server_create_socket();
161eda6f593SDavid van Moolenbroek 	server_client_create(pair[1]);
162eda6f593SDavid van Moolenbroek 
163*0a6a1f1dSLionel Sambuc 	unlink(lockfile);
164*0a6a1f1dSLionel Sambuc 	free(lockfile);
165*0a6a1f1dSLionel Sambuc 	close(lockfd);
166eda6f593SDavid van Moolenbroek 
167*0a6a1f1dSLionel Sambuc 	cfg_cmd_q = cmdq_new(NULL);
168*0a6a1f1dSLionel Sambuc 	cfg_cmd_q->emptyfn = cfg_default_done;
169*0a6a1f1dSLionel Sambuc 	cfg_finished = 0;
170*0a6a1f1dSLionel Sambuc 	cfg_references = 1;
171*0a6a1f1dSLionel Sambuc 	ARRAY_INIT(&cfg_causes);
172*0a6a1f1dSLionel Sambuc 	cfg_client = ARRAY_FIRST(&clients);
173*0a6a1f1dSLionel Sambuc 	if (cfg_client != NULL)
174*0a6a1f1dSLionel Sambuc 		cfg_client->references++;
175eda6f593SDavid van Moolenbroek 
176*0a6a1f1dSLionel Sambuc 	if (access(TMUX_CONF, R_OK) == 0) {
177*0a6a1f1dSLionel Sambuc 		if (load_cfg(TMUX_CONF, cfg_cmd_q, &cause) == -1) {
178*0a6a1f1dSLionel Sambuc 			xasprintf(&cause, "%s: %s", TMUX_CONF, cause);
179*0a6a1f1dSLionel Sambuc 			ARRAY_ADD(&cfg_causes, cause);
180*0a6a1f1dSLionel Sambuc 		}
181*0a6a1f1dSLionel Sambuc 	} else if (errno != ENOENT) {
182*0a6a1f1dSLionel Sambuc 		xasprintf(&cause, "%s: %s", TMUX_CONF, strerror(errno));
183*0a6a1f1dSLionel Sambuc 		ARRAY_ADD(&cfg_causes, cause);
184*0a6a1f1dSLionel Sambuc 	}
185*0a6a1f1dSLionel Sambuc 	if (cfg_file != NULL) {
186*0a6a1f1dSLionel Sambuc 		if (load_cfg(cfg_file, cfg_cmd_q, &cause) == -1) {
187*0a6a1f1dSLionel Sambuc 			xasprintf(&cause, "%s: %s", cfg_file, cause);
188*0a6a1f1dSLionel Sambuc 			ARRAY_ADD(&cfg_causes, cause);
189*0a6a1f1dSLionel Sambuc 		}
190*0a6a1f1dSLionel Sambuc 	}
191*0a6a1f1dSLionel Sambuc 	cmdq_continue(cfg_cmd_q);
192*0a6a1f1dSLionel Sambuc 
193*0a6a1f1dSLionel Sambuc 	server_add_accept(0);
194eda6f593SDavid van Moolenbroek 
195eda6f593SDavid van Moolenbroek 	memset(&tv, 0, sizeof tv);
196eda6f593SDavid van Moolenbroek 	tv.tv_sec = 1;
197eda6f593SDavid van Moolenbroek 	evtimer_set(&server_ev_second, server_second_callback, NULL);
198eda6f593SDavid van Moolenbroek 	evtimer_add(&server_ev_second, &tv);
199eda6f593SDavid van Moolenbroek 
200eda6f593SDavid van Moolenbroek 	set_signals(server_signal_callback);
201eda6f593SDavid van Moolenbroek 	server_loop();
202eda6f593SDavid van Moolenbroek 	exit(0);
203eda6f593SDavid van Moolenbroek }
204eda6f593SDavid van Moolenbroek 
205eda6f593SDavid van Moolenbroek /* Main server loop. */
206eda6f593SDavid van Moolenbroek void
server_loop(void)207eda6f593SDavid van Moolenbroek server_loop(void)
208eda6f593SDavid van Moolenbroek {
209eda6f593SDavid van Moolenbroek 	while (!server_should_shutdown()) {
210eda6f593SDavid van Moolenbroek 		event_loop(EVLOOP_ONCE);
211eda6f593SDavid van Moolenbroek 
212eda6f593SDavid van Moolenbroek 		server_window_loop();
213eda6f593SDavid van Moolenbroek 		server_client_loop();
214eda6f593SDavid van Moolenbroek 
215eda6f593SDavid van Moolenbroek 		key_bindings_clean();
216eda6f593SDavid van Moolenbroek 		server_clean_dead();
217eda6f593SDavid van Moolenbroek 	}
218eda6f593SDavid van Moolenbroek }
219eda6f593SDavid van Moolenbroek 
220eda6f593SDavid van Moolenbroek /* Check if the server should be shutting down (no more clients or sessions). */
221eda6f593SDavid van Moolenbroek int
server_should_shutdown(void)222eda6f593SDavid van Moolenbroek server_should_shutdown(void)
223eda6f593SDavid van Moolenbroek {
224eda6f593SDavid van Moolenbroek 	u_int	i;
225eda6f593SDavid van Moolenbroek 
226eda6f593SDavid van Moolenbroek 	if (!options_get_number(&global_options, "exit-unattached")) {
227eda6f593SDavid van Moolenbroek 		if (!RB_EMPTY(&sessions))
228eda6f593SDavid van Moolenbroek 			return (0);
229eda6f593SDavid van Moolenbroek 	}
230eda6f593SDavid van Moolenbroek 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
231eda6f593SDavid van Moolenbroek 		if (ARRAY_ITEM(&clients, i) != NULL)
232eda6f593SDavid van Moolenbroek 			return (0);
233eda6f593SDavid van Moolenbroek 	}
234eda6f593SDavid van Moolenbroek 	return (1);
235eda6f593SDavid van Moolenbroek }
236eda6f593SDavid van Moolenbroek 
237eda6f593SDavid van Moolenbroek /* Shutdown the server by killing all clients and windows. */
238eda6f593SDavid van Moolenbroek void
server_send_shutdown(void)239eda6f593SDavid van Moolenbroek server_send_shutdown(void)
240eda6f593SDavid van Moolenbroek {
241eda6f593SDavid van Moolenbroek 	struct client	*c;
242eda6f593SDavid van Moolenbroek 	struct session	*s, *next_s;
243eda6f593SDavid van Moolenbroek 	u_int		 i;
244eda6f593SDavid van Moolenbroek 
245eda6f593SDavid van Moolenbroek 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
246eda6f593SDavid van Moolenbroek 		c = ARRAY_ITEM(&clients, i);
247eda6f593SDavid van Moolenbroek 		if (c != NULL) {
248eda6f593SDavid van Moolenbroek 			if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED))
249eda6f593SDavid van Moolenbroek 				server_client_lost(c);
250eda6f593SDavid van Moolenbroek 			else
251eda6f593SDavid van Moolenbroek 				server_write_client(c, MSG_SHUTDOWN, NULL, 0);
252eda6f593SDavid van Moolenbroek 			c->session = NULL;
253eda6f593SDavid van Moolenbroek 		}
254eda6f593SDavid van Moolenbroek 	}
255eda6f593SDavid van Moolenbroek 
256eda6f593SDavid van Moolenbroek 	s = RB_MIN(sessions, &sessions);
257eda6f593SDavid van Moolenbroek 	while (s != NULL) {
258eda6f593SDavid van Moolenbroek 		next_s = RB_NEXT(sessions, &sessions, s);
259eda6f593SDavid van Moolenbroek 		session_destroy(s);
260eda6f593SDavid van Moolenbroek 		s = next_s;
261eda6f593SDavid van Moolenbroek 	}
262eda6f593SDavid van Moolenbroek }
263eda6f593SDavid van Moolenbroek 
264eda6f593SDavid van Moolenbroek /* Free dead, unreferenced clients and sessions. */
265eda6f593SDavid van Moolenbroek void
server_clean_dead(void)266eda6f593SDavid van Moolenbroek server_clean_dead(void)
267eda6f593SDavid van Moolenbroek {
268eda6f593SDavid van Moolenbroek 	struct session	*s, *next_s;
269eda6f593SDavid van Moolenbroek 	struct client	*c;
270eda6f593SDavid van Moolenbroek 	u_int		 i;
271eda6f593SDavid van Moolenbroek 
272eda6f593SDavid van Moolenbroek 	s = RB_MIN(sessions, &dead_sessions);
273eda6f593SDavid van Moolenbroek 	while (s != NULL) {
274eda6f593SDavid van Moolenbroek 		next_s = RB_NEXT(sessions, &dead_sessions, s);
275eda6f593SDavid van Moolenbroek 		if (s->references == 0) {
276eda6f593SDavid van Moolenbroek 			RB_REMOVE(sessions, &dead_sessions, s);
277*0a6a1f1dSLionel Sambuc 			free(s->name);
278*0a6a1f1dSLionel Sambuc 			free(s);
279eda6f593SDavid van Moolenbroek 		}
280eda6f593SDavid van Moolenbroek 		s = next_s;
281eda6f593SDavid van Moolenbroek 	}
282eda6f593SDavid van Moolenbroek 
283eda6f593SDavid van Moolenbroek 	for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
284eda6f593SDavid van Moolenbroek 		c = ARRAY_ITEM(&dead_clients, i);
285eda6f593SDavid van Moolenbroek 		if (c == NULL || c->references != 0)
286eda6f593SDavid van Moolenbroek 			continue;
287eda6f593SDavid van Moolenbroek 		ARRAY_SET(&dead_clients, i, NULL);
288*0a6a1f1dSLionel Sambuc 		free(c);
289eda6f593SDavid van Moolenbroek 	}
290eda6f593SDavid van Moolenbroek }
291eda6f593SDavid van Moolenbroek 
292eda6f593SDavid van Moolenbroek /* Update socket execute permissions based on whether sessions are attached. */
293eda6f593SDavid van Moolenbroek void
server_update_socket(void)294eda6f593SDavid van Moolenbroek server_update_socket(void)
295eda6f593SDavid van Moolenbroek {
296eda6f593SDavid van Moolenbroek 	struct session	*s;
297eda6f593SDavid van Moolenbroek 	static int	 last = -1;
298eda6f593SDavid van Moolenbroek 	int		 n, mode;
299eda6f593SDavid van Moolenbroek 	struct stat      sb;
300eda6f593SDavid van Moolenbroek 
301eda6f593SDavid van Moolenbroek 	n = 0;
302eda6f593SDavid van Moolenbroek 	RB_FOREACH(s, sessions, &sessions) {
303eda6f593SDavid van Moolenbroek 		if (!(s->flags & SESSION_UNATTACHED)) {
304eda6f593SDavid van Moolenbroek 			n++;
305eda6f593SDavid van Moolenbroek 			break;
306eda6f593SDavid van Moolenbroek 		}
307eda6f593SDavid van Moolenbroek 	}
308eda6f593SDavid van Moolenbroek 
309eda6f593SDavid van Moolenbroek 	if (n != last) {
310eda6f593SDavid van Moolenbroek 		last = n;
311eda6f593SDavid van Moolenbroek 
312eda6f593SDavid van Moolenbroek 		if (stat(socket_path, &sb) != 0)
313eda6f593SDavid van Moolenbroek 			return;
314eda6f593SDavid van Moolenbroek 		mode = sb.st_mode;
315eda6f593SDavid van Moolenbroek 		if (n != 0) {
316eda6f593SDavid van Moolenbroek 			if (mode & S_IRUSR)
317eda6f593SDavid van Moolenbroek 				mode |= S_IXUSR;
318eda6f593SDavid van Moolenbroek 			if (mode & S_IRGRP)
319eda6f593SDavid van Moolenbroek 				mode |= S_IXGRP;
320eda6f593SDavid van Moolenbroek 			if (mode & S_IROTH)
321eda6f593SDavid van Moolenbroek 				mode |= S_IXOTH;
322eda6f593SDavid van Moolenbroek 		} else
323eda6f593SDavid van Moolenbroek 			mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
324eda6f593SDavid van Moolenbroek 		chmod(socket_path, mode);
325eda6f593SDavid van Moolenbroek 	}
326eda6f593SDavid van Moolenbroek }
327eda6f593SDavid van Moolenbroek 
328eda6f593SDavid van Moolenbroek /* Callback for server socket. */
329eda6f593SDavid van Moolenbroek void
server_accept_callback(int fd,short events,unused void * data)330eda6f593SDavid van Moolenbroek server_accept_callback(int fd, short events, unused void *data)
331eda6f593SDavid van Moolenbroek {
332eda6f593SDavid van Moolenbroek 	struct sockaddr_storage	sa;
333eda6f593SDavid van Moolenbroek 	socklen_t		slen = sizeof sa;
334eda6f593SDavid van Moolenbroek 	int			newfd;
335eda6f593SDavid van Moolenbroek 
336*0a6a1f1dSLionel Sambuc 	server_add_accept(0);
337eda6f593SDavid van Moolenbroek 	if (!(events & EV_READ))
338eda6f593SDavid van Moolenbroek 		return;
339eda6f593SDavid van Moolenbroek 
340eda6f593SDavid van Moolenbroek 	newfd = accept(fd, (struct sockaddr *) &sa, &slen);
341eda6f593SDavid van Moolenbroek 	if (newfd == -1) {
342eda6f593SDavid van Moolenbroek 		if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
343eda6f593SDavid van Moolenbroek 			return;
344*0a6a1f1dSLionel Sambuc 		if (errno == ENFILE || errno == EMFILE) {
345*0a6a1f1dSLionel Sambuc 			/* Delete and don't try again for 1 second. */
346*0a6a1f1dSLionel Sambuc 			server_add_accept(1);
347*0a6a1f1dSLionel Sambuc 			return;
348*0a6a1f1dSLionel Sambuc 		}
349eda6f593SDavid van Moolenbroek 		fatal("accept failed");
350eda6f593SDavid van Moolenbroek 	}
351eda6f593SDavid van Moolenbroek 	if (server_shutdown) {
352eda6f593SDavid van Moolenbroek 		close(newfd);
353eda6f593SDavid van Moolenbroek 		return;
354eda6f593SDavid van Moolenbroek 	}
355eda6f593SDavid van Moolenbroek 	server_client_create(newfd);
356eda6f593SDavid van Moolenbroek }
357eda6f593SDavid van Moolenbroek 
358*0a6a1f1dSLionel Sambuc /*
359*0a6a1f1dSLionel Sambuc  * Add accept event. If timeout is nonzero, add as a timeout instead of a read
360*0a6a1f1dSLionel Sambuc  * event - used to backoff when running out of file descriptors.
361*0a6a1f1dSLionel Sambuc  */
362*0a6a1f1dSLionel Sambuc void
server_add_accept(int timeout)363*0a6a1f1dSLionel Sambuc server_add_accept(int timeout)
364*0a6a1f1dSLionel Sambuc {
365*0a6a1f1dSLionel Sambuc 	struct timeval tv = { timeout, 0 };
366*0a6a1f1dSLionel Sambuc 
367*0a6a1f1dSLionel Sambuc 	if (event_initialized(&server_ev_accept))
368*0a6a1f1dSLionel Sambuc 		event_del(&server_ev_accept);
369*0a6a1f1dSLionel Sambuc 
370*0a6a1f1dSLionel Sambuc 	if (timeout == 0) {
371*0a6a1f1dSLionel Sambuc 		event_set(&server_ev_accept,
372*0a6a1f1dSLionel Sambuc 		    server_fd, EV_READ, server_accept_callback, NULL);
373*0a6a1f1dSLionel Sambuc 		event_add(&server_ev_accept, NULL);
374*0a6a1f1dSLionel Sambuc 	} else {
375*0a6a1f1dSLionel Sambuc 		event_set(&server_ev_accept,
376*0a6a1f1dSLionel Sambuc 		    server_fd, EV_TIMEOUT, server_accept_callback, NULL);
377*0a6a1f1dSLionel Sambuc 		event_add(&server_ev_accept, &tv);
378*0a6a1f1dSLionel Sambuc 	}
379*0a6a1f1dSLionel Sambuc }
380*0a6a1f1dSLionel Sambuc 
381eda6f593SDavid van Moolenbroek /* Signal handler. */
382eda6f593SDavid van Moolenbroek void
server_signal_callback(int sig,unused short events,unused void * data)383eda6f593SDavid van Moolenbroek server_signal_callback(int sig, unused short events, unused void *data)
384eda6f593SDavid van Moolenbroek {
385eda6f593SDavid van Moolenbroek 	switch (sig) {
386eda6f593SDavid van Moolenbroek 	case SIGTERM:
387eda6f593SDavid van Moolenbroek 		server_shutdown = 1;
388eda6f593SDavid van Moolenbroek 		server_send_shutdown();
389eda6f593SDavid van Moolenbroek 		break;
390eda6f593SDavid van Moolenbroek 	case SIGCHLD:
391eda6f593SDavid van Moolenbroek 		server_child_signal();
392eda6f593SDavid van Moolenbroek 		break;
393eda6f593SDavid van Moolenbroek 	case SIGUSR1:
394eda6f593SDavid van Moolenbroek 		event_del(&server_ev_accept);
395eda6f593SDavid van Moolenbroek 		close(server_fd);
396eda6f593SDavid van Moolenbroek 		server_fd = server_create_socket();
397*0a6a1f1dSLionel Sambuc 		server_add_accept(0);
398eda6f593SDavid van Moolenbroek 		break;
399eda6f593SDavid van Moolenbroek 	}
400eda6f593SDavid van Moolenbroek }
401eda6f593SDavid van Moolenbroek 
402eda6f593SDavid van Moolenbroek /* Handle SIGCHLD. */
403eda6f593SDavid van Moolenbroek void
server_child_signal(void)404eda6f593SDavid van Moolenbroek server_child_signal(void)
405eda6f593SDavid van Moolenbroek {
406eda6f593SDavid van Moolenbroek 	int	 status;
407eda6f593SDavid van Moolenbroek 	pid_t	 pid;
408eda6f593SDavid van Moolenbroek 
409eda6f593SDavid van Moolenbroek 	for (;;) {
410eda6f593SDavid van Moolenbroek 		switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
411eda6f593SDavid van Moolenbroek 		case -1:
412eda6f593SDavid van Moolenbroek 			if (errno == ECHILD)
413eda6f593SDavid van Moolenbroek 				return;
414eda6f593SDavid van Moolenbroek 			fatal("waitpid failed");
415eda6f593SDavid van Moolenbroek 		case 0:
416eda6f593SDavid van Moolenbroek 			return;
417eda6f593SDavid van Moolenbroek 		}
418eda6f593SDavid van Moolenbroek 		if (WIFSTOPPED(status))
419eda6f593SDavid van Moolenbroek 			server_child_stopped(pid, status);
420eda6f593SDavid van Moolenbroek 		else if (WIFEXITED(status) || WIFSIGNALED(status))
421eda6f593SDavid van Moolenbroek 			server_child_exited(pid, status);
422eda6f593SDavid van Moolenbroek 	}
423eda6f593SDavid van Moolenbroek }
424eda6f593SDavid van Moolenbroek 
425eda6f593SDavid van Moolenbroek /* Handle exited children. */
426eda6f593SDavid van Moolenbroek void
server_child_exited(pid_t pid,int status)427eda6f593SDavid van Moolenbroek server_child_exited(pid_t pid, int status)
428eda6f593SDavid van Moolenbroek {
429eda6f593SDavid van Moolenbroek 	struct window		*w;
430eda6f593SDavid van Moolenbroek 	struct window_pane	*wp;
431eda6f593SDavid van Moolenbroek 	struct job		*job;
432eda6f593SDavid van Moolenbroek 	u_int		 	 i;
433eda6f593SDavid van Moolenbroek 
434eda6f593SDavid van Moolenbroek 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
435eda6f593SDavid van Moolenbroek 		if ((w = ARRAY_ITEM(&windows, i)) == NULL)
436eda6f593SDavid van Moolenbroek 			continue;
437eda6f593SDavid van Moolenbroek 		TAILQ_FOREACH(wp, &w->panes, entry) {
438eda6f593SDavid van Moolenbroek 			if (wp->pid == pid) {
439eda6f593SDavid van Moolenbroek 				server_destroy_pane(wp);
440eda6f593SDavid van Moolenbroek 				break;
441eda6f593SDavid van Moolenbroek 			}
442eda6f593SDavid van Moolenbroek 		}
443eda6f593SDavid van Moolenbroek 	}
444eda6f593SDavid van Moolenbroek 
445eda6f593SDavid van Moolenbroek 	LIST_FOREACH(job, &all_jobs, lentry) {
446eda6f593SDavid van Moolenbroek 		if (pid == job->pid) {
447eda6f593SDavid van Moolenbroek 			job_died(job, status);	/* might free job */
448eda6f593SDavid van Moolenbroek 			break;
449eda6f593SDavid van Moolenbroek 		}
450eda6f593SDavid van Moolenbroek 	}
451eda6f593SDavid van Moolenbroek }
452eda6f593SDavid van Moolenbroek 
453eda6f593SDavid van Moolenbroek /* Handle stopped children. */
454eda6f593SDavid van Moolenbroek void
server_child_stopped(pid_t pid,int status)455eda6f593SDavid van Moolenbroek server_child_stopped(pid_t pid, int status)
456eda6f593SDavid van Moolenbroek {
457eda6f593SDavid van Moolenbroek 	struct window		*w;
458eda6f593SDavid van Moolenbroek 	struct window_pane	*wp;
459eda6f593SDavid van Moolenbroek 	u_int			 i;
460eda6f593SDavid van Moolenbroek 
461eda6f593SDavid van Moolenbroek 	if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
462eda6f593SDavid van Moolenbroek 		return;
463eda6f593SDavid van Moolenbroek 
464eda6f593SDavid van Moolenbroek 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
465eda6f593SDavid van Moolenbroek 		if ((w = ARRAY_ITEM(&windows, i)) == NULL)
466eda6f593SDavid van Moolenbroek 			continue;
467eda6f593SDavid van Moolenbroek 		TAILQ_FOREACH(wp, &w->panes, entry) {
468eda6f593SDavid van Moolenbroek 			if (wp->pid == pid) {
469eda6f593SDavid van Moolenbroek 				if (killpg(pid, SIGCONT) != 0)
470eda6f593SDavid van Moolenbroek 					kill(pid, SIGCONT);
471eda6f593SDavid van Moolenbroek 			}
472eda6f593SDavid van Moolenbroek 		}
473eda6f593SDavid van Moolenbroek 	}
474eda6f593SDavid van Moolenbroek }
475eda6f593SDavid van Moolenbroek 
476eda6f593SDavid van Moolenbroek /* Handle once-per-second timer events. */
477eda6f593SDavid van Moolenbroek void
server_second_callback(unused int fd,unused short events,unused void * arg)478eda6f593SDavid van Moolenbroek server_second_callback(unused int fd, unused short events, unused void *arg)
479eda6f593SDavid van Moolenbroek {
480eda6f593SDavid van Moolenbroek 	struct window		*w;
481eda6f593SDavid van Moolenbroek 	struct window_pane	*wp;
482eda6f593SDavid van Moolenbroek 	struct timeval		 tv;
483eda6f593SDavid van Moolenbroek 	u_int		 	 i;
484eda6f593SDavid van Moolenbroek 
485eda6f593SDavid van Moolenbroek 	if (options_get_number(&global_s_options, "lock-server"))
486eda6f593SDavid van Moolenbroek 		server_lock_server();
487eda6f593SDavid van Moolenbroek 	else
488eda6f593SDavid van Moolenbroek 		server_lock_sessions();
489eda6f593SDavid van Moolenbroek 
490eda6f593SDavid van Moolenbroek 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
491eda6f593SDavid van Moolenbroek 		w = ARRAY_ITEM(&windows, i);
492eda6f593SDavid van Moolenbroek 		if (w == NULL)
493eda6f593SDavid van Moolenbroek 			continue;
494eda6f593SDavid van Moolenbroek 
495eda6f593SDavid van Moolenbroek 		TAILQ_FOREACH(wp, &w->panes, entry) {
496eda6f593SDavid van Moolenbroek 			if (wp->mode != NULL && wp->mode->timer != NULL)
497eda6f593SDavid van Moolenbroek 				wp->mode->timer(wp);
498eda6f593SDavid van Moolenbroek 		}
499eda6f593SDavid van Moolenbroek 	}
500eda6f593SDavid van Moolenbroek 
501eda6f593SDavid van Moolenbroek 	server_client_status_timer();
502eda6f593SDavid van Moolenbroek 
503eda6f593SDavid van Moolenbroek 	evtimer_del(&server_ev_second);
504eda6f593SDavid van Moolenbroek 	memset(&tv, 0, sizeof tv);
505eda6f593SDavid van Moolenbroek 	tv.tv_sec = 1;
506eda6f593SDavid van Moolenbroek 	evtimer_add(&server_ev_second, &tv);
507eda6f593SDavid van Moolenbroek }
508eda6f593SDavid van Moolenbroek 
509eda6f593SDavid van Moolenbroek /* Lock the server if ALL sessions have hit the time limit. */
510eda6f593SDavid van Moolenbroek void
server_lock_server(void)511eda6f593SDavid van Moolenbroek server_lock_server(void)
512eda6f593SDavid van Moolenbroek {
513eda6f593SDavid van Moolenbroek 	struct session  *s;
514eda6f593SDavid van Moolenbroek 	int		 timeout;
515eda6f593SDavid van Moolenbroek 	time_t           t;
516eda6f593SDavid van Moolenbroek 
517eda6f593SDavid van Moolenbroek 	t = time(NULL);
518eda6f593SDavid van Moolenbroek 	RB_FOREACH(s, sessions, &sessions) {
519eda6f593SDavid van Moolenbroek 		if (s->flags & SESSION_UNATTACHED)
520eda6f593SDavid van Moolenbroek 			continue;
521eda6f593SDavid van Moolenbroek 		timeout = options_get_number(&s->options, "lock-after-time");
522eda6f593SDavid van Moolenbroek 		if (timeout <= 0 || t <= s->activity_time.tv_sec + timeout)
523eda6f593SDavid van Moolenbroek 			return;	/* not timed out */
524eda6f593SDavid van Moolenbroek 	}
525eda6f593SDavid van Moolenbroek 
526eda6f593SDavid van Moolenbroek 	server_lock();
527eda6f593SDavid van Moolenbroek 	recalculate_sizes();
528eda6f593SDavid van Moolenbroek }
529eda6f593SDavid van Moolenbroek 
530eda6f593SDavid van Moolenbroek /* Lock any sessions which have timed out. */
531eda6f593SDavid van Moolenbroek void
server_lock_sessions(void)532eda6f593SDavid van Moolenbroek server_lock_sessions(void)
533eda6f593SDavid van Moolenbroek {
534eda6f593SDavid van Moolenbroek 	struct session  *s;
535eda6f593SDavid van Moolenbroek 	int		 timeout;
536eda6f593SDavid van Moolenbroek 	time_t		 t;
537eda6f593SDavid van Moolenbroek 
538eda6f593SDavid van Moolenbroek 	t = time(NULL);
539eda6f593SDavid van Moolenbroek 	RB_FOREACH(s, sessions, &sessions) {
540eda6f593SDavid van Moolenbroek 		if (s->flags & SESSION_UNATTACHED)
541eda6f593SDavid van Moolenbroek 			continue;
542eda6f593SDavid van Moolenbroek 		timeout = options_get_number(&s->options, "lock-after-time");
543eda6f593SDavid van Moolenbroek 		if (timeout > 0 && t > s->activity_time.tv_sec + timeout) {
544eda6f593SDavid van Moolenbroek 			server_lock_session(s);
545eda6f593SDavid van Moolenbroek 			recalculate_sizes();
546eda6f593SDavid van Moolenbroek 		}
547eda6f593SDavid van Moolenbroek 	}
548eda6f593SDavid van Moolenbroek }
549