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