Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/kern_environment.c
Show All 38 Lines | |||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/mutex.h> | #include <sys/rmlock.h> | ||||
#include <sys/priv.h> | #include <sys/priv.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/sysent.h> | #include <sys/sysent.h> | ||||
#include <sys/sysproto.h> | #include <sys/sysproto.h> | ||||
#include <sys/libkern.h> | #include <sys/libkern.h> | ||||
#include <sys/kenv.h> | #include <sys/kenv.h> | ||||
#include <sys/limits.h> | #include <sys/limits.h> | ||||
Show All 17 Lines | |||||
char *md_envp; | char *md_envp; | ||||
static int md_env_len; | static int md_env_len; | ||||
static int md_env_pos; | static int md_env_pos; | ||||
static char *kernenv_next(char *); | static char *kernenv_next(char *); | ||||
/* dynamic environment variables */ | /* dynamic environment variables */ | ||||
char **kenvp; | char **kenvp; | ||||
struct mtx kenv_lock; | struct rmlock kenv_lock; | ||||
/* | /* | ||||
* No need to protect this with a mutex since SYSINITS are single threaded. | * No need to protect this with a mutex since SYSINITS are single threaded. | ||||
*/ | */ | ||||
bool dynamic_kenv; | bool dynamic_kenv; | ||||
#define KENV_CHECK if (!dynamic_kenv) \ | #define KENV_CHECK if (!dynamic_kenv) \ | ||||
panic("%s: called before SI_SUB_KMEM", __func__) | panic("%s: called before SI_SUB_KMEM", __func__) | ||||
static char *getenv_string_buffer(const char *); | static char *getenv_string_buffer(const char *); | ||||
int | int | ||||
sys_kenv(td, uap) | sys_kenv(td, uap) | ||||
struct thread *td; | struct thread *td; | ||||
struct kenv_args /* { | struct kenv_args /* { | ||||
int what; | int what; | ||||
const char *name; | const char *name; | ||||
char *value; | char *value; | ||||
int len; | int len; | ||||
} */ *uap; | } */ *uap; | ||||
{ | { | ||||
struct rm_priotracker tracker; | |||||
char *name, *value, *buffer = NULL; | char *name, *value, *buffer = NULL; | ||||
size_t len, done, needed, buflen; | size_t len, done, needed, buflen; | ||||
int error, i; | int error, i; | ||||
KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = false")); | KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = false")); | ||||
error = 0; | error = 0; | ||||
if (uap->what == KENV_DUMP) { | if (uap->what == KENV_DUMP) { | ||||
#ifdef MAC | #ifdef MAC | ||||
error = mac_kenv_check_dump(td->td_ucred); | error = mac_kenv_check_dump(td->td_ucred); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
#endif | #endif | ||||
done = needed = 0; | done = needed = 0; | ||||
buflen = uap->len; | buflen = uap->len; | ||||
if (buflen > KENV_SIZE * (KENV_MNAMELEN + kenv_mvallen + 2)) | if (buflen > KENV_SIZE * (KENV_MNAMELEN + kenv_mvallen + 2)) | ||||
buflen = KENV_SIZE * (KENV_MNAMELEN + | buflen = KENV_SIZE * (KENV_MNAMELEN + | ||||
kenv_mvallen + 2); | kenv_mvallen + 2); | ||||
if (uap->len > 0 && uap->value != NULL) | if (uap->len > 0 && uap->value != NULL) | ||||
buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO); | buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO); | ||||
mtx_lock(&kenv_lock); | rm_rlock(&kenv_lock, &tracker); | ||||
for (i = 0; kenvp[i] != NULL; i++) { | for (i = 0; kenvp[i] != NULL; i++) { | ||||
len = strlen(kenvp[i]) + 1; | len = strlen(kenvp[i]) + 1; | ||||
needed += len; | needed += len; | ||||
len = min(len, buflen - done); | len = min(len, buflen - done); | ||||
/* | /* | ||||
* If called with a NULL or insufficiently large | * If called with a NULL or insufficiently large | ||||
* buffer, just keep computing the required size. | * buffer, just keep computing the required size. | ||||
*/ | */ | ||||
if (uap->value != NULL && buffer != NULL && len > 0) { | if (uap->value != NULL && buffer != NULL && len > 0) { | ||||
bcopy(kenvp[i], buffer + done, len); | bcopy(kenvp[i], buffer + done, len); | ||||
done += len; | done += len; | ||||
} | } | ||||
} | } | ||||
mtx_unlock(&kenv_lock); | rm_runlock(&kenv_lock, &tracker); | ||||
if (buffer != NULL) { | if (buffer != NULL) { | ||||
error = copyout(buffer, uap->value, done); | error = copyout(buffer, uap->value, done); | ||||
free(buffer, M_TEMP); | free(buffer, M_TEMP); | ||||
} | } | ||||
td->td_retval[0] = ((done == needed) ? 0 : needed); | td->td_retval[0] = ((done == needed) ? 0 : needed); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 245 Lines • ▼ Show 20 Lines | init_dynamic_kenv(void *data __unused) | ||||
kenvp = malloc((KENV_SIZE + 1) * sizeof(char *), M_KENV, | kenvp = malloc((KENV_SIZE + 1) * sizeof(char *), M_KENV, | ||||
M_WAITOK | M_ZERO); | M_WAITOK | M_ZERO); | ||||
dynamic_envpos = 0; | dynamic_envpos = 0; | ||||
init_dynamic_kenv_from(md_envp, &dynamic_envpos); | init_dynamic_kenv_from(md_envp, &dynamic_envpos); | ||||
init_dynamic_kenv_from(kern_envp, &dynamic_envpos); | init_dynamic_kenv_from(kern_envp, &dynamic_envpos); | ||||
kenvp[dynamic_envpos] = NULL; | kenvp[dynamic_envpos] = NULL; | ||||
mtx_init(&kenv_lock, "kernel environment", NULL, MTX_DEF); | rm_init(&kenv_lock, "kernel environment"); | ||||
dynamic_kenv = true; | dynamic_kenv = true; | ||||
} | } | ||||
SYSINIT(kenv, SI_SUB_KMEM + 1, SI_ORDER_FIRST, init_dynamic_kenv, NULL); | SYSINIT(kenv, SI_SUB_KMEM + 1, SI_ORDER_FIRST, init_dynamic_kenv, NULL); | ||||
void | void | ||||
freeenv(char *env) | freeenv(char *env) | ||||
{ | { | ||||
Show All 23 Lines | _getenv_dynamic_locked(const char *name, int *idx) | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
static char * | static char * | ||||
_getenv_dynamic(const char *name, int *idx) | _getenv_dynamic(const char *name, int *idx) | ||||
{ | { | ||||
mtx_assert(&kenv_lock, MA_OWNED); | rm_assert(&kenv_lock, RA_LOCKED); | ||||
return (_getenv_dynamic_locked(name, idx)); | return (_getenv_dynamic_locked(name, idx)); | ||||
} | } | ||||
static char * | static char * | ||||
_getenv_static_from(char *chkenv, const char *name) | _getenv_static_from(char *chkenv, const char *name) | ||||
{ | { | ||||
char *cp, *ep; | char *cp, *ep; | ||||
int len; | int len; | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* Test if an environment variable is defined. | * Test if an environment variable is defined. | ||||
*/ | */ | ||||
int | int | ||||
testenv(const char *name) | testenv(const char *name) | ||||
{ | { | ||||
struct rm_priotracker tracker; | |||||
char *cp; | char *cp; | ||||
if (dynamic_kenv) { | if (dynamic_kenv) { | ||||
mtx_lock(&kenv_lock); | rm_rlock(&kenv_lock, &tracker); | ||||
cp = _getenv_dynamic(name, NULL); | cp = _getenv_dynamic(name, NULL); | ||||
mtx_unlock(&kenv_lock); | rm_runlock(&kenv_lock, &tracker); | ||||
} else | } else | ||||
cp = _getenv_static(name); | cp = _getenv_static(name); | ||||
if (cp != NULL) | if (cp != NULL) | ||||
return (1); | return (1); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
Show All 39 Lines | kern_setenv(const char *name, const char *value) | ||||
if (namelen > KENV_MNAMELEN + 1) | if (namelen > KENV_MNAMELEN + 1) | ||||
return (-1); | return (-1); | ||||
vallen = strlen(value) + 1; | vallen = strlen(value) + 1; | ||||
if (vallen > kenv_mvallen + 1) | if (vallen > kenv_mvallen + 1) | ||||
return (-1); | return (-1); | ||||
buf = malloc(namelen + vallen, M_KENV, M_WAITOK); | buf = malloc(namelen + vallen, M_KENV, M_WAITOK); | ||||
sprintf(buf, "%s=%s", name, value); | sprintf(buf, "%s=%s", name, value); | ||||
mtx_lock(&kenv_lock); | rm_wlock(&kenv_lock); | ||||
cp = _getenv_dynamic(name, &i); | cp = _getenv_dynamic(name, &i); | ||||
if (cp != NULL) { | if (cp != NULL) { | ||||
oldenv = kenvp[i]; | oldenv = kenvp[i]; | ||||
kenvp[i] = buf; | kenvp[i] = buf; | ||||
mtx_unlock(&kenv_lock); | rm_wunlock(&kenv_lock); | ||||
free(oldenv, M_KENV); | free(oldenv, M_KENV); | ||||
} else { | } else { | ||||
/* We add the option if it wasn't found */ | /* We add the option if it wasn't found */ | ||||
for (i = 0; (cp = kenvp[i]) != NULL; i++) | for (i = 0; (cp = kenvp[i]) != NULL; i++) | ||||
; | ; | ||||
/* Bounds checking */ | /* Bounds checking */ | ||||
if (i < 0 || i >= KENV_SIZE) { | if (i < 0 || i >= KENV_SIZE) { | ||||
free(buf, M_KENV); | free(buf, M_KENV); | ||||
mtx_unlock(&kenv_lock); | rm_wunlock(&kenv_lock); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
kenvp[i] = buf; | kenvp[i] = buf; | ||||
kenvp[i + 1] = NULL; | kenvp[i + 1] = NULL; | ||||
mtx_unlock(&kenv_lock); | rm_wunlock(&kenv_lock); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Unset an environment variable string. | * Unset an environment variable string. | ||||
*/ | */ | ||||
int | int | ||||
kern_unsetenv(const char *name) | kern_unsetenv(const char *name) | ||||
{ | { | ||||
char *cp, *oldenv; | char *cp, *oldenv; | ||||
int i, j; | int i, j; | ||||
KENV_CHECK; | KENV_CHECK; | ||||
mtx_lock(&kenv_lock); | rm_wlock(&kenv_lock); | ||||
cp = _getenv_dynamic(name, &i); | cp = _getenv_dynamic(name, &i); | ||||
if (cp != NULL) { | if (cp != NULL) { | ||||
oldenv = kenvp[i]; | oldenv = kenvp[i]; | ||||
for (j = i + 1; kenvp[j] != NULL; j++) | for (j = i + 1; kenvp[j] != NULL; j++) | ||||
kenvp[i++] = kenvp[j]; | kenvp[i++] = kenvp[j]; | ||||
kenvp[i] = NULL; | kenvp[i] = NULL; | ||||
mtx_unlock(&kenv_lock); | rm_wunlock(&kenv_lock); | ||||
zfree(oldenv, M_KENV); | zfree(oldenv, M_KENV); | ||||
return (0); | return (0); | ||||
} | } | ||||
mtx_unlock(&kenv_lock); | rm_wunlock(&kenv_lock); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
/* | /* | ||||
* Return a buffer containing the string value from an environment variable | * Return a buffer containing the string value from an environment variable | ||||
*/ | */ | ||||
static char * | static char * | ||||
getenv_string_buffer(const char *name) | getenv_string_buffer(const char *name) | ||||
{ | { | ||||
struct rm_priotracker tracker; | |||||
char *cp, *ret; | char *cp, *ret; | ||||
int len; | int len; | ||||
if (dynamic_kenv) { | if (dynamic_kenv) { | ||||
len = KENV_MNAMELEN + 1 + kenv_mvallen + 1; | len = KENV_MNAMELEN + 1 + kenv_mvallen + 1; | ||||
ret = uma_zalloc(kenv_zone, M_WAITOK | M_ZERO); | ret = uma_zalloc(kenv_zone, M_WAITOK | M_ZERO); | ||||
mtx_lock(&kenv_lock); | rm_rlock(&kenv_lock, &tracker); | ||||
cp = _getenv_dynamic(name, NULL); | cp = _getenv_dynamic(name, NULL); | ||||
if (cp != NULL) | if (cp != NULL) | ||||
strlcpy(ret, cp, len); | strlcpy(ret, cp, len); | ||||
mtx_unlock(&kenv_lock); | rm_runlock(&kenv_lock, &tracker); | ||||
if (cp == NULL) { | if (cp == NULL) { | ||||
uma_zfree(kenv_zone, ret); | uma_zfree(kenv_zone, ret); | ||||
ret = NULL; | ret = NULL; | ||||
} | } | ||||
} else | } else | ||||
ret = _getenv_static(name); | ret = _getenv_static(name); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
/* | /* | ||||
* Return a string value from an environment variable. | * Return a string value from an environment variable. | ||||
*/ | */ | ||||
int | int | ||||
getenv_string(const char *name, char *data, int size) | getenv_string(const char *name, char *data, int size) | ||||
{ | { | ||||
struct rm_priotracker tracker; | |||||
char *cp; | char *cp; | ||||
if (dynamic_kenv) { | if (dynamic_kenv) { | ||||
mtx_lock(&kenv_lock); | rm_rlock(&kenv_lock, &tracker); | ||||
cp = _getenv_dynamic(name, NULL); | cp = _getenv_dynamic(name, NULL); | ||||
if (cp != NULL) | if (cp != NULL) | ||||
strlcpy(data, cp, size); | strlcpy(data, cp, size); | ||||
mtx_unlock(&kenv_lock); | rm_runlock(&kenv_lock, &tracker); | ||||
} else { | } else { | ||||
cp = _getenv_static(name); | cp = _getenv_static(name); | ||||
if (cp != NULL) | if (cp != NULL) | ||||
strlcpy(data, cp, size); | strlcpy(data, cp, size); | ||||
} | } | ||||
return (cp != NULL); | return (cp != NULL); | ||||
} | } | ||||
/* | /* | ||||
* Return an array of integers at the given type size and signedness. | * Return an array of integers at the given type size and signedness. | ||||
*/ | */ | ||||
int | int | ||||
getenv_array(const char *name, void *pdata, int size, int *psize, | getenv_array(const char *name, void *pdata, int size, int *psize, | ||||
int type_size, bool allow_signed) | int type_size, bool allow_signed) | ||||
{ | { | ||||
struct rm_priotracker tracker; | |||||
uint8_t shift; | uint8_t shift; | ||||
int64_t value; | int64_t value; | ||||
int64_t old; | int64_t old; | ||||
char *buf; | const char *buf; | ||||
char *end; | char *end; | ||||
char *ptr; | const char *ptr; | ||||
int n; | int n; | ||||
int rc; | int rc; | ||||
if ((buf = getenv_string_buffer(name)) == NULL) | if (dynamic_kenv) { | ||||
return (0); | rm_rlock(&kenv_lock, &tracker); | ||||
buf = _getenv_dynamic(name, NULL); | |||||
} else | |||||
buf = _getenv_static(name); | |||||
mjg: This should be folded into a helper similar to what getenv_string_buffer is right now.
Then… | |||||
rc = 0; /* assume failure */ | rc = 0; /* assume failure */ | ||||
if (buf == NULL) | |||||
goto error; | |||||
/* get maximum number of elements */ | /* get maximum number of elements */ | ||||
size /= type_size; | size /= type_size; | ||||
n = 0; | n = 0; | ||||
for (ptr = buf; *ptr != 0; ) { | for (ptr = buf; *ptr != 0; ) { | ||||
value = strtoq(ptr, &end, 0); | value = strtoq(ptr, &end, 0); | ||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | for (ptr = buf; *ptr != 0; ) { | ||||
n++; | n++; | ||||
} | } | ||||
*psize = n * type_size; | *psize = n * type_size; | ||||
if (n != 0) | if (n != 0) | ||||
rc = 1; /* success */ | rc = 1; /* success */ | ||||
error: | error: | ||||
if (dynamic_kenv) | if (dynamic_kenv) | ||||
uma_zfree(kenv_zone, buf); | rm_runlock(&kenv_lock, &tracker); | ||||
return (rc); | return (rc); | ||||
} | } | ||||
/* | /* | ||||
* Return an integer value from an environment variable. | * Return an integer value from an environment variable. | ||||
*/ | */ | ||||
int | int | ||||
getenv_int(const char *name, int *data) | getenv_int(const char *name, int *data) | ||||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* Return a quad_t value from an environment variable. | * Return a quad_t value from an environment variable. | ||||
*/ | */ | ||||
int | int | ||||
getenv_quad(const char *name, quad_t *data) | getenv_quad(const char *name, quad_t *data) | ||||
{ | { | ||||
char *value, *vtp; | struct rm_priotracker tracker; | ||||
const char *value; | |||||
char suffix, *vtp; | |||||
quad_t iv; | quad_t iv; | ||||
value = getenv_string_buffer(name); | if (dynamic_kenv) { | ||||
if (value == NULL) | rm_rlock(&kenv_lock, &tracker); | ||||
return (0); | value = _getenv_dynamic(name, NULL); | ||||
} else | |||||
value = _getenv_static(name); | |||||
if (value == NULL) { | |||||
goto error; | |||||
} | |||||
iv = strtoq(value, &vtp, 0); | iv = strtoq(value, &vtp, 0); | ||||
if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { | if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { | ||||
freeenv(value); | goto error; | ||||
return (0); | |||||
} | } | ||||
switch (vtp[0]) { | suffix = vtp[0]; | ||||
if (dynamic_kenv) | |||||
rm_runlock(&kenv_lock, &tracker); | |||||
switch (suffix) { | |||||
case 't': case 'T': | case 't': case 'T': | ||||
iv *= 1024; | iv *= 1024; | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
case 'g': case 'G': | case 'g': case 'G': | ||||
iv *= 1024; | iv *= 1024; | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
case 'm': case 'M': | case 'm': case 'M': | ||||
iv *= 1024; | iv *= 1024; | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
case 'k': case 'K': | case 'k': case 'K': | ||||
iv *= 1024; | iv *= 1024; | ||||
case '\0': | case '\0': | ||||
break; | break; | ||||
default: | default: | ||||
freeenv(value); | |||||
return (0); | return (0); | ||||
} | } | ||||
freeenv(value); | |||||
*data = iv; | *data = iv; | ||||
return (1); | return (1); | ||||
error: | |||||
if (dynamic_kenv) | |||||
rm_runlock(&kenv_lock, &tracker); | |||||
return (0); | |||||
} | } | ||||
/* | /* | ||||
* Find the next entry after the one which (cp) falls within, return a | * Find the next entry after the one which (cp) falls within, return a | ||||
* pointer to its start or NULL if there are no more. | * pointer to its start or NULL if there are no more. | ||||
*/ | */ | ||||
static char * | static char * | ||||
kernenv_next(char *cp) | kernenv_next(char *cp) | ||||
▲ Show 20 Lines • Show All 67 Lines • Show Last 20 Lines |
This should be folded into a helper similar to what getenv_string_buffer is right now.
Then this and the other caller would be: