xref: /openbsd-src/usr.bin/tmux/server.c (revision 514fd64db65eb8fdaad8eb512948cdee09ecebcf)
1*514fd64dSnicm /* $OpenBSD: server.c,v 1.177 2017/10/12 11:32:27 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
498ca8272Snicm  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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;
479883b791Snicm static int		 server_fd;
489883b791Snicm static int		 server_exit;
499883b791Snicm static struct event	 server_ev_accept;
50b7fa064bSnicm 
517519eda3Snicm struct cmd_find_state	 marked_pane;
522986c025Snicm 
539883b791Snicm static int	server_create_socket(void);
549883b791Snicm static int	server_loop(void);
559883b791Snicm static void	server_send_exit(void);
569883b791Snicm static void	server_accept(int, short, void *);
579883b791Snicm static void	server_signal(int);
589883b791Snicm static void	server_child_signal(void);
599883b791Snicm static void	server_child_exited(pid_t, int);
609883b791Snicm static void	server_child_stopped(pid_t, int);
61b7fa064bSnicm 
622986c025Snicm /* Set marked pane. */
632986c025Snicm void
642986c025Snicm server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
652986c025Snicm {
6693e732aaSnicm 	cmd_find_clear_state(&marked_pane, 0);
677519eda3Snicm 	marked_pane.s = s;
687519eda3Snicm 	marked_pane.wl = wl;
697519eda3Snicm 	marked_pane.w = wl->window;
707519eda3Snicm 	marked_pane.wp = wp;
712986c025Snicm }
722986c025Snicm 
732986c025Snicm /* Clear marked pane. */
742986c025Snicm void
752986c025Snicm server_clear_marked(void)
762986c025Snicm {
7793e732aaSnicm 	cmd_find_clear_state(&marked_pane, 0);
782986c025Snicm }
792986c025Snicm 
802986c025Snicm /* Is this the marked pane? */
812986c025Snicm int
822986c025Snicm server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
832986c025Snicm {
842986c025Snicm 	if (s == NULL || wl == NULL || wp == NULL)
852986c025Snicm 		return (0);
867519eda3Snicm 	if (marked_pane.s != s || marked_pane.wl != wl)
872986c025Snicm 		return (0);
887519eda3Snicm 	if (marked_pane.wp != wp)
892986c025Snicm 		return (0);
902986c025Snicm 	return (server_check_marked());
912986c025Snicm }
922986c025Snicm 
932986c025Snicm /* Check if the marked pane is still valid. */
942986c025Snicm int
952986c025Snicm server_check_marked(void)
962986c025Snicm {
977519eda3Snicm 	return (cmd_find_valid_state(&marked_pane));
982986c025Snicm }
992986c025Snicm 
10089654e7cSnicm /* Create server socket. */
1019883b791Snicm static int
10289654e7cSnicm server_create_socket(void)
103311827fbSnicm {
10489654e7cSnicm 	struct sockaddr_un	sa;
10589654e7cSnicm 	size_t			size;
10689654e7cSnicm 	mode_t			mask;
107a0ee4254Snicm 	int			fd;
10889654e7cSnicm 
10989654e7cSnicm 	memset(&sa, 0, sizeof sa);
11089654e7cSnicm 	sa.sun_family = AF_UNIX;
11189654e7cSnicm 	size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
11289654e7cSnicm 	if (size >= sizeof sa.sun_path) {
11389654e7cSnicm 		errno = ENAMETOOLONG;
114d53cf33dSnicm 		return (-1);
11589654e7cSnicm 	}
11689654e7cSnicm 	unlink(sa.sun_path);
11789654e7cSnicm 
11889654e7cSnicm 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
119d53cf33dSnicm 		return (-1);
12089654e7cSnicm 
1217394227cSnicm 	mask = umask(S_IXUSR|S_IXGRP|S_IRWXO);
122fb807b18Snicm 	if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
123fb807b18Snicm 		close(fd);
124d53cf33dSnicm 		return (-1);
125fb807b18Snicm 	}
12689654e7cSnicm 	umask(mask);
12789654e7cSnicm 
128fb807b18Snicm 	if (listen(fd, 128) == -1) {
129fb807b18Snicm 		close(fd);
130d53cf33dSnicm 		return (-1);
131fb807b18Snicm 	}
132a0ee4254Snicm 	setblocking(fd, 0);
133311827fbSnicm 
13489654e7cSnicm 	return (fd);
13589654e7cSnicm }
136311827fbSnicm 
137311827fbSnicm /* Fork new server. */
138311827fbSnicm int
139c37a9299Snicm server_start(struct tmuxproc *client, struct event_base *base, int lockfd,
140c37a9299Snicm     char *lockfile)
141311827fbSnicm {
1424e7bd783Snicm 	int		 pair[2];
1431a432fefSnicm 	struct job	*job;
144189d1393Snicm 	sigset_t	 set, oldset;
145311827fbSnicm 
146311827fbSnicm 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
147311827fbSnicm 		fatal("socketpair failed");
148311827fbSnicm 
149189d1393Snicm 	sigfillset(&set);
150189d1393Snicm 	sigprocmask(SIG_BLOCK, &set, &oldset);
151c37a9299Snicm 	switch (fork()) {
152c37a9299Snicm 	case -1:
153c37a9299Snicm 		fatal("fork failed");
154c37a9299Snicm 	case 0:
155c37a9299Snicm 		break;
156c37a9299Snicm 	default:
157189d1393Snicm 		sigprocmask(SIG_SETMASK, &oldset, NULL);
158311827fbSnicm 		close(pair[1]);
159311827fbSnicm 		return (pair[0]);
160311827fbSnicm 	}
161311827fbSnicm 	close(pair[0]);
162c37a9299Snicm 	if (daemon(1, 0) != 0)
163c37a9299Snicm 		fatal("daemon failed");
16489b52a5bSnicm 	proc_clear_signals(client, 0);
165c37a9299Snicm 	if (event_reinit(base) != 0)
166c37a9299Snicm 		fatalx("event_reinit failed");
167c37a9299Snicm 	server_proc = proc_start("server");
168c37a9299Snicm 	proc_set_signals(server_proc, server_signal);
169189d1393Snicm 	sigprocmask(SIG_SETMASK, &oldset, NULL);
170c37a9299Snicm 
17179c9b201Snicm 	if (log_get_level() > 1)
1726d2e9b46Snicm 		tty_create_log();
173cb19d99cSnicm 	if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec "
174cb19d99cSnicm 	    "tty ps", NULL) != 0)
1754e7a7adeSnicm 		fatal("pledge failed");
1764e7a7adeSnicm 
177ae69181dSnicm 	RB_INIT(&windows);
1783e0f2533Snicm 	RB_INIT(&all_window_panes);
17982873134Snicm 	TAILQ_INIT(&clients);
18059996dc3Snicm 	RB_INIT(&sessions);
18106440b28Snicm 	RB_INIT(&session_groups);
182311827fbSnicm 	key_bindings_init();
183311827fbSnicm 
1848c1ade38Snicm 	gettimeofday(&start_time, NULL);
185311827fbSnicm 
1867724d0b0Snicm 	server_fd = server_create_socket();
187d53cf33dSnicm 	if (server_fd == -1)
188d53cf33dSnicm 		fatal("couldn't create socket");
189d53cf33dSnicm 	server_update_socket();
19089654e7cSnicm 	server_client_create(pair[1]);
191311827fbSnicm 
1925c705c36Snicm 	if (lockfd >= 0) {
193549634dfSnicm 		unlink(lockfile);
1947d053cf9Snicm 		free(lockfile);
195549634dfSnicm 		close(lockfd);
1965c705c36Snicm 	}
197549634dfSnicm 
19884cce0efSnicm 	start_cfg();
199175d36ccSnicm 
200a06e463cSnicm 	server_add_accept(0);
2017724d0b0Snicm 
2025b8ac713Snicm 	proc_loop(server_proc, server_loop);
2031a432fefSnicm 
2041a432fefSnicm 	LIST_FOREACH(job, &all_jobs, entry) {
2051a432fefSnicm 		if (job->pid != -1)
2061a432fefSnicm 			kill(job->pid, SIGTERM);
2071a432fefSnicm 	}
2081a432fefSnicm 
209179ef399Snicm 	status_prompt_save_history();
2107724d0b0Snicm 	exit(0);
211311827fbSnicm }
212311827fbSnicm 
2135b8ac713Snicm /* Server loop callback. */
2149883b791Snicm static int
2157724d0b0Snicm server_loop(void)
216311827fbSnicm {
2175b8ac713Snicm 	struct client	*c;
218765b9a58Snicm 	u_int		 items;
219765b9a58Snicm 
220765b9a58Snicm 	do {
221765b9a58Snicm 		items = cmdq_next(NULL);
2226a2dc005Snicm 		TAILQ_FOREACH(c, &clients, entry) {
2236a2dc005Snicm 			if (c->flags & CLIENT_IDENTIFIED)
224765b9a58Snicm 				items += cmdq_next(c);
2256a2dc005Snicm 		}
226765b9a58Snicm 	} while (items != 0);
227311827fbSnicm 
22889654e7cSnicm 	server_client_loop();
22930da262fSnicm 
230d89252e5Snicm 	if (!options_get_number(global_options, "exit-unattached")) {
23159996dc3Snicm 		if (!RB_EMPTY(&sessions))
23230da262fSnicm 			return (0);
23330da262fSnicm 	}
2347d5939e2Snicm 
23582873134Snicm 	TAILQ_FOREACH(c, &clients, entry) {
23682873134Snicm 		if (c->session != NULL)
2377d5939e2Snicm 			return (0);
2387d5939e2Snicm 	}
2397d5939e2Snicm 
2407d5939e2Snicm 	/*
2417d5939e2Snicm 	 * No attached clients therefore want to exit - flush any waiting
2427d5939e2Snicm 	 * clients but don't actually exit until they've gone.
2437d5939e2Snicm 	 */
2447d5939e2Snicm 	cmd_wait_for_flush();
24582873134Snicm 	if (!TAILQ_EMPTY(&clients))
24630da262fSnicm 		return (0);
2477d5939e2Snicm 
24830da262fSnicm 	return (1);
249311827fbSnicm }
250311827fbSnicm 
2514d9325ceSnicm /* Exit the server by killing all clients and windows. */
2529883b791Snicm static void
2534d9325ceSnicm server_send_exit(void)
254311827fbSnicm {
25582873134Snicm 	struct client	*c, *c1;
25682873134Snicm 	struct session	*s, *s1;
257311827fbSnicm 
2587d5939e2Snicm 	cmd_wait_for_flush();
2597d5939e2Snicm 
26082873134Snicm 	TAILQ_FOREACH_SAFE(c, &clients, entry, c1) {
2615b8ac713Snicm 		if (c->flags & CLIENT_SUSPENDED)
2627724d0b0Snicm 			server_client_lost(c);
2637724d0b0Snicm 		else
2645b8ac713Snicm 			proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0);
2657724d0b0Snicm 		c->session = NULL;
2668148c7feSnicm 	}
267311827fbSnicm 
26882873134Snicm 	RB_FOREACH_SAFE(s, sessions, &sessions, s1)
269bc4816c6Snicm 		session_destroy(s, __func__);
270311827fbSnicm }
271311827fbSnicm 
2727724d0b0Snicm /* Update socket execute permissions based on whether sessions are attached. */
27337dfd443Snicm void
2747724d0b0Snicm server_update_socket(void)
2757724d0b0Snicm {
2767724d0b0Snicm 	struct session	*s;
2777724d0b0Snicm 	static int	 last = -1;
2786d9987f5Snicm 	int		 n, mode;
2796d9987f5Snicm 	struct stat      sb;
2807724d0b0Snicm 
2817724d0b0Snicm 	n = 0;
28259996dc3Snicm 	RB_FOREACH(s, sessions, &sessions) {
28359996dc3Snicm 		if (!(s->flags & SESSION_UNATTACHED)) {
2847724d0b0Snicm 			n++;
2857724d0b0Snicm 			break;
2867724d0b0Snicm 		}
2877724d0b0Snicm 	}
2887724d0b0Snicm 
2897724d0b0Snicm 	if (n != last) {
2907724d0b0Snicm 		last = n;
2916d9987f5Snicm 
2926d9987f5Snicm 		if (stat(socket_path, &sb) != 0)
2936d9987f5Snicm 			return;
294d61e0664Ssemarie 		mode = sb.st_mode & ACCESSPERMS;
2956d9987f5Snicm 		if (n != 0) {
2966d9987f5Snicm 			if (mode & S_IRUSR)
2976d9987f5Snicm 				mode |= S_IXUSR;
2986d9987f5Snicm 			if (mode & S_IRGRP)
2996d9987f5Snicm 				mode |= S_IXGRP;
3006d9987f5Snicm 			if (mode & S_IROTH)
3016d9987f5Snicm 				mode |= S_IXOTH;
3026d9987f5Snicm 		} else
3036d9987f5Snicm 			mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
3046d9987f5Snicm 		chmod(socket_path, mode);
3057724d0b0Snicm 	}
3067724d0b0Snicm }
3077724d0b0Snicm 
3087724d0b0Snicm /* Callback for server socket. */
3099883b791Snicm static void
310d0e2e7f1Snicm server_accept(int fd, short events, __unused void *data)
3117724d0b0Snicm {
3127724d0b0Snicm 	struct sockaddr_storage	sa;
3137724d0b0Snicm 	socklen_t		slen = sizeof sa;
3147724d0b0Snicm 	int			newfd;
3157724d0b0Snicm 
316a06e463cSnicm 	server_add_accept(0);
3177724d0b0Snicm 	if (!(events & EV_READ))
3187724d0b0Snicm 		return;
3197724d0b0Snicm 
3207724d0b0Snicm 	newfd = accept(fd, (struct sockaddr *) &sa, &slen);
3217724d0b0Snicm 	if (newfd == -1) {
3227724d0b0Snicm 		if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
3237724d0b0Snicm 			return;
324a06e463cSnicm 		if (errno == ENFILE || errno == EMFILE) {
325a06e463cSnicm 			/* Delete and don't try again for 1 second. */
326a06e463cSnicm 			server_add_accept(1);
327a06e463cSnicm 			return;
328a06e463cSnicm 		}
3297724d0b0Snicm 		fatal("accept failed");
3307724d0b0Snicm 	}
3314d9325ceSnicm 	if (server_exit) {
3327724d0b0Snicm 		close(newfd);
3337724d0b0Snicm 		return;
3347724d0b0Snicm 	}
3357724d0b0Snicm 	server_client_create(newfd);
3367724d0b0Snicm }
3377724d0b0Snicm 
338a06e463cSnicm /*
339a06e463cSnicm  * Add accept event. If timeout is nonzero, add as a timeout instead of a read
340a06e463cSnicm  * event - used to backoff when running out of file descriptors.
341a06e463cSnicm  */
342a06e463cSnicm void
343a06e463cSnicm server_add_accept(int timeout)
344a06e463cSnicm {
345a06e463cSnicm 	struct timeval tv = { timeout, 0 };
346a06e463cSnicm 
347a06e463cSnicm 	if (event_initialized(&server_ev_accept))
348a06e463cSnicm 		event_del(&server_ev_accept);
349a06e463cSnicm 
350a06e463cSnicm 	if (timeout == 0) {
3515b8ac713Snicm 		event_set(&server_ev_accept, server_fd, EV_READ, server_accept,
3525b8ac713Snicm 		    NULL);
353a06e463cSnicm 		event_add(&server_ev_accept, NULL);
354a06e463cSnicm 	} else {
3555b8ac713Snicm 		event_set(&server_ev_accept, server_fd, EV_TIMEOUT,
3565b8ac713Snicm 		    server_accept, NULL);
357a06e463cSnicm 		event_add(&server_ev_accept, &tv);
358a06e463cSnicm 	}
359a06e463cSnicm }
360a06e463cSnicm 
3617724d0b0Snicm /* Signal handler. */
3629883b791Snicm static void
3635b8ac713Snicm server_signal(int sig)
3647724d0b0Snicm {
365d53cf33dSnicm 	int	fd;
3665235cd7fSnicm 
367bc4816c6Snicm 	log_debug("%s: %s", __func__, strsignal(sig));
3687724d0b0Snicm 	switch (sig) {
3697724d0b0Snicm 	case SIGTERM:
3704d9325ceSnicm 		server_exit = 1;
3714d9325ceSnicm 		server_send_exit();
3727724d0b0Snicm 		break;
3737724d0b0Snicm 	case SIGCHLD:
3747724d0b0Snicm 		server_child_signal();
3757724d0b0Snicm 		break;
3767724d0b0Snicm 	case SIGUSR1:
3777724d0b0Snicm 		event_del(&server_ev_accept);
378d53cf33dSnicm 		fd = server_create_socket();
379d53cf33dSnicm 		if (fd != -1) {
3807724d0b0Snicm 			close(server_fd);
381d53cf33dSnicm 			server_fd = fd;
382d53cf33dSnicm 			server_update_socket();
383d53cf33dSnicm 		}
384a06e463cSnicm 		server_add_accept(0);
3857724d0b0Snicm 		break;
38679c9b201Snicm 	case SIGUSR2:
38779c9b201Snicm 		proc_toggle_log(server_proc);
38879c9b201Snicm 		break;
3897724d0b0Snicm 	}
3907724d0b0Snicm }
3917724d0b0Snicm 
3927724d0b0Snicm /* Handle SIGCHLD. */
3939883b791Snicm static void
3947724d0b0Snicm server_child_signal(void)
3957724d0b0Snicm {
3967724d0b0Snicm 	int	 status;
3977724d0b0Snicm 	pid_t	 pid;
3987724d0b0Snicm 
3997724d0b0Snicm 	for (;;) {
4007724d0b0Snicm 		switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
4017724d0b0Snicm 		case -1:
4027724d0b0Snicm 			if (errno == ECHILD)
4037724d0b0Snicm 				return;
4047724d0b0Snicm 			fatal("waitpid failed");
4057724d0b0Snicm 		case 0:
4067724d0b0Snicm 			return;
4077724d0b0Snicm 		}
4087724d0b0Snicm 		if (WIFSTOPPED(status))
4097724d0b0Snicm 			server_child_stopped(pid, status);
410a236b623Snicm 		else if (WIFEXITED(status) || WIFSIGNALED(status))
4117724d0b0Snicm 			server_child_exited(pid, status);
4127724d0b0Snicm 	}
4137724d0b0Snicm }
4147724d0b0Snicm 
4157724d0b0Snicm /* Handle exited children. */
4169883b791Snicm static void
4177724d0b0Snicm server_child_exited(pid_t pid, int status)
418311827fbSnicm {
419ae69181dSnicm 	struct window		*w, *w1;
42089654e7cSnicm 	struct window_pane	*wp;
4217724d0b0Snicm 	struct job		*job;
4227724d0b0Snicm 
423ae69181dSnicm 	RB_FOREACH_SAFE(w, windows, &windows, w1) {
4247724d0b0Snicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
4257724d0b0Snicm 			if (wp->pid == pid) {
4263e459475Snicm 				wp->status = status;
427*514fd64dSnicm 				wp->flags |= PANE_STATUSREADY;
428f35e5f52Snicm 
429f35e5f52Snicm 				log_debug("%%%u exited", wp->id);
430f35e5f52Snicm 				wp->flags |= PANE_EXITED;
431f35e5f52Snicm 
432f35e5f52Snicm 				if (window_pane_destroy_ready(wp))
4334fc586aaSnicm 					server_destroy_pane(wp, 1);
4347c6e570cSnicm 				break;
4357724d0b0Snicm 			}
4367724d0b0Snicm 		}
4377724d0b0Snicm 	}
4387724d0b0Snicm 
4391a432fefSnicm 	LIST_FOREACH(job, &all_jobs, entry) {
4407724d0b0Snicm 		if (pid == job->pid) {
44124abd014Snicm 			job_died(job, status);	/* might free job */
44224abd014Snicm 			break;
4437724d0b0Snicm 		}
4447724d0b0Snicm 	}
4457724d0b0Snicm }
4467724d0b0Snicm 
4477724d0b0Snicm /* Handle stopped children. */
4489883b791Snicm static void
4497724d0b0Snicm server_child_stopped(pid_t pid, int status)
4507724d0b0Snicm {
4517724d0b0Snicm 	struct window		*w;
4527724d0b0Snicm 	struct window_pane	*wp;
4537724d0b0Snicm 
4547724d0b0Snicm 	if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
4557724d0b0Snicm 		return;
4567724d0b0Snicm 
457ae69181dSnicm 	RB_FOREACH(w, windows, &windows) {
4587724d0b0Snicm 		TAILQ_FOREACH(wp, &w->panes, entry) {
4597724d0b0Snicm 			if (wp->pid == pid) {
4607724d0b0Snicm 				if (killpg(pid, SIGCONT) != 0)
4617724d0b0Snicm 					kill(pid, SIGCONT);
4627724d0b0Snicm 			}
4637724d0b0Snicm 		}
4647724d0b0Snicm 	}
4657724d0b0Snicm }
466