mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 00:56:10 +00:00 
			
		
		
		
	Rewrite the code for reading and writing files. Now, if the client is
not attached, the server process asks it to open the file, similar to how works for stdin, stdout, stderr. This makes special files like /dev/fd/X work (used by some shells). stdin, stdout and stderr and control mode are now just special cases of the same mechanism. This will also make it easier to use for other commands that read files such as source-file.
This commit is contained in:
		
							
								
								
									
										1
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
									
									
									
									
								
							@@ -73,6 +73,7 @@ SRCS=	alerts.c \
 | 
			
		||||
	control-notify.c \
 | 
			
		||||
	control.c \
 | 
			
		||||
	environ.c \
 | 
			
		||||
	file.c \
 | 
			
		||||
	format.c \
 | 
			
		||||
	format-draw.c \
 | 
			
		||||
	grid-view.c \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										332
									
								
								client.c
									
									
									
									
									
								
							
							
						
						
									
										332
									
								
								client.c
									
									
									
									
									
								
							@@ -35,7 +35,6 @@
 | 
			
		||||
static struct tmuxproc	*client_proc;
 | 
			
		||||
static struct tmuxpeer	*client_peer;
 | 
			
		||||
static int		 client_flags;
 | 
			
		||||
static struct event	 client_stdin;
 | 
			
		||||
static enum {
 | 
			
		||||
	CLIENT_EXIT_NONE,
 | 
			
		||||
	CLIENT_EXIT_DETACHED,
 | 
			
		||||
@@ -52,13 +51,12 @@ static const char	*client_exitsession;
 | 
			
		||||
static const char	*client_execshell;
 | 
			
		||||
static const char	*client_execcmd;
 | 
			
		||||
static int		 client_attached;
 | 
			
		||||
static struct client_files client_files = RB_INITIALIZER(&client_files);
 | 
			
		||||
 | 
			
		||||
static __dead void	 client_exec(const char *,const char *);
 | 
			
		||||
static int		 client_get_lock(char *);
 | 
			
		||||
static int		 client_connect(struct event_base *, const char *, int);
 | 
			
		||||
static void		 client_send_identify(const char *, const char *);
 | 
			
		||||
static void		 client_stdin_callback(int, short, void *);
 | 
			
		||||
static void		 client_write(int, const char *, size_t);
 | 
			
		||||
static void		 client_signal(int);
 | 
			
		||||
static void		 client_dispatch(struct imsg *, void *);
 | 
			
		||||
static void		 client_dispatch_attached(struct imsg *);
 | 
			
		||||
@@ -217,7 +215,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_parse_result	*pr;
 | 
			
		||||
	struct cmd		*cmd;
 | 
			
		||||
	struct msg_command_data	*data;
 | 
			
		||||
	struct msg_command	*data;
 | 
			
		||||
	int			 cmdflags, fd, i;
 | 
			
		||||
	const char		*ttynam, *cwd;
 | 
			
		||||
	pid_t			 ppid;
 | 
			
		||||
@@ -291,7 +289,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
 | 
			
		||||
	 *
 | 
			
		||||
	 * "sendfd" is dropped later in client_dispatch_wait().
 | 
			
		||||
	 */
 | 
			
		||||
	if (pledge("stdio unix sendfd proc exec tty", NULL) != 0)
 | 
			
		||||
	if (pledge(
 | 
			
		||||
	    "stdio rpath wpath cpath unix sendfd proc exec tty",
 | 
			
		||||
	    NULL) != 0)
 | 
			
		||||
		fatal("pledge failed");
 | 
			
		||||
 | 
			
		||||
	/* Free stuff that is not used in the client. */
 | 
			
		||||
@@ -302,10 +302,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
 | 
			
		||||
	options_free(global_w_options);
 | 
			
		||||
	environ_free(global_environ);
 | 
			
		||||
 | 
			
		||||
	/* Create stdin handler. */
 | 
			
		||||
	setblocking(STDIN_FILENO, 0);
 | 
			
		||||
	event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST,
 | 
			
		||||
	    client_stdin_callback, NULL);
 | 
			
		||||
	/* Set up control mode. */
 | 
			
		||||
	if (client_flags & CLIENT_CONTROLCONTROL) {
 | 
			
		||||
		if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) {
 | 
			
		||||
			fprintf(stderr, "tcgetattr failed: %s\n",
 | 
			
		||||
@@ -426,41 +423,236 @@ client_send_identify(const char *ttynam, const char *cwd)
 | 
			
		||||
	proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Callback for client stdin read events. */
 | 
			
		||||
/* File write error callback. */
 | 
			
		||||
static void
 | 
			
		||||
client_stdin_callback(__unused int fd, __unused short events,
 | 
			
		||||
    __unused void *arg)
 | 
			
		||||
client_write_error_callback(__unused struct bufferevent *bev,
 | 
			
		||||
    __unused short what, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	struct msg_stdin_data	data;
 | 
			
		||||
	struct client_file	*cf = arg;
 | 
			
		||||
 | 
			
		||||
	data.size = read(STDIN_FILENO, data.data, sizeof data.data);
 | 
			
		||||
	if (data.size == -1 && (errno == EINTR || errno == EAGAIN))
 | 
			
		||||
		return;
 | 
			
		||||
	log_debug("write error file %d", cf->stream);
 | 
			
		||||
 | 
			
		||||
	proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data);
 | 
			
		||||
	if (data.size <= 0)
 | 
			
		||||
		event_del(&client_stdin);
 | 
			
		||||
	bufferevent_free(cf->event);
 | 
			
		||||
	cf->event = NULL;
 | 
			
		||||
 | 
			
		||||
	close(cf->fd);
 | 
			
		||||
	cf->fd = -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Force write to file descriptor. */
 | 
			
		||||
/* File write callback. */
 | 
			
		||||
static void
 | 
			
		||||
client_write(int fd, const char *data, size_t size)
 | 
			
		||||
client_write_callback(__unused struct bufferevent *bev, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	ssize_t	used;
 | 
			
		||||
	struct client_file	*cf = arg;
 | 
			
		||||
 | 
			
		||||
	log_debug("%s: %.*s", __func__, (int)size, data);
 | 
			
		||||
	while (size != 0) {
 | 
			
		||||
		used = write(fd, data, size);
 | 
			
		||||
		if (used == -1) {
 | 
			
		||||
			if (errno == EINTR || errno == EAGAIN)
 | 
			
		||||
				continue;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		data += used;
 | 
			
		||||
		size -= used;
 | 
			
		||||
	if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
 | 
			
		||||
		bufferevent_free(cf->event);
 | 
			
		||||
		close(cf->fd);
 | 
			
		||||
		RB_REMOVE(client_files, &client_files, cf);
 | 
			
		||||
		file_free(cf);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Open write file. */
 | 
			
		||||
static void
 | 
			
		||||
client_write_open(void *data, size_t datalen)
 | 
			
		||||
{
 | 
			
		||||
	struct msg_write_open	*msg = data;
 | 
			
		||||
	struct msg_write_ready	 reply;
 | 
			
		||||
	struct client_file	 find, *cf;
 | 
			
		||||
	const int		 flags = O_NONBLOCK|O_WRONLY|O_CREAT;
 | 
			
		||||
	int			 error = 0;
 | 
			
		||||
 | 
			
		||||
	if (datalen != sizeof *msg)
 | 
			
		||||
		fatalx("bad MSG_WRITE_OPEN size");
 | 
			
		||||
	log_debug("open write file %d %s", msg->stream, msg->path);
 | 
			
		||||
 | 
			
		||||
	find.stream = msg->stream;
 | 
			
		||||
	if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
 | 
			
		||||
		cf = file_create(NULL, msg->stream, NULL, NULL);
 | 
			
		||||
		RB_INSERT(client_files, &client_files, cf);
 | 
			
		||||
	} else {
 | 
			
		||||
		error = EBADF;
 | 
			
		||||
		goto reply;
 | 
			
		||||
	}
 | 
			
		||||
	if (cf->closed) {
 | 
			
		||||
		error = EBADF;
 | 
			
		||||
		goto reply;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cf->fd = -1;
 | 
			
		||||
	if (msg->fd == -1)
 | 
			
		||||
		cf->fd = open(msg->path, msg->flags|flags, 0644);
 | 
			
		||||
	else {
 | 
			
		||||
		if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
 | 
			
		||||
			errno = EBADF;
 | 
			
		||||
		else {
 | 
			
		||||
			cf->fd = dup(msg->fd);
 | 
			
		||||
			if (client_flags & CLIENT_CONTROL)
 | 
			
		||||
				close(msg->fd); /* can only be used once */
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (cf->fd == -1) {
 | 
			
		||||
		error = errno;
 | 
			
		||||
		goto reply;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cf->event = bufferevent_new(cf->fd, NULL, client_write_callback,
 | 
			
		||||
	    client_write_error_callback, cf);
 | 
			
		||||
	bufferevent_enable(cf->event, EV_WRITE);
 | 
			
		||||
	goto reply;
 | 
			
		||||
 | 
			
		||||
reply:
 | 
			
		||||
	reply.stream = msg->stream;
 | 
			
		||||
	reply.error = error;
 | 
			
		||||
	proc_send(client_peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write to client file. */
 | 
			
		||||
static void
 | 
			
		||||
client_write_data(void *data, size_t datalen)
 | 
			
		||||
{
 | 
			
		||||
	struct msg_write_data	*msg = data;
 | 
			
		||||
	struct client_file	 find, *cf;
 | 
			
		||||
 | 
			
		||||
	if (datalen != sizeof *msg)
 | 
			
		||||
		fatalx("bad MSG_WRITE size");
 | 
			
		||||
	find.stream = msg->stream;
 | 
			
		||||
	if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
 | 
			
		||||
		fatalx("unknown stream number");
 | 
			
		||||
	log_debug("write %zu to file %d", msg->size, cf->stream);
 | 
			
		||||
 | 
			
		||||
	if (cf->event != NULL)
 | 
			
		||||
		bufferevent_write(cf->event, msg->data, msg->size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Close client file. */
 | 
			
		||||
static void
 | 
			
		||||
client_write_close(void *data, size_t datalen)
 | 
			
		||||
{
 | 
			
		||||
	struct msg_write_close	*msg = data;
 | 
			
		||||
	struct client_file	 find, *cf;
 | 
			
		||||
 | 
			
		||||
	if (datalen != sizeof *msg)
 | 
			
		||||
		fatalx("bad MSG_WRITE_CLOSE size");
 | 
			
		||||
	find.stream = msg->stream;
 | 
			
		||||
	if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
 | 
			
		||||
		fatalx("unknown stream number");
 | 
			
		||||
	log_debug("close file %d", cf->stream);
 | 
			
		||||
 | 
			
		||||
	if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
 | 
			
		||||
		if (cf->event != NULL)
 | 
			
		||||
			bufferevent_free(cf->event);
 | 
			
		||||
		if (cf->fd != -1)
 | 
			
		||||
			close(cf->fd);
 | 
			
		||||
		RB_REMOVE(client_files, &client_files, cf);
 | 
			
		||||
		file_free(cf);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* File read callback. */
 | 
			
		||||
static void
 | 
			
		||||
client_read_callback(__unused struct bufferevent *bev, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	*cf = arg;
 | 
			
		||||
	void			*bdata;
 | 
			
		||||
	size_t			 bsize;
 | 
			
		||||
	struct msg_read_data	 msg;
 | 
			
		||||
 | 
			
		||||
	for (;;) {
 | 
			
		||||
		bdata = EVBUFFER_DATA(cf->event->input);
 | 
			
		||||
		bsize = EVBUFFER_LENGTH(cf->event->input);
 | 
			
		||||
 | 
			
		||||
		if (bsize == 0)
 | 
			
		||||
			break;
 | 
			
		||||
		if (bsize > sizeof msg.data)
 | 
			
		||||
			bsize = sizeof msg.data;
 | 
			
		||||
		log_debug("read %zu from file %d", bsize, cf->stream);
 | 
			
		||||
 | 
			
		||||
		memcpy(msg.data, bdata, bsize);
 | 
			
		||||
		msg.size = bsize;
 | 
			
		||||
 | 
			
		||||
		msg.stream = cf->stream;
 | 
			
		||||
		proc_send(client_peer, MSG_READ, -1, &msg, sizeof msg);
 | 
			
		||||
 | 
			
		||||
		evbuffer_drain(cf->event->input, bsize);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* File read error callback. */
 | 
			
		||||
static void
 | 
			
		||||
client_read_error_callback(__unused struct bufferevent *bev,
 | 
			
		||||
    __unused short what, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	*cf = arg;
 | 
			
		||||
	struct msg_read_done	 msg;
 | 
			
		||||
 | 
			
		||||
	log_debug("read error file %d", cf->stream);
 | 
			
		||||
 | 
			
		||||
	msg.stream = cf->stream;
 | 
			
		||||
	msg.error = 0;
 | 
			
		||||
	proc_send(client_peer, MSG_READ_DONE, -1, &msg, sizeof msg);
 | 
			
		||||
 | 
			
		||||
	bufferevent_free(cf->event);
 | 
			
		||||
	close(cf->fd);
 | 
			
		||||
	RB_REMOVE(client_files, &client_files, cf);
 | 
			
		||||
	file_free(cf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Open read file. */
 | 
			
		||||
static void
 | 
			
		||||
client_read_open(void *data, size_t datalen)
 | 
			
		||||
{
 | 
			
		||||
	struct msg_read_open	*msg = data;
 | 
			
		||||
	struct msg_read_done	 reply;
 | 
			
		||||
	struct client_file	 find, *cf;
 | 
			
		||||
	const int		 flags = O_NONBLOCK|O_RDONLY;
 | 
			
		||||
	int			 error = 0;
 | 
			
		||||
 | 
			
		||||
	if (datalen != sizeof *msg)
 | 
			
		||||
		fatalx("bad MSG_READ_OPEN size");
 | 
			
		||||
	log_debug("open read file %d %s", msg->stream, msg->path);
 | 
			
		||||
 | 
			
		||||
	find.stream = msg->stream;
 | 
			
		||||
	if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
 | 
			
		||||
		cf = file_create(NULL, msg->stream, NULL, NULL);
 | 
			
		||||
		RB_INSERT(client_files, &client_files, cf);
 | 
			
		||||
	} else {
 | 
			
		||||
		error = EBADF;
 | 
			
		||||
		goto reply;
 | 
			
		||||
	}
 | 
			
		||||
	if (cf->closed) {
 | 
			
		||||
		error = EBADF;
 | 
			
		||||
		goto reply;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cf->fd = -1;
 | 
			
		||||
	if (msg->fd == -1)
 | 
			
		||||
		cf->fd = open(msg->path, flags);
 | 
			
		||||
	else {
 | 
			
		||||
		if (msg->fd != STDIN_FILENO)
 | 
			
		||||
			errno = EBADF;
 | 
			
		||||
		else {
 | 
			
		||||
			cf->fd = dup(msg->fd);
 | 
			
		||||
			close(msg->fd); /* can only be used once */
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (cf->fd == -1) {
 | 
			
		||||
		error = errno;
 | 
			
		||||
		goto reply;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cf->event = bufferevent_new(cf->fd, client_read_callback, NULL,
 | 
			
		||||
	    client_read_error_callback, cf);
 | 
			
		||||
	bufferevent_enable(cf->event, EV_READ);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
reply:
 | 
			
		||||
	reply.stream = msg->stream;
 | 
			
		||||
	reply.error = error;
 | 
			
		||||
	proc_send(client_peer, MSG_READ_DONE, -1, &reply, sizeof reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Run command in shell; used for -c. */
 | 
			
		||||
static __dead void
 | 
			
		||||
client_exec(const char *shell, const char *shellcmd)
 | 
			
		||||
@@ -532,6 +724,29 @@ client_signal(int sig)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Exit if all streams flushed. */
 | 
			
		||||
static void
 | 
			
		||||
client_exit(__unused int fd, __unused short events, __unused void *arg)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	*cf;
 | 
			
		||||
	size_t 			 left;
 | 
			
		||||
	int			 waiting = 0;
 | 
			
		||||
 | 
			
		||||
	RB_FOREACH (cf, client_files, &client_files) {
 | 
			
		||||
		if (cf->event == NULL)
 | 
			
		||||
			continue;
 | 
			
		||||
		left = EVBUFFER_LENGTH(cf->event->output);
 | 
			
		||||
		if (left != 0) {
 | 
			
		||||
			waiting++;
 | 
			
		||||
			log_debug("file %u %zu bytes left", cf->stream, left);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (waiting == 0)
 | 
			
		||||
		proc_exit(client_proc);
 | 
			
		||||
	else
 | 
			
		||||
		event_once(-1, EV_TIMEOUT, client_exit, NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Callback for client read events. */
 | 
			
		||||
static void
 | 
			
		||||
client_dispatch(struct imsg *imsg, __unused void *arg)
 | 
			
		||||
@@ -553,12 +768,10 @@ client_dispatch(struct imsg *imsg, __unused void *arg)
 | 
			
		||||
static void
 | 
			
		||||
client_dispatch_wait(struct imsg *imsg)
 | 
			
		||||
{
 | 
			
		||||
	char			*data;
 | 
			
		||||
	ssize_t			 datalen;
 | 
			
		||||
	struct msg_stdout_data	 stdoutdata;
 | 
			
		||||
	struct msg_stderr_data	 stderrdata;
 | 
			
		||||
	int			 retval;
 | 
			
		||||
	static int		 pledge_applied;
 | 
			
		||||
	char		*data;
 | 
			
		||||
	ssize_t		 datalen;
 | 
			
		||||
	int		 retval;
 | 
			
		||||
	static int	 pledge_applied;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * "sendfd" is no longer required once all of the identify messages
 | 
			
		||||
@@ -567,10 +780,12 @@ client_dispatch_wait(struct imsg *imsg)
 | 
			
		||||
	 * get the first message from the server.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!pledge_applied) {
 | 
			
		||||
		if (pledge("stdio unix proc exec tty", NULL) != 0)
 | 
			
		||||
		if (pledge(
 | 
			
		||||
		    "stdio rpath wpath cpath unix proc exec tty",
 | 
			
		||||
		    NULL) != 0)
 | 
			
		||||
			fatal("pledge failed");
 | 
			
		||||
		pledge_applied = 1;
 | 
			
		||||
	};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data = imsg->data;
 | 
			
		||||
	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
 | 
			
		||||
@@ -584,38 +799,15 @@ client_dispatch_wait(struct imsg *imsg)
 | 
			
		||||
			memcpy(&retval, data, sizeof retval);
 | 
			
		||||
			client_exitval = retval;
 | 
			
		||||
		}
 | 
			
		||||
		proc_exit(client_proc);
 | 
			
		||||
		event_once(-1, EV_TIMEOUT, client_exit, NULL, NULL);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_READY:
 | 
			
		||||
		if (datalen != 0)
 | 
			
		||||
			fatalx("bad MSG_READY size");
 | 
			
		||||
 | 
			
		||||
		event_del(&client_stdin);
 | 
			
		||||
		client_attached = 1;
 | 
			
		||||
		proc_send(client_peer, MSG_RESIZE, -1, NULL, 0);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_STDIN:
 | 
			
		||||
		if (datalen != 0)
 | 
			
		||||
			fatalx("bad MSG_STDIN size");
 | 
			
		||||
 | 
			
		||||
		event_add(&client_stdin, NULL);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_STDOUT:
 | 
			
		||||
		if (datalen != sizeof stdoutdata)
 | 
			
		||||
			fatalx("bad MSG_STDOUT size");
 | 
			
		||||
		memcpy(&stdoutdata, data, sizeof stdoutdata);
 | 
			
		||||
 | 
			
		||||
		client_write(STDOUT_FILENO, stdoutdata.data,
 | 
			
		||||
		    stdoutdata.size);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_STDERR:
 | 
			
		||||
		if (datalen != sizeof stderrdata)
 | 
			
		||||
			fatalx("bad MSG_STDERR size");
 | 
			
		||||
		memcpy(&stderrdata, data, sizeof stderrdata);
 | 
			
		||||
 | 
			
		||||
		client_write(STDERR_FILENO, stderrdata.data,
 | 
			
		||||
		    stderrdata.size);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_VERSION:
 | 
			
		||||
		if (datalen != 0)
 | 
			
		||||
			fatalx("bad MSG_VERSION size");
 | 
			
		||||
@@ -639,6 +831,18 @@ client_dispatch_wait(struct imsg *imsg)
 | 
			
		||||
	case MSG_EXITED:
 | 
			
		||||
		proc_exit(client_proc);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_READ_OPEN:
 | 
			
		||||
		client_read_open(data, datalen);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_WRITE_OPEN:
 | 
			
		||||
		client_write_open(data, datalen);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_WRITE:
 | 
			
		||||
		client_write_data(data, datalen);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_WRITE_CLOSE:
 | 
			
		||||
		client_write_close(data, datalen);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -193,7 +193,7 @@ static enum cmd_retval
 | 
			
		||||
cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
{
 | 
			
		||||
	struct args		*args = self->args;
 | 
			
		||||
	struct client		*c;
 | 
			
		||||
	struct client		*c = item->client;
 | 
			
		||||
	struct window_pane	*wp = item->target.wp;
 | 
			
		||||
	char			*buf, *cause;
 | 
			
		||||
	const char		*bufname;
 | 
			
		||||
@@ -214,18 +214,15 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
		return (CMD_RETURN_ERROR);
 | 
			
		||||
 | 
			
		||||
	if (args_has(args, 'p')) {
 | 
			
		||||
		c = item->client;
 | 
			
		||||
		if (c == NULL ||
 | 
			
		||||
		    (c->session != NULL && !(c->flags & CLIENT_CONTROL))) {
 | 
			
		||||
			cmdq_error(item, "can't write to stdout");
 | 
			
		||||
		if (!file_can_print(c)) {
 | 
			
		||||
			cmdq_error(item, "can't write output to client");
 | 
			
		||||
			free(buf);
 | 
			
		||||
			return (CMD_RETURN_ERROR);
 | 
			
		||||
		}
 | 
			
		||||
		evbuffer_add(c->stdout_data, buf, len);
 | 
			
		||||
		free(buf);
 | 
			
		||||
		file_print_buffer(c, buf, len);
 | 
			
		||||
		if (args_has(args, 'P') && len > 0)
 | 
			
		||||
		    evbuffer_add(c->stdout_data, "\n", 1);
 | 
			
		||||
		server_client_push_stdout(c);
 | 
			
		||||
			file_print(c, "\n");
 | 
			
		||||
		free(buf);
 | 
			
		||||
	} else {
 | 
			
		||||
		bufname = NULL;
 | 
			
		||||
		if (args_has(args, 'b'))
 | 
			
		||||
 
 | 
			
		||||
@@ -33,8 +33,6 @@
 | 
			
		||||
 | 
			
		||||
static enum cmd_retval	cmd_load_buffer_exec(struct cmd *, struct cmdq_item *);
 | 
			
		||||
 | 
			
		||||
static void		cmd_load_buffer_callback(struct client *, int, void *);
 | 
			
		||||
 | 
			
		||||
const struct cmd_entry cmd_load_buffer_entry = {
 | 
			
		||||
	.name = "load-buffer",
 | 
			
		||||
	.alias = "loadb",
 | 
			
		||||
@@ -48,9 +46,40 @@ const struct cmd_entry cmd_load_buffer_entry = {
 | 
			
		||||
 | 
			
		||||
struct cmd_load_buffer_data {
 | 
			
		||||
	struct cmdq_item	*item;
 | 
			
		||||
	char			*bufname;
 | 
			
		||||
	char			*name;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
 | 
			
		||||
    int closed, struct evbuffer *buffer, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_load_buffer_data	*cdata = data;
 | 
			
		||||
	struct cmdq_item		*item = cdata->item;
 | 
			
		||||
	void				*bdata = EVBUFFER_DATA(buffer);
 | 
			
		||||
	size_t				 bsize = EVBUFFER_LENGTH(buffer);
 | 
			
		||||
	void				*copy;
 | 
			
		||||
	char				*cause;
 | 
			
		||||
 | 
			
		||||
	if (!closed)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (error != 0)
 | 
			
		||||
		cmdq_error(item, "%s: %s", path, strerror(error));
 | 
			
		||||
	else if (bsize != 0) {
 | 
			
		||||
		copy = xmalloc(bsize);
 | 
			
		||||
		memcpy(copy, bdata, bsize);
 | 
			
		||||
		if (paste_set(copy, bsize, cdata->name, &cause) != 0) {
 | 
			
		||||
			cmdq_error(item, "%s", cause);
 | 
			
		||||
			free(cause);
 | 
			
		||||
			free(copy);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	cmdq_continue(item);
 | 
			
		||||
 | 
			
		||||
	free(cdata->name);
 | 
			
		||||
	free(cdata);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static enum cmd_retval
 | 
			
		||||
cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
{
 | 
			
		||||
@@ -60,124 +89,19 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
	struct session			*s = item->target.s;
 | 
			
		||||
	struct winlink			*wl = item->target.wl;
 | 
			
		||||
	struct window_pane		*wp = item->target.wp;
 | 
			
		||||
	FILE				*f;
 | 
			
		||||
	const char			*bufname;
 | 
			
		||||
	char				*pdata = NULL, *new_pdata, *cause;
 | 
			
		||||
	char				*path, *file;
 | 
			
		||||
	size_t				 psize;
 | 
			
		||||
	int				 ch, error;
 | 
			
		||||
	const char			*bufname = args_get(args, 'b');
 | 
			
		||||
	char				*path;
 | 
			
		||||
 | 
			
		||||
	bufname = NULL;
 | 
			
		||||
	if (args_has(args, 'b'))
 | 
			
		||||
		bufname = args_get(args, 'b');
 | 
			
		||||
	cdata = xmalloc(sizeof *cdata);
 | 
			
		||||
	cdata->item = item;
 | 
			
		||||
	if (bufname != NULL)
 | 
			
		||||
		cdata->name = xstrdup(bufname);
 | 
			
		||||
	else
 | 
			
		||||
		cdata->name = NULL;
 | 
			
		||||
 | 
			
		||||
	path = format_single(item, args->argv[0], c, s, wl, wp);
 | 
			
		||||
	if (strcmp(path, "-") == 0) {
 | 
			
		||||
		free(path);
 | 
			
		||||
		c = item->client;
 | 
			
		||||
 | 
			
		||||
		cdata = xcalloc(1, sizeof *cdata);
 | 
			
		||||
		cdata->item = item;
 | 
			
		||||
 | 
			
		||||
		if (bufname != NULL)
 | 
			
		||||
			cdata->bufname = xstrdup(bufname);
 | 
			
		||||
 | 
			
		||||
		error = server_set_stdin_callback(c, cmd_load_buffer_callback,
 | 
			
		||||
		    cdata, &cause);
 | 
			
		||||
		if (error != 0) {
 | 
			
		||||
			cmdq_error(item, "-: %s", cause);
 | 
			
		||||
			free(cause);
 | 
			
		||||
			free(cdata);
 | 
			
		||||
			return (CMD_RETURN_ERROR);
 | 
			
		||||
		}
 | 
			
		||||
		return (CMD_RETURN_WAIT);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	file = server_client_get_path(item->client, path);
 | 
			
		||||
	file_read(item->client, path, cmd_load_buffer_done, cdata);
 | 
			
		||||
	free(path);
 | 
			
		||||
 | 
			
		||||
	f = fopen(file, "rb");
 | 
			
		||||
	if (f == NULL) {
 | 
			
		||||
		cmdq_error(item, "%s: %s", file, strerror(errno));
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pdata = NULL;
 | 
			
		||||
	psize = 0;
 | 
			
		||||
	while ((ch = getc(f)) != EOF) {
 | 
			
		||||
		/* Do not let the server die due to memory exhaustion. */
 | 
			
		||||
		if ((new_pdata = realloc(pdata, psize + 2)) == NULL) {
 | 
			
		||||
			cmdq_error(item, "realloc error: %s", strerror(errno));
 | 
			
		||||
			goto error;
 | 
			
		||||
		}
 | 
			
		||||
		pdata = new_pdata;
 | 
			
		||||
		pdata[psize++] = ch;
 | 
			
		||||
	}
 | 
			
		||||
	if (ferror(f)) {
 | 
			
		||||
		cmdq_error(item, "%s: read error", file);
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
	if (pdata != NULL)
 | 
			
		||||
		pdata[psize] = '\0';
 | 
			
		||||
 | 
			
		||||
	fclose(f);
 | 
			
		||||
	free(file);
 | 
			
		||||
 | 
			
		||||
	if (paste_set(pdata, psize, bufname, &cause) != 0) {
 | 
			
		||||
		cmdq_error(item, "%s", cause);
 | 
			
		||||
		free(pdata);
 | 
			
		||||
		free(cause);
 | 
			
		||||
		return (CMD_RETURN_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (CMD_RETURN_NORMAL);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	free(pdata);
 | 
			
		||||
	if (f != NULL)
 | 
			
		||||
		fclose(f);
 | 
			
		||||
	free(file);
 | 
			
		||||
	return (CMD_RETURN_ERROR);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
cmd_load_buffer_callback(struct client *c, int closed, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_load_buffer_data	*cdata = data;
 | 
			
		||||
	char				*pdata, *cause, *saved;
 | 
			
		||||
	size_t				 psize;
 | 
			
		||||
 | 
			
		||||
	if (!closed)
 | 
			
		||||
		return;
 | 
			
		||||
	c->stdin_callback = NULL;
 | 
			
		||||
 | 
			
		||||
	server_client_unref(c);
 | 
			
		||||
	if (c->flags & CLIENT_DEAD)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	psize = EVBUFFER_LENGTH(c->stdin_data);
 | 
			
		||||
	if (psize == 0 || (pdata = malloc(psize + 1)) == NULL)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize);
 | 
			
		||||
	pdata[psize] = '\0';
 | 
			
		||||
	evbuffer_drain(c->stdin_data, psize);
 | 
			
		||||
 | 
			
		||||
	if (paste_set(pdata, psize, cdata->bufname, &cause) != 0) {
 | 
			
		||||
		/* No context so can't use server_client_msg_error. */
 | 
			
		||||
		if (~c->flags & CLIENT_UTF8) {
 | 
			
		||||
			saved = cause;
 | 
			
		||||
			cause = utf8_sanitize(saved);
 | 
			
		||||
			free(saved);
 | 
			
		||||
		}
 | 
			
		||||
		evbuffer_add_printf(c->stderr_data, "%s", cause);
 | 
			
		||||
		server_client_push_stderr(c);
 | 
			
		||||
		free(pdata);
 | 
			
		||||
		free(cause);
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	cmdq_continue(cdata->item);
 | 
			
		||||
 | 
			
		||||
	free(cdata->bufname);
 | 
			
		||||
	free(cdata);
 | 
			
		||||
	return (CMD_RETURN_WAIT);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								cmd-queue.c
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								cmd-queue.c
									
									
									
									
									
								
							@@ -475,13 +475,11 @@ void
 | 
			
		||||
cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
 | 
			
		||||
{
 | 
			
		||||
	struct client	*c = item->client;
 | 
			
		||||
	long		 t = item->time;
 | 
			
		||||
	u_int		 number = item->number;
 | 
			
		||||
 | 
			
		||||
	if (c == NULL || !(c->flags & CLIENT_CONTROL))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
 | 
			
		||||
	    (long)item->time, item->number, flags);
 | 
			
		||||
	server_client_push_stdout(c);
 | 
			
		||||
	if (c != NULL && (c->flags & CLIENT_CONTROL))
 | 
			
		||||
		file_print(c, "%%%s %ld %u %d\n", guard, t, number, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Show message from command. */
 | 
			
		||||
@@ -495,20 +493,20 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
 | 
			
		||||
	char				*tmp, *msg;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	xvasprintf(&msg, fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
 | 
			
		||||
	log_debug("%s: %s", __func__, msg);
 | 
			
		||||
 | 
			
		||||
	if (c == NULL)
 | 
			
		||||
		/* nothing */;
 | 
			
		||||
	else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
 | 
			
		||||
		if (~c->flags & CLIENT_UTF8) {
 | 
			
		||||
			xvasprintf(&tmp, fmt, ap);
 | 
			
		||||
			tmp = msg;
 | 
			
		||||
			msg = utf8_sanitize(tmp);
 | 
			
		||||
			free(tmp);
 | 
			
		||||
			evbuffer_add(c->stdout_data, msg, strlen(msg));
 | 
			
		||||
			free(msg);
 | 
			
		||||
		} else
 | 
			
		||||
			evbuffer_add_vprintf(c->stdout_data, fmt, ap);
 | 
			
		||||
		evbuffer_add(c->stdout_data, "\n", 1);
 | 
			
		||||
		server_client_push_stdout(c);
 | 
			
		||||
		}
 | 
			
		||||
		file_print(c, "%s\n", msg);
 | 
			
		||||
	} else {
 | 
			
		||||
		wp = c->session->curw->window->active;
 | 
			
		||||
		wme = TAILQ_FIRST(&wp->modes);
 | 
			
		||||
@@ -517,7 +515,7 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
 | 
			
		||||
		window_copy_vadd(wp, fmt, ap);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
	free(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Show error from command. */
 | 
			
		||||
@@ -528,11 +526,10 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
 | 
			
		||||
	struct cmd	*cmd = item->cmd;
 | 
			
		||||
	va_list		 ap;
 | 
			
		||||
	char		*msg;
 | 
			
		||||
	size_t		 msglen;
 | 
			
		||||
	char		*tmp;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	msglen = xvasprintf(&msg, fmt, ap);
 | 
			
		||||
	xvasprintf(&msg, fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
 | 
			
		||||
	log_debug("%s: %s", __func__, msg);
 | 
			
		||||
@@ -544,11 +541,8 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
 | 
			
		||||
			tmp = msg;
 | 
			
		||||
			msg = utf8_sanitize(tmp);
 | 
			
		||||
			free(tmp);
 | 
			
		||||
			msglen = strlen(msg);
 | 
			
		||||
		}
 | 
			
		||||
		evbuffer_add(c->stderr_data, msg, msglen);
 | 
			
		||||
		evbuffer_add(c->stderr_data, "\n", 1);
 | 
			
		||||
		server_client_push_stderr(c);
 | 
			
		||||
		file_error(c, "%s\n", msg);
 | 
			
		||||
		c->retval = 1;
 | 
			
		||||
	} else {
 | 
			
		||||
		*msg = toupper((u_char) *msg);
 | 
			
		||||
 
 | 
			
		||||
@@ -56,6 +56,20 @@ const struct cmd_entry cmd_show_buffer_entry = {
 | 
			
		||||
	.exec = cmd_save_buffer_exec
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
cmd_save_buffer_done(__unused struct client *c, const char *path, int error,
 | 
			
		||||
    __unused int closed, __unused struct evbuffer *buffer, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct cmdq_item	*item = data;
 | 
			
		||||
 | 
			
		||||
	if (!closed)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (error != 0)
 | 
			
		||||
		cmdq_error(item, "%s: %s", path, strerror(error));
 | 
			
		||||
	cmdq_continue(item);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static enum cmd_retval
 | 
			
		||||
cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
{
 | 
			
		||||
@@ -65,18 +79,17 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
	struct winlink		*wl = item->target.wl;
 | 
			
		||||
	struct window_pane	*wp = item->target.wp;
 | 
			
		||||
	struct paste_buffer	*pb;
 | 
			
		||||
	const char		*bufname, *bufdata, *start, *end, *flags;
 | 
			
		||||
	char			*msg, *path, *file;
 | 
			
		||||
	size_t			 size, used, msglen, bufsize;
 | 
			
		||||
	FILE			*f;
 | 
			
		||||
	int			 flags;
 | 
			
		||||
	const char		*bufname = args_get(args, 'b'), *bufdata;
 | 
			
		||||
	size_t			 bufsize;
 | 
			
		||||
	char			*path;
 | 
			
		||||
 | 
			
		||||
	if (!args_has(args, 'b')) {
 | 
			
		||||
	if (bufname == NULL) {
 | 
			
		||||
		if ((pb = paste_get_top(NULL)) == NULL) {
 | 
			
		||||
			cmdq_error(item, "no buffers");
 | 
			
		||||
			return (CMD_RETURN_ERROR);
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		bufname = args_get(args, 'b');
 | 
			
		||||
		pb = paste_get_name(bufname);
 | 
			
		||||
		if (pb == NULL) {
 | 
			
		||||
			cmdq_error(item, "no buffer %s", bufname);
 | 
			
		||||
@@ -89,74 +102,13 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
		path = xstrdup("-");
 | 
			
		||||
	else
 | 
			
		||||
		path = format_single(item, args->argv[0], c, s, wl, wp);
 | 
			
		||||
	if (strcmp(path, "-") == 0) {
 | 
			
		||||
		free(path);
 | 
			
		||||
		c = item->client;
 | 
			
		||||
		if (c == NULL) {
 | 
			
		||||
			cmdq_error(item, "can't write to stdout");
 | 
			
		||||
			return (CMD_RETURN_ERROR);
 | 
			
		||||
		}
 | 
			
		||||
		if (c->session == NULL || (c->flags & CLIENT_CONTROL))
 | 
			
		||||
			goto do_stdout;
 | 
			
		||||
		goto do_print;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	flags = "wb";
 | 
			
		||||
	if (args_has(self->args, 'a'))
 | 
			
		||||
		flags = "ab";
 | 
			
		||||
 | 
			
		||||
	file = server_client_get_path(item->client, path);
 | 
			
		||||
		flags = O_APPEND;
 | 
			
		||||
	else
 | 
			
		||||
		flags = 0;
 | 
			
		||||
	file_write(item->client, path, flags, bufdata, bufsize,
 | 
			
		||||
	    cmd_save_buffer_done, item);
 | 
			
		||||
	free(path);
 | 
			
		||||
 | 
			
		||||
	f = fopen(file, flags);
 | 
			
		||||
	if (f == NULL) {
 | 
			
		||||
		cmdq_error(item, "%s: %s", file, strerror(errno));
 | 
			
		||||
		free(file);
 | 
			
		||||
		return (CMD_RETURN_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (fwrite(bufdata, 1, bufsize, f) != bufsize) {
 | 
			
		||||
		cmdq_error(item, "%s: write error", file);
 | 
			
		||||
		fclose(f);
 | 
			
		||||
		free(file);
 | 
			
		||||
		return (CMD_RETURN_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fclose(f);
 | 
			
		||||
	free(file);
 | 
			
		||||
 | 
			
		||||
	return (CMD_RETURN_NORMAL);
 | 
			
		||||
 | 
			
		||||
do_stdout:
 | 
			
		||||
	evbuffer_add(c->stdout_data, bufdata, bufsize);
 | 
			
		||||
	server_client_push_stdout(c);
 | 
			
		||||
	return (CMD_RETURN_NORMAL);
 | 
			
		||||
 | 
			
		||||
do_print:
 | 
			
		||||
	if (bufsize > (INT_MAX / 4) - 1) {
 | 
			
		||||
		cmdq_error(item, "buffer too big");
 | 
			
		||||
		return (CMD_RETURN_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
	msg = NULL;
 | 
			
		||||
 | 
			
		||||
	used = 0;
 | 
			
		||||
	while (used != bufsize) {
 | 
			
		||||
		start = bufdata + used;
 | 
			
		||||
		end = memchr(start, '\n', bufsize - used);
 | 
			
		||||
		if (end != NULL)
 | 
			
		||||
			size = end - start;
 | 
			
		||||
		else
 | 
			
		||||
			size = bufsize - used;
 | 
			
		||||
 | 
			
		||||
		msglen = size * 4 + 1;
 | 
			
		||||
		msg = xrealloc(msg, msglen);
 | 
			
		||||
 | 
			
		||||
		strvisx(msg, start, size, VIS_OCTAL|VIS_TAB);
 | 
			
		||||
		cmdq_print(item, "%s", msg);
 | 
			
		||||
 | 
			
		||||
		used += size + (end != NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	free(msg);
 | 
			
		||||
	return (CMD_RETURN_NORMAL);
 | 
			
		||||
	return (CMD_RETURN_WAIT);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@ control_notify_input(struct client *c, struct window_pane *wp,
 | 
			
		||||
			else
 | 
			
		||||
			    evbuffer_add_printf(message, "%c", buf[i]);
 | 
			
		||||
		}
 | 
			
		||||
		control_write_buffer(c, message);
 | 
			
		||||
		control_write(c, "%s", EVBUFFER_DATA(message));
 | 
			
		||||
		evbuffer_free(message);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								control.c
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								control.c
									
									
									
									
									
								
							@@ -30,23 +30,12 @@
 | 
			
		||||
void
 | 
			
		||||
control_write(struct client *c, const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list		 ap;
 | 
			
		||||
	va_list	ap;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	evbuffer_add_vprintf(c->stdout_data, fmt, ap);
 | 
			
		||||
	file_vprint(c, fmt, ap);
 | 
			
		||||
	file_print(c, "\n");
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
 | 
			
		||||
	evbuffer_add(c->stdout_data, "\n", 1);
 | 
			
		||||
	server_client_push_stdout(c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write a buffer, adding a terminal newline. Empties buffer. */
 | 
			
		||||
void
 | 
			
		||||
control_write_buffer(struct client *c, struct evbuffer *buffer)
 | 
			
		||||
{
 | 
			
		||||
	evbuffer_add_buffer(c->stdout_data, buffer);
 | 
			
		||||
	evbuffer_add(c->stdout_data, "\n", 1);
 | 
			
		||||
	server_client_push_stdout(c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Control error callback. */
 | 
			
		||||
@@ -65,20 +54,22 @@ control_error(struct cmdq_item *item, void *data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Control input callback. Read lines and fire commands. */
 | 
			
		||||
void
 | 
			
		||||
control_callback(struct client *c, int closed, __unused void *data)
 | 
			
		||||
static void
 | 
			
		||||
control_callback(__unused struct client *c, __unused const char *path,
 | 
			
		||||
    int error, int closed, struct evbuffer *buffer, __unused void *data)
 | 
			
		||||
{
 | 
			
		||||
	char			*line;
 | 
			
		||||
	struct cmdq_item	*item;
 | 
			
		||||
	struct cmd_parse_result	*pr;
 | 
			
		||||
 | 
			
		||||
	if (closed)
 | 
			
		||||
	if (closed || error != 0)
 | 
			
		||||
		c->flags |= CLIENT_EXIT;
 | 
			
		||||
 | 
			
		||||
	for (;;) {
 | 
			
		||||
		line = evbuffer_readln(c->stdin_data, NULL, EVBUFFER_EOL_LF);
 | 
			
		||||
		line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
 | 
			
		||||
		if (line == NULL)
 | 
			
		||||
			break;
 | 
			
		||||
		log_debug("%s: %s", __func__, line);
 | 
			
		||||
		if (*line == '\0') { /* empty line exit */
 | 
			
		||||
			free(line);
 | 
			
		||||
			c->flags |= CLIENT_EXIT;
 | 
			
		||||
@@ -104,3 +95,12 @@ control_callback(struct client *c, int closed, __unused void *data)
 | 
			
		||||
		free(line);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
control_start(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	file_read(c, "-", control_callback, c);
 | 
			
		||||
 | 
			
		||||
	if (c->flags & CLIENT_CONTROLCONTROL)
 | 
			
		||||
		file_print(c, "\033P1000p");
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										390
									
								
								file.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										390
									
								
								file.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,390 @@
 | 
			
		||||
/* $OpenBSD$ */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
 | 
			
		||||
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "tmux.h"
 | 
			
		||||
 | 
			
		||||
static int	file_next_stream = 3;
 | 
			
		||||
 | 
			
		||||
RB_GENERATE(client_files, client_file, entry, file_cmp);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
file_cmp(struct client_file *cf1, struct client_file *cf2)
 | 
			
		||||
{
 | 
			
		||||
	if (cf1->stream < cf2->stream)
 | 
			
		||||
		return (-1);
 | 
			
		||||
	if (cf1->stream > cf2->stream)
 | 
			
		||||
		return (1);
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct client_file *
 | 
			
		||||
file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	*cf;
 | 
			
		||||
 | 
			
		||||
	cf = xcalloc(1, sizeof *cf);
 | 
			
		||||
	cf->c = c;
 | 
			
		||||
	cf->references = 1;
 | 
			
		||||
	cf->stream = stream;
 | 
			
		||||
 | 
			
		||||
	cf->buffer = evbuffer_new();
 | 
			
		||||
	if (cf->buffer == NULL)
 | 
			
		||||
		fatalx("out of memory");
 | 
			
		||||
 | 
			
		||||
	cf->cb = cb;
 | 
			
		||||
	cf->data = cbdata;
 | 
			
		||||
 | 
			
		||||
	if (cf->c != NULL) {
 | 
			
		||||
		RB_INSERT(client_files, &cf->c->files, cf);
 | 
			
		||||
		cf->c->references++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (cf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
file_free(struct client_file *cf)
 | 
			
		||||
{
 | 
			
		||||
	if (--cf->references != 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	evbuffer_free(cf->buffer);
 | 
			
		||||
	free(cf->path);
 | 
			
		||||
 | 
			
		||||
	if (cf->c != NULL) {
 | 
			
		||||
		RB_REMOVE(client_files, &cf->c->files, cf);
 | 
			
		||||
		server_client_unref(cf->c);
 | 
			
		||||
	}
 | 
			
		||||
	free(cf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
file_fire_done_cb(__unused int fd, __unused short events, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	*cf = arg;
 | 
			
		||||
	struct client		*c = cf->c;
 | 
			
		||||
 | 
			
		||||
	if (cf->cb != NULL && (~c->flags & CLIENT_DEAD))
 | 
			
		||||
		cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
 | 
			
		||||
	file_free(cf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
file_fire_done(struct client_file *cf)
 | 
			
		||||
{
 | 
			
		||||
	event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
file_fire_read(struct client_file *cf)
 | 
			
		||||
{
 | 
			
		||||
	struct client	*c = cf->c;
 | 
			
		||||
 | 
			
		||||
	if (cf->cb != NULL)
 | 
			
		||||
		cf->cb(c, cf->path, cf->error, 0, cf->buffer, cf->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
file_can_print(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	if (c == NULL)
 | 
			
		||||
		return (0);
 | 
			
		||||
	if (c->session != NULL && (~c->flags & CLIENT_CONTROL))
 | 
			
		||||
		return (0);
 | 
			
		||||
	return (1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
file_print(struct client *c, const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list	ap;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	file_vprint(c, fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
file_vprint(struct client *c, const char *fmt, va_list ap)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	 find, *cf;
 | 
			
		||||
	struct msg_write_open	 msg;
 | 
			
		||||
 | 
			
		||||
	if (!file_can_print(c))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	find.stream = 1;
 | 
			
		||||
	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
 | 
			
		||||
		cf = file_create(c, 1, NULL, NULL);
 | 
			
		||||
		cf->path = xstrdup("-");
 | 
			
		||||
 | 
			
		||||
		evbuffer_add_vprintf(cf->buffer, fmt, ap);
 | 
			
		||||
 | 
			
		||||
		msg.stream = 1;
 | 
			
		||||
		msg.fd = STDOUT_FILENO;
 | 
			
		||||
		msg.flags = 0;
 | 
			
		||||
		strlcpy(msg.path, "-", sizeof msg.path);
 | 
			
		||||
		proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
 | 
			
		||||
	} else {
 | 
			
		||||
		evbuffer_add_vprintf(cf->buffer, fmt, ap);
 | 
			
		||||
		file_push(cf);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
file_print_buffer(struct client *c, void *data, size_t size)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	 find, *cf;
 | 
			
		||||
	struct msg_write_open	 msg;
 | 
			
		||||
 | 
			
		||||
	if (!file_can_print(c))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	find.stream = 1;
 | 
			
		||||
	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
 | 
			
		||||
		cf = file_create(c, 1, NULL, NULL);
 | 
			
		||||
		cf->path = xstrdup("-");
 | 
			
		||||
 | 
			
		||||
		evbuffer_add(cf->buffer, data, size);
 | 
			
		||||
 | 
			
		||||
		msg.stream = 1;
 | 
			
		||||
		msg.fd = STDOUT_FILENO;
 | 
			
		||||
		msg.flags = 0;
 | 
			
		||||
		strlcpy(msg.path, "-", sizeof msg.path);
 | 
			
		||||
		proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
 | 
			
		||||
	} else {
 | 
			
		||||
		evbuffer_add(cf->buffer, data, size);
 | 
			
		||||
		file_push(cf);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
file_error(struct client *c, const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	 find, *cf;
 | 
			
		||||
	struct msg_write_open	 msg;
 | 
			
		||||
	va_list			 ap;
 | 
			
		||||
 | 
			
		||||
	if (!file_can_print(c))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
 | 
			
		||||
	find.stream = 2;
 | 
			
		||||
	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
 | 
			
		||||
		cf = file_create(c, 2, NULL, NULL);
 | 
			
		||||
		cf->path = xstrdup("-");
 | 
			
		||||
 | 
			
		||||
		evbuffer_add_vprintf(cf->buffer, fmt, ap);
 | 
			
		||||
 | 
			
		||||
		msg.stream = 2;
 | 
			
		||||
		msg.fd = STDERR_FILENO;
 | 
			
		||||
		msg.flags = 0;
 | 
			
		||||
		strlcpy(msg.path, "-", sizeof msg.path);
 | 
			
		||||
		proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
 | 
			
		||||
	} else {
 | 
			
		||||
		evbuffer_add_vprintf(cf->buffer, fmt, ap);
 | 
			
		||||
		file_push(cf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
file_write(struct client *c, const char *path, int flags, const void *bdata,
 | 
			
		||||
    size_t bsize, client_file_cb cb, void *cbdata)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	*cf;
 | 
			
		||||
	FILE			*f;
 | 
			
		||||
	struct msg_write_open	 msg;
 | 
			
		||||
	int			 fd = -1;
 | 
			
		||||
	const char		*mode;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(path, "-") == 0) {
 | 
			
		||||
		cf = file_create(c, file_next_stream++, cb, cbdata);
 | 
			
		||||
		cf->path = xstrdup("-");
 | 
			
		||||
 | 
			
		||||
		fd = STDOUT_FILENO;
 | 
			
		||||
		if (c == NULL || c->flags & CLIENT_ATTACHED) {
 | 
			
		||||
			cf->error = EBADF;
 | 
			
		||||
			goto done;
 | 
			
		||||
		}
 | 
			
		||||
		goto skip;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cf = file_create(c, file_next_stream++, cb, cbdata);
 | 
			
		||||
	cf->path = server_client_get_path(c, path);
 | 
			
		||||
 | 
			
		||||
	if (c == NULL || c->flags & CLIENT_ATTACHED) {
 | 
			
		||||
		if (flags & O_APPEND)
 | 
			
		||||
			mode = "ab";
 | 
			
		||||
		else
 | 
			
		||||
			mode = "wb";
 | 
			
		||||
		f = fopen(cf->path, mode);
 | 
			
		||||
		if (f == NULL) {
 | 
			
		||||
			cf->error = errno;
 | 
			
		||||
			goto done;
 | 
			
		||||
		}
 | 
			
		||||
		if (fwrite(bdata, 1, bsize, f) != bsize) {
 | 
			
		||||
			fclose(f);
 | 
			
		||||
			cf->error = EIO;
 | 
			
		||||
			goto done;
 | 
			
		||||
		}
 | 
			
		||||
		fclose(f);
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
skip:
 | 
			
		||||
	evbuffer_add(cf->buffer, bdata, bsize);
 | 
			
		||||
 | 
			
		||||
	msg.stream = cf->stream;
 | 
			
		||||
	msg.fd = fd;
 | 
			
		||||
	msg.flags = flags;
 | 
			
		||||
	if (strlcpy(msg.path, cf->path, sizeof msg.path) >= sizeof msg.path) {
 | 
			
		||||
		cf->error = E2BIG;
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
	if (proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg) != 0) {
 | 
			
		||||
		cf->error = EINVAL;
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	file_fire_done(cf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	*cf;
 | 
			
		||||
	FILE			*f;
 | 
			
		||||
	struct msg_read_open	 msg;
 | 
			
		||||
	int			 fd = -1;
 | 
			
		||||
	char			 buffer[BUFSIZ];
 | 
			
		||||
	size_t			 size;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(path, "-") == 0) {
 | 
			
		||||
		cf = file_create(c, file_next_stream++, cb, cbdata);
 | 
			
		||||
		cf->path = xstrdup("-");
 | 
			
		||||
 | 
			
		||||
		fd = STDIN_FILENO;
 | 
			
		||||
		if (c == NULL || c->flags & CLIENT_ATTACHED) {
 | 
			
		||||
			cf->error = EBADF;
 | 
			
		||||
			goto done;
 | 
			
		||||
		}
 | 
			
		||||
		goto skip;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cf = file_create(c, file_next_stream++, cb, cbdata);
 | 
			
		||||
	cf->path = server_client_get_path(c, path);
 | 
			
		||||
 | 
			
		||||
	if (c == NULL || c->flags & CLIENT_ATTACHED) {
 | 
			
		||||
		f = fopen(cf->path, "rb");
 | 
			
		||||
		if (f == NULL) {
 | 
			
		||||
			cf->error = errno;
 | 
			
		||||
			goto done;
 | 
			
		||||
		}
 | 
			
		||||
		for (;;) {
 | 
			
		||||
			size = fread(buffer, 1, sizeof buffer, f);
 | 
			
		||||
			if (evbuffer_add(cf->buffer, buffer, size) != 0) {
 | 
			
		||||
				cf->error = ENOMEM;
 | 
			
		||||
				goto done;
 | 
			
		||||
			}
 | 
			
		||||
			if (size != sizeof buffer)
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		if (ferror(f)) {
 | 
			
		||||
			cf->error = EIO;
 | 
			
		||||
			goto done;
 | 
			
		||||
		}
 | 
			
		||||
		fclose(f);
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
skip:
 | 
			
		||||
	msg.stream = cf->stream;
 | 
			
		||||
	msg.fd = fd;
 | 
			
		||||
	if (strlcpy(msg.path, cf->path, sizeof msg.path) >= sizeof msg.path) {
 | 
			
		||||
		cf->error = E2BIG;
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
	if (proc_send(c->peer, MSG_READ_OPEN, -1, &msg, sizeof msg) != 0) {
 | 
			
		||||
		cf->error = EINVAL;
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	file_fire_done(cf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
file_push_cb(__unused int fd, __unused short events, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	*cf = arg;
 | 
			
		||||
	struct client		*c = cf->c;
 | 
			
		||||
 | 
			
		||||
	if (~c->flags & CLIENT_DEAD)
 | 
			
		||||
		file_push(cf);
 | 
			
		||||
	file_free(cf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
file_push(struct client_file *cf)
 | 
			
		||||
{
 | 
			
		||||
	struct client		*c = cf->c;
 | 
			
		||||
	struct msg_write_data	 msg;
 | 
			
		||||
	struct msg_write_close	 close;
 | 
			
		||||
	size_t			 sent, left;
 | 
			
		||||
 | 
			
		||||
	left = EVBUFFER_LENGTH(cf->buffer);
 | 
			
		||||
	while (left != 0) {
 | 
			
		||||
		sent = left;
 | 
			
		||||
		if (sent > sizeof msg.data)
 | 
			
		||||
			sent = sizeof msg.data;
 | 
			
		||||
		memcpy(msg.data, EVBUFFER_DATA(cf->buffer), sent);
 | 
			
		||||
		msg.size = sent;
 | 
			
		||||
 | 
			
		||||
		msg.stream = cf->stream;
 | 
			
		||||
		if (proc_send(c->peer, MSG_WRITE, -1, &msg, sizeof msg) != 0)
 | 
			
		||||
			break;
 | 
			
		||||
		evbuffer_drain(cf->buffer, sent);
 | 
			
		||||
 | 
			
		||||
		left = EVBUFFER_LENGTH(cf->buffer);
 | 
			
		||||
		log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream,
 | 
			
		||||
		    sent, left);
 | 
			
		||||
	}
 | 
			
		||||
	if (left != 0) {
 | 
			
		||||
		cf->references++;
 | 
			
		||||
		event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
 | 
			
		||||
	} else if (cf->stream > 2) {
 | 
			
		||||
		close.stream = cf->stream;
 | 
			
		||||
		proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
 | 
			
		||||
		file_fire_done(cf);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										223
									
								
								server-client.c
									
									
									
									
									
								
							
							
						
						
									
										223
									
								
								server-client.c
									
									
									
									
									
								
							@@ -50,6 +50,12 @@ static void	server_client_dispatch(struct imsg *, void *);
 | 
			
		||||
static void	server_client_dispatch_command(struct client *, struct imsg *);
 | 
			
		||||
static void	server_client_dispatch_identify(struct client *, struct imsg *);
 | 
			
		||||
static void	server_client_dispatch_shell(struct client *);
 | 
			
		||||
static void	server_client_dispatch_write_ready(struct client *,
 | 
			
		||||
		    struct imsg *);
 | 
			
		||||
static void	server_client_dispatch_read_data(struct client *,
 | 
			
		||||
		    struct imsg *);
 | 
			
		||||
static void	server_client_dispatch_read_done(struct client *,
 | 
			
		||||
		    struct imsg *);
 | 
			
		||||
 | 
			
		||||
/* Number of attached clients. */
 | 
			
		||||
u_int
 | 
			
		||||
@@ -197,16 +203,6 @@ server_client_create(int fd)
 | 
			
		||||
 | 
			
		||||
	TAILQ_INIT(&c->queue);
 | 
			
		||||
 | 
			
		||||
	c->stdin_data = evbuffer_new();
 | 
			
		||||
	if (c->stdin_data == NULL)
 | 
			
		||||
		fatalx("out of memory");
 | 
			
		||||
	c->stdout_data = evbuffer_new();
 | 
			
		||||
	if (c->stdout_data == NULL)
 | 
			
		||||
		fatalx("out of memory");
 | 
			
		||||
	c->stderr_data = evbuffer_new();
 | 
			
		||||
	if (c->stderr_data == NULL)
 | 
			
		||||
		fatalx("out of memory");
 | 
			
		||||
 | 
			
		||||
	c->tty.fd = -1;
 | 
			
		||||
	c->title = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -225,6 +221,8 @@ server_client_create(int fd)
 | 
			
		||||
	c->prompt_buffer = NULL;
 | 
			
		||||
	c->prompt_index = 0;
 | 
			
		||||
 | 
			
		||||
	RB_INIT(&c->files);
 | 
			
		||||
 | 
			
		||||
	c->flags |= CLIENT_FOCUSED;
 | 
			
		||||
 | 
			
		||||
	c->keytable = key_bindings_get_table("root", 1);
 | 
			
		||||
@@ -266,6 +264,7 @@ void
 | 
			
		||||
server_client_lost(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	struct message_entry	*msg, *msg1;
 | 
			
		||||
	struct client_file	*cf;
 | 
			
		||||
 | 
			
		||||
	c->flags |= CLIENT_DEAD;
 | 
			
		||||
 | 
			
		||||
@@ -273,8 +272,10 @@ server_client_lost(struct client *c)
 | 
			
		||||
	status_prompt_clear(c);
 | 
			
		||||
	status_message_clear(c);
 | 
			
		||||
 | 
			
		||||
	if (c->stdin_callback != NULL)
 | 
			
		||||
		c->stdin_callback(c, 1, c->stdin_callback_data);
 | 
			
		||||
	RB_FOREACH(cf, client_files, &c->files) {
 | 
			
		||||
		cf->error = EINTR;
 | 
			
		||||
		file_fire_done(cf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	TAILQ_REMOVE(&clients, c, entry);
 | 
			
		||||
	log_debug("lost client %p", c);
 | 
			
		||||
@@ -288,11 +289,6 @@ server_client_lost(struct client *c)
 | 
			
		||||
	free(c->ttyname);
 | 
			
		||||
	free(c->term);
 | 
			
		||||
 | 
			
		||||
	evbuffer_free(c->stdin_data);
 | 
			
		||||
	evbuffer_free(c->stdout_data);
 | 
			
		||||
	if (c->stderr_data != c->stdout_data)
 | 
			
		||||
		evbuffer_free(c->stderr_data);
 | 
			
		||||
 | 
			
		||||
	status_free(c);
 | 
			
		||||
 | 
			
		||||
	free(c->title);
 | 
			
		||||
@@ -1560,17 +1556,17 @@ server_client_click_timer(__unused int fd, __unused short events, void *data)
 | 
			
		||||
static void
 | 
			
		||||
server_client_check_exit(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	*cf;
 | 
			
		||||
 | 
			
		||||
	if (~c->flags & CLIENT_EXIT)
 | 
			
		||||
		return;
 | 
			
		||||
	if (c->flags & CLIENT_EXITED)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (EVBUFFER_LENGTH(c->stdin_data) != 0)
 | 
			
		||||
		return;
 | 
			
		||||
	if (EVBUFFER_LENGTH(c->stdout_data) != 0)
 | 
			
		||||
		return;
 | 
			
		||||
	if (EVBUFFER_LENGTH(c->stderr_data) != 0)
 | 
			
		||||
		return;
 | 
			
		||||
	RB_FOREACH(cf, client_files, &c->files) {
 | 
			
		||||
		if (EVBUFFER_LENGTH(cf->buffer) != 0)
 | 
			
		||||
			return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (c->flags & CLIENT_ATTACHED)
 | 
			
		||||
		notify_client("client-detached", c);
 | 
			
		||||
@@ -1709,11 +1705,10 @@ server_client_set_title(struct client *c)
 | 
			
		||||
static void
 | 
			
		||||
server_client_dispatch(struct imsg *imsg, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	struct client		*c = arg;
 | 
			
		||||
	struct msg_stdin_data	 stdindata;
 | 
			
		||||
	const char		*data;
 | 
			
		||||
	ssize_t			 datalen;
 | 
			
		||||
	struct session		*s;
 | 
			
		||||
	struct client	*c = arg;
 | 
			
		||||
	const char	*data;
 | 
			
		||||
	ssize_t		 datalen;
 | 
			
		||||
	struct session	*s;
 | 
			
		||||
 | 
			
		||||
	if (c->flags & CLIENT_DEAD)
 | 
			
		||||
		return;
 | 
			
		||||
@@ -1740,21 +1735,6 @@ server_client_dispatch(struct imsg *imsg, void *arg)
 | 
			
		||||
	case MSG_COMMAND:
 | 
			
		||||
		server_client_dispatch_command(c, imsg);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_STDIN:
 | 
			
		||||
		if (datalen != sizeof stdindata)
 | 
			
		||||
			fatalx("bad MSG_STDIN size");
 | 
			
		||||
		memcpy(&stdindata, data, sizeof stdindata);
 | 
			
		||||
 | 
			
		||||
		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;
 | 
			
		||||
	case MSG_RESIZE:
 | 
			
		||||
		if (datalen != 0)
 | 
			
		||||
			fatalx("bad MSG_RESIZE size");
 | 
			
		||||
@@ -1806,6 +1786,15 @@ server_client_dispatch(struct imsg *imsg, void *arg)
 | 
			
		||||
 | 
			
		||||
		server_client_dispatch_shell(c);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_WRITE_READY:
 | 
			
		||||
		server_client_dispatch_write_ready(c, imsg);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_READ:
 | 
			
		||||
		server_client_dispatch_read_data(c, imsg);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_READ_DONE:
 | 
			
		||||
		server_client_dispatch_read_done(c, imsg);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1824,7 +1813,7 @@ server_client_command_done(struct cmdq_item *item, __unused void *data)
 | 
			
		||||
static void
 | 
			
		||||
server_client_dispatch_command(struct client *c, struct imsg *imsg)
 | 
			
		||||
{
 | 
			
		||||
	struct msg_command_data	  data;
 | 
			
		||||
	struct msg_command	  data;
 | 
			
		||||
	char			 *buf;
 | 
			
		||||
	size_t			  len;
 | 
			
		||||
	int			  argc;
 | 
			
		||||
@@ -1964,19 +1953,11 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
 | 
			
		||||
	log_debug("client %p name is %s", c, c->name);
 | 
			
		||||
 | 
			
		||||
	if (c->flags & CLIENT_CONTROL) {
 | 
			
		||||
		c->stdin_callback = control_callback;
 | 
			
		||||
 | 
			
		||||
		evbuffer_free(c->stderr_data);
 | 
			
		||||
		c->stderr_data = c->stdout_data;
 | 
			
		||||
 | 
			
		||||
		if (c->flags & CLIENT_CONTROLCONTROL)
 | 
			
		||||
			evbuffer_add_printf(c->stdout_data, "\033P1000p");
 | 
			
		||||
		proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
 | 
			
		||||
 | 
			
		||||
		c->tty.fd = -1;
 | 
			
		||||
 | 
			
		||||
		close(c->fd);
 | 
			
		||||
		c->fd = -1;
 | 
			
		||||
 | 
			
		||||
		control_start(c);
 | 
			
		||||
		c->tty.fd = -1;
 | 
			
		||||
	} else if (c->fd != -1) {
 | 
			
		||||
		if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
 | 
			
		||||
			close(c->fd);
 | 
			
		||||
@@ -2016,93 +1997,71 @@ server_client_dispatch_shell(struct client *c)
 | 
			
		||||
	proc_kill_peer(c->peer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Event callback to push more stdout data if any left. */
 | 
			
		||||
/* Handle write ready message. */
 | 
			
		||||
static void
 | 
			
		||||
server_client_stdout_cb(__unused int fd, __unused short events, void *arg)
 | 
			
		||||
server_client_dispatch_write_ready(struct client *c, struct imsg *imsg)
 | 
			
		||||
{
 | 
			
		||||
	struct client	*c = arg;
 | 
			
		||||
	struct msg_write_ready	*msg = imsg->data;
 | 
			
		||||
	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
 | 
			
		||||
	struct client_file	 find, *cf;
 | 
			
		||||
 | 
			
		||||
	if (~c->flags & CLIENT_DEAD)
 | 
			
		||||
		server_client_push_stdout(c);
 | 
			
		||||
	server_client_unref(c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Push stdout to client if possible. */
 | 
			
		||||
void
 | 
			
		||||
server_client_push_stdout(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	struct msg_stdout_data data;
 | 
			
		||||
	size_t		       sent, left;
 | 
			
		||||
 | 
			
		||||
	left = EVBUFFER_LENGTH(c->stdout_data);
 | 
			
		||||
	while (left != 0) {
 | 
			
		||||
		sent = left;
 | 
			
		||||
		if (sent > sizeof data.data)
 | 
			
		||||
			sent = sizeof data.data;
 | 
			
		||||
		memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent);
 | 
			
		||||
		data.size = sent;
 | 
			
		||||
 | 
			
		||||
		if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0)
 | 
			
		||||
			break;
 | 
			
		||||
		evbuffer_drain(c->stdout_data, sent);
 | 
			
		||||
 | 
			
		||||
		left = EVBUFFER_LENGTH(c->stdout_data);
 | 
			
		||||
		log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
 | 
			
		||||
		    sent, left);
 | 
			
		||||
	}
 | 
			
		||||
	if (left != 0) {
 | 
			
		||||
		c->references++;
 | 
			
		||||
		event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL);
 | 
			
		||||
		log_debug("%s: client %p, queued", __func__, c);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Event callback to push more stderr data if any left. */
 | 
			
		||||
static void
 | 
			
		||||
server_client_stderr_cb(__unused int fd, __unused short events, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	struct client	*c = arg;
 | 
			
		||||
 | 
			
		||||
	if (~c->flags & CLIENT_DEAD)
 | 
			
		||||
		server_client_push_stderr(c);
 | 
			
		||||
	server_client_unref(c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Push stderr to client if possible. */
 | 
			
		||||
void
 | 
			
		||||
server_client_push_stderr(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	struct msg_stderr_data data;
 | 
			
		||||
	size_t		       sent, left;
 | 
			
		||||
 | 
			
		||||
	if (c->stderr_data == c->stdout_data) {
 | 
			
		||||
		server_client_push_stdout(c);
 | 
			
		||||
	if (msglen != sizeof *msg)
 | 
			
		||||
		fatalx("bad MSG_WRITE_READY size");
 | 
			
		||||
	find.stream = msg->stream;
 | 
			
		||||
	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (msg->error != 0) {
 | 
			
		||||
		cf->error = msg->error;
 | 
			
		||||
		file_fire_done(cf);
 | 
			
		||||
	} else
 | 
			
		||||
		file_push(cf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	left = EVBUFFER_LENGTH(c->stderr_data);
 | 
			
		||||
	while (left != 0) {
 | 
			
		||||
		sent = left;
 | 
			
		||||
		if (sent > sizeof data.data)
 | 
			
		||||
			sent = sizeof data.data;
 | 
			
		||||
		memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent);
 | 
			
		||||
		data.size = sent;
 | 
			
		||||
/* Handle read data message. */
 | 
			
		||||
static void
 | 
			
		||||
server_client_dispatch_read_data(struct client *c, struct imsg *imsg)
 | 
			
		||||
{
 | 
			
		||||
	struct msg_read_data	*msg = imsg->data;
 | 
			
		||||
	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
 | 
			
		||||
	struct client_file	 find, *cf;
 | 
			
		||||
	void			*bdata = msg->data;
 | 
			
		||||
	size_t			 bsize = msg->size;
 | 
			
		||||
 | 
			
		||||
		if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0)
 | 
			
		||||
			break;
 | 
			
		||||
		evbuffer_drain(c->stderr_data, sent);
 | 
			
		||||
	if (msglen != sizeof *msg)
 | 
			
		||||
		fatalx("bad MSG_READ_DATA size");
 | 
			
		||||
	find.stream = msg->stream;
 | 
			
		||||
	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
		left = EVBUFFER_LENGTH(c->stderr_data);
 | 
			
		||||
		log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
 | 
			
		||||
		    sent, left);
 | 
			
		||||
	}
 | 
			
		||||
	if (left != 0) {
 | 
			
		||||
		c->references++;
 | 
			
		||||
		event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL);
 | 
			
		||||
		log_debug("%s: client %p, queued", __func__, c);
 | 
			
		||||
	log_debug("%s: file %d read %zu bytes", c->name, cf->stream, bsize);
 | 
			
		||||
	if (cf->error == 0) {
 | 
			
		||||
		if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
 | 
			
		||||
			cf->error = ENOMEM;
 | 
			
		||||
			file_fire_done(cf);
 | 
			
		||||
		} else
 | 
			
		||||
			file_fire_read(cf);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle read done message. */
 | 
			
		||||
static void
 | 
			
		||||
server_client_dispatch_read_done(struct client *c, struct imsg *imsg)
 | 
			
		||||
{
 | 
			
		||||
	struct msg_read_done	*msg = imsg->data;
 | 
			
		||||
	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
 | 
			
		||||
	struct client_file	 find, *cf;
 | 
			
		||||
 | 
			
		||||
	if (msglen != sizeof *msg)
 | 
			
		||||
		fatalx("bad MSG_READ_DONE size");
 | 
			
		||||
	find.stream = msg->stream;
 | 
			
		||||
	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	log_debug("%s: file %d read done", c->name, cf->stream);
 | 
			
		||||
	cf->error = msg->error;
 | 
			
		||||
	file_fire_done(cf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add to client message log. */
 | 
			
		||||
void
 | 
			
		||||
server_client_add_message(struct client *c, const char *fmt, ...)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										30
									
								
								server-fn.c
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								server-fn.c
									
									
									
									
									
								
							@@ -439,36 +439,6 @@ server_check_unattached(void)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
 | 
			
		||||
    void *), void *cb_data, char **cause)
 | 
			
		||||
{
 | 
			
		||||
	if (c == NULL || c->session != 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 is 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);
 | 
			
		||||
 | 
			
		||||
	proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
 | 
			
		||||
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
server_unzoom_window(struct window *w)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										108
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								tmux.h
									
									
									
									
									
								
							@@ -478,13 +478,21 @@ enum msgtype {
 | 
			
		||||
	MSG_RESIZE,
 | 
			
		||||
	MSG_SHELL,
 | 
			
		||||
	MSG_SHUTDOWN,
 | 
			
		||||
	MSG_STDERR,
 | 
			
		||||
	MSG_STDIN,
 | 
			
		||||
	MSG_STDOUT,
 | 
			
		||||
	MSG_OLDSTDERR, /* unused */
 | 
			
		||||
	MSG_OLDSTDIN, /* unused */
 | 
			
		||||
	MSG_OLDSTDOUT, /* unused */
 | 
			
		||||
	MSG_SUSPEND,
 | 
			
		||||
	MSG_UNLOCK,
 | 
			
		||||
	MSG_WAKEUP,
 | 
			
		||||
	MSG_EXEC,
 | 
			
		||||
 | 
			
		||||
	MSG_READ_OPEN = 300,
 | 
			
		||||
	MSG_READ,
 | 
			
		||||
	MSG_READ_DONE,
 | 
			
		||||
	MSG_WRITE_OPEN,
 | 
			
		||||
	MSG_WRITE,
 | 
			
		||||
	MSG_WRITE_READY,
 | 
			
		||||
	MSG_WRITE_CLOSE
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -492,23 +500,47 @@ enum msgtype {
 | 
			
		||||
 *
 | 
			
		||||
 * Don't forget to bump PROTOCOL_VERSION if any of these change!
 | 
			
		||||
 */
 | 
			
		||||
struct msg_command_data {
 | 
			
		||||
struct msg_command {
 | 
			
		||||
	int	argc;
 | 
			
		||||
}; /* followed by packed argv */
 | 
			
		||||
 | 
			
		||||
struct msg_stdin_data {
 | 
			
		||||
	ssize_t	size;
 | 
			
		||||
struct msg_read_open {
 | 
			
		||||
	int	stream;
 | 
			
		||||
	int	fd;
 | 
			
		||||
	char	path[PATH_MAX];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct msg_read_data {
 | 
			
		||||
	int	stream;
 | 
			
		||||
	size_t	size;
 | 
			
		||||
	char	data[BUFSIZ];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct msg_stdout_data {
 | 
			
		||||
	ssize_t	size;
 | 
			
		||||
struct msg_read_done {
 | 
			
		||||
	int	stream;
 | 
			
		||||
	int	error;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct msg_write_open {
 | 
			
		||||
	int	stream;
 | 
			
		||||
	int	fd;
 | 
			
		||||
	char	path[PATH_MAX];
 | 
			
		||||
	int	flags;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct msg_write_data {
 | 
			
		||||
	int	stream;
 | 
			
		||||
	size_t	size;
 | 
			
		||||
	char	data[BUFSIZ];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct msg_stderr_data {
 | 
			
		||||
	ssize_t	size;
 | 
			
		||||
	char	data[BUFSIZ];
 | 
			
		||||
struct msg_write_ready {
 | 
			
		||||
	int	stream;
 | 
			
		||||
	int	error;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct msg_write_close {
 | 
			
		||||
	int	stream;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Mode keys. */
 | 
			
		||||
@@ -1474,6 +1506,29 @@ struct status_line {
 | 
			
		||||
	struct status_line_entry entries[STATUS_LINES_LIMIT];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* File in client. */
 | 
			
		||||
typedef void (*client_file_cb) (struct client *, const char *, int, int,
 | 
			
		||||
    struct evbuffer *, void *);
 | 
			
		||||
struct client_file {
 | 
			
		||||
	struct client			*c;
 | 
			
		||||
	int				 references;
 | 
			
		||||
	int				 stream;
 | 
			
		||||
 | 
			
		||||
	char				*path;
 | 
			
		||||
	struct evbuffer			*buffer;
 | 
			
		||||
	struct bufferevent		*event;
 | 
			
		||||
 | 
			
		||||
	int				 fd;
 | 
			
		||||
	int				 error;
 | 
			
		||||
	int				 closed;
 | 
			
		||||
 | 
			
		||||
	client_file_cb			 cb;
 | 
			
		||||
	void				*data;
 | 
			
		||||
 | 
			
		||||
	RB_ENTRY (client_file)		 entry;
 | 
			
		||||
};
 | 
			
		||||
RB_HEAD(client_files, client_file);
 | 
			
		||||
 | 
			
		||||
/* Client connection. */
 | 
			
		||||
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
 | 
			
		||||
typedef void (*prompt_free_cb)(void *);
 | 
			
		||||
@@ -1507,13 +1562,6 @@ struct client {
 | 
			
		||||
	size_t		 discarded;
 | 
			
		||||
	size_t		 redraw;
 | 
			
		||||
 | 
			
		||||
	void		(*stdin_callback)(struct client *, int, void *);
 | 
			
		||||
	void		*stdin_callback_data;
 | 
			
		||||
	struct evbuffer	*stdin_data;
 | 
			
		||||
	int		 stdin_closed;
 | 
			
		||||
	struct evbuffer	*stdout_data;
 | 
			
		||||
	struct evbuffer	*stderr_data;
 | 
			
		||||
 | 
			
		||||
	struct event	 repeat_timer;
 | 
			
		||||
 | 
			
		||||
	struct event	 click_timer;
 | 
			
		||||
@@ -1597,6 +1645,8 @@ struct client {
 | 
			
		||||
	void		*overlay_data;
 | 
			
		||||
	struct event	 overlay_timer;
 | 
			
		||||
 | 
			
		||||
	struct client_files files;
 | 
			
		||||
 | 
			
		||||
	TAILQ_ENTRY(client) entry;
 | 
			
		||||
};
 | 
			
		||||
TAILQ_HEAD(clients, client);
 | 
			
		||||
@@ -2129,6 +2179,23 @@ void	alerts_reset_all(void);
 | 
			
		||||
void	alerts_queue(struct window *, int);
 | 
			
		||||
void	alerts_check_session(struct session *);
 | 
			
		||||
 | 
			
		||||
/* file.c */
 | 
			
		||||
int	 file_cmp(struct client_file *, struct client_file *);
 | 
			
		||||
RB_PROTOTYPE(client_files, client_file, entry, file_cmp);
 | 
			
		||||
struct client_file *file_create(struct client *, int, client_file_cb, void *);
 | 
			
		||||
void	 file_free(struct client_file *);
 | 
			
		||||
void	 file_fire_done(struct client_file *);
 | 
			
		||||
void	 file_fire_read(struct client_file *);
 | 
			
		||||
int	 file_can_print(struct client *);
 | 
			
		||||
void printflike(2, 3) file_print(struct client *, const char *, ...);
 | 
			
		||||
void 	 file_vprint(struct client *, const char *, va_list);
 | 
			
		||||
void 	 file_print_buffer(struct client *, void *, size_t);
 | 
			
		||||
void printflike(2, 3) file_error(struct client *, const char *, ...);
 | 
			
		||||
void	 file_write(struct client *, const char *, int, const void *, size_t,
 | 
			
		||||
	     client_file_cb, void *);
 | 
			
		||||
void	 file_read(struct client *, const char *, client_file_cb, void *);
 | 
			
		||||
void	 file_push(struct client_file *);
 | 
			
		||||
 | 
			
		||||
/* server.c */
 | 
			
		||||
extern struct tmuxproc *server_proc;
 | 
			
		||||
extern struct clients clients;
 | 
			
		||||
@@ -2187,8 +2254,6 @@ void	 server_unlink_window(struct session *, struct winlink *);
 | 
			
		||||
void	 server_destroy_pane(struct window_pane *, int);
 | 
			
		||||
void	 server_destroy_session(struct session *);
 | 
			
		||||
void	 server_check_unattached(void);
 | 
			
		||||
int	 server_set_stdin_callback(struct client *, void (*)(struct client *,
 | 
			
		||||
	     int, void *), void *, char **);
 | 
			
		||||
void	 server_unzoom_window(struct window *);
 | 
			
		||||
 | 
			
		||||
/* status.c */
 | 
			
		||||
@@ -2573,9 +2638,8 @@ char	*default_window_name(struct window *);
 | 
			
		||||
char	*parse_window_name(const char *);
 | 
			
		||||
 | 
			
		||||
/* control.c */
 | 
			
		||||
void	control_callback(struct client *, int, void *);
 | 
			
		||||
void	control_start(struct client *);
 | 
			
		||||
void printflike(2, 3) control_write(struct client *, const char *, ...);
 | 
			
		||||
void	control_write_buffer(struct client *, struct evbuffer *);
 | 
			
		||||
 | 
			
		||||
/* control-notify.c */
 | 
			
		||||
void	control_notify_input(struct client *, struct window_pane *,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								window.c
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								window.c
									
									
									
									
									
								
							@@ -1598,31 +1598,28 @@ winlink_shuffle_up(struct session *s, struct winlink *wl)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
window_pane_input_callback(struct client *c, int closed, void *data)
 | 
			
		||||
window_pane_input_callback(struct client *c, __unused const char *path,
 | 
			
		||||
    int error, int closed, struct evbuffer *buffer, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct window_pane_input_data	*cdata = data;
 | 
			
		||||
	struct window_pane		*wp;
 | 
			
		||||
	struct evbuffer			*evb = c->stdin_data;
 | 
			
		||||
	u_char				*buf = EVBUFFER_DATA(evb);
 | 
			
		||||
	size_t				 len = EVBUFFER_LENGTH(evb);
 | 
			
		||||
	u_char				*buf = EVBUFFER_DATA(buffer);
 | 
			
		||||
	size_t				 len = EVBUFFER_LENGTH(buffer);
 | 
			
		||||
 | 
			
		||||
	wp = window_pane_find_by_id(cdata->wp);
 | 
			
		||||
	if (wp == NULL || closed || c->flags & CLIENT_DEAD) {
 | 
			
		||||
	if (wp == NULL || closed || error != 0 || c->flags & CLIENT_DEAD) {
 | 
			
		||||
		if (wp == NULL)
 | 
			
		||||
			c->flags |= CLIENT_EXIT;
 | 
			
		||||
		evbuffer_drain(evb, len);
 | 
			
		||||
 | 
			
		||||
		c->stdin_callback = NULL;
 | 
			
		||||
		server_client_unref(c);
 | 
			
		||||
 | 
			
		||||
		evbuffer_drain(buffer, len);
 | 
			
		||||
		cmdq_continue(cdata->item);
 | 
			
		||||
		free(cdata);
 | 
			
		||||
 | 
			
		||||
		server_client_unref(c);
 | 
			
		||||
		free(cdata);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	input_parse_buffer(wp, buf, len);
 | 
			
		||||
	evbuffer_drain(evb, len);
 | 
			
		||||
	evbuffer_drain(buffer, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
@@ -1641,6 +1638,8 @@ window_pane_start_input(struct window_pane *wp, struct cmdq_item *item,
 | 
			
		||||
	cdata->item = item;
 | 
			
		||||
	cdata->wp = wp->id;
 | 
			
		||||
 | 
			
		||||
	return (server_set_stdin_callback(c, window_pane_input_callback, cdata,
 | 
			
		||||
	    cause));
 | 
			
		||||
	c->references++;
 | 
			
		||||
	file_read(c, "-", window_pane_input_callback, cdata);
 | 
			
		||||
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user