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