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