xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/dsvclockd.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 /*
30*0Sstevel@tonic-gate  * #define _POSIX_PTHREAD_SEMANTICS before #including <signal.h> so that we
31*0Sstevel@tonic-gate  * get the right (POSIX) version of sigwait(2).
32*0Sstevel@tonic-gate  */
33*0Sstevel@tonic-gate #define	_POSIX_PTHREAD_SEMANTICS
34*0Sstevel@tonic-gate 
35*0Sstevel@tonic-gate #include <sys/types.h>
36*0Sstevel@tonic-gate #include <sys/stat.h>
37*0Sstevel@tonic-gate #include <sys/mman.h>
38*0Sstevel@tonic-gate #include <sys/sysmacros.h>
39*0Sstevel@tonic-gate #include <dhcp_svc_private.h>
40*0Sstevel@tonic-gate #include <pthread.h>
41*0Sstevel@tonic-gate #include <stdlib.h>
42*0Sstevel@tonic-gate #include <dhcpmsg.h>
43*0Sstevel@tonic-gate #include <assert.h>
44*0Sstevel@tonic-gate #include <stddef.h>
45*0Sstevel@tonic-gate #include <stdio.h>
46*0Sstevel@tonic-gate #include <string.h>
47*0Sstevel@tonic-gate #include <unistd.h>
48*0Sstevel@tonic-gate #include <signal.h>
49*0Sstevel@tonic-gate #include <locale.h>
50*0Sstevel@tonic-gate #include <synch.h>
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate #include "datastore.h"
53*0Sstevel@tonic-gate #include "dsvclockd.h"
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate /*
56*0Sstevel@tonic-gate  * The DHCP service daemon synchronizes access to containers within a given
57*0Sstevel@tonic-gate  * datastore.  Any datastore which is willing to accept the synchronization
58*0Sstevel@tonic-gate  * constraints imposed by the DHCP service daemon can use this daemon in
59*0Sstevel@tonic-gate  * lieu of rolling their own synchronization code.
60*0Sstevel@tonic-gate  *
61*0Sstevel@tonic-gate  * See $SRC/lib/libdhcpsvc/private/README.synch for more information.
62*0Sstevel@tonic-gate  */
63*0Sstevel@tonic-gate 
64*0Sstevel@tonic-gate #ifndef	TEXT_DOMAIN
65*0Sstevel@tonic-gate #define	TEXT_DOMAIN		"SYS_TEST"
66*0Sstevel@tonic-gate #endif
67*0Sstevel@tonic-gate 
68*0Sstevel@tonic-gate #define	DSVCD_REAP_INTERVAL	(60 * 60 * 24)	/* seconds, thus once a day */
69*0Sstevel@tonic-gate #define	DSVCD_REAP_THRESH	(60 * 60)	/* seconds, thus 1 hour stale */
70*0Sstevel@tonic-gate #define	DSVCD_STACK_REDSIZE	(8 * 1024)	/* redzone size, in bytes */
71*0Sstevel@tonic-gate #define	UD_RECLAIM_MAX		128		/* unlock door descriptors */
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate /*
74*0Sstevel@tonic-gate  * Unlock descriptor -- one for each lock granted.  This descriptor is used
75*0Sstevel@tonic-gate  * to subsequently unlock the granted lock (and to synchronize unlocking of
76*0Sstevel@tonic-gate  * the lock; see svc_unlock() below for details).
77*0Sstevel@tonic-gate  */
78*0Sstevel@tonic-gate typedef struct dsvcd_unlock_desc {
79*0Sstevel@tonic-gate 	int			ud_fd;
80*0Sstevel@tonic-gate 	mutex_t			ud_lock;
81*0Sstevel@tonic-gate 	dsvcd_container_t	*ud_cn;
82*0Sstevel@tonic-gate 	struct dsvcd_unlock_desc *ud_next;
83*0Sstevel@tonic-gate } dsvcd_unlock_desc_t;
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate static mutex_t			ud_reclaim_lock = DEFAULTMUTEX;
86*0Sstevel@tonic-gate static unsigned int		ud_reclaim_count = 0;
87*0Sstevel@tonic-gate static dsvcd_unlock_desc_t	*ud_reclaim_list = NULL;
88*0Sstevel@tonic-gate 
89*0Sstevel@tonic-gate static void			*reaper(void *);
90*0Sstevel@tonic-gate static int			daemonize(void);
91*0Sstevel@tonic-gate static void			*stack_create(unsigned int *);
92*0Sstevel@tonic-gate static void			stack_destroy(void *, unsigned int);
93*0Sstevel@tonic-gate static void			doorserv_create(door_info_t *);
94*0Sstevel@tonic-gate static dsvcd_unlock_desc_t	*ud_create(dsvcd_container_t *, int *);
95*0Sstevel@tonic-gate static void			ud_destroy(dsvcd_unlock_desc_t *, boolean_t);
96*0Sstevel@tonic-gate static dsvcd_svc_t		svc_lock, svc_unlock;
97*0Sstevel@tonic-gate 
98*0Sstevel@tonic-gate int
99*0Sstevel@tonic-gate main(int argc, char **argv)
100*0Sstevel@tonic-gate {
101*0Sstevel@tonic-gate 	dsvcd_datastore_t	**ds_table;
102*0Sstevel@tonic-gate 	dsvc_datastore_t	dd;
103*0Sstevel@tonic-gate 	dsvc_synchtype_t	synchtype;
104*0Sstevel@tonic-gate 	char			**modules;
105*0Sstevel@tonic-gate 	unsigned int		i, j;
106*0Sstevel@tonic-gate 	int			debug_level = 0;
107*0Sstevel@tonic-gate 	boolean_t		is_daemon = B_TRUE;
108*0Sstevel@tonic-gate 	boolean_t		is_verbose = B_FALSE;
109*0Sstevel@tonic-gate 	int			sig, nmodules, nsynchmods, c;
110*0Sstevel@tonic-gate 	sigset_t		sigset;
111*0Sstevel@tonic-gate 	char			signame[SIG2STR_MAX];
112*0Sstevel@tonic-gate 	char			*progname;
113*0Sstevel@tonic-gate 	void			*stackbase;
114*0Sstevel@tonic-gate 	unsigned int		stacksize = 16 * 1024;
115*0Sstevel@tonic-gate 	struct rlimit		rl;
116*0Sstevel@tonic-gate 
117*0Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
118*0Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
119*0Sstevel@tonic-gate 
120*0Sstevel@tonic-gate 	/*
121*0Sstevel@tonic-gate 	 * Mask all signals except SIGABRT; doing this here ensures that
122*0Sstevel@tonic-gate 	 * all threads created through door_create() have them masked too.
123*0Sstevel@tonic-gate 	 */
124*0Sstevel@tonic-gate 	(void) sigfillset(&sigset);
125*0Sstevel@tonic-gate 	(void) sigdelset(&sigset, SIGABRT);
126*0Sstevel@tonic-gate 	(void) thr_sigsetmask(SIG_BLOCK, &sigset, NULL);
127*0Sstevel@tonic-gate 
128*0Sstevel@tonic-gate 	/*
129*0Sstevel@tonic-gate 	 * Figure out our program name; just keep the final piece so that
130*0Sstevel@tonic-gate 	 * our dhcpmsg() messages don't get too long.
131*0Sstevel@tonic-gate 	 */
132*0Sstevel@tonic-gate 	progname = strrchr(argv[0], '/');
133*0Sstevel@tonic-gate 	if (progname != NULL)
134*0Sstevel@tonic-gate 		progname++;
135*0Sstevel@tonic-gate 	else
136*0Sstevel@tonic-gate 		progname = argv[0];
137*0Sstevel@tonic-gate 
138*0Sstevel@tonic-gate 	/*
139*0Sstevel@tonic-gate 	 * Set the door thread creation procedure so that all of our
140*0Sstevel@tonic-gate 	 * threads are created with thread stacks with backing store.
141*0Sstevel@tonic-gate 	 */
142*0Sstevel@tonic-gate 	(void) door_server_create(doorserv_create);
143*0Sstevel@tonic-gate 
144*0Sstevel@tonic-gate 	while ((c = getopt(argc, argv, "d:fv")) != EOF) {
145*0Sstevel@tonic-gate 		switch (c) {
146*0Sstevel@tonic-gate 
147*0Sstevel@tonic-gate 		case 'd':
148*0Sstevel@tonic-gate 			debug_level = atoi(optarg);
149*0Sstevel@tonic-gate 			break;
150*0Sstevel@tonic-gate 
151*0Sstevel@tonic-gate 		case 'f':
152*0Sstevel@tonic-gate 			is_daemon = B_FALSE;
153*0Sstevel@tonic-gate 			break;
154*0Sstevel@tonic-gate 
155*0Sstevel@tonic-gate 		case 'v':
156*0Sstevel@tonic-gate 			is_verbose = B_TRUE;
157*0Sstevel@tonic-gate 			break;
158*0Sstevel@tonic-gate 
159*0Sstevel@tonic-gate 		case '?':
160*0Sstevel@tonic-gate 			(void) fprintf(stderr,
161*0Sstevel@tonic-gate 			    gettext("usage: %s [-dn] [-f] [-v]\n"), progname);
162*0Sstevel@tonic-gate 			return (EXIT_FAILURE);
163*0Sstevel@tonic-gate 
164*0Sstevel@tonic-gate 		default:
165*0Sstevel@tonic-gate 			break;
166*0Sstevel@tonic-gate 		}
167*0Sstevel@tonic-gate 	}
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate 	if (geteuid() != 0) {
170*0Sstevel@tonic-gate 		dhcpmsg_init(progname, B_FALSE, is_verbose, debug_level);
171*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERROR, "must be super-user");
172*0Sstevel@tonic-gate 		dhcpmsg_fini();
173*0Sstevel@tonic-gate 		return (EXIT_FAILURE);
174*0Sstevel@tonic-gate 	}
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate 	if (is_daemon && daemonize() == 0) {
177*0Sstevel@tonic-gate 		dhcpmsg_init(progname, B_FALSE, is_verbose, debug_level);
178*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERROR, "cannot become daemon, exiting");
179*0Sstevel@tonic-gate 		dhcpmsg_fini();
180*0Sstevel@tonic-gate 		return (EXIT_FAILURE);
181*0Sstevel@tonic-gate 	}
182*0Sstevel@tonic-gate 
183*0Sstevel@tonic-gate 	dhcpmsg_init(progname, is_daemon, is_verbose, debug_level);
184*0Sstevel@tonic-gate 	(void) atexit(dhcpmsg_fini);
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate 	/*
187*0Sstevel@tonic-gate 	 * Max out the number available descriptors since we need to
188*0Sstevel@tonic-gate 	 * allocate two per held lock.
189*0Sstevel@tonic-gate 	 */
190*0Sstevel@tonic-gate 	rl.rlim_cur = RLIM_INFINITY;
191*0Sstevel@tonic-gate 	rl.rlim_max = RLIM_INFINITY;
192*0Sstevel@tonic-gate 	if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
193*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "setrlimit failed");
194*0Sstevel@tonic-gate 
195*0Sstevel@tonic-gate 	if (enumerate_dd(&modules, &nmodules) != DSVC_SUCCESS) {
196*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERROR, "cannot enumerate public modules, exiting");
197*0Sstevel@tonic-gate 		return (EXIT_FAILURE);
198*0Sstevel@tonic-gate 	}
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate 	/*
201*0Sstevel@tonic-gate 	 * NOTE: this code assumes that a module that needs dsvclockd will
202*0Sstevel@tonic-gate 	 * always need it (even as the container version is ramped).  If
203*0Sstevel@tonic-gate 	 * this becomes bogus in a future release, we'll have to make this
204*0Sstevel@tonic-gate 	 * logic more sophisticated.
205*0Sstevel@tonic-gate 	 */
206*0Sstevel@tonic-gate 	nsynchmods = nmodules;
207*0Sstevel@tonic-gate 	for (i = 0; i < nmodules; i++) {
208*0Sstevel@tonic-gate 		dd.d_resource = modules[i];
209*0Sstevel@tonic-gate 		dd.d_conver = DSVC_CUR_CONVER;
210*0Sstevel@tonic-gate 		dd.d_location = "";
211*0Sstevel@tonic-gate 		if (module_synchtype(&dd, &synchtype) != DSVC_SUCCESS) {
212*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "cannot determine synchronization "
213*0Sstevel@tonic-gate 			    "type for `%s', skipping", modules[i]);
214*0Sstevel@tonic-gate 			free(modules[i]);
215*0Sstevel@tonic-gate 			modules[i] = NULL;
216*0Sstevel@tonic-gate 			nsynchmods--;
217*0Sstevel@tonic-gate 			continue;
218*0Sstevel@tonic-gate 		}
219*0Sstevel@tonic-gate 		if ((synchtype & DSVC_SYNCH_STRATMASK) != DSVC_SYNCH_DSVCD) {
220*0Sstevel@tonic-gate 			free(modules[i]);
221*0Sstevel@tonic-gate 			modules[i] = NULL;
222*0Sstevel@tonic-gate 			nsynchmods--;
223*0Sstevel@tonic-gate 		}
224*0Sstevel@tonic-gate 	}
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate 	if (nsynchmods == 0) {
227*0Sstevel@tonic-gate 		dhcpmsg(MSG_INFO, "no public modules need synchronization");
228*0Sstevel@tonic-gate 		return (EXIT_SUCCESS);
229*0Sstevel@tonic-gate 	}
230*0Sstevel@tonic-gate 
231*0Sstevel@tonic-gate 	/*
232*0Sstevel@tonic-gate 	 * Allocate the datastore table; include one extra entry so that
233*0Sstevel@tonic-gate 	 * the table is NULL-terminated.
234*0Sstevel@tonic-gate 	 */
235*0Sstevel@tonic-gate 	ds_table = calloc(nsynchmods + 1, sizeof (dsvcd_datastore_t *));
236*0Sstevel@tonic-gate 	if (ds_table == NULL) {
237*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "cannot allocate datastore table, exiting");
238*0Sstevel@tonic-gate 		return (EXIT_FAILURE);
239*0Sstevel@tonic-gate 	}
240*0Sstevel@tonic-gate 	ds_table[nsynchmods] = NULL;
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate 	/*
243*0Sstevel@tonic-gate 	 * Create the datastores (which implicitly creates the doors).
244*0Sstevel@tonic-gate 	 * then sit around and wait for requests to come in on the doors.
245*0Sstevel@tonic-gate 	 */
246*0Sstevel@tonic-gate 	for (i = 0, j = 0; i < nmodules; i++) {
247*0Sstevel@tonic-gate 		if (modules[i] != NULL) {
248*0Sstevel@tonic-gate 			ds_table[j] = ds_create(modules[i], svc_lock);
249*0Sstevel@tonic-gate 			if (ds_table[j] == NULL) {
250*0Sstevel@tonic-gate 				while (j-- > 0)
251*0Sstevel@tonic-gate 					ds_destroy(ds_table[j]);
252*0Sstevel@tonic-gate 				return (EXIT_FAILURE);
253*0Sstevel@tonic-gate 			}
254*0Sstevel@tonic-gate 			free(modules[i]);
255*0Sstevel@tonic-gate 			j++;
256*0Sstevel@tonic-gate 		}
257*0Sstevel@tonic-gate 	}
258*0Sstevel@tonic-gate 	free(modules);
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate 	stackbase = stack_create(&stacksize);
261*0Sstevel@tonic-gate 	if (stackbase == NULL)
262*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "cannot create reaper stack; containers "
263*0Sstevel@tonic-gate 		    "will not be reaped");
264*0Sstevel@tonic-gate 	else {
265*0Sstevel@tonic-gate 		errno = thr_create(stackbase, stacksize, reaper, ds_table,
266*0Sstevel@tonic-gate 		    THR_DAEMON, NULL);
267*0Sstevel@tonic-gate 		if (errno != 0) {
268*0Sstevel@tonic-gate 			dhcpmsg(MSG_ERR, "cannot create reaper thread; "
269*0Sstevel@tonic-gate 			    "containers will not be reaped");
270*0Sstevel@tonic-gate 			stack_destroy(stackbase, stacksize);
271*0Sstevel@tonic-gate 		}
272*0Sstevel@tonic-gate 	}
273*0Sstevel@tonic-gate 
274*0Sstevel@tonic-gate 	/*
275*0Sstevel@tonic-gate 	 * Synchronously wait for a QUIT, TERM, or INT, then shutdown.
276*0Sstevel@tonic-gate 	 */
277*0Sstevel@tonic-gate 	(void) sigemptyset(&sigset);
278*0Sstevel@tonic-gate 	(void) sigaddset(&sigset, SIGQUIT);
279*0Sstevel@tonic-gate 	(void) sigaddset(&sigset, SIGTERM);
280*0Sstevel@tonic-gate 	(void) sigaddset(&sigset, SIGINT);
281*0Sstevel@tonic-gate 
282*0Sstevel@tonic-gate 	(void) sigwait(&sigset, &sig);
283*0Sstevel@tonic-gate 	if (sig != SIGTERM && sig != SIGQUIT && sig != SIGINT)
284*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "received unexpected signal");
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate 	if (sig2str(sig, signame) == -1)
287*0Sstevel@tonic-gate 		(void) strlcpy(signame, "???", sizeof (signame));
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate 	dhcpmsg(MSG_INFO, "shutting down via SIG%s", signame);
290*0Sstevel@tonic-gate 
291*0Sstevel@tonic-gate 	for (i = 0; i < nsynchmods; i++)
292*0Sstevel@tonic-gate 		ds_destroy(ds_table[i]);
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate 	return (EXIT_SUCCESS);
295*0Sstevel@tonic-gate }
296*0Sstevel@tonic-gate 
297*0Sstevel@tonic-gate /*
298*0Sstevel@tonic-gate  * Sanity check that dsvcd_request_t `req' (which is `reqsize' bytes long)
299*0Sstevel@tonic-gate  * is a correctly formed request; if not, return an error which will be
300*0Sstevel@tonic-gate  * returned to the door caller.
301*0Sstevel@tonic-gate  */
302*0Sstevel@tonic-gate static int
303*0Sstevel@tonic-gate check_door_req(dsvcd_request_t *req, size_t reqsize, size_t minsize)
304*0Sstevel@tonic-gate {
305*0Sstevel@tonic-gate 	door_cred_t cred;
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 	if (req == NULL) {
308*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "empty request, ignoring");
309*0Sstevel@tonic-gate 		return (DSVC_SYNCH_ERR);
310*0Sstevel@tonic-gate 	}
311*0Sstevel@tonic-gate 
312*0Sstevel@tonic-gate 	/*
313*0Sstevel@tonic-gate 	 * Check credentials; we don't allow any non-super-user requests
314*0Sstevel@tonic-gate 	 * since this would open a denial-of-service hole (since a lock
315*0Sstevel@tonic-gate 	 * could be checked out indefinitely).
316*0Sstevel@tonic-gate 	 */
317*0Sstevel@tonic-gate 	if (door_cred(&cred) != 0) {
318*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "request with unknown credentials");
319*0Sstevel@tonic-gate 		return (DSVC_ACCESS);
320*0Sstevel@tonic-gate 	}
321*0Sstevel@tonic-gate 
322*0Sstevel@tonic-gate 	if (cred.dc_euid != 0) {
323*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "request with non-super-user credentials");
324*0Sstevel@tonic-gate 		return (DSVC_ACCESS);
325*0Sstevel@tonic-gate 	}
326*0Sstevel@tonic-gate 
327*0Sstevel@tonic-gate 	/*
328*0Sstevel@tonic-gate 	 * Check the version and size; we check this before checking the
329*0Sstevel@tonic-gate 	 * size of the request structure since an "incompatible version"
330*0Sstevel@tonic-gate 	 * message is more helpful than a "short request" message.
331*0Sstevel@tonic-gate 	 */
332*0Sstevel@tonic-gate 	if (reqsize > offsetof(dsvcd_request_t, rq_version) &&
333*0Sstevel@tonic-gate 	    req->rq_version != DSVCD_DOOR_VERSION) {
334*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "request with unsupported version `%d'",
335*0Sstevel@tonic-gate 		    req->rq_version);
336*0Sstevel@tonic-gate 		return (DSVC_SYNCH_ERR);
337*0Sstevel@tonic-gate 	}
338*0Sstevel@tonic-gate 
339*0Sstevel@tonic-gate 	if (reqsize < minsize) {
340*0Sstevel@tonic-gate 		dhcpmsg(MSG_VERBOSE, "short request (%d bytes, minimum %d "
341*0Sstevel@tonic-gate 		    "bytes)", reqsize, minsize);
342*0Sstevel@tonic-gate 		return (DSVC_SYNCH_ERR);
343*0Sstevel@tonic-gate 	}
344*0Sstevel@tonic-gate 
345*0Sstevel@tonic-gate 	return (DSVC_SUCCESS);
346*0Sstevel@tonic-gate }
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 
349*0Sstevel@tonic-gate /*
350*0Sstevel@tonic-gate  * Service a lock request `req' passed across the door for datastore `ds'.
351*0Sstevel@tonic-gate  * After verifying that the request is well-formed, locks the container and
352*0Sstevel@tonic-gate  * creates an "unlock" door descriptor that the client uses to unlock the
353*0Sstevel@tonic-gate  * door (either explicitly through door_call()) or implicitly through
354*0Sstevel@tonic-gate  * terminating abnormally).
355*0Sstevel@tonic-gate  */
356*0Sstevel@tonic-gate /* ARGSUSED */
357*0Sstevel@tonic-gate static void
358*0Sstevel@tonic-gate svc_lock(void *cookie, dsvcd_request_t *req, size_t reqsize,
359*0Sstevel@tonic-gate     door_desc_t *doorp, uint_t ndoors)
360*0Sstevel@tonic-gate {
361*0Sstevel@tonic-gate 	dsvcd_reply_t		reply;
362*0Sstevel@tonic-gate 	door_desc_t		door_desc;
363*0Sstevel@tonic-gate 	dsvcd_lock_request_t	*lreq = (dsvcd_lock_request_t *)req;
364*0Sstevel@tonic-gate 	dsvcd_datastore_t	*ds = (dsvcd_datastore_t *)cookie;
365*0Sstevel@tonic-gate 	dsvcd_container_t	*cn;
366*0Sstevel@tonic-gate 	dsvcd_unlock_desc_t	*ud;
367*0Sstevel@tonic-gate 	char			conid[MAXPATHLEN];
368*0Sstevel@tonic-gate 	unsigned int		attempts = 0;
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate 	reply.rp_version = DSVCD_DOOR_VERSION;
371*0Sstevel@tonic-gate 	reply.rp_retval  = check_door_req(req, reqsize,
372*0Sstevel@tonic-gate 	    sizeof (dsvcd_lock_request_t));
373*0Sstevel@tonic-gate 	if (reply.rp_retval != DSVC_SUCCESS) {
374*0Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
375*0Sstevel@tonic-gate 		return;
376*0Sstevel@tonic-gate 	}
377*0Sstevel@tonic-gate 
378*0Sstevel@tonic-gate 	/*
379*0Sstevel@tonic-gate 	 * Verify that this is a lock request; if in the future we support
380*0Sstevel@tonic-gate 	 * other requests, we'll have to abstract this a bit.
381*0Sstevel@tonic-gate 	 */
382*0Sstevel@tonic-gate 	if (req->rq_reqtype != DSVCD_LOCK) {
383*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "unsupported request `%d' on lock "
384*0Sstevel@tonic-gate 		    "request door", req->rq_reqtype);
385*0Sstevel@tonic-gate 		reply.rp_retval = DSVC_SYNCH_ERR;
386*0Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
387*0Sstevel@tonic-gate 		return;
388*0Sstevel@tonic-gate 	}
389*0Sstevel@tonic-gate 	if (lreq->lrq_locktype != DSVCD_RDLOCK &&
390*0Sstevel@tonic-gate 	    lreq->lrq_locktype != DSVCD_WRLOCK) {
391*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "request for unsupported locktype `%d'",
392*0Sstevel@tonic-gate 		    lreq->lrq_locktype);
393*0Sstevel@tonic-gate 		reply.rp_retval = DSVC_SYNCH_ERR;
394*0Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
395*0Sstevel@tonic-gate 		return;
396*0Sstevel@tonic-gate 	}
397*0Sstevel@tonic-gate 
398*0Sstevel@tonic-gate 	/*
399*0Sstevel@tonic-gate 	 * Find the container; create if it doesn't already exist.  We do
400*0Sstevel@tonic-gate 	 * this as a single operation to avoid race conditions.
401*0Sstevel@tonic-gate 	 */
402*0Sstevel@tonic-gate 	(void) snprintf(conid, sizeof (conid), "%s/%s%d_%s", lreq->lrq_loctoken,
403*0Sstevel@tonic-gate 	    ds->ds_name, lreq->lrq_conver, lreq->lrq_conname);
404*0Sstevel@tonic-gate 	cn = ds_get_container(ds, conid, lreq->lrq_crosshost);
405*0Sstevel@tonic-gate 	if (cn == NULL) {
406*0Sstevel@tonic-gate 		reply.rp_retval = DSVC_NO_MEMORY;
407*0Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
408*0Sstevel@tonic-gate 		return;
409*0Sstevel@tonic-gate 	}
410*0Sstevel@tonic-gate 
411*0Sstevel@tonic-gate 	/*
412*0Sstevel@tonic-gate 	 * We need another door descriptor which is passed back with the
413*0Sstevel@tonic-gate 	 * request.  This descriptor is used when the caller wants to
414*0Sstevel@tonic-gate 	 * gracefully unlock or when the caller terminates abnormally.
415*0Sstevel@tonic-gate 	 */
416*0Sstevel@tonic-gate 	ud = ud_create(cn, &reply.rp_retval);
417*0Sstevel@tonic-gate 	if (ud == NULL) {
418*0Sstevel@tonic-gate 		ds_release_container(ds, cn);
419*0Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
420*0Sstevel@tonic-gate 		return;
421*0Sstevel@tonic-gate 	}
422*0Sstevel@tonic-gate 
423*0Sstevel@tonic-gate 	/*
424*0Sstevel@tonic-gate 	 * We pass a duped door descriptor with the DOOR_RELEASE flag set
425*0Sstevel@tonic-gate 	 * instead of just passing the descriptor itself to handle the case
426*0Sstevel@tonic-gate 	 * where the client has gone away before we door_return().  Since
427*0Sstevel@tonic-gate 	 * we duped, the door descriptor itself will have a refcount of 2
428*0Sstevel@tonic-gate 	 * when we go to pass it to the client; if the client does not
429*0Sstevel@tonic-gate 	 * exist, the DOOR_RELEASE will drop the count from 2 to 1 which
430*0Sstevel@tonic-gate 	 * will cause a DOOR_UNREF_DATA call.
431*0Sstevel@tonic-gate 	 *
432*0Sstevel@tonic-gate 	 * In the regular (non-error) case, the door_return() will handoff
433*0Sstevel@tonic-gate 	 * the descriptor to the client, bumping the refcount to 3, and
434*0Sstevel@tonic-gate 	 * then the DOOR_RELEASE will drop the count to 2.  If the client
435*0Sstevel@tonic-gate 	 * terminates abnormally after this point, the count will drop from
436*0Sstevel@tonic-gate 	 * 2 to 1 which will cause a DOOR_UNREF_DATA call.  If the client
437*0Sstevel@tonic-gate 	 * unlocks gracefully, the refcount will still be 2 when the unlock
438*0Sstevel@tonic-gate 	 * door server procedure is called, and the unlock procedure will
439*0Sstevel@tonic-gate 	 * unlock the lock and note that the lock has been unlocked (so
440*0Sstevel@tonic-gate 	 * that we know the DOOR_UNREF_DATA call generated from the client
441*0Sstevel@tonic-gate 	 * subsequently closing the unlock descriptor is benign).
442*0Sstevel@tonic-gate 	 *
443*0Sstevel@tonic-gate 	 * Note that a DOOR_UNREF_DATA call will be generated *any time*
444*0Sstevel@tonic-gate 	 * the refcount goes from 2 to 1 -- even if *we* cause it to
445*0Sstevel@tonic-gate 	 * happen, which by default will happen in some of the error logic
446*0Sstevel@tonic-gate 	 * below (when we close the duped descriptor).  To prevent this
447*0Sstevel@tonic-gate 	 * scenario, we tell ud_destroy() *not* to cache the unlock
448*0Sstevel@tonic-gate 	 * descriptor, which forces it to blow away the descriptor using
449*0Sstevel@tonic-gate 	 * door_revoke(), making the close() that follows benign.
450*0Sstevel@tonic-gate 	 */
451*0Sstevel@tonic-gate 	door_desc.d_attributes = DOOR_DESCRIPTOR|DOOR_RELEASE;
452*0Sstevel@tonic-gate 	door_desc.d_data.d_desc.d_descriptor = dup(ud->ud_fd);
453*0Sstevel@tonic-gate 	if (door_desc.d_data.d_desc.d_descriptor == -1) {
454*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "cannot dup unlock door; denying %s "
455*0Sstevel@tonic-gate 		    "lock request", cn->cn_id);
456*0Sstevel@tonic-gate 		ud_destroy(ud, B_TRUE);
457*0Sstevel@tonic-gate 		ds_release_container(ds, cn);
458*0Sstevel@tonic-gate 		reply.rp_retval = DSVC_NO_RESOURCES;
459*0Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
460*0Sstevel@tonic-gate 		return;
461*0Sstevel@tonic-gate 	}
462*0Sstevel@tonic-gate 
463*0Sstevel@tonic-gate 	/*
464*0Sstevel@tonic-gate 	 * Acquire the actual read or write lock on the container.
465*0Sstevel@tonic-gate 	 */
466*0Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "tid %d: %s locking %s", thr_self(),
467*0Sstevel@tonic-gate 	    lreq->lrq_locktype == DSVCD_RDLOCK ? "read" : "write", cn->cn_id);
468*0Sstevel@tonic-gate 
469*0Sstevel@tonic-gate 	if (lreq->lrq_locktype == DSVCD_RDLOCK)
470*0Sstevel@tonic-gate 		reply.rp_retval = cn_rdlock(cn, lreq->lrq_nonblock);
471*0Sstevel@tonic-gate 	else if (lreq->lrq_locktype == DSVCD_WRLOCK)
472*0Sstevel@tonic-gate 		reply.rp_retval = cn_wrlock(cn, lreq->lrq_nonblock);
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "tid %d: %s %s lock operation: %s", thr_self(),
475*0Sstevel@tonic-gate 	    cn->cn_id, lreq->lrq_locktype == DSVCD_RDLOCK ? "read" : "write",
476*0Sstevel@tonic-gate 	    dhcpsvc_errmsg(reply.rp_retval));
477*0Sstevel@tonic-gate 
478*0Sstevel@tonic-gate 	ds_release_container(ds, cn);
479*0Sstevel@tonic-gate 	if (reply.rp_retval != DSVC_SUCCESS) {
480*0Sstevel@tonic-gate 		ud_destroy(ud, B_FALSE);
481*0Sstevel@tonic-gate 		(void) close(door_desc.d_data.d_desc.d_descriptor);
482*0Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
483*0Sstevel@tonic-gate 		return;
484*0Sstevel@tonic-gate 	}
485*0Sstevel@tonic-gate 
486*0Sstevel@tonic-gate 	while (door_return((char *)&reply, sizeof (reply), &door_desc, 1)
487*0Sstevel@tonic-gate 	    == -1 && errno == EMFILE) {
488*0Sstevel@tonic-gate 		if (lreq->lrq_nonblock) {
489*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "unable to grant lock; client"
490*0Sstevel@tonic-gate 			    " is out of file descriptors");
491*0Sstevel@tonic-gate 			(void) cn_unlock(cn);
492*0Sstevel@tonic-gate 			ud_destroy(ud, B_FALSE);
493*0Sstevel@tonic-gate 			(void) close(door_desc.d_data.d_desc.d_descriptor);
494*0Sstevel@tonic-gate 			reply.rp_retval = DSVC_BUSY;
495*0Sstevel@tonic-gate 			(void) door_return((char *)&reply, sizeof (reply),
496*0Sstevel@tonic-gate 			    NULL, 0);
497*0Sstevel@tonic-gate 			return;
498*0Sstevel@tonic-gate 		}
499*0Sstevel@tonic-gate 
500*0Sstevel@tonic-gate 		if (attempts++ == 0) {
501*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "unable to grant lock; client"
502*0Sstevel@tonic-gate 			    " is out of file descriptors (retrying)");
503*0Sstevel@tonic-gate 		}
504*0Sstevel@tonic-gate 		(void) poll(NULL, 0, 100);
505*0Sstevel@tonic-gate 	}
506*0Sstevel@tonic-gate }
507*0Sstevel@tonic-gate 
508*0Sstevel@tonic-gate /*
509*0Sstevel@tonic-gate  * Service an unlock request `req' passed across the door associated with
510*0Sstevel@tonic-gate  * the unlock token `cookie'.  We may be called explicitly (in which case
511*0Sstevel@tonic-gate  * the request is a well-formed dsvcd_request_t) or implicitly (in which
512*0Sstevel@tonic-gate  * case our request is set to the value DOOR_UNREF_DATA); this latter case
513*0Sstevel@tonic-gate  * occurs when a process holding a lock terminates.  In either case, unlock
514*0Sstevel@tonic-gate  * the lock; in the implicit case, log a message as well.
515*0Sstevel@tonic-gate  */
516*0Sstevel@tonic-gate /* ARGSUSED */
517*0Sstevel@tonic-gate static void
518*0Sstevel@tonic-gate svc_unlock(void *cookie, dsvcd_request_t *req, size_t reqsize,
519*0Sstevel@tonic-gate     door_desc_t *doorp, uint_t ndoors)
520*0Sstevel@tonic-gate {
521*0Sstevel@tonic-gate 	dsvcd_unlock_desc_t	*ud = cookie;
522*0Sstevel@tonic-gate 	dsvcd_container_t	*cn;
523*0Sstevel@tonic-gate 	dsvcd_reply_t		reply;
524*0Sstevel@tonic-gate 
525*0Sstevel@tonic-gate 	/*
526*0Sstevel@tonic-gate 	 * Although unlock descriptors are handed out to only a single
527*0Sstevel@tonic-gate 	 * thread who has been granted a lock (ergo it seems that only one
528*0Sstevel@tonic-gate 	 * thread should be able to call us back), there's a potential race
529*0Sstevel@tonic-gate 	 * here if the process crashes while in this door_call(), since
530*0Sstevel@tonic-gate 	 * both this thread and the unref kernel upcall thread may run at
531*0Sstevel@tonic-gate 	 * the same time.  Protect against this case with a mutex.
532*0Sstevel@tonic-gate 	 */
533*0Sstevel@tonic-gate 	(void) mutex_lock(&ud->ud_lock);
534*0Sstevel@tonic-gate 	cn = ud->ud_cn;
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate 	/*
537*0Sstevel@tonic-gate 	 * First handle the case where the lock owner has closed the unlock
538*0Sstevel@tonic-gate 	 * descriptor, either because they have unlocked the lock and are
539*0Sstevel@tonic-gate 	 * thus done using the descriptor, or because they crashed.  In the
540*0Sstevel@tonic-gate 	 * second case, print a message.
541*0Sstevel@tonic-gate 	 */
542*0Sstevel@tonic-gate 	if (req == DOOR_UNREF_DATA) {
543*0Sstevel@tonic-gate 		/*
544*0Sstevel@tonic-gate 		 * The last reference is ours; we can free the descriptor.
545*0Sstevel@tonic-gate 		 */
546*0Sstevel@tonic-gate 		(void) mutex_unlock(&ud->ud_lock);
547*0Sstevel@tonic-gate 		ud_destroy(ud, B_TRUE);
548*0Sstevel@tonic-gate 
549*0Sstevel@tonic-gate 		/*
550*0Sstevel@tonic-gate 		 * Normal case: the caller is closing the unlock descriptor
551*0Sstevel@tonic-gate 		 * on a lock they've already unlocked -- just return.
552*0Sstevel@tonic-gate 		 */
553*0Sstevel@tonic-gate 		if (cn == NULL) {
554*0Sstevel@tonic-gate 			(void) door_return(NULL, 0, NULL, 0);
555*0Sstevel@tonic-gate 			return;
556*0Sstevel@tonic-gate 		}
557*0Sstevel@tonic-gate 
558*0Sstevel@tonic-gate 		/*
559*0Sstevel@tonic-gate 		 * Error case: the caller has crashed while holding the
560*0Sstevel@tonic-gate 		 * unlock descriptor (or is otherwise in violation of
561*0Sstevel@tonic-gate 		 * protocol).  Since all datastores are required to be
562*0Sstevel@tonic-gate 		 * robust even if unexpected termination occurs, we assume
563*0Sstevel@tonic-gate 		 * the container is not corrupt, even if the process
564*0Sstevel@tonic-gate 		 * crashed with the write lock held.
565*0Sstevel@tonic-gate 		 */
566*0Sstevel@tonic-gate 		switch (cn_locktype(cn)) {
567*0Sstevel@tonic-gate 		case DSVCD_RDLOCK:
568*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "process exited while reading "
569*0Sstevel@tonic-gate 			    "`%s'; unlocking", cn->cn_id);
570*0Sstevel@tonic-gate 			(void) cn_unlock(cn);
571*0Sstevel@tonic-gate 			break;
572*0Sstevel@tonic-gate 
573*0Sstevel@tonic-gate 		case DSVCD_WRLOCK:
574*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "process exited while writing "
575*0Sstevel@tonic-gate 			    "`%s'; unlocking", cn->cn_id);
576*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "note that this write operation "
577*0Sstevel@tonic-gate 			    "may or may not have succeeded");
578*0Sstevel@tonic-gate 			(void) cn_unlock(cn);
579*0Sstevel@tonic-gate 			break;
580*0Sstevel@tonic-gate 
581*0Sstevel@tonic-gate 		case DSVCD_NOLOCK:
582*0Sstevel@tonic-gate 			dhcpmsg(MSG_CRIT, "unreferenced unheld lock");
583*0Sstevel@tonic-gate 			break;
584*0Sstevel@tonic-gate 		}
585*0Sstevel@tonic-gate 
586*0Sstevel@tonic-gate 		(void) door_return(NULL, 0, NULL, 0);
587*0Sstevel@tonic-gate 		return;
588*0Sstevel@tonic-gate 	}
589*0Sstevel@tonic-gate 
590*0Sstevel@tonic-gate 	/*
591*0Sstevel@tonic-gate 	 * Verify that this is a unlock request; if in the future we support
592*0Sstevel@tonic-gate 	 * other requests, we'll have to abstract this a bit.
593*0Sstevel@tonic-gate 	 */
594*0Sstevel@tonic-gate 	reply.rp_version = DSVCD_DOOR_VERSION;
595*0Sstevel@tonic-gate 	reply.rp_retval = check_door_req(req, reqsize,
596*0Sstevel@tonic-gate 	    sizeof (dsvcd_unlock_request_t));
597*0Sstevel@tonic-gate 	if (reply.rp_retval != DSVC_SUCCESS) {
598*0Sstevel@tonic-gate 		(void) mutex_unlock(&ud->ud_lock);
599*0Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
600*0Sstevel@tonic-gate 		return;
601*0Sstevel@tonic-gate 	}
602*0Sstevel@tonic-gate 
603*0Sstevel@tonic-gate 	if (req->rq_reqtype != DSVCD_UNLOCK) {
604*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "unsupported request `%d' on unlock "
605*0Sstevel@tonic-gate 		    "request door", req->rq_reqtype);
606*0Sstevel@tonic-gate 		(void) mutex_unlock(&ud->ud_lock);
607*0Sstevel@tonic-gate 		reply.rp_retval = DSVC_SYNCH_ERR;
608*0Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
609*0Sstevel@tonic-gate 		return;
610*0Sstevel@tonic-gate 	}
611*0Sstevel@tonic-gate 
612*0Sstevel@tonic-gate 	/*
613*0Sstevel@tonic-gate 	 * Attempt to unlock an already-unlocked container; log and return.
614*0Sstevel@tonic-gate 	 */
615*0Sstevel@tonic-gate 	if (cn == NULL) {
616*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "process tried to re-unlock a lock");
617*0Sstevel@tonic-gate 		(void) mutex_unlock(&ud->ud_lock);
618*0Sstevel@tonic-gate 		reply.rp_retval = DSVC_SYNCH_ERR;
619*0Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
620*0Sstevel@tonic-gate 		return;
621*0Sstevel@tonic-gate 	}
622*0Sstevel@tonic-gate 	ud->ud_cn = NULL;
623*0Sstevel@tonic-gate 
624*0Sstevel@tonic-gate 	/*
625*0Sstevel@tonic-gate 	 * Unlock the container; note that after cn_unlock() has been done
626*0Sstevel@tonic-gate 	 * cn->cn_id is no longer accessible.
627*0Sstevel@tonic-gate 	 */
628*0Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "tid %d: unlocking %s", thr_self(), cn->cn_id);
629*0Sstevel@tonic-gate 	reply.rp_retval = cn_unlock(cn);
630*0Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "tid %d: unlock operation: %s", thr_self(),
631*0Sstevel@tonic-gate 	    dhcpsvc_errmsg(reply.rp_retval));
632*0Sstevel@tonic-gate 
633*0Sstevel@tonic-gate 	/*
634*0Sstevel@tonic-gate 	 * Even though we've unlocked the lock, we cannot yet destroy the
635*0Sstevel@tonic-gate 	 * unlock descriptor (even if we revoke the door) because it's
636*0Sstevel@tonic-gate 	 * possible the unref thread is already waiting on ud_lock.
637*0Sstevel@tonic-gate 	 */
638*0Sstevel@tonic-gate 	(void) mutex_unlock(&ud->ud_lock);
639*0Sstevel@tonic-gate 	(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
640*0Sstevel@tonic-gate }
641*0Sstevel@tonic-gate 
642*0Sstevel@tonic-gate /*
643*0Sstevel@tonic-gate  * Reap containers that have not been recently used.
644*0Sstevel@tonic-gate  */
645*0Sstevel@tonic-gate static void *
646*0Sstevel@tonic-gate reaper(void *ds_table_raw)
647*0Sstevel@tonic-gate {
648*0Sstevel@tonic-gate 	dsvcd_datastore_t	**ds_table;
649*0Sstevel@tonic-gate 	unsigned int		i, nreaped;
650*0Sstevel@tonic-gate 
651*0Sstevel@tonic-gate 	ds_table = (dsvcd_datastore_t **)ds_table_raw;
652*0Sstevel@tonic-gate 	for (;;) {
653*0Sstevel@tonic-gate 		(void) sleep(DSVCD_REAP_INTERVAL);
654*0Sstevel@tonic-gate 		for (i = 0; ds_table[i] != NULL; i++) {
655*0Sstevel@tonic-gate 			nreaped = ds_reap_containers(ds_table[i],
656*0Sstevel@tonic-gate 			    DSVCD_REAP_THRESH);
657*0Sstevel@tonic-gate 			if (nreaped > 0) {
658*0Sstevel@tonic-gate 				dhcpmsg(MSG_VERBOSE, "reaped %u container "
659*0Sstevel@tonic-gate 				    "synchpoints from %s", nreaped,
660*0Sstevel@tonic-gate 				    ds_table[i]->ds_name);
661*0Sstevel@tonic-gate 			}
662*0Sstevel@tonic-gate 		}
663*0Sstevel@tonic-gate 	}
664*0Sstevel@tonic-gate 	/* NOTREACHED */
665*0Sstevel@tonic-gate 	return (NULL);
666*0Sstevel@tonic-gate }
667*0Sstevel@tonic-gate 
668*0Sstevel@tonic-gate /*
669*0Sstevel@tonic-gate  * Daemonize the process.
670*0Sstevel@tonic-gate  */
671*0Sstevel@tonic-gate static int
672*0Sstevel@tonic-gate daemonize(void)
673*0Sstevel@tonic-gate {
674*0Sstevel@tonic-gate 	switch (fork()) {
675*0Sstevel@tonic-gate 
676*0Sstevel@tonic-gate 	case -1:
677*0Sstevel@tonic-gate 		return (0);
678*0Sstevel@tonic-gate 
679*0Sstevel@tonic-gate 	case  0:
680*0Sstevel@tonic-gate 		/*
681*0Sstevel@tonic-gate 		 * Lose our controlling terminal, and become both a session
682*0Sstevel@tonic-gate 		 * leader and a process group leader.
683*0Sstevel@tonic-gate 		 */
684*0Sstevel@tonic-gate 		if (setsid() == -1)
685*0Sstevel@tonic-gate 			return (0);
686*0Sstevel@tonic-gate 
687*0Sstevel@tonic-gate 		/*
688*0Sstevel@tonic-gate 		 * Under POSIX, a session leader can accidentally (through
689*0Sstevel@tonic-gate 		 * open(2)) acquire a controlling terminal if it does not
690*0Sstevel@tonic-gate 		 * have one.  Just to be safe, fork() again so we are not a
691*0Sstevel@tonic-gate 		 * session leader.
692*0Sstevel@tonic-gate 		 */
693*0Sstevel@tonic-gate 		switch (fork()) {
694*0Sstevel@tonic-gate 
695*0Sstevel@tonic-gate 		case -1:
696*0Sstevel@tonic-gate 			return (0);
697*0Sstevel@tonic-gate 
698*0Sstevel@tonic-gate 		case 0:
699*0Sstevel@tonic-gate 			(void) signal(SIGHUP, SIG_IGN);
700*0Sstevel@tonic-gate 			(void) chdir("/");
701*0Sstevel@tonic-gate 			(void) umask(022);
702*0Sstevel@tonic-gate 			closefrom(0);
703*0Sstevel@tonic-gate 			break;
704*0Sstevel@tonic-gate 
705*0Sstevel@tonic-gate 		default:
706*0Sstevel@tonic-gate 			_exit(EXIT_SUCCESS);
707*0Sstevel@tonic-gate 		}
708*0Sstevel@tonic-gate 		break;
709*0Sstevel@tonic-gate 
710*0Sstevel@tonic-gate 	default:
711*0Sstevel@tonic-gate 		_exit(EXIT_SUCCESS);
712*0Sstevel@tonic-gate 	}
713*0Sstevel@tonic-gate 
714*0Sstevel@tonic-gate 	return (1);
715*0Sstevel@tonic-gate }
716*0Sstevel@tonic-gate 
717*0Sstevel@tonic-gate /*
718*0Sstevel@tonic-gate  * Create an unlock descriptor for container `cn' -- returns an unlock
719*0Sstevel@tonic-gate  * descriptor on success, or NULL on failure; the reason for failure is in
720*0Sstevel@tonic-gate  * `retvalp'.  Since creating door descriptors is expensive, we keep a few
721*0Sstevel@tonic-gate  * cache a small list of old descriptors around on a reclaim list and only
722*0Sstevel@tonic-gate  * allocate a new one if the list is empty.
723*0Sstevel@tonic-gate  */
724*0Sstevel@tonic-gate static dsvcd_unlock_desc_t *
725*0Sstevel@tonic-gate ud_create(dsvcd_container_t *cn, int *retvalp)
726*0Sstevel@tonic-gate {
727*0Sstevel@tonic-gate 	dsvcd_unlock_desc_t *ud;
728*0Sstevel@tonic-gate 
729*0Sstevel@tonic-gate 	*retvalp = DSVC_SUCCESS;
730*0Sstevel@tonic-gate 	(void) mutex_lock(&ud_reclaim_lock);
731*0Sstevel@tonic-gate 	if (ud_reclaim_list != NULL) {
732*0Sstevel@tonic-gate 		ud = ud_reclaim_list;
733*0Sstevel@tonic-gate 		ud_reclaim_list = ud->ud_next;
734*0Sstevel@tonic-gate 		ud_reclaim_count--;
735*0Sstevel@tonic-gate 		(void) mutex_unlock(&ud_reclaim_lock);
736*0Sstevel@tonic-gate 	} else {
737*0Sstevel@tonic-gate 		(void) mutex_unlock(&ud_reclaim_lock);
738*0Sstevel@tonic-gate 		ud = malloc(sizeof (dsvcd_unlock_desc_t));
739*0Sstevel@tonic-gate 		if (ud == NULL) {
740*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "cannot allocate unlock door "
741*0Sstevel@tonic-gate 			    "descriptor; denying %s lock request", cn->cn_id);
742*0Sstevel@tonic-gate 			*retvalp = DSVC_NO_MEMORY;
743*0Sstevel@tonic-gate 			return (NULL);
744*0Sstevel@tonic-gate 		}
745*0Sstevel@tonic-gate 
746*0Sstevel@tonic-gate 		(void) mutex_init(&ud->ud_lock, USYNC_THREAD, NULL);
747*0Sstevel@tonic-gate 		ud->ud_fd = door_create((void (*)())svc_unlock, ud,
748*0Sstevel@tonic-gate 		    DOOR_UNREF_MULTI | DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
749*0Sstevel@tonic-gate 		if (ud->ud_fd == -1) {
750*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "cannot create unlock door; "
751*0Sstevel@tonic-gate 			    "denying %s lock request", cn->cn_id);
752*0Sstevel@tonic-gate 			free(ud);
753*0Sstevel@tonic-gate 			*retvalp = DSVC_NO_RESOURCES;
754*0Sstevel@tonic-gate 			return (NULL);
755*0Sstevel@tonic-gate 		}
756*0Sstevel@tonic-gate 	}
757*0Sstevel@tonic-gate 
758*0Sstevel@tonic-gate 	ud->ud_next = NULL;
759*0Sstevel@tonic-gate 	ud->ud_cn = cn;
760*0Sstevel@tonic-gate 	return (ud);
761*0Sstevel@tonic-gate }
762*0Sstevel@tonic-gate 
763*0Sstevel@tonic-gate /*
764*0Sstevel@tonic-gate  * Destroy the unlock descriptor `ud' -- `ud' must be unlocked on entry.
765*0Sstevel@tonic-gate  * If there's room and `cacheable' is set, then, keep the unlock descriptor
766*0Sstevel@tonic-gate  * on the reclaim list to lower future creation cost.
767*0Sstevel@tonic-gate  */
768*0Sstevel@tonic-gate static void
769*0Sstevel@tonic-gate ud_destroy(dsvcd_unlock_desc_t *ud, boolean_t cacheable)
770*0Sstevel@tonic-gate {
771*0Sstevel@tonic-gate 	assert(!MUTEX_HELD(&ud->ud_lock));
772*0Sstevel@tonic-gate 
773*0Sstevel@tonic-gate 	ud->ud_cn = NULL;
774*0Sstevel@tonic-gate 	(void) mutex_lock(&ud_reclaim_lock);
775*0Sstevel@tonic-gate 	if (cacheable && ud_reclaim_count < UD_RECLAIM_MAX) {
776*0Sstevel@tonic-gate 		ud->ud_next = ud_reclaim_list;
777*0Sstevel@tonic-gate 		ud_reclaim_list = ud;
778*0Sstevel@tonic-gate 		ud_reclaim_count++;
779*0Sstevel@tonic-gate 		(void) mutex_unlock(&ud_reclaim_lock);
780*0Sstevel@tonic-gate 	} else {
781*0Sstevel@tonic-gate 		(void) mutex_unlock(&ud_reclaim_lock);
782*0Sstevel@tonic-gate 		(void) door_revoke(ud->ud_fd);
783*0Sstevel@tonic-gate 		(void) mutex_destroy(&ud->ud_lock);
784*0Sstevel@tonic-gate 		free(ud);
785*0Sstevel@tonic-gate 	}
786*0Sstevel@tonic-gate }
787*0Sstevel@tonic-gate 
788*0Sstevel@tonic-gate /*
789*0Sstevel@tonic-gate  * Create a stack of `*stacksizep' bytes (rounded up to the nearest page)
790*0Sstevel@tonic-gate  * including a redzone for catching stack overflow.  Set `stacksizep' to
791*0Sstevel@tonic-gate  * point to the actual usable size of the stack (i.e., everything but the
792*0Sstevel@tonic-gate  * redzone).  Returns a pointer to the base of the stack (not including the
793*0Sstevel@tonic-gate  * redzone).
794*0Sstevel@tonic-gate  */
795*0Sstevel@tonic-gate static void *
796*0Sstevel@tonic-gate stack_create(unsigned int *stacksizep)
797*0Sstevel@tonic-gate {
798*0Sstevel@tonic-gate 	caddr_t		stackbase;
799*0Sstevel@tonic-gate 	unsigned int	redzone = roundup(DSVCD_STACK_REDSIZE, PAGESIZE);
800*0Sstevel@tonic-gate 	unsigned int	stacksize = *stacksizep;
801*0Sstevel@tonic-gate 
802*0Sstevel@tonic-gate 	if (stacksize < sysconf(_SC_THREAD_STACK_MIN))
803*0Sstevel@tonic-gate 		stacksize = sysconf(_SC_THREAD_STACK_MIN);
804*0Sstevel@tonic-gate 
805*0Sstevel@tonic-gate 	stacksize = roundup(stacksize, PAGESIZE);
806*0Sstevel@tonic-gate 	stackbase = mmap(NULL, stacksize + redzone, PROT_READ | PROT_WRITE,
807*0Sstevel@tonic-gate 	    MAP_ANON | MAP_PRIVATE, -1, 0);
808*0Sstevel@tonic-gate 	if (stackbase == MAP_FAILED)
809*0Sstevel@tonic-gate 		return (NULL);
810*0Sstevel@tonic-gate 
811*0Sstevel@tonic-gate 	*stacksizep = stacksize;
812*0Sstevel@tonic-gate 	(void) mprotect(stackbase, redzone, PROT_NONE);
813*0Sstevel@tonic-gate 	return (stackbase + redzone);
814*0Sstevel@tonic-gate }
815*0Sstevel@tonic-gate 
816*0Sstevel@tonic-gate /*
817*0Sstevel@tonic-gate  * Destroy the stack of `stacksize' bytes pointed to by `stackbase'.
818*0Sstevel@tonic-gate  */
819*0Sstevel@tonic-gate static void
820*0Sstevel@tonic-gate stack_destroy(void *stackbase, unsigned int stacksize)
821*0Sstevel@tonic-gate {
822*0Sstevel@tonic-gate 	unsigned int	redzone = roundup(DSVCD_STACK_REDSIZE, PAGESIZE);
823*0Sstevel@tonic-gate 
824*0Sstevel@tonic-gate 	(void) munmap((caddr_t)stackbase - redzone, stacksize + redzone);
825*0Sstevel@tonic-gate }
826*0Sstevel@tonic-gate 
827*0Sstevel@tonic-gate /*
828*0Sstevel@tonic-gate  * Start function for door server threads; turns off thread cancellation
829*0Sstevel@tonic-gate  * and then parks in the kernel via door_return().
830*0Sstevel@tonic-gate  */
831*0Sstevel@tonic-gate /* ARGSUSED */
832*0Sstevel@tonic-gate static void *
833*0Sstevel@tonic-gate doorserv_thread(void *arg)
834*0Sstevel@tonic-gate {
835*0Sstevel@tonic-gate 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
836*0Sstevel@tonic-gate 	(void) door_return(NULL, 0, NULL, 0);
837*0Sstevel@tonic-gate 	return (NULL);
838*0Sstevel@tonic-gate }
839*0Sstevel@tonic-gate 
840*0Sstevel@tonic-gate /*
841*0Sstevel@tonic-gate  * Creation function for door server threads.  We require door threads to
842*0Sstevel@tonic-gate  * have 32K of backed stack.  This is a guess but will be more than
843*0Sstevel@tonic-gate  * sufficient for our uses, since door threads have a shallow call depth
844*0Sstevel@tonic-gate  * and the functions use little automatic storage.
845*0Sstevel@tonic-gate  */
846*0Sstevel@tonic-gate /* ARGSUSED */
847*0Sstevel@tonic-gate static void
848*0Sstevel@tonic-gate doorserv_create(door_info_t *infop)
849*0Sstevel@tonic-gate {
850*0Sstevel@tonic-gate 	void		*stackbase;
851*0Sstevel@tonic-gate 	unsigned int	stacksize = 32 * 1024;
852*0Sstevel@tonic-gate 
853*0Sstevel@tonic-gate 	stackbase = stack_create(&stacksize);
854*0Sstevel@tonic-gate 	if (stackbase != NULL) {
855*0Sstevel@tonic-gate 		errno = thr_create(stackbase, stacksize, doorserv_thread, NULL,
856*0Sstevel@tonic-gate 		    THR_BOUND | THR_DETACHED, NULL);
857*0Sstevel@tonic-gate 		if (errno != 0) {
858*0Sstevel@tonic-gate 			dhcpmsg(MSG_ERR, "cannot create door server thread; "
859*0Sstevel@tonic-gate 			    "server thread pool will not be grown");
860*0Sstevel@tonic-gate 			stack_destroy(stackbase, stacksize);
861*0Sstevel@tonic-gate 		}
862*0Sstevel@tonic-gate 	}
863*0Sstevel@tonic-gate }
864