mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 09:26:05 +00:00 
			
		
		
		
	Merge branch 'obsd-master'
This commit is contained in:
		
							
								
								
									
										16
									
								
								client.c
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								client.c
									
									
									
									
									
								
							@@ -386,6 +386,11 @@ client_main(struct event_base *base, int argc, char **argv, int flags, int feat)
 | 
			
		||||
		client_exec(client_execshell, client_execcmd);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Restore streams to blocking. */
 | 
			
		||||
	setblocking(STDIN_FILENO, 1);
 | 
			
		||||
	setblocking(STDOUT_FILENO, 1);
 | 
			
		||||
	setblocking(STDERR_FILENO, 1);
 | 
			
		||||
 | 
			
		||||
	/* Print the exit message, if any, and exit. */
 | 
			
		||||
	if (client_attached) {
 | 
			
		||||
		if (client_exitreason != CLIENT_EXIT_NONE)
 | 
			
		||||
@@ -394,18 +399,17 @@ client_main(struct event_base *base, int argc, char **argv, int flags, int feat)
 | 
			
		||||
		ppid = getppid();
 | 
			
		||||
		if (client_exittype == MSG_DETACHKILL && ppid > 1)
 | 
			
		||||
			kill(ppid, SIGHUP);
 | 
			
		||||
	} else if (client_flags & CLIENT_CONTROLCONTROL) {
 | 
			
		||||
	} else if (client_flags & CLIENT_CONTROL) {
 | 
			
		||||
		if (client_exitreason != CLIENT_EXIT_NONE)
 | 
			
		||||
			printf("%%exit %s\n", client_exit_message());
 | 
			
		||||
		else
 | 
			
		||||
			printf("%%exit\n");
 | 
			
		||||
		printf("\033\\");
 | 
			
		||||
		tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio);
 | 
			
		||||
		if (client_flags & CLIENT_CONTROLCONTROL) {
 | 
			
		||||
			printf("\033\\");
 | 
			
		||||
			tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio);
 | 
			
		||||
		}
 | 
			
		||||
	} else if (client_exitreason != CLIENT_EXIT_NONE)
 | 
			
		||||
		fprintf(stderr, "%s\n", client_exit_message());
 | 
			
		||||
	setblocking(STDIN_FILENO, 1);
 | 
			
		||||
	setblocking(STDOUT_FILENO, 1);
 | 
			
		||||
	setblocking(STDERR_FILENO, 1);
 | 
			
		||||
	return (client_exitval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -214,15 +214,20 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
		return (CMD_RETURN_ERROR);
 | 
			
		||||
 | 
			
		||||
	if (args_has(args, 'p')) {
 | 
			
		||||
		if (!file_can_print(c)) {
 | 
			
		||||
			cmdq_error(item, "can't write output to client");
 | 
			
		||||
			free(buf);
 | 
			
		||||
			return (CMD_RETURN_ERROR);
 | 
			
		||||
		}
 | 
			
		||||
		file_print_buffer(c, buf, len);
 | 
			
		||||
		if (args_has(args, 'P') && len > 0)
 | 
			
		||||
		if (len > 0 && buf[len - 1] == '\n')
 | 
			
		||||
			len--;
 | 
			
		||||
		if (c->flags & CLIENT_CONTROL)
 | 
			
		||||
			control_write(c, "%.*s", (int)len, buf);
 | 
			
		||||
		else {
 | 
			
		||||
			if (!file_can_print(c)) {
 | 
			
		||||
				cmdq_error(item, "can't write to client");
 | 
			
		||||
				free(buf);
 | 
			
		||||
				return (CMD_RETURN_ERROR);
 | 
			
		||||
			}
 | 
			
		||||
			file_print_buffer(c, buf, len);
 | 
			
		||||
			file_print(c, "\n");
 | 
			
		||||
		free(buf);
 | 
			
		||||
			free(buf);
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		bufname = NULL;
 | 
			
		||||
		if (args_has(args, 'b'))
 | 
			
		||||
 
 | 
			
		||||
@@ -165,7 +165,10 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
	 * the terminal as that calls tcsetattr() to prepare for tmux taking
 | 
			
		||||
	 * over.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!detached && !already_attached && c->fd != -1) {
 | 
			
		||||
	if (!detached &&
 | 
			
		||||
	    !already_attached &&
 | 
			
		||||
	    c->fd != -1 &&
 | 
			
		||||
	    (~c->flags & CLIENT_CONTROL)) {
 | 
			
		||||
		if (server_client_check_nested(cmdq_get_client(item))) {
 | 
			
		||||
			cmdq_error(item, "sessions should be nested with care, "
 | 
			
		||||
			    "unset $TMUX to force");
 | 
			
		||||
 
 | 
			
		||||
@@ -780,7 +780,7 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
 | 
			
		||||
	u_int		 number = item->number;
 | 
			
		||||
 | 
			
		||||
	if (c != NULL && (c->flags & CLIENT_CONTROL))
 | 
			
		||||
		file_print(c, "%%%s %ld %u %d\n", guard, t, number, flags);
 | 
			
		||||
		control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Show message from command. */
 | 
			
		||||
@@ -807,7 +807,10 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
 | 
			
		||||
			msg = utf8_sanitize(tmp);
 | 
			
		||||
			free(tmp);
 | 
			
		||||
		}
 | 
			
		||||
		file_print(c, "%s\n", msg);
 | 
			
		||||
		if (c->flags & CLIENT_CONTROL)
 | 
			
		||||
			control_write(c, "%s", msg);
 | 
			
		||||
		else
 | 
			
		||||
			file_print(c, "%s\n", msg);
 | 
			
		||||
	} else {
 | 
			
		||||
		wp = server_client_get_pane(c);
 | 
			
		||||
		wme = TAILQ_FIRST(&wp->modes);
 | 
			
		||||
@@ -849,7 +852,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
 | 
			
		||||
			free(tmp);
 | 
			
		||||
		}
 | 
			
		||||
		if (c->flags & CLIENT_CONTROL)
 | 
			
		||||
			file_print(c, "%s\n", msg);
 | 
			
		||||
			control_write(c, "%s", msg);
 | 
			
		||||
		else
 | 
			
		||||
			file_error(c, "%s\n", msg);
 | 
			
		||||
		c->retval = 1;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										474
									
								
								control.c
									
									
									
									
									
								
							
							
						
						
									
										474
									
								
								control.c
									
									
									
									
									
								
							@@ -27,112 +27,183 @@
 | 
			
		||||
 | 
			
		||||
#include "tmux.h"
 | 
			
		||||
 | 
			
		||||
/* Control client offset. */
 | 
			
		||||
struct control_offset {
 | 
			
		||||
	u_int				pane;
 | 
			
		||||
/*
 | 
			
		||||
 * Block of data to output. Each client has one "all" queue of blocks and
 | 
			
		||||
 * another queue for each pane (in struct client_offset). %output blocks are
 | 
			
		||||
 * added to both queues and other output lines (notifications) added only to
 | 
			
		||||
 * the client queue.
 | 
			
		||||
 *
 | 
			
		||||
 * When a client becomes writeable, data from blocks on the pane queue are sent
 | 
			
		||||
 * up to the maximum size (CLIENT_BUFFER_HIGH). If a block is entirely written,
 | 
			
		||||
 * it is removed from both pane and client queues and if this means non-%output
 | 
			
		||||
 * blocks are now at the head of the client queue, they are written.
 | 
			
		||||
 *
 | 
			
		||||
 * This means a %output block holds up any subsequent non-%output blocks until
 | 
			
		||||
 * it is written which enforces ordering even if the client cannot accept the
 | 
			
		||||
 * entire block in one go.
 | 
			
		||||
 */
 | 
			
		||||
struct control_block {
 | 
			
		||||
	size_t				 size;
 | 
			
		||||
	char				*line;
 | 
			
		||||
 | 
			
		||||
	struct window_pane_offset	offset;
 | 
			
		||||
	int				flags;
 | 
			
		||||
#define CONTROL_OFFSET_OFF 0x1
 | 
			
		||||
	TAILQ_ENTRY(control_block)	 entry;
 | 
			
		||||
	TAILQ_ENTRY(control_block)	 all_entry;
 | 
			
		||||
 };
 | 
			
		||||
 | 
			
		||||
	RB_ENTRY(control_offset)	entry;
 | 
			
		||||
/* Control client pane. */
 | 
			
		||||
struct control_pane {
 | 
			
		||||
	u_int				 pane;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Offsets into the pane data. The first (offset) is the data we have
 | 
			
		||||
	 * written; the second (queued) the data we have queued (pointed to by
 | 
			
		||||
	 * a block).
 | 
			
		||||
	 */
 | 
			
		||||
	struct window_pane_offset	 offset;
 | 
			
		||||
	struct window_pane_offset	 queued;
 | 
			
		||||
 | 
			
		||||
	int				 flags;
 | 
			
		||||
#define CONTROL_PANE_OFF 0x1
 | 
			
		||||
 | 
			
		||||
	int				 pending_flag;
 | 
			
		||||
	TAILQ_ENTRY(control_pane)	 pending_entry;
 | 
			
		||||
 | 
			
		||||
	TAILQ_HEAD(, control_block)	 blocks;
 | 
			
		||||
 | 
			
		||||
	RB_ENTRY(control_pane)		 entry;
 | 
			
		||||
};
 | 
			
		||||
RB_HEAD(control_offsets, control_offset);
 | 
			
		||||
RB_HEAD(control_panes, control_pane);
 | 
			
		||||
 | 
			
		||||
/* Control client state. */
 | 
			
		||||
struct control_state {
 | 
			
		||||
	struct control_offsets	 offsets;
 | 
			
		||||
	struct control_panes		 panes;
 | 
			
		||||
 | 
			
		||||
	struct bufferevent	*read_event;
 | 
			
		||||
	struct bufferevent	*write_event;
 | 
			
		||||
	TAILQ_HEAD(, control_pane)	 pending_list;
 | 
			
		||||
	u_int				 pending_count;
 | 
			
		||||
 | 
			
		||||
	TAILQ_HEAD(, control_block)	 all_blocks;
 | 
			
		||||
 | 
			
		||||
	struct bufferevent		*read_event;
 | 
			
		||||
	struct bufferevent		*write_event;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Compare client offsets. */
 | 
			
		||||
/* Low watermark. */
 | 
			
		||||
#define CONTROL_BUFFER_LOW 512
 | 
			
		||||
#define CONTROL_BUFFER_HIGH 8192
 | 
			
		||||
 | 
			
		||||
/* Minimum to write to each client. */
 | 
			
		||||
#define CONTROL_WRITE_MINIMUM 32
 | 
			
		||||
 | 
			
		||||
/* Flags to ignore client. */
 | 
			
		||||
#define CONTROL_IGNORE_FLAGS \
 | 
			
		||||
	(CLIENT_CONTROL_NOOUTPUT| \
 | 
			
		||||
	 CLIENT_UNATTACHEDFLAGS)
 | 
			
		||||
 | 
			
		||||
/* Compare client panes. */
 | 
			
		||||
static int
 | 
			
		||||
control_offset_cmp(struct control_offset *co1, struct control_offset *co2)
 | 
			
		||||
control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2)
 | 
			
		||||
{
 | 
			
		||||
	if (co1->pane < co2->pane)
 | 
			
		||||
	if (cp1->pane < cp2->pane)
 | 
			
		||||
		return (-1);
 | 
			
		||||
	if (co1->pane > co2->pane)
 | 
			
		||||
	if (cp1->pane > cp2->pane)
 | 
			
		||||
		return (1);
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
RB_GENERATE_STATIC(control_offsets, control_offset, entry, control_offset_cmp);
 | 
			
		||||
RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp);
 | 
			
		||||
 | 
			
		||||
/* Free a block. */
 | 
			
		||||
static void
 | 
			
		||||
control_free_block(struct control_state *cs, struct control_block *cb)
 | 
			
		||||
{
 | 
			
		||||
	free(cb->line);
 | 
			
		||||
	TAILQ_REMOVE(&cs->all_blocks, cb, all_entry);
 | 
			
		||||
	free(cb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get pane offsets for this client. */
 | 
			
		||||
static struct control_offset *
 | 
			
		||||
control_get_offset(struct client *c, struct window_pane *wp)
 | 
			
		||||
static struct control_pane *
 | 
			
		||||
control_get_pane(struct client *c, struct window_pane *wp)
 | 
			
		||||
{
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
	struct control_offset	 co = { .pane = wp->id };
 | 
			
		||||
	struct control_pane	 cp = { .pane = wp->id };
 | 
			
		||||
 | 
			
		||||
	return (RB_FIND(control_offsets, &cs->offsets, &co));
 | 
			
		||||
	return (RB_FIND(control_panes, &cs->panes, &cp));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add pane offsets for this client. */
 | 
			
		||||
static struct control_offset *
 | 
			
		||||
control_add_offset(struct client *c, struct window_pane *wp)
 | 
			
		||||
static struct control_pane *
 | 
			
		||||
control_add_pane(struct client *c, struct window_pane *wp)
 | 
			
		||||
{
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
	struct control_offset	*co;
 | 
			
		||||
	struct control_pane	*cp;
 | 
			
		||||
 | 
			
		||||
	co = control_get_offset(c, wp);
 | 
			
		||||
	if (co != NULL)
 | 
			
		||||
		return (co);
 | 
			
		||||
	cp = control_get_pane(c, wp);
 | 
			
		||||
	if (cp != NULL)
 | 
			
		||||
		return (cp);
 | 
			
		||||
 | 
			
		||||
	co = xcalloc(1, sizeof *co);
 | 
			
		||||
	co->pane = wp->id;
 | 
			
		||||
	RB_INSERT(control_offsets, &cs->offsets, co);
 | 
			
		||||
	memcpy(&co->offset, &wp->offset, sizeof co->offset);
 | 
			
		||||
	return (co);
 | 
			
		||||
	cp = xcalloc(1, sizeof *cp);
 | 
			
		||||
	cp->pane = wp->id;
 | 
			
		||||
	RB_INSERT(control_panes, &cs->panes, cp);
 | 
			
		||||
 | 
			
		||||
	memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
 | 
			
		||||
	memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
 | 
			
		||||
	TAILQ_INIT(&cp->blocks);
 | 
			
		||||
 | 
			
		||||
	return (cp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Free control offsets. */
 | 
			
		||||
/* Reset control offsets. */
 | 
			
		||||
void
 | 
			
		||||
control_free_offsets(struct client *c)
 | 
			
		||||
control_reset_offsets(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
	struct control_offset	*co, *co1;
 | 
			
		||||
	struct control_pane	*cp, *cp1;
 | 
			
		||||
 | 
			
		||||
	RB_FOREACH_SAFE(co, control_offsets, &cs->offsets, co1) {
 | 
			
		||||
		RB_REMOVE(control_offsets, &cs->offsets, co);
 | 
			
		||||
		free(co);
 | 
			
		||||
	RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) {
 | 
			
		||||
		RB_REMOVE(control_panes, &cs->panes, cp);
 | 
			
		||||
		free(cp);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	TAILQ_INIT(&cs->pending_list);
 | 
			
		||||
	cs->pending_count = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get offsets for client. */
 | 
			
		||||
struct window_pane_offset *
 | 
			
		||||
control_pane_offset(struct client *c, struct window_pane *wp, int *off)
 | 
			
		||||
{
 | 
			
		||||
	struct control_offset	*co;
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
	struct control_pane	*cp;
 | 
			
		||||
 | 
			
		||||
	if (c->flags & CLIENT_CONTROL_NOOUTPUT) {
 | 
			
		||||
		*off = 0;
 | 
			
		||||
		return (NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	co = control_get_offset(c, wp);
 | 
			
		||||
	if (co == NULL) {
 | 
			
		||||
	cp = control_get_pane(c, wp);
 | 
			
		||||
	if (cp == NULL) {
 | 
			
		||||
		*off = 0;
 | 
			
		||||
		return (NULL);
 | 
			
		||||
	}
 | 
			
		||||
	if (co->flags & CONTROL_OFFSET_OFF) {
 | 
			
		||||
	if (cp->flags & CONTROL_PANE_OFF) {
 | 
			
		||||
		*off = 1;
 | 
			
		||||
		return (NULL);
 | 
			
		||||
	}
 | 
			
		||||
	return (&co->offset);
 | 
			
		||||
	*off = (EVBUFFER_LENGTH(cs->write_event->output) >= CONTROL_BUFFER_LOW);
 | 
			
		||||
	return (&cp->offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Set pane as on. */
 | 
			
		||||
void
 | 
			
		||||
control_set_pane_on(struct client *c, struct window_pane *wp)
 | 
			
		||||
{
 | 
			
		||||
	struct control_offset	*co;
 | 
			
		||||
	struct control_pane	*cp;
 | 
			
		||||
 | 
			
		||||
	co = control_get_offset(c, wp);
 | 
			
		||||
	if (co != NULL) {
 | 
			
		||||
		co->flags &= ~CONTROL_OFFSET_OFF;
 | 
			
		||||
		memcpy(&co->offset, &wp->offset, sizeof co->offset);
 | 
			
		||||
	cp = control_get_pane(c, wp);
 | 
			
		||||
	if (cp != NULL) {
 | 
			
		||||
		cp->flags &= ~CONTROL_PANE_OFF;
 | 
			
		||||
		memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
 | 
			
		||||
		memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -140,10 +211,27 @@ control_set_pane_on(struct client *c, struct window_pane *wp)
 | 
			
		||||
void
 | 
			
		||||
control_set_pane_off(struct client *c, struct window_pane *wp)
 | 
			
		||||
{
 | 
			
		||||
	struct control_offset	*co;
 | 
			
		||||
	struct control_pane	*cp;
 | 
			
		||||
 | 
			
		||||
	co = control_add_offset(c, wp);
 | 
			
		||||
	co->flags |= CONTROL_OFFSET_OFF;
 | 
			
		||||
	cp = control_add_pane(c, wp);
 | 
			
		||||
	cp->flags |= CONTROL_PANE_OFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write a line. */
 | 
			
		||||
static void
 | 
			
		||||
control_vwrite(struct client *c, const char *fmt, va_list ap)
 | 
			
		||||
{
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
	char			*s;
 | 
			
		||||
 | 
			
		||||
	xvasprintf(&s, fmt, ap);
 | 
			
		||||
	log_debug("%s: %s: writing line: %s", __func__, c->name, s);
 | 
			
		||||
 | 
			
		||||
	bufferevent_write(cs->write_event, s, strlen(s));
 | 
			
		||||
	bufferevent_write(cs->write_event, "\n", 1);
 | 
			
		||||
 | 
			
		||||
	bufferevent_enable(cs->write_event, EV_WRITE);
 | 
			
		||||
	free(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write a line. */
 | 
			
		||||
@@ -151,16 +239,25 @@ void
 | 
			
		||||
control_write(struct client *c, const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
	struct control_block	*cb;
 | 
			
		||||
	va_list			 ap;
 | 
			
		||||
	char			*s;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	xvasprintf(&s, fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
 | 
			
		||||
	bufferevent_write(cs->write_event, s, strlen(s));
 | 
			
		||||
	bufferevent_write(cs->write_event, "\n", 1);
 | 
			
		||||
	free(s);
 | 
			
		||||
	if (TAILQ_EMPTY(&cs->all_blocks)) {
 | 
			
		||||
		control_vwrite(c, fmt, ap);
 | 
			
		||||
		va_end(ap);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cb = xcalloc(1, sizeof *cb);
 | 
			
		||||
	xvasprintf(&cb->line, fmt, ap);
 | 
			
		||||
	TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
 | 
			
		||||
 | 
			
		||||
	log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line);
 | 
			
		||||
	bufferevent_enable(cs->write_event, EV_WRITE);
 | 
			
		||||
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write output from a pane. */
 | 
			
		||||
@@ -168,42 +265,50 @@ void
 | 
			
		||||
control_write_output(struct client *c, struct window_pane *wp)
 | 
			
		||||
{
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
	struct control_offset	*co;
 | 
			
		||||
	struct evbuffer		*message;
 | 
			
		||||
	u_char			*new_data;
 | 
			
		||||
	size_t			 new_size, i;
 | 
			
		||||
	struct control_pane	*cp;
 | 
			
		||||
	struct control_block	*cb;
 | 
			
		||||
	size_t			 new_size;
 | 
			
		||||
 | 
			
		||||
	if (c->flags & CLIENT_CONTROL_NOOUTPUT)
 | 
			
		||||
		return;
 | 
			
		||||
	if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	co = control_add_offset(c, wp);
 | 
			
		||||
	if (co->flags & CONTROL_OFFSET_OFF) {
 | 
			
		||||
		window_pane_update_used_data(wp, &co->offset, SIZE_MAX, 1);
 | 
			
		||||
	if (c->flags & CONTROL_IGNORE_FLAGS) {
 | 
			
		||||
		cp = control_get_pane(c, wp);
 | 
			
		||||
		if (cp != NULL)
 | 
			
		||||
			goto ignore;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	new_data = window_pane_get_new_data(wp, &co->offset, &new_size);
 | 
			
		||||
	cp = control_add_pane(c, wp);
 | 
			
		||||
	if (cp->flags & CONTROL_PANE_OFF)
 | 
			
		||||
		goto ignore;
 | 
			
		||||
 | 
			
		||||
	window_pane_get_new_data(wp, &cp->queued, &new_size);
 | 
			
		||||
	if (new_size == 0)
 | 
			
		||||
		return;
 | 
			
		||||
	window_pane_update_used_data(wp, &cp->queued, new_size);
 | 
			
		||||
 | 
			
		||||
	message = evbuffer_new();
 | 
			
		||||
	if (message == NULL)
 | 
			
		||||
		fatalx("out of memory");
 | 
			
		||||
	evbuffer_add_printf(message, "%%output %%%u ", wp->id);
 | 
			
		||||
	cb = xcalloc(1, sizeof *cb);
 | 
			
		||||
	cb->size = new_size;
 | 
			
		||||
	TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < new_size; i++) {
 | 
			
		||||
		if (new_data[i] < ' ' || new_data[i] == '\\')
 | 
			
		||||
			evbuffer_add_printf(message, "\\%03o", new_data[i]);
 | 
			
		||||
		else
 | 
			
		||||
			evbuffer_add_printf(message, "%c", new_data[i]);
 | 
			
		||||
	TAILQ_INSERT_TAIL(&cp->blocks, cb, entry);
 | 
			
		||||
	log_debug("%s: %s: new output block of %zu for %%%u", __func__, c->name,
 | 
			
		||||
	    cb->size, wp->id);
 | 
			
		||||
 | 
			
		||||
	if (!cp->pending_flag) {
 | 
			
		||||
		log_debug("%s: %s: %%%u now pending", __func__, c->name,
 | 
			
		||||
		    wp->id);
 | 
			
		||||
		TAILQ_INSERT_TAIL(&cs->pending_list, cp, pending_entry);
 | 
			
		||||
		cp->pending_flag = 1;
 | 
			
		||||
		cs->pending_count++;
 | 
			
		||||
	}
 | 
			
		||||
	evbuffer_add(message, "\n", 1);
 | 
			
		||||
	bufferevent_enable(cs->write_event, EV_WRITE);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
	bufferevent_write_buffer(cs->write_event, message);
 | 
			
		||||
	evbuffer_free(message);
 | 
			
		||||
 | 
			
		||||
	window_pane_update_used_data(wp, &co->offset, new_size, 1);
 | 
			
		||||
ignore:
 | 
			
		||||
	log_debug("%s: %s: ignoring pane %%%u", __func__, c->name, wp->id);
 | 
			
		||||
	window_pane_update_used_data(wp, &cp->offset, SIZE_MAX);
 | 
			
		||||
	window_pane_update_used_data(wp, &cp->queued, SIZE_MAX);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Control client error callback. */
 | 
			
		||||
@@ -246,8 +351,8 @@ control_read_callback(__unused struct bufferevent *bufev, void *data)
 | 
			
		||||
		line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
 | 
			
		||||
		if (line == NULL)
 | 
			
		||||
			break;
 | 
			
		||||
		log_debug("%s: %s", __func__, line);
 | 
			
		||||
		if (*line == '\0') { /* empty line exit */
 | 
			
		||||
		log_debug("%s: %s: %s", __func__, c->name, line);
 | 
			
		||||
		if (*line == '\0') { /* empty line detach */
 | 
			
		||||
			free(line);
 | 
			
		||||
			c->flags |= CLIENT_EXIT;
 | 
			
		||||
			break;
 | 
			
		||||
@@ -263,6 +368,168 @@ control_read_callback(__unused struct bufferevent *bufev, void *data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Does this control client have outstanding data to write? */
 | 
			
		||||
int
 | 
			
		||||
control_all_done(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
 | 
			
		||||
	if (!TAILQ_EMPTY(&cs->all_blocks))
 | 
			
		||||
		return (0);
 | 
			
		||||
	return (EVBUFFER_LENGTH(cs->write_event->output) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Flush all blocks until output. */
 | 
			
		||||
static void
 | 
			
		||||
control_flush_all_blocks(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
	struct control_block	*cb, *cb1;
 | 
			
		||||
 | 
			
		||||
	TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) {
 | 
			
		||||
		if (cb->size != 0)
 | 
			
		||||
			break;
 | 
			
		||||
		log_debug("%s: %s: flushing line: %s", __func__, c->name,
 | 
			
		||||
		    cb->line);
 | 
			
		||||
 | 
			
		||||
		bufferevent_write(cs->write_event, cb->line, strlen(cb->line));
 | 
			
		||||
		bufferevent_write(cs->write_event, "\n", 1);
 | 
			
		||||
		control_free_block(cs, cb);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Append data to buffer. */
 | 
			
		||||
static struct evbuffer *
 | 
			
		||||
control_append_data(struct control_pane *cp, struct evbuffer *message,
 | 
			
		||||
    struct window_pane *wp, size_t size)
 | 
			
		||||
{
 | 
			
		||||
	u_char	*new_data;
 | 
			
		||||
	size_t	 new_size;
 | 
			
		||||
	u_int	 i;
 | 
			
		||||
 | 
			
		||||
	if (message == NULL) {
 | 
			
		||||
		message = evbuffer_new();
 | 
			
		||||
		if (message == NULL)
 | 
			
		||||
			fatalx("out of memory");
 | 
			
		||||
		evbuffer_add_printf(message, "%%output %%%u ", wp->id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	new_data = window_pane_get_new_data(wp, &cp->offset, &new_size);
 | 
			
		||||
	if (new_size < size)
 | 
			
		||||
		fatalx("not enough data: %zu < %zu", new_size, size);
 | 
			
		||||
	for (i = 0; i < size; i++) {
 | 
			
		||||
		if (new_data[i] < ' ' || new_data[i] == '\\')
 | 
			
		||||
			evbuffer_add_printf(message, "\\%03o", new_data[i]);
 | 
			
		||||
		else
 | 
			
		||||
			evbuffer_add_printf(message, "%c", new_data[i]);
 | 
			
		||||
	}
 | 
			
		||||
	window_pane_update_used_data(wp, &cp->offset, size);
 | 
			
		||||
	return (message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write buffer. */
 | 
			
		||||
static void
 | 
			
		||||
control_write_data(struct client *c, struct evbuffer *message)
 | 
			
		||||
{
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
 | 
			
		||||
	log_debug("%s: %s: %.*s", __func__, c->name,
 | 
			
		||||
	    (int)EVBUFFER_LENGTH(message), EVBUFFER_DATA(message));
 | 
			
		||||
 | 
			
		||||
	evbuffer_add(message, "\n", 1);
 | 
			
		||||
	bufferevent_write_buffer(cs->write_event, message);
 | 
			
		||||
	evbuffer_free(message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write output to client. */
 | 
			
		||||
static int
 | 
			
		||||
control_write_pending(struct client *c, struct control_pane *cp, size_t limit)
 | 
			
		||||
{
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
	struct session		*s = c->session;
 | 
			
		||||
	struct window_pane	*wp = NULL;
 | 
			
		||||
	struct evbuffer		*message = NULL;
 | 
			
		||||
	size_t			 used = 0, size;
 | 
			
		||||
	struct control_block	*cb, *cb1;
 | 
			
		||||
 | 
			
		||||
	if (s == NULL ||
 | 
			
		||||
	    (wp = window_pane_find_by_id(cp->pane)) == NULL ||
 | 
			
		||||
	    winlink_find_by_window(&s->windows, wp->window) == NULL) {
 | 
			
		||||
		TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1)
 | 
			
		||||
			control_free_block(cs, cb);
 | 
			
		||||
		control_flush_all_blocks(c);
 | 
			
		||||
		return (0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while (used != limit && !TAILQ_EMPTY(&cp->blocks)) {
 | 
			
		||||
		cb = TAILQ_FIRST(&cp->blocks);
 | 
			
		||||
		log_debug("%s: %s: output block %zu for %%%u (used %zu/%zu)",
 | 
			
		||||
		    __func__, c->name, cb->size, cp->pane, used, limit);
 | 
			
		||||
 | 
			
		||||
		size = cb->size;
 | 
			
		||||
		if (size > limit - used)
 | 
			
		||||
			size = limit - used;
 | 
			
		||||
		used += size;
 | 
			
		||||
 | 
			
		||||
		message = control_append_data(cp, message, wp, size);
 | 
			
		||||
 | 
			
		||||
		cb->size -= size;
 | 
			
		||||
		if (cb->size == 0) {
 | 
			
		||||
			TAILQ_REMOVE(&cp->blocks, cb, entry);
 | 
			
		||||
			control_free_block(cs, cb);
 | 
			
		||||
 | 
			
		||||
			cb = TAILQ_FIRST(&cs->all_blocks);
 | 
			
		||||
			if (cb != NULL && cb->size == 0) {
 | 
			
		||||
				if (wp != NULL && message != NULL) {
 | 
			
		||||
					control_write_data(c, message);
 | 
			
		||||
					message = NULL;
 | 
			
		||||
				}
 | 
			
		||||
				control_flush_all_blocks(c);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (message != NULL)
 | 
			
		||||
		control_write_data(c, message);
 | 
			
		||||
	return (!TAILQ_EMPTY(&cp->blocks));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Control client write callback. */
 | 
			
		||||
static void
 | 
			
		||||
control_write_callback(__unused struct bufferevent *bufev, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct client		*c = data;
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
	struct control_pane	*cp, *cp1;
 | 
			
		||||
	struct evbuffer		*evb = cs->write_event->output;
 | 
			
		||||
	size_t			 space, limit;
 | 
			
		||||
 | 
			
		||||
	control_flush_all_blocks(c);
 | 
			
		||||
 | 
			
		||||
	while (EVBUFFER_LENGTH(evb) < CONTROL_BUFFER_HIGH) {
 | 
			
		||||
		if (cs->pending_count == 0)
 | 
			
		||||
			break;
 | 
			
		||||
		space = CONTROL_BUFFER_HIGH - EVBUFFER_LENGTH(evb);
 | 
			
		||||
		log_debug("%s: %s: %zu bytes available, %u panes", __func__,
 | 
			
		||||
		    c->name, space, cs->pending_count);
 | 
			
		||||
 | 
			
		||||
		limit = (space / cs->pending_count / 3); /* 3 bytes for \xxx */
 | 
			
		||||
		if (limit < CONTROL_WRITE_MINIMUM)
 | 
			
		||||
			limit = CONTROL_WRITE_MINIMUM;
 | 
			
		||||
 | 
			
		||||
		TAILQ_FOREACH_SAFE(cp, &cs->pending_list, pending_entry, cp1) {
 | 
			
		||||
			if (EVBUFFER_LENGTH(evb) >= CONTROL_BUFFER_HIGH)
 | 
			
		||||
				break;
 | 
			
		||||
			if (control_write_pending(c, cp, limit))
 | 
			
		||||
				continue;
 | 
			
		||||
			TAILQ_REMOVE(&cs->pending_list, cp, pending_entry);
 | 
			
		||||
			cp->pending_flag = 0;
 | 
			
		||||
			cs->pending_count--;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (EVBUFFER_LENGTH(evb) == 0)
 | 
			
		||||
		bufferevent_disable(cs->write_event, EV_WRITE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize for control mode. */
 | 
			
		||||
void
 | 
			
		||||
control_start(struct client *c)
 | 
			
		||||
@@ -277,22 +544,43 @@ control_start(struct client *c)
 | 
			
		||||
	setblocking(c->fd, 0);
 | 
			
		||||
 | 
			
		||||
	cs = c->control_state = xcalloc(1, sizeof *cs);
 | 
			
		||||
	RB_INIT(&cs->offsets);
 | 
			
		||||
	RB_INIT(&cs->panes);
 | 
			
		||||
	TAILQ_INIT(&cs->pending_list);
 | 
			
		||||
	TAILQ_INIT(&cs->all_blocks);
 | 
			
		||||
 | 
			
		||||
	cs->read_event = bufferevent_new(c->fd, control_read_callback, NULL,
 | 
			
		||||
	    control_error_callback, c);
 | 
			
		||||
	cs->read_event = bufferevent_new(c->fd, control_read_callback,
 | 
			
		||||
	    control_write_callback, control_error_callback, c);
 | 
			
		||||
	bufferevent_enable(cs->read_event, EV_READ);
 | 
			
		||||
 | 
			
		||||
	if (c->flags & CLIENT_CONTROLCONTROL)
 | 
			
		||||
		cs->write_event = cs->read_event;
 | 
			
		||||
	else {
 | 
			
		||||
		cs->write_event = bufferevent_new(c->out_fd, NULL, NULL,
 | 
			
		||||
		    control_error_callback, c);
 | 
			
		||||
		cs->write_event = bufferevent_new(c->out_fd, NULL,
 | 
			
		||||
		    control_write_callback, control_error_callback, c);
 | 
			
		||||
	}
 | 
			
		||||
	bufferevent_enable(cs->write_event, EV_WRITE);
 | 
			
		||||
	bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
 | 
			
		||||
	    0);
 | 
			
		||||
 | 
			
		||||
	if (c->flags & CLIENT_CONTROLCONTROL)
 | 
			
		||||
		control_write(c, "\033P1000p");
 | 
			
		||||
	if (c->flags & CLIENT_CONTROLCONTROL) {
 | 
			
		||||
		bufferevent_write(cs->write_event, "\033P1000p", 7);
 | 
			
		||||
		bufferevent_enable(cs->write_event, EV_WRITE);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Flush all output for a client that is detaching. */
 | 
			
		||||
void
 | 
			
		||||
control_flush(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
	struct control_pane	*cp;
 | 
			
		||||
	struct control_block	*cb, *cb1;
 | 
			
		||||
 | 
			
		||||
	RB_FOREACH(cp, control_panes, &cs->panes) {
 | 
			
		||||
		TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) {
 | 
			
		||||
			TAILQ_REMOVE(&cp->blocks, cb, entry);
 | 
			
		||||
			control_free_block(cs, cb);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Stop control mode. */
 | 
			
		||||
@@ -300,11 +588,15 @@ void
 | 
			
		||||
control_stop(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	struct control_state	*cs = c->control_state;
 | 
			
		||||
	struct control_block	*cb, *cb1;
 | 
			
		||||
 | 
			
		||||
	if (~c->flags & CLIENT_CONTROLCONTROL)
 | 
			
		||||
		bufferevent_free(cs->write_event);
 | 
			
		||||
	bufferevent_free(cs->read_event);
 | 
			
		||||
 | 
			
		||||
	control_free_offsets(c);
 | 
			
		||||
	TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
 | 
			
		||||
		control_free_block(cs, cb);
 | 
			
		||||
	control_reset_offsets(c);
 | 
			
		||||
 | 
			
		||||
	free(cs);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								input.c
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								input.c
									
									
									
									
									
								
							@@ -947,7 +947,7 @@ input_parse_pane(struct window_pane *wp)
 | 
			
		||||
 | 
			
		||||
	new_data = window_pane_get_new_data(wp, &wp->offset, &new_size);
 | 
			
		||||
	input_parse_buffer(wp, new_data, new_size);
 | 
			
		||||
	window_pane_update_used_data(wp, &wp->offset, new_size, 1);
 | 
			
		||||
	window_pane_update_used_data(wp, &wp->offset, new_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Parse given input. */
 | 
			
		||||
 
 | 
			
		||||
@@ -56,6 +56,9 @@ static void	server_client_dispatch_read_data(struct client *,
 | 
			
		||||
static void	server_client_dispatch_read_done(struct client *,
 | 
			
		||||
		    struct imsg *);
 | 
			
		||||
 | 
			
		||||
/* Maximum data allowed to be held for a pane for a control client. */
 | 
			
		||||
#define SERVER_CLIENT_PANE_LIMIT 16777216
 | 
			
		||||
 | 
			
		||||
/* Compare client windows. */
 | 
			
		||||
static int
 | 
			
		||||
server_client_window_cmp(struct client_window *cw1,
 | 
			
		||||
@@ -78,7 +81,7 @@ server_client_how_many(void)
 | 
			
		||||
 | 
			
		||||
	n = 0;
 | 
			
		||||
	TAILQ_FOREACH(c, &clients, entry) {
 | 
			
		||||
		if (c->session != NULL && (~c->flags & CLIENT_DETACHING))
 | 
			
		||||
		if (c->session != NULL && (~c->flags & CLIENT_UNATTACHEDFLAGS))
 | 
			
		||||
			n++;
 | 
			
		||||
	}
 | 
			
		||||
	return (n);
 | 
			
		||||
@@ -384,7 +387,7 @@ server_client_suspend(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	struct session	*s = c->session;
 | 
			
		||||
 | 
			
		||||
	if (s == NULL || (c->flags & CLIENT_DETACHING))
 | 
			
		||||
	if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	tty_stop_tty(&c->tty);
 | 
			
		||||
@@ -398,12 +401,14 @@ server_client_detach(struct client *c, enum msgtype msgtype)
 | 
			
		||||
{
 | 
			
		||||
	struct session	*s = c->session;
 | 
			
		||||
 | 
			
		||||
	if (s == NULL || (c->flags & CLIENT_DETACHING))
 | 
			
		||||
	if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	c->flags |= CLIENT_DETACHING;
 | 
			
		||||
	notify_client("client-detached", c);
 | 
			
		||||
	proc_send(c->peer, msgtype, -1, s->name, strlen(s->name) + 1);
 | 
			
		||||
	c->flags |= CLIENT_EXIT;
 | 
			
		||||
 | 
			
		||||
	c->exit_type = CLIENT_EXIT_DETACH;
 | 
			
		||||
	c->exit_msgtype = msgtype;
 | 
			
		||||
	c->exit_session = xstrdup(s->name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Execute command to replace a client. */
 | 
			
		||||
@@ -1505,12 +1510,12 @@ server_client_check_pane_buffer(struct window_pane *wp)
 | 
			
		||||
	u_int				 attached_clients = 0;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Work out the minimum acknowledged size. This is the most that can be
 | 
			
		||||
	 * removed from the buffer.
 | 
			
		||||
	 * Work out the minimum used size. This is the most that can be removed
 | 
			
		||||
	 * from the buffer.
 | 
			
		||||
	 */
 | 
			
		||||
	minimum = wp->offset.acknowledged;
 | 
			
		||||
	if (wp->pipe_fd != -1 && wp->pipe_offset.acknowledged < minimum)
 | 
			
		||||
		minimum = wp->pipe_offset.acknowledged;
 | 
			
		||||
	minimum = wp->offset.used;
 | 
			
		||||
	if (wp->pipe_fd != -1 && wp->pipe_offset.used < minimum)
 | 
			
		||||
		minimum = wp->pipe_offset.used;
 | 
			
		||||
	TAILQ_FOREACH(c, &clients, entry) {
 | 
			
		||||
		if (c->session == NULL)
 | 
			
		||||
			continue;
 | 
			
		||||
@@ -1528,11 +1533,13 @@ server_client_check_pane_buffer(struct window_pane *wp)
 | 
			
		||||
		if (!flag)
 | 
			
		||||
			off = 0;
 | 
			
		||||
 | 
			
		||||
		log_debug("%s: %s has %zu bytes used, %zu bytes acknowledged "
 | 
			
		||||
		    "for %%%u", __func__, c->name, wpo->used, wpo->acknowledged,
 | 
			
		||||
		    wp->id);
 | 
			
		||||
		if (wpo->acknowledged < minimum)
 | 
			
		||||
			minimum = wpo->acknowledged;
 | 
			
		||||
		log_debug("%s: %s has %zu bytes used for %%%u", __func__,
 | 
			
		||||
		    c->name, wpo->used - wp->base_offset, wp->id);
 | 
			
		||||
		if (wpo->used - wp->base_offset > SERVER_CLIENT_PANE_LIMIT) {
 | 
			
		||||
			control_flush(c);
 | 
			
		||||
			c->flags |= CLIENT_EXIT;
 | 
			
		||||
		} else if (wpo->used < minimum)
 | 
			
		||||
			minimum = wpo->used;
 | 
			
		||||
	}
 | 
			
		||||
	if (attached_clients == 0)
 | 
			
		||||
		off = 0;
 | 
			
		||||
@@ -1541,8 +1548,8 @@ server_client_check_pane_buffer(struct window_pane *wp)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	/* Drain the buffer. */
 | 
			
		||||
	log_debug("%s: %%%u has %zu minimum (of %zu) bytes acknowledged",
 | 
			
		||||
	    __func__, wp->id, minimum, EVBUFFER_LENGTH(evb));
 | 
			
		||||
	log_debug("%s: %%%u has %zu minimum (of %zu) bytes used", __func__,
 | 
			
		||||
	    wp->id, minimum, EVBUFFER_LENGTH(evb));
 | 
			
		||||
	evbuffer_drain(evb, minimum);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
@@ -1551,20 +1558,15 @@ server_client_check_pane_buffer(struct window_pane *wp)
 | 
			
		||||
	 */
 | 
			
		||||
	if (wp->base_offset > SIZE_MAX - minimum) {
 | 
			
		||||
		log_debug("%s: %%%u base offset has wrapped", __func__, wp->id);
 | 
			
		||||
		wp->offset.acknowledged -= wp->base_offset;
 | 
			
		||||
		wp->offset.used -= wp->base_offset;
 | 
			
		||||
		if (wp->pipe_fd != -1) {
 | 
			
		||||
			wp->pipe_offset.acknowledged -= wp->base_offset;
 | 
			
		||||
		if (wp->pipe_fd != -1)
 | 
			
		||||
			wp->pipe_offset.used -= wp->base_offset;
 | 
			
		||||
		}
 | 
			
		||||
		TAILQ_FOREACH(c, &clients, entry) {
 | 
			
		||||
			if (c->session == NULL || (~c->flags & CLIENT_CONTROL))
 | 
			
		||||
				continue;
 | 
			
		||||
			wpo = control_pane_offset(c, wp, &flag);
 | 
			
		||||
			if (wpo != NULL && !flag) {
 | 
			
		||||
				wpo->acknowledged -= wp->base_offset;
 | 
			
		||||
			if (wpo != NULL && !flag)
 | 
			
		||||
				wpo->used -= wp->base_offset;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		wp->base_offset = minimum;
 | 
			
		||||
	} else
 | 
			
		||||
@@ -1577,6 +1579,7 @@ out:
 | 
			
		||||
	 * clients, all of which are control clients which are not able to
 | 
			
		||||
	 * accept any more data.
 | 
			
		||||
	 */
 | 
			
		||||
	log_debug("%s: pane %%%u is %s", __func__, wp->id, off ? "off" : "on");
 | 
			
		||||
	if (off)
 | 
			
		||||
		bufferevent_disable(wp->event, EV_READ);
 | 
			
		||||
	else
 | 
			
		||||
@@ -1768,12 +1771,16 @@ static void
 | 
			
		||||
server_client_check_exit(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	*cf;
 | 
			
		||||
	const char		*name = c->exit_session;
 | 
			
		||||
 | 
			
		||||
	if (~c->flags & CLIENT_EXIT)
 | 
			
		||||
		return;
 | 
			
		||||
	if (c->flags & CLIENT_EXITED)
 | 
			
		||||
	if ((c->flags & CLIENT_EXITED) || (~c->flags & CLIENT_EXIT))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (c->flags & CLIENT_CONTROL) {
 | 
			
		||||
		control_flush(c);
 | 
			
		||||
		if (!control_all_done(c))
 | 
			
		||||
			return;
 | 
			
		||||
	}
 | 
			
		||||
	RB_FOREACH(cf, client_files, &c->files) {
 | 
			
		||||
		if (EVBUFFER_LENGTH(cf->buffer) != 0)
 | 
			
		||||
			return;
 | 
			
		||||
@@ -1781,8 +1788,20 @@ server_client_check_exit(struct client *c)
 | 
			
		||||
 | 
			
		||||
	if (c->flags & CLIENT_ATTACHED)
 | 
			
		||||
		notify_client("client-detached", c);
 | 
			
		||||
	proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval);
 | 
			
		||||
	c->flags |= CLIENT_EXITED;
 | 
			
		||||
 | 
			
		||||
	switch (c->exit_type) {
 | 
			
		||||
	case CLIENT_EXIT_RETURN:
 | 
			
		||||
		proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval);
 | 
			
		||||
		break;
 | 
			
		||||
	case CLIENT_EXIT_SHUTDOWN:
 | 
			
		||||
		proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0);
 | 
			
		||||
		break;
 | 
			
		||||
	case CLIENT_EXIT_DETACH:
 | 
			
		||||
		proc_send(c->peer, c->exit_msgtype, -1, name, strlen(name) + 1);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	free(c->exit_session);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Redraw timer callback. */
 | 
			
		||||
@@ -1994,7 +2013,6 @@ server_client_dispatch(struct imsg *imsg, void *arg)
 | 
			
		||||
	case MSG_EXITING:
 | 
			
		||||
		if (datalen != 0)
 | 
			
		||||
			fatalx("bad MSG_EXITING size");
 | 
			
		||||
 | 
			
		||||
		c->session = NULL;
 | 
			
		||||
		tty_close(&c->tty);
 | 
			
		||||
		proc_send(c->peer, MSG_EXITED, -1, NULL, 0);
 | 
			
		||||
@@ -2048,7 +2066,7 @@ server_client_command_done(struct cmdq_item *item, __unused void *data)
 | 
			
		||||
 | 
			
		||||
	if (~c->flags & CLIENT_ATTACHED)
 | 
			
		||||
		c->flags |= CLIENT_EXIT;
 | 
			
		||||
	else if (~c->flags & CLIENT_DETACHING)
 | 
			
		||||
	else if (~c->flags & CLIENT_EXIT)
 | 
			
		||||
		tty_send_requests(&c->tty);
 | 
			
		||||
	return (CMD_RETURN_NORMAL);
 | 
			
		||||
}
 | 
			
		||||
@@ -2374,7 +2392,7 @@ server_client_set_flags(struct client *c, const char *flags)
 | 
			
		||||
		else
 | 
			
		||||
			c->flags |= flag;
 | 
			
		||||
		if (flag == CLIENT_CONTROL_NOOUTPUT)
 | 
			
		||||
			control_free_offsets(c);
 | 
			
		||||
			control_reset_offsets(c);
 | 
			
		||||
	}
 | 
			
		||||
	free(copy);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								server.c
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								server.c
									
									
									
									
									
								
							@@ -294,9 +294,8 @@ server_send_exit(void)
 | 
			
		||||
		if (c->flags & CLIENT_SUSPENDED)
 | 
			
		||||
			server_client_lost(c);
 | 
			
		||||
		else {
 | 
			
		||||
			if (c->flags & CLIENT_ATTACHED)
 | 
			
		||||
				notify_client("client-detached", c);
 | 
			
		||||
			proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0);
 | 
			
		||||
			c->flags |= CLIENT_EXIT;
 | 
			
		||||
			c->exit_type = CLIENT_EXIT_SHUTDOWN;
 | 
			
		||||
		}
 | 
			
		||||
		c->session = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								tmux.h
									
									
									
									
									
								
							@@ -87,9 +87,6 @@ struct winlink;
 | 
			
		||||
/* Automatic name refresh interval, in microseconds. Must be < 1 second. */
 | 
			
		||||
#define NAME_INTERVAL 500000
 | 
			
		||||
 | 
			
		||||
/* Maximum size of data to hold from a pane. */
 | 
			
		||||
#define READ_SIZE 8192
 | 
			
		||||
 | 
			
		||||
/* Default pixel cell sizes. */
 | 
			
		||||
#define DEFAULT_XPIXEL 16
 | 
			
		||||
#define DEFAULT_YPIXEL 32
 | 
			
		||||
@@ -917,7 +914,6 @@ struct window_mode_entry {
 | 
			
		||||
/* Offsets into pane buffer. */
 | 
			
		||||
struct window_pane_offset {
 | 
			
		||||
	size_t	used;
 | 
			
		||||
	size_t	acknowledged;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Child window structure. */
 | 
			
		||||
@@ -1629,7 +1625,7 @@ struct client {
 | 
			
		||||
#define CLIENT_DEAD 0x200
 | 
			
		||||
#define CLIENT_REDRAWBORDERS 0x400
 | 
			
		||||
#define CLIENT_READONLY 0x800
 | 
			
		||||
#define CLIENT_DETACHING 0x1000
 | 
			
		||||
/* 0x1000 unused */
 | 
			
		||||
#define CLIENT_CONTROL 0x2000
 | 
			
		||||
#define CLIENT_CONTROLCONTROL 0x4000
 | 
			
		||||
#define CLIENT_FOCUSED 0x8000
 | 
			
		||||
@@ -1659,12 +1655,21 @@ struct client {
 | 
			
		||||
#define CLIENT_UNATTACHEDFLAGS	\
 | 
			
		||||
	(CLIENT_DEAD|		\
 | 
			
		||||
	 CLIENT_SUSPENDED|	\
 | 
			
		||||
	 CLIENT_DETACHING)
 | 
			
		||||
	 CLIENT_EXIT)
 | 
			
		||||
#define CLIENT_NOSIZEFLAGS	\
 | 
			
		||||
	(CLIENT_DEAD|		\
 | 
			
		||||
	 CLIENT_SUSPENDED|	\
 | 
			
		||||
	 CLIENT_DETACHING)
 | 
			
		||||
	 CLIENT_EXIT)
 | 
			
		||||
	uint64_t	 flags;
 | 
			
		||||
 | 
			
		||||
	enum {
 | 
			
		||||
		CLIENT_EXIT_RETURN,
 | 
			
		||||
		CLIENT_EXIT_SHUTDOWN,
 | 
			
		||||
		CLIENT_EXIT_DETACH
 | 
			
		||||
	}		 exit_type;
 | 
			
		||||
	enum msgtype	 exit_msgtype;
 | 
			
		||||
	char		*exit_session;
 | 
			
		||||
 | 
			
		||||
	struct key_table *keytable;
 | 
			
		||||
 | 
			
		||||
	uint64_t	 redraw_panes;
 | 
			
		||||
@@ -2714,8 +2719,6 @@ int		 window_pane_start_input(struct window_pane *,
 | 
			
		||||
void		*window_pane_get_new_data(struct window_pane *,
 | 
			
		||||
		     struct window_pane_offset *, size_t *);
 | 
			
		||||
void		 window_pane_update_used_data(struct window_pane *,
 | 
			
		||||
		     struct window_pane_offset *, size_t, int);
 | 
			
		||||
void		 window_pane_acknowledge_data(struct window_pane *,
 | 
			
		||||
		     struct window_pane_offset *, size_t);
 | 
			
		||||
 | 
			
		||||
/* layout.c */
 | 
			
		||||
@@ -2831,15 +2834,17 @@ char	*default_window_name(struct window *);
 | 
			
		||||
char	*parse_window_name(const char *);
 | 
			
		||||
 | 
			
		||||
/* control.c */
 | 
			
		||||
void	control_flush(struct client *);
 | 
			
		||||
void	control_start(struct client *);
 | 
			
		||||
void	control_stop(struct client *);
 | 
			
		||||
void	control_set_pane_on(struct client *, struct window_pane *);
 | 
			
		||||
void	control_set_pane_off(struct client *, struct window_pane *);
 | 
			
		||||
struct window_pane_offset *control_pane_offset(struct client *,
 | 
			
		||||
	   struct window_pane *, int *);
 | 
			
		||||
void	control_free_offsets(struct client *);
 | 
			
		||||
void	control_reset_offsets(struct client *);
 | 
			
		||||
void printflike(2, 3) control_write(struct client *, const char *, ...);
 | 
			
		||||
void	control_write_output(struct client *, struct window_pane *);
 | 
			
		||||
int	control_all_done(struct client *);
 | 
			
		||||
 | 
			
		||||
/* control-notify.c */
 | 
			
		||||
void	control_notify_input(struct client *, struct window_pane *,
 | 
			
		||||
 
 | 
			
		||||
@@ -166,7 +166,7 @@ window_client_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
 | 
			
		||||
	data->item_size = 0;
 | 
			
		||||
 | 
			
		||||
	TAILQ_FOREACH(c, &clients, entry) {
 | 
			
		||||
		if (c->session == NULL || (c->flags & (CLIENT_DETACHING)))
 | 
			
		||||
		if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		item = window_client_add_item(data);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								window.c
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								window.c
									
									
									
									
									
								
							@@ -954,16 +954,17 @@ window_pane_read_callback(__unused struct bufferevent *bufev, void *data)
 | 
			
		||||
		new_data = window_pane_get_new_data(wp, wpo, &new_size);
 | 
			
		||||
		if (new_size > 0) {
 | 
			
		||||
			bufferevent_write(wp->pipe_event, new_data, new_size);
 | 
			
		||||
			window_pane_update_used_data(wp, wpo, new_size, 1);
 | 
			
		||||
			window_pane_update_used_data(wp, wpo, new_size);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log_debug("%%%u has %zu bytes", wp->id, size);
 | 
			
		||||
	TAILQ_FOREACH(c, &clients, entry) {
 | 
			
		||||
		if (c->session != NULL && c->flags & CLIENT_CONTROL)
 | 
			
		||||
		if (c->session != NULL && (c->flags & CLIENT_CONTROL))
 | 
			
		||||
			control_write_output(c, wp);
 | 
			
		||||
	}
 | 
			
		||||
	input_parse_pane(wp);
 | 
			
		||||
	bufferevent_disable(wp->event, EV_READ);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -988,7 +989,6 @@ window_pane_set_event(struct window_pane *wp)
 | 
			
		||||
	    NULL, window_pane_error_callback, wp);
 | 
			
		||||
	wp->ictx = input_init(wp, wp->event);
 | 
			
		||||
 | 
			
		||||
	bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE);
 | 
			
		||||
	bufferevent_enable(wp->event, EV_READ|EV_WRITE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1569,27 +1569,11 @@ window_pane_get_new_data(struct window_pane *wp,
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
window_pane_update_used_data(struct window_pane *wp,
 | 
			
		||||
    struct window_pane_offset *wpo, size_t size, int acknowledge)
 | 
			
		||||
    struct window_pane_offset *wpo, size_t size)
 | 
			
		||||
{
 | 
			
		||||
	size_t	used = wpo->used - wp->base_offset;
 | 
			
		||||
 | 
			
		||||
	if (size > EVBUFFER_LENGTH(wp->event->input) - used)
 | 
			
		||||
		size = EVBUFFER_LENGTH(wp->event->input) - used;
 | 
			
		||||
	wpo->used += size;
 | 
			
		||||
 | 
			
		||||
	if (acknowledge)
 | 
			
		||||
		window_pane_acknowledge_data(wp, wpo, size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
window_pane_acknowledge_data(struct window_pane *wp,
 | 
			
		||||
    struct window_pane_offset *wpo, size_t size)
 | 
			
		||||
{
 | 
			
		||||
	size_t	acknowledged = wpo->acknowledged - wp->base_offset;
 | 
			
		||||
 | 
			
		||||
	if (size > EVBUFFER_LENGTH(wp->event->input) - acknowledged)
 | 
			
		||||
		size = EVBUFFER_LENGTH(wp->event->input) - acknowledged;
 | 
			
		||||
	wpo->acknowledged += size;
 | 
			
		||||
	if (wpo->acknowledged > wpo->used)
 | 
			
		||||
		wpo->acknowledged = wpo->used;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user