mirror of
https://github.com/tmux/tmux.git
synced 2025-03-27 00:18:49 +00:00
Support -c like sh(1) to execute a command, useful when tmux is a login
shell. Suggested by halex@. This includes another protocol version increase (the last for now) so again restart the tmux server before upgrading.
This commit is contained in:
parent
18ea820cb0
commit
9200a0be7a
2
client.c
2
client.c
@ -91,6 +91,8 @@ server_started:
|
|||||||
fatal("fcntl failed");
|
fatal("fcntl failed");
|
||||||
if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
|
if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
|
||||||
fatal("fcntl failed");
|
fatal("fcntl failed");
|
||||||
|
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
|
||||||
|
fatal("fcntl failed");
|
||||||
imsg_init(&cctx->ibuf, fd);
|
imsg_init(&cctx->ibuf, fd);
|
||||||
|
|
||||||
if (cmdflags & CMD_SENDENVIRON)
|
if (cmdflags & CMD_SENDENVIRON)
|
||||||
|
25
server-msg.c
25
server-msg.c
@ -20,6 +20,7 @@
|
|||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <paths.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@ -29,6 +30,7 @@
|
|||||||
|
|
||||||
void server_msg_command(struct client *, struct msg_command_data *);
|
void server_msg_command(struct client *, struct msg_command_data *);
|
||||||
void server_msg_identify(struct client *, struct msg_identify_data *, int);
|
void server_msg_identify(struct client *, struct msg_identify_data *, int);
|
||||||
|
void server_msg_shell(struct client *);
|
||||||
|
|
||||||
void printflike2 server_msg_command_error(struct cmd_ctx *, const char *, ...);
|
void printflike2 server_msg_command_error(struct cmd_ctx *, const char *, ...);
|
||||||
void printflike2 server_msg_command_print(struct cmd_ctx *, const char *, ...);
|
void printflike2 server_msg_command_print(struct cmd_ctx *, const char *, ...);
|
||||||
@ -113,6 +115,12 @@ server_msg_dispatch(struct client *c)
|
|||||||
if (strchr(environdata.var, '=') != NULL)
|
if (strchr(environdata.var, '=') != NULL)
|
||||||
environ_put(&c->environ, environdata.var);
|
environ_put(&c->environ, environdata.var);
|
||||||
break;
|
break;
|
||||||
|
case MSG_SHELL:
|
||||||
|
if (datalen != 0)
|
||||||
|
fatalx("bad MSG_SHELL size");
|
||||||
|
|
||||||
|
server_msg_shell(c);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
fatalx("unexpected message");
|
fatalx("unexpected message");
|
||||||
}
|
}
|
||||||
@ -248,3 +256,20 @@ server_msg_identify(struct client *c, struct msg_identify_data *data, int fd)
|
|||||||
|
|
||||||
c->flags |= CLIENT_TERMINAL;
|
c->flags |= CLIENT_TERMINAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
server_msg_shell(struct client *c)
|
||||||
|
{
|
||||||
|
struct msg_shell_data data;
|
||||||
|
const char *shell;
|
||||||
|
|
||||||
|
shell = options_get_string(&global_s_options, "default-shell");
|
||||||
|
|
||||||
|
if (*shell == '\0' || areshell(shell))
|
||||||
|
shell = _PATH_BSHELL;
|
||||||
|
if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell)
|
||||||
|
strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell);
|
||||||
|
|
||||||
|
server_write_client(c, MSG_SHELL, &data, sizeof data);
|
||||||
|
c->flags |= CLIENT_BAD; /* it will die after exec */
|
||||||
|
}
|
||||||
|
10
tmux.1
10
tmux.1
@ -24,6 +24,7 @@
|
|||||||
.Nm tmux
|
.Nm tmux
|
||||||
.Bk -words
|
.Bk -words
|
||||||
.Op Fl 28dlquv
|
.Op Fl 28dlquv
|
||||||
|
.Op Fl c Ar shell-command
|
||||||
.Op Fl f Ar file
|
.Op Fl f Ar file
|
||||||
.Op Fl L Ar socket-name
|
.Op Fl L Ar socket-name
|
||||||
.Op Fl S Ar socket-path
|
.Op Fl S Ar socket-path
|
||||||
@ -101,6 +102,15 @@ to assume the terminal supports 256 colours.
|
|||||||
Like
|
Like
|
||||||
.Fl 2 ,
|
.Fl 2 ,
|
||||||
but indicates that the terminal supports 88 colours.
|
but indicates that the terminal supports 88 colours.
|
||||||
|
.It Fl c Ar shell-command
|
||||||
|
Execute
|
||||||
|
.Ar shell-command
|
||||||
|
using the default shell.
|
||||||
|
If necessary, the
|
||||||
|
.Nm
|
||||||
|
server will be started to retrieve the
|
||||||
|
.Ic default-shell
|
||||||
|
option.
|
||||||
.It Fl d
|
.It Fl d
|
||||||
Force
|
Force
|
||||||
.Nm
|
.Nm
|
||||||
|
66
tmux.c
66
tmux.c
@ -57,13 +57,14 @@ int login_shell;
|
|||||||
__dead void usage(void);
|
__dead void usage(void);
|
||||||
char *makesockpath(const char *);
|
char *makesockpath(const char *);
|
||||||
int prepare_cmd(enum msgtype *, void **, size_t *, int, char **);
|
int prepare_cmd(enum msgtype *, void **, size_t *, int, char **);
|
||||||
int dispatch_imsg(struct client_ctx *, int *);
|
int dispatch_imsg(struct client_ctx *, const char *, int *);
|
||||||
|
__dead void shell_exec(const char *, const char *);
|
||||||
|
|
||||||
__dead void
|
__dead void
|
||||||
usage(void)
|
usage(void)
|
||||||
{
|
{
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"usage: %s [-28dlquv] [-f file] [-L socket-name]\n"
|
"usage: %s [-28dlquv] [-c shell-command] [-f file] [-L socket-name]\n"
|
||||||
" [-S socket-path] [command [flags]]\n",
|
" [-S socket-path] [command [flags]]\n",
|
||||||
__progname);
|
__progname);
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -275,17 +276,17 @@ main(int argc, char **argv)
|
|||||||
struct passwd *pw;
|
struct passwd *pw;
|
||||||
struct options *so, *wo;
|
struct options *so, *wo;
|
||||||
struct keylist *keylist;
|
struct keylist *keylist;
|
||||||
char *s, *path, *label, *home, *cause, **var;
|
char *s, *shellcmd, *path, *label, *home, *cause;
|
||||||
char cwd[MAXPATHLEN];
|
char cwd[MAXPATHLEN], **var;
|
||||||
void *buf;
|
void *buf;
|
||||||
size_t len;
|
size_t len;
|
||||||
int retcode, opt, flags, cmdflags = 0;
|
int retcode, opt, flags, cmdflags = 0;
|
||||||
int nfds;
|
int nfds;
|
||||||
|
|
||||||
flags = 0;
|
flags = 0;
|
||||||
label = path = NULL;
|
shellcmd = label = path = NULL;
|
||||||
login_shell = (**argv == '-');
|
login_shell = (**argv == '-');
|
||||||
while ((opt = getopt(argc, argv, "28df:lL:qS:uUv")) != -1) {
|
while ((opt = getopt(argc, argv, "28c:df:lL:qS:uUv")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case '2':
|
case '2':
|
||||||
flags |= IDENTIFY_256COLOURS;
|
flags |= IDENTIFY_256COLOURS;
|
||||||
@ -295,6 +296,11 @@ main(int argc, char **argv)
|
|||||||
flags |= IDENTIFY_88COLOURS;
|
flags |= IDENTIFY_88COLOURS;
|
||||||
flags &= ~IDENTIFY_256COLOURS;
|
flags &= ~IDENTIFY_256COLOURS;
|
||||||
break;
|
break;
|
||||||
|
case 'c':
|
||||||
|
if (shellcmd != NULL)
|
||||||
|
xfree(shellcmd);
|
||||||
|
shellcmd = xstrdup(optarg);
|
||||||
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
flags |= IDENTIFY_HASDEFAULTS;
|
flags |= IDENTIFY_HASDEFAULTS;
|
||||||
break;
|
break;
|
||||||
@ -332,6 +338,9 @@ main(int argc, char **argv)
|
|||||||
argc -= optind;
|
argc -= optind;
|
||||||
argv += optind;
|
argv += optind;
|
||||||
|
|
||||||
|
if (shellcmd != NULL && argc != 0)
|
||||||
|
usage();
|
||||||
|
|
||||||
log_open_tty(debug_level);
|
log_open_tty(debug_level);
|
||||||
siginit();
|
siginit();
|
||||||
|
|
||||||
@ -477,10 +486,16 @@ main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
xfree(label);
|
xfree(label);
|
||||||
|
|
||||||
if (prepare_cmd(&msg, &buf, &len, argc, argv) != 0)
|
if (shellcmd != NULL) {
|
||||||
|
msg = MSG_SHELL;
|
||||||
|
buf = NULL;
|
||||||
|
len = 0;
|
||||||
|
} else if (prepare_cmd(&msg, &buf, &len, argc, argv) != 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
if (argc == 0) /* new-session is the default */
|
if (shellcmd != NULL)
|
||||||
|
cmdflags |= CMD_STARTSERVER;
|
||||||
|
else if (argc == 0) /* new-session is the default */
|
||||||
cmdflags |= CMD_STARTSERVER|CMD_SENDENVIRON;
|
cmdflags |= CMD_STARTSERVER|CMD_SENDENVIRON;
|
||||||
else {
|
else {
|
||||||
/*
|
/*
|
||||||
@ -529,7 +544,7 @@ main(int argc, char **argv)
|
|||||||
fatalx("socket error");
|
fatalx("socket error");
|
||||||
|
|
||||||
if (pfd.revents & POLLIN) {
|
if (pfd.revents & POLLIN) {
|
||||||
if (dispatch_imsg(&cctx, &retcode) != 0)
|
if (dispatch_imsg(&cctx, shellcmd, &retcode) != 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,11 +561,12 @@ main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
dispatch_imsg(struct client_ctx *cctx, int *retcode)
|
dispatch_imsg(struct client_ctx *cctx, const char *shellcmd, int *retcode)
|
||||||
{
|
{
|
||||||
struct imsg imsg;
|
struct imsg imsg;
|
||||||
ssize_t n, datalen;
|
ssize_t n, datalen;
|
||||||
struct msg_print_data printdata;
|
struct msg_print_data printdata;
|
||||||
|
struct msg_shell_data shelldata;
|
||||||
|
|
||||||
if ((n = imsg_read(&cctx->ibuf)) == -1 || n == 0)
|
if ((n = imsg_read(&cctx->ibuf)) == -1 || n == 0)
|
||||||
fatalx("imsg_read failed");
|
fatalx("imsg_read failed");
|
||||||
@ -594,6 +610,13 @@ dispatch_imsg(struct client_ctx *cctx, int *retcode)
|
|||||||
"server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
|
"server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
|
||||||
*retcode = 1;
|
*retcode = 1;
|
||||||
return (-1);
|
return (-1);
|
||||||
|
case MSG_SHELL:
|
||||||
|
if (datalen != sizeof shelldata)
|
||||||
|
fatalx("bad MSG_SHELL size");
|
||||||
|
memcpy(&shelldata, imsg.data, sizeof shelldata);
|
||||||
|
shelldata.shell[(sizeof shelldata.shell) - 1] = '\0';
|
||||||
|
|
||||||
|
shell_exec(shelldata.shell, shellcmd);
|
||||||
default:
|
default:
|
||||||
fatalx("unexpected message");
|
fatalx("unexpected message");
|
||||||
}
|
}
|
||||||
@ -601,3 +624,26 @@ dispatch_imsg(struct client_ctx *cctx, int *retcode)
|
|||||||
imsg_free(&imsg);
|
imsg_free(&imsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__dead void
|
||||||
|
shell_exec(const char *shell, const char *shellcmd)
|
||||||
|
{
|
||||||
|
const char *shellname, *ptr;
|
||||||
|
char *argv0;
|
||||||
|
|
||||||
|
sigreset();
|
||||||
|
|
||||||
|
ptr = strrchr(shell, '/');
|
||||||
|
if (ptr != NULL && *(ptr + 1) != '\0')
|
||||||
|
shellname = ptr + 1;
|
||||||
|
else
|
||||||
|
shellname = shell;
|
||||||
|
if (login_shell)
|
||||||
|
xasprintf(&argv0, "-%s", shellname);
|
||||||
|
else
|
||||||
|
xasprintf(&argv0, "%s", shellname);
|
||||||
|
setenv("SHELL", shell, 1);
|
||||||
|
|
||||||
|
execl(shell, argv0, "-c", shellcmd, (char *) NULL);
|
||||||
|
fatal("execl failed");
|
||||||
|
}
|
||||||
|
10
tmux.h
10
tmux.h
@ -19,7 +19,7 @@
|
|||||||
#ifndef TMUX_H
|
#ifndef TMUX_H
|
||||||
#define TMUX_H
|
#define TMUX_H
|
||||||
|
|
||||||
#define PROTOCOL_VERSION 4
|
#define PROTOCOL_VERSION 5
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
@ -130,6 +130,7 @@ enum key_code {
|
|||||||
|
|
||||||
/* Function keys. */
|
/* Function keys. */
|
||||||
KEYC_F1,
|
KEYC_F1,
|
||||||
|
|
||||||
KEYC_F2,
|
KEYC_F2,
|
||||||
KEYC_F3,
|
KEYC_F3,
|
||||||
KEYC_F4,
|
KEYC_F4,
|
||||||
@ -308,7 +309,8 @@ enum msgtype {
|
|||||||
MSG_WAKEUP,
|
MSG_WAKEUP,
|
||||||
MSG_ENVIRON,
|
MSG_ENVIRON,
|
||||||
MSG_UNLOCK,
|
MSG_UNLOCK,
|
||||||
MSG_LOCK
|
MSG_LOCK,
|
||||||
|
MSG_SHELL
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -348,6 +350,10 @@ struct msg_environ_data {
|
|||||||
char var[ENVIRON_LENGTH];
|
char var[ENVIRON_LENGTH];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct msg_shell_data {
|
||||||
|
char shell[MAXPATHLEN];
|
||||||
|
};
|
||||||
|
|
||||||
/* Mode key commands. */
|
/* Mode key commands. */
|
||||||
enum mode_key_cmd {
|
enum mode_key_cmd {
|
||||||
MODEKEY_NONE,
|
MODEKEY_NONE,
|
||||||
|
17
tty.c
17
tty.c
@ -44,7 +44,6 @@ void tty_cell(struct tty *,
|
|||||||
void
|
void
|
||||||
tty_init(struct tty *tty, int fd, char *term)
|
tty_init(struct tty *tty, int fd, char *term)
|
||||||
{
|
{
|
||||||
int mode;
|
|
||||||
char *path;
|
char *path;
|
||||||
|
|
||||||
memset(tty, 0, sizeof *tty);
|
memset(tty, 0, sizeof *tty);
|
||||||
@ -55,10 +54,6 @@ tty_init(struct tty *tty, int fd, char *term)
|
|||||||
else
|
else
|
||||||
tty->termname = xstrdup(term);
|
tty->termname = xstrdup(term);
|
||||||
|
|
||||||
if ((mode = fcntl(fd, F_GETFL)) == -1)
|
|
||||||
fatal("fcntl failed");
|
|
||||||
if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
|
|
||||||
fatal("fcntl failed");
|
|
||||||
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
|
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
|
||||||
fatal("fcntl failed");
|
fatal("fcntl failed");
|
||||||
tty->fd = fd;
|
tty->fd = fd;
|
||||||
@ -129,11 +124,16 @@ void
|
|||||||
tty_start_tty(struct tty *tty)
|
tty_start_tty(struct tty *tty)
|
||||||
{
|
{
|
||||||
struct termios tio;
|
struct termios tio;
|
||||||
int what;
|
int what, mode;
|
||||||
|
|
||||||
if (tty->fd == -1)
|
if (tty->fd == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if ((mode = fcntl(tty->fd, F_GETFL)) == -1)
|
||||||
|
fatal("fcntl failed");
|
||||||
|
if (fcntl(tty->fd, F_SETFL, mode|O_NONBLOCK) == -1)
|
||||||
|
fatal("fcntl failed");
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
tty_detect_utf8(tty);
|
tty_detect_utf8(tty);
|
||||||
#endif
|
#endif
|
||||||
@ -183,6 +183,7 @@ void
|
|||||||
tty_stop_tty(struct tty *tty)
|
tty_stop_tty(struct tty *tty)
|
||||||
{
|
{
|
||||||
struct winsize ws;
|
struct winsize ws;
|
||||||
|
int mode;
|
||||||
|
|
||||||
if (!(tty->flags & TTY_STARTED))
|
if (!(tty->flags & TTY_STARTED))
|
||||||
return;
|
return;
|
||||||
@ -193,6 +194,10 @@ tty_stop_tty(struct tty *tty)
|
|||||||
* because the fd is invalid. Things like ssh -t can easily leave us
|
* because the fd is invalid. Things like ssh -t can easily leave us
|
||||||
* with a dead tty.
|
* with a dead tty.
|
||||||
*/
|
*/
|
||||||
|
if ((mode = fcntl(tty->fd, F_GETFL)) == -1)
|
||||||
|
return;
|
||||||
|
if (fcntl(tty->fd, F_SETFL, mode & ~O_NONBLOCK) == -1)
|
||||||
|
return;
|
||||||
if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1)
|
if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1)
|
||||||
return;
|
return;
|
||||||
if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1)
|
if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1)
|
||||||
|
Loading…
Reference in New Issue
Block a user