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