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