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