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:
		
							
								
								
									
										45
									
								
								client.c
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								client.c
									
									
									
									
									
								
							@@ -45,11 +45,13 @@ static enum {
 | 
			
		||||
	CLIENT_EXIT_LOST_SERVER,
 | 
			
		||||
	CLIENT_EXIT_EXITED,
 | 
			
		||||
	CLIENT_EXIT_SERVER_EXITED,
 | 
			
		||||
	CLIENT_EXIT_MESSAGE_PROVIDED
 | 
			
		||||
} client_exitreason = CLIENT_EXIT_NONE;
 | 
			
		||||
static int		 client_exitflag;
 | 
			
		||||
static int		 client_exitval;
 | 
			
		||||
static enum msgtype	 client_exittype;
 | 
			
		||||
static const char	*client_exitsession;
 | 
			
		||||
static char		*client_exitmessage;
 | 
			
		||||
static const char	*client_execshell;
 | 
			
		||||
static const char	*client_execcmd;
 | 
			
		||||
static int		 client_attached;
 | 
			
		||||
@@ -207,6 +209,8 @@ client_exit_message(void)
 | 
			
		||||
		return ("exited");
 | 
			
		||||
	case CLIENT_EXIT_SERVER_EXITED:
 | 
			
		||||
		return ("server exited");
 | 
			
		||||
	case CLIENT_EXIT_MESSAGE_PROVIDED:
 | 
			
		||||
		return (client_exitmessage);
 | 
			
		||||
	}
 | 
			
		||||
	return ("unknown reason");
 | 
			
		||||
}
 | 
			
		||||
@@ -793,13 +797,38 @@ client_dispatch(struct imsg *imsg, __unused void *arg)
 | 
			
		||||
		client_dispatch_wait(imsg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Process an exit message. */
 | 
			
		||||
static void
 | 
			
		||||
client_dispatch_exit_message(char *data, size_t datalen)
 | 
			
		||||
{
 | 
			
		||||
	int	retval;
 | 
			
		||||
 | 
			
		||||
	if (datalen < sizeof retval && datalen != 0)
 | 
			
		||||
		fatalx("bad MSG_EXIT size");
 | 
			
		||||
 | 
			
		||||
	if (datalen >= sizeof retval) {
 | 
			
		||||
		memcpy(&retval, data, sizeof retval);
 | 
			
		||||
		client_exitval = retval;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (datalen > sizeof retval) {
 | 
			
		||||
		datalen -= sizeof retval;
 | 
			
		||||
		data += sizeof retval;
 | 
			
		||||
 | 
			
		||||
		client_exitmessage = xmalloc(datalen);
 | 
			
		||||
		memcpy(client_exitmessage, data, datalen);
 | 
			
		||||
		client_exitmessage[datalen - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
		client_exitreason = CLIENT_EXIT_MESSAGE_PROVIDED;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Dispatch imsgs when in wait state (before MSG_READY). */
 | 
			
		||||
static void
 | 
			
		||||
client_dispatch_wait(struct imsg *imsg)
 | 
			
		||||
{
 | 
			
		||||
	char		*data;
 | 
			
		||||
	ssize_t		 datalen;
 | 
			
		||||
	int		 retval;
 | 
			
		||||
	static int	 pledge_applied;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
@@ -822,12 +851,7 @@ client_dispatch_wait(struct imsg *imsg)
 | 
			
		||||
	switch (imsg->hdr.type) {
 | 
			
		||||
	case MSG_EXIT:
 | 
			
		||||
	case MSG_SHUTDOWN:
 | 
			
		||||
		if (datalen != sizeof retval && datalen != 0)
 | 
			
		||||
			fatalx("bad MSG_EXIT size");
 | 
			
		||||
		if (datalen == sizeof retval) {
 | 
			
		||||
			memcpy(&retval, data, sizeof retval);
 | 
			
		||||
			client_exitval = retval;
 | 
			
		||||
		}
 | 
			
		||||
		client_dispatch_exit_message(data, datalen);
 | 
			
		||||
		client_exitflag = 1;
 | 
			
		||||
		client_exit();
 | 
			
		||||
		break;
 | 
			
		||||
@@ -918,11 +942,10 @@ client_dispatch_attached(struct imsg *imsg)
 | 
			
		||||
		proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_EXIT:
 | 
			
		||||
		if (datalen != 0 && datalen != sizeof (int))
 | 
			
		||||
			fatalx("bad MSG_EXIT size");
 | 
			
		||||
 | 
			
		||||
		client_dispatch_exit_message(data, datalen);
 | 
			
		||||
		if (client_exitreason == CLIENT_EXIT_NONE)
 | 
			
		||||
			client_exitreason = CLIENT_EXIT_EXITED;
 | 
			
		||||
		proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
 | 
			
		||||
		client_exitreason = CLIENT_EXIT_EXITED;
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_EXITED:
 | 
			
		||||
		if (datalen != 0)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										57
									
								
								control.c
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								control.c
									
									
									
									
									
								
							@@ -89,13 +89,16 @@ struct control_state {
 | 
			
		||||
	struct bufferevent		*write_event;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Low watermark. */
 | 
			
		||||
/* Low and high watermarks. */
 | 
			
		||||
#define CONTROL_BUFFER_LOW 512
 | 
			
		||||
#define CONTROL_BUFFER_HIGH 8192
 | 
			
		||||
 | 
			
		||||
/* Minimum to write to each client. */
 | 
			
		||||
#define CONTROL_WRITE_MINIMUM 32
 | 
			
		||||
 | 
			
		||||
/* Maximum age for clients that are not using pause mode. */
 | 
			
		||||
#define CONTROL_MAXIMUM_AGE 300000
 | 
			
		||||
 | 
			
		||||
/* Flags to ignore client. */
 | 
			
		||||
#define CONTROL_IGNORE_FLAGS \
 | 
			
		||||
	(CLIENT_CONTROL_NOOUTPUT| \
 | 
			
		||||
@@ -306,6 +309,41 @@ control_write(struct client *c, const char *fmt, ...)
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check age for this pane. */
 | 
			
		||||
static int
 | 
			
		||||
control_check_age(struct client *c, struct window_pane *wp,
 | 
			
		||||
    struct control_pane *cp)
 | 
			
		||||
{
 | 
			
		||||
	struct control_block	*cb;
 | 
			
		||||
	uint64_t		 t, age;
 | 
			
		||||
 | 
			
		||||
	cb = TAILQ_FIRST(&cp->blocks);
 | 
			
		||||
	if (cb == NULL)
 | 
			
		||||
		return (0);
 | 
			
		||||
	t = get_timer();
 | 
			
		||||
	if (cb->t >= t)
 | 
			
		||||
		return (0);
 | 
			
		||||
 | 
			
		||||
	age = t - cb->t;
 | 
			
		||||
	log_debug("%s: %s: %%%u is %llu behind", __func__, c->name, wp->id,
 | 
			
		||||
	    (unsigned long long)age);
 | 
			
		||||
 | 
			
		||||
	if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
 | 
			
		||||
		if (age < c->pause_age)
 | 
			
		||||
			return (0);
 | 
			
		||||
		cp->flags |= CONTROL_PANE_PAUSED;
 | 
			
		||||
		control_discard_pane(c, cp);
 | 
			
		||||
		control_write(c, "%%pause %%%u", wp->id);
 | 
			
		||||
	} else {
 | 
			
		||||
		if (age < CONTROL_MAXIMUM_AGE)
 | 
			
		||||
			return (0);
 | 
			
		||||
		c->exit_message = xstrdup("too far behind");
 | 
			
		||||
		c->flags |= CLIENT_EXIT;
 | 
			
		||||
		control_discard(c);
 | 
			
		||||
	}
 | 
			
		||||
	return (1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write output from a pane. */
 | 
			
		||||
void
 | 
			
		||||
control_write_output(struct client *c, struct window_pane *wp)
 | 
			
		||||
@@ -314,7 +352,6 @@ control_write_output(struct client *c, struct window_pane *wp)
 | 
			
		||||
	struct control_pane	*cp;
 | 
			
		||||
	struct control_block	*cb;
 | 
			
		||||
	size_t			 new_size;
 | 
			
		||||
	uint64_t		 t;
 | 
			
		||||
 | 
			
		||||
	if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
@@ -328,20 +365,8 @@ control_write_output(struct client *c, struct window_pane *wp)
 | 
			
		||||
	cp = control_add_pane(c, wp);
 | 
			
		||||
	if (cp->flags & (CONTROL_PANE_OFF|CONTROL_PANE_PAUSED))
 | 
			
		||||
		goto ignore;
 | 
			
		||||
	if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
 | 
			
		||||
		cb = TAILQ_FIRST(&cp->blocks);
 | 
			
		||||
		if (cb != NULL) {
 | 
			
		||||
			t = get_timer();
 | 
			
		||||
			log_debug("%s: %s: %%%u is %lld behind", __func__,
 | 
			
		||||
			    c->name, wp->id, (long long)t - cb->t);
 | 
			
		||||
			if (cb->t < t - c->pause_age) {
 | 
			
		||||
				cp->flags |= CONTROL_PANE_PAUSED;
 | 
			
		||||
				control_discard_pane(c, cp);
 | 
			
		||||
				control_write(c, "%%pause %%%u", wp->id);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (control_check_age(c, wp, cp))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	window_pane_get_new_data(wp, &cp->queued, &new_size);
 | 
			
		||||
	if (new_size == 0)
 | 
			
		||||
 
 | 
			
		||||
@@ -55,9 +55,6 @@ 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,
 | 
			
		||||
@@ -1526,10 +1523,6 @@ server_client_check_pane_buffer(struct window_pane *wp)
 | 
			
		||||
		log_debug("%s: %s has %zu bytes used and %zu left for %%%u",
 | 
			
		||||
		    __func__, c->name, wpo->used - wp->base_offset, new_size,
 | 
			
		||||
		    wp->id);
 | 
			
		||||
		if (new_size > SERVER_CLIENT_PANE_LIMIT) {
 | 
			
		||||
			control_discard(c);
 | 
			
		||||
			c->flags |= CLIENT_EXIT;
 | 
			
		||||
		}
 | 
			
		||||
		if (wpo->used < minimum)
 | 
			
		||||
			minimum = wpo->used;
 | 
			
		||||
	}
 | 
			
		||||
@@ -1764,6 +1757,8 @@ server_client_check_exit(struct client *c)
 | 
			
		||||
{
 | 
			
		||||
	struct client_file	*cf;
 | 
			
		||||
	const char		*name = c->exit_session;
 | 
			
		||||
	char			*data;
 | 
			
		||||
	size_t			 size, msize;
 | 
			
		||||
 | 
			
		||||
	if (c->flags & (CLIENT_DEAD|CLIENT_EXITED))
 | 
			
		||||
		return;
 | 
			
		||||
@@ -1786,7 +1781,17 @@ server_client_check_exit(struct client *c)
 | 
			
		||||
 | 
			
		||||
	switch (c->exit_type) {
 | 
			
		||||
	case CLIENT_EXIT_RETURN:
 | 
			
		||||
		proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval);
 | 
			
		||||
		if (c->exit_message != NULL) {
 | 
			
		||||
			msize = strlen(c->exit_message) + 1;
 | 
			
		||||
			size = (sizeof c->retval) + msize;
 | 
			
		||||
		} else
 | 
			
		||||
			size = (sizeof c->retval);
 | 
			
		||||
		data = xmalloc(size);
 | 
			
		||||
		memcpy(data, &c->retval, sizeof c->retval);
 | 
			
		||||
		if (c->exit_message != NULL)
 | 
			
		||||
			memcpy(data + sizeof c->retval, c->exit_message, msize);
 | 
			
		||||
		proc_send(c->peer, MSG_EXIT, -1, data, size);
 | 
			
		||||
		free(data);
 | 
			
		||||
		break;
 | 
			
		||||
	case CLIENT_EXIT_SHUTDOWN:
 | 
			
		||||
		proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0);
 | 
			
		||||
@@ -1796,6 +1801,7 @@ server_client_check_exit(struct client *c)
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	free(c->exit_session);
 | 
			
		||||
	free(c->exit_message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Redraw timer callback. */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								tmux.1
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								tmux.1
									
									
									
									
									
								
							@@ -5727,29 +5727,32 @@ When a
 | 
			
		||||
client detaches, it prints a message.
 | 
			
		||||
This may be one of:
 | 
			
		||||
.Bl -tag -width Ds
 | 
			
		||||
.It [detached (from session ...)]
 | 
			
		||||
.It detached (from session ...)
 | 
			
		||||
The client was detached normally.
 | 
			
		||||
.It [detached and SIGHUP]
 | 
			
		||||
.It detached and SIGHUP
 | 
			
		||||
The client was detached and its parent sent the
 | 
			
		||||
.Dv SIGHUP
 | 
			
		||||
signal (for example with
 | 
			
		||||
.Ic detach-client
 | 
			
		||||
.Fl P ) .
 | 
			
		||||
.It [lost tty]
 | 
			
		||||
.It lost tty
 | 
			
		||||
The client's
 | 
			
		||||
.Xr tty 4
 | 
			
		||||
or
 | 
			
		||||
.Xr pty 4
 | 
			
		||||
was unexpectedly destroyed.
 | 
			
		||||
.It [terminated]
 | 
			
		||||
.It terminated
 | 
			
		||||
The client was killed with
 | 
			
		||||
.Dv SIGTERM .
 | 
			
		||||
.It [exited]
 | 
			
		||||
.It too far behind
 | 
			
		||||
The client is in control mode and became unable to keep up with the data from
 | 
			
		||||
.Nm .
 | 
			
		||||
.It exited
 | 
			
		||||
The server exited when it had no sessions.
 | 
			
		||||
.It [server exited]
 | 
			
		||||
.It server exited
 | 
			
		||||
The server exited when it received
 | 
			
		||||
.Dv SIGTERM .
 | 
			
		||||
.It [server exited unexpectedly]
 | 
			
		||||
.It server exited unexpectedly
 | 
			
		||||
The server crashed or otherwise exited without telling the client the reason.
 | 
			
		||||
.El
 | 
			
		||||
.Sh TERMINFO EXTENSIONS
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user