Home | History | Annotate | Download | only in configd
      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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 #include <assert.h>
     29 #include <door.h>
     30 #include <errno.h>
     31 #include <fcntl.h>
     32 #include <limits.h>
     33 #include <priv.h>
     34 #include <procfs.h>
     35 #include <pthread.h>
     36 #include <signal.h>
     37 #include <stdarg.h>
     38 #include <stdio.h>
     39 #include <stdio_ext.h>
     40 #include <stdlib.h>
     41 #include <string.h>
     42 #include <syslog.h>
     43 #include <sys/corectl.h>
     44 #include <sys/resource.h>
     45 #include <sys/stat.h>
     46 #include <sys/wait.h>
     47 #include <ucontext.h>
     48 #include <unistd.h>
     49 
     50 #include "configd.h"
     51 
     52 /*
     53  * This file manages the overall startup and shutdown of configd, as well
     54  * as managing its door thread pool and per-thread datastructures.
     55  *
     56  * 1.  Per-thread Datastructures
     57  * -----------------------------
     58  * Each configd thread has an associated thread_info_t which contains its
     59  * current state.  A pointer is kept to this in TSD, keyed by thread_info_key.
     60  * The thread_info_ts for all threads in configd are kept on a single global
     61  * list, thread_list.  After creation, the state in the thread_info structure
     62  * is only modified by the associated thread, so no locking is needed.  A TSD
     63  * destructor removes the thread_info from the global list and frees it at
     64  * pthread_exit() time.
     65  *
     66  * Threads access their per-thread data using thread_self()
     67  *
     68  * The thread_list is protected by thread_lock, a leaf lock.
     69  *
     70  * 2. Door Thread Pool Management
     71  * ------------------------------
     72  * Whenever door_return(3door) returns from the kernel and there are no
     73  * other configd threads waiting for requests, libdoor automatically
     74  * invokes a function registered with door_server_create(), to request a new
     75  * door server thread.  The default function just creates a thread that calls
     76  * door_return(3door).  Unfortunately, since it can take a while for the new
     77  * thread to *get* to door_return(3door), a stream of requests can cause a
     78  * large number of threads to be created, even though they aren't all needed.
     79  *
     80  * In our callback, new_server_needed(), we limit ourself to two new threads
     81  * at a time -- this logic is handled in reserve_new_thread().  This keeps
     82  * us from creating an absurd number of threads in response to peaking load.
     83  */
     84 static pthread_key_t	thread_info_key;
     85 static pthread_attr_t	thread_attr;
     86 
     87 static pthread_mutex_t	thread_lock = PTHREAD_MUTEX_INITIALIZER;
     88 int			num_started;	/* number actually running */
     89 int			num_servers;	/* number in-progress or running */
     90 static uu_list_pool_t	*thread_pool;
     91 uu_list_t		*thread_list;
     92 
     93 static thread_info_t	main_thread_info;
     94 
     95 static int	finished;
     96 
     97 static pid_t	privileged_pid = 0;
     98 static int	privileged_psinfo_fd = -1;
     99 
    100 static int	privileged_user = 0;
    101 
    102 static priv_set_t *privileged_privs;
    103 
    104 static int	log_to_syslog = 0;
    105 
    106 int		is_main_repository = 1;
    107 
    108 int		max_repository_backups = 4;
    109 
    110 #define	CONFIGD_MAX_FDS		262144
    111 
    112 /*
    113  * Thanks, Mike
    114  */
    115 void
    116 abort_handler(int sig, siginfo_t *sip, ucontext_t *ucp)
    117 {
    118 	struct sigaction act;
    119 
    120 	(void) sigemptyset(&act.sa_mask);
    121 	act.sa_handler = SIG_DFL;
    122 	act.sa_flags = 0;
    123 	(void) sigaction(sig, &act, NULL);
    124 
    125 	(void) printstack(2);
    126 
    127 	if (sip != NULL && SI_FROMUSER(sip))
    128 		(void) pthread_kill(pthread_self(), sig);
    129 	(void) sigfillset(&ucp->uc_sigmask);
    130 	(void) sigdelset(&ucp->uc_sigmask, sig);
    131 	ucp->uc_flags |= UC_SIGMASK;
    132 	(void) setcontext(ucp);
    133 }
    134 
    135 /*
    136  * Don't want to have more than a couple thread creates outstanding
    137  */
    138 static int
    139 reserve_new_thread(void)
    140 {
    141 	(void) pthread_mutex_lock(&thread_lock);
    142 	assert(num_started >= 0);
    143 	if (num_servers > num_started + 1) {
    144 		(void) pthread_mutex_unlock(&thread_lock);
    145 		return (0);
    146 	}
    147 	++num_servers;
    148 	(void) pthread_mutex_unlock(&thread_lock);
    149 	return (1);
    150 }
    151 
    152 static void
    153 thread_info_free(thread_info_t *ti)
    154 {
    155 	uu_list_node_fini(ti, &ti->ti_node, thread_pool);
    156 	if (ti->ti_ucred != NULL)
    157 		uu_free(ti->ti_ucred);
    158 	uu_free(ti);
    159 }
    160 
    161 static void
    162 thread_exiting(void *arg)
    163 {
    164 	thread_info_t *ti = arg;
    165 
    166 	if (ti != NULL)
    167 		log_enter(&ti->ti_log);
    168 
    169 	(void) pthread_mutex_lock(&thread_lock);
    170 	if (ti != NULL) {
    171 		num_started--;
    172 		uu_list_remove(thread_list, ti);
    173 	}
    174 	assert(num_servers > 0);
    175 	--num_servers;
    176 
    177 	if (num_servers == 0) {
    178 		configd_critical("no door server threads\n");
    179 		abort();
    180 	}
    181 	(void) pthread_mutex_unlock(&thread_lock);
    182 
    183 	if (ti != NULL && ti != &main_thread_info)
    184 		thread_info_free(ti);
    185 }
    186 
    187 void
    188 thread_newstate(thread_info_t *ti, thread_state_t newstate)
    189 {
    190 	ti->ti_ucred_read = 0;			/* invalidate cached ucred */
    191 	if (newstate != ti->ti_state) {
    192 		ti->ti_prev_state = ti->ti_state;
    193 		ti->ti_state = newstate;
    194 		ti->ti_lastchange = gethrtime();
    195 	}
    196 }
    197 
    198 thread_info_t *
    199 thread_self(void)
    200 {
    201 	return (pthread_getspecific(thread_info_key));
    202 }
    203 
    204 /*
    205  * get_ucred() returns NULL if it was unable to get the credential
    206  * information.
    207  */
    208 ucred_t *
    209 get_ucred(void)
    210 {
    211 	thread_info_t *ti = thread_self();
    212 	ucred_t **ret = &ti->ti_ucred;
    213 
    214 	if (ti->ti_ucred_read)
    215 		return (*ret);			/* cached value */
    216 
    217 	if (door_ucred(ret) != 0)
    218 		return (NULL);
    219 	ti->ti_ucred_read = 1;
    220 
    221 	return (*ret);
    222 }
    223 
    224 int
    225 ucred_is_privileged(ucred_t *uc)
    226 {
    227 	const priv_set_t *ps;
    228 
    229 	if ((ps = ucred_getprivset(uc, PRIV_EFFECTIVE)) != NULL) {
    230 		if (priv_isfullset(ps))
    231 			return (1);		/* process has all privs */
    232 
    233 		if (privileged_privs != NULL &&
    234 		    priv_issubset(privileged_privs, ps))
    235 			return (1);		/* process has zone privs */
    236 	}
    237 
    238 	return (0);
    239 }
    240 
    241 /*
    242  * The purpose of this function is to get the audit session data for use in
    243  * generating SMF audit events.  We use a single audit session per client.
    244  *
    245  * get_audit_session() may return NULL.  It is legal to use a NULL pointer
    246  * in subsequent calls to adt_* functions.
    247  */
    248 adt_session_data_t *
    249 get_audit_session(void)
    250 {
    251 	thread_info_t	*ti = thread_self();
    252 
    253 	return (ti->ti_active_client->rc_adt_session);
    254 }
    255 
    256 static void *
    257 thread_start(void *arg)
    258 {
    259 	thread_info_t *ti = arg;
    260 
    261 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    262 
    263 	(void) pthread_mutex_lock(&thread_lock);
    264 	num_started++;
    265 	(void) uu_list_insert_after(thread_list, uu_list_last(thread_list),
    266 	    ti);
    267 	(void) pthread_mutex_unlock(&thread_lock);
    268 	(void) pthread_setspecific(thread_info_key, ti);
    269 
    270 	thread_newstate(ti, TI_DOOR_RETURN);
    271 
    272 	/*
    273 	 * Start handling door calls
    274 	 */
    275 	(void) door_return(NULL, 0, NULL, 0);
    276 	return (arg);
    277 }
    278 
    279 static void
    280 new_thread_needed(door_info_t *dip)
    281 {
    282 	thread_info_t *ti;
    283 
    284 	sigset_t new, old;
    285 
    286 	assert(dip == NULL);
    287 
    288 	if (!reserve_new_thread())
    289 		return;
    290 
    291 	if ((ti = uu_zalloc(sizeof (*ti))) == NULL)
    292 		goto fail;
    293 
    294 	uu_list_node_init(ti, &ti->ti_node, thread_pool);
    295 	ti->ti_state = TI_CREATED;
    296 	ti->ti_prev_state = TI_CREATED;
    297 
    298 	if ((ti->ti_ucred = uu_zalloc(ucred_size())) == NULL)
    299 		goto fail;
    300 
    301 	(void) sigfillset(&new);
    302 	(void) pthread_sigmask(SIG_SETMASK, &new, &old);
    303 	if ((errno = pthread_create(&ti->ti_thread, &thread_attr, thread_start,
    304 	    ti)) != 0) {
    305 		(void) pthread_sigmask(SIG_SETMASK, &old, NULL);
    306 		goto fail;
    307 	}
    308 
    309 	(void) pthread_sigmask(SIG_SETMASK, &old, NULL);
    310 	return;
    311 
    312 fail:
    313 	/*
    314 	 * Since the thread_info structure was never linked onto the
    315 	 * thread list, thread_exiting() can't handle the cleanup.
    316 	 */
    317 	thread_exiting(NULL);
    318 	if (ti != NULL)
    319 		thread_info_free(ti);
    320 }
    321 
    322 int
    323 create_connection(ucred_t *uc, repository_door_request_t *rp,
    324     size_t rp_size, int *out_fd)
    325 {
    326 	int flags;
    327 	int privileged = 0;
    328 	uint32_t debugflags = 0;
    329 	psinfo_t info;
    330 
    331 	if (privileged_pid != 0) {
    332 		/*
    333 		 * in privileged pid mode, we only allow connections from
    334 		 * our original parent -- the psinfo read verifies that
    335 		 * it is the same process which we started with.
    336 		 */
    337 		if (ucred_getpid(uc) != privileged_pid ||
    338 		    read(privileged_psinfo_fd, &info, sizeof (info)) !=
    339 		    sizeof (info))
    340 			return (REPOSITORY_DOOR_FAIL_PERMISSION_DENIED);
    341 
    342 		privileged = 1;			/* he gets full privileges */
    343 	} else if (privileged_user != 0) {
    344 		/*
    345 		 * in privileged user mode, only one particular user is
    346 		 * allowed to connect to us, and he can do anything.
    347 		 */
    348 		if (ucred_geteuid(uc) != privileged_user)
    349 			return (REPOSITORY_DOOR_FAIL_PERMISSION_DENIED);
    350 
    351 		privileged = 1;
    352 	}
    353 
    354 	/*
    355 	 * Check that rp, of size rp_size, is large enough to
    356 	 * contain field 'f'.  If so, write the value into *out, and return 1.
    357 	 * Otherwise, return 0.
    358 	 */
    359 #define	GET_ARG(rp, rp_size, f, out)					\
    360 	(((rp_size) >= offsetofend(repository_door_request_t, f)) ?	\
    361 	    ((*(out) = (rp)->f), 1) : 0)
    362 
    363 	if (!GET_ARG(rp, rp_size, rdr_flags, &flags))
    364 		return (REPOSITORY_DOOR_FAIL_BAD_REQUEST);
    365 
    366 #if (REPOSITORY_DOOR_FLAG_ALL != REPOSITORY_DOOR_FLAG_DEBUG)
    367 #error Need to update flag checks
    368 #endif
    369 
    370 	if (flags & ~REPOSITORY_DOOR_FLAG_ALL)
    371 		return (REPOSITORY_DOOR_FAIL_BAD_FLAG);
    372 
    373 	if (flags & REPOSITORY_DOOR_FLAG_DEBUG)
    374 		if (!GET_ARG(rp, rp_size, rdr_debug, &debugflags))
    375 			return (REPOSITORY_DOOR_FAIL_BAD_REQUEST);
    376 #undef GET_ARG
    377 
    378 	return (create_client(ucred_getpid(uc), debugflags, privileged,
    379 	    out_fd));
    380 }
    381 
    382 void
    383 configd_vlog(int severity, const char *prefix, const char *message,
    384     va_list args)
    385 {
    386 	if (log_to_syslog)
    387 		vsyslog(severity, message, args);
    388 	else {
    389 		flockfile(stderr);
    390 		if (prefix != NULL)
    391 			(void) fprintf(stderr, "%s", prefix);
    392 		(void) vfprintf(stderr, message, args);
    393 		if (message[0] == 0 || message[strlen(message) - 1] != '\n')
    394 			(void) fprintf(stderr, "\n");
    395 		funlockfile(stderr);
    396 	}
    397 }
    398 
    399 void
    400 configd_vcritical(const char *message, va_list args)
    401 {
    402 	configd_vlog(LOG_CRIT, "svc.configd: Fatal error: ", message, args);
    403 }
    404 
    405 void
    406 configd_critical(const char *message, ...)
    407 {
    408 	va_list args;
    409 	va_start(args, message);
    410 	configd_vcritical(message, args);
    411 	va_end(args);
    412 }
    413 
    414 void
    415 configd_info(const char *message, ...)
    416 {
    417 	va_list args;
    418 	va_start(args, message);
    419 	configd_vlog(LOG_INFO, "svc.configd: ", message, args);
    420 	va_end(args);
    421 }
    422 
    423 static void
    424 usage(const char *prog, int ret)
    425 {
    426 	(void) fprintf(stderr,
    427 	    "usage: %s [-np] [-d door_path] [-r repository_path]\n"
    428 	    "    [-t nonpersist_repository]\n", prog);
    429 	exit(ret);
    430 }
    431 
    432 /*ARGSUSED*/
    433 static void
    434 handler(int sig, siginfo_t *info, void *data)
    435 {
    436 	finished = 1;
    437 }
    438 
    439 static int pipe_fd = -1;
    440 
    441 static int
    442 daemonize_start(void)
    443 {
    444 	char data;
    445 	int status;
    446 
    447 	int filedes[2];
    448 	pid_t pid;
    449 
    450 	(void) close(0);
    451 	(void) dup2(2, 1);		/* stderr only */
    452 
    453 	if (pipe(filedes) < 0)
    454 		return (-1);
    455 
    456 	if ((pid = fork1()) < 0)
    457 		return (-1);
    458 
    459 	if (pid != 0) {
    460 		/*
    461 		 * parent
    462 		 */
    463 		struct sigaction act;
    464 
    465 		act.sa_sigaction = SIG_DFL;
    466 		(void) sigemptyset(&act.sa_mask);
    467 		act.sa_flags = 0;
    468 
    469 		(void) sigaction(SIGPIPE, &act, NULL);	/* ignore SIGPIPE */
    470 
    471 		(void) close(filedes[1]);
    472 		if (read(filedes[0], &data, 1) == 1) {
    473 			/* presume success */
    474 			_exit(CONFIGD_EXIT_OKAY);
    475 		}
    476 
    477 		status = -1;
    478 		(void) wait4(pid, &status, 0, NULL);
    479 		if (WIFEXITED(status))
    480 			_exit(WEXITSTATUS(status));
    481 		else
    482 			_exit(-1);
    483 	}
    484 
    485 	/*
    486 	 * child
    487 	 */
    488 	pipe_fd = filedes[1];
    489 	(void) close(filedes[0]);
    490 
    491 	/*
    492 	 * generic Unix setup
    493 	 */
    494 	(void) setsid();
    495 	(void) umask(0077);
    496 
    497 	return (0);
    498 }
    499 
    500 static void
    501 daemonize_ready(void)
    502 {
    503 	char data = '\0';
    504 
    505 	/*
    506 	 * wake the parent
    507 	 */
    508 	(void) write(pipe_fd, &data, 1);
    509 	(void) close(pipe_fd);
    510 }
    511 
    512 const char *
    513 regularize_path(const char *dir, const char *base, char *tmpbuf)
    514 {
    515 	if (base == NULL)
    516 		return (NULL);
    517 	if (base[0] == '/')
    518 		return (base);
    519 
    520 	if (snprintf(tmpbuf, PATH_MAX, "%s/%s", dir, base) >= PATH_MAX) {
    521 		(void) fprintf(stderr, "svc.configd: %s/%s: path too long\n",
    522 		    dir, base);
    523 		exit(CONFIGD_EXIT_BAD_ARGS);
    524 	}
    525 
    526 	return (tmpbuf);
    527 }
    528 
    529 int
    530 main(int argc, char *argv[])
    531 {
    532 	thread_info_t *ti = &main_thread_info;
    533 
    534 	char pidpath[sizeof ("/proc/" "/psinfo") + 10];
    535 
    536 	struct rlimit fd_new;
    537 
    538 	const char *endptr;
    539 	sigset_t myset;
    540 	int c;
    541 	int ret;
    542 	int fd;
    543 
    544 	char curdir[PATH_MAX];
    545 	char dbtmp[PATH_MAX];
    546 	char npdbtmp[PATH_MAX];
    547 	char doortmp[PATH_MAX];
    548 
    549 	const char *dbpath = NULL;
    550 	const char *npdbpath = NULL;
    551 	const char *doorpath = REPOSITORY_DOOR_NAME;
    552 	struct sigaction act;
    553 
    554 	int daemonize = 1;		/* default to daemonizing */
    555 	int have_npdb = 1;
    556 
    557 	closefrom(3);			/* get rid of extraneous fds */
    558 
    559 	if (getcwd(curdir, sizeof (curdir)) == NULL) {
    560 		(void) fprintf(stderr,
    561 		    "%s: unable to get current directory: %s\n",
    562 		    argv[0], strerror(errno));
    563 		exit(CONFIGD_EXIT_INIT_FAILED);
    564 	}
    565 
    566 	while ((c = getopt(argc, argv, "Dnpd:r:t:")) != -1) {
    567 		switch (c) {
    568 		case 'n':
    569 			daemonize = 0;
    570 			break;
    571 		case 'd':
    572 			doorpath = regularize_path(curdir, optarg, doortmp);
    573 			have_npdb = 0;		/* default to no non-persist */
    574 			break;
    575 		case 'p':
    576 			log_to_syslog = 0;	/* don't use syslog */
    577 
    578 			/*
    579 			 * If our parent exits while we're opening its /proc
    580 			 * psinfo, we're vulnerable to a pid wrapping.  To
    581 			 * protect against that, re-check our ppid after
    582 			 * opening it.
    583 			 */
    584 			privileged_pid = getppid();
    585 			(void) snprintf(pidpath, sizeof (pidpath),
    586 			    "/proc/%d/psinfo", privileged_pid);
    587 			if ((fd = open(pidpath, O_RDONLY)) < 0 ||
    588 			    getppid() != privileged_pid) {
    589 				(void) fprintf(stderr,
    590 				    "%s: unable to get parent info\n", argv[0]);
    591 				exit(CONFIGD_EXIT_BAD_ARGS);
    592 			}
    593 			privileged_psinfo_fd = fd;
    594 			break;
    595 		case 'r':
    596 			dbpath = regularize_path(curdir, optarg, dbtmp);
    597 			is_main_repository = 0;
    598 			break;
    599 		case 't':
    600 			npdbpath = regularize_path(curdir, optarg, npdbtmp);
    601 			is_main_repository = 0;
    602 			break;
    603 		default:
    604 			usage(argv[0], CONFIGD_EXIT_BAD_ARGS);
    605 			break;
    606 		}
    607 	}
    608 
    609 	/*
    610 	 * If we're not running as root, allow our euid full access, and
    611 	 * everyone else no access.
    612 	 */
    613 	if (privileged_pid == 0 && geteuid() != 0) {
    614 		privileged_user = geteuid();
    615 	}
    616 
    617 	privileged_privs = priv_str_to_set("zone", "", &endptr);
    618 	if (endptr != NULL && privileged_privs != NULL) {
    619 		priv_freeset(privileged_privs);
    620 		privileged_privs = NULL;
    621 	}
    622 
    623 	openlog("svc.configd", LOG_PID | LOG_CONS, LOG_DAEMON);
    624 	(void) setlogmask(LOG_UPTO(LOG_NOTICE));
    625 
    626 	/*
    627 	 * if a non-persist db is specified, always enable it
    628 	 */
    629 	if (npdbpath)
    630 		have_npdb = 1;
    631 
    632 	if (optind != argc)
    633 		usage(argv[0], CONFIGD_EXIT_BAD_ARGS);
    634 
    635 	if (daemonize) {
    636 		if (getuid() == 0)
    637 			(void) chdir("/");
    638 		if (daemonize_start() < 0) {
    639 			(void) perror("unable to daemonize");
    640 			exit(CONFIGD_EXIT_INIT_FAILED);
    641 		}
    642 	}
    643 	if (getuid() == 0)
    644 		(void) core_set_process_path(CONFIGD_CORE,
    645 		    strlen(CONFIGD_CORE) + 1, getpid());
    646 
    647 	/*
    648 	 * this should be enabled once we can drop privileges and still get
    649 	 * a core dump.
    650 	 */
    651 #if 0
    652 	/* turn off basic privileges we do not need */
    653 	(void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_FILE_LINK_ANY,
    654 	    PRIV_PROC_EXEC, PRIV_PROC_FORK, PRIV_PROC_SESSION, NULL);
    655 #endif
    656 
    657 	/* not that we can exec, but to be safe, shut them all off... */
    658 	(void) priv_set(PRIV_SET, PRIV_INHERITABLE, NULL);
    659 
    660 	(void) sigfillset(&act.sa_mask);
    661 
    662 	/* signals to ignore */
    663 	act.sa_sigaction = SIG_IGN;
    664 	act.sa_flags = 0;
    665 	(void) sigaction(SIGPIPE, &act, NULL);
    666 	(void) sigaction(SIGALRM, &act, NULL);
    667 	(void) sigaction(SIGUSR1, &act, NULL);
    668 	(void) sigaction(SIGUSR2, &act, NULL);
    669 	(void) sigaction(SIGPOLL, &act, NULL);
    670 
    671 	/* signals to abort on */
    672 	act.sa_sigaction = (void (*)(int, siginfo_t *, void *))&abort_handler;
    673 	act.sa_flags = SA_SIGINFO;
    674 
    675 	(void) sigaction(SIGABRT, &act, NULL);
    676 
    677 	/* signals to handle */
    678 	act.sa_sigaction = &handler;
    679 	act.sa_flags = SA_SIGINFO;
    680 
    681 	(void) sigaction(SIGHUP, &act, NULL);
    682 	(void) sigaction(SIGINT, &act, NULL);
    683 	(void) sigaction(SIGTERM, &act, NULL);
    684 
    685 	(void) sigemptyset(&myset);
    686 	(void) sigaddset(&myset, SIGHUP);
    687 	(void) sigaddset(&myset, SIGINT);
    688 	(void) sigaddset(&myset, SIGTERM);
    689 
    690 	if ((errno = pthread_attr_init(&thread_attr)) != 0) {
    691 		(void) perror("initializing");
    692 		exit(CONFIGD_EXIT_INIT_FAILED);
    693 	}
    694 
    695 	/*
    696 	 * Set the hard and soft limits to CONFIGD_MAX_FDS.
    697 	 */
    698 	fd_new.rlim_max = fd_new.rlim_cur = CONFIGD_MAX_FDS;
    699 	(void) setrlimit(RLIMIT_NOFILE, &fd_new);
    700 
    701 #ifndef NATIVE_BUILD /* Allow building on snv_38 and earlier; remove later. */
    702 	(void) enable_extended_FILE_stdio(-1, -1);
    703 #endif
    704 
    705 	if ((ret = backend_init(dbpath, npdbpath, have_npdb)) !=
    706 	    CONFIGD_EXIT_OKAY)
    707 		exit(ret);
    708 
    709 	if (!client_init())
    710 		exit(CONFIGD_EXIT_INIT_FAILED);
    711 
    712 	if (!rc_node_init())
    713 		exit(CONFIGD_EXIT_INIT_FAILED);
    714 
    715 	(void) pthread_attr_setdetachstate(&thread_attr,
    716 	    PTHREAD_CREATE_DETACHED);
    717 	(void) pthread_attr_setscope(&thread_attr, PTHREAD_SCOPE_SYSTEM);
    718 
    719 	if ((errno = pthread_key_create(&thread_info_key,
    720 	    thread_exiting)) != 0) {
    721 		perror("pthread_key_create");
    722 		exit(CONFIGD_EXIT_INIT_FAILED);
    723 	}
    724 
    725 	if ((thread_pool = uu_list_pool_create("thread_pool",
    726 	    sizeof (thread_info_t), offsetof(thread_info_t, ti_node),
    727 	    NULL, UU_LIST_POOL_DEBUG)) == NULL) {
    728 		configd_critical("uu_list_pool_create: %s\n",
    729 		    uu_strerror(uu_error()));
    730 		exit(CONFIGD_EXIT_INIT_FAILED);
    731 	}
    732 
    733 	if ((thread_list = uu_list_create(thread_pool, NULL, 0)) == NULL) {
    734 		configd_critical("uu_list_create: %s\n",
    735 		    uu_strerror(uu_error()));
    736 		exit(CONFIGD_EXIT_INIT_FAILED);
    737 	}
    738 
    739 	(void) memset(ti, '\0', sizeof (*ti));
    740 	uu_list_node_init(ti, &ti->ti_node, thread_pool);
    741 	(void) uu_list_insert_before(thread_list, uu_list_first(thread_list),
    742 	    ti);
    743 
    744 	ti->ti_thread = pthread_self();
    745 	ti->ti_state = TI_SIGNAL_WAIT;
    746 	ti->ti_prev_state = TI_SIGNAL_WAIT;
    747 
    748 	(void) pthread_setspecific(thread_info_key, ti);
    749 
    750 	(void) door_server_create(new_thread_needed);
    751 
    752 	if (!setup_main_door(doorpath)) {
    753 		configd_critical("Setting up main door failed.\n");
    754 		exit(CONFIGD_EXIT_DOOR_INIT_FAILED);
    755 	}
    756 
    757 	if (daemonize)
    758 		daemonize_ready();
    759 
    760 	(void) pthread_sigmask(SIG_BLOCK, &myset, NULL);
    761 	while (!finished) {
    762 		int sig = sigwait(&myset);
    763 		if (sig > 0) {
    764 			break;
    765 		}
    766 	}
    767 
    768 	backend_fini();
    769 
    770 	return (CONFIGD_EXIT_OKAY);
    771 }
    772