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 * 94 get_log(void) 95 { 96 thread_info_t *ti = thread_self(); 97 return (&ti->ti_log); 98 } 99 100 void 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 * 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 137 end_log(void) 138 { 139 request_log_entry_t *rlp = get_log(); 140 141 rlp->rl_end = gethrtime(); 142 } 143 144 static void 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 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 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 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 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 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 * 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 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 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 * 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 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 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 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 * 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 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 * 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 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 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 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 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 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 * 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 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 * 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 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 627 iter_release(repcache_iter_t *iter) 628 { 629 (void) pthread_mutex_unlock(&iter->ri_lock); 630 } 631 632 static void 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 }