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