kern_environment.c revision 190301
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 190301 2009-03-23 00:00:50Z cperciva $"); 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> 4794936Smux#include <sys/malloc.h> 4894936Smux#include <sys/mutex.h> 49164033Srwatson#include <sys/priv.h> 5040090Smsmith#include <sys/kernel.h> 5140090Smsmith#include <sys/systm.h> 5294936Smux#include <sys/sysent.h> 5394936Smux#include <sys/sysproto.h> 5440090Smsmith#include <sys/libkern.h> 5594936Smux#include <sys/kenv.h> 5640090Smsmith 57163606Srwatson#include <security/mac/mac_framework.h> 58163606Srwatson 59141616Sphkstatic MALLOC_DEFINE(M_KENV, "kenv", "kernel environment"); 6040090Smsmith 6194936Smux#define KENV_SIZE 512 /* Maximum number of environment strings */ 6240090Smsmith 6394936Smux/* pointer to the static environment */ 6494936Smuxchar *kern_envp; 6594936Smuxstatic char *kernenv_next(char *); 6694936Smux 6794936Smux/* dynamic environment variables */ 6894936Smuxchar **kenvp; 69160217Sscottlstruct mtx kenv_lock; 7094936Smux 7185385Sjhb/* 72167232Srwatson * No need to protect this with a mutex 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{ 89160217Sscottl char *name, *value, *buffer = NULL; 90190301Scperciva size_t len, done, needed, buflen; 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 98172930Srwatson error = mac_kenv_check_dump(td->td_ucred); 99106308Srwatson if (error) 100106308Srwatson return (error); 101106308Srwatson#endif 102128697Sdas done = needed = 0; 103190301Scperciva buflen = uap->len; 104190301Scperciva if (buflen > KENV_SIZE * (KENV_MNAMELEN + KENV_MVALLEN + 2)) 105190301Scperciva buflen = KENV_SIZE * (KENV_MNAMELEN + 106190301Scperciva KENV_MVALLEN + 2); 107160217Sscottl if (uap->len > 0 && uap->value != NULL) 108190301Scperciva buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO); 109160217Sscottl mtx_lock(&kenv_lock); 110128697Sdas for (i = 0; kenvp[i] != NULL; i++) { 111128697Sdas len = strlen(kenvp[i]) + 1; 112128697Sdas needed += len; 113190301Scperciva len = min(len, buflen - done); 114128697Sdas /* 115128697Sdas * If called with a NULL or insufficiently large 116128697Sdas * buffer, just keep computing the required size. 117128697Sdas */ 118160217Sscottl if (uap->value != NULL && buffer != NULL && len > 0) { 119160217Sscottl bcopy(kenvp[i], buffer + done, len); 120128697Sdas done += len; 12194936Smux } 12294936Smux } 123160217Sscottl mtx_unlock(&kenv_lock); 124160217Sscottl if (buffer != NULL) { 125160217Sscottl error = copyout(buffer, uap->value, done); 126160217Sscottl free(buffer, M_TEMP); 127160217Sscottl } 128128697Sdas td->td_retval[0] = ((done == needed) ? 0 : needed); 129128697Sdas return (error); 13094936Smux } 13194936Smux 132164033Srwatson switch (uap->what) { 133164033Srwatson case KENV_SET: 134164033Srwatson error = priv_check(td, PRIV_KENV_SET); 13594936Smux if (error) 13694936Smux return (error); 137164033Srwatson break; 138164033Srwatson 139164033Srwatson case KENV_UNSET: 140164033Srwatson error = priv_check(td, PRIV_KENV_UNSET); 141164033Srwatson if (error) 142164033Srwatson return (error); 143164033Srwatson break; 14494936Smux } 14594936Smux 146111119Simp name = malloc(KENV_MNAMELEN, M_TEMP, M_WAITOK); 14794936Smux 148107849Salfred error = copyinstr(uap->name, name, KENV_MNAMELEN, NULL); 14994936Smux if (error) 15094936Smux goto done; 15194936Smux 152107849Salfred switch (uap->what) { 15394936Smux case KENV_GET: 154106308Srwatson#ifdef MAC 155172930Srwatson error = mac_kenv_check_get(td->td_ucred, name); 156106308Srwatson if (error) 157106308Srwatson goto done; 158106308Srwatson#endif 15994936Smux value = getenv(name); 16094936Smux if (value == NULL) { 16194936Smux error = ENOENT; 16294936Smux goto done; 16394936Smux } 16494936Smux len = strlen(value) + 1; 165107849Salfred if (len > uap->len) 166107849Salfred len = uap->len; 167107849Salfred error = copyout(value, uap->value, len); 16894936Smux freeenv(value); 16994936Smux if (error) 17094936Smux goto done; 17194936Smux td->td_retval[0] = len; 17294936Smux break; 17394936Smux case KENV_SET: 174107849Salfred len = uap->len; 17594936Smux if (len < 1) { 17694936Smux error = EINVAL; 17794936Smux goto done; 17894936Smux } 17994936Smux if (len > KENV_MVALLEN) 18094936Smux len = KENV_MVALLEN; 181111119Simp value = malloc(len, M_TEMP, M_WAITOK); 182107849Salfred error = copyinstr(uap->value, value, len, NULL); 18394936Smux if (error) { 18494936Smux free(value, M_TEMP); 18594936Smux goto done; 18694936Smux } 187106308Srwatson#ifdef MAC 188172930Srwatson error = mac_kenv_check_set(td->td_ucred, name, value); 189106308Srwatson if (error == 0) 190106308Srwatson#endif 191106308Srwatson setenv(name, value); 19294936Smux free(value, M_TEMP); 19394936Smux break; 19494936Smux case KENV_UNSET: 195106308Srwatson#ifdef MAC 196172930Srwatson error = mac_kenv_check_unset(td->td_ucred, name); 197106308Srwatson if (error) 198106308Srwatson goto done; 199106308Srwatson#endif 20094936Smux error = unsetenv(name); 20194936Smux if (error) 20294936Smux error = ENOENT; 20394936Smux break; 20494936Smux default: 20594936Smux error = EINVAL; 20694936Smux break; 20794936Smux } 20894936Smuxdone: 20994936Smux free(name, M_TEMP); 21094936Smux return (error); 21194936Smux} 21294936Smux 21394936Smux/* 21494936Smux * Setup the dynamic kernel environment. 21594936Smux */ 21694936Smuxstatic void 21794936Smuxinit_dynamic_kenv(void *data __unused) 21894936Smux{ 21994936Smux char *cp; 22094936Smux int len, i; 22194936Smux 222148585Snetchild kenvp = malloc((KENV_SIZE + 1) * sizeof(char *), M_KENV, 223148585Snetchild M_WAITOK | M_ZERO); 22494936Smux i = 0; 22594936Smux for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) { 22694936Smux len = strlen(cp) + 1; 227156748Snetchild if (i < KENV_SIZE) { 228156748Snetchild kenvp[i] = malloc(len, M_KENV, M_WAITOK); 229156482Sphk strcpy(kenvp[i++], cp); 230156748Snetchild } else 231156482Sphk printf( 232156482Sphk "WARNING: too many kenv strings, ignoring %s\n", 233156482Sphk cp); 23494936Smux } 23594936Smux kenvp[i] = NULL; 23694936Smux 237160217Sscottl mtx_init(&kenv_lock, "kernel environment", NULL, MTX_DEF); 23894936Smux dynamic_kenv = 1; 23994936Smux} 24094936SmuxSYSINIT(kenv, SI_SUB_KMEM, SI_ORDER_ANY, init_dynamic_kenv, NULL); 24194936Smux 24294936Smuxvoid 24394936Smuxfreeenv(char *env) 24494936Smux{ 24594936Smux 24694936Smux if (dynamic_kenv) 24794936Smux free(env, M_KENV); 24894936Smux} 24994936Smux 25094936Smux/* 25194936Smux * Internal functions for string lookup. 25294936Smux */ 25394936Smuxstatic char * 25494936Smux_getenv_dynamic(const char *name, int *idx) 25594936Smux{ 25694936Smux char *cp; 25794936Smux int len, i; 25894936Smux 259160217Sscottl mtx_assert(&kenv_lock, MA_OWNED); 26094936Smux len = strlen(name); 26194936Smux for (cp = kenvp[0], i = 0; cp != NULL; cp = kenvp[++i]) { 262150568Sdavidxu if ((strncmp(cp, name, len) == 0) && 263150568Sdavidxu (cp[len] == '=')) { 26494936Smux if (idx != NULL) 26594936Smux *idx = i; 26694936Smux return (cp + len + 1); 26794936Smux } 26894936Smux } 26994936Smux return (NULL); 27094936Smux} 27194936Smux 27294936Smuxstatic char * 27394936Smux_getenv_static(const char *name) 27494936Smux{ 27594936Smux char *cp, *ep; 27694936Smux int len; 27794936Smux 27894936Smux for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) { 27994936Smux for (ep = cp; (*ep != '=') && (*ep != 0); ep++) 28094936Smux ; 28195467Sbde if (*ep != '=') 28295467Sbde continue; 28394936Smux len = ep - cp; 28495467Sbde ep++; 28595467Sbde if (!strncmp(name, cp, len) && name[len] == 0) 28694936Smux return (ep); 28794936Smux } 28894936Smux return (NULL); 28994936Smux} 29094936Smux 29194936Smux/* 29285385Sjhb * Look up an environment variable by name. 29394936Smux * Return a pointer to the string if found. 29494936Smux * The pointer has to be freed with freeenv() 29594936Smux * after use. 29685385Sjhb */ 29740090Smsmithchar * 29878247Spetergetenv(const char *name) 29940090Smsmith{ 30094959Smux char buf[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1]; 30194936Smux char *ret, *cp; 30294936Smux int len; 30394936Smux 30494936Smux if (dynamic_kenv) { 305160217Sscottl mtx_lock(&kenv_lock); 30694936Smux cp = _getenv_dynamic(name, NULL); 30794936Smux if (cp != NULL) { 30894959Smux strcpy(buf, cp); 309160217Sscottl mtx_unlock(&kenv_lock); 31094959Smux len = strlen(buf) + 1; 311111119Simp ret = malloc(len, M_KENV, M_WAITOK); 31294959Smux strcpy(ret, buf); 31394959Smux } else { 314160217Sscottl mtx_unlock(&kenv_lock); 31594936Smux ret = NULL; 31694959Smux } 31794936Smux } else 31894936Smux ret = _getenv_static(name); 31994936Smux return (ret); 32040090Smsmith} 32140090Smsmith 32242706Smsmith/* 32394936Smux * Test if an environment variable is defined. 32494936Smux */ 32594936Smuxint 32694936Smuxtestenv(const char *name) 32794936Smux{ 32894936Smux char *cp; 32994936Smux 33094936Smux if (dynamic_kenv) { 331160217Sscottl mtx_lock(&kenv_lock); 33294936Smux cp = _getenv_dynamic(name, NULL); 333160217Sscottl mtx_unlock(&kenv_lock); 33494936Smux } else 33594936Smux cp = _getenv_static(name); 33694936Smux if (cp != NULL) 33794936Smux return (1); 33894936Smux return (0); 33994936Smux} 34094936Smux 34194936Smux/* 34294936Smux * Set an environment variable by name. 34394936Smux */ 34494959Smuxint 34594936Smuxsetenv(const char *name, const char *value) 34694936Smux{ 34794959Smux char *buf, *cp, *oldenv; 34894959Smux int namelen, vallen, i; 34994936Smux 35094936Smux KENV_CHECK; 35194936Smux 35294959Smux namelen = strlen(name) + 1; 35394959Smux if (namelen > KENV_MNAMELEN) 35494959Smux return (-1); 35594959Smux vallen = strlen(value) + 1; 35694959Smux if (vallen > KENV_MVALLEN) 35794959Smux return (-1); 358111119Simp buf = malloc(namelen + vallen, M_KENV, M_WAITOK); 35994936Smux sprintf(buf, "%s=%s", name, value); 36094936Smux 361160217Sscottl mtx_lock(&kenv_lock); 36294936Smux cp = _getenv_dynamic(name, &i); 36394936Smux if (cp != NULL) { 36494959Smux oldenv = kenvp[i]; 36594936Smux kenvp[i] = buf; 366160217Sscottl mtx_unlock(&kenv_lock); 36794959Smux free(oldenv, M_KENV); 36894936Smux } else { 36994936Smux /* We add the option if it wasn't found */ 37094936Smux for (i = 0; (cp = kenvp[i]) != NULL; i++) 37194936Smux ; 372148585Snetchild 373148585Snetchild /* Bounds checking */ 374148585Snetchild if (i < 0 || i >= KENV_SIZE) { 375148585Snetchild free(buf, M_KENV); 376160217Sscottl mtx_unlock(&kenv_lock); 377148585Snetchild return (-1); 378148585Snetchild } 379148585Snetchild 38094936Smux kenvp[i] = buf; 38194936Smux kenvp[i + 1] = NULL; 382160217Sscottl mtx_unlock(&kenv_lock); 38394936Smux } 38494959Smux return (0); 38594936Smux} 38694936Smux 38794936Smux/* 38894936Smux * Unset an environment variable string. 38994936Smux */ 39094936Smuxint 39194936Smuxunsetenv(const char *name) 39294936Smux{ 39394959Smux char *cp, *oldenv; 39494936Smux int i, j; 39594936Smux 39694936Smux KENV_CHECK; 39794936Smux 398160217Sscottl mtx_lock(&kenv_lock); 39994936Smux cp = _getenv_dynamic(name, &i); 40094936Smux if (cp != NULL) { 40194959Smux oldenv = kenvp[i]; 40294936Smux for (j = i + 1; kenvp[j] != NULL; j++) 40394936Smux kenvp[i++] = kenvp[j]; 40494936Smux kenvp[i] = NULL; 405160217Sscottl mtx_unlock(&kenv_lock); 40694959Smux free(oldenv, M_KENV); 40794936Smux return (0); 40894936Smux } 409160217Sscottl mtx_unlock(&kenv_lock); 41094936Smux return (-1); 41194936Smux} 41294936Smux 41394936Smux/* 41485385Sjhb * Return a string value from an environment variable. 41585385Sjhb */ 41685385Sjhbint 41785385Sjhbgetenv_string(const char *name, char *data, int size) 41885385Sjhb{ 41995839Speter char *tmp; 42085385Sjhb 42195839Speter tmp = getenv(name); 42295839Speter if (tmp != NULL) { 423105354Srobert strlcpy(data, tmp, size); 42495839Speter freeenv(tmp); 42595839Speter return (1); 42695839Speter } else 42795839Speter return (0); 42885385Sjhb} 42985385Sjhb 43085385Sjhb/* 43142706Smsmith * Return an integer value from an environment variable. 43242706Smsmith */ 43342706Smsmithint 43478247Spetergetenv_int(const char *name, int *data) 43542706Smsmith{ 43695839Speter quad_t tmp; 43795839Speter int rval; 43852947Smjacob 43995839Speter rval = getenv_quad(name, &tmp); 44095839Speter if (rval) 44195839Speter *data = (int) tmp; 44295839Speter return (rval); 44352947Smjacob} 44452947Smjacob 44552947Smjacob/* 446172612Sdes * Return an unsigned integer value from an environment variable. 447172612Sdes */ 448172612Sdesint 449172612Sdesgetenv_uint(const char *name, unsigned int *data) 450172612Sdes{ 451172612Sdes quad_t tmp; 452172612Sdes int rval; 453172612Sdes 454172612Sdes rval = getenv_quad(name, &tmp); 455172612Sdes if (rval) 456172612Sdes *data = (unsigned int) tmp; 457172612Sdes return (rval); 458172612Sdes} 459172612Sdes 460172612Sdes/* 461137099Sdes * Return a long value from an environment variable. 462137099Sdes */ 463172612Sdesint 464137099Sdesgetenv_long(const char *name, long *data) 465137099Sdes{ 466137099Sdes quad_t tmp; 467172612Sdes int rval; 468137099Sdes 469137099Sdes rval = getenv_quad(name, &tmp); 470137099Sdes if (rval) 471137099Sdes *data = (long) tmp; 472137099Sdes return (rval); 473137099Sdes} 474137099Sdes 475137099Sdes/* 476137099Sdes * Return an unsigned long value from an environment variable. 477137099Sdes */ 478172612Sdesint 479137099Sdesgetenv_ulong(const char *name, unsigned long *data) 480137099Sdes{ 481137099Sdes quad_t tmp; 482172612Sdes int rval; 483137099Sdes 484137099Sdes rval = getenv_quad(name, &tmp); 485137099Sdes if (rval) 486137099Sdes *data = (unsigned long) tmp; 487137099Sdes return (rval); 488137099Sdes} 489137099Sdes 490137099Sdes/* 49152947Smjacob * Return a quad_t value from an environment variable. 49252947Smjacob */ 49385385Sjhbint 49478247Spetergetenv_quad(const char *name, quad_t *data) 49552947Smjacob{ 49695839Speter char *value; 49795839Speter char *vtp; 49895839Speter quad_t iv; 49995839Speter 50095839Speter value = getenv(name); 50195839Speter if (value == NULL) 50295839Speter return (0); 50395839Speter iv = strtoq(value, &vtp, 0); 504143319Sdes if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { 505143319Sdes freeenv(value); 50695839Speter return (0); 507143319Sdes } 508143151Sdes switch (vtp[0]) { 509143151Sdes case 't': case 'T': 510143151Sdes iv *= 1024; 511143151Sdes case 'g': case 'G': 512143151Sdes iv *= 1024; 513143151Sdes case 'm': case 'M': 514143151Sdes iv *= 1024; 515143151Sdes case 'k': case 'K': 516143151Sdes iv *= 1024; 517143151Sdes case '\0': 518143151Sdes break; 519143151Sdes default: 520143319Sdes freeenv(value); 521143151Sdes return (0); 52295839Speter } 52395839Speter *data = iv; 524143319Sdes freeenv(value); 52595839Speter return (1); 52642706Smsmith} 52740090Smsmith 52883744Speter/* 52940090Smsmith * Find the next entry after the one which (cp) falls within, return a 53040090Smsmith * pointer to its start or NULL if there are no more. 53140090Smsmith */ 53240090Smsmithstatic char * 53340090Smsmithkernenv_next(char *cp) 53440090Smsmith{ 53595839Speter 53695839Speter if (cp != NULL) { 53795839Speter while (*cp != 0) 53895839Speter cp++; 53995839Speter cp++; 54095839Speter if (*cp == 0) 54195839Speter cp = NULL; 54295839Speter } 54395839Speter return (cp); 54440090Smsmith} 54540090Smsmith 54677900Spetervoid 54777900Spetertunable_int_init(void *data) 54877900Speter{ 54977900Speter struct tunable_int *d = (struct tunable_int *)data; 55077900Speter 55177900Speter TUNABLE_INT_FETCH(d->path, d->var); 55277900Speter} 55377900Speter 55477900Spetervoid 555137099Sdestunable_long_init(void *data) 556137099Sdes{ 557137099Sdes struct tunable_long *d = (struct tunable_long *)data; 558137099Sdes 559137099Sdes TUNABLE_LONG_FETCH(d->path, d->var); 560137099Sdes} 561137099Sdes 562137099Sdesvoid 563137099Sdestunable_ulong_init(void *data) 564137099Sdes{ 565137099Sdes struct tunable_ulong *d = (struct tunable_ulong *)data; 566137099Sdes 567137099Sdes TUNABLE_ULONG_FETCH(d->path, d->var); 568137099Sdes} 569137099Sdes 570137099Sdesvoid 571180661Spjdtunable_quad_init(void *data) 572180661Spjd{ 573180661Spjd struct tunable_quad *d = (struct tunable_quad *)data; 574180661Spjd 575180661Spjd TUNABLE_QUAD_FETCH(d->path, d->var); 576180661Spjd} 577180661Spjd 578180661Spjdvoid 57977900Spetertunable_str_init(void *data) 58077900Speter{ 58177900Speter struct tunable_str *d = (struct tunable_str *)data; 58277900Speter 58377900Speter TUNABLE_STR_FETCH(d->path, d->var, d->size); 58477900Speter} 585