xref: /openbsd-src/usr.bin/tmux/server.c (revision 7519eda3a2d74d39e8eb8c0689e3da0f2bc3bae0)
1*7519eda3Snicm /* $OpenBSD: server.c,v 1.155 2015/12/15 00:00:01 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>
277724d0b0Snicm #include <event.h>
28311827fbSnicm #include <fcntl.h>
29a575a2dfSnicm #include <paths.h>
30311827fbSnicm #include <signal.h>
31311827fbSnicm #include <stdio.h>
32311827fbSnicm #include <stdlib.h>
33311827fbSnicm #include <string.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 struct clients		 clients;
45311827fbSnicm 
465b8ac713Snicm struct tmuxproc		*server_proc;
477724d0b0Snicm int			 server_fd;
484d9325ceSnicm int			 server_exit;
497724d0b0Snicm struct event		 server_ev_accept;
50b7fa064bSnicm 
51*7519eda3Snicm struct cmd_find_state	 marked_pane;
522986c025Snicm 
53311827fbSnicm int	server_create_socket(void);
545b8ac713Snicm int	server_loop(void);
554d9325ceSnicm int	server_should_exit(void);
564d9325ceSnicm void	server_send_exit(void);
575b8ac713Snicm void	server_accept(int, short, void *);
585b8ac713Snicm void	server_signal(int);
597724d0b0Snicm void	server_child_signal(void);
607724d0b0Snicm void	server_child_exited(pid_t, int);
617724d0b0Snicm void	server_child_stopped(pid_t, int);
62b7fa064bSnicm 
632986c025Snicm /* Set marked pane. */
642986c025Snicm void
652986c025Snicm server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
662986c025Snicm {
67*7519eda3Snicm 	cmd_find_clear_state(&marked_pane, NULL, 0);
68*7519eda3Snicm 	marked_pane.s = s;
69*7519eda3Snicm 	marked_pane.wl = wl;
70*7519eda3Snicm 	marked_pane.w = wl->window;
71*7519eda3Snicm 	marked_pane.wp = wp;
722986c025Snicm }
732986c025Snicm 
742986c025Snicm /* Clear marked pane. */
752986c025Snicm void
762986c025Snicm server_clear_marked(void)
772986c025Snicm {
78*7519eda3Snicm 	cmd_find_clear_state(&marked_pane, NULL, 0);
792986c025Snicm }
802986c025Snicm 
812986c025Snicm /* Is this the marked pane? */
822986c025Snicm int
832986c025Snicm server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
842986c025Snicm {
852986c025Snicm 	if (s == NULL || wl == NULL || wp == NULL)
862986c025Snicm 		return (0);
87*7519eda3Snicm 	if (marked_pane.s != s || marked_pane.wl != wl)
882986c025Snicm 		return (0);
89*7519eda3Snicm 	if (marked_pane.wp != wp)
902986c025Snicm 		return (0);
912986c025Snicm 	return (server_check_marked());
922986c025Snicm }
932986c025Snicm 
942986c025Snicm /* Check if the marked pane is still valid. */
952986c025Snicm int
962986c025Snicm server_check_marked(void)
972986c025Snicm {
98*7519eda3Snicm 	return (cmd_find_valid_state(&marked_pane));
992986c025Snicm }
1002986c025Snicm 
10189654e7cSnicm /* Create server socket. */
10289654e7cSnicm int
10389654e7cSnicm server_create_socket(void)
104311827fbSnicm {
10589654e7cSnicm 	struct sockaddr_un	sa;
10689654e7cSnicm 	size_t			size;
10789654e7cSnicm 	mode_t			mask;
108a0ee4254Snicm 	int			fd;
10989654e7cSnicm 
11089654e7cSnicm 	memset(&sa, 0, sizeof sa);
11189654e7cSnicm 	sa.sun_family = AF_UNIX;
11289654e7cSnicm 	size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
11389654e7cSnicm 	if (size >= sizeof sa.sun_path) {
11489654e7cSnicm 		errno = ENAMETOOLONG;
115d53cf33dSnicm 		return (-1);
11689654e7cSnicm 	}
11789654e7cSnicm 	unlink(sa.sun_path);
11889654e7cSnicm 
11989654e7cSnicm 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
120d53cf33dSnicm 		return (-1);
12189654e7cSnicm 
1227394227cSnicm 	mask = umask(S_IXUSR|S_IXGRP|S_IRWXO);
123fbc54f53Sguenther 	if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1)
124d53cf33dSnicm 		return (-1);
12589654e7cSnicm 	umask(mask);
12689654e7cSnicm 
12789654e7cSnicm 	if (listen(fd, 16) == -1)
128d53cf33dSnicm 		return (-1);
129a0ee4254Snicm 	setblocking(fd, 0);
130311827fbSnicm 
13189654e7cSnicm 	return (fd);
13289654e7cSnicm }
133311827fbSnicm 
134311827fbSnicm /* Fork new server. */
135311827fbSnicm int
1367b560ed5Snicm server_start(struct event_base *base, int lockfd, char *lockfile)
137311827fbSnicm {
1384e7bd783Snicm 	int	pair[2];
139311827fbSnicm 
140311827fbSnicm 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
141311827fbSnicm 		fatal("socketpair failed");
142311827fbSnicm 
1435b8ac713Snicm 	server_proc = proc_start("server", base, 1, server_signal);
1445b8ac713Snicm 	if (server_proc == NULL) {
145311827fbSnicm 		close(pair[1]);
146311827fbSnicm 		return (pair[0]);
147311827fbSnicm 	}
148311827fbSnicm 	close(pair[0]);
149311827fbSnicm 
1500a607e68Snicm 	if (log_get_level() > 3)
1516d2e9b46Snicm 		tty_create_log();
152cb19d99cSnicm 	if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec "
153cb19d99cSnicm 	    "tty ps", NULL) != 0)
1544e7a7adeSnicm 		fatal("pledge failed");
1554e7a7adeSnicm 
156ae69181dSnicm 	RB_INIT(&windows);
1573e0f2533Snicm 	RB_INIT(&all_window_panes);
15882873134Snicm 	TAILQ_INIT(&clients);
15959996dc3Snicm 	RB_INIT(&sessions);
16001b2421eSnicm 	TAILQ_INIT(&session_groups);
16168895571Snicm 	mode_key_init_trees();
162311827fbSnicm 	key_bindings_init();
163311827fbSnicm 
1648c1ade38Snicm 	gettimeofday(&start_time, NULL);
165311827fbSnicm 
1667724d0b0Snicm 	server_fd = server_create_socket();
167d53cf33dSnicm 	if (server_fd == -1)
168d53cf33dSnicm 		fatal("couldn't create socket");
169d53cf33dSnicm 	server_update_socket();
17089654e7cSnicm 	server_client_create(pair[1]);
171311827fbSnicm 
1725c705c36Snicm 	if (lockfd >= 0) {
173549634dfSnicm 		unlink(lockfile);
1747d053cf9Snicm 		free(lockfile);
175549634dfSnicm 		close(lockfd);
1765c705c36Snicm 	}
177549634dfSnicm 
17884cce0efSnicm 	start_cfg();
179175d36ccSnicm 
180179ef399Snicm 	status_prompt_load_history();
1810cd9638eSnicm 
182a06e463cSnicm 	server_add_accept(0);
1837724d0b0Snicm 
1845b8ac713Snicm 	proc_loop(server_proc, server_loop);
185179ef399Snicm 	status_prompt_save_history();
1867724d0b0Snicm 	exit(0);
187311827fbSnicm }
188311827fbSnicm 
1895b8ac713Snicm /* Server loop callback. */
1905b8ac713Snicm int
1917724d0b0Snicm server_loop(void)
192311827fbSnicm {
1935b8ac713Snicm 	struct client	*c;
194311827fbSnicm 
19589654e7cSnicm 	server_client_loop();
19630da262fSnicm 
197d89252e5Snicm 	if (!options_get_number(global_options, "exit-unattached")) {
19859996dc3Snicm 		if (!RB_EMPTY(&sessions))
19930da262fSnicm 			return (0);
20030da262fSnicm 	}
2017d5939e2Snicm 
20282873134Snicm 	TAILQ_FOREACH(c, &clients, entry) {
20382873134Snicm 		if (c->session != NULL)
2047d5939e2Snicm 			return (0);
2057d5939e2Snicm 	}
2067d5939e2Snicm 
2077d5939e2Snicm 	/*
2087d5939e2Snicm 	 * No attached clients therefore want to exit - flush any waiting
2097d5939e2Snicm 	 * clients but don't actually exit until they've gone.
2107d5939e2Snicm 	 */
2117d5939e2Snicm 	cmd_wait_for_flush();
21282873134Snicm 	if (!TAILQ_EMPTY(&clients))
21330da262fSnicm 		return (0);
2147d5939e2Snicm 
21530da262fSnicm 	return (1);
216311827fbSnicm }
217311827fbSnicm 
2184d9325ceSnicm /* Exit the server by killing all clients and windows. */
219311827fbSnicm void
2204d9325ceSnicm server_send_exit(void)
221311827fbSnicm {
22282873134Snicm 	struct client	*c, *c1;
22382873134Snicm 	struct session	*s, *s1;
224311827fbSnicm 
2257d5939e2Snicm 	cmd_wait_for_flush();
2267d5939e2Snicm 
22782873134Snicm 	TAILQ_FOREACH_SAFE(c, &clients, entry, c1) {
2285b8ac713Snicm 		if (c->flags & CLIENT_SUSPENDED)
2297724d0b0Snicm 			server_client_lost(c);
2307724d0b0Snicm 		else
2315b8ac713Snicm 			proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0);
2327724d0b0Snicm 		c->session = NULL;
2338148c7feSnicm 	}
234311827fbSnicm 
23582873134Snicm 	RB_FOREACH_SAFE(s, sessions, &sessions, s1)
2367724d0b0Snicm 		session_destroy(s);
237311827fbSnicm }
238311827fbSnicm 
2397724d0b0Snicm /* Update socket execute permissions based on whether sessions are attached. */
24037dfd443Snicm void
2417724d0b0Snicm server_update_socket(void)
2427724d0b0Snicm {
2437724d0b0Snicm 	struct session	*s;
2447724d0b0Snicm 	static int	 last = -1;
2456d9987f5Snicm 	int		 n, mode;
2466d9987f5Snicm 	struct stat      sb;
2477724d0b0Snicm 
2487724d0b0Snicm 	n = 0;
24959996dc3Snicm 	RB_FOREACH(s, sessions, &sessions) {
25059996dc3Snicm 		if (!(s->flags & SESSION_UNATTACHED)) {
2517724d0b0Snicm 			n++;
2527724d0b0Snicm 			break;
2537724d0b0Snicm 		}
2547724d0b0Snicm 	}
2557724d0b0Snicm 
2567724d0b0Snicm 	if (n != last) {
2577724d0b0Snicm 		last = n;
2586d9987f5Snicm 
2596d9987f5Snicm 		if (stat(socket_path, &sb) != 0)
2606d9987f5Snicm 			return;
2616d9987f5Snicm 		mode = sb.st_mode;
2626d9987f5Snicm 		if (n != 0) {
2636d9987f5Snicm 			if (mode & S_IRUSR)
2646d9987f5Snicm 				mode |= S_IXUSR;
2656d9987f5Snicm 			if (mode & S_IRGRP)
2666d9987f5Snicm 				mode |= S_IXGRP;
2676d9987f5Snicm 			if (mode & S_IROTH)
2686d9987f5Snicm 				mode |= S_IXOTH;
2696d9987f5Snicm 		} else
2706d9987f5Snicm 			mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
2716d9987f5Snicm 		chmod(socket_path, mode);
2727724d0b0Snicm 	}
2737724d0b0Snicm }
2747724d0b0Snicm 
2757724d0b0Snicm /* Callback for server socket. */
276311827fbSnicm void
277d0e2e7f1Snicm server_accept(int fd, short events, __unused void *data)
2787724d0b0Snicm {
2797724d0b0Snicm 	struct sockaddr_storage	sa;
2807724d0b0Snicm 	socklen_t		slen = sizeof sa;
2817724d0b0Snicm 	int			newfd;
2827724d0b0Snicm 
283a06e463cSnicm 	server_add_accept(0);
2847724d0b0Snicm 	if (!(events & EV_READ))
2857724d0b0Snicm 		return;
2867724d0b0Snicm 
2877724d0b0Snicm 	newfd = accept(fd, (struct sockaddr *) &sa, &slen);
2887724d0b0Snicm 	if (newfd == -1) {
2897724d0b0Snicm 		if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
2907724d0b0Snicm 			return;
291a06e463cSnicm 		if (errno == ENFILE || errno == EMFILE) {
292a06e463cSnicm 			/* Delete and don't try again for 1 second. */
293a06e463cSnicm 			server_add_accept(1);
294a06e463cSnicm 			return;
295a06e463cSnicm 		}
2967724d0b0Snicm 		fatal("accept failed");
2977724d0b0Snicm 	}
2984d9325ceSnicm 	if (server_exit) {
2997724d0b0Snicm 		close(newfd);
3007724d0b0Snicm 		return;
3017724d0b0Snicm 	}
3027724d0b0Snicm 	server_client_create(newfd);
3037724d0b0Snicm }
3047724d0b0Snicm 
305a06e463cSnicm /*
306a06e463cSnicm  * Add accept event. If timeout is nonzero, add as a timeout instead of a read
307a06e463cSnicm  * event - used to backoff when running out of file descriptors.
308a06e463cSnicm  */
309a06e463cSnicm void
310a06e463cSnicm server_add_accept(int timeout)
311a06e463cSnicm {
312a06e463cSnicm 	struct timeval tv = { timeout, 0 };
313a06e463cSnicm 
314a06e463cSnicm 	if (event_initialized(&server_ev_accept))
315a06e463cSnicm 		event_del(&server_ev_accept);
316a06e463cSnicm 
317a06e463cSnicm 	if (timeout == 0) {
3185b8ac713Snicm 		event_set(&server_ev_accept, server_fd, EV_READ, server_accept,
3195b8ac713Snicm 		    NULL);
320a06e463cSnicm 		event_add(&server_ev_accept, NULL);
321a06e463cSnicm 	} else {
3225b8ac713Snicm 		event_set(&server_ev_accept, server_fd, EV_TIMEOUT,
3235b8ac713Snicm 		    server_accept, NULL);
324a06e463cSnicm 		event_add(&server_ev_accept, &tv);
325a06e463cSnicm 	}
326a06e463cSnicm }
327a06e463cSnicm 
3287724d0b0Snicm /* Signal handler. */
3297724d0b0Snicm void
3305b8ac713Snicm server_signal(int sig)
3317724d0b0Snicm {
332d53cf33dSnicm 	int	fd;
3335235cd7fSnicm 
3347724d0b0Snicm 	switch (sig) {
3357724d0b0Snicm 	case SIGTERM:
3364d9325ceSnicm 		server_exit = 1;
3374d9325ceSnicm 		server_send_exit();
3387724d0b0Snicm 		break;
3397724d0b0Snicm 	case SIGCHLD:
3407724d0b0Snicm 		server_child_signal();
3417724d0b0Snicm 		break;
3427724d0b0Snicm 	case SIGUSR1:
3437724d0b0Snicm 		event_del(&server_ev_accept);
344d53cf33dSnicm 		fd = server_create_socket();
345d53cf33dSnicm 		if (fd != -1) {
3467724d0b0Snicm 			close(server_fd);
347d53cf33dSnicm 			server_fd = fd;
348d53cf33dSnicm 			server_update_socket();
349d53cf33dSnicm 		}
350a06e463cSnicm 		server_add_accept(0);
3517724d0b0Snicm 		break;
3527724d0b0Snicm 	}
3537724d0b0Snicm }
3547724d0b0Snicm 
3557724d0b0Snicm /* Handle SIGCHLD. */
3567724d0b0Snicm void
3577724d0b0Snicm server_child_signal(void)
3587724d0b0Snicm {
3597724d0b0Snicm 	int	 status;
3607724d0b0Snicm 	pid_t	 pid;
3617724d0b0Snicm 
3627724d0b0Snicm 	for (;;) {
3637724d0b0Snicm 		switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
3647724d0b0Snicm 		case -1:
3657724d0b0Snicm 			if (errno == ECHILD)
3667724d0b0Snicm 				return;
3677724d0b0Snicm 			fatal("waitpid failed");
3687724d0b0Snicm 		case 0:
3697724d0b0Snicm 			return;
3707724d0b0Snicm 		}
3717724d0b0Snicm 		if (WIFSTOPPED(status))
3727724d0b0Snicm 			server_child_stopped(pid, status);
373a236b623Snicm 		else if (WIFEXITED(status) || WIFSIGNALED(status))
3747724d0b0Snicm 			server_child_exited(pid, status);
3757724d0b0Snicm 	}
3767724d0b0Snicm }
3777724d0b0Snicm 
3787724d0b0Snicm /* Handle exited children. */
3797724d0b0Snicm void
3807724d0b0Snicm server_child_exited(pid_t pid, int status)
381311827fbSnicm {
382ae69181dSnicm 	struct window		*w, *w1;
38389654e7cSnicm 	struct window_pane	*wp;
3847724d0b0Snicm 	struct job		*job;
3857724d0b0Snicm 
386ae69181dSnicm 	RB_FOREACH_SAFE(w, windows, &windows, w1) {
3877724d0b0Snicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
3887724d0b0Snicm 			if (wp->pid == pid) {
3893e459475Snicm 				wp->status = status;
3907c6e570cSnicm 				server_destroy_pane(wp);
3917c6e570cSnicm 				break;
3927724d0b0Snicm 			}
3937724d0b0Snicm 		}
3947724d0b0Snicm 	}
3957724d0b0Snicm 
3960a271d7eSnicm 	LIST_FOREACH(job, &all_jobs, lentry) {
3977724d0b0Snicm 		if (pid == job->pid) {
39824abd014Snicm 			job_died(job, status);	/* might free job */
39924abd014Snicm 			break;
4007724d0b0Snicm 		}
4017724d0b0Snicm 	}
4027724d0b0Snicm }
4037724d0b0Snicm 
4047724d0b0Snicm /* Handle stopped children. */
4057724d0b0Snicm void
4067724d0b0Snicm server_child_stopped(pid_t pid, int status)
4077724d0b0Snicm {
4087724d0b0Snicm 	struct window		*w;
4097724d0b0Snicm 	struct window_pane	*wp;
4107724d0b0Snicm 
4117724d0b0Snicm 	if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
4127724d0b0Snicm 		return;
4137724d0b0Snicm 
414ae69181dSnicm 	RB_FOREACH(w, windows, &windows) {
4157724d0b0Snicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
4167724d0b0Snicm 			if (wp->pid == pid) {
4177724d0b0Snicm 				if (killpg(pid, SIGCONT) != 0)
4187724d0b0Snicm 					kill(pid, SIGCONT);
4197724d0b0Snicm 			}
4207724d0b0Snicm 		}
4217724d0b0Snicm 	}
4227724d0b0Snicm }
423