dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / svc / configd / client.c
bloba315d94a0fcd4afe7536e22f81b70b689081a7e3
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.
24 * Copyright 2015 RackTop Systems.
28 * This is the client layer for svc.configd. All direct protocol interactions
29 * are handled here.
31 * Essentially, the job of this layer is to turn the idempotent protocol
32 * into a series of non-idempotent calls into the object layer, while
33 * also handling the necessary locking.
36 #include <alloca.h>
37 #include <assert.h>
38 #include <bsm/adt_event.h>
39 #include <door.h>
40 #include <errno.h>
41 #include <libintl.h>
42 #include <limits.h>
43 #include <pthread.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <syslog.h>
48 #include <ucred.h>
49 #include <unistd.h>
51 #include <libuutil.h>
53 #include "configd.h"
54 #include "repcache_protocol.h"
56 #define INVALID_CHANGEID (0)
57 #define INVALID_DOORID ((door_id_t)-1)
58 #define INVALID_RESULT ((rep_protocol_responseid_t)INT_MIN)
61 * lint doesn't like constant assertions
63 #define assert_nolint(x) assert(x)
66 * Protects client linkage and the freelist
68 #define CLIENT_HASH_SIZE 64
70 #pragma align 64(client_hash)
71 static client_bucket_t client_hash[CLIENT_HASH_SIZE];
73 static uu_avl_pool_t *entity_pool;
74 static uu_avl_pool_t *iter_pool;
75 static uu_list_pool_t *client_pool;
77 #define CLIENT_HASH(id) (&client_hash[((id) & (CLIENT_HASH_SIZE - 1))])
79 uint_t request_log_size = 1024; /* tunable, before we start */
81 static pthread_mutex_t request_log_lock = PTHREAD_MUTEX_INITIALIZER;
82 static uint_t request_log_cur;
83 request_log_entry_t *request_log;
85 static uint32_t client_maxid;
86 static pthread_mutex_t client_lock; /* protects client_maxid */
88 static request_log_entry_t *
89 get_log(void)
91 thread_info_t *ti = thread_self();
92 return (&ti->ti_log);
95 void
96 log_enter(request_log_entry_t *rlp)
98 if (rlp->rl_start != 0 && request_log != NULL) {
99 request_log_entry_t *logrlp;
101 (void) pthread_mutex_lock(&request_log_lock);
102 assert(request_log_cur < request_log_size);
103 logrlp = &request_log[request_log_cur++];
104 if (request_log_cur == request_log_size)
105 request_log_cur = 0;
106 (void) memcpy(logrlp, rlp, sizeof (*rlp));
107 (void) pthread_mutex_unlock(&request_log_lock);
112 * Note that the svc.configd dmod will join all of the per-thread log entries
113 * with the main log, so that even if the log is disabled, there is some
114 * information available.
116 static request_log_entry_t *
117 start_log(uint32_t clientid)
119 request_log_entry_t *rlp = get_log();
121 log_enter(rlp);
123 (void) memset(rlp, 0, sizeof (*rlp));
124 rlp->rl_start = gethrtime();
125 rlp->rl_tid = pthread_self();
126 rlp->rl_clientid = clientid;
128 return (rlp);
131 void
132 end_log(void)
134 request_log_entry_t *rlp = get_log();
136 rlp->rl_end = gethrtime();
139 static void
140 add_log_ptr(request_log_entry_t *rlp, enum rc_ptr_type type, uint32_t id,
141 void *ptr)
143 request_log_ptr_t *rpp;
145 if (rlp == NULL)
146 return;
148 if (rlp->rl_num_ptrs >= MAX_PTRS)
149 return;
151 rpp = &rlp->rl_ptrs[rlp->rl_num_ptrs++];
152 rpp->rlp_type = type;
153 rpp->rlp_id = id;
154 rpp->rlp_ptr = ptr;
157 * For entities, it's useful to have the node pointer at the start
158 * of the request.
160 if (type == RC_PTR_TYPE_ENTITY && ptr != NULL)
161 rpp->rlp_data = ((repcache_entity_t *)ptr)->re_node.rnp_node;
165 client_is_privileged(void)
167 thread_info_t *ti = thread_self();
169 ucred_t *uc;
171 if (ti->ti_active_client != NULL &&
172 ti->ti_active_client->rc_all_auths)
173 return (1);
175 if ((uc = get_ucred()) == NULL)
176 return (0);
178 return (ucred_is_privileged(uc));
181 /*ARGSUSED*/
182 static int
183 client_compare(const void *lc_arg, const void *rc_arg, void *private)
185 uint32_t l_id = ((const repcache_client_t *)lc_arg)->rc_id;
186 uint32_t r_id = ((const repcache_client_t *)rc_arg)->rc_id;
188 if (l_id > r_id)
189 return (1);
190 if (l_id < r_id)
191 return (-1);
192 return (0);
195 /*ARGSUSED*/
196 static int
197 entity_compare(const void *lc_arg, const void *rc_arg, void *private)
199 uint32_t l_id = ((const repcache_entity_t *)lc_arg)->re_id;
200 uint32_t r_id = ((const repcache_entity_t *)rc_arg)->re_id;
202 if (l_id > r_id)
203 return (1);
204 if (l_id < r_id)
205 return (-1);
206 return (0);
209 /*ARGSUSED*/
210 static int
211 iter_compare(const void *lc_arg, const void *rc_arg, void *private)
213 uint32_t l_id = ((const repcache_iter_t *)lc_arg)->ri_id;
214 uint32_t r_id = ((const repcache_iter_t *)rc_arg)->ri_id;
216 if (l_id > r_id)
217 return (1);
218 if (l_id < r_id)
219 return (-1);
220 return (0);
223 static int
224 client_hash_init(void)
226 int x;
228 assert_nolint(offsetof(repcache_entity_t, re_id) == 0);
229 entity_pool = uu_avl_pool_create("repcache_entitys",
230 sizeof (repcache_entity_t), offsetof(repcache_entity_t, re_link),
231 entity_compare, UU_AVL_POOL_DEBUG);
233 assert_nolint(offsetof(repcache_iter_t, ri_id) == 0);
234 iter_pool = uu_avl_pool_create("repcache_iters",
235 sizeof (repcache_iter_t), offsetof(repcache_iter_t, ri_link),
236 iter_compare, UU_AVL_POOL_DEBUG);
238 assert_nolint(offsetof(repcache_client_t, rc_id) == 0);
239 client_pool = uu_list_pool_create("repcache_clients",
240 sizeof (repcache_client_t), offsetof(repcache_client_t, rc_link),
241 client_compare, UU_LIST_POOL_DEBUG);
243 if (entity_pool == NULL || iter_pool == NULL || client_pool == NULL)
244 return (0);
246 for (x = 0; x < CLIENT_HASH_SIZE; x++) {
247 uu_list_t *lp = uu_list_create(client_pool, &client_hash[x],
248 UU_LIST_SORTED);
249 if (lp == NULL)
250 return (0);
252 (void) pthread_mutex_init(&client_hash[x].cb_lock, NULL);
253 client_hash[x].cb_list = lp;
256 return (1);
259 static repcache_client_t *
260 client_alloc(void)
262 repcache_client_t *cp;
263 cp = uu_zalloc(sizeof (*cp));
264 if (cp == NULL)
265 return (NULL);
267 cp->rc_entities = uu_avl_create(entity_pool, cp, 0);
268 if (cp->rc_entities == NULL)
269 goto fail;
271 cp->rc_iters = uu_avl_create(iter_pool, cp, 0);
272 if (cp->rc_iters == NULL)
273 goto fail;
275 uu_list_node_init(cp, &cp->rc_link, client_pool);
277 cp->rc_doorfd = -1;
278 cp->rc_doorid = INVALID_DOORID;
280 (void) pthread_mutex_init(&cp->rc_lock, NULL);
281 (void) pthread_mutex_init(&cp->rc_annotate_lock, NULL);
283 rc_node_ptr_init(&cp->rc_notify_ptr);
285 return (cp);
287 fail:
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);
296 static void
297 client_free(repcache_client_t *cp)
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 (void) pthread_mutex_destroy(&cp->rc_annotate_lock);
310 rc_node_ptr_free_mem(&cp->rc_notify_ptr);
311 uu_free(cp);
314 static void
315 client_insert(repcache_client_t *cp)
317 client_bucket_t *bp = CLIENT_HASH(cp->rc_id);
318 uu_list_index_t idx;
320 assert(cp->rc_id > 0);
322 (void) pthread_mutex_lock(&bp->cb_lock);
324 * We assume it does not already exist
326 (void) uu_list_find(bp->cb_list, cp, NULL, &idx);
327 uu_list_insert(bp->cb_list, cp, idx);
329 (void) pthread_mutex_unlock(&bp->cb_lock);
332 static repcache_client_t *
333 client_lookup(uint32_t id)
335 client_bucket_t *bp = CLIENT_HASH(id);
336 repcache_client_t *cp;
338 (void) pthread_mutex_lock(&bp->cb_lock);
340 cp = uu_list_find(bp->cb_list, &id, NULL, NULL);
343 * Bump the reference count
345 if (cp != NULL) {
346 (void) pthread_mutex_lock(&cp->rc_lock);
347 assert(!(cp->rc_flags & RC_CLIENT_DEAD));
348 cp->rc_refcnt++;
349 (void) pthread_mutex_unlock(&cp->rc_lock);
351 (void) pthread_mutex_unlock(&bp->cb_lock);
353 return (cp);
356 static void
357 client_release(repcache_client_t *cp)
359 (void) pthread_mutex_lock(&cp->rc_lock);
360 assert(cp->rc_refcnt > 0);
361 assert(cp->rc_insert_thr != pthread_self());
363 --cp->rc_refcnt;
364 (void) pthread_cond_broadcast(&cp->rc_cv);
365 (void) pthread_mutex_unlock(&cp->rc_lock);
369 * We only allow one thread to be inserting at a time, to prevent
370 * insert/insert races.
372 static void
373 client_start_insert(repcache_client_t *cp)
375 (void) pthread_mutex_lock(&cp->rc_lock);
376 assert(cp->rc_refcnt > 0);
378 while (cp->rc_insert_thr != 0) {
379 assert(cp->rc_insert_thr != pthread_self());
380 (void) pthread_cond_wait(&cp->rc_cv, &cp->rc_lock);
382 cp->rc_insert_thr = pthread_self();
383 (void) pthread_mutex_unlock(&cp->rc_lock);
386 static void
387 client_end_insert(repcache_client_t *cp)
389 (void) pthread_mutex_lock(&cp->rc_lock);
390 assert(cp->rc_insert_thr == pthread_self());
391 cp->rc_insert_thr = 0;
392 (void) pthread_cond_broadcast(&cp->rc_cv);
393 (void) pthread_mutex_unlock(&cp->rc_lock);
396 /*ARGSUSED*/
397 static repcache_entity_t *
398 entity_alloc(repcache_client_t *cp)
400 repcache_entity_t *ep = uu_zalloc(sizeof (repcache_entity_t));
401 if (ep != NULL) {
402 uu_avl_node_init(ep, &ep->re_link, entity_pool);
404 return (ep);
407 static void
408 entity_add(repcache_client_t *cp, repcache_entity_t *ep)
410 uu_avl_index_t idx;
412 (void) pthread_mutex_lock(&cp->rc_lock);
413 assert(cp->rc_insert_thr == pthread_self());
415 (void) uu_avl_find(cp->rc_entities, ep, NULL, &idx);
416 uu_avl_insert(cp->rc_entities, ep, idx);
418 (void) pthread_mutex_unlock(&cp->rc_lock);
421 static repcache_entity_t *
422 entity_find(repcache_client_t *cp, uint32_t id)
424 repcache_entity_t *ep;
426 (void) pthread_mutex_lock(&cp->rc_lock);
427 ep = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
428 if (ep != NULL) {
429 add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, ep);
430 (void) pthread_mutex_lock(&ep->re_lock);
432 (void) pthread_mutex_unlock(&cp->rc_lock);
434 return (ep);
438 * Fails with
439 * _DUPLICATE_ID - the ids are equal
440 * _UNKNOWN_ID - an id does not designate an active register
442 static int
443 entity_find2(repcache_client_t *cp, uint32_t id1, repcache_entity_t **out1,
444 uint32_t id2, repcache_entity_t **out2)
446 repcache_entity_t *e1, *e2;
447 request_log_entry_t *rlp;
449 if (id1 == id2)
450 return (REP_PROTOCOL_FAIL_DUPLICATE_ID);
452 (void) pthread_mutex_lock(&cp->rc_lock);
453 e1 = uu_avl_find(cp->rc_entities, &id1, NULL, NULL);
454 e2 = uu_avl_find(cp->rc_entities, &id2, NULL, NULL);
455 if (e1 == NULL || e2 == NULL) {
456 (void) pthread_mutex_unlock(&cp->rc_lock);
457 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
460 assert(e1 != e2);
463 * locks are ordered by id number
465 if (id1 < id2) {
466 (void) pthread_mutex_lock(&e1->re_lock);
467 (void) pthread_mutex_lock(&e2->re_lock);
468 } else {
469 (void) pthread_mutex_lock(&e2->re_lock);
470 (void) pthread_mutex_lock(&e1->re_lock);
472 *out1 = e1;
473 *out2 = e2;
475 (void) pthread_mutex_unlock(&cp->rc_lock);
477 if ((rlp = get_log()) != NULL) {
478 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, id1, e1);
479 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, id2, e2);
482 return (REP_PROTOCOL_SUCCESS);
485 static void
486 entity_release(repcache_entity_t *ep)
488 assert(ep->re_node.rnp_node == NULL ||
489 !MUTEX_HELD(&ep->re_node.rnp_node->rn_lock));
490 (void) pthread_mutex_unlock(&ep->re_lock);
493 static void
494 entity_destroy(repcache_entity_t *entity)
496 (void) pthread_mutex_lock(&entity->re_lock);
497 rc_node_clear(&entity->re_node, 0);
498 (void) pthread_mutex_unlock(&entity->re_lock);
500 uu_avl_node_fini(entity, &entity->re_link, entity_pool);
501 (void) pthread_mutex_destroy(&entity->re_lock);
502 rc_node_ptr_free_mem(&entity->re_node);
503 uu_free(entity);
506 static void
507 entity_remove(repcache_client_t *cp, uint32_t id)
509 repcache_entity_t *entity;
511 (void) pthread_mutex_lock(&cp->rc_lock);
512 entity = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
513 if (entity != NULL) {
514 add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, entity);
516 uu_avl_remove(cp->rc_entities, entity);
518 (void) pthread_mutex_unlock(&cp->rc_lock);
520 if (entity != NULL)
521 entity_destroy(entity);
524 static void
525 entity_cleanup(repcache_client_t *cp)
527 repcache_entity_t *ep;
528 void *cookie = NULL;
530 (void) pthread_mutex_lock(&cp->rc_lock);
531 while ((ep = uu_avl_teardown(cp->rc_entities, &cookie)) != NULL) {
532 (void) pthread_mutex_unlock(&cp->rc_lock);
533 entity_destroy(ep);
534 (void) pthread_mutex_lock(&cp->rc_lock);
536 (void) pthread_mutex_unlock(&cp->rc_lock);
539 /*ARGSUSED*/
540 static repcache_iter_t *
541 iter_alloc(repcache_client_t *cp)
543 repcache_iter_t *iter;
544 iter = uu_zalloc(sizeof (repcache_iter_t));
545 if (iter != NULL)
546 uu_avl_node_init(iter, &iter->ri_link, iter_pool);
547 return (iter);
550 static void
551 iter_add(repcache_client_t *cp, repcache_iter_t *iter)
553 uu_list_index_t idx;
555 (void) pthread_mutex_lock(&cp->rc_lock);
556 assert(cp->rc_insert_thr == pthread_self());
558 (void) uu_avl_find(cp->rc_iters, iter, NULL, &idx);
559 uu_avl_insert(cp->rc_iters, iter, idx);
561 (void) pthread_mutex_unlock(&cp->rc_lock);
564 static repcache_iter_t *
565 iter_find(repcache_client_t *cp, uint32_t id)
567 repcache_iter_t *iter;
569 (void) pthread_mutex_lock(&cp->rc_lock);
571 iter = uu_avl_find(cp->rc_iters, &id, NULL, NULL);
572 if (iter != NULL) {
573 add_log_ptr(get_log(), RC_PTR_TYPE_ITER, id, iter);
574 (void) pthread_mutex_lock(&iter->ri_lock);
576 (void) pthread_mutex_unlock(&cp->rc_lock);
578 return (iter);
582 * Fails with
583 * _UNKNOWN_ID - iter_id or entity_id does not designate an active register
585 static int
586 iter_find_w_entity(repcache_client_t *cp, uint32_t iter_id,
587 repcache_iter_t **iterp, uint32_t entity_id, repcache_entity_t **epp)
589 repcache_iter_t *iter;
590 repcache_entity_t *ep;
591 request_log_entry_t *rlp;
593 (void) pthread_mutex_lock(&cp->rc_lock);
594 iter = uu_avl_find(cp->rc_iters, &iter_id, NULL, NULL);
595 ep = uu_avl_find(cp->rc_entities, &entity_id, NULL, NULL);
597 assert(iter == NULL || !MUTEX_HELD(&iter->ri_lock));
598 assert(ep == NULL || !MUTEX_HELD(&ep->re_lock));
600 if (iter == NULL || ep == NULL) {
601 (void) pthread_mutex_unlock(&cp->rc_lock);
602 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
605 (void) pthread_mutex_lock(&iter->ri_lock);
606 (void) pthread_mutex_lock(&ep->re_lock);
608 (void) pthread_mutex_unlock(&cp->rc_lock);
610 *iterp = iter;
611 *epp = ep;
613 if ((rlp = get_log()) != NULL) {
614 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, entity_id, ep);
615 add_log_ptr(rlp, RC_PTR_TYPE_ITER, iter_id, iter);
618 return (REP_PROTOCOL_SUCCESS);
621 static void
622 iter_release(repcache_iter_t *iter)
624 (void) pthread_mutex_unlock(&iter->ri_lock);
627 static void
628 iter_destroy(repcache_iter_t *iter)
630 (void) pthread_mutex_lock(&iter->ri_lock);
631 rc_iter_destroy(&iter->ri_iter);
632 (void) pthread_mutex_unlock(&iter->ri_lock);
634 uu_avl_node_fini(iter, &iter->ri_link, iter_pool);
635 (void) pthread_mutex_destroy(&iter->ri_lock);
636 uu_free(iter);
639 static void
640 iter_remove(repcache_client_t *cp, uint32_t id)
642 repcache_iter_t *iter;
644 (void) pthread_mutex_lock(&cp->rc_lock);
645 iter = uu_avl_find(cp->rc_iters, &id, NULL, NULL);
646 if (iter != NULL)
647 uu_avl_remove(cp->rc_iters, iter);
648 (void) pthread_mutex_unlock(&cp->rc_lock);
650 if (iter != NULL)
651 iter_destroy(iter);
654 static void
655 iter_cleanup(repcache_client_t *cp)
657 repcache_iter_t *iter;
658 void *cookie = NULL;
660 (void) pthread_mutex_lock(&cp->rc_lock);
661 while ((iter = uu_avl_teardown(cp->rc_iters, &cookie)) != NULL) {
662 (void) pthread_mutex_unlock(&cp->rc_lock);
663 iter_destroy(iter);
664 (void) pthread_mutex_lock(&cp->rc_lock);
666 (void) pthread_mutex_unlock(&cp->rc_lock);
670 * Ensure that the passed client id is no longer usable, wait for any
671 * outstanding invocations to complete, then destroy the client
672 * structure.
674 static void
675 client_destroy(uint32_t id)
677 client_bucket_t *bp = CLIENT_HASH(id);
678 repcache_client_t *cp;
680 (void) pthread_mutex_lock(&bp->cb_lock);
682 cp = uu_list_find(bp->cb_list, &id, NULL, NULL);
684 if (cp == NULL) {
685 (void) pthread_mutex_unlock(&bp->cb_lock);
686 return;
689 uu_list_remove(bp->cb_list, cp);
691 (void) pthread_mutex_unlock(&bp->cb_lock);
693 /* kick the waiters out */
694 rc_notify_info_fini(&cp->rc_notify_info);
696 (void) pthread_mutex_lock(&cp->rc_lock);
697 assert(!(cp->rc_flags & RC_CLIENT_DEAD));
698 cp->rc_flags |= RC_CLIENT_DEAD;
700 if (cp->rc_doorfd != -1) {
701 if (door_revoke(cp->rc_doorfd) < 0)
702 perror("door_revoke");
703 cp->rc_doorfd = -1;
704 cp->rc_doorid = INVALID_DOORID;
707 while (cp->rc_refcnt > 0)
708 (void) pthread_cond_wait(&cp->rc_cv, &cp->rc_lock);
710 assert(cp->rc_insert_thr == 0 && cp->rc_notify_thr == 0);
711 (void) pthread_mutex_unlock(&cp->rc_lock);
714 * destroy outstanding objects
716 entity_cleanup(cp);
717 iter_cleanup(cp);
720 * clean up notifications
722 rc_pg_notify_fini(&cp->rc_pg_notify);
725 * clean up annotations
727 if (cp->rc_operation != NULL)
728 free((void *)cp->rc_operation);
729 if (cp->rc_file != NULL)
730 free((void *)cp->rc_file);
733 * End audit session.
735 #ifndef NATIVE_BUILD
736 (void) adt_end_session(cp->rc_adt_session);
737 #endif
739 client_free(cp);
743 * Fails with
744 * _TYPE_MISMATCH - the entity is already set up with a different type
745 * _NO_RESOURCES - out of memory
747 static int
748 entity_setup(repcache_client_t *cp, struct rep_protocol_entity_setup *rpr)
750 repcache_entity_t *ep;
751 uint32_t type;
753 client_start_insert(cp);
755 if ((ep = entity_find(cp, rpr->rpr_entityid)) != NULL) {
756 type = ep->re_type;
757 entity_release(ep);
759 client_end_insert(cp);
761 if (type != rpr->rpr_entitytype)
762 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
763 return (REP_PROTOCOL_SUCCESS);
766 switch (type = rpr->rpr_entitytype) {
767 case REP_PROTOCOL_ENTITY_SCOPE:
768 case REP_PROTOCOL_ENTITY_SERVICE:
769 case REP_PROTOCOL_ENTITY_INSTANCE:
770 case REP_PROTOCOL_ENTITY_SNAPSHOT:
771 case REP_PROTOCOL_ENTITY_SNAPLEVEL:
772 case REP_PROTOCOL_ENTITY_PROPERTYGRP:
773 case REP_PROTOCOL_ENTITY_PROPERTY:
774 break;
775 default:
776 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
779 ep = entity_alloc(cp);
780 if (ep == NULL) {
781 client_end_insert(cp);
782 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
785 ep->re_id = rpr->rpr_entityid;
786 ep->re_changeid = INVALID_CHANGEID;
788 ep->re_type = type;
789 rc_node_ptr_init(&ep->re_node);
791 entity_add(cp, ep);
792 client_end_insert(cp);
793 return (REP_PROTOCOL_SUCCESS);
796 /*ARGSUSED*/
797 static void
798 entity_name(repcache_client_t *cp, const void *in, size_t insz, void *out_arg,
799 size_t *outsz, void *arg)
801 const struct rep_protocol_entity_name *rpr = in;
802 struct rep_protocol_name_response *out = out_arg;
803 repcache_entity_t *ep;
804 size_t sz = sizeof (out->rpr_name);
806 assert(*outsz == sizeof (*out));
808 ep = entity_find(cp, rpr->rpr_entityid);
810 if (ep == NULL) {
811 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
812 *outsz = sizeof (out->rpr_response);
813 return;
815 out->rpr_response = rc_node_name(&ep->re_node, out->rpr_name,
816 sz, rpr->rpr_answertype, &sz);
817 entity_release(ep);
820 * If we fail, we only return the response code.
821 * If we succeed, we don't return anything after the '\0' in rpr_name.
823 if (out->rpr_response != REP_PROTOCOL_SUCCESS)
824 *outsz = sizeof (out->rpr_response);
825 else
826 *outsz = offsetof(struct rep_protocol_name_response,
827 rpr_name[sz + 1]);
830 /*ARGSUSED*/
831 static void
832 entity_parent_type(repcache_client_t *cp, const void *in, size_t insz,
833 void *out_arg, size_t *outsz, void *arg)
835 const struct rep_protocol_entity_name *rpr = in;
836 struct rep_protocol_integer_response *out = out_arg;
837 repcache_entity_t *ep;
839 assert(*outsz == sizeof (*out));
841 ep = entity_find(cp, rpr->rpr_entityid);
843 if (ep == NULL) {
844 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
845 *outsz = sizeof (out->rpr_response);
846 return;
849 out->rpr_response = rc_node_parent_type(&ep->re_node, &out->rpr_value);
850 entity_release(ep);
852 if (out->rpr_response != REP_PROTOCOL_SUCCESS)
853 *outsz = sizeof (out->rpr_response);
857 * Fails with
858 * _DUPLICATE_ID - the ids are equal
859 * _UNKNOWN_ID - an id does not designate an active register
860 * _INVALID_TYPE - type is invalid
861 * _TYPE_MISMATCH - np doesn't carry children of type type
862 * _DELETED - np has been deleted
863 * _NOT_FOUND - no child with that name/type combo found
864 * _NO_RESOURCES
865 * _BACKEND_ACCESS
867 static int
868 entity_get_child(repcache_client_t *cp,
869 struct rep_protocol_entity_get_child *rpr)
871 repcache_entity_t *parent, *child;
872 int result;
874 uint32_t parentid = rpr->rpr_entityid;
875 uint32_t childid = rpr->rpr_childid;
877 result = entity_find2(cp, childid, &child, parentid, &parent);
878 if (result != REP_PROTOCOL_SUCCESS)
879 return (result);
881 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
883 result = rc_node_get_child(&parent->re_node, rpr->rpr_name,
884 child->re_type, &child->re_node);
886 entity_release(child);
887 entity_release(parent);
889 return (result);
893 * Returns _FAIL_DUPLICATE_ID, _FAIL_UNKNOWN_ID, _FAIL_NOT_SET, _FAIL_DELETED,
894 * _FAIL_TYPE_MISMATCH, _FAIL_NOT_FOUND (scope has no parent), or _SUCCESS.
895 * Fails with
896 * _DUPLICATE_ID - the ids are equal
897 * _UNKNOWN_ID - an id does not designate an active register
898 * _NOT_SET - child is not set
899 * _DELETED - child has been deleted
900 * _TYPE_MISMATCH - child's parent does not match that of the parent register
901 * _NOT_FOUND - child has no parent (and is a scope)
903 static int
904 entity_get_parent(repcache_client_t *cp, struct rep_protocol_entity_parent *rpr)
906 repcache_entity_t *child, *parent;
907 int result;
909 uint32_t childid = rpr->rpr_entityid;
910 uint32_t outid = rpr->rpr_outid;
912 result = entity_find2(cp, childid, &child, outid, &parent);
913 if (result != REP_PROTOCOL_SUCCESS)
914 return (result);
916 result = rc_node_get_parent(&child->re_node, parent->re_type,
917 &parent->re_node);
919 entity_release(child);
920 entity_release(parent);
922 return (result);
925 static int
926 entity_get(repcache_client_t *cp, struct rep_protocol_entity_get *rpr)
928 repcache_entity_t *ep;
929 int result;
931 ep = entity_find(cp, rpr->rpr_entityid);
933 if (ep == NULL)
934 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
936 switch (rpr->rpr_object) {
937 case RP_ENTITY_GET_INVALIDATE:
938 rc_node_clear(&ep->re_node, 0);
939 result = REP_PROTOCOL_SUCCESS;
940 break;
941 case RP_ENTITY_GET_MOST_LOCAL_SCOPE:
942 result = rc_local_scope(ep->re_type, &ep->re_node);
943 break;
944 default:
945 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
946 break;
949 entity_release(ep);
951 return (result);
954 static int
955 entity_update(repcache_client_t *cp, struct rep_protocol_entity_update *rpr)
957 repcache_entity_t *ep;
958 int result;
960 if (rpr->rpr_changeid == INVALID_CHANGEID)
961 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
963 ep = entity_find(cp, rpr->rpr_entityid);
965 if (ep == NULL)
966 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
968 if (ep->re_changeid == rpr->rpr_changeid) {
969 result = REP_PROTOCOL_DONE;
970 } else {
971 result = rc_node_update(&ep->re_node);
972 if (result == REP_PROTOCOL_DONE)
973 ep->re_changeid = rpr->rpr_changeid;
976 entity_release(ep);
978 return (result);
981 static int
982 entity_reset(repcache_client_t *cp, struct rep_protocol_entity_reset *rpr)
984 repcache_entity_t *ep;
986 ep = entity_find(cp, rpr->rpr_entityid);
987 if (ep == NULL)
988 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
990 rc_node_clear(&ep->re_node, 0);
991 ep->re_txstate = REPCACHE_TX_INIT;
993 entity_release(ep);
994 return (REP_PROTOCOL_SUCCESS);
998 * Fails with
999 * _BAD_REQUEST - request has invalid changeid
1000 * rpr_name is invalid
1001 * cannot create children for parent's type of node
1002 * _DUPLICATE_ID - request has duplicate ids
1003 * _UNKNOWN_ID - request has unknown id
1004 * _DELETED - parent has been deleted
1005 * _NOT_SET - parent is reset
1006 * _NOT_APPLICABLE - rpr_childtype is _PROPERTYGRP
1007 * _INVALID_TYPE - parent is corrupt or rpr_childtype is invalid
1008 * _TYPE_MISMATCH - parent cannot have children of type rpr_childtype
1009 * _NO_RESOURCES
1010 * _PERMISSION_DENIED
1011 * _BACKEND_ACCESS
1012 * _BACKEND_READONLY
1013 * _EXISTS - child already exists
1015 static int
1016 entity_create_child(repcache_client_t *cp,
1017 struct rep_protocol_entity_create_child *rpr)
1019 repcache_entity_t *parent;
1020 repcache_entity_t *child;
1022 uint32_t parentid = rpr->rpr_entityid;
1023 uint32_t childid = rpr->rpr_childid;
1025 int result;
1027 if (rpr->rpr_changeid == INVALID_CHANGEID)
1028 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1030 result = entity_find2(cp, parentid, &parent, childid, &child);
1031 if (result != REP_PROTOCOL_SUCCESS)
1032 return (result);
1034 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1036 if (child->re_changeid == rpr->rpr_changeid) {
1037 result = REP_PROTOCOL_SUCCESS;
1038 } else {
1039 result = rc_node_create_child(&parent->re_node,
1040 rpr->rpr_childtype, rpr->rpr_name, &child->re_node);
1041 if (result == REP_PROTOCOL_SUCCESS)
1042 child->re_changeid = rpr->rpr_changeid;
1045 entity_release(parent);
1046 entity_release(child);
1048 return (result);
1051 static int
1052 entity_create_pg(repcache_client_t *cp,
1053 struct rep_protocol_entity_create_pg *rpr)
1055 repcache_entity_t *parent;
1056 repcache_entity_t *child;
1058 uint32_t parentid = rpr->rpr_entityid;
1059 uint32_t childid = rpr->rpr_childid;
1061 int result;
1063 if (rpr->rpr_changeid == INVALID_CHANGEID)
1064 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1066 result = entity_find2(cp, parentid, &parent, childid, &child);
1067 if (result != REP_PROTOCOL_SUCCESS)
1068 return (result);
1070 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1071 rpr->rpr_type[sizeof (rpr->rpr_type) - 1] = 0;
1073 if (child->re_changeid == rpr->rpr_changeid) {
1074 result = REP_PROTOCOL_SUCCESS;
1075 } else {
1076 result = rc_node_create_child_pg(&parent->re_node,
1077 child->re_type, rpr->rpr_name, rpr->rpr_type,
1078 rpr->rpr_flags, &child->re_node);
1079 if (result == REP_PROTOCOL_SUCCESS)
1080 child->re_changeid = rpr->rpr_changeid;
1083 entity_release(parent);
1084 entity_release(child);
1086 return (result);
1089 static int
1090 entity_delete(repcache_client_t *cp,
1091 struct rep_protocol_entity_delete *rpr)
1093 repcache_entity_t *entity;
1095 uint32_t entityid = rpr->rpr_entityid;
1097 int result;
1099 if (rpr->rpr_changeid == INVALID_CHANGEID)
1100 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1102 entity = entity_find(cp, entityid);
1104 if (entity == NULL)
1105 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1107 if (entity->re_changeid == rpr->rpr_changeid) {
1108 result = REP_PROTOCOL_SUCCESS;
1109 } else {
1110 result = rc_node_delete(&entity->re_node);
1111 if (result == REP_PROTOCOL_SUCCESS)
1112 entity->re_changeid = rpr->rpr_changeid;
1115 entity_release(entity);
1117 return (result);
1120 static rep_protocol_responseid_t
1121 entity_teardown(repcache_client_t *cp, struct rep_protocol_entity_teardown *rpr)
1123 entity_remove(cp, rpr->rpr_entityid);
1125 return (REP_PROTOCOL_SUCCESS);
1129 * Fails with
1130 * _MISORDERED - the iterator exists and is not reset
1131 * _NO_RESOURCES - out of memory
1133 static int
1134 iter_setup(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1136 repcache_iter_t *iter;
1137 uint32_t sequence;
1139 client_start_insert(cp);
1141 * If the iter already exists, and hasn't been read from,
1142 * we assume the previous call succeeded.
1144 if ((iter = iter_find(cp, rpr->rpr_iterid)) != NULL) {
1145 sequence = iter->ri_sequence;
1146 iter_release(iter);
1148 client_end_insert(cp);
1150 if (sequence != 0)
1151 return (REP_PROTOCOL_FAIL_MISORDERED);
1152 return (REP_PROTOCOL_SUCCESS);
1155 iter = iter_alloc(cp);
1156 if (iter == NULL) {
1157 client_end_insert(cp);
1158 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1161 iter->ri_id = rpr->rpr_iterid;
1162 iter->ri_type = REP_PROTOCOL_TYPE_INVALID;
1163 iter->ri_sequence = 0;
1164 iter_add(cp, iter);
1166 client_end_insert(cp);
1167 return (REP_PROTOCOL_SUCCESS);
1171 * Fails with
1172 * _UNKNOWN_ID
1173 * _MISORDERED - iterator has already been started
1174 * _NOT_SET
1175 * _DELETED
1176 * _TYPE_MISMATCH - entity cannot have type children
1177 * _BAD_REQUEST - rpr_flags is invalid
1178 * rpr_pattern is invalid
1179 * _NO_RESOURCES
1180 * _INVALID_TYPE
1181 * _BACKEND_ACCESS
1183 static int
1184 iter_start(repcache_client_t *cp, struct rep_protocol_iter_start *rpr)
1186 int result;
1187 repcache_iter_t *iter;
1188 repcache_entity_t *ep;
1190 result = iter_find_w_entity(cp, rpr->rpr_iterid, &iter,
1191 rpr->rpr_entity, &ep);
1193 if (result != REP_PROTOCOL_SUCCESS)
1194 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1196 if (iter->ri_sequence > 1) {
1197 result = REP_PROTOCOL_FAIL_MISORDERED;
1198 goto end;
1201 if (iter->ri_sequence == 1) {
1202 result = REP_PROTOCOL_SUCCESS;
1203 goto end;
1206 rpr->rpr_pattern[sizeof (rpr->rpr_pattern) - 1] = 0;
1208 result = rc_node_setup_iter(&ep->re_node, &iter->ri_iter,
1209 rpr->rpr_itertype, rpr->rpr_flags, rpr->rpr_pattern);
1211 if (result == REP_PROTOCOL_SUCCESS)
1212 iter->ri_sequence++;
1214 end:
1215 iter_release(iter);
1216 entity_release(ep);
1217 return (result);
1221 * Returns
1222 * _UNKNOWN_ID
1223 * _NOT_SET - iter has not been started
1224 * _MISORDERED
1225 * _BAD_REQUEST - iter walks values
1226 * _TYPE_MISMATCH - iter does not walk type entities
1227 * _DELETED - parent was deleted
1228 * _NO_RESOURCES
1229 * _INVALID_TYPE - type is invalid
1230 * _DONE
1231 * _SUCCESS
1233 * For composed property group iterators, can also return
1234 * _TYPE_MISMATCH - parent cannot have type children
1235 * _BACKEND_ACCESS
1237 static rep_protocol_responseid_t
1238 iter_read(repcache_client_t *cp, struct rep_protocol_iter_read *rpr)
1240 rep_protocol_responseid_t result;
1241 repcache_iter_t *iter;
1242 repcache_entity_t *ep;
1243 uint32_t sequence;
1245 result = iter_find_w_entity(cp, rpr->rpr_iterid, &iter,
1246 rpr->rpr_entityid, &ep);
1248 if (result != REP_PROTOCOL_SUCCESS)
1249 return (result);
1251 sequence = rpr->rpr_sequence;
1253 if (iter->ri_sequence == 0) {
1254 iter_release(iter);
1255 entity_release(ep);
1256 return (REP_PROTOCOL_FAIL_NOT_SET);
1259 if (sequence == 1) {
1260 iter_release(iter);
1261 entity_release(ep);
1262 return (REP_PROTOCOL_FAIL_MISORDERED);
1265 if (sequence == iter->ri_sequence) {
1266 iter_release(iter);
1267 entity_release(ep);
1268 return (REP_PROTOCOL_SUCCESS);
1271 if (sequence == iter->ri_sequence + 1) {
1272 result = rc_iter_next(iter->ri_iter, &ep->re_node,
1273 ep->re_type);
1275 if (result == REP_PROTOCOL_SUCCESS)
1276 iter->ri_sequence++;
1278 iter_release(iter);
1279 entity_release(ep);
1281 return (result);
1284 iter_release(iter);
1285 entity_release(ep);
1286 return (REP_PROTOCOL_FAIL_MISORDERED);
1289 /*ARGSUSED*/
1290 static void
1291 iter_read_value(repcache_client_t *cp, const void *in, size_t insz,
1292 void *out_arg, size_t *outsz, void *arg)
1294 const struct rep_protocol_iter_read_value *rpr = in;
1295 struct rep_protocol_value_response *out = out_arg;
1296 rep_protocol_responseid_t result;
1298 repcache_iter_t *iter;
1299 uint32_t sequence;
1300 int repeat;
1302 assert(*outsz == sizeof (*out));
1304 iter = iter_find(cp, rpr->rpr_iterid);
1306 if (iter == NULL) {
1307 result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1308 goto out;
1311 sequence = rpr->rpr_sequence;
1313 if (iter->ri_sequence == 0) {
1314 iter_release(iter);
1315 result = REP_PROTOCOL_FAIL_NOT_SET;
1316 goto out;
1319 repeat = (sequence == iter->ri_sequence);
1321 if (sequence == 1 || (!repeat && sequence != iter->ri_sequence + 1)) {
1322 iter_release(iter);
1323 result = REP_PROTOCOL_FAIL_MISORDERED;
1324 goto out;
1327 result = rc_iter_next_value(iter->ri_iter, out, outsz, repeat);
1329 if (!repeat && result == REP_PROTOCOL_SUCCESS)
1330 iter->ri_sequence++;
1332 iter_release(iter);
1334 out:
1336 * If we fail, we only return the response code.
1337 * If we succeed, rc_iter_next_value has shortened *outsz
1338 * to only include the value bytes needed.
1340 if (result != REP_PROTOCOL_SUCCESS && result != REP_PROTOCOL_DONE)
1341 *outsz = sizeof (out->rpr_response);
1343 out->rpr_response = result;
1346 static int
1347 iter_reset(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1349 repcache_iter_t *iter = iter_find(cp, rpr->rpr_iterid);
1351 if (iter == NULL)
1352 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1354 if (iter->ri_sequence != 0) {
1355 iter->ri_sequence = 0;
1356 rc_iter_destroy(&iter->ri_iter);
1358 iter_release(iter);
1359 return (REP_PROTOCOL_SUCCESS);
1362 static rep_protocol_responseid_t
1363 iter_teardown(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1365 iter_remove(cp, rpr->rpr_iterid);
1367 return (REP_PROTOCOL_SUCCESS);
1370 static rep_protocol_responseid_t
1371 tx_start(repcache_client_t *cp, struct rep_protocol_transaction_start *rpr)
1373 repcache_entity_t *tx;
1374 repcache_entity_t *ep;
1375 rep_protocol_responseid_t result;
1377 uint32_t txid = rpr->rpr_entityid_tx;
1378 uint32_t epid = rpr->rpr_entityid;
1380 result = entity_find2(cp, txid, &tx, epid, &ep);
1381 if (result != REP_PROTOCOL_SUCCESS)
1382 return (result);
1384 if (tx->re_txstate == REPCACHE_TX_SETUP) {
1385 result = REP_PROTOCOL_SUCCESS;
1386 goto end;
1388 if (tx->re_txstate != REPCACHE_TX_INIT) {
1389 result = REP_PROTOCOL_FAIL_MISORDERED;
1390 goto end;
1393 result = rc_node_setup_tx(&ep->re_node, &tx->re_node);
1395 end:
1396 if (result == REP_PROTOCOL_SUCCESS)
1397 tx->re_txstate = REPCACHE_TX_SETUP;
1398 else
1399 rc_node_clear(&tx->re_node, 0);
1401 entity_release(ep);
1402 entity_release(tx);
1403 return (result);
1406 /*ARGSUSED*/
1407 static void
1408 tx_commit(repcache_client_t *cp, const void *in, size_t insz,
1409 void *out_arg, size_t *outsz, void *arg)
1411 struct rep_protocol_response *out = out_arg;
1412 const struct rep_protocol_transaction_commit *rpr = in;
1413 repcache_entity_t *tx;
1415 assert(*outsz == sizeof (*out));
1416 assert(insz >= REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE);
1418 if (rpr->rpr_size != insz) {
1419 out->rpr_response = REP_PROTOCOL_FAIL_BAD_REQUEST;
1420 return;
1423 tx = entity_find(cp, rpr->rpr_entityid);
1425 if (tx == NULL) {
1426 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1427 return;
1430 switch (tx->re_txstate) {
1431 case REPCACHE_TX_INIT:
1432 out->rpr_response = REP_PROTOCOL_FAIL_MISORDERED;
1433 break;
1435 case REPCACHE_TX_SETUP:
1436 out->rpr_response = rc_tx_commit(&tx->re_node, rpr->rpr_cmd,
1437 insz - REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE);
1439 if (out->rpr_response == REP_PROTOCOL_SUCCESS) {
1440 tx->re_txstate = REPCACHE_TX_COMMITTED;
1441 rc_node_clear(&tx->re_node, 0);
1444 break;
1445 case REPCACHE_TX_COMMITTED:
1446 out->rpr_response = REP_PROTOCOL_SUCCESS;
1447 break;
1448 default:
1449 assert(0); /* CAN'T HAPPEN */
1450 break;
1453 entity_release(tx);
1456 static rep_protocol_responseid_t
1457 next_snaplevel(repcache_client_t *cp, struct rep_protocol_entity_pair *rpr)
1459 repcache_entity_t *src;
1460 repcache_entity_t *dest;
1462 uint32_t srcid = rpr->rpr_entity_src;
1463 uint32_t destid = rpr->rpr_entity_dst;
1465 int result;
1467 result = entity_find2(cp, srcid, &src, destid, &dest);
1468 if (result != REP_PROTOCOL_SUCCESS)
1469 return (result);
1471 result = rc_node_next_snaplevel(&src->re_node, &dest->re_node);
1473 entity_release(src);
1474 entity_release(dest);
1476 return (result);
1479 static rep_protocol_responseid_t
1480 snapshot_take(repcache_client_t *cp, struct rep_protocol_snapshot_take *rpr)
1482 repcache_entity_t *src;
1483 uint32_t srcid = rpr->rpr_entityid_src;
1484 repcache_entity_t *dest;
1485 uint32_t destid = rpr->rpr_entityid_dest;
1487 int result;
1489 result = entity_find2(cp, srcid, &src, destid, &dest);
1490 if (result != REP_PROTOCOL_SUCCESS)
1491 return (result);
1493 if (dest->re_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
1494 result = REP_PROTOCOL_FAIL_TYPE_MISMATCH;
1495 } else {
1496 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1498 if (rpr->rpr_flags == REP_SNAPSHOT_NEW)
1499 result = rc_snapshot_take_new(&src->re_node, NULL,
1500 NULL, rpr->rpr_name, &dest->re_node);
1501 else if (rpr->rpr_flags == REP_SNAPSHOT_ATTACH &&
1502 rpr->rpr_name[0] == 0)
1503 result = rc_snapshot_take_attach(&src->re_node,
1504 &dest->re_node);
1505 else
1506 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
1508 entity_release(src);
1509 entity_release(dest);
1511 return (result);
1514 static rep_protocol_responseid_t
1515 snapshot_take_named(repcache_client_t *cp,
1516 struct rep_protocol_snapshot_take_named *rpr)
1518 repcache_entity_t *src;
1519 uint32_t srcid = rpr->rpr_entityid_src;
1520 repcache_entity_t *dest;
1521 uint32_t destid = rpr->rpr_entityid_dest;
1523 int result;
1525 result = entity_find2(cp, srcid, &src, destid, &dest);
1526 if (result != REP_PROTOCOL_SUCCESS)
1527 return (result);
1529 if (dest->re_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
1530 result = REP_PROTOCOL_FAIL_TYPE_MISMATCH;
1531 } else {
1532 rpr->rpr_svcname[sizeof (rpr->rpr_svcname) - 1] = 0;
1533 rpr->rpr_instname[sizeof (rpr->rpr_instname) - 1] = 0;
1534 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1536 result = rc_snapshot_take_new(&src->re_node, rpr->rpr_svcname,
1537 rpr->rpr_instname, rpr->rpr_name, &dest->re_node);
1539 entity_release(src);
1540 entity_release(dest);
1542 return (result);
1545 static rep_protocol_responseid_t
1546 snapshot_attach(repcache_client_t *cp, struct rep_protocol_snapshot_attach *rpr)
1548 repcache_entity_t *src;
1549 uint32_t srcid = rpr->rpr_entityid_src;
1550 repcache_entity_t *dest;
1551 uint32_t destid = rpr->rpr_entityid_dest;
1553 int result;
1555 result = entity_find2(cp, srcid, &src, destid, &dest);
1556 if (result != REP_PROTOCOL_SUCCESS)
1557 return (result);
1559 result = rc_snapshot_attach(&src->re_node, &dest->re_node);
1561 entity_release(src);
1562 entity_release(dest);
1564 return (result);
1567 /*ARGSUSED*/
1568 static void
1569 property_get_type(repcache_client_t *cp, const void *in, size_t insz,
1570 void *out_arg, size_t *outsz, void *arg)
1572 const struct rep_protocol_property_request *rpr = in;
1573 struct rep_protocol_integer_response *out = out_arg;
1574 repcache_entity_t *ep;
1575 rep_protocol_value_type_t t = 0;
1577 assert(*outsz == sizeof (*out));
1579 ep = entity_find(cp, rpr->rpr_entityid);
1581 if (ep == NULL) {
1582 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1583 *outsz = sizeof (out->rpr_response);
1584 return;
1587 out->rpr_response = rc_node_get_property_type(&ep->re_node, &t);
1589 entity_release(ep);
1591 if (out->rpr_response != REP_PROTOCOL_SUCCESS)
1592 *outsz = sizeof (out->rpr_response);
1593 else
1594 out->rpr_value = t;
1598 * Fails with:
1599 * _UNKNOWN_ID - an id does not designate an active register
1600 * _NOT_SET - The property is not set
1601 * _DELETED - The property has been deleted
1602 * _TYPE_MISMATCH - The object is not a property
1603 * _NOT_FOUND - The property has no values.
1605 * Succeeds with:
1606 * _SUCCESS - The property has 1 value.
1607 * _TRUNCATED - The property has >1 value.
1609 /*ARGSUSED*/
1610 static void
1611 property_get_value(repcache_client_t *cp, const void *in, size_t insz,
1612 void *out_arg, size_t *outsz, void *arg)
1614 const struct rep_protocol_property_request *rpr = in;
1615 struct rep_protocol_value_response *out = out_arg;
1616 repcache_entity_t *ep;
1618 assert(*outsz == sizeof (*out));
1620 ep = entity_find(cp, rpr->rpr_entityid);
1621 if (ep == NULL) {
1622 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1623 *outsz = sizeof (out->rpr_response);
1624 return;
1627 out->rpr_response = rc_node_get_property_value(&ep->re_node, out,
1628 outsz);
1630 entity_release(ep);
1633 * If we fail, we only return the response code.
1634 * If we succeed, rc_node_get_property_value has shortened *outsz
1635 * to only include the value bytes needed.
1637 if (out->rpr_response != REP_PROTOCOL_SUCCESS &&
1638 out->rpr_response != REP_PROTOCOL_FAIL_TRUNCATED)
1639 *outsz = sizeof (out->rpr_response);
1642 static rep_protocol_responseid_t
1643 propertygrp_notify(repcache_client_t *cp,
1644 struct rep_protocol_propertygrp_request *rpr, int *out_fd)
1646 int fds[2];
1647 int ours, theirs;
1649 rep_protocol_responseid_t result;
1650 repcache_entity_t *ep;
1652 if (pipe(fds) < 0)
1653 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1655 ours = fds[0];
1656 theirs = fds[1];
1658 if ((ep = entity_find(cp, rpr->rpr_entityid)) == NULL) {
1659 result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1660 goto fail;
1664 * While the following can race with other threads setting up a
1665 * notification, the worst that can happen is that our fd has
1666 * already been closed before we return.
1668 result = rc_pg_notify_setup(&cp->rc_pg_notify, &ep->re_node,
1669 ours);
1671 entity_release(ep);
1673 if (result != REP_PROTOCOL_SUCCESS)
1674 goto fail;
1676 *out_fd = theirs;
1677 return (REP_PROTOCOL_SUCCESS);
1679 fail:
1680 (void) close(ours);
1681 (void) close(theirs);
1683 return (result);
1686 static rep_protocol_responseid_t
1687 client_add_notify(repcache_client_t *cp,
1688 struct rep_protocol_notify_request *rpr)
1690 rpr->rpr_pattern[sizeof (rpr->rpr_pattern) - 1] = 0;
1692 switch (rpr->rpr_type) {
1693 case REP_PROTOCOL_NOTIFY_PGNAME:
1694 return (rc_notify_info_add_name(&cp->rc_notify_info,
1695 rpr->rpr_pattern));
1697 case REP_PROTOCOL_NOTIFY_PGTYPE:
1698 return (rc_notify_info_add_type(&cp->rc_notify_info,
1699 rpr->rpr_pattern));
1701 default:
1702 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1706 /*ARGSUSED*/
1707 static void
1708 client_wait(repcache_client_t *cp, const void *in, size_t insz,
1709 void *out_arg, size_t *outsz, void *arg)
1711 int result;
1712 repcache_entity_t *ep;
1713 const struct rep_protocol_wait_request *rpr = in;
1714 struct rep_protocol_fmri_response *out = out_arg;
1716 assert(*outsz == sizeof (*out));
1718 (void) pthread_mutex_lock(&cp->rc_lock);
1719 if (cp->rc_notify_thr != 0) {
1720 (void) pthread_mutex_unlock(&cp->rc_lock);
1721 out->rpr_response = REP_PROTOCOL_FAIL_EXISTS;
1722 *outsz = sizeof (out->rpr_response);
1723 return;
1725 cp->rc_notify_thr = pthread_self();
1726 (void) pthread_mutex_unlock(&cp->rc_lock);
1728 result = rc_notify_info_wait(&cp->rc_notify_info, &cp->rc_notify_ptr,
1729 out->rpr_fmri, sizeof (out->rpr_fmri));
1731 if (result == REP_PROTOCOL_SUCCESS) {
1732 if ((ep = entity_find(cp, rpr->rpr_entityid)) != NULL) {
1733 if (ep->re_type == REP_PROTOCOL_ENTITY_PROPERTYGRP) {
1734 rc_node_ptr_assign(&ep->re_node,
1735 &cp->rc_notify_ptr);
1736 } else {
1737 result = REP_PROTOCOL_FAIL_TYPE_MISMATCH;
1739 entity_release(ep);
1740 } else {
1741 result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1743 rc_node_clear(&cp->rc_notify_ptr, 0);
1746 (void) pthread_mutex_lock(&cp->rc_lock);
1747 assert(cp->rc_notify_thr == pthread_self());
1748 cp->rc_notify_thr = 0;
1749 (void) pthread_mutex_unlock(&cp->rc_lock);
1751 out->rpr_response = result;
1752 if (result != REP_PROTOCOL_SUCCESS)
1753 *outsz = sizeof (out->rpr_response);
1757 * Can return:
1758 * _PERMISSION_DENIED not enough privileges to do request.
1759 * _BAD_REQUEST name is not valid or reserved
1760 * _TRUNCATED name is too long for current repository path
1761 * _UNKNOWN failed for unknown reason (details written to
1762 * console)
1763 * _BACKEND_READONLY backend is not writable
1764 * _NO_RESOURCES out of memory
1765 * _SUCCESS Backup completed successfully.
1767 static rep_protocol_responseid_t
1768 backup_repository(repcache_client_t *cp,
1769 struct rep_protocol_backup_request *rpr)
1771 rep_protocol_responseid_t result;
1772 ucred_t *uc = get_ucred();
1774 if (!client_is_privileged() && (uc == NULL || ucred_geteuid(uc) != 0))
1775 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
1777 rpr->rpr_name[REP_PROTOCOL_NAME_LEN - 1] = 0;
1778 if (strcmp(rpr->rpr_name, REPOSITORY_BOOT_BACKUP) == 0)
1779 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1781 (void) pthread_mutex_lock(&cp->rc_lock);
1782 if (rpr->rpr_changeid != cp->rc_changeid) {
1783 result = backend_create_backup(rpr->rpr_name);
1784 if (result == REP_PROTOCOL_SUCCESS)
1785 cp->rc_changeid = rpr->rpr_changeid;
1786 } else {
1787 result = REP_PROTOCOL_SUCCESS;
1789 (void) pthread_mutex_unlock(&cp->rc_lock);
1791 return (result);
1795 * This function captures the information that will be used for an
1796 * annotation audit event. Specifically, it captures the operation to be
1797 * performed and the name of the file that is being used. These values are
1798 * copied from the rep_protocol_annotation request at rpr to the client
1799 * structure. If both these values are null, the client is turning
1800 * annotation off.
1802 * Fails with
1803 * _NO_RESOURCES - unable to allocate memory
1805 static rep_protocol_responseid_t
1806 set_annotation(repcache_client_t *cp, struct rep_protocol_annotation *rpr)
1808 au_id_t audit_uid;
1809 const char *file = NULL;
1810 const char *old_ptrs[2];
1811 const char *operation = NULL;
1812 rep_protocol_responseid_t rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1813 au_asid_t sessionid;
1815 (void) memset(old_ptrs, 0, sizeof (old_ptrs));
1817 /* Copy rpr_operation and rpr_file if they are not empty strings. */
1818 if (rpr->rpr_operation[0] != 0) {
1820 * Make sure that client did not send us an unterminated buffer.
1822 rpr->rpr_operation[sizeof (rpr->rpr_operation) - 1] = 0;
1823 if ((operation = strdup(rpr->rpr_operation)) == NULL)
1824 goto out;
1826 if (rpr->rpr_file[0] != 0) {
1828 * Make sure that client did not send us an unterminated buffer.
1830 rpr->rpr_file[sizeof (rpr->rpr_file) - 1] = 0;
1831 if ((file = strdup(rpr->rpr_file)) == NULL)
1832 goto out;
1835 (void) pthread_mutex_lock(&cp->rc_annotate_lock);
1836 /* Save addresses of memory to free when not locked */
1837 old_ptrs[0] = cp->rc_operation;
1838 old_ptrs[1] = cp->rc_file;
1840 /* Save pointers to annotation strings. */
1841 cp->rc_operation = operation;
1842 cp->rc_file = file;
1845 * Set annotation flag. Annotations should be turned on if either
1846 * operation or file are not NULL.
1848 cp->rc_annotate = (operation != NULL) || (file != NULL);
1849 (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1852 * operation and file pointers are saved in cp, so don't free them
1853 * during cleanup.
1855 operation = NULL;
1856 file = NULL;
1857 rc = REP_PROTOCOL_SUCCESS;
1860 * Native builds are done to create svc.configd-native. This
1861 * program runs only on the Open Solaris build machines to create
1862 * the seed repository. Until the SMF auditing code is distributed
1863 * to the Open Solaris build machines, adt_get_unique_id() in the
1864 * following code is not a global function in libbsm. Hence the
1865 * following conditional compilation.
1867 #ifndef NATIVE_BUILD
1869 * Set the appropriate audit session id.
1871 if (cp->rc_annotate) {
1873 * We're starting a group of annotated audit events, so
1874 * create and set an audit session ID for this annotation.
1876 adt_get_auid(cp->rc_adt_session, &audit_uid);
1877 sessionid = adt_get_unique_id(audit_uid);
1878 } else {
1880 * Annotation is done so restore our client audit session
1881 * id.
1883 sessionid = cp->rc_adt_sessionid;
1885 adt_set_asid(cp->rc_adt_session, sessionid);
1886 #endif /* NATIVE_BUILD */
1888 out:
1889 if (operation != NULL)
1890 free((void *)operation);
1891 if (file != NULL)
1892 free((void *)file);
1893 free((void *)old_ptrs[0]);
1894 free((void *)old_ptrs[1]);
1895 return (rc);
1899 * Determine if an annotation event needs to be generated. If it does
1900 * provide the operation and file name that should be used in the event.
1902 * Can return:
1903 * 0 No annotation event needed or buffers are not large
1904 * enough. Either way an event should not be
1905 * generated.
1906 * 1 Generate annotation event.
1909 client_annotation_needed(char *operation, size_t oper_sz,
1910 char *file, size_t file_sz)
1912 thread_info_t *ti = thread_self();
1913 repcache_client_t *cp = ti->ti_active_client;
1914 int rc = 0;
1916 (void) pthread_mutex_lock(&cp->rc_annotate_lock);
1917 if (cp->rc_annotate) {
1918 rc = 1;
1919 if (cp->rc_operation == NULL) {
1920 if (oper_sz > 0)
1921 operation[0] = 0;
1922 } else {
1923 if (strlcpy(operation, cp->rc_operation, oper_sz) >=
1924 oper_sz) {
1925 /* Buffer overflow, so do not generate event */
1926 rc = 0;
1929 if (cp->rc_file == NULL) {
1930 if (file_sz > 0)
1931 file[0] = 0;
1932 } else if (rc == 1) {
1933 if (strlcpy(file, cp->rc_file, file_sz) >= file_sz) {
1934 /* Buffer overflow, so do not generate event */
1935 rc = 0;
1939 (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1940 return (rc);
1943 void
1944 client_annotation_finished()
1946 thread_info_t *ti = thread_self();
1947 repcache_client_t *cp = ti->ti_active_client;
1949 (void) pthread_mutex_lock(&cp->rc_annotate_lock);
1950 cp->rc_annotate = 0;
1951 (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1954 #ifndef NATIVE_BUILD
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);
2011 #endif
2014 * Handle switch client request
2016 * This routine can return:
2018 * _PERMISSION_DENIED not enough privileges to do request.
2019 * _UNKNOWN file operation error (details written to
2020 * the console).
2021 * _SUCCESS switch operation is completed.
2022 * _BACKEND_ACCESS backend access fails.
2023 * _NO_RESOURCES out of memory.
2024 * _BACKEND_READONLY backend is not writable.
2026 static rep_protocol_responseid_t
2027 repository_switch(repcache_client_t *cp,
2028 struct rep_protocol_switch_request *rpr)
2030 rep_protocol_responseid_t result;
2031 ucred_t *uc = get_ucred();
2033 if (!client_is_privileged() && (uc == NULL ||
2034 ucred_geteuid(uc) != 0)) {
2035 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
2038 (void) pthread_mutex_lock(&cp->rc_lock);
2039 if (rpr->rpr_changeid != cp->rc_changeid) {
2040 if ((result = backend_switch(rpr->rpr_flag)) ==
2041 REP_PROTOCOL_SUCCESS)
2042 cp->rc_changeid = rpr->rpr_changeid;
2043 } else {
2044 result = REP_PROTOCOL_SUCCESS;
2046 (void) pthread_mutex_unlock(&cp->rc_lock);
2048 return (result);
2051 typedef rep_protocol_responseid_t protocol_simple_f(repcache_client_t *cp,
2052 const void *rpr);
2054 /*ARGSUSED*/
2055 static void
2056 simple_handler(repcache_client_t *cp, const void *in, size_t insz,
2057 void *out_arg, size_t *outsz, void *arg)
2059 protocol_simple_f *f = (protocol_simple_f *)arg;
2060 rep_protocol_response_t *out = out_arg;
2062 assert(*outsz == sizeof (*out));
2063 assert(f != NULL);
2065 out->rpr_response = (*f)(cp, in);
2068 typedef rep_protocol_responseid_t protocol_simple_fd_f(repcache_client_t *cp,
2069 const void *rpr, int *out_fd);
2071 /*ARGSUSED*/
2072 static void
2073 simple_fd_handler(repcache_client_t *cp, const void *in, size_t insz,
2074 void *out_arg, size_t *outsz, void *arg, int *out_fd)
2076 protocol_simple_fd_f *f = (protocol_simple_fd_f *)arg;
2077 rep_protocol_response_t *out = out_arg;
2079 assert(*outsz == sizeof (*out));
2080 assert(f != NULL);
2082 out->rpr_response = (*f)(cp, in, out_fd);
2085 typedef void protocol_handler_f(repcache_client_t *, const void *in,
2086 size_t insz, void *out, size_t *outsz, void *arg);
2088 typedef void protocol_handler_fdret_f(repcache_client_t *, const void *in,
2089 size_t insz, void *out, size_t *outsz, void *arg, int *fd_out);
2091 #define PROTO(p, f, in) { \
2092 p, #p, simple_handler, (void *)(&f), NULL, \
2093 sizeof (in), sizeof (rep_protocol_response_t), 0 \
2096 #define PROTO_FD_OUT(p, f, in) { \
2097 p, #p, NULL, (void *)(&f), simple_fd_handler, \
2098 sizeof (in), \
2099 sizeof (rep_protocol_response_t), \
2100 PROTO_FLAG_RETFD \
2103 #define PROTO_VARIN(p, f, insz) { \
2104 p, #p, &(f), NULL, NULL, \
2105 insz, sizeof (rep_protocol_response_t), \
2106 PROTO_FLAG_VARINPUT \
2109 #define PROTO_UINT_OUT(p, f, in) { \
2110 p, #p, &(f), NULL, NULL, \
2111 sizeof (in), \
2112 sizeof (struct rep_protocol_integer_response), 0 \
2115 #define PROTO_NAME_OUT(p, f, in) { \
2116 p, #p, &(f), NULL, NULL, \
2117 sizeof (in), \
2118 sizeof (struct rep_protocol_name_response), 0 \
2121 #define PROTO_FMRI_OUT(p, f, in) { \
2122 p, #p, &(f), NULL, NULL, \
2123 sizeof (in), \
2124 sizeof (struct rep_protocol_fmri_response), 0 \
2127 #define PROTO_VALUE_OUT(p, f, in) { \
2128 p, #p, &(f), NULL, NULL, \
2129 sizeof (in), \
2130 sizeof (struct rep_protocol_value_response), 0 \
2133 #define PROTO_PANIC(p) { p, #p, NULL, NULL, NULL, 0, 0, PROTO_FLAG_PANIC }
2134 #define PROTO_END() { 0, NULL, NULL, NULL, NULL, 0, 0, PROTO_FLAG_PANIC }
2136 #define PROTO_FLAG_PANIC 0x00000001 /* should never be called */
2137 #define PROTO_FLAG_VARINPUT 0x00000004 /* in_size is minimum size */
2138 #define PROTO_FLAG_RETFD 0x00000008 /* can also return an FD */
2140 #define PROTO_ALL_FLAGS 0x0000000f /* all flags */
2142 static struct protocol_entry {
2143 enum rep_protocol_requestid pt_request;
2144 const char *pt_name;
2145 protocol_handler_f *pt_handler;
2146 void *pt_arg;
2147 protocol_handler_fdret_f *pt_fd_handler;
2148 size_t pt_in_size;
2149 size_t pt_out_max;
2150 uint32_t pt_flags;
2151 } protocol_table[] = {
2152 PROTO_PANIC(REP_PROTOCOL_CLOSE), /* special case */
2154 PROTO(REP_PROTOCOL_ENTITY_SETUP, entity_setup,
2155 struct rep_protocol_entity_setup),
2156 PROTO_NAME_OUT(REP_PROTOCOL_ENTITY_NAME, entity_name,
2157 struct rep_protocol_entity_name),
2158 PROTO_UINT_OUT(REP_PROTOCOL_ENTITY_PARENT_TYPE, entity_parent_type,
2159 struct rep_protocol_entity_parent_type),
2160 PROTO(REP_PROTOCOL_ENTITY_GET_CHILD, entity_get_child,
2161 struct rep_protocol_entity_get_child),
2162 PROTO(REP_PROTOCOL_ENTITY_GET_PARENT, entity_get_parent,
2163 struct rep_protocol_entity_parent),
2164 PROTO(REP_PROTOCOL_ENTITY_GET, entity_get,
2165 struct rep_protocol_entity_get),
2166 PROTO(REP_PROTOCOL_ENTITY_UPDATE, entity_update,
2167 struct rep_protocol_entity_update),
2168 PROTO(REP_PROTOCOL_ENTITY_CREATE_CHILD, entity_create_child,
2169 struct rep_protocol_entity_create_child),
2170 PROTO(REP_PROTOCOL_ENTITY_CREATE_PG, entity_create_pg,
2171 struct rep_protocol_entity_create_pg),
2172 PROTO(REP_PROTOCOL_ENTITY_DELETE, entity_delete,
2173 struct rep_protocol_entity_delete),
2174 PROTO(REP_PROTOCOL_ENTITY_RESET, entity_reset,
2175 struct rep_protocol_entity_reset),
2176 PROTO(REP_PROTOCOL_ENTITY_TEARDOWN, entity_teardown,
2177 struct rep_protocol_entity_teardown),
2179 PROTO(REP_PROTOCOL_ITER_SETUP, iter_setup,
2180 struct rep_protocol_iter_request),
2181 PROTO(REP_PROTOCOL_ITER_START, iter_start,
2182 struct rep_protocol_iter_start),
2183 PROTO(REP_PROTOCOL_ITER_READ, iter_read,
2184 struct rep_protocol_iter_read),
2185 PROTO_VALUE_OUT(REP_PROTOCOL_ITER_READ_VALUE, iter_read_value,
2186 struct rep_protocol_iter_read_value),
2187 PROTO(REP_PROTOCOL_ITER_RESET, iter_reset,
2188 struct rep_protocol_iter_request),
2189 PROTO(REP_PROTOCOL_ITER_TEARDOWN, iter_teardown,
2190 struct rep_protocol_iter_request),
2192 PROTO(REP_PROTOCOL_NEXT_SNAPLEVEL, next_snaplevel,
2193 struct rep_protocol_entity_pair),
2195 PROTO(REP_PROTOCOL_SNAPSHOT_TAKE, snapshot_take,
2196 struct rep_protocol_snapshot_take),
2197 PROTO(REP_PROTOCOL_SNAPSHOT_TAKE_NAMED, snapshot_take_named,
2198 struct rep_protocol_snapshot_take_named),
2199 PROTO(REP_PROTOCOL_SNAPSHOT_ATTACH, snapshot_attach,
2200 struct rep_protocol_snapshot_attach),
2202 PROTO_UINT_OUT(REP_PROTOCOL_PROPERTY_GET_TYPE, property_get_type,
2203 struct rep_protocol_property_request),
2204 PROTO_VALUE_OUT(REP_PROTOCOL_PROPERTY_GET_VALUE, property_get_value,
2205 struct rep_protocol_property_request),
2207 PROTO_FD_OUT(REP_PROTOCOL_PROPERTYGRP_SETUP_WAIT, propertygrp_notify,
2208 struct rep_protocol_propertygrp_request),
2209 PROTO(REP_PROTOCOL_PROPERTYGRP_TX_START, tx_start,
2210 struct rep_protocol_transaction_start),
2211 PROTO_VARIN(REP_PROTOCOL_PROPERTYGRP_TX_COMMIT, tx_commit,
2212 REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE),
2214 PROTO(REP_PROTOCOL_CLIENT_ADD_NOTIFY, client_add_notify,
2215 struct rep_protocol_notify_request),
2216 PROTO_FMRI_OUT(REP_PROTOCOL_CLIENT_WAIT, client_wait,
2217 struct rep_protocol_wait_request),
2219 PROTO(REP_PROTOCOL_BACKUP, backup_repository,
2220 struct rep_protocol_backup_request),
2222 PROTO(REP_PROTOCOL_SET_AUDIT_ANNOTATION, set_annotation,
2223 struct rep_protocol_annotation),
2225 PROTO(REP_PROTOCOL_SWITCH, repository_switch,
2226 struct rep_protocol_switch_request),
2228 PROTO_END()
2230 #undef PROTO
2231 #undef PROTO_FMRI_OUT
2232 #undef PROTO_NAME_OUT
2233 #undef PROTO_UINT_OUT
2234 #undef PROTO_PANIC
2235 #undef PROTO_END
2238 * The number of entries, sans PROTO_END()
2240 #define PROTOCOL_ENTRIES \
2241 (sizeof (protocol_table) / sizeof (*protocol_table) - 1)
2243 #define PROTOCOL_PREFIX "REP_PROTOCOL_"
2246 client_init(void)
2248 int i;
2249 struct protocol_entry *e;
2251 if (!client_hash_init())
2252 return (0);
2254 if (request_log_size > 0) {
2255 request_log = uu_zalloc(request_log_size *
2256 sizeof (request_log_entry_t));
2260 * update the names to not include REP_PROTOCOL_
2262 for (i = 0; i < PROTOCOL_ENTRIES; i++) {
2263 e = &protocol_table[i];
2264 assert(strncmp(e->pt_name, PROTOCOL_PREFIX,
2265 strlen(PROTOCOL_PREFIX)) == 0);
2266 e->pt_name += strlen(PROTOCOL_PREFIX);
2269 * verify the protocol table is consistent
2271 for (i = 0; i < PROTOCOL_ENTRIES; i++) {
2272 e = &protocol_table[i];
2273 assert(e->pt_request == (REP_PROTOCOL_BASE + i));
2275 assert((e->pt_flags & ~PROTO_ALL_FLAGS) == 0);
2277 if (e->pt_flags & PROTO_FLAG_PANIC)
2278 assert(e->pt_in_size == 0 && e->pt_out_max == 0 &&
2279 e->pt_handler == NULL);
2280 else
2281 assert(e->pt_in_size != 0 && e->pt_out_max != 0 &&
2282 (e->pt_handler != NULL ||
2283 e->pt_fd_handler != NULL));
2285 assert((REP_PROTOCOL_BASE + i) == REP_PROTOCOL_MAX_REQUEST);
2287 assert(protocol_table[i].pt_request == 0);
2289 return (1);
2292 static void
2293 client_switcher(void *cookie, char *argp, size_t arg_size, door_desc_t *desc_in,
2294 uint_t n_desc)
2296 thread_info_t *ti = thread_self();
2298 repcache_client_t *cp;
2299 uint32_t id = (uint32_t)cookie;
2300 enum rep_protocol_requestid request_code;
2302 rep_protocol_responseid_t result = INVALID_RESULT;
2304 struct protocol_entry *e;
2306 char *retval = NULL;
2307 size_t retsize = 0;
2309 int retfd = -1;
2310 door_desc_t desc;
2311 request_log_entry_t *rlp;
2313 rlp = start_log(id);
2315 if (n_desc != 0)
2316 uu_die("can't happen: %d descriptors @%p (cookie %p)",
2317 n_desc, desc_in, cookie);
2319 if (argp == DOOR_UNREF_DATA) {
2320 client_destroy(id);
2321 goto bad_end;
2324 thread_newstate(ti, TI_CLIENT_CALL);
2327 * To simplify returning just a result code, we set up for
2328 * that case here.
2330 retval = (char *)&result;
2331 retsize = sizeof (result);
2333 if (arg_size < sizeof (request_code)) {
2334 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2335 goto end_unheld;
2338 ti->ti_client_request = (void *)argp;
2340 /* LINTED alignment */
2341 request_code = *(uint32_t *)argp;
2343 if (rlp != NULL) {
2344 rlp->rl_request = request_code;
2347 * In order to avoid locking problems on removal, we handle the
2348 * "close" case before doing a lookup.
2350 if (request_code == REP_PROTOCOL_CLOSE) {
2351 client_destroy(id);
2352 result = REP_PROTOCOL_SUCCESS;
2353 goto end_unheld;
2356 cp = client_lookup(id);
2358 * cp is held
2361 if (cp == NULL)
2362 goto bad_end;
2364 if (rlp != NULL)
2365 rlp->rl_client = cp;
2367 ti->ti_active_client = cp;
2369 if (request_code < REP_PROTOCOL_BASE ||
2370 request_code >= REP_PROTOCOL_BASE + PROTOCOL_ENTRIES) {
2371 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2372 goto end;
2375 e = &protocol_table[request_code - REP_PROTOCOL_BASE];
2377 assert(!(e->pt_flags & PROTO_FLAG_PANIC));
2379 if (e->pt_flags & PROTO_FLAG_VARINPUT) {
2380 if (arg_size < e->pt_in_size) {
2381 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2382 goto end;
2384 } else if (arg_size != e->pt_in_size) {
2385 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2386 goto end;
2389 if (retsize != e->pt_out_max) {
2390 retsize = e->pt_out_max;
2391 retval = alloca(retsize);
2394 if (e->pt_flags & PROTO_FLAG_RETFD)
2395 e->pt_fd_handler(cp, argp, arg_size, retval, &retsize,
2396 e->pt_arg, &retfd);
2397 else
2398 e->pt_handler(cp, argp, arg_size, retval, &retsize, e->pt_arg);
2400 end:
2401 ti->ti_active_client = NULL;
2402 client_release(cp);
2404 end_unheld:
2405 if (rlp != NULL) {
2406 /* LINTED alignment */
2407 rlp->rl_response = *(uint32_t *)retval;
2408 end_log();
2409 rlp = NULL;
2411 ti->ti_client_request = NULL;
2412 thread_newstate(ti, TI_DOOR_RETURN);
2414 if (retval == (char *)&result) {
2415 assert(result != INVALID_RESULT && retsize == sizeof (result));
2416 } else {
2417 /* LINTED alignment */
2418 result = *(uint32_t *)retval;
2420 if (retfd != -1) {
2421 desc.d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE;
2422 desc.d_data.d_desc.d_descriptor = retfd;
2423 (void) door_return(retval, retsize, &desc, 1);
2424 } else {
2425 (void) door_return(retval, retsize, NULL, 0);
2427 bad_end:
2428 if (rlp != NULL) {
2429 rlp->rl_response = -1;
2430 end_log();
2431 rlp = NULL;
2433 (void) door_return(NULL, 0, NULL, 0);
2437 create_client(pid_t pid, uint32_t debugflags, int privileged, int *out_fd)
2439 int fd;
2441 repcache_client_t *cp;
2443 struct door_info info;
2445 int door_flags = DOOR_UNREF | DOOR_REFUSE_DESC;
2446 #ifdef DOOR_NO_CANCEL
2447 door_flags |= DOOR_NO_CANCEL;
2448 #endif
2450 cp = client_alloc();
2451 if (cp == NULL)
2452 return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2454 (void) pthread_mutex_lock(&client_lock);
2455 cp->rc_id = ++client_maxid;
2456 (void) pthread_mutex_unlock(&client_lock);
2458 cp->rc_all_auths = privileged;
2459 cp->rc_pid = pid;
2460 cp->rc_debug = debugflags;
2462 #ifndef NATIVE_BUILD
2463 start_audit_session(cp);
2464 #endif
2466 cp->rc_doorfd = door_create(client_switcher, (void *)cp->rc_id,
2467 door_flags);
2469 if (cp->rc_doorfd < 0) {
2470 client_free(cp);
2471 return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2473 #ifdef DOOR_PARAM_DATA_MIN
2474 (void) door_setparam(cp->rc_doorfd, DOOR_PARAM_DATA_MIN,
2475 sizeof (enum rep_protocol_requestid));
2476 #endif
2478 if ((fd = dup(cp->rc_doorfd)) < 0 ||
2479 door_info(cp->rc_doorfd, &info) < 0) {
2480 if (fd >= 0)
2481 (void) close(fd);
2482 (void) door_revoke(cp->rc_doorfd);
2483 cp->rc_doorfd = -1;
2484 client_free(cp);
2485 return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2488 rc_pg_notify_init(&cp->rc_pg_notify);
2489 rc_notify_info_init(&cp->rc_notify_info);
2491 client_insert(cp);
2493 cp->rc_doorid = info.di_uniquifier;
2494 *out_fd = fd;
2496 return (REPOSITORY_DOOR_SUCCESS);