16ba9413bSMike Smith /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 38a36da99SPedro F. Giffuni * 46ba9413bSMike Smith * Copyright (c) 1998 Michael Smith 56ba9413bSMike Smith * All rights reserved. 66ba9413bSMike Smith * 76ba9413bSMike Smith * Redistribution and use in source and binary forms, with or without 86ba9413bSMike Smith * modification, are permitted provided that the following conditions 96ba9413bSMike Smith * are met: 106ba9413bSMike Smith * 1. Redistributions of source code must retain the above copyright 116ba9413bSMike Smith * notice, this list of conditions and the following disclaimer. 126ba9413bSMike Smith * 2. Redistributions in binary form must reproduce the above copyright 136ba9413bSMike Smith * notice, this list of conditions and the following disclaimer in the 146ba9413bSMike Smith * documentation and/or other materials provided with the distribution. 156ba9413bSMike Smith * 166ba9413bSMike Smith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 176ba9413bSMike Smith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 186ba9413bSMike Smith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 196ba9413bSMike Smith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 206ba9413bSMike Smith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 216ba9413bSMike Smith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 226ba9413bSMike Smith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 236ba9413bSMike Smith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 246ba9413bSMike Smith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 256ba9413bSMike Smith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 266ba9413bSMike Smith * SUCH DAMAGE. 27e4f570a2SJordan K. Hubbard */ 286ba9413bSMike Smith 296ba9413bSMike Smith /* 306ba9413bSMike Smith * The unified bootloader passes us a pointer to a preserved copy of 31d786139cSMaxime Henrion * bootstrap/kernel environment variables. We convert them to a 32d786139cSMaxime Henrion * dynamic array of strings later when the VM subsystem is up. 336ba9413bSMike Smith * 34d786139cSMaxime Henrion * We make these available through the kenv(2) syscall for userland 352be111bfSDavide Italiano * and through kern_getenv()/freeenv() kern_setenv() kern_unsetenv() testenv() for 36d786139cSMaxime Henrion * the kernel. 376ba9413bSMike Smith */ 386ba9413bSMike Smith 396ba9413bSMike Smith #include <sys/param.h> 40cf7974fdSZhenlei Huang #include <sys/eventhandler.h> 41df949e76SJose Luis Duran #include <sys/systm.h> 42df949e76SJose Luis Duran #include <sys/kenv.h> 43df949e76SJose Luis Duran #include <sys/kernel.h> 44df949e76SJose Luis Duran #include <sys/libkern.h> 45df949e76SJose Luis Duran #include <sys/limits.h> 46d786139cSMaxime Henrion #include <sys/lock.h> 47d786139cSMaxime Henrion #include <sys/malloc.h> 48d786139cSMaxime Henrion #include <sys/mutex.h> 49acd3428bSRobert Watson #include <sys/priv.h> 50df949e76SJose Luis Duran #include <sys/proc.h> 51df949e76SJose Luis Duran #include <sys/queue.h> 52df949e76SJose Luis Duran #include <sys/sysent.h> 53d786139cSMaxime Henrion #include <sys/sysproto.h> 546ba9413bSMike Smith 55aed55708SRobert Watson #include <security/mac/mac_framework.h> 56aed55708SRobert Watson 57*6bb132baSBrooks Davis #include <vm/uma.h> 58*6bb132baSBrooks Davis 5939d44f7fSKyle Evans static char *_getenv_dynamic_locked(const char *name, int *idx); 6039d44f7fSKyle Evans static char *_getenv_dynamic(const char *name, int *idx); 6139d44f7fSKyle Evans 62f3ba85ccSJason A. Harmening static char *kenv_acquire(const char *name); 63f3ba85ccSJason A. Harmening static void kenv_release(const char *buf); 64f3ba85ccSJason A. Harmening 65c711aea6SPoul-Henning Kamp static MALLOC_DEFINE(M_KENV, "kenv", "kernel environment"); 66d786139cSMaxime Henrion 67d786139cSMaxime Henrion #define KENV_SIZE 512 /* Maximum number of environment strings */ 68d786139cSMaxime Henrion 6973845fdbSSimon J. Gerraty static uma_zone_t kenv_zone; 7073845fdbSSimon J. Gerraty static int kenv_mvallen = KENV_MVALLEN; 7173845fdbSSimon J. Gerraty 7239d44f7fSKyle Evans /* pointer to the config-generated static environment */ 736ba9413bSMike Smith char *kern_envp; 7439d44f7fSKyle Evans 7539d44f7fSKyle Evans /* pointer to the md-static environment */ 7639d44f7fSKyle Evans char *md_envp; 7739d44f7fSKyle Evans static int md_env_len; 7839d44f7fSKyle Evans static int md_env_pos; 7939d44f7fSKyle Evans 80d786139cSMaxime Henrion static char *kernenv_next(char *); 816ba9413bSMike Smith 82d786139cSMaxime Henrion /* dynamic environment variables */ 83d786139cSMaxime Henrion char **kenvp; 84e3546a75SScott Long struct mtx kenv_lock; 856ba9413bSMike Smith 8621cbf0ccSJohn Baldwin /* 87873fbcd7SRobert Watson * No need to protect this with a mutex since SYSINITS are single threaded. 8821cbf0ccSJohn Baldwin */ 89c7a82b9cSKyle Evans bool dynamic_kenv; 90d786139cSMaxime Henrion 91d786139cSMaxime Henrion #define KENV_CHECK if (!dynamic_kenv) \ 92d786139cSMaxime Henrion panic("%s: called before SI_SUB_KMEM", __func__) 93d786139cSMaxime Henrion 94db0f2643SKyle Evans static int 95db0f2643SKyle Evans kenv_dump(struct thread *td, char **envp, int what, char *value, int len) 96d786139cSMaxime Henrion { 97db0f2643SKyle Evans char *buffer, *senv; 98db0f2643SKyle Evans size_t done, needed, buflen; 99db0f2643SKyle Evans int error; 100d786139cSMaxime Henrion 101d786139cSMaxime Henrion error = 0; 102db0f2643SKyle Evans buffer = NULL; 10306afcd9dSDavid Schultz done = needed = 0; 104db0f2643SKyle Evans 105db0f2643SKyle Evans MPASS(what == KENV_DUMP || what == KENV_DUMP_LOADER || 106db0f2643SKyle Evans what == KENV_DUMP_STATIC); 107db0f2643SKyle Evans 108db0f2643SKyle Evans /* 109db0f2643SKyle Evans * For non-dynamic kernel environment, we pass in either md_envp or 110db0f2643SKyle Evans * kern_envp and we must traverse with kernenv_next(). This shuffling 111db0f2643SKyle Evans * of pointers simplifies the below loop by only differing in how envp 112db0f2643SKyle Evans * is modified. 113db0f2643SKyle Evans */ 114db0f2643SKyle Evans if (what != KENV_DUMP) { 115db0f2643SKyle Evans senv = (char *)envp; 116db0f2643SKyle Evans envp = &senv; 117db0f2643SKyle Evans } 118db0f2643SKyle Evans 119db0f2643SKyle Evans buflen = len; 12073845fdbSSimon J. Gerraty if (buflen > KENV_SIZE * (KENV_MNAMELEN + kenv_mvallen + 2)) 1213f935cf3SColin Percival buflen = KENV_SIZE * (KENV_MNAMELEN + 12273845fdbSSimon J. Gerraty kenv_mvallen + 2); 123db0f2643SKyle Evans if (len > 0 && value != NULL) 1243f935cf3SColin Percival buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO); 125db0f2643SKyle Evans 126db0f2643SKyle Evans /* Only take the lock for the dynamic kenv. */ 127db0f2643SKyle Evans if (what == KENV_DUMP) 128e3546a75SScott Long mtx_lock(&kenv_lock); 129db0f2643SKyle Evans while (*envp != NULL) { 130db0f2643SKyle Evans len = strlen(*envp) + 1; 13106afcd9dSDavid Schultz needed += len; 1323f935cf3SColin Percival len = min(len, buflen - done); 13306afcd9dSDavid Schultz /* 13406afcd9dSDavid Schultz * If called with a NULL or insufficiently large 13506afcd9dSDavid Schultz * buffer, just keep computing the required size. 13606afcd9dSDavid Schultz */ 137db0f2643SKyle Evans if (value != NULL && buffer != NULL && len > 0) { 138db0f2643SKyle Evans bcopy(*envp, buffer + done, len); 139d786139cSMaxime Henrion done += len; 140d786139cSMaxime Henrion } 141db0f2643SKyle Evans 142db0f2643SKyle Evans /* Advance the pointer depending on the kenv format. */ 143db0f2643SKyle Evans if (what == KENV_DUMP) 144db0f2643SKyle Evans envp++; 145db0f2643SKyle Evans else 146db0f2643SKyle Evans senv = kernenv_next(senv); 14706afcd9dSDavid Schultz } 148db0f2643SKyle Evans if (what == KENV_DUMP) 149e3546a75SScott Long mtx_unlock(&kenv_lock); 150e3546a75SScott Long if (buffer != NULL) { 151db0f2643SKyle Evans error = copyout(buffer, value, done); 152e3546a75SScott Long free(buffer, M_TEMP); 153e3546a75SScott Long } 15406afcd9dSDavid Schultz td->td_retval[0] = ((done == needed) ? 0 : needed); 15506afcd9dSDavid Schultz return (error); 156d786139cSMaxime Henrion } 157d786139cSMaxime Henrion 158db0f2643SKyle Evans int 159db0f2643SKyle Evans sys_kenv(struct thread *td, struct kenv_args *uap) 160db0f2643SKyle Evans { 161db0f2643SKyle Evans char *name, *value; 162db0f2643SKyle Evans size_t len; 163db0f2643SKyle Evans int error; 164db0f2643SKyle Evans 165db0f2643SKyle Evans KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = false")); 166db0f2643SKyle Evans 167db0f2643SKyle Evans error = 0; 168db0f2643SKyle Evans 169acd3428bSRobert Watson switch (uap->what) { 170db0f2643SKyle Evans case KENV_DUMP: 171db0f2643SKyle Evans #ifdef MAC 172db0f2643SKyle Evans error = mac_kenv_check_dump(td->td_ucred); 173db0f2643SKyle Evans if (error) 174db0f2643SKyle Evans return (error); 175db0f2643SKyle Evans #endif 176db0f2643SKyle Evans return (kenv_dump(td, kenvp, uap->what, uap->value, uap->len)); 177db0f2643SKyle Evans case KENV_DUMP_LOADER: 178db0f2643SKyle Evans case KENV_DUMP_STATIC: 179db0f2643SKyle Evans #ifdef MAC 180db0f2643SKyle Evans error = mac_kenv_check_dump(td->td_ucred); 181db0f2643SKyle Evans if (error) 182db0f2643SKyle Evans return (error); 183db0f2643SKyle Evans #endif 184db0f2643SKyle Evans #ifdef PRESERVE_EARLY_KENV 185db0f2643SKyle Evans return (kenv_dump(td, 186db0f2643SKyle Evans uap->what == KENV_DUMP_LOADER ? (char **)md_envp : 187db0f2643SKyle Evans (char **)kern_envp, uap->what, uap->value, uap->len)); 188db0f2643SKyle Evans #else 189db0f2643SKyle Evans return (ENOENT); 190db0f2643SKyle Evans #endif 191acd3428bSRobert Watson case KENV_SET: 192acd3428bSRobert Watson error = priv_check(td, PRIV_KENV_SET); 193d786139cSMaxime Henrion if (error) 194d786139cSMaxime Henrion return (error); 195acd3428bSRobert Watson break; 196acd3428bSRobert Watson 197acd3428bSRobert Watson case KENV_UNSET: 198acd3428bSRobert Watson error = priv_check(td, PRIV_KENV_UNSET); 199acd3428bSRobert Watson if (error) 200acd3428bSRobert Watson return (error); 201acd3428bSRobert Watson break; 202d786139cSMaxime Henrion } 203d786139cSMaxime Henrion 2042f0ac259SJaakko Heinonen name = malloc(KENV_MNAMELEN + 1, M_TEMP, M_WAITOK); 205d786139cSMaxime Henrion 2062f0ac259SJaakko Heinonen error = copyinstr(uap->name, name, KENV_MNAMELEN + 1, NULL); 207d786139cSMaxime Henrion if (error) 208d786139cSMaxime Henrion goto done; 209d786139cSMaxime Henrion 210d1e405c5SAlfred Perlstein switch (uap->what) { 211d786139cSMaxime Henrion case KENV_GET: 212e686e5aeSRobert Watson #ifdef MAC 21330d239bcSRobert Watson error = mac_kenv_check_get(td->td_ucred, name); 214e686e5aeSRobert Watson if (error) 215e686e5aeSRobert Watson goto done; 216e686e5aeSRobert Watson #endif 2172be111bfSDavide Italiano value = kern_getenv(name); 218d786139cSMaxime Henrion if (value == NULL) { 219d786139cSMaxime Henrion error = ENOENT; 220d786139cSMaxime Henrion goto done; 221d786139cSMaxime Henrion } 222d786139cSMaxime Henrion len = strlen(value) + 1; 223d1e405c5SAlfred Perlstein if (len > uap->len) 224d1e405c5SAlfred Perlstein len = uap->len; 225d1e405c5SAlfred Perlstein error = copyout(value, uap->value, len); 226d786139cSMaxime Henrion freeenv(value); 227d786139cSMaxime Henrion if (error) 228d786139cSMaxime Henrion goto done; 229d786139cSMaxime Henrion td->td_retval[0] = len; 230d786139cSMaxime Henrion break; 231d786139cSMaxime Henrion case KENV_SET: 232d1e405c5SAlfred Perlstein len = uap->len; 233d786139cSMaxime Henrion if (len < 1) { 234d786139cSMaxime Henrion error = EINVAL; 235d786139cSMaxime Henrion goto done; 236d786139cSMaxime Henrion } 23773845fdbSSimon J. Gerraty if (len > kenv_mvallen + 1) 23873845fdbSSimon J. Gerraty len = kenv_mvallen + 1; 239a163d034SWarner Losh value = malloc(len, M_TEMP, M_WAITOK); 240d1e405c5SAlfred Perlstein error = copyinstr(uap->value, value, len, NULL); 241d786139cSMaxime Henrion if (error) { 242d786139cSMaxime Henrion free(value, M_TEMP); 243d786139cSMaxime Henrion goto done; 244d786139cSMaxime Henrion } 245e686e5aeSRobert Watson #ifdef MAC 24630d239bcSRobert Watson error = mac_kenv_check_set(td->td_ucred, name, value); 247e686e5aeSRobert Watson if (error == 0) 248e686e5aeSRobert Watson #endif 2492be111bfSDavide Italiano kern_setenv(name, value); 250d786139cSMaxime Henrion free(value, M_TEMP); 251d786139cSMaxime Henrion break; 252d786139cSMaxime Henrion case KENV_UNSET: 253e686e5aeSRobert Watson #ifdef MAC 25430d239bcSRobert Watson error = mac_kenv_check_unset(td->td_ucred, name); 255e686e5aeSRobert Watson if (error) 256e686e5aeSRobert Watson goto done; 257e686e5aeSRobert Watson #endif 2582be111bfSDavide Italiano error = kern_unsetenv(name); 259d786139cSMaxime Henrion if (error) 260d786139cSMaxime Henrion error = ENOENT; 261d786139cSMaxime Henrion break; 262d786139cSMaxime Henrion default: 263d786139cSMaxime Henrion error = EINVAL; 264d786139cSMaxime Henrion break; 265d786139cSMaxime Henrion } 266d786139cSMaxime Henrion done: 267d786139cSMaxime Henrion free(name, M_TEMP); 268d786139cSMaxime Henrion return (error); 269d786139cSMaxime Henrion } 270d786139cSMaxime Henrion 27169dcb7e7SIan Lepore /* 27269dcb7e7SIan Lepore * Populate the initial kernel environment. 27369dcb7e7SIan Lepore * 27469dcb7e7SIan Lepore * This is called very early in MD startup, either to provide a copy of the 27569dcb7e7SIan Lepore * environment obtained from a boot loader, or to provide an empty buffer into 27669dcb7e7SIan Lepore * which MD code can store an initial environment using kern_setenv() calls. 27769dcb7e7SIan Lepore * 27839d44f7fSKyle Evans * kern_envp is set to the static_env generated by config(8). This implements 27939d44f7fSKyle Evans * the env keyword described in config(5). 28069dcb7e7SIan Lepore * 28169dcb7e7SIan Lepore * If len is non-zero, the caller is providing an empty buffer. The caller will 28269dcb7e7SIan Lepore * subsequently use kern_setenv() to add up to len bytes of initial environment 28369dcb7e7SIan Lepore * before the dynamic environment is available. 28469dcb7e7SIan Lepore * 28569dcb7e7SIan Lepore * If len is zero, the caller is providing a pre-loaded buffer containing 28669dcb7e7SIan Lepore * environment strings. Additional strings cannot be added until the dynamic 28769dcb7e7SIan Lepore * environment is available. The memory pointed to must remain stable at least 28839d44f7fSKyle Evans * until sysinit runs init_dynamic_kenv() and preferably until after SI_SUB_KMEM 28939d44f7fSKyle Evans * is finished so that subr_hints routines may continue to use it until the 29039d44f7fSKyle Evans * environments have been fully merged at the end of the pass. If no initial 29139d44f7fSKyle Evans * environment is available from the boot loader, passing a NULL pointer allows 29239d44f7fSKyle Evans * the static_env to be installed if it is configured. In this case, any call 29339d44f7fSKyle Evans * to kern_setenv() prior to the setup of the dynamic environment will result in 29439d44f7fSKyle Evans * a panic. 29569dcb7e7SIan Lepore */ 296eae8e367SWarner Losh void 297eae8e367SWarner Losh init_static_kenv(char *buf, size_t len) 298eae8e367SWarner Losh { 29985143dd1SIan Lepore 30075beb4d4SKyle Evans KASSERT(!dynamic_kenv, ("kenv: dynamic_kenv already initialized")); 3015163b1a7SKyle Evans /* 3025163b1a7SKyle Evans * Suitably sized means it must be able to hold at least one empty 3035163b1a7SKyle Evans * variable, otherwise things go belly up if a kern_getenv call is 3045163b1a7SKyle Evans * made without a prior call to kern_setenv as we have a malformed 3055163b1a7SKyle Evans * environment. 3065163b1a7SKyle Evans */ 3075163b1a7SKyle Evans KASSERT(len == 0 || len >= 2, 3085163b1a7SKyle Evans ("kenv: static env must be initialized or suitably sized")); 3095163b1a7SKyle Evans KASSERT(len == 0 || (*buf == '\0' && *(buf + 1) == '\0'), 310436c4687SKyle Evans ("kenv: sized buffer must be initially empty")); 3116fd2dcd4SBruce Evans 3126fd2dcd4SBruce Evans /* 3136fd2dcd4SBruce Evans * We may be called twice, with the second call needed to relocate 3146fd2dcd4SBruce Evans * md_envp after enabling paging. md_envp is then garbage if it is 3156fd2dcd4SBruce Evans * not null and the relocation will move it. Discard it so as to 3166fd2dcd4SBruce Evans * not crash using its old value in our first call to kern_getenv(). 3176fd2dcd4SBruce Evans * 3186fd2dcd4SBruce Evans * The second call gives the same environment as the first except 3196fd2dcd4SBruce Evans * in silly configurations where the static env disables itself. 3206fd2dcd4SBruce Evans * 3216fd2dcd4SBruce Evans * Other env calls don't handle possibly-garbage pointers, so must 3226fd2dcd4SBruce Evans * not be made between enabling paging and calling here. 3236fd2dcd4SBruce Evans */ 3246fd2dcd4SBruce Evans md_envp = NULL; 3256fd2dcd4SBruce Evans md_env_len = 0; 3266fd2dcd4SBruce Evans md_env_pos = 0; 3276fd2dcd4SBruce Evans 32839d44f7fSKyle Evans /* 32944314c35SKyle Evans * Give the static environment a chance to disable the loader(8) 33044314c35SKyle Evans * environment first. This is done with loader_env.disabled=1. 33144314c35SKyle Evans * 33239d44f7fSKyle Evans * static_env and static_hints may both be disabled, but in slightly 33339d44f7fSKyle Evans * different ways. For static_env, we just don't setup kern_envp and 33439d44f7fSKyle Evans * it's as if a static env wasn't even provided. For static_hints, 33539d44f7fSKyle Evans * we effectively zero out the buffer to stop the rest of the kernel 33639d44f7fSKyle Evans * from being able to use it. 33739d44f7fSKyle Evans * 33839d44f7fSKyle Evans * We're intentionally setting this up so that static_hints.disabled may 33939d44f7fSKyle Evans * be specified in either the MD env or the static env. This keeps us 34039d44f7fSKyle Evans * consistent in our new world view. 34144314c35SKyle Evans * 34244314c35SKyle Evans * As a warning, the static environment may not be disabled in any way 34344314c35SKyle Evans * if the static environment has disabled the loader environment. 34439d44f7fSKyle Evans */ 34539d44f7fSKyle Evans kern_envp = static_env; 346624a7e1fSMitchell Horne if (!getenv_is_true("loader_env.disabled")) { 34744314c35SKyle Evans md_envp = buf; 34844314c35SKyle Evans md_env_len = len; 34944314c35SKyle Evans md_env_pos = 0; 35044314c35SKyle Evans 351624a7e1fSMitchell Horne if (getenv_is_true("static_env.disabled")) { 3526fd2dcd4SBruce Evans kern_envp[0] = '\0'; 3536fd2dcd4SBruce Evans kern_envp[1] = '\0'; 3546fd2dcd4SBruce Evans } 35544314c35SKyle Evans } 356624a7e1fSMitchell Horne if (getenv_is_true("static_hints.disabled")) { 3576fd2dcd4SBruce Evans static_hints[0] = '\0'; 3586fd2dcd4SBruce Evans static_hints[1] = '\0'; 3596fd2dcd4SBruce Evans } 360e2868734SKyle Evans } 3618ef58863SKyle Evans 362c32bd976SColin Percival /* Maximum suffix number appended for duplicate environment variable names. */ 363c32bd976SColin Percival #define MAXSUFFIX 9999 364c32bd976SColin Percival #define SUFFIXLEN strlen("_" __XSTRING(MAXSUFFIX)) 365c32bd976SColin Percival 366c32bd976SColin Percival static void 367c32bd976SColin Percival getfreesuffix(char *cp, size_t *n) 368c32bd976SColin Percival { 369c32bd976SColin Percival size_t len = strlen(cp); 370c32bd976SColin Percival char * ncp; 371c32bd976SColin Percival 372c32bd976SColin Percival ncp = malloc(len + SUFFIXLEN + 1, M_KENV, M_WAITOK); 373c32bd976SColin Percival memcpy(ncp, cp, len); 374c32bd976SColin Percival for (*n = 1; *n <= MAXSUFFIX; (*n)++) { 375c32bd976SColin Percival sprintf(&ncp[len], "_%zu", *n); 376c32bd976SColin Percival if (!_getenv_dynamic_locked(ncp, NULL)) 377c32bd976SColin Percival break; 378c32bd976SColin Percival } 379c32bd976SColin Percival free(ncp, M_KENV); 380c32bd976SColin Percival if (*n > MAXSUFFIX) 381c32bd976SColin Percival panic("Too many duplicate kernel environment values: %s", cp); 382c32bd976SColin Percival } 383c32bd976SColin Percival 38439d44f7fSKyle Evans static void 38539d44f7fSKyle Evans init_dynamic_kenv_from(char *init_env, int *curpos) 38639d44f7fSKyle Evans { 38739d44f7fSKyle Evans char *cp, *cpnext, *eqpos, *found; 388c32bd976SColin Percival size_t len, n; 38939d44f7fSKyle Evans int i; 39039d44f7fSKyle Evans 39139d44f7fSKyle Evans if (init_env && *init_env != '\0') { 39239d44f7fSKyle Evans found = NULL; 39339d44f7fSKyle Evans i = *curpos; 39439d44f7fSKyle Evans for (cp = init_env; cp != NULL; cp = cpnext) { 39539d44f7fSKyle Evans cpnext = kernenv_next(cp); 39639d44f7fSKyle Evans len = strlen(cp) + 1; 397c32bd976SColin Percival if (i > KENV_SIZE) { 398c32bd976SColin Percival printf( 399c32bd976SColin Percival "WARNING: too many kenv strings, ignoring %s\n", 400c32bd976SColin Percival cp); 401c32bd976SColin Percival goto sanitize; 402c32bd976SColin Percival } 40373845fdbSSimon J. Gerraty if (len > KENV_MNAMELEN + 1 + kenv_mvallen + 1) { 40439d44f7fSKyle Evans printf( 40539d44f7fSKyle Evans "WARNING: too long kenv string, ignoring %s\n", 40639d44f7fSKyle Evans cp); 40739d44f7fSKyle Evans goto sanitize; 40839d44f7fSKyle Evans } 40939d44f7fSKyle Evans eqpos = strchr(cp, '='); 41039d44f7fSKyle Evans if (eqpos == NULL) { 41139d44f7fSKyle Evans printf( 41239d44f7fSKyle Evans "WARNING: malformed static env value, ignoring %s\n", 41339d44f7fSKyle Evans cp); 41439d44f7fSKyle Evans goto sanitize; 41539d44f7fSKyle Evans } 41639d44f7fSKyle Evans *eqpos = 0; 41739d44f7fSKyle Evans /* 418c32bd976SColin Percival * Handle duplicates in the environment as we go; we 419c32bd976SColin Percival * add the duplicated assignments with _N suffixes. 420c32bd976SColin Percival * This ensures that (a) if a variable is set in the 421c32bd976SColin Percival * static environment and in the "loader" environment 422c32bd976SColin Percival * provided by MD code, the value from the loader will 423c32bd976SColin Percival * have the expected variable name and the value from 424c32bd976SColin Percival * the static environment will have the suffix; and (b) 425c32bd976SColin Percival * if the "loader" environment has the same variable 426c32bd976SColin Percival * set multiple times (as is possible with values being 427c32bd976SColin Percival * passed via the kernel "command line") the extra 428c32bd976SColin Percival * values are visible to code which knows where to look 429c32bd976SColin Percival * for them. 43039d44f7fSKyle Evans */ 43139d44f7fSKyle Evans found = _getenv_dynamic_locked(cp, NULL); 432c32bd976SColin Percival if (found != NULL) { 433c32bd976SColin Percival getfreesuffix(cp, &n); 434c32bd976SColin Percival kenvp[i] = malloc(len + SUFFIXLEN, 435c32bd976SColin Percival M_KENV, M_WAITOK); 436c32bd976SColin Percival sprintf(kenvp[i++], "%s_%zu=%s", cp, n, 437c32bd976SColin Percival &eqpos[1]); 438c32bd976SColin Percival } else { 43939d44f7fSKyle Evans kenvp[i] = malloc(len, M_KENV, M_WAITOK); 440c32bd976SColin Percival *eqpos = '='; 44139d44f7fSKyle Evans strcpy(kenvp[i++], cp); 442c32bd976SColin Percival } 44339d44f7fSKyle Evans sanitize: 4447a129c97SKyle Evans #ifdef PRESERVE_EARLY_KENV 4457a129c97SKyle Evans continue; 4467a129c97SKyle Evans #else 44739d44f7fSKyle Evans explicit_bzero(cp, len - 1); 4487a129c97SKyle Evans #endif 44939d44f7fSKyle Evans } 45039d44f7fSKyle Evans *curpos = i; 451eae8e367SWarner Losh } 45269dcb7e7SIan Lepore } 453eae8e367SWarner Losh 454d786139cSMaxime Henrion /* 455d786139cSMaxime Henrion * Setup the dynamic kernel environment. 456d786139cSMaxime Henrion */ 457d786139cSMaxime Henrion static void 458d786139cSMaxime Henrion init_dynamic_kenv(void *data __unused) 459d786139cSMaxime Henrion { 46039d44f7fSKyle Evans int dynamic_envpos; 46173845fdbSSimon J. Gerraty int size; 46273845fdbSSimon J. Gerraty 46373845fdbSSimon J. Gerraty TUNABLE_INT_FETCH("kenv_mvallen", &kenv_mvallen); 46473845fdbSSimon J. Gerraty size = KENV_MNAMELEN + 1 + kenv_mvallen + 1; 46573845fdbSSimon J. Gerraty 46673845fdbSSimon J. Gerraty kenv_zone = uma_zcreate("kenv", size, NULL, NULL, NULL, NULL, 46773845fdbSSimon J. Gerraty UMA_ALIGN_PTR, 0); 468d786139cSMaxime Henrion 4693904769bSAlexander Leidinger kenvp = malloc((KENV_SIZE + 1) * sizeof(char *), M_KENV, 4703904769bSAlexander Leidinger M_WAITOK | M_ZERO); 47139d44f7fSKyle Evans 47239d44f7fSKyle Evans dynamic_envpos = 0; 47339d44f7fSKyle Evans init_dynamic_kenv_from(md_envp, &dynamic_envpos); 47439d44f7fSKyle Evans init_dynamic_kenv_from(kern_envp, &dynamic_envpos); 47539d44f7fSKyle Evans kenvp[dynamic_envpos] = NULL; 476d786139cSMaxime Henrion 477e3546a75SScott Long mtx_init(&kenv_lock, "kernel environment", NULL, MTX_DEF); 478c7a82b9cSKyle Evans dynamic_kenv = true; 479d786139cSMaxime Henrion } 480cae22dd9SKyle Evans SYSINIT(kenv, SI_SUB_KMEM + 1, SI_ORDER_FIRST, init_dynamic_kenv, NULL); 481d786139cSMaxime Henrion 482d786139cSMaxime Henrion void 483d786139cSMaxime Henrion freeenv(char *env) 484d786139cSMaxime Henrion { 485d786139cSMaxime Henrion 4862735a91dSXin LI if (dynamic_kenv && env != NULL) { 48797603f1dSIan Lepore explicit_bzero(env, strlen(env)); 48873845fdbSSimon J. Gerraty uma_zfree(kenv_zone, env); 489d786139cSMaxime Henrion } 490b9f6af45SColin Percival } 491d786139cSMaxime Henrion 492d786139cSMaxime Henrion /* 493d786139cSMaxime Henrion * Internal functions for string lookup. 494d786139cSMaxime Henrion */ 495d786139cSMaxime Henrion static char * 49639d44f7fSKyle Evans _getenv_dynamic_locked(const char *name, int *idx) 497d786139cSMaxime Henrion { 498d786139cSMaxime Henrion char *cp; 499d786139cSMaxime Henrion int len, i; 500d786139cSMaxime Henrion 501d786139cSMaxime Henrion len = strlen(name); 502d786139cSMaxime Henrion for (cp = kenvp[0], i = 0; cp != NULL; cp = kenvp[++i]) { 5032b7182c6SDavid Xu if ((strncmp(cp, name, len) == 0) && 5042b7182c6SDavid Xu (cp[len] == '=')) { 505d786139cSMaxime Henrion if (idx != NULL) 506d786139cSMaxime Henrion *idx = i; 507d786139cSMaxime Henrion return (cp + len + 1); 508d786139cSMaxime Henrion } 509d786139cSMaxime Henrion } 510d786139cSMaxime Henrion return (NULL); 511d786139cSMaxime Henrion } 512d786139cSMaxime Henrion 513d786139cSMaxime Henrion static char * 51439d44f7fSKyle Evans _getenv_dynamic(const char *name, int *idx) 51539d44f7fSKyle Evans { 51639d44f7fSKyle Evans 51739d44f7fSKyle Evans mtx_assert(&kenv_lock, MA_OWNED); 51839d44f7fSKyle Evans return (_getenv_dynamic_locked(name, idx)); 51939d44f7fSKyle Evans } 52039d44f7fSKyle Evans 52139d44f7fSKyle Evans static char * 52239d44f7fSKyle Evans _getenv_static_from(char *chkenv, const char *name) 5236ba9413bSMike Smith { 5246ba9413bSMike Smith char *cp, *ep; 5256ba9413bSMike Smith int len; 5266ba9413bSMike Smith 52739d44f7fSKyle Evans for (cp = chkenv; cp != NULL; cp = kernenv_next(cp)) { 5286ba9413bSMike Smith for (ep = cp; (*ep != '=') && (*ep != 0); ep++) 5296ba9413bSMike Smith ; 5302c900f64SBruce Evans if (*ep != '=') 5312c900f64SBruce Evans continue; 5326ba9413bSMike Smith len = ep - cp; 5336ba9413bSMike Smith ep++; 5342c900f64SBruce Evans if (!strncmp(name, cp, len) && name[len] == 0) 5356ba9413bSMike Smith return (ep); 5366ba9413bSMike Smith } 5376ba9413bSMike Smith return (NULL); 5386ba9413bSMike Smith } 5396ba9413bSMike Smith 54039d44f7fSKyle Evans static char * 54139d44f7fSKyle Evans _getenv_static(const char *name) 54239d44f7fSKyle Evans { 54339d44f7fSKyle Evans char *val; 54439d44f7fSKyle Evans 54539d44f7fSKyle Evans val = _getenv_static_from(md_envp, name); 54639d44f7fSKyle Evans if (val != NULL) 54739d44f7fSKyle Evans return (val); 54839d44f7fSKyle Evans val = _getenv_static_from(kern_envp, name); 54939d44f7fSKyle Evans if (val != NULL) 55039d44f7fSKyle Evans return (val); 55139d44f7fSKyle Evans return (NULL); 55239d44f7fSKyle Evans } 55339d44f7fSKyle Evans 5542084f96cSMike Smith /* 555d786139cSMaxime Henrion * Look up an environment variable by name. 556d786139cSMaxime Henrion * Return a pointer to the string if found. 557d786139cSMaxime Henrion * The pointer has to be freed with freeenv() 558d786139cSMaxime Henrion * after use. 559d786139cSMaxime Henrion */ 560d786139cSMaxime Henrion char * 5612be111bfSDavide Italiano kern_getenv(const char *name) 562d786139cSMaxime Henrion { 563f3ba85ccSJason A. Harmening char *cp, *ret; 564f3ba85ccSJason A. Harmening int len; 565d786139cSMaxime Henrion 566d786139cSMaxime Henrion if (dynamic_kenv) { 567f3ba85ccSJason A. Harmening len = KENV_MNAMELEN + 1 + kenv_mvallen + 1; 568f3ba85ccSJason A. Harmening ret = uma_zalloc(kenv_zone, M_WAITOK | M_ZERO); 569f3ba85ccSJason A. Harmening mtx_lock(&kenv_lock); 570f3ba85ccSJason A. Harmening cp = _getenv_dynamic(name, NULL); 571f3ba85ccSJason A. Harmening if (cp != NULL) 572f3ba85ccSJason A. Harmening strlcpy(ret, cp, len); 573f3ba85ccSJason A. Harmening mtx_unlock(&kenv_lock); 574f3ba85ccSJason A. Harmening if (cp == NULL) { 575f3ba85ccSJason A. Harmening uma_zfree(kenv_zone, ret); 576f3ba85ccSJason A. Harmening ret = NULL; 577b48a4280SMaxime Henrion } 578d786139cSMaxime Henrion } else 579d786139cSMaxime Henrion ret = _getenv_static(name); 580f3ba85ccSJason A. Harmening 581d786139cSMaxime Henrion return (ret); 582d786139cSMaxime Henrion } 583d786139cSMaxime Henrion 584d786139cSMaxime Henrion /* 585d786139cSMaxime Henrion * Test if an environment variable is defined. 586d786139cSMaxime Henrion */ 587d786139cSMaxime Henrion int 588d786139cSMaxime Henrion testenv(const char *name) 589d786139cSMaxime Henrion { 590d786139cSMaxime Henrion char *cp; 591d786139cSMaxime Henrion 592f3ba85ccSJason A. Harmening cp = kenv_acquire(name); 593f3ba85ccSJason A. Harmening kenv_release(cp); 594f3ba85ccSJason A. Harmening 595d786139cSMaxime Henrion if (cp != NULL) 596d786139cSMaxime Henrion return (1); 597d786139cSMaxime Henrion return (0); 598d786139cSMaxime Henrion } 599d786139cSMaxime Henrion 60039d44f7fSKyle Evans /* 60139d44f7fSKyle Evans * Set an environment variable in the MD-static environment. This cannot 60239d44f7fSKyle Evans * feasibly be done on config(8)-generated static environments as they don't 60339d44f7fSKyle Evans * generally include space for extra variables. 60439d44f7fSKyle Evans */ 605eae8e367SWarner Losh static int 606eae8e367SWarner Losh setenv_static(const char *name, const char *value) 607eae8e367SWarner Losh { 608eae8e367SWarner Losh int len; 609eae8e367SWarner Losh 61039d44f7fSKyle Evans if (md_env_pos >= md_env_len) 611eae8e367SWarner Losh return (-1); 612eae8e367SWarner Losh 613eae8e367SWarner Losh /* Check space for x=y and two nuls */ 614eae8e367SWarner Losh len = strlen(name) + strlen(value); 61539d44f7fSKyle Evans if (len + 3 < md_env_len - md_env_pos) { 61639d44f7fSKyle Evans len = sprintf(&md_envp[md_env_pos], "%s=%s", name, value); 61739d44f7fSKyle Evans md_env_pos += len+1; 61839d44f7fSKyle Evans md_envp[md_env_pos] = '\0'; 619eae8e367SWarner Losh return (0); 620eae8e367SWarner Losh } else 621eae8e367SWarner Losh return (-1); 622eae8e367SWarner Losh 623eae8e367SWarner Losh } 624eae8e367SWarner Losh 625d786139cSMaxime Henrion /* 626d786139cSMaxime Henrion * Set an environment variable by name. 627d786139cSMaxime Henrion */ 628b48a4280SMaxime Henrion int 6292be111bfSDavide Italiano kern_setenv(const char *name, const char *value) 630d786139cSMaxime Henrion { 631b48a4280SMaxime Henrion char *buf, *cp, *oldenv; 632b48a4280SMaxime Henrion int namelen, vallen, i; 633d786139cSMaxime Henrion 634c7a82b9cSKyle Evans if (!dynamic_kenv && md_env_len > 0) 635eae8e367SWarner Losh return (setenv_static(name, value)); 636eae8e367SWarner Losh 637d786139cSMaxime Henrion KENV_CHECK; 638d786139cSMaxime Henrion 639b48a4280SMaxime Henrion namelen = strlen(name) + 1; 6402f0ac259SJaakko Heinonen if (namelen > KENV_MNAMELEN + 1) 641b48a4280SMaxime Henrion return (-1); 642b48a4280SMaxime Henrion vallen = strlen(value) + 1; 64373845fdbSSimon J. Gerraty if (vallen > kenv_mvallen + 1) 644b48a4280SMaxime Henrion return (-1); 645a163d034SWarner Losh buf = malloc(namelen + vallen, M_KENV, M_WAITOK); 646d786139cSMaxime Henrion sprintf(buf, "%s=%s", name, value); 647d786139cSMaxime Henrion 648e3546a75SScott Long mtx_lock(&kenv_lock); 649d786139cSMaxime Henrion cp = _getenv_dynamic(name, &i); 650d786139cSMaxime Henrion if (cp != NULL) { 651b48a4280SMaxime Henrion oldenv = kenvp[i]; 652d786139cSMaxime Henrion kenvp[i] = buf; 653e3546a75SScott Long mtx_unlock(&kenv_lock); 654b48a4280SMaxime Henrion free(oldenv, M_KENV); 655d786139cSMaxime Henrion } else { 656d786139cSMaxime Henrion /* We add the option if it wasn't found */ 657d786139cSMaxime Henrion for (i = 0; (cp = kenvp[i]) != NULL; i++) 658d786139cSMaxime Henrion ; 6593904769bSAlexander Leidinger 6603904769bSAlexander Leidinger /* Bounds checking */ 6613904769bSAlexander Leidinger if (i < 0 || i >= KENV_SIZE) { 6623904769bSAlexander Leidinger free(buf, M_KENV); 663e3546a75SScott Long mtx_unlock(&kenv_lock); 6643904769bSAlexander Leidinger return (-1); 6653904769bSAlexander Leidinger } 6663904769bSAlexander Leidinger 667d786139cSMaxime Henrion kenvp[i] = buf; 668d786139cSMaxime Henrion kenvp[i + 1] = NULL; 669e3546a75SScott Long mtx_unlock(&kenv_lock); 670d786139cSMaxime Henrion } 671cf7974fdSZhenlei Huang EVENTHANDLER_INVOKE(setenv, name); 672b48a4280SMaxime Henrion return (0); 673b48a4280SMaxime Henrion } 674d786139cSMaxime Henrion 675d786139cSMaxime Henrion /* 676d786139cSMaxime Henrion * Unset an environment variable string. 677d786139cSMaxime Henrion */ 678d786139cSMaxime Henrion int 6792be111bfSDavide Italiano kern_unsetenv(const char *name) 680d786139cSMaxime Henrion { 681b48a4280SMaxime Henrion char *cp, *oldenv; 682d786139cSMaxime Henrion int i, j; 683d786139cSMaxime Henrion 684d786139cSMaxime Henrion KENV_CHECK; 685d786139cSMaxime Henrion 686e3546a75SScott Long mtx_lock(&kenv_lock); 687d786139cSMaxime Henrion cp = _getenv_dynamic(name, &i); 688d786139cSMaxime Henrion if (cp != NULL) { 689b48a4280SMaxime Henrion oldenv = kenvp[i]; 690d786139cSMaxime Henrion for (j = i + 1; kenvp[j] != NULL; j++) 691d786139cSMaxime Henrion kenvp[i++] = kenvp[j]; 692d786139cSMaxime Henrion kenvp[i] = NULL; 693e3546a75SScott Long mtx_unlock(&kenv_lock); 6944a711b8dSJohn Baldwin zfree(oldenv, M_KENV); 695cf7974fdSZhenlei Huang EVENTHANDLER_INVOKE(unsetenv, name); 696d786139cSMaxime Henrion return (0); 697d786139cSMaxime Henrion } 698e3546a75SScott Long mtx_unlock(&kenv_lock); 699d786139cSMaxime Henrion return (-1); 700d786139cSMaxime Henrion } 701d786139cSMaxime Henrion 702d786139cSMaxime Henrion /* 703f3ba85ccSJason A. Harmening * Return the internal kenv buffer for the variable name, if it exists. 704f3ba85ccSJason A. Harmening * If the dynamic kenv is initialized and the name is present, return 705f3ba85ccSJason A. Harmening * with kenv_lock held. 70673845fdbSSimon J. Gerraty */ 70773845fdbSSimon J. Gerraty static char * 708f3ba85ccSJason A. Harmening kenv_acquire(const char *name) 70973845fdbSSimon J. Gerraty { 710f3ba85ccSJason A. Harmening char *value; 71173845fdbSSimon J. Gerraty 71273845fdbSSimon J. Gerraty if (dynamic_kenv) { 71373845fdbSSimon J. Gerraty mtx_lock(&kenv_lock); 714f3ba85ccSJason A. Harmening value = _getenv_dynamic(name, NULL); 715f3ba85ccSJason A. Harmening if (value == NULL) 71673845fdbSSimon J. Gerraty mtx_unlock(&kenv_lock); 717f3ba85ccSJason A. Harmening return (value); 71873845fdbSSimon J. Gerraty } else 719f3ba85ccSJason A. Harmening return (_getenv_static(name)); 720f3ba85ccSJason A. Harmening } 72173845fdbSSimon J. Gerraty 722f3ba85ccSJason A. Harmening /* 723f3ba85ccSJason A. Harmening * Undo a previous kenv_acquire() operation 724f3ba85ccSJason A. Harmening */ 725f3ba85ccSJason A. Harmening static void 726f3ba85ccSJason A. Harmening kenv_release(const char *buf) 727f3ba85ccSJason A. Harmening { 728f3ba85ccSJason A. Harmening if ((buf != NULL) && dynamic_kenv) 729f3ba85ccSJason A. Harmening mtx_unlock(&kenv_lock); 73073845fdbSSimon J. Gerraty } 73173845fdbSSimon J. Gerraty 73273845fdbSSimon J. Gerraty /* 73321cbf0ccSJohn Baldwin * Return a string value from an environment variable. 73421cbf0ccSJohn Baldwin */ 73521cbf0ccSJohn Baldwin int 73621cbf0ccSJohn Baldwin getenv_string(const char *name, char *data, int size) 73721cbf0ccSJohn Baldwin { 738915f2b7cSAlexander Motin char *cp; 73921cbf0ccSJohn Baldwin 740f3ba85ccSJason A. Harmening cp = kenv_acquire(name); 741f3ba85ccSJason A. Harmening 742915f2b7cSAlexander Motin if (cp != NULL) 743915f2b7cSAlexander Motin strlcpy(data, cp, size); 744f3ba85ccSJason A. Harmening 745f3ba85ccSJason A. Harmening kenv_release(cp); 746f3ba85ccSJason A. Harmening 747915f2b7cSAlexander Motin return (cp != NULL); 74821cbf0ccSJohn Baldwin } 74921cbf0ccSJohn Baldwin 75021cbf0ccSJohn Baldwin /* 751ce70c572SHans Petter Selasky * Return an array of integers at the given type size and signedness. 752ce70c572SHans Petter Selasky */ 753ce70c572SHans Petter Selasky int 754ce70c572SHans Petter Selasky getenv_array(const char *name, void *pdata, int size, int *psize, 755ce70c572SHans Petter Selasky int type_size, bool allow_signed) 756ce70c572SHans Petter Selasky { 757ce70c572SHans Petter Selasky uint8_t shift; 758ce70c572SHans Petter Selasky int64_t value; 759ce70c572SHans Petter Selasky int64_t old; 760f3ba85ccSJason A. Harmening const char *buf; 761ce70c572SHans Petter Selasky char *end; 762f3ba85ccSJason A. Harmening const char *ptr; 763ce70c572SHans Petter Selasky int n; 76473845fdbSSimon J. Gerraty int rc; 765ce70c572SHans Petter Selasky 76673845fdbSSimon J. Gerraty rc = 0; /* assume failure */ 767f3ba85ccSJason A. Harmening 768f3ba85ccSJason A. Harmening buf = kenv_acquire(name); 769f3ba85ccSJason A. Harmening if (buf == NULL) 770f3ba85ccSJason A. Harmening goto error; 771f3ba85ccSJason A. Harmening 772ce70c572SHans Petter Selasky /* get maximum number of elements */ 773ce70c572SHans Petter Selasky size /= type_size; 774ce70c572SHans Petter Selasky 775ce70c572SHans Petter Selasky n = 0; 776ce70c572SHans Petter Selasky 777ce70c572SHans Petter Selasky for (ptr = buf; *ptr != 0; ) { 778ce70c572SHans Petter Selasky value = strtoq(ptr, &end, 0); 779ce70c572SHans Petter Selasky 780ce70c572SHans Petter Selasky /* check if signed numbers are allowed */ 781ce70c572SHans Petter Selasky if (value < 0 && !allow_signed) 782ce70c572SHans Petter Selasky goto error; 783ce70c572SHans Petter Selasky 784ce70c572SHans Petter Selasky /* check for invalid value */ 785ce70c572SHans Petter Selasky if (ptr == end) 786ce70c572SHans Petter Selasky goto error; 787ce70c572SHans Petter Selasky 788ce70c572SHans Petter Selasky /* check for valid suffix */ 789ce70c572SHans Petter Selasky switch (*end) { 790ce70c572SHans Petter Selasky case 't': 791ce70c572SHans Petter Selasky case 'T': 792ce70c572SHans Petter Selasky shift = 40; 793ce70c572SHans Petter Selasky end++; 794ce70c572SHans Petter Selasky break; 795ce70c572SHans Petter Selasky case 'g': 796ce70c572SHans Petter Selasky case 'G': 797ce70c572SHans Petter Selasky shift = 30; 798ce70c572SHans Petter Selasky end++; 799ce70c572SHans Petter Selasky break; 800ce70c572SHans Petter Selasky case 'm': 801ce70c572SHans Petter Selasky case 'M': 802ce70c572SHans Petter Selasky shift = 20; 803ce70c572SHans Petter Selasky end++; 804ce70c572SHans Petter Selasky break; 805ce70c572SHans Petter Selasky case 'k': 806ce70c572SHans Petter Selasky case 'K': 807ce70c572SHans Petter Selasky shift = 10; 808ce70c572SHans Petter Selasky end++; 809ce70c572SHans Petter Selasky break; 810ce70c572SHans Petter Selasky case ' ': 811ce70c572SHans Petter Selasky case '\t': 812ce70c572SHans Petter Selasky case ',': 813ce70c572SHans Petter Selasky case 0: 814ce70c572SHans Petter Selasky shift = 0; 815ce70c572SHans Petter Selasky break; 816ce70c572SHans Petter Selasky default: 817ce70c572SHans Petter Selasky /* garbage after numeric value */ 818ce70c572SHans Petter Selasky goto error; 819ce70c572SHans Petter Selasky } 820ce70c572SHans Petter Selasky 821ce70c572SHans Petter Selasky /* skip till next value, if any */ 822ce70c572SHans Petter Selasky while (*end == '\t' || *end == ',' || *end == ' ') 823ce70c572SHans Petter Selasky end++; 824ce70c572SHans Petter Selasky 825ce70c572SHans Petter Selasky /* update pointer */ 826ce70c572SHans Petter Selasky ptr = end; 827ce70c572SHans Petter Selasky 828ce70c572SHans Petter Selasky /* apply shift */ 829ce70c572SHans Petter Selasky old = value; 830ce70c572SHans Petter Selasky value <<= shift; 831ce70c572SHans Petter Selasky 832ce70c572SHans Petter Selasky /* overflow check */ 833ce70c572SHans Petter Selasky if ((value >> shift) != old) 834ce70c572SHans Petter Selasky goto error; 835ce70c572SHans Petter Selasky 836ce70c572SHans Petter Selasky /* check for buffer overflow */ 837ce70c572SHans Petter Selasky if (n >= size) 838ce70c572SHans Petter Selasky goto error; 839ce70c572SHans Petter Selasky 840ce70c572SHans Petter Selasky /* store value according to type size */ 841ce70c572SHans Petter Selasky switch (type_size) { 842ce70c572SHans Petter Selasky case 1: 843ce70c572SHans Petter Selasky if (allow_signed) { 844ce70c572SHans Petter Selasky if (value < SCHAR_MIN || value > SCHAR_MAX) 845ce70c572SHans Petter Selasky goto error; 846ce70c572SHans Petter Selasky } else { 847ce70c572SHans Petter Selasky if (value < 0 || value > UCHAR_MAX) 848ce70c572SHans Petter Selasky goto error; 849ce70c572SHans Petter Selasky } 850ce70c572SHans Petter Selasky ((uint8_t *)pdata)[n] = (uint8_t)value; 851ce70c572SHans Petter Selasky break; 852ce70c572SHans Petter Selasky case 2: 853ce70c572SHans Petter Selasky if (allow_signed) { 854ce70c572SHans Petter Selasky if (value < SHRT_MIN || value > SHRT_MAX) 855ce70c572SHans Petter Selasky goto error; 856ce70c572SHans Petter Selasky } else { 857ce70c572SHans Petter Selasky if (value < 0 || value > USHRT_MAX) 858ce70c572SHans Petter Selasky goto error; 859ce70c572SHans Petter Selasky } 860ce70c572SHans Petter Selasky ((uint16_t *)pdata)[n] = (uint16_t)value; 861ce70c572SHans Petter Selasky break; 862ce70c572SHans Petter Selasky case 4: 863ce70c572SHans Petter Selasky if (allow_signed) { 864ce70c572SHans Petter Selasky if (value < INT_MIN || value > INT_MAX) 865ce70c572SHans Petter Selasky goto error; 866ce70c572SHans Petter Selasky } else { 867ce70c572SHans Petter Selasky if (value > UINT_MAX) 868ce70c572SHans Petter Selasky goto error; 869ce70c572SHans Petter Selasky } 870ce70c572SHans Petter Selasky ((uint32_t *)pdata)[n] = (uint32_t)value; 871ce70c572SHans Petter Selasky break; 872ce70c572SHans Petter Selasky case 8: 873ce70c572SHans Petter Selasky ((uint64_t *)pdata)[n] = (uint64_t)value; 874ce70c572SHans Petter Selasky break; 875ce70c572SHans Petter Selasky default: 876ce70c572SHans Petter Selasky goto error; 877ce70c572SHans Petter Selasky } 878ce70c572SHans Petter Selasky n++; 879ce70c572SHans Petter Selasky } 880ce70c572SHans Petter Selasky *psize = n * type_size; 881ce70c572SHans Petter Selasky 882ce70c572SHans Petter Selasky if (n != 0) 88373845fdbSSimon J. Gerraty rc = 1; /* success */ 884ce70c572SHans Petter Selasky error: 885f3ba85ccSJason A. Harmening kenv_release(buf); 88673845fdbSSimon J. Gerraty return (rc); 887ce70c572SHans Petter Selasky } 888ce70c572SHans Petter Selasky 889ce70c572SHans Petter Selasky /* 8902084f96cSMike Smith * Return an integer value from an environment variable. 8912084f96cSMike Smith */ 8922084f96cSMike Smith int 893b93c3c5eSPeter Wemm getenv_int(const char *name, int *data) 8942084f96cSMike Smith { 895c1dc94eeSMatt Jacob quad_t tmp; 896c1dc94eeSMatt Jacob int rval; 897c1dc94eeSMatt Jacob 898c1dc94eeSMatt Jacob rval = getenv_quad(name, &tmp); 8996692ac66SPeter Wemm if (rval) 900c1dc94eeSMatt Jacob *data = (int) tmp; 901c1dc94eeSMatt Jacob return (rval); 902c1dc94eeSMatt Jacob } 903c1dc94eeSMatt Jacob 904c1dc94eeSMatt Jacob /* 905d302c56dSDag-Erling Smørgrav * Return an unsigned integer value from an environment variable. 906d302c56dSDag-Erling Smørgrav */ 907d302c56dSDag-Erling Smørgrav int 908d302c56dSDag-Erling Smørgrav getenv_uint(const char *name, unsigned int *data) 909d302c56dSDag-Erling Smørgrav { 910d302c56dSDag-Erling Smørgrav quad_t tmp; 911d302c56dSDag-Erling Smørgrav int rval; 912d302c56dSDag-Erling Smørgrav 913d302c56dSDag-Erling Smørgrav rval = getenv_quad(name, &tmp); 914d302c56dSDag-Erling Smørgrav if (rval) 915d302c56dSDag-Erling Smørgrav *data = (unsigned int) tmp; 916d302c56dSDag-Erling Smørgrav return (rval); 917d302c56dSDag-Erling Smørgrav } 918d302c56dSDag-Erling Smørgrav 919d302c56dSDag-Erling Smørgrav /* 92015be49f5SWarner Losh * Return an int64_t value from an environment variable. 92115be49f5SWarner Losh */ 92215be49f5SWarner Losh int 92315be49f5SWarner Losh getenv_int64(const char *name, int64_t *data) 92415be49f5SWarner Losh { 92515be49f5SWarner Losh quad_t tmp; 92615be49f5SWarner Losh int64_t rval; 92715be49f5SWarner Losh 92815be49f5SWarner Losh rval = getenv_quad(name, &tmp); 92915be49f5SWarner Losh if (rval) 93015be49f5SWarner Losh *data = (int64_t) tmp; 93115be49f5SWarner Losh return (rval); 93215be49f5SWarner Losh } 93315be49f5SWarner Losh 93415be49f5SWarner Losh /* 93515be49f5SWarner Losh * Return an uint64_t value from an environment variable. 93615be49f5SWarner Losh */ 93715be49f5SWarner Losh int 93815be49f5SWarner Losh getenv_uint64(const char *name, uint64_t *data) 93915be49f5SWarner Losh { 94015be49f5SWarner Losh quad_t tmp; 94115be49f5SWarner Losh uint64_t rval; 94215be49f5SWarner Losh 94315be49f5SWarner Losh rval = getenv_quad(name, &tmp); 94415be49f5SWarner Losh if (rval) 94515be49f5SWarner Losh *data = (uint64_t) tmp; 94615be49f5SWarner Losh return (rval); 94715be49f5SWarner Losh } 94815be49f5SWarner Losh 94915be49f5SWarner Losh /* 950b0e1e474SDag-Erling Smørgrav * Return a long value from an environment variable. 951b0e1e474SDag-Erling Smørgrav */ 952d302c56dSDag-Erling Smørgrav int 953b0e1e474SDag-Erling Smørgrav getenv_long(const char *name, long *data) 954b0e1e474SDag-Erling Smørgrav { 955b0e1e474SDag-Erling Smørgrav quad_t tmp; 956d302c56dSDag-Erling Smørgrav int rval; 957b0e1e474SDag-Erling Smørgrav 958b0e1e474SDag-Erling Smørgrav rval = getenv_quad(name, &tmp); 959b0e1e474SDag-Erling Smørgrav if (rval) 960b0e1e474SDag-Erling Smørgrav *data = (long) tmp; 961b0e1e474SDag-Erling Smørgrav return (rval); 962b0e1e474SDag-Erling Smørgrav } 963b0e1e474SDag-Erling Smørgrav 964b0e1e474SDag-Erling Smørgrav /* 965b0e1e474SDag-Erling Smørgrav * Return an unsigned long value from an environment variable. 966b0e1e474SDag-Erling Smørgrav */ 967d302c56dSDag-Erling Smørgrav int 968b0e1e474SDag-Erling Smørgrav getenv_ulong(const char *name, unsigned long *data) 969b0e1e474SDag-Erling Smørgrav { 970b0e1e474SDag-Erling Smørgrav quad_t tmp; 971d302c56dSDag-Erling Smørgrav int rval; 972b0e1e474SDag-Erling Smørgrav 973b0e1e474SDag-Erling Smørgrav rval = getenv_quad(name, &tmp); 974b0e1e474SDag-Erling Smørgrav if (rval) 975b0e1e474SDag-Erling Smørgrav *data = (unsigned long) tmp; 976b0e1e474SDag-Erling Smørgrav return (rval); 977b0e1e474SDag-Erling Smørgrav } 978b0e1e474SDag-Erling Smørgrav 979b0e1e474SDag-Erling Smørgrav /* 980c1dc94eeSMatt Jacob * Return a quad_t value from an environment variable. 981c1dc94eeSMatt Jacob */ 98221cbf0ccSJohn Baldwin int 983b93c3c5eSPeter Wemm getenv_quad(const char *name, quad_t *data) 984c1dc94eeSMatt Jacob { 985f3ba85ccSJason A. Harmening const char *value; 986f3ba85ccSJason A. Harmening char suffix, *vtp; 9872084f96cSMike Smith quad_t iv; 9882084f96cSMike Smith 989f3ba85ccSJason A. Harmening value = kenv_acquire(name); 990f3ba85ccSJason A. Harmening if (value == NULL) { 991f3ba85ccSJason A. Harmening goto error; 992f3ba85ccSJason A. Harmening } 9932084f96cSMike Smith iv = strtoq(value, &vtp, 0); 99473845fdbSSimon J. Gerraty if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { 995f3ba85ccSJason A. Harmening goto error; 99673845fdbSSimon J. Gerraty } 997f3ba85ccSJason A. Harmening suffix = vtp[0]; 998f3ba85ccSJason A. Harmening kenv_release(value); 999f3ba85ccSJason A. Harmening switch (suffix) { 1000f3301d15SDag-Erling Smørgrav case 't': case 'T': 1001f3301d15SDag-Erling Smørgrav iv *= 1024; 1002970a174fSMark Johnston /* FALLTHROUGH */ 1003f3301d15SDag-Erling Smørgrav case 'g': case 'G': 1004f3301d15SDag-Erling Smørgrav iv *= 1024; 1005970a174fSMark Johnston /* FALLTHROUGH */ 1006f3301d15SDag-Erling Smørgrav case 'm': case 'M': 1007f3301d15SDag-Erling Smørgrav iv *= 1024; 1008970a174fSMark Johnston /* FALLTHROUGH */ 1009f3301d15SDag-Erling Smørgrav case 'k': case 'K': 1010f3301d15SDag-Erling Smørgrav iv *= 1024; 1011f3301d15SDag-Erling Smørgrav case '\0': 1012f3301d15SDag-Erling Smørgrav break; 1013f3301d15SDag-Erling Smørgrav default: 10142084f96cSMike Smith return (0); 1015d786139cSMaxime Henrion } 1016c1dc94eeSMatt Jacob *data = iv; 10172084f96cSMike Smith return (1); 1018f3ba85ccSJason A. Harmening error: 1019f3ba85ccSJason A. Harmening kenv_release(value); 1020f3ba85ccSJason A. Harmening return (0); 10212084f96cSMike Smith } 10226ba9413bSMike Smith 10231114d185SPeter Wemm /* 1024cba446e2SMitchell Horne * Return a boolean value from an environment variable. This can be in 1025cba446e2SMitchell Horne * numerical or string form, i.e. "1" or "true". 1026cba446e2SMitchell Horne */ 1027cba446e2SMitchell Horne int 1028cba446e2SMitchell Horne getenv_bool(const char *name, bool *data) 1029cba446e2SMitchell Horne { 1030cba446e2SMitchell Horne char *val; 1031cba446e2SMitchell Horne int ret = 0; 1032cba446e2SMitchell Horne 1033cba446e2SMitchell Horne if (name == NULL) 1034cba446e2SMitchell Horne return (0); 1035cba446e2SMitchell Horne 1036cba446e2SMitchell Horne val = kern_getenv(name); 1037cba446e2SMitchell Horne if (val == NULL) 1038cba446e2SMitchell Horne return (0); 1039cba446e2SMitchell Horne 1040cba446e2SMitchell Horne if ((strcmp(val, "1") == 0) || (strcasecmp(val, "true") == 0)) { 1041cba446e2SMitchell Horne *data = true; 1042cba446e2SMitchell Horne ret = 1; 1043cba446e2SMitchell Horne } else if ((strcmp(val, "0") == 0) || (strcasecmp(val, "false") == 0)) { 1044cba446e2SMitchell Horne *data = false; 1045cba446e2SMitchell Horne ret = 1; 1046cba446e2SMitchell Horne } else { 1047cba446e2SMitchell Horne /* Spit out a warning for malformed boolean variables. */ 1048cba446e2SMitchell Horne printf("Environment variable %s has non-boolean value \"%s\"\n", 1049cba446e2SMitchell Horne name, val); 1050cba446e2SMitchell Horne } 1051cba446e2SMitchell Horne freeenv(val); 1052cba446e2SMitchell Horne 1053cba446e2SMitchell Horne return (ret); 1054cba446e2SMitchell Horne } 1055cba446e2SMitchell Horne 1056cba446e2SMitchell Horne /* 1057cba446e2SMitchell Horne * Wrapper around getenv_bool to easily check for true. 1058cba446e2SMitchell Horne */ 1059cba446e2SMitchell Horne bool 1060cba446e2SMitchell Horne getenv_is_true(const char *name) 1061cba446e2SMitchell Horne { 1062cba446e2SMitchell Horne bool val; 1063cba446e2SMitchell Horne 1064cba446e2SMitchell Horne if (getenv_bool(name, &val) != 0) 1065cba446e2SMitchell Horne return (val); 1066cba446e2SMitchell Horne return (false); 1067cba446e2SMitchell Horne } 1068cba446e2SMitchell Horne 1069cba446e2SMitchell Horne /* 1070cba446e2SMitchell Horne * Wrapper around getenv_bool to easily check for false. 1071cba446e2SMitchell Horne */ 1072cba446e2SMitchell Horne bool 1073cba446e2SMitchell Horne getenv_is_false(const char *name) 1074cba446e2SMitchell Horne { 1075cba446e2SMitchell Horne bool val; 1076cba446e2SMitchell Horne 1077cba446e2SMitchell Horne if (getenv_bool(name, &val) != 0) 1078cba446e2SMitchell Horne return (!val); 1079cba446e2SMitchell Horne return (false); 1080cba446e2SMitchell Horne } 1081cba446e2SMitchell Horne 1082cba446e2SMitchell Horne /* 10836ba9413bSMike Smith * Find the next entry after the one which (cp) falls within, return a 10846ba9413bSMike Smith * pointer to its start or NULL if there are no more. 10856ba9413bSMike Smith */ 10866ba9413bSMike Smith static char * 10876ba9413bSMike Smith kernenv_next(char *cp) 10886ba9413bSMike Smith { 10896692ac66SPeter Wemm 10906ba9413bSMike Smith if (cp != NULL) { 10916ba9413bSMike Smith while (*cp != 0) 10926ba9413bSMike Smith cp++; 10936ba9413bSMike Smith cp++; 10946ba9413bSMike Smith if (*cp == 0) 10956ba9413bSMike Smith cp = NULL; 10966ba9413bSMike Smith } 10976ba9413bSMike Smith return (cp); 10986ba9413bSMike Smith } 10996ba9413bSMike Smith 110009786698SPeter Wemm void 110109786698SPeter Wemm tunable_int_init(void *data) 110209786698SPeter Wemm { 110309786698SPeter Wemm struct tunable_int *d = (struct tunable_int *)data; 110409786698SPeter Wemm 110509786698SPeter Wemm TUNABLE_INT_FETCH(d->path, d->var); 110609786698SPeter Wemm } 110709786698SPeter Wemm 110809786698SPeter Wemm void 1109b0e1e474SDag-Erling Smørgrav tunable_long_init(void *data) 1110b0e1e474SDag-Erling Smørgrav { 1111b0e1e474SDag-Erling Smørgrav struct tunable_long *d = (struct tunable_long *)data; 1112b0e1e474SDag-Erling Smørgrav 1113b0e1e474SDag-Erling Smørgrav TUNABLE_LONG_FETCH(d->path, d->var); 1114b0e1e474SDag-Erling Smørgrav } 1115b0e1e474SDag-Erling Smørgrav 1116b0e1e474SDag-Erling Smørgrav void 1117b0e1e474SDag-Erling Smørgrav tunable_ulong_init(void *data) 1118b0e1e474SDag-Erling Smørgrav { 1119b0e1e474SDag-Erling Smørgrav struct tunable_ulong *d = (struct tunable_ulong *)data; 1120b0e1e474SDag-Erling Smørgrav 1121b0e1e474SDag-Erling Smørgrav TUNABLE_ULONG_FETCH(d->path, d->var); 1122b0e1e474SDag-Erling Smørgrav } 1123b0e1e474SDag-Erling Smørgrav 1124b0e1e474SDag-Erling Smørgrav void 112515be49f5SWarner Losh tunable_int64_init(void *data) 112615be49f5SWarner Losh { 112715be49f5SWarner Losh struct tunable_int64 *d = (struct tunable_int64 *)data; 112815be49f5SWarner Losh 112915be49f5SWarner Losh TUNABLE_INT64_FETCH(d->path, d->var); 113015be49f5SWarner Losh } 113115be49f5SWarner Losh 113215be49f5SWarner Losh void 113315be49f5SWarner Losh tunable_uint64_init(void *data) 113415be49f5SWarner Losh { 113515be49f5SWarner Losh struct tunable_uint64 *d = (struct tunable_uint64 *)data; 113615be49f5SWarner Losh 113715be49f5SWarner Losh TUNABLE_UINT64_FETCH(d->path, d->var); 113815be49f5SWarner Losh } 113915be49f5SWarner Losh 114015be49f5SWarner Losh void 11417f41115eSPawel Jakub Dawidek tunable_quad_init(void *data) 11427f41115eSPawel Jakub Dawidek { 11437f41115eSPawel Jakub Dawidek struct tunable_quad *d = (struct tunable_quad *)data; 11447f41115eSPawel Jakub Dawidek 11457f41115eSPawel Jakub Dawidek TUNABLE_QUAD_FETCH(d->path, d->var); 11467f41115eSPawel Jakub Dawidek } 11477f41115eSPawel Jakub Dawidek 11487f41115eSPawel Jakub Dawidek void 1149cba446e2SMitchell Horne tunable_bool_init(void *data) 1150cba446e2SMitchell Horne { 1151cba446e2SMitchell Horne struct tunable_bool *d = (struct tunable_bool *)data; 1152cba446e2SMitchell Horne 1153cba446e2SMitchell Horne TUNABLE_BOOL_FETCH(d->path, d->var); 1154cba446e2SMitchell Horne } 1155cba446e2SMitchell Horne 1156cba446e2SMitchell Horne void 115709786698SPeter Wemm tunable_str_init(void *data) 115809786698SPeter Wemm { 115909786698SPeter Wemm struct tunable_str *d = (struct tunable_str *)data; 116009786698SPeter Wemm 116109786698SPeter Wemm TUNABLE_STR_FETCH(d->path, d->var, d->size); 116209786698SPeter Wemm } 1163