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