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