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 /*
  23  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2015 RackTop Systems.
  25  */
  26 
  27 /*
  28  * This is the client layer for svc.configd.  All direct protocol interactions
  29  * are handled here.
  30  *
  31  * Essentially, the job of this layer is to turn the idempotent protocol
  32  * into a series of non-idempotent calls into the object layer, while
  33  * also handling the necessary locking.
  34  */
  35 
  36 #include <alloca.h>
  37 #include <assert.h>
  38 #include <bsm/adt_event.h>
  39 #include <door.h>
  40 #include <errno.h>
  41 #include <libintl.h>
  42 #include <limits.h>
  43 #include <pthread.h>
  44 #include <stdio.h>
  45 #include <stdlib.h>
  46 #include <string.h>
  47 #include <syslog.h>
  48 #include <ucred.h>
  49 #include <unistd.h>
  50 
  51 #include <libuutil.h>
  52 
  53 #include "configd.h"
  54 #include "repcache_protocol.h"
  55 
  56 #define INVALID_CHANGEID        (0)
  57 #define INVALID_DOORID          ((door_id_t)-1)
  58 #define INVALID_RESULT          ((rep_protocol_responseid_t)INT_MIN)
  59 
  60 /*
  61  * lint doesn't like constant assertions
  62  */
  63 #ifdef lint
  64 #define assert_nolint(x) (void)0
  65 #else
  66 #define assert_nolint(x) assert(x)
  67 #endif
  68 
  69 /*
  70  * Protects client linkage and the freelist
  71  */
  72 #define CLIENT_HASH_SIZE        64
  73 
  74 #pragma align 64(client_hash)
  75 static client_bucket_t client_hash[CLIENT_HASH_SIZE];
  76 
  77 static uu_avl_pool_t *entity_pool;
  78 static uu_avl_pool_t *iter_pool;
  79 static uu_list_pool_t *client_pool;
  80 
  81 #define CLIENT_HASH(id)         (&client_hash[((id) & (CLIENT_HASH_SIZE - 1))])
  82 
  83 uint_t request_log_size = 1024;         /* tunable, before we start */
  84 
  85 static pthread_mutex_t request_log_lock = PTHREAD_MUTEX_INITIALIZER;
  86 static uint_t request_log_cur;
  87 request_log_entry_t     *request_log;
  88 
  89 static uint32_t         client_maxid;
  90 static pthread_mutex_t  client_lock;    /* protects client_maxid */
  91 
  92 static request_log_entry_t *
  93 get_log(void)
  94 {
  95         thread_info_t *ti = thread_self();
  96         return (&ti->ti_log);
  97 }
  98 
  99 void
 100 log_enter(request_log_entry_t *rlp)
 101 {
 102         if (rlp->rl_start != 0 && request_log != NULL) {
 103                 request_log_entry_t *logrlp;
 104 
 105                 (void) pthread_mutex_lock(&request_log_lock);
 106                 assert(request_log_cur < request_log_size);
 107                 logrlp = &request_log[request_log_cur++];
 108                 if (request_log_cur == request_log_size)
 109                         request_log_cur = 0;
 110                 (void) memcpy(logrlp, rlp, sizeof (*rlp));
 111                 (void) pthread_mutex_unlock(&request_log_lock);
 112         }
 113 }
 114 
 115 /*
 116  * Note that the svc.configd dmod will join all of the per-thread log entries
 117  * with the main log, so that even if the log is disabled, there is some
 118  * information available.
 119  */
 120 static request_log_entry_t *
 121 start_log(uint32_t clientid)
 122 {
 123         request_log_entry_t *rlp = get_log();
 124 
 125         log_enter(rlp);
 126 
 127         (void) memset(rlp, 0, sizeof (*rlp));
 128         rlp->rl_start = gethrtime();
 129         rlp->rl_tid = pthread_self();
 130         rlp->rl_clientid = clientid;
 131 
 132         return (rlp);
 133 }
 134 
 135 void
 136 end_log(void)
 137 {
 138         request_log_entry_t *rlp = get_log();
 139 
 140         rlp->rl_end = gethrtime();
 141 }
 142 
 143 static void
 144 add_log_ptr(request_log_entry_t *rlp, enum rc_ptr_type type, uint32_t id,
 145     void *ptr)
 146 {
 147         request_log_ptr_t *rpp;
 148 
 149         if (rlp == NULL)
 150                 return;
 151 
 152         if (rlp->rl_num_ptrs >= MAX_PTRS)
 153                 return;
 154 
 155         rpp = &rlp->rl_ptrs[rlp->rl_num_ptrs++];
 156         rpp->rlp_type = type;
 157         rpp->rlp_id = id;
 158         rpp->rlp_ptr = ptr;
 159 
 160         /*
 161          * For entities, it's useful to have the node pointer at the start
 162          * of the request.
 163          */
 164         if (type == RC_PTR_TYPE_ENTITY && ptr != NULL)
 165                 rpp->rlp_data = ((repcache_entity_t *)ptr)->re_node.rnp_node;
 166 }
 167 
 168 int
 169 client_is_privileged(void)
 170 {
 171         thread_info_t *ti = thread_self();
 172 
 173         ucred_t *uc;
 174 
 175         if (ti->ti_active_client != NULL &&
 176             ti->ti_active_client->rc_all_auths)
 177                 return (1);
 178 
 179         if ((uc = get_ucred()) == NULL)
 180                 return (0);
 181 
 182         return (ucred_is_privileged(uc));
 183 }
 184 
 185 /*ARGSUSED*/
 186 static int
 187 client_compare(const void *lc_arg, const void *rc_arg, void *private)
 188 {
 189         uint32_t l_id = ((const repcache_client_t *)lc_arg)->rc_id;
 190         uint32_t r_id = ((const repcache_client_t *)rc_arg)->rc_id;
 191 
 192         if (l_id > r_id)
 193                 return (1);
 194         if (l_id < r_id)
 195                 return (-1);
 196         return (0);
 197 }
 198 
 199 /*ARGSUSED*/
 200 static int
 201 entity_compare(const void *lc_arg, const void *rc_arg, void *private)
 202 {
 203         uint32_t l_id = ((const repcache_entity_t *)lc_arg)->re_id;
 204         uint32_t r_id = ((const repcache_entity_t *)rc_arg)->re_id;
 205 
 206         if (l_id > r_id)
 207                 return (1);
 208         if (l_id < r_id)
 209                 return (-1);
 210         return (0);
 211 }
 212 
 213 /*ARGSUSED*/
 214 static int
 215 iter_compare(const void *lc_arg, const void *rc_arg, void *private)
 216 {
 217         uint32_t l_id = ((const repcache_iter_t *)lc_arg)->ri_id;
 218         uint32_t r_id = ((const repcache_iter_t *)rc_arg)->ri_id;
 219 
 220         if (l_id > r_id)
 221                 return (1);
 222         if (l_id < r_id)
 223                 return (-1);
 224         return (0);
 225 }
 226 
 227 static int
 228 client_hash_init(void)
 229 {
 230         int x;
 231 
 232         assert_nolint(offsetof(repcache_entity_t, re_id) == 0);
 233         entity_pool = uu_avl_pool_create("repcache_entitys",
 234             sizeof (repcache_entity_t), offsetof(repcache_entity_t, re_link),
 235             entity_compare, UU_AVL_POOL_DEBUG);
 236 
 237         assert_nolint(offsetof(repcache_iter_t, ri_id) == 0);
 238         iter_pool = uu_avl_pool_create("repcache_iters",
 239             sizeof (repcache_iter_t), offsetof(repcache_iter_t, ri_link),
 240             iter_compare, UU_AVL_POOL_DEBUG);
 241 
 242         assert_nolint(offsetof(repcache_client_t, rc_id) == 0);
 243         client_pool = uu_list_pool_create("repcache_clients",
 244             sizeof (repcache_client_t), offsetof(repcache_client_t, rc_link),
 245             client_compare, UU_LIST_POOL_DEBUG);
 246 
 247         if (entity_pool == NULL || iter_pool == NULL || client_pool == NULL)
 248                 return (0);
 249 
 250         for (x = 0; x < CLIENT_HASH_SIZE; x++) {
 251                 uu_list_t *lp = uu_list_create(client_pool, &client_hash[x],
 252                     UU_LIST_SORTED);
 253                 if (lp == NULL)
 254                         return (0);
 255 
 256                 (void) pthread_mutex_init(&client_hash[x].cb_lock, NULL);
 257                 client_hash[x].cb_list = lp;
 258         }
 259 
 260         return (1);
 261 }
 262 
 263 static repcache_client_t *
 264 client_alloc(void)
 265 {
 266         repcache_client_t *cp;
 267         cp = uu_zalloc(sizeof (*cp));
 268         if (cp == NULL)
 269                 return (NULL);
 270 
 271         cp->rc_entities = uu_avl_create(entity_pool, cp, 0);
 272         if (cp->rc_entities == NULL)
 273                 goto fail;
 274 
 275         cp->rc_iters = uu_avl_create(iter_pool, cp, 0);
 276         if (cp->rc_iters == NULL)
 277                 goto fail;
 278 
 279         uu_list_node_init(cp, &cp->rc_link, client_pool);
 280 
 281         cp->rc_doorfd = -1;
 282         cp->rc_doorid = INVALID_DOORID;
 283 
 284         (void) pthread_mutex_init(&cp->rc_lock, NULL);
 285         (void) pthread_mutex_init(&cp->rc_annotate_lock, NULL);
 286 
 287         rc_node_ptr_init(&cp->rc_notify_ptr);
 288 
 289         return (cp);
 290 
 291 fail:
 292         if (cp->rc_iters != NULL)
 293                 uu_avl_destroy(cp->rc_iters);
 294         if (cp->rc_entities != NULL)
 295                 uu_avl_destroy(cp->rc_entities);
 296         uu_free(cp);
 297         return (NULL);
 298 }
 299 
 300 static void
 301 client_free(repcache_client_t *cp)
 302 {
 303         assert(cp->rc_insert_thr == 0);
 304         assert(cp->rc_refcnt == 0);
 305         assert(cp->rc_doorfd == -1);
 306         assert(cp->rc_doorid == INVALID_DOORID);
 307         assert(uu_avl_first(cp->rc_entities) == NULL);
 308         assert(uu_avl_first(cp->rc_iters) == NULL);
 309         uu_avl_destroy(cp->rc_entities);
 310         uu_avl_destroy(cp->rc_iters);
 311         uu_list_node_fini(cp, &cp->rc_link, client_pool);
 312         (void) pthread_mutex_destroy(&cp->rc_lock);
 313         (void) pthread_mutex_destroy(&cp->rc_annotate_lock);
 314         rc_node_ptr_free_mem(&cp->rc_notify_ptr);
 315         uu_free(cp);
 316 }
 317 
 318 static void
 319 client_insert(repcache_client_t *cp)
 320 {
 321         client_bucket_t *bp = CLIENT_HASH(cp->rc_id);
 322         uu_list_index_t idx;
 323 
 324         assert(cp->rc_id > 0);
 325 
 326         (void) pthread_mutex_lock(&bp->cb_lock);
 327         /*
 328          * We assume it does not already exist
 329          */
 330         (void) uu_list_find(bp->cb_list, cp, NULL, &idx);
 331         uu_list_insert(bp->cb_list, cp, idx);
 332 
 333         (void) pthread_mutex_unlock(&bp->cb_lock);
 334 }
 335 
 336 static repcache_client_t *
 337 client_lookup(uint32_t id)
 338 {
 339         client_bucket_t *bp = CLIENT_HASH(id);
 340         repcache_client_t *cp;
 341 
 342         (void) pthread_mutex_lock(&bp->cb_lock);
 343 
 344         cp = uu_list_find(bp->cb_list, &id, NULL, NULL);
 345 
 346         /*
 347          * Bump the reference count
 348          */
 349         if (cp != NULL) {
 350                 (void) pthread_mutex_lock(&cp->rc_lock);
 351                 assert(!(cp->rc_flags & RC_CLIENT_DEAD));
 352                 cp->rc_refcnt++;
 353                 (void) pthread_mutex_unlock(&cp->rc_lock);
 354         }
 355         (void) pthread_mutex_unlock(&bp->cb_lock);
 356 
 357         return (cp);
 358 }
 359 
 360 static void
 361 client_release(repcache_client_t *cp)
 362 {
 363         (void) pthread_mutex_lock(&cp->rc_lock);
 364         assert(cp->rc_refcnt > 0);
 365         assert(cp->rc_insert_thr != pthread_self());
 366 
 367         --cp->rc_refcnt;
 368         (void) pthread_cond_broadcast(&cp->rc_cv);
 369         (void) pthread_mutex_unlock(&cp->rc_lock);
 370 }
 371 
 372 /*
 373  * We only allow one thread to be inserting at a time, to prevent
 374  * insert/insert races.
 375  */
 376 static void
 377 client_start_insert(repcache_client_t *cp)
 378 {
 379         (void) pthread_mutex_lock(&cp->rc_lock);
 380         assert(cp->rc_refcnt > 0);
 381 
 382         while (cp->rc_insert_thr != 0) {
 383                 assert(cp->rc_insert_thr != pthread_self());
 384                 (void) pthread_cond_wait(&cp->rc_cv, &cp->rc_lock);
 385         }
 386         cp->rc_insert_thr = pthread_self();
 387         (void) pthread_mutex_unlock(&cp->rc_lock);
 388 }
 389 
 390 static void
 391 client_end_insert(repcache_client_t *cp)
 392 {
 393         (void) pthread_mutex_lock(&cp->rc_lock);
 394         assert(cp->rc_insert_thr == pthread_self());
 395         cp->rc_insert_thr = 0;
 396         (void) pthread_cond_broadcast(&cp->rc_cv);
 397         (void) pthread_mutex_unlock(&cp->rc_lock);
 398 }
 399 
 400 /*ARGSUSED*/
 401 static repcache_entity_t *
 402 entity_alloc(repcache_client_t *cp)
 403 {
 404         repcache_entity_t *ep = uu_zalloc(sizeof (repcache_entity_t));
 405         if (ep != NULL) {
 406                 uu_avl_node_init(ep, &ep->re_link, entity_pool);
 407         }
 408         return (ep);
 409 }
 410 
 411 static void
 412 entity_add(repcache_client_t *cp, repcache_entity_t *ep)
 413 {
 414         uu_avl_index_t idx;
 415 
 416         (void) pthread_mutex_lock(&cp->rc_lock);
 417         assert(cp->rc_insert_thr == pthread_self());
 418 
 419         (void) uu_avl_find(cp->rc_entities, ep, NULL, &idx);
 420         uu_avl_insert(cp->rc_entities, ep, idx);
 421 
 422         (void) pthread_mutex_unlock(&cp->rc_lock);
 423 }
 424 
 425 static repcache_entity_t *
 426 entity_find(repcache_client_t *cp, uint32_t id)
 427 {
 428         repcache_entity_t *ep;
 429 
 430         (void) pthread_mutex_lock(&cp->rc_lock);
 431         ep = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
 432         if (ep != NULL) {
 433                 add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, ep);
 434                 (void) pthread_mutex_lock(&ep->re_lock);
 435         }
 436         (void) pthread_mutex_unlock(&cp->rc_lock);
 437 
 438         return (ep);
 439 }
 440 
 441 /*
 442  * Fails with
 443  *   _DUPLICATE_ID - the ids are equal
 444  *   _UNKNOWN_ID - an id does not designate an active register
 445  */
 446 static int
 447 entity_find2(repcache_client_t *cp, uint32_t id1, repcache_entity_t **out1,
 448     uint32_t id2, repcache_entity_t **out2)
 449 {
 450         repcache_entity_t *e1, *e2;
 451         request_log_entry_t *rlp;
 452 
 453         if (id1 == id2)
 454                 return (REP_PROTOCOL_FAIL_DUPLICATE_ID);
 455 
 456         (void) pthread_mutex_lock(&cp->rc_lock);
 457         e1 = uu_avl_find(cp->rc_entities, &id1, NULL, NULL);
 458         e2 = uu_avl_find(cp->rc_entities, &id2, NULL, NULL);
 459         if (e1 == NULL || e2 == NULL) {
 460                 (void) pthread_mutex_unlock(&cp->rc_lock);
 461                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
 462         }
 463 
 464         assert(e1 != e2);
 465 
 466         /*
 467          * locks are ordered by id number
 468          */
 469         if (id1 < id2) {
 470                 (void) pthread_mutex_lock(&e1->re_lock);
 471                 (void) pthread_mutex_lock(&e2->re_lock);
 472         } else {
 473                 (void) pthread_mutex_lock(&e2->re_lock);
 474                 (void) pthread_mutex_lock(&e1->re_lock);
 475         }
 476         *out1 = e1;
 477         *out2 = e2;
 478 
 479         (void) pthread_mutex_unlock(&cp->rc_lock);
 480 
 481         if ((rlp = get_log()) != NULL) {
 482                 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, id1, e1);
 483                 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, id2, e2);
 484         }
 485 
 486         return (REP_PROTOCOL_SUCCESS);
 487 }
 488 
 489 static void
 490 entity_release(repcache_entity_t *ep)
 491 {
 492         assert(ep->re_node.rnp_node == NULL ||
 493             !MUTEX_HELD(&ep->re_node.rnp_node->rn_lock));
 494         (void) pthread_mutex_unlock(&ep->re_lock);
 495 }
 496 
 497 static void
 498 entity_destroy(repcache_entity_t *entity)
 499 {
 500         (void) pthread_mutex_lock(&entity->re_lock);
 501         rc_node_clear(&entity->re_node, 0);
 502         (void) pthread_mutex_unlock(&entity->re_lock);
 503 
 504         uu_avl_node_fini(entity, &entity->re_link, entity_pool);
 505         (void) pthread_mutex_destroy(&entity->re_lock);
 506         rc_node_ptr_free_mem(&entity->re_node);
 507         uu_free(entity);
 508 }
 509 
 510 static void
 511 entity_remove(repcache_client_t *cp, uint32_t id)
 512 {
 513         repcache_entity_t *entity;
 514 
 515         (void) pthread_mutex_lock(&cp->rc_lock);
 516         entity = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
 517         if (entity != NULL) {
 518                 add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, entity);
 519 
 520                 uu_avl_remove(cp->rc_entities, entity);
 521         }
 522         (void) pthread_mutex_unlock(&cp->rc_lock);
 523 
 524         if (entity != NULL)
 525                 entity_destroy(entity);
 526 }
 527 
 528 static void
 529 entity_cleanup(repcache_client_t *cp)
 530 {
 531         repcache_entity_t *ep;
 532         void *cookie = NULL;
 533 
 534         (void) pthread_mutex_lock(&cp->rc_lock);
 535         while ((ep = uu_avl_teardown(cp->rc_entities, &cookie)) != NULL) {
 536                 (void) pthread_mutex_unlock(&cp->rc_lock);
 537                 entity_destroy(ep);
 538                 (void) pthread_mutex_lock(&cp->rc_lock);
 539         }
 540         (void) pthread_mutex_unlock(&cp->rc_lock);
 541 }
 542 
 543 /*ARGSUSED*/
 544 static repcache_iter_t *
 545 iter_alloc(repcache_client_t *cp)
 546 {
 547         repcache_iter_t *iter;
 548         iter = uu_zalloc(sizeof (repcache_iter_t));
 549         if (iter != NULL)
 550                 uu_avl_node_init(iter, &iter->ri_link, iter_pool);
 551         return (iter);
 552 }
 553 
 554 static void
 555 iter_add(repcache_client_t *cp, repcache_iter_t *iter)
 556 {
 557         uu_list_index_t idx;
 558 
 559         (void) pthread_mutex_lock(&cp->rc_lock);
 560         assert(cp->rc_insert_thr == pthread_self());
 561 
 562         (void) uu_avl_find(cp->rc_iters, iter, NULL, &idx);
 563         uu_avl_insert(cp->rc_iters, iter, idx);
 564 
 565         (void) pthread_mutex_unlock(&cp->rc_lock);
 566 }
 567 
 568 static repcache_iter_t *
 569 iter_find(repcache_client_t *cp, uint32_t id)
 570 {
 571         repcache_iter_t *iter;
 572 
 573         (void) pthread_mutex_lock(&cp->rc_lock);
 574 
 575         iter = uu_avl_find(cp->rc_iters, &id, NULL, NULL);
 576         if (iter != NULL) {
 577                 add_log_ptr(get_log(), RC_PTR_TYPE_ITER, id, iter);
 578                 (void) pthread_mutex_lock(&iter->ri_lock);
 579         }
 580         (void) pthread_mutex_unlock(&cp->rc_lock);
 581 
 582         return (iter);
 583 }
 584 
 585 /*
 586  * Fails with
 587  *   _UNKNOWN_ID - iter_id or entity_id does not designate an active register
 588  */
 589 static int
 590 iter_find_w_entity(repcache_client_t *cp, uint32_t iter_id,
 591     repcache_iter_t **iterp, uint32_t entity_id, repcache_entity_t **epp)
 592 {
 593         repcache_iter_t *iter;
 594         repcache_entity_t *ep;
 595         request_log_entry_t *rlp;
 596 
 597         (void) pthread_mutex_lock(&cp->rc_lock);
 598         iter = uu_avl_find(cp->rc_iters, &iter_id, NULL, NULL);
 599         ep = uu_avl_find(cp->rc_entities, &entity_id, NULL, NULL);
 600 
 601         assert(iter == NULL || !MUTEX_HELD(&iter->ri_lock));
 602         assert(ep == NULL || !MUTEX_HELD(&ep->re_lock));
 603 
 604         if (iter == NULL || ep == NULL) {
 605                 (void) pthread_mutex_unlock(&cp->rc_lock);
 606                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
 607         }
 608 
 609         (void) pthread_mutex_lock(&iter->ri_lock);
 610         (void) pthread_mutex_lock(&ep->re_lock);
 611 
 612         (void) pthread_mutex_unlock(&cp->rc_lock);
 613 
 614         *iterp = iter;
 615         *epp = ep;
 616 
 617         if ((rlp = get_log()) != NULL) {
 618                 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, entity_id, ep);
 619                 add_log_ptr(rlp, RC_PTR_TYPE_ITER, iter_id, iter);
 620         }
 621 
 622         return (REP_PROTOCOL_SUCCESS);
 623 }
 624 
 625 static void
 626 iter_release(repcache_iter_t *iter)
 627 {
 628         (void) pthread_mutex_unlock(&iter->ri_lock);
 629 }
 630 
 631 static void
 632 iter_destroy(repcache_iter_t *iter)
 633 {
 634         (void) pthread_mutex_lock(&iter->ri_lock);
 635         rc_iter_destroy(&iter->ri_iter);
 636         (void) pthread_mutex_unlock(&iter->ri_lock);
 637 
 638         uu_avl_node_fini(iter, &iter->ri_link, iter_pool);
 639         (void) pthread_mutex_destroy(&iter->ri_lock);
 640         uu_free(iter);
 641 }
 642 
 643 static void
 644 iter_remove(repcache_client_t *cp, uint32_t id)
 645 {
 646         repcache_iter_t *iter;
 647 
 648         (void) pthread_mutex_lock(&cp->rc_lock);
 649         iter = uu_avl_find(cp->rc_iters, &id, NULL, NULL);
 650         if (iter != NULL)
 651                 uu_avl_remove(cp->rc_iters, iter);
 652         (void) pthread_mutex_unlock(&cp->rc_lock);
 653 
 654         if (iter != NULL)
 655                 iter_destroy(iter);
 656 }
 657 
 658 static void
 659 iter_cleanup(repcache_client_t *cp)
 660 {
 661         repcache_iter_t *iter;
 662         void *cookie = NULL;
 663 
 664         (void) pthread_mutex_lock(&cp->rc_lock);
 665         while ((iter = uu_avl_teardown(cp->rc_iters, &cookie)) != NULL) {
 666                 (void) pthread_mutex_unlock(&cp->rc_lock);
 667                 iter_destroy(iter);
 668                 (void) pthread_mutex_lock(&cp->rc_lock);
 669         }
 670         (void) pthread_mutex_unlock(&cp->rc_lock);
 671 }
 672 
 673 /*
 674  * Ensure that the passed client id is no longer usable, wait for any
 675  * outstanding invocations to complete, then destroy the client
 676  * structure.
 677  */
 678 static void
 679 client_destroy(uint32_t id)
 680 {
 681         client_bucket_t *bp = CLIENT_HASH(id);
 682         repcache_client_t *cp;
 683 
 684         (void) pthread_mutex_lock(&bp->cb_lock);
 685 
 686         cp = uu_list_find(bp->cb_list, &id, NULL, NULL);
 687 
 688         if (cp == NULL) {
 689                 (void) pthread_mutex_unlock(&bp->cb_lock);
 690                 return;
 691         }
 692 
 693         uu_list_remove(bp->cb_list, cp);
 694 
 695         (void) pthread_mutex_unlock(&bp->cb_lock);
 696 
 697         /* kick the waiters out */
 698         rc_notify_info_fini(&cp->rc_notify_info);
 699 
 700         (void) pthread_mutex_lock(&cp->rc_lock);
 701         assert(!(cp->rc_flags & RC_CLIENT_DEAD));
 702         cp->rc_flags |= RC_CLIENT_DEAD;
 703 
 704         if (cp->rc_doorfd != -1) {
 705                 if (door_revoke(cp->rc_doorfd) < 0)
 706                         perror("door_revoke");
 707                 cp->rc_doorfd = -1;
 708                 cp->rc_doorid = INVALID_DOORID;
 709         }
 710 
 711         while (cp->rc_refcnt > 0)
 712                 (void) pthread_cond_wait(&cp->rc_cv, &cp->rc_lock);
 713 
 714         assert(cp->rc_insert_thr == 0 && cp->rc_notify_thr == 0);
 715         (void) pthread_mutex_unlock(&cp->rc_lock);
 716 
 717         /*
 718          * destroy outstanding objects
 719          */
 720         entity_cleanup(cp);
 721         iter_cleanup(cp);
 722 
 723         /*
 724          * clean up notifications
 725          */
 726         rc_pg_notify_fini(&cp->rc_pg_notify);
 727 
 728         /*
 729          * clean up annotations
 730          */
 731         if (cp->rc_operation != NULL)
 732                 free((void *)cp->rc_operation);
 733         if (cp->rc_file != NULL)
 734                 free((void *)cp->rc_file);
 735 
 736         /*
 737          * End audit session.
 738          */
 739 #ifndef NATIVE_BUILD
 740         (void) adt_end_session(cp->rc_adt_session);
 741 #endif
 742 
 743         client_free(cp);
 744 }
 745 
 746 /*
 747  * Fails with
 748  *   _TYPE_MISMATCH - the entity is already set up with a different type
 749  *   _NO_RESOURCES - out of memory
 750  */
 751 static int
 752 entity_setup(repcache_client_t *cp, struct rep_protocol_entity_setup *rpr)
 753 {
 754         repcache_entity_t *ep;
 755         uint32_t type;
 756 
 757         client_start_insert(cp);
 758 
 759         if ((ep = entity_find(cp, rpr->rpr_entityid)) != NULL) {
 760                 type = ep->re_type;
 761                 entity_release(ep);
 762 
 763                 client_end_insert(cp);
 764 
 765                 if (type != rpr->rpr_entitytype)
 766                         return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
 767                 return (REP_PROTOCOL_SUCCESS);
 768         }
 769 
 770         switch (type = rpr->rpr_entitytype) {
 771         case REP_PROTOCOL_ENTITY_SCOPE:
 772         case REP_PROTOCOL_ENTITY_SERVICE:
 773         case REP_PROTOCOL_ENTITY_INSTANCE:
 774         case REP_PROTOCOL_ENTITY_SNAPSHOT:
 775         case REP_PROTOCOL_ENTITY_SNAPLEVEL:
 776         case REP_PROTOCOL_ENTITY_PROPERTYGRP:
 777         case REP_PROTOCOL_ENTITY_PROPERTY:
 778                 break;
 779         default:
 780                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
 781         }
 782 
 783         ep = entity_alloc(cp);
 784         if (ep == NULL) {
 785                 client_end_insert(cp);
 786                 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
 787         }
 788 
 789         ep->re_id = rpr->rpr_entityid;
 790         ep->re_changeid = INVALID_CHANGEID;
 791 
 792         ep->re_type = type;
 793         rc_node_ptr_init(&ep->re_node);
 794 
 795         entity_add(cp, ep);
 796         client_end_insert(cp);
 797         return (REP_PROTOCOL_SUCCESS);
 798 }
 799 
 800 /*ARGSUSED*/
 801 static void
 802 entity_name(repcache_client_t *cp, const void *in, size_t insz, void *out_arg,
 803     size_t *outsz, void *arg)
 804 {
 805         const struct rep_protocol_entity_name *rpr = in;
 806         struct rep_protocol_name_response *out = out_arg;
 807         repcache_entity_t *ep;
 808         size_t sz = sizeof (out->rpr_name);
 809 
 810         assert(*outsz == sizeof (*out));
 811 
 812         ep = entity_find(cp, rpr->rpr_entityid);
 813 
 814         if (ep == NULL) {
 815                 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
 816                 *outsz = sizeof (out->rpr_response);
 817                 return;
 818         }
 819         out->rpr_response = rc_node_name(&ep->re_node, out->rpr_name,
 820             sz, rpr->rpr_answertype, &sz);
 821         entity_release(ep);
 822 
 823         /*
 824          * If we fail, we only return the response code.
 825          * If we succeed, we don't return anything after the '\0' in rpr_name.
 826          */
 827         if (out->rpr_response != REP_PROTOCOL_SUCCESS)
 828                 *outsz = sizeof (out->rpr_response);
 829         else
 830                 *outsz = offsetof(struct rep_protocol_name_response,
 831                     rpr_name[sz + 1]);
 832 }
 833 
 834 /*ARGSUSED*/
 835 static void
 836 entity_parent_type(repcache_client_t *cp, const void *in, size_t insz,
 837     void *out_arg, size_t *outsz, void *arg)
 838 {
 839         const struct rep_protocol_entity_name *rpr = in;
 840         struct rep_protocol_integer_response *out = out_arg;
 841         repcache_entity_t *ep;
 842 
 843         assert(*outsz == sizeof (*out));
 844 
 845         ep = entity_find(cp, rpr->rpr_entityid);
 846 
 847         if (ep == NULL) {
 848                 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
 849                 *outsz = sizeof (out->rpr_response);
 850                 return;
 851         }
 852 
 853         out->rpr_response = rc_node_parent_type(&ep->re_node, &out->rpr_value);
 854         entity_release(ep);
 855 
 856         if (out->rpr_response != REP_PROTOCOL_SUCCESS)
 857                 *outsz = sizeof (out->rpr_response);
 858 }
 859 
 860 /*
 861  * Fails with
 862  *   _DUPLICATE_ID - the ids are equal
 863  *   _UNKNOWN_ID - an id does not designate an active register
 864  *   _INVALID_TYPE - type is invalid
 865  *   _TYPE_MISMATCH - np doesn't carry children of type type
 866  *   _DELETED - np has been deleted
 867  *   _NOT_FOUND - no child with that name/type combo found
 868  *   _NO_RESOURCES
 869  *   _BACKEND_ACCESS
 870  */
 871 static int
 872 entity_get_child(repcache_client_t *cp,
 873     struct rep_protocol_entity_get_child *rpr)
 874 {
 875         repcache_entity_t *parent, *child;
 876         int result;
 877 
 878         uint32_t parentid = rpr->rpr_entityid;
 879         uint32_t childid = rpr->rpr_childid;
 880 
 881         result = entity_find2(cp, childid, &child, parentid, &parent);
 882         if (result != REP_PROTOCOL_SUCCESS)
 883                 return (result);
 884 
 885         rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
 886 
 887         result = rc_node_get_child(&parent->re_node, rpr->rpr_name,
 888             child->re_type, &child->re_node);
 889 
 890         entity_release(child);
 891         entity_release(parent);
 892 
 893         return (result);
 894 }
 895 
 896 /*
 897  * Returns _FAIL_DUPLICATE_ID, _FAIL_UNKNOWN_ID, _FAIL_NOT_SET, _FAIL_DELETED,
 898  * _FAIL_TYPE_MISMATCH, _FAIL_NOT_FOUND (scope has no parent), or _SUCCESS.
 899  * Fails with
 900  *   _DUPLICATE_ID - the ids are equal
 901  *   _UNKNOWN_ID - an id does not designate an active register
 902  *   _NOT_SET - child is not set
 903  *   _DELETED - child has been deleted
 904  *   _TYPE_MISMATCH - child's parent does not match that of the parent register
 905  *   _NOT_FOUND - child has no parent (and is a scope)
 906  */
 907 static int
 908 entity_get_parent(repcache_client_t *cp, struct rep_protocol_entity_parent *rpr)
 909 {
 910         repcache_entity_t *child, *parent;
 911         int result;
 912 
 913         uint32_t childid = rpr->rpr_entityid;
 914         uint32_t outid = rpr->rpr_outid;
 915 
 916         result = entity_find2(cp, childid, &child, outid, &parent);
 917         if (result != REP_PROTOCOL_SUCCESS)
 918                 return (result);
 919 
 920         result = rc_node_get_parent(&child->re_node, parent->re_type,
 921             &parent->re_node);
 922 
 923         entity_release(child);
 924         entity_release(parent);
 925 
 926         return (result);
 927 }
 928 
 929 static int
 930 entity_get(repcache_client_t *cp, struct rep_protocol_entity_get *rpr)
 931 {
 932         repcache_entity_t *ep;
 933         int result;
 934 
 935         ep = entity_find(cp, rpr->rpr_entityid);
 936 
 937         if (ep == NULL)
 938                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
 939 
 940         switch (rpr->rpr_object) {
 941         case RP_ENTITY_GET_INVALIDATE:
 942                 rc_node_clear(&ep->re_node, 0);
 943                 result = REP_PROTOCOL_SUCCESS;
 944                 break;
 945         case RP_ENTITY_GET_MOST_LOCAL_SCOPE:
 946                 result = rc_local_scope(ep->re_type, &ep->re_node);
 947                 break;
 948         default:
 949                 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
 950                 break;
 951         }
 952 
 953         entity_release(ep);
 954 
 955         return (result);
 956 }
 957 
 958 static int
 959 entity_update(repcache_client_t *cp, struct rep_protocol_entity_update *rpr)
 960 {
 961         repcache_entity_t *ep;
 962         int result;
 963 
 964         if (rpr->rpr_changeid == INVALID_CHANGEID)
 965                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
 966 
 967         ep = entity_find(cp, rpr->rpr_entityid);
 968 
 969         if (ep == NULL)
 970                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
 971 
 972         if (ep->re_changeid == rpr->rpr_changeid) {
 973                 result = REP_PROTOCOL_DONE;
 974         } else {
 975                 result = rc_node_update(&ep->re_node);
 976                 if (result == REP_PROTOCOL_DONE)
 977                         ep->re_changeid = rpr->rpr_changeid;
 978         }
 979 
 980         entity_release(ep);
 981 
 982         return (result);
 983 }
 984 
 985 static int
 986 entity_reset(repcache_client_t *cp, struct rep_protocol_entity_reset *rpr)
 987 {
 988         repcache_entity_t *ep;
 989 
 990         ep = entity_find(cp, rpr->rpr_entityid);
 991         if (ep == NULL)
 992                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
 993 
 994         rc_node_clear(&ep->re_node, 0);
 995         ep->re_txstate = REPCACHE_TX_INIT;
 996 
 997         entity_release(ep);
 998         return (REP_PROTOCOL_SUCCESS);
 999 }
1000 
1001 /*
1002  * Fails with
1003  *   _BAD_REQUEST - request has invalid changeid
1004  *                  rpr_name is invalid
1005  *                  cannot create children for parent's type of node
1006  *   _DUPLICATE_ID - request has duplicate ids
1007  *   _UNKNOWN_ID - request has unknown id
1008  *   _DELETED - parent has been deleted
1009  *   _NOT_SET - parent is reset
1010  *   _NOT_APPLICABLE - rpr_childtype is _PROPERTYGRP
1011  *   _INVALID_TYPE - parent is corrupt or rpr_childtype is invalid
1012  *   _TYPE_MISMATCH - parent cannot have children of type rpr_childtype
1013  *   _NO_RESOURCES
1014  *   _PERMISSION_DENIED
1015  *   _BACKEND_ACCESS
1016  *   _BACKEND_READONLY
1017  *   _EXISTS - child already exists
1018  */
1019 static int
1020 entity_create_child(repcache_client_t *cp,
1021     struct rep_protocol_entity_create_child *rpr)
1022 {
1023         repcache_entity_t *parent;
1024         repcache_entity_t *child;
1025 
1026         uint32_t parentid = rpr->rpr_entityid;
1027         uint32_t childid = rpr->rpr_childid;
1028 
1029         int result;
1030 
1031         if (rpr->rpr_changeid == INVALID_CHANGEID)
1032                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1033 
1034         result = entity_find2(cp, parentid, &parent, childid, &child);
1035         if (result != REP_PROTOCOL_SUCCESS)
1036                 return (result);
1037 
1038         rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1039 
1040         if (child->re_changeid == rpr->rpr_changeid) {
1041                 result = REP_PROTOCOL_SUCCESS;
1042         } else {
1043                 result = rc_node_create_child(&parent->re_node,
1044                     rpr->rpr_childtype, rpr->rpr_name, &child->re_node);
1045                 if (result == REP_PROTOCOL_SUCCESS)
1046                         child->re_changeid = rpr->rpr_changeid;
1047         }
1048 
1049         entity_release(parent);
1050         entity_release(child);
1051 
1052         return (result);
1053 }
1054 
1055 static int
1056 entity_create_pg(repcache_client_t *cp,
1057     struct rep_protocol_entity_create_pg *rpr)
1058 {
1059         repcache_entity_t *parent;
1060         repcache_entity_t *child;
1061 
1062         uint32_t parentid = rpr->rpr_entityid;
1063         uint32_t childid = rpr->rpr_childid;
1064 
1065         int result;
1066 
1067         if (rpr->rpr_changeid == INVALID_CHANGEID)
1068                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1069 
1070         result = entity_find2(cp, parentid, &parent, childid, &child);
1071         if (result != REP_PROTOCOL_SUCCESS)
1072                 return (result);
1073 
1074         rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1075         rpr->rpr_type[sizeof (rpr->rpr_type) - 1] = 0;
1076 
1077         if (child->re_changeid == rpr->rpr_changeid) {
1078                 result = REP_PROTOCOL_SUCCESS;
1079         } else {
1080                 result = rc_node_create_child_pg(&parent->re_node,
1081                     child->re_type, rpr->rpr_name, rpr->rpr_type,
1082                     rpr->rpr_flags, &child->re_node);
1083                 if (result == REP_PROTOCOL_SUCCESS)
1084                         child->re_changeid = rpr->rpr_changeid;
1085         }
1086 
1087         entity_release(parent);
1088         entity_release(child);
1089 
1090         return (result);
1091 }
1092 
1093 static int
1094 entity_delete(repcache_client_t *cp,
1095     struct rep_protocol_entity_delete *rpr)
1096 {
1097         repcache_entity_t *entity;
1098 
1099         uint32_t entityid = rpr->rpr_entityid;
1100 
1101         int result;
1102 
1103         if (rpr->rpr_changeid == INVALID_CHANGEID)
1104                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1105 
1106         entity = entity_find(cp, entityid);
1107 
1108         if (entity == NULL)
1109                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1110 
1111         if (entity->re_changeid == rpr->rpr_changeid) {
1112                 result = REP_PROTOCOL_SUCCESS;
1113         } else {
1114                 result = rc_node_delete(&entity->re_node);
1115                 if (result == REP_PROTOCOL_SUCCESS)
1116                         entity->re_changeid = rpr->rpr_changeid;
1117         }
1118 
1119         entity_release(entity);
1120 
1121         return (result);
1122 }
1123 
1124 static rep_protocol_responseid_t
1125 entity_teardown(repcache_client_t *cp, struct rep_protocol_entity_teardown *rpr)
1126 {
1127         entity_remove(cp, rpr->rpr_entityid);
1128 
1129         return (REP_PROTOCOL_SUCCESS);
1130 }
1131 
1132 /*
1133  * Fails with
1134  *   _MISORDERED - the iterator exists and is not reset
1135  *   _NO_RESOURCES - out of memory
1136  */
1137 static int
1138 iter_setup(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1139 {
1140         repcache_iter_t *iter;
1141         uint32_t sequence;
1142 
1143         client_start_insert(cp);
1144         /*
1145          * If the iter already exists, and hasn't been read from,
1146          * we assume the previous call succeeded.
1147          */
1148         if ((iter = iter_find(cp, rpr->rpr_iterid)) != NULL) {
1149                 sequence = iter->ri_sequence;
1150                 iter_release(iter);
1151 
1152                 client_end_insert(cp);
1153 
1154                 if (sequence != 0)
1155                         return (REP_PROTOCOL_FAIL_MISORDERED);
1156                 return (REP_PROTOCOL_SUCCESS);
1157         }
1158 
1159         iter = iter_alloc(cp);
1160         if (iter == NULL) {
1161                 client_end_insert(cp);
1162                 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1163         }
1164 
1165         iter->ri_id = rpr->rpr_iterid;
1166         iter->ri_type = REP_PROTOCOL_TYPE_INVALID;
1167         iter->ri_sequence = 0;
1168         iter_add(cp, iter);
1169 
1170         client_end_insert(cp);
1171         return (REP_PROTOCOL_SUCCESS);
1172 }
1173 
1174 /*
1175  * Fails with
1176  *   _UNKNOWN_ID
1177  *   _MISORDERED - iterator has already been started
1178  *   _NOT_SET
1179  *   _DELETED
1180  *   _TYPE_MISMATCH - entity cannot have type children
1181  *   _BAD_REQUEST - rpr_flags is invalid
1182  *                  rpr_pattern is invalid
1183  *   _NO_RESOURCES
1184  *   _INVALID_TYPE
1185  *   _BACKEND_ACCESS
1186  */
1187 static int
1188 iter_start(repcache_client_t *cp, struct rep_protocol_iter_start *rpr)
1189 {
1190         int result;
1191         repcache_iter_t *iter;
1192         repcache_entity_t *ep;
1193 
1194         result = iter_find_w_entity(cp, rpr->rpr_iterid, &iter,
1195             rpr->rpr_entity, &ep);
1196 
1197         if (result != REP_PROTOCOL_SUCCESS)
1198                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1199 
1200         if (iter->ri_sequence > 1) {
1201                 result = REP_PROTOCOL_FAIL_MISORDERED;
1202                 goto end;
1203         }
1204 
1205         if (iter->ri_sequence == 1) {
1206                 result = REP_PROTOCOL_SUCCESS;
1207                 goto end;
1208         }
1209 
1210         rpr->rpr_pattern[sizeof (rpr->rpr_pattern) - 1] = 0;
1211 
1212         result = rc_node_setup_iter(&ep->re_node, &iter->ri_iter,
1213             rpr->rpr_itertype, rpr->rpr_flags, rpr->rpr_pattern);
1214 
1215         if (result == REP_PROTOCOL_SUCCESS)
1216                 iter->ri_sequence++;
1217 
1218 end:
1219         iter_release(iter);
1220         entity_release(ep);
1221         return (result);
1222 }
1223 
1224 /*
1225  * Returns
1226  *   _UNKNOWN_ID
1227  *   _NOT_SET - iter has not been started
1228  *   _MISORDERED
1229  *   _BAD_REQUEST - iter walks values
1230  *   _TYPE_MISMATCH - iter does not walk type entities
1231  *   _DELETED - parent was deleted
1232  *   _NO_RESOURCES
1233  *   _INVALID_TYPE - type is invalid
1234  *   _DONE
1235  *   _SUCCESS
1236  *
1237  * For composed property group iterators, can also return
1238  *   _TYPE_MISMATCH - parent cannot have type children
1239  *   _BACKEND_ACCESS
1240  */
1241 static rep_protocol_responseid_t
1242 iter_read(repcache_client_t *cp, struct rep_protocol_iter_read *rpr)
1243 {
1244         rep_protocol_responseid_t result;
1245         repcache_iter_t *iter;
1246         repcache_entity_t *ep;
1247         uint32_t sequence;
1248 
1249         result = iter_find_w_entity(cp, rpr->rpr_iterid, &iter,
1250             rpr->rpr_entityid, &ep);
1251 
1252         if (result != REP_PROTOCOL_SUCCESS)
1253                 return (result);
1254 
1255         sequence = rpr->rpr_sequence;
1256 
1257         if (iter->ri_sequence == 0) {
1258                 iter_release(iter);
1259                 entity_release(ep);
1260                 return (REP_PROTOCOL_FAIL_NOT_SET);
1261         }
1262 
1263         if (sequence == 1) {
1264                 iter_release(iter);
1265                 entity_release(ep);
1266                 return (REP_PROTOCOL_FAIL_MISORDERED);
1267         }
1268 
1269         if (sequence == iter->ri_sequence) {
1270                 iter_release(iter);
1271                 entity_release(ep);
1272                 return (REP_PROTOCOL_SUCCESS);
1273         }
1274 
1275         if (sequence == iter->ri_sequence + 1) {
1276                 result = rc_iter_next(iter->ri_iter, &ep->re_node,
1277                     ep->re_type);
1278 
1279                 if (result == REP_PROTOCOL_SUCCESS)
1280                         iter->ri_sequence++;
1281 
1282                 iter_release(iter);
1283                 entity_release(ep);
1284 
1285                 return (result);
1286         }
1287 
1288         iter_release(iter);
1289         entity_release(ep);
1290         return (REP_PROTOCOL_FAIL_MISORDERED);
1291 }
1292 
1293 /*ARGSUSED*/
1294 static void
1295 iter_read_value(repcache_client_t *cp, const void *in, size_t insz,
1296     void *out_arg, size_t *outsz, void *arg)
1297 {
1298         const struct rep_protocol_iter_read_value *rpr = in;
1299         struct rep_protocol_value_response *out = out_arg;
1300         rep_protocol_responseid_t result;
1301 
1302         repcache_iter_t *iter;
1303         uint32_t sequence;
1304         int repeat;
1305 
1306         assert(*outsz == sizeof (*out));
1307 
1308         iter = iter_find(cp, rpr->rpr_iterid);
1309 
1310         if (iter == NULL) {
1311                 result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1312                 goto out;
1313         }
1314 
1315         sequence = rpr->rpr_sequence;
1316 
1317         if (iter->ri_sequence == 0) {
1318                 iter_release(iter);
1319                 result = REP_PROTOCOL_FAIL_NOT_SET;
1320                 goto out;
1321         }
1322 
1323         repeat = (sequence == iter->ri_sequence);
1324 
1325         if (sequence == 1 || (!repeat && sequence != iter->ri_sequence + 1)) {
1326                 iter_release(iter);
1327                 result = REP_PROTOCOL_FAIL_MISORDERED;
1328                 goto out;
1329         }
1330 
1331         result = rc_iter_next_value(iter->ri_iter, out, outsz, repeat);
1332 
1333         if (!repeat && result == REP_PROTOCOL_SUCCESS)
1334                 iter->ri_sequence++;
1335 
1336         iter_release(iter);
1337 
1338 out:
1339         /*
1340          * If we fail, we only return the response code.
1341          * If we succeed, rc_iter_next_value has shortened *outsz
1342          * to only include the value bytes needed.
1343          */
1344         if (result != REP_PROTOCOL_SUCCESS && result != REP_PROTOCOL_DONE)
1345                 *outsz = sizeof (out->rpr_response);
1346 
1347         out->rpr_response = result;
1348 }
1349 
1350 static int
1351 iter_reset(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1352 {
1353         repcache_iter_t *iter = iter_find(cp, rpr->rpr_iterid);
1354 
1355         if (iter == NULL)
1356                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1357 
1358         if (iter->ri_sequence != 0) {
1359                 iter->ri_sequence = 0;
1360                 rc_iter_destroy(&iter->ri_iter);
1361         }
1362         iter_release(iter);
1363         return (REP_PROTOCOL_SUCCESS);
1364 }
1365 
1366 static rep_protocol_responseid_t
1367 iter_teardown(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1368 {
1369         iter_remove(cp, rpr->rpr_iterid);
1370 
1371         return (REP_PROTOCOL_SUCCESS);
1372 }
1373 
1374 static rep_protocol_responseid_t
1375 tx_start(repcache_client_t *cp, struct rep_protocol_transaction_start *rpr)
1376 {
1377         repcache_entity_t *tx;
1378         repcache_entity_t *ep;
1379         rep_protocol_responseid_t result;
1380 
1381         uint32_t txid = rpr->rpr_entityid_tx;
1382         uint32_t epid = rpr->rpr_entityid;
1383 
1384         result = entity_find2(cp, txid, &tx, epid, &ep);
1385         if (result != REP_PROTOCOL_SUCCESS)
1386                 return (result);
1387 
1388         if (tx->re_txstate == REPCACHE_TX_SETUP) {
1389                 result = REP_PROTOCOL_SUCCESS;
1390                 goto end;
1391         }
1392         if (tx->re_txstate != REPCACHE_TX_INIT) {
1393                 result = REP_PROTOCOL_FAIL_MISORDERED;
1394                 goto end;
1395         }
1396 
1397         result = rc_node_setup_tx(&ep->re_node, &tx->re_node);
1398 
1399 end:
1400         if (result == REP_PROTOCOL_SUCCESS)
1401                 tx->re_txstate = REPCACHE_TX_SETUP;
1402         else
1403                 rc_node_clear(&tx->re_node, 0);
1404 
1405         entity_release(ep);
1406         entity_release(tx);
1407         return (result);
1408 }
1409 
1410 /*ARGSUSED*/
1411 static void
1412 tx_commit(repcache_client_t *cp, const void *in, size_t insz,
1413     void *out_arg, size_t *outsz, void *arg)
1414 {
1415         struct rep_protocol_response *out = out_arg;
1416         const struct rep_protocol_transaction_commit *rpr = in;
1417         repcache_entity_t *tx;
1418 
1419         assert(*outsz == sizeof (*out));
1420         assert(insz >= REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE);
1421 
1422         if (rpr->rpr_size != insz) {
1423                 out->rpr_response = REP_PROTOCOL_FAIL_BAD_REQUEST;
1424                 return;
1425         }
1426 
1427         tx = entity_find(cp, rpr->rpr_entityid);
1428 
1429         if (tx == NULL) {
1430                 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1431                 return;
1432         }
1433 
1434         switch (tx->re_txstate) {
1435         case REPCACHE_TX_INIT:
1436                 out->rpr_response = REP_PROTOCOL_FAIL_MISORDERED;
1437                 break;
1438 
1439         case REPCACHE_TX_SETUP:
1440                 out->rpr_response = rc_tx_commit(&tx->re_node, rpr->rpr_cmd,
1441                     insz - REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE);
1442 
1443                 if (out->rpr_response == REP_PROTOCOL_SUCCESS) {
1444                         tx->re_txstate = REPCACHE_TX_COMMITTED;
1445                         rc_node_clear(&tx->re_node, 0);
1446                 }
1447 
1448                 break;
1449         case REPCACHE_TX_COMMITTED:
1450                 out->rpr_response = REP_PROTOCOL_SUCCESS;
1451                 break;
1452         default:
1453                 assert(0);      /* CAN'T HAPPEN */
1454                 break;
1455         }
1456 
1457         entity_release(tx);
1458 }
1459 
1460 static rep_protocol_responseid_t
1461 next_snaplevel(repcache_client_t *cp, struct rep_protocol_entity_pair *rpr)
1462 {
1463         repcache_entity_t *src;
1464         repcache_entity_t *dest;
1465 
1466         uint32_t srcid = rpr->rpr_entity_src;
1467         uint32_t destid = rpr->rpr_entity_dst;
1468 
1469         int result;
1470 
1471         result = entity_find2(cp, srcid, &src, destid, &dest);
1472         if (result != REP_PROTOCOL_SUCCESS)
1473                 return (result);
1474 
1475         result = rc_node_next_snaplevel(&src->re_node, &dest->re_node);
1476 
1477         entity_release(src);
1478         entity_release(dest);
1479 
1480         return (result);
1481 }
1482 
1483 static rep_protocol_responseid_t
1484 snapshot_take(repcache_client_t *cp, struct rep_protocol_snapshot_take *rpr)
1485 {
1486         repcache_entity_t *src;
1487         uint32_t srcid = rpr->rpr_entityid_src;
1488         repcache_entity_t *dest;
1489         uint32_t destid = rpr->rpr_entityid_dest;
1490 
1491         int result;
1492 
1493         result = entity_find2(cp, srcid, &src, destid, &dest);
1494         if (result != REP_PROTOCOL_SUCCESS)
1495                 return (result);
1496 
1497         if (dest->re_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
1498                 result = REP_PROTOCOL_FAIL_TYPE_MISMATCH;
1499         } else {
1500                 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1501 
1502                 if (rpr->rpr_flags == REP_SNAPSHOT_NEW)
1503                         result = rc_snapshot_take_new(&src->re_node, NULL,
1504                             NULL, rpr->rpr_name, &dest->re_node);
1505                 else if (rpr->rpr_flags == REP_SNAPSHOT_ATTACH &&
1506                     rpr->rpr_name[0] == 0)
1507                         result = rc_snapshot_take_attach(&src->re_node,
1508                             &dest->re_node);
1509                 else
1510                         result = REP_PROTOCOL_FAIL_BAD_REQUEST;
1511         }
1512         entity_release(src);
1513         entity_release(dest);
1514 
1515         return (result);
1516 }
1517 
1518 static rep_protocol_responseid_t
1519 snapshot_take_named(repcache_client_t *cp,
1520     struct rep_protocol_snapshot_take_named *rpr)
1521 {
1522         repcache_entity_t *src;
1523         uint32_t srcid = rpr->rpr_entityid_src;
1524         repcache_entity_t *dest;
1525         uint32_t destid = rpr->rpr_entityid_dest;
1526 
1527         int result;
1528 
1529         result = entity_find2(cp, srcid, &src, destid, &dest);
1530         if (result != REP_PROTOCOL_SUCCESS)
1531                 return (result);
1532 
1533         if (dest->re_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
1534                 result = REP_PROTOCOL_FAIL_TYPE_MISMATCH;
1535         } else {
1536                 rpr->rpr_svcname[sizeof (rpr->rpr_svcname) - 1] = 0;
1537                 rpr->rpr_instname[sizeof (rpr->rpr_instname) - 1] = 0;
1538                 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1539 
1540                 result = rc_snapshot_take_new(&src->re_node, rpr->rpr_svcname,
1541                     rpr->rpr_instname, rpr->rpr_name, &dest->re_node);
1542         }
1543         entity_release(src);
1544         entity_release(dest);
1545 
1546         return (result);
1547 }
1548 
1549 static rep_protocol_responseid_t
1550 snapshot_attach(repcache_client_t *cp, struct rep_protocol_snapshot_attach *rpr)
1551 {
1552         repcache_entity_t *src;
1553         uint32_t srcid = rpr->rpr_entityid_src;
1554         repcache_entity_t *dest;
1555         uint32_t destid = rpr->rpr_entityid_dest;
1556 
1557         int result;
1558 
1559         result = entity_find2(cp, srcid, &src, destid, &dest);
1560         if (result != REP_PROTOCOL_SUCCESS)
1561                 return (result);
1562 
1563         result = rc_snapshot_attach(&src->re_node, &dest->re_node);
1564 
1565         entity_release(src);
1566         entity_release(dest);
1567 
1568         return (result);
1569 }
1570 
1571 /*ARGSUSED*/
1572 static void
1573 property_get_type(repcache_client_t *cp, const void *in, size_t insz,
1574     void *out_arg, size_t *outsz, void *arg)
1575 {
1576         const struct rep_protocol_property_request *rpr = in;
1577         struct rep_protocol_integer_response *out = out_arg;
1578         repcache_entity_t *ep;
1579         rep_protocol_value_type_t t = 0;
1580 
1581         assert(*outsz == sizeof (*out));
1582 
1583         ep = entity_find(cp, rpr->rpr_entityid);
1584 
1585         if (ep == NULL) {
1586                 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1587                 *outsz = sizeof (out->rpr_response);
1588                 return;
1589         }
1590 
1591         out->rpr_response = rc_node_get_property_type(&ep->re_node, &t);
1592 
1593         entity_release(ep);
1594 
1595         if (out->rpr_response != REP_PROTOCOL_SUCCESS)
1596                 *outsz = sizeof (out->rpr_response);
1597         else
1598                 out->rpr_value = t;
1599 }
1600 
1601 /*
1602  * Fails with:
1603  *      _UNKNOWN_ID - an id does not designate an active register
1604  *      _NOT_SET - The property is not set
1605  *      _DELETED - The property has been deleted
1606  *      _TYPE_MISMATCH - The object is not a property
1607  *      _NOT_FOUND - The property has no values.
1608  *
1609  * Succeeds with:
1610  *      _SUCCESS - The property has 1 value.
1611  *      _TRUNCATED - The property has >1 value.
1612  */
1613 /*ARGSUSED*/
1614 static void
1615 property_get_value(repcache_client_t *cp, const void *in, size_t insz,
1616     void *out_arg, size_t *outsz, void *arg)
1617 {
1618         const struct rep_protocol_property_request *rpr = in;
1619         struct rep_protocol_value_response *out = out_arg;
1620         repcache_entity_t *ep;
1621 
1622         assert(*outsz == sizeof (*out));
1623 
1624         ep = entity_find(cp, rpr->rpr_entityid);
1625         if (ep == NULL) {
1626                 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1627                 *outsz = sizeof (out->rpr_response);
1628                 return;
1629         }
1630 
1631         out->rpr_response = rc_node_get_property_value(&ep->re_node, out,
1632             outsz);
1633 
1634         entity_release(ep);
1635 
1636         /*
1637          * If we fail, we only return the response code.
1638          * If we succeed, rc_node_get_property_value has shortened *outsz
1639          * to only include the value bytes needed.
1640          */
1641         if (out->rpr_response != REP_PROTOCOL_SUCCESS &&
1642             out->rpr_response != REP_PROTOCOL_FAIL_TRUNCATED)
1643                 *outsz = sizeof (out->rpr_response);
1644 }
1645 
1646 static rep_protocol_responseid_t
1647 propertygrp_notify(repcache_client_t *cp,
1648     struct rep_protocol_propertygrp_request *rpr, int *out_fd)
1649 {
1650         int fds[2];
1651         int ours, theirs;
1652 
1653         rep_protocol_responseid_t result;
1654         repcache_entity_t *ep;
1655 
1656         if (pipe(fds) < 0)
1657                 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1658 
1659         ours = fds[0];
1660         theirs = fds[1];
1661 
1662         if ((ep = entity_find(cp, rpr->rpr_entityid)) == NULL) {
1663                 result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1664                 goto fail;
1665         }
1666 
1667         /*
1668          * While the following can race with other threads setting up a
1669          * notification, the worst that can happen is that our fd has
1670          * already been closed before we return.
1671          */
1672         result = rc_pg_notify_setup(&cp->rc_pg_notify, &ep->re_node,
1673             ours);
1674 
1675         entity_release(ep);
1676 
1677         if (result != REP_PROTOCOL_SUCCESS)
1678                 goto fail;
1679 
1680         *out_fd = theirs;
1681         return (REP_PROTOCOL_SUCCESS);
1682 
1683 fail:
1684         (void) close(ours);
1685         (void) close(theirs);
1686 
1687         return (result);
1688 }
1689 
1690 static rep_protocol_responseid_t
1691 client_add_notify(repcache_client_t *cp,
1692     struct rep_protocol_notify_request *rpr)
1693 {
1694         rpr->rpr_pattern[sizeof (rpr->rpr_pattern) - 1] = 0;
1695 
1696         switch (rpr->rpr_type) {
1697         case REP_PROTOCOL_NOTIFY_PGNAME:
1698                 return (rc_notify_info_add_name(&cp->rc_notify_info,
1699                     rpr->rpr_pattern));
1700 
1701         case REP_PROTOCOL_NOTIFY_PGTYPE:
1702                 return (rc_notify_info_add_type(&cp->rc_notify_info,
1703                     rpr->rpr_pattern));
1704 
1705         default:
1706                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1707         }
1708 }
1709 
1710 /*ARGSUSED*/
1711 static void
1712 client_wait(repcache_client_t *cp, const void *in, size_t insz,
1713     void *out_arg, size_t *outsz, void *arg)
1714 {
1715         int result;
1716         repcache_entity_t *ep;
1717         const struct rep_protocol_wait_request *rpr = in;
1718         struct rep_protocol_fmri_response *out = out_arg;
1719 
1720         assert(*outsz == sizeof (*out));
1721 
1722         (void) pthread_mutex_lock(&cp->rc_lock);
1723         if (cp->rc_notify_thr != 0) {
1724                 (void) pthread_mutex_unlock(&cp->rc_lock);
1725                 out->rpr_response = REP_PROTOCOL_FAIL_EXISTS;
1726                 *outsz = sizeof (out->rpr_response);
1727                 return;
1728         }
1729         cp->rc_notify_thr = pthread_self();
1730         (void) pthread_mutex_unlock(&cp->rc_lock);
1731 
1732         result = rc_notify_info_wait(&cp->rc_notify_info, &cp->rc_notify_ptr,
1733             out->rpr_fmri, sizeof (out->rpr_fmri));
1734 
1735         if (result == REP_PROTOCOL_SUCCESS) {
1736                 if ((ep = entity_find(cp, rpr->rpr_entityid)) != NULL) {
1737                         if (ep->re_type == REP_PROTOCOL_ENTITY_PROPERTYGRP) {
1738                                 rc_node_ptr_assign(&ep->re_node,
1739                                     &cp->rc_notify_ptr);
1740                         } else {
1741                                 result = REP_PROTOCOL_FAIL_TYPE_MISMATCH;
1742                         }
1743                         entity_release(ep);
1744                 } else {
1745                         result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1746                 }
1747                 rc_node_clear(&cp->rc_notify_ptr, 0);
1748         }
1749 
1750         (void) pthread_mutex_lock(&cp->rc_lock);
1751         assert(cp->rc_notify_thr == pthread_self());
1752         cp->rc_notify_thr = 0;
1753         (void) pthread_mutex_unlock(&cp->rc_lock);
1754 
1755         out->rpr_response = result;
1756         if (result != REP_PROTOCOL_SUCCESS)
1757                 *outsz = sizeof (out->rpr_response);
1758 }
1759 
1760 /*
1761  * Can return:
1762  *      _PERMISSION_DENIED      not enough privileges to do request.
1763  *      _BAD_REQUEST            name is not valid or reserved
1764  *      _TRUNCATED              name is too long for current repository path
1765  *      _UNKNOWN                failed for unknown reason (details written to
1766  *                              console)
1767  *      _BACKEND_READONLY       backend is not writable
1768  *      _NO_RESOURCES           out of memory
1769  *      _SUCCESS                Backup completed successfully.
1770  */
1771 static rep_protocol_responseid_t
1772 backup_repository(repcache_client_t *cp,
1773     struct rep_protocol_backup_request *rpr)
1774 {
1775         rep_protocol_responseid_t result;
1776         ucred_t *uc = get_ucred();
1777 
1778         if (!client_is_privileged() && (uc == NULL || ucred_geteuid(uc) != 0))
1779                 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
1780 
1781         rpr->rpr_name[REP_PROTOCOL_NAME_LEN - 1] = 0;
1782         if (strcmp(rpr->rpr_name, REPOSITORY_BOOT_BACKUP) == 0)
1783                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1784 
1785         (void) pthread_mutex_lock(&cp->rc_lock);
1786         if (rpr->rpr_changeid != cp->rc_changeid) {
1787                 result = backend_create_backup(rpr->rpr_name);
1788                 if (result == REP_PROTOCOL_SUCCESS)
1789                         cp->rc_changeid = rpr->rpr_changeid;
1790         } else {
1791                 result = REP_PROTOCOL_SUCCESS;
1792         }
1793         (void) pthread_mutex_unlock(&cp->rc_lock);
1794 
1795         return (result);
1796 }
1797 
1798 /*
1799  * This function captures the information that will be used for an
1800  * annotation audit event.  Specifically, it captures the operation to be
1801  * performed and the name of the file that is being used.  These values are
1802  * copied from the rep_protocol_annotation request at rpr to the client
1803  * structure.  If both these values are null, the client is turning
1804  * annotation off.
1805  *
1806  * Fails with
1807  *      _NO_RESOURCES - unable to allocate memory
1808  */
1809 static rep_protocol_responseid_t
1810 set_annotation(repcache_client_t *cp, struct rep_protocol_annotation *rpr)
1811 {
1812         au_id_t audit_uid;
1813         const char *file = NULL;
1814         const char *old_ptrs[2];
1815         const char *operation = NULL;
1816         rep_protocol_responseid_t rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1817         au_asid_t sessionid;
1818 
1819         (void) memset((void *)old_ptrs, 0, sizeof (old_ptrs));
1820 
1821         /* Copy rpr_operation and rpr_file if they are not empty strings. */
1822         if (rpr->rpr_operation[0] != 0) {
1823                 /*
1824                  * Make sure that client did not send us an unterminated buffer.
1825                  */
1826                 rpr->rpr_operation[sizeof (rpr->rpr_operation) - 1] = 0;
1827                 if ((operation = strdup(rpr->rpr_operation)) == NULL)
1828                         goto out;
1829         }
1830         if (rpr->rpr_file[0] != 0) {
1831                 /*
1832                  * Make sure that client did not send us an unterminated buffer.
1833                  */
1834                 rpr->rpr_file[sizeof (rpr->rpr_file) - 1] = 0;
1835                 if ((file = strdup(rpr->rpr_file)) == NULL)
1836                         goto out;
1837         }
1838 
1839         (void) pthread_mutex_lock(&cp->rc_annotate_lock);
1840         /* Save addresses of memory to free when not locked */
1841         old_ptrs[0] = cp->rc_operation;
1842         old_ptrs[1] = cp->rc_file;
1843 
1844         /* Save pointers to annotation strings. */
1845         cp->rc_operation = operation;
1846         cp->rc_file = file;
1847 
1848         /*
1849          * Set annotation flag.  Annotations should be turned on if either
1850          * operation or file are not NULL.
1851          */
1852         cp->rc_annotate = (operation != NULL) || (file != NULL);
1853         (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1854 
1855         /*
1856          * operation and file pointers are saved in cp, so don't free them
1857          * during cleanup.
1858          */
1859         operation = NULL;
1860         file = NULL;
1861         rc = REP_PROTOCOL_SUCCESS;
1862 
1863         /*
1864          * Native builds are done to create svc.configd-native.  This
1865          * program runs only on the Open Solaris build machines to create
1866          * the seed repository.  Until the SMF auditing code is distributed
1867          * to the Open Solaris build machines, adt_get_unique_id() in the
1868          * following code is not a global function in libbsm.  Hence the
1869          * following conditional compilation.
1870          */
1871 #ifndef NATIVE_BUILD
1872         /*
1873          * Set the appropriate audit session id.
1874          */
1875         if (cp->rc_annotate) {
1876                 /*
1877                  * We're starting a group of annotated audit events, so
1878                  * create and set an audit session ID for this annotation.
1879                  */
1880                 adt_get_auid(cp->rc_adt_session, &audit_uid);
1881                 sessionid = adt_get_unique_id(audit_uid);
1882         } else {
1883                 /*
1884                  * Annotation is done so restore our client audit session
1885                  * id.
1886                  */
1887                 sessionid = cp->rc_adt_sessionid;
1888         }
1889         adt_set_asid(cp->rc_adt_session, sessionid);
1890 #endif  /* NATIVE_BUILD */
1891 
1892 out:
1893         if (operation != NULL)
1894                 free((void *)operation);
1895         if (file != NULL)
1896                 free((void *)file);
1897         free((void *)old_ptrs[0]);
1898         free((void *)old_ptrs[1]);
1899         return (rc);
1900 }
1901 
1902 /*
1903  * Determine if an annotation event needs to be generated.  If it does
1904  * provide the operation and file name that should be used in the event.
1905  *
1906  * Can return:
1907  *      0               No annotation event needed or buffers are not large
1908  *                      enough.  Either way an event should not be
1909  *                      generated.
1910  *      1               Generate annotation event.
1911  */
1912 int
1913 client_annotation_needed(char *operation, size_t oper_sz,
1914     char *file, size_t file_sz)
1915 {
1916         thread_info_t *ti = thread_self();
1917         repcache_client_t *cp = ti->ti_active_client;
1918         int rc = 0;
1919 
1920         (void) pthread_mutex_lock(&cp->rc_annotate_lock);
1921         if (cp->rc_annotate) {
1922                 rc = 1;
1923                 if (cp->rc_operation == NULL) {
1924                         if (oper_sz > 0)
1925                                 operation[0] = 0;
1926                 } else {
1927                         if (strlcpy(operation, cp->rc_operation, oper_sz) >=
1928                             oper_sz) {
1929                                 /* Buffer overflow, so do not generate event */
1930                                 rc = 0;
1931                         }
1932                 }
1933                 if (cp->rc_file == NULL) {
1934                         if (file_sz > 0)
1935                                 file[0] = 0;
1936                 } else if (rc == 1) {
1937                         if (strlcpy(file, cp->rc_file, file_sz) >= file_sz) {
1938                                 /* Buffer overflow, so do not generate event */
1939                                 rc = 0;
1940                         }
1941                 }
1942         }
1943         (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1944         return (rc);
1945 }
1946 
1947 void
1948 client_annotation_finished()
1949 {
1950         thread_info_t *ti = thread_self();
1951         repcache_client_t *cp = ti->ti_active_client;
1952 
1953         (void) pthread_mutex_lock(&cp->rc_annotate_lock);
1954         cp->rc_annotate = 0;
1955         (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1956 }
1957 
1958 #ifndef NATIVE_BUILD
1959 static void
1960 start_audit_session(repcache_client_t *cp)
1961 {
1962         ucred_t *cred = NULL;
1963         adt_session_data_t *session;
1964 
1965         /*
1966          * A NULL session pointer value can legally be used in all
1967          * subsequent calls to adt_* functions.
1968          */
1969         cp->rc_adt_session = NULL;
1970 
1971         if (!adt_audit_state(AUC_AUDITING))
1972                 return;
1973 
1974         if (door_ucred(&cred) != 0) {
1975                 switch (errno) {
1976                 case EAGAIN:
1977                 case ENOMEM:
1978                         syslog(LOG_ERR, gettext("start_audit_session(): cannot "
1979                             "get ucred.  %m\n"));
1980                         return;
1981                 case EINVAL:
1982                         /*
1983                          * Door client went away.  This is a normal,
1984                          * although infrequent event, so there is no need
1985                          * to create a syslog message.
1986                          */
1987                         return;
1988                 case EFAULT:
1989                 default:
1990                         bad_error("door_ucred", errno);
1991                         return;
1992                 }
1993         }
1994         if (adt_start_session(&session, NULL, 0) != 0) {
1995                 syslog(LOG_ERR, gettext("start_audit_session(): could not "
1996                     "start audit session.\n"));
1997                 ucred_free(cred);
1998                 return;
1999         }
2000         if (adt_set_from_ucred(session, cred, ADT_NEW) != 0) {
2001                 syslog(LOG_ERR, gettext("start_audit_session(): cannot set "
2002                     "audit session data from ucred\n"));
2003                 /* Something went wrong.  End the session. */
2004                 (void) adt_end_session(session);
2005                 ucred_free(cred);
2006                 return;
2007         }
2008 
2009         /* All went well.  Save the session data and session ID */
2010         cp->rc_adt_session = session;
2011         adt_get_asid(session, &cp->rc_adt_sessionid);
2012 
2013         ucred_free(cred);
2014 }
2015 #endif
2016 
2017 /*
2018  * Handle switch client request
2019  *
2020  * This routine can return:
2021  *
2022  *      _PERMISSION_DENIED      not enough privileges to do request.
2023  *      _UNKNOWN                file operation error (details written to
2024  *                              the console).
2025  *      _SUCCESS                switch operation is completed.
2026  *      _BACKEND_ACCESS         backend access fails.
2027  *      _NO_RESOURCES           out of memory.
2028  *      _BACKEND_READONLY       backend is not writable.
2029  */
2030 static rep_protocol_responseid_t
2031 repository_switch(repcache_client_t *cp,
2032     struct rep_protocol_switch_request *rpr)
2033 {
2034         rep_protocol_responseid_t result;
2035         ucred_t *uc = get_ucred();
2036 
2037         if (!client_is_privileged() && (uc == NULL ||
2038             ucred_geteuid(uc) != 0)) {
2039                 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
2040         }
2041 
2042         (void) pthread_mutex_lock(&cp->rc_lock);
2043         if (rpr->rpr_changeid != cp->rc_changeid) {
2044                 if ((result = backend_switch(rpr->rpr_flag)) ==
2045                     REP_PROTOCOL_SUCCESS)
2046                         cp->rc_changeid = rpr->rpr_changeid;
2047         } else {
2048                 result = REP_PROTOCOL_SUCCESS;
2049         }
2050         (void) pthread_mutex_unlock(&cp->rc_lock);
2051 
2052         return (result);
2053 }
2054 
2055 typedef rep_protocol_responseid_t protocol_simple_f(repcache_client_t *cp,
2056     const void *rpr);
2057 
2058 /*ARGSUSED*/
2059 static void
2060 simple_handler(repcache_client_t *cp, const void *in, size_t insz,
2061     void *out_arg, size_t *outsz, void *arg)
2062 {
2063         protocol_simple_f *f = (protocol_simple_f *)arg;
2064         rep_protocol_response_t *out = out_arg;
2065 
2066         assert(*outsz == sizeof (*out));
2067         assert(f != NULL);
2068 
2069         out->rpr_response = (*f)(cp, in);
2070 }
2071 
2072 typedef rep_protocol_responseid_t protocol_simple_fd_f(repcache_client_t *cp,
2073     const void *rpr, int *out_fd);
2074 
2075 /*ARGSUSED*/
2076 static void
2077 simple_fd_handler(repcache_client_t *cp, const void *in, size_t insz,
2078     void *out_arg, size_t *outsz, void *arg, int *out_fd)
2079 {
2080         protocol_simple_fd_f *f = (protocol_simple_fd_f *)arg;
2081         rep_protocol_response_t *out = out_arg;
2082 
2083         assert(*outsz == sizeof (*out));
2084         assert(f != NULL);
2085 
2086         out->rpr_response = (*f)(cp, in, out_fd);
2087 }
2088 
2089 typedef void protocol_handler_f(repcache_client_t *, const void *in,
2090     size_t insz, void *out, size_t *outsz, void *arg);
2091 
2092 typedef void protocol_handler_fdret_f(repcache_client_t *, const void *in,
2093     size_t insz, void *out, size_t *outsz, void *arg, int *fd_out);
2094 
2095 #define PROTO(p, f, in) {                                               \
2096                 p, #p, simple_handler, (void *)(&f), NULL,          \
2097                     sizeof (in), sizeof (rep_protocol_response_t), 0    \
2098         }
2099 
2100 #define PROTO_FD_OUT(p, f, in) {                                        \
2101                 p, #p, NULL, (void *)(&f), simple_fd_handler,               \
2102                     sizeof (in),                                        \
2103                     sizeof (rep_protocol_response_t),                   \
2104                     PROTO_FLAG_RETFD                                    \
2105         }
2106 
2107 #define PROTO_VARIN(p, f, insz) {                                       \
2108                 p, #p, &(f), NULL, NULL,                            \
2109                     insz, sizeof (rep_protocol_response_t),             \
2110                     PROTO_FLAG_VARINPUT                                 \
2111         }
2112 
2113 #define PROTO_UINT_OUT(p, f, in) {                                      \
2114                 p, #p, &(f), NULL, NULL,                            \
2115                     sizeof (in),                                        \
2116                     sizeof (struct rep_protocol_integer_response), 0    \
2117         }
2118 
2119 #define PROTO_NAME_OUT(p, f, in) {                                      \
2120                 p, #p, &(f), NULL, NULL,                            \
2121                     sizeof (in),                                        \
2122                     sizeof (struct rep_protocol_name_response), 0       \
2123         }
2124 
2125 #define PROTO_FMRI_OUT(p, f, in) {                                      \
2126                 p, #p, &(f), NULL, NULL,                            \
2127                     sizeof (in),                                        \
2128                     sizeof (struct rep_protocol_fmri_response), 0       \
2129         }
2130 
2131 #define PROTO_VALUE_OUT(p, f, in) {                                     \
2132                 p, #p, &(f), NULL, NULL,                            \
2133                     sizeof (in),                                        \
2134                     sizeof (struct rep_protocol_value_response), 0      \
2135         }
2136 
2137 #define PROTO_PANIC(p)  { p, #p, NULL, NULL, NULL, 0, 0, PROTO_FLAG_PANIC }
2138 #define PROTO_END()     { 0, NULL, NULL, NULL, NULL, 0, 0, PROTO_FLAG_PANIC }
2139 
2140 #define PROTO_FLAG_PANIC        0x00000001      /* should never be called */
2141 #define PROTO_FLAG_VARINPUT     0x00000004      /* in_size is minimum size */
2142 #define PROTO_FLAG_RETFD        0x00000008      /* can also return an FD */
2143 
2144 #define PROTO_ALL_FLAGS         0x0000000f      /* all flags */
2145 
2146 static struct protocol_entry {
2147         enum rep_protocol_requestid     pt_request;
2148         const char                      *pt_name;
2149         protocol_handler_f              *pt_handler;
2150         void                            *pt_arg;
2151         protocol_handler_fdret_f        *pt_fd_handler;
2152         size_t                          pt_in_size;
2153         size_t                          pt_out_max;
2154         uint32_t                        pt_flags;
2155 } protocol_table[] = {
2156         PROTO_PANIC(REP_PROTOCOL_CLOSE),                /* special case */
2157 
2158         PROTO(REP_PROTOCOL_ENTITY_SETUP,                entity_setup,
2159             struct rep_protocol_entity_setup),
2160         PROTO_NAME_OUT(REP_PROTOCOL_ENTITY_NAME,        entity_name,
2161             struct rep_protocol_entity_name),
2162         PROTO_UINT_OUT(REP_PROTOCOL_ENTITY_PARENT_TYPE, entity_parent_type,
2163             struct rep_protocol_entity_parent_type),
2164         PROTO(REP_PROTOCOL_ENTITY_GET_CHILD,            entity_get_child,
2165             struct rep_protocol_entity_get_child),
2166         PROTO(REP_PROTOCOL_ENTITY_GET_PARENT,           entity_get_parent,
2167             struct rep_protocol_entity_parent),
2168         PROTO(REP_PROTOCOL_ENTITY_GET,                  entity_get,
2169             struct rep_protocol_entity_get),
2170         PROTO(REP_PROTOCOL_ENTITY_UPDATE,               entity_update,
2171             struct rep_protocol_entity_update),
2172         PROTO(REP_PROTOCOL_ENTITY_CREATE_CHILD,         entity_create_child,
2173             struct rep_protocol_entity_create_child),
2174         PROTO(REP_PROTOCOL_ENTITY_CREATE_PG,            entity_create_pg,
2175             struct rep_protocol_entity_create_pg),
2176         PROTO(REP_PROTOCOL_ENTITY_DELETE,               entity_delete,
2177             struct rep_protocol_entity_delete),
2178         PROTO(REP_PROTOCOL_ENTITY_RESET,                entity_reset,
2179             struct rep_protocol_entity_reset),
2180         PROTO(REP_PROTOCOL_ENTITY_TEARDOWN,             entity_teardown,
2181             struct rep_protocol_entity_teardown),
2182 
2183         PROTO(REP_PROTOCOL_ITER_SETUP,                  iter_setup,
2184             struct rep_protocol_iter_request),
2185         PROTO(REP_PROTOCOL_ITER_START,                  iter_start,
2186             struct rep_protocol_iter_start),
2187         PROTO(REP_PROTOCOL_ITER_READ,                   iter_read,
2188             struct rep_protocol_iter_read),
2189         PROTO_VALUE_OUT(REP_PROTOCOL_ITER_READ_VALUE,   iter_read_value,
2190             struct rep_protocol_iter_read_value),
2191         PROTO(REP_PROTOCOL_ITER_RESET,                  iter_reset,
2192             struct rep_protocol_iter_request),
2193         PROTO(REP_PROTOCOL_ITER_TEARDOWN,               iter_teardown,
2194             struct rep_protocol_iter_request),
2195 
2196         PROTO(REP_PROTOCOL_NEXT_SNAPLEVEL,              next_snaplevel,
2197             struct rep_protocol_entity_pair),
2198 
2199         PROTO(REP_PROTOCOL_SNAPSHOT_TAKE,               snapshot_take,
2200             struct rep_protocol_snapshot_take),
2201         PROTO(REP_PROTOCOL_SNAPSHOT_TAKE_NAMED,         snapshot_take_named,
2202             struct rep_protocol_snapshot_take_named),
2203         PROTO(REP_PROTOCOL_SNAPSHOT_ATTACH,             snapshot_attach,
2204             struct rep_protocol_snapshot_attach),
2205 
2206         PROTO_UINT_OUT(REP_PROTOCOL_PROPERTY_GET_TYPE,  property_get_type,
2207             struct rep_protocol_property_request),
2208         PROTO_VALUE_OUT(REP_PROTOCOL_PROPERTY_GET_VALUE, property_get_value,
2209             struct rep_protocol_property_request),
2210 
2211         PROTO_FD_OUT(REP_PROTOCOL_PROPERTYGRP_SETUP_WAIT, propertygrp_notify,
2212             struct rep_protocol_propertygrp_request),
2213         PROTO(REP_PROTOCOL_PROPERTYGRP_TX_START,        tx_start,
2214             struct rep_protocol_transaction_start),
2215         PROTO_VARIN(REP_PROTOCOL_PROPERTYGRP_TX_COMMIT, tx_commit,
2216             REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE),
2217 
2218         PROTO(REP_PROTOCOL_CLIENT_ADD_NOTIFY,           client_add_notify,
2219             struct rep_protocol_notify_request),
2220         PROTO_FMRI_OUT(REP_PROTOCOL_CLIENT_WAIT,        client_wait,
2221             struct rep_protocol_wait_request),
2222 
2223         PROTO(REP_PROTOCOL_BACKUP,                      backup_repository,
2224             struct rep_protocol_backup_request),
2225 
2226         PROTO(REP_PROTOCOL_SET_AUDIT_ANNOTATION,        set_annotation,
2227             struct rep_protocol_annotation),
2228 
2229         PROTO(REP_PROTOCOL_SWITCH,                      repository_switch,
2230             struct rep_protocol_switch_request),
2231 
2232         PROTO_END()
2233 };
2234 #undef PROTO
2235 #undef PROTO_FMRI_OUT
2236 #undef PROTO_NAME_OUT
2237 #undef PROTO_UINT_OUT
2238 #undef PROTO_PANIC
2239 #undef PROTO_END
2240 
2241 /*
2242  * The number of entries, sans PROTO_END()
2243  */
2244 #define PROTOCOL_ENTRIES \
2245             (sizeof (protocol_table) / sizeof (*protocol_table) - 1)
2246 
2247 #define PROTOCOL_PREFIX "REP_PROTOCOL_"
2248 
2249 int
2250 client_init(void)
2251 {
2252         int i;
2253         struct protocol_entry *e;
2254 
2255         if (!client_hash_init())
2256                 return (0);
2257 
2258         if (request_log_size > 0) {
2259                 request_log = uu_zalloc(request_log_size *
2260                     sizeof (request_log_entry_t));
2261         }
2262 
2263         /*
2264          * update the names to not include REP_PROTOCOL_
2265          */
2266         for (i = 0; i < PROTOCOL_ENTRIES; i++) {
2267                 e = &protocol_table[i];
2268                 assert(strncmp(e->pt_name, PROTOCOL_PREFIX,
2269                     strlen(PROTOCOL_PREFIX)) == 0);
2270                 e->pt_name += strlen(PROTOCOL_PREFIX);
2271         }
2272         /*
2273          * verify the protocol table is consistent
2274          */
2275         for (i = 0; i < PROTOCOL_ENTRIES; i++) {
2276                 e = &protocol_table[i];
2277                 assert(e->pt_request == (REP_PROTOCOL_BASE + i));
2278 
2279                 assert((e->pt_flags & ~PROTO_ALL_FLAGS) == 0);
2280 
2281                 if (e->pt_flags & PROTO_FLAG_PANIC)
2282                         assert(e->pt_in_size == 0 && e->pt_out_max == 0 &&
2283                             e->pt_handler == NULL);
2284                 else
2285                         assert(e->pt_in_size != 0 && e->pt_out_max != 0 &&
2286                             (e->pt_handler != NULL ||
2287                             e->pt_fd_handler != NULL));
2288         }
2289         assert((REP_PROTOCOL_BASE + i) == REP_PROTOCOL_MAX_REQUEST);
2290 
2291         assert(protocol_table[i].pt_request == 0);
2292 
2293         return (1);
2294 }
2295 
2296 static void
2297 client_switcher(void *cookie, char *argp, size_t arg_size, door_desc_t *desc_in,
2298     uint_t n_desc)
2299 {
2300         thread_info_t *ti = thread_self();
2301 
2302         repcache_client_t *cp;
2303         uint32_t id = (uint32_t)cookie;
2304         enum rep_protocol_requestid request_code;
2305 
2306         rep_protocol_responseid_t result = INVALID_RESULT;
2307 
2308         struct protocol_entry *e;
2309 
2310         char *retval = NULL;
2311         size_t retsize = 0;
2312 
2313         int retfd = -1;
2314         door_desc_t desc;
2315         request_log_entry_t *rlp;
2316 
2317         rlp = start_log(id);
2318 
2319         if (n_desc != 0)
2320                 uu_die("can't happen: %d descriptors @%p (cookie %p)",
2321                     n_desc, desc_in, cookie);
2322 
2323         if (argp == DOOR_UNREF_DATA) {
2324                 client_destroy(id);
2325                 goto bad_end;
2326         }
2327 
2328         thread_newstate(ti, TI_CLIENT_CALL);
2329 
2330         /*
2331          * To simplify returning just a result code, we set up for
2332          * that case here.
2333          */
2334         retval = (char *)&result;
2335         retsize = sizeof (result);
2336 
2337         if (arg_size < sizeof (request_code)) {
2338                 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2339                 goto end_unheld;
2340         }
2341 
2342         ti->ti_client_request = (void *)argp;
2343 
2344         /* LINTED alignment */
2345         request_code = *(uint32_t *)argp;
2346 
2347         if (rlp != NULL) {
2348                 rlp->rl_request = request_code;
2349         }
2350         /*
2351          * In order to avoid locking problems on removal, we handle the
2352          * "close" case before doing a lookup.
2353          */
2354         if (request_code == REP_PROTOCOL_CLOSE) {
2355                 client_destroy(id);
2356                 result = REP_PROTOCOL_SUCCESS;
2357                 goto end_unheld;
2358         }
2359 
2360         cp = client_lookup(id);
2361         /*
2362          * cp is held
2363          */
2364 
2365         if (cp == NULL)
2366                 goto bad_end;
2367 
2368         if (rlp != NULL)
2369                 rlp->rl_client = cp;
2370 
2371         ti->ti_active_client = cp;
2372 
2373         if (request_code < REP_PROTOCOL_BASE ||
2374             request_code >= REP_PROTOCOL_BASE + PROTOCOL_ENTRIES) {
2375                 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2376                 goto end;
2377         }
2378 
2379         e = &protocol_table[request_code - REP_PROTOCOL_BASE];
2380 
2381         assert(!(e->pt_flags & PROTO_FLAG_PANIC));
2382 
2383         if (e->pt_flags & PROTO_FLAG_VARINPUT) {
2384                 if (arg_size < e->pt_in_size) {
2385                         result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2386                         goto end;
2387                 }
2388         } else if (arg_size != e->pt_in_size) {
2389                 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2390                 goto end;
2391         }
2392 
2393         if (retsize != e->pt_out_max) {
2394                 retsize = e->pt_out_max;
2395                 retval = alloca(retsize);
2396         }
2397 
2398         if (e->pt_flags & PROTO_FLAG_RETFD)
2399                 e->pt_fd_handler(cp, argp, arg_size, retval, &retsize,
2400                     e->pt_arg, &retfd);
2401         else
2402                 e->pt_handler(cp, argp, arg_size, retval, &retsize, e->pt_arg);
2403 
2404 end:
2405         ti->ti_active_client = NULL;
2406         client_release(cp);
2407 
2408 end_unheld:
2409         if (rlp != NULL) {
2410                 /* LINTED alignment */
2411                 rlp->rl_response = *(uint32_t *)retval;
2412                 end_log();
2413                 rlp = NULL;
2414         }
2415         ti->ti_client_request = NULL;
2416         thread_newstate(ti, TI_DOOR_RETURN);
2417 
2418         if (retval == (char *)&result) {
2419                 assert(result != INVALID_RESULT && retsize == sizeof (result));
2420         } else {
2421                 /* LINTED alignment */
2422                 result = *(uint32_t *)retval;
2423         }
2424         if (retfd != -1) {
2425                 desc.d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE;
2426                 desc.d_data.d_desc.d_descriptor = retfd;
2427                 (void) door_return(retval, retsize, &desc, 1);
2428         } else {
2429                 (void) door_return(retval, retsize, NULL, 0);
2430         }
2431 bad_end:
2432         if (rlp != NULL) {
2433                 rlp->rl_response = -1;
2434                 end_log();
2435                 rlp = NULL;
2436         }
2437         (void) door_return(NULL, 0, NULL, 0);
2438 }
2439 
2440 int
2441 create_client(pid_t pid, uint32_t debugflags, int privileged, int *out_fd)
2442 {
2443         int fd;
2444 
2445         repcache_client_t *cp;
2446 
2447         struct door_info info;
2448 
2449         int door_flags = DOOR_UNREF | DOOR_REFUSE_DESC;
2450 #ifdef DOOR_NO_CANCEL
2451         door_flags |= DOOR_NO_CANCEL;
2452 #endif
2453 
2454         cp = client_alloc();
2455         if (cp == NULL)
2456                 return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2457 
2458         (void) pthread_mutex_lock(&client_lock);
2459         cp->rc_id = ++client_maxid;
2460         (void) pthread_mutex_unlock(&client_lock);
2461 
2462         cp->rc_all_auths = privileged;
2463         cp->rc_pid = pid;
2464         cp->rc_debug = debugflags;
2465 
2466 #ifndef NATIVE_BUILD
2467         start_audit_session(cp);
2468 #endif
2469 
2470         cp->rc_doorfd = door_create(client_switcher, (void *)cp->rc_id,
2471             door_flags);
2472 
2473         if (cp->rc_doorfd < 0) {
2474                 client_free(cp);
2475                 return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2476         }
2477 #ifdef DOOR_PARAM_DATA_MIN
2478         (void) door_setparam(cp->rc_doorfd, DOOR_PARAM_DATA_MIN,
2479             sizeof (enum rep_protocol_requestid));
2480 #endif
2481 
2482         if ((fd = dup(cp->rc_doorfd)) < 0 ||
2483             door_info(cp->rc_doorfd, &info) < 0) {
2484                 if (fd >= 0)
2485                         (void) close(fd);
2486                 (void) door_revoke(cp->rc_doorfd);
2487                 cp->rc_doorfd = -1;
2488                 client_free(cp);
2489                 return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2490         }
2491 
2492         rc_pg_notify_init(&cp->rc_pg_notify);
2493         rc_notify_info_init(&cp->rc_notify_info);
2494 
2495         client_insert(cp);
2496 
2497         cp->rc_doorid = info.di_uniquifier;
2498         *out_fd = fd;
2499 
2500         return (REPOSITORY_DOOR_SUCCESS);
2501 }