From 29b1b2fb5eee4319cdc1464ee377b68f3171dc27 Mon Sep 17 00:00:00 2001 From: Tiago Cunha Date: Sun, 9 Aug 2009 17:48:55 +0000 Subject: [PATCH] Sync OpenBSD patchset 231: 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 session 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. --- client.c | 20 +++++- cmd-attach-session.c | 9 ++- cmd-new-session.c | 15 ++++- cmd-respawn-window.c | 13 ++-- cmd-set-environment.c | 88 ++++++++++++++++++++++++ cmd-set-option.c | 3 +- cmd-show-environment.c | 67 +++++++++++++++++++ cmd-split-window.c | 13 ++-- cmd-string.c | 30 +++++---- cmd.c | 4 +- environ.c | 147 +++++++++++++++++++++++++++++++++++++++++ server-fn.c | 23 +++---- server-msg.c | 12 +++- session.c | 24 +++++-- tmux.1 | 68 ++++++++++++++++++- tmux.c | 16 +++-- tmux.h | 48 ++++++++++++-- window.c | 39 +++++++---- 18 files changed, 562 insertions(+), 77 deletions(-) create mode 100644 cmd-set-environment.c create mode 100644 cmd-show-environment.c create mode 100644 environ.c diff --git a/client.c b/client.c index b76e139e..13d62e86 100644 --- a/client.c +++ b/client.c @@ -1,4 +1,4 @@ -/* $Id: client.c,v 1.60 2009-08-09 17:43:00 tcunha Exp $ */ +/* $Id: client.c,v 1.61 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -33,6 +33,7 @@ #include "tmux.h" +void client_send_environ(struct client_ctx *); void client_handle_winch(struct client_ctx *); int @@ -99,6 +100,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)"); @@ -137,6 +140,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) { @@ -246,8 +262,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 d39cdeff..d83e4c58 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -1,4 +1,4 @@ -/* $Id: cmd-attach-session.c,v 1.31 2009-08-09 15:26:24 tcunha Exp $ */ +/* $Id: cmd-attach-session.c,v 1.32 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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 21c7902a..181d9bb8 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -1,4 +1,4 @@ -/* $Id: cmd-new-session.c,v 1.50 2009-08-09 15:26:24 tcunha Exp $ */ +/* $Id: cmd-new-session.c,v 1.51 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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 0bda1068..c5888c51 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -1,4 +1,4 @@ -/* $Id: cmd-respawn-window.c,v 1.18 2009-07-28 22:12:16 tcunha Exp $ */ +/* $Id: cmd-respawn-window.c,v 1.19 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -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..31d7aa79 --- /dev/null +++ b/cmd-set-environment.c @@ -0,0 +1,88 @@ +/* $Id: cmd-set-environment.c,v 1.1 2009-08-09 17:48:55 tcunha Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * 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 + +#include +#include + +#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 cddf62db..0cf00020 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -1,4 +1,4 @@ -/* $Id: cmd-set-option.c,v 1.73 2009-08-09 17:40:17 tcunha Exp $ */ +/* $Id: cmd-set-option.c,v 1.74 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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..8f0932bd --- /dev/null +++ b/cmd-show-environment.c @@ -0,0 +1,67 @@ +/* $Id: cmd-show-environment.c,v 1.1 2009-08-09 17:48:55 tcunha Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * 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 + +#include +#include + +#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 253c95e7..07b1f56d 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -1,4 +1,4 @@ -/* $Id: cmd-split-window.c,v 1.21 2009-07-28 22:12:16 tcunha Exp $ */ +/* $Id: cmd-split-window.c,v 1.22 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -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 995ce163..40d5c285 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -1,4 +1,4 @@ -/* $Id: cmd-string.c,v 1.22 2009-08-09 15:26:24 tcunha Exp $ */ +/* $Id: cmd-string.c,v 1.23 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -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 0777d7d1..192d0add 100644 --- a/cmd.c +++ b/cmd.c @@ -1,4 +1,4 @@ -/* $Id: cmd.c,v 1.111 2009-07-30 20:45:20 tcunha Exp $ */ +/* $Id: cmd.c,v 1.112 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -83,10 +83,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..7111ebb5 --- /dev/null +++ b/environ.c @@ -0,0 +1,147 @@ +/* $Id: environ.c,v 1.1 2009-08-09 17:48:55 tcunha Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * 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 + +#include +#include + +#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 30f9de4a..35162c93 100644 --- a/server-fn.c +++ b/server-fn.c @@ -1,4 +1,4 @@ -/* $Id: server-fn.c,v 1.79 2009-08-09 17:19:18 tcunha Exp $ */ +/* $Id: server-fn.c,v 1.80 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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; + "%s,%ld,%u", socket_path, (long) getpid(), idx); + environ_set(env, "TMUX", tmuxvar); - xsnprintf(termvar, sizeof termvar, - "TERM=%s", options_get_string(&s->options, "default-terminal")); - env[1] = termvar; - - 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 97a4b5cd..c6721c09 100644 --- a/server-msg.c +++ b/server-msg.c @@ -1,4 +1,4 @@ -/* $Id: server-msg.c,v 1.76 2009-07-30 21:01:01 tcunha Exp $ */ +/* $Id: server-msg.c,v 1.77 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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 04811759..7b9bf26a 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -/* $Id: session.c,v 1.59 2009-07-08 18:03:03 nicm Exp $ */ +/* $Id: session.c,v 1.60 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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 b8088887..bd3cc1d8 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $Id: tmux.1,v 1.144 2009-08-09 17:40:17 tcunha Exp $ +.\" $Id: tmux.1,v 1.145 2009-08-09 17:48:55 tcunha Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -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 69826d35..24085085 100644 --- a/tmux.c +++ b/tmux.c @@ -1,4 +1,4 @@ -/* $Id: tmux.c,v 1.157 2009-08-09 17:40:17 tcunha Exp $ */ +/* $Id: tmux.c,v 1.158 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -49,6 +49,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; @@ -271,7 +272,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; @@ -329,6 +330,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 @@ -385,6 +390,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); @@ -478,10 +484,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 76e09fb9..7d2518b2 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $Id: tmux.h,v 1.407 2009-08-09 17:32:06 tcunha Exp $ */ +/* $Id: tmux.h,v 1.408 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -38,6 +38,7 @@ #include "compat.h" extern char *__progname; +extern char **environ; /* Default configuration files. */ #define DEFAULT_CFG ".tmux.conf" @@ -68,6 +69,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); @@ -301,6 +303,7 @@ enum msgtype { MSG_SUSPEND, MSG_UNLOCK, MSG_WAKEUP, + MSG_ENVIRON }; /* @@ -355,6 +358,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, @@ -764,6 +771,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; @@ -791,6 +807,8 @@ struct session { #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ int flags; + + struct environ environ; }; ARRAY_DECL(sessions, struct session *); @@ -893,6 +911,8 @@ struct client { struct buffer *in; struct buffer *out; + struct environ environ; + char *title; char *cwd; @@ -991,6 +1011,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) \ @@ -1073,6 +1094,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; @@ -1122,6 +1144,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_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 */ u_char tty_get_acs(struct tty *, u_char); void tty_reset(struct tty *); @@ -1284,10 +1318,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_server_info_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_password_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_environment_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_source_file_entry; @@ -1383,7 +1419,7 @@ int server_start(char *); int server_msg_dispatch(struct client *); /* 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_client( struct client *, enum msgtype, const void *, size_t); @@ -1553,8 +1589,8 @@ void winlink_stack_push(struct winlink_stack *, struct winlink *); void winlink_stack_remove(struct winlink_stack *, struct winlink *); int window_index(struct window *, u_int *); struct window *window_create1(u_int, u_int); -struct window *window_create(const char *, const char *, - const char *, const char **, u_int, u_int, u_int, char **); +struct window *window_create(const char *, const char *, const char *, + struct environ *, u_int, u_int, u_int, char **); void window_destroy(struct window *); void window_set_active_pane(struct window *, struct window_pane *); struct window_pane *window_add_pane(struct window *, u_int); @@ -1567,7 +1603,7 @@ void window_destroy_panes(struct window *); struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(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); int window_pane_set_mode( struct window_pane *, const struct window_mode *); @@ -1647,7 +1683,7 @@ int session_alert_has(struct session *, struct winlink *, int); int session_alert_has_window(struct session *, struct window *, int); struct session *session_find(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 *); int session_index(struct session *, u_int *); struct winlink *session_new(struct session *, diff --git a/window.c b/window.c index b25c45aa..60cc4d54 100644 --- a/window.c +++ b/window.c @@ -1,4 +1,4 @@ -/* $Id: window.c,v 1.99 2009-07-28 23:04:29 tcunha Exp $ */ +/* $Id: window.c,v 1.100 2009-08-09 17:48:55 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -252,7 +252,7 @@ window_create1(u_int sx, u_int sy) struct window * 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_pane *wp; @@ -260,7 +260,7 @@ window_create(const char *name, const char *cmd, const char *cwd, w = window_create1(sx, sy); wp = window_add_pane(w, hlimit); 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); return (NULL); } @@ -454,13 +454,16 @@ window_pane_destroy(struct window_pane *wp) int 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; - int mode; - const char **envq, *ptr; - char *argv0; - struct timeval tv; + struct winsize ws; + int mode; + char *argv0, **varp, *var; + ARRAY_DECL(, char *) varlist; + struct environ_entry *envent; + const char *ptr; + struct timeval tv; + u_int i; if (wp->fd != -1) close(wp->fd); @@ -493,10 +496,22 @@ window_pane_spawn(struct window_pane *wp, case 0: if (chdir(wp->cwd) != 0) chdir("/"); - for (envq = envp; *envq != NULL; envq++) { - if (putenv(xstrdup(*envq)) != 0) - fatal("putenv failed"); + + ARRAY_INIT(&varlist); + 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(); log_close();