xref: /openbsd-src/usr.bin/tmux/environ.c (revision 505ee9ea3b177e2387d907a91ca7da069f3f14d8)
1 /* $OpenBSD: environ.c,v 1.25 2020/05/16 15:40:44 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 <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 
25 #include "tmux.h"
26 
27 /*
28  * Environment - manipulate a set of environment variables.
29  */
30 
31 RB_HEAD(environ, environ_entry);
32 static int environ_cmp(struct environ_entry *, struct environ_entry *);
33 RB_GENERATE_STATIC(environ, environ_entry, entry, environ_cmp);
34 
35 static int
36 environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2)
37 {
38 	return (strcmp(envent1->name, envent2->name));
39 }
40 
41 /* Initialise the environment. */
42 struct environ *
43 environ_create(void)
44 {
45 	struct environ	*env;
46 
47 	env = xcalloc(1, sizeof *env);
48 	RB_INIT(env);
49 
50 	return (env);
51 }
52 
53 /* Free an environment. */
54 void
55 environ_free(struct environ *env)
56 {
57 	struct environ_entry	*envent, *envent1;
58 
59 	RB_FOREACH_SAFE(envent, environ, env, envent1) {
60 		RB_REMOVE(environ, env, envent);
61 		free(envent->name);
62 		free(envent->value);
63 		free(envent);
64 	}
65 	free(env);
66 }
67 
68 struct environ_entry *
69 environ_first(struct environ *env)
70 {
71 	return (RB_MIN(environ, env));
72 }
73 
74 struct environ_entry *
75 environ_next(struct environ_entry *envent)
76 {
77 	return (RB_NEXT(environ, env, envent));
78 }
79 
80 /* Copy one environment into another. */
81 void
82 environ_copy(struct environ *srcenv, struct environ *dstenv)
83 {
84 	struct environ_entry	*envent;
85 
86 	RB_FOREACH(envent, environ, srcenv) {
87 		if (envent->value == NULL)
88 			environ_clear(dstenv, envent->name);
89 		else {
90 			environ_set(dstenv, envent->name, envent->flags,
91 			    "%s", envent->value);
92 		}
93 	}
94 }
95 
96 /* Find an environment variable. */
97 struct environ_entry *
98 environ_find(struct environ *env, const char *name)
99 {
100 	struct environ_entry	envent;
101 
102 	envent.name = (char *) name;
103 	return (RB_FIND(environ, env, &envent));
104 }
105 
106 /* Set an environment variable. */
107 void
108 environ_set(struct environ *env, const char *name, int flags, const char *fmt,
109     ...)
110 {
111 	struct environ_entry	*envent;
112 	va_list			 ap;
113 
114 	va_start(ap, fmt);
115 	if ((envent = environ_find(env, name)) != NULL) {
116 		envent->flags = flags;
117 		free(envent->value);
118 		xvasprintf(&envent->value, fmt, ap);
119 	} else {
120 		envent = xmalloc(sizeof *envent);
121 		envent->name = xstrdup(name);
122 		envent->flags = flags;
123 		xvasprintf(&envent->value, fmt, ap);
124 		RB_INSERT(environ, env, envent);
125 	}
126 	va_end(ap);
127 }
128 
129 /* Clear an environment variable. */
130 void
131 environ_clear(struct environ *env, const char *name)
132 {
133 	struct environ_entry	*envent;
134 
135 	if ((envent = environ_find(env, name)) != NULL) {
136 		free(envent->value);
137 		envent->value = NULL;
138 	} else {
139 		envent = xmalloc(sizeof *envent);
140 		envent->name = xstrdup(name);
141 		envent->flags = 0;
142 		envent->value = NULL;
143 		RB_INSERT(environ, env, envent);
144 	}
145 }
146 
147 /* Set an environment variable from a NAME=VALUE string. */
148 void
149 environ_put(struct environ *env, const char *var, int flags)
150 {
151 	char	*name, *value;
152 
153 	value = strchr(var, '=');
154 	if (value == NULL)
155 		return;
156 	value++;
157 
158 	name = xstrdup(var);
159 	name[strcspn(name, "=")] = '\0';
160 
161 	environ_set(env, name, flags, "%s", value);
162 	free(name);
163 }
164 
165 /* Unset an environment variable. */
166 void
167 environ_unset(struct environ *env, const char *name)
168 {
169 	struct environ_entry	*envent;
170 
171 	if ((envent = environ_find(env, name)) == NULL)
172 		return;
173 	RB_REMOVE(environ, env, envent);
174 	free(envent->name);
175 	free(envent->value);
176 	free(envent);
177 }
178 
179 /* Copy variables from a destination into a source environment. */
180 void
181 environ_update(struct options *oo, struct environ *src, struct environ *dst)
182 {
183 	struct environ_entry		*envent;
184 	struct options_entry		*o;
185 	struct options_array_item	*a;
186 	union options_value		*ov;
187 
188 	o = options_get(oo, "update-environment");
189 	if (o == NULL)
190 		return;
191 	a = options_array_first(o);
192 	while (a != NULL) {
193 		ov = options_array_item_value(a);
194 		if ((envent = environ_find(src, ov->string)) == NULL)
195 			environ_clear(dst, ov->string);
196 		else
197 			environ_set(dst, envent->name, 0, "%s", envent->value);
198 		a = options_array_next(a);
199 	}
200 }
201 
202 /* Push environment into the real environment - use after fork(). */
203 void
204 environ_push(struct environ *env)
205 {
206 	struct environ_entry	*envent;
207 
208 	environ = xcalloc(1, sizeof *environ);
209 	RB_FOREACH(envent, environ, env) {
210 		if (envent->value != NULL &&
211 		    *envent->name != '\0' &&
212 		    (~envent->flags & ENVIRON_HIDDEN))
213 			setenv(envent->name, envent->value, 1);
214 	}
215 }
216 
217 /* Log the environment. */
218 void
219 environ_log(struct environ *env, const char *fmt, ...)
220 {
221 	struct environ_entry	*envent;
222 	va_list			 ap;
223 	char			*prefix;
224 
225 	va_start(ap, fmt);
226 	vasprintf(&prefix, fmt, ap);
227 	va_end(ap);
228 
229 	RB_FOREACH(envent, environ, env) {
230 		if (envent->value != NULL && *envent->name != '\0') {
231 			log_debug("%s%s=%s", prefix, envent->name,
232 			    envent->value);
233 		}
234 	}
235 
236 	free(prefix);
237 }
238 
239 /* Create initial environment for new child. */
240 struct environ *
241 environ_for_session(struct session *s, int no_TERM)
242 {
243 	struct environ	*env;
244 	const char	*value;
245 	int		 idx;
246 
247 	env = environ_create();
248 	environ_copy(global_environ, env);
249 	if (s != NULL)
250 		environ_copy(s->environ, env);
251 
252 	if (!no_TERM) {
253 		value = options_get_string(global_options, "default-terminal");
254 		environ_set(env, "TERM", 0, "%s", value);
255 		environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux");
256 		environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion());
257 	}
258 
259 	if (s != NULL)
260 		idx = s->id;
261 	else
262 		idx = -1;
263 	environ_set(env, "TMUX", 0, "%s,%ld,%d", socket_path, (long)getpid(),
264 	    idx);
265 
266 	return (env);
267 }
268