1 /* $OpenBSD: env.c,v 1.5 2016/09/15 00:58:23 deraadt Exp $ */
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.
20 #include <sys/types.h>
45 RB_ENTRY(envnode
) node
;
52 RB_HEAD(envtree
, envnode
) root
;
56 static inline char const *action_to_string(enum action action
) __returns_nonnull
;
57 static inline char const *action_to_string(enum action action
)
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
);
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
)
97 static inline struct envnode
*createnode(char const *restrict name
,
98 char const *restrict value
,
100 __returns_nonnull
__nonnull((1)) __warn_unused_result __malloc
;
101 static inline struct envnode
*createnode(char const *restrict name
,
102 char const *restrict value
,
105 struct envnode
*node
= xmalloc(sizeof(*node
));
106 node
->name
= xstrdup(name
);
107 node
->value
= value
!= NULL
? xstrdup(value
) : NULL
;
108 node
->action
= action
;
112 static inline struct envnode
*insertnode(struct env
*restrict env
,
113 struct envnode
*restrict node
)
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
);
121 env
->count
+= (node
->action
!= ACTION_UNSET
);
123 errx(EXIT_FAILURE
, "there is an element with the colliding key %s", node
->name
);
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
);
136 env
->count
-= (node
->action
!= ACTION_UNSET
);
138 errx(EXIT_FAILURE
, "there is no element with the given key %s", node
->name
);
143 static inline int process_collision(struct env
*const restrict env
,
144 struct envnode
*const restrict node
,
145 char const *const restrict name
,
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
,
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
));
160 switch (node
->action
) {
162 /* Inherited environs never repeat,
163 so just remove previous entry. */
164 destroynode(removenode(env
, node
));
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
));
173 warnx("%s environ specified twice in %senv section", name
, action_to_string(action
));
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
},
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
,
210 insertnode(env
, createnode(envlist
[i
].name
,
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
) {
234 /* At last, we have something to insert. */
235 insertnode(env
, createnode(key
.name
, key
.value
, ACTION_INHERIT
));
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
, '=');
253 key
.name
= xstrndup(key
.name
, eq
- key
.name
);
256 if (*key
.value
!= '$') {
257 warnx("$ character expected just after =");
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)
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
));
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
, '=');
293 warnc(EINVAL
, "expected NAME=VALUE, but got: %s", e
);
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)
305 insertnode(env
, createnode(key
.name
, eq
+ 1, ACTION_SET
));
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
);
327 if ((node
= RB_FIND(envtree
, &env
->root
, &key
)) != NULL
)
328 if (process_collision(env
, node
, key
.name
, ACTION_UNSET
) != 0)
331 insertnode(env
, createnode(key
.name
, NULL
, ACTION_UNSET
));
337 struct env
*createenv(void)
339 struct env
*env
= xmalloc(sizeof(*env
));
347 static inline __malloc __returns_nonnull
__nonnull((1)) __warn_unused_result
char **flattenenv(struct env
*env
)
349 struct envnode
*node
, *next_node
;
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';
367 destroynode(removenode(env
, node
));
377 char **prepenv(struct env
*const restrict env
)
379 return flattenenv(env
);