xref: /freebsd/sys/kern/kern_environment.c (revision 66df505066f51e6d8411b966765d828817f88971)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 1998 Michael Smith
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
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 kern_getenv()/freeenv() kern_setenv() kern_unsetenv() testenv() for
36  * the kernel.
37  */
38 
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41 
42 #include <sys/param.h>
43 #include <sys/proc.h>
44 #include <sys/queue.h>
45 #include <sys/lock.h>
46 #include <sys/malloc.h>
47 #include <sys/mutex.h>
48 #include <sys/priv.h>
49 #include <sys/kenv.h>
50 #include <sys/kernel.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 #include <sys/limits.h>
57 
58 #include <security/mac/mac_framework.h>
59 
60 static char *_getenv_dynamic_locked(const char *name, int *idx);
61 static char *_getenv_dynamic(const char *name, int *idx);
62 
63 static char *kenv_acquire(const char *name);
64 static void kenv_release(const char *buf);
65 
66 static MALLOC_DEFINE(M_KENV, "kenv", "kernel environment");
67 
68 #define KENV_SIZE	512	/* Maximum number of environment strings */
69 
70 static uma_zone_t kenv_zone;
71 static int	kenv_mvallen = KENV_MVALLEN;
72 
73 /* pointer to the config-generated static environment */
74 char		*kern_envp;
75 
76 /* pointer to the md-static environment */
77 char		*md_envp;
78 static int	md_env_len;
79 static int	md_env_pos;
80 
81 static char	*kernenv_next(char *);
82 
83 /* dynamic environment variables */
84 char		**kenvp;
85 struct mtx	kenv_lock;
86 
87 /*
88  * No need to protect this with a mutex since SYSINITS are single threaded.
89  */
90 bool	dynamic_kenv;
91 
92 #define KENV_CHECK	if (!dynamic_kenv) \
93 			    panic("%s: called before SI_SUB_KMEM", __func__)
94 
95 static int
96 kenv_dump(struct thread *td, char **envp, int what, char *value, int len)
97 {
98 	char *buffer, *senv;
99 	size_t done, needed, buflen;
100 	int error;
101 
102 	error = 0;
103 	buffer = NULL;
104 	done = needed = 0;
105 
106 	MPASS(what == KENV_DUMP || what == KENV_DUMP_LOADER ||
107 	    what == KENV_DUMP_STATIC);
108 
109 	/*
110 	 * For non-dynamic kernel environment, we pass in either md_envp or
111 	 * kern_envp and we must traverse with kernenv_next().  This shuffling
112 	 * of pointers simplifies the below loop by only differing in how envp
113 	 * is modified.
114 	 */
115 	if (what != KENV_DUMP) {
116 		senv = (char *)envp;
117 		envp = &senv;
118 	}
119 
120 	buflen = len;
121 	if (buflen > KENV_SIZE * (KENV_MNAMELEN + kenv_mvallen + 2))
122 		buflen = KENV_SIZE * (KENV_MNAMELEN +
123 		    kenv_mvallen + 2);
124 	if (len > 0 && value != NULL)
125 		buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO);
126 
127 	/* Only take the lock for the dynamic kenv. */
128 	if (what == KENV_DUMP)
129 		mtx_lock(&kenv_lock);
130 	while (*envp != NULL) {
131 		len = strlen(*envp) + 1;
132 		needed += len;
133 		len = min(len, buflen - done);
134 		/*
135 		 * If called with a NULL or insufficiently large
136 		 * buffer, just keep computing the required size.
137 		 */
138 		if (value != NULL && buffer != NULL && len > 0) {
139 			bcopy(*envp, buffer + done, len);
140 			done += len;
141 		}
142 
143 		/* Advance the pointer depending on the kenv format. */
144 		if (what == KENV_DUMP)
145 			envp++;
146 		else
147 			senv = kernenv_next(senv);
148 	}
149 	if (what == KENV_DUMP)
150 		mtx_unlock(&kenv_lock);
151 	if (buffer != NULL) {
152 		error = copyout(buffer, value, done);
153 		free(buffer, M_TEMP);
154 	}
155 	td->td_retval[0] = ((done == needed) ? 0 : needed);
156 	return (error);
157 }
158 
159 int
160 sys_kenv(struct thread *td, struct kenv_args *uap)
161 {
162 	char *name, *value;
163 	size_t len;
164 	int error;
165 
166 	KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = false"));
167 
168 	error = 0;
169 
170 	switch (uap->what) {
171 	case KENV_DUMP:
172 #ifdef MAC
173 		error = mac_kenv_check_dump(td->td_ucred);
174 		if (error)
175 			return (error);
176 #endif
177 		return (kenv_dump(td, kenvp, uap->what, uap->value, uap->len));
178 	case KENV_DUMP_LOADER:
179 	case KENV_DUMP_STATIC:
180 #ifdef MAC
181 		error = mac_kenv_check_dump(td->td_ucred);
182 		if (error)
183 			return (error);
184 #endif
185 #ifdef PRESERVE_EARLY_KENV
186 		return (kenv_dump(td,
187 		    uap->what == KENV_DUMP_LOADER ? (char **)md_envp :
188 		    (char **)kern_envp, uap->what, uap->value, uap->len));
189 #else
190 		return (ENOENT);
191 #endif
192 	case KENV_SET:
193 		error = priv_check(td, PRIV_KENV_SET);
194 		if (error)
195 			return (error);
196 		break;
197 
198 	case KENV_UNSET:
199 		error = priv_check(td, PRIV_KENV_UNSET);
200 		if (error)
201 			return (error);
202 		break;
203 	}
204 
205 	name = malloc(KENV_MNAMELEN + 1, M_TEMP, M_WAITOK);
206 
207 	error = copyinstr(uap->name, name, KENV_MNAMELEN + 1, NULL);
208 	if (error)
209 		goto done;
210 
211 	switch (uap->what) {
212 	case KENV_GET:
213 #ifdef MAC
214 		error = mac_kenv_check_get(td->td_ucred, name);
215 		if (error)
216 			goto done;
217 #endif
218 		value = kern_getenv(name);
219 		if (value == NULL) {
220 			error = ENOENT;
221 			goto done;
222 		}
223 		len = strlen(value) + 1;
224 		if (len > uap->len)
225 			len = uap->len;
226 		error = copyout(value, uap->value, len);
227 		freeenv(value);
228 		if (error)
229 			goto done;
230 		td->td_retval[0] = len;
231 		break;
232 	case KENV_SET:
233 		len = uap->len;
234 		if (len < 1) {
235 			error = EINVAL;
236 			goto done;
237 		}
238 		if (len > kenv_mvallen + 1)
239 			len = kenv_mvallen + 1;
240 		value = malloc(len, M_TEMP, M_WAITOK);
241 		error = copyinstr(uap->value, value, len, NULL);
242 		if (error) {
243 			free(value, M_TEMP);
244 			goto done;
245 		}
246 #ifdef MAC
247 		error = mac_kenv_check_set(td->td_ucred, name, value);
248 		if (error == 0)
249 #endif
250 			kern_setenv(name, value);
251 		free(value, M_TEMP);
252 		break;
253 	case KENV_UNSET:
254 #ifdef MAC
255 		error = mac_kenv_check_unset(td->td_ucred, name);
256 		if (error)
257 			goto done;
258 #endif
259 		error = kern_unsetenv(name);
260 		if (error)
261 			error = ENOENT;
262 		break;
263 	default:
264 		error = EINVAL;
265 		break;
266 	}
267 done:
268 	free(name, M_TEMP);
269 	return (error);
270 }
271 
272 /*
273  * Populate the initial kernel environment.
274  *
275  * This is called very early in MD startup, either to provide a copy of the
276  * environment obtained from a boot loader, or to provide an empty buffer into
277  * which MD code can store an initial environment using kern_setenv() calls.
278  *
279  * kern_envp is set to the static_env generated by config(8).  This implements
280  * the env keyword described in config(5).
281  *
282  * If len is non-zero, the caller is providing an empty buffer.  The caller will
283  * subsequently use kern_setenv() to add up to len bytes of initial environment
284  * before the dynamic environment is available.
285  *
286  * If len is zero, the caller is providing a pre-loaded buffer containing
287  * environment strings.  Additional strings cannot be added until the dynamic
288  * environment is available.  The memory pointed to must remain stable at least
289  * until sysinit runs init_dynamic_kenv() and preferably until after SI_SUB_KMEM
290  * is finished so that subr_hints routines may continue to use it until the
291  * environments have been fully merged at the end of the pass.  If no initial
292  * environment is available from the boot loader, passing a NULL pointer allows
293  * the static_env to be installed if it is configured.  In this case, any call
294  * to kern_setenv() prior to the setup of the dynamic environment will result in
295  * a panic.
296  */
297 void
298 init_static_kenv(char *buf, size_t len)
299 {
300 
301 	KASSERT(!dynamic_kenv, ("kenv: dynamic_kenv already initialized"));
302 	/*
303 	 * Suitably sized means it must be able to hold at least one empty
304 	 * variable, otherwise things go belly up if a kern_getenv call is
305 	 * made without a prior call to kern_setenv as we have a malformed
306 	 * environment.
307 	 */
308 	KASSERT(len == 0 || len >= 2,
309 	    ("kenv: static env must be initialized or suitably sized"));
310 	KASSERT(len == 0 || (*buf == '\0' && *(buf + 1) == '\0'),
311 	    ("kenv: sized buffer must be initially empty"));
312 
313 	/*
314 	 * We may be called twice, with the second call needed to relocate
315 	 * md_envp after enabling paging.  md_envp is then garbage if it is
316 	 * not null and the relocation will move it.  Discard it so as to
317 	 * not crash using its old value in our first call to kern_getenv().
318 	 *
319 	 * The second call gives the same environment as the first except
320 	 * in silly configurations where the static env disables itself.
321 	 *
322 	 * Other env calls don't handle possibly-garbage pointers, so must
323 	 * not be made between enabling paging and calling here.
324 	 */
325 	md_envp = NULL;
326 	md_env_len = 0;
327 	md_env_pos = 0;
328 
329 	/*
330 	 * Give the static environment a chance to disable the loader(8)
331 	 * environment first.  This is done with loader_env.disabled=1.
332 	 *
333 	 * static_env and static_hints may both be disabled, but in slightly
334 	 * different ways.  For static_env, we just don't setup kern_envp and
335 	 * it's as if a static env wasn't even provided.  For static_hints,
336 	 * we effectively zero out the buffer to stop the rest of the kernel
337 	 * from being able to use it.
338 	 *
339 	 * We're intentionally setting this up so that static_hints.disabled may
340 	 * be specified in either the MD env or the static env. This keeps us
341 	 * consistent in our new world view.
342 	 *
343 	 * As a warning, the static environment may not be disabled in any way
344 	 * if the static environment has disabled the loader environment.
345 	 */
346 	kern_envp = static_env;
347 	if (!getenv_is_true("loader_env.disabled")) {
348 		md_envp = buf;
349 		md_env_len = len;
350 		md_env_pos = 0;
351 
352 		if (getenv_is_true("static_env.disabled")) {
353 			kern_envp[0] = '\0';
354 			kern_envp[1] = '\0';
355 		}
356 	}
357 	if (getenv_is_true("static_hints.disabled")) {
358 		static_hints[0] = '\0';
359 		static_hints[1] = '\0';
360 	}
361 }
362 
363 static void
364 init_dynamic_kenv_from(char *init_env, int *curpos)
365 {
366 	char *cp, *cpnext, *eqpos, *found;
367 	size_t len;
368 	int i;
369 
370 	if (init_env && *init_env != '\0') {
371 		found = NULL;
372 		i = *curpos;
373 		for (cp = init_env; cp != NULL; cp = cpnext) {
374 			cpnext = kernenv_next(cp);
375 			len = strlen(cp) + 1;
376 			if (len > KENV_MNAMELEN + 1 + kenv_mvallen + 1) {
377 				printf(
378 				"WARNING: too long kenv string, ignoring %s\n",
379 				    cp);
380 				goto sanitize;
381 			}
382 			eqpos = strchr(cp, '=');
383 			if (eqpos == NULL) {
384 				printf(
385 				"WARNING: malformed static env value, ignoring %s\n",
386 				    cp);
387 				goto sanitize;
388 			}
389 			*eqpos = 0;
390 			/*
391 			 * De-dupe the environment as we go.  We don't add the
392 			 * duplicated assignments because config(8) will flip
393 			 * the order of the static environment around to make
394 			 * kernel processing match the order of specification
395 			 * in the kernel config.
396 			 */
397 			found = _getenv_dynamic_locked(cp, NULL);
398 			*eqpos = '=';
399 			if (found != NULL)
400 				goto sanitize;
401 			if (i > KENV_SIZE) {
402 				printf(
403 				"WARNING: too many kenv strings, ignoring %s\n",
404 				    cp);
405 				goto sanitize;
406 			}
407 
408 			kenvp[i] = malloc(len, M_KENV, M_WAITOK);
409 			strcpy(kenvp[i++], cp);
410 sanitize:
411 #ifdef PRESERVE_EARLY_KENV
412 			continue;
413 #else
414 			explicit_bzero(cp, len - 1);
415 #endif
416 		}
417 		*curpos = i;
418 	}
419 }
420 
421 /*
422  * Setup the dynamic kernel environment.
423  */
424 static void
425 init_dynamic_kenv(void *data __unused)
426 {
427 	int dynamic_envpos;
428 	int size;
429 
430 	TUNABLE_INT_FETCH("kenv_mvallen", &kenv_mvallen);
431 	size = KENV_MNAMELEN + 1 + kenv_mvallen + 1;
432 
433 	kenv_zone = uma_zcreate("kenv", size, NULL, NULL, NULL, NULL,
434 	    UMA_ALIGN_PTR, 0);
435 
436 	kenvp = malloc((KENV_SIZE + 1) * sizeof(char *), M_KENV,
437 		M_WAITOK | M_ZERO);
438 
439 	dynamic_envpos = 0;
440 	init_dynamic_kenv_from(md_envp, &dynamic_envpos);
441 	init_dynamic_kenv_from(kern_envp, &dynamic_envpos);
442 	kenvp[dynamic_envpos] = NULL;
443 
444 	mtx_init(&kenv_lock, "kernel environment", NULL, MTX_DEF);
445 	dynamic_kenv = true;
446 }
447 SYSINIT(kenv, SI_SUB_KMEM + 1, SI_ORDER_FIRST, init_dynamic_kenv, NULL);
448 
449 void
450 freeenv(char *env)
451 {
452 
453 	if (dynamic_kenv && env != NULL) {
454 		explicit_bzero(env, strlen(env));
455 		uma_zfree(kenv_zone, env);
456 	}
457 }
458 
459 /*
460  * Internal functions for string lookup.
461  */
462 static char *
463 _getenv_dynamic_locked(const char *name, int *idx)
464 {
465 	char *cp;
466 	int len, i;
467 
468 	len = strlen(name);
469 	for (cp = kenvp[0], i = 0; cp != NULL; cp = kenvp[++i]) {
470 		if ((strncmp(cp, name, len) == 0) &&
471 		    (cp[len] == '=')) {
472 			if (idx != NULL)
473 				*idx = i;
474 			return (cp + len + 1);
475 		}
476 	}
477 	return (NULL);
478 }
479 
480 static char *
481 _getenv_dynamic(const char *name, int *idx)
482 {
483 
484 	mtx_assert(&kenv_lock, MA_OWNED);
485 	return (_getenv_dynamic_locked(name, idx));
486 }
487 
488 static char *
489 _getenv_static_from(char *chkenv, const char *name)
490 {
491 	char *cp, *ep;
492 	int len;
493 
494 	for (cp = chkenv; cp != NULL; cp = kernenv_next(cp)) {
495 		for (ep = cp; (*ep != '=') && (*ep != 0); ep++)
496 			;
497 		if (*ep != '=')
498 			continue;
499 		len = ep - cp;
500 		ep++;
501 		if (!strncmp(name, cp, len) && name[len] == 0)
502 			return (ep);
503 	}
504 	return (NULL);
505 }
506 
507 static char *
508 _getenv_static(const char *name)
509 {
510 	char *val;
511 
512 	val = _getenv_static_from(md_envp, name);
513 	if (val != NULL)
514 		return (val);
515 	val = _getenv_static_from(kern_envp, name);
516 	if (val != NULL)
517 		return (val);
518 	return (NULL);
519 }
520 
521 /*
522  * Look up an environment variable by name.
523  * Return a pointer to the string if found.
524  * The pointer has to be freed with freeenv()
525  * after use.
526  */
527 char *
528 kern_getenv(const char *name)
529 {
530 	char *cp, *ret;
531 	int len;
532 
533 	if (dynamic_kenv) {
534 		len = KENV_MNAMELEN + 1 + kenv_mvallen + 1;
535 		ret = uma_zalloc(kenv_zone, M_WAITOK | M_ZERO);
536 		mtx_lock(&kenv_lock);
537 		cp = _getenv_dynamic(name, NULL);
538 		if (cp != NULL)
539 			strlcpy(ret, cp, len);
540 		mtx_unlock(&kenv_lock);
541 		if (cp == NULL) {
542 			uma_zfree(kenv_zone, ret);
543 			ret = NULL;
544 		}
545 	} else
546 		ret = _getenv_static(name);
547 
548 	return (ret);
549 }
550 
551 /*
552  * Test if an environment variable is defined.
553  */
554 int
555 testenv(const char *name)
556 {
557 	char *cp;
558 
559 	cp = kenv_acquire(name);
560 	kenv_release(cp);
561 
562 	if (cp != NULL)
563 		return (1);
564 	return (0);
565 }
566 
567 /*
568  * Set an environment variable in the MD-static environment.  This cannot
569  * feasibly be done on config(8)-generated static environments as they don't
570  * generally include space for extra variables.
571  */
572 static int
573 setenv_static(const char *name, const char *value)
574 {
575 	int len;
576 
577 	if (md_env_pos >= md_env_len)
578 		return (-1);
579 
580 	/* Check space for x=y and two nuls */
581 	len = strlen(name) + strlen(value);
582 	if (len + 3 < md_env_len - md_env_pos) {
583 		len = sprintf(&md_envp[md_env_pos], "%s=%s", name, value);
584 		md_env_pos += len+1;
585 		md_envp[md_env_pos] = '\0';
586 		return (0);
587 	} else
588 		return (-1);
589 
590 }
591 
592 /*
593  * Set an environment variable by name.
594  */
595 int
596 kern_setenv(const char *name, const char *value)
597 {
598 	char *buf, *cp, *oldenv;
599 	int namelen, vallen, i;
600 
601 	if (!dynamic_kenv && md_env_len > 0)
602 		return (setenv_static(name, value));
603 
604 	KENV_CHECK;
605 
606 	namelen = strlen(name) + 1;
607 	if (namelen > KENV_MNAMELEN + 1)
608 		return (-1);
609 	vallen = strlen(value) + 1;
610 	if (vallen > kenv_mvallen + 1)
611 		return (-1);
612 	buf = malloc(namelen + vallen, M_KENV, M_WAITOK);
613 	sprintf(buf, "%s=%s", name, value);
614 
615 	mtx_lock(&kenv_lock);
616 	cp = _getenv_dynamic(name, &i);
617 	if (cp != NULL) {
618 		oldenv = kenvp[i];
619 		kenvp[i] = buf;
620 		mtx_unlock(&kenv_lock);
621 		free(oldenv, M_KENV);
622 	} else {
623 		/* We add the option if it wasn't found */
624 		for (i = 0; (cp = kenvp[i]) != NULL; i++)
625 			;
626 
627 		/* Bounds checking */
628 		if (i < 0 || i >= KENV_SIZE) {
629 			free(buf, M_KENV);
630 			mtx_unlock(&kenv_lock);
631 			return (-1);
632 		}
633 
634 		kenvp[i] = buf;
635 		kenvp[i + 1] = NULL;
636 		mtx_unlock(&kenv_lock);
637 	}
638 	return (0);
639 }
640 
641 /*
642  * Unset an environment variable string.
643  */
644 int
645 kern_unsetenv(const char *name)
646 {
647 	char *cp, *oldenv;
648 	int i, j;
649 
650 	KENV_CHECK;
651 
652 	mtx_lock(&kenv_lock);
653 	cp = _getenv_dynamic(name, &i);
654 	if (cp != NULL) {
655 		oldenv = kenvp[i];
656 		for (j = i + 1; kenvp[j] != NULL; j++)
657 			kenvp[i++] = kenvp[j];
658 		kenvp[i] = NULL;
659 		mtx_unlock(&kenv_lock);
660 		zfree(oldenv, M_KENV);
661 		return (0);
662 	}
663 	mtx_unlock(&kenv_lock);
664 	return (-1);
665 }
666 
667 /*
668  * Return the internal kenv buffer for the variable name, if it exists.
669  * If the dynamic kenv is initialized and the name is present, return
670  * with kenv_lock held.
671  */
672 static char *
673 kenv_acquire(const char *name)
674 {
675 	char *value;
676 
677 	if (dynamic_kenv) {
678 		mtx_lock(&kenv_lock);
679 		value = _getenv_dynamic(name, NULL);
680 		if (value == NULL)
681 			mtx_unlock(&kenv_lock);
682 		return (value);
683 	} else
684 		return (_getenv_static(name));
685 }
686 
687 /*
688  * Undo a previous kenv_acquire() operation
689  */
690 static void
691 kenv_release(const char *buf)
692 {
693 	if ((buf != NULL) && dynamic_kenv)
694 		mtx_unlock(&kenv_lock);
695 }
696 
697 /*
698  * Return a string value from an environment variable.
699  */
700 int
701 getenv_string(const char *name, char *data, int size)
702 {
703 	char *cp;
704 
705 	cp = kenv_acquire(name);
706 
707 	if (cp != NULL)
708 		strlcpy(data, cp, size);
709 
710 	kenv_release(cp);
711 
712 	return (cp != NULL);
713 }
714 
715 /*
716  * Return an array of integers at the given type size and signedness.
717  */
718 int
719 getenv_array(const char *name, void *pdata, int size, int *psize,
720     int type_size, bool allow_signed)
721 {
722 	uint8_t shift;
723 	int64_t value;
724 	int64_t old;
725 	const char *buf;
726 	char *end;
727 	const char *ptr;
728 	int n;
729 	int rc;
730 
731 	rc = 0;			  /* assume failure */
732 
733 	buf = kenv_acquire(name);
734 	if (buf == NULL)
735 		goto error;
736 
737 	/* get maximum number of elements */
738 	size /= type_size;
739 
740 	n = 0;
741 
742 	for (ptr = buf; *ptr != 0; ) {
743 		value = strtoq(ptr, &end, 0);
744 
745 		/* check if signed numbers are allowed */
746 		if (value < 0 && !allow_signed)
747 			goto error;
748 
749 		/* check for invalid value */
750 		if (ptr == end)
751 			goto error;
752 
753 		/* check for valid suffix */
754 		switch (*end) {
755 		case 't':
756 		case 'T':
757 			shift = 40;
758 			end++;
759 			break;
760 		case 'g':
761 		case 'G':
762 			shift = 30;
763 			end++;
764 			break;
765 		case 'm':
766 		case 'M':
767 			shift = 20;
768 			end++;
769 			break;
770 		case 'k':
771 		case 'K':
772 			shift = 10;
773 			end++;
774 			break;
775 		case ' ':
776 		case '\t':
777 		case ',':
778 		case 0:
779 			shift = 0;
780 			break;
781 		default:
782 			/* garbage after numeric value */
783 			goto error;
784 		}
785 
786 		/* skip till next value, if any */
787 		while (*end == '\t' || *end == ',' || *end == ' ')
788 			end++;
789 
790 		/* update pointer */
791 		ptr = end;
792 
793 		/* apply shift */
794 		old = value;
795 		value <<= shift;
796 
797 		/* overflow check */
798 		if ((value >> shift) != old)
799 			goto error;
800 
801 		/* check for buffer overflow */
802 		if (n >= size)
803 			goto error;
804 
805 		/* store value according to type size */
806 		switch (type_size) {
807 		case 1:
808 			if (allow_signed) {
809 				if (value < SCHAR_MIN || value > SCHAR_MAX)
810 					goto error;
811 			} else {
812 				if (value < 0 || value > UCHAR_MAX)
813 					goto error;
814 			}
815 			((uint8_t *)pdata)[n] = (uint8_t)value;
816 			break;
817 		case 2:
818 			if (allow_signed) {
819 				if (value < SHRT_MIN || value > SHRT_MAX)
820 					goto error;
821 			} else {
822 				if (value < 0 || value > USHRT_MAX)
823 					goto error;
824 			}
825 			((uint16_t *)pdata)[n] = (uint16_t)value;
826 			break;
827 		case 4:
828 			if (allow_signed) {
829 				if (value < INT_MIN || value > INT_MAX)
830 					goto error;
831 			} else {
832 				if (value > UINT_MAX)
833 					goto error;
834 			}
835 			((uint32_t *)pdata)[n] = (uint32_t)value;
836 			break;
837 		case 8:
838 			((uint64_t *)pdata)[n] = (uint64_t)value;
839 			break;
840 		default:
841 			goto error;
842 		}
843 		n++;
844 	}
845 	*psize = n * type_size;
846 
847 	if (n != 0)
848 		rc = 1;	/* success */
849 error:
850 	kenv_release(buf);
851 	return (rc);
852 }
853 
854 /*
855  * Return an integer value from an environment variable.
856  */
857 int
858 getenv_int(const char *name, int *data)
859 {
860 	quad_t tmp;
861 	int rval;
862 
863 	rval = getenv_quad(name, &tmp);
864 	if (rval)
865 		*data = (int) tmp;
866 	return (rval);
867 }
868 
869 /*
870  * Return an unsigned integer value from an environment variable.
871  */
872 int
873 getenv_uint(const char *name, unsigned int *data)
874 {
875 	quad_t tmp;
876 	int rval;
877 
878 	rval = getenv_quad(name, &tmp);
879 	if (rval)
880 		*data = (unsigned int) tmp;
881 	return (rval);
882 }
883 
884 /*
885  * Return an int64_t value from an environment variable.
886  */
887 int
888 getenv_int64(const char *name, int64_t *data)
889 {
890 	quad_t tmp;
891 	int64_t rval;
892 
893 	rval = getenv_quad(name, &tmp);
894 	if (rval)
895 		*data = (int64_t) tmp;
896 	return (rval);
897 }
898 
899 /*
900  * Return an uint64_t value from an environment variable.
901  */
902 int
903 getenv_uint64(const char *name, uint64_t *data)
904 {
905 	quad_t tmp;
906 	uint64_t rval;
907 
908 	rval = getenv_quad(name, &tmp);
909 	if (rval)
910 		*data = (uint64_t) tmp;
911 	return (rval);
912 }
913 
914 /*
915  * Return a long value from an environment variable.
916  */
917 int
918 getenv_long(const char *name, long *data)
919 {
920 	quad_t tmp;
921 	int rval;
922 
923 	rval = getenv_quad(name, &tmp);
924 	if (rval)
925 		*data = (long) tmp;
926 	return (rval);
927 }
928 
929 /*
930  * Return an unsigned long value from an environment variable.
931  */
932 int
933 getenv_ulong(const char *name, unsigned long *data)
934 {
935 	quad_t tmp;
936 	int rval;
937 
938 	rval = getenv_quad(name, &tmp);
939 	if (rval)
940 		*data = (unsigned long) tmp;
941 	return (rval);
942 }
943 
944 /*
945  * Return a quad_t value from an environment variable.
946  */
947 int
948 getenv_quad(const char *name, quad_t *data)
949 {
950 	const char	*value;
951 	char		suffix, *vtp;
952 	quad_t		iv;
953 
954 	value = kenv_acquire(name);
955 	if (value == NULL) {
956 		goto error;
957 	}
958 	iv = strtoq(value, &vtp, 0);
959 	if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
960 		goto error;
961 	}
962 	suffix = vtp[0];
963 	kenv_release(value);
964 	switch (suffix) {
965 	case 't': case 'T':
966 		iv *= 1024;
967 		/* FALLTHROUGH */
968 	case 'g': case 'G':
969 		iv *= 1024;
970 		/* FALLTHROUGH */
971 	case 'm': case 'M':
972 		iv *= 1024;
973 		/* FALLTHROUGH */
974 	case 'k': case 'K':
975 		iv *= 1024;
976 	case '\0':
977 		break;
978 	default:
979 		return (0);
980 	}
981 	*data = iv;
982 	return (1);
983 error:
984 	kenv_release(value);
985 	return (0);
986 }
987 
988 /*
989  * Return a boolean value from an environment variable. This can be in
990  * numerical or string form, i.e. "1" or "true".
991  */
992 int
993 getenv_bool(const char *name, bool *data)
994 {
995 	char *val;
996 	int ret = 0;
997 
998 	if (name == NULL)
999 		return (0);
1000 
1001 	val = kern_getenv(name);
1002 	if (val == NULL)
1003 		return (0);
1004 
1005 	if ((strcmp(val, "1") == 0) || (strcasecmp(val, "true") == 0)) {
1006 		*data = true;
1007 		ret = 1;
1008 	} else if ((strcmp(val, "0") == 0) || (strcasecmp(val, "false") == 0)) {
1009 		*data = false;
1010 		ret = 1;
1011 	} else {
1012 		/* Spit out a warning for malformed boolean variables. */
1013 		printf("Environment variable %s has non-boolean value \"%s\"\n",
1014 		    name, val);
1015 	}
1016 	freeenv(val);
1017 
1018 	return (ret);
1019 }
1020 
1021 /*
1022  * Wrapper around getenv_bool to easily check for true.
1023  */
1024 bool
1025 getenv_is_true(const char *name)
1026 {
1027 	bool val;
1028 
1029 	if (getenv_bool(name, &val) != 0)
1030 		return (val);
1031 	return (false);
1032 }
1033 
1034 /*
1035  * Wrapper around getenv_bool to easily check for false.
1036  */
1037 bool
1038 getenv_is_false(const char *name)
1039 {
1040 	bool val;
1041 
1042 	if (getenv_bool(name, &val) != 0)
1043 		return (!val);
1044 	return (false);
1045 }
1046 
1047 /*
1048  * Find the next entry after the one which (cp) falls within, return a
1049  * pointer to its start or NULL if there are no more.
1050  */
1051 static char *
1052 kernenv_next(char *cp)
1053 {
1054 
1055 	if (cp != NULL) {
1056 		while (*cp != 0)
1057 			cp++;
1058 		cp++;
1059 		if (*cp == 0)
1060 			cp = NULL;
1061 	}
1062 	return (cp);
1063 }
1064 
1065 void
1066 tunable_int_init(void *data)
1067 {
1068 	struct tunable_int *d = (struct tunable_int *)data;
1069 
1070 	TUNABLE_INT_FETCH(d->path, d->var);
1071 }
1072 
1073 void
1074 tunable_long_init(void *data)
1075 {
1076 	struct tunable_long *d = (struct tunable_long *)data;
1077 
1078 	TUNABLE_LONG_FETCH(d->path, d->var);
1079 }
1080 
1081 void
1082 tunable_ulong_init(void *data)
1083 {
1084 	struct tunable_ulong *d = (struct tunable_ulong *)data;
1085 
1086 	TUNABLE_ULONG_FETCH(d->path, d->var);
1087 }
1088 
1089 void
1090 tunable_int64_init(void *data)
1091 {
1092 	struct tunable_int64 *d = (struct tunable_int64 *)data;
1093 
1094 	TUNABLE_INT64_FETCH(d->path, d->var);
1095 }
1096 
1097 void
1098 tunable_uint64_init(void *data)
1099 {
1100 	struct tunable_uint64 *d = (struct tunable_uint64 *)data;
1101 
1102 	TUNABLE_UINT64_FETCH(d->path, d->var);
1103 }
1104 
1105 void
1106 tunable_quad_init(void *data)
1107 {
1108 	struct tunable_quad *d = (struct tunable_quad *)data;
1109 
1110 	TUNABLE_QUAD_FETCH(d->path, d->var);
1111 }
1112 
1113 void
1114 tunable_bool_init(void *data)
1115 {
1116 	struct tunable_bool *d = (struct tunable_bool *)data;
1117 
1118 	TUNABLE_BOOL_FETCH(d->path, d->var);
1119 }
1120 
1121 void
1122 tunable_str_init(void *data)
1123 {
1124 	struct tunable_str *d = (struct tunable_str *)data;
1125 
1126 	TUNABLE_STR_FETCH(d->path, d->var, d->size);
1127 }
1128