kern_environment.c revision 148585
140090Smsmith/*- 240090Smsmith * Copyright (c) 1998 Michael Smith 340090Smsmith * All rights reserved. 440090Smsmith * 540090Smsmith * Redistribution and use in source and binary forms, with or without 640090Smsmith * modification, are permitted provided that the following conditions 740090Smsmith * are met: 840090Smsmith * 1. Redistributions of source code must retain the above copyright 940090Smsmith * notice, this list of conditions and the following disclaimer. 1040090Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1140090Smsmith * notice, this list of conditions and the following disclaimer in the 1240090Smsmith * documentation and/or other materials provided with the distribution. 1340090Smsmith * 1440090Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1540090Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1640090Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1740090Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1840090Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1940090Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2040090Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2140090Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2240090Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2340090Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2440090Smsmith * SUCH DAMAGE. 2540116Sjkh */ 2640090Smsmith 2740090Smsmith/* 2840090Smsmith * The unified bootloader passes us a pointer to a preserved copy of 2994936Smux * bootstrap/kernel environment variables. We convert them to a 3094936Smux * dynamic array of strings later when the VM subsystem is up. 3140090Smsmith * 3294936Smux * We make these available through the kenv(2) syscall for userland 3394936Smux * and through getenv()/freeenv() setenv() unsetenv() testenv() for 3494936Smux * the kernel. 3540090Smsmith */ 3640090Smsmith 37116182Sobrien#include <sys/cdefs.h> 38116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/kern_environment.c 148585 2005-07-31 10:28:35Z netchild $"); 39116182Sobrien 40106308Srwatson#include "opt_mac.h" 41106308Srwatson 4294936Smux#include <sys/types.h> 4340090Smsmith#include <sys/param.h> 4494936Smux#include <sys/proc.h> 4594936Smux#include <sys/queue.h> 4694936Smux#include <sys/lock.h> 47106308Srwatson#include <sys/mac.h> 4894936Smux#include <sys/malloc.h> 4994936Smux#include <sys/mutex.h> 5040090Smsmith#include <sys/kernel.h> 5194936Smux#include <sys/sx.h> 5240090Smsmith#include <sys/systm.h> 5394936Smux#include <sys/sysent.h> 5494936Smux#include <sys/sysproto.h> 5540090Smsmith#include <sys/libkern.h> 5694936Smux#include <sys/kenv.h> 5740090Smsmith 58141616Sphkstatic MALLOC_DEFINE(M_KENV, "kenv", "kernel environment"); 5940090Smsmith 6094936Smux#define KENV_SIZE 512 /* Maximum number of environment strings */ 6140090Smsmith 6294936Smux/* pointer to the static environment */ 6394936Smuxchar *kern_envp; 6494936Smuxstatic char *kernenv_next(char *); 6594936Smux 6694936Smux/* dynamic environment variables */ 6794936Smuxchar **kenvp; 6894936Smuxstruct sx kenv_lock; 6994936Smux 7085385Sjhb/* 7194936Smux * No need to protect this with a mutex 7294936Smux * since SYSINITS are single threaded. 7394936Smux */ 7494936Smuxint dynamic_kenv = 0; 7594936Smux 7694936Smux#define KENV_CHECK if (!dynamic_kenv) \ 7794936Smux panic("%s: called before SI_SUB_KMEM", __func__) 7894936Smux 7994936Smuxint 8094936Smuxkenv(td, uap) 8194936Smux struct thread *td; 8294936Smux struct kenv_args /* { 83107850Salfred int what; 84107850Salfred const char *name; 85107850Salfred char *value; 86107850Salfred int len; 8794936Smux } */ *uap; 8894936Smux{ 8994936Smux char *name, *value; 90128697Sdas size_t len, done, needed; 9194936Smux int error, i; 9295839Speter 9394936Smux KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = 0")); 9494936Smux 9594936Smux error = 0; 96107849Salfred if (uap->what == KENV_DUMP) { 97106308Srwatson#ifdef MAC 98106308Srwatson error = mac_check_kenv_dump(td->td_ucred); 99106308Srwatson if (error) 100106308Srwatson return (error); 101106308Srwatson#endif 102128697Sdas done = needed = 0; 10394936Smux sx_slock(&kenv_lock); 104128697Sdas for (i = 0; kenvp[i] != NULL; i++) { 105128697Sdas len = strlen(kenvp[i]) + 1; 106128697Sdas needed += len; 107128697Sdas len = min(len, uap->len - done); 108128697Sdas /* 109128697Sdas * If called with a NULL or insufficiently large 110128697Sdas * buffer, just keep computing the required size. 111128697Sdas */ 112128697Sdas if (uap->value != NULL && len > 0) { 113128697Sdas error = copyout(kenvp[i], uap->value + done, 114128697Sdas len); 115128697Sdas if (error) 116128697Sdas break; 117128697Sdas done += len; 11894936Smux } 11994936Smux } 12094936Smux sx_sunlock(&kenv_lock); 121128697Sdas td->td_retval[0] = ((done == needed) ? 0 : needed); 122128697Sdas return (error); 12394936Smux } 12494936Smux 125107849Salfred if ((uap->what == KENV_SET) || 126107849Salfred (uap->what == KENV_UNSET)) { 12794936Smux error = suser(td); 12894936Smux if (error) 12994936Smux return (error); 13094936Smux } 13194936Smux 132111119Simp name = malloc(KENV_MNAMELEN, M_TEMP, M_WAITOK); 13394936Smux 134107849Salfred error = copyinstr(uap->name, name, KENV_MNAMELEN, NULL); 13594936Smux if (error) 13694936Smux goto done; 13794936Smux 138107849Salfred switch (uap->what) { 13994936Smux case KENV_GET: 140106308Srwatson#ifdef MAC 141106308Srwatson error = mac_check_kenv_get(td->td_ucred, name); 142106308Srwatson if (error) 143106308Srwatson goto done; 144106308Srwatson#endif 14594936Smux value = getenv(name); 14694936Smux if (value == NULL) { 14794936Smux error = ENOENT; 14894936Smux goto done; 14994936Smux } 15094936Smux len = strlen(value) + 1; 151107849Salfred if (len > uap->len) 152107849Salfred len = uap->len; 153107849Salfred error = copyout(value, uap->value, len); 15494936Smux freeenv(value); 15594936Smux if (error) 15694936Smux goto done; 15794936Smux td->td_retval[0] = len; 15894936Smux break; 15994936Smux case KENV_SET: 160107849Salfred len = uap->len; 16194936Smux if (len < 1) { 16294936Smux error = EINVAL; 16394936Smux goto done; 16494936Smux } 16594936Smux if (len > KENV_MVALLEN) 16694936Smux len = KENV_MVALLEN; 167111119Simp value = malloc(len, M_TEMP, M_WAITOK); 168107849Salfred error = copyinstr(uap->value, value, len, NULL); 16994936Smux if (error) { 17094936Smux free(value, M_TEMP); 17194936Smux goto done; 17294936Smux } 173106308Srwatson#ifdef MAC 174106308Srwatson error = mac_check_kenv_set(td->td_ucred, name, value); 175106308Srwatson if (error == 0) 176106308Srwatson#endif 177106308Srwatson setenv(name, value); 17894936Smux free(value, M_TEMP); 17994936Smux break; 18094936Smux case KENV_UNSET: 181106308Srwatson#ifdef MAC 182106308Srwatson error = mac_check_kenv_unset(td->td_ucred, name); 183106308Srwatson if (error) 184106308Srwatson goto done; 185106308Srwatson#endif 18694936Smux error = unsetenv(name); 18794936Smux if (error) 18894936Smux error = ENOENT; 18994936Smux break; 19094936Smux default: 19194936Smux error = EINVAL; 19294936Smux break; 19394936Smux } 19494936Smuxdone: 19594936Smux free(name, M_TEMP); 19694936Smux return (error); 19794936Smux} 19894936Smux 19994936Smux/* 20094936Smux * Setup the dynamic kernel environment. 20194936Smux */ 20294936Smuxstatic void 20394936Smuxinit_dynamic_kenv(void *data __unused) 20494936Smux{ 20594936Smux char *cp; 20694936Smux int len, i; 20794936Smux 208148585Snetchild kenvp = malloc((KENV_SIZE + 1) * sizeof(char *), M_KENV, 209148585Snetchild M_WAITOK | M_ZERO); 21094936Smux i = 0; 21194936Smux for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) { 21294936Smux len = strlen(cp) + 1; 213111119Simp kenvp[i] = malloc(len, M_KENV, M_WAITOK); 21494936Smux strcpy(kenvp[i++], cp); 21594936Smux } 21694936Smux kenvp[i] = NULL; 21794936Smux 21894936Smux sx_init(&kenv_lock, "kernel environment"); 21994936Smux dynamic_kenv = 1; 22094936Smux} 22194936SmuxSYSINIT(kenv, SI_SUB_KMEM, SI_ORDER_ANY, init_dynamic_kenv, NULL); 22294936Smux 22394936Smuxvoid 22494936Smuxfreeenv(char *env) 22594936Smux{ 22694936Smux 22794936Smux if (dynamic_kenv) 22894936Smux free(env, M_KENV); 22994936Smux} 23094936Smux 23194936Smux/* 23294936Smux * Internal functions for string lookup. 23394936Smux */ 23494936Smuxstatic char * 23594936Smux_getenv_dynamic(const char *name, int *idx) 23694936Smux{ 23794936Smux char *cp; 23894936Smux int len, i; 23994936Smux 24094936Smux sx_assert(&kenv_lock, SX_LOCKED); 24194936Smux len = strlen(name); 24294936Smux for (cp = kenvp[0], i = 0; cp != NULL; cp = kenvp[++i]) { 24394936Smux if ((cp[len] == '=') && 24494936Smux (strncmp(cp, name, len) == 0)) { 24594936Smux if (idx != NULL) 24694936Smux *idx = i; 24794936Smux return (cp + len + 1); 24894936Smux } 24994936Smux } 25094936Smux return (NULL); 25194936Smux} 25294936Smux 25394936Smuxstatic char * 25494936Smux_getenv_static(const char *name) 25594936Smux{ 25694936Smux char *cp, *ep; 25794936Smux int len; 25894936Smux 25994936Smux for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) { 26094936Smux for (ep = cp; (*ep != '=') && (*ep != 0); ep++) 26194936Smux ; 26295467Sbde if (*ep != '=') 26395467Sbde continue; 26494936Smux len = ep - cp; 26595467Sbde ep++; 26695467Sbde if (!strncmp(name, cp, len) && name[len] == 0) 26794936Smux return (ep); 26894936Smux } 26994936Smux return (NULL); 27094936Smux} 27194936Smux 27294936Smux/* 27385385Sjhb * Look up an environment variable by name. 27494936Smux * Return a pointer to the string if found. 27594936Smux * The pointer has to be freed with freeenv() 27694936Smux * after use. 27785385Sjhb */ 27840090Smsmithchar * 27978247Spetergetenv(const char *name) 28040090Smsmith{ 28194959Smux char buf[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1]; 28294936Smux char *ret, *cp; 28394936Smux int len; 28494936Smux 28594936Smux if (dynamic_kenv) { 28694936Smux sx_slock(&kenv_lock); 28794936Smux cp = _getenv_dynamic(name, NULL); 28894936Smux if (cp != NULL) { 28994959Smux strcpy(buf, cp); 29094959Smux sx_sunlock(&kenv_lock); 29194959Smux len = strlen(buf) + 1; 292111119Simp ret = malloc(len, M_KENV, M_WAITOK); 29394959Smux strcpy(ret, buf); 29494959Smux } else { 29594959Smux sx_sunlock(&kenv_lock); 29694936Smux ret = NULL; 29794959Smux } 29894936Smux } else 29994936Smux ret = _getenv_static(name); 30094936Smux return (ret); 30140090Smsmith} 30240090Smsmith 30342706Smsmith/* 30494936Smux * Test if an environment variable is defined. 30594936Smux */ 30694936Smuxint 30794936Smuxtestenv(const char *name) 30894936Smux{ 30994936Smux char *cp; 31094936Smux 31194936Smux if (dynamic_kenv) { 31294936Smux sx_slock(&kenv_lock); 31394936Smux cp = _getenv_dynamic(name, NULL); 31494936Smux sx_sunlock(&kenv_lock); 31594936Smux } else 31694936Smux cp = _getenv_static(name); 31794936Smux if (cp != NULL) 31894936Smux return (1); 31994936Smux return (0); 32094936Smux} 32194936Smux 32294936Smux/* 32394936Smux * Set an environment variable by name. 32494936Smux */ 32594959Smuxint 32694936Smuxsetenv(const char *name, const char *value) 32794936Smux{ 32894959Smux char *buf, *cp, *oldenv; 32994959Smux int namelen, vallen, i; 33094936Smux 33194936Smux KENV_CHECK; 33294936Smux 33394959Smux namelen = strlen(name) + 1; 33494959Smux if (namelen > KENV_MNAMELEN) 33594959Smux return (-1); 33694959Smux vallen = strlen(value) + 1; 33794959Smux if (vallen > KENV_MVALLEN) 33894959Smux return (-1); 339111119Simp buf = malloc(namelen + vallen, M_KENV, M_WAITOK); 34094936Smux sprintf(buf, "%s=%s", name, value); 34194936Smux 34294936Smux sx_xlock(&kenv_lock); 34394936Smux cp = _getenv_dynamic(name, &i); 34494936Smux if (cp != NULL) { 34594959Smux oldenv = kenvp[i]; 34694936Smux kenvp[i] = buf; 34794959Smux sx_xunlock(&kenv_lock); 34894959Smux free(oldenv, M_KENV); 34994936Smux } else { 35094936Smux /* We add the option if it wasn't found */ 35194936Smux for (i = 0; (cp = kenvp[i]) != NULL; i++) 35294936Smux ; 353148585Snetchild 354148585Snetchild /* Bounds checking */ 355148585Snetchild if (i < 0 || i >= KENV_SIZE) { 356148585Snetchild free(buf, M_KENV); 357148585Snetchild sx_xunlock(&kenv_lock); 358148585Snetchild return (-1); 359148585Snetchild } 360148585Snetchild 36194936Smux kenvp[i] = buf; 36294936Smux kenvp[i + 1] = NULL; 36394959Smux sx_xunlock(&kenv_lock); 36494936Smux } 36594959Smux return (0); 36694936Smux} 36794936Smux 36894936Smux/* 36994936Smux * Unset an environment variable string. 37094936Smux */ 37194936Smuxint 37294936Smuxunsetenv(const char *name) 37394936Smux{ 37494959Smux char *cp, *oldenv; 37594936Smux int i, j; 37694936Smux 37794936Smux KENV_CHECK; 37894936Smux 37994936Smux sx_xlock(&kenv_lock); 38094936Smux cp = _getenv_dynamic(name, &i); 38194936Smux if (cp != NULL) { 38294959Smux oldenv = kenvp[i]; 38394936Smux for (j = i + 1; kenvp[j] != NULL; j++) 38494936Smux kenvp[i++] = kenvp[j]; 38594936Smux kenvp[i] = NULL; 38694936Smux sx_xunlock(&kenv_lock); 38794959Smux free(oldenv, M_KENV); 38894936Smux return (0); 38994936Smux } 39094936Smux sx_xunlock(&kenv_lock); 39194936Smux return (-1); 39294936Smux} 39394936Smux 39494936Smux/* 39585385Sjhb * Return a string value from an environment variable. 39685385Sjhb */ 39785385Sjhbint 39885385Sjhbgetenv_string(const char *name, char *data, int size) 39985385Sjhb{ 40095839Speter char *tmp; 40185385Sjhb 40295839Speter tmp = getenv(name); 40395839Speter if (tmp != NULL) { 404105354Srobert strlcpy(data, tmp, size); 40595839Speter freeenv(tmp); 40695839Speter return (1); 40795839Speter } else 40895839Speter return (0); 40985385Sjhb} 41085385Sjhb 41185385Sjhb/* 41242706Smsmith * Return an integer value from an environment variable. 41342706Smsmith */ 41442706Smsmithint 41578247Spetergetenv_int(const char *name, int *data) 41642706Smsmith{ 41795839Speter quad_t tmp; 41895839Speter int rval; 41952947Smjacob 42095839Speter rval = getenv_quad(name, &tmp); 42195839Speter if (rval) 42295839Speter *data = (int) tmp; 42395839Speter return (rval); 42452947Smjacob} 42552947Smjacob 42652947Smjacob/* 427137099Sdes * Return a long value from an environment variable. 428137099Sdes */ 429137099Sdeslong 430137099Sdesgetenv_long(const char *name, long *data) 431137099Sdes{ 432137099Sdes quad_t tmp; 433137099Sdes long rval; 434137099Sdes 435137099Sdes rval = getenv_quad(name, &tmp); 436137099Sdes if (rval) 437137099Sdes *data = (long) tmp; 438137099Sdes return (rval); 439137099Sdes} 440137099Sdes 441137099Sdes/* 442137099Sdes * Return an unsigned long value from an environment variable. 443137099Sdes */ 444137099Sdesunsigned long 445137099Sdesgetenv_ulong(const char *name, unsigned long *data) 446137099Sdes{ 447137099Sdes quad_t tmp; 448137099Sdes long rval; 449137099Sdes 450137099Sdes rval = getenv_quad(name, &tmp); 451137099Sdes if (rval) 452137099Sdes *data = (unsigned long) tmp; 453137099Sdes return (rval); 454137099Sdes} 455137099Sdes 456137099Sdes/* 45752947Smjacob * Return a quad_t value from an environment variable. 45852947Smjacob */ 45985385Sjhbint 46078247Spetergetenv_quad(const char *name, quad_t *data) 46152947Smjacob{ 46295839Speter char *value; 46395839Speter char *vtp; 46495839Speter quad_t iv; 46595839Speter 46695839Speter value = getenv(name); 46795839Speter if (value == NULL) 46895839Speter return (0); 46995839Speter iv = strtoq(value, &vtp, 0); 470143319Sdes if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { 471143319Sdes freeenv(value); 47295839Speter return (0); 473143319Sdes } 474143151Sdes switch (vtp[0]) { 475143151Sdes case 't': case 'T': 476143151Sdes iv *= 1024; 477143151Sdes case 'g': case 'G': 478143151Sdes iv *= 1024; 479143151Sdes case 'm': case 'M': 480143151Sdes iv *= 1024; 481143151Sdes case 'k': case 'K': 482143151Sdes iv *= 1024; 483143151Sdes case '\0': 484143151Sdes break; 485143151Sdes default: 486143319Sdes freeenv(value); 487143151Sdes return (0); 48895839Speter } 48995839Speter *data = iv; 490143319Sdes freeenv(value); 49195839Speter return (1); 49242706Smsmith} 49340090Smsmith 49483744Speter/* 49540090Smsmith * Find the next entry after the one which (cp) falls within, return a 49640090Smsmith * pointer to its start or NULL if there are no more. 49740090Smsmith */ 49840090Smsmithstatic char * 49940090Smsmithkernenv_next(char *cp) 50040090Smsmith{ 50195839Speter 50295839Speter if (cp != NULL) { 50395839Speter while (*cp != 0) 50495839Speter cp++; 50595839Speter cp++; 50695839Speter if (*cp == 0) 50795839Speter cp = NULL; 50895839Speter } 50995839Speter return (cp); 51040090Smsmith} 51140090Smsmith 51277900Spetervoid 51377900Spetertunable_int_init(void *data) 51477900Speter{ 51577900Speter struct tunable_int *d = (struct tunable_int *)data; 51677900Speter 51777900Speter TUNABLE_INT_FETCH(d->path, d->var); 51877900Speter} 51977900Speter 52077900Spetervoid 521137099Sdestunable_long_init(void *data) 522137099Sdes{ 523137099Sdes struct tunable_long *d = (struct tunable_long *)data; 524137099Sdes 525137099Sdes TUNABLE_LONG_FETCH(d->path, d->var); 526137099Sdes} 527137099Sdes 528137099Sdesvoid 529137099Sdestunable_ulong_init(void *data) 530137099Sdes{ 531137099Sdes struct tunable_ulong *d = (struct tunable_ulong *)data; 532137099Sdes 533137099Sdes TUNABLE_ULONG_FETCH(d->path, d->var); 534137099Sdes} 535137099Sdes 536137099Sdesvoid 53777900Spetertunable_str_init(void *data) 53877900Speter{ 53977900Speter struct tunable_str *d = (struct tunable_str *)data; 54077900Speter 54177900Speter TUNABLE_STR_FETCH(d->path, d->var, d->size); 54277900Speter} 543