mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +00:00 
			
		
		
		
	Send all three of stdin, stdout, stderr from the client to the server, so that
commands can directly make use of them. This means that load-buffer and save-buffer can have "-" as the file to read from stdin or write to stdout. This is a protocol version bump so the tmux server will need to be restarted after upgrade (or an older client used).
This commit is contained in:
		
							
								
								
									
										9
									
								
								client.c
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								client.c
									
									
									
									
									
								
							| @@ -96,7 +96,6 @@ server_started: | ||||
|  | ||||
| 	if (cmdflags & CMD_SENDENVIRON) | ||||
| 		client_send_environ(); | ||||
| 	if (isatty(STDIN_FILENO)) | ||||
| 	client_send_identify(flags); | ||||
|  | ||||
| 	return (&client_ibuf); | ||||
| @@ -131,6 +130,14 @@ client_send_identify(int flags) | ||||
| 		fatal("dup failed"); | ||||
| 	imsg_compose(&client_ibuf, | ||||
| 	    MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data); | ||||
|  | ||||
| 	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); | ||||
| } | ||||
|  | ||||
| void | ||||
|   | ||||
| @@ -16,10 +16,13 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "tmux.h" | ||||
|  | ||||
| @@ -45,7 +48,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| { | ||||
| 	struct cmd_buffer_data	*data = self->data; | ||||
| 	struct session		*s; | ||||
| 	FILE			*f; | ||||
| 	FILE			*f, *close_f; | ||||
| 	char		      	*pdata, *new_pdata; | ||||
| 	size_t			 psize; | ||||
| 	u_int			 limit; | ||||
| @@ -54,10 +57,24 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 	if ((s = cmd_find_session(ctx, data->target)) == NULL) | ||||
| 		return (-1); | ||||
|  | ||||
| 	if (strcmp(data->arg, "-") == 0 ) { | ||||
| 		if (ctx->cmdclient == NULL) { | ||||
| 			ctx->error(ctx, "%s: can't read from stdin", data->arg); | ||||
| 			return (-1); | ||||
| 		} | ||||
| 		f = ctx->cmdclient->stdin_file; | ||||
| 		if (isatty(fileno(ctx->cmdclient->stdin_file))) { | ||||
| 			ctx->error(ctx, "%s: stdin is a tty", data->arg); | ||||
| 			return (-1); | ||||
| 		} | ||||
| 		close_f = NULL; | ||||
| 	} else { | ||||
| 		if ((f = fopen(data->arg, "rb")) == NULL) { | ||||
| 			ctx->error(ctx, "%s: %s", data->arg, strerror(errno)); | ||||
| 			return (-1); | ||||
| 		} | ||||
| 		close_f = f; | ||||
| 	} | ||||
|  | ||||
| 	pdata = NULL; | ||||
| 	psize = 0; | ||||
| @@ -77,7 +94,8 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 	if (pdata != NULL) | ||||
| 		pdata[psize] = '\0'; | ||||
|  | ||||
| 	fclose(f); | ||||
| 	if (close_f != NULL) | ||||
| 		fclose(close_f); | ||||
|  | ||||
| 	limit = options_get_number(&s->options, "buffer-limit"); | ||||
| 	if (data->buffer == -1) { | ||||
| @@ -94,6 +112,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| error: | ||||
| 	if (pdata != NULL) | ||||
| 		xfree(pdata); | ||||
| 	fclose(f); | ||||
| 	if (close_f != NULL) | ||||
| 		fclose(close_f); | ||||
| 	return (-1); | ||||
| } | ||||
|   | ||||
| @@ -48,7 +48,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 	struct session		*s; | ||||
| 	struct paste_buffer	*pb; | ||||
| 	mode_t			mask; | ||||
| 	FILE			*f; | ||||
| 	FILE			*f, *close_f; | ||||
|  | ||||
| 	if ((s = cmd_find_session(ctx, data->target)) == NULL) | ||||
| 		return (-1); | ||||
| @@ -65,6 +65,14 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (strcmp(data->arg, "-") == 0) { | ||||
| 		if (ctx->cmdclient == NULL) { | ||||
| 			ctx->error(ctx, "%s: can't write to stdout", data->arg); | ||||
| 			return (-1); | ||||
| 		} | ||||
| 		f = ctx->cmdclient->stdout_file; | ||||
| 		close_f = NULL; | ||||
| 	} else { | ||||
| 		mask = umask(S_IRWXG | S_IRWXO); | ||||
| 		if (cmd_check_flag(data->chflags, 'a')) | ||||
| 			f = fopen(data->arg, "ab"); | ||||
| @@ -75,6 +83,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 			ctx->error(ctx, "%s: %s", data->arg, strerror(errno)); | ||||
| 			return (-1); | ||||
| 		} | ||||
| 		close_f = f; | ||||
| 	} | ||||
|  | ||||
| 	if (fwrite(pb->data, 1, pb->size, f) != pb->size) { | ||||
| 	    	ctx->error(ctx, "%s: fwrite error", data->arg); | ||||
| @@ -82,7 +92,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 	    	return (-1); | ||||
| 	} | ||||
|  | ||||
| 	fclose(f); | ||||
| 	if (close_f != NULL) | ||||
| 		fclose(close_f); | ||||
|  | ||||
| 	return (0); | ||||
| } | ||||
|   | ||||
| @@ -69,6 +69,10 @@ server_client_create(int fd) | ||||
|  | ||||
| 	ARRAY_INIT(&c->prompt_hdata); | ||||
|  | ||||
| 	c->stdin_file = NULL; | ||||
| 	c->stdout_file = NULL; | ||||
| 	c->stderr_file = NULL; | ||||
|  | ||||
| 	c->tty.fd = -1; | ||||
| 	c->title = NULL; | ||||
|  | ||||
| @@ -118,6 +122,13 @@ server_client_lost(struct client *c) | ||||
| 	if (c->flags & CLIENT_TERMINAL) | ||||
| 		tty_free(&c->tty); | ||||
|  | ||||
| 	if (c->stdin_file != NULL) | ||||
| 		fclose(c->stdin_file); | ||||
| 	if (c->stdout_file != NULL) | ||||
| 		fclose(c->stdout_file); | ||||
| 	if (c->stderr_file != NULL) | ||||
| 		fclose(c->stderr_file); | ||||
|  | ||||
| 	screen_free(&c->status); | ||||
| 	job_tree_free(&c->status_jobs); | ||||
|  | ||||
| @@ -555,8 +566,31 @@ server_client_msg_dispatch(struct client *c) | ||||
| 				fatalx("MSG_IDENTIFY missing fd"); | ||||
| 			memcpy(&identifydata, imsg.data, sizeof identifydata); | ||||
|  | ||||
| 			c->stdin_file = fdopen(imsg.fd, "r"); | ||||
| 			if (c->stdin_file == NULL) | ||||
| 				fatal("fdopen(stdin) failed"); | ||||
| 			server_client_msg_identify(c, &identifydata, imsg.fd); | ||||
| 			break; | ||||
| 		case MSG_STDOUT: | ||||
| 			if (datalen != 0) | ||||
| 				fatalx("bad MSG_STDOUT size"); | ||||
| 			if (imsg.fd == -1) | ||||
| 				fatalx("MSG_STDOUT missing fd"); | ||||
|  | ||||
| 			c->stdout_file = fdopen(imsg.fd, "w"); | ||||
| 			if (c->stdout_file == NULL) | ||||
| 				fatal("fdopen(stdout) failed"); | ||||
| 			break; | ||||
| 		case MSG_STDERR: | ||||
| 			if (datalen != 0) | ||||
| 				fatalx("bad MSG_STDERR size"); | ||||
| 			if (imsg.fd == -1) | ||||
| 				fatalx("MSG_STDERR missing fd"); | ||||
|  | ||||
| 			c->stderr_file = fdopen(imsg.fd, "w"); | ||||
| 			if (c->stderr_file == NULL) | ||||
| 				fatal("fdopen(stderr) failed"); | ||||
| 			break; | ||||
| 		case MSG_RESIZE: | ||||
| 			if (datalen != 0) | ||||
| 				fatalx("bad MSG_RESIZE size"); | ||||
| @@ -622,45 +656,45 @@ server_client_msg_dispatch(struct client *c) | ||||
| void printflike2 | ||||
| server_client_msg_error(struct cmd_ctx *ctx, const char *fmt, ...) | ||||
| { | ||||
| 	struct msg_print_data	data; | ||||
| 	va_list	ap; | ||||
|  | ||||
| 	va_start(ap, fmt); | ||||
| 	xvsnprintf(data.msg, sizeof data.msg, fmt, ap); | ||||
| 	vfprintf(ctx->cmdclient->stderr_file, fmt, ap); | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	server_write_client(ctx->cmdclient, MSG_ERROR, &data, sizeof data); | ||||
| 	fputc('\n', ctx->cmdclient->stderr_file); | ||||
| 	fflush(ctx->cmdclient->stderr_file); | ||||
| } | ||||
|  | ||||
| /* Callback to send print message to client. */ | ||||
| void printflike2 | ||||
| server_client_msg_print(struct cmd_ctx *ctx, const char *fmt, ...) | ||||
| { | ||||
| 	struct msg_print_data	data; | ||||
| 	va_list	ap; | ||||
|  | ||||
| 	va_start(ap, fmt); | ||||
| 	xvsnprintf(data.msg, sizeof data.msg, fmt, ap); | ||||
| 	vfprintf(ctx->cmdclient->stdout_file, fmt, ap); | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	server_write_client(ctx->cmdclient, MSG_PRINT, &data, sizeof data); | ||||
| 	fputc('\n', ctx->cmdclient->stdout_file); | ||||
| 	fflush(ctx->cmdclient->stdout_file); | ||||
| } | ||||
|  | ||||
| /* Callback to send print message to client, if not quiet. */ | ||||
| void printflike2 | ||||
| server_client_msg_info(struct cmd_ctx *ctx, const char *fmt, ...) | ||||
| { | ||||
| 	struct msg_print_data	data; | ||||
| 	va_list	ap; | ||||
|  | ||||
| 	if (options_get_number(&global_options, "quiet")) | ||||
| 		return; | ||||
|  | ||||
| 	va_start(ap, fmt); | ||||
| 	xvsnprintf(data.msg, sizeof data.msg, fmt, ap); | ||||
| 	vfprintf(ctx->cmdclient->stdout_file, fmt, ap); | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	server_write_client(ctx->cmdclient, MSG_PRINT, &data, sizeof data); | ||||
| 	fputc('\n', ctx->cmdclient->stderr_file); | ||||
| 	fflush(ctx->cmdclient->stdout_file); | ||||
| } | ||||
|  | ||||
| /* Handle command message. */ | ||||
| @@ -717,13 +751,19 @@ void | ||||
| server_client_msg_identify( | ||||
|     struct client *c, struct msg_identify_data *data, int fd) | ||||
| { | ||||
| 	int	tty_fd; | ||||
|  | ||||
| 	c->cwd = NULL; | ||||
| 	data->cwd[(sizeof data->cwd) - 1] = '\0'; | ||||
| 	if (*data->cwd != '\0') | ||||
| 		c->cwd = xstrdup(data->cwd); | ||||
|  | ||||
| 	if (!isatty(fd)) | ||||
| 	    return; | ||||
| 	if ((tty_fd = dup(fd)) == -1) | ||||
| 		fatal("dup failed"); | ||||
| 	data->term[(sizeof data->term) - 1] = '\0'; | ||||
| 	tty_init(&c->tty, fd, data->term); | ||||
| 	tty_init(&c->tty, tty_fd, data->term); | ||||
| 	if (data->flags & IDENTIFY_UTF8) | ||||
| 		c->tty.flags |= TTY_UTF8; | ||||
| 	if (data->flags & IDENTIFY_256COLOURS) | ||||
|   | ||||
							
								
								
									
										12
									
								
								tmux.c
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								tmux.c
									
									
									
									
									
								
							| @@ -596,7 +596,6 @@ main_dispatch(const char *shellcmd) | ||||
| { | ||||
| 	struct imsg		imsg; | ||||
| 	ssize_t			n, datalen; | ||||
| 	struct msg_print_data	printdata; | ||||
| 	struct msg_shell_data	shelldata; | ||||
|  | ||||
| 	if ((n = imsg_read(main_ibuf)) == -1 || n == 0) | ||||
| @@ -616,17 +615,6 @@ main_dispatch(const char *shellcmd) | ||||
| 				fatalx("bad MSG_EXIT size"); | ||||
|  | ||||
| 			exit(main_exitval); | ||||
| 		case MSG_ERROR: | ||||
| 		case MSG_PRINT: | ||||
| 			if (datalen != sizeof printdata) | ||||
| 				fatalx("bad MSG_PRINT size"); | ||||
| 			memcpy(&printdata, imsg.data, sizeof printdata); | ||||
| 			printdata.msg[(sizeof printdata.msg) - 1] = '\0'; | ||||
|  | ||||
| 			log_info("%s", printdata.msg); | ||||
| 			if (imsg.hdr.type == MSG_ERROR) | ||||
| 				main_exitval = 1; | ||||
| 			break; | ||||
| 		case MSG_READY: | ||||
| 			if (datalen != 0) | ||||
| 				fatalx("bad MSG_READY size"); | ||||
|   | ||||
							
								
								
									
										15
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								tmux.h
									
									
									
									
									
								
							| @@ -19,7 +19,7 @@ | ||||
| #ifndef TMUX_H | ||||
| #define TMUX_H | ||||
|  | ||||
| #define PROTOCOL_VERSION 5 | ||||
| #define PROTOCOL_VERSION 6 | ||||
|  | ||||
| #include <sys/param.h> | ||||
| #include <sys/time.h> | ||||
| @@ -68,7 +68,6 @@ extern char   **environ; | ||||
|  */ | ||||
| #define COMMAND_LENGTH 2048	/* packed argv size */ | ||||
| #define TERMINAL_LENGTH 128	/* length of TERM environment variable */ | ||||
| #define PRINT_LENGTH 512	/* printed error/message size */ | ||||
| #define ENVIRON_LENGTH 1024	/* environment variable length */ | ||||
|  | ||||
| /* | ||||
| @@ -373,7 +372,9 @@ enum msgtype { | ||||
| 	MSG_ENVIRON, | ||||
| 	MSG_UNLOCK, | ||||
| 	MSG_LOCK, | ||||
| 	MSG_SHELL | ||||
| 	MSG_SHELL, | ||||
| 	MSG_STDERR, | ||||
| 	MSG_STDOUT, | ||||
| }; | ||||
|  | ||||
| /* | ||||
| @@ -381,10 +382,6 @@ enum msgtype { | ||||
|  * | ||||
|  * Don't forget to bump PROTOCOL_VERSION if any of these change! | ||||
|  */ | ||||
| struct msg_print_data { | ||||
| 	char		msg[PRINT_LENGTH]; | ||||
| }; | ||||
|  | ||||
| struct msg_command_data { | ||||
| 	pid_t		pid;			/* pid from $TMUX or -1 */ | ||||
| 	u_int		idx;			/* index from $TMUX */ | ||||
| @@ -1080,6 +1077,10 @@ struct client { | ||||
| 	char		*cwd; | ||||
|  | ||||
| 	struct tty	 tty; | ||||
| 	FILE		*stdin_file; | ||||
| 	FILE		*stdout_file; | ||||
| 	FILE		*stderr_file; | ||||
|  | ||||
| 	struct event	 repeat_timer; | ||||
|  | ||||
| 	struct timeval	 status_timer; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Nicholas Marriott
					Nicholas Marriott