1 /* $OpenBSD: environ.c,v 1.26 2020/10/07 08:23:55 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 32 RB_HEAD(environ, environ_entry); 33 static int environ_cmp(struct environ_entry *, struct environ_entry *); 34 RB_GENERATE_STATIC(environ, environ_entry, entry, environ_cmp); 35 36 static int 37 environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2) 38 { 39 return (strcmp(envent1->name, envent2->name)); 40 } 41 42 /* Initialise the environment. */ 43 struct environ * 44 environ_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. */ 55 void 56 environ_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 69 struct environ_entry * 70 environ_first(struct environ *env) 71 { 72 return (RB_MIN(environ, env)); 73 } 74 75 struct environ_entry * 76 environ_next(struct environ_entry *envent) 77 { 78 return (RB_NEXT(environ, env, envent)); 79 } 80 81 /* Copy one environment into another. */ 82 void 83 environ_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. */ 98 struct environ_entry * 99 environ_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. */ 108 void 109 environ_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. */ 131 void 132 environ_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. */ 149 void 150 environ_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. */ 167 void 168 environ_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. */ 181 void 182 environ_update(struct options *oo, struct environ *src, struct environ *dst) 183 { 184 struct environ_entry *envent; 185 struct options_entry *o; 186 struct options_array_item *a; 187 union options_value *ov; 188 189 o = options_get(oo, "update-environment"); 190 if (o == NULL) 191 return; 192 a = options_array_first(o); 193 while (a != NULL) { 194 ov = options_array_item_value(a); 195 RB_FOREACH(envent, environ, src) { 196 if (fnmatch(ov->string, envent->name, 0) == 0) 197 break; 198 } 199 if (envent == NULL) 200 environ_clear(dst, ov->string); 201 else 202 environ_set(dst, envent->name, 0, "%s", envent->value); 203 a = options_array_next(a); 204 } 205 } 206 207 /* Push environment into the real environment - use after fork(). */ 208 void 209 environ_push(struct environ *env) 210 { 211 struct environ_entry *envent; 212 213 environ = xcalloc(1, sizeof *environ); 214 RB_FOREACH(envent, environ, env) { 215 if (envent->value != NULL && 216 *envent->name != '\0' && 217 (~envent->flags & ENVIRON_HIDDEN)) 218 setenv(envent->name, envent->value, 1); 219 } 220 } 221 222 /* Log the environment. */ 223 void 224 environ_log(struct environ *env, const char *fmt, ...) 225 { 226 struct environ_entry *envent; 227 va_list ap; 228 char *prefix; 229 230 va_start(ap, fmt); 231 vasprintf(&prefix, fmt, ap); 232 va_end(ap); 233 234 RB_FOREACH(envent, environ, env) { 235 if (envent->value != NULL && *envent->name != '\0') { 236 log_debug("%s%s=%s", prefix, envent->name, 237 envent->value); 238 } 239 } 240 241 free(prefix); 242 } 243 244 /* Create initial environment for new child. */ 245 struct environ * 246 environ_for_session(struct session *s, int no_TERM) 247 { 248 struct environ *env; 249 const char *value; 250 int idx; 251 252 env = environ_create(); 253 environ_copy(global_environ, env); 254 if (s != NULL) 255 environ_copy(s->environ, env); 256 257 if (!no_TERM) { 258 value = options_get_string(global_options, "default-terminal"); 259 environ_set(env, "TERM", 0, "%s", value); 260 environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux"); 261 environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion()); 262 } 263 264 if (s != NULL) 265 idx = s->id; 266 else 267 idx = -1; 268 environ_set(env, "TMUX", 0, "%s,%ld,%d", socket_path, (long)getpid(), 269 idx); 270 271 return (env); 272 } 273