mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +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:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -45,7 +45,7 @@ CDIAGFLAGS+= -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations | |||||||
| CDIAGFLAGS+= -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare | CDIAGFLAGS+= -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare | ||||||
| CDIAGFLAGS+= -Wundef -Wbad-function-cast -Winline -Wcast-align | CDIAGFLAGS+= -Wundef -Wbad-function-cast -Winline -Wcast-align | ||||||
|  |  | ||||||
| LDADD=  -lutil -lcurses | LDADD=  -lutil -lcurses -levent | ||||||
| DPADD=  ${LIBUTIL} | DPADD=  ${LIBUTIL} | ||||||
|  |  | ||||||
| .include <bsd.prog.mk> | .include <bsd.prog.mk> | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ | |||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
|  |  | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
|  | #include <event.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  |  | ||||||
| #include "tmux.h" | #include "tmux.h" | ||||||
| @@ -29,9 +30,7 @@ buffer_poll(int fd, int events, struct buffer *in, struct buffer *out) | |||||||
| { | { | ||||||
| 	ssize_t	n; | 	ssize_t	n; | ||||||
|  |  | ||||||
| 	if (events & (POLLERR|POLLNVAL)) | 	if (in != NULL && events & EV_READ) { | ||||||
| 		return (-1); |  | ||||||
| 	if (in != NULL && events & POLLIN) { |  | ||||||
| 		buffer_ensure(in, BUFSIZ); | 		buffer_ensure(in, BUFSIZ); | ||||||
| 		n = read(fd, BUFFER_IN(in), BUFFER_FREE(in)); | 		n = read(fd, BUFFER_IN(in), BUFFER_FREE(in)); | ||||||
| 		if (n == 0) | 		if (n == 0) | ||||||
| @@ -41,9 +40,8 @@ buffer_poll(int fd, int events, struct buffer *in, struct buffer *out) | |||||||
| 				return (-1); | 				return (-1); | ||||||
| 		} else | 		} else | ||||||
| 			buffer_add(in, n); | 			buffer_add(in, n); | ||||||
| 	} else if (events & POLLHUP) | 	} | ||||||
| 		return (-1); | 	if (out != NULL && BUFFER_USED(out) > 0 && events & EV_WRITE) { | ||||||
| 	if (out != NULL && BUFFER_USED(out) > 0 && events & POLLOUT) { |  | ||||||
| 		n = write(fd, BUFFER_OUT(out), BUFFER_USED(out)); | 		n = write(fd, BUFFER_OUT(out), BUFFER_USED(out)); | ||||||
| 		if (n == -1) { | 		if (n == -1) { | ||||||
| 			if (errno != EINTR && errno != EAGAIN) | 			if (errno != EINTR && errno != EAGAIN) | ||||||
|   | |||||||
							
								
								
									
										212
									
								
								client.c
									
									
									
									
									
								
							
							
						
						
									
										212
									
								
								client.c
									
									
									
									
									
								
							| @@ -24,6 +24,7 @@ | |||||||
| #include <sys/wait.h> | #include <sys/wait.h> | ||||||
|  |  | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
|  | #include <event.h> | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <pwd.h> | #include <pwd.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| @@ -34,13 +35,15 @@ | |||||||
| #include "tmux.h" | #include "tmux.h" | ||||||
|  |  | ||||||
| struct imsgbuf	client_ibuf; | struct imsgbuf	client_ibuf; | ||||||
|  | struct event	client_event; | ||||||
| const char     *client_exitmsg; | const char     *client_exitmsg; | ||||||
|  |  | ||||||
| void	client_send_identify(int); | void		client_send_identify(int); | ||||||
| void	client_send_environ(void); | void		client_send_environ(void); | ||||||
| void	client_write_server(enum msgtype, void *, size_t); | void		client_write_server(enum msgtype, void *, size_t); | ||||||
| int	client_dispatch(void); | void		client_signal(int, short, void *); | ||||||
| void	client_suspend(void); | void		client_callback(int, short, void *); | ||||||
|  | int		client_dispatch(void); | ||||||
|  |  | ||||||
| struct imsgbuf * | struct imsgbuf * | ||||||
| client_init(char *path, int cmdflags, int flags) | client_init(char *path, int cmdflags, int flags) | ||||||
| @@ -154,76 +157,54 @@ client_write_server(enum msgtype type, void *buf, size_t len) | |||||||
| __dead void | __dead void | ||||||
| client_main(void) | client_main(void) | ||||||
| { | { | ||||||
| 	struct pollfd	 pfd; | 	struct event		ev_sigcont, ev_sigterm, ev_sigwinch; | ||||||
| 	int		 n, nfds; | 	struct sigaction	sigact; | ||||||
|  | 	short	 		events; | ||||||
| 	siginit(); |  | ||||||
|  |  | ||||||
| 	logfile("client"); | 	logfile("client"); | ||||||
|  |  | ||||||
|  | 	/* Note: event_init() has already been called. */ | ||||||
|  |  | ||||||
|  |  	/* Set up signals. */ | ||||||
|  | 	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(SIGUSR1, &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(&ev_sigcont, SIGCONT, client_signal, NULL); | ||||||
|  | 	signal_add(&ev_sigcont, NULL); | ||||||
|  | 	signal_set(&ev_sigterm, SIGTERM, client_signal, NULL); | ||||||
|  | 	signal_add(&ev_sigterm, NULL); | ||||||
|  | 	signal_set(&ev_sigwinch, SIGWINCH, client_signal, NULL); | ||||||
|  | 	signal_add(&ev_sigwinch, NULL); | ||||||
|  |  | ||||||
| 	/* | 	/* | ||||||
| 	 * imsg_read in the first client poll loop (before the terminal has | 	 * imsg_read in the first client poll loop (before the terminal has | ||||||
| 	 * been initialiased) may have read messages into the buffer after the | 	 * been initialised) may have read messages into the buffer after the | ||||||
| 	 * MSG_READY switched to here. Process anything outstanding now so poll | 	 * MSG_READY switched to here. Process anything outstanding now to | ||||||
| 	 * doesn't hang waiting for messages that have already arrived. | 	 * avoid hanging waiting for messages that have already arrived. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (client_dispatch() != 0) | 	if (client_dispatch() != 0) | ||||||
| 		goto out; | 		goto out; | ||||||
|  |  | ||||||
| 	for (;;) { | 	/* Set up the client-server socket event. */ | ||||||
| 		if (sigterm) { | 	events = EV_READ; | ||||||
| 			client_exitmsg = "terminated"; | 	if (client_ibuf.w.queued > 0) | ||||||
| 			client_write_server(MSG_EXITING, NULL, 0); | 		events |= EV_WRITE; | ||||||
| 		} | 	event_set(&client_event, client_ibuf.fd, events, client_callback, NULL); | ||||||
| 		if (sigchld) { | 	event_add(&client_event, NULL); | ||||||
| 			sigchld = 0; |  | ||||||
| 			waitpid(WAIT_ANY, NULL, WNOHANG); |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
| 		if (sigwinch) { |  | ||||||
| 			sigwinch = 0; |  | ||||||
| 			client_write_server(MSG_RESIZE, NULL, 0); |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
| 		if (sigcont) { |  | ||||||
| 			sigcont = 0; |  | ||||||
| 			siginit(); |  | ||||||
| 			client_write_server(MSG_WAKEUP, NULL, 0); |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
| 	 | 	 | ||||||
| 		pfd.fd = client_ibuf.fd; | 	event_dispatch(); | ||||||
| 		pfd.events = POLLIN; |  | ||||||
| 		if (client_ibuf.w.queued > 0) |  | ||||||
| 			pfd.events |= POLLOUT; |  | ||||||
|  |  | ||||||
| 		if ((nfds = poll(&pfd, 1, INFTIM)) == -1) { |  | ||||||
| 			if (errno == EAGAIN || errno == EINTR) |  | ||||||
| 				continue; |  | ||||||
| 			fatal("poll failed"); |  | ||||||
| 		} |  | ||||||
| 		if (nfds == 0) |  | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		if (pfd.revents & (POLLERR|POLLHUP|POLLNVAL)) |  | ||||||
| 			fatalx("socket error"); |  | ||||||
|  |  | ||||||
| 		if (pfd.revents & POLLIN) { |  | ||||||
| 			if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) { |  | ||||||
| 				client_exitmsg = "lost server"; |  | ||||||
| 				break; |  | ||||||
| 			} |  | ||||||
| 			if (client_dispatch() != 0) |  | ||||||
| 				break; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (pfd.revents & POLLOUT) { |  | ||||||
| 			if (msgbuf_write(&client_ibuf.w) < 0) { |  | ||||||
| 				client_exitmsg = "lost server"; |  | ||||||
| 				break; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| out: | out: | ||||||
| 	/* Print the exit message, if any, and exit. */ | 	/* Print the exit message, if any, and exit. */ | ||||||
| @@ -235,12 +216,78 @@ out: | |||||||
| 	exit(0); | 	exit(0); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | client_signal(int sig, short events, unused void *data) | ||||||
|  | { | ||||||
|  | 	struct sigaction	sigact; | ||||||
|  |  | ||||||
|  | 	switch (sig) { | ||||||
|  | 	case SIGTERM: | ||||||
|  | 		client_exitmsg = "terminated"; | ||||||
|  | 		client_write_server(MSG_EXITING, NULL, 0); | ||||||
|  | 		break; | ||||||
|  | 	case SIGWINCH: | ||||||
|  | 		client_write_server(MSG_RESIZE, NULL, 0); | ||||||
|  | 		break; | ||||||
|  | 	case SIGCONT: | ||||||
|  | 		memset(&sigact, 0, sizeof sigact); | ||||||
|  | 		sigemptyset(&sigact.sa_mask); | ||||||
|  | 		sigact.sa_flags = SA_RESTART; | ||||||
|  | 		sigact.sa_handler = SIG_IGN; | ||||||
|  | 		if (sigaction(SIGTSTP, &sigact, NULL) != 0) | ||||||
|  | 			fatal("sigaction failed"); | ||||||
|  | 		client_write_server(MSG_WAKEUP, NULL, 0); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	event_del(&client_event); | ||||||
|  | 	events = EV_READ; | ||||||
|  | 	if (client_ibuf.w.queued > 0) | ||||||
|  | 		events |= EV_WRITE; | ||||||
|  | 	event_set(&client_event, client_ibuf.fd, events, client_callback, NULL); | ||||||
|  | 	event_add(&client_event, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | client_callback(unused int fd, short events, unused void *data) | ||||||
|  | { | ||||||
|  | 	int	n; | ||||||
|  |  | ||||||
|  | 	if (events & EV_READ) { | ||||||
|  | 		if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) | ||||||
|  | 			goto lost_server; | ||||||
|  | 		if (client_dispatch() != 0) { | ||||||
|  | 			event_loopexit(NULL); | ||||||
|  | 			return; | ||||||
|  | 		}	 | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	if (events & EV_WRITE) { | ||||||
|  | 		if (msgbuf_write(&client_ibuf.w) < 0) | ||||||
|  | 			goto lost_server; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	event_del(&client_event); | ||||||
|  | 	events = EV_READ; | ||||||
|  | 	if (client_ibuf.w.queued > 0) | ||||||
|  | 		events |= EV_WRITE; | ||||||
|  | 	event_set(&client_event, client_ibuf.fd, events, client_callback, NULL); | ||||||
|  | 	event_add(&client_event, NULL); | ||||||
|  |  | ||||||
|  | 	return; | ||||||
|  |  | ||||||
|  | lost_server: | ||||||
|  | 	client_exitmsg = "lost server"; | ||||||
|  | 	event_loopexit(NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
| int | int | ||||||
| client_dispatch(void) | client_dispatch(void) | ||||||
| { | { | ||||||
| 	struct imsg		 imsg; | 	struct imsg		imsg; | ||||||
| 	struct msg_lock_data	 lockdata; | 	struct msg_lock_data	lockdata; | ||||||
| 	ssize_t			 n, datalen; | 	struct sigaction	sigact; | ||||||
|  | 	ssize_t			n, datalen; | ||||||
|  |  | ||||||
| 	for (;;) { | 	for (;;) { | ||||||
| 		if ((n = imsg_get(&client_ibuf, &imsg)) == -1) | 		if ((n = imsg_get(&client_ibuf, &imsg)) == -1) | ||||||
| @@ -249,6 +296,7 @@ client_dispatch(void) | |||||||
| 			return (0); | 			return (0); | ||||||
| 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE; | 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE; | ||||||
|  |  | ||||||
|  | 		log_debug("client got %d", imsg.hdr.type); | ||||||
| 		switch (imsg.hdr.type) { | 		switch (imsg.hdr.type) { | ||||||
| 		case MSG_DETACH: | 		case MSG_DETACH: | ||||||
| 			if (datalen != 0) | 			if (datalen != 0) | ||||||
| @@ -281,7 +329,13 @@ client_dispatch(void) | |||||||
| 			if (datalen != 0) | 			if (datalen != 0) | ||||||
| 				fatalx("bad MSG_SUSPEND size"); | 				fatalx("bad MSG_SUSPEND size"); | ||||||
|  |  | ||||||
| 			client_suspend(); | 			memset(&sigact, 0, sizeof sigact); | ||||||
|  | 			sigemptyset(&sigact.sa_mask); | ||||||
|  | 			sigact.sa_flags = SA_RESTART; | ||||||
|  | 			sigact.sa_handler = SIG_DFL; | ||||||
|  | 			if (sigaction(SIGTSTP, &sigact, NULL) != 0) | ||||||
|  | 				fatal("sigaction failed"); | ||||||
|  | 			kill(getpid(), SIGTSTP); | ||||||
| 			break; | 			break; | ||||||
| 		case MSG_LOCK: | 		case MSG_LOCK: | ||||||
| 			if (datalen != sizeof lockdata) | 			if (datalen != sizeof lockdata) | ||||||
| @@ -299,23 +353,3 @@ client_dispatch(void) | |||||||
| 		imsg_free(&imsg); | 		imsg_free(&imsg); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| void |  | ||||||
| client_suspend(void) |  | ||||||
| { |  | ||||||
| 	struct sigaction	 act; |  | ||||||
|  |  | ||||||
| 	memset(&act, 0, sizeof act); |  | ||||||
| 	sigemptyset(&act.sa_mask); |  | ||||||
| 	act.sa_flags = SA_RESTART; |  | ||||||
|  |  | ||||||
| 	act.sa_handler = SIG_DFL; |  | ||||||
| 	if (sigaction(SIGTSTP, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
|  |  | ||||||
| 	act.sa_handler = sighandler; |  | ||||||
| 	if (sigaction(SIGCONT, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
|  |  | ||||||
| 	kill(getpid(), SIGTSTP); |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -43,7 +43,7 @@ const struct cmd_entry cmd_kill_server_entry = { | |||||||
| int | int | ||||||
| cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx) | cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx) | ||||||
| { | { | ||||||
| 	sigterm = 1; | 	kill(getpid(), SIGTERM); | ||||||
|  |  | ||||||
| 	return (0); | 	return (0); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -88,7 +88,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_ctx *ctx) | |||||||
| 	case 0: | 	case 0: | ||||||
| 		/* Child process. */ | 		/* Child process. */ | ||||||
| 		close(pipe_fd[0]); | 		close(pipe_fd[0]); | ||||||
| 		sigreset(); | 		server_signal_clear(); | ||||||
|  |  | ||||||
| 		if (dup2(pipe_fd[1], STDIN_FILENO) == -1) | 		if (dup2(pipe_fd[1], STDIN_FILENO) == -1) | ||||||
| 			_exit(1); | 			_exit(1); | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								job.c
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								job.c
									
									
									
									
									
								
							| @@ -87,6 +87,7 @@ job_add(struct jobs *jobs, int flags, struct client *c, const char *cmd, | |||||||
|  |  | ||||||
| 	job->fd = -1; | 	job->fd = -1; | ||||||
| 	job->out = buffer_create(BUFSIZ); | 	job->out = buffer_create(BUFSIZ); | ||||||
|  | 	memset(&job->event, 0, sizeof job->event); | ||||||
|  |  | ||||||
| 	job->callbackfn = callbackfn; | 	job->callbackfn = callbackfn; | ||||||
| 	job->freefn = freefn; | 	job->freefn = freefn; | ||||||
| @@ -126,6 +127,7 @@ job_free(struct job *job) | |||||||
| 		close(job->fd); | 		close(job->fd); | ||||||
| 	if (job->out != NULL) | 	if (job->out != NULL) | ||||||
| 		buffer_destroy(job->out); | 		buffer_destroy(job->out); | ||||||
|  | 	event_del(&job->event); | ||||||
|  |  | ||||||
| 	xfree(job); | 	xfree(job); | ||||||
| } | } | ||||||
| @@ -147,7 +149,7 @@ job_run(struct job *job) | |||||||
| 	case -1: | 	case -1: | ||||||
| 		return (-1); | 		return (-1); | ||||||
| 	case 0:		/* child */ | 	case 0:		/* child */ | ||||||
| 		sigreset(); | 		server_signal_clear(); | ||||||
| 		/* XXX environ? */ | 		/* XXX environ? */ | ||||||
|  |  | ||||||
| 		if (dup2(out[1], STDOUT_FILENO) == -1) | 		if (dup2(out[1], STDOUT_FILENO) == -1) | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ | |||||||
|  |  | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
|  |  | ||||||
|  | #include <event.h> | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
| @@ -134,6 +135,8 @@ server_client_lost(struct client *c) | |||||||
|  |  | ||||||
| 	close(c->ibuf.fd); | 	close(c->ibuf.fd); | ||||||
| 	imsg_clear(&c->ibuf); | 	imsg_clear(&c->ibuf); | ||||||
|  | 	event_del(&c->event); | ||||||
|  | 	event_del(&c->tty.event); | ||||||
| 	 | 	 | ||||||
| 	for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { | 	for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { | ||||||
| 		if (ARRAY_ITEM(&dead_clients, i) == NULL) { | 		if (ARRAY_ITEM(&dead_clients, i) == NULL) { | ||||||
| @@ -162,25 +165,31 @@ server_client_prepare(void) | |||||||
|  |  | ||||||
| 		events = 0; | 		events = 0; | ||||||
| 		if (!(c->flags & CLIENT_BAD)) | 		if (!(c->flags & CLIENT_BAD)) | ||||||
| 			events |= POLLIN; | 			events |= EV_READ; | ||||||
| 		if (c->ibuf.w.queued > 0) | 		if (c->ibuf.w.queued > 0) | ||||||
| 			events |= POLLOUT; | 			events |= EV_WRITE; | ||||||
| 		server_poll_add(c->ibuf.fd, events, server_client_callback, c); | 		event_del(&c->event); | ||||||
|  | 		event_set(&c->event, | ||||||
|  | 		    c->ibuf.fd, events, server_client_callback, c); | ||||||
|  | 		event_add(&c->event, NULL); | ||||||
|  |  | ||||||
| 		if (c->tty.fd == -1) | 		if (c->tty.fd == -1) | ||||||
| 			continue; | 			continue; | ||||||
| 		if (c->flags & CLIENT_SUSPENDED || c->session == NULL) | 		if (c->flags & CLIENT_SUSPENDED || c->session == NULL) | ||||||
| 			continue; | 			continue; | ||||||
| 		events = POLLIN; | 		events = EV_READ; | ||||||
| 		if (BUFFER_USED(c->tty.out) > 0) | 		if (BUFFER_USED(c->tty.out) > 0) | ||||||
| 			events |= POLLOUT; | 			events |= EV_WRITE; | ||||||
| 		server_poll_add(c->tty.fd, events, server_client_callback, c); | 		event_del(&c->tty.event); | ||||||
|  | 		event_set(&c->tty.event, | ||||||
|  | 		    c->tty.fd, events, server_client_callback, c); | ||||||
|  | 		event_add(&c->tty.event, NULL); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Process a single client event. */ | /* Process a single client event. */ | ||||||
| void | void | ||||||
| server_client_callback(int fd, int events, void *data) | server_client_callback(int fd, short events, void *data) | ||||||
| { | { | ||||||
| 	struct client	*c = data; | 	struct client	*c = data; | ||||||
|  |  | ||||||
| @@ -188,10 +197,7 @@ server_client_callback(int fd, int events, void *data) | |||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| 	if (fd == c->ibuf.fd) { | 	if (fd == c->ibuf.fd) { | ||||||
| 		if (events & (POLLERR|POLLNVAL|POLLHUP)) | 		if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0) | ||||||
| 			goto client_lost; |  | ||||||
|  |  | ||||||
| 		if (events & POLLOUT && msgbuf_write(&c->ibuf.w) < 0) |  | ||||||
| 			goto client_lost; | 			goto client_lost; | ||||||
|  |  | ||||||
| 		if (c->flags & CLIENT_BAD) { | 		if (c->flags & CLIENT_BAD) { | ||||||
| @@ -200,7 +206,7 @@ server_client_callback(int fd, int events, void *data) | |||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (events & POLLIN && server_client_msg_dispatch(c) != 0) | 		if (events & EV_READ && server_client_msg_dispatch(c) != 0) | ||||||
| 			goto client_lost; | 			goto client_lost; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ | |||||||
|  |  | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
|  |  | ||||||
|  | #include <event.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  |  | ||||||
| #include "tmux.h" | #include "tmux.h" | ||||||
| @@ -31,13 +32,16 @@ server_job_prepare(void) | |||||||
| 	SLIST_FOREACH(job, &all_jobs, lentry) { | 	SLIST_FOREACH(job, &all_jobs, lentry) { | ||||||
| 		if (job->fd == -1) | 		if (job->fd == -1) | ||||||
| 			continue; | 			continue; | ||||||
| 		server_poll_add(job->fd, POLLIN, server_job_callback, job); | 		event_del(&job->event); | ||||||
|  | 		event_set( | ||||||
|  | 		    &job->event, job->fd, EV_READ, server_job_callback, job); | ||||||
|  | 		event_add(&job->event, NULL); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Process a single job event. */ | /* Process a single job event. */ | ||||||
| void | void | ||||||
| server_job_callback(int fd, int events, void *data) | server_job_callback(int fd, short events, void *data) | ||||||
| { | { | ||||||
| 	struct job	*job = data; | 	struct job	*job = data; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ | |||||||
|  |  | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
|  |  | ||||||
|  | #include <event.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  |  | ||||||
| #include "tmux.h" | #include "tmux.h" | ||||||
| @@ -47,19 +48,23 @@ server_window_prepare(void) | |||||||
| 				continue; | 				continue; | ||||||
| 			events = 0; | 			events = 0; | ||||||
| 			if (!server_window_backoff(wp)) | 			if (!server_window_backoff(wp)) | ||||||
| 				events |= POLLIN; | 				events = EV_READ; | ||||||
| 			if (BUFFER_USED(wp->out) > 0) | 			if (BUFFER_USED(wp->out) > 0) | ||||||
| 				events |= POLLOUT; | 				events |= EV_WRITE; | ||||||
| 			server_poll_add( | 			event_del(&wp->event); | ||||||
|  | 			event_set(&wp->event, | ||||||
| 			    wp->fd, events, server_window_callback, wp); | 			    wp->fd, events, server_window_callback, wp); | ||||||
|  | 			event_add(&wp->event, NULL); | ||||||
|  |  | ||||||
| 			if (wp->pipe_fd == -1) | 			if (wp->pipe_fd == -1) | ||||||
| 				continue; | 				continue; | ||||||
| 			events = 0; | 			events = 0; | ||||||
| 			if (BUFFER_USED(wp->pipe_buf) > 0) | 			if (BUFFER_USED(wp->pipe_buf) > 0) | ||||||
| 				events |= POLLOUT; | 				events |= EV_WRITE; | ||||||
| 			server_poll_add( | 			event_del(&wp->pipe_event); | ||||||
|  | 			event_set(&wp->pipe_event, | ||||||
| 			    wp->pipe_fd, events, server_window_callback, wp); | 			    wp->pipe_fd, events, server_window_callback, wp); | ||||||
|  | 			event_add(&wp->pipe_event, NULL); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -90,7 +95,7 @@ server_window_backoff(struct window_pane *wp) | |||||||
|  |  | ||||||
| /* Process a single window pane event. */ | /* Process a single window pane event. */ | ||||||
| void | void | ||||||
| server_window_callback(int fd, int events, void *data) | server_window_callback(int fd, short events, void *data) | ||||||
| { | { | ||||||
| 	struct window_pane	*wp = data; | 	struct window_pane	*wp = data; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										599
									
								
								server.c
									
									
									
									
									
								
							
							
						
						
									
										599
									
								
								server.c
									
									
									
									
									
								
							| @@ -24,6 +24,7 @@ | |||||||
| #include <sys/wait.h> | #include <sys/wait.h> | ||||||
|  |  | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
|  | #include <event.h> | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <paths.h> | #include <paths.h> | ||||||
| #include <signal.h> | #include <signal.h> | ||||||
| @@ -45,112 +46,28 @@ | |||||||
| struct clients	 clients; | struct clients	 clients; | ||||||
| struct clients	 dead_clients; | struct clients	 dead_clients; | ||||||
|  |  | ||||||
| /* Mapping of a pollfd to an fd independent of its position in the array. */ | int		 server_fd; | ||||||
| struct poll_item { | int		 server_shutdown; | ||||||
| 	int	 fd; | struct event	 server_ev_accept; | ||||||
| 	int	 events; | struct event	 server_ev_sigterm; | ||||||
|  | struct event	 server_ev_sigusr1; | ||||||
| 	void	 (*fn)(int, int, void *); | struct event	 server_ev_sigchld; | ||||||
| 	void	*data; | struct event	 server_ev_second; | ||||||
|  |  | ||||||
| 	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_create_socket(void); | int		 server_create_socket(void); | ||||||
| void		 server_callback(int, int, void *); | void		 server_loop(void); | ||||||
| int		 server_main(int); |  | ||||||
| void		 server_shutdown(void); |  | ||||||
| int		 server_should_shutdown(void); | int		 server_should_shutdown(void); | ||||||
| void		 server_child_signal(void); | void		 server_send_shutdown(void); | ||||||
| void		 server_clean_dead(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_server(void); | ||||||
| void		 server_lock_sessions(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. */ | /* Create server socket. */ | ||||||
| int | int | ||||||
| @@ -191,40 +108,14 @@ server_create_socket(void) | |||||||
| 	return (fd); | 	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. */ | /* Fork new server. */ | ||||||
| int | int | ||||||
| server_start(char *path) | server_start(char *path) | ||||||
| { | { | ||||||
| 	struct client	*c; | 	struct client	*c; | ||||||
| 	int		 pair[2], srv_fd; | 	int		 pair[2]; | ||||||
| 	char		*cause; | 	char		*cause, rpathbuf[MAXPATHLEN]; | ||||||
| 	char		 rpathbuf[MAXPATHLEN]; | 	struct timeval	 tv; | ||||||
|  |  | ||||||
| 	/* The first client is special and gets a socketpair; create it. */ | 	/* The first client is special and gets a socketpair; create it. */ | ||||||
| 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) | 	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); | 	log_debug("socket path %s", socket_path); | ||||||
| 	setproctitle("server (%s)", rpathbuf); | 	setproctitle("server (%s)", rpathbuf); | ||||||
|  |  | ||||||
| 	srv_fd = server_create_socket(); | 	server_fd = server_create_socket(); | ||||||
| 	server_client_create(pair[1]); | 	server_client_create(pair[1]); | ||||||
|  |  | ||||||
| 	if (access(SYSTEM_CFG, R_OK) == 0) { | 	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) | 	if (cfg_file != NULL && load_cfg(cfg_file, NULL, &cause) != 0) | ||||||
| 		goto error; | 		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: | error: | ||||||
| 	/* Write the error and shutdown the server. */ | 	/* Write the error and shutdown the server. */ | ||||||
| @@ -291,150 +195,39 @@ error: | |||||||
| 	server_write_error(c, cause); | 	server_write_error(c, cause); | ||||||
| 	xfree(cause); | 	xfree(cause); | ||||||
|  |  | ||||||
| 	sigterm = 1; | 	server_shutdown = 1; | ||||||
| 	server_shutdown(); |  | ||||||
|  |  | ||||||
| 	exit(server_main(srv_fd)); | 	server_signal_set(); | ||||||
|  | 	server_loop(); | ||||||
|  | 	exit(1); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Main server loop. */ | /* Main server loop. */ | ||||||
| int | void | ||||||
| server_main(int srv_fd) | server_loop(void) | ||||||
| { | { | ||||||
| 	struct pollfd	*pfds; | 	struct timeval	tv; | ||||||
| 	int		 nfds, xtimeout; |  | ||||||
| 	u_int		 i; |  | ||||||
| 	time_t		 now, last; |  | ||||||
|  |  | ||||||
| 	siginit(); | 	memset(&tv, 0, sizeof tv); | ||||||
| 	log_debug("server socket is %d", srv_fd); | 	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_job_prepare(); | ||||||
| 		server_window_prepare(); | 		server_window_prepare(); | ||||||
| 		server_client_prepare(); | 		server_client_prepare(); | ||||||
|  |  | ||||||
| 		/* Update socket permissions. */ | 		event_loopexit(&tv); | ||||||
| 		xtimeout = INFTIM; | 		event_loop(EVLOOP_ONCE); | ||||||
| 		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); |  | ||||||
|  |  | ||||||
| 		/* Call second-based timers. */ |  | ||||||
| 		now = time(NULL); |  | ||||||
| 		if (now != last) { |  | ||||||
| 			last = now; |  | ||||||
| 			server_second_timers(); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		/* Run once-per-loop events. */ |  | ||||||
| 		server_job_loop(); | 		server_job_loop(); | ||||||
| 		server_window_loop(); | 		server_window_loop(); | ||||||
| 		server_client_loop(); | 		server_client_loop(); | ||||||
|  |  | ||||||
| 		/* Collect any unset key bindings. */ |  | ||||||
| 		key_bindings_clean(); | 		key_bindings_clean(); | ||||||
|  |  | ||||||
| 		/* Collect dead clients and sessions. */ |  | ||||||
| 		server_clean_dead(); | 		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). */ | /* Check if the server should be shutting down (no more clients or windows). */ | ||||||
| @@ -454,49 +247,28 @@ server_should_shutdown(void) | |||||||
| 	return (1); | 	return (1); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Handle SIGCHLD. */ | /* Shutdown the server by killing all clients and windows. */ | ||||||
| void | void | ||||||
| server_child_signal(void) | server_send_shutdown(void) | ||||||
| { | { | ||||||
| 	struct window		*w; | 	struct client	*c; | ||||||
| 	struct window_pane	*wp; | 	struct session	*s; | ||||||
| 	struct job		*job; | 	u_int		 i; | ||||||
| 	int		 	 status; |  | ||||||
| 	pid_t		 	 pid; |  | ||||||
| 	u_int		 	 i; |  | ||||||
|  |  | ||||||
| 	for (;;) { | 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) { | ||||||
| 		switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) { | 		c = ARRAY_ITEM(&clients, i); | ||||||
| 		case -1: | 		if (c != NULL) { | ||||||
| 			if (errno == ECHILD) | 			if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) | ||||||
| 				return; | 				server_client_lost(c); | ||||||
| 			fatal("waitpid failed"); | 			else | ||||||
| 		case 0: | 				server_write_client(c, MSG_SHUTDOWN, NULL, 0); | ||||||
| 			return; | 			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++) { | 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { | ||||||
| 			w = ARRAY_ITEM(&windows, i); | 		if ((s = ARRAY_ITEM(&sessions, i)) != NULL) | ||||||
| 			if (w == NULL) | 			session_destroy(s); | ||||||
| 				continue; |  | ||||||
| 			TAILQ_FOREACH(wp, &w->panes, entry) { |  | ||||||
| 				if (wp->pid == pid) { |  | ||||||
| 					if (killpg(pid, SIGCONT) != 0) |  | ||||||
| 						kill(pid, SIGCONT); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -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 | 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		*w; | ||||||
| 	struct window_pane	*wp; | 	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; | 	u_int		 	 i; | ||||||
|  |  | ||||||
| 	if (options_get_number(&global_s_options, "lock-server")) | 	if (options_get_number(&global_s_options, "lock-server")) | ||||||
| @@ -548,6 +523,11 @@ server_second_timers(void) | |||||||
| 				wp->mode->timer(wp); | 				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. */ | /* 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); |  | ||||||
| } |  | ||||||
|   | |||||||
							
								
								
									
										301
									
								
								tmux.c
									
									
									
									
									
								
							
							
						
						
									
										301
									
								
								tmux.c
									
									
									
									
									
								
							| @@ -20,6 +20,7 @@ | |||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
|  |  | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
|  | #include <event.h> | ||||||
| #include <paths.h> | #include <paths.h> | ||||||
| #include <pwd.h> | #include <pwd.h> | ||||||
| #include <signal.h> | #include <signal.h> | ||||||
| @@ -34,13 +35,6 @@ | |||||||
| extern char	*malloc_options; | extern char	*malloc_options; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| volatile sig_atomic_t sigwinch; |  | ||||||
| volatile sig_atomic_t sigterm; |  | ||||||
| volatile sig_atomic_t sigcont; |  | ||||||
| volatile sig_atomic_t sigchld; |  | ||||||
| volatile sig_atomic_t sigusr1; |  | ||||||
| volatile sig_atomic_t sigusr2; |  | ||||||
|  |  | ||||||
| char		*cfg_file; | char		*cfg_file; | ||||||
| struct options	 global_s_options;	/* session options */ | struct options	 global_s_options;	/* session options */ | ||||||
| struct options	 global_w_options;	/* window options */ | struct options	 global_w_options;	/* window options */ | ||||||
| @@ -55,9 +49,18 @@ int		 login_shell; | |||||||
| __dead void	 usage(void); | __dead void	 usage(void); | ||||||
| void	 	 fill_session(struct msg_command_data *); | void	 	 fill_session(struct msg_command_data *); | ||||||
| char 		*makesockpath(const char *); | char 		*makesockpath(const char *); | ||||||
| int		 dispatch_imsg(struct imsgbuf *, const char *, int *); |  | ||||||
| __dead void	 shell_exec(const char *, const char *); | __dead void	 shell_exec(const char *, const char *); | ||||||
|  |  | ||||||
|  | struct imsgbuf	*main_ibuf; | ||||||
|  | struct event	 main_ev_sigterm; | ||||||
|  | int	         main_exitval; | ||||||
|  |  | ||||||
|  | void		 main_set_signals(void); | ||||||
|  | void		 main_clear_signals(void); | ||||||
|  | void		 main_signal(int, short, unused void *); | ||||||
|  | void		 main_callback(int, short, void *); | ||||||
|  | void		 main_dispatch(const char *); | ||||||
|  |  | ||||||
| __dead void | __dead void | ||||||
| usage(void) | usage(void) | ||||||
| { | { | ||||||
| @@ -81,96 +84,6 @@ logfile(const char *name) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| void |  | ||||||
| sighandler(int sig) |  | ||||||
| { |  | ||||||
| 	int	saved_errno; |  | ||||||
|  |  | ||||||
| 	saved_errno = errno; |  | ||||||
| 	switch (sig) { |  | ||||||
| 	case SIGWINCH: |  | ||||||
| 		sigwinch = 1; |  | ||||||
| 		break; |  | ||||||
| 	case SIGTERM: |  | ||||||
| 		sigterm = 1; |  | ||||||
| 		break; |  | ||||||
| 	case SIGCHLD: |  | ||||||
| 		sigchld = 1; |  | ||||||
| 		break; |  | ||||||
| 	case SIGCONT: |  | ||||||
| 		sigcont = 1; |  | ||||||
| 		break; |  | ||||||
| 	case SIGUSR1: |  | ||||||
| 		sigusr1 = 1; |  | ||||||
| 		break; |  | ||||||
| 	case SIGUSR2: |  | ||||||
| 		sigusr2 = 1; |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
| 	errno = saved_errno; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void |  | ||||||
| siginit(void) |  | ||||||
| { |  | ||||||
| 	struct sigaction	 act; |  | ||||||
|  |  | ||||||
| 	memset(&act, 0, sizeof act); |  | ||||||
| 	sigemptyset(&act.sa_mask); |  | ||||||
| 	act.sa_flags = SA_RESTART; |  | ||||||
|  |  | ||||||
| 	act.sa_handler = SIG_IGN; |  | ||||||
| 	if (sigaction(SIGPIPE, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGINT, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGTSTP, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGQUIT, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
|  |  | ||||||
| 	act.sa_handler = sighandler; |  | ||||||
| 	if (sigaction(SIGWINCH, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGTERM, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGCHLD, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGUSR1, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGUSR2, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void |  | ||||||
| sigreset(void) |  | ||||||
| { |  | ||||||
| 	struct sigaction act; |  | ||||||
|  |  | ||||||
| 	memset(&act, 0, sizeof act); |  | ||||||
| 	sigemptyset(&act.sa_mask); |  | ||||||
|  |  | ||||||
| 	act.sa_handler = SIG_DFL; |  | ||||||
| 	if (sigaction(SIGPIPE, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGUSR1, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGUSR2, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGINT, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGTSTP, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGQUIT, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGWINCH, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGTERM, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| 	if (sigaction(SIGCHLD, &act, NULL) != 0) |  | ||||||
| 		fatal("sigaction failed"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const char * | const char * | ||||||
| getshell(void) | getshell(void) | ||||||
| { | { | ||||||
| @@ -281,23 +194,43 @@ makesockpath(const char *label) | |||||||
| 	return (path); | 	return (path); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | __dead void | ||||||
|  | shell_exec(const char *shell, const char *shellcmd) | ||||||
|  | { | ||||||
|  | 	const char	*shellname, *ptr; | ||||||
|  | 	char		*argv0; | ||||||
|  |  | ||||||
|  | 	ptr = strrchr(shell, '/'); | ||||||
|  | 	if (ptr != NULL && *(ptr + 1) != '\0') | ||||||
|  | 		shellname = ptr + 1; | ||||||
|  | 	else | ||||||
|  | 		shellname = shell; | ||||||
|  | 	if (login_shell) | ||||||
|  | 		xasprintf(&argv0, "-%s", shellname); | ||||||
|  | 	else | ||||||
|  | 		xasprintf(&argv0, "%s", shellname); | ||||||
|  | 	setenv("SHELL", shell, 1); | ||||||
|  |  | ||||||
|  | 	execl(shell, argv0, "-c", shellcmd, (char *) NULL); | ||||||
|  | 	fatal("execl failed"); | ||||||
|  | } | ||||||
|  |  | ||||||
| int | int | ||||||
| main(int argc, char **argv) | main(int argc, char **argv) | ||||||
| { | { | ||||||
| 	struct cmd_list		*cmdlist; | 	struct cmd_list		*cmdlist; | ||||||
|  	struct cmd		*cmd; |  	struct cmd		*cmd; | ||||||
| 	struct pollfd		 pfd; |  | ||||||
| 	enum msgtype		 msg; | 	enum msgtype		 msg; | ||||||
| 	struct passwd		*pw; | 	struct passwd		*pw; | ||||||
| 	struct options		*so, *wo; | 	struct options		*so, *wo; | ||||||
| 	struct keylist		*keylist; | 	struct keylist		*keylist; | ||||||
| 	struct imsgbuf		*ibuf; |  | ||||||
| 	struct msg_command_data	 cmddata; | 	struct msg_command_data	 cmddata; | ||||||
| 	char			*s, *shellcmd, *path, *label, *home, *cause; | 	char			*s, *shellcmd, *path, *label, *home, *cause; | ||||||
| 	char			 cwd[MAXPATHLEN], **var; | 	char			 cwd[MAXPATHLEN], **var; | ||||||
| 	void			*buf; | 	void			*buf; | ||||||
| 	size_t			 len; | 	size_t			 len; | ||||||
| 	int	 		 nfds, retcode, opt, flags, cmdflags = 0; | 	int	 		 opt, flags, cmdflags = 0; | ||||||
|  | 	short		 	 events; | ||||||
|  |  | ||||||
| #ifdef DEBUG | #ifdef DEBUG | ||||||
| 	malloc_options = (char *) "AFGJPX"; | 	malloc_options = (char *) "AFGJPX"; | ||||||
| @@ -359,7 +292,6 @@ main(int argc, char **argv) | |||||||
| 		usage(); | 		usage(); | ||||||
|  |  | ||||||
| 	log_open_tty(debug_level); | 	log_open_tty(debug_level); | ||||||
| 	siginit(); |  | ||||||
|  |  | ||||||
| 	if (!(flags & IDENTIFY_UTF8)) { | 	if (!(flags & IDENTIFY_UTF8)) { | ||||||
| 		/* | 		/* | ||||||
| @@ -549,63 +481,121 @@ main(int argc, char **argv) | |||||||
| 		cmd_list_free(cmdlist); | 		cmd_list_free(cmdlist); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if ((ibuf = client_init(path, cmdflags, flags)) == NULL) | 	if ((main_ibuf = client_init(path, cmdflags, flags)) == NULL) | ||||||
| 		exit(1); | 		exit(1); | ||||||
| 	xfree(path); | 	xfree(path); | ||||||
|  |  | ||||||
|  	imsg_compose(ibuf, msg, PROTOCOL_VERSION, -1, -1, buf, len); | 	event_init(); | ||||||
|  |  | ||||||
| 	retcode = 0; |  	imsg_compose(main_ibuf, msg, PROTOCOL_VERSION, -1, -1, buf, len); | ||||||
| 	for (;;) { |  | ||||||
| 		pfd.fd = ibuf->fd; |  | ||||||
| 		pfd.events = POLLIN; |  | ||||||
| 		if (ibuf->w.queued != 0) |  | ||||||
| 			pfd.events |= POLLOUT; |  | ||||||
|  |  | ||||||
| 		if ((nfds = poll(&pfd, 1, INFTIM)) == -1) { | 	main_set_signals(); | ||||||
| 			if (errno == EAGAIN || errno == EINTR) |  | ||||||
| 				continue; |  | ||||||
| 			fatal("poll failed"); |  | ||||||
| 		} |  | ||||||
| 		if (nfds == 0) |  | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		if (pfd.revents & (POLLERR|POLLHUP|POLLNVAL)) | 	events = EV_READ; | ||||||
| 			fatalx("socket error"); | 	if (main_ibuf->w.queued > 0) | ||||||
|  | 		events |= EV_WRITE; | ||||||
|  | 	event_once(main_ibuf->fd, events, main_callback, shellcmd, NULL); | ||||||
|  |  | ||||||
| 		if (pfd.revents & POLLIN) { | 	main_exitval = 0; | ||||||
| 			if (dispatch_imsg(ibuf, shellcmd, &retcode) != 0) | 	event_dispatch(); | ||||||
| 				break; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (pfd.revents & POLLOUT) { | 	main_clear_signals(); | ||||||
| 			if (msgbuf_write(&ibuf->w) < 0) |  | ||||||
| 				fatalx("msgbuf_write failed"); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	options_free(&global_s_options); | 	client_main();	/* doesn't return */ | ||||||
| 	options_free(&global_w_options); |  | ||||||
|  |  | ||||||
| 	return (retcode); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int |  | ||||||
| dispatch_imsg(struct imsgbuf *ibuf, const char *shellcmd, int *retcode) | void | ||||||
|  | main_set_signals(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(SIGUSR1, &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(&main_ev_sigterm, SIGTERM, main_signal, NULL); | ||||||
|  | 	signal_add(&main_ev_sigterm, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | main_clear_signals(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(SIGUSR1, &sigact, NULL) != 0) | ||||||
|  | 		fatal("sigaction failed"); | ||||||
|  | 	if (sigaction(SIGUSR2, &sigact, NULL) != 0) | ||||||
|  | 		fatal("sigaction failed"); | ||||||
|  | 	if (sigaction(SIGTSTP, &sigact, NULL) != 0) | ||||||
|  | 		fatal("sigaction failed"); | ||||||
|  | 	 | ||||||
|  | 	event_del(&main_ev_sigterm); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | main_signal(int sig, unused short events, unused void *data) | ||||||
|  | { | ||||||
|  | 	switch (sig) { | ||||||
|  | 	case SIGTERM: | ||||||
|  | 		exit(1); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | main_callback(unused int fd, short events, void *data) | ||||||
|  | { | ||||||
|  | 	char	*shellcmd = data; | ||||||
|  |  | ||||||
|  | 	if (events & EV_READ) | ||||||
|  | 		main_dispatch(shellcmd); | ||||||
|  | 	 | ||||||
|  | 	if (events & EV_WRITE) { | ||||||
|  | 		if (msgbuf_write(&main_ibuf->w) < 0) | ||||||
|  | 			fatalx("msgbuf_write failed"); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	events = EV_READ; | ||||||
|  | 	if (main_ibuf->w.queued > 0) | ||||||
|  | 		events |= EV_WRITE; | ||||||
|  | 	event_once(main_ibuf->fd, events, main_callback, shellcmd, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | main_dispatch(const char *shellcmd) | ||||||
| { | { | ||||||
| 	struct imsg		imsg; | 	struct imsg		imsg; | ||||||
| 	ssize_t			n, datalen; | 	ssize_t			n, datalen; | ||||||
| 	struct msg_print_data	printdata; | 	struct msg_print_data	printdata; | ||||||
| 	struct msg_shell_data	shelldata; | 	struct msg_shell_data	shelldata; | ||||||
|  |  | ||||||
| 	if ((n = imsg_read(ibuf)) == -1 || n == 0) | 	if ((n = imsg_read(main_ibuf)) == -1 || n == 0) | ||||||
| 		fatalx("imsg_read failed"); | 		fatalx("imsg_read failed"); | ||||||
|  |  | ||||||
| 	for (;;) { | 	for (;;) { | ||||||
| 		if ((n = imsg_get(ibuf, &imsg)) == -1) | 		if ((n = imsg_get(main_ibuf, &imsg)) == -1) | ||||||
| 			fatalx("imsg_get failed"); | 			fatalx("imsg_get failed"); | ||||||
| 		if (n == 0) | 		if (n == 0) | ||||||
| 			return (0); | 			return; | ||||||
| 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE; | 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE; | ||||||
|  |  | ||||||
| 		switch (imsg.hdr.type) { | 		switch (imsg.hdr.type) { | ||||||
| @@ -614,10 +604,8 @@ dispatch_imsg(struct imsgbuf *ibuf, const char *shellcmd, int *retcode) | |||||||
| 			if (datalen != 0) | 			if (datalen != 0) | ||||||
| 				fatalx("bad MSG_EXIT size"); | 				fatalx("bad MSG_EXIT size"); | ||||||
|  |  | ||||||
| 			return (-1); | 			exit(main_exitval); | ||||||
| 		case MSG_ERROR: | 		case MSG_ERROR: | ||||||
| 			*retcode = 1; |  | ||||||
| 			/* FALLTHROUGH */ |  | ||||||
| 		case MSG_PRINT: | 		case MSG_PRINT: | ||||||
| 			if (datalen != sizeof printdata) | 			if (datalen != sizeof printdata) | ||||||
| 				fatalx("bad MSG_PRINT size"); | 				fatalx("bad MSG_PRINT size"); | ||||||
| @@ -625,26 +613,30 @@ dispatch_imsg(struct imsgbuf *ibuf, const char *shellcmd, int *retcode) | |||||||
| 			printdata.msg[(sizeof printdata.msg) - 1] = '\0'; | 			printdata.msg[(sizeof printdata.msg) - 1] = '\0'; | ||||||
|  |  | ||||||
| 			log_info("%s", printdata.msg); | 			log_info("%s", printdata.msg); | ||||||
|  | 			if (imsg.hdr.type == MSG_ERROR) | ||||||
|  | 				main_exitval = 1; | ||||||
| 			break; | 			break; | ||||||
| 		case MSG_READY: | 		case MSG_READY: | ||||||
| 			if (datalen != 0) | 			if (datalen != 0) | ||||||
| 				fatalx("bad MSG_READY size"); | 				fatalx("bad MSG_READY size"); | ||||||
|  |  | ||||||
| 			client_main();	/* doesn't return */ | 			event_loopexit(NULL);	/* move to client_main() */ | ||||||
|  | 			break; | ||||||
| 		case MSG_VERSION: | 		case MSG_VERSION: | ||||||
| 			if (datalen != 0) | 			if (datalen != 0) | ||||||
| 				fatalx("bad MSG_VERSION size"); | 				fatalx("bad MSG_VERSION size"); | ||||||
|  |  | ||||||
| 			log_warnx("protocol version mismatch (client %u, " | 			log_warnx("protocol version mismatch (client %u, " | ||||||
| 			    "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid); | 			    "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid); | ||||||
| 			*retcode = 1; | 			exit(1); | ||||||
| 			return (-1); |  | ||||||
| 		case MSG_SHELL: | 		case MSG_SHELL: | ||||||
| 			if (datalen != sizeof shelldata) | 			if (datalen != sizeof shelldata) | ||||||
| 				fatalx("bad MSG_SHELL size"); | 				fatalx("bad MSG_SHELL size"); | ||||||
| 			memcpy(&shelldata, imsg.data, sizeof shelldata); | 			memcpy(&shelldata, imsg.data, sizeof shelldata); | ||||||
| 			shelldata.shell[(sizeof shelldata.shell) - 1] = '\0'; | 			shelldata.shell[(sizeof shelldata.shell) - 1] = '\0'; | ||||||
|  |  | ||||||
|  | 			main_clear_signals(); | ||||||
|  |  | ||||||
| 			shell_exec(shelldata.shell, shellcmd); | 			shell_exec(shelldata.shell, shellcmd); | ||||||
| 		default: | 		default: | ||||||
| 			fatalx("unexpected message"); | 			fatalx("unexpected message"); | ||||||
| @@ -653,26 +645,3 @@ dispatch_imsg(struct imsgbuf *ibuf, const char *shellcmd, int *retcode) | |||||||
| 		imsg_free(&imsg); | 		imsg_free(&imsg); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| __dead void |  | ||||||
| shell_exec(const char *shell, const char *shellcmd) |  | ||||||
| { |  | ||||||
| 	const char	*shellname, *ptr; |  | ||||||
| 	char		*argv0; |  | ||||||
|  |  | ||||||
| 	sigreset(); |  | ||||||
| 				 |  | ||||||
| 	ptr = strrchr(shell, '/'); |  | ||||||
| 	if (ptr != NULL && *(ptr + 1) != '\0') |  | ||||||
| 		shellname = ptr + 1; |  | ||||||
| 	else |  | ||||||
| 		shellname = shell; |  | ||||||
| 	if (login_shell) |  | ||||||
| 		xasprintf(&argv0, "-%s", shellname); |  | ||||||
| 	else |  | ||||||
| 		xasprintf(&argv0, "%s", shellname); |  | ||||||
| 	setenv("SHELL", shell, 1); |  | ||||||
|  |  | ||||||
| 	execl(shell, argv0, "-c", shellcmd, (char *) NULL); |  | ||||||
| 	fatal("execl failed"); |  | ||||||
| } |  | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								tmux.h
									
									
									
									
									
								
							| @@ -28,9 +28,9 @@ | |||||||
| #include <sys/uio.h> | #include <sys/uio.h> | ||||||
|  |  | ||||||
| #include <bitstring.h> | #include <bitstring.h> | ||||||
|  | #include <event.h> | ||||||
| #include <getopt.h> | #include <getopt.h> | ||||||
| #include <limits.h> | #include <limits.h> | ||||||
| #include <poll.h> |  | ||||||
| #include <signal.h> | #include <signal.h> | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| @@ -661,6 +661,7 @@ struct job { | |||||||
| 	struct client	*client; | 	struct client	*client; | ||||||
|  |  | ||||||
| 	int		 fd; | 	int		 fd; | ||||||
|  | 	struct event	 event; | ||||||
| 	struct buffer	*out; | 	struct buffer	*out; | ||||||
|  |  | ||||||
| 	void		(*callbackfn)(struct job *); | 	void		(*callbackfn)(struct job *); | ||||||
| @@ -796,14 +797,17 @@ struct window_pane { | |||||||
| 	char		*cwd; | 	char		*cwd; | ||||||
|  |  | ||||||
| 	pid_t		 pid; | 	pid_t		 pid; | ||||||
| 	int		 fd; |  | ||||||
| 	char		 tty[TTY_NAME_MAX]; | 	char		 tty[TTY_NAME_MAX]; | ||||||
|  |  | ||||||
|  | 	int		 fd; | ||||||
|  | 	struct event	 event; | ||||||
| 	struct buffer	*in; | 	struct buffer	*in; | ||||||
| 	struct buffer	*out; | 	struct buffer	*out; | ||||||
|  |  | ||||||
| 	struct input_ctx ictx; | 	struct input_ctx ictx; | ||||||
|  |  | ||||||
| 	int		 pipe_fd; | 	int		 pipe_fd; | ||||||
|  | 	struct event	 pipe_event; | ||||||
| 	struct buffer	*pipe_buf; | 	struct buffer	*pipe_buf; | ||||||
| 	size_t		 pipe_off; | 	size_t		 pipe_off; | ||||||
|  |  | ||||||
| @@ -998,6 +1002,7 @@ struct tty { | |||||||
| 	struct tty_term	*term; | 	struct tty_term	*term; | ||||||
|  |  | ||||||
| 	int		 fd; | 	int		 fd; | ||||||
|  | 	struct event	 event; | ||||||
| 	struct buffer	*in; | 	struct buffer	*in; | ||||||
| 	struct buffer	*out; | 	struct buffer	*out; | ||||||
|  |  | ||||||
| @@ -1062,6 +1067,7 @@ struct mouse_event { | |||||||
| /* Client connection. */ | /* Client connection. */ | ||||||
| struct client { | struct client { | ||||||
| 	struct imsgbuf	 ibuf; | 	struct imsgbuf	 ibuf; | ||||||
|  | 	struct event	 event; | ||||||
|  |  | ||||||
| 	struct timeval	 creation_time; | 	struct timeval	 creation_time; | ||||||
| 	struct timeval	 activity_time; | 	struct timeval	 activity_time; | ||||||
| @@ -1235,12 +1241,6 @@ extern const struct set_option_entry set_option_table[]; | |||||||
| extern const struct set_option_entry set_window_option_table[]; | extern const struct set_option_entry set_window_option_table[]; | ||||||
|  |  | ||||||
| /* tmux.c */ | /* tmux.c */ | ||||||
| extern volatile sig_atomic_t sigwinch; |  | ||||||
| extern volatile sig_atomic_t sigterm; |  | ||||||
| extern volatile sig_atomic_t sigcont; |  | ||||||
| extern volatile sig_atomic_t sigchld; |  | ||||||
| extern volatile sig_atomic_t sigusr1; |  | ||||||
| extern volatile sig_atomic_t sigusr2; |  | ||||||
| extern struct options global_s_options; | extern struct options global_s_options; | ||||||
| extern struct options global_w_options; | extern struct options global_w_options; | ||||||
| extern struct environ global_environ; | extern struct environ global_environ; | ||||||
| @@ -1251,9 +1251,6 @@ extern time_t	 start_time; | |||||||
| extern char 	*socket_path; | extern char 	*socket_path; | ||||||
| extern int	 login_shell; | extern int	 login_shell; | ||||||
| void		 logfile(const char *); | void		 logfile(const char *); | ||||||
| void		 siginit(void); |  | ||||||
| void		 sigreset(void); |  | ||||||
| void		 sighandler(int); |  | ||||||
| const char	*getshell(void); | const char	*getshell(void); | ||||||
| int		 checkshell(const char *); | int		 checkshell(const char *); | ||||||
| int		 areshell(const char *); | int		 areshell(const char *); | ||||||
| @@ -1582,23 +1579,24 @@ const char *key_string_lookup_key(int); | |||||||
| extern struct clients clients; | extern struct clients clients; | ||||||
| extern struct clients dead_clients; | extern struct clients dead_clients; | ||||||
| int	 server_start(char *); | int	 server_start(char *); | ||||||
| void	 server_poll_add(int, int, void (*)(int, int, void *), void *); | void	 server_signal_set(void); | ||||||
|  | void	 server_signal_clear(void); | ||||||
|  |  | ||||||
| /* server-client.c */ | /* server-client.c */ | ||||||
| void	 server_client_create(int); | void	 server_client_create(int); | ||||||
| void	 server_client_lost(struct client *); | void	 server_client_lost(struct client *); | ||||||
| void	 server_client_prepare(void); | void	 server_client_prepare(void); | ||||||
| void	 server_client_callback(int, int, void *); | void	 server_client_callback(int, short, void *); | ||||||
| void	 server_client_loop(void); | void	 server_client_loop(void); | ||||||
|  |  | ||||||
| /* server-job.c */ | /* server-job.c */ | ||||||
| void	 server_job_prepare(void); | void	 server_job_prepare(void); | ||||||
| void	 server_job_callback(int, int, void *); | void	 server_job_callback(int, short, void *); | ||||||
| void	 server_job_loop(void); | void	 server_job_loop(void); | ||||||
|  |  | ||||||
| /* server-window.c */ | /* server-window.c */ | ||||||
| void	 server_window_prepare(void); | void	 server_window_prepare(void); | ||||||
| void	 server_window_callback(int, int, void *); | void	 server_window_callback(int, short, void *); | ||||||
| void	 server_window_loop(void); | void	 server_window_loop(void); | ||||||
|  |  | ||||||
| /* server-fn.c */ | /* server-fn.c */ | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								window.c
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								window.c
									
									
									
									
									
								
							| @@ -455,10 +455,12 @@ window_pane_destroy(struct window_pane *wp) | |||||||
| 	if (wp->pipe_fd != -1) { | 	if (wp->pipe_fd != -1) { | ||||||
| 		buffer_destroy(wp->pipe_buf); | 		buffer_destroy(wp->pipe_buf); | ||||||
| 		close(wp->pipe_fd); | 		close(wp->pipe_fd); | ||||||
|  | 		event_del(&wp->pipe_event); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	buffer_destroy(wp->in); | 	buffer_destroy(wp->in); | ||||||
| 	buffer_destroy(wp->out); | 	buffer_destroy(wp->out); | ||||||
|  | 	event_del(&wp->event); | ||||||
|  |  | ||||||
| 	if (wp->cwd != NULL) | 	if (wp->cwd != NULL) | ||||||
| 		xfree(wp->cwd); | 		xfree(wp->cwd); | ||||||
| @@ -543,7 +545,7 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, | |||||||
| 				setenv(envent->name, envent->value, 1); | 				setenv(envent->name, envent->value, 1); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		sigreset(); | 		server_signal_clear(); | ||||||
| 		log_close(); | 		log_close(); | ||||||
|  |  | ||||||
| 		if (*wp->cmd != '\0') { | 		if (*wp->cmd != '\0') { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Nicholas Marriott
					Nicholas Marriott