kern_environment.c revision 143319
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 143319 2005-03-09 12:16:45Z des $"); 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 208111119Simp kenvp = malloc(KENV_SIZE * sizeof(char *), M_KENV, M_WAITOK | M_ZERO); 20994936Smux i = 0; 21094936Smux for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) { 21194936Smux len = strlen(cp) + 1; 212111119Simp kenvp[i] = malloc(len, M_KENV, M_WAITOK); 21394936Smux strcpy(kenvp[i++], cp); 21494936Smux } 21594936Smux kenvp[i] = NULL; 21694936Smux 21794936Smux sx_init(&kenv_lock, "kernel environment"); 21894936Smux dynamic_kenv = 1; 21994936Smux} 22094936SmuxSYSINIT(kenv, SI_SUB_KMEM, SI_ORDER_ANY, init_dynamic_kenv, NULL); 22194936Smux 22294936Smuxvoid 22394936Smuxfreeenv(char *env) 22494936Smux{ 22594936Smux 22694936Smux if (dynamic_kenv) 22794936Smux free(env, M_KENV); 22894936Smux} 22994936Smux 23094936Smux/* 23194936Smux * Internal functions for string lookup. 23294936Smux */ 23394936Smuxstatic char * 23494936Smux_getenv_dynamic(const char *name, int *idx) 23594936Smux{ 23694936Smux char *cp; 23794936Smux int len, i; 23894936Smux 23994936Smux sx_assert(&kenv_lock, SX_LOCKED); 24094936Smux len = strlen(name); 24194936Smux for (cp = kenvp[0], i = 0; cp != NULL; cp = kenvp[++i]) { 24294936Smux if ((cp[len] == '=') && 24394936Smux (strncmp(cp, name, len) == 0)) { 24494936Smux if (idx != NULL) 24594936Smux *idx = i; 24694936Smux return (cp + len + 1); 24794936Smux } 24894936Smux } 24994936Smux return (NULL); 25094936Smux} 25194936Smux 25294936Smuxstatic char * 25394936Smux_getenv_static(const char *name) 25494936Smux{ 25594936Smux char *cp, *ep; 25694936Smux int len; 25794936Smux 25894936Smux for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) { 25994936Smux for (ep = cp; (*ep != '=') && (*ep != 0); ep++) 26094936Smux ; 26195467Sbde if (*ep != '=') 26295467Sbde continue; 26394936Smux len = ep - cp; 26495467Sbde ep++; 26595467Sbde if (!strncmp(name, cp, len) && name[len] == 0) 26694936Smux return (ep); 26794936Smux } 26894936Smux return (NULL); 26994936Smux} 27094936Smux 27194936Smux/* 27285385Sjhb * Look up an environment variable by name. 27394936Smux * Return a pointer to the string if found. 27494936Smux * The pointer has to be freed with freeenv() 27594936Smux * after use. 27685385Sjhb */ 27740090Smsmithchar * 27878247Spetergetenv(const char *name) 27940090Smsmith{ 28094959Smux char buf[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1]; 28194936Smux char *ret, *cp; 28294936Smux int len; 28394936Smux 28494936Smux if (dynamic_kenv) { 28594936Smux sx_slock(&kenv_lock); 28694936Smux cp = _getenv_dynamic(name, NULL); 28794936Smux if (cp != NULL) { 28894959Smux strcpy(buf, cp); 28994959Smux sx_sunlock(&kenv_lock); 29094959Smux len = strlen(buf) + 1; 291111119Simp ret = malloc(len, M_KENV, M_WAITOK); 29294959Smux strcpy(ret, buf); 29394959Smux } else { 29494959Smux sx_sunlock(&kenv_lock); 29594936Smux ret = NULL; 29694959Smux } 29794936Smux } else 29894936Smux ret = _getenv_static(name); 29994936Smux return (ret); 30040090Smsmith} 30140090Smsmith 30242706Smsmith/* 30394936Smux * Test if an environment variable is defined. 30494936Smux */ 30594936Smuxint 30694936Smuxtestenv(const char *name) 30794936Smux{ 30894936Smux char *cp; 30994936Smux 31094936Smux if (dynamic_kenv) { 31194936Smux sx_slock(&kenv_lock); 31294936Smux cp = _getenv_dynamic(name, NULL); 31394936Smux sx_sunlock(&kenv_lock); 31494936Smux } else 31594936Smux cp = _getenv_static(name); 31694936Smux if (cp != NULL) 31794936Smux return (1); 31894936Smux return (0); 31994936Smux} 32094936Smux 32194936Smux/* 32294936Smux * Set an environment variable by name. 32394936Smux */ 32494959Smuxint 32594936Smuxsetenv(const char *name, const char *value) 32694936Smux{ 32794959Smux char *buf, *cp, *oldenv; 32894959Smux int namelen, vallen, i; 32994936Smux 33094936Smux KENV_CHECK; 33194936Smux 33294959Smux namelen = strlen(name) + 1; 33394959Smux if (namelen > KENV_MNAMELEN) 33494959Smux return (-1); 33594959Smux vallen = strlen(value) + 1; 33694959Smux if (vallen > KENV_MVALLEN) 33794959Smux return (-1); 338111119Simp buf = malloc(namelen + vallen, M_KENV, M_WAITOK); 33994936Smux sprintf(buf, "%s=%s", name, value); 34094936Smux 34194936Smux sx_xlock(&kenv_lock); 34294936Smux cp = _getenv_dynamic(name, &i); 34394936Smux if (cp != NULL) { 34494959Smux oldenv = kenvp[i]; 34594936Smux kenvp[i] = buf; 34694959Smux sx_xunlock(&kenv_lock); 34794959Smux free(oldenv, M_KENV); 34894936Smux } else { 34994936Smux /* We add the option if it wasn't found */ 35094936Smux for (i = 0; (cp = kenvp[i]) != NULL; i++) 35194936Smux ; 35294936Smux kenvp[i] = buf; 35394936Smux kenvp[i + 1] = NULL; 35494959Smux sx_xunlock(&kenv_lock); 35594936Smux } 35694959Smux return (0); 35794936Smux} 35894936Smux 35994936Smux/* 36094936Smux * Unset an environment variable string. 36194936Smux */ 36294936Smuxint 36394936Smuxunsetenv(const char *name) 36494936Smux{ 36594959Smux char *cp, *oldenv; 36694936Smux int i, j; 36794936Smux 36894936Smux KENV_CHECK; 36994936Smux 37094936Smux sx_xlock(&kenv_lock); 37194936Smux cp = _getenv_dynamic(name, &i); 37294936Smux if (cp != NULL) { 37394959Smux oldenv = kenvp[i]; 37494936Smux for (j = i + 1; kenvp[j] != NULL; j++) 37594936Smux kenvp[i++] = kenvp[j]; 37694936Smux kenvp[i] = NULL; 37794936Smux sx_xunlock(&kenv_lock); 37894959Smux free(oldenv, M_KENV); 37994936Smux return (0); 38094936Smux } 38194936Smux sx_xunlock(&kenv_lock); 38294936Smux return (-1); 38394936Smux} 38494936Smux 38594936Smux/* 38685385Sjhb * Return a string value from an environment variable. 38785385Sjhb */ 38885385Sjhbint 38985385Sjhbgetenv_string(const char *name, char *data, int size) 39085385Sjhb{ 39195839Speter char *tmp; 39285385Sjhb 39395839Speter tmp = getenv(name); 39495839Speter if (tmp != NULL) { 395105354Srobert strlcpy(data, tmp, size); 39695839Speter freeenv(tmp); 39795839Speter return (1); 39895839Speter } else 39995839Speter return (0); 40085385Sjhb} 40185385Sjhb 40285385Sjhb/* 40342706Smsmith * Return an integer value from an environment variable. 40442706Smsmith */ 40542706Smsmithint 40678247Spetergetenv_int(const char *name, int *data) 40742706Smsmith{ 40895839Speter quad_t tmp; 40995839Speter int rval; 41052947Smjacob 41195839Speter rval = getenv_quad(name, &tmp); 41295839Speter if (rval) 41395839Speter *data = (int) tmp; 41495839Speter return (rval); 41552947Smjacob} 41652947Smjacob 41752947Smjacob/* 418137099Sdes * Return a long value from an environment variable. 419137099Sdes */ 420137099Sdeslong 421137099Sdesgetenv_long(const char *name, long *data) 422137099Sdes{ 423137099Sdes quad_t tmp; 424137099Sdes long rval; 425137099Sdes 426137099Sdes rval = getenv_quad(name, &tmp); 427137099Sdes if (rval) 428137099Sdes *data = (long) tmp; 429137099Sdes return (rval); 430137099Sdes} 431137099Sdes 432137099Sdes/* 433137099Sdes * Return an unsigned long value from an environment variable. 434137099Sdes */ 435137099Sdesunsigned long 436137099Sdesgetenv_ulong(const char *name, unsigned long *data) 437137099Sdes{ 438137099Sdes quad_t tmp; 439137099Sdes long rval; 440137099Sdes 441137099Sdes rval = getenv_quad(name, &tmp); 442137099Sdes if (rval) 443137099Sdes *data = (unsigned long) tmp; 444137099Sdes return (rval); 445137099Sdes} 446137099Sdes 447137099Sdes/* 44852947Smjacob * Return a quad_t value from an environment variable. 44952947Smjacob */ 45085385Sjhbint 45178247Spetergetenv_quad(const char *name, quad_t *data) 45252947Smjacob{ 45395839Speter char *value; 45495839Speter char *vtp; 45595839Speter quad_t iv; 45695839Speter 45795839Speter value = getenv(name); 45895839Speter if (value == NULL) 45995839Speter return (0); 46095839Speter iv = strtoq(value, &vtp, 0); 461143319Sdes if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { 462143319Sdes freeenv(value); 46395839Speter return (0); 464143319Sdes } 465143151Sdes switch (vtp[0]) { 466143151Sdes case 't': case 'T': 467143151Sdes iv *= 1024; 468143151Sdes case 'g': case 'G': 469143151Sdes iv *= 1024; 470143151Sdes case 'm': case 'M': 471143151Sdes iv *= 1024; 472143151Sdes case 'k': case 'K': 473143151Sdes iv *= 1024; 474143151Sdes case '\0': 475143151Sdes break; 476143151Sdes default: 477143319Sdes freeenv(value); 478143151Sdes return (0); 47995839Speter } 48095839Speter *data = iv; 481143319Sdes freeenv(value); 48295839Speter return (1); 48342706Smsmith} 48440090Smsmith 48583744Speter/* 48640090Smsmith * Find the next entry after the one which (cp) falls within, return a 48740090Smsmith * pointer to its start or NULL if there are no more. 48840090Smsmith */ 48940090Smsmithstatic char * 49040090Smsmithkernenv_next(char *cp) 49140090Smsmith{ 49295839Speter 49395839Speter if (cp != NULL) { 49495839Speter while (*cp != 0) 49595839Speter cp++; 49695839Speter cp++; 49795839Speter if (*cp == 0) 49895839Speter cp = NULL; 49995839Speter } 50095839Speter return (cp); 50140090Smsmith} 50240090Smsmith 50377900Spetervoid 50477900Spetertunable_int_init(void *data) 50577900Speter{ 50677900Speter struct tunable_int *d = (struct tunable_int *)data; 50777900Speter 50877900Speter TUNABLE_INT_FETCH(d->path, d->var); 50977900Speter} 51077900Speter 51177900Spetervoid 512137099Sdestunable_long_init(void *data) 513137099Sdes{ 514137099Sdes struct tunable_long *d = (struct tunable_long *)data; 515137099Sdes 516137099Sdes TUNABLE_LONG_FETCH(d->path, d->var); 517137099Sdes} 518137099Sdes 519137099Sdesvoid 520137099Sdestunable_ulong_init(void *data) 521137099Sdes{ 522137099Sdes struct tunable_ulong *d = (struct tunable_ulong *)data; 523137099Sdes 524137099Sdes TUNABLE_ULONG_FETCH(d->path, d->var); 525137099Sdes} 526137099Sdes 527137099Sdesvoid 52877900Spetertunable_str_init(void *data) 52977900Speter{ 53077900Speter struct tunable_str *d = (struct tunable_str *)data; 53177900Speter 53277900Speter TUNABLE_STR_FETCH(d->path, d->var, d->size); 53377900Speter} 534