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