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