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