xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/datastore.c (revision 0:68f95e015346)
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