mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-03 17:24:18 +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:
		
							
								
								
									
										11
									
								
								client.c
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								client.c
									
									
									
									
									
								
							@@ -96,8 +96,7 @@ server_started:
 | 
			
		||||
 | 
			
		||||
	if (cmdflags & CMD_SENDENVIRON)
 | 
			
		||||
		client_send_environ();
 | 
			
		||||
	if (isatty(STDIN_FILENO))
 | 
			
		||||
		client_send_identify(flags);
 | 
			
		||||
	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,9 +57,23 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
 | 
			
		||||
	if ((s = cmd_find_session(ctx, data->target)) == NULL)
 | 
			
		||||
		return (-1);
 | 
			
		||||
 | 
			
		||||
	if ((f = fopen(data->arg, "rb")) == NULL) {
 | 
			
		||||
		ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
 | 
			
		||||
		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;
 | 
			
		||||
@@ -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,15 +65,25 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mask = umask(S_IRWXG | S_IRWXO);
 | 
			
		||||
	if (cmd_check_flag(data->chflags, 'a'))
 | 
			
		||||
		f = fopen(data->arg, "ab");
 | 
			
		||||
	else
 | 
			
		||||
		f = fopen(data->arg, "wb");
 | 
			
		||||
	umask(mask);
 | 
			
		||||
	if (f == NULL) {
 | 
			
		||||
		ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
 | 
			
		||||
		return (-1);
 | 
			
		||||
	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");
 | 
			
		||||
		else
 | 
			
		||||
			f = fopen(data->arg, "wb");
 | 
			
		||||
		umask(mask);
 | 
			
		||||
		if (f == NULL) {
 | 
			
		||||
			ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
 | 
			
		||||
			return (-1);
 | 
			
		||||
		}
 | 
			
		||||
		close_f = f;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (fwrite(pb->data, 1, pb->size, f) != pb->size) {
 | 
			
		||||
@@ -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_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_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;
 | 
			
		||||
	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