xref: /onnv-gate/usr/src/cmd/svc/configd/rc_node.c (revision 12430:cde097a78a5c)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53179Sjeanm  * Common Development and Distribution License (the "License").
63179Sjeanm  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
215040Swesolows 
220Sstevel@tonic-gate /*
2312273SCasper.Dik@Sun.COM  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
277266Sbustos  * rc_node.c - In-memory SCF object management
287266Sbustos  *
297266Sbustos  * This layer manages the in-memory cache (the Repository Cache) of SCF
307266Sbustos  * data.  Read requests are usually satisfied from here, but may require
317266Sbustos  * load calls to the "object" layer.  Modify requests always write-through
327266Sbustos  * to the object layer.
330Sstevel@tonic-gate  *
347266Sbustos  * SCF data comprises scopes, services, instances, snapshots, snaplevels,
357266Sbustos  * property groups, properties, and property values.  All but the last are
367266Sbustos  * known here as "entities" and are represented by rc_node_t data
377266Sbustos  * structures.  (Property values are kept in the rn_values member of the
387266Sbustos  * respective property, not as separate objects.)  All entities besides
397266Sbustos  * the "localhost" scope have some entity as a parent, and therefore form
407266Sbustos  * a tree.
410Sstevel@tonic-gate  *
420Sstevel@tonic-gate  * The entity tree is rooted at rc_scope, which rc_node_init() initializes to
430Sstevel@tonic-gate  * the "localhost" scope.  The tree is filled in from the database on-demand
447266Sbustos  * by rc_node_fill_children().
457266Sbustos  *
467266Sbustos  * rc_node_t's are also placed in the cache_hash[] hash table, for rapid
477266Sbustos  * lookup.
480Sstevel@tonic-gate  *
497266Sbustos  * Multiple threads may service client requests, so access to each
507266Sbustos  * rc_node_t is synchronized by its rn_lock member.  Some fields are
517266Sbustos  * protected by bits in the rn_flags field instead, to support operations
527266Sbustos  * which need to drop rn_lock, for example to respect locking order.  Such
537266Sbustos  * flags should be manipulated with the rc_node_{hold,rele}_flag()
547266Sbustos  * functions.
550Sstevel@tonic-gate  *
567266Sbustos  * We track references to nodes to tell when they can be free()d.  rn_refs
577266Sbustos  * should be incremented with rc_node_hold() on the creation of client
587266Sbustos  * references (rc_node_ptr_t's and rc_iter_t's).  rn_erefs ("ephemeral
597266Sbustos  * references") should be incremented when a pointer is read into a local
607266Sbustos  * variable of a thread, with rc_node_hold_ephemeral_locked().  This
617266Sbustos  * hasn't been fully implemented, however, so rc_node_rele() tolerates
627266Sbustos  * rn_erefs being 0.  Some code which predates rn_erefs counts ephemeral
637266Sbustos  * references in rn_refs.  Other references are tracked by the
647266Sbustos  * rn_other_refs field and the RC_NODE_DEAD, RC_NODE_IN_PARENT,
657266Sbustos  * RC_NODE_OLD, and RC_NODE_ON_FORMER flags.
660Sstevel@tonic-gate  *
670Sstevel@tonic-gate  * Locking rules: To dereference an rc_node_t * (usually to lock it), you must
680Sstevel@tonic-gate  * have a hold (rc_node_hold()) on it or otherwise be sure that it hasn't been
690Sstevel@tonic-gate  * rc_node_destroy()ed (hold a lock on its parent or child, hold a flag,
700Sstevel@tonic-gate  * etc.).  Once you have locked an rc_node_t you must check its rn_flags for
710Sstevel@tonic-gate  * RC_NODE_DEAD before you can use it.  This is usually done with the
720Sstevel@tonic-gate  * rc_node_{wait,hold}_flag() functions (often via the rc_node_check_*()
730Sstevel@tonic-gate  * functions & RC_NODE_*() macros), which fail if the object has died.
740Sstevel@tonic-gate  *
757266Sbustos  * When a transactional node (property group or snapshot) is updated,
767266Sbustos  * a new node takes the place of the old node in the global hash and the
777266Sbustos  * old node is hung off of the rn_former list of the new node.  At the
787266Sbustos  * same time, all of its children have their rn_parent_ref pointer set,
797266Sbustos  * and any holds they have are reflected in the old node's rn_other_refs
807266Sbustos  * count.  This is automatically kept up to date until the final reference
817266Sbustos  * to the subgraph is dropped, at which point the node is unrefed and
827266Sbustos  * destroyed, along with all of its children.
837266Sbustos  *
845040Swesolows  * Because name service lookups may take a long time and, more importantly
855040Swesolows  * may trigger additional accesses to the repository, perm_granted() must be
865040Swesolows  * called without holding any locks.
875040Swesolows  *
880Sstevel@tonic-gate  * An ITER_START for a non-ENTITY_VALUE induces an rc_node_fill_children()
890Sstevel@tonic-gate  * call via rc_node_setup_iter() to populate the rn_children uu_list of the
900Sstevel@tonic-gate  * rc_node_t * in question and a call to uu_list_walk_start() on that list.  For
910Sstevel@tonic-gate  * ITER_READ, rc_iter_next() uses uu_list_walk_next() to find the next
920Sstevel@tonic-gate  * apropriate child.
930Sstevel@tonic-gate  *
940Sstevel@tonic-gate  * An ITER_START for an ENTITY_VALUE makes sure the node has its values
950Sstevel@tonic-gate  * filled, and sets up the iterator.  An ITER_READ_VALUE just copies out
960Sstevel@tonic-gate  * the proper values and updates the offset information.
970Sstevel@tonic-gate  *
980Sstevel@tonic-gate  * To allow aliases, snapshots are implemented with a level of indirection.
990Sstevel@tonic-gate  * A snapshot rc_node_t has a snapid which refers to an rc_snapshot_t in
1000Sstevel@tonic-gate  * snapshot.c which contains the authoritative snaplevel information.  The
1010Sstevel@tonic-gate  * snapid is "assigned" by rc_attach_snapshot().
1020Sstevel@tonic-gate  *
1030Sstevel@tonic-gate  * We provide the client layer with rc_node_ptr_t's to reference objects.
1040Sstevel@tonic-gate  * Objects referred to by them are automatically held & released by
1050Sstevel@tonic-gate  * rc_node_assign() & rc_node_clear().  The RC_NODE_PTR_*() macros are used at
1060Sstevel@tonic-gate  * client.c entry points to read the pointers.  They fetch the pointer to the
1070Sstevel@tonic-gate  * object, return (from the function) if it is dead, and lock, hold, or hold
1080Sstevel@tonic-gate  * a flag of the object.
1090Sstevel@tonic-gate  */
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate /*
1120Sstevel@tonic-gate  * Permission checking is authorization-based: some operations may only
1130Sstevel@tonic-gate  * proceed if the user has been assigned at least one of a set of
1140Sstevel@tonic-gate  * authorization strings.  The set of enabling authorizations depends on the
1150Sstevel@tonic-gate  * operation and the target object.  The set of authorizations assigned to
11612273SCasper.Dik@Sun.COM  * a user is determined by an algorithm defined in libsecdb.
1170Sstevel@tonic-gate  *
1180Sstevel@tonic-gate  * The fastest way to decide whether the two sets intersect is by entering the
1190Sstevel@tonic-gate  * strings into a hash table and detecting collisions, which takes linear time
1200Sstevel@tonic-gate  * in the total size of the sets.  Except for the authorization patterns which
1210Sstevel@tonic-gate  * may be assigned to users, which without advanced pattern-matching
1220Sstevel@tonic-gate  * algorithms will take O(n) in the number of enabling authorizations, per
1230Sstevel@tonic-gate  * pattern.
1240Sstevel@tonic-gate  *
1250Sstevel@tonic-gate  * We can achieve some practical speed-ups by noting that if we enter all of
1260Sstevel@tonic-gate  * the authorizations from one of the sets into the hash table we can merely
1270Sstevel@tonic-gate  * check the elements of the second set for existence without adding them.
1280Sstevel@tonic-gate  * This reduces memory requirements and hash table clutter.  The enabling set
1290Sstevel@tonic-gate  * is well suited for this because it is internal to configd (for now, at
1300Sstevel@tonic-gate  * least).  Combine this with short-circuiting and we can even minimize the
1310Sstevel@tonic-gate  * number of queries to the security databases (user_attr & prof_attr).
1320Sstevel@tonic-gate  *
1330Sstevel@tonic-gate  * To force this usage onto clients we provide functions for adding
1340Sstevel@tonic-gate  * authorizations to the enabling set of a permission context structure
1350Sstevel@tonic-gate  * (perm_add_*()) and one to decide whether the the user associated with the
1360Sstevel@tonic-gate  * current door call client possesses any of them (perm_granted()).
1370Sstevel@tonic-gate  *
1380Sstevel@tonic-gate  * At some point, a generic version of this should move to libsecdb.
1395777Stw21770  *
1405777Stw21770  * While entering the enabling strings into the hash table, we keep track
1415777Stw21770  * of which is the most specific for use in generating auditing events.
1425777Stw21770  * See the "Collecting the Authorization String" section of the "SMF Audit
1435777Stw21770  * Events" block comment below.
1440Sstevel@tonic-gate  */
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate /*
1470Sstevel@tonic-gate  * Composition is the combination of sets of properties.  The sets are ordered
1480Sstevel@tonic-gate  * and properties in higher sets obscure properties of the same name in lower
1490Sstevel@tonic-gate  * sets.  Here we present a composed view of an instance's properties as the
1500Sstevel@tonic-gate  * union of its properties and its service's properties.  Similarly the
1510Sstevel@tonic-gate  * properties of snaplevels are combined to form a composed view of the
1520Sstevel@tonic-gate  * properties of a snapshot (which should match the composed view of the
1530Sstevel@tonic-gate  * properties of the instance when the snapshot was taken).
1540Sstevel@tonic-gate  *
1550Sstevel@tonic-gate  * In terms of the client interface, the client may request that a property
1560Sstevel@tonic-gate  * group iterator for an instance or snapshot be composed.  Property groups
1570Sstevel@tonic-gate  * traversed by such an iterator may not have the target entity as a parent.
1580Sstevel@tonic-gate  * Similarly, the properties traversed by a property iterator for those
1590Sstevel@tonic-gate  * property groups may not have the property groups iterated as parents.
1600Sstevel@tonic-gate  *
1610Sstevel@tonic-gate  * Implementation requires that iterators for instances and snapshots be
1620Sstevel@tonic-gate  * composition-savvy, and that we have a "composed property group" entity
1630Sstevel@tonic-gate  * which represents the composition of a number of property groups.  Iteration
1640Sstevel@tonic-gate  * over "composed property groups" yields properties which may have different
1650Sstevel@tonic-gate  * parents, but for all other operations a composed property group behaves
1660Sstevel@tonic-gate  * like the top-most property group it represents.
1670Sstevel@tonic-gate  *
1680Sstevel@tonic-gate  * The implementation is based on the rn_cchain[] array of rc_node_t pointers
1690Sstevel@tonic-gate  * in rc_node_t.  For instances, the pointers point to the instance and its
1700Sstevel@tonic-gate  * parent service.  For snapshots they point to the child snaplevels, and for
1710Sstevel@tonic-gate  * composed property groups they point to property groups.  A composed
1720Sstevel@tonic-gate  * iterator carries an index into rn_cchain[].  Thus most of the magic ends up
1730Sstevel@tonic-gate  * int the rc_iter_*() code.
1740Sstevel@tonic-gate  */
1755777Stw21770 /*
1765777Stw21770  * SMF Audit Events:
1775777Stw21770  * ================
1785777Stw21770  *
1795777Stw21770  * To maintain security, SMF generates audit events whenever
1805777Stw21770  * privileged operations are attempted.  See the System Administration
1815777Stw21770  * Guide:Security Services answerbook for a discussion of the Solaris
1825777Stw21770  * audit system.
1835777Stw21770  *
1845777Stw21770  * The SMF audit event codes are defined in adt_event.h by symbols
1855777Stw21770  * starting with ADT_smf_ and are described in audit_event.txt.  The
1865777Stw21770  * audit record structures are defined in the SMF section of adt.xml.
1875777Stw21770  * adt.xml is used to automatically generate adt_event.h which
1885777Stw21770  * contains the definitions that we code to in this file.  For the
1895777Stw21770  * most part the audit events map closely to actions that you would
1905777Stw21770  * perform with svcadm or svccfg, but there are some special cases
1915777Stw21770  * which we'll discuss later.
1925777Stw21770  *
1935777Stw21770  * The software associated with SMF audit events falls into three
1945777Stw21770  * categories:
1955777Stw21770  * 	- collecting information to be written to the audit
1965777Stw21770  *	  records
1975777Stw21770  *	- using the adt_* functions in
1985777Stw21770  *	  usr/src/lib/libbsm/common/adt.c to generate the audit
1995777Stw21770  *	  records.
2005777Stw21770  * 	- handling special cases
2015777Stw21770  *
2025777Stw21770  * Collecting Information:
2035777Stw21770  * ----------------------
2045777Stw21770  *
2055777Stw21770  * Most all of the audit events require the FMRI of the affected
2065777Stw21770  * object and the authorization string that was used.  The one
2075777Stw21770  * exception is ADT_smf_annotation which we'll talk about later.
2085777Stw21770  *
2095777Stw21770  * Collecting the FMRI:
2105777Stw21770  *
2115777Stw21770  * The rc_node structure has a member called rn_fmri which points to
2125777Stw21770  * its FMRI.  This is initialized by a call to rc_node_build_fmri()
2135777Stw21770  * when the node's parent is established.  The reason for doing it
2145777Stw21770  * at this time is that a node's FMRI is basically the concatenation
2155777Stw21770  * of the parent's FMRI and the node's name with the appropriate
2165777Stw21770  * decoration.  rc_node_build_fmri() does this concatenation and
2175777Stw21770  * decorating.  It is called from rc_node_link_child() and
2185777Stw21770  * rc_node_relink_child() where a node is linked to its parent.
2195777Stw21770  *
2205777Stw21770  * rc_node_get_fmri_or_fragment() is called to retrieve a node's FMRI
2215777Stw21770  * when it is needed.  It returns rn_fmri if it is set.  If the node
2225777Stw21770  * is at the top level, however, rn_fmri won't be set because it was
2235777Stw21770  * never linked to a parent.  In this case,
2245777Stw21770  * rc_node_get_fmri_or_fragment() constructs an FMRI fragment based on
2255777Stw21770  * its node type and its name, rn_name.
2265777Stw21770  *
2275777Stw21770  * Collecting the Authorization String:
2285777Stw21770  *
2295777Stw21770  * Naturally, the authorization string is captured during the
2305777Stw21770  * authorization checking process.  Acceptable authorization strings
2315777Stw21770  * are added to a permcheck_t hash table as noted in the section on
2325777Stw21770  * permission checking above.  Once all entries have been added to the
2335777Stw21770  * hash table, perm_granted() is called.  If the client is authorized,
2345777Stw21770  * perm_granted() returns with pc_auth_string of the permcheck_t
2355777Stw21770  * structure pointing to the authorization string.
2365777Stw21770  *
2375777Stw21770  * This works fine if the client is authorized, but what happens if
2385777Stw21770  * the client is not authorized?  We need to report the required
2395777Stw21770  * authorization string.  This is the authorization that would have
2405777Stw21770  * been used if permission had been granted.  perm_granted() will
2415777Stw21770  * find no match, so it needs to decide which string in the hash
2425777Stw21770  * table to use as the required authorization string.  It needs to do
2435777Stw21770  * this, because configd is still going to generate an event.  A
2445777Stw21770  * design decision was made to use the most specific authorization
2455777Stw21770  * in the hash table.  The pc_auth_type enum designates the
2465777Stw21770  * specificity of an authorization string.  For example, an
2475777Stw21770  * authorization string that is declared in an instance PG is more
2485777Stw21770  * specific than one that is declared in a service PG.
2495777Stw21770  *
2505777Stw21770  * The pc_add() function keeps track of the most specific
2515777Stw21770  * authorization in the hash table.  It does this using the
2525777Stw21770  * pc_specific and pc_specific_type members of the permcheck
2535777Stw21770  * structure.  pc_add() updates these members whenever a more
2545777Stw21770  * specific authorization string is added to the hash table.  Thus, if
2555777Stw21770  * an authorization match is not found, perm_granted() will return
2565777Stw21770  * with pc_auth_string in the permcheck_t pointing to the string that
2575777Stw21770  * is referenced by pc_specific.
2585777Stw21770  *
2595777Stw21770  * Generating the Audit Events:
2605777Stw21770  * ===========================
2615777Stw21770  *
2625777Stw21770  * As the functions in this file process requests for clients of
2635777Stw21770  * configd, they gather the information that is required for an audit
2645777Stw21770  * event.  Eventually, the request processing gets to the point where
2655777Stw21770  * the authorization is rejected or to the point where the requested
2665777Stw21770  * action was attempted.  At these two points smf_audit_event() is
2675777Stw21770  * called.
2685777Stw21770  *
2695777Stw21770  * smf_audit_event() takes 4 parameters:
2705777Stw21770  * 	- the event ID which is one of the ADT_smf_* symbols from
2715777Stw21770  *	  adt_event.h.
2725777Stw21770  * 	- status to pass to adt_put_event()
2735777Stw21770  * 	- return value to pass to adt_put_event()
2745777Stw21770  * 	- the event data (see audit_event_data structure)
2755777Stw21770  *
2765777Stw21770  * All interactions with the auditing software require an audit
2775777Stw21770  * session.  We use one audit session per configd client.  We keep
2785777Stw21770  * track of the audit session in the repcache_client structure.
2795777Stw21770  * smf_audit_event() calls get_audit_session() to get the session
2805777Stw21770  * pointer.
2815777Stw21770  *
2825777Stw21770  * smf_audit_event() then calls adt_alloc_event() to allocate an
2835777Stw21770  * adt_event_data union which is defined in adt_event.h, copies the
2845777Stw21770  * data into the appropriate members of the union and calls
2855777Stw21770  * adt_put_event() to generate the event.
2865777Stw21770  *
2875777Stw21770  * Special Cases:
2885777Stw21770  * =============
2895777Stw21770  *
2905777Stw21770  * There are three major types of special cases:
2915777Stw21770  *
2925777Stw21770  * 	- gathering event information for each action in a
2935777Stw21770  *	  transaction
2945777Stw21770  * 	- Higher level events represented by special property
2955777Stw21770  *	  group/property name combinations.  Many of these are
2965777Stw21770  *	  restarter actions.
2975777Stw21770  * 	- ADT_smf_annotation event
2985777Stw21770  *
2995777Stw21770  * Processing Transaction Actions:
3005777Stw21770  * ------------------------------
3015777Stw21770  *
3025777Stw21770  * A transaction can contain multiple actions to modify, create or
3035777Stw21770  * delete one or more properties.  We need to capture information so
3045777Stw21770  * that we can generate an event for each property action.  The
3055777Stw21770  * transaction information is stored in a tx_commmit_data_t, and
3065777Stw21770  * object.c provides accessor functions to retrieve data from this
3075777Stw21770  * structure.  rc_tx_commit() obtains a tx_commit_data_t by calling
3085777Stw21770  * tx_commit_data_new() and passes this to object_tx_commit() to
3095777Stw21770  * commit the transaction.  Then we call generate_property_events() to
3105777Stw21770  * generate an audit event for each property action.
3115777Stw21770  *
3125777Stw21770  * Special Properties:
3135777Stw21770  * ------------------
3145777Stw21770  *
3155777Stw21770  * There are combinations of property group/property name that are special.
3165777Stw21770  * They are special because they have specific meaning to startd.  startd
3175777Stw21770  * interprets them in a service-independent fashion.
3185777Stw21770  * restarter_actions/refresh and general/enabled are two examples of these.
3195777Stw21770  * A special event is generated for these properties in addition to the
3205777Stw21770  * regular property event described in the previous section.  The special
3215777Stw21770  * properties are declared as an array of audit_special_prop_item
3225777Stw21770  * structures at special_props_list in rc_node.c.
3235777Stw21770  *
3245777Stw21770  * In the previous section, we mentioned the
3255777Stw21770  * generate_property_event() function that generates an event for
3265777Stw21770  * every property action.  Before generating the event,
3275777Stw21770  * generate_property_event() calls special_property_event().
3285777Stw21770  * special_property_event() checks to see if the action involves a
3295777Stw21770  * special property.  If it does, it generates a special audit
3305777Stw21770  * event.
3315777Stw21770  *
3325777Stw21770  * ADT_smf_annotation event:
3335777Stw21770  * ------------------------
3345777Stw21770  *
3355777Stw21770  * This is a special event unlike any other.  It allows the svccfg
3365777Stw21770  * program to store an annotation in the event log before a series
3375777Stw21770  * of transactions is processed.  It is used with the import and
3385777Stw21770  * apply svccfg commands.  svccfg uses the rep_protocol_annotation
3395777Stw21770  * message to pass the operation (import or apply) and the file name
3405777Stw21770  * to configd.  The set_annotation() function in client.c stores
3415777Stw21770  * these away in the a repcache_client structure.  The address of
3425777Stw21770  * this structure is saved in the thread_info structure.
3435777Stw21770  *
3445777Stw21770  * Before it generates any events, smf_audit_event() calls
3455777Stw21770  * smf_annotation_event().  smf_annotation_event() calls
3465777Stw21770  * client_annotation_needed() which is defined in client.c.  If an
3475777Stw21770  * annotation is needed client_annotation_needed() returns the
3485777Stw21770  * operation and filename strings that were saved from the
3495777Stw21770  * rep_protocol_annotation message.  smf_annotation_event() then
3505777Stw21770  * generates the ADT_smf_annotation event.
3515777Stw21770  */
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate #include <assert.h>
3540Sstevel@tonic-gate #include <atomic.h>
3555777Stw21770 #include <bsm/adt_event.h>
3560Sstevel@tonic-gate #include <errno.h>
3570Sstevel@tonic-gate #include <libuutil.h>
3580Sstevel@tonic-gate #include <libscf.h>
3590Sstevel@tonic-gate #include <libscf_priv.h>
3600Sstevel@tonic-gate #include <pthread.h>
3616059Sgww #include <pwd.h>
3620Sstevel@tonic-gate #include <stdio.h>
3630Sstevel@tonic-gate #include <stdlib.h>
3640Sstevel@tonic-gate #include <strings.h>
3650Sstevel@tonic-gate #include <sys/types.h>
3665777Stw21770 #include <syslog.h>
3670Sstevel@tonic-gate #include <unistd.h>
36812273SCasper.Dik@Sun.COM #include <secdb.h>
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate #include "configd.h"
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate #define	AUTH_PREFIX		"solaris.smf."
3730Sstevel@tonic-gate #define	AUTH_MANAGE		AUTH_PREFIX "manage"
3740Sstevel@tonic-gate #define	AUTH_MODIFY		AUTH_PREFIX "modify"
3750Sstevel@tonic-gate #define	AUTH_MODIFY_PREFIX	AUTH_MODIFY "."
3760Sstevel@tonic-gate #define	AUTH_PG_ACTIONS		SCF_PG_RESTARTER_ACTIONS
3770Sstevel@tonic-gate #define	AUTH_PG_ACTIONS_TYPE	SCF_PG_RESTARTER_ACTIONS_TYPE
3780Sstevel@tonic-gate #define	AUTH_PG_GENERAL		SCF_PG_GENERAL
3790Sstevel@tonic-gate #define	AUTH_PG_GENERAL_TYPE	SCF_PG_GENERAL_TYPE
3800Sstevel@tonic-gate #define	AUTH_PG_GENERAL_OVR	SCF_PG_GENERAL_OVR
3810Sstevel@tonic-gate #define	AUTH_PG_GENERAL_OVR_TYPE  SCF_PG_GENERAL_OVR_TYPE
3820Sstevel@tonic-gate #define	AUTH_PROP_ACTION	"action_authorization"
3830Sstevel@tonic-gate #define	AUTH_PROP_ENABLED	"enabled"
3840Sstevel@tonic-gate #define	AUTH_PROP_MODIFY	"modify_authorization"
3850Sstevel@tonic-gate #define	AUTH_PROP_VALUE		"value_authorization"
3865040Swesolows #define	AUTH_PROP_READ		"read_authorization"
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate #define	MAX_VALID_CHILDREN 3
3890Sstevel@tonic-gate 
3905777Stw21770 /*
3915777Stw21770  * The ADT_smf_* symbols may not be defined on the build machine.  Because
3925777Stw21770  * of this, we do not want to compile the _smf_aud_event() function when
3935777Stw21770  * doing native builds.
3945777Stw21770  */
3955777Stw21770 #ifdef	NATIVE_BUILD
3965777Stw21770 #define	smf_audit_event(i, s, r, d)
3975777Stw21770 #else
3985777Stw21770 #define	smf_audit_event(i, s, r, d)	_smf_audit_event(i, s, r, d)
3995777Stw21770 #endif	/* NATIVE_BUILD */
4005777Stw21770 
4010Sstevel@tonic-gate typedef struct rc_type_info {
4020Sstevel@tonic-gate 	uint32_t	rt_type;		/* matches array index */
4030Sstevel@tonic-gate 	uint32_t	rt_num_ids;
4040Sstevel@tonic-gate 	uint32_t	rt_name_flags;
4050Sstevel@tonic-gate 	uint32_t	rt_valid_children[MAX_VALID_CHILDREN];
4060Sstevel@tonic-gate } rc_type_info_t;
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate #define	RT_NO_NAME	-1U
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate static rc_type_info_t rc_types[] = {
4110Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_NONE, 0, RT_NO_NAME},
4120Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_SCOPE, 0, 0,
4130Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_SERVICE, REP_PROTOCOL_ENTITY_SCOPE}},
4140Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_SERVICE, 0, UU_NAME_DOMAIN | UU_NAME_PATH,
4150Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_INSTANCE, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
4160Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_INSTANCE, 1, UU_NAME_DOMAIN,
4170Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_SNAPSHOT, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
4180Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_SNAPSHOT, 2, UU_NAME_DOMAIN,
4190Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_SNAPLEVEL, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
4200Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_SNAPLEVEL, 4, RT_NO_NAME,
4210Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_PROPERTYGRP}},
4220Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_PROPERTYGRP, 5, UU_NAME_DOMAIN,
4230Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_PROPERTY}},
4240Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_CPROPERTYGRP, 0, UU_NAME_DOMAIN,
4250Sstevel@tonic-gate 	    {REP_PROTOCOL_ENTITY_PROPERTY}},
4260Sstevel@tonic-gate 	{REP_PROTOCOL_ENTITY_PROPERTY, 7, UU_NAME_DOMAIN},
4270Sstevel@tonic-gate 	{-1UL}
4280Sstevel@tonic-gate };
4290Sstevel@tonic-gate #define	NUM_TYPES	((sizeof (rc_types) / sizeof (*rc_types)))
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate /* Element of a permcheck_t hash table. */
4320Sstevel@tonic-gate struct pc_elt {
4330Sstevel@tonic-gate 	struct pc_elt	*pce_next;
4340Sstevel@tonic-gate 	char		pce_auth[1];
4350Sstevel@tonic-gate };
4360Sstevel@tonic-gate 
4375777Stw21770 /*
4385777Stw21770  * If an authorization fails, we must decide which of the elements in the
4395777Stw21770  * permcheck hash table to use in the audit event.  That is to say of all
4405777Stw21770  * the strings in the hash table, we must choose one and use it in the audit
4415777Stw21770  * event.  It is desirable to use the most specific string in the audit
4425777Stw21770  * event.
4435777Stw21770  *
4445777Stw21770  * The pc_auth_type specifies the types (sources) of authorization
4455777Stw21770  * strings.  The enum is ordered in increasing specificity.
4465777Stw21770  */
4475777Stw21770 typedef enum pc_auth_type {
4485777Stw21770 	PC_AUTH_NONE = 0,	/* no auth string available. */
4495777Stw21770 	PC_AUTH_SMF,		/* strings coded into SMF. */
4505777Stw21770 	PC_AUTH_SVC,		/* strings specified in PG of a service. */
4515777Stw21770 	PC_AUTH_INST		/* strings specified in PG of an instance. */
4525777Stw21770 } pc_auth_type_t;
4535777Stw21770 
4548497SThomas.Whitten@Sun.COM /*
4558497SThomas.Whitten@Sun.COM  * The following enum is used to represent the results of the checks to see
4568497SThomas.Whitten@Sun.COM  * if the client has the appropriate permissions to perform an action.
4578497SThomas.Whitten@Sun.COM  */
4588497SThomas.Whitten@Sun.COM typedef enum perm_status {
4598497SThomas.Whitten@Sun.COM 	PERM_DENIED = 0,	/* Permission denied. */
4608497SThomas.Whitten@Sun.COM 	PERM_GRANTED,		/* Client has authorizations. */
4618497SThomas.Whitten@Sun.COM 	PERM_GONE,		/* Door client went away. */
4628497SThomas.Whitten@Sun.COM 	PERM_FAIL		/* Generic failure. e.g. resources */
4638497SThomas.Whitten@Sun.COM } perm_status_t;
4648497SThomas.Whitten@Sun.COM 
4650Sstevel@tonic-gate /* An authorization set hash table. */
4660Sstevel@tonic-gate typedef struct {
4670Sstevel@tonic-gate 	struct pc_elt	**pc_buckets;
4680Sstevel@tonic-gate 	uint_t		pc_bnum;		/* number of buckets */
4690Sstevel@tonic-gate 	uint_t		pc_enum;		/* number of elements */
4705777Stw21770 	struct pc_elt	*pc_specific;		/* most specific element */
4715777Stw21770 	pc_auth_type_t	pc_specific_type;	/* type of pc_specific */
4725777Stw21770 	char		*pc_auth_string;	/* authorization string */
4735777Stw21770 						/* for audit events */
4740Sstevel@tonic-gate } permcheck_t;
4750Sstevel@tonic-gate 
4765777Stw21770 /*
4775777Stw21770  * Structure for holding audit event data.  Not all events use all members
4785777Stw21770  * of the structure.
4795777Stw21770  */
4805777Stw21770 typedef struct audit_event_data {
4815777Stw21770 	char		*ed_auth;	/* authorization string. */
4825777Stw21770 	char		*ed_fmri;	/* affected FMRI. */
4835777Stw21770 	char		*ed_snapname;	/* name of snapshot. */
4845777Stw21770 	char		*ed_old_fmri;	/* old fmri in attach case. */
4855777Stw21770 	char		*ed_old_name;	/* old snapshot in attach case. */
4865777Stw21770 	char		*ed_type;	/* prop. group or prop. type. */
4875777Stw21770 	char		*ed_prop_value;	/* property value. */
4885777Stw21770 } audit_event_data_t;
4895777Stw21770 
4905777Stw21770 /*
4915777Stw21770  * Pointer to function to do special processing to get audit event ID.
4925777Stw21770  * Audit event IDs are defined in /usr/include/bsm/adt_event.h.  Function
4935777Stw21770  * returns 0 if ID successfully retrieved.  Otherwise it returns -1.
4945777Stw21770  */
4955777Stw21770 typedef int (*spc_getid_fn_t)(tx_commit_data_t *, size_t, const char *,
4965777Stw21770     au_event_t *);
4975777Stw21770 static int general_enable_id(tx_commit_data_t *, size_t, const char *,
4985777Stw21770     au_event_t *);
4995777Stw21770 
5000Sstevel@tonic-gate static uu_list_pool_t *rc_children_pool;
5010Sstevel@tonic-gate static uu_list_pool_t *rc_pg_notify_pool;
5020Sstevel@tonic-gate static uu_list_pool_t *rc_notify_pool;
5030Sstevel@tonic-gate static uu_list_pool_t *rc_notify_info_pool;
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate static rc_node_t *rc_scope;
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate static pthread_mutex_t	rc_pg_notify_lock = PTHREAD_MUTEX_INITIALIZER;
5080Sstevel@tonic-gate static pthread_cond_t	rc_pg_notify_cv = PTHREAD_COND_INITIALIZER;
5090Sstevel@tonic-gate static uint_t		rc_notify_in_use;	/* blocks removals */
5100Sstevel@tonic-gate 
5115777Stw21770 /*
5125777Stw21770  * Some combinations of property group/property name require a special
5135777Stw21770  * audit event to be generated when there is a change.
5145777Stw21770  * audit_special_prop_item_t is used to specify these special cases.  The
5155777Stw21770  * special_props_list array defines a list of these special properties.
5165777Stw21770  */
5175777Stw21770 typedef struct audit_special_prop_item {
5185777Stw21770 	const char	*api_pg_name;	/* property group name. */
5195777Stw21770 	const char	*api_prop_name;	/* property name. */
5205777Stw21770 	au_event_t	api_event_id;	/* event id or 0. */
5215777Stw21770 	spc_getid_fn_t	api_event_func; /* function to get event id. */
5225777Stw21770 } audit_special_prop_item_t;
5235777Stw21770 
5245777Stw21770 /*
5255777Stw21770  * Native builds are done using the build machine's standard include
5265777Stw21770  * files.  These files may not yet have the definitions for the ADT_smf_*
5275777Stw21770  * symbols.  Thus, we do not compile this table when doing native builds.
5285777Stw21770  */
5295777Stw21770 #ifndef	NATIVE_BUILD
5305777Stw21770 /*
5315777Stw21770  * The following special_props_list array specifies property group/property
5325777Stw21770  * name combinations that have specific meaning to startd.  A special event
5335777Stw21770  * is generated for these combinations in addition to the regular property
5345777Stw21770  * event.
5355777Stw21770  *
5365777Stw21770  * At run time this array gets sorted.  See the call to qsort(3C) in
5375777Stw21770  * rc_node_init().  The array is sorted, so that bsearch(3C) can be used
5385777Stw21770  * to do lookups.
5395777Stw21770  */
5405777Stw21770 static audit_special_prop_item_t special_props_list[] = {
5415777Stw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_DEGRADED, ADT_smf_degrade,
5425777Stw21770 	    NULL},
5435777Stw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_DEGRADE_IMMEDIATE,
5445777Stw21770 	    ADT_smf_immediate_degrade, NULL},
5455777Stw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_OFF, ADT_smf_clear, NULL},
5465777Stw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON,
5475777Stw21770 	    ADT_smf_maintenance, NULL},
5485777Stw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_IMMEDIATE,
5495777Stw21770 	    ADT_smf_immediate_maintenance, NULL},
5505777Stw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_IMMTEMP,
5515777Stw21770 	    ADT_smf_immtmp_maintenance, NULL},
5525777Stw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_TEMPORARY,
5535777Stw21770 	    ADT_smf_tmp_maintenance, NULL},
5545777Stw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_REFRESH, ADT_smf_refresh, NULL},
5555777Stw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_RESTART, ADT_smf_restart, NULL},
5565777Stw21770 	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_RESTORE, ADT_smf_clear, NULL},
5575777Stw21770 	{SCF_PG_OPTIONS, SCF_PROPERTY_MILESTONE, ADT_smf_milestone, NULL},
5585777Stw21770 	{SCF_PG_OPTIONS_OVR, SCF_PROPERTY_MILESTONE, ADT_smf_milestone, NULL},
5595777Stw21770 	{SCF_PG_GENERAL, SCF_PROPERTY_ENABLED, 0, general_enable_id},
5605777Stw21770 	{SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED, 0, general_enable_id}
5615777Stw21770 };
5625777Stw21770 #define	SPECIAL_PROP_COUNT	(sizeof (special_props_list) /\
5635777Stw21770 	sizeof (audit_special_prop_item_t))
5645777Stw21770 #endif	/* NATIVE_BUILD */
5655777Stw21770 
5660Sstevel@tonic-gate /*
5670Sstevel@tonic-gate  * We support an arbitrary number of clients interested in events for certain
5680Sstevel@tonic-gate  * types of changes.  Each client is represented by an rc_notify_info_t, and
5690Sstevel@tonic-gate  * all clients are chained onto the rc_notify_info_list.
5700Sstevel@tonic-gate  *
5710Sstevel@tonic-gate  * The rc_notify_list is the global notification list.  Each entry is of
5720Sstevel@tonic-gate  * type rc_notify_t, which is embedded in one of three other structures:
5730Sstevel@tonic-gate  *
5740Sstevel@tonic-gate  *	rc_node_t		property group update notification
5750Sstevel@tonic-gate  *	rc_notify_delete_t	object deletion notification
5760Sstevel@tonic-gate  *	rc_notify_info_t	notification clients
5770Sstevel@tonic-gate  *
5780Sstevel@tonic-gate  * Which type of object is determined by which pointer in the rc_notify_t is
5790Sstevel@tonic-gate  * non-NULL.
5800Sstevel@tonic-gate  *
5810Sstevel@tonic-gate  * New notifications and clients are added to the end of the list.
5820Sstevel@tonic-gate  * Notifications no-one is interested in are never added to the list.
5830Sstevel@tonic-gate  *
5840Sstevel@tonic-gate  * Clients use their position in the list to track which notifications they
5850Sstevel@tonic-gate  * have not yet reported.  As they process notifications, they move forward
5860Sstevel@tonic-gate  * in the list past them.  There is always a client at the beginning of the
5870Sstevel@tonic-gate  * list -- as he moves past notifications, he removes them from the list and
5880Sstevel@tonic-gate  * cleans them up.
5890Sstevel@tonic-gate  *
5900Sstevel@tonic-gate  * The rc_pg_notify_lock protects all notification state.  The rc_pg_notify_cv
5910Sstevel@tonic-gate  * is used for global signalling, and each client has a cv which he waits for
5920Sstevel@tonic-gate  * events of interest on.
59312299Stom.whitten@oracle.com  *
59412299Stom.whitten@oracle.com  * rc_notify_in_use is used to protect rc_notify_list from deletions when
59512299Stom.whitten@oracle.com  * the rc_pg_notify_lock is dropped.  Specifically, rc_notify_info_wait()
59612299Stom.whitten@oracle.com  * must drop the lock to call rc_node_assign(), and then it reacquires the
59712299Stom.whitten@oracle.com  * lock.  Deletions from rc_notify_list during this period are not
59812299Stom.whitten@oracle.com  * allowed.  Insertions do not matter, because they are always done at the
59912299Stom.whitten@oracle.com  * end of the list.
6000Sstevel@tonic-gate  */
6010Sstevel@tonic-gate static uu_list_t	*rc_notify_info_list;
6020Sstevel@tonic-gate static uu_list_t	*rc_notify_list;
6030Sstevel@tonic-gate 
6040Sstevel@tonic-gate #define	HASH_SIZE	512
6050Sstevel@tonic-gate #define	HASH_MASK	(HASH_SIZE - 1)
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate #pragma align 64(cache_hash)
6080Sstevel@tonic-gate static cache_bucket_t cache_hash[HASH_SIZE];
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate #define	CACHE_BUCKET(h)		(&cache_hash[(h) & HASH_MASK])
6110Sstevel@tonic-gate 
6127266Sbustos 
6137266Sbustos static void rc_node_no_client_refs(rc_node_t *np);
6147266Sbustos 
6157266Sbustos 
6160Sstevel@tonic-gate static uint32_t
rc_node_hash(rc_node_lookup_t * lp)6170Sstevel@tonic-gate rc_node_hash(rc_node_lookup_t *lp)
6180Sstevel@tonic-gate {
6190Sstevel@tonic-gate 	uint32_t type = lp->rl_type;
6200Sstevel@tonic-gate 	uint32_t backend = lp->rl_backend;
621471Shg115875 	uint32_t mainid = lp->rl_main_id;
6220Sstevel@tonic-gate 	uint32_t *ids = lp->rl_ids;
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 	rc_type_info_t *tp = &rc_types[type];
6250Sstevel@tonic-gate 	uint32_t num_ids;
6260Sstevel@tonic-gate 	uint32_t left;
6270Sstevel@tonic-gate 	uint32_t hash;
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate 	assert(backend == BACKEND_TYPE_NORMAL ||
6300Sstevel@tonic-gate 	    backend == BACKEND_TYPE_NONPERSIST);
6310Sstevel@tonic-gate 
6320Sstevel@tonic-gate 	assert(type > 0 && type < NUM_TYPES);
6330Sstevel@tonic-gate 	num_ids = tp->rt_num_ids;
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	left = MAX_IDS - num_ids;
6360Sstevel@tonic-gate 	assert(num_ids <= MAX_IDS);
6370Sstevel@tonic-gate 
638471Shg115875 	hash = type * 7 + mainid * 5 + backend;
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	while (num_ids-- > 0)
6410Sstevel@tonic-gate 		hash = hash * 11 + *ids++ * 7;
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 	/*
6440Sstevel@tonic-gate 	 * the rest should be zeroed
6450Sstevel@tonic-gate 	 */
6460Sstevel@tonic-gate 	while (left-- > 0)
6470Sstevel@tonic-gate 		assert(*ids++ == 0);
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 	return (hash);
6500Sstevel@tonic-gate }
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate static int
rc_node_match(rc_node_t * np,rc_node_lookup_t * l)6530Sstevel@tonic-gate rc_node_match(rc_node_t *np, rc_node_lookup_t *l)
6540Sstevel@tonic-gate {
6550Sstevel@tonic-gate 	rc_node_lookup_t *r = &np->rn_id;
6560Sstevel@tonic-gate 	rc_type_info_t *tp;
6570Sstevel@tonic-gate 	uint32_t type;
6580Sstevel@tonic-gate 	uint32_t num_ids;
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 	if (r->rl_main_id != l->rl_main_id)
6610Sstevel@tonic-gate 		return (0);
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 	type = r->rl_type;
6640Sstevel@tonic-gate 	if (type != l->rl_type)
6650Sstevel@tonic-gate 		return (0);
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate 	assert(type > 0 && type < NUM_TYPES);
6680Sstevel@tonic-gate 
6690Sstevel@tonic-gate 	tp = &rc_types[r->rl_type];
6700Sstevel@tonic-gate 	num_ids = tp->rt_num_ids;
6710Sstevel@tonic-gate 
6720Sstevel@tonic-gate 	assert(num_ids <= MAX_IDS);
6730Sstevel@tonic-gate 	while (num_ids-- > 0)
6740Sstevel@tonic-gate 		if (r->rl_ids[num_ids] != l->rl_ids[num_ids])
6750Sstevel@tonic-gate 			return (0);
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 	return (1);
6780Sstevel@tonic-gate }
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate /*
6817266Sbustos  * Register an ephemeral reference to np.  This should be done while both
6827266Sbustos  * the persistent reference from which the np pointer was read is locked
6837266Sbustos  * and np itself is locked.  This guarantees that another thread which
6847266Sbustos  * thinks it has the last reference will yield without destroying the
6857266Sbustos  * node.
6867266Sbustos  */
6877266Sbustos static void
rc_node_hold_ephemeral_locked(rc_node_t * np)6887266Sbustos rc_node_hold_ephemeral_locked(rc_node_t *np)
6897266Sbustos {
6907266Sbustos 	assert(MUTEX_HELD(&np->rn_lock));
6917266Sbustos 
6927266Sbustos 	++np->rn_erefs;
6937266Sbustos }
6947266Sbustos 
6957266Sbustos /*
6960Sstevel@tonic-gate  * the "other" references on a node are maintained in an atomically
6970Sstevel@tonic-gate  * updated refcount, rn_other_refs.  This can be bumped from arbitrary
6980Sstevel@tonic-gate  * context, and tracks references to a possibly out-of-date node's children.
6990Sstevel@tonic-gate  *
7000Sstevel@tonic-gate  * To prevent the node from disappearing between the final drop of
7010Sstevel@tonic-gate  * rn_other_refs and the unref handling, rn_other_refs_held is bumped on
7020Sstevel@tonic-gate  * 0->1 transitions and decremented (with the node lock held) on 1->0
7030Sstevel@tonic-gate  * transitions.
7040Sstevel@tonic-gate  */
7050Sstevel@tonic-gate static void
rc_node_hold_other(rc_node_t * np)7060Sstevel@tonic-gate rc_node_hold_other(rc_node_t *np)
7070Sstevel@tonic-gate {
7080Sstevel@tonic-gate 	if (atomic_add_32_nv(&np->rn_other_refs, 1) == 1) {
7090Sstevel@tonic-gate 		atomic_add_32(&np->rn_other_refs_held, 1);
7100Sstevel@tonic-gate 		assert(np->rn_other_refs_held > 0);
7110Sstevel@tonic-gate 	}
7120Sstevel@tonic-gate 	assert(np->rn_other_refs > 0);
7130Sstevel@tonic-gate }
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate /*
7160Sstevel@tonic-gate  * No node locks may be held
7170Sstevel@tonic-gate  */
7180Sstevel@tonic-gate static void
rc_node_rele_other(rc_node_t * np)7190Sstevel@tonic-gate rc_node_rele_other(rc_node_t *np)
7200Sstevel@tonic-gate {
7210Sstevel@tonic-gate 	assert(np->rn_other_refs > 0);
7220Sstevel@tonic-gate 	if (atomic_add_32_nv(&np->rn_other_refs, -1) == 0) {
7230Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
7240Sstevel@tonic-gate 		assert(np->rn_other_refs_held > 0);
7250Sstevel@tonic-gate 		if (atomic_add_32_nv(&np->rn_other_refs_held, -1) == 0 &&
7267266Sbustos 		    np->rn_refs == 0 && (np->rn_flags & RC_NODE_OLD)) {
7277266Sbustos 			/*
7287266Sbustos 			 * This was the last client reference.  Destroy
7297266Sbustos 			 * any other references and free() the node.
7307266Sbustos 			 */
7317266Sbustos 			rc_node_no_client_refs(np);
7327266Sbustos 		} else {
7330Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
7347266Sbustos 		}
7350Sstevel@tonic-gate 	}
7360Sstevel@tonic-gate }
7370Sstevel@tonic-gate 
7380Sstevel@tonic-gate static void
rc_node_hold_locked(rc_node_t * np)7390Sstevel@tonic-gate rc_node_hold_locked(rc_node_t *np)
7400Sstevel@tonic-gate {
7410Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
7420Sstevel@tonic-gate 
7430Sstevel@tonic-gate 	if (np->rn_refs == 0 && (np->rn_flags & RC_NODE_PARENT_REF))
7440Sstevel@tonic-gate 		rc_node_hold_other(np->rn_parent_ref);
7450Sstevel@tonic-gate 	np->rn_refs++;
7460Sstevel@tonic-gate 	assert(np->rn_refs > 0);
7470Sstevel@tonic-gate }
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate static void
rc_node_hold(rc_node_t * np)7500Sstevel@tonic-gate rc_node_hold(rc_node_t *np)
7510Sstevel@tonic-gate {
7520Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
7530Sstevel@tonic-gate 	rc_node_hold_locked(np);
7540Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
7550Sstevel@tonic-gate }
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate static void
rc_node_rele_locked(rc_node_t * np)7580Sstevel@tonic-gate rc_node_rele_locked(rc_node_t *np)
7590Sstevel@tonic-gate {
7600Sstevel@tonic-gate 	int unref = 0;
7610Sstevel@tonic-gate 	rc_node_t *par_ref = NULL;
7620Sstevel@tonic-gate 
7630Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
7640Sstevel@tonic-gate 	assert(np->rn_refs > 0);
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate 	if (--np->rn_refs == 0) {
7670Sstevel@tonic-gate 		if (np->rn_flags & RC_NODE_PARENT_REF)
7680Sstevel@tonic-gate 			par_ref = np->rn_parent_ref;
7690Sstevel@tonic-gate 
770294Sbustos 		/*
771294Sbustos 		 * Composed property groups are only as good as their
772294Sbustos 		 * references.
773294Sbustos 		 */
774294Sbustos 		if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP)
775294Sbustos 			np->rn_flags |= RC_NODE_DEAD;
776294Sbustos 
7770Sstevel@tonic-gate 		if ((np->rn_flags & (RC_NODE_DEAD|RC_NODE_OLD)) &&
7780Sstevel@tonic-gate 		    np->rn_other_refs == 0 && np->rn_other_refs_held == 0)
7790Sstevel@tonic-gate 			unref = 1;
7800Sstevel@tonic-gate 	}
7810Sstevel@tonic-gate 
7827266Sbustos 	if (unref) {
7837266Sbustos 		/*
7847266Sbustos 		 * This was the last client reference.  Destroy any other
7857266Sbustos 		 * references and free() the node.
7867266Sbustos 		 */
7877266Sbustos 		rc_node_no_client_refs(np);
7887266Sbustos 	} else {
7897266Sbustos 		/*
7907266Sbustos 		 * rn_erefs can be 0 if we acquired the reference in
7917266Sbustos 		 * a path which hasn't been updated to increment rn_erefs.
7927266Sbustos 		 * When all paths which end here are updated, we should
7937266Sbustos 		 * assert rn_erefs > 0 and always decrement it.
7947266Sbustos 		 */
7957266Sbustos 		if (np->rn_erefs > 0)
7967266Sbustos 			--np->rn_erefs;
7970Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
7987266Sbustos 	}
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 	if (par_ref != NULL)
8010Sstevel@tonic-gate 		rc_node_rele_other(par_ref);
8020Sstevel@tonic-gate }
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate void
rc_node_rele(rc_node_t * np)8050Sstevel@tonic-gate rc_node_rele(rc_node_t *np)
8060Sstevel@tonic-gate {
8070Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
8080Sstevel@tonic-gate 	rc_node_rele_locked(np);
8090Sstevel@tonic-gate }
8100Sstevel@tonic-gate 
8110Sstevel@tonic-gate static cache_bucket_t *
cache_hold(uint32_t h)8120Sstevel@tonic-gate cache_hold(uint32_t h)
8130Sstevel@tonic-gate {
8140Sstevel@tonic-gate 	cache_bucket_t *bp = CACHE_BUCKET(h);
8150Sstevel@tonic-gate 	(void) pthread_mutex_lock(&bp->cb_lock);
8160Sstevel@tonic-gate 	return (bp);
8170Sstevel@tonic-gate }
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate static void
cache_release(cache_bucket_t * bp)8200Sstevel@tonic-gate cache_release(cache_bucket_t *bp)
8210Sstevel@tonic-gate {
8220Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&bp->cb_lock);
8230Sstevel@tonic-gate }
8240Sstevel@tonic-gate 
8250Sstevel@tonic-gate static rc_node_t *
cache_lookup_unlocked(cache_bucket_t * bp,rc_node_lookup_t * lp)8260Sstevel@tonic-gate cache_lookup_unlocked(cache_bucket_t *bp, rc_node_lookup_t *lp)
8270Sstevel@tonic-gate {
8280Sstevel@tonic-gate 	uint32_t h = rc_node_hash(lp);
8290Sstevel@tonic-gate 	rc_node_t *np;
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate 	assert(MUTEX_HELD(&bp->cb_lock));
8320Sstevel@tonic-gate 	assert(bp == CACHE_BUCKET(h));
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate 	for (np = bp->cb_head; np != NULL; np = np->rn_hash_next) {
8350Sstevel@tonic-gate 		if (np->rn_hash == h && rc_node_match(np, lp)) {
8360Sstevel@tonic-gate 			rc_node_hold(np);
8370Sstevel@tonic-gate 			return (np);
8380Sstevel@tonic-gate 		}
8390Sstevel@tonic-gate 	}
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 	return (NULL);
8420Sstevel@tonic-gate }
8430Sstevel@tonic-gate 
8440Sstevel@tonic-gate static rc_node_t *
cache_lookup(rc_node_lookup_t * lp)8450Sstevel@tonic-gate cache_lookup(rc_node_lookup_t *lp)
8460Sstevel@tonic-gate {
8470Sstevel@tonic-gate 	uint32_t h;
8480Sstevel@tonic-gate 	cache_bucket_t *bp;
8490Sstevel@tonic-gate 	rc_node_t *np;
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	h = rc_node_hash(lp);
8520Sstevel@tonic-gate 	bp = cache_hold(h);
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 	np = cache_lookup_unlocked(bp, lp);
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate 	cache_release(bp);
8570Sstevel@tonic-gate 
8580Sstevel@tonic-gate 	return (np);
8590Sstevel@tonic-gate }
8600Sstevel@tonic-gate 
8610Sstevel@tonic-gate static void
cache_insert_unlocked(cache_bucket_t * bp,rc_node_t * np)8620Sstevel@tonic-gate cache_insert_unlocked(cache_bucket_t *bp, rc_node_t *np)
8630Sstevel@tonic-gate {
8640Sstevel@tonic-gate 	assert(MUTEX_HELD(&bp->cb_lock));
8650Sstevel@tonic-gate 	assert(np->rn_hash == rc_node_hash(&np->rn_id));
8660Sstevel@tonic-gate 	assert(bp == CACHE_BUCKET(np->rn_hash));
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate 	assert(np->rn_hash_next == NULL);
8690Sstevel@tonic-gate 
8700Sstevel@tonic-gate 	np->rn_hash_next = bp->cb_head;
8710Sstevel@tonic-gate 	bp->cb_head = np;
8720Sstevel@tonic-gate }
8730Sstevel@tonic-gate 
8740Sstevel@tonic-gate static void
cache_remove_unlocked(cache_bucket_t * bp,rc_node_t * np)8750Sstevel@tonic-gate cache_remove_unlocked(cache_bucket_t *bp, rc_node_t *np)
8760Sstevel@tonic-gate {
8770Sstevel@tonic-gate 	rc_node_t **npp;
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate 	assert(MUTEX_HELD(&bp->cb_lock));
8800Sstevel@tonic-gate 	assert(np->rn_hash == rc_node_hash(&np->rn_id));
8810Sstevel@tonic-gate 	assert(bp == CACHE_BUCKET(np->rn_hash));
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 	for (npp = &bp->cb_head; *npp != NULL; npp = &(*npp)->rn_hash_next)
8840Sstevel@tonic-gate 		if (*npp == np)
8850Sstevel@tonic-gate 			break;
8860Sstevel@tonic-gate 
8870Sstevel@tonic-gate 	assert(*npp == np);
8880Sstevel@tonic-gate 	*npp = np->rn_hash_next;
8890Sstevel@tonic-gate 	np->rn_hash_next = NULL;
8900Sstevel@tonic-gate }
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate /*
8930Sstevel@tonic-gate  * verify that the 'parent' type can have a child typed 'child'
8940Sstevel@tonic-gate  * Fails with
8950Sstevel@tonic-gate  *   _INVALID_TYPE - argument is invalid
8960Sstevel@tonic-gate  *   _TYPE_MISMATCH - parent type cannot have children of type child
8970Sstevel@tonic-gate  */
8980Sstevel@tonic-gate static int
rc_check_parent_child(uint32_t parent,uint32_t child)8990Sstevel@tonic-gate rc_check_parent_child(uint32_t parent, uint32_t child)
9000Sstevel@tonic-gate {
9010Sstevel@tonic-gate 	int idx;
9020Sstevel@tonic-gate 	uint32_t type;
9030Sstevel@tonic-gate 
9040Sstevel@tonic-gate 	if (parent == 0 || parent >= NUM_TYPES ||
9050Sstevel@tonic-gate 	    child == 0 || child >= NUM_TYPES)
9060Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_INVALID_TYPE); /* invalid types */
9070Sstevel@tonic-gate 
9080Sstevel@tonic-gate 	for (idx = 0; idx < MAX_VALID_CHILDREN; idx++) {
9090Sstevel@tonic-gate 		type = rc_types[parent].rt_valid_children[idx];
9100Sstevel@tonic-gate 		if (type == child)
9110Sstevel@tonic-gate 			return (REP_PROTOCOL_SUCCESS);
9120Sstevel@tonic-gate 	}
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate 	return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
9150Sstevel@tonic-gate }
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate /*
9180Sstevel@tonic-gate  * Fails with
9190Sstevel@tonic-gate  *   _INVALID_TYPE - type is invalid
9200Sstevel@tonic-gate  *   _BAD_REQUEST - name is an invalid name for a node of type type
9210Sstevel@tonic-gate  */
9220Sstevel@tonic-gate int
rc_check_type_name(uint32_t type,const char * name)9230Sstevel@tonic-gate rc_check_type_name(uint32_t type, const char *name)
9240Sstevel@tonic-gate {
9250Sstevel@tonic-gate 	if (type == 0 || type >= NUM_TYPES)
9260Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_INVALID_TYPE); /* invalid types */
9270Sstevel@tonic-gate 
9280Sstevel@tonic-gate 	if (uu_check_name(name, rc_types[type].rt_name_flags) == -1)
9290Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
9300Sstevel@tonic-gate 
9310Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
9320Sstevel@tonic-gate }
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate static int
rc_check_pgtype_name(const char * name)9350Sstevel@tonic-gate rc_check_pgtype_name(const char *name)
9360Sstevel@tonic-gate {
9370Sstevel@tonic-gate 	if (uu_check_name(name, UU_NAME_DOMAIN) == -1)
9380Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
9410Sstevel@tonic-gate }
9420Sstevel@tonic-gate 
9435777Stw21770 /*
9445777Stw21770  * rc_node_free_fmri should be called whenever a node loses its parent.
9455777Stw21770  * The reason is that the node's fmri string is built up by concatenating
9465777Stw21770  * its name to the parent's fmri.  Thus, when the node no longer has a
9475777Stw21770  * parent, its fmri is no longer valid.
9485777Stw21770  */
9495777Stw21770 static void
rc_node_free_fmri(rc_node_t * np)9505777Stw21770 rc_node_free_fmri(rc_node_t *np)
9515777Stw21770 {
9525777Stw21770 	if (np->rn_fmri != NULL) {
9535777Stw21770 		free((void *)np->rn_fmri);
9545777Stw21770 		np->rn_fmri = NULL;
9555777Stw21770 	}
9565777Stw21770 }
9575777Stw21770 
9585777Stw21770 /*
9595777Stw21770  * Concatenate the appropriate separator and the FMRI element to the base
9605777Stw21770  * FMRI string at fmri.
9615777Stw21770  *
9625777Stw21770  * Fails with
9635777Stw21770  *	_TRUNCATED	Not enough room in buffer at fmri.
9645777Stw21770  */
9655777Stw21770 static int
rc_concat_fmri_element(char * fmri,size_t bufsize,size_t * sz_out,const char * element,rep_protocol_entity_t type)9665777Stw21770 rc_concat_fmri_element(
9675777Stw21770 	char *fmri,			/* base fmri */
9685777Stw21770 	size_t bufsize,			/* size of buf at fmri */
9695777Stw21770 	size_t *sz_out,			/* receives result size. */
9705777Stw21770 	const char *element,		/* element name to concat */
9715777Stw21770 	rep_protocol_entity_t type)	/* type of element */
9725777Stw21770 {
9735777Stw21770 	size_t actual;
9745777Stw21770 	const char *name = element;
9755777Stw21770 	int rc;
9765777Stw21770 	const char *separator;
9775777Stw21770 
9785777Stw21770 	if (bufsize > 0)
9795777Stw21770 		*sz_out = strlen(fmri);
9805777Stw21770 	else
9815777Stw21770 		*sz_out = 0;
9825777Stw21770 
9835777Stw21770 	switch (type) {
9845777Stw21770 	case REP_PROTOCOL_ENTITY_SCOPE:
9855777Stw21770 		if (strcmp(element, SCF_FMRI_LOCAL_SCOPE) == 0) {
9865777Stw21770 			/*
9875777Stw21770 			 * No need to display scope information if we are
9885777Stw21770 			 * in the local scope.
9895777Stw21770 			 */
9905777Stw21770 			separator = SCF_FMRI_SVC_PREFIX;
9915777Stw21770 			name = NULL;
9925777Stw21770 		} else {
9935777Stw21770 			/*
9945777Stw21770 			 * Need to display scope information, because it is
9955777Stw21770 			 * not the local scope.
9965777Stw21770 			 */
9975777Stw21770 			separator = SCF_FMRI_SVC_PREFIX SCF_FMRI_SCOPE_PREFIX;
9985777Stw21770 		}
9995777Stw21770 		break;
10005777Stw21770 	case REP_PROTOCOL_ENTITY_SERVICE:
10015777Stw21770 		separator = SCF_FMRI_SERVICE_PREFIX;
10025777Stw21770 		break;
10035777Stw21770 	case REP_PROTOCOL_ENTITY_INSTANCE:
10045777Stw21770 		separator = SCF_FMRI_INSTANCE_PREFIX;
10055777Stw21770 		break;
10065777Stw21770 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
10075777Stw21770 	case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
10085777Stw21770 		separator = SCF_FMRI_PROPERTYGRP_PREFIX;
10095777Stw21770 		break;
10105777Stw21770 	case REP_PROTOCOL_ENTITY_PROPERTY:
10115777Stw21770 		separator = SCF_FMRI_PROPERTY_PREFIX;
10125777Stw21770 		break;
10135777Stw21770 	case REP_PROTOCOL_ENTITY_VALUE:
10145777Stw21770 		/*
10155777Stw21770 		 * A value does not have a separate FMRI from its property,
10165777Stw21770 		 * so there is nothing to concat.
10175777Stw21770 		 */
10185777Stw21770 		return (REP_PROTOCOL_SUCCESS);
10195777Stw21770 	case REP_PROTOCOL_ENTITY_SNAPSHOT:
10205777Stw21770 	case REP_PROTOCOL_ENTITY_SNAPLEVEL:
10215777Stw21770 		/* Snapshots do not have FMRIs, so there is nothing to do. */
10225777Stw21770 		return (REP_PROTOCOL_SUCCESS);
10235777Stw21770 	default:
10245777Stw21770 		(void) fprintf(stderr, "%s:%d: Unknown protocol type %d.\n",
10255777Stw21770 		    __FILE__, __LINE__, type);
10265777Stw21770 		abort();	/* Missing a case in switch if we get here. */
10275777Stw21770 	}
10285777Stw21770 
10295777Stw21770 	/* Concatenate separator and element to the fmri buffer. */
10305777Stw21770 
10315777Stw21770 	actual = strlcat(fmri, separator, bufsize);
10325777Stw21770 	if (name != NULL) {
10335777Stw21770 		if (actual < bufsize) {
10345777Stw21770 			actual = strlcat(fmri, name, bufsize);
10355777Stw21770 		} else {
10365777Stw21770 			actual += strlen(name);
10375777Stw21770 		}
10385777Stw21770 	}
10395777Stw21770 	if (actual < bufsize) {
10405777Stw21770 		rc = REP_PROTOCOL_SUCCESS;
10415777Stw21770 	} else {
10425777Stw21770 		rc = REP_PROTOCOL_FAIL_TRUNCATED;
10435777Stw21770 	}
10445777Stw21770 	*sz_out = actual;
10455777Stw21770 	return (rc);
10465777Stw21770 }
10475777Stw21770 
10485777Stw21770 /*
10495777Stw21770  * Get the FMRI for the node at np.  The fmri will be placed in buf.  On
10505777Stw21770  * success sz_out will be set to the size of the fmri in buf.  If
10515777Stw21770  * REP_PROTOCOL_FAIL_TRUNCATED is returned, sz_out will be set to the size
10525777Stw21770  * of the buffer that would be required to avoid truncation.
10535777Stw21770  *
10545777Stw21770  * Fails with
10555777Stw21770  *	_TRUNCATED	not enough room in buf for the FMRI.
10565777Stw21770  */
10575777Stw21770 static int
rc_node_get_fmri_or_fragment(rc_node_t * np,char * buf,size_t bufsize,size_t * sz_out)10585777Stw21770 rc_node_get_fmri_or_fragment(rc_node_t *np, char *buf, size_t bufsize,
10595777Stw21770     size_t *sz_out)
10605777Stw21770 {
10615777Stw21770 	size_t fmri_len = 0;
10625777Stw21770 	int r;
10635777Stw21770 
10645777Stw21770 	if (bufsize > 0)
10655777Stw21770 		*buf = 0;
10665777Stw21770 	*sz_out = 0;
10675777Stw21770 
10685777Stw21770 	if (np->rn_fmri == NULL) {
10695777Stw21770 		/*
10705777Stw21770 		 * A NULL rn_fmri implies that this is a top level scope.
10715777Stw21770 		 * Child nodes will always have an rn_fmri established
10725777Stw21770 		 * because both rc_node_link_child() and
10735777Stw21770 		 * rc_node_relink_child() call rc_node_build_fmri().  In
10745777Stw21770 		 * this case, we'll just return our name preceded by the
10755777Stw21770 		 * appropriate FMRI decorations.
10765777Stw21770 		 */
10775777Stw21770 		assert(np->rn_parent == NULL);
10785777Stw21770 		r = rc_concat_fmri_element(buf, bufsize, &fmri_len, np->rn_name,
10795777Stw21770 		    np->rn_id.rl_type);
10805777Stw21770 		if (r != REP_PROTOCOL_SUCCESS)
10815777Stw21770 			return (r);
10825777Stw21770 	} else {
10835777Stw21770 		/* We have an fmri, so return it. */
10845777Stw21770 		fmri_len = strlcpy(buf, np->rn_fmri, bufsize);
10855777Stw21770 	}
10865777Stw21770 
10875777Stw21770 	*sz_out = fmri_len;
10885777Stw21770 
10895777Stw21770 	if (fmri_len >= bufsize)
10905777Stw21770 		return (REP_PROTOCOL_FAIL_TRUNCATED);
10915777Stw21770 
10925777Stw21770 	return (REP_PROTOCOL_SUCCESS);
10935777Stw21770 }
10945777Stw21770 
10955777Stw21770 /*
10965777Stw21770  * Build an FMRI string for this node and save it in rn_fmri.
10975777Stw21770  *
10985777Stw21770  * The basic strategy here is to get the fmri of our parent and then
10995777Stw21770  * concatenate the appropriate separator followed by our name.  If our name
11005777Stw21770  * is null, the resulting fmri will just be a copy of the parent fmri.
11015777Stw21770  * rc_node_build_fmri() should be called with the RC_NODE_USING_PARENT flag
11025777Stw21770  * set.  Also the rn_lock for this node should be held.
11035777Stw21770  *
11045777Stw21770  * Fails with
11055777Stw21770  *	_NO_RESOURCES	Could not allocate memory.
11065777Stw21770  */
11075777Stw21770 static int
rc_node_build_fmri(rc_node_t * np)11085777Stw21770 rc_node_build_fmri(rc_node_t *np)
11095777Stw21770 {
11105777Stw21770 	size_t actual;
11115777Stw21770 	char fmri[REP_PROTOCOL_FMRI_LEN];
11125777Stw21770 	int rc;
11135777Stw21770 	size_t	sz = REP_PROTOCOL_FMRI_LEN;
11145777Stw21770 
11155777Stw21770 	assert(MUTEX_HELD(&np->rn_lock));
11165777Stw21770 	assert(np->rn_flags & RC_NODE_USING_PARENT);
11175777Stw21770 
11185777Stw21770 	rc_node_free_fmri(np);
11195777Stw21770 
11205777Stw21770 	rc = rc_node_get_fmri_or_fragment(np->rn_parent, fmri, sz, &actual);
11215777Stw21770 	assert(rc == REP_PROTOCOL_SUCCESS);
11225777Stw21770 
11235777Stw21770 	if (np->rn_name != NULL) {
11245777Stw21770 		rc = rc_concat_fmri_element(fmri, sz, &actual, np->rn_name,
11255777Stw21770 		    np->rn_id.rl_type);
11265777Stw21770 		assert(rc == REP_PROTOCOL_SUCCESS);
11275777Stw21770 		np->rn_fmri = strdup(fmri);
11285777Stw21770 	} else {
11295777Stw21770 		np->rn_fmri = strdup(fmri);
11305777Stw21770 	}
11315777Stw21770 	if (np->rn_fmri == NULL) {
11325777Stw21770 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
11335777Stw21770 	} else {
11345777Stw21770 		rc = REP_PROTOCOL_SUCCESS;
11355777Stw21770 	}
11365777Stw21770 
11375777Stw21770 	return (rc);
11385777Stw21770 }
11395777Stw21770 
11405777Stw21770 /*
11415777Stw21770  * Get the FMRI of the node at np placing the result in fmri.  Then
11425777Stw21770  * concatenate the additional element to fmri.  The type variable indicates
11435777Stw21770  * the type of element, so that the appropriate separator can be
11445777Stw21770  * generated.  size is the number of bytes in the buffer at fmri, and
11455777Stw21770  * sz_out receives the size of the generated string.  If the result is
11465777Stw21770  * truncated, sz_out will receive the size of the buffer that would be
11475777Stw21770  * required to avoid truncation.
11485777Stw21770  *
11495777Stw21770  * Fails with
11505777Stw21770  *	_TRUNCATED	Not enough room in buffer at fmri.
11515777Stw21770  */
11525777Stw21770 static int
rc_get_fmri_and_concat(rc_node_t * np,char * fmri,size_t size,size_t * sz_out,const char * element,rep_protocol_entity_t type)11535777Stw21770 rc_get_fmri_and_concat(rc_node_t *np, char *fmri, size_t size, size_t *sz_out,
11545777Stw21770     const char *element, rep_protocol_entity_t type)
11555777Stw21770 {
11565777Stw21770 	int rc;
11575777Stw21770 
11585777Stw21770 	if ((rc = rc_node_get_fmri_or_fragment(np, fmri, size, sz_out)) !=
11595777Stw21770 	    REP_PROTOCOL_SUCCESS) {
11605777Stw21770 		return (rc);
11615777Stw21770 	}
11625777Stw21770 	if ((rc = rc_concat_fmri_element(fmri, size, sz_out, element, type)) !=
11635777Stw21770 	    REP_PROTOCOL_SUCCESS) {
11645777Stw21770 		return (rc);
11655777Stw21770 	}
11665777Stw21770 
11675777Stw21770 	return (REP_PROTOCOL_SUCCESS);
11685777Stw21770 }
11695777Stw21770 
11700Sstevel@tonic-gate static int
rc_notify_info_interested(rc_notify_info_t * rnip,rc_notify_t * np)11710Sstevel@tonic-gate rc_notify_info_interested(rc_notify_info_t *rnip, rc_notify_t *np)
11720Sstevel@tonic-gate {
11730Sstevel@tonic-gate 	rc_node_t *nnp = np->rcn_node;
11740Sstevel@tonic-gate 	int i;
11750Sstevel@tonic-gate 
11760Sstevel@tonic-gate 	assert(MUTEX_HELD(&rc_pg_notify_lock));
11770Sstevel@tonic-gate 
11780Sstevel@tonic-gate 	if (np->rcn_delete != NULL) {
11790Sstevel@tonic-gate 		assert(np->rcn_info == NULL && np->rcn_node == NULL);
11800Sstevel@tonic-gate 		return (1);		/* everyone likes deletes */
11810Sstevel@tonic-gate 	}
11820Sstevel@tonic-gate 	if (np->rcn_node == NULL) {
11830Sstevel@tonic-gate 		assert(np->rcn_info != NULL || np->rcn_delete != NULL);
11840Sstevel@tonic-gate 		return (0);
11850Sstevel@tonic-gate 	}
11860Sstevel@tonic-gate 	assert(np->rcn_info == NULL);
11870Sstevel@tonic-gate 
11880Sstevel@tonic-gate 	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
11890Sstevel@tonic-gate 		if (rnip->rni_namelist[i] != NULL) {
11900Sstevel@tonic-gate 			if (strcmp(nnp->rn_name, rnip->rni_namelist[i]) == 0)
11910Sstevel@tonic-gate 				return (1);
11920Sstevel@tonic-gate 		}
11930Sstevel@tonic-gate 		if (rnip->rni_typelist[i] != NULL) {
11940Sstevel@tonic-gate 			if (strcmp(nnp->rn_type, rnip->rni_typelist[i]) == 0)
11950Sstevel@tonic-gate 				return (1);
11960Sstevel@tonic-gate 		}
11970Sstevel@tonic-gate 	}
11980Sstevel@tonic-gate 	return (0);
11990Sstevel@tonic-gate }
12000Sstevel@tonic-gate 
12010Sstevel@tonic-gate static void
rc_notify_insert_node(rc_node_t * nnp)12020Sstevel@tonic-gate rc_notify_insert_node(rc_node_t *nnp)
12030Sstevel@tonic-gate {
12040Sstevel@tonic-gate 	rc_notify_t *np = &nnp->rn_notify;
12050Sstevel@tonic-gate 	rc_notify_info_t *nip;
12060Sstevel@tonic-gate 	int found = 0;
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate 	assert(np->rcn_info == NULL);
12090Sstevel@tonic-gate 
12100Sstevel@tonic-gate 	if (nnp->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
12110Sstevel@tonic-gate 		return;
12120Sstevel@tonic-gate 
12130Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
12140Sstevel@tonic-gate 	np->rcn_node = nnp;
12150Sstevel@tonic-gate 	for (nip = uu_list_first(rc_notify_info_list); nip != NULL;
12160Sstevel@tonic-gate 	    nip = uu_list_next(rc_notify_info_list, nip)) {
12170Sstevel@tonic-gate 		if (rc_notify_info_interested(nip, np)) {
12180Sstevel@tonic-gate 			(void) pthread_cond_broadcast(&nip->rni_cv);
12190Sstevel@tonic-gate 			found++;
12200Sstevel@tonic-gate 		}
12210Sstevel@tonic-gate 	}
12220Sstevel@tonic-gate 	if (found)
12230Sstevel@tonic-gate 		(void) uu_list_insert_before(rc_notify_list, NULL, np);
12240Sstevel@tonic-gate 	else
12250Sstevel@tonic-gate 		np->rcn_node = NULL;
12260Sstevel@tonic-gate 
12270Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
12280Sstevel@tonic-gate }
12290Sstevel@tonic-gate 
12300Sstevel@tonic-gate static void
rc_notify_deletion(rc_notify_delete_t * ndp,const char * service,const char * instance,const char * pg)12310Sstevel@tonic-gate rc_notify_deletion(rc_notify_delete_t *ndp, const char *service,
12320Sstevel@tonic-gate     const char *instance, const char *pg)
12330Sstevel@tonic-gate {
12340Sstevel@tonic-gate 	rc_notify_info_t *nip;
12350Sstevel@tonic-gate 
12360Sstevel@tonic-gate 	uu_list_node_init(&ndp->rnd_notify, &ndp->rnd_notify.rcn_list_node,
12370Sstevel@tonic-gate 	    rc_notify_pool);
12380Sstevel@tonic-gate 	ndp->rnd_notify.rcn_delete = ndp;
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate 	(void) snprintf(ndp->rnd_fmri, sizeof (ndp->rnd_fmri),
12410Sstevel@tonic-gate 	    "svc:/%s%s%s%s%s", service,
12420Sstevel@tonic-gate 	    (instance != NULL)? ":" : "", (instance != NULL)? instance : "",
12430Sstevel@tonic-gate 	    (pg != NULL)? "/:properties/" : "", (pg != NULL)? pg : "");
12440Sstevel@tonic-gate 
12450Sstevel@tonic-gate 	/*
12460Sstevel@tonic-gate 	 * add to notification list, notify watchers
12470Sstevel@tonic-gate 	 */
12480Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
12490Sstevel@tonic-gate 	for (nip = uu_list_first(rc_notify_info_list); nip != NULL;
12500Sstevel@tonic-gate 	    nip = uu_list_next(rc_notify_info_list, nip))
12510Sstevel@tonic-gate 		(void) pthread_cond_broadcast(&nip->rni_cv);
12520Sstevel@tonic-gate 	(void) uu_list_insert_before(rc_notify_list, NULL, ndp);
12530Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
12540Sstevel@tonic-gate }
12550Sstevel@tonic-gate 
12560Sstevel@tonic-gate static void
rc_notify_remove_node(rc_node_t * nnp)12570Sstevel@tonic-gate rc_notify_remove_node(rc_node_t *nnp)
12580Sstevel@tonic-gate {
12590Sstevel@tonic-gate 	rc_notify_t *np = &nnp->rn_notify;
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate 	assert(np->rcn_info == NULL);
12620Sstevel@tonic-gate 	assert(!MUTEX_HELD(&nnp->rn_lock));
12630Sstevel@tonic-gate 
12640Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
12650Sstevel@tonic-gate 	while (np->rcn_node != NULL) {
12660Sstevel@tonic-gate 		if (rc_notify_in_use) {
12670Sstevel@tonic-gate 			(void) pthread_cond_wait(&rc_pg_notify_cv,
12680Sstevel@tonic-gate 			    &rc_pg_notify_lock);
12690Sstevel@tonic-gate 			continue;
12700Sstevel@tonic-gate 		}
12710Sstevel@tonic-gate 		(void) uu_list_remove(rc_notify_list, np);
12720Sstevel@tonic-gate 		np->rcn_node = NULL;
12730Sstevel@tonic-gate 		break;
12740Sstevel@tonic-gate 	}
12750Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
12760Sstevel@tonic-gate }
12770Sstevel@tonic-gate 
12780Sstevel@tonic-gate static void
rc_notify_remove_locked(rc_notify_t * np)12790Sstevel@tonic-gate rc_notify_remove_locked(rc_notify_t *np)
12800Sstevel@tonic-gate {
12810Sstevel@tonic-gate 	assert(MUTEX_HELD(&rc_pg_notify_lock));
12820Sstevel@tonic-gate 	assert(rc_notify_in_use == 0);
12830Sstevel@tonic-gate 
12840Sstevel@tonic-gate 	(void) uu_list_remove(rc_notify_list, np);
12850Sstevel@tonic-gate 	if (np->rcn_node) {
12860Sstevel@tonic-gate 		np->rcn_node = NULL;
12870Sstevel@tonic-gate 	} else if (np->rcn_delete) {
12880Sstevel@tonic-gate 		uu_free(np->rcn_delete);
12890Sstevel@tonic-gate 	} else {
12900Sstevel@tonic-gate 		assert(0);	/* CAN'T HAPPEN */
12910Sstevel@tonic-gate 	}
12920Sstevel@tonic-gate }
12930Sstevel@tonic-gate 
12940Sstevel@tonic-gate /*
12950Sstevel@tonic-gate  * Permission checking functions.  See comment atop this file.
12960Sstevel@tonic-gate  */
12970Sstevel@tonic-gate #ifndef NATIVE_BUILD
12980Sstevel@tonic-gate static permcheck_t *
pc_create()12990Sstevel@tonic-gate pc_create()
13000Sstevel@tonic-gate {
13010Sstevel@tonic-gate 	permcheck_t *p;
13020Sstevel@tonic-gate 
13030Sstevel@tonic-gate 	p = uu_zalloc(sizeof (*p));
13040Sstevel@tonic-gate 	if (p == NULL)
13050Sstevel@tonic-gate 		return (NULL);
13060Sstevel@tonic-gate 	p->pc_bnum = 8;			/* Normal case will only have 2 elts. */
13070Sstevel@tonic-gate 	p->pc_buckets = uu_zalloc(sizeof (*p->pc_buckets) * p->pc_bnum);
13080Sstevel@tonic-gate 	if (p->pc_buckets == NULL) {
13090Sstevel@tonic-gate 		uu_free(p);
13100Sstevel@tonic-gate 		return (NULL);
13110Sstevel@tonic-gate 	}
13120Sstevel@tonic-gate 
13130Sstevel@tonic-gate 	p->pc_enum = 0;
13140Sstevel@tonic-gate 	return (p);
13150Sstevel@tonic-gate }
13160Sstevel@tonic-gate 
13170Sstevel@tonic-gate static void
pc_free(permcheck_t * pcp)13180Sstevel@tonic-gate pc_free(permcheck_t *pcp)
13190Sstevel@tonic-gate {
13200Sstevel@tonic-gate 	uint_t i;
13210Sstevel@tonic-gate 	struct pc_elt *ep, *next;
13220Sstevel@tonic-gate 
13230Sstevel@tonic-gate 	for (i = 0; i < pcp->pc_bnum; ++i) {
13240Sstevel@tonic-gate 		for (ep = pcp->pc_buckets[i]; ep != NULL; ep = next) {
13250Sstevel@tonic-gate 			next = ep->pce_next;
13260Sstevel@tonic-gate 			free(ep);
13270Sstevel@tonic-gate 		}
13280Sstevel@tonic-gate 	}
13290Sstevel@tonic-gate 
13300Sstevel@tonic-gate 	free(pcp->pc_buckets);
13310Sstevel@tonic-gate 	free(pcp);
13320Sstevel@tonic-gate }
13330Sstevel@tonic-gate 
13340Sstevel@tonic-gate static uint32_t
pc_hash(const char * auth)13350Sstevel@tonic-gate pc_hash(const char *auth)
13360Sstevel@tonic-gate {
13370Sstevel@tonic-gate 	uint32_t h = 0, g;
13380Sstevel@tonic-gate 	const char *p;
13390Sstevel@tonic-gate 
13400Sstevel@tonic-gate 	/*
13410Sstevel@tonic-gate 	 * Generic hash function from uts/common/os/modhash.c.
13420Sstevel@tonic-gate 	 */
13430Sstevel@tonic-gate 	for (p = auth; *p != '\0'; ++p) {
13440Sstevel@tonic-gate 		h = (h << 4) + *p;
13450Sstevel@tonic-gate 		g = (h & 0xf0000000);
13460Sstevel@tonic-gate 		if (g != 0) {
13470Sstevel@tonic-gate 			h ^= (g >> 24);
13480Sstevel@tonic-gate 			h ^= g;
13490Sstevel@tonic-gate 		}
13500Sstevel@tonic-gate 	}
13510Sstevel@tonic-gate 
13520Sstevel@tonic-gate 	return (h);
13530Sstevel@tonic-gate }
13540Sstevel@tonic-gate 
13558497SThomas.Whitten@Sun.COM static perm_status_t
pc_exists(permcheck_t * pcp,const char * auth)13565777Stw21770 pc_exists(permcheck_t *pcp, const char *auth)
13570Sstevel@tonic-gate {
13580Sstevel@tonic-gate 	uint32_t h;
13590Sstevel@tonic-gate 	struct pc_elt *ep;
13600Sstevel@tonic-gate 
13610Sstevel@tonic-gate 	h = pc_hash(auth);
13620Sstevel@tonic-gate 	for (ep = pcp->pc_buckets[h & (pcp->pc_bnum - 1)];
13630Sstevel@tonic-gate 	    ep != NULL;
13640Sstevel@tonic-gate 	    ep = ep->pce_next) {
13655777Stw21770 		if (strcmp(auth, ep->pce_auth) == 0) {
13665777Stw21770 			pcp->pc_auth_string = ep->pce_auth;
13678497SThomas.Whitten@Sun.COM 			return (PERM_GRANTED);
13685777Stw21770 		}
13690Sstevel@tonic-gate 	}
13700Sstevel@tonic-gate 
13718497SThomas.Whitten@Sun.COM 	return (PERM_DENIED);
13728497SThomas.Whitten@Sun.COM }
13738497SThomas.Whitten@Sun.COM 
13748497SThomas.Whitten@Sun.COM static perm_status_t
pc_match(permcheck_t * pcp,const char * pattern)13755777Stw21770 pc_match(permcheck_t *pcp, const char *pattern)
13760Sstevel@tonic-gate {
13770Sstevel@tonic-gate 	uint_t i;
13780Sstevel@tonic-gate 	struct pc_elt *ep;
13790Sstevel@tonic-gate 
13800Sstevel@tonic-gate 	for (i = 0; i < pcp->pc_bnum; ++i) {
13810Sstevel@tonic-gate 		for (ep = pcp->pc_buckets[i]; ep != NULL; ep = ep->pce_next) {
13825777Stw21770 			if (_auth_match(pattern, ep->pce_auth)) {
13835777Stw21770 				pcp->pc_auth_string = ep->pce_auth;
13848497SThomas.Whitten@Sun.COM 				return (PERM_GRANTED);
13855777Stw21770 			}
13860Sstevel@tonic-gate 		}
13870Sstevel@tonic-gate 	}
13880Sstevel@tonic-gate 
13898497SThomas.Whitten@Sun.COM 	return (PERM_DENIED);
13900Sstevel@tonic-gate }
13910Sstevel@tonic-gate 
13920Sstevel@tonic-gate static int
pc_grow(permcheck_t * pcp)13930Sstevel@tonic-gate pc_grow(permcheck_t *pcp)
13940Sstevel@tonic-gate {
13950Sstevel@tonic-gate 	uint_t new_bnum, i, j;
13960Sstevel@tonic-gate 	struct pc_elt **new_buckets;
13970Sstevel@tonic-gate 	struct pc_elt *ep, *next;
13980Sstevel@tonic-gate 
13990Sstevel@tonic-gate 	new_bnum = pcp->pc_bnum * 2;
14000Sstevel@tonic-gate 	if (new_bnum < pcp->pc_bnum)
14010Sstevel@tonic-gate 		/* Homey don't play that. */
14020Sstevel@tonic-gate 		return (-1);
14030Sstevel@tonic-gate 
14040Sstevel@tonic-gate 	new_buckets = uu_zalloc(sizeof (*new_buckets) * new_bnum);
14050Sstevel@tonic-gate 	if (new_buckets == NULL)
14060Sstevel@tonic-gate 		return (-1);
14070Sstevel@tonic-gate 
14080Sstevel@tonic-gate 	for (i = 0; i < pcp->pc_bnum; ++i) {
14090Sstevel@tonic-gate 		for (ep = pcp->pc_buckets[i]; ep != NULL; ep = next) {
14100Sstevel@tonic-gate 			next = ep->pce_next;
14110Sstevel@tonic-gate 			j = pc_hash(ep->pce_auth) & (new_bnum - 1);
14120Sstevel@tonic-gate 			ep->pce_next = new_buckets[j];
14130Sstevel@tonic-gate 			new_buckets[j] = ep;
14140Sstevel@tonic-gate 		}
14150Sstevel@tonic-gate 	}
14160Sstevel@tonic-gate 
14170Sstevel@tonic-gate 	uu_free(pcp->pc_buckets);
14180Sstevel@tonic-gate 	pcp->pc_buckets = new_buckets;
14190Sstevel@tonic-gate 	pcp->pc_bnum = new_bnum;
14200Sstevel@tonic-gate 
14210Sstevel@tonic-gate 	return (0);
14220Sstevel@tonic-gate }
14230Sstevel@tonic-gate 
14240Sstevel@tonic-gate static int
pc_add(permcheck_t * pcp,const char * auth,pc_auth_type_t auth_type)14255777Stw21770 pc_add(permcheck_t *pcp, const char *auth, pc_auth_type_t auth_type)
14260Sstevel@tonic-gate {
14270Sstevel@tonic-gate 	struct pc_elt *ep;
14280Sstevel@tonic-gate 	uint_t i;
14290Sstevel@tonic-gate 
14300Sstevel@tonic-gate 	ep = uu_zalloc(offsetof(struct pc_elt, pce_auth) + strlen(auth) + 1);
14310Sstevel@tonic-gate 	if (ep == NULL)
14320Sstevel@tonic-gate 		return (-1);
14330Sstevel@tonic-gate 
14340Sstevel@tonic-gate 	/* Grow if pc_enum / pc_bnum > 3/4. */
14350Sstevel@tonic-gate 	if (pcp->pc_enum * 4 > 3 * pcp->pc_bnum)
14360Sstevel@tonic-gate 		/* Failure is not a stopper; we'll try again next time. */
14370Sstevel@tonic-gate 		(void) pc_grow(pcp);
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate 	(void) strcpy(ep->pce_auth, auth);
14400Sstevel@tonic-gate 
14410Sstevel@tonic-gate 	i = pc_hash(auth) & (pcp->pc_bnum - 1);
14420Sstevel@tonic-gate 	ep->pce_next = pcp->pc_buckets[i];
14430Sstevel@tonic-gate 	pcp->pc_buckets[i] = ep;
14440Sstevel@tonic-gate 
14455777Stw21770 	if (auth_type > pcp->pc_specific_type) {
14465777Stw21770 		pcp->pc_specific_type = auth_type;
14475777Stw21770 		pcp->pc_specific = ep;
14485777Stw21770 	}
14495777Stw21770 
14500Sstevel@tonic-gate 	++pcp->pc_enum;
14510Sstevel@tonic-gate 
14520Sstevel@tonic-gate 	return (0);
14530Sstevel@tonic-gate }
14540Sstevel@tonic-gate 
14550Sstevel@tonic-gate /*
14560Sstevel@tonic-gate  * For the type of a property group, return the authorization which may be
14570Sstevel@tonic-gate  * used to modify it.
14580Sstevel@tonic-gate  */
14590Sstevel@tonic-gate static const char *
perm_auth_for_pgtype(const char * pgtype)14600Sstevel@tonic-gate perm_auth_for_pgtype(const char *pgtype)
14610Sstevel@tonic-gate {
14620Sstevel@tonic-gate 	if (strcmp(pgtype, SCF_GROUP_METHOD) == 0)
14630Sstevel@tonic-gate 		return (AUTH_MODIFY_PREFIX "method");
14640Sstevel@tonic-gate 	else if (strcmp(pgtype, SCF_GROUP_DEPENDENCY) == 0)
14650Sstevel@tonic-gate 		return (AUTH_MODIFY_PREFIX "dependency");
14660Sstevel@tonic-gate 	else if (strcmp(pgtype, SCF_GROUP_APPLICATION) == 0)
14670Sstevel@tonic-gate 		return (AUTH_MODIFY_PREFIX "application");
14680Sstevel@tonic-gate 	else if (strcmp(pgtype, SCF_GROUP_FRAMEWORK) == 0)
14690Sstevel@tonic-gate 		return (AUTH_MODIFY_PREFIX "framework");
14700Sstevel@tonic-gate 	else
14710Sstevel@tonic-gate 		return (NULL);
14720Sstevel@tonic-gate }
14730Sstevel@tonic-gate 
14740Sstevel@tonic-gate /*
14750Sstevel@tonic-gate  * Fails with
14760Sstevel@tonic-gate  *   _NO_RESOURCES - out of memory
14770Sstevel@tonic-gate  */
14780Sstevel@tonic-gate static int
perm_add_enabling_type(permcheck_t * pcp,const char * auth,pc_auth_type_t auth_type)14795777Stw21770 perm_add_enabling_type(permcheck_t *pcp, const char *auth,
14805777Stw21770     pc_auth_type_t auth_type)
14815777Stw21770 {
14825777Stw21770 	return (pc_add(pcp, auth, auth_type) == 0 ? REP_PROTOCOL_SUCCESS :
14835777Stw21770 	    REP_PROTOCOL_FAIL_NO_RESOURCES);
14845777Stw21770 }
14855777Stw21770 
14865777Stw21770 /*
14875777Stw21770  * Fails with
14885777Stw21770  *   _NO_RESOURCES - out of memory
14895777Stw21770  */
14905777Stw21770 static int
perm_add_enabling(permcheck_t * pcp,const char * auth)14910Sstevel@tonic-gate perm_add_enabling(permcheck_t *pcp, const char *auth)
14920Sstevel@tonic-gate {
14935777Stw21770 	return (perm_add_enabling_type(pcp, auth, PC_AUTH_SMF));
14940Sstevel@tonic-gate }
14950Sstevel@tonic-gate 
14960Sstevel@tonic-gate /* Note that perm_add_enabling_values() is defined below. */
14970Sstevel@tonic-gate 
14980Sstevel@tonic-gate /*
14998497SThomas.Whitten@Sun.COM  * perm_granted() returns PERM_GRANTED if the current door caller has one of
15008497SThomas.Whitten@Sun.COM  * the enabling authorizations in pcp, PERM_DENIED if it doesn't, PERM_GONE if
15018497SThomas.Whitten@Sun.COM  * the door client went away and PERM_FAIL if an error (usually lack of
150212273SCasper.Dik@Sun.COM  * memory) occurs.  auth_cb() checks each and every authorizations as
150312273SCasper.Dik@Sun.COM  * enumerated by _enum_auths.  When we find a result other than PERM_DENIED,
150412273SCasper.Dik@Sun.COM  * we short-cut the enumeration and return non-zero.
15050Sstevel@tonic-gate  */
150612273SCasper.Dik@Sun.COM 
150712273SCasper.Dik@Sun.COM static int
auth_cb(const char * auth,void * ctxt,void * vres)150812273SCasper.Dik@Sun.COM auth_cb(const char *auth, void *ctxt, void *vres)
150912273SCasper.Dik@Sun.COM {
151012273SCasper.Dik@Sun.COM 	permcheck_t *pcp = ctxt;
151112273SCasper.Dik@Sun.COM 	int *pret = vres;
151212273SCasper.Dik@Sun.COM 
151312273SCasper.Dik@Sun.COM 	if (strchr(auth, KV_WILDCHAR) == NULL)
151412273SCasper.Dik@Sun.COM 		*pret = pc_exists(pcp, auth);
151512273SCasper.Dik@Sun.COM 	else
151612273SCasper.Dik@Sun.COM 		*pret = pc_match(pcp, auth);
151712273SCasper.Dik@Sun.COM 
151812273SCasper.Dik@Sun.COM 	if (*pret != PERM_DENIED)
151912273SCasper.Dik@Sun.COM 		return (1);
15205777Stw21770 	/*
15215777Stw21770 	 * If we failed, choose the most specific auth string for use in
15225777Stw21770 	 * the audit event.
15235777Stw21770 	 */
15245777Stw21770 	assert(pcp->pc_specific != NULL);
15255777Stw21770 	pcp->pc_auth_string = pcp->pc_specific->pce_auth;
15265777Stw21770 
152712273SCasper.Dik@Sun.COM 	return (0);		/* Tells that we need to continue */
15280Sstevel@tonic-gate }
15290Sstevel@tonic-gate 
15308497SThomas.Whitten@Sun.COM static perm_status_t
perm_granted(permcheck_t * pcp)15315777Stw21770 perm_granted(permcheck_t *pcp)
15320Sstevel@tonic-gate {
15330Sstevel@tonic-gate 	ucred_t *uc;
15340Sstevel@tonic-gate 
15358497SThomas.Whitten@Sun.COM 	perm_status_t ret = PERM_DENIED;
15360Sstevel@tonic-gate 	uid_t uid;
15376059Sgww 	struct passwd pw;
15386059Sgww 	char pwbuf[1024];	/* XXX should be NSS_BUFLEN_PASSWD */
15390Sstevel@tonic-gate 
15400Sstevel@tonic-gate 	/* Get the uid */
15410Sstevel@tonic-gate 	if ((uc = get_ucred()) == NULL) {
15420Sstevel@tonic-gate 		if (errno == EINVAL) {
15430Sstevel@tonic-gate 			/*
15440Sstevel@tonic-gate 			 * Client is no longer waiting for our response (e.g.,
15450Sstevel@tonic-gate 			 * it received a signal & resumed with EINTR).
15460Sstevel@tonic-gate 			 * Punting with door_return() would be nice but we
15470Sstevel@tonic-gate 			 * need to release all of the locks & references we
15480Sstevel@tonic-gate 			 * hold.  And we must report failure to the client
15490Sstevel@tonic-gate 			 * layer to keep it from ignoring retries as
15500Sstevel@tonic-gate 			 * already-done (idempotency & all that).  None of the
15510Sstevel@tonic-gate 			 * error codes fit very well, so we might as well
15520Sstevel@tonic-gate 			 * force the return of _PERMISSION_DENIED since we
15530Sstevel@tonic-gate 			 * couldn't determine the user.
15540Sstevel@tonic-gate 			 */
15558497SThomas.Whitten@Sun.COM 			return (PERM_GONE);
15560Sstevel@tonic-gate 		}
15570Sstevel@tonic-gate 		assert(0);
15580Sstevel@tonic-gate 		abort();
15590Sstevel@tonic-gate 	}
15600Sstevel@tonic-gate 
15610Sstevel@tonic-gate 	uid = ucred_geteuid(uc);
15624321Scasper 	assert(uid != (uid_t)-1);
15630Sstevel@tonic-gate 
15646059Sgww 	if (getpwuid_r(uid, &pw, pwbuf, sizeof (pwbuf)) == NULL) {
15658497SThomas.Whitten@Sun.COM 		return (PERM_FAIL);
15666059Sgww 	}
15676059Sgww 
15686059Sgww 	/*
156912273SCasper.Dik@Sun.COM 	 * Enumerate all the auths defined for the user and return the
157012273SCasper.Dik@Sun.COM 	 * result in ret.
15716059Sgww 	 */
157212273SCasper.Dik@Sun.COM 	if (_enum_auths(pw.pw_name, auth_cb, pcp, &ret) < 0)
157312273SCasper.Dik@Sun.COM 		return (PERM_FAIL);
15740Sstevel@tonic-gate 
15750Sstevel@tonic-gate 	return (ret);
15760Sstevel@tonic-gate }
15778497SThomas.Whitten@Sun.COM 
15788497SThomas.Whitten@Sun.COM static int
map_granted_status(perm_status_t status,permcheck_t * pcp,char ** match_auth)15798497SThomas.Whitten@Sun.COM map_granted_status(perm_status_t status, permcheck_t *pcp,
15808497SThomas.Whitten@Sun.COM     char **match_auth)
15818497SThomas.Whitten@Sun.COM {
15828497SThomas.Whitten@Sun.COM 	int rc;
15838497SThomas.Whitten@Sun.COM 
15848497SThomas.Whitten@Sun.COM 	*match_auth = NULL;
15858497SThomas.Whitten@Sun.COM 	switch (status) {
15868497SThomas.Whitten@Sun.COM 	case PERM_DENIED:
15878497SThomas.Whitten@Sun.COM 		*match_auth = strdup(pcp->pc_auth_string);
15888497SThomas.Whitten@Sun.COM 		if (*match_auth == NULL)
15898497SThomas.Whitten@Sun.COM 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
15908497SThomas.Whitten@Sun.COM 		else
15918497SThomas.Whitten@Sun.COM 			rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
15928497SThomas.Whitten@Sun.COM 		break;
15938497SThomas.Whitten@Sun.COM 	case PERM_GRANTED:
15948497SThomas.Whitten@Sun.COM 		*match_auth = strdup(pcp->pc_auth_string);
15958497SThomas.Whitten@Sun.COM 		if (*match_auth == NULL)
15968497SThomas.Whitten@Sun.COM 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
15978497SThomas.Whitten@Sun.COM 		else
15988497SThomas.Whitten@Sun.COM 			rc = REP_PROTOCOL_SUCCESS;
15998497SThomas.Whitten@Sun.COM 		break;
16008497SThomas.Whitten@Sun.COM 	case PERM_GONE:
16018497SThomas.Whitten@Sun.COM 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
16028497SThomas.Whitten@Sun.COM 		break;
16038497SThomas.Whitten@Sun.COM 	case PERM_FAIL:
16048497SThomas.Whitten@Sun.COM 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
16058497SThomas.Whitten@Sun.COM 		break;
16068497SThomas.Whitten@Sun.COM 	}
16078497SThomas.Whitten@Sun.COM 	return (rc);
16088497SThomas.Whitten@Sun.COM }
16090Sstevel@tonic-gate #endif /* NATIVE_BUILD */
16100Sstevel@tonic-gate 
16110Sstevel@tonic-gate /*
16120Sstevel@tonic-gate  * flags in RC_NODE_WAITING_FLAGS are broadcast when unset, and are used to
16130Sstevel@tonic-gate  * serialize certain actions, and to wait for certain operations to complete
16140Sstevel@tonic-gate  *
16150Sstevel@tonic-gate  * The waiting flags are:
16160Sstevel@tonic-gate  *	RC_NODE_CHILDREN_CHANGING
16170Sstevel@tonic-gate  *		The child list is being built or changed (due to creation
16180Sstevel@tonic-gate  *		or deletion).  All iterators pause.
16190Sstevel@tonic-gate  *
16200Sstevel@tonic-gate  *	RC_NODE_USING_PARENT
16210Sstevel@tonic-gate  *		Someone is actively using the parent pointer, so we can't
16220Sstevel@tonic-gate  *		be removed from the parent list.
16230Sstevel@tonic-gate  *
16240Sstevel@tonic-gate  *	RC_NODE_CREATING_CHILD
16250Sstevel@tonic-gate  *		A child is being created -- locks out other creations, to
16260Sstevel@tonic-gate  *		prevent insert-insert races.
16270Sstevel@tonic-gate  *
16280Sstevel@tonic-gate  *	RC_NODE_IN_TX
16290Sstevel@tonic-gate  *		This object is running a transaction.
16300Sstevel@tonic-gate  *
16310Sstevel@tonic-gate  *	RC_NODE_DYING
16320Sstevel@tonic-gate  *		This node might be dying.  Always set as a set, using
16330Sstevel@tonic-gate  *		RC_NODE_DYING_FLAGS (which is everything but
16340Sstevel@tonic-gate  *		RC_NODE_USING_PARENT)
16350Sstevel@tonic-gate  */
16360Sstevel@tonic-gate static int
rc_node_hold_flag(rc_node_t * np,uint32_t flag)16370Sstevel@tonic-gate rc_node_hold_flag(rc_node_t *np, uint32_t flag)
16380Sstevel@tonic-gate {
16390Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
16400Sstevel@tonic-gate 	assert((flag & ~RC_NODE_WAITING_FLAGS) == 0);
16410Sstevel@tonic-gate 
16420Sstevel@tonic-gate 	while (!(np->rn_flags & RC_NODE_DEAD) && (np->rn_flags & flag)) {
16430Sstevel@tonic-gate 		(void) pthread_cond_wait(&np->rn_cv, &np->rn_lock);
16440Sstevel@tonic-gate 	}
16450Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_DEAD)
16460Sstevel@tonic-gate 		return (0);
16470Sstevel@tonic-gate 
16480Sstevel@tonic-gate 	np->rn_flags |= flag;
16490Sstevel@tonic-gate 	return (1);
16500Sstevel@tonic-gate }
16510Sstevel@tonic-gate 
16520Sstevel@tonic-gate static void
rc_node_rele_flag(rc_node_t * np,uint32_t flag)16530Sstevel@tonic-gate rc_node_rele_flag(rc_node_t *np, uint32_t flag)
16540Sstevel@tonic-gate {
16550Sstevel@tonic-gate 	assert((flag & ~RC_NODE_WAITING_FLAGS) == 0);
16560Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
16570Sstevel@tonic-gate 	assert((np->rn_flags & flag) == flag);
16580Sstevel@tonic-gate 	np->rn_flags &= ~flag;
16590Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&np->rn_cv);
16600Sstevel@tonic-gate }
16610Sstevel@tonic-gate 
16620Sstevel@tonic-gate /*
16630Sstevel@tonic-gate  * wait until a particular flag has cleared.  Fails if the object dies.
16640Sstevel@tonic-gate  */
16650Sstevel@tonic-gate static int
rc_node_wait_flag(rc_node_t * np,uint32_t flag)16660Sstevel@tonic-gate rc_node_wait_flag(rc_node_t *np, uint32_t flag)
16670Sstevel@tonic-gate {
16680Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
16690Sstevel@tonic-gate 	while (!(np->rn_flags & RC_NODE_DEAD) && (np->rn_flags & flag))
16700Sstevel@tonic-gate 		(void) pthread_cond_wait(&np->rn_cv, &np->rn_lock);
16710Sstevel@tonic-gate 
16720Sstevel@tonic-gate 	return (!(np->rn_flags & RC_NODE_DEAD));
16730Sstevel@tonic-gate }
16740Sstevel@tonic-gate 
16750Sstevel@tonic-gate /*
16760Sstevel@tonic-gate  * On entry, np's lock must be held, and this thread must be holding
16770Sstevel@tonic-gate  * RC_NODE_USING_PARENT.  On return, both of them are released.
16780Sstevel@tonic-gate  *
16790Sstevel@tonic-gate  * If the return value is NULL, np either does not have a parent, or
16800Sstevel@tonic-gate  * the parent has been marked DEAD.
16810Sstevel@tonic-gate  *
16820Sstevel@tonic-gate  * If the return value is non-NULL, it is the parent of np, and both
16830Sstevel@tonic-gate  * its lock and the requested flags are held.
16840Sstevel@tonic-gate  */
16850Sstevel@tonic-gate static rc_node_t *
rc_node_hold_parent_flag(rc_node_t * np,uint32_t flag)16860Sstevel@tonic-gate rc_node_hold_parent_flag(rc_node_t *np, uint32_t flag)
16870Sstevel@tonic-gate {
16880Sstevel@tonic-gate 	rc_node_t *pp;
16890Sstevel@tonic-gate 
16900Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
16910Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_USING_PARENT);
16920Sstevel@tonic-gate 
16930Sstevel@tonic-gate 	if ((pp = np->rn_parent) == NULL) {
16940Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_USING_PARENT);
16950Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
16960Sstevel@tonic-gate 		return (NULL);
16970Sstevel@tonic-gate 	}
16980Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
16990Sstevel@tonic-gate 
17000Sstevel@tonic-gate 	(void) pthread_mutex_lock(&pp->rn_lock);
17010Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
17020Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_USING_PARENT);
17030Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
17040Sstevel@tonic-gate 
17050Sstevel@tonic-gate 	if (!rc_node_hold_flag(pp, flag)) {
17060Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&pp->rn_lock);
17070Sstevel@tonic-gate 		return (NULL);
17080Sstevel@tonic-gate 	}
17090Sstevel@tonic-gate 	return (pp);
17100Sstevel@tonic-gate }
17110Sstevel@tonic-gate 
17120Sstevel@tonic-gate rc_node_t *
rc_node_alloc(void)17130Sstevel@tonic-gate rc_node_alloc(void)
17140Sstevel@tonic-gate {
17150Sstevel@tonic-gate 	rc_node_t *np = uu_zalloc(sizeof (*np));
17160Sstevel@tonic-gate 
17170Sstevel@tonic-gate 	if (np == NULL)
17180Sstevel@tonic-gate 		return (NULL);
17190Sstevel@tonic-gate 
17200Sstevel@tonic-gate 	(void) pthread_mutex_init(&np->rn_lock, NULL);
17210Sstevel@tonic-gate 	(void) pthread_cond_init(&np->rn_cv, NULL);
17220Sstevel@tonic-gate 
17230Sstevel@tonic-gate 	np->rn_children = uu_list_create(rc_children_pool, np, 0);
17240Sstevel@tonic-gate 	np->rn_pg_notify_list = uu_list_create(rc_pg_notify_pool, np, 0);
17250Sstevel@tonic-gate 
17260Sstevel@tonic-gate 	uu_list_node_init(np, &np->rn_sibling_node, rc_children_pool);
17270Sstevel@tonic-gate 
17280Sstevel@tonic-gate 	uu_list_node_init(&np->rn_notify, &np->rn_notify.rcn_list_node,
17290Sstevel@tonic-gate 	    rc_notify_pool);
17300Sstevel@tonic-gate 
17310Sstevel@tonic-gate 	return (np);
17320Sstevel@tonic-gate }
17330Sstevel@tonic-gate 
17340Sstevel@tonic-gate void
rc_node_destroy(rc_node_t * np)17350Sstevel@tonic-gate rc_node_destroy(rc_node_t *np)
17360Sstevel@tonic-gate {
17370Sstevel@tonic-gate 	int i;
17380Sstevel@tonic-gate 
17390Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_UNREFED)
17400Sstevel@tonic-gate 		return;				/* being handled elsewhere */
17410Sstevel@tonic-gate 
17420Sstevel@tonic-gate 	assert(np->rn_refs == 0 && np->rn_other_refs == 0);
17430Sstevel@tonic-gate 	assert(np->rn_former == NULL);
17440Sstevel@tonic-gate 
17450Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
17460Sstevel@tonic-gate 		/* Release the holds from rc_iter_next(). */
17470Sstevel@tonic-gate 		for (i = 0; i < COMPOSITION_DEPTH; ++i) {
17480Sstevel@tonic-gate 			/* rn_cchain[i] may be NULL for empty snapshots. */
17490Sstevel@tonic-gate 			if (np->rn_cchain[i] != NULL)
17500Sstevel@tonic-gate 				rc_node_rele(np->rn_cchain[i]);
17510Sstevel@tonic-gate 		}
17520Sstevel@tonic-gate 	}
17530Sstevel@tonic-gate 
17540Sstevel@tonic-gate 	if (np->rn_name != NULL)
17550Sstevel@tonic-gate 		free((void *)np->rn_name);
17560Sstevel@tonic-gate 	np->rn_name = NULL;
17570Sstevel@tonic-gate 	if (np->rn_type != NULL)
17580Sstevel@tonic-gate 		free((void *)np->rn_type);
17590Sstevel@tonic-gate 	np->rn_type = NULL;
17600Sstevel@tonic-gate 	if (np->rn_values != NULL)
17610Sstevel@tonic-gate 		object_free_values(np->rn_values, np->rn_valtype,
17620Sstevel@tonic-gate 		    np->rn_values_count, np->rn_values_size);
17630Sstevel@tonic-gate 	np->rn_values = NULL;
17645777Stw21770 	rc_node_free_fmri(np);
17650Sstevel@tonic-gate 
17660Sstevel@tonic-gate 	if (np->rn_snaplevel != NULL)
17670Sstevel@tonic-gate 		rc_snaplevel_rele(np->rn_snaplevel);
17680Sstevel@tonic-gate 	np->rn_snaplevel = NULL;
17690Sstevel@tonic-gate 
17700Sstevel@tonic-gate 	uu_list_node_fini(np, &np->rn_sibling_node, rc_children_pool);
17710Sstevel@tonic-gate 
17720Sstevel@tonic-gate 	uu_list_node_fini(&np->rn_notify, &np->rn_notify.rcn_list_node,
17730Sstevel@tonic-gate 	    rc_notify_pool);
17740Sstevel@tonic-gate 
17750Sstevel@tonic-gate 	assert(uu_list_first(np->rn_children) == NULL);
17760Sstevel@tonic-gate 	uu_list_destroy(np->rn_children);
17770Sstevel@tonic-gate 	uu_list_destroy(np->rn_pg_notify_list);
17780Sstevel@tonic-gate 
17790Sstevel@tonic-gate 	(void) pthread_mutex_destroy(&np->rn_lock);
17800Sstevel@tonic-gate 	(void) pthread_cond_destroy(&np->rn_cv);
17810Sstevel@tonic-gate 
17820Sstevel@tonic-gate 	uu_free(np);
17830Sstevel@tonic-gate }
17840Sstevel@tonic-gate 
17850Sstevel@tonic-gate /*
17860Sstevel@tonic-gate  * Link in a child node.
17870Sstevel@tonic-gate  *
17880Sstevel@tonic-gate  * Because of the lock ordering, cp has to already be in the hash table with
17890Sstevel@tonic-gate  * its lock dropped before we get it.  To prevent anyone from noticing that
17900Sstevel@tonic-gate  * it is parentless, the creation code sets the RC_NODE_USING_PARENT.  Once
17910Sstevel@tonic-gate  * we've linked it in, we release the flag.
17920Sstevel@tonic-gate  */
17930Sstevel@tonic-gate static void
rc_node_link_child(rc_node_t * np,rc_node_t * cp)17940Sstevel@tonic-gate rc_node_link_child(rc_node_t *np, rc_node_t *cp)
17950Sstevel@tonic-gate {
17960Sstevel@tonic-gate 	assert(!MUTEX_HELD(&np->rn_lock));
17970Sstevel@tonic-gate 	assert(!MUTEX_HELD(&cp->rn_lock));
17980Sstevel@tonic-gate 
17990Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
18000Sstevel@tonic-gate 	(void) pthread_mutex_lock(&cp->rn_lock);
18010Sstevel@tonic-gate 	assert(!(cp->rn_flags & RC_NODE_IN_PARENT) &&
18020Sstevel@tonic-gate 	    (cp->rn_flags & RC_NODE_USING_PARENT));
18030Sstevel@tonic-gate 
18040Sstevel@tonic-gate 	assert(rc_check_parent_child(np->rn_id.rl_type, cp->rn_id.rl_type) ==
18050Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS);
18060Sstevel@tonic-gate 
18070Sstevel@tonic-gate 	cp->rn_parent = np;
18080Sstevel@tonic-gate 	cp->rn_flags |= RC_NODE_IN_PARENT;
18090Sstevel@tonic-gate 	(void) uu_list_insert_before(np->rn_children, NULL, cp);
18105777Stw21770 	(void) rc_node_build_fmri(cp);
18110Sstevel@tonic-gate 
18120Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
18130Sstevel@tonic-gate 
18140Sstevel@tonic-gate 	rc_node_rele_flag(cp, RC_NODE_USING_PARENT);
18150Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&cp->rn_lock);
18160Sstevel@tonic-gate }
18170Sstevel@tonic-gate 
18180Sstevel@tonic-gate /*
18190Sstevel@tonic-gate  * Sets the rn_parent_ref field of all the children of np to pp -- always
18200Sstevel@tonic-gate  * initially invoked as rc_node_setup_parent_ref(np, np), we then recurse.
18210Sstevel@tonic-gate  *
18220Sstevel@tonic-gate  * This is used when we mark a node RC_NODE_OLD, so that when the object and
18230Sstevel@tonic-gate  * its children are no longer referenced, they will all be deleted as a unit.
18240Sstevel@tonic-gate  */
18250Sstevel@tonic-gate static void
rc_node_setup_parent_ref(rc_node_t * np,rc_node_t * pp)18260Sstevel@tonic-gate rc_node_setup_parent_ref(rc_node_t *np, rc_node_t *pp)
18270Sstevel@tonic-gate {
18280Sstevel@tonic-gate 	rc_node_t *cp;
18290Sstevel@tonic-gate 
18300Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
18310Sstevel@tonic-gate 
18320Sstevel@tonic-gate 	for (cp = uu_list_first(np->rn_children); cp != NULL;
18330Sstevel@tonic-gate 	    cp = uu_list_next(np->rn_children, cp)) {
18340Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
18350Sstevel@tonic-gate 		if (cp->rn_flags & RC_NODE_PARENT_REF) {
18360Sstevel@tonic-gate 			assert(cp->rn_parent_ref == pp);
18370Sstevel@tonic-gate 		} else {
18380Sstevel@tonic-gate 			assert(cp->rn_parent_ref == NULL);
18390Sstevel@tonic-gate 
18400Sstevel@tonic-gate 			cp->rn_flags |= RC_NODE_PARENT_REF;
18410Sstevel@tonic-gate 			cp->rn_parent_ref = pp;
18420Sstevel@tonic-gate 			if (cp->rn_refs != 0)
18430Sstevel@tonic-gate 				rc_node_hold_other(pp);
18440Sstevel@tonic-gate 		}
18450Sstevel@tonic-gate 		rc_node_setup_parent_ref(cp, pp);		/* recurse */
18460Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&cp->rn_lock);
18470Sstevel@tonic-gate 	}
18480Sstevel@tonic-gate }
18490Sstevel@tonic-gate 
18500Sstevel@tonic-gate /*
18510Sstevel@tonic-gate  * Atomically replace 'np' with 'newp', with a parent of 'pp'.
18520Sstevel@tonic-gate  *
18530Sstevel@tonic-gate  * Requirements:
18540Sstevel@tonic-gate  *	*no* node locks may be held.
18550Sstevel@tonic-gate  *	pp must be held with RC_NODE_CHILDREN_CHANGING
18560Sstevel@tonic-gate  *	newp and np must be held with RC_NODE_IN_TX
18570Sstevel@tonic-gate  *	np must be marked RC_NODE_IN_PARENT, newp must not be
18580Sstevel@tonic-gate  *	np must be marked RC_NODE_OLD
18590Sstevel@tonic-gate  *
18600Sstevel@tonic-gate  * Afterwards:
18610Sstevel@tonic-gate  *	pp's RC_NODE_CHILDREN_CHANGING is dropped
18620Sstevel@tonic-gate  *	newp and np's RC_NODE_IN_TX is dropped
18630Sstevel@tonic-gate  *	newp->rn_former = np;
18640Sstevel@tonic-gate  *	newp is RC_NODE_IN_PARENT, np is not.
18650Sstevel@tonic-gate  *	interested notify subscribers have been notified of newp's new status.
18660Sstevel@tonic-gate  */
18670Sstevel@tonic-gate static void
rc_node_relink_child(rc_node_t * pp,rc_node_t * np,rc_node_t * newp)18680Sstevel@tonic-gate rc_node_relink_child(rc_node_t *pp, rc_node_t *np, rc_node_t *newp)
18690Sstevel@tonic-gate {
18700Sstevel@tonic-gate 	cache_bucket_t *bp;
18710Sstevel@tonic-gate 	/*
18720Sstevel@tonic-gate 	 * First, swap np and nnp in the cache.  newp's RC_NODE_IN_TX flag
18730Sstevel@tonic-gate 	 * keeps rc_node_update() from seeing it until we are done.
18740Sstevel@tonic-gate 	 */
18750Sstevel@tonic-gate 	bp = cache_hold(newp->rn_hash);
18760Sstevel@tonic-gate 	cache_remove_unlocked(bp, np);
18770Sstevel@tonic-gate 	cache_insert_unlocked(bp, newp);
18780Sstevel@tonic-gate 	cache_release(bp);
18790Sstevel@tonic-gate 
18800Sstevel@tonic-gate 	/*
18810Sstevel@tonic-gate 	 * replace np with newp in pp's list, and attach it to newp's rn_former
18820Sstevel@tonic-gate 	 * link.
18830Sstevel@tonic-gate 	 */
18840Sstevel@tonic-gate 	(void) pthread_mutex_lock(&pp->rn_lock);
18850Sstevel@tonic-gate 	assert(pp->rn_flags & RC_NODE_CHILDREN_CHANGING);
18860Sstevel@tonic-gate 
18870Sstevel@tonic-gate 	(void) pthread_mutex_lock(&newp->rn_lock);
18880Sstevel@tonic-gate 	assert(!(newp->rn_flags & RC_NODE_IN_PARENT));
18890Sstevel@tonic-gate 	assert(newp->rn_flags & RC_NODE_IN_TX);
18900Sstevel@tonic-gate 
18910Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
18920Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_IN_PARENT);
18930Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_OLD);
18940Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_IN_TX);
18950Sstevel@tonic-gate 
18960Sstevel@tonic-gate 	newp->rn_parent = pp;
18970Sstevel@tonic-gate 	newp->rn_flags |= RC_NODE_IN_PARENT;
18980Sstevel@tonic-gate 
18990Sstevel@tonic-gate 	/*
19000Sstevel@tonic-gate 	 * Note that we carefully add newp before removing np -- this
19010Sstevel@tonic-gate 	 * keeps iterators on the list from missing us.
19020Sstevel@tonic-gate 	 */
19030Sstevel@tonic-gate 	(void) uu_list_insert_after(pp->rn_children, np, newp);
19045777Stw21770 	(void) rc_node_build_fmri(newp);
19050Sstevel@tonic-gate 	(void) uu_list_remove(pp->rn_children, np);
19060Sstevel@tonic-gate 
19070Sstevel@tonic-gate 	/*
19080Sstevel@tonic-gate 	 * re-set np
19090Sstevel@tonic-gate 	 */
19100Sstevel@tonic-gate 	newp->rn_former = np;
19110Sstevel@tonic-gate 	np->rn_parent = NULL;
19120Sstevel@tonic-gate 	np->rn_flags &= ~RC_NODE_IN_PARENT;
19130Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_ON_FORMER;
19140Sstevel@tonic-gate 
19150Sstevel@tonic-gate 	rc_notify_insert_node(newp);
19160Sstevel@tonic-gate 
19170Sstevel@tonic-gate 	rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
19180Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&pp->rn_lock);
19190Sstevel@tonic-gate 	rc_node_rele_flag(newp, RC_NODE_USING_PARENT | RC_NODE_IN_TX);
19200Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&newp->rn_lock);
19210Sstevel@tonic-gate 	rc_node_setup_parent_ref(np, np);
19220Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_IN_TX);
19230Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
19240Sstevel@tonic-gate }
19250Sstevel@tonic-gate 
19260Sstevel@tonic-gate /*
19270Sstevel@tonic-gate  * makes sure a node with lookup 'nip', name 'name', and parent 'pp' exists.
19280Sstevel@tonic-gate  * 'cp' is used (and returned) if the node does not yet exist.  If it does
19290Sstevel@tonic-gate  * exist, 'cp' is freed, and the existent node is returned instead.
19300Sstevel@tonic-gate  */
19310Sstevel@tonic-gate rc_node_t *
rc_node_setup(rc_node_t * cp,rc_node_lookup_t * nip,const char * name,rc_node_t * pp)19320Sstevel@tonic-gate rc_node_setup(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
19330Sstevel@tonic-gate     rc_node_t *pp)
19340Sstevel@tonic-gate {
19350Sstevel@tonic-gate 	rc_node_t *np;
19360Sstevel@tonic-gate 	cache_bucket_t *bp;
19370Sstevel@tonic-gate 	uint32_t h = rc_node_hash(nip);
19380Sstevel@tonic-gate 
19390Sstevel@tonic-gate 	assert(cp->rn_refs == 0);
19400Sstevel@tonic-gate 
19410Sstevel@tonic-gate 	bp = cache_hold(h);
19420Sstevel@tonic-gate 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
19430Sstevel@tonic-gate 		cache_release(bp);
19440Sstevel@tonic-gate 
19450Sstevel@tonic-gate 		/*
19460Sstevel@tonic-gate 		 * make sure it matches our expectations
19470Sstevel@tonic-gate 		 */
1948887Shg115875 		(void) pthread_mutex_lock(&np->rn_lock);
1949887Shg115875 		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
1950887Shg115875 			assert(np->rn_parent == pp);
1951887Shg115875 			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
1952887Shg115875 			assert(strcmp(np->rn_name, name) == 0);
1953887Shg115875 			assert(np->rn_type == NULL);
1954887Shg115875 			assert(np->rn_flags & RC_NODE_IN_PARENT);
1955887Shg115875 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
1956887Shg115875 		}
1957887Shg115875 		(void) pthread_mutex_unlock(&np->rn_lock);
19580Sstevel@tonic-gate 
19590Sstevel@tonic-gate 		rc_node_destroy(cp);
19600Sstevel@tonic-gate 		return (np);
19610Sstevel@tonic-gate 	}
19620Sstevel@tonic-gate 
19630Sstevel@tonic-gate 	/*
19647266Sbustos 	 * No one is there -- setup & install the new node.
19650Sstevel@tonic-gate 	 */
19660Sstevel@tonic-gate 	np = cp;
19670Sstevel@tonic-gate 	rc_node_hold(np);
19680Sstevel@tonic-gate 	np->rn_id = *nip;
19690Sstevel@tonic-gate 	np->rn_hash = h;
19700Sstevel@tonic-gate 	np->rn_name = strdup(name);
19710Sstevel@tonic-gate 
19720Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_USING_PARENT;
19730Sstevel@tonic-gate 
19740Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE) {
19750Sstevel@tonic-gate #if COMPOSITION_DEPTH == 2
19760Sstevel@tonic-gate 		np->rn_cchain[0] = np;
19770Sstevel@tonic-gate 		np->rn_cchain[1] = pp;
19780Sstevel@tonic-gate #else
19790Sstevel@tonic-gate #error This code must be updated.
19800Sstevel@tonic-gate #endif
19810Sstevel@tonic-gate 	}
19820Sstevel@tonic-gate 
19830Sstevel@tonic-gate 	cache_insert_unlocked(bp, np);
19840Sstevel@tonic-gate 	cache_release(bp);		/* we are now visible */
19850Sstevel@tonic-gate 
19860Sstevel@tonic-gate 	rc_node_link_child(pp, np);
19870Sstevel@tonic-gate 
19880Sstevel@tonic-gate 	return (np);
19890Sstevel@tonic-gate }
19900Sstevel@tonic-gate 
19910Sstevel@tonic-gate /*
19920Sstevel@tonic-gate  * makes sure a snapshot with lookup 'nip', name 'name', and parent 'pp' exists.
19930Sstevel@tonic-gate  * 'cp' is used (and returned) if the node does not yet exist.  If it does
19940Sstevel@tonic-gate  * exist, 'cp' is freed, and the existent node is returned instead.
19950Sstevel@tonic-gate  */
19960Sstevel@tonic-gate rc_node_t *
rc_node_setup_snapshot(rc_node_t * cp,rc_node_lookup_t * nip,const char * name,uint32_t snap_id,rc_node_t * pp)19970Sstevel@tonic-gate rc_node_setup_snapshot(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
19980Sstevel@tonic-gate     uint32_t snap_id, rc_node_t *pp)
19990Sstevel@tonic-gate {
20000Sstevel@tonic-gate 	rc_node_t *np;
20010Sstevel@tonic-gate 	cache_bucket_t *bp;
20020Sstevel@tonic-gate 	uint32_t h = rc_node_hash(nip);
20030Sstevel@tonic-gate 
20040Sstevel@tonic-gate 	assert(cp->rn_refs == 0);
20050Sstevel@tonic-gate 
20060Sstevel@tonic-gate 	bp = cache_hold(h);
20070Sstevel@tonic-gate 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
20080Sstevel@tonic-gate 		cache_release(bp);
20090Sstevel@tonic-gate 
20100Sstevel@tonic-gate 		/*
20110Sstevel@tonic-gate 		 * make sure it matches our expectations
20120Sstevel@tonic-gate 		 */
2013887Shg115875 		(void) pthread_mutex_lock(&np->rn_lock);
2014887Shg115875 		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
2015887Shg115875 			assert(np->rn_parent == pp);
2016887Shg115875 			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
2017887Shg115875 			assert(strcmp(np->rn_name, name) == 0);
2018887Shg115875 			assert(np->rn_type == NULL);
2019887Shg115875 			assert(np->rn_flags & RC_NODE_IN_PARENT);
2020887Shg115875 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
2021887Shg115875 		}
2022887Shg115875 		(void) pthread_mutex_unlock(&np->rn_lock);
20230Sstevel@tonic-gate 
20240Sstevel@tonic-gate 		rc_node_destroy(cp);
20250Sstevel@tonic-gate 		return (np);
20260Sstevel@tonic-gate 	}
20270Sstevel@tonic-gate 
20280Sstevel@tonic-gate 	/*
20290Sstevel@tonic-gate 	 * No one is there -- create a new node.
20300Sstevel@tonic-gate 	 */
20310Sstevel@tonic-gate 	np = cp;
20320Sstevel@tonic-gate 	rc_node_hold(np);
20330Sstevel@tonic-gate 	np->rn_id = *nip;
20340Sstevel@tonic-gate 	np->rn_hash = h;
20350Sstevel@tonic-gate 	np->rn_name = strdup(name);
20360Sstevel@tonic-gate 	np->rn_snapshot_id = snap_id;
20370Sstevel@tonic-gate 
20380Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_USING_PARENT;
20390Sstevel@tonic-gate 
20400Sstevel@tonic-gate 	cache_insert_unlocked(bp, np);
20410Sstevel@tonic-gate 	cache_release(bp);		/* we are now visible */
20420Sstevel@tonic-gate 
20430Sstevel@tonic-gate 	rc_node_link_child(pp, np);
20440Sstevel@tonic-gate 
20450Sstevel@tonic-gate 	return (np);
20460Sstevel@tonic-gate }
20470Sstevel@tonic-gate 
20480Sstevel@tonic-gate /*
20490Sstevel@tonic-gate  * makes sure a snaplevel with lookup 'nip' and parent 'pp' exists.  'cp' is
20500Sstevel@tonic-gate  * used (and returned) if the node does not yet exist.  If it does exist, 'cp'
20510Sstevel@tonic-gate  * is freed, and the existent node is returned instead.
20520Sstevel@tonic-gate  */
20530Sstevel@tonic-gate rc_node_t *
rc_node_setup_snaplevel(rc_node_t * cp,rc_node_lookup_t * nip,rc_snaplevel_t * lvl,rc_node_t * pp)20540Sstevel@tonic-gate rc_node_setup_snaplevel(rc_node_t *cp, rc_node_lookup_t *nip,
20550Sstevel@tonic-gate     rc_snaplevel_t *lvl, rc_node_t *pp)
20560Sstevel@tonic-gate {
20570Sstevel@tonic-gate 	rc_node_t *np;
20580Sstevel@tonic-gate 	cache_bucket_t *bp;
20590Sstevel@tonic-gate 	uint32_t h = rc_node_hash(nip);
20600Sstevel@tonic-gate 
20610Sstevel@tonic-gate 	assert(cp->rn_refs == 0);
20620Sstevel@tonic-gate 
20630Sstevel@tonic-gate 	bp = cache_hold(h);
20640Sstevel@tonic-gate 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
20650Sstevel@tonic-gate 		cache_release(bp);
20660Sstevel@tonic-gate 
20670Sstevel@tonic-gate 		/*
20680Sstevel@tonic-gate 		 * make sure it matches our expectations
20690Sstevel@tonic-gate 		 */
2070887Shg115875 		(void) pthread_mutex_lock(&np->rn_lock);
2071887Shg115875 		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
2072887Shg115875 			assert(np->rn_parent == pp);
2073887Shg115875 			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
2074887Shg115875 			assert(np->rn_name == NULL);
2075887Shg115875 			assert(np->rn_type == NULL);
2076887Shg115875 			assert(np->rn_flags & RC_NODE_IN_PARENT);
2077887Shg115875 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
2078887Shg115875 		}
2079887Shg115875 		(void) pthread_mutex_unlock(&np->rn_lock);
20800Sstevel@tonic-gate 
20810Sstevel@tonic-gate 		rc_node_destroy(cp);
20820Sstevel@tonic-gate 		return (np);
20830Sstevel@tonic-gate 	}
20840Sstevel@tonic-gate 
20850Sstevel@tonic-gate 	/*
20860Sstevel@tonic-gate 	 * No one is there -- create a new node.
20870Sstevel@tonic-gate 	 */
20880Sstevel@tonic-gate 	np = cp;
20890Sstevel@tonic-gate 	rc_node_hold(np);	/* released in snapshot_fill_children() */
20900Sstevel@tonic-gate 	np->rn_id = *nip;
20910Sstevel@tonic-gate 	np->rn_hash = h;
20920Sstevel@tonic-gate 
20930Sstevel@tonic-gate 	rc_snaplevel_hold(lvl);
20940Sstevel@tonic-gate 	np->rn_snaplevel = lvl;
20950Sstevel@tonic-gate 
20960Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_USING_PARENT;
20970Sstevel@tonic-gate 
20980Sstevel@tonic-gate 	cache_insert_unlocked(bp, np);
20990Sstevel@tonic-gate 	cache_release(bp);		/* we are now visible */
21000Sstevel@tonic-gate 
21010Sstevel@tonic-gate 	/* Add this snaplevel to the snapshot's composition chain. */
21020Sstevel@tonic-gate 	assert(pp->rn_cchain[lvl->rsl_level_num - 1] == NULL);
21030Sstevel@tonic-gate 	pp->rn_cchain[lvl->rsl_level_num - 1] = np;
21040Sstevel@tonic-gate 
21050Sstevel@tonic-gate 	rc_node_link_child(pp, np);
21060Sstevel@tonic-gate 
21070Sstevel@tonic-gate 	return (np);
21080Sstevel@tonic-gate }
21090Sstevel@tonic-gate 
21100Sstevel@tonic-gate /*
21110Sstevel@tonic-gate  * Returns NULL if strdup() fails.
21120Sstevel@tonic-gate  */
21130Sstevel@tonic-gate rc_node_t *
rc_node_setup_pg(rc_node_t * cp,rc_node_lookup_t * nip,const char * name,const char * type,uint32_t flags,uint32_t gen_id,rc_node_t * pp)21140Sstevel@tonic-gate rc_node_setup_pg(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
21150Sstevel@tonic-gate     const char *type, uint32_t flags, uint32_t gen_id, rc_node_t *pp)
21160Sstevel@tonic-gate {
21170Sstevel@tonic-gate 	rc_node_t *np;
21180Sstevel@tonic-gate 	cache_bucket_t *bp;
21190Sstevel@tonic-gate 
21200Sstevel@tonic-gate 	uint32_t h = rc_node_hash(nip);
21210Sstevel@tonic-gate 	bp = cache_hold(h);
21220Sstevel@tonic-gate 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
21230Sstevel@tonic-gate 		cache_release(bp);
21240Sstevel@tonic-gate 
21250Sstevel@tonic-gate 		/*
21260Sstevel@tonic-gate 		 * make sure it matches our expectations (don't check
21270Sstevel@tonic-gate 		 * the generation number or parent, since someone could
21280Sstevel@tonic-gate 		 * have gotten a transaction through while we weren't
21290Sstevel@tonic-gate 		 * looking)
21300Sstevel@tonic-gate 		 */
2131887Shg115875 		(void) pthread_mutex_lock(&np->rn_lock);
2132887Shg115875 		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
2133887Shg115875 			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
2134887Shg115875 			assert(strcmp(np->rn_name, name) == 0);
2135887Shg115875 			assert(strcmp(np->rn_type, type) == 0);
2136887Shg115875 			assert(np->rn_pgflags == flags);
2137887Shg115875 			assert(np->rn_flags & RC_NODE_IN_PARENT);
2138887Shg115875 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
2139887Shg115875 		}
2140887Shg115875 		(void) pthread_mutex_unlock(&np->rn_lock);
21410Sstevel@tonic-gate 
21420Sstevel@tonic-gate 		rc_node_destroy(cp);
21430Sstevel@tonic-gate 		return (np);
21440Sstevel@tonic-gate 	}
21450Sstevel@tonic-gate 
21460Sstevel@tonic-gate 	np = cp;
21470Sstevel@tonic-gate 	rc_node_hold(np);		/* released in fill_pg_callback() */
21480Sstevel@tonic-gate 	np->rn_id = *nip;
21490Sstevel@tonic-gate 	np->rn_hash = h;
21500Sstevel@tonic-gate 	np->rn_name = strdup(name);
21510Sstevel@tonic-gate 	if (np->rn_name == NULL) {
21520Sstevel@tonic-gate 		rc_node_rele(np);
21530Sstevel@tonic-gate 		return (NULL);
21540Sstevel@tonic-gate 	}
21550Sstevel@tonic-gate 	np->rn_type = strdup(type);
21560Sstevel@tonic-gate 	if (np->rn_type == NULL) {
21570Sstevel@tonic-gate 		free((void *)np->rn_name);
21580Sstevel@tonic-gate 		rc_node_rele(np);
21590Sstevel@tonic-gate 		return (NULL);
21600Sstevel@tonic-gate 	}
21610Sstevel@tonic-gate 	np->rn_pgflags = flags;
21620Sstevel@tonic-gate 	np->rn_gen_id = gen_id;
21630Sstevel@tonic-gate 
21640Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_USING_PARENT;
21650Sstevel@tonic-gate 
21660Sstevel@tonic-gate 	cache_insert_unlocked(bp, np);
21670Sstevel@tonic-gate 	cache_release(bp);		/* we are now visible */
21680Sstevel@tonic-gate 
21690Sstevel@tonic-gate 	rc_node_link_child(pp, np);
21700Sstevel@tonic-gate 
21710Sstevel@tonic-gate 	return (np);
21720Sstevel@tonic-gate }
21730Sstevel@tonic-gate 
21740Sstevel@tonic-gate #if COMPOSITION_DEPTH == 2
21750Sstevel@tonic-gate /*
21760Sstevel@tonic-gate  * Initialize a "composed property group" which represents the composition of
21770Sstevel@tonic-gate  * property groups pg1 & pg2.  It is ephemeral: once created & returned for an
21780Sstevel@tonic-gate  * ITER_READ request, keeping it out of cache_hash and any child lists
21790Sstevel@tonic-gate  * prevents it from being looked up.  Operations besides iteration are passed
21800Sstevel@tonic-gate  * through to pg1.
21810Sstevel@tonic-gate  *
21820Sstevel@tonic-gate  * pg1 & pg2 should be held before entering this function.  They will be
21830Sstevel@tonic-gate  * released in rc_node_destroy().
21840Sstevel@tonic-gate  */
21850Sstevel@tonic-gate static int
rc_node_setup_cpg(rc_node_t * cpg,rc_node_t * pg1,rc_node_t * pg2)21860Sstevel@tonic-gate rc_node_setup_cpg(rc_node_t *cpg, rc_node_t *pg1, rc_node_t *pg2)
21870Sstevel@tonic-gate {
21880Sstevel@tonic-gate 	if (strcmp(pg1->rn_type, pg2->rn_type) != 0)
21890Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
21900Sstevel@tonic-gate 
21910Sstevel@tonic-gate 	cpg->rn_id.rl_type = REP_PROTOCOL_ENTITY_CPROPERTYGRP;
21920Sstevel@tonic-gate 	cpg->rn_name = strdup(pg1->rn_name);
21930Sstevel@tonic-gate 	if (cpg->rn_name == NULL)
21940Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
21950Sstevel@tonic-gate 
21960Sstevel@tonic-gate 	cpg->rn_cchain[0] = pg1;
21970Sstevel@tonic-gate 	cpg->rn_cchain[1] = pg2;
21980Sstevel@tonic-gate 
21990Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
22000Sstevel@tonic-gate }
22010Sstevel@tonic-gate #else
22020Sstevel@tonic-gate #error This code must be updated.
22030Sstevel@tonic-gate #endif
22040Sstevel@tonic-gate 
22050Sstevel@tonic-gate /*
22060Sstevel@tonic-gate  * Fails with _NO_RESOURCES.
22070Sstevel@tonic-gate  */
22080Sstevel@tonic-gate int
rc_node_create_property(rc_node_t * pp,rc_node_lookup_t * nip,const char * name,rep_protocol_value_type_t type,const char * vals,size_t count,size_t size)22090Sstevel@tonic-gate rc_node_create_property(rc_node_t *pp, rc_node_lookup_t *nip,
22100Sstevel@tonic-gate     const char *name, rep_protocol_value_type_t type,
22110Sstevel@tonic-gate     const char *vals, size_t count, size_t size)
22120Sstevel@tonic-gate {
22130Sstevel@tonic-gate 	rc_node_t *np;
22140Sstevel@tonic-gate 	cache_bucket_t *bp;
22150Sstevel@tonic-gate 
22160Sstevel@tonic-gate 	uint32_t h = rc_node_hash(nip);
22170Sstevel@tonic-gate 	bp = cache_hold(h);
22180Sstevel@tonic-gate 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
22190Sstevel@tonic-gate 		cache_release(bp);
22200Sstevel@tonic-gate 		/*
22210Sstevel@tonic-gate 		 * make sure it matches our expectations
22220Sstevel@tonic-gate 		 */
22230Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
22240Sstevel@tonic-gate 		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
22250Sstevel@tonic-gate 			assert(np->rn_parent == pp);
22260Sstevel@tonic-gate 			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
22270Sstevel@tonic-gate 			assert(strcmp(np->rn_name, name) == 0);
22280Sstevel@tonic-gate 			assert(np->rn_valtype == type);
22290Sstevel@tonic-gate 			assert(np->rn_values_count == count);
22300Sstevel@tonic-gate 			assert(np->rn_values_size == size);
22310Sstevel@tonic-gate 			assert(vals == NULL ||
22320Sstevel@tonic-gate 			    memcmp(np->rn_values, vals, size) == 0);
22330Sstevel@tonic-gate 			assert(np->rn_flags & RC_NODE_IN_PARENT);
22340Sstevel@tonic-gate 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
22350Sstevel@tonic-gate 		}
22360Sstevel@tonic-gate 		rc_node_rele_locked(np);
22370Sstevel@tonic-gate 		object_free_values(vals, type, count, size);
22380Sstevel@tonic-gate 		return (REP_PROTOCOL_SUCCESS);
22390Sstevel@tonic-gate 	}
22400Sstevel@tonic-gate 
22410Sstevel@tonic-gate 	/*
22420Sstevel@tonic-gate 	 * No one is there -- create a new node.
22430Sstevel@tonic-gate 	 */
22440Sstevel@tonic-gate 	np = rc_node_alloc();
22450Sstevel@tonic-gate 	if (np == NULL) {
22460Sstevel@tonic-gate 		cache_release(bp);
22470Sstevel@tonic-gate 		object_free_values(vals, type, count, size);
22480Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
22490Sstevel@tonic-gate 	}
22500Sstevel@tonic-gate 	np->rn_id = *nip;
22510Sstevel@tonic-gate 	np->rn_hash = h;
22520Sstevel@tonic-gate 	np->rn_name = strdup(name);
22530Sstevel@tonic-gate 	if (np->rn_name == NULL) {
22540Sstevel@tonic-gate 		cache_release(bp);
22550Sstevel@tonic-gate 		object_free_values(vals, type, count, size);
22560Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
22570Sstevel@tonic-gate 	}
22580Sstevel@tonic-gate 
22590Sstevel@tonic-gate 	np->rn_valtype = type;
22600Sstevel@tonic-gate 	np->rn_values = vals;
22610Sstevel@tonic-gate 	np->rn_values_count = count;
22620Sstevel@tonic-gate 	np->rn_values_size = size;
22630Sstevel@tonic-gate 
22640Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_USING_PARENT;
22650Sstevel@tonic-gate 
22660Sstevel@tonic-gate 	cache_insert_unlocked(bp, np);
22670Sstevel@tonic-gate 	cache_release(bp);		/* we are now visible */
22680Sstevel@tonic-gate 
22690Sstevel@tonic-gate 	rc_node_link_child(pp, np);
22700Sstevel@tonic-gate 
22710Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
22720Sstevel@tonic-gate }
22730Sstevel@tonic-gate 
22745777Stw21770 /*
22755777Stw21770  * This function implements a decision table to determine the event ID for
22765777Stw21770  * changes to the enabled (SCF_PROPERTY_ENABLED) property.  The event ID is
22775777Stw21770  * determined by the value of the first property in the command specified
22785777Stw21770  * by cmd_no and the name of the property group.  Here is the decision
22795777Stw21770  * table:
22805777Stw21770  *
22815777Stw21770  *				Property Group Name
22825777Stw21770  *	Property	------------------------------------------
22835777Stw21770  *	Value		SCF_PG_GENERAL		SCF_PG_GENERAL_OVR
22845777Stw21770  *	--------	--------------		------------------
22855777Stw21770  *	"0"		ADT_smf_disable		ADT_smf_tmp_disable
22865777Stw21770  *	"1"		ADT_smf_enable		ADT_smf_tmp_enable
22875777Stw21770  *
22885777Stw21770  * This function is called by special_property_event through a function
22895777Stw21770  * pointer in the special_props_list array.
22905777Stw21770  *
22915777Stw21770  * Since the ADT_smf_* symbols may not be defined in the build machine's
22925777Stw21770  * include files, this function is not compiled when doing native builds.
22935777Stw21770  */
22945777Stw21770 #ifndef NATIVE_BUILD
22955777Stw21770 static int
general_enable_id(tx_commit_data_t * tx_data,size_t cmd_no,const char * pg,au_event_t * event_id)22965777Stw21770 general_enable_id(tx_commit_data_t *tx_data, size_t cmd_no, const char *pg,
22975777Stw21770     au_event_t *event_id)
22985777Stw21770 {
22995777Stw21770 	const char *value;
23005777Stw21770 	uint32_t nvalues;
23015777Stw21770 	int enable;
23025777Stw21770 
23035777Stw21770 	/*
23045777Stw21770 	 * First, check property value.
23055777Stw21770 	 */
23065777Stw21770 	if (tx_cmd_nvalues(tx_data, cmd_no, &nvalues) != REP_PROTOCOL_SUCCESS)
23075777Stw21770 		return (-1);
23085777Stw21770 	if (nvalues == 0)
23095777Stw21770 		return (-1);
23105777Stw21770 	if (tx_cmd_value(tx_data, cmd_no, 0, &value) != REP_PROTOCOL_SUCCESS)
23115777Stw21770 		return (-1);
23125777Stw21770 	if (strcmp(value, "0") == 0) {
23135777Stw21770 		enable = 0;
23145777Stw21770 	} else if (strcmp(value, "1") == 0) {
23155777Stw21770 		enable = 1;
23165777Stw21770 	} else {
23175777Stw21770 		return (-1);
23185777Stw21770 	}
23195777Stw21770 
23205777Stw21770 	/*
23215777Stw21770 	 * Now check property group name.
23225777Stw21770 	 */
23235777Stw21770 	if (strcmp(pg, SCF_PG_GENERAL) == 0) {
23245777Stw21770 		*event_id = enable ? ADT_smf_enable : ADT_smf_disable;
23255777Stw21770 		return (0);
23265777Stw21770 	} else if (strcmp(pg, SCF_PG_GENERAL_OVR) == 0) {
23275777Stw21770 		*event_id = enable ? ADT_smf_tmp_enable : ADT_smf_tmp_disable;
23285777Stw21770 		return (0);
23295777Stw21770 	}
23305777Stw21770 	return (-1);
23315777Stw21770 }
23325777Stw21770 #endif	/* NATIVE_BUILD */
23335777Stw21770 
23345777Stw21770 /*
23355777Stw21770  * This function compares two audit_special_prop_item_t structures
23365777Stw21770  * represented by item1 and item2.  It returns an integer greater than 0 if
23375777Stw21770  * item1 is greater than item2.  It returns 0 if they are equal and an
23385777Stw21770  * integer less than 0 if item1 is less than item2.  api_prop_name and
23395777Stw21770  * api_pg_name are the key fields for sorting.
23405777Stw21770  *
23415777Stw21770  * This function is suitable for calls to bsearch(3C) and qsort(3C).
23425777Stw21770  */
23435777Stw21770 static int
special_prop_compare(const void * item1,const void * item2)23445777Stw21770 special_prop_compare(const void *item1, const void *item2)
23455777Stw21770 {
23465777Stw21770 	const audit_special_prop_item_t *a = (audit_special_prop_item_t *)item1;
23475777Stw21770 	const audit_special_prop_item_t *b = (audit_special_prop_item_t *)item2;
23485777Stw21770 	int r;
23495777Stw21770 
23505777Stw21770 	r = strcmp(a->api_prop_name, b->api_prop_name);
23515777Stw21770 	if (r == 0) {
23525777Stw21770 		/*
23535777Stw21770 		 * Primary keys are the same, so check the secondary key.
23545777Stw21770 		 */
23555777Stw21770 		r = strcmp(a->api_pg_name, b->api_pg_name);
23565777Stw21770 	}
23575777Stw21770 	return (r);
23585777Stw21770 }
23595777Stw21770 
23600Sstevel@tonic-gate int
rc_node_init(void)23610Sstevel@tonic-gate rc_node_init(void)
23620Sstevel@tonic-gate {
23630Sstevel@tonic-gate 	rc_node_t *np;
23640Sstevel@tonic-gate 	cache_bucket_t *bp;
23650Sstevel@tonic-gate 
23660Sstevel@tonic-gate 	rc_children_pool = uu_list_pool_create("rc_children_pool",
23670Sstevel@tonic-gate 	    sizeof (rc_node_t), offsetof(rc_node_t, rn_sibling_node),
23680Sstevel@tonic-gate 	    NULL, UU_LIST_POOL_DEBUG);
23690Sstevel@tonic-gate 
23700Sstevel@tonic-gate 	rc_pg_notify_pool = uu_list_pool_create("rc_pg_notify_pool",
23710Sstevel@tonic-gate 	    sizeof (rc_node_pg_notify_t),
23720Sstevel@tonic-gate 	    offsetof(rc_node_pg_notify_t, rnpn_node),
23730Sstevel@tonic-gate 	    NULL, UU_LIST_POOL_DEBUG);
23740Sstevel@tonic-gate 
23750Sstevel@tonic-gate 	rc_notify_pool = uu_list_pool_create("rc_notify_pool",
23760Sstevel@tonic-gate 	    sizeof (rc_notify_t), offsetof(rc_notify_t, rcn_list_node),
23770Sstevel@tonic-gate 	    NULL, UU_LIST_POOL_DEBUG);
23780Sstevel@tonic-gate 
23790Sstevel@tonic-gate 	rc_notify_info_pool = uu_list_pool_create("rc_notify_info_pool",
23800Sstevel@tonic-gate 	    sizeof (rc_notify_info_t),
23810Sstevel@tonic-gate 	    offsetof(rc_notify_info_t, rni_list_node),
23820Sstevel@tonic-gate 	    NULL, UU_LIST_POOL_DEBUG);
23830Sstevel@tonic-gate 
23840Sstevel@tonic-gate 	if (rc_children_pool == NULL || rc_pg_notify_pool == NULL ||
23850Sstevel@tonic-gate 	    rc_notify_pool == NULL || rc_notify_info_pool == NULL)
23860Sstevel@tonic-gate 		uu_die("out of memory");
23870Sstevel@tonic-gate 
23880Sstevel@tonic-gate 	rc_notify_list = uu_list_create(rc_notify_pool,
23890Sstevel@tonic-gate 	    &rc_notify_list, 0);
23900Sstevel@tonic-gate 
23910Sstevel@tonic-gate 	rc_notify_info_list = uu_list_create(rc_notify_info_pool,
23920Sstevel@tonic-gate 	    &rc_notify_info_list, 0);
23930Sstevel@tonic-gate 
23940Sstevel@tonic-gate 	if (rc_notify_list == NULL || rc_notify_info_list == NULL)
23950Sstevel@tonic-gate 		uu_die("out of memory");
23960Sstevel@tonic-gate 
23975777Stw21770 	/*
23985777Stw21770 	 * Sort the special_props_list array so that it can be searched
23995777Stw21770 	 * with bsearch(3C).
24005777Stw21770 	 *
24015777Stw21770 	 * The special_props_list array is not compiled into the native
24025777Stw21770 	 * build code, so there is no need to call qsort if NATIVE_BUILD is
24035777Stw21770 	 * defined.
24045777Stw21770 	 */
24055777Stw21770 #ifndef	NATIVE_BUILD
24065777Stw21770 	qsort(special_props_list, SPECIAL_PROP_COUNT,
24075777Stw21770 	    sizeof (special_props_list[0]), special_prop_compare);
24085777Stw21770 #endif	/* NATIVE_BUILD */
24095777Stw21770 
24100Sstevel@tonic-gate 	if ((np = rc_node_alloc()) == NULL)
24110Sstevel@tonic-gate 		uu_die("out of memory");
24120Sstevel@tonic-gate 
24130Sstevel@tonic-gate 	rc_node_hold(np);
24140Sstevel@tonic-gate 	np->rn_id.rl_type = REP_PROTOCOL_ENTITY_SCOPE;
24150Sstevel@tonic-gate 	np->rn_id.rl_backend = BACKEND_TYPE_NORMAL;
24160Sstevel@tonic-gate 	np->rn_hash = rc_node_hash(&np->rn_id);
24170Sstevel@tonic-gate 	np->rn_name = "localhost";
24180Sstevel@tonic-gate 
24190Sstevel@tonic-gate 	bp = cache_hold(np->rn_hash);
24200Sstevel@tonic-gate 	cache_insert_unlocked(bp, np);
24210Sstevel@tonic-gate 	cache_release(bp);
24220Sstevel@tonic-gate 
24230Sstevel@tonic-gate 	rc_scope = np;
24240Sstevel@tonic-gate 	return (1);
24250Sstevel@tonic-gate }
24260Sstevel@tonic-gate 
24270Sstevel@tonic-gate /*
24280Sstevel@tonic-gate  * Fails with
24290Sstevel@tonic-gate  *   _INVALID_TYPE - type is invalid
24300Sstevel@tonic-gate  *   _TYPE_MISMATCH - np doesn't carry children of type type
24310Sstevel@tonic-gate  *   _DELETED - np has been deleted
24320Sstevel@tonic-gate  *   _NO_RESOURCES
24330Sstevel@tonic-gate  */
24340Sstevel@tonic-gate static int
rc_node_fill_children(rc_node_t * np,uint32_t type)24350Sstevel@tonic-gate rc_node_fill_children(rc_node_t *np, uint32_t type)
24360Sstevel@tonic-gate {
24370Sstevel@tonic-gate 	int rc;
24380Sstevel@tonic-gate 
24390Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
24400Sstevel@tonic-gate 
24410Sstevel@tonic-gate 	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
24420Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS)
24430Sstevel@tonic-gate 		return (rc);
24440Sstevel@tonic-gate 
24450Sstevel@tonic-gate 	if (!rc_node_hold_flag(np, RC_NODE_CHILDREN_CHANGING))
24460Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_DELETED);
24470Sstevel@tonic-gate 
24480Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_HAS_CHILDREN) {
24490Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_CHILDREN_CHANGING);
24500Sstevel@tonic-gate 		return (REP_PROTOCOL_SUCCESS);
24510Sstevel@tonic-gate 	}
24520Sstevel@tonic-gate 
24530Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
24540Sstevel@tonic-gate 	rc = object_fill_children(np);
24550Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
24560Sstevel@tonic-gate 
24570Sstevel@tonic-gate 	if (rc == REP_PROTOCOL_SUCCESS) {
24580Sstevel@tonic-gate 		np->rn_flags |= RC_NODE_HAS_CHILDREN;
24590Sstevel@tonic-gate 	}
24600Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_CHILDREN_CHANGING);
24610Sstevel@tonic-gate 
24620Sstevel@tonic-gate 	return (rc);
24630Sstevel@tonic-gate }
24640Sstevel@tonic-gate 
24650Sstevel@tonic-gate /*
24660Sstevel@tonic-gate  * Returns
24670Sstevel@tonic-gate  *   _INVALID_TYPE - type is invalid
24680Sstevel@tonic-gate  *   _TYPE_MISMATCH - np doesn't carry children of type type
24690Sstevel@tonic-gate  *   _DELETED - np has been deleted
24700Sstevel@tonic-gate  *   _NO_RESOURCES
24710Sstevel@tonic-gate  *   _SUCCESS - if *cpp is not NULL, it is held
24720Sstevel@tonic-gate  */
24730Sstevel@tonic-gate static int
rc_node_find_named_child(rc_node_t * np,const char * name,uint32_t type,rc_node_t ** cpp)24740Sstevel@tonic-gate rc_node_find_named_child(rc_node_t *np, const char *name, uint32_t type,
24750Sstevel@tonic-gate     rc_node_t **cpp)
24760Sstevel@tonic-gate {
24770Sstevel@tonic-gate 	int ret;
24780Sstevel@tonic-gate 	rc_node_t *cp;
24790Sstevel@tonic-gate 
24800Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
24810Sstevel@tonic-gate 	assert(np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP);
24820Sstevel@tonic-gate 
24830Sstevel@tonic-gate 	ret = rc_node_fill_children(np, type);
24840Sstevel@tonic-gate 	if (ret != REP_PROTOCOL_SUCCESS)
24850Sstevel@tonic-gate 		return (ret);
24860Sstevel@tonic-gate 
24870Sstevel@tonic-gate 	for (cp = uu_list_first(np->rn_children);
24880Sstevel@tonic-gate 	    cp != NULL;
24890Sstevel@tonic-gate 	    cp = uu_list_next(np->rn_children, cp)) {
24900Sstevel@tonic-gate 		if (cp->rn_id.rl_type == type && strcmp(cp->rn_name, name) == 0)
24910Sstevel@tonic-gate 			break;
24920Sstevel@tonic-gate 	}
24930Sstevel@tonic-gate 
24940Sstevel@tonic-gate 	if (cp != NULL)
24950Sstevel@tonic-gate 		rc_node_hold(cp);
24960Sstevel@tonic-gate 	*cpp = cp;
24970Sstevel@tonic-gate 
24980Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
24990Sstevel@tonic-gate }
25000Sstevel@tonic-gate 
25015040Swesolows static int rc_node_parent(rc_node_t *, rc_node_t **);
25025040Swesolows 
25035040Swesolows /*
25045040Swesolows  * Returns
25055040Swesolows  *   _INVALID_TYPE - type is invalid
25065040Swesolows  *   _DELETED - np or an ancestor has been deleted
25075040Swesolows  *   _NOT_FOUND - no ancestor of specified type exists
25085040Swesolows  *   _SUCCESS - *app is held
25095040Swesolows  */
25105040Swesolows static int
rc_node_find_ancestor(rc_node_t * np,uint32_t type,rc_node_t ** app)25115040Swesolows rc_node_find_ancestor(rc_node_t *np, uint32_t type, rc_node_t **app)
25125040Swesolows {
25135040Swesolows 	int ret;
25145040Swesolows 	rc_node_t *parent, *np_orig;
25155040Swesolows 
25165040Swesolows 	if (type >= REP_PROTOCOL_ENTITY_MAX)
25175040Swesolows 		return (REP_PROTOCOL_FAIL_INVALID_TYPE);
25185040Swesolows 
25195040Swesolows 	np_orig = np;
25205040Swesolows 
25215040Swesolows 	while (np->rn_id.rl_type > type) {
25225040Swesolows 		ret = rc_node_parent(np, &parent);
25235040Swesolows 		if (np != np_orig)
25245040Swesolows 			rc_node_rele(np);
25255040Swesolows 		if (ret != REP_PROTOCOL_SUCCESS)
25265040Swesolows 			return (ret);
25275040Swesolows 		np = parent;
25285040Swesolows 	}
25295040Swesolows 
25305040Swesolows 	if (np->rn_id.rl_type == type) {
25315040Swesolows 		*app = parent;
25325040Swesolows 		return (REP_PROTOCOL_SUCCESS);
25335040Swesolows 	}
25345040Swesolows 
25355040Swesolows 	return (REP_PROTOCOL_FAIL_NOT_FOUND);
25365040Swesolows }
25375040Swesolows 
25380Sstevel@tonic-gate #ifndef NATIVE_BUILD
25390Sstevel@tonic-gate /*
25400Sstevel@tonic-gate  * If the propname property exists in pg, and it is of type string, add its
25410Sstevel@tonic-gate  * values as authorizations to pcp.  pg must not be locked on entry, and it is
25420Sstevel@tonic-gate  * returned unlocked.  Returns
25430Sstevel@tonic-gate  *   _DELETED - pg was deleted
25440Sstevel@tonic-gate  *   _NO_RESOURCES
25450Sstevel@tonic-gate  *   _NOT_FOUND - pg has no property named propname
25460Sstevel@tonic-gate  *   _SUCCESS
25470Sstevel@tonic-gate  */
25480Sstevel@tonic-gate static int
perm_add_pg_prop_values(permcheck_t * pcp,rc_node_t * pg,const char * propname)25490Sstevel@tonic-gate perm_add_pg_prop_values(permcheck_t *pcp, rc_node_t *pg, const char *propname)
25500Sstevel@tonic-gate {
25510Sstevel@tonic-gate 	rc_node_t *prop;
25520Sstevel@tonic-gate 	int result;
25530Sstevel@tonic-gate 
25540Sstevel@tonic-gate 	uint_t count;
25550Sstevel@tonic-gate 	const char *cp;
25560Sstevel@tonic-gate 
25570Sstevel@tonic-gate 	assert(!MUTEX_HELD(&pg->rn_lock));
25580Sstevel@tonic-gate 	assert(pg->rn_id.rl_type == REP_PROTOCOL_ENTITY_PROPERTYGRP);
25590Sstevel@tonic-gate 
25600Sstevel@tonic-gate 	(void) pthread_mutex_lock(&pg->rn_lock);
25610Sstevel@tonic-gate 	result = rc_node_find_named_child(pg, propname,
25620Sstevel@tonic-gate 	    REP_PROTOCOL_ENTITY_PROPERTY, &prop);
25630Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&pg->rn_lock);
25640Sstevel@tonic-gate 	if (result != REP_PROTOCOL_SUCCESS) {
25650Sstevel@tonic-gate 		switch (result) {
25660Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_DELETED:
25670Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
25680Sstevel@tonic-gate 			return (result);
25690Sstevel@tonic-gate 
25700Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_INVALID_TYPE:
25710Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_TYPE_MISMATCH:
25720Sstevel@tonic-gate 		default:
25730Sstevel@tonic-gate 			bad_error("rc_node_find_named_child", result);
25740Sstevel@tonic-gate 		}
25750Sstevel@tonic-gate 	}
25760Sstevel@tonic-gate 
25770Sstevel@tonic-gate 	if (prop == NULL)
25780Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
25790Sstevel@tonic-gate 
25800Sstevel@tonic-gate 	/* rn_valtype is immutable, so no locking. */
25810Sstevel@tonic-gate 	if (prop->rn_valtype != REP_PROTOCOL_TYPE_STRING) {
25820Sstevel@tonic-gate 		rc_node_rele(prop);
25830Sstevel@tonic-gate 		return (REP_PROTOCOL_SUCCESS);
25840Sstevel@tonic-gate 	}
25850Sstevel@tonic-gate 
25860Sstevel@tonic-gate 	(void) pthread_mutex_lock(&prop->rn_lock);
25870Sstevel@tonic-gate 	for (count = prop->rn_values_count, cp = prop->rn_values;
25880Sstevel@tonic-gate 	    count > 0;
25890Sstevel@tonic-gate 	    --count) {
25905777Stw21770 		result = perm_add_enabling_type(pcp, cp,
25915777Stw21770 		    (pg->rn_id.rl_ids[ID_INSTANCE]) ? PC_AUTH_INST :
25925777Stw21770 		    PC_AUTH_SVC);
25930Sstevel@tonic-gate 		if (result != REP_PROTOCOL_SUCCESS)
25940Sstevel@tonic-gate 			break;
25950Sstevel@tonic-gate 
25960Sstevel@tonic-gate 		cp = strchr(cp, '\0') + 1;
25970Sstevel@tonic-gate 	}
25980Sstevel@tonic-gate 
25990Sstevel@tonic-gate 	rc_node_rele_locked(prop);
26000Sstevel@tonic-gate 
26010Sstevel@tonic-gate 	return (result);
26020Sstevel@tonic-gate }
26030Sstevel@tonic-gate 
26040Sstevel@tonic-gate /*
26050Sstevel@tonic-gate  * Assuming that ent is a service or instance node, if the pgname property
26060Sstevel@tonic-gate  * group has type pgtype, and it has a propname property with string type, add
26070Sstevel@tonic-gate  * its values as authorizations to pcp.  If pgtype is NULL, it is not checked.
26080Sstevel@tonic-gate  * Returns
26090Sstevel@tonic-gate  *   _SUCCESS
26100Sstevel@tonic-gate  *   _DELETED - ent was deleted
26110Sstevel@tonic-gate  *   _NO_RESOURCES - no resources
26120Sstevel@tonic-gate  *   _NOT_FOUND - ent does not have pgname pg or propname property
26130Sstevel@tonic-gate  */
26140Sstevel@tonic-gate static int
perm_add_ent_prop_values(permcheck_t * pcp,rc_node_t * ent,const char * pgname,const char * pgtype,const char * propname)26150Sstevel@tonic-gate perm_add_ent_prop_values(permcheck_t *pcp, rc_node_t *ent, const char *pgname,
26160Sstevel@tonic-gate     const char *pgtype, const char *propname)
26170Sstevel@tonic-gate {
26180Sstevel@tonic-gate 	int r;
26190Sstevel@tonic-gate 	rc_node_t *pg;
26200Sstevel@tonic-gate 
26210Sstevel@tonic-gate 	assert(!MUTEX_HELD(&ent->rn_lock));
26220Sstevel@tonic-gate 
26230Sstevel@tonic-gate 	(void) pthread_mutex_lock(&ent->rn_lock);
26240Sstevel@tonic-gate 	r = rc_node_find_named_child(ent, pgname,
26250Sstevel@tonic-gate 	    REP_PROTOCOL_ENTITY_PROPERTYGRP, &pg);
26260Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&ent->rn_lock);
26270Sstevel@tonic-gate 
26280Sstevel@tonic-gate 	switch (r) {
26290Sstevel@tonic-gate 	case REP_PROTOCOL_SUCCESS:
26300Sstevel@tonic-gate 		break;
26310Sstevel@tonic-gate 
26320Sstevel@tonic-gate 	case REP_PROTOCOL_FAIL_DELETED:
26330Sstevel@tonic-gate 	case REP_PROTOCOL_FAIL_NO_RESOURCES:
26340Sstevel@tonic-gate 		return (r);
26350Sstevel@tonic-gate 
26360Sstevel@tonic-gate 	default:
26370Sstevel@tonic-gate 		bad_error("rc_node_find_named_child", r);
26380Sstevel@tonic-gate 	}
26390Sstevel@tonic-gate 
26400Sstevel@tonic-gate 	if (pg == NULL)
26410Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
26420Sstevel@tonic-gate 
26430Sstevel@tonic-gate 	if (pgtype == NULL || strcmp(pg->rn_type, pgtype) == 0) {
26440Sstevel@tonic-gate 		r = perm_add_pg_prop_values(pcp, pg, propname);
26450Sstevel@tonic-gate 		switch (r) {
26460Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_DELETED:
26470Sstevel@tonic-gate 			r = REP_PROTOCOL_FAIL_NOT_FOUND;
26480Sstevel@tonic-gate 			break;
26490Sstevel@tonic-gate 
26500Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
26510Sstevel@tonic-gate 		case REP_PROTOCOL_SUCCESS:
26520Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_NOT_FOUND:
26530Sstevel@tonic-gate 			break;
26540Sstevel@tonic-gate 
26550Sstevel@tonic-gate 		default:
26560Sstevel@tonic-gate 			bad_error("perm_add_pg_prop_values", r);
26570Sstevel@tonic-gate 		}
26580Sstevel@tonic-gate 	}
26590Sstevel@tonic-gate 
26600Sstevel@tonic-gate 	rc_node_rele(pg);
26610Sstevel@tonic-gate 
26620Sstevel@tonic-gate 	return (r);
26630Sstevel@tonic-gate }
26640Sstevel@tonic-gate 
26650Sstevel@tonic-gate /*
26665040Swesolows  * If pg has a property named propname, and is string typed, add its values as
26670Sstevel@tonic-gate  * authorizations to pcp.  If pg has no such property, and its parent is an
26680Sstevel@tonic-gate  * instance, walk up to the service and try doing the same with the property
26690Sstevel@tonic-gate  * of the same name from the property group of the same name.  Returns
26700Sstevel@tonic-gate  *   _SUCCESS
26710Sstevel@tonic-gate  *   _NO_RESOURCES
26720Sstevel@tonic-gate  *   _DELETED - pg (or an ancestor) was deleted
26730Sstevel@tonic-gate  */
26740Sstevel@tonic-gate static int
perm_add_enabling_values(permcheck_t * pcp,rc_node_t * pg,const char * propname)26750Sstevel@tonic-gate perm_add_enabling_values(permcheck_t *pcp, rc_node_t *pg, const char *propname)
26760Sstevel@tonic-gate {
26770Sstevel@tonic-gate 	int r;
26785040Swesolows 	char pgname[REP_PROTOCOL_NAME_LEN + 1];
26795040Swesolows 	rc_node_t *svc;
26805040Swesolows 	size_t sz;
26810Sstevel@tonic-gate 
26820Sstevel@tonic-gate 	r = perm_add_pg_prop_values(pcp, pg, propname);
26830Sstevel@tonic-gate 
26845040Swesolows 	if (r != REP_PROTOCOL_FAIL_NOT_FOUND)
26855040Swesolows 		return (r);
26865040Swesolows 
26875040Swesolows 	assert(!MUTEX_HELD(&pg->rn_lock));
26885040Swesolows 
26895040Swesolows 	if (pg->rn_id.rl_ids[ID_INSTANCE] == 0)
26905040Swesolows 		return (REP_PROTOCOL_SUCCESS);
26915040Swesolows 
26925040Swesolows 	sz = strlcpy(pgname, pg->rn_name, sizeof (pgname));
26935040Swesolows 	assert(sz < sizeof (pgname));
26945040Swesolows 
26955040Swesolows 	/*
26965040Swesolows 	 * If pg is a child of an instance or snapshot, we want to compose the
26975040Swesolows 	 * authorization property with the service's (if it exists).  The
26985040Swesolows 	 * snapshot case applies only to read_authorization.  In all other
26995040Swesolows 	 * cases, the pg's parent will be the instance.
27005040Swesolows 	 */
27015040Swesolows 	r = rc_node_find_ancestor(pg, REP_PROTOCOL_ENTITY_SERVICE, &svc);
27025040Swesolows 	if (r != REP_PROTOCOL_SUCCESS) {
27035040Swesolows 		assert(r == REP_PROTOCOL_FAIL_DELETED);
27045040Swesolows 		return (r);
27055040Swesolows 	}
27065040Swesolows 	assert(svc->rn_id.rl_type == REP_PROTOCOL_ENTITY_SERVICE);
27075040Swesolows 
27085040Swesolows 	r = perm_add_ent_prop_values(pcp, svc, pgname, NULL, propname);
27095040Swesolows 
27105040Swesolows 	rc_node_rele(svc);
27115040Swesolows 
27125040Swesolows 	if (r == REP_PROTOCOL_FAIL_NOT_FOUND)
27135040Swesolows 		r = REP_PROTOCOL_SUCCESS;
27140Sstevel@tonic-gate 
27150Sstevel@tonic-gate 	return (r);
27160Sstevel@tonic-gate }
27170Sstevel@tonic-gate 
27180Sstevel@tonic-gate /*
27190Sstevel@tonic-gate  * Call perm_add_enabling_values() for the "action_authorization" property of
27200Sstevel@tonic-gate  * the "general" property group of inst.  Returns
27210Sstevel@tonic-gate  *   _DELETED - inst (or an ancestor) was deleted
27220Sstevel@tonic-gate  *   _NO_RESOURCES
27230Sstevel@tonic-gate  *   _SUCCESS
27240Sstevel@tonic-gate  */
27250Sstevel@tonic-gate static int
perm_add_inst_action_auth(permcheck_t * pcp,rc_node_t * inst)27260Sstevel@tonic-gate perm_add_inst_action_auth(permcheck_t *pcp, rc_node_t *inst)
27270Sstevel@tonic-gate {
27280Sstevel@tonic-gate 	int r;
27290Sstevel@tonic-gate 	rc_node_t *svc;
27300Sstevel@tonic-gate 
27310Sstevel@tonic-gate 	assert(inst->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE);
27320Sstevel@tonic-gate 
27330Sstevel@tonic-gate 	r = perm_add_ent_prop_values(pcp, inst, AUTH_PG_GENERAL,
27340Sstevel@tonic-gate 	    AUTH_PG_GENERAL_TYPE, AUTH_PROP_ACTION);
27350Sstevel@tonic-gate 
27360Sstevel@tonic-gate 	if (r != REP_PROTOCOL_FAIL_NOT_FOUND)
27370Sstevel@tonic-gate 		return (r);
27380Sstevel@tonic-gate 
27390Sstevel@tonic-gate 	r = rc_node_parent(inst, &svc);
27400Sstevel@tonic-gate 	if (r != REP_PROTOCOL_SUCCESS) {
27410Sstevel@tonic-gate 		assert(r == REP_PROTOCOL_FAIL_DELETED);
27420Sstevel@tonic-gate 		return (r);
27430Sstevel@tonic-gate 	}
27440Sstevel@tonic-gate 
27450Sstevel@tonic-gate 	r = perm_add_ent_prop_values(pcp, svc, AUTH_PG_GENERAL,
27460Sstevel@tonic-gate 	    AUTH_PG_GENERAL_TYPE, AUTH_PROP_ACTION);
27470Sstevel@tonic-gate 
27480Sstevel@tonic-gate 	return (r == REP_PROTOCOL_FAIL_NOT_FOUND ? REP_PROTOCOL_SUCCESS : r);
27490Sstevel@tonic-gate }
27500Sstevel@tonic-gate #endif /* NATIVE_BUILD */
27510Sstevel@tonic-gate 
27520Sstevel@tonic-gate void
rc_node_ptr_init(rc_node_ptr_t * out)27530Sstevel@tonic-gate rc_node_ptr_init(rc_node_ptr_t *out)
27540Sstevel@tonic-gate {
27550Sstevel@tonic-gate 	out->rnp_node = NULL;
27565777Stw21770 	out->rnp_auth_string = NULL;
27575777Stw21770 	out->rnp_authorized = RC_AUTH_UNKNOWN;
27580Sstevel@tonic-gate 	out->rnp_deleted = 0;
27590Sstevel@tonic-gate }
27600Sstevel@tonic-gate 
27615777Stw21770 void
rc_node_ptr_free_mem(rc_node_ptr_t * npp)27625777Stw21770 rc_node_ptr_free_mem(rc_node_ptr_t *npp)
27635777Stw21770 {
27645777Stw21770 	if (npp->rnp_auth_string != NULL) {
27655777Stw21770 		free((void *)npp->rnp_auth_string);
27665777Stw21770 		npp->rnp_auth_string = NULL;
27675777Stw21770 	}
27685777Stw21770 }
27695777Stw21770 
27700Sstevel@tonic-gate static void
rc_node_assign(rc_node_ptr_t * out,rc_node_t * val)27710Sstevel@tonic-gate rc_node_assign(rc_node_ptr_t *out, rc_node_t *val)
27720Sstevel@tonic-gate {
27730Sstevel@tonic-gate 	rc_node_t *cur = out->rnp_node;
27740Sstevel@tonic-gate 	if (val != NULL)
27750Sstevel@tonic-gate 		rc_node_hold(val);
27760Sstevel@tonic-gate 	out->rnp_node = val;
27777266Sbustos 	if (cur != NULL) {
27787266Sbustos 		NODE_LOCK(cur);
27797266Sbustos 
27807266Sbustos 		/*
27817266Sbustos 		 * Register the ephemeral reference created by reading
27827266Sbustos 		 * out->rnp_node into cur.  Note that the persistent
27837266Sbustos 		 * reference we're destroying is locked by the client
27847266Sbustos 		 * layer.
27857266Sbustos 		 */
27867266Sbustos 		rc_node_hold_ephemeral_locked(cur);
27877266Sbustos 
27887266Sbustos 		rc_node_rele_locked(cur);
27897266Sbustos 	}
27905777Stw21770 	out->rnp_authorized = RC_AUTH_UNKNOWN;
27915777Stw21770 	rc_node_ptr_free_mem(out);
27920Sstevel@tonic-gate 	out->rnp_deleted = 0;
27930Sstevel@tonic-gate }
27940Sstevel@tonic-gate 
27950Sstevel@tonic-gate void
rc_node_clear(rc_node_ptr_t * out,int deleted)27960Sstevel@tonic-gate rc_node_clear(rc_node_ptr_t *out, int deleted)
27970Sstevel@tonic-gate {
27980Sstevel@tonic-gate 	rc_node_assign(out, NULL);
27990Sstevel@tonic-gate 	out->rnp_deleted = deleted;
28000Sstevel@tonic-gate }
28010Sstevel@tonic-gate 
28020Sstevel@tonic-gate void
rc_node_ptr_assign(rc_node_ptr_t * out,const rc_node_ptr_t * val)28030Sstevel@tonic-gate rc_node_ptr_assign(rc_node_ptr_t *out, const rc_node_ptr_t *val)
28040Sstevel@tonic-gate {
28050Sstevel@tonic-gate 	rc_node_assign(out, val->rnp_node);
28060Sstevel@tonic-gate }
28070Sstevel@tonic-gate 
28080Sstevel@tonic-gate /*
28090Sstevel@tonic-gate  * rc_node_check()/RC_NODE_CHECK()
28100Sstevel@tonic-gate  *	generic "entry" checks, run before the use of an rc_node pointer.
28110Sstevel@tonic-gate  *
28120Sstevel@tonic-gate  * Fails with
28130Sstevel@tonic-gate  *   _NOT_SET
28140Sstevel@tonic-gate  *   _DELETED
28150Sstevel@tonic-gate  */
28160Sstevel@tonic-gate static int
rc_node_check_and_lock(rc_node_t * np)28170Sstevel@tonic-gate rc_node_check_and_lock(rc_node_t *np)
28180Sstevel@tonic-gate {
28190Sstevel@tonic-gate 	int result = REP_PROTOCOL_SUCCESS;
28200Sstevel@tonic-gate 	if (np == NULL)
28210Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_SET);
28220Sstevel@tonic-gate 
28230Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
28240Sstevel@tonic-gate 	if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
28250Sstevel@tonic-gate 		result = REP_PROTOCOL_FAIL_DELETED;
28260Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
28270Sstevel@tonic-gate 	}
28280Sstevel@tonic-gate 
28290Sstevel@tonic-gate 	return (result);
28300Sstevel@tonic-gate }
28310Sstevel@tonic-gate 
28320Sstevel@tonic-gate /*
28330Sstevel@tonic-gate  * Fails with
28340Sstevel@tonic-gate  *   _NOT_SET - ptr is reset
28350Sstevel@tonic-gate  *   _DELETED - node has been deleted
28360Sstevel@tonic-gate  */
28370Sstevel@tonic-gate static rc_node_t *
rc_node_ptr_check_and_lock(rc_node_ptr_t * npp,int * res)28380Sstevel@tonic-gate rc_node_ptr_check_and_lock(rc_node_ptr_t *npp, int *res)
28390Sstevel@tonic-gate {
28400Sstevel@tonic-gate 	rc_node_t *np = npp->rnp_node;
28410Sstevel@tonic-gate 	if (np == NULL) {
28420Sstevel@tonic-gate 		if (npp->rnp_deleted)
28430Sstevel@tonic-gate 			*res = REP_PROTOCOL_FAIL_DELETED;
28440Sstevel@tonic-gate 		else
28450Sstevel@tonic-gate 			*res = REP_PROTOCOL_FAIL_NOT_SET;
28460Sstevel@tonic-gate 		return (NULL);
28470Sstevel@tonic-gate 	}
28480Sstevel@tonic-gate 
28490Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
28500Sstevel@tonic-gate 	if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
28510Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
28520Sstevel@tonic-gate 		rc_node_clear(npp, 1);
28530Sstevel@tonic-gate 		*res = REP_PROTOCOL_FAIL_DELETED;
28540Sstevel@tonic-gate 		return (NULL);
28550Sstevel@tonic-gate 	}
28560Sstevel@tonic-gate 	return (np);
28570Sstevel@tonic-gate }
28580Sstevel@tonic-gate 
28590Sstevel@tonic-gate #define	RC_NODE_CHECK_AND_LOCK(n) {					\
28600Sstevel@tonic-gate 	int rc__res;							\
28610Sstevel@tonic-gate 	if ((rc__res = rc_node_check_and_lock(n)) != REP_PROTOCOL_SUCCESS) \
28620Sstevel@tonic-gate 		return (rc__res);					\
28630Sstevel@tonic-gate }
28640Sstevel@tonic-gate 
28650Sstevel@tonic-gate #define	RC_NODE_CHECK(n) {						\
28660Sstevel@tonic-gate 	RC_NODE_CHECK_AND_LOCK(n);					\
28670Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&(n)->rn_lock);			\
28680Sstevel@tonic-gate }
28690Sstevel@tonic-gate 
28700Sstevel@tonic-gate #define	RC_NODE_CHECK_AND_HOLD(n) {					\
28710Sstevel@tonic-gate 	RC_NODE_CHECK_AND_LOCK(n);					\
28720Sstevel@tonic-gate 	rc_node_hold_locked(n);						\
28730Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&(n)->rn_lock);			\
28740Sstevel@tonic-gate }
28750Sstevel@tonic-gate 
28760Sstevel@tonic-gate #define	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp) {			\
28770Sstevel@tonic-gate 	int rc__res;							\
28780Sstevel@tonic-gate 	if (((np) = rc_node_ptr_check_and_lock(npp, &rc__res)) == NULL)	\
28790Sstevel@tonic-gate 		return (rc__res);					\
28800Sstevel@tonic-gate }
28810Sstevel@tonic-gate 
28828497SThomas.Whitten@Sun.COM #define	RC_NODE_PTR_CHECK_LOCK_OR_FREE_RETURN(np, npp, mem) {		\
28838497SThomas.Whitten@Sun.COM 	int rc__res;							\
28848497SThomas.Whitten@Sun.COM 	if (((np) = rc_node_ptr_check_and_lock(npp, &rc__res)) == 	\
28858497SThomas.Whitten@Sun.COM 	    NULL) {							\
28868497SThomas.Whitten@Sun.COM 		if ((mem) != NULL)					\
28878497SThomas.Whitten@Sun.COM 			free((mem));					\
28888497SThomas.Whitten@Sun.COM 		return (rc__res);					\
28898497SThomas.Whitten@Sun.COM 	}								\
28908497SThomas.Whitten@Sun.COM }
28918497SThomas.Whitten@Sun.COM 
28920Sstevel@tonic-gate #define	RC_NODE_PTR_GET_CHECK(np, npp) {				\
28930Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);			\
28940Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&(np)->rn_lock);			\
28950Sstevel@tonic-gate }
28960Sstevel@tonic-gate 
28970Sstevel@tonic-gate #define	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp) {			\
28980Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);			\
28990Sstevel@tonic-gate 	rc_node_hold_locked(np);					\
29000Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&(np)->rn_lock);			\
29010Sstevel@tonic-gate }
29020Sstevel@tonic-gate 
29030Sstevel@tonic-gate #define	HOLD_FLAG_OR_RETURN(np, flag) {					\
29040Sstevel@tonic-gate 	assert(MUTEX_HELD(&(np)->rn_lock));				\
29050Sstevel@tonic-gate 	assert(!((np)->rn_flags & RC_NODE_DEAD));			\
29060Sstevel@tonic-gate 	if (!rc_node_hold_flag((np), flag)) {				\
29070Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&(np)->rn_lock);		\
29080Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_DELETED);			\
29090Sstevel@tonic-gate 	}								\
29100Sstevel@tonic-gate }
29110Sstevel@tonic-gate 
29125777Stw21770 #define	HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, flag, mem) {		\
29135777Stw21770 	assert(MUTEX_HELD(&(np)->rn_lock));				\
29145777Stw21770 	if (!rc_node_hold_flag((np), flag)) {				\
29155777Stw21770 		(void) pthread_mutex_unlock(&(np)->rn_lock);		\
29165777Stw21770 		assert((np) == (npp)->rnp_node);			\
29175777Stw21770 		rc_node_clear(npp, 1);					\
29185777Stw21770 		if ((mem) != NULL)					\
29195777Stw21770 			free((mem));					\
29205777Stw21770 		return (REP_PROTOCOL_FAIL_DELETED);			\
29215777Stw21770 	}								\
29225777Stw21770 }
29235777Stw21770 
29240Sstevel@tonic-gate int
rc_local_scope(uint32_t type,rc_node_ptr_t * out)29250Sstevel@tonic-gate rc_local_scope(uint32_t type, rc_node_ptr_t *out)
29260Sstevel@tonic-gate {
29270Sstevel@tonic-gate 	if (type != REP_PROTOCOL_ENTITY_SCOPE) {
29280Sstevel@tonic-gate 		rc_node_clear(out, 0);
29290Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
29300Sstevel@tonic-gate 	}
29310Sstevel@tonic-gate 
29320Sstevel@tonic-gate 	/*
29330Sstevel@tonic-gate 	 * the main scope never gets destroyed
29340Sstevel@tonic-gate 	 */
29350Sstevel@tonic-gate 	rc_node_assign(out, rc_scope);
29360Sstevel@tonic-gate 
29370Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
29380Sstevel@tonic-gate }
29390Sstevel@tonic-gate 
29400Sstevel@tonic-gate /*
29410Sstevel@tonic-gate  * Fails with
29420Sstevel@tonic-gate  *   _NOT_SET - npp is not set
29430Sstevel@tonic-gate  *   _DELETED - the node npp pointed at has been deleted
29440Sstevel@tonic-gate  *   _TYPE_MISMATCH - type is not _SCOPE
29450Sstevel@tonic-gate  *   _NOT_FOUND - scope has no parent
29460Sstevel@tonic-gate  */
29470Sstevel@tonic-gate static int
rc_scope_parent_scope(rc_node_ptr_t * npp,uint32_t type,rc_node_ptr_t * out)29480Sstevel@tonic-gate rc_scope_parent_scope(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out)
29490Sstevel@tonic-gate {
29500Sstevel@tonic-gate 	rc_node_t *np;
29510Sstevel@tonic-gate 
29520Sstevel@tonic-gate 	rc_node_clear(out, 0);
29530Sstevel@tonic-gate 
29540Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK(np, npp);
29550Sstevel@tonic-gate 
29560Sstevel@tonic-gate 	if (type != REP_PROTOCOL_ENTITY_SCOPE)
29570Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
29580Sstevel@tonic-gate 
29590Sstevel@tonic-gate 	return (REP_PROTOCOL_FAIL_NOT_FOUND);
29600Sstevel@tonic-gate }
29610Sstevel@tonic-gate 
29625040Swesolows static int rc_node_pg_check_read_protect(rc_node_t *);
29635040Swesolows 
29640Sstevel@tonic-gate /*
29650Sstevel@tonic-gate  * Fails with
29660Sstevel@tonic-gate  *   _NOT_SET
29670Sstevel@tonic-gate  *   _DELETED
29680Sstevel@tonic-gate  *   _NOT_APPLICABLE
29690Sstevel@tonic-gate  *   _NOT_FOUND
29700Sstevel@tonic-gate  *   _BAD_REQUEST
29710Sstevel@tonic-gate  *   _TRUNCATED
29725040Swesolows  *   _NO_RESOURCES
29730Sstevel@tonic-gate  */
29740Sstevel@tonic-gate int
rc_node_name(rc_node_ptr_t * npp,char * buf,size_t sz,uint32_t answertype,size_t * sz_out)29750Sstevel@tonic-gate rc_node_name(rc_node_ptr_t *npp, char *buf, size_t sz, uint32_t answertype,
29760Sstevel@tonic-gate     size_t *sz_out)
29770Sstevel@tonic-gate {
29780Sstevel@tonic-gate 	size_t actual;
29790Sstevel@tonic-gate 	rc_node_t *np;
29800Sstevel@tonic-gate 
29810Sstevel@tonic-gate 	assert(sz == *sz_out);
29820Sstevel@tonic-gate 
29830Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK(np, npp);
29840Sstevel@tonic-gate 
29850Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
29860Sstevel@tonic-gate 		np = np->rn_cchain[0];
29870Sstevel@tonic-gate 		RC_NODE_CHECK(np);
29880Sstevel@tonic-gate 	}
29890Sstevel@tonic-gate 
29900Sstevel@tonic-gate 	switch (answertype) {
29910Sstevel@tonic-gate 	case RP_ENTITY_NAME_NAME:
29920Sstevel@tonic-gate 		if (np->rn_name == NULL)
29930Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
29940Sstevel@tonic-gate 		actual = strlcpy(buf, np->rn_name, sz);
29950Sstevel@tonic-gate 		break;
29960Sstevel@tonic-gate 	case RP_ENTITY_NAME_PGTYPE:
29970Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
29980Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
29990Sstevel@tonic-gate 		actual = strlcpy(buf, np->rn_type, sz);
30000Sstevel@tonic-gate 		break;
30010Sstevel@tonic-gate 	case RP_ENTITY_NAME_PGFLAGS:
30020Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
30030Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
30040Sstevel@tonic-gate 		actual = snprintf(buf, sz, "%d", np->rn_pgflags);
30050Sstevel@tonic-gate 		break;
30060Sstevel@tonic-gate 	case RP_ENTITY_NAME_SNAPLEVEL_SCOPE:
30070Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
30080Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
30090Sstevel@tonic-gate 		actual = strlcpy(buf, np->rn_snaplevel->rsl_scope, sz);
30100Sstevel@tonic-gate 		break;
30110Sstevel@tonic-gate 	case RP_ENTITY_NAME_SNAPLEVEL_SERVICE:
30120Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
30130Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
30140Sstevel@tonic-gate 		actual = strlcpy(buf, np->rn_snaplevel->rsl_service, sz);
30150Sstevel@tonic-gate 		break;
30160Sstevel@tonic-gate 	case RP_ENTITY_NAME_SNAPLEVEL_INSTANCE:
30170Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
30180Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
30190Sstevel@tonic-gate 		if (np->rn_snaplevel->rsl_instance == NULL)
30200Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NOT_FOUND);
30210Sstevel@tonic-gate 		actual = strlcpy(buf, np->rn_snaplevel->rsl_instance, sz);
30220Sstevel@tonic-gate 		break;
30235040Swesolows 	case RP_ENTITY_NAME_PGREADPROT:
30245040Swesolows 	{
30255040Swesolows 		int ret;
30265040Swesolows 
30275040Swesolows 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
30285040Swesolows 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
30295040Swesolows 		ret = rc_node_pg_check_read_protect(np);
30305040Swesolows 		assert(ret != REP_PROTOCOL_FAIL_TYPE_MISMATCH);
30315040Swesolows 		switch (ret) {
30325040Swesolows 		case REP_PROTOCOL_FAIL_PERMISSION_DENIED:
30335040Swesolows 			actual = snprintf(buf, sz, "1");
30345040Swesolows 			break;
30355040Swesolows 		case REP_PROTOCOL_SUCCESS:
30365040Swesolows 			actual = snprintf(buf, sz, "0");
30375040Swesolows 			break;
30385040Swesolows 		default:
30395040Swesolows 			return (ret);
30405040Swesolows 		}
30415040Swesolows 		break;
30425040Swesolows 	}
30430Sstevel@tonic-gate 	default:
30440Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
30450Sstevel@tonic-gate 	}
30460Sstevel@tonic-gate 	if (actual >= sz)
30470Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TRUNCATED);
30480Sstevel@tonic-gate 
30490Sstevel@tonic-gate 	*sz_out = actual;
30500Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
30510Sstevel@tonic-gate }
30520Sstevel@tonic-gate 
30530Sstevel@tonic-gate int
rc_node_get_property_type(rc_node_ptr_t * npp,rep_protocol_value_type_t * out)30540Sstevel@tonic-gate rc_node_get_property_type(rc_node_ptr_t *npp, rep_protocol_value_type_t *out)
30550Sstevel@tonic-gate {
30560Sstevel@tonic-gate 	rc_node_t *np;
30570Sstevel@tonic-gate 
30580Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK(np, npp);
30590Sstevel@tonic-gate 
30600Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
30610Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
30620Sstevel@tonic-gate 
30630Sstevel@tonic-gate 	*out = np->rn_valtype;
30640Sstevel@tonic-gate 
30650Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
30660Sstevel@tonic-gate }
30670Sstevel@tonic-gate 
30680Sstevel@tonic-gate /*
30690Sstevel@tonic-gate  * Get np's parent.  If np is deleted, returns _DELETED.  Otherwise puts a hold
30700Sstevel@tonic-gate  * on the parent, returns a pointer to it in *out, and returns _SUCCESS.
30710Sstevel@tonic-gate  */
30720Sstevel@tonic-gate static int
rc_node_parent(rc_node_t * np,rc_node_t ** out)30730Sstevel@tonic-gate rc_node_parent(rc_node_t *np, rc_node_t **out)
30740Sstevel@tonic-gate {
30750Sstevel@tonic-gate 	rc_node_t *pnp;
30760Sstevel@tonic-gate 	rc_node_t *np_orig;
30770Sstevel@tonic-gate 
30780Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
30790Sstevel@tonic-gate 		RC_NODE_CHECK_AND_LOCK(np);
30800Sstevel@tonic-gate 	} else {
30810Sstevel@tonic-gate 		np = np->rn_cchain[0];
30820Sstevel@tonic-gate 		RC_NODE_CHECK_AND_LOCK(np);
30830Sstevel@tonic-gate 	}
30840Sstevel@tonic-gate 
30850Sstevel@tonic-gate 	np_orig = np;
30860Sstevel@tonic-gate 	rc_node_hold_locked(np);		/* simplifies the remainder */
30870Sstevel@tonic-gate 
30880Sstevel@tonic-gate 	for (;;) {
30890Sstevel@tonic-gate 		if (!rc_node_wait_flag(np,
30900Sstevel@tonic-gate 		    RC_NODE_IN_TX | RC_NODE_USING_PARENT)) {
30910Sstevel@tonic-gate 			rc_node_rele_locked(np);
30920Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_DELETED);
30930Sstevel@tonic-gate 		}
30940Sstevel@tonic-gate 
30950Sstevel@tonic-gate 		if (!(np->rn_flags & RC_NODE_OLD))
30960Sstevel@tonic-gate 			break;
30970Sstevel@tonic-gate 
30980Sstevel@tonic-gate 		rc_node_rele_locked(np);
30990Sstevel@tonic-gate 		np = cache_lookup(&np_orig->rn_id);
31000Sstevel@tonic-gate 		assert(np != np_orig);
31010Sstevel@tonic-gate 
31020Sstevel@tonic-gate 		if (np == NULL)
31030Sstevel@tonic-gate 			goto deleted;
31040Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
31050Sstevel@tonic-gate 	}
31060Sstevel@tonic-gate 
31070Sstevel@tonic-gate 	/* guaranteed to succeed without dropping the lock */
31080Sstevel@tonic-gate 	if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
31090Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
31100Sstevel@tonic-gate 		*out = NULL;
31110Sstevel@tonic-gate 		rc_node_rele(np);
31120Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_DELETED);
31130Sstevel@tonic-gate 	}
31140Sstevel@tonic-gate 
31150Sstevel@tonic-gate 	assert(np->rn_parent != NULL);
31160Sstevel@tonic-gate 	pnp = np->rn_parent;
31170Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
31180Sstevel@tonic-gate 
31190Sstevel@tonic-gate 	(void) pthread_mutex_lock(&pnp->rn_lock);
31200Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
31210Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_USING_PARENT);
31220Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
31230Sstevel@tonic-gate 
31240Sstevel@tonic-gate 	rc_node_hold_locked(pnp);
31250Sstevel@tonic-gate 
31260Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&pnp->rn_lock);
31270Sstevel@tonic-gate 
31280Sstevel@tonic-gate 	rc_node_rele(np);
31290Sstevel@tonic-gate 	*out = pnp;
31300Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
31310Sstevel@tonic-gate 
31320Sstevel@tonic-gate deleted:
31330Sstevel@tonic-gate 	rc_node_rele(np);
31340Sstevel@tonic-gate 	return (REP_PROTOCOL_FAIL_DELETED);
31350Sstevel@tonic-gate }
31360Sstevel@tonic-gate 
31370Sstevel@tonic-gate /*
31380Sstevel@tonic-gate  * Fails with
31390Sstevel@tonic-gate  *   _NOT_SET
31400Sstevel@tonic-gate  *   _DELETED
31410Sstevel@tonic-gate  */
31420Sstevel@tonic-gate static int
rc_node_ptr_parent(rc_node_ptr_t * npp,rc_node_t ** out)31430Sstevel@tonic-gate rc_node_ptr_parent(rc_node_ptr_t *npp, rc_node_t **out)
31440Sstevel@tonic-gate {
31450Sstevel@tonic-gate 	rc_node_t *np;
31460Sstevel@tonic-gate 
31470Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK(np, npp);
31480Sstevel@tonic-gate 
31490Sstevel@tonic-gate 	return (rc_node_parent(np, out));
31500Sstevel@tonic-gate }
31510Sstevel@tonic-gate 
31520Sstevel@tonic-gate /*
31530Sstevel@tonic-gate  * Fails with
31540Sstevel@tonic-gate  *   _NOT_SET - npp is not set
31550Sstevel@tonic-gate  *   _DELETED - the node npp pointed at has been deleted
31560Sstevel@tonic-gate  *   _TYPE_MISMATCH - npp's node's parent is not of type type
31570Sstevel@tonic-gate  *
31580Sstevel@tonic-gate  * If npp points to a scope, can also fail with
31590Sstevel@tonic-gate  *   _NOT_FOUND - scope has no parent
31600Sstevel@tonic-gate  */
31610Sstevel@tonic-gate int
rc_node_get_parent(rc_node_ptr_t * npp,uint32_t type,rc_node_ptr_t * out)31620Sstevel@tonic-gate rc_node_get_parent(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out)
31630Sstevel@tonic-gate {
31640Sstevel@tonic-gate 	rc_node_t *pnp;
31650Sstevel@tonic-gate 	int rc;
31660Sstevel@tonic-gate 
31670Sstevel@tonic-gate 	if (npp->rnp_node != NULL &&
31680Sstevel@tonic-gate 	    npp->rnp_node->rn_id.rl_type == REP_PROTOCOL_ENTITY_SCOPE)
31690Sstevel@tonic-gate 		return (rc_scope_parent_scope(npp, type, out));
31700Sstevel@tonic-gate 
31710Sstevel@tonic-gate 	if ((rc = rc_node_ptr_parent(npp, &pnp)) != REP_PROTOCOL_SUCCESS) {
31720Sstevel@tonic-gate 		rc_node_clear(out, 0);
31730Sstevel@tonic-gate 		return (rc);
31740Sstevel@tonic-gate 	}
31750Sstevel@tonic-gate 
31760Sstevel@tonic-gate 	if (type != pnp->rn_id.rl_type) {
31770Sstevel@tonic-gate 		rc_node_rele(pnp);
31780Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
31790Sstevel@tonic-gate 	}
31800Sstevel@tonic-gate 
31810Sstevel@tonic-gate 	rc_node_assign(out, pnp);
31820Sstevel@tonic-gate 	rc_node_rele(pnp);
31830Sstevel@tonic-gate 
31840Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
31850Sstevel@tonic-gate }
31860Sstevel@tonic-gate 
31870Sstevel@tonic-gate int
rc_node_parent_type(rc_node_ptr_t * npp,uint32_t * type_out)31880Sstevel@tonic-gate rc_node_parent_type(rc_node_ptr_t *npp, uint32_t *type_out)
31890Sstevel@tonic-gate {
31900Sstevel@tonic-gate 	rc_node_t *pnp;
31910Sstevel@tonic-gate 	int rc;
31920Sstevel@tonic-gate 
31930Sstevel@tonic-gate 	if (npp->rnp_node != NULL &&
31940Sstevel@tonic-gate 	    npp->rnp_node->rn_id.rl_type == REP_PROTOCOL_ENTITY_SCOPE) {
31950Sstevel@tonic-gate 		*type_out = REP_PROTOCOL_ENTITY_SCOPE;
31960Sstevel@tonic-gate 		return (REP_PROTOCOL_SUCCESS);
31970Sstevel@tonic-gate 	}
31980Sstevel@tonic-gate 
31990Sstevel@tonic-gate 	if ((rc = rc_node_ptr_parent(npp, &pnp)) != REP_PROTOCOL_SUCCESS)
32000Sstevel@tonic-gate 		return (rc);
32010Sstevel@tonic-gate 
32020Sstevel@tonic-gate 	*type_out = pnp->rn_id.rl_type;
32030Sstevel@tonic-gate 
32040Sstevel@tonic-gate 	rc_node_rele(pnp);
32050Sstevel@tonic-gate 
32060Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
32070Sstevel@tonic-gate }
32080Sstevel@tonic-gate 
32090Sstevel@tonic-gate /*
32100Sstevel@tonic-gate  * Fails with
32110Sstevel@tonic-gate  *   _INVALID_TYPE - type is invalid
32120Sstevel@tonic-gate  *   _TYPE_MISMATCH - np doesn't carry children of type type
32130Sstevel@tonic-gate  *   _DELETED - np has been deleted
32140Sstevel@tonic-gate  *   _NOT_FOUND - no child with that name/type combo found
32150Sstevel@tonic-gate  *   _NO_RESOURCES
32160Sstevel@tonic-gate  *   _BACKEND_ACCESS
32170Sstevel@tonic-gate  */
32180Sstevel@tonic-gate int
rc_node_get_child(rc_node_ptr_t * npp,const char * name,uint32_t type,rc_node_ptr_t * outp)32190Sstevel@tonic-gate rc_node_get_child(rc_node_ptr_t *npp, const char *name, uint32_t type,
32200Sstevel@tonic-gate     rc_node_ptr_t *outp)
32210Sstevel@tonic-gate {
32220Sstevel@tonic-gate 	rc_node_t *np, *cp;
32230Sstevel@tonic-gate 	rc_node_t *child = NULL;
32240Sstevel@tonic-gate 	int ret, idx;
32250Sstevel@tonic-gate 
32260Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
32270Sstevel@tonic-gate 	if ((ret = rc_check_type_name(type, name)) == REP_PROTOCOL_SUCCESS) {
32280Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
32290Sstevel@tonic-gate 			ret = rc_node_find_named_child(np, name, type, &child);
32300Sstevel@tonic-gate 		} else {
32310Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
32320Sstevel@tonic-gate 			ret = REP_PROTOCOL_SUCCESS;
32330Sstevel@tonic-gate 			for (idx = 0; idx < COMPOSITION_DEPTH; idx++) {
32340Sstevel@tonic-gate 				cp = np->rn_cchain[idx];
32350Sstevel@tonic-gate 				if (cp == NULL)
32360Sstevel@tonic-gate 					break;
32370Sstevel@tonic-gate 				RC_NODE_CHECK_AND_LOCK(cp);
32380Sstevel@tonic-gate 				ret = rc_node_find_named_child(cp, name, type,
32390Sstevel@tonic-gate 				    &child);
32400Sstevel@tonic-gate 				(void) pthread_mutex_unlock(&cp->rn_lock);
32410Sstevel@tonic-gate 				/*
32420Sstevel@tonic-gate 				 * loop only if we succeeded, but no child of
32430Sstevel@tonic-gate 				 * the correct name was found.
32440Sstevel@tonic-gate 				 */
32450Sstevel@tonic-gate 				if (ret != REP_PROTOCOL_SUCCESS ||
32460Sstevel@tonic-gate 				    child != NULL)
32470Sstevel@tonic-gate 					break;
32480Sstevel@tonic-gate 			}
32490Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
32500Sstevel@tonic-gate 		}
32510Sstevel@tonic-gate 	}
32520Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
32530Sstevel@tonic-gate 
32540Sstevel@tonic-gate 	if (ret == REP_PROTOCOL_SUCCESS) {
32550Sstevel@tonic-gate 		rc_node_assign(outp, child);
32560Sstevel@tonic-gate 		if (child != NULL)
32570Sstevel@tonic-gate 			rc_node_rele(child);
32580Sstevel@tonic-gate 		else
32590Sstevel@tonic-gate 			ret = REP_PROTOCOL_FAIL_NOT_FOUND;
32600Sstevel@tonic-gate 	} else {
32610Sstevel@tonic-gate 		rc_node_assign(outp, NULL);
32620Sstevel@tonic-gate 	}
32630Sstevel@tonic-gate 	return (ret);
32640Sstevel@tonic-gate }
32650Sstevel@tonic-gate 
32660Sstevel@tonic-gate int
rc_node_update(rc_node_ptr_t * npp)32670Sstevel@tonic-gate rc_node_update(rc_node_ptr_t *npp)
32680Sstevel@tonic-gate {
32690Sstevel@tonic-gate 	cache_bucket_t *bp;
32700Sstevel@tonic-gate 	rc_node_t *np = npp->rnp_node;
32710Sstevel@tonic-gate 	rc_node_t *nnp;
32720Sstevel@tonic-gate 	rc_node_t *cpg = NULL;
32730Sstevel@tonic-gate 
32740Sstevel@tonic-gate 	if (np != NULL &&
32750Sstevel@tonic-gate 	    np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
32760Sstevel@tonic-gate 		/*
32770Sstevel@tonic-gate 		 * If we're updating a composed property group, actually
32780Sstevel@tonic-gate 		 * update the top-level property group & return the
32790Sstevel@tonic-gate 		 * appropriate value.  But leave *nnp pointing at us.
32800Sstevel@tonic-gate 		 */
32810Sstevel@tonic-gate 		cpg = np;
32820Sstevel@tonic-gate 		np = np->rn_cchain[0];
32830Sstevel@tonic-gate 	}
32840Sstevel@tonic-gate 
32850Sstevel@tonic-gate 	RC_NODE_CHECK(np);
32860Sstevel@tonic-gate 
32870Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP &&
32880Sstevel@tonic-gate 	    np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT)
32890Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
32900Sstevel@tonic-gate 
32910Sstevel@tonic-gate 	for (;;) {
32920Sstevel@tonic-gate 		bp = cache_hold(np->rn_hash);
32930Sstevel@tonic-gate 		nnp = cache_lookup_unlocked(bp, &np->rn_id);
32940Sstevel@tonic-gate 		if (nnp == NULL) {
32950Sstevel@tonic-gate 			cache_release(bp);
32960Sstevel@tonic-gate 			rc_node_clear(npp, 1);
32970Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_DELETED);
32980Sstevel@tonic-gate 		}
32990Sstevel@tonic-gate 		/*
33000Sstevel@tonic-gate 		 * grab the lock before dropping the cache bucket, so
33010Sstevel@tonic-gate 		 * that no one else can sneak in
33020Sstevel@tonic-gate 		 */
33030Sstevel@tonic-gate 		(void) pthread_mutex_lock(&nnp->rn_lock);
33040Sstevel@tonic-gate 		cache_release(bp);
33050Sstevel@tonic-gate 
33060Sstevel@tonic-gate 		if (!(nnp->rn_flags & RC_NODE_IN_TX) ||
33070Sstevel@tonic-gate 		    !rc_node_wait_flag(nnp, RC_NODE_IN_TX))
33080Sstevel@tonic-gate 			break;
33090Sstevel@tonic-gate 
33100Sstevel@tonic-gate 		rc_node_rele_locked(nnp);
33110Sstevel@tonic-gate 	}
33120Sstevel@tonic-gate 
33130Sstevel@tonic-gate 	/*
33140Sstevel@tonic-gate 	 * If it is dead, we want to update it so that it will continue to
33150Sstevel@tonic-gate 	 * report being dead.
33160Sstevel@tonic-gate 	 */
33170Sstevel@tonic-gate 	if (nnp->rn_flags & RC_NODE_DEAD) {
33180Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&nnp->rn_lock);
33190Sstevel@tonic-gate 		if (nnp != np && cpg == NULL)
33200Sstevel@tonic-gate 			rc_node_assign(npp, nnp);	/* updated */
33210Sstevel@tonic-gate 		rc_node_rele(nnp);
33220Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_DELETED);
33230Sstevel@tonic-gate 	}
33240Sstevel@tonic-gate 
33250Sstevel@tonic-gate 	assert(!(nnp->rn_flags & RC_NODE_OLD));
33260Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&nnp->rn_lock);
33270Sstevel@tonic-gate 
33280Sstevel@tonic-gate 	if (nnp != np && cpg == NULL)
33290Sstevel@tonic-gate 		rc_node_assign(npp, nnp);		/* updated */
33300Sstevel@tonic-gate 
33310Sstevel@tonic-gate 	rc_node_rele(nnp);
33320Sstevel@tonic-gate 
33330Sstevel@tonic-gate 	return ((nnp == np)? REP_PROTOCOL_SUCCESS : REP_PROTOCOL_DONE);
33340Sstevel@tonic-gate }
33350Sstevel@tonic-gate 
33360Sstevel@tonic-gate /*
33370Sstevel@tonic-gate  * does a generic modification check, for creation, deletion, and snapshot
33380Sstevel@tonic-gate  * management only.  Property group transactions have different checks.
33395777Stw21770  *
33405777Stw21770  * The string returned to *match_auth must be freed.
33410Sstevel@tonic-gate  */
33428497SThomas.Whitten@Sun.COM static perm_status_t
rc_node_modify_permission_check(char ** match_auth)33435777Stw21770 rc_node_modify_permission_check(char **match_auth)
33440Sstevel@tonic-gate {
33450Sstevel@tonic-gate 	permcheck_t *pcp;
33468497SThomas.Whitten@Sun.COM 	perm_status_t granted = PERM_GRANTED;
33478497SThomas.Whitten@Sun.COM 	int rc;
33480Sstevel@tonic-gate 
33495777Stw21770 	*match_auth = NULL;
33500Sstevel@tonic-gate #ifdef NATIVE_BUILD
33515777Stw21770 	if (!client_is_privileged()) {
33528497SThomas.Whitten@Sun.COM 		granted = PERM_DENIED;
33538497SThomas.Whitten@Sun.COM 	}
33548497SThomas.Whitten@Sun.COM 	return (granted);
33550Sstevel@tonic-gate #else
33565777Stw21770 	if (is_main_repository == 0)
33578497SThomas.Whitten@Sun.COM 		return (PERM_GRANTED);
33585777Stw21770 	pcp = pc_create();
33595777Stw21770 	if (pcp != NULL) {
33605777Stw21770 		rc = perm_add_enabling(pcp, AUTH_MODIFY);
33615777Stw21770 
33625777Stw21770 		if (rc == REP_PROTOCOL_SUCCESS) {
33635777Stw21770 			granted = perm_granted(pcp);
33645777Stw21770 
33658497SThomas.Whitten@Sun.COM 			if ((granted == PERM_GRANTED) ||
33668497SThomas.Whitten@Sun.COM 			    (granted == PERM_DENIED)) {
33675777Stw21770 				/*
33685777Stw21770 				 * Copy off the authorization
33695777Stw21770 				 * string before freeing pcp.
33705777Stw21770 				 */
33715777Stw21770 				*match_auth =
33725777Stw21770 				    strdup(pcp->pc_auth_string);
33735777Stw21770 				if (*match_auth == NULL)
33748497SThomas.Whitten@Sun.COM 					granted = PERM_FAIL;
33750Sstevel@tonic-gate 			}
33768497SThomas.Whitten@Sun.COM 		} else {
33778497SThomas.Whitten@Sun.COM 			granted = PERM_FAIL;
33780Sstevel@tonic-gate 		}
33790Sstevel@tonic-gate 
33805777Stw21770 		pc_free(pcp);
33815777Stw21770 	} else {
33828497SThomas.Whitten@Sun.COM 		granted = PERM_FAIL;
33838497SThomas.Whitten@Sun.COM 	}
33848497SThomas.Whitten@Sun.COM 
33858497SThomas.Whitten@Sun.COM 	return (granted);
33860Sstevel@tonic-gate #endif /* NATIVE_BUILD */
33875777Stw21770 }
33885777Stw21770 
33895777Stw21770 /*
33905777Stw21770  * Native builds are done to create svc.configd-native.  This program runs
33915777Stw21770  * only on the Solaris build machines to create the seed repository, and it
33925777Stw21770  * is compiled against the build machine's header files.  The ADT_smf_*
33935777Stw21770  * symbols may not be defined in these header files.  For this reason
33945777Stw21770  * smf_annotation_event(), _smf_audit_event() and special_property_event()
33955777Stw21770  * are not compiled for native builds.
33965777Stw21770  */
33975777Stw21770 #ifndef	NATIVE_BUILD
33985777Stw21770 
33995777Stw21770 /*
34005777Stw21770  * This function generates an annotation audit event if one has been setup.
34015777Stw21770  * Annotation events should only be generated immediately before the audit
34025777Stw21770  * record from the first attempt to modify the repository from a client
34035777Stw21770  * which has requested an annotation.
34045777Stw21770  */
34055777Stw21770 static void
smf_annotation_event(int status,int return_val)34065777Stw21770 smf_annotation_event(int status, int return_val)
34075777Stw21770 {
34085777Stw21770 	adt_session_data_t *session;
34095777Stw21770 	adt_event_data_t *event = NULL;
34105777Stw21770 	char file[MAXPATHLEN];
34115777Stw21770 	char operation[REP_PROTOCOL_NAME_LEN];
34125777Stw21770 
34135777Stw21770 	/* Don't audit if we're using an alternate repository. */
34145777Stw21770 	if (is_main_repository == 0)
34155777Stw21770 		return;
34165777Stw21770 
34175777Stw21770 	if (client_annotation_needed(operation, sizeof (operation), file,
34185777Stw21770 	    sizeof (file)) == 0) {
34195777Stw21770 		return;
34205777Stw21770 	}
34215777Stw21770 	if (file[0] == 0) {
34225777Stw21770 		(void) strlcpy(file, "NO FILE", sizeof (file));
34235777Stw21770 	}
34245777Stw21770 	if (operation[0] == 0) {
34255777Stw21770 		(void) strlcpy(operation, "NO OPERATION",
34265777Stw21770 		    sizeof (operation));
34275777Stw21770 	}
34285777Stw21770 	if ((session = get_audit_session()) == NULL)
34295777Stw21770 		return;
34305777Stw21770 	if ((event = adt_alloc_event(session, ADT_smf_annotation)) == NULL) {
34315777Stw21770 		uu_warn("smf_annotation_event cannot allocate event "
34325777Stw21770 		    "data.  %s\n", strerror(errno));
34335777Stw21770 		return;
34345777Stw21770 	}
34355777Stw21770 	event->adt_smf_annotation.operation = operation;
34365777Stw21770 	event->adt_smf_annotation.file = file;
34375777Stw21770 	if (adt_put_event(event, status, return_val) == 0) {
34385777Stw21770 		client_annotation_finished();
34395777Stw21770 	} else {
34405777Stw21770 		uu_warn("smf_annotation_event failed to put event.  "
34415777Stw21770 		    "%s\n", strerror(errno));
34425777Stw21770 	}
34435777Stw21770 	adt_free_event(event);
34445777Stw21770 }
34455777Stw21770 
34465777Stw21770 /*
34475777Stw21770  * _smf_audit_event interacts with the security auditing system to generate
34485777Stw21770  * an audit event structure.  It establishes an audit session and allocates
34495777Stw21770  * an audit event.  The event is filled in from the audit data, and
34505777Stw21770  * adt_put_event is called to generate the event.
34515777Stw21770  */
34525777Stw21770 static void
_smf_audit_event(au_event_t event_id,int status,int return_val,audit_event_data_t * data)34535777Stw21770 _smf_audit_event(au_event_t event_id, int status, int return_val,
34545777Stw21770     audit_event_data_t *data)
34555777Stw21770 {
34565777Stw21770 	char *auth_used;
34575777Stw21770 	char *fmri;
34585777Stw21770 	char *prop_value;
34595777Stw21770 	adt_session_data_t *session;
34605777Stw21770 	adt_event_data_t *event = NULL;
34615777Stw21770 
34625777Stw21770 	/* Don't audit if we're using an alternate repository */
34635777Stw21770 	if (is_main_repository == 0)
34645777Stw21770 		return;
34655777Stw21770 
34665777Stw21770 	smf_annotation_event(status, return_val);
34675777Stw21770 	if ((session = get_audit_session()) == NULL)
34685777Stw21770 		return;
34695777Stw21770 	if ((event = adt_alloc_event(session, event_id)) == NULL) {
34705777Stw21770 		uu_warn("_smf_audit_event cannot allocate event "
34715777Stw21770 		    "data.  %s\n", strerror(errno));
34725777Stw21770 		return;
34735777Stw21770 	}
34745777Stw21770 
34755777Stw21770 	/*
34765777Stw21770 	 * Handle possibility of NULL authorization strings, FMRIs and
34775777Stw21770 	 * property values.
34785777Stw21770 	 */
34795777Stw21770 	if (data->ed_auth == NULL) {
34805777Stw21770 		auth_used = "PRIVILEGED";
34815777Stw21770 	} else {
34825777Stw21770 		auth_used = data->ed_auth;
34835777Stw21770 	}
34845777Stw21770 	if (data->ed_fmri == NULL) {
34855777Stw21770 		syslog(LOG_WARNING, "_smf_audit_event called with "
34865777Stw21770 		    "empty FMRI string");
34875777Stw21770 		fmri = "UNKNOWN FMRI";
34885777Stw21770 	} else {
34895777Stw21770 		fmri = data->ed_fmri;
34905777Stw21770 	}
34915777Stw21770 	if (data->ed_prop_value == NULL) {
34925777Stw21770 		prop_value = "";
34935777Stw21770 	} else {
34945777Stw21770 		prop_value = data->ed_prop_value;
34955777Stw21770 	}
34965777Stw21770 
34975777Stw21770 	/* Fill in the event data. */
34985777Stw21770 	switch (event_id) {
34995777Stw21770 	case ADT_smf_attach_snap:
35005777Stw21770 		event->adt_smf_attach_snap.auth_used = auth_used;
35015777Stw21770 		event->adt_smf_attach_snap.old_fmri = data->ed_old_fmri;
35025777Stw21770 		event->adt_smf_attach_snap.old_name = data->ed_old_name;
35035777Stw21770 		event->adt_smf_attach_snap.new_fmri = fmri;
35045777Stw21770 		event->adt_smf_attach_snap.new_name = data->ed_snapname;
35055777Stw21770 		break;
35065777Stw21770 	case ADT_smf_change_prop:
35075777Stw21770 		event->adt_smf_change_prop.auth_used = auth_used;
35085777Stw21770 		event->adt_smf_change_prop.fmri = fmri;
35095777Stw21770 		event->adt_smf_change_prop.type = data->ed_type;
35105777Stw21770 		event->adt_smf_change_prop.value = prop_value;
35115777Stw21770 		break;
35125777Stw21770 	case ADT_smf_clear:
35135777Stw21770 		event->adt_smf_clear.auth_used = auth_used;
35145777Stw21770 		event->adt_smf_clear.fmri = fmri;
35155777Stw21770 		break;
35165777Stw21770 	case ADT_smf_create:
35175777Stw21770 		event->adt_smf_create.fmri = fmri;
35185777Stw21770 		event->adt_smf_create.auth_used = auth_used;
35195777Stw21770 		break;
35205777Stw21770 	case ADT_smf_create_npg:
35215777Stw21770 		event->adt_smf_create_npg.auth_used = auth_used;
35225777Stw21770 		event->adt_smf_create_npg.fmri = fmri;
35235777Stw21770 		event->adt_smf_create_npg.type = data->ed_type;
35245777Stw21770 		break;
35255777Stw21770 	case ADT_smf_create_pg:
35265777Stw21770 		event->adt_smf_create_pg.auth_used = auth_used;
35275777Stw21770 		event->adt_smf_create_pg.fmri = fmri;
35285777Stw21770 		event->adt_smf_create_pg.type = data->ed_type;
35295777Stw21770 		break;
35305777Stw21770 	case ADT_smf_create_prop:
35315777Stw21770 		event->adt_smf_create_prop.auth_used = auth_used;
35325777Stw21770 		event->adt_smf_create_prop.fmri = fmri;
35335777Stw21770 		event->adt_smf_create_prop.type = data->ed_type;
35345777Stw21770 		event->adt_smf_create_prop.value = prop_value;
35355777Stw21770 		break;
35365777Stw21770 	case ADT_smf_create_snap:
35375777Stw21770 		event->adt_smf_create_snap.auth_used = auth_used;
35385777Stw21770 		event->adt_smf_create_snap.fmri = fmri;
35395777Stw21770 		event->adt_smf_create_snap.name = data->ed_snapname;
35405777Stw21770 		break;
35415777Stw21770 	case ADT_smf_degrade:
35425777Stw21770 		event->adt_smf_degrade.auth_used = auth_used;
35435777Stw21770 		event->adt_smf_degrade.fmri = fmri;
35445777Stw21770 		break;
35455777Stw21770 	case ADT_smf_delete:
35465777Stw21770 		event->adt_smf_delete.fmri = fmri;
35475777Stw21770 		event->adt_smf_delete.auth_used = auth_used;
35485777Stw21770 		break;
35495777Stw21770 	case ADT_smf_delete_npg:
35505777Stw21770 		event->adt_smf_delete_npg.auth_used = auth_used;
35515777Stw21770 		event->adt_smf_delete_npg.fmri = fmri;
35525777Stw21770 		event->adt_smf_delete_npg.type = data->ed_type;
35535777Stw21770 		break;
35545777Stw21770 	case ADT_smf_delete_pg:
35555777Stw21770 		event->adt_smf_delete_pg.auth_used = auth_used;
35565777Stw21770 		event->adt_smf_delete_pg.fmri = fmri;
35575777Stw21770 		event->adt_smf_delete_pg.type = data->ed_type;
35585777Stw21770 		break;
35595777Stw21770 	case ADT_smf_delete_prop:
35605777Stw21770 		event->adt_smf_delete_prop.auth_used = auth_used;
35615777Stw21770 		event->adt_smf_delete_prop.fmri = fmri;
35625777Stw21770 		break;
35635777Stw21770 	case ADT_smf_delete_snap:
35645777Stw21770 		event->adt_smf_delete_snap.auth_used = auth_used;
35655777Stw21770 		event->adt_smf_delete_snap.fmri = fmri;
35665777Stw21770 		event->adt_smf_delete_snap.name = data->ed_snapname;
35675777Stw21770 		break;
35685777Stw21770 	case ADT_smf_disable:
35695777Stw21770 		event->adt_smf_disable.auth_used = auth_used;
35705777Stw21770 		event->adt_smf_disable.fmri = fmri;
35715777Stw21770 		break;
35725777Stw21770 	case ADT_smf_enable:
35735777Stw21770 		event->adt_smf_enable.auth_used = auth_used;
35745777Stw21770 		event->adt_smf_enable.fmri = fmri;
35755777Stw21770 		break;
35765777Stw21770 	case ADT_smf_immediate_degrade:
35775777Stw21770 		event->adt_smf_immediate_degrade.auth_used = auth_used;
35785777Stw21770 		event->adt_smf_immediate_degrade.fmri = fmri;
35795777Stw21770 		break;
35805777Stw21770 	case ADT_smf_immediate_maintenance:
35815777Stw21770 		event->adt_smf_immediate_maintenance.auth_used = auth_used;
35825777Stw21770 		event->adt_smf_immediate_maintenance.fmri = fmri;
35835777Stw21770 		break;
35845777Stw21770 	case ADT_smf_immtmp_maintenance:
35855777Stw21770 		event->adt_smf_immtmp_maintenance.auth_used = auth_used;
35865777Stw21770 		event->adt_smf_immtmp_maintenance.fmri = fmri;
35875777Stw21770 		break;
35885777Stw21770 	case ADT_smf_maintenance:
35895777Stw21770 		event->adt_smf_maintenance.auth_used = auth_used;
35905777Stw21770 		event->adt_smf_maintenance.fmri = fmri;
35915777Stw21770 		break;
35925777Stw21770 	case ADT_smf_milestone:
35935777Stw21770 		event->adt_smf_milestone.auth_used = auth_used;
35945777Stw21770 		event->adt_smf_milestone.fmri = fmri;
35955777Stw21770 		break;
35965777Stw21770 	case ADT_smf_read_prop:
35975777Stw21770 		event->adt_smf_read_prop.auth_used = auth_used;
35985777Stw21770 		event->adt_smf_read_prop.fmri = fmri;
35995777Stw21770 		break;
36005777Stw21770 	case ADT_smf_refresh:
36015777Stw21770 		event->adt_smf_refresh.auth_used = auth_used;
36025777Stw21770 		event->adt_smf_refresh.fmri = fmri;
36035777Stw21770 		break;
36045777Stw21770 	case ADT_smf_restart:
36055777Stw21770 		event->adt_smf_restart.auth_used = auth_used;
36065777Stw21770 		event->adt_smf_restart.fmri = fmri;
36075777Stw21770 		break;
36085777Stw21770 	case ADT_smf_tmp_disable:
36095777Stw21770 		event->adt_smf_tmp_disable.auth_used = auth_used;
36105777Stw21770 		event->adt_smf_tmp_disable.fmri = fmri;
36115777Stw21770 		break;
36125777Stw21770 	case ADT_smf_tmp_enable:
36135777Stw21770 		event->adt_smf_tmp_enable.auth_used = auth_used;
36145777Stw21770 		event->adt_smf_tmp_enable.fmri = fmri;
36155777Stw21770 		break;
36165777Stw21770 	case ADT_smf_tmp_maintenance:
36175777Stw21770 		event->adt_smf_tmp_maintenance.auth_used = auth_used;
36185777Stw21770 		event->adt_smf_tmp_maintenance.fmri = fmri;
36195777Stw21770 		break;
36205777Stw21770 	default:
36215777Stw21770 		abort();	/* Need to cover all SMF event IDs */
36225777Stw21770 	}
36235777Stw21770 
36245777Stw21770 	if (adt_put_event(event, status, return_val) != 0) {
36255777Stw21770 		uu_warn("_smf_audit_event failed to put event.  %s\n",
36265777Stw21770 		    strerror(errno));
36275777Stw21770 	}
36285777Stw21770 	adt_free_event(event);
36295777Stw21770 }
36305777Stw21770 
36315777Stw21770 /*
36325777Stw21770  * Determine if the combination of the property group at pg_name and the
36335777Stw21770  * property at prop_name are in the set of special startd properties.  If
36345777Stw21770  * they are, a special audit event will be generated.
36355777Stw21770  */
36365777Stw21770 static void
special_property_event(audit_event_data_t * evdp,const char * prop_name,char * pg_name,int status,int return_val,tx_commit_data_t * tx_data,size_t cmd_no)36375777Stw21770 special_property_event(audit_event_data_t *evdp, const char *prop_name,
36385777Stw21770     char *pg_name, int status, int return_val, tx_commit_data_t *tx_data,
36395777Stw21770     size_t cmd_no)
36405777Stw21770 {
36415777Stw21770 	au_event_t event_id;
36425777Stw21770 	audit_special_prop_item_t search_key;
36435777Stw21770 	audit_special_prop_item_t *found;
36445777Stw21770 
36455777Stw21770 	/* Use bsearch to find the special property information. */
36465777Stw21770 	search_key.api_prop_name = prop_name;
36475777Stw21770 	search_key.api_pg_name = pg_name;
36485777Stw21770 	found = (audit_special_prop_item_t *)bsearch(&search_key,
36495777Stw21770 	    special_props_list, SPECIAL_PROP_COUNT,
36505777Stw21770 	    sizeof (special_props_list[0]), special_prop_compare);
36515777Stw21770 	if (found == NULL) {
36525777Stw21770 		/* Not a special property. */
36535777Stw21770 		return;
36545777Stw21770 	}
36555777Stw21770 
36565777Stw21770 	/* Get the event id */
36575777Stw21770 	if (found->api_event_func == NULL) {
36585777Stw21770 		event_id = found->api_event_id;
36595777Stw21770 	} else {
36605777Stw21770 		if ((*found->api_event_func)(tx_data, cmd_no,
36615777Stw21770 		    found->api_pg_name, &event_id) < 0)
36625777Stw21770 			return;
36635777Stw21770 	}
36645777Stw21770 
36655777Stw21770 	/* Generate the event. */
36665777Stw21770 	smf_audit_event(event_id, status, return_val, evdp);
36675777Stw21770 }
36685777Stw21770 #endif	/* NATIVE_BUILD */
36695777Stw21770 
36705777Stw21770 /*
36715777Stw21770  * Return a pointer to a string containing all the values of the command
36725777Stw21770  * specified by cmd_no with each value enclosed in quotes.  It is up to the
36735777Stw21770  * caller to free the memory at the returned pointer.
36745777Stw21770  */
36755777Stw21770 static char *
generate_value_list(tx_commit_data_t * tx_data,size_t cmd_no)36765777Stw21770 generate_value_list(tx_commit_data_t *tx_data, size_t cmd_no)
36775777Stw21770 {
36785777Stw21770 	const char *cp;
36795777Stw21770 	const char *cur_value;
36805777Stw21770 	size_t byte_count = 0;
36815777Stw21770 	uint32_t i;
36825777Stw21770 	uint32_t nvalues;
36835777Stw21770 	size_t str_size = 0;
36845777Stw21770 	char *values = NULL;
36855777Stw21770 	char *vp;
36865777Stw21770 
36875777Stw21770 	if (tx_cmd_nvalues(tx_data, cmd_no, &nvalues) != REP_PROTOCOL_SUCCESS)
36885777Stw21770 		return (NULL);
36895777Stw21770 	/*
36905777Stw21770 	 * First determine the size of the buffer that we will need.  We
36915777Stw21770 	 * will represent each property value surrounded by quotes with a
36925777Stw21770 	 * space separating the values.  Thus, we need to find the total
36935777Stw21770 	 * size of all the value strings and add 3 for each value.
36945777Stw21770 	 *
36955777Stw21770 	 * There is one catch, though.  We need to escape any internal
36965777Stw21770 	 * quote marks in the values.  So for each quote in the value we
36975777Stw21770 	 * need to add another byte to the buffer size.
36985777Stw21770 	 */
36995777Stw21770 	for (i = 0; i < nvalues; i++) {
37005777Stw21770 		if (tx_cmd_value(tx_data, cmd_no, i, &cur_value) !=
37015777Stw21770 		    REP_PROTOCOL_SUCCESS)
37025777Stw21770 			return (NULL);
37035777Stw21770 		for (cp = cur_value; *cp != 0; cp++) {
37045777Stw21770 			byte_count += (*cp == '"') ? 2 : 1;
37055777Stw21770 		}
37065777Stw21770 		byte_count += 3;	/* surrounding quotes & space */
37075777Stw21770 	}
37085777Stw21770 	byte_count++;		/* nul terminator */
37095777Stw21770 	values = malloc(byte_count);
37105777Stw21770 	if (values == NULL)
37115777Stw21770 		return (NULL);
37125777Stw21770 	*values = 0;
37135777Stw21770 
37145777Stw21770 	/* Now build up the string of values. */
37155777Stw21770 	for (i = 0; i < nvalues; i++) {
37165777Stw21770 		if (tx_cmd_value(tx_data, cmd_no, i, &cur_value) !=
37175777Stw21770 		    REP_PROTOCOL_SUCCESS) {
37185777Stw21770 			free(values);
37195777Stw21770 			return (NULL);
37205777Stw21770 		}
37215777Stw21770 		(void) strlcat(values, "\"", byte_count);
37225777Stw21770 		for (cp = cur_value, vp = values + strlen(values);
37235777Stw21770 		    *cp != 0; cp++) {
37245777Stw21770 			if (*cp == '"') {
37255777Stw21770 				*vp++ = '\\';
37265777Stw21770 				*vp++ = '"';
37275777Stw21770 			} else {
37285777Stw21770 				*vp++ = *cp;
37295777Stw21770 			}
37305777Stw21770 		}
37315777Stw21770 		*vp = 0;
37325777Stw21770 		str_size = strlcat(values, "\" ", byte_count);
37335777Stw21770 		assert(str_size < byte_count);
37345777Stw21770 	}
37355777Stw21770 	if (str_size > 0)
37365777Stw21770 		values[str_size - 1] = 0;	/* get rid of trailing space */
37375777Stw21770 	return (values);
37385777Stw21770 }
37395777Stw21770 
37405777Stw21770 /*
37415777Stw21770  * generate_property_events takes the transaction commit data at tx_data
37425777Stw21770  * and generates an audit event for each command.
37435777Stw21770  *
37445777Stw21770  * Native builds are done to create svc.configd-native.  This program runs
37455777Stw21770  * only on the Solaris build machines to create the seed repository.  Thus,
37465777Stw21770  * no audit events should be generated when running svc.configd-native.
37475777Stw21770  */
37485777Stw21770 static void
generate_property_events(tx_commit_data_t * tx_data,char * pg_fmri,char * auth_string,int auth_status,int auth_ret_value)37495777Stw21770 generate_property_events(
37505777Stw21770 	tx_commit_data_t *tx_data,
37515777Stw21770 	char *pg_fmri,		/* FMRI of property group */
37525777Stw21770 	char *auth_string,
37535777Stw21770 	int auth_status,
37545777Stw21770 	int auth_ret_value)
37555777Stw21770 {
37565777Stw21770 #ifndef	NATIVE_BUILD
37575777Stw21770 	enum rep_protocol_transaction_action action;
37585777Stw21770 	audit_event_data_t audit_data;
37595777Stw21770 	size_t count;
37605777Stw21770 	size_t cmd_no;
37615777Stw21770 	char *cp;
37625777Stw21770 	au_event_t event_id;
37635777Stw21770 	char fmri[REP_PROTOCOL_FMRI_LEN];
37645777Stw21770 	char pg_name[REP_PROTOCOL_NAME_LEN];
37655777Stw21770 	char *pg_end;		/* End of prop. group fmri */
37665777Stw21770 	const char *prop_name;
37675777Stw21770 	uint32_t ptype;
37685777Stw21770 	char prop_type[3];
37695777Stw21770 	enum rep_protocol_responseid rc;
37705777Stw21770 	size_t sz_out;
37715777Stw21770 
37725777Stw21770 	/* Make sure we have something to do. */
37735777Stw21770 	if (tx_data == NULL)
37745777Stw21770 		return;
37755777Stw21770 	if ((count = tx_cmd_count(tx_data)) == 0)
37765777Stw21770 		return;
37775777Stw21770 
37785777Stw21770 	/* Copy the property group fmri */
37795777Stw21770 	pg_end = fmri;
37805777Stw21770 	pg_end += strlcpy(fmri, pg_fmri, sizeof (fmri));
37815777Stw21770 
37825777Stw21770 	/*
37835777Stw21770 	 * Get the property group name.  It is the first component after
37845777Stw21770 	 * the last occurance of SCF_FMRI_PROPERTYGRP_PREFIX in the fmri.
37855777Stw21770 	 */
37865777Stw21770 	cp = strstr(pg_fmri, SCF_FMRI_PROPERTYGRP_PREFIX);
37875777Stw21770 	if (cp == NULL) {
37885777Stw21770 		pg_name[0] = 0;
37895777Stw21770 	} else {
37905777Stw21770 		cp += strlen(SCF_FMRI_PROPERTYGRP_PREFIX);
37915777Stw21770 		(void) strlcpy(pg_name, cp, sizeof (pg_name));
37925777Stw21770 	}
37935777Stw21770 
37945777Stw21770 	audit_data.ed_auth = auth_string;
37955777Stw21770 	audit_data.ed_fmri = fmri;
37965777Stw21770 	audit_data.ed_type = prop_type;
37975777Stw21770 
37985777Stw21770 	/*
37995777Stw21770 	 * Property type is two characters (see
38005777Stw21770 	 * rep_protocol_value_type_t), so terminate the string.
38015777Stw21770 	 */
38025777Stw21770 	prop_type[2] = 0;
38035777Stw21770 
38045777Stw21770 	for (cmd_no = 0; cmd_no < count; cmd_no++) {
38055777Stw21770 		/* Construct FMRI of the property */
38065777Stw21770 		*pg_end = 0;
38075777Stw21770 		if (tx_cmd_prop(tx_data, cmd_no, &prop_name) !=
38085777Stw21770 		    REP_PROTOCOL_SUCCESS) {
38095777Stw21770 			continue;
38105777Stw21770 		}
38115777Stw21770 		rc = rc_concat_fmri_element(fmri, sizeof (fmri), &sz_out,
38125777Stw21770 		    prop_name, REP_PROTOCOL_ENTITY_PROPERTY);
38135777Stw21770 		if (rc != REP_PROTOCOL_SUCCESS) {
38145777Stw21770 			/*
38155777Stw21770 			 * If we can't get the FMRI, we'll abandon this
38165777Stw21770 			 * command
38175777Stw21770 			 */
38185777Stw21770 			continue;
38195777Stw21770 		}
38205777Stw21770 
38215777Stw21770 		/* Generate special property event if necessary. */
38225777Stw21770 		special_property_event(&audit_data, prop_name, pg_name,
38235777Stw21770 		    auth_status, auth_ret_value, tx_data, cmd_no);
38245777Stw21770 
38255777Stw21770 		/* Capture rest of audit data. */
38265777Stw21770 		if (tx_cmd_prop_type(tx_data, cmd_no, &ptype) !=
38275777Stw21770 		    REP_PROTOCOL_SUCCESS) {
38285777Stw21770 			continue;
38295777Stw21770 		}
38305777Stw21770 		prop_type[0] = REP_PROTOCOL_BASE_TYPE(ptype);
38315777Stw21770 		prop_type[1] = REP_PROTOCOL_SUBTYPE(ptype);
38325777Stw21770 		audit_data.ed_prop_value = generate_value_list(tx_data, cmd_no);
38335777Stw21770 
38345777Stw21770 		/* Determine the event type. */
38355777Stw21770 		if (tx_cmd_action(tx_data, cmd_no, &action) !=
38365777Stw21770 		    REP_PROTOCOL_SUCCESS) {
38375777Stw21770 			free(audit_data.ed_prop_value);
38385777Stw21770 			continue;
38395777Stw21770 		}
38405777Stw21770 		switch (action) {
38415777Stw21770 		case REP_PROTOCOL_TX_ENTRY_NEW:
38425777Stw21770 			event_id = ADT_smf_create_prop;
38435777Stw21770 			break;
38445777Stw21770 		case REP_PROTOCOL_TX_ENTRY_CLEAR:
38455777Stw21770 			event_id = ADT_smf_change_prop;
38465777Stw21770 			break;
38475777Stw21770 		case REP_PROTOCOL_TX_ENTRY_REPLACE:
38485777Stw21770 			event_id = ADT_smf_change_prop;
38495777Stw21770 			break;
38505777Stw21770 		case REP_PROTOCOL_TX_ENTRY_DELETE:
38515777Stw21770 			event_id = ADT_smf_delete_prop;
38525777Stw21770 			break;
38535777Stw21770 		default:
38545777Stw21770 			assert(0);	/* Missing a case */
38555777Stw21770 			free(audit_data.ed_prop_value);
38565777Stw21770 			continue;
38575777Stw21770 		}
38585777Stw21770 
38595777Stw21770 		/* Generate the event. */
38605777Stw21770 		smf_audit_event(event_id, auth_status, auth_ret_value,
38615777Stw21770 		    &audit_data);
38625777Stw21770 		free(audit_data.ed_prop_value);
38635777Stw21770 	}
38645777Stw21770 #endif /* NATIVE_BUILD */
38650Sstevel@tonic-gate }
38660Sstevel@tonic-gate 
38670Sstevel@tonic-gate /*
38680Sstevel@tonic-gate  * Fails with
38690Sstevel@tonic-gate  *   _DELETED - node has been deleted
38700Sstevel@tonic-gate  *   _NOT_SET - npp is reset
38710Sstevel@tonic-gate  *   _NOT_APPLICABLE - type is _PROPERTYGRP
38720Sstevel@tonic-gate  *   _INVALID_TYPE - node is corrupt or type is invalid
38730Sstevel@tonic-gate  *   _TYPE_MISMATCH - node cannot have children of type type
38740Sstevel@tonic-gate  *   _BAD_REQUEST - name is invalid
38750Sstevel@tonic-gate  *		    cannot create children for this type of node
38760Sstevel@tonic-gate  *   _NO_RESOURCES - out of memory, or could not allocate new id
38770Sstevel@tonic-gate  *   _PERMISSION_DENIED
38780Sstevel@tonic-gate  *   _BACKEND_ACCESS
38790Sstevel@tonic-gate  *   _BACKEND_READONLY
38800Sstevel@tonic-gate  *   _EXISTS - child already exists
38815777Stw21770  *   _TRUNCATED - truncated FMRI for the audit record
38820Sstevel@tonic-gate  */
38830Sstevel@tonic-gate int
rc_node_create_child(rc_node_ptr_t * npp,uint32_t type,const char * name,rc_node_ptr_t * cpp)38840Sstevel@tonic-gate rc_node_create_child(rc_node_ptr_t *npp, uint32_t type, const char *name,
38850Sstevel@tonic-gate     rc_node_ptr_t *cpp)
38860Sstevel@tonic-gate {
38870Sstevel@tonic-gate 	rc_node_t *np;
38880Sstevel@tonic-gate 	rc_node_t *cp = NULL;
38898497SThomas.Whitten@Sun.COM 	int rc;
38908497SThomas.Whitten@Sun.COM 	perm_status_t perm_rc;
38915777Stw21770 	size_t sz_out;
38925777Stw21770 	char fmri[REP_PROTOCOL_FMRI_LEN];
38935777Stw21770 	audit_event_data_t audit_data;
38940Sstevel@tonic-gate 
38950Sstevel@tonic-gate 	rc_node_clear(cpp, 0);
38960Sstevel@tonic-gate 
38978497SThomas.Whitten@Sun.COM 	/*
38988497SThomas.Whitten@Sun.COM 	 * rc_node_modify_permission_check() must be called before the node
38998497SThomas.Whitten@Sun.COM 	 * is locked.  This is because the library functions that check
39008497SThomas.Whitten@Sun.COM 	 * authorizations can trigger calls back into configd.
39018497SThomas.Whitten@Sun.COM 	 */
39025777Stw21770 	perm_rc = rc_node_modify_permission_check(&audit_data.ed_auth);
39038497SThomas.Whitten@Sun.COM 	switch (perm_rc) {
39048497SThomas.Whitten@Sun.COM 	case PERM_DENIED:
39058497SThomas.Whitten@Sun.COM 		/*
39068497SThomas.Whitten@Sun.COM 		 * We continue in this case, so that an audit event can be
39078497SThomas.Whitten@Sun.COM 		 * generated later in the function.
39088497SThomas.Whitten@Sun.COM 		 */
39098497SThomas.Whitten@Sun.COM 		break;
39108497SThomas.Whitten@Sun.COM 	case PERM_GRANTED:
39118497SThomas.Whitten@Sun.COM 		break;
39128497SThomas.Whitten@Sun.COM 	case PERM_GONE:
39138497SThomas.Whitten@Sun.COM 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
39148497SThomas.Whitten@Sun.COM 	case PERM_FAIL:
39158497SThomas.Whitten@Sun.COM 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
39168497SThomas.Whitten@Sun.COM 	default:
39178497SThomas.Whitten@Sun.COM 		bad_error(rc_node_modify_permission_check, perm_rc);
39188497SThomas.Whitten@Sun.COM 	}
39198497SThomas.Whitten@Sun.COM 
39208497SThomas.Whitten@Sun.COM 	RC_NODE_PTR_CHECK_LOCK_OR_FREE_RETURN(np, npp, audit_data.ed_auth);
39210Sstevel@tonic-gate 
39225777Stw21770 	audit_data.ed_fmri = fmri;
39235777Stw21770 
39240Sstevel@tonic-gate 	/*
39250Sstevel@tonic-gate 	 * there is a separate interface for creating property groups
39260Sstevel@tonic-gate 	 */
39270Sstevel@tonic-gate 	if (type == REP_PROTOCOL_ENTITY_PROPERTYGRP) {
39280Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
39295777Stw21770 		free(audit_data.ed_auth);
39300Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
39310Sstevel@tonic-gate 	}
39320Sstevel@tonic-gate 
39330Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
39340Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
39350Sstevel@tonic-gate 		np = np->rn_cchain[0];
39368497SThomas.Whitten@Sun.COM 		if ((rc = rc_node_check_and_lock(np)) != REP_PROTOCOL_SUCCESS) {
39378497SThomas.Whitten@Sun.COM 			free(audit_data.ed_auth);
39388497SThomas.Whitten@Sun.COM 			return (rc);
39398497SThomas.Whitten@Sun.COM 		}
39400Sstevel@tonic-gate 	}
39410Sstevel@tonic-gate 
39420Sstevel@tonic-gate 	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
39430Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS) {
39440Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
39455777Stw21770 		free(audit_data.ed_auth);
39460Sstevel@tonic-gate 		return (rc);
39470Sstevel@tonic-gate 	}
39480Sstevel@tonic-gate 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS) {
39490Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
39505777Stw21770 		free(audit_data.ed_auth);
39510Sstevel@tonic-gate 		return (rc);
39520Sstevel@tonic-gate 	}
39530Sstevel@tonic-gate 
39545777Stw21770 	if ((rc = rc_get_fmri_and_concat(np, fmri, sizeof (fmri), &sz_out,
39555777Stw21770 	    name, type)) != REP_PROTOCOL_SUCCESS) {
39565777Stw21770 		(void) pthread_mutex_unlock(&np->rn_lock);
39575777Stw21770 		free(audit_data.ed_auth);
39585777Stw21770 		return (rc);
39595777Stw21770 	}
39608497SThomas.Whitten@Sun.COM 	if (perm_rc == PERM_DENIED) {
39610Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
39625777Stw21770 		smf_audit_event(ADT_smf_create, ADT_FAILURE,
39635777Stw21770 		    ADT_FAIL_VALUE_AUTH, &audit_data);
39645777Stw21770 		free(audit_data.ed_auth);
39658497SThomas.Whitten@Sun.COM 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
39660Sstevel@tonic-gate 	}
39670Sstevel@tonic-gate 
39685777Stw21770 	HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
39695777Stw21770 	    audit_data.ed_auth);
39700Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
39710Sstevel@tonic-gate 
39720Sstevel@tonic-gate 	rc = object_create(np, type, name, &cp);
39730Sstevel@tonic-gate 	assert(rc != REP_PROTOCOL_FAIL_NOT_APPLICABLE);
39740Sstevel@tonic-gate 
39750Sstevel@tonic-gate 	if (rc == REP_PROTOCOL_SUCCESS) {
39760Sstevel@tonic-gate 		rc_node_assign(cpp, cp);
39770Sstevel@tonic-gate 		rc_node_rele(cp);
39780Sstevel@tonic-gate 	}
39790Sstevel@tonic-gate 
39800Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
39810Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
39820Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
39830Sstevel@tonic-gate 
39845777Stw21770 	if (rc == REP_PROTOCOL_SUCCESS) {
39855777Stw21770 		smf_audit_event(ADT_smf_create, ADT_SUCCESS, ADT_SUCCESS,
39865777Stw21770 		    &audit_data);
39875777Stw21770 	}
39885777Stw21770 
39895777Stw21770 	free(audit_data.ed_auth);
39905777Stw21770 
39910Sstevel@tonic-gate 	return (rc);
39920Sstevel@tonic-gate }
39930Sstevel@tonic-gate 
39940Sstevel@tonic-gate int
rc_node_create_child_pg(rc_node_ptr_t * npp,uint32_t type,const char * name,const char * pgtype,uint32_t flags,rc_node_ptr_t * cpp)39950Sstevel@tonic-gate rc_node_create_child_pg(rc_node_ptr_t *npp, uint32_t type, const char *name,
39960Sstevel@tonic-gate     const char *pgtype, uint32_t flags, rc_node_ptr_t *cpp)
39970Sstevel@tonic-gate {
39980Sstevel@tonic-gate 	rc_node_t *np;
39990Sstevel@tonic-gate 	rc_node_t *cp;
40000Sstevel@tonic-gate 	int rc;
40010Sstevel@tonic-gate 	permcheck_t *pcp;
40028497SThomas.Whitten@Sun.COM 	perm_status_t granted;
40035777Stw21770 	char fmri[REP_PROTOCOL_FMRI_LEN];
40045777Stw21770 	audit_event_data_t audit_data;
40055777Stw21770 	au_event_t event_id;
40065777Stw21770 	size_t sz_out;
40075777Stw21770 
40085777Stw21770 	audit_data.ed_auth = NULL;
40095777Stw21770 	audit_data.ed_fmri = fmri;
40105777Stw21770 	audit_data.ed_type = (char *)pgtype;
40110Sstevel@tonic-gate 
40120Sstevel@tonic-gate 	rc_node_clear(cpp, 0);
40130Sstevel@tonic-gate 
40140Sstevel@tonic-gate 	/* verify flags is valid */
40150Sstevel@tonic-gate 	if (flags & ~SCF_PG_FLAG_NONPERSISTENT)
40160Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
40170Sstevel@tonic-gate 
40180Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
40190Sstevel@tonic-gate 
40200Sstevel@tonic-gate 	if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
40210Sstevel@tonic-gate 		rc_node_rele(np);
40220Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
40230Sstevel@tonic-gate 	}
40240Sstevel@tonic-gate 
40250Sstevel@tonic-gate 	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
40260Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS) {
40270Sstevel@tonic-gate 		rc_node_rele(np);
40280Sstevel@tonic-gate 		return (rc);
40290Sstevel@tonic-gate 	}
40300Sstevel@tonic-gate 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS ||
40310Sstevel@tonic-gate 	    (rc = rc_check_pgtype_name(pgtype)) != REP_PROTOCOL_SUCCESS) {
40320Sstevel@tonic-gate 		rc_node_rele(np);
40330Sstevel@tonic-gate 		return (rc);
40340Sstevel@tonic-gate 	}
40350Sstevel@tonic-gate 
40365777Stw21770 #ifdef NATIVE_BUILD
40375496Sdm120769 	if (!client_is_privileged()) {
40380Sstevel@tonic-gate 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
40395777Stw21770 	}
40400Sstevel@tonic-gate #else
40415777Stw21770 	if (flags & SCF_PG_FLAG_NONPERSISTENT) {
40425777Stw21770 		event_id = ADT_smf_create_npg;
40435777Stw21770 	} else {
40445777Stw21770 		event_id = ADT_smf_create_pg;
40455777Stw21770 	}
40465777Stw21770 	if ((rc = rc_get_fmri_and_concat(np, fmri, sizeof (fmri), &sz_out,
40475777Stw21770 	    name, REP_PROTOCOL_ENTITY_PROPERTYGRP)) != REP_PROTOCOL_SUCCESS) {
40485777Stw21770 		rc_node_rele(np);
40495777Stw21770 		return (rc);
40505777Stw21770 	}
40515777Stw21770 
40525777Stw21770 	if (is_main_repository) {
40530Sstevel@tonic-gate 		/* Must have .smf.modify or smf.modify.<type> authorization */
40540Sstevel@tonic-gate 		pcp = pc_create();
40550Sstevel@tonic-gate 		if (pcp != NULL) {
40560Sstevel@tonic-gate 			rc = perm_add_enabling(pcp, AUTH_MODIFY);
40570Sstevel@tonic-gate 
40580Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS) {
40590Sstevel@tonic-gate 				const char * const auth =
40600Sstevel@tonic-gate 				    perm_auth_for_pgtype(pgtype);
40610Sstevel@tonic-gate 
40620Sstevel@tonic-gate 				if (auth != NULL)
40630Sstevel@tonic-gate 					rc = perm_add_enabling(pcp, auth);
40640Sstevel@tonic-gate 			}
40650Sstevel@tonic-gate 
40660Sstevel@tonic-gate 			/*
40670Sstevel@tonic-gate 			 * .manage or $action_authorization can be used to
40680Sstevel@tonic-gate 			 * create the actions pg and the general_ovr pg.
40690Sstevel@tonic-gate 			 */
40700Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS &&
40710Sstevel@tonic-gate 			    (flags & SCF_PG_FLAG_NONPERSISTENT) != 0 &&
40720Sstevel@tonic-gate 			    np->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE &&
40730Sstevel@tonic-gate 			    ((strcmp(name, AUTH_PG_ACTIONS) == 0 &&
40740Sstevel@tonic-gate 			    strcmp(pgtype, AUTH_PG_ACTIONS_TYPE) == 0) ||
40750Sstevel@tonic-gate 			    (strcmp(name, AUTH_PG_GENERAL_OVR) == 0 &&
40760Sstevel@tonic-gate 			    strcmp(pgtype, AUTH_PG_GENERAL_OVR_TYPE) == 0))) {
40770Sstevel@tonic-gate 				rc = perm_add_enabling(pcp, AUTH_MANAGE);
40780Sstevel@tonic-gate 
40790Sstevel@tonic-gate 				if (rc == REP_PROTOCOL_SUCCESS)
40800Sstevel@tonic-gate 					rc = perm_add_inst_action_auth(pcp, np);
40810Sstevel@tonic-gate 			}
40820Sstevel@tonic-gate 
40830Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS) {
40840Sstevel@tonic-gate 				granted = perm_granted(pcp);
40850Sstevel@tonic-gate 
40868497SThomas.Whitten@Sun.COM 				rc = map_granted_status(granted, pcp,
40878497SThomas.Whitten@Sun.COM 				    &audit_data.ed_auth);
40888497SThomas.Whitten@Sun.COM 				if (granted == PERM_GONE) {
40898497SThomas.Whitten@Sun.COM 					/* No auditing if client gone. */
40908497SThomas.Whitten@Sun.COM 					pc_free(pcp);
40918497SThomas.Whitten@Sun.COM 					rc_node_rele(np);
40928497SThomas.Whitten@Sun.COM 					return (rc);
40935777Stw21770 				}
40940Sstevel@tonic-gate 			}
40950Sstevel@tonic-gate 
40960Sstevel@tonic-gate 			pc_free(pcp);
40970Sstevel@tonic-gate 		} else {
40980Sstevel@tonic-gate 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
40990Sstevel@tonic-gate 		}
41000Sstevel@tonic-gate 
41015777Stw21770 	} else {
41025777Stw21770 		rc = REP_PROTOCOL_SUCCESS;
41035777Stw21770 	}
41040Sstevel@tonic-gate #endif /* NATIVE_BUILD */
41050Sstevel@tonic-gate 
41068497SThomas.Whitten@Sun.COM 
41075777Stw21770 	if (rc != REP_PROTOCOL_SUCCESS) {
41085777Stw21770 		rc_node_rele(np);
41098497SThomas.Whitten@Sun.COM 		if (rc != REP_PROTOCOL_FAIL_NO_RESOURCES) {
41108497SThomas.Whitten@Sun.COM 			smf_audit_event(event_id, ADT_FAILURE,
41118497SThomas.Whitten@Sun.COM 			    ADT_FAIL_VALUE_AUTH, &audit_data);
41128497SThomas.Whitten@Sun.COM 		}
41135777Stw21770 		if (audit_data.ed_auth != NULL)
41145777Stw21770 			free(audit_data.ed_auth);
41155777Stw21770 		return (rc);
41160Sstevel@tonic-gate 	}
41170Sstevel@tonic-gate 
41180Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
41195777Stw21770 	HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
41205777Stw21770 	    audit_data.ed_auth);
41210Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
41220Sstevel@tonic-gate 
41230Sstevel@tonic-gate 	rc = object_create_pg(np, type, name, pgtype, flags, &cp);
41240Sstevel@tonic-gate 
41250Sstevel@tonic-gate 	if (rc == REP_PROTOCOL_SUCCESS) {
41260Sstevel@tonic-gate 		rc_node_assign(cpp, cp);
41270Sstevel@tonic-gate 		rc_node_rele(cp);
41280Sstevel@tonic-gate 	}
41290Sstevel@tonic-gate 
41300Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
41310Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
41320Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
41330Sstevel@tonic-gate 
41345777Stw21770 	if (rc == REP_PROTOCOL_SUCCESS) {
41355777Stw21770 		smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS,
41365777Stw21770 		    &audit_data);
41375777Stw21770 	}
41385777Stw21770 	if (audit_data.ed_auth != NULL)
41395777Stw21770 		free(audit_data.ed_auth);
41405777Stw21770 
41410Sstevel@tonic-gate 	return (rc);
41420Sstevel@tonic-gate }
41430Sstevel@tonic-gate 
41440Sstevel@tonic-gate static void
rc_pg_notify_fire(rc_node_pg_notify_t * pnp)41450Sstevel@tonic-gate rc_pg_notify_fire(rc_node_pg_notify_t *pnp)
41460Sstevel@tonic-gate {
41470Sstevel@tonic-gate 	assert(MUTEX_HELD(&rc_pg_notify_lock));
41480Sstevel@tonic-gate 
41490Sstevel@tonic-gate 	if (pnp->rnpn_pg != NULL) {
41500Sstevel@tonic-gate 		uu_list_remove(pnp->rnpn_pg->rn_pg_notify_list, pnp);
41510Sstevel@tonic-gate 		(void) close(pnp->rnpn_fd);
41520Sstevel@tonic-gate 
41530Sstevel@tonic-gate 		pnp->rnpn_pg = NULL;
41540Sstevel@tonic-gate 		pnp->rnpn_fd = -1;
41550Sstevel@tonic-gate 	} else {
41560Sstevel@tonic-gate 		assert(pnp->rnpn_fd == -1);
41570Sstevel@tonic-gate 	}
41580Sstevel@tonic-gate }
41590Sstevel@tonic-gate 
41600Sstevel@tonic-gate static void
rc_notify_node_delete(rc_notify_delete_t * ndp,rc_node_t * np_arg)41610Sstevel@tonic-gate rc_notify_node_delete(rc_notify_delete_t *ndp, rc_node_t *np_arg)
41620Sstevel@tonic-gate {
41630Sstevel@tonic-gate 	rc_node_t *svc = NULL;
41640Sstevel@tonic-gate 	rc_node_t *inst = NULL;
41650Sstevel@tonic-gate 	rc_node_t *pg = NULL;
41660Sstevel@tonic-gate 	rc_node_t *np = np_arg;
41670Sstevel@tonic-gate 	rc_node_t *nnp;
41680Sstevel@tonic-gate 
41690Sstevel@tonic-gate 	while (svc == NULL) {
41700Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
41710Sstevel@tonic-gate 		if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
41720Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
41730Sstevel@tonic-gate 			goto cleanup;
41740Sstevel@tonic-gate 		}
41750Sstevel@tonic-gate 		nnp = np->rn_parent;
41760Sstevel@tonic-gate 		rc_node_hold_locked(np);	/* hold it in place */
41770Sstevel@tonic-gate 
41780Sstevel@tonic-gate 		switch (np->rn_id.rl_type) {
41790Sstevel@tonic-gate 		case REP_PROTOCOL_ENTITY_PROPERTYGRP:
41800Sstevel@tonic-gate 			assert(pg == NULL);
41810Sstevel@tonic-gate 			pg = np;
41820Sstevel@tonic-gate 			break;
41830Sstevel@tonic-gate 		case REP_PROTOCOL_ENTITY_INSTANCE:
41840Sstevel@tonic-gate 			assert(inst == NULL);
41850Sstevel@tonic-gate 			inst = np;
41860Sstevel@tonic-gate 			break;
41870Sstevel@tonic-gate 		case REP_PROTOCOL_ENTITY_SERVICE:
41880Sstevel@tonic-gate 			assert(svc == NULL);
41890Sstevel@tonic-gate 			svc = np;
41900Sstevel@tonic-gate 			break;
41910Sstevel@tonic-gate 		default:
41920Sstevel@tonic-gate 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
41930Sstevel@tonic-gate 			rc_node_rele_locked(np);
41940Sstevel@tonic-gate 			goto cleanup;
41950Sstevel@tonic-gate 		}
41960Sstevel@tonic-gate 
41970Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
41980Sstevel@tonic-gate 
41990Sstevel@tonic-gate 		np = nnp;
42000Sstevel@tonic-gate 		if (np == NULL)
42010Sstevel@tonic-gate 			goto cleanup;
42020Sstevel@tonic-gate 	}
42030Sstevel@tonic-gate 
42040Sstevel@tonic-gate 	rc_notify_deletion(ndp,
42050Sstevel@tonic-gate 	    svc->rn_name,
42060Sstevel@tonic-gate 	    inst != NULL ? inst->rn_name : NULL,
42070Sstevel@tonic-gate 	    pg != NULL ? pg->rn_name : NULL);
42080Sstevel@tonic-gate 
42090Sstevel@tonic-gate 	ndp = NULL;
42100Sstevel@tonic-gate 
42110Sstevel@tonic-gate cleanup:
42120Sstevel@tonic-gate 	if (ndp != NULL)
42130Sstevel@tonic-gate 		uu_free(ndp);
42140Sstevel@tonic-gate 
42150Sstevel@tonic-gate 	for (;;) {
42160Sstevel@tonic-gate 		if (svc != NULL) {
42170Sstevel@tonic-gate 			np = svc;
42180Sstevel@tonic-gate 			svc = NULL;
42190Sstevel@tonic-gate 		} else if (inst != NULL) {
42200Sstevel@tonic-gate 			np = inst;
42210Sstevel@tonic-gate 			inst = NULL;
42220Sstevel@tonic-gate 		} else if (pg != NULL) {
42230Sstevel@tonic-gate 			np = pg;
42240Sstevel@tonic-gate 			pg = NULL;
42250Sstevel@tonic-gate 		} else
42260Sstevel@tonic-gate 			break;
42270Sstevel@tonic-gate 
42280Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
42290Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_USING_PARENT);
42300Sstevel@tonic-gate 		rc_node_rele_locked(np);
42310Sstevel@tonic-gate 	}
42320Sstevel@tonic-gate }
42330Sstevel@tonic-gate 
42340Sstevel@tonic-gate /*
42357266Sbustos  * Hold RC_NODE_DYING_FLAGS on np's descendents.  If andformer is true, do
42367266Sbustos  * the same down the rn_former chain.
42370Sstevel@tonic-gate  */
42380Sstevel@tonic-gate static void
rc_node_delete_hold(rc_node_t * np,int andformer)42390Sstevel@tonic-gate rc_node_delete_hold(rc_node_t *np, int andformer)
42400Sstevel@tonic-gate {
42410Sstevel@tonic-gate 	rc_node_t *cp;
42420Sstevel@tonic-gate 
42430Sstevel@tonic-gate again:
42440Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
42450Sstevel@tonic-gate 	assert((np->rn_flags & RC_NODE_DYING_FLAGS) == RC_NODE_DYING_FLAGS);
42460Sstevel@tonic-gate 
42470Sstevel@tonic-gate 	for (cp = uu_list_first(np->rn_children); cp != NULL;
42480Sstevel@tonic-gate 	    cp = uu_list_next(np->rn_children, cp)) {
42490Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
42500Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
42510Sstevel@tonic-gate 		if (!rc_node_hold_flag(cp, RC_NODE_DYING_FLAGS)) {
42520Sstevel@tonic-gate 			/*
42530Sstevel@tonic-gate 			 * already marked as dead -- can't happen, since that
42540Sstevel@tonic-gate 			 * would require setting RC_NODE_CHILDREN_CHANGING
42550Sstevel@tonic-gate 			 * in np, and we're holding that...
42560Sstevel@tonic-gate 			 */
42570Sstevel@tonic-gate 			abort();
42580Sstevel@tonic-gate 		}
42590Sstevel@tonic-gate 		rc_node_delete_hold(cp, andformer);	/* recurse, drop lock */
42600Sstevel@tonic-gate 
42610Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
42620Sstevel@tonic-gate 	}
42630Sstevel@tonic-gate 	if (andformer && (cp = np->rn_former) != NULL) {
42640Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
42650Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
42660Sstevel@tonic-gate 		if (!rc_node_hold_flag(cp, RC_NODE_DYING_FLAGS))
42670Sstevel@tonic-gate 			abort();		/* can't happen, see above */
42680Sstevel@tonic-gate 		np = cp;
42690Sstevel@tonic-gate 		goto again;		/* tail-recurse down rn_former */
42700Sstevel@tonic-gate 	}
42710Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
42720Sstevel@tonic-gate }
42730Sstevel@tonic-gate 
42740Sstevel@tonic-gate /*
42750Sstevel@tonic-gate  * N.B.:  this function drops np->rn_lock on the way out.
42760Sstevel@tonic-gate  */
42770Sstevel@tonic-gate static void
rc_node_delete_rele(rc_node_t * np,int andformer)42780Sstevel@tonic-gate rc_node_delete_rele(rc_node_t *np, int andformer)
42790Sstevel@tonic-gate {
42800Sstevel@tonic-gate 	rc_node_t *cp;
42810Sstevel@tonic-gate 
42820Sstevel@tonic-gate again:
42830Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
42840Sstevel@tonic-gate 	assert((np->rn_flags & RC_NODE_DYING_FLAGS) == RC_NODE_DYING_FLAGS);
42850Sstevel@tonic-gate 
42860Sstevel@tonic-gate 	for (cp = uu_list_first(np->rn_children); cp != NULL;
42870Sstevel@tonic-gate 	    cp = uu_list_next(np->rn_children, cp)) {
42880Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
42890Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
42900Sstevel@tonic-gate 		rc_node_delete_rele(cp, andformer);	/* recurse, drop lock */
42910Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
42920Sstevel@tonic-gate 	}
42930Sstevel@tonic-gate 	if (andformer && (cp = np->rn_former) != NULL) {
42940Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
42950Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
42960Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
42970Sstevel@tonic-gate 
42980Sstevel@tonic-gate 		np = cp;
42990Sstevel@tonic-gate 		goto again;		/* tail-recurse down rn_former */
43000Sstevel@tonic-gate 	}
43010Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
43020Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
43030Sstevel@tonic-gate }
43040Sstevel@tonic-gate 
43050Sstevel@tonic-gate static void
rc_node_finish_delete(rc_node_t * cp)43060Sstevel@tonic-gate rc_node_finish_delete(rc_node_t *cp)
43070Sstevel@tonic-gate {
43080Sstevel@tonic-gate 	cache_bucket_t *bp;
43090Sstevel@tonic-gate 	rc_node_pg_notify_t *pnp;
43100Sstevel@tonic-gate 
43110Sstevel@tonic-gate 	assert(MUTEX_HELD(&cp->rn_lock));
43120Sstevel@tonic-gate 
43130Sstevel@tonic-gate 	if (!(cp->rn_flags & RC_NODE_OLD)) {
43140Sstevel@tonic-gate 		assert(cp->rn_flags & RC_NODE_IN_PARENT);
43150Sstevel@tonic-gate 		if (!rc_node_wait_flag(cp, RC_NODE_USING_PARENT)) {
43160Sstevel@tonic-gate 			abort();		/* can't happen, see above */
43170Sstevel@tonic-gate 		}
43180Sstevel@tonic-gate 		cp->rn_flags &= ~RC_NODE_IN_PARENT;
43190Sstevel@tonic-gate 		cp->rn_parent = NULL;
43205777Stw21770 		rc_node_free_fmri(cp);
43210Sstevel@tonic-gate 	}
43220Sstevel@tonic-gate 
43230Sstevel@tonic-gate 	cp->rn_flags |= RC_NODE_DEAD;
43240Sstevel@tonic-gate 
43250Sstevel@tonic-gate 	/*
43260Sstevel@tonic-gate 	 * If this node is not out-dated, we need to remove it from
43270Sstevel@tonic-gate 	 * the notify list and cache hash table.
43280Sstevel@tonic-gate 	 */
43290Sstevel@tonic-gate 	if (!(cp->rn_flags & RC_NODE_OLD)) {
43300Sstevel@tonic-gate 		assert(cp->rn_refs > 0);	/* can't go away yet */
43310Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&cp->rn_lock);
43320Sstevel@tonic-gate 
43330Sstevel@tonic-gate 		(void) pthread_mutex_lock(&rc_pg_notify_lock);
43340Sstevel@tonic-gate 		while ((pnp = uu_list_first(cp->rn_pg_notify_list)) != NULL)
43350Sstevel@tonic-gate 			rc_pg_notify_fire(pnp);
43360Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
43370Sstevel@tonic-gate 		rc_notify_remove_node(cp);
43380Sstevel@tonic-gate 
43390Sstevel@tonic-gate 		bp = cache_hold(cp->rn_hash);
43400Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
43410Sstevel@tonic-gate 		cache_remove_unlocked(bp, cp);
43420Sstevel@tonic-gate 		cache_release(bp);
43430Sstevel@tonic-gate 	}
43440Sstevel@tonic-gate }
43450Sstevel@tonic-gate 
43460Sstevel@tonic-gate /*
43477266Sbustos  * For each child, call rc_node_finish_delete() and recurse.  If andformer
43487266Sbustos  * is set, also recurse down rn_former.  Finally release np, which might
43497266Sbustos  * free it.
43500Sstevel@tonic-gate  */
43510Sstevel@tonic-gate static void
rc_node_delete_children(rc_node_t * np,int andformer)43520Sstevel@tonic-gate rc_node_delete_children(rc_node_t *np, int andformer)
43530Sstevel@tonic-gate {
43540Sstevel@tonic-gate 	rc_node_t *cp;
43550Sstevel@tonic-gate 
43560Sstevel@tonic-gate again:
43570Sstevel@tonic-gate 	assert(np->rn_refs > 0);
43580Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
43590Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_DEAD);
43600Sstevel@tonic-gate 
43610Sstevel@tonic-gate 	while ((cp = uu_list_first(np->rn_children)) != NULL) {
43620Sstevel@tonic-gate 		uu_list_remove(np->rn_children, cp);
43630Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
43640Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
43650Sstevel@tonic-gate 		rc_node_hold_locked(cp);	/* hold while we recurse */
43660Sstevel@tonic-gate 		rc_node_finish_delete(cp);
43670Sstevel@tonic-gate 		rc_node_delete_children(cp, andformer);	/* drops lock + ref */
43680Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
43690Sstevel@tonic-gate 	}
43700Sstevel@tonic-gate 
43710Sstevel@tonic-gate 	/*
43726748Srm88369 	 * When we drop cp's lock, all the children will be gone, so we
43730Sstevel@tonic-gate 	 * can release DYING_FLAGS.
43740Sstevel@tonic-gate 	 */
43750Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
43760Sstevel@tonic-gate 	if (andformer && (cp = np->rn_former) != NULL) {
43770Sstevel@tonic-gate 		np->rn_former = NULL;		/* unlink */
43780Sstevel@tonic-gate 		(void) pthread_mutex_lock(&cp->rn_lock);
43797266Sbustos 
43807266Sbustos 		/*
43817266Sbustos 		 * Register the ephemeral reference created by reading
43827266Sbustos 		 * np->rn_former into cp.  Note that the persistent
43837266Sbustos 		 * reference (np->rn_former) is locked because we haven't
43847266Sbustos 		 * dropped np's lock since we dropped its RC_NODE_IN_TX
43857266Sbustos 		 * (via RC_NODE_DYING_FLAGS).
43867266Sbustos 		 */
43877266Sbustos 		rc_node_hold_ephemeral_locked(cp);
43887266Sbustos 
43890Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
43906748Srm88369 		cp->rn_flags &= ~RC_NODE_ON_FORMER;
43910Sstevel@tonic-gate 
43920Sstevel@tonic-gate 		rc_node_hold_locked(cp);	/* hold while we loop */
43930Sstevel@tonic-gate 
43940Sstevel@tonic-gate 		rc_node_finish_delete(cp);
43950Sstevel@tonic-gate 
43960Sstevel@tonic-gate 		rc_node_rele(np);		/* drop the old reference */
43970Sstevel@tonic-gate 
43980Sstevel@tonic-gate 		np = cp;
43990Sstevel@tonic-gate 		goto again;		/* tail-recurse down rn_former */
44000Sstevel@tonic-gate 	}
44010Sstevel@tonic-gate 	rc_node_rele_locked(np);
44020Sstevel@tonic-gate }
44030Sstevel@tonic-gate 
44047266Sbustos /*
44057266Sbustos  * The last client or child reference to np, which must be either
44067266Sbustos  * RC_NODE_OLD or RC_NODE_DEAD, has been destroyed.  We'll destroy any
44077266Sbustos  * remaining references (e.g., rn_former) and call rc_node_destroy() to
44087266Sbustos  * free np.
44097266Sbustos  */
44100Sstevel@tonic-gate static void
rc_node_no_client_refs(rc_node_t * np)44117266Sbustos rc_node_no_client_refs(rc_node_t *np)
44120Sstevel@tonic-gate {
44130Sstevel@tonic-gate 	int unrefed;
44147266Sbustos 	rc_node_t *current, *cur;
44150Sstevel@tonic-gate 
44160Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
44170Sstevel@tonic-gate 	assert(np->rn_refs == 0);
44180Sstevel@tonic-gate 	assert(np->rn_other_refs == 0);
44190Sstevel@tonic-gate 	assert(np->rn_other_refs_held == 0);
44200Sstevel@tonic-gate 
44210Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_DEAD) {
44227266Sbustos 		/*
44237266Sbustos 		 * The node is DEAD, so the deletion code should have
44247266Sbustos 		 * destroyed all rn_children or rn_former references.
44257266Sbustos 		 * Since the last client or child reference has been
44267266Sbustos 		 * destroyed, we're free to destroy np.  Unless another
44277266Sbustos 		 * thread has an ephemeral reference, in which case we'll
44287266Sbustos 		 * pass the buck.
44297266Sbustos 		 */
44307266Sbustos 		if (np->rn_erefs > 1) {
44317266Sbustos 			--np->rn_erefs;
44327266Sbustos 			NODE_UNLOCK(np);
44337266Sbustos 			return;
44347266Sbustos 		}
44357266Sbustos 
44360Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
44370Sstevel@tonic-gate 		rc_node_destroy(np);
44380Sstevel@tonic-gate 		return;
44390Sstevel@tonic-gate 	}
44400Sstevel@tonic-gate 
44417266Sbustos 	/* We only collect DEAD and OLD nodes, thank you. */
44420Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_OLD);
44437266Sbustos 
44447266Sbustos 	/*
44457266Sbustos 	 * RC_NODE_UNREFED keeps multiple threads from processing OLD
44467266Sbustos 	 * nodes.  But it's vulnerable to unfriendly scheduling, so full
44477266Sbustos 	 * use of rn_erefs should supersede it someday.
44487266Sbustos 	 */
44490Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_UNREFED) {
44500Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
44510Sstevel@tonic-gate 		return;
44520Sstevel@tonic-gate 	}
44530Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_UNREFED;
44540Sstevel@tonic-gate 
44557266Sbustos 	/*
44567266Sbustos 	 * Now we'll remove the node from the rn_former chain and take its
44577266Sbustos 	 * DYING_FLAGS.
44587266Sbustos 	 */
44590Sstevel@tonic-gate 
44600Sstevel@tonic-gate 	/*
44617266Sbustos 	 * Since this node is OLD, it should be on an rn_former chain.  To
44627266Sbustos 	 * remove it, we must find the current in-hash object and grab its
44637266Sbustos 	 * RC_NODE_IN_TX flag to protect the entire rn_former chain.
44640Sstevel@tonic-gate 	 */
44657266Sbustos 
44667266Sbustos 	(void) pthread_mutex_unlock(&np->rn_lock);
44677266Sbustos 
44680Sstevel@tonic-gate 	for (;;) {
44697266Sbustos 		current = cache_lookup(&np->rn_id);
44707266Sbustos 
44717266Sbustos 		if (current == NULL) {
44720Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
44737266Sbustos 
44740Sstevel@tonic-gate 			if (np->rn_flags & RC_NODE_DEAD)
44750Sstevel@tonic-gate 				goto died;
44767266Sbustos 
44770Sstevel@tonic-gate 			/*
44780Sstevel@tonic-gate 			 * We are trying to unreference this node, but the
44790Sstevel@tonic-gate 			 * owner of the former list does not exist.  It must
44800Sstevel@tonic-gate 			 * be the case that another thread is deleting this
44810Sstevel@tonic-gate 			 * entire sub-branch, but has not yet reached us.
44820Sstevel@tonic-gate 			 * We will in short order be deleted.
44830Sstevel@tonic-gate 			 */
44840Sstevel@tonic-gate 			np->rn_flags &= ~RC_NODE_UNREFED;
44850Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
44860Sstevel@tonic-gate 			return;
44870Sstevel@tonic-gate 		}
44887266Sbustos 
44897266Sbustos 		if (current == np) {
44900Sstevel@tonic-gate 			/*
44910Sstevel@tonic-gate 			 * no longer unreferenced
44920Sstevel@tonic-gate 			 */
44930Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
44940Sstevel@tonic-gate 			np->rn_flags &= ~RC_NODE_UNREFED;
44957266Sbustos 			/* held in cache_lookup() */
44960Sstevel@tonic-gate 			rc_node_rele_locked(np);
44970Sstevel@tonic-gate 			return;
44980Sstevel@tonic-gate 		}
44997266Sbustos 
45007266Sbustos 		(void) pthread_mutex_lock(&current->rn_lock);
45017266Sbustos 		if (current->rn_flags & RC_NODE_OLD) {
45027266Sbustos 			/*
45037266Sbustos 			 * current has been replaced since we looked it
45047266Sbustos 			 * up.  Try again.
45057266Sbustos 			 */
45067266Sbustos 			/* held in cache_lookup() */
45077266Sbustos 			rc_node_rele_locked(current);
45087266Sbustos 			continue;
45097266Sbustos 		}
45107266Sbustos 
45117266Sbustos 		if (!rc_node_hold_flag(current, RC_NODE_IN_TX)) {
45127266Sbustos 			/*
45137266Sbustos 			 * current has been deleted since we looked it up.  Try
45147266Sbustos 			 * again.
45157266Sbustos 			 */
45167266Sbustos 			/* held in cache_lookup() */
45177266Sbustos 			rc_node_rele_locked(current);
45180Sstevel@tonic-gate 			continue;
45190Sstevel@tonic-gate 		}
45207266Sbustos 
45217266Sbustos 		/*
45227266Sbustos 		 * rc_node_hold_flag() might have dropped current's lock, so
45237266Sbustos 		 * check OLD again.
45247266Sbustos 		 */
45257266Sbustos 		if (!(current->rn_flags & RC_NODE_OLD)) {
45267266Sbustos 			/* Not old.  Stop looping. */
45277266Sbustos 			(void) pthread_mutex_unlock(&current->rn_lock);
45280Sstevel@tonic-gate 			break;
45290Sstevel@tonic-gate 		}
45307266Sbustos 
45317266Sbustos 		rc_node_rele_flag(current, RC_NODE_IN_TX);
45327266Sbustos 		rc_node_rele_locked(current);
45337266Sbustos 	}
45347266Sbustos 
45357266Sbustos 	/* To take np's RC_NODE_DYING_FLAGS, we need its lock. */
45360Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
45377266Sbustos 
45387266Sbustos 	/*
45397266Sbustos 	 * While we didn't have the lock, a thread may have added
45407266Sbustos 	 * a reference or changed the flags.
45417266Sbustos 	 */
45420Sstevel@tonic-gate 	if (!(np->rn_flags & (RC_NODE_OLD | RC_NODE_DEAD)) ||
45430Sstevel@tonic-gate 	    np->rn_refs != 0 || np->rn_other_refs != 0 ||
45440Sstevel@tonic-gate 	    np->rn_other_refs_held != 0) {
45450Sstevel@tonic-gate 		np->rn_flags &= ~RC_NODE_UNREFED;
45467266Sbustos 
45477266Sbustos 		(void) pthread_mutex_lock(&current->rn_lock);
45487266Sbustos 		rc_node_rele_flag(current, RC_NODE_IN_TX);
45497266Sbustos 		/* held by cache_lookup() */
45507266Sbustos 		rc_node_rele_locked(current);
45510Sstevel@tonic-gate 		return;
45520Sstevel@tonic-gate 	}
45530Sstevel@tonic-gate 
45540Sstevel@tonic-gate 	if (!rc_node_hold_flag(np, RC_NODE_DYING_FLAGS)) {
45557266Sbustos 		/*
45567266Sbustos 		 * Someone deleted the node while we were waiting for
45577266Sbustos 		 * DYING_FLAGS.  Undo the modifications to current.
45587266Sbustos 		 */
45590Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
45600Sstevel@tonic-gate 
45617266Sbustos 		rc_node_rele_flag(current, RC_NODE_IN_TX);
45627266Sbustos 		/* held by cache_lookup() */
45637266Sbustos 		rc_node_rele_locked(current);
45640Sstevel@tonic-gate 
45650Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
45660Sstevel@tonic-gate 		goto died;
45670Sstevel@tonic-gate 	}
45680Sstevel@tonic-gate 
45697266Sbustos 	/* Take RC_NODE_DYING_FLAGS on np's descendents. */
45707266Sbustos 	rc_node_delete_hold(np, 0);		/* drops np->rn_lock */
45717266Sbustos 
45727266Sbustos 	/* Mark np DEAD.  This requires the lock. */
45730Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
45747266Sbustos 
45757266Sbustos 	/* Recheck for new references. */
45760Sstevel@tonic-gate 	if (!(np->rn_flags & RC_NODE_OLD) ||
45770Sstevel@tonic-gate 	    np->rn_refs != 0 || np->rn_other_refs != 0 ||
45780Sstevel@tonic-gate 	    np->rn_other_refs_held != 0) {
45790Sstevel@tonic-gate 		np->rn_flags &= ~RC_NODE_UNREFED;
45807266Sbustos 		rc_node_delete_rele(np, 0);	/* drops np's lock */
45817266Sbustos 
45827266Sbustos 		(void) pthread_mutex_lock(&current->rn_lock);
45837266Sbustos 		rc_node_rele_flag(current, RC_NODE_IN_TX);
45847266Sbustos 		/* held by cache_lookup() */
45857266Sbustos 		rc_node_rele_locked(current);
45860Sstevel@tonic-gate 		return;
45870Sstevel@tonic-gate 	}
45880Sstevel@tonic-gate 
45890Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_DEAD;
45900Sstevel@tonic-gate 
45910Sstevel@tonic-gate 	/*
45927266Sbustos 	 * Delete the children.  This calls rc_node_rele_locked() on np at
45937266Sbustos 	 * the end, so add a reference to keep the count from going
45947266Sbustos 	 * negative.  It will recurse with RC_NODE_DEAD set, so we'll call
45957266Sbustos 	 * rc_node_destroy() above, but RC_NODE_UNREFED is also set, so it
45967266Sbustos 	 * shouldn't actually free() np.
45970Sstevel@tonic-gate 	 */
45987266Sbustos 	rc_node_hold_locked(np);
45997266Sbustos 	rc_node_delete_children(np, 0);		/* unlocks np */
46007266Sbustos 
46017266Sbustos 	/* Remove np from current's rn_former chain. */
46027266Sbustos 	(void) pthread_mutex_lock(&current->rn_lock);
46037266Sbustos 	for (cur = current; cur != NULL && cur->rn_former != np;
46040Sstevel@tonic-gate 	    cur = cur->rn_former)
46050Sstevel@tonic-gate 		;
46060Sstevel@tonic-gate 	assert(cur != NULL && cur != np);
46070Sstevel@tonic-gate 
46080Sstevel@tonic-gate 	cur->rn_former = np->rn_former;
46090Sstevel@tonic-gate 	np->rn_former = NULL;
46100Sstevel@tonic-gate 
46117266Sbustos 	rc_node_rele_flag(current, RC_NODE_IN_TX);
46127266Sbustos 	/* held by cache_lookup() */
46137266Sbustos 	rc_node_rele_locked(current);
46147266Sbustos 
46157266Sbustos 	/* Clear ON_FORMER and UNREFED, and destroy. */
46160Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
46170Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_ON_FORMER);
46180Sstevel@tonic-gate 	np->rn_flags &= ~(RC_NODE_UNREFED | RC_NODE_ON_FORMER);
46197266Sbustos 
46207266Sbustos 	if (np->rn_erefs > 1) {
46217266Sbustos 		/* Still referenced.  Stay execution. */
46227266Sbustos 		--np->rn_erefs;
46237266Sbustos 		NODE_UNLOCK(np);
46247266Sbustos 		return;
46257266Sbustos 	}
46267266Sbustos 
46270Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
46280Sstevel@tonic-gate 	rc_node_destroy(np);
46290Sstevel@tonic-gate 	return;
46300Sstevel@tonic-gate 
46310Sstevel@tonic-gate died:
46327266Sbustos 	/*
46337266Sbustos 	 * Another thread marked np DEAD.  If there still aren't any
46347266Sbustos 	 * persistent references, destroy the node.
46357266Sbustos 	 */
46360Sstevel@tonic-gate 	np->rn_flags &= ~RC_NODE_UNREFED;
46377266Sbustos 
46380Sstevel@tonic-gate 	unrefed = (np->rn_refs == 0 && np->rn_other_refs == 0 &&
46390Sstevel@tonic-gate 	    np->rn_other_refs_held == 0);
46407266Sbustos 
46417266Sbustos 	if (np->rn_erefs > 0)
46427266Sbustos 		--np->rn_erefs;
46437266Sbustos 
46447266Sbustos 	if (unrefed && np->rn_erefs > 0) {
46457266Sbustos 		NODE_UNLOCK(np);
46467266Sbustos 		return;
46477266Sbustos 	}
46487266Sbustos 
46490Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
46507266Sbustos 
46510Sstevel@tonic-gate 	if (unrefed)
46520Sstevel@tonic-gate 		rc_node_destroy(np);
46530Sstevel@tonic-gate }
46540Sstevel@tonic-gate 
46555777Stw21770 static au_event_t
get_delete_event_id(rep_protocol_entity_t entity,uint32_t pgflags)46565777Stw21770 get_delete_event_id(rep_protocol_entity_t entity, uint32_t pgflags)
46575777Stw21770 {
46585777Stw21770 	au_event_t	id = 0;
46595777Stw21770 
46605777Stw21770 #ifndef NATIVE_BUILD
46615777Stw21770 	switch (entity) {
46625777Stw21770 	case REP_PROTOCOL_ENTITY_SERVICE:
46635777Stw21770 	case REP_PROTOCOL_ENTITY_INSTANCE:
46645777Stw21770 		id = ADT_smf_delete;
46655777Stw21770 		break;
46665777Stw21770 	case REP_PROTOCOL_ENTITY_SNAPSHOT:
46675777Stw21770 		id = ADT_smf_delete_snap;
46685777Stw21770 		break;
46695777Stw21770 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
46705777Stw21770 	case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
46715777Stw21770 		if (pgflags & SCF_PG_FLAG_NONPERSISTENT) {
46725777Stw21770 			id = ADT_smf_delete_npg;
46735777Stw21770 		} else {
46745777Stw21770 			id = ADT_smf_delete_pg;
46755777Stw21770 		}
46765777Stw21770 		break;
46775777Stw21770 	default:
46785777Stw21770 		abort();
46795777Stw21770 	}
46805777Stw21770 #endif	/* NATIVE_BUILD */
46815777Stw21770 	return (id);
46825777Stw21770 }
46835777Stw21770 
46840Sstevel@tonic-gate /*
46850Sstevel@tonic-gate  * Fails with
46860Sstevel@tonic-gate  *   _NOT_SET
46870Sstevel@tonic-gate  *   _DELETED
46880Sstevel@tonic-gate  *   _BAD_REQUEST
46890Sstevel@tonic-gate  *   _PERMISSION_DENIED
46900Sstevel@tonic-gate  *   _NO_RESOURCES
46915777Stw21770  *   _TRUNCATED
46920Sstevel@tonic-gate  * and whatever object_delete() fails with.
46930Sstevel@tonic-gate  */
46940Sstevel@tonic-gate int
rc_node_delete(rc_node_ptr_t * npp)46950Sstevel@tonic-gate rc_node_delete(rc_node_ptr_t *npp)
46960Sstevel@tonic-gate {
46970Sstevel@tonic-gate 	rc_node_t *np, *np_orig;
46980Sstevel@tonic-gate 	rc_node_t *pp = NULL;
46990Sstevel@tonic-gate 	int rc;
47000Sstevel@tonic-gate 	rc_node_pg_notify_t *pnp;
47010Sstevel@tonic-gate 	cache_bucket_t *bp;
47020Sstevel@tonic-gate 	rc_notify_delete_t *ndp;
47030Sstevel@tonic-gate 	permcheck_t *pcp;
47040Sstevel@tonic-gate 	int granted;
47055777Stw21770 	au_event_t event_id = 0;
47065777Stw21770 	size_t sz_out;
47075777Stw21770 	audit_event_data_t audit_data;
47085777Stw21770 	int audit_failure = 0;
47090Sstevel@tonic-gate 
47100Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
47110Sstevel@tonic-gate 
47125777Stw21770 	audit_data.ed_fmri = NULL;
47135777Stw21770 	audit_data.ed_auth = NULL;
47145777Stw21770 	audit_data.ed_snapname = NULL;
47155777Stw21770 	audit_data.ed_type = NULL;
47165777Stw21770 
47170Sstevel@tonic-gate 	switch (np->rn_id.rl_type) {
47180Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_SERVICE:
47195777Stw21770 		event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_SERVICE,
47205777Stw21770 		    np->rn_pgflags);
47215777Stw21770 		break;
47220Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_INSTANCE:
47235777Stw21770 		event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_INSTANCE,
47245777Stw21770 		    np->rn_pgflags);
47255777Stw21770 		break;
47260Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_SNAPSHOT:
47275777Stw21770 		event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_SNAPSHOT,
47285777Stw21770 		    np->rn_pgflags);
47295777Stw21770 		audit_data.ed_snapname = strdup(np->rn_name);
47305777Stw21770 		if (audit_data.ed_snapname == NULL) {
47315777Stw21770 			(void) pthread_mutex_unlock(&np->rn_lock);
47325777Stw21770 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
47335777Stw21770 		}
47340Sstevel@tonic-gate 		break;			/* deletable */
47350Sstevel@tonic-gate 
47360Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_SCOPE:
47370Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_SNAPLEVEL:
47380Sstevel@tonic-gate 		/* Scopes and snaplevels are indelible. */
47390Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
47400Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
47410Sstevel@tonic-gate 
47420Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
47430Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
47440Sstevel@tonic-gate 		np = np->rn_cchain[0];
47450Sstevel@tonic-gate 		RC_NODE_CHECK_AND_LOCK(np);
47465777Stw21770 		event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_CPROPERTYGRP,
47475777Stw21770 		    np->rn_pgflags);
47480Sstevel@tonic-gate 		break;
47490Sstevel@tonic-gate 
47500Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
47515777Stw21770 		if (np->rn_id.rl_ids[ID_SNAPSHOT] == 0) {
47525777Stw21770 			event_id =
47535777Stw21770 			    get_delete_event_id(REP_PROTOCOL_ENTITY_PROPERTYGRP,
47545777Stw21770 			    np->rn_pgflags);
47555777Stw21770 			audit_data.ed_type = strdup(np->rn_type);
47565777Stw21770 			if (audit_data.ed_type == NULL) {
47575777Stw21770 				(void) pthread_mutex_unlock(&np->rn_lock);
47585777Stw21770 				return (REP_PROTOCOL_FAIL_NO_RESOURCES);
47595777Stw21770 			}
47600Sstevel@tonic-gate 			break;
47615777Stw21770 		}
47620Sstevel@tonic-gate 
47630Sstevel@tonic-gate 		/* Snapshot property groups are indelible. */
47640Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
47650Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
47660Sstevel@tonic-gate 
47670Sstevel@tonic-gate 	case REP_PROTOCOL_ENTITY_PROPERTY:
47680Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
47690Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
47700Sstevel@tonic-gate 
47710Sstevel@tonic-gate 	default:
47720Sstevel@tonic-gate 		assert(0);
47730Sstevel@tonic-gate 		abort();
47740Sstevel@tonic-gate 		break;
47750Sstevel@tonic-gate 	}
47760Sstevel@tonic-gate 
47775777Stw21770 	audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
47785777Stw21770 	if (audit_data.ed_fmri == NULL) {
47795777Stw21770 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
47805777Stw21770 		goto cleanout;
47815777Stw21770 	}
47820Sstevel@tonic-gate 	np_orig = np;
47830Sstevel@tonic-gate 	rc_node_hold_locked(np);	/* simplifies rest of the code */
47840Sstevel@tonic-gate 
47850Sstevel@tonic-gate again:
47860Sstevel@tonic-gate 	/*
47870Sstevel@tonic-gate 	 * The following loop is to deal with the fact that snapshots and
47880Sstevel@tonic-gate 	 * property groups are moving targets -- changes to them result
47890Sstevel@tonic-gate 	 * in a new "child" node.  Since we can only delete from the top node,
47900Sstevel@tonic-gate 	 * we have to loop until we have a non-RC_NODE_OLD version.
47910Sstevel@tonic-gate 	 */
47920Sstevel@tonic-gate 	for (;;) {
47930Sstevel@tonic-gate 		if (!rc_node_wait_flag(np,
47940Sstevel@tonic-gate 		    RC_NODE_IN_TX | RC_NODE_USING_PARENT)) {
47950Sstevel@tonic-gate 			rc_node_rele_locked(np);
47965777Stw21770 			rc = REP_PROTOCOL_FAIL_DELETED;
47975777Stw21770 			goto cleanout;
47980Sstevel@tonic-gate 		}
47990Sstevel@tonic-gate 
48000Sstevel@tonic-gate 		if (np->rn_flags & RC_NODE_OLD) {
48010Sstevel@tonic-gate 			rc_node_rele_locked(np);
48020Sstevel@tonic-gate 			np = cache_lookup(&np_orig->rn_id);
48030Sstevel@tonic-gate 			assert(np != np_orig);
48040Sstevel@tonic-gate 
48050Sstevel@tonic-gate 			if (np == NULL) {
48060Sstevel@tonic-gate 				rc = REP_PROTOCOL_FAIL_DELETED;
48070Sstevel@tonic-gate 				goto fail;
48080Sstevel@tonic-gate 			}
48090Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
48100Sstevel@tonic-gate 			continue;
48110Sstevel@tonic-gate 		}
48120Sstevel@tonic-gate 
48130Sstevel@tonic-gate 		if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
48140Sstevel@tonic-gate 			rc_node_rele_locked(np);
48150Sstevel@tonic-gate 			rc_node_clear(npp, 1);
48165777Stw21770 			rc = REP_PROTOCOL_FAIL_DELETED;
48170Sstevel@tonic-gate 		}
48180Sstevel@tonic-gate 
48190Sstevel@tonic-gate 		/*
48200Sstevel@tonic-gate 		 * Mark our parent as children changing.  this call drops our
48210Sstevel@tonic-gate 		 * lock and the RC_NODE_USING_PARENT flag, and returns with
48220Sstevel@tonic-gate 		 * pp's lock held
48230Sstevel@tonic-gate 		 */
48240Sstevel@tonic-gate 		pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
48250Sstevel@tonic-gate 		if (pp == NULL) {
48260Sstevel@tonic-gate 			/* our parent is gone, we're going next... */
48270Sstevel@tonic-gate 			rc_node_rele(np);
48280Sstevel@tonic-gate 
48290Sstevel@tonic-gate 			rc_node_clear(npp, 1);
48305777Stw21770 			rc = REP_PROTOCOL_FAIL_DELETED;
48315777Stw21770 			goto cleanout;
48320Sstevel@tonic-gate 		}
48330Sstevel@tonic-gate 
48340Sstevel@tonic-gate 		rc_node_hold_locked(pp);		/* hold for later */
48350Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&pp->rn_lock);
48360Sstevel@tonic-gate 
48370Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
48380Sstevel@tonic-gate 		if (!(np->rn_flags & RC_NODE_OLD))
48390Sstevel@tonic-gate 			break;			/* not old -- we're done */
48400Sstevel@tonic-gate 
48410Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
48420Sstevel@tonic-gate 		(void) pthread_mutex_lock(&pp->rn_lock);
48430Sstevel@tonic-gate 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
48440Sstevel@tonic-gate 		rc_node_rele_locked(pp);
48450Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
48460Sstevel@tonic-gate 		continue;			/* loop around and try again */
48470Sstevel@tonic-gate 	}
48480Sstevel@tonic-gate 	/*
48490Sstevel@tonic-gate 	 * Everyone out of the pool -- we grab everything but
48500Sstevel@tonic-gate 	 * RC_NODE_USING_PARENT (including RC_NODE_DYING) to keep
48510Sstevel@tonic-gate 	 * any changes from occurring while we are attempting to
48520Sstevel@tonic-gate 	 * delete the node.
48530Sstevel@tonic-gate 	 */
48540Sstevel@tonic-gate 	if (!rc_node_hold_flag(np, RC_NODE_DYING_FLAGS)) {
48550Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
48560Sstevel@tonic-gate 		rc = REP_PROTOCOL_FAIL_DELETED;
48570Sstevel@tonic-gate 		goto fail;
48580Sstevel@tonic-gate 	}
48590Sstevel@tonic-gate 
48600Sstevel@tonic-gate 	assert(!(np->rn_flags & RC_NODE_OLD));
48610Sstevel@tonic-gate 
48625777Stw21770 	if ((rc = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
48635777Stw21770 	    REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
48645777Stw21770 		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
48655777Stw21770 		(void) pthread_mutex_unlock(&np->rn_lock);
48665777Stw21770 		goto fail;
48675777Stw21770 	}
48685777Stw21770 
48695777Stw21770 #ifdef NATIVE_BUILD
48705496Sdm120769 	if (!client_is_privileged()) {
48715777Stw21770 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
48725777Stw21770 	}
48735777Stw21770 #else
48745777Stw21770 	if (is_main_repository) {
48755496Sdm120769 		/* permission check */
48765405Stw21770 		(void) pthread_mutex_unlock(&np->rn_lock);
48770Sstevel@tonic-gate 		pcp = pc_create();
48780Sstevel@tonic-gate 		if (pcp != NULL) {
48790Sstevel@tonic-gate 			rc = perm_add_enabling(pcp, AUTH_MODIFY);
48800Sstevel@tonic-gate 
48810Sstevel@tonic-gate 			/* add .smf.modify.<type> for pgs. */
48820Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS && np->rn_id.rl_type ==
48830Sstevel@tonic-gate 			    REP_PROTOCOL_ENTITY_PROPERTYGRP) {
48840Sstevel@tonic-gate 				const char * const auth =
48850Sstevel@tonic-gate 				    perm_auth_for_pgtype(np->rn_type);
48860Sstevel@tonic-gate 
48870Sstevel@tonic-gate 				if (auth != NULL)
48880Sstevel@tonic-gate 					rc = perm_add_enabling(pcp, auth);
48890Sstevel@tonic-gate 			}
48900Sstevel@tonic-gate 
48910Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS) {
48920Sstevel@tonic-gate 				granted = perm_granted(pcp);
48930Sstevel@tonic-gate 
48948497SThomas.Whitten@Sun.COM 				rc = map_granted_status(granted, pcp,
48958497SThomas.Whitten@Sun.COM 				    &audit_data.ed_auth);
48968497SThomas.Whitten@Sun.COM 				if (granted == PERM_GONE) {
48978497SThomas.Whitten@Sun.COM 					/* No need to audit if client gone. */
48988497SThomas.Whitten@Sun.COM 					pc_free(pcp);
48998497SThomas.Whitten@Sun.COM 					rc_node_rele_flag(np,
49008497SThomas.Whitten@Sun.COM 					    RC_NODE_DYING_FLAGS);
49018497SThomas.Whitten@Sun.COM 					return (rc);
49025777Stw21770 				}
49038497SThomas.Whitten@Sun.COM 				if (granted == PERM_DENIED)
49048497SThomas.Whitten@Sun.COM 					audit_failure = 1;
49050Sstevel@tonic-gate 			}
49060Sstevel@tonic-gate 
49070Sstevel@tonic-gate 			pc_free(pcp);
49080Sstevel@tonic-gate 		} else {
49090Sstevel@tonic-gate 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
49100Sstevel@tonic-gate 		}
49110Sstevel@tonic-gate 
49125777Stw21770 		(void) pthread_mutex_lock(&np->rn_lock);
49135777Stw21770 	} else {
49145777Stw21770 		rc = REP_PROTOCOL_SUCCESS;
49155777Stw21770 	}
49165496Sdm120769 #endif /* NATIVE_BUILD */
49175496Sdm120769 
49185777Stw21770 	if (rc != REP_PROTOCOL_SUCCESS) {
49195777Stw21770 		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
49205777Stw21770 		(void) pthread_mutex_unlock(&np->rn_lock);
49215777Stw21770 		goto fail;
49220Sstevel@tonic-gate 	}
49230Sstevel@tonic-gate 
49240Sstevel@tonic-gate 	ndp = uu_zalloc(sizeof (*ndp));
49250Sstevel@tonic-gate 	if (ndp == NULL) {
49260Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
49270Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
49280Sstevel@tonic-gate 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
49290Sstevel@tonic-gate 		goto fail;
49300Sstevel@tonic-gate 	}
49310Sstevel@tonic-gate 
49320Sstevel@tonic-gate 	rc_node_delete_hold(np, 1);	/* hold entire subgraph, drop lock */
49330Sstevel@tonic-gate 
49340Sstevel@tonic-gate 	rc = object_delete(np);
49350Sstevel@tonic-gate 
49360Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS) {
49370Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
49380Sstevel@tonic-gate 		rc_node_delete_rele(np, 1);		/* drops lock */
49390Sstevel@tonic-gate 		uu_free(ndp);
49400Sstevel@tonic-gate 		goto fail;
49410Sstevel@tonic-gate 	}
49420Sstevel@tonic-gate 
49430Sstevel@tonic-gate 	/*
49440Sstevel@tonic-gate 	 * Now, delicately unlink and delete the object.
49450Sstevel@tonic-gate 	 *
49460Sstevel@tonic-gate 	 * Create the delete notification, atomically remove
49470Sstevel@tonic-gate 	 * from the hash table and set the NODE_DEAD flag, and
49480Sstevel@tonic-gate 	 * remove from the parent's children list.
49490Sstevel@tonic-gate 	 */
49500Sstevel@tonic-gate 	rc_notify_node_delete(ndp, np); /* frees or uses ndp */
49510Sstevel@tonic-gate 
49520Sstevel@tonic-gate 	bp = cache_hold(np->rn_hash);
49530Sstevel@tonic-gate 
49540Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
49550Sstevel@tonic-gate 	cache_remove_unlocked(bp, np);
49560Sstevel@tonic-gate 	cache_release(bp);
49570Sstevel@tonic-gate 
49580Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_DEAD;
49597266Sbustos 
49600Sstevel@tonic-gate 	if (pp != NULL) {
49617266Sbustos 		/*
49627266Sbustos 		 * Remove from pp's rn_children.  This requires pp's lock,
49637266Sbustos 		 * so we must drop np's lock to respect lock order.
49647266Sbustos 		 */
49650Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
49660Sstevel@tonic-gate 		(void) pthread_mutex_lock(&pp->rn_lock);
49670Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
49687266Sbustos 
49690Sstevel@tonic-gate 		uu_list_remove(pp->rn_children, np);
49707266Sbustos 
49710Sstevel@tonic-gate 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
49727266Sbustos 
49730Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&pp->rn_lock);
49747266Sbustos 
49750Sstevel@tonic-gate 		np->rn_flags &= ~RC_NODE_IN_PARENT;
49760Sstevel@tonic-gate 	}
49777266Sbustos 
49780Sstevel@tonic-gate 	/*
49797266Sbustos 	 * finally, propagate death to our children (including marking
49807266Sbustos 	 * them DEAD), handle notifications, and release our hold.
49810Sstevel@tonic-gate 	 */
49820Sstevel@tonic-gate 	rc_node_hold_locked(np);	/* hold for delete */
49830Sstevel@tonic-gate 	rc_node_delete_children(np, 1);	/* drops DYING_FLAGS, lock, ref */
49840Sstevel@tonic-gate 
49850Sstevel@tonic-gate 	rc_node_clear(npp, 1);
49860Sstevel@tonic-gate 
49870Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
49880Sstevel@tonic-gate 	while ((pnp = uu_list_first(np->rn_pg_notify_list)) != NULL)
49890Sstevel@tonic-gate 		rc_pg_notify_fire(pnp);
49900Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
49910Sstevel@tonic-gate 	rc_notify_remove_node(np);
49920Sstevel@tonic-gate 
49930Sstevel@tonic-gate 	rc_node_rele(np);
49940Sstevel@tonic-gate 
49955777Stw21770 	smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS,
49965777Stw21770 	    &audit_data);
49975777Stw21770 	free(audit_data.ed_auth);
49985777Stw21770 	free(audit_data.ed_snapname);
49995777Stw21770 	free(audit_data.ed_type);
50005777Stw21770 	free(audit_data.ed_fmri);
50010Sstevel@tonic-gate 	return (rc);
50020Sstevel@tonic-gate 
50030Sstevel@tonic-gate fail:
50040Sstevel@tonic-gate 	rc_node_rele(np);
50050Sstevel@tonic-gate 	if (rc == REP_PROTOCOL_FAIL_DELETED)
50060Sstevel@tonic-gate 		rc_node_clear(npp, 1);
50070Sstevel@tonic-gate 	if (pp != NULL) {
50080Sstevel@tonic-gate 		(void) pthread_mutex_lock(&pp->rn_lock);
50090Sstevel@tonic-gate 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
50100Sstevel@tonic-gate 		rc_node_rele_locked(pp);	/* drop ref and lock */
50110Sstevel@tonic-gate 	}
50125777Stw21770 	if (audit_failure) {
50135777Stw21770 		smf_audit_event(event_id, ADT_FAILURE,
50145777Stw21770 		    ADT_FAIL_VALUE_AUTH, &audit_data);
50155777Stw21770 	}
50165777Stw21770 cleanout:
50175777Stw21770 	free(audit_data.ed_auth);
50185777Stw21770 	free(audit_data.ed_snapname);
50195777Stw21770 	free(audit_data.ed_type);
50205777Stw21770 	free(audit_data.ed_fmri);
50210Sstevel@tonic-gate 	return (rc);
50220Sstevel@tonic-gate }
50230Sstevel@tonic-gate 
50240Sstevel@tonic-gate int
rc_node_next_snaplevel(rc_node_ptr_t * npp,rc_node_ptr_t * cpp)50250Sstevel@tonic-gate rc_node_next_snaplevel(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
50260Sstevel@tonic-gate {
50270Sstevel@tonic-gate 	rc_node_t *np;
50280Sstevel@tonic-gate 	rc_node_t *cp, *pp;
50290Sstevel@tonic-gate 	int res;
50300Sstevel@tonic-gate 
50310Sstevel@tonic-gate 	rc_node_clear(cpp, 0);
50320Sstevel@tonic-gate 
50330Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
50340Sstevel@tonic-gate 
50350Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT &&
50360Sstevel@tonic-gate 	    np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL) {
50370Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
50380Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
50390Sstevel@tonic-gate 	}
50400Sstevel@tonic-gate 
50410Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_SNAPSHOT) {
50420Sstevel@tonic-gate 		if ((res = rc_node_fill_children(np,
50430Sstevel@tonic-gate 		    REP_PROTOCOL_ENTITY_SNAPLEVEL)) != REP_PROTOCOL_SUCCESS) {
50440Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
50450Sstevel@tonic-gate 			return (res);
50460Sstevel@tonic-gate 		}
50470Sstevel@tonic-gate 
50480Sstevel@tonic-gate 		for (cp = uu_list_first(np->rn_children);
50490Sstevel@tonic-gate 		    cp != NULL;
50500Sstevel@tonic-gate 		    cp = uu_list_next(np->rn_children, cp)) {
50510Sstevel@tonic-gate 			if (cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
50520Sstevel@tonic-gate 				continue;
50530Sstevel@tonic-gate 			rc_node_hold(cp);
50540Sstevel@tonic-gate 			break;
50550Sstevel@tonic-gate 		}
50560Sstevel@tonic-gate 
50570Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
50580Sstevel@tonic-gate 	} else {
50598497SThomas.Whitten@Sun.COM 		if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
50608497SThomas.Whitten@Sun.COM 			(void) pthread_mutex_unlock(&np->rn_lock);
50618497SThomas.Whitten@Sun.COM 			rc_node_clear(npp, 1);
50628497SThomas.Whitten@Sun.COM 			return (REP_PROTOCOL_FAIL_DELETED);
50638497SThomas.Whitten@Sun.COM 		}
50648497SThomas.Whitten@Sun.COM 
50650Sstevel@tonic-gate 		/*
50660Sstevel@tonic-gate 		 * mark our parent as children changing.  This call drops our
50670Sstevel@tonic-gate 		 * lock and the RC_NODE_USING_PARENT flag, and returns with
50680Sstevel@tonic-gate 		 * pp's lock held
50690Sstevel@tonic-gate 		 */
50700Sstevel@tonic-gate 		pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
50710Sstevel@tonic-gate 		if (pp == NULL) {
50720Sstevel@tonic-gate 			/* our parent is gone, we're going next... */
50730Sstevel@tonic-gate 
50740Sstevel@tonic-gate 			rc_node_clear(npp, 1);
50750Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_DELETED);
50760Sstevel@tonic-gate 		}
50770Sstevel@tonic-gate 
50780Sstevel@tonic-gate 		/*
50790Sstevel@tonic-gate 		 * find the next snaplevel
50800Sstevel@tonic-gate 		 */
50810Sstevel@tonic-gate 		cp = np;
50820Sstevel@tonic-gate 		while ((cp = uu_list_next(pp->rn_children, cp)) != NULL &&
50830Sstevel@tonic-gate 		    cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
50840Sstevel@tonic-gate 			;
50850Sstevel@tonic-gate 
50860Sstevel@tonic-gate 		/* it must match the snaplevel list */
50870Sstevel@tonic-gate 		assert((cp == NULL && np->rn_snaplevel->rsl_next == NULL) ||
50880Sstevel@tonic-gate 		    (cp != NULL && np->rn_snaplevel->rsl_next ==
50890Sstevel@tonic-gate 		    cp->rn_snaplevel));
50900Sstevel@tonic-gate 
50910Sstevel@tonic-gate 		if (cp != NULL)
50920Sstevel@tonic-gate 			rc_node_hold(cp);
50930Sstevel@tonic-gate 
50940Sstevel@tonic-gate 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
50950Sstevel@tonic-gate 
50960Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&pp->rn_lock);
50970Sstevel@tonic-gate 	}
50980Sstevel@tonic-gate 
50990Sstevel@tonic-gate 	rc_node_assign(cpp, cp);
51000Sstevel@tonic-gate 	if (cp != NULL) {
51010Sstevel@tonic-gate 		rc_node_rele(cp);
51020Sstevel@tonic-gate 
51030Sstevel@tonic-gate 		return (REP_PROTOCOL_SUCCESS);
51040Sstevel@tonic-gate 	}
51050Sstevel@tonic-gate 	return (REP_PROTOCOL_FAIL_NOT_FOUND);
51060Sstevel@tonic-gate }
51070Sstevel@tonic-gate 
51080Sstevel@tonic-gate /*
51090Sstevel@tonic-gate  * This call takes a snapshot (np) and either:
51100Sstevel@tonic-gate  *	an existing snapid (to be associated with np), or
51110Sstevel@tonic-gate  *	a non-NULL parentp (from which a new snapshot is taken, and associated
51120Sstevel@tonic-gate  *	    with np)
51130Sstevel@tonic-gate  *
51140Sstevel@tonic-gate  * To do the association, np is duplicated, the duplicate is made to
51150Sstevel@tonic-gate  * represent the new snapid, and np is replaced with the new rc_node_t on
51160Sstevel@tonic-gate  * np's parent's child list. np is placed on the new node's rn_former list,
51170Sstevel@tonic-gate  * and replaces np in cache_hash (so rc_node_update() will find the new one).
51185777Stw21770  *
51195777Stw21770  * old_fmri and old_name point to the original snap shot's FMRI and name.
51205777Stw21770  * These values are used when generating audit events.
51215777Stw21770  *
51225777Stw21770  * Fails with
51235777Stw21770  *	_BAD_REQUEST
51245777Stw21770  *	_BACKEND_READONLY
51255777Stw21770  *	_DELETED
51265777Stw21770  *	_NO_RESOURCES
51275777Stw21770  *	_TRUNCATED
51285777Stw21770  *	_TYPE_MISMATCH
51290Sstevel@tonic-gate  */
51300Sstevel@tonic-gate static int
rc_attach_snapshot(rc_node_t * np,uint32_t snapid,rc_node_t * parentp,char * old_fmri,char * old_name)51315777Stw21770 rc_attach_snapshot(
51325777Stw21770 	rc_node_t *np,
51335777Stw21770 	uint32_t snapid,
51345777Stw21770 	rc_node_t *parentp,
51355777Stw21770 	char *old_fmri,
51365777Stw21770 	char *old_name)
51370Sstevel@tonic-gate {
51380Sstevel@tonic-gate 	rc_node_t *np_orig;
51390Sstevel@tonic-gate 	rc_node_t *nnp, *prev;
51400Sstevel@tonic-gate 	rc_node_t *pp;
51410Sstevel@tonic-gate 	int rc;
51425777Stw21770 	size_t sz_out;
51438497SThomas.Whitten@Sun.COM 	perm_status_t granted;
51445777Stw21770 	au_event_t event_id;
51455777Stw21770 	audit_event_data_t audit_data;
51465777Stw21770 
51475777Stw21770 	if (parentp == NULL) {
51485777Stw21770 		assert(old_fmri != NULL);
51495777Stw21770 	} else {
51500Sstevel@tonic-gate 		assert(snapid == 0);
51515777Stw21770 	}
51520Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
51530Sstevel@tonic-gate 
51545777Stw21770 	/* Gather the audit data. */
51555777Stw21770 	/*
51565777Stw21770 	 * ADT_smf_* symbols may not be defined in the /usr/include header
51575777Stw21770 	 * files on the build machine.  Thus, the following if-else will
51585777Stw21770 	 * not be compiled when doing native builds.
51595777Stw21770 	 */
51605777Stw21770 #ifndef	NATIVE_BUILD
51615777Stw21770 	if (parentp == NULL) {
51625777Stw21770 		event_id = ADT_smf_attach_snap;
51635777Stw21770 	} else {
51645777Stw21770 		event_id = ADT_smf_create_snap;
51655777Stw21770 	}
51665777Stw21770 #endif	/* NATIVE_BUILD */
51675777Stw21770 	audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
51685777Stw21770 	audit_data.ed_snapname = malloc(REP_PROTOCOL_NAME_LEN);
51695777Stw21770 	if ((audit_data.ed_fmri == NULL) || (audit_data.ed_snapname == NULL)) {
51705777Stw21770 		(void) pthread_mutex_unlock(&np->rn_lock);
51715777Stw21770 		free(audit_data.ed_fmri);
51725777Stw21770 		free(audit_data.ed_snapname);
51735777Stw21770 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
51745777Stw21770 	}
51755777Stw21770 	audit_data.ed_auth = NULL;
51765777Stw21770 	if (strlcpy(audit_data.ed_snapname, np->rn_name,
51775777Stw21770 	    REP_PROTOCOL_NAME_LEN) >= REP_PROTOCOL_NAME_LEN) {
51785777Stw21770 		abort();
51795777Stw21770 	}
51805777Stw21770 	audit_data.ed_old_fmri = old_fmri;
51815777Stw21770 	audit_data.ed_old_name = old_name ? old_name : "NO NAME";
51825777Stw21770 
51835777Stw21770 	if (parentp == NULL) {
51845777Stw21770 		/*
51855777Stw21770 		 * In the attach case, get the instance FMRIs of the
51865777Stw21770 		 * snapshots.
51875777Stw21770 		 */
51885777Stw21770 		if ((rc = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
51895777Stw21770 		    REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
51905777Stw21770 			(void) pthread_mutex_unlock(&np->rn_lock);
51915777Stw21770 			free(audit_data.ed_fmri);
51925777Stw21770 			free(audit_data.ed_snapname);
51935777Stw21770 			return (rc);
51945777Stw21770 		}
51955777Stw21770 	} else {
51965777Stw21770 		/*
51975777Stw21770 		 * Capture the FMRI of the parent if we're actually going
51985777Stw21770 		 * to take the snapshot.
51995777Stw21770 		 */
52005777Stw21770 		if ((rc = rc_node_get_fmri_or_fragment(parentp,
52015777Stw21770 		    audit_data.ed_fmri, REP_PROTOCOL_FMRI_LEN, &sz_out)) !=
52025777Stw21770 		    REP_PROTOCOL_SUCCESS) {
52035777Stw21770 			(void) pthread_mutex_unlock(&np->rn_lock);
52045777Stw21770 			free(audit_data.ed_fmri);
52055777Stw21770 			free(audit_data.ed_snapname);
52065777Stw21770 			return (rc);
52075777Stw21770 		}
52085777Stw21770 	}
52095777Stw21770 
52100Sstevel@tonic-gate 	np_orig = np;
52110Sstevel@tonic-gate 	rc_node_hold_locked(np);		/* simplifies the remainder */
52120Sstevel@tonic-gate 
52135040Swesolows 	(void) pthread_mutex_unlock(&np->rn_lock);
52148497SThomas.Whitten@Sun.COM 	granted = rc_node_modify_permission_check(&audit_data.ed_auth);
52158497SThomas.Whitten@Sun.COM 	switch (granted) {
52168497SThomas.Whitten@Sun.COM 	case PERM_DENIED:
52175777Stw21770 		smf_audit_event(event_id, ADT_FAILURE, ADT_FAIL_VALUE_AUTH,
52185777Stw21770 		    &audit_data);
52198497SThomas.Whitten@Sun.COM 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
52208497SThomas.Whitten@Sun.COM 		rc_node_rele(np);
52215777Stw21770 		goto cleanout;
52228497SThomas.Whitten@Sun.COM 	case PERM_GRANTED:
52238497SThomas.Whitten@Sun.COM 		break;
52248497SThomas.Whitten@Sun.COM 	case PERM_GONE:
52258497SThomas.Whitten@Sun.COM 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
52268497SThomas.Whitten@Sun.COM 		rc_node_rele(np);
52278497SThomas.Whitten@Sun.COM 		goto cleanout;
52288497SThomas.Whitten@Sun.COM 	case PERM_FAIL:
52298497SThomas.Whitten@Sun.COM 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
52308497SThomas.Whitten@Sun.COM 		rc_node_rele(np);
52318497SThomas.Whitten@Sun.COM 		goto cleanout;
52328497SThomas.Whitten@Sun.COM 	default:
52338497SThomas.Whitten@Sun.COM 		bad_error(rc_node_modify_permission_check, granted);
52345777Stw21770 	}
52355040Swesolows 	(void) pthread_mutex_lock(&np->rn_lock);
52365040Swesolows 
52370Sstevel@tonic-gate 	/*
52380Sstevel@tonic-gate 	 * get the latest node, holding RC_NODE_IN_TX to keep the rn_former
52390Sstevel@tonic-gate 	 * list from changing.
52400Sstevel@tonic-gate 	 */
52410Sstevel@tonic-gate 	for (;;) {
52420Sstevel@tonic-gate 		if (!(np->rn_flags & RC_NODE_OLD)) {
52430Sstevel@tonic-gate 			if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
52440Sstevel@tonic-gate 				goto again;
52450Sstevel@tonic-gate 			}
52460Sstevel@tonic-gate 			pp = rc_node_hold_parent_flag(np,
52470Sstevel@tonic-gate 			    RC_NODE_CHILDREN_CHANGING);
52480Sstevel@tonic-gate 
52490Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
52500Sstevel@tonic-gate 			if (pp == NULL) {
52510Sstevel@tonic-gate 				goto again;
52520Sstevel@tonic-gate 			}
52530Sstevel@tonic-gate 			if (np->rn_flags & RC_NODE_OLD) {
52540Sstevel@tonic-gate 				rc_node_rele_flag(pp,
52550Sstevel@tonic-gate 				    RC_NODE_CHILDREN_CHANGING);
52560Sstevel@tonic-gate 				(void) pthread_mutex_unlock(&pp->rn_lock);
52570Sstevel@tonic-gate 				goto again;
52580Sstevel@tonic-gate 			}
52590Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&pp->rn_lock);
52600Sstevel@tonic-gate 
52610Sstevel@tonic-gate 			if (!rc_node_hold_flag(np, RC_NODE_IN_TX)) {
52620Sstevel@tonic-gate 				/*
52630Sstevel@tonic-gate 				 * Can't happen, since we're holding our
52640Sstevel@tonic-gate 				 * parent's CHILDREN_CHANGING flag...
52650Sstevel@tonic-gate 				 */
52660Sstevel@tonic-gate 				abort();
52670Sstevel@tonic-gate 			}
52680Sstevel@tonic-gate 			break;			/* everything's ready */
52690Sstevel@tonic-gate 		}
52700Sstevel@tonic-gate again:
52710Sstevel@tonic-gate 		rc_node_rele_locked(np);
52720Sstevel@tonic-gate 		np = cache_lookup(&np_orig->rn_id);
52730Sstevel@tonic-gate 
52745777Stw21770 		if (np == NULL) {
52755777Stw21770 			rc = REP_PROTOCOL_FAIL_DELETED;
52765777Stw21770 			goto cleanout;
52775777Stw21770 		}
52780Sstevel@tonic-gate 
52790Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
52800Sstevel@tonic-gate 	}
52810Sstevel@tonic-gate 
52820Sstevel@tonic-gate 	if (parentp != NULL) {
52830Sstevel@tonic-gate 		if (pp != parentp) {
52840Sstevel@tonic-gate 			rc = REP_PROTOCOL_FAIL_BAD_REQUEST;
52850Sstevel@tonic-gate 			goto fail;
52860Sstevel@tonic-gate 		}
52870Sstevel@tonic-gate 		nnp = NULL;
52880Sstevel@tonic-gate 	} else {
52890Sstevel@tonic-gate 		/*
52900Sstevel@tonic-gate 		 * look for a former node with the snapid we need.
52910Sstevel@tonic-gate 		 */
52920Sstevel@tonic-gate 		if (np->rn_snapshot_id == snapid) {
52930Sstevel@tonic-gate 			rc_node_rele_flag(np, RC_NODE_IN_TX);
52940Sstevel@tonic-gate 			rc_node_rele_locked(np);
52950Sstevel@tonic-gate 
52960Sstevel@tonic-gate 			(void) pthread_mutex_lock(&pp->rn_lock);
52970Sstevel@tonic-gate 			rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
52980Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&pp->rn_lock);
52995777Stw21770 			rc = REP_PROTOCOL_SUCCESS;	/* nothing to do */
53005777Stw21770 			goto cleanout;
53010Sstevel@tonic-gate 		}
53020Sstevel@tonic-gate 
53030Sstevel@tonic-gate 		prev = np;
53040Sstevel@tonic-gate 		while ((nnp = prev->rn_former) != NULL) {
53050Sstevel@tonic-gate 			if (nnp->rn_snapshot_id == snapid) {
53060Sstevel@tonic-gate 				rc_node_hold(nnp);
53070Sstevel@tonic-gate 				break;		/* existing node with that id */
53080Sstevel@tonic-gate 			}
53090Sstevel@tonic-gate 			prev = nnp;
53100Sstevel@tonic-gate 		}
53110Sstevel@tonic-gate 	}
53120Sstevel@tonic-gate 
53130Sstevel@tonic-gate 	if (nnp == NULL) {
53140Sstevel@tonic-gate 		prev = NULL;
53150Sstevel@tonic-gate 		nnp = rc_node_alloc();
53160Sstevel@tonic-gate 		if (nnp == NULL) {
53170Sstevel@tonic-gate 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
53180Sstevel@tonic-gate 			goto fail;
53190Sstevel@tonic-gate 		}
53200Sstevel@tonic-gate 
53210Sstevel@tonic-gate 		nnp->rn_id = np->rn_id;		/* structure assignment */
53220Sstevel@tonic-gate 		nnp->rn_hash = np->rn_hash;
53230Sstevel@tonic-gate 		nnp->rn_name = strdup(np->rn_name);
53240Sstevel@tonic-gate 		nnp->rn_snapshot_id = snapid;
53250Sstevel@tonic-gate 		nnp->rn_flags = RC_NODE_IN_TX | RC_NODE_USING_PARENT;
53260Sstevel@tonic-gate 
53270Sstevel@tonic-gate 		if (nnp->rn_name == NULL) {
53280Sstevel@tonic-gate 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
53290Sstevel@tonic-gate 			goto fail;
53300Sstevel@tonic-gate 		}
53310Sstevel@tonic-gate 	}
53320Sstevel@tonic-gate 
53330Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
53340Sstevel@tonic-gate 
53350Sstevel@tonic-gate 	rc = object_snapshot_attach(&np->rn_id, &snapid, (parentp != NULL));
53360Sstevel@tonic-gate 
53370Sstevel@tonic-gate 	if (parentp != NULL)
53380Sstevel@tonic-gate 		nnp->rn_snapshot_id = snapid;	/* fill in new snapid */
53390Sstevel@tonic-gate 	else
53400Sstevel@tonic-gate 		assert(nnp->rn_snapshot_id == snapid);
53410Sstevel@tonic-gate 
53420Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
53430Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS)
53440Sstevel@tonic-gate 		goto fail;
53450Sstevel@tonic-gate 
53460Sstevel@tonic-gate 	/*
53470Sstevel@tonic-gate 	 * fix up the former chain
53480Sstevel@tonic-gate 	 */
53490Sstevel@tonic-gate 	if (prev != NULL) {
53500Sstevel@tonic-gate 		prev->rn_former = nnp->rn_former;
53510Sstevel@tonic-gate 		(void) pthread_mutex_lock(&nnp->rn_lock);
53520Sstevel@tonic-gate 		nnp->rn_flags &= ~RC_NODE_ON_FORMER;
53530Sstevel@tonic-gate 		nnp->rn_former = NULL;
53540Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&nnp->rn_lock);
53550Sstevel@tonic-gate 	}
53560Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_OLD;
53570Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
53580Sstevel@tonic-gate 
53590Sstevel@tonic-gate 	/*
53600Sstevel@tonic-gate 	 * replace np with nnp
53610Sstevel@tonic-gate 	 */
53620Sstevel@tonic-gate 	rc_node_relink_child(pp, np, nnp);
53630Sstevel@tonic-gate 
53640Sstevel@tonic-gate 	rc_node_rele(np);
53655777Stw21770 	smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS, &audit_data);
53665777Stw21770 	rc = REP_PROTOCOL_SUCCESS;
53675777Stw21770 
53685777Stw21770 cleanout:
53695777Stw21770 	free(audit_data.ed_auth);
53705777Stw21770 	free(audit_data.ed_fmri);
53715777Stw21770 	free(audit_data.ed_snapname);
53725777Stw21770 	return (rc);
53730Sstevel@tonic-gate 
53740Sstevel@tonic-gate fail:
53750Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_IN_TX);
53760Sstevel@tonic-gate 	rc_node_rele_locked(np);
53770Sstevel@tonic-gate 	(void) pthread_mutex_lock(&pp->rn_lock);
53780Sstevel@tonic-gate 	rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
53790Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&pp->rn_lock);
53800Sstevel@tonic-gate 
53810Sstevel@tonic-gate 	if (nnp != NULL) {
53820Sstevel@tonic-gate 		if (prev == NULL)
53830Sstevel@tonic-gate 			rc_node_destroy(nnp);
53840Sstevel@tonic-gate 		else
53850Sstevel@tonic-gate 			rc_node_rele(nnp);
53860Sstevel@tonic-gate 	}
53870Sstevel@tonic-gate 
53885777Stw21770 	free(audit_data.ed_auth);
53895777Stw21770 	free(audit_data.ed_fmri);
53905777Stw21770 	free(audit_data.ed_snapname);
53910Sstevel@tonic-gate 	return (rc);
53920Sstevel@tonic-gate }
53930Sstevel@tonic-gate 
53940Sstevel@tonic-gate int
rc_snapshot_take_new(rc_node_ptr_t * npp,const char * svcname,const char * instname,const char * name,rc_node_ptr_t * outpp)53950Sstevel@tonic-gate rc_snapshot_take_new(rc_node_ptr_t *npp, const char *svcname,
53960Sstevel@tonic-gate     const char *instname, const char *name, rc_node_ptr_t *outpp)
53970Sstevel@tonic-gate {
53988497SThomas.Whitten@Sun.COM 	perm_status_t granted;
53990Sstevel@tonic-gate 	rc_node_t *np;
54000Sstevel@tonic-gate 	rc_node_t *outp = NULL;
54015040Swesolows 	int rc, perm_rc;
54025777Stw21770 	char fmri[REP_PROTOCOL_FMRI_LEN];
54035777Stw21770 	audit_event_data_t audit_data;
54045777Stw21770 	size_t sz_out;
54050Sstevel@tonic-gate 
54060Sstevel@tonic-gate 	rc_node_clear(outpp, 0);
54070Sstevel@tonic-gate 
54088497SThomas.Whitten@Sun.COM 	/*
54098497SThomas.Whitten@Sun.COM 	 * rc_node_modify_permission_check() must be called before the node
54108497SThomas.Whitten@Sun.COM 	 * is locked.  This is because the library functions that check
54118497SThomas.Whitten@Sun.COM 	 * authorizations can trigger calls back into configd.
54128497SThomas.Whitten@Sun.COM 	 */
54138497SThomas.Whitten@Sun.COM 	granted = rc_node_modify_permission_check(&audit_data.ed_auth);
54148497SThomas.Whitten@Sun.COM 	switch (granted) {
54158497SThomas.Whitten@Sun.COM 	case PERM_DENIED:
54168497SThomas.Whitten@Sun.COM 		/*
54178497SThomas.Whitten@Sun.COM 		 * We continue in this case, so that we can generate an
54188497SThomas.Whitten@Sun.COM 		 * audit event later in this function.
54198497SThomas.Whitten@Sun.COM 		 */
54208497SThomas.Whitten@Sun.COM 		perm_rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
54218497SThomas.Whitten@Sun.COM 		break;
54228497SThomas.Whitten@Sun.COM 	case PERM_GRANTED:
54238497SThomas.Whitten@Sun.COM 		perm_rc = REP_PROTOCOL_SUCCESS;
54248497SThomas.Whitten@Sun.COM 		break;
54258497SThomas.Whitten@Sun.COM 	case PERM_GONE:
54268497SThomas.Whitten@Sun.COM 		/* No need to produce audit event if client is gone. */
54278497SThomas.Whitten@Sun.COM 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
54288497SThomas.Whitten@Sun.COM 	case PERM_FAIL:
54298497SThomas.Whitten@Sun.COM 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
54308497SThomas.Whitten@Sun.COM 	default:
54318497SThomas.Whitten@Sun.COM 		bad_error("rc_node_modify_permission_check", granted);
54328497SThomas.Whitten@Sun.COM 		break;
54338497SThomas.Whitten@Sun.COM 	}
54348497SThomas.Whitten@Sun.COM 
54358497SThomas.Whitten@Sun.COM 	RC_NODE_PTR_CHECK_LOCK_OR_FREE_RETURN(np, npp, audit_data.ed_auth);
54360Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) {
54370Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
54385777Stw21770 		free(audit_data.ed_auth);
54390Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
54400Sstevel@tonic-gate 	}
54410Sstevel@tonic-gate 
54420Sstevel@tonic-gate 	rc = rc_check_type_name(REP_PROTOCOL_ENTITY_SNAPSHOT, name);
54430Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS) {
54440Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
54455777Stw21770 		free(audit_data.ed_auth);
54460Sstevel@tonic-gate 		return (rc);
54470Sstevel@tonic-gate 	}
54480Sstevel@tonic-gate 
54490Sstevel@tonic-gate 	if (svcname != NULL && (rc =
54500Sstevel@tonic-gate 	    rc_check_type_name(REP_PROTOCOL_ENTITY_SERVICE, svcname)) !=
54510Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS) {
54520Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
54535777Stw21770 		free(audit_data.ed_auth);
54540Sstevel@tonic-gate 		return (rc);
54550Sstevel@tonic-gate 	}
54560Sstevel@tonic-gate 
54570Sstevel@tonic-gate 	if (instname != NULL && (rc =
54580Sstevel@tonic-gate 	    rc_check_type_name(REP_PROTOCOL_ENTITY_INSTANCE, instname)) !=
54590Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS) {
54600Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
54615777Stw21770 		free(audit_data.ed_auth);
54620Sstevel@tonic-gate 		return (rc);
54630Sstevel@tonic-gate 	}
54640Sstevel@tonic-gate 
54655777Stw21770 	audit_data.ed_fmri = fmri;
54665777Stw21770 	audit_data.ed_snapname = (char *)name;
54675777Stw21770 
54685777Stw21770 	if ((rc = rc_node_get_fmri_or_fragment(np, fmri, sizeof (fmri),
54695777Stw21770 	    &sz_out)) != REP_PROTOCOL_SUCCESS) {
54705777Stw21770 		(void) pthread_mutex_unlock(&np->rn_lock);
54715777Stw21770 		free(audit_data.ed_auth);
54725777Stw21770 		return (rc);
54735777Stw21770 	}
54745040Swesolows 	if (perm_rc != REP_PROTOCOL_SUCCESS) {
54750Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
54765777Stw21770 		smf_audit_event(ADT_smf_create_snap, ADT_FAILURE,
54775777Stw21770 		    ADT_FAIL_VALUE_AUTH, &audit_data);
54785777Stw21770 		free(audit_data.ed_auth);
54795040Swesolows 		return (perm_rc);
54800Sstevel@tonic-gate 	}
54810Sstevel@tonic-gate 
54825777Stw21770 	HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
54835777Stw21770 	    audit_data.ed_auth);
54840Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
54850Sstevel@tonic-gate 
54860Sstevel@tonic-gate 	rc = object_snapshot_take_new(np, svcname, instname, name, &outp);
54870Sstevel@tonic-gate 
54880Sstevel@tonic-gate 	if (rc == REP_PROTOCOL_SUCCESS) {
54890Sstevel@tonic-gate 		rc_node_assign(outpp, outp);
54900Sstevel@tonic-gate 		rc_node_rele(outp);
54910Sstevel@tonic-gate 	}
54920Sstevel@tonic-gate 
54930Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
54940Sstevel@tonic-gate 	rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
54950Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
54960Sstevel@tonic-gate 
54975777Stw21770 	if (rc == REP_PROTOCOL_SUCCESS) {
54985777Stw21770 		smf_audit_event(ADT_smf_create_snap, ADT_SUCCESS, ADT_SUCCESS,
54995777Stw21770 		    &audit_data);
55005777Stw21770 	}
55015777Stw21770 	if (audit_data.ed_auth != NULL)
55025777Stw21770 		free(audit_data.ed_auth);
55030Sstevel@tonic-gate 	return (rc);
55040Sstevel@tonic-gate }
55050Sstevel@tonic-gate 
55060Sstevel@tonic-gate int
rc_snapshot_take_attach(rc_node_ptr_t * npp,rc_node_ptr_t * outpp)55070Sstevel@tonic-gate rc_snapshot_take_attach(rc_node_ptr_t *npp, rc_node_ptr_t *outpp)
55080Sstevel@tonic-gate {
55090Sstevel@tonic-gate 	rc_node_t *np, *outp;
55100Sstevel@tonic-gate 
55110Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK(np, npp);
55120Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) {
55130Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
55140Sstevel@tonic-gate 	}
55150Sstevel@tonic-gate 
55160Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(outp, outpp);
55170Sstevel@tonic-gate 	if (outp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
55180Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&outp->rn_lock);
55190Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
55200Sstevel@tonic-gate 	}
55210Sstevel@tonic-gate 
55225777Stw21770 	return (rc_attach_snapshot(outp, 0, np, NULL,
55235777Stw21770 	    NULL));					/* drops outp's lock */
55240Sstevel@tonic-gate }
55250Sstevel@tonic-gate 
55260Sstevel@tonic-gate int
rc_snapshot_attach(rc_node_ptr_t * npp,rc_node_ptr_t * cpp)55270Sstevel@tonic-gate rc_snapshot_attach(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
55280Sstevel@tonic-gate {
55290Sstevel@tonic-gate 	rc_node_t *np;
55300Sstevel@tonic-gate 	rc_node_t *cp;
55310Sstevel@tonic-gate 	uint32_t snapid;
55325777Stw21770 	char old_name[REP_PROTOCOL_NAME_LEN];
55335777Stw21770 	int rc;
55345777Stw21770 	size_t sz_out;
55355777Stw21770 	char old_fmri[REP_PROTOCOL_FMRI_LEN];
55360Sstevel@tonic-gate 
55370Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
55380Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
55390Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
55400Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
55410Sstevel@tonic-gate 	}
55420Sstevel@tonic-gate 	snapid = np->rn_snapshot_id;
55435777Stw21770 	rc = rc_node_get_fmri_or_fragment(np, old_fmri, sizeof (old_fmri),
55445777Stw21770 	    &sz_out);
55450Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
55465777Stw21770 	if (rc != REP_PROTOCOL_SUCCESS)
55475777Stw21770 		return (rc);
55485777Stw21770 	if (np->rn_name != NULL) {
55495777Stw21770 		if (strlcpy(old_name, np->rn_name, sizeof (old_name)) >=
55505777Stw21770 		    sizeof (old_name)) {
55515777Stw21770 			return (REP_PROTOCOL_FAIL_TRUNCATED);
55525777Stw21770 		}
55535777Stw21770 	}
55540Sstevel@tonic-gate 
55550Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(cp, cpp);
55560Sstevel@tonic-gate 	if (cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
55570Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&cp->rn_lock);
55580Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
55590Sstevel@tonic-gate 	}
55600Sstevel@tonic-gate 
55615777Stw21770 	rc = rc_attach_snapshot(cp, snapid, NULL,
55625777Stw21770 	    old_fmri, old_name);			/* drops cp's lock */
55635777Stw21770 	return (rc);
55640Sstevel@tonic-gate }
55650Sstevel@tonic-gate 
55660Sstevel@tonic-gate /*
55675040Swesolows  * If the pgname property group under ent has type pgtype, and it has a
55685040Swesolows  * propname property with type ptype, return _SUCCESS.  If pgtype is NULL,
55695040Swesolows  * it is not checked.  If ent is not a service node, we will return _SUCCESS if
55705040Swesolows  * a property meeting the requirements exists in either the instance or its
55715040Swesolows  * parent.
55725040Swesolows  *
55735040Swesolows  * Returns
55745040Swesolows  *   _SUCCESS - see above
55755040Swesolows  *   _DELETED - ent or one of its ancestors was deleted
55765040Swesolows  *   _NO_RESOURCES - no resources
55775040Swesolows  *   _NOT_FOUND - no matching property was found
55785040Swesolows  */
55795040Swesolows static int
rc_svc_prop_exists(rc_node_t * ent,const char * pgname,const char * pgtype,const char * propname,rep_protocol_value_type_t ptype)55805040Swesolows rc_svc_prop_exists(rc_node_t *ent, const char *pgname, const char *pgtype,
55815040Swesolows     const char *propname, rep_protocol_value_type_t ptype)
55825040Swesolows {
55835040Swesolows 	int ret;
55845040Swesolows 	rc_node_t *pg = NULL, *spg = NULL, *svc, *prop;
55855040Swesolows 
55865040Swesolows 	assert(!MUTEX_HELD(&ent->rn_lock));
55875040Swesolows 
55885040Swesolows 	(void) pthread_mutex_lock(&ent->rn_lock);
55895040Swesolows 	ret = rc_node_find_named_child(ent, pgname,
55905040Swesolows 	    REP_PROTOCOL_ENTITY_PROPERTYGRP, &pg);
55915040Swesolows 	(void) pthread_mutex_unlock(&ent->rn_lock);
55925040Swesolows 
55935040Swesolows 	switch (ret) {
55945040Swesolows 	case REP_PROTOCOL_SUCCESS:
55955040Swesolows 		break;
55965040Swesolows 
55975040Swesolows 	case REP_PROTOCOL_FAIL_DELETED:
55985040Swesolows 	case REP_PROTOCOL_FAIL_NO_RESOURCES:
55995040Swesolows 		return (ret);
56005040Swesolows 
56015040Swesolows 	default:
56025040Swesolows 		bad_error("rc_node_find_named_child", ret);
56035040Swesolows 	}
56045040Swesolows 
56055040Swesolows 	if (ent->rn_id.rl_type != REP_PROTOCOL_ENTITY_SERVICE) {
56065040Swesolows 		ret = rc_node_find_ancestor(ent, REP_PROTOCOL_ENTITY_SERVICE,
56075040Swesolows 		    &svc);
56085040Swesolows 		if (ret != REP_PROTOCOL_SUCCESS) {
56095040Swesolows 			assert(ret == REP_PROTOCOL_FAIL_DELETED);
56105040Swesolows 			if (pg != NULL)
56115040Swesolows 				rc_node_rele(pg);
56125040Swesolows 			return (ret);
56135040Swesolows 		}
56145040Swesolows 		assert(svc->rn_id.rl_type == REP_PROTOCOL_ENTITY_SERVICE);
56155040Swesolows 
56165040Swesolows 		(void) pthread_mutex_lock(&svc->rn_lock);
56175040Swesolows 		ret = rc_node_find_named_child(svc, pgname,
56185040Swesolows 		    REP_PROTOCOL_ENTITY_PROPERTYGRP, &spg);
56195040Swesolows 		(void) pthread_mutex_unlock(&svc->rn_lock);
56205040Swesolows 
56215040Swesolows 		rc_node_rele(svc);
56225040Swesolows 
56235040Swesolows 		switch (ret) {
56245040Swesolows 		case REP_PROTOCOL_SUCCESS:
56255040Swesolows 			break;
56265040Swesolows 
56275040Swesolows 		case REP_PROTOCOL_FAIL_DELETED:
56285040Swesolows 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
56295040Swesolows 			if (pg != NULL)
56305040Swesolows 				rc_node_rele(pg);
56315040Swesolows 			return (ret);
56325040Swesolows 
56335040Swesolows 		default:
56345040Swesolows 			bad_error("rc_node_find_named_child", ret);
56355040Swesolows 		}
56365040Swesolows 	}
56375040Swesolows 
56385040Swesolows 	if (pg != NULL &&
56395040Swesolows 	    pgtype != NULL && strcmp(pg->rn_type, pgtype) != 0) {
56405040Swesolows 		rc_node_rele(pg);
56415040Swesolows 		pg = NULL;
56425040Swesolows 	}
56435040Swesolows 
56445040Swesolows 	if (spg != NULL &&
56455040Swesolows 	    pgtype != NULL && strcmp(spg->rn_type, pgtype) != 0) {
56465040Swesolows 		rc_node_rele(spg);
56475040Swesolows 		spg = NULL;
56485040Swesolows 	}
56495040Swesolows 
56505040Swesolows 	if (pg == NULL) {
56515040Swesolows 		if (spg == NULL)
56525040Swesolows 			return (REP_PROTOCOL_FAIL_NOT_FOUND);
56535040Swesolows 		pg = spg;
56545040Swesolows 		spg = NULL;
56555040Swesolows 	}
56565040Swesolows 
56575040Swesolows 	/*
56585040Swesolows 	 * At this point, pg is non-NULL, and is a property group node of the
56595040Swesolows 	 * correct type.  spg, if non-NULL, is also a property group node of
56605040Swesolows 	 * the correct type.  Check for the property in pg first, then spg
56615040Swesolows 	 * (if applicable).
56625040Swesolows 	 */
56635040Swesolows 	(void) pthread_mutex_lock(&pg->rn_lock);
56645040Swesolows 	ret = rc_node_find_named_child(pg, propname,
56655040Swesolows 	    REP_PROTOCOL_ENTITY_PROPERTY, &prop);
56665040Swesolows 	(void) pthread_mutex_unlock(&pg->rn_lock);
56675040Swesolows 	rc_node_rele(pg);
56685040Swesolows 	switch (ret) {
56695040Swesolows 	case REP_PROTOCOL_SUCCESS:
56705040Swesolows 		if (prop != NULL) {
56715040Swesolows 			if (prop->rn_valtype == ptype) {
56725040Swesolows 				rc_node_rele(prop);
56735040Swesolows 				if (spg != NULL)
56745040Swesolows 					rc_node_rele(spg);
56755040Swesolows 				return (REP_PROTOCOL_SUCCESS);
56765040Swesolows 			}
56775040Swesolows 			rc_node_rele(prop);
56785040Swesolows 		}
56795040Swesolows 		break;
56805040Swesolows 
56815040Swesolows 	case REP_PROTOCOL_FAIL_NO_RESOURCES:
56825040Swesolows 		if (spg != NULL)
56835040Swesolows 			rc_node_rele(spg);
56845040Swesolows 		return (ret);
56855040Swesolows 
56865040Swesolows 	case REP_PROTOCOL_FAIL_DELETED:
56875040Swesolows 		break;
56885040Swesolows 
56895040Swesolows 	default:
56905040Swesolows 		bad_error("rc_node_find_named_child", ret);
56915040Swesolows 	}
56925040Swesolows 
56935040Swesolows 	if (spg == NULL)
56945040Swesolows 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
56955040Swesolows 
56965040Swesolows 	pg = spg;
56975040Swesolows 
56985040Swesolows 	(void) pthread_mutex_lock(&pg->rn_lock);
56995040Swesolows 	ret = rc_node_find_named_child(pg, propname,
57005040Swesolows 	    REP_PROTOCOL_ENTITY_PROPERTY, &prop);
57015040Swesolows 	(void) pthread_mutex_unlock(&pg->rn_lock);
57025040Swesolows 	rc_node_rele(pg);
57035040Swesolows 	switch (ret) {
57045040Swesolows 	case REP_PROTOCOL_SUCCESS:
57055040Swesolows 		if (prop != NULL) {
57065040Swesolows 			if (prop->rn_valtype == ptype) {
57075040Swesolows 				rc_node_rele(prop);
57085040Swesolows 				return (REP_PROTOCOL_SUCCESS);
57095040Swesolows 			}
57105040Swesolows 			rc_node_rele(prop);
57115040Swesolows 		}
57125040Swesolows 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
57135040Swesolows 
57145040Swesolows 	case REP_PROTOCOL_FAIL_NO_RESOURCES:
57155040Swesolows 		return (ret);
57165040Swesolows 
57175040Swesolows 	case REP_PROTOCOL_FAIL_DELETED:
57185040Swesolows 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
57195040Swesolows 
57205040Swesolows 	default:
57215040Swesolows 		bad_error("rc_node_find_named_child", ret);
57225040Swesolows 	}
57235040Swesolows 
57245040Swesolows 	return (REP_PROTOCOL_SUCCESS);
57255040Swesolows }
57265040Swesolows 
57275040Swesolows /*
57285040Swesolows  * Given a property group node, returns _SUCCESS if the property group may
57295040Swesolows  * be read without any special authorization.
57305040Swesolows  *
57315040Swesolows  * Fails with:
57325040Swesolows  *   _DELETED - np or an ancestor node was deleted
57335040Swesolows  *   _TYPE_MISMATCH - np does not refer to a property group
57345040Swesolows  *   _NO_RESOURCES - no resources
57355040Swesolows  *   _PERMISSION_DENIED - authorization is required
57365040Swesolows  */
57375040Swesolows static int
rc_node_pg_check_read_protect(rc_node_t * np)57385040Swesolows rc_node_pg_check_read_protect(rc_node_t *np)
57395040Swesolows {
57405040Swesolows 	int ret;
57415040Swesolows 	rc_node_t *ent;
57425040Swesolows 
57435040Swesolows 	assert(!MUTEX_HELD(&np->rn_lock));
57445040Swesolows 
57455040Swesolows 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
57465040Swesolows 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
57475040Swesolows 
57485040Swesolows 	if (strcmp(np->rn_type, SCF_GROUP_FRAMEWORK) == 0 ||
57495040Swesolows 	    strcmp(np->rn_type, SCF_GROUP_DEPENDENCY) == 0 ||
57505040Swesolows 	    strcmp(np->rn_type, SCF_GROUP_METHOD) == 0)
57515040Swesolows 		return (REP_PROTOCOL_SUCCESS);
57525040Swesolows 
57535040Swesolows 	ret = rc_node_parent(np, &ent);
57545040Swesolows 
57555040Swesolows 	if (ret != REP_PROTOCOL_SUCCESS)
57565040Swesolows 		return (ret);
57575040Swesolows 
57585040Swesolows 	ret = rc_svc_prop_exists(ent, np->rn_name, np->rn_type,
57595040Swesolows 	    AUTH_PROP_READ, REP_PROTOCOL_TYPE_STRING);
57605040Swesolows 
57615040Swesolows 	rc_node_rele(ent);
57625040Swesolows 
57635040Swesolows 	switch (ret) {
57645040Swesolows 	case REP_PROTOCOL_FAIL_NOT_FOUND:
57655040Swesolows 		return (REP_PROTOCOL_SUCCESS);
57665040Swesolows 	case REP_PROTOCOL_SUCCESS:
57675040Swesolows 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
57685040Swesolows 	case REP_PROTOCOL_FAIL_DELETED:
57695040Swesolows 	case REP_PROTOCOL_FAIL_NO_RESOURCES:
57705040Swesolows 		return (ret);
57715040Swesolows 	default:
57725040Swesolows 		bad_error("rc_svc_prop_exists", ret);
57735040Swesolows 	}
57745040Swesolows 
57755040Swesolows 	return (REP_PROTOCOL_SUCCESS);
57765040Swesolows }
57775040Swesolows 
57785040Swesolows /*
57795040Swesolows  * Fails with
57805040Swesolows  *   _DELETED - np's node or parent has been deleted
57815040Swesolows  *   _TYPE_MISMATCH - np's node is not a property
57825040Swesolows  *   _NO_RESOURCES - out of memory
57835040Swesolows  *   _PERMISSION_DENIED - no authorization to read this property's value(s)
57845040Swesolows  *   _BAD_REQUEST - np's parent is not a property group
57855040Swesolows  */
57865040Swesolows static int
rc_node_property_may_read(rc_node_t * np)57875040Swesolows rc_node_property_may_read(rc_node_t *np)
57885040Swesolows {
57898497SThomas.Whitten@Sun.COM 	int ret;
57908497SThomas.Whitten@Sun.COM 	perm_status_t granted = PERM_DENIED;
57915040Swesolows 	rc_node_t *pgp;
57925040Swesolows 	permcheck_t *pcp;
57935777Stw21770 	audit_event_data_t audit_data;
57945777Stw21770 	size_t sz_out;
57955040Swesolows 
57965040Swesolows 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
57975040Swesolows 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
57985040Swesolows 
57995040Swesolows 	if (client_is_privileged())
58005040Swesolows 		return (REP_PROTOCOL_SUCCESS);
58015040Swesolows 
58025040Swesolows #ifdef NATIVE_BUILD
58035040Swesolows 	return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
58045040Swesolows #else
58055040Swesolows 	ret = rc_node_parent(np, &pgp);
58065040Swesolows 
58075040Swesolows 	if (ret != REP_PROTOCOL_SUCCESS)
58085040Swesolows 		return (ret);
58095040Swesolows 
58105040Swesolows 	if (pgp->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
58115040Swesolows 		rc_node_rele(pgp);
58125040Swesolows 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
58135040Swesolows 	}
58145040Swesolows 
58155040Swesolows 	ret = rc_node_pg_check_read_protect(pgp);
58165040Swesolows 
58175040Swesolows 	if (ret != REP_PROTOCOL_FAIL_PERMISSION_DENIED) {
58185040Swesolows 		rc_node_rele(pgp);
58195040Swesolows 		return (ret);
58205040Swesolows 	}
58215040Swesolows 
58225040Swesolows 	pcp = pc_create();
58235040Swesolows 
58245040Swesolows 	if (pcp == NULL) {
58255040Swesolows 		rc_node_rele(pgp);
58265040Swesolows 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
58275040Swesolows 	}
58285040Swesolows 
58295040Swesolows 	ret = perm_add_enabling(pcp, AUTH_MODIFY);
58305040Swesolows 
58315040Swesolows 	if (ret == REP_PROTOCOL_SUCCESS) {
58325040Swesolows 		const char * const auth =
58335040Swesolows 		    perm_auth_for_pgtype(pgp->rn_type);
58345040Swesolows 
58355040Swesolows 		if (auth != NULL)
58365040Swesolows 			ret = perm_add_enabling(pcp, auth);
58375040Swesolows 	}
58385040Swesolows 
58395040Swesolows 	/*
58405040Swesolows 	 * If you are permitted to modify the value, you may also
58415040Swesolows 	 * read it.  This means that both the MODIFY and VALUE
58425040Swesolows 	 * authorizations are acceptable.  We don't allow requests
58435040Swesolows 	 * for AUTH_PROP_MODIFY if all you have is $AUTH_PROP_VALUE,
58445040Swesolows 	 * however, to avoid leaking possibly valuable information
58455040Swesolows 	 * since such a user can't change the property anyway.
58465040Swesolows 	 */
58475040Swesolows 	if (ret == REP_PROTOCOL_SUCCESS)
58485040Swesolows 		ret = perm_add_enabling_values(pcp, pgp,
58495040Swesolows 		    AUTH_PROP_MODIFY);
58505040Swesolows 
58515040Swesolows 	if (ret == REP_PROTOCOL_SUCCESS &&
58525040Swesolows 	    strcmp(np->rn_name, AUTH_PROP_MODIFY) != 0)
58535040Swesolows 		ret = perm_add_enabling_values(pcp, pgp,
58545040Swesolows 		    AUTH_PROP_VALUE);
58555040Swesolows 
58565040Swesolows 	if (ret == REP_PROTOCOL_SUCCESS)
58575040Swesolows 		ret = perm_add_enabling_values(pcp, pgp,
58585040Swesolows 		    AUTH_PROP_READ);
58595040Swesolows 
58605040Swesolows 	rc_node_rele(pgp);
58615040Swesolows 
58625040Swesolows 	if (ret == REP_PROTOCOL_SUCCESS) {
58635040Swesolows 		granted = perm_granted(pcp);
58648497SThomas.Whitten@Sun.COM 		if (granted == PERM_FAIL)
58655040Swesolows 			ret = REP_PROTOCOL_FAIL_NO_RESOURCES;
58668497SThomas.Whitten@Sun.COM 		if (granted == PERM_GONE)
58678497SThomas.Whitten@Sun.COM 			ret = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
58688497SThomas.Whitten@Sun.COM 	}
58698497SThomas.Whitten@Sun.COM 
58705777Stw21770 	if (ret == REP_PROTOCOL_SUCCESS) {
58715777Stw21770 		/* Generate a read_prop audit event. */
58725777Stw21770 		audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
58735777Stw21770 		if (audit_data.ed_fmri == NULL)
58745777Stw21770 			ret = REP_PROTOCOL_FAIL_NO_RESOURCES;
58755777Stw21770 	}
58768497SThomas.Whitten@Sun.COM 	if (ret == REP_PROTOCOL_SUCCESS) {
58778497SThomas.Whitten@Sun.COM 		ret = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
58788497SThomas.Whitten@Sun.COM 		    REP_PROTOCOL_FMRI_LEN, &sz_out);
58798497SThomas.Whitten@Sun.COM 	}
58805777Stw21770 	if (ret == REP_PROTOCOL_SUCCESS) {
58815777Stw21770 		int status;
58825777Stw21770 		int ret_value;
58835777Stw21770 
58848497SThomas.Whitten@Sun.COM 		if (granted == PERM_DENIED) {
58855777Stw21770 			status = ADT_FAILURE;
58865777Stw21770 			ret_value = ADT_FAIL_VALUE_AUTH;
58875777Stw21770 		} else {
58885777Stw21770 			status = ADT_SUCCESS;
58895777Stw21770 			ret_value = ADT_SUCCESS;
58905777Stw21770 		}
58915777Stw21770 		audit_data.ed_auth = pcp->pc_auth_string;
58925777Stw21770 		smf_audit_event(ADT_smf_read_prop,
58935777Stw21770 		    status, ret_value, &audit_data);
58945777Stw21770 	}
58955777Stw21770 	free(audit_data.ed_fmri);
58965040Swesolows 
58975040Swesolows 	pc_free(pcp);
58985040Swesolows 
58998497SThomas.Whitten@Sun.COM 	if ((ret == REP_PROTOCOL_SUCCESS) && (granted == PERM_DENIED))
59005040Swesolows 		ret = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
59015040Swesolows 
59025040Swesolows 	return (ret);
59035040Swesolows #endif	/* NATIVE_BUILD */
59045040Swesolows }
59055040Swesolows 
59065040Swesolows /*
59070Sstevel@tonic-gate  * Iteration
59080Sstevel@tonic-gate  */
59090Sstevel@tonic-gate static int
rc_iter_filter_name(rc_node_t * np,void * s)59100Sstevel@tonic-gate rc_iter_filter_name(rc_node_t *np, void *s)
59110Sstevel@tonic-gate {
59120Sstevel@tonic-gate 	const char *name = s;
59130Sstevel@tonic-gate 
59140Sstevel@tonic-gate 	return (strcmp(np->rn_name, name) == 0);
59150Sstevel@tonic-gate }
59160Sstevel@tonic-gate 
59170Sstevel@tonic-gate static int
rc_iter_filter_type(rc_node_t * np,void * s)59180Sstevel@tonic-gate rc_iter_filter_type(rc_node_t *np, void *s)
59190Sstevel@tonic-gate {
59200Sstevel@tonic-gate 	const char *type = s;
59210Sstevel@tonic-gate 
59220Sstevel@tonic-gate 	return (np->rn_type != NULL && strcmp(np->rn_type, type) == 0);
59230Sstevel@tonic-gate }
59240Sstevel@tonic-gate 
59250Sstevel@tonic-gate /*ARGSUSED*/
59260Sstevel@tonic-gate static int
rc_iter_null_filter(rc_node_t * np,void * s)59270Sstevel@tonic-gate rc_iter_null_filter(rc_node_t *np, void *s)
59280Sstevel@tonic-gate {
59290Sstevel@tonic-gate 	return (1);
59300Sstevel@tonic-gate }
59310Sstevel@tonic-gate 
59320Sstevel@tonic-gate /*
59330Sstevel@tonic-gate  * Allocate & initialize an rc_node_iter_t structure.  Essentially, ensure
59340Sstevel@tonic-gate  * np->rn_children is populated and call uu_list_walk_start(np->rn_children).
59350Sstevel@tonic-gate  * If successful, leaves a hold on np & increments np->rn_other_refs
59360Sstevel@tonic-gate  *
59370Sstevel@tonic-gate  * If composed is true, then set up for iteration across the top level of np's
59380Sstevel@tonic-gate  * composition chain.  If successful, leaves a hold on np and increments
59390Sstevel@tonic-gate  * rn_other_refs for the top level of np's composition chain.
59400Sstevel@tonic-gate  *
59410Sstevel@tonic-gate  * Fails with
59420Sstevel@tonic-gate  *   _NO_RESOURCES
59430Sstevel@tonic-gate  *   _INVALID_TYPE
59440Sstevel@tonic-gate  *   _TYPE_MISMATCH - np cannot carry type children
59450Sstevel@tonic-gate  *   _DELETED
59460Sstevel@tonic-gate  */
59470Sstevel@tonic-gate static int
rc_iter_create(rc_node_iter_t ** resp,rc_node_t * np,uint32_t type,rc_iter_filter_func * filter,void * arg,boolean_t composed)59480Sstevel@tonic-gate rc_iter_create(rc_node_iter_t **resp, rc_node_t *np, uint32_t type,
59490Sstevel@tonic-gate     rc_iter_filter_func *filter, void *arg, boolean_t composed)
59500Sstevel@tonic-gate {
59510Sstevel@tonic-gate 	rc_node_iter_t *nip;
59520Sstevel@tonic-gate 	int res;
59530Sstevel@tonic-gate 
59540Sstevel@tonic-gate 	assert(*resp == NULL);
59550Sstevel@tonic-gate 
59560Sstevel@tonic-gate 	nip = uu_zalloc(sizeof (*nip));
59570Sstevel@tonic-gate 	if (nip == NULL)
59580Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
59590Sstevel@tonic-gate 
59600Sstevel@tonic-gate 	/* np is held by the client's rc_node_ptr_t */
59610Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP)
59620Sstevel@tonic-gate 		composed = 1;
59630Sstevel@tonic-gate 
59640Sstevel@tonic-gate 	if (!composed) {
59650Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
59660Sstevel@tonic-gate 
59670Sstevel@tonic-gate 		if ((res = rc_node_fill_children(np, type)) !=
59680Sstevel@tonic-gate 		    REP_PROTOCOL_SUCCESS) {
59690Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
59700Sstevel@tonic-gate 			uu_free(nip);
59710Sstevel@tonic-gate 			return (res);
59720Sstevel@tonic-gate 		}
59730Sstevel@tonic-gate 
59740Sstevel@tonic-gate 		nip->rni_clevel = -1;
59750Sstevel@tonic-gate 
59760Sstevel@tonic-gate 		nip->rni_iter = uu_list_walk_start(np->rn_children,
59770Sstevel@tonic-gate 		    UU_WALK_ROBUST);
59780Sstevel@tonic-gate 		if (nip->rni_iter != NULL) {
59790Sstevel@tonic-gate 			nip->rni_iter_node = np;
59800Sstevel@tonic-gate 			rc_node_hold_other(np);
59810Sstevel@tonic-gate 		} else {
59820Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
59830Sstevel@tonic-gate 			uu_free(nip);
59840Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
59850Sstevel@tonic-gate 		}
59860Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
59870Sstevel@tonic-gate 	} else {
59880Sstevel@tonic-gate 		rc_node_t *ent;
59890Sstevel@tonic-gate 
59900Sstevel@tonic-gate 		if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_SNAPSHOT) {
59910Sstevel@tonic-gate 			/* rn_cchain isn't valid until children are loaded. */
59920Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
59930Sstevel@tonic-gate 			res = rc_node_fill_children(np,
59940Sstevel@tonic-gate 			    REP_PROTOCOL_ENTITY_SNAPLEVEL);
59950Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
59960Sstevel@tonic-gate 			if (res != REP_PROTOCOL_SUCCESS) {
59970Sstevel@tonic-gate 				uu_free(nip);
59980Sstevel@tonic-gate 				return (res);
59990Sstevel@tonic-gate 			}
60000Sstevel@tonic-gate 
60010Sstevel@tonic-gate 			/* Check for an empty snapshot. */
60020Sstevel@tonic-gate 			if (np->rn_cchain[0] == NULL)
60030Sstevel@tonic-gate 				goto empty;
60040Sstevel@tonic-gate 		}
60050Sstevel@tonic-gate 
60060Sstevel@tonic-gate 		/* Start at the top of the composition chain. */
60070Sstevel@tonic-gate 		for (nip->rni_clevel = 0; ; ++nip->rni_clevel) {
60080Sstevel@tonic-gate 			if (nip->rni_clevel >= COMPOSITION_DEPTH) {
60090Sstevel@tonic-gate 				/* Empty composition chain. */
60100Sstevel@tonic-gate empty:
60110Sstevel@tonic-gate 				nip->rni_clevel = -1;
60120Sstevel@tonic-gate 				nip->rni_iter = NULL;
60130Sstevel@tonic-gate 				/* It's ok, iter_next() will return _DONE. */
60140Sstevel@tonic-gate 				goto out;
60150Sstevel@tonic-gate 			}
60160Sstevel@tonic-gate 
60170Sstevel@tonic-gate 			ent = np->rn_cchain[nip->rni_clevel];
60180Sstevel@tonic-gate 			assert(ent != NULL);
60190Sstevel@tonic-gate 
60200Sstevel@tonic-gate 			if (rc_node_check_and_lock(ent) == REP_PROTOCOL_SUCCESS)
60210Sstevel@tonic-gate 				break;
60220Sstevel@tonic-gate 
60230Sstevel@tonic-gate 			/* Someone deleted it, so try the next one. */
60240Sstevel@tonic-gate 		}
60250Sstevel@tonic-gate 
60260Sstevel@tonic-gate 		res = rc_node_fill_children(ent, type);
60270Sstevel@tonic-gate 
60280Sstevel@tonic-gate 		if (res == REP_PROTOCOL_SUCCESS) {
60290Sstevel@tonic-gate 			nip->rni_iter = uu_list_walk_start(ent->rn_children,
60300Sstevel@tonic-gate 			    UU_WALK_ROBUST);
60310Sstevel@tonic-gate 
60320Sstevel@tonic-gate 			if (nip->rni_iter == NULL)
60330Sstevel@tonic-gate 				res = REP_PROTOCOL_FAIL_NO_RESOURCES;
60340Sstevel@tonic-gate 			else {
60350Sstevel@tonic-gate 				nip->rni_iter_node = ent;
60360Sstevel@tonic-gate 				rc_node_hold_other(ent);
60370Sstevel@tonic-gate 			}
60380Sstevel@tonic-gate 		}
60390Sstevel@tonic-gate 
60400Sstevel@tonic-gate 		if (res != REP_PROTOCOL_SUCCESS) {
60410Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&ent->rn_lock);
60420Sstevel@tonic-gate 			uu_free(nip);
60430Sstevel@tonic-gate 			return (res);
60440Sstevel@tonic-gate 		}
60450Sstevel@tonic-gate 
60460Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&ent->rn_lock);
60470Sstevel@tonic-gate 	}
60480Sstevel@tonic-gate 
60490Sstevel@tonic-gate out:
60500Sstevel@tonic-gate 	rc_node_hold(np);		/* released by rc_iter_end() */
60510Sstevel@tonic-gate 	nip->rni_parent = np;
60520Sstevel@tonic-gate 	nip->rni_type = type;
60530Sstevel@tonic-gate 	nip->rni_filter = (filter != NULL)? filter : rc_iter_null_filter;
60540Sstevel@tonic-gate 	nip->rni_filter_arg = arg;
60550Sstevel@tonic-gate 	*resp = nip;
60560Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
60570Sstevel@tonic-gate }
60580Sstevel@tonic-gate 
60590Sstevel@tonic-gate static void
rc_iter_end(rc_node_iter_t * iter)60600Sstevel@tonic-gate rc_iter_end(rc_node_iter_t *iter)
60610Sstevel@tonic-gate {
60620Sstevel@tonic-gate 	rc_node_t *np = iter->rni_parent;
60630Sstevel@tonic-gate 
60640Sstevel@tonic-gate 	if (iter->rni_clevel >= 0)
60650Sstevel@tonic-gate 		np = np->rn_cchain[iter->rni_clevel];
60660Sstevel@tonic-gate 
60670Sstevel@tonic-gate 	assert(MUTEX_HELD(&np->rn_lock));
60680Sstevel@tonic-gate 	if (iter->rni_iter != NULL)
60690Sstevel@tonic-gate 		uu_list_walk_end(iter->rni_iter);
60700Sstevel@tonic-gate 	iter->rni_iter = NULL;
60710Sstevel@tonic-gate 
60720Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
60730Sstevel@tonic-gate 	rc_node_rele(iter->rni_parent);
60740Sstevel@tonic-gate 	if (iter->rni_iter_node != NULL)
60750Sstevel@tonic-gate 		rc_node_rele_other(iter->rni_iter_node);
60760Sstevel@tonic-gate }
60770Sstevel@tonic-gate 
60780Sstevel@tonic-gate /*
60790Sstevel@tonic-gate  * Fails with
60800Sstevel@tonic-gate  *   _NOT_SET - npp is reset
60810Sstevel@tonic-gate  *   _DELETED - npp's node has been deleted
60820Sstevel@tonic-gate  *   _NOT_APPLICABLE - npp's node is not a property
60830Sstevel@tonic-gate  *   _NO_RESOURCES - out of memory
60840Sstevel@tonic-gate  */
60850Sstevel@tonic-gate static int
rc_node_setup_value_iter(rc_node_ptr_t * npp,rc_node_iter_t ** iterp)60860Sstevel@tonic-gate rc_node_setup_value_iter(rc_node_ptr_t *npp, rc_node_iter_t **iterp)
60870Sstevel@tonic-gate {
60880Sstevel@tonic-gate 	rc_node_t *np;
60890Sstevel@tonic-gate 
60900Sstevel@tonic-gate 	rc_node_iter_t *nip;
60910Sstevel@tonic-gate 
60920Sstevel@tonic-gate 	assert(*iterp == NULL);
60930Sstevel@tonic-gate 
60940Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
60950Sstevel@tonic-gate 
60960Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) {
60970Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
60980Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
60990Sstevel@tonic-gate 	}
61000Sstevel@tonic-gate 
61010Sstevel@tonic-gate 	nip = uu_zalloc(sizeof (*nip));
61020Sstevel@tonic-gate 	if (nip == NULL) {
61030Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
61040Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
61050Sstevel@tonic-gate 	}
61060Sstevel@tonic-gate 
61070Sstevel@tonic-gate 	nip->rni_parent = np;
61080Sstevel@tonic-gate 	nip->rni_iter = NULL;
61090Sstevel@tonic-gate 	nip->rni_clevel = -1;
61100Sstevel@tonic-gate 	nip->rni_type = REP_PROTOCOL_ENTITY_VALUE;
61110Sstevel@tonic-gate 	nip->rni_offset = 0;
61120Sstevel@tonic-gate 	nip->rni_last_offset = 0;
61130Sstevel@tonic-gate 
61140Sstevel@tonic-gate 	rc_node_hold_locked(np);
61150Sstevel@tonic-gate 
61160Sstevel@tonic-gate 	*iterp = nip;
61170Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
61180Sstevel@tonic-gate 
61190Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
61200Sstevel@tonic-gate }
61210Sstevel@tonic-gate 
61220Sstevel@tonic-gate /*
61230Sstevel@tonic-gate  * Returns:
61245040Swesolows  *   _NO_RESOURCES - out of memory
61250Sstevel@tonic-gate  *   _NOT_SET - npp is reset
61260Sstevel@tonic-gate  *   _DELETED - npp's node has been deleted
61270Sstevel@tonic-gate  *   _TYPE_MISMATCH - npp's node is not a property
61280Sstevel@tonic-gate  *   _NOT_FOUND - property has no values
61290Sstevel@tonic-gate  *   _TRUNCATED - property has >1 values (first is written into out)
61300Sstevel@tonic-gate  *   _SUCCESS - property has 1 value (which is written into out)
61315040Swesolows  *   _PERMISSION_DENIED - no authorization to read property value(s)
61320Sstevel@tonic-gate  *
61330Sstevel@tonic-gate  * We shorten *sz_out to not include anything after the final '\0'.
61340Sstevel@tonic-gate  */
61350Sstevel@tonic-gate int
rc_node_get_property_value(rc_node_ptr_t * npp,struct rep_protocol_value_response * out,size_t * sz_out)61360Sstevel@tonic-gate rc_node_get_property_value(rc_node_ptr_t *npp,
61370Sstevel@tonic-gate     struct rep_protocol_value_response *out, size_t *sz_out)
61380Sstevel@tonic-gate {
61390Sstevel@tonic-gate 	rc_node_t *np;
61400Sstevel@tonic-gate 	size_t w;
61410Sstevel@tonic-gate 	int ret;
61420Sstevel@tonic-gate 
61430Sstevel@tonic-gate 	assert(*sz_out == sizeof (*out));
61440Sstevel@tonic-gate 
61455040Swesolows 	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
61465040Swesolows 	ret = rc_node_property_may_read(np);
61475040Swesolows 	rc_node_rele(np);
61485040Swesolows 
61495040Swesolows 	if (ret != REP_PROTOCOL_SUCCESS)
61505040Swesolows 		return (ret);
61515040Swesolows 
61520Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
61530Sstevel@tonic-gate 
61540Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) {
61550Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
61560Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
61570Sstevel@tonic-gate 	}
61580Sstevel@tonic-gate 
61590Sstevel@tonic-gate 	if (np->rn_values_size == 0) {
61600Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
61610Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
61620Sstevel@tonic-gate 	}
61630Sstevel@tonic-gate 	out->rpr_type = np->rn_valtype;
61640Sstevel@tonic-gate 	w = strlcpy(out->rpr_value, &np->rn_values[0],
61650Sstevel@tonic-gate 	    sizeof (out->rpr_value));
61660Sstevel@tonic-gate 
61670Sstevel@tonic-gate 	if (w >= sizeof (out->rpr_value))
61680Sstevel@tonic-gate 		backend_panic("value too large");
61690Sstevel@tonic-gate 
61700Sstevel@tonic-gate 	*sz_out = offsetof(struct rep_protocol_value_response,
61710Sstevel@tonic-gate 	    rpr_value[w + 1]);
61720Sstevel@tonic-gate 
61730Sstevel@tonic-gate 	ret = (np->rn_values_count != 1)? REP_PROTOCOL_FAIL_TRUNCATED :
61740Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS;
61750Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
61760Sstevel@tonic-gate 	return (ret);
61770Sstevel@tonic-gate }
61780Sstevel@tonic-gate 
61790Sstevel@tonic-gate int
rc_iter_next_value(rc_node_iter_t * iter,struct rep_protocol_value_response * out,size_t * sz_out,int repeat)61800Sstevel@tonic-gate rc_iter_next_value(rc_node_iter_t *iter,
61810Sstevel@tonic-gate     struct rep_protocol_value_response *out, size_t *sz_out, int repeat)
61820Sstevel@tonic-gate {
61830Sstevel@tonic-gate 	rc_node_t *np = iter->rni_parent;
61840Sstevel@tonic-gate 	const char *vals;
61850Sstevel@tonic-gate 	size_t len;
61860Sstevel@tonic-gate 
61870Sstevel@tonic-gate 	size_t start;
61880Sstevel@tonic-gate 	size_t w;
61895040Swesolows 	int ret;
61900Sstevel@tonic-gate 
61910Sstevel@tonic-gate 	rep_protocol_responseid_t result;
61920Sstevel@tonic-gate 
61930Sstevel@tonic-gate 	assert(*sz_out == sizeof (*out));
61940Sstevel@tonic-gate 
61950Sstevel@tonic-gate 	(void) memset(out, '\0', *sz_out);
61960Sstevel@tonic-gate 
61970Sstevel@tonic-gate 	if (iter->rni_type != REP_PROTOCOL_ENTITY_VALUE)
61980Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
61990Sstevel@tonic-gate 
62005040Swesolows 	RC_NODE_CHECK(np);
62015040Swesolows 	ret = rc_node_property_may_read(np);
62025040Swesolows 
62035040Swesolows 	if (ret != REP_PROTOCOL_SUCCESS)
62045040Swesolows 		return (ret);
62055040Swesolows 
62060Sstevel@tonic-gate 	RC_NODE_CHECK_AND_LOCK(np);
62070Sstevel@tonic-gate 
62080Sstevel@tonic-gate 	vals = np->rn_values;
62090Sstevel@tonic-gate 	len = np->rn_values_size;
62100Sstevel@tonic-gate 
62110Sstevel@tonic-gate 	out->rpr_type = np->rn_valtype;
62120Sstevel@tonic-gate 
62130Sstevel@tonic-gate 	start = (repeat)? iter->rni_last_offset : iter->rni_offset;
62140Sstevel@tonic-gate 
62150Sstevel@tonic-gate 	if (len == 0 || start >= len) {
62160Sstevel@tonic-gate 		result = REP_PROTOCOL_DONE;
62170Sstevel@tonic-gate 		*sz_out -= sizeof (out->rpr_value);
62180Sstevel@tonic-gate 	} else {
62190Sstevel@tonic-gate 		w = strlcpy(out->rpr_value, &vals[start],
62200Sstevel@tonic-gate 		    sizeof (out->rpr_value));
62210Sstevel@tonic-gate 
62220Sstevel@tonic-gate 		if (w >= sizeof (out->rpr_value))
62230Sstevel@tonic-gate 			backend_panic("value too large");
62240Sstevel@tonic-gate 
62250Sstevel@tonic-gate 		*sz_out = offsetof(struct rep_protocol_value_response,
62260Sstevel@tonic-gate 		    rpr_value[w + 1]);
62270Sstevel@tonic-gate 
62280Sstevel@tonic-gate 		/*
62290Sstevel@tonic-gate 		 * update the offsets if we're not repeating
62300Sstevel@tonic-gate 		 */
62310Sstevel@tonic-gate 		if (!repeat) {
62320Sstevel@tonic-gate 			iter->rni_last_offset = iter->rni_offset;
62330Sstevel@tonic-gate 			iter->rni_offset += (w + 1);
62340Sstevel@tonic-gate 		}
62350Sstevel@tonic-gate 
62360Sstevel@tonic-gate 		result = REP_PROTOCOL_SUCCESS;
62370Sstevel@tonic-gate 	}
62380Sstevel@tonic-gate 
62390Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
62400Sstevel@tonic-gate 	return (result);
62410Sstevel@tonic-gate }
62420Sstevel@tonic-gate 
62430Sstevel@tonic-gate /*
62440Sstevel@tonic-gate  * Entry point for ITER_START from client.c.  Validate the arguments & call
62450Sstevel@tonic-gate  * rc_iter_create().
62460Sstevel@tonic-gate  *
62470Sstevel@tonic-gate  * Fails with
62480Sstevel@tonic-gate  *   _NOT_SET
62490Sstevel@tonic-gate  *   _DELETED
62500Sstevel@tonic-gate  *   _TYPE_MISMATCH - np cannot carry type children
62510Sstevel@tonic-gate  *   _BAD_REQUEST - flags is invalid
62520Sstevel@tonic-gate  *		    pattern is invalid
62530Sstevel@tonic-gate  *   _NO_RESOURCES
62540Sstevel@tonic-gate  *   _INVALID_TYPE
62550Sstevel@tonic-gate  *   _TYPE_MISMATCH - *npp cannot have children of type
62560Sstevel@tonic-gate  *   _BACKEND_ACCESS
62570Sstevel@tonic-gate  */
62580Sstevel@tonic-gate int
rc_node_setup_iter(rc_node_ptr_t * npp,rc_node_iter_t ** iterp,uint32_t type,uint32_t flags,const char * pattern)62590Sstevel@tonic-gate rc_node_setup_iter(rc_node_ptr_t *npp, rc_node_iter_t **iterp,
62600Sstevel@tonic-gate     uint32_t type, uint32_t flags, const char *pattern)
62610Sstevel@tonic-gate {
62620Sstevel@tonic-gate 	rc_node_t *np;
62630Sstevel@tonic-gate 	rc_iter_filter_func *f = NULL;
62640Sstevel@tonic-gate 	int rc;
62650Sstevel@tonic-gate 
62660Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK(np, npp);
62670Sstevel@tonic-gate 
62680Sstevel@tonic-gate 	if (pattern != NULL && pattern[0] == '\0')
62690Sstevel@tonic-gate 		pattern = NULL;
62700Sstevel@tonic-gate 
62710Sstevel@tonic-gate 	if (type == REP_PROTOCOL_ENTITY_VALUE) {
62720Sstevel@tonic-gate 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
62730Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
62740Sstevel@tonic-gate 		if (flags != RP_ITER_START_ALL || pattern != NULL)
62750Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_BAD_REQUEST);
62760Sstevel@tonic-gate 
62770Sstevel@tonic-gate 		rc = rc_node_setup_value_iter(npp, iterp);
62780Sstevel@tonic-gate 		assert(rc != REP_PROTOCOL_FAIL_NOT_APPLICABLE);
62790Sstevel@tonic-gate 		return (rc);
62800Sstevel@tonic-gate 	}
62810Sstevel@tonic-gate 
62820Sstevel@tonic-gate 	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
62830Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS)
62840Sstevel@tonic-gate 		return (rc);
62850Sstevel@tonic-gate 
62860Sstevel@tonic-gate 	if (((flags & RP_ITER_START_FILT_MASK) == RP_ITER_START_ALL) ^
62870Sstevel@tonic-gate 	    (pattern == NULL))
62880Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
62890Sstevel@tonic-gate 
62900Sstevel@tonic-gate 	/* Composition only works for instances & snapshots. */
62910Sstevel@tonic-gate 	if ((flags & RP_ITER_START_COMPOSED) &&
62920Sstevel@tonic-gate 	    (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE &&
62930Sstevel@tonic-gate 	    np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT))
62940Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
62950Sstevel@tonic-gate 
62960Sstevel@tonic-gate 	if (pattern != NULL) {
62970Sstevel@tonic-gate 		if ((rc = rc_check_type_name(type, pattern)) !=
62980Sstevel@tonic-gate 		    REP_PROTOCOL_SUCCESS)
62990Sstevel@tonic-gate 			return (rc);
63000Sstevel@tonic-gate 		pattern = strdup(pattern);
63010Sstevel@tonic-gate 		if (pattern == NULL)
63020Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
63030Sstevel@tonic-gate 	}
63040Sstevel@tonic-gate 
63050Sstevel@tonic-gate 	switch (flags & RP_ITER_START_FILT_MASK) {
63060Sstevel@tonic-gate 	case RP_ITER_START_ALL:
63070Sstevel@tonic-gate 		f = NULL;
63080Sstevel@tonic-gate 		break;
63090Sstevel@tonic-gate 	case RP_ITER_START_EXACT:
63100Sstevel@tonic-gate 		f = rc_iter_filter_name;
63110Sstevel@tonic-gate 		break;
63120Sstevel@tonic-gate 	case RP_ITER_START_PGTYPE:
63130Sstevel@tonic-gate 		if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
63140Sstevel@tonic-gate 			free((void *)pattern);
63150Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_BAD_REQUEST);
63160Sstevel@tonic-gate 		}
63170Sstevel@tonic-gate 		f = rc_iter_filter_type;
63180Sstevel@tonic-gate 		break;
63190Sstevel@tonic-gate 	default:
63200Sstevel@tonic-gate 		free((void *)pattern);
63210Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
63220Sstevel@tonic-gate 	}
63230Sstevel@tonic-gate 
63240Sstevel@tonic-gate 	rc = rc_iter_create(iterp, np, type, f, (void *)pattern,
63250Sstevel@tonic-gate 	    flags & RP_ITER_START_COMPOSED);
63260Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS && pattern != NULL)
63270Sstevel@tonic-gate 		free((void *)pattern);
63280Sstevel@tonic-gate 
63290Sstevel@tonic-gate 	return (rc);
63300Sstevel@tonic-gate }
63310Sstevel@tonic-gate 
63320Sstevel@tonic-gate /*
63330Sstevel@tonic-gate  * Do uu_list_walk_next(iter->rni_iter) until we find a child which matches
63340Sstevel@tonic-gate  * the filter.
63350Sstevel@tonic-gate  * For composed iterators, then check to see if there's an overlapping entity
63360Sstevel@tonic-gate  * (see embedded comments).  If we reach the end of the list, start over at
63370Sstevel@tonic-gate  * the next level.
63380Sstevel@tonic-gate  *
63390Sstevel@tonic-gate  * Returns
63400Sstevel@tonic-gate  *   _BAD_REQUEST - iter walks values
63410Sstevel@tonic-gate  *   _TYPE_MISMATCH - iter does not walk type entities
63420Sstevel@tonic-gate  *   _DELETED - parent was deleted
63430Sstevel@tonic-gate  *   _NO_RESOURCES
63440Sstevel@tonic-gate  *   _INVALID_TYPE - type is invalid
63450Sstevel@tonic-gate  *   _DONE
63460Sstevel@tonic-gate  *   _SUCCESS
63470Sstevel@tonic-gate  *
63480Sstevel@tonic-gate  * For composed property group iterators, can also return
63490Sstevel@tonic-gate  *   _TYPE_MISMATCH - parent cannot have type children
63500Sstevel@tonic-gate  */
63510Sstevel@tonic-gate int
rc_iter_next(rc_node_iter_t * iter,rc_node_ptr_t * out,uint32_t type)63520Sstevel@tonic-gate rc_iter_next(rc_node_iter_t *iter, rc_node_ptr_t *out, uint32_t type)
63530Sstevel@tonic-gate {
63540Sstevel@tonic-gate 	rc_node_t *np = iter->rni_parent;
63550Sstevel@tonic-gate 	rc_node_t *res;
63560Sstevel@tonic-gate 	int rc;
63570Sstevel@tonic-gate 
63580Sstevel@tonic-gate 	if (iter->rni_type == REP_PROTOCOL_ENTITY_VALUE)
63590Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
63600Sstevel@tonic-gate 
63610Sstevel@tonic-gate 	if (iter->rni_iter == NULL) {
63620Sstevel@tonic-gate 		rc_node_clear(out, 0);
63630Sstevel@tonic-gate 		return (REP_PROTOCOL_DONE);
63640Sstevel@tonic-gate 	}
63650Sstevel@tonic-gate 
63660Sstevel@tonic-gate 	if (iter->rni_type != type) {
63670Sstevel@tonic-gate 		rc_node_clear(out, 0);
63680Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
63690Sstevel@tonic-gate 	}
63700Sstevel@tonic-gate 
63710Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);  /* held by _iter_create() */
63720Sstevel@tonic-gate 
63730Sstevel@tonic-gate 	if (!rc_node_wait_flag(np, RC_NODE_CHILDREN_CHANGING)) {
63740Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
63750Sstevel@tonic-gate 		rc_node_clear(out, 1);
63760Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_DELETED);
63770Sstevel@tonic-gate 	}
63780Sstevel@tonic-gate 
63790Sstevel@tonic-gate 	if (iter->rni_clevel >= 0) {
63800Sstevel@tonic-gate 		/* Composed iterator.  Iterate over appropriate level. */
63810Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
63820Sstevel@tonic-gate 		np = np->rn_cchain[iter->rni_clevel];
63830Sstevel@tonic-gate 		/*
63840Sstevel@tonic-gate 		 * If iter->rni_parent is an instance or a snapshot, np must
63850Sstevel@tonic-gate 		 * be valid since iter holds iter->rni_parent & possible
63860Sstevel@tonic-gate 		 * levels (service, instance, snaplevel) cannot be destroyed
63870Sstevel@tonic-gate 		 * while rni_parent is held.  If iter->rni_parent is
63880Sstevel@tonic-gate 		 * a composed property group then rc_node_setup_cpg() put
63890Sstevel@tonic-gate 		 * a hold on np.
63900Sstevel@tonic-gate 		 */
63910Sstevel@tonic-gate 
63920Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
63930Sstevel@tonic-gate 
63940Sstevel@tonic-gate 		if (!rc_node_wait_flag(np, RC_NODE_CHILDREN_CHANGING)) {
63950Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
63960Sstevel@tonic-gate 			rc_node_clear(out, 1);
63970Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_DELETED);
63980Sstevel@tonic-gate 		}
63990Sstevel@tonic-gate 	}
64000Sstevel@tonic-gate 
64010Sstevel@tonic-gate 	assert(np->rn_flags & RC_NODE_HAS_CHILDREN);
64020Sstevel@tonic-gate 
64030Sstevel@tonic-gate 	for (;;) {
64040Sstevel@tonic-gate 		res = uu_list_walk_next(iter->rni_iter);
64050Sstevel@tonic-gate 		if (res == NULL) {
64060Sstevel@tonic-gate 			rc_node_t *parent = iter->rni_parent;
64070Sstevel@tonic-gate 
64080Sstevel@tonic-gate #if COMPOSITION_DEPTH == 2
64090Sstevel@tonic-gate 			if (iter->rni_clevel < 0 || iter->rni_clevel == 1) {
64100Sstevel@tonic-gate 				/* release walker and lock */
64110Sstevel@tonic-gate 				rc_iter_end(iter);
64120Sstevel@tonic-gate 				break;
64130Sstevel@tonic-gate 			}
64140Sstevel@tonic-gate 
64150Sstevel@tonic-gate 			/* Stop walking current level. */
64160Sstevel@tonic-gate 			uu_list_walk_end(iter->rni_iter);
64170Sstevel@tonic-gate 			iter->rni_iter = NULL;
64180Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
64190Sstevel@tonic-gate 			rc_node_rele_other(iter->rni_iter_node);
64200Sstevel@tonic-gate 			iter->rni_iter_node = NULL;
64210Sstevel@tonic-gate 
64220Sstevel@tonic-gate 			/* Start walking next level. */
64230Sstevel@tonic-gate 			++iter->rni_clevel;
64240Sstevel@tonic-gate 			np = parent->rn_cchain[iter->rni_clevel];
64250Sstevel@tonic-gate 			assert(np != NULL);
64260Sstevel@tonic-gate #else
64270Sstevel@tonic-gate #error This code must be updated.
64280Sstevel@tonic-gate #endif
64290Sstevel@tonic-gate 
64300Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
64310Sstevel@tonic-gate 
64320Sstevel@tonic-gate 			rc = rc_node_fill_children(np, iter->rni_type);
64330Sstevel@tonic-gate 
64340Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS) {
64350Sstevel@tonic-gate 				iter->rni_iter =
64360Sstevel@tonic-gate 				    uu_list_walk_start(np->rn_children,
64375040Swesolows 				    UU_WALK_ROBUST);
64380Sstevel@tonic-gate 
64390Sstevel@tonic-gate 				if (iter->rni_iter == NULL)
64400Sstevel@tonic-gate 					rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
64410Sstevel@tonic-gate 				else {
64420Sstevel@tonic-gate 					iter->rni_iter_node = np;
64430Sstevel@tonic-gate 					rc_node_hold_other(np);
64440Sstevel@tonic-gate 				}
64450Sstevel@tonic-gate 			}
64460Sstevel@tonic-gate 
64470Sstevel@tonic-gate 			if (rc != REP_PROTOCOL_SUCCESS) {
64480Sstevel@tonic-gate 				(void) pthread_mutex_unlock(&np->rn_lock);
64490Sstevel@tonic-gate 				rc_node_clear(out, 0);
64500Sstevel@tonic-gate 				return (rc);
64510Sstevel@tonic-gate 			}
64520Sstevel@tonic-gate 
64530Sstevel@tonic-gate 			continue;
64540Sstevel@tonic-gate 		}
64550Sstevel@tonic-gate 
64560Sstevel@tonic-gate 		if (res->rn_id.rl_type != type ||
64570Sstevel@tonic-gate 		    !iter->rni_filter(res, iter->rni_filter_arg))
64580Sstevel@tonic-gate 			continue;
64590Sstevel@tonic-gate 
64600Sstevel@tonic-gate 		/*
64610Sstevel@tonic-gate 		 * If we're composed and not at the top level, check to see if
64620Sstevel@tonic-gate 		 * there's an entity at a higher level with the same name.  If
64630Sstevel@tonic-gate 		 * so, skip this one.
64640Sstevel@tonic-gate 		 */
64650Sstevel@tonic-gate 		if (iter->rni_clevel > 0) {
64660Sstevel@tonic-gate 			rc_node_t *ent = iter->rni_parent->rn_cchain[0];
64670Sstevel@tonic-gate 			rc_node_t *pg;
64680Sstevel@tonic-gate 
64690Sstevel@tonic-gate #if COMPOSITION_DEPTH == 2
64700Sstevel@tonic-gate 			assert(iter->rni_clevel == 1);
64710Sstevel@tonic-gate 
64720Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
64730Sstevel@tonic-gate 			(void) pthread_mutex_lock(&ent->rn_lock);
64740Sstevel@tonic-gate 			rc = rc_node_find_named_child(ent, res->rn_name, type,
64750Sstevel@tonic-gate 			    &pg);
64760Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS && pg != NULL)
64770Sstevel@tonic-gate 				rc_node_rele(pg);
64780Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&ent->rn_lock);
64790Sstevel@tonic-gate 			if (rc != REP_PROTOCOL_SUCCESS) {
64800Sstevel@tonic-gate 				rc_node_clear(out, 0);
64810Sstevel@tonic-gate 				return (rc);
64820Sstevel@tonic-gate 			}
64830Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
64840Sstevel@tonic-gate 
64850Sstevel@tonic-gate 			/* Make sure np isn't being deleted all of a sudden. */
64860Sstevel@tonic-gate 			if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
64870Sstevel@tonic-gate 				(void) pthread_mutex_unlock(&np->rn_lock);
64880Sstevel@tonic-gate 				rc_node_clear(out, 1);
64890Sstevel@tonic-gate 				return (REP_PROTOCOL_FAIL_DELETED);
64900Sstevel@tonic-gate 			}
64910Sstevel@tonic-gate 
64920Sstevel@tonic-gate 			if (pg != NULL)
64930Sstevel@tonic-gate 				/* Keep going. */
64940Sstevel@tonic-gate 				continue;
64950Sstevel@tonic-gate #else
64960Sstevel@tonic-gate #error This code must be updated.
64970Sstevel@tonic-gate #endif
64980Sstevel@tonic-gate 		}
64990Sstevel@tonic-gate 
65000Sstevel@tonic-gate 		/*
65010Sstevel@tonic-gate 		 * If we're composed, iterating over property groups, and not
65020Sstevel@tonic-gate 		 * at the bottom level, check to see if there's a pg at lower
65030Sstevel@tonic-gate 		 * level with the same name.  If so, return a cpg.
65040Sstevel@tonic-gate 		 */
65050Sstevel@tonic-gate 		if (iter->rni_clevel >= 0 &&
65060Sstevel@tonic-gate 		    type == REP_PROTOCOL_ENTITY_PROPERTYGRP &&
65070Sstevel@tonic-gate 		    iter->rni_clevel < COMPOSITION_DEPTH - 1) {
65080Sstevel@tonic-gate #if COMPOSITION_DEPTH == 2
65090Sstevel@tonic-gate 			rc_node_t *pg;
65100Sstevel@tonic-gate 			rc_node_t *ent = iter->rni_parent->rn_cchain[1];
65110Sstevel@tonic-gate 
65120Sstevel@tonic-gate 			rc_node_hold(res);	/* While we drop np->rn_lock */
65130Sstevel@tonic-gate 
65140Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
65150Sstevel@tonic-gate 			(void) pthread_mutex_lock(&ent->rn_lock);
65160Sstevel@tonic-gate 			rc = rc_node_find_named_child(ent, res->rn_name, type,
65170Sstevel@tonic-gate 			    &pg);
65180Sstevel@tonic-gate 			/* holds pg if not NULL */
65190Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&ent->rn_lock);
65200Sstevel@tonic-gate 			if (rc != REP_PROTOCOL_SUCCESS) {
65210Sstevel@tonic-gate 				rc_node_rele(res);
65220Sstevel@tonic-gate 				rc_node_clear(out, 0);
65230Sstevel@tonic-gate 				return (rc);
65240Sstevel@tonic-gate 			}
65250Sstevel@tonic-gate 
65260Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
65270Sstevel@tonic-gate 			if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
65280Sstevel@tonic-gate 				(void) pthread_mutex_unlock(&np->rn_lock);
65290Sstevel@tonic-gate 				rc_node_rele(res);
65300Sstevel@tonic-gate 				if (pg != NULL)
65310Sstevel@tonic-gate 					rc_node_rele(pg);
65320Sstevel@tonic-gate 				rc_node_clear(out, 1);
65330Sstevel@tonic-gate 				return (REP_PROTOCOL_FAIL_DELETED);
65340Sstevel@tonic-gate 			}
65350Sstevel@tonic-gate 
65360Sstevel@tonic-gate 			if (pg == NULL) {
65370Sstevel@tonic-gate 				rc_node_rele(res);
65380Sstevel@tonic-gate 			} else {
65390Sstevel@tonic-gate 				rc_node_t *cpg;
65400Sstevel@tonic-gate 
65410Sstevel@tonic-gate 				/* Keep res held for rc_node_setup_cpg(). */
65420Sstevel@tonic-gate 
65430Sstevel@tonic-gate 				cpg = rc_node_alloc();
65440Sstevel@tonic-gate 				if (cpg == NULL) {
65450Sstevel@tonic-gate 					(void) pthread_mutex_unlock(
65460Sstevel@tonic-gate 					    &np->rn_lock);
65470Sstevel@tonic-gate 					rc_node_rele(res);
65480Sstevel@tonic-gate 					rc_node_rele(pg);
65490Sstevel@tonic-gate 					rc_node_clear(out, 0);
65500Sstevel@tonic-gate 					return (REP_PROTOCOL_FAIL_NO_RESOURCES);
65510Sstevel@tonic-gate 				}
65520Sstevel@tonic-gate 
65530Sstevel@tonic-gate 				switch (rc_node_setup_cpg(cpg, res, pg)) {
65540Sstevel@tonic-gate 				case REP_PROTOCOL_SUCCESS:
65550Sstevel@tonic-gate 					res = cpg;
65560Sstevel@tonic-gate 					break;
65570Sstevel@tonic-gate 
65580Sstevel@tonic-gate 				case REP_PROTOCOL_FAIL_TYPE_MISMATCH:
65590Sstevel@tonic-gate 					/* Nevermind. */
65600Sstevel@tonic-gate 					rc_node_destroy(cpg);
65610Sstevel@tonic-gate 					rc_node_rele(pg);
65620Sstevel@tonic-gate 					rc_node_rele(res);
65630Sstevel@tonic-gate 					break;
65640Sstevel@tonic-gate 
65650Sstevel@tonic-gate 				case REP_PROTOCOL_FAIL_NO_RESOURCES:
65660Sstevel@tonic-gate 					rc_node_destroy(cpg);
65670Sstevel@tonic-gate 					(void) pthread_mutex_unlock(
65680Sstevel@tonic-gate 					    &np->rn_lock);
65690Sstevel@tonic-gate 					rc_node_rele(res);
65700Sstevel@tonic-gate 					rc_node_rele(pg);
65710Sstevel@tonic-gate 					rc_node_clear(out, 0);
65720Sstevel@tonic-gate 					return (REP_PROTOCOL_FAIL_NO_RESOURCES);
65730Sstevel@tonic-gate 
65740Sstevel@tonic-gate 				default:
65750Sstevel@tonic-gate 					assert(0);
65760Sstevel@tonic-gate 					abort();
65770Sstevel@tonic-gate 				}
65780Sstevel@tonic-gate 			}
65790Sstevel@tonic-gate #else
65800Sstevel@tonic-gate #error This code must be updated.
65810Sstevel@tonic-gate #endif
65820Sstevel@tonic-gate 		}
65830Sstevel@tonic-gate 
65840Sstevel@tonic-gate 		rc_node_hold(res);
65850Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
65860Sstevel@tonic-gate 		break;
65870Sstevel@tonic-gate 	}
65880Sstevel@tonic-gate 	rc_node_assign(out, res);
65890Sstevel@tonic-gate 
65900Sstevel@tonic-gate 	if (res == NULL)
65910Sstevel@tonic-gate 		return (REP_PROTOCOL_DONE);
65920Sstevel@tonic-gate 	rc_node_rele(res);
65930Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
65940Sstevel@tonic-gate }
65950Sstevel@tonic-gate 
65960Sstevel@tonic-gate void
rc_iter_destroy(rc_node_iter_t ** nipp)65970Sstevel@tonic-gate rc_iter_destroy(rc_node_iter_t **nipp)
65980Sstevel@tonic-gate {
65990Sstevel@tonic-gate 	rc_node_iter_t *nip = *nipp;
66000Sstevel@tonic-gate 	rc_node_t *np;
66010Sstevel@tonic-gate 
66020Sstevel@tonic-gate 	if (nip == NULL)
66030Sstevel@tonic-gate 		return;				/* already freed */
66040Sstevel@tonic-gate 
66050Sstevel@tonic-gate 	np = nip->rni_parent;
66060Sstevel@tonic-gate 
66070Sstevel@tonic-gate 	if (nip->rni_filter_arg != NULL)
66080Sstevel@tonic-gate 		free(nip->rni_filter_arg);
66090Sstevel@tonic-gate 	nip->rni_filter_arg = NULL;
66100Sstevel@tonic-gate 
66110Sstevel@tonic-gate 	if (nip->rni_type == REP_PROTOCOL_ENTITY_VALUE ||
66120Sstevel@tonic-gate 	    nip->rni_iter != NULL) {
66130Sstevel@tonic-gate 		if (nip->rni_clevel < 0)
66140Sstevel@tonic-gate 			(void) pthread_mutex_lock(&np->rn_lock);
66150Sstevel@tonic-gate 		else
66160Sstevel@tonic-gate 			(void) pthread_mutex_lock(
66170Sstevel@tonic-gate 			    &np->rn_cchain[nip->rni_clevel]->rn_lock);
66180Sstevel@tonic-gate 		rc_iter_end(nip);		/* release walker and lock */
66190Sstevel@tonic-gate 	}
66200Sstevel@tonic-gate 	nip->rni_parent = NULL;
66210Sstevel@tonic-gate 
66220Sstevel@tonic-gate 	uu_free(nip);
66230Sstevel@tonic-gate 	*nipp = NULL;
66240Sstevel@tonic-gate }
66250Sstevel@tonic-gate 
66260Sstevel@tonic-gate int
rc_node_setup_tx(rc_node_ptr_t * npp,rc_node_ptr_t * txp)66270Sstevel@tonic-gate rc_node_setup_tx(rc_node_ptr_t *npp, rc_node_ptr_t *txp)
66280Sstevel@tonic-gate {
66290Sstevel@tonic-gate 	rc_node_t *np;
66300Sstevel@tonic-gate 	permcheck_t *pcp;
66310Sstevel@tonic-gate 	int ret;
66328497SThomas.Whitten@Sun.COM 	perm_status_t granted;
66335777Stw21770 	rc_auth_state_t authorized = RC_AUTH_UNKNOWN;
66345777Stw21770 	char *auth_string = NULL;
66350Sstevel@tonic-gate 
66360Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
66370Sstevel@tonic-gate 
66380Sstevel@tonic-gate 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
66390Sstevel@tonic-gate 		rc_node_rele(np);
66400Sstevel@tonic-gate 		np = np->rn_cchain[0];
66410Sstevel@tonic-gate 		RC_NODE_CHECK_AND_HOLD(np);
66420Sstevel@tonic-gate 	}
66430Sstevel@tonic-gate 
66440Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
66450Sstevel@tonic-gate 		rc_node_rele(np);
66460Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
66470Sstevel@tonic-gate 	}
66480Sstevel@tonic-gate 
66490Sstevel@tonic-gate 	if (np->rn_id.rl_ids[ID_SNAPSHOT] != 0) {
66500Sstevel@tonic-gate 		rc_node_rele(np);
66510Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
66520Sstevel@tonic-gate 	}
66530Sstevel@tonic-gate 
66545777Stw21770 #ifdef NATIVE_BUILD
66550Sstevel@tonic-gate 	if (client_is_privileged())
66560Sstevel@tonic-gate 		goto skip_checks;
66570Sstevel@tonic-gate 	rc_node_rele(np);
66580Sstevel@tonic-gate 	return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
66590Sstevel@tonic-gate #else
66605777Stw21770 	if (is_main_repository == 0)
66615777Stw21770 		goto skip_checks;
66625777Stw21770 
66630Sstevel@tonic-gate 	/* permission check */
66640Sstevel@tonic-gate 	pcp = pc_create();
66650Sstevel@tonic-gate 	if (pcp == NULL) {
66660Sstevel@tonic-gate 		rc_node_rele(np);
66670Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
66680Sstevel@tonic-gate 	}
66690Sstevel@tonic-gate 
66700Sstevel@tonic-gate 	if (np->rn_id.rl_ids[ID_INSTANCE] != 0 &&	/* instance pg */
66710Sstevel@tonic-gate 	    ((strcmp(np->rn_name, AUTH_PG_ACTIONS) == 0 &&
66720Sstevel@tonic-gate 	    strcmp(np->rn_type, AUTH_PG_ACTIONS_TYPE) == 0) ||
66730Sstevel@tonic-gate 	    (strcmp(np->rn_name, AUTH_PG_GENERAL_OVR) == 0 &&
66740Sstevel@tonic-gate 	    strcmp(np->rn_type, AUTH_PG_GENERAL_OVR_TYPE) == 0))) {
66750Sstevel@tonic-gate 		rc_node_t *instn;
66760Sstevel@tonic-gate 
667711223STruong.Q.Nguyen@Sun.COM 		/* solaris.smf.modify can be used */
667811223STruong.Q.Nguyen@Sun.COM 		ret = perm_add_enabling(pcp, AUTH_MODIFY);
667911223STruong.Q.Nguyen@Sun.COM 		if (ret != REP_PROTOCOL_SUCCESS) {
668011223STruong.Q.Nguyen@Sun.COM 			pc_free(pcp);
668111223STruong.Q.Nguyen@Sun.COM 			rc_node_rele(np);
668211223STruong.Q.Nguyen@Sun.COM 			return (ret);
668311223STruong.Q.Nguyen@Sun.COM 		}
668411223STruong.Q.Nguyen@Sun.COM 
66850Sstevel@tonic-gate 		/* solaris.smf.manage can be used. */
66860Sstevel@tonic-gate 		ret = perm_add_enabling(pcp, AUTH_MANAGE);
66870Sstevel@tonic-gate 
66880Sstevel@tonic-gate 		if (ret != REP_PROTOCOL_SUCCESS) {
66890Sstevel@tonic-gate 			pc_free(pcp);
66900Sstevel@tonic-gate 			rc_node_rele(np);
66910Sstevel@tonic-gate 			return (ret);
66920Sstevel@tonic-gate 		}
66930Sstevel@tonic-gate 
66940Sstevel@tonic-gate 		/* general/action_authorization values can be used. */
66950Sstevel@tonic-gate 		ret = rc_node_parent(np, &instn);
66960Sstevel@tonic-gate 		if (ret != REP_PROTOCOL_SUCCESS) {
66970Sstevel@tonic-gate 			assert(ret == REP_PROTOCOL_FAIL_DELETED);
66980Sstevel@tonic-gate 			rc_node_rele(np);
66990Sstevel@tonic-gate 			pc_free(pcp);
67000Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_DELETED);
67010Sstevel@tonic-gate 		}
67020Sstevel@tonic-gate 
67030Sstevel@tonic-gate 		assert(instn->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE);
67040Sstevel@tonic-gate 
67050Sstevel@tonic-gate 		ret = perm_add_inst_action_auth(pcp, instn);
67060Sstevel@tonic-gate 		rc_node_rele(instn);
67070Sstevel@tonic-gate 		switch (ret) {
67080Sstevel@tonic-gate 		case REP_PROTOCOL_SUCCESS:
67090Sstevel@tonic-gate 			break;
67100Sstevel@tonic-gate 
67110Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_DELETED:
67120Sstevel@tonic-gate 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
67130Sstevel@tonic-gate 			rc_node_rele(np);
67140Sstevel@tonic-gate 			pc_free(pcp);
67150Sstevel@tonic-gate 			return (ret);
67160Sstevel@tonic-gate 
67170Sstevel@tonic-gate 		default:
67180Sstevel@tonic-gate 			bad_error("perm_add_inst_action_auth", ret);
67190Sstevel@tonic-gate 		}
67200Sstevel@tonic-gate 
67210Sstevel@tonic-gate 		if (strcmp(np->rn_name, AUTH_PG_ACTIONS) == 0)
67225777Stw21770 			authorized = RC_AUTH_PASSED; /* No check on commit. */
67230Sstevel@tonic-gate 	} else {
67240Sstevel@tonic-gate 		ret = perm_add_enabling(pcp, AUTH_MODIFY);
67250Sstevel@tonic-gate 
67260Sstevel@tonic-gate 		if (ret == REP_PROTOCOL_SUCCESS) {
67270Sstevel@tonic-gate 			/* propertygroup-type-specific authorization */
67280Sstevel@tonic-gate 			/* no locking because rn_type won't change anyway */
67290Sstevel@tonic-gate 			const char * const auth =
67300Sstevel@tonic-gate 			    perm_auth_for_pgtype(np->rn_type);
67310Sstevel@tonic-gate 
67320Sstevel@tonic-gate 			if (auth != NULL)
67330Sstevel@tonic-gate 				ret = perm_add_enabling(pcp, auth);
67340Sstevel@tonic-gate 		}
67350Sstevel@tonic-gate 
67360Sstevel@tonic-gate 		if (ret == REP_PROTOCOL_SUCCESS)
67370Sstevel@tonic-gate 			/* propertygroup/transaction-type-specific auths */
67380Sstevel@tonic-gate 			ret =
67390Sstevel@tonic-gate 			    perm_add_enabling_values(pcp, np, AUTH_PROP_VALUE);
67400Sstevel@tonic-gate 
67410Sstevel@tonic-gate 		if (ret == REP_PROTOCOL_SUCCESS)
67420Sstevel@tonic-gate 			ret =
67430Sstevel@tonic-gate 			    perm_add_enabling_values(pcp, np, AUTH_PROP_MODIFY);
67440Sstevel@tonic-gate 
67450Sstevel@tonic-gate 		/* AUTH_MANAGE can manipulate general/AUTH_PROP_ACTION */
67460Sstevel@tonic-gate 		if (ret == REP_PROTOCOL_SUCCESS &&
67470Sstevel@tonic-gate 		    strcmp(np->rn_name, AUTH_PG_GENERAL) == 0 &&
67480Sstevel@tonic-gate 		    strcmp(np->rn_type, AUTH_PG_GENERAL_TYPE) == 0)
67490Sstevel@tonic-gate 			ret = perm_add_enabling(pcp, AUTH_MANAGE);
67500Sstevel@tonic-gate 
67510Sstevel@tonic-gate 		if (ret != REP_PROTOCOL_SUCCESS) {
67520Sstevel@tonic-gate 			pc_free(pcp);
67530Sstevel@tonic-gate 			rc_node_rele(np);
67540Sstevel@tonic-gate 			return (ret);
67550Sstevel@tonic-gate 		}
67560Sstevel@tonic-gate 	}
67570Sstevel@tonic-gate 
67588497SThomas.Whitten@Sun.COM 	granted = perm_granted(pcp);
67598497SThomas.Whitten@Sun.COM 	ret = map_granted_status(granted, pcp, &auth_string);
67605777Stw21770 	pc_free(pcp);
67618497SThomas.Whitten@Sun.COM 
67628497SThomas.Whitten@Sun.COM 	if ((granted == PERM_GONE) || (granted == PERM_FAIL) ||
67638497SThomas.Whitten@Sun.COM 	    (ret == REP_PROTOCOL_FAIL_NO_RESOURCES)) {
67648497SThomas.Whitten@Sun.COM 		free(auth_string);
67650Sstevel@tonic-gate 		rc_node_rele(np);
67668497SThomas.Whitten@Sun.COM 		return (ret);
67678497SThomas.Whitten@Sun.COM 	}
67688497SThomas.Whitten@Sun.COM 
67698497SThomas.Whitten@Sun.COM 	if (granted == PERM_DENIED) {
67705777Stw21770 		/*
67715777Stw21770 		 * If we get here, the authorization failed.
67725777Stw21770 		 * Unfortunately, we don't have enough information at this
67735777Stw21770 		 * point to generate the security audit events.  We'll only
67745777Stw21770 		 * get that information when the client tries to commit the
67755777Stw21770 		 * event.  Thus, we'll remember the failed authorization,
67765777Stw21770 		 * so that we can generate the audit events later.
67775777Stw21770 		 */
67785777Stw21770 		authorized = RC_AUTH_FAILED;
67795777Stw21770 	}
67800Sstevel@tonic-gate #endif /* NATIVE_BUILD */
67810Sstevel@tonic-gate 
67820Sstevel@tonic-gate skip_checks:
67830Sstevel@tonic-gate 	rc_node_assign(txp, np);
67840Sstevel@tonic-gate 	txp->rnp_authorized = authorized;
67855777Stw21770 	if (authorized != RC_AUTH_UNKNOWN) {
67865777Stw21770 		/* Save the authorization string. */
67875777Stw21770 		if (txp->rnp_auth_string != NULL)
67885777Stw21770 			free((void *)txp->rnp_auth_string);
67895777Stw21770 		txp->rnp_auth_string = auth_string;
67905777Stw21770 		auth_string = NULL;	/* Don't free until done with txp. */
67915777Stw21770 	}
67920Sstevel@tonic-gate 
67930Sstevel@tonic-gate 	rc_node_rele(np);
67945777Stw21770 	if (auth_string != NULL)
67955777Stw21770 		free(auth_string);
67960Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
67970Sstevel@tonic-gate }
67980Sstevel@tonic-gate 
67990Sstevel@tonic-gate /*
68000Sstevel@tonic-gate  * Return 1 if the given transaction commands only modify the values of
68010Sstevel@tonic-gate  * properties other than "modify_authorization".  Return -1 if any of the
68020Sstevel@tonic-gate  * commands are invalid, and 0 otherwise.
68030Sstevel@tonic-gate  */
68040Sstevel@tonic-gate static int
tx_allow_value(const void * cmds_arg,size_t cmds_sz,rc_node_t * pg)68050Sstevel@tonic-gate tx_allow_value(const void *cmds_arg, size_t cmds_sz, rc_node_t *pg)
68060Sstevel@tonic-gate {
68070Sstevel@tonic-gate 	const struct rep_protocol_transaction_cmd *cmds;
68080Sstevel@tonic-gate 	uintptr_t loc;
68090Sstevel@tonic-gate 	uint32_t sz;
68100Sstevel@tonic-gate 	rc_node_t *prop;
68110Sstevel@tonic-gate 	boolean_t ok;
68120Sstevel@tonic-gate 
68130Sstevel@tonic-gate 	assert(!MUTEX_HELD(&pg->rn_lock));
68140Sstevel@tonic-gate 
68150Sstevel@tonic-gate 	loc = (uintptr_t)cmds_arg;
68160Sstevel@tonic-gate 
68170Sstevel@tonic-gate 	while (cmds_sz > 0) {
68180Sstevel@tonic-gate 		cmds = (struct rep_protocol_transaction_cmd *)loc;
68190Sstevel@tonic-gate 
68200Sstevel@tonic-gate 		if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
68210Sstevel@tonic-gate 			return (-1);
68220Sstevel@tonic-gate 
68230Sstevel@tonic-gate 		sz = cmds->rptc_size;
68240Sstevel@tonic-gate 		if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
68250Sstevel@tonic-gate 			return (-1);
68260Sstevel@tonic-gate 
68270Sstevel@tonic-gate 		sz = TX_SIZE(sz);
68280Sstevel@tonic-gate 		if (sz > cmds_sz)
68290Sstevel@tonic-gate 			return (-1);
68300Sstevel@tonic-gate 
68310Sstevel@tonic-gate 		switch (cmds[0].rptc_action) {
68320Sstevel@tonic-gate 		case REP_PROTOCOL_TX_ENTRY_CLEAR:
68330Sstevel@tonic-gate 			break;
68340Sstevel@tonic-gate 
68350Sstevel@tonic-gate 		case REP_PROTOCOL_TX_ENTRY_REPLACE:
68360Sstevel@tonic-gate 			/* Check type */
68370Sstevel@tonic-gate 			(void) pthread_mutex_lock(&pg->rn_lock);
6838*12430Stom.whitten@oracle.com 			ok = B_FALSE;
68390Sstevel@tonic-gate 			if (rc_node_find_named_child(pg,
68400Sstevel@tonic-gate 			    (const char *)cmds[0].rptc_data,
68410Sstevel@tonic-gate 			    REP_PROTOCOL_ENTITY_PROPERTY, &prop) ==
68420Sstevel@tonic-gate 			    REP_PROTOCOL_SUCCESS) {
6843*12430Stom.whitten@oracle.com 				if (prop != NULL) {
6844*12430Stom.whitten@oracle.com 					ok = prop->rn_valtype ==
6845*12430Stom.whitten@oracle.com 					    cmds[0].rptc_type;
6846*12430Stom.whitten@oracle.com 					/*
6847*12430Stom.whitten@oracle.com 					 * rc_node_find_named_child()
6848*12430Stom.whitten@oracle.com 					 * places a hold on prop which we
6849*12430Stom.whitten@oracle.com 					 * do not need to hang on to.
6850*12430Stom.whitten@oracle.com 					 */
6851*12430Stom.whitten@oracle.com 					rc_node_rele(prop);
6852*12430Stom.whitten@oracle.com 				}
68530Sstevel@tonic-gate 			}
68540Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&pg->rn_lock);
68550Sstevel@tonic-gate 			if (ok)
68560Sstevel@tonic-gate 				break;
68570Sstevel@tonic-gate 			return (0);
68580Sstevel@tonic-gate 
68590Sstevel@tonic-gate 		default:
68600Sstevel@tonic-gate 			return (0);
68610Sstevel@tonic-gate 		}
68620Sstevel@tonic-gate 
68630Sstevel@tonic-gate 		if (strcmp((const char *)cmds[0].rptc_data, AUTH_PROP_MODIFY)
68640Sstevel@tonic-gate 		    == 0)
68650Sstevel@tonic-gate 			return (0);
68660Sstevel@tonic-gate 
68670Sstevel@tonic-gate 		loc += sz;
68680Sstevel@tonic-gate 		cmds_sz -= sz;
68690Sstevel@tonic-gate 	}
68700Sstevel@tonic-gate 
68710Sstevel@tonic-gate 	return (1);
68720Sstevel@tonic-gate }
68730Sstevel@tonic-gate 
68740Sstevel@tonic-gate /*
68750Sstevel@tonic-gate  * Return 1 if any of the given transaction commands affect
68760Sstevel@tonic-gate  * "action_authorization".  Return -1 if any of the commands are invalid and
68770Sstevel@tonic-gate  * 0 in all other cases.
68780Sstevel@tonic-gate  */
68790Sstevel@tonic-gate static int
tx_modifies_action(const void * cmds_arg,size_t cmds_sz)68800Sstevel@tonic-gate tx_modifies_action(const void *cmds_arg, size_t cmds_sz)
68810Sstevel@tonic-gate {
68820Sstevel@tonic-gate 	const struct rep_protocol_transaction_cmd *cmds;
68830Sstevel@tonic-gate 	uintptr_t loc;
68840Sstevel@tonic-gate 	uint32_t sz;
68850Sstevel@tonic-gate 
68860Sstevel@tonic-gate 	loc = (uintptr_t)cmds_arg;
68870Sstevel@tonic-gate 
68880Sstevel@tonic-gate 	while (cmds_sz > 0) {
68890Sstevel@tonic-gate 		cmds = (struct rep_protocol_transaction_cmd *)loc;
68900Sstevel@tonic-gate 
68910Sstevel@tonic-gate 		if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
68920Sstevel@tonic-gate 			return (-1);
68930Sstevel@tonic-gate 
68940Sstevel@tonic-gate 		sz = cmds->rptc_size;
68950Sstevel@tonic-gate 		if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
68960Sstevel@tonic-gate 			return (-1);
68970Sstevel@tonic-gate 
68980Sstevel@tonic-gate 		sz = TX_SIZE(sz);
68990Sstevel@tonic-gate 		if (sz > cmds_sz)
69000Sstevel@tonic-gate 			return (-1);
69010Sstevel@tonic-gate 
69020Sstevel@tonic-gate 		if (strcmp((const char *)cmds[0].rptc_data, AUTH_PROP_ACTION)
69030Sstevel@tonic-gate 		    == 0)
69040Sstevel@tonic-gate 			return (1);
69050Sstevel@tonic-gate 
69060Sstevel@tonic-gate 		loc += sz;
69070Sstevel@tonic-gate 		cmds_sz -= sz;
69080Sstevel@tonic-gate 	}
69090Sstevel@tonic-gate 
69100Sstevel@tonic-gate 	return (0);
69110Sstevel@tonic-gate }
69120Sstevel@tonic-gate 
69130Sstevel@tonic-gate /*
69140Sstevel@tonic-gate  * Returns 1 if the transaction commands only modify properties named
69150Sstevel@tonic-gate  * 'enabled'.
69160Sstevel@tonic-gate  */
69170Sstevel@tonic-gate static int
tx_only_enabled(const void * cmds_arg,size_t cmds_sz)69180Sstevel@tonic-gate tx_only_enabled(const void *cmds_arg, size_t cmds_sz)
69190Sstevel@tonic-gate {
69200Sstevel@tonic-gate 	const struct rep_protocol_transaction_cmd *cmd;
69210Sstevel@tonic-gate 	uintptr_t loc;
69220Sstevel@tonic-gate 	uint32_t sz;
69230Sstevel@tonic-gate 
69240Sstevel@tonic-gate 	loc = (uintptr_t)cmds_arg;
69250Sstevel@tonic-gate 
69260Sstevel@tonic-gate 	while (cmds_sz > 0) {
69270Sstevel@tonic-gate 		cmd = (struct rep_protocol_transaction_cmd *)loc;
69280Sstevel@tonic-gate 
69290Sstevel@tonic-gate 		if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
69300Sstevel@tonic-gate 			return (-1);
69310Sstevel@tonic-gate 
69320Sstevel@tonic-gate 		sz = cmd->rptc_size;
69330Sstevel@tonic-gate 		if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
69340Sstevel@tonic-gate 			return (-1);
69350Sstevel@tonic-gate 
69360Sstevel@tonic-gate 		sz = TX_SIZE(sz);
69370Sstevel@tonic-gate 		if (sz > cmds_sz)
69380Sstevel@tonic-gate 			return (-1);
69390Sstevel@tonic-gate 
69400Sstevel@tonic-gate 		if (strcmp((const char *)cmd->rptc_data, AUTH_PROP_ENABLED)
69410Sstevel@tonic-gate 		    != 0)
69420Sstevel@tonic-gate 			return (0);
69430Sstevel@tonic-gate 
69440Sstevel@tonic-gate 		loc += sz;
69450Sstevel@tonic-gate 		cmds_sz -= sz;
69460Sstevel@tonic-gate 	}
69470Sstevel@tonic-gate 
69480Sstevel@tonic-gate 	return (1);
69490Sstevel@tonic-gate }
69500Sstevel@tonic-gate 
69510Sstevel@tonic-gate int
rc_tx_commit(rc_node_ptr_t * txp,const void * cmds,size_t cmds_sz)69520Sstevel@tonic-gate rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz)
69530Sstevel@tonic-gate {
69540Sstevel@tonic-gate 	rc_node_t *np = txp->rnp_node;
69550Sstevel@tonic-gate 	rc_node_t *pp;
69560Sstevel@tonic-gate 	rc_node_t *nnp;
69570Sstevel@tonic-gate 	rc_node_pg_notify_t *pnp;
69580Sstevel@tonic-gate 	int rc;
69590Sstevel@tonic-gate 	permcheck_t *pcp;
69608497SThomas.Whitten@Sun.COM 	perm_status_t granted;
69618497SThomas.Whitten@Sun.COM 	int normal;
69625777Stw21770 	char *pg_fmri = NULL;
69635777Stw21770 	char *auth_string = NULL;
69645777Stw21770 	int auth_status = ADT_SUCCESS;
69655777Stw21770 	int auth_ret_value = ADT_SUCCESS;
69665777Stw21770 	size_t sz_out;
69675777Stw21770 	int tx_flag = 1;
69685777Stw21770 	tx_commit_data_t *tx_data = NULL;
69690Sstevel@tonic-gate 
69700Sstevel@tonic-gate 	RC_NODE_CHECK(np);
69710Sstevel@tonic-gate 
69725777Stw21770 	if ((txp->rnp_authorized != RC_AUTH_UNKNOWN) &&
69735777Stw21770 	    (txp->rnp_auth_string != NULL)) {
69745777Stw21770 		auth_string = strdup(txp->rnp_auth_string);
69755777Stw21770 		if (auth_string == NULL)
69765777Stw21770 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
69775777Stw21770 	}
69785777Stw21770 
69795777Stw21770 	if ((txp->rnp_authorized == RC_AUTH_UNKNOWN) &&
69805777Stw21770 	    is_main_repository) {
69810Sstevel@tonic-gate #ifdef NATIVE_BUILD
69825777Stw21770 		if (!client_is_privileged()) {
69835777Stw21770 			return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
69845777Stw21770 		}
69850Sstevel@tonic-gate #else
69860Sstevel@tonic-gate 		/* permission check: depends on contents of transaction */
69870Sstevel@tonic-gate 		pcp = pc_create();
69880Sstevel@tonic-gate 		if (pcp == NULL)
69890Sstevel@tonic-gate 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
69900Sstevel@tonic-gate 
69910Sstevel@tonic-gate 		/* If normal is cleared, we won't do the normal checks. */
69920Sstevel@tonic-gate 		normal = 1;
69930Sstevel@tonic-gate 		rc = REP_PROTOCOL_SUCCESS;
69940Sstevel@tonic-gate 
69950Sstevel@tonic-gate 		if (strcmp(np->rn_name, AUTH_PG_GENERAL) == 0 &&
69960Sstevel@tonic-gate 		    strcmp(np->rn_type, AUTH_PG_GENERAL_TYPE) == 0) {
69970Sstevel@tonic-gate 			/* Touching general[framework]/action_authorization? */
69980Sstevel@tonic-gate 			rc = tx_modifies_action(cmds, cmds_sz);
69990Sstevel@tonic-gate 			if (rc == -1) {
70000Sstevel@tonic-gate 				pc_free(pcp);
70010Sstevel@tonic-gate 				return (REP_PROTOCOL_FAIL_BAD_REQUEST);
70020Sstevel@tonic-gate 			}
70030Sstevel@tonic-gate 
70040Sstevel@tonic-gate 			if (rc) {
700511223STruong.Q.Nguyen@Sun.COM 				/*
700611223STruong.Q.Nguyen@Sun.COM 				 * Yes: only AUTH_MODIFY and AUTH_MANAGE
700711223STruong.Q.Nguyen@Sun.COM 				 * can be used.
700811223STruong.Q.Nguyen@Sun.COM 				 */
700911223STruong.Q.Nguyen@Sun.COM 				rc = perm_add_enabling(pcp, AUTH_MODIFY);
701011223STruong.Q.Nguyen@Sun.COM 
701111223STruong.Q.Nguyen@Sun.COM 				if (rc == REP_PROTOCOL_SUCCESS)
701211223STruong.Q.Nguyen@Sun.COM 					rc = perm_add_enabling(pcp,
701311223STruong.Q.Nguyen@Sun.COM 					    AUTH_MANAGE);
701411223STruong.Q.Nguyen@Sun.COM 
70150Sstevel@tonic-gate 				normal = 0;
70160Sstevel@tonic-gate 			} else {
70170Sstevel@tonic-gate 				rc = REP_PROTOCOL_SUCCESS;
70180Sstevel@tonic-gate 			}
70190Sstevel@tonic-gate 		} else if (np->rn_id.rl_ids[ID_INSTANCE] != 0 &&
70200Sstevel@tonic-gate 		    strcmp(np->rn_name, AUTH_PG_GENERAL_OVR) == 0 &&
70210Sstevel@tonic-gate 		    strcmp(np->rn_type, AUTH_PG_GENERAL_OVR_TYPE) == 0) {
70220Sstevel@tonic-gate 			rc_node_t *instn;
70230Sstevel@tonic-gate 
70240Sstevel@tonic-gate 			rc = tx_only_enabled(cmds, cmds_sz);
70250Sstevel@tonic-gate 			if (rc == -1) {
70260Sstevel@tonic-gate 				pc_free(pcp);
70270Sstevel@tonic-gate 				return (REP_PROTOCOL_FAIL_BAD_REQUEST);
70280Sstevel@tonic-gate 			}
70290Sstevel@tonic-gate 
70300Sstevel@tonic-gate 			if (rc) {
70310Sstevel@tonic-gate 				rc = rc_node_parent(np, &instn);
70320Sstevel@tonic-gate 				if (rc != REP_PROTOCOL_SUCCESS) {
70330Sstevel@tonic-gate 					assert(rc == REP_PROTOCOL_FAIL_DELETED);
70340Sstevel@tonic-gate 					pc_free(pcp);
70350Sstevel@tonic-gate 					return (rc);
70360Sstevel@tonic-gate 				}
70370Sstevel@tonic-gate 
70380Sstevel@tonic-gate 				assert(instn->rn_id.rl_type ==
70390Sstevel@tonic-gate 				    REP_PROTOCOL_ENTITY_INSTANCE);
70400Sstevel@tonic-gate 
70410Sstevel@tonic-gate 				rc = perm_add_inst_action_auth(pcp, instn);
70420Sstevel@tonic-gate 				rc_node_rele(instn);
70430Sstevel@tonic-gate 				switch (rc) {
70440Sstevel@tonic-gate 				case REP_PROTOCOL_SUCCESS:
70450Sstevel@tonic-gate 					break;
70460Sstevel@tonic-gate 
70470Sstevel@tonic-gate 				case REP_PROTOCOL_FAIL_DELETED:
70480Sstevel@tonic-gate 				case REP_PROTOCOL_FAIL_NO_RESOURCES:
70490Sstevel@tonic-gate 					pc_free(pcp);
70500Sstevel@tonic-gate 					return (rc);
70510Sstevel@tonic-gate 
70520Sstevel@tonic-gate 				default:
70530Sstevel@tonic-gate 					bad_error("perm_add_inst_action_auth",
70540Sstevel@tonic-gate 					    rc);
70550Sstevel@tonic-gate 				}
70560Sstevel@tonic-gate 			} else {
70570Sstevel@tonic-gate 				rc = REP_PROTOCOL_SUCCESS;
70580Sstevel@tonic-gate 			}
70590Sstevel@tonic-gate 		}
70600Sstevel@tonic-gate 
70610Sstevel@tonic-gate 		if (rc == REP_PROTOCOL_SUCCESS && normal) {
70620Sstevel@tonic-gate 			rc = perm_add_enabling(pcp, AUTH_MODIFY);
70630Sstevel@tonic-gate 
70640Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS) {
70650Sstevel@tonic-gate 				/* Add pgtype-specific authorization. */
70660Sstevel@tonic-gate 				const char * const auth =
70670Sstevel@tonic-gate 				    perm_auth_for_pgtype(np->rn_type);
70680Sstevel@tonic-gate 
70690Sstevel@tonic-gate 				if (auth != NULL)
70700Sstevel@tonic-gate 					rc = perm_add_enabling(pcp, auth);
70710Sstevel@tonic-gate 			}
70720Sstevel@tonic-gate 
70730Sstevel@tonic-gate 			/* Add pg-specific modify_authorization auths. */
70740Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS)
70750Sstevel@tonic-gate 				rc = perm_add_enabling_values(pcp, np,
70760Sstevel@tonic-gate 				    AUTH_PROP_MODIFY);
70770Sstevel@tonic-gate 
70780Sstevel@tonic-gate 			/* If value_authorization values are ok, add them. */
70790Sstevel@tonic-gate 			if (rc == REP_PROTOCOL_SUCCESS) {
70800Sstevel@tonic-gate 				rc = tx_allow_value(cmds, cmds_sz, np);
70810Sstevel@tonic-gate 				if (rc == -1)
70820Sstevel@tonic-gate 					rc = REP_PROTOCOL_FAIL_BAD_REQUEST;
70830Sstevel@tonic-gate 				else if (rc)
70840Sstevel@tonic-gate 					rc = perm_add_enabling_values(pcp, np,
70850Sstevel@tonic-gate 					    AUTH_PROP_VALUE);
70860Sstevel@tonic-gate 			}
70870Sstevel@tonic-gate 		}
70880Sstevel@tonic-gate 
70890Sstevel@tonic-gate 		if (rc == REP_PROTOCOL_SUCCESS) {
70900Sstevel@tonic-gate 			granted = perm_granted(pcp);
70918497SThomas.Whitten@Sun.COM 			rc = map_granted_status(granted, pcp, &auth_string);
70928497SThomas.Whitten@Sun.COM 			if ((granted == PERM_DENIED) && auth_string) {
70935777Stw21770 				/*
70948497SThomas.Whitten@Sun.COM 				 * _PERMISSION_DENIED should not cause us
70958497SThomas.Whitten@Sun.COM 				 * to exit at this point, because we still
70968497SThomas.Whitten@Sun.COM 				 * want to generate an audit event.
70975777Stw21770 				 */
70988497SThomas.Whitten@Sun.COM 				rc = REP_PROTOCOL_SUCCESS;
70995777Stw21770 			}
71000Sstevel@tonic-gate 		}
71010Sstevel@tonic-gate 
71020Sstevel@tonic-gate 		pc_free(pcp);
71030Sstevel@tonic-gate 
71040Sstevel@tonic-gate 		if (rc != REP_PROTOCOL_SUCCESS)
71055777Stw21770 			goto cleanout;
71065777Stw21770 
71078497SThomas.Whitten@Sun.COM 		if (granted == PERM_DENIED) {
71085777Stw21770 			auth_status = ADT_FAILURE;
71095777Stw21770 			auth_ret_value = ADT_FAIL_VALUE_AUTH;
71105777Stw21770 			tx_flag = 0;
71115777Stw21770 		}
71120Sstevel@tonic-gate #endif /* NATIVE_BUILD */
71135777Stw21770 	} else if (txp->rnp_authorized == RC_AUTH_FAILED) {
71145777Stw21770 		auth_status = ADT_FAILURE;
71155777Stw21770 		auth_ret_value = ADT_FAIL_VALUE_AUTH;
71165777Stw21770 		tx_flag = 0;
71175777Stw21770 	}
71185777Stw21770 
71195777Stw21770 	pg_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
71205777Stw21770 	if (pg_fmri == NULL) {
71215777Stw21770 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
71225777Stw21770 		goto cleanout;
71235777Stw21770 	}
71245777Stw21770 	if ((rc = rc_node_get_fmri_or_fragment(np, pg_fmri,
71255777Stw21770 	    REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
71265777Stw21770 		goto cleanout;
71275777Stw21770 	}
71285777Stw21770 
71295777Stw21770 	/*
71305777Stw21770 	 * Parse the transaction commands into a useful form.
71315777Stw21770 	 */
71325777Stw21770 	if ((rc = tx_commit_data_new(cmds, cmds_sz, &tx_data)) !=
71335777Stw21770 	    REP_PROTOCOL_SUCCESS) {
71345777Stw21770 		goto cleanout;
71355777Stw21770 	}
71365777Stw21770 
71375777Stw21770 	if (tx_flag == 0) {
71385777Stw21770 		/* Authorization failed.  Generate audit events. */
71395777Stw21770 		generate_property_events(tx_data, pg_fmri, auth_string,
71405777Stw21770 		    auth_status, auth_ret_value);
71415777Stw21770 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
71425777Stw21770 		goto cleanout;
71430Sstevel@tonic-gate 	}
71440Sstevel@tonic-gate 
71450Sstevel@tonic-gate 	nnp = rc_node_alloc();
71465777Stw21770 	if (nnp == NULL) {
71475777Stw21770 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
71485777Stw21770 		goto cleanout;
71495777Stw21770 	}
71500Sstevel@tonic-gate 
71510Sstevel@tonic-gate 	nnp->rn_id = np->rn_id;			/* structure assignment */
71520Sstevel@tonic-gate 	nnp->rn_hash = np->rn_hash;
71530Sstevel@tonic-gate 	nnp->rn_name = strdup(np->rn_name);
71540Sstevel@tonic-gate 	nnp->rn_type = strdup(np->rn_type);
71550Sstevel@tonic-gate 	nnp->rn_pgflags = np->rn_pgflags;
71560Sstevel@tonic-gate 
71570Sstevel@tonic-gate 	nnp->rn_flags = RC_NODE_IN_TX | RC_NODE_USING_PARENT;
71580Sstevel@tonic-gate 
71590Sstevel@tonic-gate 	if (nnp->rn_name == NULL || nnp->rn_type == NULL) {
71600Sstevel@tonic-gate 		rc_node_destroy(nnp);
71615777Stw21770 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
71625777Stw21770 		goto cleanout;
71630Sstevel@tonic-gate 	}
71640Sstevel@tonic-gate 
71650Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
71665777Stw21770 
71670Sstevel@tonic-gate 	/*
71680Sstevel@tonic-gate 	 * We must have all of the old properties in the cache, or the
71690Sstevel@tonic-gate 	 * database deletions could cause inconsistencies.
71700Sstevel@tonic-gate 	 */
71710Sstevel@tonic-gate 	if ((rc = rc_node_fill_children(np, REP_PROTOCOL_ENTITY_PROPERTY)) !=
71720Sstevel@tonic-gate 	    REP_PROTOCOL_SUCCESS) {
71730Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
71740Sstevel@tonic-gate 		rc_node_destroy(nnp);
71755777Stw21770 		goto cleanout;
71760Sstevel@tonic-gate 	}
71770Sstevel@tonic-gate 
71780Sstevel@tonic-gate 	if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
71790Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
71800Sstevel@tonic-gate 		rc_node_destroy(nnp);
71815777Stw21770 		rc = REP_PROTOCOL_FAIL_DELETED;
71825777Stw21770 		goto cleanout;
71830Sstevel@tonic-gate 	}
71840Sstevel@tonic-gate 
71850Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_OLD) {
71860Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_USING_PARENT);
71870Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
71880Sstevel@tonic-gate 		rc_node_destroy(nnp);
71895777Stw21770 		rc = REP_PROTOCOL_FAIL_NOT_LATEST;
71905777Stw21770 		goto cleanout;
71910Sstevel@tonic-gate 	}
71920Sstevel@tonic-gate 
71930Sstevel@tonic-gate 	pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
71940Sstevel@tonic-gate 	if (pp == NULL) {
71950Sstevel@tonic-gate 		/* our parent is gone, we're going next... */
71960Sstevel@tonic-gate 		rc_node_destroy(nnp);
71970Sstevel@tonic-gate 		(void) pthread_mutex_lock(&np->rn_lock);
71980Sstevel@tonic-gate 		if (np->rn_flags & RC_NODE_OLD) {
71990Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&np->rn_lock);
72005777Stw21770 			rc = REP_PROTOCOL_FAIL_NOT_LATEST;
72015777Stw21770 			goto cleanout;
72020Sstevel@tonic-gate 		}
72030Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
72045777Stw21770 		rc = REP_PROTOCOL_FAIL_DELETED;
72055777Stw21770 		goto cleanout;
72060Sstevel@tonic-gate 	}
72070Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&pp->rn_lock);
72080Sstevel@tonic-gate 
72090Sstevel@tonic-gate 	/*
72100Sstevel@tonic-gate 	 * prepare for the transaction
72110Sstevel@tonic-gate 	 */
72120Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
72130Sstevel@tonic-gate 	if (!rc_node_hold_flag(np, RC_NODE_IN_TX)) {
72140Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
72150Sstevel@tonic-gate 		(void) pthread_mutex_lock(&pp->rn_lock);
72160Sstevel@tonic-gate 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
72170Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&pp->rn_lock);
72180Sstevel@tonic-gate 		rc_node_destroy(nnp);
72195777Stw21770 		rc = REP_PROTOCOL_FAIL_DELETED;
72205777Stw21770 		goto cleanout;
72210Sstevel@tonic-gate 	}
72220Sstevel@tonic-gate 	nnp->rn_gen_id = np->rn_gen_id;
72230Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
72240Sstevel@tonic-gate 
72250Sstevel@tonic-gate 	/* Sets nnp->rn_gen_id on success. */
72265777Stw21770 	rc = object_tx_commit(&np->rn_id, tx_data, &nnp->rn_gen_id);
72270Sstevel@tonic-gate 
72280Sstevel@tonic-gate 	(void) pthread_mutex_lock(&np->rn_lock);
72290Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS) {
72300Sstevel@tonic-gate 		rc_node_rele_flag(np, RC_NODE_IN_TX);
72310Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
72320Sstevel@tonic-gate 		(void) pthread_mutex_lock(&pp->rn_lock);
72330Sstevel@tonic-gate 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
72340Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&pp->rn_lock);
72350Sstevel@tonic-gate 		rc_node_destroy(nnp);
72360Sstevel@tonic-gate 		rc_node_clear(txp, 0);
72370Sstevel@tonic-gate 		if (rc == REP_PROTOCOL_DONE)
72380Sstevel@tonic-gate 			rc = REP_PROTOCOL_SUCCESS; /* successful empty tx */
72395777Stw21770 		goto cleanout;
72400Sstevel@tonic-gate 	}
72410Sstevel@tonic-gate 
72420Sstevel@tonic-gate 	/*
72430Sstevel@tonic-gate 	 * Notify waiters
72440Sstevel@tonic-gate 	 */
72450Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
72460Sstevel@tonic-gate 	while ((pnp = uu_list_first(np->rn_pg_notify_list)) != NULL)
72470Sstevel@tonic-gate 		rc_pg_notify_fire(pnp);
72480Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
72490Sstevel@tonic-gate 
72500Sstevel@tonic-gate 	np->rn_flags |= RC_NODE_OLD;
72510Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
72520Sstevel@tonic-gate 
72530Sstevel@tonic-gate 	rc_notify_remove_node(np);
72540Sstevel@tonic-gate 
72550Sstevel@tonic-gate 	/*
72560Sstevel@tonic-gate 	 * replace np with nnp
72570Sstevel@tonic-gate 	 */
72580Sstevel@tonic-gate 	rc_node_relink_child(pp, np, nnp);
72590Sstevel@tonic-gate 
72600Sstevel@tonic-gate 	/*
72610Sstevel@tonic-gate 	 * all done -- clear the transaction.
72620Sstevel@tonic-gate 	 */
72630Sstevel@tonic-gate 	rc_node_clear(txp, 0);
72645777Stw21770 	generate_property_events(tx_data, pg_fmri, auth_string,
72655777Stw21770 	    auth_status, auth_ret_value);
72665777Stw21770 
72675777Stw21770 	rc = REP_PROTOCOL_SUCCESS;
72685777Stw21770 
72695777Stw21770 cleanout:
72705777Stw21770 	free(auth_string);
72715777Stw21770 	free(pg_fmri);
72725777Stw21770 	tx_commit_data_free(tx_data);
72735777Stw21770 	return (rc);
72740Sstevel@tonic-gate }
72750Sstevel@tonic-gate 
72760Sstevel@tonic-gate void
rc_pg_notify_init(rc_node_pg_notify_t * pnp)72770Sstevel@tonic-gate rc_pg_notify_init(rc_node_pg_notify_t *pnp)
72780Sstevel@tonic-gate {
72790Sstevel@tonic-gate 	uu_list_node_init(pnp, &pnp->rnpn_node, rc_pg_notify_pool);
72800Sstevel@tonic-gate 	pnp->rnpn_pg = NULL;
72810Sstevel@tonic-gate 	pnp->rnpn_fd = -1;
72820Sstevel@tonic-gate }
72830Sstevel@tonic-gate 
72840Sstevel@tonic-gate int
rc_pg_notify_setup(rc_node_pg_notify_t * pnp,rc_node_ptr_t * npp,int fd)72850Sstevel@tonic-gate rc_pg_notify_setup(rc_node_pg_notify_t *pnp, rc_node_ptr_t *npp, int fd)
72860Sstevel@tonic-gate {
72870Sstevel@tonic-gate 	rc_node_t *np;
72880Sstevel@tonic-gate 
72890Sstevel@tonic-gate 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
72900Sstevel@tonic-gate 
72910Sstevel@tonic-gate 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
72920Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
72930Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
72940Sstevel@tonic-gate 	}
72950Sstevel@tonic-gate 
72960Sstevel@tonic-gate 	/*
72970Sstevel@tonic-gate 	 * wait for any transaction in progress to complete
72980Sstevel@tonic-gate 	 */
72990Sstevel@tonic-gate 	if (!rc_node_wait_flag(np, RC_NODE_IN_TX)) {
73000Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
73010Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_DELETED);
73020Sstevel@tonic-gate 	}
73030Sstevel@tonic-gate 
73040Sstevel@tonic-gate 	if (np->rn_flags & RC_NODE_OLD) {
73050Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&np->rn_lock);
73060Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NOT_LATEST);
73070Sstevel@tonic-gate 	}
73080Sstevel@tonic-gate 
73090Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
73100Sstevel@tonic-gate 	rc_pg_notify_fire(pnp);
73110Sstevel@tonic-gate 	pnp->rnpn_pg = np;
73120Sstevel@tonic-gate 	pnp->rnpn_fd = fd;
73130Sstevel@tonic-gate 	(void) uu_list_insert_after(np->rn_pg_notify_list, NULL, pnp);
73140Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
73150Sstevel@tonic-gate 
73160Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&np->rn_lock);
73170Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
73180Sstevel@tonic-gate }
73190Sstevel@tonic-gate 
73200Sstevel@tonic-gate void
rc_pg_notify_fini(rc_node_pg_notify_t * pnp)73210Sstevel@tonic-gate rc_pg_notify_fini(rc_node_pg_notify_t *pnp)
73220Sstevel@tonic-gate {
73230Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
73240Sstevel@tonic-gate 	rc_pg_notify_fire(pnp);
73250Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
73260Sstevel@tonic-gate 
73270Sstevel@tonic-gate 	uu_list_node_fini(pnp, &pnp->rnpn_node, rc_pg_notify_pool);
73280Sstevel@tonic-gate }
73290Sstevel@tonic-gate 
73300Sstevel@tonic-gate void
rc_notify_info_init(rc_notify_info_t * rnip)73310Sstevel@tonic-gate rc_notify_info_init(rc_notify_info_t *rnip)
73320Sstevel@tonic-gate {
73330Sstevel@tonic-gate 	int i;
73340Sstevel@tonic-gate 
73350Sstevel@tonic-gate 	uu_list_node_init(rnip, &rnip->rni_list_node, rc_notify_info_pool);
73360Sstevel@tonic-gate 	uu_list_node_init(&rnip->rni_notify, &rnip->rni_notify.rcn_list_node,
73370Sstevel@tonic-gate 	    rc_notify_pool);
73380Sstevel@tonic-gate 
73390Sstevel@tonic-gate 	rnip->rni_notify.rcn_node = NULL;
73400Sstevel@tonic-gate 	rnip->rni_notify.rcn_info = rnip;
73410Sstevel@tonic-gate 
73420Sstevel@tonic-gate 	bzero(rnip->rni_namelist, sizeof (rnip->rni_namelist));
73430Sstevel@tonic-gate 	bzero(rnip->rni_typelist, sizeof (rnip->rni_typelist));
73440Sstevel@tonic-gate 
73450Sstevel@tonic-gate 	(void) pthread_cond_init(&rnip->rni_cv, NULL);
73460Sstevel@tonic-gate 
73470Sstevel@tonic-gate 	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
73480Sstevel@tonic-gate 		rnip->rni_namelist[i] = NULL;
73490Sstevel@tonic-gate 		rnip->rni_typelist[i] = NULL;
73500Sstevel@tonic-gate 	}
73510Sstevel@tonic-gate }
73520Sstevel@tonic-gate 
73530Sstevel@tonic-gate static void
rc_notify_info_insert_locked(rc_notify_info_t * rnip)73540Sstevel@tonic-gate rc_notify_info_insert_locked(rc_notify_info_t *rnip)
73550Sstevel@tonic-gate {
73560Sstevel@tonic-gate 	assert(MUTEX_HELD(&rc_pg_notify_lock));
73570Sstevel@tonic-gate 
73580Sstevel@tonic-gate 	assert(!(rnip->rni_flags & RC_NOTIFY_ACTIVE));
73590Sstevel@tonic-gate 
73600Sstevel@tonic-gate 	rnip->rni_flags |= RC_NOTIFY_ACTIVE;
73610Sstevel@tonic-gate 	(void) uu_list_insert_after(rc_notify_info_list, NULL, rnip);
73620Sstevel@tonic-gate 	(void) uu_list_insert_before(rc_notify_list, NULL, &rnip->rni_notify);
73630Sstevel@tonic-gate }
73640Sstevel@tonic-gate 
73650Sstevel@tonic-gate static void
rc_notify_info_remove_locked(rc_notify_info_t * rnip)73660Sstevel@tonic-gate rc_notify_info_remove_locked(rc_notify_info_t *rnip)
73670Sstevel@tonic-gate {
73680Sstevel@tonic-gate 	rc_notify_t *me = &rnip->rni_notify;
73690Sstevel@tonic-gate 	rc_notify_t *np;
73700Sstevel@tonic-gate 
73710Sstevel@tonic-gate 	assert(MUTEX_HELD(&rc_pg_notify_lock));
73720Sstevel@tonic-gate 
73730Sstevel@tonic-gate 	assert(rnip->rni_flags & RC_NOTIFY_ACTIVE);
73740Sstevel@tonic-gate 
73750Sstevel@tonic-gate 	assert(!(rnip->rni_flags & RC_NOTIFY_DRAIN));
73760Sstevel@tonic-gate 	rnip->rni_flags |= RC_NOTIFY_DRAIN;
73770Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&rnip->rni_cv);
73780Sstevel@tonic-gate 
73790Sstevel@tonic-gate 	(void) uu_list_remove(rc_notify_info_list, rnip);
73800Sstevel@tonic-gate 
73810Sstevel@tonic-gate 	/*
73820Sstevel@tonic-gate 	 * clean up any notifications at the beginning of the list
73830Sstevel@tonic-gate 	 */
73840Sstevel@tonic-gate 	if (uu_list_first(rc_notify_list) == me) {
738512299Stom.whitten@oracle.com 		/*
738612299Stom.whitten@oracle.com 		 * We can't call rc_notify_remove_locked() unless
738712299Stom.whitten@oracle.com 		 * rc_notify_in_use is 0.
738812299Stom.whitten@oracle.com 		 */
738912299Stom.whitten@oracle.com 		while (rc_notify_in_use) {
739012299Stom.whitten@oracle.com 			(void) pthread_cond_wait(&rc_pg_notify_cv,
739112299Stom.whitten@oracle.com 			    &rc_pg_notify_lock);
739212299Stom.whitten@oracle.com 		}
73930Sstevel@tonic-gate 		while ((np = uu_list_next(rc_notify_list, me)) != NULL &&
73940Sstevel@tonic-gate 		    np->rcn_info == NULL)
73950Sstevel@tonic-gate 			rc_notify_remove_locked(np);
73960Sstevel@tonic-gate 	}
73970Sstevel@tonic-gate 	(void) uu_list_remove(rc_notify_list, me);
73980Sstevel@tonic-gate 
73990Sstevel@tonic-gate 	while (rnip->rni_waiters) {
74000Sstevel@tonic-gate 		(void) pthread_cond_broadcast(&rc_pg_notify_cv);
74010Sstevel@tonic-gate 		(void) pthread_cond_broadcast(&rnip->rni_cv);
74020Sstevel@tonic-gate 		(void) pthread_cond_wait(&rnip->rni_cv, &rc_pg_notify_lock);
74030Sstevel@tonic-gate 	}
74040Sstevel@tonic-gate 
74050Sstevel@tonic-gate 	rnip->rni_flags &= ~(RC_NOTIFY_DRAIN | RC_NOTIFY_ACTIVE);
74060Sstevel@tonic-gate }
74070Sstevel@tonic-gate 
74080Sstevel@tonic-gate static int
rc_notify_info_add_watch(rc_notify_info_t * rnip,const char ** arr,const char * name)74090Sstevel@tonic-gate rc_notify_info_add_watch(rc_notify_info_t *rnip, const char **arr,
74100Sstevel@tonic-gate     const char *name)
74110Sstevel@tonic-gate {
74120Sstevel@tonic-gate 	int i;
74130Sstevel@tonic-gate 	int rc;
74140Sstevel@tonic-gate 	char *f;
74150Sstevel@tonic-gate 
74160Sstevel@tonic-gate 	rc = rc_check_type_name(REP_PROTOCOL_ENTITY_PROPERTYGRP, name);
74170Sstevel@tonic-gate 	if (rc != REP_PROTOCOL_SUCCESS)
74180Sstevel@tonic-gate 		return (rc);
74190Sstevel@tonic-gate 
74200Sstevel@tonic-gate 	f = strdup(name);
74210Sstevel@tonic-gate 	if (f == NULL)
74220Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
74230Sstevel@tonic-gate 
74240Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
74250Sstevel@tonic-gate 
74260Sstevel@tonic-gate 	while (rnip->rni_flags & RC_NOTIFY_EMPTYING)
74270Sstevel@tonic-gate 		(void) pthread_cond_wait(&rnip->rni_cv, &rc_pg_notify_lock);
74280Sstevel@tonic-gate 
74296961Stn143363 	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
74300Sstevel@tonic-gate 		if (arr[i] == NULL)
74310Sstevel@tonic-gate 			break;
74320Sstevel@tonic-gate 
74336961Stn143363 		/*
74346961Stn143363 		 * Don't add name if it's already being tracked.
74356961Stn143363 		 */
74366961Stn143363 		if (strcmp(arr[i], f) == 0) {
74376961Stn143363 			free(f);
74386961Stn143363 			goto out;
74396961Stn143363 		}
74406961Stn143363 	}
74416961Stn143363 
74420Sstevel@tonic-gate 	if (i == RC_NOTIFY_MAX_NAMES) {
74430Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
74440Sstevel@tonic-gate 		free(f);
74450Sstevel@tonic-gate 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
74460Sstevel@tonic-gate 	}
74470Sstevel@tonic-gate 
74480Sstevel@tonic-gate 	arr[i] = f;
74496961Stn143363 
74506961Stn143363 out:
74510Sstevel@tonic-gate 	if (!(rnip->rni_flags & RC_NOTIFY_ACTIVE))
74520Sstevel@tonic-gate 		rc_notify_info_insert_locked(rnip);
74530Sstevel@tonic-gate 
74540Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
74550Sstevel@tonic-gate 	return (REP_PROTOCOL_SUCCESS);
74560Sstevel@tonic-gate }
74570Sstevel@tonic-gate 
74580Sstevel@tonic-gate int
rc_notify_info_add_name(rc_notify_info_t * rnip,const char * name)74590Sstevel@tonic-gate rc_notify_info_add_name(rc_notify_info_t *rnip, const char *name)
74600Sstevel@tonic-gate {
74610Sstevel@tonic-gate 	return (rc_notify_info_add_watch(rnip, rnip->rni_namelist, name));
74620Sstevel@tonic-gate }
74630Sstevel@tonic-gate 
74640Sstevel@tonic-gate int
rc_notify_info_add_type(rc_notify_info_t * rnip,const char * type)74650Sstevel@tonic-gate rc_notify_info_add_type(rc_notify_info_t *rnip, const char *type)
74660Sstevel@tonic-gate {
74670Sstevel@tonic-gate 	return (rc_notify_info_add_watch(rnip, rnip->rni_typelist, type));
74680Sstevel@tonic-gate }
74690Sstevel@tonic-gate 
74700Sstevel@tonic-gate /*
74710Sstevel@tonic-gate  * Wait for and report an event of interest to rnip, a notification client
74720Sstevel@tonic-gate  */
74730Sstevel@tonic-gate int
rc_notify_info_wait(rc_notify_info_t * rnip,rc_node_ptr_t * out,char * outp,size_t sz)74740Sstevel@tonic-gate rc_notify_info_wait(rc_notify_info_t *rnip, rc_node_ptr_t *out,
74750Sstevel@tonic-gate     char *outp, size_t sz)
74760Sstevel@tonic-gate {
74770Sstevel@tonic-gate 	rc_notify_t *np;
74780Sstevel@tonic-gate 	rc_notify_t *me = &rnip->rni_notify;
74790Sstevel@tonic-gate 	rc_node_t *nnp;
74800Sstevel@tonic-gate 	rc_notify_delete_t *ndp;
74810Sstevel@tonic-gate 
74820Sstevel@tonic-gate 	int am_first_info;
74830Sstevel@tonic-gate 
74840Sstevel@tonic-gate 	if (sz > 0)
74850Sstevel@tonic-gate 		outp[0] = 0;
74860Sstevel@tonic-gate 
74870Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
74880Sstevel@tonic-gate 
74890Sstevel@tonic-gate 	while ((rnip->rni_flags & (RC_NOTIFY_ACTIVE | RC_NOTIFY_DRAIN)) ==
74900Sstevel@tonic-gate 	    RC_NOTIFY_ACTIVE) {
74910Sstevel@tonic-gate 		/*
74920Sstevel@tonic-gate 		 * If I'm first on the notify list, it is my job to
74930Sstevel@tonic-gate 		 * clean up any notifications I pass by.  I can't do that
74940Sstevel@tonic-gate 		 * if someone is blocking the list from removals, so I
74950Sstevel@tonic-gate 		 * have to wait until they have all drained.
74960Sstevel@tonic-gate 		 */
74970Sstevel@tonic-gate 		am_first_info = (uu_list_first(rc_notify_list) == me);
74980Sstevel@tonic-gate 		if (am_first_info && rc_notify_in_use) {
74990Sstevel@tonic-gate 			rnip->rni_waiters++;
75000Sstevel@tonic-gate 			(void) pthread_cond_wait(&rc_pg_notify_cv,
75010Sstevel@tonic-gate 			    &rc_pg_notify_lock);
75020Sstevel@tonic-gate 			rnip->rni_waiters--;
75030Sstevel@tonic-gate 			continue;
75040Sstevel@tonic-gate 		}
75050Sstevel@tonic-gate 
75060Sstevel@tonic-gate 		/*
75070Sstevel@tonic-gate 		 * Search the list for a node of interest.
75080Sstevel@tonic-gate 		 */
75090Sstevel@tonic-gate 		np = uu_list_next(rc_notify_list, me);
75100Sstevel@tonic-gate 		while (np != NULL && !rc_notify_info_interested(rnip, np)) {
75110Sstevel@tonic-gate 			rc_notify_t *next = uu_list_next(rc_notify_list, np);
75120Sstevel@tonic-gate 
75130Sstevel@tonic-gate 			if (am_first_info) {
75140Sstevel@tonic-gate 				if (np->rcn_info) {
75150Sstevel@tonic-gate 					/*
75160Sstevel@tonic-gate 					 * Passing another client -- stop
75170Sstevel@tonic-gate 					 * cleaning up notifications
75180Sstevel@tonic-gate 					 */
75190Sstevel@tonic-gate 					am_first_info = 0;
75200Sstevel@tonic-gate 				} else {
75210Sstevel@tonic-gate 					rc_notify_remove_locked(np);
75220Sstevel@tonic-gate 				}
75230Sstevel@tonic-gate 			}
75240Sstevel@tonic-gate 			np = next;
75250Sstevel@tonic-gate 		}
75260Sstevel@tonic-gate 
75270Sstevel@tonic-gate 		/*
75280Sstevel@tonic-gate 		 * Nothing of interest -- wait for notification
75290Sstevel@tonic-gate 		 */
75300Sstevel@tonic-gate 		if (np == NULL) {
75310Sstevel@tonic-gate 			rnip->rni_waiters++;
75320Sstevel@tonic-gate 			(void) pthread_cond_wait(&rnip->rni_cv,
75330Sstevel@tonic-gate 			    &rc_pg_notify_lock);
75340Sstevel@tonic-gate 			rnip->rni_waiters--;
75350Sstevel@tonic-gate 			continue;
75360Sstevel@tonic-gate 		}
75370Sstevel@tonic-gate 
75380Sstevel@tonic-gate 		/*
75390Sstevel@tonic-gate 		 * found something to report -- move myself after the
75400Sstevel@tonic-gate 		 * notification and process it.
75410Sstevel@tonic-gate 		 */
75420Sstevel@tonic-gate 		(void) uu_list_remove(rc_notify_list, me);
75430Sstevel@tonic-gate 		(void) uu_list_insert_after(rc_notify_list, np, me);
75440Sstevel@tonic-gate 
75450Sstevel@tonic-gate 		if ((ndp = np->rcn_delete) != NULL) {
75460Sstevel@tonic-gate 			(void) strlcpy(outp, ndp->rnd_fmri, sz);
75470Sstevel@tonic-gate 			if (am_first_info)
75480Sstevel@tonic-gate 				rc_notify_remove_locked(np);
75490Sstevel@tonic-gate 			(void) pthread_mutex_unlock(&rc_pg_notify_lock);
75500Sstevel@tonic-gate 			rc_node_clear(out, 0);
75510Sstevel@tonic-gate 			return (REP_PROTOCOL_SUCCESS);
75520Sstevel@tonic-gate 		}
75530Sstevel@tonic-gate 
75540Sstevel@tonic-gate 		nnp = np->rcn_node;
75550Sstevel@tonic-gate 		assert(nnp != NULL);
75560Sstevel@tonic-gate 
75570Sstevel@tonic-gate 		/*
75580Sstevel@tonic-gate 		 * We can't bump nnp's reference count without grabbing its
75590Sstevel@tonic-gate 		 * lock, and rc_pg_notify_lock is a leaf lock.  So we
75600Sstevel@tonic-gate 		 * temporarily block all removals to keep nnp from
75610Sstevel@tonic-gate 		 * disappearing.
75620Sstevel@tonic-gate 		 */
75630Sstevel@tonic-gate 		rc_notify_in_use++;
75640Sstevel@tonic-gate 		assert(rc_notify_in_use > 0);
75650Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
75660Sstevel@tonic-gate 
75670Sstevel@tonic-gate 		rc_node_assign(out, nnp);
75680Sstevel@tonic-gate 
75690Sstevel@tonic-gate 		(void) pthread_mutex_lock(&rc_pg_notify_lock);
75700Sstevel@tonic-gate 		assert(rc_notify_in_use > 0);
75710Sstevel@tonic-gate 		rc_notify_in_use--;
757212299Stom.whitten@oracle.com 
757312299Stom.whitten@oracle.com 		if (am_first_info) {
757412299Stom.whitten@oracle.com 			/*
757512299Stom.whitten@oracle.com 			 * While we had the lock dropped, another thread
757612299Stom.whitten@oracle.com 			 * may have also incremented rc_notify_in_use.  We
757712299Stom.whitten@oracle.com 			 * need to make sure that we're back to 0 before
757812299Stom.whitten@oracle.com 			 * removing the node.
757912299Stom.whitten@oracle.com 			 */
758012299Stom.whitten@oracle.com 			while (rc_notify_in_use) {
758112299Stom.whitten@oracle.com 				(void) pthread_cond_wait(&rc_pg_notify_cv,
758212299Stom.whitten@oracle.com 				    &rc_pg_notify_lock);
758312299Stom.whitten@oracle.com 			}
75840Sstevel@tonic-gate 			rc_notify_remove_locked(np);
758512299Stom.whitten@oracle.com 		}
75860Sstevel@tonic-gate 		if (rc_notify_in_use == 0)
75870Sstevel@tonic-gate 			(void) pthread_cond_broadcast(&rc_pg_notify_cv);
75880Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
75890Sstevel@tonic-gate 
75900Sstevel@tonic-gate 		return (REP_PROTOCOL_SUCCESS);
75910Sstevel@tonic-gate 	}
75920Sstevel@tonic-gate 	/*
75930Sstevel@tonic-gate 	 * If we're the last one out, let people know it's clear.
75940Sstevel@tonic-gate 	 */
75950Sstevel@tonic-gate 	if (rnip->rni_waiters == 0)
75960Sstevel@tonic-gate 		(void) pthread_cond_broadcast(&rnip->rni_cv);
75970Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
75980Sstevel@tonic-gate 	return (REP_PROTOCOL_DONE);
75990Sstevel@tonic-gate }
76000Sstevel@tonic-gate 
76010Sstevel@tonic-gate static void
rc_notify_info_reset(rc_notify_info_t * rnip)76020Sstevel@tonic-gate rc_notify_info_reset(rc_notify_info_t *rnip)
76030Sstevel@tonic-gate {
76040Sstevel@tonic-gate 	int i;
76050Sstevel@tonic-gate 
76060Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
76070Sstevel@tonic-gate 	if (rnip->rni_flags & RC_NOTIFY_ACTIVE)
76080Sstevel@tonic-gate 		rc_notify_info_remove_locked(rnip);
76090Sstevel@tonic-gate 	assert(!(rnip->rni_flags & (RC_NOTIFY_DRAIN | RC_NOTIFY_EMPTYING)));
76100Sstevel@tonic-gate 	rnip->rni_flags |= RC_NOTIFY_EMPTYING;
76110Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
76120Sstevel@tonic-gate 
76130Sstevel@tonic-gate 	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
76140Sstevel@tonic-gate 		if (rnip->rni_namelist[i] != NULL) {
76150Sstevel@tonic-gate 			free((void *)rnip->rni_namelist[i]);
76160Sstevel@tonic-gate 			rnip->rni_namelist[i] = NULL;
76170Sstevel@tonic-gate 		}
76180Sstevel@tonic-gate 		if (rnip->rni_typelist[i] != NULL) {
76190Sstevel@tonic-gate 			free((void *)rnip->rni_typelist[i]);
76200Sstevel@tonic-gate 			rnip->rni_typelist[i] = NULL;
76210Sstevel@tonic-gate 		}
76220Sstevel@tonic-gate 	}
76230Sstevel@tonic-gate 
76240Sstevel@tonic-gate 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
76250Sstevel@tonic-gate 	rnip->rni_flags &= ~RC_NOTIFY_EMPTYING;
76260Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
76270Sstevel@tonic-gate }
76280Sstevel@tonic-gate 
76290Sstevel@tonic-gate void
rc_notify_info_fini(rc_notify_info_t * rnip)76300Sstevel@tonic-gate rc_notify_info_fini(rc_notify_info_t *rnip)
76310Sstevel@tonic-gate {
76320Sstevel@tonic-gate 	rc_notify_info_reset(rnip);
76330Sstevel@tonic-gate 
76340Sstevel@tonic-gate 	uu_list_node_fini(rnip, &rnip->rni_list_node, rc_notify_info_pool);
76350Sstevel@tonic-gate 	uu_list_node_fini(&rnip->rni_notify, &rnip->rni_notify.rcn_list_node,
76360Sstevel@tonic-gate 	    rc_notify_pool);
76370Sstevel@tonic-gate }
7638