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 *
ds_create(const char * ds_name,dsvcd_svc_t * ds_callback)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
ds_destroy(dsvcd_datastore_t * ds)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 *
ds_get_container(dsvcd_datastore_t * ds,const char * cn_id,boolean_t crosshost)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
ds_release_container(dsvcd_datastore_t * ds,dsvcd_container_t * cn)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
ds_reap_containers(dsvcd_datastore_t * ds,unsigned int idle)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
ds_hash(const char * cn_id)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 }
312