mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 00:56:10 +00:00 
			
		
		
		
	Add -E to detach-client to exec a command to replace the client instead
of exiting it, useful if tmux wasn't exec'd itself. From Jenna Magius.
This commit is contained in:
		
							
								
								
									
										20
									
								
								client.c
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								client.c
									
									
									
									
									
								
							@@ -51,6 +51,8 @@ static enum {
 | 
			
		||||
static int		 client_exitval;
 | 
			
		||||
static enum msgtype	 client_exittype;
 | 
			
		||||
static const char	*client_exitsession;
 | 
			
		||||
static const char	*client_execshell;
 | 
			
		||||
static const char	*client_execcmd;
 | 
			
		||||
static int		 client_attached;
 | 
			
		||||
 | 
			
		||||
static __dead void	 client_exec(const char *,const char *);
 | 
			
		||||
@@ -358,6 +360,14 @@ client_main(struct event_base *base, int argc, char **argv, int flags,
 | 
			
		||||
	/* Start main loop. */
 | 
			
		||||
	proc_loop(client_proc, NULL);
 | 
			
		||||
 | 
			
		||||
	/* Run command if user requested exec, instead of exiting. */
 | 
			
		||||
	if (client_exittype == MSG_EXEC) {
 | 
			
		||||
		if (client_flags & CLIENT_CONTROLCONTROL)
 | 
			
		||||
			tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio);
 | 
			
		||||
		clear_signals(0);
 | 
			
		||||
		client_exec(client_execshell, client_execcmd);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Print the exit message, if any, and exit. */
 | 
			
		||||
	if (client_attached) {
 | 
			
		||||
		if (client_exitreason != CLIENT_EXIT_NONE)
 | 
			
		||||
@@ -655,6 +665,16 @@ client_dispatch_attached(struct imsg *imsg)
 | 
			
		||||
			client_exitreason = CLIENT_EXIT_DETACHED;
 | 
			
		||||
		proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_EXEC:
 | 
			
		||||
		if (datalen == 0 || data[datalen - 1] != '\0' ||
 | 
			
		||||
		    strlen(data) + 1 == (size_t)datalen)
 | 
			
		||||
			fatalx("bad MSG_EXEC string");
 | 
			
		||||
		client_execcmd = xstrdup(data);
 | 
			
		||||
		client_execshell = xstrdup(data + strlen(data) + 1);
 | 
			
		||||
 | 
			
		||||
		client_exittype = imsg->hdr.type;
 | 
			
		||||
		proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_EXIT:
 | 
			
		||||
		if (datalen != 0 && datalen != sizeof (int))
 | 
			
		||||
			fatalx("bad MSG_EXIT size");
 | 
			
		||||
 
 | 
			
		||||
@@ -33,8 +33,9 @@ const struct cmd_entry cmd_detach_client_entry = {
 | 
			
		||||
	.name = "detach-client",
 | 
			
		||||
	.alias = "detach",
 | 
			
		||||
 | 
			
		||||
	.args = { "as:t:P", 0, 0 },
 | 
			
		||||
	.usage = "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE,
 | 
			
		||||
	.args = { "aE:s:t:P", 0, 0 },
 | 
			
		||||
	.usage = "[-aP] [-E shell-command] "
 | 
			
		||||
	         "[-s target-session] " CMD_TARGET_CLIENT_USAGE,
 | 
			
		||||
 | 
			
		||||
	.sflag = CMD_SESSION,
 | 
			
		||||
	.tflag = CMD_CLIENT,
 | 
			
		||||
@@ -63,6 +64,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
	struct client	*c = item->state.c, *cloop;
 | 
			
		||||
	struct session	*s;
 | 
			
		||||
	enum msgtype	 msgtype;
 | 
			
		||||
	const char	*cmd = args_get(args, 'E');
 | 
			
		||||
 | 
			
		||||
	if (self->entry == &cmd_suspend_client_entry) {
 | 
			
		||||
		tty_stop_tty(&c->tty);
 | 
			
		||||
@@ -79,20 +81,31 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
	if (args_has(args, 's')) {
 | 
			
		||||
		s = item->state.sflag.s;
 | 
			
		||||
		TAILQ_FOREACH(cloop, &clients, entry) {
 | 
			
		||||
			if (cloop->session == s)
 | 
			
		||||
				server_client_detach(cloop, msgtype);
 | 
			
		||||
			if (cloop->session == s) {
 | 
			
		||||
				if (cmd != NULL)
 | 
			
		||||
					server_client_exec(cloop, cmd);
 | 
			
		||||
				else
 | 
			
		||||
					server_client_detach(cloop, msgtype);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return (CMD_RETURN_STOP);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (args_has(args, 'a')) {
 | 
			
		||||
		TAILQ_FOREACH(cloop, &clients, entry) {
 | 
			
		||||
			if (cloop->session != NULL && cloop != c)
 | 
			
		||||
				server_client_detach(cloop, msgtype);
 | 
			
		||||
			if (cloop->session != NULL && cloop != c) {
 | 
			
		||||
				if (cmd != NULL)
 | 
			
		||||
					server_client_exec(cloop, cmd);
 | 
			
		||||
				else
 | 
			
		||||
					server_client_detach(cloop, msgtype);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return (CMD_RETURN_NORMAL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	server_client_detach(c, msgtype);
 | 
			
		||||
	if (cmd != NULL)
 | 
			
		||||
		server_client_exec(c, cmd);
 | 
			
		||||
	else
 | 
			
		||||
		server_client_detach(c, msgtype);
 | 
			
		||||
	return (CMD_RETURN_STOP);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -296,6 +296,32 @@ server_client_detach(struct client *c, enum msgtype msgtype)
 | 
			
		||||
	proc_send_s(c->peer, msgtype, s->name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Execute command to replace a client, */
 | 
			
		||||
void
 | 
			
		||||
server_client_exec(struct client *c, const char *cmd)
 | 
			
		||||
{
 | 
			
		||||
	struct session	*s = c->session;
 | 
			
		||||
	char		*msg, *shell;
 | 
			
		||||
	size_t		 cmdsize, shellsize;
 | 
			
		||||
 | 
			
		||||
	if (*cmd == '\0')
 | 
			
		||||
		return;
 | 
			
		||||
	cmdsize = strlen(cmd) + 1;
 | 
			
		||||
 | 
			
		||||
	if (s != NULL)
 | 
			
		||||
		shell = options_get_string(s->options, "default-shell");
 | 
			
		||||
	else
 | 
			
		||||
		shell = options_get_string(global_s_options, "default-shell");
 | 
			
		||||
	shellsize = strlen(shell) + 1;
 | 
			
		||||
 | 
			
		||||
	msg = xmalloc(cmdsize + shellsize);
 | 
			
		||||
	memcpy(msg, cmd, cmdsize);
 | 
			
		||||
	memcpy(msg + cmdsize, shell, shellsize);
 | 
			
		||||
 | 
			
		||||
	proc_send(c->peer, MSG_EXEC, -1, msg, cmdsize + shellsize);
 | 
			
		||||
	free(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check for mouse keys. */
 | 
			
		||||
static key_code
 | 
			
		||||
server_client_check_mouse(struct client *c)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								tmux.1
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								tmux.1
									
									
									
									
									
								
							@@ -724,6 +724,7 @@ is used, the
 | 
			
		||||
option will not be applied.
 | 
			
		||||
.It Xo Ic detach-client
 | 
			
		||||
.Op Fl aP
 | 
			
		||||
.Op Fl E Ar shell-command
 | 
			
		||||
.Op Fl s Ar target-session
 | 
			
		||||
.Op Fl t Ar target-client
 | 
			
		||||
.Xc
 | 
			
		||||
@@ -740,6 +741,11 @@ If
 | 
			
		||||
.Fl P
 | 
			
		||||
is given, send SIGHUP to the parent process of the client, typically causing it
 | 
			
		||||
to exit.
 | 
			
		||||
With
 | 
			
		||||
.Fl E ,
 | 
			
		||||
run
 | 
			
		||||
.Ar shell-command
 | 
			
		||||
to replace the client.
 | 
			
		||||
.It Ic has-session Op Fl t Ar target-session
 | 
			
		||||
.D1 (alias: Ic has )
 | 
			
		||||
Report an error and exit with 1 if the specified session does not exist.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								tmux.h
									
									
									
									
									
								
							@@ -443,6 +443,7 @@ enum msgtype {
 | 
			
		||||
	MSG_SUSPEND,
 | 
			
		||||
	MSG_UNLOCK,
 | 
			
		||||
	MSG_WAKEUP,
 | 
			
		||||
	MSG_EXEC,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -1880,6 +1881,7 @@ int	 server_client_open(struct client *, char **);
 | 
			
		||||
void	 server_client_unref(struct client *);
 | 
			
		||||
void	 server_client_lost(struct client *);
 | 
			
		||||
void	 server_client_detach(struct client *, enum msgtype);
 | 
			
		||||
void	 server_client_exec(struct client *, const char *);
 | 
			
		||||
void	 server_client_loop(void);
 | 
			
		||||
void	 server_client_push_stdout(struct client *);
 | 
			
		||||
void	 server_client_push_stderr(struct client *);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user