xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/dsvclockd.c (revision 5006:304eb1332eef)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51914Scasper  * Common Development and Distribution License (the "License").
61914Scasper  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*5006Sek110237  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * #define _POSIX_PTHREAD_SEMANTICS before #including <signal.h> so that we
300Sstevel@tonic-gate  * get the right (POSIX) version of sigwait(2).
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate #define	_POSIX_PTHREAD_SEMANTICS
330Sstevel@tonic-gate 
340Sstevel@tonic-gate #include <sys/types.h>
350Sstevel@tonic-gate #include <sys/stat.h>
360Sstevel@tonic-gate #include <sys/mman.h>
370Sstevel@tonic-gate #include <sys/sysmacros.h>
380Sstevel@tonic-gate #include <dhcp_svc_private.h>
390Sstevel@tonic-gate #include <pthread.h>
400Sstevel@tonic-gate #include <stdlib.h>
410Sstevel@tonic-gate #include <dhcpmsg.h>
420Sstevel@tonic-gate #include <assert.h>
430Sstevel@tonic-gate #include <stddef.h>
440Sstevel@tonic-gate #include <stdio.h>
451914Scasper #include <stdio_ext.h>
460Sstevel@tonic-gate #include <string.h>
470Sstevel@tonic-gate #include <unistd.h>
480Sstevel@tonic-gate #include <signal.h>
490Sstevel@tonic-gate #include <locale.h>
500Sstevel@tonic-gate #include <synch.h>
51*5006Sek110237 #include <sys/resource.h>
520Sstevel@tonic-gate 
530Sstevel@tonic-gate #include "datastore.h"
540Sstevel@tonic-gate #include "dsvclockd.h"
550Sstevel@tonic-gate 
560Sstevel@tonic-gate /*
570Sstevel@tonic-gate  * The DHCP service daemon synchronizes access to containers within a given
580Sstevel@tonic-gate  * datastore.  Any datastore which is willing to accept the synchronization
590Sstevel@tonic-gate  * constraints imposed by the DHCP service daemon can use this daemon in
600Sstevel@tonic-gate  * lieu of rolling their own synchronization code.
610Sstevel@tonic-gate  *
620Sstevel@tonic-gate  * See $SRC/lib/libdhcpsvc/private/README.synch for more information.
630Sstevel@tonic-gate  */
640Sstevel@tonic-gate 
650Sstevel@tonic-gate #ifndef	TEXT_DOMAIN
660Sstevel@tonic-gate #define	TEXT_DOMAIN		"SYS_TEST"
670Sstevel@tonic-gate #endif
680Sstevel@tonic-gate 
690Sstevel@tonic-gate #define	DSVCD_REAP_INTERVAL	(60 * 60 * 24)	/* seconds, thus once a day */
700Sstevel@tonic-gate #define	DSVCD_REAP_THRESH	(60 * 60)	/* seconds, thus 1 hour stale */
710Sstevel@tonic-gate #define	DSVCD_STACK_REDSIZE	(8 * 1024)	/* redzone size, in bytes */
720Sstevel@tonic-gate #define	UD_RECLAIM_MAX		128		/* unlock door descriptors */
730Sstevel@tonic-gate 
740Sstevel@tonic-gate /*
750Sstevel@tonic-gate  * Unlock descriptor -- one for each lock granted.  This descriptor is used
760Sstevel@tonic-gate  * to subsequently unlock the granted lock (and to synchronize unlocking of
770Sstevel@tonic-gate  * the lock; see svc_unlock() below for details).
780Sstevel@tonic-gate  */
790Sstevel@tonic-gate typedef struct dsvcd_unlock_desc {
800Sstevel@tonic-gate 	int			ud_fd;
810Sstevel@tonic-gate 	mutex_t			ud_lock;
820Sstevel@tonic-gate 	dsvcd_container_t	*ud_cn;
830Sstevel@tonic-gate 	struct dsvcd_unlock_desc *ud_next;
840Sstevel@tonic-gate } dsvcd_unlock_desc_t;
850Sstevel@tonic-gate 
860Sstevel@tonic-gate static mutex_t			ud_reclaim_lock = DEFAULTMUTEX;
870Sstevel@tonic-gate static unsigned int		ud_reclaim_count = 0;
880Sstevel@tonic-gate static dsvcd_unlock_desc_t	*ud_reclaim_list = NULL;
890Sstevel@tonic-gate 
900Sstevel@tonic-gate static void			*reaper(void *);
910Sstevel@tonic-gate static int			daemonize(void);
920Sstevel@tonic-gate static void			*stack_create(unsigned int *);
930Sstevel@tonic-gate static void			stack_destroy(void *, unsigned int);
940Sstevel@tonic-gate static void			doorserv_create(door_info_t *);
950Sstevel@tonic-gate static dsvcd_unlock_desc_t	*ud_create(dsvcd_container_t *, int *);
960Sstevel@tonic-gate static void			ud_destroy(dsvcd_unlock_desc_t *, boolean_t);
970Sstevel@tonic-gate static dsvcd_svc_t		svc_lock, svc_unlock;
980Sstevel@tonic-gate 
990Sstevel@tonic-gate int
main(int argc,char ** argv)1000Sstevel@tonic-gate main(int argc, char **argv)
1010Sstevel@tonic-gate {
1020Sstevel@tonic-gate 	dsvcd_datastore_t	**ds_table;
1030Sstevel@tonic-gate 	dsvc_datastore_t	dd;
1040Sstevel@tonic-gate 	dsvc_synchtype_t	synchtype;
1050Sstevel@tonic-gate 	char			**modules;
1060Sstevel@tonic-gate 	unsigned int		i, j;
1070Sstevel@tonic-gate 	int			debug_level = 0;
1080Sstevel@tonic-gate 	boolean_t		is_daemon = B_TRUE;
1090Sstevel@tonic-gate 	boolean_t		is_verbose = B_FALSE;
1100Sstevel@tonic-gate 	int			sig, nmodules, nsynchmods, c;
1110Sstevel@tonic-gate 	sigset_t		sigset;
1120Sstevel@tonic-gate 	char			signame[SIG2STR_MAX];
1130Sstevel@tonic-gate 	char			*progname;
1140Sstevel@tonic-gate 	void			*stackbase;
1150Sstevel@tonic-gate 	unsigned int		stacksize = 16 * 1024;
1160Sstevel@tonic-gate 	struct rlimit		rl;
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
1190Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate 	/*
1220Sstevel@tonic-gate 	 * Mask all signals except SIGABRT; doing this here ensures that
1230Sstevel@tonic-gate 	 * all threads created through door_create() have them masked too.
1240Sstevel@tonic-gate 	 */
1250Sstevel@tonic-gate 	(void) sigfillset(&sigset);
1260Sstevel@tonic-gate 	(void) sigdelset(&sigset, SIGABRT);
1270Sstevel@tonic-gate 	(void) thr_sigsetmask(SIG_BLOCK, &sigset, NULL);
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate 	/*
1300Sstevel@tonic-gate 	 * Figure out our program name; just keep the final piece so that
1310Sstevel@tonic-gate 	 * our dhcpmsg() messages don't get too long.
1320Sstevel@tonic-gate 	 */
1330Sstevel@tonic-gate 	progname = strrchr(argv[0], '/');
1340Sstevel@tonic-gate 	if (progname != NULL)
1350Sstevel@tonic-gate 		progname++;
1360Sstevel@tonic-gate 	else
1370Sstevel@tonic-gate 		progname = argv[0];
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate 	/*
1400Sstevel@tonic-gate 	 * Set the door thread creation procedure so that all of our
1410Sstevel@tonic-gate 	 * threads are created with thread stacks with backing store.
1420Sstevel@tonic-gate 	 */
1430Sstevel@tonic-gate 	(void) door_server_create(doorserv_create);
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate 	while ((c = getopt(argc, argv, "d:fv")) != EOF) {
1460Sstevel@tonic-gate 		switch (c) {
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate 		case 'd':
1490Sstevel@tonic-gate 			debug_level = atoi(optarg);
1500Sstevel@tonic-gate 			break;
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate 		case 'f':
1530Sstevel@tonic-gate 			is_daemon = B_FALSE;
1540Sstevel@tonic-gate 			break;
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 		case 'v':
1570Sstevel@tonic-gate 			is_verbose = B_TRUE;
1580Sstevel@tonic-gate 			break;
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate 		case '?':
1610Sstevel@tonic-gate 			(void) fprintf(stderr,
1620Sstevel@tonic-gate 			    gettext("usage: %s [-dn] [-f] [-v]\n"), progname);
1630Sstevel@tonic-gate 			return (EXIT_FAILURE);
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 		default:
1660Sstevel@tonic-gate 			break;
1670Sstevel@tonic-gate 		}
1680Sstevel@tonic-gate 	}
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	if (geteuid() != 0) {
1710Sstevel@tonic-gate 		dhcpmsg_init(progname, B_FALSE, is_verbose, debug_level);
1720Sstevel@tonic-gate 		dhcpmsg(MSG_ERROR, "must be super-user");
1730Sstevel@tonic-gate 		dhcpmsg_fini();
1740Sstevel@tonic-gate 		return (EXIT_FAILURE);
1750Sstevel@tonic-gate 	}
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 	if (is_daemon && daemonize() == 0) {
1780Sstevel@tonic-gate 		dhcpmsg_init(progname, B_FALSE, is_verbose, debug_level);
1790Sstevel@tonic-gate 		dhcpmsg(MSG_ERROR, "cannot become daemon, exiting");
1800Sstevel@tonic-gate 		dhcpmsg_fini();
1810Sstevel@tonic-gate 		return (EXIT_FAILURE);
1820Sstevel@tonic-gate 	}
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 	dhcpmsg_init(progname, is_daemon, is_verbose, debug_level);
1850Sstevel@tonic-gate 	(void) atexit(dhcpmsg_fini);
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate 	/*
1880Sstevel@tonic-gate 	 * Max out the number available descriptors since we need to
1890Sstevel@tonic-gate 	 * allocate two per held lock.
1900Sstevel@tonic-gate 	 */
1910Sstevel@tonic-gate 	rl.rlim_cur = RLIM_INFINITY;
1920Sstevel@tonic-gate 	rl.rlim_max = RLIM_INFINITY;
1930Sstevel@tonic-gate 	if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
1940Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "setrlimit failed");
1950Sstevel@tonic-gate 
1961914Scasper 	(void) enable_extended_FILE_stdio(-1, -1);
1971914Scasper 
1980Sstevel@tonic-gate 	if (enumerate_dd(&modules, &nmodules) != DSVC_SUCCESS) {
1990Sstevel@tonic-gate 		dhcpmsg(MSG_ERROR, "cannot enumerate public modules, exiting");
2000Sstevel@tonic-gate 		return (EXIT_FAILURE);
2010Sstevel@tonic-gate 	}
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	/*
2040Sstevel@tonic-gate 	 * NOTE: this code assumes that a module that needs dsvclockd will
2050Sstevel@tonic-gate 	 * always need it (even as the container version is ramped).  If
2060Sstevel@tonic-gate 	 * this becomes bogus in a future release, we'll have to make this
2070Sstevel@tonic-gate 	 * logic more sophisticated.
2080Sstevel@tonic-gate 	 */
2090Sstevel@tonic-gate 	nsynchmods = nmodules;
2100Sstevel@tonic-gate 	for (i = 0; i < nmodules; i++) {
2110Sstevel@tonic-gate 		dd.d_resource = modules[i];
2120Sstevel@tonic-gate 		dd.d_conver = DSVC_CUR_CONVER;
2130Sstevel@tonic-gate 		dd.d_location = "";
2140Sstevel@tonic-gate 		if (module_synchtype(&dd, &synchtype) != DSVC_SUCCESS) {
2150Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "cannot determine synchronization "
2160Sstevel@tonic-gate 			    "type for `%s', skipping", modules[i]);
2170Sstevel@tonic-gate 			free(modules[i]);
2180Sstevel@tonic-gate 			modules[i] = NULL;
2190Sstevel@tonic-gate 			nsynchmods--;
2200Sstevel@tonic-gate 			continue;
2210Sstevel@tonic-gate 		}
2220Sstevel@tonic-gate 		if ((synchtype & DSVC_SYNCH_STRATMASK) != DSVC_SYNCH_DSVCD) {
2230Sstevel@tonic-gate 			free(modules[i]);
2240Sstevel@tonic-gate 			modules[i] = NULL;
2250Sstevel@tonic-gate 			nsynchmods--;
2260Sstevel@tonic-gate 		}
2270Sstevel@tonic-gate 	}
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 	if (nsynchmods == 0) {
2300Sstevel@tonic-gate 		dhcpmsg(MSG_INFO, "no public modules need synchronization");
2310Sstevel@tonic-gate 		return (EXIT_SUCCESS);
2320Sstevel@tonic-gate 	}
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 	/*
2350Sstevel@tonic-gate 	 * Allocate the datastore table; include one extra entry so that
2360Sstevel@tonic-gate 	 * the table is NULL-terminated.
2370Sstevel@tonic-gate 	 */
2380Sstevel@tonic-gate 	ds_table = calloc(nsynchmods + 1, sizeof (dsvcd_datastore_t *));
2390Sstevel@tonic-gate 	if (ds_table == NULL) {
2400Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "cannot allocate datastore table, exiting");
2410Sstevel@tonic-gate 		return (EXIT_FAILURE);
2420Sstevel@tonic-gate 	}
2430Sstevel@tonic-gate 	ds_table[nsynchmods] = NULL;
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	/*
2460Sstevel@tonic-gate 	 * Create the datastores (which implicitly creates the doors).
2470Sstevel@tonic-gate 	 * then sit around and wait for requests to come in on the doors.
2480Sstevel@tonic-gate 	 */
2490Sstevel@tonic-gate 	for (i = 0, j = 0; i < nmodules; i++) {
2500Sstevel@tonic-gate 		if (modules[i] != NULL) {
2510Sstevel@tonic-gate 			ds_table[j] = ds_create(modules[i], svc_lock);
2520Sstevel@tonic-gate 			if (ds_table[j] == NULL) {
2530Sstevel@tonic-gate 				while (j-- > 0)
2540Sstevel@tonic-gate 					ds_destroy(ds_table[j]);
2550Sstevel@tonic-gate 				return (EXIT_FAILURE);
2560Sstevel@tonic-gate 			}
2570Sstevel@tonic-gate 			free(modules[i]);
2580Sstevel@tonic-gate 			j++;
2590Sstevel@tonic-gate 		}
2600Sstevel@tonic-gate 	}
2610Sstevel@tonic-gate 	free(modules);
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 	stackbase = stack_create(&stacksize);
2640Sstevel@tonic-gate 	if (stackbase == NULL)
2650Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "cannot create reaper stack; containers "
2660Sstevel@tonic-gate 		    "will not be reaped");
2670Sstevel@tonic-gate 	else {
2680Sstevel@tonic-gate 		errno = thr_create(stackbase, stacksize, reaper, ds_table,
2690Sstevel@tonic-gate 		    THR_DAEMON, NULL);
2700Sstevel@tonic-gate 		if (errno != 0) {
2710Sstevel@tonic-gate 			dhcpmsg(MSG_ERR, "cannot create reaper thread; "
2720Sstevel@tonic-gate 			    "containers will not be reaped");
2730Sstevel@tonic-gate 			stack_destroy(stackbase, stacksize);
2740Sstevel@tonic-gate 		}
2750Sstevel@tonic-gate 	}
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 	/*
2780Sstevel@tonic-gate 	 * Synchronously wait for a QUIT, TERM, or INT, then shutdown.
2790Sstevel@tonic-gate 	 */
2800Sstevel@tonic-gate 	(void) sigemptyset(&sigset);
2810Sstevel@tonic-gate 	(void) sigaddset(&sigset, SIGQUIT);
2820Sstevel@tonic-gate 	(void) sigaddset(&sigset, SIGTERM);
2830Sstevel@tonic-gate 	(void) sigaddset(&sigset, SIGINT);
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	(void) sigwait(&sigset, &sig);
2860Sstevel@tonic-gate 	if (sig != SIGTERM && sig != SIGQUIT && sig != SIGINT)
2870Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "received unexpected signal");
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	if (sig2str(sig, signame) == -1)
2900Sstevel@tonic-gate 		(void) strlcpy(signame, "???", sizeof (signame));
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	dhcpmsg(MSG_INFO, "shutting down via SIG%s", signame);
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	for (i = 0; i < nsynchmods; i++)
2950Sstevel@tonic-gate 		ds_destroy(ds_table[i]);
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	return (EXIT_SUCCESS);
2980Sstevel@tonic-gate }
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate /*
3010Sstevel@tonic-gate  * Sanity check that dsvcd_request_t `req' (which is `reqsize' bytes long)
3020Sstevel@tonic-gate  * is a correctly formed request; if not, return an error which will be
3030Sstevel@tonic-gate  * returned to the door caller.
3040Sstevel@tonic-gate  */
3050Sstevel@tonic-gate static int
check_door_req(dsvcd_request_t * req,size_t reqsize,size_t minsize)3060Sstevel@tonic-gate check_door_req(dsvcd_request_t *req, size_t reqsize, size_t minsize)
3070Sstevel@tonic-gate {
3080Sstevel@tonic-gate 	door_cred_t cred;
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 	if (req == NULL) {
3110Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "empty request, ignoring");
3120Sstevel@tonic-gate 		return (DSVC_SYNCH_ERR);
3130Sstevel@tonic-gate 	}
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 	/*
3160Sstevel@tonic-gate 	 * Check credentials; we don't allow any non-super-user requests
3170Sstevel@tonic-gate 	 * since this would open a denial-of-service hole (since a lock
3180Sstevel@tonic-gate 	 * could be checked out indefinitely).
3190Sstevel@tonic-gate 	 */
3200Sstevel@tonic-gate 	if (door_cred(&cred) != 0) {
3210Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "request with unknown credentials");
3220Sstevel@tonic-gate 		return (DSVC_ACCESS);
3230Sstevel@tonic-gate 	}
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	if (cred.dc_euid != 0) {
3260Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "request with non-super-user credentials");
3270Sstevel@tonic-gate 		return (DSVC_ACCESS);
3280Sstevel@tonic-gate 	}
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 	/*
3310Sstevel@tonic-gate 	 * Check the version and size; we check this before checking the
3320Sstevel@tonic-gate 	 * size of the request structure since an "incompatible version"
3330Sstevel@tonic-gate 	 * message is more helpful than a "short request" message.
3340Sstevel@tonic-gate 	 */
3350Sstevel@tonic-gate 	if (reqsize > offsetof(dsvcd_request_t, rq_version) &&
3360Sstevel@tonic-gate 	    req->rq_version != DSVCD_DOOR_VERSION) {
3370Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "request with unsupported version `%d'",
3380Sstevel@tonic-gate 		    req->rq_version);
3390Sstevel@tonic-gate 		return (DSVC_SYNCH_ERR);
3400Sstevel@tonic-gate 	}
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	if (reqsize < minsize) {
3430Sstevel@tonic-gate 		dhcpmsg(MSG_VERBOSE, "short request (%d bytes, minimum %d "
3440Sstevel@tonic-gate 		    "bytes)", reqsize, minsize);
3450Sstevel@tonic-gate 		return (DSVC_SYNCH_ERR);
3460Sstevel@tonic-gate 	}
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	return (DSVC_SUCCESS);
3490Sstevel@tonic-gate }
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate /*
3530Sstevel@tonic-gate  * Service a lock request `req' passed across the door for datastore `ds'.
3540Sstevel@tonic-gate  * After verifying that the request is well-formed, locks the container and
3550Sstevel@tonic-gate  * creates an "unlock" door descriptor that the client uses to unlock the
3560Sstevel@tonic-gate  * door (either explicitly through door_call()) or implicitly through
3570Sstevel@tonic-gate  * terminating abnormally).
3580Sstevel@tonic-gate  */
3590Sstevel@tonic-gate /* ARGSUSED */
3600Sstevel@tonic-gate static void
svc_lock(void * cookie,dsvcd_request_t * req,size_t reqsize,door_desc_t * doorp,uint_t ndoors)3610Sstevel@tonic-gate svc_lock(void *cookie, dsvcd_request_t *req, size_t reqsize,
3620Sstevel@tonic-gate     door_desc_t *doorp, uint_t ndoors)
3630Sstevel@tonic-gate {
3640Sstevel@tonic-gate 	dsvcd_reply_t		reply;
3650Sstevel@tonic-gate 	door_desc_t		door_desc;
3660Sstevel@tonic-gate 	dsvcd_lock_request_t	*lreq = (dsvcd_lock_request_t *)req;
3670Sstevel@tonic-gate 	dsvcd_datastore_t	*ds = (dsvcd_datastore_t *)cookie;
3680Sstevel@tonic-gate 	dsvcd_container_t	*cn;
3690Sstevel@tonic-gate 	dsvcd_unlock_desc_t	*ud;
3700Sstevel@tonic-gate 	char			conid[MAXPATHLEN];
3710Sstevel@tonic-gate 	unsigned int		attempts = 0;
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	reply.rp_version = DSVCD_DOOR_VERSION;
3740Sstevel@tonic-gate 	reply.rp_retval  = check_door_req(req, reqsize,
3750Sstevel@tonic-gate 	    sizeof (dsvcd_lock_request_t));
3760Sstevel@tonic-gate 	if (reply.rp_retval != DSVC_SUCCESS) {
3770Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
3780Sstevel@tonic-gate 		return;
3790Sstevel@tonic-gate 	}
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate 	/*
3820Sstevel@tonic-gate 	 * Verify that this is a lock request; if in the future we support
3830Sstevel@tonic-gate 	 * other requests, we'll have to abstract this a bit.
3840Sstevel@tonic-gate 	 */
3850Sstevel@tonic-gate 	if (req->rq_reqtype != DSVCD_LOCK) {
3860Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "unsupported request `%d' on lock "
3870Sstevel@tonic-gate 		    "request door", req->rq_reqtype);
3880Sstevel@tonic-gate 		reply.rp_retval = DSVC_SYNCH_ERR;
3890Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
3900Sstevel@tonic-gate 		return;
3910Sstevel@tonic-gate 	}
3920Sstevel@tonic-gate 	if (lreq->lrq_locktype != DSVCD_RDLOCK &&
3930Sstevel@tonic-gate 	    lreq->lrq_locktype != DSVCD_WRLOCK) {
3940Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "request for unsupported locktype `%d'",
3950Sstevel@tonic-gate 		    lreq->lrq_locktype);
3960Sstevel@tonic-gate 		reply.rp_retval = DSVC_SYNCH_ERR;
3970Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
3980Sstevel@tonic-gate 		return;
3990Sstevel@tonic-gate 	}
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 	/*
4020Sstevel@tonic-gate 	 * Find the container; create if it doesn't already exist.  We do
4030Sstevel@tonic-gate 	 * this as a single operation to avoid race conditions.
4040Sstevel@tonic-gate 	 */
4050Sstevel@tonic-gate 	(void) snprintf(conid, sizeof (conid), "%s/%s%d_%s", lreq->lrq_loctoken,
4060Sstevel@tonic-gate 	    ds->ds_name, lreq->lrq_conver, lreq->lrq_conname);
4070Sstevel@tonic-gate 	cn = ds_get_container(ds, conid, lreq->lrq_crosshost);
4080Sstevel@tonic-gate 	if (cn == NULL) {
4090Sstevel@tonic-gate 		reply.rp_retval = DSVC_NO_MEMORY;
4100Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
4110Sstevel@tonic-gate 		return;
4120Sstevel@tonic-gate 	}
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	/*
4150Sstevel@tonic-gate 	 * We need another door descriptor which is passed back with the
4160Sstevel@tonic-gate 	 * request.  This descriptor is used when the caller wants to
4170Sstevel@tonic-gate 	 * gracefully unlock or when the caller terminates abnormally.
4180Sstevel@tonic-gate 	 */
4190Sstevel@tonic-gate 	ud = ud_create(cn, &reply.rp_retval);
4200Sstevel@tonic-gate 	if (ud == NULL) {
4210Sstevel@tonic-gate 		ds_release_container(ds, cn);
4220Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
4230Sstevel@tonic-gate 		return;
4240Sstevel@tonic-gate 	}
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate 	/*
4270Sstevel@tonic-gate 	 * We pass a duped door descriptor with the DOOR_RELEASE flag set
4280Sstevel@tonic-gate 	 * instead of just passing the descriptor itself to handle the case
4290Sstevel@tonic-gate 	 * where the client has gone away before we door_return().  Since
4300Sstevel@tonic-gate 	 * we duped, the door descriptor itself will have a refcount of 2
4310Sstevel@tonic-gate 	 * when we go to pass it to the client; if the client does not
4320Sstevel@tonic-gate 	 * exist, the DOOR_RELEASE will drop the count from 2 to 1 which
4330Sstevel@tonic-gate 	 * will cause a DOOR_UNREF_DATA call.
4340Sstevel@tonic-gate 	 *
4350Sstevel@tonic-gate 	 * In the regular (non-error) case, the door_return() will handoff
4360Sstevel@tonic-gate 	 * the descriptor to the client, bumping the refcount to 3, and
4370Sstevel@tonic-gate 	 * then the DOOR_RELEASE will drop the count to 2.  If the client
4380Sstevel@tonic-gate 	 * terminates abnormally after this point, the count will drop from
4390Sstevel@tonic-gate 	 * 2 to 1 which will cause a DOOR_UNREF_DATA call.  If the client
4400Sstevel@tonic-gate 	 * unlocks gracefully, the refcount will still be 2 when the unlock
4410Sstevel@tonic-gate 	 * door server procedure is called, and the unlock procedure will
4420Sstevel@tonic-gate 	 * unlock the lock and note that the lock has been unlocked (so
4430Sstevel@tonic-gate 	 * that we know the DOOR_UNREF_DATA call generated from the client
4440Sstevel@tonic-gate 	 * subsequently closing the unlock descriptor is benign).
4450Sstevel@tonic-gate 	 *
4460Sstevel@tonic-gate 	 * Note that a DOOR_UNREF_DATA call will be generated *any time*
4470Sstevel@tonic-gate 	 * the refcount goes from 2 to 1 -- even if *we* cause it to
4480Sstevel@tonic-gate 	 * happen, which by default will happen in some of the error logic
4490Sstevel@tonic-gate 	 * below (when we close the duped descriptor).  To prevent this
4500Sstevel@tonic-gate 	 * scenario, we tell ud_destroy() *not* to cache the unlock
4510Sstevel@tonic-gate 	 * descriptor, which forces it to blow away the descriptor using
4520Sstevel@tonic-gate 	 * door_revoke(), making the close() that follows benign.
4530Sstevel@tonic-gate 	 */
4540Sstevel@tonic-gate 	door_desc.d_attributes = DOOR_DESCRIPTOR|DOOR_RELEASE;
4550Sstevel@tonic-gate 	door_desc.d_data.d_desc.d_descriptor = dup(ud->ud_fd);
4560Sstevel@tonic-gate 	if (door_desc.d_data.d_desc.d_descriptor == -1) {
4570Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "cannot dup unlock door; denying %s "
4580Sstevel@tonic-gate 		    "lock request", cn->cn_id);
4590Sstevel@tonic-gate 		ud_destroy(ud, B_TRUE);
4600Sstevel@tonic-gate 		ds_release_container(ds, cn);
4610Sstevel@tonic-gate 		reply.rp_retval = DSVC_NO_RESOURCES;
4620Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
4630Sstevel@tonic-gate 		return;
4640Sstevel@tonic-gate 	}
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 	/*
4670Sstevel@tonic-gate 	 * Acquire the actual read or write lock on the container.
4680Sstevel@tonic-gate 	 */
4690Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "tid %d: %s locking %s", thr_self(),
4700Sstevel@tonic-gate 	    lreq->lrq_locktype == DSVCD_RDLOCK ? "read" : "write", cn->cn_id);
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate 	if (lreq->lrq_locktype == DSVCD_RDLOCK)
4730Sstevel@tonic-gate 		reply.rp_retval = cn_rdlock(cn, lreq->lrq_nonblock);
4740Sstevel@tonic-gate 	else if (lreq->lrq_locktype == DSVCD_WRLOCK)
4750Sstevel@tonic-gate 		reply.rp_retval = cn_wrlock(cn, lreq->lrq_nonblock);
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "tid %d: %s %s lock operation: %s", thr_self(),
4780Sstevel@tonic-gate 	    cn->cn_id, lreq->lrq_locktype == DSVCD_RDLOCK ? "read" : "write",
4790Sstevel@tonic-gate 	    dhcpsvc_errmsg(reply.rp_retval));
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate 	ds_release_container(ds, cn);
4820Sstevel@tonic-gate 	if (reply.rp_retval != DSVC_SUCCESS) {
4830Sstevel@tonic-gate 		ud_destroy(ud, B_FALSE);
4840Sstevel@tonic-gate 		(void) close(door_desc.d_data.d_desc.d_descriptor);
4850Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
4860Sstevel@tonic-gate 		return;
4870Sstevel@tonic-gate 	}
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 	while (door_return((char *)&reply, sizeof (reply), &door_desc, 1)
4900Sstevel@tonic-gate 	    == -1 && errno == EMFILE) {
4910Sstevel@tonic-gate 		if (lreq->lrq_nonblock) {
4920Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "unable to grant lock; client"
4930Sstevel@tonic-gate 			    " is out of file descriptors");
4940Sstevel@tonic-gate 			(void) cn_unlock(cn);
4950Sstevel@tonic-gate 			ud_destroy(ud, B_FALSE);
4960Sstevel@tonic-gate 			(void) close(door_desc.d_data.d_desc.d_descriptor);
4970Sstevel@tonic-gate 			reply.rp_retval = DSVC_BUSY;
4980Sstevel@tonic-gate 			(void) door_return((char *)&reply, sizeof (reply),
4990Sstevel@tonic-gate 			    NULL, 0);
5000Sstevel@tonic-gate 			return;
5010Sstevel@tonic-gate 		}
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 		if (attempts++ == 0) {
5040Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "unable to grant lock; client"
5050Sstevel@tonic-gate 			    " is out of file descriptors (retrying)");
5060Sstevel@tonic-gate 		}
5070Sstevel@tonic-gate 		(void) poll(NULL, 0, 100);
5080Sstevel@tonic-gate 	}
5090Sstevel@tonic-gate }
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate /*
5120Sstevel@tonic-gate  * Service an unlock request `req' passed across the door associated with
5130Sstevel@tonic-gate  * the unlock token `cookie'.  We may be called explicitly (in which case
5140Sstevel@tonic-gate  * the request is a well-formed dsvcd_request_t) or implicitly (in which
5150Sstevel@tonic-gate  * case our request is set to the value DOOR_UNREF_DATA); this latter case
5160Sstevel@tonic-gate  * occurs when a process holding a lock terminates.  In either case, unlock
5170Sstevel@tonic-gate  * the lock; in the implicit case, log a message as well.
5180Sstevel@tonic-gate  */
5190Sstevel@tonic-gate /* ARGSUSED */
5200Sstevel@tonic-gate static void
svc_unlock(void * cookie,dsvcd_request_t * req,size_t reqsize,door_desc_t * doorp,uint_t ndoors)5210Sstevel@tonic-gate svc_unlock(void *cookie, dsvcd_request_t *req, size_t reqsize,
5220Sstevel@tonic-gate     door_desc_t *doorp, uint_t ndoors)
5230Sstevel@tonic-gate {
5240Sstevel@tonic-gate 	dsvcd_unlock_desc_t	*ud = cookie;
5250Sstevel@tonic-gate 	dsvcd_container_t	*cn;
5260Sstevel@tonic-gate 	dsvcd_reply_t		reply;
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 	/*
5290Sstevel@tonic-gate 	 * Although unlock descriptors are handed out to only a single
5300Sstevel@tonic-gate 	 * thread who has been granted a lock (ergo it seems that only one
5310Sstevel@tonic-gate 	 * thread should be able to call us back), there's a potential race
5320Sstevel@tonic-gate 	 * here if the process crashes while in this door_call(), since
5330Sstevel@tonic-gate 	 * both this thread and the unref kernel upcall thread may run at
5340Sstevel@tonic-gate 	 * the same time.  Protect against this case with a mutex.
5350Sstevel@tonic-gate 	 */
5360Sstevel@tonic-gate 	(void) mutex_lock(&ud->ud_lock);
5370Sstevel@tonic-gate 	cn = ud->ud_cn;
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 	/*
5400Sstevel@tonic-gate 	 * First handle the case where the lock owner has closed the unlock
5410Sstevel@tonic-gate 	 * descriptor, either because they have unlocked the lock and are
5420Sstevel@tonic-gate 	 * thus done using the descriptor, or because they crashed.  In the
5430Sstevel@tonic-gate 	 * second case, print a message.
5440Sstevel@tonic-gate 	 */
5450Sstevel@tonic-gate 	if (req == DOOR_UNREF_DATA) {
5460Sstevel@tonic-gate 		/*
5470Sstevel@tonic-gate 		 * The last reference is ours; we can free the descriptor.
5480Sstevel@tonic-gate 		 */
5490Sstevel@tonic-gate 		(void) mutex_unlock(&ud->ud_lock);
5500Sstevel@tonic-gate 		ud_destroy(ud, B_TRUE);
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 		/*
5530Sstevel@tonic-gate 		 * Normal case: the caller is closing the unlock descriptor
5540Sstevel@tonic-gate 		 * on a lock they've already unlocked -- just return.
5550Sstevel@tonic-gate 		 */
5560Sstevel@tonic-gate 		if (cn == NULL) {
5570Sstevel@tonic-gate 			(void) door_return(NULL, 0, NULL, 0);
5580Sstevel@tonic-gate 			return;
5590Sstevel@tonic-gate 		}
5600Sstevel@tonic-gate 
5610Sstevel@tonic-gate 		/*
5620Sstevel@tonic-gate 		 * Error case: the caller has crashed while holding the
5630Sstevel@tonic-gate 		 * unlock descriptor (or is otherwise in violation of
5640Sstevel@tonic-gate 		 * protocol).  Since all datastores are required to be
5650Sstevel@tonic-gate 		 * robust even if unexpected termination occurs, we assume
5660Sstevel@tonic-gate 		 * the container is not corrupt, even if the process
5670Sstevel@tonic-gate 		 * crashed with the write lock held.
5680Sstevel@tonic-gate 		 */
5690Sstevel@tonic-gate 		switch (cn_locktype(cn)) {
5700Sstevel@tonic-gate 		case DSVCD_RDLOCK:
5710Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "process exited while reading "
5720Sstevel@tonic-gate 			    "`%s'; unlocking", cn->cn_id);
5730Sstevel@tonic-gate 			(void) cn_unlock(cn);
5740Sstevel@tonic-gate 			break;
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 		case DSVCD_WRLOCK:
5770Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "process exited while writing "
5780Sstevel@tonic-gate 			    "`%s'; unlocking", cn->cn_id);
5790Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "note that this write operation "
5800Sstevel@tonic-gate 			    "may or may not have succeeded");
5810Sstevel@tonic-gate 			(void) cn_unlock(cn);
5820Sstevel@tonic-gate 			break;
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 		case DSVCD_NOLOCK:
5850Sstevel@tonic-gate 			dhcpmsg(MSG_CRIT, "unreferenced unheld lock");
5860Sstevel@tonic-gate 			break;
5870Sstevel@tonic-gate 		}
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 		(void) door_return(NULL, 0, NULL, 0);
5900Sstevel@tonic-gate 		return;
5910Sstevel@tonic-gate 	}
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 	/*
5940Sstevel@tonic-gate 	 * Verify that this is a unlock request; if in the future we support
5950Sstevel@tonic-gate 	 * other requests, we'll have to abstract this a bit.
5960Sstevel@tonic-gate 	 */
5970Sstevel@tonic-gate 	reply.rp_version = DSVCD_DOOR_VERSION;
5980Sstevel@tonic-gate 	reply.rp_retval = check_door_req(req, reqsize,
5990Sstevel@tonic-gate 	    sizeof (dsvcd_unlock_request_t));
6000Sstevel@tonic-gate 	if (reply.rp_retval != DSVC_SUCCESS) {
6010Sstevel@tonic-gate 		(void) mutex_unlock(&ud->ud_lock);
6020Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
6030Sstevel@tonic-gate 		return;
6040Sstevel@tonic-gate 	}
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 	if (req->rq_reqtype != DSVCD_UNLOCK) {
6070Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "unsupported request `%d' on unlock "
6080Sstevel@tonic-gate 		    "request door", req->rq_reqtype);
6090Sstevel@tonic-gate 		(void) mutex_unlock(&ud->ud_lock);
6100Sstevel@tonic-gate 		reply.rp_retval = DSVC_SYNCH_ERR;
6110Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
6120Sstevel@tonic-gate 		return;
6130Sstevel@tonic-gate 	}
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 	/*
6160Sstevel@tonic-gate 	 * Attempt to unlock an already-unlocked container; log and return.
6170Sstevel@tonic-gate 	 */
6180Sstevel@tonic-gate 	if (cn == NULL) {
6190Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "process tried to re-unlock a lock");
6200Sstevel@tonic-gate 		(void) mutex_unlock(&ud->ud_lock);
6210Sstevel@tonic-gate 		reply.rp_retval = DSVC_SYNCH_ERR;
6220Sstevel@tonic-gate 		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
6230Sstevel@tonic-gate 		return;
6240Sstevel@tonic-gate 	}
6250Sstevel@tonic-gate 	ud->ud_cn = NULL;
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate 	/*
6280Sstevel@tonic-gate 	 * Unlock the container; note that after cn_unlock() has been done
6290Sstevel@tonic-gate 	 * cn->cn_id is no longer accessible.
6300Sstevel@tonic-gate 	 */
6310Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "tid %d: unlocking %s", thr_self(), cn->cn_id);
6320Sstevel@tonic-gate 	reply.rp_retval = cn_unlock(cn);
6330Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "tid %d: unlock operation: %s", thr_self(),
6340Sstevel@tonic-gate 	    dhcpsvc_errmsg(reply.rp_retval));
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 	/*
6370Sstevel@tonic-gate 	 * Even though we've unlocked the lock, we cannot yet destroy the
6380Sstevel@tonic-gate 	 * unlock descriptor (even if we revoke the door) because it's
6390Sstevel@tonic-gate 	 * possible the unref thread is already waiting on ud_lock.
6400Sstevel@tonic-gate 	 */
6410Sstevel@tonic-gate 	(void) mutex_unlock(&ud->ud_lock);
6420Sstevel@tonic-gate 	(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
6430Sstevel@tonic-gate }
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate /*
6460Sstevel@tonic-gate  * Reap containers that have not been recently used.
6470Sstevel@tonic-gate  */
6480Sstevel@tonic-gate static void *
reaper(void * ds_table_raw)6490Sstevel@tonic-gate reaper(void *ds_table_raw)
6500Sstevel@tonic-gate {
6510Sstevel@tonic-gate 	dsvcd_datastore_t	**ds_table;
6520Sstevel@tonic-gate 	unsigned int		i, nreaped;
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate 	ds_table = (dsvcd_datastore_t **)ds_table_raw;
6550Sstevel@tonic-gate 	for (;;) {
6560Sstevel@tonic-gate 		(void) sleep(DSVCD_REAP_INTERVAL);
6570Sstevel@tonic-gate 		for (i = 0; ds_table[i] != NULL; i++) {
6580Sstevel@tonic-gate 			nreaped = ds_reap_containers(ds_table[i],
6590Sstevel@tonic-gate 			    DSVCD_REAP_THRESH);
6600Sstevel@tonic-gate 			if (nreaped > 0) {
6610Sstevel@tonic-gate 				dhcpmsg(MSG_VERBOSE, "reaped %u container "
6620Sstevel@tonic-gate 				    "synchpoints from %s", nreaped,
6630Sstevel@tonic-gate 				    ds_table[i]->ds_name);
6640Sstevel@tonic-gate 			}
6650Sstevel@tonic-gate 		}
6660Sstevel@tonic-gate 	}
6670Sstevel@tonic-gate 	/* NOTREACHED */
6680Sstevel@tonic-gate 	return (NULL);
6690Sstevel@tonic-gate }
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate /*
6720Sstevel@tonic-gate  * Daemonize the process.
6730Sstevel@tonic-gate  */
6740Sstevel@tonic-gate static int
daemonize(void)6750Sstevel@tonic-gate daemonize(void)
6760Sstevel@tonic-gate {
6770Sstevel@tonic-gate 	switch (fork()) {
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate 	case -1:
6800Sstevel@tonic-gate 		return (0);
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 	case  0:
6830Sstevel@tonic-gate 		/*
6840Sstevel@tonic-gate 		 * Lose our controlling terminal, and become both a session
6850Sstevel@tonic-gate 		 * leader and a process group leader.
6860Sstevel@tonic-gate 		 */
6870Sstevel@tonic-gate 		if (setsid() == -1)
6880Sstevel@tonic-gate 			return (0);
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 		/*
6910Sstevel@tonic-gate 		 * Under POSIX, a session leader can accidentally (through
6920Sstevel@tonic-gate 		 * open(2)) acquire a controlling terminal if it does not
6930Sstevel@tonic-gate 		 * have one.  Just to be safe, fork() again so we are not a
6940Sstevel@tonic-gate 		 * session leader.
6950Sstevel@tonic-gate 		 */
6960Sstevel@tonic-gate 		switch (fork()) {
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate 		case -1:
6990Sstevel@tonic-gate 			return (0);
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 		case 0:
7020Sstevel@tonic-gate 			(void) signal(SIGHUP, SIG_IGN);
7030Sstevel@tonic-gate 			(void) chdir("/");
7040Sstevel@tonic-gate 			(void) umask(022);
7050Sstevel@tonic-gate 			closefrom(0);
7060Sstevel@tonic-gate 			break;
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 		default:
7090Sstevel@tonic-gate 			_exit(EXIT_SUCCESS);
7100Sstevel@tonic-gate 		}
7110Sstevel@tonic-gate 		break;
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 	default:
7140Sstevel@tonic-gate 		_exit(EXIT_SUCCESS);
7150Sstevel@tonic-gate 	}
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 	return (1);
7180Sstevel@tonic-gate }
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate /*
7210Sstevel@tonic-gate  * Create an unlock descriptor for container `cn' -- returns an unlock
7220Sstevel@tonic-gate  * descriptor on success, or NULL on failure; the reason for failure is in
7230Sstevel@tonic-gate  * `retvalp'.  Since creating door descriptors is expensive, we keep a few
7240Sstevel@tonic-gate  * cache a small list of old descriptors around on a reclaim list and only
7250Sstevel@tonic-gate  * allocate a new one if the list is empty.
7260Sstevel@tonic-gate  */
7270Sstevel@tonic-gate static dsvcd_unlock_desc_t *
ud_create(dsvcd_container_t * cn,int * retvalp)7280Sstevel@tonic-gate ud_create(dsvcd_container_t *cn, int *retvalp)
7290Sstevel@tonic-gate {
7300Sstevel@tonic-gate 	dsvcd_unlock_desc_t *ud;
7310Sstevel@tonic-gate 
7320Sstevel@tonic-gate 	*retvalp = DSVC_SUCCESS;
7330Sstevel@tonic-gate 	(void) mutex_lock(&ud_reclaim_lock);
7340Sstevel@tonic-gate 	if (ud_reclaim_list != NULL) {
7350Sstevel@tonic-gate 		ud = ud_reclaim_list;
7360Sstevel@tonic-gate 		ud_reclaim_list = ud->ud_next;
7370Sstevel@tonic-gate 		ud_reclaim_count--;
7380Sstevel@tonic-gate 		(void) mutex_unlock(&ud_reclaim_lock);
7390Sstevel@tonic-gate 	} else {
7400Sstevel@tonic-gate 		(void) mutex_unlock(&ud_reclaim_lock);
7410Sstevel@tonic-gate 		ud = malloc(sizeof (dsvcd_unlock_desc_t));
7420Sstevel@tonic-gate 		if (ud == NULL) {
7430Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "cannot allocate unlock door "
7440Sstevel@tonic-gate 			    "descriptor; denying %s lock request", cn->cn_id);
7450Sstevel@tonic-gate 			*retvalp = DSVC_NO_MEMORY;
7460Sstevel@tonic-gate 			return (NULL);
7470Sstevel@tonic-gate 		}
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate 		(void) mutex_init(&ud->ud_lock, USYNC_THREAD, NULL);
7500Sstevel@tonic-gate 		ud->ud_fd = door_create((void (*)())svc_unlock, ud,
7510Sstevel@tonic-gate 		    DOOR_UNREF_MULTI | DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
7520Sstevel@tonic-gate 		if (ud->ud_fd == -1) {
7530Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "cannot create unlock door; "
7540Sstevel@tonic-gate 			    "denying %s lock request", cn->cn_id);
7550Sstevel@tonic-gate 			free(ud);
7560Sstevel@tonic-gate 			*retvalp = DSVC_NO_RESOURCES;
7570Sstevel@tonic-gate 			return (NULL);
7580Sstevel@tonic-gate 		}
7590Sstevel@tonic-gate 	}
7600Sstevel@tonic-gate 
7610Sstevel@tonic-gate 	ud->ud_next = NULL;
7620Sstevel@tonic-gate 	ud->ud_cn = cn;
7630Sstevel@tonic-gate 	return (ud);
7640Sstevel@tonic-gate }
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate /*
7670Sstevel@tonic-gate  * Destroy the unlock descriptor `ud' -- `ud' must be unlocked on entry.
7680Sstevel@tonic-gate  * If there's room and `cacheable' is set, then, keep the unlock descriptor
7690Sstevel@tonic-gate  * on the reclaim list to lower future creation cost.
7700Sstevel@tonic-gate  */
7710Sstevel@tonic-gate static void
ud_destroy(dsvcd_unlock_desc_t * ud,boolean_t cacheable)7720Sstevel@tonic-gate ud_destroy(dsvcd_unlock_desc_t *ud, boolean_t cacheable)
7730Sstevel@tonic-gate {
7740Sstevel@tonic-gate 	assert(!MUTEX_HELD(&ud->ud_lock));
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	ud->ud_cn = NULL;
7770Sstevel@tonic-gate 	(void) mutex_lock(&ud_reclaim_lock);
7780Sstevel@tonic-gate 	if (cacheable && ud_reclaim_count < UD_RECLAIM_MAX) {
7790Sstevel@tonic-gate 		ud->ud_next = ud_reclaim_list;
7800Sstevel@tonic-gate 		ud_reclaim_list = ud;
7810Sstevel@tonic-gate 		ud_reclaim_count++;
7820Sstevel@tonic-gate 		(void) mutex_unlock(&ud_reclaim_lock);
7830Sstevel@tonic-gate 	} else {
7840Sstevel@tonic-gate 		(void) mutex_unlock(&ud_reclaim_lock);
7850Sstevel@tonic-gate 		(void) door_revoke(ud->ud_fd);
7860Sstevel@tonic-gate 		(void) mutex_destroy(&ud->ud_lock);
7870Sstevel@tonic-gate 		free(ud);
7880Sstevel@tonic-gate 	}
7890Sstevel@tonic-gate }
7900Sstevel@tonic-gate 
7910Sstevel@tonic-gate /*
7920Sstevel@tonic-gate  * Create a stack of `*stacksizep' bytes (rounded up to the nearest page)
7930Sstevel@tonic-gate  * including a redzone for catching stack overflow.  Set `stacksizep' to
7940Sstevel@tonic-gate  * point to the actual usable size of the stack (i.e., everything but the
7950Sstevel@tonic-gate  * redzone).  Returns a pointer to the base of the stack (not including the
7960Sstevel@tonic-gate  * redzone).
7970Sstevel@tonic-gate  */
7980Sstevel@tonic-gate static void *
stack_create(unsigned int * stacksizep)7990Sstevel@tonic-gate stack_create(unsigned int *stacksizep)
8000Sstevel@tonic-gate {
8010Sstevel@tonic-gate 	caddr_t		stackbase;
8020Sstevel@tonic-gate 	unsigned int	redzone = roundup(DSVCD_STACK_REDSIZE, PAGESIZE);
8030Sstevel@tonic-gate 	unsigned int	stacksize = *stacksizep;
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate 	if (stacksize < sysconf(_SC_THREAD_STACK_MIN))
8060Sstevel@tonic-gate 		stacksize = sysconf(_SC_THREAD_STACK_MIN);
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate 	stacksize = roundup(stacksize, PAGESIZE);
8090Sstevel@tonic-gate 	stackbase = mmap(NULL, stacksize + redzone, PROT_READ | PROT_WRITE,
8100Sstevel@tonic-gate 	    MAP_ANON | MAP_PRIVATE, -1, 0);
8110Sstevel@tonic-gate 	if (stackbase == MAP_FAILED)
8120Sstevel@tonic-gate 		return (NULL);
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate 	*stacksizep = stacksize;
8150Sstevel@tonic-gate 	(void) mprotect(stackbase, redzone, PROT_NONE);
8160Sstevel@tonic-gate 	return (stackbase + redzone);
8170Sstevel@tonic-gate }
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate /*
8200Sstevel@tonic-gate  * Destroy the stack of `stacksize' bytes pointed to by `stackbase'.
8210Sstevel@tonic-gate  */
8220Sstevel@tonic-gate static void
stack_destroy(void * stackbase,unsigned int stacksize)8230Sstevel@tonic-gate stack_destroy(void *stackbase, unsigned int stacksize)
8240Sstevel@tonic-gate {
8250Sstevel@tonic-gate 	unsigned int	redzone = roundup(DSVCD_STACK_REDSIZE, PAGESIZE);
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate 	(void) munmap((caddr_t)stackbase - redzone, stacksize + redzone);
8280Sstevel@tonic-gate }
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate /*
8310Sstevel@tonic-gate  * Start function for door server threads; turns off thread cancellation
8320Sstevel@tonic-gate  * and then parks in the kernel via door_return().
8330Sstevel@tonic-gate  */
8340Sstevel@tonic-gate /* ARGSUSED */
8350Sstevel@tonic-gate static void *
doorserv_thread(void * arg)8360Sstevel@tonic-gate doorserv_thread(void *arg)
8370Sstevel@tonic-gate {
8380Sstevel@tonic-gate 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
8390Sstevel@tonic-gate 	(void) door_return(NULL, 0, NULL, 0);
8400Sstevel@tonic-gate 	return (NULL);
8410Sstevel@tonic-gate }
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate /*
8440Sstevel@tonic-gate  * Creation function for door server threads.  We require door threads to
8450Sstevel@tonic-gate  * have 32K of backed stack.  This is a guess but will be more than
8460Sstevel@tonic-gate  * sufficient for our uses, since door threads have a shallow call depth
8470Sstevel@tonic-gate  * and the functions use little automatic storage.
8480Sstevel@tonic-gate  */
8490Sstevel@tonic-gate /* ARGSUSED */
8500Sstevel@tonic-gate static void
doorserv_create(door_info_t * infop)8510Sstevel@tonic-gate doorserv_create(door_info_t *infop)
8520Sstevel@tonic-gate {
8530Sstevel@tonic-gate 	void		*stackbase;
8540Sstevel@tonic-gate 	unsigned int	stacksize = 32 * 1024;
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate 	stackbase = stack_create(&stacksize);
8570Sstevel@tonic-gate 	if (stackbase != NULL) {
8580Sstevel@tonic-gate 		errno = thr_create(stackbase, stacksize, doorserv_thread, NULL,
8590Sstevel@tonic-gate 		    THR_BOUND | THR_DETACHED, NULL);
8600Sstevel@tonic-gate 		if (errno != 0) {
8610Sstevel@tonic-gate 			dhcpmsg(MSG_ERR, "cannot create door server thread; "
8620Sstevel@tonic-gate 			    "server thread pool will not be grown");
8630Sstevel@tonic-gate 			stack_destroy(stackbase, stacksize);
8640Sstevel@tonic-gate 		}
8650Sstevel@tonic-gate 	}
8660Sstevel@tonic-gate }
867