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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * rc_node.c - object management primitives 30 * 31 * This layer manages entities, their data structure, its locking, iterators, 32 * transactions, and change notification requests. Entities (scopes, 33 * services, instances, snapshots, snaplevels, property groups, "composed" 34 * property groups (see composition below), and properties) are represented by 35 * rc_node_t's and are kept in the cache_hash hash table. (Property values 36 * are kept in the rn_values member of the respective property -- not as 37 * separate objects.) Iterators are represented by rc_node_iter_t's. 38 * Transactions are represented by rc_node_tx_t's and are only allocated as 39 * part of repcache_tx_t's in the client layer (client.c). Change 40 * notification requests are represented by rc_notify_t structures and are 41 * described below. 42 * 43 * The entity tree is rooted at rc_scope, which rc_node_init() initializes to 44 * the "localhost" scope. The tree is filled in from the database on-demand 45 * by rc_node_fill_children(), usually from rc_iter_create() since iterators 46 * are the only way to find the children of an entity. 47 * 48 * Each rc_node_t is protected by its rn_lock member. Operations which can 49 * take too long, however, should serialize on an RC_NODE_WAITING_FLAGS bit in 50 * rn_flags with the rc_node_{hold,rele}_flag() functions. And since pointers 51 * to rc_node_t's are allowed, rn_refs is a reference count maintained by 52 * rc_node_{hold,rele}(). See configd.h for locking order information. 53 * 54 * When a node (property group or snapshot) is updated, a new node takes the 55 * place of the old node in the global hash, and the old node is hung off of 56 * the rn_former list of the new node. At the same time, all of its children 57 * have their rn_parent_ref pointer set, and any holds they have are reflected 58 * in the old node's rn_other_refs count. This is automatically kept up 59 * to date, until the final reference to the subgraph is dropped, at which 60 * point the node is unrefed and destroyed, along with all of its children. 61 * 62 * Locking rules: To dereference an rc_node_t * (usually to lock it), you must 63 * have a hold (rc_node_hold()) on it or otherwise be sure that it hasn't been 64 * rc_node_destroy()ed (hold a lock on its parent or child, hold a flag, 65 * etc.). Once you have locked an rc_node_t you must check its rn_flags for 66 * RC_NODE_DEAD before you can use it. This is usually done with the 67 * rc_node_{wait,hold}_flag() functions (often via the rc_node_check_*() 68 * functions & RC_NODE_*() macros), which fail if the object has died. 69 * 70 * An ITER_START for a non-ENTITY_VALUE induces an rc_node_fill_children() 71 * call via rc_node_setup_iter() to populate the rn_children uu_list of the 72 * rc_node_t * in question and a call to uu_list_walk_start() on that list. For 73 * ITER_READ, rc_iter_next() uses uu_list_walk_next() to find the next 74 * apropriate child. 75 * 76 * An ITER_START for an ENTITY_VALUE makes sure the node has its values 77 * filled, and sets up the iterator. An ITER_READ_VALUE just copies out 78 * the proper values and updates the offset information. 79 * 80 * When a property group gets changed by a transaction, it sticks around as 81 * a child of its replacement property group, but is removed from the parent. 82 * 83 * To allow aliases, snapshots are implemented with a level of indirection. 84 * A snapshot rc_node_t has a snapid which refers to an rc_snapshot_t in 85 * snapshot.c which contains the authoritative snaplevel information. The 86 * snapid is "assigned" by rc_attach_snapshot(). 87 * 88 * We provide the client layer with rc_node_ptr_t's to reference objects. 89 * Objects referred to by them are automatically held & released by 90 * rc_node_assign() & rc_node_clear(). The RC_NODE_PTR_*() macros are used at 91 * client.c entry points to read the pointers. They fetch the pointer to the 92 * object, return (from the function) if it is dead, and lock, hold, or hold 93 * a flag of the object. 94 */ 95 96 /* 97 * Permission checking is authorization-based: some operations may only 98 * proceed if the user has been assigned at least one of a set of 99 * authorization strings. The set of enabling authorizations depends on the 100 * operation and the target object. The set of authorizations assigned to 101 * a user is determined by reading /etc/security/policy.conf, querying the 102 * user_attr database, and possibly querying the prof_attr database, as per 103 * chkauthattr() in libsecdb. 104 * 105 * The fastest way to decide whether the two sets intersect is by entering the 106 * strings into a hash table and detecting collisions, which takes linear time 107 * in the total size of the sets. Except for the authorization patterns which 108 * may be assigned to users, which without advanced pattern-matching 109 * algorithms will take O(n) in the number of enabling authorizations, per 110 * pattern. 111 * 112 * We can achieve some practical speed-ups by noting that if we enter all of 113 * the authorizations from one of the sets into the hash table we can merely 114 * check the elements of the second set for existence without adding them. 115 * This reduces memory requirements and hash table clutter. The enabling set 116 * is well suited for this because it is internal to configd (for now, at 117 * least). Combine this with short-circuiting and we can even minimize the 118 * number of queries to the security databases (user_attr & prof_attr). 119 * 120 * To force this usage onto clients we provide functions for adding 121 * authorizations to the enabling set of a permission context structure 122 * (perm_add_*()) and one to decide whether the the user associated with the 123 * current door call client possesses any of them (perm_granted()). 124 * 125 * At some point, a generic version of this should move to libsecdb. 126 */ 127 128 /* 129 * Composition is the combination of sets of properties. The sets are ordered 130 * and properties in higher sets obscure properties of the same name in lower 131 * sets. Here we present a composed view of an instance's properties as the 132 * union of its properties and its service's properties. Similarly the 133 * properties of snaplevels are combined to form a composed view of the 134 * properties of a snapshot (which should match the composed view of the 135 * properties of the instance when the snapshot was taken). 136 * 137 * In terms of the client interface, the client may request that a property 138 * group iterator for an instance or snapshot be composed. Property groups 139 * traversed by such an iterator may not have the target entity as a parent. 140 * Similarly, the properties traversed by a property iterator for those 141 * property groups may not have the property groups iterated as parents. 142 * 143 * Implementation requires that iterators for instances and snapshots be 144 * composition-savvy, and that we have a "composed property group" entity 145 * which represents the composition of a number of property groups. Iteration 146 * over "composed property groups" yields properties which may have different 147 * parents, but for all other operations a composed property group behaves 148 * like the top-most property group it represents. 149 * 150 * The implementation is based on the rn_cchain[] array of rc_node_t pointers 151 * in rc_node_t. For instances, the pointers point to the instance and its 152 * parent service. For snapshots they point to the child snaplevels, and for 153 * composed property groups they point to property groups. A composed 154 * iterator carries an index into rn_cchain[]. Thus most of the magic ends up 155 * int the rc_iter_*() code. 156 */ 157 158 #include <assert.h> 159 #include <atomic.h> 160 #include <errno.h> 161 #include <libuutil.h> 162 #include <libscf.h> 163 #include <libscf_priv.h> 164 #include <prof_attr.h> 165 #include <pthread.h> 166 #include <stdio.h> 167 #include <stdlib.h> 168 #include <strings.h> 169 #include <sys/types.h> 170 #include <unistd.h> 171 #include <user_attr.h> 172 173 #include "configd.h" 174 175 #define AUTH_PREFIX "solaris.smf." 176 #define AUTH_MANAGE AUTH_PREFIX "manage" 177 #define AUTH_MODIFY AUTH_PREFIX "modify" 178 #define AUTH_MODIFY_PREFIX AUTH_MODIFY "." 179 #define AUTH_PG_ACTIONS SCF_PG_RESTARTER_ACTIONS 180 #define AUTH_PG_ACTIONS_TYPE SCF_PG_RESTARTER_ACTIONS_TYPE 181 #define AUTH_PG_GENERAL SCF_PG_GENERAL 182 #define AUTH_PG_GENERAL_TYPE SCF_PG_GENERAL_TYPE 183 #define AUTH_PG_GENERAL_OVR SCF_PG_GENERAL_OVR 184 #define AUTH_PG_GENERAL_OVR_TYPE SCF_PG_GENERAL_OVR_TYPE 185 #define AUTH_PROP_ACTION "action_authorization" 186 #define AUTH_PROP_ENABLED "enabled" 187 #define AUTH_PROP_MODIFY "modify_authorization" 188 #define AUTH_PROP_VALUE "value_authorization" 189 /* libsecdb should take care of this. */ 190 #define RBAC_AUTH_SEP "," 191 192 #define MAX_VALID_CHILDREN 3 193 194 typedef struct rc_type_info { 195 uint32_t rt_type; /* matches array index */ 196 uint32_t rt_num_ids; 197 uint32_t rt_name_flags; 198 uint32_t rt_valid_children[MAX_VALID_CHILDREN]; 199 } rc_type_info_t; 200 201 #define RT_NO_NAME -1U 202 203 static rc_type_info_t rc_types[] = { 204 {REP_PROTOCOL_ENTITY_NONE, 0, RT_NO_NAME}, 205 {REP_PROTOCOL_ENTITY_SCOPE, 0, 0, 206 {REP_PROTOCOL_ENTITY_SERVICE, REP_PROTOCOL_ENTITY_SCOPE}}, 207 {REP_PROTOCOL_ENTITY_SERVICE, 0, UU_NAME_DOMAIN | UU_NAME_PATH, 208 {REP_PROTOCOL_ENTITY_INSTANCE, REP_PROTOCOL_ENTITY_PROPERTYGRP}}, 209 {REP_PROTOCOL_ENTITY_INSTANCE, 1, UU_NAME_DOMAIN, 210 {REP_PROTOCOL_ENTITY_SNAPSHOT, REP_PROTOCOL_ENTITY_PROPERTYGRP}}, 211 {REP_PROTOCOL_ENTITY_SNAPSHOT, 2, UU_NAME_DOMAIN, 212 {REP_PROTOCOL_ENTITY_SNAPLEVEL, REP_PROTOCOL_ENTITY_PROPERTYGRP}}, 213 {REP_PROTOCOL_ENTITY_SNAPLEVEL, 4, RT_NO_NAME, 214 {REP_PROTOCOL_ENTITY_PROPERTYGRP}}, 215 {REP_PROTOCOL_ENTITY_PROPERTYGRP, 5, UU_NAME_DOMAIN, 216 {REP_PROTOCOL_ENTITY_PROPERTY}}, 217 {REP_PROTOCOL_ENTITY_CPROPERTYGRP, 0, UU_NAME_DOMAIN, 218 {REP_PROTOCOL_ENTITY_PROPERTY}}, 219 {REP_PROTOCOL_ENTITY_PROPERTY, 7, UU_NAME_DOMAIN}, 220 {-1UL} 221 }; 222 #define NUM_TYPES ((sizeof (rc_types) / sizeof (*rc_types))) 223 224 /* Element of a permcheck_t hash table. */ 225 struct pc_elt { 226 struct pc_elt *pce_next; 227 char pce_auth[1]; 228 }; 229 230 /* An authorization set hash table. */ 231 typedef struct { 232 struct pc_elt **pc_buckets; 233 uint_t pc_bnum; /* number of buckets */ 234 uint_t pc_enum; /* number of elements */ 235 } permcheck_t; 236 237 static uu_list_pool_t *rc_children_pool; 238 static uu_list_pool_t *rc_pg_notify_pool; 239 static uu_list_pool_t *rc_notify_pool; 240 static uu_list_pool_t *rc_notify_info_pool; 241 242 static rc_node_t *rc_scope; 243 244 static pthread_mutex_t rc_pg_notify_lock = PTHREAD_MUTEX_INITIALIZER; 245 static pthread_cond_t rc_pg_notify_cv = PTHREAD_COND_INITIALIZER; 246 static uint_t rc_notify_in_use; /* blocks removals */ 247 248 static pthread_mutex_t perm_lock = PTHREAD_MUTEX_INITIALIZER; 249 250 static void rc_node_unrefed(rc_node_t *np); 251 252 /* 253 * We support an arbitrary number of clients interested in events for certain 254 * types of changes. Each client is represented by an rc_notify_info_t, and 255 * all clients are chained onto the rc_notify_info_list. 256 * 257 * The rc_notify_list is the global notification list. Each entry is of 258 * type rc_notify_t, which is embedded in one of three other structures: 259 * 260 * rc_node_t property group update notification 261 * rc_notify_delete_t object deletion notification 262 * rc_notify_info_t notification clients 263 * 264 * Which type of object is determined by which pointer in the rc_notify_t is 265 * non-NULL. 266 * 267 * New notifications and clients are added to the end of the list. 268 * Notifications no-one is interested in are never added to the list. 269 * 270 * Clients use their position in the list to track which notifications they 271 * have not yet reported. As they process notifications, they move forward 272 * in the list past them. There is always a client at the beginning of the 273 * list -- as he moves past notifications, he removes them from the list and 274 * cleans them up. 275 * 276 * The rc_pg_notify_lock protects all notification state. The rc_pg_notify_cv 277 * is used for global signalling, and each client has a cv which he waits for 278 * events of interest on. 279 */ 280 static uu_list_t *rc_notify_info_list; 281 static uu_list_t *rc_notify_list; 282 283 #define HASH_SIZE 512 284 #define HASH_MASK (HASH_SIZE - 1) 285 286 #pragma align 64(cache_hash) 287 static cache_bucket_t cache_hash[HASH_SIZE]; 288 289 #define CACHE_BUCKET(h) (&cache_hash[(h) & HASH_MASK]) 290 291 static uint32_t 292 rc_node_hash(rc_node_lookup_t *lp) 293 { 294 uint32_t type = lp->rl_type; 295 uint32_t backend = lp->rl_backend; 296 uint32_t mainid = lp->rl_main_id; 297 uint32_t *ids = lp->rl_ids; 298 299 rc_type_info_t *tp = &rc_types[type]; 300 uint32_t num_ids; 301 uint32_t left; 302 uint32_t hash; 303 304 assert(backend == BACKEND_TYPE_NORMAL || 305 backend == BACKEND_TYPE_NONPERSIST); 306 307 assert(type > 0 && type < NUM_TYPES); 308 num_ids = tp->rt_num_ids; 309 310 left = MAX_IDS - num_ids; 311 assert(num_ids <= MAX_IDS); 312 313 hash = type * 7 + mainid * 5 + backend; 314 315 while (num_ids-- > 0) 316 hash = hash * 11 + *ids++ * 7; 317 318 /* 319 * the rest should be zeroed 320 */ 321 while (left-- > 0) 322 assert(*ids++ == 0); 323 324 return (hash); 325 } 326 327 static int 328 rc_node_match(rc_node_t *np, rc_node_lookup_t *l) 329 { 330 rc_node_lookup_t *r = &np->rn_id; 331 rc_type_info_t *tp; 332 uint32_t type; 333 uint32_t num_ids; 334 335 if (r->rl_main_id != l->rl_main_id) 336 return (0); 337 338 type = r->rl_type; 339 if (type != l->rl_type) 340 return (0); 341 342 assert(type > 0 && type < NUM_TYPES); 343 344 tp = &rc_types[r->rl_type]; 345 num_ids = tp->rt_num_ids; 346 347 assert(num_ids <= MAX_IDS); 348 while (num_ids-- > 0) 349 if (r->rl_ids[num_ids] != l->rl_ids[num_ids]) 350 return (0); 351 352 return (1); 353 } 354 355 /* 356 * the "other" references on a node are maintained in an atomically 357 * updated refcount, rn_other_refs. This can be bumped from arbitrary 358 * context, and tracks references to a possibly out-of-date node's children. 359 * 360 * To prevent the node from disappearing between the final drop of 361 * rn_other_refs and the unref handling, rn_other_refs_held is bumped on 362 * 0->1 transitions and decremented (with the node lock held) on 1->0 363 * transitions. 364 */ 365 static void 366 rc_node_hold_other(rc_node_t *np) 367 { 368 if (atomic_add_32_nv(&np->rn_other_refs, 1) == 1) { 369 atomic_add_32(&np->rn_other_refs_held, 1); 370 assert(np->rn_other_refs_held > 0); 371 } 372 assert(np->rn_other_refs > 0); 373 } 374 375 /* 376 * No node locks may be held 377 */ 378 static void 379 rc_node_rele_other(rc_node_t *np) 380 { 381 assert(np->rn_other_refs > 0); 382 if (atomic_add_32_nv(&np->rn_other_refs, -1) == 0) { 383 (void) pthread_mutex_lock(&np->rn_lock); 384 assert(np->rn_other_refs_held > 0); 385 if (atomic_add_32_nv(&np->rn_other_refs_held, -1) == 0 && 386 np->rn_refs == 0 && (np->rn_flags & RC_NODE_OLD)) 387 rc_node_unrefed(np); 388 else 389 (void) pthread_mutex_unlock(&np->rn_lock); 390 } 391 } 392 393 static void 394 rc_node_hold_locked(rc_node_t *np) 395 { 396 assert(MUTEX_HELD(&np->rn_lock)); 397 398 if (np->rn_refs == 0 && (np->rn_flags & RC_NODE_PARENT_REF)) 399 rc_node_hold_other(np->rn_parent_ref); 400 np->rn_refs++; 401 assert(np->rn_refs > 0); 402 } 403 404 static void 405 rc_node_hold(rc_node_t *np) 406 { 407 (void) pthread_mutex_lock(&np->rn_lock); 408 rc_node_hold_locked(np); 409 (void) pthread_mutex_unlock(&np->rn_lock); 410 } 411 412 static void 413 rc_node_rele_locked(rc_node_t *np) 414 { 415 int unref = 0; 416 rc_node_t *par_ref = NULL; 417 418 assert(MUTEX_HELD(&np->rn_lock)); 419 assert(np->rn_refs > 0); 420 421 if (--np->rn_refs == 0) { 422 if (np->rn_flags & RC_NODE_PARENT_REF) 423 par_ref = np->rn_parent_ref; 424 425 /* 426 * Composed property groups are only as good as their 427 * references. 428 */ 429 if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) 430 np->rn_flags |= RC_NODE_DEAD; 431 432 if ((np->rn_flags & (RC_NODE_DEAD|RC_NODE_OLD)) && 433 np->rn_other_refs == 0 && np->rn_other_refs_held == 0) 434 unref = 1; 435 } 436 437 if (unref) 438 rc_node_unrefed(np); 439 else 440 (void) pthread_mutex_unlock(&np->rn_lock); 441 442 if (par_ref != NULL) 443 rc_node_rele_other(par_ref); 444 } 445 446 void 447 rc_node_rele(rc_node_t *np) 448 { 449 (void) pthread_mutex_lock(&np->rn_lock); 450 rc_node_rele_locked(np); 451 } 452 453 static cache_bucket_t * 454 cache_hold(uint32_t h) 455 { 456 cache_bucket_t *bp = CACHE_BUCKET(h); 457 (void) pthread_mutex_lock(&bp->cb_lock); 458 return (bp); 459 } 460 461 static void 462 cache_release(cache_bucket_t *bp) 463 { 464 (void) pthread_mutex_unlock(&bp->cb_lock); 465 } 466 467 static rc_node_t * 468 cache_lookup_unlocked(cache_bucket_t *bp, rc_node_lookup_t *lp) 469 { 470 uint32_t h = rc_node_hash(lp); 471 rc_node_t *np; 472 473 assert(MUTEX_HELD(&bp->cb_lock)); 474 assert(bp == CACHE_BUCKET(h)); 475 476 for (np = bp->cb_head; np != NULL; np = np->rn_hash_next) { 477 if (np->rn_hash == h && rc_node_match(np, lp)) { 478 rc_node_hold(np); 479 return (np); 480 } 481 } 482 483 return (NULL); 484 } 485 486 static rc_node_t * 487 cache_lookup(rc_node_lookup_t *lp) 488 { 489 uint32_t h; 490 cache_bucket_t *bp; 491 rc_node_t *np; 492 493 h = rc_node_hash(lp); 494 bp = cache_hold(h); 495 496 np = cache_lookup_unlocked(bp, lp); 497 498 cache_release(bp); 499 500 return (np); 501 } 502 503 static void 504 cache_insert_unlocked(cache_bucket_t *bp, rc_node_t *np) 505 { 506 assert(MUTEX_HELD(&bp->cb_lock)); 507 assert(np->rn_hash == rc_node_hash(&np->rn_id)); 508 assert(bp == CACHE_BUCKET(np->rn_hash)); 509 510 assert(np->rn_hash_next == NULL); 511 512 np->rn_hash_next = bp->cb_head; 513 bp->cb_head = np; 514 } 515 516 static void 517 cache_remove_unlocked(cache_bucket_t *bp, rc_node_t *np) 518 { 519 rc_node_t **npp; 520 521 assert(MUTEX_HELD(&bp->cb_lock)); 522 assert(np->rn_hash == rc_node_hash(&np->rn_id)); 523 assert(bp == CACHE_BUCKET(np->rn_hash)); 524 525 for (npp = &bp->cb_head; *npp != NULL; npp = &(*npp)->rn_hash_next) 526 if (*npp == np) 527 break; 528 529 assert(*npp == np); 530 *npp = np->rn_hash_next; 531 np->rn_hash_next = NULL; 532 } 533 534 /* 535 * verify that the 'parent' type can have a child typed 'child' 536 * Fails with 537 * _INVALID_TYPE - argument is invalid 538 * _TYPE_MISMATCH - parent type cannot have children of type child 539 */ 540 static int 541 rc_check_parent_child(uint32_t parent, uint32_t child) 542 { 543 int idx; 544 uint32_t type; 545 546 if (parent == 0 || parent >= NUM_TYPES || 547 child == 0 || child >= NUM_TYPES) 548 return (REP_PROTOCOL_FAIL_INVALID_TYPE); /* invalid types */ 549 550 for (idx = 0; idx < MAX_VALID_CHILDREN; idx++) { 551 type = rc_types[parent].rt_valid_children[idx]; 552 if (type == child) 553 return (REP_PROTOCOL_SUCCESS); 554 } 555 556 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH); 557 } 558 559 /* 560 * Fails with 561 * _INVALID_TYPE - type is invalid 562 * _BAD_REQUEST - name is an invalid name for a node of type type 563 */ 564 int 565 rc_check_type_name(uint32_t type, const char *name) 566 { 567 if (type == 0 || type >= NUM_TYPES) 568 return (REP_PROTOCOL_FAIL_INVALID_TYPE); /* invalid types */ 569 570 if (uu_check_name(name, rc_types[type].rt_name_flags) == -1) 571 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 572 573 return (REP_PROTOCOL_SUCCESS); 574 } 575 576 static int 577 rc_check_pgtype_name(const char *name) 578 { 579 if (uu_check_name(name, UU_NAME_DOMAIN) == -1) 580 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 581 582 return (REP_PROTOCOL_SUCCESS); 583 } 584 585 static int 586 rc_notify_info_interested(rc_notify_info_t *rnip, rc_notify_t *np) 587 { 588 rc_node_t *nnp = np->rcn_node; 589 int i; 590 591 assert(MUTEX_HELD(&rc_pg_notify_lock)); 592 593 if (np->rcn_delete != NULL) { 594 assert(np->rcn_info == NULL && np->rcn_node == NULL); 595 return (1); /* everyone likes deletes */ 596 } 597 if (np->rcn_node == NULL) { 598 assert(np->rcn_info != NULL || np->rcn_delete != NULL); 599 return (0); 600 } 601 assert(np->rcn_info == NULL); 602 603 for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) { 604 if (rnip->rni_namelist[i] != NULL) { 605 if (strcmp(nnp->rn_name, rnip->rni_namelist[i]) == 0) 606 return (1); 607 } 608 if (rnip->rni_typelist[i] != NULL) { 609 if (strcmp(nnp->rn_type, rnip->rni_typelist[i]) == 0) 610 return (1); 611 } 612 } 613 return (0); 614 } 615 616 static void 617 rc_notify_insert_node(rc_node_t *nnp) 618 { 619 rc_notify_t *np = &nnp->rn_notify; 620 rc_notify_info_t *nip; 621 int found = 0; 622 623 assert(np->rcn_info == NULL); 624 625 if (nnp->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) 626 return; 627 628 (void) pthread_mutex_lock(&rc_pg_notify_lock); 629 np->rcn_node = nnp; 630 for (nip = uu_list_first(rc_notify_info_list); nip != NULL; 631 nip = uu_list_next(rc_notify_info_list, nip)) { 632 if (rc_notify_info_interested(nip, np)) { 633 (void) pthread_cond_broadcast(&nip->rni_cv); 634 found++; 635 } 636 } 637 if (found) 638 (void) uu_list_insert_before(rc_notify_list, NULL, np); 639 else 640 np->rcn_node = NULL; 641 642 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 643 } 644 645 static void 646 rc_notify_deletion(rc_notify_delete_t *ndp, const char *service, 647 const char *instance, const char *pg) 648 { 649 rc_notify_info_t *nip; 650 651 uu_list_node_init(&ndp->rnd_notify, &ndp->rnd_notify.rcn_list_node, 652 rc_notify_pool); 653 ndp->rnd_notify.rcn_delete = ndp; 654 655 (void) snprintf(ndp->rnd_fmri, sizeof (ndp->rnd_fmri), 656 "svc:/%s%s%s%s%s", service, 657 (instance != NULL)? ":" : "", (instance != NULL)? instance : "", 658 (pg != NULL)? "/:properties/" : "", (pg != NULL)? pg : ""); 659 660 /* 661 * add to notification list, notify watchers 662 */ 663 (void) pthread_mutex_lock(&rc_pg_notify_lock); 664 for (nip = uu_list_first(rc_notify_info_list); nip != NULL; 665 nip = uu_list_next(rc_notify_info_list, nip)) 666 (void) pthread_cond_broadcast(&nip->rni_cv); 667 (void) uu_list_insert_before(rc_notify_list, NULL, ndp); 668 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 669 } 670 671 static void 672 rc_notify_remove_node(rc_node_t *nnp) 673 { 674 rc_notify_t *np = &nnp->rn_notify; 675 676 assert(np->rcn_info == NULL); 677 assert(!MUTEX_HELD(&nnp->rn_lock)); 678 679 (void) pthread_mutex_lock(&rc_pg_notify_lock); 680 while (np->rcn_node != NULL) { 681 if (rc_notify_in_use) { 682 (void) pthread_cond_wait(&rc_pg_notify_cv, 683 &rc_pg_notify_lock); 684 continue; 685 } 686 (void) uu_list_remove(rc_notify_list, np); 687 np->rcn_node = NULL; 688 break; 689 } 690 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 691 } 692 693 static void 694 rc_notify_remove_locked(rc_notify_t *np) 695 { 696 assert(MUTEX_HELD(&rc_pg_notify_lock)); 697 assert(rc_notify_in_use == 0); 698 699 (void) uu_list_remove(rc_notify_list, np); 700 if (np->rcn_node) { 701 np->rcn_node = NULL; 702 } else if (np->rcn_delete) { 703 uu_free(np->rcn_delete); 704 } else { 705 assert(0); /* CAN'T HAPPEN */ 706 } 707 } 708 709 /* 710 * Permission checking functions. See comment atop this file. 711 */ 712 #ifndef NATIVE_BUILD 713 static permcheck_t * 714 pc_create() 715 { 716 permcheck_t *p; 717 718 p = uu_zalloc(sizeof (*p)); 719 if (p == NULL) 720 return (NULL); 721 p->pc_bnum = 8; /* Normal case will only have 2 elts. */ 722 p->pc_buckets = uu_zalloc(sizeof (*p->pc_buckets) * p->pc_bnum); 723 if (p->pc_buckets == NULL) { 724 uu_free(p); 725 return (NULL); 726 } 727 728 p->pc_enum = 0; 729 return (p); 730 } 731 732 static void 733 pc_free(permcheck_t *pcp) 734 { 735 uint_t i; 736 struct pc_elt *ep, *next; 737 738 for (i = 0; i < pcp->pc_bnum; ++i) { 739 for (ep = pcp->pc_buckets[i]; ep != NULL; ep = next) { 740 next = ep->pce_next; 741 free(ep); 742 } 743 } 744 745 free(pcp->pc_buckets); 746 free(pcp); 747 } 748 749 static uint32_t 750 pc_hash(const char *auth) 751 { 752 uint32_t h = 0, g; 753 const char *p; 754 755 /* 756 * Generic hash function from uts/common/os/modhash.c. 757 */ 758 for (p = auth; *p != '\0'; ++p) { 759 h = (h << 4) + *p; 760 g = (h & 0xf0000000); 761 if (g != 0) { 762 h ^= (g >> 24); 763 h ^= g; 764 } 765 } 766 767 return (h); 768 } 769 770 static int 771 pc_exists(const permcheck_t *pcp, const char *auth) 772 { 773 uint32_t h; 774 struct pc_elt *ep; 775 776 h = pc_hash(auth); 777 for (ep = pcp->pc_buckets[h & (pcp->pc_bnum - 1)]; 778 ep != NULL; 779 ep = ep->pce_next) { 780 if (strcmp(auth, ep->pce_auth) == 0) 781 return (1); 782 } 783 784 return (0); 785 } 786 787 static int 788 pc_match(const permcheck_t *pcp, const char *pattern) 789 { 790 uint_t i; 791 struct pc_elt *ep; 792 793 for (i = 0; i < pcp->pc_bnum; ++i) { 794 for (ep = pcp->pc_buckets[i]; ep != NULL; ep = ep->pce_next) { 795 if (_auth_match(pattern, ep->pce_auth)) 796 return (1); 797 } 798 } 799 800 return (0); 801 } 802 803 static int 804 pc_grow(permcheck_t *pcp) 805 { 806 uint_t new_bnum, i, j; 807 struct pc_elt **new_buckets; 808 struct pc_elt *ep, *next; 809 810 new_bnum = pcp->pc_bnum * 2; 811 if (new_bnum < pcp->pc_bnum) 812 /* Homey don't play that. */ 813 return (-1); 814 815 new_buckets = uu_zalloc(sizeof (*new_buckets) * new_bnum); 816 if (new_buckets == NULL) 817 return (-1); 818 819 for (i = 0; i < pcp->pc_bnum; ++i) { 820 for (ep = pcp->pc_buckets[i]; ep != NULL; ep = next) { 821 next = ep->pce_next; 822 j = pc_hash(ep->pce_auth) & (new_bnum - 1); 823 ep->pce_next = new_buckets[j]; 824 new_buckets[j] = ep; 825 } 826 } 827 828 uu_free(pcp->pc_buckets); 829 pcp->pc_buckets = new_buckets; 830 pcp->pc_bnum = new_bnum; 831 832 return (0); 833 } 834 835 static int 836 pc_add(permcheck_t *pcp, const char *auth) 837 { 838 struct pc_elt *ep; 839 uint_t i; 840 841 ep = uu_zalloc(offsetof(struct pc_elt, pce_auth) + strlen(auth) + 1); 842 if (ep == NULL) 843 return (-1); 844 845 /* Grow if pc_enum / pc_bnum > 3/4. */ 846 if (pcp->pc_enum * 4 > 3 * pcp->pc_bnum) 847 /* Failure is not a stopper; we'll try again next time. */ 848 (void) pc_grow(pcp); 849 850 (void) strcpy(ep->pce_auth, auth); 851 852 i = pc_hash(auth) & (pcp->pc_bnum - 1); 853 ep->pce_next = pcp->pc_buckets[i]; 854 pcp->pc_buckets[i] = ep; 855 856 ++pcp->pc_enum; 857 858 return (0); 859 } 860 861 /* 862 * For the type of a property group, return the authorization which may be 863 * used to modify it. 864 */ 865 static const char * 866 perm_auth_for_pgtype(const char *pgtype) 867 { 868 if (strcmp(pgtype, SCF_GROUP_METHOD) == 0) 869 return (AUTH_MODIFY_PREFIX "method"); 870 else if (strcmp(pgtype, SCF_GROUP_DEPENDENCY) == 0) 871 return (AUTH_MODIFY_PREFIX "dependency"); 872 else if (strcmp(pgtype, SCF_GROUP_APPLICATION) == 0) 873 return (AUTH_MODIFY_PREFIX "application"); 874 else if (strcmp(pgtype, SCF_GROUP_FRAMEWORK) == 0) 875 return (AUTH_MODIFY_PREFIX "framework"); 876 else 877 return (NULL); 878 } 879 880 /* 881 * Fails with 882 * _NO_RESOURCES - out of memory 883 */ 884 static int 885 perm_add_enabling(permcheck_t *pcp, const char *auth) 886 { 887 return (pc_add(pcp, auth) == 0 ? REP_PROTOCOL_SUCCESS : 888 REP_PROTOCOL_FAIL_NO_RESOURCES); 889 } 890 891 /* Note that perm_add_enabling_values() is defined below. */ 892 893 /* 894 * perm_granted() returns 1 if the current door caller has one of the enabling 895 * authorizations in pcp, 0 if it doesn't, and -1 if an error (usually lack of 896 * memory) occurs. check_auth_list() checks an RBAC_AUTH_SEP-separated list 897 * of authorizations for existance in pcp, and check_prof_list() checks the 898 * authorizations granted to an RBAC_AUTH_SEP-separated list of profiles. 899 */ 900 static int 901 check_auth_list(const permcheck_t *pcp, char *authlist) 902 { 903 char *auth, *lasts; 904 int ret; 905 906 for (auth = (char *)strtok_r(authlist, RBAC_AUTH_SEP, &lasts); 907 auth != NULL; 908 auth = (char *)strtok_r(NULL, RBAC_AUTH_SEP, &lasts)) { 909 if (strchr(auth, KV_WILDCHAR) == NULL) 910 ret = pc_exists(pcp, auth); 911 else 912 ret = pc_match(pcp, auth); 913 914 if (ret) 915 return (ret); 916 } 917 918 return (0); 919 } 920 921 static int 922 check_prof_list(const permcheck_t *pcp, char *proflist) 923 { 924 char *prof, *lasts, *authlist, *subproflist; 925 profattr_t *pap; 926 int ret = 0; 927 928 for (prof = strtok_r(proflist, RBAC_AUTH_SEP, &lasts); 929 prof != NULL; 930 prof = strtok_r(NULL, RBAC_AUTH_SEP, &lasts)) { 931 pap = getprofnam(prof); 932 if (pap == NULL) 933 continue; 934 935 authlist = kva_match(pap->attr, PROFATTR_AUTHS_KW); 936 if (authlist != NULL) 937 ret = check_auth_list(pcp, authlist); 938 939 if (!ret) { 940 subproflist = kva_match(pap->attr, PROFATTR_PROFS_KW); 941 if (subproflist != NULL) 942 /* depth check to avoid invinite recursion? */ 943 ret = check_prof_list(pcp, subproflist); 944 } 945 946 free_profattr(pap); 947 if (ret) 948 return (ret); 949 } 950 951 return (ret); 952 } 953 954 static int 955 perm_granted(const permcheck_t *pcp) 956 { 957 ucred_t *uc; 958 959 int ret = 0; 960 uid_t uid; 961 userattr_t *uap; 962 char *authlist, *userattr_authlist, *proflist, *def_prof = NULL; 963 964 /* 965 * Get generic authorizations from policy.conf 966 * 967 * Note that _get_auth_policy is not threadsafe, so we single-thread 968 * access to it. 969 */ 970 (void) pthread_mutex_lock(&perm_lock); 971 ret = _get_auth_policy(&authlist, &def_prof); 972 (void) pthread_mutex_unlock(&perm_lock); 973 974 if (ret != 0) 975 return (-1); 976 977 if (authlist != NULL) { 978 ret = check_auth_list(pcp, authlist); 979 980 if (ret) { 981 _free_auth_policy(authlist, def_prof); 982 return (ret); 983 } 984 } 985 986 /* 987 * Put off checking def_prof for later in an attempt to consolidate 988 * prof_attr accesses. 989 */ 990 991 /* Get the uid */ 992 if ((uc = get_ucred()) == NULL) { 993 _free_auth_policy(authlist, def_prof); 994 995 if (errno == EINVAL) { 996 /* 997 * Client is no longer waiting for our response (e.g., 998 * it received a signal & resumed with EINTR). 999 * Punting with door_return() would be nice but we 1000 * need to release all of the locks & references we 1001 * hold. And we must report failure to the client 1002 * layer to keep it from ignoring retries as 1003 * already-done (idempotency & all that). None of the 1004 * error codes fit very well, so we might as well 1005 * force the return of _PERMISSION_DENIED since we 1006 * couldn't determine the user. 1007 */ 1008 return (0); 1009 } 1010 assert(0); 1011 abort(); 1012 } 1013 1014 uid = ucred_geteuid(uc); 1015 assert(uid != (uid_t)-1); 1016 1017 uap = getuseruid(uid); 1018 if (uap != NULL) { 1019 /* Get the authorizations from user_attr. */ 1020 userattr_authlist = kva_match(uap->attr, USERATTR_AUTHS_KW); 1021 if (userattr_authlist != NULL) 1022 ret = check_auth_list(pcp, userattr_authlist); 1023 } 1024 1025 if (!ret && def_prof != NULL) { 1026 /* Check generic profiles. */ 1027 ret = check_prof_list(pcp, def_prof); 1028 } 1029 1030 if (!ret && uap != NULL) { 1031 proflist = kva_match(uap->attr, USERATTR_PROFILES_KW); 1032 if (proflist != NULL) 1033 ret = check_prof_list(pcp, proflist); 1034 } 1035 1036 _free_auth_policy(authlist, def_prof); 1037 if (uap != NULL) 1038 free_userattr(uap); 1039 1040 return (ret); 1041 } 1042 #endif /* NATIVE_BUILD */ 1043 1044 /* 1045 * flags in RC_NODE_WAITING_FLAGS are broadcast when unset, and are used to 1046 * serialize certain actions, and to wait for certain operations to complete 1047 * 1048 * The waiting flags are: 1049 * RC_NODE_CHILDREN_CHANGING 1050 * The child list is being built or changed (due to creation 1051 * or deletion). All iterators pause. 1052 * 1053 * RC_NODE_USING_PARENT 1054 * Someone is actively using the parent pointer, so we can't 1055 * be removed from the parent list. 1056 * 1057 * RC_NODE_CREATING_CHILD 1058 * A child is being created -- locks out other creations, to 1059 * prevent insert-insert races. 1060 * 1061 * RC_NODE_IN_TX 1062 * This object is running a transaction. 1063 * 1064 * RC_NODE_DYING 1065 * This node might be dying. Always set as a set, using 1066 * RC_NODE_DYING_FLAGS (which is everything but 1067 * RC_NODE_USING_PARENT) 1068 */ 1069 static int 1070 rc_node_hold_flag(rc_node_t *np, uint32_t flag) 1071 { 1072 assert(MUTEX_HELD(&np->rn_lock)); 1073 assert((flag & ~RC_NODE_WAITING_FLAGS) == 0); 1074 1075 while (!(np->rn_flags & RC_NODE_DEAD) && (np->rn_flags & flag)) { 1076 (void) pthread_cond_wait(&np->rn_cv, &np->rn_lock); 1077 } 1078 if (np->rn_flags & RC_NODE_DEAD) 1079 return (0); 1080 1081 np->rn_flags |= flag; 1082 return (1); 1083 } 1084 1085 static void 1086 rc_node_rele_flag(rc_node_t *np, uint32_t flag) 1087 { 1088 assert((flag & ~RC_NODE_WAITING_FLAGS) == 0); 1089 assert(MUTEX_HELD(&np->rn_lock)); 1090 assert((np->rn_flags & flag) == flag); 1091 np->rn_flags &= ~flag; 1092 (void) pthread_cond_broadcast(&np->rn_cv); 1093 } 1094 1095 /* 1096 * wait until a particular flag has cleared. Fails if the object dies. 1097 */ 1098 static int 1099 rc_node_wait_flag(rc_node_t *np, uint32_t flag) 1100 { 1101 assert(MUTEX_HELD(&np->rn_lock)); 1102 while (!(np->rn_flags & RC_NODE_DEAD) && (np->rn_flags & flag)) 1103 (void) pthread_cond_wait(&np->rn_cv, &np->rn_lock); 1104 1105 return (!(np->rn_flags & RC_NODE_DEAD)); 1106 } 1107 1108 /* 1109 * On entry, np's lock must be held, and this thread must be holding 1110 * RC_NODE_USING_PARENT. On return, both of them are released. 1111 * 1112 * If the return value is NULL, np either does not have a parent, or 1113 * the parent has been marked DEAD. 1114 * 1115 * If the return value is non-NULL, it is the parent of np, and both 1116 * its lock and the requested flags are held. 1117 */ 1118 static rc_node_t * 1119 rc_node_hold_parent_flag(rc_node_t *np, uint32_t flag) 1120 { 1121 rc_node_t *pp; 1122 1123 assert(MUTEX_HELD(&np->rn_lock)); 1124 assert(np->rn_flags & RC_NODE_USING_PARENT); 1125 1126 if ((pp = np->rn_parent) == NULL) { 1127 rc_node_rele_flag(np, RC_NODE_USING_PARENT); 1128 (void) pthread_mutex_unlock(&np->rn_lock); 1129 return (NULL); 1130 } 1131 (void) pthread_mutex_unlock(&np->rn_lock); 1132 1133 (void) pthread_mutex_lock(&pp->rn_lock); 1134 (void) pthread_mutex_lock(&np->rn_lock); 1135 rc_node_rele_flag(np, RC_NODE_USING_PARENT); 1136 (void) pthread_mutex_unlock(&np->rn_lock); 1137 1138 if (!rc_node_hold_flag(pp, flag)) { 1139 (void) pthread_mutex_unlock(&pp->rn_lock); 1140 return (NULL); 1141 } 1142 return (pp); 1143 } 1144 1145 rc_node_t * 1146 rc_node_alloc(void) 1147 { 1148 rc_node_t *np = uu_zalloc(sizeof (*np)); 1149 1150 if (np == NULL) 1151 return (NULL); 1152 1153 (void) pthread_mutex_init(&np->rn_lock, NULL); 1154 (void) pthread_cond_init(&np->rn_cv, NULL); 1155 1156 np->rn_children = uu_list_create(rc_children_pool, np, 0); 1157 np->rn_pg_notify_list = uu_list_create(rc_pg_notify_pool, np, 0); 1158 1159 uu_list_node_init(np, &np->rn_sibling_node, rc_children_pool); 1160 1161 uu_list_node_init(&np->rn_notify, &np->rn_notify.rcn_list_node, 1162 rc_notify_pool); 1163 1164 return (np); 1165 } 1166 1167 void 1168 rc_node_destroy(rc_node_t *np) 1169 { 1170 int i; 1171 1172 if (np->rn_flags & RC_NODE_UNREFED) 1173 return; /* being handled elsewhere */ 1174 1175 assert(np->rn_refs == 0 && np->rn_other_refs == 0); 1176 assert(np->rn_former == NULL); 1177 1178 if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) { 1179 /* Release the holds from rc_iter_next(). */ 1180 for (i = 0; i < COMPOSITION_DEPTH; ++i) { 1181 /* rn_cchain[i] may be NULL for empty snapshots. */ 1182 if (np->rn_cchain[i] != NULL) 1183 rc_node_rele(np->rn_cchain[i]); 1184 } 1185 } 1186 1187 if (np->rn_name != NULL) 1188 free((void *)np->rn_name); 1189 np->rn_name = NULL; 1190 if (np->rn_type != NULL) 1191 free((void *)np->rn_type); 1192 np->rn_type = NULL; 1193 if (np->rn_values != NULL) 1194 object_free_values(np->rn_values, np->rn_valtype, 1195 np->rn_values_count, np->rn_values_size); 1196 np->rn_values = NULL; 1197 1198 if (np->rn_snaplevel != NULL) 1199 rc_snaplevel_rele(np->rn_snaplevel); 1200 np->rn_snaplevel = NULL; 1201 1202 uu_list_node_fini(np, &np->rn_sibling_node, rc_children_pool); 1203 1204 uu_list_node_fini(&np->rn_notify, &np->rn_notify.rcn_list_node, 1205 rc_notify_pool); 1206 1207 assert(uu_list_first(np->rn_children) == NULL); 1208 uu_list_destroy(np->rn_children); 1209 uu_list_destroy(np->rn_pg_notify_list); 1210 1211 (void) pthread_mutex_destroy(&np->rn_lock); 1212 (void) pthread_cond_destroy(&np->rn_cv); 1213 1214 uu_free(np); 1215 } 1216 1217 /* 1218 * Link in a child node. 1219 * 1220 * Because of the lock ordering, cp has to already be in the hash table with 1221 * its lock dropped before we get it. To prevent anyone from noticing that 1222 * it is parentless, the creation code sets the RC_NODE_USING_PARENT. Once 1223 * we've linked it in, we release the flag. 1224 */ 1225 static void 1226 rc_node_link_child(rc_node_t *np, rc_node_t *cp) 1227 { 1228 assert(!MUTEX_HELD(&np->rn_lock)); 1229 assert(!MUTEX_HELD(&cp->rn_lock)); 1230 1231 (void) pthread_mutex_lock(&np->rn_lock); 1232 (void) pthread_mutex_lock(&cp->rn_lock); 1233 assert(!(cp->rn_flags & RC_NODE_IN_PARENT) && 1234 (cp->rn_flags & RC_NODE_USING_PARENT)); 1235 1236 assert(rc_check_parent_child(np->rn_id.rl_type, cp->rn_id.rl_type) == 1237 REP_PROTOCOL_SUCCESS); 1238 1239 cp->rn_parent = np; 1240 cp->rn_flags |= RC_NODE_IN_PARENT; 1241 (void) uu_list_insert_before(np->rn_children, NULL, cp); 1242 1243 (void) pthread_mutex_unlock(&np->rn_lock); 1244 1245 rc_node_rele_flag(cp, RC_NODE_USING_PARENT); 1246 (void) pthread_mutex_unlock(&cp->rn_lock); 1247 } 1248 1249 /* 1250 * Sets the rn_parent_ref field of all the children of np to pp -- always 1251 * initially invoked as rc_node_setup_parent_ref(np, np), we then recurse. 1252 * 1253 * This is used when we mark a node RC_NODE_OLD, so that when the object and 1254 * its children are no longer referenced, they will all be deleted as a unit. 1255 */ 1256 static void 1257 rc_node_setup_parent_ref(rc_node_t *np, rc_node_t *pp) 1258 { 1259 rc_node_t *cp; 1260 1261 assert(MUTEX_HELD(&np->rn_lock)); 1262 1263 for (cp = uu_list_first(np->rn_children); cp != NULL; 1264 cp = uu_list_next(np->rn_children, cp)) { 1265 (void) pthread_mutex_lock(&cp->rn_lock); 1266 if (cp->rn_flags & RC_NODE_PARENT_REF) { 1267 assert(cp->rn_parent_ref == pp); 1268 } else { 1269 assert(cp->rn_parent_ref == NULL); 1270 1271 cp->rn_flags |= RC_NODE_PARENT_REF; 1272 cp->rn_parent_ref = pp; 1273 if (cp->rn_refs != 0) 1274 rc_node_hold_other(pp); 1275 } 1276 rc_node_setup_parent_ref(cp, pp); /* recurse */ 1277 (void) pthread_mutex_unlock(&cp->rn_lock); 1278 } 1279 } 1280 1281 /* 1282 * Atomically replace 'np' with 'newp', with a parent of 'pp'. 1283 * 1284 * Requirements: 1285 * *no* node locks may be held. 1286 * pp must be held with RC_NODE_CHILDREN_CHANGING 1287 * newp and np must be held with RC_NODE_IN_TX 1288 * np must be marked RC_NODE_IN_PARENT, newp must not be 1289 * np must be marked RC_NODE_OLD 1290 * 1291 * Afterwards: 1292 * pp's RC_NODE_CHILDREN_CHANGING is dropped 1293 * newp and np's RC_NODE_IN_TX is dropped 1294 * newp->rn_former = np; 1295 * newp is RC_NODE_IN_PARENT, np is not. 1296 * interested notify subscribers have been notified of newp's new status. 1297 */ 1298 static void 1299 rc_node_relink_child(rc_node_t *pp, rc_node_t *np, rc_node_t *newp) 1300 { 1301 cache_bucket_t *bp; 1302 /* 1303 * First, swap np and nnp in the cache. newp's RC_NODE_IN_TX flag 1304 * keeps rc_node_update() from seeing it until we are done. 1305 */ 1306 bp = cache_hold(newp->rn_hash); 1307 cache_remove_unlocked(bp, np); 1308 cache_insert_unlocked(bp, newp); 1309 cache_release(bp); 1310 1311 /* 1312 * replace np with newp in pp's list, and attach it to newp's rn_former 1313 * link. 1314 */ 1315 (void) pthread_mutex_lock(&pp->rn_lock); 1316 assert(pp->rn_flags & RC_NODE_CHILDREN_CHANGING); 1317 1318 (void) pthread_mutex_lock(&newp->rn_lock); 1319 assert(!(newp->rn_flags & RC_NODE_IN_PARENT)); 1320 assert(newp->rn_flags & RC_NODE_IN_TX); 1321 1322 (void) pthread_mutex_lock(&np->rn_lock); 1323 assert(np->rn_flags & RC_NODE_IN_PARENT); 1324 assert(np->rn_flags & RC_NODE_OLD); 1325 assert(np->rn_flags & RC_NODE_IN_TX); 1326 1327 newp->rn_parent = pp; 1328 newp->rn_flags |= RC_NODE_IN_PARENT; 1329 1330 /* 1331 * Note that we carefully add newp before removing np -- this 1332 * keeps iterators on the list from missing us. 1333 */ 1334 (void) uu_list_insert_after(pp->rn_children, np, newp); 1335 (void) uu_list_remove(pp->rn_children, np); 1336 1337 /* 1338 * re-set np 1339 */ 1340 newp->rn_former = np; 1341 np->rn_parent = NULL; 1342 np->rn_flags &= ~RC_NODE_IN_PARENT; 1343 np->rn_flags |= RC_NODE_ON_FORMER; 1344 1345 rc_notify_insert_node(newp); 1346 1347 rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING); 1348 (void) pthread_mutex_unlock(&pp->rn_lock); 1349 rc_node_rele_flag(newp, RC_NODE_USING_PARENT | RC_NODE_IN_TX); 1350 (void) pthread_mutex_unlock(&newp->rn_lock); 1351 rc_node_setup_parent_ref(np, np); 1352 rc_node_rele_flag(np, RC_NODE_IN_TX); 1353 (void) pthread_mutex_unlock(&np->rn_lock); 1354 } 1355 1356 /* 1357 * makes sure a node with lookup 'nip', name 'name', and parent 'pp' exists. 1358 * 'cp' is used (and returned) if the node does not yet exist. If it does 1359 * exist, 'cp' is freed, and the existent node is returned instead. 1360 */ 1361 rc_node_t * 1362 rc_node_setup(rc_node_t *cp, rc_node_lookup_t *nip, const char *name, 1363 rc_node_t *pp) 1364 { 1365 rc_node_t *np; 1366 cache_bucket_t *bp; 1367 uint32_t h = rc_node_hash(nip); 1368 1369 assert(cp->rn_refs == 0); 1370 1371 bp = cache_hold(h); 1372 if ((np = cache_lookup_unlocked(bp, nip)) != NULL) { 1373 cache_release(bp); 1374 1375 /* 1376 * make sure it matches our expectations 1377 */ 1378 (void) pthread_mutex_lock(&np->rn_lock); 1379 if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) { 1380 assert(np->rn_parent == pp); 1381 assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0); 1382 assert(strcmp(np->rn_name, name) == 0); 1383 assert(np->rn_type == NULL); 1384 assert(np->rn_flags & RC_NODE_IN_PARENT); 1385 rc_node_rele_flag(np, RC_NODE_USING_PARENT); 1386 } 1387 (void) pthread_mutex_unlock(&np->rn_lock); 1388 1389 rc_node_destroy(cp); 1390 return (np); 1391 } 1392 1393 /* 1394 * No one is there -- create a new node. 1395 */ 1396 np = cp; 1397 rc_node_hold(np); 1398 np->rn_id = *nip; 1399 np->rn_hash = h; 1400 np->rn_name = strdup(name); 1401 1402 np->rn_flags |= RC_NODE_USING_PARENT; 1403 1404 if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE) { 1405 #if COMPOSITION_DEPTH == 2 1406 np->rn_cchain[0] = np; 1407 np->rn_cchain[1] = pp; 1408 #else 1409 #error This code must be updated. 1410 #endif 1411 } 1412 1413 cache_insert_unlocked(bp, np); 1414 cache_release(bp); /* we are now visible */ 1415 1416 rc_node_link_child(pp, np); 1417 1418 return (np); 1419 } 1420 1421 /* 1422 * makes sure a snapshot with lookup 'nip', name 'name', and parent 'pp' exists. 1423 * 'cp' is used (and returned) if the node does not yet exist. If it does 1424 * exist, 'cp' is freed, and the existent node is returned instead. 1425 */ 1426 rc_node_t * 1427 rc_node_setup_snapshot(rc_node_t *cp, rc_node_lookup_t *nip, const char *name, 1428 uint32_t snap_id, rc_node_t *pp) 1429 { 1430 rc_node_t *np; 1431 cache_bucket_t *bp; 1432 uint32_t h = rc_node_hash(nip); 1433 1434 assert(cp->rn_refs == 0); 1435 1436 bp = cache_hold(h); 1437 if ((np = cache_lookup_unlocked(bp, nip)) != NULL) { 1438 cache_release(bp); 1439 1440 /* 1441 * make sure it matches our expectations 1442 */ 1443 (void) pthread_mutex_lock(&np->rn_lock); 1444 if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) { 1445 assert(np->rn_parent == pp); 1446 assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0); 1447 assert(strcmp(np->rn_name, name) == 0); 1448 assert(np->rn_type == NULL); 1449 assert(np->rn_flags & RC_NODE_IN_PARENT); 1450 rc_node_rele_flag(np, RC_NODE_USING_PARENT); 1451 } 1452 (void) pthread_mutex_unlock(&np->rn_lock); 1453 1454 rc_node_destroy(cp); 1455 return (np); 1456 } 1457 1458 /* 1459 * No one is there -- create a new node. 1460 */ 1461 np = cp; 1462 rc_node_hold(np); 1463 np->rn_id = *nip; 1464 np->rn_hash = h; 1465 np->rn_name = strdup(name); 1466 np->rn_snapshot_id = snap_id; 1467 1468 np->rn_flags |= RC_NODE_USING_PARENT; 1469 1470 cache_insert_unlocked(bp, np); 1471 cache_release(bp); /* we are now visible */ 1472 1473 rc_node_link_child(pp, np); 1474 1475 return (np); 1476 } 1477 1478 /* 1479 * makes sure a snaplevel with lookup 'nip' and parent 'pp' exists. 'cp' is 1480 * used (and returned) if the node does not yet exist. If it does exist, 'cp' 1481 * is freed, and the existent node is returned instead. 1482 */ 1483 rc_node_t * 1484 rc_node_setup_snaplevel(rc_node_t *cp, rc_node_lookup_t *nip, 1485 rc_snaplevel_t *lvl, rc_node_t *pp) 1486 { 1487 rc_node_t *np; 1488 cache_bucket_t *bp; 1489 uint32_t h = rc_node_hash(nip); 1490 1491 assert(cp->rn_refs == 0); 1492 1493 bp = cache_hold(h); 1494 if ((np = cache_lookup_unlocked(bp, nip)) != NULL) { 1495 cache_release(bp); 1496 1497 /* 1498 * make sure it matches our expectations 1499 */ 1500 (void) pthread_mutex_lock(&np->rn_lock); 1501 if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) { 1502 assert(np->rn_parent == pp); 1503 assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0); 1504 assert(np->rn_name == NULL); 1505 assert(np->rn_type == NULL); 1506 assert(np->rn_flags & RC_NODE_IN_PARENT); 1507 rc_node_rele_flag(np, RC_NODE_USING_PARENT); 1508 } 1509 (void) pthread_mutex_unlock(&np->rn_lock); 1510 1511 rc_node_destroy(cp); 1512 return (np); 1513 } 1514 1515 /* 1516 * No one is there -- create a new node. 1517 */ 1518 np = cp; 1519 rc_node_hold(np); /* released in snapshot_fill_children() */ 1520 np->rn_id = *nip; 1521 np->rn_hash = h; 1522 1523 rc_snaplevel_hold(lvl); 1524 np->rn_snaplevel = lvl; 1525 1526 np->rn_flags |= RC_NODE_USING_PARENT; 1527 1528 cache_insert_unlocked(bp, np); 1529 cache_release(bp); /* we are now visible */ 1530 1531 /* Add this snaplevel to the snapshot's composition chain. */ 1532 assert(pp->rn_cchain[lvl->rsl_level_num - 1] == NULL); 1533 pp->rn_cchain[lvl->rsl_level_num - 1] = np; 1534 1535 rc_node_link_child(pp, np); 1536 1537 return (np); 1538 } 1539 1540 /* 1541 * Returns NULL if strdup() fails. 1542 */ 1543 rc_node_t * 1544 rc_node_setup_pg(rc_node_t *cp, rc_node_lookup_t *nip, const char *name, 1545 const char *type, uint32_t flags, uint32_t gen_id, rc_node_t *pp) 1546 { 1547 rc_node_t *np; 1548 cache_bucket_t *bp; 1549 1550 uint32_t h = rc_node_hash(nip); 1551 bp = cache_hold(h); 1552 if ((np = cache_lookup_unlocked(bp, nip)) != NULL) { 1553 cache_release(bp); 1554 1555 /* 1556 * make sure it matches our expectations (don't check 1557 * the generation number or parent, since someone could 1558 * have gotten a transaction through while we weren't 1559 * looking) 1560 */ 1561 (void) pthread_mutex_lock(&np->rn_lock); 1562 if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) { 1563 assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0); 1564 assert(strcmp(np->rn_name, name) == 0); 1565 assert(strcmp(np->rn_type, type) == 0); 1566 assert(np->rn_pgflags == flags); 1567 assert(np->rn_flags & RC_NODE_IN_PARENT); 1568 rc_node_rele_flag(np, RC_NODE_USING_PARENT); 1569 } 1570 (void) pthread_mutex_unlock(&np->rn_lock); 1571 1572 rc_node_destroy(cp); 1573 return (np); 1574 } 1575 1576 np = cp; 1577 rc_node_hold(np); /* released in fill_pg_callback() */ 1578 np->rn_id = *nip; 1579 np->rn_hash = h; 1580 np->rn_name = strdup(name); 1581 if (np->rn_name == NULL) { 1582 rc_node_rele(np); 1583 return (NULL); 1584 } 1585 np->rn_type = strdup(type); 1586 if (np->rn_type == NULL) { 1587 free((void *)np->rn_name); 1588 rc_node_rele(np); 1589 return (NULL); 1590 } 1591 np->rn_pgflags = flags; 1592 np->rn_gen_id = gen_id; 1593 1594 np->rn_flags |= RC_NODE_USING_PARENT; 1595 1596 cache_insert_unlocked(bp, np); 1597 cache_release(bp); /* we are now visible */ 1598 1599 rc_node_link_child(pp, np); 1600 1601 return (np); 1602 } 1603 1604 #if COMPOSITION_DEPTH == 2 1605 /* 1606 * Initialize a "composed property group" which represents the composition of 1607 * property groups pg1 & pg2. It is ephemeral: once created & returned for an 1608 * ITER_READ request, keeping it out of cache_hash and any child lists 1609 * prevents it from being looked up. Operations besides iteration are passed 1610 * through to pg1. 1611 * 1612 * pg1 & pg2 should be held before entering this function. They will be 1613 * released in rc_node_destroy(). 1614 */ 1615 static int 1616 rc_node_setup_cpg(rc_node_t *cpg, rc_node_t *pg1, rc_node_t *pg2) 1617 { 1618 if (strcmp(pg1->rn_type, pg2->rn_type) != 0) 1619 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH); 1620 1621 cpg->rn_id.rl_type = REP_PROTOCOL_ENTITY_CPROPERTYGRP; 1622 cpg->rn_name = strdup(pg1->rn_name); 1623 if (cpg->rn_name == NULL) 1624 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 1625 1626 cpg->rn_cchain[0] = pg1; 1627 cpg->rn_cchain[1] = pg2; 1628 1629 return (REP_PROTOCOL_SUCCESS); 1630 } 1631 #else 1632 #error This code must be updated. 1633 #endif 1634 1635 /* 1636 * Fails with _NO_RESOURCES. 1637 */ 1638 int 1639 rc_node_create_property(rc_node_t *pp, rc_node_lookup_t *nip, 1640 const char *name, rep_protocol_value_type_t type, 1641 const char *vals, size_t count, size_t size) 1642 { 1643 rc_node_t *np; 1644 cache_bucket_t *bp; 1645 1646 uint32_t h = rc_node_hash(nip); 1647 bp = cache_hold(h); 1648 if ((np = cache_lookup_unlocked(bp, nip)) != NULL) { 1649 cache_release(bp); 1650 /* 1651 * make sure it matches our expectations 1652 */ 1653 (void) pthread_mutex_lock(&np->rn_lock); 1654 if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) { 1655 assert(np->rn_parent == pp); 1656 assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0); 1657 assert(strcmp(np->rn_name, name) == 0); 1658 assert(np->rn_valtype == type); 1659 assert(np->rn_values_count == count); 1660 assert(np->rn_values_size == size); 1661 assert(vals == NULL || 1662 memcmp(np->rn_values, vals, size) == 0); 1663 assert(np->rn_flags & RC_NODE_IN_PARENT); 1664 rc_node_rele_flag(np, RC_NODE_USING_PARENT); 1665 } 1666 rc_node_rele_locked(np); 1667 object_free_values(vals, type, count, size); 1668 return (REP_PROTOCOL_SUCCESS); 1669 } 1670 1671 /* 1672 * No one is there -- create a new node. 1673 */ 1674 np = rc_node_alloc(); 1675 if (np == NULL) { 1676 cache_release(bp); 1677 object_free_values(vals, type, count, size); 1678 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 1679 } 1680 np->rn_id = *nip; 1681 np->rn_hash = h; 1682 np->rn_name = strdup(name); 1683 if (np->rn_name == NULL) { 1684 cache_release(bp); 1685 object_free_values(vals, type, count, size); 1686 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 1687 } 1688 1689 np->rn_valtype = type; 1690 np->rn_values = vals; 1691 np->rn_values_count = count; 1692 np->rn_values_size = size; 1693 1694 np->rn_flags |= RC_NODE_USING_PARENT; 1695 1696 cache_insert_unlocked(bp, np); 1697 cache_release(bp); /* we are now visible */ 1698 1699 rc_node_link_child(pp, np); 1700 1701 return (REP_PROTOCOL_SUCCESS); 1702 } 1703 1704 int 1705 rc_node_init(void) 1706 { 1707 rc_node_t *np; 1708 cache_bucket_t *bp; 1709 1710 rc_children_pool = uu_list_pool_create("rc_children_pool", 1711 sizeof (rc_node_t), offsetof(rc_node_t, rn_sibling_node), 1712 NULL, UU_LIST_POOL_DEBUG); 1713 1714 rc_pg_notify_pool = uu_list_pool_create("rc_pg_notify_pool", 1715 sizeof (rc_node_pg_notify_t), 1716 offsetof(rc_node_pg_notify_t, rnpn_node), 1717 NULL, UU_LIST_POOL_DEBUG); 1718 1719 rc_notify_pool = uu_list_pool_create("rc_notify_pool", 1720 sizeof (rc_notify_t), offsetof(rc_notify_t, rcn_list_node), 1721 NULL, UU_LIST_POOL_DEBUG); 1722 1723 rc_notify_info_pool = uu_list_pool_create("rc_notify_info_pool", 1724 sizeof (rc_notify_info_t), 1725 offsetof(rc_notify_info_t, rni_list_node), 1726 NULL, UU_LIST_POOL_DEBUG); 1727 1728 if (rc_children_pool == NULL || rc_pg_notify_pool == NULL || 1729 rc_notify_pool == NULL || rc_notify_info_pool == NULL) 1730 uu_die("out of memory"); 1731 1732 rc_notify_list = uu_list_create(rc_notify_pool, 1733 &rc_notify_list, 0); 1734 1735 rc_notify_info_list = uu_list_create(rc_notify_info_pool, 1736 &rc_notify_info_list, 0); 1737 1738 if (rc_notify_list == NULL || rc_notify_info_list == NULL) 1739 uu_die("out of memory"); 1740 1741 if ((np = rc_node_alloc()) == NULL) 1742 uu_die("out of memory"); 1743 1744 rc_node_hold(np); 1745 np->rn_id.rl_type = REP_PROTOCOL_ENTITY_SCOPE; 1746 np->rn_id.rl_backend = BACKEND_TYPE_NORMAL; 1747 np->rn_hash = rc_node_hash(&np->rn_id); 1748 np->rn_name = "localhost"; 1749 1750 bp = cache_hold(np->rn_hash); 1751 cache_insert_unlocked(bp, np); 1752 cache_release(bp); 1753 1754 rc_scope = np; 1755 return (1); 1756 } 1757 1758 /* 1759 * Fails with 1760 * _INVALID_TYPE - type is invalid 1761 * _TYPE_MISMATCH - np doesn't carry children of type type 1762 * _DELETED - np has been deleted 1763 * _NO_RESOURCES 1764 */ 1765 static int 1766 rc_node_fill_children(rc_node_t *np, uint32_t type) 1767 { 1768 int rc; 1769 1770 assert(MUTEX_HELD(&np->rn_lock)); 1771 1772 if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) != 1773 REP_PROTOCOL_SUCCESS) 1774 return (rc); 1775 1776 if (!rc_node_hold_flag(np, RC_NODE_CHILDREN_CHANGING)) 1777 return (REP_PROTOCOL_FAIL_DELETED); 1778 1779 if (np->rn_flags & RC_NODE_HAS_CHILDREN) { 1780 rc_node_rele_flag(np, RC_NODE_CHILDREN_CHANGING); 1781 return (REP_PROTOCOL_SUCCESS); 1782 } 1783 1784 (void) pthread_mutex_unlock(&np->rn_lock); 1785 rc = object_fill_children(np); 1786 (void) pthread_mutex_lock(&np->rn_lock); 1787 1788 if (rc == REP_PROTOCOL_SUCCESS) { 1789 np->rn_flags |= RC_NODE_HAS_CHILDREN; 1790 } 1791 rc_node_rele_flag(np, RC_NODE_CHILDREN_CHANGING); 1792 1793 return (rc); 1794 } 1795 1796 /* 1797 * Returns 1798 * _INVALID_TYPE - type is invalid 1799 * _TYPE_MISMATCH - np doesn't carry children of type type 1800 * _DELETED - np has been deleted 1801 * _NO_RESOURCES 1802 * _SUCCESS - if *cpp is not NULL, it is held 1803 */ 1804 static int 1805 rc_node_find_named_child(rc_node_t *np, const char *name, uint32_t type, 1806 rc_node_t **cpp) 1807 { 1808 int ret; 1809 rc_node_t *cp; 1810 1811 assert(MUTEX_HELD(&np->rn_lock)); 1812 assert(np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP); 1813 1814 ret = rc_node_fill_children(np, type); 1815 if (ret != REP_PROTOCOL_SUCCESS) 1816 return (ret); 1817 1818 for (cp = uu_list_first(np->rn_children); 1819 cp != NULL; 1820 cp = uu_list_next(np->rn_children, cp)) { 1821 if (cp->rn_id.rl_type == type && strcmp(cp->rn_name, name) == 0) 1822 break; 1823 } 1824 1825 if (cp != NULL) 1826 rc_node_hold(cp); 1827 *cpp = cp; 1828 1829 return (REP_PROTOCOL_SUCCESS); 1830 } 1831 1832 #ifndef NATIVE_BUILD 1833 static int rc_node_parent(rc_node_t *, rc_node_t **); 1834 1835 /* 1836 * If the propname property exists in pg, and it is of type string, add its 1837 * values as authorizations to pcp. pg must not be locked on entry, and it is 1838 * returned unlocked. Returns 1839 * _DELETED - pg was deleted 1840 * _NO_RESOURCES 1841 * _NOT_FOUND - pg has no property named propname 1842 * _SUCCESS 1843 */ 1844 static int 1845 perm_add_pg_prop_values(permcheck_t *pcp, rc_node_t *pg, const char *propname) 1846 { 1847 rc_node_t *prop; 1848 int result; 1849 1850 uint_t count; 1851 const char *cp; 1852 1853 assert(!MUTEX_HELD(&pg->rn_lock)); 1854 assert(pg->rn_id.rl_type == REP_PROTOCOL_ENTITY_PROPERTYGRP); 1855 assert(pg->rn_id.rl_ids[ID_SNAPSHOT] == 0); 1856 1857 (void) pthread_mutex_lock(&pg->rn_lock); 1858 result = rc_node_find_named_child(pg, propname, 1859 REP_PROTOCOL_ENTITY_PROPERTY, &prop); 1860 (void) pthread_mutex_unlock(&pg->rn_lock); 1861 if (result != REP_PROTOCOL_SUCCESS) { 1862 switch (result) { 1863 case REP_PROTOCOL_FAIL_DELETED: 1864 case REP_PROTOCOL_FAIL_NO_RESOURCES: 1865 return (result); 1866 1867 case REP_PROTOCOL_FAIL_INVALID_TYPE: 1868 case REP_PROTOCOL_FAIL_TYPE_MISMATCH: 1869 default: 1870 bad_error("rc_node_find_named_child", result); 1871 } 1872 } 1873 1874 if (prop == NULL) 1875 return (REP_PROTOCOL_FAIL_NOT_FOUND); 1876 1877 /* rn_valtype is immutable, so no locking. */ 1878 if (prop->rn_valtype != REP_PROTOCOL_TYPE_STRING) { 1879 rc_node_rele(prop); 1880 return (REP_PROTOCOL_SUCCESS); 1881 } 1882 1883 (void) pthread_mutex_lock(&prop->rn_lock); 1884 for (count = prop->rn_values_count, cp = prop->rn_values; 1885 count > 0; 1886 --count) { 1887 result = perm_add_enabling(pcp, cp); 1888 if (result != REP_PROTOCOL_SUCCESS) 1889 break; 1890 1891 cp = strchr(cp, '\0') + 1; 1892 } 1893 1894 rc_node_rele_locked(prop); 1895 1896 return (result); 1897 } 1898 1899 /* 1900 * Assuming that ent is a service or instance node, if the pgname property 1901 * group has type pgtype, and it has a propname property with string type, add 1902 * its values as authorizations to pcp. If pgtype is NULL, it is not checked. 1903 * Returns 1904 * _SUCCESS 1905 * _DELETED - ent was deleted 1906 * _NO_RESOURCES - no resources 1907 * _NOT_FOUND - ent does not have pgname pg or propname property 1908 */ 1909 static int 1910 perm_add_ent_prop_values(permcheck_t *pcp, rc_node_t *ent, const char *pgname, 1911 const char *pgtype, const char *propname) 1912 { 1913 int r; 1914 rc_node_t *pg; 1915 1916 assert(!MUTEX_HELD(&ent->rn_lock)); 1917 1918 (void) pthread_mutex_lock(&ent->rn_lock); 1919 r = rc_node_find_named_child(ent, pgname, 1920 REP_PROTOCOL_ENTITY_PROPERTYGRP, &pg); 1921 (void) pthread_mutex_unlock(&ent->rn_lock); 1922 1923 switch (r) { 1924 case REP_PROTOCOL_SUCCESS: 1925 break; 1926 1927 case REP_PROTOCOL_FAIL_DELETED: 1928 case REP_PROTOCOL_FAIL_NO_RESOURCES: 1929 return (r); 1930 1931 default: 1932 bad_error("rc_node_find_named_child", r); 1933 } 1934 1935 if (pg == NULL) 1936 return (REP_PROTOCOL_FAIL_NOT_FOUND); 1937 1938 if (pgtype == NULL || strcmp(pg->rn_type, pgtype) == 0) { 1939 r = perm_add_pg_prop_values(pcp, pg, propname); 1940 switch (r) { 1941 case REP_PROTOCOL_FAIL_DELETED: 1942 r = REP_PROTOCOL_FAIL_NOT_FOUND; 1943 break; 1944 1945 case REP_PROTOCOL_FAIL_NO_RESOURCES: 1946 case REP_PROTOCOL_SUCCESS: 1947 case REP_PROTOCOL_FAIL_NOT_FOUND: 1948 break; 1949 1950 default: 1951 bad_error("perm_add_pg_prop_values", r); 1952 } 1953 } 1954 1955 rc_node_rele(pg); 1956 1957 return (r); 1958 } 1959 1960 /* 1961 * If pg has a property named propname, and it string typed, add its values as 1962 * authorizations to pcp. If pg has no such property, and its parent is an 1963 * instance, walk up to the service and try doing the same with the property 1964 * of the same name from the property group of the same name. Returns 1965 * _SUCCESS 1966 * _NO_RESOURCES 1967 * _DELETED - pg (or an ancestor) was deleted 1968 */ 1969 static int 1970 perm_add_enabling_values(permcheck_t *pcp, rc_node_t *pg, const char *propname) 1971 { 1972 int r; 1973 1974 r = perm_add_pg_prop_values(pcp, pg, propname); 1975 1976 if (r == REP_PROTOCOL_FAIL_NOT_FOUND) { 1977 char pgname[REP_PROTOCOL_NAME_LEN + 1]; 1978 rc_node_t *inst, *svc; 1979 size_t sz; 1980 1981 assert(!MUTEX_HELD(&pg->rn_lock)); 1982 1983 if (pg->rn_id.rl_ids[ID_INSTANCE] == 0) { 1984 /* not an instance pg */ 1985 return (REP_PROTOCOL_SUCCESS); 1986 } 1987 1988 sz = strlcpy(pgname, pg->rn_name, sizeof (pgname)); 1989 assert(sz < sizeof (pgname)); 1990 1991 /* get pg's parent */ 1992 r = rc_node_parent(pg, &inst); 1993 if (r != REP_PROTOCOL_SUCCESS) { 1994 assert(r == REP_PROTOCOL_FAIL_DELETED); 1995 return (r); 1996 } 1997 1998 assert(inst->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE); 1999 2000 /* get instance's parent */ 2001 r = rc_node_parent(inst, &svc); 2002 rc_node_rele(inst); 2003 if (r != REP_PROTOCOL_SUCCESS) { 2004 assert(r == REP_PROTOCOL_FAIL_DELETED); 2005 return (r); 2006 } 2007 2008 assert(svc->rn_id.rl_type == REP_PROTOCOL_ENTITY_SERVICE); 2009 2010 r = perm_add_ent_prop_values(pcp, svc, pgname, NULL, propname); 2011 2012 rc_node_rele(svc); 2013 2014 if (r == REP_PROTOCOL_FAIL_NOT_FOUND) 2015 r = REP_PROTOCOL_SUCCESS; 2016 } 2017 2018 return (r); 2019 } 2020 2021 /* 2022 * Call perm_add_enabling_values() for the "action_authorization" property of 2023 * the "general" property group of inst. Returns 2024 * _DELETED - inst (or an ancestor) was deleted 2025 * _NO_RESOURCES 2026 * _SUCCESS 2027 */ 2028 static int 2029 perm_add_inst_action_auth(permcheck_t *pcp, rc_node_t *inst) 2030 { 2031 int r; 2032 rc_node_t *svc; 2033 2034 assert(inst->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE); 2035 2036 r = perm_add_ent_prop_values(pcp, inst, AUTH_PG_GENERAL, 2037 AUTH_PG_GENERAL_TYPE, AUTH_PROP_ACTION); 2038 2039 if (r != REP_PROTOCOL_FAIL_NOT_FOUND) 2040 return (r); 2041 2042 r = rc_node_parent(inst, &svc); 2043 if (r != REP_PROTOCOL_SUCCESS) { 2044 assert(r == REP_PROTOCOL_FAIL_DELETED); 2045 return (r); 2046 } 2047 2048 r = perm_add_ent_prop_values(pcp, svc, AUTH_PG_GENERAL, 2049 AUTH_PG_GENERAL_TYPE, AUTH_PROP_ACTION); 2050 2051 return (r == REP_PROTOCOL_FAIL_NOT_FOUND ? REP_PROTOCOL_SUCCESS : r); 2052 } 2053 #endif /* NATIVE_BUILD */ 2054 2055 void 2056 rc_node_ptr_init(rc_node_ptr_t *out) 2057 { 2058 out->rnp_node = NULL; 2059 out->rnp_authorized = 0; 2060 out->rnp_deleted = 0; 2061 } 2062 2063 static void 2064 rc_node_assign(rc_node_ptr_t *out, rc_node_t *val) 2065 { 2066 rc_node_t *cur = out->rnp_node; 2067 if (val != NULL) 2068 rc_node_hold(val); 2069 out->rnp_node = val; 2070 if (cur != NULL) 2071 rc_node_rele(cur); 2072 out->rnp_authorized = 0; 2073 out->rnp_deleted = 0; 2074 } 2075 2076 void 2077 rc_node_clear(rc_node_ptr_t *out, int deleted) 2078 { 2079 rc_node_assign(out, NULL); 2080 out->rnp_deleted = deleted; 2081 } 2082 2083 void 2084 rc_node_ptr_assign(rc_node_ptr_t *out, const rc_node_ptr_t *val) 2085 { 2086 rc_node_assign(out, val->rnp_node); 2087 } 2088 2089 /* 2090 * rc_node_check()/RC_NODE_CHECK() 2091 * generic "entry" checks, run before the use of an rc_node pointer. 2092 * 2093 * Fails with 2094 * _NOT_SET 2095 * _DELETED 2096 */ 2097 static int 2098 rc_node_check_and_lock(rc_node_t *np) 2099 { 2100 int result = REP_PROTOCOL_SUCCESS; 2101 if (np == NULL) 2102 return (REP_PROTOCOL_FAIL_NOT_SET); 2103 2104 (void) pthread_mutex_lock(&np->rn_lock); 2105 if (!rc_node_wait_flag(np, RC_NODE_DYING)) { 2106 result = REP_PROTOCOL_FAIL_DELETED; 2107 (void) pthread_mutex_unlock(&np->rn_lock); 2108 } 2109 2110 return (result); 2111 } 2112 2113 /* 2114 * Fails with 2115 * _NOT_SET - ptr is reset 2116 * _DELETED - node has been deleted 2117 */ 2118 static rc_node_t * 2119 rc_node_ptr_check_and_lock(rc_node_ptr_t *npp, int *res) 2120 { 2121 rc_node_t *np = npp->rnp_node; 2122 if (np == NULL) { 2123 if (npp->rnp_deleted) 2124 *res = REP_PROTOCOL_FAIL_DELETED; 2125 else 2126 *res = REP_PROTOCOL_FAIL_NOT_SET; 2127 return (NULL); 2128 } 2129 2130 (void) pthread_mutex_lock(&np->rn_lock); 2131 if (!rc_node_wait_flag(np, RC_NODE_DYING)) { 2132 (void) pthread_mutex_unlock(&np->rn_lock); 2133 rc_node_clear(npp, 1); 2134 *res = REP_PROTOCOL_FAIL_DELETED; 2135 return (NULL); 2136 } 2137 return (np); 2138 } 2139 2140 #define RC_NODE_CHECK_AND_LOCK(n) { \ 2141 int rc__res; \ 2142 if ((rc__res = rc_node_check_and_lock(n)) != REP_PROTOCOL_SUCCESS) \ 2143 return (rc__res); \ 2144 } 2145 2146 #define RC_NODE_CHECK(n) { \ 2147 RC_NODE_CHECK_AND_LOCK(n); \ 2148 (void) pthread_mutex_unlock(&(n)->rn_lock); \ 2149 } 2150 2151 #define RC_NODE_CHECK_AND_HOLD(n) { \ 2152 RC_NODE_CHECK_AND_LOCK(n); \ 2153 rc_node_hold_locked(n); \ 2154 (void) pthread_mutex_unlock(&(n)->rn_lock); \ 2155 } 2156 2157 #define RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp) { \ 2158 int rc__res; \ 2159 if (((np) = rc_node_ptr_check_and_lock(npp, &rc__res)) == NULL) \ 2160 return (rc__res); \ 2161 } 2162 2163 #define RC_NODE_PTR_GET_CHECK(np, npp) { \ 2164 RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp); \ 2165 (void) pthread_mutex_unlock(&(np)->rn_lock); \ 2166 } 2167 2168 #define RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp) { \ 2169 RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp); \ 2170 rc_node_hold_locked(np); \ 2171 (void) pthread_mutex_unlock(&(np)->rn_lock); \ 2172 } 2173 2174 #define HOLD_FLAG_OR_RETURN(np, flag) { \ 2175 assert(MUTEX_HELD(&(np)->rn_lock)); \ 2176 assert(!((np)->rn_flags & RC_NODE_DEAD)); \ 2177 if (!rc_node_hold_flag((np), flag)) { \ 2178 (void) pthread_mutex_unlock(&(np)->rn_lock); \ 2179 return (REP_PROTOCOL_FAIL_DELETED); \ 2180 } \ 2181 } 2182 2183 #define HOLD_PTR_FLAG_OR_RETURN(np, npp, flag) { \ 2184 assert(MUTEX_HELD(&(np)->rn_lock)); \ 2185 assert(!((np)->rn_flags & RC_NODE_DEAD)); \ 2186 if (!rc_node_hold_flag((np), flag)) { \ 2187 (void) pthread_mutex_unlock(&(np)->rn_lock); \ 2188 assert((np) == (npp)->rnp_node); \ 2189 rc_node_clear(npp, 1); \ 2190 return (REP_PROTOCOL_FAIL_DELETED); \ 2191 } \ 2192 } 2193 2194 int 2195 rc_local_scope(uint32_t type, rc_node_ptr_t *out) 2196 { 2197 if (type != REP_PROTOCOL_ENTITY_SCOPE) { 2198 rc_node_clear(out, 0); 2199 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH); 2200 } 2201 2202 /* 2203 * the main scope never gets destroyed 2204 */ 2205 rc_node_assign(out, rc_scope); 2206 2207 return (REP_PROTOCOL_SUCCESS); 2208 } 2209 2210 /* 2211 * Fails with 2212 * _NOT_SET - npp is not set 2213 * _DELETED - the node npp pointed at has been deleted 2214 * _TYPE_MISMATCH - type is not _SCOPE 2215 * _NOT_FOUND - scope has no parent 2216 */ 2217 static int 2218 rc_scope_parent_scope(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out) 2219 { 2220 rc_node_t *np; 2221 2222 rc_node_clear(out, 0); 2223 2224 RC_NODE_PTR_GET_CHECK(np, npp); 2225 2226 if (type != REP_PROTOCOL_ENTITY_SCOPE) 2227 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH); 2228 2229 return (REP_PROTOCOL_FAIL_NOT_FOUND); 2230 } 2231 2232 /* 2233 * Fails with 2234 * _NOT_SET 2235 * _DELETED 2236 * _NOT_APPLICABLE 2237 * _NOT_FOUND 2238 * _BAD_REQUEST 2239 * _TRUNCATED 2240 */ 2241 int 2242 rc_node_name(rc_node_ptr_t *npp, char *buf, size_t sz, uint32_t answertype, 2243 size_t *sz_out) 2244 { 2245 size_t actual; 2246 rc_node_t *np; 2247 2248 assert(sz == *sz_out); 2249 2250 RC_NODE_PTR_GET_CHECK(np, npp); 2251 2252 if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) { 2253 np = np->rn_cchain[0]; 2254 RC_NODE_CHECK(np); 2255 } 2256 2257 switch (answertype) { 2258 case RP_ENTITY_NAME_NAME: 2259 if (np->rn_name == NULL) 2260 return (REP_PROTOCOL_FAIL_NOT_APPLICABLE); 2261 actual = strlcpy(buf, np->rn_name, sz); 2262 break; 2263 case RP_ENTITY_NAME_PGTYPE: 2264 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) 2265 return (REP_PROTOCOL_FAIL_NOT_APPLICABLE); 2266 actual = strlcpy(buf, np->rn_type, sz); 2267 break; 2268 case RP_ENTITY_NAME_PGFLAGS: 2269 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) 2270 return (REP_PROTOCOL_FAIL_NOT_APPLICABLE); 2271 actual = snprintf(buf, sz, "%d", np->rn_pgflags); 2272 break; 2273 case RP_ENTITY_NAME_SNAPLEVEL_SCOPE: 2274 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL) 2275 return (REP_PROTOCOL_FAIL_NOT_APPLICABLE); 2276 actual = strlcpy(buf, np->rn_snaplevel->rsl_scope, sz); 2277 break; 2278 case RP_ENTITY_NAME_SNAPLEVEL_SERVICE: 2279 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL) 2280 return (REP_PROTOCOL_FAIL_NOT_APPLICABLE); 2281 actual = strlcpy(buf, np->rn_snaplevel->rsl_service, sz); 2282 break; 2283 case RP_ENTITY_NAME_SNAPLEVEL_INSTANCE: 2284 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL) 2285 return (REP_PROTOCOL_FAIL_NOT_APPLICABLE); 2286 if (np->rn_snaplevel->rsl_instance == NULL) 2287 return (REP_PROTOCOL_FAIL_NOT_FOUND); 2288 actual = strlcpy(buf, np->rn_snaplevel->rsl_instance, sz); 2289 break; 2290 default: 2291 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 2292 } 2293 if (actual >= sz) 2294 return (REP_PROTOCOL_FAIL_TRUNCATED); 2295 2296 *sz_out = actual; 2297 return (REP_PROTOCOL_SUCCESS); 2298 } 2299 2300 int 2301 rc_node_get_property_type(rc_node_ptr_t *npp, rep_protocol_value_type_t *out) 2302 { 2303 rc_node_t *np; 2304 2305 RC_NODE_PTR_GET_CHECK(np, npp); 2306 2307 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) 2308 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH); 2309 2310 *out = np->rn_valtype; 2311 2312 return (REP_PROTOCOL_SUCCESS); 2313 } 2314 2315 /* 2316 * Get np's parent. If np is deleted, returns _DELETED. Otherwise puts a hold 2317 * on the parent, returns a pointer to it in *out, and returns _SUCCESS. 2318 */ 2319 static int 2320 rc_node_parent(rc_node_t *np, rc_node_t **out) 2321 { 2322 rc_node_t *pnp; 2323 rc_node_t *np_orig; 2324 2325 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP) { 2326 RC_NODE_CHECK_AND_LOCK(np); 2327 } else { 2328 np = np->rn_cchain[0]; 2329 RC_NODE_CHECK_AND_LOCK(np); 2330 } 2331 2332 np_orig = np; 2333 rc_node_hold_locked(np); /* simplifies the remainder */ 2334 2335 for (;;) { 2336 if (!rc_node_wait_flag(np, 2337 RC_NODE_IN_TX | RC_NODE_USING_PARENT)) { 2338 rc_node_rele_locked(np); 2339 return (REP_PROTOCOL_FAIL_DELETED); 2340 } 2341 2342 if (!(np->rn_flags & RC_NODE_OLD)) 2343 break; 2344 2345 rc_node_rele_locked(np); 2346 np = cache_lookup(&np_orig->rn_id); 2347 assert(np != np_orig); 2348 2349 if (np == NULL) 2350 goto deleted; 2351 (void) pthread_mutex_lock(&np->rn_lock); 2352 } 2353 2354 /* guaranteed to succeed without dropping the lock */ 2355 if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) { 2356 (void) pthread_mutex_unlock(&np->rn_lock); 2357 *out = NULL; 2358 rc_node_rele(np); 2359 return (REP_PROTOCOL_FAIL_DELETED); 2360 } 2361 2362 assert(np->rn_parent != NULL); 2363 pnp = np->rn_parent; 2364 (void) pthread_mutex_unlock(&np->rn_lock); 2365 2366 (void) pthread_mutex_lock(&pnp->rn_lock); 2367 (void) pthread_mutex_lock(&np->rn_lock); 2368 rc_node_rele_flag(np, RC_NODE_USING_PARENT); 2369 (void) pthread_mutex_unlock(&np->rn_lock); 2370 2371 rc_node_hold_locked(pnp); 2372 2373 (void) pthread_mutex_unlock(&pnp->rn_lock); 2374 2375 rc_node_rele(np); 2376 *out = pnp; 2377 return (REP_PROTOCOL_SUCCESS); 2378 2379 deleted: 2380 rc_node_rele(np); 2381 return (REP_PROTOCOL_FAIL_DELETED); 2382 } 2383 2384 /* 2385 * Fails with 2386 * _NOT_SET 2387 * _DELETED 2388 */ 2389 static int 2390 rc_node_ptr_parent(rc_node_ptr_t *npp, rc_node_t **out) 2391 { 2392 rc_node_t *np; 2393 2394 RC_NODE_PTR_GET_CHECK(np, npp); 2395 2396 return (rc_node_parent(np, out)); 2397 } 2398 2399 /* 2400 * Fails with 2401 * _NOT_SET - npp is not set 2402 * _DELETED - the node npp pointed at has been deleted 2403 * _TYPE_MISMATCH - npp's node's parent is not of type type 2404 * 2405 * If npp points to a scope, can also fail with 2406 * _NOT_FOUND - scope has no parent 2407 */ 2408 int 2409 rc_node_get_parent(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out) 2410 { 2411 rc_node_t *pnp; 2412 int rc; 2413 2414 if (npp->rnp_node != NULL && 2415 npp->rnp_node->rn_id.rl_type == REP_PROTOCOL_ENTITY_SCOPE) 2416 return (rc_scope_parent_scope(npp, type, out)); 2417 2418 if ((rc = rc_node_ptr_parent(npp, &pnp)) != REP_PROTOCOL_SUCCESS) { 2419 rc_node_clear(out, 0); 2420 return (rc); 2421 } 2422 2423 if (type != pnp->rn_id.rl_type) { 2424 rc_node_rele(pnp); 2425 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH); 2426 } 2427 2428 rc_node_assign(out, pnp); 2429 rc_node_rele(pnp); 2430 2431 return (REP_PROTOCOL_SUCCESS); 2432 } 2433 2434 int 2435 rc_node_parent_type(rc_node_ptr_t *npp, uint32_t *type_out) 2436 { 2437 rc_node_t *pnp; 2438 int rc; 2439 2440 if (npp->rnp_node != NULL && 2441 npp->rnp_node->rn_id.rl_type == REP_PROTOCOL_ENTITY_SCOPE) { 2442 *type_out = REP_PROTOCOL_ENTITY_SCOPE; 2443 return (REP_PROTOCOL_SUCCESS); 2444 } 2445 2446 if ((rc = rc_node_ptr_parent(npp, &pnp)) != REP_PROTOCOL_SUCCESS) 2447 return (rc); 2448 2449 *type_out = pnp->rn_id.rl_type; 2450 2451 rc_node_rele(pnp); 2452 2453 return (REP_PROTOCOL_SUCCESS); 2454 } 2455 2456 /* 2457 * Fails with 2458 * _INVALID_TYPE - type is invalid 2459 * _TYPE_MISMATCH - np doesn't carry children of type type 2460 * _DELETED - np has been deleted 2461 * _NOT_FOUND - no child with that name/type combo found 2462 * _NO_RESOURCES 2463 * _BACKEND_ACCESS 2464 */ 2465 int 2466 rc_node_get_child(rc_node_ptr_t *npp, const char *name, uint32_t type, 2467 rc_node_ptr_t *outp) 2468 { 2469 rc_node_t *np, *cp; 2470 rc_node_t *child = NULL; 2471 int ret, idx; 2472 2473 RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp); 2474 if ((ret = rc_check_type_name(type, name)) == REP_PROTOCOL_SUCCESS) { 2475 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP) { 2476 ret = rc_node_find_named_child(np, name, type, &child); 2477 } else { 2478 (void) pthread_mutex_unlock(&np->rn_lock); 2479 ret = REP_PROTOCOL_SUCCESS; 2480 for (idx = 0; idx < COMPOSITION_DEPTH; idx++) { 2481 cp = np->rn_cchain[idx]; 2482 if (cp == NULL) 2483 break; 2484 RC_NODE_CHECK_AND_LOCK(cp); 2485 ret = rc_node_find_named_child(cp, name, type, 2486 &child); 2487 (void) pthread_mutex_unlock(&cp->rn_lock); 2488 /* 2489 * loop only if we succeeded, but no child of 2490 * the correct name was found. 2491 */ 2492 if (ret != REP_PROTOCOL_SUCCESS || 2493 child != NULL) 2494 break; 2495 } 2496 (void) pthread_mutex_lock(&np->rn_lock); 2497 } 2498 } 2499 (void) pthread_mutex_unlock(&np->rn_lock); 2500 2501 if (ret == REP_PROTOCOL_SUCCESS) { 2502 rc_node_assign(outp, child); 2503 if (child != NULL) 2504 rc_node_rele(child); 2505 else 2506 ret = REP_PROTOCOL_FAIL_NOT_FOUND; 2507 } else { 2508 rc_node_assign(outp, NULL); 2509 } 2510 return (ret); 2511 } 2512 2513 int 2514 rc_node_update(rc_node_ptr_t *npp) 2515 { 2516 cache_bucket_t *bp; 2517 rc_node_t *np = npp->rnp_node; 2518 rc_node_t *nnp; 2519 rc_node_t *cpg = NULL; 2520 2521 if (np != NULL && 2522 np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) { 2523 /* 2524 * If we're updating a composed property group, actually 2525 * update the top-level property group & return the 2526 * appropriate value. But leave *nnp pointing at us. 2527 */ 2528 cpg = np; 2529 np = np->rn_cchain[0]; 2530 } 2531 2532 RC_NODE_CHECK(np); 2533 2534 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP && 2535 np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) 2536 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 2537 2538 for (;;) { 2539 bp = cache_hold(np->rn_hash); 2540 nnp = cache_lookup_unlocked(bp, &np->rn_id); 2541 if (nnp == NULL) { 2542 cache_release(bp); 2543 rc_node_clear(npp, 1); 2544 return (REP_PROTOCOL_FAIL_DELETED); 2545 } 2546 /* 2547 * grab the lock before dropping the cache bucket, so 2548 * that no one else can sneak in 2549 */ 2550 (void) pthread_mutex_lock(&nnp->rn_lock); 2551 cache_release(bp); 2552 2553 if (!(nnp->rn_flags & RC_NODE_IN_TX) || 2554 !rc_node_wait_flag(nnp, RC_NODE_IN_TX)) 2555 break; 2556 2557 rc_node_rele_locked(nnp); 2558 } 2559 2560 /* 2561 * If it is dead, we want to update it so that it will continue to 2562 * report being dead. 2563 */ 2564 if (nnp->rn_flags & RC_NODE_DEAD) { 2565 (void) pthread_mutex_unlock(&nnp->rn_lock); 2566 if (nnp != np && cpg == NULL) 2567 rc_node_assign(npp, nnp); /* updated */ 2568 rc_node_rele(nnp); 2569 return (REP_PROTOCOL_FAIL_DELETED); 2570 } 2571 2572 assert(!(nnp->rn_flags & RC_NODE_OLD)); 2573 (void) pthread_mutex_unlock(&nnp->rn_lock); 2574 2575 if (nnp != np && cpg == NULL) 2576 rc_node_assign(npp, nnp); /* updated */ 2577 2578 rc_node_rele(nnp); 2579 2580 return ((nnp == np)? REP_PROTOCOL_SUCCESS : REP_PROTOCOL_DONE); 2581 } 2582 2583 /* 2584 * does a generic modification check, for creation, deletion, and snapshot 2585 * management only. Property group transactions have different checks. 2586 */ 2587 int 2588 rc_node_modify_permission_check(void) 2589 { 2590 int rc = REP_PROTOCOL_SUCCESS; 2591 permcheck_t *pcp; 2592 int granted; 2593 2594 if (!client_is_privileged()) { 2595 #ifdef NATIVE_BUILD 2596 rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED; 2597 #else 2598 pcp = pc_create(); 2599 if (pcp != NULL) { 2600 rc = perm_add_enabling(pcp, AUTH_MODIFY); 2601 2602 if (rc == REP_PROTOCOL_SUCCESS) { 2603 granted = perm_granted(pcp); 2604 2605 if (granted < 0) 2606 rc = REP_PROTOCOL_FAIL_NO_RESOURCES; 2607 } 2608 2609 pc_free(pcp); 2610 } else { 2611 rc = REP_PROTOCOL_FAIL_NO_RESOURCES; 2612 } 2613 2614 if (rc == REP_PROTOCOL_SUCCESS && !granted) 2615 rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED; 2616 #endif /* NATIVE_BUILD */ 2617 } 2618 return (rc); 2619 } 2620 2621 /* 2622 * Fails with 2623 * _DELETED - node has been deleted 2624 * _NOT_SET - npp is reset 2625 * _NOT_APPLICABLE - type is _PROPERTYGRP 2626 * _INVALID_TYPE - node is corrupt or type is invalid 2627 * _TYPE_MISMATCH - node cannot have children of type type 2628 * _BAD_REQUEST - name is invalid 2629 * cannot create children for this type of node 2630 * _NO_RESOURCES - out of memory, or could not allocate new id 2631 * _PERMISSION_DENIED 2632 * _BACKEND_ACCESS 2633 * _BACKEND_READONLY 2634 * _EXISTS - child already exists 2635 */ 2636 int 2637 rc_node_create_child(rc_node_ptr_t *npp, uint32_t type, const char *name, 2638 rc_node_ptr_t *cpp) 2639 { 2640 rc_node_t *np; 2641 rc_node_t *cp = NULL; 2642 int rc; 2643 2644 rc_node_clear(cpp, 0); 2645 2646 RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp); 2647 2648 /* 2649 * there is a separate interface for creating property groups 2650 */ 2651 if (type == REP_PROTOCOL_ENTITY_PROPERTYGRP) { 2652 (void) pthread_mutex_unlock(&np->rn_lock); 2653 return (REP_PROTOCOL_FAIL_NOT_APPLICABLE); 2654 } 2655 2656 if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) { 2657 (void) pthread_mutex_unlock(&np->rn_lock); 2658 np = np->rn_cchain[0]; 2659 RC_NODE_CHECK_AND_LOCK(np); 2660 } 2661 2662 if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) != 2663 REP_PROTOCOL_SUCCESS) { 2664 (void) pthread_mutex_unlock(&np->rn_lock); 2665 return (rc); 2666 } 2667 if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS) { 2668 (void) pthread_mutex_unlock(&np->rn_lock); 2669 return (rc); 2670 } 2671 2672 if ((rc = rc_node_modify_permission_check()) != REP_PROTOCOL_SUCCESS) { 2673 (void) pthread_mutex_unlock(&np->rn_lock); 2674 return (rc); 2675 } 2676 2677 HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_CREATING_CHILD); 2678 (void) pthread_mutex_unlock(&np->rn_lock); 2679 2680 rc = object_create(np, type, name, &cp); 2681 assert(rc != REP_PROTOCOL_FAIL_NOT_APPLICABLE); 2682 2683 if (rc == REP_PROTOCOL_SUCCESS) { 2684 rc_node_assign(cpp, cp); 2685 rc_node_rele(cp); 2686 } 2687 2688 (void) pthread_mutex_lock(&np->rn_lock); 2689 rc_node_rele_flag(np, RC_NODE_CREATING_CHILD); 2690 (void) pthread_mutex_unlock(&np->rn_lock); 2691 2692 return (rc); 2693 } 2694 2695 int 2696 rc_node_create_child_pg(rc_node_ptr_t *npp, uint32_t type, const char *name, 2697 const char *pgtype, uint32_t flags, rc_node_ptr_t *cpp) 2698 { 2699 rc_node_t *np; 2700 rc_node_t *cp; 2701 int rc; 2702 permcheck_t *pcp; 2703 int granted; 2704 2705 rc_node_clear(cpp, 0); 2706 2707 /* verify flags is valid */ 2708 if (flags & ~SCF_PG_FLAG_NONPERSISTENT) 2709 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 2710 2711 RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp); 2712 2713 if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) { 2714 rc_node_rele(np); 2715 return (REP_PROTOCOL_FAIL_NOT_APPLICABLE); 2716 } 2717 2718 if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) != 2719 REP_PROTOCOL_SUCCESS) { 2720 rc_node_rele(np); 2721 return (rc); 2722 } 2723 if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS || 2724 (rc = rc_check_pgtype_name(pgtype)) != REP_PROTOCOL_SUCCESS) { 2725 rc_node_rele(np); 2726 return (rc); 2727 } 2728 2729 if (!client_is_privileged()) { 2730 #ifdef NATIVE_BUILD 2731 rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED; 2732 #else 2733 /* Must have .smf.modify or smf.modify.<type> authorization */ 2734 pcp = pc_create(); 2735 if (pcp != NULL) { 2736 rc = perm_add_enabling(pcp, AUTH_MODIFY); 2737 2738 if (rc == REP_PROTOCOL_SUCCESS) { 2739 const char * const auth = 2740 perm_auth_for_pgtype(pgtype); 2741 2742 if (auth != NULL) 2743 rc = perm_add_enabling(pcp, auth); 2744 } 2745 2746 /* 2747 * .manage or $action_authorization can be used to 2748 * create the actions pg and the general_ovr pg. 2749 */ 2750 if (rc == REP_PROTOCOL_SUCCESS && 2751 (flags & SCF_PG_FLAG_NONPERSISTENT) != 0 && 2752 np->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE && 2753 ((strcmp(name, AUTH_PG_ACTIONS) == 0 && 2754 strcmp(pgtype, AUTH_PG_ACTIONS_TYPE) == 0) || 2755 (strcmp(name, AUTH_PG_GENERAL_OVR) == 0 && 2756 strcmp(pgtype, AUTH_PG_GENERAL_OVR_TYPE) == 0))) { 2757 rc = perm_add_enabling(pcp, AUTH_MANAGE); 2758 2759 if (rc == REP_PROTOCOL_SUCCESS) 2760 rc = perm_add_inst_action_auth(pcp, np); 2761 } 2762 2763 if (rc == REP_PROTOCOL_SUCCESS) { 2764 granted = perm_granted(pcp); 2765 2766 if (granted < 0) 2767 rc = REP_PROTOCOL_FAIL_NO_RESOURCES; 2768 } 2769 2770 pc_free(pcp); 2771 } else { 2772 rc = REP_PROTOCOL_FAIL_NO_RESOURCES; 2773 } 2774 2775 if (rc == REP_PROTOCOL_SUCCESS && !granted) 2776 rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED; 2777 #endif /* NATIVE_BUILD */ 2778 2779 if (rc != REP_PROTOCOL_SUCCESS) { 2780 rc_node_rele(np); 2781 return (rc); 2782 } 2783 } 2784 2785 (void) pthread_mutex_lock(&np->rn_lock); 2786 HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_CREATING_CHILD); 2787 (void) pthread_mutex_unlock(&np->rn_lock); 2788 2789 rc = object_create_pg(np, type, name, pgtype, flags, &cp); 2790 2791 if (rc == REP_PROTOCOL_SUCCESS) { 2792 rc_node_assign(cpp, cp); 2793 rc_node_rele(cp); 2794 } 2795 2796 (void) pthread_mutex_lock(&np->rn_lock); 2797 rc_node_rele_flag(np, RC_NODE_CREATING_CHILD); 2798 (void) pthread_mutex_unlock(&np->rn_lock); 2799 2800 return (rc); 2801 } 2802 2803 static void 2804 rc_pg_notify_fire(rc_node_pg_notify_t *pnp) 2805 { 2806 assert(MUTEX_HELD(&rc_pg_notify_lock)); 2807 2808 if (pnp->rnpn_pg != NULL) { 2809 uu_list_remove(pnp->rnpn_pg->rn_pg_notify_list, pnp); 2810 (void) close(pnp->rnpn_fd); 2811 2812 pnp->rnpn_pg = NULL; 2813 pnp->rnpn_fd = -1; 2814 } else { 2815 assert(pnp->rnpn_fd == -1); 2816 } 2817 } 2818 2819 static void 2820 rc_notify_node_delete(rc_notify_delete_t *ndp, rc_node_t *np_arg) 2821 { 2822 rc_node_t *svc = NULL; 2823 rc_node_t *inst = NULL; 2824 rc_node_t *pg = NULL; 2825 rc_node_t *np = np_arg; 2826 rc_node_t *nnp; 2827 2828 while (svc == NULL) { 2829 (void) pthread_mutex_lock(&np->rn_lock); 2830 if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) { 2831 (void) pthread_mutex_unlock(&np->rn_lock); 2832 goto cleanup; 2833 } 2834 nnp = np->rn_parent; 2835 rc_node_hold_locked(np); /* hold it in place */ 2836 2837 switch (np->rn_id.rl_type) { 2838 case REP_PROTOCOL_ENTITY_PROPERTYGRP: 2839 assert(pg == NULL); 2840 pg = np; 2841 break; 2842 case REP_PROTOCOL_ENTITY_INSTANCE: 2843 assert(inst == NULL); 2844 inst = np; 2845 break; 2846 case REP_PROTOCOL_ENTITY_SERVICE: 2847 assert(svc == NULL); 2848 svc = np; 2849 break; 2850 default: 2851 rc_node_rele_flag(np, RC_NODE_USING_PARENT); 2852 rc_node_rele_locked(np); 2853 goto cleanup; 2854 } 2855 2856 (void) pthread_mutex_unlock(&np->rn_lock); 2857 2858 np = nnp; 2859 if (np == NULL) 2860 goto cleanup; 2861 } 2862 2863 rc_notify_deletion(ndp, 2864 svc->rn_name, 2865 inst != NULL ? inst->rn_name : NULL, 2866 pg != NULL ? pg->rn_name : NULL); 2867 2868 ndp = NULL; 2869 2870 cleanup: 2871 if (ndp != NULL) 2872 uu_free(ndp); 2873 2874 for (;;) { 2875 if (svc != NULL) { 2876 np = svc; 2877 svc = NULL; 2878 } else if (inst != NULL) { 2879 np = inst; 2880 inst = NULL; 2881 } else if (pg != NULL) { 2882 np = pg; 2883 pg = NULL; 2884 } else 2885 break; 2886 2887 (void) pthread_mutex_lock(&np->rn_lock); 2888 rc_node_rele_flag(np, RC_NODE_USING_PARENT); 2889 rc_node_rele_locked(np); 2890 } 2891 } 2892 2893 /* 2894 * N.B.: this function drops np->rn_lock on the way out. 2895 */ 2896 static void 2897 rc_node_delete_hold(rc_node_t *np, int andformer) 2898 { 2899 rc_node_t *cp; 2900 2901 again: 2902 assert(MUTEX_HELD(&np->rn_lock)); 2903 assert((np->rn_flags & RC_NODE_DYING_FLAGS) == RC_NODE_DYING_FLAGS); 2904 2905 for (cp = uu_list_first(np->rn_children); cp != NULL; 2906 cp = uu_list_next(np->rn_children, cp)) { 2907 (void) pthread_mutex_lock(&cp->rn_lock); 2908 (void) pthread_mutex_unlock(&np->rn_lock); 2909 if (!rc_node_hold_flag(cp, RC_NODE_DYING_FLAGS)) { 2910 /* 2911 * already marked as dead -- can't happen, since that 2912 * would require setting RC_NODE_CHILDREN_CHANGING 2913 * in np, and we're holding that... 2914 */ 2915 abort(); 2916 } 2917 rc_node_delete_hold(cp, andformer); /* recurse, drop lock */ 2918 2919 (void) pthread_mutex_lock(&np->rn_lock); 2920 } 2921 if (andformer && (cp = np->rn_former) != NULL) { 2922 (void) pthread_mutex_lock(&cp->rn_lock); 2923 (void) pthread_mutex_unlock(&np->rn_lock); 2924 if (!rc_node_hold_flag(cp, RC_NODE_DYING_FLAGS)) 2925 abort(); /* can't happen, see above */ 2926 np = cp; 2927 goto again; /* tail-recurse down rn_former */ 2928 } 2929 (void) pthread_mutex_unlock(&np->rn_lock); 2930 } 2931 2932 /* 2933 * N.B.: this function drops np->rn_lock on the way out. 2934 */ 2935 static void 2936 rc_node_delete_rele(rc_node_t *np, int andformer) 2937 { 2938 rc_node_t *cp; 2939 2940 again: 2941 assert(MUTEX_HELD(&np->rn_lock)); 2942 assert((np->rn_flags & RC_NODE_DYING_FLAGS) == RC_NODE_DYING_FLAGS); 2943 2944 for (cp = uu_list_first(np->rn_children); cp != NULL; 2945 cp = uu_list_next(np->rn_children, cp)) { 2946 (void) pthread_mutex_lock(&cp->rn_lock); 2947 (void) pthread_mutex_unlock(&np->rn_lock); 2948 rc_node_delete_rele(cp, andformer); /* recurse, drop lock */ 2949 (void) pthread_mutex_lock(&np->rn_lock); 2950 } 2951 if (andformer && (cp = np->rn_former) != NULL) { 2952 (void) pthread_mutex_lock(&cp->rn_lock); 2953 rc_node_rele_flag(np, RC_NODE_DYING_FLAGS); 2954 (void) pthread_mutex_unlock(&np->rn_lock); 2955 2956 np = cp; 2957 goto again; /* tail-recurse down rn_former */ 2958 } 2959 rc_node_rele_flag(np, RC_NODE_DYING_FLAGS); 2960 (void) pthread_mutex_unlock(&np->rn_lock); 2961 } 2962 2963 static void 2964 rc_node_finish_delete(rc_node_t *cp) 2965 { 2966 cache_bucket_t *bp; 2967 rc_node_pg_notify_t *pnp; 2968 2969 assert(MUTEX_HELD(&cp->rn_lock)); 2970 2971 if (!(cp->rn_flags & RC_NODE_OLD)) { 2972 assert(cp->rn_flags & RC_NODE_IN_PARENT); 2973 if (!rc_node_wait_flag(cp, RC_NODE_USING_PARENT)) { 2974 abort(); /* can't happen, see above */ 2975 } 2976 cp->rn_flags &= ~RC_NODE_IN_PARENT; 2977 cp->rn_parent = NULL; 2978 } 2979 2980 cp->rn_flags |= RC_NODE_DEAD; 2981 2982 /* 2983 * If this node is not out-dated, we need to remove it from 2984 * the notify list and cache hash table. 2985 */ 2986 if (!(cp->rn_flags & RC_NODE_OLD)) { 2987 assert(cp->rn_refs > 0); /* can't go away yet */ 2988 (void) pthread_mutex_unlock(&cp->rn_lock); 2989 2990 (void) pthread_mutex_lock(&rc_pg_notify_lock); 2991 while ((pnp = uu_list_first(cp->rn_pg_notify_list)) != NULL) 2992 rc_pg_notify_fire(pnp); 2993 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 2994 rc_notify_remove_node(cp); 2995 2996 bp = cache_hold(cp->rn_hash); 2997 (void) pthread_mutex_lock(&cp->rn_lock); 2998 cache_remove_unlocked(bp, cp); 2999 cache_release(bp); 3000 } 3001 } 3002 3003 /* 3004 * N.B.: this function drops np->rn_lock and a reference on the way out. 3005 */ 3006 static void 3007 rc_node_delete_children(rc_node_t *np, int andformer) 3008 { 3009 rc_node_t *cp; 3010 3011 again: 3012 assert(np->rn_refs > 0); 3013 assert(MUTEX_HELD(&np->rn_lock)); 3014 assert(np->rn_flags & RC_NODE_DEAD); 3015 3016 while ((cp = uu_list_first(np->rn_children)) != NULL) { 3017 uu_list_remove(np->rn_children, cp); 3018 (void) pthread_mutex_lock(&cp->rn_lock); 3019 (void) pthread_mutex_unlock(&np->rn_lock); 3020 rc_node_hold_locked(cp); /* hold while we recurse */ 3021 rc_node_finish_delete(cp); 3022 rc_node_delete_children(cp, andformer); /* drops lock + ref */ 3023 (void) pthread_mutex_lock(&np->rn_lock); 3024 } 3025 3026 /* 3027 * when we drop cp's lock, all the children will be gone, so we 3028 * can release DYING_FLAGS. 3029 */ 3030 rc_node_rele_flag(np, RC_NODE_DYING_FLAGS); 3031 if (andformer && (cp = np->rn_former) != NULL) { 3032 np->rn_former = NULL; /* unlink */ 3033 (void) pthread_mutex_lock(&cp->rn_lock); 3034 (void) pthread_mutex_unlock(&np->rn_lock); 3035 np->rn_flags &= ~RC_NODE_ON_FORMER; 3036 3037 rc_node_hold_locked(cp); /* hold while we loop */ 3038 3039 rc_node_finish_delete(cp); 3040 3041 rc_node_rele(np); /* drop the old reference */ 3042 3043 np = cp; 3044 goto again; /* tail-recurse down rn_former */ 3045 } 3046 rc_node_rele_locked(np); 3047 } 3048 3049 static void 3050 rc_node_unrefed(rc_node_t *np) 3051 { 3052 int unrefed; 3053 rc_node_t *pp, *cur; 3054 3055 assert(MUTEX_HELD(&np->rn_lock)); 3056 assert(np->rn_refs == 0); 3057 assert(np->rn_other_refs == 0); 3058 assert(np->rn_other_refs_held == 0); 3059 3060 if (np->rn_flags & RC_NODE_DEAD) { 3061 (void) pthread_mutex_unlock(&np->rn_lock); 3062 rc_node_destroy(np); 3063 return; 3064 } 3065 3066 assert(np->rn_flags & RC_NODE_OLD); 3067 if (np->rn_flags & RC_NODE_UNREFED) { 3068 (void) pthread_mutex_unlock(&np->rn_lock); 3069 return; 3070 } 3071 np->rn_flags |= RC_NODE_UNREFED; 3072 3073 (void) pthread_mutex_unlock(&np->rn_lock); 3074 3075 /* 3076 * find the current in-hash object, and grab it's RC_NODE_IN_TX 3077 * flag. That protects the entire rn_former chain. 3078 */ 3079 for (;;) { 3080 pp = cache_lookup(&np->rn_id); 3081 if (pp == NULL) { 3082 (void) pthread_mutex_lock(&np->rn_lock); 3083 if (np->rn_flags & RC_NODE_DEAD) 3084 goto died; 3085 /* 3086 * We are trying to unreference this node, but the 3087 * owner of the former list does not exist. It must 3088 * be the case that another thread is deleting this 3089 * entire sub-branch, but has not yet reached us. 3090 * We will in short order be deleted. 3091 */ 3092 np->rn_flags &= ~RC_NODE_UNREFED; 3093 (void) pthread_mutex_unlock(&np->rn_lock); 3094 return; 3095 } 3096 if (pp == np) { 3097 /* 3098 * no longer unreferenced 3099 */ 3100 (void) pthread_mutex_lock(&np->rn_lock); 3101 np->rn_flags &= ~RC_NODE_UNREFED; 3102 rc_node_rele_locked(np); 3103 return; 3104 } 3105 (void) pthread_mutex_lock(&pp->rn_lock); 3106 if ((pp->rn_flags & RC_NODE_OLD) || 3107 !rc_node_hold_flag(pp, RC_NODE_IN_TX)) { 3108 rc_node_rele_locked(pp); 3109 continue; 3110 } 3111 if (!(pp->rn_flags & RC_NODE_OLD)) { 3112 (void) pthread_mutex_unlock(&pp->rn_lock); 3113 break; 3114 } 3115 rc_node_rele_flag(pp, RC_NODE_IN_TX); 3116 rc_node_rele_locked(pp); 3117 } 3118 3119 (void) pthread_mutex_lock(&np->rn_lock); 3120 if (!(np->rn_flags & (RC_NODE_OLD | RC_NODE_DEAD)) || 3121 np->rn_refs != 0 || np->rn_other_refs != 0 || 3122 np->rn_other_refs_held != 0) { 3123 np->rn_flags &= ~RC_NODE_UNREFED; 3124 (void) pthread_mutex_lock(&pp->rn_lock); 3125 3126 rc_node_rele_flag(pp, RC_NODE_IN_TX); 3127 rc_node_rele_locked(pp); 3128 return; 3129 } 3130 3131 if (!rc_node_hold_flag(np, RC_NODE_DYING_FLAGS)) { 3132 (void) pthread_mutex_unlock(&np->rn_lock); 3133 3134 rc_node_rele_flag(pp, RC_NODE_IN_TX); 3135 rc_node_rele_locked(pp); 3136 3137 (void) pthread_mutex_lock(&np->rn_lock); 3138 goto died; 3139 } 3140 3141 rc_node_delete_hold(np, 0); 3142 3143 (void) pthread_mutex_lock(&np->rn_lock); 3144 if (!(np->rn_flags & RC_NODE_OLD) || 3145 np->rn_refs != 0 || np->rn_other_refs != 0 || 3146 np->rn_other_refs_held != 0) { 3147 np->rn_flags &= ~RC_NODE_UNREFED; 3148 rc_node_delete_rele(np, 0); 3149 3150 (void) pthread_mutex_lock(&pp->rn_lock); 3151 rc_node_rele_flag(pp, RC_NODE_IN_TX); 3152 rc_node_rele_locked(pp); 3153 return; 3154 } 3155 3156 np->rn_flags |= RC_NODE_DEAD; 3157 rc_node_hold_locked(np); 3158 rc_node_delete_children(np, 0); 3159 3160 /* 3161 * It's gone -- remove it from the former chain and destroy it. 3162 */ 3163 (void) pthread_mutex_lock(&pp->rn_lock); 3164 for (cur = pp; cur != NULL && cur->rn_former != np; 3165 cur = cur->rn_former) 3166 ; 3167 assert(cur != NULL && cur != np); 3168 3169 cur->rn_former = np->rn_former; 3170 np->rn_former = NULL; 3171 3172 rc_node_rele_flag(pp, RC_NODE_IN_TX); 3173 rc_node_rele_locked(pp); 3174 3175 (void) pthread_mutex_lock(&np->rn_lock); 3176 assert(np->rn_flags & RC_NODE_ON_FORMER); 3177 np->rn_flags &= ~(RC_NODE_UNREFED | RC_NODE_ON_FORMER); 3178 (void) pthread_mutex_unlock(&np->rn_lock); 3179 rc_node_destroy(np); 3180 return; 3181 3182 died: 3183 np->rn_flags &= ~RC_NODE_UNREFED; 3184 unrefed = (np->rn_refs == 0 && np->rn_other_refs == 0 && 3185 np->rn_other_refs_held == 0); 3186 (void) pthread_mutex_unlock(&np->rn_lock); 3187 if (unrefed) 3188 rc_node_destroy(np); 3189 } 3190 3191 /* 3192 * Fails with 3193 * _NOT_SET 3194 * _DELETED 3195 * _BAD_REQUEST 3196 * _PERMISSION_DENIED 3197 * _NO_RESOURCES 3198 * and whatever object_delete() fails with. 3199 */ 3200 int 3201 rc_node_delete(rc_node_ptr_t *npp) 3202 { 3203 rc_node_t *np, *np_orig; 3204 rc_node_t *pp = NULL; 3205 int rc; 3206 rc_node_pg_notify_t *pnp; 3207 cache_bucket_t *bp; 3208 rc_notify_delete_t *ndp; 3209 permcheck_t *pcp; 3210 int granted; 3211 3212 RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp); 3213 3214 switch (np->rn_id.rl_type) { 3215 case REP_PROTOCOL_ENTITY_SERVICE: 3216 case REP_PROTOCOL_ENTITY_INSTANCE: 3217 case REP_PROTOCOL_ENTITY_SNAPSHOT: 3218 break; /* deletable */ 3219 3220 case REP_PROTOCOL_ENTITY_SCOPE: 3221 case REP_PROTOCOL_ENTITY_SNAPLEVEL: 3222 /* Scopes and snaplevels are indelible. */ 3223 (void) pthread_mutex_unlock(&np->rn_lock); 3224 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 3225 3226 case REP_PROTOCOL_ENTITY_CPROPERTYGRP: 3227 (void) pthread_mutex_unlock(&np->rn_lock); 3228 np = np->rn_cchain[0]; 3229 RC_NODE_CHECK_AND_LOCK(np); 3230 break; 3231 3232 case REP_PROTOCOL_ENTITY_PROPERTYGRP: 3233 if (np->rn_id.rl_ids[ID_SNAPSHOT] == 0) 3234 break; 3235 3236 /* Snapshot property groups are indelible. */ 3237 (void) pthread_mutex_unlock(&np->rn_lock); 3238 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED); 3239 3240 case REP_PROTOCOL_ENTITY_PROPERTY: 3241 (void) pthread_mutex_unlock(&np->rn_lock); 3242 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 3243 3244 default: 3245 assert(0); 3246 abort(); 3247 break; 3248 } 3249 3250 np_orig = np; 3251 rc_node_hold_locked(np); /* simplifies rest of the code */ 3252 3253 again: 3254 /* 3255 * The following loop is to deal with the fact that snapshots and 3256 * property groups are moving targets -- changes to them result 3257 * in a new "child" node. Since we can only delete from the top node, 3258 * we have to loop until we have a non-RC_NODE_OLD version. 3259 */ 3260 for (;;) { 3261 if (!rc_node_wait_flag(np, 3262 RC_NODE_IN_TX | RC_NODE_USING_PARENT)) { 3263 rc_node_rele_locked(np); 3264 return (REP_PROTOCOL_FAIL_DELETED); 3265 } 3266 3267 if (np->rn_flags & RC_NODE_OLD) { 3268 rc_node_rele_locked(np); 3269 np = cache_lookup(&np_orig->rn_id); 3270 assert(np != np_orig); 3271 3272 if (np == NULL) { 3273 rc = REP_PROTOCOL_FAIL_DELETED; 3274 goto fail; 3275 } 3276 (void) pthread_mutex_lock(&np->rn_lock); 3277 continue; 3278 } 3279 3280 if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) { 3281 rc_node_rele_locked(np); 3282 rc_node_clear(npp, 1); 3283 return (REP_PROTOCOL_FAIL_DELETED); 3284 } 3285 3286 /* 3287 * Mark our parent as children changing. this call drops our 3288 * lock and the RC_NODE_USING_PARENT flag, and returns with 3289 * pp's lock held 3290 */ 3291 pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING); 3292 if (pp == NULL) { 3293 /* our parent is gone, we're going next... */ 3294 rc_node_rele(np); 3295 3296 rc_node_clear(npp, 1); 3297 return (REP_PROTOCOL_FAIL_DELETED); 3298 } 3299 3300 rc_node_hold_locked(pp); /* hold for later */ 3301 (void) pthread_mutex_unlock(&pp->rn_lock); 3302 3303 (void) pthread_mutex_lock(&np->rn_lock); 3304 if (!(np->rn_flags & RC_NODE_OLD)) 3305 break; /* not old -- we're done */ 3306 3307 (void) pthread_mutex_unlock(&np->rn_lock); 3308 (void) pthread_mutex_lock(&pp->rn_lock); 3309 rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING); 3310 rc_node_rele_locked(pp); 3311 (void) pthread_mutex_lock(&np->rn_lock); 3312 continue; /* loop around and try again */ 3313 } 3314 /* 3315 * Everyone out of the pool -- we grab everything but 3316 * RC_NODE_USING_PARENT (including RC_NODE_DYING) to keep 3317 * any changes from occurring while we are attempting to 3318 * delete the node. 3319 */ 3320 if (!rc_node_hold_flag(np, RC_NODE_DYING_FLAGS)) { 3321 (void) pthread_mutex_unlock(&np->rn_lock); 3322 rc = REP_PROTOCOL_FAIL_DELETED; 3323 goto fail; 3324 } 3325 3326 assert(!(np->rn_flags & RC_NODE_OLD)); 3327 3328 if (!client_is_privileged()) { 3329 /* permission check */ 3330 (void) pthread_mutex_unlock(&np->rn_lock); 3331 3332 #ifdef NATIVE_BUILD 3333 rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED; 3334 #else 3335 pcp = pc_create(); 3336 if (pcp != NULL) { 3337 rc = perm_add_enabling(pcp, AUTH_MODIFY); 3338 3339 /* add .smf.modify.<type> for pgs. */ 3340 if (rc == REP_PROTOCOL_SUCCESS && np->rn_id.rl_type == 3341 REP_PROTOCOL_ENTITY_PROPERTYGRP) { 3342 const char * const auth = 3343 perm_auth_for_pgtype(np->rn_type); 3344 3345 if (auth != NULL) 3346 rc = perm_add_enabling(pcp, auth); 3347 } 3348 3349 if (rc == REP_PROTOCOL_SUCCESS) { 3350 granted = perm_granted(pcp); 3351 3352 if (granted < 0) 3353 rc = REP_PROTOCOL_FAIL_NO_RESOURCES; 3354 } 3355 3356 pc_free(pcp); 3357 } else { 3358 rc = REP_PROTOCOL_FAIL_NO_RESOURCES; 3359 } 3360 3361 if (rc == REP_PROTOCOL_SUCCESS && !granted) 3362 rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED; 3363 #endif /* NATIVE_BUILD */ 3364 3365 if (rc != REP_PROTOCOL_SUCCESS) { 3366 (void) pthread_mutex_lock(&np->rn_lock); 3367 rc_node_rele_flag(np, RC_NODE_DYING_FLAGS); 3368 (void) pthread_mutex_unlock(&np->rn_lock); 3369 goto fail; 3370 } 3371 3372 (void) pthread_mutex_lock(&np->rn_lock); 3373 } 3374 3375 ndp = uu_zalloc(sizeof (*ndp)); 3376 if (ndp == NULL) { 3377 rc_node_rele_flag(np, RC_NODE_DYING_FLAGS); 3378 (void) pthread_mutex_unlock(&np->rn_lock); 3379 rc = REP_PROTOCOL_FAIL_NO_RESOURCES; 3380 goto fail; 3381 } 3382 3383 rc_node_delete_hold(np, 1); /* hold entire subgraph, drop lock */ 3384 3385 rc = object_delete(np); 3386 3387 if (rc != REP_PROTOCOL_SUCCESS) { 3388 (void) pthread_mutex_lock(&np->rn_lock); 3389 rc_node_delete_rele(np, 1); /* drops lock */ 3390 uu_free(ndp); 3391 goto fail; 3392 } 3393 3394 /* 3395 * Now, delicately unlink and delete the object. 3396 * 3397 * Create the delete notification, atomically remove 3398 * from the hash table and set the NODE_DEAD flag, and 3399 * remove from the parent's children list. 3400 */ 3401 rc_notify_node_delete(ndp, np); /* frees or uses ndp */ 3402 3403 bp = cache_hold(np->rn_hash); 3404 3405 (void) pthread_mutex_lock(&np->rn_lock); 3406 cache_remove_unlocked(bp, np); 3407 cache_release(bp); 3408 3409 np->rn_flags |= RC_NODE_DEAD; 3410 if (pp != NULL) { 3411 (void) pthread_mutex_unlock(&np->rn_lock); 3412 3413 (void) pthread_mutex_lock(&pp->rn_lock); 3414 (void) pthread_mutex_lock(&np->rn_lock); 3415 uu_list_remove(pp->rn_children, np); 3416 rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING); 3417 (void) pthread_mutex_unlock(&pp->rn_lock); 3418 np->rn_flags &= ~RC_NODE_IN_PARENT; 3419 } 3420 /* 3421 * finally, propagate death to our children, handle notifications, 3422 * and release our hold. 3423 */ 3424 rc_node_hold_locked(np); /* hold for delete */ 3425 rc_node_delete_children(np, 1); /* drops DYING_FLAGS, lock, ref */ 3426 3427 rc_node_clear(npp, 1); 3428 3429 (void) pthread_mutex_lock(&rc_pg_notify_lock); 3430 while ((pnp = uu_list_first(np->rn_pg_notify_list)) != NULL) 3431 rc_pg_notify_fire(pnp); 3432 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 3433 rc_notify_remove_node(np); 3434 3435 rc_node_rele(np); 3436 3437 return (rc); 3438 3439 fail: 3440 rc_node_rele(np); 3441 if (rc == REP_PROTOCOL_FAIL_DELETED) 3442 rc_node_clear(npp, 1); 3443 if (pp != NULL) { 3444 (void) pthread_mutex_lock(&pp->rn_lock); 3445 rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING); 3446 rc_node_rele_locked(pp); /* drop ref and lock */ 3447 } 3448 return (rc); 3449 } 3450 3451 int 3452 rc_node_next_snaplevel(rc_node_ptr_t *npp, rc_node_ptr_t *cpp) 3453 { 3454 rc_node_t *np; 3455 rc_node_t *cp, *pp; 3456 int res; 3457 3458 rc_node_clear(cpp, 0); 3459 3460 RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp); 3461 3462 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT && 3463 np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL) { 3464 (void) pthread_mutex_unlock(&np->rn_lock); 3465 return (REP_PROTOCOL_FAIL_NOT_APPLICABLE); 3466 } 3467 3468 if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_SNAPSHOT) { 3469 if ((res = rc_node_fill_children(np, 3470 REP_PROTOCOL_ENTITY_SNAPLEVEL)) != REP_PROTOCOL_SUCCESS) { 3471 (void) pthread_mutex_unlock(&np->rn_lock); 3472 return (res); 3473 } 3474 3475 for (cp = uu_list_first(np->rn_children); 3476 cp != NULL; 3477 cp = uu_list_next(np->rn_children, cp)) { 3478 if (cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL) 3479 continue; 3480 rc_node_hold(cp); 3481 break; 3482 } 3483 3484 (void) pthread_mutex_unlock(&np->rn_lock); 3485 } else { 3486 HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_USING_PARENT); 3487 /* 3488 * mark our parent as children changing. This call drops our 3489 * lock and the RC_NODE_USING_PARENT flag, and returns with 3490 * pp's lock held 3491 */ 3492 pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING); 3493 if (pp == NULL) { 3494 /* our parent is gone, we're going next... */ 3495 3496 rc_node_clear(npp, 1); 3497 return (REP_PROTOCOL_FAIL_DELETED); 3498 } 3499 3500 /* 3501 * find the next snaplevel 3502 */ 3503 cp = np; 3504 while ((cp = uu_list_next(pp->rn_children, cp)) != NULL && 3505 cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL) 3506 ; 3507 3508 /* it must match the snaplevel list */ 3509 assert((cp == NULL && np->rn_snaplevel->rsl_next == NULL) || 3510 (cp != NULL && np->rn_snaplevel->rsl_next == 3511 cp->rn_snaplevel)); 3512 3513 if (cp != NULL) 3514 rc_node_hold(cp); 3515 3516 rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING); 3517 3518 (void) pthread_mutex_unlock(&pp->rn_lock); 3519 } 3520 3521 rc_node_assign(cpp, cp); 3522 if (cp != NULL) { 3523 rc_node_rele(cp); 3524 3525 return (REP_PROTOCOL_SUCCESS); 3526 } 3527 return (REP_PROTOCOL_FAIL_NOT_FOUND); 3528 } 3529 3530 /* 3531 * This call takes a snapshot (np) and either: 3532 * an existing snapid (to be associated with np), or 3533 * a non-NULL parentp (from which a new snapshot is taken, and associated 3534 * with np) 3535 * 3536 * To do the association, np is duplicated, the duplicate is made to 3537 * represent the new snapid, and np is replaced with the new rc_node_t on 3538 * np's parent's child list. np is placed on the new node's rn_former list, 3539 * and replaces np in cache_hash (so rc_node_update() will find the new one). 3540 */ 3541 static int 3542 rc_attach_snapshot(rc_node_t *np, uint32_t snapid, rc_node_t *parentp) 3543 { 3544 rc_node_t *np_orig; 3545 rc_node_t *nnp, *prev; 3546 rc_node_t *pp; 3547 int rc; 3548 3549 if (parentp != NULL) 3550 assert(snapid == 0); 3551 3552 assert(MUTEX_HELD(&np->rn_lock)); 3553 3554 if ((rc = rc_node_modify_permission_check()) != REP_PROTOCOL_SUCCESS) { 3555 (void) pthread_mutex_unlock(&np->rn_lock); 3556 return (rc); 3557 } 3558 3559 np_orig = np; 3560 rc_node_hold_locked(np); /* simplifies the remainder */ 3561 3562 /* 3563 * get the latest node, holding RC_NODE_IN_TX to keep the rn_former 3564 * list from changing. 3565 */ 3566 for (;;) { 3567 if (!(np->rn_flags & RC_NODE_OLD)) { 3568 if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) { 3569 goto again; 3570 } 3571 pp = rc_node_hold_parent_flag(np, 3572 RC_NODE_CHILDREN_CHANGING); 3573 3574 (void) pthread_mutex_lock(&np->rn_lock); 3575 if (pp == NULL) { 3576 goto again; 3577 } 3578 if (np->rn_flags & RC_NODE_OLD) { 3579 rc_node_rele_flag(pp, 3580 RC_NODE_CHILDREN_CHANGING); 3581 (void) pthread_mutex_unlock(&pp->rn_lock); 3582 goto again; 3583 } 3584 (void) pthread_mutex_unlock(&pp->rn_lock); 3585 3586 if (!rc_node_hold_flag(np, RC_NODE_IN_TX)) { 3587 /* 3588 * Can't happen, since we're holding our 3589 * parent's CHILDREN_CHANGING flag... 3590 */ 3591 abort(); 3592 } 3593 break; /* everything's ready */ 3594 } 3595 again: 3596 rc_node_rele_locked(np); 3597 np = cache_lookup(&np_orig->rn_id); 3598 3599 if (np == NULL) 3600 return (REP_PROTOCOL_FAIL_DELETED); 3601 3602 (void) pthread_mutex_lock(&np->rn_lock); 3603 } 3604 3605 if (parentp != NULL) { 3606 if (pp != parentp) { 3607 rc = REP_PROTOCOL_FAIL_BAD_REQUEST; 3608 goto fail; 3609 } 3610 nnp = NULL; 3611 } else { 3612 /* 3613 * look for a former node with the snapid we need. 3614 */ 3615 if (np->rn_snapshot_id == snapid) { 3616 rc_node_rele_flag(np, RC_NODE_IN_TX); 3617 rc_node_rele_locked(np); 3618 3619 (void) pthread_mutex_lock(&pp->rn_lock); 3620 rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING); 3621 (void) pthread_mutex_unlock(&pp->rn_lock); 3622 return (REP_PROTOCOL_SUCCESS); /* nothing to do */ 3623 } 3624 3625 prev = np; 3626 while ((nnp = prev->rn_former) != NULL) { 3627 if (nnp->rn_snapshot_id == snapid) { 3628 rc_node_hold(nnp); 3629 break; /* existing node with that id */ 3630 } 3631 prev = nnp; 3632 } 3633 } 3634 3635 if (nnp == NULL) { 3636 prev = NULL; 3637 nnp = rc_node_alloc(); 3638 if (nnp == NULL) { 3639 rc = REP_PROTOCOL_FAIL_NO_RESOURCES; 3640 goto fail; 3641 } 3642 3643 nnp->rn_id = np->rn_id; /* structure assignment */ 3644 nnp->rn_hash = np->rn_hash; 3645 nnp->rn_name = strdup(np->rn_name); 3646 nnp->rn_snapshot_id = snapid; 3647 nnp->rn_flags = RC_NODE_IN_TX | RC_NODE_USING_PARENT; 3648 3649 if (nnp->rn_name == NULL) { 3650 rc = REP_PROTOCOL_FAIL_NO_RESOURCES; 3651 goto fail; 3652 } 3653 } 3654 3655 (void) pthread_mutex_unlock(&np->rn_lock); 3656 3657 rc = object_snapshot_attach(&np->rn_id, &snapid, (parentp != NULL)); 3658 3659 if (parentp != NULL) 3660 nnp->rn_snapshot_id = snapid; /* fill in new snapid */ 3661 else 3662 assert(nnp->rn_snapshot_id == snapid); 3663 3664 (void) pthread_mutex_lock(&np->rn_lock); 3665 if (rc != REP_PROTOCOL_SUCCESS) 3666 goto fail; 3667 3668 /* 3669 * fix up the former chain 3670 */ 3671 if (prev != NULL) { 3672 prev->rn_former = nnp->rn_former; 3673 (void) pthread_mutex_lock(&nnp->rn_lock); 3674 nnp->rn_flags &= ~RC_NODE_ON_FORMER; 3675 nnp->rn_former = NULL; 3676 (void) pthread_mutex_unlock(&nnp->rn_lock); 3677 } 3678 np->rn_flags |= RC_NODE_OLD; 3679 (void) pthread_mutex_unlock(&np->rn_lock); 3680 3681 /* 3682 * replace np with nnp 3683 */ 3684 rc_node_relink_child(pp, np, nnp); 3685 3686 rc_node_rele(np); 3687 3688 return (REP_PROTOCOL_SUCCESS); 3689 3690 fail: 3691 rc_node_rele_flag(np, RC_NODE_IN_TX); 3692 rc_node_rele_locked(np); 3693 (void) pthread_mutex_lock(&pp->rn_lock); 3694 rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING); 3695 (void) pthread_mutex_unlock(&pp->rn_lock); 3696 3697 if (nnp != NULL) { 3698 if (prev == NULL) 3699 rc_node_destroy(nnp); 3700 else 3701 rc_node_rele(nnp); 3702 } 3703 3704 return (rc); 3705 } 3706 3707 int 3708 rc_snapshot_take_new(rc_node_ptr_t *npp, const char *svcname, 3709 const char *instname, const char *name, rc_node_ptr_t *outpp) 3710 { 3711 rc_node_t *np; 3712 rc_node_t *outp = NULL; 3713 int rc; 3714 3715 rc_node_clear(outpp, 0); 3716 3717 RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp); 3718 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) { 3719 (void) pthread_mutex_unlock(&np->rn_lock); 3720 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH); 3721 } 3722 3723 rc = rc_check_type_name(REP_PROTOCOL_ENTITY_SNAPSHOT, name); 3724 if (rc != REP_PROTOCOL_SUCCESS) { 3725 (void) pthread_mutex_unlock(&np->rn_lock); 3726 return (rc); 3727 } 3728 3729 if (svcname != NULL && (rc = 3730 rc_check_type_name(REP_PROTOCOL_ENTITY_SERVICE, svcname)) != 3731 REP_PROTOCOL_SUCCESS) { 3732 (void) pthread_mutex_unlock(&np->rn_lock); 3733 return (rc); 3734 } 3735 3736 if (instname != NULL && (rc = 3737 rc_check_type_name(REP_PROTOCOL_ENTITY_INSTANCE, instname)) != 3738 REP_PROTOCOL_SUCCESS) { 3739 (void) pthread_mutex_unlock(&np->rn_lock); 3740 return (rc); 3741 } 3742 3743 if ((rc = rc_node_modify_permission_check()) != REP_PROTOCOL_SUCCESS) { 3744 (void) pthread_mutex_unlock(&np->rn_lock); 3745 return (rc); 3746 } 3747 3748 HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_CREATING_CHILD); 3749 (void) pthread_mutex_unlock(&np->rn_lock); 3750 3751 rc = object_snapshot_take_new(np, svcname, instname, name, &outp); 3752 3753 if (rc == REP_PROTOCOL_SUCCESS) { 3754 rc_node_assign(outpp, outp); 3755 rc_node_rele(outp); 3756 } 3757 3758 (void) pthread_mutex_lock(&np->rn_lock); 3759 rc_node_rele_flag(np, RC_NODE_CREATING_CHILD); 3760 (void) pthread_mutex_unlock(&np->rn_lock); 3761 3762 return (rc); 3763 } 3764 3765 int 3766 rc_snapshot_take_attach(rc_node_ptr_t *npp, rc_node_ptr_t *outpp) 3767 { 3768 rc_node_t *np, *outp; 3769 3770 RC_NODE_PTR_GET_CHECK(np, npp); 3771 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) { 3772 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH); 3773 } 3774 3775 RC_NODE_PTR_GET_CHECK_AND_LOCK(outp, outpp); 3776 if (outp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) { 3777 (void) pthread_mutex_unlock(&outp->rn_lock); 3778 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 3779 } 3780 3781 return (rc_attach_snapshot(outp, 0, np)); /* drops outp's lock */ 3782 } 3783 3784 int 3785 rc_snapshot_attach(rc_node_ptr_t *npp, rc_node_ptr_t *cpp) 3786 { 3787 rc_node_t *np; 3788 rc_node_t *cp; 3789 uint32_t snapid; 3790 3791 RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp); 3792 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) { 3793 (void) pthread_mutex_unlock(&np->rn_lock); 3794 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 3795 } 3796 snapid = np->rn_snapshot_id; 3797 (void) pthread_mutex_unlock(&np->rn_lock); 3798 3799 RC_NODE_PTR_GET_CHECK_AND_LOCK(cp, cpp); 3800 if (cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) { 3801 (void) pthread_mutex_unlock(&cp->rn_lock); 3802 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 3803 } 3804 3805 return (rc_attach_snapshot(cp, snapid, NULL)); /* drops cp's lock */ 3806 } 3807 3808 /* 3809 * Iteration 3810 */ 3811 static int 3812 rc_iter_filter_name(rc_node_t *np, void *s) 3813 { 3814 const char *name = s; 3815 3816 return (strcmp(np->rn_name, name) == 0); 3817 } 3818 3819 static int 3820 rc_iter_filter_type(rc_node_t *np, void *s) 3821 { 3822 const char *type = s; 3823 3824 return (np->rn_type != NULL && strcmp(np->rn_type, type) == 0); 3825 } 3826 3827 /*ARGSUSED*/ 3828 static int 3829 rc_iter_null_filter(rc_node_t *np, void *s) 3830 { 3831 return (1); 3832 } 3833 3834 /* 3835 * Allocate & initialize an rc_node_iter_t structure. Essentially, ensure 3836 * np->rn_children is populated and call uu_list_walk_start(np->rn_children). 3837 * If successful, leaves a hold on np & increments np->rn_other_refs 3838 * 3839 * If composed is true, then set up for iteration across the top level of np's 3840 * composition chain. If successful, leaves a hold on np and increments 3841 * rn_other_refs for the top level of np's composition chain. 3842 * 3843 * Fails with 3844 * _NO_RESOURCES 3845 * _INVALID_TYPE 3846 * _TYPE_MISMATCH - np cannot carry type children 3847 * _DELETED 3848 */ 3849 static int 3850 rc_iter_create(rc_node_iter_t **resp, rc_node_t *np, uint32_t type, 3851 rc_iter_filter_func *filter, void *arg, boolean_t composed) 3852 { 3853 rc_node_iter_t *nip; 3854 int res; 3855 3856 assert(*resp == NULL); 3857 3858 nip = uu_zalloc(sizeof (*nip)); 3859 if (nip == NULL) 3860 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 3861 3862 /* np is held by the client's rc_node_ptr_t */ 3863 if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) 3864 composed = 1; 3865 3866 if (!composed) { 3867 (void) pthread_mutex_lock(&np->rn_lock); 3868 3869 if ((res = rc_node_fill_children(np, type)) != 3870 REP_PROTOCOL_SUCCESS) { 3871 (void) pthread_mutex_unlock(&np->rn_lock); 3872 uu_free(nip); 3873 return (res); 3874 } 3875 3876 nip->rni_clevel = -1; 3877 3878 nip->rni_iter = uu_list_walk_start(np->rn_children, 3879 UU_WALK_ROBUST); 3880 if (nip->rni_iter != NULL) { 3881 nip->rni_iter_node = np; 3882 rc_node_hold_other(np); 3883 } else { 3884 (void) pthread_mutex_unlock(&np->rn_lock); 3885 uu_free(nip); 3886 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 3887 } 3888 (void) pthread_mutex_unlock(&np->rn_lock); 3889 } else { 3890 rc_node_t *ent; 3891 3892 if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_SNAPSHOT) { 3893 /* rn_cchain isn't valid until children are loaded. */ 3894 (void) pthread_mutex_lock(&np->rn_lock); 3895 res = rc_node_fill_children(np, 3896 REP_PROTOCOL_ENTITY_SNAPLEVEL); 3897 (void) pthread_mutex_unlock(&np->rn_lock); 3898 if (res != REP_PROTOCOL_SUCCESS) { 3899 uu_free(nip); 3900 return (res); 3901 } 3902 3903 /* Check for an empty snapshot. */ 3904 if (np->rn_cchain[0] == NULL) 3905 goto empty; 3906 } 3907 3908 /* Start at the top of the composition chain. */ 3909 for (nip->rni_clevel = 0; ; ++nip->rni_clevel) { 3910 if (nip->rni_clevel >= COMPOSITION_DEPTH) { 3911 /* Empty composition chain. */ 3912 empty: 3913 nip->rni_clevel = -1; 3914 nip->rni_iter = NULL; 3915 /* It's ok, iter_next() will return _DONE. */ 3916 goto out; 3917 } 3918 3919 ent = np->rn_cchain[nip->rni_clevel]; 3920 assert(ent != NULL); 3921 3922 if (rc_node_check_and_lock(ent) == REP_PROTOCOL_SUCCESS) 3923 break; 3924 3925 /* Someone deleted it, so try the next one. */ 3926 } 3927 3928 res = rc_node_fill_children(ent, type); 3929 3930 if (res == REP_PROTOCOL_SUCCESS) { 3931 nip->rni_iter = uu_list_walk_start(ent->rn_children, 3932 UU_WALK_ROBUST); 3933 3934 if (nip->rni_iter == NULL) 3935 res = REP_PROTOCOL_FAIL_NO_RESOURCES; 3936 else { 3937 nip->rni_iter_node = ent; 3938 rc_node_hold_other(ent); 3939 } 3940 } 3941 3942 if (res != REP_PROTOCOL_SUCCESS) { 3943 (void) pthread_mutex_unlock(&ent->rn_lock); 3944 uu_free(nip); 3945 return (res); 3946 } 3947 3948 (void) pthread_mutex_unlock(&ent->rn_lock); 3949 } 3950 3951 out: 3952 rc_node_hold(np); /* released by rc_iter_end() */ 3953 nip->rni_parent = np; 3954 nip->rni_type = type; 3955 nip->rni_filter = (filter != NULL)? filter : rc_iter_null_filter; 3956 nip->rni_filter_arg = arg; 3957 *resp = nip; 3958 return (REP_PROTOCOL_SUCCESS); 3959 } 3960 3961 static void 3962 rc_iter_end(rc_node_iter_t *iter) 3963 { 3964 rc_node_t *np = iter->rni_parent; 3965 3966 if (iter->rni_clevel >= 0) 3967 np = np->rn_cchain[iter->rni_clevel]; 3968 3969 assert(MUTEX_HELD(&np->rn_lock)); 3970 if (iter->rni_iter != NULL) 3971 uu_list_walk_end(iter->rni_iter); 3972 iter->rni_iter = NULL; 3973 3974 (void) pthread_mutex_unlock(&np->rn_lock); 3975 rc_node_rele(iter->rni_parent); 3976 if (iter->rni_iter_node != NULL) 3977 rc_node_rele_other(iter->rni_iter_node); 3978 } 3979 3980 /* 3981 * Fails with 3982 * _NOT_SET - npp is reset 3983 * _DELETED - npp's node has been deleted 3984 * _NOT_APPLICABLE - npp's node is not a property 3985 * _NO_RESOURCES - out of memory 3986 */ 3987 static int 3988 rc_node_setup_value_iter(rc_node_ptr_t *npp, rc_node_iter_t **iterp) 3989 { 3990 rc_node_t *np; 3991 3992 rc_node_iter_t *nip; 3993 3994 assert(*iterp == NULL); 3995 3996 RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp); 3997 3998 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) { 3999 (void) pthread_mutex_unlock(&np->rn_lock); 4000 return (REP_PROTOCOL_FAIL_NOT_APPLICABLE); 4001 } 4002 4003 nip = uu_zalloc(sizeof (*nip)); 4004 if (nip == NULL) { 4005 (void) pthread_mutex_unlock(&np->rn_lock); 4006 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 4007 } 4008 4009 nip->rni_parent = np; 4010 nip->rni_iter = NULL; 4011 nip->rni_clevel = -1; 4012 nip->rni_type = REP_PROTOCOL_ENTITY_VALUE; 4013 nip->rni_offset = 0; 4014 nip->rni_last_offset = 0; 4015 4016 rc_node_hold_locked(np); 4017 4018 *iterp = nip; 4019 (void) pthread_mutex_unlock(&np->rn_lock); 4020 4021 return (REP_PROTOCOL_SUCCESS); 4022 } 4023 4024 /* 4025 * Returns: 4026 * _NOT_SET - npp is reset 4027 * _DELETED - npp's node has been deleted 4028 * _TYPE_MISMATCH - npp's node is not a property 4029 * _NOT_FOUND - property has no values 4030 * _TRUNCATED - property has >1 values (first is written into out) 4031 * _SUCCESS - property has 1 value (which is written into out) 4032 * 4033 * We shorten *sz_out to not include anything after the final '\0'. 4034 */ 4035 int 4036 rc_node_get_property_value(rc_node_ptr_t *npp, 4037 struct rep_protocol_value_response *out, size_t *sz_out) 4038 { 4039 rc_node_t *np; 4040 size_t w; 4041 int ret; 4042 4043 assert(*sz_out == sizeof (*out)); 4044 4045 RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp); 4046 4047 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) { 4048 (void) pthread_mutex_unlock(&np->rn_lock); 4049 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH); 4050 } 4051 4052 if (np->rn_values_size == 0) { 4053 (void) pthread_mutex_unlock(&np->rn_lock); 4054 return (REP_PROTOCOL_FAIL_NOT_FOUND); 4055 } 4056 out->rpr_type = np->rn_valtype; 4057 w = strlcpy(out->rpr_value, &np->rn_values[0], 4058 sizeof (out->rpr_value)); 4059 4060 if (w >= sizeof (out->rpr_value)) 4061 backend_panic("value too large"); 4062 4063 *sz_out = offsetof(struct rep_protocol_value_response, 4064 rpr_value[w + 1]); 4065 4066 ret = (np->rn_values_count != 1)? REP_PROTOCOL_FAIL_TRUNCATED : 4067 REP_PROTOCOL_SUCCESS; 4068 (void) pthread_mutex_unlock(&np->rn_lock); 4069 return (ret); 4070 } 4071 4072 int 4073 rc_iter_next_value(rc_node_iter_t *iter, 4074 struct rep_protocol_value_response *out, size_t *sz_out, int repeat) 4075 { 4076 rc_node_t *np = iter->rni_parent; 4077 const char *vals; 4078 size_t len; 4079 4080 size_t start; 4081 size_t w; 4082 4083 rep_protocol_responseid_t result; 4084 4085 assert(*sz_out == sizeof (*out)); 4086 4087 (void) memset(out, '\0', *sz_out); 4088 4089 if (iter->rni_type != REP_PROTOCOL_ENTITY_VALUE) 4090 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4091 4092 RC_NODE_CHECK_AND_LOCK(np); 4093 4094 vals = np->rn_values; 4095 len = np->rn_values_size; 4096 4097 out->rpr_type = np->rn_valtype; 4098 4099 start = (repeat)? iter->rni_last_offset : iter->rni_offset; 4100 4101 if (len == 0 || start >= len) { 4102 result = REP_PROTOCOL_DONE; 4103 *sz_out -= sizeof (out->rpr_value); 4104 } else { 4105 w = strlcpy(out->rpr_value, &vals[start], 4106 sizeof (out->rpr_value)); 4107 4108 if (w >= sizeof (out->rpr_value)) 4109 backend_panic("value too large"); 4110 4111 *sz_out = offsetof(struct rep_protocol_value_response, 4112 rpr_value[w + 1]); 4113 4114 /* 4115 * update the offsets if we're not repeating 4116 */ 4117 if (!repeat) { 4118 iter->rni_last_offset = iter->rni_offset; 4119 iter->rni_offset += (w + 1); 4120 } 4121 4122 result = REP_PROTOCOL_SUCCESS; 4123 } 4124 4125 (void) pthread_mutex_unlock(&np->rn_lock); 4126 return (result); 4127 } 4128 4129 /* 4130 * Entry point for ITER_START from client.c. Validate the arguments & call 4131 * rc_iter_create(). 4132 * 4133 * Fails with 4134 * _NOT_SET 4135 * _DELETED 4136 * _TYPE_MISMATCH - np cannot carry type children 4137 * _BAD_REQUEST - flags is invalid 4138 * pattern is invalid 4139 * _NO_RESOURCES 4140 * _INVALID_TYPE 4141 * _TYPE_MISMATCH - *npp cannot have children of type 4142 * _BACKEND_ACCESS 4143 */ 4144 int 4145 rc_node_setup_iter(rc_node_ptr_t *npp, rc_node_iter_t **iterp, 4146 uint32_t type, uint32_t flags, const char *pattern) 4147 { 4148 rc_node_t *np; 4149 rc_iter_filter_func *f = NULL; 4150 int rc; 4151 4152 RC_NODE_PTR_GET_CHECK(np, npp); 4153 4154 if (pattern != NULL && pattern[0] == '\0') 4155 pattern = NULL; 4156 4157 if (type == REP_PROTOCOL_ENTITY_VALUE) { 4158 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) 4159 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH); 4160 if (flags != RP_ITER_START_ALL || pattern != NULL) 4161 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4162 4163 rc = rc_node_setup_value_iter(npp, iterp); 4164 assert(rc != REP_PROTOCOL_FAIL_NOT_APPLICABLE); 4165 return (rc); 4166 } 4167 4168 if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) != 4169 REP_PROTOCOL_SUCCESS) 4170 return (rc); 4171 4172 if (((flags & RP_ITER_START_FILT_MASK) == RP_ITER_START_ALL) ^ 4173 (pattern == NULL)) 4174 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4175 4176 /* Composition only works for instances & snapshots. */ 4177 if ((flags & RP_ITER_START_COMPOSED) && 4178 (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE && 4179 np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT)) 4180 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4181 4182 if (pattern != NULL) { 4183 if ((rc = rc_check_type_name(type, pattern)) != 4184 REP_PROTOCOL_SUCCESS) 4185 return (rc); 4186 pattern = strdup(pattern); 4187 if (pattern == NULL) 4188 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 4189 } 4190 4191 switch (flags & RP_ITER_START_FILT_MASK) { 4192 case RP_ITER_START_ALL: 4193 f = NULL; 4194 break; 4195 case RP_ITER_START_EXACT: 4196 f = rc_iter_filter_name; 4197 break; 4198 case RP_ITER_START_PGTYPE: 4199 if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) { 4200 free((void *)pattern); 4201 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4202 } 4203 f = rc_iter_filter_type; 4204 break; 4205 default: 4206 free((void *)pattern); 4207 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4208 } 4209 4210 rc = rc_iter_create(iterp, np, type, f, (void *)pattern, 4211 flags & RP_ITER_START_COMPOSED); 4212 if (rc != REP_PROTOCOL_SUCCESS && pattern != NULL) 4213 free((void *)pattern); 4214 4215 return (rc); 4216 } 4217 4218 /* 4219 * Do uu_list_walk_next(iter->rni_iter) until we find a child which matches 4220 * the filter. 4221 * For composed iterators, then check to see if there's an overlapping entity 4222 * (see embedded comments). If we reach the end of the list, start over at 4223 * the next level. 4224 * 4225 * Returns 4226 * _BAD_REQUEST - iter walks values 4227 * _TYPE_MISMATCH - iter does not walk type entities 4228 * _DELETED - parent was deleted 4229 * _NO_RESOURCES 4230 * _INVALID_TYPE - type is invalid 4231 * _DONE 4232 * _SUCCESS 4233 * 4234 * For composed property group iterators, can also return 4235 * _TYPE_MISMATCH - parent cannot have type children 4236 */ 4237 int 4238 rc_iter_next(rc_node_iter_t *iter, rc_node_ptr_t *out, uint32_t type) 4239 { 4240 rc_node_t *np = iter->rni_parent; 4241 rc_node_t *res; 4242 int rc; 4243 4244 if (iter->rni_type == REP_PROTOCOL_ENTITY_VALUE) 4245 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4246 4247 if (iter->rni_iter == NULL) { 4248 rc_node_clear(out, 0); 4249 return (REP_PROTOCOL_DONE); 4250 } 4251 4252 if (iter->rni_type != type) { 4253 rc_node_clear(out, 0); 4254 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH); 4255 } 4256 4257 (void) pthread_mutex_lock(&np->rn_lock); /* held by _iter_create() */ 4258 4259 if (!rc_node_wait_flag(np, RC_NODE_CHILDREN_CHANGING)) { 4260 (void) pthread_mutex_unlock(&np->rn_lock); 4261 rc_node_clear(out, 1); 4262 return (REP_PROTOCOL_FAIL_DELETED); 4263 } 4264 4265 if (iter->rni_clevel >= 0) { 4266 /* Composed iterator. Iterate over appropriate level. */ 4267 (void) pthread_mutex_unlock(&np->rn_lock); 4268 np = np->rn_cchain[iter->rni_clevel]; 4269 /* 4270 * If iter->rni_parent is an instance or a snapshot, np must 4271 * be valid since iter holds iter->rni_parent & possible 4272 * levels (service, instance, snaplevel) cannot be destroyed 4273 * while rni_parent is held. If iter->rni_parent is 4274 * a composed property group then rc_node_setup_cpg() put 4275 * a hold on np. 4276 */ 4277 4278 (void) pthread_mutex_lock(&np->rn_lock); 4279 4280 if (!rc_node_wait_flag(np, RC_NODE_CHILDREN_CHANGING)) { 4281 (void) pthread_mutex_unlock(&np->rn_lock); 4282 rc_node_clear(out, 1); 4283 return (REP_PROTOCOL_FAIL_DELETED); 4284 } 4285 } 4286 4287 assert(np->rn_flags & RC_NODE_HAS_CHILDREN); 4288 4289 for (;;) { 4290 res = uu_list_walk_next(iter->rni_iter); 4291 if (res == NULL) { 4292 rc_node_t *parent = iter->rni_parent; 4293 4294 #if COMPOSITION_DEPTH == 2 4295 if (iter->rni_clevel < 0 || iter->rni_clevel == 1) { 4296 /* release walker and lock */ 4297 rc_iter_end(iter); 4298 break; 4299 } 4300 4301 /* Stop walking current level. */ 4302 uu_list_walk_end(iter->rni_iter); 4303 iter->rni_iter = NULL; 4304 (void) pthread_mutex_unlock(&np->rn_lock); 4305 rc_node_rele_other(iter->rni_iter_node); 4306 iter->rni_iter_node = NULL; 4307 4308 /* Start walking next level. */ 4309 ++iter->rni_clevel; 4310 np = parent->rn_cchain[iter->rni_clevel]; 4311 assert(np != NULL); 4312 #else 4313 #error This code must be updated. 4314 #endif 4315 4316 (void) pthread_mutex_lock(&np->rn_lock); 4317 4318 rc = rc_node_fill_children(np, iter->rni_type); 4319 4320 if (rc == REP_PROTOCOL_SUCCESS) { 4321 iter->rni_iter = 4322 uu_list_walk_start(np->rn_children, 4323 UU_WALK_ROBUST); 4324 4325 if (iter->rni_iter == NULL) 4326 rc = REP_PROTOCOL_FAIL_NO_RESOURCES; 4327 else { 4328 iter->rni_iter_node = np; 4329 rc_node_hold_other(np); 4330 } 4331 } 4332 4333 if (rc != REP_PROTOCOL_SUCCESS) { 4334 (void) pthread_mutex_unlock(&np->rn_lock); 4335 rc_node_clear(out, 0); 4336 return (rc); 4337 } 4338 4339 continue; 4340 } 4341 4342 if (res->rn_id.rl_type != type || 4343 !iter->rni_filter(res, iter->rni_filter_arg)) 4344 continue; 4345 4346 /* 4347 * If we're composed and not at the top level, check to see if 4348 * there's an entity at a higher level with the same name. If 4349 * so, skip this one. 4350 */ 4351 if (iter->rni_clevel > 0) { 4352 rc_node_t *ent = iter->rni_parent->rn_cchain[0]; 4353 rc_node_t *pg; 4354 4355 #if COMPOSITION_DEPTH == 2 4356 assert(iter->rni_clevel == 1); 4357 4358 (void) pthread_mutex_unlock(&np->rn_lock); 4359 (void) pthread_mutex_lock(&ent->rn_lock); 4360 rc = rc_node_find_named_child(ent, res->rn_name, type, 4361 &pg); 4362 if (rc == REP_PROTOCOL_SUCCESS && pg != NULL) 4363 rc_node_rele(pg); 4364 (void) pthread_mutex_unlock(&ent->rn_lock); 4365 if (rc != REP_PROTOCOL_SUCCESS) { 4366 rc_node_clear(out, 0); 4367 return (rc); 4368 } 4369 (void) pthread_mutex_lock(&np->rn_lock); 4370 4371 /* Make sure np isn't being deleted all of a sudden. */ 4372 if (!rc_node_wait_flag(np, RC_NODE_DYING)) { 4373 (void) pthread_mutex_unlock(&np->rn_lock); 4374 rc_node_clear(out, 1); 4375 return (REP_PROTOCOL_FAIL_DELETED); 4376 } 4377 4378 if (pg != NULL) 4379 /* Keep going. */ 4380 continue; 4381 #else 4382 #error This code must be updated. 4383 #endif 4384 } 4385 4386 /* 4387 * If we're composed, iterating over property groups, and not 4388 * at the bottom level, check to see if there's a pg at lower 4389 * level with the same name. If so, return a cpg. 4390 */ 4391 if (iter->rni_clevel >= 0 && 4392 type == REP_PROTOCOL_ENTITY_PROPERTYGRP && 4393 iter->rni_clevel < COMPOSITION_DEPTH - 1) { 4394 #if COMPOSITION_DEPTH == 2 4395 rc_node_t *pg; 4396 rc_node_t *ent = iter->rni_parent->rn_cchain[1]; 4397 4398 rc_node_hold(res); /* While we drop np->rn_lock */ 4399 4400 (void) pthread_mutex_unlock(&np->rn_lock); 4401 (void) pthread_mutex_lock(&ent->rn_lock); 4402 rc = rc_node_find_named_child(ent, res->rn_name, type, 4403 &pg); 4404 /* holds pg if not NULL */ 4405 (void) pthread_mutex_unlock(&ent->rn_lock); 4406 if (rc != REP_PROTOCOL_SUCCESS) { 4407 rc_node_rele(res); 4408 rc_node_clear(out, 0); 4409 return (rc); 4410 } 4411 4412 (void) pthread_mutex_lock(&np->rn_lock); 4413 if (!rc_node_wait_flag(np, RC_NODE_DYING)) { 4414 (void) pthread_mutex_unlock(&np->rn_lock); 4415 rc_node_rele(res); 4416 if (pg != NULL) 4417 rc_node_rele(pg); 4418 rc_node_clear(out, 1); 4419 return (REP_PROTOCOL_FAIL_DELETED); 4420 } 4421 4422 if (pg == NULL) { 4423 rc_node_rele(res); 4424 } else { 4425 rc_node_t *cpg; 4426 4427 /* Keep res held for rc_node_setup_cpg(). */ 4428 4429 cpg = rc_node_alloc(); 4430 if (cpg == NULL) { 4431 (void) pthread_mutex_unlock( 4432 &np->rn_lock); 4433 rc_node_rele(res); 4434 rc_node_rele(pg); 4435 rc_node_clear(out, 0); 4436 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 4437 } 4438 4439 switch (rc_node_setup_cpg(cpg, res, pg)) { 4440 case REP_PROTOCOL_SUCCESS: 4441 res = cpg; 4442 break; 4443 4444 case REP_PROTOCOL_FAIL_TYPE_MISMATCH: 4445 /* Nevermind. */ 4446 rc_node_destroy(cpg); 4447 rc_node_rele(pg); 4448 rc_node_rele(res); 4449 break; 4450 4451 case REP_PROTOCOL_FAIL_NO_RESOURCES: 4452 rc_node_destroy(cpg); 4453 (void) pthread_mutex_unlock( 4454 &np->rn_lock); 4455 rc_node_rele(res); 4456 rc_node_rele(pg); 4457 rc_node_clear(out, 0); 4458 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 4459 4460 default: 4461 assert(0); 4462 abort(); 4463 } 4464 } 4465 #else 4466 #error This code must be updated. 4467 #endif 4468 } 4469 4470 rc_node_hold(res); 4471 (void) pthread_mutex_unlock(&np->rn_lock); 4472 break; 4473 } 4474 rc_node_assign(out, res); 4475 4476 if (res == NULL) 4477 return (REP_PROTOCOL_DONE); 4478 rc_node_rele(res); 4479 return (REP_PROTOCOL_SUCCESS); 4480 } 4481 4482 void 4483 rc_iter_destroy(rc_node_iter_t **nipp) 4484 { 4485 rc_node_iter_t *nip = *nipp; 4486 rc_node_t *np; 4487 4488 if (nip == NULL) 4489 return; /* already freed */ 4490 4491 np = nip->rni_parent; 4492 4493 if (nip->rni_filter_arg != NULL) 4494 free(nip->rni_filter_arg); 4495 nip->rni_filter_arg = NULL; 4496 4497 if (nip->rni_type == REP_PROTOCOL_ENTITY_VALUE || 4498 nip->rni_iter != NULL) { 4499 if (nip->rni_clevel < 0) 4500 (void) pthread_mutex_lock(&np->rn_lock); 4501 else 4502 (void) pthread_mutex_lock( 4503 &np->rn_cchain[nip->rni_clevel]->rn_lock); 4504 rc_iter_end(nip); /* release walker and lock */ 4505 } 4506 nip->rni_parent = NULL; 4507 4508 uu_free(nip); 4509 *nipp = NULL; 4510 } 4511 4512 int 4513 rc_node_setup_tx(rc_node_ptr_t *npp, rc_node_ptr_t *txp) 4514 { 4515 rc_node_t *np; 4516 permcheck_t *pcp; 4517 int ret; 4518 int authorized = 0; 4519 4520 RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp); 4521 4522 if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) { 4523 rc_node_rele(np); 4524 np = np->rn_cchain[0]; 4525 RC_NODE_CHECK_AND_HOLD(np); 4526 } 4527 4528 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) { 4529 rc_node_rele(np); 4530 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH); 4531 } 4532 4533 if (np->rn_id.rl_ids[ID_SNAPSHOT] != 0) { 4534 rc_node_rele(np); 4535 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED); 4536 } 4537 4538 if (client_is_privileged()) 4539 goto skip_checks; 4540 4541 #ifdef NATIVE_BUILD 4542 rc_node_rele(np); 4543 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED); 4544 #else 4545 /* permission check */ 4546 pcp = pc_create(); 4547 if (pcp == NULL) { 4548 rc_node_rele(np); 4549 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 4550 } 4551 4552 if (np->rn_id.rl_ids[ID_INSTANCE] != 0 && /* instance pg */ 4553 ((strcmp(np->rn_name, AUTH_PG_ACTIONS) == 0 && 4554 strcmp(np->rn_type, AUTH_PG_ACTIONS_TYPE) == 0) || 4555 (strcmp(np->rn_name, AUTH_PG_GENERAL_OVR) == 0 && 4556 strcmp(np->rn_type, AUTH_PG_GENERAL_OVR_TYPE) == 0))) { 4557 rc_node_t *instn; 4558 4559 /* solaris.smf.manage can be used. */ 4560 ret = perm_add_enabling(pcp, AUTH_MANAGE); 4561 4562 if (ret != REP_PROTOCOL_SUCCESS) { 4563 pc_free(pcp); 4564 rc_node_rele(np); 4565 return (ret); 4566 } 4567 4568 /* general/action_authorization values can be used. */ 4569 ret = rc_node_parent(np, &instn); 4570 if (ret != REP_PROTOCOL_SUCCESS) { 4571 assert(ret == REP_PROTOCOL_FAIL_DELETED); 4572 rc_node_rele(np); 4573 pc_free(pcp); 4574 return (REP_PROTOCOL_FAIL_DELETED); 4575 } 4576 4577 assert(instn->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE); 4578 4579 ret = perm_add_inst_action_auth(pcp, instn); 4580 rc_node_rele(instn); 4581 switch (ret) { 4582 case REP_PROTOCOL_SUCCESS: 4583 break; 4584 4585 case REP_PROTOCOL_FAIL_DELETED: 4586 case REP_PROTOCOL_FAIL_NO_RESOURCES: 4587 rc_node_rele(np); 4588 pc_free(pcp); 4589 return (ret); 4590 4591 default: 4592 bad_error("perm_add_inst_action_auth", ret); 4593 } 4594 4595 if (strcmp(np->rn_name, AUTH_PG_ACTIONS) == 0) 4596 authorized = 1; /* Don't check on commit. */ 4597 } else { 4598 ret = perm_add_enabling(pcp, AUTH_MODIFY); 4599 4600 if (ret == REP_PROTOCOL_SUCCESS) { 4601 /* propertygroup-type-specific authorization */ 4602 /* no locking because rn_type won't change anyway */ 4603 const char * const auth = 4604 perm_auth_for_pgtype(np->rn_type); 4605 4606 if (auth != NULL) 4607 ret = perm_add_enabling(pcp, auth); 4608 } 4609 4610 if (ret == REP_PROTOCOL_SUCCESS) 4611 /* propertygroup/transaction-type-specific auths */ 4612 ret = 4613 perm_add_enabling_values(pcp, np, AUTH_PROP_VALUE); 4614 4615 if (ret == REP_PROTOCOL_SUCCESS) 4616 ret = 4617 perm_add_enabling_values(pcp, np, AUTH_PROP_MODIFY); 4618 4619 /* AUTH_MANAGE can manipulate general/AUTH_PROP_ACTION */ 4620 if (ret == REP_PROTOCOL_SUCCESS && 4621 strcmp(np->rn_name, AUTH_PG_GENERAL) == 0 && 4622 strcmp(np->rn_type, AUTH_PG_GENERAL_TYPE) == 0) 4623 ret = perm_add_enabling(pcp, AUTH_MANAGE); 4624 4625 if (ret != REP_PROTOCOL_SUCCESS) { 4626 pc_free(pcp); 4627 rc_node_rele(np); 4628 return (ret); 4629 } 4630 } 4631 4632 ret = perm_granted(pcp); 4633 if (ret != 1) { 4634 pc_free(pcp); 4635 rc_node_rele(np); 4636 return (ret == 0 ? REP_PROTOCOL_FAIL_PERMISSION_DENIED : 4637 REP_PROTOCOL_FAIL_NO_RESOURCES); 4638 } 4639 4640 pc_free(pcp); 4641 #endif /* NATIVE_BUILD */ 4642 4643 skip_checks: 4644 rc_node_assign(txp, np); 4645 txp->rnp_authorized = authorized; 4646 4647 rc_node_rele(np); 4648 return (REP_PROTOCOL_SUCCESS); 4649 } 4650 4651 /* 4652 * Return 1 if the given transaction commands only modify the values of 4653 * properties other than "modify_authorization". Return -1 if any of the 4654 * commands are invalid, and 0 otherwise. 4655 */ 4656 static int 4657 tx_allow_value(const void *cmds_arg, size_t cmds_sz, rc_node_t *pg) 4658 { 4659 const struct rep_protocol_transaction_cmd *cmds; 4660 uintptr_t loc; 4661 uint32_t sz; 4662 rc_node_t *prop; 4663 boolean_t ok; 4664 4665 assert(!MUTEX_HELD(&pg->rn_lock)); 4666 4667 loc = (uintptr_t)cmds_arg; 4668 4669 while (cmds_sz > 0) { 4670 cmds = (struct rep_protocol_transaction_cmd *)loc; 4671 4672 if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE) 4673 return (-1); 4674 4675 sz = cmds->rptc_size; 4676 if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE) 4677 return (-1); 4678 4679 sz = TX_SIZE(sz); 4680 if (sz > cmds_sz) 4681 return (-1); 4682 4683 switch (cmds[0].rptc_action) { 4684 case REP_PROTOCOL_TX_ENTRY_CLEAR: 4685 break; 4686 4687 case REP_PROTOCOL_TX_ENTRY_REPLACE: 4688 /* Check type */ 4689 (void) pthread_mutex_lock(&pg->rn_lock); 4690 if (rc_node_find_named_child(pg, 4691 (const char *)cmds[0].rptc_data, 4692 REP_PROTOCOL_ENTITY_PROPERTY, &prop) == 4693 REP_PROTOCOL_SUCCESS) { 4694 ok = (prop != NULL && 4695 prop->rn_valtype == cmds[0].rptc_type); 4696 } else { 4697 /* Return more particular error? */ 4698 ok = B_FALSE; 4699 } 4700 (void) pthread_mutex_unlock(&pg->rn_lock); 4701 if (ok) 4702 break; 4703 return (0); 4704 4705 default: 4706 return (0); 4707 } 4708 4709 if (strcmp((const char *)cmds[0].rptc_data, AUTH_PROP_MODIFY) 4710 == 0) 4711 return (0); 4712 4713 loc += sz; 4714 cmds_sz -= sz; 4715 } 4716 4717 return (1); 4718 } 4719 4720 /* 4721 * Return 1 if any of the given transaction commands affect 4722 * "action_authorization". Return -1 if any of the commands are invalid and 4723 * 0 in all other cases. 4724 */ 4725 static int 4726 tx_modifies_action(const void *cmds_arg, size_t cmds_sz) 4727 { 4728 const struct rep_protocol_transaction_cmd *cmds; 4729 uintptr_t loc; 4730 uint32_t sz; 4731 4732 loc = (uintptr_t)cmds_arg; 4733 4734 while (cmds_sz > 0) { 4735 cmds = (struct rep_protocol_transaction_cmd *)loc; 4736 4737 if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE) 4738 return (-1); 4739 4740 sz = cmds->rptc_size; 4741 if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE) 4742 return (-1); 4743 4744 sz = TX_SIZE(sz); 4745 if (sz > cmds_sz) 4746 return (-1); 4747 4748 if (strcmp((const char *)cmds[0].rptc_data, AUTH_PROP_ACTION) 4749 == 0) 4750 return (1); 4751 4752 loc += sz; 4753 cmds_sz -= sz; 4754 } 4755 4756 return (0); 4757 } 4758 4759 /* 4760 * Returns 1 if the transaction commands only modify properties named 4761 * 'enabled'. 4762 */ 4763 static int 4764 tx_only_enabled(const void *cmds_arg, size_t cmds_sz) 4765 { 4766 const struct rep_protocol_transaction_cmd *cmd; 4767 uintptr_t loc; 4768 uint32_t sz; 4769 4770 loc = (uintptr_t)cmds_arg; 4771 4772 while (cmds_sz > 0) { 4773 cmd = (struct rep_protocol_transaction_cmd *)loc; 4774 4775 if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE) 4776 return (-1); 4777 4778 sz = cmd->rptc_size; 4779 if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE) 4780 return (-1); 4781 4782 sz = TX_SIZE(sz); 4783 if (sz > cmds_sz) 4784 return (-1); 4785 4786 if (strcmp((const char *)cmd->rptc_data, AUTH_PROP_ENABLED) 4787 != 0) 4788 return (0); 4789 4790 loc += sz; 4791 cmds_sz -= sz; 4792 } 4793 4794 return (1); 4795 } 4796 4797 int 4798 rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz) 4799 { 4800 rc_node_t *np = txp->rnp_node; 4801 rc_node_t *pp; 4802 rc_node_t *nnp; 4803 rc_node_pg_notify_t *pnp; 4804 int rc; 4805 permcheck_t *pcp; 4806 int granted, normal; 4807 4808 RC_NODE_CHECK(np); 4809 4810 if (!client_is_privileged() && !txp->rnp_authorized) { 4811 #ifdef NATIVE_BUILD 4812 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED); 4813 #else 4814 /* permission check: depends on contents of transaction */ 4815 pcp = pc_create(); 4816 if (pcp == NULL) 4817 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 4818 4819 /* If normal is cleared, we won't do the normal checks. */ 4820 normal = 1; 4821 rc = REP_PROTOCOL_SUCCESS; 4822 4823 if (strcmp(np->rn_name, AUTH_PG_GENERAL) == 0 && 4824 strcmp(np->rn_type, AUTH_PG_GENERAL_TYPE) == 0) { 4825 /* Touching general[framework]/action_authorization? */ 4826 rc = tx_modifies_action(cmds, cmds_sz); 4827 if (rc == -1) { 4828 pc_free(pcp); 4829 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4830 } 4831 4832 if (rc) { 4833 /* Yes: only AUTH_MANAGE can be used. */ 4834 rc = perm_add_enabling(pcp, AUTH_MANAGE); 4835 normal = 0; 4836 } else { 4837 rc = REP_PROTOCOL_SUCCESS; 4838 } 4839 } else if (np->rn_id.rl_ids[ID_INSTANCE] != 0 && 4840 strcmp(np->rn_name, AUTH_PG_GENERAL_OVR) == 0 && 4841 strcmp(np->rn_type, AUTH_PG_GENERAL_OVR_TYPE) == 0) { 4842 rc_node_t *instn; 4843 4844 rc = tx_only_enabled(cmds, cmds_sz); 4845 if (rc == -1) { 4846 pc_free(pcp); 4847 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4848 } 4849 4850 if (rc) { 4851 rc = rc_node_parent(np, &instn); 4852 if (rc != REP_PROTOCOL_SUCCESS) { 4853 assert(rc == REP_PROTOCOL_FAIL_DELETED); 4854 pc_free(pcp); 4855 return (rc); 4856 } 4857 4858 assert(instn->rn_id.rl_type == 4859 REP_PROTOCOL_ENTITY_INSTANCE); 4860 4861 rc = perm_add_inst_action_auth(pcp, instn); 4862 rc_node_rele(instn); 4863 switch (rc) { 4864 case REP_PROTOCOL_SUCCESS: 4865 break; 4866 4867 case REP_PROTOCOL_FAIL_DELETED: 4868 case REP_PROTOCOL_FAIL_NO_RESOURCES: 4869 pc_free(pcp); 4870 return (rc); 4871 4872 default: 4873 bad_error("perm_add_inst_action_auth", 4874 rc); 4875 } 4876 } else { 4877 rc = REP_PROTOCOL_SUCCESS; 4878 } 4879 } 4880 4881 if (rc == REP_PROTOCOL_SUCCESS && normal) { 4882 rc = perm_add_enabling(pcp, AUTH_MODIFY); 4883 4884 if (rc == REP_PROTOCOL_SUCCESS) { 4885 /* Add pgtype-specific authorization. */ 4886 const char * const auth = 4887 perm_auth_for_pgtype(np->rn_type); 4888 4889 if (auth != NULL) 4890 rc = perm_add_enabling(pcp, auth); 4891 } 4892 4893 /* Add pg-specific modify_authorization auths. */ 4894 if (rc == REP_PROTOCOL_SUCCESS) 4895 rc = perm_add_enabling_values(pcp, np, 4896 AUTH_PROP_MODIFY); 4897 4898 /* If value_authorization values are ok, add them. */ 4899 if (rc == REP_PROTOCOL_SUCCESS) { 4900 rc = tx_allow_value(cmds, cmds_sz, np); 4901 if (rc == -1) 4902 rc = REP_PROTOCOL_FAIL_BAD_REQUEST; 4903 else if (rc) 4904 rc = perm_add_enabling_values(pcp, np, 4905 AUTH_PROP_VALUE); 4906 } 4907 } 4908 4909 if (rc == REP_PROTOCOL_SUCCESS) { 4910 granted = perm_granted(pcp); 4911 if (granted < 0) 4912 rc = REP_PROTOCOL_FAIL_NO_RESOURCES; 4913 } 4914 4915 pc_free(pcp); 4916 4917 if (rc != REP_PROTOCOL_SUCCESS) 4918 return (rc); 4919 4920 if (!granted) 4921 return (REP_PROTOCOL_FAIL_PERMISSION_DENIED); 4922 #endif /* NATIVE_BUILD */ 4923 } 4924 4925 nnp = rc_node_alloc(); 4926 if (nnp == NULL) 4927 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 4928 4929 nnp->rn_id = np->rn_id; /* structure assignment */ 4930 nnp->rn_hash = np->rn_hash; 4931 nnp->rn_name = strdup(np->rn_name); 4932 nnp->rn_type = strdup(np->rn_type); 4933 nnp->rn_pgflags = np->rn_pgflags; 4934 4935 nnp->rn_flags = RC_NODE_IN_TX | RC_NODE_USING_PARENT; 4936 4937 if (nnp->rn_name == NULL || nnp->rn_type == NULL) { 4938 rc_node_destroy(nnp); 4939 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 4940 } 4941 4942 (void) pthread_mutex_lock(&np->rn_lock); 4943 /* 4944 * We must have all of the old properties in the cache, or the 4945 * database deletions could cause inconsistencies. 4946 */ 4947 if ((rc = rc_node_fill_children(np, REP_PROTOCOL_ENTITY_PROPERTY)) != 4948 REP_PROTOCOL_SUCCESS) { 4949 (void) pthread_mutex_unlock(&np->rn_lock); 4950 rc_node_destroy(nnp); 4951 return (rc); 4952 } 4953 4954 if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) { 4955 (void) pthread_mutex_unlock(&np->rn_lock); 4956 rc_node_destroy(nnp); 4957 return (REP_PROTOCOL_FAIL_DELETED); 4958 } 4959 4960 if (np->rn_flags & RC_NODE_OLD) { 4961 rc_node_rele_flag(np, RC_NODE_USING_PARENT); 4962 (void) pthread_mutex_unlock(&np->rn_lock); 4963 rc_node_destroy(nnp); 4964 return (REP_PROTOCOL_FAIL_NOT_LATEST); 4965 } 4966 4967 pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING); 4968 if (pp == NULL) { 4969 /* our parent is gone, we're going next... */ 4970 rc_node_destroy(nnp); 4971 (void) pthread_mutex_lock(&np->rn_lock); 4972 if (np->rn_flags & RC_NODE_OLD) { 4973 (void) pthread_mutex_unlock(&np->rn_lock); 4974 return (REP_PROTOCOL_FAIL_NOT_LATEST); 4975 } 4976 (void) pthread_mutex_unlock(&np->rn_lock); 4977 return (REP_PROTOCOL_FAIL_DELETED); 4978 } 4979 (void) pthread_mutex_unlock(&pp->rn_lock); 4980 4981 /* 4982 * prepare for the transaction 4983 */ 4984 (void) pthread_mutex_lock(&np->rn_lock); 4985 if (!rc_node_hold_flag(np, RC_NODE_IN_TX)) { 4986 (void) pthread_mutex_unlock(&np->rn_lock); 4987 (void) pthread_mutex_lock(&pp->rn_lock); 4988 rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING); 4989 (void) pthread_mutex_unlock(&pp->rn_lock); 4990 rc_node_destroy(nnp); 4991 return (REP_PROTOCOL_FAIL_DELETED); 4992 } 4993 nnp->rn_gen_id = np->rn_gen_id; 4994 (void) pthread_mutex_unlock(&np->rn_lock); 4995 4996 /* Sets nnp->rn_gen_id on success. */ 4997 rc = object_tx_commit(&np->rn_id, cmds, cmds_sz, &nnp->rn_gen_id); 4998 4999 (void) pthread_mutex_lock(&np->rn_lock); 5000 if (rc != REP_PROTOCOL_SUCCESS) { 5001 rc_node_rele_flag(np, RC_NODE_IN_TX); 5002 (void) pthread_mutex_unlock(&np->rn_lock); 5003 (void) pthread_mutex_lock(&pp->rn_lock); 5004 rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING); 5005 (void) pthread_mutex_unlock(&pp->rn_lock); 5006 rc_node_destroy(nnp); 5007 rc_node_clear(txp, 0); 5008 if (rc == REP_PROTOCOL_DONE) 5009 rc = REP_PROTOCOL_SUCCESS; /* successful empty tx */ 5010 return (rc); 5011 } 5012 5013 /* 5014 * Notify waiters 5015 */ 5016 (void) pthread_mutex_lock(&rc_pg_notify_lock); 5017 while ((pnp = uu_list_first(np->rn_pg_notify_list)) != NULL) 5018 rc_pg_notify_fire(pnp); 5019 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 5020 5021 np->rn_flags |= RC_NODE_OLD; 5022 (void) pthread_mutex_unlock(&np->rn_lock); 5023 5024 rc_notify_remove_node(np); 5025 5026 /* 5027 * replace np with nnp 5028 */ 5029 rc_node_relink_child(pp, np, nnp); 5030 5031 /* 5032 * all done -- clear the transaction. 5033 */ 5034 rc_node_clear(txp, 0); 5035 5036 return (REP_PROTOCOL_SUCCESS); 5037 } 5038 5039 void 5040 rc_pg_notify_init(rc_node_pg_notify_t *pnp) 5041 { 5042 uu_list_node_init(pnp, &pnp->rnpn_node, rc_pg_notify_pool); 5043 pnp->rnpn_pg = NULL; 5044 pnp->rnpn_fd = -1; 5045 } 5046 5047 int 5048 rc_pg_notify_setup(rc_node_pg_notify_t *pnp, rc_node_ptr_t *npp, int fd) 5049 { 5050 rc_node_t *np; 5051 5052 RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp); 5053 5054 if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) { 5055 (void) pthread_mutex_unlock(&np->rn_lock); 5056 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 5057 } 5058 5059 /* 5060 * wait for any transaction in progress to complete 5061 */ 5062 if (!rc_node_wait_flag(np, RC_NODE_IN_TX)) { 5063 (void) pthread_mutex_unlock(&np->rn_lock); 5064 return (REP_PROTOCOL_FAIL_DELETED); 5065 } 5066 5067 if (np->rn_flags & RC_NODE_OLD) { 5068 (void) pthread_mutex_unlock(&np->rn_lock); 5069 return (REP_PROTOCOL_FAIL_NOT_LATEST); 5070 } 5071 5072 (void) pthread_mutex_lock(&rc_pg_notify_lock); 5073 rc_pg_notify_fire(pnp); 5074 pnp->rnpn_pg = np; 5075 pnp->rnpn_fd = fd; 5076 (void) uu_list_insert_after(np->rn_pg_notify_list, NULL, pnp); 5077 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 5078 5079 (void) pthread_mutex_unlock(&np->rn_lock); 5080 return (REP_PROTOCOL_SUCCESS); 5081 } 5082 5083 void 5084 rc_pg_notify_fini(rc_node_pg_notify_t *pnp) 5085 { 5086 (void) pthread_mutex_lock(&rc_pg_notify_lock); 5087 rc_pg_notify_fire(pnp); 5088 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 5089 5090 uu_list_node_fini(pnp, &pnp->rnpn_node, rc_pg_notify_pool); 5091 } 5092 5093 void 5094 rc_notify_info_init(rc_notify_info_t *rnip) 5095 { 5096 int i; 5097 5098 uu_list_node_init(rnip, &rnip->rni_list_node, rc_notify_info_pool); 5099 uu_list_node_init(&rnip->rni_notify, &rnip->rni_notify.rcn_list_node, 5100 rc_notify_pool); 5101 5102 rnip->rni_notify.rcn_node = NULL; 5103 rnip->rni_notify.rcn_info = rnip; 5104 5105 bzero(rnip->rni_namelist, sizeof (rnip->rni_namelist)); 5106 bzero(rnip->rni_typelist, sizeof (rnip->rni_typelist)); 5107 5108 (void) pthread_cond_init(&rnip->rni_cv, NULL); 5109 5110 for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) { 5111 rnip->rni_namelist[i] = NULL; 5112 rnip->rni_typelist[i] = NULL; 5113 } 5114 } 5115 5116 static void 5117 rc_notify_info_insert_locked(rc_notify_info_t *rnip) 5118 { 5119 assert(MUTEX_HELD(&rc_pg_notify_lock)); 5120 5121 assert(!(rnip->rni_flags & RC_NOTIFY_ACTIVE)); 5122 5123 rnip->rni_flags |= RC_NOTIFY_ACTIVE; 5124 (void) uu_list_insert_after(rc_notify_info_list, NULL, rnip); 5125 (void) uu_list_insert_before(rc_notify_list, NULL, &rnip->rni_notify); 5126 } 5127 5128 static void 5129 rc_notify_info_remove_locked(rc_notify_info_t *rnip) 5130 { 5131 rc_notify_t *me = &rnip->rni_notify; 5132 rc_notify_t *np; 5133 5134 assert(MUTEX_HELD(&rc_pg_notify_lock)); 5135 5136 assert(rnip->rni_flags & RC_NOTIFY_ACTIVE); 5137 5138 assert(!(rnip->rni_flags & RC_NOTIFY_DRAIN)); 5139 rnip->rni_flags |= RC_NOTIFY_DRAIN; 5140 (void) pthread_cond_broadcast(&rnip->rni_cv); 5141 5142 (void) uu_list_remove(rc_notify_info_list, rnip); 5143 5144 /* 5145 * clean up any notifications at the beginning of the list 5146 */ 5147 if (uu_list_first(rc_notify_list) == me) { 5148 while ((np = uu_list_next(rc_notify_list, me)) != NULL && 5149 np->rcn_info == NULL) 5150 rc_notify_remove_locked(np); 5151 } 5152 (void) uu_list_remove(rc_notify_list, me); 5153 5154 while (rnip->rni_waiters) { 5155 (void) pthread_cond_broadcast(&rc_pg_notify_cv); 5156 (void) pthread_cond_broadcast(&rnip->rni_cv); 5157 (void) pthread_cond_wait(&rnip->rni_cv, &rc_pg_notify_lock); 5158 } 5159 5160 rnip->rni_flags &= ~(RC_NOTIFY_DRAIN | RC_NOTIFY_ACTIVE); 5161 } 5162 5163 static int 5164 rc_notify_info_add_watch(rc_notify_info_t *rnip, const char **arr, 5165 const char *name) 5166 { 5167 int i; 5168 int rc; 5169 char *f; 5170 5171 rc = rc_check_type_name(REP_PROTOCOL_ENTITY_PROPERTYGRP, name); 5172 if (rc != REP_PROTOCOL_SUCCESS) 5173 return (rc); 5174 5175 f = strdup(name); 5176 if (f == NULL) 5177 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 5178 5179 (void) pthread_mutex_lock(&rc_pg_notify_lock); 5180 5181 while (rnip->rni_flags & RC_NOTIFY_EMPTYING) 5182 (void) pthread_cond_wait(&rnip->rni_cv, &rc_pg_notify_lock); 5183 5184 for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) 5185 if (arr[i] == NULL) 5186 break; 5187 5188 if (i == RC_NOTIFY_MAX_NAMES) { 5189 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 5190 free(f); 5191 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 5192 } 5193 5194 arr[i] = f; 5195 if (!(rnip->rni_flags & RC_NOTIFY_ACTIVE)) 5196 rc_notify_info_insert_locked(rnip); 5197 5198 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 5199 return (REP_PROTOCOL_SUCCESS); 5200 } 5201 5202 int 5203 rc_notify_info_add_name(rc_notify_info_t *rnip, const char *name) 5204 { 5205 return (rc_notify_info_add_watch(rnip, rnip->rni_namelist, name)); 5206 } 5207 5208 int 5209 rc_notify_info_add_type(rc_notify_info_t *rnip, const char *type) 5210 { 5211 return (rc_notify_info_add_watch(rnip, rnip->rni_typelist, type)); 5212 } 5213 5214 /* 5215 * Wait for and report an event of interest to rnip, a notification client 5216 */ 5217 int 5218 rc_notify_info_wait(rc_notify_info_t *rnip, rc_node_ptr_t *out, 5219 char *outp, size_t sz) 5220 { 5221 rc_notify_t *np; 5222 rc_notify_t *me = &rnip->rni_notify; 5223 rc_node_t *nnp; 5224 rc_notify_delete_t *ndp; 5225 5226 int am_first_info; 5227 5228 if (sz > 0) 5229 outp[0] = 0; 5230 5231 (void) pthread_mutex_lock(&rc_pg_notify_lock); 5232 5233 while ((rnip->rni_flags & (RC_NOTIFY_ACTIVE | RC_NOTIFY_DRAIN)) == 5234 RC_NOTIFY_ACTIVE) { 5235 /* 5236 * If I'm first on the notify list, it is my job to 5237 * clean up any notifications I pass by. I can't do that 5238 * if someone is blocking the list from removals, so I 5239 * have to wait until they have all drained. 5240 */ 5241 am_first_info = (uu_list_first(rc_notify_list) == me); 5242 if (am_first_info && rc_notify_in_use) { 5243 rnip->rni_waiters++; 5244 (void) pthread_cond_wait(&rc_pg_notify_cv, 5245 &rc_pg_notify_lock); 5246 rnip->rni_waiters--; 5247 continue; 5248 } 5249 5250 /* 5251 * Search the list for a node of interest. 5252 */ 5253 np = uu_list_next(rc_notify_list, me); 5254 while (np != NULL && !rc_notify_info_interested(rnip, np)) { 5255 rc_notify_t *next = uu_list_next(rc_notify_list, np); 5256 5257 if (am_first_info) { 5258 if (np->rcn_info) { 5259 /* 5260 * Passing another client -- stop 5261 * cleaning up notifications 5262 */ 5263 am_first_info = 0; 5264 } else { 5265 rc_notify_remove_locked(np); 5266 } 5267 } 5268 np = next; 5269 } 5270 5271 /* 5272 * Nothing of interest -- wait for notification 5273 */ 5274 if (np == NULL) { 5275 rnip->rni_waiters++; 5276 (void) pthread_cond_wait(&rnip->rni_cv, 5277 &rc_pg_notify_lock); 5278 rnip->rni_waiters--; 5279 continue; 5280 } 5281 5282 /* 5283 * found something to report -- move myself after the 5284 * notification and process it. 5285 */ 5286 (void) uu_list_remove(rc_notify_list, me); 5287 (void) uu_list_insert_after(rc_notify_list, np, me); 5288 5289 if ((ndp = np->rcn_delete) != NULL) { 5290 (void) strlcpy(outp, ndp->rnd_fmri, sz); 5291 if (am_first_info) 5292 rc_notify_remove_locked(np); 5293 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 5294 rc_node_clear(out, 0); 5295 return (REP_PROTOCOL_SUCCESS); 5296 } 5297 5298 nnp = np->rcn_node; 5299 assert(nnp != NULL); 5300 5301 /* 5302 * We can't bump nnp's reference count without grabbing its 5303 * lock, and rc_pg_notify_lock is a leaf lock. So we 5304 * temporarily block all removals to keep nnp from 5305 * disappearing. 5306 */ 5307 rc_notify_in_use++; 5308 assert(rc_notify_in_use > 0); 5309 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 5310 5311 rc_node_assign(out, nnp); 5312 5313 (void) pthread_mutex_lock(&rc_pg_notify_lock); 5314 assert(rc_notify_in_use > 0); 5315 rc_notify_in_use--; 5316 if (am_first_info) 5317 rc_notify_remove_locked(np); 5318 if (rc_notify_in_use == 0) 5319 (void) pthread_cond_broadcast(&rc_pg_notify_cv); 5320 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 5321 5322 return (REP_PROTOCOL_SUCCESS); 5323 } 5324 /* 5325 * If we're the last one out, let people know it's clear. 5326 */ 5327 if (rnip->rni_waiters == 0) 5328 (void) pthread_cond_broadcast(&rnip->rni_cv); 5329 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 5330 return (REP_PROTOCOL_DONE); 5331 } 5332 5333 static void 5334 rc_notify_info_reset(rc_notify_info_t *rnip) 5335 { 5336 int i; 5337 5338 (void) pthread_mutex_lock(&rc_pg_notify_lock); 5339 if (rnip->rni_flags & RC_NOTIFY_ACTIVE) 5340 rc_notify_info_remove_locked(rnip); 5341 assert(!(rnip->rni_flags & (RC_NOTIFY_DRAIN | RC_NOTIFY_EMPTYING))); 5342 rnip->rni_flags |= RC_NOTIFY_EMPTYING; 5343 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 5344 5345 for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) { 5346 if (rnip->rni_namelist[i] != NULL) { 5347 free((void *)rnip->rni_namelist[i]); 5348 rnip->rni_namelist[i] = NULL; 5349 } 5350 if (rnip->rni_typelist[i] != NULL) { 5351 free((void *)rnip->rni_typelist[i]); 5352 rnip->rni_typelist[i] = NULL; 5353 } 5354 } 5355 5356 (void) pthread_mutex_lock(&rc_pg_notify_lock); 5357 rnip->rni_flags &= ~RC_NOTIFY_EMPTYING; 5358 (void) pthread_mutex_unlock(&rc_pg_notify_lock); 5359 } 5360 5361 void 5362 rc_notify_info_fini(rc_notify_info_t *rnip) 5363 { 5364 rc_notify_info_reset(rnip); 5365 5366 uu_list_node_fini(rnip, &rnip->rni_list_node, rc_notify_info_pool); 5367 uu_list_node_fini(&rnip->rni_notify, &rnip->rni_notify.rcn_list_node, 5368 rc_notify_pool); 5369 } 5370