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