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