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