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