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
27 /*
28 * The unified bootloader passes us a pointer to a preserved copy of
29 * bootstrap/kernel environment variables. We convert them to a
30 * dynamic array of strings later when the VM subsystem is up.
31 *
32 * We make these available through the kenv(2) syscall for userland
33 * and through getenv()/freeenv() setenv() unsetenv() testenv() for
34 * the kernel.
35 */
36
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <sys/proc.h>
43 #include <sys/queue.h>
44 #include <sys/lock.h>
45 #include <sys/malloc.h>
46 #include <sys/mutex.h>
47 #include <sys/priv.h>
48 #include <sys/kernel.h>
49 #include <sys/systm.h>
50 #include <sys/sysent.h>
51 #include <sys/sysproto.h>
52 #include <sys/libkern.h>
53 #include <sys/kenv.h>
54
55 #include <security/mac/mac_framework.h>
56
57 static MALLOC_DEFINE(M_KENV, "kenv", "kernel environment");
58
59 #define KENV_SIZE 512 /* Maximum number of environment strings */
60
61 /* pointer to the static environment */
62 char *kern_envp;
63 static char *kernenv_next(char *);
64
65 /* dynamic environment variables */
66 char **kenvp;
67 struct mtx kenv_lock;
68
69 /*
70 * No need to protect this with a mutex since SYSINITS are single threaded.
71 */
72 int dynamic_kenv = 0;
73
74 #define KENV_CHECK if (!dynamic_kenv) \
75 panic("%s: called before SI_SUB_KMEM", __func__)
76
77 int
78 kenv(td, uap)
79 struct thread *td;
80 struct kenv_args /* {
81 int what;
82 const char *name;
83 char *value;
84 int len;
85 } */ *uap;
86 {
87 char *name, *value, *buffer = NULL;
88 size_t len, done, needed, buflen;
89 int error, i;
90
91 KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = 0"));
92
93 error = 0;
94 if (uap->what == KENV_DUMP) {
95 #ifdef MAC
96 error = mac_kenv_check_dump(td->td_ucred);
97 if (error)
98 return (error);
99 #endif
100 done = needed = 0;
101 buflen = uap->len;
102 if (buflen > KENV_SIZE * (KENV_MNAMELEN + KENV_MVALLEN + 2))
103 buflen = KENV_SIZE * (KENV_MNAMELEN +
104 KENV_MVALLEN + 2);
105 if (uap->len > 0 && uap->value != NULL)
106 buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO);
107 mtx_lock(&kenv_lock);
108 for (i = 0; kenvp[i] != NULL; i++) {
109 len = strlen(kenvp[i]) + 1;
110 needed += len;
111 len = min(len, buflen - done);
112 /*
113 * If called with a NULL or insufficiently large
114 * buffer, just keep computing the required size.
115 */
116 if (uap->value != NULL && buffer != NULL && len > 0) {
117 bcopy(kenvp[i], buffer + done, len);
118 done += len;
119 }
120 }
121 mtx_unlock(&kenv_lock);
122 if (buffer != NULL) {
123 error = copyout(buffer, uap->value, done);
124 free(buffer, M_TEMP);
125 }
126 td->td_retval[0] = ((done == needed) ? 0 : needed);
127 return (error);
128 }
129
130 switch (uap->what) {
131 case KENV_SET:
132 error = priv_check(td, PRIV_KENV_SET);
133 if (error)
134 return (error);
135 break;
136
137 case KENV_UNSET:
138 error = priv_check(td, PRIV_KENV_UNSET);
139 if (error)
140 return (error);
141 break;
142 }
143
144 name = malloc(KENV_MNAMELEN, M_TEMP, M_WAITOK);
145
146 error = copyinstr(uap->name, name, KENV_MNAMELEN, NULL);
147 if (error)
148 goto done;
149
150 switch (uap->what) {
151 case KENV_GET:
152 #ifdef MAC
153 error = mac_kenv_check_get(td->td_ucred, name);
154 if (error)
155 goto done;
156 #endif
157 value = getenv(name);
158 if (value == NULL) {
159 error = ENOENT;
160 goto done;
161 }
162 len = strlen(value) + 1;
163 if (len > uap->len)
164 len = uap->len;
165 error = copyout(value, uap->value, len);
166 freeenv(value);
167 if (error)
168 goto done;
169 td->td_retval[0] = len;
170 break;
171 case KENV_SET:
172 len = uap->len;
173 if (len < 1) {
174 error = EINVAL;
175 goto done;
176 }
177 if (len > KENV_MVALLEN)
178 len = KENV_MVALLEN;
179 value = malloc(len, M_TEMP, M_WAITOK);
180 error = copyinstr(uap->value, value, len, NULL);
181 if (error) {
182 free(value, M_TEMP);
183 goto done;
184 }
185 #ifdef MAC
186 error = mac_kenv_check_set(td->td_ucred, name, value);
187 if (error == 0)
188 #endif
189 setenv(name, value);
190 free(value, M_TEMP);
191 break;
192 case KENV_UNSET:
193 #ifdef MAC
194 error = mac_kenv_check_unset(td->td_ucred, name);
195 if (error)
196 goto done;
197 #endif
198 error = unsetenv(name);
199 if (error)
200 error = ENOENT;
201 break;
202 default:
203 error = EINVAL;
204 break;
205 }
206 done:
207 free(name, M_TEMP);
208 return (error);
209 }
210
211 /*
212 * Setup the dynamic kernel environment.
213 */
214 static void
215 init_dynamic_kenv(void *data __unused)
216 {
217 char *cp;
218 size_t len;
219 int i;
220
221 kenvp = malloc((KENV_SIZE + 1) * sizeof(char *), M_KENV,
222 M_WAITOK | M_ZERO);
223 i = 0;
224 for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
225 len = strlen(cp) + 1;
226 if (len > KENV_MNAMELEN + 1 + KENV_MVALLEN + 1) {
227 printf("WARNING: too long kenv string, ignoring %s\n",
228 cp);
229 continue;
230 }
231 if (i < KENV_SIZE) {
232 kenvp[i] = malloc(len, M_KENV, M_WAITOK);
233 strcpy(kenvp[i++], cp);
234 } else
235 printf(
236 "WARNING: too many kenv strings, ignoring %s\n",
237 cp);
238 }
239 kenvp[i] = NULL;
240
241 mtx_init(&kenv_lock, "kernel environment", NULL, MTX_DEF);
242 dynamic_kenv = 1;
243 }
244 SYSINIT(kenv, SI_SUB_KMEM, SI_ORDER_ANY, init_dynamic_kenv, NULL);
245
246 void
247 freeenv(char *env)
248 {
249
250 if (dynamic_kenv)
251 free(env, M_KENV);
252 }
253
254 /*
255 * Internal functions for string lookup.
256 */
257 static char *
258 _getenv_dynamic(const char *name, int *idx)
259 {
260 char *cp;
261 int len, i;
262
263 mtx_assert(&kenv_lock, MA_OWNED);
264 len = strlen(name);
265 for (cp = kenvp[0], i = 0; cp != NULL; cp = kenvp[++i]) {
266 if ((strncmp(cp, name, len) == 0) &&
267 (cp[len] == '=')) {
268 if (idx != NULL)
269 *idx = i;
270 return (cp + len + 1);
271 }
272 }
273 return (NULL);
274 }
275
276 static char *
277 _getenv_static(const char *name)
278 {
279 char *cp, *ep;
280 int len;
281
282 for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
283 for (ep = cp; (*ep != '=') && (*ep != 0); ep++)
284 ;
285 if (*ep != '=')
286 continue;
287 len = ep - cp;
288 ep++;
289 if (!strncmp(name, cp, len) && name[len] == 0)
290 return (ep);
291 }
292 return (NULL);
293 }
294
295 /*
296 * Look up an environment variable by name.
297 * Return a pointer to the string if found.
298 * The pointer has to be freed with freeenv()
299 * after use.
300 */
301 char *
302 getenv(const char *name)
303 {
304 char buf[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1];
305 char *ret, *cp;
306 int len;
307
308 if (dynamic_kenv) {
309 mtx_lock(&kenv_lock);
310 cp = _getenv_dynamic(name, NULL);
311 if (cp != NULL) {
312 strcpy(buf, cp);
313 mtx_unlock(&kenv_lock);
314 len = strlen(buf) + 1;
315 ret = malloc(len, M_KENV, M_WAITOK);
316 strcpy(ret, buf);
317 } else {
318 mtx_unlock(&kenv_lock);
319 ret = NULL;
320 }
321 } else
322 ret = _getenv_static(name);
323 return (ret);
324 }
325
326 /*
327 * Test if an environment variable is defined.
328 */
329 int
330 testenv(const char *name)
331 {
332 char *cp;
333
334 if (dynamic_kenv) {
335 mtx_lock(&kenv_lock);
336 cp = _getenv_dynamic(name, NULL);
337 mtx_unlock(&kenv_lock);
338 } else
339 cp = _getenv_static(name);
340 if (cp != NULL)
341 return (1);
342 return (0);
343 }
344
345 /*
346 * Set an environment variable by name.
347 */
348 int
349 setenv(const char *name, const char *value)
350 {
351 char *buf, *cp, *oldenv;
352 int namelen, vallen, i;
353
354 KENV_CHECK;
355
356 namelen = strlen(name) + 1;
357 if (namelen > KENV_MNAMELEN)
358 return (-1);
359 vallen = strlen(value) + 1;
360 if (vallen > KENV_MVALLEN)
361 return (-1);
362 buf = malloc(namelen + vallen, M_KENV, M_WAITOK);
363 sprintf(buf, "%s=%s", name, value);
364
365 mtx_lock(&kenv_lock);
366 cp = _getenv_dynamic(name, &i);
367 if (cp != NULL) {
368 oldenv = kenvp[i];
369 kenvp[i] = buf;
370 mtx_unlock(&kenv_lock);
371 free(oldenv, M_KENV);
372 } else {
373 /* We add the option if it wasn't found */
374 for (i = 0; (cp = kenvp[i]) != NULL; i++)
375 ;
376
377 /* Bounds checking */
378 if (i < 0 || i >= KENV_SIZE) {
379 free(buf, M_KENV);
380 mtx_unlock(&kenv_lock);
381 return (-1);
382 }
383
384 kenvp[i] = buf;
385 kenvp[i + 1] = NULL;
386 mtx_unlock(&kenv_lock);
387 }
388 return (0);
389 }
390
391 /*
392 * Unset an environment variable string.
393 */
394 int
395 unsetenv(const char *name)
396 {
397 char *cp, *oldenv;
398 int i, j;
399
400 KENV_CHECK;
401
402 mtx_lock(&kenv_lock);
403 cp = _getenv_dynamic(name, &i);
404 if (cp != NULL) {
405 oldenv = kenvp[i];
406 for (j = i + 1; kenvp[j] != NULL; j++)
407 kenvp[i++] = kenvp[j];
408 kenvp[i] = NULL;
409 mtx_unlock(&kenv_lock);
410 free(oldenv, M_KENV);
411 return (0);
412 }
413 mtx_unlock(&kenv_lock);
414 return (-1);
415 }
416
417 /*
418 * Return a string value from an environment variable.
419 */
420 int
421 getenv_string(const char *name, char *data, int size)
422 {
423 char *tmp;
424
425 tmp = getenv(name);
426 if (tmp != NULL) {
427 strlcpy(data, tmp, size);
428 freeenv(tmp);
429 return (1);
430 } else
431 return (0);
432 }
433
434 /*
435 * Return an integer value from an environment variable.
436 */
437 int
438 getenv_int(const char *name, int *data)
439 {
440 quad_t tmp;
441 int rval;
442
443 rval = getenv_quad(name, &tmp);
444 if (rval)
445 *data = (int) tmp;
446 return (rval);
447 }
448
449 /*
450 * Return an unsigned integer value from an environment variable.
451 */
452 int
453 getenv_uint(const char *name, unsigned int *data)
454 {
455 quad_t tmp;
456 int rval;
457
458 rval = getenv_quad(name, &tmp);
459 if (rval)
460 *data = (unsigned int) tmp;
461 return (rval);
462 }
463
464 /*
465 * Return a long value from an environment variable.
466 */
467 int
468 getenv_long(const char *name, long *data)
469 {
470 quad_t tmp;
471 int rval;
472
473 rval = getenv_quad(name, &tmp);
474 if (rval)
475 *data = (long) tmp;
476 return (rval);
477 }
478
479 /*
480 * Return an unsigned long value from an environment variable.
481 */
482 int
483 getenv_ulong(const char *name, unsigned long *data)
484 {
485 quad_t tmp;
486 int rval;
487
488 rval = getenv_quad(name, &tmp);
489 if (rval)
490 *data = (unsigned long) tmp;
491 return (rval);
492 }
493
494 /*
495 * Return a quad_t value from an environment variable.
496 */
497 int
498 getenv_quad(const char *name, quad_t *data)
499 {
500 char *value;
501 char *vtp;
502 quad_t iv;
503
504 value = getenv(name);
505 if (value == NULL)
506 return (0);
507 iv = strtoq(value, &vtp, 0);
508 if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
509 freeenv(value);
510 return (0);
511 }
512 switch (vtp[0]) {
513 case 't': case 'T':
514 iv *= 1024;
515 case 'g': case 'G':
516 iv *= 1024;
517 case 'm': case 'M':
518 iv *= 1024;
519 case 'k': case 'K':
520 iv *= 1024;
521 case '\0':
522 break;
523 default:
524 freeenv(value);
525 return (0);
526 }
527 *data = iv;
528 freeenv(value);
529 return (1);
530 }
531
532 /*
533 * Find the next entry after the one which (cp) falls within, return a
534 * pointer to its start or NULL if there are no more.
535 */
536 static char *
537 kernenv_next(char *cp)
538 {
539
540 if (cp != NULL) {
541 while (*cp != 0)
542 cp++;
543 cp++;
544 if (*cp == 0)
545 cp = NULL;
546 }
547 return (cp);
548 }
549
550 void
551 tunable_int_init(void *data)
552 {
553 struct tunable_int *d = (struct tunable_int *)data;
554
555 TUNABLE_INT_FETCH(d->path, d->var);
556 }
557
558 void
559 tunable_long_init(void *data)
560 {
561 struct tunable_long *d = (struct tunable_long *)data;
562
563 TUNABLE_LONG_FETCH(d->path, d->var);
564 }
565
566 void
567 tunable_ulong_init(void *data)
568 {
569 struct tunable_ulong *d = (struct tunable_ulong *)data;
570
571 TUNABLE_ULONG_FETCH(d->path, d->var);
572 }
573
574 void
575 tunable_quad_init(void *data)
576 {
577 struct tunable_quad *d = (struct tunable_quad *)data;
578
579 TUNABLE_QUAD_FETCH(d->path, d->var);
580 }
581
582 void
583 tunable_str_init(void *data)
584 {
585 struct tunable_str *d = (struct tunable_str *)data;
586
587 TUNABLE_STR_FETCH(d->path, d->var, d->size);
588 }
Cache object: 7b82278731f7a46bf2970252fc64ae2e
|