mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +00:00 
			
		
		
		
	Merge branch 'obsd-master'
This commit is contained in:
		
							
								
								
									
										12
									
								
								client.c
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								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); | 		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. */ | 	/* Print the exit message, if any, and exit. */ | ||||||
| 	if (client_attached) { | 	if (client_attached) { | ||||||
| 		if (client_exitreason != CLIENT_EXIT_NONE) | 		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(); | 		ppid = getppid(); | ||||||
| 		if (client_exittype == MSG_DETACHKILL && ppid > 1) | 		if (client_exittype == MSG_DETACHKILL && ppid > 1) | ||||||
| 			kill(ppid, SIGHUP); | 			kill(ppid, SIGHUP); | ||||||
| 	} else if (client_flags & CLIENT_CONTROLCONTROL) { | 	} else if (client_flags & CLIENT_CONTROL) { | ||||||
| 		if (client_exitreason != CLIENT_EXIT_NONE) | 		if (client_exitreason != CLIENT_EXIT_NONE) | ||||||
| 			printf("%%exit %s\n", client_exit_message()); | 			printf("%%exit %s\n", client_exit_message()); | ||||||
| 		else | 		else | ||||||
| 			printf("%%exit\n"); | 			printf("%%exit\n"); | ||||||
|  | 		if (client_flags & CLIENT_CONTROLCONTROL) { | ||||||
| 			printf("\033\\"); | 			printf("\033\\"); | ||||||
| 			tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); | 			tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); | ||||||
|  | 		} | ||||||
| 	} else if (client_exitreason != CLIENT_EXIT_NONE) | 	} else if (client_exitreason != CLIENT_EXIT_NONE) | ||||||
| 		fprintf(stderr, "%s\n", client_exit_message()); | 		fprintf(stderr, "%s\n", client_exit_message()); | ||||||
| 	setblocking(STDIN_FILENO, 1); |  | ||||||
| 	setblocking(STDOUT_FILENO, 1); |  | ||||||
| 	setblocking(STDERR_FILENO, 1); |  | ||||||
| 	return (client_exitval); | 	return (client_exitval); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -214,15 +214,20 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item) | |||||||
| 		return (CMD_RETURN_ERROR); | 		return (CMD_RETURN_ERROR); | ||||||
|  |  | ||||||
| 	if (args_has(args, 'p')) { | 	if (args_has(args, 'p')) { | ||||||
|  | 		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)) { | 			if (!file_can_print(c)) { | ||||||
| 			cmdq_error(item, "can't write output to client"); | 				cmdq_error(item, "can't write to client"); | ||||||
| 				free(buf); | 				free(buf); | ||||||
| 				return (CMD_RETURN_ERROR); | 				return (CMD_RETURN_ERROR); | ||||||
| 			} | 			} | ||||||
| 			file_print_buffer(c, buf, len); | 			file_print_buffer(c, buf, len); | ||||||
| 		if (args_has(args, 'P') && len > 0) |  | ||||||
| 			file_print(c, "\n"); | 			file_print(c, "\n"); | ||||||
| 			free(buf); | 			free(buf); | ||||||
|  | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		bufname = NULL; | 		bufname = NULL; | ||||||
| 		if (args_has(args, 'b')) | 		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 | 	 * the terminal as that calls tcsetattr() to prepare for tmux taking | ||||||
| 	 * over. | 	 * 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))) { | 		if (server_client_check_nested(cmdq_get_client(item))) { | ||||||
| 			cmdq_error(item, "sessions should be nested with care, " | 			cmdq_error(item, "sessions should be nested with care, " | ||||||
| 			    "unset $TMUX to force"); | 			    "unset $TMUX to force"); | ||||||
|   | |||||||
| @@ -780,7 +780,7 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags) | |||||||
| 	u_int		 number = item->number; | 	u_int		 number = item->number; | ||||||
|  |  | ||||||
| 	if (c != NULL && (c->flags & CLIENT_CONTROL)) | 	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. */ | /* Show message from command. */ | ||||||
| @@ -807,6 +807,9 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...) | |||||||
| 			msg = utf8_sanitize(tmp); | 			msg = utf8_sanitize(tmp); | ||||||
| 			free(tmp); | 			free(tmp); | ||||||
| 		} | 		} | ||||||
|  | 		if (c->flags & CLIENT_CONTROL) | ||||||
|  | 			control_write(c, "%s", msg); | ||||||
|  | 		else | ||||||
| 			file_print(c, "%s\n", msg); | 			file_print(c, "%s\n", msg); | ||||||
| 	} else { | 	} else { | ||||||
| 		wp = server_client_get_pane(c); | 		wp = server_client_get_pane(c); | ||||||
| @@ -849,7 +852,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) | |||||||
| 			free(tmp); | 			free(tmp); | ||||||
| 		} | 		} | ||||||
| 		if (c->flags & CLIENT_CONTROL) | 		if (c->flags & CLIENT_CONTROL) | ||||||
| 			file_print(c, "%s\n", msg); | 			control_write(c, "%s", msg); | ||||||
| 		else | 		else | ||||||
| 			file_error(c, "%s\n", msg); | 			file_error(c, "%s\n", msg); | ||||||
| 		c->retval = 1; | 		c->retval = 1; | ||||||
|   | |||||||
							
								
								
									
										466
									
								
								control.c
									
									
									
									
									
								
							
							
						
						
									
										466
									
								
								control.c
									
									
									
									
									
								
							| @@ -27,112 +27,183 @@ | |||||||
|  |  | ||||||
| #include "tmux.h" | #include "tmux.h" | ||||||
|  |  | ||||||
| /* Control client offset. */ | /* | ||||||
| struct control_offset { |  * 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; | ||||||
|  |  | ||||||
|  | 	TAILQ_ENTRY(control_block)	 entry; | ||||||
|  | 	TAILQ_ENTRY(control_block)	 all_entry; | ||||||
|  |  }; | ||||||
|  |  | ||||||
|  | /* Control client pane. */ | ||||||
|  | struct control_pane { | ||||||
| 	u_int				 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	 offset; | ||||||
| 	int				flags; | 	struct window_pane_offset	 queued; | ||||||
| #define CONTROL_OFFSET_OFF 0x1 |  | ||||||
|  |  | ||||||
| 	RB_ENTRY(control_offset)	entry; | 	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. */ | /* Control client state. */ | ||||||
| struct control_state { | struct control_state { | ||||||
| 	struct control_offsets	 offsets; | 	struct control_panes		 panes; | ||||||
|  |  | ||||||
|  | 	TAILQ_HEAD(, control_pane)	 pending_list; | ||||||
|  | 	u_int				 pending_count; | ||||||
|  |  | ||||||
|  | 	TAILQ_HEAD(, control_block)	 all_blocks; | ||||||
|  |  | ||||||
| 	struct bufferevent		*read_event; | 	struct bufferevent		*read_event; | ||||||
| 	struct bufferevent		*write_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 | 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); | 		return (-1); | ||||||
| 	if (co1->pane > co2->pane) | 	if (cp1->pane > cp2->pane) | ||||||
| 		return (1); | 		return (1); | ||||||
| 	return (0); | 	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. */ | /* Get pane offsets for this client. */ | ||||||
| static struct control_offset * | static struct control_pane * | ||||||
| control_get_offset(struct client *c, struct window_pane *wp) | control_get_pane(struct client *c, struct window_pane *wp) | ||||||
| { | { | ||||||
| 	struct control_state	*cs = c->control_state; | 	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. */ | /* Add pane offsets for this client. */ | ||||||
| static struct control_offset * | static struct control_pane * | ||||||
| control_add_offset(struct client *c, struct window_pane *wp) | control_add_pane(struct client *c, struct window_pane *wp) | ||||||
| { | { | ||||||
| 	struct control_state	*cs = c->control_state; | 	struct control_state	*cs = c->control_state; | ||||||
| 	struct control_offset	*co; | 	struct control_pane	*cp; | ||||||
|  |  | ||||||
| 	co = control_get_offset(c, wp); | 	cp = control_get_pane(c, wp); | ||||||
| 	if (co != NULL) | 	if (cp != NULL) | ||||||
| 		return (co); | 		return (cp); | ||||||
|  |  | ||||||
| 	co = xcalloc(1, sizeof *co); | 	cp = xcalloc(1, sizeof *cp); | ||||||
| 	co->pane = wp->id; | 	cp->pane = wp->id; | ||||||
| 	RB_INSERT(control_offsets, &cs->offsets, co); | 	RB_INSERT(control_panes, &cs->panes, cp); | ||||||
| 	memcpy(&co->offset, &wp->offset, sizeof co->offset); |  | ||||||
| 	return (co); | 	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 | void | ||||||
| control_free_offsets(struct client *c) | control_reset_offsets(struct client *c) | ||||||
| { | { | ||||||
| 	struct control_state	*cs = c->control_state; | 	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_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) { | ||||||
| 		RB_REMOVE(control_offsets, &cs->offsets, co); | 		RB_REMOVE(control_panes, &cs->panes, cp); | ||||||
| 		free(co); | 		free(cp); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	TAILQ_INIT(&cs->pending_list); | ||||||
|  | 	cs->pending_count = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Get offsets for client. */ | /* Get offsets for client. */ | ||||||
| struct window_pane_offset * | struct window_pane_offset * | ||||||
| control_pane_offset(struct client *c, struct window_pane *wp, int *off) | 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) { | 	if (c->flags & CLIENT_CONTROL_NOOUTPUT) { | ||||||
| 		*off = 0; | 		*off = 0; | ||||||
| 		return (NULL); | 		return (NULL); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	co = control_get_offset(c, wp); | 	cp = control_get_pane(c, wp); | ||||||
| 	if (co == NULL) { | 	if (cp == NULL) { | ||||||
| 		*off = 0; | 		*off = 0; | ||||||
| 		return (NULL); | 		return (NULL); | ||||||
| 	} | 	} | ||||||
| 	if (co->flags & CONTROL_OFFSET_OFF) { | 	if (cp->flags & CONTROL_PANE_OFF) { | ||||||
| 		*off = 1; | 		*off = 1; | ||||||
| 		return (NULL); | 		return (NULL); | ||||||
| 	} | 	} | ||||||
| 	return (&co->offset); | 	*off = (EVBUFFER_LENGTH(cs->write_event->output) >= CONTROL_BUFFER_LOW); | ||||||
|  | 	return (&cp->offset); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Set pane as on. */ | /* Set pane as on. */ | ||||||
| void | void | ||||||
| control_set_pane_on(struct client *c, struct window_pane *wp) | 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); | 	cp = control_get_pane(c, wp); | ||||||
| 	if (co != NULL) { | 	if (cp != NULL) { | ||||||
| 		co->flags &= ~CONTROL_OFFSET_OFF; | 		cp->flags &= ~CONTROL_PANE_OFF; | ||||||
| 		memcpy(&co->offset, &wp->offset, sizeof co->offset); | 		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 | void | ||||||
| control_set_pane_off(struct client *c, struct window_pane *wp) | 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); | 	cp = control_add_pane(c, wp); | ||||||
| 	co->flags |= CONTROL_OFFSET_OFF; | 	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. */ | /* Write a line. */ | ||||||
| @@ -151,16 +239,25 @@ void | |||||||
| control_write(struct client *c, const char *fmt, ...) | control_write(struct client *c, const char *fmt, ...) | ||||||
| { | { | ||||||
| 	struct control_state	*cs = c->control_state; | 	struct control_state	*cs = c->control_state; | ||||||
|  | 	struct control_block	*cb; | ||||||
| 	va_list			 ap; | 	va_list			 ap; | ||||||
| 	char			*s; |  | ||||||
|  |  | ||||||
| 	va_start(ap, fmt); | 	va_start(ap, fmt); | ||||||
| 	xvasprintf(&s, fmt, ap); |  | ||||||
| 	va_end(ap); |  | ||||||
|  |  | ||||||
| 	bufferevent_write(cs->write_event, s, strlen(s)); | 	if (TAILQ_EMPTY(&cs->all_blocks)) { | ||||||
| 	bufferevent_write(cs->write_event, "\n", 1); | 		control_vwrite(c, fmt, ap); | ||||||
| 	free(s); | 		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. */ | /* Write output from a pane. */ | ||||||
| @@ -168,42 +265,50 @@ void | |||||||
| control_write_output(struct client *c, struct window_pane *wp) | control_write_output(struct client *c, struct window_pane *wp) | ||||||
| { | { | ||||||
| 	struct control_state	*cs = c->control_state; | 	struct control_state	*cs = c->control_state; | ||||||
| 	struct control_offset	*co; | 	struct control_pane	*cp; | ||||||
| 	struct evbuffer		*message; | 	struct control_block	*cb; | ||||||
| 	u_char			*new_data; | 	size_t			 new_size; | ||||||
| 	size_t			 new_size, i; |  | ||||||
|  |  | ||||||
| 	if (c->flags & CLIENT_CONTROL_NOOUTPUT) |  | ||||||
| 		return; |  | ||||||
| 	if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) | 	if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| 	co = control_add_offset(c, wp); | 	if (c->flags & CONTROL_IGNORE_FLAGS) { | ||||||
| 	if (co->flags & CONTROL_OFFSET_OFF) { | 		cp = control_get_pane(c, wp); | ||||||
| 		window_pane_update_used_data(wp, &co->offset, SIZE_MAX, 1); | 		if (cp != NULL) | ||||||
|  | 			goto ignore; | ||||||
| 		return; | 		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) | 	if (new_size == 0) | ||||||
| 		return; | 		return; | ||||||
|  | 	window_pane_update_used_data(wp, &cp->queued, new_size); | ||||||
|  |  | ||||||
| 	message = evbuffer_new(); | 	cb = xcalloc(1, sizeof *cb); | ||||||
| 	if (message == NULL) | 	cb->size = new_size; | ||||||
| 		fatalx("out of memory"); | 	TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry); | ||||||
| 	evbuffer_add_printf(message, "%%output %%%u ", wp->id); |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < new_size; i++) { | 	TAILQ_INSERT_TAIL(&cp->blocks, cb, entry); | ||||||
| 		if (new_data[i] < ' ' || new_data[i] == '\\') | 	log_debug("%s: %s: new output block of %zu for %%%u", __func__, c->name, | ||||||
| 			evbuffer_add_printf(message, "\\%03o", new_data[i]); | 	    cb->size, wp->id); | ||||||
| 		else |  | ||||||
| 			evbuffer_add_printf(message, "%c", new_data[i]); | 	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); | ignore: | ||||||
| 	evbuffer_free(message); | 	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, &co->offset, new_size, 1); | 	window_pane_update_used_data(wp, &cp->queued, SIZE_MAX); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Control client error callback. */ | /* 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); | 		line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF); | ||||||
| 		if (line == NULL) | 		if (line == NULL) | ||||||
| 			break; | 			break; | ||||||
| 		log_debug("%s: %s", __func__, line); | 		log_debug("%s: %s: %s", __func__, c->name, line); | ||||||
| 		if (*line == '\0') { /* empty line exit */ | 		if (*line == '\0') { /* empty line detach */ | ||||||
| 			free(line); | 			free(line); | ||||||
| 			c->flags |= CLIENT_EXIT; | 			c->flags |= CLIENT_EXIT; | ||||||
| 			break; | 			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. */ | /* Initialize for control mode. */ | ||||||
| void | void | ||||||
| control_start(struct client *c) | control_start(struct client *c) | ||||||
| @@ -277,22 +544,43 @@ control_start(struct client *c) | |||||||
| 	setblocking(c->fd, 0); | 	setblocking(c->fd, 0); | ||||||
|  |  | ||||||
| 	cs = c->control_state = xcalloc(1, sizeof *cs); | 	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, | 	cs->read_event = bufferevent_new(c->fd, control_read_callback, | ||||||
| 	    control_error_callback, c); | 	    control_write_callback, control_error_callback, c); | ||||||
| 	bufferevent_enable(cs->read_event, EV_READ); | 	bufferevent_enable(cs->read_event, EV_READ); | ||||||
|  |  | ||||||
| 	if (c->flags & CLIENT_CONTROLCONTROL) | 	if (c->flags & CLIENT_CONTROLCONTROL) | ||||||
| 		cs->write_event = cs->read_event; | 		cs->write_event = cs->read_event; | ||||||
| 	else { | 	else { | ||||||
| 		cs->write_event = bufferevent_new(c->out_fd, NULL, NULL, | 		cs->write_event = bufferevent_new(c->out_fd, NULL, | ||||||
| 		    control_error_callback, c); | 		    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) | 	if (c->flags & CLIENT_CONTROLCONTROL) { | ||||||
| 		control_write(c, "\033P1000p"); | 		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. */ | /* Stop control mode. */ | ||||||
| @@ -300,11 +588,15 @@ void | |||||||
| control_stop(struct client *c) | control_stop(struct client *c) | ||||||
| { | { | ||||||
| 	struct control_state	*cs = c->control_state; | 	struct control_state	*cs = c->control_state; | ||||||
|  | 	struct control_block	*cb, *cb1; | ||||||
|  |  | ||||||
| 	if (~c->flags & CLIENT_CONTROLCONTROL) | 	if (~c->flags & CLIENT_CONTROLCONTROL) | ||||||
| 		bufferevent_free(cs->write_event); | 		bufferevent_free(cs->write_event); | ||||||
| 	bufferevent_free(cs->read_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); | 	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); | 	new_data = window_pane_get_new_data(wp, &wp->offset, &new_size); | ||||||
| 	input_parse_buffer(wp, new_data, 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. */ | /* 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 *, | static void	server_client_dispatch_read_done(struct client *, | ||||||
| 		    struct imsg *); | 		    struct imsg *); | ||||||
|  |  | ||||||
|  | /* Maximum data allowed to be held for a pane for a control client. */ | ||||||
|  | #define SERVER_CLIENT_PANE_LIMIT 16777216 | ||||||
|  |  | ||||||
| /* Compare client windows. */ | /* Compare client windows. */ | ||||||
| static int | static int | ||||||
| server_client_window_cmp(struct client_window *cw1, | server_client_window_cmp(struct client_window *cw1, | ||||||
| @@ -78,7 +81,7 @@ server_client_how_many(void) | |||||||
|  |  | ||||||
| 	n = 0; | 	n = 0; | ||||||
| 	TAILQ_FOREACH(c, &clients, entry) { | 	TAILQ_FOREACH(c, &clients, entry) { | ||||||
| 		if (c->session != NULL && (~c->flags & CLIENT_DETACHING)) | 		if (c->session != NULL && (~c->flags & CLIENT_UNATTACHEDFLAGS)) | ||||||
| 			n++; | 			n++; | ||||||
| 	} | 	} | ||||||
| 	return (n); | 	return (n); | ||||||
| @@ -384,7 +387,7 @@ server_client_suspend(struct client *c) | |||||||
| { | { | ||||||
| 	struct session	*s = c->session; | 	struct session	*s = c->session; | ||||||
|  |  | ||||||
| 	if (s == NULL || (c->flags & CLIENT_DETACHING)) | 	if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| 	tty_stop_tty(&c->tty); | 	tty_stop_tty(&c->tty); | ||||||
| @@ -398,12 +401,14 @@ server_client_detach(struct client *c, enum msgtype msgtype) | |||||||
| { | { | ||||||
| 	struct session	*s = c->session; | 	struct session	*s = c->session; | ||||||
|  |  | ||||||
| 	if (s == NULL || (c->flags & CLIENT_DETACHING)) | 	if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| 	c->flags |= CLIENT_DETACHING; | 	c->flags |= CLIENT_EXIT; | ||||||
| 	notify_client("client-detached", c); |  | ||||||
| 	proc_send(c->peer, msgtype, -1, s->name, strlen(s->name) + 1); | 	c->exit_type = CLIENT_EXIT_DETACH; | ||||||
|  | 	c->exit_msgtype = msgtype; | ||||||
|  | 	c->exit_session = xstrdup(s->name); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Execute command to replace a client. */ | /* Execute command to replace a client. */ | ||||||
| @@ -1505,12 +1510,12 @@ server_client_check_pane_buffer(struct window_pane *wp) | |||||||
| 	u_int				 attached_clients = 0; | 	u_int				 attached_clients = 0; | ||||||
|  |  | ||||||
| 	/* | 	/* | ||||||
| 	 * Work out the minimum acknowledged size. This is the most that can be | 	 * Work out the minimum used size. This is the most that can be removed | ||||||
| 	 * removed from the buffer. | 	 * from the buffer. | ||||||
| 	 */ | 	 */ | ||||||
| 	minimum = wp->offset.acknowledged; | 	minimum = wp->offset.used; | ||||||
| 	if (wp->pipe_fd != -1 && wp->pipe_offset.acknowledged < minimum) | 	if (wp->pipe_fd != -1 && wp->pipe_offset.used < minimum) | ||||||
| 		minimum = wp->pipe_offset.acknowledged; | 		minimum = wp->pipe_offset.used; | ||||||
| 	TAILQ_FOREACH(c, &clients, entry) { | 	TAILQ_FOREACH(c, &clients, entry) { | ||||||
| 		if (c->session == NULL) | 		if (c->session == NULL) | ||||||
| 			continue; | 			continue; | ||||||
| @@ -1528,11 +1533,13 @@ server_client_check_pane_buffer(struct window_pane *wp) | |||||||
| 		if (!flag) | 		if (!flag) | ||||||
| 			off = 0; | 			off = 0; | ||||||
|  |  | ||||||
| 		log_debug("%s: %s has %zu bytes used, %zu bytes acknowledged " | 		log_debug("%s: %s has %zu bytes used for %%%u", __func__, | ||||||
| 		    "for %%%u", __func__, c->name, wpo->used, wpo->acknowledged, | 		    c->name, wpo->used - wp->base_offset, wp->id); | ||||||
| 		    wp->id); | 		if (wpo->used - wp->base_offset > SERVER_CLIENT_PANE_LIMIT) { | ||||||
| 		if (wpo->acknowledged < minimum) | 			control_flush(c); | ||||||
| 			minimum = wpo->acknowledged; | 			c->flags |= CLIENT_EXIT; | ||||||
|  | 		} else if (wpo->used < minimum) | ||||||
|  | 			minimum = wpo->used; | ||||||
| 	} | 	} | ||||||
| 	if (attached_clients == 0) | 	if (attached_clients == 0) | ||||||
| 		off = 0; | 		off = 0; | ||||||
| @@ -1541,8 +1548,8 @@ server_client_check_pane_buffer(struct window_pane *wp) | |||||||
| 		goto out; | 		goto out; | ||||||
|  |  | ||||||
| 	/* Drain the buffer. */ | 	/* Drain the buffer. */ | ||||||
| 	log_debug("%s: %%%u has %zu minimum (of %zu) bytes acknowledged", | 	log_debug("%s: %%%u has %zu minimum (of %zu) bytes used", __func__, | ||||||
| 	    __func__, wp->id, minimum, EVBUFFER_LENGTH(evb)); | 	    wp->id, minimum, EVBUFFER_LENGTH(evb)); | ||||||
| 	evbuffer_drain(evb, minimum); | 	evbuffer_drain(evb, minimum); | ||||||
|  |  | ||||||
| 	/* | 	/* | ||||||
| @@ -1551,21 +1558,16 @@ server_client_check_pane_buffer(struct window_pane *wp) | |||||||
| 	 */ | 	 */ | ||||||
| 	if (wp->base_offset > SIZE_MAX - minimum) { | 	if (wp->base_offset > SIZE_MAX - minimum) { | ||||||
| 		log_debug("%s: %%%u base offset has wrapped", __func__, wp->id); | 		log_debug("%s: %%%u base offset has wrapped", __func__, wp->id); | ||||||
| 		wp->offset.acknowledged -= wp->base_offset; |  | ||||||
| 		wp->offset.used -= wp->base_offset; | 		wp->offset.used -= wp->base_offset; | ||||||
| 		if (wp->pipe_fd != -1) { | 		if (wp->pipe_fd != -1) | ||||||
| 			wp->pipe_offset.acknowledged -= wp->base_offset; |  | ||||||
| 			wp->pipe_offset.used -= wp->base_offset; | 			wp->pipe_offset.used -= wp->base_offset; | ||||||
| 		} |  | ||||||
| 		TAILQ_FOREACH(c, &clients, entry) { | 		TAILQ_FOREACH(c, &clients, entry) { | ||||||
| 			if (c->session == NULL || (~c->flags & CLIENT_CONTROL)) | 			if (c->session == NULL || (~c->flags & CLIENT_CONTROL)) | ||||||
| 				continue; | 				continue; | ||||||
| 			wpo = control_pane_offset(c, wp, &flag); | 			wpo = control_pane_offset(c, wp, &flag); | ||||||
| 			if (wpo != NULL && !flag) { | 			if (wpo != NULL && !flag) | ||||||
| 				wpo->acknowledged -= wp->base_offset; |  | ||||||
| 				wpo->used -= wp->base_offset; | 				wpo->used -= wp->base_offset; | ||||||
| 		} | 		} | ||||||
| 		} |  | ||||||
| 		wp->base_offset = minimum; | 		wp->base_offset = minimum; | ||||||
| 	} else | 	} else | ||||||
| 		wp->base_offset += minimum; | 		wp->base_offset += minimum; | ||||||
| @@ -1577,6 +1579,7 @@ out: | |||||||
| 	 * clients, all of which are control clients which are not able to | 	 * clients, all of which are control clients which are not able to | ||||||
| 	 * accept any more data. | 	 * accept any more data. | ||||||
| 	 */ | 	 */ | ||||||
|  | 	log_debug("%s: pane %%%u is %s", __func__, wp->id, off ? "off" : "on"); | ||||||
| 	if (off) | 	if (off) | ||||||
| 		bufferevent_disable(wp->event, EV_READ); | 		bufferevent_disable(wp->event, EV_READ); | ||||||
| 	else | 	else | ||||||
| @@ -1768,12 +1771,16 @@ static void | |||||||
| server_client_check_exit(struct client *c) | server_client_check_exit(struct client *c) | ||||||
| { | { | ||||||
| 	struct client_file	*cf; | 	struct client_file	*cf; | ||||||
|  | 	const char		*name = c->exit_session; | ||||||
|  |  | ||||||
| 	if (~c->flags & CLIENT_EXIT) | 	if ((c->flags & CLIENT_EXITED) || (~c->flags & CLIENT_EXIT)) | ||||||
| 		return; |  | ||||||
| 	if (c->flags & CLIENT_EXITED) |  | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
|  | 	if (c->flags & CLIENT_CONTROL) { | ||||||
|  | 		control_flush(c); | ||||||
|  | 		if (!control_all_done(c)) | ||||||
|  | 			return; | ||||||
|  | 	} | ||||||
| 	RB_FOREACH(cf, client_files, &c->files) { | 	RB_FOREACH(cf, client_files, &c->files) { | ||||||
| 		if (EVBUFFER_LENGTH(cf->buffer) != 0) | 		if (EVBUFFER_LENGTH(cf->buffer) != 0) | ||||||
| 			return; | 			return; | ||||||
| @@ -1781,8 +1788,20 @@ server_client_check_exit(struct client *c) | |||||||
|  |  | ||||||
| 	if (c->flags & CLIENT_ATTACHED) | 	if (c->flags & CLIENT_ATTACHED) | ||||||
| 		notify_client("client-detached", c); | 		notify_client("client-detached", c); | ||||||
| 	proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval); |  | ||||||
| 	c->flags |= CLIENT_EXITED; | 	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. */ | /* Redraw timer callback. */ | ||||||
| @@ -1994,7 +2013,6 @@ server_client_dispatch(struct imsg *imsg, void *arg) | |||||||
| 	case MSG_EXITING: | 	case MSG_EXITING: | ||||||
| 		if (datalen != 0) | 		if (datalen != 0) | ||||||
| 			fatalx("bad MSG_EXITING size"); | 			fatalx("bad MSG_EXITING size"); | ||||||
|  |  | ||||||
| 		c->session = NULL; | 		c->session = NULL; | ||||||
| 		tty_close(&c->tty); | 		tty_close(&c->tty); | ||||||
| 		proc_send(c->peer, MSG_EXITED, -1, NULL, 0); | 		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) | 	if (~c->flags & CLIENT_ATTACHED) | ||||||
| 		c->flags |= CLIENT_EXIT; | 		c->flags |= CLIENT_EXIT; | ||||||
| 	else if (~c->flags & CLIENT_DETACHING) | 	else if (~c->flags & CLIENT_EXIT) | ||||||
| 		tty_send_requests(&c->tty); | 		tty_send_requests(&c->tty); | ||||||
| 	return (CMD_RETURN_NORMAL); | 	return (CMD_RETURN_NORMAL); | ||||||
| } | } | ||||||
| @@ -2374,7 +2392,7 @@ server_client_set_flags(struct client *c, const char *flags) | |||||||
| 		else | 		else | ||||||
| 			c->flags |= flag; | 			c->flags |= flag; | ||||||
| 		if (flag == CLIENT_CONTROL_NOOUTPUT) | 		if (flag == CLIENT_CONTROL_NOOUTPUT) | ||||||
| 			control_free_offsets(c); | 			control_reset_offsets(c); | ||||||
| 	} | 	} | ||||||
| 	free(copy); | 	free(copy); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								server.c
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								server.c
									
									
									
									
									
								
							| @@ -294,9 +294,8 @@ server_send_exit(void) | |||||||
| 		if (c->flags & CLIENT_SUSPENDED) | 		if (c->flags & CLIENT_SUSPENDED) | ||||||
| 			server_client_lost(c); | 			server_client_lost(c); | ||||||
| 		else { | 		else { | ||||||
| 			if (c->flags & CLIENT_ATTACHED) | 			c->flags |= CLIENT_EXIT; | ||||||
| 				notify_client("client-detached", c); | 			c->exit_type = CLIENT_EXIT_SHUTDOWN; | ||||||
| 			proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0); |  | ||||||
| 		} | 		} | ||||||
| 		c->session = NULL; | 		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. */ | /* Automatic name refresh interval, in microseconds. Must be < 1 second. */ | ||||||
| #define NAME_INTERVAL 500000 | #define NAME_INTERVAL 500000 | ||||||
|  |  | ||||||
| /* Maximum size of data to hold from a pane. */ |  | ||||||
| #define READ_SIZE 8192 |  | ||||||
|  |  | ||||||
| /* Default pixel cell sizes. */ | /* Default pixel cell sizes. */ | ||||||
| #define DEFAULT_XPIXEL 16 | #define DEFAULT_XPIXEL 16 | ||||||
| #define DEFAULT_YPIXEL 32 | #define DEFAULT_YPIXEL 32 | ||||||
| @@ -917,7 +914,6 @@ struct window_mode_entry { | |||||||
| /* Offsets into pane buffer. */ | /* Offsets into pane buffer. */ | ||||||
| struct window_pane_offset { | struct window_pane_offset { | ||||||
| 	size_t	used; | 	size_t	used; | ||||||
| 	size_t	acknowledged; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Child window structure. */ | /* Child window structure. */ | ||||||
| @@ -1629,7 +1625,7 @@ struct client { | |||||||
| #define CLIENT_DEAD 0x200 | #define CLIENT_DEAD 0x200 | ||||||
| #define CLIENT_REDRAWBORDERS 0x400 | #define CLIENT_REDRAWBORDERS 0x400 | ||||||
| #define CLIENT_READONLY 0x800 | #define CLIENT_READONLY 0x800 | ||||||
| #define CLIENT_DETACHING 0x1000 | /* 0x1000 unused */ | ||||||
| #define CLIENT_CONTROL 0x2000 | #define CLIENT_CONTROL 0x2000 | ||||||
| #define CLIENT_CONTROLCONTROL 0x4000 | #define CLIENT_CONTROLCONTROL 0x4000 | ||||||
| #define CLIENT_FOCUSED 0x8000 | #define CLIENT_FOCUSED 0x8000 | ||||||
| @@ -1659,12 +1655,21 @@ struct client { | |||||||
| #define CLIENT_UNATTACHEDFLAGS	\ | #define CLIENT_UNATTACHEDFLAGS	\ | ||||||
| 	(CLIENT_DEAD|		\ | 	(CLIENT_DEAD|		\ | ||||||
| 	 CLIENT_SUSPENDED|	\ | 	 CLIENT_SUSPENDED|	\ | ||||||
| 	 CLIENT_DETACHING) | 	 CLIENT_EXIT) | ||||||
| #define CLIENT_NOSIZEFLAGS	\ | #define CLIENT_NOSIZEFLAGS	\ | ||||||
| 	(CLIENT_DEAD|		\ | 	(CLIENT_DEAD|		\ | ||||||
| 	 CLIENT_SUSPENDED|	\ | 	 CLIENT_SUSPENDED|	\ | ||||||
| 	 CLIENT_DETACHING) | 	 CLIENT_EXIT) | ||||||
| 	uint64_t	 flags; | 	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; | 	struct key_table *keytable; | ||||||
|  |  | ||||||
| 	uint64_t	 redraw_panes; | 	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 *, | void		*window_pane_get_new_data(struct window_pane *, | ||||||
| 		     struct window_pane_offset *, size_t *); | 		     struct window_pane_offset *, size_t *); | ||||||
| void		 window_pane_update_used_data(struct window_pane *, | 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); | 		     struct window_pane_offset *, size_t); | ||||||
|  |  | ||||||
| /* layout.c */ | /* layout.c */ | ||||||
| @@ -2831,15 +2834,17 @@ char	*default_window_name(struct window *); | |||||||
| char	*parse_window_name(const char *); | char	*parse_window_name(const char *); | ||||||
|  |  | ||||||
| /* control.c */ | /* control.c */ | ||||||
|  | void	control_flush(struct client *); | ||||||
| void	control_start(struct client *); | void	control_start(struct client *); | ||||||
| void	control_stop(struct client *); | void	control_stop(struct client *); | ||||||
| void	control_set_pane_on(struct client *, struct window_pane *); | void	control_set_pane_on(struct client *, struct window_pane *); | ||||||
| void	control_set_pane_off(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_offset *control_pane_offset(struct client *, | ||||||
| 	   struct window_pane *, int *); | 	   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 printflike(2, 3) control_write(struct client *, const char *, ...); | ||||||
| void	control_write_output(struct client *, struct window_pane *); | void	control_write_output(struct client *, struct window_pane *); | ||||||
|  | int	control_all_done(struct client *); | ||||||
|  |  | ||||||
| /* control-notify.c */ | /* control-notify.c */ | ||||||
| void	control_notify_input(struct client *, struct window_pane *, | 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; | 	data->item_size = 0; | ||||||
|  |  | ||||||
| 	TAILQ_FOREACH(c, &clients, entry) { | 	TAILQ_FOREACH(c, &clients, entry) { | ||||||
| 		if (c->session == NULL || (c->flags & (CLIENT_DETACHING))) | 		if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) | ||||||
| 			continue; | 			continue; | ||||||
|  |  | ||||||
| 		item = window_client_add_item(data); | 		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); | 		new_data = window_pane_get_new_data(wp, wpo, &new_size); | ||||||
| 		if (new_size > 0) { | 		if (new_size > 0) { | ||||||
| 			bufferevent_write(wp->pipe_event, new_data, new_size); | 			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); | 	log_debug("%%%u has %zu bytes", wp->id, size); | ||||||
| 	TAILQ_FOREACH(c, &clients, entry) { | 	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); | 			control_write_output(c, wp); | ||||||
| 	} | 	} | ||||||
| 	input_parse_pane(wp); | 	input_parse_pane(wp); | ||||||
|  | 	bufferevent_disable(wp->event, EV_READ); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| @@ -988,7 +989,6 @@ window_pane_set_event(struct window_pane *wp) | |||||||
| 	    NULL, window_pane_error_callback, wp); | 	    NULL, window_pane_error_callback, wp); | ||||||
| 	wp->ictx = input_init(wp, wp->event); | 	wp->ictx = input_init(wp, wp->event); | ||||||
|  |  | ||||||
| 	bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); |  | ||||||
| 	bufferevent_enable(wp->event, EV_READ|EV_WRITE); | 	bufferevent_enable(wp->event, EV_READ|EV_WRITE); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1569,27 +1569,11 @@ window_pane_get_new_data(struct window_pane *wp, | |||||||
|  |  | ||||||
| void | void | ||||||
| window_pane_update_used_data(struct window_pane *wp, | 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; | 	size_t	used = wpo->used - wp->base_offset; | ||||||
|  |  | ||||||
| 	if (size > EVBUFFER_LENGTH(wp->event->input) - used) | 	if (size > EVBUFFER_LENGTH(wp->event->input) - used) | ||||||
| 		size = EVBUFFER_LENGTH(wp->event->input) - used; | 		size = EVBUFFER_LENGTH(wp->event->input) - used; | ||||||
| 	wpo->used += size; | 	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
	 Thomas Adam
					Thomas Adam