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