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 int len, i; 219 220 kenvp = malloc((KENV_SIZE + 1) * sizeof(char *), M_KENV, 221 M_WAITOK | M_ZERO); 222 i = 0; 223 for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) { 224 len = strlen(cp) + 1; 225 if (i < KENV_SIZE) { 226 kenvp[i] = malloc(len, M_KENV, M_WAITOK); 227 strcpy(kenvp[i++], cp); 228 } else 229 printf( 230 "WARNING: too many kenv strings, ignoring %s\n", 231 cp); 232 } 233 kenvp[i] = NULL; 234 235 mtx_init(&kenv_lock, "kernel environment", NULL, MTX_DEF); 236 dynamic_kenv = 1; 237 } 238 SYSINIT(kenv, SI_SUB_KMEM, SI_ORDER_ANY, init_dynamic_kenv, NULL); 239 240 void 241 freeenv(char *env) 242 { 243 244 if (dynamic_kenv) 245 free(env, M_KENV); 246 } 247 248 /* 249 * Internal functions for string lookup. 250 */ 251 static char * 252 _getenv_dynamic(const char *name, int *idx) 253 { 254 char *cp; 255 int len, i; 256 257 mtx_assert(&kenv_lock, MA_OWNED); 258 len = strlen(name); 259 for (cp = kenvp[0], i = 0; cp != NULL; cp = kenvp[++i]) { 260 if ((strncmp(cp, name, len) == 0) && 261 (cp[len] == '=')) { 262 if (idx != NULL) 263 *idx = i; 264 return (cp + len + 1); 265 } 266 } 267 return (NULL); 268 } 269 270 static char * 271 _getenv_static(const char *name) 272 { 273 char *cp, *ep; 274 int len; 275 276 for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) { 277 for (ep = cp; (*ep != '=') && (*ep != 0); ep++) 278 ; 279 if (*ep != '=') 280 continue; 281 len = ep - cp; 282 ep++; 283 if (!strncmp(name, cp, len) && name[len] == 0) 284 return (ep); 285 } 286 return (NULL); 287 } 288 289 /* 290 * Look up an environment variable by name. 291 * Return a pointer to the string if found. 292 * The pointer has to be freed with freeenv() 293 * after use. 294 */ 295 char * 296 getenv(const char *name) 297 { 298 char buf[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1]; 299 char *ret, *cp; 300 int len; 301 302 if (dynamic_kenv) { 303 mtx_lock(&kenv_lock); 304 cp = _getenv_dynamic(name, NULL); 305 if (cp != NULL) { 306 strcpy(buf, cp); 307 mtx_unlock(&kenv_lock); 308 len = strlen(buf) + 1; 309 ret = malloc(len, M_KENV, M_WAITOK); 310 strcpy(ret, buf); 311 } else { 312 mtx_unlock(&kenv_lock); 313 ret = NULL; 314 } 315 } else 316 ret = _getenv_static(name); 317 return (ret); 318 } 319 320 /* 321 * Test if an environment variable is defined. 322 */ 323 int 324 testenv(const char *name) 325 { 326 char *cp; 327 328 if (dynamic_kenv) { 329 mtx_lock(&kenv_lock); 330 cp = _getenv_dynamic(name, NULL); 331 mtx_unlock(&kenv_lock); 332 } else 333 cp = _getenv_static(name); 334 if (cp != NULL) 335 return (1); 336 return (0); 337 } 338 339 /* 340 * Set an environment variable by name. 341 */ 342 int 343 setenv(const char *name, const char *value) 344 { 345 char *buf, *cp, *oldenv; 346 int namelen, vallen, i; 347 348 KENV_CHECK; 349 350 namelen = strlen(name) + 1; 351 if (namelen > KENV_MNAMELEN) 352 return (-1); 353 vallen = strlen(value) + 1; 354 if (vallen > KENV_MVALLEN) 355 return (-1); 356 buf = malloc(namelen + vallen, M_KENV, M_WAITOK); 357 sprintf(buf, "%s=%s", name, value); 358 359 mtx_lock(&kenv_lock); 360 cp = _getenv_dynamic(name, &i); 361 if (cp != NULL) { 362 oldenv = kenvp[i]; 363 kenvp[i] = buf; 364 mtx_unlock(&kenv_lock); 365 free(oldenv, M_KENV); 366 } else { 367 /* We add the option if it wasn't found */ 368 for (i = 0; (cp = kenvp[i]) != NULL; i++) 369 ; 370 371 /* Bounds checking */ 372 if (i < 0 || i >= KENV_SIZE) { 373 free(buf, M_KENV); 374 mtx_unlock(&kenv_lock); 375 return (-1); 376 } 377 378 kenvp[i] = buf; 379 kenvp[i + 1] = NULL; 380 mtx_unlock(&kenv_lock); 381 } 382 return (0); 383 } 384 385 /* 386 * Unset an environment variable string. 387 */ 388 int 389 unsetenv(const char *name) 390 { 391 char *cp, *oldenv; 392 int i, j; 393 394 KENV_CHECK; 395 396 mtx_lock(&kenv_lock); 397 cp = _getenv_dynamic(name, &i); 398 if (cp != NULL) { 399 oldenv = kenvp[i]; 400 for (j = i + 1; kenvp[j] != NULL; j++) 401 kenvp[i++] = kenvp[j]; 402 kenvp[i] = NULL; 403 mtx_unlock(&kenv_lock); 404 free(oldenv, M_KENV); 405 return (0); 406 } 407 mtx_unlock(&kenv_lock); 408 return (-1); 409 } 410 411 /* 412 * Return a string value from an environment variable. 413 */ 414 int 415 getenv_string(const char *name, char *data, int size) 416 { 417 char *tmp; 418 419 tmp = getenv(name); 420 if (tmp != NULL) { 421 strlcpy(data, tmp, size); 422 freeenv(tmp); 423 return (1); 424 } else 425 return (0); 426 } 427 428 /* 429 * Return an integer value from an environment variable. 430 */ 431 int 432 getenv_int(const char *name, int *data) 433 { 434 quad_t tmp; 435 int rval; 436 437 rval = getenv_quad(name, &tmp); 438 if (rval) 439 *data = (int) tmp; 440 return (rval); 441 } 442 443 /* 444 * Return an unsigned integer value from an environment variable. 445 */ 446 int 447 getenv_uint(const char *name, unsigned int *data) 448 { 449 quad_t tmp; 450 int rval; 451 452 rval = getenv_quad(name, &tmp); 453 if (rval) 454 *data = (unsigned int) tmp; 455 return (rval); 456 } 457 458 /* 459 * Return a long value from an environment variable. 460 */ 461 int 462 getenv_long(const char *name, long *data) 463 { 464 quad_t tmp; 465 int rval; 466 467 rval = getenv_quad(name, &tmp); 468 if (rval) 469 *data = (long) tmp; 470 return (rval); 471 } 472 473 /* 474 * Return an unsigned long value from an environment variable. 475 */ 476 int 477 getenv_ulong(const char *name, unsigned long *data) 478 { 479 quad_t tmp; 480 int rval; 481 482 rval = getenv_quad(name, &tmp); 483 if (rval) 484 *data = (unsigned long) tmp; 485 return (rval); 486 } 487 488 /* 489 * Return a quad_t value from an environment variable. 490 */ 491 int 492 getenv_quad(const char *name, quad_t *data) 493 { 494 char *value; 495 char *vtp; 496 quad_t iv; 497 498 value = getenv(name); 499 if (value == NULL) 500 return (0); 501 iv = strtoq(value, &vtp, 0); 502 if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { 503 freeenv(value); 504 return (0); 505 } 506 switch (vtp[0]) { 507 case 't': case 'T': 508 iv *= 1024; 509 case 'g': case 'G': 510 iv *= 1024; 511 case 'm': case 'M': 512 iv *= 1024; 513 case 'k': case 'K': 514 iv *= 1024; 515 case '\0': 516 break; 517 default: 518 freeenv(value); 519 return (0); 520 } 521 *data = iv; 522 freeenv(value); 523 return (1); 524 } 525 526 /* 527 * Find the next entry after the one which (cp) falls within, return a 528 * pointer to its start or NULL if there are no more. 529 */ 530 static char * 531 kernenv_next(char *cp) 532 { 533 534 if (cp != NULL) { 535 while (*cp != 0) 536 cp++; 537 cp++; 538 if (*cp == 0) 539 cp = NULL; 540 } 541 return (cp); 542 } 543 544 void 545 tunable_int_init(void *data) 546 { 547 struct tunable_int *d = (struct tunable_int *)data; 548 549 TUNABLE_INT_FETCH(d->path, d->var); 550 } 551 552 void 553 tunable_long_init(void *data) 554 { 555 struct tunable_long *d = (struct tunable_long *)data; 556 557 TUNABLE_LONG_FETCH(d->path, d->var); 558 } 559 560 void 561 tunable_ulong_init(void *data) 562 { 563 struct tunable_ulong *d = (struct tunable_ulong *)data; 564 565 TUNABLE_ULONG_FETCH(d->path, d->var); 566 } 567 568 void 569 tunable_quad_init(void *data) 570 { 571 struct tunable_quad *d = (struct tunable_quad *)data; 572 573 TUNABLE_QUAD_FETCH(d->path, d->var); 574 } 575 576 void 577 tunable_str_init(void *data) 578 { 579 struct tunable_str *d = (struct tunable_str *)data; 580 581 TUNABLE_STR_FETCH(d->path, d->var, d->size); 582 } 583