Home | History | Annotate | Line # | Download | only in dist
      1 /* $OpenBSD$ */
      2 
      3 /*
      4  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott (at) 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 = __UNCONST(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 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(). */
    211 void
    212 environ_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. */
    226 void
    227 environ_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. */
    248 struct environ *
    249 environ_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 		environ_set(env, "COLORTERM", 0, "truecolor");
    266 	} else {
    267 		environ_unset(env, "TERM");
    268 		environ_unset(env, "TERM_PROGRAM");
    269 		environ_unset(env, "TERM_PROGRAM_VERSION");
    270 		environ_unset(env, "COLORTERM");
    271 	}
    272 
    273 #ifdef HAVE_SYSTEMD
    274 	environ_clear(env, "LISTEN_PID");
    275 	environ_clear(env, "LISTEN_FDS");
    276 	environ_clear(env, "LISTEN_FDNAMES");
    277 #endif
    278 
    279 	if (s != NULL)
    280 		idx = s->id;
    281 	else
    282 		idx = -1;
    283 	environ_set(env, "TMUX", 0, "%s,%ld,%d", socket_path, (long)getpid(),
    284 	    idx);
    285 
    286 	return (env);
    287 }
    288