xref: /freebsd/sys/kern/kern_environment.c (revision ee2ea5ceafed78a5bd9810beb9e3ca927180c226)
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$
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 <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/proc.h>
42 #include <sys/queue.h>
43 #include <sys/lock.h>
44 #include <sys/malloc.h>
45 #include <sys/mutex.h>
46 #include <sys/kernel.h>
47 #include <sys/sx.h>
48 #include <sys/systm.h>
49 #include <sys/sysent.h>
50 #include <sys/sysproto.h>
51 #include <sys/libkern.h>
52 #include <sys/kenv.h>
53 
54 MALLOC_DEFINE(M_KENV, "kenv", "kernel environment");
55 
56 #define KENV_SIZE	512	/* Maximum number of environment strings */
57 
58 /* pointer to the static environment */
59 char		*kern_envp;
60 static char	*kernenv_next(char *);
61 
62 /* dynamic environment variables */
63 char		**kenvp;
64 struct sx	kenv_lock;
65 
66 /*
67  * No need to protect this with a mutex
68  * since SYSINITS are single threaded.
69  */
70 int	dynamic_kenv = 0;
71 
72 #define KENV_CHECK	if (!dynamic_kenv) \
73 			    panic("%s: called before SI_SUB_KMEM", __func__)
74 
75 int
76 kenv(td, uap)
77 	struct thread *td;
78 	struct kenv_args /* {
79 		syscallarg(int) what;
80 		syscallarg(const char *) name;
81 		syscallarg(char *) value;
82 		syscallarg(int) len;
83 	} */ *uap;
84 {
85 	char *name, *value;
86 	size_t len, done;
87 	int error, i;
88 
89 	KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = 0"));
90 
91 	error = 0;
92 	if (SCARG(uap, what) == KENV_DUMP) {
93 		len = 0;
94 		/* Return the size if called with a NULL buffer */
95 		if (SCARG(uap, value) == NULL) {
96 			sx_slock(&kenv_lock);
97 			for (i = 0; kenvp[i] != NULL; i++)
98 				len += strlen(kenvp[i]) + 1;
99 			sx_sunlock(&kenv_lock);
100 			td->td_retval[0] = len;
101 			return (0);
102 		}
103 		done = 0;
104 		sx_slock(&kenv_lock);
105 		for (i = 0; kenvp[i] != NULL && done < SCARG(uap, len); i++) {
106 			len = min(strlen(kenvp[i]) + 1, SCARG(uap, len) - done);
107 			error = copyout(kenvp[i], SCARG(uap, value) + done,
108 			    len);
109 			if (error) {
110 				sx_sunlock(&kenv_lock);
111 				return (error);
112 			}
113 			done += len;
114 		}
115 		sx_sunlock(&kenv_lock);
116 		return (0);
117 	}
118 
119 	if ((SCARG(uap, what) == KENV_SET) ||
120 	    (SCARG(uap, what) == KENV_UNSET)) {
121 		error = suser(td);
122 		if (error)
123 			return (error);
124 	}
125 
126 	name = malloc(KENV_MNAMELEN, M_TEMP, M_WAITOK);
127 
128 	error = copyinstr(SCARG(uap, name), name, KENV_MNAMELEN, NULL);
129 	if (error)
130 		goto done;
131 
132 	switch (SCARG(uap, what)) {
133 	case KENV_GET:
134 		value = getenv(name);
135 		if (value == NULL) {
136 			error = ENOENT;
137 			goto done;
138 		}
139 		len = strlen(value) + 1;
140 		if (len > SCARG(uap, len))
141 			len = SCARG(uap, len);
142 		error = copyout(value, SCARG(uap, value), len);
143 		freeenv(value);
144 		if (error)
145 			goto done;
146 		td->td_retval[0] = len;
147 		break;
148 	case KENV_SET:
149 		len = SCARG(uap, len);
150 		if (len < 1) {
151 			error = EINVAL;
152 			goto done;
153 		}
154 		if (len > KENV_MVALLEN)
155 			len = KENV_MVALLEN;
156 		value = malloc(len, M_TEMP, M_WAITOK);
157 		error = copyinstr(SCARG(uap, value), value, len, NULL);
158 		if (error) {
159 			free(value, M_TEMP);
160 			goto done;
161 		}
162 		setenv(name, value);
163 		free(value, M_TEMP);
164 		break;
165 	case KENV_UNSET:
166 		error = unsetenv(name);
167 		if (error)
168 			error = ENOENT;
169 		break;
170 	default:
171 		error = EINVAL;
172 		break;
173 	}
174 done:
175 	free(name, M_TEMP);
176 	return (error);
177 }
178 
179 /*
180  * Setup the dynamic kernel environment.
181  */
182 static void
183 init_dynamic_kenv(void *data __unused)
184 {
185 	char *cp;
186 	int len, i;
187 
188 	kenvp = malloc(KENV_SIZE * sizeof(char *), M_KENV, M_WAITOK | M_ZERO);
189 	i = 0;
190 	for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
191 		len = strlen(cp) + 1;
192 		kenvp[i] = malloc(len, M_KENV, M_WAITOK);
193 		strcpy(kenvp[i++], cp);
194 	}
195 	kenvp[i] = NULL;
196 
197 	sx_init(&kenv_lock, "kernel environment");
198 	dynamic_kenv = 1;
199 }
200 SYSINIT(kenv, SI_SUB_KMEM, SI_ORDER_ANY, init_dynamic_kenv, NULL);
201 
202 void
203 freeenv(char *env)
204 {
205 
206 	if (dynamic_kenv)
207 		free(env, M_KENV);
208 }
209 
210 /*
211  * Internal functions for string lookup.
212  */
213 static char *
214 _getenv_dynamic(const char *name, int *idx)
215 {
216 	char *cp;
217 	int len, i;
218 
219 	sx_assert(&kenv_lock, SX_LOCKED);
220 	len = strlen(name);
221 	for (cp = kenvp[0], i = 0; cp != NULL; cp = kenvp[++i]) {
222 		if ((cp[len] == '=') &&
223 		    (strncmp(cp, name, len) == 0)) {
224 			if (idx != NULL)
225 				*idx = i;
226 			return (cp + len + 1);
227 		}
228 	}
229 	return (NULL);
230 }
231 
232 static char *
233 _getenv_static(const char *name)
234 {
235 	char *cp, *ep;
236 	int len;
237 
238 	for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
239 		for (ep = cp; (*ep != '=') && (*ep != 0); ep++)
240 			;
241 		if (*ep != '=')
242 			continue;
243 		len = ep - cp;
244 		ep++;
245 		if (!strncmp(name, cp, len) && name[len] == 0)
246 			return (ep);
247 	}
248 	return (NULL);
249 }
250 
251 /*
252  * Look up an environment variable by name.
253  * Return a pointer to the string if found.
254  * The pointer has to be freed with freeenv()
255  * after use.
256  */
257 char *
258 getenv(const char *name)
259 {
260 	char buf[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1];
261 	char *ret, *cp;
262 	int len;
263 
264 	if (dynamic_kenv) {
265 		sx_slock(&kenv_lock);
266 		cp = _getenv_dynamic(name, NULL);
267 		if (cp != NULL) {
268 			strcpy(buf, cp);
269 			sx_sunlock(&kenv_lock);
270 			len = strlen(buf) + 1;
271 			ret = malloc(len, M_KENV, M_WAITOK);
272 			strcpy(ret, buf);
273 		} else {
274 			sx_sunlock(&kenv_lock);
275 			ret = NULL;
276 		}
277 	} else
278 		ret = _getenv_static(name);
279 	return (ret);
280 }
281 
282 /*
283  * Test if an environment variable is defined.
284  */
285 int
286 testenv(const char *name)
287 {
288 	char *cp;
289 
290 	if (dynamic_kenv) {
291 		sx_slock(&kenv_lock);
292 		cp = _getenv_dynamic(name, NULL);
293 		sx_sunlock(&kenv_lock);
294 	} else
295 		cp = _getenv_static(name);
296 	if (cp != NULL)
297 		return (1);
298 	return (0);
299 }
300 
301 /*
302  * Set an environment variable by name.
303  */
304 int
305 setenv(const char *name, const char *value)
306 {
307 	char *buf, *cp, *oldenv;
308 	int namelen, vallen, i;
309 
310 	KENV_CHECK;
311 
312 	namelen = strlen(name) + 1;
313 	if (namelen > KENV_MNAMELEN)
314 		return (-1);
315 	vallen = strlen(value) + 1;
316 	if (vallen > KENV_MVALLEN)
317 		return (-1);
318 	buf = malloc(namelen + vallen, M_KENV, M_WAITOK);
319 	sprintf(buf, "%s=%s", name, value);
320 
321 	sx_xlock(&kenv_lock);
322 	cp = _getenv_dynamic(name, &i);
323 	if (cp != NULL) {
324 		oldenv = kenvp[i];
325 		kenvp[i] = buf;
326 		sx_xunlock(&kenv_lock);
327 		free(oldenv, M_KENV);
328 	} else {
329 		/* We add the option if it wasn't found */
330 		for (i = 0; (cp = kenvp[i]) != NULL; i++)
331 			;
332 		kenvp[i] = buf;
333 		kenvp[i + 1] = NULL;
334 		sx_xunlock(&kenv_lock);
335 	}
336 	return (0);
337 }
338 
339 /*
340  * Unset an environment variable string.
341  */
342 int
343 unsetenv(const char *name)
344 {
345 	char *cp, *oldenv;
346 	int i, j;
347 
348 	KENV_CHECK;
349 
350 	sx_xlock(&kenv_lock);
351 	cp = _getenv_dynamic(name, &i);
352 	if (cp != NULL) {
353 		oldenv = kenvp[i];
354 		for (j = i + 1; kenvp[j] != NULL; j++)
355 			kenvp[i++] = kenvp[j];
356 		kenvp[i] = NULL;
357 		sx_xunlock(&kenv_lock);
358 		free(oldenv, M_KENV);
359 		return (0);
360 	}
361 	sx_xunlock(&kenv_lock);
362 	return (-1);
363 }
364 
365 /*
366  * Return a string value from an environment variable.
367  */
368 int
369 getenv_string(const char *name, char *data, int size)
370 {
371 	char *tmp;
372 
373 	tmp = getenv(name);
374 	if (tmp != NULL) {
375 		strncpy(data, tmp, size);
376 		freeenv(tmp);
377 		data[size - 1] = 0;
378 		return (1);
379 	} else
380 		return (0);
381 }
382 
383 /*
384  * Return an integer value from an environment variable.
385  */
386 int
387 getenv_int(const char *name, int *data)
388 {
389 	quad_t tmp;
390 	int rval;
391 
392 	rval = getenv_quad(name, &tmp);
393 	if (rval)
394 		*data = (int) tmp;
395 	return (rval);
396 }
397 
398 /*
399  * Return a quad_t value from an environment variable.
400  */
401 int
402 getenv_quad(const char *name, quad_t *data)
403 {
404 	char	*value;
405 	char	*vtp;
406 	quad_t	iv;
407 
408 	value = getenv(name);
409 	if (value == NULL)
410 		return (0);
411 	iv = strtoq(value, &vtp, 0);
412 	if ((vtp == value) || (*vtp != '\0')) {
413 		freeenv(value);
414 		return (0);
415 	}
416 	freeenv(value);
417 	*data = iv;
418 	return (1);
419 }
420 
421 /*
422  * Find the next entry after the one which (cp) falls within, return a
423  * pointer to its start or NULL if there are no more.
424  */
425 static char *
426 kernenv_next(char *cp)
427 {
428 
429 	if (cp != NULL) {
430 		while (*cp != 0)
431 			cp++;
432 		cp++;
433 		if (*cp == 0)
434 			cp = NULL;
435 	}
436 	return (cp);
437 }
438 
439 void
440 tunable_int_init(void *data)
441 {
442 	struct tunable_int *d = (struct tunable_int *)data;
443 
444 	TUNABLE_INT_FETCH(d->path, d->var);
445 }
446 
447 void
448 tunable_quad_init(void *data)
449 {
450 	struct tunable_quad *d = (struct tunable_quad *)data;
451 
452 	TUNABLE_QUAD_FETCH(d->path, d->var);
453 }
454 
455 void
456 tunable_str_init(void *data)
457 {
458 	struct tunable_str *d = (struct tunable_str *)data;
459 
460 	TUNABLE_STR_FETCH(d->path, d->var, d->size);
461 }
462