xref: /openbsd-src/usr.bin/tmux/environ.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /* $OpenBSD: environ.c,v 1.23 2019/04/25 19:36:59 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, "%s", envent->value);
91 	}
92 }
93 
94 /* Find an environment variable. */
95 struct environ_entry *
96 environ_find(struct environ *env, const char *name)
97 {
98 	struct environ_entry	envent;
99 
100 	envent.name = (char *) name;
101 	return (RB_FIND(environ, env, &envent));
102 }
103 
104 /* Set an environment variable. */
105 void
106 environ_set(struct environ *env, const char *name, const char *fmt, ...)
107 {
108 	struct environ_entry	*envent;
109 	va_list			 ap;
110 
111 	va_start(ap, fmt);
112 	if ((envent = environ_find(env, name)) != NULL) {
113 		free(envent->value);
114 		xvasprintf(&envent->value, fmt, ap);
115 	} else {
116 		envent = xmalloc(sizeof *envent);
117 		envent->name = xstrdup(name);
118 		xvasprintf(&envent->value, fmt, ap);
119 		RB_INSERT(environ, env, envent);
120 	}
121 	va_end(ap);
122 }
123 
124 /* Clear an environment variable. */
125 void
126 environ_clear(struct environ *env, const char *name)
127 {
128 	struct environ_entry	*envent;
129 
130 	if ((envent = environ_find(env, name)) != NULL) {
131 		free(envent->value);
132 		envent->value = NULL;
133 	} else {
134 		envent = xmalloc(sizeof *envent);
135 		envent->name = xstrdup(name);
136 		envent->value = NULL;
137 		RB_INSERT(environ, env, envent);
138 	}
139 }
140 
141 /* Set an environment variable from a NAME=VALUE string. */
142 void
143 environ_put(struct environ *env, const char *var)
144 {
145 	char	*name, *value;
146 
147 	value = strchr(var, '=');
148 	if (value == NULL)
149 		return;
150 	value++;
151 
152 	name = xstrdup(var);
153 	name[strcspn(name, "=")] = '\0';
154 
155 	environ_set(env, name, "%s", value);
156 	free(name);
157 }
158 
159 /* Unset an environment variable. */
160 void
161 environ_unset(struct environ *env, const char *name)
162 {
163 	struct environ_entry	*envent;
164 
165 	if ((envent = environ_find(env, name)) == NULL)
166 		return;
167 	RB_REMOVE(environ, env, envent);
168 	free(envent->name);
169 	free(envent->value);
170 	free(envent);
171 }
172 
173 /* Copy variables from a destination into a source * environment. */
174 void
175 environ_update(struct options *oo, struct environ *src, struct environ *dst)
176 {
177 	struct environ_entry		*envent;
178 	struct options_entry		*o;
179 	struct options_array_item	*a;
180 	union options_value		*ov;
181 
182 	o = options_get(oo, "update-environment");
183 	if (o == NULL)
184 		return;
185 	a = options_array_first(o);
186 	while (a != NULL) {
187 		ov = options_array_item_value(a);
188 		if ((envent = environ_find(src, ov->string)) == NULL)
189 			environ_clear(dst, ov->string);
190 		else
191 			environ_set(dst, envent->name, "%s", envent->value);
192 		a = options_array_next(a);
193 	}
194 }
195 
196 /* Push environment into the real environment - use after fork(). */
197 void
198 environ_push(struct environ *env)
199 {
200 	struct environ_entry	*envent;
201 
202 	environ = xcalloc(1, sizeof *environ);
203 	RB_FOREACH(envent, environ, env) {
204 		if (envent->value != NULL && *envent->name != '\0')
205 			setenv(envent->name, envent->value, 1);
206 	}
207 }
208 
209 /* Log the environment. */
210 void
211 environ_log(struct environ *env, const char *fmt, ...)
212 {
213 	struct environ_entry	*envent;
214 	va_list			 ap;
215 	char			*prefix;
216 
217 	va_start(ap, fmt);
218 	vasprintf(&prefix, fmt, ap);
219 	va_end(ap);
220 
221 	RB_FOREACH(envent, environ, env) {
222 		if (envent->value != NULL && *envent->name != '\0') {
223 			log_debug("%s%s=%s", prefix, envent->name,
224 			    envent->value);
225 		}
226 	}
227 
228 	free(prefix);
229 }
230 
231 /* Create initial environment for new child. */
232 struct environ *
233 environ_for_session(struct session *s, int no_TERM)
234 {
235 	struct environ	*env;
236 	const char	*value;
237 	int		 idx;
238 
239 	env = environ_create();
240 	environ_copy(global_environ, env);
241 	if (s != NULL)
242 		environ_copy(s->environ, env);
243 
244 	if (!no_TERM) {
245 		value = options_get_string(global_options, "default-terminal");
246 		environ_set(env, "TERM", "%s", value);
247 	}
248 
249 	if (s != NULL)
250 		idx = s->id;
251 	else
252 		idx = -1;
253 	environ_set(env, "TMUX", "%s,%ld,%d", socket_path, (long)getpid(), idx);
254 
255 	return (env);
256 }
257