summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--client.c18
-rw-r--r--cmd-attach-session.c7
-rw-r--r--cmd-new-session.c13
-rw-r--r--cmd-respawn-window.c11
-rw-r--r--cmd-set-environment.c88
-rw-r--r--cmd-set-option.c1
-rw-r--r--cmd-show-environment.c67
-rw-r--r--cmd-split-window.c11
-rw-r--r--cmd-string.c28
-rw-r--r--cmd.c2
-rw-r--r--environ.c147
-rw-r--r--server-fn.c21
-rw-r--r--server-msg.c10
-rw-r--r--session.c22
-rw-r--r--tmux.166
-rw-r--r--tmux.c14
-rw-r--r--tmux.h46
-rw-r--r--window.c37
19 files changed, 549 insertions, 63 deletions
diff --git a/Makefile b/Makefile
index ccd472cf..bec2a8bb 100644
--- a/Makefile
+++ b/Makefile
@@ -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-suspend-client.c cmd-swap-pane.c cmd-swap-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 \
- 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 \
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 \
diff --git a/client.c b/client.c
index 829b3d3c..4e756981 100644
--- a/client.c
+++ b/client.c
@@ -33,6 +33,7 @@
#include "tmux.h"
+void client_send_environ(struct client_ctx *);
void client_handle_winch(struct client_ctx *);
int
@@ -95,6 +96,8 @@ server_started:
cctx->srv_in = buffer_create(BUFSIZ);
cctx->srv_out = buffer_create(BUFSIZ);
+ if (cmdflags & CMD_SENDENVIRON)
+ client_send_environ(cctx);
if (isatty(STDIN_FILENO)) {
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)
fatal("ioctl(TIOCGWINSZ)");
@@ -133,6 +136,19 @@ not_found:
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
client_main(struct client_ctx *cctx)
{
@@ -242,8 +258,8 @@ client_msg_dispatch(struct client_ctx *cctx)
if (hdr.size != sizeof printdata)
fatalx("bad MSG_PRINT size");
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);
return (-1);
case MSG_EXIT:
diff --git a/cmd-attach-session.c b/cmd-attach-session.c
index 4604ee27..92be3085 100644
--- a/cmd-attach-session.c
+++ b/cmd-attach-session.c
@@ -29,7 +29,7 @@ int cmd_attach_session_exec(struct cmd *, struct cmd_ctx *);
const struct cmd_entry cmd_attach_session_entry = {
"attach-session", "attach",
"[-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_parse,
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 session *s;
struct client *c;
+ const char *update;
char *overrides, *cause;
u_int i;
@@ -93,6 +94,10 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx)
ctx->cmdclient->session = s;
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);
}
recalculate_sizes();
diff --git a/cmd-new-session.c b/cmd-new-session.c
index c1ba6e30..61f18c8f 100644
--- a/cmd-new-session.c
+++ b/cmd-new-session.c
@@ -40,7 +40,7 @@ struct cmd_new_session_data {
const struct cmd_entry cmd_new_session_entry = {
"new-session", "new",
"[-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_parse,
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 session *s;
+ struct environ env;
+ const char *update;
char *overrides, *cmd, *cwd, *cause;
int detached;
u_int sx, sy;
@@ -184,13 +186,20 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
else
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. */
- s = session_create(data->newname, cmd, cwd, sx, sy, &cause);
+ s = session_create(data->newname, cmd, cwd, &env, sx, sy, &cause);
if (s == NULL) {
ctx->error(ctx, "create session failed: %s", cause);
xfree(cause);
return (-1);
}
+ environ_free(&env);
if (data->winname != NULL) {
xfree(s->curw->window->name);
diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c
index 70573929..735c637b 100644
--- a/cmd-respawn-window.c
+++ b/cmd-respawn-window.c
@@ -47,7 +47,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx)
struct window *w;
struct window_pane *wp;
struct session *s;
- const char **env;
+ struct environ env;
char *cause;
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);
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);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
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);
xfree(cause);
+ environ_free(&env);
return (-1);
}
layout_init(w);
@@ -84,5 +88,6 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx)
recalculate_sizes();
server_redraw_window(w);
+ environ_free(&env);
return (0);
}
diff --git a/cmd-set-environment.c b/cmd-set-environment.c
new file mode 100644
index 00000000..85399a94
--- /dev/null
+++ b/cmd-set-environment.c
@@ -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);
+}
diff --git a/cmd-set-option.c b/cmd-set-option.c
index 3f2a2ed2..7dad54a6 100644
--- a/cmd-set-option.c
+++ b/cmd-set-option.c
@@ -85,6 +85,7 @@ const struct set_option_entry set_option_table[] = {
{ "status-right-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL },
{ "status-utf8", SET_OPTION_FLAG, 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-bell", SET_OPTION_FLAG, 0, 0, NULL },
{ "visual-content", SET_OPTION_FLAG, 0, 0, NULL },
diff --git a/cmd-show-environment.c b/cmd-show-environment.c
new file mode 100644
index 00000000..6a86ec67
--- /dev/null
+++ b/cmd-show-environment.c
@@ -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);
+}
diff --git a/cmd-split-window.c b/cmd-split-window.c
index b483cdd4..70004a14 100644
--- a/cmd-split-window.c
+++ b/cmd-split-window.c
@@ -149,7 +149,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
struct winlink *wl;
struct window *w;
struct window_pane *wp;
- const char **env;
+ struct environ env;
char *cmd, *cwd, *cause;
u_int hlimit;
int size;
@@ -159,7 +159,10 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
return (-1);
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;
if (cmd == NULL)
@@ -181,7 +184,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
type = LAYOUT_LEFTRIGHT;
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;
if (layout_split_pane(w->active, type, size, wp) != 0) {
cause = xstrdup("pane too small");
@@ -197,9 +200,11 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
} else
server_status_session(s);
+ environ_free(&env);
return (0);
error:
+ environ_free(&env);
if (wp != NULL)
window_remove_pane(w, wp);
ctx->error(ctx, "create pane failed: %s", cause);
diff --git a/cmd-string.c b/cmd-string.c
index c12ac1d7..41b20948 100644
--- a/cmd-string.c
+++ b/cmd-string.c
@@ -59,21 +59,11 @@ int
cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause)
{
size_t p;
- int ch, argc, rval, have_arg;
- char **argv, *buf, *t, *u;
+ int ch, i, argc, rval, have_arg;
+ char **argv, *buf, *t;
+ const char *whitespace, *equals;
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;
argc = 0;
@@ -147,6 +137,18 @@ cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause)
if (argc == 0)
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);
if (*cmdlist == NULL)
goto out;
diff --git a/cmd.c b/cmd.c
index 31b80b50..b0f785f9 100644
--- a/cmd.c
+++ b/cmd.c
@@ -84,10 +84,12 @@ const struct cmd_entry *cmd_table[] = {
&cmd_send_prefix_entry,
&cmd_server_info_entry,
&cmd_set_buffer_entry,
+ &cmd_set_environment_entry,
&cmd_set_option_entry,
&cmd_set_password_entry,
&cmd_set_window_option_entry,
&cmd_show_buffer_entry,
+ &cmd_show_environment_entry,
&cmd_show_options_entry,
&cmd_show_window_options_entry,
&cmd_source_file_entry,
diff --git a/environ.c b/environ.c
new file mode 100644
index 00000000..e9f95742
--- /dev/null
+++ b/environ.c
@@ -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);
+}
diff --git a/server-fn.c b/server-fn.c
index 83ef6ca8..6f6ed2c8 100644
--- a/server-fn.c
+++ b/server-fn.c
@@ -26,25 +26,20 @@
int server_lock_callback(void *, const char *);
-const char **
-server_fill_environ(struct session *s)
+void
+server_fill_environ(struct session *s, struct environ *env)
{
- static const char *env[] = { NULL /* TMUX= */, NULL /* TERM */, NULL };
- static char tmuxvar[MAXPATHLEN + 256], termvar[256];
- u_int idx;
+ char tmuxvar[MAXPATHLEN], *term;
+ u_int idx;
if (session_index(s, &idx) != 0)
fatalx("session not found");
-
xsnprintf(tmuxvar, sizeof tmuxvar,
- "TMUX=%s,%ld,%u", socket_path, (long) getpid(), idx);
- env[0] = tmuxvar;
-
- xsnprintf(termvar, sizeof termvar,
- "TERM=%s", options_get_string(&s->options, "default-terminal"));
- env[1] = termvar;
+ "%s,%ld,%u", socket_path, (long) getpid(), idx);
+ environ_set(env, "TMUX", tmuxvar);
- return (env);
+ term = options_get_string(&s->options, "default-terminal");
+ environ_set(env, "TERM", term);
}
void
diff --git a/server-msg.c b/server-msg.c
index 2ee357ed..b23ec4c4 100644
--- a/server-msg.c
+++ b/server-msg.c
@@ -42,6 +42,7 @@ server_msg_dispatch(struct client *c)
struct msg_identify_data identifydata;
struct msg_resize_data resizedata;
struct msg_unlock_data unlockdata;
+ struct msg_environ_data environdata;
for (;;) {
if (BUFFER_USED(c->in) < sizeof hdr)
@@ -100,6 +101,15 @@ server_msg_dispatch(struct client *c)
tty_start_tty(&c->tty);
server_redraw_client(c);
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:
fatalx("unexpected message");
}
diff --git a/session.c b/session.c
index 6bf2a3ea..f53e14da 100644
--- a/session.c
+++ b/session.c
@@ -112,8 +112,8 @@ session_find(const char *name)
/* Create a new session. */
struct session *
-session_create(const char *name,
- const char *cmd, const char *cwd, u_int sx, u_int sy, char **cause)
+session_create(const char *name, const char *cmd, const char *cwd,
+ struct environ *env, u_int sx, u_int sy, char **cause)
{
struct session *s;
u_int i;
@@ -128,6 +128,9 @@ session_create(const char *name,
SLIST_INIT(&s->alerts);
paste_init_stack(&s->buffers);
options_init(&s->options, &global_s_options);
+ environ_init(&s->environ);
+ if (env != NULL)
+ environ_copy(env, &s->environ);
s->sx = sx;
s->sy = sy;
@@ -171,6 +174,7 @@ session_destroy(struct session *s)
ARRAY_TRUNC(&sessions, 1);
session_alert_cancel(s, NULL);
+ environ_free(&s->environ);
options_free(&s->options);
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)
{
struct window *w;
- const char **env;
+ struct environ env;
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");
- w = window_create(name, cmd, cwd, env, s->sx, s->sy, hlimit, cause);
- if (w == NULL)
+ w = window_create(name, cmd, cwd, &env, s->sx, s->sy, hlimit, cause);
+ if (w == NULL) {
+ environ_free(&env);
return (NULL);
+ }
+ environ_free(&env);
if (options_get_number(&s->options, "set-remain-on-exit"))
options_set_number(&w->options, "remain-on-exit", 1);
diff --git a/tmux.1 b/tmux.1
index 58609e12..1aebdbaf 100644
--- a/tmux.1
+++ b/tmux.1
@@ -1321,6 +1321,18 @@ entry for terminals which support 88 or 256 colours:
.Bd -literal -offset indent
"*88col*:colors=88,*256col*:colors=256"
.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
.Op Ic on | off
.Xc
@@ -1525,6 +1537,60 @@ or the global window options if
.Fl g
is used.
.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
.Nm
includes an optional status line which is displayed in the bottom line of each
diff --git a/tmux.c b/tmux.c
index ec2c4d3e..72ea52b3 100644
--- a/tmux.c
+++ b/tmux.c
@@ -44,6 +44,7 @@ volatile sig_atomic_t sigusr2;
char *cfg_file;
struct options global_s_options; /* session options */
struct options global_w_options; /* window options */
+struct environ global_environ;
int server_locked;
u_int password_failures;
@@ -262,7 +263,7 @@ main(int argc, char **argv)
struct hdr hdr;
struct passwd *pw;
struct msg_print_data printdata;
- char *s, *path, *label, *home, *cause;
+ char *s, *path, *label, *home, *cause, **var;
char cwd[MAXPATHLEN];
void *buf;
size_t len;
@@ -320,6 +321,10 @@ main(int argc, char **argv)
log_open_tty(debug_level);
siginit();
+ environ_init(&global_environ);
+ for (var = environ; *var != NULL; var++)
+ environ_put(&global_environ, *var);
+
if (!(flags & IDENTIFY_UTF8)) {
/*
* 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_string(&global_s_options,
"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-bell", 0);
options_set_number(&global_s_options, "visual-content", 0);
@@ -469,10 +475,10 @@ main(int argc, char **argv)
}
cmdflags &= ~CMD_STARTSERVER;
TAILQ_FOREACH(cmd, cmdlist, qentry) {
- if (cmd->entry->flags & CMD_STARTSERVER) {
+ if (cmd->entry->flags & CMD_STARTSERVER)
cmdflags |= CMD_STARTSERVER;
- break;
- }
+ if (cmd->entry->flags & CMD_SENDENVIRON)
+ cmdflags |= CMD_SENDENVIRON;
}
cmd_list_free(cmdlist);
}
diff --git a/tmux.h b/tmux.h
index 44c169ef..96e2bf76 100644
--- a/tmux.h
+++ b/tmux.h
@@ -39,6 +39,7 @@
#include "array.h"
extern char *__progname;
+extern char **environ;
/* Default configuration files. */
#define DEFAULT_CFG ".tmux.conf"
@@ -69,6 +70,7 @@ extern char *__progname;
#define COMMAND_LENGTH 2048 /* packed argv size */
#define TERMINAL_LENGTH 128 /* length of TERM environment variable */
#define PRINT_LENGTH 512 /* printed error/message size */
+#define ENVIRON_LENGTH 1024 /* environment variable length */
/* Fatal errors. */
#define fatal(msg) log_fatal("%s: %s", __func__, msg);
@@ -302,6 +304,7 @@ enum msgtype {
MSG_SUSPEND,
MSG_UNLOCK,
MSG_WAKEUP,
+ MSG_ENVIRON
};
/*
@@ -356,6 +359,10 @@ struct msg_unlock_data {
char pass[PASS_MAX];
};
+struct msg_environ_data {
+ char var[ENVIRON_LENGTH];
+};
+
/* Mode key commands. */
enum mode_key_cmd {
MODEKEY_NONE,
@@ -765,6 +772,15 @@ 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. */
struct session_alert {
struct winlink *wl;
@@ -792,6 +808,8 @@ struct session {
#define SESSION_UNATTACHED 0x1 /* not attached to any clients */
int flags;
+
+ struct environ environ;
};
ARRAY_DECL(sessions, struct session *);
@@ -894,6 +912,8 @@ struct client {
struct buffer *in;
struct buffer *out;
+ struct environ environ;
+
char *title;
char *cwd;
@@ -992,6 +1012,7 @@ struct cmd_entry {
#define CMD_CANTNEST 0x2
#define CMD_ARG1 0x4
#define CMD_ARG01 0x8
+#define CMD_SENDENVIRON 0x10
int flags;
#define CMD_CHFLAG(flag) \
@@ -1074,6 +1095,7 @@ extern volatile sig_atomic_t sigusr1;
extern volatile sig_atomic_t sigusr2;
extern struct options global_s_options;
extern struct options global_w_options;
+extern struct environ global_environ;
extern char *cfg_file;
extern int server_locked;
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);
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_cm