6930152 6192139 (no reboot audit -- PSARC/2009/354) points out less than optimal...
[unleashed.git] / usr / src / cmd / svc / configd / client.c
blobcd6e4d8d10deb6d696a5fab5bfe9a1dc0113ee31
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
27 * This is the client layer for svc.configd. All direct protocol interactions
28 * are handled here.
30 * Essentially, the job of this layer is to turn the idempotent protocol
31 * into a series of non-idempotent calls into the object layer, while
32 * also handling the necessary locking.
35 #include <alloca.h>
36 #include <assert.h>
37 #include <bsm/adt_event.h>
38 #include <door.h>
39 #include <errno.h>
40 #include <libintl.h>
41 #include <limits.h>
42 #include <pthread.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <syslog.h>
47 #include <ucred.h>
48 #include <unistd.h>
50 #include <libuutil.h>
52 #include "configd.h"
53 #include "repcache_protocol.h"
55 #define INVALID_CHANGEID (0)
56 #define INVALID_DOORID ((door_id_t)-1)
57 #define INVALID_RESULT ((rep_protocol_responseid_t)INT_MIN)
60 * lint doesn't like constant assertions
62 #ifdef lint
63 #define assert_nolint(x) (void)0
64 #else
65 #define assert_nolint(x) assert(x)
66 #endif
69 * Protects client linkage and the freelist
71 #define CLIENT_HASH_SIZE 64
73 #pragma align 64(client_hash)
74 static client_bucket_t client_hash[CLIENT_HASH_SIZE];
76 static uu_avl_pool_t *entity_pool;
77 static uu_avl_pool_t *iter_pool;
78 static uu_list_pool_t *client_pool;
80 #define CLIENT_HASH(id) (&client_hash[((id) & (CLIENT_HASH_SIZE - 1))])
82 uint_t request_log_size = 1024; /* tunable, before we start */
84 static pthread_mutex_t request_log_lock = PTHREAD_MUTEX_INITIALIZER;
85 static uint_t request_log_cur;
86 request_log_entry_t *request_log;
88 static uint32_t client_maxid;
89 static pthread_mutex_t client_lock; /* protects client_maxid */
91 static request_log_entry_t *
92 get_log(void)
94 thread_info_t *ti = thread_self();
95 return (&ti->ti_log);
98 void
99 log_enter(request_log_entry_t *rlp)
101 if (rlp->rl_start != 0 && request_log != NULL) {
102 request_log_entry_t *logrlp;
104 (void) pthread_mutex_lock(&request_log_lock);
105 assert(request_log_cur < request_log_size);
106 logrlp = &request_log[request_log_cur++];
107 if (request_log_cur == request_log_size)
108 request_log_cur = 0;
109 (void) memcpy(logrlp, rlp, sizeof (*rlp));
110 (void) pthread_mutex_unlock(&request_log_lock);
115 * Note that the svc.configd dmod will join all of the per-thread log entries
116 * with the main log, so that even if the log is disabled, there is some
117 * information available.
119 static request_log_entry_t *
120 start_log(uint32_t clientid)
122 request_log_entry_t *rlp = get_log();
124 log_enter(rlp);
126 (void) memset(rlp, 0, sizeof (*rlp));
127 rlp->rl_start = gethrtime();
128 rlp->rl_tid = pthread_self();
129 rlp->rl_clientid = clientid;
131 return (rlp);
134 void
135 end_log(void)
137 request_log_entry_t *rlp = get_log();
139 rlp->rl_end = gethrtime();
142 static void
143 add_log_ptr(request_log_entry_t *rlp, enum rc_ptr_type type, uint32_t id,
144 void *ptr)
146 request_log_ptr_t *rpp;
148 if (rlp == NULL)
149 return;
151 if (rlp->rl_num_ptrs >= MAX_PTRS)
152 return;
154 rpp = &rlp->rl_ptrs[rlp->rl_num_ptrs++];
155 rpp->rlp_type = type;
156 rpp->rlp_id = id;
157 rpp->rlp_ptr = ptr;
160 * For entities, it's useful to have the node pointer at the start
161 * of the request.
163 if (type == RC_PTR_TYPE_ENTITY && ptr != NULL)
164 rpp->rlp_data = ((repcache_entity_t *)ptr)->re_node.rnp_node;
168 client_is_privileged(void)
170 thread_info_t *ti = thread_self();
172 ucred_t *uc;
174 if (ti->ti_active_client != NULL &&
175 ti->ti_active_client->rc_all_auths)
176 return (1);
178 if ((uc = get_ucred()) == NULL)
179 return (0);
181 return (ucred_is_privileged(uc));
184 /*ARGSUSED*/
185 static int
186 client_compare(const void *lc_arg, const void *rc_arg, void *private)
188 uint32_t l_id = ((const repcache_client_t *)lc_arg)->rc_id;
189 uint32_t r_id = ((const repcache_client_t *)rc_arg)->rc_id;
191 if (l_id > r_id)
192 return (1);
193 if (l_id < r_id)
194 return (-1);
195 return (0);
198 /*ARGSUSED*/
199 static int
200 entity_compare(const void *lc_arg, const void *rc_arg, void *private)
202 uint32_t l_id = ((const repcache_entity_t *)lc_arg)->re_id;
203 uint32_t r_id = ((const repcache_entity_t *)rc_arg)->re_id;
205 if (l_id > r_id)
206 return (1);
207 if (l_id < r_id)
208 return (-1);
209 return (0);
212 /*ARGSUSED*/
213 static int
214 iter_compare(const void *lc_arg, const void *rc_arg, void *private)
216 uint32_t l_id = ((const repcache_iter_t *)lc_arg)->ri_id;
217 uint32_t r_id = ((const repcache_iter_t *)rc_arg)->ri_id;
219 if (l_id > r_id)
220 return (1);
221 if (l_id < r_id)
222 return (-1);
223 return (0);
226 static int
227 client_hash_init(void)
229 int x;
231 assert_nolint(offsetof(repcache_entity_t, re_id) == 0);
232 entity_pool = uu_avl_pool_create("repcache_entitys",
233 sizeof (repcache_entity_t), offsetof(repcache_entity_t, re_link),
234 entity_compare, UU_AVL_POOL_DEBUG);
236 assert_nolint(offsetof(repcache_iter_t, ri_id) == 0);
237 iter_pool = uu_avl_pool_create("repcache_iters",
238 sizeof (repcache_iter_t), offsetof(repcache_iter_t, ri_link),
239 iter_compare, UU_AVL_POOL_DEBUG);
241 assert_nolint(offsetof(repcache_client_t, rc_id) == 0);
242 client_pool = uu_list_pool_create("repcache_clients",
243 sizeof (repcache_client_t), offsetof(repcache_client_t, rc_link),
244 client_compare, UU_LIST_POOL_DEBUG);
246 if (entity_pool == NULL || iter_pool == NULL || client_pool == NULL)
247 return (0);
249 for (x = 0; x < CLIENT_HASH_SIZE; x++) {
250 uu_list_t *lp = uu_list_create(client_pool, &client_hash[x],
251 UU_LIST_SORTED);
252 if (lp == NULL)
253 return (0);
255 (void) pthread_mutex_init(&client_hash[x].cb_lock, NULL);
256 client_hash[x].cb_list = lp;
259 return (1);
262 static repcache_client_t *
263 client_alloc(void)
265 repcache_client_t *cp;
266 cp = uu_zalloc(sizeof (*cp));
267 if (cp == NULL)
268 return (NULL);
270 cp->rc_entities = uu_avl_create(entity_pool, cp, 0);
271 if (cp->rc_entities == NULL)
272 goto fail;
274 cp->rc_iters = uu_avl_create(iter_pool, cp, 0);
275 if (cp->rc_iters == NULL)
276 goto fail;
278 uu_list_node_init(cp, &cp->rc_link, client_pool);
280 cp->rc_doorfd = -1;
281 cp->rc_doorid = INVALID_DOORID;
283 (void) pthread_mutex_init(&cp->rc_lock, NULL);
284 (void) pthread_mutex_init(&cp->rc_annotate_lock, NULL);
286 rc_node_ptr_init(&cp->rc_notify_ptr);
288 return (cp);
290 fail:
291 if (cp->rc_iters != NULL)
292 uu_avl_destroy(cp->rc_iters);
293 if (cp->rc_entities != NULL)
294 uu_avl_destroy(cp->rc_entities);
295 uu_free(cp);
296 return (NULL);
299 static void
300 client_free(repcache_client_t *cp)
302 assert(cp->rc_insert_thr == 0);
303 assert(cp->rc_refcnt == 0);
304 assert(cp->rc_doorfd == -1);
305 assert(cp->rc_doorid == INVALID_DOORID);
306 assert(uu_avl_first(cp->rc_entities) == NULL);
307 assert(uu_avl_first(cp->rc_iters) == NULL);
308 uu_avl_destroy(cp->rc_entities);
309 uu_avl_destroy(cp->rc_iters);
310 uu_list_node_fini(cp, &cp->rc_link, client_pool);
311 (void) pthread_mutex_destroy(&cp->rc_lock);
312 (void) pthread_mutex_destroy(&cp->rc_annotate_lock);
313 rc_node_ptr_free_mem(&cp->rc_notify_ptr);
314 uu_free(cp);
317 static void
318 client_insert(repcache_client_t *cp)
320 client_bucket_t *bp = CLIENT_HASH(cp->rc_id);
321 uu_list_index_t idx;
323 assert(cp->rc_id > 0);
325 (void) pthread_mutex_lock(&bp->cb_lock);
327 * We assume it does not already exist
329 (void) uu_list_find(bp->cb_list, cp, NULL, &idx);
330 uu_list_insert(bp->cb_list, cp, idx);
332 (void) pthread_mutex_unlock(&bp->cb_lock);
335 static repcache_client_t *
336 client_lookup(uint32_t id)
338 client_bucket_t *bp = CLIENT_HASH(id);
339 repcache_client_t *cp;
341 (void) pthread_mutex_lock(&bp->cb_lock);
343 cp = uu_list_find(bp->cb_list, &id, NULL, NULL);
346 * Bump the reference count
348 if (cp != NULL) {
349 (void) pthread_mutex_lock(&cp->rc_lock);
350 assert(!(cp->rc_flags & RC_CLIENT_DEAD));
351 cp->rc_refcnt++;
352 (void) pthread_mutex_unlock(&cp->rc_lock);
354 (void) pthread_mutex_unlock(&bp->cb_lock);
356 return (cp);
359 static void
360 client_release(repcache_client_t *cp)
362 (void) pthread_mutex_lock(&cp->rc_lock);
363 assert(cp->rc_refcnt > 0);
364 assert(cp->rc_insert_thr != pthread_self());
366 --cp->rc_refcnt;
367 (void) pthread_cond_broadcast(&cp->rc_cv);
368 (void) pthread_mutex_unlock(&cp->rc_lock);
372 * We only allow one thread to be inserting at a time, to prevent
373 * insert/insert races.
375 static void
376 client_start_insert(repcache_client_t *cp)
378 (void) pthread_mutex_lock(&cp->rc_lock);
379 assert(cp->rc_refcnt > 0);
381 while (cp->rc_insert_thr != 0) {
382 assert(cp->rc_insert_thr != pthread_self());
383 (void) pthread_cond_wait(&cp->rc_cv, &cp->rc_lock);
385 cp->rc_insert_thr = pthread_self();
386 (void) pthread_mutex_unlock(&cp->rc_lock);
389 static void
390 client_end_insert(repcache_client_t *cp)
392 (void) pthread_mutex_lock(&cp->rc_lock);
393 assert(cp->rc_insert_thr == pthread_self());
394 cp->rc_insert_thr = 0;
395 (void) pthread_cond_broadcast(&cp->rc_cv);
396 (void) pthread_mutex_unlock(&cp->rc_lock);
399 /*ARGSUSED*/
400 static repcache_entity_t *
401 entity_alloc(repcache_client_t *cp)
403 repcache_entity_t *ep = uu_zalloc(sizeof (repcache_entity_t));
404 if (ep != NULL) {
405 uu_avl_node_init(ep, &ep->re_link, entity_pool);
407 return (ep);
410 static void
411 entity_add(repcache_client_t *cp, repcache_entity_t *ep)
413 uu_avl_index_t idx;
415 (void) pthread_mutex_lock(&cp->rc_lock);
416 assert(cp->rc_insert_thr == pthread_self());
418 (void) uu_avl_find(cp->rc_entities, ep, NULL, &idx);
419 uu_avl_insert(cp->rc_entities, ep, idx);
421 (void) pthread_mutex_unlock(&cp->rc_lock);
424 static repcache_entity_t *
425 entity_find(repcache_client_t *cp, uint32_t id)
427 repcache_entity_t *ep;
429 (void) pthread_mutex_lock(&cp->rc_lock);
430 ep = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
431 if (ep != NULL) {
432 add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, ep);
433 (void) pthread_mutex_lock(&ep->re_lock);
435 (void) pthread_mutex_unlock(&cp->rc_lock);
437 return (ep);
441 * Fails with
442 * _DUPLICATE_ID - the ids are equal
443 * _UNKNOWN_ID - an id does not designate an active register
445 static int
446 entity_find2(repcache_client_t *cp, uint32_t id1, repcache_entity_t **out1,
447 uint32_t id2, repcache_entity_t **out2)
449 repcache_entity_t *e1, *e2;
450 request_log_entry_t *rlp;
452 if (id1 == id2)
453 return (REP_PROTOCOL_FAIL_DUPLICATE_ID);
455 (void) pthread_mutex_lock(&cp->rc_lock);
456 e1 = uu_avl_find(cp->rc_entities, &id1, NULL, NULL);
457 e2 = uu_avl_find(cp->rc_entities, &id2, NULL, NULL);
458 if (e1 == NULL || e2 == NULL) {
459 (void) pthread_mutex_unlock(&cp->rc_lock);
460 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
463 assert(e1 != e2);
466 * locks are ordered by id number
468 if (id1 < id2) {
469 (void) pthread_mutex_lock(&e1->re_lock);
470 (void) pthread_mutex_lock(&e2->re_lock);
471 } else {
472 (void) pthread_mutex_lock(&e2->re_lock);
473 (void) pthread_mutex_lock(&e1->re_lock);
475 *out1 = e1;
476 *out2 = e2;
478 (void) pthread_mutex_unlock(&cp->rc_lock);
480 if ((rlp = get_log()) != NULL) {
481 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, id1, e1);
482 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, id2, e2);
485 return (REP_PROTOCOL_SUCCESS);
488 static void
489 entity_release(repcache_entity_t *ep)
491 assert(ep->re_node.rnp_node == NULL ||
492 !MUTEX_HELD(&ep->re_node.rnp_node->rn_lock));
493 (void) pthread_mutex_unlock(&ep->re_lock);
496 static void
497 entity_destroy(repcache_entity_t *entity)
499 (void) pthread_mutex_lock(&entity->re_lock);
500 rc_node_clear(&entity->re_node, 0);
501 (void) pthread_mutex_unlock(&entity->re_lock);
503 uu_avl_node_fini(entity, &entity->re_link, entity_pool);
504 (void) pthread_mutex_destroy(&entity->re_lock);
505 rc_node_ptr_free_mem(&entity->re_node);
506 uu_free(entity);
509 static void
510 entity_remove(repcache_client_t *cp, uint32_t id)
512 repcache_entity_t *entity;
514 (void) pthread_mutex_lock(&cp->rc_lock);
515 entity = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
516 if (entity != NULL) {
517 add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, entity);
519 uu_avl_remove(cp->rc_entities, entity);
521 (void) pthread_mutex_unlock(&cp->rc_lock);
523 if (entity != NULL)
524 entity_destroy(entity);
527 static void
528 entity_cleanup(repcache_client_t *cp)
530 repcache_entity_t *ep;
531 void *cookie = NULL;
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);
539 (void) pthread_mutex_unlock(&cp->rc_lock);
542 /*ARGSUSED*/
543 static repcache_iter_t *
544 iter_alloc(repcache_client_t *cp)
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);
553 static void
554 iter_add(repcache_client_t *cp, repcache_iter_t *iter)
556 uu_list_index_t idx;
558 (void) pthread_mutex_lock(&cp->rc_lock);
559 assert(cp->rc_insert_thr == pthread_self());
561 (void) uu_avl_find(cp->rc_iters, iter, NULL, &idx);
562 uu_avl_insert(cp->rc_iters, iter, idx);
564 (void) pthread_mutex_unlock(&cp->rc_lock);
567 static repcache_iter_t *
568 iter_find(repcache_client_t *cp, uint32_t id)
570 repcache_iter_t *iter;
572 (void) pthread_mutex_lock(&cp->rc_lock);
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);
579 (void) pthread_mutex_unlock(&cp->rc_lock);
581 return (iter);
585 * Fails with
586 * _UNKNOWN_ID - iter_id or entity_id does not designate an active register
588 static int
589 iter_find_w_entity(repcache_client_t *cp, uint32_t iter_id,
590 repcache_iter_t **iterp, uint32_t entity_id, repcache_entity_t **epp)
592 repcache_iter_t *iter;
593 repcache_entity_t *ep;
594 request_log_entry_t *rlp;
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);
600 assert(iter == NULL || !MUTEX_HELD(&iter->ri_lock));
601 assert(ep == NULL || !MUTEX_HELD(&ep->re_lock));
603 if (iter == NULL || ep == NULL) {
604 (void) pthread_mutex_unlock(&cp->rc_lock);
605 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
608 (void) pthread_mutex_lock(&iter->ri_lock);
609 (void) pthread_mutex_lock(&ep->re_lock);
611 (void) pthread_mutex_unlock(&cp->rc_lock);
613 *iterp = iter;
614 *epp = ep;
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);
621 return (REP_PROTOCOL_SUCCESS);
624 static void
625 iter_release(repcache_iter_t *iter)
627 (void) pthread_mutex_unlock(&iter->ri_lock);
630 static void
631 iter_destroy(repcache_iter_t *iter)
633 (void) pthread_mutex_lock(&iter->ri_lock);
634 rc_iter_destroy(&iter->ri_iter);
635 (void) pthread_mutex_unlock(&iter->ri_lock);
637 uu_avl_node_fini(iter, &iter->ri_link, iter_pool);
638 (void) pthread_mutex_destroy(&iter->ri_lock);
639 uu_free(iter);
642 static void
643 iter_remove(repcache_client_t *cp, uint32_t id)
645 repcache_iter_t *iter;
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);
653 if (iter != NULL)
654 iter_destroy(iter);
657 static void
658 iter_cleanup(repcache_client_t *cp)
660 repcache_iter_t *iter;
661 void *cookie = NULL;
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);
669 (void) pthread_mutex_unlock(&cp->rc_lock);
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.
677 static void
678 client_destroy(uint32_t id)
680 client_bucket_t *bp = CLIENT_HASH(id);
681 repcache_client_t *cp;
683 (void) pthread_mutex_lock(&bp->cb_lock);
685 cp = uu_list_find(bp->cb_list, &id, NULL, NULL);
687 if (cp == NULL) {
688 (void) pthread_mutex_unlock(&bp->cb_lock);
689 return;
692 uu_list_remove(bp->cb_list, cp);
694 (void) pthread_mutex_unlock(&bp->cb_lock);
696 /* kick the waiters out */
697 rc_notify_info_fini(&cp->rc_notify_info);
699 (void) pthread_mutex_lock(&cp->rc_lock);
700 assert(!(cp->rc_flags & RC_CLIENT_DEAD));
701 cp->rc_flags |= RC_CLIENT_DEAD;
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;
710 while (cp->rc_refcnt > 0)
711 (void) pthread_cond_wait(&cp->rc_cv, &cp->rc_lock);
713 assert(cp->rc_insert_thr == 0 && cp->rc_notify_thr == 0);
714 (void) pthread_mutex_unlock(&cp->rc_lock);
717 * destroy outstanding objects
719 entity_cleanup(cp);
720 iter_cleanup(cp);
723 * clean up notifications
725 rc_pg_notify_fini(&cp->rc_pg_notify);
728 * clean up annotations
730 if (cp->rc_operation != NULL)
731 free((void *)cp->rc_operation);
732 if (cp->rc_file != NULL)
733 free((void *)cp->rc_file);
736 * End audit session.
738 (void) adt_end_session(cp->rc_adt_session);
740 client_free(cp);
744 * Fails with
745 * _TYPE_MISMATCH - the entity is already set up with a different type
746 * _NO_RESOURCES - out of memory
748 static int
749 entity_setup(repcache_client_t *cp, struct rep_protocol_entity_setup *rpr)
751 repcache_entity_t *ep;
752 uint32_t type;
754 client_start_insert(cp);
756 if ((ep = entity_find(cp, rpr->rpr_entityid)) != NULL) {
757 type = ep->re_type;
758 entity_release(ep);
760 client_end_insert(cp);
762 if (type != rpr->rpr_entitytype)
763 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
764 return (REP_PROTOCOL_SUCCESS);
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);
780 ep = entity_alloc(cp);
781 if (ep == NULL) {
782 client_end_insert(cp);
783 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
786 ep->re_id = rpr->rpr_entityid;
787 ep->re_changeid = INVALID_CHANGEID;
789 ep->re_type = type;
790 rc_node_ptr_init(&ep->re_node);
792 entity_add(cp, ep);
793 client_end_insert(cp);
794 return (REP_PROTOCOL_SUCCESS);
797 /*ARGSUSED*/
798 static void
799 entity_name(repcache_client_t *cp, const void *in, size_t insz, void *out_arg,
800 size_t *outsz, void *arg)
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);
807 assert(*outsz == sizeof (*out));
809 ep = entity_find(cp, rpr->rpr_entityid);
811 if (ep == NULL) {
812 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
813 *outsz = sizeof (out->rpr_response);
814 return;
816 out->rpr_response = rc_node_name(&ep->re_node, out->rpr_name,
817 sz, rpr->rpr_answertype, &sz);
818 entity_release(ep);
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.
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]);
831 /*ARGSUSED*/
832 static void
833 entity_parent_type(repcache_client_t *cp, const void *in, size_t insz,
834 void *out_arg, size_t *outsz, void *arg)
836 const struct rep_protocol_entity_name *rpr = in;
837 struct rep_protocol_integer_response *out = out_arg;
838 repcache_entity_t *ep;
840 assert(*outsz == sizeof (*out));
842 ep = entity_find(cp, rpr->rpr_entityid);
844 if (ep == NULL) {
845 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
846 *outsz = sizeof (out->rpr_response);
847 return;
850 out->rpr_response = rc_node_parent_type(&ep->re_node, &out->rpr_value);
851 entity_release(ep);
853 if (out->rpr_response != REP_PROTOCOL_SUCCESS)
854 *outsz = sizeof (out->rpr_response);
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
868 static int
869 entity_get_child(repcache_client_t *cp,
870 struct rep_protocol_entity_get_child *rpr)
872 repcache_entity_t *parent, *child;
873 int result;
875 uint32_t parentid = rpr->rpr_entityid;
876 uint32_t childid = rpr->rpr_childid;
878 result = entity_find2(cp, childid, &child, parentid, &parent);
879 if (result != REP_PROTOCOL_SUCCESS)
880 return (result);
882 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
884 result = rc_node_get_child(&parent->re_node, rpr->rpr_name,
885 child->re_type, &child->re_node);
887 entity_release(child);
888 entity_release(parent);
890 return (result);
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)
904 static int
905 entity_get_parent(repcache_client_t *cp, struct rep_protocol_entity_parent *rpr)
907 repcache_entity_t *child, *parent;
908 int result;
910 uint32_t childid = rpr->rpr_entityid;
911 uint32_t outid = rpr->rpr_outid;
913 result = entity_find2(cp, childid, &child, outid, &parent);
914 if (result != REP_PROTOCOL_SUCCESS)
915 return (result);
917 result = rc_node_get_parent(&child->re_node, parent->re_type,
918 &parent->re_node);
920 entity_release(child);
921 entity_release(parent);
923 return (result);
926 static int
927 entity_get(repcache_client_t *cp, struct rep_protocol_entity_get *rpr)
929 repcache_entity_t *ep;
930 int result;
932 ep = entity_find(cp, rpr->rpr_entityid);
934 if (ep == NULL)
935 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
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;
950 entity_release(ep);
952 return (result);
955 static int
956 entity_update(repcache_client_t *cp, struct rep_protocol_entity_update *rpr)
958 repcache_entity_t *ep;
959 int result;
961 if (rpr->rpr_changeid == INVALID_CHANGEID)
962 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
964 ep = entity_find(cp, rpr->rpr_entityid);
966 if (ep == NULL)
967 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
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;
977 entity_release(ep);
979 return (result);
982 static int
983 entity_reset(repcache_client_t *cp, struct rep_protocol_entity_reset *rpr)
985 repcache_entity_t *ep;
987 ep = entity_find(cp, rpr->rpr_entityid);
988 if (ep == NULL)
989 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
991 rc_node_clear(&ep->re_node, 0);
992 ep->re_txstate = REPCACHE_TX_INIT;
994 entity_release(ep);
995 return (REP_PROTOCOL_SUCCESS);
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
1016 static int
1017 entity_create_child(repcache_client_t *cp,
1018 struct rep_protocol_entity_create_child *rpr)
1020 repcache_entity_t *parent;
1021 repcache_entity_t *child;
1023 uint32_t parentid = rpr->rpr_entityid;
1024 uint32_t childid = rpr->rpr_childid;
1026 int result;
1028 if (rpr->rpr_changeid == INVALID_CHANGEID)
1029 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1031 result = entity_find2(cp, parentid, &parent, childid, &child);
1032 if (result != REP_PROTOCOL_SUCCESS)
1033 return (result);
1035 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
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;
1046 entity_release(parent);
1047 entity_release(child);
1049 return (result);
1052 static int
1053 entity_create_pg(repcache_client_t *cp,
1054 struct rep_protocol_entity_create_pg *rpr)
1056 repcache_entity_t *parent;
1057 repcache_entity_t *child;
1059 uint32_t parentid = rpr->rpr_entityid;
1060 uint32_t childid = rpr->rpr_childid;
1062 int result;
1064 if (rpr->rpr_changeid == INVALID_CHANGEID)
1065 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1067 result = entity_find2(cp, parentid, &parent, childid, &child);
1068 if (result != REP_PROTOCOL_SUCCESS)
1069 return (result);
1071 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1072 rpr->rpr_type[sizeof (rpr->rpr_type) - 1] = 0;
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;
1084 entity_release(parent);
1085 entity_release(child);
1087 return (result);
1090 static int
1091 entity_delete(repcache_client_t *cp,
1092 struct rep_protocol_entity_delete *rpr)
1094 repcache_entity_t *entity;
1096 uint32_t entityid = rpr->rpr_entityid;
1098 int result;
1100 if (rpr->rpr_changeid == INVALID_CHANGEID)
1101 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1103 entity = entity_find(cp, entityid);
1105 if (entity == NULL)
1106 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
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;
1116 entity_release(entity);
1118 return (result);
1121 static rep_protocol_responseid_t
1122 entity_teardown(repcache_client_t *cp, struct rep_protocol_entity_teardown *rpr)
1124 entity_remove(cp, rpr->rpr_entityid);
1126 return (REP_PROTOCOL_SUCCESS);
1130 * Fails with
1131 * _MISORDERED - the iterator exists and is not reset
1132 * _NO_RESOURCES - out of memory
1134 static int
1135 iter_setup(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1137 repcache_iter_t *iter;
1138 uint32_t sequence;
1140 client_start_insert(cp);
1142 * If the iter already exists, and hasn't been read from,
1143 * we assume the previous call succeeded.
1145 if ((iter = iter_find(cp, rpr->rpr_iterid)) != NULL) {
1146 sequence = iter->ri_sequence;
1147 iter_release(iter);
1149 client_end_insert(cp);
1151 if (sequence != 0)
1152 return (REP_PROTOCOL_FAIL_MISORDERED);
1153 return (REP_PROTOCOL_SUCCESS);
1156 iter = iter_alloc(cp);
1157 if (iter == NULL) {
1158 client_end_insert(cp);
1159 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
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);
1167 client_end_insert(cp);
1168 return (REP_PROTOCOL_SUCCESS);
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
1184 static int
1185 iter_start(repcache_client_t *cp, struct rep_protocol_iter_start *rpr)
1187 int result;
1188 repcache_iter_t *iter;
1189 repcache_entity_t *ep;
1191 result = iter_find_w_entity(cp, rpr->rpr_iterid, &iter,
1192 rpr->rpr_entity, &ep);
1194 if (result != REP_PROTOCOL_SUCCESS)
1195 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1197 if (iter->ri_sequence > 1) {
1198 result = REP_PROTOCOL_FAIL_MISORDERED;
1199 goto end;
1202 if (iter->ri_sequence == 1) {
1203 result = REP_PROTOCOL_SUCCESS;
1204 goto end;
1207 rpr->rpr_pattern[sizeof (rpr->rpr_pattern) - 1] = 0;
1209 result = rc_node_setup_iter(&ep->re_node, &iter->ri_iter,
1210 rpr->rpr_itertype, rpr->rpr_flags, rpr->rpr_pattern);
1212 if (result == REP_PROTOCOL_SUCCESS)
1213 iter->ri_sequence++;
1215 end:
1216 iter_release(iter);
1217 entity_release(ep);
1218 return (result);
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
1234 * For composed property group iterators, can also return
1235 * _TYPE_MISMATCH - parent cannot have type children
1236 * _BACKEND_ACCESS
1238 static rep_protocol_responseid_t
1239 iter_read(repcache_client_t *cp, struct rep_protocol_iter_read *rpr)
1241 rep_protocol_responseid_t result;
1242 repcache_iter_t *iter;
1243 repcache_entity_t *ep;
1244 uint32_t sequence;
1246 result = iter_find_w_entity(cp, rpr->rpr_iterid, &iter,
1247 rpr->rpr_entityid, &ep);
1249 if (result != REP_PROTOCOL_SUCCESS)
1250 return (result);
1252 sequence = rpr->rpr_sequence;
1254 if (iter->ri_sequence == 0) {
1255 iter_release(iter);
1256 entity_release(ep);
1257 return (REP_PROTOCOL_FAIL_NOT_SET);
1260 if (sequence == 1) {
1261 iter_release(iter);
1262 entity_release(ep);
1263 return (REP_PROTOCOL_FAIL_MISORDERED);
1266 if (sequence == iter->ri_sequence) {
1267 iter_release(iter);
1268 entity_release(ep);
1269 return (REP_PROTOCOL_SUCCESS);
1272 if (sequence == iter->ri_sequence + 1) {
1273 result = rc_iter_next(iter->ri_iter, &ep->re_node,
1274 ep->re_type);
1276 if (result == REP_PROTOCOL_SUCCESS)
1277 iter->ri_sequence++;
1279 iter_release(iter);
1280 entity_release(ep);
1282 return (result);
1285 iter_release(iter);
1286 entity_release(ep);
1287 return (REP_PROTOCOL_FAIL_MISORDERED);
1290 /*ARGSUSED*/
1291 static void
1292 iter_read_value(repcache_client_t *cp, const void *in, size_t insz,
1293 void *out_arg, size_t *outsz, void *arg)
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;
1299 repcache_iter_t *iter;
1300 uint32_t sequence;
1301 int repeat;
1303 assert(*outsz == sizeof (*out));
1305 iter = iter_find(cp, rpr->rpr_iterid);
1307 if (iter == NULL) {
1308 result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1309 goto out;
1312 sequence = rpr->rpr_sequence;
1314 if (iter->ri_sequence == 0) {
1315 iter_release(iter);
1316 result = REP_PROTOCOL_FAIL_NOT_SET;
1317 goto out;
1320 repeat = (sequence == iter->ri_sequence);
1322 if (sequence == 1 || (!repeat && sequence != iter->ri_sequence + 1)) {
1323 iter_release(iter);
1324 result = REP_PROTOCOL_FAIL_MISORDERED;
1325 goto out;
1328 result = rc_iter_next_value(iter->ri_iter, out, outsz, repeat);
1330 if (!repeat && result == REP_PROTOCOL_SUCCESS)
1331 iter->ri_sequence++;
1333 iter_release(iter);
1335 out:
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.
1341 if (result != REP_PROTOCOL_SUCCESS && result != REP_PROTOCOL_DONE)
1342 *outsz = sizeof (out->rpr_response);
1344 out->rpr_response = result;
1347 static int
1348 iter_reset(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1350 repcache_iter_t *iter = iter_find(cp, rpr->rpr_iterid);
1352 if (iter == NULL)
1353 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1355 if (iter->ri_sequence != 0) {
1356 iter->ri_sequence = 0;
1357 rc_iter_destroy(&iter->ri_iter);
1359 iter_release(iter);
1360 return (REP_PROTOCOL_SUCCESS);
1363 static rep_protocol_responseid_t
1364 iter_teardown(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1366 iter_remove(cp, rpr->rpr_iterid);
1368 return (REP_PROTOCOL_SUCCESS);
1371 static rep_protocol_responseid_t
1372 tx_start(repcache_client_t *cp, struct rep_protocol_transaction_start *rpr)
1374 repcache_entity_t *tx;
1375 repcache_entity_t *ep;
1376 rep_protocol_responseid_t result;
1378 uint32_t txid = rpr->rpr_entityid_tx;
1379 uint32_t epid = rpr->rpr_entityid;
1381 result = entity_find2(cp, txid, &tx, epid, &ep);
1382 if (result != REP_PROTOCOL_SUCCESS)
1383 return (result);
1385 if (tx->re_txstate == REPCACHE_TX_SETUP) {
1386 result = REP_PROTOCOL_SUCCESS;
1387 goto end;
1389 if (tx->re_txstate != REPCACHE_TX_INIT) {
1390 result = REP_PROTOCOL_FAIL_MISORDERED;
1391 goto end;
1394 result = rc_node_setup_tx(&ep->re_node, &tx->re_node);
1396 end:
1397 if (result == REP_PROTOCOL_SUCCESS)
1398 tx->re_txstate = REPCACHE_TX_SETUP;
1399 else
1400 rc_node_clear(&tx->re_node, 0);
1402 entity_release(ep);
1403 entity_release(tx);
1404 return (result);
1407 /*ARGSUSED*/
1408 static void
1409 tx_commit(repcache_client_t *cp, const void *in, size_t insz,
1410 void *out_arg, size_t *outsz, void *arg)
1412 struct rep_protocol_response *out = out_arg;
1413 const struct rep_protocol_transaction_commit *rpr = in;
1414 repcache_entity_t *tx;
1416 assert(*outsz == sizeof (*out));
1417 assert(insz >= REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE);
1419 if (rpr->rpr_size != insz) {
1420 out->rpr_response = REP_PROTOCOL_FAIL_BAD_REQUEST;
1421 return;
1424 tx = entity_find(cp, rpr->rpr_entityid);
1426 if (tx == NULL) {
1427 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1428 return;
1431 switch (tx->re_txstate) {
1432 case REPCACHE_TX_INIT:
1433 out->rpr_response = REP_PROTOCOL_FAIL_MISORDERED;
1434 break;
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);
1440 if (out->rpr_response == REP_PROTOCOL_SUCCESS) {
1441 tx->re_txstate = REPCACHE_TX_COMMITTED;
1442 rc_node_clear(&tx->re_node, 0);
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;
1454 entity_release(tx);
1457 static rep_protocol_responseid_t
1458 next_snaplevel(repcache_client_t *cp, struct rep_protocol_entity_pair *rpr)
1460 repcache_entity_t *src;
1461 repcache_entity_t *dest;
1463 uint32_t srcid = rpr->rpr_entity_src;
1464 uint32_t destid = rpr->rpr_entity_dst;
1466 int result;
1468 result = entity_find2(cp, srcid, &src, destid, &dest);
1469 if (result != REP_PROTOCOL_SUCCESS)
1470 return (result);
1472 result = rc_node_next_snaplevel(&src->re_node, &dest->re_node);
1474 entity_release(src);
1475 entity_release(dest);
1477 return (result);
1480 static rep_protocol_responseid_t
1481 snapshot_take(repcache_client_t *cp, struct rep_protocol_snapshot_take *rpr)
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;
1488 int result;
1490 result = entity_find2(cp, srcid, &src, destid, &dest);
1491 if (result != REP_PROTOCOL_SUCCESS)
1492 return (result);
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;
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;
1509 entity_release(src);
1510 entity_release(dest);
1512 return (result);
1515 static rep_protocol_responseid_t
1516 snapshot_take_named(repcache_client_t *cp,
1517 struct rep_protocol_snapshot_take_named *rpr)
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;
1524 int result;
1526 result = entity_find2(cp, srcid, &src, destid, &dest);
1527 if (result != REP_PROTOCOL_SUCCESS)
1528 return (result);
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;
1537 result = rc_snapshot_take_new(&src->re_node, rpr->rpr_svcname,
1538 rpr->rpr_instname, rpr->rpr_name, &dest->re_node);
1540 entity_release(src);
1541 entity_release(dest);
1543 return (result);
1546 static rep_protocol_responseid_t
1547 snapshot_attach(repcache_client_t *cp, struct rep_protocol_snapshot_attach *rpr)
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;
1554 int result;
1556 result = entity_find2(cp, srcid, &src, destid, &dest);
1557 if (result != REP_PROTOCOL_SUCCESS)
1558 return (result);
1560 result = rc_snapshot_attach(&src->re_node, &dest->re_node);
1562 entity_release(src);
1563 entity_release(dest);
1565 return (result);
1568 /*ARGSUSED*/
1569 static void
1570 property_get_type(repcache_client_t *cp, const void *in, size_t insz,
1571 void *out_arg, size_t *outsz, void *arg)
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;
1578 assert(*outsz == sizeof (*out));
1580 ep = entity_find(cp, rpr->rpr_entityid);
1582 if (ep == NULL) {
1583 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1584 *outsz = sizeof (out->rpr_response);
1585 return;
1588 out->rpr_response = rc_node_get_property_type(&ep->re_node, &t);
1590 entity_release(ep);
1592 if (out->rpr_response != REP_PROTOCOL_SUCCESS)
1593 *outsz = sizeof (out->rpr_response);
1594 else
1595 out->rpr_value = t;
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.
1606 * Succeeds with:
1607 * _SUCCESS - The property has 1 value.
1608 * _TRUNCATED - The property has >1 value.
1610 /*ARGSUSED*/
1611 static void
1612 property_get_value(repcache_client_t *cp, const void *in, size_t insz,
1613 void *out_arg, size_t *outsz, void *arg)
1615 const struct rep_protocol_property_request *rpr = in;
1616 struct rep_protocol_value_response *out = out_arg;
1617 repcache_entity_t *ep;
1619 assert(*outsz == sizeof (*out));
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;
1628 out->rpr_response = rc_node_get_property_value(&ep->re_node, out,
1629 outsz);
1631 entity_release(ep);
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.
1638 if (out->rpr_response != REP_PROTOCOL_SUCCESS &&
1639 out->rpr_response != REP_PROTOCOL_FAIL_TRUNCATED)
1640 *outsz = sizeof (out->rpr_response);
1643 static rep_protocol_responseid_t
1644 propertygrp_notify(repcache_client_t *cp,
1645 struct rep_protocol_propertygrp_request *rpr, int *out_fd)
1647 int fds[2];
1648 int ours, theirs;
1650 rep_protocol_responseid_t result;
1651 repcache_entity_t *ep;
1653 if (pipe(fds) < 0)
1654 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1656 ours = fds[0];
1657 theirs = fds[1];
1659 if ((ep = entity_find(cp, rpr->rpr_entityid)) == NULL) {
1660 result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1661 goto fail;
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.
1669 result = rc_pg_notify_setup(&cp->rc_pg_notify, &ep->re_node,
1670 ours);
1672 entity_release(ep);
1674 if (result != REP_PROTOCOL_SUCCESS)
1675 goto fail;
1677 *out_fd = theirs;
1678 return (REP_PROTOCOL_SUCCESS);
1680 fail:
1681 (void) close(ours);
1682 (void) close(theirs);
1684 return (result);
1687 static rep_protocol_responseid_t
1688 client_add_notify(repcache_client_t *cp,
1689 struct rep_protocol_notify_request *rpr)
1691 rpr->rpr_pattern[sizeof (rpr->rpr_pattern) - 1] = 0;
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));
1698 case REP_PROTOCOL_NOTIFY_PGTYPE:
1699 return (rc_notify_info_add_type(&cp->rc_notify_info,
1700 rpr->rpr_pattern));
1702 default:
1703 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1707 /*ARGSUSED*/
1708 static void
1709 client_wait(repcache_client_t *cp, const void *in, size_t insz,
1710 void *out_arg, size_t *outsz, void *arg)
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;
1717 assert(*outsz == sizeof (*out));
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;
1726 cp->rc_notify_thr = pthread_self();
1727 (void) pthread_mutex_unlock(&cp->rc_lock);
1729 result = rc_notify_info_wait(&cp->rc_notify_info, &cp->rc_notify_ptr,
1730 out->rpr_fmri, sizeof (out->rpr_fmri));
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;
1740 entity_release(ep);
1741 } else {
1742 result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1744 rc_node_clear(&cp->rc_notify_ptr, 0);
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);
1752 out->rpr_response = result;
1753 if (result != REP_PROTOCOL_SUCCESS)
1754 *outsz = sizeof (out->rpr_response);
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.
1768 static rep_protocol_responseid_t
1769 backup_repository(repcache_client_t *cp,
1770 struct rep_protocol_backup_request *rpr)
1772 rep_protocol_responseid_t result;
1773 ucred_t *uc = get_ucred();
1775 if (!client_is_privileged() && (uc == NULL || ucred_geteuid(uc) != 0))
1776 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
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);
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;
1790 (void) pthread_mutex_unlock(&cp->rc_lock);
1792 return (result);
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.
1803 * Fails with
1804 * _NO_RESOURCES - unable to allocate memory
1806 static rep_protocol_responseid_t
1807 set_annotation(repcache_client_t *cp, struct rep_protocol_annotation *rpr)
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;
1816 (void) memset((void *)old_ptrs, 0, sizeof (old_ptrs));
1818 /* Copy rpr_operation and rpr_file if they are not empty strings. */
1819 if (rpr->rpr_operation[0] != 0) {
1821 * Make sure that client did not send us an unterminated buffer.
1823 rpr->rpr_operation[sizeof (rpr->rpr_operation) - 1] = 0;
1824 if ((operation = strdup(rpr->rpr_operation)) == NULL)
1825 goto out;
1827 if (rpr->rpr_file[0] != 0) {
1829 * Make sure that client did not send us an unterminated buffer.
1831 rpr->rpr_file[sizeof (rpr->rpr_file) - 1] = 0;
1832 if ((file = strdup(rpr->rpr_file)) == NULL)
1833 goto out;
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;
1841 /* Save pointers to annotation strings. */
1842 cp->rc_operation = operation;
1843 cp->rc_file = file;
1846 * Set annotation flag. Annotations should be turned on if either
1847 * operation or file are not NULL.
1849 cp->rc_annotate = (operation != NULL) || (file != NULL);
1850 (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1853 * operation and file pointers are saved in cp, so don't free them
1854 * during cleanup.
1856 operation = NULL;
1857 file = NULL;
1858 rc = REP_PROTOCOL_SUCCESS;
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.
1868 #ifndef NATIVE_BUILD
1870 * Set the appropriate audit session id.
1872 if (cp->rc_annotate) {
1874 * We're starting a group of annotated audit events, so
1875 * create and set an audit session ID for this annotation.
1877 adt_get_auid(cp->rc_adt_session, &audit_uid);
1878 sessionid = adt_get_unique_id(audit_uid);
1879 } else {
1881 * Annotation is done so restore our client audit session
1882 * id.
1884 sessionid = cp->rc_adt_sessionid;
1886 adt_set_asid(cp->rc_adt_session, sessionid);
1887 #endif /* NATIVE_BUILD */
1889 out:
1890 if (operation != NULL)
1891 free((void *)operation);
1892 if (file != NULL)
1893 free((void *)file);
1894 free((void *)old_ptrs[0]);
1895 free((void *)old_ptrs[1]);
1896 return (rc);
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.
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.
1910 client_annotation_needed(char *operation, size_t oper_sz,
1911 char *file, size_t file_sz)
1913 thread_info_t *ti = thread_self();
1914 repcache_client_t *cp = ti->ti_active_client;
1915 int rc = 0;
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;
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;
1940 (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1941 return (rc);
1944 void
1945 client_annotation_finished()
1947 thread_info_t *ti = thread_self();
1948 repcache_client_t *cp = ti->ti_active_client;
1950 (void) pthread_mutex_lock(&cp->rc_annotate_lock);
1951 cp->rc_annotate = 0;
1952 (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1955 static void
1956 start_audit_session(repcache_client_t *cp)
1958 ucred_t *cred = NULL;
1959 adt_session_data_t *session;
1962 * A NULL session pointer value can legally be used in all
1963 * subsequent calls to adt_* functions.
1965 cp->rc_adt_session = NULL;
1967 if (!adt_audit_state(AUC_AUDITING))
1968 return;
1970 if (door_ucred(&cred) != 0) {
1971 switch (errno) {
1972 case EAGAIN:
1973 case ENOMEM:
1974 syslog(LOG_ERR, gettext("start_audit_session(): cannot "
1975 "get ucred. %m\n"));
1976 return;
1977 case EINVAL:
1979 * Door client went away. This is a normal,
1980 * although infrequent event, so there is no need
1981 * to create a syslog message.
1983 return;
1984 case EFAULT:
1985 default:
1986 bad_error("door_ucred", errno);
1987 return;
1990 if (adt_start_session(&session, NULL, 0) != 0) {
1991 syslog(LOG_ERR, gettext("start_audit_session(): could not "
1992 "start audit session.\n"));
1993 ucred_free(cred);
1994 return;
1996 if (adt_set_from_ucred(session, cred, ADT_NEW) != 0) {
1997 syslog(LOG_ERR, gettext("start_audit_session(): cannot set "
1998 "audit session data from ucred\n"));
1999 /* Something went wrong. End the session. */
2000 (void) adt_end_session(session);
2001 ucred_free(cred);
2002 return;
2005 /* All went well. Save the session data and session ID */
2006 cp->rc_adt_session = session;
2007 adt_get_asid(session, &cp->rc_adt_sessionid);
2009 ucred_free(cred);
2013 * Handle switch client request
2015 * This routine can return:
2017 * _PERMISSION_DENIED not enough privileges to do request.
2018 * _UNKNOWN file operation error (details written to
2019 * the console).
2020 * _SUCCESS switch operation is completed.
2021 * _BACKEND_ACCESS backend access fails.
2022 * _NO_RESOURCES out of memory.
2023 * _BACKEND_READONLY backend is not writable.
2025 static rep_protocol_responseid_t
2026 repository_switch(repcache_client_t *cp,
2027 struct rep_protocol_switch_request *rpr)
2029 rep_protocol_responseid_t result;
2030 ucred_t *uc = get_ucred();
2032 if (!client_is_privileged() && (uc == NULL ||
2033 ucred_geteuid(uc) != 0)) {
2034 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
2037 (void) pthread_mutex_lock(&cp->rc_lock);
2038 if (rpr->rpr_changeid != cp->rc_changeid) {
2039 if ((result = backend_switch(rpr->rpr_flag)) ==
2040 REP_PROTOCOL_SUCCESS)
2041 cp->rc_changeid = rpr->rpr_changeid;
2042 } else {
2043 result = REP_PROTOCOL_SUCCESS;
2045 (void) pthread_mutex_unlock(&cp->rc_lock);
2047 return (result);
2050 typedef rep_protocol_responseid_t protocol_simple_f(repcache_client_t *cp,
2051 const void *rpr);
2053 /*ARGSUSED*/
2054 static void
2055 simple_handler(repcache_client_t *cp, const void *in, size_t insz,
2056 void *out_arg, size_t *outsz, void *arg)
2058 protocol_simple_f *f = (protocol_simple_f *)arg;
2059 rep_protocol_response_t *out = out_arg;
2061 assert(*outsz == sizeof (*out));
2062 assert(f != NULL);
2064 out->rpr_response = (*f)(cp, in);
2067 typedef rep_protocol_responseid_t protocol_simple_fd_f(repcache_client_t *cp,
2068 const void *rpr, int *out_fd);
2070 /*ARGSUSED*/
2071 static void
2072 simple_fd_handler(repcache_client_t *cp, const void *in, size_t insz,
2073 void *out_arg, size_t *outsz, void *arg, int *out_fd)
2075 protocol_simple_fd_f *f = (protocol_simple_fd_f *)arg;
2076 rep_protocol_response_t *out = out_arg;
2078 assert(*outsz == sizeof (*out));
2079 assert(f != NULL);
2081 out->rpr_response = (*f)(cp, in, out_fd);
2084 typedef void protocol_handler_f(repcache_client_t *, const void *in,
2085 size_t insz, void *out, size_t *outsz, void *arg);
2087 typedef void protocol_handler_fdret_f(repcache_client_t *, const void *in,
2088 size_t insz, void *out, size_t *outsz, void *arg, int *fd_out);
2090 #define PROTO(p, f, in) { \
2091 p, #p, simple_handler, (void *)(&f), NULL, \
2092 sizeof (in), sizeof (rep_protocol_response_t), 0 \
2095 #define PROTO_FD_OUT(p, f, in) { \
2096 p, #p, NULL, (void *)(&f), simple_fd_handler, \
2097 sizeof (in), \
2098 sizeof (rep_protocol_response_t), \
2099 PROTO_FLAG_RETFD \
2102 #define PROTO_VARIN(p, f, insz) { \
2103 p, #p, &(f), NULL, NULL, \
2104 insz, sizeof (rep_protocol_response_t), \
2105 PROTO_FLAG_VARINPUT \
2108 #define PROTO_UINT_OUT(p, f, in) { \
2109 p, #p, &(f), NULL, NULL, \
2110 sizeof (in), \
2111 sizeof (struct rep_protocol_integer_response), 0 \
2114 #define PROTO_NAME_OUT(p, f, in) { \
2115 p, #p, &(f), NULL, NULL, \
2116 sizeof (in), \
2117 sizeof (struct rep_protocol_name_response), 0 \
2120 #define PROTO_FMRI_OUT(p, f, in) { \
2121 p, #p, &(f), NULL, NULL, \
2122 sizeof (in), \
2123 sizeof (struct rep_protocol_fmri_response), 0 \
2126 #define PROTO_VALUE_OUT(p, f, in) { \
2127 p, #p, &(f), NULL, NULL, \
2128 sizeof (in), \
2129 sizeof (struct rep_protocol_value_response), 0 \
2132 #define PROTO_PANIC(p) { p, #p, NULL, NULL, NULL, 0, 0, PROTO_FLAG_PANIC }
2133 #define PROTO_END() { 0, NULL, NULL, NULL, NULL, 0, 0, PROTO_FLAG_PANIC }
2135 #define PROTO_FLAG_PANIC 0x00000001 /* should never be called */
2136 #define PROTO_FLAG_VARINPUT 0x00000004 /* in_size is minimum size */
2137 #define PROTO_FLAG_RETFD 0x00000008 /* can also return an FD */
2139 #define PROTO_ALL_FLAGS 0x0000000f /* all flags */
2141 static struct protocol_entry {
2142 enum rep_protocol_requestid pt_request;
2143 const char *pt_name;
2144 protocol_handler_f *pt_handler;
2145 void *pt_arg;
2146 protocol_handler_fdret_f *pt_fd_handler;
2147 size_t pt_in_size;
2148 size_t pt_out_max;
2149 uint32_t pt_flags;
2150 } protocol_table[] = {
2151 PROTO_PANIC(REP_PROTOCOL_CLOSE), /* special case */
2153 PROTO(REP_PROTOCOL_ENTITY_SETUP, entity_setup,
2154 struct rep_protocol_entity_setup),
2155 PROTO_NAME_OUT(REP_PROTOCOL_ENTITY_NAME, entity_name,
2156 struct rep_protocol_entity_name),
2157 PROTO_UINT_OUT(REP_PROTOCOL_ENTITY_PARENT_TYPE, entity_parent_type,
2158 struct rep_protocol_entity_parent_type),
2159 PROTO(REP_PROTOCOL_ENTITY_GET_CHILD, entity_get_child,
2160 struct rep_protocol_entity_get_child),
2161 PROTO(REP_PROTOCOL_ENTITY_GET_PARENT, entity_get_parent,
2162 struct rep_protocol_entity_parent),
2163 PROTO(REP_PROTOCOL_ENTITY_GET, entity_get,
2164 struct rep_protocol_entity_get),
2165 PROTO(REP_PROTOCOL_ENTITY_UPDATE, entity_update,
2166 struct rep_protocol_entity_update),
2167 PROTO(REP_PROTOCOL_ENTITY_CREATE_CHILD, entity_create_child,
2168 struct rep_protocol_entity_create_child),
2169 PROTO(REP_PROTOCOL_ENTITY_CREATE_PG, entity_create_pg,
2170 struct rep_protocol_entity_create_pg),
2171 PROTO(REP_PROTOCOL_ENTITY_DELETE, entity_delete,
2172 struct rep_protocol_entity_delete),
2173 PROTO(REP_PROTOCOL_ENTITY_RESET, entity_reset,
2174 struct rep_protocol_entity_reset),
2175 PROTO(REP_PROTOCOL_ENTITY_TEARDOWN, entity_teardown,
2176 struct rep_protocol_entity_teardown),
2178 PROTO(REP_PROTOCOL_ITER_SETUP, iter_setup,
2179 struct rep_protocol_iter_request),
2180 PROTO(REP_PROTOCOL_ITER_START, iter_start,
2181 struct rep_protocol_iter_start),
2182 PROTO(REP_PROTOCOL_ITER_READ, iter_read,
2183 struct rep_protocol_iter_read),
2184 PROTO_VALUE_OUT(REP_PROTOCOL_ITER_READ_VALUE, iter_read_value,
2185 struct rep_protocol_iter_read_value),
2186 PROTO(REP_PROTOCOL_ITER_RESET, iter_reset,
2187 struct rep_protocol_iter_request),
2188 PROTO(REP_PROTOCOL_ITER_TEARDOWN, iter_teardown,
2189 struct rep_protocol_iter_request),
2191 PROTO(REP_PROTOCOL_NEXT_SNAPLEVEL, next_snaplevel,
2192 struct rep_protocol_entity_pair),
2194 PROTO(REP_PROTOCOL_SNAPSHOT_TAKE, snapshot_take,
2195 struct rep_protocol_snapshot_take),
2196 PROTO(REP_PROTOCOL_SNAPSHOT_TAKE_NAMED, snapshot_take_named,
2197 struct rep_protocol_snapshot_take_named),
2198 PROTO(REP_PROTOCOL_SNAPSHOT_ATTACH, snapshot_attach,
2199 struct rep_protocol_snapshot_attach),
2201 PROTO_UINT_OUT(REP_PROTOCOL_PROPERTY_GET_TYPE, property_get_type,
2202 struct rep_protocol_property_request),
2203 PROTO_VALUE_OUT(REP_PROTOCOL_PROPERTY_GET_VALUE, property_get_value,
2204 struct rep_protocol_property_request),
2206 PROTO_FD_OUT(REP_PROTOCOL_PROPERTYGRP_SETUP_WAIT, propertygrp_notify,
2207 struct rep_protocol_propertygrp_request),
2208 PROTO(REP_PROTOCOL_PROPERTYGRP_TX_START, tx_start,
2209 struct rep_protocol_transaction_start),
2210 PROTO_VARIN(REP_PROTOCOL_PROPERTYGRP_TX_COMMIT, tx_commit,
2211 REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE),
2213 PROTO(REP_PROTOCOL_CLIENT_ADD_NOTIFY, client_add_notify,
2214 struct rep_protocol_notify_request),
2215 PROTO_FMRI_OUT(REP_PROTOCOL_CLIENT_WAIT, client_wait,
2216 struct rep_protocol_wait_request),
2218 PROTO(REP_PROTOCOL_BACKUP, backup_repository,
2219 struct rep_protocol_backup_request),
2221 PROTO(REP_PROTOCOL_SET_AUDIT_ANNOTATION, set_annotation,
2222 struct rep_protocol_annotation),
2224 PROTO(REP_PROTOCOL_SWITCH, repository_switch,
2225 struct rep_protocol_switch_request),
2227 PROTO_END()
2229 #undef PROTO
2230 #undef PROTO_FMRI_OUT
2231 #undef PROTO_NAME_OUT
2232 #undef PROTO_UINT_OUT
2233 #undef PROTO_PANIC
2234 #undef PROTO_END
2237 * The number of entries, sans PROTO_END()
2239 #define PROTOCOL_ENTRIES \
2240 (sizeof (protocol_table) / sizeof (*protocol_table) - 1)
2242 #define PROTOCOL_PREFIX "REP_PROTOCOL_"
2245 client_init(void)
2247 int i;
2248 struct protocol_entry *e;
2250 if (!client_hash_init())
2251 return (0);
2253 if (request_log_size > 0) {
2254 request_log = uu_zalloc(request_log_size *
2255 sizeof (request_log_entry_t));
2259 * update the names to not include REP_PROTOCOL_
2261 for (i = 0; i < PROTOCOL_ENTRIES; i++) {
2262 e = &protocol_table[i];
2263 assert(strncmp(e->pt_name, PROTOCOL_PREFIX,
2264 strlen(PROTOCOL_PREFIX)) == 0);
2265 e->pt_name += strlen(PROTOCOL_PREFIX);
2268 * verify the protocol table is consistent
2270 for (i = 0; i < PROTOCOL_ENTRIES; i++) {
2271 e = &protocol_table[i];
2272 assert(e->pt_request == (REP_PROTOCOL_BASE + i));
2274 assert((e->pt_flags & ~PROTO_ALL_FLAGS) == 0);
2276 if (e->pt_flags & PROTO_FLAG_PANIC)
2277 assert(e->pt_in_size == 0 && e->pt_out_max == 0 &&
2278 e->pt_handler == NULL);
2279 else
2280 assert(e->pt_in_size != 0 && e->pt_out_max != 0 &&
2281 (e->pt_handler != NULL ||
2282 e->pt_fd_handler != NULL));
2284 assert((REP_PROTOCOL_BASE + i) == REP_PROTOCOL_MAX_REQUEST);
2286 assert(protocol_table[i].pt_request == 0);
2288 return (1);
2291 static void
2292 client_switcher(void *cookie, char *argp, size_t arg_size, door_desc_t *desc_in,
2293 uint_t n_desc)
2295 thread_info_t *ti = thread_self();
2297 repcache_client_t *cp;
2298 uint32_t id = (uint32_t)cookie;
2299 enum rep_protocol_requestid request_code;
2301 rep_protocol_responseid_t result = INVALID_RESULT;
2303 struct protocol_entry *e;
2305 char *retval = NULL;
2306 size_t retsize = 0;
2308 int retfd = -1;
2309 door_desc_t desc;
2310 request_log_entry_t *rlp;
2312 rlp = start_log(id);
2314 if (n_desc != 0)
2315 uu_die("can't happen: %d descriptors @%p (cookie %p)",
2316 n_desc, desc_in, cookie);
2318 if (argp == DOOR_UNREF_DATA) {
2319 client_destroy(id);
2320 goto bad_end;
2323 thread_newstate(ti, TI_CLIENT_CALL);
2326 * To simplify returning just a result code, we set up for
2327 * that case here.
2329 retval = (char *)&result;
2330 retsize = sizeof (result);
2332 if (arg_size < sizeof (request_code)) {
2333 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2334 goto end_unheld;
2337 ti->ti_client_request = (void *)argp;
2339 /* LINTED alignment */
2340 request_code = *(uint32_t *)argp;
2342 if (rlp != NULL) {
2343 rlp->rl_request = request_code;
2346 * In order to avoid locking problems on removal, we handle the
2347 * "close" case before doing a lookup.
2349 if (request_code == REP_PROTOCOL_CLOSE) {
2350 client_destroy(id);
2351 result = REP_PROTOCOL_SUCCESS;
2352 goto end_unheld;
2355 cp = client_lookup(id);
2357 * cp is held
2360 if (cp == NULL)
2361 goto bad_end;
2363 if (rlp != NULL)
2364 rlp->rl_client = cp;
2366 ti->ti_active_client = cp;
2368 if (request_code < REP_PROTOCOL_BASE ||
2369 request_code >= REP_PROTOCOL_BASE + PROTOCOL_ENTRIES) {
2370 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2371 goto end;
2374 e = &protocol_table[request_code - REP_PROTOCOL_BASE];
2376 assert(!(e->pt_flags & PROTO_FLAG_PANIC));
2378 if (e->pt_flags & PROTO_FLAG_VARINPUT) {
2379 if (arg_size < e->pt_in_size) {
2380 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2381 goto end;
2383 } else if (arg_size != e->pt_in_size) {
2384 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2385 goto end;
2388 if (retsize != e->pt_out_max) {
2389 retsize = e->pt_out_max;
2390 retval = alloca(retsize);
2393 if (e->pt_flags & PROTO_FLAG_RETFD)
2394 e->pt_fd_handler(cp, argp, arg_size, retval, &retsize,
2395 e->pt_arg, &retfd);
2396 else
2397 e->pt_handler(cp, argp, arg_size, retval, &retsize, e->pt_arg);
2399 end:
2400 ti->ti_active_client = NULL;
2401 client_release(cp);
2403 end_unheld:
2404 if (rlp != NULL) {
2405 /* LINTED alignment */
2406 rlp->rl_response = *(uint32_t *)retval;
2407 end_log();
2408 rlp = NULL;
2410 ti->ti_client_request = NULL;
2411 thread_newstate(ti, TI_DOOR_RETURN);
2413 if (retval == (char *)&result) {
2414 assert(result != INVALID_RESULT && retsize == sizeof (result));
2415 } else {
2416 /* LINTED alignment */
2417 result = *(uint32_t *)retval;
2419 if (retfd != -1) {
2420 desc.d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE;
2421 desc.d_data.d_desc.d_descriptor = retfd;
2422 (void) door_return(retval, retsize, &desc, 1);
2423 } else {
2424 (void) door_return(retval, retsize, NULL, 0);
2426 bad_end:
2427 if (rlp != NULL) {
2428 rlp->rl_response = -1;
2429 end_log();
2430 rlp = NULL;
2432 (void) door_return(NULL, 0, NULL, 0);
2436 create_client(pid_t pid, uint32_t debugflags, int privileged, int *out_fd)
2438 int fd;
2440 repcache_client_t *cp;
2442 struct door_info info;
2444 int door_flags = DOOR_UNREF | DOOR_REFUSE_DESC;
2445 #ifdef DOOR_NO_CANCEL
2446 door_flags |= DOOR_NO_CANCEL;
2447 #endif
2449 cp = client_alloc();
2450 if (cp == NULL)
2451 return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2453 (void) pthread_mutex_lock(&client_lock);
2454 cp->rc_id = ++client_maxid;
2455 (void) pthread_mutex_unlock(&client_lock);
2457 cp->rc_all_auths = privileged;
2458 cp->rc_pid = pid;
2459 cp->rc_debug = debugflags;
2461 start_audit_session(cp);
2463 cp->rc_doorfd = door_create(client_switcher, (void *)cp->rc_id,
2464 door_flags);
2466 if (cp->rc_doorfd < 0) {
2467 client_free(cp);
2468 return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2470 #ifdef DOOR_PARAM_DATA_MIN
2471 (void) door_setparam(cp->rc_doorfd, DOOR_PARAM_DATA_MIN,
2472 sizeof (enum rep_protocol_requestid));
2473 #endif
2475 if ((fd = dup(cp->rc_doorfd)) < 0 ||
2476 door_info(cp->rc_doorfd, &info) < 0) {
2477 if (fd >= 0)
2478 (void) close(fd);
2479 (void) door_revoke(cp->rc_doorfd);
2480 cp->rc_doorfd = -1;
2481 client_free(cp);
2482 return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2485 rc_pg_notify_init(&cp->rc_pg_notify);
2486 rc_notify_info_init(&cp->rc_notify_info);
2488 client_insert(cp);
2490 cp->rc_doorid = info.di_uniquifier;
2491 *out_fd = fd;
2493 return (REPOSITORY_DOOR_SUCCESS);