#include "stand.h"
#include <string.h>
struct env_var *environ = NULL;
struct env_var *
env_getenv(const char *name)
{
struct env_var *ev;
for (ev = environ; ev != NULL; ev = ev->ev_next)
if (!strcmp(ev->ev_name, name))
break;
return (ev);
}
int
env_setenv(const char *name, int flags, const void *value,
ev_sethook_t sethook, ev_unsethook_t unsethook)
{
struct env_var *ev, *curr, *last;
if ((ev = env_getenv(name)) != NULL) {
if (!(flags & EV_NOKENV))
ev->ev_flags &= ~EV_NOKENV;
if ((ev->ev_sethook != NULL) && !(flags & EV_NOHOOK))
return (ev->ev_sethook(ev, flags, value));
if (ev->ev_value != NULL && (ev->ev_flags & EV_DYNAMIC) != 0)
free(ev->ev_value);
ev->ev_value = NULL;
ev->ev_flags &= ~EV_DYNAMIC;
} else {
ev = malloc(sizeof(struct env_var));
ev->ev_name = strdup(name);
ev->ev_value = NULL;
ev->ev_flags = 0;
ev->ev_sethook = sethook;
ev->ev_unsethook = unsethook;
ev->ev_prev = NULL;
ev->ev_next = NULL;
for (last = NULL, curr = environ; curr != NULL;
last = curr, curr = curr->ev_next) {
if (strcmp(ev->ev_name, curr->ev_name) < 0) {
if (curr->ev_prev) {
curr->ev_prev->ev_next = ev;
} else {
environ = ev;
}
ev->ev_next = curr;
ev->ev_prev = curr->ev_prev;
curr->ev_prev = ev;
break;
}
}
if (curr == NULL) {
if (last == NULL) {
environ = ev;
} else {
last->ev_next = ev;
ev->ev_prev = last;
}
}
}
if (flags & EV_VOLATILE) {
ev->ev_value = strdup(value);
flags |= EV_DYNAMIC;
} else {
ev->ev_value = (char *)value;
}
ev->ev_flags |= flags & (EV_DYNAMIC | EV_NOKENV);
return (0);
}
char *
getenv(const char *name)
{
struct env_var *ev;
if ((ev = env_getenv(name)) != NULL) {
if (ev->ev_value != NULL)
return (ev->ev_value);
return ("");
}
return (NULL);
}
int
setenv(const char *name, const char *value, int overwrite)
{
if (overwrite || (env_getenv(name) == NULL))
return (env_setenv(name, EV_VOLATILE, value, NULL, NULL));
return (0);
}
int
putenv(char *string)
{
char *value, *copy;
int result;
copy = strdup(string);
if ((value = strchr(copy, '=')) != NULL)
*(value++) = 0;
result = setenv(copy, value, 1);
free(copy);
return (result);
}
int
unsetenv(const char *name)
{
struct env_var *ev;
int err;
err = 0;
if ((ev = env_getenv(name)) == NULL) {
err = ENOENT;
} else {
if (ev->ev_unsethook != NULL)
err = ev->ev_unsethook(ev);
if (err == 0) {
env_discard(ev);
}
}
return (err);
}
void
env_discard(struct env_var *ev)
{
if (ev->ev_prev)
ev->ev_prev->ev_next = ev->ev_next;
if (ev->ev_next)
ev->ev_next->ev_prev = ev->ev_prev;
if (environ == ev)
environ = ev->ev_next;
free(ev->ev_name);
if (ev->ev_value != NULL && (ev->ev_flags & EV_DYNAMIC) != 0)
free(ev->ev_value);
free(ev);
}
int
env_noset(struct env_var *ev __unused, int flags __unused,
const void *value __unused)
{
return (EPERM);
}
bool
is_restricted_var(const char *name)
{
const char *allowed[] = {
"boot_function=",
"boot_phase=",
"boot_recover_cli=",
"boot_recover_volume=",
"boot_safe=",
"boot_set=",
"boot_single=",
"boot_verbose=",
#ifdef ENV_IS_RESTRICTED_ALLOWED_LIST
ENV_IS_RESTRICTED_ALLOWED_LIST,
#endif
NULL,
};
const char *restricted[] = {
"boot",
"init",
"loader.ve.",
"rootfs",
"secur",
"vfs.",
#ifdef ENV_IS_RESTRICTED_LIST
ENV_IS_RESTRICTED_LIST,
#endif
NULL,
};
const char **cp;
int ok = -1;
for (cp = restricted; *cp; cp++) {
if (strncmp(name, *cp, strlen(*cp)) == 0) {
ok = 0;
break;
}
}
if (!ok) {
for (cp = allowed; *cp; cp++) {
if (strncmp(name, *cp, strlen(*cp)) == 0) {
ok = 1;
break;
}
}
}
return (ok == 0);
}
static bool check_restricted = false;
void
set_check_restricted(bool b)
{
check_restricted = b;
}
int
boot_setenv(const char *name, const char *value)
{
if (check_restricted && is_restricted_var(name)) {
errno = EPERM;
return -1;
}
return setenv(name, value, 1);
}
int
env_nounset(struct env_var *ev __unused)
{
return (EPERM);
}