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