xref: /netbsd-src/external/bsd/tmux/dist/environ.c (revision 890b6d91a44b7fcb2dfbcbc1e93463086e462d2d)
199e242abSchristos /* $OpenBSD$ */
2698d5317Sjmmv 
3698d5317Sjmmv /*
4f26e8bc9Schristos  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
5698d5317Sjmmv  *
6698d5317Sjmmv  * Permission to use, copy, modify, and distribute this software for any
7698d5317Sjmmv  * purpose with or without fee is hereby granted, provided that the above
8698d5317Sjmmv  * copyright notice and this permission notice appear in all copies.
9698d5317Sjmmv  *
10698d5317Sjmmv  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11698d5317Sjmmv  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12698d5317Sjmmv  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13698d5317Sjmmv  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14698d5317Sjmmv  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15698d5317Sjmmv  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16698d5317Sjmmv  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17698d5317Sjmmv  */
18698d5317Sjmmv 
19698d5317Sjmmv #include <sys/types.h>
20698d5317Sjmmv 
21e271dbb8Schristos #include <fnmatch.h>
22698d5317Sjmmv #include <stdlib.h>
23698d5317Sjmmv #include <string.h>
24e9a2d6faSchristos #include <unistd.h>
25698d5317Sjmmv 
26698d5317Sjmmv #include "tmux.h"
27698d5317Sjmmv 
28698d5317Sjmmv /*
29698d5317Sjmmv  * Environment - manipulate a set of environment variables.
30698d5317Sjmmv  */
31698d5317Sjmmv 
32f26e8bc9Schristos RB_HEAD(environ, environ_entry);
33e9a2d6faSchristos static int environ_cmp(struct environ_entry *, struct environ_entry *);
34e9a2d6faSchristos RB_GENERATE_STATIC(environ, environ_entry, entry, environ_cmp);
35698d5317Sjmmv 
36e9a2d6faSchristos static int
37698d5317Sjmmv environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2)
38698d5317Sjmmv {
39698d5317Sjmmv 	return (strcmp(envent1->name, envent2->name));
40698d5317Sjmmv }
41698d5317Sjmmv 
42698d5317Sjmmv /* Initialise the environment. */
43f26e8bc9Schristos struct environ *
44f26e8bc9Schristos environ_create(void)
45698d5317Sjmmv {
46f26e8bc9Schristos 	struct environ	*env;
47f26e8bc9Schristos 
48f26e8bc9Schristos 	env = xcalloc(1, sizeof *env);
49698d5317Sjmmv 	RB_INIT(env);
50f26e8bc9Schristos 
51f26e8bc9Schristos 	return (env);
52698d5317Sjmmv }
53698d5317Sjmmv 
54698d5317Sjmmv /* Free an environment. */
55698d5317Sjmmv void
56698d5317Sjmmv environ_free(struct environ *env)
57698d5317Sjmmv {
58f26e8bc9Schristos 	struct environ_entry	*envent, *envent1;
59698d5317Sjmmv 
60f26e8bc9Schristos 	RB_FOREACH_SAFE(envent, environ, env, envent1) {
61698d5317Sjmmv 		RB_REMOVE(environ, env, envent);
6261fba46bSchristos 		free(envent->name);
6361fba46bSchristos 		free(envent->value);
6461fba46bSchristos 		free(envent);
65698d5317Sjmmv 	}
66f26e8bc9Schristos 	free(env);
67f26e8bc9Schristos }
68f26e8bc9Schristos 
69f26e8bc9Schristos struct environ_entry *
70f26e8bc9Schristos environ_first(struct environ *env)
71f26e8bc9Schristos {
72f26e8bc9Schristos 	return (RB_MIN(environ, env));
73f26e8bc9Schristos }
74f26e8bc9Schristos 
75f26e8bc9Schristos struct environ_entry *
76f26e8bc9Schristos environ_next(struct environ_entry *envent)
77f26e8bc9Schristos {
78f26e8bc9Schristos 	return (RB_NEXT(environ, env, envent));
79698d5317Sjmmv }
80698d5317Sjmmv 
81698d5317Sjmmv /* Copy one environment into another. */
82698d5317Sjmmv void
83698d5317Sjmmv environ_copy(struct environ *srcenv, struct environ *dstenv)
84698d5317Sjmmv {
85698d5317Sjmmv 	struct environ_entry	*envent;
86698d5317Sjmmv 
87f26e8bc9Schristos 	RB_FOREACH(envent, environ, srcenv) {
88f26e8bc9Schristos 		if (envent->value == NULL)
89f26e8bc9Schristos 			environ_clear(dstenv, envent->name);
90e271dbb8Schristos 		else {
91e271dbb8Schristos 			environ_set(dstenv, envent->name, envent->flags,
92e271dbb8Schristos 			    "%s", envent->value);
93e271dbb8Schristos 		}
94f26e8bc9Schristos 	}
95698d5317Sjmmv }
96698d5317Sjmmv 
97698d5317Sjmmv /* Find an environment variable. */
98698d5317Sjmmv struct environ_entry *
99698d5317Sjmmv environ_find(struct environ *env, const char *name)
100698d5317Sjmmv {
101698d5317Sjmmv 	struct environ_entry	envent;
102698d5317Sjmmv 
103cad4076fSchristos 	envent.name = __UNCONST(name);
104698d5317Sjmmv 	return (RB_FIND(environ, env, &envent));
105698d5317Sjmmv }
106698d5317Sjmmv 
107698d5317Sjmmv /* Set an environment variable. */
108698d5317Sjmmv void
109e271dbb8Schristos environ_set(struct environ *env, const char *name, int flags, const char *fmt,
110e271dbb8Schristos     ...)
111f26e8bc9Schristos {
112f26e8bc9Schristos 	struct environ_entry	*envent;
113f26e8bc9Schristos 	va_list			 ap;
114f26e8bc9Schristos 
115f26e8bc9Schristos 	va_start(ap, fmt);
116f26e8bc9Schristos 	if ((envent = environ_find(env, name)) != NULL) {
117e271dbb8Schristos 		envent->flags = flags;
118f26e8bc9Schristos 		free(envent->value);
119f26e8bc9Schristos 		xvasprintf(&envent->value, fmt, ap);
120f26e8bc9Schristos 	} else {
121f26e8bc9Schristos 		envent = xmalloc(sizeof *envent);
122f26e8bc9Schristos 		envent->name = xstrdup(name);
123e271dbb8Schristos 		envent->flags = flags;
124f26e8bc9Schristos 		xvasprintf(&envent->value, fmt, ap);
125f26e8bc9Schristos 		RB_INSERT(environ, env, envent);
126f26e8bc9Schristos 	}
127f26e8bc9Schristos 	va_end(ap);
128f26e8bc9Schristos }
129f26e8bc9Schristos 
130f26e8bc9Schristos /* Clear an environment variable. */
131f26e8bc9Schristos void
132f26e8bc9Schristos environ_clear(struct environ *env, const char *name)
133698d5317Sjmmv {
134698d5317Sjmmv 	struct environ_entry	*envent;
135698d5317Sjmmv 
136698d5317Sjmmv 	if ((envent = environ_find(env, name)) != NULL) {
13761fba46bSchristos 		free(envent->value);
138698d5317Sjmmv 		envent->value = NULL;
139698d5317Sjmmv 	} else {
140698d5317Sjmmv 		envent = xmalloc(sizeof *envent);
141698d5317Sjmmv 		envent->name = xstrdup(name);
142e271dbb8Schristos 		envent->flags = 0;
143698d5317Sjmmv 		envent->value = NULL;
144698d5317Sjmmv 		RB_INSERT(environ, env, envent);
145698d5317Sjmmv 	}
146698d5317Sjmmv }
147698d5317Sjmmv 
148698d5317Sjmmv /* Set an environment variable from a NAME=VALUE string. */
149698d5317Sjmmv void
150e271dbb8Schristos environ_put(struct environ *env, const char *var, int flags)
151698d5317Sjmmv {
152698d5317Sjmmv 	char	*name, *value;
153698d5317Sjmmv 
154698d5317Sjmmv 	value = strchr(var, '=');
155698d5317Sjmmv 	if (value == NULL)
156698d5317Sjmmv 		return;
157698d5317Sjmmv 	value++;
158698d5317Sjmmv 
159698d5317Sjmmv 	name = xstrdup(var);
160698d5317Sjmmv 	name[strcspn(name, "=")] = '\0';
161698d5317Sjmmv 
162e271dbb8Schristos 	environ_set(env, name, flags, "%s", value);
16361fba46bSchristos 	free(name);
164698d5317Sjmmv }
165698d5317Sjmmv 
166698d5317Sjmmv /* Unset an environment variable. */
167698d5317Sjmmv void
168698d5317Sjmmv environ_unset(struct environ *env, const char *name)
169698d5317Sjmmv {
170698d5317Sjmmv 	struct environ_entry	*envent;
171698d5317Sjmmv 
172698d5317Sjmmv 	if ((envent = environ_find(env, name)) == NULL)
173698d5317Sjmmv 		return;
174698d5317Sjmmv 	RB_REMOVE(environ, env, envent);
17561fba46bSchristos 	free(envent->name);
17661fba46bSchristos 	free(envent->value);
17761fba46bSchristos 	free(envent);
178698d5317Sjmmv }
179698d5317Sjmmv 
180e271dbb8Schristos /* Copy variables from a destination into a source environment. */
181698d5317Sjmmv void
182e9a2d6faSchristos environ_update(struct options *oo, struct environ *src, struct environ *dst)
183698d5317Sjmmv {
184698d5317Sjmmv 	struct environ_entry		*envent;
185f844e94eSwiz 	struct environ_entry		*envent1;
186e9a2d6faSchristos 	struct options_entry		*o;
1870a274e86Schristos 	struct options_array_item	*a;
18830744affSchristos 	union options_value		*ov;
189f844e94eSwiz 	int				 found;
190698d5317Sjmmv 
191e9a2d6faSchristos 	o = options_get(oo, "update-environment");
1920a274e86Schristos 	if (o == NULL)
193e9a2d6faSchristos 		return;
1940a274e86Schristos 	a = options_array_first(o);
1950a274e86Schristos 	while (a != NULL) {
19630744affSchristos 		ov = options_array_item_value(a);
197f844e94eSwiz 		found = 0;
198f844e94eSwiz 		RB_FOREACH_SAFE(envent, environ, src, envent1) {
199f844e94eSwiz 			if (fnmatch(ov->string, envent->name, 0) == 0) {
200e271dbb8Schristos 				environ_set(dst, envent->name, 0, "%s", envent->value);
201f844e94eSwiz 				found = 1;
202f844e94eSwiz 			}
203f844e94eSwiz 		}
204f844e94eSwiz 		if (!found)
205f844e94eSwiz 			environ_clear(dst, ov->string);
2060a274e86Schristos 		a = options_array_next(a);
207698d5317Sjmmv 	}
208698d5317Sjmmv }
209698d5317Sjmmv 
210698d5317Sjmmv /* Push environment into the real environment - use after fork(). */
211698d5317Sjmmv void
212698d5317Sjmmv environ_push(struct environ *env)
213698d5317Sjmmv {
214698d5317Sjmmv 	struct environ_entry	*envent;
215698d5317Sjmmv 
216e9a2d6faSchristos 	environ = xcalloc(1, sizeof *environ);
217698d5317Sjmmv 	RB_FOREACH(envent, environ, env) {
218e271dbb8Schristos 		if (envent->value != NULL &&
219e271dbb8Schristos 		    *envent->name != '\0' &&
220e271dbb8Schristos 		    (~envent->flags & ENVIRON_HIDDEN))
221698d5317Sjmmv 			setenv(envent->name, envent->value, 1);
222698d5317Sjmmv 	}
223698d5317Sjmmv }
224e9a2d6faSchristos 
225e9a2d6faSchristos /* Log the environment. */
226e9a2d6faSchristos void
227fe99a117Schristos environ_log(struct environ *env, const char *fmt, ...)
228e9a2d6faSchristos {
229e9a2d6faSchristos 	struct environ_entry	*envent;
230fe99a117Schristos 	va_list			 ap;
231fe99a117Schristos 	char			*prefix;
232fe99a117Schristos 
233fe99a117Schristos 	va_start(ap, fmt);
234fe99a117Schristos 	vasprintf(&prefix, fmt, ap);
235fe99a117Schristos 	va_end(ap);
236e9a2d6faSchristos 
237e9a2d6faSchristos 	RB_FOREACH(envent, environ, env) {
238e9a2d6faSchristos 		if (envent->value != NULL && *envent->name != '\0') {
239e9a2d6faSchristos 			log_debug("%s%s=%s", prefix, envent->name,
240e9a2d6faSchristos 			    envent->value);
241e9a2d6faSchristos 		}
242e9a2d6faSchristos 	}
243fe99a117Schristos 
244fe99a117Schristos 	free(prefix);
245e9a2d6faSchristos }
246e9a2d6faSchristos 
247e9a2d6faSchristos /* Create initial environment for new child. */
248e9a2d6faSchristos struct environ *
249fe99a117Schristos environ_for_session(struct session *s, int no_TERM)
250e9a2d6faSchristos {
251e9a2d6faSchristos 	struct environ	*env;
252e9a2d6faSchristos 	const char	*value;
253e9a2d6faSchristos 	int		 idx;
254e9a2d6faSchristos 
255e9a2d6faSchristos 	env = environ_create();
256e9a2d6faSchristos 	environ_copy(global_environ, env);
257e9a2d6faSchristos 	if (s != NULL)
258e9a2d6faSchristos 		environ_copy(s->environ, env);
259e9a2d6faSchristos 
260fe99a117Schristos 	if (!no_TERM) {
261e9a2d6faSchristos 		value = options_get_string(global_options, "default-terminal");
262e271dbb8Schristos 		environ_set(env, "TERM", 0, "%s", value);
263e271dbb8Schristos 		environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux");
264e271dbb8Schristos 		environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion());
265fe99a117Schristos 	}
266e9a2d6faSchristos 
267*890b6d91Swiz #ifdef HAVE_SYSTEMD
268*890b6d91Swiz 	environ_clear(env, "LISTEN_PID");
269*890b6d91Swiz 	environ_clear(env, "LISTEN_FDS");
270*890b6d91Swiz 	environ_clear(env, "LISTEN_FDNAMES");
271*890b6d91Swiz #endif
272*890b6d91Swiz 
273e9a2d6faSchristos 	if (s != NULL)
274e9a2d6faSchristos 		idx = s->id;
275e9a2d6faSchristos 	else
276e9a2d6faSchristos 		idx = -1;
277e271dbb8Schristos 	environ_set(env, "TMUX", 0, "%s,%ld,%d", socket_path, (long)getpid(),
278e271dbb8Schristos 	    idx);
279e9a2d6faSchristos 
280e9a2d6faSchristos 	return (env);
281e9a2d6faSchristos }
282