configd.c revision 5777:e3276fcb93e7
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 */ 84static pthread_key_t thread_info_key; 85static pthread_attr_t thread_attr; 86 87static pthread_mutex_t thread_lock = PTHREAD_MUTEX_INITIALIZER; 88int num_started; /* number actually running */ 89int num_servers; /* number in-progress or running */ 90static uu_list_pool_t *thread_pool; 91uu_list_t *thread_list; 92 93static thread_info_t main_thread_info; 94 95static int finished; 96 97static pid_t privileged_pid = 0; 98static int privileged_psinfo_fd = -1; 99 100static int privileged_user = 0; 101 102static priv_set_t *privileged_privs; 103 104static int log_to_syslog = 0; 105 106int is_main_repository = 1; 107 108int max_repository_backups = 4; 109 110#define CONFIGD_MAX_FDS 262144 111 112/* 113 * Thanks, Mike 114 */ 115void 116abort_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 */ 138static int 139reserve_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 152static void 153thread_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 161static void 162thread_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 187void 188thread_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 198thread_info_t * 199thread_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 */ 208ucred_t * 209get_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 224int 225ucred_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 */ 248adt_session_data_t * 249get_audit_session(void) 250{ 251 thread_info_t *ti = thread_self(); 252 253 return (ti->ti_active_client->rc_adt_session); 254} 255 256static void * 257thread_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 279static void 280new_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 312fail: 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 322int 323create_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 382void 383configd_vcritical(const char *message, va_list args) 384{ 385 if (log_to_syslog) 386 vsyslog(LOG_CRIT, message, args); 387 else { 388 flockfile(stderr); 389 (void) fprintf(stderr, "svc.configd: Fatal error: "); 390 (void) vfprintf(stderr, message, args); 391 if (message[0] == 0 || message[strlen(message) - 1] != '\n') 392 (void) fprintf(stderr, "\n"); 393 funlockfile(stderr); 394 } 395} 396 397void 398configd_critical(const char *message, ...) 399{ 400 va_list args; 401 va_start(args, message); 402 configd_vcritical(message, args); 403 va_end(args); 404} 405 406static void 407usage(const char *prog, int ret) 408{ 409 (void) fprintf(stderr, 410 "usage: %s [-np] [-d door_path] [-r repository_path]\n" 411 " [-t nonpersist_repository]\n", prog); 412 exit(ret); 413} 414 415/*ARGSUSED*/ 416static void 417handler(int sig, siginfo_t *info, void *data) 418{ 419 finished = 1; 420} 421 422static int pipe_fd = -1; 423 424static int 425daemonize_start(void) 426{ 427 char data; 428 int status; 429 430 int filedes[2]; 431 pid_t pid; 432 433 (void) close(0); 434 (void) dup2(2, 1); /* stderr only */ 435 436 if (pipe(filedes) < 0) 437 return (-1); 438 439 if ((pid = fork1()) < 0) 440 return (-1); 441 442 if (pid != 0) { 443 /* 444 * parent 445 */ 446 struct sigaction act; 447 448 act.sa_sigaction = SIG_DFL; 449 (void) sigemptyset(&act.sa_mask); 450 act.sa_flags = 0; 451 452 (void) sigaction(SIGPIPE, &act, NULL); /* ignore SIGPIPE */ 453 454 (void) close(filedes[1]); 455 if (read(filedes[0], &data, 1) == 1) { 456 /* presume success */ 457 _exit(CONFIGD_EXIT_OKAY); 458 } 459 460 status = -1; 461 (void) wait4(pid, &status, 0, NULL); 462 if (WIFEXITED(status)) 463 _exit(WEXITSTATUS(status)); 464 else 465 _exit(-1); 466 } 467 468 /* 469 * child 470 */ 471 pipe_fd = filedes[1]; 472 (void) close(filedes[0]); 473 474 /* 475 * generic Unix setup 476 */ 477 (void) setsid(); 478 (void) umask(0077); 479 480 return (0); 481} 482 483static void 484daemonize_ready(void) 485{ 486 char data = '\0'; 487 488 /* 489 * wake the parent 490 */ 491 (void) write(pipe_fd, &data, 1); 492 (void) close(pipe_fd); 493} 494 495const char * 496regularize_path(const char *dir, const char *base, char *tmpbuf) 497{ 498 if (base == NULL) 499 return (NULL); 500 if (base[0] == '/') 501 return (base); 502 503 if (snprintf(tmpbuf, PATH_MAX, "%s/%s", dir, base) >= PATH_MAX) { 504 (void) fprintf(stderr, "svc.configd: %s/%s: path too long\n", 505 dir, base); 506 exit(CONFIGD_EXIT_BAD_ARGS); 507 } 508 509 return (tmpbuf); 510} 511 512int 513main(int argc, char *argv[]) 514{ 515 thread_info_t *ti = &main_thread_info; 516 517 char pidpath[sizeof ("/proc/" "/psinfo") + 10]; 518 519 struct rlimit fd_new; 520 521 const char *endptr; 522 sigset_t myset; 523 int c; 524 int ret; 525 int fd; 526 527 char curdir[PATH_MAX]; 528 char dbtmp[PATH_MAX]; 529 char npdbtmp[PATH_MAX]; 530 char doortmp[PATH_MAX]; 531 532 const char *dbpath = NULL; 533 const char *npdbpath = NULL; 534 const char *doorpath = REPOSITORY_DOOR_NAME; 535 struct sigaction act; 536 537 int daemonize = 1; /* default to daemonizing */ 538 int have_npdb = 1; 539 540 closefrom(3); /* get rid of extraneous fds */ 541 542 if (getcwd(curdir, sizeof (curdir)) == NULL) { 543 (void) fprintf(stderr, 544 "%s: unable to get current directory: %s\n", 545 argv[0], strerror(errno)); 546 exit(CONFIGD_EXIT_INIT_FAILED); 547 } 548 549 while ((c = getopt(argc, argv, "Dnpd:r:t:")) != -1) { 550 switch (c) { 551 case 'n': 552 daemonize = 0; 553 break; 554 case 'd': 555 doorpath = regularize_path(curdir, optarg, doortmp); 556 have_npdb = 0; /* default to no non-persist */ 557 break; 558 case 'p': 559 log_to_syslog = 0; /* don't use syslog */ 560 561 /* 562 * If our parent exits while we're opening its /proc 563 * psinfo, we're vulnerable to a pid wrapping. To 564 * protect against that, re-check our ppid after 565 * opening it. 566 */ 567 privileged_pid = getppid(); 568 (void) snprintf(pidpath, sizeof (pidpath), 569 "/proc/%d/psinfo", privileged_pid); 570 if ((fd = open(pidpath, O_RDONLY)) < 0 || 571 getppid() != privileged_pid) { 572 (void) fprintf(stderr, 573 "%s: unable to get parent info\n", argv[0]); 574 exit(CONFIGD_EXIT_BAD_ARGS); 575 } 576 privileged_psinfo_fd = fd; 577 break; 578 case 'r': 579 dbpath = regularize_path(curdir, optarg, dbtmp); 580 is_main_repository = 0; 581 break; 582 case 't': 583 npdbpath = regularize_path(curdir, optarg, npdbtmp); 584 is_main_repository = 0; 585 break; 586 default: 587 usage(argv[0], CONFIGD_EXIT_BAD_ARGS); 588 break; 589 } 590 } 591 592 /* 593 * If we're not running as root, allow our euid full access, and 594 * everyone else no access. 595 */ 596 if (privileged_pid == 0 && geteuid() != 0) { 597 privileged_user = geteuid(); 598 } 599 600 privileged_privs = priv_str_to_set("zone", "", &endptr); 601 if (endptr != NULL && privileged_privs != NULL) { 602 priv_freeset(privileged_privs); 603 privileged_privs = NULL; 604 } 605 606 openlog("svc.configd", LOG_PID | LOG_CONS, LOG_DAEMON); 607 (void) setlogmask(LOG_UPTO(LOG_NOTICE)); 608 609 /* 610 * if a non-persist db is specified, always enable it 611 */ 612 if (npdbpath) 613 have_npdb = 1; 614 615 if (optind != argc) 616 usage(argv[0], CONFIGD_EXIT_BAD_ARGS); 617 618 if (daemonize) { 619 if (getuid() == 0) 620 (void) chdir("/"); 621 if (daemonize_start() < 0) { 622 (void) perror("unable to daemonize"); 623 exit(CONFIGD_EXIT_INIT_FAILED); 624 } 625 } 626 if (getuid() == 0) 627 (void) core_set_process_path(CONFIGD_CORE, 628 strlen(CONFIGD_CORE) + 1, getpid()); 629 630 /* 631 * this should be enabled once we can drop privileges and still get 632 * a core dump. 633 */ 634#if 0 635 /* turn off basic privileges we do not need */ 636 (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_FILE_LINK_ANY, 637 PRIV_PROC_EXEC, PRIV_PROC_FORK, PRIV_PROC_SESSION, NULL); 638#endif 639 640 /* not that we can exec, but to be safe, shut them all off... */ 641 (void) priv_set(PRIV_SET, PRIV_INHERITABLE, NULL); 642 643 (void) sigfillset(&act.sa_mask); 644 645 /* signals to ignore */ 646 act.sa_sigaction = SIG_IGN; 647 act.sa_flags = 0; 648 (void) sigaction(SIGPIPE, &act, NULL); 649 (void) sigaction(SIGALRM, &act, NULL); 650 (void) sigaction(SIGUSR1, &act, NULL); 651 (void) sigaction(SIGUSR2, &act, NULL); 652 (void) sigaction(SIGPOLL, &act, NULL); 653 654 /* signals to abort on */ 655 act.sa_sigaction = (void (*)(int, siginfo_t *, void *))&abort_handler; 656 act.sa_flags = SA_SIGINFO; 657 658 (void) sigaction(SIGABRT, &act, NULL); 659 660 /* signals to handle */ 661 act.sa_sigaction = &handler; 662 act.sa_flags = SA_SIGINFO; 663 664 (void) sigaction(SIGHUP, &act, NULL); 665 (void) sigaction(SIGINT, &act, NULL); 666 (void) sigaction(SIGTERM, &act, NULL); 667 668 (void) sigemptyset(&myset); 669 (void) sigaddset(&myset, SIGHUP); 670 (void) sigaddset(&myset, SIGINT); 671 (void) sigaddset(&myset, SIGTERM); 672 673 if ((errno = pthread_attr_init(&thread_attr)) != 0) { 674 (void) perror("initializing"); 675 exit(CONFIGD_EXIT_INIT_FAILED); 676 } 677 678 /* 679 * Set the hard and soft limits to CONFIGD_MAX_FDS. 680 */ 681 fd_new.rlim_max = fd_new.rlim_cur = CONFIGD_MAX_FDS; 682 (void) setrlimit(RLIMIT_NOFILE, &fd_new); 683 684#ifndef NATIVE_BUILD /* Allow building on snv_38 and earlier; remove later. */ 685 (void) enable_extended_FILE_stdio(-1, -1); 686#endif 687 688 if ((ret = backend_init(dbpath, npdbpath, have_npdb)) != 689 CONFIGD_EXIT_OKAY) 690 exit(ret); 691 692 if (!client_init()) 693 exit(CONFIGD_EXIT_INIT_FAILED); 694 695 if (!rc_node_init()) 696 exit(CONFIGD_EXIT_INIT_FAILED); 697 698 (void) pthread_attr_setdetachstate(&thread_attr, 699 PTHREAD_CREATE_DETACHED); 700 (void) pthread_attr_setscope(&thread_attr, PTHREAD_SCOPE_SYSTEM); 701 702 if ((errno = pthread_key_create(&thread_info_key, 703 thread_exiting)) != 0) { 704 perror("pthread_key_create"); 705 exit(CONFIGD_EXIT_INIT_FAILED); 706 } 707 708 if ((thread_pool = uu_list_pool_create("thread_pool", 709 sizeof (thread_info_t), offsetof(thread_info_t, ti_node), 710 NULL, UU_LIST_POOL_DEBUG)) == NULL) { 711 configd_critical("uu_list_pool_create: %s\n", 712 uu_strerror(uu_error())); 713 exit(CONFIGD_EXIT_INIT_FAILED); 714 } 715 716 if ((thread_list = uu_list_create(thread_pool, NULL, 0)) == NULL) { 717 configd_critical("uu_list_create: %s\n", 718 uu_strerror(uu_error())); 719 exit(CONFIGD_EXIT_INIT_FAILED); 720 } 721 722 (void) memset(ti, '\0', sizeof (*ti)); 723 uu_list_node_init(ti, &ti->ti_node, thread_pool); 724 (void) uu_list_insert_before(thread_list, uu_list_first(thread_list), 725 ti); 726 727 ti->ti_thread = pthread_self(); 728 ti->ti_state = TI_SIGNAL_WAIT; 729 ti->ti_prev_state = TI_SIGNAL_WAIT; 730 731 (void) pthread_setspecific(thread_info_key, ti); 732 733 (void) door_server_create(new_thread_needed); 734 735 if (!setup_main_door(doorpath)) { 736 configd_critical("Setting up main door failed.\n"); 737 exit(CONFIGD_EXIT_DOOR_INIT_FAILED); 738 } 739 740 if (daemonize) 741 daemonize_ready(); 742 743 (void) pthread_sigmask(SIG_BLOCK, &myset, NULL); 744 while (!finished) { 745 int sig = sigwait(&myset); 746 if (sig > 0) { 747 break; 748 } 749 } 750 751 backend_fini(); 752 753 return (CONFIGD_EXIT_OKAY); 754} 755