rc_node.c revision 5405:f7a026c6d133
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * rc_node.c - object management primitives
31 *
32 * This layer manages entities, their data structure, its locking, iterators,
33 * transactions, and change notification requests.  Entities (scopes,
34 * services, instances, snapshots, snaplevels, property groups, "composed"
35 * property groups (see composition below), and properties) are represented by
36 * rc_node_t's and are kept in the cache_hash hash table.  (Property values
37 * are kept in the rn_values member of the respective property -- not as
38 * separate objects.)  Iterators are represented by rc_node_iter_t's.
39 * Transactions are represented by rc_node_tx_t's and are only allocated as
40 * part of repcache_tx_t's in the client layer (client.c).  Change
41 * notification requests are represented by rc_notify_t structures and are
42 * described below.
43 *
44 * The entity tree is rooted at rc_scope, which rc_node_init() initializes to
45 * the "localhost" scope.  The tree is filled in from the database on-demand
46 * by rc_node_fill_children(), usually from rc_iter_create() since iterators
47 * are the only way to find the children of an entity.
48 *
49 * Each rc_node_t is protected by its rn_lock member.  Operations which can
50 * take too long, however, should serialize on an RC_NODE_WAITING_FLAGS bit in
51 * rn_flags with the rc_node_{hold,rele}_flag() functions.  And since pointers
52 * to rc_node_t's are allowed, rn_refs is a reference count maintained by
53 * rc_node_{hold,rele}().  See configd.h for locking order information.
54 *
55 * When a node (property group or snapshot) is updated, a new node takes the
56 * place of the old node in the global hash, and the old node is hung off of
57 * the rn_former list of the new node.  At the same time, all of its children
58 * have their rn_parent_ref pointer set, and any holds they have are reflected
59 * in the old node's rn_other_refs count.  This is automatically kept up
60 * to date, until the final reference to the subgraph is dropped, at which
61 * point the node is unrefed and destroyed, along with all of its children.
62 *
63 * Locking rules: To dereference an rc_node_t * (usually to lock it), you must
64 * have a hold (rc_node_hold()) on it or otherwise be sure that it hasn't been
65 * rc_node_destroy()ed (hold a lock on its parent or child, hold a flag,
66 * etc.).  Once you have locked an rc_node_t you must check its rn_flags for
67 * RC_NODE_DEAD before you can use it.  This is usually done with the
68 * rc_node_{wait,hold}_flag() functions (often via the rc_node_check_*()
69 * functions & RC_NODE_*() macros), which fail if the object has died.
70 *
71 * Because name service lookups may take a long time and, more importantly
72 * may trigger additional accesses to the repository, perm_granted() must be
73 * called without holding any locks.
74 *
75 * An ITER_START for a non-ENTITY_VALUE induces an rc_node_fill_children()
76 * call via rc_node_setup_iter() to populate the rn_children uu_list of the
77 * rc_node_t * in question and a call to uu_list_walk_start() on that list.  For
78 * ITER_READ, rc_iter_next() uses uu_list_walk_next() to find the next
79 * apropriate child.
80 *
81 * An ITER_START for an ENTITY_VALUE makes sure the node has its values
82 * filled, and sets up the iterator.  An ITER_READ_VALUE just copies out
83 * the proper values and updates the offset information.
84 *
85 * When a property group gets changed by a transaction, it sticks around as
86 * a child of its replacement property group, but is removed from the parent.
87 *
88 * To allow aliases, snapshots are implemented with a level of indirection.
89 * A snapshot rc_node_t has a snapid which refers to an rc_snapshot_t in
90 * snapshot.c which contains the authoritative snaplevel information.  The
91 * snapid is "assigned" by rc_attach_snapshot().
92 *
93 * We provide the client layer with rc_node_ptr_t's to reference objects.
94 * Objects referred to by them are automatically held & released by
95 * rc_node_assign() & rc_node_clear().  The RC_NODE_PTR_*() macros are used at
96 * client.c entry points to read the pointers.  They fetch the pointer to the
97 * object, return (from the function) if it is dead, and lock, hold, or hold
98 * a flag of the object.
99 */
100
101/*
102 * Permission checking is authorization-based: some operations may only
103 * proceed if the user has been assigned at least one of a set of
104 * authorization strings.  The set of enabling authorizations depends on the
105 * operation and the target object.  The set of authorizations assigned to
106 * a user is determined by reading /etc/security/policy.conf, querying the
107 * user_attr database, and possibly querying the prof_attr database, as per
108 * chkauthattr() in libsecdb.
109 *
110 * The fastest way to decide whether the two sets intersect is by entering the
111 * strings into a hash table and detecting collisions, which takes linear time
112 * in the total size of the sets.  Except for the authorization patterns which
113 * may be assigned to users, which without advanced pattern-matching
114 * algorithms will take O(n) in the number of enabling authorizations, per
115 * pattern.
116 *
117 * We can achieve some practical speed-ups by noting that if we enter all of
118 * the authorizations from one of the sets into the hash table we can merely
119 * check the elements of the second set for existence without adding them.
120 * This reduces memory requirements and hash table clutter.  The enabling set
121 * is well suited for this because it is internal to configd (for now, at
122 * least).  Combine this with short-circuiting and we can even minimize the
123 * number of queries to the security databases (user_attr & prof_attr).
124 *
125 * To force this usage onto clients we provide functions for adding
126 * authorizations to the enabling set of a permission context structure
127 * (perm_add_*()) and one to decide whether the the user associated with the
128 * current door call client possesses any of them (perm_granted()).
129 *
130 * At some point, a generic version of this should move to libsecdb.
131 *
132 * While entering the enabling strings into the hash table, we keep track
133 * of which is the most specific for use in generating auditing events.
134 * See the "Collecting the Authorization String" section of the "SMF Audit
135 * Events" block comment below.
136 */
137
138/*
139 * Composition is the combination of sets of properties.  The sets are ordered
140 * and properties in higher sets obscure properties of the same name in lower
141 * sets.  Here we present a composed view of an instance's properties as the
142 * union of its properties and its service's properties.  Similarly the
143 * properties of snaplevels are combined to form a composed view of the
144 * properties of a snapshot (which should match the composed view of the
145 * properties of the instance when the snapshot was taken).
146 *
147 * In terms of the client interface, the client may request that a property
148 * group iterator for an instance or snapshot be composed.  Property groups
149 * traversed by such an iterator may not have the target entity as a parent.
150 * Similarly, the properties traversed by a property iterator for those
151 * property groups may not have the property groups iterated as parents.
152 *
153 * Implementation requires that iterators for instances and snapshots be
154 * composition-savvy, and that we have a "composed property group" entity
155 * which represents the composition of a number of property groups.  Iteration
156 * over "composed property groups" yields properties which may have different
157 * parents, but for all other operations a composed property group behaves
158 * like the top-most property group it represents.
159 *
160 * The implementation is based on the rn_cchain[] array of rc_node_t pointers
161 * in rc_node_t.  For instances, the pointers point to the instance and its
162 * parent service.  For snapshots they point to the child snaplevels, and for
163 * composed property groups they point to property groups.  A composed
164 * iterator carries an index into rn_cchain[].  Thus most of the magic ends up
165 * int the rc_iter_*() code.
166 */
167/*
168 * SMF Audit Events:
169 * ================
170 *
171 * To maintain security, SMF generates audit events whenever
172 * privileged operations are attempted.  See the System Administration
173 * Guide:Security Services answerbook for a discussion of the Solaris
174 * audit system.
175 *
176 * The SMF audit event codes are defined in adt_event.h by symbols
177 * starting with ADT_smf_ and are described in audit_event.txt.  The
178 * audit record structures are defined in the SMF section of adt.xml.
179 * adt.xml is used to automatically generate adt_event.h which
180 * contains the definitions that we code to in this file.  For the
181 * most part the audit events map closely to actions that you would
182 * perform with svcadm or svccfg, but there are some special cases
183 * which we'll discuss later.
184 *
185 * The software associated with SMF audit events falls into three
186 * categories:
187 * 	- collecting information to be written to the audit
188 *	  records
189 *	- using the adt_* functions in
190 *	  usr/src/lib/libbsm/common/adt.c to generate the audit
191 *	  records.
192 * 	- handling special cases
193 *
194 * Collecting Information:
195 * ----------------------
196 *
197 * Most all of the audit events require the FMRI of the affected
198 * object and the authorization string that was used.  The one
199 * exception is ADT_smf_annotation which we'll talk about later.
200 *
201 * Collecting the FMRI:
202 *
203 * The rc_node structure has a member called rn_fmri which points to
204 * its FMRI.  This is initialized by a call to rc_node_build_fmri()
205 * when the node's parent is established.  The reason for doing it
206 * at this time is that a node's FMRI is basically the concatenation
207 * of the parent's FMRI and the node's name with the appropriate
208 * decoration.  rc_node_build_fmri() does this concatenation and
209 * decorating.  It is called from rc_node_link_child() and
210 * rc_node_relink_child() where a node is linked to its parent.
211 *
212 * rc_node_get_fmri_or_fragment() is called to retrieve a node's FMRI
213 * when it is needed.  It returns rn_fmri if it is set.  If the node
214 * is at the top level, however, rn_fmri won't be set because it was
215 * never linked to a parent.  In this case,
216 * rc_node_get_fmri_or_fragment() constructs an FMRI fragment based on
217 * its node type and its name, rn_name.
218 *
219 * Collecting the Authorization String:
220 *
221 * Naturally, the authorization string is captured during the
222 * authorization checking process.  Acceptable authorization strings
223 * are added to a permcheck_t hash table as noted in the section on
224 * permission checking above.  Once all entries have been added to the
225 * hash table, perm_granted() is called.  If the client is authorized,
226 * perm_granted() returns with pc_auth_string of the permcheck_t
227 * structure pointing to the authorization string.
228 *
229 * This works fine if the client is authorized, but what happens if
230 * the client is not authorized?  We need to report the required
231 * authorization string.  This is the authorization that would have
232 * been used if permission had been granted.  perm_granted() will
233 * find no match, so it needs to decide which string in the hash
234 * table to use as the required authorization string.  It needs to do
235 * this, because configd is still going to generate an event.  A
236 * design decision was made to use the most specific authorization
237 * in the hash table.  The pc_auth_type enum designates the
238 * specificity of an authorization string.  For example, an
239 * authorization string that is declared in an instance PG is more
240 * specific than one that is declared in a service PG.
241 *
242 * The pc_add() function keeps track of the most specific
243 * authorization in the hash table.  It does this using the
244 * pc_specific and pc_specific_type members of the permcheck
245 * structure.  pc_add() updates these members whenever a more
246 * specific authorization string is added to the hash table.  Thus, if
247 * an authorization match is not found, perm_granted() will return
248 * with pc_auth_string in the permcheck_t pointing to the string that
249 * is referenced by pc_specific.
250 *
251 * Generating the Audit Events:
252 * ===========================
253 *
254 * As the functions in this file process requests for clients of
255 * configd, they gather the information that is required for an audit
256 * event.  Eventually, the request processing gets to the point where
257 * the authorization is rejected or to the point where the requested
258 * action was attempted.  At these two points smf_audit_event() is
259 * called.
260 *
261 * smf_audit_event() takes 4 parameters:
262 * 	- the event ID which is one of the ADT_smf_* symbols from
263 *	  adt_event.h.
264 * 	- status to pass to adt_put_event()
265 * 	- return value to pass to adt_put_event()
266 * 	- the event data (see audit_event_data structure)
267 *
268 * All interactions with the auditing software require an audit
269 * session.  We use one audit session per configd client.  We keep
270 * track of the audit session in the repcache_client structure.
271 * smf_audit_event() calls get_audit_session() to get the session
272 * pointer.
273 *
274 * smf_audit_event() then calls adt_alloc_event() to allocate an
275 * adt_event_data union which is defined in adt_event.h, copies the
276 * data into the appropriate members of the union and calls
277 * adt_put_event() to generate the event.
278 *
279 * Special Cases:
280 * =============
281 *
282 * There are three major types of special cases:
283 *
284 * 	- gathering event information for each action in a
285 *	  transaction
286 * 	- Higher level events represented by special property
287 *	  group/property name combinations.  Many of these are
288 *	  restarter actions.
289 * 	- ADT_smf_annotation event
290 *
291 * Processing Transaction Actions:
292 * ------------------------------
293 *
294 * A transaction can contain multiple actions to modify, create or
295 * delete one or more properties.  We need to capture information so
296 * that we can generate an event for each property action.  The
297 * transaction information is stored in a tx_commmit_data_t, and
298 * object.c provides accessor functions to retrieve data from this
299 * structure.  rc_tx_commit() obtains a tx_commit_data_t by calling
300 * tx_commit_data_new() and passes this to object_tx_commit() to
301 * commit the transaction.  Then we call generate_property_events() to
302 * generate an audit event for each property action.
303 *
304 * Special Properties:
305 * ------------------
306 *
307 * There are combinations of property group/property name that are special.
308 * They are special because they have specific meaning to startd.  startd
309 * interprets them in a service-independent fashion.
310 * restarter_actions/refresh and general/enabled are two examples of these.
311 * A special event is generated for these properties in addition to the
312 * regular property event described in the previous section.  The special
313 * properties are declared as an array of audit_special_prop_item
314 * structures at special_props_list in rc_node.c.
315 *
316 * In the previous section, we mentioned the
317 * generate_property_event() function that generates an event for
318 * every property action.  Before generating the event,
319 * generate_property_event() calls special_property_event().
320 * special_property_event() checks to see if the action involves a
321 * special property.  If it does, it generates a special audit
322 * event.
323 *
324 * ADT_smf_annotation event:
325 * ------------------------
326 *
327 * This is a special event unlike any other.  It allows the svccfg
328 * program to store an annotation in the event log before a series
329 * of transactions is processed.  It is used with the import and
330 * apply svccfg commands.  svccfg uses the rep_protocol_annotation
331 * message to pass the operation (import or apply) and the file name
332 * to configd.  The set_annotation() function in client.c stores
333 * these away in the a repcache_client structure.  The address of
334 * this structure is saved in the thread_info structure.
335 *
336 * Before it generates any events, smf_audit_event() calls
337 * smf_annotation_event().  smf_annotation_event() calls
338 * client_annotation_needed() which is defined in client.c.  If an
339 * annotation is needed client_annotation_needed() returns the
340 * operation and filename strings that were saved from the
341 * rep_protocol_annotation message.  smf_annotation_event() then
342 * generates the ADT_smf_annotation event.
343 */
344
345#include <assert.h>
346#include <atomic.h>
347#include <bsm/adt_event.h>
348#include <errno.h>
349#include <libuutil.h>
350#include <libscf.h>
351#include <libscf_priv.h>
352#include <prof_attr.h>
353#include <pthread.h>
354#include <stdio.h>
355#include <stdlib.h>
356#include <strings.h>
357#include <sys/types.h>
358#include <syslog.h>
359#include <unistd.h>
360#include <user_attr.h>
361
362#include "configd.h"
363
364#define	AUTH_PREFIX		"solaris.smf."
365#define	AUTH_MANAGE		AUTH_PREFIX "manage"
366#define	AUTH_MODIFY		AUTH_PREFIX "modify"
367#define	AUTH_MODIFY_PREFIX	AUTH_MODIFY "."
368#define	AUTH_PG_ACTIONS		SCF_PG_RESTARTER_ACTIONS
369#define	AUTH_PG_ACTIONS_TYPE	SCF_PG_RESTARTER_ACTIONS_TYPE
370#define	AUTH_PG_GENERAL		SCF_PG_GENERAL
371#define	AUTH_PG_GENERAL_TYPE	SCF_PG_GENERAL_TYPE
372#define	AUTH_PG_GENERAL_OVR	SCF_PG_GENERAL_OVR
373#define	AUTH_PG_GENERAL_OVR_TYPE  SCF_PG_GENERAL_OVR_TYPE
374#define	AUTH_PROP_ACTION	"action_authorization"
375#define	AUTH_PROP_ENABLED	"enabled"
376#define	AUTH_PROP_MODIFY	"modify_authorization"
377#define	AUTH_PROP_VALUE		"value_authorization"
378#define	AUTH_PROP_READ		"read_authorization"
379/* libsecdb should take care of this. */
380#define	RBAC_AUTH_SEP		","
381
382#define	MAX_VALID_CHILDREN 3
383
384/*
385 * The ADT_smf_* symbols may not be defined on the build machine.  Because
386 * of this, we do not want to compile the _smf_aud_event() function when
387 * doing native builds.
388 */
389#ifdef	NATIVE_BUILD
390#define	smf_audit_event(i, s, r, d)
391#else
392#define	smf_audit_event(i, s, r, d)	_smf_audit_event(i, s, r, d)
393#endif	/* NATIVE_BUILD */
394
395typedef struct rc_type_info {
396	uint32_t	rt_type;		/* matches array index */
397	uint32_t	rt_num_ids;
398	uint32_t	rt_name_flags;
399	uint32_t	rt_valid_children[MAX_VALID_CHILDREN];
400} rc_type_info_t;
401
402#define	RT_NO_NAME	-1U
403
404static rc_type_info_t rc_types[] = {
405	{REP_PROTOCOL_ENTITY_NONE, 0, RT_NO_NAME},
406	{REP_PROTOCOL_ENTITY_SCOPE, 0, 0,
407	    {REP_PROTOCOL_ENTITY_SERVICE, REP_PROTOCOL_ENTITY_SCOPE}},
408	{REP_PROTOCOL_ENTITY_SERVICE, 0, UU_NAME_DOMAIN | UU_NAME_PATH,
409	    {REP_PROTOCOL_ENTITY_INSTANCE, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
410	{REP_PROTOCOL_ENTITY_INSTANCE, 1, UU_NAME_DOMAIN,
411	    {REP_PROTOCOL_ENTITY_SNAPSHOT, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
412	{REP_PROTOCOL_ENTITY_SNAPSHOT, 2, UU_NAME_DOMAIN,
413	    {REP_PROTOCOL_ENTITY_SNAPLEVEL, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
414	{REP_PROTOCOL_ENTITY_SNAPLEVEL, 4, RT_NO_NAME,
415	    {REP_PROTOCOL_ENTITY_PROPERTYGRP}},
416	{REP_PROTOCOL_ENTITY_PROPERTYGRP, 5, UU_NAME_DOMAIN,
417	    {REP_PROTOCOL_ENTITY_PROPERTY}},
418	{REP_PROTOCOL_ENTITY_CPROPERTYGRP, 0, UU_NAME_DOMAIN,
419	    {REP_PROTOCOL_ENTITY_PROPERTY}},
420	{REP_PROTOCOL_ENTITY_PROPERTY, 7, UU_NAME_DOMAIN},
421	{-1UL}
422};
423#define	NUM_TYPES	((sizeof (rc_types) / sizeof (*rc_types)))
424
425/* Element of a permcheck_t hash table. */
426struct pc_elt {
427	struct pc_elt	*pce_next;
428	char		pce_auth[1];
429};
430
431/*
432 * If an authorization fails, we must decide which of the elements in the
433 * permcheck hash table to use in the audit event.  That is to say of all
434 * the strings in the hash table, we must choose one and use it in the audit
435 * event.  It is desirable to use the most specific string in the audit
436 * event.
437 *
438 * The pc_auth_type specifies the types (sources) of authorization
439 * strings.  The enum is ordered in increasing specificity.
440 */
441typedef enum pc_auth_type {
442	PC_AUTH_NONE = 0,	/* no auth string available. */
443	PC_AUTH_SMF,		/* strings coded into SMF. */
444	PC_AUTH_SVC,		/* strings specified in PG of a service. */
445	PC_AUTH_INST		/* strings specified in PG of an instance. */
446} pc_auth_type_t;
447
448/* An authorization set hash table. */
449typedef struct {
450	struct pc_elt	**pc_buckets;
451	uint_t		pc_bnum;		/* number of buckets */
452	uint_t		pc_enum;		/* number of elements */
453	struct pc_elt	*pc_specific;		/* most specific element */
454	pc_auth_type_t	pc_specific_type;	/* type of pc_specific */
455	char		*pc_auth_string;	/* authorization string */
456						/* for audit events */
457} permcheck_t;
458
459/*
460 * Structure for holding audit event data.  Not all events use all members
461 * of the structure.
462 */
463typedef struct audit_event_data {
464	char		*ed_auth;	/* authorization string. */
465	char		*ed_fmri;	/* affected FMRI. */
466	char		*ed_snapname;	/* name of snapshot. */
467	char		*ed_old_fmri;	/* old fmri in attach case. */
468	char		*ed_old_name;	/* old snapshot in attach case. */
469	char		*ed_type;	/* prop. group or prop. type. */
470	char		*ed_prop_value;	/* property value. */
471} audit_event_data_t;
472
473/*
474 * Pointer to function to do special processing to get audit event ID.
475 * Audit event IDs are defined in /usr/include/bsm/adt_event.h.  Function
476 * returns 0 if ID successfully retrieved.  Otherwise it returns -1.
477 */
478typedef int (*spc_getid_fn_t)(tx_commit_data_t *, size_t, const char *,
479    au_event_t *);
480static int general_enable_id(tx_commit_data_t *, size_t, const char *,
481    au_event_t *);
482
483static uu_list_pool_t *rc_children_pool;
484static uu_list_pool_t *rc_pg_notify_pool;
485static uu_list_pool_t *rc_notify_pool;
486static uu_list_pool_t *rc_notify_info_pool;
487
488static rc_node_t *rc_scope;
489
490static pthread_mutex_t	rc_pg_notify_lock = PTHREAD_MUTEX_INITIALIZER;
491static pthread_cond_t	rc_pg_notify_cv = PTHREAD_COND_INITIALIZER;
492static uint_t		rc_notify_in_use;	/* blocks removals */
493
494static pthread_mutex_t	perm_lock = PTHREAD_MUTEX_INITIALIZER;
495
496/*
497 * Some combinations of property group/property name require a special
498 * audit event to be generated when there is a change.
499 * audit_special_prop_item_t is used to specify these special cases.  The
500 * special_props_list array defines a list of these special properties.
501 */
502typedef struct audit_special_prop_item {
503	const char	*api_pg_name;	/* property group name. */
504	const char	*api_prop_name;	/* property name. */
505	au_event_t	api_event_id;	/* event id or 0. */
506	spc_getid_fn_t	api_event_func; /* function to get event id. */
507} audit_special_prop_item_t;
508
509/*
510 * Native builds are done using the build machine's standard include
511 * files.  These files may not yet have the definitions for the ADT_smf_*
512 * symbols.  Thus, we do not compile this table when doing native builds.
513 */
514#ifndef	NATIVE_BUILD
515/*
516 * The following special_props_list array specifies property group/property
517 * name combinations that have specific meaning to startd.  A special event
518 * is generated for these combinations in addition to the regular property
519 * event.
520 *
521 * At run time this array gets sorted.  See the call to qsort(3C) in
522 * rc_node_init().  The array is sorted, so that bsearch(3C) can be used
523 * to do lookups.
524 */
525static audit_special_prop_item_t special_props_list[] = {
526	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_DEGRADED, ADT_smf_degrade,
527	    NULL},
528	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_DEGRADE_IMMEDIATE,
529	    ADT_smf_immediate_degrade, NULL},
530	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_OFF, ADT_smf_clear, NULL},
531	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON,
532	    ADT_smf_maintenance, NULL},
533	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_IMMEDIATE,
534	    ADT_smf_immediate_maintenance, NULL},
535	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_IMMTEMP,
536	    ADT_smf_immtmp_maintenance, NULL},
537	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_TEMPORARY,
538	    ADT_smf_tmp_maintenance, NULL},
539	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_REFRESH, ADT_smf_refresh, NULL},
540	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_RESTART, ADT_smf_restart, NULL},
541	{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_RESTORE, ADT_smf_clear, NULL},
542	{SCF_PG_OPTIONS, SCF_PROPERTY_MILESTONE, ADT_smf_milestone, NULL},
543	{SCF_PG_OPTIONS_OVR, SCF_PROPERTY_MILESTONE, ADT_smf_milestone, NULL},
544	{SCF_PG_GENERAL, SCF_PROPERTY_ENABLED, 0, general_enable_id},
545	{SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED, 0, general_enable_id}
546};
547#define	SPECIAL_PROP_COUNT	(sizeof (special_props_list) /\
548	sizeof (audit_special_prop_item_t))
549#endif	/* NATIVE_BUILD */
550
551static void rc_node_unrefed(rc_node_t *np);
552
553/*
554 * We support an arbitrary number of clients interested in events for certain
555 * types of changes.  Each client is represented by an rc_notify_info_t, and
556 * all clients are chained onto the rc_notify_info_list.
557 *
558 * The rc_notify_list is the global notification list.  Each entry is of
559 * type rc_notify_t, which is embedded in one of three other structures:
560 *
561 *	rc_node_t		property group update notification
562 *	rc_notify_delete_t	object deletion notification
563 *	rc_notify_info_t	notification clients
564 *
565 * Which type of object is determined by which pointer in the rc_notify_t is
566 * non-NULL.
567 *
568 * New notifications and clients are added to the end of the list.
569 * Notifications no-one is interested in are never added to the list.
570 *
571 * Clients use their position in the list to track which notifications they
572 * have not yet reported.  As they process notifications, they move forward
573 * in the list past them.  There is always a client at the beginning of the
574 * list -- as he moves past notifications, he removes them from the list and
575 * cleans them up.
576 *
577 * The rc_pg_notify_lock protects all notification state.  The rc_pg_notify_cv
578 * is used for global signalling, and each client has a cv which he waits for
579 * events of interest on.
580 */
581static uu_list_t	*rc_notify_info_list;
582static uu_list_t	*rc_notify_list;
583
584#define	HASH_SIZE	512
585#define	HASH_MASK	(HASH_SIZE - 1)
586
587#pragma align 64(cache_hash)
588static cache_bucket_t cache_hash[HASH_SIZE];
589
590#define	CACHE_BUCKET(h)		(&cache_hash[(h) & HASH_MASK])
591
592static uint32_t
593rc_node_hash(rc_node_lookup_t *lp)
594{
595	uint32_t type = lp->rl_type;
596	uint32_t backend = lp->rl_backend;
597	uint32_t mainid = lp->rl_main_id;
598	uint32_t *ids = lp->rl_ids;
599
600	rc_type_info_t *tp = &rc_types[type];
601	uint32_t num_ids;
602	uint32_t left;
603	uint32_t hash;
604
605	assert(backend == BACKEND_TYPE_NORMAL ||
606	    backend == BACKEND_TYPE_NONPERSIST);
607
608	assert(type > 0 && type < NUM_TYPES);
609	num_ids = tp->rt_num_ids;
610
611	left = MAX_IDS - num_ids;
612	assert(num_ids <= MAX_IDS);
613
614	hash = type * 7 + mainid * 5 + backend;
615
616	while (num_ids-- > 0)
617		hash = hash * 11 + *ids++ * 7;
618
619	/*
620	 * the rest should be zeroed
621	 */
622	while (left-- > 0)
623		assert(*ids++ == 0);
624
625	return (hash);
626}
627
628static int
629rc_node_match(rc_node_t *np, rc_node_lookup_t *l)
630{
631	rc_node_lookup_t *r = &np->rn_id;
632	rc_type_info_t *tp;
633	uint32_t type;
634	uint32_t num_ids;
635
636	if (r->rl_main_id != l->rl_main_id)
637		return (0);
638
639	type = r->rl_type;
640	if (type != l->rl_type)
641		return (0);
642
643	assert(type > 0 && type < NUM_TYPES);
644
645	tp = &rc_types[r->rl_type];
646	num_ids = tp->rt_num_ids;
647
648	assert(num_ids <= MAX_IDS);
649	while (num_ids-- > 0)
650		if (r->rl_ids[num_ids] != l->rl_ids[num_ids])
651			return (0);
652
653	return (1);
654}
655
656/*
657 * the "other" references on a node are maintained in an atomically
658 * updated refcount, rn_other_refs.  This can be bumped from arbitrary
659 * context, and tracks references to a possibly out-of-date node's children.
660 *
661 * To prevent the node from disappearing between the final drop of
662 * rn_other_refs and the unref handling, rn_other_refs_held is bumped on
663 * 0->1 transitions and decremented (with the node lock held) on 1->0
664 * transitions.
665 */
666static void
667rc_node_hold_other(rc_node_t *np)
668{
669	if (atomic_add_32_nv(&np->rn_other_refs, 1) == 1) {
670		atomic_add_32(&np->rn_other_refs_held, 1);
671		assert(np->rn_other_refs_held > 0);
672	}
673	assert(np->rn_other_refs > 0);
674}
675
676/*
677 * No node locks may be held
678 */
679static void
680rc_node_rele_other(rc_node_t *np)
681{
682	assert(np->rn_other_refs > 0);
683	if (atomic_add_32_nv(&np->rn_other_refs, -1) == 0) {
684		(void) pthread_mutex_lock(&np->rn_lock);
685		assert(np->rn_other_refs_held > 0);
686		if (atomic_add_32_nv(&np->rn_other_refs_held, -1) == 0 &&
687		    np->rn_refs == 0 && (np->rn_flags & RC_NODE_OLD))
688			rc_node_unrefed(np);
689		else
690			(void) pthread_mutex_unlock(&np->rn_lock);
691	}
692}
693
694static void
695rc_node_hold_locked(rc_node_t *np)
696{
697	assert(MUTEX_HELD(&np->rn_lock));
698
699	if (np->rn_refs == 0 && (np->rn_flags & RC_NODE_PARENT_REF))
700		rc_node_hold_other(np->rn_parent_ref);
701	np->rn_refs++;
702	assert(np->rn_refs > 0);
703}
704
705static void
706rc_node_hold(rc_node_t *np)
707{
708	(void) pthread_mutex_lock(&np->rn_lock);
709	rc_node_hold_locked(np);
710	(void) pthread_mutex_unlock(&np->rn_lock);
711}
712
713static void
714rc_node_rele_locked(rc_node_t *np)
715{
716	int unref = 0;
717	rc_node_t *par_ref = NULL;
718
719	assert(MUTEX_HELD(&np->rn_lock));
720	assert(np->rn_refs > 0);
721
722	if (--np->rn_refs == 0) {
723		if (np->rn_flags & RC_NODE_PARENT_REF)
724			par_ref = np->rn_parent_ref;
725
726		/*
727		 * Composed property groups are only as good as their
728		 * references.
729		 */
730		if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP)
731			np->rn_flags |= RC_NODE_DEAD;
732
733		if ((np->rn_flags & (RC_NODE_DEAD|RC_NODE_OLD)) &&
734		    np->rn_other_refs == 0 && np->rn_other_refs_held == 0)
735			unref = 1;
736	}
737
738	if (unref)
739		rc_node_unrefed(np);
740	else
741		(void) pthread_mutex_unlock(&np->rn_lock);
742
743	if (par_ref != NULL)
744		rc_node_rele_other(par_ref);
745}
746
747void
748rc_node_rele(rc_node_t *np)
749{
750	(void) pthread_mutex_lock(&np->rn_lock);
751	rc_node_rele_locked(np);
752}
753
754static cache_bucket_t *
755cache_hold(uint32_t h)
756{
757	cache_bucket_t *bp = CACHE_BUCKET(h);
758	(void) pthread_mutex_lock(&bp->cb_lock);
759	return (bp);
760}
761
762static void
763cache_release(cache_bucket_t *bp)
764{
765	(void) pthread_mutex_unlock(&bp->cb_lock);
766}
767
768static rc_node_t *
769cache_lookup_unlocked(cache_bucket_t *bp, rc_node_lookup_t *lp)
770{
771	uint32_t h = rc_node_hash(lp);
772	rc_node_t *np;
773
774	assert(MUTEX_HELD(&bp->cb_lock));
775	assert(bp == CACHE_BUCKET(h));
776
777	for (np = bp->cb_head; np != NULL; np = np->rn_hash_next) {
778		if (np->rn_hash == h && rc_node_match(np, lp)) {
779			rc_node_hold(np);
780			return (np);
781		}
782	}
783
784	return (NULL);
785}
786
787static rc_node_t *
788cache_lookup(rc_node_lookup_t *lp)
789{
790	uint32_t h;
791	cache_bucket_t *bp;
792	rc_node_t *np;
793
794	h = rc_node_hash(lp);
795	bp = cache_hold(h);
796
797	np = cache_lookup_unlocked(bp, lp);
798
799	cache_release(bp);
800
801	return (np);
802}
803
804static void
805cache_insert_unlocked(cache_bucket_t *bp, rc_node_t *np)
806{
807	assert(MUTEX_HELD(&bp->cb_lock));
808	assert(np->rn_hash == rc_node_hash(&np->rn_id));
809	assert(bp == CACHE_BUCKET(np->rn_hash));
810
811	assert(np->rn_hash_next == NULL);
812
813	np->rn_hash_next = bp->cb_head;
814	bp->cb_head = np;
815}
816
817static void
818cache_remove_unlocked(cache_bucket_t *bp, rc_node_t *np)
819{
820	rc_node_t **npp;
821
822	assert(MUTEX_HELD(&bp->cb_lock));
823	assert(np->rn_hash == rc_node_hash(&np->rn_id));
824	assert(bp == CACHE_BUCKET(np->rn_hash));
825
826	for (npp = &bp->cb_head; *npp != NULL; npp = &(*npp)->rn_hash_next)
827		if (*npp == np)
828			break;
829
830	assert(*npp == np);
831	*npp = np->rn_hash_next;
832	np->rn_hash_next = NULL;
833}
834
835/*
836 * verify that the 'parent' type can have a child typed 'child'
837 * Fails with
838 *   _INVALID_TYPE - argument is invalid
839 *   _TYPE_MISMATCH - parent type cannot have children of type child
840 */
841static int
842rc_check_parent_child(uint32_t parent, uint32_t child)
843{
844	int idx;
845	uint32_t type;
846
847	if (parent == 0 || parent >= NUM_TYPES ||
848	    child == 0 || child >= NUM_TYPES)
849		return (REP_PROTOCOL_FAIL_INVALID_TYPE); /* invalid types */
850
851	for (idx = 0; idx < MAX_VALID_CHILDREN; idx++) {
852		type = rc_types[parent].rt_valid_children[idx];
853		if (type == child)
854			return (REP_PROTOCOL_SUCCESS);
855	}
856
857	return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
858}
859
860/*
861 * Fails with
862 *   _INVALID_TYPE - type is invalid
863 *   _BAD_REQUEST - name is an invalid name for a node of type type
864 */
865int
866rc_check_type_name(uint32_t type, const char *name)
867{
868	if (type == 0 || type >= NUM_TYPES)
869		return (REP_PROTOCOL_FAIL_INVALID_TYPE); /* invalid types */
870
871	if (uu_check_name(name, rc_types[type].rt_name_flags) == -1)
872		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
873
874	return (REP_PROTOCOL_SUCCESS);
875}
876
877static int
878rc_check_pgtype_name(const char *name)
879{
880	if (uu_check_name(name, UU_NAME_DOMAIN) == -1)
881		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
882
883	return (REP_PROTOCOL_SUCCESS);
884}
885
886/*
887 * rc_node_free_fmri should be called whenever a node loses its parent.
888 * The reason is that the node's fmri string is built up by concatenating
889 * its name to the parent's fmri.  Thus, when the node no longer has a
890 * parent, its fmri is no longer valid.
891 */
892static void
893rc_node_free_fmri(rc_node_t *np)
894{
895	if (np->rn_fmri != NULL) {
896		free((void *)np->rn_fmri);
897		np->rn_fmri = NULL;
898	}
899}
900
901/*
902 * Concatenate the appropriate separator and the FMRI element to the base
903 * FMRI string at fmri.
904 *
905 * Fails with
906 *	_TRUNCATED	Not enough room in buffer at fmri.
907 */
908static int
909rc_concat_fmri_element(
910	char *fmri,			/* base fmri */
911	size_t bufsize,			/* size of buf at fmri */
912	size_t *sz_out,			/* receives result size. */
913	const char *element,		/* element name to concat */
914	rep_protocol_entity_t type)	/* type of element */
915{
916	size_t actual;
917	const char *name = element;
918	int rc;
919	const char *separator;
920
921	if (bufsize > 0)
922		*sz_out = strlen(fmri);
923	else
924		*sz_out = 0;
925
926	switch (type) {
927	case REP_PROTOCOL_ENTITY_SCOPE:
928		if (strcmp(element, SCF_FMRI_LOCAL_SCOPE) == 0) {
929			/*
930			 * No need to display scope information if we are
931			 * in the local scope.
932			 */
933			separator = SCF_FMRI_SVC_PREFIX;
934			name = NULL;
935		} else {
936			/*
937			 * Need to display scope information, because it is
938			 * not the local scope.
939			 */
940			separator = SCF_FMRI_SVC_PREFIX SCF_FMRI_SCOPE_PREFIX;
941		}
942		break;
943	case REP_PROTOCOL_ENTITY_SERVICE:
944		separator = SCF_FMRI_SERVICE_PREFIX;
945		break;
946	case REP_PROTOCOL_ENTITY_INSTANCE:
947		separator = SCF_FMRI_INSTANCE_PREFIX;
948		break;
949	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
950	case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
951		separator = SCF_FMRI_PROPERTYGRP_PREFIX;
952		break;
953	case REP_PROTOCOL_ENTITY_PROPERTY:
954		separator = SCF_FMRI_PROPERTY_PREFIX;
955		break;
956	case REP_PROTOCOL_ENTITY_VALUE:
957		/*
958		 * A value does not have a separate FMRI from its property,
959		 * so there is nothing to concat.
960		 */
961		return (REP_PROTOCOL_SUCCESS);
962	case REP_PROTOCOL_ENTITY_SNAPSHOT:
963	case REP_PROTOCOL_ENTITY_SNAPLEVEL:
964		/* Snapshots do not have FMRIs, so there is nothing to do. */
965		return (REP_PROTOCOL_SUCCESS);
966	default:
967		(void) fprintf(stderr, "%s:%d: Unknown protocol type %d.\n",
968		    __FILE__, __LINE__, type);
969		abort();	/* Missing a case in switch if we get here. */
970	}
971
972	/* Concatenate separator and element to the fmri buffer. */
973
974	actual = strlcat(fmri, separator, bufsize);
975	if (name != NULL) {
976		if (actual < bufsize) {
977			actual = strlcat(fmri, name, bufsize);
978		} else {
979			actual += strlen(name);
980		}
981	}
982	if (actual < bufsize) {
983		rc = REP_PROTOCOL_SUCCESS;
984	} else {
985		rc = REP_PROTOCOL_FAIL_TRUNCATED;
986	}
987	*sz_out = actual;
988	return (rc);
989}
990
991/*
992 * Get the FMRI for the node at np.  The fmri will be placed in buf.  On
993 * success sz_out will be set to the size of the fmri in buf.  If
994 * REP_PROTOCOL_FAIL_TRUNCATED is returned, sz_out will be set to the size
995 * of the buffer that would be required to avoid truncation.
996 *
997 * Fails with
998 *	_TRUNCATED	not enough room in buf for the FMRI.
999 */
1000static int
1001rc_node_get_fmri_or_fragment(rc_node_t *np, char *buf, size_t bufsize,
1002    size_t *sz_out)
1003{
1004	size_t fmri_len = 0;
1005	int r;
1006
1007	if (bufsize > 0)
1008		*buf = 0;
1009	*sz_out = 0;
1010
1011	if (np->rn_fmri == NULL) {
1012		/*
1013		 * A NULL rn_fmri implies that this is a top level scope.
1014		 * Child nodes will always have an rn_fmri established
1015		 * because both rc_node_link_child() and
1016		 * rc_node_relink_child() call rc_node_build_fmri().  In
1017		 * this case, we'll just return our name preceded by the
1018		 * appropriate FMRI decorations.
1019		 */
1020		assert(np->rn_parent == NULL);
1021		r = rc_concat_fmri_element(buf, bufsize, &fmri_len, np->rn_name,
1022		    np->rn_id.rl_type);
1023		if (r != REP_PROTOCOL_SUCCESS)
1024			return (r);
1025	} else {
1026		/* We have an fmri, so return it. */
1027		fmri_len = strlcpy(buf, np->rn_fmri, bufsize);
1028	}
1029
1030	*sz_out = fmri_len;
1031
1032	if (fmri_len >= bufsize)
1033		return (REP_PROTOCOL_FAIL_TRUNCATED);
1034
1035	return (REP_PROTOCOL_SUCCESS);
1036}
1037
1038/*
1039 * Build an FMRI string for this node and save it in rn_fmri.
1040 *
1041 * The basic strategy here is to get the fmri of our parent and then
1042 * concatenate the appropriate separator followed by our name.  If our name
1043 * is null, the resulting fmri will just be a copy of the parent fmri.
1044 * rc_node_build_fmri() should be called with the RC_NODE_USING_PARENT flag
1045 * set.  Also the rn_lock for this node should be held.
1046 *
1047 * Fails with
1048 *	_NO_RESOURCES	Could not allocate memory.
1049 */
1050static int
1051rc_node_build_fmri(rc_node_t *np)
1052{
1053	size_t actual;
1054	char fmri[REP_PROTOCOL_FMRI_LEN];
1055	int rc;
1056	size_t	sz = REP_PROTOCOL_FMRI_LEN;
1057
1058	assert(MUTEX_HELD(&np->rn_lock));
1059	assert(np->rn_flags & RC_NODE_USING_PARENT);
1060
1061	rc_node_free_fmri(np);
1062
1063	rc = rc_node_get_fmri_or_fragment(np->rn_parent, fmri, sz, &actual);
1064	assert(rc == REP_PROTOCOL_SUCCESS);
1065
1066	if (np->rn_name != NULL) {
1067		rc = rc_concat_fmri_element(fmri, sz, &actual, np->rn_name,
1068		    np->rn_id.rl_type);
1069		assert(rc == REP_PROTOCOL_SUCCESS);
1070		np->rn_fmri = strdup(fmri);
1071	} else {
1072		np->rn_fmri = strdup(fmri);
1073	}
1074	if (np->rn_fmri == NULL) {
1075		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1076	} else {
1077		rc = REP_PROTOCOL_SUCCESS;
1078	}
1079
1080	return (rc);
1081}
1082
1083/*
1084 * Get the FMRI of the node at np placing the result in fmri.  Then
1085 * concatenate the additional element to fmri.  The type variable indicates
1086 * the type of element, so that the appropriate separator can be
1087 * generated.  size is the number of bytes in the buffer at fmri, and
1088 * sz_out receives the size of the generated string.  If the result is
1089 * truncated, sz_out will receive the size of the buffer that would be
1090 * required to avoid truncation.
1091 *
1092 * Fails with
1093 *	_TRUNCATED	Not enough room in buffer at fmri.
1094 */
1095static int
1096rc_get_fmri_and_concat(rc_node_t *np, char *fmri, size_t size, size_t *sz_out,
1097    const char *element, rep_protocol_entity_t type)
1098{
1099	int rc;
1100
1101	if ((rc = rc_node_get_fmri_or_fragment(np, fmri, size, sz_out)) !=
1102	    REP_PROTOCOL_SUCCESS) {
1103		return (rc);
1104	}
1105	if ((rc = rc_concat_fmri_element(fmri, size, sz_out, element, type)) !=
1106	    REP_PROTOCOL_SUCCESS) {
1107		return (rc);
1108	}
1109
1110	return (REP_PROTOCOL_SUCCESS);
1111}
1112
1113static int
1114rc_notify_info_interested(rc_notify_info_t *rnip, rc_notify_t *np)
1115{
1116	rc_node_t *nnp = np->rcn_node;
1117	int i;
1118
1119	assert(MUTEX_HELD(&rc_pg_notify_lock));
1120
1121	if (np->rcn_delete != NULL) {
1122		assert(np->rcn_info == NULL && np->rcn_node == NULL);
1123		return (1);		/* everyone likes deletes */
1124	}
1125	if (np->rcn_node == NULL) {
1126		assert(np->rcn_info != NULL || np->rcn_delete != NULL);
1127		return (0);
1128	}
1129	assert(np->rcn_info == NULL);
1130
1131	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
1132		if (rnip->rni_namelist[i] != NULL) {
1133			if (strcmp(nnp->rn_name, rnip->rni_namelist[i]) == 0)
1134				return (1);
1135		}
1136		if (rnip->rni_typelist[i] != NULL) {
1137			if (strcmp(nnp->rn_type, rnip->rni_typelist[i]) == 0)
1138				return (1);
1139		}
1140	}
1141	return (0);
1142}
1143
1144static void
1145rc_notify_insert_node(rc_node_t *nnp)
1146{
1147	rc_notify_t *np = &nnp->rn_notify;
1148	rc_notify_info_t *nip;
1149	int found = 0;
1150
1151	assert(np->rcn_info == NULL);
1152
1153	if (nnp->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
1154		return;
1155
1156	(void) pthread_mutex_lock(&rc_pg_notify_lock);
1157	np->rcn_node = nnp;
1158	for (nip = uu_list_first(rc_notify_info_list); nip != NULL;
1159	    nip = uu_list_next(rc_notify_info_list, nip)) {
1160		if (rc_notify_info_interested(nip, np)) {
1161			(void) pthread_cond_broadcast(&nip->rni_cv);
1162			found++;
1163		}
1164	}
1165	if (found)
1166		(void) uu_list_insert_before(rc_notify_list, NULL, np);
1167	else
1168		np->rcn_node = NULL;
1169
1170	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
1171}
1172
1173static void
1174rc_notify_deletion(rc_notify_delete_t *ndp, const char *service,
1175    const char *instance, const char *pg)
1176{
1177	rc_notify_info_t *nip;
1178
1179	uu_list_node_init(&ndp->rnd_notify, &ndp->rnd_notify.rcn_list_node,
1180	    rc_notify_pool);
1181	ndp->rnd_notify.rcn_delete = ndp;
1182
1183	(void) snprintf(ndp->rnd_fmri, sizeof (ndp->rnd_fmri),
1184	    "svc:/%s%s%s%s%s", service,
1185	    (instance != NULL)? ":" : "", (instance != NULL)? instance : "",
1186	    (pg != NULL)? "/:properties/" : "", (pg != NULL)? pg : "");
1187
1188	/*
1189	 * add to notification list, notify watchers
1190	 */
1191	(void) pthread_mutex_lock(&rc_pg_notify_lock);
1192	for (nip = uu_list_first(rc_notify_info_list); nip != NULL;
1193	    nip = uu_list_next(rc_notify_info_list, nip))
1194		(void) pthread_cond_broadcast(&nip->rni_cv);
1195	(void) uu_list_insert_before(rc_notify_list, NULL, ndp);
1196	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
1197}
1198
1199static void
1200rc_notify_remove_node(rc_node_t *nnp)
1201{
1202	rc_notify_t *np = &nnp->rn_notify;
1203
1204	assert(np->rcn_info == NULL);
1205	assert(!MUTEX_HELD(&nnp->rn_lock));
1206
1207	(void) pthread_mutex_lock(&rc_pg_notify_lock);
1208	while (np->rcn_node != NULL) {
1209		if (rc_notify_in_use) {
1210			(void) pthread_cond_wait(&rc_pg_notify_cv,
1211			    &rc_pg_notify_lock);
1212			continue;
1213		}
1214		(void) uu_list_remove(rc_notify_list, np);
1215		np->rcn_node = NULL;
1216		break;
1217	}
1218	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
1219}
1220
1221static void
1222rc_notify_remove_locked(rc_notify_t *np)
1223{
1224	assert(MUTEX_HELD(&rc_pg_notify_lock));
1225	assert(rc_notify_in_use == 0);
1226
1227	(void) uu_list_remove(rc_notify_list, np);
1228	if (np->rcn_node) {
1229		np->rcn_node = NULL;
1230	} else if (np->rcn_delete) {
1231		uu_free(np->rcn_delete);
1232	} else {
1233		assert(0);	/* CAN'T HAPPEN */
1234	}
1235}
1236
1237/*
1238 * Permission checking functions.  See comment atop this file.
1239 */
1240#ifndef NATIVE_BUILD
1241static permcheck_t *
1242pc_create()
1243{
1244	permcheck_t *p;
1245
1246	p = uu_zalloc(sizeof (*p));
1247	if (p == NULL)
1248		return (NULL);
1249	p->pc_bnum = 8;			/* Normal case will only have 2 elts. */
1250	p->pc_buckets = uu_zalloc(sizeof (*p->pc_buckets) * p->pc_bnum);
1251	if (p->pc_buckets == NULL) {
1252		uu_free(p);
1253		return (NULL);
1254	}
1255
1256	p->pc_enum = 0;
1257	return (p);
1258}
1259
1260static void
1261pc_free(permcheck_t *pcp)
1262{
1263	uint_t i;
1264	struct pc_elt *ep, *next;
1265
1266	for (i = 0; i < pcp->pc_bnum; ++i) {
1267		for (ep = pcp->pc_buckets[i]; ep != NULL; ep = next) {
1268			next = ep->pce_next;
1269			free(ep);
1270		}
1271	}
1272
1273	free(pcp->pc_buckets);
1274	free(pcp);
1275}
1276
1277static uint32_t
1278pc_hash(const char *auth)
1279{
1280	uint32_t h = 0, g;
1281	const char *p;
1282
1283	/*
1284	 * Generic hash function from uts/common/os/modhash.c.
1285	 */
1286	for (p = auth; *p != '\0'; ++p) {
1287		h = (h << 4) + *p;
1288		g = (h & 0xf0000000);
1289		if (g != 0) {
1290			h ^= (g >> 24);
1291			h ^= g;
1292		}
1293	}
1294
1295	return (h);
1296}
1297
1298static int
1299pc_exists(permcheck_t *pcp, const char *auth)
1300{
1301	uint32_t h;
1302	struct pc_elt *ep;
1303
1304	h = pc_hash(auth);
1305	for (ep = pcp->pc_buckets[h & (pcp->pc_bnum - 1)];
1306	    ep != NULL;
1307	    ep = ep->pce_next) {
1308		if (strcmp(auth, ep->pce_auth) == 0) {
1309			pcp->pc_auth_string = ep->pce_auth;
1310			return (1);
1311		}
1312	}
1313
1314	return (0);
1315}
1316
1317static int
1318pc_match(permcheck_t *pcp, const char *pattern)
1319{
1320	uint_t i;
1321	struct pc_elt *ep;
1322
1323	for (i = 0; i < pcp->pc_bnum; ++i) {
1324		for (ep = pcp->pc_buckets[i]; ep != NULL; ep = ep->pce_next) {
1325			if (_auth_match(pattern, ep->pce_auth)) {
1326				pcp->pc_auth_string = ep->pce_auth;
1327				return (1);
1328			}
1329		}
1330	}
1331
1332	return (0);
1333}
1334
1335static int
1336pc_grow(permcheck_t *pcp)
1337{
1338	uint_t new_bnum, i, j;
1339	struct pc_elt **new_buckets;
1340	struct pc_elt *ep, *next;
1341
1342	new_bnum = pcp->pc_bnum * 2;
1343	if (new_bnum < pcp->pc_bnum)
1344		/* Homey don't play that. */
1345		return (-1);
1346
1347	new_buckets = uu_zalloc(sizeof (*new_buckets) * new_bnum);
1348	if (new_buckets == NULL)
1349		return (-1);
1350
1351	for (i = 0; i < pcp->pc_bnum; ++i) {
1352		for (ep = pcp->pc_buckets[i]; ep != NULL; ep = next) {
1353			next = ep->pce_next;
1354			j = pc_hash(ep->pce_auth) & (new_bnum - 1);
1355			ep->pce_next = new_buckets[j];
1356			new_buckets[j] = ep;
1357		}
1358	}
1359
1360	uu_free(pcp->pc_buckets);
1361	pcp->pc_buckets = new_buckets;
1362	pcp->pc_bnum = new_bnum;
1363
1364	return (0);
1365}
1366
1367static int
1368pc_add(permcheck_t *pcp, const char *auth, pc_auth_type_t auth_type)
1369{
1370	struct pc_elt *ep;
1371	uint_t i;
1372
1373	ep = uu_zalloc(offsetof(struct pc_elt, pce_auth) + strlen(auth) + 1);
1374	if (ep == NULL)
1375		return (-1);
1376
1377	/* Grow if pc_enum / pc_bnum > 3/4. */
1378	if (pcp->pc_enum * 4 > 3 * pcp->pc_bnum)
1379		/* Failure is not a stopper; we'll try again next time. */
1380		(void) pc_grow(pcp);
1381
1382	(void) strcpy(ep->pce_auth, auth);
1383
1384	i = pc_hash(auth) & (pcp->pc_bnum - 1);
1385	ep->pce_next = pcp->pc_buckets[i];
1386	pcp->pc_buckets[i] = ep;
1387
1388	if (auth_type > pcp->pc_specific_type) {
1389		pcp->pc_specific_type = auth_type;
1390		pcp->pc_specific = ep;
1391	}
1392
1393	++pcp->pc_enum;
1394
1395	return (0);
1396}
1397
1398/*
1399 * For the type of a property group, return the authorization which may be
1400 * used to modify it.
1401 */
1402static const char *
1403perm_auth_for_pgtype(const char *pgtype)
1404{
1405	if (strcmp(pgtype, SCF_GROUP_METHOD) == 0)
1406		return (AUTH_MODIFY_PREFIX "method");
1407	else if (strcmp(pgtype, SCF_GROUP_DEPENDENCY) == 0)
1408		return (AUTH_MODIFY_PREFIX "dependency");
1409	else if (strcmp(pgtype, SCF_GROUP_APPLICATION) == 0)
1410		return (AUTH_MODIFY_PREFIX "application");
1411	else if (strcmp(pgtype, SCF_GROUP_FRAMEWORK) == 0)
1412		return (AUTH_MODIFY_PREFIX "framework");
1413	else
1414		return (NULL);
1415}
1416
1417/*
1418 * Fails with
1419 *   _NO_RESOURCES - out of memory
1420 */
1421static int
1422perm_add_enabling_type(permcheck_t *pcp, const char *auth,
1423    pc_auth_type_t auth_type)
1424{
1425	return (pc_add(pcp, auth, auth_type) == 0 ? REP_PROTOCOL_SUCCESS :
1426	    REP_PROTOCOL_FAIL_NO_RESOURCES);
1427}
1428
1429/*
1430 * Fails with
1431 *   _NO_RESOURCES - out of memory
1432 */
1433static int
1434perm_add_enabling(permcheck_t *pcp, const char *auth)
1435{
1436	return (perm_add_enabling_type(pcp, auth, PC_AUTH_SMF));
1437}
1438
1439/* Note that perm_add_enabling_values() is defined below. */
1440
1441/*
1442 * perm_granted() returns 1 if the current door caller has one of the enabling
1443 * authorizations in pcp, 0 if it doesn't, and -1 if an error (usually lack of
1444 * memory) occurs.  check_auth_list() checks an RBAC_AUTH_SEP-separated list
1445 * of authorizations for existence in pcp, and check_prof_list() checks the
1446 * authorizations granted to an RBAC_AUTH_SEP-separated list of profiles.
1447 */
1448static int
1449check_auth_list(permcheck_t *pcp, char *authlist)
1450{
1451	char *auth, *lasts;
1452	int ret;
1453
1454	for (auth = (char *)strtok_r(authlist, RBAC_AUTH_SEP, &lasts);
1455	    auth != NULL;
1456	    auth = (char *)strtok_r(NULL, RBAC_AUTH_SEP, &lasts)) {
1457		if (strchr(auth, KV_WILDCHAR) == NULL)
1458			ret = pc_exists(pcp, auth);
1459		else
1460			ret = pc_match(pcp, auth);
1461
1462		if (ret)
1463			return (ret);
1464	}
1465
1466	/*
1467	 * If we failed, choose the most specific auth string for use in
1468	 * the audit event.
1469	 */
1470	assert(pcp->pc_specific != NULL);
1471	pcp->pc_auth_string = pcp->pc_specific->pce_auth;
1472
1473	return (0);
1474}
1475
1476static int
1477check_prof_list(permcheck_t *pcp, char *proflist)
1478{
1479	char *prof, *lasts, *authlist, *subproflist;
1480	profattr_t *pap;
1481	int ret = 0;
1482
1483	for (prof = strtok_r(proflist, RBAC_AUTH_SEP, &lasts);
1484	    prof != NULL;
1485	    prof = strtok_r(NULL, RBAC_AUTH_SEP, &lasts)) {
1486		pap = getprofnam(prof);
1487		if (pap == NULL)
1488			continue;
1489
1490		authlist = kva_match(pap->attr, PROFATTR_AUTHS_KW);
1491		if (authlist != NULL)
1492			ret = check_auth_list(pcp, authlist);
1493
1494		if (!ret) {
1495			subproflist = kva_match(pap->attr, PROFATTR_PROFS_KW);
1496			if (subproflist != NULL)
1497				/* depth check to avoid infinite recursion? */
1498				ret = check_prof_list(pcp, subproflist);
1499		}
1500
1501		free_profattr(pap);
1502		if (ret)
1503			return (ret);
1504	}
1505
1506	return (ret);
1507}
1508
1509static int
1510perm_granted(permcheck_t *pcp)
1511{
1512	ucred_t *uc;
1513
1514	int ret = 0;
1515	uid_t uid;
1516	userattr_t *uap;
1517	char *authlist, *userattr_authlist, *proflist, *def_prof = NULL;
1518
1519	/*
1520	 * Get generic authorizations from policy.conf
1521	 *
1522	 * Note that _get_auth_policy is not threadsafe, so we single-thread
1523	 * access to it.
1524	 */
1525	(void) pthread_mutex_lock(&perm_lock);
1526	ret = _get_auth_policy(&authlist, &def_prof);
1527	(void) pthread_mutex_unlock(&perm_lock);
1528
1529	if (ret != 0)
1530		return (-1);
1531
1532	if (authlist != NULL) {
1533		ret = check_auth_list(pcp, authlist);
1534
1535		if (ret) {
1536			_free_auth_policy(authlist, def_prof);
1537			return (ret);
1538		}
1539	}
1540
1541	/*
1542	 * Put off checking def_prof for later in an attempt to consolidate
1543	 * prof_attr accesses.
1544	 */
1545
1546	/* Get the uid */
1547	if ((uc = get_ucred()) == NULL) {
1548		_free_auth_policy(authlist, def_prof);
1549
1550		if (errno == EINVAL) {
1551			/*
1552			 * Client is no longer waiting for our response (e.g.,
1553			 * it received a signal & resumed with EINTR).
1554			 * Punting with door_return() would be nice but we
1555			 * need to release all of the locks & references we
1556			 * hold.  And we must report failure to the client
1557			 * layer to keep it from ignoring retries as
1558			 * already-done (idempotency & all that).  None of the
1559			 * error codes fit very well, so we might as well
1560			 * force the return of _PERMISSION_DENIED since we
1561			 * couldn't determine the user.
1562			 */
1563			return (0);
1564		}
1565		assert(0);
1566		abort();
1567	}
1568
1569	uid = ucred_geteuid(uc);
1570	assert(uid != (uid_t)-1);
1571
1572	uap = getuseruid(uid);
1573	if (uap != NULL) {
1574		/* Get the authorizations from user_attr. */
1575		userattr_authlist = kva_match(uap->attr, USERATTR_AUTHS_KW);
1576		if (userattr_authlist != NULL) {
1577			ret = check_auth_list(pcp, userattr_authlist);
1578		}
1579	}
1580
1581	if (!ret && def_prof != NULL) {
1582		/* Check generic profiles. */
1583		ret = check_prof_list(pcp, def_prof);
1584	}
1585
1586	if (!ret && uap != NULL) {
1587		proflist = kva_match(uap->attr, USERATTR_PROFILES_KW);
1588		if (proflist != NULL)
1589			ret = check_prof_list(pcp, proflist);
1590	}
1591
1592	_free_auth_policy(authlist, def_prof);
1593	if (uap != NULL)
1594		free_userattr(uap);
1595
1596	return (ret);
1597}
1598#endif /* NATIVE_BUILD */
1599
1600/*
1601 * flags in RC_NODE_WAITING_FLAGS are broadcast when unset, and are used to
1602 * serialize certain actions, and to wait for certain operations to complete
1603 *
1604 * The waiting flags are:
1605 *	RC_NODE_CHILDREN_CHANGING
1606 *		The child list is being built or changed (due to creation
1607 *		or deletion).  All iterators pause.
1608 *
1609 *	RC_NODE_USING_PARENT
1610 *		Someone is actively using the parent pointer, so we can't
1611 *		be removed from the parent list.
1612 *
1613 *	RC_NODE_CREATING_CHILD
1614 *		A child is being created -- locks out other creations, to
1615 *		prevent insert-insert races.
1616 *
1617 *	RC_NODE_IN_TX
1618 *		This object is running a transaction.
1619 *
1620 *	RC_NODE_DYING
1621 *		This node might be dying.  Always set as a set, using
1622 *		RC_NODE_DYING_FLAGS (which is everything but
1623 *		RC_NODE_USING_PARENT)
1624 */
1625static int
1626rc_node_hold_flag(rc_node_t *np, uint32_t flag)
1627{
1628	assert(MUTEX_HELD(&np->rn_lock));
1629	assert((flag & ~RC_NODE_WAITING_FLAGS) == 0);
1630
1631	while (!(np->rn_flags & RC_NODE_DEAD) && (np->rn_flags & flag)) {
1632		(void) pthread_cond_wait(&np->rn_cv, &np->rn_lock);
1633	}
1634	if (np->rn_flags & RC_NODE_DEAD)
1635		return (0);
1636
1637	np->rn_flags |= flag;
1638	return (1);
1639}
1640
1641static void
1642rc_node_rele_flag(rc_node_t *np, uint32_t flag)
1643{
1644	assert((flag & ~RC_NODE_WAITING_FLAGS) == 0);
1645	assert(MUTEX_HELD(&np->rn_lock));
1646	assert((np->rn_flags & flag) == flag);
1647	np->rn_flags &= ~flag;
1648	(void) pthread_cond_broadcast(&np->rn_cv);
1649}
1650
1651/*
1652 * wait until a particular flag has cleared.  Fails if the object dies.
1653 */
1654static int
1655rc_node_wait_flag(rc_node_t *np, uint32_t flag)
1656{
1657	assert(MUTEX_HELD(&np->rn_lock));
1658	while (!(np->rn_flags & RC_NODE_DEAD) && (np->rn_flags & flag))
1659		(void) pthread_cond_wait(&np->rn_cv, &np->rn_lock);
1660
1661	return (!(np->rn_flags & RC_NODE_DEAD));
1662}
1663
1664/*
1665 * On entry, np's lock must be held, and this thread must be holding
1666 * RC_NODE_USING_PARENT.  On return, both of them are released.
1667 *
1668 * If the return value is NULL, np either does not have a parent, or
1669 * the parent has been marked DEAD.
1670 *
1671 * If the return value is non-NULL, it is the parent of np, and both
1672 * its lock and the requested flags are held.
1673 */
1674static rc_node_t *
1675rc_node_hold_parent_flag(rc_node_t *np, uint32_t flag)
1676{
1677	rc_node_t *pp;
1678
1679	assert(MUTEX_HELD(&np->rn_lock));
1680	assert(np->rn_flags & RC_NODE_USING_PARENT);
1681
1682	if ((pp = np->rn_parent) == NULL) {
1683		rc_node_rele_flag(np, RC_NODE_USING_PARENT);
1684		(void) pthread_mutex_unlock(&np->rn_lock);
1685		return (NULL);
1686	}
1687	(void) pthread_mutex_unlock(&np->rn_lock);
1688
1689	(void) pthread_mutex_lock(&pp->rn_lock);
1690	(void) pthread_mutex_lock(&np->rn_lock);
1691	rc_node_rele_flag(np, RC_NODE_USING_PARENT);
1692	(void) pthread_mutex_unlock(&np->rn_lock);
1693
1694	if (!rc_node_hold_flag(pp, flag)) {
1695		(void) pthread_mutex_unlock(&pp->rn_lock);
1696		return (NULL);
1697	}
1698	return (pp);
1699}
1700
1701rc_node_t *
1702rc_node_alloc(void)
1703{
1704	rc_node_t *np = uu_zalloc(sizeof (*np));
1705
1706	if (np == NULL)
1707		return (NULL);
1708
1709	(void) pthread_mutex_init(&np->rn_lock, NULL);
1710	(void) pthread_cond_init(&np->rn_cv, NULL);
1711
1712	np->rn_children = uu_list_create(rc_children_pool, np, 0);
1713	np->rn_pg_notify_list = uu_list_create(rc_pg_notify_pool, np, 0);
1714
1715	uu_list_node_init(np, &np->rn_sibling_node, rc_children_pool);
1716
1717	uu_list_node_init(&np->rn_notify, &np->rn_notify.rcn_list_node,
1718	    rc_notify_pool);
1719
1720	return (np);
1721}
1722
1723void
1724rc_node_destroy(rc_node_t *np)
1725{
1726	int i;
1727
1728	if (np->rn_flags & RC_NODE_UNREFED)
1729		return;				/* being handled elsewhere */
1730
1731	assert(np->rn_refs == 0 && np->rn_other_refs == 0);
1732	assert(np->rn_former == NULL);
1733
1734	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
1735		/* Release the holds from rc_iter_next(). */
1736		for (i = 0; i < COMPOSITION_DEPTH; ++i) {
1737			/* rn_cchain[i] may be NULL for empty snapshots. */
1738			if (np->rn_cchain[i] != NULL)
1739				rc_node_rele(np->rn_cchain[i]);
1740		}
1741	}
1742
1743	if (np->rn_name != NULL)
1744		free((void *)np->rn_name);
1745	np->rn_name = NULL;
1746	if (np->rn_type != NULL)
1747		free((void *)np->rn_type);
1748	np->rn_type = NULL;
1749	if (np->rn_values != NULL)
1750		object_free_values(np->rn_values, np->rn_valtype,
1751		    np->rn_values_count, np->rn_values_size);
1752	np->rn_values = NULL;
1753	rc_node_free_fmri(np);
1754
1755	if (np->rn_snaplevel != NULL)
1756		rc_snaplevel_rele(np->rn_snaplevel);
1757	np->rn_snaplevel = NULL;
1758
1759	uu_list_node_fini(np, &np->rn_sibling_node, rc_children_pool);
1760
1761	uu_list_node_fini(&np->rn_notify, &np->rn_notify.rcn_list_node,
1762	    rc_notify_pool);
1763
1764	assert(uu_list_first(np->rn_children) == NULL);
1765	uu_list_destroy(np->rn_children);
1766	uu_list_destroy(np->rn_pg_notify_list);
1767
1768	(void) pthread_mutex_destroy(&np->rn_lock);
1769	(void) pthread_cond_destroy(&np->rn_cv);
1770
1771	uu_free(np);
1772}
1773
1774/*
1775 * Link in a child node.
1776 *
1777 * Because of the lock ordering, cp has to already be in the hash table with
1778 * its lock dropped before we get it.  To prevent anyone from noticing that
1779 * it is parentless, the creation code sets the RC_NODE_USING_PARENT.  Once
1780 * we've linked it in, we release the flag.
1781 */
1782static void
1783rc_node_link_child(rc_node_t *np, rc_node_t *cp)
1784{
1785	assert(!MUTEX_HELD(&np->rn_lock));
1786	assert(!MUTEX_HELD(&cp->rn_lock));
1787
1788	(void) pthread_mutex_lock(&np->rn_lock);
1789	(void) pthread_mutex_lock(&cp->rn_lock);
1790	assert(!(cp->rn_flags & RC_NODE_IN_PARENT) &&
1791	    (cp->rn_flags & RC_NODE_USING_PARENT));
1792
1793	assert(rc_check_parent_child(np->rn_id.rl_type, cp->rn_id.rl_type) ==
1794	    REP_PROTOCOL_SUCCESS);
1795
1796	cp->rn_parent = np;
1797	cp->rn_flags |= RC_NODE_IN_PARENT;
1798	(void) uu_list_insert_before(np->rn_children, NULL, cp);
1799	(void) rc_node_build_fmri(cp);
1800
1801	(void) pthread_mutex_unlock(&np->rn_lock);
1802
1803	rc_node_rele_flag(cp, RC_NODE_USING_PARENT);
1804	(void) pthread_mutex_unlock(&cp->rn_lock);
1805}
1806
1807/*
1808 * Sets the rn_parent_ref field of all the children of np to pp -- always
1809 * initially invoked as rc_node_setup_parent_ref(np, np), we then recurse.
1810 *
1811 * This is used when we mark a node RC_NODE_OLD, so that when the object and
1812 * its children are no longer referenced, they will all be deleted as a unit.
1813 */
1814static void
1815rc_node_setup_parent_ref(rc_node_t *np, rc_node_t *pp)
1816{
1817	rc_node_t *cp;
1818
1819	assert(MUTEX_HELD(&np->rn_lock));
1820
1821	for (cp = uu_list_first(np->rn_children); cp != NULL;
1822	    cp = uu_list_next(np->rn_children, cp)) {
1823		(void) pthread_mutex_lock(&cp->rn_lock);
1824		if (cp->rn_flags & RC_NODE_PARENT_REF) {
1825			assert(cp->rn_parent_ref == pp);
1826		} else {
1827			assert(cp->rn_parent_ref == NULL);
1828
1829			cp->rn_flags |= RC_NODE_PARENT_REF;
1830			cp->rn_parent_ref = pp;
1831			if (cp->rn_refs != 0)
1832				rc_node_hold_other(pp);
1833		}
1834		rc_node_setup_parent_ref(cp, pp);		/* recurse */
1835		(void) pthread_mutex_unlock(&cp->rn_lock);
1836	}
1837}
1838
1839/*
1840 * Atomically replace 'np' with 'newp', with a parent of 'pp'.
1841 *
1842 * Requirements:
1843 *	*no* node locks may be held.
1844 *	pp must be held with RC_NODE_CHILDREN_CHANGING
1845 *	newp and np must be held with RC_NODE_IN_TX
1846 *	np must be marked RC_NODE_IN_PARENT, newp must not be
1847 *	np must be marked RC_NODE_OLD
1848 *
1849 * Afterwards:
1850 *	pp's RC_NODE_CHILDREN_CHANGING is dropped
1851 *	newp and np's RC_NODE_IN_TX is dropped
1852 *	newp->rn_former = np;
1853 *	newp is RC_NODE_IN_PARENT, np is not.
1854 *	interested notify subscribers have been notified of newp's new status.
1855 */
1856static void
1857rc_node_relink_child(rc_node_t *pp, rc_node_t *np, rc_node_t *newp)
1858{
1859	cache_bucket_t *bp;
1860	/*
1861	 * First, swap np and nnp in the cache.  newp's RC_NODE_IN_TX flag
1862	 * keeps rc_node_update() from seeing it until we are done.
1863	 */
1864	bp = cache_hold(newp->rn_hash);
1865	cache_remove_unlocked(bp, np);
1866	cache_insert_unlocked(bp, newp);
1867	cache_release(bp);
1868
1869	/*
1870	 * replace np with newp in pp's list, and attach it to newp's rn_former
1871	 * link.
1872	 */
1873	(void) pthread_mutex_lock(&pp->rn_lock);
1874	assert(pp->rn_flags & RC_NODE_CHILDREN_CHANGING);
1875
1876	(void) pthread_mutex_lock(&newp->rn_lock);
1877	assert(!(newp->rn_flags & RC_NODE_IN_PARENT));
1878	assert(newp->rn_flags & RC_NODE_IN_TX);
1879
1880	(void) pthread_mutex_lock(&np->rn_lock);
1881	assert(np->rn_flags & RC_NODE_IN_PARENT);
1882	assert(np->rn_flags & RC_NODE_OLD);
1883	assert(np->rn_flags & RC_NODE_IN_TX);
1884
1885	newp->rn_parent = pp;
1886	newp->rn_flags |= RC_NODE_IN_PARENT;
1887
1888	/*
1889	 * Note that we carefully add newp before removing np -- this
1890	 * keeps iterators on the list from missing us.
1891	 */
1892	(void) uu_list_insert_after(pp->rn_children, np, newp);
1893	(void) rc_node_build_fmri(newp);
1894	(void) uu_list_remove(pp->rn_children, np);
1895
1896	/*
1897	 * re-set np
1898	 */
1899	newp->rn_former = np;
1900	np->rn_parent = NULL;
1901	np->rn_flags &= ~RC_NODE_IN_PARENT;
1902	np->rn_flags |= RC_NODE_ON_FORMER;
1903
1904	rc_notify_insert_node(newp);
1905
1906	rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
1907	(void) pthread_mutex_unlock(&pp->rn_lock);
1908	rc_node_rele_flag(newp, RC_NODE_USING_PARENT | RC_NODE_IN_TX);
1909	(void) pthread_mutex_unlock(&newp->rn_lock);
1910	rc_node_setup_parent_ref(np, np);
1911	rc_node_rele_flag(np, RC_NODE_IN_TX);
1912	(void) pthread_mutex_unlock(&np->rn_lock);
1913}
1914
1915/*
1916 * makes sure a node with lookup 'nip', name 'name', and parent 'pp' exists.
1917 * 'cp' is used (and returned) if the node does not yet exist.  If it does
1918 * exist, 'cp' is freed, and the existent node is returned instead.
1919 */
1920rc_node_t *
1921rc_node_setup(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
1922    rc_node_t *pp)
1923{
1924	rc_node_t *np;
1925	cache_bucket_t *bp;
1926	uint32_t h = rc_node_hash(nip);
1927
1928	assert(cp->rn_refs == 0);
1929
1930	bp = cache_hold(h);
1931	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
1932		cache_release(bp);
1933
1934		/*
1935		 * make sure it matches our expectations
1936		 */
1937		(void) pthread_mutex_lock(&np->rn_lock);
1938		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
1939			assert(np->rn_parent == pp);
1940			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
1941			assert(strcmp(np->rn_name, name) == 0);
1942			assert(np->rn_type == NULL);
1943			assert(np->rn_flags & RC_NODE_IN_PARENT);
1944			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
1945		}
1946		(void) pthread_mutex_unlock(&np->rn_lock);
1947
1948		rc_node_destroy(cp);
1949		return (np);
1950	}
1951
1952	/*
1953	 * No one is there -- create a new node.
1954	 */
1955	np = cp;
1956	rc_node_hold(np);
1957	np->rn_id = *nip;
1958	np->rn_hash = h;
1959	np->rn_name = strdup(name);
1960
1961	np->rn_flags |= RC_NODE_USING_PARENT;
1962
1963	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE) {
1964#if COMPOSITION_DEPTH == 2
1965		np->rn_cchain[0] = np;
1966		np->rn_cchain[1] = pp;
1967#else
1968#error This code must be updated.
1969#endif
1970	}
1971
1972	cache_insert_unlocked(bp, np);
1973	cache_release(bp);		/* we are now visible */
1974
1975	rc_node_link_child(pp, np);
1976
1977	return (np);
1978}
1979
1980/*
1981 * makes sure a snapshot with lookup 'nip', name 'name', and parent 'pp' exists.
1982 * 'cp' is used (and returned) if the node does not yet exist.  If it does
1983 * exist, 'cp' is freed, and the existent node is returned instead.
1984 */
1985rc_node_t *
1986rc_node_setup_snapshot(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
1987    uint32_t snap_id, rc_node_t *pp)
1988{
1989	rc_node_t *np;
1990	cache_bucket_t *bp;
1991	uint32_t h = rc_node_hash(nip);
1992
1993	assert(cp->rn_refs == 0);
1994
1995	bp = cache_hold(h);
1996	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
1997		cache_release(bp);
1998
1999		/*
2000		 * make sure it matches our expectations
2001		 */
2002		(void) pthread_mutex_lock(&np->rn_lock);
2003		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
2004			assert(np->rn_parent == pp);
2005			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
2006			assert(strcmp(np->rn_name, name) == 0);
2007			assert(np->rn_type == NULL);
2008			assert(np->rn_flags & RC_NODE_IN_PARENT);
2009			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
2010		}
2011		(void) pthread_mutex_unlock(&np->rn_lock);
2012
2013		rc_node_destroy(cp);
2014		return (np);
2015	}
2016
2017	/*
2018	 * No one is there -- create a new node.
2019	 */
2020	np = cp;
2021	rc_node_hold(np);
2022	np->rn_id = *nip;
2023	np->rn_hash = h;
2024	np->rn_name = strdup(name);
2025	np->rn_snapshot_id = snap_id;
2026
2027	np->rn_flags |= RC_NODE_USING_PARENT;
2028
2029	cache_insert_unlocked(bp, np);
2030	cache_release(bp);		/* we are now visible */
2031
2032	rc_node_link_child(pp, np);
2033
2034	return (np);
2035}
2036
2037/*
2038 * makes sure a snaplevel with lookup 'nip' and parent 'pp' exists.  'cp' is
2039 * used (and returned) if the node does not yet exist.  If it does exist, 'cp'
2040 * is freed, and the existent node is returned instead.
2041 */
2042rc_node_t *
2043rc_node_setup_snaplevel(rc_node_t *cp, rc_node_lookup_t *nip,
2044    rc_snaplevel_t *lvl, rc_node_t *pp)
2045{
2046	rc_node_t *np;
2047	cache_bucket_t *bp;
2048	uint32_t h = rc_node_hash(nip);
2049
2050	assert(cp->rn_refs == 0);
2051
2052	bp = cache_hold(h);
2053	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
2054		cache_release(bp);
2055
2056		/*
2057		 * make sure it matches our expectations
2058		 */
2059		(void) pthread_mutex_lock(&np->rn_lock);
2060		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
2061			assert(np->rn_parent == pp);
2062			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
2063			assert(np->rn_name == NULL);
2064			assert(np->rn_type == NULL);
2065			assert(np->rn_flags & RC_NODE_IN_PARENT);
2066			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
2067		}
2068		(void) pthread_mutex_unlock(&np->rn_lock);
2069
2070		rc_node_destroy(cp);
2071		return (np);
2072	}
2073
2074	/*
2075	 * No one is there -- create a new node.
2076	 */
2077	np = cp;
2078	rc_node_hold(np);	/* released in snapshot_fill_children() */
2079	np->rn_id = *nip;
2080	np->rn_hash = h;
2081
2082	rc_snaplevel_hold(lvl);
2083	np->rn_snaplevel = lvl;
2084
2085	np->rn_flags |= RC_NODE_USING_PARENT;
2086
2087	cache_insert_unlocked(bp, np);
2088	cache_release(bp);		/* we are now visible */
2089
2090	/* Add this snaplevel to the snapshot's composition chain. */
2091	assert(pp->rn_cchain[lvl->rsl_level_num - 1] == NULL);
2092	pp->rn_cchain[lvl->rsl_level_num - 1] = np;
2093
2094	rc_node_link_child(pp, np);
2095
2096	return (np);
2097}
2098
2099/*
2100 * Returns NULL if strdup() fails.
2101 */
2102rc_node_t *
2103rc_node_setup_pg(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
2104    const char *type, uint32_t flags, uint32_t gen_id, rc_node_t *pp)
2105{
2106	rc_node_t *np;
2107	cache_bucket_t *bp;
2108
2109	uint32_t h = rc_node_hash(nip);
2110	bp = cache_hold(h);
2111	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
2112		cache_release(bp);
2113
2114		/*
2115		 * make sure it matches our expectations (don't check
2116		 * the generation number or parent, since someone could
2117		 * have gotten a transaction through while we weren't
2118		 * looking)
2119		 */
2120		(void) pthread_mutex_lock(&np->rn_lock);
2121		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
2122			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
2123			assert(strcmp(np->rn_name, name) == 0);
2124			assert(strcmp(np->rn_type, type) == 0);
2125			assert(np->rn_pgflags == flags);
2126			assert(np->rn_flags & RC_NODE_IN_PARENT);
2127			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
2128		}
2129		(void) pthread_mutex_unlock(&np->rn_lock);
2130
2131		rc_node_destroy(cp);
2132		return (np);
2133	}
2134
2135	np = cp;
2136	rc_node_hold(np);		/* released in fill_pg_callback() */
2137	np->rn_id = *nip;
2138	np->rn_hash = h;
2139	np->rn_name = strdup(name);
2140	if (np->rn_name == NULL) {
2141		rc_node_rele(np);
2142		return (NULL);
2143	}
2144	np->rn_type = strdup(type);
2145	if (np->rn_type == NULL) {
2146		free((void *)np->rn_name);
2147		rc_node_rele(np);
2148		return (NULL);
2149	}
2150	np->rn_pgflags = flags;
2151	np->rn_gen_id = gen_id;
2152
2153	np->rn_flags |= RC_NODE_USING_PARENT;
2154
2155	cache_insert_unlocked(bp, np);
2156	cache_release(bp);		/* we are now visible */
2157
2158	rc_node_link_child(pp, np);
2159
2160	return (np);
2161}
2162
2163#if COMPOSITION_DEPTH == 2
2164/*
2165 * Initialize a "composed property group" which represents the composition of
2166 * property groups pg1 & pg2.  It is ephemeral: once created & returned for an
2167 * ITER_READ request, keeping it out of cache_hash and any child lists
2168 * prevents it from being looked up.  Operations besides iteration are passed
2169 * through to pg1.
2170 *
2171 * pg1 & pg2 should be held before entering this function.  They will be
2172 * released in rc_node_destroy().
2173 */
2174static int
2175rc_node_setup_cpg(rc_node_t *cpg, rc_node_t *pg1, rc_node_t *pg2)
2176{
2177	if (strcmp(pg1->rn_type, pg2->rn_type) != 0)
2178		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2179
2180	cpg->rn_id.rl_type = REP_PROTOCOL_ENTITY_CPROPERTYGRP;
2181	cpg->rn_name = strdup(pg1->rn_name);
2182	if (cpg->rn_name == NULL)
2183		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
2184
2185	cpg->rn_cchain[0] = pg1;
2186	cpg->rn_cchain[1] = pg2;
2187
2188	return (REP_PROTOCOL_SUCCESS);
2189}
2190#else
2191#error This code must be updated.
2192#endif
2193
2194/*
2195 * Fails with _NO_RESOURCES.
2196 */
2197int
2198rc_node_create_property(rc_node_t *pp, rc_node_lookup_t *nip,
2199    const char *name, rep_protocol_value_type_t type,
2200    const char *vals, size_t count, size_t size)
2201{
2202	rc_node_t *np;
2203	cache_bucket_t *bp;
2204
2205	uint32_t h = rc_node_hash(nip);
2206	bp = cache_hold(h);
2207	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
2208		cache_release(bp);
2209		/*
2210		 * make sure it matches our expectations
2211		 */
2212		(void) pthread_mutex_lock(&np->rn_lock);
2213		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
2214			assert(np->rn_parent == pp);
2215			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
2216			assert(strcmp(np->rn_name, name) == 0);
2217			assert(np->rn_valtype == type);
2218			assert(np->rn_values_count == count);
2219			assert(np->rn_values_size == size);
2220			assert(vals == NULL ||
2221			    memcmp(np->rn_values, vals, size) == 0);
2222			assert(np->rn_flags & RC_NODE_IN_PARENT);
2223			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
2224		}
2225		rc_node_rele_locked(np);
2226		object_free_values(vals, type, count, size);
2227		return (REP_PROTOCOL_SUCCESS);
2228	}
2229
2230	/*
2231	 * No one is there -- create a new node.
2232	 */
2233	np = rc_node_alloc();
2234	if (np == NULL) {
2235		cache_release(bp);
2236		object_free_values(vals, type, count, size);
2237		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
2238	}
2239	np->rn_id = *nip;
2240	np->rn_hash = h;
2241	np->rn_name = strdup(name);
2242	if (np->rn_name == NULL) {
2243		cache_release(bp);
2244		object_free_values(vals, type, count, size);
2245		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
2246	}
2247
2248	np->rn_valtype = type;
2249	np->rn_values = vals;
2250	np->rn_values_count = count;
2251	np->rn_values_size = size;
2252
2253	np->rn_flags |= RC_NODE_USING_PARENT;
2254
2255	cache_insert_unlocked(bp, np);
2256	cache_release(bp);		/* we are now visible */
2257
2258	rc_node_link_child(pp, np);
2259
2260	return (REP_PROTOCOL_SUCCESS);
2261}
2262
2263/*
2264 * This function implements a decision table to determine the event ID for
2265 * changes to the enabled (SCF_PROPERTY_ENABLED) property.  The event ID is
2266 * determined by the value of the first property in the command specified
2267 * by cmd_no and the name of the property group.  Here is the decision
2268 * table:
2269 *
2270 *				Property Group Name
2271 *	Property	------------------------------------------
2272 *	Value		SCF_PG_GENERAL		SCF_PG_GENERAL_OVR
2273 *	--------	--------------		------------------
2274 *	"0"		ADT_smf_disable		ADT_smf_tmp_disable
2275 *	"1"		ADT_smf_enable		ADT_smf_tmp_enable
2276 *
2277 * This function is called by special_property_event through a function
2278 * pointer in the special_props_list array.
2279 *
2280 * Since the ADT_smf_* symbols may not be defined in the build machine's
2281 * include files, this function is not compiled when doing native builds.
2282 */
2283#ifndef NATIVE_BUILD
2284static int
2285general_enable_id(tx_commit_data_t *tx_data, size_t cmd_no, const char *pg,
2286    au_event_t *event_id)
2287{
2288	const char *value;
2289	uint32_t nvalues;
2290	int enable;
2291
2292	/*
2293	 * First, check property value.
2294	 */
2295	if (tx_cmd_nvalues(tx_data, cmd_no, &nvalues) != REP_PROTOCOL_SUCCESS)
2296		return (-1);
2297	if (nvalues == 0)
2298		return (-1);
2299	if (tx_cmd_value(tx_data, cmd_no, 0, &value) != REP_PROTOCOL_SUCCESS)
2300		return (-1);
2301	if (strcmp(value, "0") == 0) {
2302		enable = 0;
2303	} else if (strcmp(value, "1") == 0) {
2304		enable = 1;
2305	} else {
2306		return (-1);
2307	}
2308
2309	/*
2310	 * Now check property group name.
2311	 */
2312	if (strcmp(pg, SCF_PG_GENERAL) == 0) {
2313		*event_id = enable ? ADT_smf_enable : ADT_smf_disable;
2314		return (0);
2315	} else if (strcmp(pg, SCF_PG_GENERAL_OVR) == 0) {
2316		*event_id = enable ? ADT_smf_tmp_enable : ADT_smf_tmp_disable;
2317		return (0);
2318	}
2319	return (-1);
2320}
2321#endif	/* NATIVE_BUILD */
2322
2323/*
2324 * This function compares two audit_special_prop_item_t structures
2325 * represented by item1 and item2.  It returns an integer greater than 0 if
2326 * item1 is greater than item2.  It returns 0 if they are equal and an
2327 * integer less than 0 if item1 is less than item2.  api_prop_name and
2328 * api_pg_name are the key fields for sorting.
2329 *
2330 * This function is suitable for calls to bsearch(3C) and qsort(3C).
2331 */
2332static int
2333special_prop_compare(const void *item1, const void *item2)
2334{
2335	const audit_special_prop_item_t *a = (audit_special_prop_item_t *)item1;
2336	const audit_special_prop_item_t *b = (audit_special_prop_item_t *)item2;
2337	int r;
2338
2339	r = strcmp(a->api_prop_name, b->api_prop_name);
2340	if (r == 0) {
2341		/*
2342		 * Primary keys are the same, so check the secondary key.
2343		 */
2344		r = strcmp(a->api_pg_name, b->api_pg_name);
2345	}
2346	return (r);
2347}
2348
2349int
2350rc_node_init(void)
2351{
2352	rc_node_t *np;
2353	cache_bucket_t *bp;
2354
2355	rc_children_pool = uu_list_pool_create("rc_children_pool",
2356	    sizeof (rc_node_t), offsetof(rc_node_t, rn_sibling_node),
2357	    NULL, UU_LIST_POOL_DEBUG);
2358
2359	rc_pg_notify_pool = uu_list_pool_create("rc_pg_notify_pool",
2360	    sizeof (rc_node_pg_notify_t),
2361	    offsetof(rc_node_pg_notify_t, rnpn_node),
2362	    NULL, UU_LIST_POOL_DEBUG);
2363
2364	rc_notify_pool = uu_list_pool_create("rc_notify_pool",
2365	    sizeof (rc_notify_t), offsetof(rc_notify_t, rcn_list_node),
2366	    NULL, UU_LIST_POOL_DEBUG);
2367
2368	rc_notify_info_pool = uu_list_pool_create("rc_notify_info_pool",
2369	    sizeof (rc_notify_info_t),
2370	    offsetof(rc_notify_info_t, rni_list_node),
2371	    NULL, UU_LIST_POOL_DEBUG);
2372
2373	if (rc_children_pool == NULL || rc_pg_notify_pool == NULL ||
2374	    rc_notify_pool == NULL || rc_notify_info_pool == NULL)
2375		uu_die("out of memory");
2376
2377	rc_notify_list = uu_list_create(rc_notify_pool,
2378	    &rc_notify_list, 0);
2379
2380	rc_notify_info_list = uu_list_create(rc_notify_info_pool,
2381	    &rc_notify_info_list, 0);
2382
2383	if (rc_notify_list == NULL || rc_notify_info_list == NULL)
2384		uu_die("out of memory");
2385
2386	/*
2387	 * Sort the special_props_list array so that it can be searched
2388	 * with bsearch(3C).
2389	 *
2390	 * The special_props_list array is not compiled into the native
2391	 * build code, so there is no need to call qsort if NATIVE_BUILD is
2392	 * defined.
2393	 */
2394#ifndef	NATIVE_BUILD
2395	qsort(special_props_list, SPECIAL_PROP_COUNT,
2396	    sizeof (special_props_list[0]), special_prop_compare);
2397#endif	/* NATIVE_BUILD */
2398
2399	if ((np = rc_node_alloc()) == NULL)
2400		uu_die("out of memory");
2401
2402	rc_node_hold(np);
2403	np->rn_id.rl_type = REP_PROTOCOL_ENTITY_SCOPE;
2404	np->rn_id.rl_backend = BACKEND_TYPE_NORMAL;
2405	np->rn_hash = rc_node_hash(&np->rn_id);
2406	np->rn_name = "localhost";
2407
2408	bp = cache_hold(np->rn_hash);
2409	cache_insert_unlocked(bp, np);
2410	cache_release(bp);
2411
2412	rc_scope = np;
2413	return (1);
2414}
2415
2416/*
2417 * Fails with
2418 *   _INVALID_TYPE - type is invalid
2419 *   _TYPE_MISMATCH - np doesn't carry children of type type
2420 *   _DELETED - np has been deleted
2421 *   _NO_RESOURCES
2422 */
2423static int
2424rc_node_fill_children(rc_node_t *np, uint32_t type)
2425{
2426	int rc;
2427
2428	assert(MUTEX_HELD(&np->rn_lock));
2429
2430	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
2431	    REP_PROTOCOL_SUCCESS)
2432		return (rc);
2433
2434	if (!rc_node_hold_flag(np, RC_NODE_CHILDREN_CHANGING))
2435		return (REP_PROTOCOL_FAIL_DELETED);
2436
2437	if (np->rn_flags & RC_NODE_HAS_CHILDREN) {
2438		rc_node_rele_flag(np, RC_NODE_CHILDREN_CHANGING);
2439		return (REP_PROTOCOL_SUCCESS);
2440	}
2441
2442	(void) pthread_mutex_unlock(&np->rn_lock);
2443	rc = object_fill_children(np);
2444	(void) pthread_mutex_lock(&np->rn_lock);
2445
2446	if (rc == REP_PROTOCOL_SUCCESS) {
2447		np->rn_flags |= RC_NODE_HAS_CHILDREN;
2448	}
2449	rc_node_rele_flag(np, RC_NODE_CHILDREN_CHANGING);
2450
2451	return (rc);
2452}
2453
2454/*
2455 * Returns
2456 *   _INVALID_TYPE - type is invalid
2457 *   _TYPE_MISMATCH - np doesn't carry children of type type
2458 *   _DELETED - np has been deleted
2459 *   _NO_RESOURCES
2460 *   _SUCCESS - if *cpp is not NULL, it is held
2461 */
2462static int
2463rc_node_find_named_child(rc_node_t *np, const char *name, uint32_t type,
2464    rc_node_t **cpp)
2465{
2466	int ret;
2467	rc_node_t *cp;
2468
2469	assert(MUTEX_HELD(&np->rn_lock));
2470	assert(np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP);
2471
2472	ret = rc_node_fill_children(np, type);
2473	if (ret != REP_PROTOCOL_SUCCESS)
2474		return (ret);
2475
2476	for (cp = uu_list_first(np->rn_children);
2477	    cp != NULL;
2478	    cp = uu_list_next(np->rn_children, cp)) {
2479		if (cp->rn_id.rl_type == type && strcmp(cp->rn_name, name) == 0)
2480			break;
2481	}
2482
2483	if (cp != NULL)
2484		rc_node_hold(cp);
2485	*cpp = cp;
2486
2487	return (REP_PROTOCOL_SUCCESS);
2488}
2489
2490static int rc_node_parent(rc_node_t *, rc_node_t **);
2491
2492/*
2493 * Returns
2494 *   _INVALID_TYPE - type is invalid
2495 *   _DELETED - np or an ancestor has been deleted
2496 *   _NOT_FOUND - no ancestor of specified type exists
2497 *   _SUCCESS - *app is held
2498 */
2499static int
2500rc_node_find_ancestor(rc_node_t *np, uint32_t type, rc_node_t **app)
2501{
2502	int ret;
2503	rc_node_t *parent, *np_orig;
2504
2505	if (type >= REP_PROTOCOL_ENTITY_MAX)
2506		return (REP_PROTOCOL_FAIL_INVALID_TYPE);
2507
2508	np_orig = np;
2509
2510	while (np->rn_id.rl_type > type) {
2511		ret = rc_node_parent(np, &parent);
2512		if (np != np_orig)
2513			rc_node_rele(np);
2514		if (ret != REP_PROTOCOL_SUCCESS)
2515			return (ret);
2516		np = parent;
2517	}
2518
2519	if (np->rn_id.rl_type == type) {
2520		*app = parent;
2521		return (REP_PROTOCOL_SUCCESS);
2522	}
2523
2524	return (REP_PROTOCOL_FAIL_NOT_FOUND);
2525}
2526
2527#ifndef NATIVE_BUILD
2528/*
2529 * If the propname property exists in pg, and it is of type string, add its
2530 * values as authorizations to pcp.  pg must not be locked on entry, and it is
2531 * returned unlocked.  Returns
2532 *   _DELETED - pg was deleted
2533 *   _NO_RESOURCES
2534 *   _NOT_FOUND - pg has no property named propname
2535 *   _SUCCESS
2536 */
2537static int
2538perm_add_pg_prop_values(permcheck_t *pcp, rc_node_t *pg, const char *propname)
2539{
2540	rc_node_t *prop;
2541	int result;
2542
2543	uint_t count;
2544	const char *cp;
2545
2546	assert(!MUTEX_HELD(&pg->rn_lock));
2547	assert(pg->rn_id.rl_type == REP_PROTOCOL_ENTITY_PROPERTYGRP);
2548
2549	(void) pthread_mutex_lock(&pg->rn_lock);
2550	result = rc_node_find_named_child(pg, propname,
2551	    REP_PROTOCOL_ENTITY_PROPERTY, &prop);
2552	(void) pthread_mutex_unlock(&pg->rn_lock);
2553	if (result != REP_PROTOCOL_SUCCESS) {
2554		switch (result) {
2555		case REP_PROTOCOL_FAIL_DELETED:
2556		case REP_PROTOCOL_FAIL_NO_RESOURCES:
2557			return (result);
2558
2559		case REP_PROTOCOL_FAIL_INVALID_TYPE:
2560		case REP_PROTOCOL_FAIL_TYPE_MISMATCH:
2561		default:
2562			bad_error("rc_node_find_named_child", result);
2563		}
2564	}
2565
2566	if (prop == NULL)
2567		return (REP_PROTOCOL_FAIL_NOT_FOUND);
2568
2569	/* rn_valtype is immutable, so no locking. */
2570	if (prop->rn_valtype != REP_PROTOCOL_TYPE_STRING) {
2571		rc_node_rele(prop);
2572		return (REP_PROTOCOL_SUCCESS);
2573	}
2574
2575	(void) pthread_mutex_lock(&prop->rn_lock);
2576	for (count = prop->rn_values_count, cp = prop->rn_values;
2577	    count > 0;
2578	    --count) {
2579		result = perm_add_enabling_type(pcp, cp,
2580		    (pg->rn_id.rl_ids[ID_INSTANCE]) ? PC_AUTH_INST :
2581		    PC_AUTH_SVC);
2582		if (result != REP_PROTOCOL_SUCCESS)
2583			break;
2584
2585		cp = strchr(cp, '\0') + 1;
2586	}
2587
2588	rc_node_rele_locked(prop);
2589
2590	return (result);
2591}
2592
2593/*
2594 * Assuming that ent is a service or instance node, if the pgname property
2595 * group has type pgtype, and it has a propname property with string type, add
2596 * its values as authorizations to pcp.  If pgtype is NULL, it is not checked.
2597 * Returns
2598 *   _SUCCESS
2599 *   _DELETED - ent was deleted
2600 *   _NO_RESOURCES - no resources
2601 *   _NOT_FOUND - ent does not have pgname pg or propname property
2602 */
2603static int
2604perm_add_ent_prop_values(permcheck_t *pcp, rc_node_t *ent, const char *pgname,
2605    const char *pgtype, const char *propname)
2606{
2607	int r;
2608	rc_node_t *pg;
2609
2610	assert(!MUTEX_HELD(&ent->rn_lock));
2611
2612	(void) pthread_mutex_lock(&ent->rn_lock);
2613	r = rc_node_find_named_child(ent, pgname,
2614	    REP_PROTOCOL_ENTITY_PROPERTYGRP, &pg);
2615	(void) pthread_mutex_unlock(&ent->rn_lock);
2616
2617	switch (r) {
2618	case REP_PROTOCOL_SUCCESS:
2619		break;
2620
2621	case REP_PROTOCOL_FAIL_DELETED:
2622	case REP_PROTOCOL_FAIL_NO_RESOURCES:
2623		return (r);
2624
2625	default:
2626		bad_error("rc_node_find_named_child", r);
2627	}
2628
2629	if (pg == NULL)
2630		return (REP_PROTOCOL_FAIL_NOT_FOUND);
2631
2632	if (pgtype == NULL || strcmp(pg->rn_type, pgtype) == 0) {
2633		r = perm_add_pg_prop_values(pcp, pg, propname);
2634		switch (r) {
2635		case REP_PROTOCOL_FAIL_DELETED:
2636			r = REP_PROTOCOL_FAIL_NOT_FOUND;
2637			break;
2638
2639		case REP_PROTOCOL_FAIL_NO_RESOURCES:
2640		case REP_PROTOCOL_SUCCESS:
2641		case REP_PROTOCOL_FAIL_NOT_FOUND:
2642			break;
2643
2644		default:
2645			bad_error("perm_add_pg_prop_values", r);
2646		}
2647	}
2648
2649	rc_node_rele(pg);
2650
2651	return (r);
2652}
2653
2654/*
2655 * If pg has a property named propname, and is string typed, add its values as
2656 * authorizations to pcp.  If pg has no such property, and its parent is an
2657 * instance, walk up to the service and try doing the same with the property
2658 * of the same name from the property group of the same name.  Returns
2659 *   _SUCCESS
2660 *   _NO_RESOURCES
2661 *   _DELETED - pg (or an ancestor) was deleted
2662 */
2663static int
2664perm_add_enabling_values(permcheck_t *pcp, rc_node_t *pg, const char *propname)
2665{
2666	int r;
2667	char pgname[REP_PROTOCOL_NAME_LEN + 1];
2668	rc_node_t *svc;
2669	size_t sz;
2670
2671	r = perm_add_pg_prop_values(pcp, pg, propname);
2672
2673	if (r != REP_PROTOCOL_FAIL_NOT_FOUND)
2674		return (r);
2675
2676	assert(!MUTEX_HELD(&pg->rn_lock));
2677
2678	if (pg->rn_id.rl_ids[ID_INSTANCE] == 0)
2679		return (REP_PROTOCOL_SUCCESS);
2680
2681	sz = strlcpy(pgname, pg->rn_name, sizeof (pgname));
2682	assert(sz < sizeof (pgname));
2683
2684	/*
2685	 * If pg is a child of an instance or snapshot, we want to compose the
2686	 * authorization property with the service's (if it exists).  The
2687	 * snapshot case applies only to read_authorization.  In all other
2688	 * cases, the pg's parent will be the instance.
2689	 */
2690	r = rc_node_find_ancestor(pg, REP_PROTOCOL_ENTITY_SERVICE, &svc);
2691	if (r != REP_PROTOCOL_SUCCESS) {
2692		assert(r == REP_PROTOCOL_FAIL_DELETED);
2693		return (r);
2694	}
2695	assert(svc->rn_id.rl_type == REP_PROTOCOL_ENTITY_SERVICE);
2696
2697	r = perm_add_ent_prop_values(pcp, svc, pgname, NULL, propname);
2698
2699	rc_node_rele(svc);
2700
2701	if (r == REP_PROTOCOL_FAIL_NOT_FOUND)
2702		r = REP_PROTOCOL_SUCCESS;
2703
2704	return (r);
2705}
2706
2707/*
2708 * Call perm_add_enabling_values() for the "action_authorization" property of
2709 * the "general" property group of inst.  Returns
2710 *   _DELETED - inst (or an ancestor) was deleted
2711 *   _NO_RESOURCES
2712 *   _SUCCESS
2713 */
2714static int
2715perm_add_inst_action_auth(permcheck_t *pcp, rc_node_t *inst)
2716{
2717	int r;
2718	rc_node_t *svc;
2719
2720	assert(inst->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE);
2721
2722	r = perm_add_ent_prop_values(pcp, inst, AUTH_PG_GENERAL,
2723	    AUTH_PG_GENERAL_TYPE, AUTH_PROP_ACTION);
2724
2725	if (r != REP_PROTOCOL_FAIL_NOT_FOUND)
2726		return (r);
2727
2728	r = rc_node_parent(inst, &svc);
2729	if (r != REP_PROTOCOL_SUCCESS) {
2730		assert(r == REP_PROTOCOL_FAIL_DELETED);
2731		return (r);
2732	}
2733
2734	r = perm_add_ent_prop_values(pcp, svc, AUTH_PG_GENERAL,
2735	    AUTH_PG_GENERAL_TYPE, AUTH_PROP_ACTION);
2736
2737	return (r == REP_PROTOCOL_FAIL_NOT_FOUND ? REP_PROTOCOL_SUCCESS : r);
2738}
2739#endif /* NATIVE_BUILD */
2740
2741void
2742rc_node_ptr_init(rc_node_ptr_t *out)
2743{
2744	out->rnp_node = NULL;
2745	out->rnp_auth_string = NULL;
2746	out->rnp_authorized = RC_AUTH_UNKNOWN;
2747	out->rnp_deleted = 0;
2748}
2749
2750void
2751rc_node_ptr_free_mem(rc_node_ptr_t *npp)
2752{
2753	if (npp->rnp_auth_string != NULL) {
2754		free((void *)npp->rnp_auth_string);
2755		npp->rnp_auth_string = NULL;
2756	}
2757}
2758
2759static void
2760rc_node_assign(rc_node_ptr_t *out, rc_node_t *val)
2761{
2762	rc_node_t *cur = out->rnp_node;
2763	if (val != NULL)
2764		rc_node_hold(val);
2765	out->rnp_node = val;
2766	if (cur != NULL)
2767		rc_node_rele(cur);
2768	out->rnp_authorized = RC_AUTH_UNKNOWN;
2769	rc_node_ptr_free_mem(out);
2770	out->rnp_deleted = 0;
2771}
2772
2773void
2774rc_node_clear(rc_node_ptr_t *out, int deleted)
2775{
2776	rc_node_assign(out, NULL);
2777	out->rnp_deleted = deleted;
2778}
2779
2780void
2781rc_node_ptr_assign(rc_node_ptr_t *out, const rc_node_ptr_t *val)
2782{
2783	rc_node_assign(out, val->rnp_node);
2784}
2785
2786/*
2787 * rc_node_check()/RC_NODE_CHECK()
2788 *	generic "entry" checks, run before the use of an rc_node pointer.
2789 *
2790 * Fails with
2791 *   _NOT_SET
2792 *   _DELETED
2793 */
2794static int
2795rc_node_check_and_lock(rc_node_t *np)
2796{
2797	int result = REP_PROTOCOL_SUCCESS;
2798	if (np == NULL)
2799		return (REP_PROTOCOL_FAIL_NOT_SET);
2800
2801	(void) pthread_mutex_lock(&np->rn_lock);
2802	if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
2803		result = REP_PROTOCOL_FAIL_DELETED;
2804		(void) pthread_mutex_unlock(&np->rn_lock);
2805	}
2806
2807	return (result);
2808}
2809
2810/*
2811 * Fails with
2812 *   _NOT_SET - ptr is reset
2813 *   _DELETED - node has been deleted
2814 */
2815static rc_node_t *
2816rc_node_ptr_check_and_lock(rc_node_ptr_t *npp, int *res)
2817{
2818	rc_node_t *np = npp->rnp_node;
2819	if (np == NULL) {
2820		if (npp->rnp_deleted)
2821			*res = REP_PROTOCOL_FAIL_DELETED;
2822		else
2823			*res = REP_PROTOCOL_FAIL_NOT_SET;
2824		return (NULL);
2825	}
2826
2827	(void) pthread_mutex_lock(&np->rn_lock);
2828	if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
2829		(void) pthread_mutex_unlock(&np->rn_lock);
2830		rc_node_clear(npp, 1);
2831		*res = REP_PROTOCOL_FAIL_DELETED;
2832		return (NULL);
2833	}
2834	return (np);
2835}
2836
2837#define	RC_NODE_CHECK_AND_LOCK(n) {					\
2838	int rc__res;							\
2839	if ((rc__res = rc_node_check_and_lock(n)) != REP_PROTOCOL_SUCCESS) \
2840		return (rc__res);					\
2841}
2842
2843#define	RC_NODE_CHECK(n) {						\
2844	RC_NODE_CHECK_AND_LOCK(n);					\
2845	(void) pthread_mutex_unlock(&(n)->rn_lock);			\
2846}
2847
2848#define	RC_NODE_CHECK_AND_HOLD(n) {					\
2849	RC_NODE_CHECK_AND_LOCK(n);					\
2850	rc_node_hold_locked(n);						\
2851	(void) pthread_mutex_unlock(&(n)->rn_lock);			\
2852}
2853
2854#define	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp) {			\
2855	int rc__res;							\
2856	if (((np) = rc_node_ptr_check_and_lock(npp, &rc__res)) == NULL)	\
2857		return (rc__res);					\
2858}
2859
2860#define	RC_NODE_PTR_GET_CHECK(np, npp) {				\
2861	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);			\
2862	(void) pthread_mutex_unlock(&(np)->rn_lock);			\
2863}
2864
2865#define	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp) {			\
2866	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);			\
2867	rc_node_hold_locked(np);					\
2868	(void) pthread_mutex_unlock(&(np)->rn_lock);			\
2869}
2870
2871#define	HOLD_FLAG_OR_RETURN(np, flag) {					\
2872	assert(MUTEX_HELD(&(np)->rn_lock));				\
2873	assert(!((np)->rn_flags & RC_NODE_DEAD));			\
2874	if (!rc_node_hold_flag((np), flag)) {				\
2875		(void) pthread_mutex_unlock(&(np)->rn_lock);		\
2876		return (REP_PROTOCOL_FAIL_DELETED);			\
2877	}								\
2878}
2879
2880#define	HOLD_PTR_FLAG_OR_RETURN(np, npp, flag) {			\
2881	assert(MUTEX_HELD(&(np)->rn_lock));				\
2882	assert(!((np)->rn_flags & RC_NODE_DEAD));			\
2883	if (!rc_node_hold_flag((np), flag)) {				\
2884		(void) pthread_mutex_unlock(&(np)->rn_lock);		\
2885		assert((np) == (npp)->rnp_node);			\
2886		rc_node_clear(npp, 1);					\
2887		return (REP_PROTOCOL_FAIL_DELETED);			\
2888	}								\
2889}
2890
2891#define	HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, flag, mem) {		\
2892	assert(MUTEX_HELD(&(np)->rn_lock));				\
2893	assert(!((np)->rn_flags & RC_NODE_DEAD));			\
2894	if (!rc_node_hold_flag((np), flag)) {				\
2895		(void) pthread_mutex_unlock(&(np)->rn_lock);		\
2896		assert((np) == (npp)->rnp_node);			\
2897		rc_node_clear(npp, 1);					\
2898		if ((mem) != NULL)					\
2899			free((mem));					\
2900		return (REP_PROTOCOL_FAIL_DELETED);			\
2901	}								\
2902}
2903
2904int
2905rc_local_scope(uint32_t type, rc_node_ptr_t *out)
2906{
2907	if (type != REP_PROTOCOL_ENTITY_SCOPE) {
2908		rc_node_clear(out, 0);
2909		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2910	}
2911
2912	/*
2913	 * the main scope never gets destroyed
2914	 */
2915	rc_node_assign(out, rc_scope);
2916
2917	return (REP_PROTOCOL_SUCCESS);
2918}
2919
2920/*
2921 * Fails with
2922 *   _NOT_SET - npp is not set
2923 *   _DELETED - the node npp pointed at has been deleted
2924 *   _TYPE_MISMATCH - type is not _SCOPE
2925 *   _NOT_FOUND - scope has no parent
2926 */
2927static int
2928rc_scope_parent_scope(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out)
2929{
2930	rc_node_t *np;
2931
2932	rc_node_clear(out, 0);
2933
2934	RC_NODE_PTR_GET_CHECK(np, npp);
2935
2936	if (type != REP_PROTOCOL_ENTITY_SCOPE)
2937		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2938
2939	return (REP_PROTOCOL_FAIL_NOT_FOUND);
2940}
2941
2942static int rc_node_pg_check_read_protect(rc_node_t *);
2943
2944/*
2945 * Fails with
2946 *   _NOT_SET
2947 *   _DELETED
2948 *   _NOT_APPLICABLE
2949 *   _NOT_FOUND
2950 *   _BAD_REQUEST
2951 *   _TRUNCATED
2952 *   _NO_RESOURCES
2953 */
2954int
2955rc_node_name(rc_node_ptr_t *npp, char *buf, size_t sz, uint32_t answertype,
2956    size_t *sz_out)
2957{
2958	size_t actual;
2959	rc_node_t *np;
2960
2961	assert(sz == *sz_out);
2962
2963	RC_NODE_PTR_GET_CHECK(np, npp);
2964
2965	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
2966		np = np->rn_cchain[0];
2967		RC_NODE_CHECK(np);
2968	}
2969
2970	switch (answertype) {
2971	case RP_ENTITY_NAME_NAME:
2972		if (np->rn_name == NULL)
2973			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2974		actual = strlcpy(buf, np->rn_name, sz);
2975		break;
2976	case RP_ENTITY_NAME_PGTYPE:
2977		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
2978			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2979		actual = strlcpy(buf, np->rn_type, sz);
2980		break;
2981	case RP_ENTITY_NAME_PGFLAGS:
2982		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
2983			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2984		actual = snprintf(buf, sz, "%d", np->rn_pgflags);
2985		break;
2986	case RP_ENTITY_NAME_SNAPLEVEL_SCOPE:
2987		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
2988			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2989		actual = strlcpy(buf, np->rn_snaplevel->rsl_scope, sz);
2990		break;
2991	case RP_ENTITY_NAME_SNAPLEVEL_SERVICE:
2992		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
2993			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2994		actual = strlcpy(buf, np->rn_snaplevel->rsl_service, sz);
2995		break;
2996	case RP_ENTITY_NAME_SNAPLEVEL_INSTANCE:
2997		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
2998			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2999		if (np->rn_snaplevel->rsl_instance == NULL)
3000			return (REP_PROTOCOL_FAIL_NOT_FOUND);
3001		actual = strlcpy(buf, np->rn_snaplevel->rsl_instance, sz);
3002		break;
3003	case RP_ENTITY_NAME_PGREADPROT:
3004	{
3005		int ret;
3006
3007		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
3008			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
3009		ret = rc_node_pg_check_read_protect(np);
3010		assert(ret != REP_PROTOCOL_FAIL_TYPE_MISMATCH);
3011		switch (ret) {
3012		case REP_PROTOCOL_FAIL_PERMISSION_DENIED:
3013			actual = snprintf(buf, sz, "1");
3014			break;
3015		case REP_PROTOCOL_SUCCESS:
3016			actual = snprintf(buf, sz, "0");
3017			break;
3018		default:
3019			return (ret);
3020		}
3021		break;
3022	}
3023	default:
3024		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
3025	}
3026	if (actual >= sz)
3027		return (REP_PROTOCOL_FAIL_TRUNCATED);
3028
3029	*sz_out = actual;
3030	return (REP_PROTOCOL_SUCCESS);
3031}
3032
3033int
3034rc_node_get_property_type(rc_node_ptr_t *npp, rep_protocol_value_type_t *out)
3035{
3036	rc_node_t *np;
3037
3038	RC_NODE_PTR_GET_CHECK(np, npp);
3039
3040	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
3041		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
3042
3043	*out = np->rn_valtype;
3044
3045	return (REP_PROTOCOL_SUCCESS);
3046}
3047
3048/*
3049 * Get np's parent.  If np is deleted, returns _DELETED.  Otherwise puts a hold
3050 * on the parent, returns a pointer to it in *out, and returns _SUCCESS.
3051 */
3052static int
3053rc_node_parent(rc_node_t *np, rc_node_t **out)
3054{
3055	rc_node_t *pnp;
3056	rc_node_t *np_orig;
3057
3058	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
3059		RC_NODE_CHECK_AND_LOCK(np);
3060	} else {
3061		np = np->rn_cchain[0];
3062		RC_NODE_CHECK_AND_LOCK(np);
3063	}
3064
3065	np_orig = np;
3066	rc_node_hold_locked(np);		/* simplifies the remainder */
3067
3068	for (;;) {
3069		if (!rc_node_wait_flag(np,
3070		    RC_NODE_IN_TX | RC_NODE_USING_PARENT)) {
3071			rc_node_rele_locked(np);
3072			return (REP_PROTOCOL_FAIL_DELETED);
3073		}
3074
3075		if (!(np->rn_flags & RC_NODE_OLD))
3076			break;
3077
3078		rc_node_rele_locked(np);
3079		np = cache_lookup(&np_orig->rn_id);
3080		assert(np != np_orig);
3081
3082		if (np == NULL)
3083			goto deleted;
3084		(void) pthread_mutex_lock(&np->rn_lock);
3085	}
3086
3087	/* guaranteed to succeed without dropping the lock */
3088	if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
3089		(void) pthread_mutex_unlock(&np->rn_lock);
3090		*out = NULL;
3091		rc_node_rele(np);
3092		return (REP_PROTOCOL_FAIL_DELETED);
3093	}
3094
3095	assert(np->rn_parent != NULL);
3096	pnp = np->rn_parent;
3097	(void) pthread_mutex_unlock(&np->rn_lock);
3098
3099	(void) pthread_mutex_lock(&pnp->rn_lock);
3100	(void) pthread_mutex_lock(&np->rn_lock);
3101	rc_node_rele_flag(np, RC_NODE_USING_PARENT);
3102	(void) pthread_mutex_unlock(&np->rn_lock);
3103
3104	rc_node_hold_locked(pnp);
3105
3106	(void) pthread_mutex_unlock(&pnp->rn_lock);
3107
3108	rc_node_rele(np);
3109	*out = pnp;
3110	return (REP_PROTOCOL_SUCCESS);
3111
3112deleted:
3113	rc_node_rele(np);
3114	return (REP_PROTOCOL_FAIL_DELETED);
3115}
3116
3117/*
3118 * Fails with
3119 *   _NOT_SET
3120 *   _DELETED
3121 */
3122static int
3123rc_node_ptr_parent(rc_node_ptr_t *npp, rc_node_t **out)
3124{
3125	rc_node_t *np;
3126
3127	RC_NODE_PTR_GET_CHECK(np, npp);
3128
3129	return (rc_node_parent(np, out));
3130}
3131
3132/*
3133 * Fails with
3134 *   _NOT_SET - npp is not set
3135 *   _DELETED - the node npp pointed at has been deleted
3136 *   _TYPE_MISMATCH - npp's node's parent is not of type type
3137 *
3138 * If npp points to a scope, can also fail with
3139 *   _NOT_FOUND - scope has no parent
3140 */
3141int
3142rc_node_get_parent(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out)
3143{
3144	rc_node_t *pnp;
3145	int rc;
3146
3147	if (npp->rnp_node != NULL &&
3148	    npp->rnp_node->rn_id.rl_type == REP_PROTOCOL_ENTITY_SCOPE)
3149		return (rc_scope_parent_scope(npp, type, out));
3150
3151	if ((rc = rc_node_ptr_parent(npp, &pnp)) != REP_PROTOCOL_SUCCESS) {
3152		rc_node_clear(out, 0);
3153		return (rc);
3154	}
3155
3156	if (type != pnp->rn_id.rl_type) {
3157		rc_node_rele(pnp);
3158		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
3159	}
3160
3161	rc_node_assign(out, pnp);
3162	rc_node_rele(pnp);
3163
3164	return (REP_PROTOCOL_SUCCESS);
3165}
3166
3167int
3168rc_node_parent_type(rc_node_ptr_t *npp, uint32_t *type_out)
3169{
3170	rc_node_t *pnp;
3171	int rc;
3172
3173	if (npp->rnp_node != NULL &&
3174	    npp->rnp_node->rn_id.rl_type == REP_PROTOCOL_ENTITY_SCOPE) {
3175		*type_out = REP_PROTOCOL_ENTITY_SCOPE;
3176		return (REP_PROTOCOL_SUCCESS);
3177	}
3178
3179	if ((rc = rc_node_ptr_parent(npp, &pnp)) != REP_PROTOCOL_SUCCESS)
3180		return (rc);
3181
3182	*type_out = pnp->rn_id.rl_type;
3183
3184	rc_node_rele(pnp);
3185
3186	return (REP_PROTOCOL_SUCCESS);
3187}
3188
3189/*
3190 * Fails with
3191 *   _INVALID_TYPE - type is invalid
3192 *   _TYPE_MISMATCH - np doesn't carry children of type type
3193 *   _DELETED - np has been deleted
3194 *   _NOT_FOUND - no child with that name/type combo found
3195 *   _NO_RESOURCES
3196 *   _BACKEND_ACCESS
3197 */
3198int
3199rc_node_get_child(rc_node_ptr_t *npp, const char *name, uint32_t type,
3200    rc_node_ptr_t *outp)
3201{
3202	rc_node_t *np, *cp;
3203	rc_node_t *child = NULL;
3204	int ret, idx;
3205
3206	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
3207	if ((ret = rc_check_type_name(type, name)) == REP_PROTOCOL_SUCCESS) {
3208		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
3209			ret = rc_node_find_named_child(np, name, type, &child);
3210		} else {
3211			(void) pthread_mutex_unlock(&np->rn_lock);
3212			ret = REP_PROTOCOL_SUCCESS;
3213			for (idx = 0; idx < COMPOSITION_DEPTH; idx++) {
3214				cp = np->rn_cchain[idx];
3215				if (cp == NULL)
3216					break;
3217				RC_NODE_CHECK_AND_LOCK(cp);
3218				ret = rc_node_find_named_child(cp, name, type,
3219				    &child);
3220				(void) pthread_mutex_unlock(&cp->rn_lock);
3221				/*
3222				 * loop only if we succeeded, but no child of
3223				 * the correct name was found.
3224				 */
3225				if (ret != REP_PROTOCOL_SUCCESS ||
3226				    child != NULL)
3227					break;
3228			}
3229			(void) pthread_mutex_lock(&np->rn_lock);
3230		}
3231	}
3232	(void) pthread_mutex_unlock(&np->rn_lock);
3233
3234	if (ret == REP_PROTOCOL_SUCCESS) {
3235		rc_node_assign(outp, child);
3236		if (child != NULL)
3237			rc_node_rele(child);
3238		else
3239			ret = REP_PROTOCOL_FAIL_NOT_FOUND;
3240	} else {
3241		rc_node_assign(outp, NULL);
3242	}
3243	return (ret);
3244}
3245
3246int
3247rc_node_update(rc_node_ptr_t *npp)
3248{
3249	cache_bucket_t *bp;
3250	rc_node_t *np = npp->rnp_node;
3251	rc_node_t *nnp;
3252	rc_node_t *cpg = NULL;
3253
3254	if (np != NULL &&
3255	    np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
3256		/*
3257		 * If we're updating a composed property group, actually
3258		 * update the top-level property group & return the
3259		 * appropriate value.  But leave *nnp pointing at us.
3260		 */
3261		cpg = np;
3262		np = np->rn_cchain[0];
3263	}
3264
3265	RC_NODE_CHECK(np);
3266
3267	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP &&
3268	    np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT)
3269		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
3270
3271	for (;;) {
3272		bp = cache_hold(np->rn_hash);
3273		nnp = cache_lookup_unlocked(bp, &np->rn_id);
3274		if (nnp == NULL) {
3275			cache_release(bp);
3276			rc_node_clear(npp, 1);
3277			return (REP_PROTOCOL_FAIL_DELETED);
3278		}
3279		/*
3280		 * grab the lock before dropping the cache bucket, so
3281		 * that no one else can sneak in
3282		 */
3283		(void) pthread_mutex_lock(&nnp->rn_lock);
3284		cache_release(bp);
3285
3286		if (!(nnp->rn_flags & RC_NODE_IN_TX) ||
3287		    !rc_node_wait_flag(nnp, RC_NODE_IN_TX))
3288			break;
3289
3290		rc_node_rele_locked(nnp);
3291	}
3292
3293	/*
3294	 * If it is dead, we want to update it so that it will continue to
3295	 * report being dead.
3296	 */
3297	if (nnp->rn_flags & RC_NODE_DEAD) {
3298		(void) pthread_mutex_unlock(&nnp->rn_lock);
3299		if (nnp != np && cpg == NULL)
3300			rc_node_assign(npp, nnp);	/* updated */
3301		rc_node_rele(nnp);
3302		return (REP_PROTOCOL_FAIL_DELETED);
3303	}
3304
3305	assert(!(nnp->rn_flags & RC_NODE_OLD));
3306	(void) pthread_mutex_unlock(&nnp->rn_lock);
3307
3308	if (nnp != np && cpg == NULL)
3309		rc_node_assign(npp, nnp);		/* updated */
3310
3311	rc_node_rele(nnp);
3312
3313	return ((nnp == np)? REP_PROTOCOL_SUCCESS : REP_PROTOCOL_DONE);
3314}
3315
3316/*
3317 * does a generic modification check, for creation, deletion, and snapshot
3318 * management only.  Property group transactions have different checks.
3319 *
3320 * The string returned to *match_auth must be freed.
3321 */
3322int
3323rc_node_modify_permission_check(char **match_auth)
3324{
3325	int rc = REP_PROTOCOL_SUCCESS;
3326	permcheck_t *pcp;
3327	int granted;
3328
3329	*match_auth = NULL;
3330#ifdef NATIVE_BUILD
3331	if (!client_is_privileged()) {
3332		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
3333	}
3334	return (rc);
3335#else
3336	if (is_main_repository == 0)
3337		return (REP_PROTOCOL_SUCCESS);
3338	pcp = pc_create();
3339	if (pcp != NULL) {
3340		rc = perm_add_enabling(pcp, AUTH_MODIFY);
3341
3342		if (rc == REP_PROTOCOL_SUCCESS) {
3343			granted = perm_granted(pcp);
3344
3345			if (granted < 0) {
3346				rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
3347			} else {
3348				/*
3349				 * Copy off the authorization
3350				 * string before freeing pcp.
3351				 */
3352				*match_auth =
3353				    strdup(pcp->pc_auth_string);
3354				if (*match_auth == NULL)
3355					rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
3356			}
3357		}
3358
3359		pc_free(pcp);
3360	} else {
3361		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
3362	}
3363
3364	if (rc == REP_PROTOCOL_SUCCESS && !granted)
3365		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
3366
3367	return (rc);
3368#endif /* NATIVE_BUILD */
3369}
3370
3371/*
3372 * Native builds are done to create svc.configd-native.  This program runs
3373 * only on the Solaris build machines to create the seed repository, and it
3374 * is compiled against the build machine's header files.  The ADT_smf_*
3375 * symbols may not be defined in these header files.  For this reason
3376 * smf_annotation_event(), _smf_audit_event() and special_property_event()
3377 * are not compiled for native builds.
3378 */
3379#ifndef	NATIVE_BUILD
3380
3381/*
3382 * This function generates an annotation audit event if one has been setup.
3383 * Annotation events should only be generated immediately before the audit
3384 * record from the first attempt to modify the repository from a client
3385 * which has requested an annotation.
3386 */
3387static void
3388smf_annotation_event(int status, int return_val)
3389{
3390	adt_session_data_t *session;
3391	adt_event_data_t *event = NULL;
3392	char file[MAXPATHLEN];
3393	char operation[REP_PROTOCOL_NAME_LEN];
3394
3395	/* Don't audit if we're using an alternate repository. */
3396	if (is_main_repository == 0)
3397		return;
3398
3399	if (client_annotation_needed(operation, sizeof (operation), file,
3400	    sizeof (file)) == 0) {
3401		return;
3402	}
3403	if (file[0] == 0) {
3404		(void) strlcpy(file, "NO FILE", sizeof (file));
3405	}
3406	if (operation[0] == 0) {
3407		(void) strlcpy(operation, "NO OPERATION",
3408		    sizeof (operation));
3409	}
3410	if ((session = get_audit_session()) == NULL)
3411		return;
3412	if ((event = adt_alloc_event(session, ADT_smf_annotation)) == NULL) {
3413		uu_warn("smf_annotation_event cannot allocate event "
3414		    "data.  %s\n", strerror(errno));
3415		return;
3416	}
3417	event->adt_smf_annotation.operation = operation;
3418	event->adt_smf_annotation.file = file;
3419	if (adt_put_event(event, status, return_val) == 0) {
3420		client_annotation_finished();
3421	} else {
3422		uu_warn("smf_annotation_event failed to put event.  "
3423		    "%s\n", strerror(errno));
3424	}
3425	adt_free_event(event);
3426}
3427
3428/*
3429 * _smf_audit_event interacts with the security auditing system to generate
3430 * an audit event structure.  It establishes an audit session and allocates
3431 * an audit event.  The event is filled in from the audit data, and
3432 * adt_put_event is called to generate the event.
3433 */
3434static void
3435_smf_audit_event(au_event_t event_id, int status, int return_val,
3436    audit_event_data_t *data)
3437{
3438	char *auth_used;
3439	char *fmri;
3440	char *prop_value;
3441	adt_session_data_t *session;
3442	adt_event_data_t *event = NULL;
3443
3444	/* Don't audit if we're using an alternate repository */
3445	if (is_main_repository == 0)
3446		return;
3447
3448	smf_annotation_event(status, return_val);
3449	if ((session = get_audit_session()) == NULL)
3450		return;
3451	if ((event = adt_alloc_event(session, event_id)) == NULL) {
3452		uu_warn("_smf_audit_event cannot allocate event "
3453		    "data.  %s\n", strerror(errno));
3454		return;
3455	}
3456
3457	/*
3458	 * Handle possibility of NULL authorization strings, FMRIs and
3459	 * property values.
3460	 */
3461	if (data->ed_auth == NULL) {
3462		auth_used = "PRIVILEGED";
3463	} else {
3464		auth_used = data->ed_auth;
3465	}
3466	if (data->ed_fmri == NULL) {
3467		syslog(LOG_WARNING, "_smf_audit_event called with "
3468		    "empty FMRI string");
3469		fmri = "UNKNOWN FMRI";
3470	} else {
3471		fmri = data->ed_fmri;
3472	}
3473	if (data->ed_prop_value == NULL) {
3474		prop_value = "";
3475	} else {
3476		prop_value = data->ed_prop_value;
3477	}
3478
3479	/* Fill in the event data. */
3480	switch (event_id) {
3481	case ADT_smf_attach_snap:
3482		event->adt_smf_attach_snap.auth_used = auth_used;
3483		event->adt_smf_attach_snap.old_fmri = data->ed_old_fmri;
3484		event->adt_smf_attach_snap.old_name = data->ed_old_name;
3485		event->adt_smf_attach_snap.new_fmri = fmri;
3486		event->adt_smf_attach_snap.new_name = data->ed_snapname;
3487		break;
3488	case ADT_smf_change_prop:
3489		event->adt_smf_change_prop.auth_used = auth_used;
3490		event->adt_smf_change_prop.fmri = fmri;
3491		event->adt_smf_change_prop.type = data->ed_type;
3492		event->adt_smf_change_prop.value = prop_value;
3493		break;
3494	case ADT_smf_clear:
3495		event->adt_smf_clear.auth_used = auth_used;
3496		event->adt_smf_clear.fmri = fmri;
3497		break;
3498	case ADT_smf_create:
3499		event->adt_smf_create.fmri = fmri;
3500		event->adt_smf_create.auth_used = auth_used;
3501		break;
3502	case ADT_smf_create_npg:
3503		event->adt_smf_create_npg.auth_used = auth_used;
3504		event->adt_smf_create_npg.fmri = fmri;
3505		event->adt_smf_create_npg.type = data->ed_type;
3506		break;
3507	case ADT_smf_create_pg:
3508		event->adt_smf_create_pg.auth_used = auth_used;
3509		event->adt_smf_create_pg.fmri = fmri;
3510		event->adt_smf_create_pg.type = data->ed_type;
3511		break;
3512	case ADT_smf_create_prop:
3513		event->adt_smf_create_prop.auth_used = auth_used;
3514		event->adt_smf_create_prop.fmri = fmri;
3515		event->adt_smf_create_prop.type = data->ed_type;
3516		event->adt_smf_create_prop.value = prop_value;
3517		break;
3518	case ADT_smf_create_snap:
3519		event->adt_smf_create_snap.auth_used = auth_used;
3520		event->adt_smf_create_snap.fmri = fmri;
3521		event->adt_smf_create_snap.name = data->ed_snapname;
3522		break;
3523	case ADT_smf_degrade:
3524		event->adt_smf_degrade.auth_used = auth_used;
3525		event->adt_smf_degrade.fmri = fmri;
3526		break;
3527	case ADT_smf_delete:
3528		event->adt_smf_delete.fmri = fmri;
3529		event->adt_smf_delete.auth_used = auth_used;
3530		break;
3531	case ADT_smf_delete_npg:
3532		event->adt_smf_delete_npg.auth_used = auth_used;
3533		event->adt_smf_delete_npg.fmri = fmri;
3534		event->adt_smf_delete_npg.type = data->ed_type;
3535		break;
3536	case ADT_smf_delete_pg:
3537		event->adt_smf_delete_pg.auth_used = auth_used;
3538		event->adt_smf_delete_pg.fmri = fmri;
3539		event->adt_smf_delete_pg.type = data->ed_type;
3540		break;
3541	case ADT_smf_delete_prop:
3542		event->adt_smf_delete_prop.auth_used = auth_used;
3543		event->adt_smf_delete_prop.fmri = fmri;
3544		break;
3545	case ADT_smf_delete_snap:
3546		event->adt_smf_delete_snap.auth_used = auth_used;
3547		event->adt_smf_delete_snap.fmri = fmri;
3548		event->adt_smf_delete_snap.name = data->ed_snapname;
3549		break;
3550	case ADT_smf_disable:
3551		event->adt_smf_disable.auth_used = auth_used;
3552		event->adt_smf_disable.fmri = fmri;
3553		break;
3554	case ADT_smf_enable:
3555		event->adt_smf_enable.auth_used = auth_used;
3556		event->adt_smf_enable.fmri = fmri;
3557		break;
3558	case ADT_smf_immediate_degrade:
3559		event->adt_smf_immediate_degrade.auth_used = auth_used;
3560		event->adt_smf_immediate_degrade.fmri = fmri;
3561		break;
3562	case ADT_smf_immediate_maintenance:
3563		event->adt_smf_immediate_maintenance.auth_used = auth_used;
3564		event->adt_smf_immediate_maintenance.fmri = fmri;
3565		break;
3566	case ADT_smf_immtmp_maintenance:
3567		event->adt_smf_immtmp_maintenance.auth_used = auth_used;
3568		event->adt_smf_immtmp_maintenance.fmri = fmri;
3569		break;
3570	case ADT_smf_maintenance:
3571		event->adt_smf_maintenance.auth_used = auth_used;
3572		event->adt_smf_maintenance.fmri = fmri;
3573		break;
3574	case ADT_smf_milestone:
3575		event->adt_smf_milestone.auth_used = auth_used;
3576		event->adt_smf_milestone.fmri = fmri;
3577		break;
3578	case ADT_smf_read_prop:
3579		event->adt_smf_read_prop.auth_used = auth_used;
3580		event->adt_smf_read_prop.fmri = fmri;
3581		break;
3582	case ADT_smf_refresh:
3583		event->adt_smf_refresh.auth_used = auth_used;
3584		event->adt_smf_refresh.fmri = fmri;
3585		break;
3586	case ADT_smf_restart:
3587		event->adt_smf_restart.auth_used = auth_used;
3588		event->adt_smf_restart.fmri = fmri;
3589		break;
3590	case ADT_smf_tmp_disable:
3591		event->adt_smf_tmp_disable.auth_used = auth_used;
3592		event->adt_smf_tmp_disable.fmri = fmri;
3593		break;
3594	case ADT_smf_tmp_enable:
3595		event->adt_smf_tmp_enable.auth_used = auth_used;
3596		event->adt_smf_tmp_enable.fmri = fmri;
3597		break;
3598	case ADT_smf_tmp_maintenance:
3599		event->adt_smf_tmp_maintenance.auth_used = auth_used;
3600		event->adt_smf_tmp_maintenance.fmri = fmri;
3601		break;
3602	default:
3603		abort();	/* Need to cover all SMF event IDs */
3604	}
3605
3606	if (adt_put_event(event, status, return_val) != 0) {
3607		uu_warn("_smf_audit_event failed to put event.  %s\n",
3608		    strerror(errno));
3609	}
3610	adt_free_event(event);
3611}
3612
3613/*
3614 * Determine if the combination of the property group at pg_name and the
3615 * property at prop_name are in the set of special startd properties.  If
3616 * they are, a special audit event will be generated.
3617 */
3618static void
3619special_property_event(audit_event_data_t *evdp, const char *prop_name,
3620    char *pg_name, int status, int return_val, tx_commit_data_t *tx_data,
3621    size_t cmd_no)
3622{
3623	au_event_t event_id;
3624	audit_special_prop_item_t search_key;
3625	audit_special_prop_item_t *found;
3626
3627	/* Use bsearch to find the special property information. */
3628	search_key.api_prop_name = prop_name;
3629	search_key.api_pg_name = pg_name;
3630	found = (audit_special_prop_item_t *)bsearch(&search_key,
3631	    special_props_list, SPECIAL_PROP_COUNT,
3632	    sizeof (special_props_list[0]), special_prop_compare);
3633	if (found == NULL) {
3634		/* Not a special property. */
3635		return;
3636	}
3637
3638	/* Get the event id */
3639	if (found->api_event_func == NULL) {
3640		event_id = found->api_event_id;
3641	} else {
3642		if ((*found->api_event_func)(tx_data, cmd_no,
3643		    found->api_pg_name, &event_id) < 0)
3644			return;
3645	}
3646
3647	/* Generate the event. */
3648	smf_audit_event(event_id, status, return_val, evdp);
3649}
3650#endif	/* NATIVE_BUILD */
3651
3652/*
3653 * Return a pointer to a string containing all the values of the command
3654 * specified by cmd_no with each value enclosed in quotes.  It is up to the
3655 * caller to free the memory at the returned pointer.
3656 */
3657static char *
3658generate_value_list(tx_commit_data_t *tx_data, size_t cmd_no)
3659{
3660	const char *cp;
3661	const char *cur_value;
3662	size_t byte_count = 0;
3663	uint32_t i;
3664	uint32_t nvalues;
3665	size_t str_size = 0;
3666	char *values = NULL;
3667	char *vp;
3668
3669	if (tx_cmd_nvalues(tx_data, cmd_no, &nvalues) != REP_PROTOCOL_SUCCESS)
3670		return (NULL);
3671	/*
3672	 * First determine the size of the buffer that we will need.  We
3673	 * will represent each property value surrounded by quotes with a
3674	 * space separating the values.  Thus, we need to find the total
3675	 * size of all the value strings and add 3 for each value.
3676	 *
3677	 * There is one catch, though.  We need to escape any internal
3678	 * quote marks in the values.  So for each quote in the value we
3679	 * need to add another byte to the buffer size.
3680	 */
3681	for (i = 0; i < nvalues; i++) {
3682		if (tx_cmd_value(tx_data, cmd_no, i, &cur_value) !=
3683		    REP_PROTOCOL_SUCCESS)
3684			return (NULL);
3685		for (cp = cur_value; *cp != 0; cp++) {
3686			byte_count += (*cp == '"') ? 2 : 1;
3687		}
3688		byte_count += 3;	/* surrounding quotes & space */
3689	}
3690	byte_count++;		/* nul terminator */
3691	values = malloc(byte_count);
3692	if (values == NULL)
3693		return (NULL);
3694	*values = 0;
3695
3696	/* Now build up the string of values. */
3697	for (i = 0; i < nvalues; i++) {
3698		if (tx_cmd_value(tx_data, cmd_no, i, &cur_value) !=
3699		    REP_PROTOCOL_SUCCESS) {
3700			free(values);
3701			return (NULL);
3702		}
3703		(void) strlcat(values, "\"", byte_count);
3704		for (cp = cur_value, vp = values + strlen(values);
3705		    *cp != 0; cp++) {
3706			if (*cp == '"') {
3707				*vp++ = '\\';
3708				*vp++ = '"';
3709			} else {
3710				*vp++ = *cp;
3711			}
3712		}
3713		*vp = 0;
3714		str_size = strlcat(values, "\" ", byte_count);
3715		assert(str_size < byte_count);
3716	}
3717	if (str_size > 0)
3718		values[str_size - 1] = 0;	/* get rid of trailing space */
3719	return (values);
3720}
3721
3722/*
3723 * generate_property_events takes the transaction commit data at tx_data
3724 * and generates an audit event for each command.
3725 *
3726 * Native builds are done to create svc.configd-native.  This program runs
3727 * only on the Solaris build machines to create the seed repository.  Thus,
3728 * no audit events should be generated when running svc.configd-native.
3729 */
3730static void
3731generate_property_events(
3732	tx_commit_data_t *tx_data,
3733	char *pg_fmri,		/* FMRI of property group */
3734	char *auth_string,
3735	int auth_status,
3736	int auth_ret_value)
3737{
3738#ifndef	NATIVE_BUILD
3739	enum rep_protocol_transaction_action action;
3740	audit_event_data_t audit_data;
3741	size_t count;
3742	size_t cmd_no;
3743	char *cp;
3744	au_event_t event_id;
3745	char fmri[REP_PROTOCOL_FMRI_LEN];
3746	char pg_name[REP_PROTOCOL_NAME_LEN];
3747	char *pg_end;		/* End of prop. group fmri */
3748	const char *prop_name;
3749	uint32_t ptype;
3750	char prop_type[3];
3751	enum rep_protocol_responseid rc;
3752	size_t sz_out;
3753
3754	/* Make sure we have something to do. */
3755	if (tx_data == NULL)
3756		return;
3757	if ((count = tx_cmd_count(tx_data)) == 0)
3758		return;
3759
3760	/* Copy the property group fmri */
3761	pg_end = fmri;
3762	pg_end += strlcpy(fmri, pg_fmri, sizeof (fmri));
3763
3764	/*
3765	 * Get the property group name.  It is the first component after
3766	 * the last occurance of SCF_FMRI_PROPERTYGRP_PREFIX in the fmri.
3767	 */
3768	cp = strstr(pg_fmri, SCF_FMRI_PROPERTYGRP_PREFIX);
3769	if (cp == NULL) {
3770		pg_name[0] = 0;
3771	} else {
3772		cp += strlen(SCF_FMRI_PROPERTYGRP_PREFIX);
3773		(void) strlcpy(pg_name, cp, sizeof (pg_name));
3774	}
3775
3776	audit_data.ed_auth = auth_string;
3777	audit_data.ed_fmri = fmri;
3778	audit_data.ed_type = prop_type;
3779
3780	/*
3781	 * Property type is two characters (see
3782	 * rep_protocol_value_type_t), so terminate the string.
3783	 */
3784	prop_type[2] = 0;
3785
3786	for (cmd_no = 0; cmd_no < count; cmd_no++) {
3787		/* Construct FMRI of the property */
3788		*pg_end = 0;
3789		if (tx_cmd_prop(tx_data, cmd_no, &prop_name) !=
3790		    REP_PROTOCOL_SUCCESS) {
3791			continue;
3792		}
3793		rc = rc_concat_fmri_element(fmri, sizeof (fmri), &sz_out,
3794		    prop_name, REP_PROTOCOL_ENTITY_PROPERTY);
3795		if (rc != REP_PROTOCOL_SUCCESS) {
3796			/*
3797			 * If we can't get the FMRI, we'll abandon this
3798			 * command
3799			 */
3800			continue;
3801		}
3802
3803		/* Generate special property event if necessary. */
3804		special_property_event(&audit_data, prop_name, pg_name,
3805		    auth_status, auth_ret_value, tx_data, cmd_no);
3806
3807		/* Capture rest of audit data. */
3808		if (tx_cmd_prop_type(tx_data, cmd_no, &ptype) !=
3809		    REP_PROTOCOL_SUCCESS) {
3810			continue;
3811		}
3812		prop_type[0] = REP_PROTOCOL_BASE_TYPE(ptype);
3813		prop_type[1] = REP_PROTOCOL_SUBTYPE(ptype);
3814		audit_data.ed_prop_value = generate_value_list(tx_data, cmd_no);
3815
3816		/* Determine the event type. */
3817		if (tx_cmd_action(tx_data, cmd_no, &action) !=
3818		    REP_PROTOCOL_SUCCESS) {
3819			free(audit_data.ed_prop_value);
3820			continue;
3821		}
3822		switch (action) {
3823		case REP_PROTOCOL_TX_ENTRY_NEW:
3824			event_id = ADT_smf_create_prop;
3825			break;
3826		case REP_PROTOCOL_TX_ENTRY_CLEAR:
3827			event_id = ADT_smf_change_prop;
3828			break;
3829		case REP_PROTOCOL_TX_ENTRY_REPLACE:
3830			event_id = ADT_smf_change_prop;
3831			break;
3832		case REP_PROTOCOL_TX_ENTRY_DELETE:
3833			event_id = ADT_smf_delete_prop;
3834			break;
3835		default:
3836			assert(0);	/* Missing a case */
3837			free(audit_data.ed_prop_value);
3838			continue;
3839		}
3840
3841		/* Generate the event. */
3842		smf_audit_event(event_id, auth_status, auth_ret_value,
3843		    &audit_data);
3844		free(audit_data.ed_prop_value);
3845	}
3846#endif /* NATIVE_BUILD */
3847}
3848
3849/*
3850 * Fails with
3851 *   _DELETED - node has been deleted
3852 *   _NOT_SET - npp is reset
3853 *   _NOT_APPLICABLE - type is _PROPERTYGRP
3854 *   _INVALID_TYPE - node is corrupt or type is invalid
3855 *   _TYPE_MISMATCH - node cannot have children of type type
3856 *   _BAD_REQUEST - name is invalid
3857 *		    cannot create children for this type of node
3858 *   _NO_RESOURCES - out of memory, or could not allocate new id
3859 *   _PERMISSION_DENIED
3860 *   _BACKEND_ACCESS
3861 *   _BACKEND_READONLY
3862 *   _EXISTS - child already exists
3863 *   _TRUNCATED - truncated FMRI for the audit record
3864 */
3865int
3866rc_node_create_child(rc_node_ptr_t *npp, uint32_t type, const char *name,
3867    rc_node_ptr_t *cpp)
3868{
3869	rc_node_t *np;
3870	rc_node_t *cp = NULL;
3871	int rc,  perm_rc;
3872	size_t sz_out;
3873	char fmri[REP_PROTOCOL_FMRI_LEN];
3874	audit_event_data_t audit_data;
3875
3876	rc_node_clear(cpp, 0);
3877
3878	perm_rc = rc_node_modify_permission_check(&audit_data.ed_auth);
3879
3880	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
3881
3882	audit_data.ed_fmri = fmri;
3883	audit_data.ed_auth = NULL;
3884
3885	/*
3886	 * there is a separate interface for creating property groups
3887	 */
3888	if (type == REP_PROTOCOL_ENTITY_PROPERTYGRP) {
3889		(void) pthread_mutex_unlock(&np->rn_lock);
3890		free(audit_data.ed_auth);
3891		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
3892	}
3893
3894	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
3895		(void) pthread_mutex_unlock(&np->rn_lock);
3896		np = np->rn_cchain[0];
3897		RC_NODE_CHECK_AND_LOCK(np);
3898	}
3899
3900	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
3901	    REP_PROTOCOL_SUCCESS) {
3902		(void) pthread_mutex_unlock(&np->rn_lock);
3903		free(audit_data.ed_auth);
3904		return (rc);
3905	}
3906	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS) {
3907		(void) pthread_mutex_unlock(&np->rn_lock);
3908		free(audit_data.ed_auth);
3909		return (rc);
3910	}
3911
3912	if ((rc = rc_get_fmri_and_concat(np, fmri, sizeof (fmri), &sz_out,
3913	    name, type)) != REP_PROTOCOL_SUCCESS) {
3914		(void) pthread_mutex_unlock(&np->rn_lock);
3915		free(audit_data.ed_auth);
3916		return (rc);
3917	}
3918	if (perm_rc != REP_PROTOCOL_SUCCESS) {
3919		(void) pthread_mutex_unlock(&np->rn_lock);
3920		smf_audit_event(ADT_smf_create, ADT_FAILURE,
3921		    ADT_FAIL_VALUE_AUTH, &audit_data);
3922		free(audit_data.ed_auth);
3923		return (perm_rc);
3924	}
3925
3926	HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
3927	    audit_data.ed_auth);
3928	(void) pthread_mutex_unlock(&np->rn_lock);
3929
3930	rc = object_create(np, type, name, &cp);
3931	assert(rc != REP_PROTOCOL_FAIL_NOT_APPLICABLE);
3932
3933	if (rc == REP_PROTOCOL_SUCCESS) {
3934		rc_node_assign(cpp, cp);
3935		rc_node_rele(cp);
3936	}
3937
3938	(void) pthread_mutex_lock(&np->rn_lock);
3939	rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
3940	(void) pthread_mutex_unlock(&np->rn_lock);
3941
3942	if (rc == REP_PROTOCOL_SUCCESS) {
3943		smf_audit_event(ADT_smf_create, ADT_SUCCESS, ADT_SUCCESS,
3944		    &audit_data);
3945	}
3946
3947	free(audit_data.ed_auth);
3948
3949	return (rc);
3950}
3951
3952int
3953rc_node_create_child_pg(rc_node_ptr_t *npp, uint32_t type, const char *name,
3954    const char *pgtype, uint32_t flags, rc_node_ptr_t *cpp)
3955{
3956	rc_node_t *np;
3957	rc_node_t *cp;
3958	int rc;
3959	permcheck_t *pcp;
3960	int granted;
3961	char fmri[REP_PROTOCOL_FMRI_LEN];
3962	audit_event_data_t audit_data;
3963	au_event_t event_id;
3964	size_t sz_out;
3965
3966	audit_data.ed_auth = NULL;
3967	audit_data.ed_fmri = fmri;
3968	audit_data.ed_type = (char *)pgtype;
3969
3970	rc_node_clear(cpp, 0);
3971
3972	/* verify flags is valid */
3973	if (flags & ~SCF_PG_FLAG_NONPERSISTENT)
3974		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
3975
3976	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
3977
3978	if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
3979		rc_node_rele(np);
3980		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
3981	}
3982
3983	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
3984	    REP_PROTOCOL_SUCCESS) {
3985		rc_node_rele(np);
3986		return (rc);
3987	}
3988	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS ||
3989	    (rc = rc_check_pgtype_name(pgtype)) != REP_PROTOCOL_SUCCESS) {
3990		rc_node_rele(np);
3991		return (rc);
3992	}
3993
3994#ifdef NATIVE_BUILD
3995	if (!client_is_privileged()) {
3996		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
3997	}
3998#else
3999	if (flags & SCF_PG_FLAG_NONPERSISTENT) {
4000		event_id = ADT_smf_create_npg;
4001	} else {
4002		event_id = ADT_smf_create_pg;
4003	}
4004	if ((rc = rc_get_fmri_and_concat(np, fmri, sizeof (fmri), &sz_out,
4005	    name, REP_PROTOCOL_ENTITY_PROPERTYGRP)) != REP_PROTOCOL_SUCCESS) {
4006		rc_node_rele(np);
4007		return (rc);
4008	}
4009
4010	if (is_main_repository) {
4011		/* Must have .smf.modify or smf.modify.<type> authorization */
4012		pcp = pc_create();
4013		if (pcp != NULL) {
4014			rc = perm_add_enabling(pcp, AUTH_MODIFY);
4015
4016			if (rc == REP_PROTOCOL_SUCCESS) {
4017				const char * const auth =
4018				    perm_auth_for_pgtype(pgtype);
4019
4020				if (auth != NULL)
4021					rc = perm_add_enabling(pcp, auth);
4022			}
4023
4024			/*
4025			 * .manage or $action_authorization can be used to
4026			 * create the actions pg and the general_ovr pg.
4027			 */
4028			if (rc == REP_PROTOCOL_SUCCESS &&
4029			    (flags & SCF_PG_FLAG_NONPERSISTENT) != 0 &&
4030			    np->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE &&
4031			    ((strcmp(name, AUTH_PG_ACTIONS) == 0 &&
4032			    strcmp(pgtype, AUTH_PG_ACTIONS_TYPE) == 0) ||
4033			    (strcmp(name, AUTH_PG_GENERAL_OVR) == 0 &&
4034			    strcmp(pgtype, AUTH_PG_GENERAL_OVR_TYPE) == 0))) {
4035				rc = perm_add_enabling(pcp, AUTH_MANAGE);
4036
4037				if (rc == REP_PROTOCOL_SUCCESS)
4038					rc = perm_add_inst_action_auth(pcp, np);
4039			}
4040
4041			if (rc == REP_PROTOCOL_SUCCESS) {
4042				granted = perm_granted(pcp);
4043
4044				if (granted < 0) {
4045					rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
4046				} else {
4047					/*
4048					 * Copy out the authorization
4049					 * string before freeing pcp.
4050					 */
4051					audit_data.ed_auth =
4052					    strdup(pcp->pc_auth_string);
4053					if (audit_data.ed_auth == NULL) {
4054						/*
4055						 * Following code line
4056						 * cannot meet both the
4057						 * indentation and the line
4058						 * length requirements of
4059						 * cstyle.  Indendation has
4060						 * been sacrificed.
4061						 */
4062						/* CSTYLED */
4063					    rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
4064					}
4065				}
4066			}
4067
4068			pc_free(pcp);
4069		} else {
4070			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
4071		}
4072
4073		if (rc == REP_PROTOCOL_SUCCESS && !granted)
4074			rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
4075	} else {
4076		rc = REP_PROTOCOL_SUCCESS;
4077	}
4078#endif /* NATIVE_BUILD */
4079
4080	if (rc != REP_PROTOCOL_SUCCESS) {
4081		rc_node_rele(np);
4082		smf_audit_event(event_id, ADT_FAILURE,
4083		    ADT_FAIL_VALUE_AUTH, &audit_data);
4084		if (audit_data.ed_auth != NULL)
4085			free(audit_data.ed_auth);
4086		return (rc);
4087	}
4088
4089	(void) pthread_mutex_lock(&np->rn_lock);
4090	HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
4091	    audit_data.ed_auth);
4092	(void) pthread_mutex_unlock(&np->rn_lock);
4093
4094	rc = object_create_pg(np, type, name, pgtype, flags, &cp);
4095
4096	if (rc == REP_PROTOCOL_SUCCESS) {
4097		rc_node_assign(cpp, cp);
4098		rc_node_rele(cp);
4099	}
4100
4101	(void) pthread_mutex_lock(&np->rn_lock);
4102	rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
4103	(void) pthread_mutex_unlock(&np->rn_lock);
4104
4105	if (rc == REP_PROTOCOL_SUCCESS) {
4106		smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS,
4107		    &audit_data);
4108	}
4109	if (audit_data.ed_auth != NULL)
4110		free(audit_data.ed_auth);
4111
4112	return (rc);
4113}
4114
4115static void
4116rc_pg_notify_fire(rc_node_pg_notify_t *pnp)
4117{
4118	assert(MUTEX_HELD(&rc_pg_notify_lock));
4119
4120	if (pnp->rnpn_pg != NULL) {
4121		uu_list_remove(pnp->rnpn_pg->rn_pg_notify_list, pnp);
4122		(void) close(pnp->rnpn_fd);
4123
4124		pnp->rnpn_pg = NULL;
4125		pnp->rnpn_fd = -1;
4126	} else {
4127		assert(pnp->rnpn_fd == -1);
4128	}
4129}
4130
4131static void
4132rc_notify_node_delete(rc_notify_delete_t *ndp, rc_node_t *np_arg)
4133{
4134	rc_node_t *svc = NULL;
4135	rc_node_t *inst = NULL;
4136	rc_node_t *pg = NULL;
4137	rc_node_t *np = np_arg;
4138	rc_node_t *nnp;
4139
4140	while (svc == NULL) {
4141		(void) pthread_mutex_lock(&np->rn_lock);
4142		if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
4143			(void) pthread_mutex_unlock(&np->rn_lock);
4144			goto cleanup;
4145		}
4146		nnp = np->rn_parent;
4147		rc_node_hold_locked(np);	/* hold it in place */
4148
4149		switch (np->rn_id.rl_type) {
4150		case REP_PROTOCOL_ENTITY_PROPERTYGRP:
4151			assert(pg == NULL);
4152			pg = np;
4153			break;
4154		case REP_PROTOCOL_ENTITY_INSTANCE:
4155			assert(inst == NULL);
4156			inst = np;
4157			break;
4158		case REP_PROTOCOL_ENTITY_SERVICE:
4159			assert(svc == NULL);
4160			svc = np;
4161			break;
4162		default:
4163			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
4164			rc_node_rele_locked(np);
4165			goto cleanup;
4166		}
4167
4168		(void) pthread_mutex_unlock(&np->rn_lock);
4169
4170		np = nnp;
4171		if (np == NULL)
4172			goto cleanup;
4173	}
4174
4175	rc_notify_deletion(ndp,
4176	    svc->rn_name,
4177	    inst != NULL ? inst->rn_name : NULL,
4178	    pg != NULL ? pg->rn_name : NULL);
4179
4180	ndp = NULL;
4181
4182cleanup:
4183	if (ndp != NULL)
4184		uu_free(ndp);
4185
4186	for (;;) {
4187		if (svc != NULL) {
4188			np = svc;
4189			svc = NULL;
4190		} else if (inst != NULL) {
4191			np = inst;
4192			inst = NULL;
4193		} else if (pg != NULL) {
4194			np = pg;
4195			pg = NULL;
4196		} else
4197			break;
4198
4199		(void) pthread_mutex_lock(&np->rn_lock);
4200		rc_node_rele_flag(np, RC_NODE_USING_PARENT);
4201		rc_node_rele_locked(np);
4202	}
4203}
4204
4205/*
4206 * N.B.:  this function drops np->rn_lock on the way out.
4207 */
4208static void
4209rc_node_delete_hold(rc_node_t *np, int andformer)
4210{
4211	rc_node_t *cp;
4212
4213again:
4214	assert(MUTEX_HELD(&np->rn_lock));
4215	assert((np->rn_flags & RC_NODE_DYING_FLAGS) == RC_NODE_DYING_FLAGS);
4216
4217	for (cp = uu_list_first(np->rn_children); cp != NULL;
4218	    cp = uu_list_next(np->rn_children, cp)) {
4219		(void) pthread_mutex_lock(&cp->rn_lock);
4220		(void) pthread_mutex_unlock(&np->rn_lock);
4221		if (!rc_node_hold_flag(cp, RC_NODE_DYING_FLAGS)) {
4222			/*
4223			 * already marked as dead -- can't happen, since that
4224			 * would require setting RC_NODE_CHILDREN_CHANGING
4225			 * in np, and we're holding that...
4226			 */
4227			abort();
4228		}
4229		rc_node_delete_hold(cp, andformer);	/* recurse, drop lock */
4230
4231		(void) pthread_mutex_lock(&np->rn_lock);
4232	}
4233	if (andformer && (cp = np->rn_former) != NULL) {
4234		(void) pthread_mutex_lock(&cp->rn_lock);
4235		(void) pthread_mutex_unlock(&np->rn_lock);
4236		if (!rc_node_hold_flag(cp, RC_NODE_DYING_FLAGS))
4237			abort();		/* can't happen, see above */
4238		np = cp;
4239		goto again;		/* tail-recurse down rn_former */
4240	}
4241	(void) pthread_mutex_unlock(&np->rn_lock);
4242}
4243
4244/*
4245 * N.B.:  this function drops np->rn_lock on the way out.
4246 */
4247static void
4248rc_node_delete_rele(rc_node_t *np, int andformer)
4249{
4250	rc_node_t *cp;
4251
4252again:
4253	assert(MUTEX_HELD(&np->rn_lock));
4254	assert((np->rn_flags & RC_NODE_DYING_FLAGS) == RC_NODE_DYING_FLAGS);
4255
4256	for (cp = uu_list_first(np->rn_children); cp != NULL;
4257	    cp = uu_list_next(np->rn_children, cp)) {
4258		(void) pthread_mutex_lock(&cp->rn_lock);
4259		(void) pthread_mutex_unlock(&np->rn_lock);
4260		rc_node_delete_rele(cp, andformer);	/* recurse, drop lock */
4261		(void) pthread_mutex_lock(&np->rn_lock);
4262	}
4263	if (andformer && (cp = np->rn_former) != NULL) {
4264		(void) pthread_mutex_lock(&cp->rn_lock);
4265		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
4266		(void) pthread_mutex_unlock(&np->rn_lock);
4267
4268		np = cp;
4269		goto again;		/* tail-recurse down rn_former */
4270	}
4271	rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
4272	(void) pthread_mutex_unlock(&np->rn_lock);
4273}
4274
4275static void
4276rc_node_finish_delete(rc_node_t *cp)
4277{
4278	cache_bucket_t *bp;
4279	rc_node_pg_notify_t *pnp;
4280
4281	assert(MUTEX_HELD(&cp->rn_lock));
4282
4283	if (!(cp->rn_flags & RC_NODE_OLD)) {
4284		assert(cp->rn_flags & RC_NODE_IN_PARENT);
4285		if (!rc_node_wait_flag(cp, RC_NODE_USING_PARENT)) {
4286			abort();		/* can't happen, see above */
4287		}
4288		cp->rn_flags &= ~RC_NODE_IN_PARENT;
4289		cp->rn_parent = NULL;
4290		rc_node_free_fmri(cp);
4291	}
4292
4293	cp->rn_flags |= RC_NODE_DEAD;
4294
4295	/*
4296	 * If this node is not out-dated, we need to remove it from
4297	 * the notify list and cache hash table.
4298	 */
4299	if (!(cp->rn_flags & RC_NODE_OLD)) {
4300		assert(cp->rn_refs > 0);	/* can't go away yet */
4301		(void) pthread_mutex_unlock(&cp->rn_lock);
4302
4303		(void) pthread_mutex_lock(&rc_pg_notify_lock);
4304		while ((pnp = uu_list_first(cp->rn_pg_notify_list)) != NULL)
4305			rc_pg_notify_fire(pnp);
4306		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
4307		rc_notify_remove_node(cp);
4308
4309		bp = cache_hold(cp->rn_hash);
4310		(void) pthread_mutex_lock(&cp->rn_lock);
4311		cache_remove_unlocked(bp, cp);
4312		cache_release(bp);
4313	}
4314}
4315
4316/*
4317 * N.B.:  this function drops np->rn_lock and a reference on the way out.
4318 */
4319static void
4320rc_node_delete_children(rc_node_t *np, int andformer)
4321{
4322	rc_node_t *cp;
4323
4324again:
4325	assert(np->rn_refs > 0);
4326	assert(MUTEX_HELD(&np->rn_lock));
4327	assert(np->rn_flags & RC_NODE_DEAD);
4328
4329	while ((cp = uu_list_first(np->rn_children)) != NULL) {
4330		uu_list_remove(np->rn_children, cp);
4331		(void) pthread_mutex_lock(&cp->rn_lock);
4332		(void) pthread_mutex_unlock(&np->rn_lock);
4333		rc_node_hold_locked(cp);	/* hold while we recurse */
4334		rc_node_finish_delete(cp);
4335		rc_node_delete_children(cp, andformer);	/* drops lock + ref */
4336		(void) pthread_mutex_lock(&np->rn_lock);
4337	}
4338
4339	/*
4340	 * when we drop cp's lock, all the children will be gone, so we
4341	 * can release DYING_FLAGS.
4342	 */
4343	rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
4344	if (andformer && (cp = np->rn_former) != NULL) {
4345		np->rn_former = NULL;		/* unlink */
4346		(void) pthread_mutex_lock(&cp->rn_lock);
4347		(void) pthread_mutex_unlock(&np->rn_lock);
4348		np->rn_flags &= ~RC_NODE_ON_FORMER;
4349
4350		rc_node_hold_locked(cp);	/* hold while we loop */
4351
4352		rc_node_finish_delete(cp);
4353
4354		rc_node_rele(np);		/* drop the old reference */
4355
4356		np = cp;
4357		goto again;		/* tail-recurse down rn_former */
4358	}
4359	rc_node_rele_locked(np);
4360}
4361
4362static void
4363rc_node_unrefed(rc_node_t *np)
4364{
4365	int unrefed;
4366	rc_node_t *pp, *cur;
4367
4368	assert(MUTEX_HELD(&np->rn_lock));
4369	assert(np->rn_refs == 0);
4370	assert(np->rn_other_refs == 0);
4371	assert(np->rn_other_refs_held == 0);
4372
4373	if (np->rn_flags & RC_NODE_DEAD) {
4374		(void) pthread_mutex_unlock(&np->rn_lock);
4375		rc_node_destroy(np);
4376		return;
4377	}
4378
4379	assert(np->rn_flags & RC_NODE_OLD);
4380	if (np->rn_flags & RC_NODE_UNREFED) {
4381		(void) pthread_mutex_unlock(&np->rn_lock);
4382		return;
4383	}
4384	np->rn_flags |= RC_NODE_UNREFED;
4385
4386	(void) pthread_mutex_unlock(&np->rn_lock);
4387
4388	/*
4389	 * find the current in-hash object, and grab it's RC_NODE_IN_TX
4390	 * flag.  That protects the entire rn_former chain.
4391	 */
4392	for (;;) {
4393		pp = cache_lookup(&np->rn_id);
4394		if (pp == NULL) {
4395			(void) pthread_mutex_lock(&np->rn_lock);
4396			if (np->rn_flags & RC_NODE_DEAD)
4397				goto died;
4398			/*
4399			 * We are trying to unreference this node, but the
4400			 * owner of the former list does not exist.  It must
4401			 * be the case that another thread is deleting this
4402			 * entire sub-branch, but has not yet reached us.
4403			 * We will in short order be deleted.
4404			 */
4405			np->rn_flags &= ~RC_NODE_UNREFED;
4406			(void) pthread_mutex_unlock(&np->rn_lock);
4407			return;
4408		}
4409		if (pp == np) {
4410			/*
4411			 * no longer unreferenced
4412			 */
4413			(void) pthread_mutex_lock(&np->rn_lock);
4414			np->rn_flags &= ~RC_NODE_UNREFED;
4415			rc_node_rele_locked(np);
4416			return;
4417		}
4418		(void) pthread_mutex_lock(&pp->rn_lock);
4419		if ((pp->rn_flags & RC_NODE_OLD) ||
4420		    !rc_node_hold_flag(pp, RC_NODE_IN_TX)) {
4421			rc_node_rele_locked(pp);
4422			continue;
4423		}
4424		if (!(pp->rn_flags & RC_NODE_OLD)) {
4425			(void) pthread_mutex_unlock(&pp->rn_lock);
4426			break;
4427		}
4428		rc_node_rele_flag(pp, RC_NODE_IN_TX);
4429		rc_node_rele_locked(pp);
4430	}
4431
4432	(void) pthread_mutex_lock(&np->rn_lock);
4433	if (!(np->rn_flags & (RC_NODE_OLD | RC_NODE_DEAD)) ||
4434	    np->rn_refs != 0 || np->rn_other_refs != 0 ||
4435	    np->rn_other_refs_held != 0) {
4436		np->rn_flags &= ~RC_NODE_UNREFED;
4437		(void) pthread_mutex_lock(&pp->rn_lock);
4438
4439		rc_node_rele_flag(pp, RC_NODE_IN_TX);
4440		rc_node_rele_locked(pp);
4441		return;
4442	}
4443
4444	if (!rc_node_hold_flag(np, RC_NODE_DYING_FLAGS)) {
4445		(void) pthread_mutex_unlock(&np->rn_lock);
4446
4447		rc_node_rele_flag(pp, RC_NODE_IN_TX);
4448		rc_node_rele_locked(pp);
4449
4450		(void) pthread_mutex_lock(&np->rn_lock);
4451		goto died;
4452	}
4453
4454	rc_node_delete_hold(np, 0);
4455
4456	(void) pthread_mutex_lock(&np->rn_lock);
4457	if (!(np->rn_flags & RC_NODE_OLD) ||
4458	    np->rn_refs != 0 || np->rn_other_refs != 0 ||
4459	    np->rn_other_refs_held != 0) {
4460		np->rn_flags &= ~RC_NODE_UNREFED;
4461		rc_node_delete_rele(np, 0);
4462
4463		(void) pthread_mutex_lock(&pp->rn_lock);
4464		rc_node_rele_flag(pp, RC_NODE_IN_TX);
4465		rc_node_rele_locked(pp);
4466		return;
4467	}
4468
4469	np->rn_flags |= RC_NODE_DEAD;
4470	rc_node_hold_locked(np);
4471	rc_node_delete_children(np, 0);
4472
4473	/*
4474	 * It's gone -- remove it from the former chain and destroy it.
4475	 */
4476	(void) pthread_mutex_lock(&pp->rn_lock);
4477	for (cur = pp; cur != NULL && cur->rn_former != np;
4478	    cur = cur->rn_former)
4479		;
4480	assert(cur != NULL && cur != np);
4481
4482	cur->rn_former = np->rn_former;
4483	np->rn_former = NULL;
4484
4485	rc_node_rele_flag(pp, RC_NODE_IN_TX);
4486	rc_node_rele_locked(pp);
4487
4488	(void) pthread_mutex_lock(&np->rn_lock);
4489	assert(np->rn_flags & RC_NODE_ON_FORMER);
4490	np->rn_flags &= ~(RC_NODE_UNREFED | RC_NODE_ON_FORMER);
4491	(void) pthread_mutex_unlock(&np->rn_lock);
4492	rc_node_destroy(np);
4493	return;
4494
4495died:
4496	np->rn_flags &= ~RC_NODE_UNREFED;
4497	unrefed = (np->rn_refs == 0 && np->rn_other_refs == 0 &&
4498	    np->rn_other_refs_held == 0);
4499	(void) pthread_mutex_unlock(&np->rn_lock);
4500	if (unrefed)
4501		rc_node_destroy(np);
4502}
4503
4504static au_event_t
4505get_delete_event_id(rep_protocol_entity_t entity, uint32_t pgflags)
4506{
4507	au_event_t	id = 0;
4508
4509#ifndef NATIVE_BUILD
4510	switch (entity) {
4511	case REP_PROTOCOL_ENTITY_SERVICE:
4512	case REP_PROTOCOL_ENTITY_INSTANCE:
4513		id = ADT_smf_delete;
4514		break;
4515	case REP_PROTOCOL_ENTITY_SNAPSHOT:
4516		id = ADT_smf_delete_snap;
4517		break;
4518	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
4519	case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
4520		if (pgflags & SCF_PG_FLAG_NONPERSISTENT) {
4521			id = ADT_smf_delete_npg;
4522		} else {
4523			id = ADT_smf_delete_pg;
4524		}
4525		break;
4526	default:
4527		abort();
4528	}
4529#endif	/* NATIVE_BUILD */
4530	return (id);
4531}
4532
4533/*
4534 * Fails with
4535 *   _NOT_SET
4536 *   _DELETED
4537 *   _BAD_REQUEST
4538 *   _PERMISSION_DENIED
4539 *   _NO_RESOURCES
4540 *   _TRUNCATED
4541 * and whatever object_delete() fails with.
4542 */
4543int
4544rc_node_delete(rc_node_ptr_t *npp)
4545{
4546	rc_node_t *np, *np_orig;
4547	rc_node_t *pp = NULL;
4548	int rc;
4549	rc_node_pg_notify_t *pnp;
4550	cache_bucket_t *bp;
4551	rc_notify_delete_t *ndp;
4552	permcheck_t *pcp;
4553	int granted;
4554	au_event_t event_id = 0;
4555	size_t sz_out;
4556	audit_event_data_t audit_data;
4557	int audit_failure = 0;
4558
4559	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
4560
4561	audit_data.ed_fmri = NULL;
4562	audit_data.ed_auth = NULL;
4563	audit_data.ed_snapname = NULL;
4564	audit_data.ed_type = NULL;
4565
4566	switch (np->rn_id.rl_type) {
4567	case REP_PROTOCOL_ENTITY_SERVICE:
4568		event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_SERVICE,
4569		    np->rn_pgflags);
4570		break;
4571	case REP_PROTOCOL_ENTITY_INSTANCE:
4572		event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_INSTANCE,
4573		    np->rn_pgflags);
4574		break;
4575	case REP_PROTOCOL_ENTITY_SNAPSHOT:
4576		event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_SNAPSHOT,
4577		    np->rn_pgflags);
4578		audit_data.ed_snapname = strdup(np->rn_name);
4579		if (audit_data.ed_snapname == NULL) {
4580			(void) pthread_mutex_unlock(&np->rn_lock);
4581			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
4582		}
4583		break;			/* deletable */
4584
4585	case REP_PROTOCOL_ENTITY_SCOPE:
4586	case REP_PROTOCOL_ENTITY_SNAPLEVEL:
4587		/* Scopes and snaplevels are indelible. */
4588		(void) pthread_mutex_unlock(&np->rn_lock);
4589		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
4590
4591	case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
4592		(void) pthread_mutex_unlock(&np->rn_lock);
4593		np = np->rn_cchain[0];
4594		RC_NODE_CHECK_AND_LOCK(np);
4595		event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_CPROPERTYGRP,
4596		    np->rn_pgflags);
4597		break;
4598
4599	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
4600		if (np->rn_id.rl_ids[ID_SNAPSHOT] == 0) {
4601			event_id =
4602			    get_delete_event_id(REP_PROTOCOL_ENTITY_PROPERTYGRP,
4603			    np->rn_pgflags);
4604			audit_data.ed_type = strdup(np->rn_type);
4605			if (audit_data.ed_type == NULL) {
4606				(void) pthread_mutex_unlock(&np->rn_lock);
4607				return (REP_PROTOCOL_FAIL_NO_RESOURCES);
4608			}
4609			break;
4610		}
4611
4612		/* Snapshot property groups are indelible. */
4613		(void) pthread_mutex_unlock(&np->rn_lock);
4614		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
4615
4616	case REP_PROTOCOL_ENTITY_PROPERTY:
4617		(void) pthread_mutex_unlock(&np->rn_lock);
4618		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
4619
4620	default:
4621		assert(0);
4622		abort();
4623		break;
4624	}
4625
4626	audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
4627	if (audit_data.ed_fmri == NULL) {
4628		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
4629		goto cleanout;
4630	}
4631	np_orig = np;
4632	rc_node_hold_locked(np);	/* simplifies rest of the code */
4633
4634again:
4635	/*
4636	 * The following loop is to deal with the fact that snapshots and
4637	 * property groups are moving targets -- changes to them result
4638	 * in a new "child" node.  Since we can only delete from the top node,
4639	 * we have to loop until we have a non-RC_NODE_OLD version.
4640	 */
4641	for (;;) {
4642		if (!rc_node_wait_flag(np,
4643		    RC_NODE_IN_TX | RC_NODE_USING_PARENT)) {
4644			rc_node_rele_locked(np);
4645			rc = REP_PROTOCOL_FAIL_DELETED;
4646			goto cleanout;
4647		}
4648
4649		if (np->rn_flags & RC_NODE_OLD) {
4650			rc_node_rele_locked(np);
4651			np = cache_lookup(&np_orig->rn_id);
4652			assert(np != np_orig);
4653
4654			if (np == NULL) {
4655				rc = REP_PROTOCOL_FAIL_DELETED;
4656				goto fail;
4657			}
4658			(void) pthread_mutex_lock(&np->rn_lock);
4659			continue;
4660		}
4661
4662		if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
4663			rc_node_rele_locked(np);
4664			rc_node_clear(npp, 1);
4665			rc = REP_PROTOCOL_FAIL_DELETED;
4666		}
4667
4668		/*
4669		 * Mark our parent as children changing.  this call drops our
4670		 * lock and the RC_NODE_USING_PARENT flag, and returns with
4671		 * pp's lock held
4672		 */
4673		pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
4674		if (pp == NULL) {
4675			/* our parent is gone, we're going next... */
4676			rc_node_rele(np);
4677
4678			rc_node_clear(npp, 1);
4679			rc = REP_PROTOCOL_FAIL_DELETED;
4680			goto cleanout;
4681		}
4682
4683		rc_node_hold_locked(pp);		/* hold for later */
4684		(void) pthread_mutex_unlock(&pp->rn_lock);
4685
4686		(void) pthread_mutex_lock(&np->rn_lock);
4687		if (!(np->rn_flags & RC_NODE_OLD))
4688			break;			/* not old -- we're done */
4689
4690		(void) pthread_mutex_unlock(&np->rn_lock);
4691		(void) pthread_mutex_lock(&pp->rn_lock);
4692		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
4693		rc_node_rele_locked(pp);
4694		(void) pthread_mutex_lock(&np->rn_lock);
4695		continue;			/* loop around and try again */
4696	}
4697	/*
4698	 * Everyone out of the pool -- we grab everything but
4699	 * RC_NODE_USING_PARENT (including RC_NODE_DYING) to keep
4700	 * any changes from occurring while we are attempting to
4701	 * delete the node.
4702	 */
4703	if (!rc_node_hold_flag(np, RC_NODE_DYING_FLAGS)) {
4704		(void) pthread_mutex_unlock(&np->rn_lock);
4705		rc = REP_PROTOCOL_FAIL_DELETED;
4706		goto fail;
4707	}
4708
4709	assert(!(np->rn_flags & RC_NODE_OLD));
4710
4711	if ((rc = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
4712	    REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
4713		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
4714		(void) pthread_mutex_unlock(&np->rn_lock);
4715		goto fail;
4716	}
4717
4718#ifdef NATIVE_BUILD
4719	if (!client_is_privileged()) {
4720		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
4721	}
4722#else
4723	if (is_main_repository) {
4724		/* permission check */
4725		(void) pthread_mutex_unlock(&np->rn_lock);
4726		pcp = pc_create();
4727		if (pcp != NULL) {
4728			rc = perm_add_enabling(pcp, AUTH_MODIFY);
4729
4730			/* add .smf.modify.<type> for pgs. */
4731			if (rc == REP_PROTOCOL_SUCCESS && np->rn_id.rl_type ==
4732			    REP_PROTOCOL_ENTITY_PROPERTYGRP) {
4733				const char * const auth =
4734				    perm_auth_for_pgtype(np->rn_type);
4735
4736				if (auth != NULL)
4737					rc = perm_add_enabling(pcp, auth);
4738			}
4739
4740			if (rc == REP_PROTOCOL_SUCCESS) {
4741				granted = perm_granted(pcp);
4742
4743				if (granted < 0) {
4744					rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
4745				} else {
4746					/*
4747					 * Copy out the authorization
4748					 * string before freeing pcp.
4749					 */
4750					audit_data.ed_auth =
4751					    strdup(pcp->pc_auth_string);
4752					if (audit_data.ed_auth == NULL) {
4753						/*
4754						 * Following code line
4755						 * cannot meet both the
4756						 * indentation and the line
4757						 * length requirements of
4758						 * cstyle.  Indendation has
4759						 * been sacrificed.
4760						 */
4761						/* CSTYLED */
4762					    rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
4763					}
4764				}
4765			}
4766
4767			pc_free(pcp);
4768		} else {
4769			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
4770		}
4771
4772		if (rc == REP_PROTOCOL_SUCCESS && !granted) {
4773			rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
4774			audit_failure = 1;
4775		}
4776		(void) pthread_mutex_lock(&np->rn_lock);
4777	} else {
4778		rc = REP_PROTOCOL_SUCCESS;
4779	}
4780#endif /* NATIVE_BUILD */
4781
4782	if (rc != REP_PROTOCOL_SUCCESS) {
4783		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
4784		(void) pthread_mutex_unlock(&np->rn_lock);
4785		goto fail;
4786	}
4787
4788	ndp = uu_zalloc(sizeof (*ndp));
4789	if (ndp == NULL) {
4790		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
4791		(void) pthread_mutex_unlock(&np->rn_lock);
4792		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
4793		goto fail;
4794	}
4795
4796	rc_node_delete_hold(np, 1);	/* hold entire subgraph, drop lock */
4797
4798	rc = object_delete(np);
4799
4800	if (rc != REP_PROTOCOL_SUCCESS) {
4801		(void) pthread_mutex_lock(&np->rn_lock);
4802		rc_node_delete_rele(np, 1);		/* drops lock */
4803		uu_free(ndp);
4804		goto fail;
4805	}
4806
4807	/*
4808	 * Now, delicately unlink and delete the object.
4809	 *
4810	 * Create the delete notification, atomically remove
4811	 * from the hash table and set the NODE_DEAD flag, and
4812	 * remove from the parent's children list.
4813	 */
4814	rc_notify_node_delete(ndp, np); /* frees or uses ndp */
4815
4816	bp = cache_hold(np->rn_hash);
4817
4818	(void) pthread_mutex_lock(&np->rn_lock);
4819	cache_remove_unlocked(bp, np);
4820	cache_release(bp);
4821
4822	np->rn_flags |= RC_NODE_DEAD;
4823	if (pp != NULL) {
4824		(void) pthread_mutex_unlock(&np->rn_lock);
4825
4826		(void) pthread_mutex_lock(&pp->rn_lock);
4827		(void) pthread_mutex_lock(&np->rn_lock);
4828		uu_list_remove(pp->rn_children, np);
4829		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
4830		(void) pthread_mutex_unlock(&pp->rn_lock);
4831		np->rn_flags &= ~RC_NODE_IN_PARENT;
4832	}
4833	/*
4834	 * finally, propagate death to our children, handle notifications,
4835	 * and release our hold.
4836	 */
4837	rc_node_hold_locked(np);	/* hold for delete */
4838	rc_node_delete_children(np, 1);	/* drops DYING_FLAGS, lock, ref */
4839
4840	rc_node_clear(npp, 1);
4841
4842	(void) pthread_mutex_lock(&rc_pg_notify_lock);
4843	while ((pnp = uu_list_first(np->rn_pg_notify_list)) != NULL)
4844		rc_pg_notify_fire(pnp);
4845	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
4846	rc_notify_remove_node(np);
4847
4848	rc_node_rele(np);
4849
4850	smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS,
4851	    &audit_data);
4852	free(audit_data.ed_auth);
4853	free(audit_data.ed_snapname);
4854	free(audit_data.ed_type);
4855	free(audit_data.ed_fmri);
4856	return (rc);
4857
4858fail:
4859	rc_node_rele(np);
4860	if (rc == REP_PROTOCOL_FAIL_DELETED)
4861		rc_node_clear(npp, 1);
4862	if (pp != NULL) {
4863		(void) pthread_mutex_lock(&pp->rn_lock);
4864		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
4865		rc_node_rele_locked(pp);	/* drop ref and lock */
4866	}
4867	if (audit_failure) {
4868		smf_audit_event(event_id, ADT_FAILURE,
4869		    ADT_FAIL_VALUE_AUTH, &audit_data);
4870	}
4871cleanout:
4872	free(audit_data.ed_auth);
4873	free(audit_data.ed_snapname);
4874	free(audit_data.ed_type);
4875	free(audit_data.ed_fmri);
4876	return (rc);
4877}
4878
4879int
4880rc_node_next_snaplevel(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
4881{
4882	rc_node_t *np;
4883	rc_node_t *cp, *pp;
4884	int res;
4885
4886	rc_node_clear(cpp, 0);
4887
4888	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
4889
4890	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT &&
4891	    np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL) {
4892		(void) pthread_mutex_unlock(&np->rn_lock);
4893		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
4894	}
4895
4896	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_SNAPSHOT) {
4897		if ((res = rc_node_fill_children(np,
4898		    REP_PROTOCOL_ENTITY_SNAPLEVEL)) != REP_PROTOCOL_SUCCESS) {
4899			(void) pthread_mutex_unlock(&np->rn_lock);
4900			return (res);
4901		}
4902
4903		for (cp = uu_list_first(np->rn_children);
4904		    cp != NULL;
4905		    cp = uu_list_next(np->rn_children, cp)) {
4906			if (cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
4907				continue;
4908			rc_node_hold(cp);
4909			break;
4910		}
4911
4912		(void) pthread_mutex_unlock(&np->rn_lock);
4913	} else {
4914		HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_USING_PARENT);
4915		/*
4916		 * mark our parent as children changing.  This call drops our
4917		 * lock and the RC_NODE_USING_PARENT flag, and returns with
4918		 * pp's lock held
4919		 */
4920		pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
4921		if (pp == NULL) {
4922			/* our parent is gone, we're going next... */
4923
4924			rc_node_clear(npp, 1);
4925			return (REP_PROTOCOL_FAIL_DELETED);
4926		}
4927
4928		/*
4929		 * find the next snaplevel
4930		 */
4931		cp = np;
4932		while ((cp = uu_list_next(pp->rn_children, cp)) != NULL &&
4933		    cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
4934			;
4935
4936		/* it must match the snaplevel list */
4937		assert((cp == NULL && np->rn_snaplevel->rsl_next == NULL) ||
4938		    (cp != NULL && np->rn_snaplevel->rsl_next ==
4939		    cp->rn_snaplevel));
4940
4941		if (cp != NULL)
4942			rc_node_hold(cp);
4943
4944		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
4945
4946		(void) pthread_mutex_unlock(&pp->rn_lock);
4947	}
4948
4949	rc_node_assign(cpp, cp);
4950	if (cp != NULL) {
4951		rc_node_rele(cp);
4952
4953		return (REP_PROTOCOL_SUCCESS);
4954	}
4955	return (REP_PROTOCOL_FAIL_NOT_FOUND);
4956}
4957
4958/*
4959 * This call takes a snapshot (np) and either:
4960 *	an existing snapid (to be associated with np), or
4961 *	a non-NULL parentp (from which a new snapshot is taken, and associated
4962 *	    with np)
4963 *
4964 * To do the association, np is duplicated, the duplicate is made to
4965 * represent the new snapid, and np is replaced with the new rc_node_t on
4966 * np's parent's child list. np is placed on the new node's rn_former list,
4967 * and replaces np in cache_hash (so rc_node_update() will find the new one).
4968 *
4969 * old_fmri and old_name point to the original snap shot's FMRI and name.
4970 * These values are used when generating audit events.
4971 *
4972 * Fails with
4973 *	_BAD_REQUEST
4974 *	_BACKEND_READONLY
4975 *	_DELETED
4976 *	_NO_RESOURCES
4977 *	_TRUNCATED
4978 *	_TYPE_MISMATCH
4979 */
4980static int
4981rc_attach_snapshot(
4982	rc_node_t *np,
4983	uint32_t snapid,
4984	rc_node_t *parentp,
4985	char *old_fmri,
4986	char *old_name)
4987{
4988	rc_node_t *np_orig;
4989	rc_node_t *nnp, *prev;
4990	rc_node_t *pp;
4991	int rc;
4992	size_t sz_out;
4993	au_event_t event_id;
4994	audit_event_data_t audit_data;
4995
4996	if (parentp == NULL) {
4997		assert(old_fmri != NULL);
4998	} else {
4999		assert(snapid == 0);
5000	}
5001	assert(MUTEX_HELD(&np->rn_lock));
5002
5003	/* Gather the audit data. */
5004	/*
5005	 * ADT_smf_* symbols may not be defined in the /usr/include header
5006	 * files on the build machine.  Thus, the following if-else will
5007	 * not be compiled when doing native builds.
5008	 */
5009#ifndef	NATIVE_BUILD
5010	if (parentp == NULL) {
5011		event_id = ADT_smf_attach_snap;
5012	} else {
5013		event_id = ADT_smf_create_snap;
5014	}
5015#endif	/* NATIVE_BUILD */
5016	audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
5017	audit_data.ed_snapname = malloc(REP_PROTOCOL_NAME_LEN);
5018	if ((audit_data.ed_fmri == NULL) || (audit_data.ed_snapname == NULL)) {
5019		(void) pthread_mutex_unlock(&np->rn_lock);
5020		free(audit_data.ed_fmri);
5021		free(audit_data.ed_snapname);
5022		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
5023	}
5024	audit_data.ed_auth = NULL;
5025	if (strlcpy(audit_data.ed_snapname, np->rn_name,
5026	    REP_PROTOCOL_NAME_LEN) >= REP_PROTOCOL_NAME_LEN) {
5027		abort();
5028	}
5029	audit_data.ed_old_fmri = old_fmri;
5030	audit_data.ed_old_name = old_name ? old_name : "NO NAME";
5031
5032	if (parentp == NULL) {
5033		/*
5034		 * In the attach case, get the instance FMRIs of the
5035		 * snapshots.
5036		 */
5037		if ((rc = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
5038		    REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
5039			(void) pthread_mutex_unlock(&np->rn_lock);
5040			free(audit_data.ed_fmri);
5041			free(audit_data.ed_snapname);
5042			return (rc);
5043		}
5044	} else {
5045		/*
5046		 * Capture the FMRI of the parent if we're actually going
5047		 * to take the snapshot.
5048		 */
5049		if ((rc = rc_node_get_fmri_or_fragment(parentp,
5050		    audit_data.ed_fmri, REP_PROTOCOL_FMRI_LEN, &sz_out)) !=
5051		    REP_PROTOCOL_SUCCESS) {
5052			(void) pthread_mutex_unlock(&np->rn_lock);
5053			free(audit_data.ed_fmri);
5054			free(audit_data.ed_snapname);
5055			return (rc);
5056		}
5057	}
5058
5059	np_orig = np;
5060	rc_node_hold_locked(np);		/* simplifies the remainder */
5061
5062	(void) pthread_mutex_unlock(&np->rn_lock);
5063	if ((rc = rc_node_modify_permission_check(&audit_data.ed_auth)) !=
5064	    REP_PROTOCOL_SUCCESS) {
5065		smf_audit_event(event_id, ADT_FAILURE, ADT_FAIL_VALUE_AUTH,
5066		    &audit_data);
5067		goto cleanout;
5068	}
5069	(void) pthread_mutex_lock(&np->rn_lock);
5070
5071	/*
5072	 * get the latest node, holding RC_NODE_IN_TX to keep the rn_former
5073	 * list from changing.
5074	 */
5075	for (;;) {
5076		if (!(np->rn_flags & RC_NODE_OLD)) {
5077			if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
5078				goto again;
5079			}
5080			pp = rc_node_hold_parent_flag(np,
5081			    RC_NODE_CHILDREN_CHANGING);
5082
5083			(void) pthread_mutex_lock(&np->rn_lock);
5084			if (pp == NULL) {
5085				goto again;
5086			}
5087			if (np->rn_flags & RC_NODE_OLD) {
5088				rc_node_rele_flag(pp,
5089				    RC_NODE_CHILDREN_CHANGING);
5090				(void) pthread_mutex_unlock(&pp->rn_lock);
5091				goto again;
5092			}
5093			(void) pthread_mutex_unlock(&pp->rn_lock);
5094
5095			if (!rc_node_hold_flag(np, RC_NODE_IN_TX)) {
5096				/*
5097				 * Can't happen, since we're holding our
5098				 * parent's CHILDREN_CHANGING flag...
5099				 */
5100				abort();
5101			}
5102			break;			/* everything's ready */
5103		}
5104again:
5105		rc_node_rele_locked(np);
5106		np = cache_lookup(&np_orig->rn_id);
5107
5108		if (np == NULL) {
5109			rc = REP_PROTOCOL_FAIL_DELETED;
5110			goto cleanout;
5111		}
5112
5113		(void) pthread_mutex_lock(&np->rn_lock);
5114	}
5115
5116	if (parentp != NULL) {
5117		if (pp != parentp) {
5118			rc = REP_PROTOCOL_FAIL_BAD_REQUEST;
5119			goto fail;
5120		}
5121		nnp = NULL;
5122	} else {
5123		/*
5124		 * look for a former node with the snapid we need.
5125		 */
5126		if (np->rn_snapshot_id == snapid) {
5127			rc_node_rele_flag(np, RC_NODE_IN_TX);
5128			rc_node_rele_locked(np);
5129
5130			(void) pthread_mutex_lock(&pp->rn_lock);
5131			rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
5132			(void) pthread_mutex_unlock(&pp->rn_lock);
5133			rc = REP_PROTOCOL_SUCCESS;	/* nothing to do */
5134			goto cleanout;
5135		}
5136
5137		prev = np;
5138		while ((nnp = prev->rn_former) != NULL) {
5139			if (nnp->rn_snapshot_id == snapid) {
5140				rc_node_hold(nnp);
5141				break;		/* existing node with that id */
5142			}
5143			prev = nnp;
5144		}
5145	}
5146
5147	if (nnp == NULL) {
5148		prev = NULL;
5149		nnp = rc_node_alloc();
5150		if (nnp == NULL) {
5151			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
5152			goto fail;
5153		}
5154
5155		nnp->rn_id = np->rn_id;		/* structure assignment */
5156		nnp->rn_hash = np->rn_hash;
5157		nnp->rn_name = strdup(np->rn_name);
5158		nnp->rn_snapshot_id = snapid;
5159		nnp->rn_flags = RC_NODE_IN_TX | RC_NODE_USING_PARENT;
5160
5161		if (nnp->rn_name == NULL) {
5162			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
5163			goto fail;
5164		}
5165	}
5166
5167	(void) pthread_mutex_unlock(&np->rn_lock);
5168
5169	rc = object_snapshot_attach(&np->rn_id, &snapid, (parentp != NULL));
5170
5171	if (parentp != NULL)
5172		nnp->rn_snapshot_id = snapid;	/* fill in new snapid */
5173	else
5174		assert(nnp->rn_snapshot_id == snapid);
5175
5176	(void) pthread_mutex_lock(&np->rn_lock);
5177	if (rc != REP_PROTOCOL_SUCCESS)
5178		goto fail;
5179
5180	/*
5181	 * fix up the former chain
5182	 */
5183	if (prev != NULL) {
5184		prev->rn_former = nnp->rn_former;
5185		(void) pthread_mutex_lock(&nnp->rn_lock);
5186		nnp->rn_flags &= ~RC_NODE_ON_FORMER;
5187		nnp->rn_former = NULL;
5188		(void) pthread_mutex_unlock(&nnp->rn_lock);
5189	}
5190	np->rn_flags |= RC_NODE_OLD;
5191	(void) pthread_mutex_unlock(&np->rn_lock);
5192
5193	/*
5194	 * replace np with nnp
5195	 */
5196	rc_node_relink_child(pp, np, nnp);
5197
5198	rc_node_rele(np);
5199	smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS, &audit_data);
5200	rc = REP_PROTOCOL_SUCCESS;
5201
5202cleanout:
5203	free(audit_data.ed_auth);
5204	free(audit_data.ed_fmri);
5205	free(audit_data.ed_snapname);
5206	return (rc);
5207
5208fail:
5209	rc_node_rele_flag(np, RC_NODE_IN_TX);
5210	rc_node_rele_locked(np);
5211	(void) pthread_mutex_lock(&pp->rn_lock);
5212	rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
5213	(void) pthread_mutex_unlock(&pp->rn_lock);
5214
5215	if (nnp != NULL) {
5216		if (prev == NULL)
5217			rc_node_destroy(nnp);
5218		else
5219			rc_node_rele(nnp);
5220	}
5221
5222	free(audit_data.ed_auth);
5223	free(audit_data.ed_fmri);
5224	free(audit_data.ed_snapname);
5225	return (rc);
5226}
5227
5228int
5229rc_snapshot_take_new(rc_node_ptr_t *npp, const char *svcname,
5230    const char *instname, const char *name, rc_node_ptr_t *outpp)
5231{
5232	rc_node_t *np;
5233	rc_node_t *outp = NULL;
5234	int rc, perm_rc;
5235	char fmri[REP_PROTOCOL_FMRI_LEN];
5236	audit_event_data_t audit_data;
5237	size_t sz_out;
5238
5239	rc_node_clear(outpp, 0);
5240
5241	perm_rc = rc_node_modify_permission_check(&audit_data.ed_auth);
5242
5243	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
5244	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) {
5245		(void) pthread_mutex_unlock(&np->rn_lock);
5246		free(audit_data.ed_auth);
5247		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
5248	}
5249
5250	rc = rc_check_type_name(REP_PROTOCOL_ENTITY_SNAPSHOT, name);
5251	if (rc != REP_PROTOCOL_SUCCESS) {
5252		(void) pthread_mutex_unlock(&np->rn_lock);
5253		free(audit_data.ed_auth);
5254		return (rc);
5255	}
5256
5257	if (svcname != NULL && (rc =
5258	    rc_check_type_name(REP_PROTOCOL_ENTITY_SERVICE, svcname)) !=
5259	    REP_PROTOCOL_SUCCESS) {
5260		(void) pthread_mutex_unlock(&np->rn_lock);
5261		free(audit_data.ed_auth);
5262		return (rc);
5263	}
5264
5265	if (instname != NULL && (rc =
5266	    rc_check_type_name(REP_PROTOCOL_ENTITY_INSTANCE, instname)) !=
5267	    REP_PROTOCOL_SUCCESS) {
5268		(void) pthread_mutex_unlock(&np->rn_lock);
5269		free(audit_data.ed_auth);
5270		return (rc);
5271	}
5272
5273	audit_data.ed_auth = NULL;
5274	audit_data.ed_fmri = fmri;
5275	audit_data.ed_snapname = (char *)name;
5276
5277	if ((rc = rc_node_get_fmri_or_fragment(np, fmri, sizeof (fmri),
5278	    &sz_out)) != REP_PROTOCOL_SUCCESS) {
5279		(void) pthread_mutex_unlock(&np->rn_lock);
5280		free(audit_data.ed_auth);
5281		return (rc);
5282	}
5283	if (perm_rc != REP_PROTOCOL_SUCCESS) {
5284		(void) pthread_mutex_unlock(&np->rn_lock);
5285		smf_audit_event(ADT_smf_create_snap, ADT_FAILURE,
5286		    ADT_FAIL_VALUE_AUTH, &audit_data);
5287		free(audit_data.ed_auth);
5288		return (perm_rc);
5289	}
5290
5291	HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
5292	    audit_data.ed_auth);
5293	(void) pthread_mutex_unlock(&np->rn_lock);
5294
5295	rc = object_snapshot_take_new(np, svcname, instname, name, &outp);
5296
5297	if (rc == REP_PROTOCOL_SUCCESS) {
5298		rc_node_assign(outpp, outp);
5299		rc_node_rele(outp);
5300	}
5301
5302	(void) pthread_mutex_lock(&np->rn_lock);
5303	rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
5304	(void) pthread_mutex_unlock(&np->rn_lock);
5305
5306	if (rc == REP_PROTOCOL_SUCCESS) {
5307		smf_audit_event(ADT_smf_create_snap, ADT_SUCCESS, ADT_SUCCESS,
5308		    &audit_data);
5309	}
5310	if (audit_data.ed_auth != NULL)
5311		free(audit_data.ed_auth);
5312	return (rc);
5313}
5314
5315int
5316rc_snapshot_take_attach(rc_node_ptr_t *npp, rc_node_ptr_t *outpp)
5317{
5318	rc_node_t *np, *outp;
5319
5320	RC_NODE_PTR_GET_CHECK(np, npp);
5321	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) {
5322		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
5323	}
5324
5325	RC_NODE_PTR_GET_CHECK_AND_LOCK(outp, outpp);
5326	if (outp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
5327		(void) pthread_mutex_unlock(&outp->rn_lock);
5328		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
5329	}
5330
5331	return (rc_attach_snapshot(outp, 0, np, NULL,
5332	    NULL));					/* drops outp's lock */
5333}
5334
5335int
5336rc_snapshot_attach(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
5337{
5338	rc_node_t *np;
5339	rc_node_t *cp;
5340	uint32_t snapid;
5341	char old_name[REP_PROTOCOL_NAME_LEN];
5342	int rc;
5343	size_t sz_out;
5344	char old_fmri[REP_PROTOCOL_FMRI_LEN];
5345
5346	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
5347	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
5348		(void) pthread_mutex_unlock(&np->rn_lock);
5349		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
5350	}
5351	snapid = np->rn_snapshot_id;
5352	rc = rc_node_get_fmri_or_fragment(np, old_fmri, sizeof (old_fmri),
5353	    &sz_out);
5354	(void) pthread_mutex_unlock(&np->rn_lock);
5355	if (rc != REP_PROTOCOL_SUCCESS)
5356		return (rc);
5357	if (np->rn_name != NULL) {
5358		if (strlcpy(old_name, np->rn_name, sizeof (old_name)) >=
5359		    sizeof (old_name)) {
5360			return (REP_PROTOCOL_FAIL_TRUNCATED);
5361		}
5362	}
5363
5364	RC_NODE_PTR_GET_CHECK_AND_LOCK(cp, cpp);
5365	if (cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
5366		(void) pthread_mutex_unlock(&cp->rn_lock);
5367		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
5368	}
5369
5370	rc = rc_attach_snapshot(cp, snapid, NULL,
5371	    old_fmri, old_name);			/* drops cp's lock */
5372	return (rc);
5373}
5374
5375/*
5376 * If the pgname property group under ent has type pgtype, and it has a
5377 * propname property with type ptype, return _SUCCESS.  If pgtype is NULL,
5378 * it is not checked.  If ent is not a service node, we will return _SUCCESS if
5379 * a property meeting the requirements exists in either the instance or its
5380 * parent.
5381 *
5382 * Returns
5383 *   _SUCCESS - see above
5384 *   _DELETED - ent or one of its ancestors was deleted
5385 *   _NO_RESOURCES - no resources
5386 *   _NOT_FOUND - no matching property was found
5387 */
5388static int
5389rc_svc_prop_exists(rc_node_t *ent, const char *pgname, const char *pgtype,
5390    const char *propname, rep_protocol_value_type_t ptype)
5391{
5392	int ret;
5393	rc_node_t *pg = NULL, *spg = NULL, *svc, *prop;
5394
5395	assert(!MUTEX_HELD(&ent->rn_lock));
5396
5397	(void) pthread_mutex_lock(&ent->rn_lock);
5398	ret = rc_node_find_named_child(ent, pgname,
5399	    REP_PROTOCOL_ENTITY_PROPERTYGRP, &pg);
5400	(void) pthread_mutex_unlock(&ent->rn_lock);
5401
5402	switch (ret) {
5403	case REP_PROTOCOL_SUCCESS:
5404		break;
5405
5406	case REP_PROTOCOL_FAIL_DELETED:
5407	case REP_PROTOCOL_FAIL_NO_RESOURCES:
5408		return (ret);
5409
5410	default:
5411		bad_error("rc_node_find_named_child", ret);
5412	}
5413
5414	if (ent->rn_id.rl_type != REP_PROTOCOL_ENTITY_SERVICE) {
5415		ret = rc_node_find_ancestor(ent, REP_PROTOCOL_ENTITY_SERVICE,
5416		    &svc);
5417		if (ret != REP_PROTOCOL_SUCCESS) {
5418			assert(ret == REP_PROTOCOL_FAIL_DELETED);
5419			if (pg != NULL)
5420				rc_node_rele(pg);
5421			return (ret);
5422		}
5423		assert(svc->rn_id.rl_type == REP_PROTOCOL_ENTITY_SERVICE);
5424
5425		(void) pthread_mutex_lock(&svc->rn_lock);
5426		ret = rc_node_find_named_child(svc, pgname,
5427		    REP_PROTOCOL_ENTITY_PROPERTYGRP, &spg);
5428		(void) pthread_mutex_unlock(&svc->rn_lock);
5429
5430		rc_node_rele(svc);
5431
5432		switch (ret) {
5433		case REP_PROTOCOL_SUCCESS:
5434			break;
5435
5436		case REP_PROTOCOL_FAIL_DELETED:
5437		case REP_PROTOCOL_FAIL_NO_RESOURCES:
5438			if (pg != NULL)
5439				rc_node_rele(pg);
5440			return (ret);
5441
5442		default:
5443			bad_error("rc_node_find_named_child", ret);
5444		}
5445	}
5446
5447	if (pg != NULL &&
5448	    pgtype != NULL && strcmp(pg->rn_type, pgtype) != 0) {
5449		rc_node_rele(pg);
5450		pg = NULL;
5451	}
5452
5453	if (spg != NULL &&
5454	    pgtype != NULL && strcmp(spg->rn_type, pgtype) != 0) {
5455		rc_node_rele(spg);
5456		spg = NULL;
5457	}
5458
5459	if (pg == NULL) {
5460		if (spg == NULL)
5461			return (REP_PROTOCOL_FAIL_NOT_FOUND);
5462		pg = spg;
5463		spg = NULL;
5464	}
5465
5466	/*
5467	 * At this point, pg is non-NULL, and is a property group node of the
5468	 * correct type.  spg, if non-NULL, is also a property group node of
5469	 * the correct type.  Check for the property in pg first, then spg
5470	 * (if applicable).
5471	 */
5472	(void) pthread_mutex_lock(&pg->rn_lock);
5473	ret = rc_node_find_named_child(pg, propname,
5474	    REP_PROTOCOL_ENTITY_PROPERTY, &prop);
5475	(void) pthread_mutex_unlock(&pg->rn_lock);
5476	rc_node_rele(pg);
5477	switch (ret) {
5478	case REP_PROTOCOL_SUCCESS:
5479		if (prop != NULL) {
5480			if (prop->rn_valtype == ptype) {
5481				rc_node_rele(prop);
5482				if (spg != NULL)
5483					rc_node_rele(spg);
5484				return (REP_PROTOCOL_SUCCESS);
5485			}
5486			rc_node_rele(prop);
5487		}
5488		break;
5489
5490	case REP_PROTOCOL_FAIL_NO_RESOURCES:
5491		if (spg != NULL)
5492			rc_node_rele(spg);
5493		return (ret);
5494
5495	case REP_PROTOCOL_FAIL_DELETED:
5496		break;
5497
5498	default:
5499		bad_error("rc_node_find_named_child", ret);
5500	}
5501
5502	if (spg == NULL)
5503		return (REP_PROTOCOL_FAIL_NOT_FOUND);
5504
5505	pg = spg;
5506
5507	(void) pthread_mutex_lock(&pg->rn_lock);
5508	ret = rc_node_find_named_child(pg, propname,
5509	    REP_PROTOCOL_ENTITY_PROPERTY, &prop);
5510	(void) pthread_mutex_unlock(&pg->rn_lock);
5511	rc_node_rele(pg);
5512	switch (ret) {
5513	case REP_PROTOCOL_SUCCESS:
5514		if (prop != NULL) {
5515			if (prop->rn_valtype == ptype) {
5516				rc_node_rele(prop);
5517				return (REP_PROTOCOL_SUCCESS);
5518			}
5519			rc_node_rele(prop);
5520		}
5521		return (REP_PROTOCOL_FAIL_NOT_FOUND);
5522
5523	case REP_PROTOCOL_FAIL_NO_RESOURCES:
5524		return (ret);
5525
5526	case REP_PROTOCOL_FAIL_DELETED:
5527		return (REP_PROTOCOL_FAIL_NOT_FOUND);
5528
5529	default:
5530		bad_error("rc_node_find_named_child", ret);
5531	}
5532
5533	return (REP_PROTOCOL_SUCCESS);
5534}
5535
5536/*
5537 * Given a property group node, returns _SUCCESS if the property group may
5538 * be read without any special authorization.
5539 *
5540 * Fails with:
5541 *   _DELETED - np or an ancestor node was deleted
5542 *   _TYPE_MISMATCH - np does not refer to a property group
5543 *   _NO_RESOURCES - no resources
5544 *   _PERMISSION_DENIED - authorization is required
5545 */
5546static int
5547rc_node_pg_check_read_protect(rc_node_t *np)
5548{
5549	int ret;
5550	rc_node_t *ent;
5551
5552	assert(!MUTEX_HELD(&np->rn_lock));
5553
5554	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
5555		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
5556
5557	if (strcmp(np->rn_type, SCF_GROUP_FRAMEWORK) == 0 ||
5558	    strcmp(np->rn_type, SCF_GROUP_DEPENDENCY) == 0 ||
5559	    strcmp(np->rn_type, SCF_GROUP_METHOD) == 0)
5560		return (REP_PROTOCOL_SUCCESS);
5561
5562	ret = rc_node_parent(np, &ent);
5563
5564	if (ret != REP_PROTOCOL_SUCCESS)
5565		return (ret);
5566
5567	ret = rc_svc_prop_exists(ent, np->rn_name, np->rn_type,
5568	    AUTH_PROP_READ, REP_PROTOCOL_TYPE_STRING);
5569
5570	rc_node_rele(ent);
5571
5572	switch (ret) {
5573	case REP_PROTOCOL_FAIL_NOT_FOUND:
5574		return (REP_PROTOCOL_SUCCESS);
5575	case REP_PROTOCOL_SUCCESS:
5576		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
5577	case REP_PROTOCOL_FAIL_DELETED:
5578	case REP_PROTOCOL_FAIL_NO_RESOURCES:
5579		return (ret);
5580	default:
5581		bad_error("rc_svc_prop_exists", ret);
5582	}
5583
5584	return (REP_PROTOCOL_SUCCESS);
5585}
5586
5587/*
5588 * Fails with
5589 *   _DELETED - np's node or parent has been deleted
5590 *   _TYPE_MISMATCH - np's node is not a property
5591 *   _NO_RESOURCES - out of memory
5592 *   _PERMISSION_DENIED - no authorization to read this property's value(s)
5593 *   _BAD_REQUEST - np's parent is not a property group
5594 */
5595static int
5596rc_node_property_may_read(rc_node_t *np)
5597{
5598	int ret, granted = 0;
5599	rc_node_t *pgp;
5600	permcheck_t *pcp;
5601	audit_event_data_t audit_data;
5602	size_t sz_out;
5603
5604	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
5605		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
5606
5607	if (client_is_privileged())
5608		return (REP_PROTOCOL_SUCCESS);
5609
5610#ifdef NATIVE_BUILD
5611	return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
5612#else
5613	ret = rc_node_parent(np, &pgp);
5614
5615	if (ret != REP_PROTOCOL_SUCCESS)
5616		return (ret);
5617
5618	if (pgp->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
5619		rc_node_rele(pgp);
5620		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
5621	}
5622
5623	ret = rc_node_pg_check_read_protect(pgp);
5624
5625	if (ret != REP_PROTOCOL_FAIL_PERMISSION_DENIED) {
5626		rc_node_rele(pgp);
5627		return (ret);
5628	}
5629
5630	pcp = pc_create();
5631
5632	if (pcp == NULL) {
5633		rc_node_rele(pgp);
5634		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
5635	}
5636
5637	ret = perm_add_enabling(pcp, AUTH_MODIFY);
5638
5639	if (ret == REP_PROTOCOL_SUCCESS) {
5640		const char * const auth =
5641		    perm_auth_for_pgtype(pgp->rn_type);
5642
5643		if (auth != NULL)
5644			ret = perm_add_enabling(pcp, auth);
5645	}
5646
5647	/*
5648	 * If you are permitted to modify the value, you may also
5649	 * read it.  This means that both the MODIFY and VALUE
5650	 * authorizations are acceptable.  We don't allow requests
5651	 * for AUTH_PROP_MODIFY if all you have is $AUTH_PROP_VALUE,
5652	 * however, to avoid leaking possibly valuable information
5653	 * since such a user can't change the property anyway.
5654	 */
5655	if (ret == REP_PROTOCOL_SUCCESS)
5656		ret = perm_add_enabling_values(pcp, pgp,
5657		    AUTH_PROP_MODIFY);
5658
5659	if (ret == REP_PROTOCOL_SUCCESS &&
5660	    strcmp(np->rn_name, AUTH_PROP_MODIFY) != 0)
5661		ret = perm_add_enabling_values(pcp, pgp,
5662		    AUTH_PROP_VALUE);
5663
5664	if (ret == REP_PROTOCOL_SUCCESS)
5665		ret = perm_add_enabling_values(pcp, pgp,
5666		    AUTH_PROP_READ);
5667
5668	rc_node_rele(pgp);
5669
5670	if (ret == REP_PROTOCOL_SUCCESS) {
5671		granted = perm_granted(pcp);
5672		if (granted < 0)
5673			ret = REP_PROTOCOL_FAIL_NO_RESOURCES;
5674	}
5675	if (ret == REP_PROTOCOL_SUCCESS) {
5676		/* Generate a read_prop audit event. */
5677		audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
5678		if (audit_data.ed_fmri == NULL)
5679			ret = REP_PROTOCOL_FAIL_NO_RESOURCES;
5680	}
5681	ret = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
5682	    REP_PROTOCOL_FMRI_LEN, &sz_out);
5683	assert(ret == REP_PROTOCOL_SUCCESS);
5684	if (ret == REP_PROTOCOL_SUCCESS) {
5685		int status;
5686		int ret_value;
5687
5688		if (granted == 0) {
5689			status = ADT_FAILURE;
5690			ret_value = ADT_FAIL_VALUE_AUTH;
5691		} else {
5692			status = ADT_SUCCESS;
5693			ret_value = ADT_SUCCESS;
5694		}
5695		audit_data.ed_auth = pcp->pc_auth_string;
5696		smf_audit_event(ADT_smf_read_prop,
5697		    status, ret_value, &audit_data);
5698	}
5699	free(audit_data.ed_fmri);
5700
5701	pc_free(pcp);
5702
5703	if (ret == REP_PROTOCOL_SUCCESS && !granted)
5704		ret = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
5705
5706	return (ret);
5707#endif	/* NATIVE_BUILD */
5708}
5709
5710/*
5711 * Iteration
5712 */
5713static int
5714rc_iter_filter_name(rc_node_t *np, void *s)
5715{
5716	const char *name = s;
5717
5718	return (strcmp(np->rn_name, name) == 0);
5719}
5720
5721static int
5722rc_iter_filter_type(rc_node_t *np, void *s)
5723{
5724	const char *type = s;
5725
5726	return (np->rn_type != NULL && strcmp(np->rn_type, type) == 0);
5727}
5728
5729/*ARGSUSED*/
5730static int
5731rc_iter_null_filter(rc_node_t *np, void *s)
5732{
5733	return (1);
5734}
5735
5736/*
5737 * Allocate & initialize an rc_node_iter_t structure.  Essentially, ensure
5738 * np->rn_children is populated and call uu_list_walk_start(np->rn_children).
5739 * If successful, leaves a hold on np & increments np->rn_other_refs
5740 *
5741 * If composed is true, then set up for iteration across the top level of np's
5742 * composition chain.  If successful, leaves a hold on np and increments
5743 * rn_other_refs for the top level of np's composition chain.
5744 *
5745 * Fails with
5746 *   _NO_RESOURCES
5747 *   _INVALID_TYPE
5748 *   _TYPE_MISMATCH - np cannot carry type children
5749 *   _DELETED
5750 */
5751static int
5752rc_iter_create(rc_node_iter_t **resp, rc_node_t *np, uint32_t type,
5753    rc_iter_filter_func *filter, void *arg, boolean_t composed)
5754{
5755	rc_node_iter_t *nip;
5756	int res;
5757
5758	assert(*resp == NULL);
5759
5760	nip = uu_zalloc(sizeof (*nip));
5761	if (nip == NULL)
5762		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
5763
5764	/* np is held by the client's rc_node_ptr_t */
5765	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP)
5766		composed = 1;
5767
5768	if (!composed) {
5769		(void) pthread_mutex_lock(&np->rn_lock);
5770
5771		if ((res = rc_node_fill_children(np, type)) !=
5772		    REP_PROTOCOL_SUCCESS) {
5773			(void) pthread_mutex_unlock(&np->rn_lock);
5774			uu_free(nip);
5775			return (res);
5776		}
5777
5778		nip->rni_clevel = -1;
5779
5780		nip->rni_iter = uu_list_walk_start(np->rn_children,
5781		    UU_WALK_ROBUST);
5782		if (nip->rni_iter != NULL) {
5783			nip->rni_iter_node = np;
5784			rc_node_hold_other(np);
5785		} else {
5786			(void) pthread_mutex_unlock(&np->rn_lock);
5787			uu_free(nip);
5788			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
5789		}
5790		(void) pthread_mutex_unlock(&np->rn_lock);
5791	} else {
5792		rc_node_t *ent;
5793
5794		if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_SNAPSHOT) {
5795			/* rn_cchain isn't valid until children are loaded. */
5796			(void) pthread_mutex_lock(&np->rn_lock);
5797			res = rc_node_fill_children(np,
5798			    REP_PROTOCOL_ENTITY_SNAPLEVEL);
5799			(void) pthread_mutex_unlock(&np->rn_lock);
5800			if (res != REP_PROTOCOL_SUCCESS) {
5801				uu_free(nip);
5802				return (res);
5803			}
5804
5805			/* Check for an empty snapshot. */
5806			if (np->rn_cchain[0] == NULL)
5807				goto empty;
5808		}
5809
5810		/* Start at the top of the composition chain. */
5811		for (nip->rni_clevel = 0; ; ++nip->rni_clevel) {
5812			if (nip->rni_clevel >= COMPOSITION_DEPTH) {
5813				/* Empty composition chain. */
5814empty:
5815				nip->rni_clevel = -1;
5816				nip->rni_iter = NULL;
5817				/* It's ok, iter_next() will return _DONE. */
5818				goto out;
5819			}
5820
5821			ent = np->rn_cchain[nip->rni_clevel];
5822			assert(ent != NULL);
5823
5824			if (rc_node_check_and_lock(ent) == REP_PROTOCOL_SUCCESS)
5825				break;
5826
5827			/* Someone deleted it, so try the next one. */
5828		}
5829
5830		res = rc_node_fill_children(ent, type);
5831
5832		if (res == REP_PROTOCOL_SUCCESS) {
5833			nip->rni_iter = uu_list_walk_start(ent->rn_children,
5834			    UU_WALK_ROBUST);
5835
5836			if (nip->rni_iter == NULL)
5837				res = REP_PROTOCOL_FAIL_NO_RESOURCES;
5838			else {
5839				nip->rni_iter_node = ent;
5840				rc_node_hold_other(ent);
5841			}
5842		}
5843
5844		if (res != REP_PROTOCOL_SUCCESS) {
5845			(void) pthread_mutex_unlock(&ent->rn_lock);
5846			uu_free(nip);
5847			return (res);
5848		}
5849
5850		(void) pthread_mutex_unlock(&ent->rn_lock);
5851	}
5852
5853out:
5854	rc_node_hold(np);		/* released by rc_iter_end() */
5855	nip->rni_parent = np;
5856	nip->rni_type = type;
5857	nip->rni_filter = (filter != NULL)? filter : rc_iter_null_filter;
5858	nip->rni_filter_arg = arg;
5859	*resp = nip;
5860	return (REP_PROTOCOL_SUCCESS);
5861}
5862
5863static void
5864rc_iter_end(rc_node_iter_t *iter)
5865{
5866	rc_node_t *np = iter->rni_parent;
5867
5868	if (iter->rni_clevel >= 0)
5869		np = np->rn_cchain[iter->rni_clevel];
5870
5871	assert(MUTEX_HELD(&np->rn_lock));
5872	if (iter->rni_iter != NULL)
5873		uu_list_walk_end(iter->rni_iter);
5874	iter->rni_iter = NULL;
5875
5876	(void) pthread_mutex_unlock(&np->rn_lock);
5877	rc_node_rele(iter->rni_parent);
5878	if (iter->rni_iter_node != NULL)
5879		rc_node_rele_other(iter->rni_iter_node);
5880}
5881
5882/*
5883 * Fails with
5884 *   _NOT_SET - npp is reset
5885 *   _DELETED - npp's node has been deleted
5886 *   _NOT_APPLICABLE - npp's node is not a property
5887 *   _NO_RESOURCES - out of memory
5888 */
5889static int
5890rc_node_setup_value_iter(rc_node_ptr_t *npp, rc_node_iter_t **iterp)
5891{
5892	rc_node_t *np;
5893
5894	rc_node_iter_t *nip;
5895
5896	assert(*iterp == NULL);
5897
5898	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
5899
5900	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) {
5901		(void) pthread_mutex_unlock(&np->rn_lock);
5902		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
5903	}
5904
5905	nip = uu_zalloc(sizeof (*nip));
5906	if (nip == NULL) {
5907		(void) pthread_mutex_unlock(&np->rn_lock);
5908		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
5909	}
5910
5911	nip->rni_parent = np;
5912	nip->rni_iter = NULL;
5913	nip->rni_clevel = -1;
5914	nip->rni_type = REP_PROTOCOL_ENTITY_VALUE;
5915	nip->rni_offset = 0;
5916	nip->rni_last_offset = 0;
5917
5918	rc_node_hold_locked(np);
5919
5920	*iterp = nip;
5921	(void) pthread_mutex_unlock(&np->rn_lock);
5922
5923	return (REP_PROTOCOL_SUCCESS);
5924}
5925
5926/*
5927 * Returns:
5928 *   _NO_RESOURCES - out of memory
5929 *   _NOT_SET - npp is reset
5930 *   _DELETED - npp's node has been deleted
5931 *   _TYPE_MISMATCH - npp's node is not a property
5932 *   _NOT_FOUND - property has no values
5933 *   _TRUNCATED - property has >1 values (first is written into out)
5934 *   _SUCCESS - property has 1 value (which is written into out)
5935 *   _PERMISSION_DENIED - no authorization to read property value(s)
5936 *
5937 * We shorten *sz_out to not include anything after the final '\0'.
5938 */
5939int
5940rc_node_get_property_value(rc_node_ptr_t *npp,
5941    struct rep_protocol_value_response *out, size_t *sz_out)
5942{
5943	rc_node_t *np;
5944	size_t w;
5945	int ret;
5946
5947	assert(*sz_out == sizeof (*out));
5948
5949	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
5950	ret = rc_node_property_may_read(np);
5951	rc_node_rele(np);
5952
5953	if (ret != REP_PROTOCOL_SUCCESS)
5954		return (ret);
5955
5956	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
5957
5958	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) {
5959		(void) pthread_mutex_unlock(&np->rn_lock);
5960		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
5961	}
5962
5963	if (np->rn_values_size == 0) {
5964		(void) pthread_mutex_unlock(&np->rn_lock);
5965		return (REP_PROTOCOL_FAIL_NOT_FOUND);
5966	}
5967	out->rpr_type = np->rn_valtype;
5968	w = strlcpy(out->rpr_value, &np->rn_values[0],
5969	    sizeof (out->rpr_value));
5970
5971	if (w >= sizeof (out->rpr_value))
5972		backend_panic("value too large");
5973
5974	*sz_out = offsetof(struct rep_protocol_value_response,
5975	    rpr_value[w + 1]);
5976
5977	ret = (np->rn_values_count != 1)? REP_PROTOCOL_FAIL_TRUNCATED :
5978	    REP_PROTOCOL_SUCCESS;
5979	(void) pthread_mutex_unlock(&np->rn_lock);
5980	return (ret);
5981}
5982
5983int
5984rc_iter_next_value(rc_node_iter_t *iter,
5985    struct rep_protocol_value_response *out, size_t *sz_out, int repeat)
5986{
5987	rc_node_t *np = iter->rni_parent;
5988	const char *vals;
5989	size_t len;
5990
5991	size_t start;
5992	size_t w;
5993	int ret;
5994
5995	rep_protocol_responseid_t result;
5996
5997	assert(*sz_out == sizeof (*out));
5998
5999	(void) memset(out, '\0', *sz_out);
6000
6001	if (iter->rni_type != REP_PROTOCOL_ENTITY_VALUE)
6002		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
6003
6004	RC_NODE_CHECK(np);
6005	ret = rc_node_property_may_read(np);
6006
6007	if (ret != REP_PROTOCOL_SUCCESS)
6008		return (ret);
6009
6010	RC_NODE_CHECK_AND_LOCK(np);
6011
6012	vals = np->rn_values;
6013	len = np->rn_values_size;
6014
6015	out->rpr_type = np->rn_valtype;
6016
6017	start = (repeat)? iter->rni_last_offset : iter->rni_offset;
6018
6019	if (len == 0 || start >= len) {
6020		result = REP_PROTOCOL_DONE;
6021		*sz_out -= sizeof (out->rpr_value);
6022	} else {
6023		w = strlcpy(out->rpr_value, &vals[start],
6024		    sizeof (out->rpr_value));
6025
6026		if (w >= sizeof (out->rpr_value))
6027			backend_panic("value too large");
6028
6029		*sz_out = offsetof(struct rep_protocol_value_response,
6030		    rpr_value[w + 1]);
6031
6032		/*
6033		 * update the offsets if we're not repeating
6034		 */
6035		if (!repeat) {
6036			iter->rni_last_offset = iter->rni_offset;
6037			iter->rni_offset += (w + 1);
6038		}
6039
6040		result = REP_PROTOCOL_SUCCESS;
6041	}
6042
6043	(void) pthread_mutex_unlock(&np->rn_lock);
6044	return (result);
6045}
6046
6047/*
6048 * Entry point for ITER_START from client.c.  Validate the arguments & call
6049 * rc_iter_create().
6050 *
6051 * Fails with
6052 *   _NOT_SET
6053 *   _DELETED
6054 *   _TYPE_MISMATCH - np cannot carry type children
6055 *   _BAD_REQUEST - flags is invalid
6056 *		    pattern is invalid
6057 *   _NO_RESOURCES
6058 *   _INVALID_TYPE
6059 *   _TYPE_MISMATCH - *npp cannot have children of type
6060 *   _BACKEND_ACCESS
6061 */
6062int
6063rc_node_setup_iter(rc_node_ptr_t *npp, rc_node_iter_t **iterp,
6064    uint32_t type, uint32_t flags, const char *pattern)
6065{
6066	rc_node_t *np;
6067	rc_iter_filter_func *f = NULL;
6068	int rc;
6069
6070	RC_NODE_PTR_GET_CHECK(np, npp);
6071
6072	if (pattern != NULL && pattern[0] == '\0')
6073		pattern = NULL;
6074
6075	if (type == REP_PROTOCOL_ENTITY_VALUE) {
6076		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
6077			return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
6078		if (flags != RP_ITER_START_ALL || pattern != NULL)
6079			return (REP_PROTOCOL_FAIL_BAD_REQUEST);
6080
6081		rc = rc_node_setup_value_iter(npp, iterp);
6082		assert(rc != REP_PROTOCOL_FAIL_NOT_APPLICABLE);
6083		return (rc);
6084	}
6085
6086	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
6087	    REP_PROTOCOL_SUCCESS)
6088		return (rc);
6089
6090	if (((flags & RP_ITER_START_FILT_MASK) == RP_ITER_START_ALL) ^
6091	    (pattern == NULL))
6092		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
6093
6094	/* Composition only works for instances & snapshots. */
6095	if ((flags & RP_ITER_START_COMPOSED) &&
6096	    (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE &&
6097	    np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT))
6098		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
6099
6100	if (pattern != NULL) {
6101		if ((rc = rc_check_type_name(type, pattern)) !=
6102		    REP_PROTOCOL_SUCCESS)
6103			return (rc);
6104		pattern = strdup(pattern);
6105		if (pattern == NULL)
6106			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
6107	}
6108
6109	switch (flags & RP_ITER_START_FILT_MASK) {
6110	case RP_ITER_START_ALL:
6111		f = NULL;
6112		break;
6113	case RP_ITER_START_EXACT:
6114		f = rc_iter_filter_name;
6115		break;
6116	case RP_ITER_START_PGTYPE:
6117		if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
6118			free((void *)pattern);
6119			return (REP_PROTOCOL_FAIL_BAD_REQUEST);
6120		}
6121		f = rc_iter_filter_type;
6122		break;
6123	default:
6124		free((void *)pattern);
6125		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
6126	}
6127
6128	rc = rc_iter_create(iterp, np, type, f, (void *)pattern,
6129	    flags & RP_ITER_START_COMPOSED);
6130	if (rc != REP_PROTOCOL_SUCCESS && pattern != NULL)
6131		free((void *)pattern);
6132
6133	return (rc);
6134}
6135
6136/*
6137 * Do uu_list_walk_next(iter->rni_iter) until we find a child which matches
6138 * the filter.
6139 * For composed iterators, then check to see if there's an overlapping entity
6140 * (see embedded comments).  If we reach the end of the list, start over at
6141 * the next level.
6142 *
6143 * Returns
6144 *   _BAD_REQUEST - iter walks values
6145 *   _TYPE_MISMATCH - iter does not walk type entities
6146 *   _DELETED - parent was deleted
6147 *   _NO_RESOURCES
6148 *   _INVALID_TYPE - type is invalid
6149 *   _DONE
6150 *   _SUCCESS
6151 *
6152 * For composed property group iterators, can also return
6153 *   _TYPE_MISMATCH - parent cannot have type children
6154 */
6155int
6156rc_iter_next(rc_node_iter_t *iter, rc_node_ptr_t *out, uint32_t type)
6157{
6158	rc_node_t *np = iter->rni_parent;
6159	rc_node_t *res;
6160	int rc;
6161
6162	if (iter->rni_type == REP_PROTOCOL_ENTITY_VALUE)
6163		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
6164
6165	if (iter->rni_iter == NULL) {
6166		rc_node_clear(out, 0);
6167		return (REP_PROTOCOL_DONE);
6168	}
6169
6170	if (iter->rni_type != type) {
6171		rc_node_clear(out, 0);
6172		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
6173	}
6174
6175	(void) pthread_mutex_lock(&np->rn_lock);  /* held by _iter_create() */
6176
6177	if (!rc_node_wait_flag(np, RC_NODE_CHILDREN_CHANGING)) {
6178		(void) pthread_mutex_unlock(&np->rn_lock);
6179		rc_node_clear(out, 1);
6180		return (REP_PROTOCOL_FAIL_DELETED);
6181	}
6182
6183	if (iter->rni_clevel >= 0) {
6184		/* Composed iterator.  Iterate over appropriate level. */
6185		(void) pthread_mutex_unlock(&np->rn_lock);
6186		np = np->rn_cchain[iter->rni_clevel];
6187		/*
6188		 * If iter->rni_parent is an instance or a snapshot, np must
6189		 * be valid since iter holds iter->rni_parent & possible
6190		 * levels (service, instance, snaplevel) cannot be destroyed
6191		 * while rni_parent is held.  If iter->rni_parent is
6192		 * a composed property group then rc_node_setup_cpg() put
6193		 * a hold on np.
6194		 */
6195
6196		(void) pthread_mutex_lock(&np->rn_lock);
6197
6198		if (!rc_node_wait_flag(np, RC_NODE_CHILDREN_CHANGING)) {
6199			(void) pthread_mutex_unlock(&np->rn_lock);
6200			rc_node_clear(out, 1);
6201			return (REP_PROTOCOL_FAIL_DELETED);
6202		}
6203	}
6204
6205	assert(np->rn_flags & RC_NODE_HAS_CHILDREN);
6206
6207	for (;;) {
6208		res = uu_list_walk_next(iter->rni_iter);
6209		if (res == NULL) {
6210			rc_node_t *parent = iter->rni_parent;
6211
6212#if COMPOSITION_DEPTH == 2
6213			if (iter->rni_clevel < 0 || iter->rni_clevel == 1) {
6214				/* release walker and lock */
6215				rc_iter_end(iter);
6216				break;
6217			}
6218
6219			/* Stop walking current level. */
6220			uu_list_walk_end(iter->rni_iter);
6221			iter->rni_iter = NULL;
6222			(void) pthread_mutex_unlock(&np->rn_lock);
6223			rc_node_rele_other(iter->rni_iter_node);
6224			iter->rni_iter_node = NULL;
6225
6226			/* Start walking next level. */
6227			++iter->rni_clevel;
6228			np = parent->rn_cchain[iter->rni_clevel];
6229			assert(np != NULL);
6230#else
6231#error This code must be updated.
6232#endif
6233
6234			(void) pthread_mutex_lock(&np->rn_lock);
6235
6236			rc = rc_node_fill_children(np, iter->rni_type);
6237
6238			if (rc == REP_PROTOCOL_SUCCESS) {
6239				iter->rni_iter =
6240				    uu_list_walk_start(np->rn_children,
6241				    UU_WALK_ROBUST);
6242
6243				if (iter->rni_iter == NULL)
6244					rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
6245				else {
6246					iter->rni_iter_node = np;
6247					rc_node_hold_other(np);
6248				}
6249			}
6250
6251			if (rc != REP_PROTOCOL_SUCCESS) {
6252				(void) pthread_mutex_unlock(&np->rn_lock);
6253				rc_node_clear(out, 0);
6254				return (rc);
6255			}
6256
6257			continue;
6258		}
6259
6260		if (res->rn_id.rl_type != type ||
6261		    !iter->rni_filter(res, iter->rni_filter_arg))
6262			continue;
6263
6264		/*
6265		 * If we're composed and not at the top level, check to see if
6266		 * there's an entity at a higher level with the same name.  If
6267		 * so, skip this one.
6268		 */
6269		if (iter->rni_clevel > 0) {
6270			rc_node_t *ent = iter->rni_parent->rn_cchain[0];
6271			rc_node_t *pg;
6272
6273#if COMPOSITION_DEPTH == 2
6274			assert(iter->rni_clevel == 1);
6275
6276			(void) pthread_mutex_unlock(&np->rn_lock);
6277			(void) pthread_mutex_lock(&ent->rn_lock);
6278			rc = rc_node_find_named_child(ent, res->rn_name, type,
6279			    &pg);
6280			if (rc == REP_PROTOCOL_SUCCESS && pg != NULL)
6281				rc_node_rele(pg);
6282			(void) pthread_mutex_unlock(&ent->rn_lock);
6283			if (rc != REP_PROTOCOL_SUCCESS) {
6284				rc_node_clear(out, 0);
6285				return (rc);
6286			}
6287			(void) pthread_mutex_lock(&np->rn_lock);
6288
6289			/* Make sure np isn't being deleted all of a sudden. */
6290			if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
6291				(void) pthread_mutex_unlock(&np->rn_lock);
6292				rc_node_clear(out, 1);
6293				return (REP_PROTOCOL_FAIL_DELETED);
6294			}
6295
6296			if (pg != NULL)
6297				/* Keep going. */
6298				continue;
6299#else
6300#error This code must be updated.
6301#endif
6302		}
6303
6304		/*
6305		 * If we're composed, iterating over property groups, and not
6306		 * at the bottom level, check to see if there's a pg at lower
6307		 * level with the same name.  If so, return a cpg.
6308		 */
6309		if (iter->rni_clevel >= 0 &&
6310		    type == REP_PROTOCOL_ENTITY_PROPERTYGRP &&
6311		    iter->rni_clevel < COMPOSITION_DEPTH - 1) {
6312#if COMPOSITION_DEPTH == 2
6313			rc_node_t *pg;
6314			rc_node_t *ent = iter->rni_parent->rn_cchain[1];
6315
6316			rc_node_hold(res);	/* While we drop np->rn_lock */
6317
6318			(void) pthread_mutex_unlock(&np->rn_lock);
6319			(void) pthread_mutex_lock(&ent->rn_lock);
6320			rc = rc_node_find_named_child(ent, res->rn_name, type,
6321			    &pg);
6322			/* holds pg if not NULL */
6323			(void) pthread_mutex_unlock(&ent->rn_lock);
6324			if (rc != REP_PROTOCOL_SUCCESS) {
6325				rc_node_rele(res);
6326				rc_node_clear(out, 0);
6327				return (rc);
6328			}
6329
6330			(void) pthread_mutex_lock(&np->rn_lock);
6331			if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
6332				(void) pthread_mutex_unlock(&np->rn_lock);
6333				rc_node_rele(res);
6334				if (pg != NULL)
6335					rc_node_rele(pg);
6336				rc_node_clear(out, 1);
6337				return (REP_PROTOCOL_FAIL_DELETED);
6338			}
6339
6340			if (pg == NULL) {
6341				rc_node_rele(res);
6342			} else {
6343				rc_node_t *cpg;
6344
6345				/* Keep res held for rc_node_setup_cpg(). */
6346
6347				cpg = rc_node_alloc();
6348				if (cpg == NULL) {
6349					(void) pthread_mutex_unlock(
6350					    &np->rn_lock);
6351					rc_node_rele(res);
6352					rc_node_rele(pg);
6353					rc_node_clear(out, 0);
6354					return (REP_PROTOCOL_FAIL_NO_RESOURCES);
6355				}
6356
6357				switch (rc_node_setup_cpg(cpg, res, pg)) {
6358				case REP_PROTOCOL_SUCCESS:
6359					res = cpg;
6360					break;
6361
6362				case REP_PROTOCOL_FAIL_TYPE_MISMATCH:
6363					/* Nevermind. */
6364					rc_node_destroy(cpg);
6365					rc_node_rele(pg);
6366					rc_node_rele(res);
6367					break;
6368
6369				case REP_PROTOCOL_FAIL_NO_RESOURCES:
6370					rc_node_destroy(cpg);
6371					(void) pthread_mutex_unlock(
6372					    &np->rn_lock);
6373					rc_node_rele(res);
6374					rc_node_rele(pg);
6375					rc_node_clear(out, 0);
6376					return (REP_PROTOCOL_FAIL_NO_RESOURCES);
6377
6378				default:
6379					assert(0);
6380					abort();
6381				}
6382			}
6383#else
6384#error This code must be updated.
6385#endif
6386		}
6387
6388		rc_node_hold(res);
6389		(void) pthread_mutex_unlock(&np->rn_lock);
6390		break;
6391	}
6392	rc_node_assign(out, res);
6393
6394	if (res == NULL)
6395		return (REP_PROTOCOL_DONE);
6396	rc_node_rele(res);
6397	return (REP_PROTOCOL_SUCCESS);
6398}
6399
6400void
6401rc_iter_destroy(rc_node_iter_t **nipp)
6402{
6403	rc_node_iter_t *nip = *nipp;
6404	rc_node_t *np;
6405
6406	if (nip == NULL)
6407		return;				/* already freed */
6408
6409	np = nip->rni_parent;
6410
6411	if (nip->rni_filter_arg != NULL)
6412		free(nip->rni_filter_arg);
6413	nip->rni_filter_arg = NULL;
6414
6415	if (nip->rni_type == REP_PROTOCOL_ENTITY_VALUE ||
6416	    nip->rni_iter != NULL) {
6417		if (nip->rni_clevel < 0)
6418			(void) pthread_mutex_lock(&np->rn_lock);
6419		else
6420			(void) pthread_mutex_lock(
6421			    &np->rn_cchain[nip->rni_clevel]->rn_lock);
6422		rc_iter_end(nip);		/* release walker and lock */
6423	}
6424	nip->rni_parent = NULL;
6425
6426	uu_free(nip);
6427	*nipp = NULL;
6428}
6429
6430int
6431rc_node_setup_tx(rc_node_ptr_t *npp, rc_node_ptr_t *txp)
6432{
6433	rc_node_t *np;
6434	permcheck_t *pcp;
6435	int ret;
6436	rc_auth_state_t authorized = RC_AUTH_UNKNOWN;
6437	char *auth_string = NULL;
6438
6439	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
6440
6441	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
6442		rc_node_rele(np);
6443		np = np->rn_cchain[0];
6444		RC_NODE_CHECK_AND_HOLD(np);
6445	}
6446
6447	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
6448		rc_node_rele(np);
6449		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
6450	}
6451
6452	if (np->rn_id.rl_ids[ID_SNAPSHOT] != 0) {
6453		rc_node_rele(np);
6454		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
6455	}
6456
6457#ifdef NATIVE_BUILD
6458	if (client_is_privileged())
6459		goto skip_checks;
6460	rc_node_rele(np);
6461	return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
6462#else
6463	if (is_main_repository == 0)
6464		goto skip_checks;
6465
6466	/* permission check */
6467	pcp = pc_create();
6468	if (pcp == NULL) {
6469		rc_node_rele(np);
6470		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
6471	}
6472
6473	if (np->rn_id.rl_ids[ID_INSTANCE] != 0 &&	/* instance pg */
6474	    ((strcmp(np->rn_name, AUTH_PG_ACTIONS) == 0 &&
6475	    strcmp(np->rn_type, AUTH_PG_ACTIONS_TYPE) == 0) ||
6476	    (strcmp(np->rn_name, AUTH_PG_GENERAL_OVR) == 0 &&
6477	    strcmp(np->rn_type, AUTH_PG_GENERAL_OVR_TYPE) == 0))) {
6478		rc_node_t *instn;
6479
6480		/* solaris.smf.manage can be used. */
6481		ret = perm_add_enabling(pcp, AUTH_MANAGE);
6482
6483		if (ret != REP_PROTOCOL_SUCCESS) {
6484			pc_free(pcp);
6485			rc_node_rele(np);
6486			return (ret);
6487		}
6488
6489		/* general/action_authorization values can be used. */
6490		ret = rc_node_parent(np, &instn);
6491		if (ret != REP_PROTOCOL_SUCCESS) {
6492			assert(ret == REP_PROTOCOL_FAIL_DELETED);
6493			rc_node_rele(np);
6494			pc_free(pcp);
6495			return (REP_PROTOCOL_FAIL_DELETED);
6496		}
6497
6498		assert(instn->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE);
6499
6500		ret = perm_add_inst_action_auth(pcp, instn);
6501		rc_node_rele(instn);
6502		switch (ret) {
6503		case REP_PROTOCOL_SUCCESS:
6504			break;
6505
6506		case REP_PROTOCOL_FAIL_DELETED:
6507		case REP_PROTOCOL_FAIL_NO_RESOURCES:
6508			rc_node_rele(np);
6509			pc_free(pcp);
6510			return (ret);
6511
6512		default:
6513			bad_error("perm_add_inst_action_auth", ret);
6514		}
6515
6516		if (strcmp(np->rn_name, AUTH_PG_ACTIONS) == 0)
6517			authorized = RC_AUTH_PASSED; /* No check on commit. */
6518	} else {
6519		ret = perm_add_enabling(pcp, AUTH_MODIFY);
6520
6521		if (ret == REP_PROTOCOL_SUCCESS) {
6522			/* propertygroup-type-specific authorization */
6523			/* no locking because rn_type won't change anyway */
6524			const char * const auth =
6525			    perm_auth_for_pgtype(np->rn_type);
6526
6527			if (auth != NULL)
6528				ret = perm_add_enabling(pcp, auth);
6529		}
6530
6531		if (ret == REP_PROTOCOL_SUCCESS)
6532			/* propertygroup/transaction-type-specific auths */
6533			ret =
6534			    perm_add_enabling_values(pcp, np, AUTH_PROP_VALUE);
6535
6536		if (ret == REP_PROTOCOL_SUCCESS)
6537			ret =
6538			    perm_add_enabling_values(pcp, np, AUTH_PROP_MODIFY);
6539
6540		/* AUTH_MANAGE can manipulate general/AUTH_PROP_ACTION */
6541		if (ret == REP_PROTOCOL_SUCCESS &&
6542		    strcmp(np->rn_name, AUTH_PG_GENERAL) == 0 &&
6543		    strcmp(np->rn_type, AUTH_PG_GENERAL_TYPE) == 0)
6544			ret = perm_add_enabling(pcp, AUTH_MANAGE);
6545
6546		if (ret != REP_PROTOCOL_SUCCESS) {
6547			pc_free(pcp);
6548			rc_node_rele(np);
6549			return (ret);
6550		}
6551	}
6552
6553	ret = perm_granted(pcp);
6554	/*
6555	 * Copy out the authorization string before freeing pcp.
6556	 */
6557	if (ret >= 0) {
6558		auth_string = strdup(pcp->pc_auth_string);
6559	}
6560	pc_free(pcp);
6561	if ((auth_string == NULL) || (ret < 0)) {
6562		rc_node_rele(np);
6563		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
6564	}
6565
6566	if (ret == 0) {
6567		/*
6568		 * If we get here, the authorization failed.
6569		 * Unfortunately, we don't have enough information at this
6570		 * point to generate the security audit events.  We'll only
6571		 * get that information when the client tries to commit the
6572		 * event.  Thus, we'll remember the failed authorization,
6573		 * so that we can generate the audit events later.
6574		 */
6575		authorized = RC_AUTH_FAILED;
6576	}
6577#endif /* NATIVE_BUILD */
6578
6579skip_checks:
6580	rc_node_assign(txp, np);
6581	txp->rnp_authorized = authorized;
6582	if (authorized != RC_AUTH_UNKNOWN) {
6583		/* Save the authorization string. */
6584		if (txp->rnp_auth_string != NULL)
6585			free((void *)txp->rnp_auth_string);
6586		txp->rnp_auth_string = auth_string;
6587		auth_string = NULL;	/* Don't free until done with txp. */
6588	}
6589
6590	rc_node_rele(np);
6591	if (auth_string != NULL)
6592		free(auth_string);
6593	return (REP_PROTOCOL_SUCCESS);
6594}
6595
6596/*
6597 * Return 1 if the given transaction commands only modify the values of
6598 * properties other than "modify_authorization".  Return -1 if any of the
6599 * commands are invalid, and 0 otherwise.
6600 */
6601static int
6602tx_allow_value(const void *cmds_arg, size_t cmds_sz, rc_node_t *pg)
6603{
6604	const struct rep_protocol_transaction_cmd *cmds;
6605	uintptr_t loc;
6606	uint32_t sz;
6607	rc_node_t *prop;
6608	boolean_t ok;
6609
6610	assert(!MUTEX_HELD(&pg->rn_lock));
6611
6612	loc = (uintptr_t)cmds_arg;
6613
6614	while (cmds_sz > 0) {
6615		cmds = (struct rep_protocol_transaction_cmd *)loc;
6616
6617		if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
6618			return (-1);
6619
6620		sz = cmds->rptc_size;
6621		if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
6622			return (-1);
6623
6624		sz = TX_SIZE(sz);
6625		if (sz > cmds_sz)
6626			return (-1);
6627
6628		switch (cmds[0].rptc_action) {
6629		case REP_PROTOCOL_TX_ENTRY_CLEAR:
6630			break;
6631
6632		case REP_PROTOCOL_TX_ENTRY_REPLACE:
6633			/* Check type */
6634			(void) pthread_mutex_lock(&pg->rn_lock);
6635			if (rc_node_find_named_child(pg,
6636			    (const char *)cmds[0].rptc_data,
6637			    REP_PROTOCOL_ENTITY_PROPERTY, &prop) ==
6638			    REP_PROTOCOL_SUCCESS) {
6639				ok = (prop != NULL &&
6640				    prop->rn_valtype == cmds[0].rptc_type);
6641			} else {
6642				/* Return more particular error? */
6643				ok = B_FALSE;
6644			}
6645			(void) pthread_mutex_unlock(&pg->rn_lock);
6646			if (ok)
6647				break;
6648			return (0);
6649
6650		default:
6651			return (0);
6652		}
6653
6654		if (strcmp((const char *)cmds[0].rptc_data, AUTH_PROP_MODIFY)
6655		    == 0)
6656			return (0);
6657
6658		loc += sz;
6659		cmds_sz -= sz;
6660	}
6661
6662	return (1);
6663}
6664
6665/*
6666 * Return 1 if any of the given transaction commands affect
6667 * "action_authorization".  Return -1 if any of the commands are invalid and
6668 * 0 in all other cases.
6669 */
6670static int
6671tx_modifies_action(const void *cmds_arg, size_t cmds_sz)
6672{
6673	const struct rep_protocol_transaction_cmd *cmds;
6674	uintptr_t loc;
6675	uint32_t sz;
6676
6677	loc = (uintptr_t)cmds_arg;
6678
6679	while (cmds_sz > 0) {
6680		cmds = (struct rep_protocol_transaction_cmd *)loc;
6681
6682		if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
6683			return (-1);
6684
6685		sz = cmds->rptc_size;
6686		if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
6687			return (-1);
6688
6689		sz = TX_SIZE(sz);
6690		if (sz > cmds_sz)
6691			return (-1);
6692
6693		if (strcmp((const char *)cmds[0].rptc_data, AUTH_PROP_ACTION)
6694		    == 0)
6695			return (1);
6696
6697		loc += sz;
6698		cmds_sz -= sz;
6699	}
6700
6701	return (0);
6702}
6703
6704/*
6705 * Returns 1 if the transaction commands only modify properties named
6706 * 'enabled'.
6707 */
6708static int
6709tx_only_enabled(const void *cmds_arg, size_t cmds_sz)
6710{
6711	const struct rep_protocol_transaction_cmd *cmd;
6712	uintptr_t loc;
6713	uint32_t sz;
6714
6715	loc = (uintptr_t)cmds_arg;
6716
6717	while (cmds_sz > 0) {
6718		cmd = (struct rep_protocol_transaction_cmd *)loc;
6719
6720		if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
6721			return (-1);
6722
6723		sz = cmd->rptc_size;
6724		if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
6725			return (-1);
6726
6727		sz = TX_SIZE(sz);
6728		if (sz > cmds_sz)
6729			return (-1);
6730
6731		if (strcmp((const char *)cmd->rptc_data, AUTH_PROP_ENABLED)
6732		    != 0)
6733			return (0);
6734
6735		loc += sz;
6736		cmds_sz -= sz;
6737	}
6738
6739	return (1);
6740}
6741
6742int
6743rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz)
6744{
6745	rc_node_t *np = txp->rnp_node;
6746	rc_node_t *pp;
6747	rc_node_t *nnp;
6748	rc_node_pg_notify_t *pnp;
6749	int rc;
6750	permcheck_t *pcp;
6751	int granted, normal;
6752	char *pg_fmri = NULL;
6753	char *auth_string = NULL;
6754	int auth_status = ADT_SUCCESS;
6755	int auth_ret_value = ADT_SUCCESS;
6756	size_t sz_out;
6757	int tx_flag = 1;
6758	tx_commit_data_t *tx_data = NULL;
6759
6760	RC_NODE_CHECK(np);
6761
6762	if ((txp->rnp_authorized != RC_AUTH_UNKNOWN) &&
6763	    (txp->rnp_auth_string != NULL)) {
6764		auth_string = strdup(txp->rnp_auth_string);
6765		if (auth_string == NULL)
6766			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
6767	}
6768
6769	if ((txp->rnp_authorized == RC_AUTH_UNKNOWN) &&
6770	    is_main_repository) {
6771#ifdef NATIVE_BUILD
6772		if (!client_is_privileged()) {
6773			return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
6774		}
6775#else
6776		/* permission check: depends on contents of transaction */
6777		pcp = pc_create();
6778		if (pcp == NULL)
6779			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
6780
6781		/* If normal is cleared, we won't do the normal checks. */
6782		normal = 1;
6783		rc = REP_PROTOCOL_SUCCESS;
6784
6785		if (strcmp(np->rn_name, AUTH_PG_GENERAL) == 0 &&
6786		    strcmp(np->rn_type, AUTH_PG_GENERAL_TYPE) == 0) {
6787			/* Touching general[framework]/action_authorization? */
6788			rc = tx_modifies_action(cmds, cmds_sz);
6789			if (rc == -1) {
6790				pc_free(pcp);
6791				return (REP_PROTOCOL_FAIL_BAD_REQUEST);
6792			}
6793
6794			if (rc) {
6795				/* Yes: only AUTH_MANAGE can be used. */
6796				rc = perm_add_enabling(pcp, AUTH_MANAGE);
6797				normal = 0;
6798			} else {
6799				rc = REP_PROTOCOL_SUCCESS;
6800			}
6801		} else if (np->rn_id.rl_ids[ID_INSTANCE] != 0 &&
6802		    strcmp(np->rn_name, AUTH_PG_GENERAL_OVR) == 0 &&
6803		    strcmp(np->rn_type, AUTH_PG_GENERAL_OVR_TYPE) == 0) {
6804			rc_node_t *instn;
6805
6806			rc = tx_only_enabled(cmds, cmds_sz);
6807			if (rc == -1) {
6808				pc_free(pcp);
6809				return (REP_PROTOCOL_FAIL_BAD_REQUEST);
6810			}
6811
6812			if (rc) {
6813				rc = rc_node_parent(np, &instn);
6814				if (rc != REP_PROTOCOL_SUCCESS) {
6815					assert(rc == REP_PROTOCOL_FAIL_DELETED);
6816					pc_free(pcp);
6817					return (rc);
6818				}
6819
6820				assert(instn->rn_id.rl_type ==
6821				    REP_PROTOCOL_ENTITY_INSTANCE);
6822
6823				rc = perm_add_inst_action_auth(pcp, instn);
6824				rc_node_rele(instn);
6825				switch (rc) {
6826				case REP_PROTOCOL_SUCCESS:
6827					break;
6828
6829				case REP_PROTOCOL_FAIL_DELETED:
6830				case REP_PROTOCOL_FAIL_NO_RESOURCES:
6831					pc_free(pcp);
6832					return (rc);
6833
6834				default:
6835					bad_error("perm_add_inst_action_auth",
6836					    rc);
6837				}
6838			} else {
6839				rc = REP_PROTOCOL_SUCCESS;
6840			}
6841		}
6842
6843		if (rc == REP_PROTOCOL_SUCCESS && normal) {
6844			rc = perm_add_enabling(pcp, AUTH_MODIFY);
6845
6846			if (rc == REP_PROTOCOL_SUCCESS) {
6847				/* Add pgtype-specific authorization. */
6848				const char * const auth =
6849				    perm_auth_for_pgtype(np->rn_type);
6850
6851				if (auth != NULL)
6852					rc = perm_add_enabling(pcp, auth);
6853			}
6854
6855			/* Add pg-specific modify_authorization auths. */
6856			if (rc == REP_PROTOCOL_SUCCESS)
6857				rc = perm_add_enabling_values(pcp, np,
6858				    AUTH_PROP_MODIFY);
6859
6860			/* If value_authorization values are ok, add them. */
6861			if (rc == REP_PROTOCOL_SUCCESS) {
6862				rc = tx_allow_value(cmds, cmds_sz, np);
6863				if (rc == -1)
6864					rc = REP_PROTOCOL_FAIL_BAD_REQUEST;
6865				else if (rc)
6866					rc = perm_add_enabling_values(pcp, np,
6867					    AUTH_PROP_VALUE);
6868			}
6869		}
6870
6871		if (rc == REP_PROTOCOL_SUCCESS) {
6872			granted = perm_granted(pcp);
6873			if (granted < 0) {
6874				rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
6875			} else {
6876				/*
6877				 * Copy out the authorization string before
6878				 * freeing pcp.
6879				 */
6880				auth_string = strdup(pcp->pc_auth_string);
6881				if (auth_string == NULL)
6882					rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
6883			}
6884		}
6885
6886		pc_free(pcp);
6887
6888		if (rc != REP_PROTOCOL_SUCCESS)
6889			goto cleanout;
6890
6891		if (!granted) {
6892			auth_status = ADT_FAILURE;
6893			auth_ret_value = ADT_FAIL_VALUE_AUTH;
6894			tx_flag = 0;
6895		}
6896#endif /* NATIVE_BUILD */
6897	} else if (txp->rnp_authorized == RC_AUTH_FAILED) {
6898		auth_status = ADT_FAILURE;
6899		auth_ret_value = ADT_FAIL_VALUE_AUTH;
6900		tx_flag = 0;
6901	}
6902
6903	pg_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
6904	if (pg_fmri == NULL) {
6905		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
6906		goto cleanout;
6907	}
6908	if ((rc = rc_node_get_fmri_or_fragment(np, pg_fmri,
6909	    REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
6910		goto cleanout;
6911	}
6912
6913	/*
6914	 * Parse the transaction commands into a useful form.
6915	 */
6916	if ((rc = tx_commit_data_new(cmds, cmds_sz, &tx_data)) !=
6917	    REP_PROTOCOL_SUCCESS) {
6918		goto cleanout;
6919	}
6920
6921	if (tx_flag == 0) {
6922		/* Authorization failed.  Generate audit events. */
6923		generate_property_events(tx_data, pg_fmri, auth_string,
6924		    auth_status, auth_ret_value);
6925		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
6926		goto cleanout;
6927	}
6928
6929	nnp = rc_node_alloc();
6930	if (nnp == NULL) {
6931		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
6932		goto cleanout;
6933	}
6934
6935	nnp->rn_id = np->rn_id;			/* structure assignment */
6936	nnp->rn_hash = np->rn_hash;
6937	nnp->rn_name = strdup(np->rn_name);
6938	nnp->rn_type = strdup(np->rn_type);
6939	nnp->rn_pgflags = np->rn_pgflags;
6940
6941	nnp->rn_flags = RC_NODE_IN_TX | RC_NODE_USING_PARENT;
6942
6943	if (nnp->rn_name == NULL || nnp->rn_type == NULL) {
6944		rc_node_destroy(nnp);
6945		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
6946		goto cleanout;
6947	}
6948
6949	(void) pthread_mutex_lock(&np->rn_lock);
6950
6951	/*
6952	 * We must have all of the old properties in the cache, or the
6953	 * database deletions could cause inconsistencies.
6954	 */
6955	if ((rc = rc_node_fill_children(np, REP_PROTOCOL_ENTITY_PROPERTY)) !=
6956	    REP_PROTOCOL_SUCCESS) {
6957		(void) pthread_mutex_unlock(&np->rn_lock);
6958		rc_node_destroy(nnp);
6959		goto cleanout;
6960	}
6961
6962	if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
6963		(void) pthread_mutex_unlock(&np->rn_lock);
6964		rc_node_destroy(nnp);
6965		rc = REP_PROTOCOL_FAIL_DELETED;
6966		goto cleanout;
6967	}
6968
6969	if (np->rn_flags & RC_NODE_OLD) {
6970		rc_node_rele_flag(np, RC_NODE_USING_PARENT);
6971		(void) pthread_mutex_unlock(&np->rn_lock);
6972		rc_node_destroy(nnp);
6973		rc = REP_PROTOCOL_FAIL_NOT_LATEST;
6974		goto cleanout;
6975	}
6976
6977	pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
6978	if (pp == NULL) {
6979		/* our parent is gone, we're going next... */
6980		rc_node_destroy(nnp);
6981		(void) pthread_mutex_lock(&np->rn_lock);
6982		if (np->rn_flags & RC_NODE_OLD) {
6983			(void) pthread_mutex_unlock(&np->rn_lock);
6984			rc = REP_PROTOCOL_FAIL_NOT_LATEST;
6985			goto cleanout;
6986		}
6987		(void) pthread_mutex_unlock(&np->rn_lock);
6988		rc = REP_PROTOCOL_FAIL_DELETED;
6989		goto cleanout;
6990	}
6991	(void) pthread_mutex_unlock(&pp->rn_lock);
6992
6993	/*
6994	 * prepare for the transaction
6995	 */
6996	(void) pthread_mutex_lock(&np->rn_lock);
6997	if (!rc_node_hold_flag(np, RC_NODE_IN_TX)) {
6998		(void) pthread_mutex_unlock(&np->rn_lock);
6999		(void) pthread_mutex_lock(&pp->rn_lock);
7000		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
7001		(void) pthread_mutex_unlock(&pp->rn_lock);
7002		rc_node_destroy(nnp);
7003		rc = REP_PROTOCOL_FAIL_DELETED;
7004		goto cleanout;
7005	}
7006	nnp->rn_gen_id = np->rn_gen_id;
7007	(void) pthread_mutex_unlock(&np->rn_lock);
7008
7009	/* Sets nnp->rn_gen_id on success. */
7010	rc = object_tx_commit(&np->rn_id, tx_data, &nnp->rn_gen_id);
7011
7012	(void) pthread_mutex_lock(&np->rn_lock);
7013	if (rc != REP_PROTOCOL_SUCCESS) {
7014		rc_node_rele_flag(np, RC_NODE_IN_TX);
7015		(void) pthread_mutex_unlock(&np->rn_lock);
7016		(void) pthread_mutex_lock(&pp->rn_lock);
7017		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
7018		(void) pthread_mutex_unlock(&pp->rn_lock);
7019		rc_node_destroy(nnp);
7020		rc_node_clear(txp, 0);
7021		if (rc == REP_PROTOCOL_DONE)
7022			rc = REP_PROTOCOL_SUCCESS; /* successful empty tx */
7023		goto cleanout;
7024	}
7025
7026	/*
7027	 * Notify waiters
7028	 */
7029	(void) pthread_mutex_lock(&rc_pg_notify_lock);
7030	while ((pnp = uu_list_first(np->rn_pg_notify_list)) != NULL)
7031		rc_pg_notify_fire(pnp);
7032	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
7033
7034	np->rn_flags |= RC_NODE_OLD;
7035	(void) pthread_mutex_unlock(&np->rn_lock);
7036
7037	rc_notify_remove_node(np);
7038
7039	/*
7040	 * replace np with nnp
7041	 */
7042	rc_node_relink_child(pp, np, nnp);
7043
7044	/*
7045	 * all done -- clear the transaction.
7046	 */
7047	rc_node_clear(txp, 0);
7048	generate_property_events(tx_data, pg_fmri, auth_string,
7049	    auth_status, auth_ret_value);
7050
7051	rc = REP_PROTOCOL_SUCCESS;
7052
7053cleanout:
7054	free(auth_string);
7055	free(pg_fmri);
7056	tx_commit_data_free(tx_data);
7057	return (rc);
7058}
7059
7060void
7061rc_pg_notify_init(rc_node_pg_notify_t *pnp)
7062{
7063	uu_list_node_init(pnp, &pnp->rnpn_node, rc_pg_notify_pool);
7064	pnp->rnpn_pg = NULL;
7065	pnp->rnpn_fd = -1;
7066}
7067
7068int
7069rc_pg_notify_setup(rc_node_pg_notify_t *pnp, rc_node_ptr_t *npp, int fd)
7070{
7071	rc_node_t *np;
7072
7073	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
7074
7075	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
7076		(void) pthread_mutex_unlock(&np->rn_lock);
7077		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
7078	}
7079
7080	/*
7081	 * wait for any transaction in progress to complete
7082	 */
7083	if (!rc_node_wait_flag(np, RC_NODE_IN_TX)) {
7084		(void) pthread_mutex_unlock(&np->rn_lock);
7085		return (REP_PROTOCOL_FAIL_DELETED);
7086	}
7087
7088	if (np->rn_flags & RC_NODE_OLD) {
7089		(void) pthread_mutex_unlock(&np->rn_lock);
7090		return (REP_PROTOCOL_FAIL_NOT_LATEST);
7091	}
7092
7093	(void) pthread_mutex_lock(&rc_pg_notify_lock);
7094	rc_pg_notify_fire(pnp);
7095	pnp->rnpn_pg = np;
7096	pnp->rnpn_fd = fd;
7097	(void) uu_list_insert_after(np->rn_pg_notify_list, NULL, pnp);
7098	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
7099
7100	(void) pthread_mutex_unlock(&np->rn_lock);
7101	return (REP_PROTOCOL_SUCCESS);
7102}
7103
7104void
7105rc_pg_notify_fini(rc_node_pg_notify_t *pnp)
7106{
7107	(void) pthread_mutex_lock(&rc_pg_notify_lock);
7108	rc_pg_notify_fire(pnp);
7109	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
7110
7111	uu_list_node_fini(pnp, &pnp->rnpn_node, rc_pg_notify_pool);
7112}
7113
7114void
7115rc_notify_info_init(rc_notify_info_t *rnip)
7116{
7117	int i;
7118
7119	uu_list_node_init(rnip, &rnip->rni_list_node, rc_notify_info_pool);
7120	uu_list_node_init(&rnip->rni_notify, &rnip->rni_notify.rcn_list_node,
7121	    rc_notify_pool);
7122
7123	rnip->rni_notify.rcn_node = NULL;
7124	rnip->rni_notify.rcn_info = rnip;
7125
7126	bzero(rnip->rni_namelist, sizeof (rnip->rni_namelist));
7127	bzero(rnip->rni_typelist, sizeof (rnip->rni_typelist));
7128
7129	(void) pthread_cond_init(&rnip->rni_cv, NULL);
7130
7131	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
7132		rnip->rni_namelist[i] = NULL;
7133		rnip->rni_typelist[i] = NULL;
7134	}
7135}
7136
7137static void
7138rc_notify_info_insert_locked(rc_notify_info_t *rnip)
7139{
7140	assert(MUTEX_HELD(&rc_pg_notify_lock));
7141
7142	assert(!(rnip->rni_flags & RC_NOTIFY_ACTIVE));
7143
7144	rnip->rni_flags |= RC_NOTIFY_ACTIVE;
7145	(void) uu_list_insert_after(rc_notify_info_list, NULL, rnip);
7146	(void) uu_list_insert_before(rc_notify_list, NULL, &rnip->rni_notify);
7147}
7148
7149static void
7150rc_notify_info_remove_locked(rc_notify_info_t *rnip)
7151{
7152	rc_notify_t *me = &rnip->rni_notify;
7153	rc_notify_t *np;
7154
7155	assert(MUTEX_HELD(&rc_pg_notify_lock));
7156
7157	assert(rnip->rni_flags & RC_NOTIFY_ACTIVE);
7158
7159	assert(!(rnip->rni_flags & RC_NOTIFY_DRAIN));
7160	rnip->rni_flags |= RC_NOTIFY_DRAIN;
7161	(void) pthread_cond_broadcast(&rnip->rni_cv);
7162
7163	(void) uu_list_remove(rc_notify_info_list, rnip);
7164
7165	/*
7166	 * clean up any notifications at the beginning of the list
7167	 */
7168	if (uu_list_first(rc_notify_list) == me) {
7169		while ((np = uu_list_next(rc_notify_list, me)) != NULL &&
7170		    np->rcn_info == NULL)
7171			rc_notify_remove_locked(np);
7172	}
7173	(void) uu_list_remove(rc_notify_list, me);
7174
7175	while (rnip->rni_waiters) {
7176		(void) pthread_cond_broadcast(&rc_pg_notify_cv);
7177		(void) pthread_cond_broadcast(&rnip->rni_cv);
7178		(void) pthread_cond_wait(&rnip->rni_cv, &rc_pg_notify_lock);
7179	}
7180
7181	rnip->rni_flags &= ~(RC_NOTIFY_DRAIN | RC_NOTIFY_ACTIVE);
7182}
7183
7184static int
7185rc_notify_info_add_watch(rc_notify_info_t *rnip, const char **arr,
7186    const char *name)
7187{
7188	int i;
7189	int rc;
7190	char *f;
7191
7192	rc = rc_check_type_name(REP_PROTOCOL_ENTITY_PROPERTYGRP, name);
7193	if (rc != REP_PROTOCOL_SUCCESS)
7194		return (rc);
7195
7196	f = strdup(name);
7197	if (f == NULL)
7198		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
7199
7200	(void) pthread_mutex_lock(&rc_pg_notify_lock);
7201
7202	while (rnip->rni_flags & RC_NOTIFY_EMPTYING)
7203		(void) pthread_cond_wait(&rnip->rni_cv, &rc_pg_notify_lock);
7204
7205	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++)
7206		if (arr[i] == NULL)
7207			break;
7208
7209	if (i == RC_NOTIFY_MAX_NAMES) {
7210		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
7211		free(f);
7212		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
7213	}
7214
7215	arr[i] = f;
7216	if (!(rnip->rni_flags & RC_NOTIFY_ACTIVE))
7217		rc_notify_info_insert_locked(rnip);
7218
7219	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
7220	return (REP_PROTOCOL_SUCCESS);
7221}
7222
7223int
7224rc_notify_info_add_name(rc_notify_info_t *rnip, const char *name)
7225{
7226	return (rc_notify_info_add_watch(rnip, rnip->rni_namelist, name));
7227}
7228
7229int
7230rc_notify_info_add_type(rc_notify_info_t *rnip, const char *type)
7231{
7232	return (rc_notify_info_add_watch(rnip, rnip->rni_typelist, type));
7233}
7234
7235/*
7236 * Wait for and report an event of interest to rnip, a notification client
7237 */
7238int
7239rc_notify_info_wait(rc_notify_info_t *rnip, rc_node_ptr_t *out,
7240    char *outp, size_t sz)
7241{
7242	rc_notify_t *np;
7243	rc_notify_t *me = &rnip->rni_notify;
7244	rc_node_t *nnp;
7245	rc_notify_delete_t *ndp;
7246
7247	int am_first_info;
7248
7249	if (sz > 0)
7250		outp[0] = 0;
7251
7252	(void) pthread_mutex_lock(&rc_pg_notify_lock);
7253
7254	while ((rnip->rni_flags & (RC_NOTIFY_ACTIVE | RC_NOTIFY_DRAIN)) ==
7255	    RC_NOTIFY_ACTIVE) {
7256		/*
7257		 * If I'm first on the notify list, it is my job to
7258		 * clean up any notifications I pass by.  I can't do that
7259		 * if someone is blocking the list from removals, so I
7260		 * have to wait until they have all drained.
7261		 */
7262		am_first_info = (uu_list_first(rc_notify_list) == me);
7263		if (am_first_info && rc_notify_in_use) {
7264			rnip->rni_waiters++;
7265			(void) pthread_cond_wait(&rc_pg_notify_cv,
7266			    &rc_pg_notify_lock);
7267			rnip->rni_waiters--;
7268			continue;
7269		}
7270
7271		/*
7272		 * Search the list for a node of interest.
7273		 */
7274		np = uu_list_next(rc_notify_list, me);
7275		while (np != NULL && !rc_notify_info_interested(rnip, np)) {
7276			rc_notify_t *next = uu_list_next(rc_notify_list, np);
7277
7278			if (am_first_info) {
7279				if (np->rcn_info) {
7280					/*
7281					 * Passing another client -- stop
7282					 * cleaning up notifications
7283					 */
7284					am_first_info = 0;
7285				} else {
7286					rc_notify_remove_locked(np);
7287				}
7288			}
7289			np = next;
7290		}
7291
7292		/*
7293		 * Nothing of interest -- wait for notification
7294		 */
7295		if (np == NULL) {
7296			rnip->rni_waiters++;
7297			(void) pthread_cond_wait(&rnip->rni_cv,
7298			    &rc_pg_notify_lock);
7299			rnip->rni_waiters--;
7300			continue;
7301		}
7302
7303		/*
7304		 * found something to report -- move myself after the
7305		 * notification and process it.
7306		 */
7307		(void) uu_list_remove(rc_notify_list, me);
7308		(void) uu_list_insert_after(rc_notify_list, np, me);
7309
7310		if ((ndp = np->rcn_delete) != NULL) {
7311			(void) strlcpy(outp, ndp->rnd_fmri, sz);
7312			if (am_first_info)
7313				rc_notify_remove_locked(np);
7314			(void) pthread_mutex_unlock(&rc_pg_notify_lock);
7315			rc_node_clear(out, 0);
7316			return (REP_PROTOCOL_SUCCESS);
7317		}
7318
7319		nnp = np->rcn_node;
7320		assert(nnp != NULL);
7321
7322		/*
7323		 * We can't bump nnp's reference count without grabbing its
7324		 * lock, and rc_pg_notify_lock is a leaf lock.  So we
7325		 * temporarily block all removals to keep nnp from
7326		 * disappearing.
7327		 */
7328		rc_notify_in_use++;
7329		assert(rc_notify_in_use > 0);
7330		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
7331
7332		rc_node_assign(out, nnp);
7333
7334		(void) pthread_mutex_lock(&rc_pg_notify_lock);
7335		assert(rc_notify_in_use > 0);
7336		rc_notify_in_use--;
7337		if (am_first_info)
7338			rc_notify_remove_locked(np);
7339		if (rc_notify_in_use == 0)
7340			(void) pthread_cond_broadcast(&rc_pg_notify_cv);
7341		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
7342
7343		return (REP_PROTOCOL_SUCCESS);
7344	}
7345	/*
7346	 * If we're the last one out, let people know it's clear.
7347	 */
7348	if (rnip->rni_waiters == 0)
7349		(void) pthread_cond_broadcast(&rnip->rni_cv);
7350	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
7351	return (REP_PROTOCOL_DONE);
7352}
7353
7354static void
7355rc_notify_info_reset(rc_notify_info_t *rnip)
7356{
7357	int i;
7358
7359	(void) pthread_mutex_lock(&rc_pg_notify_lock);
7360	if (rnip->rni_flags & RC_NOTIFY_ACTIVE)
7361		rc_notify_info_remove_locked(rnip);
7362	assert(!(rnip->rni_flags & (RC_NOTIFY_DRAIN | RC_NOTIFY_EMPTYING)));
7363	rnip->rni_flags |= RC_NOTIFY_EMPTYING;
7364	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
7365
7366	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
7367		if (rnip->rni_namelist[i] != NULL) {
7368			free((void *)rnip->rni_namelist[i]);
7369			rnip->rni_namelist[i] = NULL;
7370		}
7371		if (rnip->rni_typelist[i] != NULL) {
7372			free((void *)rnip->rni_typelist[i]);
7373			rnip->rni_typelist[i] = NULL;
7374		}
7375	}
7376
7377	(void) pthread_mutex_lock(&rc_pg_notify_lock);
7378	rnip->rni_flags &= ~RC_NOTIFY_EMPTYING;
7379	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
7380}
7381
7382void
7383rc_notify_info_fini(rc_notify_info_t *rnip)
7384{
7385	rc_notify_info_reset(rnip);
7386
7387	uu_list_node_fini(rnip, &rnip->rni_list_node, rc_notify_info_pool);
7388	uu_list_node_fini(&rnip->rni_notify, &rnip->rni_notify.rcn_list_node,
7389	    rc_notify_pool);
7390}
7391