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