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 #include <sys/types.h> 30*0Sstevel@tonic-gate #include <stdlib.h> 31*0Sstevel@tonic-gate #include <string.h> 32*0Sstevel@tonic-gate #include <stropts.h> 33*0Sstevel@tonic-gate #include <synch.h> 34*0Sstevel@tonic-gate #include <fcntl.h> 35*0Sstevel@tonic-gate #include <unistd.h> 36*0Sstevel@tonic-gate #include <stdio.h> 37*0Sstevel@tonic-gate #include <dhcp_svc_private.h> 38*0Sstevel@tonic-gate #include <sys/time.h> 39*0Sstevel@tonic-gate #include <dhcpmsg.h> 40*0Sstevel@tonic-gate 41*0Sstevel@tonic-gate #include "dsvclockd.h" 42*0Sstevel@tonic-gate #include "datastore.h" 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate static uint32_t ds_hash(const char *); 45*0Sstevel@tonic-gate 46*0Sstevel@tonic-gate /* 47*0Sstevel@tonic-gate * Create a datastore named `ds_name' and a door which will service requests 48*0Sstevel@tonic-gate * for this datastore. When the door is called, callback `ds_callback'. 49*0Sstevel@tonic-gate * Returns the created datastore. 50*0Sstevel@tonic-gate */ 51*0Sstevel@tonic-gate dsvcd_datastore_t * 52*0Sstevel@tonic-gate ds_create(const char *ds_name, dsvcd_svc_t *ds_callback) 53*0Sstevel@tonic-gate { 54*0Sstevel@tonic-gate char door_path[MAXPATHLEN]; 55*0Sstevel@tonic-gate dsvcd_datastore_t *ds = NULL; 56*0Sstevel@tonic-gate int fd; 57*0Sstevel@tonic-gate unsigned int i; 58*0Sstevel@tonic-gate door_info_t info; 59*0Sstevel@tonic-gate 60*0Sstevel@tonic-gate dhcpmsg(MSG_VERBOSE, "managing locks for datastore `%s'", ds_name); 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate ds = malloc(sizeof (dsvcd_datastore_t)); 63*0Sstevel@tonic-gate if (ds == NULL) { 64*0Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot manage locks for datastore `%s'", 65*0Sstevel@tonic-gate ds_name); 66*0Sstevel@tonic-gate return (NULL); 67*0Sstevel@tonic-gate } 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gate ds->ds_name = strdup(ds_name); 70*0Sstevel@tonic-gate if (ds->ds_name == NULL) { 71*0Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot manage locks for datastore `%s'", 72*0Sstevel@tonic-gate ds_name); 73*0Sstevel@tonic-gate free(ds); 74*0Sstevel@tonic-gate return (NULL); 75*0Sstevel@tonic-gate } 76*0Sstevel@tonic-gate 77*0Sstevel@tonic-gate ds->ds_doorfd = door_create((void (*)())ds_callback, ds, 78*0Sstevel@tonic-gate DOOR_REFUSE_DESC | DOOR_NO_CANCEL); 79*0Sstevel@tonic-gate if (ds->ds_doorfd == -1) { 80*0Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot create door for datastore `%s'", 81*0Sstevel@tonic-gate ds_name); 82*0Sstevel@tonic-gate free(ds->ds_name); 83*0Sstevel@tonic-gate free(ds); 84*0Sstevel@tonic-gate return (NULL); 85*0Sstevel@tonic-gate } 86*0Sstevel@tonic-gate 87*0Sstevel@tonic-gate for (i = 0; i < DSVCD_DS_HASH_SIZE; i++) { 88*0Sstevel@tonic-gate ds->ds_hash[i].cl_head = NULL; 89*0Sstevel@tonic-gate (void) mutex_init(&ds->ds_hash[i].cl_lock, USYNC_THREAD, 0); 90*0Sstevel@tonic-gate } 91*0Sstevel@tonic-gate 92*0Sstevel@tonic-gate /* 93*0Sstevel@tonic-gate * Create the door name in the filesystem. First, check to see if 94*0Sstevel@tonic-gate * a door already exists at the specified pathname. If it does, 95*0Sstevel@tonic-gate * and the server process (no doubt another copy of us) is already 96*0Sstevel@tonic-gate * running, then fail. Otherwise, unlink the old door and fattach 97*0Sstevel@tonic-gate * a new one. 98*0Sstevel@tonic-gate */ 99*0Sstevel@tonic-gate (void) snprintf(door_path, sizeof (door_path), DSVCD_DOOR_FMT, ds_name); 100*0Sstevel@tonic-gate 101*0Sstevel@tonic-gate fd = open(door_path, O_RDWR); 102*0Sstevel@tonic-gate if (fd != -1) { 103*0Sstevel@tonic-gate if (door_info(fd, &info) == 0 && info.di_target != -1) { 104*0Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "%s is in use by process %u", 105*0Sstevel@tonic-gate door_path, info.di_target); 106*0Sstevel@tonic-gate (void) close(fd); 107*0Sstevel@tonic-gate (void) close(ds->ds_doorfd); 108*0Sstevel@tonic-gate free(ds->ds_name); 109*0Sstevel@tonic-gate free(ds); 110*0Sstevel@tonic-gate return (NULL); 111*0Sstevel@tonic-gate } 112*0Sstevel@tonic-gate (void) close(fd); 113*0Sstevel@tonic-gate (void) unlink(door_path); 114*0Sstevel@tonic-gate } 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate fd = open(door_path, O_CREAT|O_EXCL|O_RDWR, 0644); 117*0Sstevel@tonic-gate if (fd == -1) { 118*0Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot create door rendezvous for datastore " 119*0Sstevel@tonic-gate "`%s'", ds_name); 120*0Sstevel@tonic-gate (void) close(ds->ds_doorfd); 121*0Sstevel@tonic-gate free(ds->ds_name); 122*0Sstevel@tonic-gate free(ds); 123*0Sstevel@tonic-gate return (NULL); 124*0Sstevel@tonic-gate } 125*0Sstevel@tonic-gate (void) close(fd); 126*0Sstevel@tonic-gate 127*0Sstevel@tonic-gate /* 128*0Sstevel@tonic-gate * Attach the door onto the name 129*0Sstevel@tonic-gate */ 130*0Sstevel@tonic-gate if (fattach(ds->ds_doorfd, door_path) == -1) { 131*0Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot fattach door rendezvous for datastore " 132*0Sstevel@tonic-gate "`%s'", ds_name); 133*0Sstevel@tonic-gate (void) close(ds->ds_doorfd); 134*0Sstevel@tonic-gate free(ds->ds_name); 135*0Sstevel@tonic-gate free(ds); 136*0Sstevel@tonic-gate return (NULL); 137*0Sstevel@tonic-gate } 138*0Sstevel@tonic-gate 139*0Sstevel@tonic-gate return (ds); 140*0Sstevel@tonic-gate } 141*0Sstevel@tonic-gate 142*0Sstevel@tonic-gate /* 143*0Sstevel@tonic-gate * Destroy a datastore `ds' and its associated containers, and remove 144*0Sstevel@tonic-gate * its door from the filesystem. 145*0Sstevel@tonic-gate */ 146*0Sstevel@tonic-gate void 147*0Sstevel@tonic-gate ds_destroy(dsvcd_datastore_t *ds) 148*0Sstevel@tonic-gate { 149*0Sstevel@tonic-gate unsigned int i; 150*0Sstevel@tonic-gate char door_path[MAXPATHLEN]; 151*0Sstevel@tonic-gate dsvcd_container_t *cn, *cn_next; 152*0Sstevel@tonic-gate 153*0Sstevel@tonic-gate dhcpmsg(MSG_VERBOSE, "stopping lock management for datastore `%s'", 154*0Sstevel@tonic-gate ds->ds_name); 155*0Sstevel@tonic-gate 156*0Sstevel@tonic-gate /* 157*0Sstevel@tonic-gate * Detach and revoke access to the door. The detach makes it so 158*0Sstevel@tonic-gate * new callers who open the door will fail; the revoke makes it 159*0Sstevel@tonic-gate * so that callers that already have a door descriptor will fail. 160*0Sstevel@tonic-gate * We do this prior to calling cn_destroy() to make it easier for 161*0Sstevel@tonic-gate * the container lockcount to drain. 162*0Sstevel@tonic-gate */ 163*0Sstevel@tonic-gate (void) snprintf(door_path, MAXPATHLEN, DSVCD_DOOR_FMT, ds->ds_name); 164*0Sstevel@tonic-gate (void) fdetach(door_path); 165*0Sstevel@tonic-gate (void) unlink(door_path); 166*0Sstevel@tonic-gate (void) door_revoke(ds->ds_doorfd); 167*0Sstevel@tonic-gate (void) close(ds->ds_doorfd); 168*0Sstevel@tonic-gate 169*0Sstevel@tonic-gate /* 170*0Sstevel@tonic-gate * Destroy all the underlying containers. We're single-threaded at 171*0Sstevel@tonic-gate * this point, so don't worry about locks. 172*0Sstevel@tonic-gate */ 173*0Sstevel@tonic-gate for (i = 0; i < DSVCD_DS_HASH_SIZE; i++) { 174*0Sstevel@tonic-gate for (cn = ds->ds_hash[i].cl_head; cn != NULL; cn = cn_next) { 175*0Sstevel@tonic-gate cn_next = cn->cn_next; 176*0Sstevel@tonic-gate cn_destroy(cn); 177*0Sstevel@tonic-gate } 178*0Sstevel@tonic-gate (void) mutex_destroy(&ds->ds_hash[i].cl_lock); 179*0Sstevel@tonic-gate } 180*0Sstevel@tonic-gate 181*0Sstevel@tonic-gate free(ds->ds_name); 182*0Sstevel@tonic-gate free(ds); 183*0Sstevel@tonic-gate } 184*0Sstevel@tonic-gate 185*0Sstevel@tonic-gate /* 186*0Sstevel@tonic-gate * Get a container with id `cn_id' from datastore `ds'; create the 187*0Sstevel@tonic-gate * container if it does not exist. If `crosshost' is set and the container 188*0Sstevel@tonic-gate * does not yet exist, then the container will synchronize across hosts. . 189*0Sstevel@tonic-gate * If the container cannot be found or created, NULL is returned. When the 190*0Sstevel@tonic-gate * calling thread is done with the container, ds_release_container() must 191*0Sstevel@tonic-gate * be called. 192*0Sstevel@tonic-gate */ 193*0Sstevel@tonic-gate dsvcd_container_t * 194*0Sstevel@tonic-gate ds_get_container(dsvcd_datastore_t *ds, const char *cn_id, boolean_t crosshost) 195*0Sstevel@tonic-gate { 196*0Sstevel@tonic-gate dsvcd_container_list_t *cn_list; 197*0Sstevel@tonic-gate dsvcd_container_t *cn; 198*0Sstevel@tonic-gate uint32_t idhash = ds_hash(cn_id); 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gate cn_list = &ds->ds_hash[idhash % DSVCD_DS_HASH_SIZE]; 201*0Sstevel@tonic-gate (void) mutex_lock(&cn_list->cl_lock); 202*0Sstevel@tonic-gate 203*0Sstevel@tonic-gate for (cn = cn_list->cl_head; cn != NULL; cn = cn->cn_next) { 204*0Sstevel@tonic-gate if (idhash == cn->cn_idhash && strcmp(cn_id, cn->cn_id) == 0) 205*0Sstevel@tonic-gate break; 206*0Sstevel@tonic-gate } 207*0Sstevel@tonic-gate 208*0Sstevel@tonic-gate if (cn == NULL) { 209*0Sstevel@tonic-gate cn = cn_create(cn_id, crosshost); 210*0Sstevel@tonic-gate if (cn != NULL) { 211*0Sstevel@tonic-gate if (cn_list->cl_head != NULL) 212*0Sstevel@tonic-gate cn_list->cl_head->cn_prev = cn; 213*0Sstevel@tonic-gate 214*0Sstevel@tonic-gate cn->cn_next = cn_list->cl_head; 215*0Sstevel@tonic-gate cn->cn_prev = NULL; 216*0Sstevel@tonic-gate cn_list->cl_head = cn; 217*0Sstevel@tonic-gate cn->cn_idhash = idhash; 218*0Sstevel@tonic-gate cn->cn_nout = 0; 219*0Sstevel@tonic-gate cn->cn_lastrel = 0; 220*0Sstevel@tonic-gate } 221*0Sstevel@tonic-gate } 222*0Sstevel@tonic-gate 223*0Sstevel@tonic-gate if (cn != NULL) 224*0Sstevel@tonic-gate cn->cn_nout++; 225*0Sstevel@tonic-gate 226*0Sstevel@tonic-gate (void) mutex_unlock(&cn_list->cl_lock); 227*0Sstevel@tonic-gate return (cn); 228*0Sstevel@tonic-gate } 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gate /* 231*0Sstevel@tonic-gate * Release a container `cn' belonging to datastore `ds'. Once a container 232*0Sstevel@tonic-gate * has been released, it can no longer be used by the releasing thread. 233*0Sstevel@tonic-gate * Used to track the number of active instances of a container. 234*0Sstevel@tonic-gate */ 235*0Sstevel@tonic-gate void 236*0Sstevel@tonic-gate ds_release_container(dsvcd_datastore_t *ds, dsvcd_container_t *cn) 237*0Sstevel@tonic-gate { 238*0Sstevel@tonic-gate dsvcd_container_list_t *cn_list; 239*0Sstevel@tonic-gate uint32_t idhash = ds_hash(cn->cn_id); 240*0Sstevel@tonic-gate 241*0Sstevel@tonic-gate cn_list = &ds->ds_hash[idhash % DSVCD_DS_HASH_SIZE]; 242*0Sstevel@tonic-gate 243*0Sstevel@tonic-gate (void) mutex_lock(&cn_list->cl_lock); 244*0Sstevel@tonic-gate 245*0Sstevel@tonic-gate cn->cn_nout--; 246*0Sstevel@tonic-gate cn->cn_lastrel = time(NULL); 247*0Sstevel@tonic-gate 248*0Sstevel@tonic-gate (void) mutex_unlock(&cn_list->cl_lock); 249*0Sstevel@tonic-gate } 250*0Sstevel@tonic-gate 251*0Sstevel@tonic-gate /* 252*0Sstevel@tonic-gate * Destroy any containers in datastore `ds' that have not been accessed in 253*0Sstevel@tonic-gate * the last `idle' seconds. Return the number of destroyed (reaped) 254*0Sstevel@tonic-gate * containers. 255*0Sstevel@tonic-gate */ 256*0Sstevel@tonic-gate unsigned int 257*0Sstevel@tonic-gate ds_reap_containers(dsvcd_datastore_t *ds, unsigned int idle) 258*0Sstevel@tonic-gate { 259*0Sstevel@tonic-gate dsvcd_container_list_t *cn_list; 260*0Sstevel@tonic-gate dsvcd_container_t *cn, *cn_next; 261*0Sstevel@tonic-gate unsigned int i, nreaped = 0; 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gate for (i = 0; i < DSVCD_DS_HASH_SIZE; i++) { 264*0Sstevel@tonic-gate cn_list = &ds->ds_hash[i]; 265*0Sstevel@tonic-gate 266*0Sstevel@tonic-gate (void) mutex_lock(&cn_list->cl_lock); 267*0Sstevel@tonic-gate for (cn = cn_list->cl_head; cn != NULL; cn = cn_next) { 268*0Sstevel@tonic-gate cn_next = cn->cn_next; 269*0Sstevel@tonic-gate 270*0Sstevel@tonic-gate /* 271*0Sstevel@tonic-gate * Since a container is not checked out across a 272*0Sstevel@tonic-gate * lock operation, we must check if the lock is 273*0Sstevel@tonic-gate * held as well as the number of instances checked 274*0Sstevel@tonic-gate * out. 275*0Sstevel@tonic-gate */ 276*0Sstevel@tonic-gate if (cn->cn_nout != 0 || 277*0Sstevel@tonic-gate cn_locktype(cn) != DSVCD_NOLOCK || 278*0Sstevel@tonic-gate cn->cn_lastrel + idle >= time(NULL)) 279*0Sstevel@tonic-gate continue; 280*0Sstevel@tonic-gate 281*0Sstevel@tonic-gate if (cn == cn_list->cl_head) 282*0Sstevel@tonic-gate cn_list->cl_head = cn->cn_next; 283*0Sstevel@tonic-gate else 284*0Sstevel@tonic-gate cn->cn_prev->cn_next = cn->cn_next; 285*0Sstevel@tonic-gate 286*0Sstevel@tonic-gate if (cn->cn_next != NULL) 287*0Sstevel@tonic-gate cn->cn_next->cn_prev = cn->cn_prev; 288*0Sstevel@tonic-gate 289*0Sstevel@tonic-gate cn_destroy(cn); 290*0Sstevel@tonic-gate nreaped++; 291*0Sstevel@tonic-gate } 292*0Sstevel@tonic-gate (void) mutex_unlock(&cn_list->cl_lock); 293*0Sstevel@tonic-gate } 294*0Sstevel@tonic-gate 295*0Sstevel@tonic-gate return (nreaped); 296*0Sstevel@tonic-gate } 297*0Sstevel@tonic-gate 298*0Sstevel@tonic-gate /* 299*0Sstevel@tonic-gate * Hash a container identified by `cn_id' into a 32-bit unsigned integer 300*0Sstevel@tonic-gate * suitable for use as a key in a hash table. 301*0Sstevel@tonic-gate */ 302*0Sstevel@tonic-gate static uint32_t 303*0Sstevel@tonic-gate ds_hash(const char *cn_id) 304*0Sstevel@tonic-gate { 305*0Sstevel@tonic-gate uint32_t result = 0; 306*0Sstevel@tonic-gate unsigned int i; 307*0Sstevel@tonic-gate 308*0Sstevel@tonic-gate for (i = 0; cn_id[i] != '\0'; i++) 309*0Sstevel@tonic-gate result += cn_id[i] << i; 310*0Sstevel@tonic-gate 311*0Sstevel@tonic-gate return (result); 312*0Sstevel@tonic-gate } 313