Attempt to create .deps directory every time we build objects.
[doas.git] / env.c
blob4630a0bcb29621d892f03dbb187c3688f39a3cca
1 /* $OpenBSD: env.c,v 1.5 2016/09/15 00:58:23 deraadt Exp $ */
2 /*
3 * Copyright (c) 2016 Ted Unangst <tedu@openbsd.org>
4 * Copyright (c) 2021-2022 Sergey Sushilin <sergeysushilin@protonmail.com>
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.
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 USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/tree.h>
20 #include <sys/types.h>
22 #include <assert.h>
23 #include <err.h>
24 #include <errno.h>
25 #include <pwd.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
31 #include "compat.h"
32 #include "env.h"
33 #include "rule.h"
34 #include "wrappers.h"
36 enum action {
37 ACTION_INHERIT,
38 ACTION_KEEP,
39 ACTION_SET,
40 ACTION_UNSET,
41 ACTION_FORCE
44 struct envnode {
45 RB_ENTRY(envnode) node;
46 char const *name;
47 char const *value;
48 enum action action;
51 struct env {
52 RB_HEAD(envtree, envnode) root;
53 u_int count;
56 static inline char const *action_to_string(enum action action) __returns_nonnull;
57 static inline char const *action_to_string(enum action action)
59 switch (action) {
60 case ACTION_INHERIT:
61 return "inherit";
62 case ACTION_KEEP:
63 return "keep";
64 case ACTION_SET:
65 return "set";
66 case ACTION_UNSET:
67 return "unset";
68 case ACTION_FORCE:
69 return "force";
72 err(EXIT_FAILURE, "unknown action");
75 static inline int envcmp(struct envnode const *restrict a,
76 struct envnode const *restrict b)
77 __nonnull((1, 2)) __warn_unused_result;
78 static inline int envcmp(struct envnode const *restrict a,
79 struct envnode const *restrict b)
81 return strcmp(a->name, b->name);
84 #if defined(__DragonFly__)
85 RB_PROTOTYPE_STATIC(envtree, envnode, node, envcmp);
86 #endif
87 RB_GENERATE_STATIC(envtree, envnode, node, envcmp);
89 static inline void destroynode(struct envnode *node) __nonnull((1));
90 static inline void destroynode(struct envnode *node)
92 xfree(node->name);
93 xfree(node->value);
94 xfree(node);
97 static inline struct envnode *createnode(char const *restrict name,
98 char const *restrict value,
99 enum action action)
100 __returns_nonnull __nonnull((1)) __warn_unused_result __malloc;
101 static inline struct envnode *createnode(char const *restrict name,
102 char const *restrict value,
103 enum action action)
105 struct envnode *node = xmalloc(sizeof(*node));
106 node->name = xstrdup(name);
107 node->value = value != NULL ? xstrdup(value) : NULL;
108 node->action = action;
109 return node;
112 static inline struct envnode *insertnode(struct env *restrict env,
113 struct envnode *restrict node)
114 __nonnull((1, 2));
115 static inline struct envnode *insertnode(struct env *restrict env,
116 struct envnode *restrict node)
118 struct envnode *en = RB_INSERT(envtree, &env->root, node);
120 if (en == NULL)
121 env->count += (node->action != ACTION_UNSET);
122 else
123 errx(EXIT_FAILURE, "there is an element with the colliding key %s", node->name);
125 return en;
127 static inline struct envnode *removenode(struct env *restrict env,
128 struct envnode *restrict node)
129 __returns_nonnull __nonnull((1, 2));
130 static inline struct envnode *removenode(struct env *restrict env,
131 struct envnode *restrict node)
133 struct envnode *en = RB_REMOVE(envtree, &env->root, node);
135 if (en != NULL)
136 env->count -= (node->action != ACTION_UNSET);
137 else
138 errx(EXIT_FAILURE, "there is no element with the given key %s", node->name);
140 return en;
143 static inline int process_collision(struct env *const restrict env,
144 struct envnode *const restrict node,
145 char const *const restrict name,
146 enum action action)
147 __nonnull((1, 2, 3));
148 static inline int process_collision(struct env *const restrict env,
149 struct envnode *const restrict node,
150 char const *const restrict name,
151 enum action action)
153 if ((node->action == ACTION_FORCE && action != ACTION_INHERIT)
154 || (action == ACTION_FORCE && node->action != ACTION_INHERIT)) {
155 warnx("%s: attempt to apply %s action to forced environ variable",
156 name, action_to_string(action == ACTION_FORCE ? node->action : action));
157 return -1;
160 switch (node->action) {
161 case ACTION_INHERIT:
162 /* Inherited environs never repeat,
163 so just remove previous entry. */
164 destroynode(removenode(env, node));
165 break;
166 case ACTION_KEEP:
167 case ACTION_SET:
168 case ACTION_UNSET:
169 if (action != node->action)
170 warnx("%s: attempt to do two different actions (%s and %s)",
171 name, action_to_string(node->action), action_to_string(action));
172 else
173 warnx("%s environ specified twice in %senv section", name, action_to_string(action));
175 return -1;
176 case ACTION_FORCE:
177 break;
180 return 0;
183 void defaultenv(struct env *const restrict env,
184 struct passwd const *const restrict original,
185 struct passwd const *const restrict target)
187 struct envnode envlist[] = {
188 { .name = "DISPLAY", .value = getenv("DISPLAY"), .action = ACTION_INHERIT },
189 { .name = "TERM", .value = getenv("TERM"), .action = ACTION_INHERIT },
190 { .name = "HOME", .value = target->pw_dir, .action = ACTION_INHERIT },
191 { .name = "DOAS_USER", .value = original->pw_name, .action = ACTION_FORCE },
192 { .name = "LOGNAME", .value = target->pw_name, .action = ACTION_FORCE },
193 { .name = "PATH", .value = GLOBAL_PATH, .action = ACTION_FORCE },
194 { .name = "SHELL", .value = target->pw_shell, .action = ACTION_FORCE },
195 { .name = "USER", .value = target->pw_name, .action = ACTION_FORCE },
197 int i;
199 for (i = 0; i < countof(envlist); i++) {
200 if (envlist[i].value != NULL) {
201 struct envnode *node;
203 /* Process previous copies. */
204 if ((node = RB_FIND(envtree, &env->root, &envlist[i])) != NULL)
205 process_collision(env, node,
206 envlist[i].name,
207 envlist[i].action);
209 /* Assign value. */
210 insertnode(env, createnode(envlist[i].name,
211 envlist[i].value,
212 envlist[i].action));
217 void inheritenv(struct env *env)
219 char const *const *envp;
221 for (envp = (char const *const *)environ; *envp != NULL; envp++) {
222 char const *e = *envp;
223 char const *const eq = strchr(e, '=');
224 struct envnode *node;
225 struct envnode key = { .name = NULL, .value = NULL };
227 key.name = xstrndup(e, eq - e);
229 /* Process previous copies. */
230 /* If there is any environ with the same name, then yield. */
231 if ((node = RB_FIND(envtree, &env->root, &key)) == NULL) {
232 key.value = eq + 1;
234 /* At last, we have something to insert. */
235 insertnode(env, createnode(key.name, key.value, ACTION_INHERIT));
238 xfree(key.name);
242 int keepenv(struct env *const restrict env,
243 char const *const restrict *const restrict envlist)
245 char const *const restrict *envp;
247 for (envp = envlist; *envp != NULL; envp++) {
248 struct envnode *node;
249 struct envnode key = { .name = *envp, .value = NULL };
250 char const *eq = strchr(key.name, '=');
252 if (eq != NULL) {
253 key.name = xstrndup(key.name, eq - key.name);
254 key.value = eq + 1;
256 if (*key.value != '$') {
257 warnx("$ character expected just after =");
258 return -1;
261 key.value++;
264 /* Process previous copies. */
265 if ((node = RB_FIND(envtree, &env->root, &key)) != NULL)
266 if (process_collision(env, node, key.name, ACTION_KEEP) != 0)
267 return -1;
269 /* Inherit from environ. */
270 key.value = getenv(key.value != NULL ? key.value : key.name);
272 /* At last, we have something to insert. */
273 if (key.value != NULL)
274 insertnode(env, createnode(key.name, key.value, ACTION_KEEP));
277 return 0;
280 int fillenv(struct env *restrict env,
281 char const *const restrict *const restrict envlist)
283 char const *const restrict *envp;
285 for (envp = envlist; *envp != NULL; envp++) {
286 char const *const e = *envp;
287 struct envnode *node, key = { .name = NULL, .value = NULL };
289 /* Parse out environ name. */
290 char const *const eq = strchr(e, '=');
292 if (eq == NULL) {
293 warnc(EINVAL, "expected NAME=VALUE, but got: %s", e);
294 return -1;
297 key.name = xstrndup(e, eq - e);
299 /* Process previous copies. */
300 if ((node = RB_FIND(envtree, &env->root, &key)) != NULL)
301 if (process_collision(env, node, key.name, ACTION_SET) != 0)
302 return -1;
304 /* Assign value. */
305 insertnode(env, createnode(key.name, eq + 1, ACTION_SET));
307 xfree(key.name);
310 return 0;
313 int unfillenv(struct env *restrict env,
314 char const *const restrict *const restrict envlist)
316 char const *const restrict *envp;
318 for (envp = envlist; *envp != NULL; envp++) {
319 struct envnode *node;
320 struct envnode key = { .name = *envp, .value = NULL };
322 if (strchr(key.name, '=') != NULL) {
323 warnc(EINVAL, "unexpected '=' character in environ name: %s", key.name);
324 return -1;
327 if ((node = RB_FIND(envtree, &env->root, &key)) != NULL)
328 if (process_collision(env, node, key.name, ACTION_UNSET) != 0)
329 return -1;
331 insertnode(env, createnode(key.name, NULL, ACTION_UNSET));
334 return 0;
337 struct env *createenv(void)
339 struct env *env = xmalloc(sizeof(*env));
341 RB_INIT(&env->root);
342 env->count = 0;
344 return env;
347 static inline __malloc __returns_nonnull __nonnull((1)) __warn_unused_result char **flattenenv(struct env *env)
349 struct envnode *node, *next_node;
350 u_int i = 0;
351 char **envp = xreallocarray(NULL, env->count + 1, sizeof(char *));
353 RB_FOREACH_SAFE(node, envtree, &env->root, next_node) {
354 if (node->action != ACTION_UNSET) {
355 size_t name_length = strlen(node->name);
356 size_t value_length = strlen(node->value);
357 char *e = xmalloc(name_length + 1 + value_length + 1);
359 memcpy(e, node->name, name_length);
360 e[name_length] = '=';
361 memcpy(e + name_length + 1, node->value, value_length);
362 e[name_length + value_length + 1] = '\0';
364 envp[i++] = e;
367 destroynode(removenode(env, node));
370 xfree(env);
372 envp[i] = NULL;
374 return envp;
377 char **prepenv(struct env *const restrict env)
379 return flattenenv(env);