1/* $OpenBSD: environ.c,v 1.27 2022/08/15 08:37:03 nicm Exp $ */ 2 3/* 4 * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/types.h> 20 21#include <fnmatch.h> 22#include <stdlib.h> 23#include <string.h> 24#include <unistd.h> 25 26#include "tmux.h" 27 28/* 29 * Environment - manipulate a set of environment variables. 30 */ 31 32RB_HEAD(environ, environ_entry); 33static int environ_cmp(struct environ_entry *, struct environ_entry *); 34RB_GENERATE_STATIC(environ, environ_entry, entry, environ_cmp); 35 36static int 37environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2) 38{ 39 return (strcmp(envent1->name, envent2->name)); 40} 41 42/* Initialise the environment. */ 43struct environ * 44environ_create(void) 45{ 46 struct environ *env; 47 48 env = xcalloc(1, sizeof *env); 49 RB_INIT(env); 50 51 return (env); 52} 53 54/* Free an environment. */ 55void 56environ_free(struct environ *env) 57{ 58 struct environ_entry *envent, *envent1; 59 60 RB_FOREACH_SAFE(envent, environ, env, envent1) { 61 RB_REMOVE(environ, env, envent); 62 free(envent->name); 63 free(envent->value); 64 free(envent); 65 } 66 free(env); 67} 68 69struct environ_entry * 70environ_first(struct environ *env) 71{ 72 return (RB_MIN(environ, env)); 73} 74 75struct environ_entry * 76environ_next(struct environ_entry *envent) 77{ 78 return (RB_NEXT(environ, env, envent)); 79} 80 81/* Copy one environment into another. */ 82void 83environ_copy(struct environ *srcenv, struct environ *dstenv) 84{ 85 struct environ_entry *envent; 86 87 RB_FOREACH(envent, environ, srcenv) { 88 if (envent->value == NULL) 89 environ_clear(dstenv, envent->name); 90 else { 91 environ_set(dstenv, envent->name, envent->flags, 92 "%s", envent->value); 93 } 94 } 95} 96 97/* Find an environment variable. */ 98struct environ_entry * 99environ_find(struct environ *env, const char *name) 100{ 101 struct environ_entry envent; 102 103 envent.name = (char *) name; 104 return (RB_FIND(environ, env, &envent)); 105} 106 107/* Set an environment variable. */ 108void 109environ_set(struct environ *env, const char *name, int flags, const char *fmt, 110 ...) 111{ 112 struct environ_entry *envent; 113 va_list ap; 114 115 va_start(ap, fmt); 116 if ((envent = environ_find(env, name)) != NULL) { 117 envent->flags = flags; 118 free(envent->value); 119 xvasprintf(&envent->value, fmt, ap); 120 } else { 121 envent = xmalloc(sizeof *envent); 122 envent->name = xstrdup(name); 123 envent->flags = flags; 124 xvasprintf(&envent->value, fmt, ap); 125 RB_INSERT(environ, env, envent); 126 } 127 va_end(ap); 128} 129 130/* Clear an environment variable. */ 131void 132environ_clear(struct environ *env, const char *name) 133{ 134 struct environ_entry *envent; 135 136 if ((envent = environ_find(env, name)) != NULL) { 137 free(envent->value); 138 envent->value = NULL; 139 } else { 140 envent = xmalloc(sizeof *envent); 141 envent->name = xstrdup(name); 142 envent->flags = 0; 143 envent->value = NULL; 144 RB_INSERT(environ, env, envent); 145 } 146} 147 148/* Set an environment variable from a NAME=VALUE string. */ 149void 150environ_put(struct environ *env, const char *var, int flags) 151{ 152 char *name, *value; 153 154 value = strchr(var, '='); 155 if (value == NULL) 156 return; 157 value++; 158 159 name = xstrdup(var); 160 name[strcspn(name, "=")] = '\0'; 161 162 environ_set(env, name, flags, "%s", value); 163 free(name); 164} 165 166/* Unset an environment variable. */ 167void 168environ_unset(struct environ *env, const char *name) 169{ 170 struct environ_entry *envent; 171 172 if ((envent = environ_find(env, name)) == NULL) 173 return; 174 RB_REMOVE(environ, env, envent); 175 free(envent->name); 176 free(envent->value); 177 free(envent); 178} 179 180/* Copy variables from a destination into a source environment. */ 181void 182environ_update(struct options *oo, struct environ *src, struct environ *dst) 183{ 184 struct environ_entry *envent; 185 struct environ_entry *envent1; 186 struct options_entry *o; 187 struct options_array_item *a; 188 union options_value *ov; 189 int found; 190 191 o = options_get(oo, "update-environment"); 192 if (o == NULL) 193 return; 194 a = options_array_first(o); 195 while (a != NULL) { 196 ov = options_array_item_value(a); 197 found = 0; 198 RB_FOREACH_SAFE(envent, environ, src, envent1) { 199 if (fnmatch(ov->string, envent->name, 0) == 0) { 200 environ_set(dst, envent->name, 0, "%s", envent->value); 201 found = 1; 202 } 203 } 204 if (!found) 205 environ_clear(dst, ov->string); 206 a = options_array_next(a); 207 } 208} 209 210/* Push environment into the real environment - use after fork(). */ 211void 212environ_push(struct environ *env) 213{ 214 struct environ_entry *envent; 215 216 environ = xcalloc(1, sizeof *environ); 217 RB_FOREACH(envent, environ, env) { 218 if (envent->value != NULL && 219 *envent->name != '\0' && 220 (~envent->flags & ENVIRON_HIDDEN)) 221 setenv(envent->name, envent->value, 1); 222 } 223} 224 225/* Log the environment. */ 226void 227environ_log(struct environ *env, const char *fmt, ...) 228{ 229 struct environ_entry *envent; 230 va_list ap; 231 char *prefix; 232 233 va_start(ap, fmt); 234 vasprintf(&prefix, fmt, ap); 235 va_end(ap); 236 237 RB_FOREACH(envent, environ, env) { 238 if (envent->value != NULL && *envent->name != '\0') { 239 log_debug("%s%s=%s", prefix, envent->name, 240 envent->value); 241 } 242 } 243 244 free(prefix); 245} 246 247/* Create initial environment for new child. */ 248struct environ * 249environ_for_session(struct session *s, int no_TERM) 250{ 251 struct environ *env; 252 const char *value; 253 int idx; 254 255 env = environ_create(); 256 environ_copy(global_environ, env); 257 if (s != NULL) 258 environ_copy(s->environ, env); 259 260 if (!no_TERM) { 261 value = options_get_string(global_options, "default-terminal"); 262 environ_set(env, "TERM", 0, "%s", value); 263 environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux"); 264 environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion()); 265 } 266 267 if (s != NULL) 268 idx = s->id; 269 else 270 idx = -1; 271 environ_set(env, "TMUX", 0, "%s,%ld,%d", socket_path, (long)getpid(), 272 idx); 273 274 return (env); 275} 276