xref: /titanic_51/usr/src/cmd/svc/configd/rc_node.c (revision a29160b0f5f650ae34e2273cacdd3eff15c62fba)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5d3186a0eSjeanm  * Common Development and Distribution License (the "License").
6d3186a0eSjeanm  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
213eae19d9Swesolows 
227c478bd9Sstevel@tonic-gate /*
23134a1f4eSCasper H.S. Dik  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24*a29160b0SRobert Mustacchi  * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
286643e1ffSbustos  * rc_node.c - In-memory SCF object management
297c478bd9Sstevel@tonic-gate  *
306643e1ffSbustos  * This layer manages the in-memory cache (the Repository Cache) of SCF
316643e1ffSbustos  * data.  Read requests are usually satisfied from here, but may require
326643e1ffSbustos  * load calls to the "object" layer.  Modify requests always write-through
336643e1ffSbustos  * to the object layer.
346643e1ffSbustos  *
356643e1ffSbustos  * SCF data comprises scopes, services, instances, snapshots, snaplevels,
366643e1ffSbustos  * property groups, properties, and property values.  All but the last are
376643e1ffSbustos  * known here as "entities" and are represented by rc_node_t data
386643e1ffSbustos  * structures.  (Property values are kept in the rn_values member of the
396643e1ffSbustos  * respective property, not as separate objects.)  All entities besides
406643e1ffSbustos  * the "localhost" scope have some entity as a parent, and therefore form
416643e1ffSbustos  * a tree.
427c478bd9Sstevel@tonic-gate  *
437c478bd9Sstevel@tonic-gate  * The entity tree is rooted at rc_scope, which rc_node_init() initializes to
447c478bd9Sstevel@tonic-gate  * the "localhost" scope.  The tree is filled in from the database on-demand
456643e1ffSbustos  * by rc_node_fill_children().
467c478bd9Sstevel@tonic-gate  *
476643e1ffSbustos  * rc_node_t's are also placed in the cache_hash[] hash table, for rapid
486643e1ffSbustos  * lookup.
497c478bd9Sstevel@tonic-gate  *
506643e1ffSbustos  * Multiple threads may service client requests, so access to each
516643e1ffSbustos  * rc_node_t is synchronized by its rn_lock member.  Some fields are
526643e1ffSbustos  * protected by bits in the rn_flags field instead, to support operations
536643e1ffSbustos  * which need to drop rn_lock, for example to respect locking order.  Such
546643e1ffSbustos  * flags should be manipulated with the rc_node_{hold,rele}_flag()
556643e1ffSbustos  * functions.
566643e1ffSbustos  *
576643e1ffSbustos  * We track references to nodes to tell when they can be free()d.  rn_refs
586643e1ffSbustos  * should be incremented with rc_node_hold() on the creation of client
596643e1ffSbustos  * references (rc_node_ptr_t's and rc_iter_t's).  rn_erefs ("ephemeral
606643e1ffSbustos  * references") should be incremented when a pointer is read into a local
616643e1ffSbustos  * variable of a thread, with rc_node_hold_ephemeral_locked().  This
626643e1ffSbustos  * hasn't been fully implemented, however, so rc_node_rele() tolerates
636643e1ffSbustos  * rn_erefs being 0.  Some code which predates rn_erefs counts ephemeral
646643e1ffSbustos  * references in rn_refs.  Other references are tracked by the
656643e1ffSbustos  * rn_other_refs field and the RC_NODE_DEAD, RC_NODE_IN_PARENT,
666643e1ffSbustos  * RC_NODE_OLD, and RC_NODE_ON_FORMER flags.
677c478bd9Sstevel@tonic-gate  *
687c478bd9Sstevel@tonic-gate  * Locking rules: To dereference an rc_node_t * (usually to lock it), you must
697c478bd9Sstevel@tonic-gate  * have a hold (rc_node_hold()) on it or otherwise be sure that it hasn't been
707c478bd9Sstevel@tonic-gate  * rc_node_destroy()ed (hold a lock on its parent or child, hold a flag,
717c478bd9Sstevel@tonic-gate  * etc.).  Once you have locked an rc_node_t you must check its rn_flags for
727c478bd9Sstevel@tonic-gate  * RC_NODE_DEAD before you can use it.  This is usually done with the
737c478bd9Sstevel@tonic-gate  * rc_node_{wait,hold}_flag() functions (often via the rc_node_check_*()
747c478bd9Sstevel@tonic-gate  * functions & RC_NODE_*() macros), which fail if the object has died.
757c478bd9Sstevel@tonic-gate  *
766643e1ffSbustos  * When a transactional node (property group or snapshot) is updated,
776643e1ffSbustos  * a new node takes the place of the old node in the global hash and the
786643e1ffSbustos  * old node is hung off of the rn_former list of the new node.  At the
796643e1ffSbustos  * same time, all of its children have their rn_parent_ref pointer set,
806643e1ffSbustos  * and any holds they have are reflected in the old node's rn_other_refs
816643e1ffSbustos  * count.  This is automatically kept up to date until the final reference
826643e1ffSbustos  * to the subgraph is dropped, at which point the node is unrefed and
836643e1ffSbustos  * destroyed, along with all of its children.
846643e1ffSbustos  *
853eae19d9Swesolows  * Because name service lookups may take a long time and, more importantly
863eae19d9Swesolows  * may trigger additional accesses to the repository, perm_granted() must be
873eae19d9Swesolows  * called without holding any locks.
883eae19d9Swesolows  *
897c478bd9Sstevel@tonic-gate  * An ITER_START for a non-ENTITY_VALUE induces an rc_node_fill_children()
907c478bd9Sstevel@tonic-gate  * call via rc_node_setup_iter() to populate the rn_children uu_list of the
917c478bd9Sstevel@tonic-gate  * rc_node_t * in question and a call to uu_list_walk_start() on that list.  For
927c478bd9Sstevel@tonic-gate  * ITER_READ, rc_iter_next() uses uu_list_walk_next() to find the next
937c478bd9Sstevel@tonic-gate  * apropriate child.
947c478bd9Sstevel@tonic-gate  *
957c478bd9Sstevel@tonic-gate  * An ITER_START for an ENTITY_VALUE makes sure the node has its values
967c478bd9Sstevel@tonic-gate  * filled, and sets up the iterator.  An ITER_READ_VALUE just copies out
977c478bd9Sstevel@tonic-gate  * the proper values and updates the offset information.
987c478bd9Sstevel@tonic-gate  *
997c478bd9Sstevel@tonic-gate  * To allow aliases, snapshots are implemented with a level of indirection.
1007c478bd9Sstevel@tonic-gate  * A snapshot rc_node_t has a snapid which refers to an rc_snapshot_t in
1017c478bd9Sstevel@tonic-gate  * snapshot.c which contains the authoritative snaplevel information.  The
1027c478bd9Sstevel@tonic-gate  * snapid is "assigned" by rc_attach_snapshot().
1037c478bd9Sstevel@tonic-gate  *
1047c478bd9Sstevel@tonic-gate  * We provide the client layer with rc_node_ptr_t's to reference objects.
1057c478bd9Sstevel@tonic-gate  * Objects referred to by them are automatically held & released by
1067c478bd9Sstevel@tonic-gate  * rc_node_assign() & rc_node_clear().  The RC_NODE_PTR_*() macros are used at
1077c478bd9Sstevel@tonic-gate  * client.c entry points to read the pointers.  They fetch the pointer to the
1087c478bd9Sstevel@tonic-gate  * object, return (from the function) if it is dead, and lock, hold, or hold
1097c478bd9Sstevel@tonic-gate  * a flag of the object.
1107c478bd9Sstevel@tonic-gate  */
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate /*
1137c478bd9Sstevel@tonic-gate  * Permission checking is authorization-based: some operations may only
1147c478bd9Sstevel@tonic-gate  * proceed if the user has been assigned at least one of a set of
1157c478bd9Sstevel@tonic-gate  * authorization strings.  The set of enabling authorizations depends on the
1167c478bd9Sstevel@tonic-gate  * operation and the target object.  The set of authorizations assigned to
117134a1f4eSCasper H.S. Dik  * a user is determined by an algorithm defined in libsecdb.
1187c478bd9Sstevel@tonic-gate  *
1197c478bd9Sstevel@tonic-gate  * The fastest way to decide whether the two sets intersect is by entering the
1207c478bd9Sstevel@tonic-gate  * strings into a hash table and detecting collisions, which takes linear time
1217c478bd9Sstevel@tonic-gate  * in the total size of the sets.  Except for the authorization patterns which
1227c478bd9Sstevel@tonic-gate  * may be assigned to users, which without advanced pattern-matching
1237c478bd9Sstevel@tonic-gate  * algorithms will take O(n) in the number of enabling authorizations, per
1247c478bd9Sstevel@tonic-gate  * pattern.
1257c478bd9Sstevel@tonic-gate  *
1267c478bd9Sstevel@tonic-gate  * We can achieve some practical speed-ups by noting that if we enter all of
1277c478bd9Sstevel@tonic-gate  * the authorizations from one of the sets into the hash table we can merely
1287c478bd9Sstevel@tonic-gate  * check the elements of the second set for existence without adding them.
1297c478bd9Sstevel@tonic-gate  * This reduces memory requirements and hash table clutter.  The enabling set
1307c478bd9Sstevel@tonic-gate  * is well suited for this because it is internal to configd (for now, at
1317c478bd9Sstevel@tonic-gate  * least).  Combine this with short-circuiting and we can even minimize the
1327c478bd9Sstevel@tonic-gate  * number of queries to the security databases (user_attr & prof_attr).
1337c478bd9Sstevel@tonic-gate  *
1347c478bd9Sstevel@tonic-gate  * To force this usage onto clients we provide functions for adding
1357c478bd9Sstevel@tonic-gate  * authorizations to the enabling set of a permission context structure
1367c478bd9Sstevel@tonic-gate  * (perm_add_*()) and one to decide whether the the user associated with the
1377c478bd9Sstevel@tonic-gate  * current door call client possesses any of them (perm_granted()).
1387c478bd9Sstevel@tonic-gate  *
1397c478bd9Sstevel@tonic-gate  * At some point, a generic version of this should move to libsecdb.
1405b7f77adStw21770  *
1415b7f77adStw21770  * While entering the enabling strings into the hash table, we keep track
1425b7f77adStw21770  * of which is the most specific for use in generating auditing events.
1435b7f77adStw21770  * See the "Collecting the Authorization String" section of the "SMF Audit
1445b7f77adStw21770  * Events" block comment below.
1457c478bd9Sstevel@tonic-gate  */
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate /*
1487c478bd9Sstevel@tonic-gate  * Composition is the combination of sets of properties.  The sets are ordered
1497c478bd9Sstevel@tonic-gate  * and properties in higher sets obscure properties of the same name in lower
1507c478bd9Sstevel@tonic-gate  * sets.  Here we present a composed view of an instance's properties as the
1517c478bd9Sstevel@tonic-gate  * union of its properties and its service's properties.  Similarly the
1527c478bd9Sstevel@tonic-gate  * properties of snaplevels are combined to form a composed view of the
1537c478bd9Sstevel@tonic-gate  * properties of a snapshot (which should match the composed view of the
1547c478bd9Sstevel@tonic-gate  * properties of the instance when the snapshot was taken).
1557c478bd9Sstevel@tonic-gate  *
1567c478bd9Sstevel@tonic-gate  * In terms of the client interface, the client may request that a property
1577c478bd9Sstevel@tonic-gate  * group iterator for an instance or snapshot be composed.  Property groups
1587c478bd9Sstevel@tonic-gate  * traversed by such an iterator may not have the target entity as a parent.
1597c478bd9Sstevel@tonic-gate  * Similarly, the properties traversed by a property iterator for those
1607c478bd9Sstevel@tonic-gate  * property groups may not have the property groups iterated as parents.
1617c478bd9Sstevel@tonic-gate  *
1627c478bd9Sstevel@tonic-gate  * Implementation requires that iterators for instances and snapshots be
1637c478bd9Sstevel@tonic-gate  * composition-savvy, and that we have a "composed property group" entity
1647c478bd9Sstevel@tonic-gate  * which represents the composition of a number of property groups.  Iteration
1657c478bd9Sstevel@tonic-gate  * over "composed property groups" yields properties which may have different
1667c478bd9Sstevel@tonic-gate  * parents, but for all other operations a composed property group behaves
1677c478bd9Sstevel@tonic-gate  * like the top-most property group it represents.
1687c478bd9Sstevel@tonic-gate  *
1697c478bd9Sstevel@tonic-gate  * The implementation is based on the rn_cchain[] array of rc_node_t pointers
1707c478bd9Sstevel@tonic-gate  * in rc_node_t.  For instances, the pointers point to the instance and its
1717c478bd9Sstevel@tonic-gate  * parent service.  For snapshots they point to the child snaplevels, and for
1727c478bd9Sstevel@tonic-gate  * composed property groups they point to property groups.  A composed
1737c478bd9Sstevel@tonic-gate  * iterator carries an index into rn_cchain[].  Thus most of the magic ends up
1747c478bd9Sstevel@tonic-gate  * int the rc_iter_*() code.
1757c478bd9Sstevel@tonic-gate  */
1765b7f77adStw21770 /*
1775b7f77adStw21770  * SMF Audit Events:
1785b7f77adStw21770  * ================
1795b7f77adStw21770  *
1805b7f77adStw21770  * To maintain security, SMF generates audit events whenever
1815b7f77adStw21770  * privileged operations are attempted.  See the System Administration
1825b7f77adStw21770  * Guide:Security Services answerbook for a discussion of the Solaris
1835b7f77adStw21770  * audit system.
1845b7f77adStw21770  *
1855b7f77adStw21770  * The SMF audit event codes are defined in adt_event.h by symbols
1865b7f77adStw21770  * starting with ADT_smf_ and are described in audit_event.txt.  The
1875b7f77adStw21770  * audit record structures are defined in the SMF section of adt.xml.
1885b7f77adStw21770  * adt.xml is used to automatically generate adt_event.h which
1895b7f77adStw21770  * contains the definitions that we code to in this file.  For the
1905b7f77adStw21770  * most part the audit events map closely to actions that you would
1915b7f77adStw21770  * perform with svcadm or svccfg, but there are some special cases
1925b7f77adStw21770  * which we'll discuss later.
1935b7f77adStw21770  *
1945b7f77adStw21770  * The software associated with SMF audit events falls into three
1955b7f77adStw21770  * categories:
1965b7f77adStw21770  * 	- collecting information to be written to the audit
1975b7f77adStw21770  *	  records
1985b7f77adStw21770  *	- using the adt_* functions in
1995b7f77adStw21770  *	  usr/src/lib/libbsm/common/adt.c to generate the audit
2005b7f77adStw21770  *	  records.
2015b7f77adStw21770  * 	- handling special cases
2025b7f77adStw21770  *
2035b7f77adStw21770  * Collecting Information:
2045b7f77adStw21770  * ----------------------
2055b7f77adStw21770  *
2065b7f77adStw21770  * Most all of the audit events require the FMRI of the affected
2075b7f77adStw21770  * object and the authorization string that was used.  The one
2085b7f77adStw21770  * exception is ADT_smf_annotation which we'll talk about later.
2095b7f77adStw21770  *
2105b7f77adStw21770  * Collecting the FMRI:
2115b7f77adStw21770  *
2125b7f77adStw21770  * The rc_node structure has a member called rn_fmri which points to
2135b7f77adStw21770  * its FMRI.  This is initialized by a call to rc_node_build_fmri()
2145b7f77adStw21770  * when the node's parent is established.  The reason for doing it
2155b7f77adStw21770  * at this time is that a node's FMRI is basically the concatenation
2165b7f77adStw21770  * of the parent's FMRI and the node's name with the appropriate
2175b7f77adStw21770  * decoration.  rc_node_build_fmri() does this concatenation and
2185b7f77adStw21770  * decorating.  It is called from rc_node_link_child() and
2195b7f77adStw21770  * rc_node_relink_child() where a node is linked to its parent.
2205b7f77adStw21770  *
2215b7f77adStw21770  * rc_node_get_fmri_or_fragment() is called to retrieve a node's FMRI
2225b7f77adStw21770  * when it is needed.  It returns rn_fmri if it is set.  If the node
2235b7f77adStw21770  * is at the top level, however, rn_fmri won't be set because it was
2245b7f77adStw21770  * never linked to a parent.  In this case,
2255b7f77adStw21770  * rc_node_get_fmri_or_fragment() constructs an FMRI fragment based on
2265b7f77adStw21770  * its node type and its name, rn_name.
2275b7f77adStw21770  *
2285b7f77adStw21770  * Collecting the Authorization String:
2295b7f77adStw21770  *
2305b7f77adStw21770  * Naturally, the authorization string is captured during the
2315b7f77adStw21770  * authorization checking process.  Acceptable authorization strings
2325b7f77adStw21770  * are added to a permcheck_t hash table as noted in the section on
2335b7f77adStw21770  * permission checking above.  Once all entries have been added to the
2345b7f77adStw21770  * hash table, perm_granted() is called.  If the client is authorized,
2355b7f77adStw21770  * perm_granted() returns with pc_auth_string of the permcheck_t
2365b7f77adStw21770  * structure pointing to the authorization string.
2375b7f77adStw21770  *
2385b7f77adStw21770  * This works fine if the client is authorized, but what happens if
2395b7f77adStw21770  * the client is not authorized?  We need to report the required
2405b7f77adStw21770  * authorization string.  This is the authorization that would have
2415b7f77adStw21770  * been used if permission had been granted.  perm_granted() will
2425b7f77adStw21770  * find no match, so it needs to decide which string in the hash
2435b7f77adStw21770  * table to use as the required authorization string.  It needs to do
2445b7f77adStw21770  * this, because configd is still going to generate an event.  A
2455b7f77adStw21770  * design decision was made to use the most specific authorization
2465b7f77adStw21770  * in the hash table.  The pc_auth_type enum designates the
2475b7f77adStw21770  * specificity of an authorization string.  For example, an
2485b7f77adStw21770  * authorization string that is declared in an instance PG is more
2495b7f77adStw21770  * specific than one that is declared in a service PG.
2505b7f77adStw21770  *
2515b7f77adStw21770  * The pc_add() function keeps track of the most specific
2525b7f77adStw21770  * authorization in the hash table.  It does this using the
2535b7f77adStw21770  * pc_specific and pc_specific_type members of the permcheck
2545b7f77adStw21770  * structure.  pc_add() updates these members whenever a more
2555b7f77adStw21770  * specific authorization string is added to the hash table.  Thus, if
2565b7f77adStw21770  * an authorization match is not found, perm_granted() will return
2575b7f77adStw21770  * with pc_auth_string in the permcheck_t pointing to the string that
2585b7f77adStw21770  * is referenced by pc_specific.
2595b7f77adStw21770  *
2605b7f77adStw21770  * Generating the Audit Events:
2615b7f77adStw21770  * ===========================
2625b7f77adStw21770  *
2635b7f77adStw21770  * As the functions in this file process requests for clients of
2645b7f77adStw21770  * configd, they gather the information that is required for an audit
2655b7f77adStw21770  * event.  Eventually, the request processing gets to the point where
2665b7f77adStw21770  * the authorization is rejected or to the point where the requested
2675b7f77adStw21770  * action was attempted.  At these two points smf_audit_event() is
2685b7f77adStw21770  * called.
2695b7f77adStw21770  *
2705b7f77adStw21770  * smf_audit_event() takes 4 parameters:
2715b7f77adStw21770  * 	- the event ID which is one of the ADT_smf_* symbols from
2725b7f77adStw21770  *	  adt_event.h.
2735b7f77adStw21770  * 	- status to pass to adt_put_event()
2745b7f77adStw21770  * 	- return value to pass to adt_put_event()
2755b7f77adStw21770  * 	- the event data (see audit_event_data structure)
2765b7f77adStw21770  *
2775b7f77adStw21770  * All interactions with the auditing software require an audit
2785b7f77adStw21770  * session.  We use one audit session per configd client.  We keep
2795b7f77adStw21770  * track of the audit session in the repcache_client structure.
2805b7f77adStw21770  * smf_audit_event() calls get_audit_session() to get the session
2815b7f77adStw21770  * pointer.
2825b7f77adStw21770  *
2835b7f77adStw21770  * smf_audit_event() then calls adt_alloc_event() to allocate an
2845b7f77adStw21770  * adt_event_data union which is defined in adt_event.h, copies the
2855b7f77adStw21770  * data into the appropriate members of the union and calls
2865b7f77adStw21770  * adt_put_event() to generate the event.
2875b7f77adStw21770  *
2885b7f77adStw21770  * Special Cases:
2895b7f77adStw21770  * =============
2905b7f77adStw21770  *
2915b7f77adStw21770  * There are three major types of special cases:
2925b7f77adStw21770  *
2935b7f77adStw21770  * 	- gathering event information for each action in a
2945b7f77adStw21770  *	  transaction
2955b7f77adStw21770  * 	- Higher level events represented by special property
2965b7f77adStw21770  *	  group/property name combinations.  Many of these are
2975b7f77adStw21770  *	  restarter actions.
2985b7f77adStw21770  * 	- ADT_smf_annotation event
2995b7f77adStw21770  *
3005b7f77adStw21770  * Processing Transaction Actions:
3015b7f77adStw21770  * ------------------------------
3025b7f77adStw21770  *
3035b7f77adStw21770  * A transaction can contain multiple actions to modify, create or
3045b7f77adStw21770  * delete one or more properties.  We need to capture information so
3055b7f77adStw21770  * that we can generate an event for each property action.  The
3065b7f77adStw21770  * transaction information is stored in a tx_commmit_data_t, and
3075b7f77adStw21770  * object.c provides accessor functions to retrieve data from this
3085b7f77adStw21770  * structure.  rc_tx_commit() obtains a tx_commit_data_t by calling
3095b7f77adStw21770  * tx_commit_data_new() and passes this to object_tx_commit() to
3105b7f77adStw21770  * commit the transaction.  Then we call generate_property_events() to
3115b7f77adStw21770  * generate an audit event for each property action.
3125b7f77adStw21770  *
3135b7f77adStw21770  * Special Properties:
3145b7f77adStw21770  * ------------------
3155b7f77adStw21770  *
3165b7f77adStw21770  * There are combinations of property group/property name that are special.
3175b7f77adStw21770  * They are special because they have specific meaning to startd.  startd
3185b7f77adStw21770  * interprets them in a service-independent fashion.
3195b7f77adStw21770  * restarter_actions/refresh and general/enabled are two examples of these.
3205b7f77adStw21770  * A special event is generated for these properties in addition to the
3215b7f77adStw21770  * regular property event described in the previous section.  The special
3225b7f77adStw21770  * properties are declared as an array of audit_special_prop_item
3235b7f77adStw21770  * structures at special_props_list in rc_node.c.
3245b7f77adStw21770  *
3255b7f77adStw21770  * In the previous section, we mentioned the
3265b7f77adStw21770  * generate_property_event() function that generates an event for
3275b7f77adStw21770  * every property action.  Before generating the event,
3285b7f77adStw21770  * generate_property_event() calls special_property_event().
3295b7f77adStw21770  * special_property_event() checks to see if the action involves a
3305b7f77adStw21770  * special property.  If it does, it generates a special audit
3315b7f77adStw21770  * event.
3325b7f77adStw21770  *
3335b7f77adStw21770  * ADT_smf_annotation event:
3345b7f77adStw21770  * ------------------------
3355b7f77adStw21770  *
3365b7f77adStw21770  * This is a special event unlike any other.  It allows the svccfg
3375b7f77adStw21770  * program to store an annotation in the event log before a series
3385b7f77adStw21770  * of transactions is processed.  It is used with the import and
3395b7f77adStw21770  * apply svccfg commands.  svccfg uses the rep_protocol_annotation
3405b7f77adStw21770  * message to pass the operation (import or apply) and the file name
3415b7f77adStw21770  * to configd.  The set_annotation() function in client.c stores
3425b7f77adStw21770  * these away in the a repcache_client structure.  The address of
3435b7f77adStw21770  * this structure is saved in the thread_info structure.
3445b7f77adStw21770  *
3455b7f77adStw21770  * Before it generates any events, smf_audit_event() calls
3465b7f77adStw21770  * smf_annotation_event().  smf_annotation_event() calls
3475b7f77adStw21770  * client_annotation_needed() which is defined in client.c.  If an
3485b7f77adStw21770  * annotation is needed client_annotation_needed() returns the
3495b7f77adStw21770  * operation and filename strings that were saved from the
3505b7f77adStw21770  * rep_protocol_annotation message.  smf_annotation_event() then
3515b7f77adStw21770  * generates the ADT_smf_annotation event.
3525b7f77adStw21770  */
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate #include <assert.h>
3557c478bd9Sstevel@tonic-gate #include <atomic.h>
3565b7f77adStw21770 #include <bsm/adt_event.h>
3577c478bd9Sstevel@tonic-gate #include <errno.h>
3587c478bd9Sstevel@tonic-gate #include <libuutil.h>
3597c478bd9Sstevel@tonic-gate #include <libscf.h>
3607c478bd9Sstevel@tonic-gate #include <libscf_priv.h>
3617c478bd9Sstevel@tonic-gate #include <pthread.h>
362499fd601Sgww #include <pwd.h>
3637c478bd9Sstevel@tonic-gate #include <stdio.h>
3647c478bd9Sstevel@tonic-gate #include <stdlib.h>
3657c478bd9Sstevel@tonic-gate #include <strings.h>
3667c478bd9Sstevel@tonic-gate #include <sys/types.h>
3675b7f77adStw21770 #include <syslog.h>
3687c478bd9Sstevel@tonic-gate #include <unistd.h>
369134a1f4eSCasper H.S. Dik #include <secdb.h>
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate #include "configd.h"
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate #define	AUTH_PREFIX		"solaris.smf."
3747c478bd9Sstevel@tonic-gate #define	AUTH_MANAGE		AUTH_PREFIX "manage"
3757c478bd9Sstevel@tonic-gate #define	AUTH_MODIFY		AUTH_PREFIX "modify"
3767c478bd9Sstevel@tonic-gate #define	AUTH_MODIFY_PREFIX	AUTH_MODIFY "."
3777c478bd9Sstevel@tonic-gate #define	AUTH_PG_ACTIONS		SCF_PG_RESTARTER_ACTIONS
3787c478bd9Sstevel@tonic-gate #define	AUTH_PG_ACTIONS_TYPE	SCF_PG_RESTARTER_ACTIONS_TYPE
3797c478bd9Sstevel@tonic-gate #define	AUTH_PG_GENERAL		SCF_PG_GENERAL
3807c478bd9Sstevel@tonic-gate #define	AUTH_PG_GENERAL_TYPE	SCF_PG_GENERAL_TYPE
3817c478bd9Sstevel@tonic-gate #define	AUTH_PG_GENERAL_OVR	SCF_PG_GENERAL_OVR
3827c478bd9Sstevel@tonic-gate #define	AUTH_PG_GENERAL_OVR_TYPE  SCF_PG_GENERAL_OVR_TYPE
3837c478bd9Sstevel@tonic-gate #define	AUTH_PROP_ACTION	"action_authorization"
3847c478bd9Sstevel@tonic-gate #define	AUTH_PROP_ENABLED	"enabled"
3857c478bd9Sstevel@tonic-gate #define	AUTH_PROP_MODIFY	"modify_authorization"
3867c478bd9Sstevel@tonic-gate #define	AUTH_PROP_VALUE		"value_authorization"
3873eae19d9Swesolows #define	AUTH_PROP_READ		"read_authorization"
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate #define	MAX_VALID_CHILDREN 3
3907c478bd9Sstevel@tonic-gate 
3915b7f77adStw21770 /*
3925b7f77adStw21770  * The ADT_smf_* symbols may not be defined on the build machine.  Because
3935b7f77adStw21770  * of this, we do not want to compile the _smf_aud_event() function when
3945b7f77adStw21770  * doing native builds.
3955b7f77adStw21770  */
3965b7f77adStw21770 #ifdef	NATIVE_BUILD
3975b7f77adStw21770 #define	smf_audit_event(i, s, r, d)
3985b7f77adStw21770 #else
3995b7f77adStw21770 #define	smf_audit_event(i, s, r, d)	_smf_audit_event(i, s, r, d)
4005b7f77adStw21770 #endif	/* NATIVE_BUILD */
4015b7f77adStw21770 
4027c478bd9Sstevel@tonic-gate typedef struct rc_type_info {
4037c478bd9Sstevel@tonic-gate 	uint32_t	rt_type;		/* matches array index */
4047c478bd9Sstevel@tonic-gate 	uint32_t	rt_num_ids;
4057c478bd9Sstevel@tonic-gate 	uint32_t	rt_name_flags;
4067c478bd9Sstevel@tonic-gate 	uint32_t	rt_valid_children[MAX_VALID_CHILDREN];
4077c478bd9Sstevel@tonic-gate } rc_type_info_t;
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate #define	RT_NO_NAME	-1U
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate static rc_type_info_t rc_types[] = {
4127c478bd9Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_NONE, 0, RT_NO_NAME},
4137c478bd9Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_SCOPE, 0, 0,
4147c478bd9Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_SERVICE, REP_PROTOCOL_ENTITY_SCOPE}},
4157c478bd9Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_SERVICE, 0, UU_NAME_DOMAIN | UU_NAME_PATH,
4167c478bd9Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_INSTANCE, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
4177c478bd9Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_INSTANCE, 1, UU_NAME_DOMAIN,
4187c478bd9Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_SNAPSHOT, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
4197c478bd9Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_SNAPSHOT, 2, UU_NAME_DOMAIN,
4207c478bd9Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_SNAPLEVEL, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
4217c478bd9Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_SNAPLEVEL, 4, RT_NO_NAME,
4227c478bd9Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_PROPERTYGRP}},
4237c478bd9Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_PROPERTYGRP, 5, UU_NAME_DOMAIN,
4247c478bd9Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_PROPERTY}},
4257c478bd9Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_CPROPERTYGRP, 0, UU_NAME_DOMAIN,
4267c478bd9Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_PROPERTY}},
4277c478bd9Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_PROPERTY, 7, UU_NAME_DOMAIN},
4287c478bd9Sstevel@tonic-gate 	{-1UL}
4297c478bd9Sstevel@tonic-gate };
4307c478bd9Sstevel@tonic-gate #define	NUM_TYPES	((sizeof (rc_types) / sizeof (*rc_types)))
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate /* Element of a permcheck_t hash table. */
4337c478bd9Sstevel@tonic-gate struct pc_elt {
4347c478bd9Sstevel@tonic-gate 	struct pc_elt	*pce_next;
4357c478bd9Sstevel@tonic-gate 	char		pce_auth[1];
4367c478bd9Sstevel@tonic-gate };
4377c478bd9Sstevel@tonic-gate 
4385b7f77adStw21770 /*
4395b7f77adStw21770  * If an authorization fails, we must decide which of the elements in the
4405b7f77adStw21770  * permcheck hash table to use in the audit event.  That is to say of all
4415b7f77adStw21770  * the strings in the hash table, we must choose one and use it in the audit
4425b7f77adStw21770  * event.  It is desirable to use the most specific string in the audit
4435b7f77adStw21770  * event.
4445b7f77adStw21770  *
4455b7f77adStw21770  * The pc_auth_type specifies the types (sources) of authorization
4465b7f77adStw21770  * strings.  The enum is ordered in increasing specificity.
4475b7f77adStw21770  */
4485b7f77adStw21770 typedef enum pc_auth_type {
4495b7f77adStw21770 	PC_AUTH_NONE = 0,	/* no auth string available. */
4505b7f77adStw21770 	PC_AUTH_SMF,		/* strings coded into SMF. */
4515b7f77adStw21770 	PC_AUTH_SVC,		/* strings specified in PG of a service. */
4525b7f77adStw21770 	PC_AUTH_INST		/* strings specified in PG of an instance. */
4535b7f77adStw21770 } pc_auth_type_t;
4545b7f77adStw21770 
455a4dc1477STom Whitten /*
456a4dc1477STom Whitten  * The following enum is used to represent the results of the checks to see
457a4dc1477STom Whitten  * if the client has the appropriate permissions to perform an action.
458a4dc1477STom Whitten  */
459a4dc1477STom Whitten typedef enum perm_status {
460a4dc1477STom Whitten 	PERM_DENIED = 0,	/* Permission denied. */
461a4dc1477STom Whitten 	PERM_GRANTED,		/* Client has authorizations. */
462a4dc1477STom Whitten 	PERM_GONE,		/* Door client went away. */
463a4dc1477STom Whitten 	PERM_FAIL		/* Generic failure. e.g. resources */
464a4dc1477STom Whitten } perm_status_t;
465a4dc1477STom Whitten 
4667c478bd9Sstevel@tonic-gate /* An authorization set hash table. */
4677c478bd9Sstevel@tonic-gate typedef struct {
4687c478bd9Sstevel@tonic-gate 	struct pc_elt	**pc_buckets;
4697c478bd9Sstevel@tonic-gate 	uint_t		pc_bnum;		/* number of buckets */
4707c478bd9Sstevel@tonic-gate 	uint_t		pc_enum;		/* number of elements */
4715b7f77adStw21770 	struct pc_elt	*pc_specific;		/* most specific element */
4725b7f77adStw21770 	pc_auth_type_t	pc_specific_type;	/* type of pc_specific */
4735b7f77adStw21770 	char		*pc_auth_string;	/* authorization string */
4745b7f77adStw21770 						/* for audit events */
4757c478bd9Sstevel@tonic-gate } permcheck_t;
4767c478bd9Sstevel@tonic-gate 
4775b7f77adStw21770 /*
4785b7f77adStw21770  * Structure for holding audit event data.  Not all events use all members
4795b7f77adStw21770  * of the structure.
4805b7f77adStw21770  */
4815b7f77adStw21770 typedef struct audit_event_data {
4825b7f77adStw21770 	char		*ed_auth;	/* authorization string. */
4835b7f77adStw21770 	char		*ed_fmri;	/* affected FMRI. */
4845b7f77adStw21770 	char		*ed_snapname;	/* name of snapshot. */
4855b7f77adStw21770 	char		*ed_old_fmri;	/* old fmri in attach case. */
4865b7f77adStw21770 	char		*ed_old_name;	/* old snapshot in attach case. */
4875b7f77adStw21770 	char		*ed_type;	/* prop. group or prop. type. */
4885b7f77adStw21770 	char		*ed_prop_value;	/* property value. */
4895b7f77adStw21770 } audit_event_data_t;
4905b7f77adStw21770 
4915b7f77adStw21770 /*
4925b7f77adStw21770  * Pointer to function to do special processing to get audit event ID.
4935b7f77adStw21770  * Audit event IDs are defined in /usr/include/bsm/adt_event.h.  Function
4945b7f77adStw21770  * returns 0 if ID successfully retrieved.  Otherwise it returns -1.
4955b7f77adStw21770  */
4965b7f77adStw21770 typedef int (*spc_getid_fn_t)(tx_commit_data_t *, size_t, const char *,
4975b7f77adStw21770     au_event_t *);
4985b7f77adStw21770 static int general_enable_id(tx_commit_data_t *, size_t, const char *,
4995b7f77adStw21770     au_event_t *);
5005b7f77adStw21770 
5017c478bd9Sstevel@tonic-gate static uu_list_pool_t *rc_children_pool;
5027c478bd9Sstevel@tonic-gate static uu_list_pool_t *rc_pg_notify_pool;
5037c478bd9Sstevel@tonic-gate static uu_list_pool_t *rc_notify_pool;
5047c478bd9Sstevel@tonic-gate static uu_list_pool_t *rc_notify_info_pool;
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate static rc_node_t *rc_scope;
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate static pthread_mutex_t	rc_pg_notify_lock = PTHREAD_MUTEX_INITIALIZER;
5097c478bd9Sstevel@tonic-gate static pthread_cond_t	rc_pg_notify_cv = PTHREAD_COND_INITIALIZER;
5107c478bd9Sstevel@tonic-gate static uint_t		rc_notify_in_use;	/* blocks removals */
5117c478bd9Sstevel@tonic-gate 
5125b7f77adStw21770 /*
5135b7f77adStw21770  * Some combinations of property group/property name require a special
5145b7f77adStw21770  * audit event to be generated when there is a change.
5155b7f77adStw21770  * audit_special_prop_item_t is used to specify these special cases.  The
5165b7f77adStw21770  * special_props_list array defines a list of these special properties.
5175b7f77adStw21770  */
5185b7f77adStw21770 typedef struct audit_special_prop_item {
5195b7f77adStw21770 	const char	*api_pg_name;	/* property group name. */
5205b7f77adStw21770 	const char	*api_prop_name;	/* property name. */
5215b7f77adStw21770 	au_event_t	api_event_id;	/* event id or 0. */
5225b7f77adStw21770 	spc_getid_fn_t	api_event_func; /* function to get event id. */
5235b7f77adStw21770 } audit_special_prop_item_t;
5245b7f77adStw21770 
5255b7f77adStw21770 /*
5265b7f77adStw21770  * Native builds are done using the build machine's standard include
5275b7f77adStw21770  * files.  These files may not yet have the definitions for the ADT_smf_*
5285b7f77adStw21770  * symbols.  Thus, we do not compile this table when doing native builds.
5295b7f77adStw21770  */
5305b7f77adStw21770 #ifndef	NATIVE_BUILD
5315b7f77adStw21770 /*
5325b7f77adStw21770  * The following special_props_list array specifies property group/property
5335b7f77adStw21770  * name combinations that have specific meaning to startd.  A special event
5345b7f77adStw21770  * is generated for these combinations in addition to the regular property
5355b7f77adStw21770  * event.
5365b7f77adStw21770  *
5375b7f77adStw21770  * At run time this array gets sorted.  See the call to qsort(3C) in
5385b7f77adStw21770  * rc_node_init().  The array is sorted, so that bsearch(3C) can be used
5395b7f77adStw21770  * to do lookups.
5405b7f77adStw21770  */
5415b7f77adStw21770 static audit_special_prop_item_t special_props_list[] = {
5425b7f77adStw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_DEGRADED, ADT_smf_degrade,
5435b7f77adStw21770 	    NULL},
5445b7f77adStw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_DEGRADE_IMMEDIATE,
5455b7f77adStw21770 	    ADT_smf_immediate_degrade, NULL},
5465b7f77adStw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_OFF, ADT_smf_clear, NULL},
5475b7f77adStw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON,
5485b7f77adStw21770 	    ADT_smf_maintenance, NULL},
5495b7f77adStw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_IMMEDIATE,
5505b7f77adStw21770 	    ADT_smf_immediate_maintenance, NULL},
5515b7f77adStw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_IMMTEMP,
5525b7f77adStw21770 	    ADT_smf_immtmp_maintenance, NULL},
5535b7f77adStw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_TEMPORARY,
5545b7f77adStw21770 	    ADT_smf_tmp_maintenance, NULL},
5555b7f77adStw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_REFRESH, ADT_smf_refresh, NULL},
5565b7f77adStw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_RESTART, ADT_smf_restart, NULL},
5575b7f77adStw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_RESTORE, ADT_smf_clear, NULL},
5585b7f77adStw21770 	{SCF_PG_OPTIONS, SCF_PROPERTY_MILESTONE, ADT_smf_milestone, NULL},
5595b7f77adStw21770 	{SCF_PG_OPTIONS_OVR, SCF_PROPERTY_MILESTONE, ADT_smf_milestone, NULL},
5605b7f77adStw21770 	{SCF_PG_GENERAL, SCF_PROPERTY_ENABLED, 0, general_enable_id},
5615b7f77adStw21770 	{SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED, 0, general_enable_id}
5625b7f77adStw21770 };
5635b7f77adStw21770 #define	SPECIAL_PROP_COUNT	(sizeof (special_props_list) /\
5645b7f77adStw21770 	sizeof (audit_special_prop_item_t))
5655b7f77adStw21770 #endif	/* NATIVE_BUILD */
5665b7f77adStw21770 
5677c478bd9Sstevel@tonic-gate /*
5687c478bd9Sstevel@tonic-gate  * We support an arbitrary number of clients interested in events for certain
5697c478bd9Sstevel@tonic-gate  * types of changes.  Each client is represented by an rc_notify_info_t, and
5707c478bd9Sstevel@tonic-gate  * all clients are chained onto the rc_notify_info_list.
5717c478bd9Sstevel@tonic-gate  *
5727c478bd9Sstevel@tonic-gate  * The rc_notify_list is the global notification list.  Each entry is of
5737c478bd9Sstevel@tonic-gate  * type rc_notify_t, which is embedded in one of three other structures:
5747c478bd9Sstevel@tonic-gate  *
5757c478bd9Sstevel@tonic-gate  *	rc_node_t		property group update notification
5767c478bd9Sstevel@tonic-gate  *	rc_notify_delete_t	object deletion notification
5777c478bd9Sstevel@tonic-gate  *	rc_notify_info_t	notification clients
5787c478bd9Sstevel@tonic-gate  *
5797c478bd9Sstevel@tonic-gate  * Which type of object is determined by which pointer in the rc_notify_t is
5807c478bd9Sstevel@tonic-gate  * non-NULL.
5817c478bd9Sstevel@tonic-gate  *
5827c478bd9Sstevel@tonic-gate  * New notifications and clients are added to the end of the list.
5837c478bd9Sstevel@tonic-gate  * Notifications no-one is interested in are never added to the list.
5847c478bd9Sstevel@tonic-gate  *
5857c478bd9Sstevel@tonic-gate  * Clients use their position in the list to track which notifications they
5867c478bd9Sstevel@tonic-gate  * have not yet reported.  As they process notifications, they move forward
5877c478bd9Sstevel@tonic-gate  * in the list past them.  There is always a client at the beginning of the
5887c478bd9Sstevel@tonic-gate  * list -- as he moves past notifications, he removes them from the list and
5897c478bd9Sstevel@tonic-gate  * cleans them up.
5907c478bd9Sstevel@tonic-gate  *
5917c478bd9Sstevel@tonic-gate  * The rc_pg_notify_lock protects all notification state.  The rc_pg_notify_cv
5927c478bd9Sstevel@tonic-gate  * is used for global signalling, and each client has a cv which he waits for
5937c478bd9Sstevel@tonic-gate  * events of interest on.
594b5cbdab0STom Whitten  *
595b5cbdab0STom Whitten  * rc_notify_in_use is used to protect rc_notify_list from deletions when
596b5cbdab0STom Whitten  * the rc_pg_notify_lock is dropped.  Specifically, rc_notify_info_wait()
597b5cbdab0STom Whitten  * must drop the lock to call rc_node_assign(), and then it reacquires the
598b5cbdab0STom Whitten  * lock.  Deletions from rc_notify_list during this period are not
599b5cbdab0STom Whitten  * allowed.  Insertions do not matter, because they are always done at the
600b5cbdab0STom Whitten  * end of the list.
6017c478bd9Sstevel@tonic-gate  */
6027c478bd9Sstevel@tonic-gate static uu_list_t	*rc_notify_info_list;
6037c478bd9Sstevel@tonic-gate static uu_list_t	*rc_notify_list;
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate #define	HASH_SIZE	512
6067c478bd9Sstevel@tonic-gate #define	HASH_MASK	(HASH_SIZE - 1)
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate #pragma align 64(cache_hash)
6097c478bd9Sstevel@tonic-gate static cache_bucket_t cache_hash[HASH_SIZE];
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate #define	CACHE_BUCKET(h)		(&cache_hash[(h) & HASH_MASK])
6127c478bd9Sstevel@tonic-gate 
6136643e1ffSbustos 
6146643e1ffSbustos static void rc_node_no_client_refs(rc_node_t *np);
6156643e1ffSbustos 
6166643e1ffSbustos 
6177c478bd9Sstevel@tonic-gate static uint32_t
6187c478bd9Sstevel@tonic-gate rc_node_hash(rc_node_lookup_t *lp)
6197c478bd9Sstevel@tonic-gate {
6207c478bd9Sstevel@tonic-gate 	uint32_t type = lp->rl_type;
6217c478bd9Sstevel@tonic-gate 	uint32_t backend = lp->rl_backend;
6220b5c9250Shg115875 	uint32_t mainid = lp->rl_main_id;
6237c478bd9Sstevel@tonic-gate 	uint32_t *ids = lp->rl_ids;
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate 	rc_type_info_t *tp = &rc_types[type];
6267c478bd9Sstevel@tonic-gate 	uint32_t num_ids;
6277c478bd9Sstevel@tonic-gate 	uint32_t left;
6287c478bd9Sstevel@tonic-gate 	uint32_t hash;
6297c478bd9Sstevel@tonic-gate 
6307c478bd9Sstevel@tonic-gate 	assert(backend == BACKEND_TYPE_NORMAL ||
6317c478bd9Sstevel@tonic-gate 	    backend == BACKEND_TYPE_NONPERSIST);
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 	assert(type > 0 && type < NUM_TYPES);
6347c478bd9Sstevel@tonic-gate 	num_ids = tp->rt_num_ids;
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 	left = MAX_IDS - num_ids;
6377c478bd9Sstevel@tonic-gate 	assert(num_ids <= MAX_IDS);
6387c478bd9Sstevel@tonic-gate 
6390b5c9250Shg115875 	hash = type * 7 + mainid * 5 + backend;
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 	while (num_ids-- > 0)
6427c478bd9Sstevel@tonic-gate 		hash = hash * 11 + *ids++ * 7;
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate 	/*
6457c478bd9Sstevel@tonic-gate 	 * the rest should be zeroed
6467c478bd9Sstevel@tonic-gate 	 */
6477c478bd9Sstevel@tonic-gate 	while (left-- > 0)
6487c478bd9Sstevel@tonic-gate 		assert(*ids++ == 0);
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate 	return (hash);
6517c478bd9Sstevel@tonic-gate }
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate static int
6547c478bd9Sstevel@tonic-gate rc_node_match(rc_node_t *np, rc_node_lookup_t *l)
6557c478bd9Sstevel@tonic-gate {
6567c478bd9Sstevel@tonic-gate 	rc_node_lookup_t *r = &np->rn_id;
6577c478bd9Sstevel@tonic-gate 	rc_type_info_t *tp;
6587c478bd9Sstevel@tonic-gate 	uint32_t type;
6597c478bd9Sstevel@tonic-gate 	uint32_t num_ids;
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 	if (r->rl_main_id != l->rl_main_id)
6627c478bd9Sstevel@tonic-gate 		return (0);
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 	type = r->rl_type;
6657c478bd9Sstevel@tonic-gate 	if (type != l->rl_type)
6667c478bd9Sstevel@tonic-gate 		return (0);
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 	assert(type > 0 && type < NUM_TYPES);
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 	tp = &rc_types[r->rl_type];
6717c478bd9Sstevel@tonic-gate 	num_ids = tp->rt_num_ids;
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate 	assert(num_ids <= MAX_IDS);
6747c478bd9Sstevel@tonic-gate 	while (num_ids-- > 0)
6757c478bd9Sstevel@tonic-gate 		if (r->rl_ids[num_ids] != l->rl_ids[num_ids])
6767c478bd9Sstevel@tonic-gate 			return (0);
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	return (1);
6797c478bd9Sstevel@tonic-gate }
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate /*
6826643e1ffSbustos  * Register an ephemeral reference to np.  This should be done while both
6836643e1ffSbustos  * the persistent reference from which the np pointer was read is locked
6846643e1ffSbustos  * and np itself is locked.  This guarantees that another thread which
6856643e1ffSbustos  * thinks it has the last reference will yield without destroying the
6866643e1ffSbustos  * node.
6876643e1ffSbustos  */
6886643e1ffSbustos static void
6896643e1ffSbustos rc_node_hold_ephemeral_locked(rc_node_t *np)
6906643e1ffSbustos {
6916643e1ffSbustos 	assert(MUTEX_HELD(&np->rn_lock));
6926643e1ffSbustos 
6936643e1ffSbustos 	++np->rn_erefs;
6946643e1ffSbustos }
6956643e1ffSbustos 
6966643e1ffSbustos /*
6977c478bd9Sstevel@tonic-gate  * the "other" references on a node are maintained in an atomically
6987c478bd9Sstevel@tonic-gate  * updated refcount, rn_other_refs.  This can be bumped from arbitrary
6997c478bd9Sstevel@tonic-gate  * context, and tracks references to a possibly out-of-date node's children.
7007c478bd9Sstevel@tonic-gate  *
7017c478bd9Sstevel@tonic-gate  * To prevent the node from disappearing between the final drop of
7027c478bd9Sstevel@tonic-gate  * rn_other_refs and the unref handling, rn_other_refs_held is bumped on
7037c478bd9Sstevel@tonic-gate  * 0->1 transitions and decremented (with the node lock held) on 1->0
7047c478bd9Sstevel@tonic-gate  * transitions.
7057c478bd9Sstevel@tonic-gate  */
7067c478bd9Sstevel@tonic-gate static void
7077c478bd9Sstevel@tonic-gate rc_node_hold_other(rc_node_t *np)
7087c478bd9Sstevel@tonic-gate {
7097c478bd9Sstevel@tonic-gate 	if (atomic_add_32_nv(&np->rn_other_refs, 1) == 1) {
7107c478bd9Sstevel@tonic-gate 		atomic_add_32(&np->rn_other_refs_held, 1);
7117c478bd9Sstevel@tonic-gate 		assert(np->rn_other_refs_held > 0);
7127c478bd9Sstevel@tonic-gate 	}
7137c478bd9Sstevel@tonic-gate 	assert(np->rn_other_refs > 0);
7147c478bd9Sstevel@tonic-gate }
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate /*
7177c478bd9Sstevel@tonic-gate  * No node locks may be held
7187c478bd9Sstevel@tonic-gate  */
7197c478bd9Sstevel@tonic-gate static void
7207c478bd9Sstevel@tonic-gate rc_node_rele_other(rc_node_t *np)
7217c478bd9Sstevel@tonic-gate {
7227c478bd9Sstevel@tonic-gate 	assert(np->rn_other_refs > 0);
7237c478bd9Sstevel@tonic-gate 	if (atomic_add_32_nv(&np->rn_other_refs, -1) == 0) {
7247c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
7257c478bd9Sstevel@tonic-gate 		assert(np->rn_other_refs_held > 0);
7267c478bd9Sstevel@tonic-gate 		if (atomic_add_32_nv(&np->rn_other_refs_held, -1) == 0 &&
7276643e1ffSbustos 		    np->rn_refs == 0 && (np->rn_flags & RC_NODE_OLD)) {
7286643e1ffSbustos 			/*
7296643e1ffSbustos 			 * This was the last client reference.  Destroy
7306643e1ffSbustos 			 * any other references and free() the node.
7316643e1ffSbustos 			 */
7326643e1ffSbustos 			rc_node_no_client_refs(np);
7336643e1ffSbustos 		} else {
7347c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
7357c478bd9Sstevel@tonic-gate 		}
7367c478bd9Sstevel@tonic-gate 	}
7376643e1ffSbustos }
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate static void
7407c478bd9Sstevel@tonic-gate rc_node_hold_locked(rc_node_t *np)
7417c478bd9Sstevel@tonic-gate {
7427c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 	if (np->rn_refs == 0 && (np->rn_flags & RC_NODE_PARENT_REF))
7457c478bd9Sstevel@tonic-gate 		rc_node_hold_other(np->rn_parent_ref);
7467c478bd9Sstevel@tonic-gate 	np->rn_refs++;
7477c478bd9Sstevel@tonic-gate 	assert(np->rn_refs > 0);
7487c478bd9Sstevel@tonic-gate }
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate static void
7517c478bd9Sstevel@tonic-gate rc_node_hold(rc_node_t *np)
7527c478bd9Sstevel@tonic-gate {
7537c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
7547c478bd9Sstevel@tonic-gate 	rc_node_hold_locked(np);
7557c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
7567c478bd9Sstevel@tonic-gate }
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate static void
7597c478bd9Sstevel@tonic-gate rc_node_rele_locked(rc_node_t *np)
7607c478bd9Sstevel@tonic-gate {
7617c478bd9Sstevel@tonic-gate 	int unref = 0;
7627c478bd9Sstevel@tonic-gate 	rc_node_t *par_ref = NULL;
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
7657c478bd9Sstevel@tonic-gate 	assert(np->rn_refs > 0);
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 	if (--np->rn_refs == 0) {
7687c478bd9Sstevel@tonic-gate 		if (np->rn_flags & RC_NODE_PARENT_REF)
7697c478bd9Sstevel@tonic-gate 			par_ref = np->rn_parent_ref;
7707c478bd9Sstevel@tonic-gate 
771fda74dabSbustos 		/*
772fda74dabSbustos 		 * Composed property groups are only as good as their
773fda74dabSbustos 		 * references.
774fda74dabSbustos 		 */
775fda74dabSbustos 		if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP)
776fda74dabSbustos 			np->rn_flags |= RC_NODE_DEAD;
777fda74dabSbustos 
7787c478bd9Sstevel@tonic-gate 		if ((np->rn_flags & (RC_NODE_DEAD|RC_NODE_OLD)) &&
7797c478bd9Sstevel@tonic-gate 		    np->rn_other_refs == 0 && np->rn_other_refs_held == 0)
7807c478bd9Sstevel@tonic-gate 			unref = 1;
7817c478bd9Sstevel@tonic-gate 	}
7827c478bd9Sstevel@tonic-gate 
7836643e1ffSbustos 	if (unref) {
7846643e1ffSbustos 		/*
7856643e1ffSbustos 		 * This was the last client reference.  Destroy any other
7866643e1ffSbustos 		 * references and free() the node.
7876643e1ffSbustos 		 */
7886643e1ffSbustos 		rc_node_no_client_refs(np);
7896643e1ffSbustos 	} else {
7906643e1ffSbustos 		/*
7916643e1ffSbustos 		 * rn_erefs can be 0 if we acquired the reference in
7926643e1ffSbustos 		 * a path which hasn't been updated to increment rn_erefs.
7936643e1ffSbustos 		 * When all paths which end here are updated, we should
7946643e1ffSbustos 		 * assert rn_erefs > 0 and always decrement it.
7956643e1ffSbustos 		 */
7966643e1ffSbustos 		if (np->rn_erefs > 0)
7976643e1ffSbustos 			--np->rn_erefs;
7987c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
7996643e1ffSbustos 	}
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 	if (par_ref != NULL)
8027c478bd9Sstevel@tonic-gate 		rc_node_rele_other(par_ref);
8037c478bd9Sstevel@tonic-gate }
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate void
8067c478bd9Sstevel@tonic-gate rc_node_rele(rc_node_t *np)
8077c478bd9Sstevel@tonic-gate {
8087c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
8097c478bd9Sstevel@tonic-gate 	rc_node_rele_locked(np);
8107c478bd9Sstevel@tonic-gate }
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate static cache_bucket_t *
8137c478bd9Sstevel@tonic-gate cache_hold(uint32_t h)
8147c478bd9Sstevel@tonic-gate {
8157c478bd9Sstevel@tonic-gate 	cache_bucket_t *bp = CACHE_BUCKET(h);
8167c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&bp->cb_lock);
8177c478bd9Sstevel@tonic-gate 	return (bp);
8187c478bd9Sstevel@tonic-gate }
8197c478bd9Sstevel@tonic-gate 
8207c478bd9Sstevel@tonic-gate static void
8217c478bd9Sstevel@tonic-gate cache_release(cache_bucket_t *bp)
8227c478bd9Sstevel@tonic-gate {
8237c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&bp->cb_lock);
8247c478bd9Sstevel@tonic-gate }
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate static rc_node_t *
8277c478bd9Sstevel@tonic-gate cache_lookup_unlocked(cache_bucket_t *bp, rc_node_lookup_t *lp)
8287c478bd9Sstevel@tonic-gate {
8297c478bd9Sstevel@tonic-gate 	uint32_t h = rc_node_hash(lp);
8307c478bd9Sstevel@tonic-gate 	rc_node_t *np;
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&bp->cb_lock));
8337c478bd9Sstevel@tonic-gate 	assert(bp == CACHE_BUCKET(h));
8347c478bd9Sstevel@tonic-gate 
8357c478bd9Sstevel@tonic-gate 	for (np = bp->cb_head; np != NULL; np = np->rn_hash_next) {
8367c478bd9Sstevel@tonic-gate 		if (np->rn_hash == h && rc_node_match(np, lp)) {
8377c478bd9Sstevel@tonic-gate 			rc_node_hold(np);
8387c478bd9Sstevel@tonic-gate 			return (np);
8397c478bd9Sstevel@tonic-gate 		}
8407c478bd9Sstevel@tonic-gate 	}
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate 	return (NULL);
8437c478bd9Sstevel@tonic-gate }
8447c478bd9Sstevel@tonic-gate 
8457c478bd9Sstevel@tonic-gate static rc_node_t *
8467c478bd9Sstevel@tonic-gate cache_lookup(rc_node_lookup_t *lp)
8477c478bd9Sstevel@tonic-gate {
8487c478bd9Sstevel@tonic-gate 	uint32_t h;
8497c478bd9Sstevel@tonic-gate 	cache_bucket_t *bp;
8507c478bd9Sstevel@tonic-gate 	rc_node_t *np;
8517c478bd9Sstevel@tonic-gate 
8527c478bd9Sstevel@tonic-gate 	h = rc_node_hash(lp);
8537c478bd9Sstevel@tonic-gate 	bp = cache_hold(h);
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	np = cache_lookup_unlocked(bp, lp);
8567c478bd9Sstevel@tonic-gate 
8577c478bd9Sstevel@tonic-gate 	cache_release(bp);
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 	return (np);
8607c478bd9Sstevel@tonic-gate }
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate static void
8637c478bd9Sstevel@tonic-gate cache_insert_unlocked(cache_bucket_t *bp, rc_node_t *np)
8647c478bd9Sstevel@tonic-gate {
8657c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&bp->cb_lock));
8667c478bd9Sstevel@tonic-gate 	assert(np->rn_hash == rc_node_hash(&np->rn_id));
8677c478bd9Sstevel@tonic-gate 	assert(bp == CACHE_BUCKET(np->rn_hash));
8687c478bd9Sstevel@tonic-gate 
8697c478bd9Sstevel@tonic-gate 	assert(np->rn_hash_next == NULL);
8707c478bd9Sstevel@tonic-gate 
8717c478bd9Sstevel@tonic-gate 	np->rn_hash_next = bp->cb_head;
8727c478bd9Sstevel@tonic-gate 	bp->cb_head = np;
8737c478bd9Sstevel@tonic-gate }
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate static void
8767c478bd9Sstevel@tonic-gate cache_remove_unlocked(cache_bucket_t *bp, rc_node_t *np)
8777c478bd9Sstevel@tonic-gate {
8787c478bd9Sstevel@tonic-gate 	rc_node_t **npp;
8797c478bd9Sstevel@tonic-gate 
8807c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&bp->cb_lock));
8817c478bd9Sstevel@tonic-gate 	assert(np->rn_hash == rc_node_hash(&np->rn_id));
8827c478bd9Sstevel@tonic-gate 	assert(bp == CACHE_BUCKET(np->rn_hash));
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 	for (npp = &bp->cb_head; *npp != NULL; npp = &(*npp)->rn_hash_next)
8857c478bd9Sstevel@tonic-gate 		if (*npp == np)
8867c478bd9Sstevel@tonic-gate 			break;
8877c478bd9Sstevel@tonic-gate 
8887c478bd9Sstevel@tonic-gate 	assert(*npp == np);
8897c478bd9Sstevel@tonic-gate 	*npp = np->rn_hash_next;
8907c478bd9Sstevel@tonic-gate 	np->rn_hash_next = NULL;
8917c478bd9Sstevel@tonic-gate }
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate /*
8947c478bd9Sstevel@tonic-gate  * verify that the 'parent' type can have a child typed 'child'
8957c478bd9Sstevel@tonic-gate  * Fails with
8967c478bd9Sstevel@tonic-gate  *   _INVALID_TYPE - argument is invalid
8977c478bd9Sstevel@tonic-gate  *   _TYPE_MISMATCH - parent type cannot have children of type child
8987c478bd9Sstevel@tonic-gate  */
8997c478bd9Sstevel@tonic-gate static int
9007c478bd9Sstevel@tonic-gate rc_check_parent_child(uint32_t parent, uint32_t child)
9017c478bd9Sstevel@tonic-gate {
9027c478bd9Sstevel@tonic-gate 	int idx;
9037c478bd9Sstevel@tonic-gate 	uint32_t type;
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 	if (parent == 0 || parent >= NUM_TYPES ||
9067c478bd9Sstevel@tonic-gate 	    child == 0 || child >= NUM_TYPES)
9077c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_INVALID_TYPE); /* invalid types */
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 	for (idx = 0; idx < MAX_VALID_CHILDREN; idx++) {
9107c478bd9Sstevel@tonic-gate 		type = rc_types[parent].rt_valid_children[idx];
9117c478bd9Sstevel@tonic-gate 		if (type == child)
9127c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_SUCCESS);
9137c478bd9Sstevel@tonic-gate 	}
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
9167c478bd9Sstevel@tonic-gate }
9177c478bd9Sstevel@tonic-gate 
9187c478bd9Sstevel@tonic-gate /*
9197c478bd9Sstevel@tonic-gate  * Fails with
9207c478bd9Sstevel@tonic-gate  *   _INVALID_TYPE - type is invalid
9217c478bd9Sstevel@tonic-gate  *   _BAD_REQUEST - name is an invalid name for a node of type type
9227c478bd9Sstevel@tonic-gate  */
9237c478bd9Sstevel@tonic-gate int
9247c478bd9Sstevel@tonic-gate rc_check_type_name(uint32_t type, const char *name)
9257c478bd9Sstevel@tonic-gate {
9267c478bd9Sstevel@tonic-gate 	if (type == 0 || type >= NUM_TYPES)
9277c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_INVALID_TYPE); /* invalid types */
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate 	if (uu_check_name(name, rc_types[type].rt_name_flags) == -1)
9307c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
9337c478bd9Sstevel@tonic-gate }
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate static int
9367c478bd9Sstevel@tonic-gate rc_check_pgtype_name(const char *name)
9377c478bd9Sstevel@tonic-gate {
9387c478bd9Sstevel@tonic-gate 	if (uu_check_name(name, UU_NAME_DOMAIN) == -1)
9397c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
9427c478bd9Sstevel@tonic-gate }
9437c478bd9Sstevel@tonic-gate 
9445b7f77adStw21770 /*
9455b7f77adStw21770  * rc_node_free_fmri should be called whenever a node loses its parent.
9465b7f77adStw21770  * The reason is that the node's fmri string is built up by concatenating
9475b7f77adStw21770  * its name to the parent's fmri.  Thus, when the node no longer has a
9485b7f77adStw21770  * parent, its fmri is no longer valid.
9495b7f77adStw21770  */
9505b7f77adStw21770 static void
9515b7f77adStw21770 rc_node_free_fmri(rc_node_t *np)
9525b7f77adStw21770 {
9535b7f77adStw21770 	if (np->rn_fmri != NULL) {
9545b7f77adStw21770 		free((void *)np->rn_fmri);
9555b7f77adStw21770 		np->rn_fmri = NULL;
9565b7f77adStw21770 	}
9575b7f77adStw21770 }
9585b7f77adStw21770 
9595b7f77adStw21770 /*
9605b7f77adStw21770  * Concatenate the appropriate separator and the FMRI element to the base
9615b7f77adStw21770  * FMRI string at fmri.
9625b7f77adStw21770  *
9635b7f77adStw21770  * Fails with
9645b7f77adStw21770  *	_TRUNCATED	Not enough room in buffer at fmri.
9655b7f77adStw21770  */
9665b7f77adStw21770 static int
9675b7f77adStw21770 rc_concat_fmri_element(
9685b7f77adStw21770 	char *fmri,			/* base fmri */
9695b7f77adStw21770 	size_t bufsize,			/* size of buf at fmri */
9705b7f77adStw21770 	size_t *sz_out,			/* receives result size. */
9715b7f77adStw21770 	const char *element,		/* element name to concat */
9725b7f77adStw21770 	rep_protocol_entity_t type)	/* type of element */
9735b7f77adStw21770 {
9745b7f77adStw21770 	size_t actual;
9755b7f77adStw21770 	const char *name = element;
9765b7f77adStw21770 	int rc;
9775b7f77adStw21770 	const char *separator;
9785b7f77adStw21770 
9795b7f77adStw21770 	if (bufsize > 0)
9805b7f77adStw21770 		*sz_out = strlen(fmri);
9815b7f77adStw21770 	else
9825b7f77adStw21770 		*sz_out = 0;
9835b7f77adStw21770 
9845b7f77adStw21770 	switch (type) {
9855b7f77adStw21770 	case REP_PROTOCOL_ENTITY_SCOPE:
9865b7f77adStw21770 		if (strcmp(element, SCF_FMRI_LOCAL_SCOPE) == 0) {
9875b7f77adStw21770 			/*
9885b7f77adStw21770 			 * No need to display scope information if we are
9895b7f77adStw21770 			 * in the local scope.
9905b7f77adStw21770 			 */
9915b7f77adStw21770 			separator = SCF_FMRI_SVC_PREFIX;
9925b7f77adStw21770 			name = NULL;
9935b7f77adStw21770 		} else {
9945b7f77adStw21770 			/*
9955b7f77adStw21770 			 * Need to display scope information, because it is
9965b7f77adStw21770 			 * not the local scope.
9975b7f77adStw21770 			 */
9985b7f77adStw21770 			separator = SCF_FMRI_SVC_PREFIX SCF_FMRI_SCOPE_PREFIX;
9995b7f77adStw21770 		}
10005b7f77adStw21770 		break;
10015b7f77adStw21770 	case REP_PROTOCOL_ENTITY_SERVICE:
10025b7f77adStw21770 		separator = SCF_FMRI_SERVICE_PREFIX;
10035b7f77adStw21770 		break;
10045b7f77adStw21770 	case REP_PROTOCOL_ENTITY_INSTANCE:
10055b7f77adStw21770 		separator = SCF_FMRI_INSTANCE_PREFIX;
10065b7f77adStw21770 		break;
10075b7f77adStw21770 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
10085b7f77adStw21770 	case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
10095b7f77adStw21770 		separator = SCF_FMRI_PROPERTYGRP_PREFIX;
10105b7f77adStw21770 		break;
10115b7f77adStw21770 	case REP_PROTOCOL_ENTITY_PROPERTY:
10125b7f77adStw21770 		separator = SCF_FMRI_PROPERTY_PREFIX;
10135b7f77adStw21770 		break;
10145b7f77adStw21770 	case REP_PROTOCOL_ENTITY_VALUE:
10155b7f77adStw21770 		/*
10165b7f77adStw21770 		 * A value does not have a separate FMRI from its property,
10175b7f77adStw21770 		 * so there is nothing to concat.
10185b7f77adStw21770 		 */
10195b7f77adStw21770 		return (REP_PROTOCOL_SUCCESS);
10205b7f77adStw21770 	case REP_PROTOCOL_ENTITY_SNAPSHOT:
10215b7f77adStw21770 	case REP_PROTOCOL_ENTITY_SNAPLEVEL:
10225b7f77adStw21770 		/* Snapshots do not have FMRIs, so there is nothing to do. */
10235b7f77adStw21770 		return (REP_PROTOCOL_SUCCESS);
10245b7f77adStw21770 	default:
10255b7f77adStw21770 		(void) fprintf(stderr, "%s:%d: Unknown protocol type %d.\n",
10265b7f77adStw21770 		    __FILE__, __LINE__, type);
10275b7f77adStw21770 		abort();	/* Missing a case in switch if we get here. */
10285b7f77adStw21770 	}
10295b7f77adStw21770 
10305b7f77adStw21770 	/* Concatenate separator and element to the fmri buffer. */
10315b7f77adStw21770 
10325b7f77adStw21770 	actual = strlcat(fmri, separator, bufsize);
10335b7f77adStw21770 	if (name != NULL) {
10345b7f77adStw21770 		if (actual < bufsize) {
10355b7f77adStw21770 			actual = strlcat(fmri, name, bufsize);
10365b7f77adStw21770 		} else {
10375b7f77adStw21770 			actual += strlen(name);
10385b7f77adStw21770 		}
10395b7f77adStw21770 	}
10405b7f77adStw21770 	if (actual < bufsize) {
10415b7f77adStw21770 		rc = REP_PROTOCOL_SUCCESS;
10425b7f77adStw21770 	} else {
10435b7f77adStw21770 		rc = REP_PROTOCOL_FAIL_TRUNCATED;
10445b7f77adStw21770 	}
10455b7f77adStw21770 	*sz_out = actual;
10465b7f77adStw21770 	return (rc);
10475b7f77adStw21770 }
10485b7f77adStw21770 
10495b7f77adStw21770 /*
10505b7f77adStw21770  * Get the FMRI for the node at np.  The fmri will be placed in buf.  On
10515b7f77adStw21770  * success sz_out will be set to the size of the fmri in buf.  If
10525b7f77adStw21770  * REP_PROTOCOL_FAIL_TRUNCATED is returned, sz_out will be set to the size
10535b7f77adStw21770  * of the buffer that would be required to avoid truncation.
10545b7f77adStw21770  *
10555b7f77adStw21770  * Fails with
10565b7f77adStw21770  *	_TRUNCATED	not enough room in buf for the FMRI.
10575b7f77adStw21770  */
10585b7f77adStw21770 static int
10595b7f77adStw21770 rc_node_get_fmri_or_fragment(rc_node_t *np, char *buf, size_t bufsize,
10605b7f77adStw21770     size_t *sz_out)
10615b7f77adStw21770 {
10625b7f77adStw21770 	size_t fmri_len = 0;
10635b7f77adStw21770 	int r;
10645b7f77adStw21770 
10655b7f77adStw21770 	if (bufsize > 0)
10665b7f77adStw21770 		*buf = 0;
10675b7f77adStw21770 	*sz_out = 0;
10685b7f77adStw21770 
10695b7f77adStw21770 	if (np->rn_fmri == NULL) {
10705b7f77adStw21770 		/*
10715b7f77adStw21770 		 * A NULL rn_fmri implies that this is a top level scope.
10725b7f77adStw21770 		 * Child nodes will always have an rn_fmri established
10735b7f77adStw21770 		 * because both rc_node_link_child() and
10745b7f77adStw21770 		 * rc_node_relink_child() call rc_node_build_fmri().  In
10755b7f77adStw21770 		 * this case, we'll just return our name preceded by the
10765b7f77adStw21770 		 * appropriate FMRI decorations.
10775b7f77adStw21770 		 */
10785b7f77adStw21770 		assert(np->rn_parent == NULL);
10795b7f77adStw21770 		r = rc_concat_fmri_element(buf, bufsize, &fmri_len, np->rn_name,
10805b7f77adStw21770 		    np->rn_id.rl_type);
10815b7f77adStw21770 		if (r != REP_PROTOCOL_SUCCESS)
10825b7f77adStw21770 			return (r);
10835b7f77adStw21770 	} else {
10845b7f77adStw21770 		/* We have an fmri, so return it. */
10855b7f77adStw21770 		fmri_len = strlcpy(buf, np->rn_fmri, bufsize);
10865b7f77adStw21770 	}
10875b7f77adStw21770 
10885b7f77adStw21770 	*sz_out = fmri_len;
10895b7f77adStw21770 
10905b7f77adStw21770 	if (fmri_len >= bufsize)
10915b7f77adStw21770 		return (REP_PROTOCOL_FAIL_TRUNCATED);
10925b7f77adStw21770 
10935b7f77adStw21770 	return (REP_PROTOCOL_SUCCESS);
10945b7f77adStw21770 }
10955b7f77adStw21770 
10965b7f77adStw21770 /*
10975b7f77adStw21770  * Build an FMRI string for this node and save it in rn_fmri.
10985b7f77adStw21770  *
10995b7f77adStw21770  * The basic strategy here is to get the fmri of our parent and then
11005b7f77adStw21770  * concatenate the appropriate separator followed by our name.  If our name
11015b7f77adStw21770  * is null, the resulting fmri will just be a copy of the parent fmri.
11025b7f77adStw21770  * rc_node_build_fmri() should be called with the RC_NODE_USING_PARENT flag
11035b7f77adStw21770  * set.  Also the rn_lock for this node should be held.
11045b7f77adStw21770  *
11055b7f77adStw21770  * Fails with
11065b7f77adStw21770  *	_NO_RESOURCES	Could not allocate memory.
11075b7f77adStw21770  */
11085b7f77adStw21770 static int
11095b7f77adStw21770 rc_node_build_fmri(rc_node_t *np)
11105b7f77adStw21770 {
11115b7f77adStw21770 	size_t actual;
11125b7f77adStw21770 	char fmri[REP_PROTOCOL_FMRI_LEN];
11135b7f77adStw21770 	int rc;
11145b7f77adStw21770 	size_t	sz = REP_PROTOCOL_FMRI_LEN;
11155b7f77adStw21770 
11165b7f77adStw21770 	assert(MUTEX_HELD(&np->rn_lock));
11175b7f77adStw21770 	assert(np->rn_flags & RC_NODE_USING_PARENT);
11185b7f77adStw21770 
11195b7f77adStw21770 	rc_node_free_fmri(np);
11205b7f77adStw21770 
11215b7f77adStw21770 	rc = rc_node_get_fmri_or_fragment(np->rn_parent, fmri, sz, &actual);
11225b7f77adStw21770 	assert(rc == REP_PROTOCOL_SUCCESS);
11235b7f77adStw21770 
11245b7f77adStw21770 	if (np->rn_name != NULL) {
11255b7f77adStw21770 		rc = rc_concat_fmri_element(fmri, sz, &actual, np->rn_name,
11265b7f77adStw21770 		    np->rn_id.rl_type);
11275b7f77adStw21770 		assert(rc == REP_PROTOCOL_SUCCESS);
11285b7f77adStw21770 		np->rn_fmri = strdup(fmri);
11295b7f77adStw21770 	} else {
11305b7f77adStw21770 		np->rn_fmri = strdup(fmri);
11315b7f77adStw21770 	}
11325b7f77adStw21770 	if (np->rn_fmri == NULL) {
11335b7f77adStw21770 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
11345b7f77adStw21770 	} else {
11355b7f77adStw21770 		rc = REP_PROTOCOL_SUCCESS;
11365b7f77adStw21770 	}
11375b7f77adStw21770 
11385b7f77adStw21770 	return (rc);
11395b7f77adStw21770 }
11405b7f77adStw21770 
11415b7f77adStw21770 /*
11425b7f77adStw21770  * Get the FMRI of the node at np placing the result in fmri.  Then
11435b7f77adStw21770  * concatenate the additional element to fmri.  The type variable indicates
11445b7f77adStw21770  * the type of element, so that the appropriate separator can be
11455b7f77adStw21770  * generated.  size is the number of bytes in the buffer at fmri, and
11465b7f77adStw21770  * sz_out receives the size of the generated string.  If the result is
11475b7f77adStw21770  * truncated, sz_out will receive the size of the buffer that would be
11485b7f77adStw21770  * required to avoid truncation.
11495b7f77adStw21770  *
11505b7f77adStw21770  * Fails with
11515b7f77adStw21770  *	_TRUNCATED	Not enough room in buffer at fmri.
11525b7f77adStw21770  */
11535b7f77adStw21770 static int
11545b7f77adStw21770 rc_get_fmri_and_concat(rc_node_t *np, char *fmri, size_t size, size_t *sz_out,
11555b7f77adStw21770     const char *element, rep_protocol_entity_t type)
11565b7f77adStw21770 {
11575b7f77adStw21770 	int rc;
11585b7f77adStw21770 
11595b7f77adStw21770 	if ((rc = rc_node_get_fmri_or_fragment(np, fmri, size, sz_out)) !=
11605b7f77adStw21770 	    REP_PROTOCOL_SUCCESS) {
11615b7f77adStw21770 		return (rc);
11625b7f77adStw21770 	}
11635b7f77adStw21770 	if ((rc = rc_concat_fmri_element(fmri, size, sz_out, element, type)) !=
11645b7f77adStw21770 	    REP_PROTOCOL_SUCCESS) {
11655b7f77adStw21770 		return (rc);
11665b7f77adStw21770 	}
11675b7f77adStw21770 
11685b7f77adStw21770 	return (REP_PROTOCOL_SUCCESS);
11695b7f77adStw21770 }
11705b7f77adStw21770 
11717c478bd9Sstevel@tonic-gate static int
11727c478bd9Sstevel@tonic-gate rc_notify_info_interested(rc_notify_info_t *rnip, rc_notify_t *np)
11737c478bd9Sstevel@tonic-gate {
11747c478bd9Sstevel@tonic-gate 	rc_node_t *nnp = np->rcn_node;
11757c478bd9Sstevel@tonic-gate 	int i;
11767c478bd9Sstevel@tonic-gate 
11777c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&rc_pg_notify_lock));
11787c478bd9Sstevel@tonic-gate 
11797c478bd9Sstevel@tonic-gate 	if (np->rcn_delete != NULL) {
11807c478bd9Sstevel@tonic-gate 		assert(np->rcn_info == NULL && np->rcn_node == NULL);
11817c478bd9Sstevel@tonic-gate 		return (1);		/* everyone likes deletes */
11827c478bd9Sstevel@tonic-gate 	}
11837c478bd9Sstevel@tonic-gate 	if (np->rcn_node == NULL) {
11847c478bd9Sstevel@tonic-gate 		assert(np->rcn_info != NULL || np->rcn_delete != NULL);
11857c478bd9Sstevel@tonic-gate 		return (0);
11867c478bd9Sstevel@tonic-gate 	}
11877c478bd9Sstevel@tonic-gate 	assert(np->rcn_info == NULL);
11887c478bd9Sstevel@tonic-gate 
11897c478bd9Sstevel@tonic-gate 	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
11907c478bd9Sstevel@tonic-gate 		if (rnip->rni_namelist[i] != NULL) {
11917c478bd9Sstevel@tonic-gate 			if (strcmp(nnp->rn_name, rnip->rni_namelist[i]) == 0)
11927c478bd9Sstevel@tonic-gate 				return (1);
11937c478bd9Sstevel@tonic-gate 		}
11947c478bd9Sstevel@tonic-gate 		if (rnip->rni_typelist[i] != NULL) {
11957c478bd9Sstevel@tonic-gate 			if (strcmp(nnp->rn_type, rnip->rni_typelist[i]) == 0)
11967c478bd9Sstevel@tonic-gate 				return (1);
11977c478bd9Sstevel@tonic-gate 		}
11987c478bd9Sstevel@tonic-gate 	}
11997c478bd9Sstevel@tonic-gate 	return (0);
12007c478bd9Sstevel@tonic-gate }
12017c478bd9Sstevel@tonic-gate 
12027c478bd9Sstevel@tonic-gate static void
12037c478bd9Sstevel@tonic-gate rc_notify_insert_node(rc_node_t *nnp)
12047c478bd9Sstevel@tonic-gate {
12057c478bd9Sstevel@tonic-gate 	rc_notify_t *np = &nnp->rn_notify;
12067c478bd9Sstevel@tonic-gate 	rc_notify_info_t *nip;
12077c478bd9Sstevel@tonic-gate 	int found = 0;
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate 	assert(np->rcn_info == NULL);
12107c478bd9Sstevel@tonic-gate 
12117c478bd9Sstevel@tonic-gate 	if (nnp->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
12127c478bd9Sstevel@tonic-gate 		return;
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
12157c478bd9Sstevel@tonic-gate 	np->rcn_node = nnp;
12167c478bd9Sstevel@tonic-gate 	for (nip = uu_list_first(rc_notify_info_list); nip != NULL;
12177c478bd9Sstevel@tonic-gate 	    nip = uu_list_next(rc_notify_info_list, nip)) {
12187c478bd9Sstevel@tonic-gate 		if (rc_notify_info_interested(nip, np)) {
12197c478bd9Sstevel@tonic-gate 			(void) pthread_cond_broadcast(&nip->rni_cv);
12207c478bd9Sstevel@tonic-gate 			found++;
12217c478bd9Sstevel@tonic-gate 		}
12227c478bd9Sstevel@tonic-gate 	}
12237c478bd9Sstevel@tonic-gate 	if (found)
12247c478bd9Sstevel@tonic-gate 		(void) uu_list_insert_before(rc_notify_list, NULL, np);
12257c478bd9Sstevel@tonic-gate 	else
12267c478bd9Sstevel@tonic-gate 		np->rcn_node = NULL;
12277c478bd9Sstevel@tonic-gate 
12287c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
12297c478bd9Sstevel@tonic-gate }
12307c478bd9Sstevel@tonic-gate 
12317c478bd9Sstevel@tonic-gate static void
12327c478bd9Sstevel@tonic-gate rc_notify_deletion(rc_notify_delete_t *ndp, const char *service,
12337c478bd9Sstevel@tonic-gate     const char *instance, const char *pg)
12347c478bd9Sstevel@tonic-gate {
12357c478bd9Sstevel@tonic-gate 	rc_notify_info_t *nip;
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate 	uu_list_node_init(&ndp->rnd_notify, &ndp->rnd_notify.rcn_list_node,
12387c478bd9Sstevel@tonic-gate 	    rc_notify_pool);
12397c478bd9Sstevel@tonic-gate 	ndp->rnd_notify.rcn_delete = ndp;
12407c478bd9Sstevel@tonic-gate 
12417c478bd9Sstevel@tonic-gate 	(void) snprintf(ndp->rnd_fmri, sizeof (ndp->rnd_fmri),
12427c478bd9Sstevel@tonic-gate 	    "svc:/%s%s%s%s%s", service,
12437c478bd9Sstevel@tonic-gate 	    (instance != NULL)? ":" : "", (instance != NULL)? instance : "",
12447c478bd9Sstevel@tonic-gate 	    (pg != NULL)? "/:properties/" : "", (pg != NULL)? pg : "");
12457c478bd9Sstevel@tonic-gate 
12467c478bd9Sstevel@tonic-gate 	/*
12477c478bd9Sstevel@tonic-gate 	 * add to notification list, notify watchers
12487c478bd9Sstevel@tonic-gate 	 */
12497c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
12507c478bd9Sstevel@tonic-gate 	for (nip = uu_list_first(rc_notify_info_list); nip != NULL;
12517c478bd9Sstevel@tonic-gate 	    nip = uu_list_next(rc_notify_info_list, nip))
12527c478bd9Sstevel@tonic-gate 		(void) pthread_cond_broadcast(&nip->rni_cv);
12537c478bd9Sstevel@tonic-gate 	(void) uu_list_insert_before(rc_notify_list, NULL, ndp);
12547c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
12557c478bd9Sstevel@tonic-gate }
12567c478bd9Sstevel@tonic-gate 
12577c478bd9Sstevel@tonic-gate static void
12587c478bd9Sstevel@tonic-gate rc_notify_remove_node(rc_node_t *nnp)
12597c478bd9Sstevel@tonic-gate {
12607c478bd9Sstevel@tonic-gate 	rc_notify_t *np = &nnp->rn_notify;
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate 	assert(np->rcn_info == NULL);
12637c478bd9Sstevel@tonic-gate 	assert(!MUTEX_HELD(&nnp->rn_lock));
12647c478bd9Sstevel@tonic-gate 
12657c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
12667c478bd9Sstevel@tonic-gate 	while (np->rcn_node != NULL) {
12677c478bd9Sstevel@tonic-gate 		if (rc_notify_in_use) {
12687c478bd9Sstevel@tonic-gate 			(void) pthread_cond_wait(&rc_pg_notify_cv,
12697c478bd9Sstevel@tonic-gate 			    &rc_pg_notify_lock);
12707c478bd9Sstevel@tonic-gate 			continue;
12717c478bd9Sstevel@tonic-gate 		}
12727c478bd9Sstevel@tonic-gate 		(void) uu_list_remove(rc_notify_list, np);
12737c478bd9Sstevel@tonic-gate 		np->rcn_node = NULL;
12747c478bd9Sstevel@tonic-gate 		break;
12757c478bd9Sstevel@tonic-gate 	}
12767c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
12777c478bd9Sstevel@tonic-gate }
12787c478bd9Sstevel@tonic-gate 
12797c478bd9Sstevel@tonic-gate static void
12807c478bd9Sstevel@tonic-gate rc_notify_remove_locked(rc_notify_t *np)
12817c478bd9Sstevel@tonic-gate {
12827c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&rc_pg_notify_lock));
12837c478bd9Sstevel@tonic-gate 	assert(rc_notify_in_use == 0);
12847c478bd9Sstevel@tonic-gate 
12857c478bd9Sstevel@tonic-gate 	(void) uu_list_remove(rc_notify_list, np);
12867c478bd9Sstevel@tonic-gate 	if (np->rcn_node) {
12877c478bd9Sstevel@tonic-gate 		np->rcn_node = NULL;
12887c478bd9Sstevel@tonic-gate 	} else if (np->rcn_delete) {
12897c478bd9Sstevel@tonic-gate 		uu_free(np->rcn_delete);
12907c478bd9Sstevel@tonic-gate 	} else {
12917c478bd9Sstevel@tonic-gate 		assert(0);	/* CAN'T HAPPEN */
12927c478bd9Sstevel@tonic-gate 	}
12937c478bd9Sstevel@tonic-gate }
12947c478bd9Sstevel@tonic-gate 
12957c478bd9Sstevel@tonic-gate /*
12967c478bd9Sstevel@tonic-gate  * Permission checking functions.  See comment atop this file.
12977c478bd9Sstevel@tonic-gate  */
12987c478bd9Sstevel@tonic-gate #ifndef NATIVE_BUILD
12997c478bd9Sstevel@tonic-gate static permcheck_t *
13007c478bd9Sstevel@tonic-gate pc_create()
13017c478bd9Sstevel@tonic-gate {
13027c478bd9Sstevel@tonic-gate 	permcheck_t *p;
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate 	p = uu_zalloc(sizeof (*p));
13057c478bd9Sstevel@tonic-gate 	if (p == NULL)
13067c478bd9Sstevel@tonic-gate 		return (NULL);
13077c478bd9Sstevel@tonic-gate 	p->pc_bnum = 8;			/* Normal case will only have 2 elts. */
13087c478bd9Sstevel@tonic-gate 	p->pc_buckets = uu_zalloc(sizeof (*p->pc_buckets) * p->pc_bnum);
13097c478bd9Sstevel@tonic-gate 	if (p->pc_buckets == NULL) {
13107c478bd9Sstevel@tonic-gate 		uu_free(p);
13117c478bd9Sstevel@tonic-gate 		return (NULL);
13127c478bd9Sstevel@tonic-gate 	}
13137c478bd9Sstevel@tonic-gate 
13147c478bd9Sstevel@tonic-gate 	p->pc_enum = 0;
13157c478bd9Sstevel@tonic-gate 	return (p);
13167c478bd9Sstevel@tonic-gate }
13177c478bd9Sstevel@tonic-gate 
13187c478bd9Sstevel@tonic-gate static void
13197c478bd9Sstevel@tonic-gate pc_free(permcheck_t *pcp)
13207c478bd9Sstevel@tonic-gate {
13217c478bd9Sstevel@tonic-gate 	uint_t i;
13227c478bd9Sstevel@tonic-gate 	struct pc_elt *ep, *next;
13237c478bd9Sstevel@tonic-gate 
13247c478bd9Sstevel@tonic-gate 	for (i = 0; i < pcp->pc_bnum; ++i) {
13257c478bd9Sstevel@tonic-gate 		for (ep = pcp->pc_buckets[i]; ep != NULL; ep = next) {
13267c478bd9Sstevel@tonic-gate 			next = ep->pce_next;
13277c478bd9Sstevel@tonic-gate 			free(ep);
13287c478bd9Sstevel@tonic-gate 		}
13297c478bd9Sstevel@tonic-gate 	}
13307c478bd9Sstevel@tonic-gate 
13317c478bd9Sstevel@tonic-gate 	free(pcp->pc_buckets);
13327c478bd9Sstevel@tonic-gate 	free(pcp);
13337c478bd9Sstevel@tonic-gate }
13347c478bd9Sstevel@tonic-gate 
13357c478bd9Sstevel@tonic-gate static uint32_t
13367c478bd9Sstevel@tonic-gate pc_hash(const char *auth)
13377c478bd9Sstevel@tonic-gate {
13387c478bd9Sstevel@tonic-gate 	uint32_t h = 0, g;
13397c478bd9Sstevel@tonic-gate 	const char *p;
13407c478bd9Sstevel@tonic-gate 
13417c478bd9Sstevel@tonic-gate 	/*
13427c478bd9Sstevel@tonic-gate 	 * Generic hash function from uts/common/os/modhash.c.
13437c478bd9Sstevel@tonic-gate 	 */
13447c478bd9Sstevel@tonic-gate 	for (p = auth; *p != '\0'; ++p) {
13457c478bd9Sstevel@tonic-gate 		h = (h << 4) + *p;
13467c478bd9Sstevel@tonic-gate 		g = (h & 0xf0000000);
13477c478bd9Sstevel@tonic-gate 		if (g != 0) {
13487c478bd9Sstevel@tonic-gate 			h ^= (g >> 24);
13497c478bd9Sstevel@tonic-gate 			h ^= g;
13507c478bd9Sstevel@tonic-gate 		}
13517c478bd9Sstevel@tonic-gate 	}
13527c478bd9Sstevel@tonic-gate 
13537c478bd9Sstevel@tonic-gate 	return (h);
13547c478bd9Sstevel@tonic-gate }
13557c478bd9Sstevel@tonic-gate 
1356a4dc1477STom Whitten static perm_status_t
13575b7f77adStw21770 pc_exists(permcheck_t *pcp, const char *auth)
13587c478bd9Sstevel@tonic-gate {
13597c478bd9Sstevel@tonic-gate 	uint32_t h;
13607c478bd9Sstevel@tonic-gate 	struct pc_elt *ep;
13617c478bd9Sstevel@tonic-gate 
13627c478bd9Sstevel@tonic-gate 	h = pc_hash(auth);
13637c478bd9Sstevel@tonic-gate 	for (ep = pcp->pc_buckets[h & (pcp->pc_bnum - 1)];
13647c478bd9Sstevel@tonic-gate 	    ep != NULL;
13657c478bd9Sstevel@tonic-gate 	    ep = ep->pce_next) {
13665b7f77adStw21770 		if (strcmp(auth, ep->pce_auth) == 0) {
13675b7f77adStw21770 			pcp->pc_auth_string = ep->pce_auth;
1368a4dc1477STom Whitten 			return (PERM_GRANTED);
13697c478bd9Sstevel@tonic-gate 		}
13705b7f77adStw21770 	}
13717c478bd9Sstevel@tonic-gate 
1372a4dc1477STom Whitten 	return (PERM_DENIED);
13737c478bd9Sstevel@tonic-gate }
13747c478bd9Sstevel@tonic-gate 
1375a4dc1477STom Whitten static perm_status_t
13765b7f77adStw21770 pc_match(permcheck_t *pcp, const char *pattern)
13777c478bd9Sstevel@tonic-gate {
13787c478bd9Sstevel@tonic-gate 	uint_t i;
13797c478bd9Sstevel@tonic-gate 	struct pc_elt *ep;
13807c478bd9Sstevel@tonic-gate 
13817c478bd9Sstevel@tonic-gate 	for (i = 0; i < pcp->pc_bnum; ++i) {
13827c478bd9Sstevel@tonic-gate 		for (ep = pcp->pc_buckets[i]; ep != NULL; ep = ep->pce_next) {
13835b7f77adStw21770 			if (_auth_match(pattern, ep->pce_auth)) {
13845b7f77adStw21770 				pcp->pc_auth_string = ep->pce_auth;
1385a4dc1477STom Whitten 				return (PERM_GRANTED);
13867c478bd9Sstevel@tonic-gate 			}
13877c478bd9Sstevel@tonic-gate 		}
13885b7f77adStw21770 	}
13897c478bd9Sstevel@tonic-gate 
1390a4dc1477STom Whitten 	return (PERM_DENIED);
13917c478bd9Sstevel@tonic-gate }
13927c478bd9Sstevel@tonic-gate 
13937c478bd9Sstevel@tonic-gate static int
13947c478bd9Sstevel@tonic-gate pc_grow(permcheck_t *pcp)
13957c478bd9Sstevel@tonic-gate {
13967c478bd9Sstevel@tonic-gate 	uint_t new_bnum, i, j;
13977c478bd9Sstevel@tonic-gate 	struct pc_elt **new_buckets;
13987c478bd9Sstevel@tonic-gate 	struct pc_elt *ep, *next;
13997c478bd9Sstevel@tonic-gate 
14007c478bd9Sstevel@tonic-gate 	new_bnum = pcp->pc_bnum * 2;
14017c478bd9Sstevel@tonic-gate 	if (new_bnum < pcp->pc_bnum)
14027c478bd9Sstevel@tonic-gate 		/* Homey don't play that. */
14037c478bd9Sstevel@tonic-gate 		return (-1);
14047c478bd9Sstevel@tonic-gate 
14057c478bd9Sstevel@tonic-gate 	new_buckets = uu_zalloc(sizeof (*new_buckets) * new_bnum);
14067c478bd9Sstevel@tonic-gate 	if (new_buckets == NULL)
14077c478bd9Sstevel@tonic-gate 		return (-1);
14087c478bd9Sstevel@tonic-gate 
14097c478bd9Sstevel@tonic-gate 	for (i = 0; i < pcp->pc_bnum; ++i) {
14107c478bd9Sstevel@tonic-gate 		for (ep = pcp->pc_buckets[i]; ep != NULL; ep = next) {
14117c478bd9Sstevel@tonic-gate 			next = ep->pce_next;
14127c478bd9Sstevel@tonic-gate 			j = pc_hash(ep->pce_auth) & (new_bnum - 1);
14137c478bd9Sstevel@tonic-gate 			ep->pce_next = new_buckets[j];
14147c478bd9Sstevel@tonic-gate 			new_buckets[j] = ep;
14157c478bd9Sstevel@tonic-gate 		}
14167c478bd9Sstevel@tonic-gate 	}
14177c478bd9Sstevel@tonic-gate 
14187c478bd9Sstevel@tonic-gate 	uu_free(pcp->pc_buckets);
14197c478bd9Sstevel@tonic-gate 	pcp->pc_buckets = new_buckets;
14207c478bd9Sstevel@tonic-gate 	pcp->pc_bnum = new_bnum;
14217c478bd9Sstevel@tonic-gate 
14227c478bd9Sstevel@tonic-gate 	return (0);
14237c478bd9Sstevel@tonic-gate }
14247c478bd9Sstevel@tonic-gate 
14257c478bd9Sstevel@tonic-gate static int
14265b7f77adStw21770 pc_add(permcheck_t *pcp, const char *auth, pc_auth_type_t auth_type)
14277c478bd9Sstevel@tonic-gate {
14287c478bd9Sstevel@tonic-gate 	struct pc_elt *ep;
14297c478bd9Sstevel@tonic-gate 	uint_t i;
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate 	ep = uu_zalloc(offsetof(struct pc_elt, pce_auth) + strlen(auth) + 1);
14327c478bd9Sstevel@tonic-gate 	if (ep == NULL)
14337c478bd9Sstevel@tonic-gate 		return (-1);
14347c478bd9Sstevel@tonic-gate 
14357c478bd9Sstevel@tonic-gate 	/* Grow if pc_enum / pc_bnum > 3/4. */
14367c478bd9Sstevel@tonic-gate 	if (pcp->pc_enum * 4 > 3 * pcp->pc_bnum)
14377c478bd9Sstevel@tonic-gate 		/* Failure is not a stopper; we'll try again next time. */
14387c478bd9Sstevel@tonic-gate 		(void) pc_grow(pcp);
14397c478bd9Sstevel@tonic-gate 
14407c478bd9Sstevel@tonic-gate 	(void) strcpy(ep->pce_auth, auth);
14417c478bd9Sstevel@tonic-gate 
14427c478bd9Sstevel@tonic-gate 	i = pc_hash(auth) & (pcp->pc_bnum - 1);
14437c478bd9Sstevel@tonic-gate 	ep->pce_next = pcp->pc_buckets[i];
14447c478bd9Sstevel@tonic-gate 	pcp->pc_buckets[i] = ep;
14457c478bd9Sstevel@tonic-gate 
14465b7f77adStw21770 	if (auth_type > pcp->pc_specific_type) {
14475b7f77adStw21770 		pcp->pc_specific_type = auth_type;
14485b7f77adStw21770 		pcp->pc_specific = ep;
14495b7f77adStw21770 	}
14505b7f77adStw21770 
14517c478bd9Sstevel@tonic-gate 	++pcp->pc_enum;
14527c478bd9Sstevel@tonic-gate 
14537c478bd9Sstevel@tonic-gate 	return (0);
14547c478bd9Sstevel@tonic-gate }
14557c478bd9Sstevel@tonic-gate 
14567c478bd9Sstevel@tonic-gate /*
14577c478bd9Sstevel@tonic-gate  * For the type of a property group, return the authorization which may be
14587c478bd9Sstevel@tonic-gate  * used to modify it.
14597c478bd9Sstevel@tonic-gate  */
14607c478bd9Sstevel@tonic-gate static const char *
14617c478bd9Sstevel@tonic-gate perm_auth_for_pgtype(const char *pgtype)
14627c478bd9Sstevel@tonic-gate {
14637c478bd9Sstevel@tonic-gate 	if (strcmp(pgtype, SCF_GROUP_METHOD) == 0)
14647c478bd9Sstevel@tonic-gate 		return (AUTH_MODIFY_PREFIX "method");
14657c478bd9Sstevel@tonic-gate 	else if (strcmp(pgtype, SCF_GROUP_DEPENDENCY) == 0)
14667c478bd9Sstevel@tonic-gate 		return (AUTH_MODIFY_PREFIX "dependency");
14677c478bd9Sstevel@tonic-gate 	else if (strcmp(pgtype, SCF_GROUP_APPLICATION) == 0)
14687c478bd9Sstevel@tonic-gate 		return (AUTH_MODIFY_PREFIX "application");
14697c478bd9Sstevel@tonic-gate 	else if (strcmp(pgtype, SCF_GROUP_FRAMEWORK) == 0)
14707c478bd9Sstevel@tonic-gate 		return (AUTH_MODIFY_PREFIX "framework");
14717c478bd9Sstevel@tonic-gate 	else
14727c478bd9Sstevel@tonic-gate 		return (NULL);
14737c478bd9Sstevel@tonic-gate }
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate /*
14767c478bd9Sstevel@tonic-gate  * Fails with
14777c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES - out of memory
14787c478bd9Sstevel@tonic-gate  */
14797c478bd9Sstevel@tonic-gate static int
14805b7f77adStw21770 perm_add_enabling_type(permcheck_t *pcp, const char *auth,
14815b7f77adStw21770     pc_auth_type_t auth_type)
14825b7f77adStw21770 {
14835b7f77adStw21770 	return (pc_add(pcp, auth, auth_type) == 0 ? REP_PROTOCOL_SUCCESS :
14845b7f77adStw21770 	    REP_PROTOCOL_FAIL_NO_RESOURCES);
14855b7f77adStw21770 }
14865b7f77adStw21770 
14875b7f77adStw21770 /*
14885b7f77adStw21770  * Fails with
14895b7f77adStw21770  *   _NO_RESOURCES - out of memory
14905b7f77adStw21770  */
14915b7f77adStw21770 static int
14927c478bd9Sstevel@tonic-gate perm_add_enabling(permcheck_t *pcp, const char *auth)
14937c478bd9Sstevel@tonic-gate {
14945b7f77adStw21770 	return (perm_add_enabling_type(pcp, auth, PC_AUTH_SMF));
14957c478bd9Sstevel@tonic-gate }
14967c478bd9Sstevel@tonic-gate 
14977c478bd9Sstevel@tonic-gate /* Note that perm_add_enabling_values() is defined below. */
14987c478bd9Sstevel@tonic-gate 
14997c478bd9Sstevel@tonic-gate /*
1500a4dc1477STom Whitten  * perm_granted() returns PERM_GRANTED if the current door caller has one of
1501a4dc1477STom Whitten  * the enabling authorizations in pcp, PERM_DENIED if it doesn't, PERM_GONE if
1502a4dc1477STom Whitten  * the door client went away and PERM_FAIL if an error (usually lack of
1503134a1f4eSCasper H.S. Dik  * memory) occurs.  auth_cb() checks each and every authorizations as
1504134a1f4eSCasper H.S. Dik  * enumerated by _enum_auths.  When we find a result other than PERM_DENIED,
1505134a1f4eSCasper H.S. Dik  * we short-cut the enumeration and return non-zero.
15067c478bd9Sstevel@tonic-gate  */
1507134a1f4eSCasper H.S. Dik 
1508134a1f4eSCasper H.S. Dik static int
1509134a1f4eSCasper H.S. Dik auth_cb(const char *auth, void *ctxt, void *vres)
15107c478bd9Sstevel@tonic-gate {
1511134a1f4eSCasper H.S. Dik 	permcheck_t *pcp = ctxt;
1512134a1f4eSCasper H.S. Dik 	int *pret = vres;
15137c478bd9Sstevel@tonic-gate 
15147c478bd9Sstevel@tonic-gate 	if (strchr(auth, KV_WILDCHAR) == NULL)
1515134a1f4eSCasper H.S. Dik 		*pret = pc_exists(pcp, auth);
15167c478bd9Sstevel@tonic-gate 	else
1517134a1f4eSCasper H.S. Dik 		*pret = pc_match(pcp, auth);
15187c478bd9Sstevel@tonic-gate 
1519134a1f4eSCasper H.S. Dik 	if (*pret != PERM_DENIED)
1520134a1f4eSCasper H.S. Dik 		return (1);
15215b7f77adStw21770 	/*
15225b7f77adStw21770 	 * If we failed, choose the most specific auth string for use in
15235b7f77adStw21770 	 * the audit event.
15245b7f77adStw21770 	 */
15255b7f77adStw21770 	assert(pcp->pc_specific != NULL);
15265b7f77adStw21770 	pcp->pc_auth_string = pcp->pc_specific->pce_auth;
15275b7f77adStw21770 
1528134a1f4eSCasper H.S. Dik 	return (0);		/* Tells that we need to continue */
15297c478bd9Sstevel@tonic-gate }
15307c478bd9Sstevel@tonic-gate 
1531a4dc1477STom Whitten static perm_status_t
15325b7f77adStw21770 perm_granted(permcheck_t *pcp)
15337c478bd9Sstevel@tonic-gate {
15347c478bd9Sstevel@tonic-gate 	ucred_t *uc;
15357c478bd9Sstevel@tonic-gate 
1536a4dc1477STom Whitten 	perm_status_t ret = PERM_DENIED;
15377c478bd9Sstevel@tonic-gate 	uid_t uid;
1538499fd601Sgww 	struct passwd pw;
1539499fd601Sgww 	char pwbuf[1024];	/* XXX should be NSS_BUFLEN_PASSWD */
15407c478bd9Sstevel@tonic-gate 
15417c478bd9Sstevel@tonic-gate 	/* Get the uid */
15427c478bd9Sstevel@tonic-gate 	if ((uc = get_ucred()) == NULL) {
15437c478bd9Sstevel@tonic-gate 		if (errno == EINVAL) {
15447c478bd9Sstevel@tonic-gate 			/*
15457c478bd9Sstevel@tonic-gate 			 * Client is no longer waiting for our response (e.g.,
15467c478bd9Sstevel@tonic-gate 			 * it received a signal & resumed with EINTR).
15477c478bd9Sstevel@tonic-gate 			 * Punting with door_return() would be nice but we
15487c478bd9Sstevel@tonic-gate 			 * need to release all of the locks & references we
15497c478bd9Sstevel@tonic-gate 			 * hold.  And we must report failure to the client
15507c478bd9Sstevel@tonic-gate 			 * layer to keep it from ignoring retries as
15517c478bd9Sstevel@tonic-gate 			 * already-done (idempotency & all that).  None of the
15527c478bd9Sstevel@tonic-gate 			 * error codes fit very well, so we might as well
15537c478bd9Sstevel@tonic-gate 			 * force the return of _PERMISSION_DENIED since we
15547c478bd9Sstevel@tonic-gate 			 * couldn't determine the user.
15557c478bd9Sstevel@tonic-gate 			 */
1556a4dc1477STom Whitten 			return (PERM_GONE);
15577c478bd9Sstevel@tonic-gate 		}
15587c478bd9Sstevel@tonic-gate 		assert(0);
15597c478bd9Sstevel@tonic-gate 		abort();
15607c478bd9Sstevel@tonic-gate 	}
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate 	uid = ucred_geteuid(uc);
1563f48205beScasper 	assert(uid != (uid_t)-1);
15647c478bd9Sstevel@tonic-gate 
1565499fd601Sgww 	if (getpwuid_r(uid, &pw, pwbuf, sizeof (pwbuf)) == NULL) {
1566a4dc1477STom Whitten 		return (PERM_FAIL);
1567499fd601Sgww 	}
1568499fd601Sgww 
1569499fd601Sgww 	/*
1570134a1f4eSCasper H.S. Dik 	 * Enumerate all the auths defined for the user and return the
1571134a1f4eSCasper H.S. Dik 	 * result in ret.
1572499fd601Sgww 	 */
1573134a1f4eSCasper H.S. Dik 	if (_enum_auths(pw.pw_name, auth_cb, pcp, &ret) < 0)
1574a4dc1477STom Whitten 		return (PERM_FAIL);
1575499fd601Sgww 
15767c478bd9Sstevel@tonic-gate 	return (ret);
15777c478bd9Sstevel@tonic-gate }
1578a4dc1477STom Whitten 
1579a4dc1477STom Whitten static int
1580a4dc1477STom Whitten map_granted_status(perm_status_t status, permcheck_t *pcp,
1581a4dc1477STom Whitten     char **match_auth)
1582a4dc1477STom Whitten {
1583a4dc1477STom Whitten 	int rc;
1584a4dc1477STom Whitten 
1585a4dc1477STom Whitten 	*match_auth = NULL;
1586a4dc1477STom Whitten 	switch (status) {
1587a4dc1477STom Whitten 	case PERM_DENIED:
1588a4dc1477STom Whitten 		*match_auth = strdup(pcp->pc_auth_string);
1589a4dc1477STom Whitten 		if (*match_auth == NULL)
1590a4dc1477STom Whitten 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1591a4dc1477STom Whitten 		else
1592a4dc1477STom Whitten 			rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
1593a4dc1477STom Whitten 		break;
1594a4dc1477STom Whitten 	case PERM_GRANTED:
1595a4dc1477STom Whitten 		*match_auth = strdup(pcp->pc_auth_string);
1596a4dc1477STom Whitten 		if (*match_auth == NULL)
1597a4dc1477STom Whitten 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1598a4dc1477STom Whitten 		else
1599a4dc1477STom Whitten 			rc = REP_PROTOCOL_SUCCESS;
1600a4dc1477STom Whitten 		break;
1601a4dc1477STom Whitten 	case PERM_GONE:
1602a4dc1477STom Whitten 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
1603a4dc1477STom Whitten 		break;
1604a4dc1477STom Whitten 	case PERM_FAIL:
1605a4dc1477STom Whitten 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1606a4dc1477STom Whitten 		break;
1607a4dc1477STom Whitten 	}
1608a4dc1477STom Whitten 	return (rc);
1609a4dc1477STom Whitten }
16107c478bd9Sstevel@tonic-gate #endif /* NATIVE_BUILD */
16117c478bd9Sstevel@tonic-gate 
16127c478bd9Sstevel@tonic-gate /*
16137c478bd9Sstevel@tonic-gate  * flags in RC_NODE_WAITING_FLAGS are broadcast when unset, and are used to
16147c478bd9Sstevel@tonic-gate  * serialize certain actions, and to wait for certain operations to complete
16157c478bd9Sstevel@tonic-gate  *
16167c478bd9Sstevel@tonic-gate  * The waiting flags are:
16177c478bd9Sstevel@tonic-gate  *	RC_NODE_CHILDREN_CHANGING
16187c478bd9Sstevel@tonic-gate  *		The child list is being built or changed (due to creation
16197c478bd9Sstevel@tonic-gate  *		or deletion).  All iterators pause.
16207c478bd9Sstevel@tonic-gate  *
16217c478bd9Sstevel@tonic-gate  *	RC_NODE_USING_PARENT
16227c478bd9Sstevel@tonic-gate  *		Someone is actively using the parent pointer, so we can't
16237c478bd9Sstevel@tonic-gate  *		be removed from the parent list.
16247c478bd9Sstevel@tonic-gate  *
16257c478bd9Sstevel@tonic-gate  *	RC_NODE_CREATING_CHILD
16267c478bd9Sstevel@tonic-gate  *		A child is being created -- locks out other creations, to
16277c478bd9Sstevel@tonic-gate  *		prevent insert-insert races.
16287c478bd9Sstevel@tonic-gate  *
16297c478bd9Sstevel@tonic-gate  *	RC_NODE_IN_TX
16307c478bd9Sstevel@tonic-gate  *		This object is running a transaction.
16317c478bd9Sstevel@tonic-gate  *
16327c478bd9Sstevel@tonic-gate  *	RC_NODE_DYING
16337c478bd9Sstevel@tonic-gate  *		This node might be dying.  Always set as a set, using
16347c478bd9Sstevel@tonic-gate  *		RC_NODE_DYING_FLAGS (which is everything but
16357c478bd9Sstevel@tonic-gate  *		RC_NODE_USING_PARENT)
16367c478bd9Sstevel@tonic-gate  */
16377c478bd9Sstevel@tonic-gate static int
16387c478bd9Sstevel@tonic-gate rc_node_hold_flag(rc_node_t *np, uint32_t flag)
16397c478bd9Sstevel@tonic-gate {
16407c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
16417c478bd9Sstevel@tonic-gate 	assert((flag & ~RC_NODE_WAITING_FLAGS) == 0);
16427c478bd9Sstevel@tonic-gate 
16437c478bd9Sstevel@tonic-gate 	while (!(np->rn_flags & RC_NODE_DEAD) && (np->rn_flags & flag)) {
16447c478bd9Sstevel@tonic-gate 		(void) pthread_cond_wait(&np->rn_cv, &np->rn_lock);
16457c478bd9Sstevel@tonic-gate 	}
16467c478bd9Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_DEAD)
16477c478bd9Sstevel@tonic-gate 		return (0);
16487c478bd9Sstevel@tonic-gate 
16497c478bd9Sstevel@tonic-gate 	np->rn_flags |= flag;
16507c478bd9Sstevel@tonic-gate 	return (1);
16517c478bd9Sstevel@tonic-gate }
16527c478bd9Sstevel@tonic-gate 
16537c478bd9Sstevel@tonic-gate static void
16547c478bd9Sstevel@tonic-gate rc_node_rele_flag(rc_node_t *np, uint32_t flag)
16557c478bd9Sstevel@tonic-gate {
16567c478bd9Sstevel@tonic-gate 	assert((flag & ~RC_NODE_WAITING_FLAGS) == 0);
16577c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
16587c478bd9Sstevel@tonic-gate 	assert((np->rn_flags & flag) == flag);
16597c478bd9Sstevel@tonic-gate 	np->rn_flags &= ~flag;
16607c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&np->rn_cv);
16617c478bd9Sstevel@tonic-gate }
16627c478bd9Sstevel@tonic-gate 
16637c478bd9Sstevel@tonic-gate /*
16647c478bd9Sstevel@tonic-gate  * wait until a particular flag has cleared.  Fails if the object dies.
16657c478bd9Sstevel@tonic-gate  */
16667c478bd9Sstevel@tonic-gate static int
16677c478bd9Sstevel@tonic-gate rc_node_wait_flag(rc_node_t *np, uint32_t flag)
16687c478bd9Sstevel@tonic-gate {
16697c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
16707c478bd9Sstevel@tonic-gate 	while (!(np->rn_flags & RC_NODE_DEAD) && (np->rn_flags & flag))
16717c478bd9Sstevel@tonic-gate 		(void) pthread_cond_wait(&np->rn_cv, &np->rn_lock);
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate 	return (!(np->rn_flags & RC_NODE_DEAD));
16747c478bd9Sstevel@tonic-gate }
16757c478bd9Sstevel@tonic-gate 
16767c478bd9Sstevel@tonic-gate /*
16777c478bd9Sstevel@tonic-gate  * On entry, np's lock must be held, and this thread must be holding
16787c478bd9Sstevel@tonic-gate  * RC_NODE_USING_PARENT.  On return, both of them are released.
16797c478bd9Sstevel@tonic-gate  *
16807c478bd9Sstevel@tonic-gate  * If the return value is NULL, np either does not have a parent, or
16817c478bd9Sstevel@tonic-gate  * the parent has been marked DEAD.
16827c478bd9Sstevel@tonic-gate  *
16837c478bd9Sstevel@tonic-gate  * If the return value is non-NULL, it is the parent of np, and both
16847c478bd9Sstevel@tonic-gate  * its lock and the requested flags are held.
16857c478bd9Sstevel@tonic-gate  */
16867c478bd9Sstevel@tonic-gate static rc_node_t *
16877c478bd9Sstevel@tonic-gate rc_node_hold_parent_flag(rc_node_t *np, uint32_t flag)
16887c478bd9Sstevel@tonic-gate {
16897c478bd9Sstevel@tonic-gate 	rc_node_t *pp;
16907c478bd9Sstevel@tonic-gate 
16917c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
16927c478bd9Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_USING_PARENT);
16937c478bd9Sstevel@tonic-gate 
16947c478bd9Sstevel@tonic-gate 	if ((pp = np->rn_parent) == NULL) {
16957c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_USING_PARENT);
16967c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
16977c478bd9Sstevel@tonic-gate 		return (NULL);
16987c478bd9Sstevel@tonic-gate 	}
16997c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&pp->rn_lock);
17027c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
17037c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_USING_PARENT);
17047c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
17057c478bd9Sstevel@tonic-gate 
17067c478bd9Sstevel@tonic-gate 	if (!rc_node_hold_flag(pp, flag)) {
17077c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&pp->rn_lock);
17087c478bd9Sstevel@tonic-gate 		return (NULL);
17097c478bd9Sstevel@tonic-gate 	}
17107c478bd9Sstevel@tonic-gate 	return (pp);
17117c478bd9Sstevel@tonic-gate }
17127c478bd9Sstevel@tonic-gate 
17137c478bd9Sstevel@tonic-gate rc_node_t *
17147c478bd9Sstevel@tonic-gate rc_node_alloc(void)
17157c478bd9Sstevel@tonic-gate {
17167c478bd9Sstevel@tonic-gate 	rc_node_t *np = uu_zalloc(sizeof (*np));
17177c478bd9Sstevel@tonic-gate 
17187c478bd9Sstevel@tonic-gate 	if (np == NULL)
17197c478bd9Sstevel@tonic-gate 		return (NULL);
17207c478bd9Sstevel@tonic-gate 
17217c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_init(&np->rn_lock, NULL);
17227c478bd9Sstevel@tonic-gate 	(void) pthread_cond_init(&np->rn_cv, NULL);
17237c478bd9Sstevel@tonic-gate 
17247c478bd9Sstevel@tonic-gate 	np->rn_children = uu_list_create(rc_children_pool, np, 0);
17257c478bd9Sstevel@tonic-gate 	np->rn_pg_notify_list = uu_list_create(rc_pg_notify_pool, np, 0);
17267c478bd9Sstevel@tonic-gate 
17277c478bd9Sstevel@tonic-gate 	uu_list_node_init(np, &np->rn_sibling_node, rc_children_pool);
17287c478bd9Sstevel@tonic-gate 
17297c478bd9Sstevel@tonic-gate 	uu_list_node_init(&np->rn_notify, &np->rn_notify.rcn_list_node,
17307c478bd9Sstevel@tonic-gate 	    rc_notify_pool);
17317c478bd9Sstevel@tonic-gate 
17327c478bd9Sstevel@tonic-gate 	return (np);
17337c478bd9Sstevel@tonic-gate }
17347c478bd9Sstevel@tonic-gate 
17357c478bd9Sstevel@tonic-gate void
17367c478bd9Sstevel@tonic-gate rc_node_destroy(rc_node_t *np)
17377c478bd9Sstevel@tonic-gate {
17387c478bd9Sstevel@tonic-gate 	int i;
17397c478bd9Sstevel@tonic-gate 
17407c478bd9Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_UNREFED)
17417c478bd9Sstevel@tonic-gate 		return;				/* being handled elsewhere */
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate 	assert(np->rn_refs == 0 && np->rn_other_refs == 0);
17447c478bd9Sstevel@tonic-gate 	assert(np->rn_former == NULL);
17457c478bd9Sstevel@tonic-gate 
17467c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
17477c478bd9Sstevel@tonic-gate 		/* Release the holds from rc_iter_next(). */
17487c478bd9Sstevel@tonic-gate 		for (i = 0; i < COMPOSITION_DEPTH; ++i) {
17497c478bd9Sstevel@tonic-gate 			/* rn_cchain[i] may be NULL for empty snapshots. */
17507c478bd9Sstevel@tonic-gate 			if (np->rn_cchain[i] != NULL)
17517c478bd9Sstevel@tonic-gate 				rc_node_rele(np->rn_cchain[i]);
17527c478bd9Sstevel@tonic-gate 		}
17537c478bd9Sstevel@tonic-gate 	}
17547c478bd9Sstevel@tonic-gate 
17557c478bd9Sstevel@tonic-gate 	if (np->rn_name != NULL)
17567c478bd9Sstevel@tonic-gate 		free((void *)np->rn_name);
17577c478bd9Sstevel@tonic-gate 	np->rn_name = NULL;
17587c478bd9Sstevel@tonic-gate 	if (np->rn_type != NULL)
17597c478bd9Sstevel@tonic-gate 		free((void *)np->rn_type);
17607c478bd9Sstevel@tonic-gate 	np->rn_type = NULL;
17617c478bd9Sstevel@tonic-gate 	if (np->rn_values != NULL)
17627c478bd9Sstevel@tonic-gate 		object_free_values(np->rn_values, np->rn_valtype,
17637c478bd9Sstevel@tonic-gate 		    np->rn_values_count, np->rn_values_size);
17647c478bd9Sstevel@tonic-gate 	np->rn_values = NULL;
17655b7f77adStw21770 	rc_node_free_fmri(np);
17667c478bd9Sstevel@tonic-gate 
17677c478bd9Sstevel@tonic-gate 	if (np->rn_snaplevel != NULL)
17687c478bd9Sstevel@tonic-gate 		rc_snaplevel_rele(np->rn_snaplevel);
17697c478bd9Sstevel@tonic-gate 	np->rn_snaplevel = NULL;
17707c478bd9Sstevel@tonic-gate 
17717c478bd9Sstevel@tonic-gate 	uu_list_node_fini(np, &np->rn_sibling_node, rc_children_pool);
17727c478bd9Sstevel@tonic-gate 
17737c478bd9Sstevel@tonic-gate 	uu_list_node_fini(&np->rn_notify, &np->rn_notify.rcn_list_node,
17747c478bd9Sstevel@tonic-gate 	    rc_notify_pool);
17757c478bd9Sstevel@tonic-gate 
17767c478bd9Sstevel@tonic-gate 	assert(uu_list_first(np->rn_children) == NULL);
17777c478bd9Sstevel@tonic-gate 	uu_list_destroy(np->rn_children);
17787c478bd9Sstevel@tonic-gate 	uu_list_destroy(np->rn_pg_notify_list);
17797c478bd9Sstevel@tonic-gate 
17807c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_destroy(&np->rn_lock);
17817c478bd9Sstevel@tonic-gate 	(void) pthread_cond_destroy(&np->rn_cv);
17827c478bd9Sstevel@tonic-gate 
17837c478bd9Sstevel@tonic-gate 	uu_free(np);
17847c478bd9Sstevel@tonic-gate }
17857c478bd9Sstevel@tonic-gate 
17867c478bd9Sstevel@tonic-gate /*
17877c478bd9Sstevel@tonic-gate  * Link in a child node.
17887c478bd9Sstevel@tonic-gate  *
17897c478bd9Sstevel@tonic-gate  * Because of the lock ordering, cp has to already be in the hash table with
17907c478bd9Sstevel@tonic-gate  * its lock dropped before we get it.  To prevent anyone from noticing that
17917c478bd9Sstevel@tonic-gate  * it is parentless, the creation code sets the RC_NODE_USING_PARENT.  Once
17927c478bd9Sstevel@tonic-gate  * we've linked it in, we release the flag.
17937c478bd9Sstevel@tonic-gate  */
17947c478bd9Sstevel@tonic-gate static void
17957c478bd9Sstevel@tonic-gate rc_node_link_child(rc_node_t *np, rc_node_t *cp)
17967c478bd9Sstevel@tonic-gate {
17977c478bd9Sstevel@tonic-gate 	assert(!MUTEX_HELD(&np->rn_lock));
17987c478bd9Sstevel@tonic-gate 	assert(!MUTEX_HELD(&cp->rn_lock));
17997c478bd9Sstevel@tonic-gate 
18007c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
18017c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&cp->rn_lock);
18027c478bd9Sstevel@tonic-gate 	assert(!(cp->rn_flags & RC_NODE_IN_PARENT) &&
18037c478bd9Sstevel@tonic-gate 	    (cp->rn_flags & RC_NODE_USING_PARENT));
18047c478bd9Sstevel@tonic-gate 
18057c478bd9Sstevel@tonic-gate 	assert(rc_check_parent_child(np->rn_id.rl_type, cp->rn_id.rl_type) ==
18067c478bd9Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS);
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate 	cp->rn_parent = np;
18097c478bd9Sstevel@tonic-gate 	cp->rn_flags |= RC_NODE_IN_PARENT;
18107c478bd9Sstevel@tonic-gate 	(void) uu_list_insert_before(np->rn_children, NULL, cp);
18115b7f77adStw21770 	(void) rc_node_build_fmri(cp);
18127c478bd9Sstevel@tonic-gate 
18137c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
18147c478bd9Sstevel@tonic-gate 
18157c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(cp, RC_NODE_USING_PARENT);
18167c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&cp->rn_lock);
18177c478bd9Sstevel@tonic-gate }
18187c478bd9Sstevel@tonic-gate 
18197c478bd9Sstevel@tonic-gate /*
18207c478bd9Sstevel@tonic-gate  * Sets the rn_parent_ref field of all the children of np to pp -- always
18217c478bd9Sstevel@tonic-gate  * initially invoked as rc_node_setup_parent_ref(np, np), we then recurse.
18227c478bd9Sstevel@tonic-gate  *
18237c478bd9Sstevel@tonic-gate  * This is used when we mark a node RC_NODE_OLD, so that when the object and
18247c478bd9Sstevel@tonic-gate  * its children are no longer referenced, they will all be deleted as a unit.
18257c478bd9Sstevel@tonic-gate  */
18267c478bd9Sstevel@tonic-gate static void
18277c478bd9Sstevel@tonic-gate rc_node_setup_parent_ref(rc_node_t *np, rc_node_t *pp)
18287c478bd9Sstevel@tonic-gate {
18297c478bd9Sstevel@tonic-gate 	rc_node_t *cp;
18307c478bd9Sstevel@tonic-gate 
18317c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
18327c478bd9Sstevel@tonic-gate 
18337c478bd9Sstevel@tonic-gate 	for (cp = uu_list_first(np->rn_children); cp != NULL;
18347c478bd9Sstevel@tonic-gate 	    cp = uu_list_next(np->rn_children, cp)) {
18357c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
18367c478bd9Sstevel@tonic-gate 		if (cp->rn_flags & RC_NODE_PARENT_REF) {
18377c478bd9Sstevel@tonic-gate 			assert(cp->rn_parent_ref == pp);
18387c478bd9Sstevel@tonic-gate 		} else {
18397c478bd9Sstevel@tonic-gate 			assert(cp->rn_parent_ref == NULL);
18407c478bd9Sstevel@tonic-gate 
18417c478bd9Sstevel@tonic-gate 			cp->rn_flags |= RC_NODE_PARENT_REF;
18427c478bd9Sstevel@tonic-gate 			cp->rn_parent_ref = pp;
18437c478bd9Sstevel@tonic-gate 			if (cp->rn_refs != 0)
18447c478bd9Sstevel@tonic-gate 				rc_node_hold_other(pp);
18457c478bd9Sstevel@tonic-gate 		}
18467c478bd9Sstevel@tonic-gate 		rc_node_setup_parent_ref(cp, pp);		/* recurse */
18477c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&cp->rn_lock);
18487c478bd9Sstevel@tonic-gate 	}
18497c478bd9Sstevel@tonic-gate }
18507c478bd9Sstevel@tonic-gate 
18517c478bd9Sstevel@tonic-gate /*
18527c478bd9Sstevel@tonic-gate  * Atomically replace 'np' with 'newp', with a parent of 'pp'.
18537c478bd9Sstevel@tonic-gate  *
18547c478bd9Sstevel@tonic-gate  * Requirements:
18557c478bd9Sstevel@tonic-gate  *	*no* node locks may be held.
18567c478bd9Sstevel@tonic-gate  *	pp must be held with RC_NODE_CHILDREN_CHANGING
18577c478bd9Sstevel@tonic-gate  *	newp and np must be held with RC_NODE_IN_TX
18587c478bd9Sstevel@tonic-gate  *	np must be marked RC_NODE_IN_PARENT, newp must not be
18597c478bd9Sstevel@tonic-gate  *	np must be marked RC_NODE_OLD
18607c478bd9Sstevel@tonic-gate  *
18617c478bd9Sstevel@tonic-gate  * Afterwards:
18627c478bd9Sstevel@tonic-gate  *	pp's RC_NODE_CHILDREN_CHANGING is dropped
18637c478bd9Sstevel@tonic-gate  *	newp and np's RC_NODE_IN_TX is dropped
18647c478bd9Sstevel@tonic-gate  *	newp->rn_former = np;
18657c478bd9Sstevel@tonic-gate  *	newp is RC_NODE_IN_PARENT, np is not.
18667c478bd9Sstevel@tonic-gate  *	interested notify subscribers have been notified of newp's new status.
18677c478bd9Sstevel@tonic-gate  */
18687c478bd9Sstevel@tonic-gate static void
18697c478bd9Sstevel@tonic-gate rc_node_relink_child(rc_node_t *pp, rc_node_t *np, rc_node_t *newp)
18707c478bd9Sstevel@tonic-gate {
18717c478bd9Sstevel@tonic-gate 	cache_bucket_t *bp;
18727c478bd9Sstevel@tonic-gate 	/*
18737c478bd9Sstevel@tonic-gate 	 * First, swap np and nnp in the cache.  newp's RC_NODE_IN_TX flag
18747c478bd9Sstevel@tonic-gate 	 * keeps rc_node_update() from seeing it until we are done.
18757c478bd9Sstevel@tonic-gate 	 */
18767c478bd9Sstevel@tonic-gate 	bp = cache_hold(newp->rn_hash);
18777c478bd9Sstevel@tonic-gate 	cache_remove_unlocked(bp, np);
18787c478bd9Sstevel@tonic-gate 	cache_insert_unlocked(bp, newp);
18797c478bd9Sstevel@tonic-gate 	cache_release(bp);
18807c478bd9Sstevel@tonic-gate 
18817c478bd9Sstevel@tonic-gate 	/*
18827c478bd9Sstevel@tonic-gate 	 * replace np with newp in pp's list, and attach it to newp's rn_former
18837c478bd9Sstevel@tonic-gate 	 * link.
18847c478bd9Sstevel@tonic-gate 	 */
18857c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&pp->rn_lock);
18867c478bd9Sstevel@tonic-gate 	assert(pp->rn_flags & RC_NODE_CHILDREN_CHANGING);
18877c478bd9Sstevel@tonic-gate 
18887c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&newp->rn_lock);
18897c478bd9Sstevel@tonic-gate 	assert(!(newp->rn_flags & RC_NODE_IN_PARENT));
18907c478bd9Sstevel@tonic-gate 	assert(newp->rn_flags & RC_NODE_IN_TX);
18917c478bd9Sstevel@tonic-gate 
18927c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
18937c478bd9Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_IN_PARENT);
18947c478bd9Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_OLD);
18957c478bd9Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_IN_TX);
18967c478bd9Sstevel@tonic-gate 
18977c478bd9Sstevel@tonic-gate 	newp->rn_parent = pp;
18987c478bd9Sstevel@tonic-gate 	newp->rn_flags |= RC_NODE_IN_PARENT;
18997c478bd9Sstevel@tonic-gate 
19007c478bd9Sstevel@tonic-gate 	/*
19017c478bd9Sstevel@tonic-gate 	 * Note that we carefully add newp before removing np -- this
19027c478bd9Sstevel@tonic-gate 	 * keeps iterators on the list from missing us.
19037c478bd9Sstevel@tonic-gate 	 */
19047c478bd9Sstevel@tonic-gate 	(void) uu_list_insert_after(pp->rn_children, np, newp);
19055b7f77adStw21770 	(void) rc_node_build_fmri(newp);
19067c478bd9Sstevel@tonic-gate 	(void) uu_list_remove(pp->rn_children, np);
19077c478bd9Sstevel@tonic-gate 
19087c478bd9Sstevel@tonic-gate 	/*
19097c478bd9Sstevel@tonic-gate 	 * re-set np
19107c478bd9Sstevel@tonic-gate 	 */
19117c478bd9Sstevel@tonic-gate 	newp->rn_former = np;
19127c478bd9Sstevel@tonic-gate 	np->rn_parent = NULL;
19137c478bd9Sstevel@tonic-gate 	np->rn_flags &= ~RC_NODE_IN_PARENT;
19147c478bd9Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_ON_FORMER;
19157c478bd9Sstevel@tonic-gate 
19167c478bd9Sstevel@tonic-gate 	rc_notify_insert_node(newp);
19177c478bd9Sstevel@tonic-gate 
19187c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
19197c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&pp->rn_lock);
19207c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(newp, RC_NODE_USING_PARENT | RC_NODE_IN_TX);
19217c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&newp->rn_lock);
19227c478bd9Sstevel@tonic-gate 	rc_node_setup_parent_ref(np, np);
19237c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_IN_TX);
19247c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
19257c478bd9Sstevel@tonic-gate }
19267c478bd9Sstevel@tonic-gate 
19277c478bd9Sstevel@tonic-gate /*
19287c478bd9Sstevel@tonic-gate  * makes sure a node with lookup 'nip', name 'name', and parent 'pp' exists.
19297c478bd9Sstevel@tonic-gate  * 'cp' is used (and returned) if the node does not yet exist.  If it does
19307c478bd9Sstevel@tonic-gate  * exist, 'cp' is freed, and the existent node is returned instead.
19317c478bd9Sstevel@tonic-gate  */
19327c478bd9Sstevel@tonic-gate rc_node_t *
19337c478bd9Sstevel@tonic-gate rc_node_setup(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
19347c478bd9Sstevel@tonic-gate     rc_node_t *pp)
19357c478bd9Sstevel@tonic-gate {
19367c478bd9Sstevel@tonic-gate 	rc_node_t *np;
19377c478bd9Sstevel@tonic-gate 	cache_bucket_t *bp;
19387c478bd9Sstevel@tonic-gate 	uint32_t h = rc_node_hash(nip);
19397c478bd9Sstevel@tonic-gate 
19407c478bd9Sstevel@tonic-gate 	assert(cp->rn_refs == 0);
19417c478bd9Sstevel@tonic-gate 
19427c478bd9Sstevel@tonic-gate 	bp = cache_hold(h);
19437c478bd9Sstevel@tonic-gate 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
19447c478bd9Sstevel@tonic-gate 		cache_release(bp);
19457c478bd9Sstevel@tonic-gate 
19467c478bd9Sstevel@tonic-gate 		/*
19477c478bd9Sstevel@tonic-gate 		 * make sure it matches our expectations
19487c478bd9Sstevel@tonic-gate 		 */
1949cfae1de8Shg115875 		(void) pthread_mutex_lock(&np->rn_lock);
1950cfae1de8Shg115875 		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
19517c478bd9Sstevel@tonic-gate 			assert(np->rn_parent == pp);
19527c478bd9Sstevel@tonic-gate 			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
19537c478bd9Sstevel@tonic-gate 			assert(strcmp(np->rn_name, name) == 0);
19547c478bd9Sstevel@tonic-gate 			assert(np->rn_type == NULL);
19557c478bd9Sstevel@tonic-gate 			assert(np->rn_flags & RC_NODE_IN_PARENT);
1956cfae1de8Shg115875 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
1957cfae1de8Shg115875 		}
1958cfae1de8Shg115875 		(void) pthread_mutex_unlock(&np->rn_lock);
19597c478bd9Sstevel@tonic-gate 
19607c478bd9Sstevel@tonic-gate 		rc_node_destroy(cp);
19617c478bd9Sstevel@tonic-gate 		return (np);
19627c478bd9Sstevel@tonic-gate 	}
19637c478bd9Sstevel@tonic-gate 
19647c478bd9Sstevel@tonic-gate 	/*
19656643e1ffSbustos 	 * No one is there -- setup & install the new node.
19667c478bd9Sstevel@tonic-gate 	 */
19677c478bd9Sstevel@tonic-gate 	np = cp;
19687c478bd9Sstevel@tonic-gate 	rc_node_hold(np);
19697c478bd9Sstevel@tonic-gate 	np->rn_id = *nip;
19707c478bd9Sstevel@tonic-gate 	np->rn_hash = h;
19717c478bd9Sstevel@tonic-gate 	np->rn_name = strdup(name);
19727c478bd9Sstevel@tonic-gate 
19737c478bd9Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_USING_PARENT;
19747c478bd9Sstevel@tonic-gate 
19757c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE) {
19767c478bd9Sstevel@tonic-gate #if COMPOSITION_DEPTH == 2
19777c478bd9Sstevel@tonic-gate 		np->rn_cchain[0] = np;
19787c478bd9Sstevel@tonic-gate 		np->rn_cchain[1] = pp;
19797c478bd9Sstevel@tonic-gate #else
19807c478bd9Sstevel@tonic-gate #error This code must be updated.
19817c478bd9Sstevel@tonic-gate #endif
19827c478bd9Sstevel@tonic-gate 	}
19837c478bd9Sstevel@tonic-gate 
19847c478bd9Sstevel@tonic-gate 	cache_insert_unlocked(bp, np);
19857c478bd9Sstevel@tonic-gate 	cache_release(bp);		/* we are now visible */
19867c478bd9Sstevel@tonic-gate 
19877c478bd9Sstevel@tonic-gate 	rc_node_link_child(pp, np);
19887c478bd9Sstevel@tonic-gate 
19897c478bd9Sstevel@tonic-gate 	return (np);
19907c478bd9Sstevel@tonic-gate }
19917c478bd9Sstevel@tonic-gate 
19927c478bd9Sstevel@tonic-gate /*
19937c478bd9Sstevel@tonic-gate  * makes sure a snapshot with lookup 'nip', name 'name', and parent 'pp' exists.
19947c478bd9Sstevel@tonic-gate  * 'cp' is used (and returned) if the node does not yet exist.  If it does
19957c478bd9Sstevel@tonic-gate  * exist, 'cp' is freed, and the existent node is returned instead.
19967c478bd9Sstevel@tonic-gate  */
19977c478bd9Sstevel@tonic-gate rc_node_t *
19987c478bd9Sstevel@tonic-gate rc_node_setup_snapshot(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
19997c478bd9Sstevel@tonic-gate     uint32_t snap_id, rc_node_t *pp)
20007c478bd9Sstevel@tonic-gate {
20017c478bd9Sstevel@tonic-gate 	rc_node_t *np;
20027c478bd9Sstevel@tonic-gate 	cache_bucket_t *bp;
20037c478bd9Sstevel@tonic-gate 	uint32_t h = rc_node_hash(nip);
20047c478bd9Sstevel@tonic-gate 
20057c478bd9Sstevel@tonic-gate 	assert(cp->rn_refs == 0);
20067c478bd9Sstevel@tonic-gate 
20077c478bd9Sstevel@tonic-gate 	bp = cache_hold(h);
20087c478bd9Sstevel@tonic-gate 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
20097c478bd9Sstevel@tonic-gate 		cache_release(bp);
20107c478bd9Sstevel@tonic-gate 
20117c478bd9Sstevel@tonic-gate 		/*
20127c478bd9Sstevel@tonic-gate 		 * make sure it matches our expectations
20137c478bd9Sstevel@tonic-gate 		 */
2014cfae1de8Shg115875 		(void) pthread_mutex_lock(&np->rn_lock);
2015cfae1de8Shg115875 		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
20167c478bd9Sstevel@tonic-gate 			assert(np->rn_parent == pp);
20177c478bd9Sstevel@tonic-gate 			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
20187c478bd9Sstevel@tonic-gate 			assert(strcmp(np->rn_name, name) == 0);
20197c478bd9Sstevel@tonic-gate 			assert(np->rn_type == NULL);
20207c478bd9Sstevel@tonic-gate 			assert(np->rn_flags & RC_NODE_IN_PARENT);
2021cfae1de8Shg115875 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
2022cfae1de8Shg115875 		}
2023cfae1de8Shg115875 		(void) pthread_mutex_unlock(&np->rn_lock);
20247c478bd9Sstevel@tonic-gate 
20257c478bd9Sstevel@tonic-gate 		rc_node_destroy(cp);
20267c478bd9Sstevel@tonic-gate 		return (np);
20277c478bd9Sstevel@tonic-gate 	}
20287c478bd9Sstevel@tonic-gate 
20297c478bd9Sstevel@tonic-gate 	/*
20307c478bd9Sstevel@tonic-gate 	 * No one is there -- create a new node.
20317c478bd9Sstevel@tonic-gate 	 */
20327c478bd9Sstevel@tonic-gate 	np = cp;
20337c478bd9Sstevel@tonic-gate 	rc_node_hold(np);
20347c478bd9Sstevel@tonic-gate 	np->rn_id = *nip;
20357c478bd9Sstevel@tonic-gate 	np->rn_hash = h;
20367c478bd9Sstevel@tonic-gate 	np->rn_name = strdup(name);
20377c478bd9Sstevel@tonic-gate 	np->rn_snapshot_id = snap_id;
20387c478bd9Sstevel@tonic-gate 
20397c478bd9Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_USING_PARENT;
20407c478bd9Sstevel@tonic-gate 
20417c478bd9Sstevel@tonic-gate 	cache_insert_unlocked(bp, np);
20427c478bd9Sstevel@tonic-gate 	cache_release(bp);		/* we are now visible */
20437c478bd9Sstevel@tonic-gate 
20447c478bd9Sstevel@tonic-gate 	rc_node_link_child(pp, np);
20457c478bd9Sstevel@tonic-gate 
20467c478bd9Sstevel@tonic-gate 	return (np);
20477c478bd9Sstevel@tonic-gate }
20487c478bd9Sstevel@tonic-gate 
20497c478bd9Sstevel@tonic-gate /*
20507c478bd9Sstevel@tonic-gate  * makes sure a snaplevel with lookup 'nip' and parent 'pp' exists.  'cp' is
20517c478bd9Sstevel@tonic-gate  * used (and returned) if the node does not yet exist.  If it does exist, 'cp'
20527c478bd9Sstevel@tonic-gate  * is freed, and the existent node is returned instead.
20537c478bd9Sstevel@tonic-gate  */
20547c478bd9Sstevel@tonic-gate rc_node_t *
20557c478bd9Sstevel@tonic-gate rc_node_setup_snaplevel(rc_node_t *cp, rc_node_lookup_t *nip,
20567c478bd9Sstevel@tonic-gate     rc_snaplevel_t *lvl, rc_node_t *pp)
20577c478bd9Sstevel@tonic-gate {
20587c478bd9Sstevel@tonic-gate 	rc_node_t *np;
20597c478bd9Sstevel@tonic-gate 	cache_bucket_t *bp;
20607c478bd9Sstevel@tonic-gate 	uint32_t h = rc_node_hash(nip);
20617c478bd9Sstevel@tonic-gate 
20627c478bd9Sstevel@tonic-gate 	assert(cp->rn_refs == 0);
20637c478bd9Sstevel@tonic-gate 
20647c478bd9Sstevel@tonic-gate 	bp = cache_hold(h);
20657c478bd9Sstevel@tonic-gate 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
20667c478bd9Sstevel@tonic-gate 		cache_release(bp);
20677c478bd9Sstevel@tonic-gate 
20687c478bd9Sstevel@tonic-gate 		/*
20697c478bd9Sstevel@tonic-gate 		 * make sure it matches our expectations
20707c478bd9Sstevel@tonic-gate 		 */
2071cfae1de8Shg115875 		(void) pthread_mutex_lock(&np->rn_lock);
2072cfae1de8Shg115875 		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
20737c478bd9Sstevel@tonic-gate 			assert(np->rn_parent == pp);
20747c478bd9Sstevel@tonic-gate 			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
20757c478bd9Sstevel@tonic-gate 			assert(np->rn_name == NULL);
20767c478bd9Sstevel@tonic-gate 			assert(np->rn_type == NULL);
20777c478bd9Sstevel@tonic-gate 			assert(np->rn_flags & RC_NODE_IN_PARENT);
2078cfae1de8Shg115875 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
2079cfae1de8Shg115875 		}
2080cfae1de8Shg115875 		(void) pthread_mutex_unlock(&np->rn_lock);
20817c478bd9Sstevel@tonic-gate 
20827c478bd9Sstevel@tonic-gate 		rc_node_destroy(cp);
20837c478bd9Sstevel@tonic-gate 		return (np);
20847c478bd9Sstevel@tonic-gate 	}
20857c478bd9Sstevel@tonic-gate 
20867c478bd9Sstevel@tonic-gate 	/*
20877c478bd9Sstevel@tonic-gate 	 * No one is there -- create a new node.
20887c478bd9Sstevel@tonic-gate 	 */
20897c478bd9Sstevel@tonic-gate 	np = cp;
20907c478bd9Sstevel@tonic-gate 	rc_node_hold(np);	/* released in snapshot_fill_children() */
20917c478bd9Sstevel@tonic-gate 	np->rn_id = *nip;
20927c478bd9Sstevel@tonic-gate 	np->rn_hash = h;
20937c478bd9Sstevel@tonic-gate 
20947c478bd9Sstevel@tonic-gate 	rc_snaplevel_hold(lvl);
20957c478bd9Sstevel@tonic-gate 	np->rn_snaplevel = lvl;
20967c478bd9Sstevel@tonic-gate 
20977c478bd9Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_USING_PARENT;
20987c478bd9Sstevel@tonic-gate 
20997c478bd9Sstevel@tonic-gate 	cache_insert_unlocked(bp, np);
21007c478bd9Sstevel@tonic-gate 	cache_release(bp);		/* we are now visible */
21017c478bd9Sstevel@tonic-gate 
21027c478bd9Sstevel@tonic-gate 	/* Add this snaplevel to the snapshot's composition chain. */
21037c478bd9Sstevel@tonic-gate 	assert(pp->rn_cchain[lvl->rsl_level_num - 1] == NULL);
21047c478bd9Sstevel@tonic-gate 	pp->rn_cchain[lvl->rsl_level_num - 1] = np;
21057c478bd9Sstevel@tonic-gate 
21067c478bd9Sstevel@tonic-gate 	rc_node_link_child(pp, np);
21077c478bd9Sstevel@tonic-gate 
21087c478bd9Sstevel@tonic-gate 	return (np);
21097c478bd9Sstevel@tonic-gate }
21107c478bd9Sstevel@tonic-gate 
21117c478bd9Sstevel@tonic-gate /*
21127c478bd9Sstevel@tonic-gate  * Returns NULL if strdup() fails.
21137c478bd9Sstevel@tonic-gate  */
21147c478bd9Sstevel@tonic-gate rc_node_t *
21157c478bd9Sstevel@tonic-gate rc_node_setup_pg(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
21167c478bd9Sstevel@tonic-gate     const char *type, uint32_t flags, uint32_t gen_id, rc_node_t *pp)
21177c478bd9Sstevel@tonic-gate {
21187c478bd9Sstevel@tonic-gate 	rc_node_t *np;
21197c478bd9Sstevel@tonic-gate 	cache_bucket_t *bp;
21207c478bd9Sstevel@tonic-gate 
21217c478bd9Sstevel@tonic-gate 	uint32_t h = rc_node_hash(nip);
21227c478bd9Sstevel@tonic-gate 	bp = cache_hold(h);
21237c478bd9Sstevel@tonic-gate 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
21247c478bd9Sstevel@tonic-gate 		cache_release(bp);
21257c478bd9Sstevel@tonic-gate 
21267c478bd9Sstevel@tonic-gate 		/*
21277c478bd9Sstevel@tonic-gate 		 * make sure it matches our expectations (don't check
21287c478bd9Sstevel@tonic-gate 		 * the generation number or parent, since someone could
21297c478bd9Sstevel@tonic-gate 		 * have gotten a transaction through while we weren't
21307c478bd9Sstevel@tonic-gate 		 * looking)
21317c478bd9Sstevel@tonic-gate 		 */
2132cfae1de8Shg115875 		(void) pthread_mutex_lock(&np->rn_lock);
2133cfae1de8Shg115875 		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
21347c478bd9Sstevel@tonic-gate 			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
21357c478bd9Sstevel@tonic-gate 			assert(strcmp(np->rn_name, name) == 0);
21367c478bd9Sstevel@tonic-gate 			assert(strcmp(np->rn_type, type) == 0);
21377c478bd9Sstevel@tonic-gate 			assert(np->rn_pgflags == flags);
21387c478bd9Sstevel@tonic-gate 			assert(np->rn_flags & RC_NODE_IN_PARENT);
2139cfae1de8Shg115875 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
2140cfae1de8Shg115875 		}
2141cfae1de8Shg115875 		(void) pthread_mutex_unlock(&np->rn_lock);
21427c478bd9Sstevel@tonic-gate 
21437c478bd9Sstevel@tonic-gate 		rc_node_destroy(cp);
21447c478bd9Sstevel@tonic-gate 		return (np);
21457c478bd9Sstevel@tonic-gate 	}
21467c478bd9Sstevel@tonic-gate 
21477c478bd9Sstevel@tonic-gate 	np = cp;
21487c478bd9Sstevel@tonic-gate 	rc_node_hold(np);		/* released in fill_pg_callback() */
21497c478bd9Sstevel@tonic-gate 	np->rn_id = *nip;
21507c478bd9Sstevel@tonic-gate 	np->rn_hash = h;
21517c478bd9Sstevel@tonic-gate 	np->rn_name = strdup(name);
21527c478bd9Sstevel@tonic-gate 	if (np->rn_name == NULL) {
21537c478bd9Sstevel@tonic-gate 		rc_node_rele(np);
21547c478bd9Sstevel@tonic-gate 		return (NULL);
21557c478bd9Sstevel@tonic-gate 	}
21567c478bd9Sstevel@tonic-gate 	np->rn_type = strdup(type);
21577c478bd9Sstevel@tonic-gate 	if (np->rn_type == NULL) {
21587c478bd9Sstevel@tonic-gate 		free((void *)np->rn_name);
21597c478bd9Sstevel@tonic-gate 		rc_node_rele(np);
21607c478bd9Sstevel@tonic-gate 		return (NULL);
21617c478bd9Sstevel@tonic-gate 	}
21627c478bd9Sstevel@tonic-gate 	np->rn_pgflags = flags;
21637c478bd9Sstevel@tonic-gate 	np->rn_gen_id = gen_id;
21647c478bd9Sstevel@tonic-gate 
21657c478bd9Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_USING_PARENT;
21667c478bd9Sstevel@tonic-gate 
21677c478bd9Sstevel@tonic-gate 	cache_insert_unlocked(bp, np);
21687c478bd9Sstevel@tonic-gate 	cache_release(bp);		/* we are now visible */
21697c478bd9Sstevel@tonic-gate 
21707c478bd9Sstevel@tonic-gate 	rc_node_link_child(pp, np);
21717c478bd9Sstevel@tonic-gate 
21727c478bd9Sstevel@tonic-gate 	return (np);
21737c478bd9Sstevel@tonic-gate }
21747c478bd9Sstevel@tonic-gate 
21757c478bd9Sstevel@tonic-gate #if COMPOSITION_DEPTH == 2
21767c478bd9Sstevel@tonic-gate /*
21777c478bd9Sstevel@tonic-gate  * Initialize a "composed property group" which represents the composition of
21787c478bd9Sstevel@tonic-gate  * property groups pg1 & pg2.  It is ephemeral: once created & returned for an
21797c478bd9Sstevel@tonic-gate  * ITER_READ request, keeping it out of cache_hash and any child lists
21807c478bd9Sstevel@tonic-gate  * prevents it from being looked up.  Operations besides iteration are passed
21817c478bd9Sstevel@tonic-gate  * through to pg1.
21827c478bd9Sstevel@tonic-gate  *
21837c478bd9Sstevel@tonic-gate  * pg1 & pg2 should be held before entering this function.  They will be
21847c478bd9Sstevel@tonic-gate  * released in rc_node_destroy().
21857c478bd9Sstevel@tonic-gate  */
21867c478bd9Sstevel@tonic-gate static int
21877c478bd9Sstevel@tonic-gate rc_node_setup_cpg(rc_node_t *cpg, rc_node_t *pg1, rc_node_t *pg2)
21887c478bd9Sstevel@tonic-gate {
21897c478bd9Sstevel@tonic-gate 	if (strcmp(pg1->rn_type, pg2->rn_type) != 0)
21907c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
21917c478bd9Sstevel@tonic-gate 
21927c478bd9Sstevel@tonic-gate 	cpg->rn_id.rl_type = REP_PROTOCOL_ENTITY_CPROPERTYGRP;
21937c478bd9Sstevel@tonic-gate 	cpg->rn_name = strdup(pg1->rn_name);
21947c478bd9Sstevel@tonic-gate 	if (cpg->rn_name == NULL)
21957c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
21967c478bd9Sstevel@tonic-gate 
21977c478bd9Sstevel@tonic-gate 	cpg->rn_cchain[0] = pg1;
21987c478bd9Sstevel@tonic-gate 	cpg->rn_cchain[1] = pg2;
21997c478bd9Sstevel@tonic-gate 
22007c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
22017c478bd9Sstevel@tonic-gate }
22027c478bd9Sstevel@tonic-gate #else
22037c478bd9Sstevel@tonic-gate #error This code must be updated.
22047c478bd9Sstevel@tonic-gate #endif
22057c478bd9Sstevel@tonic-gate 
22067c478bd9Sstevel@tonic-gate /*
22077c478bd9Sstevel@tonic-gate  * Fails with _NO_RESOURCES.
22087c478bd9Sstevel@tonic-gate  */
22097c478bd9Sstevel@tonic-gate int
22107c478bd9Sstevel@tonic-gate rc_node_create_property(rc_node_t *pp, rc_node_lookup_t *nip,
22117c478bd9Sstevel@tonic-gate     const char *name, rep_protocol_value_type_t type,
22127c478bd9Sstevel@tonic-gate     const char *vals, size_t count, size_t size)
22137c478bd9Sstevel@tonic-gate {
22147c478bd9Sstevel@tonic-gate 	rc_node_t *np;
22157c478bd9Sstevel@tonic-gate 	cache_bucket_t *bp;
22167c478bd9Sstevel@tonic-gate 
22177c478bd9Sstevel@tonic-gate 	uint32_t h = rc_node_hash(nip);
22187c478bd9Sstevel@tonic-gate 	bp = cache_hold(h);
22197c478bd9Sstevel@tonic-gate 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
22207c478bd9Sstevel@tonic-gate 		cache_release(bp);
22217c478bd9Sstevel@tonic-gate 		/*
22227c478bd9Sstevel@tonic-gate 		 * make sure it matches our expectations
22237c478bd9Sstevel@tonic-gate 		 */
22247c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
22257c478bd9Sstevel@tonic-gate 		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
22267c478bd9Sstevel@tonic-gate 			assert(np->rn_parent == pp);
22277c478bd9Sstevel@tonic-gate 			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
22287c478bd9Sstevel@tonic-gate 			assert(strcmp(np->rn_name, name) == 0);
22297c478bd9Sstevel@tonic-gate 			assert(np->rn_valtype == type);
22307c478bd9Sstevel@tonic-gate 			assert(np->rn_values_count == count);
22317c478bd9Sstevel@tonic-gate 			assert(np->rn_values_size == size);
22327c478bd9Sstevel@tonic-gate 			assert(vals == NULL ||
22337c478bd9Sstevel@tonic-gate 			    memcmp(np->rn_values, vals, size) == 0);
22347c478bd9Sstevel@tonic-gate 			assert(np->rn_flags & RC_NODE_IN_PARENT);
22357c478bd9Sstevel@tonic-gate 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
22367c478bd9Sstevel@tonic-gate 		}
22377c478bd9Sstevel@tonic-gate 		rc_node_rele_locked(np);
22387c478bd9Sstevel@tonic-gate 		object_free_values(vals, type, count, size);
22397c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_SUCCESS);
22407c478bd9Sstevel@tonic-gate 	}
22417c478bd9Sstevel@tonic-gate 
22427c478bd9Sstevel@tonic-gate 	/*
22437c478bd9Sstevel@tonic-gate 	 * No one is there -- create a new node.
22447c478bd9Sstevel@tonic-gate 	 */
22457c478bd9Sstevel@tonic-gate 	np = rc_node_alloc();
22467c478bd9Sstevel@tonic-gate 	if (np == NULL) {
22477c478bd9Sstevel@tonic-gate 		cache_release(bp);
22487c478bd9Sstevel@tonic-gate 		object_free_values(vals, type, count, size);
22497c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
22507c478bd9Sstevel@tonic-gate 	}
22517c478bd9Sstevel@tonic-gate 	np->rn_id = *nip;
22527c478bd9Sstevel@tonic-gate 	np->rn_hash = h;
22537c478bd9Sstevel@tonic-gate 	np->rn_name = strdup(name);
22547c478bd9Sstevel@tonic-gate 	if (np->rn_name == NULL) {
22557c478bd9Sstevel@tonic-gate 		cache_release(bp);
22567c478bd9Sstevel@tonic-gate 		object_free_values(vals, type, count, size);
22577c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
22587c478bd9Sstevel@tonic-gate 	}
22597c478bd9Sstevel@tonic-gate 
22607c478bd9Sstevel@tonic-gate 	np->rn_valtype = type;
22617c478bd9Sstevel@tonic-gate 	np->rn_values = vals;
22627c478bd9Sstevel@tonic-gate 	np->rn_values_count = count;
22637c478bd9Sstevel@tonic-gate 	np->rn_values_size = size;
22647c478bd9Sstevel@tonic-gate 
22657c478bd9Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_USING_PARENT;
22667c478bd9Sstevel@tonic-gate 
22677c478bd9Sstevel@tonic-gate 	cache_insert_unlocked(bp, np);
22687c478bd9Sstevel@tonic-gate 	cache_release(bp);		/* we are now visible */
22697c478bd9Sstevel@tonic-gate 
22707c478bd9Sstevel@tonic-gate 	rc_node_link_child(pp, np);
22717c478bd9Sstevel@tonic-gate 
22727c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
22737c478bd9Sstevel@tonic-gate }
22747c478bd9Sstevel@tonic-gate 
22755b7f77adStw21770 /*
22765b7f77adStw21770  * This function implements a decision table to determine the event ID for
22775b7f77adStw21770  * changes to the enabled (SCF_PROPERTY_ENABLED) property.  The event ID is
22785b7f77adStw21770  * determined by the value of the first property in the command specified
22795b7f77adStw21770  * by cmd_no and the name of the property group.  Here is the decision
22805b7f77adStw21770  * table:
22815b7f77adStw21770  *
22825b7f77adStw21770  *				Property Group Name
22835b7f77adStw21770  *	Property	------------------------------------------
22845b7f77adStw21770  *	Value		SCF_PG_GENERAL		SCF_PG_GENERAL_OVR
22855b7f77adStw21770  *	--------	--------------		------------------
22865b7f77adStw21770  *	"0"		ADT_smf_disable		ADT_smf_tmp_disable
22875b7f77adStw21770  *	"1"		ADT_smf_enable		ADT_smf_tmp_enable
22885b7f77adStw21770  *
22895b7f77adStw21770  * This function is called by special_property_event through a function
22905b7f77adStw21770  * pointer in the special_props_list array.
22915b7f77adStw21770  *
22925b7f77adStw21770  * Since the ADT_smf_* symbols may not be defined in the build machine's
22935b7f77adStw21770  * include files, this function is not compiled when doing native builds.
22945b7f77adStw21770  */
22955b7f77adStw21770 #ifndef NATIVE_BUILD
22965b7f77adStw21770 static int
22975b7f77adStw21770 general_enable_id(tx_commit_data_t *tx_data, size_t cmd_no, const char *pg,
22985b7f77adStw21770     au_event_t *event_id)
22995b7f77adStw21770 {
23005b7f77adStw21770 	const char *value;
23015b7f77adStw21770 	uint32_t nvalues;
23025b7f77adStw21770 	int enable;
23035b7f77adStw21770 
23045b7f77adStw21770 	/*
23055b7f77adStw21770 	 * First, check property value.
23065b7f77adStw21770 	 */
23075b7f77adStw21770 	if (tx_cmd_nvalues(tx_data, cmd_no, &nvalues) != REP_PROTOCOL_SUCCESS)
23085b7f77adStw21770 		return (-1);
23095b7f77adStw21770 	if (nvalues == 0)
23105b7f77adStw21770 		return (-1);
23115b7f77adStw21770 	if (tx_cmd_value(tx_data, cmd_no, 0, &value) != REP_PROTOCOL_SUCCESS)
23125b7f77adStw21770 		return (-1);
23135b7f77adStw21770 	if (strcmp(value, "0") == 0) {
23145b7f77adStw21770 		enable = 0;
23155b7f77adStw21770 	} else if (strcmp(value, "1") == 0) {
23165b7f77adStw21770 		enable = 1;
23175b7f77adStw21770 	} else {
23185b7f77adStw21770 		return (-1);
23195b7f77adStw21770 	}
23205b7f77adStw21770 
23215b7f77adStw21770 	/*
23225b7f77adStw21770 	 * Now check property group name.
23235b7f77adStw21770 	 */
23245b7f77adStw21770 	if (strcmp(pg, SCF_PG_GENERAL) == 0) {
23255b7f77adStw21770 		*event_id = enable ? ADT_smf_enable : ADT_smf_disable;
23265b7f77adStw21770 		return (0);
23275b7f77adStw21770 	} else if (strcmp(pg, SCF_PG_GENERAL_OVR) == 0) {
23285b7f77adStw21770 		*event_id = enable ? ADT_smf_tmp_enable : ADT_smf_tmp_disable;
23295b7f77adStw21770 		return (0);
23305b7f77adStw21770 	}
23315b7f77adStw21770 	return (-1);
23325b7f77adStw21770 }
23335b7f77adStw21770 #endif	/* NATIVE_BUILD */
23345b7f77adStw21770 
23355b7f77adStw21770 /*
23365b7f77adStw21770  * This function compares two audit_special_prop_item_t structures
23375b7f77adStw21770  * represented by item1 and item2.  It returns an integer greater than 0 if
23385b7f77adStw21770  * item1 is greater than item2.  It returns 0 if they are equal and an
23395b7f77adStw21770  * integer less than 0 if item1 is less than item2.  api_prop_name and
23405b7f77adStw21770  * api_pg_name are the key fields for sorting.
23415b7f77adStw21770  *
23425b7f77adStw21770  * This function is suitable for calls to bsearch(3C) and qsort(3C).
23435b7f77adStw21770  */
23445b7f77adStw21770 static int
23455b7f77adStw21770 special_prop_compare(const void *item1, const void *item2)
23465b7f77adStw21770 {
23475b7f77adStw21770 	const audit_special_prop_item_t *a = (audit_special_prop_item_t *)item1;
23485b7f77adStw21770 	const audit_special_prop_item_t *b = (audit_special_prop_item_t *)item2;
23495b7f77adStw21770 	int r;
23505b7f77adStw21770 
23515b7f77adStw21770 	r = strcmp(a->api_prop_name, b->api_prop_name);
23525b7f77adStw21770 	if (r == 0) {
23535b7f77adStw21770 		/*
23545b7f77adStw21770 		 * Primary keys are the same, so check the secondary key.
23555b7f77adStw21770 		 */
23565b7f77adStw21770 		r = strcmp(a->api_pg_name, b->api_pg_name);
23575b7f77adStw21770 	}
23585b7f77adStw21770 	return (r);
23595b7f77adStw21770 }
23605b7f77adStw21770 
23617c478bd9Sstevel@tonic-gate int
23627c478bd9Sstevel@tonic-gate rc_node_init(void)
23637c478bd9Sstevel@tonic-gate {
23647c478bd9Sstevel@tonic-gate 	rc_node_t *np;
23657c478bd9Sstevel@tonic-gate 	cache_bucket_t *bp;
23667c478bd9Sstevel@tonic-gate 
23677c478bd9Sstevel@tonic-gate 	rc_children_pool = uu_list_pool_create("rc_children_pool",
23687c478bd9Sstevel@tonic-gate 	    sizeof (rc_node_t), offsetof(rc_node_t, rn_sibling_node),
23697c478bd9Sstevel@tonic-gate 	    NULL, UU_LIST_POOL_DEBUG);
23707c478bd9Sstevel@tonic-gate 
23717c478bd9Sstevel@tonic-gate 	rc_pg_notify_pool = uu_list_pool_create("rc_pg_notify_pool",
23727c478bd9Sstevel@tonic-gate 	    sizeof (rc_node_pg_notify_t),
23737c478bd9Sstevel@tonic-gate 	    offsetof(rc_node_pg_notify_t, rnpn_node),
23747c478bd9Sstevel@tonic-gate 	    NULL, UU_LIST_POOL_DEBUG);
23757c478bd9Sstevel@tonic-gate 
23767c478bd9Sstevel@tonic-gate 	rc_notify_pool = uu_list_pool_create("rc_notify_pool",
23777c478bd9Sstevel@tonic-gate 	    sizeof (rc_notify_t), offsetof(rc_notify_t, rcn_list_node),
23787c478bd9Sstevel@tonic-gate 	    NULL, UU_LIST_POOL_DEBUG);
23797c478bd9Sstevel@tonic-gate 
23807c478bd9Sstevel@tonic-gate 	rc_notify_info_pool = uu_list_pool_create("rc_notify_info_pool",
23817c478bd9Sstevel@tonic-gate 	    sizeof (rc_notify_info_t),
23827c478bd9Sstevel@tonic-gate 	    offsetof(rc_notify_info_t, rni_list_node),
23837c478bd9Sstevel@tonic-gate 	    NULL, UU_LIST_POOL_DEBUG);
23847c478bd9Sstevel@tonic-gate 
23857c478bd9Sstevel@tonic-gate 	if (rc_children_pool == NULL || rc_pg_notify_pool == NULL ||
23867c478bd9Sstevel@tonic-gate 	    rc_notify_pool == NULL || rc_notify_info_pool == NULL)
23877c478bd9Sstevel@tonic-gate 		uu_die("out of memory");
23887c478bd9Sstevel@tonic-gate 
23897c478bd9Sstevel@tonic-gate 	rc_notify_list = uu_list_create(rc_notify_pool,
23907c478bd9Sstevel@tonic-gate 	    &rc_notify_list, 0);
23917c478bd9Sstevel@tonic-gate 
23927c478bd9Sstevel@tonic-gate 	rc_notify_info_list = uu_list_create(rc_notify_info_pool,
23937c478bd9Sstevel@tonic-gate 	    &rc_notify_info_list, 0);
23947c478bd9Sstevel@tonic-gate 
23957c478bd9Sstevel@tonic-gate 	if (rc_notify_list == NULL || rc_notify_info_list == NULL)
23967c478bd9Sstevel@tonic-gate 		uu_die("out of memory");
23977c478bd9Sstevel@tonic-gate 
23985b7f77adStw21770 	/*
23995b7f77adStw21770 	 * Sort the special_props_list array so that it can be searched
24005b7f77adStw21770 	 * with bsearch(3C).
24015b7f77adStw21770 	 *
24025b7f77adStw21770 	 * The special_props_list array is not compiled into the native
24035b7f77adStw21770 	 * build code, so there is no need to call qsort if NATIVE_BUILD is
24045b7f77adStw21770 	 * defined.
24055b7f77adStw21770 	 */
24065b7f77adStw21770 #ifndef	NATIVE_BUILD
24075b7f77adStw21770 	qsort(special_props_list, SPECIAL_PROP_COUNT,
24085b7f77adStw21770 	    sizeof (special_props_list[0]), special_prop_compare);
24095b7f77adStw21770 #endif	/* NATIVE_BUILD */
24105b7f77adStw21770 
24117c478bd9Sstevel@tonic-gate 	if ((np = rc_node_alloc()) == NULL)
24127c478bd9Sstevel@tonic-gate 		uu_die("out of memory");
24137c478bd9Sstevel@tonic-gate 
24147c478bd9Sstevel@tonic-gate 	rc_node_hold(np);
24157c478bd9Sstevel@tonic-gate 	np->rn_id.rl_type = REP_PROTOCOL_ENTITY_SCOPE;
24167c478bd9Sstevel@tonic-gate 	np->rn_id.rl_backend = BACKEND_TYPE_NORMAL;
24177c478bd9Sstevel@tonic-gate 	np->rn_hash = rc_node_hash(&np->rn_id);
24187c478bd9Sstevel@tonic-gate 	np->rn_name = "localhost";
24197c478bd9Sstevel@tonic-gate 
24207c478bd9Sstevel@tonic-gate 	bp = cache_hold(np->rn_hash);
24217c478bd9Sstevel@tonic-gate 	cache_insert_unlocked(bp, np);
24227c478bd9Sstevel@tonic-gate 	cache_release(bp);
24237c478bd9Sstevel@tonic-gate 
24247c478bd9Sstevel@tonic-gate 	rc_scope = np;
24257c478bd9Sstevel@tonic-gate 	return (1);
24267c478bd9Sstevel@tonic-gate }
24277c478bd9Sstevel@tonic-gate 
24287c478bd9Sstevel@tonic-gate /*
24297c478bd9Sstevel@tonic-gate  * Fails with
24307c478bd9Sstevel@tonic-gate  *   _INVALID_TYPE - type is invalid
24317c478bd9Sstevel@tonic-gate  *   _TYPE_MISMATCH - np doesn't carry children of type type
24327c478bd9Sstevel@tonic-gate  *   _DELETED - np has been deleted
24337c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES
24347c478bd9Sstevel@tonic-gate  */
24357c478bd9Sstevel@tonic-gate static int
24367c478bd9Sstevel@tonic-gate rc_node_fill_children(rc_node_t *np, uint32_t type)
24377c478bd9Sstevel@tonic-gate {
24387c478bd9Sstevel@tonic-gate 	int rc;
24397c478bd9Sstevel@tonic-gate 
24407c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
24417c478bd9Sstevel@tonic-gate 
24427c478bd9Sstevel@tonic-gate 	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
24437c478bd9Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS)
24447c478bd9Sstevel@tonic-gate 		return (rc);
24457c478bd9Sstevel@tonic-gate 
24467c478bd9Sstevel@tonic-gate 	if (!rc_node_hold_flag(np, RC_NODE_CHILDREN_CHANGING))
24477c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_DELETED);
24487c478bd9Sstevel@tonic-gate 
24497c478bd9Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_HAS_CHILDREN) {
24507c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_CHILDREN_CHANGING);
24517c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_SUCCESS);
24527c478bd9Sstevel@tonic-gate 	}
24537c478bd9Sstevel@tonic-gate 
24547c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
24557c478bd9Sstevel@tonic-gate 	rc = object_fill_children(np);
24567c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
24577c478bd9Sstevel@tonic-gate 
24587c478bd9Sstevel@tonic-gate 	if (rc == REP_PROTOCOL_SUCCESS) {
24597c478bd9Sstevel@tonic-gate 		np->rn_flags |= RC_NODE_HAS_CHILDREN;
24607c478bd9Sstevel@tonic-gate 	}
24617c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_CHILDREN_CHANGING);
24627c478bd9Sstevel@tonic-gate 
24637c478bd9Sstevel@tonic-gate 	return (rc);
24647c478bd9Sstevel@tonic-gate }
24657c478bd9Sstevel@tonic-gate 
24667c478bd9Sstevel@tonic-gate /*
24677c478bd9Sstevel@tonic-gate  * Returns
24687c478bd9Sstevel@tonic-gate  *   _INVALID_TYPE - type is invalid
24697c478bd9Sstevel@tonic-gate  *   _TYPE_MISMATCH - np doesn't carry children of type type
24707c478bd9Sstevel@tonic-gate  *   _DELETED - np has been deleted
24717c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES
24727c478bd9Sstevel@tonic-gate  *   _SUCCESS - if *cpp is not NULL, it is held
24737c478bd9Sstevel@tonic-gate  */
24747c478bd9Sstevel@tonic-gate static int
24757c478bd9Sstevel@tonic-gate rc_node_find_named_child(rc_node_t *np, const char *name, uint32_t type,
24767c478bd9Sstevel@tonic-gate     rc_node_t **cpp)
24777c478bd9Sstevel@tonic-gate {
24787c478bd9Sstevel@tonic-gate 	int ret;
24797c478bd9Sstevel@tonic-gate 	rc_node_t *cp;
24807c478bd9Sstevel@tonic-gate 
24817c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
24827c478bd9Sstevel@tonic-gate 	assert(np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP);
24837c478bd9Sstevel@tonic-gate 
24847c478bd9Sstevel@tonic-gate 	ret = rc_node_fill_children(np, type);
24857c478bd9Sstevel@tonic-gate 	if (ret != REP_PROTOCOL_SUCCESS)
24867c478bd9Sstevel@tonic-gate 		return (ret);
24877c478bd9Sstevel@tonic-gate 
24887c478bd9Sstevel@tonic-gate 	for (cp = uu_list_first(np->rn_children);
24897c478bd9Sstevel@tonic-gate 	    cp != NULL;
24907c478bd9Sstevel@tonic-gate 	    cp = uu_list_next(np->rn_children, cp)) {
24917c478bd9Sstevel@tonic-gate 		if (cp->rn_id.rl_type == type && strcmp(cp->rn_name, name) == 0)
24927c478bd9Sstevel@tonic-gate 			break;
24937c478bd9Sstevel@tonic-gate 	}
24947c478bd9Sstevel@tonic-gate 
24957c478bd9Sstevel@tonic-gate 	if (cp != NULL)
24967c478bd9Sstevel@tonic-gate 		rc_node_hold(cp);
24977c478bd9Sstevel@tonic-gate 	*cpp = cp;
24987c478bd9Sstevel@tonic-gate 
24997c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
25007c478bd9Sstevel@tonic-gate }
25017c478bd9Sstevel@tonic-gate 
25027c478bd9Sstevel@tonic-gate static int rc_node_parent(rc_node_t *, rc_node_t **);
25037c478bd9Sstevel@tonic-gate 
25047c478bd9Sstevel@tonic-gate /*
25053eae19d9Swesolows  * Returns
25063eae19d9Swesolows  *   _INVALID_TYPE - type is invalid
25073eae19d9Swesolows  *   _DELETED - np or an ancestor has been deleted
25083eae19d9Swesolows  *   _NOT_FOUND - no ancestor of specified type exists
25093eae19d9Swesolows  *   _SUCCESS - *app is held
25103eae19d9Swesolows  */
25113eae19d9Swesolows static int
25123eae19d9Swesolows rc_node_find_ancestor(rc_node_t *np, uint32_t type, rc_node_t **app)
25133eae19d9Swesolows {
25143eae19d9Swesolows 	int ret;
25153eae19d9Swesolows 	rc_node_t *parent, *np_orig;
25163eae19d9Swesolows 
25173eae19d9Swesolows 	if (type >= REP_PROTOCOL_ENTITY_MAX)
25183eae19d9Swesolows 		return (REP_PROTOCOL_FAIL_INVALID_TYPE);
25193eae19d9Swesolows 
25203eae19d9Swesolows 	np_orig = np;
25213eae19d9Swesolows 
25223eae19d9Swesolows 	while (np->rn_id.rl_type > type) {
25233eae19d9Swesolows 		ret = rc_node_parent(np, &parent);
25243eae19d9Swesolows 		if (np != np_orig)
25253eae19d9Swesolows 			rc_node_rele(np);
25263eae19d9Swesolows 		if (ret != REP_PROTOCOL_SUCCESS)
25273eae19d9Swesolows 			return (ret);
25283eae19d9Swesolows 		np = parent;
25293eae19d9Swesolows 	}
25303eae19d9Swesolows 
25313eae19d9Swesolows 	if (np->rn_id.rl_type == type) {
25323eae19d9Swesolows 		*app = parent;
25333eae19d9Swesolows 		return (REP_PROTOCOL_SUCCESS);
25343eae19d9Swesolows 	}
25353eae19d9Swesolows 
25363eae19d9Swesolows 	return (REP_PROTOCOL_FAIL_NOT_FOUND);
25373eae19d9Swesolows }
25383eae19d9Swesolows 
25393eae19d9Swesolows #ifndef NATIVE_BUILD
25403eae19d9Swesolows /*
25417c478bd9Sstevel@tonic-gate  * If the propname property exists in pg, and it is of type string, add its
25427c478bd9Sstevel@tonic-gate  * values as authorizations to pcp.  pg must not be locked on entry, and it is
25437c478bd9Sstevel@tonic-gate  * returned unlocked.  Returns
25447c478bd9Sstevel@tonic-gate  *   _DELETED - pg was deleted
25457c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES
25467c478bd9Sstevel@tonic-gate  *   _NOT_FOUND - pg has no property named propname
25477c478bd9Sstevel@tonic-gate  *   _SUCCESS
25487c478bd9Sstevel@tonic-gate  */
25497c478bd9Sstevel@tonic-gate static int
25507c478bd9Sstevel@tonic-gate perm_add_pg_prop_values(permcheck_t *pcp, rc_node_t *pg, const char *propname)
25517c478bd9Sstevel@tonic-gate {
25527c478bd9Sstevel@tonic-gate 	rc_node_t *prop;
25537c478bd9Sstevel@tonic-gate 	int result;
25547c478bd9Sstevel@tonic-gate 
25557c478bd9Sstevel@tonic-gate 	uint_t count;
25567c478bd9Sstevel@tonic-gate 	const char *cp;
25577c478bd9Sstevel@tonic-gate 
25587c478bd9Sstevel@tonic-gate 	assert(!MUTEX_HELD(&pg->rn_lock));
25597c478bd9Sstevel@tonic-gate 	assert(pg->rn_id.rl_type == REP_PROTOCOL_ENTITY_PROPERTYGRP);
25607c478bd9Sstevel@tonic-gate 
25617c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&pg->rn_lock);
25627c478bd9Sstevel@tonic-gate 	result = rc_node_find_named_child(pg, propname,
25637c478bd9Sstevel@tonic-gate 	    REP_PROTOCOL_ENTITY_PROPERTY, &prop);
25647c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&pg->rn_lock);
25657c478bd9Sstevel@tonic-gate 	if (result != REP_PROTOCOL_SUCCESS) {
25667c478bd9Sstevel@tonic-gate 		switch (result) {
25677c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_DELETED:
25687c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
25697c478bd9Sstevel@tonic-gate 			return (result);
25707c478bd9Sstevel@tonic-gate 
25717c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_INVALID_TYPE:
25727c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_TYPE_MISMATCH:
25737c478bd9Sstevel@tonic-gate 		default:
25747c478bd9Sstevel@tonic-gate 			bad_error("rc_node_find_named_child", result);
25757c478bd9Sstevel@tonic-gate 		}
25767c478bd9Sstevel@tonic-gate 	}
25777c478bd9Sstevel@tonic-gate 
25787c478bd9Sstevel@tonic-gate 	if (prop == NULL)
25797c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
25807c478bd9Sstevel@tonic-gate 
25817c478bd9Sstevel@tonic-gate 	/* rn_valtype is immutable, so no locking. */
25827c478bd9Sstevel@tonic-gate 	if (prop->rn_valtype != REP_PROTOCOL_TYPE_STRING) {
25837c478bd9Sstevel@tonic-gate 		rc_node_rele(prop);
25847c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_SUCCESS);
25857c478bd9Sstevel@tonic-gate 	}
25867c478bd9Sstevel@tonic-gate 
25877c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&prop->rn_lock);
25887c478bd9Sstevel@tonic-gate 	for (count = prop->rn_values_count, cp = prop->rn_values;
25897c478bd9Sstevel@tonic-gate 	    count > 0;
25907c478bd9Sstevel@tonic-gate 	    --count) {
25915b7f77adStw21770 		result = perm_add_enabling_type(pcp, cp,
25925b7f77adStw21770 		    (pg->rn_id.rl_ids[ID_INSTANCE]) ? PC_AUTH_INST :
25935b7f77adStw21770 		    PC_AUTH_SVC);
25947c478bd9Sstevel@tonic-gate 		if (result != REP_PROTOCOL_SUCCESS)
25957c478bd9Sstevel@tonic-gate 			break;
25967c478bd9Sstevel@tonic-gate 
25977c478bd9Sstevel@tonic-gate 		cp = strchr(cp, '\0') + 1;
25987c478bd9Sstevel@tonic-gate 	}
25997c478bd9Sstevel@tonic-gate 
26007c478bd9Sstevel@tonic-gate 	rc_node_rele_locked(prop);
26017c478bd9Sstevel@tonic-gate 
26027c478bd9Sstevel@tonic-gate 	return (result);
26037c478bd9Sstevel@tonic-gate }
26047c478bd9Sstevel@tonic-gate 
26057c478bd9Sstevel@tonic-gate /*
26067c478bd9Sstevel@tonic-gate  * Assuming that ent is a service or instance node, if the pgname property
26077c478bd9Sstevel@tonic-gate  * group has type pgtype, and it has a propname property with string type, add
26087c478bd9Sstevel@tonic-gate  * its values as authorizations to pcp.  If pgtype is NULL, it is not checked.
26097c478bd9Sstevel@tonic-gate  * Returns
26107c478bd9Sstevel@tonic-gate  *   _SUCCESS
26117c478bd9Sstevel@tonic-gate  *   _DELETED - ent was deleted
26127c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES - no resources
26137c478bd9Sstevel@tonic-gate  *   _NOT_FOUND - ent does not have pgname pg or propname property
26147c478bd9Sstevel@tonic-gate  */
26157c478bd9Sstevel@tonic-gate static int
26167c478bd9Sstevel@tonic-gate perm_add_ent_prop_values(permcheck_t *pcp, rc_node_t *ent, const char *pgname,
26177c478bd9Sstevel@tonic-gate     const char *pgtype, const char *propname)
26187c478bd9Sstevel@tonic-gate {
26197c478bd9Sstevel@tonic-gate 	int r;
26207c478bd9Sstevel@tonic-gate 	rc_node_t *pg;
26217c478bd9Sstevel@tonic-gate 
26227c478bd9Sstevel@tonic-gate 	assert(!MUTEX_HELD(&ent->rn_lock));
26237c478bd9Sstevel@tonic-gate 
26247c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&ent->rn_lock);
26257c478bd9Sstevel@tonic-gate 	r = rc_node_find_named_child(ent, pgname,
26267c478bd9Sstevel@tonic-gate 	    REP_PROTOCOL_ENTITY_PROPERTYGRP, &pg);
26277c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&ent->rn_lock);
26287c478bd9Sstevel@tonic-gate 
26297c478bd9Sstevel@tonic-gate 	switch (r) {
26307c478bd9Sstevel@tonic-gate 	case REP_PROTOCOL_SUCCESS:
26317c478bd9Sstevel@tonic-gate 		break;
26327c478bd9Sstevel@tonic-gate 
26337c478bd9Sstevel@tonic-gate 	case REP_PROTOCOL_FAIL_DELETED:
26347c478bd9Sstevel@tonic-gate 	case REP_PROTOCOL_FAIL_NO_RESOURCES:
26357c478bd9Sstevel@tonic-gate 		return (r);
26367c478bd9Sstevel@tonic-gate 
26377c478bd9Sstevel@tonic-gate 	default:
26387c478bd9Sstevel@tonic-gate 		bad_error("rc_node_find_named_child", r);
26397c478bd9Sstevel@tonic-gate 	}
26407c478bd9Sstevel@tonic-gate 
26417c478bd9Sstevel@tonic-gate 	if (pg == NULL)
26427c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
26437c478bd9Sstevel@tonic-gate 
26447c478bd9Sstevel@tonic-gate 	if (pgtype == NULL || strcmp(pg->rn_type, pgtype) == 0) {
26457c478bd9Sstevel@tonic-gate 		r = perm_add_pg_prop_values(pcp, pg, propname);
26467c478bd9Sstevel@tonic-gate 		switch (r) {
26477c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_DELETED:
26487c478bd9Sstevel@tonic-gate 			r = REP_PROTOCOL_FAIL_NOT_FOUND;
26497c478bd9Sstevel@tonic-gate 			break;
26507c478bd9Sstevel@tonic-gate 
26517c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
26527c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_SUCCESS:
26537c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_NOT_FOUND:
26547c478bd9Sstevel@tonic-gate 			break;
26557c478bd9Sstevel@tonic-gate 
26567c478bd9Sstevel@tonic-gate 		default:
26577c478bd9Sstevel@tonic-gate 			bad_error("perm_add_pg_prop_values", r);
26587c478bd9Sstevel@tonic-gate 		}
26597c478bd9Sstevel@tonic-gate 	}
26607c478bd9Sstevel@tonic-gate 
26617c478bd9Sstevel@tonic-gate 	rc_node_rele(pg);
26627c478bd9Sstevel@tonic-gate 
26637c478bd9Sstevel@tonic-gate 	return (r);
26647c478bd9Sstevel@tonic-gate }
26657c478bd9Sstevel@tonic-gate 
26667c478bd9Sstevel@tonic-gate /*
26673eae19d9Swesolows  * If pg has a property named propname, and is string typed, add its values as
26687c478bd9Sstevel@tonic-gate  * authorizations to pcp.  If pg has no such property, and its parent is an
26697c478bd9Sstevel@tonic-gate  * instance, walk up to the service and try doing the same with the property
26707c478bd9Sstevel@tonic-gate  * of the same name from the property group of the same name.  Returns
26717c478bd9Sstevel@tonic-gate  *   _SUCCESS
26727c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES
26737c478bd9Sstevel@tonic-gate  *   _DELETED - pg (or an ancestor) was deleted
26747c478bd9Sstevel@tonic-gate  */
26757c478bd9Sstevel@tonic-gate static int
26767c478bd9Sstevel@tonic-gate perm_add_enabling_values(permcheck_t *pcp, rc_node_t *pg, const char *propname)
26777c478bd9Sstevel@tonic-gate {
26787c478bd9Sstevel@tonic-gate 	int r;
26793eae19d9Swesolows 	char pgname[REP_PROTOCOL_NAME_LEN + 1];
26803eae19d9Swesolows 	rc_node_t *svc;
26813eae19d9Swesolows 	size_t sz;
26827c478bd9Sstevel@tonic-gate 
26837c478bd9Sstevel@tonic-gate 	r = perm_add_pg_prop_values(pcp, pg, propname);
26847c478bd9Sstevel@tonic-gate 
26853eae19d9Swesolows 	if (r != REP_PROTOCOL_FAIL_NOT_FOUND)
26863eae19d9Swesolows 		return (r);
26877c478bd9Sstevel@tonic-gate 
26887c478bd9Sstevel@tonic-gate 	assert(!MUTEX_HELD(&pg->rn_lock));
26897c478bd9Sstevel@tonic-gate 
26903eae19d9Swesolows 	if (pg->rn_id.rl_ids[ID_INSTANCE] == 0)
26917c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_SUCCESS);
26927c478bd9Sstevel@tonic-gate 
26937c478bd9Sstevel@tonic-gate 	sz = strlcpy(pgname, pg->rn_name, sizeof (pgname));
26947c478bd9Sstevel@tonic-gate 	assert(sz < sizeof (pgname));
26957c478bd9Sstevel@tonic-gate 
26963eae19d9Swesolows 	/*
26973eae19d9Swesolows 	 * If pg is a child of an instance or snapshot, we want to compose the
26983eae19d9Swesolows 	 * authorization property with the service's (if it exists).  The
26993eae19d9Swesolows 	 * snapshot case applies only to read_authorization.  In all other
27003eae19d9Swesolows 	 * cases, the pg's parent will be the instance.
27013eae19d9Swesolows 	 */
27023eae19d9Swesolows 	r = rc_node_find_ancestor(pg, REP_PROTOCOL_ENTITY_SERVICE, &svc);
27037c478bd9Sstevel@tonic-gate 	if (r != REP_PROTOCOL_SUCCESS) {
27047c478bd9Sstevel@tonic-gate 		assert(r == REP_PROTOCOL_FAIL_DELETED);
27057c478bd9Sstevel@tonic-gate 		return (r);
27067c478bd9Sstevel@tonic-gate 	}
27077c478bd9Sstevel@tonic-gate 	assert(svc->rn_id.rl_type == REP_PROTOCOL_ENTITY_SERVICE);
27087c478bd9Sstevel@tonic-gate 
27097c478bd9Sstevel@tonic-gate 	r = perm_add_ent_prop_values(pcp, svc, pgname, NULL, propname);
27107c478bd9Sstevel@tonic-gate 
27117c478bd9Sstevel@tonic-gate 	rc_node_rele(svc);
27127c478bd9Sstevel@tonic-gate 
27137c478bd9Sstevel@tonic-gate 	if (r == REP_PROTOCOL_FAIL_NOT_FOUND)
27147c478bd9Sstevel@tonic-gate 		r = REP_PROTOCOL_SUCCESS;
27157c478bd9Sstevel@tonic-gate 
27167c478bd9Sstevel@tonic-gate 	return (r);
27177c478bd9Sstevel@tonic-gate }
27187c478bd9Sstevel@tonic-gate 
27197c478bd9Sstevel@tonic-gate /*
27207c478bd9Sstevel@tonic-gate  * Call perm_add_enabling_values() for the "action_authorization" property of
27217c478bd9Sstevel@tonic-gate  * the "general" property group of inst.  Returns
27227c478bd9Sstevel@tonic-gate  *   _DELETED - inst (or an ancestor) was deleted
27237c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES
27247c478bd9Sstevel@tonic-gate  *   _SUCCESS
27257c478bd9Sstevel@tonic-gate  */
27267c478bd9Sstevel@tonic-gate static int
27277c478bd9Sstevel@tonic-gate perm_add_inst_action_auth(permcheck_t *pcp, rc_node_t *inst)
27287c478bd9Sstevel@tonic-gate {
27297c478bd9Sstevel@tonic-gate 	int r;
27307c478bd9Sstevel@tonic-gate 	rc_node_t *svc;
27317c478bd9Sstevel@tonic-gate 
27327c478bd9Sstevel@tonic-gate 	assert(inst->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE);
27337c478bd9Sstevel@tonic-gate 
27347c478bd9Sstevel@tonic-gate 	r = perm_add_ent_prop_values(pcp, inst, AUTH_PG_GENERAL,
27357c478bd9Sstevel@tonic-gate 	    AUTH_PG_GENERAL_TYPE, AUTH_PROP_ACTION);
27367c478bd9Sstevel@tonic-gate 
27377c478bd9Sstevel@tonic-gate 	if (r != REP_PROTOCOL_FAIL_NOT_FOUND)
27387c478bd9Sstevel@tonic-gate 		return (r);
27397c478bd9Sstevel@tonic-gate 
27407c478bd9Sstevel@tonic-gate 	r = rc_node_parent(inst, &svc);
27417c478bd9Sstevel@tonic-gate 	if (r != REP_PROTOCOL_SUCCESS) {
27427c478bd9Sstevel@tonic-gate 		assert(r == REP_PROTOCOL_FAIL_DELETED);
27437c478bd9Sstevel@tonic-gate 		return (r);
27447c478bd9Sstevel@tonic-gate 	}
27457c478bd9Sstevel@tonic-gate 
27467c478bd9Sstevel@tonic-gate 	r = perm_add_ent_prop_values(pcp, svc, AUTH_PG_GENERAL,
27477c478bd9Sstevel@tonic-gate 	    AUTH_PG_GENERAL_TYPE, AUTH_PROP_ACTION);
27487c478bd9Sstevel@tonic-gate 
27497c478bd9Sstevel@tonic-gate 	return (r == REP_PROTOCOL_FAIL_NOT_FOUND ? REP_PROTOCOL_SUCCESS : r);
27507c478bd9Sstevel@tonic-gate }
27517c478bd9Sstevel@tonic-gate #endif /* NATIVE_BUILD */
27527c478bd9Sstevel@tonic-gate 
27537c478bd9Sstevel@tonic-gate void
27547c478bd9Sstevel@tonic-gate rc_node_ptr_init(rc_node_ptr_t *out)
27557c478bd9Sstevel@tonic-gate {
27567c478bd9Sstevel@tonic-gate 	out->rnp_node = NULL;
27575b7f77adStw21770 	out->rnp_auth_string = NULL;
27585b7f77adStw21770 	out->rnp_authorized = RC_AUTH_UNKNOWN;
27597c478bd9Sstevel@tonic-gate 	out->rnp_deleted = 0;
27607c478bd9Sstevel@tonic-gate }
27617c478bd9Sstevel@tonic-gate 
27625b7f77adStw21770 void
27635b7f77adStw21770 rc_node_ptr_free_mem(rc_node_ptr_t *npp)
27645b7f77adStw21770 {
27655b7f77adStw21770 	if (npp->rnp_auth_string != NULL) {
27665b7f77adStw21770 		free((void *)npp->rnp_auth_string);
27675b7f77adStw21770 		npp->rnp_auth_string = NULL;
27685b7f77adStw21770 	}
27695b7f77adStw21770 }
27705b7f77adStw21770 
27717c478bd9Sstevel@tonic-gate static void
27727c478bd9Sstevel@tonic-gate rc_node_assign(rc_node_ptr_t *out, rc_node_t *val)
27737c478bd9Sstevel@tonic-gate {
27747c478bd9Sstevel@tonic-gate 	rc_node_t *cur = out->rnp_node;
27757c478bd9Sstevel@tonic-gate 	if (val != NULL)
27767c478bd9Sstevel@tonic-gate 		rc_node_hold(val);
27777c478bd9Sstevel@tonic-gate 	out->rnp_node = val;
27786643e1ffSbustos 	if (cur != NULL) {
27796643e1ffSbustos 		NODE_LOCK(cur);
27806643e1ffSbustos 
27816643e1ffSbustos 		/*
27826643e1ffSbustos 		 * Register the ephemeral reference created by reading
27836643e1ffSbustos 		 * out->rnp_node into cur.  Note that the persistent
27846643e1ffSbustos 		 * reference we're destroying is locked by the client
27856643e1ffSbustos 		 * layer.
27866643e1ffSbustos 		 */
27876643e1ffSbustos 		rc_node_hold_ephemeral_locked(cur);
27886643e1ffSbustos 
27896643e1ffSbustos 		rc_node_rele_locked(cur);
27906643e1ffSbustos 	}
27915b7f77adStw21770 	out->rnp_authorized = RC_AUTH_UNKNOWN;
27925b7f77adStw21770 	rc_node_ptr_free_mem(out);
27937c478bd9Sstevel@tonic-gate 	out->rnp_deleted = 0;
27947c478bd9Sstevel@tonic-gate }
27957c478bd9Sstevel@tonic-gate 
27967c478bd9Sstevel@tonic-gate void
27977c478bd9Sstevel@tonic-gate rc_node_clear(rc_node_ptr_t *out, int deleted)
27987c478bd9Sstevel@tonic-gate {
27997c478bd9Sstevel@tonic-gate 	rc_node_assign(out, NULL);
28007c478bd9Sstevel@tonic-gate 	out->rnp_deleted = deleted;
28017c478bd9Sstevel@tonic-gate }
28027c478bd9Sstevel@tonic-gate 
28037c478bd9Sstevel@tonic-gate void
28047c478bd9Sstevel@tonic-gate rc_node_ptr_assign(rc_node_ptr_t *out, const rc_node_ptr_t *val)
28057c478bd9Sstevel@tonic-gate {
28067c478bd9Sstevel@tonic-gate 	rc_node_assign(out, val->rnp_node);
28077c478bd9Sstevel@tonic-gate }
28087c478bd9Sstevel@tonic-gate 
28097c478bd9Sstevel@tonic-gate /*
28107c478bd9Sstevel@tonic-gate  * rc_node_check()/RC_NODE_CHECK()
28117c478bd9Sstevel@tonic-gate  *	generic "entry" checks, run before the use of an rc_node pointer.
28127c478bd9Sstevel@tonic-gate  *
28137c478bd9Sstevel@tonic-gate  * Fails with
28147c478bd9Sstevel@tonic-gate  *   _NOT_SET
28157c478bd9Sstevel@tonic-gate  *   _DELETED
28167c478bd9Sstevel@tonic-gate  */
28177c478bd9Sstevel@tonic-gate static int
28187c478bd9Sstevel@tonic-gate rc_node_check_and_lock(rc_node_t *np)
28197c478bd9Sstevel@tonic-gate {
28207c478bd9Sstevel@tonic-gate 	int result = REP_PROTOCOL_SUCCESS;
28217c478bd9Sstevel@tonic-gate 	if (np == NULL)
28227c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_SET);
28237c478bd9Sstevel@tonic-gate 
28247c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
28257c478bd9Sstevel@tonic-gate 	if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
28267c478bd9Sstevel@tonic-gate 		result = REP_PROTOCOL_FAIL_DELETED;
28277c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
28287c478bd9Sstevel@tonic-gate 	}
28297c478bd9Sstevel@tonic-gate 
28307c478bd9Sstevel@tonic-gate 	return (result);
28317c478bd9Sstevel@tonic-gate }
28327c478bd9Sstevel@tonic-gate 
28337c478bd9Sstevel@tonic-gate /*
28347c478bd9Sstevel@tonic-gate  * Fails with
28357c478bd9Sstevel@tonic-gate  *   _NOT_SET - ptr is reset
28367c478bd9Sstevel@tonic-gate  *   _DELETED - node has been deleted
28377c478bd9Sstevel@tonic-gate  */
28387c478bd9Sstevel@tonic-gate static rc_node_t *
28397c478bd9Sstevel@tonic-gate rc_node_ptr_check_and_lock(rc_node_ptr_t *npp, int *res)
28407c478bd9Sstevel@tonic-gate {
28417c478bd9Sstevel@tonic-gate 	rc_node_t *np = npp->rnp_node;
28427c478bd9Sstevel@tonic-gate 	if (np == NULL) {
28437c478bd9Sstevel@tonic-gate 		if (npp->rnp_deleted)
28447c478bd9Sstevel@tonic-gate 			*res = REP_PROTOCOL_FAIL_DELETED;
28457c478bd9Sstevel@tonic-gate 		else
28467c478bd9Sstevel@tonic-gate 			*res = REP_PROTOCOL_FAIL_NOT_SET;
28477c478bd9Sstevel@tonic-gate 		return (NULL);
28487c478bd9Sstevel@tonic-gate 	}
28497c478bd9Sstevel@tonic-gate 
28507c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
28517c478bd9Sstevel@tonic-gate 	if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
28527c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
28537c478bd9Sstevel@tonic-gate 		rc_node_clear(npp, 1);
28547c478bd9Sstevel@tonic-gate 		*res = REP_PROTOCOL_FAIL_DELETED;
28557c478bd9Sstevel@tonic-gate 		return (NULL);
28567c478bd9Sstevel@tonic-gate 	}
28577c478bd9Sstevel@tonic-gate 	return (np);
28587c478bd9Sstevel@tonic-gate }
28597c478bd9Sstevel@tonic-gate 
28607c478bd9Sstevel@tonic-gate #define	RC_NODE_CHECK_AND_LOCK(n) {					\
28617c478bd9Sstevel@tonic-gate 	int rc__res;							\
28627c478bd9Sstevel@tonic-gate 	if ((rc__res = rc_node_check_and_lock(n)) != REP_PROTOCOL_SUCCESS) \
28637c478bd9Sstevel@tonic-gate 		return (rc__res);					\
28647c478bd9Sstevel@tonic-gate }
28657c478bd9Sstevel@tonic-gate 
28667c478bd9Sstevel@tonic-gate #define	RC_NODE_CHECK(n) {						\
28677c478bd9Sstevel@tonic-gate 	RC_NODE_CHECK_AND_LOCK(n);					\
28687c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&(n)->rn_lock);			\
28697c478bd9Sstevel@tonic-gate }
28707c478bd9Sstevel@tonic-gate 
28717c478bd9Sstevel@tonic-gate #define	RC_NODE_CHECK_AND_HOLD(n) {					\
28727c478bd9Sstevel@tonic-gate 	RC_NODE_CHECK_AND_LOCK(n);					\
28737c478bd9Sstevel@tonic-gate 	rc_node_hold_locked(n);						\
28747c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&(n)->rn_lock);			\
28757c478bd9Sstevel@tonic-gate }
28767c478bd9Sstevel@tonic-gate 
28777c478bd9Sstevel@tonic-gate #define	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp) {			\
28787c478bd9Sstevel@tonic-gate 	int rc__res;							\
28797c478bd9Sstevel@tonic-gate 	if (((np) = rc_node_ptr_check_and_lock(npp, &rc__res)) == NULL)	\
28807c478bd9Sstevel@tonic-gate 		return (rc__res);					\
28817c478bd9Sstevel@tonic-gate }
28827c478bd9Sstevel@tonic-gate 
2883a4dc1477STom Whitten #define	RC_NODE_PTR_CHECK_LOCK_OR_FREE_RETURN(np, npp, mem) {		\
2884a4dc1477STom Whitten 	int rc__res;							\
2885a4dc1477STom Whitten 	if (((np) = rc_node_ptr_check_and_lock(npp, &rc__res)) == 	\
2886a4dc1477STom Whitten 	    NULL) {							\
2887a4dc1477STom Whitten 		if ((mem) != NULL)					\
2888a4dc1477STom Whitten 			free((mem));					\
2889a4dc1477STom Whitten 		return (rc__res);					\
2890a4dc1477STom Whitten 	}								\
2891a4dc1477STom Whitten }
2892a4dc1477STom Whitten 
28937c478bd9Sstevel@tonic-gate #define	RC_NODE_PTR_GET_CHECK(np, npp) {				\
28947c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);			\
28957c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&(np)->rn_lock);			\
28967c478bd9Sstevel@tonic-gate }
28977c478bd9Sstevel@tonic-gate 
28987c478bd9Sstevel@tonic-gate #define	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp) {			\
28997c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);			\
29007c478bd9Sstevel@tonic-gate 	rc_node_hold_locked(np);					\
29017c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&(np)->rn_lock);			\
29027c478bd9Sstevel@tonic-gate }
29037c478bd9Sstevel@tonic-gate 
29047c478bd9Sstevel@tonic-gate #define	HOLD_FLAG_OR_RETURN(np, flag) {					\
29057c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&(np)->rn_lock));				\
29067c478bd9Sstevel@tonic-gate 	assert(!((np)->rn_flags & RC_NODE_DEAD));			\
29077c478bd9Sstevel@tonic-gate 	if (!rc_node_hold_flag((np), flag)) {				\
29087c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&(np)->rn_lock);		\
29097c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_DELETED);			\
29107c478bd9Sstevel@tonic-gate 	}								\
29117c478bd9Sstevel@tonic-gate }
29127c478bd9Sstevel@tonic-gate 
29135b7f77adStw21770 #define	HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, flag, mem) {		\
29145b7f77adStw21770 	assert(MUTEX_HELD(&(np)->rn_lock));				\
29155b7f77adStw21770 	if (!rc_node_hold_flag((np), flag)) {				\
29165b7f77adStw21770 		(void) pthread_mutex_unlock(&(np)->rn_lock);		\
29175b7f77adStw21770 		assert((np) == (npp)->rnp_node);			\
29185b7f77adStw21770 		rc_node_clear(npp, 1);					\
29195b7f77adStw21770 		if ((mem) != NULL)					\
29205b7f77adStw21770 			free((mem));					\
29215b7f77adStw21770 		return (REP_PROTOCOL_FAIL_DELETED);			\
29225b7f77adStw21770 	}								\
29235b7f77adStw21770 }
29245b7f77adStw21770 
29257c478bd9Sstevel@tonic-gate int
29267c478bd9Sstevel@tonic-gate rc_local_scope(uint32_t type, rc_node_ptr_t *out)
29277c478bd9Sstevel@tonic-gate {
29287c478bd9Sstevel@tonic-gate 	if (type != REP_PROTOCOL_ENTITY_SCOPE) {
29297c478bd9Sstevel@tonic-gate 		rc_node_clear(out, 0);
29307c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
29317c478bd9Sstevel@tonic-gate 	}
29327c478bd9Sstevel@tonic-gate 
29337c478bd9Sstevel@tonic-gate 	/*
29347c478bd9Sstevel@tonic-gate 	 * the main scope never gets destroyed
29357c478bd9Sstevel@tonic-gate 	 */
29367c478bd9Sstevel@tonic-gate 	rc_node_assign(out, rc_scope);
29377c478bd9Sstevel@tonic-gate 
29387c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
29397c478bd9Sstevel@tonic-gate }
29407c478bd9Sstevel@tonic-gate 
29417c478bd9Sstevel@tonic-gate /*
29427c478bd9Sstevel@tonic-gate  * Fails with
29437c478bd9Sstevel@tonic-gate  *   _NOT_SET - npp is not set
29447c478bd9Sstevel@tonic-gate  *   _DELETED - the node npp pointed at has been deleted
29457c478bd9Sstevel@tonic-gate  *   _TYPE_MISMATCH - type is not _SCOPE
29467c478bd9Sstevel@tonic-gate  *   _NOT_FOUND - scope has no parent
29477c478bd9Sstevel@tonic-gate  */
29487c478bd9Sstevel@tonic-gate static int
29497c478bd9Sstevel@tonic-gate rc_scope_parent_scope(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out)
29507c478bd9Sstevel@tonic-gate {
29517c478bd9Sstevel@tonic-gate 	rc_node_t *np;
29527c478bd9Sstevel@tonic-gate 
29537c478bd9Sstevel@tonic-gate 	rc_node_clear(out, 0);
29547c478bd9Sstevel@tonic-gate 
29557c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK(np, npp);
29567c478bd9Sstevel@tonic-gate 
29577c478bd9Sstevel@tonic-gate 	if (type != REP_PROTOCOL_ENTITY_SCOPE)
29587c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
29597c478bd9Sstevel@tonic-gate 
29607c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_FAIL_NOT_FOUND);
29617c478bd9Sstevel@tonic-gate }
29627c478bd9Sstevel@tonic-gate 
29633eae19d9Swesolows static int rc_node_pg_check_read_protect(rc_node_t *);
29643eae19d9Swesolows 
29657c478bd9Sstevel@tonic-gate /*
29667c478bd9Sstevel@tonic-gate  * Fails with
29677c478bd9Sstevel@tonic-gate  *   _NOT_SET
29687c478bd9Sstevel@tonic-gate  *   _DELETED
29697c478bd9Sstevel@tonic-gate  *   _NOT_APPLICABLE
29707c478bd9Sstevel@tonic-gate  *   _NOT_FOUND
29717c478bd9Sstevel@tonic-gate  *   _BAD_REQUEST
29727c478bd9Sstevel@tonic-gate  *   _TRUNCATED
29733eae19d9Swesolows  *   _NO_RESOURCES
29747c478bd9Sstevel@tonic-gate  */
29757c478bd9Sstevel@tonic-gate int
29767c478bd9Sstevel@tonic-gate rc_node_name(rc_node_ptr_t *npp, char *buf, size_t sz, uint32_t answertype,
29777c478bd9Sstevel@tonic-gate     size_t *sz_out)
29787c478bd9Sstevel@tonic-gate {
29797c478bd9Sstevel@tonic-gate 	size_t actual;
29807c478bd9Sstevel@tonic-gate 	rc_node_t *np;
29817c478bd9Sstevel@tonic-gate 
29827c478bd9Sstevel@tonic-gate 	assert(sz == *sz_out);
29837c478bd9Sstevel@tonic-gate 
29847c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK(np, npp);
29857c478bd9Sstevel@tonic-gate 
29867c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
29877c478bd9Sstevel@tonic-gate 		np = np->rn_cchain[0];
29887c478bd9Sstevel@tonic-gate 		RC_NODE_CHECK(np);
29897c478bd9Sstevel@tonic-gate 	}
29907c478bd9Sstevel@tonic-gate 
29917c478bd9Sstevel@tonic-gate 	switch (answertype) {
29927c478bd9Sstevel@tonic-gate 	case RP_ENTITY_NAME_NAME:
29937c478bd9Sstevel@tonic-gate 		if (np->rn_name == NULL)
29947c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
29957c478bd9Sstevel@tonic-gate 		actual = strlcpy(buf, np->rn_name, sz);
29967c478bd9Sstevel@tonic-gate 		break;
29977c478bd9Sstevel@tonic-gate 	case RP_ENTITY_NAME_PGTYPE:
29987c478bd9Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
29997c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
30007c478bd9Sstevel@tonic-gate 		actual = strlcpy(buf, np->rn_type, sz);
30017c478bd9Sstevel@tonic-gate 		break;
30027c478bd9Sstevel@tonic-gate 	case RP_ENTITY_NAME_PGFLAGS:
30037c478bd9Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
30047c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
30057c478bd9Sstevel@tonic-gate 		actual = snprintf(buf, sz, "%d", np->rn_pgflags);
30067c478bd9Sstevel@tonic-gate 		break;
30077c478bd9Sstevel@tonic-gate 	case RP_ENTITY_NAME_SNAPLEVEL_SCOPE:
30087c478bd9Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
30097c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
30107c478bd9Sstevel@tonic-gate 		actual = strlcpy(buf, np->rn_snaplevel->rsl_scope, sz);
30117c478bd9Sstevel@tonic-gate 		break;
30127c478bd9Sstevel@tonic-gate 	case RP_ENTITY_NAME_SNAPLEVEL_SERVICE:
30137c478bd9Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
30147c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
30157c478bd9Sstevel@tonic-gate 		actual = strlcpy(buf, np->rn_snaplevel->rsl_service, sz);
30167c478bd9Sstevel@tonic-gate 		break;
30177c478bd9Sstevel@tonic-gate 	case RP_ENTITY_NAME_SNAPLEVEL_INSTANCE:
30187c478bd9Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
30197c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
30207c478bd9Sstevel@tonic-gate 		if (np->rn_snaplevel->rsl_instance == NULL)
30217c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_FOUND);
30227c478bd9Sstevel@tonic-gate 		actual = strlcpy(buf, np->rn_snaplevel->rsl_instance, sz);
30237c478bd9Sstevel@tonic-gate 		break;
30243eae19d9Swesolows 	case RP_ENTITY_NAME_PGREADPROT:
30253eae19d9Swesolows 	{
30263eae19d9Swesolows 		int ret;
30273eae19d9Swesolows 
30283eae19d9Swesolows 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
30293eae19d9Swesolows 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
30303eae19d9Swesolows 		ret = rc_node_pg_check_read_protect(np);
30313eae19d9Swesolows 		assert(ret != REP_PROTOCOL_FAIL_TYPE_MISMATCH);
30323eae19d9Swesolows 		switch (ret) {
30333eae19d9Swesolows 		case REP_PROTOCOL_FAIL_PERMISSION_DENIED:
30343eae19d9Swesolows 			actual = snprintf(buf, sz, "1");
30353eae19d9Swesolows 			break;
30363eae19d9Swesolows 		case REP_PROTOCOL_SUCCESS:
30373eae19d9Swesolows 			actual = snprintf(buf, sz, "0");
30383eae19d9Swesolows 			break;
30393eae19d9Swesolows 		default:
30403eae19d9Swesolows 			return (ret);
30413eae19d9Swesolows 		}
30423eae19d9Swesolows 		break;
30433eae19d9Swesolows 	}
30447c478bd9Sstevel@tonic-gate 	default:
30457c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
30467c478bd9Sstevel@tonic-gate 	}
30477c478bd9Sstevel@tonic-gate 	if (actual >= sz)
30487c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TRUNCATED);
30497c478bd9Sstevel@tonic-gate 
30507c478bd9Sstevel@tonic-gate 	*sz_out = actual;
30517c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
30527c478bd9Sstevel@tonic-gate }
30537c478bd9Sstevel@tonic-gate 
30547c478bd9Sstevel@tonic-gate int
30557c478bd9Sstevel@tonic-gate rc_node_get_property_type(rc_node_ptr_t *npp, rep_protocol_value_type_t *out)
30567c478bd9Sstevel@tonic-gate {
30577c478bd9Sstevel@tonic-gate 	rc_node_t *np;
30587c478bd9Sstevel@tonic-gate 
30597c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK(np, npp);
30607c478bd9Sstevel@tonic-gate 
30617c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
30627c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
30637c478bd9Sstevel@tonic-gate 
30647c478bd9Sstevel@tonic-gate 	*out = np->rn_valtype;
30657c478bd9Sstevel@tonic-gate 
30667c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
30677c478bd9Sstevel@tonic-gate }
30687c478bd9Sstevel@tonic-gate 
30697c478bd9Sstevel@tonic-gate /*
30707c478bd9Sstevel@tonic-gate  * Get np's parent.  If np is deleted, returns _DELETED.  Otherwise puts a hold
30717c478bd9Sstevel@tonic-gate  * on the parent, returns a pointer to it in *out, and returns _SUCCESS.
30727c478bd9Sstevel@tonic-gate  */
30737c478bd9Sstevel@tonic-gate static int
30747c478bd9Sstevel@tonic-gate rc_node_parent(rc_node_t *np, rc_node_t **out)
30757c478bd9Sstevel@tonic-gate {
30767c478bd9Sstevel@tonic-gate 	rc_node_t *pnp;
30777c478bd9Sstevel@tonic-gate 	rc_node_t *np_orig;
30787c478bd9Sstevel@tonic-gate 
30797c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
30807c478bd9Sstevel@tonic-gate 		RC_NODE_CHECK_AND_LOCK(np);
30817c478bd9Sstevel@tonic-gate 	} else {
30827c478bd9Sstevel@tonic-gate 		np = np->rn_cchain[0];
30837c478bd9Sstevel@tonic-gate 		RC_NODE_CHECK_AND_LOCK(np);
30847c478bd9Sstevel@tonic-gate 	}
30857c478bd9Sstevel@tonic-gate 
30867c478bd9Sstevel@tonic-gate 	np_orig = np;
30877c478bd9Sstevel@tonic-gate 	rc_node_hold_locked(np);		/* simplifies the remainder */
30887c478bd9Sstevel@tonic-gate 
30897c478bd9Sstevel@tonic-gate 	for (;;) {
30907c478bd9Sstevel@tonic-gate 		if (!rc_node_wait_flag(np,
30917c478bd9Sstevel@tonic-gate 		    RC_NODE_IN_TX | RC_NODE_USING_PARENT)) {
30927c478bd9Sstevel@tonic-gate 			rc_node_rele_locked(np);
30937c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_DELETED);
30947c478bd9Sstevel@tonic-gate 		}
30957c478bd9Sstevel@tonic-gate 
30967c478bd9Sstevel@tonic-gate 		if (!(np->rn_flags & RC_NODE_OLD))
30977c478bd9Sstevel@tonic-gate 			break;
30987c478bd9Sstevel@tonic-gate 
30997c478bd9Sstevel@tonic-gate 		rc_node_rele_locked(np);
31007c478bd9Sstevel@tonic-gate 		np = cache_lookup(&np_orig->rn_id);
31017c478bd9Sstevel@tonic-gate 		assert(np != np_orig);
31027c478bd9Sstevel@tonic-gate 
31037c478bd9Sstevel@tonic-gate 		if (np == NULL)
31047c478bd9Sstevel@tonic-gate 			goto deleted;
31057c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
31067c478bd9Sstevel@tonic-gate 	}
31077c478bd9Sstevel@tonic-gate 
31087c478bd9Sstevel@tonic-gate 	/* guaranteed to succeed without dropping the lock */
31097c478bd9Sstevel@tonic-gate 	if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
31107c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
31117c478bd9Sstevel@tonic-gate 		*out = NULL;
31127c478bd9Sstevel@tonic-gate 		rc_node_rele(np);
31137c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_DELETED);
31147c478bd9Sstevel@tonic-gate 	}
31157c478bd9Sstevel@tonic-gate 
31167c478bd9Sstevel@tonic-gate 	assert(np->rn_parent != NULL);
31177c478bd9Sstevel@tonic-gate 	pnp = np->rn_parent;
31187c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
31197c478bd9Sstevel@tonic-gate 
31207c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&pnp->rn_lock);
31217c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
31227c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_USING_PARENT);
31237c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
31247c478bd9Sstevel@tonic-gate 
31257c478bd9Sstevel@tonic-gate 	rc_node_hold_locked(pnp);
31267c478bd9Sstevel@tonic-gate 
31277c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&pnp->rn_lock);
31287c478bd9Sstevel@tonic-gate 
31297c478bd9Sstevel@tonic-gate 	rc_node_rele(np);
31307c478bd9Sstevel@tonic-gate 	*out = pnp;
31317c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
31327c478bd9Sstevel@tonic-gate 
31337c478bd9Sstevel@tonic-gate deleted:
31347c478bd9Sstevel@tonic-gate 	rc_node_rele(np);
31357c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_FAIL_DELETED);
31367c478bd9Sstevel@tonic-gate }
31377c478bd9Sstevel@tonic-gate 
31387c478bd9Sstevel@tonic-gate /*
31397c478bd9Sstevel@tonic-gate  * Fails with
31407c478bd9Sstevel@tonic-gate  *   _NOT_SET
31417c478bd9Sstevel@tonic-gate  *   _DELETED
31427c478bd9Sstevel@tonic-gate  */
31437c478bd9Sstevel@tonic-gate static int
31447c478bd9Sstevel@tonic-gate rc_node_ptr_parent(rc_node_ptr_t *npp, rc_node_t **out)
31457c478bd9Sstevel@tonic-gate {
31467c478bd9Sstevel@tonic-gate 	rc_node_t *np;
31477c478bd9Sstevel@tonic-gate 
31487c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK(np, npp);
31497c478bd9Sstevel@tonic-gate 
31507c478bd9Sstevel@tonic-gate 	return (rc_node_parent(np, out));
31517c478bd9Sstevel@tonic-gate }
31527c478bd9Sstevel@tonic-gate 
31537c478bd9Sstevel@tonic-gate /*
31547c478bd9Sstevel@tonic-gate  * Fails with
31557c478bd9Sstevel@tonic-gate  *   _NOT_SET - npp is not set
31567c478bd9Sstevel@tonic-gate  *   _DELETED - the node npp pointed at has been deleted
31577c478bd9Sstevel@tonic-gate  *   _TYPE_MISMATCH - npp's node's parent is not of type type
31587c478bd9Sstevel@tonic-gate  *
31597c478bd9Sstevel@tonic-gate  * If npp points to a scope, can also fail with
31607c478bd9Sstevel@tonic-gate  *   _NOT_FOUND - scope has no parent
31617c478bd9Sstevel@tonic-gate  */
31627c478bd9Sstevel@tonic-gate int
31637c478bd9Sstevel@tonic-gate rc_node_get_parent(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out)
31647c478bd9Sstevel@tonic-gate {
31657c478bd9Sstevel@tonic-gate 	rc_node_t *pnp;
31667c478bd9Sstevel@tonic-gate 	int rc;
31677c478bd9Sstevel@tonic-gate 
31687c478bd9Sstevel@tonic-gate 	if (npp->rnp_node != NULL &&
31697c478bd9Sstevel@tonic-gate 	    npp->rnp_node->rn_id.rl_type == REP_PROTOCOL_ENTITY_SCOPE)
31707c478bd9Sstevel@tonic-gate 		return (rc_scope_parent_scope(npp, type, out));
31717c478bd9Sstevel@tonic-gate 
31727c478bd9Sstevel@tonic-gate 	if ((rc = rc_node_ptr_parent(npp, &pnp)) != REP_PROTOCOL_SUCCESS) {
31737c478bd9Sstevel@tonic-gate 		rc_node_clear(out, 0);
31747c478bd9Sstevel@tonic-gate 		return (rc);
31757c478bd9Sstevel@tonic-gate 	}
31767c478bd9Sstevel@tonic-gate 
31777c478bd9Sstevel@tonic-gate 	if (type != pnp->rn_id.rl_type) {
31787c478bd9Sstevel@tonic-gate 		rc_node_rele(pnp);
31797c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
31807c478bd9Sstevel@tonic-gate 	}
31817c478bd9Sstevel@tonic-gate 
31827c478bd9Sstevel@tonic-gate 	rc_node_assign(out, pnp);
31837c478bd9Sstevel@tonic-gate 	rc_node_rele(pnp);
31847c478bd9Sstevel@tonic-gate 
31857c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
31867c478bd9Sstevel@tonic-gate }
31877c478bd9Sstevel@tonic-gate 
31887c478bd9Sstevel@tonic-gate int
31897c478bd9Sstevel@tonic-gate rc_node_parent_type(rc_node_ptr_t *npp, uint32_t *type_out)
31907c478bd9Sstevel@tonic-gate {
31917c478bd9Sstevel@tonic-gate 	rc_node_t *pnp;
31927c478bd9Sstevel@tonic-gate 	int rc;
31937c478bd9Sstevel@tonic-gate 
31947c478bd9Sstevel@tonic-gate 	if (npp->rnp_node != NULL &&
31957c478bd9Sstevel@tonic-gate 	    npp->rnp_node->rn_id.rl_type == REP_PROTOCOL_ENTITY_SCOPE) {
31967c478bd9Sstevel@tonic-gate 		*type_out = REP_PROTOCOL_ENTITY_SCOPE;
31977c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_SUCCESS);
31987c478bd9Sstevel@tonic-gate 	}
31997c478bd9Sstevel@tonic-gate 
32007c478bd9Sstevel@tonic-gate 	if ((rc = rc_node_ptr_parent(npp, &pnp)) != REP_PROTOCOL_SUCCESS)
32017c478bd9Sstevel@tonic-gate 		return (rc);
32027c478bd9Sstevel@tonic-gate 
32037c478bd9Sstevel@tonic-gate 	*type_out = pnp->rn_id.rl_type;
32047c478bd9Sstevel@tonic-gate 
32057c478bd9Sstevel@tonic-gate 	rc_node_rele(pnp);
32067c478bd9Sstevel@tonic-gate 
32077c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
32087c478bd9Sstevel@tonic-gate }
32097c478bd9Sstevel@tonic-gate 
32107c478bd9Sstevel@tonic-gate /*
32117c478bd9Sstevel@tonic-gate  * Fails with
32127c478bd9Sstevel@tonic-gate  *   _INVALID_TYPE - type is invalid
32137c478bd9Sstevel@tonic-gate  *   _TYPE_MISMATCH - np doesn't carry children of type type
32147c478bd9Sstevel@tonic-gate  *   _DELETED - np has been deleted
32157c478bd9Sstevel@tonic-gate  *   _NOT_FOUND - no child with that name/type combo found
32167c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES
32177c478bd9Sstevel@tonic-gate  *   _BACKEND_ACCESS
32187c478bd9Sstevel@tonic-gate  */
32197c478bd9Sstevel@tonic-gate int
32207c478bd9Sstevel@tonic-gate rc_node_get_child(rc_node_ptr_t *npp, const char *name, uint32_t type,
32217c478bd9Sstevel@tonic-gate     rc_node_ptr_t *outp)
32227c478bd9Sstevel@tonic-gate {
32237c478bd9Sstevel@tonic-gate 	rc_node_t *np, *cp;
32247c478bd9Sstevel@tonic-gate 	rc_node_t *child = NULL;
32257c478bd9Sstevel@tonic-gate 	int ret, idx;
32267c478bd9Sstevel@tonic-gate 
32277c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
32287c478bd9Sstevel@tonic-gate 	if ((ret = rc_check_type_name(type, name)) == REP_PROTOCOL_SUCCESS) {
32297c478bd9Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
32307c478bd9Sstevel@tonic-gate 			ret = rc_node_find_named_child(np, name, type, &child);
32317c478bd9Sstevel@tonic-gate 		} else {
32327c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
32337c478bd9Sstevel@tonic-gate 			ret = REP_PROTOCOL_SUCCESS;
32347c478bd9Sstevel@tonic-gate 			for (idx = 0; idx < COMPOSITION_DEPTH; idx++) {
32357c478bd9Sstevel@tonic-gate 				cp = np->rn_cchain[idx];
32367c478bd9Sstevel@tonic-gate 				if (cp == NULL)
32377c478bd9Sstevel@tonic-gate 					break;
32387c478bd9Sstevel@tonic-gate 				RC_NODE_CHECK_AND_LOCK(cp);
32397c478bd9Sstevel@tonic-gate 				ret = rc_node_find_named_child(cp, name, type,
32407c478bd9Sstevel@tonic-gate 				    &child);
32417c478bd9Sstevel@tonic-gate 				(void) pthread_mutex_unlock(&cp->rn_lock);
32427c478bd9Sstevel@tonic-gate 				/*
32437c478bd9Sstevel@tonic-gate 				 * loop only if we succeeded, but no child of
32447c478bd9Sstevel@tonic-gate 				 * the correct name was found.
32457c478bd9Sstevel@tonic-gate 				 */
32467c478bd9Sstevel@tonic-gate 				if (ret != REP_PROTOCOL_SUCCESS ||
32477c478bd9Sstevel@tonic-gate 				    child != NULL)
32487c478bd9Sstevel@tonic-gate 					break;
32497c478bd9Sstevel@tonic-gate 			}
32507c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
32517c478bd9Sstevel@tonic-gate 		}
32527c478bd9Sstevel@tonic-gate 	}
32537c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
32547c478bd9Sstevel@tonic-gate 
32557c478bd9Sstevel@tonic-gate 	if (ret == REP_PROTOCOL_SUCCESS) {
32567c478bd9Sstevel@tonic-gate 		rc_node_assign(outp, child);
32577c478bd9Sstevel@tonic-gate 		if (child != NULL)
32587c478bd9Sstevel@tonic-gate 			rc_node_rele(child);
32597c478bd9Sstevel@tonic-gate 		else
32607c478bd9Sstevel@tonic-gate 			ret = REP_PROTOCOL_FAIL_NOT_FOUND;
32617c478bd9Sstevel@tonic-gate 	} else {
32627c478bd9Sstevel@tonic-gate 		rc_node_assign(outp, NULL);
32637c478bd9Sstevel@tonic-gate 	}
32647c478bd9Sstevel@tonic-gate 	return (ret);
32657c478bd9Sstevel@tonic-gate }
32667c478bd9Sstevel@tonic-gate 
32677c478bd9Sstevel@tonic-gate int
32687c478bd9Sstevel@tonic-gate rc_node_update(rc_node_ptr_t *npp)
32697c478bd9Sstevel@tonic-gate {
32707c478bd9Sstevel@tonic-gate 	cache_bucket_t *bp;
32717c478bd9Sstevel@tonic-gate 	rc_node_t *np = npp->rnp_node;
32727c478bd9Sstevel@tonic-gate 	rc_node_t *nnp;
32737c478bd9Sstevel@tonic-gate 	rc_node_t *cpg = NULL;
32747c478bd9Sstevel@tonic-gate 
32757c478bd9Sstevel@tonic-gate 	if (np != NULL &&
32767c478bd9Sstevel@tonic-gate 	    np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
32777c478bd9Sstevel@tonic-gate 		/*
32787c478bd9Sstevel@tonic-gate 		 * If we're updating a composed property group, actually
32797c478bd9Sstevel@tonic-gate 		 * update the top-level property group & return the
32807c478bd9Sstevel@tonic-gate 		 * appropriate value.  But leave *nnp pointing at us.
32817c478bd9Sstevel@tonic-gate 		 */
32827c478bd9Sstevel@tonic-gate 		cpg = np;
32837c478bd9Sstevel@tonic-gate 		np = np->rn_cchain[0];
32847c478bd9Sstevel@tonic-gate 	}
32857c478bd9Sstevel@tonic-gate 
32867c478bd9Sstevel@tonic-gate 	RC_NODE_CHECK(np);
32877c478bd9Sstevel@tonic-gate 
32887c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP &&
32897c478bd9Sstevel@tonic-gate 	    np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT)
32907c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
32917c478bd9Sstevel@tonic-gate 
32927c478bd9Sstevel@tonic-gate 	for (;;) {
32937c478bd9Sstevel@tonic-gate 		bp = cache_hold(np->rn_hash);
32947c478bd9Sstevel@tonic-gate 		nnp = cache_lookup_unlocked(bp, &np->rn_id);
32957c478bd9Sstevel@tonic-gate 		if (nnp == NULL) {
32967c478bd9Sstevel@tonic-gate 			cache_release(bp);
32977c478bd9Sstevel@tonic-gate 			rc_node_clear(npp, 1);
32987c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_DELETED);
32997c478bd9Sstevel@tonic-gate 		}
33007c478bd9Sstevel@tonic-gate 		/*
33017c478bd9Sstevel@tonic-gate 		 * grab the lock before dropping the cache bucket, so
33027c478bd9Sstevel@tonic-gate 		 * that no one else can sneak in
33037c478bd9Sstevel@tonic-gate 		 */
33047c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&nnp->rn_lock);
33057c478bd9Sstevel@tonic-gate 		cache_release(bp);
33067c478bd9Sstevel@tonic-gate 
33077c478bd9Sstevel@tonic-gate 		if (!(nnp->rn_flags & RC_NODE_IN_TX) ||
33087c478bd9Sstevel@tonic-gate 		    !rc_node_wait_flag(nnp, RC_NODE_IN_TX))
33097c478bd9Sstevel@tonic-gate 			break;
33107c478bd9Sstevel@tonic-gate 
33117c478bd9Sstevel@tonic-gate 		rc_node_rele_locked(nnp);
33127c478bd9Sstevel@tonic-gate 	}
33137c478bd9Sstevel@tonic-gate 
33147c478bd9Sstevel@tonic-gate 	/*
33157c478bd9Sstevel@tonic-gate 	 * If it is dead, we want to update it so that it will continue to
33167c478bd9Sstevel@tonic-gate 	 * report being dead.
33177c478bd9Sstevel@tonic-gate 	 */
33187c478bd9Sstevel@tonic-gate 	if (nnp->rn_flags & RC_NODE_DEAD) {
33197c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&nnp->rn_lock);
33207c478bd9Sstevel@tonic-gate 		if (nnp != np && cpg == NULL)
33217c478bd9Sstevel@tonic-gate 			rc_node_assign(npp, nnp);	/* updated */
33227c478bd9Sstevel@tonic-gate 		rc_node_rele(nnp);
33237c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_DELETED);
33247c478bd9Sstevel@tonic-gate 	}
33257c478bd9Sstevel@tonic-gate 
33267c478bd9Sstevel@tonic-gate 	assert(!(nnp->rn_flags & RC_NODE_OLD));
33277c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&nnp->rn_lock);
33287c478bd9Sstevel@tonic-gate 
33297c478bd9Sstevel@tonic-gate 	if (nnp != np && cpg == NULL)
33307c478bd9Sstevel@tonic-gate 		rc_node_assign(npp, nnp);		/* updated */
33317c478bd9Sstevel@tonic-gate 
33327c478bd9Sstevel@tonic-gate 	rc_node_rele(nnp);
33337c478bd9Sstevel@tonic-gate 
33347c478bd9Sstevel@tonic-gate 	return ((nnp == np)? REP_PROTOCOL_SUCCESS : REP_PROTOCOL_DONE);
33357c478bd9Sstevel@tonic-gate }
33367c478bd9Sstevel@tonic-gate 
33377c478bd9Sstevel@tonic-gate /*
33387c478bd9Sstevel@tonic-gate  * does a generic modification check, for creation, deletion, and snapshot
33397c478bd9Sstevel@tonic-gate  * management only.  Property group transactions have different checks.
33405b7f77adStw21770  *
33415b7f77adStw21770  * The string returned to *match_auth must be freed.
33427c478bd9Sstevel@tonic-gate  */
3343a4dc1477STom Whitten static perm_status_t
33445b7f77adStw21770 rc_node_modify_permission_check(char **match_auth)
33457c478bd9Sstevel@tonic-gate {
33467c478bd9Sstevel@tonic-gate 	permcheck_t *pcp;
3347a4dc1477STom Whitten 	perm_status_t granted = PERM_GRANTED;
3348a4dc1477STom Whitten 	int rc;
33497c478bd9Sstevel@tonic-gate 
33505b7f77adStw21770 	*match_auth = NULL;
3351273264cdSdm120769 #ifdef NATIVE_BUILD
33525b7f77adStw21770 	if (!client_is_privileged()) {
3353a4dc1477STom Whitten 		granted = PERM_DENIED;
33545b7f77adStw21770 	}
3355a4dc1477STom Whitten 	return (granted);
33567c478bd9Sstevel@tonic-gate #else
33575b7f77adStw21770 	if (is_main_repository == 0)
3358a4dc1477STom Whitten 		return (PERM_GRANTED);
33597c478bd9Sstevel@tonic-gate 	pcp = pc_create();
33607c478bd9Sstevel@tonic-gate 	if (pcp != NULL) {
33617c478bd9Sstevel@tonic-gate 		rc = perm_add_enabling(pcp, AUTH_MODIFY);
33627c478bd9Sstevel@tonic-gate 
33637c478bd9Sstevel@tonic-gate 		if (rc == REP_PROTOCOL_SUCCESS) {
33647c478bd9Sstevel@tonic-gate 			granted = perm_granted(pcp);
33657c478bd9Sstevel@tonic-gate 
3366a4dc1477STom Whitten 			if ((granted == PERM_GRANTED) ||
3367a4dc1477STom Whitten 			    (granted == PERM_DENIED)) {
33685b7f77adStw21770 				/*
33695b7f77adStw21770 				 * Copy off the authorization
33705b7f77adStw21770 				 * string before freeing pcp.
33715b7f77adStw21770 				 */
33725b7f77adStw21770 				*match_auth =
33735b7f77adStw21770 				    strdup(pcp->pc_auth_string);
33745b7f77adStw21770 				if (*match_auth == NULL)
3375a4dc1477STom Whitten 					granted = PERM_FAIL;
33765b7f77adStw21770 			}
3377a4dc1477STom Whitten 		} else {
3378a4dc1477STom Whitten 			granted = PERM_FAIL;
33797c478bd9Sstevel@tonic-gate 		}
33807c478bd9Sstevel@tonic-gate 
33817c478bd9Sstevel@tonic-gate 		pc_free(pcp);
33827c478bd9Sstevel@tonic-gate 	} else {
3383a4dc1477STom Whitten 		granted = PERM_FAIL;
33847c478bd9Sstevel@tonic-gate 	}
33857c478bd9Sstevel@tonic-gate 
3386a4dc1477STom Whitten 	return (granted);
3387273264cdSdm120769 #endif /* NATIVE_BUILD */
3388273264cdSdm120769 }
33895b7f77adStw21770 
33905b7f77adStw21770 /*
33915b7f77adStw21770  * Native builds are done to create svc.configd-native.  This program runs
33925b7f77adStw21770  * only on the Solaris build machines to create the seed repository, and it
33935b7f77adStw21770  * is compiled against the build machine's header files.  The ADT_smf_*
33945b7f77adStw21770  * symbols may not be defined in these header files.  For this reason
33955b7f77adStw21770  * smf_annotation_event(), _smf_audit_event() and special_property_event()
33965b7f77adStw21770  * are not compiled for native builds.
33975b7f77adStw21770  */
33985b7f77adStw21770 #ifndef	NATIVE_BUILD
33995b7f77adStw21770 
34005b7f77adStw21770 /*
34015b7f77adStw21770  * This function generates an annotation audit event if one has been setup.
34025b7f77adStw21770  * Annotation events should only be generated immediately before the audit
34035b7f77adStw21770  * record from the first attempt to modify the repository from a client
34045b7f77adStw21770  * which has requested an annotation.
34055b7f77adStw21770  */
34065b7f77adStw21770 static void
34075b7f77adStw21770 smf_annotation_event(int status, int return_val)
34085b7f77adStw21770 {
34095b7f77adStw21770 	adt_session_data_t *session;
34105b7f77adStw21770 	adt_event_data_t *event = NULL;
34115b7f77adStw21770 	char file[MAXPATHLEN];
34125b7f77adStw21770 	char operation[REP_PROTOCOL_NAME_LEN];
34135b7f77adStw21770 
34145b7f77adStw21770 	/* Don't audit if we're using an alternate repository. */
34155b7f77adStw21770 	if (is_main_repository == 0)
34165b7f77adStw21770 		return;
34175b7f77adStw21770 
34185b7f77adStw21770 	if (client_annotation_needed(operation, sizeof (operation), file,
34195b7f77adStw21770 	    sizeof (file)) == 0) {
34205b7f77adStw21770 		return;
34215b7f77adStw21770 	}
34225b7f77adStw21770 	if (file[0] == 0) {
34235b7f77adStw21770 		(void) strlcpy(file, "NO FILE", sizeof (file));
34245b7f77adStw21770 	}
34255b7f77adStw21770 	if (operation[0] == 0) {
34265b7f77adStw21770 		(void) strlcpy(operation, "NO OPERATION",
34275b7f77adStw21770 		    sizeof (operation));
34285b7f77adStw21770 	}
34295b7f77adStw21770 	if ((session = get_audit_session()) == NULL)
34305b7f77adStw21770 		return;
34315b7f77adStw21770 	if ((event = adt_alloc_event(session, ADT_smf_annotation)) == NULL) {
34325b7f77adStw21770 		uu_warn("smf_annotation_event cannot allocate event "
34335b7f77adStw21770 		    "data.  %s\n", strerror(errno));
34345b7f77adStw21770 		return;
34355b7f77adStw21770 	}
34365b7f77adStw21770 	event->adt_smf_annotation.operation = operation;
34375b7f77adStw21770 	event->adt_smf_annotation.file = file;
34385b7f77adStw21770 	if (adt_put_event(event, status, return_val) == 0) {
34395b7f77adStw21770 		client_annotation_finished();
34405b7f77adStw21770 	} else {
34415b7f77adStw21770 		uu_warn("smf_annotation_event failed to put event.  "
34425b7f77adStw21770 		    "%s\n", strerror(errno));
34435b7f77adStw21770 	}
34445b7f77adStw21770 	adt_free_event(event);
34455b7f77adStw21770 }
34465b7f77adStw21770 
34475b7f77adStw21770 /*
34485b7f77adStw21770  * _smf_audit_event interacts with the security auditing system to generate
34495b7f77adStw21770  * an audit event structure.  It establishes an audit session and allocates
34505b7f77adStw21770  * an audit event.  The event is filled in from the audit data, and
34515b7f77adStw21770  * adt_put_event is called to generate the event.
34525b7f77adStw21770  */
34535b7f77adStw21770 static void
34545b7f77adStw21770 _smf_audit_event(au_event_t event_id, int status, int return_val,
34555b7f77adStw21770     audit_event_data_t *data)
34565b7f77adStw21770 {
34575b7f77adStw21770 	char *auth_used;
34585b7f77adStw21770 	char *fmri;
34595b7f77adStw21770 	char *prop_value;
34605b7f77adStw21770 	adt_session_data_t *session;
34615b7f77adStw21770 	adt_event_data_t *event = NULL;
34625b7f77adStw21770 
34635b7f77adStw21770 	/* Don't audit if we're using an alternate repository */
34645b7f77adStw21770 	if (is_main_repository == 0)
34655b7f77adStw21770 		return;
34665b7f77adStw21770 
34675b7f77adStw21770 	smf_annotation_event(status, return_val);
34685b7f77adStw21770 	if ((session = get_audit_session()) == NULL)
34695b7f77adStw21770 		return;
34705b7f77adStw21770 	if ((event = adt_alloc_event(session, event_id)) == NULL) {
34715b7f77adStw21770 		uu_warn("_smf_audit_event cannot allocate event "
34725b7f77adStw21770 		    "data.  %s\n", strerror(errno));
34735b7f77adStw21770 		return;
34745b7f77adStw21770 	}
34755b7f77adStw21770 
34765b7f77adStw21770 	/*
34775b7f77adStw21770 	 * Handle possibility of NULL authorization strings, FMRIs and
34785b7f77adStw21770 	 * property values.
34795b7f77adStw21770 	 */
34805b7f77adStw21770 	if (data->ed_auth == NULL) {
34815b7f77adStw21770 		auth_used = "PRIVILEGED";
34825b7f77adStw21770 	} else {
34835b7f77adStw21770 		auth_used = data->ed_auth;
34845b7f77adStw21770 	}
34855b7f77adStw21770 	if (data->ed_fmri == NULL) {
34865b7f77adStw21770 		syslog(LOG_WARNING, "_smf_audit_event called with "
34875b7f77adStw21770 		    "empty FMRI string");
34885b7f77adStw21770 		fmri = "UNKNOWN FMRI";
34895b7f77adStw21770 	} else {
34905b7f77adStw21770 		fmri = data->ed_fmri;
34915b7f77adStw21770 	}
34925b7f77adStw21770 	if (data->ed_prop_value == NULL) {
34935b7f77adStw21770 		prop_value = "";
34945b7f77adStw21770 	} else {
34955b7f77adStw21770 		prop_value = data->ed_prop_value;
34965b7f77adStw21770 	}
34975b7f77adStw21770 
34985b7f77adStw21770 	/* Fill in the event data. */
34995b7f77adStw21770 	switch (event_id) {
35005b7f77adStw21770 	case ADT_smf_attach_snap:
35015b7f77adStw21770 		event->adt_smf_attach_snap.auth_used = auth_used;
35025b7f77adStw21770 		event->adt_smf_attach_snap.old_fmri = data->ed_old_fmri;
35035b7f77adStw21770 		event->adt_smf_attach_snap.old_name = data->ed_old_name;
35045b7f77adStw21770 		event->adt_smf_attach_snap.new_fmri = fmri;
35055b7f77adStw21770 		event->adt_smf_attach_snap.new_name = data->ed_snapname;
35065b7f77adStw21770 		break;
35075b7f77adStw21770 	case ADT_smf_change_prop:
35085b7f77adStw21770 		event->adt_smf_change_prop.auth_used = auth_used;
35095b7f77adStw21770 		event->adt_smf_change_prop.fmri = fmri;
35105b7f77adStw21770 		event->adt_smf_change_prop.type = data->ed_type;
35115b7f77adStw21770 		event->adt_smf_change_prop.value = prop_value;
35125b7f77adStw21770 		break;
35135b7f77adStw21770 	case ADT_smf_clear:
35145b7f77adStw21770 		event->adt_smf_clear.auth_used = auth_used;
35155b7f77adStw21770 		event->adt_smf_clear.fmri = fmri;
35165b7f77adStw21770 		break;
35175b7f77adStw21770 	case ADT_smf_create:
35185b7f77adStw21770 		event->adt_smf_create.fmri = fmri;
35195b7f77adStw21770 		event->adt_smf_create.auth_used = auth_used;
35205b7f77adStw21770 		break;
35215b7f77adStw21770 	case ADT_smf_create_npg:
35225b7f77adStw21770 		event->adt_smf_create_npg.auth_used = auth_used;
35235b7f77adStw21770 		event->adt_smf_create_npg.fmri = fmri;
35245b7f77adStw21770 		event->adt_smf_create_npg.type = data->ed_type;
35255b7f77adStw21770 		break;
35265b7f77adStw21770 	case ADT_smf_create_pg:
35275b7f77adStw21770 		event->adt_smf_create_pg.auth_used = auth_used;
35285b7f77adStw21770 		event->adt_smf_create_pg.fmri = fmri;
35295b7f77adStw21770 		event->adt_smf_create_pg.type = data->ed_type;
35305b7f77adStw21770 		break;
35315b7f77adStw21770 	case ADT_smf_create_prop:
35325b7f77adStw21770 		event->adt_smf_create_prop.auth_used = auth_used;
35335b7f77adStw21770 		event->adt_smf_create_prop.fmri = fmri;
35345b7f77adStw21770 		event->adt_smf_create_prop.type = data->ed_type;
35355b7f77adStw21770 		event->adt_smf_create_prop.value = prop_value;
35365b7f77adStw21770 		break;
35375b7f77adStw21770 	case ADT_smf_create_snap:
35385b7f77adStw21770 		event->adt_smf_create_snap.auth_used = auth_used;
35395b7f77adStw21770 		event->adt_smf_create_snap.fmri = fmri;
35405b7f77adStw21770 		event->adt_smf_create_snap.name = data->ed_snapname;
35415b7f77adStw21770 		break;
35425b7f77adStw21770 	case ADT_smf_degrade:
35435b7f77adStw21770 		event->adt_smf_degrade.auth_used = auth_used;
35445b7f77adStw21770 		event->adt_smf_degrade.fmri = fmri;
35455b7f77adStw21770 		break;
35465b7f77adStw21770 	case ADT_smf_delete:
35475b7f77adStw21770 		event->adt_smf_delete.fmri = fmri;
35485b7f77adStw21770 		event->adt_smf_delete.auth_used = auth_used;
35495b7f77adStw21770 		break;
35505b7f77adStw21770 	case ADT_smf_delete_npg:
35515b7f77adStw21770 		event->adt_smf_delete_npg.auth_used = auth_used;
35525b7f77adStw21770 		event->adt_smf_delete_npg.fmri = fmri;
35535b7f77adStw21770 		event->adt_smf_delete_npg.type = data->ed_type;
35545b7f77adStw21770 		break;
35555b7f77adStw21770 	case ADT_smf_delete_pg:
35565b7f77adStw21770 		event->adt_smf_delete_pg.auth_used = auth_used;
35575b7f77adStw21770 		event->adt_smf_delete_pg.fmri = fmri;
35585b7f77adStw21770 		event->adt_smf_delete_pg.type = data->ed_type;
35595b7f77adStw21770 		break;
35605b7f77adStw21770 	case ADT_smf_delete_prop:
35615b7f77adStw21770 		event->adt_smf_delete_prop.auth_used = auth_used;
35625b7f77adStw21770 		event->adt_smf_delete_prop.fmri = fmri;
35635b7f77adStw21770 		break;
35645b7f77adStw21770 	case ADT_smf_delete_snap:
35655b7f77adStw21770 		event->adt_smf_delete_snap.auth_used = auth_used;
35665b7f77adStw21770 		event->adt_smf_delete_snap.fmri = fmri;
35675b7f77adStw21770 		event->adt_smf_delete_snap.name = data->ed_snapname;
35685b7f77adStw21770 		break;
35695b7f77adStw21770 	case ADT_smf_disable:
35705b7f77adStw21770 		event->adt_smf_disable.auth_used = auth_used;
35715b7f77adStw21770 		event->adt_smf_disable.fmri = fmri;
35725b7f77adStw21770 		break;
35735b7f77adStw21770 	case ADT_smf_enable:
35745b7f77adStw21770 		event->adt_smf_enable.auth_used = auth_used;
35755b7f77adStw21770 		event->adt_smf_enable.fmri = fmri;
35765b7f77adStw21770 		break;
35775b7f77adStw21770 	case ADT_smf_immediate_degrade:
35785b7f77adStw21770 		event->adt_smf_immediate_degrade.auth_used = auth_used;
35795b7f77adStw21770 		event->adt_smf_immediate_degrade.fmri = fmri;
35805b7f77adStw21770 		break;
35815b7f77adStw21770 	case ADT_smf_immediate_maintenance:
35825b7f77adStw21770 		event->adt_smf_immediate_maintenance.auth_used = auth_used;
35835b7f77adStw21770 		event->adt_smf_immediate_maintenance.fmri = fmri;
35845b7f77adStw21770 		break;
35855b7f77adStw21770 	case ADT_smf_immtmp_maintenance:
35865b7f77adStw21770 		event->adt_smf_immtmp_maintenance.auth_used = auth_used;
35875b7f77adStw21770 		event->adt_smf_immtmp_maintenance.fmri = fmri;
35885b7f77adStw21770 		break;
35895b7f77adStw21770 	case ADT_smf_maintenance:
35905b7f77adStw21770 		event->adt_smf_maintenance.auth_used = auth_used;
35915b7f77adStw21770 		event->adt_smf_maintenance.fmri = fmri;
35925b7f77adStw21770 		break;
35935b7f77adStw21770 	case ADT_smf_milestone:
35945b7f77adStw21770 		event->adt_smf_milestone.auth_used = auth_used;
35955b7f77adStw21770 		event->adt_smf_milestone.fmri = fmri;
35965b7f77adStw21770 		break;
35975b7f77adStw21770 	case ADT_smf_read_prop:
35985b7f77adStw21770 		event->adt_smf_read_prop.auth_used = auth_used;
35995b7f77adStw21770 		event->adt_smf_read_prop.fmri = fmri;
36005b7f77adStw21770 		break;
36015b7f77adStw21770 	case ADT_smf_refresh:
36025b7f77adStw21770 		event->adt_smf_refresh.auth_used = auth_used;
36035b7f77adStw21770 		event->adt_smf_refresh.fmri = fmri;
36045b7f77adStw21770 		break;
36055b7f77adStw21770 	case ADT_smf_restart:
36065b7f77adStw21770 		event->adt_smf_restart.auth_used = auth_used;
36075b7f77adStw21770 		event->adt_smf_restart.fmri = fmri;
36085b7f77adStw21770 		break;
36095b7f77adStw21770 	case ADT_smf_tmp_disable:
36105b7f77adStw21770 		event->adt_smf_tmp_disable.auth_used = auth_used;
36115b7f77adStw21770 		event->adt_smf_tmp_disable.fmri = fmri;
36125b7f77adStw21770 		break;
36135b7f77adStw21770 	case ADT_smf_tmp_enable:
36145b7f77adStw21770 		event->adt_smf_tmp_enable.auth_used = auth_used;
36155b7f77adStw21770 		event->adt_smf_tmp_enable.fmri = fmri;
36165b7f77adStw21770 		break;
36175b7f77adStw21770 	case ADT_smf_tmp_maintenance:
36185b7f77adStw21770 		event->adt_smf_tmp_maintenance.auth_used = auth_used;
36195b7f77adStw21770 		event->adt_smf_tmp_maintenance.fmri = fmri;
36205b7f77adStw21770 		break;
36215b7f77adStw21770 	default:
36225b7f77adStw21770 		abort();	/* Need to cover all SMF event IDs */
36235b7f77adStw21770 	}
36245b7f77adStw21770 
36255b7f77adStw21770 	if (adt_put_event(event, status, return_val) != 0) {
36265b7f77adStw21770 		uu_warn("_smf_audit_event failed to put event.  %s\n",
36275b7f77adStw21770 		    strerror(errno));
36285b7f77adStw21770 	}
36295b7f77adStw21770 	adt_free_event(event);
36305b7f77adStw21770 }
36315b7f77adStw21770 
36325b7f77adStw21770 /*
36335b7f77adStw21770  * Determine if the combination of the property group at pg_name and the
36345b7f77adStw21770  * property at prop_name are in the set of special startd properties.  If
36355b7f77adStw21770  * they are, a special audit event will be generated.
36365b7f77adStw21770  */
36375b7f77adStw21770 static void
36385b7f77adStw21770 special_property_event(audit_event_data_t *evdp, const char *prop_name,
36395b7f77adStw21770     char *pg_name, int status, int return_val, tx_commit_data_t *tx_data,
36405b7f77adStw21770     size_t cmd_no)
36415b7f77adStw21770 {
36425b7f77adStw21770 	au_event_t event_id;
36435b7f77adStw21770 	audit_special_prop_item_t search_key;
36445b7f77adStw21770 	audit_special_prop_item_t *found;
36455b7f77adStw21770 
36465b7f77adStw21770 	/* Use bsearch to find the special property information. */
36475b7f77adStw21770 	search_key.api_prop_name = prop_name;
36485b7f77adStw21770 	search_key.api_pg_name = pg_name;
36495b7f77adStw21770 	found = (audit_special_prop_item_t *)bsearch(&search_key,
36505b7f77adStw21770 	    special_props_list, SPECIAL_PROP_COUNT,
36515b7f77adStw21770 	    sizeof (special_props_list[0]), special_prop_compare);
36525b7f77adStw21770 	if (found == NULL) {
36535b7f77adStw21770 		/* Not a special property. */
36545b7f77adStw21770 		return;
36555b7f77adStw21770 	}
36565b7f77adStw21770 
36575b7f77adStw21770 	/* Get the event id */
36585b7f77adStw21770 	if (found->api_event_func == NULL) {
36595b7f77adStw21770 		event_id = found->api_event_id;
36605b7f77adStw21770 	} else {
36615b7f77adStw21770 		if ((*found->api_event_func)(tx_data, cmd_no,
36625b7f77adStw21770 		    found->api_pg_name, &event_id) < 0)
36635b7f77adStw21770 			return;
36645b7f77adStw21770 	}
36655b7f77adStw21770 
36665b7f77adStw21770 	/* Generate the event. */
36675b7f77adStw21770 	smf_audit_event(event_id, status, return_val, evdp);
36685b7f77adStw21770 }
36695b7f77adStw21770 #endif	/* NATIVE_BUILD */
36705b7f77adStw21770 
36715b7f77adStw21770 /*
36725b7f77adStw21770  * Return a pointer to a string containing all the values of the command
36735b7f77adStw21770  * specified by cmd_no with each value enclosed in quotes.  It is up to the
36745b7f77adStw21770  * caller to free the memory at the returned pointer.
36755b7f77adStw21770  */
36765b7f77adStw21770 static char *
36775b7f77adStw21770 generate_value_list(tx_commit_data_t *tx_data, size_t cmd_no)
36785b7f77adStw21770 {
36795b7f77adStw21770 	const char *cp;
36805b7f77adStw21770 	const char *cur_value;
36815b7f77adStw21770 	size_t byte_count = 0;
36825b7f77adStw21770 	uint32_t i;
36835b7f77adStw21770 	uint32_t nvalues;
36845b7f77adStw21770 	size_t str_size = 0;
36855b7f77adStw21770 	char *values = NULL;
36865b7f77adStw21770 	char *vp;
36875b7f77adStw21770 
36885b7f77adStw21770 	if (tx_cmd_nvalues(tx_data, cmd_no, &nvalues) != REP_PROTOCOL_SUCCESS)
36895b7f77adStw21770 		return (NULL);
36905b7f77adStw21770 	/*
36915b7f77adStw21770 	 * First determine the size of the buffer that we will need.  We
36925b7f77adStw21770 	 * will represent each property value surrounded by quotes with a
36935b7f77adStw21770 	 * space separating the values.  Thus, we need to find the total
36945b7f77adStw21770 	 * size of all the value strings and add 3 for each value.
36955b7f77adStw21770 	 *
36965b7f77adStw21770 	 * There is one catch, though.  We need to escape any internal
36975b7f77adStw21770 	 * quote marks in the values.  So for each quote in the value we
36985b7f77adStw21770 	 * need to add another byte to the buffer size.
36995b7f77adStw21770 	 */
37005b7f77adStw21770 	for (i = 0; i < nvalues; i++) {
37015b7f77adStw21770 		if (tx_cmd_value(tx_data, cmd_no, i, &cur_value) !=
37025b7f77adStw21770 		    REP_PROTOCOL_SUCCESS)
37035b7f77adStw21770 			return (NULL);
37045b7f77adStw21770 		for (cp = cur_value; *cp != 0; cp++) {
37055b7f77adStw21770 			byte_count += (*cp == '"') ? 2 : 1;
37065b7f77adStw21770 		}
37075b7f77adStw21770 		byte_count += 3;	/* surrounding quotes & space */
37085b7f77adStw21770 	}
37095b7f77adStw21770 	byte_count++;		/* nul terminator */
37105b7f77adStw21770 	values = malloc(byte_count);
37115b7f77adStw21770 	if (values == NULL)
37125b7f77adStw21770 		return (NULL);
37135b7f77adStw21770 	*values = 0;
37145b7f77adStw21770 
37155b7f77adStw21770 	/* Now build up the string of values. */
37165b7f77adStw21770 	for (i = 0; i < nvalues; i++) {
37175b7f77adStw21770 		if (tx_cmd_value(tx_data, cmd_no, i, &cur_value) !=
37185b7f77adStw21770 		    REP_PROTOCOL_SUCCESS) {
37195b7f77adStw21770 			free(values);
37205b7f77adStw21770 			return (NULL);
37215b7f77adStw21770 		}
37225b7f77adStw21770 		(void) strlcat(values, "\"", byte_count);
37235b7f77adStw21770 		for (cp = cur_value, vp = values + strlen(values);
37245b7f77adStw21770 		    *cp != 0; cp++) {
37255b7f77adStw21770 			if (*cp == '"') {
37265b7f77adStw21770 				*vp++ = '\\';
37275b7f77adStw21770 				*vp++ = '"';
37285b7f77adStw21770 			} else {
37295b7f77adStw21770 				*vp++ = *cp;
37305b7f77adStw21770 			}
37315b7f77adStw21770 		}
37325b7f77adStw21770 		*vp = 0;
37335b7f77adStw21770 		str_size = strlcat(values, "\" ", byte_count);
37345b7f77adStw21770 		assert(str_size < byte_count);
37355b7f77adStw21770 	}
37365b7f77adStw21770 	if (str_size > 0)
37375b7f77adStw21770 		values[str_size - 1] = 0;	/* get rid of trailing space */
37385b7f77adStw21770 	return (values);
37395b7f77adStw21770 }
37405b7f77adStw21770 
37415b7f77adStw21770 /*
37425b7f77adStw21770  * generate_property_events takes the transaction commit data at tx_data
37435b7f77adStw21770  * and generates an audit event for each command.
37445b7f77adStw21770  *
37455b7f77adStw21770  * Native builds are done to create svc.configd-native.  This program runs
37465b7f77adStw21770  * only on the Solaris build machines to create the seed repository.  Thus,
37475b7f77adStw21770  * no audit events should be generated when running svc.configd-native.
37485b7f77adStw21770  */
37495b7f77adStw21770 static void
37505b7f77adStw21770 generate_property_events(
37515b7f77adStw21770 	tx_commit_data_t *tx_data,
37525b7f77adStw21770 	char *pg_fmri,		/* FMRI of property group */
37535b7f77adStw21770 	char *auth_string,
37545b7f77adStw21770 	int auth_status,
37555b7f77adStw21770 	int auth_ret_value)
37565b7f77adStw21770 {
37575b7f77adStw21770 #ifndef	NATIVE_BUILD
37585b7f77adStw21770 	enum rep_protocol_transaction_action action;
37595b7f77adStw21770 	audit_event_data_t audit_data;
37605b7f77adStw21770 	size_t count;
37615b7f77adStw21770 	size_t cmd_no;
37625b7f77adStw21770 	char *cp;
37635b7f77adStw21770 	au_event_t event_id;
37645b7f77adStw21770 	char fmri[REP_PROTOCOL_FMRI_LEN];
37655b7f77adStw21770 	char pg_name[REP_PROTOCOL_NAME_LEN];
37665b7f77adStw21770 	char *pg_end;		/* End of prop. group fmri */
37675b7f77adStw21770 	const char *prop_name;
37685b7f77adStw21770 	uint32_t ptype;
37695b7f77adStw21770 	char prop_type[3];
37705b7f77adStw21770 	enum rep_protocol_responseid rc;
37715b7f77adStw21770 	size_t sz_out;
37725b7f77adStw21770 
37735b7f77adStw21770 	/* Make sure we have something to do. */
37745b7f77adStw21770 	if (tx_data == NULL)
37755b7f77adStw21770 		return;
37765b7f77adStw21770 	if ((count = tx_cmd_count(tx_data)) == 0)
37775b7f77adStw21770 		return;
37785b7f77adStw21770 
37795b7f77adStw21770 	/* Copy the property group fmri */
37805b7f77adStw21770 	pg_end = fmri;
37815b7f77adStw21770 	pg_end += strlcpy(fmri, pg_fmri, sizeof (fmri));
37825b7f77adStw21770 
37835b7f77adStw21770 	/*
37845b7f77adStw21770 	 * Get the property group name.  It is the first component after
37855b7f77adStw21770 	 * the last occurance of SCF_FMRI_PROPERTYGRP_PREFIX in the fmri.
37865b7f77adStw21770 	 */
37875b7f77adStw21770 	cp = strstr(pg_fmri, SCF_FMRI_PROPERTYGRP_PREFIX);
37885b7f77adStw21770 	if (cp == NULL) {
37895b7f77adStw21770 		pg_name[0] = 0;
37905b7f77adStw21770 	} else {
37915b7f77adStw21770 		cp += strlen(SCF_FMRI_PROPERTYGRP_PREFIX);
37925b7f77adStw21770 		(void) strlcpy(pg_name, cp, sizeof (pg_name));
37935b7f77adStw21770 	}
37945b7f77adStw21770 
37955b7f77adStw21770 	audit_data.ed_auth = auth_string;
37965b7f77adStw21770 	audit_data.ed_fmri = fmri;
37975b7f77adStw21770 	audit_data.ed_type = prop_type;
37985b7f77adStw21770 
37995b7f77adStw21770 	/*
38005b7f77adStw21770 	 * Property type is two characters (see
38015b7f77adStw21770 	 * rep_protocol_value_type_t), so terminate the string.
38025b7f77adStw21770 	 */
38035b7f77adStw21770 	prop_type[2] = 0;
38045b7f77adStw21770 
38055b7f77adStw21770 	for (cmd_no = 0; cmd_no < count; cmd_no++) {
38065b7f77adStw21770 		/* Construct FMRI of the property */
38075b7f77adStw21770 		*pg_end = 0;
38085b7f77adStw21770 		if (tx_cmd_prop(tx_data, cmd_no, &prop_name) !=
38095b7f77adStw21770 		    REP_PROTOCOL_SUCCESS) {
38105b7f77adStw21770 			continue;
38115b7f77adStw21770 		}
38125b7f77adStw21770 		rc = rc_concat_fmri_element(fmri, sizeof (fmri), &sz_out,
38135b7f77adStw21770 		    prop_name, REP_PROTOCOL_ENTITY_PROPERTY);
38145b7f77adStw21770 		if (rc != REP_PROTOCOL_SUCCESS) {
38155b7f77adStw21770 			/*
38165b7f77adStw21770 			 * If we can't get the FMRI, we'll abandon this
38175b7f77adStw21770 			 * command
38185b7f77adStw21770 			 */
38195b7f77adStw21770 			continue;
38205b7f77adStw21770 		}
38215b7f77adStw21770 
38225b7f77adStw21770 		/* Generate special property event if necessary. */
38235b7f77adStw21770 		special_property_event(&audit_data, prop_name, pg_name,
38245b7f77adStw21770 		    auth_status, auth_ret_value, tx_data, cmd_no);
38255b7f77adStw21770 
38265b7f77adStw21770 		/* Capture rest of audit data. */
38275b7f77adStw21770 		if (tx_cmd_prop_type(tx_data, cmd_no, &ptype) !=
38285b7f77adStw21770 		    REP_PROTOCOL_SUCCESS) {
38295b7f77adStw21770 			continue;
38305b7f77adStw21770 		}
38315b7f77adStw21770 		prop_type[0] = REP_PROTOCOL_BASE_TYPE(ptype);
38325b7f77adStw21770 		prop_type[1] = REP_PROTOCOL_SUBTYPE(ptype);
38335b7f77adStw21770 		audit_data.ed_prop_value = generate_value_list(tx_data, cmd_no);
38345b7f77adStw21770 
38355b7f77adStw21770 		/* Determine the event type. */
38365b7f77adStw21770 		if (tx_cmd_action(tx_data, cmd_no, &action) !=
38375b7f77adStw21770 		    REP_PROTOCOL_SUCCESS) {
38385b7f77adStw21770 			free(audit_data.ed_prop_value);
38395b7f77adStw21770 			continue;
38405b7f77adStw21770 		}
38415b7f77adStw21770 		switch (action) {
38425b7f77adStw21770 		case REP_PROTOCOL_TX_ENTRY_NEW:
38435b7f77adStw21770 			event_id = ADT_smf_create_prop;
38445b7f77adStw21770 			break;
38455b7f77adStw21770 		case REP_PROTOCOL_TX_ENTRY_CLEAR:
38465b7f77adStw21770 			event_id = ADT_smf_change_prop;
38475b7f77adStw21770 			break;
38485b7f77adStw21770 		case REP_PROTOCOL_TX_ENTRY_REPLACE:
38495b7f77adStw21770 			event_id = ADT_smf_change_prop;
38505b7f77adStw21770 			break;
38515b7f77adStw21770 		case REP_PROTOCOL_TX_ENTRY_DELETE:
38525b7f77adStw21770 			event_id = ADT_smf_delete_prop;
38535b7f77adStw21770 			break;
38545b7f77adStw21770 		default:
38555b7f77adStw21770 			assert(0);	/* Missing a case */
38565b7f77adStw21770 			free(audit_data.ed_prop_value);
38575b7f77adStw21770 			continue;
38585b7f77adStw21770 		}
38595b7f77adStw21770 
38605b7f77adStw21770 		/* Generate the event. */
38615b7f77adStw21770 		smf_audit_event(event_id, auth_status, auth_ret_value,
38625b7f77adStw21770 		    &audit_data);
38635b7f77adStw21770 		free(audit_data.ed_prop_value);
38645b7f77adStw21770 	}
38655b7f77adStw21770 #endif /* NATIVE_BUILD */
38667c478bd9Sstevel@tonic-gate }
38677c478bd9Sstevel@tonic-gate 
38687c478bd9Sstevel@tonic-gate /*
38697c478bd9Sstevel@tonic-gate  * Fails with
38707c478bd9Sstevel@tonic-gate  *   _DELETED - node has been deleted
38717c478bd9Sstevel@tonic-gate  *   _NOT_SET - npp is reset
38727c478bd9Sstevel@tonic-gate  *   _NOT_APPLICABLE - type is _PROPERTYGRP
38737c478bd9Sstevel@tonic-gate  *   _INVALID_TYPE - node is corrupt or type is invalid
38747c478bd9Sstevel@tonic-gate  *   _TYPE_MISMATCH - node cannot have children of type type
38757c478bd9Sstevel@tonic-gate  *   _BAD_REQUEST - name is invalid
38767c478bd9Sstevel@tonic-gate  *		    cannot create children for this type of node
38777c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES - out of memory, or could not allocate new id
38787c478bd9Sstevel@tonic-gate  *   _PERMISSION_DENIED
38797c478bd9Sstevel@tonic-gate  *   _BACKEND_ACCESS
38807c478bd9Sstevel@tonic-gate  *   _BACKEND_READONLY
38817c478bd9Sstevel@tonic-gate  *   _EXISTS - child already exists
38825b7f77adStw21770  *   _TRUNCATED - truncated FMRI for the audit record
38837c478bd9Sstevel@tonic-gate  */
38847c478bd9Sstevel@tonic-gate int
38857c478bd9Sstevel@tonic-gate rc_node_create_child(rc_node_ptr_t *npp, uint32_t type, const char *name,
38867c478bd9Sstevel@tonic-gate     rc_node_ptr_t *cpp)
38877c478bd9Sstevel@tonic-gate {
38887c478bd9Sstevel@tonic-gate 	rc_node_t *np;
38897c478bd9Sstevel@tonic-gate 	rc_node_t *cp = NULL;
3890a4dc1477STom Whitten 	int rc;
3891a4dc1477STom Whitten 	perm_status_t perm_rc;
38925b7f77adStw21770 	size_t sz_out;
38935b7f77adStw21770 	char fmri[REP_PROTOCOL_FMRI_LEN];
38945b7f77adStw21770 	audit_event_data_t audit_data;
38957c478bd9Sstevel@tonic-gate 
38967c478bd9Sstevel@tonic-gate 	rc_node_clear(cpp, 0);
38977c478bd9Sstevel@tonic-gate 
3898a4dc1477STom Whitten 	/*
3899a4dc1477STom Whitten 	 * rc_node_modify_permission_check() must be called before the node
3900a4dc1477STom Whitten 	 * is locked.  This is because the library functions that check
3901a4dc1477STom Whitten 	 * authorizations can trigger calls back into configd.
3902a4dc1477STom Whitten 	 */
39035b7f77adStw21770 	perm_rc = rc_node_modify_permission_check(&audit_data.ed_auth);
3904a4dc1477STom Whitten 	switch (perm_rc) {
3905a4dc1477STom Whitten 	case PERM_DENIED:
3906a4dc1477STom Whitten 		/*
3907a4dc1477STom Whitten 		 * We continue in this case, so that an audit event can be
3908a4dc1477STom Whitten 		 * generated later in the function.
3909a4dc1477STom Whitten 		 */
3910a4dc1477STom Whitten 		break;
3911a4dc1477STom Whitten 	case PERM_GRANTED:
3912a4dc1477STom Whitten 		break;
3913a4dc1477STom Whitten 	case PERM_GONE:
3914a4dc1477STom Whitten 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
3915a4dc1477STom Whitten 	case PERM_FAIL:
3916a4dc1477STom Whitten 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
3917a4dc1477STom Whitten 	default:
3918a4dc1477STom Whitten 		bad_error(rc_node_modify_permission_check, perm_rc);
3919a4dc1477STom Whitten 	}
39203eae19d9Swesolows 
3921a4dc1477STom Whitten 	RC_NODE_PTR_CHECK_LOCK_OR_FREE_RETURN(np, npp, audit_data.ed_auth);
39227c478bd9Sstevel@tonic-gate 
39235b7f77adStw21770 	audit_data.ed_fmri = fmri;
39245b7f77adStw21770 
39257c478bd9Sstevel@tonic-gate 	/*
39267c478bd9Sstevel@tonic-gate 	 * there is a separate interface for creating property groups
39277c478bd9Sstevel@tonic-gate 	 */
39287c478bd9Sstevel@tonic-gate 	if (type == REP_PROTOCOL_ENTITY_PROPERTYGRP) {
39297c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
39305b7f77adStw21770 		free(audit_data.ed_auth);
39317c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
39327c478bd9Sstevel@tonic-gate 	}
39337c478bd9Sstevel@tonic-gate 
39347c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
39357c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
39367c478bd9Sstevel@tonic-gate 		np = np->rn_cchain[0];
3937a4dc1477STom Whitten 		if ((rc = rc_node_check_and_lock(np)) != REP_PROTOCOL_SUCCESS) {
3938a4dc1477STom Whitten 			free(audit_data.ed_auth);
3939a4dc1477STom Whitten 			return (rc);
3940a4dc1477STom Whitten 		}
39417c478bd9Sstevel@tonic-gate 	}
39427c478bd9Sstevel@tonic-gate 
39437c478bd9Sstevel@tonic-gate 	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
39447c478bd9Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS) {
39457c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
39465b7f77adStw21770 		free(audit_data.ed_auth);
39477c478bd9Sstevel@tonic-gate 		return (rc);
39487c478bd9Sstevel@tonic-gate 	}
39497c478bd9Sstevel@tonic-gate 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS) {
39507c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
39515b7f77adStw21770 		free(audit_data.ed_auth);
39527c478bd9Sstevel@tonic-gate 		return (rc);
39537c478bd9Sstevel@tonic-gate 	}
39547c478bd9Sstevel@tonic-gate 
39555b7f77adStw21770 	if ((rc = rc_get_fmri_and_concat(np, fmri, sizeof (fmri), &sz_out,
39565b7f77adStw21770 	    name, type)) != REP_PROTOCOL_SUCCESS) {
39575b7f77adStw21770 		(void) pthread_mutex_unlock(&np->rn_lock);
39585b7f77adStw21770 		free(audit_data.ed_auth);
39595b7f77adStw21770 		return (rc);
39605b7f77adStw21770 	}
3961a4dc1477STom Whitten 	if (perm_rc == PERM_DENIED) {
39627c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
39635b7f77adStw21770 		smf_audit_event(ADT_smf_create, ADT_FAILURE,
39645b7f77adStw21770 		    ADT_FAIL_VALUE_AUTH, &audit_data);
39655b7f77adStw21770 		free(audit_data.ed_auth);
3966a4dc1477STom Whitten 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
39677c478bd9Sstevel@tonic-gate 	}
39687c478bd9Sstevel@tonic-gate 
39695b7f77adStw21770 	HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
39705b7f77adStw21770 	    audit_data.ed_auth);
39717c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
39727c478bd9Sstevel@tonic-gate 
39737c478bd9Sstevel@tonic-gate 	rc = object_create(np, type, name, &cp);
39747c478bd9Sstevel@tonic-gate 	assert(rc != REP_PROTOCOL_FAIL_NOT_APPLICABLE);
39757c478bd9Sstevel@tonic-gate 
39767c478bd9Sstevel@tonic-gate 	if (rc == REP_PROTOCOL_SUCCESS) {
39777c478bd9Sstevel@tonic-gate 		rc_node_assign(cpp, cp);
39787c478bd9Sstevel@tonic-gate 		rc_node_rele(cp);
39797c478bd9Sstevel@tonic-gate 	}
39807c478bd9Sstevel@tonic-gate 
39817c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
39827c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
39837c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
39847c478bd9Sstevel@tonic-gate 
39855b7f77adStw21770 	if (rc == REP_PROTOCOL_SUCCESS) {
39865b7f77adStw21770 		smf_audit_event(ADT_smf_create, ADT_SUCCESS, ADT_SUCCESS,
39875b7f77adStw21770 		    &audit_data);
39885b7f77adStw21770 	}
39895b7f77adStw21770 
39905b7f77adStw21770 	free(audit_data.ed_auth);
39915b7f77adStw21770 
39927c478bd9Sstevel@tonic-gate 	return (rc);
39937c478bd9Sstevel@tonic-gate }
39947c478bd9Sstevel@tonic-gate 
39957c478bd9Sstevel@tonic-gate int
39967c478bd9Sstevel@tonic-gate rc_node_create_child_pg(rc_node_ptr_t *npp, uint32_t type, const char *name,
39977c478bd9Sstevel@tonic-gate     const char *pgtype, uint32_t flags, rc_node_ptr_t *cpp)
39987c478bd9Sstevel@tonic-gate {
39997c478bd9Sstevel@tonic-gate 	rc_node_t *np;
40007c478bd9Sstevel@tonic-gate 	rc_node_t *cp;
40017c478bd9Sstevel@tonic-gate 	int rc;
40027c478bd9Sstevel@tonic-gate 	permcheck_t *pcp;
4003a4dc1477STom Whitten 	perm_status_t granted;
40045b7f77adStw21770 	char fmri[REP_PROTOCOL_FMRI_LEN];
40055b7f77adStw21770 	audit_event_data_t audit_data;
40065b7f77adStw21770 	au_event_t event_id;
40075b7f77adStw21770 	size_t sz_out;
40085b7f77adStw21770 
40095b7f77adStw21770 	audit_data.ed_auth = NULL;
40105b7f77adStw21770 	audit_data.ed_fmri = fmri;
40115b7f77adStw21770 	audit_data.ed_type = (char *)pgtype;
40127c478bd9Sstevel@tonic-gate 
40137c478bd9Sstevel@tonic-gate 	rc_node_clear(cpp, 0);
40147c478bd9Sstevel@tonic-gate 
40157c478bd9Sstevel@tonic-gate 	/* verify flags is valid */
40167c478bd9Sstevel@tonic-gate 	if (flags & ~SCF_PG_FLAG_NONPERSISTENT)
40177c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
40187c478bd9Sstevel@tonic-gate 
40197c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
40207c478bd9Sstevel@tonic-gate 
40217c478bd9Sstevel@tonic-gate 	if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
40227c478bd9Sstevel@tonic-gate 		rc_node_rele(np);
40237c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
40247c478bd9Sstevel@tonic-gate 	}
40257c478bd9Sstevel@tonic-gate 
40267c478bd9Sstevel@tonic-gate 	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
40277c478bd9Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS) {
40287c478bd9Sstevel@tonic-gate 		rc_node_rele(np);
40297c478bd9Sstevel@tonic-gate 		return (rc);
40307c478bd9Sstevel@tonic-gate 	}
40317c478bd9Sstevel@tonic-gate 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS ||
40327c478bd9Sstevel@tonic-gate 	    (rc = rc_check_pgtype_name(pgtype)) != REP_PROTOCOL_SUCCESS) {
40337c478bd9Sstevel@tonic-gate 		rc_node_rele(np);
40347c478bd9Sstevel@tonic-gate 		return (rc);
40357c478bd9Sstevel@tonic-gate 	}
40367c478bd9Sstevel@tonic-gate 
4037273264cdSdm120769 #ifdef NATIVE_BUILD
40385b7f77adStw21770 	if (!client_is_privileged()) {
40397c478bd9Sstevel@tonic-gate 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
40405b7f77adStw21770 	}
40417c478bd9Sstevel@tonic-gate #else
40425b7f77adStw21770 	if (flags & SCF_PG_FLAG_NONPERSISTENT) {
40435b7f77adStw21770 		event_id = ADT_smf_create_npg;
40445b7f77adStw21770 	} else {
40455b7f77adStw21770 		event_id = ADT_smf_create_pg;
40465b7f77adStw21770 	}
40475b7f77adStw21770 	if ((rc = rc_get_fmri_and_concat(np, fmri, sizeof (fmri), &sz_out,
40485b7f77adStw21770 	    name, REP_PROTOCOL_ENTITY_PROPERTYGRP)) != REP_PROTOCOL_SUCCESS) {
40495b7f77adStw21770 		rc_node_rele(np);
40505b7f77adStw21770 		return (rc);
40515b7f77adStw21770 	}
40525b7f77adStw21770 
40535b7f77adStw21770 	if (is_main_repository) {
40547c478bd9Sstevel@tonic-gate 		/* Must have .smf.modify or smf.modify.<type> authorization */
40557c478bd9Sstevel@tonic-gate 		pcp = pc_create();
40567c478bd9Sstevel@tonic-gate 		if (pcp != NULL) {
40577c478bd9Sstevel@tonic-gate 			rc = perm_add_enabling(pcp, AUTH_MODIFY);
40587c478bd9Sstevel@tonic-gate 
40597c478bd9Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS) {
40607c478bd9Sstevel@tonic-gate 				const char * const auth =
40617c478bd9Sstevel@tonic-gate 				    perm_auth_for_pgtype(pgtype);
40627c478bd9Sstevel@tonic-gate 
40637c478bd9Sstevel@tonic-gate 				if (auth != NULL)
40647c478bd9Sstevel@tonic-gate 					rc = perm_add_enabling(pcp, auth);
40657c478bd9Sstevel@tonic-gate 			}
40667c478bd9Sstevel@tonic-gate 
40677c478bd9Sstevel@tonic-gate 			/*
40687c478bd9Sstevel@tonic-gate 			 * .manage or $action_authorization can be used to
40697c478bd9Sstevel@tonic-gate 			 * create the actions pg and the general_ovr pg.
40707c478bd9Sstevel@tonic-gate 			 */
40717c478bd9Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS &&
40727c478bd9Sstevel@tonic-gate 			    (flags & SCF_PG_FLAG_NONPERSISTENT) != 0 &&
40737c478bd9Sstevel@tonic-gate 			    np->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE &&
40747c478bd9Sstevel@tonic-gate 			    ((strcmp(name, AUTH_PG_ACTIONS) == 0 &&
40757c478bd9Sstevel@tonic-gate 			    strcmp(pgtype, AUTH_PG_ACTIONS_TYPE) == 0) ||
40767c478bd9Sstevel@tonic-gate 			    (strcmp(name, AUTH_PG_GENERAL_OVR) == 0 &&
40777c478bd9Sstevel@tonic-gate 			    strcmp(pgtype, AUTH_PG_GENERAL_OVR_TYPE) == 0))) {
40787c478bd9Sstevel@tonic-gate 				rc = perm_add_enabling(pcp, AUTH_MANAGE);
40797c478bd9Sstevel@tonic-gate 
40807c478bd9Sstevel@tonic-gate 				if (rc == REP_PROTOCOL_SUCCESS)
40817c478bd9Sstevel@tonic-gate 					rc = perm_add_inst_action_auth(pcp, np);
40827c478bd9Sstevel@tonic-gate 			}
40837c478bd9Sstevel@tonic-gate 
40847c478bd9Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS) {
40857c478bd9Sstevel@tonic-gate 				granted = perm_granted(pcp);
40867c478bd9Sstevel@tonic-gate 
4087a4dc1477STom Whitten 				rc = map_granted_status(granted, pcp,
4088a4dc1477STom Whitten 				    &audit_data.ed_auth);
4089a4dc1477STom Whitten 				if (granted == PERM_GONE) {
4090a4dc1477STom Whitten 					/* No auditing if client gone. */
4091a4dc1477STom Whitten 					pc_free(pcp);
4092a4dc1477STom Whitten 					rc_node_rele(np);
4093a4dc1477STom Whitten 					return (rc);
40945b7f77adStw21770 				}
40957c478bd9Sstevel@tonic-gate 			}
40967c478bd9Sstevel@tonic-gate 
40977c478bd9Sstevel@tonic-gate 			pc_free(pcp);
40987c478bd9Sstevel@tonic-gate 		} else {
40997c478bd9Sstevel@tonic-gate 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
41007c478bd9Sstevel@tonic-gate 		}
41017c478bd9Sstevel@tonic-gate 
41025b7f77adStw21770 	} else {
41035b7f77adStw21770 		rc = REP_PROTOCOL_SUCCESS;
41045b7f77adStw21770 	}
41057c478bd9Sstevel@tonic-gate #endif /* NATIVE_BUILD */
41067c478bd9Sstevel@tonic-gate 
4107a4dc1477STom Whitten 
41087c478bd9Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS) {
41097c478bd9Sstevel@tonic-gate 		rc_node_rele(np);
4110a4dc1477STom Whitten 		if (rc != REP_PROTOCOL_FAIL_NO_RESOURCES) {
41115b7f77adStw21770 			smf_audit_event(event_id, ADT_FAILURE,
41125b7f77adStw21770 			    ADT_FAIL_VALUE_AUTH, &audit_data);
4113a4dc1477STom Whitten 		}
41145b7f77adStw21770 		if (audit_data.ed_auth != NULL)
41155b7f77adStw21770 			free(audit_data.ed_auth);
41167c478bd9Sstevel@tonic-gate 		return (rc);
41177c478bd9Sstevel@tonic-gate 	}
41187c478bd9Sstevel@tonic-gate 
41197c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
41205b7f77adStw21770 	HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
41215b7f77adStw21770 	    audit_data.ed_auth);
41227c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
41237c478bd9Sstevel@tonic-gate 
41247c478bd9Sstevel@tonic-gate 	rc = object_create_pg(np, type, name, pgtype, flags, &cp);
41257c478bd9Sstevel@tonic-gate 
41267c478bd9Sstevel@tonic-gate 	if (rc == REP_PROTOCOL_SUCCESS) {
41277c478bd9Sstevel@tonic-gate 		rc_node_assign(cpp, cp);
41287c478bd9Sstevel@tonic-gate 		rc_node_rele(cp);
41297c478bd9Sstevel@tonic-gate 	}
41307c478bd9Sstevel@tonic-gate 
41317c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
41327c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
41337c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
41347c478bd9Sstevel@tonic-gate 
41355b7f77adStw21770 	if (rc == REP_PROTOCOL_SUCCESS) {
41365b7f77adStw21770 		smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS,
41375b7f77adStw21770 		    &audit_data);
41385b7f77adStw21770 	}
41395b7f77adStw21770 	if (audit_data.ed_auth != NULL)
41405b7f77adStw21770 		free(audit_data.ed_auth);
41415b7f77adStw21770 
41427c478bd9Sstevel@tonic-gate 	return (rc);
41437c478bd9Sstevel@tonic-gate }
41447c478bd9Sstevel@tonic-gate 
41457c478bd9Sstevel@tonic-gate static void
41467c478bd9Sstevel@tonic-gate rc_pg_notify_fire(rc_node_pg_notify_t *pnp)
41477c478bd9Sstevel@tonic-gate {
41487c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&rc_pg_notify_lock));
41497c478bd9Sstevel@tonic-gate 
41507c478bd9Sstevel@tonic-gate 	if (pnp->rnpn_pg != NULL) {
41517c478bd9Sstevel@tonic-gate 		uu_list_remove(pnp->rnpn_pg->rn_pg_notify_list, pnp);
41527c478bd9Sstevel@tonic-gate 		(void) close(pnp->rnpn_fd);
41537c478bd9Sstevel@tonic-gate 
41547c478bd9Sstevel@tonic-gate 		pnp->rnpn_pg = NULL;
41557c478bd9Sstevel@tonic-gate 		pnp->rnpn_fd = -1;
41567c478bd9Sstevel@tonic-gate 	} else {
41577c478bd9Sstevel@tonic-gate 		assert(pnp->rnpn_fd == -1);
41587c478bd9Sstevel@tonic-gate 	}
41597c478bd9Sstevel@tonic-gate }
41607c478bd9Sstevel@tonic-gate 
41617c478bd9Sstevel@tonic-gate static void
41627c478bd9Sstevel@tonic-gate rc_notify_node_delete(rc_notify_delete_t *ndp, rc_node_t *np_arg)
41637c478bd9Sstevel@tonic-gate {
41647c478bd9Sstevel@tonic-gate 	rc_node_t *svc = NULL;
41657c478bd9Sstevel@tonic-gate 	rc_node_t *inst = NULL;
41667c478bd9Sstevel@tonic-gate 	rc_node_t *pg = NULL;
41677c478bd9Sstevel@tonic-gate 	rc_node_t *np = np_arg;
41687c478bd9Sstevel@tonic-gate 	rc_node_t *nnp;
41697c478bd9Sstevel@tonic-gate 
41707c478bd9Sstevel@tonic-gate 	while (svc == NULL) {
41717c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
41727c478bd9Sstevel@tonic-gate 		if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
41737c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
41747c478bd9Sstevel@tonic-gate 			goto cleanup;
41757c478bd9Sstevel@tonic-gate 		}
41767c478bd9Sstevel@tonic-gate 		nnp = np->rn_parent;
41777c478bd9Sstevel@tonic-gate 		rc_node_hold_locked(np);	/* hold it in place */
41787c478bd9Sstevel@tonic-gate 
41797c478bd9Sstevel@tonic-gate 		switch (np->rn_id.rl_type) {
41807c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_ENTITY_PROPERTYGRP:
41817c478bd9Sstevel@tonic-gate 			assert(pg == NULL);
41827c478bd9Sstevel@tonic-gate 			pg = np;
41837c478bd9Sstevel@tonic-gate 			break;
41847c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_ENTITY_INSTANCE:
41857c478bd9Sstevel@tonic-gate 			assert(inst == NULL);
41867c478bd9Sstevel@tonic-gate 			inst = np;
41877c478bd9Sstevel@tonic-gate 			break;
41887c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_ENTITY_SERVICE:
41897c478bd9Sstevel@tonic-gate 			assert(svc == NULL);
41907c478bd9Sstevel@tonic-gate 			svc = np;
41917c478bd9Sstevel@tonic-gate 			break;
41927c478bd9Sstevel@tonic-gate 		default:
41937c478bd9Sstevel@tonic-gate 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
41947c478bd9Sstevel@tonic-gate 			rc_node_rele_locked(np);
41957c478bd9Sstevel@tonic-gate 			goto cleanup;
41967c478bd9Sstevel@tonic-gate 		}
41977c478bd9Sstevel@tonic-gate 
41987c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
41997c478bd9Sstevel@tonic-gate 
42007c478bd9Sstevel@tonic-gate 		np = nnp;
42017c478bd9Sstevel@tonic-gate 		if (np == NULL)
42027c478bd9Sstevel@tonic-gate 			goto cleanup;
42037c478bd9Sstevel@tonic-gate 	}
42047c478bd9Sstevel@tonic-gate 
42057c478bd9Sstevel@tonic-gate 	rc_notify_deletion(ndp,
42067c478bd9Sstevel@tonic-gate 	    svc->rn_name,
42077c478bd9Sstevel@tonic-gate 	    inst != NULL ? inst->rn_name : NULL,
42087c478bd9Sstevel@tonic-gate 	    pg != NULL ? pg->rn_name : NULL);
42097c478bd9Sstevel@tonic-gate 
42107c478bd9Sstevel@tonic-gate 	ndp = NULL;
42117c478bd9Sstevel@tonic-gate 
42127c478bd9Sstevel@tonic-gate cleanup:
42137c478bd9Sstevel@tonic-gate 	if (ndp != NULL)
42147c478bd9Sstevel@tonic-gate 		uu_free(ndp);
42157c478bd9Sstevel@tonic-gate 
42167c478bd9Sstevel@tonic-gate 	for (;;) {
42177c478bd9Sstevel@tonic-gate 		if (svc != NULL) {
42187c478bd9Sstevel@tonic-gate 			np = svc;
42197c478bd9Sstevel@tonic-gate 			svc = NULL;
42207c478bd9Sstevel@tonic-gate 		} else if (inst != NULL) {
42217c478bd9Sstevel@tonic-gate 			np = inst;
42227c478bd9Sstevel@tonic-gate 			inst = NULL;
42237c478bd9Sstevel@tonic-gate 		} else if (pg != NULL) {
42247c478bd9Sstevel@tonic-gate 			np = pg;
42257c478bd9Sstevel@tonic-gate 			pg = NULL;
42267c478bd9Sstevel@tonic-gate 		} else
42277c478bd9Sstevel@tonic-gate 			break;
42287c478bd9Sstevel@tonic-gate 
42297c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
42307c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_USING_PARENT);
42317c478bd9Sstevel@tonic-gate 		rc_node_rele_locked(np);
42327c478bd9Sstevel@tonic-gate 	}
42337c478bd9Sstevel@tonic-gate }
42347c478bd9Sstevel@tonic-gate 
42357c478bd9Sstevel@tonic-gate /*
42366643e1ffSbustos  * Hold RC_NODE_DYING_FLAGS on np's descendents.  If andformer is true, do
42376643e1ffSbustos  * the same down the rn_former chain.
42387c478bd9Sstevel@tonic-gate  */
42397c478bd9Sstevel@tonic-gate static void
42407c478bd9Sstevel@tonic-gate rc_node_delete_hold(rc_node_t *np, int andformer)
42417c478bd9Sstevel@tonic-gate {
42427c478bd9Sstevel@tonic-gate 	rc_node_t *cp;
42437c478bd9Sstevel@tonic-gate 
42447c478bd9Sstevel@tonic-gate again:
42457c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
42467c478bd9Sstevel@tonic-gate 	assert((np->rn_flags & RC_NODE_DYING_FLAGS) == RC_NODE_DYING_FLAGS);
42477c478bd9Sstevel@tonic-gate 
42487c478bd9Sstevel@tonic-gate 	for (cp = uu_list_first(np->rn_children); cp != NULL;
42497c478bd9Sstevel@tonic-gate 	    cp = uu_list_next(np->rn_children, cp)) {
42507c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
42517c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
42527c478bd9Sstevel@tonic-gate 		if (!rc_node_hold_flag(cp, RC_NODE_DYING_FLAGS)) {
42537c478bd9Sstevel@tonic-gate 			/*
42547c478bd9Sstevel@tonic-gate 			 * already marked as dead -- can't happen, since that
42557c478bd9Sstevel@tonic-gate 			 * would require setting RC_NODE_CHILDREN_CHANGING
42567c478bd9Sstevel@tonic-gate 			 * in np, and we're holding that...
42577c478bd9Sstevel@tonic-gate 			 */
42587c478bd9Sstevel@tonic-gate 			abort();
42597c478bd9Sstevel@tonic-gate 		}
42607c478bd9Sstevel@tonic-gate 		rc_node_delete_hold(cp, andformer);	/* recurse, drop lock */
42617c478bd9Sstevel@tonic-gate 
42627c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
42637c478bd9Sstevel@tonic-gate 	}
42647c478bd9Sstevel@tonic-gate 	if (andformer && (cp = np->rn_former) != NULL) {
42657c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
42667c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
42677c478bd9Sstevel@tonic-gate 		if (!rc_node_hold_flag(cp, RC_NODE_DYING_FLAGS))
42687c478bd9Sstevel@tonic-gate 			abort();		/* can't happen, see above */
42697c478bd9Sstevel@tonic-gate 		np = cp;
42707c478bd9Sstevel@tonic-gate 		goto again;		/* tail-recurse down rn_former */
42717c478bd9Sstevel@tonic-gate 	}
42727c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
42737c478bd9Sstevel@tonic-gate }
42747c478bd9Sstevel@tonic-gate 
42757c478bd9Sstevel@tonic-gate /*
42767c478bd9Sstevel@tonic-gate  * N.B.:  this function drops np->rn_lock on the way out.
42777c478bd9Sstevel@tonic-gate  */
42787c478bd9Sstevel@tonic-gate static void
42797c478bd9Sstevel@tonic-gate rc_node_delete_rele(rc_node_t *np, int andformer)
42807c478bd9Sstevel@tonic-gate {
42817c478bd9Sstevel@tonic-gate 	rc_node_t *cp;
42827c478bd9Sstevel@tonic-gate 
42837c478bd9Sstevel@tonic-gate again:
42847c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
42857c478bd9Sstevel@tonic-gate 	assert((np->rn_flags & RC_NODE_DYING_FLAGS) == RC_NODE_DYING_FLAGS);
42867c478bd9Sstevel@tonic-gate 
42877c478bd9Sstevel@tonic-gate 	for (cp = uu_list_first(np->rn_children); cp != NULL;
42887c478bd9Sstevel@tonic-gate 	    cp = uu_list_next(np->rn_children, cp)) {
42897c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
42907c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
42917c478bd9Sstevel@tonic-gate 		rc_node_delete_rele(cp, andformer);	/* recurse, drop lock */
42927c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
42937c478bd9Sstevel@tonic-gate 	}
42947c478bd9Sstevel@tonic-gate 	if (andformer && (cp = np->rn_former) != NULL) {
42957c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
42967c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
42977c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
42987c478bd9Sstevel@tonic-gate 
42997c478bd9Sstevel@tonic-gate 		np = cp;
43007c478bd9Sstevel@tonic-gate 		goto again;		/* tail-recurse down rn_former */
43017c478bd9Sstevel@tonic-gate 	}
43027c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
43037c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
43047c478bd9Sstevel@tonic-gate }
43057c478bd9Sstevel@tonic-gate 
43067c478bd9Sstevel@tonic-gate static void
43077c478bd9Sstevel@tonic-gate rc_node_finish_delete(rc_node_t *cp)
43087c478bd9Sstevel@tonic-gate {
43097c478bd9Sstevel@tonic-gate 	cache_bucket_t *bp;
43107c478bd9Sstevel@tonic-gate 	rc_node_pg_notify_t *pnp;
43117c478bd9Sstevel@tonic-gate 
43127c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&cp->rn_lock));
43137c478bd9Sstevel@tonic-gate 
43147c478bd9Sstevel@tonic-gate 	if (!(cp->rn_flags & RC_NODE_OLD)) {
43157c478bd9Sstevel@tonic-gate 		assert(cp->rn_flags & RC_NODE_IN_PARENT);
43167c478bd9Sstevel@tonic-gate 		if (!rc_node_wait_flag(cp, RC_NODE_USING_PARENT)) {
43177c478bd9Sstevel@tonic-gate 			abort();		/* can't happen, see above */
43187c478bd9Sstevel@tonic-gate 		}
43197c478bd9Sstevel@tonic-gate 		cp->rn_flags &= ~RC_NODE_IN_PARENT;
43207c478bd9Sstevel@tonic-gate 		cp->rn_parent = NULL;
43215b7f77adStw21770 		rc_node_free_fmri(cp);
43227c478bd9Sstevel@tonic-gate 	}
43237c478bd9Sstevel@tonic-gate 
43247c478bd9Sstevel@tonic-gate 	cp->rn_flags |= RC_NODE_DEAD;
43257c478bd9Sstevel@tonic-gate 
43267c478bd9Sstevel@tonic-gate 	/*
43277c478bd9Sstevel@tonic-gate 	 * If this node is not out-dated, we need to remove it from
43287c478bd9Sstevel@tonic-gate 	 * the notify list and cache hash table.
43297c478bd9Sstevel@tonic-gate 	 */
43307c478bd9Sstevel@tonic-gate 	if (!(cp->rn_flags & RC_NODE_OLD)) {
43317c478bd9Sstevel@tonic-gate 		assert(cp->rn_refs > 0);	/* can't go away yet */
43327c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&cp->rn_lock);
43337c478bd9Sstevel@tonic-gate 
43347c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&rc_pg_notify_lock);
43357c478bd9Sstevel@tonic-gate 		while ((pnp = uu_list_first(cp->rn_pg_notify_list)) != NULL)
43367c478bd9Sstevel@tonic-gate 			rc_pg_notify_fire(pnp);
43377c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
43387c478bd9Sstevel@tonic-gate 		rc_notify_remove_node(cp);
43397c478bd9Sstevel@tonic-gate 
43407c478bd9Sstevel@tonic-gate 		bp = cache_hold(cp->rn_hash);
43417c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
43427c478bd9Sstevel@tonic-gate 		cache_remove_unlocked(bp, cp);
43437c478bd9Sstevel@tonic-gate 		cache_release(bp);
43447c478bd9Sstevel@tonic-gate 	}
43457c478bd9Sstevel@tonic-gate }
43467c478bd9Sstevel@tonic-gate 
43477c478bd9Sstevel@tonic-gate /*
43486643e1ffSbustos  * For each child, call rc_node_finish_delete() and recurse.  If andformer
43496643e1ffSbustos  * is set, also recurse down rn_former.  Finally release np, which might
43506643e1ffSbustos  * free it.
43517c478bd9Sstevel@tonic-gate  */
43527c478bd9Sstevel@tonic-gate static void
43537c478bd9Sstevel@tonic-gate rc_node_delete_children(rc_node_t *np, int andformer)
43547c478bd9Sstevel@tonic-gate {
43557c478bd9Sstevel@tonic-gate 	rc_node_t *cp;
43567c478bd9Sstevel@tonic-gate 
43577c478bd9Sstevel@tonic-gate again:
43587c478bd9Sstevel@tonic-gate 	assert(np->rn_refs > 0);
43597c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
43607c478bd9Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_DEAD);
43617c478bd9Sstevel@tonic-gate 
43627c478bd9Sstevel@tonic-gate 	while ((cp = uu_list_first(np->rn_children)) != NULL) {
43637c478bd9Sstevel@tonic-gate 		uu_list_remove(np->rn_children, cp);
43647c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
43657c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
43667c478bd9Sstevel@tonic-gate 		rc_node_hold_locked(cp);	/* hold while we recurse */
43677c478bd9Sstevel@tonic-gate 		rc_node_finish_delete(cp);
43687c478bd9Sstevel@tonic-gate 		rc_node_delete_children(cp, andformer);	/* drops lock + ref */
43697c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
43707c478bd9Sstevel@tonic-gate 	}
43717c478bd9Sstevel@tonic-gate 
43727c478bd9Sstevel@tonic-gate 	/*
4373addbbe95Srm88369 	 * When we drop cp's lock, all the children will be gone, so we
43747c478bd9Sstevel@tonic-gate 	 * can release DYING_FLAGS.
43757c478bd9Sstevel@tonic-gate 	 */
43767c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
43777c478bd9Sstevel@tonic-gate 	if (andformer && (cp = np->rn_former) != NULL) {
43787c478bd9Sstevel@tonic-gate 		np->rn_former = NULL;		/* unlink */
43797c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
43806643e1ffSbustos 
43816643e1ffSbustos 		/*
43826643e1ffSbustos 		 * Register the ephemeral reference created by reading
43836643e1ffSbustos 		 * np->rn_former into cp.  Note that the persistent
43846643e1ffSbustos 		 * reference (np->rn_former) is locked because we haven't
43856643e1ffSbustos 		 * dropped np's lock since we dropped its RC_NODE_IN_TX
43866643e1ffSbustos 		 * (via RC_NODE_DYING_FLAGS).
43876643e1ffSbustos 		 */
43886643e1ffSbustos 		rc_node_hold_ephemeral_locked(cp);
43896643e1ffSbustos 
43907c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
4391addbbe95Srm88369 		cp->rn_flags &= ~RC_NODE_ON_FORMER;
43927c478bd9Sstevel@tonic-gate 
43937c478bd9Sstevel@tonic-gate 		rc_node_hold_locked(cp);	/* hold while we loop */
43947c478bd9Sstevel@tonic-gate 
43957c478bd9Sstevel@tonic-gate 		rc_node_finish_delete(cp);
43967c478bd9Sstevel@tonic-gate 
43977c478bd9Sstevel@tonic-gate 		rc_node_rele(np);		/* drop the old reference */
43987c478bd9Sstevel@tonic-gate 
43997c478bd9Sstevel@tonic-gate 		np = cp;
44007c478bd9Sstevel@tonic-gate 		goto again;		/* tail-recurse down rn_former */
44017c478bd9Sstevel@tonic-gate 	}
44027c478bd9Sstevel@tonic-gate 	rc_node_rele_locked(np);
44037c478bd9Sstevel@tonic-gate }
44047c478bd9Sstevel@tonic-gate 
44056643e1ffSbustos /*
44066643e1ffSbustos  * The last client or child reference to np, which must be either
44076643e1ffSbustos  * RC_NODE_OLD or RC_NODE_DEAD, has been destroyed.  We'll destroy any
44086643e1ffSbustos  * remaining references (e.g., rn_former) and call rc_node_destroy() to
44096643e1ffSbustos  * free np.
44106643e1ffSbustos  */
44117c478bd9Sstevel@tonic-gate static void
44126643e1ffSbustos rc_node_no_client_refs(rc_node_t *np)
44137c478bd9Sstevel@tonic-gate {
44147c478bd9Sstevel@tonic-gate 	int unrefed;
44156643e1ffSbustos 	rc_node_t *current, *cur;
44167c478bd9Sstevel@tonic-gate 
44177c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
44187c478bd9Sstevel@tonic-gate 	assert(np->rn_refs == 0);
44197c478bd9Sstevel@tonic-gate 	assert(np->rn_other_refs == 0);
44207c478bd9Sstevel@tonic-gate 	assert(np->rn_other_refs_held == 0);
44217c478bd9Sstevel@tonic-gate 
44227c478bd9Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_DEAD) {
44236643e1ffSbustos 		/*
44246643e1ffSbustos 		 * The node is DEAD, so the deletion code should have
44256643e1ffSbustos 		 * destroyed all rn_children or rn_former references.
44266643e1ffSbustos 		 * Since the last client or child reference has been
44276643e1ffSbustos 		 * destroyed, we're free to destroy np.  Unless another
44286643e1ffSbustos 		 * thread has an ephemeral reference, in which case we'll
44296643e1ffSbustos 		 * pass the buck.
44306643e1ffSbustos 		 */
44316643e1ffSbustos 		if (np->rn_erefs > 1) {
44326643e1ffSbustos 			--np->rn_erefs;
44336643e1ffSbustos 			NODE_UNLOCK(np);
44346643e1ffSbustos 			return;
44356643e1ffSbustos 		}
44366643e1ffSbustos 
44377c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
44387c478bd9Sstevel@tonic-gate 		rc_node_destroy(np);
44397c478bd9Sstevel@tonic-gate 		return;
44407c478bd9Sstevel@tonic-gate 	}
44417c478bd9Sstevel@tonic-gate 
44426643e1ffSbustos 	/* We only collect DEAD and OLD nodes, thank you. */
44437c478bd9Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_OLD);
44446643e1ffSbustos 
44456643e1ffSbustos 	/*
44466643e1ffSbustos 	 * RC_NODE_UNREFED keeps multiple threads from processing OLD
44476643e1ffSbustos 	 * nodes.  But it's vulnerable to unfriendly scheduling, so full
44486643e1ffSbustos 	 * use of rn_erefs should supersede it someday.
44496643e1ffSbustos 	 */
44507c478bd9Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_UNREFED) {
44517c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
44527c478bd9Sstevel@tonic-gate 		return;
44537c478bd9Sstevel@tonic-gate 	}
44547c478bd9Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_UNREFED;
44557c478bd9Sstevel@tonic-gate 
44566643e1ffSbustos 	/*
44576643e1ffSbustos 	 * Now we'll remove the node from the rn_former chain and take its
44586643e1ffSbustos 	 * DYING_FLAGS.
44596643e1ffSbustos 	 */
44607c478bd9Sstevel@tonic-gate 
44617c478bd9Sstevel@tonic-gate 	/*
44626643e1ffSbustos 	 * Since this node is OLD, it should be on an rn_former chain.  To
44636643e1ffSbustos 	 * remove it, we must find the current in-hash object and grab its
44646643e1ffSbustos 	 * RC_NODE_IN_TX flag to protect the entire rn_former chain.
44657c478bd9Sstevel@tonic-gate 	 */
44666643e1ffSbustos 
44676643e1ffSbustos 	(void) pthread_mutex_unlock(&np->rn_lock);
44686643e1ffSbustos 
44697c478bd9Sstevel@tonic-gate 	for (;;) {
44706643e1ffSbustos 		current = cache_lookup(&np->rn_id);
44716643e1ffSbustos 
44726643e1ffSbustos 		if (current == NULL) {
44737c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
44746643e1ffSbustos 
44757c478bd9Sstevel@tonic-gate 			if (np->rn_flags & RC_NODE_DEAD)
44767c478bd9Sstevel@tonic-gate 				goto died;
44776643e1ffSbustos 
44787c478bd9Sstevel@tonic-gate 			/*
44797c478bd9Sstevel@tonic-gate 			 * We are trying to unreference this node, but the
44807c478bd9Sstevel@tonic-gate 			 * owner of the former list does not exist.  It must
44817c478bd9Sstevel@tonic-gate 			 * be the case that another thread is deleting this
44827c478bd9Sstevel@tonic-gate 			 * entire sub-branch, but has not yet reached us.
44837c478bd9Sstevel@tonic-gate 			 * We will in short order be deleted.
44847c478bd9Sstevel@tonic-gate 			 */
44857c478bd9Sstevel@tonic-gate 			np->rn_flags &= ~RC_NODE_UNREFED;
44867c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
44877c478bd9Sstevel@tonic-gate 			return;
44887c478bd9Sstevel@tonic-gate 		}
44896643e1ffSbustos 
44906643e1ffSbustos 		if (current == np) {
44917c478bd9Sstevel@tonic-gate 			/*
44927c478bd9Sstevel@tonic-gate 			 * no longer unreferenced
44937c478bd9Sstevel@tonic-gate 			 */
44947c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
44957c478bd9Sstevel@tonic-gate 			np->rn_flags &= ~RC_NODE_UNREFED;
44966643e1ffSbustos 			/* held in cache_lookup() */
44977c478bd9Sstevel@tonic-gate 			rc_node_rele_locked(np);
44987c478bd9Sstevel@tonic-gate 			return;
44997c478bd9Sstevel@tonic-gate 		}
45006643e1ffSbustos 
45016643e1ffSbustos 		(void) pthread_mutex_lock(&current->rn_lock);
45026643e1ffSbustos 		if (current->rn_flags & RC_NODE_OLD) {
45036643e1ffSbustos 			/*
45046643e1ffSbustos 			 * current has been replaced since we looked it
45056643e1ffSbustos 			 * up.  Try again.
45066643e1ffSbustos 			 */
45076643e1ffSbustos 			/* held in cache_lookup() */
45086643e1ffSbustos 			rc_node_rele_locked(current);
45097c478bd9Sstevel@tonic-gate 			continue;
45107c478bd9Sstevel@tonic-gate 		}
45116643e1ffSbustos 
45126643e1ffSbustos 		if (!rc_node_hold_flag(current, RC_NODE_IN_TX)) {
45136643e1ffSbustos 			/*
45146643e1ffSbustos 			 * current has been deleted since we looked it up.  Try
45156643e1ffSbustos 			 * again.
45166643e1ffSbustos 			 */
45176643e1ffSbustos 			/* held in cache_lookup() */
45186643e1ffSbustos 			rc_node_rele_locked(current);
45196643e1ffSbustos 			continue;
45207c478bd9Sstevel@tonic-gate 		}
45217c478bd9Sstevel@tonic-gate 
45226643e1ffSbustos 		/*
45236643e1ffSbustos 		 * rc_node_hold_flag() might have dropped current's lock, so
45246643e1ffSbustos 		 * check OLD again.
45256643e1ffSbustos 		 */
45266643e1ffSbustos 		if (!(current->rn_flags & RC_NODE_OLD)) {
45276643e1ffSbustos 			/* Not old.  Stop looping. */
45286643e1ffSbustos 			(void) pthread_mutex_unlock(&current->rn_lock);
45296643e1ffSbustos 			break;
45306643e1ffSbustos 		}
45316643e1ffSbustos 
45326643e1ffSbustos 		rc_node_rele_flag(current, RC_NODE_IN_TX);
45336643e1ffSbustos 		rc_node_rele_locked(current);
45346643e1ffSbustos 	}
45356643e1ffSbustos 
45366643e1ffSbustos 	/* To take np's RC_NODE_DYING_FLAGS, we need its lock. */
45377c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
45386643e1ffSbustos 
45396643e1ffSbustos 	/*
45406643e1ffSbustos 	 * While we didn't have the lock, a thread may have added
45416643e1ffSbustos 	 * a reference or changed the flags.
45426643e1ffSbustos 	 */
45437c478bd9Sstevel@tonic-gate 	if (!(np->rn_flags & (RC_NODE_OLD | RC_NODE_DEAD)) ||
45447c478bd9Sstevel@tonic-gate 	    np->rn_refs != 0 || np->rn_other_refs != 0 ||
45457c478bd9Sstevel@tonic-gate 	    np->rn_other_refs_held != 0) {
45467c478bd9Sstevel@tonic-gate 		np->rn_flags &= ~RC_NODE_UNREFED;
45477c478bd9Sstevel@tonic-gate 
45486643e1ffSbustos 		(void) pthread_mutex_lock(&current->rn_lock);
45496643e1ffSbustos 		rc_node_rele_flag(current, RC_NODE_IN_TX);
45506643e1ffSbustos 		/* held by cache_lookup() */
45516643e1ffSbustos 		rc_node_rele_locked(current);
45527c478bd9Sstevel@tonic-gate 		return;
45537c478bd9Sstevel@tonic-gate 	}
45547c478bd9Sstevel@tonic-gate 
45557c478bd9Sstevel@tonic-gate 	if (!rc_node_hold_flag(np, RC_NODE_DYING_FLAGS)) {
45566643e1ffSbustos 		/*
45576643e1ffSbustos 		 * Someone deleted the node while we were waiting for
45586643e1ffSbustos 		 * DYING_FLAGS.  Undo the modifications to current.
45596643e1ffSbustos 		 */
45607c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
45617c478bd9Sstevel@tonic-gate 
45626643e1ffSbustos 		rc_node_rele_flag(current, RC_NODE_IN_TX);
45636643e1ffSbustos 		/* held by cache_lookup() */
45646643e1ffSbustos 		rc_node_rele_locked(current);
45657c478bd9Sstevel@tonic-gate 
45667c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
45677c478bd9Sstevel@tonic-gate 		goto died;
45687c478bd9Sstevel@tonic-gate 	}
45697c478bd9Sstevel@tonic-gate 
45706643e1ffSbustos 	/* Take RC_NODE_DYING_FLAGS on np's descendents. */
45716643e1ffSbustos 	rc_node_delete_hold(np, 0);		/* drops np->rn_lock */
45727c478bd9Sstevel@tonic-gate 
45736643e1ffSbustos 	/* Mark np DEAD.  This requires the lock. */
45747c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
45756643e1ffSbustos 
45766643e1ffSbustos 	/* Recheck for new references. */
45777c478bd9Sstevel@tonic-gate 	if (!(np->rn_flags & RC_NODE_OLD) ||
45787c478bd9Sstevel@tonic-gate 	    np->rn_refs != 0 || np->rn_other_refs != 0 ||
45797c478bd9Sstevel@tonic-gate 	    np->rn_other_refs_held != 0) {
45807c478bd9Sstevel@tonic-gate 		np->rn_flags &= ~RC_NODE_UNREFED;
45816643e1ffSbustos 		rc_node_delete_rele(np, 0);	/* drops np's lock */
45827c478bd9Sstevel@tonic-gate 
45836643e1ffSbustos 		(void) pthread_mutex_lock(&current->rn_lock);
45846643e1ffSbustos 		rc_node_rele_flag(current, RC_NODE_IN_TX);
45856643e1ffSbustos 		/* held by cache_lookup() */
45866643e1ffSbustos 		rc_node_rele_locked(current);
45877c478bd9Sstevel@tonic-gate 		return;
45887c478bd9Sstevel@tonic-gate 	}
45897c478bd9Sstevel@tonic-gate 
45907c478bd9Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_DEAD;
45917c478bd9Sstevel@tonic-gate 
45927c478bd9Sstevel@tonic-gate 	/*
45936643e1ffSbustos 	 * Delete the children.  This calls rc_node_rele_locked() on np at
45946643e1ffSbustos 	 * the end, so add a reference to keep the count from going
45956643e1ffSbustos 	 * negative.  It will recurse with RC_NODE_DEAD set, so we'll call
45966643e1ffSbustos 	 * rc_node_destroy() above, but RC_NODE_UNREFED is also set, so it
45976643e1ffSbustos 	 * shouldn't actually free() np.
45987c478bd9Sstevel@tonic-gate 	 */
45996643e1ffSbustos 	rc_node_hold_locked(np);
46006643e1ffSbustos 	rc_node_delete_children(np, 0);		/* unlocks np */
46016643e1ffSbustos 
46026643e1ffSbustos 	/* Remove np from current's rn_former chain. */
46036643e1ffSbustos 	(void) pthread_mutex_lock(&current->rn_lock);
46046643e1ffSbustos 	for (cur = current; cur != NULL && cur->rn_former != np;
46057c478bd9Sstevel@tonic-gate 	    cur = cur->rn_former)
46067c478bd9Sstevel@tonic-gate 		;
46077c478bd9Sstevel@tonic-gate 	assert(cur != NULL && cur != np);
46087c478bd9Sstevel@tonic-gate 
46097c478bd9Sstevel@tonic-gate 	cur->rn_former = np->rn_former;
46107c478bd9Sstevel@tonic-gate 	np->rn_former = NULL;
46117c478bd9Sstevel@tonic-gate 
46126643e1ffSbustos 	rc_node_rele_flag(current, RC_NODE_IN_TX);
46136643e1ffSbustos 	/* held by cache_lookup() */
46146643e1ffSbustos 	rc_node_rele_locked(current);
46157c478bd9Sstevel@tonic-gate 
46166643e1ffSbustos 	/* Clear ON_FORMER and UNREFED, and destroy. */
46177c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
46187c478bd9Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_ON_FORMER);
46197c478bd9Sstevel@tonic-gate 	np->rn_flags &= ~(RC_NODE_UNREFED | RC_NODE_ON_FORMER);
46206643e1ffSbustos 
46216643e1ffSbustos 	if (np->rn_erefs > 1) {
46226643e1ffSbustos 		/* Still referenced.  Stay execution. */
46236643e1ffSbustos 		--np->rn_erefs;
46246643e1ffSbustos 		NODE_UNLOCK(np);
46256643e1ffSbustos 		return;
46266643e1ffSbustos 	}
46276643e1ffSbustos 
46287c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
46297c478bd9Sstevel@tonic-gate 	rc_node_destroy(np);
46307c478bd9Sstevel@tonic-gate 	return;
46317c478bd9Sstevel@tonic-gate 
46327c478bd9Sstevel@tonic-gate died:
46336643e1ffSbustos 	/*
46346643e1ffSbustos 	 * Another thread marked np DEAD.  If there still aren't any
46356643e1ffSbustos 	 * persistent references, destroy the node.
46366643e1ffSbustos 	 */
46377c478bd9Sstevel@tonic-gate 	np->rn_flags &= ~RC_NODE_UNREFED;
46386643e1ffSbustos 
46397c478bd9Sstevel@tonic-gate 	unrefed = (np->rn_refs == 0 && np->rn_other_refs == 0 &&
46407c478bd9Sstevel@tonic-gate 	    np->rn_other_refs_held == 0);
46416643e1ffSbustos 
46426643e1ffSbustos 	if (np->rn_erefs > 0)
46436643e1ffSbustos 		--np->rn_erefs;
46446643e1ffSbustos 
46456643e1ffSbustos 	if (unrefed && np->rn_erefs > 0) {
46466643e1ffSbustos 		NODE_UNLOCK(np);
46476643e1ffSbustos 		return;
46486643e1ffSbustos 	}
46496643e1ffSbustos 
46507c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
46516643e1ffSbustos 
46527c478bd9Sstevel@tonic-gate 	if (unrefed)
46537c478bd9Sstevel@tonic-gate 		rc_node_destroy(np);
46547c478bd9Sstevel@tonic-gate }
46557c478bd9Sstevel@tonic-gate 
46565b7f77adStw21770 static au_event_t
46575b7f77adStw21770 get_delete_event_id(rep_protocol_entity_t entity, uint32_t pgflags)
46585b7f77adStw21770 {
46595b7f77adStw21770 	au_event_t	id = 0;
46605b7f77adStw21770 
46615b7f77adStw21770 #ifndef NATIVE_BUILD
46625b7f77adStw21770 	switch (entity) {
46635b7f77adStw21770 	case REP_PROTOCOL_ENTITY_SERVICE:
46645b7f77adStw21770 	case REP_PROTOCOL_ENTITY_INSTANCE:
46655b7f77adStw21770 		id = ADT_smf_delete;
46665b7f77adStw21770 		break;
46675b7f77adStw21770 	case REP_PROTOCOL_ENTITY_SNAPSHOT:
46685b7f77adStw21770 		id = ADT_smf_delete_snap;
46695b7f77adStw21770 		break;
46705b7f77adStw21770 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
46715b7f77adStw21770 	case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
46725b7f77adStw21770 		if (pgflags & SCF_PG_FLAG_NONPERSISTENT) {
46735b7f77adStw21770 			id = ADT_smf_delete_npg;
46745b7f77adStw21770 		} else {
46755b7f77adStw21770 			id = ADT_smf_delete_pg;
46765b7f77adStw21770 		}
46775b7f77adStw21770 		break;
46785b7f77adStw21770 	default:
46795b7f77adStw21770 		abort();
46805b7f77adStw21770 	}
46815b7f77adStw21770 #endif	/* NATIVE_BUILD */
46825b7f77adStw21770 	return (id);
46835b7f77adStw21770 }
46845b7f77adStw21770 
46857c478bd9Sstevel@tonic-gate /*
46867c478bd9Sstevel@tonic-gate  * Fails with
46877c478bd9Sstevel@tonic-gate  *   _NOT_SET
46887c478bd9Sstevel@tonic-gate  *   _DELETED
46897c478bd9Sstevel@tonic-gate  *   _BAD_REQUEST
46907c478bd9Sstevel@tonic-gate  *   _PERMISSION_DENIED
46917c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES
46925b7f77adStw21770  *   _TRUNCATED
46937c478bd9Sstevel@tonic-gate  * and whatever object_delete() fails with.
46947c478bd9Sstevel@tonic-gate  */
46957c478bd9Sstevel@tonic-gate int
46967c478bd9Sstevel@tonic-gate rc_node_delete(rc_node_ptr_t *npp)
46977c478bd9Sstevel@tonic-gate {
46987c478bd9Sstevel@tonic-gate 	rc_node_t *np, *np_orig;
46997c478bd9Sstevel@tonic-gate 	rc_node_t *pp = NULL;
47007c478bd9Sstevel@tonic-gate 	int rc;
47017c478bd9Sstevel@tonic-gate 	rc_node_pg_notify_t *pnp;
47027c478bd9Sstevel@tonic-gate 	cache_bucket_t *bp;
47037c478bd9Sstevel@tonic-gate 	rc_notify_delete_t *ndp;
47047c478bd9Sstevel@tonic-gate 	permcheck_t *pcp;
47057c478bd9Sstevel@tonic-gate 	int granted;
47065b7f77adStw21770 	au_event_t event_id = 0;
47075b7f77adStw21770 	size_t sz_out;
47085b7f77adStw21770 	audit_event_data_t audit_data;
47095b7f77adStw21770 	int audit_failure = 0;
47107c478bd9Sstevel@tonic-gate 
47117c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
47127c478bd9Sstevel@tonic-gate 
47135b7f77adStw21770 	audit_data.ed_fmri = NULL;
47145b7f77adStw21770 	audit_data.ed_auth = NULL;
47155b7f77adStw21770 	audit_data.ed_snapname = NULL;
47165b7f77adStw21770 	audit_data.ed_type = NULL;
47175b7f77adStw21770 
47187c478bd9Sstevel@tonic-gate 	switch (np->rn_id.rl_type) {
47197c478bd9Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_SERVICE:
47205b7f77adStw21770 		event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_SERVICE,
47215b7f77adStw21770 		    np->rn_pgflags);
47225b7f77adStw21770 		break;
47237c478bd9Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_INSTANCE:
47245b7f77adStw21770 		event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_INSTANCE,
47255b7f77adStw21770 		    np->rn_pgflags);
47265b7f77adStw21770 		break;
47277c478bd9Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_SNAPSHOT:
47285b7f77adStw21770 		event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_SNAPSHOT,
47295b7f77adStw21770 		    np->rn_pgflags);
47305b7f77adStw21770 		audit_data.ed_snapname = strdup(np->rn_name);
47315b7f77adStw21770 		if (audit_data.ed_snapname == NULL) {
47325b7f77adStw21770 			(void) pthread_mutex_unlock(&np->rn_lock);
47335b7f77adStw21770 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
47345b7f77adStw21770 		}
47357c478bd9Sstevel@tonic-gate 		break;			/* deletable */
47367c478bd9Sstevel@tonic-gate 
47377c478bd9Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_SCOPE:
47387c478bd9Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_SNAPLEVEL:
47397c478bd9Sstevel@tonic-gate 		/* Scopes and snaplevels are indelible. */
47407c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
47417c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
47427c478bd9Sstevel@tonic-gate 
47437c478bd9Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
47447c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
47457c478bd9Sstevel@tonic-gate 		np = np->rn_cchain[0];
47467c478bd9Sstevel@tonic-gate 		RC_NODE_CHECK_AND_LOCK(np);
47475b7f77adStw21770 		event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_CPROPERTYGRP,
47485b7f77adStw21770 		    np->rn_pgflags);
47497c478bd9Sstevel@tonic-gate 		break;
47507c478bd9Sstevel@tonic-gate 
47517c478bd9Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
47525b7f77adStw21770 		if (np->rn_id.rl_ids[ID_SNAPSHOT] == 0) {
47535b7f77adStw21770 			event_id =
47545b7f77adStw21770 			    get_delete_event_id(REP_PROTOCOL_ENTITY_PROPERTYGRP,
47555b7f77adStw21770 			    np->rn_pgflags);
47565b7f77adStw21770 			audit_data.ed_type = strdup(np->rn_type);
47575b7f77adStw21770 			if (audit_data.ed_type == NULL) {
47585b7f77adStw21770 				(void) pthread_mutex_unlock(&np->rn_lock);
47595b7f77adStw21770 				return (REP_PROTOCOL_FAIL_NO_RESOURCES);
47605b7f77adStw21770 			}
47617c478bd9Sstevel@tonic-gate 			break;
47625b7f77adStw21770 		}
47637c478bd9Sstevel@tonic-gate 
47647c478bd9Sstevel@tonic-gate 		/* Snapshot property groups are indelible. */
47657c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
47667c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
47677c478bd9Sstevel@tonic-gate 
47687c478bd9Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_PROPERTY:
47697c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
47707c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
47717c478bd9Sstevel@tonic-gate 
47727c478bd9Sstevel@tonic-gate 	default:
47737c478bd9Sstevel@tonic-gate 		assert(0);
47747c478bd9Sstevel@tonic-gate 		abort();
47757c478bd9Sstevel@tonic-gate 		break;
47767c478bd9Sstevel@tonic-gate 	}
47777c478bd9Sstevel@tonic-gate 
47785b7f77adStw21770 	audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
47795b7f77adStw21770 	if (audit_data.ed_fmri == NULL) {
47805b7f77adStw21770 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
47815b7f77adStw21770 		goto cleanout;
47825b7f77adStw21770 	}
47837c478bd9Sstevel@tonic-gate 	np_orig = np;
47847c478bd9Sstevel@tonic-gate 	rc_node_hold_locked(np);	/* simplifies rest of the code */
47857c478bd9Sstevel@tonic-gate 
47867c478bd9Sstevel@tonic-gate again:
47877c478bd9Sstevel@tonic-gate 	/*
47887c478bd9Sstevel@tonic-gate 	 * The following loop is to deal with the fact that snapshots and
47897c478bd9Sstevel@tonic-gate 	 * property groups are moving targets -- changes to them result
47907c478bd9Sstevel@tonic-gate 	 * in a new "child" node.  Since we can only delete from the top node,
47917c478bd9Sstevel@tonic-gate 	 * we have to loop until we have a non-RC_NODE_OLD version.
47927c478bd9Sstevel@tonic-gate 	 */
47937c478bd9Sstevel@tonic-gate 	for (;;) {
47947c478bd9Sstevel@tonic-gate 		if (!rc_node_wait_flag(np,
47957c478bd9Sstevel@tonic-gate 		    RC_NODE_IN_TX | RC_NODE_USING_PARENT)) {
47967c478bd9Sstevel@tonic-gate 			rc_node_rele_locked(np);
47975b7f77adStw21770 			rc = REP_PROTOCOL_FAIL_DELETED;
47985b7f77adStw21770 			goto cleanout;
47997c478bd9Sstevel@tonic-gate 		}
48007c478bd9Sstevel@tonic-gate 
48017c478bd9Sstevel@tonic-gate 		if (np->rn_flags & RC_NODE_OLD) {
48027c478bd9Sstevel@tonic-gate 			rc_node_rele_locked(np);
48037c478bd9Sstevel@tonic-gate 			np = cache_lookup(&np_orig->rn_id);
48047c478bd9Sstevel@tonic-gate 			assert(np != np_orig);
48057c478bd9Sstevel@tonic-gate 
48067c478bd9Sstevel@tonic-gate 			if (np == NULL) {
48077c478bd9Sstevel@tonic-gate 				rc = REP_PROTOCOL_FAIL_DELETED;
48087c478bd9Sstevel@tonic-gate 				goto fail;
48097c478bd9Sstevel@tonic-gate 			}
48107c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
48117c478bd9Sstevel@tonic-gate 			continue;
48127c478bd9Sstevel@tonic-gate 		}
48137c478bd9Sstevel@tonic-gate 
48147c478bd9Sstevel@tonic-gate 		if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
48157c478bd9Sstevel@tonic-gate 			rc_node_rele_locked(np);
48167c478bd9Sstevel@tonic-gate 			rc_node_clear(npp, 1);
48175b7f77adStw21770 			rc = REP_PROTOCOL_FAIL_DELETED;
48187c478bd9Sstevel@tonic-gate 		}
48197c478bd9Sstevel@tonic-gate 
48207c478bd9Sstevel@tonic-gate 		/*
48217c478bd9Sstevel@tonic-gate 		 * Mark our parent as children changing.  this call drops our
48227c478bd9Sstevel@tonic-gate 		 * lock and the RC_NODE_USING_PARENT flag, and returns with
48237c478bd9Sstevel@tonic-gate 		 * pp's lock held
48247c478bd9Sstevel@tonic-gate 		 */
48257c478bd9Sstevel@tonic-gate 		pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
48267c478bd9Sstevel@tonic-gate 		if (pp == NULL) {
48277c478bd9Sstevel@tonic-gate 			/* our parent is gone, we're going next... */
48287c478bd9Sstevel@tonic-gate 			rc_node_rele(np);
48297c478bd9Sstevel@tonic-gate 
48307c478bd9Sstevel@tonic-gate 			rc_node_clear(npp, 1);
48315b7f77adStw21770 			rc = REP_PROTOCOL_FAIL_DELETED;
48325b7f77adStw21770 			goto cleanout;
48337c478bd9Sstevel@tonic-gate 		}
48347c478bd9Sstevel@tonic-gate 
48357c478bd9Sstevel@tonic-gate 		rc_node_hold_locked(pp);		/* hold for later */
48367c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&pp->rn_lock);
48377c478bd9Sstevel@tonic-gate 
48387c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
48397c478bd9Sstevel@tonic-gate 		if (!(np->rn_flags & RC_NODE_OLD))
48407c478bd9Sstevel@tonic-gate 			break;			/* not old -- we're done */
48417c478bd9Sstevel@tonic-gate 
48427c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
48437c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&pp->rn_lock);
48447c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
48457c478bd9Sstevel@tonic-gate 		rc_node_rele_locked(pp);
48467c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
48477c478bd9Sstevel@tonic-gate 		continue;			/* loop around and try again */
48487c478bd9Sstevel@tonic-gate 	}
48497c478bd9Sstevel@tonic-gate 	/*
48507c478bd9Sstevel@tonic-gate 	 * Everyone out of the pool -- we grab everything but
48517c478bd9Sstevel@tonic-gate 	 * RC_NODE_USING_PARENT (including RC_NODE_DYING) to keep
48527c478bd9Sstevel@tonic-gate 	 * any changes from occurring while we are attempting to
48537c478bd9Sstevel@tonic-gate 	 * delete the node.
48547c478bd9Sstevel@tonic-gate 	 */
48557c478bd9Sstevel@tonic-gate 	if (!rc_node_hold_flag(np, RC_NODE_DYING_FLAGS)) {
48567c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
48577c478bd9Sstevel@tonic-gate 		rc = REP_PROTOCOL_FAIL_DELETED;
48587c478bd9Sstevel@tonic-gate 		goto fail;
48597c478bd9Sstevel@tonic-gate 	}
48607c478bd9Sstevel@tonic-gate 
48617c478bd9Sstevel@tonic-gate 	assert(!(np->rn_flags & RC_NODE_OLD));
48627c478bd9Sstevel@tonic-gate 
48635b7f77adStw21770 	if ((rc = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
48645b7f77adStw21770 	    REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
48655b7f77adStw21770 		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
4866582271e8Stw21770 		(void) pthread_mutex_unlock(&np->rn_lock);
48675b7f77adStw21770 		goto fail;
48685b7f77adStw21770 	}
4869273264cdSdm120769 
4870273264cdSdm120769 #ifdef NATIVE_BUILD
48715b7f77adStw21770 	if (!client_is_privileged()) {
4872273264cdSdm120769 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
48735b7f77adStw21770 	}
4874273264cdSdm120769 #else
48755b7f77adStw21770 	if (is_main_repository) {
48765b7f77adStw21770 		/* permission check */
48775b7f77adStw21770 		(void) pthread_mutex_unlock(&np->rn_lock);
48787c478bd9Sstevel@tonic-gate 		pcp = pc_create();
48797c478bd9Sstevel@tonic-gate 		if (pcp != NULL) {
48807c478bd9Sstevel@tonic-gate 			rc = perm_add_enabling(pcp, AUTH_MODIFY);
48817c478bd9Sstevel@tonic-gate 
48827c478bd9Sstevel@tonic-gate 			/* add .smf.modify.<type> for pgs. */
48837c478bd9Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS && np->rn_id.rl_type ==
48847c478bd9Sstevel@tonic-gate 			    REP_PROTOCOL_ENTITY_PROPERTYGRP) {
48857c478bd9Sstevel@tonic-gate 				const char * const auth =
48867c478bd9Sstevel@tonic-gate 				    perm_auth_for_pgtype(np->rn_type);
48877c478bd9Sstevel@tonic-gate 
48887c478bd9Sstevel@tonic-gate 				if (auth != NULL)
48897c478bd9Sstevel@tonic-gate 					rc = perm_add_enabling(pcp, auth);
48907c478bd9Sstevel@tonic-gate 			}
48917c478bd9Sstevel@tonic-gate 
48927c478bd9Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS) {
48937c478bd9Sstevel@tonic-gate 				granted = perm_granted(pcp);
48947c478bd9Sstevel@tonic-gate 
4895a4dc1477STom Whitten 				rc = map_granted_status(granted, pcp,
4896a4dc1477STom Whitten 				    &audit_data.ed_auth);
4897a4dc1477STom Whitten 				if (granted == PERM_GONE) {
4898a4dc1477STom Whitten 					/* No need to audit if client gone. */
4899a4dc1477STom Whitten 					pc_free(pcp);
4900a4dc1477STom Whitten 					rc_node_rele_flag(np,
4901a4dc1477STom Whitten 					    RC_NODE_DYING_FLAGS);
4902a4dc1477STom Whitten 					return (rc);
49035b7f77adStw21770 				}
4904a4dc1477STom Whitten 				if (granted == PERM_DENIED)
4905a4dc1477STom Whitten 					audit_failure = 1;
49067c478bd9Sstevel@tonic-gate 			}
49077c478bd9Sstevel@tonic-gate 
49087c478bd9Sstevel@tonic-gate 			pc_free(pcp);
49097c478bd9Sstevel@tonic-gate 		} else {
49107c478bd9Sstevel@tonic-gate 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
49117c478bd9Sstevel@tonic-gate 		}
49127c478bd9Sstevel@tonic-gate 
49135b7f77adStw21770 		(void) pthread_mutex_lock(&np->rn_lock);
49145b7f77adStw21770 	} else {
49155b7f77adStw21770 		rc = REP_PROTOCOL_SUCCESS;
49165b7f77adStw21770 	}
49177c478bd9Sstevel@tonic-gate #endif /* NATIVE_BUILD */
49187c478bd9Sstevel@tonic-gate 
49197c478bd9Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS) {
49207c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
49217c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
49227c478bd9Sstevel@tonic-gate 		goto fail;
49237c478bd9Sstevel@tonic-gate 	}
49247c478bd9Sstevel@tonic-gate 
49257c478bd9Sstevel@tonic-gate 	ndp = uu_zalloc(sizeof (*ndp));
49267c478bd9Sstevel@tonic-gate 	if (ndp == NULL) {
49277c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
49287c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
49297c478bd9Sstevel@tonic-gate 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
49307c478bd9Sstevel@tonic-gate 		goto fail;
49317c478bd9Sstevel@tonic-gate 	}
49327c478bd9Sstevel@tonic-gate 
49337c478bd9Sstevel@tonic-gate 	rc_node_delete_hold(np, 1);	/* hold entire subgraph, drop lock */
49347c478bd9Sstevel@tonic-gate 
49357c478bd9Sstevel@tonic-gate 	rc = object_delete(np);
49367c478bd9Sstevel@tonic-gate 
49377c478bd9Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS) {
49387c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
49397c478bd9Sstevel@tonic-gate 		rc_node_delete_rele(np, 1);		/* drops lock */
49407c478bd9Sstevel@tonic-gate 		uu_free(ndp);
49417c478bd9Sstevel@tonic-gate 		goto fail;
49427c478bd9Sstevel@tonic-gate 	}
49437c478bd9Sstevel@tonic-gate 
49447c478bd9Sstevel@tonic-gate 	/*
49457c478bd9Sstevel@tonic-gate 	 * Now, delicately unlink and delete the object.
49467c478bd9Sstevel@tonic-gate 	 *
49477c478bd9Sstevel@tonic-gate 	 * Create the delete notification, atomically remove
49487c478bd9Sstevel@tonic-gate 	 * from the hash table and set the NODE_DEAD flag, and
49497c478bd9Sstevel@tonic-gate 	 * remove from the parent's children list.
49507c478bd9Sstevel@tonic-gate 	 */
49517c478bd9Sstevel@tonic-gate 	rc_notify_node_delete(ndp, np); /* frees or uses ndp */
49527c478bd9Sstevel@tonic-gate 
49537c478bd9Sstevel@tonic-gate 	bp = cache_hold(np->rn_hash);
49547c478bd9Sstevel@tonic-gate 
49557c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
49567c478bd9Sstevel@tonic-gate 	cache_remove_unlocked(bp, np);
49577c478bd9Sstevel@tonic-gate 	cache_release(bp);
49587c478bd9Sstevel@tonic-gate 
49597c478bd9Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_DEAD;
49607c478bd9Sstevel@tonic-gate 
49616643e1ffSbustos 	if (pp != NULL) {
49626643e1ffSbustos 		/*
49636643e1ffSbustos 		 * Remove from pp's rn_children.  This requires pp's lock,
49646643e1ffSbustos 		 * so we must drop np's lock to respect lock order.
49656643e1ffSbustos 		 */
49666643e1ffSbustos 		(void) pthread_mutex_unlock(&np->rn_lock);
49677c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&pp->rn_lock);
49687c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
49696643e1ffSbustos 
49707c478bd9Sstevel@tonic-gate 		uu_list_remove(pp->rn_children, np);
49716643e1ffSbustos 
49727c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
49736643e1ffSbustos 
49747c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&pp->rn_lock);
49756643e1ffSbustos 
49767c478bd9Sstevel@tonic-gate 		np->rn_flags &= ~RC_NODE_IN_PARENT;
49777c478bd9Sstevel@tonic-gate 	}
49786643e1ffSbustos 
49797c478bd9Sstevel@tonic-gate 	/*
49806643e1ffSbustos 	 * finally, propagate death to our children (including marking
49816643e1ffSbustos 	 * them DEAD), handle notifications, and release our hold.
49827c478bd9Sstevel@tonic-gate 	 */
49837c478bd9Sstevel@tonic-gate 	rc_node_hold_locked(np);	/* hold for delete */
49847c478bd9Sstevel@tonic-gate 	rc_node_delete_children(np, 1);	/* drops DYING_FLAGS, lock, ref */
49857c478bd9Sstevel@tonic-gate 
49867c478bd9Sstevel@tonic-gate 	rc_node_clear(npp, 1);
49877c478bd9Sstevel@tonic-gate 
49887c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
49897c478bd9Sstevel@tonic-gate 	while ((pnp = uu_list_first(np->rn_pg_notify_list)) != NULL)
49907c478bd9Sstevel@tonic-gate 		rc_pg_notify_fire(pnp);
49917c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
49927c478bd9Sstevel@tonic-gate 	rc_notify_remove_node(np);
49937c478bd9Sstevel@tonic-gate 
49947c478bd9Sstevel@tonic-gate 	rc_node_rele(np);
49957c478bd9Sstevel@tonic-gate 
49965b7f77adStw21770 	smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS,
49975b7f77adStw21770 	    &audit_data);
49985b7f77adStw21770 	free(audit_data.ed_auth);
49995b7f77adStw21770 	free(audit_data.ed_snapname);
50005b7f77adStw21770 	free(audit_data.ed_type);
50015b7f77adStw21770 	free(audit_data.ed_fmri);
50027c478bd9Sstevel@tonic-gate 	return (rc);
50037c478bd9Sstevel@tonic-gate 
50047c478bd9Sstevel@tonic-gate fail:
50057c478bd9Sstevel@tonic-gate 	rc_node_rele(np);
50067c478bd9Sstevel@tonic-gate 	if (rc == REP_PROTOCOL_FAIL_DELETED)
50077c478bd9Sstevel@tonic-gate 		rc_node_clear(npp, 1);
50087c478bd9Sstevel@tonic-gate 	if (pp != NULL) {
50097c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&pp->rn_lock);
50107c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
50117c478bd9Sstevel@tonic-gate 		rc_node_rele_locked(pp);	/* drop ref and lock */
50127c478bd9Sstevel@tonic-gate 	}
50135b7f77adStw21770 	if (audit_failure) {
50145b7f77adStw21770 		smf_audit_event(event_id, ADT_FAILURE,
50155b7f77adStw21770 		    ADT_FAIL_VALUE_AUTH, &audit_data);
50165b7f77adStw21770 	}
50175b7f77adStw21770 cleanout:
50185b7f77adStw21770 	free(audit_data.ed_auth);
50195b7f77adStw21770 	free(audit_data.ed_snapname);
50205b7f77adStw21770 	free(audit_data.ed_type);
50215b7f77adStw21770 	free(audit_data.ed_fmri);
50227c478bd9Sstevel@tonic-gate 	return (rc);
50237c478bd9Sstevel@tonic-gate }
50247c478bd9Sstevel@tonic-gate 
50257c478bd9Sstevel@tonic-gate int
50267c478bd9Sstevel@tonic-gate rc_node_next_snaplevel(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
50277c478bd9Sstevel@tonic-gate {
50287c478bd9Sstevel@tonic-gate 	rc_node_t *np;
50297c478bd9Sstevel@tonic-gate 	rc_node_t *cp, *pp;
50307c478bd9Sstevel@tonic-gate 	int res;
50317c478bd9Sstevel@tonic-gate 
50327c478bd9Sstevel@tonic-gate 	rc_node_clear(cpp, 0);
50337c478bd9Sstevel@tonic-gate 
50347c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
50357c478bd9Sstevel@tonic-gate 
50367c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT &&
50377c478bd9Sstevel@tonic-gate 	    np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL) {
50387c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
50397c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
50407c478bd9Sstevel@tonic-gate 	}
50417c478bd9Sstevel@tonic-gate 
50427c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_SNAPSHOT) {
50437c478bd9Sstevel@tonic-gate 		if ((res = rc_node_fill_children(np,
50447c478bd9Sstevel@tonic-gate 		    REP_PROTOCOL_ENTITY_SNAPLEVEL)) != REP_PROTOCOL_SUCCESS) {
50457c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
50467c478bd9Sstevel@tonic-gate 			return (res);
50477c478bd9Sstevel@tonic-gate 		}
50487c478bd9Sstevel@tonic-gate 
50497c478bd9Sstevel@tonic-gate 		for (cp = uu_list_first(np->rn_children);
50507c478bd9Sstevel@tonic-gate 		    cp != NULL;
50517c478bd9Sstevel@tonic-gate 		    cp = uu_list_next(np->rn_children, cp)) {
50527c478bd9Sstevel@tonic-gate 			if (cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
50537c478bd9Sstevel@tonic-gate 				continue;
50547c478bd9Sstevel@tonic-gate 			rc_node_hold(cp);
50557c478bd9Sstevel@tonic-gate 			break;
50567c478bd9Sstevel@tonic-gate 		}
50577c478bd9Sstevel@tonic-gate 
50587c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
50597c478bd9Sstevel@tonic-gate 	} else {
5060a4dc1477STom Whitten 		if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
5061a4dc1477STom Whitten 			(void) pthread_mutex_unlock(&np->rn_lock);
5062a4dc1477STom Whitten 			rc_node_clear(npp, 1);
5063a4dc1477STom Whitten 			return (REP_PROTOCOL_FAIL_DELETED);
5064a4dc1477STom Whitten 		}
5065a4dc1477STom Whitten 
50667c478bd9Sstevel@tonic-gate 		/*
50677c478bd9Sstevel@tonic-gate 		 * mark our parent as children changing.  This call drops our
50687c478bd9Sstevel@tonic-gate 		 * lock and the RC_NODE_USING_PARENT flag, and returns with
50697c478bd9Sstevel@tonic-gate 		 * pp's lock held
50707c478bd9Sstevel@tonic-gate 		 */
50717c478bd9Sstevel@tonic-gate 		pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
50727c478bd9Sstevel@tonic-gate 		if (pp == NULL) {
50737c478bd9Sstevel@tonic-gate 			/* our parent is gone, we're going next... */
50747c478bd9Sstevel@tonic-gate 
50757c478bd9Sstevel@tonic-gate 			rc_node_clear(npp, 1);
50767c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_DELETED);
50777c478bd9Sstevel@tonic-gate 		}
50787c478bd9Sstevel@tonic-gate 
50797c478bd9Sstevel@tonic-gate 		/*
50807c478bd9Sstevel@tonic-gate 		 * find the next snaplevel
50817c478bd9Sstevel@tonic-gate 		 */
50827c478bd9Sstevel@tonic-gate 		cp = np;
50837c478bd9Sstevel@tonic-gate 		while ((cp = uu_list_next(pp->rn_children, cp)) != NULL &&
50847c478bd9Sstevel@tonic-gate 		    cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
50857c478bd9Sstevel@tonic-gate 			;
50867c478bd9Sstevel@tonic-gate 
50877c478bd9Sstevel@tonic-gate 		/* it must match the snaplevel list */
50887c478bd9Sstevel@tonic-gate 		assert((cp == NULL && np->rn_snaplevel->rsl_next == NULL) ||
50897c478bd9Sstevel@tonic-gate 		    (cp != NULL && np->rn_snaplevel->rsl_next ==
50907c478bd9Sstevel@tonic-gate 		    cp->rn_snaplevel));
50917c478bd9Sstevel@tonic-gate 
50927c478bd9Sstevel@tonic-gate 		if (cp != NULL)
50937c478bd9Sstevel@tonic-gate 			rc_node_hold(cp);
50947c478bd9Sstevel@tonic-gate 
50957c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
50967c478bd9Sstevel@tonic-gate 
50977c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&pp->rn_lock);
50987c478bd9Sstevel@tonic-gate 	}
50997c478bd9Sstevel@tonic-gate 
51007c478bd9Sstevel@tonic-gate 	rc_node_assign(cpp, cp);
51017c478bd9Sstevel@tonic-gate 	if (cp != NULL) {
51027c478bd9Sstevel@tonic-gate 		rc_node_rele(cp);
51037c478bd9Sstevel@tonic-gate 
51047c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_SUCCESS);
51057c478bd9Sstevel@tonic-gate 	}
51067c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_FAIL_NOT_FOUND);
51077c478bd9Sstevel@tonic-gate }
51087c478bd9Sstevel@tonic-gate 
51097c478bd9Sstevel@tonic-gate /*
51107c478bd9Sstevel@tonic-gate  * This call takes a snapshot (np) and either:
51117c478bd9Sstevel@tonic-gate  *	an existing snapid (to be associated with np), or
51127c478bd9Sstevel@tonic-gate  *	a non-NULL parentp (from which a new snapshot is taken, and associated
51137c478bd9Sstevel@tonic-gate  *	    with np)
51147c478bd9Sstevel@tonic-gate  *
51157c478bd9Sstevel@tonic-gate  * To do the association, np is duplicated, the duplicate is made to
51167c478bd9Sstevel@tonic-gate  * represent the new snapid, and np is replaced with the new rc_node_t on
51177c478bd9Sstevel@tonic-gate  * np's parent's child list. np is placed on the new node's rn_former list,
51187c478bd9Sstevel@tonic-gate  * and replaces np in cache_hash (so rc_node_update() will find the new one).
51195b7f77adStw21770  *
51205b7f77adStw21770  * old_fmri and old_name point to the original snap shot's FMRI and name.
51215b7f77adStw21770  * These values are used when generating audit events.
51225b7f77adStw21770  *
51235b7f77adStw21770  * Fails with
51245b7f77adStw21770  *	_BAD_REQUEST
51255b7f77adStw21770  *	_BACKEND_READONLY
51265b7f77adStw21770  *	_DELETED
51275b7f77adStw21770  *	_NO_RESOURCES
51285b7f77adStw21770  *	_TRUNCATED
51295b7f77adStw21770  *	_TYPE_MISMATCH
51307c478bd9Sstevel@tonic-gate  */
51317c478bd9Sstevel@tonic-gate static int
51325b7f77adStw21770 rc_attach_snapshot(
51335b7f77adStw21770 	rc_node_t *np,
51345b7f77adStw21770 	uint32_t snapid,
51355b7f77adStw21770 	rc_node_t *parentp,
51365b7f77adStw21770 	char *old_fmri,
51375b7f77adStw21770 	char *old_name)
51387c478bd9Sstevel@tonic-gate {
51397c478bd9Sstevel@tonic-gate 	rc_node_t *np_orig;
51407c478bd9Sstevel@tonic-gate 	rc_node_t *nnp, *prev;
51417c478bd9Sstevel@tonic-gate 	rc_node_t *pp;
51427c478bd9Sstevel@tonic-gate 	int rc;
51435b7f77adStw21770 	size_t sz_out;
5144a4dc1477STom Whitten 	perm_status_t granted;
51455b7f77adStw21770 	au_event_t event_id;
51465b7f77adStw21770 	audit_event_data_t audit_data;
51477c478bd9Sstevel@tonic-gate 
51485b7f77adStw21770 	if (parentp == NULL) {
51495b7f77adStw21770 		assert(old_fmri != NULL);
51505b7f77adStw21770 	} else {
51517c478bd9Sstevel@tonic-gate 		assert(snapid == 0);
51525b7f77adStw21770 	}
51537c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
51547c478bd9Sstevel@tonic-gate 
51555b7f77adStw21770 	/* Gather the audit data. */
51565b7f77adStw21770 	/*
51575b7f77adStw21770 	 * ADT_smf_* symbols may not be defined in the /usr/include header
51585b7f77adStw21770 	 * files on the build machine.  Thus, the following if-else will
51595b7f77adStw21770 	 * not be compiled when doing native builds.
51605b7f77adStw21770 	 */
51615b7f77adStw21770 #ifndef	NATIVE_BUILD
51625b7f77adStw21770 	if (parentp == NULL) {
51635b7f77adStw21770 		event_id = ADT_smf_attach_snap;
51645b7f77adStw21770 	} else {
51655b7f77adStw21770 		event_id = ADT_smf_create_snap;
51665b7f77adStw21770 	}
51675b7f77adStw21770 #endif	/* NATIVE_BUILD */
51685b7f77adStw21770 	audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
51695b7f77adStw21770 	audit_data.ed_snapname = malloc(REP_PROTOCOL_NAME_LEN);
51705b7f77adStw21770 	if ((audit_data.ed_fmri == NULL) || (audit_data.ed_snapname == NULL)) {
51715b7f77adStw21770 		(void) pthread_mutex_unlock(&np->rn_lock);
51725b7f77adStw21770 		free(audit_data.ed_fmri);
51735b7f77adStw21770 		free(audit_data.ed_snapname);
51745b7f77adStw21770 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
51755b7f77adStw21770 	}
51765b7f77adStw21770 	audit_data.ed_auth = NULL;
51775b7f77adStw21770 	if (strlcpy(audit_data.ed_snapname, np->rn_name,
51785b7f77adStw21770 	    REP_PROTOCOL_NAME_LEN) >= REP_PROTOCOL_NAME_LEN) {
51795b7f77adStw21770 		abort();
51805b7f77adStw21770 	}
51815b7f77adStw21770 	audit_data.ed_old_fmri = old_fmri;
51825b7f77adStw21770 	audit_data.ed_old_name = old_name ? old_name : "NO NAME";
51835b7f77adStw21770 
51845b7f77adStw21770 	if (parentp == NULL) {
51855b7f77adStw21770 		/*
51865b7f77adStw21770 		 * In the attach case, get the instance FMRIs of the
51875b7f77adStw21770 		 * snapshots.
51885b7f77adStw21770 		 */
51895b7f77adStw21770 		if ((rc = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
51905b7f77adStw21770 		    REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
51915b7f77adStw21770 			(void) pthread_mutex_unlock(&np->rn_lock);
51925b7f77adStw21770 			free(audit_data.ed_fmri);
51935b7f77adStw21770 			free(audit_data.ed_snapname);
51945b7f77adStw21770 			return (rc);
51955b7f77adStw21770 		}
51965b7f77adStw21770 	} else {
51975b7f77adStw21770 		/*
51985b7f77adStw21770 		 * Capture the FMRI of the parent if we're actually going
51995b7f77adStw21770 		 * to take the snapshot.
52005b7f77adStw21770 		 */
52015b7f77adStw21770 		if ((rc = rc_node_get_fmri_or_fragment(parentp,
52025b7f77adStw21770 		    audit_data.ed_fmri, REP_PROTOCOL_FMRI_LEN, &sz_out)) !=
52035b7f77adStw21770 		    REP_PROTOCOL_SUCCESS) {
52045b7f77adStw21770 			(void) pthread_mutex_unlock(&np->rn_lock);
52055b7f77adStw21770 			free(audit_data.ed_fmri);
52065b7f77adStw21770 			free(audit_data.ed_snapname);
52075b7f77adStw21770 			return (rc);
52085b7f77adStw21770 		}
52095b7f77adStw21770 	}
52105b7f77adStw21770 
52117c478bd9Sstevel@tonic-gate 	np_orig = np;
52127c478bd9Sstevel@tonic-gate 	rc_node_hold_locked(np);		/* simplifies the remainder */
52137c478bd9Sstevel@tonic-gate 
52143eae19d9Swesolows 	(void) pthread_mutex_unlock(&np->rn_lock);
5215a4dc1477STom Whitten 	granted = rc_node_modify_permission_check(&audit_data.ed_auth);
5216a4dc1477STom Whitten 	switch (granted) {
5217a4dc1477STom Whitten 	case PERM_DENIED:
52185b7f77adStw21770 		smf_audit_event(event_id, ADT_FAILURE, ADT_FAIL_VALUE_AUTH,
52195b7f77adStw21770 		    &audit_data);
5220a4dc1477STom Whitten 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
5221a4dc1477STom Whitten 		rc_node_rele(np);
52225b7f77adStw21770 		goto cleanout;
5223a4dc1477STom Whitten 	case PERM_GRANTED:
5224a4dc1477STom Whitten 		break;
5225a4dc1477STom Whitten 	case PERM_GONE:
5226a4dc1477STom Whitten 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
5227a4dc1477STom Whitten 		rc_node_rele(np);
5228a4dc1477STom Whitten 		goto cleanout;
5229a4dc1477STom Whitten 	case PERM_FAIL:
5230a4dc1477STom Whitten 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
5231a4dc1477STom Whitten 		rc_node_rele(np);
5232a4dc1477STom Whitten 		goto cleanout;
5233a4dc1477STom Whitten 	default:
5234a4dc1477STom Whitten 		bad_error(rc_node_modify_permission_check, granted);
52355b7f77adStw21770 	}
52363eae19d9Swesolows 	(void) pthread_mutex_lock(&np->rn_lock);
52373eae19d9Swesolows 
52387c478bd9Sstevel@tonic-gate 	/*
52397c478bd9Sstevel@tonic-gate 	 * get the latest node, holding RC_NODE_IN_TX to keep the rn_former
52407c478bd9Sstevel@tonic-gate 	 * list from changing.
52417c478bd9Sstevel@tonic-gate 	 */
52427c478bd9Sstevel@tonic-gate 	for (;;) {
52437c478bd9Sstevel@tonic-gate 		if (!(np->rn_flags & RC_NODE_OLD)) {
52447c478bd9Sstevel@tonic-gate 			if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
52457c478bd9Sstevel@tonic-gate 				goto again;
52467c478bd9Sstevel@tonic-gate 			}
52477c478bd9Sstevel@tonic-gate 			pp = rc_node_hold_parent_flag(np,
52487c478bd9Sstevel@tonic-gate 			    RC_NODE_CHILDREN_CHANGING);
52497c478bd9Sstevel@tonic-gate 
52507c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
52517c478bd9Sstevel@tonic-gate 			if (pp == NULL) {
52527c478bd9Sstevel@tonic-gate 				goto again;
52537c478bd9Sstevel@tonic-gate 			}
52547c478bd9Sstevel@tonic-gate 			if (np->rn_flags & RC_NODE_OLD) {
52557c478bd9Sstevel@tonic-gate 				rc_node_rele_flag(pp,
52567c478bd9Sstevel@tonic-gate 				    RC_NODE_CHILDREN_CHANGING);
52577c478bd9Sstevel@tonic-gate 				(void) pthread_mutex_unlock(&pp->rn_lock);
52587c478bd9Sstevel@tonic-gate 				goto again;
52597c478bd9Sstevel@tonic-gate 			}
52607c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&pp->rn_lock);
52617c478bd9Sstevel@tonic-gate 
52627c478bd9Sstevel@tonic-gate 			if (!rc_node_hold_flag(np, RC_NODE_IN_TX)) {
52637c478bd9Sstevel@tonic-gate 				/*
52647c478bd9Sstevel@tonic-gate 				 * Can't happen, since we're holding our
52657c478bd9Sstevel@tonic-gate 				 * parent's CHILDREN_CHANGING flag...
52667c478bd9Sstevel@tonic-gate 				 */
52677c478bd9Sstevel@tonic-gate 				abort();
52687c478bd9Sstevel@tonic-gate 			}
52697c478bd9Sstevel@tonic-gate 			break;			/* everything's ready */
52707c478bd9Sstevel@tonic-gate 		}
52717c478bd9Sstevel@tonic-gate again:
52727c478bd9Sstevel@tonic-gate 		rc_node_rele_locked(np);
52737c478bd9Sstevel@tonic-gate 		np = cache_lookup(&np_orig->rn_id);
52747c478bd9Sstevel@tonic-gate 
52755b7f77adStw21770 		if (np == NULL) {
52765b7f77adStw21770 			rc = REP_PROTOCOL_FAIL_DELETED;
52775b7f77adStw21770 			goto cleanout;
52785b7f77adStw21770 		}
52797c478bd9Sstevel@tonic-gate 
52807c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
52817c478bd9Sstevel@tonic-gate 	}
52827c478bd9Sstevel@tonic-gate 
52837c478bd9Sstevel@tonic-gate 	if (parentp != NULL) {
52847c478bd9Sstevel@tonic-gate 		if (pp != parentp) {
52857c478bd9Sstevel@tonic-gate 			rc = REP_PROTOCOL_FAIL_BAD_REQUEST;
52867c478bd9Sstevel@tonic-gate 			goto fail;
52877c478bd9Sstevel@tonic-gate 		}
52887c478bd9Sstevel@tonic-gate 		nnp = NULL;
52897c478bd9Sstevel@tonic-gate 	} else {
52907c478bd9Sstevel@tonic-gate 		/*
52917c478bd9Sstevel@tonic-gate 		 * look for a former node with the snapid we need.
52927c478bd9Sstevel@tonic-gate 		 */
52937c478bd9Sstevel@tonic-gate 		if (np->rn_snapshot_id == snapid) {
52947c478bd9Sstevel@tonic-gate 			rc_node_rele_flag(np, RC_NODE_IN_TX);
52957c478bd9Sstevel@tonic-gate 			rc_node_rele_locked(np);
52967c478bd9Sstevel@tonic-gate 
52977c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&pp->rn_lock);
52987c478bd9Sstevel@tonic-gate 			rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
52997c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&pp->rn_lock);
53005b7f77adStw21770 			rc = REP_PROTOCOL_SUCCESS;	/* nothing to do */
53015b7f77adStw21770 			goto cleanout;
53027c478bd9Sstevel@tonic-gate 		}
53037c478bd9Sstevel@tonic-gate 
53047c478bd9Sstevel@tonic-gate 		prev = np;
53057c478bd9Sstevel@tonic-gate 		while ((nnp = prev->rn_former) != NULL) {
53067c478bd9Sstevel@tonic-gate 			if (nnp->rn_snapshot_id == snapid) {
53077c478bd9Sstevel@tonic-gate 				rc_node_hold(nnp);
53087c478bd9Sstevel@tonic-gate 				break;		/* existing node with that id */
53097c478bd9Sstevel@tonic-gate 			}
53107c478bd9Sstevel@tonic-gate 			prev = nnp;
53117c478bd9Sstevel@tonic-gate 		}
53127c478bd9Sstevel@tonic-gate 	}
53137c478bd9Sstevel@tonic-gate 
53147c478bd9Sstevel@tonic-gate 	if (nnp == NULL) {
53157c478bd9Sstevel@tonic-gate 		prev = NULL;
53167c478bd9Sstevel@tonic-gate 		nnp = rc_node_alloc();
53177c478bd9Sstevel@tonic-gate 		if (nnp == NULL) {
53187c478bd9Sstevel@tonic-gate 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
53197c478bd9Sstevel@tonic-gate 			goto fail;
53207c478bd9Sstevel@tonic-gate 		}
53217c478bd9Sstevel@tonic-gate 
53227c478bd9Sstevel@tonic-gate 		nnp->rn_id = np->rn_id;		/* structure assignment */
53237c478bd9Sstevel@tonic-gate 		nnp->rn_hash = np->rn_hash;
53247c478bd9Sstevel@tonic-gate 		nnp->rn_name = strdup(np->rn_name);
53257c478bd9Sstevel@tonic-gate 		nnp->rn_snapshot_id = snapid;
53267c478bd9Sstevel@tonic-gate 		nnp->rn_flags = RC_NODE_IN_TX | RC_NODE_USING_PARENT;
53277c478bd9Sstevel@tonic-gate 
53287c478bd9Sstevel@tonic-gate 		if (nnp->rn_name == NULL) {
53297c478bd9Sstevel@tonic-gate 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
53307c478bd9Sstevel@tonic-gate 			goto fail;
53317c478bd9Sstevel@tonic-gate 		}
53327c478bd9Sstevel@tonic-gate 	}
53337c478bd9Sstevel@tonic-gate 
53347c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
53357c478bd9Sstevel@tonic-gate 
53367c478bd9Sstevel@tonic-gate 	rc = object_snapshot_attach(&np->rn_id, &snapid, (parentp != NULL));
53377c478bd9Sstevel@tonic-gate 
53387c478bd9Sstevel@tonic-gate 	if (parentp != NULL)
53397c478bd9Sstevel@tonic-gate 		nnp->rn_snapshot_id = snapid;	/* fill in new snapid */
53407c478bd9Sstevel@tonic-gate 	else
53417c478bd9Sstevel@tonic-gate 		assert(nnp->rn_snapshot_id == snapid);
53427c478bd9Sstevel@tonic-gate 
53437c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
53447c478bd9Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS)
53457c478bd9Sstevel@tonic-gate 		goto fail;
53467c478bd9Sstevel@tonic-gate 
53477c478bd9Sstevel@tonic-gate 	/*
53487c478bd9Sstevel@tonic-gate 	 * fix up the former chain
53497c478bd9Sstevel@tonic-gate 	 */
53507c478bd9Sstevel@tonic-gate 	if (prev != NULL) {
53517c478bd9Sstevel@tonic-gate 		prev->rn_former = nnp->rn_former;
53527c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&nnp->rn_lock);
53537c478bd9Sstevel@tonic-gate 		nnp->rn_flags &= ~RC_NODE_ON_FORMER;
53547c478bd9Sstevel@tonic-gate 		nnp->rn_former = NULL;
53557c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&nnp->rn_lock);
53567c478bd9Sstevel@tonic-gate 	}
53577c478bd9Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_OLD;
53587c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
53597c478bd9Sstevel@tonic-gate 
53607c478bd9Sstevel@tonic-gate 	/*
53617c478bd9Sstevel@tonic-gate 	 * replace np with nnp
53627c478bd9Sstevel@tonic-gate 	 */
53637c478bd9Sstevel@tonic-gate 	rc_node_relink_child(pp, np, nnp);
53647c478bd9Sstevel@tonic-gate 
53657c478bd9Sstevel@tonic-gate 	rc_node_rele(np);
53665b7f77adStw21770 	smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS, &audit_data);
53675b7f77adStw21770 	rc = REP_PROTOCOL_SUCCESS;
53687c478bd9Sstevel@tonic-gate 
53695b7f77adStw21770 cleanout:
53705b7f77adStw21770 	free(audit_data.ed_auth);
53715b7f77adStw21770 	free(audit_data.ed_fmri);
53725b7f77adStw21770 	free(audit_data.ed_snapname);
53735b7f77adStw21770 	return (rc);
53747c478bd9Sstevel@tonic-gate 
53757c478bd9Sstevel@tonic-gate fail:
53767c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_IN_TX);
53777c478bd9Sstevel@tonic-gate 	rc_node_rele_locked(np);
53787c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&pp->rn_lock);
53797c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
53807c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&pp->rn_lock);
53817c478bd9Sstevel@tonic-gate 
53827c478bd9Sstevel@tonic-gate 	if (nnp != NULL) {
53837c478bd9Sstevel@tonic-gate 		if (prev == NULL)
53847c478bd9Sstevel@tonic-gate 			rc_node_destroy(nnp);
53857c478bd9Sstevel@tonic-gate 		else
53867c478bd9Sstevel@tonic-gate 			rc_node_rele(nnp);
53877c478bd9Sstevel@tonic-gate 	}
53887c478bd9Sstevel@tonic-gate 
53895b7f77adStw21770 	free(audit_data.ed_auth);
53905b7f77adStw21770 	free(audit_data.ed_fmri);
53915b7f77adStw21770 	free(audit_data.ed_snapname);
53927c478bd9Sstevel@tonic-gate 	return (rc);
53937c478bd9Sstevel@tonic-gate }
53947c478bd9Sstevel@tonic-gate 
53957c478bd9Sstevel@tonic-gate int
53967c478bd9Sstevel@tonic-gate rc_snapshot_take_new(rc_node_ptr_t *npp, const char *svcname,
53977c478bd9Sstevel@tonic-gate     const char *instname, const char *name, rc_node_ptr_t *outpp)
53987c478bd9Sstevel@tonic-gate {
5399a4dc1477STom Whitten 	perm_status_t granted;
54007c478bd9Sstevel@tonic-gate 	rc_node_t *np;
54017c478bd9Sstevel@tonic-gate 	rc_node_t *outp = NULL;
54023eae19d9Swesolows 	int rc, perm_rc;
54035b7f77adStw21770 	char fmri[REP_PROTOCOL_FMRI_LEN];
54045b7f77adStw21770 	audit_event_data_t audit_data;
54055b7f77adStw21770 	size_t sz_out;
54067c478bd9Sstevel@tonic-gate 
54077c478bd9Sstevel@tonic-gate 	rc_node_clear(outpp, 0);
54087c478bd9Sstevel@tonic-gate 
5409a4dc1477STom Whitten 	/*
5410a4dc1477STom Whitten 	 * rc_node_modify_permission_check() must be called before the node
5411a4dc1477STom Whitten 	 * is locked.  This is because the library functions that check
5412a4dc1477STom Whitten 	 * authorizations can trigger calls back into configd.
5413a4dc1477STom Whitten 	 */
5414a4dc1477STom Whitten 	granted = rc_node_modify_permission_check(&audit_data.ed_auth);
5415a4dc1477STom Whitten 	switch (granted) {
5416a4dc1477STom Whitten 	case PERM_DENIED:
5417a4dc1477STom Whitten 		/*
5418a4dc1477STom Whitten 		 * We continue in this case, so that we can generate an
5419a4dc1477STom Whitten 		 * audit event later in this function.
5420a4dc1477STom Whitten 		 */
5421a4dc1477STom Whitten 		perm_rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
5422a4dc1477STom Whitten 		break;
5423a4dc1477STom Whitten 	case PERM_GRANTED:
5424a4dc1477STom Whitten 		perm_rc = REP_PROTOCOL_SUCCESS;
5425a4dc1477STom Whitten 		break;
5426a4dc1477STom Whitten 	case PERM_GONE:
5427a4dc1477STom Whitten 		/* No need to produce audit event if client is gone. */
5428a4dc1477STom Whitten 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
5429a4dc1477STom Whitten 	case PERM_FAIL:
5430a4dc1477STom Whitten 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
5431a4dc1477STom Whitten 	default:
5432a4dc1477STom Whitten 		bad_error("rc_node_modify_permission_check", granted);
5433a4dc1477STom Whitten 		break;
5434a4dc1477STom Whitten 	}
54353eae19d9Swesolows 
5436a4dc1477STom Whitten 	RC_NODE_PTR_CHECK_LOCK_OR_FREE_RETURN(np, npp, audit_data.ed_auth);
54377c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) {
54387c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
54395b7f77adStw21770 		free(audit_data.ed_auth);
54407c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
54417c478bd9Sstevel@tonic-gate 	}
54427c478bd9Sstevel@tonic-gate 
54437c478bd9Sstevel@tonic-gate 	rc = rc_check_type_name(REP_PROTOCOL_ENTITY_SNAPSHOT, name);
54447c478bd9Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS) {
54457c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
54465b7f77adStw21770 		free(audit_data.ed_auth);
54477c478bd9Sstevel@tonic-gate 		return (rc);
54487c478bd9Sstevel@tonic-gate 	}
54497c478bd9Sstevel@tonic-gate 
54507c478bd9Sstevel@tonic-gate 	if (svcname != NULL && (rc =
54517c478bd9Sstevel@tonic-gate 	    rc_check_type_name(REP_PROTOCOL_ENTITY_SERVICE, svcname)) !=
54527c478bd9Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS) {
54537c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
54545b7f77adStw21770 		free(audit_data.ed_auth);
54557c478bd9Sstevel@tonic-gate 		return (rc);
54567c478bd9Sstevel@tonic-gate 	}
54577c478bd9Sstevel@tonic-gate 
54587c478bd9Sstevel@tonic-gate 	if (instname != NULL && (rc =
54597c478bd9Sstevel@tonic-gate 	    rc_check_type_name(REP_PROTOCOL_ENTITY_INSTANCE, instname)) !=
54607c478bd9Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS) {
54617c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
54625b7f77adStw21770 		free(audit_data.ed_auth);
54637c478bd9Sstevel@tonic-gate 		return (rc);
54647c478bd9Sstevel@tonic-gate 	}
54657c478bd9Sstevel@tonic-gate 
54665b7f77adStw21770 	audit_data.ed_fmri = fmri;
54675b7f77adStw21770 	audit_data.ed_snapname = (char *)name;
54685b7f77adStw21770 
54695b7f77adStw21770 	if ((rc = rc_node_get_fmri_or_fragment(np, fmri, sizeof (fmri),
54705b7f77adStw21770 	    &sz_out)) != REP_PROTOCOL_SUCCESS) {
54715b7f77adStw21770 		(void) pthread_mutex_unlock(&np->rn_lock);
54725b7f77adStw21770 		free(audit_data.ed_auth);
54735b7f77adStw21770 		return (rc);
54745b7f77adStw21770 	}
54753eae19d9Swesolows 	if (perm_rc != REP_PROTOCOL_SUCCESS) {
54767c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
54775b7f77adStw21770 		smf_audit_event(ADT_smf_create_snap, ADT_FAILURE,
54785b7f77adStw21770 		    ADT_FAIL_VALUE_AUTH, &audit_data);
54795b7f77adStw21770 		free(audit_data.ed_auth);
54803eae19d9Swesolows 		return (perm_rc);
54817c478bd9Sstevel@tonic-gate 	}
54827c478bd9Sstevel@tonic-gate 
54835b7f77adStw21770 	HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
54845b7f77adStw21770 	    audit_data.ed_auth);
54857c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
54867c478bd9Sstevel@tonic-gate 
54877c478bd9Sstevel@tonic-gate 	rc = object_snapshot_take_new(np, svcname, instname, name, &outp);
54887c478bd9Sstevel@tonic-gate 
54897c478bd9Sstevel@tonic-gate 	if (rc == REP_PROTOCOL_SUCCESS) {
54907c478bd9Sstevel@tonic-gate 		rc_node_assign(outpp, outp);
54917c478bd9Sstevel@tonic-gate 		rc_node_rele(outp);
54927c478bd9Sstevel@tonic-gate 	}
54937c478bd9Sstevel@tonic-gate 
54947c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
54957c478bd9Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
54967c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
54977c478bd9Sstevel@tonic-gate 
54985b7f77adStw21770 	if (rc == REP_PROTOCOL_SUCCESS) {
54995b7f77adStw21770 		smf_audit_event(ADT_smf_create_snap, ADT_SUCCESS, ADT_SUCCESS,
55005b7f77adStw21770 		    &audit_data);
55015b7f77adStw21770 	}
55025b7f77adStw21770 	if (audit_data.ed_auth != NULL)
55035b7f77adStw21770 		free(audit_data.ed_auth);
55047c478bd9Sstevel@tonic-gate 	return (rc);
55057c478bd9Sstevel@tonic-gate }
55067c478bd9Sstevel@tonic-gate 
55077c478bd9Sstevel@tonic-gate int
55087c478bd9Sstevel@tonic-gate rc_snapshot_take_attach(rc_node_ptr_t *npp, rc_node_ptr_t *outpp)
55097c478bd9Sstevel@tonic-gate {
55107c478bd9Sstevel@tonic-gate 	rc_node_t *np, *outp;
55117c478bd9Sstevel@tonic-gate 
55127c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK(np, npp);
55137c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) {
55147c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
55157c478bd9Sstevel@tonic-gate 	}
55167c478bd9Sstevel@tonic-gate 
55177c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(outp, outpp);
55187c478bd9Sstevel@tonic-gate 	if (outp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
55197c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&outp->rn_lock);
55207c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
55217c478bd9Sstevel@tonic-gate 	}
55227c478bd9Sstevel@tonic-gate 
55235b7f77adStw21770 	return (rc_attach_snapshot(outp, 0, np, NULL,
55245b7f77adStw21770 	    NULL));					/* drops outp's lock */
55257c478bd9Sstevel@tonic-gate }
55267c478bd9Sstevel@tonic-gate 
55277c478bd9Sstevel@tonic-gate int
55287c478bd9Sstevel@tonic-gate rc_snapshot_attach(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
55297c478bd9Sstevel@tonic-gate {
55307c478bd9Sstevel@tonic-gate 	rc_node_t *np;
55317c478bd9Sstevel@tonic-gate 	rc_node_t *cp;
55327c478bd9Sstevel@tonic-gate 	uint32_t snapid;
55335b7f77adStw21770 	char old_name[REP_PROTOCOL_NAME_LEN];
55345b7f77adStw21770 	int rc;
55355b7f77adStw21770 	size_t sz_out;
55365b7f77adStw21770 	char old_fmri[REP_PROTOCOL_FMRI_LEN];
55377c478bd9Sstevel@tonic-gate 
55387c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
55397c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
55407c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
55417c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
55427c478bd9Sstevel@tonic-gate 	}
55437c478bd9Sstevel@tonic-gate 	snapid = np->rn_snapshot_id;
55445b7f77adStw21770 	rc = rc_node_get_fmri_or_fragment(np, old_fmri, sizeof (old_fmri),
55455b7f77adStw21770 	    &sz_out);
55467c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
55475b7f77adStw21770 	if (rc != REP_PROTOCOL_SUCCESS)
55485b7f77adStw21770 		return (rc);
55495b7f77adStw21770 	if (np->rn_name != NULL) {
55505b7f77adStw21770 		if (strlcpy(old_name, np->rn_name, sizeof (old_name)) >=
55515b7f77adStw21770 		    sizeof (old_name)) {
55525b7f77adStw21770 			return (REP_PROTOCOL_FAIL_TRUNCATED);
55535b7f77adStw21770 		}
55545b7f77adStw21770 	}
55557c478bd9Sstevel@tonic-gate 
55567c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(cp, cpp);
55577c478bd9Sstevel@tonic-gate 	if (cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
55587c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&cp->rn_lock);
55597c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
55607c478bd9Sstevel@tonic-gate 	}
55617c478bd9Sstevel@tonic-gate 
55625b7f77adStw21770 	rc = rc_attach_snapshot(cp, snapid, NULL,
55635b7f77adStw21770 	    old_fmri, old_name);			/* drops cp's lock */
55645b7f77adStw21770 	return (rc);
55657c478bd9Sstevel@tonic-gate }
55667c478bd9Sstevel@tonic-gate 
55677c478bd9Sstevel@tonic-gate /*
55683eae19d9Swesolows  * If the pgname property group under ent has type pgtype, and it has a
55693eae19d9Swesolows  * propname property with type ptype, return _SUCCESS.  If pgtype is NULL,
55703eae19d9Swesolows  * it is not checked.  If ent is not a service node, we will return _SUCCESS if
55713eae19d9Swesolows  * a property meeting the requirements exists in either the instance or its
55723eae19d9Swesolows  * parent.
55733eae19d9Swesolows  *
55743eae19d9Swesolows  * Returns
55753eae19d9Swesolows  *   _SUCCESS - see above
55763eae19d9Swesolows  *   _DELETED - ent or one of its ancestors was deleted
55773eae19d9Swesolows  *   _NO_RESOURCES - no resources
55783eae19d9Swesolows  *   _NOT_FOUND - no matching property was found
55793eae19d9Swesolows  */
55803eae19d9Swesolows static int
55813eae19d9Swesolows rc_svc_prop_exists(rc_node_t *ent, const char *pgname, const char *pgtype,
55823eae19d9Swesolows     const char *propname, rep_protocol_value_type_t ptype)
55833eae19d9Swesolows {
55843eae19d9Swesolows 	int ret;
55853eae19d9Swesolows 	rc_node_t *pg = NULL, *spg = NULL, *svc, *prop;
55863eae19d9Swesolows 
55873eae19d9Swesolows 	assert(!MUTEX_HELD(&ent->rn_lock));
55883eae19d9Swesolows 
55893eae19d9Swesolows 	(void) pthread_mutex_lock(&ent->rn_lock);
55903eae19d9Swesolows 	ret = rc_node_find_named_child(ent, pgname,
55913eae19d9Swesolows 	    REP_PROTOCOL_ENTITY_PROPERTYGRP, &pg);
55923eae19d9Swesolows 	(void) pthread_mutex_unlock(&ent->rn_lock);
55933eae19d9Swesolows 
55943eae19d9Swesolows 	switch (ret) {
55953eae19d9Swesolows 	case REP_PROTOCOL_SUCCESS:
55963eae19d9Swesolows 		break;
55973eae19d9Swesolows 
55983eae19d9Swesolows 	case REP_PROTOCOL_FAIL_DELETED:
55993eae19d9Swesolows 	case REP_PROTOCOL_FAIL_NO_RESOURCES:
56003eae19d9Swesolows 		return (ret);
56013eae19d9Swesolows 
56023eae19d9Swesolows 	default:
56033eae19d9Swesolows 		bad_error("rc_node_find_named_child", ret);
56043eae19d9Swesolows 	}
56053eae19d9Swesolows 
56063eae19d9Swesolows 	if (ent->rn_id.rl_type != REP_PROTOCOL_ENTITY_SERVICE) {
56073eae19d9Swesolows 		ret = rc_node_find_ancestor(ent, REP_PROTOCOL_ENTITY_SERVICE,
56083eae19d9Swesolows 		    &svc);
56093eae19d9Swesolows 		if (ret != REP_PROTOCOL_SUCCESS) {
56103eae19d9Swesolows 			assert(ret == REP_PROTOCOL_FAIL_DELETED);
56113eae19d9Swesolows 			if (pg != NULL)
56123eae19d9Swesolows 				rc_node_rele(pg);
56133eae19d9Swesolows 			return (ret);
56143eae19d9Swesolows 		}
56153eae19d9Swesolows 		assert(svc->rn_id.rl_type == REP_PROTOCOL_ENTITY_SERVICE);
56163eae19d9Swesolows 
56173eae19d9Swesolows 		(void) pthread_mutex_lock(&svc->rn_lock);
56183eae19d9Swesolows 		ret = rc_node_find_named_child(svc, pgname,
56193eae19d9Swesolows 		    REP_PROTOCOL_ENTITY_PROPERTYGRP, &spg);
56203eae19d9Swesolows 		(void) pthread_mutex_unlock(&svc->rn_lock);
56213eae19d9Swesolows 
56223eae19d9Swesolows 		rc_node_rele(svc);
56233eae19d9Swesolows 
56243eae19d9Swesolows 		switch (ret) {
56253eae19d9Swesolows 		case REP_PROTOCOL_SUCCESS:
56263eae19d9Swesolows 			break;
56273eae19d9Swesolows 
56283eae19d9Swesolows 		case REP_PROTOCOL_FAIL_DELETED:
56293eae19d9Swesolows 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
56303eae19d9Swesolows 			if (pg != NULL)
56313eae19d9Swesolows 				rc_node_rele(pg);
56323eae19d9Swesolows 			return (ret);
56333eae19d9Swesolows 
56343eae19d9Swesolows 		default:
56353eae19d9Swesolows 			bad_error("rc_node_find_named_child", ret);
56363eae19d9Swesolows 		}
56373eae19d9Swesolows 	}
56383eae19d9Swesolows 
56393eae19d9Swesolows 	if (pg != NULL &&
56403eae19d9Swesolows 	    pgtype != NULL && strcmp(pg->rn_type, pgtype) != 0) {
56413eae19d9Swesolows 		rc_node_rele(pg);
56423eae19d9Swesolows 		pg = NULL;
56433eae19d9Swesolows 	}
56443eae19d9Swesolows 
56453eae19d9Swesolows 	if (spg != NULL &&
56463eae19d9Swesolows 	    pgtype != NULL && strcmp(spg->rn_type, pgtype) != 0) {
56473eae19d9Swesolows 		rc_node_rele(spg);
56483eae19d9Swesolows 		spg = NULL;
56493eae19d9Swesolows 	}
56503eae19d9Swesolows 
56513eae19d9Swesolows 	if (pg == NULL) {
56523eae19d9Swesolows 		if (spg == NULL)
56533eae19d9Swesolows 			return (REP_PROTOCOL_FAIL_NOT_FOUND);
56543eae19d9Swesolows 		pg = spg;
56553eae19d9Swesolows 		spg = NULL;
56563eae19d9Swesolows 	}
56573eae19d9Swesolows 
56583eae19d9Swesolows 	/*
56593eae19d9Swesolows 	 * At this point, pg is non-NULL, and is a property group node of the
56603eae19d9Swesolows 	 * correct type.  spg, if non-NULL, is also a property group node of
56613eae19d9Swesolows 	 * the correct type.  Check for the property in pg first, then spg
56623eae19d9Swesolows 	 * (if applicable).
56633eae19d9Swesolows 	 */
56643eae19d9Swesolows 	(void) pthread_mutex_lock(&pg->rn_lock);
56653eae19d9Swesolows 	ret = rc_node_find_named_child(pg, propname,
56663eae19d9Swesolows 	    REP_PROTOCOL_ENTITY_PROPERTY, &prop);
56673eae19d9Swesolows 	(void) pthread_mutex_unlock(&pg->rn_lock);
56683eae19d9Swesolows 	rc_node_rele(pg);
56693eae19d9Swesolows 	switch (ret) {
56703eae19d9Swesolows 	case REP_PROTOCOL_SUCCESS:
56713eae19d9Swesolows 		if (prop != NULL) {
56723eae19d9Swesolows 			if (prop->rn_valtype == ptype) {
56733eae19d9Swesolows 				rc_node_rele(prop);
56743eae19d9Swesolows 				if (spg != NULL)
56753eae19d9Swesolows 					rc_node_rele(spg);
56763eae19d9Swesolows 				return (REP_PROTOCOL_SUCCESS);
56773eae19d9Swesolows 			}
56783eae19d9Swesolows 			rc_node_rele(prop);
56793eae19d9Swesolows 		}
56803eae19d9Swesolows 		break;
56813eae19d9Swesolows 
56823eae19d9Swesolows 	case REP_PROTOCOL_FAIL_NO_RESOURCES:
56833eae19d9Swesolows 		if (spg != NULL)
56843eae19d9Swesolows 			rc_node_rele(spg);
56853eae19d9Swesolows 		return (ret);
56863eae19d9Swesolows 
56873eae19d9Swesolows 	case REP_PROTOCOL_FAIL_DELETED:
56883eae19d9Swesolows 		break;
56893eae19d9Swesolows 
56903eae19d9Swesolows 	default:
56913eae19d9Swesolows 		bad_error("rc_node_find_named_child", ret);
56923eae19d9Swesolows 	}
56933eae19d9Swesolows 
56943eae19d9Swesolows 	if (spg == NULL)
56953eae19d9Swesolows 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
56963eae19d9Swesolows 
56973eae19d9Swesolows 	pg = spg;
56983eae19d9Swesolows 
56993eae19d9Swesolows 	(void) pthread_mutex_lock(&pg->rn_lock);
57003eae19d9Swesolows 	ret = rc_node_find_named_child(pg, propname,
57013eae19d9Swesolows 	    REP_PROTOCOL_ENTITY_PROPERTY, &prop);
57023eae19d9Swesolows 	(void) pthread_mutex_unlock(&pg->rn_lock);
57033eae19d9Swesolows 	rc_node_rele(pg);
57043eae19d9Swesolows 	switch (ret) {
57053eae19d9Swesolows 	case REP_PROTOCOL_SUCCESS:
57063eae19d9Swesolows 		if (prop != NULL) {
57073eae19d9Swesolows 			if (prop->rn_valtype == ptype) {
57083eae19d9Swesolows 				rc_node_rele(prop);
57093eae19d9Swesolows 				return (REP_PROTOCOL_SUCCESS);
57103eae19d9Swesolows 			}
57113eae19d9Swesolows 			rc_node_rele(prop);
57123eae19d9Swesolows 		}
57133eae19d9Swesolows 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
57143eae19d9Swesolows 
57153eae19d9Swesolows 	case REP_PROTOCOL_FAIL_NO_RESOURCES:
57163eae19d9Swesolows 		return (ret);
57173eae19d9Swesolows 
57183eae19d9Swesolows 	case REP_PROTOCOL_FAIL_DELETED:
57193eae19d9Swesolows 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
57203eae19d9Swesolows 
57213eae19d9Swesolows 	default:
57223eae19d9Swesolows 		bad_error("rc_node_find_named_child", ret);
57233eae19d9Swesolows 	}
57243eae19d9Swesolows 
57253eae19d9Swesolows 	return (REP_PROTOCOL_SUCCESS);
57263eae19d9Swesolows }
57273eae19d9Swesolows 
57283eae19d9Swesolows /*
57293eae19d9Swesolows  * Given a property group node, returns _SUCCESS if the property group may
57303eae19d9Swesolows  * be read without any special authorization.
57313eae19d9Swesolows  *
57323eae19d9Swesolows  * Fails with:
57333eae19d9Swesolows  *   _DELETED - np or an ancestor node was deleted
57343eae19d9Swesolows  *   _TYPE_MISMATCH - np does not refer to a property group
57353eae19d9Swesolows  *   _NO_RESOURCES - no resources
57363eae19d9Swesolows  *   _PERMISSION_DENIED - authorization is required
57373eae19d9Swesolows  */
57383eae19d9Swesolows static int
57393eae19d9Swesolows rc_node_pg_check_read_protect(rc_node_t *np)
57403eae19d9Swesolows {
57413eae19d9Swesolows 	int ret;
57423eae19d9Swesolows 	rc_node_t *ent;
57433eae19d9Swesolows 
57443eae19d9Swesolows 	assert(!MUTEX_HELD(&np->rn_lock));
57453eae19d9Swesolows 
57463eae19d9Swesolows 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
57473eae19d9Swesolows 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
57483eae19d9Swesolows 
57493eae19d9Swesolows 	if (strcmp(np->rn_type, SCF_GROUP_FRAMEWORK) == 0 ||
57503eae19d9Swesolows 	    strcmp(np->rn_type, SCF_GROUP_DEPENDENCY) == 0 ||
57513eae19d9Swesolows 	    strcmp(np->rn_type, SCF_GROUP_METHOD) == 0)
57523eae19d9Swesolows 		return (REP_PROTOCOL_SUCCESS);
57533eae19d9Swesolows 
57543eae19d9Swesolows 	ret = rc_node_parent(np, &ent);
57553eae19d9Swesolows 
57563eae19d9Swesolows 	if (ret != REP_PROTOCOL_SUCCESS)
57573eae19d9Swesolows 		return (ret);
57583eae19d9Swesolows 
57593eae19d9Swesolows 	ret = rc_svc_prop_exists(ent, np->rn_name, np->rn_type,
57603eae19d9Swesolows 	    AUTH_PROP_READ, REP_PROTOCOL_TYPE_STRING);
57613eae19d9Swesolows 
57623eae19d9Swesolows 	rc_node_rele(ent);
57633eae19d9Swesolows 
57643eae19d9Swesolows 	switch (ret) {
57653eae19d9Swesolows 	case REP_PROTOCOL_FAIL_NOT_FOUND:
57663eae19d9Swesolows 		return (REP_PROTOCOL_SUCCESS);
57673eae19d9Swesolows 	case REP_PROTOCOL_SUCCESS:
57683eae19d9Swesolows 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
57693eae19d9Swesolows 	case REP_PROTOCOL_FAIL_DELETED:
57703eae19d9Swesolows 	case REP_PROTOCOL_FAIL_NO_RESOURCES:
57713eae19d9Swesolows 		return (ret);
57723eae19d9Swesolows 	default:
57733eae19d9Swesolows 		bad_error("rc_svc_prop_exists", ret);
57743eae19d9Swesolows 	}
57753eae19d9Swesolows 
57763eae19d9Swesolows 	return (REP_PROTOCOL_SUCCESS);
57773eae19d9Swesolows }
57783eae19d9Swesolows 
57793eae19d9Swesolows /*
57803eae19d9Swesolows  * Fails with
57813eae19d9Swesolows  *   _DELETED - np's node or parent has been deleted
57823eae19d9Swesolows  *   _TYPE_MISMATCH - np's node is not a property
57833eae19d9Swesolows  *   _NO_RESOURCES - out of memory
57843eae19d9Swesolows  *   _PERMISSION_DENIED - no authorization to read this property's value(s)
57853eae19d9Swesolows  *   _BAD_REQUEST - np's parent is not a property group
57863eae19d9Swesolows  */
57873eae19d9Swesolows static int
57883eae19d9Swesolows rc_node_property_may_read(rc_node_t *np)
57893eae19d9Swesolows {
5790a4dc1477STom Whitten 	int ret;
5791a4dc1477STom Whitten 	perm_status_t granted = PERM_DENIED;
57923eae19d9Swesolows 	rc_node_t *pgp;
57933eae19d9Swesolows 	permcheck_t *pcp;
57945b7f77adStw21770 	audit_event_data_t audit_data;
57955b7f77adStw21770 	size_t sz_out;
57963eae19d9Swesolows 
57973eae19d9Swesolows 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
57983eae19d9Swesolows 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
57993eae19d9Swesolows 
58003eae19d9Swesolows 	if (client_is_privileged())
58013eae19d9Swesolows 		return (REP_PROTOCOL_SUCCESS);
58023eae19d9Swesolows 
58033eae19d9Swesolows #ifdef NATIVE_BUILD
58043eae19d9Swesolows 	return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
58053eae19d9Swesolows #else
58063eae19d9Swesolows 	ret = rc_node_parent(np, &pgp);
58073eae19d9Swesolows 
58083eae19d9Swesolows 	if (ret != REP_PROTOCOL_SUCCESS)
58093eae19d9Swesolows 		return (ret);
58103eae19d9Swesolows 
58113eae19d9Swesolows 	if (pgp->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
58123eae19d9Swesolows 		rc_node_rele(pgp);
58133eae19d9Swesolows 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
58143eae19d9Swesolows 	}
58153eae19d9Swesolows 
58163eae19d9Swesolows 	ret = rc_node_pg_check_read_protect(pgp);
58173eae19d9Swesolows 
58183eae19d9Swesolows 	if (ret != REP_PROTOCOL_FAIL_PERMISSION_DENIED) {
58193eae19d9Swesolows 		rc_node_rele(pgp);
58203eae19d9Swesolows 		return (ret);
58213eae19d9Swesolows 	}
58223eae19d9Swesolows 
58233eae19d9Swesolows 	pcp = pc_create();
58243eae19d9Swesolows 
58253eae19d9Swesolows 	if (pcp == NULL) {
58263eae19d9Swesolows 		rc_node_rele(pgp);
58273eae19d9Swesolows 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
58283eae19d9Swesolows 	}
58293eae19d9Swesolows 
58303eae19d9Swesolows 	ret = perm_add_enabling(pcp, AUTH_MODIFY);
58313eae19d9Swesolows 
58323eae19d9Swesolows 	if (ret == REP_PROTOCOL_SUCCESS) {
58333eae19d9Swesolows 		const char * const auth =
58343eae19d9Swesolows 		    perm_auth_for_pgtype(pgp->rn_type);
58353eae19d9Swesolows 
58363eae19d9Swesolows 		if (auth != NULL)
58373eae19d9Swesolows 			ret = perm_add_enabling(pcp, auth);
58383eae19d9Swesolows 	}
58393eae19d9Swesolows 
58403eae19d9Swesolows 	/*
58413eae19d9Swesolows 	 * If you are permitted to modify the value, you may also
58423eae19d9Swesolows 	 * read it.  This means that both the MODIFY and VALUE
58433eae19d9Swesolows 	 * authorizations are acceptable.  We don't allow requests
58443eae19d9Swesolows 	 * for AUTH_PROP_MODIFY if all you have is $AUTH_PROP_VALUE,
58453eae19d9Swesolows 	 * however, to avoid leaking possibly valuable information
58463eae19d9Swesolows 	 * since such a user can't change the property anyway.
58473eae19d9Swesolows 	 */
58483eae19d9Swesolows 	if (ret == REP_PROTOCOL_SUCCESS)
58493eae19d9Swesolows 		ret = perm_add_enabling_values(pcp, pgp,
58503eae19d9Swesolows 		    AUTH_PROP_MODIFY);
58513eae19d9Swesolows 
58523eae19d9Swesolows 	if (ret == REP_PROTOCOL_SUCCESS &&
58533eae19d9Swesolows 	    strcmp(np->rn_name, AUTH_PROP_MODIFY) != 0)
58543eae19d9Swesolows 		ret = perm_add_enabling_values(pcp, pgp,
58553eae19d9Swesolows 		    AUTH_PROP_VALUE);
58563eae19d9Swesolows 
58573eae19d9Swesolows 	if (ret == REP_PROTOCOL_SUCCESS)
58583eae19d9Swesolows 		ret = perm_add_enabling_values(pcp, pgp,
58593eae19d9Swesolows 		    AUTH_PROP_READ);
58603eae19d9Swesolows 
58613eae19d9Swesolows 	rc_node_rele(pgp);
58623eae19d9Swesolows 
58633eae19d9Swesolows 	if (ret == REP_PROTOCOL_SUCCESS) {
58643eae19d9Swesolows 		granted = perm_granted(pcp);
5865a4dc1477STom Whitten 		if (granted == PERM_FAIL)
58663eae19d9Swesolows 			ret = REP_PROTOCOL_FAIL_NO_RESOURCES;
5867a4dc1477STom Whitten 		if (granted == PERM_GONE)
5868a4dc1477STom Whitten 			ret = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
58693eae19d9Swesolows 	}
5870a4dc1477STom Whitten 
58715b7f77adStw21770 	if (ret == REP_PROTOCOL_SUCCESS) {
58725b7f77adStw21770 		/* Generate a read_prop audit event. */
58735b7f77adStw21770 		audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
58745b7f77adStw21770 		if (audit_data.ed_fmri == NULL)
58755b7f77adStw21770 			ret = REP_PROTOCOL_FAIL_NO_RESOURCES;
58765b7f77adStw21770 	}
5877a4dc1477STom Whitten 	if (ret == REP_PROTOCOL_SUCCESS) {
58785b7f77adStw21770 		ret = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
58795b7f77adStw21770 		    REP_PROTOCOL_FMRI_LEN, &sz_out);
5880a4dc1477STom Whitten 	}
58815b7f77adStw21770 	if (ret == REP_PROTOCOL_SUCCESS) {
58825b7f77adStw21770 		int status;
58835b7f77adStw21770 		int ret_value;
58845b7f77adStw21770 
5885a4dc1477STom Whitten 		if (granted == PERM_DENIED) {
58865b7f77adStw21770 			status = ADT_FAILURE;
58875b7f77adStw21770 			ret_value = ADT_FAIL_VALUE_AUTH;
58885b7f77adStw21770 		} else {
58895b7f77adStw21770 			status = ADT_SUCCESS;
58905b7f77adStw21770 			ret_value = ADT_SUCCESS;
58915b7f77adStw21770 		}
58925b7f77adStw21770 		audit_data.ed_auth = pcp->pc_auth_string;
58935b7f77adStw21770 		smf_audit_event(ADT_smf_read_prop,
58945b7f77adStw21770 		    status, ret_value, &audit_data);
58955b7f77adStw21770 	}
58965b7f77adStw21770 	free(audit_data.ed_fmri);
58973eae19d9Swesolows 
58983eae19d9Swesolows 	pc_free(pcp);
58993eae19d9Swesolows 
5900a4dc1477STom Whitten 	if ((ret == REP_PROTOCOL_SUCCESS) && (granted == PERM_DENIED))
59013eae19d9Swesolows 		ret = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
59023eae19d9Swesolows 
59033eae19d9Swesolows 	return (ret);
59043eae19d9Swesolows #endif	/* NATIVE_BUILD */
59053eae19d9Swesolows }
59063eae19d9Swesolows 
59073eae19d9Swesolows /*
59087c478bd9Sstevel@tonic-gate  * Iteration
59097c478bd9Sstevel@tonic-gate  */
59107c478bd9Sstevel@tonic-gate static int
59117c478bd9Sstevel@tonic-gate rc_iter_filter_name(rc_node_t *np, void *s)
59127c478bd9Sstevel@tonic-gate {
59137c478bd9Sstevel@tonic-gate 	const char *name = s;
59147c478bd9Sstevel@tonic-gate 
59157c478bd9Sstevel@tonic-gate 	return (strcmp(np->rn_name, name) == 0);
59167c478bd9Sstevel@tonic-gate }
59177c478bd9Sstevel@tonic-gate 
59187c478bd9Sstevel@tonic-gate static int
59197c478bd9Sstevel@tonic-gate rc_iter_filter_type(rc_node_t *np, void *s)
59207c478bd9Sstevel@tonic-gate {
59217c478bd9Sstevel@tonic-gate 	const char *type = s;
59227c478bd9Sstevel@tonic-gate 
59237c478bd9Sstevel@tonic-gate 	return (np->rn_type != NULL && strcmp(np->rn_type, type) == 0);
59247c478bd9Sstevel@tonic-gate }
59257c478bd9Sstevel@tonic-gate 
59267c478bd9Sstevel@tonic-gate /*ARGSUSED*/
59277c478bd9Sstevel@tonic-gate static int
59287c478bd9Sstevel@tonic-gate rc_iter_null_filter(rc_node_t *np, void *s)
59297c478bd9Sstevel@tonic-gate {
59307c478bd9Sstevel@tonic-gate 	return (1);
59317c478bd9Sstevel@tonic-gate }
59327c478bd9Sstevel@tonic-gate 
59337c478bd9Sstevel@tonic-gate /*
59347c478bd9Sstevel@tonic-gate  * Allocate & initialize an rc_node_iter_t structure.  Essentially, ensure
59357c478bd9Sstevel@tonic-gate  * np->rn_children is populated and call uu_list_walk_start(np->rn_children).
59367c478bd9Sstevel@tonic-gate  * If successful, leaves a hold on np & increments np->rn_other_refs
59377c478bd9Sstevel@tonic-gate  *
59387c478bd9Sstevel@tonic-gate  * If composed is true, then set up for iteration across the top level of np's
59397c478bd9Sstevel@tonic-gate  * composition chain.  If successful, leaves a hold on np and increments
59407c478bd9Sstevel@tonic-gate  * rn_other_refs for the top level of np's composition chain.
59417c478bd9Sstevel@tonic-gate  *
59427c478bd9Sstevel@tonic-gate  * Fails with
59437c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES
59447c478bd9Sstevel@tonic-gate  *   _INVALID_TYPE
59457c478bd9Sstevel@tonic-gate  *   _TYPE_MISMATCH - np cannot carry type children
59467c478bd9Sstevel@tonic-gate  *   _DELETED
59477c478bd9Sstevel@tonic-gate  */
59487c478bd9Sstevel@tonic-gate static int
59497c478bd9Sstevel@tonic-gate rc_iter_create(rc_node_iter_t **resp, rc_node_t *np, uint32_t type,
59507c478bd9Sstevel@tonic-gate     rc_iter_filter_func *filter, void *arg, boolean_t composed)
59517c478bd9Sstevel@tonic-gate {
59527c478bd9Sstevel@tonic-gate 	rc_node_iter_t *nip;
59537c478bd9Sstevel@tonic-gate 	int res;
59547c478bd9Sstevel@tonic-gate 
59557c478bd9Sstevel@tonic-gate 	assert(*resp == NULL);
59567c478bd9Sstevel@tonic-gate 
59577c478bd9Sstevel@tonic-gate 	nip = uu_zalloc(sizeof (*nip));
59587c478bd9Sstevel@tonic-gate 	if (nip == NULL)
59597c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
59607c478bd9Sstevel@tonic-gate 
59617c478bd9Sstevel@tonic-gate 	/* np is held by the client's rc_node_ptr_t */
59627c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP)
59637c478bd9Sstevel@tonic-gate 		composed = 1;
59647c478bd9Sstevel@tonic-gate 
59657c478bd9Sstevel@tonic-gate 	if (!composed) {
59667c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
59677c478bd9Sstevel@tonic-gate 
59687c478bd9Sstevel@tonic-gate 		if ((res = rc_node_fill_children(np, type)) !=
59697c478bd9Sstevel@tonic-gate 		    REP_PROTOCOL_SUCCESS) {
59707c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
59717c478bd9Sstevel@tonic-gate 			uu_free(nip);
59727c478bd9Sstevel@tonic-gate 			return (res);
59737c478bd9Sstevel@tonic-gate 		}
59747c478bd9Sstevel@tonic-gate 
59757c478bd9Sstevel@tonic-gate 		nip->rni_clevel = -1;
59767c478bd9Sstevel@tonic-gate 
59777c478bd9Sstevel@tonic-gate 		nip->rni_iter = uu_list_walk_start(np->rn_children,
59787c478bd9Sstevel@tonic-gate 		    UU_WALK_ROBUST);
59797c478bd9Sstevel@tonic-gate 		if (nip->rni_iter != NULL) {
59807c478bd9Sstevel@tonic-gate 			nip->rni_iter_node = np;
59817c478bd9Sstevel@tonic-gate 			rc_node_hold_other(np);
59827c478bd9Sstevel@tonic-gate 		} else {
59837c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
59847c478bd9Sstevel@tonic-gate 			uu_free(nip);
59857c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
59867c478bd9Sstevel@tonic-gate 		}
59877c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
59887c478bd9Sstevel@tonic-gate 	} else {
59897c478bd9Sstevel@tonic-gate 		rc_node_t *ent;
59907c478bd9Sstevel@tonic-gate 
59917c478bd9Sstevel@tonic-gate 		if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_SNAPSHOT) {
59927c478bd9Sstevel@tonic-gate 			/* rn_cchain isn't valid until children are loaded. */
59937c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
59947c478bd9Sstevel@tonic-gate 			res = rc_node_fill_children(np,
59957c478bd9Sstevel@tonic-gate 			    REP_PROTOCOL_ENTITY_SNAPLEVEL);
59967c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
59977c478bd9Sstevel@tonic-gate 			if (res != REP_PROTOCOL_SUCCESS) {
59987c478bd9Sstevel@tonic-gate 				uu_free(nip);
59997c478bd9Sstevel@tonic-gate 				return (res);
60007c478bd9Sstevel@tonic-gate 			}
60017c478bd9Sstevel@tonic-gate 
60027c478bd9Sstevel@tonic-gate 			/* Check for an empty snapshot. */
60037c478bd9Sstevel@tonic-gate 			if (np->rn_cchain[0] == NULL)
60047c478bd9Sstevel@tonic-gate 				goto empty;
60057c478bd9Sstevel@tonic-gate 		}
60067c478bd9Sstevel@tonic-gate 
60077c478bd9Sstevel@tonic-gate 		/* Start at the top of the composition chain. */
60087c478bd9Sstevel@tonic-gate 		for (nip->rni_clevel = 0; ; ++nip->rni_clevel) {
60097c478bd9Sstevel@tonic-gate 			if (nip->rni_clevel >= COMPOSITION_DEPTH) {
60107c478bd9Sstevel@tonic-gate 				/* Empty composition chain. */
60117c478bd9Sstevel@tonic-gate empty:
60127c478bd9Sstevel@tonic-gate 				nip->rni_clevel = -1;
60137c478bd9Sstevel@tonic-gate 				nip->rni_iter = NULL;
60147c478bd9Sstevel@tonic-gate 				/* It's ok, iter_next() will return _DONE. */
60157c478bd9Sstevel@tonic-gate 				goto out;
60167c478bd9Sstevel@tonic-gate 			}
60177c478bd9Sstevel@tonic-gate 
60187c478bd9Sstevel@tonic-gate 			ent = np->rn_cchain[nip->rni_clevel];
60197c478bd9Sstevel@tonic-gate 			assert(ent != NULL);
60207c478bd9Sstevel@tonic-gate 
60217c478bd9Sstevel@tonic-gate 			if (rc_node_check_and_lock(ent) == REP_PROTOCOL_SUCCESS)
60227c478bd9Sstevel@tonic-gate 				break;
60237c478bd9Sstevel@tonic-gate 
60247c478bd9Sstevel@tonic-gate 			/* Someone deleted it, so try the next one. */
60257c478bd9Sstevel@tonic-gate 		}
60267c478bd9Sstevel@tonic-gate 
60277c478bd9Sstevel@tonic-gate 		res = rc_node_fill_children(ent, type);
60287c478bd9Sstevel@tonic-gate 
60297c478bd9Sstevel@tonic-gate 		if (res == REP_PROTOCOL_SUCCESS) {
60307c478bd9Sstevel@tonic-gate 			nip->rni_iter = uu_list_walk_start(ent->rn_children,
60317c478bd9Sstevel@tonic-gate 			    UU_WALK_ROBUST);
60327c478bd9Sstevel@tonic-gate 
60337c478bd9Sstevel@tonic-gate 			if (nip->rni_iter == NULL)
60347c478bd9Sstevel@tonic-gate 				res = REP_PROTOCOL_FAIL_NO_RESOURCES;
60357c478bd9Sstevel@tonic-gate 			else {
60367c478bd9Sstevel@tonic-gate 				nip->rni_iter_node = ent;
60377c478bd9Sstevel@tonic-gate 				rc_node_hold_other(ent);
60387c478bd9Sstevel@tonic-gate 			}
60397c478bd9Sstevel@tonic-gate 		}
60407c478bd9Sstevel@tonic-gate 
60417c478bd9Sstevel@tonic-gate 		if (res != REP_PROTOCOL_SUCCESS) {
60427c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&ent->rn_lock);
60437c478bd9Sstevel@tonic-gate 			uu_free(nip);
60447c478bd9Sstevel@tonic-gate 			return (res);
60457c478bd9Sstevel@tonic-gate 		}
60467c478bd9Sstevel@tonic-gate 
60477c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&ent->rn_lock);
60487c478bd9Sstevel@tonic-gate 	}
60497c478bd9Sstevel@tonic-gate 
60507c478bd9Sstevel@tonic-gate out:
60517c478bd9Sstevel@tonic-gate 	rc_node_hold(np);		/* released by rc_iter_end() */
60527c478bd9Sstevel@tonic-gate 	nip->rni_parent = np;
60537c478bd9Sstevel@tonic-gate 	nip->rni_type = type;
60547c478bd9Sstevel@tonic-gate 	nip->rni_filter = (filter != NULL)? filter : rc_iter_null_filter;
60557c478bd9Sstevel@tonic-gate 	nip->rni_filter_arg = arg;
60567c478bd9Sstevel@tonic-gate 	*resp = nip;
60577c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
60587c478bd9Sstevel@tonic-gate }
60597c478bd9Sstevel@tonic-gate 
60607c478bd9Sstevel@tonic-gate static void
60617c478bd9Sstevel@tonic-gate rc_iter_end(rc_node_iter_t *iter)
60627c478bd9Sstevel@tonic-gate {
60637c478bd9Sstevel@tonic-gate 	rc_node_t *np = iter->rni_parent;
60647c478bd9Sstevel@tonic-gate 
60657c478bd9Sstevel@tonic-gate 	if (iter->rni_clevel >= 0)
60667c478bd9Sstevel@tonic-gate 		np = np->rn_cchain[iter->rni_clevel];
60677c478bd9Sstevel@tonic-gate 
60687c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
60697c478bd9Sstevel@tonic-gate 	if (iter->rni_iter != NULL)
60707c478bd9Sstevel@tonic-gate 		uu_list_walk_end(iter->rni_iter);
60717c478bd9Sstevel@tonic-gate 	iter->rni_iter = NULL;
60727c478bd9Sstevel@tonic-gate 
60737c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
60747c478bd9Sstevel@tonic-gate 	rc_node_rele(iter->rni_parent);
60757c478bd9Sstevel@tonic-gate 	if (iter->rni_iter_node != NULL)
60767c478bd9Sstevel@tonic-gate 		rc_node_rele_other(iter->rni_iter_node);
60777c478bd9Sstevel@tonic-gate }
60787c478bd9Sstevel@tonic-gate 
60797c478bd9Sstevel@tonic-gate /*
60807c478bd9Sstevel@tonic-gate  * Fails with
60817c478bd9Sstevel@tonic-gate  *   _NOT_SET - npp is reset
60827c478bd9Sstevel@tonic-gate  *   _DELETED - npp's node has been deleted
60837c478bd9Sstevel@tonic-gate  *   _NOT_APPLICABLE - npp's node is not a property
60847c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES - out of memory
60857c478bd9Sstevel@tonic-gate  */
60867c478bd9Sstevel@tonic-gate static int
60877c478bd9Sstevel@tonic-gate rc_node_setup_value_iter(rc_node_ptr_t *npp, rc_node_iter_t **iterp)
60887c478bd9Sstevel@tonic-gate {
60897c478bd9Sstevel@tonic-gate 	rc_node_t *np;
60907c478bd9Sstevel@tonic-gate 
60917c478bd9Sstevel@tonic-gate 	rc_node_iter_t *nip;
60927c478bd9Sstevel@tonic-gate 
60937c478bd9Sstevel@tonic-gate 	assert(*iterp == NULL);
60947c478bd9Sstevel@tonic-gate 
60957c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
60967c478bd9Sstevel@tonic-gate 
60977c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) {
60987c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
60997c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
61007c478bd9Sstevel@tonic-gate 	}
61017c478bd9Sstevel@tonic-gate 
61027c478bd9Sstevel@tonic-gate 	nip = uu_zalloc(sizeof (*nip));
61037c478bd9Sstevel@tonic-gate 	if (nip == NULL) {
61047c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
61057c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
61067c478bd9Sstevel@tonic-gate 	}
61077c478bd9Sstevel@tonic-gate 
61087c478bd9Sstevel@tonic-gate 	nip->rni_parent = np;
61097c478bd9Sstevel@tonic-gate 	nip->rni_iter = NULL;
61107c478bd9Sstevel@tonic-gate 	nip->rni_clevel = -1;
61117c478bd9Sstevel@tonic-gate 	nip->rni_type = REP_PROTOCOL_ENTITY_VALUE;
61127c478bd9Sstevel@tonic-gate 	nip->rni_offset = 0;
61137c478bd9Sstevel@tonic-gate 	nip->rni_last_offset = 0;
61147c478bd9Sstevel@tonic-gate 
61157c478bd9Sstevel@tonic-gate 	rc_node_hold_locked(np);
61167c478bd9Sstevel@tonic-gate 
61177c478bd9Sstevel@tonic-gate 	*iterp = nip;
61187c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
61197c478bd9Sstevel@tonic-gate 
61207c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
61217c478bd9Sstevel@tonic-gate }
61227c478bd9Sstevel@tonic-gate 
61237c478bd9Sstevel@tonic-gate /*
61247c478bd9Sstevel@tonic-gate  * Returns:
61253eae19d9Swesolows  *   _NO_RESOURCES - out of memory
61267c478bd9Sstevel@tonic-gate  *   _NOT_SET - npp is reset
61277c478bd9Sstevel@tonic-gate  *   _DELETED - npp's node has been deleted
61287c478bd9Sstevel@tonic-gate  *   _TYPE_MISMATCH - npp's node is not a property
61297c478bd9Sstevel@tonic-gate  *   _NOT_FOUND - property has no values
61307c478bd9Sstevel@tonic-gate  *   _TRUNCATED - property has >1 values (first is written into out)
61317c478bd9Sstevel@tonic-gate  *   _SUCCESS - property has 1 value (which is written into out)
61323eae19d9Swesolows  *   _PERMISSION_DENIED - no authorization to read property value(s)
61337c478bd9Sstevel@tonic-gate  *
61347c478bd9Sstevel@tonic-gate  * We shorten *sz_out to not include anything after the final '\0'.
61357c478bd9Sstevel@tonic-gate  */
61367c478bd9Sstevel@tonic-gate int
61377c478bd9Sstevel@tonic-gate rc_node_get_property_value(rc_node_ptr_t *npp,
61387c478bd9Sstevel@tonic-gate     struct rep_protocol_value_response *out, size_t *sz_out)
61397c478bd9Sstevel@tonic-gate {
61407c478bd9Sstevel@tonic-gate 	rc_node_t *np;
61417c478bd9Sstevel@tonic-gate 	size_t w;
61427c478bd9Sstevel@tonic-gate 	int ret;
61437c478bd9Sstevel@tonic-gate 
61447c478bd9Sstevel@tonic-gate 	assert(*sz_out == sizeof (*out));
61457c478bd9Sstevel@tonic-gate 
61463eae19d9Swesolows 	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
61473eae19d9Swesolows 	ret = rc_node_property_may_read(np);
61483eae19d9Swesolows 	rc_node_rele(np);
61493eae19d9Swesolows 
61503eae19d9Swesolows 	if (ret != REP_PROTOCOL_SUCCESS)
61513eae19d9Swesolows 		return (ret);
61523eae19d9Swesolows 
61537c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
61547c478bd9Sstevel@tonic-gate 
61557c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) {
61567c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
61577c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
61587c478bd9Sstevel@tonic-gate 	}
61597c478bd9Sstevel@tonic-gate 
61607c478bd9Sstevel@tonic-gate 	if (np->rn_values_size == 0) {
61617c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
61627c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
61637c478bd9Sstevel@tonic-gate 	}
61647c478bd9Sstevel@tonic-gate 	out->rpr_type = np->rn_valtype;
61657c478bd9Sstevel@tonic-gate 	w = strlcpy(out->rpr_value, &np->rn_values[0],
61667c478bd9Sstevel@tonic-gate 	    sizeof (out->rpr_value));
61677c478bd9Sstevel@tonic-gate 
61687c478bd9Sstevel@tonic-gate 	if (w >= sizeof (out->rpr_value))
61697c478bd9Sstevel@tonic-gate 		backend_panic("value too large");
61707c478bd9Sstevel@tonic-gate 
61717c478bd9Sstevel@tonic-gate 	*sz_out = offsetof(struct rep_protocol_value_response,
61727c478bd9Sstevel@tonic-gate 	    rpr_value[w + 1]);
61737c478bd9Sstevel@tonic-gate 
61747c478bd9Sstevel@tonic-gate 	ret = (np->rn_values_count != 1)? REP_PROTOCOL_FAIL_TRUNCATED :
61757c478bd9Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS;
61767c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
61777c478bd9Sstevel@tonic-gate 	return (ret);
61787c478bd9Sstevel@tonic-gate }
61797c478bd9Sstevel@tonic-gate 
61807c478bd9Sstevel@tonic-gate int
61817c478bd9Sstevel@tonic-gate rc_iter_next_value(rc_node_iter_t *iter,
61827c478bd9Sstevel@tonic-gate     struct rep_protocol_value_response *out, size_t *sz_out, int repeat)
61837c478bd9Sstevel@tonic-gate {
61847c478bd9Sstevel@tonic-gate 	rc_node_t *np = iter->rni_parent;
61857c478bd9Sstevel@tonic-gate 	const char *vals;
61867c478bd9Sstevel@tonic-gate 	size_t len;
61877c478bd9Sstevel@tonic-gate 
61887c478bd9Sstevel@tonic-gate 	size_t start;
61897c478bd9Sstevel@tonic-gate 	size_t w;
61903eae19d9Swesolows 	int ret;
61917c478bd9Sstevel@tonic-gate 
61927c478bd9Sstevel@tonic-gate 	rep_protocol_responseid_t result;
61937c478bd9Sstevel@tonic-gate 
61947c478bd9Sstevel@tonic-gate 	assert(*sz_out == sizeof (*out));
61957c478bd9Sstevel@tonic-gate 
61967c478bd9Sstevel@tonic-gate 	(void) memset(out, '\0', *sz_out);
61977c478bd9Sstevel@tonic-gate 
61987c478bd9Sstevel@tonic-gate 	if (iter->rni_type != REP_PROTOCOL_ENTITY_VALUE)
61997c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
62007c478bd9Sstevel@tonic-gate 
62013eae19d9Swesolows 	RC_NODE_CHECK(np);
62023eae19d9Swesolows 	ret = rc_node_property_may_read(np);
62033eae19d9Swesolows 
62043eae19d9Swesolows 	if (ret != REP_PROTOCOL_SUCCESS)
62053eae19d9Swesolows 		return (ret);
62063eae19d9Swesolows 
62077c478bd9Sstevel@tonic-gate 	RC_NODE_CHECK_AND_LOCK(np);
62087c478bd9Sstevel@tonic-gate 
62097c478bd9Sstevel@tonic-gate 	vals = np->rn_values;
62107c478bd9Sstevel@tonic-gate 	len = np->rn_values_size;
62117c478bd9Sstevel@tonic-gate 
62127c478bd9Sstevel@tonic-gate 	out->rpr_type = np->rn_valtype;
62137c478bd9Sstevel@tonic-gate 
62147c478bd9Sstevel@tonic-gate 	start = (repeat)? iter->rni_last_offset : iter->rni_offset;
62157c478bd9Sstevel@tonic-gate 
62167c478bd9Sstevel@tonic-gate 	if (len == 0 || start >= len) {
62177c478bd9Sstevel@tonic-gate 		result = REP_PROTOCOL_DONE;
62187c478bd9Sstevel@tonic-gate 		*sz_out -= sizeof (out->rpr_value);
62197c478bd9Sstevel@tonic-gate 	} else {
62207c478bd9Sstevel@tonic-gate 		w = strlcpy(out->rpr_value, &vals[start],
62217c478bd9Sstevel@tonic-gate 		    sizeof (out->rpr_value));
62227c478bd9Sstevel@tonic-gate 
62237c478bd9Sstevel@tonic-gate 		if (w >= sizeof (out->rpr_value))
62247c478bd9Sstevel@tonic-gate 			backend_panic("value too large");
62257c478bd9Sstevel@tonic-gate 
62267c478bd9Sstevel@tonic-gate 		*sz_out = offsetof(struct rep_protocol_value_response,
62277c478bd9Sstevel@tonic-gate 		    rpr_value[w + 1]);
62287c478bd9Sstevel@tonic-gate 
62297c478bd9Sstevel@tonic-gate 		/*
62307c478bd9Sstevel@tonic-gate 		 * update the offsets if we're not repeating
62317c478bd9Sstevel@tonic-gate 		 */
62327c478bd9Sstevel@tonic-gate 		if (!repeat) {
62337c478bd9Sstevel@tonic-gate 			iter->rni_last_offset = iter->rni_offset;
62347c478bd9Sstevel@tonic-gate 			iter->rni_offset += (w + 1);
62357c478bd9Sstevel@tonic-gate 		}
62367c478bd9Sstevel@tonic-gate 
62377c478bd9Sstevel@tonic-gate 		result = REP_PROTOCOL_SUCCESS;
62387c478bd9Sstevel@tonic-gate 	}
62397c478bd9Sstevel@tonic-gate 
62407c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
62417c478bd9Sstevel@tonic-gate 	return (result);
62427c478bd9Sstevel@tonic-gate }
62437c478bd9Sstevel@tonic-gate 
62447c478bd9Sstevel@tonic-gate /*
62457c478bd9Sstevel@tonic-gate  * Entry point for ITER_START from client.c.  Validate the arguments & call
62467c478bd9Sstevel@tonic-gate  * rc_iter_create().
62477c478bd9Sstevel@tonic-gate  *
62487c478bd9Sstevel@tonic-gate  * Fails with
62497c478bd9Sstevel@tonic-gate  *   _NOT_SET
62507c478bd9Sstevel@tonic-gate  *   _DELETED
62517c478bd9Sstevel@tonic-gate  *   _TYPE_MISMATCH - np cannot carry type children
62527c478bd9Sstevel@tonic-gate  *   _BAD_REQUEST - flags is invalid
62537c478bd9Sstevel@tonic-gate  *		    pattern is invalid
62547c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES
62557c478bd9Sstevel@tonic-gate  *   _INVALID_TYPE
62567c478bd9Sstevel@tonic-gate  *   _TYPE_MISMATCH - *npp cannot have children of type
62577c478bd9Sstevel@tonic-gate  *   _BACKEND_ACCESS
62587c478bd9Sstevel@tonic-gate  */
62597c478bd9Sstevel@tonic-gate int
62607c478bd9Sstevel@tonic-gate rc_node_setup_iter(rc_node_ptr_t *npp, rc_node_iter_t **iterp,
62617c478bd9Sstevel@tonic-gate     uint32_t type, uint32_t flags, const char *pattern)
62627c478bd9Sstevel@tonic-gate {
62637c478bd9Sstevel@tonic-gate 	rc_node_t *np;
62647c478bd9Sstevel@tonic-gate 	rc_iter_filter_func *f = NULL;
62657c478bd9Sstevel@tonic-gate 	int rc;
62667c478bd9Sstevel@tonic-gate 
62677c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK(np, npp);
62687c478bd9Sstevel@tonic-gate 
62697c478bd9Sstevel@tonic-gate 	if (pattern != NULL && pattern[0] == '\0')
62707c478bd9Sstevel@tonic-gate 		pattern = NULL;
62717c478bd9Sstevel@tonic-gate 
62727c478bd9Sstevel@tonic-gate 	if (type == REP_PROTOCOL_ENTITY_VALUE) {
62737c478bd9Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
62747c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
62757c478bd9Sstevel@tonic-gate 		if (flags != RP_ITER_START_ALL || pattern != NULL)
62767c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_BAD_REQUEST);
62777c478bd9Sstevel@tonic-gate 
62787c478bd9Sstevel@tonic-gate 		rc = rc_node_setup_value_iter(npp, iterp);
62797c478bd9Sstevel@tonic-gate 		assert(rc != REP_PROTOCOL_FAIL_NOT_APPLICABLE);
62807c478bd9Sstevel@tonic-gate 		return (rc);
62817c478bd9Sstevel@tonic-gate 	}
62827c478bd9Sstevel@tonic-gate 
62837c478bd9Sstevel@tonic-gate 	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
62847c478bd9Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS)
62857c478bd9Sstevel@tonic-gate 		return (rc);
62867c478bd9Sstevel@tonic-gate 
62877c478bd9Sstevel@tonic-gate 	if (((flags & RP_ITER_START_FILT_MASK) == RP_ITER_START_ALL) ^
62887c478bd9Sstevel@tonic-gate 	    (pattern == NULL))
62897c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
62907c478bd9Sstevel@tonic-gate 
62917c478bd9Sstevel@tonic-gate 	/* Composition only works for instances & snapshots. */
62927c478bd9Sstevel@tonic-gate 	if ((flags & RP_ITER_START_COMPOSED) &&
62937c478bd9Sstevel@tonic-gate 	    (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE &&
62947c478bd9Sstevel@tonic-gate 	    np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT))
62957c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
62967c478bd9Sstevel@tonic-gate 
62977c478bd9Sstevel@tonic-gate 	if (pattern != NULL) {
62987c478bd9Sstevel@tonic-gate 		if ((rc = rc_check_type_name(type, pattern)) !=
62997c478bd9Sstevel@tonic-gate 		    REP_PROTOCOL_SUCCESS)
63007c478bd9Sstevel@tonic-gate 			return (rc);
63017c478bd9Sstevel@tonic-gate 		pattern = strdup(pattern);
63027c478bd9Sstevel@tonic-gate 		if (pattern == NULL)
63037c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
63047c478bd9Sstevel@tonic-gate 	}
63057c478bd9Sstevel@tonic-gate 
63067c478bd9Sstevel@tonic-gate 	switch (flags & RP_ITER_START_FILT_MASK) {
63077c478bd9Sstevel@tonic-gate 	case RP_ITER_START_ALL:
63087c478bd9Sstevel@tonic-gate 		f = NULL;
63097c478bd9Sstevel@tonic-gate 		break;
63107c478bd9Sstevel@tonic-gate 	case RP_ITER_START_EXACT:
63117c478bd9Sstevel@tonic-gate 		f = rc_iter_filter_name;
63127c478bd9Sstevel@tonic-gate 		break;
63137c478bd9Sstevel@tonic-gate 	case RP_ITER_START_PGTYPE:
63147c478bd9Sstevel@tonic-gate 		if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
63157c478bd9Sstevel@tonic-gate 			free((void *)pattern);
63167c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_BAD_REQUEST);
63177c478bd9Sstevel@tonic-gate 		}
63187c478bd9Sstevel@tonic-gate 		f = rc_iter_filter_type;
63197c478bd9Sstevel@tonic-gate 		break;
63207c478bd9Sstevel@tonic-gate 	default:
63217c478bd9Sstevel@tonic-gate 		free((void *)pattern);
63227c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
63237c478bd9Sstevel@tonic-gate 	}
63247c478bd9Sstevel@tonic-gate 
63257c478bd9Sstevel@tonic-gate 	rc = rc_iter_create(iterp, np, type, f, (void *)pattern,
63267c478bd9Sstevel@tonic-gate 	    flags & RP_ITER_START_COMPOSED);
63277c478bd9Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS && pattern != NULL)
63287c478bd9Sstevel@tonic-gate 		free((void *)pattern);
63297c478bd9Sstevel@tonic-gate 
63307c478bd9Sstevel@tonic-gate 	return (rc);
63317c478bd9Sstevel@tonic-gate }
63327c478bd9Sstevel@tonic-gate 
63337c478bd9Sstevel@tonic-gate /*
63347c478bd9Sstevel@tonic-gate  * Do uu_list_walk_next(iter->rni_iter) until we find a child which matches
63357c478bd9Sstevel@tonic-gate  * the filter.
63367c478bd9Sstevel@tonic-gate  * For composed iterators, then check to see if there's an overlapping entity
63377c478bd9Sstevel@tonic-gate  * (see embedded comments).  If we reach the end of the list, start over at
63387c478bd9Sstevel@tonic-gate  * the next level.
63397c478bd9Sstevel@tonic-gate  *
63407c478bd9Sstevel@tonic-gate  * Returns
63417c478bd9Sstevel@tonic-gate  *   _BAD_REQUEST - iter walks values
63427c478bd9Sstevel@tonic-gate  *   _TYPE_MISMATCH - iter does not walk type entities
63437c478bd9Sstevel@tonic-gate  *   _DELETED - parent was deleted
63447c478bd9Sstevel@tonic-gate  *   _NO_RESOURCES
63457c478bd9Sstevel@tonic-gate  *   _INVALID_TYPE - type is invalid
63467c478bd9Sstevel@tonic-gate  *   _DONE
63477c478bd9Sstevel@tonic-gate  *   _SUCCESS
63487c478bd9Sstevel@tonic-gate  *
63497c478bd9Sstevel@tonic-gate  * For composed property group iterators, can also return
63507c478bd9Sstevel@tonic-gate  *   _TYPE_MISMATCH - parent cannot have type children
63517c478bd9Sstevel@tonic-gate  */
63527c478bd9Sstevel@tonic-gate int
63537c478bd9Sstevel@tonic-gate rc_iter_next(rc_node_iter_t *iter, rc_node_ptr_t *out, uint32_t type)
63547c478bd9Sstevel@tonic-gate {
63557c478bd9Sstevel@tonic-gate 	rc_node_t *np = iter->rni_parent;
63567c478bd9Sstevel@tonic-gate 	rc_node_t *res;
63577c478bd9Sstevel@tonic-gate 	int rc;
63587c478bd9Sstevel@tonic-gate 
63597c478bd9Sstevel@tonic-gate 	if (iter->rni_type == REP_PROTOCOL_ENTITY_VALUE)
63607c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
63617c478bd9Sstevel@tonic-gate 
63627c478bd9Sstevel@tonic-gate 	if (iter->rni_iter == NULL) {
63637c478bd9Sstevel@tonic-gate 		rc_node_clear(out, 0);
63647c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_DONE);
63657c478bd9Sstevel@tonic-gate 	}
63667c478bd9Sstevel@tonic-gate 
63677c478bd9Sstevel@tonic-gate 	if (iter->rni_type != type) {
63687c478bd9Sstevel@tonic-gate 		rc_node_clear(out, 0);
63697c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
63707c478bd9Sstevel@tonic-gate 	}
63717c478bd9Sstevel@tonic-gate 
63727c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);  /* held by _iter_create() */
63737c478bd9Sstevel@tonic-gate 
63747c478bd9Sstevel@tonic-gate 	if (!rc_node_wait_flag(np, RC_NODE_CHILDREN_CHANGING)) {
63757c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
63767c478bd9Sstevel@tonic-gate 		rc_node_clear(out, 1);
63777c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_DELETED);
63787c478bd9Sstevel@tonic-gate 	}
63797c478bd9Sstevel@tonic-gate 
63807c478bd9Sstevel@tonic-gate 	if (iter->rni_clevel >= 0) {
63817c478bd9Sstevel@tonic-gate 		/* Composed iterator.  Iterate over appropriate level. */
63827c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
63837c478bd9Sstevel@tonic-gate 		np = np->rn_cchain[iter->rni_clevel];
63847c478bd9Sstevel@tonic-gate 		/*
63857c478bd9Sstevel@tonic-gate 		 * If iter->rni_parent is an instance or a snapshot, np must
63867c478bd9Sstevel@tonic-gate 		 * be valid since iter holds iter->rni_parent & possible
63877c478bd9Sstevel@tonic-gate 		 * levels (service, instance, snaplevel) cannot be destroyed
63887c478bd9Sstevel@tonic-gate 		 * while rni_parent is held.  If iter->rni_parent is
63897c478bd9Sstevel@tonic-gate 		 * a composed property group then rc_node_setup_cpg() put
63907c478bd9Sstevel@tonic-gate 		 * a hold on np.
63917c478bd9Sstevel@tonic-gate 		 */
63927c478bd9Sstevel@tonic-gate 
63937c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
63947c478bd9Sstevel@tonic-gate 
63957c478bd9Sstevel@tonic-gate 		if (!rc_node_wait_flag(np, RC_NODE_CHILDREN_CHANGING)) {
63967c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
63977c478bd9Sstevel@tonic-gate 			rc_node_clear(out, 1);
63987c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_DELETED);
63997c478bd9Sstevel@tonic-gate 		}
64007c478bd9Sstevel@tonic-gate 	}
64017c478bd9Sstevel@tonic-gate 
64027c478bd9Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_HAS_CHILDREN);
64037c478bd9Sstevel@tonic-gate 
64047c478bd9Sstevel@tonic-gate 	for (;;) {
64057c478bd9Sstevel@tonic-gate 		res = uu_list_walk_next(iter->rni_iter);
64067c478bd9Sstevel@tonic-gate 		if (res == NULL) {
64077c478bd9Sstevel@tonic-gate 			rc_node_t *parent = iter->rni_parent;
64087c478bd9Sstevel@tonic-gate 
64097c478bd9Sstevel@tonic-gate #if COMPOSITION_DEPTH == 2
64107c478bd9Sstevel@tonic-gate 			if (iter->rni_clevel < 0 || iter->rni_clevel == 1) {
64117c478bd9Sstevel@tonic-gate 				/* release walker and lock */
64127c478bd9Sstevel@tonic-gate 				rc_iter_end(iter);
64137c478bd9Sstevel@tonic-gate 				break;
64147c478bd9Sstevel@tonic-gate 			}
64157c478bd9Sstevel@tonic-gate 
64167c478bd9Sstevel@tonic-gate 			/* Stop walking current level. */
64177c478bd9Sstevel@tonic-gate 			uu_list_walk_end(iter->rni_iter);
64187c478bd9Sstevel@tonic-gate 			iter->rni_iter = NULL;
64197c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
64207c478bd9Sstevel@tonic-gate 			rc_node_rele_other(iter->rni_iter_node);
64217c478bd9Sstevel@tonic-gate 			iter->rni_iter_node = NULL;
64227c478bd9Sstevel@tonic-gate 
64237c478bd9Sstevel@tonic-gate 			/* Start walking next level. */
64247c478bd9Sstevel@tonic-gate 			++iter->rni_clevel;
64257c478bd9Sstevel@tonic-gate 			np = parent->rn_cchain[iter->rni_clevel];
64267c478bd9Sstevel@tonic-gate 			assert(np != NULL);
64277c478bd9Sstevel@tonic-gate #else
64287c478bd9Sstevel@tonic-gate #error This code must be updated.
64297c478bd9Sstevel@tonic-gate #endif
64307c478bd9Sstevel@tonic-gate 
64317c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
64327c478bd9Sstevel@tonic-gate 
64337c478bd9Sstevel@tonic-gate 			rc = rc_node_fill_children(np, iter->rni_type);
64347c478bd9Sstevel@tonic-gate 
64357c478bd9Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS) {
64367c478bd9Sstevel@tonic-gate 				iter->rni_iter =
64377c478bd9Sstevel@tonic-gate 				    uu_list_walk_start(np->rn_children,
64387c478bd9Sstevel@tonic-gate 				    UU_WALK_ROBUST);
64397c478bd9Sstevel@tonic-gate 
64407c478bd9Sstevel@tonic-gate 				if (iter->rni_iter == NULL)
64417c478bd9Sstevel@tonic-gate 					rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
64427c478bd9Sstevel@tonic-gate 				else {
64437c478bd9Sstevel@tonic-gate 					iter->rni_iter_node = np;
64447c478bd9Sstevel@tonic-gate 					rc_node_hold_other(np);
64457c478bd9Sstevel@tonic-gate 				}
64467c478bd9Sstevel@tonic-gate 			}
64477c478bd9Sstevel@tonic-gate 
64487c478bd9Sstevel@tonic-gate 			if (rc != REP_PROTOCOL_SUCCESS) {
64497c478bd9Sstevel@tonic-gate 				(void) pthread_mutex_unlock(&np->rn_lock);
64507c478bd9Sstevel@tonic-gate 				rc_node_clear(out, 0);
64517c478bd9Sstevel@tonic-gate 				return (rc);
64527c478bd9Sstevel@tonic-gate 			}
64537c478bd9Sstevel@tonic-gate 
64547c478bd9Sstevel@tonic-gate 			continue;
64557c478bd9Sstevel@tonic-gate 		}
64567c478bd9Sstevel@tonic-gate 
64577c478bd9Sstevel@tonic-gate 		if (res->rn_id.rl_type != type ||
64587c478bd9Sstevel@tonic-gate 		    !iter->rni_filter(res, iter->rni_filter_arg))
64597c478bd9Sstevel@tonic-gate 			continue;
64607c478bd9Sstevel@tonic-gate 
64617c478bd9Sstevel@tonic-gate 		/*
64627c478bd9Sstevel@tonic-gate 		 * If we're composed and not at the top level, check to see if
64637c478bd9Sstevel@tonic-gate 		 * there's an entity at a higher level with the same name.  If
64647c478bd9Sstevel@tonic-gate 		 * so, skip this one.
64657c478bd9Sstevel@tonic-gate 		 */
64667c478bd9Sstevel@tonic-gate 		if (iter->rni_clevel > 0) {
64677c478bd9Sstevel@tonic-gate 			rc_node_t *ent = iter->rni_parent->rn_cchain[0];
64687c478bd9Sstevel@tonic-gate 			rc_node_t *pg;
64697c478bd9Sstevel@tonic-gate 
64707c478bd9Sstevel@tonic-gate #if COMPOSITION_DEPTH == 2
64717c478bd9Sstevel@tonic-gate 			assert(iter->rni_clevel == 1);
64727c478bd9Sstevel@tonic-gate 
64737c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
64747c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&ent->rn_lock);
64757c478bd9Sstevel@tonic-gate 			rc = rc_node_find_named_child(ent, res->rn_name, type,
64767c478bd9Sstevel@tonic-gate 			    &pg);
64777c478bd9Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS && pg != NULL)
64787c478bd9Sstevel@tonic-gate 				rc_node_rele(pg);
64797c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&ent->rn_lock);
64807c478bd9Sstevel@tonic-gate 			if (rc != REP_PROTOCOL_SUCCESS) {
64817c478bd9Sstevel@tonic-gate 				rc_node_clear(out, 0);
64827c478bd9Sstevel@tonic-gate 				return (rc);
64837c478bd9Sstevel@tonic-gate 			}
64847c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
64857c478bd9Sstevel@tonic-gate 
64867c478bd9Sstevel@tonic-gate 			/* Make sure np isn't being deleted all of a sudden. */
64877c478bd9Sstevel@tonic-gate 			if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
64887c478bd9Sstevel@tonic-gate 				(void) pthread_mutex_unlock(&np->rn_lock);
64897c478bd9Sstevel@tonic-gate 				rc_node_clear(out, 1);
64907c478bd9Sstevel@tonic-gate 				return (REP_PROTOCOL_FAIL_DELETED);
64917c478bd9Sstevel@tonic-gate 			}
64927c478bd9Sstevel@tonic-gate 
64937c478bd9Sstevel@tonic-gate 			if (pg != NULL)
64947c478bd9Sstevel@tonic-gate 				/* Keep going. */
64957c478bd9Sstevel@tonic-gate 				continue;
64967c478bd9Sstevel@tonic-gate #else
64977c478bd9Sstevel@tonic-gate #error This code must be updated.
64987c478bd9Sstevel@tonic-gate #endif
64997c478bd9Sstevel@tonic-gate 		}
65007c478bd9Sstevel@tonic-gate 
65017c478bd9Sstevel@tonic-gate 		/*
65027c478bd9Sstevel@tonic-gate 		 * If we're composed, iterating over property groups, and not
65037c478bd9Sstevel@tonic-gate 		 * at the bottom level, check to see if there's a pg at lower
65047c478bd9Sstevel@tonic-gate 		 * level with the same name.  If so, return a cpg.
65057c478bd9Sstevel@tonic-gate 		 */
65067c478bd9Sstevel@tonic-gate 		if (iter->rni_clevel >= 0 &&
65077c478bd9Sstevel@tonic-gate 		    type == REP_PROTOCOL_ENTITY_PROPERTYGRP &&
65087c478bd9Sstevel@tonic-gate 		    iter->rni_clevel < COMPOSITION_DEPTH - 1) {
65097c478bd9Sstevel@tonic-gate #if COMPOSITION_DEPTH == 2
65107c478bd9Sstevel@tonic-gate 			rc_node_t *pg;
65117c478bd9Sstevel@tonic-gate 			rc_node_t *ent = iter->rni_parent->rn_cchain[1];
65127c478bd9Sstevel@tonic-gate 
65137c478bd9Sstevel@tonic-gate 			rc_node_hold(res);	/* While we drop np->rn_lock */
65147c478bd9Sstevel@tonic-gate 
65157c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
65167c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&ent->rn_lock);
65177c478bd9Sstevel@tonic-gate 			rc = rc_node_find_named_child(ent, res->rn_name, type,
65187c478bd9Sstevel@tonic-gate 			    &pg);
65197c478bd9Sstevel@tonic-gate 			/* holds pg if not NULL */
65207c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&ent->rn_lock);
65217c478bd9Sstevel@tonic-gate 			if (rc != REP_PROTOCOL_SUCCESS) {
65227c478bd9Sstevel@tonic-gate 				rc_node_rele(res);
65237c478bd9Sstevel@tonic-gate 				rc_node_clear(out, 0);
65247c478bd9Sstevel@tonic-gate 				return (rc);
65257c478bd9Sstevel@tonic-gate 			}
65267c478bd9Sstevel@tonic-gate 
65277c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
65287c478bd9Sstevel@tonic-gate 			if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
65297c478bd9Sstevel@tonic-gate 				(void) pthread_mutex_unlock(&np->rn_lock);
65307c478bd9Sstevel@tonic-gate 				rc_node_rele(res);
65317c478bd9Sstevel@tonic-gate 				if (pg != NULL)
65327c478bd9Sstevel@tonic-gate 					rc_node_rele(pg);
65337c478bd9Sstevel@tonic-gate 				rc_node_clear(out, 1);
65347c478bd9Sstevel@tonic-gate 				return (REP_PROTOCOL_FAIL_DELETED);
65357c478bd9Sstevel@tonic-gate 			}
65367c478bd9Sstevel@tonic-gate 
65377c478bd9Sstevel@tonic-gate 			if (pg == NULL) {
6538*a29160b0SRobert Mustacchi 				(void) pthread_mutex_unlock(&np->rn_lock);
65397c478bd9Sstevel@tonic-gate 				rc_node_rele(res);
6540*a29160b0SRobert Mustacchi 				(void) pthread_mutex_lock(&np->rn_lock);
6541*a29160b0SRobert Mustacchi 				if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
6542*a29160b0SRobert Mustacchi 					(void) pthread_mutex_unlock(&np->
6543*a29160b0SRobert Mustacchi 					    rn_lock);
6544*a29160b0SRobert Mustacchi 					rc_node_clear(out, 1);
6545*a29160b0SRobert Mustacchi 					return (REP_PROTOCOL_FAIL_DELETED);
6546*a29160b0SRobert Mustacchi 				}
65477c478bd9Sstevel@tonic-gate 			} else {
65487c478bd9Sstevel@tonic-gate 				rc_node_t *cpg;
65497c478bd9Sstevel@tonic-gate 
65507c478bd9Sstevel@tonic-gate 				/* Keep res held for rc_node_setup_cpg(). */
65517c478bd9Sstevel@tonic-gate 
65527c478bd9Sstevel@tonic-gate 				cpg = rc_node_alloc();
65537c478bd9Sstevel@tonic-gate 				if (cpg == NULL) {
65547c478bd9Sstevel@tonic-gate 					(void) pthread_mutex_unlock(
65557c478bd9Sstevel@tonic-gate 					    &np->rn_lock);
65567c478bd9Sstevel@tonic-gate 					rc_node_rele(res);
65577c478bd9Sstevel@tonic-gate 					rc_node_rele(pg);
65587c478bd9Sstevel@tonic-gate 					rc_node_clear(out, 0);
65597c478bd9Sstevel@tonic-gate 					return (REP_PROTOCOL_FAIL_NO_RESOURCES);
65607c478bd9Sstevel@tonic-gate 				}
65617c478bd9Sstevel@tonic-gate 
65627c478bd9Sstevel@tonic-gate 				switch (rc_node_setup_cpg(cpg, res, pg)) {
65637c478bd9Sstevel@tonic-gate 				case REP_PROTOCOL_SUCCESS:
65647c478bd9Sstevel@tonic-gate 					res = cpg;
65657c478bd9Sstevel@tonic-gate 					break;
65667c478bd9Sstevel@tonic-gate 
65677c478bd9Sstevel@tonic-gate 				case REP_PROTOCOL_FAIL_TYPE_MISMATCH:
65687c478bd9Sstevel@tonic-gate 					/* Nevermind. */
6569*a29160b0SRobert Mustacchi 					(void) pthread_mutex_unlock(&np->
6570*a29160b0SRobert Mustacchi 					    rn_lock);
65717c478bd9Sstevel@tonic-gate 					rc_node_destroy(cpg);
65727c478bd9Sstevel@tonic-gate 					rc_node_rele(pg);
65737c478bd9Sstevel@tonic-gate 					rc_node_rele(res);
6574*a29160b0SRobert Mustacchi 					(void) pthread_mutex_lock(&np->
6575*a29160b0SRobert Mustacchi 					    rn_lock);
6576*a29160b0SRobert Mustacchi 					if (!rc_node_wait_flag(np,
6577*a29160b0SRobert Mustacchi 					    RC_NODE_DYING)) {
6578*a29160b0SRobert Mustacchi 						(void) pthread_mutex_unlock(&
6579*a29160b0SRobert Mustacchi 						    np->rn_lock);
6580*a29160b0SRobert Mustacchi 						rc_node_clear(out, 1);
6581*a29160b0SRobert Mustacchi 						return
6582*a29160b0SRobert Mustacchi 						    (REP_PROTOCOL_FAIL_DELETED);
6583*a29160b0SRobert Mustacchi 					}
65847c478bd9Sstevel@tonic-gate 					break;
65857c478bd9Sstevel@tonic-gate 
65867c478bd9Sstevel@tonic-gate 				case REP_PROTOCOL_FAIL_NO_RESOURCES:
65877c478bd9Sstevel@tonic-gate 					rc_node_destroy(cpg);
65887c478bd9Sstevel@tonic-gate 					(void) pthread_mutex_unlock(
65897c478bd9Sstevel@tonic-gate 					    &np->rn_lock);
65907c478bd9Sstevel@tonic-gate 					rc_node_rele(res);
65917c478bd9Sstevel@tonic-gate 					rc_node_rele(pg);
65927c478bd9Sstevel@tonic-gate 					rc_node_clear(out, 0);
65937c478bd9Sstevel@tonic-gate 					return (REP_PROTOCOL_FAIL_NO_RESOURCES);
65947c478bd9Sstevel@tonic-gate 
65957c478bd9Sstevel@tonic-gate 				default:
65967c478bd9Sstevel@tonic-gate 					assert(0);
65977c478bd9Sstevel@tonic-gate 					abort();
65987c478bd9Sstevel@tonic-gate 				}
65997c478bd9Sstevel@tonic-gate 			}
66007c478bd9Sstevel@tonic-gate #else
66017c478bd9Sstevel@tonic-gate #error This code must be updated.
66027c478bd9Sstevel@tonic-gate #endif
66037c478bd9Sstevel@tonic-gate 		}
66047c478bd9Sstevel@tonic-gate 
66057c478bd9Sstevel@tonic-gate 		rc_node_hold(res);
66067c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
66077c478bd9Sstevel@tonic-gate 		break;
66087c478bd9Sstevel@tonic-gate 	}
66097c478bd9Sstevel@tonic-gate 	rc_node_assign(out, res);
66107c478bd9Sstevel@tonic-gate 
66117c478bd9Sstevel@tonic-gate 	if (res == NULL)
66127c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_DONE);
66137c478bd9Sstevel@tonic-gate 	rc_node_rele(res);
66147c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
66157c478bd9Sstevel@tonic-gate }
66167c478bd9Sstevel@tonic-gate 
66177c478bd9Sstevel@tonic-gate void
66187c478bd9Sstevel@tonic-gate rc_iter_destroy(rc_node_iter_t **nipp)
66197c478bd9Sstevel@tonic-gate {
66207c478bd9Sstevel@tonic-gate 	rc_node_iter_t *nip = *nipp;
66217c478bd9Sstevel@tonic-gate 	rc_node_t *np;
66227c478bd9Sstevel@tonic-gate 
66237c478bd9Sstevel@tonic-gate 	if (nip == NULL)
66247c478bd9Sstevel@tonic-gate 		return;				/* already freed */
66257c478bd9Sstevel@tonic-gate 
66267c478bd9Sstevel@tonic-gate 	np = nip->rni_parent;
66277c478bd9Sstevel@tonic-gate 
66287c478bd9Sstevel@tonic-gate 	if (nip->rni_filter_arg != NULL)
66297c478bd9Sstevel@tonic-gate 		free(nip->rni_filter_arg);
66307c478bd9Sstevel@tonic-gate 	nip->rni_filter_arg = NULL;
66317c478bd9Sstevel@tonic-gate 
66327c478bd9Sstevel@tonic-gate 	if (nip->rni_type == REP_PROTOCOL_ENTITY_VALUE ||
66337c478bd9Sstevel@tonic-gate 	    nip->rni_iter != NULL) {
66347c478bd9Sstevel@tonic-gate 		if (nip->rni_clevel < 0)
66357c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
66367c478bd9Sstevel@tonic-gate 		else
66377c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(
66387c478bd9Sstevel@tonic-gate 			    &np->rn_cchain[nip->rni_clevel]->rn_lock);
66397c478bd9Sstevel@tonic-gate 		rc_iter_end(nip);		/* release walker and lock */
66407c478bd9Sstevel@tonic-gate 	}
66417c478bd9Sstevel@tonic-gate 	nip->rni_parent = NULL;
66427c478bd9Sstevel@tonic-gate 
66437c478bd9Sstevel@tonic-gate 	uu_free(nip);
66447c478bd9Sstevel@tonic-gate 	*nipp = NULL;
66457c478bd9Sstevel@tonic-gate }
66467c478bd9Sstevel@tonic-gate 
66477c478bd9Sstevel@tonic-gate int
66487c478bd9Sstevel@tonic-gate rc_node_setup_tx(rc_node_ptr_t *npp, rc_node_ptr_t *txp)
66497c478bd9Sstevel@tonic-gate {
66507c478bd9Sstevel@tonic-gate 	rc_node_t *np;
66517c478bd9Sstevel@tonic-gate 	permcheck_t *pcp;
66527c478bd9Sstevel@tonic-gate 	int ret;
6653a4dc1477STom Whitten 	perm_status_t granted;
66545b7f77adStw21770 	rc_auth_state_t authorized = RC_AUTH_UNKNOWN;
66555b7f77adStw21770 	char *auth_string = NULL;
66567c478bd9Sstevel@tonic-gate 
66577c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
66587c478bd9Sstevel@tonic-gate 
66597c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
66607c478bd9Sstevel@tonic-gate 		rc_node_rele(np);
66617c478bd9Sstevel@tonic-gate 		np = np->rn_cchain[0];
66627c478bd9Sstevel@tonic-gate 		RC_NODE_CHECK_AND_HOLD(np);
66637c478bd9Sstevel@tonic-gate 	}
66647c478bd9Sstevel@tonic-gate 
66657c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
66667c478bd9Sstevel@tonic-gate 		rc_node_rele(np);
66677c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
66687c478bd9Sstevel@tonic-gate 	}
66697c478bd9Sstevel@tonic-gate 
66707c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_ids[ID_SNAPSHOT] != 0) {
66717c478bd9Sstevel@tonic-gate 		rc_node_rele(np);
66727c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
66737c478bd9Sstevel@tonic-gate 	}
66747c478bd9Sstevel@tonic-gate 
66755b7f77adStw21770 #ifdef NATIVE_BUILD
66767c478bd9Sstevel@tonic-gate 	if (client_is_privileged())
66777c478bd9Sstevel@tonic-gate 		goto skip_checks;
66787c478bd9Sstevel@tonic-gate 	rc_node_rele(np);
66797c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
66807c478bd9Sstevel@tonic-gate #else
66815b7f77adStw21770 	if (is_main_repository == 0)
66825b7f77adStw21770 		goto skip_checks;
66835b7f77adStw21770 
66847c478bd9Sstevel@tonic-gate 	/* permission check */
66857c478bd9Sstevel@tonic-gate 	pcp = pc_create();
66867c478bd9Sstevel@tonic-gate 	if (pcp == NULL) {
66877c478bd9Sstevel@tonic-gate 		rc_node_rele(np);
66887c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
66897c478bd9Sstevel@tonic-gate 	}
66907c478bd9Sstevel@tonic-gate 
66917c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_ids[ID_INSTANCE] != 0 &&	/* instance pg */
66927c478bd9Sstevel@tonic-gate 	    ((strcmp(np->rn_name, AUTH_PG_ACTIONS) == 0 &&
66937c478bd9Sstevel@tonic-gate 	    strcmp(np->rn_type, AUTH_PG_ACTIONS_TYPE) == 0) ||
66947c478bd9Sstevel@tonic-gate 	    (strcmp(np->rn_name, AUTH_PG_GENERAL_OVR) == 0 &&
66957c478bd9Sstevel@tonic-gate 	    strcmp(np->rn_type, AUTH_PG_GENERAL_OVR_TYPE) == 0))) {
66967c478bd9Sstevel@tonic-gate 		rc_node_t *instn;
66977c478bd9Sstevel@tonic-gate 
6698e8f5b3f5STruong Nguyen 		/* solaris.smf.modify can be used */
6699e8f5b3f5STruong Nguyen 		ret = perm_add_enabling(pcp, AUTH_MODIFY);
6700e8f5b3f5STruong Nguyen 		if (ret != REP_PROTOCOL_SUCCESS) {
6701e8f5b3f5STruong Nguyen 			pc_free(pcp);
6702e8f5b3f5STruong Nguyen 			rc_node_rele(np);
6703e8f5b3f5STruong Nguyen 			return (ret);
6704e8f5b3f5STruong Nguyen 		}
6705e8f5b3f5STruong Nguyen 
67067c478bd9Sstevel@tonic-gate 		/* solaris.smf.manage can be used. */
67077c478bd9Sstevel@tonic-gate 		ret = perm_add_enabling(pcp, AUTH_MANAGE);
67087c478bd9Sstevel@tonic-gate 
67097c478bd9Sstevel@tonic-gate 		if (ret != REP_PROTOCOL_SUCCESS) {
67107c478bd9Sstevel@tonic-gate 			pc_free(pcp);
67117c478bd9Sstevel@tonic-gate 			rc_node_rele(np);
67127c478bd9Sstevel@tonic-gate 			return (ret);
67137c478bd9Sstevel@tonic-gate 		}
67147c478bd9Sstevel@tonic-gate 
67157c478bd9Sstevel@tonic-gate 		/* general/action_authorization values can be used. */
67167c478bd9Sstevel@tonic-gate 		ret = rc_node_parent(np, &instn);
67177c478bd9Sstevel@tonic-gate 		if (ret != REP_PROTOCOL_SUCCESS) {
67187c478bd9Sstevel@tonic-gate 			assert(ret == REP_PROTOCOL_FAIL_DELETED);
67197c478bd9Sstevel@tonic-gate 			rc_node_rele(np);
67207c478bd9Sstevel@tonic-gate 			pc_free(pcp);
67217c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_DELETED);
67227c478bd9Sstevel@tonic-gate 		}
67237c478bd9Sstevel@tonic-gate 
67247c478bd9Sstevel@tonic-gate 		assert(instn->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE);
67257c478bd9Sstevel@tonic-gate 
67267c478bd9Sstevel@tonic-gate 		ret = perm_add_inst_action_auth(pcp, instn);
67277c478bd9Sstevel@tonic-gate 		rc_node_rele(instn);
67287c478bd9Sstevel@tonic-gate 		switch (ret) {
67297c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_SUCCESS:
67307c478bd9Sstevel@tonic-gate 			break;
67317c478bd9Sstevel@tonic-gate 
67327c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_DELETED:
67337c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
67347c478bd9Sstevel@tonic-gate 			rc_node_rele(np);
67357c478bd9Sstevel@tonic-gate 			pc_free(pcp);
67367c478bd9Sstevel@tonic-gate 			return (ret);
67377c478bd9Sstevel@tonic-gate 
67387c478bd9Sstevel@tonic-gate 		default:
67397c478bd9Sstevel@tonic-gate 			bad_error("perm_add_inst_action_auth", ret);
67407c478bd9Sstevel@tonic-gate 		}
67417c478bd9Sstevel@tonic-gate 
67427c478bd9Sstevel@tonic-gate 		if (strcmp(np->rn_name, AUTH_PG_ACTIONS) == 0)
67435b7f77adStw21770 			authorized = RC_AUTH_PASSED; /* No check on commit. */
67447c478bd9Sstevel@tonic-gate 	} else {
67457c478bd9Sstevel@tonic-gate 		ret = perm_add_enabling(pcp, AUTH_MODIFY);
67467c478bd9Sstevel@tonic-gate 
67477c478bd9Sstevel@tonic-gate 		if (ret == REP_PROTOCOL_SUCCESS) {
67487c478bd9Sstevel@tonic-gate 			/* propertygroup-type-specific authorization */
67497c478bd9Sstevel@tonic-gate 			/* no locking because rn_type won't change anyway */
67507c478bd9Sstevel@tonic-gate 			const char * const auth =
67517c478bd9Sstevel@tonic-gate 			    perm_auth_for_pgtype(np->rn_type);
67527c478bd9Sstevel@tonic-gate 
67537c478bd9Sstevel@tonic-gate 			if (auth != NULL)
67547c478bd9Sstevel@tonic-gate 				ret = perm_add_enabling(pcp, auth);
67557c478bd9Sstevel@tonic-gate 		}
67567c478bd9Sstevel@tonic-gate 
67577c478bd9Sstevel@tonic-gate 		if (ret == REP_PROTOCOL_SUCCESS)
67587c478bd9Sstevel@tonic-gate 			/* propertygroup/transaction-type-specific auths */
67597c478bd9Sstevel@tonic-gate 			ret =
67607c478bd9Sstevel@tonic-gate 			    perm_add_enabling_values(pcp, np, AUTH_PROP_VALUE);
67617c478bd9Sstevel@tonic-gate 
67627c478bd9Sstevel@tonic-gate 		if (ret == REP_PROTOCOL_SUCCESS)
67637c478bd9Sstevel@tonic-gate 			ret =
67647c478bd9Sstevel@tonic-gate 			    perm_add_enabling_values(pcp, np, AUTH_PROP_MODIFY);
67657c478bd9Sstevel@tonic-gate 
67667c478bd9Sstevel@tonic-gate 		/* AUTH_MANAGE can manipulate general/AUTH_PROP_ACTION */
67677c478bd9Sstevel@tonic-gate 		if (ret == REP_PROTOCOL_SUCCESS &&
67687c478bd9Sstevel@tonic-gate 		    strcmp(np->rn_name, AUTH_PG_GENERAL) == 0 &&
67697c478bd9Sstevel@tonic-gate 		    strcmp(np->rn_type, AUTH_PG_GENERAL_TYPE) == 0)
67707c478bd9Sstevel@tonic-gate 			ret = perm_add_enabling(pcp, AUTH_MANAGE);
67717c478bd9Sstevel@tonic-gate 
67727c478bd9Sstevel@tonic-gate 		if (ret != REP_PROTOCOL_SUCCESS) {
67737c478bd9Sstevel@tonic-gate 			pc_free(pcp);
67747c478bd9Sstevel@tonic-gate 			rc_node_rele(np);
67757c478bd9Sstevel@tonic-gate 			return (ret);
67767c478bd9Sstevel@tonic-gate 		}
67777c478bd9Sstevel@tonic-gate 	}
67787c478bd9Sstevel@tonic-gate 
6779a4dc1477STom Whitten 	granted = perm_granted(pcp);
6780a4dc1477STom Whitten 	ret = map_granted_status(granted, pcp, &auth_string);
67817c478bd9Sstevel@tonic-gate 	pc_free(pcp);
6782a4dc1477STom Whitten 
6783a4dc1477STom Whitten 	if ((granted == PERM_GONE) || (granted == PERM_FAIL) ||
6784a4dc1477STom Whitten 	    (ret == REP_PROTOCOL_FAIL_NO_RESOURCES)) {
6785a4dc1477STom Whitten 		free(auth_string);
67867c478bd9Sstevel@tonic-gate 		rc_node_rele(np);
6787a4dc1477STom Whitten 		return (ret);
67887c478bd9Sstevel@tonic-gate 	}
67897c478bd9Sstevel@tonic-gate 
6790a4dc1477STom Whitten 	if (granted == PERM_DENIED) {
67915b7f77adStw21770 		/*
67925b7f77adStw21770 		 * If we get here, the authorization failed.
67935b7f77adStw21770 		 * Unfortunately, we don't have enough information at this
67945b7f77adStw21770 		 * point to generate the security audit events.  We'll only
67955b7f77adStw21770 		 * get that information when the client tries to commit the
67965b7f77adStw21770 		 * event.  Thus, we'll remember the failed authorization,
67975b7f77adStw21770 		 * so that we can generate the audit events later.
67985b7f77adStw21770 		 */
67995b7f77adStw21770 		authorized = RC_AUTH_FAILED;
68005b7f77adStw21770 	}
68017c478bd9Sstevel@tonic-gate #endif /* NATIVE_BUILD */
68027c478bd9Sstevel@tonic-gate 
68037c478bd9Sstevel@tonic-gate skip_checks:
68047c478bd9Sstevel@tonic-gate 	rc_node_assign(txp, np);
68057c478bd9Sstevel@tonic-gate 	txp->rnp_authorized = authorized;
68065b7f77adStw21770 	if (authorized != RC_AUTH_UNKNOWN) {
68075b7f77adStw21770 		/* Save the authorization string. */
68085b7f77adStw21770 		if (txp->rnp_auth_string != NULL)
68095b7f77adStw21770 			free((void *)txp->rnp_auth_string);
68105b7f77adStw21770 		txp->rnp_auth_string = auth_string;
68115b7f77adStw21770 		auth_string = NULL;	/* Don't free until done with txp. */
68125b7f77adStw21770 	}
68137c478bd9Sstevel@tonic-gate 
68147c478bd9Sstevel@tonic-gate 	rc_node_rele(np);
68155b7f77adStw21770 	if (auth_string != NULL)
68165b7f77adStw21770 		free(auth_string);
68177c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
68187c478bd9Sstevel@tonic-gate }
68197c478bd9Sstevel@tonic-gate 
68207c478bd9Sstevel@tonic-gate /*
68217c478bd9Sstevel@tonic-gate  * Return 1 if the given transaction commands only modify the values of
68227c478bd9Sstevel@tonic-gate  * properties other than "modify_authorization".  Return -1 if any of the
68237c478bd9Sstevel@tonic-gate  * commands are invalid, and 0 otherwise.
68247c478bd9Sstevel@tonic-gate  */
68257c478bd9Sstevel@tonic-gate static int
68267c478bd9Sstevel@tonic-gate tx_allow_value(const void *cmds_arg, size_t cmds_sz, rc_node_t *pg)
68277c478bd9Sstevel@tonic-gate {
68287c478bd9Sstevel@tonic-gate 	const struct rep_protocol_transaction_cmd *cmds;
68297c478bd9Sstevel@tonic-gate 	uintptr_t loc;
68307c478bd9Sstevel@tonic-gate 	uint32_t sz;
68317c478bd9Sstevel@tonic-gate 	rc_node_t *prop;
68327c478bd9Sstevel@tonic-gate 	boolean_t ok;
68337c478bd9Sstevel@tonic-gate 
68347c478bd9Sstevel@tonic-gate 	assert(!MUTEX_HELD(&pg->rn_lock));
68357c478bd9Sstevel@tonic-gate 
68367c478bd9Sstevel@tonic-gate 	loc = (uintptr_t)cmds_arg;
68377c478bd9Sstevel@tonic-gate 
68387c478bd9Sstevel@tonic-gate 	while (cmds_sz > 0) {
68397c478bd9Sstevel@tonic-gate 		cmds = (struct rep_protocol_transaction_cmd *)loc;
68407c478bd9Sstevel@tonic-gate 
68417c478bd9Sstevel@tonic-gate 		if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
68427c478bd9Sstevel@tonic-gate 			return (-1);
68437c478bd9Sstevel@tonic-gate 
68447c478bd9Sstevel@tonic-gate 		sz = cmds->rptc_size;
68457c478bd9Sstevel@tonic-gate 		if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
68467c478bd9Sstevel@tonic-gate 			return (-1);
68477c478bd9Sstevel@tonic-gate 
68487c478bd9Sstevel@tonic-gate 		sz = TX_SIZE(sz);
68497c478bd9Sstevel@tonic-gate 		if (sz > cmds_sz)
68507c478bd9Sstevel@tonic-gate 			return (-1);
68517c478bd9Sstevel@tonic-gate 
68527c478bd9Sstevel@tonic-gate 		switch (cmds[0].rptc_action) {
68537c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_TX_ENTRY_CLEAR:
68547c478bd9Sstevel@tonic-gate 			break;
68557c478bd9Sstevel@tonic-gate 
68567c478bd9Sstevel@tonic-gate 		case REP_PROTOCOL_TX_ENTRY_REPLACE:
68577c478bd9Sstevel@tonic-gate 			/* Check type */
68587c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_lock(&pg->rn_lock);
68591ad2c453STom Whitten 			ok = B_FALSE;
68607c478bd9Sstevel@tonic-gate 			if (rc_node_find_named_child(pg,
68617c478bd9Sstevel@tonic-gate 			    (const char *)cmds[0].rptc_data,
68627c478bd9Sstevel@tonic-gate 			    REP_PROTOCOL_ENTITY_PROPERTY, &prop) ==
68637c478bd9Sstevel@tonic-gate 			    REP_PROTOCOL_SUCCESS) {
68641ad2c453STom Whitten 				if (prop != NULL) {
68651ad2c453STom Whitten 					ok = prop->rn_valtype ==
68661ad2c453STom Whitten 					    cmds[0].rptc_type;
68671ad2c453STom Whitten 					/*
68681ad2c453STom Whitten 					 * rc_node_find_named_child()
68691ad2c453STom Whitten 					 * places a hold on prop which we
68701ad2c453STom Whitten 					 * do not need to hang on to.
68711ad2c453STom Whitten 					 */
68721ad2c453STom Whitten 					rc_node_rele(prop);
68731ad2c453STom Whitten 				}
68747c478bd9Sstevel@tonic-gate 			}
68757c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&pg->rn_lock);
68767c478bd9Sstevel@tonic-gate 			if (ok)
68777c478bd9Sstevel@tonic-gate 				break;
68787c478bd9Sstevel@tonic-gate 			return (0);
68797c478bd9Sstevel@tonic-gate 
68807c478bd9Sstevel@tonic-gate 		default:
68817c478bd9Sstevel@tonic-gate 			return (0);
68827c478bd9Sstevel@tonic-gate 		}
68837c478bd9Sstevel@tonic-gate 
68847c478bd9Sstevel@tonic-gate 		if (strcmp((const char *)cmds[0].rptc_data, AUTH_PROP_MODIFY)
68857c478bd9Sstevel@tonic-gate 		    == 0)
68867c478bd9Sstevel@tonic-gate 			return (0);
68877c478bd9Sstevel@tonic-gate 
68887c478bd9Sstevel@tonic-gate 		loc += sz;
68897c478bd9Sstevel@tonic-gate 		cmds_sz -= sz;
68907c478bd9Sstevel@tonic-gate 	}
68917c478bd9Sstevel@tonic-gate 
68927c478bd9Sstevel@tonic-gate 	return (1);
68937c478bd9Sstevel@tonic-gate }
68947c478bd9Sstevel@tonic-gate 
68957c478bd9Sstevel@tonic-gate /*
68967c478bd9Sstevel@tonic-gate  * Return 1 if any of the given transaction commands affect
68977c478bd9Sstevel@tonic-gate  * "action_authorization".  Return -1 if any of the commands are invalid and
68987c478bd9Sstevel@tonic-gate  * 0 in all other cases.
68997c478bd9Sstevel@tonic-gate  */
69007c478bd9Sstevel@tonic-gate static int
69017c478bd9Sstevel@tonic-gate tx_modifies_action(const void *cmds_arg, size_t cmds_sz)
69027c478bd9Sstevel@tonic-gate {
69037c478bd9Sstevel@tonic-gate 	const struct rep_protocol_transaction_cmd *cmds;
69047c478bd9Sstevel@tonic-gate 	uintptr_t loc;
69057c478bd9Sstevel@tonic-gate 	uint32_t sz;
69067c478bd9Sstevel@tonic-gate 
69077c478bd9Sstevel@tonic-gate 	loc = (uintptr_t)cmds_arg;
69087c478bd9Sstevel@tonic-gate 
69097c478bd9Sstevel@tonic-gate 	while (cmds_sz > 0) {
69107c478bd9Sstevel@tonic-gate 		cmds = (struct rep_protocol_transaction_cmd *)loc;
69117c478bd9Sstevel@tonic-gate 
69127c478bd9Sstevel@tonic-gate 		if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
69137c478bd9Sstevel@tonic-gate 			return (-1);
69147c478bd9Sstevel@tonic-gate 
69157c478bd9Sstevel@tonic-gate 		sz = cmds->rptc_size;
69167c478bd9Sstevel@tonic-gate 		if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
69177c478bd9Sstevel@tonic-gate 			return (-1);
69187c478bd9Sstevel@tonic-gate 
69197c478bd9Sstevel@tonic-gate 		sz = TX_SIZE(sz);
69207c478bd9Sstevel@tonic-gate 		if (sz > cmds_sz)
69217c478bd9Sstevel@tonic-gate 			return (-1);
69227c478bd9Sstevel@tonic-gate 
69237c478bd9Sstevel@tonic-gate 		if (strcmp((const char *)cmds[0].rptc_data, AUTH_PROP_ACTION)
69247c478bd9Sstevel@tonic-gate 		    == 0)
69257c478bd9Sstevel@tonic-gate 			return (1);
69267c478bd9Sstevel@tonic-gate 
69277c478bd9Sstevel@tonic-gate 		loc += sz;
69287c478bd9Sstevel@tonic-gate 		cmds_sz -= sz;
69297c478bd9Sstevel@tonic-gate 	}
69307c478bd9Sstevel@tonic-gate 
69317c478bd9Sstevel@tonic-gate 	return (0);
69327c478bd9Sstevel@tonic-gate }
69337c478bd9Sstevel@tonic-gate 
69347c478bd9Sstevel@tonic-gate /*
69357c478bd9Sstevel@tonic-gate  * Returns 1 if the transaction commands only modify properties named
69367c478bd9Sstevel@tonic-gate  * 'enabled'.
69377c478bd9Sstevel@tonic-gate  */
69387c478bd9Sstevel@tonic-gate static int
69397c478bd9Sstevel@tonic-gate tx_only_enabled(const void *cmds_arg, size_t cmds_sz)
69407c478bd9Sstevel@tonic-gate {
69417c478bd9Sstevel@tonic-gate 	const struct rep_protocol_transaction_cmd *cmd;
69427c478bd9Sstevel@tonic-gate 	uintptr_t loc;
69437c478bd9Sstevel@tonic-gate 	uint32_t sz;
69447c478bd9Sstevel@tonic-gate 
69457c478bd9Sstevel@tonic-gate 	loc = (uintptr_t)cmds_arg;
69467c478bd9Sstevel@tonic-gate 
69477c478bd9Sstevel@tonic-gate 	while (cmds_sz > 0) {
69487c478bd9Sstevel@tonic-gate 		cmd = (struct rep_protocol_transaction_cmd *)loc;
69497c478bd9Sstevel@tonic-gate 
69507c478bd9Sstevel@tonic-gate 		if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
69517c478bd9Sstevel@tonic-gate 			return (-1);
69527c478bd9Sstevel@tonic-gate 
69537c478bd9Sstevel@tonic-gate 		sz = cmd->rptc_size;
69547c478bd9Sstevel@tonic-gate 		if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
69557c478bd9Sstevel@tonic-gate 			return (-1);
69567c478bd9Sstevel@tonic-gate 
69577c478bd9Sstevel@tonic-gate 		sz = TX_SIZE(sz);
69587c478bd9Sstevel@tonic-gate 		if (sz > cmds_sz)
69597c478bd9Sstevel@tonic-gate 			return (-1);
69607c478bd9Sstevel@tonic-gate 
69617c478bd9Sstevel@tonic-gate 		if (strcmp((const char *)cmd->rptc_data, AUTH_PROP_ENABLED)
69627c478bd9Sstevel@tonic-gate 		    != 0)
69637c478bd9Sstevel@tonic-gate 			return (0);
69647c478bd9Sstevel@tonic-gate 
69657c478bd9Sstevel@tonic-gate 		loc += sz;
69667c478bd9Sstevel@tonic-gate 		cmds_sz -= sz;
69677c478bd9Sstevel@tonic-gate 	}
69687c478bd9Sstevel@tonic-gate 
69697c478bd9Sstevel@tonic-gate 	return (1);
69707c478bd9Sstevel@tonic-gate }
69717c478bd9Sstevel@tonic-gate 
69727c478bd9Sstevel@tonic-gate int
69737c478bd9Sstevel@tonic-gate rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz)
69747c478bd9Sstevel@tonic-gate {
69757c478bd9Sstevel@tonic-gate 	rc_node_t *np = txp->rnp_node;
69767c478bd9Sstevel@tonic-gate 	rc_node_t *pp;
69777c478bd9Sstevel@tonic-gate 	rc_node_t *nnp;
69787c478bd9Sstevel@tonic-gate 	rc_node_pg_notify_t *pnp;
69797c478bd9Sstevel@tonic-gate 	int rc;
69807c478bd9Sstevel@tonic-gate 	permcheck_t *pcp;
6981a4dc1477STom Whitten 	perm_status_t granted;
6982a4dc1477STom Whitten 	int normal;
69835b7f77adStw21770 	char *pg_fmri = NULL;
69845b7f77adStw21770 	char *auth_string = NULL;
69855b7f77adStw21770 	int auth_status = ADT_SUCCESS;
69865b7f77adStw21770 	int auth_ret_value = ADT_SUCCESS;
69875b7f77adStw21770 	size_t sz_out;
69885b7f77adStw21770 	int tx_flag = 1;
69895b7f77adStw21770 	tx_commit_data_t *tx_data = NULL;
69907c478bd9Sstevel@tonic-gate 
69917c478bd9Sstevel@tonic-gate 	RC_NODE_CHECK(np);
69927c478bd9Sstevel@tonic-gate 
69935b7f77adStw21770 	if ((txp->rnp_authorized != RC_AUTH_UNKNOWN) &&
69945b7f77adStw21770 	    (txp->rnp_auth_string != NULL)) {
69955b7f77adStw21770 		auth_string = strdup(txp->rnp_auth_string);
69965b7f77adStw21770 		if (auth_string == NULL)
69975b7f77adStw21770 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
69985b7f77adStw21770 	}
69995b7f77adStw21770 
70005b7f77adStw21770 	if ((txp->rnp_authorized == RC_AUTH_UNKNOWN) &&
70015b7f77adStw21770 	    is_main_repository) {
70027c478bd9Sstevel@tonic-gate #ifdef NATIVE_BUILD
70035b7f77adStw21770 		if (!client_is_privileged()) {
70047c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
70055b7f77adStw21770 		}
70067c478bd9Sstevel@tonic-gate #else
70077c478bd9Sstevel@tonic-gate 		/* permission check: depends on contents of transaction */
70087c478bd9Sstevel@tonic-gate 		pcp = pc_create();
70097c478bd9Sstevel@tonic-gate 		if (pcp == NULL)
70107c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
70117c478bd9Sstevel@tonic-gate 
70127c478bd9Sstevel@tonic-gate 		/* If normal is cleared, we won't do the normal checks. */
70137c478bd9Sstevel@tonic-gate 		normal = 1;
70147c478bd9Sstevel@tonic-gate 		rc = REP_PROTOCOL_SUCCESS;
70157c478bd9Sstevel@tonic-gate 
70167c478bd9Sstevel@tonic-gate 		if (strcmp(np->rn_name, AUTH_PG_GENERAL) == 0 &&
70177c478bd9Sstevel@tonic-gate 		    strcmp(np->rn_type, AUTH_PG_GENERAL_TYPE) == 0) {
70187c478bd9Sstevel@tonic-gate 			/* Touching general[framework]/action_authorization? */
70197c478bd9Sstevel@tonic-gate 			rc = tx_modifies_action(cmds, cmds_sz);
70207c478bd9Sstevel@tonic-gate 			if (rc == -1) {
70217c478bd9Sstevel@tonic-gate 				pc_free(pcp);
70227c478bd9Sstevel@tonic-gate 				return (REP_PROTOCOL_FAIL_BAD_REQUEST);
70237c478bd9Sstevel@tonic-gate 			}
70247c478bd9Sstevel@tonic-gate 
70257c478bd9Sstevel@tonic-gate 			if (rc) {
7026e8f5b3f5STruong Nguyen 				/*
7027e8f5b3f5STruong Nguyen 				 * Yes: only AUTH_MODIFY and AUTH_MANAGE
7028e8f5b3f5STruong Nguyen 				 * can be used.
7029e8f5b3f5STruong Nguyen 				 */
7030e8f5b3f5STruong Nguyen 				rc = perm_add_enabling(pcp, AUTH_MODIFY);
7031e8f5b3f5STruong Nguyen 
7032e8f5b3f5STruong Nguyen 				if (rc == REP_PROTOCOL_SUCCESS)
7033e8f5b3f5STruong Nguyen 					rc = perm_add_enabling(pcp,
7034e8f5b3f5STruong Nguyen 					    AUTH_MANAGE);
7035e8f5b3f5STruong Nguyen 
70367c478bd9Sstevel@tonic-gate 				normal = 0;
70377c478bd9Sstevel@tonic-gate 			} else {
70387c478bd9Sstevel@tonic-gate 				rc = REP_PROTOCOL_SUCCESS;
70397c478bd9Sstevel@tonic-gate 			}
70407c478bd9Sstevel@tonic-gate 		} else if (np->rn_id.rl_ids[ID_INSTANCE] != 0 &&
70417c478bd9Sstevel@tonic-gate 		    strcmp(np->rn_name, AUTH_PG_GENERAL_OVR) == 0 &&
70427c478bd9Sstevel@tonic-gate 		    strcmp(np->rn_type, AUTH_PG_GENERAL_OVR_TYPE) == 0) {
70437c478bd9Sstevel@tonic-gate 			rc_node_t *instn;
70447c478bd9Sstevel@tonic-gate 
70457c478bd9Sstevel@tonic-gate 			rc = tx_only_enabled(cmds, cmds_sz);
70467c478bd9Sstevel@tonic-gate 			if (rc == -1) {
70477c478bd9Sstevel@tonic-gate 				pc_free(pcp);
70487c478bd9Sstevel@tonic-gate 				return (REP_PROTOCOL_FAIL_BAD_REQUEST);
70497c478bd9Sstevel@tonic-gate 			}
70507c478bd9Sstevel@tonic-gate 
70517c478bd9Sstevel@tonic-gate 			if (rc) {
70527c478bd9Sstevel@tonic-gate 				rc = rc_node_parent(np, &instn);
70537c478bd9Sstevel@tonic-gate 				if (rc != REP_PROTOCOL_SUCCESS) {
70547c478bd9Sstevel@tonic-gate 					assert(rc == REP_PROTOCOL_FAIL_DELETED);
70557c478bd9Sstevel@tonic-gate 					pc_free(pcp);
70567c478bd9Sstevel@tonic-gate 					return (rc);
70577c478bd9Sstevel@tonic-gate 				}
70587c478bd9Sstevel@tonic-gate 
70597c478bd9Sstevel@tonic-gate 				assert(instn->rn_id.rl_type ==
70607c478bd9Sstevel@tonic-gate 				    REP_PROTOCOL_ENTITY_INSTANCE);
70617c478bd9Sstevel@tonic-gate 
70627c478bd9Sstevel@tonic-gate 				rc = perm_add_inst_action_auth(pcp, instn);
70637c478bd9Sstevel@tonic-gate 				rc_node_rele(instn);
70647c478bd9Sstevel@tonic-gate 				switch (rc) {
70657c478bd9Sstevel@tonic-gate 				case REP_PROTOCOL_SUCCESS:
70667c478bd9Sstevel@tonic-gate 					break;
70677c478bd9Sstevel@tonic-gate 
70687c478bd9Sstevel@tonic-gate 				case REP_PROTOCOL_FAIL_DELETED:
70697c478bd9Sstevel@tonic-gate 				case REP_PROTOCOL_FAIL_NO_RESOURCES:
70707c478bd9Sstevel@tonic-gate 					pc_free(pcp);
70717c478bd9Sstevel@tonic-gate 					return (rc);
70727c478bd9Sstevel@tonic-gate 
70737c478bd9Sstevel@tonic-gate 				default:
70747c478bd9Sstevel@tonic-gate 					bad_error("perm_add_inst_action_auth",
70757c478bd9Sstevel@tonic-gate 					    rc);
70767c478bd9Sstevel@tonic-gate 				}
70777c478bd9Sstevel@tonic-gate 			} else {
70787c478bd9Sstevel@tonic-gate 				rc = REP_PROTOCOL_SUCCESS;
70797c478bd9Sstevel@tonic-gate 			}
70807c478bd9Sstevel@tonic-gate 		}
70817c478bd9Sstevel@tonic-gate 
70827c478bd9Sstevel@tonic-gate 		if (rc == REP_PROTOCOL_SUCCESS && normal) {
70837c478bd9Sstevel@tonic-gate 			rc = perm_add_enabling(pcp, AUTH_MODIFY);
70847c478bd9Sstevel@tonic-gate 
70857c478bd9Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS) {
70867c478bd9Sstevel@tonic-gate 				/* Add pgtype-specific authorization. */
70877c478bd9Sstevel@tonic-gate 				const char * const auth =
70887c478bd9Sstevel@tonic-gate 				    perm_auth_for_pgtype(np->rn_type);
70897c478bd9Sstevel@tonic-gate 
70907c478bd9Sstevel@tonic-gate 				if (auth != NULL)
70917c478bd9Sstevel@tonic-gate 					rc = perm_add_enabling(pcp, auth);
70927c478bd9Sstevel@tonic-gate 			}
70937c478bd9Sstevel@tonic-gate 
70947c478bd9Sstevel@tonic-gate 			/* Add pg-specific modify_authorization auths. */
70957c478bd9Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS)
70967c478bd9Sstevel@tonic-gate 				rc = perm_add_enabling_values(pcp, np,
70977c478bd9Sstevel@tonic-gate 				    AUTH_PROP_MODIFY);
70987c478bd9Sstevel@tonic-gate 
70997c478bd9Sstevel@tonic-gate 			/* If value_authorization values are ok, add them. */
71007c478bd9Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS) {
71017c478bd9Sstevel@tonic-gate 				rc = tx_allow_value(cmds, cmds_sz, np);
71027c478bd9Sstevel@tonic-gate 				if (rc == -1)
71037c478bd9Sstevel@tonic-gate 					rc = REP_PROTOCOL_FAIL_BAD_REQUEST;
71047c478bd9Sstevel@tonic-gate 				else if (rc)
71057c478bd9Sstevel@tonic-gate 					rc = perm_add_enabling_values(pcp, np,
71067c478bd9Sstevel@tonic-gate 					    AUTH_PROP_VALUE);
71077c478bd9Sstevel@tonic-gate 			}
71087c478bd9Sstevel@tonic-gate 		}
71097c478bd9Sstevel@tonic-gate 
71107c478bd9Sstevel@tonic-gate 		if (rc == REP_PROTOCOL_SUCCESS) {
71117c478bd9Sstevel@tonic-gate 			granted = perm_granted(pcp);
7112a4dc1477STom Whitten 			rc = map_granted_status(granted, pcp, &auth_string);
7113a4dc1477STom Whitten 			if ((granted == PERM_DENIED) && auth_string) {
71145b7f77adStw21770 				/*
7115a4dc1477STom Whitten 				 * _PERMISSION_DENIED should not cause us
7116a4dc1477STom Whitten 				 * to exit at this point, because we still
7117a4dc1477STom Whitten 				 * want to generate an audit event.
71185b7f77adStw21770 				 */
7119a4dc1477STom Whitten 				rc = REP_PROTOCOL_SUCCESS;
71205b7f77adStw21770 			}
71217c478bd9Sstevel@tonic-gate 		}
71227c478bd9Sstevel@tonic-gate 
71237c478bd9Sstevel@tonic-gate 		pc_free(pcp);
71247c478bd9Sstevel@tonic-gate 
71257c478bd9Sstevel@tonic-gate 		if (rc != REP_PROTOCOL_SUCCESS)
71265b7f77adStw21770 			goto cleanout;
71277c478bd9Sstevel@tonic-gate 
7128a4dc1477STom Whitten 		if (granted == PERM_DENIED) {
71295b7f77adStw21770 			auth_status = ADT_FAILURE;
71305b7f77adStw21770 			auth_ret_value = ADT_FAIL_VALUE_AUTH;
71315b7f77adStw21770 			tx_flag = 0;
71325b7f77adStw21770 		}
71337c478bd9Sstevel@tonic-gate #endif /* NATIVE_BUILD */
71345b7f77adStw21770 	} else if (txp->rnp_authorized == RC_AUTH_FAILED) {
71355b7f77adStw21770 		auth_status = ADT_FAILURE;
71365b7f77adStw21770 		auth_ret_value = ADT_FAIL_VALUE_AUTH;
71375b7f77adStw21770 		tx_flag = 0;
71385b7f77adStw21770 	}
71395b7f77adStw21770 
71405b7f77adStw21770 	pg_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
71415b7f77adStw21770 	if (pg_fmri == NULL) {
71425b7f77adStw21770 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
71435b7f77adStw21770 		goto cleanout;
71445b7f77adStw21770 	}
71455b7f77adStw21770 	if ((rc = rc_node_get_fmri_or_fragment(np, pg_fmri,
71465b7f77adStw21770 	    REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
71475b7f77adStw21770 		goto cleanout;
71485b7f77adStw21770 	}
71495b7f77adStw21770 
71505b7f77adStw21770 	/*
71515b7f77adStw21770 	 * Parse the transaction commands into a useful form.
71525b7f77adStw21770 	 */
71535b7f77adStw21770 	if ((rc = tx_commit_data_new(cmds, cmds_sz, &tx_data)) !=
71545b7f77adStw21770 	    REP_PROTOCOL_SUCCESS) {
71555b7f77adStw21770 		goto cleanout;
71565b7f77adStw21770 	}
71575b7f77adStw21770 
71585b7f77adStw21770 	if (tx_flag == 0) {
71595b7f77adStw21770 		/* Authorization failed.  Generate audit events. */
71605b7f77adStw21770 		generate_property_events(tx_data, pg_fmri, auth_string,
71615b7f77adStw21770 		    auth_status, auth_ret_value);
71625b7f77adStw21770 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
71635b7f77adStw21770 		goto cleanout;
71647c478bd9Sstevel@tonic-gate 	}
71657c478bd9Sstevel@tonic-gate 
71667c478bd9Sstevel@tonic-gate 	nnp = rc_node_alloc();
71675b7f77adStw21770 	if (nnp == NULL) {
71685b7f77adStw21770 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
71695b7f77adStw21770 		goto cleanout;
71705b7f77adStw21770 	}
71717c478bd9Sstevel@tonic-gate 
71727c478bd9Sstevel@tonic-gate 	nnp->rn_id = np->rn_id;			/* structure assignment */
71737c478bd9Sstevel@tonic-gate 	nnp->rn_hash = np->rn_hash;
71747c478bd9Sstevel@tonic-gate 	nnp->rn_name = strdup(np->rn_name);
71757c478bd9Sstevel@tonic-gate 	nnp->rn_type = strdup(np->rn_type);
71767c478bd9Sstevel@tonic-gate 	nnp->rn_pgflags = np->rn_pgflags;
71777c478bd9Sstevel@tonic-gate 
71787c478bd9Sstevel@tonic-gate 	nnp->rn_flags = RC_NODE_IN_TX | RC_NODE_USING_PARENT;
71797c478bd9Sstevel@tonic-gate 
71807c478bd9Sstevel@tonic-gate 	if (nnp->rn_name == NULL || nnp->rn_type == NULL) {
71817c478bd9Sstevel@tonic-gate 		rc_node_destroy(nnp);
71825b7f77adStw21770 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
71835b7f77adStw21770 		goto cleanout;
71847c478bd9Sstevel@tonic-gate 	}
71857c478bd9Sstevel@tonic-gate 
71867c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
71875b7f77adStw21770 
71887c478bd9Sstevel@tonic-gate 	/*
71897c478bd9Sstevel@tonic-gate 	 * We must have all of the old properties in the cache, or the
71907c478bd9Sstevel@tonic-gate 	 * database deletions could cause inconsistencies.
71917c478bd9Sstevel@tonic-gate 	 */
71927c478bd9Sstevel@tonic-gate 	if ((rc = rc_node_fill_children(np, REP_PROTOCOL_ENTITY_PROPERTY)) !=
71937c478bd9Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS) {
71947c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
71957c478bd9Sstevel@tonic-gate 		rc_node_destroy(nnp);
71965b7f77adStw21770 		goto cleanout;
71977c478bd9Sstevel@tonic-gate 	}
71987c478bd9Sstevel@tonic-gate 
71997c478bd9Sstevel@tonic-gate 	if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
72007c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
72017c478bd9Sstevel@tonic-gate 		rc_node_destroy(nnp);
72025b7f77adStw21770 		rc = REP_PROTOCOL_FAIL_DELETED;
72035b7f77adStw21770 		goto cleanout;
72047c478bd9Sstevel@tonic-gate 	}
72057c478bd9Sstevel@tonic-gate 
72067c478bd9Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_OLD) {
72077c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_USING_PARENT);
72087c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
72097c478bd9Sstevel@tonic-gate 		rc_node_destroy(nnp);
72105b7f77adStw21770 		rc = REP_PROTOCOL_FAIL_NOT_LATEST;
72115b7f77adStw21770 		goto cleanout;
72127c478bd9Sstevel@tonic-gate 	}
72137c478bd9Sstevel@tonic-gate 
72147c478bd9Sstevel@tonic-gate 	pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
72157c478bd9Sstevel@tonic-gate 	if (pp == NULL) {
72167c478bd9Sstevel@tonic-gate 		/* our parent is gone, we're going next... */
72177c478bd9Sstevel@tonic-gate 		rc_node_destroy(nnp);
72187c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
72197c478bd9Sstevel@tonic-gate 		if (np->rn_flags & RC_NODE_OLD) {
72207c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
72215b7f77adStw21770 			rc = REP_PROTOCOL_FAIL_NOT_LATEST;
72225b7f77adStw21770 			goto cleanout;
72237c478bd9Sstevel@tonic-gate 		}
72247c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
72255b7f77adStw21770 		rc = REP_PROTOCOL_FAIL_DELETED;
72265b7f77adStw21770 		goto cleanout;
72277c478bd9Sstevel@tonic-gate 	}
72287c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&pp->rn_lock);
72297c478bd9Sstevel@tonic-gate 
72307c478bd9Sstevel@tonic-gate 	/*
72317c478bd9Sstevel@tonic-gate 	 * prepare for the transaction
72327c478bd9Sstevel@tonic-gate 	 */
72337c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
72347c478bd9Sstevel@tonic-gate 	if (!rc_node_hold_flag(np, RC_NODE_IN_TX)) {
72357c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
72367c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&pp->rn_lock);
72377c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
72387c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&pp->rn_lock);
72397c478bd9Sstevel@tonic-gate 		rc_node_destroy(nnp);
72405b7f77adStw21770 		rc = REP_PROTOCOL_FAIL_DELETED;
72415b7f77adStw21770 		goto cleanout;
72427c478bd9Sstevel@tonic-gate 	}
72437c478bd9Sstevel@tonic-gate 	nnp->rn_gen_id = np->rn_gen_id;
72447c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
72457c478bd9Sstevel@tonic-gate 
72467c478bd9Sstevel@tonic-gate 	/* Sets nnp->rn_gen_id on success. */
72475b7f77adStw21770 	rc = object_tx_commit(&np->rn_id, tx_data, &nnp->rn_gen_id);
72487c478bd9Sstevel@tonic-gate 
72497c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
72507c478bd9Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS) {
72517c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_IN_TX);
72527c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
72537c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&pp->rn_lock);
72547c478bd9Sstevel@tonic-gate 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
72557c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&pp->rn_lock);
72567c478bd9Sstevel@tonic-gate 		rc_node_destroy(nnp);
72577c478bd9Sstevel@tonic-gate 		rc_node_clear(txp, 0);
72587c478bd9Sstevel@tonic-gate 		if (rc == REP_PROTOCOL_DONE)
72597c478bd9Sstevel@tonic-gate 			rc = REP_PROTOCOL_SUCCESS; /* successful empty tx */
72605b7f77adStw21770 		goto cleanout;
72617c478bd9Sstevel@tonic-gate 	}
72627c478bd9Sstevel@tonic-gate 
72637c478bd9Sstevel@tonic-gate 	/*
72647c478bd9Sstevel@tonic-gate 	 * Notify waiters
72657c478bd9Sstevel@tonic-gate 	 */
72667c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
72677c478bd9Sstevel@tonic-gate 	while ((pnp = uu_list_first(np->rn_pg_notify_list)) != NULL)
72687c478bd9Sstevel@tonic-gate 		rc_pg_notify_fire(pnp);
72697c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
72707c478bd9Sstevel@tonic-gate 
72717c478bd9Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_OLD;
72727c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
72737c478bd9Sstevel@tonic-gate 
72747c478bd9Sstevel@tonic-gate 	rc_notify_remove_node(np);
72757c478bd9Sstevel@tonic-gate 
72767c478bd9Sstevel@tonic-gate 	/*
72777c478bd9Sstevel@tonic-gate 	 * replace np with nnp
72787c478bd9Sstevel@tonic-gate 	 */
72797c478bd9Sstevel@tonic-gate 	rc_node_relink_child(pp, np, nnp);
72807c478bd9Sstevel@tonic-gate 
72817c478bd9Sstevel@tonic-gate 	/*
72827c478bd9Sstevel@tonic-gate 	 * all done -- clear the transaction.
72837c478bd9Sstevel@tonic-gate 	 */
72847c478bd9Sstevel@tonic-gate 	rc_node_clear(txp, 0);
72855b7f77adStw21770 	generate_property_events(tx_data, pg_fmri, auth_string,
72865b7f77adStw21770 	    auth_status, auth_ret_value);
72877c478bd9Sstevel@tonic-gate 
72885b7f77adStw21770 	rc = REP_PROTOCOL_SUCCESS;
72895b7f77adStw21770 
72905b7f77adStw21770 cleanout:
72915b7f77adStw21770 	free(auth_string);
72925b7f77adStw21770 	free(pg_fmri);
72935b7f77adStw21770 	tx_commit_data_free(tx_data);
72945b7f77adStw21770 	return (rc);
72957c478bd9Sstevel@tonic-gate }
72967c478bd9Sstevel@tonic-gate 
72977c478bd9Sstevel@tonic-gate void
72987c478bd9Sstevel@tonic-gate rc_pg_notify_init(rc_node_pg_notify_t *pnp)
72997c478bd9Sstevel@tonic-gate {
73007c478bd9Sstevel@tonic-gate 	uu_list_node_init(pnp, &pnp->rnpn_node, rc_pg_notify_pool);
73017c478bd9Sstevel@tonic-gate 	pnp->rnpn_pg = NULL;
73027c478bd9Sstevel@tonic-gate 	pnp->rnpn_fd = -1;
73037c478bd9Sstevel@tonic-gate }
73047c478bd9Sstevel@tonic-gate 
73057c478bd9Sstevel@tonic-gate int
73067c478bd9Sstevel@tonic-gate rc_pg_notify_setup(rc_node_pg_notify_t *pnp, rc_node_ptr_t *npp, int fd)
73077c478bd9Sstevel@tonic-gate {
73087c478bd9Sstevel@tonic-gate 	rc_node_t *np;
73097c478bd9Sstevel@tonic-gate 
73107c478bd9Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
73117c478bd9Sstevel@tonic-gate 
73127c478bd9Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
73137c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
73147c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
73157c478bd9Sstevel@tonic-gate 	}
73167c478bd9Sstevel@tonic-gate 
73177c478bd9Sstevel@tonic-gate 	/*
73187c478bd9Sstevel@tonic-gate 	 * wait for any transaction in progress to complete
73197c478bd9Sstevel@tonic-gate 	 */
73207c478bd9Sstevel@tonic-gate 	if (!rc_node_wait_flag(np, RC_NODE_IN_TX)) {
73217c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
73227c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_DELETED);
73237c478bd9Sstevel@tonic-gate 	}
73247c478bd9Sstevel@tonic-gate 
73257c478bd9Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_OLD) {
73267c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
73277c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_LATEST);
73287c478bd9Sstevel@tonic-gate 	}
73297c478bd9Sstevel@tonic-gate 
73307c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
73317c478bd9Sstevel@tonic-gate 	rc_pg_notify_fire(pnp);
73327c478bd9Sstevel@tonic-gate 	pnp->rnpn_pg = np;
73337c478bd9Sstevel@tonic-gate 	pnp->rnpn_fd = fd;
73347c478bd9Sstevel@tonic-gate 	(void) uu_list_insert_after(np->rn_pg_notify_list, NULL, pnp);
73357c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
73367c478bd9Sstevel@tonic-gate 
73377c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
73387c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
73397c478bd9Sstevel@tonic-gate }
73407c478bd9Sstevel@tonic-gate 
73417c478bd9Sstevel@tonic-gate void
73427c478bd9Sstevel@tonic-gate rc_pg_notify_fini(rc_node_pg_notify_t *pnp)
73437c478bd9Sstevel@tonic-gate {
73447c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
73457c478bd9Sstevel@tonic-gate 	rc_pg_notify_fire(pnp);
73467c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
73477c478bd9Sstevel@tonic-gate 
73487c478bd9Sstevel@tonic-gate 	uu_list_node_fini(pnp, &pnp->rnpn_node, rc_pg_notify_pool);
73497c478bd9Sstevel@tonic-gate }
73507c478bd9Sstevel@tonic-gate 
73517c478bd9Sstevel@tonic-gate void
73527c478bd9Sstevel@tonic-gate rc_notify_info_init(rc_notify_info_t *rnip)
73537c478bd9Sstevel@tonic-gate {
73547c478bd9Sstevel@tonic-gate 	int i;
73557c478bd9Sstevel@tonic-gate 
73567c478bd9Sstevel@tonic-gate 	uu_list_node_init(rnip, &rnip->rni_list_node, rc_notify_info_pool);
73577c478bd9Sstevel@tonic-gate 	uu_list_node_init(&rnip->rni_notify, &rnip->rni_notify.rcn_list_node,
73587c478bd9Sstevel@tonic-gate 	    rc_notify_pool);
73597c478bd9Sstevel@tonic-gate 
73607c478bd9Sstevel@tonic-gate 	rnip->rni_notify.rcn_node = NULL;
73617c478bd9Sstevel@tonic-gate 	rnip->rni_notify.rcn_info = rnip;
73627c478bd9Sstevel@tonic-gate 
73637c478bd9Sstevel@tonic-gate 	bzero(rnip->rni_namelist, sizeof (rnip->rni_namelist));
73647c478bd9Sstevel@tonic-gate 	bzero(rnip->rni_typelist, sizeof (rnip->rni_typelist));
73657c478bd9Sstevel@tonic-gate 
73667c478bd9Sstevel@tonic-gate 	(void) pthread_cond_init(&rnip->rni_cv, NULL);
73677c478bd9Sstevel@tonic-gate 
73687c478bd9Sstevel@tonic-gate 	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
73697c478bd9Sstevel@tonic-gate 		rnip->rni_namelist[i] = NULL;
73707c478bd9Sstevel@tonic-gate 		rnip->rni_typelist[i] = NULL;
73717c478bd9Sstevel@tonic-gate 	}
73727c478bd9Sstevel@tonic-gate }
73737c478bd9Sstevel@tonic-gate 
73747c478bd9Sstevel@tonic-gate static void
73757c478bd9Sstevel@tonic-gate rc_notify_info_insert_locked(rc_notify_info_t *rnip)
73767c478bd9Sstevel@tonic-gate {
73777c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&rc_pg_notify_lock));
73787c478bd9Sstevel@tonic-gate 
73797c478bd9Sstevel@tonic-gate 	assert(!(rnip->rni_flags & RC_NOTIFY_ACTIVE));
73807c478bd9Sstevel@tonic-gate 
73817c478bd9Sstevel@tonic-gate 	rnip->rni_flags |= RC_NOTIFY_ACTIVE;
73827c478bd9Sstevel@tonic-gate 	(void) uu_list_insert_after(rc_notify_info_list, NULL, rnip);
73837c478bd9Sstevel@tonic-gate 	(void) uu_list_insert_before(rc_notify_list, NULL, &rnip->rni_notify);
73847c478bd9Sstevel@tonic-gate }
73857c478bd9Sstevel@tonic-gate 
73867c478bd9Sstevel@tonic-gate static void
73877c478bd9Sstevel@tonic-gate rc_notify_info_remove_locked(rc_notify_info_t *rnip)
73887c478bd9Sstevel@tonic-gate {
73897c478bd9Sstevel@tonic-gate 	rc_notify_t *me = &rnip->rni_notify;
73907c478bd9Sstevel@tonic-gate 	rc_notify_t *np;
73917c478bd9Sstevel@tonic-gate 
73927c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&rc_pg_notify_lock));
73937c478bd9Sstevel@tonic-gate 
73947c478bd9Sstevel@tonic-gate 	assert(rnip->rni_flags & RC_NOTIFY_ACTIVE);
73957c478bd9Sstevel@tonic-gate 
73967c478bd9Sstevel@tonic-gate 	assert(!(rnip->rni_flags & RC_NOTIFY_DRAIN));
73977c478bd9Sstevel@tonic-gate 	rnip->rni_flags |= RC_NOTIFY_DRAIN;
73987c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&rnip->rni_cv);
73997c478bd9Sstevel@tonic-gate 
74007c478bd9Sstevel@tonic-gate 	(void) uu_list_remove(rc_notify_info_list, rnip);
74017c478bd9Sstevel@tonic-gate 
74027c478bd9Sstevel@tonic-gate 	/*
74037c478bd9Sstevel@tonic-gate 	 * clean up any notifications at the beginning of the list
74047c478bd9Sstevel@tonic-gate 	 */
74057c478bd9Sstevel@tonic-gate 	if (uu_list_first(rc_notify_list) == me) {
7406b5cbdab0STom Whitten 		/*
7407b5cbdab0STom Whitten 		 * We can't call rc_notify_remove_locked() unless
7408b5cbdab0STom Whitten 		 * rc_notify_in_use is 0.
7409b5cbdab0STom Whitten 		 */
7410b5cbdab0STom Whitten 		while (rc_notify_in_use) {
7411b5cbdab0STom Whitten 			(void) pthread_cond_wait(&rc_pg_notify_cv,
7412b5cbdab0STom Whitten 			    &rc_pg_notify_lock);
7413b5cbdab0STom Whitten 		}
74147c478bd9Sstevel@tonic-gate 		while ((np = uu_list_next(rc_notify_list, me)) != NULL &&
74157c478bd9Sstevel@tonic-gate 		    np->rcn_info == NULL)
74167c478bd9Sstevel@tonic-gate 			rc_notify_remove_locked(np);
74177c478bd9Sstevel@tonic-gate 	}
74187c478bd9Sstevel@tonic-gate 	(void) uu_list_remove(rc_notify_list, me);
74197c478bd9Sstevel@tonic-gate 
74207c478bd9Sstevel@tonic-gate 	while (rnip->rni_waiters) {
74217c478bd9Sstevel@tonic-gate 		(void) pthread_cond_broadcast(&rc_pg_notify_cv);
74227c478bd9Sstevel@tonic-gate 		(void) pthread_cond_broadcast(&rnip->rni_cv);
74237c478bd9Sstevel@tonic-gate 		(void) pthread_cond_wait(&rnip->rni_cv, &rc_pg_notify_lock);
74247c478bd9Sstevel@tonic-gate 	}
74257c478bd9Sstevel@tonic-gate 
74267c478bd9Sstevel@tonic-gate 	rnip->rni_flags &= ~(RC_NOTIFY_DRAIN | RC_NOTIFY_ACTIVE);
74277c478bd9Sstevel@tonic-gate }
74287c478bd9Sstevel@tonic-gate 
74297c478bd9Sstevel@tonic-gate static int
74307c478bd9Sstevel@tonic-gate rc_notify_info_add_watch(rc_notify_info_t *rnip, const char **arr,
74317c478bd9Sstevel@tonic-gate     const char *name)
74327c478bd9Sstevel@tonic-gate {
74337c478bd9Sstevel@tonic-gate 	int i;
74347c478bd9Sstevel@tonic-gate 	int rc;
74357c478bd9Sstevel@tonic-gate 	char *f;
74367c478bd9Sstevel@tonic-gate 
74377c478bd9Sstevel@tonic-gate 	rc = rc_check_type_name(REP_PROTOCOL_ENTITY_PROPERTYGRP, name);
74387c478bd9Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS)
74397c478bd9Sstevel@tonic-gate 		return (rc);
74407c478bd9Sstevel@tonic-gate 
74417c478bd9Sstevel@tonic-gate 	f = strdup(name);
74427c478bd9Sstevel@tonic-gate 	if (f == NULL)
74437c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
74447c478bd9Sstevel@tonic-gate 
74457c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
74467c478bd9Sstevel@tonic-gate 
74477c478bd9Sstevel@tonic-gate 	while (rnip->rni_flags & RC_NOTIFY_EMPTYING)
74487c478bd9Sstevel@tonic-gate 		(void) pthread_cond_wait(&rnip->rni_cv, &rc_pg_notify_lock);
74497c478bd9Sstevel@tonic-gate 
745042958ef4Stn143363 	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
74517c478bd9Sstevel@tonic-gate 		if (arr[i] == NULL)
74527c478bd9Sstevel@tonic-gate 			break;
74537c478bd9Sstevel@tonic-gate 
745442958ef4Stn143363 		/*
745542958ef4Stn143363 		 * Don't add name if it's already being tracked.
745642958ef4Stn143363 		 */
745742958ef4Stn143363 		if (strcmp(arr[i], f) == 0) {
745842958ef4Stn143363 			free(f);
745942958ef4Stn143363 			goto out;
746042958ef4Stn143363 		}
746142958ef4Stn143363 	}
746242958ef4Stn143363 
74637c478bd9Sstevel@tonic-gate 	if (i == RC_NOTIFY_MAX_NAMES) {
74647c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
74657c478bd9Sstevel@tonic-gate 		free(f);
74667c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
74677c478bd9Sstevel@tonic-gate 	}
74687c478bd9Sstevel@tonic-gate 
74697c478bd9Sstevel@tonic-gate 	arr[i] = f;
747042958ef4Stn143363 
747142958ef4Stn143363 out:
74727c478bd9Sstevel@tonic-gate 	if (!(rnip->rni_flags & RC_NOTIFY_ACTIVE))
74737c478bd9Sstevel@tonic-gate 		rc_notify_info_insert_locked(rnip);
74747c478bd9Sstevel@tonic-gate 
74757c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
74767c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
74777c478bd9Sstevel@tonic-gate }
74787c478bd9Sstevel@tonic-gate 
74797c478bd9Sstevel@tonic-gate int
74807c478bd9Sstevel@tonic-gate rc_notify_info_add_name(rc_notify_info_t *rnip, const char *name)
74817c478bd9Sstevel@tonic-gate {
74827c478bd9Sstevel@tonic-gate 	return (rc_notify_info_add_watch(rnip, rnip->rni_namelist, name));
74837c478bd9Sstevel@tonic-gate }
74847c478bd9Sstevel@tonic-gate 
74857c478bd9Sstevel@tonic-gate int
74867c478bd9Sstevel@tonic-gate rc_notify_info_add_type(rc_notify_info_t *rnip, const char *type)
74877c478bd9Sstevel@tonic-gate {
74887c478bd9Sstevel@tonic-gate 	return (rc_notify_info_add_watch(rnip, rnip->rni_typelist, type));
74897c478bd9Sstevel@tonic-gate }
74907c478bd9Sstevel@tonic-gate 
74917c478bd9Sstevel@tonic-gate /*
74927c478bd9Sstevel@tonic-gate  * Wait for and report an event of interest to rnip, a notification client
74937c478bd9Sstevel@tonic-gate  */
74947c478bd9Sstevel@tonic-gate int
74957c478bd9Sstevel@tonic-gate rc_notify_info_wait(rc_notify_info_t *rnip, rc_node_ptr_t *out,
74967c478bd9Sstevel@tonic-gate     char *outp, size_t sz)
74977c478bd9Sstevel@tonic-gate {
74987c478bd9Sstevel@tonic-gate 	rc_notify_t *np;
74997c478bd9Sstevel@tonic-gate 	rc_notify_t *me = &rnip->rni_notify;
75007c478bd9Sstevel@tonic-gate 	rc_node_t *nnp;
75017c478bd9Sstevel@tonic-gate 	rc_notify_delete_t *ndp;
75027c478bd9Sstevel@tonic-gate 
75037c478bd9Sstevel@tonic-gate 	int am_first_info;
75047c478bd9Sstevel@tonic-gate 
75057c478bd9Sstevel@tonic-gate 	if (sz > 0)
75067c478bd9Sstevel@tonic-gate 		outp[0] = 0;
75077c478bd9Sstevel@tonic-gate 
75087c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
75097c478bd9Sstevel@tonic-gate 
75107c478bd9Sstevel@tonic-gate 	while ((rnip->rni_flags & (RC_NOTIFY_ACTIVE | RC_NOTIFY_DRAIN)) ==
75117c478bd9Sstevel@tonic-gate 	    RC_NOTIFY_ACTIVE) {
75127c478bd9Sstevel@tonic-gate 		/*
75137c478bd9Sstevel@tonic-gate 		 * If I'm first on the notify list, it is my job to
75147c478bd9Sstevel@tonic-gate 		 * clean up any notifications I pass by.  I can't do that
75157c478bd9Sstevel@tonic-gate 		 * if someone is blocking the list from removals, so I
75167c478bd9Sstevel@tonic-gate 		 * have to wait until they have all drained.
75177c478bd9Sstevel@tonic-gate 		 */
75187c478bd9Sstevel@tonic-gate 		am_first_info = (uu_list_first(rc_notify_list) == me);
75197c478bd9Sstevel@tonic-gate 		if (am_first_info && rc_notify_in_use) {
75207c478bd9Sstevel@tonic-gate 			rnip->rni_waiters++;
75217c478bd9Sstevel@tonic-gate 			(void) pthread_cond_wait(&rc_pg_notify_cv,
75227c478bd9Sstevel@tonic-gate 			    &rc_pg_notify_lock);
75237c478bd9Sstevel@tonic-gate 			rnip->rni_waiters--;
75247c478bd9Sstevel@tonic-gate 			continue;
75257c478bd9Sstevel@tonic-gate 		}
75267c478bd9Sstevel@tonic-gate 
75277c478bd9Sstevel@tonic-gate 		/*
75287c478bd9Sstevel@tonic-gate 		 * Search the list for a node of interest.
75297c478bd9Sstevel@tonic-gate 		 */
75307c478bd9Sstevel@tonic-gate 		np = uu_list_next(rc_notify_list, me);
75317c478bd9Sstevel@tonic-gate 		while (np != NULL && !rc_notify_info_interested(rnip, np)) {
75327c478bd9Sstevel@tonic-gate 			rc_notify_t *next = uu_list_next(rc_notify_list, np);
75337c478bd9Sstevel@tonic-gate 
75347c478bd9Sstevel@tonic-gate 			if (am_first_info) {
75357c478bd9Sstevel@tonic-gate 				if (np->rcn_info) {
75367c478bd9Sstevel@tonic-gate 					/*
75377c478bd9Sstevel@tonic-gate 					 * Passing another client -- stop
75387c478bd9Sstevel@tonic-gate 					 * cleaning up notifications
75397c478bd9Sstevel@tonic-gate 					 */
75407c478bd9Sstevel@tonic-gate 					am_first_info = 0;
75417c478bd9Sstevel@tonic-gate 				} else {
75427c478bd9Sstevel@tonic-gate 					rc_notify_remove_locked(np);
75437c478bd9Sstevel@tonic-gate 				}
75447c478bd9Sstevel@tonic-gate 			}
75457c478bd9Sstevel@tonic-gate 			np = next;
75467c478bd9Sstevel@tonic-gate 		}
75477c478bd9Sstevel@tonic-gate 
75487c478bd9Sstevel@tonic-gate 		/*
75497c478bd9Sstevel@tonic-gate 		 * Nothing of interest -- wait for notification
75507c478bd9Sstevel@tonic-gate 		 */
75517c478bd9Sstevel@tonic-gate 		if (np == NULL) {
75527c478bd9Sstevel@tonic-gate 			rnip->rni_waiters++;
75537c478bd9Sstevel@tonic-gate 			(void) pthread_cond_wait(&rnip->rni_cv,
75547c478bd9Sstevel@tonic-gate 			    &rc_pg_notify_lock);
75557c478bd9Sstevel@tonic-gate 			rnip->rni_waiters--;
75567c478bd9Sstevel@tonic-gate 			continue;
75577c478bd9Sstevel@tonic-gate 		}
75587c478bd9Sstevel@tonic-gate 
75597c478bd9Sstevel@tonic-gate 		/*
75607c478bd9Sstevel@tonic-gate 		 * found something to report -- move myself after the
75617c478bd9Sstevel@tonic-gate 		 * notification and process it.
75627c478bd9Sstevel@tonic-gate 		 */
75637c478bd9Sstevel@tonic-gate 		(void) uu_list_remove(rc_notify_list, me);
75647c478bd9Sstevel@tonic-gate 		(void) uu_list_insert_after(rc_notify_list, np, me);
75657c478bd9Sstevel@tonic-gate 
75667c478bd9Sstevel@tonic-gate 		if ((ndp = np->rcn_delete) != NULL) {
75677c478bd9Sstevel@tonic-gate 			(void) strlcpy(outp, ndp->rnd_fmri, sz);
75687c478bd9Sstevel@tonic-gate 			if (am_first_info)
75697c478bd9Sstevel@tonic-gate 				rc_notify_remove_locked(np);
75707c478bd9Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&rc_pg_notify_lock);
75717c478bd9Sstevel@tonic-gate 			rc_node_clear(out, 0);
75727c478bd9Sstevel@tonic-gate 			return (REP_PROTOCOL_SUCCESS);
75737c478bd9Sstevel@tonic-gate 		}
75747c478bd9Sstevel@tonic-gate 
75757c478bd9Sstevel@tonic-gate 		nnp = np->rcn_node;
75767c478bd9Sstevel@tonic-gate 		assert(nnp != NULL);
75777c478bd9Sstevel@tonic-gate 
75787c478bd9Sstevel@tonic-gate 		/*
75797c478bd9Sstevel@tonic-gate 		 * We can't bump nnp's reference count without grabbing its
75807c478bd9Sstevel@tonic-gate 		 * lock, and rc_pg_notify_lock is a leaf lock.  So we
75817c478bd9Sstevel@tonic-gate 		 * temporarily block all removals to keep nnp from
75827c478bd9Sstevel@tonic-gate 		 * disappearing.
75837c478bd9Sstevel@tonic-gate 		 */
75847c478bd9Sstevel@tonic-gate 		rc_notify_in_use++;
75857c478bd9Sstevel@tonic-gate 		assert(rc_notify_in_use > 0);
75867c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
75877c478bd9Sstevel@tonic-gate 
75887c478bd9Sstevel@tonic-gate 		rc_node_assign(out, nnp);
75897c478bd9Sstevel@tonic-gate 
75907c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_lock(&rc_pg_notify_lock);
75917c478bd9Sstevel@tonic-gate 		assert(rc_notify_in_use > 0);
75927c478bd9Sstevel@tonic-gate 		rc_notify_in_use--;
7593b5cbdab0STom Whitten 
7594b5cbdab0STom Whitten 		if (am_first_info) {
7595b5cbdab0STom Whitten 			/*
7596b5cbdab0STom Whitten 			 * While we had the lock dropped, another thread
7597b5cbdab0STom Whitten 			 * may have also incremented rc_notify_in_use.  We
7598b5cbdab0STom Whitten 			 * need to make sure that we're back to 0 before
7599b5cbdab0STom Whitten 			 * removing the node.
7600b5cbdab0STom Whitten 			 */
7601b5cbdab0STom Whitten 			while (rc_notify_in_use) {
7602b5cbdab0STom Whitten 				(void) pthread_cond_wait(&rc_pg_notify_cv,
7603b5cbdab0STom Whitten 				    &rc_pg_notify_lock);
7604b5cbdab0STom Whitten 			}
76057c478bd9Sstevel@tonic-gate 			rc_notify_remove_locked(np);
7606b5cbdab0STom Whitten 		}
76077c478bd9Sstevel@tonic-gate 		if (rc_notify_in_use == 0)
76087c478bd9Sstevel@tonic-gate 			(void) pthread_cond_broadcast(&rc_pg_notify_cv);
76097c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
76107c478bd9Sstevel@tonic-gate 
76117c478bd9Sstevel@tonic-gate 		return (REP_PROTOCOL_SUCCESS);
76127c478bd9Sstevel@tonic-gate 	}
76137c478bd9Sstevel@tonic-gate 	/*
76147c478bd9Sstevel@tonic-gate 	 * If we're the last one out, let people know it's clear.
76157c478bd9Sstevel@tonic-gate 	 */
76167c478bd9Sstevel@tonic-gate 	if (rnip->rni_waiters == 0)
76177c478bd9Sstevel@tonic-gate 		(void) pthread_cond_broadcast(&rnip->rni_cv);
76187c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
76197c478bd9Sstevel@tonic-gate 	return (REP_PROTOCOL_DONE);
76207c478bd9Sstevel@tonic-gate }
76217c478bd9Sstevel@tonic-gate 
76227c478bd9Sstevel@tonic-gate static void
76237c478bd9Sstevel@tonic-gate rc_notify_info_reset(rc_notify_info_t *rnip)
76247c478bd9Sstevel@tonic-gate {
76257c478bd9Sstevel@tonic-gate 	int i;
76267c478bd9Sstevel@tonic-gate 
76277c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
76287c478bd9Sstevel@tonic-gate 	if (rnip->rni_flags & RC_NOTIFY_ACTIVE)
76297c478bd9Sstevel@tonic-gate 		rc_notify_info_remove_locked(rnip);
76307c478bd9Sstevel@tonic-gate 	assert(!(rnip->rni_flags & (RC_NOTIFY_DRAIN | RC_NOTIFY_EMPTYING)));
76317c478bd9Sstevel@tonic-gate 	rnip->rni_flags |= RC_NOTIFY_EMPTYING;
76327c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
76337c478bd9Sstevel@tonic-gate 
76347c478bd9Sstevel@tonic-gate 	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
76357c478bd9Sstevel@tonic-gate 		if (rnip->rni_namelist[i] != NULL) {
76367c478bd9Sstevel@tonic-gate 			free((void *)rnip->rni_namelist[i]);
76377c478bd9Sstevel@tonic-gate 			rnip->rni_namelist[i] = NULL;
76387c478bd9Sstevel@tonic-gate 		}
76397c478bd9Sstevel@tonic-gate 		if (rnip->rni_typelist[i] != NULL) {
76407c478bd9Sstevel@tonic-gate 			free((void *)rnip->rni_typelist[i]);
76417c478bd9Sstevel@tonic-gate 			rnip->rni_typelist[i] = NULL;
76427c478bd9Sstevel@tonic-gate 		}
76437c478bd9Sstevel@tonic-gate 	}
76447c478bd9Sstevel@tonic-gate 
76457c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
76467c478bd9Sstevel@tonic-gate 	rnip->rni_flags &= ~RC_NOTIFY_EMPTYING;
76477c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
76487c478bd9Sstevel@tonic-gate }
76497c478bd9Sstevel@tonic-gate 
76507c478bd9Sstevel@tonic-gate void
76517c478bd9Sstevel@tonic-gate rc_notify_info_fini(rc_notify_info_t *rnip)
76527c478bd9Sstevel@tonic-gate {
76537c478bd9Sstevel@tonic-gate 	rc_notify_info_reset(rnip);
76547c478bd9Sstevel@tonic-gate 
76557c478bd9Sstevel@tonic-gate 	uu_list_node_fini(rnip, &rnip->rni_list_node, rc_notify_info_pool);
76567c478bd9Sstevel@tonic-gate 	uu_list_node_fini(&rnip->rni_notify, &rnip->rni_notify.rcn_list_node,
76577c478bd9Sstevel@tonic-gate 	    rc_notify_pool);
76587c478bd9Sstevel@tonic-gate }
7659