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  * Copyright 2019 Joyent, Inc.
  26  */
  27 
  28 /*
  29  * This is the client layer for svc.configd.  All direct protocol interactions
  30  * are handled here.
  31  *
  32  * Essentially, the job of this layer is to turn the idempotent protocol
  33  * into a series of non-idempotent calls into the object layer, while
  34  * also handling the necessary locking.
  35  */
  36 
  37 #include <alloca.h>
  38 #include <assert.h>
  39 #include <bsm/adt_event.h>
  40 #include <door.h>
  41 #include <errno.h>
  42 #include <libintl.h>
  43 #include <limits.h>
  44 #include <pthread.h>
  45 #include <stdio.h>
  46 #include <stdlib.h>
  47 #include <string.h>
  48 #include <syslog.h>
  49 #include <ucred.h>
  50 #include <unistd.h>
  51 
  52 #include <libuutil.h>
  53 
  54 #include "configd.h"
  55 #include "repcache_protocol.h"
  56 
  57 #define INVALID_CHANGEID        (0)
  58 #define INVALID_DOORID          ((door_id_t)-1)
  59 #define INVALID_RESULT          ((rep_protocol_responseid_t)INT_MIN)
  60 
  61 /*
  62  * lint doesn't like constant assertions
  63  */
  64 #ifdef lint
  65 #define assert_nolint(x) (void)0
  66 #else
  67 #define assert_nolint(x) assert(x)
  68 #endif
  69 
  70 /*
  71  * Protects client linkage and the freelist
  72  */
  73 #define CLIENT_HASH_SIZE        64
  74 
  75 #pragma align 64(client_hash)
  76 static client_bucket_t client_hash[CLIENT_HASH_SIZE];
  77 
  78 static uu_avl_pool_t *entity_pool;
  79 static uu_avl_pool_t *iter_pool;
  80 static uu_list_pool_t *client_pool;
  81 
  82 #define CLIENT_HASH(id)         (&client_hash[((id) & (CLIENT_HASH_SIZE - 1))])
  83 
  84 uint_t request_log_size = 1024;         /* tunable, before we start */
  85 
  86 static pthread_mutex_t request_log_lock = PTHREAD_MUTEX_INITIALIZER;
  87 static uint_t request_log_cur;
  88 request_log_entry_t     *request_log;
  89 
  90 static uint32_t         client_maxid;
  91 static pthread_mutex_t  client_lock;    /* protects client_maxid */
  92 
  93 static request_log_entry_t *
  94 get_log(void)
  95 {
  96         thread_info_t *ti = thread_self();
  97         return (&ti->ti_log);
  98 }
  99 
 100 void
 101 log_enter(request_log_entry_t *rlp)
 102 {
 103         if (rlp->rl_start != 0 && request_log != NULL) {
 104                 request_log_entry_t *logrlp;
 105 
 106                 (void) pthread_mutex_lock(&request_log_lock);
 107                 assert(request_log_cur < request_log_size);
 108                 logrlp = &request_log[request_log_cur++];
 109                 if (request_log_cur == request_log_size)
 110                         request_log_cur = 0;
 111                 (void) memcpy(logrlp, rlp, sizeof (*rlp));
 112                 (void) pthread_mutex_unlock(&request_log_lock);
 113         }
 114 }
 115 
 116 /*
 117  * Note that the svc.configd dmod will join all of the per-thread log entries
 118  * with the main log, so that even if the log is disabled, there is some
 119  * information available.
 120  */
 121 static request_log_entry_t *
 122 start_log(uint32_t clientid)
 123 {
 124         request_log_entry_t *rlp = get_log();
 125 
 126         log_enter(rlp);
 127 
 128         (void) memset(rlp, 0, sizeof (*rlp));
 129         rlp->rl_start = gethrtime();
 130         rlp->rl_tid = pthread_self();
 131         rlp->rl_clientid = clientid;
 132 
 133         return (rlp);
 134 }
 135 
 136 void
 137 end_log(void)
 138 {
 139         request_log_entry_t *rlp = get_log();
 140 
 141         rlp->rl_end = gethrtime();
 142 }
 143 
 144 static void
 145 add_log_ptr(request_log_entry_t *rlp, enum rc_ptr_type type, uint32_t id,
 146     void *ptr)
 147 {
 148         request_log_ptr_t *rpp;
 149 
 150         if (rlp == NULL)
 151                 return;
 152 
 153         if (rlp->rl_num_ptrs >= MAX_PTRS)
 154                 return;
 155 
 156         rpp = &rlp->rl_ptrs[rlp->rl_num_ptrs++];
 157         rpp->rlp_type = type;
 158         rpp->rlp_id = id;
 159         rpp->rlp_ptr = ptr;
 160 
 161         /*
 162          * For entities, it's useful to have the node pointer at the start
 163          * of the request.
 164          */
 165         if (type == RC_PTR_TYPE_ENTITY && ptr != NULL)
 166                 rpp->rlp_data = ((repcache_entity_t *)ptr)->re_node.rnp_node;
 167 }
 168 
 169 int
 170 client_is_privileged(void)
 171 {
 172         thread_info_t *ti = thread_self();
 173 
 174         ucred_t *uc;
 175 
 176         if (ti->ti_active_client != NULL &&
 177             ti->ti_active_client->rc_all_auths)
 178                 return (1);
 179 
 180         if ((uc = get_ucred()) == NULL)
 181                 return (0);
 182 
 183         return (ucred_is_privileged(uc));
 184 }
 185 
 186 /*ARGSUSED*/
 187 static int
 188 client_compare(const void *lc_arg, const void *rc_arg, void *private)
 189 {
 190         uint32_t l_id = ((const repcache_client_t *)lc_arg)->rc_id;
 191         uint32_t r_id = ((const repcache_client_t *)rc_arg)->rc_id;
 192 
 193         if (l_id > r_id)
 194                 return (1);
 195         if (l_id < r_id)
 196                 return (-1);
 197         return (0);
 198 }
 199 
 200 /*ARGSUSED*/
 201 static int
 202 entity_compare(const void *lc_arg, const void *rc_arg, void *private)
 203 {
 204         uint32_t l_id = ((const repcache_entity_t *)lc_arg)->re_id;
 205         uint32_t r_id = ((const repcache_entity_t *)rc_arg)->re_id;
 206 
 207         if (l_id > r_id)
 208                 return (1);
 209         if (l_id < r_id)
 210                 return (-1);
 211         return (0);
 212 }
 213 
 214 /*ARGSUSED*/
 215 static int
 216 iter_compare(const void *lc_arg, const void *rc_arg, void *private)
 217 {
 218         uint32_t l_id = ((const repcache_iter_t *)lc_arg)->ri_id;
 219         uint32_t r_id = ((const repcache_iter_t *)rc_arg)->ri_id;
 220 
 221         if (l_id > r_id)
 222                 return (1);
 223         if (l_id < r_id)
 224                 return (-1);
 225         return (0);
 226 }
 227 
 228 static int
 229 client_hash_init(void)
 230 {
 231         int x;
 232 
 233         assert_nolint(offsetof(repcache_entity_t, re_id) == 0);
 234         entity_pool = uu_avl_pool_create("repcache_entitys",
 235             sizeof (repcache_entity_t), offsetof(repcache_entity_t, re_link),
 236             entity_compare, UU_AVL_POOL_DEBUG);
 237 
 238         assert_nolint(offsetof(repcache_iter_t, ri_id) == 0);
 239         iter_pool = uu_avl_pool_create("repcache_iters",
 240             sizeof (repcache_iter_t), offsetof(repcache_iter_t, ri_link),
 241             iter_compare, UU_AVL_POOL_DEBUG);
 242 
 243         assert_nolint(offsetof(repcache_client_t, rc_id) == 0);
 244         client_pool = uu_list_pool_create("repcache_clients",
 245             sizeof (repcache_client_t), offsetof(repcache_client_t, rc_link),
 246             client_compare, UU_LIST_POOL_DEBUG);
 247 
 248         if (entity_pool == NULL || iter_pool == NULL || client_pool == NULL)
 249                 return (0);
 250 
 251         for (x = 0; x < CLIENT_HASH_SIZE; x++) {
 252                 uu_list_t *lp = uu_list_create(client_pool, &client_hash[x],
 253                     UU_LIST_SORTED);
 254                 if (lp == NULL)
 255                         return (0);
 256 
 257                 (void) pthread_mutex_init(&client_hash[x].cb_lock, NULL);
 258                 client_hash[x].cb_list = lp;
 259         }
 260 
 261         return (1);
 262 }
 263 
 264 static repcache_client_t *
 265 client_alloc(void)
 266 {
 267         repcache_client_t *cp;
 268         cp = uu_zalloc(sizeof (*cp));
 269         if (cp == NULL)
 270                 return (NULL);
 271 
 272         cp->rc_entities = uu_avl_create(entity_pool, cp, 0);
 273         if (cp->rc_entities == NULL)
 274                 goto fail;
 275 
 276         cp->rc_iters = uu_avl_create(iter_pool, cp, 0);
 277         if (cp->rc_iters == NULL)
 278                 goto fail;
 279 
 280         uu_list_node_init(cp, &cp->rc_link, client_pool);
 281 
 282         cp->rc_doorfd = -1;
 283         cp->rc_doorid = INVALID_DOORID;
 284 
 285         (void) pthread_mutex_init(&cp->rc_lock, NULL);
 286         (void) pthread_mutex_init(&cp->rc_annotate_lock, NULL);
 287 
 288         rc_node_ptr_init(&cp->rc_notify_ptr);
 289 
 290         return (cp);
 291 
 292 fail:
 293         if (cp->rc_iters != NULL)
 294                 uu_avl_destroy(cp->rc_iters);
 295         if (cp->rc_entities != NULL)
 296                 uu_avl_destroy(cp->rc_entities);
 297         uu_free(cp);
 298         return (NULL);
 299 }
 300 
 301 static void
 302 client_free(repcache_client_t *cp)
 303 {
 304         assert(cp->rc_insert_thr == 0);
 305         assert(cp->rc_refcnt == 0);
 306         assert(cp->rc_doorfd == -1);
 307         assert(cp->rc_doorid == INVALID_DOORID);
 308         assert(uu_avl_first(cp->rc_entities) == NULL);
 309         assert(uu_avl_first(cp->rc_iters) == NULL);
 310         uu_avl_destroy(cp->rc_entities);
 311         uu_avl_destroy(cp->rc_iters);
 312         uu_list_node_fini(cp, &cp->rc_link, client_pool);
 313         (void) pthread_mutex_destroy(&cp->rc_lock);
 314         (void) pthread_mutex_destroy(&cp->rc_annotate_lock);
 315         rc_node_ptr_free_mem(&cp->rc_notify_ptr);
 316         uu_free(cp);
 317 }
 318 
 319 static void
 320 client_insert(repcache_client_t *cp)
 321 {
 322         client_bucket_t *bp = CLIENT_HASH(cp->rc_id);
 323         uu_list_index_t idx;
 324 
 325         assert(cp->rc_id > 0);
 326 
 327         (void) pthread_mutex_lock(&bp->cb_lock);
 328         /*
 329          * We assume it does not already exist
 330          */
 331         (void) uu_list_find(bp->cb_list, cp, NULL, &idx);
 332         uu_list_insert(bp->cb_list, cp, idx);
 333 
 334         (void) pthread_mutex_unlock(&bp->cb_lock);
 335 }
 336 
 337 static repcache_client_t *
 338 client_lookup(uint32_t id)
 339 {
 340         client_bucket_t *bp = CLIENT_HASH(id);
 341         repcache_client_t *cp;
 342 
 343         (void) pthread_mutex_lock(&bp->cb_lock);
 344 
 345         cp = uu_list_find(bp->cb_list, &id, NULL, NULL);
 346 
 347         /*
 348          * Bump the reference count
 349          */
 350         if (cp != NULL) {
 351                 (void) pthread_mutex_lock(&cp->rc_lock);
 352                 assert(!(cp->rc_flags & RC_CLIENT_DEAD));
 353                 cp->rc_refcnt++;
 354                 (void) pthread_mutex_unlock(&cp->rc_lock);
 355         }
 356         (void) pthread_mutex_unlock(&bp->cb_lock);
 357 
 358         return (cp);
 359 }
 360 
 361 static void
 362 client_release(repcache_client_t *cp)
 363 {
 364         (void) pthread_mutex_lock(&cp->rc_lock);
 365         assert(cp->rc_refcnt > 0);
 366         assert(cp->rc_insert_thr != pthread_self());
 367 
 368         --cp->rc_refcnt;
 369         (void) pthread_cond_broadcast(&cp->rc_cv);
 370         (void) pthread_mutex_unlock(&cp->rc_lock);
 371 }
 372 
 373 /*
 374  * We only allow one thread to be inserting at a time, to prevent
 375  * insert/insert races.
 376  */
 377 static void
 378 client_start_insert(repcache_client_t *cp)
 379 {
 380         (void) pthread_mutex_lock(&cp->rc_lock);
 381         assert(cp->rc_refcnt > 0);
 382 
 383         while (cp->rc_insert_thr != 0) {
 384                 assert(cp->rc_insert_thr != pthread_self());
 385                 (void) pthread_cond_wait(&cp->rc_cv, &cp->rc_lock);
 386         }
 387         cp->rc_insert_thr = pthread_self();
 388         (void) pthread_mutex_unlock(&cp->rc_lock);
 389 }
 390 
 391 static void
 392 client_end_insert(repcache_client_t *cp)
 393 {
 394         (void) pthread_mutex_lock(&cp->rc_lock);
 395         assert(cp->rc_insert_thr == pthread_self());
 396         cp->rc_insert_thr = 0;
 397         (void) pthread_cond_broadcast(&cp->rc_cv);
 398         (void) pthread_mutex_unlock(&cp->rc_lock);
 399 }
 400 
 401 /*ARGSUSED*/
 402 static repcache_entity_t *
 403 entity_alloc(repcache_client_t *cp)
 404 {
 405         repcache_entity_t *ep = uu_zalloc(sizeof (repcache_entity_t));
 406         if (ep != NULL) {
 407                 uu_avl_node_init(ep, &ep->re_link, entity_pool);
 408         }
 409         return (ep);
 410 }
 411 
 412 static void
 413 entity_add(repcache_client_t *cp, repcache_entity_t *ep)
 414 {
 415         uu_avl_index_t idx;
 416 
 417         (void) pthread_mutex_lock(&cp->rc_lock);
 418         assert(cp->rc_insert_thr == pthread_self());
 419 
 420         (void) uu_avl_find(cp->rc_entities, ep, NULL, &idx);
 421         uu_avl_insert(cp->rc_entities, ep, idx);
 422 
 423         (void) pthread_mutex_unlock(&cp->rc_lock);
 424 }
 425 
 426 static repcache_entity_t *
 427 entity_find(repcache_client_t *cp, uint32_t id)
 428 {
 429         repcache_entity_t *ep;
 430 
 431         (void) pthread_mutex_lock(&cp->rc_lock);
 432         ep = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
 433         if (ep != NULL) {
 434                 add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, ep);
 435                 (void) pthread_mutex_lock(&ep->re_lock);
 436         }
 437         (void) pthread_mutex_unlock(&cp->rc_lock);
 438 
 439         return (ep);
 440 }
 441 
 442 /*
 443  * Fails with
 444  *   _DUPLICATE_ID - the ids are equal
 445  *   _UNKNOWN_ID - an id does not designate an active register
 446  */
 447 static int
 448 entity_find2(repcache_client_t *cp, uint32_t id1, repcache_entity_t **out1,
 449     uint32_t id2, repcache_entity_t **out2)
 450 {
 451         repcache_entity_t *e1, *e2;
 452         request_log_entry_t *rlp;
 453 
 454         if (id1 == id2)
 455                 return (REP_PROTOCOL_FAIL_DUPLICATE_ID);
 456 
 457         (void) pthread_mutex_lock(&cp->rc_lock);
 458         e1 = uu_avl_find(cp->rc_entities, &id1, NULL, NULL);
 459         e2 = uu_avl_find(cp->rc_entities, &id2, NULL, NULL);
 460         if (e1 == NULL || e2 == NULL) {
 461                 (void) pthread_mutex_unlock(&cp->rc_lock);
 462                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
 463         }
 464 
 465         assert(e1 != e2);
 466 
 467         /*
 468          * locks are ordered by id number
 469          */
 470         if (id1 < id2) {
 471                 (void) pthread_mutex_lock(&e1->re_lock);
 472                 (void) pthread_mutex_lock(&e2->re_lock);
 473         } else {
 474                 (void) pthread_mutex_lock(&e2->re_lock);
 475                 (void) pthread_mutex_lock(&e1->re_lock);
 476         }
 477         *out1 = e1;
 478         *out2 = e2;
 479 
 480         (void) pthread_mutex_unlock(&cp->rc_lock);
 481 
 482         if ((rlp = get_log()) != NULL) {
 483                 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, id1, e1);
 484                 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, id2, e2);
 485         }
 486 
 487         return (REP_PROTOCOL_SUCCESS);
 488 }
 489 
 490 static void
 491 entity_release(repcache_entity_t *ep)
 492 {
 493         assert(ep->re_node.rnp_node == NULL ||
 494             !MUTEX_HELD(&ep->re_node.rnp_node->rn_lock));
 495         (void) pthread_mutex_unlock(&ep->re_lock);
 496 }
 497 
 498 static void
 499 entity_destroy(repcache_entity_t *entity)
 500 {
 501         (void) pthread_mutex_lock(&entity->re_lock);
 502         rc_node_clear(&entity->re_node, 0);
 503         (void) pthread_mutex_unlock(&entity->re_lock);
 504 
 505         uu_avl_node_fini(entity, &entity->re_link, entity_pool);
 506         (void) pthread_mutex_destroy(&entity->re_lock);
 507         rc_node_ptr_free_mem(&entity->re_node);
 508         uu_free(entity);
 509 }
 510 
 511 static void
 512 entity_remove(repcache_client_t *cp, uint32_t id)
 513 {
 514         repcache_entity_t *entity;
 515 
 516         (void) pthread_mutex_lock(&cp->rc_lock);
 517         entity = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
 518         if (entity != NULL) {
 519                 add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, entity);
 520 
 521                 uu_avl_remove(cp->rc_entities, entity);
 522         }
 523         (void) pthread_mutex_unlock(&cp->rc_lock);
 524 
 525         if (entity != NULL)
 526                 entity_destroy(entity);
 527 }
 528 
 529 static void
 530 entity_cleanup(repcache_client_t *cp)
 531 {
 532         repcache_entity_t *ep;
 533         void *cookie = NULL;
 534 
 535         (void) pthread_mutex_lock(&cp->rc_lock);
 536         while ((ep = uu_avl_teardown(cp->rc_entities, &cookie)) != NULL) {
 537                 (void) pthread_mutex_unlock(&cp->rc_lock);
 538                 entity_destroy(ep);
 539                 (void) pthread_mutex_lock(&cp->rc_lock);
 540         }
 541         (void) pthread_mutex_unlock(&cp->rc_lock);
 542 }
 543 
 544 /*ARGSUSED*/
 545 static repcache_iter_t *
 546 iter_alloc(repcache_client_t *cp)
 547 {
 548         repcache_iter_t *iter;
 549         iter = uu_zalloc(sizeof (repcache_iter_t));
 550         if (iter != NULL)
 551                 uu_avl_node_init(iter, &iter->ri_link, iter_pool);
 552         return (iter);
 553 }
 554 
 555 static void
 556 iter_add(repcache_client_t *cp, repcache_iter_t *iter)
 557 {
 558         uu_list_index_t idx;
 559 
 560         (void) pthread_mutex_lock(&cp->rc_lock);
 561         assert(cp->rc_insert_thr == pthread_self());
 562 
 563         (void) uu_avl_find(cp->rc_iters, iter, NULL, &idx);
 564         uu_avl_insert(cp->rc_iters, iter, idx);
 565 
 566         (void) pthread_mutex_unlock(&cp->rc_lock);
 567 }
 568 
 569 static repcache_iter_t *
 570 iter_find(repcache_client_t *cp, uint32_t id)
 571 {
 572         repcache_iter_t *iter;
 573 
 574         (void) pthread_mutex_lock(&cp->rc_lock);
 575 
 576         iter = uu_avl_find(cp->rc_iters, &id, NULL, NULL);
 577         if (iter != NULL) {
 578                 add_log_ptr(get_log(), RC_PTR_TYPE_ITER, id, iter);
 579                 (void) pthread_mutex_lock(&iter->ri_lock);
 580         }
 581         (void) pthread_mutex_unlock(&cp->rc_lock);
 582 
 583         return (iter);
 584 }
 585 
 586 /*
 587  * Fails with
 588  *   _UNKNOWN_ID - iter_id or entity_id does not designate an active register
 589  */
 590 static int
 591 iter_find_w_entity(repcache_client_t *cp, uint32_t iter_id,
 592     repcache_iter_t **iterp, uint32_t entity_id, repcache_entity_t **epp)
 593 {
 594         repcache_iter_t *iter;
 595         repcache_entity_t *ep;
 596         request_log_entry_t *rlp;
 597 
 598         (void) pthread_mutex_lock(&cp->rc_lock);
 599         iter = uu_avl_find(cp->rc_iters, &iter_id, NULL, NULL);
 600         ep = uu_avl_find(cp->rc_entities, &entity_id, NULL, NULL);
 601 
 602         assert(iter == NULL || !MUTEX_HELD(&iter->ri_lock));
 603         assert(ep == NULL || !MUTEX_HELD(&ep->re_lock));
 604 
 605         if (iter == NULL || ep == NULL) {
 606                 (void) pthread_mutex_unlock(&cp->rc_lock);
 607                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
 608         }
 609 
 610         (void) pthread_mutex_lock(&iter->ri_lock);
 611         (void) pthread_mutex_lock(&ep->re_lock);
 612 
 613         (void) pthread_mutex_unlock(&cp->rc_lock);
 614 
 615         *iterp = iter;
 616         *epp = ep;
 617 
 618         if ((rlp = get_log()) != NULL) {
 619                 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, entity_id, ep);
 620                 add_log_ptr(rlp, RC_PTR_TYPE_ITER, iter_id, iter);
 621         }
 622 
 623         return (REP_PROTOCOL_SUCCESS);
 624 }
 625 
 626 static void
 627 iter_release(repcache_iter_t *iter)
 628 {
 629         (void) pthread_mutex_unlock(&iter->ri_lock);
 630 }
 631 
 632 static void
 633 iter_destroy(repcache_iter_t *iter)
 634 {
 635         (void) pthread_mutex_lock(&iter->ri_lock);
 636         rc_iter_destroy(&iter->ri_iter);
 637         (void) pthread_mutex_unlock(&iter->ri_lock);
 638 
 639         uu_avl_node_fini(iter, &iter->ri_link, iter_pool);
 640         (void) pthread_mutex_destroy(&iter->ri_lock);
 641         uu_free(iter);
 642 }
 643 
 644 static void
 645 iter_remove(repcache_client_t *cp, uint32_t id)
 646 {
 647         repcache_iter_t *iter;
 648 
 649         (void) pthread_mutex_lock(&cp->rc_lock);
 650         iter = uu_avl_find(cp->rc_iters, &id, NULL, NULL);
 651         if (iter != NULL)
 652                 uu_avl_remove(cp->rc_iters, iter);
 653         (void) pthread_mutex_unlock(&cp->rc_lock);
 654 
 655         if (iter != NULL)
 656                 iter_destroy(iter);
 657 }
 658 
 659 static void
 660 iter_cleanup(repcache_client_t *cp)
 661 {
 662         repcache_iter_t *iter;
 663         void *cookie = NULL;
 664 
 665         (void) pthread_mutex_lock(&cp->rc_lock);
 666         while ((iter = uu_avl_teardown(cp->rc_iters, &cookie)) != NULL) {
 667                 (void) pthread_mutex_unlock(&cp->rc_lock);
 668                 iter_destroy(iter);
 669                 (void) pthread_mutex_lock(&cp->rc_lock);
 670         }
 671         (void) pthread_mutex_unlock(&cp->rc_lock);
 672 }
 673 
 674 /*
 675  * Ensure that the passed client id is no longer usable, wait for any
 676  * outstanding invocations to complete, then destroy the client
 677  * structure.
 678  */
 679 static void
 680 client_destroy(uint32_t id)
 681 {
 682         client_bucket_t *bp = CLIENT_HASH(id);
 683         repcache_client_t *cp;
 684 
 685         (void) pthread_mutex_lock(&bp->cb_lock);
 686 
 687         cp = uu_list_find(bp->cb_list, &id, NULL, NULL);
 688 
 689         if (cp == NULL) {
 690                 (void) pthread_mutex_unlock(&bp->cb_lock);
 691                 return;
 692         }
 693 
 694         uu_list_remove(bp->cb_list, cp);
 695 
 696         (void) pthread_mutex_unlock(&bp->cb_lock);
 697 
 698         /* kick the waiters out */
 699         rc_notify_info_fini(&cp->rc_notify_info);
 700 
 701         (void) pthread_mutex_lock(&cp->rc_lock);
 702         assert(!(cp->rc_flags & RC_CLIENT_DEAD));
 703         cp->rc_flags |= RC_CLIENT_DEAD;
 704 
 705         if (cp->rc_doorfd != -1) {
 706                 if (door_revoke(cp->rc_doorfd) < 0)
 707                         perror("door_revoke");
 708                 cp->rc_doorfd = -1;
 709                 cp->rc_doorid = INVALID_DOORID;
 710         }
 711 
 712         while (cp->rc_refcnt > 0)
 713                 (void) pthread_cond_wait(&cp->rc_cv, &cp->rc_lock);
 714 
 715         assert(cp->rc_insert_thr == 0 && cp->rc_notify_thr == 0);
 716         (void) pthread_mutex_unlock(&cp->rc_lock);
 717 
 718         /*
 719          * destroy outstanding objects
 720          */
 721         entity_cleanup(cp);
 722         iter_cleanup(cp);
 723 
 724         /*
 725          * clean up notifications
 726          */
 727         rc_pg_notify_fini(&cp->rc_pg_notify);
 728 
 729         /*
 730          * clean up annotations
 731          */
 732         if (cp->rc_operation != NULL)
 733                 free((void *)cp->rc_operation);
 734         if (cp->rc_file != NULL)
 735                 free((void *)cp->rc_file);
 736 
 737         /*
 738          * End audit session.
 739          */
 740 #ifndef NATIVE_BUILD
 741         (void) adt_end_session(cp->rc_adt_session);
 742 #endif
 743 
 744         client_free(cp);
 745 }
 746 
 747 /*
 748  * Fails with
 749  *   _TYPE_MISMATCH - the entity is already set up with a different type
 750  *   _NO_RESOURCES - out of memory
 751  */
 752 static int
 753 entity_setup(repcache_client_t *cp, struct rep_protocol_entity_setup *rpr)
 754 {
 755         repcache_entity_t *ep;
 756         uint32_t type;
 757 
 758         client_start_insert(cp);
 759 
 760         if ((ep = entity_find(cp, rpr->rpr_entityid)) != NULL) {
 761                 type = ep->re_type;
 762                 entity_release(ep);
 763 
 764                 client_end_insert(cp);
 765 
 766                 if (type != rpr->rpr_entitytype)
 767                         return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
 768                 return (REP_PROTOCOL_SUCCESS);
 769         }
 770 
 771         switch (type = rpr->rpr_entitytype) {
 772         case REP_PROTOCOL_ENTITY_SCOPE:
 773         case REP_PROTOCOL_ENTITY_SERVICE:
 774         case REP_PROTOCOL_ENTITY_INSTANCE:
 775         case REP_PROTOCOL_ENTITY_SNAPSHOT:
 776         case REP_PROTOCOL_ENTITY_SNAPLEVEL:
 777         case REP_PROTOCOL_ENTITY_PROPERTYGRP:
 778         case REP_PROTOCOL_ENTITY_PROPERTY:
 779                 break;
 780         default:
 781                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
 782         }
 783 
 784         ep = entity_alloc(cp);
 785         if (ep == NULL) {
 786                 client_end_insert(cp);
 787                 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
 788         }
 789 
 790         ep->re_id = rpr->rpr_entityid;
 791         ep->re_changeid = INVALID_CHANGEID;
 792 
 793         ep->re_type = type;
 794         rc_node_ptr_init(&ep->re_node);
 795 
 796         entity_add(cp, ep);
 797         client_end_insert(cp);
 798         return (REP_PROTOCOL_SUCCESS);
 799 }
 800 
 801 /*ARGSUSED*/
 802 static void
 803 entity_name(repcache_client_t *cp, const void *in, size_t insz, void *out_arg,
 804     size_t *outsz, void *arg)
 805 {
 806         const struct rep_protocol_entity_name *rpr = in;
 807         struct rep_protocol_name_response *out = out_arg;
 808         repcache_entity_t *ep;
 809         size_t sz = sizeof (out->rpr_name);
 810 
 811         assert(*outsz == sizeof (*out));
 812 
 813         ep = entity_find(cp, rpr->rpr_entityid);
 814 
 815         if (ep == NULL) {
 816                 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
 817                 *outsz = sizeof (out->rpr_response);
 818                 return;
 819         }
 820         out->rpr_response = rc_node_name(&ep->re_node, out->rpr_name,
 821             sz, rpr->rpr_answertype, &sz);
 822         entity_release(ep);
 823 
 824         /*
 825          * If we fail, we only return the response code.
 826          * If we succeed, we don't return anything after the '\0' in rpr_name.
 827          */
 828         if (out->rpr_response != REP_PROTOCOL_SUCCESS)
 829                 *outsz = sizeof (out->rpr_response);
 830         else
 831                 *outsz = offsetof(struct rep_protocol_name_response,
 832                     rpr_name[sz + 1]);
 833 }
 834 
 835 /*ARGSUSED*/
 836 static void
 837 entity_parent_type(repcache_client_t *cp, const void *in, size_t insz,
 838     void *out_arg, size_t *outsz, void *arg)
 839 {
 840         const struct rep_protocol_entity_name *rpr = in;
 841         struct rep_protocol_integer_response *out = out_arg;
 842         repcache_entity_t *ep;
 843 
 844         assert(*outsz == sizeof (*out));
 845 
 846         ep = entity_find(cp, rpr->rpr_entityid);
 847 
 848         if (ep == NULL) {
 849                 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
 850                 *outsz = sizeof (out->rpr_response);
 851                 return;
 852         }
 853 
 854         out->rpr_response = rc_node_parent_type(&ep->re_node, &out->rpr_value);
 855         entity_release(ep);
 856 
 857         if (out->rpr_response != REP_PROTOCOL_SUCCESS)
 858                 *outsz = sizeof (out->rpr_response);
 859 }
 860 
 861 /*
 862  * Fails with
 863  *   _DUPLICATE_ID - the ids are equal
 864  *   _UNKNOWN_ID - an id does not designate an active register
 865  *   _INVALID_TYPE - type is invalid
 866  *   _TYPE_MISMATCH - np doesn't carry children of type type
 867  *   _DELETED - np has been deleted
 868  *   _NOT_FOUND - no child with that name/type combo found
 869  *   _NO_RESOURCES
 870  *   _BACKEND_ACCESS
 871  */
 872 static int
 873 entity_get_child(repcache_client_t *cp,
 874     struct rep_protocol_entity_get_child *rpr)
 875 {
 876         repcache_entity_t *parent, *child;
 877         int result;
 878 
 879         uint32_t parentid = rpr->rpr_entityid;
 880         uint32_t childid = rpr->rpr_childid;
 881 
 882         result = entity_find2(cp, childid, &child, parentid, &parent);
 883         if (result != REP_PROTOCOL_SUCCESS)
 884                 return (result);
 885 
 886         rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
 887 
 888         result = rc_node_get_child(&parent->re_node, rpr->rpr_name,
 889             child->re_type, &child->re_node);
 890 
 891         entity_release(child);
 892         entity_release(parent);
 893 
 894         return (result);
 895 }
 896 
 897 /*
 898  * Returns _FAIL_DUPLICATE_ID, _FAIL_UNKNOWN_ID, _FAIL_NOT_SET, _FAIL_DELETED,
 899  * _FAIL_TYPE_MISMATCH, _FAIL_NOT_FOUND (scope has no parent), or _SUCCESS.
 900  * Fails with
 901  *   _DUPLICATE_ID - the ids are equal
 902  *   _UNKNOWN_ID - an id does not designate an active register
 903  *   _NOT_SET - child is not set
 904  *   _DELETED - child has been deleted
 905  *   _TYPE_MISMATCH - child's parent does not match that of the parent register
 906  *   _NOT_FOUND - child has no parent (and is a scope)
 907  */
 908 static int
 909 entity_get_parent(repcache_client_t *cp, struct rep_protocol_entity_parent *rpr)
 910 {
 911         repcache_entity_t *child, *parent;
 912         int result;
 913 
 914         uint32_t childid = rpr->rpr_entityid;
 915         uint32_t outid = rpr->rpr_outid;
 916 
 917         result = entity_find2(cp, childid, &child, outid, &parent);
 918         if (result != REP_PROTOCOL_SUCCESS)
 919                 return (result);
 920 
 921         result = rc_node_get_parent(&child->re_node, parent->re_type,
 922             &parent->re_node);
 923 
 924         entity_release(child);
 925         entity_release(parent);
 926 
 927         return (result);
 928 }
 929 
 930 static int
 931 entity_get(repcache_client_t *cp, struct rep_protocol_entity_get *rpr)
 932 {
 933         repcache_entity_t *ep;
 934         int result;
 935 
 936         ep = entity_find(cp, rpr->rpr_entityid);
 937 
 938         if (ep == NULL)
 939                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
 940 
 941         switch (rpr->rpr_object) {
 942         case RP_ENTITY_GET_INVALIDATE:
 943                 rc_node_clear(&ep->re_node, 0);
 944                 result = REP_PROTOCOL_SUCCESS;
 945                 break;
 946         case RP_ENTITY_GET_MOST_LOCAL_SCOPE:
 947                 result = rc_local_scope(ep->re_type, &ep->re_node);
 948                 break;
 949         default:
 950                 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
 951                 break;
 952         }
 953 
 954         entity_release(ep);
 955 
 956         return (result);
 957 }
 958 
 959 static int
 960 entity_update(repcache_client_t *cp, struct rep_protocol_entity_update *rpr)
 961 {
 962         repcache_entity_t *ep;
 963         int result;
 964 
 965         if (rpr->rpr_changeid == INVALID_CHANGEID)
 966                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
 967 
 968         ep = entity_find(cp, rpr->rpr_entityid);
 969 
 970         if (ep == NULL)
 971                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
 972 
 973         if (ep->re_changeid == rpr->rpr_changeid) {
 974                 result = REP_PROTOCOL_DONE;
 975         } else {
 976                 result = rc_node_update(&ep->re_node);
 977                 if (result == REP_PROTOCOL_DONE)
 978                         ep->re_changeid = rpr->rpr_changeid;
 979         }
 980 
 981         entity_release(ep);
 982 
 983         return (result);
 984 }
 985 
 986 static int
 987 entity_reset(repcache_client_t *cp, struct rep_protocol_entity_reset *rpr)
 988 {
 989         repcache_entity_t *ep;
 990 
 991         ep = entity_find(cp, rpr->rpr_entityid);
 992         if (ep == NULL)
 993                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
 994 
 995         rc_node_clear(&ep->re_node, 0);
 996         ep->re_txstate = REPCACHE_TX_INIT;
 997 
 998         entity_release(ep);
 999         return (REP_PROTOCOL_SUCCESS);
1000 }
1001 
1002 /*
1003  * Fails with
1004  *   _BAD_REQUEST - request has invalid changeid
1005  *                  rpr_name is invalid
1006  *                  cannot create children for parent's type of node
1007  *   _DUPLICATE_ID - request has duplicate ids
1008  *   _UNKNOWN_ID - request has unknown id
1009  *   _DELETED - parent has been deleted
1010  *   _NOT_SET - parent is reset
1011  *   _NOT_APPLICABLE - rpr_childtype is _PROPERTYGRP
1012  *   _INVALID_TYPE - parent is corrupt or rpr_childtype is invalid
1013  *   _TYPE_MISMATCH - parent cannot have children of type rpr_childtype
1014  *   _NO_RESOURCES
1015  *   _PERMISSION_DENIED
1016  *   _BACKEND_ACCESS
1017  *   _BACKEND_READONLY
1018  *   _EXISTS - child already exists
1019  */
1020 static int
1021 entity_create_child(repcache_client_t *cp,
1022     struct rep_protocol_entity_create_child *rpr)
1023 {
1024         repcache_entity_t *parent;
1025         repcache_entity_t *child;
1026 
1027         uint32_t parentid = rpr->rpr_entityid;
1028         uint32_t childid = rpr->rpr_childid;
1029 
1030         int result;
1031 
1032         if (rpr->rpr_changeid == INVALID_CHANGEID)
1033                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1034 
1035         result = entity_find2(cp, parentid, &parent, childid, &child);
1036         if (result != REP_PROTOCOL_SUCCESS)
1037                 return (result);
1038 
1039         rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1040 
1041         if (child->re_changeid == rpr->rpr_changeid) {
1042                 result = REP_PROTOCOL_SUCCESS;
1043         } else {
1044                 result = rc_node_create_child(&parent->re_node,
1045                     rpr->rpr_childtype, rpr->rpr_name, &child->re_node);
1046                 if (result == REP_PROTOCOL_SUCCESS)
1047                         child->re_changeid = rpr->rpr_changeid;
1048         }
1049 
1050         entity_release(parent);
1051         entity_release(child);
1052 
1053         return (result);
1054 }
1055 
1056 static int
1057 entity_create_pg(repcache_client_t *cp,
1058     struct rep_protocol_entity_create_pg *rpr)
1059 {
1060         repcache_entity_t *parent;
1061         repcache_entity_t *child;
1062 
1063         uint32_t parentid = rpr->rpr_entityid;
1064         uint32_t childid = rpr->rpr_childid;
1065 
1066         int result;
1067 
1068         if (rpr->rpr_changeid == INVALID_CHANGEID)
1069                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1070 
1071         result = entity_find2(cp, parentid, &parent, childid, &child);
1072         if (result != REP_PROTOCOL_SUCCESS)
1073                 return (result);
1074 
1075         rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1076         rpr->rpr_type[sizeof (rpr->rpr_type) - 1] = 0;
1077 
1078         if (child->re_changeid == rpr->rpr_changeid) {
1079                 result = REP_PROTOCOL_SUCCESS;
1080         } else {
1081                 result = rc_node_create_child_pg(&parent->re_node,
1082                     child->re_type, rpr->rpr_name, rpr->rpr_type,
1083                     rpr->rpr_flags, &child->re_node);
1084                 if (result == REP_PROTOCOL_SUCCESS)
1085                         child->re_changeid = rpr->rpr_changeid;
1086         }
1087 
1088         entity_release(parent);
1089         entity_release(child);
1090 
1091         return (result);
1092 }
1093 
1094 static int
1095 entity_delete(repcache_client_t *cp,
1096     struct rep_protocol_entity_delete *rpr)
1097 {
1098         repcache_entity_t *entity;
1099 
1100         uint32_t entityid = rpr->rpr_entityid;
1101 
1102         int result;
1103 
1104         if (rpr->rpr_changeid == INVALID_CHANGEID)
1105                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1106 
1107         entity = entity_find(cp, entityid);
1108 
1109         if (entity == NULL)
1110                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1111 
1112         if (entity->re_changeid == rpr->rpr_changeid) {
1113                 result = REP_PROTOCOL_SUCCESS;
1114         } else {
1115                 result = rc_node_delete(&entity->re_node);
1116                 if (result == REP_PROTOCOL_SUCCESS)
1117                         entity->re_changeid = rpr->rpr_changeid;
1118         }
1119 
1120         entity_release(entity);
1121 
1122         return (result);
1123 }
1124 
1125 static rep_protocol_responseid_t
1126 entity_teardown(repcache_client_t *cp, struct rep_protocol_entity_teardown *rpr)
1127 {
1128         entity_remove(cp, rpr->rpr_entityid);
1129 
1130         return (REP_PROTOCOL_SUCCESS);
1131 }
1132 
1133 /*
1134  * Fails with
1135  *   _MISORDERED - the iterator exists and is not reset
1136  *   _NO_RESOURCES - out of memory
1137  */
1138 static int
1139 iter_setup(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1140 {
1141         repcache_iter_t *iter;
1142         uint32_t sequence;
1143 
1144         client_start_insert(cp);
1145         /*
1146          * If the iter already exists, and hasn't been read from,
1147          * we assume the previous call succeeded.
1148          */
1149         if ((iter = iter_find(cp, rpr->rpr_iterid)) != NULL) {
1150                 sequence = iter->ri_sequence;
1151                 iter_release(iter);
1152 
1153                 client_end_insert(cp);
1154 
1155                 if (sequence != 0)
1156                         return (REP_PROTOCOL_FAIL_MISORDERED);
1157                 return (REP_PROTOCOL_SUCCESS);
1158         }
1159 
1160         iter = iter_alloc(cp);
1161         if (iter == NULL) {
1162                 client_end_insert(cp);
1163                 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1164         }
1165 
1166         iter->ri_id = rpr->rpr_iterid;
1167         iter->ri_type = REP_PROTOCOL_TYPE_INVALID;
1168         iter->ri_sequence = 0;
1169         iter_add(cp, iter);
1170 
1171         client_end_insert(cp);
1172         return (REP_PROTOCOL_SUCCESS);
1173 }
1174 
1175 /*
1176  * Fails with
1177  *   _UNKNOWN_ID
1178  *   _MISORDERED - iterator has already been started
1179  *   _NOT_SET
1180  *   _DELETED
1181  *   _TYPE_MISMATCH - entity cannot have type children
1182  *   _BAD_REQUEST - rpr_flags is invalid
1183  *                  rpr_pattern is invalid
1184  *   _NO_RESOURCES
1185  *   _INVALID_TYPE
1186  *   _BACKEND_ACCESS
1187  */
1188 static int
1189 iter_start(repcache_client_t *cp, struct rep_protocol_iter_start *rpr)
1190 {
1191         int result;
1192         repcache_iter_t *iter;
1193         repcache_entity_t *ep;
1194 
1195         result = iter_find_w_entity(cp, rpr->rpr_iterid, &iter,
1196             rpr->rpr_entity, &ep);
1197 
1198         if (result != REP_PROTOCOL_SUCCESS)
1199                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1200 
1201         if (iter->ri_sequence > 1) {
1202                 result = REP_PROTOCOL_FAIL_MISORDERED;
1203                 goto end;
1204         }
1205 
1206         if (iter->ri_sequence == 1) {
1207                 result = REP_PROTOCOL_SUCCESS;
1208                 goto end;
1209         }
1210 
1211         rpr->rpr_pattern[sizeof (rpr->rpr_pattern) - 1] = 0;
1212 
1213         result = rc_node_setup_iter(&ep->re_node, &iter->ri_iter,
1214             rpr->rpr_itertype, rpr->rpr_flags, rpr->rpr_pattern);
1215 
1216         if (result == REP_PROTOCOL_SUCCESS)
1217                 iter->ri_sequence++;
1218 
1219 end:
1220         iter_release(iter);
1221         entity_release(ep);
1222         return (result);
1223 }
1224 
1225 /*
1226  * Returns
1227  *   _UNKNOWN_ID
1228  *   _NOT_SET - iter has not been started
1229  *   _MISORDERED
1230  *   _BAD_REQUEST - iter walks values
1231  *   _TYPE_MISMATCH - iter does not walk type entities
1232  *   _DELETED - parent was deleted
1233  *   _NO_RESOURCES
1234  *   _INVALID_TYPE - type is invalid
1235  *   _DONE
1236  *   _SUCCESS
1237  *
1238  * For composed property group iterators, can also return
1239  *   _TYPE_MISMATCH - parent cannot have type children
1240  *   _BACKEND_ACCESS
1241  */
1242 static rep_protocol_responseid_t
1243 iter_read(repcache_client_t *cp, struct rep_protocol_iter_read *rpr)
1244 {
1245         rep_protocol_responseid_t result;
1246         repcache_iter_t *iter;
1247         repcache_entity_t *ep;
1248         uint32_t sequence;
1249 
1250         result = iter_find_w_entity(cp, rpr->rpr_iterid, &iter,
1251             rpr->rpr_entityid, &ep);
1252 
1253         if (result != REP_PROTOCOL_SUCCESS)
1254                 return (result);
1255 
1256         sequence = rpr->rpr_sequence;
1257 
1258         if (iter->ri_sequence == 0) {
1259                 iter_release(iter);
1260                 entity_release(ep);
1261                 return (REP_PROTOCOL_FAIL_NOT_SET);
1262         }
1263 
1264         if (sequence == 1) {
1265                 iter_release(iter);
1266                 entity_release(ep);
1267                 return (REP_PROTOCOL_FAIL_MISORDERED);
1268         }
1269 
1270         if (sequence == iter->ri_sequence) {
1271                 iter_release(iter);
1272                 entity_release(ep);
1273                 return (REP_PROTOCOL_SUCCESS);
1274         }
1275 
1276         if (sequence == iter->ri_sequence + 1) {
1277                 result = rc_iter_next(iter->ri_iter, &ep->re_node,
1278                     ep->re_type);
1279 
1280                 if (result == REP_PROTOCOL_SUCCESS)
1281                         iter->ri_sequence++;
1282 
1283                 iter_release(iter);
1284                 entity_release(ep);
1285 
1286                 return (result);
1287         }
1288 
1289         iter_release(iter);
1290         entity_release(ep);
1291         return (REP_PROTOCOL_FAIL_MISORDERED);
1292 }
1293 
1294 /*ARGSUSED*/
1295 static void
1296 iter_read_value(repcache_client_t *cp, const void *in, size_t insz,
1297     void *out_arg, size_t *outsz, void *arg)
1298 {
1299         const struct rep_protocol_iter_read_value *rpr = in;
1300         struct rep_protocol_value_response *out = out_arg;
1301         rep_protocol_responseid_t result;
1302 
1303         repcache_iter_t *iter;
1304         uint32_t sequence;
1305         int repeat;
1306 
1307         assert(*outsz == sizeof (*out));
1308 
1309         iter = iter_find(cp, rpr->rpr_iterid);
1310 
1311         if (iter == NULL) {
1312                 result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1313                 goto out;
1314         }
1315 
1316         sequence = rpr->rpr_sequence;
1317 
1318         if (iter->ri_sequence == 0) {
1319                 iter_release(iter);
1320                 result = REP_PROTOCOL_FAIL_NOT_SET;
1321                 goto out;
1322         }
1323 
1324         repeat = (sequence == iter->ri_sequence);
1325 
1326         if (sequence == 1 || (!repeat && sequence != iter->ri_sequence + 1)) {
1327                 iter_release(iter);
1328                 result = REP_PROTOCOL_FAIL_MISORDERED;
1329                 goto out;
1330         }
1331 
1332         result = rc_iter_next_value(iter->ri_iter, out, outsz, repeat);
1333 
1334         if (!repeat && result == REP_PROTOCOL_SUCCESS)
1335                 iter->ri_sequence++;
1336 
1337         iter_release(iter);
1338 
1339 out:
1340         /*
1341          * If we fail, we only return the response code.
1342          * If we succeed, rc_iter_next_value has shortened *outsz
1343          * to only include the value bytes needed.
1344          */
1345         if (result != REP_PROTOCOL_SUCCESS && result != REP_PROTOCOL_DONE)
1346                 *outsz = sizeof (out->rpr_response);
1347 
1348         out->rpr_response = result;
1349 }
1350 
1351 static int
1352 iter_reset(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1353 {
1354         repcache_iter_t *iter = iter_find(cp, rpr->rpr_iterid);
1355 
1356         if (iter == NULL)
1357                 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1358 
1359         if (iter->ri_sequence != 0) {
1360                 iter->ri_sequence = 0;
1361                 rc_iter_destroy(&iter->ri_iter);
1362         }
1363         iter_release(iter);
1364         return (REP_PROTOCOL_SUCCESS);
1365 }
1366 
1367 static rep_protocol_responseid_t
1368 iter_teardown(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1369 {
1370         iter_remove(cp, rpr->rpr_iterid);
1371 
1372         return (REP_PROTOCOL_SUCCESS);
1373 }
1374 
1375 static rep_protocol_responseid_t
1376 tx_start(repcache_client_t *cp, struct rep_protocol_transaction_start *rpr)
1377 {
1378         repcache_entity_t *tx;
1379         repcache_entity_t *ep;
1380         rep_protocol_responseid_t result;
1381 
1382         uint32_t txid = rpr->rpr_entityid_tx;
1383         uint32_t epid = rpr->rpr_entityid;
1384 
1385         result = entity_find2(cp, txid, &tx, epid, &ep);
1386         if (result != REP_PROTOCOL_SUCCESS)
1387                 return (result);
1388 
1389         if (tx->re_txstate == REPCACHE_TX_SETUP) {
1390                 result = REP_PROTOCOL_SUCCESS;
1391                 goto end;
1392         }
1393         if (tx->re_txstate != REPCACHE_TX_INIT) {
1394                 result = REP_PROTOCOL_FAIL_MISORDERED;
1395                 goto end;
1396         }
1397 
1398         result = rc_node_setup_tx(&ep->re_node, &tx->re_node);
1399 
1400 end:
1401         if (result == REP_PROTOCOL_SUCCESS)
1402                 tx->re_txstate = REPCACHE_TX_SETUP;
1403         else
1404                 rc_node_clear(&tx->re_node, 0);
1405 
1406         entity_release(ep);
1407         entity_release(tx);
1408         return (result);
1409 }
1410 
1411 /*ARGSUSED*/
1412 static void
1413 tx_commit(repcache_client_t *cp, const void *in, size_t insz,
1414     void *out_arg, size_t *outsz, void *arg)
1415 {
1416         struct rep_protocol_response *out = out_arg;
1417         const struct rep_protocol_transaction_commit *rpr = in;
1418         repcache_entity_t *tx;
1419 
1420         assert(*outsz == sizeof (*out));
1421         assert(insz >= REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE);
1422 
1423         if (rpr->rpr_size != insz) {
1424                 out->rpr_response = REP_PROTOCOL_FAIL_BAD_REQUEST;
1425                 return;
1426         }
1427 
1428         tx = entity_find(cp, rpr->rpr_entityid);
1429 
1430         if (tx == NULL) {
1431                 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1432                 return;
1433         }
1434 
1435         switch (tx->re_txstate) {
1436         case REPCACHE_TX_INIT:
1437                 out->rpr_response = REP_PROTOCOL_FAIL_MISORDERED;
1438                 break;
1439 
1440         case REPCACHE_TX_SETUP:
1441                 out->rpr_response = rc_tx_commit(&tx->re_node, rpr->rpr_cmd,
1442                     insz - REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE);
1443 
1444                 if (out->rpr_response == REP_PROTOCOL_SUCCESS) {
1445                         tx->re_txstate = REPCACHE_TX_COMMITTED;
1446                         rc_node_clear(&tx->re_node, 0);
1447                 }
1448 
1449                 break;
1450         case REPCACHE_TX_COMMITTED:
1451                 out->rpr_response = REP_PROTOCOL_SUCCESS;
1452                 break;
1453         default:
1454                 assert(0);      /* CAN'T HAPPEN */
1455                 break;
1456         }
1457 
1458         entity_release(tx);
1459 }
1460 
1461 static rep_protocol_responseid_t
1462 next_snaplevel(repcache_client_t *cp, struct rep_protocol_entity_pair *rpr)
1463 {
1464         repcache_entity_t *src;
1465         repcache_entity_t *dest;
1466 
1467         uint32_t srcid = rpr->rpr_entity_src;
1468         uint32_t destid = rpr->rpr_entity_dst;
1469 
1470         int result;
1471 
1472         result = entity_find2(cp, srcid, &src, destid, &dest);
1473         if (result != REP_PROTOCOL_SUCCESS)
1474                 return (result);
1475 
1476         result = rc_node_next_snaplevel(&src->re_node, &dest->re_node);
1477 
1478         entity_release(src);
1479         entity_release(dest);
1480 
1481         return (result);
1482 }
1483 
1484 static rep_protocol_responseid_t
1485 snapshot_take(repcache_client_t *cp, struct rep_protocol_snapshot_take *rpr)
1486 {
1487         repcache_entity_t *src;
1488         uint32_t srcid = rpr->rpr_entityid_src;
1489         repcache_entity_t *dest;
1490         uint32_t destid = rpr->rpr_entityid_dest;
1491 
1492         int result;
1493 
1494         result = entity_find2(cp, srcid, &src, destid, &dest);
1495         if (result != REP_PROTOCOL_SUCCESS)
1496                 return (result);
1497 
1498         if (dest->re_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
1499                 result = REP_PROTOCOL_FAIL_TYPE_MISMATCH;
1500         } else {
1501                 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1502 
1503                 if (rpr->rpr_flags == REP_SNAPSHOT_NEW)
1504                         result = rc_snapshot_take_new(&src->re_node, NULL,
1505                             NULL, rpr->rpr_name, &dest->re_node);
1506                 else if (rpr->rpr_flags == REP_SNAPSHOT_ATTACH &&
1507                     rpr->rpr_name[0] == 0)
1508                         result = rc_snapshot_take_attach(&src->re_node,
1509                             &dest->re_node);
1510                 else
1511                         result = REP_PROTOCOL_FAIL_BAD_REQUEST;
1512         }
1513         entity_release(src);
1514         entity_release(dest);
1515 
1516         return (result);
1517 }
1518 
1519 static rep_protocol_responseid_t
1520 snapshot_take_named(repcache_client_t *cp,
1521     struct rep_protocol_snapshot_take_named *rpr)
1522 {
1523         repcache_entity_t *src;
1524         uint32_t srcid = rpr->rpr_entityid_src;
1525         repcache_entity_t *dest;
1526         uint32_t destid = rpr->rpr_entityid_dest;
1527 
1528         int result;
1529 
1530         result = entity_find2(cp, srcid, &src, destid, &dest);
1531         if (result != REP_PROTOCOL_SUCCESS)
1532                 return (result);
1533 
1534         if (dest->re_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
1535                 result = REP_PROTOCOL_FAIL_TYPE_MISMATCH;
1536         } else {
1537                 rpr->rpr_svcname[sizeof (rpr->rpr_svcname) - 1] = 0;
1538                 rpr->rpr_instname[sizeof (rpr->rpr_instname) - 1] = 0;
1539                 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1540 
1541                 result = rc_snapshot_take_new(&src->re_node, rpr->rpr_svcname,
1542                     rpr->rpr_instname, rpr->rpr_name, &dest->re_node);
1543         }
1544         entity_release(src);
1545         entity_release(dest);
1546 
1547         return (result);
1548 }
1549 
1550 static rep_protocol_responseid_t
1551 snapshot_attach(repcache_client_t *cp, struct rep_protocol_snapshot_attach *rpr)
1552 {
1553         repcache_entity_t *src;
1554         uint32_t srcid = rpr->rpr_entityid_src;
1555         repcache_entity_t *dest;
1556         uint32_t destid = rpr->rpr_entityid_dest;
1557 
1558         int result;
1559 
1560         result = entity_find2(cp, srcid, &src, destid, &dest);
1561         if (result != REP_PROTOCOL_SUCCESS)
1562                 return (result);
1563 
1564         result = rc_snapshot_attach(&src->re_node, &dest->re_node);
1565 
1566         entity_release(src);
1567         entity_release(dest);
1568 
1569         return (result);
1570 }
1571 
1572 /*ARGSUSED*/
1573 static void
1574 property_get_type(repcache_client_t *cp, const void *in, size_t insz,
1575     void *out_arg, size_t *outsz, void *arg)
1576 {
1577         const struct rep_protocol_property_request *rpr = in;
1578         struct rep_protocol_integer_response *out = out_arg;
1579         repcache_entity_t *ep;
1580         rep_protocol_value_type_t t = 0;
1581 
1582         assert(*outsz == sizeof (*out));
1583 
1584         ep = entity_find(cp, rpr->rpr_entityid);
1585 
1586         if (ep == NULL) {
1587                 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1588                 *outsz = sizeof (out->rpr_response);
1589                 return;
1590         }
1591 
1592         out->rpr_response = rc_node_get_property_type(&ep->re_node, &t);
1593 
1594         entity_release(ep);
1595 
1596         if (out->rpr_response != REP_PROTOCOL_SUCCESS)
1597                 *outsz = sizeof (out->rpr_response);
1598         else
1599                 out->rpr_value = t;
1600 }
1601 
1602 /*
1603  * Fails with:
1604  *      _UNKNOWN_ID - an id does not designate an active register
1605  *      _NOT_SET - The property is not set
1606  *      _DELETED - The property has been deleted
1607  *      _TYPE_MISMATCH - The object is not a property
1608  *      _NOT_FOUND - The property has no values.
1609  *
1610  * Succeeds with:
1611  *      _SUCCESS - The property has 1 value.
1612  *      _TRUNCATED - The property has >1 value.
1613  */
1614 /*ARGSUSED*/
1615 static void
1616 property_get_value(repcache_client_t *cp, const void *in, size_t insz,
1617     void *out_arg, size_t *outsz, void *arg)
1618 {
1619         const struct rep_protocol_property_request *rpr = in;
1620         struct rep_protocol_value_response *out = out_arg;
1621         repcache_entity_t *ep;
1622 
1623         assert(*outsz == sizeof (*out));
1624 
1625         ep = entity_find(cp, rpr->rpr_entityid);
1626         if (ep == NULL) {
1627                 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1628                 *outsz = sizeof (out->rpr_response);
1629                 return;
1630         }
1631 
1632         out->rpr_response = rc_node_get_property_value(&ep->re_node, out,
1633             outsz);
1634 
1635         entity_release(ep);
1636 
1637         /*
1638          * If we fail, we only return the response code.
1639          * If we succeed, rc_node_get_property_value has shortened *outsz
1640          * to only include the value bytes needed.
1641          */
1642         if (out->rpr_response != REP_PROTOCOL_SUCCESS &&
1643             out->rpr_response != REP_PROTOCOL_FAIL_TRUNCATED)
1644                 *outsz = sizeof (out->rpr_response);
1645 }
1646 
1647 static rep_protocol_responseid_t
1648 propertygrp_notify(repcache_client_t *cp,
1649     struct rep_protocol_propertygrp_request *rpr, int *out_fd)
1650 {
1651         int fds[2];
1652         int ours, theirs;
1653 
1654         rep_protocol_responseid_t result;
1655         repcache_entity_t *ep;
1656 
1657         if (pipe(fds) < 0)
1658                 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1659 
1660         ours = fds[0];
1661         theirs = fds[1];
1662 
1663         if ((ep = entity_find(cp, rpr->rpr_entityid)) == NULL) {
1664                 result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1665                 goto fail;
1666         }
1667 
1668         /*
1669          * While the following can race with other threads setting up a
1670          * notification, the worst that can happen is that our fd has
1671          * already been closed before we return.
1672          */
1673         result = rc_pg_notify_setup(&cp->rc_pg_notify, &ep->re_node,
1674             ours);
1675 
1676         entity_release(ep);
1677 
1678         if (result != REP_PROTOCOL_SUCCESS)
1679                 goto fail;
1680 
1681         *out_fd = theirs;
1682         return (REP_PROTOCOL_SUCCESS);
1683 
1684 fail:
1685         (void) close(ours);
1686         (void) close(theirs);
1687 
1688         return (result);
1689 }
1690 
1691 static rep_protocol_responseid_t
1692 client_add_notify(repcache_client_t *cp,
1693     struct rep_protocol_notify_request *rpr)
1694 {
1695         rpr->rpr_pattern[sizeof (rpr->rpr_pattern) - 1] = 0;
1696 
1697         switch (rpr->rpr_type) {
1698         case REP_PROTOCOL_NOTIFY_PGNAME:
1699                 return (rc_notify_info_add_name(&cp->rc_notify_info,
1700                     rpr->rpr_pattern));
1701 
1702         case REP_PROTOCOL_NOTIFY_PGTYPE:
1703                 return (rc_notify_info_add_type(&cp->rc_notify_info,
1704                     rpr->rpr_pattern));
1705 
1706         default:
1707                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1708         }
1709 }
1710 
1711 /*ARGSUSED*/
1712 static void
1713 client_wait(repcache_client_t *cp, const void *in, size_t insz,
1714     void *out_arg, size_t *outsz, void *arg)
1715 {
1716         int result;
1717         repcache_entity_t *ep;
1718         const struct rep_protocol_wait_request *rpr = in;
1719         struct rep_protocol_fmri_response *out = out_arg;
1720 
1721         assert(*outsz == sizeof (*out));
1722 
1723         (void) pthread_mutex_lock(&cp->rc_lock);
1724         if (cp->rc_notify_thr != 0) {
1725                 (void) pthread_mutex_unlock(&cp->rc_lock);
1726                 out->rpr_response = REP_PROTOCOL_FAIL_EXISTS;
1727                 *outsz = sizeof (out->rpr_response);
1728                 return;
1729         }
1730         cp->rc_notify_thr = pthread_self();
1731         (void) pthread_mutex_unlock(&cp->rc_lock);
1732 
1733         result = rc_notify_info_wait(&cp->rc_notify_info, &cp->rc_notify_ptr,
1734             out->rpr_fmri, sizeof (out->rpr_fmri));
1735 
1736         if (result == REP_PROTOCOL_SUCCESS) {
1737                 if ((ep = entity_find(cp, rpr->rpr_entityid)) != NULL) {
1738                         if (ep->re_type == REP_PROTOCOL_ENTITY_PROPERTYGRP) {
1739                                 rc_node_ptr_assign(&ep->re_node,
1740                                     &cp->rc_notify_ptr);
1741                         } else {
1742                                 result = REP_PROTOCOL_FAIL_TYPE_MISMATCH;
1743                         }
1744                         entity_release(ep);
1745                 } else {
1746                         result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1747                 }
1748                 rc_node_clear(&cp->rc_notify_ptr, 0);
1749         }
1750 
1751         (void) pthread_mutex_lock(&cp->rc_lock);
1752         assert(cp->rc_notify_thr == pthread_self());
1753         cp->rc_notify_thr = 0;
1754         (void) pthread_mutex_unlock(&cp->rc_lock);
1755 
1756         out->rpr_response = result;
1757         if (result != REP_PROTOCOL_SUCCESS)
1758                 *outsz = sizeof (out->rpr_response);
1759 }
1760 
1761 /*
1762  * Can return:
1763  *      _PERMISSION_DENIED      not enough privileges to do request.
1764  *      _BAD_REQUEST            name is not valid or reserved
1765  *      _TRUNCATED              name is too long for current repository path
1766  *      _UNKNOWN                failed for unknown reason (details written to
1767  *                              console)
1768  *      _BACKEND_READONLY       backend is not writable
1769  *      _NO_RESOURCES           out of memory
1770  *      _SUCCESS                Backup completed successfully.
1771  */
1772 static rep_protocol_responseid_t
1773 backup_repository(repcache_client_t *cp,
1774     struct rep_protocol_backup_request *rpr)
1775 {
1776         rep_protocol_responseid_t result;
1777         ucred_t *uc = get_ucred();
1778 
1779         if (!client_is_privileged() && (uc == NULL || ucred_geteuid(uc) != 0))
1780                 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
1781 
1782         rpr->rpr_name[REP_PROTOCOL_NAME_LEN - 1] = 0;
1783         if (strcmp(rpr->rpr_name, REPOSITORY_BOOT_BACKUP) == 0)
1784                 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1785 
1786         (void) pthread_mutex_lock(&cp->rc_lock);
1787         if (rpr->rpr_changeid != cp->rc_changeid) {
1788                 result = backend_create_backup(rpr->rpr_name);
1789                 if (result == REP_PROTOCOL_SUCCESS)
1790                         cp->rc_changeid = rpr->rpr_changeid;
1791         } else {
1792                 result = REP_PROTOCOL_SUCCESS;
1793         }
1794         (void) pthread_mutex_unlock(&cp->rc_lock);
1795 
1796         return (result);
1797 }
1798 
1799 /*
1800  * This function captures the information that will be used for an
1801  * annotation audit event.  Specifically, it captures the operation to be
1802  * performed and the name of the file that is being used.  These values are
1803  * copied from the rep_protocol_annotation request at rpr to the client
1804  * structure.  If both these values are null, the client is turning
1805  * annotation off.
1806  *
1807  * Fails with
1808  *      _NO_RESOURCES - unable to allocate memory
1809  */
1810 static rep_protocol_responseid_t
1811 set_annotation(repcache_client_t *cp, struct rep_protocol_annotation *rpr)
1812 {
1813         au_id_t audit_uid;
1814         const char *file = NULL;
1815         const char *old_ptrs[2];
1816         const char *operation = NULL;
1817         rep_protocol_responseid_t rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1818         au_asid_t sessionid;
1819 
1820         (void) memset((void *)old_ptrs, 0, sizeof (old_ptrs));
1821 
1822         /* Copy rpr_operation and rpr_file if they are not empty strings. */
1823         if (rpr->rpr_operation[0] != 0) {
1824                 /*
1825                  * Make sure that client did not send us an unterminated buffer.
1826                  */
1827                 rpr->rpr_operation[sizeof (rpr->rpr_operation) - 1] = 0;
1828                 if ((operation = strdup(rpr->rpr_operation)) == NULL)
1829                         goto out;
1830         }
1831         if (rpr->rpr_file[0] != 0) {
1832                 /*
1833                  * Make sure that client did not send us an unterminated buffer.
1834                  */
1835                 rpr->rpr_file[sizeof (rpr->rpr_file) - 1] = 0;
1836                 if ((file = strdup(rpr->rpr_file)) == NULL)
1837                         goto out;
1838         }
1839 
1840         (void) pthread_mutex_lock(&cp->rc_annotate_lock);
1841         /* Save addresses of memory to free when not locked */
1842         old_ptrs[0] = cp->rc_operation;
1843         old_ptrs[1] = cp->rc_file;
1844 
1845         /* Save pointers to annotation strings. */
1846         cp->rc_operation = operation;
1847         cp->rc_file = file;
1848 
1849         /*
1850          * Set annotation flag.  Annotations should be turned on if either
1851          * operation or file are not NULL.
1852          */
1853         cp->rc_annotate = (operation != NULL) || (file != NULL);
1854         (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1855 
1856         /*
1857          * operation and file pointers are saved in cp, so don't free them
1858          * during cleanup.
1859          */
1860         operation = NULL;
1861         file = NULL;
1862         rc = REP_PROTOCOL_SUCCESS;
1863 
1864         /*
1865          * Native builds are done to create svc.configd-native.  This
1866          * program runs only on the Open Solaris build machines to create
1867          * the seed repository.  Until the SMF auditing code is distributed
1868          * to the Open Solaris build machines, adt_get_unique_id() in the
1869          * following code is not a global function in libbsm.  Hence the
1870          * following conditional compilation.
1871          */
1872 #ifndef NATIVE_BUILD
1873         /*
1874          * Set the appropriate audit session id.
1875          */
1876         if (cp->rc_annotate) {
1877                 /*
1878                  * We're starting a group of annotated audit events, so
1879                  * create and set an audit session ID for this annotation.
1880                  */
1881                 adt_get_auid(cp->rc_adt_session, &audit_uid);
1882                 sessionid = adt_get_unique_id(audit_uid);
1883         } else {
1884                 /*
1885                  * Annotation is done so restore our client audit session
1886                  * id.
1887                  */
1888                 sessionid = cp->rc_adt_sessionid;
1889         }
1890         adt_set_asid(cp->rc_adt_session, sessionid);
1891 #endif  /* NATIVE_BUILD */
1892 
1893 out:
1894         if (operation != NULL)
1895                 free((void *)operation);
1896         if (file != NULL)
1897                 free((void *)file);
1898         free((void *)old_ptrs[0]);
1899         free((void *)old_ptrs[1]);
1900         return (rc);
1901 }
1902 
1903 /*
1904  * Determine if an annotation event needs to be generated.  If it does
1905  * provide the operation and file name that should be used in the event.
1906  *
1907  * Can return:
1908  *      0               No annotation event needed or buffers are not large
1909  *                      enough.  Either way an event should not be
1910  *                      generated.
1911  *      1               Generate annotation event.
1912  */
1913 int
1914 client_annotation_needed(char *operation, size_t oper_sz,
1915     char *file, size_t file_sz)
1916 {
1917         thread_info_t *ti = thread_self();
1918         repcache_client_t *cp = ti->ti_active_client;
1919         int rc = 0;
1920 
1921         (void) pthread_mutex_lock(&cp->rc_annotate_lock);
1922         if (cp->rc_annotate) {
1923                 rc = 1;
1924                 if (cp->rc_operation == NULL) {
1925                         if (oper_sz > 0)
1926                                 operation[0] = 0;
1927                 } else {
1928                         if (strlcpy(operation, cp->rc_operation, oper_sz) >=
1929                             oper_sz) {
1930                                 /* Buffer overflow, so do not generate event */
1931                                 rc = 0;
1932                         }
1933                 }
1934                 if (cp->rc_file == NULL) {
1935                         if (file_sz > 0)
1936                                 file[0] = 0;
1937                 } else if (rc == 1) {
1938                         if (strlcpy(file, cp->rc_file, file_sz) >= file_sz) {
1939                                 /* Buffer overflow, so do not generate event */
1940                                 rc = 0;
1941                         }
1942                 }
1943         }
1944         (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1945         return (rc);
1946 }
1947 
1948 void
1949 client_annotation_finished()
1950 {
1951         thread_info_t *ti = thread_self();
1952         repcache_client_t *cp = ti->ti_active_client;
1953 
1954         (void) pthread_mutex_lock(&cp->rc_annotate_lock);
1955         cp->rc_annotate = 0;
1956         (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1957 }
1958 
1959 #ifndef NATIVE_BUILD
1960 static void
1961 start_audit_session(repcache_client_t *cp)
1962 {
1963         ucred_t *cred = NULL;
1964         adt_session_data_t *session;
1965 
1966         /*
1967          * A NULL session pointer value can legally be used in all
1968          * subsequent calls to adt_* functions.
1969          */
1970         cp->rc_adt_session = NULL;
1971 
1972         if (!adt_audit_state(AUC_AUDITING))
1973                 return;
1974 
1975         if (door_ucred(&cred) != 0) {
1976                 switch (errno) {
1977                 case EAGAIN:
1978                 case ENOMEM:
1979                         syslog(LOG_ERR, gettext("start_audit_session(): cannot "
1980                             "get ucred.  %m\n"));
1981                         return;
1982                 case EINVAL:
1983                         /*
1984                          * Door client went away.  This is a normal,
1985                          * although infrequent event, so there is no need
1986                          * to create a syslog message.
1987                          */
1988                         return;
1989                 case EFAULT:
1990                 default:
1991                         bad_error("door_ucred", errno);
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 }