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
 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
 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
 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
 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 *
 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
 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 *
 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
 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 *
 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
 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 *
 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
 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 }