If multiple arguments are given to new-session, new-window,

split-window, respawn-window or respawn-pane, pass them directly to
execvp() to help avoid quoting problems. One argument still goes to "sh
-c" like before. Requested by many over the years. Patch from J Raynor.
This commit is contained in:
nicm 2014-05-13 08:08:32 +00:00
parent b1a06ef22e
commit b3e8d440ed
12 changed files with 192 additions and 91 deletions

View File

@ -35,10 +35,10 @@ enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *);
const struct cmd_entry cmd_new_session_entry = {
"new-session", "new",
"Ac:dDF:n:Ps:t:x:y:", 0, 1,
"Ac:dDF:n:Ps:t:x:y:", 0, -1,
"[-AdDP] [-c start-directory] [-F format] [-n window-name] "
"[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] [-y height] "
"[command]",
"[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] "
"[-y height] [command]",
CMD_STARTSERVER|CMD_CANTNEST,
NULL,
cmd_new_session_exec
@ -55,8 +55,9 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
struct termios tio, *tiop;
const char *newname, *target, *update, *errstr, *template;
const char *path;
char *cmd, *cause, *cp;
char **argv, *cmd, *cause, *cp;
int detached, already_attached, idx, cwd, fd = -1;
int argc;
u_int sx, sy;
struct format_tree *ft;
struct environ_entry *envent;
@ -183,12 +184,21 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
sy = 1;
/* Figure out the command for the new window. */
if (target != NULL)
cmd = NULL;
else if (args->argc != 0)
cmd = args->argv[0];
else
argc = -1;
argv = NULL;
if (target == NULL && args->argc != 0) {
argc = args->argc;
argv = args->argv;
} else if (target == NULL) {
cmd = options_get_string(&global_s_options, "default-command");
if (cmd != NULL && *cmd != '\0') {
argc = 1;
argv = &cmd;
} else {
argc = 0;
argv = NULL;
}
}
path = NULL;
if (c != NULL && c->session == NULL)
@ -206,8 +216,8 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
/* Create the new session. */
idx = -1 - options_get_number(&global_s_options, "base-index");
s = session_create(newname, cmd, path, cwd, &env, tiop, idx, sx, sy,
&cause);
s = session_create(newname, argc, argv, path, cwd, &env, tiop, idx, sx,
sy, &cause);
if (s == NULL) {
cmdq_error(cmdq, "create session failed: %s", cause);
free(cause);
@ -216,7 +226,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
environ_free(&env);
/* Set the initial window name if one given. */
if (cmd != NULL && args_has(args, 'n')) {
if (argc >= 0 && args_has(args, 'n')) {
w = s->curw->window;
window_set_name(w, args_get(args, 'n'));
options_set_number(&w->options, "automatic-rename", 0);

View File

@ -34,7 +34,7 @@ enum cmd_retval cmd_new_window_exec(struct cmd *, struct cmd_q *);
const struct cmd_entry cmd_new_window_entry = {
"new-window", "neww",
"ac:dF:kn:Pt:", 0, 1,
"ac:dF:kn:Pt:", 0, -1,
"[-adkP] [-c start-directory] [-F format] [-n window-name] "
CMD_TARGET_WINDOW_USAGE " [command]",
0,
@ -50,8 +50,8 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq)
struct winlink *wl;
struct client *c;
const char *cmd, *path, *template;
char *cause, *cp;
int idx, last, detached, cwd, fd = -1;
char **argv, *cause, *cp;
int argc, idx, last, detached, cwd, fd = -1;
struct format_tree *ft;
struct environ_entry *envent;
@ -84,10 +84,19 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq)
}
detached = args_has(args, 'd');
if (args->argc == 0)
if (args->argc == 0) {
cmd = options_get_string(&s->options, "default-command");
else
cmd = args->argv[0];
if (cmd != NULL && *cmd != '\0') {
argc = 1;
argv = (char**)&cmd;
} else {
argc = 0;
argv = NULL;
}
} else {
argc = args->argc;
argv = args->argv;
}
path = NULL;
if (cmdq->client != NULL && cmdq->client->session == NULL)
@ -145,7 +154,8 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq)
if (idx == -1)
idx = -1 - options_get_number(&s->options, "base-index");
wl = session_new(s, args_get(args, 'n'), cmd, path, cwd, idx, &cause);
wl = session_new(s, args_get(args, 'n'), argc, argv, path, cwd, idx,
&cause);
if (wl == NULL) {
cmdq_error(cmdq, "create window failed: %s", cause);
free(cause);

View File

@ -32,7 +32,7 @@ enum cmd_retval cmd_respawn_pane_exec(struct cmd *, struct cmd_q *);
const struct cmd_entry cmd_respawn_pane_entry = {
"respawn-pane", "respawnp",
"kt:", 0, 1,
"kt:", 0, -1,
"[-k] " CMD_TARGET_PANE_USAGE " [command]",
0,
NULL,
@ -48,7 +48,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq)
struct window_pane *wp;
struct session *s;
struct environ env;
const char *cmd, *path;
const char *path;
char *cause;
u_int idx;
struct environ_entry *envent;
@ -74,11 +74,6 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq)
screen_reinit(&wp->base);
input_init(wp);
if (args->argc != 0)
cmd = args->argv[0];
else
cmd = NULL;
path = NULL;
if (cmdq->client != NULL && cmdq->client->session == NULL)
envent = environ_find(&cmdq->client->environ, "PATH");
@ -87,8 +82,8 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq)
if (envent != NULL)
path = envent->value;
if (window_pane_spawn(wp, cmd, path, NULL, -1, &env, s->tio,
&cause) != 0) {
if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env,
s->tio, &cause) != 0) {
cmdq_error(cmdq, "respawn pane failed: %s", cause);
free(cause);
environ_free(&env);

View File

@ -31,7 +31,7 @@ enum cmd_retval cmd_respawn_window_exec(struct cmd *, struct cmd_q *);
const struct cmd_entry cmd_respawn_window_entry = {
"respawn-window", "respawnw",
"kt:", 0, 1,
"kt:", 0, -1,
"[-k] " CMD_TARGET_WINDOW_USAGE " [command]",
0,
NULL,
@ -47,7 +47,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq)
struct window_pane *wp;
struct session *s;
struct environ env;
const char *cmd, *path;
const char *path;
char *cause;
struct environ_entry *envent;
@ -76,10 +76,6 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq)
window_destroy_panes(w);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
window_pane_resize(wp, w->sx, w->sy);
if (args->argc != 0)
cmd = args->argv[0];
else
cmd = NULL;
path = NULL;
if (cmdq->client != NULL && cmdq->client->session == NULL)
@ -89,8 +85,8 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq)
if (envent != NULL)
path = envent->value;
if (window_pane_spawn(wp, cmd, path, NULL, -1, &env, s->tio,
&cause) != 0) {
if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env,
s->tio, &cause) != 0) {
cmdq_error(cmdq, "respawn window failed: %s", cause);
free(cause);
environ_free(&env);

View File

@ -36,7 +36,7 @@ enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmd_q *);
const struct cmd_entry cmd_split_window_entry = {
"split-window", "splitw",
"c:dF:l:hp:Pt:v", 0, 1,
"c:dF:l:hp:Pt:v", 0, -1,
"[-dhvP] [-c start-directory] [-F format] [-p percentage|-l size] "
CMD_TARGET_PANE_USAGE " [command]",
0,
@ -62,9 +62,9 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq)
struct window_pane *wp, *new_wp = NULL;
struct environ env;
const char *cmd, *path, *shell, *template;
char *cause, *new_cause, *cp;
char **argv, *cause, *new_cause, *cp;
u_int hlimit;
int size, percentage, cwd, fd = -1;
int argc, size, percentage, cwd, fd = -1;
enum layout_type type;
struct layout_cell *lc;
struct client *c;
@ -81,10 +81,19 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq)
environ_copy(&s->environ, &env);
server_fill_environ(s, &env);
if (args->argc == 0)
if (args->argc == 0) {
cmd = options_get_string(&s->options, "default-command");
else
cmd = args->argv[0];
if (cmd != NULL && *cmd != '\0') {
argc = 1;
argv = (char**)&cmd;
} else {
argc = 0;
argv = NULL;
}
} else {
argc = args->argc;
argv = args->argv;
}
if (args_has(args, 'c')) {
ft = format_create();
@ -158,8 +167,8 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq)
if (envent != NULL)
path = envent->value;
if (window_pane_spawn(
new_wp, cmd, path, shell, cwd, &env, s->tio, &cause) != 0)
if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, &env,
s->tio, &cause) != 0)
goto error;
layout_assign_pane(lc, new_wp);

28
cmd.c
View File

@ -187,7 +187,7 @@ cmd_copy_argv(int argc, char **argv)
if (argc == 0)
return (NULL);
new_argv = xcalloc(argc, sizeof *new_argv);
new_argv = xcalloc(argc + 1, sizeof *new_argv);
for (i = 0; i < argc; i++) {
if (argv[i] != NULL)
new_argv[i] = xstrdup(argv[i]);
@ -207,6 +207,32 @@ cmd_free_argv(int argc, char **argv)
free(argv);
}
char *
cmd_stringify_argv(int argc, char **argv)
{
char *buf;
int i;
size_t len;
if (argc == 0)
return (xstrdup(""));
len = 0;
buf = NULL;
for (i = 0; i < argc; i++) {
len += strlen(argv[i]) + 1;
buf = xrealloc(buf, 1, len);
if (i == 0)
*buf = '\0';
else
strlcat(buf, " ", len);
strlcat(buf, argv[i], len);
}
return (buf);
}
struct cmd *
cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause)
{

View File

@ -368,7 +368,7 @@ format_get_command(struct window_pane *wp)
cmd = get_proc_name(wp->fd, wp->tty);
if (cmd == NULL || *cmd == '\0') {
free(cmd);
cmd = xstrdup(wp->cmd);
cmd = cmd_stringify_argv(wp->argc, wp->argv);
if (cmd == NULL || *cmd == '\0') {
free(cmd);
cmd = xstrdup(wp->shell);
@ -559,8 +559,10 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp)
if (wp->tty != NULL)
format_add(ft, "pane_tty", "%s", wp->tty);
format_add(ft, "pane_pid", "%ld", (long) wp->pid);
if (wp->cmd != NULL)
format_add(ft, "pane_start_command", "%s", wp->cmd);
if ((cmd = cmd_stringify_argv(wp->argc, wp->argv)) != NULL) {
format_add(ft, "pane_start_command", "%s", cmd);
free(cmd);
}
if ((cmd = format_get_command(wp)) != NULL) {
format_add(ft, "pane_current_command", "%s", cmd);
free(cmd);

12
names.c
View File

@ -68,9 +68,15 @@ window_name_callback(unused int fd, unused short events, void *data)
char *
default_window_name(struct window *w)
{
if (w->active->cmd != NULL && *w->active->cmd != '\0')
return (parse_window_name(w->active->cmd));
return (parse_window_name(w->active->shell));
char *cmd, *s;
cmd = cmd_stringify_argv(w->active->argc, w->active->argv);
if (cmd != NULL && *cmd != '\0')
s = parse_window_name(cmd);
else
s = parse_window_name(w->active->shell);
free(cmd);
return (s);
}
char *

View File

@ -85,11 +85,12 @@ session_find_by_id(u_int id)
/* Create a new session. */
struct session *
session_create(const char *name, const char *cmd, const char *path, int cwd,
struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy,
char **cause)
session_create(const char *name, int argc, char **argv, const char *path,
int cwd, struct environ *env, struct termios *tio, int idx, u_int sx,
u_int sy, char **cause)
{
struct session *s;
struct winlink *wl;
s = xmalloc(sizeof *s);
s->references = 0;
@ -132,8 +133,9 @@ session_create(const char *name, const char *cmd, const char *path, int cwd,
}
RB_INSERT(sessions, &sessions, s);
if (cmd != NULL) {
if (session_new(s, NULL, cmd, path, cwd, idx, cause) == NULL) {
if (argc >= 0) {
wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause);
if (wl == NULL) {
session_destroy(s);
return (NULL);
}
@ -227,7 +229,7 @@ session_previous_session(struct session *s)
/* Create a new window on a session. */
struct winlink *
session_new(struct session *s, const char *name, const char *cmd,
session_new(struct session *s, const char *name, int argc, char **argv,
const char *path, int cwd, int idx, char **cause)
{
struct window *w;
@ -251,8 +253,8 @@ session_new(struct session *s, const char *name, const char *cmd,
shell = _PATH_BSHELL;
hlimit = options_get_number(&s->options, "history-limit");
w = window_create(name, cmd, path, shell, cwd, &env, s->tio, s->sx,
s->sy, hlimit, cause);
w = window_create(name, argc, argv, path, shell, cwd, &env, s->tio,
s->sx, s->sy, hlimit, cause);
if (w == NULL) {
winlink_remove(&s->windows, wl);
environ_free(&env);

29
tmux.1
View File

@ -478,12 +478,37 @@ It may be used alone to target a pane or the window containing it.
arguments are
.Xr sh 1
commands.
These must be passed as a single item, which typically means quoting them, for
example:
This may be a single argument passed to the shell, for example:
.Bd -literal -offset indent
new-window 'vi /etc/passwd'
.Ed
.Pp
Will run:
.Bd -literal -offset indent
/bin/sh -c 'vi /etc/passwd'
.Ed
.Pp
Additionally, the
.Ic new-window ,
.Ic new-session ,
.Ic split-window ,
.Ic respawn-window
and
.Ic respawn-pane
commands allow
.Ar shell-command
to be given as multiple arguments and executed directly (without
.Ql sh -c ) .
This can avoid issues with shell quoting.
For example:
.Bd -literal -offset indent
$ tmux new-window vi /etc/passwd
.Ed
.Pp
Will run
.Xr vi 1
directly without invoking the shell.
.Pp
.Ar command
.Op Ar arguments
refers to a

20
tmux.h
View File

@ -914,7 +914,8 @@ struct window_pane {
#define PANE_RESIZE 0x8
#define PANE_FOCUSPUSH 0x10
char *cmd;
int argc;
char **argv;
char *shell;
int cwd;
@ -1750,6 +1751,7 @@ int cmd_pack_argv(int, char **, char *, size_t);
int cmd_unpack_argv(char *, size_t, int, char ***);
char **cmd_copy_argv(int, char **);
void cmd_free_argv(int, char **);
char *cmd_stringify_argv(int, char **);
struct cmd *cmd_parse(int, char **, const char *, u_int, char **);
size_t cmd_print(struct cmd *, char *, size_t);
struct session *cmd_current_session(struct cmd_q *, int);
@ -2140,7 +2142,7 @@ void winlink_stack_remove(struct winlink_stack *, struct winlink *);
int window_index(struct window *, u_int *);
struct window *window_find_by_id(u_int);
struct window *window_create1(u_int, u_int);
struct window *window_create(const char *, const char *, const char *,
struct window *window_create(const char *, int, char **, const char *,
const char *, int, struct environ *, struct termios *,
u_int, u_int, u_int, char **);
void window_destroy(struct window *);
@ -2166,7 +2168,7 @@ struct window_pane *window_pane_find_by_id(u_int);
struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int);
void window_pane_destroy(struct window_pane *);
void window_pane_timer_start(struct window_pane *);
int window_pane_spawn(struct window_pane *, const char *,
int window_pane_spawn(struct window_pane *, int, char **,
const char *, const char *, int, struct environ *,
struct termios *, char **);
void window_pane_resize(struct window_pane *, u_int, u_int);
@ -2304,18 +2306,18 @@ RB_PROTOTYPE(sessions, session, entry, session_cmp);
int session_alive(struct session *);
struct session *session_find(const char *);
struct session *session_find_by_id(u_int);
struct session *session_create(const char *, const char *, const char *, int,
struct environ *, struct termios *, int, u_int, u_int,
char **);
struct session *session_create(const char *, int, char **, const char *,
int, struct environ *, struct termios *, int, u_int,
u_int, char **);
void session_destroy(struct session *);
int session_check_name(const char *);
void session_update_activity(struct session *);
struct session *session_next_session(struct session *);
struct session *session_previous_session(struct session *);
struct winlink *session_new(struct session *, const char *, const char *,
struct winlink *session_new(struct session *, const char *, int, char **,
const char *, int, int, char **);
struct winlink *session_attach(
struct session *, struct window *, int, char **);
struct winlink *session_attach(struct session *, struct window *, int,
char **);
int session_detach(struct session *, struct winlink *);
struct winlink *session_has(struct session *, struct window *);
int session_next(struct session *, int);

View File

@ -307,7 +307,7 @@ window_create1(u_int sx, u_int sy)
}
struct window *
window_create(const char *name, const char *cmd, const char *path,
window_create(const char *name, int argc, char **argv, const char *path,
const char *shell, int cwd, struct environ *env, struct termios *tio,
u_int sx, u_int sy, u_int hlimit, char **cause)
{
@ -318,7 +318,7 @@ window_create(const char *name, const char *cmd, const char *path,
wp = window_add_pane(w, hlimit);
layout_init(w, wp);
if (window_pane_spawn(wp, cmd, path, shell, cwd, env, tio,
if (window_pane_spawn(wp, argc, argv, path, shell, cwd, env, tio,
cause) != 0) {
window_destroy(w);
return (NULL);
@ -678,7 +678,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
wp->id = next_window_pane_id++;
RB_INSERT(window_pane_tree, &all_window_panes, wp);
wp->cmd = NULL;
wp->argc = 0;
wp->argv = NULL;
wp->shell = NULL;
wp->cwd = -1;
@ -737,27 +738,29 @@ window_pane_destroy(struct window_pane *wp)
close(wp->cwd);
free(wp->shell);
free(wp->cmd);
cmd_free_argv(wp->argc, wp->argv);
free(wp);
}
int
window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path,
const char *shell, int cwd, struct environ *env, struct termios *tio,
char **cause)
window_pane_spawn(struct window_pane *wp, int argc, char **argv,
const char *path, const char *shell, int cwd, struct environ *env,
struct termios *tio, char **cause)
{
struct winsize ws;
char *argv0, paneid[16];
const char *ptr;
char *argv0, *cmd, **argvp, paneid[16];
const char *ptr, *first;
struct termios tio2;
int i;
if (wp->fd != -1) {
bufferevent_free(wp->event);
close(wp->fd);
}
if (cmd != NULL) {
free(wp->cmd);
wp->cmd = xstrdup(cmd);
if (argc > 0) {
cmd_free_argv(wp->argc, wp->argv);
wp->argc = argc;
wp->argv = cmd_copy_argv(argc, argv);
}
if (shell != NULL) {
free(wp->shell);
@ -768,7 +771,10 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path,
wp->cwd = dup(cwd);
}
log_debug("spawn: %s -- %s", wp->shell, wp->cmd);
cmd = cmd_stringify_argv(wp->argc, wp->argv);
log_debug("spawn: %s -- %s", wp->shell, cmd);
for (i = 0; i < wp->argc; i++)
log_debug("spawn: argv[%d] = %s", i, wp->argv[i]);
memset(&ws, 0, sizeof ws);
ws.ws_col = screen_size_x(&wp->base);
@ -778,6 +784,7 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path,
case -1:
wp->fd = -1;
xasprintf(cause, "%s: %s", cmd, strerror(errno));
free(cmd);
return (-1);
case 0:
if (fchdir(wp->cwd) != 0)
@ -805,31 +812,42 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path,
setenv("SHELL", wp->shell, 1);
ptr = strrchr(wp->shell, '/');
if (*wp->cmd != '\0') {
/* Use the command. */
/*
* If given one argument, assume it should be passed to sh -c;
* with more than one argument, use execvp(). If there is no
* arguments, create a login shell.
*/
if (wp->argc > 0) {
if (wp->argc != 1) {
/* Copy to ensure argv ends in NULL. */
argvp = cmd_copy_argv(wp->argc, wp->argv);
execvp(argvp[0], argvp);
fatal("execvp failed");
}
first = wp->argv[0];
if (ptr != NULL && *(ptr + 1) != '\0')
xasprintf(&argv0, "%s", ptr + 1);
else
xasprintf(&argv0, "%s", wp->shell);
execl(wp->shell, argv0, "-c", wp->cmd, (char *) NULL);
execl(wp->shell, argv0, "-c", first, (char *)NULL);
fatal("execl failed");
}
/* No command; fork a login shell. */
if (ptr != NULL && *(ptr + 1) != '\0')
xasprintf(&argv0, "-%s", ptr + 1);
else
xasprintf(&argv0, "-%s", wp->shell);
execl(wp->shell, argv0, (char *) NULL);
execl(wp->shell, argv0, (char *)NULL);
fatal("execl failed");
}
setblocking(wp->fd, 0);
wp->event = bufferevent_new(wp->fd,
window_pane_read_callback, NULL, window_pane_error_callback, wp);
wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL,
window_pane_error_callback, wp);
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
free(cmd);
return (0);
}