xref: /onnv-gate/usr/src/lib/libdhcpsvc/private/dsvcd_synch.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 (c) 2001 by Sun Microsystems, Inc.
24*0Sstevel@tonic-gate  * All rights reserved.
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  * Doors-daemon (dsvclockd) synchronization strategy: contacts a standalone
31*0Sstevel@tonic-gate  * daemon to coordinate access to the shared resource across multiple
32*0Sstevel@tonic-gate  * processes and multiple threads within a process.  Performance is slow
33*0Sstevel@tonic-gate  * (about 1200 locks and unlocks per second on a Ultra 170E/167 MHz) but it
34*0Sstevel@tonic-gate  * provides robust locks and scales well as the number of CPUs increase.
35*0Sstevel@tonic-gate  */
36*0Sstevel@tonic-gate 
37*0Sstevel@tonic-gate #include <sys/types.h>
38*0Sstevel@tonic-gate #include <sys/mman.h>
39*0Sstevel@tonic-gate #include <sys/wait.h>
40*0Sstevel@tonic-gate #include <fcntl.h>
41*0Sstevel@tonic-gate #include <unistd.h>
42*0Sstevel@tonic-gate #include <dsvclockd.h>
43*0Sstevel@tonic-gate #include <door.h>
44*0Sstevel@tonic-gate #include <stdlib.h>
45*0Sstevel@tonic-gate #include <errno.h>
46*0Sstevel@tonic-gate #include <string.h>
47*0Sstevel@tonic-gate #include <stdio.h>
48*0Sstevel@tonic-gate #include <dhcp_svc_private.h>
49*0Sstevel@tonic-gate 
50*0Sstevel@tonic-gate static int dsvcd_lock(dsvc_synch_t *, dsvcd_locktype_t, void **);
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate /*
53*0Sstevel@tonic-gate  * Our synchronization-private data which hangs off of sp->s_data; This
54*0Sstevel@tonic-gate  * data is thus per-open-container-instance and (of course) per-process.
55*0Sstevel@tonic-gate  */
56*0Sstevel@tonic-gate typedef struct {
57*0Sstevel@tonic-gate 	int		s_lockfd;		/* door lock request fd */
58*0Sstevel@tonic-gate 	boolean_t	s_crosshost;		/* request crosshost synch */
59*0Sstevel@tonic-gate } dsvcd_synch_t;
60*0Sstevel@tonic-gate 
61*0Sstevel@tonic-gate /*
62*0Sstevel@tonic-gate  * Initialize the dsvclockd synchronization strategy for an open container,
63*0Sstevel@tonic-gate  * whose synchronization information ("synchronization instance") is
64*0Sstevel@tonic-gate  * pointed to by `sp', by opening the door to the dsvclockd.  On success,
65*0Sstevel@tonic-gate  * hang our synchronization-private data off of `sp->s_data'.  Returns a
66*0Sstevel@tonic-gate  * DSVC_* code.
67*0Sstevel@tonic-gate  */
68*0Sstevel@tonic-gate static int
dsvcd_init(dsvc_synch_t * sp,unsigned int synchflags)69*0Sstevel@tonic-gate dsvcd_init(dsvc_synch_t *sp, unsigned int synchflags)
70*0Sstevel@tonic-gate {
71*0Sstevel@tonic-gate 	dsvcd_synch_t	*dsp;
72*0Sstevel@tonic-gate 	char		doorpath[MAXPATHLEN];
73*0Sstevel@tonic-gate 	door_info_t	info;
74*0Sstevel@tonic-gate 	unsigned int	tries;
75*0Sstevel@tonic-gate 	pid_t		dsvclockd_pid;
76*0Sstevel@tonic-gate 	int		fd;
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate 	if (geteuid() != 0)
79*0Sstevel@tonic-gate 		return (DSVC_ACCESS);
80*0Sstevel@tonic-gate 
81*0Sstevel@tonic-gate 	dsp = malloc(sizeof (dsvcd_synch_t));
82*0Sstevel@tonic-gate 	sp->s_data = dsp;
83*0Sstevel@tonic-gate 	if (dsp == NULL)
84*0Sstevel@tonic-gate 		return (DSVC_NO_MEMORY);
85*0Sstevel@tonic-gate 
86*0Sstevel@tonic-gate 	(void) snprintf(doorpath, MAXPATHLEN, DSVCD_DOOR_FMT,
87*0Sstevel@tonic-gate 	    sp->s_datastore->d_resource);
88*0Sstevel@tonic-gate 
89*0Sstevel@tonic-gate 	dsp->s_lockfd = -1;
90*0Sstevel@tonic-gate 	dsp->s_crosshost = (synchflags & DSVC_SYNCH_CROSSHOST) != 0;
91*0Sstevel@tonic-gate 
92*0Sstevel@tonic-gate 	fd = open(doorpath, O_RDONLY);
93*0Sstevel@tonic-gate 	if (fd == -1) {
94*0Sstevel@tonic-gate 		if (errno == EACCES) {
95*0Sstevel@tonic-gate 			free(dsp);
96*0Sstevel@tonic-gate 			sp->s_data = NULL;
97*0Sstevel@tonic-gate 			return (DSVC_ACCESS);
98*0Sstevel@tonic-gate 		}
99*0Sstevel@tonic-gate 	} else {
100*0Sstevel@tonic-gate 		if (door_info(fd, &info) == 0 && info.di_target != -1) {
101*0Sstevel@tonic-gate 			dsp->s_lockfd = fd;
102*0Sstevel@tonic-gate 			return (DSVC_SUCCESS);
103*0Sstevel@tonic-gate 		}
104*0Sstevel@tonic-gate 		(void) close(fd);
105*0Sstevel@tonic-gate 	}
106*0Sstevel@tonic-gate 
107*0Sstevel@tonic-gate 	switch (dsvclockd_pid = fork()) {
108*0Sstevel@tonic-gate 	case -1:
109*0Sstevel@tonic-gate 		break;
110*0Sstevel@tonic-gate 	case 0:
111*0Sstevel@tonic-gate 		/*
112*0Sstevel@tonic-gate 		 * Close all descriptors so messages don't leak through.
113*0Sstevel@tonic-gate 		 */
114*0Sstevel@tonic-gate 		(void) closefrom(0);
115*0Sstevel@tonic-gate 
116*0Sstevel@tonic-gate 		/*
117*0Sstevel@tonic-gate 		 * It's okay if the exec fails; the `default' case below
118*0Sstevel@tonic-gate 		 * will give up and return DSVC_NO_LOCKMGR.
119*0Sstevel@tonic-gate 		 */
120*0Sstevel@tonic-gate 		(void) execl(DSVCD_PATH, DSVCD_PATH, (char *)0);
121*0Sstevel@tonic-gate 		_exit(EXIT_FAILURE);
122*0Sstevel@tonic-gate 	default:
123*0Sstevel@tonic-gate 		/*
124*0Sstevel@tonic-gate 		 * Make five attempts to open the dsvclockd door, each
125*0Sstevel@tonic-gate 		 * spaced a half second apart.
126*0Sstevel@tonic-gate 		 */
127*0Sstevel@tonic-gate 		for (tries = 0; tries < 5; tries++) {
128*0Sstevel@tonic-gate 			fd = open(doorpath, O_RDONLY);
129*0Sstevel@tonic-gate 			if (fd != -1) {
130*0Sstevel@tonic-gate 				if (door_info(fd, &info) == 0 &&
131*0Sstevel@tonic-gate 				    info.di_target != -1) {
132*0Sstevel@tonic-gate 					(void) waitpid(dsvclockd_pid, NULL, 0);
133*0Sstevel@tonic-gate 					dsp->s_lockfd = fd;
134*0Sstevel@tonic-gate 					return (DSVC_SUCCESS);
135*0Sstevel@tonic-gate 				}
136*0Sstevel@tonic-gate 				(void) close(fd);
137*0Sstevel@tonic-gate 			}
138*0Sstevel@tonic-gate 			(void) poll(NULL, 0, 500);
139*0Sstevel@tonic-gate 		}
140*0Sstevel@tonic-gate 		(void) waitpid(dsvclockd_pid, NULL, 0);
141*0Sstevel@tonic-gate 		break;
142*0Sstevel@tonic-gate 	}
143*0Sstevel@tonic-gate 
144*0Sstevel@tonic-gate 	free(dsp);
145*0Sstevel@tonic-gate 	sp->s_data = NULL;
146*0Sstevel@tonic-gate 	return (DSVC_NO_LOCKMGR);
147*0Sstevel@tonic-gate }
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate /*
150*0Sstevel@tonic-gate  * Finish using the dsvclockd synchronization strategy on synchronization
151*0Sstevel@tonic-gate  * instance `sp'.
152*0Sstevel@tonic-gate  */
153*0Sstevel@tonic-gate static void
dsvcd_fini(dsvc_synch_t * sp)154*0Sstevel@tonic-gate dsvcd_fini(dsvc_synch_t *sp)
155*0Sstevel@tonic-gate {
156*0Sstevel@tonic-gate 	dsvcd_synch_t *dsp = sp->s_data;
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate 	sp->s_data = NULL;
159*0Sstevel@tonic-gate 	(void) close(dsp->s_lockfd);
160*0Sstevel@tonic-gate 	free(dsp);
161*0Sstevel@tonic-gate }
162*0Sstevel@tonic-gate 
163*0Sstevel@tonic-gate /*
164*0Sstevel@tonic-gate  * Obtain a shared lock on synchronization instance `sp'.  Upon success,
165*0Sstevel@tonic-gate  * `unlock_cookiep' is set to a token to pass to `dsvcd_unlock' to unlock
166*0Sstevel@tonic-gate  * the lock.  Returns a DSVC_* code.
167*0Sstevel@tonic-gate  */
168*0Sstevel@tonic-gate static int
dsvcd_rdlock(dsvc_synch_t * sp,void ** unlock_cookiep)169*0Sstevel@tonic-gate dsvcd_rdlock(dsvc_synch_t *sp, void **unlock_cookiep)
170*0Sstevel@tonic-gate {
171*0Sstevel@tonic-gate 	return (dsvcd_lock(sp, DSVCD_RDLOCK, unlock_cookiep));
172*0Sstevel@tonic-gate }
173*0Sstevel@tonic-gate 
174*0Sstevel@tonic-gate /*
175*0Sstevel@tonic-gate  * Obtain an exclusive lock on synchronization instance `sp'.  Upon
176*0Sstevel@tonic-gate  * success, `unlock_cookiep' is set to a token to pass to `dsvcd_unlock' to
177*0Sstevel@tonic-gate  * unlock the lock.  Returns a DSVC_* code.
178*0Sstevel@tonic-gate  */
179*0Sstevel@tonic-gate static int
dsvcd_wrlock(dsvc_synch_t * sp,void ** unlock_cookiep)180*0Sstevel@tonic-gate dsvcd_wrlock(dsvc_synch_t *sp, void **unlock_cookiep)
181*0Sstevel@tonic-gate {
182*0Sstevel@tonic-gate 	return (dsvcd_lock(sp, DSVCD_WRLOCK, unlock_cookiep));
183*0Sstevel@tonic-gate }
184*0Sstevel@tonic-gate 
185*0Sstevel@tonic-gate /*
186*0Sstevel@tonic-gate  * Lock the synchronization instance `sp' with a lock of type `locktype'.
187*0Sstevel@tonic-gate  * Upon success, `unlock_cookiep' is set to point to a door descriptor
188*0Sstevel@tonic-gate  * which is used to unlock the lock and to detect if the caller dies
189*0Sstevel@tonic-gate  * holding the lock.  Returns a DSVC_* code.
190*0Sstevel@tonic-gate  */
191*0Sstevel@tonic-gate static int
dsvcd_lock(dsvc_synch_t * sp,dsvcd_locktype_t locktype,void ** unlock_cookiep)192*0Sstevel@tonic-gate dsvcd_lock(dsvc_synch_t *sp, dsvcd_locktype_t locktype, void **unlock_cookiep)
193*0Sstevel@tonic-gate {
194*0Sstevel@tonic-gate 	door_arg_t		args;
195*0Sstevel@tonic-gate 	dsvcd_lock_request_t	request;
196*0Sstevel@tonic-gate 	dsvcd_reply_t		reply;
197*0Sstevel@tonic-gate 	door_desc_t		*descp;
198*0Sstevel@tonic-gate 	int			unlockfd;
199*0Sstevel@tonic-gate 	int			i;
200*0Sstevel@tonic-gate 	dsvcd_synch_t		*dsp = sp->s_data;
201*0Sstevel@tonic-gate 
202*0Sstevel@tonic-gate 	if (dsp->s_lockfd == -1)
203*0Sstevel@tonic-gate 		return (DSVC_NO_LOCKMGR);
204*0Sstevel@tonic-gate 
205*0Sstevel@tonic-gate 	request.lrq_request.rq_version	= DSVCD_DOOR_VERSION;
206*0Sstevel@tonic-gate 	request.lrq_request.rq_reqtype	= DSVCD_LOCK;
207*0Sstevel@tonic-gate 	request.lrq_locktype		= locktype;
208*0Sstevel@tonic-gate 	request.lrq_nonblock		= sp->s_nonblock;
209*0Sstevel@tonic-gate 	request.lrq_crosshost		= dsp->s_crosshost;
210*0Sstevel@tonic-gate 	request.lrq_conver		= sp->s_datastore->d_conver;
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate 	(void) strlcpy(request.lrq_loctoken, sp->s_loctoken,
213*0Sstevel@tonic-gate 	    sizeof (request.lrq_loctoken));
214*0Sstevel@tonic-gate 	(void) strlcpy(request.lrq_conname, sp->s_conname,
215*0Sstevel@tonic-gate 	    sizeof (request.lrq_conname));
216*0Sstevel@tonic-gate 
217*0Sstevel@tonic-gate 	args.data_ptr	= (char *)&request;
218*0Sstevel@tonic-gate 	args.data_size	= sizeof (dsvcd_lock_request_t);
219*0Sstevel@tonic-gate 	args.desc_ptr	= NULL;
220*0Sstevel@tonic-gate 	args.desc_num	= 0;
221*0Sstevel@tonic-gate 	args.rbuf	= (char *)&reply;
222*0Sstevel@tonic-gate 	args.rsize	= sizeof (dsvcd_reply_t);
223*0Sstevel@tonic-gate 
224*0Sstevel@tonic-gate 	if (door_call(dsp->s_lockfd, &args) == -1) {
225*0Sstevel@tonic-gate 		/*
226*0Sstevel@tonic-gate 		 * If the lock manager went away, we'll get back EBADF.
227*0Sstevel@tonic-gate 		 */
228*0Sstevel@tonic-gate 		return (errno == EBADF ? DSVC_NO_LOCKMGR : DSVC_SYNCH_ERR);
229*0Sstevel@tonic-gate 	}
230*0Sstevel@tonic-gate 
231*0Sstevel@tonic-gate 	descp = args.desc_ptr;
232*0Sstevel@tonic-gate 	if (args.desc_num == 0)
233*0Sstevel@tonic-gate 		unlockfd = -1;
234*0Sstevel@tonic-gate 	else {
235*0Sstevel@tonic-gate 		unlockfd = descp->d_data.d_desc.d_descriptor;
236*0Sstevel@tonic-gate 
237*0Sstevel@tonic-gate 		/*
238*0Sstevel@tonic-gate 		 * There shouldn't be more than one descriptor, but close
239*0Sstevel@tonic-gate 		 * any extras to ease future compatibility.
240*0Sstevel@tonic-gate 		 */
241*0Sstevel@tonic-gate 		for (i = 1; i < args.desc_num; i++)
242*0Sstevel@tonic-gate 			(void) close(descp[i].d_data.d_desc.d_descriptor);
243*0Sstevel@tonic-gate 	}
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate 	if (args.rbuf != (char *)&reply) {
246*0Sstevel@tonic-gate 		(void) memcpy(&reply, args.rbuf, sizeof (reply));
247*0Sstevel@tonic-gate 		(void) munmap(args.rbuf, args.rsize);
248*0Sstevel@tonic-gate 	}
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate 	if (args.data_size != sizeof (dsvcd_reply_t) ||
251*0Sstevel@tonic-gate 	    reply.rp_version != DSVCD_DOOR_VERSION) {
252*0Sstevel@tonic-gate 		(void) close(unlockfd);
253*0Sstevel@tonic-gate 		return (DSVC_SYNCH_ERR);
254*0Sstevel@tonic-gate 	}
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 	if (reply.rp_retval == DSVC_SUCCESS && unlockfd == -1)
257*0Sstevel@tonic-gate 		return (DSVC_SYNCH_ERR);
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate 	*unlock_cookiep = (void *)unlockfd;
260*0Sstevel@tonic-gate 	return (reply.rp_retval);
261*0Sstevel@tonic-gate }
262*0Sstevel@tonic-gate 
263*0Sstevel@tonic-gate /*
264*0Sstevel@tonic-gate  * Unlock the synchronization instance `sp' using the unlock token
265*0Sstevel@tonic-gate  * `unlock_cookiep'.  Returns a DSVC_* code.
266*0Sstevel@tonic-gate  */
267*0Sstevel@tonic-gate /* ARGSUSED */
268*0Sstevel@tonic-gate static int
dsvcd_unlock(dsvc_synch_t * sp,void * unlock_cookie)269*0Sstevel@tonic-gate dsvcd_unlock(dsvc_synch_t *sp, void *unlock_cookie)
270*0Sstevel@tonic-gate {
271*0Sstevel@tonic-gate 	door_arg_t		args;
272*0Sstevel@tonic-gate 	dsvcd_unlock_request_t	request;
273*0Sstevel@tonic-gate 	dsvcd_reply_t		reply;
274*0Sstevel@tonic-gate 	int			unlockfd = (int)unlock_cookie;
275*0Sstevel@tonic-gate 	int			i;
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate 	request.urq_request.rq_version = DSVCD_DOOR_VERSION;
278*0Sstevel@tonic-gate 	request.urq_request.rq_reqtype = DSVCD_UNLOCK;
279*0Sstevel@tonic-gate 
280*0Sstevel@tonic-gate 	args.data_ptr	= (char *)&request;
281*0Sstevel@tonic-gate 	args.data_size	= sizeof (dsvcd_unlock_request_t);
282*0Sstevel@tonic-gate 	args.desc_ptr	= NULL;
283*0Sstevel@tonic-gate 	args.desc_num	= 0;
284*0Sstevel@tonic-gate 	args.rbuf	= (char *)&reply;
285*0Sstevel@tonic-gate 	args.rsize	= sizeof (dsvcd_reply_t);
286*0Sstevel@tonic-gate 
287*0Sstevel@tonic-gate 	if (door_call(unlockfd, &args) == -1) {
288*0Sstevel@tonic-gate 		/*
289*0Sstevel@tonic-gate 		 * If the lock manager went away while we had a lock
290*0Sstevel@tonic-gate 		 * checked out, regard that as a synchronization error --
291*0Sstevel@tonic-gate 		 * it should never happen under correct operation.
292*0Sstevel@tonic-gate 		 */
293*0Sstevel@tonic-gate 		return (DSVC_SYNCH_ERR);
294*0Sstevel@tonic-gate 	}
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate 	/*
297*0Sstevel@tonic-gate 	 * There shouldn't be any descriptors returned from the server
298*0Sstevel@tonic-gate 	 * here, but this may change in the future -- close any to ease
299*0Sstevel@tonic-gate 	 * future compatibility.
300*0Sstevel@tonic-gate 	 */
301*0Sstevel@tonic-gate 	for (i = 0; i < args.desc_num; i++)
302*0Sstevel@tonic-gate 		(void) close(args.desc_ptr[i].d_data.d_desc.d_descriptor);
303*0Sstevel@tonic-gate 
304*0Sstevel@tonic-gate 	/*
305*0Sstevel@tonic-gate 	 * Close the unlock door even if the door_call() fails; this is so
306*0Sstevel@tonic-gate 	 * the container gets unlocked even if there's some screwup in the
307*0Sstevel@tonic-gate 	 * graceful unlocking protocol (in that case, this will generate
308*0Sstevel@tonic-gate 	 * a DOOR_UNREF_DATA call).
309*0Sstevel@tonic-gate 	 */
310*0Sstevel@tonic-gate 	(void) close(unlockfd);
311*0Sstevel@tonic-gate 
312*0Sstevel@tonic-gate 	if (args.rbuf != (char *)&reply) {
313*0Sstevel@tonic-gate 		(void) memcpy(&reply, args.rbuf, sizeof (reply));
314*0Sstevel@tonic-gate 		(void) munmap(args.rbuf, args.rsize);
315*0Sstevel@tonic-gate 	}
316*0Sstevel@tonic-gate 
317*0Sstevel@tonic-gate 	if (args.data_size != sizeof (dsvcd_reply_t) ||
318*0Sstevel@tonic-gate 	    reply.rp_version != DSVCD_DOOR_VERSION)
319*0Sstevel@tonic-gate 		return (DSVC_SYNCH_ERR);
320*0Sstevel@tonic-gate 
321*0Sstevel@tonic-gate 	return (reply.rp_retval);
322*0Sstevel@tonic-gate }
323*0Sstevel@tonic-gate 
324*0Sstevel@tonic-gate dsvc_synch_ops_t dsvcd_synch_ops = {
325*0Sstevel@tonic-gate 	dsvcd_init, dsvcd_fini, dsvcd_rdlock, dsvcd_wrlock, dsvcd_unlock
326*0Sstevel@tonic-gate };
327