xref: /openbsd-src/usr.bin/tmux/environ.c (revision 35014fc2e67d21fbdec46b30122ba5ccdb4cc692)
1*35014fc2Snicm /* $OpenBSD: environ.c,v 1.27 2022/08/15 08:37:03 nicm Exp $ */
26f7d62ebSnicm 
36f7d62ebSnicm /*
498ca8272Snicm  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
56f7d62ebSnicm  *
66f7d62ebSnicm  * Permission to use, copy, modify, and distribute this software for any
76f7d62ebSnicm  * purpose with or without fee is hereby granted, provided that the above
86f7d62ebSnicm  * copyright notice and this permission notice appear in all copies.
96f7d62ebSnicm  *
106f7d62ebSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
116f7d62ebSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
126f7d62ebSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
136f7d62ebSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
146f7d62ebSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
156f7d62ebSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
166f7d62ebSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176f7d62ebSnicm  */
186f7d62ebSnicm 
196f7d62ebSnicm #include <sys/types.h>
206f7d62ebSnicm 
219b8516b6Snicm #include <fnmatch.h>
226f7d62ebSnicm #include <stdlib.h>
236f7d62ebSnicm #include <string.h>
24720ef0b5Snicm #include <unistd.h>
256f7d62ebSnicm 
266f7d62ebSnicm #include "tmux.h"
276f7d62ebSnicm 
286f7d62ebSnicm /*
296f7d62ebSnicm  * Environment - manipulate a set of environment variables.
306f7d62ebSnicm  */
316f7d62ebSnicm 
32fb46cb3dSnicm RB_HEAD(environ, environ_entry);
339883b791Snicm static int environ_cmp(struct environ_entry *, struct environ_entry *);
349883b791Snicm RB_GENERATE_STATIC(environ, environ_entry, entry, environ_cmp);
356f7d62ebSnicm 
369883b791Snicm static int
environ_cmp(struct environ_entry * envent1,struct environ_entry * envent2)376f7d62ebSnicm environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2)
386f7d62ebSnicm {
396f7d62ebSnicm 	return (strcmp(envent1->name, envent2->name));
406f7d62ebSnicm }
416f7d62ebSnicm 
42bcaf4493Snicm /* Initialise the environment. */
43fb46cb3dSnicm struct environ *
environ_create(void)44fb46cb3dSnicm environ_create(void)
456f7d62ebSnicm {
46fb46cb3dSnicm 	struct environ	*env;
47fb46cb3dSnicm 
48fb46cb3dSnicm 	env = xcalloc(1, sizeof *env);
496f7d62ebSnicm 	RB_INIT(env);
50fb46cb3dSnicm 
51fb46cb3dSnicm 	return (env);
526f7d62ebSnicm }
536f7d62ebSnicm 
54bcaf4493Snicm /* Free an environment. */
556f7d62ebSnicm void
environ_free(struct environ * env)566f7d62ebSnicm environ_free(struct environ *env)
576f7d62ebSnicm {
58fb46cb3dSnicm 	struct environ_entry	*envent, *envent1;
596f7d62ebSnicm 
60fb46cb3dSnicm 	RB_FOREACH_SAFE(envent, environ, env, envent1) {
616f7d62ebSnicm 		RB_REMOVE(environ, env, envent);
627d053cf9Snicm 		free(envent->name);
637d053cf9Snicm 		free(envent->value);
647d053cf9Snicm 		free(envent);
656f7d62ebSnicm 	}
66fb46cb3dSnicm 	free(env);
67fb46cb3dSnicm }
68fb46cb3dSnicm 
69fb46cb3dSnicm struct environ_entry *
environ_first(struct environ * env)70fb46cb3dSnicm environ_first(struct environ *env)
71fb46cb3dSnicm {
72fb46cb3dSnicm 	return (RB_MIN(environ, env));
73fb46cb3dSnicm }
74fb46cb3dSnicm 
75fb46cb3dSnicm struct environ_entry *
environ_next(struct environ_entry * envent)76fb46cb3dSnicm environ_next(struct environ_entry *envent)
77fb46cb3dSnicm {
78fb46cb3dSnicm 	return (RB_NEXT(environ, env, envent));
796f7d62ebSnicm }
806f7d62ebSnicm 
81bcaf4493Snicm /* Copy one environment into another. */
826f7d62ebSnicm void
environ_copy(struct environ * srcenv,struct environ * dstenv)836f7d62ebSnicm environ_copy(struct environ *srcenv, struct environ *dstenv)
846f7d62ebSnicm {
856f7d62ebSnicm 	struct environ_entry	*envent;
866f7d62ebSnicm 
87c4ce9d7aSnicm 	RB_FOREACH(envent, environ, srcenv) {
88c4ce9d7aSnicm 		if (envent->value == NULL)
89c4ce9d7aSnicm 			environ_clear(dstenv, envent->name);
90d6f6a5d2Snicm 		else {
91d6f6a5d2Snicm 			environ_set(dstenv, envent->name, envent->flags,
92d6f6a5d2Snicm 			    "%s", envent->value);
93d6f6a5d2Snicm 		}
94c4ce9d7aSnicm 	}
956f7d62ebSnicm }
966f7d62ebSnicm 
97bcaf4493Snicm /* Find an environment variable. */
986f7d62ebSnicm struct environ_entry *
environ_find(struct environ * env,const char * name)996f7d62ebSnicm environ_find(struct environ *env, const char *name)
1006f7d62ebSnicm {
1016f7d62ebSnicm 	struct environ_entry	envent;
1026f7d62ebSnicm 
1036f7d62ebSnicm 	envent.name = (char *) name;
1046f7d62ebSnicm 	return (RB_FIND(environ, env, &envent));
1056f7d62ebSnicm }
1066f7d62ebSnicm 
107bcaf4493Snicm /* Set an environment variable. */
1086f7d62ebSnicm void
environ_set(struct environ * env,const char * name,int flags,const char * fmt,...)109d6f6a5d2Snicm environ_set(struct environ *env, const char *name, int flags, const char *fmt,
110d6f6a5d2Snicm     ...)
111c4ce9d7aSnicm {
112c4ce9d7aSnicm 	struct environ_entry	*envent;
113c4ce9d7aSnicm 	va_list			 ap;
114c4ce9d7aSnicm 
115c4ce9d7aSnicm 	va_start(ap, fmt);
116c4ce9d7aSnicm 	if ((envent = environ_find(env, name)) != NULL) {
117d6f6a5d2Snicm 		envent->flags = flags;
118c4ce9d7aSnicm 		free(envent->value);
119c4ce9d7aSnicm 		xvasprintf(&envent->value, fmt, ap);
120c4ce9d7aSnicm 	} else {
121c4ce9d7aSnicm 		envent = xmalloc(sizeof *envent);
122c4ce9d7aSnicm 		envent->name = xstrdup(name);
123d6f6a5d2Snicm 		envent->flags = flags;
124c4ce9d7aSnicm 		xvasprintf(&envent->value, fmt, ap);
125c4ce9d7aSnicm 		RB_INSERT(environ, env, envent);
126c4ce9d7aSnicm 	}
127c4ce9d7aSnicm 	va_end(ap);
128c4ce9d7aSnicm }
129c4ce9d7aSnicm 
130c4ce9d7aSnicm /* Clear an environment variable. */
131c4ce9d7aSnicm void
environ_clear(struct environ * env,const char * name)132c4ce9d7aSnicm environ_clear(struct environ *env, const char *name)
1336f7d62ebSnicm {
1346f7d62ebSnicm 	struct environ_entry	*envent;
1356f7d62ebSnicm 
1366f7d62ebSnicm 	if ((envent = environ_find(env, name)) != NULL) {
1377d053cf9Snicm 		free(envent->value);
1386f7d62ebSnicm 		envent->value = NULL;
1396f7d62ebSnicm 	} else {
1406f7d62ebSnicm 		envent = xmalloc(sizeof *envent);
1416f7d62ebSnicm 		envent->name = xstrdup(name);
142d6f6a5d2Snicm 		envent->flags = 0;
1436f7d62ebSnicm 		envent->value = NULL;
1446f7d62ebSnicm 		RB_INSERT(environ, env, envent);
1456f7d62ebSnicm 	}
1466f7d62ebSnicm }
1476f7d62ebSnicm 
148bcaf4493Snicm /* Set an environment variable from a NAME=VALUE string. */
1496f7d62ebSnicm void
environ_put(struct environ * env,const char * var,int flags)150d6f6a5d2Snicm environ_put(struct environ *env, const char *var, int flags)
1516f7d62ebSnicm {
1526f7d62ebSnicm 	char	*name, *value;
1536f7d62ebSnicm 
1546f7d62ebSnicm 	value = strchr(var, '=');
1556f7d62ebSnicm 	if (value == NULL)
1566f7d62ebSnicm 		return;
1576f7d62ebSnicm 	value++;
1586f7d62ebSnicm 
1596f7d62ebSnicm 	name = xstrdup(var);
1606f7d62ebSnicm 	name[strcspn(name, "=")] = '\0';
1616f7d62ebSnicm 
162d6f6a5d2Snicm 	environ_set(env, name, flags, "%s", value);
1637d053cf9Snicm 	free(name);
1646f7d62ebSnicm }
1656f7d62ebSnicm 
166bcaf4493Snicm /* Unset an environment variable. */
1676f7d62ebSnicm void
environ_unset(struct environ * env,const char * name)1686f7d62ebSnicm environ_unset(struct environ *env, const char *name)
1696f7d62ebSnicm {
1706f7d62ebSnicm 	struct environ_entry	*envent;
1716f7d62ebSnicm 
1726f7d62ebSnicm 	if ((envent = environ_find(env, name)) == NULL)
1736f7d62ebSnicm 		return;
1746f7d62ebSnicm 	RB_REMOVE(environ, env, envent);
1757d053cf9Snicm 	free(envent->name);
1767d053cf9Snicm 	free(envent->value);
1777d053cf9Snicm 	free(envent);
1786f7d62ebSnicm }
1796f7d62ebSnicm 
180d6f6a5d2Snicm /* Copy variables from a destination into a source environment. */
1816f7d62ebSnicm void
environ_update(struct options * oo,struct environ * src,struct environ * dst)182d96735deSnicm environ_update(struct options *oo, struct environ *src, struct environ *dst)
1836f7d62ebSnicm {
1846f7d62ebSnicm 	struct environ_entry		*envent;
185*35014fc2Snicm 	struct environ_entry		*envent1;
186d96735deSnicm 	struct options_entry		*o;
18739052edfSnicm 	struct options_array_item	*a;
18884306383Snicm 	union options_value		*ov;
189*35014fc2Snicm 	int				 found;
1906f7d62ebSnicm 
191d96735deSnicm 	o = options_get(oo, "update-environment");
19239052edfSnicm 	if (o == NULL)
193d96735deSnicm 		return;
19439052edfSnicm 	a = options_array_first(o);
19539052edfSnicm 	while (a != NULL) {
19684306383Snicm 		ov = options_array_item_value(a);
197*35014fc2Snicm 		found = 0;
198*35014fc2Snicm 		RB_FOREACH_SAFE(envent, environ, src, envent1) {
199*35014fc2Snicm 			if (fnmatch(ov->string, envent->name, 0) == 0) {
200d6f6a5d2Snicm 				environ_set(dst, envent->name, 0, "%s", envent->value);
201*35014fc2Snicm 				found = 1;
202*35014fc2Snicm 			}
203*35014fc2Snicm 		}
204*35014fc2Snicm 		if (!found)
205*35014fc2Snicm 			environ_clear(dst, ov->string);
20639052edfSnicm 		a = options_array_next(a);
2076f7d62ebSnicm 	}
2086f7d62ebSnicm }
209bcaf4493Snicm 
210bcaf4493Snicm /* Push environment into the real environment - use after fork(). */
211bcaf4493Snicm void
environ_push(struct environ * env)212bcaf4493Snicm environ_push(struct environ *env)
213bcaf4493Snicm {
214bcaf4493Snicm 	struct environ_entry	*envent;
215bcaf4493Snicm 
216cbb5bb31Snicm 	environ = xcalloc(1, sizeof *environ);
217bcaf4493Snicm 	RB_FOREACH(envent, environ, env) {
218d6f6a5d2Snicm 		if (envent->value != NULL &&
219d6f6a5d2Snicm 		    *envent->name != '\0' &&
220d6f6a5d2Snicm 		    (~envent->flags & ENVIRON_HIDDEN))
221bcaf4493Snicm 			setenv(envent->name, envent->value, 1);
222bcaf4493Snicm 	}
223bcaf4493Snicm }
224bafc17a6Snicm 
225bafc17a6Snicm /* Log the environment. */
226bafc17a6Snicm void
environ_log(struct environ * env,const char * fmt,...)227511eaaabSnicm environ_log(struct environ *env, const char *fmt, ...)
228bafc17a6Snicm {
229bafc17a6Snicm 	struct environ_entry	*envent;
230511eaaabSnicm 	va_list			 ap;
231511eaaabSnicm 	char			*prefix;
232511eaaabSnicm 
233511eaaabSnicm 	va_start(ap, fmt);
234511eaaabSnicm 	vasprintf(&prefix, fmt, ap);
235511eaaabSnicm 	va_end(ap);
236bafc17a6Snicm 
2378386d11bSnicm 	RB_FOREACH(envent, environ, env) {
2388386d11bSnicm 		if (envent->value != NULL && *envent->name != '\0') {
2398386d11bSnicm 			log_debug("%s%s=%s", prefix, envent->name,
2408386d11bSnicm 			    envent->value);
2418386d11bSnicm 		}
2428386d11bSnicm 	}
243511eaaabSnicm 
244511eaaabSnicm 	free(prefix);
245bafc17a6Snicm }
246720ef0b5Snicm 
247720ef0b5Snicm /* Create initial environment for new child. */
248720ef0b5Snicm struct environ *
environ_for_session(struct session * s,int no_TERM)249ff7b5ef0Snicm environ_for_session(struct session *s, int no_TERM)
250720ef0b5Snicm {
251720ef0b5Snicm 	struct environ	*env;
252720ef0b5Snicm 	const char	*value;
253720ef0b5Snicm 	int		 idx;
254720ef0b5Snicm 
255720ef0b5Snicm 	env = environ_create();
256720ef0b5Snicm 	environ_copy(global_environ, env);
257720ef0b5Snicm 	if (s != NULL)
258720ef0b5Snicm 		environ_copy(s->environ, env);
259720ef0b5Snicm 
260ff7b5ef0Snicm 	if (!no_TERM) {
261720ef0b5Snicm 		value = options_get_string(global_options, "default-terminal");
262d6f6a5d2Snicm 		environ_set(env, "TERM", 0, "%s", value);
263381ed420Snicm 		environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux");
264381ed420Snicm 		environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion());
265ff7b5ef0Snicm 	}
266720ef0b5Snicm 
267720ef0b5Snicm 	if (s != NULL)
268720ef0b5Snicm 		idx = s->id;
269720ef0b5Snicm 	else
270720ef0b5Snicm 		idx = -1;
271d6f6a5d2Snicm 	environ_set(env, "TMUX", 0, "%s,%ld,%d", socket_path, (long)getpid(),
272d6f6a5d2Snicm 	    idx);
273720ef0b5Snicm 
274720ef0b5Snicm 	return (env);
275720ef0b5Snicm }
276