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