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