xref: /illumos-gate/usr/src/cmd/svc/configd/client.c (revision dbfe038179f8f9013fb3ed57447316a24e77e93f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * This is the client layer for svc.configd.  All direct protocol interactions
28  * are handled here.
29  *
30  * Essentially, the job of this layer is to turn the idempotent protocol
31  * into a series of non-idempotent calls into the object layer, while
32  * also handling the necessary locking.
33  */
34 
35 #include <alloca.h>
36 #include <assert.h>
37 #include <bsm/adt_event.h>
38 #include <door.h>
39 #include <errno.h>
40 #include <libintl.h>
41 #include <limits.h>
42 #include <pthread.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <syslog.h>
47 #include <ucred.h>
48 #include <unistd.h>
49 
50 #include <libuutil.h>
51 
52 #include "configd.h"
53 #include "repcache_protocol.h"
54 
55 #define	INVALID_CHANGEID	(0)
56 #define	INVALID_DOORID		((door_id_t)-1)
57 #define	INVALID_RESULT		((rep_protocol_responseid_t)INT_MIN)
58 
59 /*
60  * lint doesn't like constant assertions
61  */
62 #ifdef lint
63 #define	assert_nolint(x) (void)0
64 #else
65 #define	assert_nolint(x) assert(x)
66 #endif
67 
68 /*
69  * Protects client linkage and the freelist
70  */
71 #define	CLIENT_HASH_SIZE	64
72 
73 #pragma align 64(client_hash)
74 static client_bucket_t client_hash[CLIENT_HASH_SIZE];
75 
76 static uu_avl_pool_t *entity_pool;
77 static uu_avl_pool_t *iter_pool;
78 static uu_list_pool_t *client_pool;
79 
80 #define	CLIENT_HASH(id)		(&client_hash[((id) & (CLIENT_HASH_SIZE - 1))])
81 
82 uint_t request_log_size = 1024;		/* tunable, before we start */
83 
84 static pthread_mutex_t request_log_lock = PTHREAD_MUTEX_INITIALIZER;
85 static uint_t request_log_cur;
86 request_log_entry_t	*request_log;
87 
88 static uint32_t		client_maxid;
89 static pthread_mutex_t	client_lock;	/* protects client_maxid */
90 
91 static request_log_entry_t *
92 get_log(void)
93 {
94 	thread_info_t *ti = thread_self();
95 	return (&ti->ti_log);
96 }
97 
98 void
99 log_enter(request_log_entry_t *rlp)
100 {
101 	if (rlp->rl_start != 0 && request_log != NULL) {
102 		request_log_entry_t *logrlp;
103 
104 		(void) pthread_mutex_lock(&request_log_lock);
105 		assert(request_log_cur < request_log_size);
106 		logrlp = &request_log[request_log_cur++];
107 		if (request_log_cur == request_log_size)
108 			request_log_cur = 0;
109 		(void) memcpy(logrlp, rlp, sizeof (*rlp));
110 		(void) pthread_mutex_unlock(&request_log_lock);
111 	}
112 }
113 
114 /*
115  * Note that the svc.configd dmod will join all of the per-thread log entries
116  * with the main log, so that even if the log is disabled, there is some
117  * information available.
118  */
119 static request_log_entry_t *
120 start_log(uint32_t clientid)
121 {
122 	request_log_entry_t *rlp = get_log();
123 
124 	log_enter(rlp);
125 
126 	(void) memset(rlp, 0, sizeof (*rlp));
127 	rlp->rl_start = gethrtime();
128 	rlp->rl_tid = pthread_self();
129 	rlp->rl_clientid = clientid;
130 
131 	return (rlp);
132 }
133 
134 void
135 end_log(void)
136 {
137 	request_log_entry_t *rlp = get_log();
138 
139 	rlp->rl_end = gethrtime();
140 }
141 
142 static void
143 add_log_ptr(request_log_entry_t *rlp, enum rc_ptr_type type, uint32_t id,
144     void *ptr)
145 {
146 	request_log_ptr_t *rpp;
147 
148 	if (rlp == NULL)
149 		return;
150 
151 	if (rlp->rl_num_ptrs >= MAX_PTRS)
152 		return;
153 
154 	rpp = &rlp->rl_ptrs[rlp->rl_num_ptrs++];
155 	rpp->rlp_type = type;
156 	rpp->rlp_id = id;
157 	rpp->rlp_ptr = ptr;
158 
159 	/*
160 	 * For entities, it's useful to have the node pointer at the start
161 	 * of the request.
162 	 */
163 	if (type == RC_PTR_TYPE_ENTITY && ptr != NULL)
164 		rpp->rlp_data = ((repcache_entity_t *)ptr)->re_node.rnp_node;
165 }
166 
167 int
168 client_is_privileged(void)
169 {
170 	thread_info_t *ti = thread_self();
171 
172 	ucred_t *uc;
173 
174 	if (ti->ti_active_client != NULL &&
175 	    ti->ti_active_client->rc_all_auths)
176 		return (1);
177 
178 	if ((uc = get_ucred()) == NULL)
179 		return (0);
180 
181 	return (ucred_is_privileged(uc));
182 }
183 
184 /*ARGSUSED*/
185 static int
186 client_compare(const void *lc_arg, const void *rc_arg, void *private)
187 {
188 	uint32_t l_id = ((const repcache_client_t *)lc_arg)->rc_id;
189 	uint32_t r_id = ((const repcache_client_t *)rc_arg)->rc_id;
190 
191 	if (l_id > r_id)
192 		return (1);
193 	if (l_id < r_id)
194 		return (-1);
195 	return (0);
196 }
197 
198 /*ARGSUSED*/
199 static int
200 entity_compare(const void *lc_arg, const void *rc_arg, void *private)
201 {
202 	uint32_t l_id = ((const repcache_entity_t *)lc_arg)->re_id;
203 	uint32_t r_id = ((const repcache_entity_t *)rc_arg)->re_id;
204 
205 	if (l_id > r_id)
206 		return (1);
207 	if (l_id < r_id)
208 		return (-1);
209 	return (0);
210 }
211 
212 /*ARGSUSED*/
213 static int
214 iter_compare(const void *lc_arg, const void *rc_arg, void *private)
215 {
216 	uint32_t l_id = ((const repcache_iter_t *)lc_arg)->ri_id;
217 	uint32_t r_id = ((const repcache_iter_t *)rc_arg)->ri_id;
218 
219 	if (l_id > r_id)
220 		return (1);
221 	if (l_id < r_id)
222 		return (-1);
223 	return (0);
224 }
225 
226 static int
227 client_hash_init(void)
228 {
229 	int x;
230 
231 	assert_nolint(offsetof(repcache_entity_t, re_id) == 0);
232 	entity_pool = uu_avl_pool_create("repcache_entitys",
233 	    sizeof (repcache_entity_t), offsetof(repcache_entity_t, re_link),
234 	    entity_compare, UU_AVL_POOL_DEBUG);
235 
236 	assert_nolint(offsetof(repcache_iter_t, ri_id) == 0);
237 	iter_pool = uu_avl_pool_create("repcache_iters",
238 	    sizeof (repcache_iter_t), offsetof(repcache_iter_t, ri_link),
239 	    iter_compare, UU_AVL_POOL_DEBUG);
240 
241 	assert_nolint(offsetof(repcache_client_t, rc_id) == 0);
242 	client_pool = uu_list_pool_create("repcache_clients",
243 	    sizeof (repcache_client_t), offsetof(repcache_client_t, rc_link),
244 	    client_compare, UU_LIST_POOL_DEBUG);
245 
246 	if (entity_pool == NULL || iter_pool == NULL || client_pool == NULL)
247 		return (0);
248 
249 	for (x = 0; x < CLIENT_HASH_SIZE; x++) {
250 		uu_list_t *lp = uu_list_create(client_pool, &client_hash[x],
251 		    UU_LIST_SORTED);
252 		if (lp == NULL)
253 			return (0);
254 
255 		(void) pthread_mutex_init(&client_hash[x].cb_lock, NULL);
256 		client_hash[x].cb_list = lp;
257 	}
258 
259 	return (1);
260 }
261 
262 static repcache_client_t *
263 client_alloc(void)
264 {
265 	repcache_client_t *cp;
266 	cp = uu_zalloc(sizeof (*cp));
267 	if (cp == NULL)
268 		return (NULL);
269 
270 	cp->rc_entities = uu_avl_create(entity_pool, cp, 0);
271 	if (cp->rc_entities == NULL)
272 		goto fail;
273 
274 	cp->rc_iters = uu_avl_create(iter_pool, cp, 0);
275 	if (cp->rc_iters == NULL)
276 		goto fail;
277 
278 	uu_list_node_init(cp, &cp->rc_link, client_pool);
279 
280 	cp->rc_doorfd = -1;
281 	cp->rc_doorid = INVALID_DOORID;
282 
283 	(void) pthread_mutex_init(&cp->rc_lock, NULL);
284 	(void) pthread_mutex_init(&cp->rc_annotate_lock, NULL);
285 
286 	rc_node_ptr_init(&cp->rc_notify_ptr);
287 
288 	return (cp);
289 
290 fail:
291 	if (cp->rc_iters != NULL)
292 		uu_avl_destroy(cp->rc_iters);
293 	if (cp->rc_entities != NULL)
294 		uu_avl_destroy(cp->rc_entities);
295 	uu_free(cp);
296 	return (NULL);
297 }
298 
299 static void
300 client_free(repcache_client_t *cp)
301 {
302 	assert(cp->rc_insert_thr == 0);
303 	assert(cp->rc_refcnt == 0);
304 	assert(cp->rc_doorfd == -1);
305 	assert(cp->rc_doorid == INVALID_DOORID);
306 	assert(uu_avl_first(cp->rc_entities) == NULL);
307 	assert(uu_avl_first(cp->rc_iters) == NULL);
308 	uu_avl_destroy(cp->rc_entities);
309 	uu_avl_destroy(cp->rc_iters);
310 	uu_list_node_fini(cp, &cp->rc_link, client_pool);
311 	(void) pthread_mutex_destroy(&cp->rc_lock);
312 	(void) pthread_mutex_destroy(&cp->rc_annotate_lock);
313 	rc_node_ptr_free_mem(&cp->rc_notify_ptr);
314 	uu_free(cp);
315 }
316 
317 static void
318 client_insert(repcache_client_t *cp)
319 {
320 	client_bucket_t *bp = CLIENT_HASH(cp->rc_id);
321 	uu_list_index_t idx;
322 
323 	assert(cp->rc_id > 0);
324 
325 	(void) pthread_mutex_lock(&bp->cb_lock);
326 	/*
327 	 * We assume it does not already exist
328 	 */
329 	(void) uu_list_find(bp->cb_list, cp, NULL, &idx);
330 	uu_list_insert(bp->cb_list, cp, idx);
331 
332 	(void) pthread_mutex_unlock(&bp->cb_lock);
333 }
334 
335 static repcache_client_t *
336 client_lookup(uint32_t id)
337 {
338 	client_bucket_t *bp = CLIENT_HASH(id);
339 	repcache_client_t *cp;
340 
341 	(void) pthread_mutex_lock(&bp->cb_lock);
342 
343 	cp = uu_list_find(bp->cb_list, &id, NULL, NULL);
344 
345 	/*
346 	 * Bump the reference count
347 	 */
348 	if (cp != NULL) {
349 		(void) pthread_mutex_lock(&cp->rc_lock);
350 		assert(!(cp->rc_flags & RC_CLIENT_DEAD));
351 		cp->rc_refcnt++;
352 		(void) pthread_mutex_unlock(&cp->rc_lock);
353 	}
354 	(void) pthread_mutex_unlock(&bp->cb_lock);
355 
356 	return (cp);
357 }
358 
359 static void
360 client_release(repcache_client_t *cp)
361 {
362 	(void) pthread_mutex_lock(&cp->rc_lock);
363 	assert(cp->rc_refcnt > 0);
364 	assert(cp->rc_insert_thr != pthread_self());
365 
366 	--cp->rc_refcnt;
367 	(void) pthread_cond_broadcast(&cp->rc_cv);
368 	(void) pthread_mutex_unlock(&cp->rc_lock);
369 }
370 
371 /*
372  * We only allow one thread to be inserting at a time, to prevent
373  * insert/insert races.
374  */
375 static void
376 client_start_insert(repcache_client_t *cp)
377 {
378 	(void) pthread_mutex_lock(&cp->rc_lock);
379 	assert(cp->rc_refcnt > 0);
380 
381 	while (cp->rc_insert_thr != 0) {
382 		assert(cp->rc_insert_thr != pthread_self());
383 		(void) pthread_cond_wait(&cp->rc_cv, &cp->rc_lock);
384 	}
385 	cp->rc_insert_thr = pthread_self();
386 	(void) pthread_mutex_unlock(&cp->rc_lock);
387 }
388 
389 static void
390 client_end_insert(repcache_client_t *cp)
391 {
392 	(void) pthread_mutex_lock(&cp->rc_lock);
393 	assert(cp->rc_insert_thr == pthread_self());
394 	cp->rc_insert_thr = 0;
395 	(void) pthread_cond_broadcast(&cp->rc_cv);
396 	(void) pthread_mutex_unlock(&cp->rc_lock);
397 }
398 
399 /*ARGSUSED*/
400 static repcache_entity_t *
401 entity_alloc(repcache_client_t *cp)
402 {
403 	repcache_entity_t *ep = uu_zalloc(sizeof (repcache_entity_t));
404 	if (ep != NULL) {
405 		uu_avl_node_init(ep, &ep->re_link, entity_pool);
406 	}
407 	return (ep);
408 }
409 
410 static void
411 entity_add(repcache_client_t *cp, repcache_entity_t *ep)
412 {
413 	uu_avl_index_t idx;
414 
415 	(void) pthread_mutex_lock(&cp->rc_lock);
416 	assert(cp->rc_insert_thr == pthread_self());
417 
418 	(void) uu_avl_find(cp->rc_entities, ep, NULL, &idx);
419 	uu_avl_insert(cp->rc_entities, ep, idx);
420 
421 	(void) pthread_mutex_unlock(&cp->rc_lock);
422 }
423 
424 static repcache_entity_t *
425 entity_find(repcache_client_t *cp, uint32_t id)
426 {
427 	repcache_entity_t *ep;
428 
429 	(void) pthread_mutex_lock(&cp->rc_lock);
430 	ep = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
431 	if (ep != NULL) {
432 		add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, ep);
433 		(void) pthread_mutex_lock(&ep->re_lock);
434 	}
435 	(void) pthread_mutex_unlock(&cp->rc_lock);
436 
437 	return (ep);
438 }
439 
440 /*
441  * Fails with
442  *   _DUPLICATE_ID - the ids are equal
443  *   _UNKNOWN_ID - an id does not designate an active register
444  */
445 static int
446 entity_find2(repcache_client_t *cp, uint32_t id1, repcache_entity_t **out1,
447     uint32_t id2, repcache_entity_t **out2)
448 {
449 	repcache_entity_t *e1, *e2;
450 	request_log_entry_t *rlp;
451 
452 	if (id1 == id2)
453 		return (REP_PROTOCOL_FAIL_DUPLICATE_ID);
454 
455 	(void) pthread_mutex_lock(&cp->rc_lock);
456 	e1 = uu_avl_find(cp->rc_entities, &id1, NULL, NULL);
457 	e2 = uu_avl_find(cp->rc_entities, &id2, NULL, NULL);
458 	if (e1 == NULL || e2 == NULL) {
459 		(void) pthread_mutex_unlock(&cp->rc_lock);
460 		return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
461 	}
462 
463 	assert(e1 != e2);
464 
465 	/*
466 	 * locks are ordered by id number
467 	 */
468 	if (id1 < id2) {
469 		(void) pthread_mutex_lock(&e1->re_lock);
470 		(void) pthread_mutex_lock(&e2->re_lock);
471 	} else {
472 		(void) pthread_mutex_lock(&e2->re_lock);
473 		(void) pthread_mutex_lock(&e1->re_lock);
474 	}
475 	*out1 = e1;
476 	*out2 = e2;
477 
478 	(void) pthread_mutex_unlock(&cp->rc_lock);
479 
480 	if ((rlp = get_log()) != NULL) {
481 		add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, id1, e1);
482 		add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, id2, e2);
483 	}
484 
485 	return (REP_PROTOCOL_SUCCESS);
486 }
487 
488 static void
489 entity_release(repcache_entity_t *ep)
490 {
491 	assert(ep->re_node.rnp_node == NULL ||
492 	    !MUTEX_HELD(&ep->re_node.rnp_node->rn_lock));
493 	(void) pthread_mutex_unlock(&ep->re_lock);
494 }
495 
496 static void
497 entity_destroy(repcache_entity_t *entity)
498 {
499 	(void) pthread_mutex_lock(&entity->re_lock);
500 	rc_node_clear(&entity->re_node, 0);
501 	(void) pthread_mutex_unlock(&entity->re_lock);
502 
503 	uu_avl_node_fini(entity, &entity->re_link, entity_pool);
504 	(void) pthread_mutex_destroy(&entity->re_lock);
505 	rc_node_ptr_free_mem(&entity->re_node);
506 	uu_free(entity);
507 }
508 
509 static void
510 entity_remove(repcache_client_t *cp, uint32_t id)
511 {
512 	repcache_entity_t *entity;
513 
514 	(void) pthread_mutex_lock(&cp->rc_lock);
515 	entity = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
516 	if (entity != NULL) {
517 		add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, entity);
518 
519 		uu_avl_remove(cp->rc_entities, entity);
520 	}
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 	adt_session_data_t *session;
1960 
1961 	/*
1962 	 * A NULL session pointer value can legally be used in all
1963 	 * subsequent calls to adt_* functions.
1964 	 */
1965 	cp->rc_adt_session = NULL;
1966 
1967 	if (!adt_audit_state(AUC_AUDITING))
1968 		return;
1969 
1970 	if (door_ucred(&cred) != 0) {
1971 		switch (errno) {
1972 		case EAGAIN:
1973 		case ENOMEM:
1974 			syslog(LOG_ERR, gettext("start_audit_session(): cannot "
1975 			    "get ucred.  %m\n"));
1976 			return;
1977 		case EINVAL:
1978 			/*
1979 			 * Door client went away.  This is a normal,
1980 			 * although infrequent event, so there is no need
1981 			 * to create a syslog message.
1982 			 */
1983 			return;
1984 		case EFAULT:
1985 		default:
1986 			bad_error("door_ucred", errno);
1987 			return;
1988 		}
1989 	}
1990 	if (adt_start_session(&session, NULL, 0) != 0) {
1991 		syslog(LOG_ERR, gettext("start_audit_session(): could not "
1992 		    "start audit session.\n"));
1993 		ucred_free(cred);
1994 		return;
1995 	}
1996 	if (adt_set_from_ucred(session, cred, ADT_NEW) != 0) {
1997 		syslog(LOG_ERR, gettext("start_audit_session(): cannot set "
1998 		    "audit session data from ucred\n"));
1999 		/* Something went wrong.  End the session. */
2000 		(void) adt_end_session(session);
2001 		ucred_free(cred);
2002 		return;
2003 	}
2004 
2005 	/* All went well.  Save the session data and session ID */
2006 	cp->rc_adt_session = session;
2007 	adt_get_asid(session, &cp->rc_adt_sessionid);
2008 
2009 	ucred_free(cred);
2010 }
2011 
2012 /*
2013  * Handle switch client request
2014  *
2015  * This routine can return:
2016  *
2017  *	_PERMISSION_DENIED	not enough privileges to do request.
2018  *	_UNKNOWN		file operation error (details written to
2019  *				the console).
2020  *	_SUCCESS		switch operation is completed.
2021  *	_BACKEND_ACCESS		backend access fails.
2022  *	_NO_RESOURCES		out of memory.
2023  *	_BACKEND_READONLY	backend is not writable.
2024  */
2025 static rep_protocol_responseid_t
2026 repository_switch(repcache_client_t *cp,
2027     struct rep_protocol_switch_request *rpr)
2028 {
2029 	rep_protocol_responseid_t result;
2030 	ucred_t *uc = get_ucred();
2031 
2032 	if (!client_is_privileged() && (uc == NULL ||
2033 	    ucred_geteuid(uc) != 0)) {
2034 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
2035 	}
2036 
2037 	(void) pthread_mutex_lock(&cp->rc_lock);
2038 	if (rpr->rpr_changeid != cp->rc_changeid) {
2039 		if ((result = backend_switch(rpr->rpr_flag)) ==
2040 		    REP_PROTOCOL_SUCCESS)
2041 			cp->rc_changeid = rpr->rpr_changeid;
2042 	} else {
2043 		result = REP_PROTOCOL_SUCCESS;
2044 	}
2045 	(void) pthread_mutex_unlock(&cp->rc_lock);
2046 
2047 	return (result);
2048 }
2049 
2050 typedef rep_protocol_responseid_t protocol_simple_f(repcache_client_t *cp,
2051     const void *rpr);
2052 
2053 /*ARGSUSED*/
2054 static void
2055 simple_handler(repcache_client_t *cp, const void *in, size_t insz,
2056     void *out_arg, size_t *outsz, void *arg)
2057 {
2058 	protocol_simple_f *f = (protocol_simple_f *)arg;
2059 	rep_protocol_response_t *out = out_arg;
2060 
2061 	assert(*outsz == sizeof (*out));
2062 	assert(f != NULL);
2063 
2064 	out->rpr_response = (*f)(cp, in);
2065 }
2066 
2067 typedef rep_protocol_responseid_t protocol_simple_fd_f(repcache_client_t *cp,
2068     const void *rpr, int *out_fd);
2069 
2070 /*ARGSUSED*/
2071 static void
2072 simple_fd_handler(repcache_client_t *cp, const void *in, size_t insz,
2073     void *out_arg, size_t *outsz, void *arg, int *out_fd)
2074 {
2075 	protocol_simple_fd_f *f = (protocol_simple_fd_f *)arg;
2076 	rep_protocol_response_t *out = out_arg;
2077 
2078 	assert(*outsz == sizeof (*out));
2079 	assert(f != NULL);
2080 
2081 	out->rpr_response = (*f)(cp, in, out_fd);
2082 }
2083 
2084 typedef void protocol_handler_f(repcache_client_t *, const void *in,
2085     size_t insz, void *out, size_t *outsz, void *arg);
2086 
2087 typedef void protocol_handler_fdret_f(repcache_client_t *, const void *in,
2088     size_t insz, void *out, size_t *outsz, void *arg, int *fd_out);
2089 
2090 #define	PROTO(p, f, in) {						\
2091 		p, #p, simple_handler, (void *)(&f), NULL,		\
2092 		    sizeof (in), sizeof (rep_protocol_response_t), 0	\
2093 	}
2094 
2095 #define	PROTO_FD_OUT(p, f, in) {					\
2096 		p, #p, NULL, (void *)(&f), simple_fd_handler,		\
2097 		    sizeof (in),					\
2098 		    sizeof (rep_protocol_response_t),			\
2099 		    PROTO_FLAG_RETFD					\
2100 	}
2101 
2102 #define	PROTO_VARIN(p, f, insz) {					\
2103 		p, #p, &(f), NULL, NULL,				\
2104 		    insz, sizeof (rep_protocol_response_t),		\
2105 		    PROTO_FLAG_VARINPUT					\
2106 	}
2107 
2108 #define	PROTO_UINT_OUT(p, f, in) {					\
2109 		p, #p, &(f), NULL, NULL,				\
2110 		    sizeof (in),					\
2111 		    sizeof (struct rep_protocol_integer_response), 0	\
2112 	}
2113 
2114 #define	PROTO_NAME_OUT(p, f, in) {					\
2115 		p, #p, &(f), NULL, NULL,				\
2116 		    sizeof (in),					\
2117 		    sizeof (struct rep_protocol_name_response), 0	\
2118 	}
2119 
2120 #define	PROTO_FMRI_OUT(p, f, in) {					\
2121 		p, #p, &(f), NULL, NULL,				\
2122 		    sizeof (in),					\
2123 		    sizeof (struct rep_protocol_fmri_response), 0	\
2124 	}
2125 
2126 #define	PROTO_VALUE_OUT(p, f, in) {					\
2127 		p, #p, &(f), NULL, NULL,				\
2128 		    sizeof (in),					\
2129 		    sizeof (struct rep_protocol_value_response), 0	\
2130 	}
2131 
2132 #define	PROTO_PANIC(p)	{ p, #p, NULL, NULL, NULL, 0, 0, PROTO_FLAG_PANIC }
2133 #define	PROTO_END()	{ 0, NULL, NULL, NULL, NULL, 0, 0, PROTO_FLAG_PANIC }
2134 
2135 #define	PROTO_FLAG_PANIC	0x00000001	/* should never be called */
2136 #define	PROTO_FLAG_VARINPUT	0x00000004	/* in_size is minimum size */
2137 #define	PROTO_FLAG_RETFD	0x00000008	/* can also return an FD */
2138 
2139 #define	PROTO_ALL_FLAGS		0x0000000f	/* all flags */
2140 
2141 static struct protocol_entry {
2142 	enum rep_protocol_requestid	pt_request;
2143 	const char			*pt_name;
2144 	protocol_handler_f		*pt_handler;
2145 	void				*pt_arg;
2146 	protocol_handler_fdret_f	*pt_fd_handler;
2147 	size_t				pt_in_size;
2148 	size_t				pt_out_max;
2149 	uint32_t			pt_flags;
2150 } protocol_table[] = {
2151 	PROTO_PANIC(REP_PROTOCOL_CLOSE),		/* special case */
2152 
2153 	PROTO(REP_PROTOCOL_ENTITY_SETUP,		entity_setup,
2154 	    struct rep_protocol_entity_setup),
2155 	PROTO_NAME_OUT(REP_PROTOCOL_ENTITY_NAME,	entity_name,
2156 	    struct rep_protocol_entity_name),
2157 	PROTO_UINT_OUT(REP_PROTOCOL_ENTITY_PARENT_TYPE,	entity_parent_type,
2158 	    struct rep_protocol_entity_parent_type),
2159 	PROTO(REP_PROTOCOL_ENTITY_GET_CHILD,		entity_get_child,
2160 	    struct rep_protocol_entity_get_child),
2161 	PROTO(REP_PROTOCOL_ENTITY_GET_PARENT,		entity_get_parent,
2162 	    struct rep_protocol_entity_parent),
2163 	PROTO(REP_PROTOCOL_ENTITY_GET,			entity_get,
2164 	    struct rep_protocol_entity_get),
2165 	PROTO(REP_PROTOCOL_ENTITY_UPDATE,		entity_update,
2166 	    struct rep_protocol_entity_update),
2167 	PROTO(REP_PROTOCOL_ENTITY_CREATE_CHILD,		entity_create_child,
2168 	    struct rep_protocol_entity_create_child),
2169 	PROTO(REP_PROTOCOL_ENTITY_CREATE_PG,		entity_create_pg,
2170 	    struct rep_protocol_entity_create_pg),
2171 	PROTO(REP_PROTOCOL_ENTITY_DELETE,		entity_delete,
2172 	    struct rep_protocol_entity_delete),
2173 	PROTO(REP_PROTOCOL_ENTITY_RESET,		entity_reset,
2174 	    struct rep_protocol_entity_reset),
2175 	PROTO(REP_PROTOCOL_ENTITY_TEARDOWN,		entity_teardown,
2176 	    struct rep_protocol_entity_teardown),
2177 
2178 	PROTO(REP_PROTOCOL_ITER_SETUP,			iter_setup,
2179 	    struct rep_protocol_iter_request),
2180 	PROTO(REP_PROTOCOL_ITER_START,			iter_start,
2181 	    struct rep_protocol_iter_start),
2182 	PROTO(REP_PROTOCOL_ITER_READ,			iter_read,
2183 	    struct rep_protocol_iter_read),
2184 	PROTO_VALUE_OUT(REP_PROTOCOL_ITER_READ_VALUE,	iter_read_value,
2185 	    struct rep_protocol_iter_read_value),
2186 	PROTO(REP_PROTOCOL_ITER_RESET,			iter_reset,
2187 	    struct rep_protocol_iter_request),
2188 	PROTO(REP_PROTOCOL_ITER_TEARDOWN,		iter_teardown,
2189 	    struct rep_protocol_iter_request),
2190 
2191 	PROTO(REP_PROTOCOL_NEXT_SNAPLEVEL,		next_snaplevel,
2192 	    struct rep_protocol_entity_pair),
2193 
2194 	PROTO(REP_PROTOCOL_SNAPSHOT_TAKE,		snapshot_take,
2195 	    struct rep_protocol_snapshot_take),
2196 	PROTO(REP_PROTOCOL_SNAPSHOT_TAKE_NAMED,		snapshot_take_named,
2197 	    struct rep_protocol_snapshot_take_named),
2198 	PROTO(REP_PROTOCOL_SNAPSHOT_ATTACH,		snapshot_attach,
2199 	    struct rep_protocol_snapshot_attach),
2200 
2201 	PROTO_UINT_OUT(REP_PROTOCOL_PROPERTY_GET_TYPE,	property_get_type,
2202 	    struct rep_protocol_property_request),
2203 	PROTO_VALUE_OUT(REP_PROTOCOL_PROPERTY_GET_VALUE, property_get_value,
2204 	    struct rep_protocol_property_request),
2205 
2206 	PROTO_FD_OUT(REP_PROTOCOL_PROPERTYGRP_SETUP_WAIT, propertygrp_notify,
2207 	    struct rep_protocol_propertygrp_request),
2208 	PROTO(REP_PROTOCOL_PROPERTYGRP_TX_START,	tx_start,
2209 	    struct rep_protocol_transaction_start),
2210 	PROTO_VARIN(REP_PROTOCOL_PROPERTYGRP_TX_COMMIT,	tx_commit,
2211 	    REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE),
2212 
2213 	PROTO(REP_PROTOCOL_CLIENT_ADD_NOTIFY,		client_add_notify,
2214 	    struct rep_protocol_notify_request),
2215 	PROTO_FMRI_OUT(REP_PROTOCOL_CLIENT_WAIT,	client_wait,
2216 	    struct rep_protocol_wait_request),
2217 
2218 	PROTO(REP_PROTOCOL_BACKUP,			backup_repository,
2219 	    struct rep_protocol_backup_request),
2220 
2221 	PROTO(REP_PROTOCOL_SET_AUDIT_ANNOTATION,	set_annotation,
2222 	    struct rep_protocol_annotation),
2223 
2224 	PROTO(REP_PROTOCOL_SWITCH,			repository_switch,
2225 	    struct rep_protocol_switch_request),
2226 
2227 	PROTO_END()
2228 };
2229 #undef PROTO
2230 #undef PROTO_FMRI_OUT
2231 #undef PROTO_NAME_OUT
2232 #undef PROTO_UINT_OUT
2233 #undef PROTO_PANIC
2234 #undef PROTO_END
2235 
2236 /*
2237  * The number of entries, sans PROTO_END()
2238  */
2239 #define	PROTOCOL_ENTRIES \
2240 	    (sizeof (protocol_table) / sizeof (*protocol_table) - 1)
2241 
2242 #define	PROTOCOL_PREFIX "REP_PROTOCOL_"
2243 
2244 int
2245 client_init(void)
2246 {
2247 	int i;
2248 	struct protocol_entry *e;
2249 
2250 	if (!client_hash_init())
2251 		return (0);
2252 
2253 	if (request_log_size > 0) {
2254 		request_log = uu_zalloc(request_log_size *
2255 		    sizeof (request_log_entry_t));
2256 	}
2257 
2258 	/*
2259 	 * update the names to not include REP_PROTOCOL_
2260 	 */
2261 	for (i = 0; i < PROTOCOL_ENTRIES; i++) {
2262 		e = &protocol_table[i];
2263 		assert(strncmp(e->pt_name, PROTOCOL_PREFIX,
2264 		    strlen(PROTOCOL_PREFIX)) == 0);
2265 		e->pt_name += strlen(PROTOCOL_PREFIX);
2266 	}
2267 	/*
2268 	 * verify the protocol table is consistent
2269 	 */
2270 	for (i = 0; i < PROTOCOL_ENTRIES; i++) {
2271 		e = &protocol_table[i];
2272 		assert(e->pt_request == (REP_PROTOCOL_BASE + i));
2273 
2274 		assert((e->pt_flags & ~PROTO_ALL_FLAGS) == 0);
2275 
2276 		if (e->pt_flags & PROTO_FLAG_PANIC)
2277 			assert(e->pt_in_size == 0 && e->pt_out_max == 0 &&
2278 			    e->pt_handler == NULL);
2279 		else
2280 			assert(e->pt_in_size != 0 && e->pt_out_max != 0 &&
2281 			    (e->pt_handler != NULL ||
2282 			    e->pt_fd_handler != NULL));
2283 	}
2284 	assert((REP_PROTOCOL_BASE + i) == REP_PROTOCOL_MAX_REQUEST);
2285 
2286 	assert(protocol_table[i].pt_request == 0);
2287 
2288 	return (1);
2289 }
2290 
2291 static void
2292 client_switcher(void *cookie, char *argp, size_t arg_size, door_desc_t *desc_in,
2293     uint_t n_desc)
2294 {
2295 	thread_info_t *ti = thread_self();
2296 
2297 	repcache_client_t *cp;
2298 	uint32_t id = (uint32_t)cookie;
2299 	enum rep_protocol_requestid request_code;
2300 
2301 	rep_protocol_responseid_t result = INVALID_RESULT;
2302 
2303 	struct protocol_entry *e;
2304 
2305 	char *retval = NULL;
2306 	size_t retsize = 0;
2307 
2308 	int retfd = -1;
2309 	door_desc_t desc;
2310 	request_log_entry_t *rlp;
2311 
2312 	rlp = start_log(id);
2313 
2314 	if (n_desc != 0)
2315 		uu_die("can't happen: %d descriptors @%p (cookie %p)",
2316 		    n_desc, desc_in, cookie);
2317 
2318 	if (argp == DOOR_UNREF_DATA) {
2319 		client_destroy(id);
2320 		goto bad_end;
2321 	}
2322 
2323 	thread_newstate(ti, TI_CLIENT_CALL);
2324 
2325 	/*
2326 	 * To simplify returning just a result code, we set up for
2327 	 * that case here.
2328 	 */
2329 	retval = (char *)&result;
2330 	retsize = sizeof (result);
2331 
2332 	if (arg_size < sizeof (request_code)) {
2333 		result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2334 		goto end_unheld;
2335 	}
2336 
2337 	ti->ti_client_request = (void *)argp;
2338 
2339 	/* LINTED alignment */
2340 	request_code = *(uint32_t *)argp;
2341 
2342 	if (rlp != NULL) {
2343 		rlp->rl_request = request_code;
2344 	}
2345 	/*
2346 	 * In order to avoid locking problems on removal, we handle the
2347 	 * "close" case before doing a lookup.
2348 	 */
2349 	if (request_code == REP_PROTOCOL_CLOSE) {
2350 		client_destroy(id);
2351 		result = REP_PROTOCOL_SUCCESS;
2352 		goto end_unheld;
2353 	}
2354 
2355 	cp = client_lookup(id);
2356 	/*
2357 	 * cp is held
2358 	 */
2359 
2360 	if (cp == NULL)
2361 		goto bad_end;
2362 
2363 	if (rlp != NULL)
2364 		rlp->rl_client = cp;
2365 
2366 	ti->ti_active_client = cp;
2367 
2368 	if (request_code < REP_PROTOCOL_BASE ||
2369 	    request_code >= REP_PROTOCOL_BASE + PROTOCOL_ENTRIES) {
2370 		result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2371 		goto end;
2372 	}
2373 
2374 	e = &protocol_table[request_code - REP_PROTOCOL_BASE];
2375 
2376 	assert(!(e->pt_flags & PROTO_FLAG_PANIC));
2377 
2378 	if (e->pt_flags & PROTO_FLAG_VARINPUT) {
2379 		if (arg_size < e->pt_in_size) {
2380 			result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2381 			goto end;
2382 		}
2383 	} else if (arg_size != e->pt_in_size) {
2384 		result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2385 		goto end;
2386 	}
2387 
2388 	if (retsize != e->pt_out_max) {
2389 		retsize = e->pt_out_max;
2390 		retval = alloca(retsize);
2391 	}
2392 
2393 	if (e->pt_flags & PROTO_FLAG_RETFD)
2394 		e->pt_fd_handler(cp, argp, arg_size, retval, &retsize,
2395 		    e->pt_arg, &retfd);
2396 	else
2397 		e->pt_handler(cp, argp, arg_size, retval, &retsize, e->pt_arg);
2398 
2399 end:
2400 	ti->ti_active_client = NULL;
2401 	client_release(cp);
2402 
2403 end_unheld:
2404 	if (rlp != NULL) {
2405 		/* LINTED alignment */
2406 		rlp->rl_response = *(uint32_t *)retval;
2407 		end_log();
2408 		rlp = NULL;
2409 	}
2410 	ti->ti_client_request = NULL;
2411 	thread_newstate(ti, TI_DOOR_RETURN);
2412 
2413 	if (retval == (char *)&result) {
2414 		assert(result != INVALID_RESULT && retsize == sizeof (result));
2415 	} else {
2416 		/* LINTED alignment */
2417 		result = *(uint32_t *)retval;
2418 	}
2419 	if (retfd != -1) {
2420 		desc.d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE;
2421 		desc.d_data.d_desc.d_descriptor = retfd;
2422 		(void) door_return(retval, retsize, &desc, 1);
2423 	} else {
2424 		(void) door_return(retval, retsize, NULL, 0);
2425 	}
2426 bad_end:
2427 	if (rlp != NULL) {
2428 		rlp->rl_response = -1;
2429 		end_log();
2430 		rlp = NULL;
2431 	}
2432 	(void) door_return(NULL, 0, NULL, 0);
2433 }
2434 
2435 int
2436 create_client(pid_t pid, uint32_t debugflags, int privileged, int *out_fd)
2437 {
2438 	int fd;
2439 
2440 	repcache_client_t *cp;
2441 
2442 	struct door_info info;
2443 
2444 	int door_flags = DOOR_UNREF | DOOR_REFUSE_DESC;
2445 #ifdef DOOR_NO_CANCEL
2446 	door_flags |= DOOR_NO_CANCEL;
2447 #endif
2448 
2449 	cp = client_alloc();
2450 	if (cp == NULL)
2451 		return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2452 
2453 	(void) pthread_mutex_lock(&client_lock);
2454 	cp->rc_id = ++client_maxid;
2455 	(void) pthread_mutex_unlock(&client_lock);
2456 
2457 	cp->rc_all_auths = privileged;
2458 	cp->rc_pid = pid;
2459 	cp->rc_debug = debugflags;
2460 
2461 	start_audit_session(cp);
2462 
2463 	cp->rc_doorfd = door_create(client_switcher, (void *)cp->rc_id,
2464 	    door_flags);
2465 
2466 	if (cp->rc_doorfd < 0) {
2467 		client_free(cp);
2468 		return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2469 	}
2470 #ifdef DOOR_PARAM_DATA_MIN
2471 	(void) door_setparam(cp->rc_doorfd, DOOR_PARAM_DATA_MIN,
2472 	    sizeof (enum rep_protocol_requestid));
2473 #endif
2474 
2475 	if ((fd = dup(cp->rc_doorfd)) < 0 ||
2476 	    door_info(cp->rc_doorfd, &info) < 0) {
2477 		if (fd >= 0)
2478 			(void) close(fd);
2479 		(void) door_revoke(cp->rc_doorfd);
2480 		cp->rc_doorfd = -1;
2481 		client_free(cp);
2482 		return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2483 	}
2484 
2485 	rc_pg_notify_init(&cp->rc_pg_notify);
2486 	rc_notify_info_init(&cp->rc_notify_info);
2487 
2488 	client_insert(cp);
2489 
2490 	cp->rc_doorid = info.di_uniquifier;
2491 	*out_fd = fd;
2492 
2493 	return (REPOSITORY_DOOR_SUCCESS);
2494 }
2495