1 /*- 2 * Copyright (c) 1998 Michael Smith 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: src/sys/kern/kern_environment.c,v 1.10.2.7 2002/05/07 09:57:16 bde Exp $ 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 dynamic array 32 * of strings later when the VM subsystem is up. 33 * We make these available using sysctl for both in-kernel and 34 * out-of-kernel consumers, as well as the k{get,set,unset,free,test}env() 35 * functions for in-kernel consumers. 36 * 37 * Note that the current sysctl infrastructure doesn't allow 38 * dynamic insertion or traversal through handled spaces. Grr. 39 * 40 * TODO: implement a sysctl handler to provide the functionality mentioned 41 * above. 42 */ 43 44 #include <sys/param.h> 45 #include <sys/kenv.h> 46 #include <sys/kernel.h> 47 #include <sys/libkern.h> 48 #include <sys/malloc.h> 49 #include <sys/priv.h> 50 #include <sys/spinlock.h> 51 #include <sys/spinlock2.h> 52 #include <sys/sysmsg.h> 53 #include <sys/systm.h> 54 #include <sys/sysctl.h> 55 56 /* exported variables */ 57 char *kern_envp; /* <sys/systm.h> */ 58 59 /* local variables */ 60 char **kenv_dynp; 61 int kenv_isdynamic; 62 struct spinlock kenv_dynlock; 63 64 /* constants */ 65 MALLOC_DEFINE(M_KENV, "kenv", "kernel environment dynamic storage"); 66 #define KENV_DYNMAXNUM 512 67 68 /* local prototypes */ 69 static char *kenv_getstring_dynamic(const char *name, int *idx); 70 static char *kenv_getstring_static(const char *name); 71 static char *kernenv_next(char *cp); 72 73 int 74 sys_kenv(struct sysmsg *sysmsg, const struct kenv_args *uap) 75 { 76 char *name, *value, *buffer = NULL; 77 size_t len, done, needed, buflen; 78 int error, i; 79 80 KASSERT(kenv_isdynamic, ("kenv: kenv_isdynamic = 0")); 81 82 error = 0; 83 if (uap->what == KENV_DUMP) { 84 done = needed = 0; 85 buflen = uap->len; 86 if (buflen > KENV_DYNMAXNUM * (KENV_MNAMELEN + KENV_MVALLEN + 2)) 87 buflen = KENV_DYNMAXNUM * 88 (KENV_MNAMELEN + KENV_MVALLEN + 2); 89 if (uap->len > 0 && uap->value != NULL) 90 buffer = kmalloc(buflen, M_TEMP, M_WAITOK|M_ZERO); 91 spin_lock(&kenv_dynlock); 92 for (i = 0; kenv_dynp[i] != NULL; i++) { 93 len = strlen(kenv_dynp[i]) + 1; 94 needed += len; 95 len = min(len, buflen - done); 96 /* 97 * If called with a NULL or insufficiently large 98 * buffer, just keep computing the required size. 99 */ 100 if (uap->value != NULL && buffer != NULL && len > 0) { 101 bcopy(kenv_dynp[i], buffer + done, len); 102 done += len; 103 } 104 } 105 spin_unlock(&kenv_dynlock); 106 if (buffer != NULL) { 107 error = copyout(buffer, uap->value, done); 108 kfree(buffer, M_TEMP); 109 } 110 sysmsg->sysmsg_result = ((done == needed) ? 0 : needed); 111 return (error); 112 } 113 114 switch (uap->what) { 115 case KENV_SET: 116 error = priv_check(curthread, PRIV_KENV_SET); 117 if (error) 118 return (error); 119 break; 120 121 case KENV_UNSET: 122 error = priv_check(curthread, PRIV_KENV_UNSET); 123 if (error) 124 return (error); 125 break; 126 } 127 128 name = kmalloc(KENV_MNAMELEN + 1, M_TEMP, M_WAITOK); 129 130 error = copyinstr(uap->name, name, KENV_MNAMELEN + 1, NULL); 131 if (error) 132 goto done; 133 134 switch (uap->what) { 135 case KENV_GET: 136 value = kgetenv(name); 137 if (value == NULL) { 138 error = ENOENT; 139 goto done; 140 } 141 len = strlen(value) + 1; 142 if (len > uap->len) 143 len = uap->len; 144 error = copyout(value, uap->value, len); 145 kfreeenv(value); 146 if (error) 147 goto done; 148 sysmsg-> sysmsg_result = len; 149 break; 150 case KENV_SET: 151 len = uap->len; 152 if (len < 1) { 153 error = EINVAL; 154 goto done; 155 } 156 if (len > KENV_MVALLEN + 1) 157 len = KENV_MVALLEN + 1; 158 value = kmalloc(len, M_TEMP, M_WAITOK); 159 error = copyinstr(uap->value, value, len, NULL); 160 if (error) { 161 kfree(value, M_TEMP); 162 goto done; 163 } 164 ksetenv(name, value); 165 kfree(value, M_TEMP); 166 break; 167 case KENV_UNSET: 168 error = kunsetenv(name); 169 if (error) 170 error = ENOENT; 171 break; 172 default: 173 error = EINVAL; 174 break; 175 } 176 done: 177 kfree(name, M_TEMP); 178 return (error); 179 } 180 181 /* 182 * Look up a string in the dynamic environment array. Must be called with 183 * kenv_dynlock held. 184 */ 185 static char * 186 kenv_getstring_dynamic(const char *name, int *idx) 187 { 188 char *cp; 189 int len, i; 190 191 len = strlen(name); 192 /* note: kunsetenv() never leaves NULL holes in the array */ 193 for (i = 0; (cp = kenv_dynp[i]) != NULL; i++) { 194 if ((strncmp(cp, name, len) == 0) && (cp[len] == '=')) { 195 if (idx != NULL) 196 *idx = i; 197 return(cp + len + 1); 198 } 199 } 200 return(NULL); 201 } 202 203 /* 204 * Look up a string in the static environment array. 205 */ 206 static char * 207 kenv_getstring_static(const char *name) 208 { 209 char *cp, *ep; 210 int len; 211 212 for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) { 213 for (ep = cp; (*ep != '=') && (*ep != 0); ep++) 214 ; 215 if (*ep != '=') 216 continue; 217 len = ep - cp; 218 ep++; 219 if (!strncmp(name, cp, len) && name[len] == 0) 220 return(ep); 221 } 222 return(NULL); 223 } 224 225 /* 226 * Look up an environment variable by name. 227 */ 228 char * 229 kgetenv(const char *name) 230 { 231 char buf[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1]; 232 char *cp, *ret; 233 int len; 234 235 if (kenv_isdynamic) { 236 spin_lock(&kenv_dynlock); 237 cp = kenv_getstring_dynamic(name, NULL); 238 if (cp != NULL) { 239 strcpy(buf, cp); 240 spin_unlock(&kenv_dynlock); 241 len = strlen(buf) + 1; 242 ret = kmalloc(len, M_KENV, M_WAITOK); 243 strcpy(ret, buf); 244 } else { 245 spin_unlock(&kenv_dynlock); 246 ret = NULL; 247 } 248 } else 249 ret = kenv_getstring_static(name); 250 return(ret); 251 } 252 253 /* 254 * Set an environment variable by name. 255 */ 256 int 257 ksetenv(const char *name, const char *value) 258 { 259 char *cp, *buf, *oldenv; 260 int namelen, vallen, i; 261 262 if (kenv_isdynamic) { 263 namelen = strlen(name) + 1; 264 vallen = strlen(value) + 1; 265 if ((namelen > KENV_MNAMELEN) || (vallen > KENV_MVALLEN)) 266 return(-1); 267 buf = kmalloc(namelen + vallen, M_KENV, M_WAITOK); 268 ksprintf(buf, "%s=%s", name, value); 269 spin_lock(&kenv_dynlock); 270 cp = kenv_getstring_dynamic(name, &i); 271 if (cp != NULL) { 272 /* replace existing environment variable */ 273 oldenv = kenv_dynp[i]; 274 kenv_dynp[i] = buf; 275 spin_unlock(&kenv_dynlock); 276 kfree(oldenv, M_KENV); 277 } else { 278 /* append new environment variable */ 279 for (i = 0; (cp = kenv_dynp[i]) != NULL; i++) 280 ; 281 /* bounds checking */ 282 if (i < 0 || i >= (KENV_DYNMAXNUM - 1)) { 283 kfree(buf, M_KENV); 284 spin_unlock(&kenv_dynlock); 285 return(-1); 286 } 287 kenv_dynp[i] = buf; 288 kenv_dynp[i + 1] = NULL; 289 spin_unlock(&kenv_dynlock); 290 } 291 return(0); 292 } else { 293 kprintf("WARNING: ksetenv: dynamic array not created yet\n"); 294 return(-1); 295 } 296 } 297 298 /* 299 * Unset an environment variable by name. 300 */ 301 int 302 kunsetenv(const char *name) 303 { 304 char *cp, *oldenv; 305 int i, j; 306 307 if (kenv_isdynamic) { 308 spin_lock(&kenv_dynlock); 309 cp = kenv_getstring_dynamic(name, &i); 310 if (cp != NULL) { 311 oldenv = kenv_dynp[i]; 312 /* move all pointers beyond the unset one down 1 step */ 313 for (j = i + 1; kenv_dynp[j] != NULL; j++) 314 kenv_dynp[i++] = kenv_dynp[j]; 315 kenv_dynp[i] = NULL; 316 spin_unlock(&kenv_dynlock); 317 kfree(oldenv, M_KENV); 318 return(0); 319 } 320 spin_unlock(&kenv_dynlock); 321 return(-1); 322 } else { 323 kprintf("WARNING: kunsetenv: dynamic array not created yet\n"); 324 return(-1); 325 } 326 } 327 328 /* 329 * Free an environment variable that has been copied for a consumer. 330 */ 331 void 332 kfreeenv(char *env) 333 { 334 if (kenv_isdynamic) 335 kfree(env, M_KENV); 336 } 337 338 /* 339 * Test if an environment variable is defined. 340 */ 341 int 342 ktestenv(const char *name) 343 { 344 char *cp; 345 346 if (kenv_isdynamic) { 347 spin_lock(&kenv_dynlock); 348 cp = kenv_getstring_dynamic(name, NULL); 349 spin_unlock(&kenv_dynlock); 350 } else 351 cp = kenv_getstring_static(name); 352 if (cp != NULL) 353 return(1); 354 return(0); 355 } 356 357 /* 358 * Return a string value from an environment variable. 359 */ 360 int 361 kgetenv_string(const char *name, char *data, int size) 362 { 363 char *tmp; 364 365 tmp = kgetenv(name); 366 if (tmp != NULL) { 367 strncpy(data, tmp, size); 368 data[size - 1] = 0; 369 kfreeenv(tmp); 370 return (1); 371 } else 372 return (0); 373 } 374 375 /* 376 * Return an integer value from an environment variable. 377 */ 378 int 379 kgetenv_int(const char *name, int *data) 380 { 381 quad_t tmp; 382 int rval; 383 384 rval = kgetenv_quad(name, &tmp); 385 if (rval) 386 *data = (int) tmp; 387 return (rval); 388 } 389 390 /* 391 * Return a long value from an environment variable. 392 */ 393 int 394 kgetenv_long(const char *name, long *data) 395 { 396 quad_t tmp; 397 int rval; 398 399 rval = kgetenv_quad(name, &tmp); 400 if (rval) 401 *data = (long)tmp; 402 return (rval); 403 } 404 405 /* 406 * Return an unsigned long value from an environment variable. 407 */ 408 int 409 kgetenv_ulong(const char *name, unsigned long *data) 410 { 411 quad_t tmp; 412 int rval; 413 414 rval = kgetenv_quad(name, &tmp); 415 if (rval) 416 *data = (unsigned long) tmp; 417 return (rval); 418 } 419 420 /* 421 * Return a quad_t value from an environment variable. 422 * 423 * A single character kmgtKMGT extension multiplies the value 424 * by 1024, 1024*1024, etc. 425 */ 426 int 427 kgetenv_quad(const char *name, quad_t *data) 428 { 429 char* value; 430 char* vtp; 431 quad_t iv; 432 433 if ((value = kgetenv(name)) == NULL) 434 return(0); 435 436 iv = strtoq(value, &vtp, 0); 437 switch(*vtp) { 438 case 't': 439 case 'T': 440 iv <<= 10; 441 /* fall through */ 442 case 'g': 443 case 'G': 444 iv <<= 10; 445 /* fall through */ 446 case 'm': 447 case 'M': 448 iv <<= 10; 449 /* fall through */ 450 case 'k': 451 case 'K': 452 iv <<= 10; 453 ++vtp; 454 break; 455 default: 456 break; 457 } 458 459 if ((vtp == value) || (*vtp != '\0')) { 460 kfreeenv(value); 461 return(0); 462 } 463 464 *data = iv; 465 kfreeenv(value); 466 return(1); 467 } 468 469 /* 470 * Boottime (static) kernel environment sysctl handler. 471 */ 472 static int 473 sysctl_kenv_boot(SYSCTL_HANDLER_ARGS) 474 { 475 int *name = (int *)arg1; 476 u_int namelen = arg2; 477 char *cp; 478 int i, error; 479 480 if (kern_envp == NULL) 481 return(ENOENT); 482 483 name++; 484 namelen--; 485 486 if (namelen != 1) 487 return(EINVAL); 488 489 cp = kern_envp; 490 for (i = 0; i < name[0]; i++) { 491 cp = kernenv_next(cp); 492 if (cp == NULL) 493 break; 494 } 495 496 if (cp == NULL) 497 return(ENOENT); 498 499 error = SYSCTL_OUT(req, cp, strlen(cp) + 1); 500 return (error); 501 } 502 503 SYSCTL_NODE(_kern, OID_AUTO, environment, CTLFLAG_RD, sysctl_kenv_boot, 504 "boottime (static) kernel environment space"); 505 506 /* 507 * Find the next entry after the one which (cp) falls within, return a 508 * pointer to its start or NULL if there are no more. 509 */ 510 static char * 511 kernenv_next(char *cp) 512 { 513 if (cp != NULL) { 514 while (*cp != 0) 515 cp++; 516 cp++; 517 if (*cp == 0) 518 cp = NULL; 519 } 520 return(cp); 521 } 522 523 /* 524 * TUNABLE_INT init functions. 525 */ 526 void 527 tunable_int_init(void *data) 528 { 529 struct tunable_int *d = (struct tunable_int *)data; 530 531 TUNABLE_INT_FETCH(d->path, d->var); 532 } 533 534 void 535 tunable_long_init(void *data) 536 { 537 struct tunable_long *d = (struct tunable_long *)data; 538 539 TUNABLE_LONG_FETCH(d->path, d->var); 540 } 541 542 void 543 tunable_ulong_init(void *data) 544 { 545 struct tunable_ulong *d = (struct tunable_ulong *)data; 546 547 TUNABLE_ULONG_FETCH(d->path, d->var); 548 } 549 550 void 551 tunable_quad_init(void *data) 552 { 553 struct tunable_quad *d = (struct tunable_quad *)data; 554 555 TUNABLE_QUAD_FETCH(d->path, d->var); 556 } 557 558 void 559 tunable_str_init(void *data) 560 { 561 struct tunable_str *d = (struct tunable_str *)data; 562 563 TUNABLE_STR_FETCH(d->path, d->var, d->size); 564 } 565 566 /* 567 * Create the dynamic environment array, and copy in the values from the static 568 * environment as passed by the bootloader. 569 */ 570 static void 571 kenv_init(void *dummy) 572 { 573 char *cp; 574 int len, i; 575 576 kenv_dynp = kmalloc(KENV_DYNMAXNUM * sizeof(char *), M_KENV, 577 M_WAITOK | M_ZERO); 578 579 /* copy the static environment to our dynamic environment */ 580 for (i = 0, cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) { 581 len = strlen(cp) + 1; 582 if (i < (KENV_DYNMAXNUM - 1)) { 583 kenv_dynp[i] = kmalloc(len, M_KENV, M_WAITOK); 584 strcpy(kenv_dynp[i++], cp); 585 } else 586 kprintf("WARNING: kenv: exhausted dynamic storage, " 587 "ignoring string %s\n", cp); 588 } 589 kenv_dynp[i] = NULL; 590 591 spin_init(&kenv_dynlock, "kenvdynlock"); 592 kenv_isdynamic = 1; 593 } 594 SYSINIT(kenv, SI_BOOT1_POST, SI_ORDER_ANY, kenv_init, NULL); 595