#include <assert.h>
#include <atomic.h>
#include <bsm/adt_event.h>
#include <errno.h>
#include <libuutil.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <pthread.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>
#include <secdb.h>
#include "configd.h"
#define AUTH_PREFIX "solaris.smf."
#define AUTH_MANAGE AUTH_PREFIX "manage"
#define AUTH_MODIFY AUTH_PREFIX "modify"
#define AUTH_MODIFY_PREFIX AUTH_MODIFY "."
#define AUTH_PG_ACTIONS SCF_PG_RESTARTER_ACTIONS
#define AUTH_PG_ACTIONS_TYPE SCF_PG_RESTARTER_ACTIONS_TYPE
#define AUTH_PG_GENERAL SCF_PG_GENERAL
#define AUTH_PG_GENERAL_TYPE SCF_PG_GENERAL_TYPE
#define AUTH_PG_GENERAL_OVR SCF_PG_GENERAL_OVR
#define AUTH_PG_GENERAL_OVR_TYPE SCF_PG_GENERAL_OVR_TYPE
#define AUTH_PROP_ACTION "action_authorization"
#define AUTH_PROP_ENABLED "enabled"
#define AUTH_PROP_MODIFY "modify_authorization"
#define AUTH_PROP_VALUE "value_authorization"
#define AUTH_PROP_READ "read_authorization"
#define MAX_VALID_CHILDREN 3
typedef struct rc_type_info {
uint32_t rt_type;
uint32_t rt_num_ids;
uint32_t rt_name_flags;
uint32_t rt_valid_children[MAX_VALID_CHILDREN];
} rc_type_info_t;
#define RT_NO_NAME -1U
static rc_type_info_t rc_types[] = {
{REP_PROTOCOL_ENTITY_NONE, 0, RT_NO_NAME},
{REP_PROTOCOL_ENTITY_SCOPE, 0, 0,
{REP_PROTOCOL_ENTITY_SERVICE, REP_PROTOCOL_ENTITY_SCOPE}},
{REP_PROTOCOL_ENTITY_SERVICE, 0, UU_NAME_DOMAIN | UU_NAME_PATH,
{REP_PROTOCOL_ENTITY_INSTANCE, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
{REP_PROTOCOL_ENTITY_INSTANCE, 1, UU_NAME_DOMAIN,
{REP_PROTOCOL_ENTITY_SNAPSHOT, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
{REP_PROTOCOL_ENTITY_SNAPSHOT, 2, UU_NAME_DOMAIN,
{REP_PROTOCOL_ENTITY_SNAPLEVEL, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
{REP_PROTOCOL_ENTITY_SNAPLEVEL, 4, RT_NO_NAME,
{REP_PROTOCOL_ENTITY_PROPERTYGRP}},
{REP_PROTOCOL_ENTITY_PROPERTYGRP, 5, UU_NAME_DOMAIN,
{REP_PROTOCOL_ENTITY_PROPERTY}},
{REP_PROTOCOL_ENTITY_CPROPERTYGRP, 0, UU_NAME_DOMAIN,
{REP_PROTOCOL_ENTITY_PROPERTY}},
{REP_PROTOCOL_ENTITY_PROPERTY, 7, UU_NAME_DOMAIN},
{-1UL}
};
#define NUM_TYPES ((sizeof (rc_types) / sizeof (*rc_types)))
struct pc_elt {
struct pc_elt *pce_next;
char pce_auth[1];
};
typedef enum pc_auth_type {
PC_AUTH_NONE = 0,
PC_AUTH_SMF,
PC_AUTH_SVC,
PC_AUTH_INST
} pc_auth_type_t;
typedef enum perm_status {
PERM_DENIED = 0,
PERM_GRANTED,
PERM_GONE,
PERM_FAIL
} perm_status_t;
typedef struct {
struct pc_elt **pc_buckets;
uint_t pc_bnum;
uint_t pc_enum;
struct pc_elt *pc_specific;
pc_auth_type_t pc_specific_type;
char *pc_auth_string;
} permcheck_t;
typedef struct audit_event_data {
char *ed_auth;
char *ed_fmri;
char *ed_snapname;
char *ed_old_fmri;
char *ed_old_name;
char *ed_type;
char *ed_prop_value;
} audit_event_data_t;
typedef int (*spc_getid_fn_t)(tx_commit_data_t *, size_t, const char *,
au_event_t *);
static int general_enable_id(tx_commit_data_t *, size_t, const char *,
au_event_t *);
static uu_list_pool_t *rc_children_pool;
static uu_list_pool_t *rc_pg_notify_pool;
static uu_list_pool_t *rc_notify_pool;
static uu_list_pool_t *rc_notify_info_pool;
static rc_node_t *rc_scope;
static pthread_mutex_t rc_pg_notify_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t rc_pg_notify_cv = PTHREAD_COND_INITIALIZER;
static uint_t rc_notify_in_use;
typedef struct audit_special_prop_item {
const char *api_pg_name;
const char *api_prop_name;
au_event_t api_event_id;
spc_getid_fn_t api_event_func;
} audit_special_prop_item_t;
#ifndef NATIVE_BUILD
static audit_special_prop_item_t special_props_list[] = {
{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_DEGRADED, ADT_smf_degrade,
NULL},
{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_DEGRADE_IMMEDIATE,
ADT_smf_immediate_degrade, NULL},
{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_OFF, ADT_smf_clear, NULL},
{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON,
ADT_smf_maintenance, NULL},
{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_IMMEDIATE,
ADT_smf_immediate_maintenance, NULL},
{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_IMMTEMP,
ADT_smf_immtmp_maintenance, NULL},
{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_TEMPORARY,
ADT_smf_tmp_maintenance, NULL},
{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_REFRESH, ADT_smf_refresh, NULL},
{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_RESTART, ADT_smf_restart, NULL},
{SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_RESTORE, ADT_smf_clear, NULL},
{SCF_PG_OPTIONS, SCF_PROPERTY_MILESTONE, ADT_smf_milestone, NULL},
{SCF_PG_OPTIONS_OVR, SCF_PROPERTY_MILESTONE, ADT_smf_milestone, NULL},
{SCF_PG_GENERAL, SCF_PROPERTY_ENABLED, 0, general_enable_id},
{SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED, 0, general_enable_id}
};
#define SPECIAL_PROP_COUNT (sizeof (special_props_list) /\
sizeof (audit_special_prop_item_t))
#endif
static uu_list_t *rc_notify_info_list;
static uu_list_t *rc_notify_list;
#define HASH_SIZE 512
#define HASH_MASK (HASH_SIZE - 1)
#pragma align 64(cache_hash)
static cache_bucket_t cache_hash[HASH_SIZE];
#define CACHE_BUCKET(h) (&cache_hash[(h) & HASH_MASK])
static void rc_node_no_client_refs(rc_node_t *np);
static uint32_t
rc_node_hash(rc_node_lookup_t *lp)
{
uint32_t type = lp->rl_type;
uint32_t backend = lp->rl_backend;
uint32_t mainid = lp->rl_main_id;
uint32_t *ids = lp->rl_ids;
rc_type_info_t *tp = &rc_types[type];
uint32_t num_ids;
uint32_t left;
uint32_t hash;
assert(backend == BACKEND_TYPE_NORMAL ||
backend == BACKEND_TYPE_NONPERSIST);
assert(type > 0 && type < NUM_TYPES);
num_ids = tp->rt_num_ids;
left = MAX_IDS - num_ids;
assert(num_ids <= MAX_IDS);
hash = type * 7 + mainid * 5 + backend;
while (num_ids-- > 0)
hash = hash * 11 + *ids++ * 7;
while (left-- > 0)
assert(*ids++ == 0);
return (hash);
}
static int
rc_node_match(rc_node_t *np, rc_node_lookup_t *l)
{
rc_node_lookup_t *r = &np->rn_id;
rc_type_info_t *tp;
uint32_t type;
uint32_t num_ids;
if (r->rl_main_id != l->rl_main_id)
return (0);
type = r->rl_type;
if (type != l->rl_type)
return (0);
assert(type > 0 && type < NUM_TYPES);
tp = &rc_types[r->rl_type];
num_ids = tp->rt_num_ids;
assert(num_ids <= MAX_IDS);
while (num_ids-- > 0)
if (r->rl_ids[num_ids] != l->rl_ids[num_ids])
return (0);
return (1);
}
static void
rc_node_hold_ephemeral_locked(rc_node_t *np)
{
assert(MUTEX_HELD(&np->rn_lock));
++np->rn_erefs;
}
static void
rc_node_hold_other(rc_node_t *np)
{
if (atomic_add_32_nv(&np->rn_other_refs, 1) == 1) {
atomic_add_32(&np->rn_other_refs_held, 1);
assert(np->rn_other_refs_held > 0);
}
assert(np->rn_other_refs > 0);
}
static void
rc_node_rele_other(rc_node_t *np)
{
assert(np->rn_other_refs > 0);
if (atomic_add_32_nv(&np->rn_other_refs, -1) == 0) {
(void) pthread_mutex_lock(&np->rn_lock);
assert(np->rn_other_refs_held > 0);
if (atomic_add_32_nv(&np->rn_other_refs_held, -1) == 0 &&
np->rn_refs == 0 && (np->rn_flags & RC_NODE_OLD)) {
rc_node_no_client_refs(np);
} else {
(void) pthread_mutex_unlock(&np->rn_lock);
}
}
}
static void
rc_node_hold_locked(rc_node_t *np)
{
assert(MUTEX_HELD(&np->rn_lock));
if (np->rn_refs == 0 && (np->rn_flags & RC_NODE_PARENT_REF))
rc_node_hold_other(np->rn_parent_ref);
np->rn_refs++;
assert(np->rn_refs > 0);
}
static void
rc_node_hold(rc_node_t *np)
{
(void) pthread_mutex_lock(&np->rn_lock);
rc_node_hold_locked(np);
(void) pthread_mutex_unlock(&np->rn_lock);
}
static void
rc_node_rele_locked(rc_node_t *np)
{
int unref = 0;
rc_node_t *par_ref = NULL;
assert(MUTEX_HELD(&np->rn_lock));
assert(np->rn_refs > 0);
if (--np->rn_refs == 0) {
if (np->rn_flags & RC_NODE_PARENT_REF)
par_ref = np->rn_parent_ref;
if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP)
np->rn_flags |= RC_NODE_DEAD;
if ((np->rn_flags & (RC_NODE_DEAD|RC_NODE_OLD)) &&
np->rn_other_refs == 0 && np->rn_other_refs_held == 0)
unref = 1;
}
if (unref) {
rc_node_no_client_refs(np);
} else {
if (np->rn_erefs > 0)
--np->rn_erefs;
(void) pthread_mutex_unlock(&np->rn_lock);
}
if (par_ref != NULL)
rc_node_rele_other(par_ref);
}
void
rc_node_rele(rc_node_t *np)
{
(void) pthread_mutex_lock(&np->rn_lock);
rc_node_rele_locked(np);
}
static cache_bucket_t *
cache_hold(uint32_t h)
{
cache_bucket_t *bp = CACHE_BUCKET(h);
(void) pthread_mutex_lock(&bp->cb_lock);
return (bp);
}
static void
cache_release(cache_bucket_t *bp)
{
(void) pthread_mutex_unlock(&bp->cb_lock);
}
static rc_node_t *
cache_lookup_unlocked(cache_bucket_t *bp, rc_node_lookup_t *lp)
{
uint32_t h = rc_node_hash(lp);
rc_node_t *np;
assert(MUTEX_HELD(&bp->cb_lock));
assert(bp == CACHE_BUCKET(h));
for (np = bp->cb_head; np != NULL; np = np->rn_hash_next) {
if (np->rn_hash == h && rc_node_match(np, lp)) {
rc_node_hold(np);
return (np);
}
}
return (NULL);
}
static rc_node_t *
cache_lookup(rc_node_lookup_t *lp)
{
uint32_t h;
cache_bucket_t *bp;
rc_node_t *np;
h = rc_node_hash(lp);
bp = cache_hold(h);
np = cache_lookup_unlocked(bp, lp);
cache_release(bp);
return (np);
}
static void
cache_insert_unlocked(cache_bucket_t *bp, rc_node_t *np)
{
assert(MUTEX_HELD(&bp->cb_lock));
assert(np->rn_hash == rc_node_hash(&np->rn_id));
assert(bp == CACHE_BUCKET(np->rn_hash));
assert(np->rn_hash_next == NULL);
np->rn_hash_next = bp->cb_head;
bp->cb_head = np;
}
static void
cache_remove_unlocked(cache_bucket_t *bp, rc_node_t *np)
{
rc_node_t **npp;
assert(MUTEX_HELD(&bp->cb_lock));
assert(np->rn_hash == rc_node_hash(&np->rn_id));
assert(bp == CACHE_BUCKET(np->rn_hash));
for (npp = &bp->cb_head; *npp != NULL; npp = &(*npp)->rn_hash_next)
if (*npp == np)
break;
assert(*npp == np);
*npp = np->rn_hash_next;
np->rn_hash_next = NULL;
}
static int
rc_check_parent_child(uint32_t parent, uint32_t child)
{
int idx;
uint32_t type;
if (parent == 0 || parent >= NUM_TYPES ||
child == 0 || child >= NUM_TYPES)
return (REP_PROTOCOL_FAIL_INVALID_TYPE);
for (idx = 0; idx < MAX_VALID_CHILDREN; idx++) {
type = rc_types[parent].rt_valid_children[idx];
if (type == child)
return (REP_PROTOCOL_SUCCESS);
}
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
int
rc_check_type_name(uint32_t type, const char *name)
{
if (type == 0 || type >= NUM_TYPES)
return (REP_PROTOCOL_FAIL_INVALID_TYPE);
if (uu_check_name(name, rc_types[type].rt_name_flags) == -1)
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
return (REP_PROTOCOL_SUCCESS);
}
static int
rc_check_pgtype_name(const char *name)
{
if (uu_check_name(name, UU_NAME_DOMAIN) == -1)
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
return (REP_PROTOCOL_SUCCESS);
}
static void
rc_node_free_fmri(rc_node_t *np)
{
if (np->rn_fmri != NULL) {
free((void *)np->rn_fmri);
np->rn_fmri = NULL;
}
}
static int
rc_concat_fmri_element(
char *fmri,
size_t bufsize,
size_t *sz_out,
const char *element,
rep_protocol_entity_t type)
{
size_t actual;
const char *name = element;
int rc;
const char *separator;
if (bufsize > 0)
*sz_out = strlen(fmri);
else
*sz_out = 0;
switch (type) {
case REP_PROTOCOL_ENTITY_SCOPE:
if (strcmp(element, SCF_FMRI_LOCAL_SCOPE) == 0) {
separator = SCF_FMRI_SVC_PREFIX;
name = NULL;
} else {
separator = SCF_FMRI_SVC_PREFIX SCF_FMRI_SCOPE_PREFIX;
}
break;
case REP_PROTOCOL_ENTITY_SERVICE:
separator = SCF_FMRI_SERVICE_PREFIX;
break;
case REP_PROTOCOL_ENTITY_INSTANCE:
separator = SCF_FMRI_INSTANCE_PREFIX;
break;
case REP_PROTOCOL_ENTITY_PROPERTYGRP:
case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
separator = SCF_FMRI_PROPERTYGRP_PREFIX;
break;
case REP_PROTOCOL_ENTITY_PROPERTY:
separator = SCF_FMRI_PROPERTY_PREFIX;
break;
case REP_PROTOCOL_ENTITY_VALUE:
return (REP_PROTOCOL_SUCCESS);
case REP_PROTOCOL_ENTITY_SNAPSHOT:
case REP_PROTOCOL_ENTITY_SNAPLEVEL:
return (REP_PROTOCOL_SUCCESS);
default:
(void) fprintf(stderr, "%s:%d: Unknown protocol type %d.\n",
__FILE__, __LINE__, type);
abort();
}
actual = strlcat(fmri, separator, bufsize);
if (name != NULL) {
if (actual < bufsize) {
actual = strlcat(fmri, name, bufsize);
} else {
actual += strlen(name);
}
}
if (actual < bufsize) {
rc = REP_PROTOCOL_SUCCESS;
} else {
rc = REP_PROTOCOL_FAIL_TRUNCATED;
}
*sz_out = actual;
return (rc);
}
static int
rc_node_get_fmri_or_fragment(rc_node_t *np, char *buf, size_t bufsize,
size_t *sz_out)
{
size_t fmri_len = 0;
int r;
if (bufsize > 0)
*buf = 0;
*sz_out = 0;
if (np->rn_fmri == NULL) {
assert(np->rn_parent == NULL);
r = rc_concat_fmri_element(buf, bufsize, &fmri_len, np->rn_name,
np->rn_id.rl_type);
if (r != REP_PROTOCOL_SUCCESS)
return (r);
} else {
fmri_len = strlcpy(buf, np->rn_fmri, bufsize);
}
*sz_out = fmri_len;
if (fmri_len >= bufsize)
return (REP_PROTOCOL_FAIL_TRUNCATED);
return (REP_PROTOCOL_SUCCESS);
}
static int
rc_node_build_fmri(rc_node_t *np)
{
size_t actual;
char fmri[REP_PROTOCOL_FMRI_LEN];
int rc;
size_t sz = REP_PROTOCOL_FMRI_LEN;
assert(MUTEX_HELD(&np->rn_lock));
assert(np->rn_flags & RC_NODE_USING_PARENT);
rc_node_free_fmri(np);
rc = rc_node_get_fmri_or_fragment(np->rn_parent, fmri, sz, &actual);
assert(rc == REP_PROTOCOL_SUCCESS);
if (np->rn_name != NULL) {
rc = rc_concat_fmri_element(fmri, sz, &actual, np->rn_name,
np->rn_id.rl_type);
assert(rc == REP_PROTOCOL_SUCCESS);
np->rn_fmri = strdup(fmri);
} else {
np->rn_fmri = strdup(fmri);
}
if (np->rn_fmri == NULL) {
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
} else {
rc = REP_PROTOCOL_SUCCESS;
}
return (rc);
}
static int
rc_get_fmri_and_concat(rc_node_t *np, char *fmri, size_t size, size_t *sz_out,
const char *element, rep_protocol_entity_t type)
{
int rc;
if ((rc = rc_node_get_fmri_or_fragment(np, fmri, size, sz_out)) !=
REP_PROTOCOL_SUCCESS) {
return (rc);
}
if ((rc = rc_concat_fmri_element(fmri, size, sz_out, element, type)) !=
REP_PROTOCOL_SUCCESS) {
return (rc);
}
return (REP_PROTOCOL_SUCCESS);
}
static int
rc_notify_info_interested(rc_notify_info_t *rnip, rc_notify_t *np)
{
rc_node_t *nnp = np->rcn_node;
int i;
assert(MUTEX_HELD(&rc_pg_notify_lock));
if (np->rcn_delete != NULL) {
assert(np->rcn_info == NULL && np->rcn_node == NULL);
return (1);
}
if (np->rcn_node == NULL) {
assert(np->rcn_info != NULL || np->rcn_delete != NULL);
return (0);
}
assert(np->rcn_info == NULL);
for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
if (rnip->rni_namelist[i] != NULL) {
if (strcmp(nnp->rn_name, rnip->rni_namelist[i]) == 0)
return (1);
}
if (rnip->rni_typelist[i] != NULL) {
if (strcmp(nnp->rn_type, rnip->rni_typelist[i]) == 0)
return (1);
}
}
return (0);
}
static void
rc_notify_insert_node(rc_node_t *nnp)
{
rc_notify_t *np = &nnp->rn_notify;
rc_notify_info_t *nip;
int found = 0;
assert(np->rcn_info == NULL);
if (nnp->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
return;
(void) pthread_mutex_lock(&rc_pg_notify_lock);
np->rcn_node = nnp;
for (nip = uu_list_first(rc_notify_info_list); nip != NULL;
nip = uu_list_next(rc_notify_info_list, nip)) {
if (rc_notify_info_interested(nip, np)) {
(void) pthread_cond_broadcast(&nip->rni_cv);
found++;
}
}
if (found)
(void) uu_list_insert_before(rc_notify_list, NULL, np);
else
np->rcn_node = NULL;
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
}
static void
rc_notify_deletion(rc_notify_delete_t *ndp, const char *service,
const char *instance, const char *pg)
{
rc_notify_info_t *nip;
uu_list_node_init(&ndp->rnd_notify, &ndp->rnd_notify.rcn_list_node,
rc_notify_pool);
ndp->rnd_notify.rcn_delete = ndp;
(void) snprintf(ndp->rnd_fmri, sizeof (ndp->rnd_fmri),
"svc:/%s%s%s%s%s", service,
(instance != NULL)? ":" : "", (instance != NULL)? instance : "",
(pg != NULL)? "/:properties/" : "", (pg != NULL)? pg : "");
(void) pthread_mutex_lock(&rc_pg_notify_lock);
for (nip = uu_list_first(rc_notify_info_list); nip != NULL;
nip = uu_list_next(rc_notify_info_list, nip))
(void) pthread_cond_broadcast(&nip->rni_cv);
(void) uu_list_insert_before(rc_notify_list, NULL, ndp);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
}
static void
rc_notify_remove_node(rc_node_t *nnp)
{
rc_notify_t *np = &nnp->rn_notify;
assert(np->rcn_info == NULL);
assert(!MUTEX_HELD(&nnp->rn_lock));
(void) pthread_mutex_lock(&rc_pg_notify_lock);
while (np->rcn_node != NULL) {
if (rc_notify_in_use) {
(void) pthread_cond_wait(&rc_pg_notify_cv,
&rc_pg_notify_lock);
continue;
}
(void) uu_list_remove(rc_notify_list, np);
np->rcn_node = NULL;
break;
}
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
}
static void
rc_notify_remove_locked(rc_notify_t *np)
{
assert(MUTEX_HELD(&rc_pg_notify_lock));
assert(rc_notify_in_use == 0);
(void) uu_list_remove(rc_notify_list, np);
if (np->rcn_node) {
np->rcn_node = NULL;
} else if (np->rcn_delete) {
uu_free(np->rcn_delete);
} else {
assert(0);
}
}
#ifndef NATIVE_BUILD
static permcheck_t *
pc_create()
{
permcheck_t *p;
p = uu_zalloc(sizeof (*p));
if (p == NULL)
return (NULL);
p->pc_bnum = 8;
p->pc_buckets = uu_zalloc(sizeof (*p->pc_buckets) * p->pc_bnum);
if (p->pc_buckets == NULL) {
uu_free(p);
return (NULL);
}
p->pc_enum = 0;
return (p);
}
static void
pc_free(permcheck_t *pcp)
{
uint_t i;
struct pc_elt *ep, *next;
for (i = 0; i < pcp->pc_bnum; ++i) {
for (ep = pcp->pc_buckets[i]; ep != NULL; ep = next) {
next = ep->pce_next;
free(ep);
}
}
free(pcp->pc_buckets);
free(pcp);
}
static uint32_t
pc_hash(const char *auth)
{
uint32_t h = 0, g;
const char *p;
for (p = auth; *p != '\0'; ++p) {
h = (h << 4) + *p;
g = (h & 0xf0000000);
if (g != 0) {
h ^= (g >> 24);
h ^= g;
}
}
return (h);
}
static perm_status_t
pc_exists(permcheck_t *pcp, const char *auth)
{
uint32_t h;
struct pc_elt *ep;
h = pc_hash(auth);
for (ep = pcp->pc_buckets[h & (pcp->pc_bnum - 1)];
ep != NULL;
ep = ep->pce_next) {
if (strcmp(auth, ep->pce_auth) == 0) {
pcp->pc_auth_string = ep->pce_auth;
return (PERM_GRANTED);
}
}
return (PERM_DENIED);
}
static perm_status_t
pc_match(permcheck_t *pcp, const char *pattern)
{
uint_t i;
struct pc_elt *ep;
for (i = 0; i < pcp->pc_bnum; ++i) {
for (ep = pcp->pc_buckets[i]; ep != NULL; ep = ep->pce_next) {
if (_auth_match(pattern, ep->pce_auth)) {
pcp->pc_auth_string = ep->pce_auth;
return (PERM_GRANTED);
}
}
}
return (PERM_DENIED);
}
static int
pc_grow(permcheck_t *pcp)
{
uint_t new_bnum, i, j;
struct pc_elt **new_buckets;
struct pc_elt *ep, *next;
new_bnum = pcp->pc_bnum * 2;
if (new_bnum < pcp->pc_bnum)
return (-1);
new_buckets = uu_zalloc(sizeof (*new_buckets) * new_bnum);
if (new_buckets == NULL)
return (-1);
for (i = 0; i < pcp->pc_bnum; ++i) {
for (ep = pcp->pc_buckets[i]; ep != NULL; ep = next) {
next = ep->pce_next;
j = pc_hash(ep->pce_auth) & (new_bnum - 1);
ep->pce_next = new_buckets[j];
new_buckets[j] = ep;
}
}
uu_free(pcp->pc_buckets);
pcp->pc_buckets = new_buckets;
pcp->pc_bnum = new_bnum;
return (0);
}
static int
pc_add(permcheck_t *pcp, const char *auth, pc_auth_type_t auth_type)
{
struct pc_elt *ep;
uint_t i;
ep = uu_zalloc(offsetof(struct pc_elt, pce_auth) + strlen(auth) + 1);
if (ep == NULL)
return (-1);
if (pcp->pc_enum * 4 > 3 * pcp->pc_bnum)
(void) pc_grow(pcp);
(void) strcpy(ep->pce_auth, auth);
i = pc_hash(auth) & (pcp->pc_bnum - 1);
ep->pce_next = pcp->pc_buckets[i];
pcp->pc_buckets[i] = ep;
if (auth_type > pcp->pc_specific_type) {
pcp->pc_specific_type = auth_type;
pcp->pc_specific = ep;
}
++pcp->pc_enum;
return (0);
}
static const char *
perm_auth_for_pgtype(const char *pgtype)
{
if (strcmp(pgtype, SCF_GROUP_METHOD) == 0)
return (AUTH_MODIFY_PREFIX "method");
else if (strcmp(pgtype, SCF_GROUP_DEPENDENCY) == 0)
return (AUTH_MODIFY_PREFIX "dependency");
else if (strcmp(pgtype, SCF_GROUP_APPLICATION) == 0)
return (AUTH_MODIFY_PREFIX "application");
else if (strcmp(pgtype, SCF_GROUP_FRAMEWORK) == 0)
return (AUTH_MODIFY_PREFIX "framework");
else
return (NULL);
}
static int
perm_add_enabling_type(permcheck_t *pcp, const char *auth,
pc_auth_type_t auth_type)
{
return (pc_add(pcp, auth, auth_type) == 0 ? REP_PROTOCOL_SUCCESS :
REP_PROTOCOL_FAIL_NO_RESOURCES);
}
static int
perm_add_enabling(permcheck_t *pcp, const char *auth)
{
return (perm_add_enabling_type(pcp, auth, PC_AUTH_SMF));
}
static int
auth_cb(const char *auth, void *ctxt, void *vres)
{
permcheck_t *pcp = ctxt;
int *pret = vres;
if (strchr(auth, KV_WILDCHAR) == NULL)
*pret = pc_exists(pcp, auth);
else
*pret = pc_match(pcp, auth);
if (*pret != PERM_DENIED)
return (1);
assert(pcp->pc_specific != NULL);
pcp->pc_auth_string = pcp->pc_specific->pce_auth;
return (0);
}
static perm_status_t
perm_granted(permcheck_t *pcp)
{
ucred_t *uc;
perm_status_t ret = PERM_DENIED;
uid_t uid;
struct passwd pw;
char pwbuf[1024];
if ((uc = get_ucred()) == NULL) {
if (errno == EINVAL) {
return (PERM_GONE);
}
assert(0);
abort();
}
uid = ucred_geteuid(uc);
assert(uid != (uid_t)-1);
if (getpwuid_r(uid, &pw, pwbuf, sizeof (pwbuf)) == NULL) {
return (PERM_FAIL);
}
if (_enum_auths(pw.pw_name, auth_cb, pcp, &ret) < 0)
return (PERM_FAIL);
return (ret);
}
static int
map_granted_status(perm_status_t status, permcheck_t *pcp,
char **match_auth)
{
int rc;
*match_auth = NULL;
switch (status) {
case PERM_DENIED:
*match_auth = strdup(pcp->pc_auth_string);
if (*match_auth == NULL)
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
else
rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
break;
case PERM_GRANTED:
*match_auth = strdup(pcp->pc_auth_string);
if (*match_auth == NULL)
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
else
rc = REP_PROTOCOL_SUCCESS;
break;
case PERM_GONE:
rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
break;
case PERM_FAIL:
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
break;
}
return (rc);
}
#endif
static int
rc_node_hold_flag(rc_node_t *np, uint32_t flag)
{
assert(MUTEX_HELD(&np->rn_lock));
assert((flag & ~RC_NODE_WAITING_FLAGS) == 0);
while (!(np->rn_flags & RC_NODE_DEAD) && (np->rn_flags & flag)) {
(void) pthread_cond_wait(&np->rn_cv, &np->rn_lock);
}
if (np->rn_flags & RC_NODE_DEAD)
return (0);
np->rn_flags |= flag;
return (1);
}
static void
rc_node_rele_flag(rc_node_t *np, uint32_t flag)
{
assert((flag & ~RC_NODE_WAITING_FLAGS) == 0);
assert(MUTEX_HELD(&np->rn_lock));
assert((np->rn_flags & flag) == flag);
np->rn_flags &= ~flag;
(void) pthread_cond_broadcast(&np->rn_cv);
}
static int
rc_node_wait_flag(rc_node_t *np, uint32_t flag)
{
assert(MUTEX_HELD(&np->rn_lock));
while (!(np->rn_flags & RC_NODE_DEAD) && (np->rn_flags & flag))
(void) pthread_cond_wait(&np->rn_cv, &np->rn_lock);
return (!(np->rn_flags & RC_NODE_DEAD));
}
static rc_node_t *
rc_node_hold_parent_flag(rc_node_t *np, uint32_t flag)
{
rc_node_t *pp;
assert(MUTEX_HELD(&np->rn_lock));
assert(np->rn_flags & RC_NODE_USING_PARENT);
if ((pp = np->rn_parent) == NULL) {
rc_node_rele_flag(np, RC_NODE_USING_PARENT);
(void) pthread_mutex_unlock(&np->rn_lock);
return (NULL);
}
(void) pthread_mutex_unlock(&np->rn_lock);
(void) pthread_mutex_lock(&pp->rn_lock);
(void) pthread_mutex_lock(&np->rn_lock);
rc_node_rele_flag(np, RC_NODE_USING_PARENT);
(void) pthread_mutex_unlock(&np->rn_lock);
if (!rc_node_hold_flag(pp, flag)) {
(void) pthread_mutex_unlock(&pp->rn_lock);
return (NULL);
}
return (pp);
}
rc_node_t *
rc_node_alloc(void)
{
rc_node_t *np = uu_zalloc(sizeof (*np));
if (np == NULL)
return (NULL);
(void) pthread_mutex_init(&np->rn_lock, NULL);
(void) pthread_cond_init(&np->rn_cv, NULL);
np->rn_children = uu_list_create(rc_children_pool, np, 0);
np->rn_pg_notify_list = uu_list_create(rc_pg_notify_pool, np, 0);
uu_list_node_init(np, &np->rn_sibling_node, rc_children_pool);
uu_list_node_init(&np->rn_notify, &np->rn_notify.rcn_list_node,
rc_notify_pool);
return (np);
}
void
rc_node_destroy(rc_node_t *np)
{
int i;
if (np->rn_flags & RC_NODE_UNREFED)
return;
assert(np->rn_refs == 0 && np->rn_other_refs == 0);
assert(np->rn_former == NULL);
if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
for (i = 0; i < COMPOSITION_DEPTH; ++i) {
if (np->rn_cchain[i] != NULL)
rc_node_rele(np->rn_cchain[i]);
}
}
if (np->rn_name != NULL)
free((void *)np->rn_name);
np->rn_name = NULL;
if (np->rn_type != NULL)
free((void *)np->rn_type);
np->rn_type = NULL;
if (np->rn_values != NULL)
object_free_values(np->rn_values, np->rn_valtype,
np->rn_values_count, np->rn_values_size);
np->rn_values = NULL;
rc_node_free_fmri(np);
if (np->rn_snaplevel != NULL)
rc_snaplevel_rele(np->rn_snaplevel);
np->rn_snaplevel = NULL;
uu_list_node_fini(np, &np->rn_sibling_node, rc_children_pool);
uu_list_node_fini(&np->rn_notify, &np->rn_notify.rcn_list_node,
rc_notify_pool);
assert(uu_list_first(np->rn_children) == NULL);
uu_list_destroy(np->rn_children);
uu_list_destroy(np->rn_pg_notify_list);
(void) pthread_mutex_destroy(&np->rn_lock);
(void) pthread_cond_destroy(&np->rn_cv);
uu_free(np);
}
static void
rc_node_link_child(rc_node_t *np, rc_node_t *cp)
{
assert(!MUTEX_HELD(&np->rn_lock));
assert(!MUTEX_HELD(&cp->rn_lock));
(void) pthread_mutex_lock(&np->rn_lock);
(void) pthread_mutex_lock(&cp->rn_lock);
assert(!(cp->rn_flags & RC_NODE_IN_PARENT) &&
(cp->rn_flags & RC_NODE_USING_PARENT));
assert(rc_check_parent_child(np->rn_id.rl_type, cp->rn_id.rl_type) ==
REP_PROTOCOL_SUCCESS);
cp->rn_parent = np;
cp->rn_flags |= RC_NODE_IN_PARENT;
(void) uu_list_insert_before(np->rn_children, NULL, cp);
(void) rc_node_build_fmri(cp);
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_rele_flag(cp, RC_NODE_USING_PARENT);
(void) pthread_mutex_unlock(&cp->rn_lock);
}
static void
rc_node_setup_parent_ref(rc_node_t *np, rc_node_t *pp)
{
rc_node_t *cp;
assert(MUTEX_HELD(&np->rn_lock));
for (cp = uu_list_first(np->rn_children); cp != NULL;
cp = uu_list_next(np->rn_children, cp)) {
(void) pthread_mutex_lock(&cp->rn_lock);
if (cp->rn_flags & RC_NODE_PARENT_REF) {
assert(cp->rn_parent_ref == pp);
} else {
assert(cp->rn_parent_ref == NULL);
cp->rn_flags |= RC_NODE_PARENT_REF;
cp->rn_parent_ref = pp;
if (cp->rn_refs != 0)
rc_node_hold_other(pp);
}
rc_node_setup_parent_ref(cp, pp);
(void) pthread_mutex_unlock(&cp->rn_lock);
}
}
static void
rc_node_relink_child(rc_node_t *pp, rc_node_t *np, rc_node_t *newp)
{
cache_bucket_t *bp;
bp = cache_hold(newp->rn_hash);
cache_remove_unlocked(bp, np);
cache_insert_unlocked(bp, newp);
cache_release(bp);
(void) pthread_mutex_lock(&pp->rn_lock);
assert(pp->rn_flags & RC_NODE_CHILDREN_CHANGING);
(void) pthread_mutex_lock(&newp->rn_lock);
assert(!(newp->rn_flags & RC_NODE_IN_PARENT));
assert(newp->rn_flags & RC_NODE_IN_TX);
(void) pthread_mutex_lock(&np->rn_lock);
assert(np->rn_flags & RC_NODE_IN_PARENT);
assert(np->rn_flags & RC_NODE_OLD);
assert(np->rn_flags & RC_NODE_IN_TX);
newp->rn_parent = pp;
newp->rn_flags |= RC_NODE_IN_PARENT;
(void) uu_list_insert_after(pp->rn_children, np, newp);
(void) rc_node_build_fmri(newp);
(void) uu_list_remove(pp->rn_children, np);
newp->rn_former = np;
np->rn_parent = NULL;
np->rn_flags &= ~RC_NODE_IN_PARENT;
np->rn_flags |= RC_NODE_ON_FORMER;
rc_notify_insert_node(newp);
rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
(void) pthread_mutex_unlock(&pp->rn_lock);
rc_node_rele_flag(newp, RC_NODE_USING_PARENT | RC_NODE_IN_TX);
(void) pthread_mutex_unlock(&newp->rn_lock);
rc_node_setup_parent_ref(np, np);
rc_node_rele_flag(np, RC_NODE_IN_TX);
(void) pthread_mutex_unlock(&np->rn_lock);
}
rc_node_t *
rc_node_setup(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
rc_node_t *pp)
{
rc_node_t *np;
cache_bucket_t *bp;
uint32_t h = rc_node_hash(nip);
assert(cp->rn_refs == 0);
bp = cache_hold(h);
if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
cache_release(bp);
(void) pthread_mutex_lock(&np->rn_lock);
if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
assert(np->rn_parent == pp);
assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
assert(strcmp(np->rn_name, name) == 0);
assert(np->rn_type == NULL);
assert(np->rn_flags & RC_NODE_IN_PARENT);
rc_node_rele_flag(np, RC_NODE_USING_PARENT);
}
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_destroy(cp);
return (np);
}
np = cp;
rc_node_hold(np);
np->rn_id = *nip;
np->rn_hash = h;
np->rn_name = strdup(name);
np->rn_flags |= RC_NODE_USING_PARENT;
if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE) {
#if COMPOSITION_DEPTH == 2
np->rn_cchain[0] = np;
np->rn_cchain[1] = pp;
#else
#error This code must be updated.
#endif
}
cache_insert_unlocked(bp, np);
cache_release(bp);
rc_node_link_child(pp, np);
return (np);
}
rc_node_t *
rc_node_setup_snapshot(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
uint32_t snap_id, rc_node_t *pp)
{
rc_node_t *np;
cache_bucket_t *bp;
uint32_t h = rc_node_hash(nip);
assert(cp->rn_refs == 0);
bp = cache_hold(h);
if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
cache_release(bp);
(void) pthread_mutex_lock(&np->rn_lock);
if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
assert(np->rn_parent == pp);
assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
assert(strcmp(np->rn_name, name) == 0);
assert(np->rn_type == NULL);
assert(np->rn_flags & RC_NODE_IN_PARENT);
rc_node_rele_flag(np, RC_NODE_USING_PARENT);
}
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_destroy(cp);
return (np);
}
np = cp;
rc_node_hold(np);
np->rn_id = *nip;
np->rn_hash = h;
np->rn_name = strdup(name);
np->rn_snapshot_id = snap_id;
np->rn_flags |= RC_NODE_USING_PARENT;
cache_insert_unlocked(bp, np);
cache_release(bp);
rc_node_link_child(pp, np);
return (np);
}
rc_node_t *
rc_node_setup_snaplevel(rc_node_t *cp, rc_node_lookup_t *nip,
rc_snaplevel_t *lvl, rc_node_t *pp)
{
rc_node_t *np;
cache_bucket_t *bp;
uint32_t h = rc_node_hash(nip);
assert(cp->rn_refs == 0);
bp = cache_hold(h);
if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
cache_release(bp);
(void) pthread_mutex_lock(&np->rn_lock);
if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
assert(np->rn_parent == pp);
assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
assert(np->rn_name == NULL);
assert(np->rn_type == NULL);
assert(np->rn_flags & RC_NODE_IN_PARENT);
rc_node_rele_flag(np, RC_NODE_USING_PARENT);
}
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_destroy(cp);
return (np);
}
np = cp;
rc_node_hold(np);
np->rn_id = *nip;
np->rn_hash = h;
rc_snaplevel_hold(lvl);
np->rn_snaplevel = lvl;
np->rn_flags |= RC_NODE_USING_PARENT;
cache_insert_unlocked(bp, np);
cache_release(bp);
assert(pp->rn_cchain[lvl->rsl_level_num - 1] == NULL);
pp->rn_cchain[lvl->rsl_level_num - 1] = np;
rc_node_link_child(pp, np);
return (np);
}
rc_node_t *
rc_node_setup_pg(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
const char *type, uint32_t flags, uint32_t gen_id, rc_node_t *pp)
{
rc_node_t *np;
cache_bucket_t *bp;
uint32_t h = rc_node_hash(nip);
bp = cache_hold(h);
if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
cache_release(bp);
(void) pthread_mutex_lock(&np->rn_lock);
if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
assert(strcmp(np->rn_name, name) == 0);
assert(strcmp(np->rn_type, type) == 0);
assert(np->rn_pgflags == flags);
assert(np->rn_flags & RC_NODE_IN_PARENT);
rc_node_rele_flag(np, RC_NODE_USING_PARENT);
}
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_destroy(cp);
return (np);
}
np = cp;
rc_node_hold(np);
np->rn_id = *nip;
np->rn_hash = h;
np->rn_name = strdup(name);
if (np->rn_name == NULL) {
rc_node_rele(np);
return (NULL);
}
np->rn_type = strdup(type);
if (np->rn_type == NULL) {
free((void *)np->rn_name);
rc_node_rele(np);
return (NULL);
}
np->rn_pgflags = flags;
np->rn_gen_id = gen_id;
np->rn_flags |= RC_NODE_USING_PARENT;
cache_insert_unlocked(bp, np);
cache_release(bp);
rc_node_link_child(pp, np);
return (np);
}
#if COMPOSITION_DEPTH == 2
static int
rc_node_setup_cpg(rc_node_t *cpg, rc_node_t *pg1, rc_node_t *pg2)
{
if (strcmp(pg1->rn_type, pg2->rn_type) != 0)
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
cpg->rn_id.rl_type = REP_PROTOCOL_ENTITY_CPROPERTYGRP;
cpg->rn_name = strdup(pg1->rn_name);
if (cpg->rn_name == NULL)
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
cpg->rn_cchain[0] = pg1;
cpg->rn_cchain[1] = pg2;
return (REP_PROTOCOL_SUCCESS);
}
#else
#error This code must be updated.
#endif
int
rc_node_create_property(rc_node_t *pp, rc_node_lookup_t *nip,
const char *name, rep_protocol_value_type_t type,
const char *vals, size_t count, size_t size)
{
rc_node_t *np;
cache_bucket_t *bp;
uint32_t h = rc_node_hash(nip);
bp = cache_hold(h);
if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
cache_release(bp);
(void) pthread_mutex_lock(&np->rn_lock);
if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
assert(np->rn_parent == pp);
assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
assert(strcmp(np->rn_name, name) == 0);
assert(np->rn_valtype == type);
assert(np->rn_values_count == count);
assert(np->rn_values_size == size);
assert(vals == NULL ||
memcmp(np->rn_values, vals, size) == 0);
assert(np->rn_flags & RC_NODE_IN_PARENT);
rc_node_rele_flag(np, RC_NODE_USING_PARENT);
}
rc_node_rele_locked(np);
object_free_values(vals, type, count, size);
return (REP_PROTOCOL_SUCCESS);
}
np = rc_node_alloc();
if (np == NULL) {
cache_release(bp);
object_free_values(vals, type, count, size);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
np->rn_id = *nip;
np->rn_hash = h;
np->rn_name = strdup(name);
if (np->rn_name == NULL) {
cache_release(bp);
object_free_values(vals, type, count, size);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
np->rn_valtype = type;
np->rn_values = vals;
np->rn_values_count = count;
np->rn_values_size = size;
np->rn_flags |= RC_NODE_USING_PARENT;
cache_insert_unlocked(bp, np);
cache_release(bp);
rc_node_link_child(pp, np);
return (REP_PROTOCOL_SUCCESS);
}
#ifndef NATIVE_BUILD
static int
general_enable_id(tx_commit_data_t *tx_data, size_t cmd_no, const char *pg,
au_event_t *event_id)
{
const char *value;
uint32_t nvalues;
int enable;
if (tx_cmd_nvalues(tx_data, cmd_no, &nvalues) != REP_PROTOCOL_SUCCESS)
return (-1);
if (nvalues == 0)
return (-1);
if (tx_cmd_value(tx_data, cmd_no, 0, &value) != REP_PROTOCOL_SUCCESS)
return (-1);
if (strcmp(value, "0") == 0) {
enable = 0;
} else if (strcmp(value, "1") == 0) {
enable = 1;
} else {
return (-1);
}
if (strcmp(pg, SCF_PG_GENERAL) == 0) {
*event_id = enable ? ADT_smf_enable : ADT_smf_disable;
return (0);
} else if (strcmp(pg, SCF_PG_GENERAL_OVR) == 0) {
*event_id = enable ? ADT_smf_tmp_enable : ADT_smf_tmp_disable;
return (0);
}
return (-1);
}
#endif
static int
special_prop_compare(const void *item1, const void *item2)
{
const audit_special_prop_item_t *a = (audit_special_prop_item_t *)item1;
const audit_special_prop_item_t *b = (audit_special_prop_item_t *)item2;
int r;
r = strcmp(a->api_prop_name, b->api_prop_name);
if (r == 0) {
r = strcmp(a->api_pg_name, b->api_pg_name);
}
return (r);
}
int
rc_node_init(void)
{
rc_node_t *np;
cache_bucket_t *bp;
rc_children_pool = uu_list_pool_create("rc_children_pool",
sizeof (rc_node_t), offsetof(rc_node_t, rn_sibling_node),
NULL, UU_LIST_POOL_DEBUG);
rc_pg_notify_pool = uu_list_pool_create("rc_pg_notify_pool",
sizeof (rc_node_pg_notify_t),
offsetof(rc_node_pg_notify_t, rnpn_node),
NULL, UU_LIST_POOL_DEBUG);
rc_notify_pool = uu_list_pool_create("rc_notify_pool",
sizeof (rc_notify_t), offsetof(rc_notify_t, rcn_list_node),
NULL, UU_LIST_POOL_DEBUG);
rc_notify_info_pool = uu_list_pool_create("rc_notify_info_pool",
sizeof (rc_notify_info_t),
offsetof(rc_notify_info_t, rni_list_node),
NULL, UU_LIST_POOL_DEBUG);
if (rc_children_pool == NULL || rc_pg_notify_pool == NULL ||
rc_notify_pool == NULL || rc_notify_info_pool == NULL)
uu_die("out of memory");
rc_notify_list = uu_list_create(rc_notify_pool,
&rc_notify_list, 0);
rc_notify_info_list = uu_list_create(rc_notify_info_pool,
&rc_notify_info_list, 0);
if (rc_notify_list == NULL || rc_notify_info_list == NULL)
uu_die("out of memory");
#ifndef NATIVE_BUILD
qsort(special_props_list, SPECIAL_PROP_COUNT,
sizeof (special_props_list[0]), special_prop_compare);
#endif
if ((np = rc_node_alloc()) == NULL)
uu_die("out of memory");
rc_node_hold(np);
np->rn_id.rl_type = REP_PROTOCOL_ENTITY_SCOPE;
np->rn_id.rl_backend = BACKEND_TYPE_NORMAL;
np->rn_hash = rc_node_hash(&np->rn_id);
np->rn_name = "localhost";
bp = cache_hold(np->rn_hash);
cache_insert_unlocked(bp, np);
cache_release(bp);
rc_scope = np;
return (1);
}
static int
rc_node_fill_children(rc_node_t *np, uint32_t type)
{
int rc;
assert(MUTEX_HELD(&np->rn_lock));
if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
REP_PROTOCOL_SUCCESS)
return (rc);
if (!rc_node_hold_flag(np, RC_NODE_CHILDREN_CHANGING))
return (REP_PROTOCOL_FAIL_DELETED);
if (np->rn_flags & RC_NODE_HAS_CHILDREN) {
rc_node_rele_flag(np, RC_NODE_CHILDREN_CHANGING);
return (REP_PROTOCOL_SUCCESS);
}
(void) pthread_mutex_unlock(&np->rn_lock);
rc = object_fill_children(np);
(void) pthread_mutex_lock(&np->rn_lock);
if (rc == REP_PROTOCOL_SUCCESS) {
np->rn_flags |= RC_NODE_HAS_CHILDREN;
}
rc_node_rele_flag(np, RC_NODE_CHILDREN_CHANGING);
return (rc);
}
static int
rc_node_find_named_child(rc_node_t *np, const char *name, uint32_t type,
rc_node_t **cpp)
{
int ret;
rc_node_t *cp;
assert(MUTEX_HELD(&np->rn_lock));
assert(np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP);
ret = rc_node_fill_children(np, type);
if (ret != REP_PROTOCOL_SUCCESS)
return (ret);
for (cp = uu_list_first(np->rn_children);
cp != NULL;
cp = uu_list_next(np->rn_children, cp)) {
if (cp->rn_id.rl_type == type && strcmp(cp->rn_name, name) == 0)
break;
}
if (cp != NULL)
rc_node_hold(cp);
*cpp = cp;
return (REP_PROTOCOL_SUCCESS);
}
static int rc_node_parent(rc_node_t *, rc_node_t **);
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
static int
perm_add_pg_prop_values(permcheck_t *pcp, rc_node_t *pg, const char *propname)
{
rc_node_t *prop;
int result;
uint_t count;
const char *cp;
assert(!MUTEX_HELD(&pg->rn_lock));
assert(pg->rn_id.rl_type == REP_PROTOCOL_ENTITY_PROPERTYGRP);
(void) pthread_mutex_lock(&pg->rn_lock);
result = rc_node_find_named_child(pg, propname,
REP_PROTOCOL_ENTITY_PROPERTY, &prop);
(void) pthread_mutex_unlock(&pg->rn_lock);
if (result != REP_PROTOCOL_SUCCESS) {
switch (result) {
case REP_PROTOCOL_FAIL_DELETED:
case REP_PROTOCOL_FAIL_NO_RESOURCES:
return (result);
case REP_PROTOCOL_FAIL_INVALID_TYPE:
case REP_PROTOCOL_FAIL_TYPE_MISMATCH:
default:
bad_error("rc_node_find_named_child", result);
}
}
if (prop == NULL)
return (REP_PROTOCOL_FAIL_NOT_FOUND);
if (prop->rn_valtype != REP_PROTOCOL_TYPE_STRING) {
rc_node_rele(prop);
return (REP_PROTOCOL_SUCCESS);
}
(void) pthread_mutex_lock(&prop->rn_lock);
for (count = prop->rn_values_count, cp = prop->rn_values;
count > 0;
--count) {
result = perm_add_enabling_type(pcp, cp,
(pg->rn_id.rl_ids[ID_INSTANCE]) ? PC_AUTH_INST :
PC_AUTH_SVC);
if (result != REP_PROTOCOL_SUCCESS)
break;
cp = strchr(cp, '\0') + 1;
}
rc_node_rele_locked(prop);
return (result);
}
static int
perm_add_ent_prop_values(permcheck_t *pcp, rc_node_t *ent, const char *pgname,
const char *pgtype, const char *propname)
{
int r;
rc_node_t *pg;
assert(!MUTEX_HELD(&ent->rn_lock));
(void) pthread_mutex_lock(&ent->rn_lock);
r = rc_node_find_named_child(ent, pgname,
REP_PROTOCOL_ENTITY_PROPERTYGRP, &pg);
(void) pthread_mutex_unlock(&ent->rn_lock);
switch (r) {
case REP_PROTOCOL_SUCCESS:
break;
case REP_PROTOCOL_FAIL_DELETED:
case REP_PROTOCOL_FAIL_NO_RESOURCES:
return (r);
default:
bad_error("rc_node_find_named_child", r);
}
if (pg == NULL)
return (REP_PROTOCOL_FAIL_NOT_FOUND);
if (pgtype == NULL || strcmp(pg->rn_type, pgtype) == 0) {
r = perm_add_pg_prop_values(pcp, pg, propname);
switch (r) {
case REP_PROTOCOL_FAIL_DELETED:
r = REP_PROTOCOL_FAIL_NOT_FOUND;
break;
case REP_PROTOCOL_FAIL_NO_RESOURCES:
case REP_PROTOCOL_SUCCESS:
case REP_PROTOCOL_FAIL_NOT_FOUND:
break;
default:
bad_error("perm_add_pg_prop_values", r);
}
}
rc_node_rele(pg);
return (r);
}
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)
return (r);
assert(!MUTEX_HELD(&pg->rn_lock));
if (pg->rn_id.rl_ids[ID_INSTANCE] == 0)
return (REP_PROTOCOL_SUCCESS);
sz = strlcpy(pgname, pg->rn_name, sizeof (pgname));
assert(sz < sizeof (pgname));
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);
rc_node_rele(svc);
if (r == REP_PROTOCOL_FAIL_NOT_FOUND)
r = REP_PROTOCOL_SUCCESS;
return (r);
}
static int
perm_add_inst_action_auth(permcheck_t *pcp, rc_node_t *inst)
{
int r;
rc_node_t *svc;
assert(inst->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE);
r = perm_add_ent_prop_values(pcp, inst, AUTH_PG_GENERAL,
AUTH_PG_GENERAL_TYPE, AUTH_PROP_ACTION);
if (r != REP_PROTOCOL_FAIL_NOT_FOUND)
return (r);
r = rc_node_parent(inst, &svc);
if (r != REP_PROTOCOL_SUCCESS) {
assert(r == REP_PROTOCOL_FAIL_DELETED);
return (r);
}
r = perm_add_ent_prop_values(pcp, svc, AUTH_PG_GENERAL,
AUTH_PG_GENERAL_TYPE, AUTH_PROP_ACTION);
return (r == REP_PROTOCOL_FAIL_NOT_FOUND ? REP_PROTOCOL_SUCCESS : r);
}
#endif
void
rc_node_ptr_init(rc_node_ptr_t *out)
{
out->rnp_node = NULL;
out->rnp_auth_string = NULL;
out->rnp_authorized = RC_AUTH_UNKNOWN;
out->rnp_deleted = 0;
}
void
rc_node_ptr_free_mem(rc_node_ptr_t *npp)
{
if (npp->rnp_auth_string != NULL) {
free((void *)npp->rnp_auth_string);
npp->rnp_auth_string = NULL;
}
}
static void
rc_node_assign(rc_node_ptr_t *out, rc_node_t *val)
{
rc_node_t *cur = out->rnp_node;
if (val != NULL)
rc_node_hold(val);
out->rnp_node = val;
if (cur != NULL) {
NODE_LOCK(cur);
rc_node_hold_ephemeral_locked(cur);
rc_node_rele_locked(cur);
}
out->rnp_authorized = RC_AUTH_UNKNOWN;
rc_node_ptr_free_mem(out);
out->rnp_deleted = 0;
}
void
rc_node_clear(rc_node_ptr_t *out, int deleted)
{
rc_node_assign(out, NULL);
out->rnp_deleted = deleted;
}
void
rc_node_ptr_assign(rc_node_ptr_t *out, const rc_node_ptr_t *val)
{
rc_node_assign(out, val->rnp_node);
}
static int
rc_node_check_and_lock(rc_node_t *np)
{
int result = REP_PROTOCOL_SUCCESS;
if (np == NULL)
return (REP_PROTOCOL_FAIL_NOT_SET);
(void) pthread_mutex_lock(&np->rn_lock);
if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
result = REP_PROTOCOL_FAIL_DELETED;
(void) pthread_mutex_unlock(&np->rn_lock);
}
return (result);
}
static rc_node_t *
rc_node_ptr_check_and_lock(rc_node_ptr_t *npp, int *res)
{
rc_node_t *np = npp->rnp_node;
if (np == NULL) {
if (npp->rnp_deleted)
*res = REP_PROTOCOL_FAIL_DELETED;
else
*res = REP_PROTOCOL_FAIL_NOT_SET;
return (NULL);
}
(void) pthread_mutex_lock(&np->rn_lock);
if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_clear(npp, 1);
*res = REP_PROTOCOL_FAIL_DELETED;
return (NULL);
}
return (np);
}
#define RC_NODE_CHECK_AND_LOCK(n) { \
int rc__res; \
if ((rc__res = rc_node_check_and_lock(n)) != REP_PROTOCOL_SUCCESS) \
return (rc__res); \
}
#define RC_NODE_CHECK(n) { \
RC_NODE_CHECK_AND_LOCK(n); \
(void) pthread_mutex_unlock(&(n)->rn_lock); \
}
#define RC_NODE_CHECK_AND_HOLD(n) { \
RC_NODE_CHECK_AND_LOCK(n); \
rc_node_hold_locked(n); \
(void) pthread_mutex_unlock(&(n)->rn_lock); \
}
#define RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp) { \
int rc__res; \
if (((np) = rc_node_ptr_check_and_lock(npp, &rc__res)) == NULL) \
return (rc__res); \
}
#define RC_NODE_PTR_CHECK_LOCK_OR_FREE_RETURN(np, npp, mem) { \
int rc__res; \
if (((np) = rc_node_ptr_check_and_lock(npp, &rc__res)) == \
NULL) { \
if ((mem) != NULL) \
free((mem)); \
return (rc__res); \
} \
}
#define RC_NODE_PTR_GET_CHECK(np, npp) { \
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp); \
(void) pthread_mutex_unlock(&(np)->rn_lock); \
}
#define RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp) { \
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp); \
rc_node_hold_locked(np); \
(void) pthread_mutex_unlock(&(np)->rn_lock); \
}
#define HOLD_FLAG_OR_RETURN(np, flag) { \
assert(MUTEX_HELD(&(np)->rn_lock)); \
assert(!((np)->rn_flags & RC_NODE_DEAD)); \
if (!rc_node_hold_flag((np), flag)) { \
(void) pthread_mutex_unlock(&(np)->rn_lock); \
return (REP_PROTOCOL_FAIL_DELETED); \
} \
}
#define HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, flag, mem) { \
assert(MUTEX_HELD(&(np)->rn_lock)); \
if (!rc_node_hold_flag((np), flag)) { \
(void) pthread_mutex_unlock(&(np)->rn_lock); \
assert((np) == (npp)->rnp_node); \
rc_node_clear(npp, 1); \
if ((mem) != NULL) \
free((mem)); \
return (REP_PROTOCOL_FAIL_DELETED); \
} \
}
int
rc_local_scope(uint32_t type, rc_node_ptr_t *out)
{
if (type != REP_PROTOCOL_ENTITY_SCOPE) {
rc_node_clear(out, 0);
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
rc_node_assign(out, rc_scope);
return (REP_PROTOCOL_SUCCESS);
}
static int
rc_scope_parent_scope(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out)
{
rc_node_t *np;
rc_node_clear(out, 0);
RC_NODE_PTR_GET_CHECK(np, npp);
if (type != REP_PROTOCOL_ENTITY_SCOPE)
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
return (REP_PROTOCOL_FAIL_NOT_FOUND);
}
static int rc_node_pg_check_read_protect(rc_node_t *);
int
rc_node_name(rc_node_ptr_t *npp, char *buf, size_t sz, uint32_t answertype,
size_t *sz_out)
{
size_t actual;
rc_node_t *np;
assert(sz == *sz_out);
RC_NODE_PTR_GET_CHECK(np, npp);
if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
np = np->rn_cchain[0];
RC_NODE_CHECK(np);
}
switch (answertype) {
case RP_ENTITY_NAME_NAME:
if (np->rn_name == NULL)
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
actual = strlcpy(buf, np->rn_name, sz);
break;
case RP_ENTITY_NAME_PGTYPE:
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
actual = strlcpy(buf, np->rn_type, sz);
break;
case RP_ENTITY_NAME_PGFLAGS:
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
actual = snprintf(buf, sz, "%d", np->rn_pgflags);
break;
case RP_ENTITY_NAME_SNAPLEVEL_SCOPE:
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
actual = strlcpy(buf, np->rn_snaplevel->rsl_scope, sz);
break;
case RP_ENTITY_NAME_SNAPLEVEL_SERVICE:
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
actual = strlcpy(buf, np->rn_snaplevel->rsl_service, sz);
break;
case RP_ENTITY_NAME_SNAPLEVEL_INSTANCE:
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
if (np->rn_snaplevel->rsl_instance == NULL)
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);
}
if (actual >= sz)
return (REP_PROTOCOL_FAIL_TRUNCATED);
*sz_out = actual;
return (REP_PROTOCOL_SUCCESS);
}
int
rc_node_get_property_type(rc_node_ptr_t *npp, rep_protocol_value_type_t *out)
{
rc_node_t *np;
RC_NODE_PTR_GET_CHECK(np, npp);
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
*out = np->rn_valtype;
return (REP_PROTOCOL_SUCCESS);
}
static int
rc_node_parent(rc_node_t *np, rc_node_t **out)
{
rc_node_t *pnp;
rc_node_t *np_orig;
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
RC_NODE_CHECK_AND_LOCK(np);
} else {
np = np->rn_cchain[0];
RC_NODE_CHECK_AND_LOCK(np);
}
np_orig = np;
rc_node_hold_locked(np);
for (;;) {
if (!rc_node_wait_flag(np,
RC_NODE_IN_TX | RC_NODE_USING_PARENT)) {
rc_node_rele_locked(np);
return (REP_PROTOCOL_FAIL_DELETED);
}
if (!(np->rn_flags & RC_NODE_OLD))
break;
rc_node_rele_locked(np);
np = cache_lookup(&np_orig->rn_id);
assert(np != np_orig);
if (np == NULL)
goto deleted;
(void) pthread_mutex_lock(&np->rn_lock);
}
if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
(void) pthread_mutex_unlock(&np->rn_lock);
*out = NULL;
rc_node_rele(np);
return (REP_PROTOCOL_FAIL_DELETED);
}
assert(np->rn_parent != NULL);
pnp = np->rn_parent;
(void) pthread_mutex_unlock(&np->rn_lock);
(void) pthread_mutex_lock(&pnp->rn_lock);
(void) pthread_mutex_lock(&np->rn_lock);
rc_node_rele_flag(np, RC_NODE_USING_PARENT);
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_hold_locked(pnp);
(void) pthread_mutex_unlock(&pnp->rn_lock);
rc_node_rele(np);
*out = pnp;
return (REP_PROTOCOL_SUCCESS);
deleted:
rc_node_rele(np);
return (REP_PROTOCOL_FAIL_DELETED);
}
static int
rc_node_ptr_parent(rc_node_ptr_t *npp, rc_node_t **out)
{
rc_node_t *np;
RC_NODE_PTR_GET_CHECK(np, npp);
return (rc_node_parent(np, out));
}
int
rc_node_get_parent(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out)
{
rc_node_t *pnp;
int rc;
if (npp->rnp_node != NULL &&
npp->rnp_node->rn_id.rl_type == REP_PROTOCOL_ENTITY_SCOPE)
return (rc_scope_parent_scope(npp, type, out));
if ((rc = rc_node_ptr_parent(npp, &pnp)) != REP_PROTOCOL_SUCCESS) {
rc_node_clear(out, 0);
return (rc);
}
if (type != pnp->rn_id.rl_type) {
rc_node_rele(pnp);
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
rc_node_assign(out, pnp);
rc_node_rele(pnp);
return (REP_PROTOCOL_SUCCESS);
}
int
rc_node_parent_type(rc_node_ptr_t *npp, uint32_t *type_out)
{
rc_node_t *pnp;
int rc;
if (npp->rnp_node != NULL &&
npp->rnp_node->rn_id.rl_type == REP_PROTOCOL_ENTITY_SCOPE) {
*type_out = REP_PROTOCOL_ENTITY_SCOPE;
return (REP_PROTOCOL_SUCCESS);
}
if ((rc = rc_node_ptr_parent(npp, &pnp)) != REP_PROTOCOL_SUCCESS)
return (rc);
*type_out = pnp->rn_id.rl_type;
rc_node_rele(pnp);
return (REP_PROTOCOL_SUCCESS);
}
int
rc_node_get_child(rc_node_ptr_t *npp, const char *name, uint32_t type,
rc_node_ptr_t *outp)
{
rc_node_t *np, *cp;
rc_node_t *child = NULL;
int ret, idx;
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
if ((ret = rc_check_type_name(type, name)) == REP_PROTOCOL_SUCCESS) {
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
ret = rc_node_find_named_child(np, name, type, &child);
} else {
(void) pthread_mutex_unlock(&np->rn_lock);
ret = REP_PROTOCOL_SUCCESS;
for (idx = 0; idx < COMPOSITION_DEPTH; idx++) {
cp = np->rn_cchain[idx];
if (cp == NULL)
break;
RC_NODE_CHECK_AND_LOCK(cp);
ret = rc_node_find_named_child(cp, name, type,
&child);
(void) pthread_mutex_unlock(&cp->rn_lock);
if (ret != REP_PROTOCOL_SUCCESS ||
child != NULL)
break;
}
(void) pthread_mutex_lock(&np->rn_lock);
}
}
(void) pthread_mutex_unlock(&np->rn_lock);
if (ret == REP_PROTOCOL_SUCCESS) {
rc_node_assign(outp, child);
if (child != NULL)
rc_node_rele(child);
else
ret = REP_PROTOCOL_FAIL_NOT_FOUND;
} else {
rc_node_assign(outp, NULL);
}
return (ret);
}
int
rc_node_update(rc_node_ptr_t *npp)
{
cache_bucket_t *bp;
rc_node_t *np = npp->rnp_node;
rc_node_t *nnp;
rc_node_t *cpg = NULL;
if (np != NULL &&
np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
cpg = np;
np = np->rn_cchain[0];
}
RC_NODE_CHECK(np);
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP &&
np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT)
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
for (;;) {
bp = cache_hold(np->rn_hash);
nnp = cache_lookup_unlocked(bp, &np->rn_id);
if (nnp == NULL) {
cache_release(bp);
rc_node_clear(npp, 1);
return (REP_PROTOCOL_FAIL_DELETED);
}
(void) pthread_mutex_lock(&nnp->rn_lock);
cache_release(bp);
if (!(nnp->rn_flags & RC_NODE_IN_TX) ||
!rc_node_wait_flag(nnp, RC_NODE_IN_TX))
break;
rc_node_rele_locked(nnp);
}
if (nnp->rn_flags & RC_NODE_DEAD) {
(void) pthread_mutex_unlock(&nnp->rn_lock);
if (nnp != np && cpg == NULL)
rc_node_assign(npp, nnp);
rc_node_rele(nnp);
return (REP_PROTOCOL_FAIL_DELETED);
}
assert(!(nnp->rn_flags & RC_NODE_OLD));
(void) pthread_mutex_unlock(&nnp->rn_lock);
if (nnp != np && cpg == NULL)
rc_node_assign(npp, nnp);
rc_node_rele(nnp);
return ((nnp == np)? REP_PROTOCOL_SUCCESS : REP_PROTOCOL_DONE);
}
static perm_status_t
rc_node_modify_permission_check(char **match_auth)
{
permcheck_t *pcp;
perm_status_t granted = PERM_GRANTED;
int rc;
*match_auth = NULL;
#ifdef NATIVE_BUILD
if (!client_is_privileged()) {
granted = PERM_DENIED;
}
return (granted);
#else
if (is_main_repository == 0)
return (PERM_GRANTED);
pcp = pc_create();
if (pcp != NULL) {
rc = perm_add_enabling(pcp, AUTH_MODIFY);
if (rc == REP_PROTOCOL_SUCCESS) {
granted = perm_granted(pcp);
if ((granted == PERM_GRANTED) ||
(granted == PERM_DENIED)) {
*match_auth =
strdup(pcp->pc_auth_string);
if (*match_auth == NULL)
granted = PERM_FAIL;
}
} else {
granted = PERM_FAIL;
}
pc_free(pcp);
} else {
granted = PERM_FAIL;
}
return (granted);
#endif
}
#ifndef NATIVE_BUILD
static void
smf_annotation_event(int status, int return_val)
{
adt_session_data_t *session;
adt_event_data_t *event = NULL;
char file[MAXPATHLEN];
char operation[REP_PROTOCOL_NAME_LEN];
if (is_main_repository == 0)
return;
if (client_annotation_needed(operation, sizeof (operation), file,
sizeof (file)) == 0) {
return;
}
if (file[0] == 0) {
(void) strlcpy(file, "NO FILE", sizeof (file));
}
if (operation[0] == 0) {
(void) strlcpy(operation, "NO OPERATION",
sizeof (operation));
}
if ((session = get_audit_session()) == NULL)
return;
if ((event = adt_alloc_event(session, ADT_smf_annotation)) == NULL) {
uu_warn("smf_annotation_event cannot allocate event "
"data. %s\n", strerror(errno));
return;
}
event->adt_smf_annotation.operation = operation;
event->adt_smf_annotation.file = file;
if (adt_put_event(event, status, return_val) == 0) {
client_annotation_finished();
} else {
uu_warn("smf_annotation_event failed to put event. "
"%s\n", strerror(errno));
}
adt_free_event(event);
}
#endif
static void
smf_audit_event(au_event_t event_id, int status, int return_val,
audit_event_data_t *data)
{
#ifndef NATIVE_BUILD
char *auth_used;
char *fmri;
char *prop_value;
adt_session_data_t *session;
adt_event_data_t *event = NULL;
if (is_main_repository == 0)
return;
smf_annotation_event(status, return_val);
if ((session = get_audit_session()) == NULL)
return;
if ((event = adt_alloc_event(session, event_id)) == NULL) {
uu_warn("smf_audit_event cannot allocate event "
"data. %s\n", strerror(errno));
return;
}
if (data->ed_auth == NULL) {
auth_used = "PRIVILEGED";
} else {
auth_used = data->ed_auth;
}
if (data->ed_fmri == NULL) {
syslog(LOG_WARNING, "smf_audit_event called with "
"empty FMRI string");
fmri = "UNKNOWN FMRI";
} else {
fmri = data->ed_fmri;
}
if (data->ed_prop_value == NULL) {
prop_value = "";
} else {
prop_value = data->ed_prop_value;
}
switch (event_id) {
case ADT_smf_attach_snap:
event->adt_smf_attach_snap.auth_used = auth_used;
event->adt_smf_attach_snap.old_fmri = data->ed_old_fmri;
event->adt_smf_attach_snap.old_name = data->ed_old_name;
event->adt_smf_attach_snap.new_fmri = fmri;
event->adt_smf_attach_snap.new_name = data->ed_snapname;
break;
case ADT_smf_change_prop:
event->adt_smf_change_prop.auth_used = auth_used;
event->adt_smf_change_prop.fmri = fmri;
event->adt_smf_change_prop.type = data->ed_type;
event->adt_smf_change_prop.value = prop_value;
break;
case ADT_smf_clear:
event->adt_smf_clear.auth_used = auth_used;
event->adt_smf_clear.fmri = fmri;
break;
case ADT_smf_create:
event->adt_smf_create.fmri = fmri;
event->adt_smf_create.auth_used = auth_used;
break;
case ADT_smf_create_npg:
event->adt_smf_create_npg.auth_used = auth_used;
event->adt_smf_create_npg.fmri = fmri;
event->adt_smf_create_npg.type = data->ed_type;
break;
case ADT_smf_create_pg:
event->adt_smf_create_pg.auth_used = auth_used;
event->adt_smf_create_pg.fmri = fmri;
event->adt_smf_create_pg.type = data->ed_type;
break;
case ADT_smf_create_prop:
event->adt_smf_create_prop.auth_used = auth_used;
event->adt_smf_create_prop.fmri = fmri;
event->adt_smf_create_prop.type = data->ed_type;
event->adt_smf_create_prop.value = prop_value;
break;
case ADT_smf_create_snap:
event->adt_smf_create_snap.auth_used = auth_used;
event->adt_smf_create_snap.fmri = fmri;
event->adt_smf_create_snap.name = data->ed_snapname;
break;
case ADT_smf_degrade:
event->adt_smf_degrade.auth_used = auth_used;
event->adt_smf_degrade.fmri = fmri;
break;
case ADT_smf_delete:
event->adt_smf_delete.fmri = fmri;
event->adt_smf_delete.auth_used = auth_used;
break;
case ADT_smf_delete_npg:
event->adt_smf_delete_npg.auth_used = auth_used;
event->adt_smf_delete_npg.fmri = fmri;
event->adt_smf_delete_npg.type = data->ed_type;
break;
case ADT_smf_delete_pg:
event->adt_smf_delete_pg.auth_used = auth_used;
event->adt_smf_delete_pg.fmri = fmri;
event->adt_smf_delete_pg.type = data->ed_type;
break;
case ADT_smf_delete_prop:
event->adt_smf_delete_prop.auth_used = auth_used;
event->adt_smf_delete_prop.fmri = fmri;
break;
case ADT_smf_delete_snap:
event->adt_smf_delete_snap.auth_used = auth_used;
event->adt_smf_delete_snap.fmri = fmri;
event->adt_smf_delete_snap.name = data->ed_snapname;
break;
case ADT_smf_disable:
event->adt_smf_disable.auth_used = auth_used;
event->adt_smf_disable.fmri = fmri;
break;
case ADT_smf_enable:
event->adt_smf_enable.auth_used = auth_used;
event->adt_smf_enable.fmri = fmri;
break;
case ADT_smf_immediate_degrade:
event->adt_smf_immediate_degrade.auth_used = auth_used;
event->adt_smf_immediate_degrade.fmri = fmri;
break;
case ADT_smf_immediate_maintenance:
event->adt_smf_immediate_maintenance.auth_used = auth_used;
event->adt_smf_immediate_maintenance.fmri = fmri;
break;
case ADT_smf_immtmp_maintenance:
event->adt_smf_immtmp_maintenance.auth_used = auth_used;
event->adt_smf_immtmp_maintenance.fmri = fmri;
break;
case ADT_smf_maintenance:
event->adt_smf_maintenance.auth_used = auth_used;
event->adt_smf_maintenance.fmri = fmri;
break;
case ADT_smf_milestone:
event->adt_smf_milestone.auth_used = auth_used;
event->adt_smf_milestone.fmri = fmri;
break;
case ADT_smf_read_prop:
event->adt_smf_read_prop.auth_used = auth_used;
event->adt_smf_read_prop.fmri = fmri;
break;
case ADT_smf_refresh:
event->adt_smf_refresh.auth_used = auth_used;
event->adt_smf_refresh.fmri = fmri;
break;
case ADT_smf_restart:
event->adt_smf_restart.auth_used = auth_used;
event->adt_smf_restart.fmri = fmri;
break;
case ADT_smf_tmp_disable:
event->adt_smf_tmp_disable.auth_used = auth_used;
event->adt_smf_tmp_disable.fmri = fmri;
break;
case ADT_smf_tmp_enable:
event->adt_smf_tmp_enable.auth_used = auth_used;
event->adt_smf_tmp_enable.fmri = fmri;
break;
case ADT_smf_tmp_maintenance:
event->adt_smf_tmp_maintenance.auth_used = auth_used;
event->adt_smf_tmp_maintenance.fmri = fmri;
break;
default:
abort();
}
if (adt_put_event(event, status, return_val) != 0) {
uu_warn("smf_audit_event failed to put event. %s\n",
strerror(errno));
}
adt_free_event(event);
#endif
}
#ifndef NATIVE_BUILD
static void
special_property_event(audit_event_data_t *evdp, const char *prop_name,
char *pg_name, int status, int return_val, tx_commit_data_t *tx_data,
size_t cmd_no)
{
au_event_t event_id;
audit_special_prop_item_t search_key;
audit_special_prop_item_t *found;
search_key.api_prop_name = prop_name;
search_key.api_pg_name = pg_name;
found = (audit_special_prop_item_t *)bsearch(&search_key,
special_props_list, SPECIAL_PROP_COUNT,
sizeof (special_props_list[0]), special_prop_compare);
if (found == NULL) {
return;
}
if (found->api_event_func == NULL) {
event_id = found->api_event_id;
} else {
if ((*found->api_event_func)(tx_data, cmd_no,
found->api_pg_name, &event_id) < 0)
return;
}
smf_audit_event(event_id, status, return_val, evdp);
}
#endif
static char *
generate_value_list(tx_commit_data_t *tx_data, size_t cmd_no)
{
const char *cp;
const char *cur_value;
size_t byte_count = 0;
uint32_t i;
uint32_t nvalues;
size_t str_size = 0;
char *values = NULL;
char *vp;
if (tx_cmd_nvalues(tx_data, cmd_no, &nvalues) != REP_PROTOCOL_SUCCESS)
return (NULL);
for (i = 0; i < nvalues; i++) {
if (tx_cmd_value(tx_data, cmd_no, i, &cur_value) !=
REP_PROTOCOL_SUCCESS)
return (NULL);
for (cp = cur_value; *cp != 0; cp++) {
byte_count += (*cp == '"') ? 2 : 1;
}
byte_count += 3;
}
byte_count++;
values = malloc(byte_count);
if (values == NULL)
return (NULL);
*values = 0;
for (i = 0; i < nvalues; i++) {
if (tx_cmd_value(tx_data, cmd_no, i, &cur_value) !=
REP_PROTOCOL_SUCCESS) {
free(values);
return (NULL);
}
(void) strlcat(values, "\"", byte_count);
for (cp = cur_value, vp = values + strlen(values);
*cp != 0; cp++) {
if (*cp == '"') {
*vp++ = '\\';
*vp++ = '"';
} else {
*vp++ = *cp;
}
}
*vp = 0;
str_size = strlcat(values, "\" ", byte_count);
assert(str_size < byte_count);
}
if (str_size > 0)
values[str_size - 1] = 0;
return (values);
}
static void
generate_property_events(
tx_commit_data_t *tx_data,
char *pg_fmri,
char *auth_string,
int auth_status,
int auth_ret_value)
{
#ifndef NATIVE_BUILD
enum rep_protocol_transaction_action action;
audit_event_data_t audit_data;
size_t count;
size_t cmd_no;
char *cp;
au_event_t event_id;
char fmri[REP_PROTOCOL_FMRI_LEN];
char pg_name[REP_PROTOCOL_NAME_LEN];
char *pg_end;
const char *prop_name;
uint32_t ptype;
char prop_type[3];
enum rep_protocol_responseid rc;
size_t sz_out;
if (tx_data == NULL)
return;
if ((count = tx_cmd_count(tx_data)) == 0)
return;
pg_end = fmri;
pg_end += strlcpy(fmri, pg_fmri, sizeof (fmri));
cp = strstr(pg_fmri, SCF_FMRI_PROPERTYGRP_PREFIX);
if (cp == NULL) {
pg_name[0] = 0;
} else {
cp += strlen(SCF_FMRI_PROPERTYGRP_PREFIX);
(void) strlcpy(pg_name, cp, sizeof (pg_name));
}
audit_data.ed_auth = auth_string;
audit_data.ed_fmri = fmri;
audit_data.ed_type = prop_type;
prop_type[2] = 0;
for (cmd_no = 0; cmd_no < count; cmd_no++) {
*pg_end = 0;
if (tx_cmd_prop(tx_data, cmd_no, &prop_name) !=
REP_PROTOCOL_SUCCESS) {
continue;
}
rc = rc_concat_fmri_element(fmri, sizeof (fmri), &sz_out,
prop_name, REP_PROTOCOL_ENTITY_PROPERTY);
if (rc != REP_PROTOCOL_SUCCESS) {
continue;
}
special_property_event(&audit_data, prop_name, pg_name,
auth_status, auth_ret_value, tx_data, cmd_no);
if (tx_cmd_prop_type(tx_data, cmd_no, &ptype) !=
REP_PROTOCOL_SUCCESS) {
continue;
}
prop_type[0] = REP_PROTOCOL_BASE_TYPE(ptype);
prop_type[1] = REP_PROTOCOL_SUBTYPE(ptype);
audit_data.ed_prop_value = generate_value_list(tx_data, cmd_no);
if (tx_cmd_action(tx_data, cmd_no, &action) !=
REP_PROTOCOL_SUCCESS) {
free(audit_data.ed_prop_value);
continue;
}
switch (action) {
case REP_PROTOCOL_TX_ENTRY_NEW:
event_id = ADT_smf_create_prop;
break;
case REP_PROTOCOL_TX_ENTRY_CLEAR:
event_id = ADT_smf_change_prop;
break;
case REP_PROTOCOL_TX_ENTRY_REPLACE:
event_id = ADT_smf_change_prop;
break;
case REP_PROTOCOL_TX_ENTRY_DELETE:
event_id = ADT_smf_delete_prop;
break;
default:
assert(0);
free(audit_data.ed_prop_value);
continue;
}
smf_audit_event(event_id, auth_status, auth_ret_value,
&audit_data);
free(audit_data.ed_prop_value);
}
#endif
}
int
rc_node_create_child(rc_node_ptr_t *npp, uint32_t type, const char *name,
rc_node_ptr_t *cpp)
{
rc_node_t *np;
rc_node_t *cp = NULL;
int rc;
perm_status_t perm_rc;
size_t sz_out;
char fmri[REP_PROTOCOL_FMRI_LEN];
audit_event_data_t audit_data;
rc_node_clear(cpp, 0);
perm_rc = rc_node_modify_permission_check(&audit_data.ed_auth);
switch (perm_rc) {
case PERM_DENIED:
break;
case PERM_GRANTED:
break;
case PERM_GONE:
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
case PERM_FAIL:
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
default:
bad_error(rc_node_modify_permission_check, perm_rc);
}
RC_NODE_PTR_CHECK_LOCK_OR_FREE_RETURN(np, npp, audit_data.ed_auth);
audit_data.ed_fmri = fmri;
if (type == REP_PROTOCOL_ENTITY_PROPERTYGRP) {
(void) pthread_mutex_unlock(&np->rn_lock);
free(audit_data.ed_auth);
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
}
if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
(void) pthread_mutex_unlock(&np->rn_lock);
np = np->rn_cchain[0];
if ((rc = rc_node_check_and_lock(np)) != REP_PROTOCOL_SUCCESS) {
free(audit_data.ed_auth);
return (rc);
}
}
if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
free(audit_data.ed_auth);
return (rc);
}
if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
free(audit_data.ed_auth);
return (rc);
}
if ((rc = rc_get_fmri_and_concat(np, fmri, sizeof (fmri), &sz_out,
name, type)) != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
free(audit_data.ed_auth);
return (rc);
}
if (perm_rc == PERM_DENIED) {
(void) pthread_mutex_unlock(&np->rn_lock);
smf_audit_event(ADT_smf_create, ADT_FAILURE,
ADT_FAIL_VALUE_AUTH, &audit_data);
free(audit_data.ed_auth);
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
}
HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
audit_data.ed_auth);
(void) pthread_mutex_unlock(&np->rn_lock);
rc = object_create(np, type, name, &cp);
assert(rc != REP_PROTOCOL_FAIL_NOT_APPLICABLE);
if (rc == REP_PROTOCOL_SUCCESS) {
rc_node_assign(cpp, cp);
rc_node_rele(cp);
}
(void) pthread_mutex_lock(&np->rn_lock);
rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
(void) pthread_mutex_unlock(&np->rn_lock);
if (rc == REP_PROTOCOL_SUCCESS) {
smf_audit_event(ADT_smf_create, ADT_SUCCESS, ADT_SUCCESS,
&audit_data);
}
free(audit_data.ed_auth);
return (rc);
}
int
rc_node_create_child_pg(rc_node_ptr_t *npp, uint32_t type, const char *name,
const char *pgtype, uint32_t flags, rc_node_ptr_t *cpp)
{
rc_node_t *np;
rc_node_t *cp;
int rc;
permcheck_t *pcp;
perm_status_t granted;
char fmri[REP_PROTOCOL_FMRI_LEN];
audit_event_data_t audit_data;
au_event_t event_id;
size_t sz_out;
audit_data.ed_auth = NULL;
audit_data.ed_fmri = fmri;
audit_data.ed_type = (char *)pgtype;
rc_node_clear(cpp, 0);
if (flags & ~SCF_PG_FLAG_NONPERSISTENT)
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
rc_node_rele(np);
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
}
if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
REP_PROTOCOL_SUCCESS) {
rc_node_rele(np);
return (rc);
}
if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS ||
(rc = rc_check_pgtype_name(pgtype)) != REP_PROTOCOL_SUCCESS) {
rc_node_rele(np);
return (rc);
}
#ifdef NATIVE_BUILD
if (!client_is_privileged()) {
rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
}
#else
if (flags & SCF_PG_FLAG_NONPERSISTENT) {
event_id = ADT_smf_create_npg;
} else {
event_id = ADT_smf_create_pg;
}
if ((rc = rc_get_fmri_and_concat(np, fmri, sizeof (fmri), &sz_out,
name, REP_PROTOCOL_ENTITY_PROPERTYGRP)) != REP_PROTOCOL_SUCCESS) {
rc_node_rele(np);
return (rc);
}
if (is_main_repository) {
pcp = pc_create();
if (pcp != NULL) {
rc = perm_add_enabling(pcp, AUTH_MODIFY);
if (rc == REP_PROTOCOL_SUCCESS) {
const char * const auth =
perm_auth_for_pgtype(pgtype);
if (auth != NULL)
rc = perm_add_enabling(pcp, auth);
}
if (rc == REP_PROTOCOL_SUCCESS &&
(flags & SCF_PG_FLAG_NONPERSISTENT) != 0 &&
np->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE &&
((strcmp(name, AUTH_PG_ACTIONS) == 0 &&
strcmp(pgtype, AUTH_PG_ACTIONS_TYPE) == 0) ||
(strcmp(name, AUTH_PG_GENERAL_OVR) == 0 &&
strcmp(pgtype, AUTH_PG_GENERAL_OVR_TYPE) == 0))) {
rc = perm_add_enabling(pcp, AUTH_MANAGE);
if (rc == REP_PROTOCOL_SUCCESS)
rc = perm_add_inst_action_auth(pcp, np);
}
if (rc == REP_PROTOCOL_SUCCESS) {
granted = perm_granted(pcp);
rc = map_granted_status(granted, pcp,
&audit_data.ed_auth);
if (granted == PERM_GONE) {
pc_free(pcp);
rc_node_rele(np);
return (rc);
}
}
pc_free(pcp);
} else {
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
}
} else {
rc = REP_PROTOCOL_SUCCESS;
}
#endif
if (rc != REP_PROTOCOL_SUCCESS) {
rc_node_rele(np);
if (rc != REP_PROTOCOL_FAIL_NO_RESOURCES) {
smf_audit_event(event_id, ADT_FAILURE,
ADT_FAIL_VALUE_AUTH, &audit_data);
}
if (audit_data.ed_auth != NULL)
free(audit_data.ed_auth);
return (rc);
}
(void) pthread_mutex_lock(&np->rn_lock);
HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
audit_data.ed_auth);
(void) pthread_mutex_unlock(&np->rn_lock);
rc = object_create_pg(np, type, name, pgtype, flags, &cp);
if (rc == REP_PROTOCOL_SUCCESS) {
rc_node_assign(cpp, cp);
rc_node_rele(cp);
}
(void) pthread_mutex_lock(&np->rn_lock);
rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
(void) pthread_mutex_unlock(&np->rn_lock);
if (rc == REP_PROTOCOL_SUCCESS) {
smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS,
&audit_data);
}
if (audit_data.ed_auth != NULL)
free(audit_data.ed_auth);
return (rc);
}
static void
rc_pg_notify_fire(rc_node_pg_notify_t *pnp)
{
assert(MUTEX_HELD(&rc_pg_notify_lock));
if (pnp->rnpn_pg != NULL) {
uu_list_remove(pnp->rnpn_pg->rn_pg_notify_list, pnp);
(void) close(pnp->rnpn_fd);
pnp->rnpn_pg = NULL;
pnp->rnpn_fd = -1;
} else {
assert(pnp->rnpn_fd == -1);
}
}
static void
rc_notify_node_delete(rc_notify_delete_t *ndp, rc_node_t *np_arg)
{
rc_node_t *svc = NULL;
rc_node_t *inst = NULL;
rc_node_t *pg = NULL;
rc_node_t *np = np_arg;
rc_node_t *nnp;
while (svc == NULL) {
(void) pthread_mutex_lock(&np->rn_lock);
if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
(void) pthread_mutex_unlock(&np->rn_lock);
goto cleanup;
}
nnp = np->rn_parent;
rc_node_hold_locked(np);
switch (np->rn_id.rl_type) {
case REP_PROTOCOL_ENTITY_PROPERTYGRP:
assert(pg == NULL);
pg = np;
break;
case REP_PROTOCOL_ENTITY_INSTANCE:
assert(inst == NULL);
inst = np;
break;
case REP_PROTOCOL_ENTITY_SERVICE:
assert(svc == NULL);
svc = np;
break;
default:
rc_node_rele_flag(np, RC_NODE_USING_PARENT);
rc_node_rele_locked(np);
goto cleanup;
}
(void) pthread_mutex_unlock(&np->rn_lock);
np = nnp;
if (np == NULL)
goto cleanup;
}
rc_notify_deletion(ndp,
svc->rn_name,
inst != NULL ? inst->rn_name : NULL,
pg != NULL ? pg->rn_name : NULL);
ndp = NULL;
cleanup:
if (ndp != NULL)
uu_free(ndp);
for (;;) {
if (svc != NULL) {
np = svc;
svc = NULL;
} else if (inst != NULL) {
np = inst;
inst = NULL;
} else if (pg != NULL) {
np = pg;
pg = NULL;
} else
break;
(void) pthread_mutex_lock(&np->rn_lock);
rc_node_rele_flag(np, RC_NODE_USING_PARENT);
rc_node_rele_locked(np);
}
}
static void
rc_node_delete_hold(rc_node_t *np, int andformer)
{
rc_node_t *cp;
again:
assert(MUTEX_HELD(&np->rn_lock));
assert((np->rn_flags & RC_NODE_DYING_FLAGS) == RC_NODE_DYING_FLAGS);
for (cp = uu_list_first(np->rn_children); cp != NULL;
cp = uu_list_next(np->rn_children, cp)) {
(void) pthread_mutex_lock(&cp->rn_lock);
(void) pthread_mutex_unlock(&np->rn_lock);
if (!rc_node_hold_flag(cp, RC_NODE_DYING_FLAGS)) {
abort();
}
rc_node_delete_hold(cp, andformer);
(void) pthread_mutex_lock(&np->rn_lock);
}
if (andformer && (cp = np->rn_former) != NULL) {
(void) pthread_mutex_lock(&cp->rn_lock);
(void) pthread_mutex_unlock(&np->rn_lock);
if (!rc_node_hold_flag(cp, RC_NODE_DYING_FLAGS))
abort();
np = cp;
goto again;
}
(void) pthread_mutex_unlock(&np->rn_lock);
}
static void
rc_node_delete_rele(rc_node_t *np, int andformer)
{
rc_node_t *cp;
again:
assert(MUTEX_HELD(&np->rn_lock));
assert((np->rn_flags & RC_NODE_DYING_FLAGS) == RC_NODE_DYING_FLAGS);
for (cp = uu_list_first(np->rn_children); cp != NULL;
cp = uu_list_next(np->rn_children, cp)) {
(void) pthread_mutex_lock(&cp->rn_lock);
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_delete_rele(cp, andformer);
(void) pthread_mutex_lock(&np->rn_lock);
}
if (andformer && (cp = np->rn_former) != NULL) {
(void) pthread_mutex_lock(&cp->rn_lock);
rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
(void) pthread_mutex_unlock(&np->rn_lock);
np = cp;
goto again;
}
rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
(void) pthread_mutex_unlock(&np->rn_lock);
}
static void
rc_node_finish_delete(rc_node_t *cp)
{
cache_bucket_t *bp;
rc_node_pg_notify_t *pnp;
assert(MUTEX_HELD(&cp->rn_lock));
if (!(cp->rn_flags & RC_NODE_OLD)) {
assert(cp->rn_flags & RC_NODE_IN_PARENT);
if (!rc_node_wait_flag(cp, RC_NODE_USING_PARENT)) {
abort();
}
cp->rn_flags &= ~RC_NODE_IN_PARENT;
cp->rn_parent = NULL;
rc_node_free_fmri(cp);
}
cp->rn_flags |= RC_NODE_DEAD;
if (!(cp->rn_flags & RC_NODE_OLD)) {
assert(cp->rn_refs > 0);
(void) pthread_mutex_unlock(&cp->rn_lock);
(void) pthread_mutex_lock(&rc_pg_notify_lock);
while ((pnp = uu_list_first(cp->rn_pg_notify_list)) != NULL)
rc_pg_notify_fire(pnp);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
rc_notify_remove_node(cp);
bp = cache_hold(cp->rn_hash);
(void) pthread_mutex_lock(&cp->rn_lock);
cache_remove_unlocked(bp, cp);
cache_release(bp);
}
}
static void
rc_node_delete_children(rc_node_t *np, int andformer)
{
rc_node_t *cp;
again:
assert(np->rn_refs > 0);
assert(MUTEX_HELD(&np->rn_lock));
assert(np->rn_flags & RC_NODE_DEAD);
while ((cp = uu_list_first(np->rn_children)) != NULL) {
uu_list_remove(np->rn_children, cp);
(void) pthread_mutex_lock(&cp->rn_lock);
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_hold_locked(cp);
rc_node_finish_delete(cp);
rc_node_delete_children(cp, andformer);
(void) pthread_mutex_lock(&np->rn_lock);
}
rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
if (andformer && (cp = np->rn_former) != NULL) {
np->rn_former = NULL;
(void) pthread_mutex_lock(&cp->rn_lock);
rc_node_hold_ephemeral_locked(cp);
(void) pthread_mutex_unlock(&np->rn_lock);
cp->rn_flags &= ~RC_NODE_ON_FORMER;
rc_node_hold_locked(cp);
rc_node_finish_delete(cp);
rc_node_rele(np);
np = cp;
goto again;
}
rc_node_rele_locked(np);
}
static void
rc_node_no_client_refs(rc_node_t *np)
{
int unrefed;
rc_node_t *current, *cur;
assert(MUTEX_HELD(&np->rn_lock));
assert(np->rn_refs == 0);
assert(np->rn_other_refs == 0);
assert(np->rn_other_refs_held == 0);
if (np->rn_flags & RC_NODE_DEAD) {
if (np->rn_erefs > 1) {
--np->rn_erefs;
NODE_UNLOCK(np);
return;
}
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_destroy(np);
return;
}
assert(np->rn_flags & RC_NODE_OLD);
if (np->rn_flags & RC_NODE_UNREFED) {
(void) pthread_mutex_unlock(&np->rn_lock);
return;
}
np->rn_flags |= RC_NODE_UNREFED;
(void) pthread_mutex_unlock(&np->rn_lock);
for (;;) {
current = cache_lookup(&np->rn_id);
if (current == NULL) {
(void) pthread_mutex_lock(&np->rn_lock);
if (np->rn_flags & RC_NODE_DEAD)
goto died;
np->rn_flags &= ~RC_NODE_UNREFED;
(void) pthread_mutex_unlock(&np->rn_lock);
return;
}
if (current == np) {
(void) pthread_mutex_lock(&np->rn_lock);
np->rn_flags &= ~RC_NODE_UNREFED;
rc_node_rele_locked(np);
return;
}
(void) pthread_mutex_lock(¤t->rn_lock);
if (current->rn_flags & RC_NODE_OLD) {
rc_node_rele_locked(current);
continue;
}
if (!rc_node_hold_flag(current, RC_NODE_IN_TX)) {
rc_node_rele_locked(current);
continue;
}
if (!(current->rn_flags & RC_NODE_OLD)) {
(void) pthread_mutex_unlock(¤t->rn_lock);
break;
}
rc_node_rele_flag(current, RC_NODE_IN_TX);
rc_node_rele_locked(current);
}
(void) pthread_mutex_lock(&np->rn_lock);
if (!(np->rn_flags & (RC_NODE_OLD | RC_NODE_DEAD)) ||
np->rn_refs != 0 || np->rn_other_refs != 0 ||
np->rn_other_refs_held != 0) {
np->rn_flags &= ~RC_NODE_UNREFED;
(void) pthread_mutex_lock(¤t->rn_lock);
rc_node_rele_flag(current, RC_NODE_IN_TX);
rc_node_rele_locked(current);
return;
}
if (!rc_node_hold_flag(np, RC_NODE_DYING_FLAGS)) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_rele_flag(current, RC_NODE_IN_TX);
rc_node_rele_locked(current);
(void) pthread_mutex_lock(&np->rn_lock);
goto died;
}
rc_node_delete_hold(np, 0);
(void) pthread_mutex_lock(&np->rn_lock);
if (!(np->rn_flags & RC_NODE_OLD) ||
np->rn_refs != 0 || np->rn_other_refs != 0 ||
np->rn_other_refs_held != 0) {
np->rn_flags &= ~RC_NODE_UNREFED;
rc_node_delete_rele(np, 0);
(void) pthread_mutex_lock(¤t->rn_lock);
rc_node_rele_flag(current, RC_NODE_IN_TX);
rc_node_rele_locked(current);
return;
}
np->rn_flags |= RC_NODE_DEAD;
rc_node_hold_locked(np);
rc_node_delete_children(np, 0);
(void) pthread_mutex_lock(¤t->rn_lock);
for (cur = current; cur != NULL && cur->rn_former != np;
cur = cur->rn_former)
;
assert(cur != NULL && cur != np);
cur->rn_former = np->rn_former;
np->rn_former = NULL;
rc_node_rele_flag(current, RC_NODE_IN_TX);
rc_node_rele_locked(current);
(void) pthread_mutex_lock(&np->rn_lock);
assert(np->rn_flags & RC_NODE_ON_FORMER);
np->rn_flags &= ~(RC_NODE_UNREFED | RC_NODE_ON_FORMER);
if (np->rn_erefs > 1) {
--np->rn_erefs;
NODE_UNLOCK(np);
return;
}
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_destroy(np);
return;
died:
np->rn_flags &= ~RC_NODE_UNREFED;
unrefed = (np->rn_refs == 0 && np->rn_other_refs == 0 &&
np->rn_other_refs_held == 0);
if (np->rn_erefs > 0)
--np->rn_erefs;
if (unrefed && np->rn_erefs > 0) {
NODE_UNLOCK(np);
return;
}
(void) pthread_mutex_unlock(&np->rn_lock);
if (unrefed)
rc_node_destroy(np);
}
static au_event_t
get_delete_event_id(rep_protocol_entity_t entity, uint32_t pgflags)
{
au_event_t id = 0;
#ifndef NATIVE_BUILD
switch (entity) {
case REP_PROTOCOL_ENTITY_SERVICE:
case REP_PROTOCOL_ENTITY_INSTANCE:
id = ADT_smf_delete;
break;
case REP_PROTOCOL_ENTITY_SNAPSHOT:
id = ADT_smf_delete_snap;
break;
case REP_PROTOCOL_ENTITY_PROPERTYGRP:
case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
if (pgflags & SCF_PG_FLAG_NONPERSISTENT) {
id = ADT_smf_delete_npg;
} else {
id = ADT_smf_delete_pg;
}
break;
default:
abort();
}
#endif
return (id);
}
int
rc_node_delete(rc_node_ptr_t *npp)
{
rc_node_t *np, *np_orig;
rc_node_t *pp = NULL;
int rc;
rc_node_pg_notify_t *pnp;
cache_bucket_t *bp;
rc_notify_delete_t *ndp;
permcheck_t *pcp;
int granted;
au_event_t event_id = 0;
size_t sz_out;
audit_event_data_t audit_data;
int audit_failure = 0;
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
audit_data.ed_fmri = NULL;
audit_data.ed_auth = NULL;
audit_data.ed_snapname = NULL;
audit_data.ed_type = NULL;
switch (np->rn_id.rl_type) {
case REP_PROTOCOL_ENTITY_SERVICE:
event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_SERVICE,
np->rn_pgflags);
break;
case REP_PROTOCOL_ENTITY_INSTANCE:
event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_INSTANCE,
np->rn_pgflags);
break;
case REP_PROTOCOL_ENTITY_SNAPSHOT:
event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_SNAPSHOT,
np->rn_pgflags);
audit_data.ed_snapname = strdup(np->rn_name);
if (audit_data.ed_snapname == NULL) {
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
break;
case REP_PROTOCOL_ENTITY_SCOPE:
case REP_PROTOCOL_ENTITY_SNAPLEVEL:
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
(void) pthread_mutex_unlock(&np->rn_lock);
np = np->rn_cchain[0];
RC_NODE_CHECK_AND_LOCK(np);
event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_CPROPERTYGRP,
np->rn_pgflags);
break;
case REP_PROTOCOL_ENTITY_PROPERTYGRP:
if (np->rn_id.rl_ids[ID_SNAPSHOT] == 0) {
event_id =
get_delete_event_id(REP_PROTOCOL_ENTITY_PROPERTYGRP,
np->rn_pgflags);
audit_data.ed_type = strdup(np->rn_type);
if (audit_data.ed_type == NULL) {
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
break;
}
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
case REP_PROTOCOL_ENTITY_PROPERTY:
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
default:
assert(0);
abort();
break;
}
audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
if (audit_data.ed_fmri == NULL) {
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
goto cleanout;
}
np_orig = np;
rc_node_hold_locked(np);
for (;;) {
if (!rc_node_wait_flag(np,
RC_NODE_IN_TX | RC_NODE_USING_PARENT)) {
rc_node_rele_locked(np);
rc = REP_PROTOCOL_FAIL_DELETED;
goto cleanout;
}
if (np->rn_flags & RC_NODE_OLD) {
rc_node_rele_locked(np);
np = cache_lookup(&np_orig->rn_id);
assert(np != np_orig);
if (np == NULL) {
rc = REP_PROTOCOL_FAIL_DELETED;
goto fail;
}
(void) pthread_mutex_lock(&np->rn_lock);
continue;
}
if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
rc_node_rele_locked(np);
rc_node_clear(npp, 1);
rc = REP_PROTOCOL_FAIL_DELETED;
}
pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
if (pp == NULL) {
rc_node_rele(np);
rc_node_clear(npp, 1);
rc = REP_PROTOCOL_FAIL_DELETED;
goto cleanout;
}
rc_node_hold_locked(pp);
(void) pthread_mutex_unlock(&pp->rn_lock);
(void) pthread_mutex_lock(&np->rn_lock);
if (!(np->rn_flags & RC_NODE_OLD))
break;
(void) pthread_mutex_unlock(&np->rn_lock);
(void) pthread_mutex_lock(&pp->rn_lock);
rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
rc_node_rele_locked(pp);
(void) pthread_mutex_lock(&np->rn_lock);
continue;
}
if (!rc_node_hold_flag(np, RC_NODE_DYING_FLAGS)) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc = REP_PROTOCOL_FAIL_DELETED;
goto fail;
}
assert(!(np->rn_flags & RC_NODE_OLD));
if ((rc = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
(void) pthread_mutex_unlock(&np->rn_lock);
goto fail;
}
#ifdef NATIVE_BUILD
if (!client_is_privileged()) {
rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
}
#else
if (is_main_repository) {
(void) pthread_mutex_unlock(&np->rn_lock);
pcp = pc_create();
if (pcp != NULL) {
rc = perm_add_enabling(pcp, AUTH_MODIFY);
if (rc == REP_PROTOCOL_SUCCESS && np->rn_id.rl_type ==
REP_PROTOCOL_ENTITY_PROPERTYGRP) {
const char * const auth =
perm_auth_for_pgtype(np->rn_type);
if (auth != NULL)
rc = perm_add_enabling(pcp, auth);
}
if (rc == REP_PROTOCOL_SUCCESS) {
granted = perm_granted(pcp);
rc = map_granted_status(granted, pcp,
&audit_data.ed_auth);
if (granted == PERM_GONE) {
pc_free(pcp);
rc_node_rele_flag(np,
RC_NODE_DYING_FLAGS);
return (rc);
}
if (granted == PERM_DENIED)
audit_failure = 1;
}
pc_free(pcp);
} else {
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
}
(void) pthread_mutex_lock(&np->rn_lock);
} else {
rc = REP_PROTOCOL_SUCCESS;
}
#endif
if (rc != REP_PROTOCOL_SUCCESS) {
rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
(void) pthread_mutex_unlock(&np->rn_lock);
goto fail;
}
ndp = uu_zalloc(sizeof (*ndp));
if (ndp == NULL) {
rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
(void) pthread_mutex_unlock(&np->rn_lock);
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
goto fail;
}
rc_node_delete_hold(np, 1);
rc = object_delete(np);
if (rc != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_lock(&np->rn_lock);
rc_node_delete_rele(np, 1);
uu_free(ndp);
goto fail;
}
rc_notify_node_delete(ndp, np);
bp = cache_hold(np->rn_hash);
(void) pthread_mutex_lock(&np->rn_lock);
cache_remove_unlocked(bp, np);
cache_release(bp);
np->rn_flags |= RC_NODE_DEAD;
if (pp != NULL) {
(void) pthread_mutex_unlock(&np->rn_lock);
(void) pthread_mutex_lock(&pp->rn_lock);
(void) pthread_mutex_lock(&np->rn_lock);
uu_list_remove(pp->rn_children, np);
rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
(void) pthread_mutex_unlock(&pp->rn_lock);
np->rn_flags &= ~RC_NODE_IN_PARENT;
}
rc_node_hold_locked(np);
rc_node_delete_children(np, 1);
rc_node_clear(npp, 1);
(void) pthread_mutex_lock(&rc_pg_notify_lock);
while ((pnp = uu_list_first(np->rn_pg_notify_list)) != NULL)
rc_pg_notify_fire(pnp);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
rc_notify_remove_node(np);
rc_node_rele(np);
smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS,
&audit_data);
free(audit_data.ed_auth);
free(audit_data.ed_snapname);
free(audit_data.ed_type);
free(audit_data.ed_fmri);
return (rc);
fail:
rc_node_rele(np);
if (rc == REP_PROTOCOL_FAIL_DELETED)
rc_node_clear(npp, 1);
if (pp != NULL) {
(void) pthread_mutex_lock(&pp->rn_lock);
rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
rc_node_rele_locked(pp);
}
if (audit_failure) {
smf_audit_event(event_id, ADT_FAILURE,
ADT_FAIL_VALUE_AUTH, &audit_data);
}
cleanout:
free(audit_data.ed_auth);
free(audit_data.ed_snapname);
free(audit_data.ed_type);
free(audit_data.ed_fmri);
return (rc);
}
int
rc_node_next_snaplevel(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
{
rc_node_t *np;
rc_node_t *cp, *pp;
int res;
rc_node_clear(cpp, 0);
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT &&
np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL) {
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
}
if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_SNAPSHOT) {
if ((res = rc_node_fill_children(np,
REP_PROTOCOL_ENTITY_SNAPLEVEL)) != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
return (res);
}
for (cp = uu_list_first(np->rn_children);
cp != NULL;
cp = uu_list_next(np->rn_children, cp)) {
if (cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
continue;
rc_node_hold(cp);
break;
}
(void) pthread_mutex_unlock(&np->rn_lock);
} else {
if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_clear(npp, 1);
return (REP_PROTOCOL_FAIL_DELETED);
}
pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
if (pp == NULL) {
rc_node_clear(npp, 1);
return (REP_PROTOCOL_FAIL_DELETED);
}
cp = np;
while ((cp = uu_list_next(pp->rn_children, cp)) != NULL &&
cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
;
assert((cp == NULL && np->rn_snaplevel->rsl_next == NULL) ||
(cp != NULL && np->rn_snaplevel->rsl_next ==
cp->rn_snaplevel));
if (cp != NULL)
rc_node_hold(cp);
rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
(void) pthread_mutex_unlock(&pp->rn_lock);
}
rc_node_assign(cpp, cp);
if (cp != NULL) {
rc_node_rele(cp);
return (REP_PROTOCOL_SUCCESS);
}
return (REP_PROTOCOL_FAIL_NOT_FOUND);
}
static int
rc_attach_snapshot(
rc_node_t *np,
uint32_t snapid,
rc_node_t *parentp,
char *old_fmri,
char *old_name)
{
rc_node_t *np_orig;
rc_node_t *nnp, *prev;
rc_node_t *pp;
int rc;
size_t sz_out;
perm_status_t granted;
au_event_t event_id;
audit_event_data_t audit_data;
if (parentp == NULL) {
assert(old_fmri != NULL);
} else {
assert(snapid == 0);
}
assert(MUTEX_HELD(&np->rn_lock));
#ifndef NATIVE_BUILD
if (parentp == NULL) {
event_id = ADT_smf_attach_snap;
} else {
event_id = ADT_smf_create_snap;
}
#endif
audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
audit_data.ed_snapname = malloc(REP_PROTOCOL_NAME_LEN);
if ((audit_data.ed_fmri == NULL) || (audit_data.ed_snapname == NULL)) {
(void) pthread_mutex_unlock(&np->rn_lock);
free(audit_data.ed_fmri);
free(audit_data.ed_snapname);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
audit_data.ed_auth = NULL;
if (strlcpy(audit_data.ed_snapname, np->rn_name,
REP_PROTOCOL_NAME_LEN) >= REP_PROTOCOL_NAME_LEN) {
abort();
}
audit_data.ed_old_fmri = old_fmri;
audit_data.ed_old_name = old_name ? old_name : "NO NAME";
if (parentp == NULL) {
if ((rc = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
free(audit_data.ed_fmri);
free(audit_data.ed_snapname);
return (rc);
}
} else {
if ((rc = rc_node_get_fmri_or_fragment(parentp,
audit_data.ed_fmri, REP_PROTOCOL_FMRI_LEN, &sz_out)) !=
REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
free(audit_data.ed_fmri);
free(audit_data.ed_snapname);
return (rc);
}
}
np_orig = np;
rc_node_hold_locked(np);
(void) pthread_mutex_unlock(&np->rn_lock);
granted = rc_node_modify_permission_check(&audit_data.ed_auth);
switch (granted) {
case PERM_DENIED:
smf_audit_event(event_id, ADT_FAILURE, ADT_FAIL_VALUE_AUTH,
&audit_data);
rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
rc_node_rele(np);
goto cleanout;
case PERM_GRANTED:
break;
case PERM_GONE:
rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
rc_node_rele(np);
goto cleanout;
case PERM_FAIL:
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
rc_node_rele(np);
goto cleanout;
default:
bad_error(rc_node_modify_permission_check, granted);
}
(void) pthread_mutex_lock(&np->rn_lock);
for (;;) {
if (!(np->rn_flags & RC_NODE_OLD)) {
if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
goto again;
}
pp = rc_node_hold_parent_flag(np,
RC_NODE_CHILDREN_CHANGING);
(void) pthread_mutex_lock(&np->rn_lock);
if (pp == NULL) {
goto again;
}
if (np->rn_flags & RC_NODE_OLD) {
rc_node_rele_flag(pp,
RC_NODE_CHILDREN_CHANGING);
(void) pthread_mutex_unlock(&pp->rn_lock);
goto again;
}
(void) pthread_mutex_unlock(&pp->rn_lock);
if (!rc_node_hold_flag(np, RC_NODE_IN_TX)) {
abort();
}
break;
}
again:
rc_node_rele_locked(np);
np = cache_lookup(&np_orig->rn_id);
if (np == NULL) {
rc = REP_PROTOCOL_FAIL_DELETED;
goto cleanout;
}
(void) pthread_mutex_lock(&np->rn_lock);
}
if (parentp != NULL) {
if (pp != parentp) {
rc = REP_PROTOCOL_FAIL_BAD_REQUEST;
goto fail;
}
nnp = NULL;
} else {
if (np->rn_snapshot_id == snapid) {
rc_node_rele_flag(np, RC_NODE_IN_TX);
rc_node_rele_locked(np);
(void) pthread_mutex_lock(&pp->rn_lock);
rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
(void) pthread_mutex_unlock(&pp->rn_lock);
rc = REP_PROTOCOL_SUCCESS;
goto cleanout;
}
prev = np;
while ((nnp = prev->rn_former) != NULL) {
if (nnp->rn_snapshot_id == snapid) {
rc_node_hold(nnp);
break;
}
prev = nnp;
}
}
if (nnp == NULL) {
prev = NULL;
nnp = rc_node_alloc();
if (nnp == NULL) {
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
goto fail;
}
nnp->rn_id = np->rn_id;
nnp->rn_hash = np->rn_hash;
nnp->rn_name = strdup(np->rn_name);
nnp->rn_snapshot_id = snapid;
nnp->rn_flags = RC_NODE_IN_TX | RC_NODE_USING_PARENT;
if (nnp->rn_name == NULL) {
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
goto fail;
}
}
(void) pthread_mutex_unlock(&np->rn_lock);
rc = object_snapshot_attach(&np->rn_id, &snapid, (parentp != NULL));
if (parentp != NULL)
nnp->rn_snapshot_id = snapid;
else
assert(nnp->rn_snapshot_id == snapid);
(void) pthread_mutex_lock(&np->rn_lock);
if (rc != REP_PROTOCOL_SUCCESS)
goto fail;
if (prev != NULL) {
prev->rn_former = nnp->rn_former;
(void) pthread_mutex_lock(&nnp->rn_lock);
nnp->rn_flags &= ~RC_NODE_ON_FORMER;
nnp->rn_former = NULL;
(void) pthread_mutex_unlock(&nnp->rn_lock);
}
np->rn_flags |= RC_NODE_OLD;
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_relink_child(pp, np, nnp);
rc_node_rele(np);
smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS, &audit_data);
rc = REP_PROTOCOL_SUCCESS;
cleanout:
free(audit_data.ed_auth);
free(audit_data.ed_fmri);
free(audit_data.ed_snapname);
return (rc);
fail:
rc_node_rele_flag(np, RC_NODE_IN_TX);
rc_node_rele_locked(np);
(void) pthread_mutex_lock(&pp->rn_lock);
rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
(void) pthread_mutex_unlock(&pp->rn_lock);
if (nnp != NULL) {
if (prev == NULL)
rc_node_destroy(nnp);
else
rc_node_rele(nnp);
}
free(audit_data.ed_auth);
free(audit_data.ed_fmri);
free(audit_data.ed_snapname);
return (rc);
}
int
rc_snapshot_take_new(rc_node_ptr_t *npp, const char *svcname,
const char *instname, const char *name, rc_node_ptr_t *outpp)
{
perm_status_t granted;
rc_node_t *np;
rc_node_t *outp = NULL;
int rc, perm_rc;
char fmri[REP_PROTOCOL_FMRI_LEN];
audit_event_data_t audit_data;
size_t sz_out;
rc_node_clear(outpp, 0);
granted = rc_node_modify_permission_check(&audit_data.ed_auth);
switch (granted) {
case PERM_DENIED:
perm_rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
break;
case PERM_GRANTED:
perm_rc = REP_PROTOCOL_SUCCESS;
break;
case PERM_GONE:
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
case PERM_FAIL:
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
default:
bad_error("rc_node_modify_permission_check", granted);
break;
}
RC_NODE_PTR_CHECK_LOCK_OR_FREE_RETURN(np, npp, audit_data.ed_auth);
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) {
(void) pthread_mutex_unlock(&np->rn_lock);
free(audit_data.ed_auth);
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
rc = rc_check_type_name(REP_PROTOCOL_ENTITY_SNAPSHOT, name);
if (rc != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
free(audit_data.ed_auth);
return (rc);
}
if (svcname != NULL && (rc =
rc_check_type_name(REP_PROTOCOL_ENTITY_SERVICE, svcname)) !=
REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
free(audit_data.ed_auth);
return (rc);
}
if (instname != NULL && (rc =
rc_check_type_name(REP_PROTOCOL_ENTITY_INSTANCE, instname)) !=
REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
free(audit_data.ed_auth);
return (rc);
}
audit_data.ed_fmri = fmri;
audit_data.ed_snapname = (char *)name;
if ((rc = rc_node_get_fmri_or_fragment(np, fmri, sizeof (fmri),
&sz_out)) != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
free(audit_data.ed_auth);
return (rc);
}
if (perm_rc != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
smf_audit_event(ADT_smf_create_snap, ADT_FAILURE,
ADT_FAIL_VALUE_AUTH, &audit_data);
free(audit_data.ed_auth);
return (perm_rc);
}
HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
audit_data.ed_auth);
(void) pthread_mutex_unlock(&np->rn_lock);
rc = object_snapshot_take_new(np, svcname, instname, name, &outp);
if (rc == REP_PROTOCOL_SUCCESS) {
rc_node_assign(outpp, outp);
rc_node_rele(outp);
}
(void) pthread_mutex_lock(&np->rn_lock);
rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
(void) pthread_mutex_unlock(&np->rn_lock);
if (rc == REP_PROTOCOL_SUCCESS) {
smf_audit_event(ADT_smf_create_snap, ADT_SUCCESS, ADT_SUCCESS,
&audit_data);
}
if (audit_data.ed_auth != NULL)
free(audit_data.ed_auth);
return (rc);
}
int
rc_snapshot_take_attach(rc_node_ptr_t *npp, rc_node_ptr_t *outpp)
{
rc_node_t *np, *outp;
RC_NODE_PTR_GET_CHECK(np, npp);
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) {
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
RC_NODE_PTR_GET_CHECK_AND_LOCK(outp, outpp);
if (outp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
(void) pthread_mutex_unlock(&outp->rn_lock);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
return (rc_attach_snapshot(outp, 0, np, NULL,
NULL));
}
int
rc_snapshot_attach(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
{
rc_node_t *np;
rc_node_t *cp;
uint32_t snapid;
char old_name[REP_PROTOCOL_NAME_LEN];
int rc;
size_t sz_out;
char old_fmri[REP_PROTOCOL_FMRI_LEN];
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
snapid = np->rn_snapshot_id;
rc = rc_node_get_fmri_or_fragment(np, old_fmri, sizeof (old_fmri),
&sz_out);
(void) pthread_mutex_unlock(&np->rn_lock);
if (rc != REP_PROTOCOL_SUCCESS)
return (rc);
if (np->rn_name != NULL) {
if (strlcpy(old_name, np->rn_name, sizeof (old_name)) >=
sizeof (old_name)) {
return (REP_PROTOCOL_FAIL_TRUNCATED);
}
}
RC_NODE_PTR_GET_CHECK_AND_LOCK(cp, cpp);
if (cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
(void) pthread_mutex_unlock(&cp->rn_lock);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
rc = rc_attach_snapshot(cp, snapid, NULL,
old_fmri, old_name);
return (rc);
}
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;
}
(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);
}
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);
}
static int
rc_node_property_may_read(rc_node_t *np)
{
int ret;
perm_status_t granted = PERM_DENIED;
rc_node_t *pgp;
permcheck_t *pcp;
audit_event_data_t audit_data;
size_t sz_out;
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 (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 == PERM_FAIL)
ret = REP_PROTOCOL_FAIL_NO_RESOURCES;
if (granted == PERM_GONE)
ret = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
}
if (ret == REP_PROTOCOL_SUCCESS) {
audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
if (audit_data.ed_fmri == NULL)
ret = REP_PROTOCOL_FAIL_NO_RESOURCES;
}
if (ret == REP_PROTOCOL_SUCCESS) {
ret = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
REP_PROTOCOL_FMRI_LEN, &sz_out);
}
if (ret == REP_PROTOCOL_SUCCESS) {
int status;
int ret_value;
if (granted == PERM_DENIED) {
status = ADT_FAILURE;
ret_value = ADT_FAIL_VALUE_AUTH;
} else {
status = ADT_SUCCESS;
ret_value = ADT_SUCCESS;
}
audit_data.ed_auth = pcp->pc_auth_string;
smf_audit_event(ADT_smf_read_prop,
status, ret_value, &audit_data);
}
free(audit_data.ed_fmri);
pc_free(pcp);
if ((ret == REP_PROTOCOL_SUCCESS) && (granted == PERM_DENIED))
ret = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
return (ret);
#endif
}
static int
rc_iter_filter_name(rc_node_t *np, void *s)
{
const char *name = s;
return (strcmp(np->rn_name, name) == 0);
}
static int
rc_iter_filter_type(rc_node_t *np, void *s)
{
const char *type = s;
return (np->rn_type != NULL && strcmp(np->rn_type, type) == 0);
}
static int
rc_iter_null_filter(rc_node_t *np, void *s)
{
return (1);
}
static int
rc_iter_create(rc_node_iter_t **resp, rc_node_t *np, uint32_t type,
rc_iter_filter_func *filter, void *arg, boolean_t composed)
{
rc_node_iter_t *nip;
int res;
assert(*resp == NULL);
nip = uu_zalloc(sizeof (*nip));
if (nip == NULL)
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP)
composed = 1;
if (!composed) {
(void) pthread_mutex_lock(&np->rn_lock);
if ((res = rc_node_fill_children(np, type)) !=
REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
uu_free(nip);
return (res);
}
nip->rni_clevel = -1;
nip->rni_iter = uu_list_walk_start(np->rn_children,
UU_WALK_ROBUST);
if (nip->rni_iter != NULL) {
nip->rni_iter_node = np;
rc_node_hold_other(np);
} else {
(void) pthread_mutex_unlock(&np->rn_lock);
uu_free(nip);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
(void) pthread_mutex_unlock(&np->rn_lock);
} else {
rc_node_t *ent;
if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_SNAPSHOT) {
(void) pthread_mutex_lock(&np->rn_lock);
res = rc_node_fill_children(np,
REP_PROTOCOL_ENTITY_SNAPLEVEL);
(void) pthread_mutex_unlock(&np->rn_lock);
if (res != REP_PROTOCOL_SUCCESS) {
uu_free(nip);
return (res);
}
if (np->rn_cchain[0] == NULL)
goto empty;
}
for (nip->rni_clevel = 0; ; ++nip->rni_clevel) {
if (nip->rni_clevel >= COMPOSITION_DEPTH) {
empty:
nip->rni_clevel = -1;
nip->rni_iter = NULL;
goto out;
}
ent = np->rn_cchain[nip->rni_clevel];
assert(ent != NULL);
if (rc_node_check_and_lock(ent) == REP_PROTOCOL_SUCCESS)
break;
}
res = rc_node_fill_children(ent, type);
if (res == REP_PROTOCOL_SUCCESS) {
nip->rni_iter = uu_list_walk_start(ent->rn_children,
UU_WALK_ROBUST);
if (nip->rni_iter == NULL)
res = REP_PROTOCOL_FAIL_NO_RESOURCES;
else {
nip->rni_iter_node = ent;
rc_node_hold_other(ent);
}
}
if (res != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&ent->rn_lock);
uu_free(nip);
return (res);
}
(void) pthread_mutex_unlock(&ent->rn_lock);
}
out:
rc_node_hold(np);
nip->rni_parent = np;
nip->rni_type = type;
nip->rni_filter = (filter != NULL)? filter : rc_iter_null_filter;
nip->rni_filter_arg = arg;
*resp = nip;
return (REP_PROTOCOL_SUCCESS);
}
static void
rc_iter_end(rc_node_iter_t *iter)
{
rc_node_t *np = iter->rni_parent;
if (iter->rni_clevel >= 0)
np = np->rn_cchain[iter->rni_clevel];
assert(MUTEX_HELD(&np->rn_lock));
if (iter->rni_iter != NULL)
uu_list_walk_end(iter->rni_iter);
iter->rni_iter = NULL;
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_rele(iter->rni_parent);
if (iter->rni_iter_node != NULL)
rc_node_rele_other(iter->rni_iter_node);
}
static int
rc_node_setup_value_iter(rc_node_ptr_t *npp, rc_node_iter_t **iterp)
{
rc_node_t *np;
rc_node_iter_t *nip;
assert(*iterp == NULL);
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) {
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
}
nip = uu_zalloc(sizeof (*nip));
if (nip == NULL) {
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
nip->rni_parent = np;
nip->rni_iter = NULL;
nip->rni_clevel = -1;
nip->rni_type = REP_PROTOCOL_ENTITY_VALUE;
nip->rni_offset = 0;
nip->rni_last_offset = 0;
rc_node_hold_locked(np);
*iterp = nip;
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_SUCCESS);
}
int
rc_node_get_property_value(rc_node_ptr_t *npp,
struct rep_protocol_value_response *out, size_t *sz_out)
{
rc_node_t *np;
size_t w;
int ret;
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) {
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
if (np->rn_values_size == 0) {
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_NOT_FOUND);
}
out->rpr_type = np->rn_valtype;
w = strlcpy(out->rpr_value, &np->rn_values[0],
sizeof (out->rpr_value));
if (w >= sizeof (out->rpr_value))
backend_panic("value too large");
*sz_out = offsetof(struct rep_protocol_value_response,
rpr_value[w + 1]);
ret = (np->rn_values_count != 1)? REP_PROTOCOL_FAIL_TRUNCATED :
REP_PROTOCOL_SUCCESS;
(void) pthread_mutex_unlock(&np->rn_lock);
return (ret);
}
int
rc_iter_next_value(rc_node_iter_t *iter,
struct rep_protocol_value_response *out, size_t *sz_out, int repeat)
{
rc_node_t *np = iter->rni_parent;
const char *vals;
size_t len;
size_t start;
size_t w;
int ret;
rep_protocol_responseid_t result;
assert(*sz_out == sizeof (*out));
(void) memset(out, '\0', *sz_out);
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;
len = np->rn_values_size;
out->rpr_type = np->rn_valtype;
start = (repeat)? iter->rni_last_offset : iter->rni_offset;
if (len == 0 || start >= len) {
result = REP_PROTOCOL_DONE;
*sz_out -= sizeof (out->rpr_value);
} else {
w = strlcpy(out->rpr_value, &vals[start],
sizeof (out->rpr_value));
if (w >= sizeof (out->rpr_value))
backend_panic("value too large");
*sz_out = offsetof(struct rep_protocol_value_response,
rpr_value[w + 1]);
if (!repeat) {
iter->rni_last_offset = iter->rni_offset;
iter->rni_offset += (w + 1);
}
result = REP_PROTOCOL_SUCCESS;
}
(void) pthread_mutex_unlock(&np->rn_lock);
return (result);
}
int
rc_node_setup_iter(rc_node_ptr_t *npp, rc_node_iter_t **iterp,
uint32_t type, uint32_t flags, const char *pattern)
{
rc_node_t *np;
rc_iter_filter_func *f = NULL;
int rc;
RC_NODE_PTR_GET_CHECK(np, npp);
if (pattern != NULL && pattern[0] == '\0')
pattern = NULL;
if (type == REP_PROTOCOL_ENTITY_VALUE) {
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
if (flags != RP_ITER_START_ALL || pattern != NULL)
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
rc = rc_node_setup_value_iter(npp, iterp);
assert(rc != REP_PROTOCOL_FAIL_NOT_APPLICABLE);
return (rc);
}
if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
REP_PROTOCOL_SUCCESS)
return (rc);
if (((flags & RP_ITER_START_FILT_MASK) == RP_ITER_START_ALL) ^
(pattern == NULL))
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
if ((flags & RP_ITER_START_COMPOSED) &&
(np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE &&
np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT))
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
if (pattern != NULL) {
if ((rc = rc_check_type_name(type, pattern)) !=
REP_PROTOCOL_SUCCESS)
return (rc);
pattern = strdup(pattern);
if (pattern == NULL)
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
switch (flags & RP_ITER_START_FILT_MASK) {
case RP_ITER_START_ALL:
f = NULL;
break;
case RP_ITER_START_EXACT:
f = rc_iter_filter_name;
break;
case RP_ITER_START_PGTYPE:
if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
free((void *)pattern);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
f = rc_iter_filter_type;
break;
default:
free((void *)pattern);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
rc = rc_iter_create(iterp, np, type, f, (void *)pattern,
flags & RP_ITER_START_COMPOSED);
if (rc != REP_PROTOCOL_SUCCESS && pattern != NULL)
free((void *)pattern);
return (rc);
}
int
rc_iter_next(rc_node_iter_t *iter, rc_node_ptr_t *out, uint32_t type)
{
rc_node_t *np = iter->rni_parent;
rc_node_t *res;
int rc;
if (iter->rni_type == REP_PROTOCOL_ENTITY_VALUE)
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
if (iter->rni_iter == NULL) {
rc_node_clear(out, 0);
return (REP_PROTOCOL_DONE);
}
if (iter->rni_type != type) {
rc_node_clear(out, 0);
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
(void) pthread_mutex_lock(&np->rn_lock);
if (!rc_node_wait_flag(np, RC_NODE_CHILDREN_CHANGING)) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_clear(out, 1);
return (REP_PROTOCOL_FAIL_DELETED);
}
if (iter->rni_clevel >= 0) {
(void) pthread_mutex_unlock(&np->rn_lock);
np = np->rn_cchain[iter->rni_clevel];
(void) pthread_mutex_lock(&np->rn_lock);
if (!rc_node_wait_flag(np, RC_NODE_CHILDREN_CHANGING)) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_clear(out, 1);
return (REP_PROTOCOL_FAIL_DELETED);
}
}
assert(np->rn_flags & RC_NODE_HAS_CHILDREN);
for (;;) {
res = uu_list_walk_next(iter->rni_iter);
if (res == NULL) {
rc_node_t *parent = iter->rni_parent;
#if COMPOSITION_DEPTH == 2
if (iter->rni_clevel < 0 || iter->rni_clevel == 1) {
rc_iter_end(iter);
break;
}
uu_list_walk_end(iter->rni_iter);
iter->rni_iter = NULL;
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_rele_other(iter->rni_iter_node);
iter->rni_iter_node = NULL;
++iter->rni_clevel;
np = parent->rn_cchain[iter->rni_clevel];
assert(np != NULL);
#else
#error This code must be updated.
#endif
(void) pthread_mutex_lock(&np->rn_lock);
rc = rc_node_fill_children(np, iter->rni_type);
if (rc == REP_PROTOCOL_SUCCESS) {
iter->rni_iter =
uu_list_walk_start(np->rn_children,
UU_WALK_ROBUST);
if (iter->rni_iter == NULL)
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
else {
iter->rni_iter_node = np;
rc_node_hold_other(np);
}
}
if (rc != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_clear(out, 0);
return (rc);
}
continue;
}
if (res->rn_id.rl_type != type ||
!iter->rni_filter(res, iter->rni_filter_arg))
continue;
if (iter->rni_clevel > 0) {
rc_node_t *ent = iter->rni_parent->rn_cchain[0];
rc_node_t *pg;
#if COMPOSITION_DEPTH == 2
assert(iter->rni_clevel == 1);
(void) pthread_mutex_unlock(&np->rn_lock);
(void) pthread_mutex_lock(&ent->rn_lock);
rc = rc_node_find_named_child(ent, res->rn_name, type,
&pg);
if (rc == REP_PROTOCOL_SUCCESS && pg != NULL)
rc_node_rele(pg);
(void) pthread_mutex_unlock(&ent->rn_lock);
if (rc != REP_PROTOCOL_SUCCESS) {
rc_node_clear(out, 0);
return (rc);
}
(void) pthread_mutex_lock(&np->rn_lock);
if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_clear(out, 1);
return (REP_PROTOCOL_FAIL_DELETED);
}
if (pg != NULL)
continue;
#else
#error This code must be updated.
#endif
}
if (iter->rni_clevel >= 0 &&
type == REP_PROTOCOL_ENTITY_PROPERTYGRP &&
iter->rni_clevel < COMPOSITION_DEPTH - 1) {
#if COMPOSITION_DEPTH == 2
rc_node_t *pg;
rc_node_t *ent = iter->rni_parent->rn_cchain[1];
rc_node_hold(res);
(void) pthread_mutex_unlock(&np->rn_lock);
(void) pthread_mutex_lock(&ent->rn_lock);
rc = rc_node_find_named_child(ent, res->rn_name, type,
&pg);
(void) pthread_mutex_unlock(&ent->rn_lock);
if (rc != REP_PROTOCOL_SUCCESS) {
rc_node_rele(res);
rc_node_clear(out, 0);
return (rc);
}
(void) pthread_mutex_lock(&np->rn_lock);
if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_rele(res);
if (pg != NULL)
rc_node_rele(pg);
rc_node_clear(out, 1);
return (REP_PROTOCOL_FAIL_DELETED);
}
if (pg == NULL) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_rele(res);
(void) pthread_mutex_lock(&np->rn_lock);
if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
(void) pthread_mutex_unlock(&np->
rn_lock);
rc_node_clear(out, 1);
return (REP_PROTOCOL_FAIL_DELETED);
}
} else {
rc_node_t *cpg;
cpg = rc_node_alloc();
if (cpg == NULL) {
(void) pthread_mutex_unlock(
&np->rn_lock);
rc_node_rele(res);
rc_node_rele(pg);
rc_node_clear(out, 0);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
switch (rc_node_setup_cpg(cpg, res, pg)) {
case REP_PROTOCOL_SUCCESS:
res = cpg;
break;
case REP_PROTOCOL_FAIL_TYPE_MISMATCH:
(void) pthread_mutex_unlock(&np->
rn_lock);
rc_node_destroy(cpg);
rc_node_rele(pg);
rc_node_rele(res);
(void) pthread_mutex_lock(&np->
rn_lock);
if (!rc_node_wait_flag(np,
RC_NODE_DYING)) {
(void) pthread_mutex_unlock(&
np->rn_lock);
rc_node_clear(out, 1);
return
(REP_PROTOCOL_FAIL_DELETED);
}
break;
case REP_PROTOCOL_FAIL_NO_RESOURCES:
rc_node_destroy(cpg);
(void) pthread_mutex_unlock(
&np->rn_lock);
rc_node_rele(res);
rc_node_rele(pg);
rc_node_clear(out, 0);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
default:
assert(0);
abort();
}
}
#else
#error This code must be updated.
#endif
}
rc_node_hold(res);
(void) pthread_mutex_unlock(&np->rn_lock);
break;
}
rc_node_assign(out, res);
if (res == NULL)
return (REP_PROTOCOL_DONE);
rc_node_rele(res);
return (REP_PROTOCOL_SUCCESS);
}
void
rc_iter_destroy(rc_node_iter_t **nipp)
{
rc_node_iter_t *nip = *nipp;
rc_node_t *np;
if (nip == NULL)
return;
np = nip->rni_parent;
if (nip->rni_filter_arg != NULL)
free(nip->rni_filter_arg);
nip->rni_filter_arg = NULL;
if (nip->rni_type == REP_PROTOCOL_ENTITY_VALUE ||
nip->rni_iter != NULL) {
if (nip->rni_clevel < 0)
(void) pthread_mutex_lock(&np->rn_lock);
else
(void) pthread_mutex_lock(
&np->rn_cchain[nip->rni_clevel]->rn_lock);
rc_iter_end(nip);
}
nip->rni_parent = NULL;
uu_free(nip);
*nipp = NULL;
}
int
rc_node_setup_tx(rc_node_ptr_t *npp, rc_node_ptr_t *txp)
{
rc_node_t *np;
permcheck_t *pcp;
int ret;
perm_status_t granted;
rc_auth_state_t authorized = RC_AUTH_UNKNOWN;
char *auth_string = NULL;
RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
rc_node_rele(np);
np = np->rn_cchain[0];
RC_NODE_CHECK_AND_HOLD(np);
}
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
rc_node_rele(np);
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
if (np->rn_id.rl_ids[ID_SNAPSHOT] != 0) {
rc_node_rele(np);
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
}
#ifdef NATIVE_BUILD
if (client_is_privileged())
goto skip_checks;
rc_node_rele(np);
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
#else
if (is_main_repository == 0)
goto skip_checks;
pcp = pc_create();
if (pcp == NULL) {
rc_node_rele(np);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
if (np->rn_id.rl_ids[ID_INSTANCE] != 0 &&
((strcmp(np->rn_name, AUTH_PG_ACTIONS) == 0 &&
strcmp(np->rn_type, AUTH_PG_ACTIONS_TYPE) == 0) ||
(strcmp(np->rn_name, AUTH_PG_GENERAL_OVR) == 0 &&
strcmp(np->rn_type, AUTH_PG_GENERAL_OVR_TYPE) == 0))) {
rc_node_t *instn;
ret = perm_add_enabling(pcp, AUTH_MODIFY);
if (ret != REP_PROTOCOL_SUCCESS) {
pc_free(pcp);
rc_node_rele(np);
return (ret);
}
ret = perm_add_enabling(pcp, AUTH_MANAGE);
if (ret != REP_PROTOCOL_SUCCESS) {
pc_free(pcp);
rc_node_rele(np);
return (ret);
}
ret = rc_node_parent(np, &instn);
if (ret != REP_PROTOCOL_SUCCESS) {
assert(ret == REP_PROTOCOL_FAIL_DELETED);
rc_node_rele(np);
pc_free(pcp);
return (REP_PROTOCOL_FAIL_DELETED);
}
assert(instn->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE);
ret = perm_add_inst_action_auth(pcp, instn);
rc_node_rele(instn);
switch (ret) {
case REP_PROTOCOL_SUCCESS:
break;
case REP_PROTOCOL_FAIL_DELETED:
case REP_PROTOCOL_FAIL_NO_RESOURCES:
rc_node_rele(np);
pc_free(pcp);
return (ret);
default:
bad_error("perm_add_inst_action_auth", ret);
}
if (strcmp(np->rn_name, AUTH_PG_ACTIONS) == 0)
authorized = RC_AUTH_PASSED;
} else {
ret = perm_add_enabling(pcp, AUTH_MODIFY);
if (ret == REP_PROTOCOL_SUCCESS) {
const char * const auth =
perm_auth_for_pgtype(np->rn_type);
if (auth != NULL)
ret = perm_add_enabling(pcp, auth);
}
if (ret == REP_PROTOCOL_SUCCESS)
ret =
perm_add_enabling_values(pcp, np, AUTH_PROP_VALUE);
if (ret == REP_PROTOCOL_SUCCESS)
ret =
perm_add_enabling_values(pcp, np, AUTH_PROP_MODIFY);
if (ret == REP_PROTOCOL_SUCCESS &&
strcmp(np->rn_name, AUTH_PG_GENERAL) == 0 &&
strcmp(np->rn_type, AUTH_PG_GENERAL_TYPE) == 0)
ret = perm_add_enabling(pcp, AUTH_MANAGE);
if (ret != REP_PROTOCOL_SUCCESS) {
pc_free(pcp);
rc_node_rele(np);
return (ret);
}
}
granted = perm_granted(pcp);
ret = map_granted_status(granted, pcp, &auth_string);
pc_free(pcp);
if ((granted == PERM_GONE) || (granted == PERM_FAIL) ||
(ret == REP_PROTOCOL_FAIL_NO_RESOURCES)) {
free(auth_string);
rc_node_rele(np);
return (ret);
}
if (granted == PERM_DENIED) {
authorized = RC_AUTH_FAILED;
}
#endif
skip_checks:
rc_node_assign(txp, np);
txp->rnp_authorized = authorized;
if (authorized != RC_AUTH_UNKNOWN) {
if (txp->rnp_auth_string != NULL)
free((void *)txp->rnp_auth_string);
txp->rnp_auth_string = auth_string;
auth_string = NULL;
}
rc_node_rele(np);
if (auth_string != NULL)
free(auth_string);
return (REP_PROTOCOL_SUCCESS);
}
static int
tx_allow_value(const void *cmds_arg, size_t cmds_sz, rc_node_t *pg)
{
const struct rep_protocol_transaction_cmd *cmds;
uintptr_t loc;
uint32_t sz;
rc_node_t *prop;
boolean_t ok;
assert(!MUTEX_HELD(&pg->rn_lock));
loc = (uintptr_t)cmds_arg;
while (cmds_sz > 0) {
cmds = (struct rep_protocol_transaction_cmd *)loc;
if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
return (-1);
sz = cmds->rptc_size;
if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
return (-1);
sz = TX_SIZE(sz);
if (sz > cmds_sz)
return (-1);
switch (cmds[0].rptc_action) {
case REP_PROTOCOL_TX_ENTRY_CLEAR:
break;
case REP_PROTOCOL_TX_ENTRY_REPLACE:
(void) pthread_mutex_lock(&pg->rn_lock);
ok = B_FALSE;
if (rc_node_find_named_child(pg,
(const char *)cmds[0].rptc_data,
REP_PROTOCOL_ENTITY_PROPERTY, &prop) ==
REP_PROTOCOL_SUCCESS) {
if (prop != NULL) {
ok = prop->rn_valtype ==
cmds[0].rptc_type;
rc_node_rele(prop);
}
}
(void) pthread_mutex_unlock(&pg->rn_lock);
if (ok)
break;
return (0);
default:
return (0);
}
if (strcmp((const char *)cmds[0].rptc_data, AUTH_PROP_MODIFY)
== 0)
return (0);
loc += sz;
cmds_sz -= sz;
}
return (1);
}
static int
tx_modifies_action(const void *cmds_arg, size_t cmds_sz)
{
const struct rep_protocol_transaction_cmd *cmds;
uintptr_t loc;
uint32_t sz;
loc = (uintptr_t)cmds_arg;
while (cmds_sz > 0) {
cmds = (struct rep_protocol_transaction_cmd *)loc;
if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
return (-1);
sz = cmds->rptc_size;
if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
return (-1);
sz = TX_SIZE(sz);
if (sz > cmds_sz)
return (-1);
if (strcmp((const char *)cmds[0].rptc_data, AUTH_PROP_ACTION)
== 0)
return (1);
loc += sz;
cmds_sz -= sz;
}
return (0);
}
static int
tx_only_enabled(const void *cmds_arg, size_t cmds_sz)
{
const struct rep_protocol_transaction_cmd *cmd;
uintptr_t loc;
uint32_t sz;
loc = (uintptr_t)cmds_arg;
while (cmds_sz > 0) {
cmd = (struct rep_protocol_transaction_cmd *)loc;
if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
return (-1);
sz = cmd->rptc_size;
if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
return (-1);
sz = TX_SIZE(sz);
if (sz > cmds_sz)
return (-1);
if (strcmp((const char *)cmd->rptc_data, AUTH_PROP_ENABLED)
!= 0)
return (0);
loc += sz;
cmds_sz -= sz;
}
return (1);
}
int
rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz)
{
rc_node_t *np = txp->rnp_node;
rc_node_t *pp;
rc_node_t *nnp;
rc_node_pg_notify_t *pnp;
int rc;
permcheck_t *pcp;
perm_status_t granted;
int normal;
char *pg_fmri = NULL;
char *auth_string = NULL;
int auth_status = ADT_SUCCESS;
int auth_ret_value = ADT_SUCCESS;
size_t sz_out;
int tx_flag = 1;
tx_commit_data_t *tx_data = NULL;
RC_NODE_CHECK(np);
if ((txp->rnp_authorized != RC_AUTH_UNKNOWN) &&
(txp->rnp_auth_string != NULL)) {
auth_string = strdup(txp->rnp_auth_string);
if (auth_string == NULL)
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
if ((txp->rnp_authorized == RC_AUTH_UNKNOWN) &&
is_main_repository) {
#ifdef NATIVE_BUILD
if (!client_is_privileged()) {
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
}
#else
pcp = pc_create();
if (pcp == NULL)
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
normal = 1;
rc = REP_PROTOCOL_SUCCESS;
if (strcmp(np->rn_name, AUTH_PG_GENERAL) == 0 &&
strcmp(np->rn_type, AUTH_PG_GENERAL_TYPE) == 0) {
rc = tx_modifies_action(cmds, cmds_sz);
if (rc == -1) {
pc_free(pcp);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
if (rc) {
rc = perm_add_enabling(pcp, AUTH_MODIFY);
if (rc == REP_PROTOCOL_SUCCESS)
rc = perm_add_enabling(pcp,
AUTH_MANAGE);
normal = 0;
} else {
rc = REP_PROTOCOL_SUCCESS;
}
} else if (np->rn_id.rl_ids[ID_INSTANCE] != 0 &&
strcmp(np->rn_name, AUTH_PG_GENERAL_OVR) == 0 &&
strcmp(np->rn_type, AUTH_PG_GENERAL_OVR_TYPE) == 0) {
rc_node_t *instn;
rc = tx_only_enabled(cmds, cmds_sz);
if (rc == -1) {
pc_free(pcp);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
if (rc) {
rc = rc_node_parent(np, &instn);
if (rc != REP_PROTOCOL_SUCCESS) {
assert(rc == REP_PROTOCOL_FAIL_DELETED);
pc_free(pcp);
return (rc);
}
assert(instn->rn_id.rl_type ==
REP_PROTOCOL_ENTITY_INSTANCE);
rc = perm_add_inst_action_auth(pcp, instn);
rc_node_rele(instn);
switch (rc) {
case REP_PROTOCOL_SUCCESS:
break;
case REP_PROTOCOL_FAIL_DELETED:
case REP_PROTOCOL_FAIL_NO_RESOURCES:
pc_free(pcp);
return (rc);
default:
bad_error("perm_add_inst_action_auth",
rc);
}
} else {
rc = REP_PROTOCOL_SUCCESS;
}
}
if (rc == REP_PROTOCOL_SUCCESS && normal) {
rc = perm_add_enabling(pcp, AUTH_MODIFY);
if (rc == REP_PROTOCOL_SUCCESS) {
const char * const auth =
perm_auth_for_pgtype(np->rn_type);
if (auth != NULL)
rc = perm_add_enabling(pcp, auth);
}
if (rc == REP_PROTOCOL_SUCCESS)
rc = perm_add_enabling_values(pcp, np,
AUTH_PROP_MODIFY);
if (rc == REP_PROTOCOL_SUCCESS) {
rc = tx_allow_value(cmds, cmds_sz, np);
if (rc == -1)
rc = REP_PROTOCOL_FAIL_BAD_REQUEST;
else if (rc)
rc = perm_add_enabling_values(pcp, np,
AUTH_PROP_VALUE);
}
}
if (rc == REP_PROTOCOL_SUCCESS) {
granted = perm_granted(pcp);
rc = map_granted_status(granted, pcp, &auth_string);
if ((granted == PERM_DENIED) && auth_string) {
rc = REP_PROTOCOL_SUCCESS;
}
}
pc_free(pcp);
if (rc != REP_PROTOCOL_SUCCESS)
goto cleanout;
if (granted == PERM_DENIED) {
auth_status = ADT_FAILURE;
auth_ret_value = ADT_FAIL_VALUE_AUTH;
tx_flag = 0;
}
#endif
} else if (txp->rnp_authorized == RC_AUTH_FAILED) {
auth_status = ADT_FAILURE;
auth_ret_value = ADT_FAIL_VALUE_AUTH;
tx_flag = 0;
}
pg_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
if (pg_fmri == NULL) {
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
goto cleanout;
}
if ((rc = rc_node_get_fmri_or_fragment(np, pg_fmri,
REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
goto cleanout;
}
if ((rc = tx_commit_data_new(cmds, cmds_sz, &tx_data)) !=
REP_PROTOCOL_SUCCESS) {
goto cleanout;
}
if (tx_flag == 0) {
generate_property_events(tx_data, pg_fmri, auth_string,
auth_status, auth_ret_value);
rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
goto cleanout;
}
nnp = rc_node_alloc();
if (nnp == NULL) {
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
goto cleanout;
}
nnp->rn_id = np->rn_id;
nnp->rn_hash = np->rn_hash;
nnp->rn_name = strdup(np->rn_name);
nnp->rn_type = strdup(np->rn_type);
nnp->rn_pgflags = np->rn_pgflags;
nnp->rn_flags = RC_NODE_IN_TX | RC_NODE_USING_PARENT;
if (nnp->rn_name == NULL || nnp->rn_type == NULL) {
rc_node_destroy(nnp);
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
goto cleanout;
}
(void) pthread_mutex_lock(&np->rn_lock);
if ((rc = rc_node_fill_children(np, REP_PROTOCOL_ENTITY_PROPERTY)) !=
REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_destroy(nnp);
goto cleanout;
}
if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_destroy(nnp);
rc = REP_PROTOCOL_FAIL_DELETED;
goto cleanout;
}
if (np->rn_flags & RC_NODE_OLD) {
rc_node_rele_flag(np, RC_NODE_USING_PARENT);
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_destroy(nnp);
rc = REP_PROTOCOL_FAIL_NOT_LATEST;
goto cleanout;
}
pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
if (pp == NULL) {
rc_node_destroy(nnp);
(void) pthread_mutex_lock(&np->rn_lock);
if (np->rn_flags & RC_NODE_OLD) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc = REP_PROTOCOL_FAIL_NOT_LATEST;
goto cleanout;
}
(void) pthread_mutex_unlock(&np->rn_lock);
rc = REP_PROTOCOL_FAIL_DELETED;
goto cleanout;
}
(void) pthread_mutex_unlock(&pp->rn_lock);
(void) pthread_mutex_lock(&np->rn_lock);
if (!rc_node_hold_flag(np, RC_NODE_IN_TX)) {
(void) pthread_mutex_unlock(&np->rn_lock);
(void) pthread_mutex_lock(&pp->rn_lock);
rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
(void) pthread_mutex_unlock(&pp->rn_lock);
rc_node_destroy(nnp);
rc = REP_PROTOCOL_FAIL_DELETED;
goto cleanout;
}
nnp->rn_gen_id = np->rn_gen_id;
(void) pthread_mutex_unlock(&np->rn_lock);
rc = object_tx_commit(&np->rn_id, tx_data, &nnp->rn_gen_id);
(void) pthread_mutex_lock(&np->rn_lock);
if (rc != REP_PROTOCOL_SUCCESS) {
rc_node_rele_flag(np, RC_NODE_IN_TX);
(void) pthread_mutex_unlock(&np->rn_lock);
(void) pthread_mutex_lock(&pp->rn_lock);
rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
(void) pthread_mutex_unlock(&pp->rn_lock);
rc_node_destroy(nnp);
rc_node_clear(txp, 0);
if (rc == REP_PROTOCOL_DONE)
rc = REP_PROTOCOL_SUCCESS;
goto cleanout;
}
(void) pthread_mutex_lock(&rc_pg_notify_lock);
while ((pnp = uu_list_first(np->rn_pg_notify_list)) != NULL)
rc_pg_notify_fire(pnp);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
np->rn_flags |= RC_NODE_OLD;
(void) pthread_mutex_unlock(&np->rn_lock);
rc_notify_remove_node(np);
rc_node_relink_child(pp, np, nnp);
rc_node_clear(txp, 0);
generate_property_events(tx_data, pg_fmri, auth_string,
auth_status, auth_ret_value);
rc = REP_PROTOCOL_SUCCESS;
cleanout:
free(auth_string);
free(pg_fmri);
tx_commit_data_free(tx_data);
return (rc);
}
void
rc_pg_notify_init(rc_node_pg_notify_t *pnp)
{
uu_list_node_init(pnp, &pnp->rnpn_node, rc_pg_notify_pool);
pnp->rnpn_pg = NULL;
pnp->rnpn_fd = -1;
}
int
rc_pg_notify_setup(rc_node_pg_notify_t *pnp, rc_node_ptr_t *npp, int fd)
{
rc_node_t *np;
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
if (!rc_node_wait_flag(np, RC_NODE_IN_TX)) {
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_DELETED);
}
if (np->rn_flags & RC_NODE_OLD) {
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_FAIL_NOT_LATEST);
}
(void) pthread_mutex_lock(&rc_pg_notify_lock);
rc_pg_notify_fire(pnp);
pnp->rnpn_pg = np;
pnp->rnpn_fd = fd;
(void) uu_list_insert_after(np->rn_pg_notify_list, NULL, pnp);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
(void) pthread_mutex_unlock(&np->rn_lock);
return (REP_PROTOCOL_SUCCESS);
}
void
rc_pg_notify_fini(rc_node_pg_notify_t *pnp)
{
(void) pthread_mutex_lock(&rc_pg_notify_lock);
rc_pg_notify_fire(pnp);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
uu_list_node_fini(pnp, &pnp->rnpn_node, rc_pg_notify_pool);
}
void
rc_notify_info_init(rc_notify_info_t *rnip)
{
int i;
uu_list_node_init(rnip, &rnip->rni_list_node, rc_notify_info_pool);
uu_list_node_init(&rnip->rni_notify, &rnip->rni_notify.rcn_list_node,
rc_notify_pool);
rnip->rni_notify.rcn_node = NULL;
rnip->rni_notify.rcn_info = rnip;
bzero(rnip->rni_namelist, sizeof (rnip->rni_namelist));
bzero(rnip->rni_typelist, sizeof (rnip->rni_typelist));
(void) pthread_cond_init(&rnip->rni_cv, NULL);
for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
rnip->rni_namelist[i] = NULL;
rnip->rni_typelist[i] = NULL;
}
}
static void
rc_notify_info_insert_locked(rc_notify_info_t *rnip)
{
assert(MUTEX_HELD(&rc_pg_notify_lock));
assert(!(rnip->rni_flags & RC_NOTIFY_ACTIVE));
rnip->rni_flags |= RC_NOTIFY_ACTIVE;
(void) uu_list_insert_after(rc_notify_info_list, NULL, rnip);
(void) uu_list_insert_before(rc_notify_list, NULL, &rnip->rni_notify);
}
static void
rc_notify_info_remove_locked(rc_notify_info_t *rnip)
{
rc_notify_t *me = &rnip->rni_notify;
rc_notify_t *np;
assert(MUTEX_HELD(&rc_pg_notify_lock));
assert(rnip->rni_flags & RC_NOTIFY_ACTIVE);
assert(!(rnip->rni_flags & RC_NOTIFY_DRAIN));
rnip->rni_flags |= RC_NOTIFY_DRAIN;
(void) pthread_cond_broadcast(&rnip->rni_cv);
(void) uu_list_remove(rc_notify_info_list, rnip);
if (uu_list_first(rc_notify_list) == me) {
while (rc_notify_in_use) {
(void) pthread_cond_wait(&rc_pg_notify_cv,
&rc_pg_notify_lock);
}
while ((np = uu_list_next(rc_notify_list, me)) != NULL &&
np->rcn_info == NULL)
rc_notify_remove_locked(np);
}
(void) uu_list_remove(rc_notify_list, me);
while (rnip->rni_waiters) {
(void) pthread_cond_broadcast(&rc_pg_notify_cv);
(void) pthread_cond_broadcast(&rnip->rni_cv);
(void) pthread_cond_wait(&rnip->rni_cv, &rc_pg_notify_lock);
}
rnip->rni_flags &= ~(RC_NOTIFY_DRAIN | RC_NOTIFY_ACTIVE);
}
static int
rc_notify_info_add_watch(rc_notify_info_t *rnip, const char **arr,
const char *name)
{
int i;
int rc;
char *f;
rc = rc_check_type_name(REP_PROTOCOL_ENTITY_PROPERTYGRP, name);
if (rc != REP_PROTOCOL_SUCCESS)
return (rc);
f = strdup(name);
if (f == NULL)
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
(void) pthread_mutex_lock(&rc_pg_notify_lock);
while (rnip->rni_flags & RC_NOTIFY_EMPTYING)
(void) pthread_cond_wait(&rnip->rni_cv, &rc_pg_notify_lock);
for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
if (arr[i] == NULL)
break;
if (strcmp(arr[i], f) == 0) {
free(f);
goto out;
}
}
if (i == RC_NOTIFY_MAX_NAMES) {
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
free(f);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
arr[i] = f;
out:
if (!(rnip->rni_flags & RC_NOTIFY_ACTIVE))
rc_notify_info_insert_locked(rnip);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
return (REP_PROTOCOL_SUCCESS);
}
int
rc_notify_info_add_name(rc_notify_info_t *rnip, const char *name)
{
return (rc_notify_info_add_watch(rnip, rnip->rni_namelist, name));
}
int
rc_notify_info_add_type(rc_notify_info_t *rnip, const char *type)
{
return (rc_notify_info_add_watch(rnip, rnip->rni_typelist, type));
}
int
rc_notify_info_wait(rc_notify_info_t *rnip, rc_node_ptr_t *out,
char *outp, size_t sz)
{
rc_notify_t *np;
rc_notify_t *me = &rnip->rni_notify;
rc_node_t *nnp;
rc_notify_delete_t *ndp;
int am_first_info;
if (sz > 0)
outp[0] = 0;
(void) pthread_mutex_lock(&rc_pg_notify_lock);
while ((rnip->rni_flags & (RC_NOTIFY_ACTIVE | RC_NOTIFY_DRAIN)) ==
RC_NOTIFY_ACTIVE) {
am_first_info = (uu_list_first(rc_notify_list) == me);
if (am_first_info && rc_notify_in_use) {
rnip->rni_waiters++;
(void) pthread_cond_wait(&rc_pg_notify_cv,
&rc_pg_notify_lock);
rnip->rni_waiters--;
continue;
}
np = uu_list_next(rc_notify_list, me);
while (np != NULL && !rc_notify_info_interested(rnip, np)) {
rc_notify_t *next = uu_list_next(rc_notify_list, np);
if (am_first_info) {
if (np->rcn_info) {
am_first_info = 0;
} else {
rc_notify_remove_locked(np);
}
}
np = next;
}
if (np == NULL) {
rnip->rni_waiters++;
(void) pthread_cond_wait(&rnip->rni_cv,
&rc_pg_notify_lock);
rnip->rni_waiters--;
continue;
}
(void) uu_list_remove(rc_notify_list, me);
(void) uu_list_insert_after(rc_notify_list, np, me);
if ((ndp = np->rcn_delete) != NULL) {
(void) strlcpy(outp, ndp->rnd_fmri, sz);
if (am_first_info)
rc_notify_remove_locked(np);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
rc_node_clear(out, 0);
return (REP_PROTOCOL_SUCCESS);
}
nnp = np->rcn_node;
assert(nnp != NULL);
rc_notify_in_use++;
assert(rc_notify_in_use > 0);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
rc_node_assign(out, nnp);
(void) pthread_mutex_lock(&rc_pg_notify_lock);
assert(rc_notify_in_use > 0);
rc_notify_in_use--;
if (am_first_info) {
while (rc_notify_in_use) {
(void) pthread_cond_wait(&rc_pg_notify_cv,
&rc_pg_notify_lock);
}
rc_notify_remove_locked(np);
}
if (rc_notify_in_use == 0)
(void) pthread_cond_broadcast(&rc_pg_notify_cv);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
return (REP_PROTOCOL_SUCCESS);
}
if (rnip->rni_waiters == 0)
(void) pthread_cond_broadcast(&rnip->rni_cv);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
return (REP_PROTOCOL_DONE);
}
static void
rc_notify_info_reset(rc_notify_info_t *rnip)
{
int i;
(void) pthread_mutex_lock(&rc_pg_notify_lock);
if (rnip->rni_flags & RC_NOTIFY_ACTIVE)
rc_notify_info_remove_locked(rnip);
assert(!(rnip->rni_flags & (RC_NOTIFY_DRAIN | RC_NOTIFY_EMPTYING)));
rnip->rni_flags |= RC_NOTIFY_EMPTYING;
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
if (rnip->rni_namelist[i] != NULL) {
free((void *)rnip->rni_namelist[i]);
rnip->rni_namelist[i] = NULL;
}
if (rnip->rni_typelist[i] != NULL) {
free((void *)rnip->rni_typelist[i]);
rnip->rni_typelist[i] = NULL;
}
}
(void) pthread_mutex_lock(&rc_pg_notify_lock);
rnip->rni_flags &= ~RC_NOTIFY_EMPTYING;
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
}
void
rc_notify_info_fini(rc_notify_info_t *rnip)
{
rc_notify_info_reset(rnip);
uu_list_node_fini(rnip, &rnip->rni_list_node, rc_notify_info_pool);
uu_list_node_fini(&rnip->rni_notify, &rnip->rni_notify.rcn_list_node,
rc_notify_pool);
}