1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright (c) 2019 Joyent, Inc.
29 * Copyright (c) 2015 by Delphix. All rights reserved.
30 */
31
32 #include <ctype.h>
33 #include <errno.h>
34 #include <limits.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <dlfcn.h>
38 #include "umem_base.h"
39 #include "vmem_base.h"
40
41 /*
42 * A umem environment variable, like UMEM_DEBUG, is set to a series
43 * of items, seperated by ',':
44 *
45 * UMEM_DEBUG="audit=10,guards,firewall=512"
46 *
47 * This structure describes items. Each item has a name, type, and
48 * description. During processing, an item read from the user may
49 * be either "valid" or "invalid".
50 *
51 * A valid item has an argument, if required, and it is of the right
52 * form (doesn't overflow, doesn't contain any unexpected characters).
53 *
54 * If the item is valid, item_flag_target != NULL, and:
55 * type is not CLEARFLAG, then (*item_flag_target) |= item_flag_value
56 * type is CLEARFLAG, then (*item_flag_target) &= ~item_flag_value
57 */
58
59 #define UMEM_ENV_ITEM_MAX 512
60
61 struct umem_env_item;
62
63 typedef int arg_process_t(const struct umem_env_item *item, const char *value);
64 #define ARG_SUCCESS 0 /* processing successful */
65 #define ARG_BAD 1 /* argument had a bad value */
66
67 typedef struct umem_env_item {
68 const char *item_name; /* tag in environment variable */
69 const char *item_interface_stability;
70 enum {
71 ITEM_INVALID,
72 ITEM_FLAG, /* only a flag. No argument allowed */
73 ITEM_CLEARFLAG, /* only a flag, but clear instead of set */
74 ITEM_OPTUINT, /* optional integer argument */
75 ITEM_UINT, /* required integer argument */
76 ITEM_OPTSIZE, /* optional size_t argument */
77 ITEM_SIZE, /* required size_t argument */
78 ITEM_SPECIAL /* special argument processing */
79 } item_type;
80 const char *item_description;
81 uint_t *item_flag_target; /* the variable containing the flag */
82 uint_t item_flag_value; /* the value to OR in */
83 uint_t *item_uint_target; /* the variable to hold the integer */
84 size_t *item_size_target;
85 arg_process_t *item_special; /* callback for special handling */
86 } umem_env_item_t;
87
88 #ifndef UMEM_STANDALONE
89 static arg_process_t umem_backend_process;
90 static arg_process_t umem_allocator_process;
91 #endif
92
93 static arg_process_t umem_log_process;
94
95 static size_t umem_size_tempval;
96 static arg_process_t umem_size_process;
97
98 const char *____umem_environ_msg_options = "-- UMEM_OPTIONS --";
99
100 static umem_env_item_t umem_options_items[] = {
101 #ifndef UMEM_STANDALONE
102 { "backend", "Evolving", ITEM_SPECIAL,
103 "=sbrk for sbrk(2), =mmap for mmap(2)",
104 NULL, 0, NULL, NULL,
105 &umem_backend_process
106 },
107 { "allocator", "Evolving", ITEM_SPECIAL,
108 "=best, =first, =next, or =instant",
109 NULL, 0, NULL, NULL,
110 &umem_allocator_process
111 },
112 #endif
113
114 { "concurrency", "Private", ITEM_UINT,
115 "Max concurrency",
116 NULL, 0, &umem_max_ncpus
117 },
118 { "max_contention", "Private", ITEM_UINT,
119 "Maximum contention in a reap interval before the depot is "
120 "resized.",
121 NULL, 0, &umem_depot_contention
122 },
123 { "nomagazines", "Private", ITEM_FLAG,
124 "no caches will be multithreaded, and no caching will occur.",
125 &umem_flags, UMF_NOMAGAZINE
126 },
127 { "reap_interval", "Private", ITEM_UINT,
128 "Minimum time between reaps and updates, in seconds.",
129 NULL, 0, &umem_reap_interval
130 },
131
132 { "size_add", "Private", ITEM_SPECIAL,
133 "add a size to the cache size table",
134 NULL, 0, NULL,
135 &umem_size_tempval, &umem_size_process
136 },
137 { "size_clear", "Private", ITEM_SPECIAL,
138 "clear all but the largest size from the cache size table",
139 NULL, 0, NULL,
140 &umem_size_tempval, &umem_size_process
141 },
142 { "size_remove", "Private", ITEM_SPECIAL,
143 "remove a size from the cache size table",
144 NULL, 0, NULL,
145 &umem_size_tempval, &umem_size_process
146 },
147
148 #ifndef UMEM_STANDALONE
149 { "sbrk_minalloc", "Private", ITEM_SIZE,
150 "The minimum allocation chunk for the sbrk(2) heap.",
151 NULL, 0, NULL, &vmem_sbrk_minalloc
152 },
153 { "sbrk_pagesize", "Private", ITEM_SIZE,
154 "The preferred page size for the sbrk(2) heap.",
155 NULL, 0, NULL, &vmem_sbrk_pagesize
156 },
157 #endif
158 { "perthread_cache", "Evolving", ITEM_SIZE,
159 "Size (in bytes) of per-thread allocation cache",
160 NULL, 0, NULL, &umem_ptc_size
161 },
162 { NULL, "-- end of UMEM_OPTIONS --", ITEM_INVALID }
163 };
164
165 const char *____umem_environ_msg_debug = "-- UMEM_DEBUG --";
166
167 static umem_env_item_t umem_debug_items[] = {
168 { "default", "Unstable", ITEM_FLAG,
169 "audit,contents,guards",
170 &umem_flags,
171 UMF_AUDIT | UMF_CONTENTS | UMF_DEADBEEF | UMF_REDZONE
172 },
173 { "audit", "Unstable", ITEM_OPTUINT,
174 "Enable auditing. optionally =frames to set the number of "
175 "stored stack frames",
176 &umem_flags, UMF_AUDIT, &umem_stack_depth
177 },
178 { "contents", "Unstable", ITEM_OPTSIZE,
179 "Enable contents storing. UMEM_LOGGING=contents also "
180 "required. optionally =bytes to set the number of stored "
181 "bytes",
182 &umem_flags, UMF_CONTENTS, NULL, &umem_content_maxsave
183 },
184 { "guards", "Unstable", ITEM_FLAG,
185 "Enables guards and special patterns",
186 &umem_flags, UMF_DEADBEEF | UMF_REDZONE
187 },
188 { "verbose", "Unstable", ITEM_FLAG,
189 "Enables writing error messages to stderr",
190 &umem_output, 1
191 },
192
193 { "nosignal", "Private", ITEM_FLAG,
194 "Abort if called from a signal handler. Turns on 'audit'. "
195 "Note that this is not always a bug.",
196 &umem_flags, UMF_AUDIT | UMF_CHECKSIGNAL
197 },
198 { "firewall", "Private", ITEM_SIZE,
199 "=minbytes. Every object >= minbytes in size will have its "
200 "end against an unmapped page",
201 &umem_flags, UMF_FIREWALL, NULL, &umem_minfirewall
202 },
203 { "lite", "Private", ITEM_FLAG,
204 "debugging-lite",
205 &umem_flags, UMF_LITE
206 },
207 { "maxverify", "Private", ITEM_SIZE,
208 "=maxbytes, Maximum bytes to check when 'guards' is active. "
209 "Normally all bytes are checked.",
210 NULL, 0, NULL, &umem_maxverify
211 },
212 { "noabort", "Private", ITEM_CLEARFLAG,
213 "umem will not abort when a recoverable error occurs "
214 "(i.e. double frees, certain kinds of corruption)",
215 &umem_abort, 1
216 },
217 { "mtbf", "Private", ITEM_UINT,
218 "=mtbf, the mean time between injected failures. Works best "
219 "if prime.\n",
220 NULL, 0, &umem_mtbf
221 },
222 { "random", "Private", ITEM_FLAG,
223 "randomize flags on a per-cache basis",
224 &umem_flags, UMF_RANDOMIZE
225 },
226 { "allverbose", "Private", ITEM_FLAG,
227 "Enables writing all logged messages to stderr",
228 &umem_output, 2
229 },
230 { "checknull", "Private", ITEM_FLAG,
231 "Abort if an allocation would return null",
232 &umem_flags, UMF_CHECKNULL
233 },
234
235 { NULL, "-- end of UMEM_DEBUG --", ITEM_INVALID }
236 };
237
238 const char *____umem_environ_msg_logging = "-- UMEM_LOGGING --";
239
240 static umem_env_item_t umem_logging_items[] = {
241 { "transaction", "Unstable", ITEM_SPECIAL,
242 "If 'audit' is set in UMEM_DEBUG, the audit structures "
243 "from previous transactions are entered into this log.",
244 NULL, 0, NULL,
245 &umem_transaction_log_size, &umem_log_process
246 },
247 { "contents", "Unstable", ITEM_SPECIAL,
248 "If 'audit' is set in UMEM_DEBUG, the contents of objects "
249 "are recorded in this log as they are freed. If the "
250 "'contents' option is not set in UMEM_DEBUG, the first "
251 "256 bytes of each freed buffer will be saved.",
252 &umem_flags, UMF_CONTENTS, NULL,
253 &umem_content_log_size, &umem_log_process
254 },
255 { "fail", "Unstable", ITEM_SPECIAL,
256 "Records are entered into this log for every failed "
257 "allocation.",
258 NULL, 0, NULL,
259 &umem_failure_log_size, &umem_log_process
260 },
261
262 { "slab", "Private", ITEM_SPECIAL,
263 "Every slab created will be entered into this log.",
264 NULL, 0, NULL,
265 &umem_slab_log_size, &umem_log_process
266 },
267
268 { NULL, "-- end of UMEM_LOGGING --", ITEM_INVALID }
269 };
270
271 typedef struct umem_envvar {
272 const char *env_name;
273 const char *env_func;
274 umem_env_item_t *env_item_list;
275 const char *env_getenv_result;
276 const char *env_func_result;
277 } umem_envvar_t;
278
279 static umem_envvar_t umem_envvars[] = {
280 { "UMEM_DEBUG", "_umem_debug_init", umem_debug_items },
281 { "UMEM_OPTIONS", "_umem_options_init", umem_options_items },
282 { "UMEM_LOGGING", "_umem_logging_init", umem_logging_items },
283 { NULL, NULL, NULL }
284 };
285
286 static umem_envvar_t *env_current;
287 #define CURRENT (env_current->env_name)
288
289 static int
empty(const char * str)290 empty(const char *str)
291 {
292 char c;
293
294 while ((c = *str) != '\0' && isspace(c))
295 str++;
296
297 return (*str == '\0');
298 }
299
300 static int
item_uint_process(const umem_env_item_t * item,const char * item_arg)301 item_uint_process(const umem_env_item_t *item, const char *item_arg)
302 {
303 ulong_t result;
304 char *endptr = "";
305 int olderrno;
306
307 olderrno = errno;
308 errno = 0;
309
310 if (empty(item_arg)) {
311 goto badnumber;
312 }
313
314 result = strtoul(item_arg, &endptr, 10);
315
316 if (result == ULONG_MAX && errno == ERANGE) {
317 errno = olderrno;
318 goto overflow;
319 }
320 errno = olderrno;
321
322 if (*endptr != '\0')
323 goto badnumber;
324 if ((uint_t)result != result)
325 goto overflow;
326
327 (*item->item_uint_target) = (uint_t)result;
328 return (ARG_SUCCESS);
329
330 badnumber:
331 log_message("%s: %s: not a number\n", CURRENT, item->item_name);
332 return (ARG_BAD);
333
334 overflow:
335 log_message("%s: %s: overflowed\n", CURRENT, item->item_name);
336 return (ARG_BAD);
337 }
338
339 static int
item_size_process(const umem_env_item_t * item,const char * item_arg)340 item_size_process(const umem_env_item_t *item, const char *item_arg)
341 {
342 ulong_t result;
343 ulong_t result_arg;
344 char *endptr = "";
345 int olderrno;
346
347 if (empty(item_arg))
348 goto badnumber;
349
350 olderrno = errno;
351 errno = 0;
352
353 result_arg = strtoul(item_arg, &endptr, 10);
354
355 if (result_arg == ULONG_MAX && errno == ERANGE) {
356 errno = olderrno;
357 goto overflow;
358 }
359 errno = olderrno;
360
361 result = result_arg;
362
363 switch (*endptr) {
364 case 't':
365 case 'T':
366 result *= 1024;
367 if (result < result_arg)
368 goto overflow;
369 /*FALLTHRU*/
370 case 'g':
371 case 'G':
372 result *= 1024;
373 if (result < result_arg)
374 goto overflow;
375 /*FALLTHRU*/
376 case 'm':
377 case 'M':
378 result *= 1024;
379 if (result < result_arg)
380 goto overflow;
381 /*FALLTHRU*/
382 case 'k':
383 case 'K':
384 result *= 1024;
385 if (result < result_arg)
386 goto overflow;
387 endptr++; /* skip over the size character */
388 break;
389 default:
390 break; /* handled later */
391 }
392
393 if (*endptr != '\0')
394 goto badnumber;
395
396 (*item->item_size_target) = result;
397 return (ARG_SUCCESS);
398
399 badnumber:
400 log_message("%s: %s: not a number\n", CURRENT, item->item_name);
401 return (ARG_BAD);
402
403 overflow:
404 log_message("%s: %s: overflowed\n", CURRENT, item->item_name);
405 return (ARG_BAD);
406 }
407
408 static int
umem_log_process(const umem_env_item_t * item,const char * item_arg)409 umem_log_process(const umem_env_item_t *item, const char *item_arg)
410 {
411 if (item_arg != NULL) {
412 int ret;
413 ret = item_size_process(item, item_arg);
414 if (ret != ARG_SUCCESS)
415 return (ret);
416
417 if (*item->item_size_target == 0)
418 return (ARG_SUCCESS);
419 } else
420 *item->item_size_target = 64*1024;
421
422 umem_logging = 1;
423 return (ARG_SUCCESS);
424 }
425
426 static int
umem_size_process(const umem_env_item_t * item,const char * item_arg)427 umem_size_process(const umem_env_item_t *item, const char *item_arg)
428 {
429 const char *name = item->item_name;
430 void (*action_func)(size_t);
431
432 size_t result;
433
434 int ret;
435
436 if (strcmp(name, "size_clear") == 0) {
437 if (item_arg != NULL) {
438 log_message("%s: %s: does not take a value. ignored\n",
439 CURRENT, name);
440 return (ARG_BAD);
441 }
442 umem_alloc_sizes_clear();
443 return (ARG_SUCCESS);
444 } else if (strcmp(name, "size_add") == 0) {
445 action_func = umem_alloc_sizes_add;
446 } else if (strcmp(name, "size_remove") == 0) {
447 action_func = umem_alloc_sizes_remove;
448 } else {
449 log_message("%s: %s: internally unrecognized\n",
450 CURRENT, name, name, name);
451 return (ARG_BAD);
452 }
453
454 if (item_arg == NULL) {
455 log_message("%s: %s: requires a value. ignored\n",
456 CURRENT, name);
457 return (ARG_BAD);
458 }
459
460 ret = item_size_process(item, item_arg);
461 if (ret != ARG_SUCCESS)
462 return (ret);
463
464 result = *item->item_size_target;
465 action_func(result);
466 return (ARG_SUCCESS);
467 }
468
469 #ifndef UMEM_STANDALONE
470 static int
umem_backend_process(const umem_env_item_t * item,const char * item_arg)471 umem_backend_process(const umem_env_item_t *item, const char *item_arg)
472 {
473 const char *name = item->item_name;
474
475 if (item_arg == NULL)
476 goto fail;
477
478 if (strcmp(item_arg, "sbrk") == 0)
479 vmem_backend |= VMEM_BACKEND_SBRK;
480 else if (strcmp(item_arg, "mmap") == 0)
481 vmem_backend |= VMEM_BACKEND_MMAP;
482 else
483 goto fail;
484
485 return (ARG_SUCCESS);
486
487 fail:
488 log_message("%s: %s: must be %s=sbrk or %s=mmap\n",
489 CURRENT, name, name, name);
490 return (ARG_BAD);
491 }
492
493
494 static int
umem_allocator_process(const umem_env_item_t * item,const char * item_arg)495 umem_allocator_process(const umem_env_item_t *item, const char *item_arg)
496 {
497 const char *name = item->item_name;
498
499 if (item_arg == NULL)
500 goto fail;
501
502 if (strcmp(item_arg, "best") == 0)
503 vmem_allocator = VM_BESTFIT;
504 else if (strcmp(item_arg, "next") == 0)
505 vmem_allocator = VM_NEXTFIT;
506 else if (strcmp(item_arg, "first") == 0)
507 vmem_allocator = VM_FIRSTFIT;
508 else if (strcmp(item_arg, "instant") == 0)
509 vmem_allocator = 0;
510 else
511 goto fail;
512
513 return (ARG_SUCCESS);
514
515 fail:
516 log_message("%s: %s: must be %s=best, %s=next or %s=first\n",
517 CURRENT, name, name, name, name);
518 return (ARG_BAD);
519
520 }
521 #endif
522
523 static int
process_item(const umem_env_item_t * item,const char * item_arg)524 process_item(const umem_env_item_t *item, const char *item_arg)
525 {
526 int arg_required = 0;
527 arg_process_t *processor;
528
529 switch (item->item_type) {
530 case ITEM_FLAG:
531 case ITEM_CLEARFLAG:
532 case ITEM_OPTUINT:
533 case ITEM_OPTSIZE:
534 case ITEM_SPECIAL:
535 arg_required = 0;
536 break;
537
538 case ITEM_UINT:
539 case ITEM_SIZE:
540 arg_required = 1;
541 break;
542
543 default:
544 /*
545 * These are flags that aren't supported, so they'll error out
546 * below.
547 */
548 break;
549 }
550
551 switch (item->item_type) {
552 case ITEM_FLAG:
553 case ITEM_CLEARFLAG:
554 if (item_arg != NULL) {
555 log_message("%s: %s: does not take a value. ignored\n",
556 CURRENT, item->item_name);
557 return (1);
558 }
559 processor = NULL;
560 break;
561
562 case ITEM_UINT:
563 case ITEM_OPTUINT:
564 processor = item_uint_process;
565 break;
566
567 case ITEM_SIZE:
568 case ITEM_OPTSIZE:
569 processor = item_size_process;
570 break;
571
572 case ITEM_SPECIAL:
573 processor = item->item_special;
574 break;
575
576 default:
577 log_message("%s: %s: Invalid type. Ignored\n",
578 CURRENT, item->item_name);
579 return (1);
580 }
581
582 if (arg_required && item_arg == NULL) {
583 log_message("%s: %s: Required value missing\n",
584 CURRENT, item->item_name);
585 goto invalid;
586 }
587
588 if (item_arg != NULL || item->item_type == ITEM_SPECIAL) {
589 if (processor(item, item_arg) != ARG_SUCCESS)
590 goto invalid;
591 }
592
593 if (item->item_flag_target) {
594 if (item->item_type == ITEM_CLEARFLAG)
595 (*item->item_flag_target) &= ~item->item_flag_value;
596 else
597 (*item->item_flag_target) |= item->item_flag_value;
598 }
599 return (0);
600
601 invalid:
602 return (1);
603 }
604
605 #define ENV_SHORT_BYTES 10 /* bytes to print on error */
606 void
umem_process_value(umem_env_item_t * item_list,const char * beg,const char * end)607 umem_process_value(umem_env_item_t *item_list, const char *beg, const char *end)
608 {
609 char buf[UMEM_ENV_ITEM_MAX];
610 char *argptr;
611
612 size_t count;
613
614 while (beg < end && isspace(*beg))
615 beg++;
616
617 while (beg < end && isspace(*(end - 1)))
618 end--;
619
620 if (beg >= end) {
621 log_message("%s: empty option\n", CURRENT);
622 return;
623 }
624
625 count = end - beg;
626
627 if (count + 1 > sizeof (buf)) {
628 char outbuf[ENV_SHORT_BYTES + 1];
629 /*
630 * Have to do this, since sprintf("%10s",...) calls malloc()
631 */
632 (void) strncpy(outbuf, beg, ENV_SHORT_BYTES);
633 outbuf[ENV_SHORT_BYTES] = 0;
634
635 log_message("%s: argument \"%s...\" too long\n", CURRENT,
636 outbuf);
637 return;
638 }
639
640 (void) strncpy(buf, beg, count);
641 buf[count] = 0;
642
643 argptr = strchr(buf, '=');
644
645 if (argptr != NULL)
646 *argptr++ = 0;
647
648 for (; item_list->item_name != NULL; item_list++) {
649 if (strcmp(buf, item_list->item_name) == 0) {
650 (void) process_item(item_list, argptr);
651 return;
652 }
653 }
654 log_message("%s: '%s' not recognized\n", CURRENT, buf);
655 }
656
657 /*ARGSUSED*/
658 void
umem_setup_envvars(int invalid)659 umem_setup_envvars(int invalid)
660 {
661 umem_envvar_t *cur_env;
662 static volatile enum {
663 STATE_START,
664 STATE_GETENV,
665 STATE_DLOPEN,
666 STATE_DLSYM,
667 STATE_FUNC,
668 STATE_DONE
669 } state = STATE_START;
670 #ifndef UMEM_STANDALONE
671 void *h;
672 #endif
673
674 if (invalid) {
675 const char *where;
676 /*
677 * One of the calls below invoked malloc() recursively. We
678 * remove any partial results and return.
679 */
680
681 switch (state) {
682 case STATE_START:
683 where = "before getenv(3C) calls -- "
684 "getenv(3C) results ignored.";
685 break;
686 case STATE_GETENV:
687 where = "during getenv(3C) calls -- "
688 "getenv(3C) results ignored.";
689 break;
690 case STATE_DLOPEN:
691 where = "during dlopen(3C) call -- "
692 "_umem_*() results ignored.";
693 break;
694 case STATE_DLSYM:
695 where = "during dlsym(3C) call -- "
696 "_umem_*() results ignored.";
697 break;
698 case STATE_FUNC:
699 where = "during _umem_*() call -- "
700 "_umem_*() results ignored.";
701 break;
702 case STATE_DONE:
703 where = "after dlsym() or _umem_*() calls.";
704 break;
705 default:
706 where = "at unknown point -- "
707 "_umem_*() results ignored.";
708 break;
709 }
710
711 log_message("recursive allocation %s\n", where);
712
713 for (cur_env = umem_envvars; cur_env->env_name != NULL;
714 cur_env++) {
715 if (state == STATE_GETENV)
716 cur_env->env_getenv_result = NULL;
717 if (state != STATE_DONE)
718 cur_env->env_func_result = NULL;
719 }
720
721 state = STATE_DONE;
722 return;
723 }
724
725 state = STATE_GETENV;
726
727 for (cur_env = umem_envvars; cur_env->env_name != NULL; cur_env++) {
728 cur_env->env_getenv_result = getenv(cur_env->env_name);
729 if (state == STATE_DONE)
730 return; /* recursed */
731 }
732
733 #ifndef UMEM_STANDALONE
734 state = STATE_DLOPEN;
735
736 /* get a handle to the "a.out" object */
737 if ((h = dlopen(0, RTLD_FIRST | RTLD_LAZY)) != NULL) {
738 for (cur_env = umem_envvars; cur_env->env_name != NULL;
739 cur_env++) {
740 const char *(*func)(void);
741 const char *value;
742
743 state = STATE_DLSYM;
744 func = (const char *(*)(void))dlsym(h,
745 cur_env->env_func);
746
747 if (state == STATE_DONE)
748 break; /* recursed */
749
750 state = STATE_FUNC;
751 if (func != NULL) {
752 value = func();
753 if (state == STATE_DONE)
754 break; /* recursed */
755 cur_env->env_func_result = value;
756 }
757 }
758 (void) dlclose(h);
759 } else {
760 (void) dlerror(); /* snarf dlerror() */
761 }
762 #endif /* UMEM_STANDALONE */
763
764 state = STATE_DONE;
765 }
766
767 /*
768 * Process the environment variables.
769 */
770 void
umem_process_envvars(void)771 umem_process_envvars(void)
772 {
773 const char *value;
774 const char *end, *next;
775 umem_envvar_t *cur_env;
776
777 for (cur_env = umem_envvars; cur_env->env_name != NULL; cur_env++) {
778 env_current = cur_env;
779
780 value = cur_env->env_getenv_result;
781 if (value == NULL)
782 value = cur_env->env_func_result;
783
784 /* ignore if missing or empty */
785 if (value == NULL)
786 continue;
787
788 for (end = value; *end != '\0'; value = next) {
789 end = strchr(value, ',');
790 if (end != NULL)
791 next = end + 1; /* skip the comma */
792 else
793 next = end = value + strlen(value);
794
795 umem_process_value(cur_env->env_item_list, value, end);
796 }
797 }
798 }
799