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 2007 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 /* 29 * #define _POSIX_PTHREAD_SEMANTICS before #including <signal.h> so that we 30 * get the right (POSIX) version of sigwait(2). 31 */ 32 #define _POSIX_PTHREAD_SEMANTICS 33 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <sys/mman.h> 37 #include <sys/sysmacros.h> 38 #include <dhcp_svc_private.h> 39 #include <pthread.h> 40 #include <stdlib.h> 41 #include <dhcpmsg.h> 42 #include <assert.h> 43 #include <stddef.h> 44 #include <stdio.h> 45 #include <stdio_ext.h> 46 #include <string.h> 47 #include <unistd.h> 48 #include <signal.h> 49 #include <locale.h> 50 #include <synch.h> 51 #include <sys/resource.h> 52 53 #include "datastore.h" 54 #include "dsvclockd.h" 55 56 /* 57 * The DHCP service daemon synchronizes access to containers within a given 58 * datastore. Any datastore which is willing to accept the synchronization 59 * constraints imposed by the DHCP service daemon can use this daemon in 60 * lieu of rolling their own synchronization code. 61 * 62 * See $SRC/lib/libdhcpsvc/private/README.synch for more information. 63 */ 64 65 #ifndef TEXT_DOMAIN 66 #define TEXT_DOMAIN "SYS_TEST" 67 #endif 68 69 #define DSVCD_REAP_INTERVAL (60 * 60 * 24) /* seconds, thus once a day */ 70 #define DSVCD_REAP_THRESH (60 * 60) /* seconds, thus 1 hour stale */ 71 #define DSVCD_STACK_REDSIZE (8 * 1024) /* redzone size, in bytes */ 72 #define UD_RECLAIM_MAX 128 /* unlock door descriptors */ 73 74 /* 75 * Unlock descriptor -- one for each lock granted. This descriptor is used 76 * to subsequently unlock the granted lock (and to synchronize unlocking of 77 * the lock; see svc_unlock() below for details). 78 */ 79 typedef struct dsvcd_unlock_desc { 80 int ud_fd; 81 mutex_t ud_lock; 82 dsvcd_container_t *ud_cn; 83 struct dsvcd_unlock_desc *ud_next; 84 } dsvcd_unlock_desc_t; 85 86 static mutex_t ud_reclaim_lock = DEFAULTMUTEX; 87 static unsigned int ud_reclaim_count = 0; 88 static dsvcd_unlock_desc_t *ud_reclaim_list = NULL; 89 90 static void *reaper(void *); 91 static int daemonize(void); 92 static void *stack_create(unsigned int *); 93 static void stack_destroy(void *, unsigned int); 94 static void doorserv_create(door_info_t *); 95 static dsvcd_unlock_desc_t *ud_create(dsvcd_container_t *, int *); 96 static void ud_destroy(dsvcd_unlock_desc_t *, boolean_t); 97 static dsvcd_svc_t svc_lock, svc_unlock; 98 99 int 100 main(int argc, char **argv) 101 { 102 dsvcd_datastore_t **ds_table; 103 dsvc_datastore_t dd; 104 dsvc_synchtype_t synchtype; 105 char **modules; 106 unsigned int i, j; 107 int debug_level = 0; 108 boolean_t is_daemon = B_TRUE; 109 boolean_t is_verbose = B_FALSE; 110 int sig, nmodules, nsynchmods, c; 111 sigset_t sigset; 112 char signame[SIG2STR_MAX]; 113 char *progname; 114 void *stackbase; 115 unsigned int stacksize = 16 * 1024; 116 struct rlimit rl; 117 118 (void) setlocale(LC_ALL, ""); 119 (void) textdomain(TEXT_DOMAIN); 120 121 /* 122 * Mask all signals except SIGABRT; doing this here ensures that 123 * all threads created through door_create() have them masked too. 124 */ 125 (void) sigfillset(&sigset); 126 (void) sigdelset(&sigset, SIGABRT); 127 (void) thr_sigsetmask(SIG_BLOCK, &sigset, NULL); 128 129 /* 130 * Figure out our program name; just keep the final piece so that 131 * our dhcpmsg() messages don't get too long. 132 */ 133 progname = strrchr(argv[0], '/'); 134 if (progname != NULL) 135 progname++; 136 else 137 progname = argv[0]; 138 139 /* 140 * Set the door thread creation procedure so that all of our 141 * threads are created with thread stacks with backing store. 142 */ 143 (void) door_server_create(doorserv_create); 144 145 while ((c = getopt(argc, argv, "d:fv")) != EOF) { 146 switch (c) { 147 148 case 'd': 149 debug_level = atoi(optarg); 150 break; 151 152 case 'f': 153 is_daemon = B_FALSE; 154 break; 155 156 case 'v': 157 is_verbose = B_TRUE; 158 break; 159 160 case '?': 161 (void) fprintf(stderr, 162 gettext("usage: %s [-dn] [-f] [-v]\n"), progname); 163 return (EXIT_FAILURE); 164 165 default: 166 break; 167 } 168 } 169 170 if (geteuid() != 0) { 171 dhcpmsg_init(progname, B_FALSE, is_verbose, debug_level); 172 dhcpmsg(MSG_ERROR, "must be super-user"); 173 dhcpmsg_fini(); 174 return (EXIT_FAILURE); 175 } 176 177 if (is_daemon && daemonize() == 0) { 178 dhcpmsg_init(progname, B_FALSE, is_verbose, debug_level); 179 dhcpmsg(MSG_ERROR, "cannot become daemon, exiting"); 180 dhcpmsg_fini(); 181 return (EXIT_FAILURE); 182 } 183 184 dhcpmsg_init(progname, is_daemon, is_verbose, debug_level); 185 (void) atexit(dhcpmsg_fini); 186 187 /* 188 * Max out the number available descriptors since we need to 189 * allocate two per held lock. 190 */ 191 rl.rlim_cur = RLIM_INFINITY; 192 rl.rlim_max = RLIM_INFINITY; 193 if (setrlimit(RLIMIT_NOFILE, &rl) == -1) 194 dhcpmsg(MSG_ERR, "setrlimit failed"); 195 196 (void) enable_extended_FILE_stdio(-1, -1); 197 198 if (enumerate_dd(&modules, &nmodules) != DSVC_SUCCESS) { 199 dhcpmsg(MSG_ERROR, "cannot enumerate public modules, exiting"); 200 return (EXIT_FAILURE); 201 } 202 203 /* 204 * NOTE: this code assumes that a module that needs dsvclockd will 205 * always need it (even as the container version is ramped). If 206 * this becomes bogus in a future release, we'll have to make this 207 * logic more sophisticated. 208 */ 209 nsynchmods = nmodules; 210 for (i = 0; i < nmodules; i++) { 211 dd.d_resource = modules[i]; 212 dd.d_conver = DSVC_CUR_CONVER; 213 dd.d_location = ""; 214 if (module_synchtype(&dd, &synchtype) != DSVC_SUCCESS) { 215 dhcpmsg(MSG_WARNING, "cannot determine synchronization " 216 "type for `%s', skipping", modules[i]); 217 free(modules[i]); 218 modules[i] = NULL; 219 nsynchmods--; 220 continue; 221 } 222 if ((synchtype & DSVC_SYNCH_STRATMASK) != DSVC_SYNCH_DSVCD) { 223 free(modules[i]); 224 modules[i] = NULL; 225 nsynchmods--; 226 } 227 } 228 229 if (nsynchmods == 0) { 230 dhcpmsg(MSG_INFO, "no public modules need synchronization"); 231 return (EXIT_SUCCESS); 232 } 233 234 /* 235 * Allocate the datastore table; include one extra entry so that 236 * the table is NULL-terminated. 237 */ 238 ds_table = calloc(nsynchmods + 1, sizeof (dsvcd_datastore_t *)); 239 if (ds_table == NULL) { 240 dhcpmsg(MSG_ERR, "cannot allocate datastore table, exiting"); 241 return (EXIT_FAILURE); 242 } 243 ds_table[nsynchmods] = NULL; 244 245 /* 246 * Create the datastores (which implicitly creates the doors). 247 * then sit around and wait for requests to come in on the doors. 248 */ 249 for (i = 0, j = 0; i < nmodules; i++) { 250 if (modules[i] != NULL) { 251 ds_table[j] = ds_create(modules[i], svc_lock); 252 if (ds_table[j] == NULL) { 253 while (j-- > 0) 254 ds_destroy(ds_table[j]); 255 return (EXIT_FAILURE); 256 } 257 free(modules[i]); 258 j++; 259 } 260 } 261 free(modules); 262 263 stackbase = stack_create(&stacksize); 264 if (stackbase == NULL) 265 dhcpmsg(MSG_ERR, "cannot create reaper stack; containers " 266 "will not be reaped"); 267 else { 268 errno = thr_create(stackbase, stacksize, reaper, ds_table, 269 THR_DAEMON, NULL); 270 if (errno != 0) { 271 dhcpmsg(MSG_ERR, "cannot create reaper thread; " 272 "containers will not be reaped"); 273 stack_destroy(stackbase, stacksize); 274 } 275 } 276 277 /* 278 * Synchronously wait for a QUIT, TERM, or INT, then shutdown. 279 */ 280 (void) sigemptyset(&sigset); 281 (void) sigaddset(&sigset, SIGQUIT); 282 (void) sigaddset(&sigset, SIGTERM); 283 (void) sigaddset(&sigset, SIGINT); 284 285 (void) sigwait(&sigset, &sig); 286 if (sig != SIGTERM && sig != SIGQUIT && sig != SIGINT) 287 dhcpmsg(MSG_WARNING, "received unexpected signal"); 288 289 if (sig2str(sig, signame) == -1) 290 (void) strlcpy(signame, "???", sizeof (signame)); 291 292 dhcpmsg(MSG_INFO, "shutting down via SIG%s", signame); 293 294 for (i = 0; i < nsynchmods; i++) 295 ds_destroy(ds_table[i]); 296 297 return (EXIT_SUCCESS); 298 } 299 300 /* 301 * Sanity check that dsvcd_request_t `req' (which is `reqsize' bytes long) 302 * is a correctly formed request; if not, return an error which will be 303 * returned to the door caller. 304 */ 305 static int 306 check_door_req(dsvcd_request_t *req, size_t reqsize, size_t minsize) 307 { 308 door_cred_t cred; 309 310 if (req == NULL) { 311 dhcpmsg(MSG_WARNING, "empty request, ignoring"); 312 return (DSVC_SYNCH_ERR); 313 } 314 315 /* 316 * Check credentials; we don't allow any non-super-user requests 317 * since this would open a denial-of-service hole (since a lock 318 * could be checked out indefinitely). 319 */ 320 if (door_cred(&cred) != 0) { 321 dhcpmsg(MSG_WARNING, "request with unknown credentials"); 322 return (DSVC_ACCESS); 323 } 324 325 if (cred.dc_euid != 0) { 326 dhcpmsg(MSG_WARNING, "request with non-super-user credentials"); 327 return (DSVC_ACCESS); 328 } 329 330 /* 331 * Check the version and size; we check this before checking the 332 * size of the request structure since an "incompatible version" 333 * message is more helpful than a "short request" message. 334 */ 335 if (reqsize > offsetof(dsvcd_request_t, rq_version) && 336 req->rq_version != DSVCD_DOOR_VERSION) { 337 dhcpmsg(MSG_WARNING, "request with unsupported version `%d'", 338 req->rq_version); 339 return (DSVC_SYNCH_ERR); 340 } 341 342 if (reqsize < minsize) { 343 dhcpmsg(MSG_VERBOSE, "short request (%d bytes, minimum %d " 344 "bytes)", reqsize, minsize); 345 return (DSVC_SYNCH_ERR); 346 } 347 348 return (DSVC_SUCCESS); 349 } 350 351 352 /* 353 * Service a lock request `req' passed across the door for datastore `ds'. 354 * After verifying that the request is well-formed, locks the container and 355 * creates an "unlock" door descriptor that the client uses to unlock the 356 * door (either explicitly through door_call()) or implicitly through 357 * terminating abnormally). 358 */ 359 /* ARGSUSED */ 360 static void 361 svc_lock(void *cookie, dsvcd_request_t *req, size_t reqsize, 362 door_desc_t *doorp, uint_t ndoors) 363 { 364 dsvcd_reply_t reply; 365 door_desc_t door_desc; 366 dsvcd_lock_request_t *lreq = (dsvcd_lock_request_t *)req; 367 dsvcd_datastore_t *ds = (dsvcd_datastore_t *)cookie; 368 dsvcd_container_t *cn; 369 dsvcd_unlock_desc_t *ud; 370 char conid[MAXPATHLEN]; 371 unsigned int attempts = 0; 372 373 reply.rp_version = DSVCD_DOOR_VERSION; 374 reply.rp_retval = check_door_req(req, reqsize, 375 sizeof (dsvcd_lock_request_t)); 376 if (reply.rp_retval != DSVC_SUCCESS) { 377 (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 378 return; 379 } 380 381 /* 382 * Verify that this is a lock request; if in the future we support 383 * other requests, we'll have to abstract this a bit. 384 */ 385 if (req->rq_reqtype != DSVCD_LOCK) { 386 dhcpmsg(MSG_WARNING, "unsupported request `%d' on lock " 387 "request door", req->rq_reqtype); 388 reply.rp_retval = DSVC_SYNCH_ERR; 389 (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 390 return; 391 } 392 if (lreq->lrq_locktype != DSVCD_RDLOCK && 393 lreq->lrq_locktype != DSVCD_WRLOCK) { 394 dhcpmsg(MSG_WARNING, "request for unsupported locktype `%d'", 395 lreq->lrq_locktype); 396 reply.rp_retval = DSVC_SYNCH_ERR; 397 (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 398 return; 399 } 400 401 /* 402 * Find the container; create if it doesn't already exist. We do 403 * this as a single operation to avoid race conditions. 404 */ 405 (void) snprintf(conid, sizeof (conid), "%s/%s%d_%s", lreq->lrq_loctoken, 406 ds->ds_name, lreq->lrq_conver, lreq->lrq_conname); 407 cn = ds_get_container(ds, conid, lreq->lrq_crosshost); 408 if (cn == NULL) { 409 reply.rp_retval = DSVC_NO_MEMORY; 410 (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 411 return; 412 } 413 414 /* 415 * We need another door descriptor which is passed back with the 416 * request. This descriptor is used when the caller wants to 417 * gracefully unlock or when the caller terminates abnormally. 418 */ 419 ud = ud_create(cn, &reply.rp_retval); 420 if (ud == NULL) { 421 ds_release_container(ds, cn); 422 (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 423 return; 424 } 425 426 /* 427 * We pass a duped door descriptor with the DOOR_RELEASE flag set 428 * instead of just passing the descriptor itself to handle the case 429 * where the client has gone away before we door_return(). Since 430 * we duped, the door descriptor itself will have a refcount of 2 431 * when we go to pass it to the client; if the client does not 432 * exist, the DOOR_RELEASE will drop the count from 2 to 1 which 433 * will cause a DOOR_UNREF_DATA call. 434 * 435 * In the regular (non-error) case, the door_return() will handoff 436 * the descriptor to the client, bumping the refcount to 3, and 437 * then the DOOR_RELEASE will drop the count to 2. If the client 438 * terminates abnormally after this point, the count will drop from 439 * 2 to 1 which will cause a DOOR_UNREF_DATA call. If the client 440 * unlocks gracefully, the refcount will still be 2 when the unlock 441 * door server procedure is called, and the unlock procedure will 442 * unlock the lock and note that the lock has been unlocked (so 443 * that we know the DOOR_UNREF_DATA call generated from the client 444 * subsequently closing the unlock descriptor is benign). 445 * 446 * Note that a DOOR_UNREF_DATA call will be generated *any time* 447 * the refcount goes from 2 to 1 -- even if *we* cause it to 448 * happen, which by default will happen in some of the error logic 449 * below (when we close the duped descriptor). To prevent this 450 * scenario, we tell ud_destroy() *not* to cache the unlock 451 * descriptor, which forces it to blow away the descriptor using 452 * door_revoke(), making the close() that follows benign. 453 */ 454 door_desc.d_attributes = DOOR_DESCRIPTOR|DOOR_RELEASE; 455 door_desc.d_data.d_desc.d_descriptor = dup(ud->ud_fd); 456 if (door_desc.d_data.d_desc.d_descriptor == -1) { 457 dhcpmsg(MSG_ERR, "cannot dup unlock door; denying %s " 458 "lock request", cn->cn_id); 459 ud_destroy(ud, B_TRUE); 460 ds_release_container(ds, cn); 461 reply.rp_retval = DSVC_NO_RESOURCES; 462 (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 463 return; 464 } 465 466 /* 467 * Acquire the actual read or write lock on the container. 468 */ 469 dhcpmsg(MSG_DEBUG, "tid %d: %s locking %s", thr_self(), 470 lreq->lrq_locktype == DSVCD_RDLOCK ? "read" : "write", cn->cn_id); 471 472 if (lreq->lrq_locktype == DSVCD_RDLOCK) 473 reply.rp_retval = cn_rdlock(cn, lreq->lrq_nonblock); 474 else if (lreq->lrq_locktype == DSVCD_WRLOCK) 475 reply.rp_retval = cn_wrlock(cn, lreq->lrq_nonblock); 476 477 dhcpmsg(MSG_DEBUG, "tid %d: %s %s lock operation: %s", thr_self(), 478 cn->cn_id, lreq->lrq_locktype == DSVCD_RDLOCK ? "read" : "write", 479 dhcpsvc_errmsg(reply.rp_retval)); 480 481 ds_release_container(ds, cn); 482 if (reply.rp_retval != DSVC_SUCCESS) { 483 ud_destroy(ud, B_FALSE); 484 (void) close(door_desc.d_data.d_desc.d_descriptor); 485 (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 486 return; 487 } 488 489 while (door_return((char *)&reply, sizeof (reply), &door_desc, 1) 490 == -1 && errno == EMFILE) { 491 if (lreq->lrq_nonblock) { 492 dhcpmsg(MSG_WARNING, "unable to grant lock; client" 493 " is out of file descriptors"); 494 (void) cn_unlock(cn); 495 ud_destroy(ud, B_FALSE); 496 (void) close(door_desc.d_data.d_desc.d_descriptor); 497 reply.rp_retval = DSVC_BUSY; 498 (void) door_return((char *)&reply, sizeof (reply), 499 NULL, 0); 500 return; 501 } 502 503 if (attempts++ == 0) { 504 dhcpmsg(MSG_WARNING, "unable to grant lock; client" 505 " is out of file descriptors (retrying)"); 506 } 507 (void) poll(NULL, 0, 100); 508 } 509 } 510 511 /* 512 * Service an unlock request `req' passed across the door associated with 513 * the unlock token `cookie'. We may be called explicitly (in which case 514 * the request is a well-formed dsvcd_request_t) or implicitly (in which 515 * case our request is set to the value DOOR_UNREF_DATA); this latter case 516 * occurs when a process holding a lock terminates. In either case, unlock 517 * the lock; in the implicit case, log a message as well. 518 */ 519 /* ARGSUSED */ 520 static void 521 svc_unlock(void *cookie, dsvcd_request_t *req, size_t reqsize, 522 door_desc_t *doorp, uint_t ndoors) 523 { 524 dsvcd_unlock_desc_t *ud = cookie; 525 dsvcd_container_t *cn; 526 dsvcd_reply_t reply; 527 528 /* 529 * Although unlock descriptors are handed out to only a single 530 * thread who has been granted a lock (ergo it seems that only one 531 * thread should be able to call us back), there's a potential race 532 * here if the process crashes while in this door_call(), since 533 * both this thread and the unref kernel upcall thread may run at 534 * the same time. Protect against this case with a mutex. 535 */ 536 (void) mutex_lock(&ud->ud_lock); 537 cn = ud->ud_cn; 538 539 /* 540 * First handle the case where the lock owner has closed the unlock 541 * descriptor, either because they have unlocked the lock and are 542 * thus done using the descriptor, or because they crashed. In the 543 * second case, print a message. 544 */ 545 if (req == DOOR_UNREF_DATA) { 546 /* 547 * The last reference is ours; we can free the descriptor. 548 */ 549 (void) mutex_unlock(&ud->ud_lock); 550 ud_destroy(ud, B_TRUE); 551 552 /* 553 * Normal case: the caller is closing the unlock descriptor 554 * on a lock they've already unlocked -- just return. 555 */ 556 if (cn == NULL) { 557 (void) door_return(NULL, 0, NULL, 0); 558 return; 559 } 560 561 /* 562 * Error case: the caller has crashed while holding the 563 * unlock descriptor (or is otherwise in violation of 564 * protocol). Since all datastores are required to be 565 * robust even if unexpected termination occurs, we assume 566 * the container is not corrupt, even if the process 567 * crashed with the write lock held. 568 */ 569 switch (cn_locktype(cn)) { 570 case DSVCD_RDLOCK: 571 dhcpmsg(MSG_WARNING, "process exited while reading " 572 "`%s'; unlocking", cn->cn_id); 573 (void) cn_unlock(cn); 574 break; 575 576 case DSVCD_WRLOCK: 577 dhcpmsg(MSG_WARNING, "process exited while writing " 578 "`%s'; unlocking", cn->cn_id); 579 dhcpmsg(MSG_WARNING, "note that this write operation " 580 "may or may not have succeeded"); 581 (void) cn_unlock(cn); 582 break; 583 584 case DSVCD_NOLOCK: 585 dhcpmsg(MSG_CRIT, "unreferenced unheld lock"); 586 break; 587 } 588 589 (void) door_return(NULL, 0, NULL, 0); 590 return; 591 } 592 593 /* 594 * Verify that this is a unlock request; if in the future we support 595 * other requests, we'll have to abstract this a bit. 596 */ 597 reply.rp_version = DSVCD_DOOR_VERSION; 598 reply.rp_retval = check_door_req(req, reqsize, 599 sizeof (dsvcd_unlock_request_t)); 600 if (reply.rp_retval != DSVC_SUCCESS) { 601 (void) mutex_unlock(&ud->ud_lock); 602 (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 603 return; 604 } 605 606 if (req->rq_reqtype != DSVCD_UNLOCK) { 607 dhcpmsg(MSG_WARNING, "unsupported request `%d' on unlock " 608 "request door", req->rq_reqtype); 609 (void) mutex_unlock(&ud->ud_lock); 610 reply.rp_retval = DSVC_SYNCH_ERR; 611 (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 612 return; 613 } 614 615 /* 616 * Attempt to unlock an already-unlocked container; log and return. 617 */ 618 if (cn == NULL) { 619 dhcpmsg(MSG_WARNING, "process tried to re-unlock a lock"); 620 (void) mutex_unlock(&ud->ud_lock); 621 reply.rp_retval = DSVC_SYNCH_ERR; 622 (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 623 return; 624 } 625 ud->ud_cn = NULL; 626 627 /* 628 * Unlock the container; note that after cn_unlock() has been done 629 * cn->cn_id is no longer accessible. 630 */ 631 dhcpmsg(MSG_DEBUG, "tid %d: unlocking %s", thr_self(), cn->cn_id); 632 reply.rp_retval = cn_unlock(cn); 633 dhcpmsg(MSG_DEBUG, "tid %d: unlock operation: %s", thr_self(), 634 dhcpsvc_errmsg(reply.rp_retval)); 635 636 /* 637 * Even though we've unlocked the lock, we cannot yet destroy the 638 * unlock descriptor (even if we revoke the door) because it's 639 * possible the unref thread is already waiting on ud_lock. 640 */ 641 (void) mutex_unlock(&ud->ud_lock); 642 (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 643 } 644 645 /* 646 * Reap containers that have not been recently used. 647 */ 648 static void * 649 reaper(void *ds_table_raw) 650 { 651 dsvcd_datastore_t **ds_table; 652 unsigned int i, nreaped; 653 654 ds_table = (dsvcd_datastore_t **)ds_table_raw; 655 for (;;) { 656 (void) sleep(DSVCD_REAP_INTERVAL); 657 for (i = 0; ds_table[i] != NULL; i++) { 658 nreaped = ds_reap_containers(ds_table[i], 659 DSVCD_REAP_THRESH); 660 if (nreaped > 0) { 661 dhcpmsg(MSG_VERBOSE, "reaped %u container " 662 "synchpoints from %s", nreaped, 663 ds_table[i]->ds_name); 664 } 665 } 666 } 667 /* NOTREACHED */ 668 return (NULL); 669 } 670 671 /* 672 * Daemonize the process. 673 */ 674 static int 675 daemonize(void) 676 { 677 switch (fork()) { 678 679 case -1: 680 return (0); 681 682 case 0: 683 /* 684 * Lose our controlling terminal, and become both a session 685 * leader and a process group leader. 686 */ 687 if (setsid() == -1) 688 return (0); 689 690 /* 691 * Under POSIX, a session leader can accidentally (through 692 * open(2)) acquire a controlling terminal if it does not 693 * have one. Just to be safe, fork() again so we are not a 694 * session leader. 695 */ 696 switch (fork()) { 697 698 case -1: 699 return (0); 700 701 case 0: 702 (void) signal(SIGHUP, SIG_IGN); 703 (void) chdir("/"); 704 (void) umask(022); 705 closefrom(0); 706 break; 707 708 default: 709 _exit(EXIT_SUCCESS); 710 } 711 break; 712 713 default: 714 _exit(EXIT_SUCCESS); 715 } 716 717 return (1); 718 } 719 720 /* 721 * Create an unlock descriptor for container `cn' -- returns an unlock 722 * descriptor on success, or NULL on failure; the reason for failure is in 723 * `retvalp'. Since creating door descriptors is expensive, we keep a few 724 * cache a small list of old descriptors around on a reclaim list and only 725 * allocate a new one if the list is empty. 726 */ 727 static dsvcd_unlock_desc_t * 728 ud_create(dsvcd_container_t *cn, int *retvalp) 729 { 730 dsvcd_unlock_desc_t *ud; 731 732 *retvalp = DSVC_SUCCESS; 733 (void) mutex_lock(&ud_reclaim_lock); 734 if (ud_reclaim_list != NULL) { 735 ud = ud_reclaim_list; 736 ud_reclaim_list = ud->ud_next; 737 ud_reclaim_count--; 738 (void) mutex_unlock(&ud_reclaim_lock); 739 } else { 740 (void) mutex_unlock(&ud_reclaim_lock); 741 ud = malloc(sizeof (dsvcd_unlock_desc_t)); 742 if (ud == NULL) { 743 dhcpmsg(MSG_WARNING, "cannot allocate unlock door " 744 "descriptor; denying %s lock request", cn->cn_id); 745 *retvalp = DSVC_NO_MEMORY; 746 return (NULL); 747 } 748 749 (void) mutex_init(&ud->ud_lock, USYNC_THREAD, NULL); 750 ud->ud_fd = door_create((void (*)())svc_unlock, ud, 751 DOOR_UNREF_MULTI | DOOR_REFUSE_DESC | DOOR_NO_CANCEL); 752 if (ud->ud_fd == -1) { 753 dhcpmsg(MSG_WARNING, "cannot create unlock door; " 754 "denying %s lock request", cn->cn_id); 755 free(ud); 756 *retvalp = DSVC_NO_RESOURCES; 757 return (NULL); 758 } 759 } 760 761 ud->ud_next = NULL; 762 ud->ud_cn = cn; 763 return (ud); 764 } 765 766 /* 767 * Destroy the unlock descriptor `ud' -- `ud' must be unlocked on entry. 768 * If there's room and `cacheable' is set, then, keep the unlock descriptor 769 * on the reclaim list to lower future creation cost. 770 */ 771 static void 772 ud_destroy(dsvcd_unlock_desc_t *ud, boolean_t cacheable) 773 { 774 assert(!MUTEX_HELD(&ud->ud_lock)); 775 776 ud->ud_cn = NULL; 777 (void) mutex_lock(&ud_reclaim_lock); 778 if (cacheable && ud_reclaim_count < UD_RECLAIM_MAX) { 779 ud->ud_next = ud_reclaim_list; 780 ud_reclaim_list = ud; 781 ud_reclaim_count++; 782 (void) mutex_unlock(&ud_reclaim_lock); 783 } else { 784 (void) mutex_unlock(&ud_reclaim_lock); 785 (void) door_revoke(ud->ud_fd); 786 (void) mutex_destroy(&ud->ud_lock); 787 free(ud); 788 } 789 } 790 791 /* 792 * Create a stack of `*stacksizep' bytes (rounded up to the nearest page) 793 * including a redzone for catching stack overflow. Set `stacksizep' to 794 * point to the actual usable size of the stack (i.e., everything but the 795 * redzone). Returns a pointer to the base of the stack (not including the 796 * redzone). 797 */ 798 static void * 799 stack_create(unsigned int *stacksizep) 800 { 801 caddr_t stackbase; 802 unsigned int redzone = roundup(DSVCD_STACK_REDSIZE, PAGESIZE); 803 unsigned int stacksize = *stacksizep; 804 805 if (stacksize < sysconf(_SC_THREAD_STACK_MIN)) 806 stacksize = sysconf(_SC_THREAD_STACK_MIN); 807 808 stacksize = roundup(stacksize, PAGESIZE); 809 stackbase = mmap(NULL, stacksize + redzone, PROT_READ | PROT_WRITE, 810 MAP_ANON | MAP_PRIVATE, -1, 0); 811 if (stackbase == MAP_FAILED) 812 return (NULL); 813 814 *stacksizep = stacksize; 815 (void) mprotect(stackbase, redzone, PROT_NONE); 816 return (stackbase + redzone); 817 } 818 819 /* 820 * Destroy the stack of `stacksize' bytes pointed to by `stackbase'. 821 */ 822 static void 823 stack_destroy(void *stackbase, unsigned int stacksize) 824 { 825 unsigned int redzone = roundup(DSVCD_STACK_REDSIZE, PAGESIZE); 826 827 (void) munmap((caddr_t)stackbase - redzone, stacksize + redzone); 828 } 829 830 /* 831 * Start function for door server threads; turns off thread cancellation 832 * and then parks in the kernel via door_return(). 833 */ 834 /* ARGSUSED */ 835 static void * 836 doorserv_thread(void *arg) 837 { 838 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 839 (void) door_return(NULL, 0, NULL, 0); 840 return (NULL); 841 } 842 843 /* 844 * Creation function for door server threads. We require door threads to 845 * have 32K of backed stack. This is a guess but will be more than 846 * sufficient for our uses, since door threads have a shallow call depth 847 * and the functions use little automatic storage. 848 */ 849 /* ARGSUSED */ 850 static void 851 doorserv_create(door_info_t *infop) 852 { 853 void *stackbase; 854 unsigned int stacksize = 32 * 1024; 855 856 stackbase = stack_create(&stacksize); 857 if (stackbase != NULL) { 858 errno = thr_create(stackbase, stacksize, doorserv_thread, NULL, 859 THR_BOUND | THR_DETACHED, NULL); 860 if (errno != 0) { 861 dhcpmsg(MSG_ERR, "cannot create door server thread; " 862 "server thread pool will not be grown"); 863 stack_destroy(stackbase, stacksize); 864 } 865 } 866 }