Infrastructure and commands to manage the environment for processes started

within tmux.

There is a global environment, copied from the external environment when the
server is started and each sesssion has an (initially empty) session
environment which overrides it.

New commands set-environment and show-environment manipulate or display the
environments.

A new session option, update-environment, is a space-separated list of
variables which are updated from the external environment into the session
environment every time a new session is created - the default is DISPLAY.
This commit is contained in:
Nicholas Marriott 2009-08-08 21:52:43 +00:00
parent e985629440
commit 6491274f60
19 changed files with 549 additions and 63 deletions

View File

@ -25,8 +25,9 @@ SRCS= attributes.c buffer-poll.c buffer.c cfg.c client-fn.c \
cmd-split-window.c cmd-start-server.c cmd-string.c cmd-if-shell.c \ cmd-split-window.c cmd-start-server.c cmd-string.c cmd-if-shell.c \
cmd-suspend-client.c cmd-swap-pane.c cmd-swap-window.c \ cmd-suspend-client.c cmd-swap-pane.c cmd-swap-window.c \
cmd-switch-client.c cmd-unbind-key.c cmd-unlink-window.c \ cmd-switch-client.c cmd-unbind-key.c cmd-unlink-window.c \
cmd-set-environment.c cmd-show-environment.c \
cmd-up-pane.c cmd-display-message.c cmd.c \ cmd-up-pane.c cmd-display-message.c cmd.c \
colour.c grid-view.c grid.c input-keys.c \ colour.c environ.c grid-view.c grid.c input-keys.c \
input.c key-bindings.c key-string.c layout-set.c layout.c log.c \ input.c key-bindings.c key-string.c layout-set.c layout.c log.c \
mode-key.c names.c options-cmd.c options.c paste.c procname.c \ mode-key.c names.c options-cmd.c options.c paste.c procname.c \
resize.c screen-redraw.c screen-write.c screen.c server-fn.c \ resize.c screen-redraw.c screen-write.c screen.c server-fn.c \

View File

@ -33,6 +33,7 @@
#include "tmux.h" #include "tmux.h"
void client_send_environ(struct client_ctx *);
void client_handle_winch(struct client_ctx *); void client_handle_winch(struct client_ctx *);
int int
@ -95,6 +96,8 @@ server_started:
cctx->srv_in = buffer_create(BUFSIZ); cctx->srv_in = buffer_create(BUFSIZ);
cctx->srv_out = buffer_create(BUFSIZ); cctx->srv_out = buffer_create(BUFSIZ);
if (cmdflags & CMD_SENDENVIRON)
client_send_environ(cctx);
if (isatty(STDIN_FILENO)) { if (isatty(STDIN_FILENO)) {
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)
fatal("ioctl(TIOCGWINSZ)"); fatal("ioctl(TIOCGWINSZ)");
@ -133,6 +136,19 @@ not_found:
return (1); return (1);
} }
void
client_send_environ(struct client_ctx *cctx)
{
char **var;
struct msg_environ_data data;
for (var = environ; *var != NULL; var++) {
if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var)
continue;
client_write_server(cctx, MSG_ENVIRON, &data, sizeof data);
}
}
int int
client_main(struct client_ctx *cctx) client_main(struct client_ctx *cctx)
{ {
@ -242,8 +258,8 @@ client_msg_dispatch(struct client_ctx *cctx)
if (hdr.size != sizeof printdata) if (hdr.size != sizeof printdata)
fatalx("bad MSG_PRINT size"); fatalx("bad MSG_PRINT size");
buffer_read(cctx->srv_in, &printdata, sizeof printdata); buffer_read(cctx->srv_in, &printdata, sizeof printdata);
printdata.msg[(sizeof printdata.msg) - 1] = '\0';
printdata.msg[(sizeof printdata.msg) - 1] = '\0';
cctx->errstr = xstrdup(printdata.msg); cctx->errstr = xstrdup(printdata.msg);
return (-1); return (-1);
case MSG_EXIT: case MSG_EXIT:

View File

@ -29,7 +29,7 @@ int cmd_attach_session_exec(struct cmd *, struct cmd_ctx *);
const struct cmd_entry cmd_attach_session_entry = { const struct cmd_entry cmd_attach_session_entry = {
"attach-session", "attach", "attach-session", "attach",
"[-d] " CMD_TARGET_SESSION_USAGE, "[-d] " CMD_TARGET_SESSION_USAGE,
CMD_CANTNEST|CMD_STARTSERVER, CMD_CHFLAG('d'), CMD_CANTNEST|CMD_STARTSERVER|CMD_SENDENVIRON, CMD_CHFLAG('d'),
cmd_target_init, cmd_target_init,
cmd_target_parse, cmd_target_parse,
cmd_attach_session_exec, cmd_attach_session_exec,
@ -43,6 +43,7 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx)
struct cmd_target_data *data = self->data; struct cmd_target_data *data = self->data;
struct session *s; struct session *s;
struct client *c; struct client *c;
const char *update;
char *overrides, *cause; char *overrides, *cause;
u_int i; u_int i;
@ -93,6 +94,10 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx)
ctx->cmdclient->session = s; ctx->cmdclient->session = s;
server_write_client(ctx->cmdclient, MSG_READY, NULL, 0); server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);
update = options_get_string(&s->options, "update-environment");
environ_update(update, &ctx->cmdclient->environ, &s->environ);
server_redraw_client(ctx->cmdclient); server_redraw_client(ctx->cmdclient);
} }
recalculate_sizes(); recalculate_sizes();

View File

@ -40,7 +40,7 @@ struct cmd_new_session_data {
const struct cmd_entry cmd_new_session_entry = { const struct cmd_entry cmd_new_session_entry = {
"new-session", "new", "new-session", "new",
"[-d] [-n window-name] [-s session-name] [command]", "[-d] [-n window-name] [-s session-name] [command]",
CMD_STARTSERVER|CMD_CANTNEST, 0, CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON, 0,
cmd_new_session_init, cmd_new_session_init,
cmd_new_session_parse, cmd_new_session_parse,
cmd_new_session_exec, cmd_new_session_exec,
@ -108,6 +108,8 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
{ {
struct cmd_new_session_data *data = self->data; struct cmd_new_session_data *data = self->data;
struct session *s; struct session *s;
struct environ env;
const char *update;
char *overrides, *cmd, *cwd, *cause; char *overrides, *cmd, *cwd, *cause;
int detached; int detached;
u_int sx, sy; u_int sx, sy;
@ -184,13 +186,20 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
else else
cmd = options_get_string(&global_s_options, "default-command"); cmd = options_get_string(&global_s_options, "default-command");
/* Construct the environment. */
environ_init(&env);
update = options_get_string(&global_s_options, "update-environment");
if (ctx->cmdclient != NULL)
environ_update(update, &ctx->cmdclient->environ, &env);
/* Create the new session. */ /* Create the new session. */
s = session_create(data->newname, cmd, cwd, sx, sy, &cause); s = session_create(data->newname, cmd, cwd, &env, sx, sy, &cause);
if (s == NULL) { if (s == NULL) {
ctx->error(ctx, "create session failed: %s", cause); ctx->error(ctx, "create session failed: %s", cause);
xfree(cause); xfree(cause);
return (-1); return (-1);
} }
environ_free(&env);
if (data->winname != NULL) { if (data->winname != NULL) {
xfree(s->curw->window->name); xfree(s->curw->window->name);

View File

@ -47,7 +47,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx)
struct window *w; struct window *w;
struct window_pane *wp; struct window_pane *wp;
struct session *s; struct session *s;
const char **env; struct environ env;
char *cause; char *cause;
if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL) if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL)
@ -64,7 +64,10 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx)
} }
} }
env = server_fill_environ(s); environ_init(&env);
environ_copy(&global_environ, &env);
environ_copy(&s->environ, &env);
server_fill_environ(s, &env);
wp = TAILQ_FIRST(&w->panes); wp = TAILQ_FIRST(&w->panes);
TAILQ_REMOVE(&w->panes, wp, entry); TAILQ_REMOVE(&w->panes, wp, entry);
@ -72,9 +75,10 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx)
window_destroy_panes(w); window_destroy_panes(w);
TAILQ_INSERT_HEAD(&w->panes, wp, entry); TAILQ_INSERT_HEAD(&w->panes, wp, entry);
window_pane_resize(wp, w->sx, w->sy); window_pane_resize(wp, w->sx, w->sy);
if (window_pane_spawn(wp, data->arg, NULL, env, &cause) != 0) { if (window_pane_spawn(wp, data->arg, NULL, &env, &cause) != 0) {
ctx->error(ctx, "respawn window failed: %s", cause); ctx->error(ctx, "respawn window failed: %s", cause);
xfree(cause); xfree(cause);
environ_free(&env);
return (-1); return (-1);
} }
layout_init(w); layout_init(w);
@ -84,5 +88,6 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx)
recalculate_sizes(); recalculate_sizes();
server_redraw_window(w); server_redraw_window(w);
environ_free(&env);
return (0); return (0);
} }

88
cmd-set-environment.c Normal file
View File

@ -0,0 +1,88 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Set an environment variable.
*/
int cmd_set_environment_exec(struct cmd *, struct cmd_ctx *);
const struct cmd_entry cmd_set_environment_entry = {
"set-environment", "setenv",
"[-gru] " CMD_OPTION_SESSION_USAGE,
0, CMD_CHFLAG('g')|CMD_CHFLAG('r')|CMD_CHFLAG('u'),
NULL,
cmd_option_parse,
cmd_set_environment_exec,
cmd_option_free,
cmd_option_print
};
int
cmd_set_environment_exec(struct cmd *self, struct cmd_ctx *ctx)
{
struct cmd_option_data *data = self->data;
struct session *s;
struct environ *env;
if (*data->option == '\0') {
ctx->error(ctx, "empty variable name");
return (-1);
}
if (strchr(data->option, '=') != NULL) {
ctx->error(ctx, "variable name contains =");
return (-1);
}
if (data->chflags & CMD_CHFLAG('g'))
env = &global_environ;
else {
if ((s = cmd_find_session(ctx, data->target)) == NULL)
return (-1);
env = &s->environ;
}
if (data->chflags & CMD_CHFLAG('u')) {
if (data->value != NULL) {
ctx->error(ctx, "can't specify a value with -u");
return (-1);
}
environ_unset(env, data->option);
} else if (data->chflags & CMD_CHFLAG('r')) {
if (data->value != NULL) {
ctx->error(ctx, "can't specify a value with -r");
return (-1);
}
environ_set(env, data->option, NULL);
} else {
if (data->value == NULL) {
ctx->error(ctx, "no value specified");
return (-1);
}
environ_set(env, data->option, data->value);
}
return (0);
}

View File

@ -85,6 +85,7 @@ const struct set_option_entry set_option_table[] = {
{ "status-right-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL }, { "status-right-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL },
{ "status-utf8", SET_OPTION_FLAG, 0, 0, NULL }, { "status-utf8", SET_OPTION_FLAG, 0, 0, NULL },
{ "terminal-overrides", SET_OPTION_STRING, 0, 0, NULL }, { "terminal-overrides", SET_OPTION_STRING, 0, 0, NULL },
{ "update-environment", SET_OPTION_STRING, 0, 0, NULL },
{ "visual-activity", SET_OPTION_FLAG, 0, 0, NULL }, { "visual-activity", SET_OPTION_FLAG, 0, 0, NULL },
{ "visual-bell", SET_OPTION_FLAG, 0, 0, NULL }, { "visual-bell", SET_OPTION_FLAG, 0, 0, NULL },
{ "visual-content", SET_OPTION_FLAG, 0, 0, NULL }, { "visual-content", SET_OPTION_FLAG, 0, 0, NULL },

67
cmd-show-environment.c Normal file
View File

@ -0,0 +1,67 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Show environment.
*/
int cmd_show_environment_exec(struct cmd *, struct cmd_ctx *);
const struct cmd_entry cmd_show_environment_entry = {
"show-environment", "showenv",
"[-g] " CMD_TARGET_SESSION_USAGE,
0, CMD_CHFLAG('g'),
cmd_target_init,
cmd_target_parse,
cmd_show_environment_exec,
cmd_target_free,
cmd_target_print
};
int
cmd_show_environment_exec(struct cmd *self, struct cmd_ctx *ctx)
{
struct cmd_target_data *data = self->data;
struct session *s;
struct environ *env;
struct environ_entry *envent;
if (data->chflags & CMD_CHFLAG('g'))
env = &global_environ;
else {
if ((s = cmd_find_session(ctx, data->target)) == NULL)
return (-1);
env = &s->environ;
}
RB_FOREACH(envent, environ, env) {
if (envent->value != NULL)
ctx->print(ctx, "%s=%s", envent->name, envent->value);
else
ctx->print(ctx, "-%s", envent->name);
}
return (0);
}

View File

@ -149,7 +149,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
struct winlink *wl; struct winlink *wl;
struct window *w; struct window *w;
struct window_pane *wp; struct window_pane *wp;
const char **env; struct environ env;
char *cmd, *cwd, *cause; char *cmd, *cwd, *cause;
u_int hlimit; u_int hlimit;
int size; int size;
@ -159,7 +159,10 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
return (-1); return (-1);
w = wl->window; w = wl->window;
env = server_fill_environ(s); environ_init(&env);
environ_copy(&global_environ, &env);
environ_copy(&s->environ, &env);
server_fill_environ(s, &env);
cmd = data->cmd; cmd = data->cmd;
if (cmd == NULL) if (cmd == NULL)
@ -181,7 +184,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
type = LAYOUT_LEFTRIGHT; type = LAYOUT_LEFTRIGHT;
wp = window_add_pane(w, hlimit); wp = window_add_pane(w, hlimit);
if (window_pane_spawn(wp, cmd, cwd, env, &cause) != 0) if (window_pane_spawn(wp, cmd, cwd, &env, &cause) != 0)
goto error; goto error;
if (layout_split_pane(w->active, type, size, wp) != 0) { if (layout_split_pane(w->active, type, size, wp) != 0) {
cause = xstrdup("pane too small"); cause = xstrdup("pane too small");
@ -197,9 +200,11 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
} else } else
server_status_session(s); server_status_session(s);
environ_free(&env);
return (0); return (0);
error: error:
environ_free(&env);
if (wp != NULL) if (wp != NULL)
window_remove_pane(w, wp); window_remove_pane(w, wp);
ctx->error(ctx, "create pane failed: %s", cause); ctx->error(ctx, "create pane failed: %s", cause);

View File

@ -59,21 +59,11 @@ int
cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause) cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause)
{ {
size_t p; size_t p;
int ch, argc, rval, have_arg; int ch, i, argc, rval, have_arg;
char **argv, *buf, *t, *u; char **argv, *buf, *t;
const char *whitespace, *equals;
size_t len; size_t len;
if ((t = strchr(s, ' ')) == NULL && (t = strchr(s, '\t')) == NULL)
t = strchr(s, '\0');
if ((u = strchr(s, '=')) != NULL && u < t) {
if (putenv(xstrdup(s)) != 0) {
xasprintf(cause, "assignment failed: %s", s);
return (-1);
}
*cmdlist = NULL;
return (0);
}
argv = NULL; argv = NULL;
argc = 0; argc = 0;
@ -147,6 +137,18 @@ cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause)
if (argc == 0) if (argc == 0)
goto out; goto out;
for (i = 0; i < argc; i++) {
equals = strchr(argv[i], '=');
whitespace = argv[i] + strcspn(argv[i], " \t");
if (equals == NULL || equals > whitespace)
break;
environ_put(&global_environ, argv[i]);
memmove(&argv[i], &argv[i + 1], argc - i - 1);
argc--;
}
if (argc == 0)
goto out;
*cmdlist = cmd_list_parse(argc, argv, cause); *cmdlist = cmd_list_parse(argc, argv, cause);
if (*cmdlist == NULL) if (*cmdlist == NULL)
goto out; goto out;

2
cmd.c
View File

@ -84,10 +84,12 @@ const struct cmd_entry *cmd_table[] = {
&cmd_send_prefix_entry, &cmd_send_prefix_entry,
&cmd_server_info_entry, &cmd_server_info_entry,
&cmd_set_buffer_entry, &cmd_set_buffer_entry,
&cmd_set_environment_entry,
&cmd_set_option_entry, &cmd_set_option_entry,
&cmd_set_password_entry, &cmd_set_password_entry,
&cmd_set_window_option_entry, &cmd_set_window_option_entry,
&cmd_show_buffer_entry, &cmd_show_buffer_entry,
&cmd_show_environment_entry,
&cmd_show_options_entry, &cmd_show_options_entry,
&cmd_show_window_options_entry, &cmd_show_window_options_entry,
&cmd_source_file_entry, &cmd_source_file_entry,

147
environ.c Normal file
View File

@ -0,0 +1,147 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Environment - manipulate a set of environment variables.
*/
RB_GENERATE(environ, environ_entry, entry, environ_cmp);
void environ_set1(struct environ *, char *, char *);
int
environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2)
{
return (strcmp(envent1->name, envent2->name));
}
void
environ_init(struct environ *env)
{
RB_INIT(env);
}
void
environ_free(struct environ *env)
{
struct environ_entry *envent;
while (!RB_EMPTY(env)) {
envent = RB_ROOT(env);
RB_REMOVE(environ, env, envent);
xfree(envent->name);
if (envent->value != NULL)
xfree(envent->value);
xfree(envent);
}
}
void
environ_copy(struct environ *srcenv, struct environ *dstenv)
{
struct environ_entry *envent;
RB_FOREACH(envent, environ, srcenv)
environ_set(dstenv, envent->name, envent->value);
}
struct environ_entry *
environ_find(struct environ *env, const char *name)
{
struct environ_entry envent;
envent.name = (char *) name;
return (RB_FIND(environ, env, &envent));
}
void
environ_set(struct environ *env, const char *name, const char *value)
{
struct environ_entry *envent;
if ((envent = environ_find(env, name)) != NULL) {
if (envent->value != NULL)
xfree(envent->value);
if (value != NULL)
envent->value = xstrdup(value);
else
envent->value = NULL;
} else {
envent = xmalloc(sizeof *envent);
envent->name = xstrdup(name);
if (value != NULL)
envent->value = xstrdup(value);
else
envent->value = NULL;
RB_INSERT(environ, env, envent);
}
}
void
environ_put(struct environ *env, const char *var)
{
char *name, *value;
value = strchr(var, '=');
if (value == NULL)
return;
value++;
name = xstrdup(var);
name[strcspn(name, "=")] = '\0';
environ_set(env, name, value);
xfree(name);
}
void
environ_unset(struct environ *env, const char *name)
{
struct environ_entry *envent;
if ((envent = environ_find(env, name)) == NULL)
return;
RB_REMOVE(environ, env, envent);
xfree(envent->name);
if (envent->value != NULL)
xfree(envent->value);
xfree(envent);
}
void
environ_update(const char *vars, struct environ *srcenv, struct environ *dstenv)
{
struct environ_entry *envent;
char *var, *next;
vars = next = xstrdup(vars);
while ((var = strsep(&next, " ")) != NULL) {
if ((envent = environ_find(srcenv, var)) == NULL)
environ_set(dstenv, var, NULL);
else
environ_set(dstenv, envent->name, envent->value);
}
xfree(vars);
}

View File

@ -26,25 +26,20 @@
int server_lock_callback(void *, const char *); int server_lock_callback(void *, const char *);
const char ** void
server_fill_environ(struct session *s) server_fill_environ(struct session *s, struct environ *env)
{ {
static const char *env[] = { NULL /* TMUX= */, NULL /* TERM */, NULL }; char tmuxvar[MAXPATHLEN], *term;
static char tmuxvar[MAXPATHLEN + 256], termvar[256];
u_int idx; u_int idx;
if (session_index(s, &idx) != 0) if (session_index(s, &idx) != 0)
fatalx("session not found"); fatalx("session not found");
xsnprintf(tmuxvar, sizeof tmuxvar, xsnprintf(tmuxvar, sizeof tmuxvar,
"TMUX=%s,%ld,%u", socket_path, (long) getpid(), idx); "%s,%ld,%u", socket_path, (long) getpid(), idx);
env[0] = tmuxvar; environ_set(env, "TMUX", tmuxvar);
xsnprintf(termvar, sizeof termvar, term = options_get_string(&s->options, "default-terminal");
"TERM=%s", options_get_string(&s->options, "default-terminal")); environ_set(env, "TERM", term);
env[1] = termvar;
return (env);
} }
void void

View File

@ -42,6 +42,7 @@ server_msg_dispatch(struct client *c)
struct msg_identify_data identifydata; struct msg_identify_data identifydata;
struct msg_resize_data resizedata; struct msg_resize_data resizedata;
struct msg_unlock_data unlockdata; struct msg_unlock_data unlockdata;
struct msg_environ_data environdata;
for (;;) { for (;;) {
if (BUFFER_USED(c->in) < sizeof hdr) if (BUFFER_USED(c->in) < sizeof hdr)
@ -100,6 +101,15 @@ server_msg_dispatch(struct client *c)
tty_start_tty(&c->tty); tty_start_tty(&c->tty);
server_redraw_client(c); server_redraw_client(c);
break; break;
case MSG_ENVIRON:
if (hdr.size != sizeof environdata)
fatalx("bad MSG_ENVIRON size");
buffer_read(c->in, &environdata, sizeof environdata);
environdata.var[(sizeof environdata.var) - 1] = '\0';
if (strchr(environdata.var, '=') != NULL)
environ_put(&c->environ, environdata.var);
break;
default: default:
fatalx("unexpected message"); fatalx("unexpected message");
} }

View File

@ -112,8 +112,8 @@ session_find(const char *name)
/* Create a new session. */ /* Create a new session. */
struct session * struct session *
session_create(const char *name, session_create(const char *name, const char *cmd, const char *cwd,
const char *cmd, const char *cwd, u_int sx, u_int sy, char **cause) struct environ *env, u_int sx, u_int sy, char **cause)
{ {
struct session *s; struct session *s;
u_int i; u_int i;
@ -128,6 +128,9 @@ session_create(const char *name,
SLIST_INIT(&s->alerts); SLIST_INIT(&s->alerts);
paste_init_stack(&s->buffers); paste_init_stack(&s->buffers);
options_init(&s->options, &global_s_options); options_init(&s->options, &global_s_options);
environ_init(&s->environ);
if (env != NULL)
environ_copy(env, &s->environ);
s->sx = sx; s->sx = sx;
s->sy = sy; s->sy = sy;
@ -171,6 +174,7 @@ session_destroy(struct session *s)
ARRAY_TRUNC(&sessions, 1); ARRAY_TRUNC(&sessions, 1);
session_alert_cancel(s, NULL); session_alert_cancel(s, NULL);
environ_free(&s->environ);
options_free(&s->options); options_free(&s->options);
paste_free_stack(&s->buffers); paste_free_stack(&s->buffers);
@ -200,15 +204,21 @@ session_new(struct session *s,
const char *name, const char *cmd, const char *cwd, int idx, char **cause) const char *name, const char *cmd, const char *cwd, int idx, char **cause)
{ {
struct window *w; struct window *w;
const char **env; struct environ env;
u_int hlimit; u_int hlimit;
env = server_fill_environ(s); environ_init(&env);
environ_copy(&global_environ, &env);
environ_copy(&s->environ, &env);
server_fill_environ(s, &env);
hlimit = options_get_number(&s->options, "history-limit"); hlimit = options_get_number(&s->options, "history-limit");
w = window_create(name, cmd, cwd, env, s->sx, s->sy, hlimit, cause); w = window_create(name, cmd, cwd, &env, s->sx, s->sy, hlimit, cause);
if (w == NULL) if (w == NULL) {
environ_free(&env);
return (NULL); return (NULL);
}
environ_free(&env);
if (options_get_number(&s->options, "set-remain-on-exit")) if (options_get_number(&s->options, "set-remain-on-exit"))
options_set_number(&w->options, "remain-on-exit", 1); options_set_number(&w->options, "remain-on-exit", 1);

66
tmux.1
View File

@ -1321,6 +1321,18 @@ entry for terminals which support 88 or 256 colours:
.Bd -literal -offset indent .Bd -literal -offset indent
"*88col*:colors=88,*256col*:colors=256" "*88col*:colors=88,*256col*:colors=256"
.Ed .Ed
.It Ic update-environment Ar variables
Set a space-separated string containing a list of environment variables to be
copied into the session environment when a new session is created or an
existing session is attached.
Any variables that do not exist in the source environment are set to be
removed from the session environment (as if
.Fl r
was given to the
.Ic set-environment
command).
The default is
.Ev DISPLAY .
.It Xo Ic visual-activity .It Xo Ic visual-activity
.Op Ic on | off .Op Ic on | off
.Xc .Xc
@ -1525,6 +1537,60 @@ or the global window options if
.Fl g .Fl g
is used. is used.
.El .El
.Sh ENVIRONMENT
When the server is started,
.Nm
copies the environment into the
.Em global environment ;
in addition, each session has a
.Em session environment .
When a window is created, the session and global environments are merged with
the session environment overriding any variable present in both.
This is the initial environment passed to the new process.
.Pp
The
.Ic update-environment
session option may be used to update the session environment from the client
when a new session is created or an old reattached.
.Nm
also initialises the
.Ev TMUX
variable with some internal information to allow commands to be executed
from inside, and the
.Ev TERM
variable with the correct terminal setting of
.Ql screen .
.Pp
Commands to alter and view the environment are:
.Bl -tag -width Ds
.It Xo Ic set-environment
.Op Fl gru
.Op Fl t Ar target-session
.Ar name Op Ar value
.Xc
Set or unset an environment variable.
If
.Fl g
is used, the change is made in the global environment; otherwise, it is applied
to the session environment for
.Ar target-session .
The
.Fl u
flag unsets a variable.
.Fl r
indicates the variable is to be removed from the environment before starting a
new process.
.It Xo Ic show-environment
.Op Fl g
.Op Fl t Ar target-session
.Xc
Display the environment for
.Ar target-session
or the global environment with
.Fl g .
Variables removed from the environment are prefixed with
.Ql - .
.El
.Sh STATUS LINE .Sh STATUS LINE
.Nm .Nm
includes an optional status line which is displayed in the bottom line of each includes an optional status line which is displayed in the bottom line of each

14
tmux.c
View File

@ -44,6 +44,7 @@ volatile sig_atomic_t sigusr2;
char *cfg_file; char *cfg_file;
struct options global_s_options; /* session options */ struct options global_s_options; /* session options */
struct options global_w_options; /* window options */ struct options global_w_options; /* window options */
struct environ global_environ;
int server_locked; int server_locked;
u_int password_failures; u_int password_failures;
@ -262,7 +263,7 @@ main(int argc, char **argv)
struct hdr hdr; struct hdr hdr;
struct passwd *pw; struct passwd *pw;
struct msg_print_data printdata; struct msg_print_data printdata;
char *s, *path, *label, *home, *cause; char *s, *path, *label, *home, *cause, **var;
char cwd[MAXPATHLEN]; char cwd[MAXPATHLEN];
void *buf; void *buf;
size_t len; size_t len;
@ -320,6 +321,10 @@ main(int argc, char **argv)
log_open_tty(debug_level); log_open_tty(debug_level);
siginit(); siginit();
environ_init(&global_environ);
for (var = environ; *var != NULL; var++)
environ_put(&global_environ, *var);
if (!(flags & IDENTIFY_UTF8)) { if (!(flags & IDENTIFY_UTF8)) {
/* /*
* If the user has set whichever of LC_ALL, LC_CTYPE or LANG * If the user has set whichever of LC_ALL, LC_CTYPE or LANG
@ -376,6 +381,7 @@ main(int argc, char **argv)
options_set_number(&global_s_options, "status-utf8", 0); options_set_number(&global_s_options, "status-utf8", 0);
options_set_string(&global_s_options, options_set_string(&global_s_options,
"terminal-overrides", "*88col*:colors=88,*256col*:colors=256"); "terminal-overrides", "*88col*:colors=88,*256col*:colors=256");
options_set_string(&global_s_options, "update-environment", "DISPLAY");
options_set_number(&global_s_options, "visual-activity", 0); options_set_number(&global_s_options, "visual-activity", 0);
options_set_number(&global_s_options, "visual-bell", 0); options_set_number(&global_s_options, "visual-bell", 0);
options_set_number(&global_s_options, "visual-content", 0); options_set_number(&global_s_options, "visual-content", 0);
@ -469,10 +475,10 @@ main(int argc, char **argv)
} }
cmdflags &= ~CMD_STARTSERVER; cmdflags &= ~CMD_STARTSERVER;
TAILQ_FOREACH(cmd, cmdlist, qentry) { TAILQ_FOREACH(cmd, cmdlist, qentry) {
if (cmd->entry->flags & CMD_STARTSERVER) { if (cmd->entry->flags & CMD_STARTSERVER)
cmdflags |= CMD_STARTSERVER; cmdflags |= CMD_STARTSERVER;
break; if (cmd->entry->flags & CMD_SENDENVIRON)
} cmdflags |= CMD_SENDENVIRON;
} }
cmd_list_free(cmdlist); cmd_list_free(cmdlist);
} }

46
tmux.h
View File

@ -39,6 +39,7 @@
#include "array.h" #include "array.h"
extern char *__progname; extern char *__progname;
extern char **environ;
/* Default configuration files. */ /* Default configuration files. */
#define DEFAULT_CFG ".tmux.conf" #define DEFAULT_CFG ".tmux.conf"
@ -69,6 +70,7 @@ extern char *__progname;
#define COMMAND_LENGTH 2048 /* packed argv size */ #define COMMAND_LENGTH 2048 /* packed argv size */
#define TERMINAL_LENGTH 128 /* length of TERM environment variable */ #define TERMINAL_LENGTH 128 /* length of TERM environment variable */
#define PRINT_LENGTH 512 /* printed error/message size */ #define PRINT_LENGTH 512 /* printed error/message size */
#define ENVIRON_LENGTH 1024 /* environment variable length */
/* Fatal errors. */ /* Fatal errors. */
#define fatal(msg) log_fatal("%s: %s", __func__, msg); #define fatal(msg) log_fatal("%s: %s", __func__, msg);
@ -302,6 +304,7 @@ enum msgtype {
MSG_SUSPEND, MSG_SUSPEND,
MSG_UNLOCK, MSG_UNLOCK,
MSG_WAKEUP, MSG_WAKEUP,
MSG_ENVIRON
}; };
/* /*
@ -356,6 +359,10 @@ struct msg_unlock_data {
char pass[PASS_MAX]; char pass[PASS_MAX];
}; };
struct msg_environ_data {
char var[ENVIRON_LENGTH];
};
/* Mode key commands. */ /* Mode key commands. */
enum mode_key_cmd { enum mode_key_cmd {
MODEKEY_NONE, MODEKEY_NONE,
@ -765,6 +772,15 @@ struct paste_buffer {
}; };
ARRAY_DECL(paste_stack, struct paste_buffer *); ARRAY_DECL(paste_stack, struct paste_buffer *);
/* Environment variable. */
struct environ_entry {
char *name;
char *value;
RB_ENTRY(environ_entry) entry;
};
RB_HEAD(environ, environ_entry);
/* Client session. */ /* Client session. */
struct session_alert { struct session_alert {
struct winlink *wl; struct winlink *wl;
@ -792,6 +808,8 @@ struct session {
#define SESSION_UNATTACHED 0x1 /* not attached to any clients */ #define SESSION_UNATTACHED 0x1 /* not attached to any clients */
int flags; int flags;
struct environ environ;
}; };
ARRAY_DECL(sessions, struct session *); ARRAY_DECL(sessions, struct session *);
@ -894,6 +912,8 @@ struct client {
struct buffer *in; struct buffer *in;
struct buffer *out; struct buffer *out;
struct environ environ;
char *title; char *title;
char *cwd; char *cwd;
@ -992,6 +1012,7 @@ struct cmd_entry {
#define CMD_CANTNEST 0x2 #define CMD_CANTNEST 0x2
#define CMD_ARG1 0x4 #define CMD_ARG1 0x4
#define CMD_ARG01 0x8 #define CMD_ARG01 0x8
#define CMD_SENDENVIRON 0x10
int flags; int flags;
#define CMD_CHFLAG(flag) \ #define CMD_CHFLAG(flag) \
@ -1074,6 +1095,7 @@ extern volatile sig_atomic_t sigusr1;
extern volatile sig_atomic_t sigusr2; extern volatile sig_atomic_t sigusr2;
extern struct options global_s_options; extern struct options global_s_options;
extern struct options global_w_options; extern struct options global_w_options;
extern struct environ global_environ;
extern char *cfg_file; extern char *cfg_file;
extern int server_locked; extern int server_locked;
extern u_int password_failures; extern u_int password_failures;
@ -1123,6 +1145,18 @@ char *options_get_string(struct options *, const char *);
void options_set_number(struct options *, const char *, long long); void options_set_number(struct options *, const char *, long long);
long long options_get_number(struct options *, const char *); long long options_get_number(struct options *, const char *);
/* environ.c */
int environ_cmp(struct environ_entry *, struct environ_entry *);
RB_PROTOTYPE(environ, environ_entry, entry, environ_cmp);
void environ_init(struct environ *);
void environ_free(struct environ *);
void environ_copy(struct environ *, struct environ *);
struct environ_entry *environ_find(struct environ *, const char *);
void environ_set(struct environ *, const char *, const char *);
void environ_put(struct environ *, const char *);
void environ_unset(struct environ *, const char *);
void environ_update(const char *, struct environ *, struct environ *);
/* tty.c */ /* tty.c */
u_char tty_get_acs(struct tty *, u_char); u_char tty_get_acs(struct tty *, u_char);
void tty_reset(struct tty *); void tty_reset(struct tty *);
@ -1285,10 +1319,12 @@ extern const struct cmd_entry cmd_send_keys_entry;
extern const struct cmd_entry cmd_send_prefix_entry; extern const struct cmd_entry cmd_send_prefix_entry;
extern const struct cmd_entry cmd_server_info_entry; extern const struct cmd_entry cmd_server_info_entry;
extern const struct cmd_entry cmd_set_buffer_entry; extern const struct cmd_entry cmd_set_buffer_entry;
extern const struct cmd_entry cmd_set_environment_entry;
extern const struct cmd_entry cmd_set_option_entry; extern const struct cmd_entry cmd_set_option_entry;
extern const struct cmd_entry cmd_set_password_entry; extern const struct cmd_entry cmd_set_password_entry;
extern const struct cmd_entry cmd_set_window_option_entry; extern const struct cmd_entry cmd_set_window_option_entry;
extern const struct cmd_entry cmd_show_buffer_entry; extern const struct cmd_entry cmd_show_buffer_entry;
extern const struct cmd_entry cmd_show_environment_entry;
extern const struct cmd_entry cmd_show_options_entry; extern const struct cmd_entry cmd_show_options_entry;
extern const struct cmd_entry cmd_show_window_options_entry; extern const struct cmd_entry cmd_show_window_options_entry;
extern const struct cmd_entry cmd_source_file_entry; extern const struct cmd_entry cmd_source_file_entry;
@ -1384,7 +1420,7 @@ int server_start(char *);
int server_msg_dispatch(struct client *); int server_msg_dispatch(struct client *);
/* server-fn.c */ /* server-fn.c */
const char **server_fill_environ(struct session *); void server_fill_environ(struct session *, struct environ *);
void server_write_error(struct client *, const char *); void server_write_error(struct client *, const char *);
void server_write_client( void server_write_client(
struct client *, enum msgtype, const void *, size_t); struct client *, enum msgtype, const void *, size_t);
@ -1554,8 +1590,8 @@ void winlink_stack_push(struct winlink_stack *, struct winlink *);
void winlink_stack_remove(struct winlink_stack *, struct winlink *); void winlink_stack_remove(struct winlink_stack *, struct winlink *);
int window_index(struct window *, u_int *); int window_index(struct window *, u_int *);
struct window *window_create1(u_int, u_int); struct window *window_create1(u_int, u_int);
struct window *window_create(const char *, const char *, struct window *window_create(const char *, const char *, const char *,
const char *, const char **, u_int, u_int, u_int, char **); struct environ *, u_int, u_int, u_int, char **);
void window_destroy(struct window *); void window_destroy(struct window *);
void window_set_active_pane(struct window *, struct window_pane *); void window_set_active_pane(struct window *, struct window_pane *);
struct window_pane *window_add_pane(struct window *, u_int); struct window_pane *window_add_pane(struct window *, u_int);
@ -1568,7 +1604,7 @@ void window_destroy_panes(struct window *);
struct window_pane *window_pane_create(struct window *, u_int, u_int, 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_destroy(struct window_pane *);
int window_pane_spawn(struct window_pane *, int window_pane_spawn(struct window_pane *,
const char *, const char *, const char **, char **); const char *, const char *, struct environ *, char **);
void window_pane_resize(struct window_pane *, u_int, u_int); void window_pane_resize(struct window_pane *, u_int, u_int);
int window_pane_set_mode( int window_pane_set_mode(
struct window_pane *, const struct window_mode *); struct window_pane *, const struct window_mode *);
@ -1648,7 +1684,7 @@ int session_alert_has(struct session *, struct winlink *, int);
int session_alert_has_window(struct session *, struct window *, int); int session_alert_has_window(struct session *, struct window *, int);
struct session *session_find(const char *); struct session *session_find(const char *);
struct session *session_create(const char *, const char *, struct session *session_create(const char *, const char *,
const char *, u_int, u_int, char **); const char *, struct environ *, u_int, u_int, char **);
void session_destroy(struct session *); void session_destroy(struct session *);
int session_index(struct session *, u_int *); int session_index(struct session *, u_int *);
struct winlink *session_new(struct session *, struct winlink *session_new(struct session *,

View File

@ -254,7 +254,7 @@ window_create1(u_int sx, u_int sy)
struct window * struct window *
window_create(const char *name, const char *cmd, const char *cwd, window_create(const char *name, const char *cmd, const char *cwd,
const char **envp, u_int sx, u_int sy, u_int hlimit, char **cause) struct environ *env, u_int sx, u_int sy, u_int hlimit, char **cause)
{ {
struct window *w; struct window *w;
struct window_pane *wp; struct window_pane *wp;
@ -262,7 +262,7 @@ window_create(const char *name, const char *cmd, const char *cwd,
w = window_create1(sx, sy); w = window_create1(sx, sy);
wp = window_add_pane(w, hlimit); wp = window_add_pane(w, hlimit);
layout_init(w); layout_init(w);
if (window_pane_spawn(wp, cmd, cwd, envp, cause) != 0) { if (window_pane_spawn(wp, cmd, cwd, env, cause) != 0) {
window_destroy(w); window_destroy(w);
return (NULL); return (NULL);
} }
@ -456,13 +456,16 @@ window_pane_destroy(struct window_pane *wp)
int int
window_pane_spawn(struct window_pane *wp, window_pane_spawn(struct window_pane *wp,
const char *cmd, const char *cwd, const char **envp, char **cause) const char *cmd, const char *cwd, struct environ *env, char **cause)
{ {
struct winsize ws; struct winsize ws;
int mode; int mode;
const char **envq, *ptr; char *argv0, **varp, *var;
char *argv0; ARRAY_DECL(, char *) varlist;
struct environ_entry *envent;
const char *ptr;
struct timeval tv; struct timeval tv;
u_int i;
if (wp->fd != -1) if (wp->fd != -1)
close(wp->fd); close(wp->fd);
@ -495,10 +498,22 @@ window_pane_spawn(struct window_pane *wp,
case 0: case 0:
if (chdir(wp->cwd) != 0) if (chdir(wp->cwd) != 0)
chdir("/"); chdir("/");
for (envq = envp; *envq != NULL; envq++) {
if (putenv(xstrdup(*envq)) != 0) ARRAY_INIT(&varlist);
fatal("putenv failed"); for (varp = environ; *varp != NULL; varp++) {
var = xstrdup(*varp);
var[strcspn(var, "=")] = '\0';
ARRAY_ADD(&varlist, var);
} }
for (i = 0; i < ARRAY_LENGTH(&varlist); i++) {
var = ARRAY_ITEM(&varlist, i);
unsetenv(var);
}
RB_FOREACH(envent, environ, env) {
if (envent->value != NULL)
setenv(envent->name, envent->value, 1);
}
sigreset(); sigreset();
log_close(); log_close();