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