summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorwesolows <none@none>2007-09-13 10:10:42 -0700
committerwesolows <none@none>2007-09-13 10:10:42 -0700
commit3eae19d9cf3390cf5b75e10c9c1945fd36ad856a (patch)
tree2e7dc09a2b8d857b406d077178dc53207a1d6e15 /usr/src
parentdb30663e794986128c960dc83ff43ca474b68a23 (diff)
downloadillumos-joyent-3eae19d9cf3390cf5b75e10c9c1945fd36ad856a.tar.gz
PSARC 2007/177 SMF read-protected property storage
PSARC 2007/519 svccfg(1M) restore 6537749 SMF should support read-protection of data 6538452 svccfg delete leaks memory on syntax error with options 6546699 svccfg archive should be able to be reimported 6559692 svccfg fails to import a manifest with an empty <property> 6597168 startd is setting $SMF_METHOD incorrectly 6597173 allocation failure can induce crash in scf_handle_decode_fmri() 6597183 allocation failure can induce crash in scf_simple_app_props_free() 6597190 scf_simple_app_props_get() can return with unset error code 6598922 rc_node_modify_permission_check() calls perm_granted() with rn_lock held
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/svc/configd/rc_node.c481
-rw-r--r--usr/src/cmd/svc/startd/graph.c7
-rw-r--r--usr/src/cmd/svc/startd/libscf.c25
-rw-r--r--usr/src/cmd/svc/startd/method.c3
-rw-r--r--usr/src/cmd/svc/startd/startd.c9
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg.h20
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg.l12
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg.y75
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_engine.c37
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_help.c15
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_internal.c15
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_libscf.c171
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_xml.c63
-rw-r--r--usr/src/cmd/svc/svcprop/svcprop.c24
-rw-r--r--usr/src/cmd/svc/svcs/explain.c11
-rw-r--r--usr/src/cmd/svc/svcs/svcs.c27
-rw-r--r--usr/src/common/svc/repcache_protocol.h10
-rw-r--r--usr/src/lib/librestart/common/librestart.c8
-rw-r--r--usr/src/lib/libscf/common/lowlevel.c17
-rw-r--r--usr/src/lib/libscf/common/mapfile-vers1
-rw-r--r--usr/src/lib/libscf/common/midlevel.c18
-rw-r--r--usr/src/lib/libscf/inc/libscf_priv.h12
22 files changed, 865 insertions, 196 deletions
diff --git a/usr/src/cmd/svc/configd/rc_node.c b/usr/src/cmd/svc/configd/rc_node.c
index 426f828293..4ef49a0b05 100644
--- a/usr/src/cmd/svc/configd/rc_node.c
+++ b/usr/src/cmd/svc/configd/rc_node.c
@@ -18,6 +18,7 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -67,6 +68,10 @@
* rc_node_{wait,hold}_flag() functions (often via the rc_node_check_*()
* functions & RC_NODE_*() macros), which fail if the object has died.
*
+ * Because name service lookups may take a long time and, more importantly
+ * may trigger additional accesses to the repository, perm_granted() must be
+ * called without holding any locks.
+ *
* An ITER_START for a non-ENTITY_VALUE induces an rc_node_fill_children()
* call via rc_node_setup_iter() to populate the rn_children uu_list of the
* rc_node_t * in question and a call to uu_list_walk_start() on that list. For
@@ -186,6 +191,7 @@
#define AUTH_PROP_ENABLED "enabled"
#define AUTH_PROP_MODIFY "modify_authorization"
#define AUTH_PROP_VALUE "value_authorization"
+#define AUTH_PROP_READ "read_authorization"
/* libsecdb should take care of this. */
#define RBAC_AUTH_SEP ","
@@ -1829,10 +1835,45 @@ rc_node_find_named_child(rc_node_t *np, const char *name, uint32_t type,
return (REP_PROTOCOL_SUCCESS);
}
-#ifndef NATIVE_BUILD
static int rc_node_parent(rc_node_t *, rc_node_t **);
/*
+ * Returns
+ * _INVALID_TYPE - type is invalid
+ * _DELETED - np or an ancestor has been deleted
+ * _NOT_FOUND - no ancestor of specified type exists
+ * _SUCCESS - *app is held
+ */
+static int
+rc_node_find_ancestor(rc_node_t *np, uint32_t type, rc_node_t **app)
+{
+ int ret;
+ rc_node_t *parent, *np_orig;
+
+ if (type >= REP_PROTOCOL_ENTITY_MAX)
+ return (REP_PROTOCOL_FAIL_INVALID_TYPE);
+
+ np_orig = np;
+
+ while (np->rn_id.rl_type > type) {
+ ret = rc_node_parent(np, &parent);
+ if (np != np_orig)
+ rc_node_rele(np);
+ if (ret != REP_PROTOCOL_SUCCESS)
+ return (ret);
+ np = parent;
+ }
+
+ if (np->rn_id.rl_type == type) {
+ *app = parent;
+ return (REP_PROTOCOL_SUCCESS);
+ }
+
+ return (REP_PROTOCOL_FAIL_NOT_FOUND);
+}
+
+#ifndef NATIVE_BUILD
+/*
* If the propname property exists in pg, and it is of type string, add its
* values as authorizations to pcp. pg must not be locked on entry, and it is
* returned unlocked. Returns
@@ -1852,7 +1893,6 @@ perm_add_pg_prop_values(permcheck_t *pcp, rc_node_t *pg, const char *propname)
assert(!MUTEX_HELD(&pg->rn_lock));
assert(pg->rn_id.rl_type == REP_PROTOCOL_ENTITY_PROPERTYGRP);
- assert(pg->rn_id.rl_ids[ID_SNAPSHOT] == 0);
(void) pthread_mutex_lock(&pg->rn_lock);
result = rc_node_find_named_child(pg, propname,
@@ -1958,7 +1998,7 @@ perm_add_ent_prop_values(permcheck_t *pcp, rc_node_t *ent, const char *pgname,
}
/*
- * If pg has a property named propname, and it string typed, add its values as
+ * If pg has a property named propname, and is string typed, add its values as
* authorizations to pcp. If pg has no such property, and its parent is an
* instance, walk up to the service and try doing the same with the property
* of the same name from the property group of the same name. Returns
@@ -1970,50 +2010,42 @@ static int
perm_add_enabling_values(permcheck_t *pcp, rc_node_t *pg, const char *propname)
{
int r;
+ char pgname[REP_PROTOCOL_NAME_LEN + 1];
+ rc_node_t *svc;
+ size_t sz;
r = perm_add_pg_prop_values(pcp, pg, propname);
- if (r == REP_PROTOCOL_FAIL_NOT_FOUND) {
- char pgname[REP_PROTOCOL_NAME_LEN + 1];
- rc_node_t *inst, *svc;
- size_t sz;
-
- assert(!MUTEX_HELD(&pg->rn_lock));
-
- if (pg->rn_id.rl_ids[ID_INSTANCE] == 0) {
- /* not an instance pg */
- return (REP_PROTOCOL_SUCCESS);
- }
-
- sz = strlcpy(pgname, pg->rn_name, sizeof (pgname));
- assert(sz < sizeof (pgname));
+ if (r != REP_PROTOCOL_FAIL_NOT_FOUND)
+ return (r);
- /* get pg's parent */
- r = rc_node_parent(pg, &inst);
- if (r != REP_PROTOCOL_SUCCESS) {
- assert(r == REP_PROTOCOL_FAIL_DELETED);
- return (r);
- }
+ assert(!MUTEX_HELD(&pg->rn_lock));
- assert(inst->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE);
+ if (pg->rn_id.rl_ids[ID_INSTANCE] == 0)
+ return (REP_PROTOCOL_SUCCESS);
- /* get instance's parent */
- r = rc_node_parent(inst, &svc);
- rc_node_rele(inst);
- if (r != REP_PROTOCOL_SUCCESS) {
- assert(r == REP_PROTOCOL_FAIL_DELETED);
- return (r);
- }
+ sz = strlcpy(pgname, pg->rn_name, sizeof (pgname));
+ assert(sz < sizeof (pgname));
- assert(svc->rn_id.rl_type == REP_PROTOCOL_ENTITY_SERVICE);
+ /*
+ * If pg is a child of an instance or snapshot, we want to compose the
+ * authorization property with the service's (if it exists). The
+ * snapshot case applies only to read_authorization. In all other
+ * cases, the pg's parent will be the instance.
+ */
+ r = rc_node_find_ancestor(pg, REP_PROTOCOL_ENTITY_SERVICE, &svc);
+ if (r != REP_PROTOCOL_SUCCESS) {
+ assert(r == REP_PROTOCOL_FAIL_DELETED);
+ return (r);
+ }
+ assert(svc->rn_id.rl_type == REP_PROTOCOL_ENTITY_SERVICE);
- r = perm_add_ent_prop_values(pcp, svc, pgname, NULL, propname);
+ r = perm_add_ent_prop_values(pcp, svc, pgname, NULL, propname);
- rc_node_rele(svc);
+ rc_node_rele(svc);
- if (r == REP_PROTOCOL_FAIL_NOT_FOUND)
- r = REP_PROTOCOL_SUCCESS;
- }
+ if (r == REP_PROTOCOL_FAIL_NOT_FOUND)
+ r = REP_PROTOCOL_SUCCESS;
return (r);
}
@@ -2229,6 +2261,8 @@ rc_scope_parent_scope(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out)
return (REP_PROTOCOL_FAIL_NOT_FOUND);
}
+static int rc_node_pg_check_read_protect(rc_node_t *);
+
/*
* Fails with
* _NOT_SET
@@ -2237,6 +2271,7 @@ rc_scope_parent_scope(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out)
* _NOT_FOUND
* _BAD_REQUEST
* _TRUNCATED
+ * _NO_RESOURCES
*/
int
rc_node_name(rc_node_ptr_t *npp, char *buf, size_t sz, uint32_t answertype,
@@ -2287,6 +2322,26 @@ rc_node_name(rc_node_ptr_t *npp, char *buf, size_t sz, uint32_t answertype,
return (REP_PROTOCOL_FAIL_NOT_FOUND);
actual = strlcpy(buf, np->rn_snaplevel->rsl_instance, sz);
break;
+ case RP_ENTITY_NAME_PGREADPROT:
+ {
+ int ret;
+
+ if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
+ return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
+ ret = rc_node_pg_check_read_protect(np);
+ assert(ret != REP_PROTOCOL_FAIL_TYPE_MISMATCH);
+ switch (ret) {
+ case REP_PROTOCOL_FAIL_PERMISSION_DENIED:
+ actual = snprintf(buf, sz, "1");
+ break;
+ case REP_PROTOCOL_SUCCESS:
+ actual = snprintf(buf, sz, "0");
+ break;
+ default:
+ return (ret);
+ }
+ break;
+ }
default:
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
@@ -2639,10 +2694,12 @@ rc_node_create_child(rc_node_ptr_t *npp, uint32_t type, const char *name,
{
rc_node_t *np;
rc_node_t *cp = NULL;
- int rc;
+ int rc, perm_rc;
rc_node_clear(cpp, 0);
+ perm_rc = rc_node_modify_permission_check();
+
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
/*
@@ -2669,9 +2726,9 @@ rc_node_create_child(rc_node_ptr_t *npp, uint32_t type, const char *name,
return (rc);
}
- if ((rc = rc_node_modify_permission_check()) != REP_PROTOCOL_SUCCESS) {
+ if (perm_rc != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
- return (rc);
+ return (perm_rc);
}
HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_CREATING_CHILD);
@@ -3551,14 +3608,14 @@ rc_attach_snapshot(rc_node_t *np, uint32_t snapid, rc_node_t *parentp)
assert(MUTEX_HELD(&np->rn_lock));
- if ((rc = rc_node_modify_permission_check()) != REP_PROTOCOL_SUCCESS) {
- (void) pthread_mutex_unlock(&np->rn_lock);
- return (rc);
- }
-
np_orig = np;
rc_node_hold_locked(np); /* simplifies the remainder */
+ (void) pthread_mutex_unlock(&np->rn_lock);
+ if ((rc = rc_node_modify_permission_check()) != REP_PROTOCOL_SUCCESS)
+ return (rc);
+ (void) pthread_mutex_lock(&np->rn_lock);
+
/*
* get the latest node, holding RC_NODE_IN_TX to keep the rn_former
* list from changing.
@@ -3710,10 +3767,12 @@ rc_snapshot_take_new(rc_node_ptr_t *npp, const char *svcname,
{
rc_node_t *np;
rc_node_t *outp = NULL;
- int rc;
+ int rc, perm_rc;
rc_node_clear(outpp, 0);
+ perm_rc = rc_node_modify_permission_check();
+
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) {
(void) pthread_mutex_unlock(&np->rn_lock);
@@ -3740,9 +3799,9 @@ rc_snapshot_take_new(rc_node_ptr_t *npp, const char *svcname,
return (rc);
}
- if ((rc = rc_node_modify_permission_check()) != REP_PROTOCOL_SUCCESS) {
+ if (perm_rc != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
- return (rc);
+ return (perm_rc);
}
HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_CREATING_CHILD);
@@ -3806,6 +3865,314 @@ rc_snapshot_attach(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
}
/*
+ * If the pgname property group under ent has type pgtype, and it has a
+ * propname property with type ptype, return _SUCCESS. If pgtype is NULL,
+ * it is not checked. If ent is not a service node, we will return _SUCCESS if
+ * a property meeting the requirements exists in either the instance or its
+ * parent.
+ *
+ * Returns
+ * _SUCCESS - see above
+ * _DELETED - ent or one of its ancestors was deleted
+ * _NO_RESOURCES - no resources
+ * _NOT_FOUND - no matching property was found
+ */
+static int
+rc_svc_prop_exists(rc_node_t *ent, const char *pgname, const char *pgtype,
+ const char *propname, rep_protocol_value_type_t ptype)
+{
+ int ret;
+ rc_node_t *pg = NULL, *spg = NULL, *svc, *prop;
+
+ assert(!MUTEX_HELD(&ent->rn_lock));
+
+ (void) pthread_mutex_lock(&ent->rn_lock);
+ ret = rc_node_find_named_child(ent, pgname,
+ REP_PROTOCOL_ENTITY_PROPERTYGRP, &pg);
+ (void) pthread_mutex_unlock(&ent->rn_lock);
+
+ switch (ret) {
+ case REP_PROTOCOL_SUCCESS:
+ break;
+
+ case REP_PROTOCOL_FAIL_DELETED:
+ case REP_PROTOCOL_FAIL_NO_RESOURCES:
+ return (ret);
+
+ default:
+ bad_error("rc_node_find_named_child", ret);
+ }
+
+ if (ent->rn_id.rl_type != REP_PROTOCOL_ENTITY_SERVICE) {
+ ret = rc_node_find_ancestor(ent, REP_PROTOCOL_ENTITY_SERVICE,
+ &svc);
+ if (ret != REP_PROTOCOL_SUCCESS) {
+ assert(ret == REP_PROTOCOL_FAIL_DELETED);
+ if (pg != NULL)
+ rc_node_rele(pg);
+ return (ret);
+ }
+ assert(svc->rn_id.rl_type == REP_PROTOCOL_ENTITY_SERVICE);
+
+ (void) pthread_mutex_lock(&svc->rn_lock);
+ ret = rc_node_find_named_child(svc, pgname,
+ REP_PROTOCOL_ENTITY_PROPERTYGRP, &spg);
+ (void) pthread_mutex_unlock(&svc->rn_lock);
+
+ rc_node_rele(svc);
+
+ switch (ret) {
+ case REP_PROTOCOL_SUCCESS:
+ break;
+
+ case REP_PROTOCOL_FAIL_DELETED:
+ case REP_PROTOCOL_FAIL_NO_RESOURCES:
+ if (pg != NULL)
+ rc_node_rele(pg);
+ return (ret);
+
+ default:
+ bad_error("rc_node_find_named_child", ret);
+ }
+ }
+
+ if (pg != NULL &&
+ pgtype != NULL && strcmp(pg->rn_type, pgtype) != 0) {
+ rc_node_rele(pg);
+ pg = NULL;
+ }
+
+ if (spg != NULL &&
+ pgtype != NULL && strcmp(spg->rn_type, pgtype) != 0) {
+ rc_node_rele(spg);
+ spg = NULL;
+ }
+
+ if (pg == NULL) {
+ if (spg == NULL)
+ return (REP_PROTOCOL_FAIL_NOT_FOUND);
+ pg = spg;
+ spg = NULL;
+ }
+
+ /*
+ * At this point, pg is non-NULL, and is a property group node of the
+ * correct type. spg, if non-NULL, is also a property group node of
+ * the correct type. Check for the property in pg first, then spg
+ * (if applicable).
+ */
+ (void) pthread_mutex_lock(&pg->rn_lock);
+ ret = rc_node_find_named_child(pg, propname,
+ REP_PROTOCOL_ENTITY_PROPERTY, &prop);
+ (void) pthread_mutex_unlock(&pg->rn_lock);
+ rc_node_rele(pg);
+ switch (ret) {
+ case REP_PROTOCOL_SUCCESS:
+ if (prop != NULL) {
+ if (prop->rn_valtype == ptype) {
+ rc_node_rele(prop);
+ if (spg != NULL)
+ rc_node_rele(spg);
+ return (REP_PROTOCOL_SUCCESS);
+ }
+ rc_node_rele(prop);
+ }
+ break;
+
+ case REP_PROTOCOL_FAIL_NO_RESOURCES:
+ if (spg != NULL)
+ rc_node_rele(spg);
+ return (ret);
+
+ case REP_PROTOCOL_FAIL_DELETED:
+ break;
+
+ default:
+ bad_error("rc_node_find_named_child", ret);
+ }
+
+ if (spg == NULL)
+ return (REP_PROTOCOL_FAIL_NOT_FOUND);
+
+ pg = spg;
+
+ (void) pthread_mutex_lock(&pg->rn_lock);
+ ret = rc_node_find_named_child(pg, propname,
+ REP_PROTOCOL_ENTITY_PROPERTY, &prop);
+ (void) pthread_mutex_unlock(&pg->rn_lock);
+ rc_node_rele(pg);
+ switch (ret) {
+ case REP_PROTOCOL_SUCCESS:
+ if (prop != NULL) {
+ if (prop->rn_valtype == ptype) {
+ rc_node_rele(prop);
+ return (REP_PROTOCOL_SUCCESS);
+ }
+ rc_node_rele(prop);
+ }
+ return (REP_PROTOCOL_FAIL_NOT_FOUND);
+
+ case REP_PROTOCOL_FAIL_NO_RESOURCES:
+ return (ret);
+
+ case REP_PROTOCOL_FAIL_DELETED:
+ return (REP_PROTOCOL_FAIL_NOT_FOUND);
+
+ default:
+ bad_error("rc_node_find_named_child", ret);
+ }
+
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*
+ * Given a property group node, returns _SUCCESS if the property group may
+ * be read without any special authorization.
+ *
+ * Fails with:
+ * _DELETED - np or an ancestor node was deleted
+ * _TYPE_MISMATCH - np does not refer to a property group
+ * _NO_RESOURCES - no resources
+ * _PERMISSION_DENIED - authorization is required
+ */
+static int
+rc_node_pg_check_read_protect(rc_node_t *np)
+{
+ int ret;
+ rc_node_t *ent;
+
+ assert(!MUTEX_HELD(&np->rn_lock));
+
+ if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
+ return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
+
+ if (strcmp(np->rn_type, SCF_GROUP_FRAMEWORK) == 0 ||
+ strcmp(np->rn_type, SCF_GROUP_DEPENDENCY) == 0 ||
+ strcmp(np->rn_type, SCF_GROUP_METHOD) == 0)
+ return (REP_PROTOCOL_SUCCESS);
+
+ ret = rc_node_parent(np, &ent);
+
+ if (ret != REP_PROTOCOL_SUCCESS)
+ return (ret);
+
+ ret = rc_svc_prop_exists(ent, np->rn_name, np->rn_type,
+ AUTH_PROP_READ, REP_PROTOCOL_TYPE_STRING);
+
+ rc_node_rele(ent);
+
+ switch (ret) {
+ case REP_PROTOCOL_FAIL_NOT_FOUND:
+ return (REP_PROTOCOL_SUCCESS);
+ case REP_PROTOCOL_SUCCESS:
+ return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
+ case REP_PROTOCOL_FAIL_DELETED:
+ case REP_PROTOCOL_FAIL_NO_RESOURCES:
+ return (ret);
+ default:
+ bad_error("rc_svc_prop_exists", ret);
+ }
+
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*
+ * Fails with
+ * _DELETED - np's node or parent has been deleted
+ * _TYPE_MISMATCH - np's node is not a property
+ * _NO_RESOURCES - out of memory
+ * _PERMISSION_DENIED - no authorization to read this property's value(s)
+ * _BAD_REQUEST - np's parent is not a property group
+ */
+static int
+rc_node_property_may_read(rc_node_t *np)
+{
+ int ret, granted = 0;
+ rc_node_t *pgp;
+ permcheck_t *pcp;
+
+ if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
+ return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
+
+ if (client_is_privileged())
+ return (REP_PROTOCOL_SUCCESS);
+
+#ifdef NATIVE_BUILD
+ return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
+#else
+ ret = rc_node_parent(np, &pgp);
+
+ if (ret != REP_PROTOCOL_SUCCESS)
+ return (ret);
+
+ if (pgp->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
+ rc_node_rele(pgp);
+ return (REP_PROTOCOL_FAIL_BAD_REQUEST);
+ }
+
+ ret = rc_node_pg_check_read_protect(pgp);
+
+ if (ret != REP_PROTOCOL_FAIL_PERMISSION_DENIED) {
+ rc_node_rele(pgp);
+ return (ret);
+ }
+
+ pcp = pc_create();
+
+ if (pcp == NULL) {
+ rc_node_rele(pgp);
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+ }
+
+ ret = perm_add_enabling(pcp, AUTH_MODIFY);
+
+ if (ret == REP_PROTOCOL_SUCCESS) {
+ const char * const auth =
+ perm_auth_for_pgtype(pgp->rn_type);
+
+ if (auth != NULL)
+ ret = perm_add_enabling(pcp, auth);
+ }
+
+ /*
+ * If you are permitted to modify the value, you may also
+ * read it. This means that both the MODIFY and VALUE
+ * authorizations are acceptable. We don't allow requests
+ * for AUTH_PROP_MODIFY if all you have is $AUTH_PROP_VALUE,
+ * however, to avoid leaking possibly valuable information
+ * since such a user can't change the property anyway.
+ */
+ if (ret == REP_PROTOCOL_SUCCESS)
+ ret = perm_add_enabling_values(pcp, pgp,
+ AUTH_PROP_MODIFY);
+
+ if (ret == REP_PROTOCOL_SUCCESS &&
+ strcmp(np->rn_name, AUTH_PROP_MODIFY) != 0)
+ ret = perm_add_enabling_values(pcp, pgp,
+ AUTH_PROP_VALUE);
+
+ if (ret == REP_PROTOCOL_SUCCESS)
+ ret = perm_add_enabling_values(pcp, pgp,
+ AUTH_PROP_READ);
+
+ rc_node_rele(pgp);
+
+ if (ret == REP_PROTOCOL_SUCCESS) {
+ granted = perm_granted(pcp);
+ if (granted < 0)
+ ret = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ }
+
+ pc_free(pcp);
+
+ if (ret == REP_PROTOCOL_SUCCESS && !granted)
+ ret = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
+
+ return (ret);
+#endif /* NATIVE_BUILD */
+}
+
+/*
* Iteration
*/
static int
@@ -4023,12 +4390,14 @@ rc_node_setup_value_iter(rc_node_ptr_t *npp, rc_node_iter_t **iterp)
/*
* Returns:
+ * _NO_RESOURCES - out of memory
* _NOT_SET - npp is reset
* _DELETED - npp's node has been deleted
* _TYPE_MISMATCH - npp's node is not a property
* _NOT_FOUND - property has no values
* _TRUNCATED - property has >1 values (first is written into out)
* _SUCCESS - property has 1 value (which is written into out)
+ * _PERMISSION_DENIED - no authorization to read property value(s)
*
* We shorten *sz_out to not include anything after the final '\0'.
*/
@@ -4042,6 +4411,13 @@ rc_node_get_property_value(rc_node_ptr_t *npp,
assert(*sz_out == sizeof (*out));
+ RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
+ ret = rc_node_property_may_read(np);
+ rc_node_rele(np);
+
+ if (ret != REP_PROTOCOL_SUCCESS)
+ return (ret);
+
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) {
@@ -4079,6 +4455,7 @@ rc_iter_next_value(rc_node_iter_t *iter,
size_t start;
size_t w;
+ int ret;
rep_protocol_responseid_t result;
@@ -4089,6 +4466,12 @@ rc_iter_next_value(rc_node_iter_t *iter,
if (iter->rni_type != REP_PROTOCOL_ENTITY_VALUE)
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
+ RC_NODE_CHECK(np);
+ ret = rc_node_property_may_read(np);
+
+ if (ret != REP_PROTOCOL_SUCCESS)
+ return (ret);
+
RC_NODE_CHECK_AND_LOCK(np);
vals = np->rn_values;
@@ -4320,7 +4703,7 @@ rc_iter_next(rc_node_iter_t *iter, rc_node_ptr_t *out, uint32_t type)
if (rc == REP_PROTOCOL_SUCCESS) {
iter->rni_iter =
uu_list_walk_start(np->rn_children,
- UU_WALK_ROBUST);
+ UU_WALK_ROBUST);
if (iter->rni_iter == NULL)
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
diff --git a/usr/src/cmd/svc/startd/graph.c b/usr/src/cmd/svc/startd/graph.c
index d70d4bf2f7..f877b36542 100644
--- a/usr/src/cmd/svc/startd/graph.c
+++ b/usr/src/cmd/svc/startd/graph.c
@@ -1953,7 +1953,7 @@ rebound:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
- bad_error("scf_iter_service_instances", scf_error())
+ bad_error("scf_iter_service_instances", scf_error());
}
}
@@ -3381,9 +3381,9 @@ sulogin_thread(void *unused)
assert(sulogin_thread_running);
- do
+ do {
(void) run_sulogin("Console login service(s) cannot run\n");
- while (!can_come_up());
+ } while (!can_come_up());
sulogin_thread_running = B_FALSE;
MUTEX_UNLOCK(&dgraph_lock);
@@ -5595,6 +5595,7 @@ process_actions(scf_handle_t *h, scf_propertygroup_t *pg, scf_instance_t *inst)
continue;
case SCF_ERROR_NOT_SET:
+ case SCF_ERROR_PERMISSION_DENIED:
bad_error("scf_property_get_value",
scf_error());
}
diff --git a/usr/src/cmd/svc/startd/libscf.c b/usr/src/cmd/svc/startd/libscf.c
index 471d2860f7..5006687fcd 100644
--- a/usr/src/cmd/svc/startd/libscf.c
+++ b/usr/src/cmd/svc/startd/libscf.c
@@ -18,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -630,6 +631,7 @@ depgroup_read_restart(scf_handle_t *h, scf_propertygroup_t *pg)
* ENOENT - the property doesn't exist or has no values
* EINVAL - the property has the wrong type
* the property is not single-valued
+ * EACCES - the current user does not have permission to read the value
*/
static int
get_boolean(scf_propertygroup_t *pg, const char *propname, uint8_t *valuep)
@@ -703,6 +705,10 @@ get_boolean(scf_propertygroup_t *pg, const char *propname, uint8_t *valuep)
ret = EINVAL;
goto out;
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = EACCES;
+ goto out;
+
case SCF_ERROR_NOT_SET:
bad_error("scf_property_get_value", scf_error());
}
@@ -728,6 +734,7 @@ out:
* ENOENT - the property doesn't exist or has no values
* EINVAL - the property has the wrong type
* the property is not single-valued
+ * EACCES - the current user does not have permission to read the value
*/
static int
get_count(scf_propertygroup_t *pg, const char *propname, uint64_t *valuep)
@@ -804,6 +811,10 @@ get_count(scf_propertygroup_t *pg, const char *propname, uint64_t *valuep)
ret = EINVAL;
goto out;
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = EACCES;
+ goto out;
+
case SCF_ERROR_NOT_SET:
bad_error("scf_property_get_value", scf_error());
}
@@ -978,6 +989,7 @@ libscf_get_basic_instance_data(scf_handle_t *h, scf_instance_t *inst,
*enabled_ovrp = -1;
break;
+ case EACCES:
default:
bad_error("get_boolean", r);
}
@@ -1035,6 +1047,7 @@ enabled:
*enabledp = -1;
break;
+ case EACCES:
default:
bad_error("get_boolean", r);
}
@@ -1693,6 +1706,7 @@ pg_get_milestone(scf_propertygroup_t *pg, scf_property_t *prop,
return (EINVAL);
case SCF_ERROR_NOT_SET:
+ case SCF_ERROR_PERMISSION_DENIED:
bad_error("scf_property_get_value", scf_error());
}
}
@@ -1829,6 +1843,7 @@ libscf_extract_runlevel(scf_property_t *prop, char *rlp)
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
+ case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_property_get_value", scf_error());
}
@@ -2335,6 +2350,7 @@ libscf_read_method_ids(scf_handle_t *h, scf_instance_t *inst, const char *fmri,
case ECANCELED:
goto read_id_err;
+ case EACCES:
default:
bad_error("get_count", ret);
}
@@ -2361,6 +2377,7 @@ read_trans:
case ECANCELED:
goto read_id_err;
+ case EACCES:
default:
bad_error("get_count", ret);
}
@@ -2386,6 +2403,7 @@ read_pid_only:
case ECANCELED:
goto read_id_err;
+ case EACCES:
default:
bad_error("get_count", ret);
}
@@ -2897,6 +2915,7 @@ libscf_get_method(scf_handle_t *h, int type, restarter_inst_t *inst,
*timeout = METHOD_TIMEOUT_INFINITE;
break;
+ case EACCES:
default:
bad_error("get_count", r);
}
@@ -2969,6 +2988,7 @@ libscf_get_method(scf_handle_t *h, int type, restarter_inst_t *inst,
*need_sessionp = 0;
break;
+ case EACCES:
default:
bad_error("get_boolean", r);
}
@@ -2994,6 +3014,7 @@ libscf_get_method(scf_handle_t *h, int type, restarter_inst_t *inst,
*timeout_retry = 1;
break;
+ case EACCES:
default:
bad_error("get_boolean", r);
}
@@ -3177,6 +3198,7 @@ libscf_unset_action(scf_handle_t *h, scf_propertygroup_t *pg,
rep_ts = ts;
break;
+ case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_NOT_SET:
assert(0);
abort();
@@ -3494,6 +3516,7 @@ walk_property_astrings(scf_property_t *prop, callback_t cb, void *arg)
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
+ case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_iter_next_value", scf_error());
}
diff --git a/usr/src/cmd/svc/startd/method.c b/usr/src/cmd/svc/startd/method.c
index fecb5d528b..e919593537 100644
--- a/usr/src/cmd/svc/startd/method.c
+++ b/usr/src/cmd/svc/startd/method.c
@@ -524,7 +524,8 @@ exec_method(const restarter_inst_t *inst, int type, const char *method,
}
}
- nenv = set_smf_env(mcp->env, mcp->env_sz, NULL, inst, method);
+ nenv = set_smf_env(mcp->env, mcp->env_sz, NULL, inst,
+ method_names[type]);
log_preexec();
diff --git a/usr/src/cmd/svc/startd/startd.c b/usr/src/cmd/svc/startd/startd.c
index 8458a2e45a..19adda1373 100644
--- a/usr/src/cmd/svc/startd/startd.c
+++ b/usr/src/cmd/svc/startd/startd.c
@@ -18,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -519,6 +520,12 @@ timestamp:
"values; ignored.\n", buf);
continue;
+ case SCF_ERROR_PERMISSION_DENIED:
+ uu_warn("property \"options/%s\" cannot be "
+ "read because startd has insufficient "
+ "permission; ignored.\n", buf);
+ continue;
+
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
diff --git a/usr/src/cmd/svc/svccfg/svccfg.h b/usr/src/cmd/svc/svccfg/svccfg.h
index e71ee2bdac..e15fc47afa 100644
--- a/usr/src/cmd/svc/svccfg/svccfg.h
+++ b/usr/src/cmd/svc/svccfg/svccfg.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -55,6 +55,10 @@ extern "C" {
#define SCI_FRESH 0x10 /* Freshly imported service */
#define SCI_FORCE 0x20 /* Override-import. */
#define SCI_KEEP 0x40 /* Don't delete when SCI_FORCEing */
+#define SCI_NOSNAP 0x80 /* Don't take last-import snapshot */
+
+/* Flags for lscf_service_export() */
+#define SCE_ALL_VALUES 0x01 /* Include all property values */
#ifdef lint
extern int yyerror(const char *);
@@ -111,6 +115,12 @@ enum import_state {
IMPORT_REFRESHED
};
+typedef enum svccfg_op {
+ SVCCFG_OP_IMPORT = 0,
+ SVCCFG_OP_APPLY,
+ SVCCFG_OP_RESTORE
+} svccfg_op_t;
+
typedef struct entity {
uu_list_node_t sc_node;
entity_type_t sc_etype;
@@ -340,7 +350,7 @@ CPL_MATCH_FN(complete_select);
CPL_MATCH_FN(complete_command);
int lxml_init(void);
-int lxml_get_bundle_file(bundle_t *, const char *, int);
+int lxml_get_bundle_file(bundle_t *, const char *, svccfg_op_t);
void engine_init(void);
int engine_exec_cmd(void);
diff --git a/usr/src/cmd/svc/svccfg/svccfg.l b/usr/src/cmd/svc/svccfg/svccfg.l
index 6d06f48a8b..47055f80a9 100644
--- a/usr/src/cmd/svc/svccfg/svccfg.l
+++ b/usr/src/cmd/svc/svccfg/svccfg.l
@@ -3,9 +3,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,10 @@
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -79,6 +80,7 @@ extern int yyerror(const char *);
<INITIAL>import { BEGIN WORD; return (SCC_IMPORT); }
<INITIAL>export { BEGIN WORD; return (SCC_EXPORT); }
<INITIAL>archive { BEGIN WORD; return (SCC_ARCHIVE); }
+<INITIAL>restore { BEGIN WORD; return (SCC_RESTORE); }
<INITIAL>apply { BEGIN WORD; return (SCC_APPLY); }
<INITIAL>extract { BEGIN WORD; return (SCC_EXTRACT); }
<INITIAL>repository { BEGIN WORD; return (SCC_REPOSITORY); }
diff --git a/usr/src/cmd/svc/svccfg/svccfg.y b/usr/src/cmd/svc/svccfg/svccfg.y
index c5b5cede9c..311a2d6de1 100644
--- a/usr/src/cmd/svc/svccfg/svccfg.y
+++ b/usr/src/cmd/svc/svccfg/svccfg.y
@@ -19,6 +19,7 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -43,7 +44,7 @@ uu_list_pool_t *string_pool;
%start commands
%token SCC_VALIDATE SCC_IMPORT SCC_EXPORT SCC_ARCHIVE SCC_APPLY SCC_EXTRACT
-%token SCC_REPOSITORY SCC_INVENTORY SCC_SET SCC_END SCC_HELP
+%token SCC_REPOSITORY SCC_INVENTORY SCC_SET SCC_END SCC_HELP SCC_RESTORE
%token SCC_LIST SCC_ADD SCC_DELETE SCC_SELECT SCC_UNSELECT
%token SCC_LISTPG SCC_ADDPG SCC_DELPG
%token SCC_LISTPROP SCC_SETPROP SCC_DELPROP SCC_EDITPROP
@@ -75,6 +76,7 @@ command : terminator
| import_cmd
| export_cmd
| archive_cmd
+ | restore_cmd
| apply_cmd
| extract_cmd
| repository_cmd
@@ -128,7 +130,7 @@ unknown_cmd : SCV_WORD terminator
validate_cmd : SCC_VALIDATE SCV_WORD terminator
{
bundle_t *b = internal_bundle_new();
- lxml_get_bundle_file(b, $2, 0);
+ lxml_get_bundle_file(b, $2, SVCCFG_OP_IMPORT);
(void) internal_bundle_free(b);
free($2);
}
@@ -155,28 +157,87 @@ import_cmd : SCC_IMPORT string_list terminator
export_cmd : SCC_EXPORT SCV_WORD terminator
{
- lscf_service_export($2, NULL);
+ lscf_service_export($2, NULL, 0);
free($2);
}
| SCC_EXPORT SCV_WORD SCS_REDIRECT SCV_WORD terminator
{
- lscf_service_export($2, $4);
+ lscf_service_export($2, $4, 0);
free($2);
free($4);
}
+ | SCC_EXPORT SCV_WORD SCV_WORD terminator
+ {
+ if (strcmp($2, "-a") == 0) {
+ lscf_service_export($3, NULL, SCE_ALL_VALUES);
+ free($2);
+ free($3);
+ } else {
+ synerr(SCC_EXPORT);
+ free($2);
+ free($3);
+ return (0);
+ }
+ }
+ | SCC_EXPORT SCV_WORD SCV_WORD SCS_REDIRECT SCV_WORD terminator
+ {
+ if (strcmp($2, "-a") == 0) {
+ lscf_service_export($3, $5, SCE_ALL_VALUES);
+ free($2);
+ free($3);
+ free($5);
+ } else {
+ synerr(SCC_EXPORT);
+ free($2);
+ free($3);
+ free($5);
+ return (0);
+ }
+ }
| SCC_EXPORT error terminator { synerr(SCC_EXPORT); return(0); }
archive_cmd : SCC_ARCHIVE terminator
{
- lscf_archive(NULL);
+ lscf_archive(NULL, 0);
+ }
+ | SCC_ARCHIVE SCV_WORD terminator
+ {
+ if (strcmp($2, "-a") == 0) {
+ lscf_archive(NULL, SCE_ALL_VALUES);
+ free($2);
+ } else {
+ synerr(SCC_ARCHIVE);
+ free($2);
+ return (0);
+ }
}
| SCC_ARCHIVE SCS_REDIRECT SCV_WORD terminator
{
- lscf_archive($3);
+ lscf_archive($3, 0);
free($3);
}
+ | SCC_ARCHIVE SCV_WORD SCS_REDIRECT SCV_WORD terminator
+ {
+ if (strcmp($2, "-a") == 0) {
+ lscf_archive($4, SCE_ALL_VALUES);
+ free($2);
+ free($4);
+ } else {
+ synerr(SCC_ARCHIVE);
+ free($2);
+ free($4);
+ return (0);
+ }
+ }
| SCC_ARCHIVE error terminator { synerr(SCC_ARCHIVE); return(0); }
+restore_cmd : SCC_RESTORE SCV_WORD terminator
+ {
+ (void) engine_restore($2);
+ free($2);
+ }
+ | SCC_RESTORE error terminator { synerr(SCC_RESTORE); return(0); }
+
apply_cmd : SCC_APPLY SCV_WORD terminator
{ (void) engine_apply($2); free($2); }
| SCC_APPLY error terminator { synerr(SCC_APPLY); return(0); }
@@ -254,6 +315,8 @@ delete_cmd : SCC_DELETE SCV_WORD terminator
free($3);
} else {
synerr(SCC_DELETE);
+ free($2);
+ free($3);
return(0);
}
}
diff --git a/usr/src/cmd/svc/svccfg/svccfg_engine.c b/usr/src/cmd/svc/svccfg/svccfg_engine.c
index 927026c138..4d69b7941f 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_engine.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_engine.c
@@ -18,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -409,9 +410,9 @@ engine_source(const char *name, boolean_t dont_exit)
goto fail;
}
- do
+ do {
ret = fstat(fileno(est->sc_cmd_file), &st);
- while (ret != 0 && errno == EINTR);
+ } while (ret != 0 && errno == EINTR);
if (ret != 0) {
(void) fclose(est->sc_cmd_file);
est->sc_cmd_file = NULL; /* for semerr() */
@@ -570,7 +571,7 @@ engine_import(uu_list_t *args)
/* Load */
b = internal_bundle_new();
- if (lxml_get_bundle_file(b, file, 0) != 0) {
+ if (lxml_get_bundle_file(b, file, SVCCFG_OP_IMPORT) != 0) {
internal_bundle_free(b);
return (-1);
}
@@ -594,7 +595,7 @@ engine_import(uu_list_t *args)
semerr(errstr);
else
semerr(gettext("Unknown error from "
- "mhash_store_entry()\n"));
+ "mhash_store_entry()\n"));
}
free(pname);
@@ -623,7 +624,7 @@ engine_apply(const char *file)
b = internal_bundle_new();
- if (lxml_get_bundle_file(b, file, 1) != 0) {
+ if (lxml_get_bundle_file(b, file, SVCCFG_OP_APPLY) != 0) {
internal_bundle_free(b);
return (-1);
}
@@ -647,6 +648,30 @@ engine_apply(const char *file)
}
int
+engine_restore(const char *file)
+{
+ bundle_t *b;
+
+ lscf_prep_hndl();
+
+ b = internal_bundle_new();
+
+ if (lxml_get_bundle_file(b, file, SVCCFG_OP_RESTORE) != 0) {
+ internal_bundle_free(b);
+ return (-1);
+ }
+
+ if (lscf_bundle_import(b, file, SCI_NOSNAP) != 0) {
+ internal_bundle_free(b);
+ return (-1);
+ }
+
+ internal_bundle_free(b);
+
+ return (0);
+}
+
+int
engine_set(uu_list_t *args)
{
uu_list_walk_t *walk;
diff --git a/usr/src/cmd/svc/svccfg/svccfg_help.c b/usr/src/cmd/svc/svccfg/svccfg_help.c
index 2c431e65dc..67ba47d9f4 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_help.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_help.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -34,12 +34,15 @@ struct help_message help_messages[] = {
"Process a manifest file without changing the repository."
},
{ SCC_IMPORT, "import file\n\nImport a manifest into the repository." },
- { SCC_EXPORT, "export {service | pattern} [> file]\n\n"
+ { SCC_EXPORT, "export [-a] {service | pattern} [> file]\n\n"
"Print a manifest for service to file, or standard output if not specified."
},
- { SCC_ARCHIVE, "archive [> file]\n\n"
+ { SCC_ARCHIVE, "archive [-a] [> file]\n\n"
"Print an archive to file, or standard output if not specified."
},
+ { SCC_RESTORE, "restore file\n\n"
+"Restore the contents of a previously-generated archive."
+ },
{ SCC_APPLY, "apply file\n\nApply a profile." },
{ SCC_EXTRACT, "extract [> file]\n\n"
"Print a profile to file, or standard output if not specified." },
diff --git a/usr/src/cmd/svc/svccfg/svccfg_internal.c b/usr/src/cmd/svc/svccfg/svccfg_internal.c
index 87e5a0890f..5a4d3515a0 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_internal.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_internal.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -738,6 +738,7 @@ load_fini(void)
* ECANCELED - prop's pg was deleted
* ECONNABORTED - repository disconnected
* ENOMEM - out of memory
+ * EACCES - permission denied when reading property
*/
static int
load_property(scf_property_t *prop, property_t **ipp)
@@ -822,6 +823,10 @@ load_property(scf_property_t *prop, property_t **ipp)
r = ECONNABORTED;
goto out;
+ case SCF_ERROR_PERMISSION_DENIED:
+ r = EACCES;
+ goto out;
+
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
@@ -978,6 +983,7 @@ load_pg_attrs(const scf_propertygroup_t *pg, pgroup_t **ipgp)
* ECONNABORTED - repository disconnected
* EBADF - pg is corrupt (error printed if fmri is given)
* ENOMEM - out of memory
+ * EACCES - permission denied when reading property
*/
int
load_pg(const scf_propertygroup_t *pg, pgroup_t **ipgp, const char *fmri,
@@ -1050,6 +1056,7 @@ load_pg(const scf_propertygroup_t *pg, pgroup_t **ipgp, const char *fmri,
case ECANCELED:
case ECONNABORTED:
case ENOMEM:
+ case EACCES:
goto out;
default:
diff --git a/usr/src/cmd/svc/svccfg/svccfg_libscf.c b/usr/src/cmd/svc/svccfg/svccfg_libscf.c
index ca53a21a95..0397af151c 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_libscf.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_libscf.c
@@ -18,6 +18,7 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -40,6 +41,7 @@
#include <limits.h>
#include <stdarg.h>
#include <string.h>
+#include <strings.h>
#include <unistd.h>
#include <wait.h>
@@ -107,6 +109,14 @@ struct snaplevel {
scf_snaplevel_t *sl;
};
+/*
+ * This is used for communication between lscf_service_export and
+ * export_callback.
+ */
+struct export_args {
+ const char *filename;
+ int flags;
+};
const char * const scf_pg_general = SCF_PG_GENERAL;
const char * const scf_group_framework = SCF_GROUP_FRAMEWORK;
@@ -1057,7 +1067,9 @@ prop_get_val(scf_property_t *prop, scf_value_t *val)
err = scf_error();
- if (err != SCF_ERROR_NOT_FOUND && err != SCF_ERROR_CONSTRAINT_VIOLATED)
+ if (err != SCF_ERROR_NOT_FOUND &&
+ err != SCF_ERROR_CONSTRAINT_VIOLATED &&
+ err != SCF_ERROR_PERMISSION_DENIED)
scfdie();
if (g_verbose) {
@@ -1076,9 +1088,11 @@ prop_get_val(scf_property_t *prop, scf_value_t *val)
if (err == SCF_ERROR_NOT_FOUND)
emsg = gettext("Property %s has no values; expected "
"one.\n");
- else
+ else if (err == SCF_ERROR_CONSTRAINT_VIOLATED)
emsg = gettext("Property %s has multiple values; "
"expected one.\n");
+ else
+ emsg = gettext("No permission to read property %s.\n");
warn(emsg, fmri);
@@ -3057,6 +3071,7 @@ upgrade_dependent(const scf_property_t *prop, const entity_t *ient,
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
+ case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_property_get_value",
scf_error());
@@ -3103,6 +3118,7 @@ upgrade_dependent(const scf_property_t *prop, const entity_t *ient,
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
+ case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_property_get_value",
scf_error());
@@ -3169,6 +3185,7 @@ upgrade_dependent(const scf_property_t *prop, const entity_t *ient,
case EBADF:
return (r);
+ case EACCES:
default:
bad_error("load_pg", r);
}
@@ -3233,6 +3250,7 @@ upgrade_dependent(const scf_property_t *prop, const entity_t *ient,
internal_pgroup_free(old_dpt_pgroup);
return (r);
+ case EACCES:
default:
bad_error("load_pg", r);
}
@@ -3366,6 +3384,7 @@ delprop:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
+ case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_property_get_value", scf_error());
}
@@ -3413,6 +3432,7 @@ delprop:
case EBADF:
return (r);
+ case EACCES:
default:
bad_error("load_pg", r);
}
@@ -3494,6 +3514,7 @@ delprop:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
+ case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_property_get_value", scf_error());
}
@@ -3601,6 +3622,7 @@ delprop:
case EBADF:
goto out;
+ case EACCES:
default:
bad_error("load_pg", r);
}
@@ -3653,6 +3675,7 @@ nocust:
case EBADF:
return (r);
+ case EACCES:
default:
bad_error("load_pg", r);
}
@@ -3716,6 +3739,7 @@ nocust:
case EBADF:
goto out;
+ case EACCES:
default:
bad_error("load_pg", r);
}
@@ -3821,6 +3845,7 @@ nocust:
case EBADF:
goto out;
+ case EACCES:
default:
bad_error("load_pg", r);
}
@@ -4073,6 +4098,7 @@ handle_dependent_conflict(const entity_t * const ient,
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
+ case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_property_get_value",
scf_error());
@@ -4201,6 +4227,7 @@ handle_dependent_conflict(const entity_t * const ient,
internal_pgroup_free(pgroup);
return (0);
+ case EACCES:
default:
bad_error("load_pg", r);
}
@@ -4239,6 +4266,7 @@ handle_dependent_conflict(const entity_t * const ient,
* - couldn't upgrade dependents (backend access denied)
* - couldn't import pg (backend access denied)
* - couldn't upgrade pg (backend access denied)
+ * - couldn't read property (backend access denied)
* EBUSY - property group was added (error printed)
* - property group was deleted (error printed)
* - property group changed (error printed)
@@ -4385,6 +4413,7 @@ process_old_pg(const scf_propertygroup_t *lipg, entity_t *ient, void *ent,
case ECONNABORTED:
case ENOMEM:
case EBADF:
+ case EACCES:
return (r);
default:
@@ -4400,6 +4429,7 @@ process_old_pg(const scf_propertygroup_t *lipg, entity_t *ient, void *ent,
case ECONNABORTED:
case ENOMEM:
case EBADF:
+ case EACCES:
internal_pgroup_free(lipg_i);
return (r);
@@ -4454,6 +4484,7 @@ process_old_pg(const scf_propertygroup_t *lipg, entity_t *ient, void *ent,
case ECONNABORTED:
case EBADF:
case ENOMEM:
+ case EACCES:
return (r);
default:
@@ -4541,6 +4572,7 @@ process_old_pg(const scf_propertygroup_t *lipg, entity_t *ient, void *ent,
case ECONNABORTED:
case EBADF:
case ENOMEM:
+ case EACCES:
goto out;
default:
@@ -4936,6 +4968,7 @@ upgrade_props(void *ent, scf_snaplevel_t *running, scf_snaplevel_t *snpl,
case ECONNABORTED:
case EBADF:
case ENOMEM:
+ case EACCES:
return (r);
default:
@@ -5591,6 +5624,9 @@ nosnap:
}
lionly:
+ if (lcbdata->sc_flags & SCI_NOSNAP)
+ goto deltemp;
+
/* transfer snapshot from temporary instance */
if (g_verbose)
warn(gettext("Taking \"%s\" snapshot for %s.\n"),
@@ -7245,10 +7281,10 @@ write_service_bundle(xmlDocPtr doc, FILE *f)
*/
static void
export_property(scf_property_t *prop, const char *name_arg,
- struct pg_elts *elts)
+ struct pg_elts *elts, int flags)
{
const char *type;
- scf_error_t err;
+ scf_error_t err = 0;
xmlNodePtr pnode, lnode;
char *lnname;
int ret;
@@ -7267,7 +7303,10 @@ export_property(scf_property_t *prop, const char *name_arg,
uu_die(gettext("Can't export property %s: unknown type.\n"),
exp_str);
- /* Is there a single value? */
+ /* If we're exporting values, and there's just one, export it here. */
+ if (!(flags & SCE_ALL_VALUES))
+ goto empty;
+
if (scf_property_get_value(prop, exp_val) == SCF_SUCCESS) {
xmlNodePtr n;
@@ -7292,9 +7331,18 @@ export_property(scf_property_t *prop, const char *name_arg,
}
err = scf_error();
- if (err != SCF_ERROR_CONSTRAINT_VIOLATED && err != SCF_ERROR_NOT_FOUND)
+
+ if (err == SCF_ERROR_PERMISSION_DENIED) {
+ semerr(emsg_permission_denied);
+ return;
+ }
+
+ if (err != SCF_ERROR_CONSTRAINT_VIOLATED &&
+ err != SCF_ERROR_NOT_FOUND &&
+ err != SCF_ERROR_PERMISSION_DENIED)
scfdie();
+empty:
/* Multiple (or no) values, so use property */
pnode = xmlNewNode(NULL, (xmlChar *)"property");
if (pnode == NULL)
@@ -7345,11 +7393,12 @@ export_property(scf_property_t *prop, const char *name_arg,
* Add a property_group element for this property group to elts.
*/
static void
-export_pg(scf_propertygroup_t *pg, struct entity_elts *eelts)
+export_pg(scf_propertygroup_t *pg, struct entity_elts *eelts, int flags)
{
xmlNodePtr n;
struct pg_elts elts;
int ret;
+ boolean_t read_protected;
n = xmlNewNode(NULL, (xmlChar *)"property_group");
@@ -7369,6 +7418,17 @@ export_pg(scf_propertygroup_t *pg, struct entity_elts *eelts)
(void) memset(&elts, 0, sizeof (elts));
+ /*
+ * If this property group is not read protected, we always want to
+ * output all the values. Otherwise, we only output the values if the
+ * caller set SCE_ALL_VALUES (i.e., the user gave us export/archive -a).
+ */
+ if (_scf_pg_is_read_protected(pg, &read_protected) != SCF_SUCCESS)
+ scfdie();
+
+ if (!read_protected)
+ flags |= SCE_ALL_VALUES;
+
while ((ret = scf_iter_next_property(exp_prop_iter, exp_prop)) == 1) {
if (scf_property_get_name(exp_prop, exp_str, exp_str_sz) < 0)
scfdie();
@@ -7388,7 +7448,7 @@ export_pg(scf_propertygroup_t *pg, struct entity_elts *eelts)
xmlFreeNode(m);
}
- export_property(exp_prop, NULL, &elts);
+ export_property(exp_prop, NULL, &elts, flags);
}
if (ret == -1)
scfdie();
@@ -7504,7 +7564,7 @@ export_dependency(scf_propertygroup_t *pg, struct entity_elts *eelts)
if (err) {
xmlFreeNode(n);
- export_pg(pg, eelts);
+ export_pg(pg, eelts, 0);
return;
}
@@ -7539,7 +7599,7 @@ export_dependency(scf_propertygroup_t *pg, struct entity_elts *eelts)
xmlFreeNode(m);
}
- export_property(exp_prop, exp_str, &elts);
+ export_property(exp_prop, exp_str, &elts, 0);
}
if (ret == -1)
scfdie();
@@ -7667,7 +7727,7 @@ export_method(scf_propertygroup_t *pg, struct entity_elts *eelts)
if (err) {
xmlFreeNode(n);
- export_pg(pg, eelts);
+ export_pg(pg, eelts, 0);
return;
}
@@ -7842,7 +7902,7 @@ export_method(scf_propertygroup_t *pg, struct entity_elts *eelts)
continue;
}
- export_property(exp_prop, exp_str, &elts);
+ export_property(exp_prop, exp_str, &elts, 0);
}
if (ret == -1)
scfdie();
@@ -7958,7 +8018,7 @@ export_svc_general(scf_propertygroup_t *pg, struct entity_elts *selts)
xmlFreeNode(s);
}
- export_property(exp_prop, exp_str, &elts);
+ export_property(exp_prop, exp_str, &elts, 0);
}
if (ret == -1)
scfdie();
@@ -7988,7 +8048,7 @@ export_method_context(scf_propertygroup_t *pg, struct entity_elts *elts)
elts->method_context = n;
} else {
xmlFreeNode(n);
- export_pg(pg, elts);
+ export_pg(pg, elts, 0);
}
return;
}
@@ -8060,7 +8120,7 @@ export_method_context(scf_propertygroup_t *pg, struct entity_elts *elts)
if (err && env == NULL) {
xmlFreeNode(n);
- export_pg(pg, elts);
+ export_pg(pg, elts, 0);
return;
}
@@ -8174,7 +8234,7 @@ export_dependent(scf_propertygroup_t *pg, const char *name, const char *tfmri)
xmlFreeNode(s);
}
- export_property(exp_prop, exp_str, &pgelts);
+ export_property(exp_prop, exp_str, &pgelts, 0);
}
if (ret == -1)
scfdie();
@@ -8223,7 +8283,7 @@ export_dependents(scf_propertygroup_t *pg, struct entity_elts *eelts)
if ((ty != SCF_TYPE_ASTRING &&
prop_check_type(exp_prop, SCF_TYPE_FMRI) != 0) ||
prop_get_val(exp_prop, exp_val) != 0) {
- export_property(exp_prop, NULL, &pgelts);
+ export_property(exp_prop, NULL, &pgelts, 0);
continue;
}
@@ -8254,7 +8314,7 @@ export_dependents(scf_propertygroup_t *pg, struct entity_elts *eelts)
"FMRI.\n"), fmri);
}
- export_property(exp_prop, exp_str, &pgelts);
+ export_property(exp_prop, exp_str, &pgelts, 0);
continue;
case SCF_ERROR_CONSTRAINT_VIOLATED:
@@ -8267,7 +8327,7 @@ export_dependents(scf_propertygroup_t *pg, struct entity_elts *eelts)
"a service or an instance.\n"), fmri);
}
- export_property(exp_prop, exp_str, &pgelts);
+ export_property(exp_prop, exp_str, &pgelts, 0);
continue;
case SCF_ERROR_NOT_FOUND:
@@ -8280,7 +8340,7 @@ export_dependents(scf_propertygroup_t *pg, struct entity_elts *eelts)
"not exist.\n"), fmri);
}
- export_property(exp_prop, exp_str, &pgelts);
+ export_property(exp_prop, exp_str, &pgelts, 0);
continue;
default:
@@ -8299,7 +8359,7 @@ export_dependents(scf_propertygroup_t *pg, struct entity_elts *eelts)
warn(gettext("Entity %s is missing dependency property "
"group %s.\n"), fmri, exp_str);
- export_property(exp_prop, NULL, &pgelts);
+ export_property(exp_prop, NULL, &pgelts, 0);
continue;
}
@@ -8313,13 +8373,13 @@ export_dependents(scf_propertygroup_t *pg, struct entity_elts *eelts)
warn(gettext("Property group %s is not of "
"expected type %s.\n"), fmri, SCF_GROUP_DEPENDENCY);
- export_property(exp_prop, NULL, &pgelts);
+ export_property(exp_prop, NULL, &pgelts, 0);
continue;
}
n = export_dependent(opg, exp_str, fmri);
if (n == NULL)
- export_property(exp_prop, exp_str, &pgelts);
+ export_property(exp_prop, exp_str, &pgelts, 0);
else {
if (eelts->dependents == NULL)
eelts->dependents = n;
@@ -8444,12 +8504,12 @@ export_template(scf_propertygroup_t *pg, struct entity_elts *elts,
if (strcmp(exp_str, SCF_PG_TM_COMMON_NAME) == 0) {
telts->common_name = export_tm_loctext(pg, "common_name");
if (telts->common_name == NULL)
- export_pg(pg, elts);
+ export_pg(pg, elts, 0);
return;
} else if (strcmp(exp_str, SCF_PG_TM_DESCRIPTION) == 0) {
telts->description = export_tm_loctext(pg, "description");
if (telts->description == NULL)
- export_pg(pg, elts);
+ export_pg(pg, elts, 0);
return;
}
@@ -8463,7 +8523,7 @@ export_template(scf_propertygroup_t *pg, struct entity_elts *elts,
make_node(&telts->documentation, "documentation");
(void) xmlAddChild(telts->documentation, child);
} else {
- export_pg(pg, elts);
+ export_pg(pg, elts, 0);
}
}
@@ -8522,7 +8582,7 @@ export_inst_general(scf_propertygroup_t *pg, xmlNodePtr inode,
xmlFreeNode(rnode);
}
- export_property(exp_prop, exp_str, &pgelts);
+ export_property(exp_prop, exp_str, &pgelts, 0);
}
if (ret == -1)
scfdie();
@@ -8536,7 +8596,7 @@ export_inst_general(scf_propertygroup_t *pg, xmlNodePtr inode,
* Put an instance element for the given instance into selts.
*/
static void
-export_instance(scf_instance_t *inst, struct entity_elts *selts)
+export_instance(scf_instance_t *inst, struct entity_elts *selts, int flags)
{
xmlNodePtr n;
boolean_t isdefault;
@@ -8590,12 +8650,12 @@ export_instance(scf_instance_t *inst, struct entity_elts *selts)
(void) memset(&template_elts, 0, sizeof (template_elts));
while ((ret = scf_iter_next_pg(exp_pg_iter, exp_pg)) == 1) {
- uint32_t flags;
+ uint32_t pgflags;
- if (scf_pg_get_flags(exp_pg, &flags) != 0)
+ if (scf_pg_get_flags(exp_pg, &pgflags) != 0)
scfdie();
- if (flags & SCF_PG_FLAG_NONPERSISTENT)
+ if (pgflags & SCF_PG_FLAG_NONPERSISTENT)
continue;
if (scf_pg_get_type(exp_pg, exp_str, exp_str_sz) < 0)
@@ -8629,7 +8689,7 @@ export_instance(scf_instance_t *inst, struct entity_elts *selts)
}
/* Ordinary pg. */
- export_pg(exp_pg, &elts);
+ export_pg(exp_pg, &elts, flags);
}
if (ret == -1)
scfdie();
@@ -8684,7 +8744,7 @@ export_instance(scf_instance_t *inst, struct entity_elts *selts)
* Return a service element for the given service.
*/
static xmlNodePtr
-export_service(scf_service_t *svc)
+export_service(scf_service_t *svc, int flags)
{
xmlNodePtr snode;
struct entity_elts elts;
@@ -8711,12 +8771,12 @@ export_service(scf_service_t *svc)
(void) memset(&template_elts, 0, sizeof (template_elts));
while ((ret = scf_iter_next_pg(exp_pg_iter, exp_pg)) == 1) {
- uint32_t flags;
+ uint32_t pgflags;
- if (scf_pg_get_flags(exp_pg, &flags) != 0)
+ if (scf_pg_get_flags(exp_pg, &pgflags) != 0)
scfdie();
- if (flags & SCF_PG_FLAG_NONPERSISTENT)
+ if (pgflags & SCF_PG_FLAG_NONPERSISTENT)
continue;
if (scf_pg_get_type(exp_pg, exp_str, exp_str_sz) < 0)
@@ -8749,7 +8809,7 @@ export_service(scf_service_t *svc)
continue;
}
- export_pg(exp_pg, &elts);
+ export_pg(exp_pg, &elts, flags);
}
if (ret == -1)
scfdie();
@@ -8769,7 +8829,7 @@ export_service(scf_service_t *svc)
scfdie();
while ((ret = scf_iter_next_instance(exp_inst_iter, exp_inst)) == 1)
- export_instance(exp_inst, &elts);
+ export_instance(exp_inst, &elts, flags);
if (ret == -1)
scfdie();
@@ -8796,7 +8856,7 @@ export_callback(void *data, scf_walkinfo_t *wip)
xmlDocPtr doc;
xmlNodePtr sb;
int result;
- char *filename = data;
+ struct export_args *argsp = (struct export_args *)data;
if ((exp_inst = scf_instance_create(g_hndl)) == NULL ||
(exp_pg = scf_pg_create(g_hndl)) == NULL ||
@@ -8811,16 +8871,16 @@ export_callback(void *data, scf_walkinfo_t *wip)
exp_str_sz = max_scf_len + 1;
exp_str = safe_malloc(exp_str_sz);
- if (filename != NULL) {
+ if (argsp->filename != NULL) {
errno = 0;
- f = fopen(filename, "wb");
+ f = fopen(argsp->filename, "wb");
if (f == NULL) {
if (errno == 0)
uu_die(gettext("Could not open \"%s\": no free "
- "stdio streams.\n"), filename);
+ "stdio streams.\n"), argsp->filename);
else
uu_die(gettext("Could not open \"%s\""),
- filename);
+ argsp->filename);
}
} else
f = stdout;
@@ -8840,7 +8900,7 @@ export_callback(void *data, scf_walkinfo_t *wip)
safe_setprop(sb, name_attr, "export");
(void) xmlAddSibling(doc->children, sb);
- (void) xmlAddChild(sb, export_service(wip->svc));
+ (void) xmlAddChild(sb, export_service(wip->svc, argsp->flags));
result = write_service_bundle(doc, f);
@@ -8867,16 +8927,21 @@ export_callback(void *data, scf_walkinfo_t *wip)
* dump it into filename (or stdout if filename is NULL).
*/
int
-lscf_service_export(char *fmri, const char *filename)
+lscf_service_export(char *fmri, const char *filename, int flags)
{
+ struct export_args args;
int ret, err;
lscf_prep_hndl();
+ bzero(&args, sizeof (args));
+ args.filename = filename;
+ args.flags = flags;
+
err = 0;
if ((ret = scf_walk_fmri(g_hndl, 1, (char **)&fmri,
SCF_WALK_SERVICE | SCF_WALK_NOINSTANCE, export_callback,
- (void *)filename, &err, semerr)) != 0) {
+ &args, &err, semerr)) != 0) {
if (ret != -1)
semerr(gettext("Failed to walk instances: %s\n"),
scf_strerror(ret));
@@ -8898,7 +8963,7 @@ lscf_service_export(char *fmri, const char *filename)
*/
static xmlNodePtr
-make_archive()
+make_archive(int flags)
{
xmlNodePtr sb;
scf_scope_t *scope;
@@ -8947,7 +9012,7 @@ make_archive()
if (strcmp(exp_str, SCF_LEGACY_SERVICE) == 0)
continue;
- xmlAddChild(sb, export_service(svc));
+ xmlAddChild(sb, export_service(svc, flags));
}
free(exp_str);
@@ -8968,7 +9033,7 @@ make_archive()
}
int
-lscf_archive(const char *filename)
+lscf_archive(const char *filename, int flags)
{
FILE *f;
xmlDocPtr doc;
@@ -8998,7 +9063,7 @@ lscf_archive(const char *filename)
(xmlChar *)MANIFEST_DTD_PATH) == NULL)
uu_die(emsg_create_xml);
- (void) xmlAddSibling(doc->children, make_archive());
+ (void) xmlAddSibling(doc->children, make_archive(flags));
result = write_service_bundle(doc, f);
@@ -10169,6 +10234,7 @@ prop_has_multiple_values(const scf_property_t *prop, scf_value_t *val)
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
return (B_FALSE);
+ case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_CONSTRAINT_VIOLATED:
return (B_TRUE);
default:
@@ -10234,7 +10300,7 @@ list_prop_info(const scf_property_t *prop, const char *name, size_t len)
free(buf);
}
- if (ret != 0)
+ if (ret != 0 && scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
if (putchar('\n') != '\n')
@@ -11003,7 +11069,8 @@ write_edit_script(FILE *strm)
free(buf);
}
- if (ret3 < 0)
+ if (ret3 < 0 &&
+ scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
/* Write closing paren if mult-value property */
diff --git a/usr/src/cmd/svc/svccfg/svccfg_xml.c b/usr/src/cmd/svc/svccfg/svccfg_xml.c
index 484f7db28b..930a61bdd4 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_xml.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_xml.c
@@ -500,6 +500,18 @@ lxml_get_property(pgroup_t *pgrp, xmlNodePtr property)
"property \'%s/%s\'\n"), pgrp->sc_pgroup_name,
p->sc_property_name);
+ for (r = 0; r < sizeof (lxml_prop_types) / sizeof (char *); r++) {
+ if (xmlStrcmp(type, (const xmlChar *)lxml_prop_types[r]) == 0)
+ break;
+ }
+
+ if (r >= sizeof (lxml_prop_types) / sizeof (char *)) {
+ uu_die(gettext("property type invalid for property '%s/%s'\n"),
+ pgrp->sc_pgroup_name, p->sc_property_name);
+ }
+
+ p->sc_value_type = lxml_element_to_type(r);
+
for (cursor = property->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
if (lxml_ignorable_block(cursor))
@@ -524,7 +536,6 @@ lxml_get_property(pgroup_t *pgrp, xmlNodePtr property)
"type-to-list mismatch\n"),
p->sc_property_name);
- p->sc_value_type = lxml_element_to_type(r);
(void) lxml_get_value(p, r, cursor);
break;
default:
@@ -1487,11 +1498,11 @@ lxml_get_default_instance(entity_t *service, xmlNodePtr definst)
/*
* Translate an instance element into an internal property tree, added to
- * service. If apply is true, forbid subelements and set the enabled property
- * to override.
+ * service. If op is SVCCFG_OP_APPLY (i.e., apply a profile), forbid
+ * subelements and set the enabled property to override.
*/
static int
-lxml_get_instance(entity_t *service, xmlNodePtr inst, int apply)
+lxml_get_instance(entity_t *service, xmlNodePtr inst, svccfg_op_t op)
{
entity_t *i;
pgroup_t *pg;
@@ -1529,7 +1540,7 @@ lxml_get_instance(entity_t *service, xmlNodePtr inst, int apply)
p = internal_property_create(SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 1,
(uint64_t)(strcmp(true, (const char *)enabled) == 0 ? 1 : 0));
- p->sc_property_override = apply;
+ p->sc_property_override = (op == SVCCFG_OP_APPLY);
(void) internal_attach_property(pg, p);
@@ -1543,7 +1554,7 @@ lxml_get_instance(entity_t *service, xmlNodePtr inst, int apply)
if (lxml_ignorable_block(cursor))
continue;
- if (apply) {
+ if (op == SVCCFG_OP_APPLY) {
semerr(gettext("Instance \"%s\" may not contain "
"elements in profiles.\n"), i->sc_name,
cursor->name);
@@ -1608,10 +1619,10 @@ lxml_get_single_instance(entity_t *entity, xmlNodePtr si)
/*
* Translate a service element into an internal instance/property tree, added
- * to bundle. If apply is true, allow only instance subelements.
+ * to bundle. If op is SVCCFG_OP_APPLY, allow only instance subelements.
*/
static int
-lxml_get_service(bundle_t *bundle, xmlNodePtr svc, int apply)
+lxml_get_service(bundle_t *bundle, xmlNodePtr svc, svccfg_op_t op)
{
entity_t *s;
xmlNodePtr cursor;
@@ -1643,7 +1654,7 @@ lxml_get_service(bundle_t *bundle, xmlNodePtr svc, int apply)
e = lxml_xlate_element(cursor->name);
- if (apply && e != SC_INSTANCE) {
+ if (op == SVCCFG_OP_APPLY && e != SC_INSTANCE) {
semerr(gettext("Service \"%s\" may not contain the "
"non-instance element \"%s\" in a profile.\n"),
s->sc_name, cursor->name);
@@ -1653,7 +1664,7 @@ lxml_get_service(bundle_t *bundle, xmlNodePtr svc, int apply)
switch (e) {
case SC_INSTANCE:
- (void) lxml_get_instance(s, cursor, apply);
+ (void) lxml_get_instance(s, cursor, op);
break;
case SC_TEMPLATE:
(void) lxml_get_template(s, cursor);
@@ -1721,7 +1732,7 @@ lxml_is_known_dtd(const xmlChar *dtdname)
static int
lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
- xmlNodePtr subbundle, int apply)
+ xmlNodePtr subbundle, svccfg_op_t op)
{
xmlNodePtr cursor;
xmlChar *type;
@@ -1740,16 +1751,25 @@ lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
xmlFree(type);
- if (!apply) {
+ switch (op) {
+ case SVCCFG_OP_IMPORT:
if (bundle->sc_bundle_type != SVCCFG_MANIFEST) {
semerr(gettext("document is not a manifest.\n"));
return (-1);
}
- } else {
+ break;
+ case SVCCFG_OP_APPLY:
if (bundle->sc_bundle_type != SVCCFG_PROFILE) {
semerr(gettext("document is not a profile.\n"));
return (-1);
}
+ break;
+ case SVCCFG_OP_RESTORE:
+ if (bundle->sc_bundle_type != SVCCFG_ARCHIVE) {
+ semerr(gettext("document is not an archive.\n"));
+ return (-1);
+ }
+ break;
}
if ((bundle->sc_bundle_name = xmlGetProp(subbundle,
@@ -1773,11 +1793,11 @@ lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
continue;
case SC_SERVICE_BUNDLE:
- if (lxml_get_bundle(bundle, bundle_type, cursor, apply))
+ if (lxml_get_bundle(bundle, bundle_type, cursor, op))
return (-1);
break;
case SC_SERVICE:
- (void) lxml_get_service(bundle, cursor, apply);
+ (void) lxml_get_service(bundle, cursor, op);
break;
}
}
@@ -1787,11 +1807,11 @@ lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
/*
* Load an XML tree from filename and translate it into an internal service
- * tree bundle. If apply is false, require that the the bundle be of type
- * manifest, or type profile otherwise.
+ * tree bundle. Require that the bundle be of appropriate type for the
+ * operation: archive for RESTORE, manifest for IMPORT, profile for APPLY.
*/
int
-lxml_get_bundle_file(bundle_t *bundle, const char *filename, int apply)
+lxml_get_bundle_file(bundle_t *bundle, const char *filename, svccfg_op_t op)
{
xmlDocPtr document;
xmlNodePtr cursor;
@@ -1813,7 +1833,8 @@ lxml_get_bundle_file(bundle_t *bundle, const char *filename, int apply)
* Until libxml2 addresses DTD-based validation with XInclude, we don't
* validate service profiles (i.e. the apply path).
*/
- do_validate = (apply == 0) && (getenv("SVCCFG_NOVALIDATE") == NULL);
+ do_validate = (op != SVCCFG_OP_APPLY) &&
+ (getenv("SVCCFG_NOVALIDATE") == NULL);
if (do_validate)
dtdpath = getenv("SVCCFG_DTD");
@@ -1894,7 +1915,7 @@ lxml_get_bundle_file(bundle_t *bundle, const char *filename, int apply)
lxml_dump(0, cursor);
#endif /* DEBUG */
- r = lxml_get_bundle(bundle, SVCCFG_UNKNOWN_BUNDLE, cursor, apply);
+ r = lxml_get_bundle(bundle, SVCCFG_UNKNOWN_BUNDLE, cursor, op);
xmlFreeDoc(document);
@@ -1910,7 +1931,7 @@ lxml_inventory(const char *filename)
b = internal_bundle_new();
- if (lxml_get_bundle_file(b, filename, 0) != 0) {
+ if (lxml_get_bundle_file(b, filename, SVCCFG_OP_IMPORT) != 0) {
internal_bundle_free(b);
return (-1);
}
diff --git a/usr/src/cmd/svc/svcprop/svcprop.c b/usr/src/cmd/svc/svcprop/svcprop.c
index 2f9f3ddb0b..0c81f48168 100644
--- a/usr/src/cmd/svc/svcprop/svcprop.c
+++ b/usr/src/cmd/svc/svcprop/svcprop.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -222,7 +222,10 @@ display_prop(scf_propertygroup_t *pg, scf_property_t *prop)
{
scf_value_t *val;
scf_iter_t *iter;
- int ret, first;
+ int ret, first, err;
+
+ const char * const permission_denied_emsg =
+ gettext("Permission denied.\n");
if (types) {
scf_type_t ty;
@@ -277,8 +280,15 @@ display_prop(scf_propertygroup_t *pg, scf_property_t *prop)
(void) putchar(' ');
print_value(val);
}
- if (ret == -1)
- scfdie();
+ if (ret == -1) {
+ err = scf_error();
+ if (err == SCF_ERROR_PERMISSION_DENIED) {
+ if (uu_list_numnodes(prop_list) > 0)
+ die(permission_denied_emsg);
+ } else {
+ scfdie();
+ }
+ }
(void) putchar('\n');
diff --git a/usr/src/cmd/svc/svcs/explain.c b/usr/src/cmd/svc/svcs/explain.c
index 596301b55a..b76a98ee3b 100644
--- a/usr/src/cmd/svc/svcs/explain.c
+++ b/usr/src/cmd/svc/svcs/explain.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -1391,6 +1391,7 @@ check_msgbase(void)
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_CONSTRAINT_VIOLATED:
+ case SCF_ERROR_PERMISSION_DENIED:
g_msgbase = NULL;
return;
@@ -1968,7 +1969,7 @@ print_service(inst_t *svcp, int verbose)
for (tbsz = 50; ; tbsz *= 2) {
timebuf = safe_malloc(tbsz);
if (strftime(timebuf, tbsz, NULL, tmp) != 0)
- break;
+ break;
free(timebuf);
}
diff --git a/usr/src/cmd/svc/svcs/svcs.c b/usr/src/cmd/svc/svcs/svcs.c
index f565f397e9..5dc801a784 100644
--- a/usr/src/cmd/svc/svcs/svcs.c
+++ b/usr/src/cmd/svc/svcs/svcs.c
@@ -17,8 +17,10 @@
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
- *
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -376,6 +378,7 @@ pg_get_single_val(scf_propertygroup_t *pg, const char *propname, scf_type_t ty,
}
goto misconfigured;
+ case SCF_ERROR_PERMISSION_DENIED:
default:
scfdie();
}
@@ -1092,7 +1095,7 @@ sprint_desc(char **buf, scf_walkinfo_t *wip)
newsize = (*buf ? strlen(*buf) : 0) + DESC_COLUMN_WIDTH + 1;
newbuf = safe_malloc(newsize);
(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
- DESC_COLUMN_WIDTH, common_name_buf);
+ DESC_COLUMN_WIDTH, common_name_buf);
if (*buf)
free(*buf);
*buf = newbuf;
@@ -1629,13 +1632,13 @@ sprint_stime(char **buf, scf_walkinfo_t *wip)
*/
if (now - then < 24 * 60 * 60)
(void) strftime(st_buf, sizeof (st_buf), gettext(FORMAT_TIME),
- tm);
+ tm);
else if (now - then < 12 * 30 * 24 * 60 * 60)
(void) strftime(st_buf, sizeof (st_buf), gettext(FORMAT_DATE),
- tm);
+ tm);
else
(void) strftime(st_buf, sizeof (st_buf), gettext(FORMAT_YEAR),
- tm);
+ tm);
(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
STIME_COLUMN_WIDTH + 1, st_buf);
@@ -2455,14 +2458,14 @@ add_processes(scf_walkinfo_t *wip, char *line, scf_propertygroup_t *lpg)
* than 12 months ago.
*/
if (now - psi.pr_start.tv_sec < 24 * 60 * 60)
- (void) strftime(stime, sizeof (stime), gettext(FORMAT_TIME),
- tm);
+ (void) strftime(stime, sizeof (stime),
+ gettext(FORMAT_TIME), tm);
else if (now - psi.pr_start.tv_sec < 12 * 30 * 24 * 60 * 60)
- (void) strftime(stime, sizeof (stime), gettext(FORMAT_DATE),
- tm);
+ (void) strftime(stime, sizeof (stime),
+ gettext(FORMAT_DATE), tm);
else
- (void) strftime(stime, sizeof (stime), gettext(FORMAT_YEAR),
- tm);
+ (void) strftime(stime, sizeof (stime),
+ gettext(FORMAT_YEAR), tm);
(void) snprintf(cp, len, "\n %-8s %6ld %.*s",
stime, pids[i], PRFNSZ, psi.pr_fname);
diff --git a/usr/src/common/svc/repcache_protocol.h b/usr/src/common/svc/repcache_protocol.h
index 8c117e1556..cdceb76456 100644
--- a/usr/src/common/svc/repcache_protocol.h
+++ b/usr/src/common/svc/repcache_protocol.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -353,7 +353,6 @@
*
* BACKUP(name) -> result
* Backs up the persistant repository with a particular name.
- *
*/
#include <door.h>
@@ -626,6 +625,7 @@ struct rep_protocol_entity_name {
#define RP_ENTITY_NAME_SNAPLEVEL_SCOPE 3
#define RP_ENTITY_NAME_SNAPLEVEL_SERVICE 4
#define RP_ENTITY_NAME_SNAPLEVEL_INSTANCE 5
+#define RP_ENTITY_NAME_PGREADPROT 6
struct rep_protocol_entity_update {
enum rep_protocol_requestid rpr_request; /* ENTITY_UPDATE */
diff --git a/usr/src/lib/librestart/common/librestart.c b/usr/src/lib/librestart/common/librestart.c
index 88ca2ea53b..0bdafd3062 100644
--- a/usr/src/lib/librestart/common/librestart.c
+++ b/usr/src/lib/librestart/common/librestart.c
@@ -1069,6 +1069,7 @@ next_val:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
+ case SCF_ERROR_PERMISSION_DENIED:
default:
bad_fail("scf_iter_next_value",
scf_error());
@@ -1367,6 +1368,7 @@ next_val:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
+ case SCF_ERROR_PERMISSION_DENIED:
bad_fail(
"scf_iter_next_value",
scf_error());
@@ -2532,7 +2534,7 @@ restarter_get_method_context(uint_t version, scf_instance_t *inst,
/* get resource pool */
if (get_astring_val(pg, SCF_PROPERTY_RESOURCE_POOL, cip->vbuf,
- cip->vbuf_sz, prop, val) != 0) {
+ cip->vbuf_sz, prop, val) != 0) {
errstr = "Could not get value of resource pool.";
goto out;
}
@@ -2839,9 +2841,9 @@ restarter_set_method_context(struct method_context *cip, const char **fp)
* have access to the specified directory.
*/
if (cip->working_dir != NULL) {
- do
+ do {
r = chdir(cip->working_dir);
- while (r != 0 && errno == EINTR);
+ } while (r != 0 && errno == EINTR);
if (r != 0) {
*fp = "chdir";
ret = errno;
diff --git a/usr/src/lib/libscf/common/lowlevel.c b/usr/src/lib/libscf/common/lowlevel.c
index cd1c9a1459..2e31aa6e1f 100644
--- a/usr/src/lib/libscf/common/lowlevel.c
+++ b/usr/src/lib/libscf/common/lowlevel.c
@@ -6854,3 +6854,20 @@ _scf_request_backup(scf_handle_t *h, const char *name)
return (scf_set_error(proto_error(response.rpr_response)));
return (SCF_SUCCESS);
}
+
+int
+_scf_pg_is_read_protected(const scf_propertygroup_t *pg, boolean_t *out)
+{
+ char buf[REP_PROTOCOL_NAME_LEN];
+ ssize_t res;
+
+ res = datael_get_name(&pg->rd_d, buf, sizeof (buf),
+ RP_ENTITY_NAME_PGREADPROT);
+
+ if (res == -1)
+ return (-1);
+
+ if (uu_strtouint(buf, out, sizeof (*out), 0, 0, 1) == -1)
+ return (scf_set_error(SCF_ERROR_INTERNAL));
+ return (SCF_SUCCESS);
+}
diff --git a/usr/src/lib/libscf/common/mapfile-vers b/usr/src/lib/libscf/common/mapfile-vers
index 7ce168681a..d64e43b026 100644
--- a/usr/src/lib/libscf/common/mapfile-vers
+++ b/usr/src/lib/libscf/common/mapfile-vers
@@ -232,6 +232,7 @@ SUNWprivate_1.1 {
scf_set_count_property;
scf_simple_handle_destroy;
gen_filenms_from_fmri;
+ _scf_pg_is_read_protected;
local:
*;
};
diff --git a/usr/src/lib/libscf/common/midlevel.c b/usr/src/lib/libscf/common/midlevel.c
index ec642baaa3..7a45f6a34b 100644
--- a/usr/src/lib/libscf/common/midlevel.c
+++ b/usr/src/lib/libscf/common/midlevel.c
@@ -18,6 +18,7 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -303,7 +304,9 @@ fill_prop(scf_property_t *prop, const char *pgname, const char *propname,
}
if (iterret == -1) {
- if (scf_error() != SCF_ERROR_CONNECTION_BROKEN)
+ int err = scf_error();
+ if (err != SCF_ERROR_CONNECTION_BROKEN &&
+ err != SCF_ERROR_PERMISSION_DENIED)
(void) scf_set_error(SCF_ERROR_INTERNAL);
goto error1;
}
@@ -1804,7 +1807,12 @@ scf_simple_app_props_get(scf_handle_t *hin, const char *inst_fmri)
return (NULL);
}
} else {
- sys_fmri = strdup(inst_fmri);
+ if ((sys_fmri = strdup(inst_fmri)) == NULL) {
+ if (local_h)
+ scf_handle_destroy(h);
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ return (NULL);
+ }
}
if ((namelen = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH)) == -1) {
@@ -1856,6 +1864,9 @@ scf_simple_app_props_get(scf_handle_t *hin, const char *inst_fmri)
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto error1;
}
+ nextpg->pg_name = NULL;
+ nextpg->pg_next = NULL;
+ nextpg->pg_proplist = NULL;
thispg->pg_next = nextpg;
thispg = nextpg;
} else {
@@ -1874,8 +1885,6 @@ scf_simple_app_props_get(scf_handle_t *hin, const char *inst_fmri)
goto error1;
}
- nextpg->pg_next = NULL;
- nextpg->pg_proplist = NULL;
thisprop = NULL;
scf_iter_reset(propiter);
@@ -2005,6 +2014,7 @@ scf_simple_app_props_get(scf_handle_t *hin, const char *inst_fmri)
thisprop = NULL;
if ((nextpg->pg_name = strdup(pgname)) == NULL) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
free(nextpg);
goto error1;
}
diff --git a/usr/src/lib/libscf/inc/libscf_priv.h b/usr/src/lib/libscf/inc/libscf_priv.h
index a9b3e273f5..acffe5b5d8 100644
--- a/usr/src/lib/libscf/inc/libscf_priv.h
+++ b/usr/src/lib/libscf/inc/libscf_priv.h
@@ -292,6 +292,18 @@ scf_error_t scf_walk_fmri(scf_handle_t *, int, char **, int,
int _scf_request_backup(scf_handle_t *, const char *);
/*
+ * Determines whether a property group requires authorization to read; this
+ * does not in any way reflect whether the caller has that authorization.
+ * To determine that, the caller must attempt to read the value of one of the
+ * group's properties.
+ *
+ * Can fail with:
+ * _NOT_BOUND, _CONNECTION_BROKEN, _INVALID_ARGUMENT, _INTERNAL,
+ * _NO_RESOURCES, _CONSTRAINT_VIOLATED, _DELETED.
+ */
+int _scf_pg_is_read_protected(const scf_propertygroup_t *, boolean_t *);
+
+/*
* scf_pattern_t
*/
typedef struct scf_pattern {