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