mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 00:56:10 +00:00 
			
		
		
		
	Initial changes to move tmux to libevent.
This moves the client-side loops are pretty much fully over to event-based only (tmux.c and client.c) but server-side (server.c and friends) treats libevent as a sort of clever poll, waking up after every event to run various things. Moving the server stuff over to bufferevents and timers and so on will come later.
This commit is contained in:
		
							
								
								
									
										601
									
								
								server.c
									
									
									
									
									
								
							
							
						
						
									
										601
									
								
								server.c
									
									
									
									
									
								
							@@ -24,6 +24,7 @@
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <event.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <paths.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
@@ -45,112 +46,28 @@
 | 
			
		||||
struct clients	 clients;
 | 
			
		||||
struct clients	 dead_clients;
 | 
			
		||||
 | 
			
		||||
/* Mapping of a pollfd to an fd independent of its position in the array. */
 | 
			
		||||
struct poll_item {
 | 
			
		||||
	int	 fd;
 | 
			
		||||
	int	 events;
 | 
			
		||||
 | 
			
		||||
	void	 (*fn)(int, int, void *);
 | 
			
		||||
	void	*data;
 | 
			
		||||
 | 
			
		||||
	RB_ENTRY(poll_item) entry;
 | 
			
		||||
};
 | 
			
		||||
RB_HEAD(poll_items, poll_item) poll_items;
 | 
			
		||||
 | 
			
		||||
int		 server_poll_cmp(struct poll_item *, struct poll_item *);
 | 
			
		||||
struct poll_item*server_poll_lookup(int);
 | 
			
		||||
struct pollfd	*server_poll_flatten(int *);
 | 
			
		||||
void		 server_poll_dispatch(struct pollfd *, int);
 | 
			
		||||
void		 server_poll_reset(void);
 | 
			
		||||
RB_PROTOTYPE(poll_items, poll_item, entry, server_poll_cmp);
 | 
			
		||||
RB_GENERATE(poll_items, poll_item, entry, server_poll_cmp);
 | 
			
		||||
int		 server_fd;
 | 
			
		||||
int		 server_shutdown;
 | 
			
		||||
struct event	 server_ev_accept;
 | 
			
		||||
struct event	 server_ev_sigterm;
 | 
			
		||||
struct event	 server_ev_sigusr1;
 | 
			
		||||
struct event	 server_ev_sigchld;
 | 
			
		||||
struct event	 server_ev_second;
 | 
			
		||||
 | 
			
		||||
int		 server_create_socket(void);
 | 
			
		||||
void		 server_callback(int, int, void *);
 | 
			
		||||
int		 server_main(int);
 | 
			
		||||
void		 server_shutdown(void);
 | 
			
		||||
void		 server_loop(void);
 | 
			
		||||
int		 server_should_shutdown(void);
 | 
			
		||||
void		 server_child_signal(void);
 | 
			
		||||
void		 server_send_shutdown(void);
 | 
			
		||||
void		 server_clean_dead(void);
 | 
			
		||||
void		 server_second_timers(void);
 | 
			
		||||
int		 server_update_socket(void);
 | 
			
		||||
void		 server_accept_callback(int, short, void *);
 | 
			
		||||
void		 server_signal_callback(int, short, void *);
 | 
			
		||||
void		 server_child_signal(void);
 | 
			
		||||
void		 server_child_exited(pid_t, int);
 | 
			
		||||
void		 server_child_stopped(pid_t, int);
 | 
			
		||||
void		 server_second_callback(int, short, void *);
 | 
			
		||||
void		 server_lock_server(void);
 | 
			
		||||
void		 server_lock_sessions(void);
 | 
			
		||||
int		 server_update_socket(void);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
server_poll_cmp(struct poll_item *pitem1, struct poll_item *pitem2)
 | 
			
		||||
{
 | 
			
		||||
	return (pitem1->fd - pitem2->fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
server_poll_add(int fd, int events, void (*fn)(int, int, void *), void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct poll_item	*pitem;
 | 
			
		||||
 | 
			
		||||
	pitem = xmalloc(sizeof *pitem);
 | 
			
		||||
	pitem->fd = fd;
 | 
			
		||||
	pitem->events = events;
 | 
			
		||||
 | 
			
		||||
	pitem->fn = fn;
 | 
			
		||||
	pitem->data = data;
 | 
			
		||||
 | 
			
		||||
	RB_INSERT(poll_items, &poll_items, pitem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct poll_item *
 | 
			
		||||
server_poll_lookup(int fd)
 | 
			
		||||
{
 | 
			
		||||
	struct poll_item	pitem;
 | 
			
		||||
 | 
			
		||||
	pitem.fd = fd;
 | 
			
		||||
	return (RB_FIND(poll_items, &poll_items, &pitem));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct pollfd *
 | 
			
		||||
server_poll_flatten(int *nfds)
 | 
			
		||||
{
 | 
			
		||||
	struct poll_item	*pitem;
 | 
			
		||||
	struct pollfd		*pfds;
 | 
			
		||||
 | 
			
		||||
	pfds = NULL;
 | 
			
		||||
	*nfds = 0;
 | 
			
		||||
	RB_FOREACH(pitem, poll_items, &poll_items) {
 | 
			
		||||
		pfds = xrealloc(pfds, (*nfds) + 1, sizeof *pfds);
 | 
			
		||||
		pfds[*nfds].fd = pitem->fd;
 | 
			
		||||
		pfds[*nfds].events = pitem->events;
 | 
			
		||||
		(*nfds)++;
 | 
			
		||||
	}
 | 
			
		||||
	return (pfds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
server_poll_dispatch(struct pollfd *pfds, int nfds)
 | 
			
		||||
{
 | 
			
		||||
	struct poll_item	*pitem;
 | 
			
		||||
	struct pollfd		*pfd;
 | 
			
		||||
 | 
			
		||||
	while (nfds > 0) {
 | 
			
		||||
		pfd = &pfds[--nfds];
 | 
			
		||||
		if (pfd->revents != 0) {
 | 
			
		||||
			pitem = server_poll_lookup(pfd->fd);
 | 
			
		||||
			pitem->fn(pitem->fd, pfd->revents, pitem->data);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	xfree(pfds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
server_poll_reset(void)
 | 
			
		||||
{
 | 
			
		||||
	struct poll_item	*pitem;
 | 
			
		||||
 | 
			
		||||
	while (!RB_EMPTY(&poll_items)) {
 | 
			
		||||
		pitem = RB_ROOT(&poll_items);
 | 
			
		||||
		RB_REMOVE(poll_items, &poll_items, pitem);
 | 
			
		||||
		xfree(pitem);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Create server socket. */
 | 
			
		||||
int
 | 
			
		||||
@@ -191,40 +108,14 @@ server_create_socket(void)
 | 
			
		||||
	return (fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Callback for server socket. */
 | 
			
		||||
void
 | 
			
		||||
server_callback(int fd, int events, unused void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_storage	sa;
 | 
			
		||||
	socklen_t		slen = sizeof sa;
 | 
			
		||||
	int			newfd;
 | 
			
		||||
 | 
			
		||||
	if (events & (POLLERR|POLLNVAL|POLLHUP))
 | 
			
		||||
		fatalx("lost server socket");
 | 
			
		||||
	if (!(events & POLLIN))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	newfd = accept(fd, (struct sockaddr *) &sa, &slen);
 | 
			
		||||
	if (newfd == -1) {
 | 
			
		||||
		if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
 | 
			
		||||
			return;
 | 
			
		||||
		fatal("accept failed");
 | 
			
		||||
	}
 | 
			
		||||
	if (sigterm) {
 | 
			
		||||
		close(newfd);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	server_client_create(newfd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Fork new server. */
 | 
			
		||||
int
 | 
			
		||||
server_start(char *path)
 | 
			
		||||
{
 | 
			
		||||
	struct client	*c;
 | 
			
		||||
	int		 pair[2], srv_fd;
 | 
			
		||||
	char		*cause;
 | 
			
		||||
	char		 rpathbuf[MAXPATHLEN];
 | 
			
		||||
	int		 pair[2];
 | 
			
		||||
	char		*cause, rpathbuf[MAXPATHLEN];
 | 
			
		||||
	struct timeval	 tv;
 | 
			
		||||
 | 
			
		||||
	/* The first client is special and gets a socketpair; create it. */
 | 
			
		||||
	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
 | 
			
		||||
@@ -269,7 +160,7 @@ server_start(char *path)
 | 
			
		||||
	log_debug("socket path %s", socket_path);
 | 
			
		||||
	setproctitle("server (%s)", rpathbuf);
 | 
			
		||||
 | 
			
		||||
	srv_fd = server_create_socket();
 | 
			
		||||
	server_fd = server_create_socket();
 | 
			
		||||
	server_client_create(pair[1]);
 | 
			
		||||
 | 
			
		||||
	if (access(SYSTEM_CFG, R_OK) == 0) {
 | 
			
		||||
@@ -282,7 +173,20 @@ server_start(char *path)
 | 
			
		||||
	if (cfg_file != NULL && load_cfg(cfg_file, NULL, &cause) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	exit(server_main(srv_fd));
 | 
			
		||||
	event_init();
 | 
			
		||||
 | 
			
		||||
	event_set(&server_ev_accept,
 | 
			
		||||
	    server_fd, EV_READ|EV_PERSIST, server_accept_callback, NULL);
 | 
			
		||||
	event_add(&server_ev_accept, NULL);
 | 
			
		||||
 | 
			
		||||
	memset(&tv, 0, sizeof tv);
 | 
			
		||||
	tv.tv_sec = 1;
 | 
			
		||||
	evtimer_set(&server_ev_second, server_second_callback, NULL);
 | 
			
		||||
	evtimer_add(&server_ev_second, &tv);
 | 
			
		||||
 | 
			
		||||
	server_signal_set();
 | 
			
		||||
	server_loop();
 | 
			
		||||
	exit(0);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	/* Write the error and shutdown the server. */
 | 
			
		||||
@@ -291,150 +195,39 @@ error:
 | 
			
		||||
	server_write_error(c, cause);
 | 
			
		||||
	xfree(cause);
 | 
			
		||||
 | 
			
		||||
	sigterm = 1;
 | 
			
		||||
	server_shutdown();
 | 
			
		||||
	server_shutdown = 1;
 | 
			
		||||
 | 
			
		||||
	exit(server_main(srv_fd));
 | 
			
		||||
	server_signal_set();
 | 
			
		||||
	server_loop();
 | 
			
		||||
	exit(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Main server loop. */
 | 
			
		||||
int
 | 
			
		||||
server_main(int srv_fd)
 | 
			
		||||
void
 | 
			
		||||
server_loop(void)
 | 
			
		||||
{
 | 
			
		||||
	struct pollfd	*pfds;
 | 
			
		||||
	int		 nfds, xtimeout;
 | 
			
		||||
	u_int		 i;
 | 
			
		||||
	time_t		 now, last;
 | 
			
		||||
	struct timeval	tv;
 | 
			
		||||
 | 
			
		||||
	siginit();
 | 
			
		||||
	log_debug("server socket is %d", srv_fd);
 | 
			
		||||
	memset(&tv, 0, sizeof tv);
 | 
			
		||||
	tv.tv_usec = POLL_TIMEOUT * 1000;
 | 
			
		||||
 | 
			
		||||
	last = time(NULL);
 | 
			
		||||
	while (!server_should_shutdown()) {
 | 
			
		||||
		server_update_socket();
 | 
			
		||||
 | 
			
		||||
	pfds = NULL;
 | 
			
		||||
	for (;;) {
 | 
			
		||||
		/* If sigterm, kill all windows and clients. */
 | 
			
		||||
		if (sigterm)
 | 
			
		||||
			server_shutdown();
 | 
			
		||||
 | 
			
		||||
		/* Stop if no sessions or clients left. */
 | 
			
		||||
		if (server_should_shutdown())
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		/* Handle child exit. */
 | 
			
		||||
		if (sigchld) {
 | 
			
		||||
			sigchld = 0;
 | 
			
		||||
			server_child_signal();
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Recreate socket on SIGUSR1. */
 | 
			
		||||
		if (sigusr1) {
 | 
			
		||||
			sigusr1 = 0;
 | 
			
		||||
			close(srv_fd);
 | 
			
		||||
			srv_fd = server_create_socket();
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Initialise pollfd array and add server socket. */
 | 
			
		||||
		server_poll_reset();
 | 
			
		||||
		server_poll_add(srv_fd, POLLIN, server_callback, NULL);
 | 
			
		||||
 | 
			
		||||
		/* Fill window and client sockets. */
 | 
			
		||||
		server_job_prepare();
 | 
			
		||||
		server_window_prepare();
 | 
			
		||||
		server_client_prepare();
 | 
			
		||||
		
 | 
			
		||||
		/* Update socket permissions. */
 | 
			
		||||
		xtimeout = INFTIM;
 | 
			
		||||
		if (server_update_socket() != 0)
 | 
			
		||||
			xtimeout = POLL_TIMEOUT;
 | 
			
		||||
 | 
			
		||||
		/* Do the poll. */
 | 
			
		||||
		pfds = server_poll_flatten(&nfds);
 | 
			
		||||
		if (poll(pfds, nfds, xtimeout) == -1) {
 | 
			
		||||
			if (errno == EAGAIN || errno == EINTR)
 | 
			
		||||
				continue;
 | 
			
		||||
			fatal("poll failed");
 | 
			
		||||
		}
 | 
			
		||||
		server_poll_dispatch(pfds, nfds);
 | 
			
		||||
		event_loopexit(&tv);
 | 
			
		||||
		event_loop(EVLOOP_ONCE);
 | 
			
		||||
 | 
			
		||||
		/* Call second-based timers. */
 | 
			
		||||
		now = time(NULL);
 | 
			
		||||
		if (now != last) {
 | 
			
		||||
			last = now;
 | 
			
		||||
			server_second_timers();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Run once-per-loop events. */
 | 
			
		||||
		server_job_loop();
 | 
			
		||||
		server_window_loop();
 | 
			
		||||
		server_client_loop();
 | 
			
		||||
 | 
			
		||||
		/* Collect any unset key bindings. */
 | 
			
		||||
		key_bindings_clean();
 | 
			
		||||
 | 
			
		||||
		/* Collect dead clients and sessions. */
 | 
			
		||||
		server_clean_dead();
 | 
			
		||||
	}
 | 
			
		||||
	server_poll_reset();
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
 | 
			
		||||
		if (ARRAY_ITEM(&sessions, i) != NULL)
 | 
			
		||||
			session_destroy(ARRAY_ITEM(&sessions, i));
 | 
			
		||||
	}
 | 
			
		||||
	ARRAY_FREE(&sessions);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
 | 
			
		||||
		if (ARRAY_ITEM(&clients, i) != NULL)
 | 
			
		||||
			server_client_lost(ARRAY_ITEM(&clients, i));
 | 
			
		||||
	}
 | 
			
		||||
	ARRAY_FREE(&clients);
 | 
			
		||||
 | 
			
		||||
	mode_key_free_trees();
 | 
			
		||||
	key_bindings_free();
 | 
			
		||||
 | 
			
		||||
	close(srv_fd);
 | 
			
		||||
 | 
			
		||||
	unlink(socket_path);
 | 
			
		||||
	xfree(socket_path);
 | 
			
		||||
 | 
			
		||||
	options_free(&global_s_options);
 | 
			
		||||
	options_free(&global_w_options);
 | 
			
		||||
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Kill all clients. */
 | 
			
		||||
void
 | 
			
		||||
server_shutdown(void)
 | 
			
		||||
{
 | 
			
		||||
	struct session	*s;
 | 
			
		||||
	struct client	*c;
 | 
			
		||||
	u_int		 i, j;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
 | 
			
		||||
		c = ARRAY_ITEM(&clients, i);
 | 
			
		||||
		if (c != NULL) {
 | 
			
		||||
			if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED))
 | 
			
		||||
				server_client_lost(c);
 | 
			
		||||
			else
 | 
			
		||||
				server_write_client(c, MSG_SHUTDOWN, NULL, 0);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
 | 
			
		||||
		s = ARRAY_ITEM(&sessions, i);
 | 
			
		||||
		for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
 | 
			
		||||
			c = ARRAY_ITEM(&clients, j);
 | 
			
		||||
			if (c != NULL && c->session == s) {
 | 
			
		||||
				s = NULL;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (s != NULL)
 | 
			
		||||
			session_destroy(s);
 | 
			
		||||
	}
 | 
			
		||||
	} 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the server should be shutting down (no more clients or windows). */
 | 
			
		||||
@@ -454,49 +247,28 @@ server_should_shutdown(void)
 | 
			
		||||
	return (1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle SIGCHLD. */
 | 
			
		||||
/* Shutdown the server by killing all clients and windows. */
 | 
			
		||||
void
 | 
			
		||||
server_child_signal(void)
 | 
			
		||||
server_send_shutdown(void)
 | 
			
		||||
{
 | 
			
		||||
	struct window		*w;
 | 
			
		||||
	struct window_pane	*wp;
 | 
			
		||||
	struct job		*job;
 | 
			
		||||
	int		 	 status;
 | 
			
		||||
	pid_t		 	 pid;
 | 
			
		||||
	u_int		 	 i;
 | 
			
		||||
	struct client	*c;
 | 
			
		||||
	struct session	*s;
 | 
			
		||||
	u_int		 i;
 | 
			
		||||
 | 
			
		||||
	for (;;) {
 | 
			
		||||
		switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
 | 
			
		||||
		case -1:
 | 
			
		||||
			if (errno == ECHILD)
 | 
			
		||||
				return;
 | 
			
		||||
			fatal("waitpid failed");
 | 
			
		||||
		case 0:
 | 
			
		||||
			return;
 | 
			
		||||
	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
 | 
			
		||||
		c = ARRAY_ITEM(&clients, i);
 | 
			
		||||
		if (c != NULL) {
 | 
			
		||||
			if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED))
 | 
			
		||||
				server_client_lost(c);
 | 
			
		||||
			else
 | 
			
		||||
				server_write_client(c, MSG_SHUTDOWN, NULL, 0);
 | 
			
		||||
			c->session = NULL;
 | 
			
		||||
		}
 | 
			
		||||
		if (!WIFSTOPPED(status)) {
 | 
			
		||||
			SLIST_FOREACH(job, &all_jobs, lentry) {
 | 
			
		||||
				if (pid == job->pid) {
 | 
			
		||||
					job->pid = -1;
 | 
			
		||||
					job->status = status;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
 | 
			
		||||
			continue;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
 | 
			
		||||
			w = ARRAY_ITEM(&windows, i);
 | 
			
		||||
			if (w == NULL)
 | 
			
		||||
				continue;
 | 
			
		||||
			TAILQ_FOREACH(wp, &w->panes, entry) {
 | 
			
		||||
				if (wp->pid == pid) {
 | 
			
		||||
					if (killpg(pid, SIGCONT) != 0)
 | 
			
		||||
						kill(pid, SIGCONT);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
 | 
			
		||||
		if ((s = ARRAY_ITEM(&sessions, i)) != NULL)
 | 
			
		||||
			session_destroy(s);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -525,12 +297,215 @@ server_clean_dead(void)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Call any once-per-second timers. */
 | 
			
		||||
/* Update socket execute permissions based on whether sessions are attached. */
 | 
			
		||||
int
 | 
			
		||||
server_update_socket(void)
 | 
			
		||||
{
 | 
			
		||||
	struct session	*s;
 | 
			
		||||
	u_int		 i;
 | 
			
		||||
	static int	 last = -1;
 | 
			
		||||
	int		 n;
 | 
			
		||||
 | 
			
		||||
	n = 0;
 | 
			
		||||
	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
 | 
			
		||||
		s = ARRAY_ITEM(&sessions, i);
 | 
			
		||||
		if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
 | 
			
		||||
			n++;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (n != last) {
 | 
			
		||||
		last = n;
 | 
			
		||||
		if (n != 0)
 | 
			
		||||
			chmod(socket_path, S_IRWXU);
 | 
			
		||||
		else
 | 
			
		||||
			chmod(socket_path, S_IRUSR|S_IWUSR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Callback for server socket. */
 | 
			
		||||
void
 | 
			
		||||
server_second_timers(void)
 | 
			
		||||
server_accept_callback(int fd, short events, unused void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_storage	sa;
 | 
			
		||||
	socklen_t		slen = sizeof sa;
 | 
			
		||||
	int			newfd;
 | 
			
		||||
 | 
			
		||||
	if (!(events & EV_READ))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	newfd = accept(fd, (struct sockaddr *) &sa, &slen);
 | 
			
		||||
	if (newfd == -1) {
 | 
			
		||||
		if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
 | 
			
		||||
			return;
 | 
			
		||||
		fatal("accept failed");
 | 
			
		||||
	}
 | 
			
		||||
	if (server_shutdown) {
 | 
			
		||||
		close(newfd);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	server_client_create(newfd);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Set up server signal handling. */
 | 
			
		||||
void
 | 
			
		||||
server_signal_set(void)
 | 
			
		||||
{
 | 
			
		||||
	struct sigaction	 sigact;
 | 
			
		||||
 | 
			
		||||
	memset(&sigact, 0, sizeof sigact);
 | 
			
		||||
	sigemptyset(&sigact.sa_mask);
 | 
			
		||||
	sigact.sa_flags = SA_RESTART;
 | 
			
		||||
	sigact.sa_handler = SIG_IGN;
 | 
			
		||||
	if (sigaction(SIGINT, &sigact, NULL) != 0)
 | 
			
		||||
		fatal("sigaction failed");
 | 
			
		||||
	if (sigaction(SIGPIPE, &sigact, NULL) != 0)
 | 
			
		||||
		fatal("sigaction failed");
 | 
			
		||||
	if (sigaction(SIGUSR2, &sigact, NULL) != 0)
 | 
			
		||||
		fatal("sigaction failed");
 | 
			
		||||
	if (sigaction(SIGTSTP, &sigact, NULL) != 0)
 | 
			
		||||
		fatal("sigaction failed");
 | 
			
		||||
 | 
			
		||||
	signal_set(&server_ev_sigchld, SIGCHLD, server_signal_callback, NULL);
 | 
			
		||||
	signal_add(&server_ev_sigchld, NULL);
 | 
			
		||||
	signal_set(&server_ev_sigterm, SIGTERM, server_signal_callback, NULL);
 | 
			
		||||
	signal_add(&server_ev_sigterm, NULL);
 | 
			
		||||
	signal_set(&server_ev_sigusr1, SIGUSR1, server_signal_callback, NULL);
 | 
			
		||||
	signal_add(&server_ev_sigusr1, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Destroy server signal events. */
 | 
			
		||||
void
 | 
			
		||||
server_signal_clear(void)
 | 
			
		||||
{
 | 
			
		||||
	struct sigaction	 sigact;
 | 
			
		||||
 | 
			
		||||
	memset(&sigact, 0, sizeof sigact);
 | 
			
		||||
	sigemptyset(&sigact.sa_mask);
 | 
			
		||||
	sigact.sa_flags = SA_RESTART;
 | 
			
		||||
	sigact.sa_handler = SIG_DFL;
 | 
			
		||||
	if (sigaction(SIGINT, &sigact, NULL) != 0)
 | 
			
		||||
		fatal("sigaction failed");
 | 
			
		||||
	if (sigaction(SIGPIPE, &sigact, NULL) != 0)
 | 
			
		||||
		fatal("sigaction failed");
 | 
			
		||||
	if (sigaction(SIGUSR2, &sigact, NULL) != 0)
 | 
			
		||||
		fatal("sigaction failed");
 | 
			
		||||
	if (sigaction(SIGTSTP, &sigact, NULL) != 0)
 | 
			
		||||
		fatal("sigaction failed");
 | 
			
		||||
 | 
			
		||||
	signal_del(&server_ev_sigchld);
 | 
			
		||||
	signal_del(&server_ev_sigterm);
 | 
			
		||||
	signal_del(&server_ev_sigusr1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Signal handler. */
 | 
			
		||||
void
 | 
			
		||||
server_signal_callback(int sig, unused short events, unused void *data)
 | 
			
		||||
{
 | 
			
		||||
	switch (sig) {
 | 
			
		||||
	case SIGTERM:
 | 
			
		||||
		server_shutdown = 1;
 | 
			
		||||
		server_send_shutdown();
 | 
			
		||||
		break;
 | 
			
		||||
	case SIGCHLD:
 | 
			
		||||
		server_child_signal();
 | 
			
		||||
		break;
 | 
			
		||||
	case SIGUSR1:
 | 
			
		||||
		event_del(&server_ev_accept);
 | 
			
		||||
		close(server_fd);
 | 
			
		||||
		server_fd = server_create_socket();
 | 
			
		||||
		event_set(&server_ev_accept, server_fd,
 | 
			
		||||
		    EV_READ|EV_PERSIST, server_accept_callback, NULL);
 | 
			
		||||
		event_add(&server_ev_accept, NULL);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle SIGCHLD. */
 | 
			
		||||
void
 | 
			
		||||
server_child_signal(void)
 | 
			
		||||
{
 | 
			
		||||
	int	 status;
 | 
			
		||||
	pid_t	 pid;
 | 
			
		||||
 | 
			
		||||
	for (;;) {
 | 
			
		||||
		switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
 | 
			
		||||
		case -1:
 | 
			
		||||
			if (errno == ECHILD)
 | 
			
		||||
				return;
 | 
			
		||||
			fatal("waitpid failed");
 | 
			
		||||
		case 0:
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (WIFSTOPPED(status))
 | 
			
		||||
			server_child_stopped(pid, status);
 | 
			
		||||
		else if (WIFEXITED(status))
 | 
			
		||||
			server_child_exited(pid, status);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle exited children. */
 | 
			
		||||
void
 | 
			
		||||
server_child_exited(pid_t pid, int status)
 | 
			
		||||
{
 | 
			
		||||
	struct window		*w;
 | 
			
		||||
	struct window_pane	*wp;
 | 
			
		||||
	struct job		*job;
 | 
			
		||||
	u_int		 	 i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
 | 
			
		||||
		if ((w = ARRAY_ITEM(&windows, i)) == NULL)
 | 
			
		||||
			continue;
 | 
			
		||||
		TAILQ_FOREACH(wp, &w->panes, entry) {
 | 
			
		||||
			if (wp->pid == pid) {
 | 
			
		||||
				close(wp->fd);
 | 
			
		||||
				wp->fd = -1;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}		
 | 
			
		||||
 | 
			
		||||
	SLIST_FOREACH(job, &all_jobs, lentry) {
 | 
			
		||||
		if (pid == job->pid) {
 | 
			
		||||
			job->pid = -1;
 | 
			
		||||
			job->status = status;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle stopped children. */
 | 
			
		||||
void
 | 
			
		||||
server_child_stopped(pid_t pid, int status)
 | 
			
		||||
{
 | 
			
		||||
	struct window		*w;
 | 
			
		||||
	struct window_pane	*wp;
 | 
			
		||||
	u_int			 i;
 | 
			
		||||
 | 
			
		||||
	if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
 | 
			
		||||
		if ((w = ARRAY_ITEM(&windows, i)) == NULL)
 | 
			
		||||
			continue;
 | 
			
		||||
		TAILQ_FOREACH(wp, &w->panes, entry) {
 | 
			
		||||
			if (wp->pid == pid) {
 | 
			
		||||
				if (killpg(pid, SIGCONT) != 0)
 | 
			
		||||
					kill(pid, SIGCONT);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle once-per-second timer events. */
 | 
			
		||||
void
 | 
			
		||||
server_second_callback(unused int fd, unused short events, unused void *arg)
 | 
			
		||||
{
 | 
			
		||||
	struct window		*w;
 | 
			
		||||
	struct window_pane	*wp;
 | 
			
		||||
	struct timeval		 tv;
 | 
			
		||||
	u_int		 	 i;
 | 
			
		||||
 | 
			
		||||
	if (options_get_number(&global_s_options, "lock-server"))
 | 
			
		||||
@@ -548,6 +523,11 @@ server_second_timers(void)
 | 
			
		||||
				wp->mode->timer(wp);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	evtimer_del(&server_ev_second);
 | 
			
		||||
	memset(&tv, 0, sizeof tv);
 | 
			
		||||
	tv.tv_sec = 1;
 | 
			
		||||
	evtimer_add(&server_ev_second, &tv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Lock the server if ALL sessions have hit the time limit. */
 | 
			
		||||
@@ -606,32 +586,3 @@ server_lock_sessions(void)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Update socket execute permissions based on whether sessions are attached. */
 | 
			
		||||
int
 | 
			
		||||
server_update_socket(void)
 | 
			
		||||
{
 | 
			
		||||
	struct session	*s;
 | 
			
		||||
	u_int		 i;
 | 
			
		||||
	static int	 last = -1;
 | 
			
		||||
	int		 n;
 | 
			
		||||
 | 
			
		||||
	n = 0;
 | 
			
		||||
	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
 | 
			
		||||
		s = ARRAY_ITEM(&sessions, i);
 | 
			
		||||
		if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
 | 
			
		||||
			n++;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (n != last) {
 | 
			
		||||
		last = n;
 | 
			
		||||
		if (n != 0)
 | 
			
		||||
			chmod(socket_path, S_IRWXU);
 | 
			
		||||
		else
 | 
			
		||||
			chmod(socket_path, S_IRUSR|S_IWUSR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (n);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user