kern_environment.c revision 107849
1/*-
2 * Copyright (c) 1998 Michael Smith
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/kern/kern_environment.c 107849 2002-12-14 01:56:26Z alfred $
27 */
28
29/*
30 * The unified bootloader passes us a pointer to a preserved copy of
31 * bootstrap/kernel environment variables.  We convert them to a
32 * dynamic array of strings later when the VM subsystem is up.
33 *
34 * We make these available through the kenv(2) syscall for userland
35 * and through getenv()/freeenv() setenv() unsetenv() testenv() for
36 * the kernel.
37 */
38
39#include "opt_mac.h"
40
41#include <sys/types.h>
42#include <sys/param.h>
43#include <sys/proc.h>
44#include <sys/queue.h>
45#include <sys/lock.h>
46#include <sys/mac.h>
47#include <sys/malloc.h>
48#include <sys/mutex.h>
49#include <sys/kernel.h>
50#include <sys/sx.h>
51#include <sys/systm.h>
52#include <sys/sysent.h>
53#include <sys/sysproto.h>
54#include <sys/libkern.h>
55#include <sys/kenv.h>
56
57MALLOC_DEFINE(M_KENV, "kenv", "kernel environment");
58
59#define KENV_SIZE	512	/* Maximum number of environment strings */
60
61/* pointer to the static environment */
62char		*kern_envp;
63static char	*kernenv_next(char *);
64
65/* dynamic environment variables */
66char		**kenvp;
67struct sx	kenv_lock;
68
69/*
70 * No need to protect this with a mutex
71 * since SYSINITS are single threaded.
72 */
73int	dynamic_kenv = 0;
74
75#define KENV_CHECK	if (!dynamic_kenv) \
76			    panic("%s: called before SI_SUB_KMEM", __func__)
77
78int
79kenv(td, uap)
80	struct thread *td;
81	struct kenv_args /* {
82		syscallarg(int) what;
83		syscallarg(const char *) name;
84		syscallarg(char *) value;
85		syscallarg(int) len;
86	} */ *uap;
87{
88	char *name, *value;
89	size_t len, done;
90	int error, i;
91
92	KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = 0"));
93
94	error = 0;
95	if (uap->what == KENV_DUMP) {
96#ifdef MAC
97		error = mac_check_kenv_dump(td->td_ucred);
98		if (error)
99			return (error);
100#endif
101		len = 0;
102		/* Return the size if called with a NULL buffer */
103		if (uap->value == NULL) {
104			sx_slock(&kenv_lock);
105			for (i = 0; kenvp[i] != NULL; i++)
106				len += strlen(kenvp[i]) + 1;
107			sx_sunlock(&kenv_lock);
108			td->td_retval[0] = len;
109			return (0);
110		}
111		done = 0;
112		sx_slock(&kenv_lock);
113		for (i = 0; kenvp[i] != NULL && done < uap->len; i++) {
114			len = min(strlen(kenvp[i]) + 1, uap->len - done);
115			error = copyout(kenvp[i], uap->value + done,
116			    len);
117			if (error) {
118				sx_sunlock(&kenv_lock);
119				return (error);
120			}
121			done += len;
122		}
123		sx_sunlock(&kenv_lock);
124		return (0);
125	}
126
127	if ((uap->what == KENV_SET) ||
128	    (uap->what == KENV_UNSET)) {
129		error = suser(td);
130		if (error)
131			return (error);
132	}
133
134	name = malloc(KENV_MNAMELEN, M_TEMP, M_WAITOK);
135
136	error = copyinstr(uap->name, name, KENV_MNAMELEN, NULL);
137	if (error)
138		goto done;
139
140	switch (uap->what) {
141	case KENV_GET:
142#ifdef MAC
143		error = mac_check_kenv_get(td->td_ucred, name);
144		if (error)
145			goto done;
146#endif
147		value = getenv(name);
148		if (value == NULL) {
149			error = ENOENT;
150			goto done;
151		}
152		len = strlen(value) + 1;
153		if (len > uap->len)
154			len = uap->len;
155		error = copyout(value, uap->value, len);
156		freeenv(value);
157		if (error)
158			goto done;
159		td->td_retval[0] = len;
160		break;
161	case KENV_SET:
162		len = uap->len;
163		if (len < 1) {
164			error = EINVAL;
165			goto done;
166		}
167		if (len > KENV_MVALLEN)
168			len = KENV_MVALLEN;
169		value = malloc(len, M_TEMP, M_WAITOK);
170		error = copyinstr(uap->value, value, len, NULL);
171		if (error) {
172			free(value, M_TEMP);
173			goto done;
174		}
175#ifdef MAC
176		error = mac_check_kenv_set(td->td_ucred, name, value);
177		if (error == 0)
178#endif
179			setenv(name, value);
180		free(value, M_TEMP);
181		break;
182	case KENV_UNSET:
183#ifdef MAC
184		error = mac_check_kenv_unset(td->td_ucred, name);
185		if (error)
186			goto done;
187#endif
188		error = unsetenv(name);
189		if (error)
190			error = ENOENT;
191		break;
192	default:
193		error = EINVAL;
194		break;
195	}
196done:
197	free(name, M_TEMP);
198	return (error);
199}
200
201/*
202 * Setup the dynamic kernel environment.
203 */
204static void
205init_dynamic_kenv(void *data __unused)
206{
207	char *cp;
208	int len, i;
209
210	kenvp = malloc(KENV_SIZE * sizeof(char *), M_KENV, M_WAITOK | M_ZERO);
211	i = 0;
212	for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
213		len = strlen(cp) + 1;
214		kenvp[i] = malloc(len, M_KENV, M_WAITOK);
215		strcpy(kenvp[i++], cp);
216	}
217	kenvp[i] = NULL;
218
219	sx_init(&kenv_lock, "kernel environment");
220	dynamic_kenv = 1;
221}
222SYSINIT(kenv, SI_SUB_KMEM, SI_ORDER_ANY, init_dynamic_kenv, NULL);
223
224void
225freeenv(char *env)
226{
227
228	if (dynamic_kenv)
229		free(env, M_KENV);
230}
231
232/*
233 * Internal functions for string lookup.
234 */
235static char *
236_getenv_dynamic(const char *name, int *idx)
237{
238	char *cp;
239	int len, i;
240
241	sx_assert(&kenv_lock, SX_LOCKED);
242	len = strlen(name);
243	for (cp = kenvp[0], i = 0; cp != NULL; cp = kenvp[++i]) {
244		if ((cp[len] == '=') &&
245		    (strncmp(cp, name, len) == 0)) {
246			if (idx != NULL)
247				*idx = i;
248			return (cp + len + 1);
249		}
250	}
251	return (NULL);
252}
253
254static char *
255_getenv_static(const char *name)
256{
257	char *cp, *ep;
258	int len;
259
260	for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
261		for (ep = cp; (*ep != '=') && (*ep != 0); ep++)
262			;
263		if (*ep != '=')
264			continue;
265		len = ep - cp;
266		ep++;
267		if (!strncmp(name, cp, len) && name[len] == 0)
268			return (ep);
269	}
270	return (NULL);
271}
272
273/*
274 * Look up an environment variable by name.
275 * Return a pointer to the string if found.
276 * The pointer has to be freed with freeenv()
277 * after use.
278 */
279char *
280getenv(const char *name)
281{
282	char buf[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1];
283	char *ret, *cp;
284	int len;
285
286	if (dynamic_kenv) {
287		sx_slock(&kenv_lock);
288		cp = _getenv_dynamic(name, NULL);
289		if (cp != NULL) {
290			strcpy(buf, cp);
291			sx_sunlock(&kenv_lock);
292			len = strlen(buf) + 1;
293			ret = malloc(len, M_KENV, M_WAITOK);
294			strcpy(ret, buf);
295		} else {
296			sx_sunlock(&kenv_lock);
297			ret = NULL;
298		}
299	} else
300		ret = _getenv_static(name);
301	return (ret);
302}
303
304/*
305 * Test if an environment variable is defined.
306 */
307int
308testenv(const char *name)
309{
310	char *cp;
311
312	if (dynamic_kenv) {
313		sx_slock(&kenv_lock);
314		cp = _getenv_dynamic(name, NULL);
315		sx_sunlock(&kenv_lock);
316	} else
317		cp = _getenv_static(name);
318	if (cp != NULL)
319		return (1);
320	return (0);
321}
322
323/*
324 * Set an environment variable by name.
325 */
326int
327setenv(const char *name, const char *value)
328{
329	char *buf, *cp, *oldenv;
330	int namelen, vallen, i;
331
332	KENV_CHECK;
333
334	namelen = strlen(name) + 1;
335	if (namelen > KENV_MNAMELEN)
336		return (-1);
337	vallen = strlen(value) + 1;
338	if (vallen > KENV_MVALLEN)
339		return (-1);
340	buf = malloc(namelen + vallen, M_KENV, M_WAITOK);
341	sprintf(buf, "%s=%s", name, value);
342
343	sx_xlock(&kenv_lock);
344	cp = _getenv_dynamic(name, &i);
345	if (cp != NULL) {
346		oldenv = kenvp[i];
347		kenvp[i] = buf;
348		sx_xunlock(&kenv_lock);
349		free(oldenv, M_KENV);
350	} else {
351		/* We add the option if it wasn't found */
352		for (i = 0; (cp = kenvp[i]) != NULL; i++)
353			;
354		kenvp[i] = buf;
355		kenvp[i + 1] = NULL;
356		sx_xunlock(&kenv_lock);
357	}
358	return (0);
359}
360
361/*
362 * Unset an environment variable string.
363 */
364int
365unsetenv(const char *name)
366{
367	char *cp, *oldenv;
368	int i, j;
369
370	KENV_CHECK;
371
372	sx_xlock(&kenv_lock);
373	cp = _getenv_dynamic(name, &i);
374	if (cp != NULL) {
375		oldenv = kenvp[i];
376		for (j = i + 1; kenvp[j] != NULL; j++)
377			kenvp[i++] = kenvp[j];
378		kenvp[i] = NULL;
379		sx_xunlock(&kenv_lock);
380		free(oldenv, M_KENV);
381		return (0);
382	}
383	sx_xunlock(&kenv_lock);
384	return (-1);
385}
386
387/*
388 * Return a string value from an environment variable.
389 */
390int
391getenv_string(const char *name, char *data, int size)
392{
393	char *tmp;
394
395	tmp = getenv(name);
396	if (tmp != NULL) {
397		strlcpy(data, tmp, size);
398		freeenv(tmp);
399		return (1);
400	} else
401		return (0);
402}
403
404/*
405 * Return an integer value from an environment variable.
406 */
407int
408getenv_int(const char *name, int *data)
409{
410	quad_t tmp;
411	int rval;
412
413	rval = getenv_quad(name, &tmp);
414	if (rval)
415		*data = (int) tmp;
416	return (rval);
417}
418
419/*
420 * Return a quad_t value from an environment variable.
421 */
422int
423getenv_quad(const char *name, quad_t *data)
424{
425	char	*value;
426	char	*vtp;
427	quad_t	iv;
428
429	value = getenv(name);
430	if (value == NULL)
431		return (0);
432	iv = strtoq(value, &vtp, 0);
433	if ((vtp == value) || (*vtp != '\0')) {
434		freeenv(value);
435		return (0);
436	}
437	freeenv(value);
438	*data = iv;
439	return (1);
440}
441
442/*
443 * Find the next entry after the one which (cp) falls within, return a
444 * pointer to its start or NULL if there are no more.
445 */
446static char *
447kernenv_next(char *cp)
448{
449
450	if (cp != NULL) {
451		while (*cp != 0)
452			cp++;
453		cp++;
454		if (*cp == 0)
455			cp = NULL;
456	}
457	return (cp);
458}
459
460void
461tunable_int_init(void *data)
462{
463	struct tunable_int *d = (struct tunable_int *)data;
464
465	TUNABLE_INT_FETCH(d->path, d->var);
466}
467
468void
469tunable_quad_init(void *data)
470{
471	struct tunable_quad *d = (struct tunable_quad *)data;
472
473	TUNABLE_QUAD_FETCH(d->path, d->var);
474}
475
476void
477tunable_str_init(void *data)
478{
479	struct tunable_str *d = (struct tunable_str *)data;
480
481	TUNABLE_STR_FETCH(d->path, d->var, d->size);
482}
483