mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +00:00 
			
		
		
		
	Sync OpenBSD patchset 1114:
Instead of passing stdin/stdout/stderr file descriptors over imsg and handling them in the server, handle them in the client and pass buffers over imsg. This is much tidier for some upcoming changes and the performance hit isn't critical. The tty fd is still passed to the server as before. This bumps the tmux protocol version so new clients and old servers are incompatible.
This commit is contained in:
		
							
								
								
									
										64
									
								
								client.c
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								client.c
									
									
									
									
									
								
							| @@ -35,6 +35,7 @@ | |||||||
|  |  | ||||||
| struct imsgbuf	client_ibuf; | struct imsgbuf	client_ibuf; | ||||||
| struct event	client_event; | struct event	client_event; | ||||||
|  | struct event	client_stdin; | ||||||
| enum { | enum { | ||||||
| 	CLIENT_EXIT_NONE, | 	CLIENT_EXIT_NONE, | ||||||
| 	CLIENT_EXIT_DETACHED, | 	CLIENT_EXIT_DETACHED, | ||||||
| @@ -56,6 +57,7 @@ void		client_send_environ(void); | |||||||
| void		client_write_server(enum msgtype, void *, size_t); | void		client_write_server(enum msgtype, void *, size_t); | ||||||
| void		client_update_event(void); | void		client_update_event(void); | ||||||
| void		client_signal(int, short, void *); | void		client_signal(int, short, void *); | ||||||
|  | void		client_stdin_callback(int, short, void *); | ||||||
| void		client_callback(int, short, void *); | void		client_callback(int, short, void *); | ||||||
| int		client_dispatch_attached(void); | int		client_dispatch_attached(void); | ||||||
| int		client_dispatch_wait(void *); | int		client_dispatch_wait(void *); | ||||||
| @@ -229,6 +231,11 @@ client_main(int argc, char **argv, int flags) | |||||||
| 	imsg_init(&client_ibuf, fd); | 	imsg_init(&client_ibuf, fd); | ||||||
| 	event_set(&client_event, fd, EV_READ, client_callback, shell_cmd); | 	event_set(&client_event, fd, EV_READ, client_callback, shell_cmd); | ||||||
|  |  | ||||||
|  | 	/* Create stdin handler. */ | ||||||
|  | 	setblocking(STDIN_FILENO, 0); | ||||||
|  | 	event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, | ||||||
|  | 	    client_stdin_callback, NULL); | ||||||
|  |  | ||||||
| 	/* Establish signal handlers. */ | 	/* Establish signal handlers. */ | ||||||
| 	set_signals(client_signal); | 	set_signals(client_signal); | ||||||
|  |  | ||||||
| @@ -257,6 +264,7 @@ client_main(int argc, char **argv, int flags) | |||||||
|  |  | ||||||
| 	/* Set the event and dispatch. */ | 	/* Set the event and dispatch. */ | ||||||
| 	client_update_event(); | 	client_update_event(); | ||||||
|  | 	event_add (&client_stdin, NULL); | ||||||
| 	event_dispatch(); | 	event_dispatch(); | ||||||
|  |  | ||||||
| 	/* Print the exit message, if any, and exit. */ | 	/* Print the exit message, if any, and exit. */ | ||||||
| @@ -268,6 +276,7 @@ client_main(int argc, char **argv, int flags) | |||||||
| 		if (client_exittype == MSG_DETACHKILL && ppid > 1) | 		if (client_exittype == MSG_DETACHKILL && ppid > 1) | ||||||
| 			kill(ppid, SIGHUP); | 			kill(ppid, SIGHUP); | ||||||
| 	} | 	} | ||||||
|  | 	setblocking(STDIN_FILENO, 1); | ||||||
| 	return (client_exitval); | 	return (client_exitval); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -289,20 +298,11 @@ client_send_identify(int flags) | |||||||
| 	    strlcpy(data.term, term, sizeof data.term) >= sizeof data.term) | 	    strlcpy(data.term, term, sizeof data.term) >= sizeof data.term) | ||||||
| 		*data.term = '\0'; | 		*data.term = '\0'; | ||||||
|  |  | ||||||
| 	if ((fd = dup(STDOUT_FILENO)) == -1) |  | ||||||
| 		fatal("dup failed"); |  | ||||||
| 	imsg_compose(&client_ibuf, |  | ||||||
| 	    MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0); |  | ||||||
|  |  | ||||||
| 	if ((fd = dup(STDERR_FILENO)) == -1) |  | ||||||
| 		fatal("dup failed"); |  | ||||||
| 	imsg_compose(&client_ibuf, |  | ||||||
| 	    MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0); |  | ||||||
|  |  | ||||||
| 	if ((fd = dup(STDIN_FILENO)) == -1) | 	if ((fd = dup(STDIN_FILENO)) == -1) | ||||||
| 		fatal("dup failed"); | 		fatal("dup failed"); | ||||||
| 	imsg_compose(&client_ibuf, | 	imsg_compose(&client_ibuf, | ||||||
| 	    MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data); | 	    MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data); | ||||||
|  | 	client_update_event(); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Forward entire environment to server. */ | /* Forward entire environment to server. */ | ||||||
| @@ -324,6 +324,7 @@ void | |||||||
| client_write_server(enum msgtype type, void *buf, size_t len) | client_write_server(enum msgtype type, void *buf, size_t len) | ||||||
| { | { | ||||||
| 	imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len); | 	imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len); | ||||||
|  | 	client_update_event(); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Update client event based on whether it needs to read or read and write. */ | /* Update client event based on whether it needs to read or read and write. */ | ||||||
| @@ -423,6 +424,23 @@ lost_server: | |||||||
| 	event_loopexit(NULL); | 	event_loopexit(NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Callback for client stdin read events. */ | ||||||
|  | /* ARGSUSED */ | ||||||
|  | void | ||||||
|  | client_stdin_callback(unused int fd, unused short events, unused void *data1) | ||||||
|  | { | ||||||
|  | 	struct msg_stdin_data	data; | ||||||
|  |  | ||||||
|  | 	data.size = read(STDIN_FILENO, data.data, sizeof data.data); | ||||||
|  | 	if (data.size < 0 && (errno == EINTR || errno == EAGAIN)) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	client_write_server(MSG_STDIN, &data, sizeof data); | ||||||
|  | 	if (data.size <= 0) | ||||||
|  | 		event_del(&client_stdin); | ||||||
|  | 	client_update_event(); | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Dispatch imsgs when in wait state (before MSG_READY). */ | /* Dispatch imsgs when in wait state (before MSG_READY). */ | ||||||
| int | int | ||||||
| client_dispatch_wait(void *data) | client_dispatch_wait(void *data) | ||||||
| @@ -431,11 +449,10 @@ client_dispatch_wait(void *data) | |||||||
| 	ssize_t			n, datalen; | 	ssize_t			n, datalen; | ||||||
| 	struct msg_shell_data	shelldata; | 	struct msg_shell_data	shelldata; | ||||||
| 	struct msg_exit_data	exitdata; | 	struct msg_exit_data	exitdata; | ||||||
|  | 	struct msg_stdout_data	stdoutdata; | ||||||
|  | 	struct msg_stderr_data	stderrdata; | ||||||
| 	const char             *shellcmd = data; | 	const char             *shellcmd = data; | ||||||
|  |  | ||||||
| 	if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) |  | ||||||
| 		fatalx("imsg_read failed"); |  | ||||||
|  |  | ||||||
| 	for (;;) { | 	for (;;) { | ||||||
| 		if ((n = imsg_get(&client_ibuf, &imsg)) == -1) | 		if ((n = imsg_get(&client_ibuf, &imsg)) == -1) | ||||||
| 			fatalx("imsg_get failed"); | 			fatalx("imsg_get failed"); | ||||||
| @@ -443,6 +460,7 @@ client_dispatch_wait(void *data) | |||||||
| 			return (0); | 			return (0); | ||||||
| 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE; | 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE; | ||||||
|  |  | ||||||
|  | 		log_debug("got %d from server", imsg.hdr.type); | ||||||
| 		switch (imsg.hdr.type) { | 		switch (imsg.hdr.type) { | ||||||
| 		case MSG_EXIT: | 		case MSG_EXIT: | ||||||
| 		case MSG_SHUTDOWN: | 		case MSG_SHUTDOWN: | ||||||
| @@ -459,14 +477,30 @@ client_dispatch_wait(void *data) | |||||||
| 			if (datalen != 0) | 			if (datalen != 0) | ||||||
| 				fatalx("bad MSG_READY size"); | 				fatalx("bad MSG_READY size"); | ||||||
|  |  | ||||||
|  | 			event_del(&client_stdin); | ||||||
| 			client_attached = 1; | 			client_attached = 1; | ||||||
| 			break; | 			break; | ||||||
|  | 		case MSG_STDOUT: | ||||||
|  | 			if (datalen != sizeof stdoutdata) | ||||||
|  | 				fatalx("bad MSG_STDOUT"); | ||||||
|  | 			memcpy(&stdoutdata, imsg.data, sizeof stdoutdata); | ||||||
|  |  | ||||||
|  | 			fwrite(stdoutdata.data, stdoutdata.size, 1, stdout); | ||||||
|  | 			break; | ||||||
|  | 		case MSG_STDERR: | ||||||
|  | 			if (datalen != sizeof stderrdata) | ||||||
|  | 				fatalx("bad MSG_STDERR"); | ||||||
|  | 			memcpy(&stderrdata, imsg.data, sizeof stderrdata); | ||||||
|  |  | ||||||
|  | 			fwrite(stderrdata.data, stderrdata.size, 1, stderr); | ||||||
|  | 			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, " | 			fprintf(stderr, "protocol version mismatch " | ||||||
| 			    "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid); | 			    "(client %u, server %u)\n", PROTOCOL_VERSION, | ||||||
|  | 			    imsg.hdr.peerid); | ||||||
| 			client_exitval = 1; | 			client_exitval = 1; | ||||||
|  |  | ||||||
| 			imsg_free(&imsg); | 			imsg_free(&imsg); | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| int	cmd_load_buffer_exec(struct cmd *, struct cmd_ctx *); | int	cmd_load_buffer_exec(struct cmd *, struct cmd_ctx *); | ||||||
| void	cmd_load_buffer_callback(struct client *, void *); | void	cmd_load_buffer_callback(struct client *, int, void *); | ||||||
|  |  | ||||||
| const struct cmd_entry cmd_load_buffer_entry = { | const struct cmd_entry cmd_load_buffer_entry = { | ||||||
| 	"load-buffer", "loadb", | 	"load-buffer", "loadb", | ||||||
| @@ -54,8 +54,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) | |||||||
| 	char		*pdata, *new_pdata, *cause; | 	char		*pdata, *new_pdata, *cause; | ||||||
| 	size_t		 psize; | 	size_t		 psize; | ||||||
| 	u_int		 limit; | 	u_int		 limit; | ||||||
| 	int		 ch, buffer; | 	int		 ch, error, buffer, *buffer_ptr; | ||||||
| 	int		*buffer_ptr; |  | ||||||
|  |  | ||||||
| 	if (!args_has(args, 'b')) | 	if (!args_has(args, 'b')) | ||||||
| 		buffer = -1; | 		buffer = -1; | ||||||
| @@ -70,27 +69,16 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) | |||||||
|  |  | ||||||
| 	path = args->argv[0]; | 	path = args->argv[0]; | ||||||
| 	if (strcmp(path, "-") == 0) { | 	if (strcmp(path, "-") == 0) { | ||||||
| 		if (c == NULL) { |  | ||||||
| 			ctx->error(ctx, "%s: can't read from stdin", path); |  | ||||||
| 			return (-1); |  | ||||||
| 		} |  | ||||||
| 		if (c->flags & CLIENT_TERMINAL) { |  | ||||||
| 			ctx->error(ctx, "%s: stdin is a tty", path); |  | ||||||
| 			return (-1); |  | ||||||
| 		} |  | ||||||
| 		if (c->stdin_fd == -1) { |  | ||||||
| 			ctx->error(ctx, "%s: can't read from stdin", path); |  | ||||||
| 			return (-1); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		buffer_ptr = xmalloc(sizeof *buffer_ptr); | 		buffer_ptr = xmalloc(sizeof *buffer_ptr); | ||||||
| 		*buffer_ptr = buffer; | 		*buffer_ptr = buffer; | ||||||
|  |  | ||||||
| 		c->stdin_data = buffer_ptr; | 		error = server_set_stdin_callback (c, cmd_load_buffer_callback, | ||||||
| 		c->stdin_callback = cmd_load_buffer_callback; | 		    buffer_ptr, &cause); | ||||||
|  | 		if (error != 0) { | ||||||
| 		c->references++; | 			ctx->error(ctx, "%s: %s", path, cause); | ||||||
| 		bufferevent_enable(c->stdin_event, EV_READ); | 			xfree(cause); | ||||||
|  | 			return (-1); | ||||||
|  | 		} | ||||||
| 		return (1); | 		return (1); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -154,35 +142,36 @@ error: | |||||||
| } | } | ||||||
|  |  | ||||||
| void | void | ||||||
| cmd_load_buffer_callback(struct client *c, void *data) | cmd_load_buffer_callback(struct client *c, int closed, void *data) | ||||||
| { | { | ||||||
| 	int	*buffer = data; | 	int	*buffer = data; | ||||||
| 	char	*pdata; | 	char	*pdata; | ||||||
| 	size_t	 psize; | 	size_t	 psize; | ||||||
| 	u_int	 limit; | 	u_int	 limit; | ||||||
|  |  | ||||||
| 	/* | 	if (!closed) | ||||||
| 	 * Event callback has already checked client is not dead and reduced | 		return; | ||||||
| 	 * its reference count. But tell it to exit. | 	c->stdin_callback = NULL; | ||||||
| 	 */ |  | ||||||
|  | 	c->references--; | ||||||
| 	c->flags |= CLIENT_EXIT; | 	c->flags |= CLIENT_EXIT; | ||||||
|  |  | ||||||
| 	psize = EVBUFFER_LENGTH(c->stdin_event->input); | 	psize = EVBUFFER_LENGTH(c->stdin_data); | ||||||
| 	if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) { | 	if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) { | ||||||
| 		xfree(data); | 		xfree(data); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 	bufferevent_read(c->stdin_event, pdata, psize); | 	memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize); | ||||||
| 	pdata[psize] = '\0'; | 	pdata[psize] = '\0'; | ||||||
|  | 	evbuffer_drain(c->stdin_data, psize); | ||||||
|  |  | ||||||
| 	limit = options_get_number(&global_options, "buffer-limit"); | 	limit = options_get_number(&global_options, "buffer-limit"); | ||||||
| 	if (*buffer == -1) | 	if (*buffer == -1) | ||||||
| 		paste_add(&global_buffers, pdata, psize, limit); | 		paste_add(&global_buffers, pdata, psize, limit); | ||||||
| 	else if (paste_replace(&global_buffers, *buffer, pdata, psize) != 0) { | 	else if (paste_replace(&global_buffers, *buffer, pdata, psize) != 0) { | ||||||
| 		/* No context so can't use server_client_msg_error. */ | 		/* No context so can't use server_client_msg_error. */ | ||||||
| 		evbuffer_add_printf( | 		evbuffer_add_printf(c->stderr_data, "no buffer %d\n", *buffer); | ||||||
| 		    c->stderr_event->output, "no buffer %d\n", *buffer); | 		server_push_stderr(c); | ||||||
| 		bufferevent_enable(c->stderr_event, EV_WRITE); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	xfree(data); | 	xfree(data); | ||||||
|   | |||||||
| @@ -79,7 +79,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) | |||||||
| 			ctx->error(ctx, "%s: can't write to stdout", path); | 			ctx->error(ctx, "%s: can't write to stdout", path); | ||||||
| 			return (-1); | 			return (-1); | ||||||
| 		} | 		} | ||||||
| 		bufferevent_write(c->stdout_event, pb->data, pb->size); | 		evbuffer_add(c->stdout_data, pb->data, pb->size); | ||||||
|  | 		server_push_stdout(c); | ||||||
| 	} else { | 	} else { | ||||||
| 		if (c != NULL) | 		if (c != NULL) | ||||||
| 			wd = c->cwd; | 			wd = c->cwd; | ||||||
|   | |||||||
							
								
								
									
										159
									
								
								server-client.c
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								server-client.c
									
									
									
									
									
								
							| @@ -34,9 +34,6 @@ void	server_client_check_exit(struct client *); | |||||||
| void	server_client_check_redraw(struct client *); | void	server_client_check_redraw(struct client *); | ||||||
| void	server_client_set_title(struct client *); | void	server_client_set_title(struct client *); | ||||||
| void	server_client_reset_state(struct client *); | void	server_client_reset_state(struct client *); | ||||||
| void	server_client_in_callback(struct bufferevent *, short, void *); |  | ||||||
| void	server_client_out_callback(struct bufferevent *, short, void *); |  | ||||||
| void	server_client_err_callback(struct bufferevent *, short, void *); |  | ||||||
|  |  | ||||||
| int	server_client_msg_dispatch(struct client *); | int	server_client_msg_dispatch(struct client *); | ||||||
| void	server_client_msg_command(struct client *, struct msg_command_data *); | void	server_client_msg_command(struct client *, struct msg_command_data *); | ||||||
| @@ -66,9 +63,9 @@ server_client_create(int fd) | |||||||
| 		fatal("gettimeofday failed"); | 		fatal("gettimeofday failed"); | ||||||
| 	memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time); | 	memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time); | ||||||
|  |  | ||||||
| 	c->stdin_event = NULL; | 	c->stdin_data = evbuffer_new (); | ||||||
| 	c->stdout_event = NULL; | 	c->stdout_data = evbuffer_new (); | ||||||
| 	c->stderr_event = NULL; | 	c->stderr_data = evbuffer_new (); | ||||||
|  |  | ||||||
| 	c->tty.fd = -1; | 	c->tty.fd = -1; | ||||||
| 	c->title = NULL; | 	c->title = NULL; | ||||||
| @@ -143,24 +140,9 @@ server_client_lost(struct client *c) | |||||||
| 	if (c->flags & CLIENT_TERMINAL) | 	if (c->flags & CLIENT_TERMINAL) | ||||||
| 		tty_free(&c->tty); | 		tty_free(&c->tty); | ||||||
|  |  | ||||||
| 	if (c->stdin_event != NULL) | 	evbuffer_free (c->stdin_data); | ||||||
| 		bufferevent_free(c->stdin_event); | 	evbuffer_free (c->stdout_data); | ||||||
| 	if (c->stdin_fd != -1) { | 	evbuffer_free (c->stderr_data); | ||||||
| 		setblocking(c->stdin_fd, 1); |  | ||||||
| 		close(c->stdin_fd); |  | ||||||
| 	} |  | ||||||
| 	if (c->stdout_event != NULL) |  | ||||||
| 		bufferevent_free(c->stdout_event); |  | ||||||
| 	if (c->stdout_fd != -1) { |  | ||||||
| 		setblocking(c->stdout_fd, 1); |  | ||||||
| 		close(c->stdout_fd); |  | ||||||
| 	} |  | ||||||
| 	if (c->stderr_event != NULL) |  | ||||||
| 		bufferevent_free(c->stderr_event); |  | ||||||
| 	if (c->stderr_fd != -1) { |  | ||||||
| 		setblocking(c->stderr_fd, 1); |  | ||||||
| 		close(c->stderr_fd); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	status_free_jobs(&c->status_new); | 	status_free_jobs(&c->status_new); | ||||||
| 	status_free_jobs(&c->status_old); | 	status_free_jobs(&c->status_old); | ||||||
| @@ -239,6 +221,9 @@ server_client_callback(int fd, short events, void *data) | |||||||
| 			goto client_lost; | 			goto client_lost; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	server_push_stdout(c); | ||||||
|  | 	server_push_stderr(c); | ||||||
|  |  | ||||||
| 	server_update_event(c); | 	server_update_event(c); | ||||||
| 	return; | 	return; | ||||||
|  |  | ||||||
| @@ -602,11 +587,11 @@ server_client_check_exit(struct client *c) | |||||||
| 	if (!(c->flags & CLIENT_EXIT)) | 	if (!(c->flags & CLIENT_EXIT)) | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| 	if (c->stdout_fd != -1 && c->stdout_event != NULL && | 	if (EVBUFFER_LENGTH(c->stdin_data) != 0) | ||||||
| 	    EVBUFFER_LENGTH(c->stdout_event->output) != 0) |  | ||||||
| 		return; | 		return; | ||||||
| 	if (c->stderr_fd != -1 && c->stderr_event != NULL && | 	if (EVBUFFER_LENGTH(c->stdout_data) != 0) | ||||||
| 	    EVBUFFER_LENGTH(c->stderr_event->output) != 0) | 		return; | ||||||
|  | 	if (EVBUFFER_LENGTH(c->stderr_data) != 0) | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| 	exitdata.retcode = c->retcode; | 	exitdata.retcode = c->retcode; | ||||||
| @@ -685,55 +670,6 @@ server_client_set_title(struct client *c) | |||||||
| 	xfree(title); | 	xfree(title); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * Error callback for client stdin. Caller must increase reference count when |  | ||||||
|  * enabling event! |  | ||||||
|  */ |  | ||||||
| void |  | ||||||
| server_client_in_callback( |  | ||||||
|     unused struct bufferevent *bufev, unused short what, void *data) |  | ||||||
| { |  | ||||||
| 	struct client	*c = data; |  | ||||||
|  |  | ||||||
| 	c->references--; |  | ||||||
| 	if (c->flags & CLIENT_DEAD) |  | ||||||
| 		return; |  | ||||||
|  |  | ||||||
| 	bufferevent_disable(c->stdin_event, EV_READ|EV_WRITE); |  | ||||||
| 	setblocking(c->stdin_fd, 1); |  | ||||||
| 	close(c->stdin_fd); |  | ||||||
| 	c->stdin_fd = -1; |  | ||||||
|  |  | ||||||
| 	if (c->stdin_callback != NULL) |  | ||||||
| 		c->stdin_callback(c, c->stdin_data); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Error callback for client stdout. */ |  | ||||||
| void |  | ||||||
| server_client_out_callback( |  | ||||||
|     unused struct bufferevent *bufev, unused short what, unused void *data) |  | ||||||
| { |  | ||||||
| 	struct client	*c = data; |  | ||||||
|  |  | ||||||
| 	bufferevent_disable(c->stdout_event, EV_READ|EV_WRITE); |  | ||||||
| 	setblocking(c->stdout_fd, 1); |  | ||||||
| 	close(c->stdout_fd); |  | ||||||
| 	c->stdout_fd = -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Error callback for client stderr. */ |  | ||||||
| void |  | ||||||
| server_client_err_callback( |  | ||||||
|     unused struct bufferevent *bufev, unused short what, unused void *data) |  | ||||||
| { |  | ||||||
| 	struct client	*c = data; |  | ||||||
|  |  | ||||||
| 	bufferevent_disable(c->stderr_event, EV_READ|EV_WRITE); |  | ||||||
| 	setblocking(c->stderr_fd, 1); |  | ||||||
| 	close(c->stderr_fd); |  | ||||||
| 	c->stderr_fd = -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Dispatch message from client. */ | /* Dispatch message from client. */ | ||||||
| int | int | ||||||
| server_client_msg_dispatch(struct client *c) | server_client_msg_dispatch(struct client *c) | ||||||
| @@ -742,6 +678,7 @@ server_client_msg_dispatch(struct client *c) | |||||||
| 	struct msg_command_data	 commanddata; | 	struct msg_command_data	 commanddata; | ||||||
| 	struct msg_identify_data identifydata; | 	struct msg_identify_data identifydata; | ||||||
| 	struct msg_environ_data	 environdata; | 	struct msg_environ_data	 environdata; | ||||||
|  | 	struct msg_stdin_data	 stdindata; | ||||||
| 	ssize_t			 n, datalen; | 	ssize_t			 n, datalen; | ||||||
|  |  | ||||||
| 	if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) | 	if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) | ||||||
| @@ -777,42 +714,23 @@ server_client_msg_dispatch(struct client *c) | |||||||
| 				fatalx("MSG_IDENTIFY missing fd"); | 				fatalx("MSG_IDENTIFY missing fd"); | ||||||
| 			memcpy(&identifydata, imsg.data, sizeof identifydata); | 			memcpy(&identifydata, imsg.data, sizeof identifydata); | ||||||
|  |  | ||||||
| 			c->stdin_fd = imsg.fd; |  | ||||||
| 			c->stdin_event = bufferevent_new(c->stdin_fd, |  | ||||||
| 			    NULL, NULL, server_client_in_callback, c); |  | ||||||
| 			if (c->stdin_event == NULL) |  | ||||||
| 				fatalx("failed to create stdin event"); |  | ||||||
| 			setblocking(c->stdin_fd, 0); |  | ||||||
|  |  | ||||||
| 			server_client_msg_identify(c, &identifydata, imsg.fd); | 			server_client_msg_identify(c, &identifydata, imsg.fd); | ||||||
| 			break; | 			break; | ||||||
| 		case MSG_STDOUT: | 		case MSG_STDIN: | ||||||
| 			if (datalen != 0) | 			if (datalen != sizeof stdindata) | ||||||
| 				fatalx("bad MSG_STDOUT size"); | 				fatalx("bad MSG_STDIN size"); | ||||||
| 			if (imsg.fd == -1) | 			memcpy(&stdindata, imsg.data, sizeof stdindata); | ||||||
| 				fatalx("MSG_STDOUT missing fd"); |  | ||||||
|  |  | ||||||
| 			c->stdout_fd = imsg.fd; |  | ||||||
| 			c->stdout_event = bufferevent_new(c->stdout_fd, |  | ||||||
| 			    NULL, NULL, server_client_out_callback, c); |  | ||||||
| 			if (c->stdout_event == NULL) |  | ||||||
| 				fatalx("failed to create stdout event"); |  | ||||||
| 			setblocking(c->stdout_fd, 0); |  | ||||||
|  |  | ||||||
| 			break; |  | ||||||
| 		case MSG_STDERR: |  | ||||||
| 			if (datalen != 0) |  | ||||||
| 				fatalx("bad MSG_STDERR size"); |  | ||||||
| 			if (imsg.fd == -1) |  | ||||||
| 				fatalx("MSG_STDERR missing fd"); |  | ||||||
|  |  | ||||||
| 			c->stderr_fd = imsg.fd; |  | ||||||
| 			c->stderr_event = bufferevent_new(c->stderr_fd, |  | ||||||
| 			    NULL, NULL, server_client_err_callback, c); |  | ||||||
| 			if (c->stderr_event == NULL) |  | ||||||
| 				fatalx("failed to create stderr event"); |  | ||||||
| 			setblocking(c->stderr_fd, 0); |  | ||||||
|  |  | ||||||
|  | 			if (c->stdin_callback == NULL) | ||||||
|  | 				break; | ||||||
|  | 			if (stdindata.size <= 0) | ||||||
|  | 				c->stdin_closed = 1; | ||||||
|  | 			else { | ||||||
|  | 				evbuffer_add(c->stdin_data, stdindata.data, | ||||||
|  | 				    stdindata.size); | ||||||
|  | 			} | ||||||
|  | 			c->stdin_callback(c, c->stdin_closed, | ||||||
|  | 			    c->stdin_callback_data); | ||||||
| 			break; | 			break; | ||||||
| 		case MSG_RESIZE: | 		case MSG_RESIZE: | ||||||
| 			if (datalen != 0) | 			if (datalen != 0) | ||||||
| @@ -879,10 +797,11 @@ server_client_msg_error(struct cmd_ctx *ctx, const char *fmt, ...) | |||||||
| 	va_list	ap; | 	va_list	ap; | ||||||
|  |  | ||||||
| 	va_start(ap, fmt); | 	va_start(ap, fmt); | ||||||
| 	evbuffer_add_vprintf(ctx->cmdclient->stderr_event->output, fmt, ap); | 	evbuffer_add_vprintf(ctx->cmdclient->stderr_data, fmt, ap); | ||||||
| 	va_end(ap); | 	va_end(ap); | ||||||
|  |  | ||||||
| 	bufferevent_write(ctx->cmdclient->stderr_event, "\n", 1); | 	evbuffer_add(ctx->cmdclient->stderr_data, "\n", 1); | ||||||
|  | 	server_push_stderr(ctx->cmdclient); | ||||||
| 	ctx->cmdclient->retcode = 1; | 	ctx->cmdclient->retcode = 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -893,10 +812,11 @@ server_client_msg_print(struct cmd_ctx *ctx, const char *fmt, ...) | |||||||
| 	va_list	ap; | 	va_list	ap; | ||||||
|  |  | ||||||
| 	va_start(ap, fmt); | 	va_start(ap, fmt); | ||||||
| 	evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap); | 	evbuffer_add_vprintf(ctx->cmdclient->stdout_data, fmt, ap); | ||||||
| 	va_end(ap); | 	va_end(ap); | ||||||
|  |  | ||||||
| 	bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1); | 	evbuffer_add(ctx->cmdclient->stdout_data, "\n", 1); | ||||||
|  | 	server_push_stdout(ctx->cmdclient); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Callback to send print message to client, if not quiet. */ | /* Callback to send print message to client, if not quiet. */ | ||||||
| @@ -909,10 +829,11 @@ server_client_msg_info(struct cmd_ctx *ctx, const char *fmt, ...) | |||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| 	va_start(ap, fmt); | 	va_start(ap, fmt); | ||||||
| 	evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap); | 	evbuffer_add_vprintf(ctx->cmdclient->stdout_data, fmt, ap); | ||||||
| 	va_end(ap); | 	va_end(ap); | ||||||
|  |  | ||||||
| 	bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1); | 	evbuffer_add(ctx->cmdclient->stdout_data, "\n", 1); | ||||||
|  | 	server_push_stdout(ctx->cmdclient); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Handle command message. */ | /* Handle command message. */ | ||||||
| @@ -969,8 +890,6 @@ void | |||||||
| server_client_msg_identify( | server_client_msg_identify( | ||||||
|     struct client *c, struct msg_identify_data *data, int fd) |     struct client *c, struct msg_identify_data *data, int fd) | ||||||
| { | { | ||||||
| 	int	tty_fd; |  | ||||||
|  |  | ||||||
| 	c->cwd = NULL; | 	c->cwd = NULL; | ||||||
| 	data->cwd[(sizeof data->cwd) - 1] = '\0'; | 	data->cwd[(sizeof data->cwd) - 1] = '\0'; | ||||||
| 	if (*data->cwd != '\0') | 	if (*data->cwd != '\0') | ||||||
| @@ -978,10 +897,8 @@ server_client_msg_identify( | |||||||
|  |  | ||||||
| 	if (!isatty(fd)) | 	if (!isatty(fd)) | ||||||
| 	    return; | 	    return; | ||||||
| 	if ((tty_fd = dup(fd)) == -1) |  | ||||||
| 		fatal("dup failed"); |  | ||||||
| 	data->term[(sizeof data->term) - 1] = '\0'; | 	data->term[(sizeof data->term) - 1] = '\0'; | ||||||
| 	tty_init(&c->tty, tty_fd, data->term); | 	tty_init(&c->tty, fd, data->term); | ||||||
| 	if (data->flags & IDENTIFY_UTF8) | 	if (data->flags & IDENTIFY_UTF8) | ||||||
| 		c->tty.flags |= TTY_UTF8; | 		c->tty.flags |= TTY_UTF8; | ||||||
| 	if (data->flags & IDENTIFY_256COLOURS) | 	if (data->flags & IDENTIFY_256COLOURS) | ||||||
|   | |||||||
							
								
								
									
										80
									
								
								server-fn.c
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								server-fn.c
									
									
									
									
									
								
							| @@ -46,17 +46,21 @@ server_fill_environ(struct session *s, struct environ *env) | |||||||
| 	environ_set(env, "TMUX", var); | 	environ_set(env, "TMUX", var); | ||||||
| } | } | ||||||
|  |  | ||||||
| void | int | ||||||
| server_write_client( | server_write_client( | ||||||
|     struct client *c, enum msgtype type, const void *buf, size_t len) |     struct client *c, enum msgtype type, const void *buf, size_t len) | ||||||
| { | { | ||||||
| 	struct imsgbuf	*ibuf = &c->ibuf; | 	struct imsgbuf	*ibuf = &c->ibuf; | ||||||
|  | 	int              error; | ||||||
|  |  | ||||||
| 	if (c->flags & CLIENT_BAD) | 	if (c->flags & CLIENT_BAD) | ||||||
| 		return; | 		return (-1); | ||||||
| 	log_debug("writing %d to client %d", type, c->ibuf.fd); | 	log_debug("writing %d to client %d", type, c->ibuf.fd); | ||||||
| 	imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, (void *) buf, len); | 	error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, | ||||||
| 	server_update_event(c); | 	    (void *) buf, len); | ||||||
|  | 	if (error == 1) | ||||||
|  | 		server_update_event(c); | ||||||
|  | 	return (error == 1 ? 0 : -1); | ||||||
| } | } | ||||||
|  |  | ||||||
| void | void | ||||||
| @@ -502,3 +506,71 @@ server_update_event(struct client *c) | |||||||
| 	event_set(&c->event, c->ibuf.fd, events, server_client_callback, c); | 	event_set(&c->event, c->ibuf.fd, events, server_client_callback, c); | ||||||
| 	event_add(&c->event, NULL); | 	event_add(&c->event, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Push stdout to client if possible. */ | ||||||
|  | void | ||||||
|  | server_push_stdout(struct client *c) | ||||||
|  | { | ||||||
|  | 	struct msg_stdout_data data; | ||||||
|  | 	size_t                 size; | ||||||
|  |  | ||||||
|  | 	size = EVBUFFER_LENGTH(c->stdout_data); | ||||||
|  | 	if (size == 0) | ||||||
|  | 		return; | ||||||
|  | 	if (size > sizeof data.data) | ||||||
|  | 		size = sizeof data.data; | ||||||
|  |  | ||||||
|  | 	memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size); | ||||||
|  | 	data.size = size; | ||||||
|  |  | ||||||
|  | 	if (server_write_client(c, MSG_STDOUT, &data, sizeof data) == 0) | ||||||
|  | 		evbuffer_drain(c->stdout_data, size); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Push stderr to client if possible. */ | ||||||
|  | void | ||||||
|  | server_push_stderr(struct client *c) | ||||||
|  | { | ||||||
|  | 	struct msg_stderr_data data; | ||||||
|  | 	size_t                 size; | ||||||
|  |  | ||||||
|  | 	size = EVBUFFER_LENGTH(c->stderr_data); | ||||||
|  | 	if (size == 0) | ||||||
|  | 		return; | ||||||
|  | 	if (size > sizeof data.data) | ||||||
|  | 		size = sizeof data.data; | ||||||
|  |  | ||||||
|  | 	memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size); | ||||||
|  | 	data.size = size; | ||||||
|  |  | ||||||
|  | 	if (server_write_client(c, MSG_STDERR, &data, sizeof data) == 0) | ||||||
|  | 		evbuffer_drain(c->stderr_data, size); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Set stdin callback. */ | ||||||
|  | int | ||||||
|  | server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, | ||||||
|  |     void *), void *cb_data, char **cause) | ||||||
|  | { | ||||||
|  | 	if (c == NULL) { | ||||||
|  | 		*cause = xstrdup("no client with stdin"); | ||||||
|  | 		return (-1); | ||||||
|  | 	} | ||||||
|  | 	if (c->flags & CLIENT_TERMINAL) { | ||||||
|  | 		*cause = xstrdup("stdin is a tty"); | ||||||
|  | 		return (-1); | ||||||
|  | 	} | ||||||
|  | 	if (c->stdin_callback != NULL) { | ||||||
|  | 		*cause = xstrdup("stdin in use"); | ||||||
|  | 		return (-1); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c->stdin_callback_data = cb_data; | ||||||
|  | 	c->stdin_callback = cb; | ||||||
|  |  | ||||||
|  | 	c->references++; | ||||||
|  |  | ||||||
|  | 	if (c->stdin_closed) | ||||||
|  | 		c->stdin_callback (c, 1, c->stdin_callback_data); | ||||||
|  | 	return (0); | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								tmux.h
									
									
									
									
									
								
							| @@ -19,7 +19,7 @@ | |||||||
| #ifndef TMUX_H | #ifndef TMUX_H | ||||||
| #define TMUX_H | #define TMUX_H | ||||||
|  |  | ||||||
| #define PROTOCOL_VERSION 6 | #define PROTOCOL_VERSION 7 | ||||||
|  |  | ||||||
| #include <sys/param.h> | #include <sys/param.h> | ||||||
| #include <sys/time.h> | #include <sys/time.h> | ||||||
| @@ -365,7 +365,7 @@ enum msgtype { | |||||||
| 	MSG_EXITED, | 	MSG_EXITED, | ||||||
| 	MSG_EXITING, | 	MSG_EXITING, | ||||||
| 	MSG_IDENTIFY, | 	MSG_IDENTIFY, | ||||||
| 	MSG_PRINT, | 	MSG_STDIN, | ||||||
| 	MSG_READY, | 	MSG_READY, | ||||||
| 	MSG_RESIZE, | 	MSG_RESIZE, | ||||||
| 	MSG_SHUTDOWN, | 	MSG_SHUTDOWN, | ||||||
| @@ -421,6 +421,21 @@ struct msg_exit_data { | |||||||
| 	int		retcode; | 	int		retcode; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | struct msg_stdin_data { | ||||||
|  | 	ssize_t	size; | ||||||
|  | 	char	data[BUFSIZ]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct msg_stdout_data { | ||||||
|  | 	ssize_t	size; | ||||||
|  | 	char	data[BUFSIZ]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct msg_stderr_data { | ||||||
|  | 	ssize_t	size; | ||||||
|  | 	char	data[BUFSIZ]; | ||||||
|  | }; | ||||||
|  |  | ||||||
| /* Mode key commands. */ | /* Mode key commands. */ | ||||||
| enum mode_key_cmd { | enum mode_key_cmd { | ||||||
| 	MODEKEY_NONE, | 	MODEKEY_NONE, | ||||||
| @@ -1157,16 +1172,12 @@ struct client { | |||||||
|  |  | ||||||
| 	struct tty	 tty; | 	struct tty	 tty; | ||||||
|  |  | ||||||
| 	int		 stdin_fd; | 	void		(*stdin_callback)(struct client *, int, void *); | ||||||
| 	void		*stdin_data; | 	void		*stdin_callback_data; | ||||||
| 	void		(*stdin_callback)(struct client *, void *); | 	struct evbuffer	*stdin_data; | ||||||
| 	struct bufferevent *stdin_event; | 	int              stdin_closed; | ||||||
|  | 	struct evbuffer	*stdout_data; | ||||||
| 	int		 stdout_fd; | 	struct evbuffer	*stderr_data; | ||||||
| 	struct bufferevent *stdout_event; |  | ||||||
|  |  | ||||||
| 	int		 stderr_fd; |  | ||||||
| 	struct bufferevent *stderr_event; |  | ||||||
|  |  | ||||||
| 	struct event	 repeat_timer; | 	struct event	 repeat_timer; | ||||||
|  |  | ||||||
| @@ -1730,7 +1741,7 @@ void	 server_window_loop(void); | |||||||
|  |  | ||||||
| /* server-fn.c */ | /* server-fn.c */ | ||||||
| void	 server_fill_environ(struct session *, struct environ *); | void	 server_fill_environ(struct session *, struct environ *); | ||||||
| void	 server_write_client( | int	 server_write_client( | ||||||
| 	     struct client *, enum msgtype, const void *, size_t); | 	     struct client *, enum msgtype, const void *, size_t); | ||||||
| void	 server_write_session( | void	 server_write_session( | ||||||
| 	     struct session *, enum msgtype, const void *, size_t); | 	     struct session *, enum msgtype, const void *, size_t); | ||||||
| @@ -1758,6 +1769,10 @@ void	 server_check_unattached (void); | |||||||
| void	 server_set_identify(struct client *); | void	 server_set_identify(struct client *); | ||||||
| void	 server_clear_identify(struct client *); | void	 server_clear_identify(struct client *); | ||||||
| void	 server_update_event(struct client *); | void	 server_update_event(struct client *); | ||||||
|  | void	 server_push_stdout(struct client *); | ||||||
|  | void	 server_push_stderr(struct client *); | ||||||
|  | int	 server_set_stdin_callback(struct client *, void (*)(struct client *, | ||||||
|  | 	     int, void *), void *, char **); | ||||||
|  |  | ||||||
| /* status.c */ | /* status.c */ | ||||||
| int	 status_out_cmp(struct status_out *, struct status_out *); | int	 status_out_cmp(struct status_out *, struct status_out *); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Tiago Cunha
					Tiago Cunha