xref: /freebsd/sys/kern/kern_environment.c (revision 6bb132ba1e4e28cf1e713c8dcd77f2e8af7bde88)
16ba9413bSMike Smith /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
38a36da99SPedro F. Giffuni  *
46ba9413bSMike Smith  * Copyright (c) 1998 Michael Smith
56ba9413bSMike Smith  * All rights reserved.
66ba9413bSMike Smith  *
76ba9413bSMike Smith  * Redistribution and use in source and binary forms, with or without
86ba9413bSMike Smith  * modification, are permitted provided that the following conditions
96ba9413bSMike Smith  * are met:
106ba9413bSMike Smith  * 1. Redistributions of source code must retain the above copyright
116ba9413bSMike Smith  *    notice, this list of conditions and the following disclaimer.
126ba9413bSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
136ba9413bSMike Smith  *    notice, this list of conditions and the following disclaimer in the
146ba9413bSMike Smith  *    documentation and/or other materials provided with the distribution.
156ba9413bSMike Smith  *
166ba9413bSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
176ba9413bSMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
186ba9413bSMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
196ba9413bSMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
206ba9413bSMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
216ba9413bSMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
226ba9413bSMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
236ba9413bSMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
246ba9413bSMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
256ba9413bSMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
266ba9413bSMike Smith  * SUCH DAMAGE.
27e4f570a2SJordan K. Hubbard  */
286ba9413bSMike Smith 
296ba9413bSMike Smith /*
306ba9413bSMike Smith  * The unified bootloader passes us a pointer to a preserved copy of
31d786139cSMaxime Henrion  * bootstrap/kernel environment variables.  We convert them to a
32d786139cSMaxime Henrion  * dynamic array of strings later when the VM subsystem is up.
336ba9413bSMike Smith  *
34d786139cSMaxime Henrion  * We make these available through the kenv(2) syscall for userland
352be111bfSDavide Italiano  * and through kern_getenv()/freeenv() kern_setenv() kern_unsetenv() testenv() for
36d786139cSMaxime Henrion  * the kernel.
376ba9413bSMike Smith  */
386ba9413bSMike Smith 
396ba9413bSMike Smith #include <sys/param.h>
40cf7974fdSZhenlei Huang #include <sys/eventhandler.h>
41df949e76SJose Luis Duran #include <sys/systm.h>
42df949e76SJose Luis Duran #include <sys/kenv.h>
43df949e76SJose Luis Duran #include <sys/kernel.h>
44df949e76SJose Luis Duran #include <sys/libkern.h>
45df949e76SJose Luis Duran #include <sys/limits.h>
46d786139cSMaxime Henrion #include <sys/lock.h>
47d786139cSMaxime Henrion #include <sys/malloc.h>
48d786139cSMaxime Henrion #include <sys/mutex.h>
49acd3428bSRobert Watson #include <sys/priv.h>
50df949e76SJose Luis Duran #include <sys/proc.h>
51df949e76SJose Luis Duran #include <sys/queue.h>
52df949e76SJose Luis Duran #include <sys/sysent.h>
53d786139cSMaxime Henrion #include <sys/sysproto.h>
546ba9413bSMike Smith 
55aed55708SRobert Watson #include <security/mac/mac_framework.h>
56aed55708SRobert Watson 
57*6bb132baSBrooks Davis #include <vm/uma.h>
58*6bb132baSBrooks Davis 
5939d44f7fSKyle Evans static char *_getenv_dynamic_locked(const char *name, int *idx);
6039d44f7fSKyle Evans static char *_getenv_dynamic(const char *name, int *idx);
6139d44f7fSKyle Evans 
62f3ba85ccSJason A. Harmening static char *kenv_acquire(const char *name);
63f3ba85ccSJason A. Harmening static void kenv_release(const char *buf);
64f3ba85ccSJason A. Harmening 
65c711aea6SPoul-Henning Kamp static MALLOC_DEFINE(M_KENV, "kenv", "kernel environment");
66d786139cSMaxime Henrion 
67d786139cSMaxime Henrion #define KENV_SIZE	512	/* Maximum number of environment strings */
68d786139cSMaxime Henrion 
6973845fdbSSimon J. Gerraty static uma_zone_t kenv_zone;
7073845fdbSSimon J. Gerraty static int	kenv_mvallen = KENV_MVALLEN;
7173845fdbSSimon J. Gerraty 
7239d44f7fSKyle Evans /* pointer to the config-generated static environment */
736ba9413bSMike Smith char		*kern_envp;
7439d44f7fSKyle Evans 
7539d44f7fSKyle Evans /* pointer to the md-static environment */
7639d44f7fSKyle Evans char		*md_envp;
7739d44f7fSKyle Evans static int	md_env_len;
7839d44f7fSKyle Evans static int	md_env_pos;
7939d44f7fSKyle Evans 
80d786139cSMaxime Henrion static char	*kernenv_next(char *);
816ba9413bSMike Smith 
82d786139cSMaxime Henrion /* dynamic environment variables */
83d786139cSMaxime Henrion char		**kenvp;
84e3546a75SScott Long struct mtx	kenv_lock;
856ba9413bSMike Smith 
8621cbf0ccSJohn Baldwin /*
87873fbcd7SRobert Watson  * No need to protect this with a mutex since SYSINITS are single threaded.
8821cbf0ccSJohn Baldwin  */
89c7a82b9cSKyle Evans bool	dynamic_kenv;
90d786139cSMaxime Henrion 
91d786139cSMaxime Henrion #define KENV_CHECK	if (!dynamic_kenv) \
92d786139cSMaxime Henrion 			    panic("%s: called before SI_SUB_KMEM", __func__)
93d786139cSMaxime Henrion 
94db0f2643SKyle Evans static int
95db0f2643SKyle Evans kenv_dump(struct thread *td, char **envp, int what, char *value, int len)
96d786139cSMaxime Henrion {
97db0f2643SKyle Evans 	char *buffer, *senv;
98db0f2643SKyle Evans 	size_t done, needed, buflen;
99db0f2643SKyle Evans 	int error;
100d786139cSMaxime Henrion 
101d786139cSMaxime Henrion 	error = 0;
102db0f2643SKyle Evans 	buffer = NULL;
10306afcd9dSDavid Schultz 	done = needed = 0;
104db0f2643SKyle Evans 
105db0f2643SKyle Evans 	MPASS(what == KENV_DUMP || what == KENV_DUMP_LOADER ||
106db0f2643SKyle Evans 	    what == KENV_DUMP_STATIC);
107db0f2643SKyle Evans 
108db0f2643SKyle Evans 	/*
109db0f2643SKyle Evans 	 * For non-dynamic kernel environment, we pass in either md_envp or
110db0f2643SKyle Evans 	 * kern_envp and we must traverse with kernenv_next().  This shuffling
111db0f2643SKyle Evans 	 * of pointers simplifies the below loop by only differing in how envp
112db0f2643SKyle Evans 	 * is modified.
113db0f2643SKyle Evans 	 */
114db0f2643SKyle Evans 	if (what != KENV_DUMP) {
115db0f2643SKyle Evans 		senv = (char *)envp;
116db0f2643SKyle Evans 		envp = &senv;
117db0f2643SKyle Evans 	}
118db0f2643SKyle Evans 
119db0f2643SKyle Evans 	buflen = len;
12073845fdbSSimon J. Gerraty 	if (buflen > KENV_SIZE * (KENV_MNAMELEN + kenv_mvallen + 2))
1213f935cf3SColin Percival 		buflen = KENV_SIZE * (KENV_MNAMELEN +
12273845fdbSSimon J. Gerraty 		    kenv_mvallen + 2);
123db0f2643SKyle Evans 	if (len > 0 && value != NULL)
1243f935cf3SColin Percival 		buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO);
125db0f2643SKyle Evans 
126db0f2643SKyle Evans 	/* Only take the lock for the dynamic kenv. */
127db0f2643SKyle Evans 	if (what == KENV_DUMP)
128e3546a75SScott Long 		mtx_lock(&kenv_lock);
129db0f2643SKyle Evans 	while (*envp != NULL) {
130db0f2643SKyle Evans 		len = strlen(*envp) + 1;
13106afcd9dSDavid Schultz 		needed += len;
1323f935cf3SColin Percival 		len = min(len, buflen - done);
13306afcd9dSDavid Schultz 		/*
13406afcd9dSDavid Schultz 		 * If called with a NULL or insufficiently large
13506afcd9dSDavid Schultz 		 * buffer, just keep computing the required size.
13606afcd9dSDavid Schultz 		 */
137db0f2643SKyle Evans 		if (value != NULL && buffer != NULL && len > 0) {
138db0f2643SKyle Evans 			bcopy(*envp, buffer + done, len);
139d786139cSMaxime Henrion 			done += len;
140d786139cSMaxime Henrion 		}
141db0f2643SKyle Evans 
142db0f2643SKyle Evans 		/* Advance the pointer depending on the kenv format. */
143db0f2643SKyle Evans 		if (what == KENV_DUMP)
144db0f2643SKyle Evans 			envp++;
145db0f2643SKyle Evans 		else
146db0f2643SKyle Evans 			senv = kernenv_next(senv);
14706afcd9dSDavid Schultz 	}
148db0f2643SKyle Evans 	if (what == KENV_DUMP)
149e3546a75SScott Long 		mtx_unlock(&kenv_lock);
150e3546a75SScott Long 	if (buffer != NULL) {
151db0f2643SKyle Evans 		error = copyout(buffer, value, done);
152e3546a75SScott Long 		free(buffer, M_TEMP);
153e3546a75SScott Long 	}
15406afcd9dSDavid Schultz 	td->td_retval[0] = ((done == needed) ? 0 : needed);
15506afcd9dSDavid Schultz 	return (error);
156d786139cSMaxime Henrion }
157d786139cSMaxime Henrion 
158db0f2643SKyle Evans int
159db0f2643SKyle Evans sys_kenv(struct thread *td, struct kenv_args *uap)
160db0f2643SKyle Evans {
161db0f2643SKyle Evans 	char *name, *value;
162db0f2643SKyle Evans 	size_t len;
163db0f2643SKyle Evans 	int error;
164db0f2643SKyle Evans 
165db0f2643SKyle Evans 	KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = false"));
166db0f2643SKyle Evans 
167db0f2643SKyle Evans 	error = 0;
168db0f2643SKyle Evans 
169acd3428bSRobert Watson 	switch (uap->what) {
170db0f2643SKyle Evans 	case KENV_DUMP:
171db0f2643SKyle Evans #ifdef MAC
172db0f2643SKyle Evans 		error = mac_kenv_check_dump(td->td_ucred);
173db0f2643SKyle Evans 		if (error)
174db0f2643SKyle Evans 			return (error);
175db0f2643SKyle Evans #endif
176db0f2643SKyle Evans 		return (kenv_dump(td, kenvp, uap->what, uap->value, uap->len));
177db0f2643SKyle Evans 	case KENV_DUMP_LOADER:
178db0f2643SKyle Evans 	case KENV_DUMP_STATIC:
179db0f2643SKyle Evans #ifdef MAC
180db0f2643SKyle Evans 		error = mac_kenv_check_dump(td->td_ucred);
181db0f2643SKyle Evans 		if (error)
182db0f2643SKyle Evans 			return (error);
183db0f2643SKyle Evans #endif
184db0f2643SKyle Evans #ifdef PRESERVE_EARLY_KENV
185db0f2643SKyle Evans 		return (kenv_dump(td,
186db0f2643SKyle Evans 		    uap->what == KENV_DUMP_LOADER ? (char **)md_envp :
187db0f2643SKyle Evans 		    (char **)kern_envp, uap->what, uap->value, uap->len));
188db0f2643SKyle Evans #else
189db0f2643SKyle Evans 		return (ENOENT);
190db0f2643SKyle Evans #endif
191acd3428bSRobert Watson 	case KENV_SET:
192acd3428bSRobert Watson 		error = priv_check(td, PRIV_KENV_SET);
193d786139cSMaxime Henrion 		if (error)
194d786139cSMaxime Henrion 			return (error);
195acd3428bSRobert Watson 		break;
196acd3428bSRobert Watson 
197acd3428bSRobert Watson 	case KENV_UNSET:
198acd3428bSRobert Watson 		error = priv_check(td, PRIV_KENV_UNSET);
199acd3428bSRobert Watson 		if (error)
200acd3428bSRobert Watson 			return (error);
201acd3428bSRobert Watson 		break;
202d786139cSMaxime Henrion 	}
203d786139cSMaxime Henrion 
2042f0ac259SJaakko Heinonen 	name = malloc(KENV_MNAMELEN + 1, M_TEMP, M_WAITOK);
205d786139cSMaxime Henrion 
2062f0ac259SJaakko Heinonen 	error = copyinstr(uap->name, name, KENV_MNAMELEN + 1, NULL);
207d786139cSMaxime Henrion 	if (error)
208d786139cSMaxime Henrion 		goto done;
209d786139cSMaxime Henrion 
210d1e405c5SAlfred Perlstein 	switch (uap->what) {
211d786139cSMaxime Henrion 	case KENV_GET:
212e686e5aeSRobert Watson #ifdef MAC
21330d239bcSRobert Watson 		error = mac_kenv_check_get(td->td_ucred, name);
214e686e5aeSRobert Watson 		if (error)
215e686e5aeSRobert Watson 			goto done;
216e686e5aeSRobert Watson #endif
2172be111bfSDavide Italiano 		value = kern_getenv(name);
218d786139cSMaxime Henrion 		if (value == NULL) {
219d786139cSMaxime Henrion 			error = ENOENT;
220d786139cSMaxime Henrion 			goto done;
221d786139cSMaxime Henrion 		}
222d786139cSMaxime Henrion 		len = strlen(value) + 1;
223d1e405c5SAlfred Perlstein 		if (len > uap->len)
224d1e405c5SAlfred Perlstein 			len = uap->len;
225d1e405c5SAlfred Perlstein 		error = copyout(value, uap->value, len);
226d786139cSMaxime Henrion 		freeenv(value);
227d786139cSMaxime Henrion 		if (error)
228d786139cSMaxime Henrion 			goto done;
229d786139cSMaxime Henrion 		td->td_retval[0] = len;
230d786139cSMaxime Henrion 		break;
231d786139cSMaxime Henrion 	case KENV_SET:
232d1e405c5SAlfred Perlstein 		len = uap->len;
233d786139cSMaxime Henrion 		if (len < 1) {
234d786139cSMaxime Henrion 			error = EINVAL;
235d786139cSMaxime Henrion 			goto done;
236d786139cSMaxime Henrion 		}
23773845fdbSSimon J. Gerraty 		if (len > kenv_mvallen + 1)
23873845fdbSSimon J. Gerraty 			len = kenv_mvallen + 1;
239a163d034SWarner Losh 		value = malloc(len, M_TEMP, M_WAITOK);
240d1e405c5SAlfred Perlstein 		error = copyinstr(uap->value, value, len, NULL);
241d786139cSMaxime Henrion 		if (error) {
242d786139cSMaxime Henrion 			free(value, M_TEMP);
243d786139cSMaxime Henrion 			goto done;
244d786139cSMaxime Henrion 		}
245e686e5aeSRobert Watson #ifdef MAC
24630d239bcSRobert Watson 		error = mac_kenv_check_set(td->td_ucred, name, value);
247e686e5aeSRobert Watson 		if (error == 0)
248e686e5aeSRobert Watson #endif
2492be111bfSDavide Italiano 			kern_setenv(name, value);
250d786139cSMaxime Henrion 		free(value, M_TEMP);
251d786139cSMaxime Henrion 		break;
252d786139cSMaxime Henrion 	case KENV_UNSET:
253e686e5aeSRobert Watson #ifdef MAC
25430d239bcSRobert Watson 		error = mac_kenv_check_unset(td->td_ucred, name);
255e686e5aeSRobert Watson 		if (error)
256e686e5aeSRobert Watson 			goto done;
257e686e5aeSRobert Watson #endif
2582be111bfSDavide Italiano 		error = kern_unsetenv(name);
259d786139cSMaxime Henrion 		if (error)
260d786139cSMaxime Henrion 			error = ENOENT;
261d786139cSMaxime Henrion 		break;
262d786139cSMaxime Henrion 	default:
263d786139cSMaxime Henrion 		error = EINVAL;
264d786139cSMaxime Henrion 		break;
265d786139cSMaxime Henrion 	}
266d786139cSMaxime Henrion done:
267d786139cSMaxime Henrion 	free(name, M_TEMP);
268d786139cSMaxime Henrion 	return (error);
269d786139cSMaxime Henrion }
270d786139cSMaxime Henrion 
27169dcb7e7SIan Lepore /*
27269dcb7e7SIan Lepore  * Populate the initial kernel environment.
27369dcb7e7SIan Lepore  *
27469dcb7e7SIan Lepore  * This is called very early in MD startup, either to provide a copy of the
27569dcb7e7SIan Lepore  * environment obtained from a boot loader, or to provide an empty buffer into
27669dcb7e7SIan Lepore  * which MD code can store an initial environment using kern_setenv() calls.
27769dcb7e7SIan Lepore  *
27839d44f7fSKyle Evans  * kern_envp is set to the static_env generated by config(8).  This implements
27939d44f7fSKyle Evans  * the env keyword described in config(5).
28069dcb7e7SIan Lepore  *
28169dcb7e7SIan Lepore  * If len is non-zero, the caller is providing an empty buffer.  The caller will
28269dcb7e7SIan Lepore  * subsequently use kern_setenv() to add up to len bytes of initial environment
28369dcb7e7SIan Lepore  * before the dynamic environment is available.
28469dcb7e7SIan Lepore  *
28569dcb7e7SIan Lepore  * If len is zero, the caller is providing a pre-loaded buffer containing
28669dcb7e7SIan Lepore  * environment strings.  Additional strings cannot be added until the dynamic
28769dcb7e7SIan Lepore  * environment is available.  The memory pointed to must remain stable at least
28839d44f7fSKyle Evans  * until sysinit runs init_dynamic_kenv() and preferably until after SI_SUB_KMEM
28939d44f7fSKyle Evans  * is finished so that subr_hints routines may continue to use it until the
29039d44f7fSKyle Evans  * environments have been fully merged at the end of the pass.  If no initial
29139d44f7fSKyle Evans  * environment is available from the boot loader, passing a NULL pointer allows
29239d44f7fSKyle Evans  * the static_env to be installed if it is configured.  In this case, any call
29339d44f7fSKyle Evans  * to kern_setenv() prior to the setup of the dynamic environment will result in
29439d44f7fSKyle Evans  * a panic.
29569dcb7e7SIan Lepore  */
296eae8e367SWarner Losh void
297eae8e367SWarner Losh init_static_kenv(char *buf, size_t len)
298eae8e367SWarner Losh {
29985143dd1SIan Lepore 
30075beb4d4SKyle Evans 	KASSERT(!dynamic_kenv, ("kenv: dynamic_kenv already initialized"));
3015163b1a7SKyle Evans 	/*
3025163b1a7SKyle Evans 	 * Suitably sized means it must be able to hold at least one empty
3035163b1a7SKyle Evans 	 * variable, otherwise things go belly up if a kern_getenv call is
3045163b1a7SKyle Evans 	 * made without a prior call to kern_setenv as we have a malformed
3055163b1a7SKyle Evans 	 * environment.
3065163b1a7SKyle Evans 	 */
3075163b1a7SKyle Evans 	KASSERT(len == 0 || len >= 2,
3085163b1a7SKyle Evans 	    ("kenv: static env must be initialized or suitably sized"));
3095163b1a7SKyle Evans 	KASSERT(len == 0 || (*buf == '\0' && *(buf + 1) == '\0'),
310436c4687SKyle Evans 	    ("kenv: sized buffer must be initially empty"));
3116fd2dcd4SBruce Evans 
3126fd2dcd4SBruce Evans 	/*
3136fd2dcd4SBruce Evans 	 * We may be called twice, with the second call needed to relocate
3146fd2dcd4SBruce Evans 	 * md_envp after enabling paging.  md_envp is then garbage if it is
3156fd2dcd4SBruce Evans 	 * not null and the relocation will move it.  Discard it so as to
3166fd2dcd4SBruce Evans 	 * not crash using its old value in our first call to kern_getenv().
3176fd2dcd4SBruce Evans 	 *
3186fd2dcd4SBruce Evans 	 * The second call gives the same environment as the first except
3196fd2dcd4SBruce Evans 	 * in silly configurations where the static env disables itself.
3206fd2dcd4SBruce Evans 	 *
3216fd2dcd4SBruce Evans 	 * Other env calls don't handle possibly-garbage pointers, so must
3226fd2dcd4SBruce Evans 	 * not be made between enabling paging and calling here.
3236fd2dcd4SBruce Evans 	 */
3246fd2dcd4SBruce Evans 	md_envp = NULL;
3256fd2dcd4SBruce Evans 	md_env_len = 0;
3266fd2dcd4SBruce Evans 	md_env_pos = 0;
3276fd2dcd4SBruce Evans 
32839d44f7fSKyle Evans 	/*
32944314c35SKyle Evans 	 * Give the static environment a chance to disable the loader(8)
33044314c35SKyle Evans 	 * environment first.  This is done with loader_env.disabled=1.
33144314c35SKyle Evans 	 *
33239d44f7fSKyle Evans 	 * static_env and static_hints may both be disabled, but in slightly
33339d44f7fSKyle Evans 	 * different ways.  For static_env, we just don't setup kern_envp and
33439d44f7fSKyle Evans 	 * it's as if a static env wasn't even provided.  For static_hints,
33539d44f7fSKyle Evans 	 * we effectively zero out the buffer to stop the rest of the kernel
33639d44f7fSKyle Evans 	 * from being able to use it.
33739d44f7fSKyle Evans 	 *
33839d44f7fSKyle Evans 	 * We're intentionally setting this up so that static_hints.disabled may
33939d44f7fSKyle Evans 	 * be specified in either the MD env or the static env. This keeps us
34039d44f7fSKyle Evans 	 * consistent in our new world view.
34144314c35SKyle Evans 	 *
34244314c35SKyle Evans 	 * As a warning, the static environment may not be disabled in any way
34344314c35SKyle Evans 	 * if the static environment has disabled the loader environment.
34439d44f7fSKyle Evans 	 */
34539d44f7fSKyle Evans 	kern_envp = static_env;
346624a7e1fSMitchell Horne 	if (!getenv_is_true("loader_env.disabled")) {
34744314c35SKyle Evans 		md_envp = buf;
34844314c35SKyle Evans 		md_env_len = len;
34944314c35SKyle Evans 		md_env_pos = 0;
35044314c35SKyle Evans 
351624a7e1fSMitchell Horne 		if (getenv_is_true("static_env.disabled")) {
3526fd2dcd4SBruce Evans 			kern_envp[0] = '\0';
3536fd2dcd4SBruce Evans 			kern_envp[1] = '\0';
3546fd2dcd4SBruce Evans 		}
35544314c35SKyle Evans 	}
356624a7e1fSMitchell Horne 	if (getenv_is_true("static_hints.disabled")) {
3576fd2dcd4SBruce Evans 		static_hints[0] = '\0';
3586fd2dcd4SBruce Evans 		static_hints[1] = '\0';
3596fd2dcd4SBruce Evans 	}
360e2868734SKyle Evans }
3618ef58863SKyle Evans 
362c32bd976SColin Percival /* Maximum suffix number appended for duplicate environment variable names. */
363c32bd976SColin Percival #define MAXSUFFIX 9999
364c32bd976SColin Percival #define SUFFIXLEN strlen("_" __XSTRING(MAXSUFFIX))
365c32bd976SColin Percival 
366c32bd976SColin Percival static void
367c32bd976SColin Percival getfreesuffix(char *cp, size_t *n)
368c32bd976SColin Percival {
369c32bd976SColin Percival 	size_t len = strlen(cp);
370c32bd976SColin Percival 	char * ncp;
371c32bd976SColin Percival 
372c32bd976SColin Percival 	ncp = malloc(len + SUFFIXLEN + 1, M_KENV, M_WAITOK);
373c32bd976SColin Percival 	memcpy(ncp, cp, len);
374c32bd976SColin Percival 	for (*n = 1; *n <= MAXSUFFIX; (*n)++) {
375c32bd976SColin Percival 		sprintf(&ncp[len], "_%zu", *n);
376c32bd976SColin Percival 		if (!_getenv_dynamic_locked(ncp, NULL))
377c32bd976SColin Percival 			break;
378c32bd976SColin Percival 	}
379c32bd976SColin Percival 	free(ncp, M_KENV);
380c32bd976SColin Percival 	if (*n > MAXSUFFIX)
381c32bd976SColin Percival 		panic("Too many duplicate kernel environment values: %s", cp);
382c32bd976SColin Percival }
383c32bd976SColin Percival 
38439d44f7fSKyle Evans static void
38539d44f7fSKyle Evans init_dynamic_kenv_from(char *init_env, int *curpos)
38639d44f7fSKyle Evans {
38739d44f7fSKyle Evans 	char *cp, *cpnext, *eqpos, *found;
388c32bd976SColin Percival 	size_t len, n;
38939d44f7fSKyle Evans 	int i;
39039d44f7fSKyle Evans 
39139d44f7fSKyle Evans 	if (init_env && *init_env != '\0') {
39239d44f7fSKyle Evans 		found = NULL;
39339d44f7fSKyle Evans 		i = *curpos;
39439d44f7fSKyle Evans 		for (cp = init_env; cp != NULL; cp = cpnext) {
39539d44f7fSKyle Evans 			cpnext = kernenv_next(cp);
39639d44f7fSKyle Evans 			len = strlen(cp) + 1;
397c32bd976SColin Percival 			if (i > KENV_SIZE) {
398c32bd976SColin Percival 				printf(
399c32bd976SColin Percival 				"WARNING: too many kenv strings, ignoring %s\n",
400c32bd976SColin Percival 				    cp);
401c32bd976SColin Percival 				goto sanitize;
402c32bd976SColin Percival 			}
40373845fdbSSimon J. Gerraty 			if (len > KENV_MNAMELEN + 1 + kenv_mvallen + 1) {
40439d44f7fSKyle Evans 				printf(
40539d44f7fSKyle Evans 				"WARNING: too long kenv string, ignoring %s\n",
40639d44f7fSKyle Evans 				    cp);
40739d44f7fSKyle Evans 				goto sanitize;
40839d44f7fSKyle Evans 			}
40939d44f7fSKyle Evans 			eqpos = strchr(cp, '=');
41039d44f7fSKyle Evans 			if (eqpos == NULL) {
41139d44f7fSKyle Evans 				printf(
41239d44f7fSKyle Evans 				"WARNING: malformed static env value, ignoring %s\n",
41339d44f7fSKyle Evans 				    cp);
41439d44f7fSKyle Evans 				goto sanitize;
41539d44f7fSKyle Evans 			}
41639d44f7fSKyle Evans 			*eqpos = 0;
41739d44f7fSKyle Evans 			/*
418c32bd976SColin Percival 			 * Handle duplicates in the environment as we go; we
419c32bd976SColin Percival 			 * add the duplicated assignments with _N suffixes.
420c32bd976SColin Percival 			 * This ensures that (a) if a variable is set in the
421c32bd976SColin Percival 			 * static environment and in the "loader" environment
422c32bd976SColin Percival 			 * provided by MD code, the value from the loader will
423c32bd976SColin Percival 			 * have the expected variable name and the value from
424c32bd976SColin Percival 			 * the static environment will have the suffix; and (b)
425c32bd976SColin Percival 			 * if the "loader" environment has the same variable
426c32bd976SColin Percival 			 * set multiple times (as is possible with values being
427c32bd976SColin Percival 			 * passed via the kernel "command line") the extra
428c32bd976SColin Percival 			 * values are visible to code which knows where to look
429c32bd976SColin Percival 			 * for them.
43039d44f7fSKyle Evans 			 */
43139d44f7fSKyle Evans 			found = _getenv_dynamic_locked(cp, NULL);
432c32bd976SColin Percival 			if (found != NULL) {
433c32bd976SColin Percival 				getfreesuffix(cp, &n);
434c32bd976SColin Percival 				kenvp[i] = malloc(len + SUFFIXLEN,
435c32bd976SColin Percival 				    M_KENV, M_WAITOK);
436c32bd976SColin Percival 				sprintf(kenvp[i++], "%s_%zu=%s", cp, n,
437c32bd976SColin Percival 				    &eqpos[1]);
438c32bd976SColin Percival 			} else {
43939d44f7fSKyle Evans 				kenvp[i] = malloc(len, M_KENV, M_WAITOK);
440c32bd976SColin Percival 				*eqpos = '=';
44139d44f7fSKyle Evans 				strcpy(kenvp[i++], cp);
442c32bd976SColin Percival 			}
44339d44f7fSKyle Evans sanitize:
4447a129c97SKyle Evans #ifdef PRESERVE_EARLY_KENV
4457a129c97SKyle Evans 			continue;
4467a129c97SKyle Evans #else
44739d44f7fSKyle Evans 			explicit_bzero(cp, len - 1);
4487a129c97SKyle Evans #endif
44939d44f7fSKyle Evans 		}
45039d44f7fSKyle Evans 		*curpos = i;
451eae8e367SWarner Losh 	}
45269dcb7e7SIan Lepore }
453eae8e367SWarner Losh 
454d786139cSMaxime Henrion /*
455d786139cSMaxime Henrion  * Setup the dynamic kernel environment.
456d786139cSMaxime Henrion  */
457d786139cSMaxime Henrion static void
458d786139cSMaxime Henrion init_dynamic_kenv(void *data __unused)
459d786139cSMaxime Henrion {
46039d44f7fSKyle Evans 	int dynamic_envpos;
46173845fdbSSimon J. Gerraty 	int size;
46273845fdbSSimon J. Gerraty 
46373845fdbSSimon J. Gerraty 	TUNABLE_INT_FETCH("kenv_mvallen", &kenv_mvallen);
46473845fdbSSimon J. Gerraty 	size = KENV_MNAMELEN + 1 + kenv_mvallen + 1;
46573845fdbSSimon J. Gerraty 
46673845fdbSSimon J. Gerraty 	kenv_zone = uma_zcreate("kenv", size, NULL, NULL, NULL, NULL,
46773845fdbSSimon J. Gerraty 	    UMA_ALIGN_PTR, 0);
468d786139cSMaxime Henrion 
4693904769bSAlexander Leidinger 	kenvp = malloc((KENV_SIZE + 1) * sizeof(char *), M_KENV,
4703904769bSAlexander Leidinger 		M_WAITOK | M_ZERO);
47139d44f7fSKyle Evans 
47239d44f7fSKyle Evans 	dynamic_envpos = 0;
47339d44f7fSKyle Evans 	init_dynamic_kenv_from(md_envp, &dynamic_envpos);
47439d44f7fSKyle Evans 	init_dynamic_kenv_from(kern_envp, &dynamic_envpos);
47539d44f7fSKyle Evans 	kenvp[dynamic_envpos] = NULL;
476d786139cSMaxime Henrion 
477e3546a75SScott Long 	mtx_init(&kenv_lock, "kernel environment", NULL, MTX_DEF);
478c7a82b9cSKyle Evans 	dynamic_kenv = true;
479d786139cSMaxime Henrion }
480cae22dd9SKyle Evans SYSINIT(kenv, SI_SUB_KMEM + 1, SI_ORDER_FIRST, init_dynamic_kenv, NULL);
481d786139cSMaxime Henrion 
482d786139cSMaxime Henrion void
483d786139cSMaxime Henrion freeenv(char *env)
484d786139cSMaxime Henrion {
485d786139cSMaxime Henrion 
4862735a91dSXin LI 	if (dynamic_kenv && env != NULL) {
48797603f1dSIan Lepore 		explicit_bzero(env, strlen(env));
48873845fdbSSimon J. Gerraty 		uma_zfree(kenv_zone, env);
489d786139cSMaxime Henrion 	}
490b9f6af45SColin Percival }
491d786139cSMaxime Henrion 
492d786139cSMaxime Henrion /*
493d786139cSMaxime Henrion  * Internal functions for string lookup.
494d786139cSMaxime Henrion  */
495d786139cSMaxime Henrion static char *
49639d44f7fSKyle Evans _getenv_dynamic_locked(const char *name, int *idx)
497d786139cSMaxime Henrion {
498d786139cSMaxime Henrion 	char *cp;
499d786139cSMaxime Henrion 	int len, i;
500d786139cSMaxime Henrion 
501d786139cSMaxime Henrion 	len = strlen(name);
502d786139cSMaxime Henrion 	for (cp = kenvp[0], i = 0; cp != NULL; cp = kenvp[++i]) {
5032b7182c6SDavid Xu 		if ((strncmp(cp, name, len) == 0) &&
5042b7182c6SDavid Xu 		    (cp[len] == '=')) {
505d786139cSMaxime Henrion 			if (idx != NULL)
506d786139cSMaxime Henrion 				*idx = i;
507d786139cSMaxime Henrion 			return (cp + len + 1);
508d786139cSMaxime Henrion 		}
509d786139cSMaxime Henrion 	}
510d786139cSMaxime Henrion 	return (NULL);
511d786139cSMaxime Henrion }
512d786139cSMaxime Henrion 
513d786139cSMaxime Henrion static char *
51439d44f7fSKyle Evans _getenv_dynamic(const char *name, int *idx)
51539d44f7fSKyle Evans {
51639d44f7fSKyle Evans 
51739d44f7fSKyle Evans 	mtx_assert(&kenv_lock, MA_OWNED);
51839d44f7fSKyle Evans 	return (_getenv_dynamic_locked(name, idx));
51939d44f7fSKyle Evans }
52039d44f7fSKyle Evans 
52139d44f7fSKyle Evans static char *
52239d44f7fSKyle Evans _getenv_static_from(char *chkenv, const char *name)
5236ba9413bSMike Smith {
5246ba9413bSMike Smith 	char *cp, *ep;
5256ba9413bSMike Smith 	int len;
5266ba9413bSMike Smith 
52739d44f7fSKyle Evans 	for (cp = chkenv; cp != NULL; cp = kernenv_next(cp)) {
5286ba9413bSMike Smith 		for (ep = cp; (*ep != '=') && (*ep != 0); ep++)
5296ba9413bSMike Smith 			;
5302c900f64SBruce Evans 		if (*ep != '=')
5312c900f64SBruce Evans 			continue;
5326ba9413bSMike Smith 		len = ep - cp;
5336ba9413bSMike Smith 		ep++;
5342c900f64SBruce Evans 		if (!strncmp(name, cp, len) && name[len] == 0)
5356ba9413bSMike Smith 			return (ep);
5366ba9413bSMike Smith 	}
5376ba9413bSMike Smith 	return (NULL);
5386ba9413bSMike Smith }
5396ba9413bSMike Smith 
54039d44f7fSKyle Evans static char *
54139d44f7fSKyle Evans _getenv_static(const char *name)
54239d44f7fSKyle Evans {
54339d44f7fSKyle Evans 	char *val;
54439d44f7fSKyle Evans 
54539d44f7fSKyle Evans 	val = _getenv_static_from(md_envp, name);
54639d44f7fSKyle Evans 	if (val != NULL)
54739d44f7fSKyle Evans 		return (val);
54839d44f7fSKyle Evans 	val = _getenv_static_from(kern_envp, name);
54939d44f7fSKyle Evans 	if (val != NULL)
55039d44f7fSKyle Evans 		return (val);
55139d44f7fSKyle Evans 	return (NULL);
55239d44f7fSKyle Evans }
55339d44f7fSKyle Evans 
5542084f96cSMike Smith /*
555d786139cSMaxime Henrion  * Look up an environment variable by name.
556d786139cSMaxime Henrion  * Return a pointer to the string if found.
557d786139cSMaxime Henrion  * The pointer has to be freed with freeenv()
558d786139cSMaxime Henrion  * after use.
559d786139cSMaxime Henrion  */
560d786139cSMaxime Henrion char *
5612be111bfSDavide Italiano kern_getenv(const char *name)
562d786139cSMaxime Henrion {
563f3ba85ccSJason A. Harmening 	char *cp, *ret;
564f3ba85ccSJason A. Harmening 	int len;
565d786139cSMaxime Henrion 
566d786139cSMaxime Henrion 	if (dynamic_kenv) {
567f3ba85ccSJason A. Harmening 		len = KENV_MNAMELEN + 1 + kenv_mvallen + 1;
568f3ba85ccSJason A. Harmening 		ret = uma_zalloc(kenv_zone, M_WAITOK | M_ZERO);
569f3ba85ccSJason A. Harmening 		mtx_lock(&kenv_lock);
570f3ba85ccSJason A. Harmening 		cp = _getenv_dynamic(name, NULL);
571f3ba85ccSJason A. Harmening 		if (cp != NULL)
572f3ba85ccSJason A. Harmening 			strlcpy(ret, cp, len);
573f3ba85ccSJason A. Harmening 		mtx_unlock(&kenv_lock);
574f3ba85ccSJason A. Harmening 		if (cp == NULL) {
575f3ba85ccSJason A. Harmening 			uma_zfree(kenv_zone, ret);
576f3ba85ccSJason A. Harmening 			ret = NULL;
577b48a4280SMaxime Henrion 		}
578d786139cSMaxime Henrion 	} else
579d786139cSMaxime Henrion 		ret = _getenv_static(name);
580f3ba85ccSJason A. Harmening 
581d786139cSMaxime Henrion 	return (ret);
582d786139cSMaxime Henrion }
583d786139cSMaxime Henrion 
584d786139cSMaxime Henrion /*
585d786139cSMaxime Henrion  * Test if an environment variable is defined.
586d786139cSMaxime Henrion  */
587d786139cSMaxime Henrion int
588d786139cSMaxime Henrion testenv(const char *name)
589d786139cSMaxime Henrion {
590d786139cSMaxime Henrion 	char *cp;
591d786139cSMaxime Henrion 
592f3ba85ccSJason A. Harmening 	cp = kenv_acquire(name);
593f3ba85ccSJason A. Harmening 	kenv_release(cp);
594f3ba85ccSJason A. Harmening 
595d786139cSMaxime Henrion 	if (cp != NULL)
596d786139cSMaxime Henrion 		return (1);
597d786139cSMaxime Henrion 	return (0);
598d786139cSMaxime Henrion }
599d786139cSMaxime Henrion 
60039d44f7fSKyle Evans /*
60139d44f7fSKyle Evans  * Set an environment variable in the MD-static environment.  This cannot
60239d44f7fSKyle Evans  * feasibly be done on config(8)-generated static environments as they don't
60339d44f7fSKyle Evans  * generally include space for extra variables.
60439d44f7fSKyle Evans  */
605eae8e367SWarner Losh static int
606eae8e367SWarner Losh setenv_static(const char *name, const char *value)
607eae8e367SWarner Losh {
608eae8e367SWarner Losh 	int len;
609eae8e367SWarner Losh 
61039d44f7fSKyle Evans 	if (md_env_pos >= md_env_len)
611eae8e367SWarner Losh 		return (-1);
612eae8e367SWarner Losh 
613eae8e367SWarner Losh 	/* Check space for x=y and two nuls */
614eae8e367SWarner Losh 	len = strlen(name) + strlen(value);
61539d44f7fSKyle Evans 	if (len + 3 < md_env_len - md_env_pos) {
61639d44f7fSKyle Evans 		len = sprintf(&md_envp[md_env_pos], "%s=%s", name, value);
61739d44f7fSKyle Evans 		md_env_pos += len+1;
61839d44f7fSKyle Evans 		md_envp[md_env_pos] = '\0';
619eae8e367SWarner Losh 		return (0);
620eae8e367SWarner Losh 	} else
621eae8e367SWarner Losh 		return (-1);
622eae8e367SWarner Losh 
623eae8e367SWarner Losh }
624eae8e367SWarner Losh 
625d786139cSMaxime Henrion /*
626d786139cSMaxime Henrion  * Set an environment variable by name.
627d786139cSMaxime Henrion  */
628b48a4280SMaxime Henrion int
6292be111bfSDavide Italiano kern_setenv(const char *name, const char *value)
630d786139cSMaxime Henrion {
631b48a4280SMaxime Henrion 	char *buf, *cp, *oldenv;
632b48a4280SMaxime Henrion 	int namelen, vallen, i;
633d786139cSMaxime Henrion 
634c7a82b9cSKyle Evans 	if (!dynamic_kenv && md_env_len > 0)
635eae8e367SWarner Losh 		return (setenv_static(name, value));
636eae8e367SWarner Losh 
637d786139cSMaxime Henrion 	KENV_CHECK;
638d786139cSMaxime Henrion 
639b48a4280SMaxime Henrion 	namelen = strlen(name) + 1;
6402f0ac259SJaakko Heinonen 	if (namelen > KENV_MNAMELEN + 1)
641b48a4280SMaxime Henrion 		return (-1);
642b48a4280SMaxime Henrion 	vallen = strlen(value) + 1;
64373845fdbSSimon J. Gerraty 	if (vallen > kenv_mvallen + 1)
644b48a4280SMaxime Henrion 		return (-1);
645a163d034SWarner Losh 	buf = malloc(namelen + vallen, M_KENV, M_WAITOK);
646d786139cSMaxime Henrion 	sprintf(buf, "%s=%s", name, value);
647d786139cSMaxime Henrion 
648e3546a75SScott Long 	mtx_lock(&kenv_lock);
649d786139cSMaxime Henrion 	cp = _getenv_dynamic(name, &i);
650d786139cSMaxime Henrion 	if (cp != NULL) {
651b48a4280SMaxime Henrion 		oldenv = kenvp[i];
652d786139cSMaxime Henrion 		kenvp[i] = buf;
653e3546a75SScott Long 		mtx_unlock(&kenv_lock);
654b48a4280SMaxime Henrion 		free(oldenv, M_KENV);
655d786139cSMaxime Henrion 	} else {
656d786139cSMaxime Henrion 		/* We add the option if it wasn't found */
657d786139cSMaxime Henrion 		for (i = 0; (cp = kenvp[i]) != NULL; i++)
658d786139cSMaxime Henrion 			;
6593904769bSAlexander Leidinger 
6603904769bSAlexander Leidinger 		/* Bounds checking */
6613904769bSAlexander Leidinger 		if (i < 0 || i >= KENV_SIZE) {
6623904769bSAlexander Leidinger 			free(buf, M_KENV);
663e3546a75SScott Long 			mtx_unlock(&kenv_lock);
6643904769bSAlexander Leidinger 			return (-1);
6653904769bSAlexander Leidinger 		}
6663904769bSAlexander Leidinger 
667d786139cSMaxime Henrion 		kenvp[i] = buf;
668d786139cSMaxime Henrion 		kenvp[i + 1] = NULL;
669e3546a75SScott Long 		mtx_unlock(&kenv_lock);
670d786139cSMaxime Henrion 	}
671cf7974fdSZhenlei Huang 	EVENTHANDLER_INVOKE(setenv, name);
672b48a4280SMaxime Henrion 	return (0);
673b48a4280SMaxime Henrion }
674d786139cSMaxime Henrion 
675d786139cSMaxime Henrion /*
676d786139cSMaxime Henrion  * Unset an environment variable string.
677d786139cSMaxime Henrion  */
678d786139cSMaxime Henrion int
6792be111bfSDavide Italiano kern_unsetenv(const char *name)
680d786139cSMaxime Henrion {
681b48a4280SMaxime Henrion 	char *cp, *oldenv;
682d786139cSMaxime Henrion 	int i, j;
683d786139cSMaxime Henrion 
684d786139cSMaxime Henrion 	KENV_CHECK;
685d786139cSMaxime Henrion 
686e3546a75SScott Long 	mtx_lock(&kenv_lock);
687d786139cSMaxime Henrion 	cp = _getenv_dynamic(name, &i);
688d786139cSMaxime Henrion 	if (cp != NULL) {
689b48a4280SMaxime Henrion 		oldenv = kenvp[i];
690d786139cSMaxime Henrion 		for (j = i + 1; kenvp[j] != NULL; j++)
691d786139cSMaxime Henrion 			kenvp[i++] = kenvp[j];
692d786139cSMaxime Henrion 		kenvp[i] = NULL;
693e3546a75SScott Long 		mtx_unlock(&kenv_lock);
6944a711b8dSJohn Baldwin 		zfree(oldenv, M_KENV);
695cf7974fdSZhenlei Huang 		EVENTHANDLER_INVOKE(unsetenv, name);
696d786139cSMaxime Henrion 		return (0);
697d786139cSMaxime Henrion 	}
698e3546a75SScott Long 	mtx_unlock(&kenv_lock);
699d786139cSMaxime Henrion 	return (-1);
700d786139cSMaxime Henrion }
701d786139cSMaxime Henrion 
702d786139cSMaxime Henrion /*
703f3ba85ccSJason A. Harmening  * Return the internal kenv buffer for the variable name, if it exists.
704f3ba85ccSJason A. Harmening  * If the dynamic kenv is initialized and the name is present, return
705f3ba85ccSJason A. Harmening  * with kenv_lock held.
70673845fdbSSimon J. Gerraty  */
70773845fdbSSimon J. Gerraty static char *
708f3ba85ccSJason A. Harmening kenv_acquire(const char *name)
70973845fdbSSimon J. Gerraty {
710f3ba85ccSJason A. Harmening 	char *value;
71173845fdbSSimon J. Gerraty 
71273845fdbSSimon J. Gerraty 	if (dynamic_kenv) {
71373845fdbSSimon J. Gerraty 		mtx_lock(&kenv_lock);
714f3ba85ccSJason A. Harmening 		value = _getenv_dynamic(name, NULL);
715f3ba85ccSJason A. Harmening 		if (value == NULL)
71673845fdbSSimon J. Gerraty 			mtx_unlock(&kenv_lock);
717f3ba85ccSJason A. Harmening 		return (value);
71873845fdbSSimon J. Gerraty 	} else
719f3ba85ccSJason A. Harmening 		return (_getenv_static(name));
720f3ba85ccSJason A. Harmening }
72173845fdbSSimon J. Gerraty 
722f3ba85ccSJason A. Harmening /*
723f3ba85ccSJason A. Harmening  * Undo a previous kenv_acquire() operation
724f3ba85ccSJason A. Harmening  */
725f3ba85ccSJason A. Harmening static void
726f3ba85ccSJason A. Harmening kenv_release(const char *buf)
727f3ba85ccSJason A. Harmening {
728f3ba85ccSJason A. Harmening 	if ((buf != NULL) && dynamic_kenv)
729f3ba85ccSJason A. Harmening 		mtx_unlock(&kenv_lock);
73073845fdbSSimon J. Gerraty }
73173845fdbSSimon J. Gerraty 
73273845fdbSSimon J. Gerraty /*
73321cbf0ccSJohn Baldwin  * Return a string value from an environment variable.
73421cbf0ccSJohn Baldwin  */
73521cbf0ccSJohn Baldwin int
73621cbf0ccSJohn Baldwin getenv_string(const char *name, char *data, int size)
73721cbf0ccSJohn Baldwin {
738915f2b7cSAlexander Motin 	char *cp;
73921cbf0ccSJohn Baldwin 
740f3ba85ccSJason A. Harmening 	cp = kenv_acquire(name);
741f3ba85ccSJason A. Harmening 
742915f2b7cSAlexander Motin 	if (cp != NULL)
743915f2b7cSAlexander Motin 		strlcpy(data, cp, size);
744f3ba85ccSJason A. Harmening 
745f3ba85ccSJason A. Harmening 	kenv_release(cp);
746f3ba85ccSJason A. Harmening 
747915f2b7cSAlexander Motin 	return (cp != NULL);
74821cbf0ccSJohn Baldwin }
74921cbf0ccSJohn Baldwin 
75021cbf0ccSJohn Baldwin /*
751ce70c572SHans Petter Selasky  * Return an array of integers at the given type size and signedness.
752ce70c572SHans Petter Selasky  */
753ce70c572SHans Petter Selasky int
754ce70c572SHans Petter Selasky getenv_array(const char *name, void *pdata, int size, int *psize,
755ce70c572SHans Petter Selasky     int type_size, bool allow_signed)
756ce70c572SHans Petter Selasky {
757ce70c572SHans Petter Selasky 	uint8_t shift;
758ce70c572SHans Petter Selasky 	int64_t value;
759ce70c572SHans Petter Selasky 	int64_t old;
760f3ba85ccSJason A. Harmening 	const char *buf;
761ce70c572SHans Petter Selasky 	char *end;
762f3ba85ccSJason A. Harmening 	const char *ptr;
763ce70c572SHans Petter Selasky 	int n;
76473845fdbSSimon J. Gerraty 	int rc;
765ce70c572SHans Petter Selasky 
76673845fdbSSimon J. Gerraty 	rc = 0;			  /* assume failure */
767f3ba85ccSJason A. Harmening 
768f3ba85ccSJason A. Harmening 	buf = kenv_acquire(name);
769f3ba85ccSJason A. Harmening 	if (buf == NULL)
770f3ba85ccSJason A. Harmening 		goto error;
771f3ba85ccSJason A. Harmening 
772ce70c572SHans Petter Selasky 	/* get maximum number of elements */
773ce70c572SHans Petter Selasky 	size /= type_size;
774ce70c572SHans Petter Selasky 
775ce70c572SHans Petter Selasky 	n = 0;
776ce70c572SHans Petter Selasky 
777ce70c572SHans Petter Selasky 	for (ptr = buf; *ptr != 0; ) {
778ce70c572SHans Petter Selasky 		value = strtoq(ptr, &end, 0);
779ce70c572SHans Petter Selasky 
780ce70c572SHans Petter Selasky 		/* check if signed numbers are allowed */
781ce70c572SHans Petter Selasky 		if (value < 0 && !allow_signed)
782ce70c572SHans Petter Selasky 			goto error;
783ce70c572SHans Petter Selasky 
784ce70c572SHans Petter Selasky 		/* check for invalid value */
785ce70c572SHans Petter Selasky 		if (ptr == end)
786ce70c572SHans Petter Selasky 			goto error;
787ce70c572SHans Petter Selasky 
788ce70c572SHans Petter Selasky 		/* check for valid suffix */
789ce70c572SHans Petter Selasky 		switch (*end) {
790ce70c572SHans Petter Selasky 		case 't':
791ce70c572SHans Petter Selasky 		case 'T':
792ce70c572SHans Petter Selasky 			shift = 40;
793ce70c572SHans Petter Selasky 			end++;
794ce70c572SHans Petter Selasky 			break;
795ce70c572SHans Petter Selasky 		case 'g':
796ce70c572SHans Petter Selasky 		case 'G':
797ce70c572SHans Petter Selasky 			shift = 30;
798ce70c572SHans Petter Selasky 			end++;
799ce70c572SHans Petter Selasky 			break;
800ce70c572SHans Petter Selasky 		case 'm':
801ce70c572SHans Petter Selasky 		case 'M':
802ce70c572SHans Petter Selasky 			shift = 20;
803ce70c572SHans Petter Selasky 			end++;
804ce70c572SHans Petter Selasky 			break;
805ce70c572SHans Petter Selasky 		case 'k':
806ce70c572SHans Petter Selasky 		case 'K':
807ce70c572SHans Petter Selasky 			shift = 10;
808ce70c572SHans Petter Selasky 			end++;
809ce70c572SHans Petter Selasky 			break;
810ce70c572SHans Petter Selasky 		case ' ':
811ce70c572SHans Petter Selasky 		case '\t':
812ce70c572SHans Petter Selasky 		case ',':
813ce70c572SHans Petter Selasky 		case 0:
814ce70c572SHans Petter Selasky 			shift = 0;
815ce70c572SHans Petter Selasky 			break;
816ce70c572SHans Petter Selasky 		default:
817ce70c572SHans Petter Selasky 			/* garbage after numeric value */
818ce70c572SHans Petter Selasky 			goto error;
819ce70c572SHans Petter Selasky 		}
820ce70c572SHans Petter Selasky 
821ce70c572SHans Petter Selasky 		/* skip till next value, if any */
822ce70c572SHans Petter Selasky 		while (*end == '\t' || *end == ',' || *end == ' ')
823ce70c572SHans Petter Selasky 			end++;
824ce70c572SHans Petter Selasky 
825ce70c572SHans Petter Selasky 		/* update pointer */
826ce70c572SHans Petter Selasky 		ptr = end;
827ce70c572SHans Petter Selasky 
828ce70c572SHans Petter Selasky 		/* apply shift */
829ce70c572SHans Petter Selasky 		old = value;
830ce70c572SHans Petter Selasky 		value <<= shift;
831ce70c572SHans Petter Selasky 
832ce70c572SHans Petter Selasky 		/* overflow check */
833ce70c572SHans Petter Selasky 		if ((value >> shift) != old)
834ce70c572SHans Petter Selasky 			goto error;
835ce70c572SHans Petter Selasky 
836ce70c572SHans Petter Selasky 		/* check for buffer overflow */
837ce70c572SHans Petter Selasky 		if (n >= size)
838ce70c572SHans Petter Selasky 			goto error;
839ce70c572SHans Petter Selasky 
840ce70c572SHans Petter Selasky 		/* store value according to type size */
841ce70c572SHans Petter Selasky 		switch (type_size) {
842ce70c572SHans Petter Selasky 		case 1:
843ce70c572SHans Petter Selasky 			if (allow_signed) {
844ce70c572SHans Petter Selasky 				if (value < SCHAR_MIN || value > SCHAR_MAX)
845ce70c572SHans Petter Selasky 					goto error;
846ce70c572SHans Petter Selasky 			} else {
847ce70c572SHans Petter Selasky 				if (value < 0 || value > UCHAR_MAX)
848ce70c572SHans Petter Selasky 					goto error;
849ce70c572SHans Petter Selasky 			}
850ce70c572SHans Petter Selasky 			((uint8_t *)pdata)[n] = (uint8_t)value;
851ce70c572SHans Petter Selasky 			break;
852ce70c572SHans Petter Selasky 		case 2:
853ce70c572SHans Petter Selasky 			if (allow_signed) {
854ce70c572SHans Petter Selasky 				if (value < SHRT_MIN || value > SHRT_MAX)
855ce70c572SHans Petter Selasky 					goto error;
856ce70c572SHans Petter Selasky 			} else {
857ce70c572SHans Petter Selasky 				if (value < 0 || value > USHRT_MAX)
858ce70c572SHans Petter Selasky 					goto error;
859ce70c572SHans Petter Selasky 			}
860ce70c572SHans Petter Selasky 			((uint16_t *)pdata)[n] = (uint16_t)value;
861ce70c572SHans Petter Selasky 			break;
862ce70c572SHans Petter Selasky 		case 4:
863ce70c572SHans Petter Selasky 			if (allow_signed) {
864ce70c572SHans Petter Selasky 				if (value < INT_MIN || value > INT_MAX)
865ce70c572SHans Petter Selasky 					goto error;
866ce70c572SHans Petter Selasky 			} else {
867ce70c572SHans Petter Selasky 				if (value > UINT_MAX)
868ce70c572SHans Petter Selasky 					goto error;
869ce70c572SHans Petter Selasky 			}
870ce70c572SHans Petter Selasky 			((uint32_t *)pdata)[n] = (uint32_t)value;
871ce70c572SHans Petter Selasky 			break;
872ce70c572SHans Petter Selasky 		case 8:
873ce70c572SHans Petter Selasky 			((uint64_t *)pdata)[n] = (uint64_t)value;
874ce70c572SHans Petter Selasky 			break;
875ce70c572SHans Petter Selasky 		default:
876ce70c572SHans Petter Selasky 			goto error;
877ce70c572SHans Petter Selasky 		}
878ce70c572SHans Petter Selasky 		n++;
879ce70c572SHans Petter Selasky 	}
880ce70c572SHans Petter Selasky 	*psize = n * type_size;
881ce70c572SHans Petter Selasky 
882ce70c572SHans Petter Selasky 	if (n != 0)
88373845fdbSSimon J. Gerraty 		rc = 1;	/* success */
884ce70c572SHans Petter Selasky error:
885f3ba85ccSJason A. Harmening 	kenv_release(buf);
88673845fdbSSimon J. Gerraty 	return (rc);
887ce70c572SHans Petter Selasky }
888ce70c572SHans Petter Selasky 
889ce70c572SHans Petter Selasky /*
8902084f96cSMike Smith  * Return an integer value from an environment variable.
8912084f96cSMike Smith  */
8922084f96cSMike Smith int
893b93c3c5eSPeter Wemm getenv_int(const char *name, int *data)
8942084f96cSMike Smith {
895c1dc94eeSMatt Jacob 	quad_t tmp;
896c1dc94eeSMatt Jacob 	int rval;
897c1dc94eeSMatt Jacob 
898c1dc94eeSMatt Jacob 	rval = getenv_quad(name, &tmp);
8996692ac66SPeter Wemm 	if (rval)
900c1dc94eeSMatt Jacob 		*data = (int) tmp;
901c1dc94eeSMatt Jacob 	return (rval);
902c1dc94eeSMatt Jacob }
903c1dc94eeSMatt Jacob 
904c1dc94eeSMatt Jacob /*
905d302c56dSDag-Erling Smørgrav  * Return an unsigned integer value from an environment variable.
906d302c56dSDag-Erling Smørgrav  */
907d302c56dSDag-Erling Smørgrav int
908d302c56dSDag-Erling Smørgrav getenv_uint(const char *name, unsigned int *data)
909d302c56dSDag-Erling Smørgrav {
910d302c56dSDag-Erling Smørgrav 	quad_t tmp;
911d302c56dSDag-Erling Smørgrav 	int rval;
912d302c56dSDag-Erling Smørgrav 
913d302c56dSDag-Erling Smørgrav 	rval = getenv_quad(name, &tmp);
914d302c56dSDag-Erling Smørgrav 	if (rval)
915d302c56dSDag-Erling Smørgrav 		*data = (unsigned int) tmp;
916d302c56dSDag-Erling Smørgrav 	return (rval);
917d302c56dSDag-Erling Smørgrav }
918d302c56dSDag-Erling Smørgrav 
919d302c56dSDag-Erling Smørgrav /*
92015be49f5SWarner Losh  * Return an int64_t value from an environment variable.
92115be49f5SWarner Losh  */
92215be49f5SWarner Losh int
92315be49f5SWarner Losh getenv_int64(const char *name, int64_t *data)
92415be49f5SWarner Losh {
92515be49f5SWarner Losh 	quad_t tmp;
92615be49f5SWarner Losh 	int64_t rval;
92715be49f5SWarner Losh 
92815be49f5SWarner Losh 	rval = getenv_quad(name, &tmp);
92915be49f5SWarner Losh 	if (rval)
93015be49f5SWarner Losh 		*data = (int64_t) tmp;
93115be49f5SWarner Losh 	return (rval);
93215be49f5SWarner Losh }
93315be49f5SWarner Losh 
93415be49f5SWarner Losh /*
93515be49f5SWarner Losh  * Return an uint64_t value from an environment variable.
93615be49f5SWarner Losh  */
93715be49f5SWarner Losh int
93815be49f5SWarner Losh getenv_uint64(const char *name, uint64_t *data)
93915be49f5SWarner Losh {
94015be49f5SWarner Losh 	quad_t tmp;
94115be49f5SWarner Losh 	uint64_t rval;
94215be49f5SWarner Losh 
94315be49f5SWarner Losh 	rval = getenv_quad(name, &tmp);
94415be49f5SWarner Losh 	if (rval)
94515be49f5SWarner Losh 		*data = (uint64_t) tmp;
94615be49f5SWarner Losh 	return (rval);
94715be49f5SWarner Losh }
94815be49f5SWarner Losh 
94915be49f5SWarner Losh /*
950b0e1e474SDag-Erling Smørgrav  * Return a long value from an environment variable.
951b0e1e474SDag-Erling Smørgrav  */
952d302c56dSDag-Erling Smørgrav int
953b0e1e474SDag-Erling Smørgrav getenv_long(const char *name, long *data)
954b0e1e474SDag-Erling Smørgrav {
955b0e1e474SDag-Erling Smørgrav 	quad_t tmp;
956d302c56dSDag-Erling Smørgrav 	int rval;
957b0e1e474SDag-Erling Smørgrav 
958b0e1e474SDag-Erling Smørgrav 	rval = getenv_quad(name, &tmp);
959b0e1e474SDag-Erling Smørgrav 	if (rval)
960b0e1e474SDag-Erling Smørgrav 		*data = (long) tmp;
961b0e1e474SDag-Erling Smørgrav 	return (rval);
962b0e1e474SDag-Erling Smørgrav }
963b0e1e474SDag-Erling Smørgrav 
964b0e1e474SDag-Erling Smørgrav /*
965b0e1e474SDag-Erling Smørgrav  * Return an unsigned long value from an environment variable.
966b0e1e474SDag-Erling Smørgrav  */
967d302c56dSDag-Erling Smørgrav int
968b0e1e474SDag-Erling Smørgrav getenv_ulong(const char *name, unsigned long *data)
969b0e1e474SDag-Erling Smørgrav {
970b0e1e474SDag-Erling Smørgrav 	quad_t tmp;
971d302c56dSDag-Erling Smørgrav 	int rval;
972b0e1e474SDag-Erling Smørgrav 
973b0e1e474SDag-Erling Smørgrav 	rval = getenv_quad(name, &tmp);
974b0e1e474SDag-Erling Smørgrav 	if (rval)
975b0e1e474SDag-Erling Smørgrav 		*data = (unsigned long) tmp;
976b0e1e474SDag-Erling Smørgrav 	return (rval);
977b0e1e474SDag-Erling Smørgrav }
978b0e1e474SDag-Erling Smørgrav 
979b0e1e474SDag-Erling Smørgrav /*
980c1dc94eeSMatt Jacob  * Return a quad_t value from an environment variable.
981c1dc94eeSMatt Jacob  */
98221cbf0ccSJohn Baldwin int
983b93c3c5eSPeter Wemm getenv_quad(const char *name, quad_t *data)
984c1dc94eeSMatt Jacob {
985f3ba85ccSJason A. Harmening 	const char	*value;
986f3ba85ccSJason A. Harmening 	char		suffix, *vtp;
9872084f96cSMike Smith 	quad_t		iv;
9882084f96cSMike Smith 
989f3ba85ccSJason A. Harmening 	value = kenv_acquire(name);
990f3ba85ccSJason A. Harmening 	if (value == NULL) {
991f3ba85ccSJason A. Harmening 		goto error;
992f3ba85ccSJason A. Harmening 	}
9932084f96cSMike Smith 	iv = strtoq(value, &vtp, 0);
99473845fdbSSimon J. Gerraty 	if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
995f3ba85ccSJason A. Harmening 		goto error;
99673845fdbSSimon J. Gerraty 	}
997f3ba85ccSJason A. Harmening 	suffix = vtp[0];
998f3ba85ccSJason A. Harmening 	kenv_release(value);
999f3ba85ccSJason A. Harmening 	switch (suffix) {
1000f3301d15SDag-Erling Smørgrav 	case 't': case 'T':
1001f3301d15SDag-Erling Smørgrav 		iv *= 1024;
1002970a174fSMark Johnston 		/* FALLTHROUGH */
1003f3301d15SDag-Erling Smørgrav 	case 'g': case 'G':
1004f3301d15SDag-Erling Smørgrav 		iv *= 1024;
1005970a174fSMark Johnston 		/* FALLTHROUGH */
1006f3301d15SDag-Erling Smørgrav 	case 'm': case 'M':
1007f3301d15SDag-Erling Smørgrav 		iv *= 1024;
1008970a174fSMark Johnston 		/* FALLTHROUGH */
1009f3301d15SDag-Erling Smørgrav 	case 'k': case 'K':
1010f3301d15SDag-Erling Smørgrav 		iv *= 1024;
1011f3301d15SDag-Erling Smørgrav 	case '\0':
1012f3301d15SDag-Erling Smørgrav 		break;
1013f3301d15SDag-Erling Smørgrav 	default:
10142084f96cSMike Smith 		return (0);
1015d786139cSMaxime Henrion 	}
1016c1dc94eeSMatt Jacob 	*data = iv;
10172084f96cSMike Smith 	return (1);
1018f3ba85ccSJason A. Harmening error:
1019f3ba85ccSJason A. Harmening 	kenv_release(value);
1020f3ba85ccSJason A. Harmening 	return (0);
10212084f96cSMike Smith }
10226ba9413bSMike Smith 
10231114d185SPeter Wemm /*
1024cba446e2SMitchell Horne  * Return a boolean value from an environment variable. This can be in
1025cba446e2SMitchell Horne  * numerical or string form, i.e. "1" or "true".
1026cba446e2SMitchell Horne  */
1027cba446e2SMitchell Horne int
1028cba446e2SMitchell Horne getenv_bool(const char *name, bool *data)
1029cba446e2SMitchell Horne {
1030cba446e2SMitchell Horne 	char *val;
1031cba446e2SMitchell Horne 	int ret = 0;
1032cba446e2SMitchell Horne 
1033cba446e2SMitchell Horne 	if (name == NULL)
1034cba446e2SMitchell Horne 		return (0);
1035cba446e2SMitchell Horne 
1036cba446e2SMitchell Horne 	val = kern_getenv(name);
1037cba446e2SMitchell Horne 	if (val == NULL)
1038cba446e2SMitchell Horne 		return (0);
1039cba446e2SMitchell Horne 
1040cba446e2SMitchell Horne 	if ((strcmp(val, "1") == 0) || (strcasecmp(val, "true") == 0)) {
1041cba446e2SMitchell Horne 		*data = true;
1042cba446e2SMitchell Horne 		ret = 1;
1043cba446e2SMitchell Horne 	} else if ((strcmp(val, "0") == 0) || (strcasecmp(val, "false") == 0)) {
1044cba446e2SMitchell Horne 		*data = false;
1045cba446e2SMitchell Horne 		ret = 1;
1046cba446e2SMitchell Horne 	} else {
1047cba446e2SMitchell Horne 		/* Spit out a warning for malformed boolean variables. */
1048cba446e2SMitchell Horne 		printf("Environment variable %s has non-boolean value \"%s\"\n",
1049cba446e2SMitchell Horne 		    name, val);
1050cba446e2SMitchell Horne 	}
1051cba446e2SMitchell Horne 	freeenv(val);
1052cba446e2SMitchell Horne 
1053cba446e2SMitchell Horne 	return (ret);
1054cba446e2SMitchell Horne }
1055cba446e2SMitchell Horne 
1056cba446e2SMitchell Horne /*
1057cba446e2SMitchell Horne  * Wrapper around getenv_bool to easily check for true.
1058cba446e2SMitchell Horne  */
1059cba446e2SMitchell Horne bool
1060cba446e2SMitchell Horne getenv_is_true(const char *name)
1061cba446e2SMitchell Horne {
1062cba446e2SMitchell Horne 	bool val;
1063cba446e2SMitchell Horne 
1064cba446e2SMitchell Horne 	if (getenv_bool(name, &val) != 0)
1065cba446e2SMitchell Horne 		return (val);
1066cba446e2SMitchell Horne 	return (false);
1067cba446e2SMitchell Horne }
1068cba446e2SMitchell Horne 
1069cba446e2SMitchell Horne /*
1070cba446e2SMitchell Horne  * Wrapper around getenv_bool to easily check for false.
1071cba446e2SMitchell Horne  */
1072cba446e2SMitchell Horne bool
1073cba446e2SMitchell Horne getenv_is_false(const char *name)
1074cba446e2SMitchell Horne {
1075cba446e2SMitchell Horne 	bool val;
1076cba446e2SMitchell Horne 
1077cba446e2SMitchell Horne 	if (getenv_bool(name, &val) != 0)
1078cba446e2SMitchell Horne 		return (!val);
1079cba446e2SMitchell Horne 	return (false);
1080cba446e2SMitchell Horne }
1081cba446e2SMitchell Horne 
1082cba446e2SMitchell Horne /*
10836ba9413bSMike Smith  * Find the next entry after the one which (cp) falls within, return a
10846ba9413bSMike Smith  * pointer to its start or NULL if there are no more.
10856ba9413bSMike Smith  */
10866ba9413bSMike Smith static char *
10876ba9413bSMike Smith kernenv_next(char *cp)
10886ba9413bSMike Smith {
10896692ac66SPeter Wemm 
10906ba9413bSMike Smith 	if (cp != NULL) {
10916ba9413bSMike Smith 		while (*cp != 0)
10926ba9413bSMike Smith 			cp++;
10936ba9413bSMike Smith 		cp++;
10946ba9413bSMike Smith 		if (*cp == 0)
10956ba9413bSMike Smith 			cp = NULL;
10966ba9413bSMike Smith 	}
10976ba9413bSMike Smith 	return (cp);
10986ba9413bSMike Smith }
10996ba9413bSMike Smith 
110009786698SPeter Wemm void
110109786698SPeter Wemm tunable_int_init(void *data)
110209786698SPeter Wemm {
110309786698SPeter Wemm 	struct tunable_int *d = (struct tunable_int *)data;
110409786698SPeter Wemm 
110509786698SPeter Wemm 	TUNABLE_INT_FETCH(d->path, d->var);
110609786698SPeter Wemm }
110709786698SPeter Wemm 
110809786698SPeter Wemm void
1109b0e1e474SDag-Erling Smørgrav tunable_long_init(void *data)
1110b0e1e474SDag-Erling Smørgrav {
1111b0e1e474SDag-Erling Smørgrav 	struct tunable_long *d = (struct tunable_long *)data;
1112b0e1e474SDag-Erling Smørgrav 
1113b0e1e474SDag-Erling Smørgrav 	TUNABLE_LONG_FETCH(d->path, d->var);
1114b0e1e474SDag-Erling Smørgrav }
1115b0e1e474SDag-Erling Smørgrav 
1116b0e1e474SDag-Erling Smørgrav void
1117b0e1e474SDag-Erling Smørgrav tunable_ulong_init(void *data)
1118b0e1e474SDag-Erling Smørgrav {
1119b0e1e474SDag-Erling Smørgrav 	struct tunable_ulong *d = (struct tunable_ulong *)data;
1120b0e1e474SDag-Erling Smørgrav 
1121b0e1e474SDag-Erling Smørgrav 	TUNABLE_ULONG_FETCH(d->path, d->var);
1122b0e1e474SDag-Erling Smørgrav }
1123b0e1e474SDag-Erling Smørgrav 
1124b0e1e474SDag-Erling Smørgrav void
112515be49f5SWarner Losh tunable_int64_init(void *data)
112615be49f5SWarner Losh {
112715be49f5SWarner Losh 	struct tunable_int64 *d = (struct tunable_int64 *)data;
112815be49f5SWarner Losh 
112915be49f5SWarner Losh 	TUNABLE_INT64_FETCH(d->path, d->var);
113015be49f5SWarner Losh }
113115be49f5SWarner Losh 
113215be49f5SWarner Losh void
113315be49f5SWarner Losh tunable_uint64_init(void *data)
113415be49f5SWarner Losh {
113515be49f5SWarner Losh 	struct tunable_uint64 *d = (struct tunable_uint64 *)data;
113615be49f5SWarner Losh 
113715be49f5SWarner Losh 	TUNABLE_UINT64_FETCH(d->path, d->var);
113815be49f5SWarner Losh }
113915be49f5SWarner Losh 
114015be49f5SWarner Losh void
11417f41115eSPawel Jakub Dawidek tunable_quad_init(void *data)
11427f41115eSPawel Jakub Dawidek {
11437f41115eSPawel Jakub Dawidek 	struct tunable_quad *d = (struct tunable_quad *)data;
11447f41115eSPawel Jakub Dawidek 
11457f41115eSPawel Jakub Dawidek 	TUNABLE_QUAD_FETCH(d->path, d->var);
11467f41115eSPawel Jakub Dawidek }
11477f41115eSPawel Jakub Dawidek 
11487f41115eSPawel Jakub Dawidek void
1149cba446e2SMitchell Horne tunable_bool_init(void *data)
1150cba446e2SMitchell Horne {
1151cba446e2SMitchell Horne 	struct tunable_bool *d = (struct tunable_bool *)data;
1152cba446e2SMitchell Horne 
1153cba446e2SMitchell Horne 	TUNABLE_BOOL_FETCH(d->path, d->var);
1154cba446e2SMitchell Horne }
1155cba446e2SMitchell Horne 
1156cba446e2SMitchell Horne void
115709786698SPeter Wemm tunable_str_init(void *data)
115809786698SPeter Wemm {
115909786698SPeter Wemm 	struct tunable_str *d = (struct tunable_str *)data;
116009786698SPeter Wemm 
116109786698SPeter Wemm 	TUNABLE_STR_FETCH(d->path, d->var, d->size);
116209786698SPeter Wemm }
1163