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