8987 bootadm: add bootfile fallback to unix
[unleashed.git] / usr / src / cmd / svc / configd / client.c
blob13c014035419a693954a0bdfc76f77fe28cc1074
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 #ifdef lint
64 #define assert_nolint(x) (void)0
65 #else
66 #define assert_nolint(x) assert(x)
67 #endif
70 * Protects client linkage and the freelist
72 #define CLIENT_HASH_SIZE 64
74 #pragma align 64(client_hash)
75 static client_bucket_t client_hash[CLIENT_HASH_SIZE];
77 static uu_avl_pool_t *entity_pool;
78 static uu_avl_pool_t *iter_pool;
79 static uu_list_pool_t *client_pool;
81 #define CLIENT_HASH(id) (&client_hash[((id) & (CLIENT_HASH_SIZE - 1))])
83 uint_t request_log_size = 1024; /* tunable, before we start */
85 static pthread_mutex_t request_log_lock = PTHREAD_MUTEX_INITIALIZER;
86 static uint_t request_log_cur;
87 request_log_entry_t *request_log;
89 static uint32_t client_maxid;
90 static pthread_mutex_t client_lock; /* protects client_maxid */
92 static request_log_entry_t *
93 get_log(void)
95 thread_info_t *ti = thread_self();
96 return (&ti->ti_log);
99 void
100 log_enter(request_log_entry_t *rlp)
102 if (rlp->rl_start != 0 && request_log != NULL) {
103 request_log_entry_t *logrlp;
105 (void) pthread_mutex_lock(&request_log_lock);
106 assert(request_log_cur < request_log_size);
107 logrlp = &request_log[request_log_cur++];
108 if (request_log_cur == request_log_size)
109 request_log_cur = 0;
110 (void) memcpy(logrlp, rlp, sizeof (*rlp));
111 (void) pthread_mutex_unlock(&request_log_lock);
116 * Note that the svc.configd dmod will join all of the per-thread log entries
117 * with the main log, so that even if the log is disabled, there is some
118 * information available.
120 static request_log_entry_t *
121 start_log(uint32_t clientid)
123 request_log_entry_t *rlp = get_log();
125 log_enter(rlp);
127 (void) memset(rlp, 0, sizeof (*rlp));
128 rlp->rl_start = gethrtime();
129 rlp->rl_tid = pthread_self();
130 rlp->rl_clientid = clientid;
132 return (rlp);
135 void
136 end_log(void)
138 request_log_entry_t *rlp = get_log();
140 rlp->rl_end = gethrtime();
143 static void
144 add_log_ptr(request_log_entry_t *rlp, enum rc_ptr_type type, uint32_t id,
145 void *ptr)
147 request_log_ptr_t *rpp;
149 if (rlp == NULL)
150 return;
152 if (rlp->rl_num_ptrs >= MAX_PTRS)
153 return;
155 rpp = &rlp->rl_ptrs[rlp->rl_num_ptrs++];
156 rpp->rlp_type = type;
157 rpp->rlp_id = id;
158 rpp->rlp_ptr = ptr;
161 * For entities, it's useful to have the node pointer at the start
162 * of the request.
164 if (type == RC_PTR_TYPE_ENTITY && ptr != NULL)
165 rpp->rlp_data = ((repcache_entity_t *)ptr)->re_node.rnp_node;
169 client_is_privileged(void)
171 thread_info_t *ti = thread_self();
173 ucred_t *uc;
175 if (ti->ti_active_client != NULL &&
176 ti->ti_active_client->rc_all_auths)
177 return (1);
179 if ((uc = get_ucred()) == NULL)
180 return (0);
182 return (ucred_is_privileged(uc));
185 /*ARGSUSED*/
186 static int
187 client_compare(const void *lc_arg, const void *rc_arg, void *private)
189 uint32_t l_id = ((const repcache_client_t *)lc_arg)->rc_id;
190 uint32_t r_id = ((const repcache_client_t *)rc_arg)->rc_id;
192 if (l_id > r_id)
193 return (1);
194 if (l_id < r_id)
195 return (-1);
196 return (0);
199 /*ARGSUSED*/
200 static int
201 entity_compare(const void *lc_arg, const void *rc_arg, void *private)
203 uint32_t l_id = ((const repcache_entity_t *)lc_arg)->re_id;
204 uint32_t r_id = ((const repcache_entity_t *)rc_arg)->re_id;
206 if (l_id > r_id)
207 return (1);
208 if (l_id < r_id)
209 return (-1);
210 return (0);
213 /*ARGSUSED*/
214 static int
215 iter_compare(const void *lc_arg, const void *rc_arg, void *private)
217 uint32_t l_id = ((const repcache_iter_t *)lc_arg)->ri_id;
218 uint32_t r_id = ((const repcache_iter_t *)rc_arg)->ri_id;
220 if (l_id > r_id)
221 return (1);
222 if (l_id < r_id)
223 return (-1);
224 return (0);
227 static int
228 client_hash_init(void)
230 int x;
232 assert_nolint(offsetof(repcache_entity_t, re_id) == 0);
233 entity_pool = uu_avl_pool_create("repcache_entitys",
234 sizeof (repcache_entity_t), offsetof(repcache_entity_t, re_link),
235 entity_compare, UU_AVL_POOL_DEBUG);
237 assert_nolint(offsetof(repcache_iter_t, ri_id) == 0);
238 iter_pool = uu_avl_pool_create("repcache_iters",
239 sizeof (repcache_iter_t), offsetof(repcache_iter_t, ri_link),
240 iter_compare, UU_AVL_POOL_DEBUG);
242 assert_nolint(offsetof(repcache_client_t, rc_id) == 0);
243 client_pool = uu_list_pool_create("repcache_clients",
244 sizeof (repcache_client_t), offsetof(repcache_client_t, rc_link),
245 client_compare, UU_LIST_POOL_DEBUG);
247 if (entity_pool == NULL || iter_pool == NULL || client_pool == NULL)
248 return (0);
250 for (x = 0; x < CLIENT_HASH_SIZE; x++) {
251 uu_list_t *lp = uu_list_create(client_pool, &client_hash[x],
252 UU_LIST_SORTED);
253 if (lp == NULL)
254 return (0);
256 (void) pthread_mutex_init(&client_hash[x].cb_lock, NULL);
257 client_hash[x].cb_list = lp;
260 return (1);
263 static repcache_client_t *
264 client_alloc(void)
266 repcache_client_t *cp;
267 cp = uu_zalloc(sizeof (*cp));
268 if (cp == NULL)
269 return (NULL);
271 cp->rc_entities = uu_avl_create(entity_pool, cp, 0);
272 if (cp->rc_entities == NULL)
273 goto fail;
275 cp->rc_iters = uu_avl_create(iter_pool, cp, 0);
276 if (cp->rc_iters == NULL)
277 goto fail;
279 uu_list_node_init(cp, &cp->rc_link, client_pool);
281 cp->rc_doorfd = -1;
282 cp->rc_doorid = INVALID_DOORID;
284 (void) pthread_mutex_init(&cp->rc_lock, NULL);
285 (void) pthread_mutex_init(&cp->rc_annotate_lock, NULL);
287 rc_node_ptr_init(&cp->rc_notify_ptr);
289 return (cp);
291 fail:
292 if (cp->rc_iters != NULL)
293 uu_avl_destroy(cp->rc_iters);
294 if (cp->rc_entities != NULL)
295 uu_avl_destroy(cp->rc_entities);
296 uu_free(cp);
297 return (NULL);
300 static void
301 client_free(repcache_client_t *cp)
303 assert(cp->rc_insert_thr == 0);
304 assert(cp->rc_refcnt == 0);
305 assert(cp->rc_doorfd == -1);
306 assert(cp->rc_doorid == INVALID_DOORID);
307 assert(uu_avl_first(cp->rc_entities) == NULL);
308 assert(uu_avl_first(cp->rc_iters) == NULL);
309 uu_avl_destroy(cp->rc_entities);
310 uu_avl_destroy(cp->rc_iters);
311 uu_list_node_fini(cp, &cp->rc_link, client_pool);
312 (void) pthread_mutex_destroy(&cp->rc_lock);
313 (void) pthread_mutex_destroy(&cp->rc_annotate_lock);
314 rc_node_ptr_free_mem(&cp->rc_notify_ptr);
315 uu_free(cp);
318 static void
319 client_insert(repcache_client_t *cp)
321 client_bucket_t *bp = CLIENT_HASH(cp->rc_id);
322 uu_list_index_t idx;
324 assert(cp->rc_id > 0);
326 (void) pthread_mutex_lock(&bp->cb_lock);
328 * We assume it does not already exist
330 (void) uu_list_find(bp->cb_list, cp, NULL, &idx);
331 uu_list_insert(bp->cb_list, cp, idx);
333 (void) pthread_mutex_unlock(&bp->cb_lock);
336 static repcache_client_t *
337 client_lookup(uint32_t id)
339 client_bucket_t *bp = CLIENT_HASH(id);
340 repcache_client_t *cp;
342 (void) pthread_mutex_lock(&bp->cb_lock);
344 cp = uu_list_find(bp->cb_list, &id, NULL, NULL);
347 * Bump the reference count
349 if (cp != NULL) {
350 (void) pthread_mutex_lock(&cp->rc_lock);
351 assert(!(cp->rc_flags & RC_CLIENT_DEAD));
352 cp->rc_refcnt++;
353 (void) pthread_mutex_unlock(&cp->rc_lock);
355 (void) pthread_mutex_unlock(&bp->cb_lock);
357 return (cp);
360 static void
361 client_release(repcache_client_t *cp)
363 (void) pthread_mutex_lock(&cp->rc_lock);
364 assert(cp->rc_refcnt > 0);
365 assert(cp->rc_insert_thr != pthread_self());
367 --cp->rc_refcnt;
368 (void) pthread_cond_broadcast(&cp->rc_cv);
369 (void) pthread_mutex_unlock(&cp->rc_lock);
373 * We only allow one thread to be inserting at a time, to prevent
374 * insert/insert races.
376 static void
377 client_start_insert(repcache_client_t *cp)
379 (void) pthread_mutex_lock(&cp->rc_lock);
380 assert(cp->rc_refcnt > 0);
382 while (cp->rc_insert_thr != 0) {
383 assert(cp->rc_insert_thr != pthread_self());
384 (void) pthread_cond_wait(&cp->rc_cv, &cp->rc_lock);
386 cp->rc_insert_thr = pthread_self();
387 (void) pthread_mutex_unlock(&cp->rc_lock);
390 static void
391 client_end_insert(repcache_client_t *cp)
393 (void) pthread_mutex_lock(&cp->rc_lock);
394 assert(cp->rc_insert_thr == pthread_self());
395 cp->rc_insert_thr = 0;
396 (void) pthread_cond_broadcast(&cp->rc_cv);
397 (void) pthread_mutex_unlock(&cp->rc_lock);
400 /*ARGSUSED*/
401 static repcache_entity_t *
402 entity_alloc(repcache_client_t *cp)
404 repcache_entity_t *ep = uu_zalloc(sizeof (repcache_entity_t));
405 if (ep != NULL) {
406 uu_avl_node_init(ep, &ep->re_link, entity_pool);
408 return (ep);
411 static void
412 entity_add(repcache_client_t *cp, repcache_entity_t *ep)
414 uu_avl_index_t idx;
416 (void) pthread_mutex_lock(&cp->rc_lock);
417 assert(cp->rc_insert_thr == pthread_self());
419 (void) uu_avl_find(cp->rc_entities, ep, NULL, &idx);
420 uu_avl_insert(cp->rc_entities, ep, idx);
422 (void) pthread_mutex_unlock(&cp->rc_lock);
425 static repcache_entity_t *
426 entity_find(repcache_client_t *cp, uint32_t id)
428 repcache_entity_t *ep;
430 (void) pthread_mutex_lock(&cp->rc_lock);
431 ep = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
432 if (ep != NULL) {
433 add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, ep);
434 (void) pthread_mutex_lock(&ep->re_lock);
436 (void) pthread_mutex_unlock(&cp->rc_lock);
438 return (ep);
442 * Fails with
443 * _DUPLICATE_ID - the ids are equal
444 * _UNKNOWN_ID - an id does not designate an active register
446 static int
447 entity_find2(repcache_client_t *cp, uint32_t id1, repcache_entity_t **out1,
448 uint32_t id2, repcache_entity_t **out2)
450 repcache_entity_t *e1, *e2;
451 request_log_entry_t *rlp;
453 if (id1 == id2)
454 return (REP_PROTOCOL_FAIL_DUPLICATE_ID);
456 (void) pthread_mutex_lock(&cp->rc_lock);
457 e1 = uu_avl_find(cp->rc_entities, &id1, NULL, NULL);
458 e2 = uu_avl_find(cp->rc_entities, &id2, NULL, NULL);
459 if (e1 == NULL || e2 == NULL) {
460 (void) pthread_mutex_unlock(&cp->rc_lock);
461 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
464 assert(e1 != e2);
467 * locks are ordered by id number
469 if (id1 < id2) {
470 (void) pthread_mutex_lock(&e1->re_lock);
471 (void) pthread_mutex_lock(&e2->re_lock);
472 } else {
473 (void) pthread_mutex_lock(&e2->re_lock);
474 (void) pthread_mutex_lock(&e1->re_lock);
476 *out1 = e1;
477 *out2 = e2;
479 (void) pthread_mutex_unlock(&cp->rc_lock);
481 if ((rlp = get_log()) != NULL) {
482 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, id1, e1);
483 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, id2, e2);
486 return (REP_PROTOCOL_SUCCESS);
489 static void
490 entity_release(repcache_entity_t *ep)
492 assert(ep->re_node.rnp_node == NULL ||
493 !MUTEX_HELD(&ep->re_node.rnp_node->rn_lock));
494 (void) pthread_mutex_unlock(&ep->re_lock);
497 static void
498 entity_destroy(repcache_entity_t *entity)
500 (void) pthread_mutex_lock(&entity->re_lock);
501 rc_node_clear(&entity->re_node, 0);
502 (void) pthread_mutex_unlock(&entity->re_lock);
504 uu_avl_node_fini(entity, &entity->re_link, entity_pool);
505 (void) pthread_mutex_destroy(&entity->re_lock);
506 rc_node_ptr_free_mem(&entity->re_node);
507 uu_free(entity);
510 static void
511 entity_remove(repcache_client_t *cp, uint32_t id)
513 repcache_entity_t *entity;
515 (void) pthread_mutex_lock(&cp->rc_lock);
516 entity = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
517 if (entity != NULL) {
518 add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, entity);
520 uu_avl_remove(cp->rc_entities, entity);
522 (void) pthread_mutex_unlock(&cp->rc_lock);
524 if (entity != NULL)
525 entity_destroy(entity);
528 static void
529 entity_cleanup(repcache_client_t *cp)
531 repcache_entity_t *ep;
532 void *cookie = NULL;
534 (void) pthread_mutex_lock(&cp->rc_lock);
535 while ((ep = uu_avl_teardown(cp->rc_entities, &cookie)) != NULL) {
536 (void) pthread_mutex_unlock(&cp->rc_lock);
537 entity_destroy(ep);
538 (void) pthread_mutex_lock(&cp->rc_lock);
540 (void) pthread_mutex_unlock(&cp->rc_lock);
543 /*ARGSUSED*/
544 static repcache_iter_t *
545 iter_alloc(repcache_client_t *cp)
547 repcache_iter_t *iter;
548 iter = uu_zalloc(sizeof (repcache_iter_t));
549 if (iter != NULL)
550 uu_avl_node_init(iter, &iter->ri_link, iter_pool);
551 return (iter);
554 static void
555 iter_add(repcache_client_t *cp, repcache_iter_t *iter)
557 uu_list_index_t idx;
559 (void) pthread_mutex_lock(&cp->rc_lock);
560 assert(cp->rc_insert_thr == pthread_self());
562 (void) uu_avl_find(cp->rc_iters, iter, NULL, &idx);
563 uu_avl_insert(cp->rc_iters, iter, idx);
565 (void) pthread_mutex_unlock(&cp->rc_lock);
568 static repcache_iter_t *
569 iter_find(repcache_client_t *cp, uint32_t id)
571 repcache_iter_t *iter;
573 (void) pthread_mutex_lock(&cp->rc_lock);
575 iter = uu_avl_find(cp->rc_iters, &id, NULL, NULL);
576 if (iter != NULL) {
577 add_log_ptr(get_log(), RC_PTR_TYPE_ITER, id, iter);
578 (void) pthread_mutex_lock(&iter->ri_lock);
580 (void) pthread_mutex_unlock(&cp->rc_lock);
582 return (iter);
586 * Fails with
587 * _UNKNOWN_ID - iter_id or entity_id does not designate an active register
589 static int
590 iter_find_w_entity(repcache_client_t *cp, uint32_t iter_id,
591 repcache_iter_t **iterp, uint32_t entity_id, repcache_entity_t **epp)
593 repcache_iter_t *iter;
594 repcache_entity_t *ep;
595 request_log_entry_t *rlp;
597 (void) pthread_mutex_lock(&cp->rc_lock);
598 iter = uu_avl_find(cp->rc_iters, &iter_id, NULL, NULL);
599 ep = uu_avl_find(cp->rc_entities, &entity_id, NULL, NULL);
601 assert(iter == NULL || !MUTEX_HELD(&iter->ri_lock));
602 assert(ep == NULL || !MUTEX_HELD(&ep->re_lock));
604 if (iter == NULL || ep == NULL) {
605 (void) pthread_mutex_unlock(&cp->rc_lock);
606 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
609 (void) pthread_mutex_lock(&iter->ri_lock);
610 (void) pthread_mutex_lock(&ep->re_lock);
612 (void) pthread_mutex_unlock(&cp->rc_lock);
614 *iterp = iter;
615 *epp = ep;
617 if ((rlp = get_log()) != NULL) {
618 add_log_ptr(rlp, RC_PTR_TYPE_ENTITY, entity_id, ep);
619 add_log_ptr(rlp, RC_PTR_TYPE_ITER, iter_id, iter);
622 return (REP_PROTOCOL_SUCCESS);
625 static void
626 iter_release(repcache_iter_t *iter)
628 (void) pthread_mutex_unlock(&iter->ri_lock);
631 static void
632 iter_destroy(repcache_iter_t *iter)
634 (void) pthread_mutex_lock(&iter->ri_lock);
635 rc_iter_destroy(&iter->ri_iter);
636 (void) pthread_mutex_unlock(&iter->ri_lock);
638 uu_avl_node_fini(iter, &iter->ri_link, iter_pool);
639 (void) pthread_mutex_destroy(&iter->ri_lock);
640 uu_free(iter);
643 static void
644 iter_remove(repcache_client_t *cp, uint32_t id)
646 repcache_iter_t *iter;
648 (void) pthread_mutex_lock(&cp->rc_lock);
649 iter = uu_avl_find(cp->rc_iters, &id, NULL, NULL);
650 if (iter != NULL)
651 uu_avl_remove(cp->rc_iters, iter);
652 (void) pthread_mutex_unlock(&cp->rc_lock);
654 if (iter != NULL)
655 iter_destroy(iter);
658 static void
659 iter_cleanup(repcache_client_t *cp)
661 repcache_iter_t *iter;
662 void *cookie = NULL;
664 (void) pthread_mutex_lock(&cp->rc_lock);
665 while ((iter = uu_avl_teardown(cp->rc_iters, &cookie)) != NULL) {
666 (void) pthread_mutex_unlock(&cp->rc_lock);
667 iter_destroy(iter);
668 (void) pthread_mutex_lock(&cp->rc_lock);
670 (void) pthread_mutex_unlock(&cp->rc_lock);
674 * Ensure that the passed client id is no longer usable, wait for any
675 * outstanding invocations to complete, then destroy the client
676 * structure.
678 static void
679 client_destroy(uint32_t id)
681 client_bucket_t *bp = CLIENT_HASH(id);
682 repcache_client_t *cp;
684 (void) pthread_mutex_lock(&bp->cb_lock);
686 cp = uu_list_find(bp->cb_list, &id, NULL, NULL);
688 if (cp == NULL) {
689 (void) pthread_mutex_unlock(&bp->cb_lock);
690 return;
693 uu_list_remove(bp->cb_list, cp);
695 (void) pthread_mutex_unlock(&bp->cb_lock);
697 /* kick the waiters out */
698 rc_notify_info_fini(&cp->rc_notify_info);
700 (void) pthread_mutex_lock(&cp->rc_lock);
701 assert(!(cp->rc_flags & RC_CLIENT_DEAD));
702 cp->rc_flags |= RC_CLIENT_DEAD;
704 if (cp->rc_doorfd != -1) {
705 if (door_revoke(cp->rc_doorfd) < 0)
706 perror("door_revoke");
707 cp->rc_doorfd = -1;
708 cp->rc_doorid = INVALID_DOORID;
711 while (cp->rc_refcnt > 0)
712 (void) pthread_cond_wait(&cp->rc_cv, &cp->rc_lock);
714 assert(cp->rc_insert_thr == 0 && cp->rc_notify_thr == 0);
715 (void) pthread_mutex_unlock(&cp->rc_lock);
718 * destroy outstanding objects
720 entity_cleanup(cp);
721 iter_cleanup(cp);
724 * clean up notifications
726 rc_pg_notify_fini(&cp->rc_pg_notify);
729 * clean up annotations
731 if (cp->rc_operation != NULL)
732 free((void *)cp->rc_operation);
733 if (cp->rc_file != NULL)
734 free((void *)cp->rc_file);
737 * End audit session.
739 #ifndef NATIVE_BUILD
740 (void) adt_end_session(cp->rc_adt_session);
741 #endif
743 client_free(cp);
747 * Fails with
748 * _TYPE_MISMATCH - the entity is already set up with a different type
749 * _NO_RESOURCES - out of memory
751 static int
752 entity_setup(repcache_client_t *cp, struct rep_protocol_entity_setup *rpr)
754 repcache_entity_t *ep;
755 uint32_t type;
757 client_start_insert(cp);
759 if ((ep = entity_find(cp, rpr->rpr_entityid)) != NULL) {
760 type = ep->re_type;
761 entity_release(ep);
763 client_end_insert(cp);
765 if (type != rpr->rpr_entitytype)
766 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
767 return (REP_PROTOCOL_SUCCESS);
770 switch (type = rpr->rpr_entitytype) {
771 case REP_PROTOCOL_ENTITY_SCOPE:
772 case REP_PROTOCOL_ENTITY_SERVICE:
773 case REP_PROTOCOL_ENTITY_INSTANCE:
774 case REP_PROTOCOL_ENTITY_SNAPSHOT:
775 case REP_PROTOCOL_ENTITY_SNAPLEVEL:
776 case REP_PROTOCOL_ENTITY_PROPERTYGRP:
777 case REP_PROTOCOL_ENTITY_PROPERTY:
778 break;
779 default:
780 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
783 ep = entity_alloc(cp);
784 if (ep == NULL) {
785 client_end_insert(cp);
786 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
789 ep->re_id = rpr->rpr_entityid;
790 ep->re_changeid = INVALID_CHANGEID;
792 ep->re_type = type;
793 rc_node_ptr_init(&ep->re_node);
795 entity_add(cp, ep);
796 client_end_insert(cp);
797 return (REP_PROTOCOL_SUCCESS);
800 /*ARGSUSED*/
801 static void
802 entity_name(repcache_client_t *cp, const void *in, size_t insz, void *out_arg,
803 size_t *outsz, void *arg)
805 const struct rep_protocol_entity_name *rpr = in;
806 struct rep_protocol_name_response *out = out_arg;
807 repcache_entity_t *ep;
808 size_t sz = sizeof (out->rpr_name);
810 assert(*outsz == sizeof (*out));
812 ep = entity_find(cp, rpr->rpr_entityid);
814 if (ep == NULL) {
815 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
816 *outsz = sizeof (out->rpr_response);
817 return;
819 out->rpr_response = rc_node_name(&ep->re_node, out->rpr_name,
820 sz, rpr->rpr_answertype, &sz);
821 entity_release(ep);
824 * If we fail, we only return the response code.
825 * If we succeed, we don't return anything after the '\0' in rpr_name.
827 if (out->rpr_response != REP_PROTOCOL_SUCCESS)
828 *outsz = sizeof (out->rpr_response);
829 else
830 *outsz = offsetof(struct rep_protocol_name_response,
831 rpr_name[sz + 1]);
834 /*ARGSUSED*/
835 static void
836 entity_parent_type(repcache_client_t *cp, const void *in, size_t insz,
837 void *out_arg, size_t *outsz, void *arg)
839 const struct rep_protocol_entity_name *rpr = in;
840 struct rep_protocol_integer_response *out = out_arg;
841 repcache_entity_t *ep;
843 assert(*outsz == sizeof (*out));
845 ep = entity_find(cp, rpr->rpr_entityid);
847 if (ep == NULL) {
848 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
849 *outsz = sizeof (out->rpr_response);
850 return;
853 out->rpr_response = rc_node_parent_type(&ep->re_node, &out->rpr_value);
854 entity_release(ep);
856 if (out->rpr_response != REP_PROTOCOL_SUCCESS)
857 *outsz = sizeof (out->rpr_response);
861 * Fails with
862 * _DUPLICATE_ID - the ids are equal
863 * _UNKNOWN_ID - an id does not designate an active register
864 * _INVALID_TYPE - type is invalid
865 * _TYPE_MISMATCH - np doesn't carry children of type type
866 * _DELETED - np has been deleted
867 * _NOT_FOUND - no child with that name/type combo found
868 * _NO_RESOURCES
869 * _BACKEND_ACCESS
871 static int
872 entity_get_child(repcache_client_t *cp,
873 struct rep_protocol_entity_get_child *rpr)
875 repcache_entity_t *parent, *child;
876 int result;
878 uint32_t parentid = rpr->rpr_entityid;
879 uint32_t childid = rpr->rpr_childid;
881 result = entity_find2(cp, childid, &child, parentid, &parent);
882 if (result != REP_PROTOCOL_SUCCESS)
883 return (result);
885 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
887 result = rc_node_get_child(&parent->re_node, rpr->rpr_name,
888 child->re_type, &child->re_node);
890 entity_release(child);
891 entity_release(parent);
893 return (result);
897 * Returns _FAIL_DUPLICATE_ID, _FAIL_UNKNOWN_ID, _FAIL_NOT_SET, _FAIL_DELETED,
898 * _FAIL_TYPE_MISMATCH, _FAIL_NOT_FOUND (scope has no parent), or _SUCCESS.
899 * Fails with
900 * _DUPLICATE_ID - the ids are equal
901 * _UNKNOWN_ID - an id does not designate an active register
902 * _NOT_SET - child is not set
903 * _DELETED - child has been deleted
904 * _TYPE_MISMATCH - child's parent does not match that of the parent register
905 * _NOT_FOUND - child has no parent (and is a scope)
907 static int
908 entity_get_parent(repcache_client_t *cp, struct rep_protocol_entity_parent *rpr)
910 repcache_entity_t *child, *parent;
911 int result;
913 uint32_t childid = rpr->rpr_entityid;
914 uint32_t outid = rpr->rpr_outid;
916 result = entity_find2(cp, childid, &child, outid, &parent);
917 if (result != REP_PROTOCOL_SUCCESS)
918 return (result);
920 result = rc_node_get_parent(&child->re_node, parent->re_type,
921 &parent->re_node);
923 entity_release(child);
924 entity_release(parent);
926 return (result);
929 static int
930 entity_get(repcache_client_t *cp, struct rep_protocol_entity_get *rpr)
932 repcache_entity_t *ep;
933 int result;
935 ep = entity_find(cp, rpr->rpr_entityid);
937 if (ep == NULL)
938 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
940 switch (rpr->rpr_object) {
941 case RP_ENTITY_GET_INVALIDATE:
942 rc_node_clear(&ep->re_node, 0);
943 result = REP_PROTOCOL_SUCCESS;
944 break;
945 case RP_ENTITY_GET_MOST_LOCAL_SCOPE:
946 result = rc_local_scope(ep->re_type, &ep->re_node);
947 break;
948 default:
949 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
950 break;
953 entity_release(ep);
955 return (result);
958 static int
959 entity_update(repcache_client_t *cp, struct rep_protocol_entity_update *rpr)
961 repcache_entity_t *ep;
962 int result;
964 if (rpr->rpr_changeid == INVALID_CHANGEID)
965 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
967 ep = entity_find(cp, rpr->rpr_entityid);
969 if (ep == NULL)
970 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
972 if (ep->re_changeid == rpr->rpr_changeid) {
973 result = REP_PROTOCOL_DONE;
974 } else {
975 result = rc_node_update(&ep->re_node);
976 if (result == REP_PROTOCOL_DONE)
977 ep->re_changeid = rpr->rpr_changeid;
980 entity_release(ep);
982 return (result);
985 static int
986 entity_reset(repcache_client_t *cp, struct rep_protocol_entity_reset *rpr)
988 repcache_entity_t *ep;
990 ep = entity_find(cp, rpr->rpr_entityid);
991 if (ep == NULL)
992 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
994 rc_node_clear(&ep->re_node, 0);
995 ep->re_txstate = REPCACHE_TX_INIT;
997 entity_release(ep);
998 return (REP_PROTOCOL_SUCCESS);
1002 * Fails with
1003 * _BAD_REQUEST - request has invalid changeid
1004 * rpr_name is invalid
1005 * cannot create children for parent's type of node
1006 * _DUPLICATE_ID - request has duplicate ids
1007 * _UNKNOWN_ID - request has unknown id
1008 * _DELETED - parent has been deleted
1009 * _NOT_SET - parent is reset
1010 * _NOT_APPLICABLE - rpr_childtype is _PROPERTYGRP
1011 * _INVALID_TYPE - parent is corrupt or rpr_childtype is invalid
1012 * _TYPE_MISMATCH - parent cannot have children of type rpr_childtype
1013 * _NO_RESOURCES
1014 * _PERMISSION_DENIED
1015 * _BACKEND_ACCESS
1016 * _BACKEND_READONLY
1017 * _EXISTS - child already exists
1019 static int
1020 entity_create_child(repcache_client_t *cp,
1021 struct rep_protocol_entity_create_child *rpr)
1023 repcache_entity_t *parent;
1024 repcache_entity_t *child;
1026 uint32_t parentid = rpr->rpr_entityid;
1027 uint32_t childid = rpr->rpr_childid;
1029 int result;
1031 if (rpr->rpr_changeid == INVALID_CHANGEID)
1032 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1034 result = entity_find2(cp, parentid, &parent, childid, &child);
1035 if (result != REP_PROTOCOL_SUCCESS)
1036 return (result);
1038 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1040 if (child->re_changeid == rpr->rpr_changeid) {
1041 result = REP_PROTOCOL_SUCCESS;
1042 } else {
1043 result = rc_node_create_child(&parent->re_node,
1044 rpr->rpr_childtype, rpr->rpr_name, &child->re_node);
1045 if (result == REP_PROTOCOL_SUCCESS)
1046 child->re_changeid = rpr->rpr_changeid;
1049 entity_release(parent);
1050 entity_release(child);
1052 return (result);
1055 static int
1056 entity_create_pg(repcache_client_t *cp,
1057 struct rep_protocol_entity_create_pg *rpr)
1059 repcache_entity_t *parent;
1060 repcache_entity_t *child;
1062 uint32_t parentid = rpr->rpr_entityid;
1063 uint32_t childid = rpr->rpr_childid;
1065 int result;
1067 if (rpr->rpr_changeid == INVALID_CHANGEID)
1068 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1070 result = entity_find2(cp, parentid, &parent, childid, &child);
1071 if (result != REP_PROTOCOL_SUCCESS)
1072 return (result);
1074 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1075 rpr->rpr_type[sizeof (rpr->rpr_type) - 1] = 0;
1077 if (child->re_changeid == rpr->rpr_changeid) {
1078 result = REP_PROTOCOL_SUCCESS;
1079 } else {
1080 result = rc_node_create_child_pg(&parent->re_node,
1081 child->re_type, rpr->rpr_name, rpr->rpr_type,
1082 rpr->rpr_flags, &child->re_node);
1083 if (result == REP_PROTOCOL_SUCCESS)
1084 child->re_changeid = rpr->rpr_changeid;
1087 entity_release(parent);
1088 entity_release(child);
1090 return (result);
1093 static int
1094 entity_delete(repcache_client_t *cp,
1095 struct rep_protocol_entity_delete *rpr)
1097 repcache_entity_t *entity;
1099 uint32_t entityid = rpr->rpr_entityid;
1101 int result;
1103 if (rpr->rpr_changeid == INVALID_CHANGEID)
1104 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1106 entity = entity_find(cp, entityid);
1108 if (entity == NULL)
1109 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1111 if (entity->re_changeid == rpr->rpr_changeid) {
1112 result = REP_PROTOCOL_SUCCESS;
1113 } else {
1114 result = rc_node_delete(&entity->re_node);
1115 if (result == REP_PROTOCOL_SUCCESS)
1116 entity->re_changeid = rpr->rpr_changeid;
1119 entity_release(entity);
1121 return (result);
1124 static rep_protocol_responseid_t
1125 entity_teardown(repcache_client_t *cp, struct rep_protocol_entity_teardown *rpr)
1127 entity_remove(cp, rpr->rpr_entityid);
1129 return (REP_PROTOCOL_SUCCESS);
1133 * Fails with
1134 * _MISORDERED - the iterator exists and is not reset
1135 * _NO_RESOURCES - out of memory
1137 static int
1138 iter_setup(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1140 repcache_iter_t *iter;
1141 uint32_t sequence;
1143 client_start_insert(cp);
1145 * If the iter already exists, and hasn't been read from,
1146 * we assume the previous call succeeded.
1148 if ((iter = iter_find(cp, rpr->rpr_iterid)) != NULL) {
1149 sequence = iter->ri_sequence;
1150 iter_release(iter);
1152 client_end_insert(cp);
1154 if (sequence != 0)
1155 return (REP_PROTOCOL_FAIL_MISORDERED);
1156 return (REP_PROTOCOL_SUCCESS);
1159 iter = iter_alloc(cp);
1160 if (iter == NULL) {
1161 client_end_insert(cp);
1162 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1165 iter->ri_id = rpr->rpr_iterid;
1166 iter->ri_type = REP_PROTOCOL_TYPE_INVALID;
1167 iter->ri_sequence = 0;
1168 iter_add(cp, iter);
1170 client_end_insert(cp);
1171 return (REP_PROTOCOL_SUCCESS);
1175 * Fails with
1176 * _UNKNOWN_ID
1177 * _MISORDERED - iterator has already been started
1178 * _NOT_SET
1179 * _DELETED
1180 * _TYPE_MISMATCH - entity cannot have type children
1181 * _BAD_REQUEST - rpr_flags is invalid
1182 * rpr_pattern is invalid
1183 * _NO_RESOURCES
1184 * _INVALID_TYPE
1185 * _BACKEND_ACCESS
1187 static int
1188 iter_start(repcache_client_t *cp, struct rep_protocol_iter_start *rpr)
1190 int result;
1191 repcache_iter_t *iter;
1192 repcache_entity_t *ep;
1194 result = iter_find_w_entity(cp, rpr->rpr_iterid, &iter,
1195 rpr->rpr_entity, &ep);
1197 if (result != REP_PROTOCOL_SUCCESS)
1198 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1200 if (iter->ri_sequence > 1) {
1201 result = REP_PROTOCOL_FAIL_MISORDERED;
1202 goto end;
1205 if (iter->ri_sequence == 1) {
1206 result = REP_PROTOCOL_SUCCESS;
1207 goto end;
1210 rpr->rpr_pattern[sizeof (rpr->rpr_pattern) - 1] = 0;
1212 result = rc_node_setup_iter(&ep->re_node, &iter->ri_iter,
1213 rpr->rpr_itertype, rpr->rpr_flags, rpr->rpr_pattern);
1215 if (result == REP_PROTOCOL_SUCCESS)
1216 iter->ri_sequence++;
1218 end:
1219 iter_release(iter);
1220 entity_release(ep);
1221 return (result);
1225 * Returns
1226 * _UNKNOWN_ID
1227 * _NOT_SET - iter has not been started
1228 * _MISORDERED
1229 * _BAD_REQUEST - iter walks values
1230 * _TYPE_MISMATCH - iter does not walk type entities
1231 * _DELETED - parent was deleted
1232 * _NO_RESOURCES
1233 * _INVALID_TYPE - type is invalid
1234 * _DONE
1235 * _SUCCESS
1237 * For composed property group iterators, can also return
1238 * _TYPE_MISMATCH - parent cannot have type children
1239 * _BACKEND_ACCESS
1241 static rep_protocol_responseid_t
1242 iter_read(repcache_client_t *cp, struct rep_protocol_iter_read *rpr)
1244 rep_protocol_responseid_t result;
1245 repcache_iter_t *iter;
1246 repcache_entity_t *ep;
1247 uint32_t sequence;
1249 result = iter_find_w_entity(cp, rpr->rpr_iterid, &iter,
1250 rpr->rpr_entityid, &ep);
1252 if (result != REP_PROTOCOL_SUCCESS)
1253 return (result);
1255 sequence = rpr->rpr_sequence;
1257 if (iter->ri_sequence == 0) {
1258 iter_release(iter);
1259 entity_release(ep);
1260 return (REP_PROTOCOL_FAIL_NOT_SET);
1263 if (sequence == 1) {
1264 iter_release(iter);
1265 entity_release(ep);
1266 return (REP_PROTOCOL_FAIL_MISORDERED);
1269 if (sequence == iter->ri_sequence) {
1270 iter_release(iter);
1271 entity_release(ep);
1272 return (REP_PROTOCOL_SUCCESS);
1275 if (sequence == iter->ri_sequence + 1) {
1276 result = rc_iter_next(iter->ri_iter, &ep->re_node,
1277 ep->re_type);
1279 if (result == REP_PROTOCOL_SUCCESS)
1280 iter->ri_sequence++;
1282 iter_release(iter);
1283 entity_release(ep);
1285 return (result);
1288 iter_release(iter);
1289 entity_release(ep);
1290 return (REP_PROTOCOL_FAIL_MISORDERED);
1293 /*ARGSUSED*/
1294 static void
1295 iter_read_value(repcache_client_t *cp, const void *in, size_t insz,
1296 void *out_arg, size_t *outsz, void *arg)
1298 const struct rep_protocol_iter_read_value *rpr = in;
1299 struct rep_protocol_value_response *out = out_arg;
1300 rep_protocol_responseid_t result;
1302 repcache_iter_t *iter;
1303 uint32_t sequence;
1304 int repeat;
1306 assert(*outsz == sizeof (*out));
1308 iter = iter_find(cp, rpr->rpr_iterid);
1310 if (iter == NULL) {
1311 result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1312 goto out;
1315 sequence = rpr->rpr_sequence;
1317 if (iter->ri_sequence == 0) {
1318 iter_release(iter);
1319 result = REP_PROTOCOL_FAIL_NOT_SET;
1320 goto out;
1323 repeat = (sequence == iter->ri_sequence);
1325 if (sequence == 1 || (!repeat && sequence != iter->ri_sequence + 1)) {
1326 iter_release(iter);
1327 result = REP_PROTOCOL_FAIL_MISORDERED;
1328 goto out;
1331 result = rc_iter_next_value(iter->ri_iter, out, outsz, repeat);
1333 if (!repeat && result == REP_PROTOCOL_SUCCESS)
1334 iter->ri_sequence++;
1336 iter_release(iter);
1338 out:
1340 * If we fail, we only return the response code.
1341 * If we succeed, rc_iter_next_value has shortened *outsz
1342 * to only include the value bytes needed.
1344 if (result != REP_PROTOCOL_SUCCESS && result != REP_PROTOCOL_DONE)
1345 *outsz = sizeof (out->rpr_response);
1347 out->rpr_response = result;
1350 static int
1351 iter_reset(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1353 repcache_iter_t *iter = iter_find(cp, rpr->rpr_iterid);
1355 if (iter == NULL)
1356 return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
1358 if (iter->ri_sequence != 0) {
1359 iter->ri_sequence = 0;
1360 rc_iter_destroy(&iter->ri_iter);
1362 iter_release(iter);
1363 return (REP_PROTOCOL_SUCCESS);
1366 static rep_protocol_responseid_t
1367 iter_teardown(repcache_client_t *cp, struct rep_protocol_iter_request *rpr)
1369 iter_remove(cp, rpr->rpr_iterid);
1371 return (REP_PROTOCOL_SUCCESS);
1374 static rep_protocol_responseid_t
1375 tx_start(repcache_client_t *cp, struct rep_protocol_transaction_start *rpr)
1377 repcache_entity_t *tx;
1378 repcache_entity_t *ep;
1379 rep_protocol_responseid_t result;
1381 uint32_t txid = rpr->rpr_entityid_tx;
1382 uint32_t epid = rpr->rpr_entityid;
1384 result = entity_find2(cp, txid, &tx, epid, &ep);
1385 if (result != REP_PROTOCOL_SUCCESS)
1386 return (result);
1388 if (tx->re_txstate == REPCACHE_TX_SETUP) {
1389 result = REP_PROTOCOL_SUCCESS;
1390 goto end;
1392 if (tx->re_txstate != REPCACHE_TX_INIT) {
1393 result = REP_PROTOCOL_FAIL_MISORDERED;
1394 goto end;
1397 result = rc_node_setup_tx(&ep->re_node, &tx->re_node);
1399 end:
1400 if (result == REP_PROTOCOL_SUCCESS)
1401 tx->re_txstate = REPCACHE_TX_SETUP;
1402 else
1403 rc_node_clear(&tx->re_node, 0);
1405 entity_release(ep);
1406 entity_release(tx);
1407 return (result);
1410 /*ARGSUSED*/
1411 static void
1412 tx_commit(repcache_client_t *cp, const void *in, size_t insz,
1413 void *out_arg, size_t *outsz, void *arg)
1415 struct rep_protocol_response *out = out_arg;
1416 const struct rep_protocol_transaction_commit *rpr = in;
1417 repcache_entity_t *tx;
1419 assert(*outsz == sizeof (*out));
1420 assert(insz >= REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE);
1422 if (rpr->rpr_size != insz) {
1423 out->rpr_response = REP_PROTOCOL_FAIL_BAD_REQUEST;
1424 return;
1427 tx = entity_find(cp, rpr->rpr_entityid);
1429 if (tx == NULL) {
1430 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1431 return;
1434 switch (tx->re_txstate) {
1435 case REPCACHE_TX_INIT:
1436 out->rpr_response = REP_PROTOCOL_FAIL_MISORDERED;
1437 break;
1439 case REPCACHE_TX_SETUP:
1440 out->rpr_response = rc_tx_commit(&tx->re_node, rpr->rpr_cmd,
1441 insz - REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE);
1443 if (out->rpr_response == REP_PROTOCOL_SUCCESS) {
1444 tx->re_txstate = REPCACHE_TX_COMMITTED;
1445 rc_node_clear(&tx->re_node, 0);
1448 break;
1449 case REPCACHE_TX_COMMITTED:
1450 out->rpr_response = REP_PROTOCOL_SUCCESS;
1451 break;
1452 default:
1453 assert(0); /* CAN'T HAPPEN */
1454 break;
1457 entity_release(tx);
1460 static rep_protocol_responseid_t
1461 next_snaplevel(repcache_client_t *cp, struct rep_protocol_entity_pair *rpr)
1463 repcache_entity_t *src;
1464 repcache_entity_t *dest;
1466 uint32_t srcid = rpr->rpr_entity_src;
1467 uint32_t destid = rpr->rpr_entity_dst;
1469 int result;
1471 result = entity_find2(cp, srcid, &src, destid, &dest);
1472 if (result != REP_PROTOCOL_SUCCESS)
1473 return (result);
1475 result = rc_node_next_snaplevel(&src->re_node, &dest->re_node);
1477 entity_release(src);
1478 entity_release(dest);
1480 return (result);
1483 static rep_protocol_responseid_t
1484 snapshot_take(repcache_client_t *cp, struct rep_protocol_snapshot_take *rpr)
1486 repcache_entity_t *src;
1487 uint32_t srcid = rpr->rpr_entityid_src;
1488 repcache_entity_t *dest;
1489 uint32_t destid = rpr->rpr_entityid_dest;
1491 int result;
1493 result = entity_find2(cp, srcid, &src, destid, &dest);
1494 if (result != REP_PROTOCOL_SUCCESS)
1495 return (result);
1497 if (dest->re_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
1498 result = REP_PROTOCOL_FAIL_TYPE_MISMATCH;
1499 } else {
1500 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1502 if (rpr->rpr_flags == REP_SNAPSHOT_NEW)
1503 result = rc_snapshot_take_new(&src->re_node, NULL,
1504 NULL, rpr->rpr_name, &dest->re_node);
1505 else if (rpr->rpr_flags == REP_SNAPSHOT_ATTACH &&
1506 rpr->rpr_name[0] == 0)
1507 result = rc_snapshot_take_attach(&src->re_node,
1508 &dest->re_node);
1509 else
1510 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
1512 entity_release(src);
1513 entity_release(dest);
1515 return (result);
1518 static rep_protocol_responseid_t
1519 snapshot_take_named(repcache_client_t *cp,
1520 struct rep_protocol_snapshot_take_named *rpr)
1522 repcache_entity_t *src;
1523 uint32_t srcid = rpr->rpr_entityid_src;
1524 repcache_entity_t *dest;
1525 uint32_t destid = rpr->rpr_entityid_dest;
1527 int result;
1529 result = entity_find2(cp, srcid, &src, destid, &dest);
1530 if (result != REP_PROTOCOL_SUCCESS)
1531 return (result);
1533 if (dest->re_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
1534 result = REP_PROTOCOL_FAIL_TYPE_MISMATCH;
1535 } else {
1536 rpr->rpr_svcname[sizeof (rpr->rpr_svcname) - 1] = 0;
1537 rpr->rpr_instname[sizeof (rpr->rpr_instname) - 1] = 0;
1538 rpr->rpr_name[sizeof (rpr->rpr_name) - 1] = 0;
1540 result = rc_snapshot_take_new(&src->re_node, rpr->rpr_svcname,
1541 rpr->rpr_instname, rpr->rpr_name, &dest->re_node);
1543 entity_release(src);
1544 entity_release(dest);
1546 return (result);
1549 static rep_protocol_responseid_t
1550 snapshot_attach(repcache_client_t *cp, struct rep_protocol_snapshot_attach *rpr)
1552 repcache_entity_t *src;
1553 uint32_t srcid = rpr->rpr_entityid_src;
1554 repcache_entity_t *dest;
1555 uint32_t destid = rpr->rpr_entityid_dest;
1557 int result;
1559 result = entity_find2(cp, srcid, &src, destid, &dest);
1560 if (result != REP_PROTOCOL_SUCCESS)
1561 return (result);
1563 result = rc_snapshot_attach(&src->re_node, &dest->re_node);
1565 entity_release(src);
1566 entity_release(dest);
1568 return (result);
1571 /*ARGSUSED*/
1572 static void
1573 property_get_type(repcache_client_t *cp, const void *in, size_t insz,
1574 void *out_arg, size_t *outsz, void *arg)
1576 const struct rep_protocol_property_request *rpr = in;
1577 struct rep_protocol_integer_response *out = out_arg;
1578 repcache_entity_t *ep;
1579 rep_protocol_value_type_t t = 0;
1581 assert(*outsz == sizeof (*out));
1583 ep = entity_find(cp, rpr->rpr_entityid);
1585 if (ep == NULL) {
1586 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1587 *outsz = sizeof (out->rpr_response);
1588 return;
1591 out->rpr_response = rc_node_get_property_type(&ep->re_node, &t);
1593 entity_release(ep);
1595 if (out->rpr_response != REP_PROTOCOL_SUCCESS)
1596 *outsz = sizeof (out->rpr_response);
1597 else
1598 out->rpr_value = t;
1602 * Fails with:
1603 * _UNKNOWN_ID - an id does not designate an active register
1604 * _NOT_SET - The property is not set
1605 * _DELETED - The property has been deleted
1606 * _TYPE_MISMATCH - The object is not a property
1607 * _NOT_FOUND - The property has no values.
1609 * Succeeds with:
1610 * _SUCCESS - The property has 1 value.
1611 * _TRUNCATED - The property has >1 value.
1613 /*ARGSUSED*/
1614 static void
1615 property_get_value(repcache_client_t *cp, const void *in, size_t insz,
1616 void *out_arg, size_t *outsz, void *arg)
1618 const struct rep_protocol_property_request *rpr = in;
1619 struct rep_protocol_value_response *out = out_arg;
1620 repcache_entity_t *ep;
1622 assert(*outsz == sizeof (*out));
1624 ep = entity_find(cp, rpr->rpr_entityid);
1625 if (ep == NULL) {
1626 out->rpr_response = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1627 *outsz = sizeof (out->rpr_response);
1628 return;
1631 out->rpr_response = rc_node_get_property_value(&ep->re_node, out,
1632 outsz);
1634 entity_release(ep);
1637 * If we fail, we only return the response code.
1638 * If we succeed, rc_node_get_property_value has shortened *outsz
1639 * to only include the value bytes needed.
1641 if (out->rpr_response != REP_PROTOCOL_SUCCESS &&
1642 out->rpr_response != REP_PROTOCOL_FAIL_TRUNCATED)
1643 *outsz = sizeof (out->rpr_response);
1646 static rep_protocol_responseid_t
1647 propertygrp_notify(repcache_client_t *cp,
1648 struct rep_protocol_propertygrp_request *rpr, int *out_fd)
1650 int fds[2];
1651 int ours, theirs;
1653 rep_protocol_responseid_t result;
1654 repcache_entity_t *ep;
1656 if (pipe(fds) < 0)
1657 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1659 ours = fds[0];
1660 theirs = fds[1];
1662 if ((ep = entity_find(cp, rpr->rpr_entityid)) == NULL) {
1663 result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1664 goto fail;
1668 * While the following can race with other threads setting up a
1669 * notification, the worst that can happen is that our fd has
1670 * already been closed before we return.
1672 result = rc_pg_notify_setup(&cp->rc_pg_notify, &ep->re_node,
1673 ours);
1675 entity_release(ep);
1677 if (result != REP_PROTOCOL_SUCCESS)
1678 goto fail;
1680 *out_fd = theirs;
1681 return (REP_PROTOCOL_SUCCESS);
1683 fail:
1684 (void) close(ours);
1685 (void) close(theirs);
1687 return (result);
1690 static rep_protocol_responseid_t
1691 client_add_notify(repcache_client_t *cp,
1692 struct rep_protocol_notify_request *rpr)
1694 rpr->rpr_pattern[sizeof (rpr->rpr_pattern) - 1] = 0;
1696 switch (rpr->rpr_type) {
1697 case REP_PROTOCOL_NOTIFY_PGNAME:
1698 return (rc_notify_info_add_name(&cp->rc_notify_info,
1699 rpr->rpr_pattern));
1701 case REP_PROTOCOL_NOTIFY_PGTYPE:
1702 return (rc_notify_info_add_type(&cp->rc_notify_info,
1703 rpr->rpr_pattern));
1705 default:
1706 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1710 /*ARGSUSED*/
1711 static void
1712 client_wait(repcache_client_t *cp, const void *in, size_t insz,
1713 void *out_arg, size_t *outsz, void *arg)
1715 int result;
1716 repcache_entity_t *ep;
1717 const struct rep_protocol_wait_request *rpr = in;
1718 struct rep_protocol_fmri_response *out = out_arg;
1720 assert(*outsz == sizeof (*out));
1722 (void) pthread_mutex_lock(&cp->rc_lock);
1723 if (cp->rc_notify_thr != 0) {
1724 (void) pthread_mutex_unlock(&cp->rc_lock);
1725 out->rpr_response = REP_PROTOCOL_FAIL_EXISTS;
1726 *outsz = sizeof (out->rpr_response);
1727 return;
1729 cp->rc_notify_thr = pthread_self();
1730 (void) pthread_mutex_unlock(&cp->rc_lock);
1732 result = rc_notify_info_wait(&cp->rc_notify_info, &cp->rc_notify_ptr,
1733 out->rpr_fmri, sizeof (out->rpr_fmri));
1735 if (result == REP_PROTOCOL_SUCCESS) {
1736 if ((ep = entity_find(cp, rpr->rpr_entityid)) != NULL) {
1737 if (ep->re_type == REP_PROTOCOL_ENTITY_PROPERTYGRP) {
1738 rc_node_ptr_assign(&ep->re_node,
1739 &cp->rc_notify_ptr);
1740 } else {
1741 result = REP_PROTOCOL_FAIL_TYPE_MISMATCH;
1743 entity_release(ep);
1744 } else {
1745 result = REP_PROTOCOL_FAIL_UNKNOWN_ID;
1747 rc_node_clear(&cp->rc_notify_ptr, 0);
1750 (void) pthread_mutex_lock(&cp->rc_lock);
1751 assert(cp->rc_notify_thr == pthread_self());
1752 cp->rc_notify_thr = 0;
1753 (void) pthread_mutex_unlock(&cp->rc_lock);
1755 out->rpr_response = result;
1756 if (result != REP_PROTOCOL_SUCCESS)
1757 *outsz = sizeof (out->rpr_response);
1761 * Can return:
1762 * _PERMISSION_DENIED not enough privileges to do request.
1763 * _BAD_REQUEST name is not valid or reserved
1764 * _TRUNCATED name is too long for current repository path
1765 * _UNKNOWN failed for unknown reason (details written to
1766 * console)
1767 * _BACKEND_READONLY backend is not writable
1768 * _NO_RESOURCES out of memory
1769 * _SUCCESS Backup completed successfully.
1771 static rep_protocol_responseid_t
1772 backup_repository(repcache_client_t *cp,
1773 struct rep_protocol_backup_request *rpr)
1775 rep_protocol_responseid_t result;
1776 ucred_t *uc = get_ucred();
1778 if (!client_is_privileged() && (uc == NULL || ucred_geteuid(uc) != 0))
1779 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
1781 rpr->rpr_name[REP_PROTOCOL_NAME_LEN - 1] = 0;
1782 if (strcmp(rpr->rpr_name, REPOSITORY_BOOT_BACKUP) == 0)
1783 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1785 (void) pthread_mutex_lock(&cp->rc_lock);
1786 if (rpr->rpr_changeid != cp->rc_changeid) {
1787 result = backend_create_backup(rpr->rpr_name);
1788 if (result == REP_PROTOCOL_SUCCESS)
1789 cp->rc_changeid = rpr->rpr_changeid;
1790 } else {
1791 result = REP_PROTOCOL_SUCCESS;
1793 (void) pthread_mutex_unlock(&cp->rc_lock);
1795 return (result);
1799 * This function captures the information that will be used for an
1800 * annotation audit event. Specifically, it captures the operation to be
1801 * performed and the name of the file that is being used. These values are
1802 * copied from the rep_protocol_annotation request at rpr to the client
1803 * structure. If both these values are null, the client is turning
1804 * annotation off.
1806 * Fails with
1807 * _NO_RESOURCES - unable to allocate memory
1809 static rep_protocol_responseid_t
1810 set_annotation(repcache_client_t *cp, struct rep_protocol_annotation *rpr)
1812 au_id_t audit_uid;
1813 const char *file = NULL;
1814 const char *old_ptrs[2];
1815 const char *operation = NULL;
1816 rep_protocol_responseid_t rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1817 au_asid_t sessionid;
1819 (void) memset((void *)old_ptrs, 0, sizeof (old_ptrs));
1821 /* Copy rpr_operation and rpr_file if they are not empty strings. */
1822 if (rpr->rpr_operation[0] != 0) {
1824 * Make sure that client did not send us an unterminated buffer.
1826 rpr->rpr_operation[sizeof (rpr->rpr_operation) - 1] = 0;
1827 if ((operation = strdup(rpr->rpr_operation)) == NULL)
1828 goto out;
1830 if (rpr->rpr_file[0] != 0) {
1832 * Make sure that client did not send us an unterminated buffer.
1834 rpr->rpr_file[sizeof (rpr->rpr_file) - 1] = 0;
1835 if ((file = strdup(rpr->rpr_file)) == NULL)
1836 goto out;
1839 (void) pthread_mutex_lock(&cp->rc_annotate_lock);
1840 /* Save addresses of memory to free when not locked */
1841 old_ptrs[0] = cp->rc_operation;
1842 old_ptrs[1] = cp->rc_file;
1844 /* Save pointers to annotation strings. */
1845 cp->rc_operation = operation;
1846 cp->rc_file = file;
1849 * Set annotation flag. Annotations should be turned on if either
1850 * operation or file are not NULL.
1852 cp->rc_annotate = (operation != NULL) || (file != NULL);
1853 (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1856 * operation and file pointers are saved in cp, so don't free them
1857 * during cleanup.
1859 operation = NULL;
1860 file = NULL;
1861 rc = REP_PROTOCOL_SUCCESS;
1864 * Native builds are done to create svc.configd-native. This
1865 * program runs only on the Open Solaris build machines to create
1866 * the seed repository. Until the SMF auditing code is distributed
1867 * to the Open Solaris build machines, adt_get_unique_id() in the
1868 * following code is not a global function in libbsm. Hence the
1869 * following conditional compilation.
1871 #ifndef NATIVE_BUILD
1873 * Set the appropriate audit session id.
1875 if (cp->rc_annotate) {
1877 * We're starting a group of annotated audit events, so
1878 * create and set an audit session ID for this annotation.
1880 adt_get_auid(cp->rc_adt_session, &audit_uid);
1881 sessionid = adt_get_unique_id(audit_uid);
1882 } else {
1884 * Annotation is done so restore our client audit session
1885 * id.
1887 sessionid = cp->rc_adt_sessionid;
1889 adt_set_asid(cp->rc_adt_session, sessionid);
1890 #endif /* NATIVE_BUILD */
1892 out:
1893 if (operation != NULL)
1894 free((void *)operation);
1895 if (file != NULL)
1896 free((void *)file);
1897 free((void *)old_ptrs[0]);
1898 free((void *)old_ptrs[1]);
1899 return (rc);
1903 * Determine if an annotation event needs to be generated. If it does
1904 * provide the operation and file name that should be used in the event.
1906 * Can return:
1907 * 0 No annotation event needed or buffers are not large
1908 * enough. Either way an event should not be
1909 * generated.
1910 * 1 Generate annotation event.
1913 client_annotation_needed(char *operation, size_t oper_sz,
1914 char *file, size_t file_sz)
1916 thread_info_t *ti = thread_self();
1917 repcache_client_t *cp = ti->ti_active_client;
1918 int rc = 0;
1920 (void) pthread_mutex_lock(&cp->rc_annotate_lock);
1921 if (cp->rc_annotate) {
1922 rc = 1;
1923 if (cp->rc_operation == NULL) {
1924 if (oper_sz > 0)
1925 operation[0] = 0;
1926 } else {
1927 if (strlcpy(operation, cp->rc_operation, oper_sz) >=
1928 oper_sz) {
1929 /* Buffer overflow, so do not generate event */
1930 rc = 0;
1933 if (cp->rc_file == NULL) {
1934 if (file_sz > 0)
1935 file[0] = 0;
1936 } else if (rc == 1) {
1937 if (strlcpy(file, cp->rc_file, file_sz) >= file_sz) {
1938 /* Buffer overflow, so do not generate event */
1939 rc = 0;
1943 (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1944 return (rc);
1947 void
1948 client_annotation_finished()
1950 thread_info_t *ti = thread_self();
1951 repcache_client_t *cp = ti->ti_active_client;
1953 (void) pthread_mutex_lock(&cp->rc_annotate_lock);
1954 cp->rc_annotate = 0;
1955 (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
1958 #ifndef NATIVE_BUILD
1959 static void
1960 start_audit_session(repcache_client_t *cp)
1962 ucred_t *cred = NULL;
1963 adt_session_data_t *session;
1966 * A NULL session pointer value can legally be used in all
1967 * subsequent calls to adt_* functions.
1969 cp->rc_adt_session = NULL;
1971 if (!adt_audit_state(AUC_AUDITING))
1972 return;
1974 if (door_ucred(&cred) != 0) {
1975 switch (errno) {
1976 case EAGAIN:
1977 case ENOMEM:
1978 syslog(LOG_ERR, gettext("start_audit_session(): cannot "
1979 "get ucred. %m\n"));
1980 return;
1981 case EINVAL:
1983 * Door client went away. This is a normal,
1984 * although infrequent event, so there is no need
1985 * to create a syslog message.
1987 return;
1988 case EFAULT:
1989 default:
1990 bad_error("door_ucred", errno);
1991 return;
1994 if (adt_start_session(&session, NULL, 0) != 0) {
1995 syslog(LOG_ERR, gettext("start_audit_session(): could not "
1996 "start audit session.\n"));
1997 ucred_free(cred);
1998 return;
2000 if (adt_set_from_ucred(session, cred, ADT_NEW) != 0) {
2001 syslog(LOG_ERR, gettext("start_audit_session(): cannot set "
2002 "audit session data from ucred\n"));
2003 /* Something went wrong. End the session. */
2004 (void) adt_end_session(session);
2005 ucred_free(cred);
2006 return;
2009 /* All went well. Save the session data and session ID */
2010 cp->rc_adt_session = session;
2011 adt_get_asid(session, &cp->rc_adt_sessionid);
2013 ucred_free(cred);
2015 #endif
2018 * Handle switch client request
2020 * This routine can return:
2022 * _PERMISSION_DENIED not enough privileges to do request.
2023 * _UNKNOWN file operation error (details written to
2024 * the console).
2025 * _SUCCESS switch operation is completed.
2026 * _BACKEND_ACCESS backend access fails.
2027 * _NO_RESOURCES out of memory.
2028 * _BACKEND_READONLY backend is not writable.
2030 static rep_protocol_responseid_t
2031 repository_switch(repcache_client_t *cp,
2032 struct rep_protocol_switch_request *rpr)
2034 rep_protocol_responseid_t result;
2035 ucred_t *uc = get_ucred();
2037 if (!client_is_privileged() && (uc == NULL ||
2038 ucred_geteuid(uc) != 0)) {
2039 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
2042 (void) pthread_mutex_lock(&cp->rc_lock);
2043 if (rpr->rpr_changeid != cp->rc_changeid) {
2044 if ((result = backend_switch(rpr->rpr_flag)) ==
2045 REP_PROTOCOL_SUCCESS)
2046 cp->rc_changeid = rpr->rpr_changeid;
2047 } else {
2048 result = REP_PROTOCOL_SUCCESS;
2050 (void) pthread_mutex_unlock(&cp->rc_lock);
2052 return (result);
2055 typedef rep_protocol_responseid_t protocol_simple_f(repcache_client_t *cp,
2056 const void *rpr);
2058 /*ARGSUSED*/
2059 static void
2060 simple_handler(repcache_client_t *cp, const void *in, size_t insz,
2061 void *out_arg, size_t *outsz, void *arg)
2063 protocol_simple_f *f = (protocol_simple_f *)arg;
2064 rep_protocol_response_t *out = out_arg;
2066 assert(*outsz == sizeof (*out));
2067 assert(f != NULL);
2069 out->rpr_response = (*f)(cp, in);
2072 typedef rep_protocol_responseid_t protocol_simple_fd_f(repcache_client_t *cp,
2073 const void *rpr, int *out_fd);
2075 /*ARGSUSED*/
2076 static void
2077 simple_fd_handler(repcache_client_t *cp, const void *in, size_t insz,
2078 void *out_arg, size_t *outsz, void *arg, int *out_fd)
2080 protocol_simple_fd_f *f = (protocol_simple_fd_f *)arg;
2081 rep_protocol_response_t *out = out_arg;
2083 assert(*outsz == sizeof (*out));
2084 assert(f != NULL);
2086 out->rpr_response = (*f)(cp, in, out_fd);
2089 typedef void protocol_handler_f(repcache_client_t *, const void *in,
2090 size_t insz, void *out, size_t *outsz, void *arg);
2092 typedef void protocol_handler_fdret_f(repcache_client_t *, const void *in,
2093 size_t insz, void *out, size_t *outsz, void *arg, int *fd_out);
2095 #define PROTO(p, f, in) { \
2096 p, #p, simple_handler, (void *)(&f), NULL, \
2097 sizeof (in), sizeof (rep_protocol_response_t), 0 \
2100 #define PROTO_FD_OUT(p, f, in) { \
2101 p, #p, NULL, (void *)(&f), simple_fd_handler, \
2102 sizeof (in), \
2103 sizeof (rep_protocol_response_t), \
2104 PROTO_FLAG_RETFD \
2107 #define PROTO_VARIN(p, f, insz) { \
2108 p, #p, &(f), NULL, NULL, \
2109 insz, sizeof (rep_protocol_response_t), \
2110 PROTO_FLAG_VARINPUT \
2113 #define PROTO_UINT_OUT(p, f, in) { \
2114 p, #p, &(f), NULL, NULL, \
2115 sizeof (in), \
2116 sizeof (struct rep_protocol_integer_response), 0 \
2119 #define PROTO_NAME_OUT(p, f, in) { \
2120 p, #p, &(f), NULL, NULL, \
2121 sizeof (in), \
2122 sizeof (struct rep_protocol_name_response), 0 \
2125 #define PROTO_FMRI_OUT(p, f, in) { \
2126 p, #p, &(f), NULL, NULL, \
2127 sizeof (in), \
2128 sizeof (struct rep_protocol_fmri_response), 0 \
2131 #define PROTO_VALUE_OUT(p, f, in) { \
2132 p, #p, &(f), NULL, NULL, \
2133 sizeof (in), \
2134 sizeof (struct rep_protocol_value_response), 0 \
2137 #define PROTO_PANIC(p) { p, #p, NULL, NULL, NULL, 0, 0, PROTO_FLAG_PANIC }
2138 #define PROTO_END() { 0, NULL, NULL, NULL, NULL, 0, 0, PROTO_FLAG_PANIC }
2140 #define PROTO_FLAG_PANIC 0x00000001 /* should never be called */
2141 #define PROTO_FLAG_VARINPUT 0x00000004 /* in_size is minimum size */
2142 #define PROTO_FLAG_RETFD 0x00000008 /* can also return an FD */
2144 #define PROTO_ALL_FLAGS 0x0000000f /* all flags */
2146 static struct protocol_entry {
2147 enum rep_protocol_requestid pt_request;
2148 const char *pt_name;
2149 protocol_handler_f *pt_handler;
2150 void *pt_arg;
2151 protocol_handler_fdret_f *pt_fd_handler;
2152 size_t pt_in_size;
2153 size_t pt_out_max;
2154 uint32_t pt_flags;
2155 } protocol_table[] = {
2156 PROTO_PANIC(REP_PROTOCOL_CLOSE), /* special case */
2158 PROTO(REP_PROTOCOL_ENTITY_SETUP, entity_setup,
2159 struct rep_protocol_entity_setup),
2160 PROTO_NAME_OUT(REP_PROTOCOL_ENTITY_NAME, entity_name,
2161 struct rep_protocol_entity_name),
2162 PROTO_UINT_OUT(REP_PROTOCOL_ENTITY_PARENT_TYPE, entity_parent_type,
2163 struct rep_protocol_entity_parent_type),
2164 PROTO(REP_PROTOCOL_ENTITY_GET_CHILD, entity_get_child,
2165 struct rep_protocol_entity_get_child),
2166 PROTO(REP_PROTOCOL_ENTITY_GET_PARENT, entity_get_parent,
2167 struct rep_protocol_entity_parent),
2168 PROTO(REP_PROTOCOL_ENTITY_GET, entity_get,
2169 struct rep_protocol_entity_get),
2170 PROTO(REP_PROTOCOL_ENTITY_UPDATE, entity_update,
2171 struct rep_protocol_entity_update),
2172 PROTO(REP_PROTOCOL_ENTITY_CREATE_CHILD, entity_create_child,
2173 struct rep_protocol_entity_create_child),
2174 PROTO(REP_PROTOCOL_ENTITY_CREATE_PG, entity_create_pg,
2175 struct rep_protocol_entity_create_pg),
2176 PROTO(REP_PROTOCOL_ENTITY_DELETE, entity_delete,
2177 struct rep_protocol_entity_delete),
2178 PROTO(REP_PROTOCOL_ENTITY_RESET, entity_reset,
2179 struct rep_protocol_entity_reset),
2180 PROTO(REP_PROTOCOL_ENTITY_TEARDOWN, entity_teardown,
2181 struct rep_protocol_entity_teardown),
2183 PROTO(REP_PROTOCOL_ITER_SETUP, iter_setup,
2184 struct rep_protocol_iter_request),
2185 PROTO(REP_PROTOCOL_ITER_START, iter_start,
2186 struct rep_protocol_iter_start),
2187 PROTO(REP_PROTOCOL_ITER_READ, iter_read,
2188 struct rep_protocol_iter_read),
2189 PROTO_VALUE_OUT(REP_PROTOCOL_ITER_READ_VALUE, iter_read_value,
2190 struct rep_protocol_iter_read_value),
2191 PROTO(REP_PROTOCOL_ITER_RESET, iter_reset,
2192 struct rep_protocol_iter_request),
2193 PROTO(REP_PROTOCOL_ITER_TEARDOWN, iter_teardown,
2194 struct rep_protocol_iter_request),
2196 PROTO(REP_PROTOCOL_NEXT_SNAPLEVEL, next_snaplevel,
2197 struct rep_protocol_entity_pair),
2199 PROTO(REP_PROTOCOL_SNAPSHOT_TAKE, snapshot_take,
2200 struct rep_protocol_snapshot_take),
2201 PROTO(REP_PROTOCOL_SNAPSHOT_TAKE_NAMED, snapshot_take_named,
2202 struct rep_protocol_snapshot_take_named),
2203 PROTO(REP_PROTOCOL_SNAPSHOT_ATTACH, snapshot_attach,
2204 struct rep_protocol_snapshot_attach),
2206 PROTO_UINT_OUT(REP_PROTOCOL_PROPERTY_GET_TYPE, property_get_type,
2207 struct rep_protocol_property_request),
2208 PROTO_VALUE_OUT(REP_PROTOCOL_PROPERTY_GET_VALUE, property_get_value,
2209 struct rep_protocol_property_request),
2211 PROTO_FD_OUT(REP_PROTOCOL_PROPERTYGRP_SETUP_WAIT, propertygrp_notify,
2212 struct rep_protocol_propertygrp_request),
2213 PROTO(REP_PROTOCOL_PROPERTYGRP_TX_START, tx_start,
2214 struct rep_protocol_transaction_start),
2215 PROTO_VARIN(REP_PROTOCOL_PROPERTYGRP_TX_COMMIT, tx_commit,
2216 REP_PROTOCOL_TRANSACTION_COMMIT_MIN_SIZE),
2218 PROTO(REP_PROTOCOL_CLIENT_ADD_NOTIFY, client_add_notify,
2219 struct rep_protocol_notify_request),
2220 PROTO_FMRI_OUT(REP_PROTOCOL_CLIENT_WAIT, client_wait,
2221 struct rep_protocol_wait_request),
2223 PROTO(REP_PROTOCOL_BACKUP, backup_repository,
2224 struct rep_protocol_backup_request),
2226 PROTO(REP_PROTOCOL_SET_AUDIT_ANNOTATION, set_annotation,
2227 struct rep_protocol_annotation),
2229 PROTO(REP_PROTOCOL_SWITCH, repository_switch,
2230 struct rep_protocol_switch_request),
2232 PROTO_END()
2234 #undef PROTO
2235 #undef PROTO_FMRI_OUT
2236 #undef PROTO_NAME_OUT
2237 #undef PROTO_UINT_OUT
2238 #undef PROTO_PANIC
2239 #undef PROTO_END
2242 * The number of entries, sans PROTO_END()
2244 #define PROTOCOL_ENTRIES \
2245 (sizeof (protocol_table) / sizeof (*protocol_table) - 1)
2247 #define PROTOCOL_PREFIX "REP_PROTOCOL_"
2250 client_init(void)
2252 int i;
2253 struct protocol_entry *e;
2255 if (!client_hash_init())
2256 return (0);
2258 if (request_log_size > 0) {
2259 request_log = uu_zalloc(request_log_size *
2260 sizeof (request_log_entry_t));
2264 * update the names to not include REP_PROTOCOL_
2266 for (i = 0; i < PROTOCOL_ENTRIES; i++) {
2267 e = &protocol_table[i];
2268 assert(strncmp(e->pt_name, PROTOCOL_PREFIX,
2269 strlen(PROTOCOL_PREFIX)) == 0);
2270 e->pt_name += strlen(PROTOCOL_PREFIX);
2273 * verify the protocol table is consistent
2275 for (i = 0; i < PROTOCOL_ENTRIES; i++) {
2276 e = &protocol_table[i];
2277 assert(e->pt_request == (REP_PROTOCOL_BASE + i));
2279 assert((e->pt_flags & ~PROTO_ALL_FLAGS) == 0);
2281 if (e->pt_flags & PROTO_FLAG_PANIC)
2282 assert(e->pt_in_size == 0 && e->pt_out_max == 0 &&
2283 e->pt_handler == NULL);
2284 else
2285 assert(e->pt_in_size != 0 && e->pt_out_max != 0 &&
2286 (e->pt_handler != NULL ||
2287 e->pt_fd_handler != NULL));
2289 assert((REP_PROTOCOL_BASE + i) == REP_PROTOCOL_MAX_REQUEST);
2291 assert(protocol_table[i].pt_request == 0);
2293 return (1);
2296 static void
2297 client_switcher(void *cookie, char *argp, size_t arg_size, door_desc_t *desc_in,
2298 uint_t n_desc)
2300 thread_info_t *ti = thread_self();
2302 repcache_client_t *cp;
2303 uint32_t id = (uint32_t)cookie;
2304 enum rep_protocol_requestid request_code;
2306 rep_protocol_responseid_t result = INVALID_RESULT;
2308 struct protocol_entry *e;
2310 char *retval = NULL;
2311 size_t retsize = 0;
2313 int retfd = -1;
2314 door_desc_t desc;
2315 request_log_entry_t *rlp;
2317 rlp = start_log(id);
2319 if (n_desc != 0)
2320 uu_die("can't happen: %d descriptors @%p (cookie %p)",
2321 n_desc, desc_in, cookie);
2323 if (argp == DOOR_UNREF_DATA) {
2324 client_destroy(id);
2325 goto bad_end;
2328 thread_newstate(ti, TI_CLIENT_CALL);
2331 * To simplify returning just a result code, we set up for
2332 * that case here.
2334 retval = (char *)&result;
2335 retsize = sizeof (result);
2337 if (arg_size < sizeof (request_code)) {
2338 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2339 goto end_unheld;
2342 ti->ti_client_request = (void *)argp;
2344 /* LINTED alignment */
2345 request_code = *(uint32_t *)argp;
2347 if (rlp != NULL) {
2348 rlp->rl_request = request_code;
2351 * In order to avoid locking problems on removal, we handle the
2352 * "close" case before doing a lookup.
2354 if (request_code == REP_PROTOCOL_CLOSE) {
2355 client_destroy(id);
2356 result = REP_PROTOCOL_SUCCESS;
2357 goto end_unheld;
2360 cp = client_lookup(id);
2362 * cp is held
2365 if (cp == NULL)
2366 goto bad_end;
2368 if (rlp != NULL)
2369 rlp->rl_client = cp;
2371 ti->ti_active_client = cp;
2373 if (request_code < REP_PROTOCOL_BASE ||
2374 request_code >= REP_PROTOCOL_BASE + PROTOCOL_ENTRIES) {
2375 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2376 goto end;
2379 e = &protocol_table[request_code - REP_PROTOCOL_BASE];
2381 assert(!(e->pt_flags & PROTO_FLAG_PANIC));
2383 if (e->pt_flags & PROTO_FLAG_VARINPUT) {
2384 if (arg_size < e->pt_in_size) {
2385 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2386 goto end;
2388 } else if (arg_size != e->pt_in_size) {
2389 result = REP_PROTOCOL_FAIL_BAD_REQUEST;
2390 goto end;
2393 if (retsize != e->pt_out_max) {
2394 retsize = e->pt_out_max;
2395 retval = alloca(retsize);
2398 if (e->pt_flags & PROTO_FLAG_RETFD)
2399 e->pt_fd_handler(cp, argp, arg_size, retval, &retsize,
2400 e->pt_arg, &retfd);
2401 else
2402 e->pt_handler(cp, argp, arg_size, retval, &retsize, e->pt_arg);
2404 end:
2405 ti->ti_active_client = NULL;
2406 client_release(cp);
2408 end_unheld:
2409 if (rlp != NULL) {
2410 /* LINTED alignment */
2411 rlp->rl_response = *(uint32_t *)retval;
2412 end_log();
2413 rlp = NULL;
2415 ti->ti_client_request = NULL;
2416 thread_newstate(ti, TI_DOOR_RETURN);
2418 if (retval == (char *)&result) {
2419 assert(result != INVALID_RESULT && retsize == sizeof (result));
2420 } else {
2421 /* LINTED alignment */
2422 result = *(uint32_t *)retval;
2424 if (retfd != -1) {
2425 desc.d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE;
2426 desc.d_data.d_desc.d_descriptor = retfd;
2427 (void) door_return(retval, retsize, &desc, 1);
2428 } else {
2429 (void) door_return(retval, retsize, NULL, 0);
2431 bad_end:
2432 if (rlp != NULL) {
2433 rlp->rl_response = -1;
2434 end_log();
2435 rlp = NULL;
2437 (void) door_return(NULL, 0, NULL, 0);
2441 create_client(pid_t pid, uint32_t debugflags, int privileged, int *out_fd)
2443 int fd;
2445 repcache_client_t *cp;
2447 struct door_info info;
2449 int door_flags = DOOR_UNREF | DOOR_REFUSE_DESC;
2450 #ifdef DOOR_NO_CANCEL
2451 door_flags |= DOOR_NO_CANCEL;
2452 #endif
2454 cp = client_alloc();
2455 if (cp == NULL)
2456 return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2458 (void) pthread_mutex_lock(&client_lock);
2459 cp->rc_id = ++client_maxid;
2460 (void) pthread_mutex_unlock(&client_lock);
2462 cp->rc_all_auths = privileged;
2463 cp->rc_pid = pid;
2464 cp->rc_debug = debugflags;
2466 #ifndef NATIVE_BUILD
2467 start_audit_session(cp);
2468 #endif
2470 cp->rc_doorfd = door_create(client_switcher, (void *)cp->rc_id,
2471 door_flags);
2473 if (cp->rc_doorfd < 0) {
2474 client_free(cp);
2475 return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2477 #ifdef DOOR_PARAM_DATA_MIN
2478 (void) door_setparam(cp->rc_doorfd, DOOR_PARAM_DATA_MIN,
2479 sizeof (enum rep_protocol_requestid));
2480 #endif
2482 if ((fd = dup(cp->rc_doorfd)) < 0 ||
2483 door_info(cp->rc_doorfd, &info) < 0) {
2484 if (fd >= 0)
2485 (void) close(fd);
2486 (void) door_revoke(cp->rc_doorfd);
2487 cp->rc_doorfd = -1;
2488 client_free(cp);
2489 return (REPOSITORY_DOOR_FAIL_NO_RESOURCES);
2492 rc_pg_notify_init(&cp->rc_pg_notify);
2493 rc_notify_info_init(&cp->rc_notify_info);
2495 client_insert(cp);
2497 cp->rc_doorid = info.di_uniquifier;
2498 *out_fd = fd;
2500 return (REPOSITORY_DOOR_SUCCESS);