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