10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*1914Scasper * Common Development and Distribution License (the "License"). 6*1914Scasper * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*1914Scasper * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * #define _POSIX_PTHREAD_SEMANTICS before #including <signal.h> so that we 300Sstevel@tonic-gate * get the right (POSIX) version of sigwait(2). 310Sstevel@tonic-gate */ 320Sstevel@tonic-gate #define _POSIX_PTHREAD_SEMANTICS 330Sstevel@tonic-gate 340Sstevel@tonic-gate #include <sys/types.h> 350Sstevel@tonic-gate #include <sys/stat.h> 360Sstevel@tonic-gate #include <sys/mman.h> 370Sstevel@tonic-gate #include <sys/sysmacros.h> 380Sstevel@tonic-gate #include <dhcp_svc_private.h> 390Sstevel@tonic-gate #include <pthread.h> 400Sstevel@tonic-gate #include <stdlib.h> 410Sstevel@tonic-gate #include <dhcpmsg.h> 420Sstevel@tonic-gate #include <assert.h> 430Sstevel@tonic-gate #include <stddef.h> 440Sstevel@tonic-gate #include <stdio.h> 45*1914Scasper #include <stdio_ext.h> 460Sstevel@tonic-gate #include <string.h> 470Sstevel@tonic-gate #include <unistd.h> 480Sstevel@tonic-gate #include <signal.h> 490Sstevel@tonic-gate #include <locale.h> 500Sstevel@tonic-gate #include <synch.h> 510Sstevel@tonic-gate 520Sstevel@tonic-gate #include "datastore.h" 530Sstevel@tonic-gate #include "dsvclockd.h" 540Sstevel@tonic-gate 550Sstevel@tonic-gate /* 560Sstevel@tonic-gate * The DHCP service daemon synchronizes access to containers within a given 570Sstevel@tonic-gate * datastore. Any datastore which is willing to accept the synchronization 580Sstevel@tonic-gate * constraints imposed by the DHCP service daemon can use this daemon in 590Sstevel@tonic-gate * lieu of rolling their own synchronization code. 600Sstevel@tonic-gate * 610Sstevel@tonic-gate * See $SRC/lib/libdhcpsvc/private/README.synch for more information. 620Sstevel@tonic-gate */ 630Sstevel@tonic-gate 640Sstevel@tonic-gate #ifndef TEXT_DOMAIN 650Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" 660Sstevel@tonic-gate #endif 670Sstevel@tonic-gate 680Sstevel@tonic-gate #define DSVCD_REAP_INTERVAL (60 * 60 * 24) /* seconds, thus once a day */ 690Sstevel@tonic-gate #define DSVCD_REAP_THRESH (60 * 60) /* seconds, thus 1 hour stale */ 700Sstevel@tonic-gate #define DSVCD_STACK_REDSIZE (8 * 1024) /* redzone size, in bytes */ 710Sstevel@tonic-gate #define UD_RECLAIM_MAX 128 /* unlock door descriptors */ 720Sstevel@tonic-gate 730Sstevel@tonic-gate /* 740Sstevel@tonic-gate * Unlock descriptor -- one for each lock granted. This descriptor is used 750Sstevel@tonic-gate * to subsequently unlock the granted lock (and to synchronize unlocking of 760Sstevel@tonic-gate * the lock; see svc_unlock() below for details). 770Sstevel@tonic-gate */ 780Sstevel@tonic-gate typedef struct dsvcd_unlock_desc { 790Sstevel@tonic-gate int ud_fd; 800Sstevel@tonic-gate mutex_t ud_lock; 810Sstevel@tonic-gate dsvcd_container_t *ud_cn; 820Sstevel@tonic-gate struct dsvcd_unlock_desc *ud_next; 830Sstevel@tonic-gate } dsvcd_unlock_desc_t; 840Sstevel@tonic-gate 850Sstevel@tonic-gate static mutex_t ud_reclaim_lock = DEFAULTMUTEX; 860Sstevel@tonic-gate static unsigned int ud_reclaim_count = 0; 870Sstevel@tonic-gate static dsvcd_unlock_desc_t *ud_reclaim_list = NULL; 880Sstevel@tonic-gate 890Sstevel@tonic-gate static void *reaper(void *); 900Sstevel@tonic-gate static int daemonize(void); 910Sstevel@tonic-gate static void *stack_create(unsigned int *); 920Sstevel@tonic-gate static void stack_destroy(void *, unsigned int); 930Sstevel@tonic-gate static void doorserv_create(door_info_t *); 940Sstevel@tonic-gate static dsvcd_unlock_desc_t *ud_create(dsvcd_container_t *, int *); 950Sstevel@tonic-gate static void ud_destroy(dsvcd_unlock_desc_t *, boolean_t); 960Sstevel@tonic-gate static dsvcd_svc_t svc_lock, svc_unlock; 970Sstevel@tonic-gate 980Sstevel@tonic-gate int 990Sstevel@tonic-gate main(int argc, char **argv) 1000Sstevel@tonic-gate { 1010Sstevel@tonic-gate dsvcd_datastore_t **ds_table; 1020Sstevel@tonic-gate dsvc_datastore_t dd; 1030Sstevel@tonic-gate dsvc_synchtype_t synchtype; 1040Sstevel@tonic-gate char **modules; 1050Sstevel@tonic-gate unsigned int i, j; 1060Sstevel@tonic-gate int debug_level = 0; 1070Sstevel@tonic-gate boolean_t is_daemon = B_TRUE; 1080Sstevel@tonic-gate boolean_t is_verbose = B_FALSE; 1090Sstevel@tonic-gate int sig, nmodules, nsynchmods, c; 1100Sstevel@tonic-gate sigset_t sigset; 1110Sstevel@tonic-gate char signame[SIG2STR_MAX]; 1120Sstevel@tonic-gate char *progname; 1130Sstevel@tonic-gate void *stackbase; 1140Sstevel@tonic-gate unsigned int stacksize = 16 * 1024; 1150Sstevel@tonic-gate struct rlimit rl; 1160Sstevel@tonic-gate 1170Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 1180Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate /* 1210Sstevel@tonic-gate * Mask all signals except SIGABRT; doing this here ensures that 1220Sstevel@tonic-gate * all threads created through door_create() have them masked too. 1230Sstevel@tonic-gate */ 1240Sstevel@tonic-gate (void) sigfillset(&sigset); 1250Sstevel@tonic-gate (void) sigdelset(&sigset, SIGABRT); 1260Sstevel@tonic-gate (void) thr_sigsetmask(SIG_BLOCK, &sigset, NULL); 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate /* 1290Sstevel@tonic-gate * Figure out our program name; just keep the final piece so that 1300Sstevel@tonic-gate * our dhcpmsg() messages don't get too long. 1310Sstevel@tonic-gate */ 1320Sstevel@tonic-gate progname = strrchr(argv[0], '/'); 1330Sstevel@tonic-gate if (progname != NULL) 1340Sstevel@tonic-gate progname++; 1350Sstevel@tonic-gate else 1360Sstevel@tonic-gate progname = argv[0]; 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate /* 1390Sstevel@tonic-gate * Set the door thread creation procedure so that all of our 1400Sstevel@tonic-gate * threads are created with thread stacks with backing store. 1410Sstevel@tonic-gate */ 1420Sstevel@tonic-gate (void) door_server_create(doorserv_create); 1430Sstevel@tonic-gate 1440Sstevel@tonic-gate while ((c = getopt(argc, argv, "d:fv")) != EOF) { 1450Sstevel@tonic-gate switch (c) { 1460Sstevel@tonic-gate 1470Sstevel@tonic-gate case 'd': 1480Sstevel@tonic-gate debug_level = atoi(optarg); 1490Sstevel@tonic-gate break; 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate case 'f': 1520Sstevel@tonic-gate is_daemon = B_FALSE; 1530Sstevel@tonic-gate break; 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate case 'v': 1560Sstevel@tonic-gate is_verbose = B_TRUE; 1570Sstevel@tonic-gate break; 1580Sstevel@tonic-gate 1590Sstevel@tonic-gate case '?': 1600Sstevel@tonic-gate (void) fprintf(stderr, 1610Sstevel@tonic-gate gettext("usage: %s [-dn] [-f] [-v]\n"), progname); 1620Sstevel@tonic-gate return (EXIT_FAILURE); 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate default: 1650Sstevel@tonic-gate break; 1660Sstevel@tonic-gate } 1670Sstevel@tonic-gate } 1680Sstevel@tonic-gate 1690Sstevel@tonic-gate if (geteuid() != 0) { 1700Sstevel@tonic-gate dhcpmsg_init(progname, B_FALSE, is_verbose, debug_level); 1710Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "must be super-user"); 1720Sstevel@tonic-gate dhcpmsg_fini(); 1730Sstevel@tonic-gate return (EXIT_FAILURE); 1740Sstevel@tonic-gate } 1750Sstevel@tonic-gate 1760Sstevel@tonic-gate if (is_daemon && daemonize() == 0) { 1770Sstevel@tonic-gate dhcpmsg_init(progname, B_FALSE, is_verbose, debug_level); 1780Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "cannot become daemon, exiting"); 1790Sstevel@tonic-gate dhcpmsg_fini(); 1800Sstevel@tonic-gate return (EXIT_FAILURE); 1810Sstevel@tonic-gate } 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate dhcpmsg_init(progname, is_daemon, is_verbose, debug_level); 1840Sstevel@tonic-gate (void) atexit(dhcpmsg_fini); 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate /* 1870Sstevel@tonic-gate * Max out the number available descriptors since we need to 1880Sstevel@tonic-gate * allocate two per held lock. 1890Sstevel@tonic-gate */ 1900Sstevel@tonic-gate rl.rlim_cur = RLIM_INFINITY; 1910Sstevel@tonic-gate rl.rlim_max = RLIM_INFINITY; 1920Sstevel@tonic-gate if (setrlimit(RLIMIT_NOFILE, &rl) == -1) 1930Sstevel@tonic-gate dhcpmsg(MSG_ERR, "setrlimit failed"); 1940Sstevel@tonic-gate 195*1914Scasper (void) enable_extended_FILE_stdio(-1, -1); 196*1914Scasper 1970Sstevel@tonic-gate if (enumerate_dd(&modules, &nmodules) != DSVC_SUCCESS) { 1980Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "cannot enumerate public modules, exiting"); 1990Sstevel@tonic-gate return (EXIT_FAILURE); 2000Sstevel@tonic-gate } 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate /* 2030Sstevel@tonic-gate * NOTE: this code assumes that a module that needs dsvclockd will 2040Sstevel@tonic-gate * always need it (even as the container version is ramped). If 2050Sstevel@tonic-gate * this becomes bogus in a future release, we'll have to make this 2060Sstevel@tonic-gate * logic more sophisticated. 2070Sstevel@tonic-gate */ 2080Sstevel@tonic-gate nsynchmods = nmodules; 2090Sstevel@tonic-gate for (i = 0; i < nmodules; i++) { 2100Sstevel@tonic-gate dd.d_resource = modules[i]; 2110Sstevel@tonic-gate dd.d_conver = DSVC_CUR_CONVER; 2120Sstevel@tonic-gate dd.d_location = ""; 2130Sstevel@tonic-gate if (module_synchtype(&dd, &synchtype) != DSVC_SUCCESS) { 2140Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "cannot determine synchronization " 2150Sstevel@tonic-gate "type for `%s', skipping", modules[i]); 2160Sstevel@tonic-gate free(modules[i]); 2170Sstevel@tonic-gate modules[i] = NULL; 2180Sstevel@tonic-gate nsynchmods--; 2190Sstevel@tonic-gate continue; 2200Sstevel@tonic-gate } 2210Sstevel@tonic-gate if ((synchtype & DSVC_SYNCH_STRATMASK) != DSVC_SYNCH_DSVCD) { 2220Sstevel@tonic-gate free(modules[i]); 2230Sstevel@tonic-gate modules[i] = NULL; 2240Sstevel@tonic-gate nsynchmods--; 2250Sstevel@tonic-gate } 2260Sstevel@tonic-gate } 2270Sstevel@tonic-gate 2280Sstevel@tonic-gate if (nsynchmods == 0) { 2290Sstevel@tonic-gate dhcpmsg(MSG_INFO, "no public modules need synchronization"); 2300Sstevel@tonic-gate return (EXIT_SUCCESS); 2310Sstevel@tonic-gate } 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate /* 2340Sstevel@tonic-gate * Allocate the datastore table; include one extra entry so that 2350Sstevel@tonic-gate * the table is NULL-terminated. 2360Sstevel@tonic-gate */ 2370Sstevel@tonic-gate ds_table = calloc(nsynchmods + 1, sizeof (dsvcd_datastore_t *)); 2380Sstevel@tonic-gate if (ds_table == NULL) { 2390Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot allocate datastore table, exiting"); 2400Sstevel@tonic-gate return (EXIT_FAILURE); 2410Sstevel@tonic-gate } 2420Sstevel@tonic-gate ds_table[nsynchmods] = NULL; 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate /* 2450Sstevel@tonic-gate * Create the datastores (which implicitly creates the doors). 2460Sstevel@tonic-gate * then sit around and wait for requests to come in on the doors. 2470Sstevel@tonic-gate */ 2480Sstevel@tonic-gate for (i = 0, j = 0; i < nmodules; i++) { 2490Sstevel@tonic-gate if (modules[i] != NULL) { 2500Sstevel@tonic-gate ds_table[j] = ds_create(modules[i], svc_lock); 2510Sstevel@tonic-gate if (ds_table[j] == NULL) { 2520Sstevel@tonic-gate while (j-- > 0) 2530Sstevel@tonic-gate ds_destroy(ds_table[j]); 2540Sstevel@tonic-gate return (EXIT_FAILURE); 2550Sstevel@tonic-gate } 2560Sstevel@tonic-gate free(modules[i]); 2570Sstevel@tonic-gate j++; 2580Sstevel@tonic-gate } 2590Sstevel@tonic-gate } 2600Sstevel@tonic-gate free(modules); 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate stackbase = stack_create(&stacksize); 2630Sstevel@tonic-gate if (stackbase == NULL) 2640Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot create reaper stack; containers " 2650Sstevel@tonic-gate "will not be reaped"); 2660Sstevel@tonic-gate else { 2670Sstevel@tonic-gate errno = thr_create(stackbase, stacksize, reaper, ds_table, 2680Sstevel@tonic-gate THR_DAEMON, NULL); 2690Sstevel@tonic-gate if (errno != 0) { 2700Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot create reaper thread; " 2710Sstevel@tonic-gate "containers will not be reaped"); 2720Sstevel@tonic-gate stack_destroy(stackbase, stacksize); 2730Sstevel@tonic-gate } 2740Sstevel@tonic-gate } 2750Sstevel@tonic-gate 2760Sstevel@tonic-gate /* 2770Sstevel@tonic-gate * Synchronously wait for a QUIT, TERM, or INT, then shutdown. 2780Sstevel@tonic-gate */ 2790Sstevel@tonic-gate (void) sigemptyset(&sigset); 2800Sstevel@tonic-gate (void) sigaddset(&sigset, SIGQUIT); 2810Sstevel@tonic-gate (void) sigaddset(&sigset, SIGTERM); 2820Sstevel@tonic-gate (void) sigaddset(&sigset, SIGINT); 2830Sstevel@tonic-gate 2840Sstevel@tonic-gate (void) sigwait(&sigset, &sig); 2850Sstevel@tonic-gate if (sig != SIGTERM && sig != SIGQUIT && sig != SIGINT) 2860Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "received unexpected signal"); 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate if (sig2str(sig, signame) == -1) 2890Sstevel@tonic-gate (void) strlcpy(signame, "???", sizeof (signame)); 2900Sstevel@tonic-gate 2910Sstevel@tonic-gate dhcpmsg(MSG_INFO, "shutting down via SIG%s", signame); 2920Sstevel@tonic-gate 2930Sstevel@tonic-gate for (i = 0; i < nsynchmods; i++) 2940Sstevel@tonic-gate ds_destroy(ds_table[i]); 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate return (EXIT_SUCCESS); 2970Sstevel@tonic-gate } 2980Sstevel@tonic-gate 2990Sstevel@tonic-gate /* 3000Sstevel@tonic-gate * Sanity check that dsvcd_request_t `req' (which is `reqsize' bytes long) 3010Sstevel@tonic-gate * is a correctly formed request; if not, return an error which will be 3020Sstevel@tonic-gate * returned to the door caller. 3030Sstevel@tonic-gate */ 3040Sstevel@tonic-gate static int 3050Sstevel@tonic-gate check_door_req(dsvcd_request_t *req, size_t reqsize, size_t minsize) 3060Sstevel@tonic-gate { 3070Sstevel@tonic-gate door_cred_t cred; 3080Sstevel@tonic-gate 3090Sstevel@tonic-gate if (req == NULL) { 3100Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "empty request, ignoring"); 3110Sstevel@tonic-gate return (DSVC_SYNCH_ERR); 3120Sstevel@tonic-gate } 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate /* 3150Sstevel@tonic-gate * Check credentials; we don't allow any non-super-user requests 3160Sstevel@tonic-gate * since this would open a denial-of-service hole (since a lock 3170Sstevel@tonic-gate * could be checked out indefinitely). 3180Sstevel@tonic-gate */ 3190Sstevel@tonic-gate if (door_cred(&cred) != 0) { 3200Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "request with unknown credentials"); 3210Sstevel@tonic-gate return (DSVC_ACCESS); 3220Sstevel@tonic-gate } 3230Sstevel@tonic-gate 3240Sstevel@tonic-gate if (cred.dc_euid != 0) { 3250Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "request with non-super-user credentials"); 3260Sstevel@tonic-gate return (DSVC_ACCESS); 3270Sstevel@tonic-gate } 3280Sstevel@tonic-gate 3290Sstevel@tonic-gate /* 3300Sstevel@tonic-gate * Check the version and size; we check this before checking the 3310Sstevel@tonic-gate * size of the request structure since an "incompatible version" 3320Sstevel@tonic-gate * message is more helpful than a "short request" message. 3330Sstevel@tonic-gate */ 3340Sstevel@tonic-gate if (reqsize > offsetof(dsvcd_request_t, rq_version) && 3350Sstevel@tonic-gate req->rq_version != DSVCD_DOOR_VERSION) { 3360Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "request with unsupported version `%d'", 3370Sstevel@tonic-gate req->rq_version); 3380Sstevel@tonic-gate return (DSVC_SYNCH_ERR); 3390Sstevel@tonic-gate } 3400Sstevel@tonic-gate 3410Sstevel@tonic-gate if (reqsize < minsize) { 3420Sstevel@tonic-gate dhcpmsg(MSG_VERBOSE, "short request (%d bytes, minimum %d " 3430Sstevel@tonic-gate "bytes)", reqsize, minsize); 3440Sstevel@tonic-gate return (DSVC_SYNCH_ERR); 3450Sstevel@tonic-gate } 3460Sstevel@tonic-gate 3470Sstevel@tonic-gate return (DSVC_SUCCESS); 3480Sstevel@tonic-gate } 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate 3510Sstevel@tonic-gate /* 3520Sstevel@tonic-gate * Service a lock request `req' passed across the door for datastore `ds'. 3530Sstevel@tonic-gate * After verifying that the request is well-formed, locks the container and 3540Sstevel@tonic-gate * creates an "unlock" door descriptor that the client uses to unlock the 3550Sstevel@tonic-gate * door (either explicitly through door_call()) or implicitly through 3560Sstevel@tonic-gate * terminating abnormally). 3570Sstevel@tonic-gate */ 3580Sstevel@tonic-gate /* ARGSUSED */ 3590Sstevel@tonic-gate static void 3600Sstevel@tonic-gate svc_lock(void *cookie, dsvcd_request_t *req, size_t reqsize, 3610Sstevel@tonic-gate door_desc_t *doorp, uint_t ndoors) 3620Sstevel@tonic-gate { 3630Sstevel@tonic-gate dsvcd_reply_t reply; 3640Sstevel@tonic-gate door_desc_t door_desc; 3650Sstevel@tonic-gate dsvcd_lock_request_t *lreq = (dsvcd_lock_request_t *)req; 3660Sstevel@tonic-gate dsvcd_datastore_t *ds = (dsvcd_datastore_t *)cookie; 3670Sstevel@tonic-gate dsvcd_container_t *cn; 3680Sstevel@tonic-gate dsvcd_unlock_desc_t *ud; 3690Sstevel@tonic-gate char conid[MAXPATHLEN]; 3700Sstevel@tonic-gate unsigned int attempts = 0; 3710Sstevel@tonic-gate 3720Sstevel@tonic-gate reply.rp_version = DSVCD_DOOR_VERSION; 3730Sstevel@tonic-gate reply.rp_retval = check_door_req(req, reqsize, 3740Sstevel@tonic-gate sizeof (dsvcd_lock_request_t)); 3750Sstevel@tonic-gate if (reply.rp_retval != DSVC_SUCCESS) { 3760Sstevel@tonic-gate (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 3770Sstevel@tonic-gate return; 3780Sstevel@tonic-gate } 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate /* 3810Sstevel@tonic-gate * Verify that this is a lock request; if in the future we support 3820Sstevel@tonic-gate * other requests, we'll have to abstract this a bit. 3830Sstevel@tonic-gate */ 3840Sstevel@tonic-gate if (req->rq_reqtype != DSVCD_LOCK) { 3850Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "unsupported request `%d' on lock " 3860Sstevel@tonic-gate "request door", req->rq_reqtype); 3870Sstevel@tonic-gate reply.rp_retval = DSVC_SYNCH_ERR; 3880Sstevel@tonic-gate (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 3890Sstevel@tonic-gate return; 3900Sstevel@tonic-gate } 3910Sstevel@tonic-gate if (lreq->lrq_locktype != DSVCD_RDLOCK && 3920Sstevel@tonic-gate lreq->lrq_locktype != DSVCD_WRLOCK) { 3930Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "request for unsupported locktype `%d'", 3940Sstevel@tonic-gate lreq->lrq_locktype); 3950Sstevel@tonic-gate reply.rp_retval = DSVC_SYNCH_ERR; 3960Sstevel@tonic-gate (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 3970Sstevel@tonic-gate return; 3980Sstevel@tonic-gate } 3990Sstevel@tonic-gate 4000Sstevel@tonic-gate /* 4010Sstevel@tonic-gate * Find the container; create if it doesn't already exist. We do 4020Sstevel@tonic-gate * this as a single operation to avoid race conditions. 4030Sstevel@tonic-gate */ 4040Sstevel@tonic-gate (void) snprintf(conid, sizeof (conid), "%s/%s%d_%s", lreq->lrq_loctoken, 4050Sstevel@tonic-gate ds->ds_name, lreq->lrq_conver, lreq->lrq_conname); 4060Sstevel@tonic-gate cn = ds_get_container(ds, conid, lreq->lrq_crosshost); 4070Sstevel@tonic-gate if (cn == NULL) { 4080Sstevel@tonic-gate reply.rp_retval = DSVC_NO_MEMORY; 4090Sstevel@tonic-gate (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 4100Sstevel@tonic-gate return; 4110Sstevel@tonic-gate } 4120Sstevel@tonic-gate 4130Sstevel@tonic-gate /* 4140Sstevel@tonic-gate * We need another door descriptor which is passed back with the 4150Sstevel@tonic-gate * request. This descriptor is used when the caller wants to 4160Sstevel@tonic-gate * gracefully unlock or when the caller terminates abnormally. 4170Sstevel@tonic-gate */ 4180Sstevel@tonic-gate ud = ud_create(cn, &reply.rp_retval); 4190Sstevel@tonic-gate if (ud == NULL) { 4200Sstevel@tonic-gate ds_release_container(ds, cn); 4210Sstevel@tonic-gate (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 4220Sstevel@tonic-gate return; 4230Sstevel@tonic-gate } 4240Sstevel@tonic-gate 4250Sstevel@tonic-gate /* 4260Sstevel@tonic-gate * We pass a duped door descriptor with the DOOR_RELEASE flag set 4270Sstevel@tonic-gate * instead of just passing the descriptor itself to handle the case 4280Sstevel@tonic-gate * where the client has gone away before we door_return(). Since 4290Sstevel@tonic-gate * we duped, the door descriptor itself will have a refcount of 2 4300Sstevel@tonic-gate * when we go to pass it to the client; if the client does not 4310Sstevel@tonic-gate * exist, the DOOR_RELEASE will drop the count from 2 to 1 which 4320Sstevel@tonic-gate * will cause a DOOR_UNREF_DATA call. 4330Sstevel@tonic-gate * 4340Sstevel@tonic-gate * In the regular (non-error) case, the door_return() will handoff 4350Sstevel@tonic-gate * the descriptor to the client, bumping the refcount to 3, and 4360Sstevel@tonic-gate * then the DOOR_RELEASE will drop the count to 2. If the client 4370Sstevel@tonic-gate * terminates abnormally after this point, the count will drop from 4380Sstevel@tonic-gate * 2 to 1 which will cause a DOOR_UNREF_DATA call. If the client 4390Sstevel@tonic-gate * unlocks gracefully, the refcount will still be 2 when the unlock 4400Sstevel@tonic-gate * door server procedure is called, and the unlock procedure will 4410Sstevel@tonic-gate * unlock the lock and note that the lock has been unlocked (so 4420Sstevel@tonic-gate * that we know the DOOR_UNREF_DATA call generated from the client 4430Sstevel@tonic-gate * subsequently closing the unlock descriptor is benign). 4440Sstevel@tonic-gate * 4450Sstevel@tonic-gate * Note that a DOOR_UNREF_DATA call will be generated *any time* 4460Sstevel@tonic-gate * the refcount goes from 2 to 1 -- even if *we* cause it to 4470Sstevel@tonic-gate * happen, which by default will happen in some of the error logic 4480Sstevel@tonic-gate * below (when we close the duped descriptor). To prevent this 4490Sstevel@tonic-gate * scenario, we tell ud_destroy() *not* to cache the unlock 4500Sstevel@tonic-gate * descriptor, which forces it to blow away the descriptor using 4510Sstevel@tonic-gate * door_revoke(), making the close() that follows benign. 4520Sstevel@tonic-gate */ 4530Sstevel@tonic-gate door_desc.d_attributes = DOOR_DESCRIPTOR|DOOR_RELEASE; 4540Sstevel@tonic-gate door_desc.d_data.d_desc.d_descriptor = dup(ud->ud_fd); 4550Sstevel@tonic-gate if (door_desc.d_data.d_desc.d_descriptor == -1) { 4560Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot dup unlock door; denying %s " 4570Sstevel@tonic-gate "lock request", cn->cn_id); 4580Sstevel@tonic-gate ud_destroy(ud, B_TRUE); 4590Sstevel@tonic-gate ds_release_container(ds, cn); 4600Sstevel@tonic-gate reply.rp_retval = DSVC_NO_RESOURCES; 4610Sstevel@tonic-gate (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 4620Sstevel@tonic-gate return; 4630Sstevel@tonic-gate } 4640Sstevel@tonic-gate 4650Sstevel@tonic-gate /* 4660Sstevel@tonic-gate * Acquire the actual read or write lock on the container. 4670Sstevel@tonic-gate */ 4680Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "tid %d: %s locking %s", thr_self(), 4690Sstevel@tonic-gate lreq->lrq_locktype == DSVCD_RDLOCK ? "read" : "write", cn->cn_id); 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate if (lreq->lrq_locktype == DSVCD_RDLOCK) 4720Sstevel@tonic-gate reply.rp_retval = cn_rdlock(cn, lreq->lrq_nonblock); 4730Sstevel@tonic-gate else if (lreq->lrq_locktype == DSVCD_WRLOCK) 4740Sstevel@tonic-gate reply.rp_retval = cn_wrlock(cn, lreq->lrq_nonblock); 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "tid %d: %s %s lock operation: %s", thr_self(), 4770Sstevel@tonic-gate cn->cn_id, lreq->lrq_locktype == DSVCD_RDLOCK ? "read" : "write", 4780Sstevel@tonic-gate dhcpsvc_errmsg(reply.rp_retval)); 4790Sstevel@tonic-gate 4800Sstevel@tonic-gate ds_release_container(ds, cn); 4810Sstevel@tonic-gate if (reply.rp_retval != DSVC_SUCCESS) { 4820Sstevel@tonic-gate ud_destroy(ud, B_FALSE); 4830Sstevel@tonic-gate (void) close(door_desc.d_data.d_desc.d_descriptor); 4840Sstevel@tonic-gate (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 4850Sstevel@tonic-gate return; 4860Sstevel@tonic-gate } 4870Sstevel@tonic-gate 4880Sstevel@tonic-gate while (door_return((char *)&reply, sizeof (reply), &door_desc, 1) 4890Sstevel@tonic-gate == -1 && errno == EMFILE) { 4900Sstevel@tonic-gate if (lreq->lrq_nonblock) { 4910Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "unable to grant lock; client" 4920Sstevel@tonic-gate " is out of file descriptors"); 4930Sstevel@tonic-gate (void) cn_unlock(cn); 4940Sstevel@tonic-gate ud_destroy(ud, B_FALSE); 4950Sstevel@tonic-gate (void) close(door_desc.d_data.d_desc.d_descriptor); 4960Sstevel@tonic-gate reply.rp_retval = DSVC_BUSY; 4970Sstevel@tonic-gate (void) door_return((char *)&reply, sizeof (reply), 4980Sstevel@tonic-gate NULL, 0); 4990Sstevel@tonic-gate return; 5000Sstevel@tonic-gate } 5010Sstevel@tonic-gate 5020Sstevel@tonic-gate if (attempts++ == 0) { 5030Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "unable to grant lock; client" 5040Sstevel@tonic-gate " is out of file descriptors (retrying)"); 5050Sstevel@tonic-gate } 5060Sstevel@tonic-gate (void) poll(NULL, 0, 100); 5070Sstevel@tonic-gate } 5080Sstevel@tonic-gate } 5090Sstevel@tonic-gate 5100Sstevel@tonic-gate /* 5110Sstevel@tonic-gate * Service an unlock request `req' passed across the door associated with 5120Sstevel@tonic-gate * the unlock token `cookie'. We may be called explicitly (in which case 5130Sstevel@tonic-gate * the request is a well-formed dsvcd_request_t) or implicitly (in which 5140Sstevel@tonic-gate * case our request is set to the value DOOR_UNREF_DATA); this latter case 5150Sstevel@tonic-gate * occurs when a process holding a lock terminates. In either case, unlock 5160Sstevel@tonic-gate * the lock; in the implicit case, log a message as well. 5170Sstevel@tonic-gate */ 5180Sstevel@tonic-gate /* ARGSUSED */ 5190Sstevel@tonic-gate static void 5200Sstevel@tonic-gate svc_unlock(void *cookie, dsvcd_request_t *req, size_t reqsize, 5210Sstevel@tonic-gate door_desc_t *doorp, uint_t ndoors) 5220Sstevel@tonic-gate { 5230Sstevel@tonic-gate dsvcd_unlock_desc_t *ud = cookie; 5240Sstevel@tonic-gate dsvcd_container_t *cn; 5250Sstevel@tonic-gate dsvcd_reply_t reply; 5260Sstevel@tonic-gate 5270Sstevel@tonic-gate /* 5280Sstevel@tonic-gate * Although unlock descriptors are handed out to only a single 5290Sstevel@tonic-gate * thread who has been granted a lock (ergo it seems that only one 5300Sstevel@tonic-gate * thread should be able to call us back), there's a potential race 5310Sstevel@tonic-gate * here if the process crashes while in this door_call(), since 5320Sstevel@tonic-gate * both this thread and the unref kernel upcall thread may run at 5330Sstevel@tonic-gate * the same time. Protect against this case with a mutex. 5340Sstevel@tonic-gate */ 5350Sstevel@tonic-gate (void) mutex_lock(&ud->ud_lock); 5360Sstevel@tonic-gate cn = ud->ud_cn; 5370Sstevel@tonic-gate 5380Sstevel@tonic-gate /* 5390Sstevel@tonic-gate * First handle the case where the lock owner has closed the unlock 5400Sstevel@tonic-gate * descriptor, either because they have unlocked the lock and are 5410Sstevel@tonic-gate * thus done using the descriptor, or because they crashed. In the 5420Sstevel@tonic-gate * second case, print a message. 5430Sstevel@tonic-gate */ 5440Sstevel@tonic-gate if (req == DOOR_UNREF_DATA) { 5450Sstevel@tonic-gate /* 5460Sstevel@tonic-gate * The last reference is ours; we can free the descriptor. 5470Sstevel@tonic-gate */ 5480Sstevel@tonic-gate (void) mutex_unlock(&ud->ud_lock); 5490Sstevel@tonic-gate ud_destroy(ud, B_TRUE); 5500Sstevel@tonic-gate 5510Sstevel@tonic-gate /* 5520Sstevel@tonic-gate * Normal case: the caller is closing the unlock descriptor 5530Sstevel@tonic-gate * on a lock they've already unlocked -- just return. 5540Sstevel@tonic-gate */ 5550Sstevel@tonic-gate if (cn == NULL) { 5560Sstevel@tonic-gate (void) door_return(NULL, 0, NULL, 0); 5570Sstevel@tonic-gate return; 5580Sstevel@tonic-gate } 5590Sstevel@tonic-gate 5600Sstevel@tonic-gate /* 5610Sstevel@tonic-gate * Error case: the caller has crashed while holding the 5620Sstevel@tonic-gate * unlock descriptor (or is otherwise in violation of 5630Sstevel@tonic-gate * protocol). Since all datastores are required to be 5640Sstevel@tonic-gate * robust even if unexpected termination occurs, we assume 5650Sstevel@tonic-gate * the container is not corrupt, even if the process 5660Sstevel@tonic-gate * crashed with the write lock held. 5670Sstevel@tonic-gate */ 5680Sstevel@tonic-gate switch (cn_locktype(cn)) { 5690Sstevel@tonic-gate case DSVCD_RDLOCK: 5700Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "process exited while reading " 5710Sstevel@tonic-gate "`%s'; unlocking", cn->cn_id); 5720Sstevel@tonic-gate (void) cn_unlock(cn); 5730Sstevel@tonic-gate break; 5740Sstevel@tonic-gate 5750Sstevel@tonic-gate case DSVCD_WRLOCK: 5760Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "process exited while writing " 5770Sstevel@tonic-gate "`%s'; unlocking", cn->cn_id); 5780Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "note that this write operation " 5790Sstevel@tonic-gate "may or may not have succeeded"); 5800Sstevel@tonic-gate (void) cn_unlock(cn); 5810Sstevel@tonic-gate break; 5820Sstevel@tonic-gate 5830Sstevel@tonic-gate case DSVCD_NOLOCK: 5840Sstevel@tonic-gate dhcpmsg(MSG_CRIT, "unreferenced unheld lock"); 5850Sstevel@tonic-gate break; 5860Sstevel@tonic-gate } 5870Sstevel@tonic-gate 5880Sstevel@tonic-gate (void) door_return(NULL, 0, NULL, 0); 5890Sstevel@tonic-gate return; 5900Sstevel@tonic-gate } 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate /* 5930Sstevel@tonic-gate * Verify that this is a unlock request; if in the future we support 5940Sstevel@tonic-gate * other requests, we'll have to abstract this a bit. 5950Sstevel@tonic-gate */ 5960Sstevel@tonic-gate reply.rp_version = DSVCD_DOOR_VERSION; 5970Sstevel@tonic-gate reply.rp_retval = check_door_req(req, reqsize, 5980Sstevel@tonic-gate sizeof (dsvcd_unlock_request_t)); 5990Sstevel@tonic-gate if (reply.rp_retval != DSVC_SUCCESS) { 6000Sstevel@tonic-gate (void) mutex_unlock(&ud->ud_lock); 6010Sstevel@tonic-gate (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 6020Sstevel@tonic-gate return; 6030Sstevel@tonic-gate } 6040Sstevel@tonic-gate 6050Sstevel@tonic-gate if (req->rq_reqtype != DSVCD_UNLOCK) { 6060Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "unsupported request `%d' on unlock " 6070Sstevel@tonic-gate "request door", req->rq_reqtype); 6080Sstevel@tonic-gate (void) mutex_unlock(&ud->ud_lock); 6090Sstevel@tonic-gate reply.rp_retval = DSVC_SYNCH_ERR; 6100Sstevel@tonic-gate (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 6110Sstevel@tonic-gate return; 6120Sstevel@tonic-gate } 6130Sstevel@tonic-gate 6140Sstevel@tonic-gate /* 6150Sstevel@tonic-gate * Attempt to unlock an already-unlocked container; log and return. 6160Sstevel@tonic-gate */ 6170Sstevel@tonic-gate if (cn == NULL) { 6180Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "process tried to re-unlock a lock"); 6190Sstevel@tonic-gate (void) mutex_unlock(&ud->ud_lock); 6200Sstevel@tonic-gate reply.rp_retval = DSVC_SYNCH_ERR; 6210Sstevel@tonic-gate (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 6220Sstevel@tonic-gate return; 6230Sstevel@tonic-gate } 6240Sstevel@tonic-gate ud->ud_cn = NULL; 6250Sstevel@tonic-gate 6260Sstevel@tonic-gate /* 6270Sstevel@tonic-gate * Unlock the container; note that after cn_unlock() has been done 6280Sstevel@tonic-gate * cn->cn_id is no longer accessible. 6290Sstevel@tonic-gate */ 6300Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "tid %d: unlocking %s", thr_self(), cn->cn_id); 6310Sstevel@tonic-gate reply.rp_retval = cn_unlock(cn); 6320Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "tid %d: unlock operation: %s", thr_self(), 6330Sstevel@tonic-gate dhcpsvc_errmsg(reply.rp_retval)); 6340Sstevel@tonic-gate 6350Sstevel@tonic-gate /* 6360Sstevel@tonic-gate * Even though we've unlocked the lock, we cannot yet destroy the 6370Sstevel@tonic-gate * unlock descriptor (even if we revoke the door) because it's 6380Sstevel@tonic-gate * possible the unref thread is already waiting on ud_lock. 6390Sstevel@tonic-gate */ 6400Sstevel@tonic-gate (void) mutex_unlock(&ud->ud_lock); 6410Sstevel@tonic-gate (void) door_return((char *)&reply, sizeof (reply), NULL, 0); 6420Sstevel@tonic-gate } 6430Sstevel@tonic-gate 6440Sstevel@tonic-gate /* 6450Sstevel@tonic-gate * Reap containers that have not been recently used. 6460Sstevel@tonic-gate */ 6470Sstevel@tonic-gate static void * 6480Sstevel@tonic-gate reaper(void *ds_table_raw) 6490Sstevel@tonic-gate { 6500Sstevel@tonic-gate dsvcd_datastore_t **ds_table; 6510Sstevel@tonic-gate unsigned int i, nreaped; 6520Sstevel@tonic-gate 6530Sstevel@tonic-gate ds_table = (dsvcd_datastore_t **)ds_table_raw; 6540Sstevel@tonic-gate for (;;) { 6550Sstevel@tonic-gate (void) sleep(DSVCD_REAP_INTERVAL); 6560Sstevel@tonic-gate for (i = 0; ds_table[i] != NULL; i++) { 6570Sstevel@tonic-gate nreaped = ds_reap_containers(ds_table[i], 6580Sstevel@tonic-gate DSVCD_REAP_THRESH); 6590Sstevel@tonic-gate if (nreaped > 0) { 6600Sstevel@tonic-gate dhcpmsg(MSG_VERBOSE, "reaped %u container " 6610Sstevel@tonic-gate "synchpoints from %s", nreaped, 6620Sstevel@tonic-gate ds_table[i]->ds_name); 6630Sstevel@tonic-gate } 6640Sstevel@tonic-gate } 6650Sstevel@tonic-gate } 6660Sstevel@tonic-gate /* NOTREACHED */ 6670Sstevel@tonic-gate return (NULL); 6680Sstevel@tonic-gate } 6690Sstevel@tonic-gate 6700Sstevel@tonic-gate /* 6710Sstevel@tonic-gate * Daemonize the process. 6720Sstevel@tonic-gate */ 6730Sstevel@tonic-gate static int 6740Sstevel@tonic-gate daemonize(void) 6750Sstevel@tonic-gate { 6760Sstevel@tonic-gate switch (fork()) { 6770Sstevel@tonic-gate 6780Sstevel@tonic-gate case -1: 6790Sstevel@tonic-gate return (0); 6800Sstevel@tonic-gate 6810Sstevel@tonic-gate case 0: 6820Sstevel@tonic-gate /* 6830Sstevel@tonic-gate * Lose our controlling terminal, and become both a session 6840Sstevel@tonic-gate * leader and a process group leader. 6850Sstevel@tonic-gate */ 6860Sstevel@tonic-gate if (setsid() == -1) 6870Sstevel@tonic-gate return (0); 6880Sstevel@tonic-gate 6890Sstevel@tonic-gate /* 6900Sstevel@tonic-gate * Under POSIX, a session leader can accidentally (through 6910Sstevel@tonic-gate * open(2)) acquire a controlling terminal if it does not 6920Sstevel@tonic-gate * have one. Just to be safe, fork() again so we are not a 6930Sstevel@tonic-gate * session leader. 6940Sstevel@tonic-gate */ 6950Sstevel@tonic-gate switch (fork()) { 6960Sstevel@tonic-gate 6970Sstevel@tonic-gate case -1: 6980Sstevel@tonic-gate return (0); 6990Sstevel@tonic-gate 7000Sstevel@tonic-gate case 0: 7010Sstevel@tonic-gate (void) signal(SIGHUP, SIG_IGN); 7020Sstevel@tonic-gate (void) chdir("/"); 7030Sstevel@tonic-gate (void) umask(022); 7040Sstevel@tonic-gate closefrom(0); 7050Sstevel@tonic-gate break; 7060Sstevel@tonic-gate 7070Sstevel@tonic-gate default: 7080Sstevel@tonic-gate _exit(EXIT_SUCCESS); 7090Sstevel@tonic-gate } 7100Sstevel@tonic-gate break; 7110Sstevel@tonic-gate 7120Sstevel@tonic-gate default: 7130Sstevel@tonic-gate _exit(EXIT_SUCCESS); 7140Sstevel@tonic-gate } 7150Sstevel@tonic-gate 7160Sstevel@tonic-gate return (1); 7170Sstevel@tonic-gate } 7180Sstevel@tonic-gate 7190Sstevel@tonic-gate /* 7200Sstevel@tonic-gate * Create an unlock descriptor for container `cn' -- returns an unlock 7210Sstevel@tonic-gate * descriptor on success, or NULL on failure; the reason for failure is in 7220Sstevel@tonic-gate * `retvalp'. Since creating door descriptors is expensive, we keep a few 7230Sstevel@tonic-gate * cache a small list of old descriptors around on a reclaim list and only 7240Sstevel@tonic-gate * allocate a new one if the list is empty. 7250Sstevel@tonic-gate */ 7260Sstevel@tonic-gate static dsvcd_unlock_desc_t * 7270Sstevel@tonic-gate ud_create(dsvcd_container_t *cn, int *retvalp) 7280Sstevel@tonic-gate { 7290Sstevel@tonic-gate dsvcd_unlock_desc_t *ud; 7300Sstevel@tonic-gate 7310Sstevel@tonic-gate *retvalp = DSVC_SUCCESS; 7320Sstevel@tonic-gate (void) mutex_lock(&ud_reclaim_lock); 7330Sstevel@tonic-gate if (ud_reclaim_list != NULL) { 7340Sstevel@tonic-gate ud = ud_reclaim_list; 7350Sstevel@tonic-gate ud_reclaim_list = ud->ud_next; 7360Sstevel@tonic-gate ud_reclaim_count--; 7370Sstevel@tonic-gate (void) mutex_unlock(&ud_reclaim_lock); 7380Sstevel@tonic-gate } else { 7390Sstevel@tonic-gate (void) mutex_unlock(&ud_reclaim_lock); 7400Sstevel@tonic-gate ud = malloc(sizeof (dsvcd_unlock_desc_t)); 7410Sstevel@tonic-gate if (ud == NULL) { 7420Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "cannot allocate unlock door " 7430Sstevel@tonic-gate "descriptor; denying %s lock request", cn->cn_id); 7440Sstevel@tonic-gate *retvalp = DSVC_NO_MEMORY; 7450Sstevel@tonic-gate return (NULL); 7460Sstevel@tonic-gate } 7470Sstevel@tonic-gate 7480Sstevel@tonic-gate (void) mutex_init(&ud->ud_lock, USYNC_THREAD, NULL); 7490Sstevel@tonic-gate ud->ud_fd = door_create((void (*)())svc_unlock, ud, 7500Sstevel@tonic-gate DOOR_UNREF_MULTI | DOOR_REFUSE_DESC | DOOR_NO_CANCEL); 7510Sstevel@tonic-gate if (ud->ud_fd == -1) { 7520Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "cannot create unlock door; " 7530Sstevel@tonic-gate "denying %s lock request", cn->cn_id); 7540Sstevel@tonic-gate free(ud); 7550Sstevel@tonic-gate *retvalp = DSVC_NO_RESOURCES; 7560Sstevel@tonic-gate return (NULL); 7570Sstevel@tonic-gate } 7580Sstevel@tonic-gate } 7590Sstevel@tonic-gate 7600Sstevel@tonic-gate ud->ud_next = NULL; 7610Sstevel@tonic-gate ud->ud_cn = cn; 7620Sstevel@tonic-gate return (ud); 7630Sstevel@tonic-gate } 7640Sstevel@tonic-gate 7650Sstevel@tonic-gate /* 7660Sstevel@tonic-gate * Destroy the unlock descriptor `ud' -- `ud' must be unlocked on entry. 7670Sstevel@tonic-gate * If there's room and `cacheable' is set, then, keep the unlock descriptor 7680Sstevel@tonic-gate * on the reclaim list to lower future creation cost. 7690Sstevel@tonic-gate */ 7700Sstevel@tonic-gate static void 7710Sstevel@tonic-gate ud_destroy(dsvcd_unlock_desc_t *ud, boolean_t cacheable) 7720Sstevel@tonic-gate { 7730Sstevel@tonic-gate assert(!MUTEX_HELD(&ud->ud_lock)); 7740Sstevel@tonic-gate 7750Sstevel@tonic-gate ud->ud_cn = NULL; 7760Sstevel@tonic-gate (void) mutex_lock(&ud_reclaim_lock); 7770Sstevel@tonic-gate if (cacheable && ud_reclaim_count < UD_RECLAIM_MAX) { 7780Sstevel@tonic-gate ud->ud_next = ud_reclaim_list; 7790Sstevel@tonic-gate ud_reclaim_list = ud; 7800Sstevel@tonic-gate ud_reclaim_count++; 7810Sstevel@tonic-gate (void) mutex_unlock(&ud_reclaim_lock); 7820Sstevel@tonic-gate } else { 7830Sstevel@tonic-gate (void) mutex_unlock(&ud_reclaim_lock); 7840Sstevel@tonic-gate (void) door_revoke(ud->ud_fd); 7850Sstevel@tonic-gate (void) mutex_destroy(&ud->ud_lock); 7860Sstevel@tonic-gate free(ud); 7870Sstevel@tonic-gate } 7880Sstevel@tonic-gate } 7890Sstevel@tonic-gate 7900Sstevel@tonic-gate /* 7910Sstevel@tonic-gate * Create a stack of `*stacksizep' bytes (rounded up to the nearest page) 7920Sstevel@tonic-gate * including a redzone for catching stack overflow. Set `stacksizep' to 7930Sstevel@tonic-gate * point to the actual usable size of the stack (i.e., everything but the 7940Sstevel@tonic-gate * redzone). Returns a pointer to the base of the stack (not including the 7950Sstevel@tonic-gate * redzone). 7960Sstevel@tonic-gate */ 7970Sstevel@tonic-gate static void * 7980Sstevel@tonic-gate stack_create(unsigned int *stacksizep) 7990Sstevel@tonic-gate { 8000Sstevel@tonic-gate caddr_t stackbase; 8010Sstevel@tonic-gate unsigned int redzone = roundup(DSVCD_STACK_REDSIZE, PAGESIZE); 8020Sstevel@tonic-gate unsigned int stacksize = *stacksizep; 8030Sstevel@tonic-gate 8040Sstevel@tonic-gate if (stacksize < sysconf(_SC_THREAD_STACK_MIN)) 8050Sstevel@tonic-gate stacksize = sysconf(_SC_THREAD_STACK_MIN); 8060Sstevel@tonic-gate 8070Sstevel@tonic-gate stacksize = roundup(stacksize, PAGESIZE); 8080Sstevel@tonic-gate stackbase = mmap(NULL, stacksize + redzone, PROT_READ | PROT_WRITE, 8090Sstevel@tonic-gate MAP_ANON | MAP_PRIVATE, -1, 0); 8100Sstevel@tonic-gate if (stackbase == MAP_FAILED) 8110Sstevel@tonic-gate return (NULL); 8120Sstevel@tonic-gate 8130Sstevel@tonic-gate *stacksizep = stacksize; 8140Sstevel@tonic-gate (void) mprotect(stackbase, redzone, PROT_NONE); 8150Sstevel@tonic-gate return (stackbase + redzone); 8160Sstevel@tonic-gate } 8170Sstevel@tonic-gate 8180Sstevel@tonic-gate /* 8190Sstevel@tonic-gate * Destroy the stack of `stacksize' bytes pointed to by `stackbase'. 8200Sstevel@tonic-gate */ 8210Sstevel@tonic-gate static void 8220Sstevel@tonic-gate stack_destroy(void *stackbase, unsigned int stacksize) 8230Sstevel@tonic-gate { 8240Sstevel@tonic-gate unsigned int redzone = roundup(DSVCD_STACK_REDSIZE, PAGESIZE); 8250Sstevel@tonic-gate 8260Sstevel@tonic-gate (void) munmap((caddr_t)stackbase - redzone, stacksize + redzone); 8270Sstevel@tonic-gate } 8280Sstevel@tonic-gate 8290Sstevel@tonic-gate /* 8300Sstevel@tonic-gate * Start function for door server threads; turns off thread cancellation 8310Sstevel@tonic-gate * and then parks in the kernel via door_return(). 8320Sstevel@tonic-gate */ 8330Sstevel@tonic-gate /* ARGSUSED */ 8340Sstevel@tonic-gate static void * 8350Sstevel@tonic-gate doorserv_thread(void *arg) 8360Sstevel@tonic-gate { 8370Sstevel@tonic-gate (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 8380Sstevel@tonic-gate (void) door_return(NULL, 0, NULL, 0); 8390Sstevel@tonic-gate return (NULL); 8400Sstevel@tonic-gate } 8410Sstevel@tonic-gate 8420Sstevel@tonic-gate /* 8430Sstevel@tonic-gate * Creation function for door server threads. We require door threads to 8440Sstevel@tonic-gate * have 32K of backed stack. This is a guess but will be more than 8450Sstevel@tonic-gate * sufficient for our uses, since door threads have a shallow call depth 8460Sstevel@tonic-gate * and the functions use little automatic storage. 8470Sstevel@tonic-gate */ 8480Sstevel@tonic-gate /* ARGSUSED */ 8490Sstevel@tonic-gate static void 8500Sstevel@tonic-gate doorserv_create(door_info_t *infop) 8510Sstevel@tonic-gate { 8520Sstevel@tonic-gate void *stackbase; 8530Sstevel@tonic-gate unsigned int stacksize = 32 * 1024; 8540Sstevel@tonic-gate 8550Sstevel@tonic-gate stackbase = stack_create(&stacksize); 8560Sstevel@tonic-gate if (stackbase != NULL) { 8570Sstevel@tonic-gate errno = thr_create(stackbase, stacksize, doorserv_thread, NULL, 8580Sstevel@tonic-gate THR_BOUND | THR_DETACHED, NULL); 8590Sstevel@tonic-gate if (errno != 0) { 8600Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot create door server thread; " 8610Sstevel@tonic-gate "server thread pool will not be grown"); 8620Sstevel@tonic-gate stack_destroy(stackbase, stacksize); 8630Sstevel@tonic-gate } 8640Sstevel@tonic-gate } 8650Sstevel@tonic-gate } 866