1
0
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:
Nicholas Marriott 2009-09-23 12:03:30 +00:00
parent 18ea820cb0
commit 9200a0be7a
6 changed files with 112 additions and 18 deletions

View File

@ -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)

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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)