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