picltree.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This module implements the PTree interface and the PICL to PTree calls
*/
/*
* Note:
* PICL Node and Property Handles Table:
* A node or property in PICL tree has two handles: a ptree handle, which is
* used by plug-ins and the libpicltree interface, and a picl handle
* which is used by clients and the libpicl interface.
* The mapping of ptree handles to the internal PICL object (picl_obj_t) is
* kept in a ptree hash table (ptreetbl), and the mapping of a picl handle
* to its ptree handle is kept in the picl hash table (picltbl).
* The reader/writer lock, ptree_rwlock, is held when reading or modifying ptree
* hash table (ptreetbl) and/or the PICL tree structure (nodes and linkages
* between them). The reader/writer lock, picltbl_rwlock, is held when reading
* or modifying picl hash table (picltbl).
*
* The mutex, ptreehdl_lock, is used to control allocation of ptree handles.
* The mutex, piclhdl_lock, is used to control allocation of picl handles.
*
* The mutex, ptree_refresh_mutex, and the condition, ptree_refresh_cond,
* are used to synchronize PICL refreshes (ptree_refresh) and to wait/signal
* change in PICL tree structure.
*
* The counter, picl_hdl_hi, is the hi water mark for allocated picl handles.
* The counter, ptree_hdl_hi, is the hi water mark for allocated ptree handles.
* A stale handle error is returned for handle values below the hi water
* mark, and invalid handles are returned for handle values above the hi water
* mark or when the process id field of the handle does not match.
*
* Locking Scheme:
* The structure of the PICL tree is controlled by the ptree_rwlock. The
* properties of a node are controlled by individual node locks. The
* piclize-ing or unpiclize-ing of a node is controlled by picltbl_rwlock.
*
* Two-Phase Locking scheme: lock acquire phase and lock release phase.
*
* Lock Ordering:
* The ptree_rwlock and node locks are always acquired in the following order:
* lock ptree_rwlock
* lock node
*
* Lock Strategy:
* There are three locks:
* ptree_rwlock: a reader lock is obtained to do ptree hash table
* lookups and traverse tree. A writer lock is obtained
* when creating or destroying nodes from the ptree,
* or when modifying node linkages: parent, peer, child.
* picltbl_rwlock: a reader lock is obtained for picl hash table lookups.
* A writer lock is obtained when piclize-ing or
* unpiclize-ing nodes or properties.
* node_lock: This is a reader/writer lock for properties of a node.
* A reader lock is obtained before reading property
* values. A writer lock is obtained when adding or
* removing properties and when modifying a property value.
*
* Never hold more than one node lock at a time.
*
* Event Locking:
* There are two locks:
* evtq_lock: this lock protects the event queue. It is obtained
* to queue events that are posted and to unqueue
* events to be dispatched.
* evtq_cv: condition variable is protected by evtq_lock. It is
* used by the ptree event thread to wait for events
* until eventqp is not NULL.
* evtq_empty: condition variable protected by evtq_lock. It is
* used to signal when the eventq becomes empty. The
* reinitialization process waits on this condition.
* evthandler_lock: this protects the event handler list. It is obtained
* to add event handlers on registration and to remove
* event handlers on unregistration.
* (handler)->cv: condition variable per handler protected by
* evthandler_lock. It is used to wait until the
* event handler completes execution (execflg == 0)
* before unregistering the handler.
*/
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdarg.h>
#include <alloca.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <limits.h>
#include <libintl.h>
#include <syslog.h>
#include <pthread.h>
#include <synch.h>
#include <setjmp.h>
#include <signal.h>
#include <dlfcn.h>
#include <dirent.h>
#include <door.h>
#include <time.h>
#include <inttypes.h>
#include <sys/systeminfo.h>
#include <sys/utsname.h>
#include <picl.h>
#include <picltree.h>
#include "picldefs.h"
#include "ptree_impl.h"
#define SO_VERS ".so.1"
static hash_t picltbl; /* client handles to picl obj */
static hash_t ptreetbl; /* ptree handles to picl obj */
static pthread_mutex_t ptreehdl_lock;
static pthread_mutex_t piclhdl_lock;
static pthread_mutex_t ptree_refresh_mutex;
static rwlock_t picltbl_rwlock; /* PICL handle table lock */
static rwlock_t ptree_rwlock; /* PICL tree lock */
static pthread_cond_t ptree_refresh_cond = PTHREAD_COND_INITIALIZER;
static uint32_t ptree_hdl_hi = 1;
static uint32_t picl_hdl_hi = 1;
static picl_obj_t *picl_root_obj = NULL;
static picl_nodehdl_t ptree_root_hdl = PICL_INVALID_PICLHDL;
static int ptree_generation = 0;
static pid_t picld_pid;
static door_cred_t picld_cred;
static int qempty_wait; /* evtq_empty condition waiter flag */
static picld_plugin_reg_list_t *plugin_reg_list = NULL;
static picld_plugin_desc_t *plugin_desc;
static eventq_t *eventqp; /* PICL events queue */
static pthread_mutex_t evtq_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t evtq_cv = PTHREAD_COND_INITIALIZER;
static pthread_cond_t evtq_empty = PTHREAD_COND_INITIALIZER;
static evt_handler_t *evt_handlers; /* Event handler list */
static pthread_mutex_t evthandler_lock = PTHREAD_MUTEX_INITIALIZER;
/*
* PICL daemon verbose level
*/
int verbose_level;
/*
* Event handler free functions
*/
static void
free_handler(evt_handler_t *evhp)
{
if (evhp->ename)
free(evhp->ename);
(void) pthread_cond_broadcast(&evhp->cv);
(void) pthread_cond_destroy(&evhp->cv);
free(evhp);
}
/*
* queue_event to events queue
*/
static void
queue_event(eventq_t *evt)
{
eventq_t *tmpp;
evt->next = NULL;
if (eventqp == NULL)
eventqp = evt;
else {
tmpp = eventqp;
while (tmpp->next != NULL)
tmpp = tmpp->next;
tmpp->next = evt;
}
}
/*
* unqueue_event from the specified eventq
*/
static eventq_t *
unqueue_event(eventq_t **qp)
{
eventq_t *evtp;
evtp = *qp;
if (evtp != NULL)
*qp = evtp->next;
return (evtp);
}
/*
* register an event handler by adding it to the list
*/
int
ptree_register_handler(const char *ename,
void (*evt_handler)(const char *ename, const void *earg, size_t size,
void *cookie), void *cookie)
{
evt_handler_t *ent;
evt_handler_t *iter;
if (ename == NULL)
return (PICL_INVALIDARG);
/*
* Initialize event handler entry
*/
ent = malloc(sizeof (*ent));
if (ent == NULL)
return (PICL_FAILURE);
ent->ename = strdup(ename);
if (ent->ename == NULL) {
free(ent);
return (PICL_FAILURE);
}
ent->cookie = cookie;
ent->evt_handler = evt_handler;
ent->execflg = 0;
ent->wakeupflg = 0;
(void) pthread_cond_init(&ent->cv, NULL);
ent->next = NULL;
/*
* add handler to the handler list
*/
(void) pthread_mutex_lock(&evthandler_lock);
if (evt_handlers == NULL) {
evt_handlers = ent;
(void) pthread_mutex_unlock(&evthandler_lock);
return (PICL_SUCCESS);
}
iter = evt_handlers;
while (iter->next != NULL)
iter = iter->next;
iter->next = ent;
(void) pthread_mutex_unlock(&evthandler_lock);
return (PICL_SUCCESS);
}
/*
* unregister handler
*/
void
ptree_unregister_handler(const char *ename,
void (*evt_handler)(const char *ename, const void *earg, size_t size,
void *cookie), void *cookie)
{
evt_handler_t *evhdlrp, **evhdlrpp;
if (ename == NULL)
return;
/*
* unlink handler from handler list
*/
(void) pthread_mutex_lock(&evthandler_lock);
retry:
for (evhdlrpp = &evt_handlers; (evhdlrp = *evhdlrpp) != NULL;
evhdlrpp = &evhdlrp->next) {
if ((evhdlrp->cookie != cookie) ||
(strcmp(evhdlrp->ename, ename) != 0) ||
(evhdlrp->evt_handler != evt_handler))
continue;
/*
* If the handler is in execution, release the lock
* and wait for it to complete and retry.
*/
if (evhdlrp->execflg) {
evhdlrp->wakeupflg = 1;
(void) pthread_cond_wait(&evhdlrp->cv,
&evthandler_lock);
goto retry;
}
/*
* Unlink this handler from the linked list
*/
*evhdlrpp = evhdlrp->next;
free_handler(evhdlrp);
break;
}
(void) pthread_mutex_unlock(&evthandler_lock);
}
/*
* Call all registered handlers for the event
*/
static void
call_event_handlers(eventq_t *ev)
{
evt_handler_t *iter;
void (*evhandler)(const char *, const void *, size_t, void *);
void (*completion_handler)(char *ename, void *earg, size_t size);
(void) pthread_mutex_lock(&evthandler_lock);
iter = evt_handlers;
while (iter != NULL) {
if (strcmp(iter->ename, ev->ename) == 0) {
evhandler = iter->evt_handler;
iter->execflg = 1;
(void) pthread_mutex_unlock(&evthandler_lock);
if (evhandler) {
dbg_print(2, "ptree_evthr: Invoking evthdlr:%p"
" ename:%s\n", evhandler, ev->ename);
(*evhandler)(ev->ename, ev->earg, ev->size,
iter->cookie);
dbg_print(2, "ptree_evthr: done evthdlr:%p "
"ename:%s\n", evhandler, ev->ename);
}
(void) pthread_mutex_lock(&evthandler_lock);
iter->execflg = 0;
if (iter->wakeupflg) {
iter->wakeupflg = 0;
(void) pthread_cond_broadcast(&iter->cv);
}
}
iter = iter->next;
}
(void) pthread_mutex_unlock(&evthandler_lock);
if ((completion_handler = ev->completion_handler) != NULL) {
dbg_print(2,
"ptree_evthr: Invoking completion hdlr:%p ename:%s\n",
completion_handler, ev->ename);
(*completion_handler)((char *)ev->ename, (void *)ev->earg,
ev->size);
dbg_print(2, "ptree_evthr: done completion hdlr:%p ename:%s\n",
completion_handler, ev->ename);
}
(void) pthread_mutex_lock(&ptree_refresh_mutex);
++ptree_generation;
(void) pthread_cond_broadcast(&ptree_refresh_cond);
(void) pthread_mutex_unlock(&ptree_refresh_mutex);
}
/*
* This function is called by a plug-in to post an event
*/
int
ptree_post_event(const char *ename, const void *earg, size_t size,
void (*completion_handler)(char *ename, void *earg, size_t size))
{
eventq_t *evt;
if (ename == NULL)
return (PICL_INVALIDARG);
evt = malloc(sizeof (*evt));
if (evt == NULL)
return (PICL_FAILURE);
evt->ename = ename;
evt->earg = earg;
evt->size = size;
evt->completion_handler = completion_handler;
(void) pthread_mutex_lock(&evtq_lock);
queue_event(evt);
(void) pthread_cond_broadcast(&evtq_cv);
(void) pthread_mutex_unlock(&evtq_lock);
return (PICL_SUCCESS);
}
/*
* PICLTREE event thread
*/
/*ARGSUSED*/
static void *
ptree_event_thread(void *argp)
{
eventq_t *evt;
for (;;) {
(void) pthread_mutex_lock(&evtq_lock);
while (eventqp == NULL) {
/*
* Signal empty queue
*/
if (qempty_wait)
(void) pthread_cond_broadcast(&evtq_empty);
(void) pthread_cond_wait(&evtq_cv, &evtq_lock);
}
if ((evt = unqueue_event(&eventqp)) != NULL) {
(void) pthread_mutex_unlock(&evtq_lock);
call_event_handlers(evt);
free(evt);
} else
(void) pthread_mutex_unlock(&evtq_lock);
}
/*NOTREACHED*/
return (NULL);
}
/*
* Create a new element
*/
static hash_elem_t *
hash_newobj(uint32_t hdl_val, void *obj_val)
{
hash_elem_t *n;
n = malloc(sizeof (*n));
if (n == NULL)
return (NULL);
n->hdl = hdl_val;
n->hash_obj = obj_val;
n->next = NULL;
return (n);
}
static hash_elem_t *
hash_newhdl(uint32_t picl_hdl, uint32_t ptreeh)
{
hash_elem_t *n;
n = malloc(sizeof (*n));
if (n == NULL)
return (NULL);
n->hdl = picl_hdl;
n->hash_hdl = ptreeh;
n->next = NULL;
return (n);
}
/*
* Initialize a hash table by setting all entries to NULL
*/
static int
hash_init(hash_t *htbl)
{
int i;
htbl->hash_size = HASH_TBL_SIZE;
htbl->tbl = malloc(sizeof (hash_elem_t *) * HASH_TBL_SIZE);
if (htbl->tbl == NULL)
return (-1);
for (i = 0; i < htbl->hash_size; ++i)
htbl->tbl[i] = NULL;
return (0);
}
/*
* Lock free function to add an entry in the hash table
*/
static int
hash_add_newobj(hash_t *htbl, picl_hdl_t hdl, void *pobj)
{
int indx;
hash_elem_t *n;
uint32_t hash_val = HASH_VAL(hdl);
n = hash_newobj(hash_val, pobj);
if (n == NULL)
return (-1);
indx = HASH_INDEX(htbl->hash_size, hash_val);
n->next = htbl->tbl[indx];
htbl->tbl[indx] = n;
return (0);
}
static int
hash_add_newhdl(hash_t *htbl, picl_hdl_t piclh, picl_hdl_t ptreeh)
{
int indx;
hash_elem_t *n;
uint32_t picl_val = HASH_VAL(piclh);
uint32_t ptree_val = HASH_VAL(ptreeh);
n = hash_newhdl(picl_val, ptree_val);
if (n == NULL)
return (-1);
indx = HASH_INDEX(htbl->hash_size, picl_val);
n->next = htbl->tbl[indx];
htbl->tbl[indx] = n;
return (0);
}
/*
* Lock free function to remove the handle from the hash table
* Returns -1 if element not found, 0 if successful
*/
static int
hash_remove(hash_t *htbl, picl_hdl_t hdl)
{
hash_elem_t *nxt;
hash_elem_t *cur;
int i;
uint32_t hash_val = HASH_VAL(hdl);
i = HASH_INDEX(htbl->hash_size, hash_val);
if (htbl->tbl[i] == NULL)
return (-1);
cur = htbl->tbl[i];
if (cur->hdl == hash_val) {
htbl->tbl[i] = cur->next;
free(cur);
return (0);
}
nxt = cur->next;
while (nxt != NULL) {
if (nxt->hdl == hash_val) {
cur->next = nxt->next;
free(nxt);
return (0);
}
cur = nxt;
nxt = nxt->next;
}
return (-1);
}
/*
* Lock free function to lookup the hash table for a given handle
* Returns NULL if not found
*/
static void *
hash_lookup_obj(hash_t *htbl, picl_hdl_t hdl)
{
hash_elem_t *tmp;
int i;
uint32_t hash_val;
hash_val = HASH_VAL(hdl);
i = HASH_INDEX(htbl->hash_size, hash_val);
tmp = htbl->tbl[i];
while (tmp != NULL) {
if (tmp->hdl == hash_val)
return (tmp->hash_obj);
tmp = tmp->next;
}
return (NULL);
}
static picl_hdl_t
hash_lookup_hdl(hash_t *htbl, picl_hdl_t hdl)
{
hash_elem_t *tmp;
int i;
uint32_t hash_val;
hash_val = HASH_VAL(hdl);
i = HASH_INDEX(htbl->hash_size, hash_val);
tmp = htbl->tbl[i];
while (tmp != NULL) {
if (tmp->hdl == hash_val)
return (MAKE_HANDLE(picld_pid, tmp->hash_hdl));
tmp = tmp->next;
}
return (PICL_INVALID_PICLHDL);
}
/*
* Is the PICL handle stale or invalid handle?
*/
static int
picl_hdl_error(picl_hdl_t hdl)
{
uint32_t hash_val = HASH_VAL(hdl);
pid_t pid = GET_PID(hdl);
int err;
(void) pthread_mutex_lock(&piclhdl_lock);
err = PICL_STALEHANDLE;
if ((pid != picld_pid) || (hash_val >= picl_hdl_hi) ||
(hash_val == NULL))
err = PICL_INVALIDHANDLE;
(void) pthread_mutex_unlock(&piclhdl_lock);
return (err);
}
/*
* Is the Ptree handle stale or invalid handle?
*/
static int
ptree_hdl_error(picl_hdl_t hdl)
{
uint32_t hash_val = HASH_VAL(hdl);
pid_t pid = GET_PID(hdl);
int err;
(void) pthread_mutex_lock(&ptreehdl_lock);
err = PICL_STALEHANDLE;
if ((pid != picld_pid) || (hash_val >= ptree_hdl_hi) ||
(hash_val == NULL))
err = PICL_INVALIDHANDLE;
(void) pthread_mutex_unlock(&ptreehdl_lock);
return (err);
}
/*
* For a PICL handle, return the PTree handle and the PICL object
* Locks and releases the PICL table.
*/
int
cvt_picl2ptree(picl_hdl_t hdl, picl_hdl_t *ptree_hdl)
{
picl_hdl_t tmph;
int err;
(void) rw_rdlock(&picltbl_rwlock); /* lock picl */
tmph = hash_lookup_hdl(&picltbl, hdl);
if (tmph == PICL_INVALID_PICLHDL) {
err = picl_hdl_error(hdl);
(void) rw_unlock(&picltbl_rwlock); /* unlock picl */
return (err);
}
*ptree_hdl = tmph;
(void) rw_unlock(&picltbl_rwlock); /* unlock picl */
return (PICL_SUCCESS);
}
/*
* Allocate a ptree handle
*/
static picl_hdl_t
alloc_ptreehdl(void)
{
picl_hdl_t hdl;
(void) pthread_mutex_lock(&ptreehdl_lock); /* lock ptreehdl */
hdl = MAKE_HANDLE(picld_pid, ptree_hdl_hi);
++ptree_hdl_hi;
(void) pthread_mutex_unlock(&ptreehdl_lock); /* unlock ptreehdl */
return (hdl);
}
/*
* Allocate a picl handle
* A PICL handle is ptree_hdl value with 1 in MSB of handle value.
* If a ptree handle already has 1 in MSB, then it cannot be piclized
* and the daemon must be restarted.
*/
static picl_hdl_t
alloc_piclhdl(void)
{
picl_hdl_t hdl;
(void) pthread_mutex_lock(&piclhdl_lock); /* lock piclhdl */
hdl = MAKE_HANDLE(picld_pid, picl_hdl_hi);
++picl_hdl_hi;
(void) pthread_mutex_unlock(&piclhdl_lock); /* unlock piclhdl */
return (hdl);
}
/*
* Allocate and add handle to PTree hash table
*/
static void
alloc_and_add_to_ptree(picl_obj_t *pobj)
{
pobj->ptree_hdl = alloc_ptreehdl();
(void) rw_wrlock(&ptree_rwlock);
(void) hash_add_newobj(&ptreetbl, pobj->ptree_hdl, pobj);
(void) rw_unlock(&ptree_rwlock);
}
/*
* Lock a picl node object
*/
static int
lock_obj(int rw, picl_obj_t *nodep)
{
if (rw == RDLOCK_NODE)
(void) rw_rdlock(&nodep->node_lock);
else if (rw == WRLOCK_NODE)
(void) rw_wrlock(&nodep->node_lock);
else
return (-1);
return (0);
}
/*
* Release the picl node object.
* This function may be called with a NULL object pointer.
*/
static void
unlock_node(picl_obj_t *nodep)
{
if (nodep == NULL)
return;
(void) rw_unlock(&nodep->node_lock);
}
/*
* This function locks the node of a property and returns the node object
* and the property object.
*/
static int
lookup_and_lock_propnode(int rw, picl_prophdl_t proph, picl_obj_t **nodep,
picl_obj_t **propp)
{
picl_obj_t *pobj;
picl_obj_t *nobj;
pobj = hash_lookup_obj(&ptreetbl, proph);
if (pobj == NULL)
return (ptree_hdl_error(proph));
/*
* Get the property's or table entry's node object
*/
nobj = NULL;
if (pobj->obj_type == PICL_OBJ_PROP)
nobj = pobj->prop_node;
else if (pobj->obj_type == (PICL_OBJ_PROP|PICL_OBJ_TABLEENTRY))
nobj = pobj->prop_table->prop_node;
else {
*propp = pobj; /* return the prop */
return (PICL_NOTPROP);
}
if (nobj && (lock_obj(rw, nobj) < 0)) /* Lock node */
return (PICL_FAILURE);
*nodep = nobj;
*propp = pobj;
return (PICL_SUCCESS);
}
/*
* This function locks the node of a table and returns the node object
* and the table object.
*/
static int
lookup_and_lock_tablenode(int rw, picl_prophdl_t tblh, picl_obj_t **nodep,
picl_obj_t **tblobj)
{
picl_obj_t *pobj;
picl_obj_t *nobj;
pobj = hash_lookup_obj(&ptreetbl, tblh);
if (pobj == NULL)
return (ptree_hdl_error(tblh));
/*
* Get the property's or table entry's node object
*/
nobj = NULL;
if (pobj->obj_type != PICL_OBJ_TABLE)
return (PICL_NOTTABLE);
nobj = pobj->prop_node;
if (nobj && (lock_obj(rw, nobj) < 0)) /* Lock node */
return (PICL_FAILURE);
*nodep = nobj;
*tblobj = pobj;
return (PICL_SUCCESS);
}
/*
* This locks the node of a table or a table entry and returns the
* node object and the table or table entry object
*/
static int
lookup_and_lock_tableprop_node(int rw, picl_prophdl_t tblproph,
picl_obj_t **nodep, picl_obj_t **tblpropp)
{
picl_obj_t *pobj;
picl_obj_t *nobj;
pobj = hash_lookup_obj(&ptreetbl, tblproph);
if (pobj == NULL)
return (ptree_hdl_error(tblproph));
/*
* Get the property's or table entry's node object
*/
nobj = NULL;
if ((pobj->obj_type != PICL_OBJ_TABLE) && /* not a table */
!(pobj->obj_type & PICL_OBJ_TABLEENTRY)) /* or an entry */
return (PICL_NOTTABLE);
if (pobj->obj_type == PICL_OBJ_TABLE)
nobj = pobj->prop_node;
else
nobj = pobj->prop_table->prop_node;
if (nobj && (lock_obj(rw, nobj) < 0)) /* Lock node */
return (PICL_FAILURE);
*tblpropp = pobj;
*nodep = nobj;
return (PICL_SUCCESS);
}
/*
* Lock the node corresponding to the given handle and return its object
*/
static int
lookup_and_lock_node(int rw, picl_nodehdl_t nodeh, picl_obj_t **nodep)
{
picl_obj_t *nobj;
nobj = hash_lookup_obj(&ptreetbl, nodeh);
if (nobj == NULL)
return (ptree_hdl_error(nodeh));
else if (nobj->obj_type != PICL_OBJ_NODE)
return (PICL_NOTNODE);
if (lock_obj(rw, nobj) < 0) /* Lock node */
return (PICL_FAILURE);
*nodep = nobj;
return (PICL_SUCCESS);
}
/*
* Is the property name a restricted property name?
*/
static int
picl_restricted(const char *name)
{
if (strcmp(name, PICL_PROP_CLASSNAME) == 0)
return (0); /* not restricted */
if ((name[0] == '_') && (strchr(&name[1], '_') == NULL))
return (1);
return (0);
}
/*
* Check the value size with the property size
* Return PICL_INVALIDARG if the size does not match exactly for strongly
* typed properties.
* For charstring reads allow sizes that match the value size
* For bytearray return PICL_VALUETOOBIG
* if the size is greater than the buffer size.
*/
static int
check_propsize(int op, picl_obj_t *propp, size_t sz)
{
if (propp->prop_mode & PICL_VOLATILE) {
if (sz != propp->prop_size)
return (PICL_INVALIDARG);
else
return (PICL_SUCCESS);
}
/*
* check size for non-volatile properties
*/
switch (propp->prop_type) {
case PICL_PTYPE_CHARSTRING:
if ((op == PROP_READ) &&
(strlen(propp->prop_val) >= sz))
return (PICL_VALUETOOBIG);
if ((op == PROP_WRITE) && (sz > propp->prop_size))
return (PICL_VALUETOOBIG);
break;
case PICL_PTYPE_BYTEARRAY:
if (op == PROP_WRITE) {
if (sz > propp->prop_size)
return (PICL_VALUETOOBIG);
return (PICL_SUCCESS); /* allow small writes */
}
/* fall through for reads */
default:
if (propp->prop_size != sz)
return (PICL_INVALIDARG);
break;
}
return (PICL_SUCCESS);
}
void
cvt_ptree2picl(picl_hdl_t *handlep)
{
picl_obj_t *pobj;
(void) rw_rdlock(&ptree_rwlock);
pobj = hash_lookup_obj(&ptreetbl, *handlep);
if (pobj == NULL)
*handlep = PICL_INVALID_PICLHDL;
else
(void) memcpy(handlep, &pobj->picl_hdl, sizeof (*handlep));
(void) rw_unlock(&ptree_rwlock);
}
/*
* The caller of the piclize() set of functions is assumed to hold
* the ptree_rwlock().
*/
static void
piclize_obj(picl_obj_t *pobj)
{
(void) rw_wrlock(&picltbl_rwlock);
pobj->picl_hdl = alloc_piclhdl();
(void) hash_add_newhdl(&picltbl, pobj->picl_hdl, pobj->ptree_hdl);
(void) rw_unlock(&picltbl_rwlock);
}
static void
piclize_table(picl_obj_t *tbl_obj)
{
picl_obj_t *rowp;
picl_obj_t *colp;
for (rowp = tbl_obj->next_row; rowp != NULL; rowp = rowp->next_col)
for (colp = rowp; colp != NULL; colp = colp->next_row)
piclize_obj(colp);
}
static void
piclize_prop(picl_obj_t *propp)
{
picl_obj_t *tbl_obj;
picl_prophdl_t tblh;
piclize_obj(propp);
if (!(propp->prop_mode & PICL_VOLATILE) &&
(propp->prop_type == PICL_PTYPE_TABLE)) {
tblh = *(picl_prophdl_t *)propp->prop_val;
tbl_obj = hash_lookup_obj(&ptreetbl, tblh);
if (tbl_obj == NULL)
return;
piclize_obj(tbl_obj);
piclize_table(tbl_obj);
}
}
/*
* Function to create PICL handles for a subtree and add them to
* the table
*/
static void
piclize_node(picl_obj_t *nodep)
{
picl_obj_t *propp;
picl_obj_t *chdp;
piclize_obj(nodep);
propp = nodep->first_prop;
while (propp != NULL) {
piclize_prop(propp);
propp = propp->next_prop;
}
/* go through the children */
for (chdp = nodep->child_node; chdp != NULL; chdp = chdp->sibling_node)
piclize_node(chdp);
}
/*
* Function to remove PICL handles
*/
static void
unpiclize_obj(picl_obj_t *pobj)
{
(void) rw_wrlock(&picltbl_rwlock);
(void) hash_remove(&picltbl, pobj->picl_hdl);
pobj->picl_hdl = PICL_INVALID_PICLHDL;
(void) rw_unlock(&picltbl_rwlock);
}
static void
unpiclize_table(picl_obj_t *tbl_obj)
{
picl_obj_t *rowp;
picl_obj_t *colp;
for (rowp = tbl_obj->next_row; rowp != NULL; rowp = rowp->next_col)
for (colp = rowp; colp != NULL; colp = colp->next_row)
unpiclize_obj(colp);
unpiclize_obj(tbl_obj);
}
static void
unpiclize_prop(picl_obj_t *propp)
{
picl_obj_t *tbl_obj;
picl_prophdl_t tblh;
if (!IS_PICLIZED(propp))
return;
unpiclize_obj(propp);
if (!(propp->prop_mode & PICL_VOLATILE) &&
(propp->prop_type == PICL_PTYPE_TABLE)) {
tblh = *(picl_prophdl_t *)propp->prop_val;
tbl_obj = hash_lookup_obj(&ptreetbl, tblh);
unpiclize_table(tbl_obj);
}
}
/*
* Function to remove PICL handles for a subtree and its
* properties
*/
static void
unpiclize_node(picl_obj_t *nodep)
{
picl_obj_t *propp;
picl_obj_t *chdp;
if (!IS_PICLIZED(nodep))
return;
unpiclize_obj(nodep);
propp = nodep->first_prop;
while (propp != NULL) {
unpiclize_prop(propp);
propp = propp->next_prop;
}
/* go through the children */
for (chdp = nodep->child_node; chdp != NULL; chdp = chdp->sibling_node)
unpiclize_node(chdp);
}
/*
* The caller holds the lock on the ptree_lock when calling this.
* If ret is not NULL then this function returns the referenced object.
*/
static int
lookup_verify_ref_prop(picl_obj_t *propp, picl_obj_t **ret)
{
picl_nodehdl_t refh;
picl_obj_t *refobj;
refh = *(picl_nodehdl_t *)propp->prop_val;
refobj = hash_lookup_obj(&ptreetbl, refh);
if (refobj == NULL)
return (ptree_hdl_error(refh));
else if (refobj->obj_type != PICL_OBJ_NODE)
return (PICL_INVREFERENCE);
if (ret)
*ret = refobj;
return (PICL_SUCCESS);
}
/*
* The caller holds the lock on ptree_lock when calling this.
* If ret is not NULL, then this function returns the table object
*/
static int
lookup_verify_table_prop(picl_obj_t *propp, picl_obj_t **ret)
{
picl_prophdl_t tblh;
picl_obj_t *tbl_obj;
tblh = *(picl_prophdl_t *)propp->prop_val;
tbl_obj = hash_lookup_obj(&ptreetbl, tblh);
if (tbl_obj == NULL)
return (ptree_hdl_error(tblh));
else if (!(tbl_obj->obj_type & PICL_OBJ_TABLE))
return (PICL_NOTTABLE);
if (ret)
*ret = tbl_obj;
return (PICL_SUCCESS);
}
static int
lookup_verify_prop_handle(picl_prophdl_t proph, picl_obj_t **ret)
{
picl_obj_t *propp;
propp = hash_lookup_obj(&ptreetbl, proph);
if (propp == NULL)
return (ptree_hdl_error(proph));
else if (!(propp->obj_type & PICL_OBJ_PROP))
return (PICL_NOTPROP);
if (ret)
*ret = propp;
return (PICL_SUCCESS);
}
static int
lookup_verify_node_handle(picl_nodehdl_t nodeh, picl_obj_t **ret)
{
picl_obj_t *nodep;
nodep = hash_lookup_obj(&ptreetbl, nodeh);
if (nodep == NULL)
return (ptree_hdl_error(nodeh));
else if (nodep->obj_type != PICL_OBJ_NODE)
return (PICL_NOTNODE);
if (ret)
*ret = nodep;
return (PICL_SUCCESS);
}
static int
lookup_prop_by_name(picl_obj_t *nodep, const char *pname, picl_obj_t **ret)
{
picl_obj_t *propp;
if (strcmp(pname, PICL_PROP_PARENT) == 0) {
if (nodep->parent_node == NULL)
return (PICL_PROPNOTFOUND);
else
return (PICL_SUCCESS);
}
if (strcmp(pname, PICL_PROP_CHILD) == 0) {
if (nodep->child_node == NULL)
return (PICL_PROPNOTFOUND);
else
return (PICL_SUCCESS);
}
if (strcmp(pname, PICL_PROP_PEER) == 0) {
if (nodep->sibling_node == NULL)
return (PICL_PROPNOTFOUND);
else
return (PICL_SUCCESS);
}
propp = nodep->first_prop;
while (propp != NULL) {
if (strcmp(propp->prop_name, pname) == 0) {
if (ret)
*ret = propp;
return (PICL_SUCCESS);
}
propp = propp->next_prop;
}
return (PICL_PROPNOTFOUND);
}
/*
* This function locks the ptree, verifies that the handle is a reference
* to a node of specified class name, releases the lock
*/
static int
check_ref_handle(picl_nodehdl_t refh, char *clname)
{
picl_obj_t *refobj;
picl_obj_t *propp;
int err;
(void) rw_rdlock(&ptree_rwlock); /* Lock ptree */
refobj = hash_lookup_obj(&ptreetbl, refh);
if ((refobj == NULL) || !(refobj->obj_type & PICL_OBJ_NODE)) {
(void) rw_unlock(&ptree_rwlock);
return (PICL_INVREFERENCE);
}
err = lookup_prop_by_name(refobj, PICL_PROP_CLASSNAME, &propp);
if ((err != PICL_SUCCESS) || (propp->prop_val == NULL) ||
(strcmp(propp->prop_val, clname) != 0))
err = PICL_INVREFERENCE;
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
static int
check_table_handle(picl_prophdl_t tblh)
{
picl_obj_t *tbl_obj;
int err;
(void) rw_rdlock(&ptree_rwlock);
err = PICL_SUCCESS;
tbl_obj = hash_lookup_obj(&ptreetbl, tblh);
if ((tbl_obj == NULL) || !(tbl_obj->obj_type & PICL_OBJ_TABLE))
err = PICL_NOTTABLE;
(void) rw_unlock(&ptree_rwlock);
return (err);
}
/*
* PICLTree Interface routines for plug-in modules
*/
int
ptree_get_root(picl_nodehdl_t *rooth)
{
*rooth = ptree_root_hdl;
return (PICL_SUCCESS);
}
/*
* Lock free create a property object
*/
static int
create_propobj(const ptree_propinfo_t *pinfo, const void *valbuf,
picl_obj_t **pobjp)
{
picl_obj_t *pobj;
if (pinfo->version != PTREE_PROPINFO_VERSION_1)
return (PICL_NOTSUPPORTED);
if (!(pinfo->piclinfo.accessmode & PICL_VOLATILE) &&
(pinfo->piclinfo.type != PICL_PTYPE_VOID) &&
(valbuf == NULL))
return (PICL_INVALIDARG);
pobj = malloc(sizeof (picl_obj_t));
if (pobj == NULL)
return (PICL_FAILURE);
pobj->obj_type = PICL_OBJ_PROP;
pobj->pinfo_ver = pinfo->version;
pobj->prop_type = pinfo->piclinfo.type;
pobj->prop_mode = pinfo->piclinfo.accessmode;
pobj->prop_size = pinfo->piclinfo.size;
(void) strcpy(pobj->prop_name, pinfo->piclinfo.name);
pobj->read_func = pinfo->read;
pobj->write_func = pinfo->write;
pobj->prop_val = NULL;
if (!(pinfo->piclinfo.accessmode & PICL_VOLATILE)) {
pobj->prop_val = malloc(pinfo->piclinfo.size);
if (pobj->prop_val == NULL) {
free(pobj);
return (PICL_FAILURE);
}
if (pobj->prop_type == PICL_PTYPE_CHARSTRING)
(void) strlcpy(pobj->prop_val, valbuf,
pinfo->piclinfo.size);
else
(void) memcpy(pobj->prop_val, valbuf,
pinfo->piclinfo.size);
}
pobj->prop_node = NULL;
pobj->ptree_hdl = PICL_INVALID_PICLHDL;
pobj->picl_hdl = PICL_INVALID_PICLHDL;
pobj->next_prop = NULL;
pobj->next_row = NULL;
pobj->next_col = NULL;
*pobjp = pobj;
return (PICL_SUCCESS);
}
/*
* Check for valid arguments, create a property object,
* Lock ptree_rwlock, add the new property handle, release the lock
* For reference properties and table properties, the handles are verified
* before creating the property.
*/
int
ptree_create_prop(const ptree_propinfo_t *pinfo, const void *valbuf,
picl_prophdl_t *proph)
{
picl_obj_t *pobj;
picl_nodehdl_t refh;
picl_prophdl_t tblh;
int err;
char *ptr;
int refflag;
char classname[PICL_PROPNAMELEN_MAX];
if (pinfo == NULL)
return (PICL_INVALIDARG);
if (pinfo->version != PTREE_PROPINFO_VERSION_1)
return (PICL_NOTSUPPORTED);
if (pinfo->piclinfo.size >= PICL_PROPSIZE_MAX)
return (PICL_VALUETOOBIG);
if (picl_restricted(pinfo->piclinfo.name))
return (PICL_RESERVEDNAME);
refflag = 0;
if ((pinfo->piclinfo.name[0] == '_') &&
(strchr(&pinfo->piclinfo.name[1], '_') != NULL))
refflag = 1;
if (pinfo->piclinfo.type == PICL_PTYPE_REFERENCE) {
if (refflag == 0)
return (PICL_INVREFERENCE);
/*
* check valid reference handle for non-volatiles
*/
if (!(pinfo->piclinfo.accessmode & PICL_VOLATILE)) {
if (valbuf == NULL)
return (PICL_INVREFERENCE);
if (pinfo->piclinfo.size != sizeof (picl_nodehdl_t))
return (PICL_INVREFERENCE);
(void) strcpy(classname, pinfo->piclinfo.name);
ptr = strchr(&classname[1], '_');
*ptr = '\0';
refh = *(picl_hdl_t *)valbuf;
err = check_ref_handle(refh, &classname[1]);
if (err != PICL_SUCCESS)
return (err);
}
} else if (refflag == 1)
return (PICL_INVREFERENCE);
else if ((pinfo->piclinfo.type == PICL_PTYPE_TABLE) &&
(!(pinfo->piclinfo.accessmode & PICL_VOLATILE))) {
if (pinfo->piclinfo.size != sizeof (picl_prophdl_t))
return (PICL_INVALIDARG);
tblh = *(picl_prophdl_t *)valbuf;
err = check_table_handle(tblh);
if (err != PICL_SUCCESS)
return (err);
} else if ((strcmp(pinfo->piclinfo.name, PICL_PROP_CLASSNAME) == 0) &&
((pinfo->piclinfo.type != PICL_PTYPE_CHARSTRING) ||
(strlen(valbuf) >= PICL_CLASSNAMELEN_MAX)))
return (PICL_RESERVEDNAME);
else if ((strcmp(pinfo->piclinfo.name, PICL_PROP_NAME) == 0) &&
(pinfo->piclinfo.type != PICL_PTYPE_CHARSTRING))
return (PICL_RESERVEDNAME);
/*
* No locks held when you get here
*/
err = create_propobj(pinfo, valbuf, &pobj);
if (err != PICL_SUCCESS)
return (err);
alloc_and_add_to_ptree(pobj);
*proph = pobj->ptree_hdl;
return (PICL_SUCCESS);
}
/*
* Lock free routine to destroy table entries
* This function removes the destroyed handles from the hash table
* Uses lock free routines: hash_lookup() and hash_remove()
*/
static void
destroy_table(picl_obj_t *pobj)
{
picl_prophdl_t tblh;
picl_obj_t *tbl_obj;
picl_obj_t *rowp;
picl_obj_t *colp;
picl_obj_t *freep;
tblh = *(picl_prophdl_t *)pobj->prop_val;
tbl_obj = hash_lookup_obj(&ptreetbl, tblh);
if (tbl_obj == NULL)
return;
assert(tbl_obj->obj_type & PICL_OBJ_TABLE);
/* Delete all entries */
rowp = tbl_obj->next_row;
while (rowp != NULL) {
colp = rowp;
rowp = rowp->next_col;
while (colp != NULL) {
freep = colp;
colp = colp->next_row;
(void) hash_remove(&ptreetbl, freep->ptree_hdl);
if (freep->prop_val)
free(freep->prop_val);
free(freep);
}
}
(void) hash_remove(&ptreetbl, tbl_obj->ptree_hdl);
free(tbl_obj);
}
/*
* Lock free function that frees up a property object and removes the
* handles from Ptree table
*/
static void
destroy_propobj(picl_obj_t *propp)
{
if (propp->prop_type == PICL_PTYPE_TABLE)
destroy_table(propp);
(void) hash_remove(&ptreetbl, propp->ptree_hdl);
if (propp->prop_val)
free(propp->prop_val);
free(propp);
}
/*
* This function destroys a previously deleted property.
* A deleted property does not have an associated node.
* All memory allocated for this property are freed
*/
int
ptree_destroy_prop(picl_prophdl_t proph)
{
picl_obj_t *propp;
(void) rw_wrlock(&ptree_rwlock); /* Exclusive Lock ptree */
propp = hash_lookup_obj(&ptreetbl, proph);
if (propp == NULL) {
(void) rw_unlock(&ptree_rwlock); /* Unlock ptree */
return (ptree_hdl_error(proph));
}
/* Is the prop still attached to a node? */
if (propp->prop_node != NULL) {
(void) rw_unlock(&ptree_rwlock); /* Unlock ptree */
return (PICL_CANTDESTROY);
}
destroy_propobj(propp);
(void) rw_unlock(&ptree_rwlock); /* Unlock ptree */
return (PICL_SUCCESS);
}
/*
* This function adds a property to the property list of a node and adds
* it to the PICL table if the node has a PICL handle.
* This function locks the picl_rwlock and ptree_rwlock.
*/
int
ptree_add_prop(picl_nodehdl_t nodeh, picl_prophdl_t proph)
{
int err;
picl_obj_t *nodep;
picl_obj_t *propp;
picl_obj_t *tbl_obj;
picl_obj_t *refobj;
(void) rw_rdlock(&ptree_rwlock); /* RDLock ptree */
/*
* Verify property handle
*/
err = lookup_verify_prop_handle(proph, &propp);
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* Unlock ptree */
return (err);
}
if (propp->prop_node != NULL) {
(void) rw_unlock(&ptree_rwlock);
return (PICL_INVALIDARG);
}
nodep = NULL;
/*
* Exclusive Lock the node's properties
*/
err = lookup_and_lock_node(WRLOCK_NODE, nodeh, &nodep);
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* Unlock ptree */
return (err);
}
/*
* check if prop already exists
*/
err = lookup_prop_by_name(nodep, propp->prop_name, NULL);
if (err == PICL_SUCCESS) {
unlock_node(nodep); /* Unlock node */
(void) rw_unlock(&ptree_rwlock); /* Unlock table */
return (PICL_PROPEXISTS);
}
/*
* Verify property's value
*/
tbl_obj = NULL;
switch (propp->prop_type) {
case PICL_PTYPE_TABLE:
if (propp->prop_mode & PICL_VOLATILE)
break;
err = lookup_verify_table_prop(propp, &tbl_obj);
if (err != PICL_SUCCESS) {
unlock_node(nodep);
(void) rw_unlock(&ptree_rwlock);
return (err);
}
tbl_obj->prop_node = nodep; /* set table's nodep */
tbl_obj->table_prop = propp; /* set table prop */
break;
case PICL_PTYPE_REFERENCE:
if (propp->prop_mode & PICL_VOLATILE)
break;
err = lookup_verify_ref_prop(propp, &refobj);
if (err != PICL_SUCCESS) {
unlock_node(nodep);
(void) rw_unlock(&ptree_rwlock);
return (err);
}
if (IS_PICLIZED(nodep) && !IS_PICLIZED(refobj)) {
unlock_node(nodep);
(void) rw_unlock(&ptree_rwlock);
return (err);
}
break;
default:
break;
}
if (IS_PICLIZED(nodep))
piclize_prop(propp);
/*
* Add prop to beginning of list
*/
propp->prop_node = nodep; /* set prop's nodep */
propp->next_prop = nodep->first_prop;
nodep->first_prop = propp;
unlock_node(nodep); /* Unlock node */
(void) rw_unlock(&ptree_rwlock); /* Unlock table */
return (PICL_SUCCESS);
}
/*
* Lock free function that unlinks a property from its node
*/
static int
unlink_prop(picl_obj_t *nodep, picl_obj_t *propp)
{
picl_obj_t *iterp;
iterp = nodep->first_prop;
if (iterp == propp) { /* first property */
nodep->first_prop = iterp->next_prop;
return (PICL_SUCCESS);
}
while ((iterp != NULL) && (iterp->next_prop != propp))
iterp = iterp->next_prop;
if (iterp == NULL)
return (PICL_PROPNOTFOUND);
iterp->next_prop = propp->next_prop;
return (PICL_SUCCESS);
}
/*
* This function deletes the specified property from the property list
* of its node and removes the handle from PICL table, if the node
* was piclized.
*/
int
ptree_delete_prop(picl_prophdl_t proph)
{
int err;
picl_obj_t *nodep;
picl_obj_t *propp;
(void) rw_rdlock(&ptree_rwlock); /* lock ptree */
/*
* Lookup the property's node and lock it if there is one
* return the objects for the property and the node
*/
nodep = propp = NULL;
err = lookup_and_lock_propnode(WRLOCK_NODE, proph, &nodep, &propp);
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
} else if (nodep == NULL) {
/* Nothing to do - already deleted! */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (PICL_SUCCESS);
}
if (propp->obj_type & PICL_OBJ_TABLEENTRY) {
unlock_node(nodep); /* Unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (PICL_NOTPROP);
}
err = unlink_prop(nodep, propp);
if (err != PICL_SUCCESS) {
unlock_node(nodep); /* Unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
propp->prop_node = NULL; /* reset prop's nodep */
propp->next_prop = NULL;
unpiclize_prop(propp);
unlock_node(nodep); /* Unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (PICL_SUCCESS);
}
/*
* Create a table object and return its handle
*/
int
ptree_create_table(picl_prophdl_t *tblh)
{
picl_obj_t *pobj;
pobj = malloc(sizeof (picl_obj_t));
if (pobj == NULL)
return (PICL_FAILURE);
pobj->obj_type = PICL_OBJ_TABLE;
pobj->prop_val = NULL;
pobj->prop_node = NULL;
pobj->ptree_hdl = PICL_INVALID_PICLHDL;
pobj->picl_hdl = PICL_INVALID_PICLHDL;
pobj->table_prop = NULL;
pobj->next_row = NULL;
pobj->next_col = NULL;
alloc_and_add_to_ptree(pobj);
*tblh = pobj->ptree_hdl;
return (PICL_SUCCESS);
}
/*
* Add the properties in <props> array as a row in the table
* Add PICL handles if the table has a valid PICL handle
*/
int
ptree_add_row_to_table(picl_prophdl_t tblh, int nprops,
const picl_prophdl_t *props)
{
picl_obj_t *tbl_obj;
picl_obj_t *nodep;
picl_obj_t *lastrow;
picl_obj_t **newrow;
int i;
int err;
picl_obj_t *pobj;
int picl_it;
if (nprops < 1)
return (PICL_INVALIDARG);
newrow = malloc(sizeof (picl_obj_t *) * nprops);
if (newrow == NULL)
return (PICL_FAILURE);
(void) rw_rdlock(&ptree_rwlock); /* Lock ptree */
err = lookup_and_lock_tablenode(WRLOCK_NODE, tblh, &nodep, &tbl_obj);
if (err != PICL_SUCCESS) {
free(newrow);
(void) rw_unlock(&ptree_rwlock); /* Unlock table */
return (err);
}
/*
* make sure all are either props or table handles
*/
for (i = 0; i < nprops; ++i) {
pobj = newrow[i] = hash_lookup_obj(&ptreetbl, props[i]);
if (pobj == NULL) { /* no object */
err = ptree_hdl_error(props[i]);
break;
}
if ((!(pobj->obj_type & PICL_OBJ_PROP)) &&
(!(pobj->obj_type & PICL_OBJ_TABLE))) {
err = PICL_NOTPROP;
break;
}
if (IS_PICLIZED(pobj) || (pobj->prop_table != NULL) ||
(pobj->prop_node != NULL)) {
err = PICL_INVALIDARG;
break;
}
}
if (err != PICL_SUCCESS) {
free(newrow);
unlock_node(nodep);
(void) rw_unlock(&ptree_rwlock); /* Unlock table */
return (err);
}
/*
* Mark all props as table entries, set up row linkages
*/
picl_it = 0;
if (IS_PICLIZED(tbl_obj))
picl_it = 1;
for (i = 0; i < nprops; ++i) {
newrow[i]->obj_type |= PICL_OBJ_TABLEENTRY;
newrow[i]->prop_table = tbl_obj;
newrow[i]->next_prop = NULL;
newrow[i]->next_col = NULL;
if (picl_it)
piclize_obj(newrow[i]);
if (i != nprops - 1)
newrow[i]->next_row = newrow[i+1];
}
newrow[nprops - 1]->next_row = NULL;
if (tbl_obj->next_row == NULL) { /* add first row */
tbl_obj->next_row = newrow[0];
tbl_obj->next_col = newrow[0];
} else {
lastrow = tbl_obj->next_row;
while (lastrow->next_col != NULL)
lastrow = lastrow->next_col;
i = 0;
while (lastrow != NULL) {
lastrow->next_col = newrow[i];
lastrow = lastrow->next_row;
++i;
}
}
unlock_node(nodep); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* Unlock ptree */
free(newrow);
return (PICL_SUCCESS);
}
/*
* This function returns the handle of the next property in the row
*/
int
ptree_get_next_by_row(picl_prophdl_t proph, picl_prophdl_t *nextrowh)
{
int err;
picl_obj_t *nodep;
picl_obj_t *propp;
(void) rw_rdlock(&ptree_rwlock); /* lock ptree */
nodep = propp = NULL;
/*
* proph could be a table handle or a table entry handle
* Look it up as a table entry handle first, check error code
* to see if it is a table handle
*/
err = lookup_and_lock_tableprop_node(RDLOCK_NODE, proph, &nodep,
&propp);
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock);
return (err);
}
if (propp->next_row)
*nextrowh = propp->next_row->ptree_hdl;
else
err = PICL_ENDOFLIST;
unlock_node(nodep); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
int
ptree_get_next_by_col(picl_prophdl_t proph, picl_prophdl_t *nextcolh)
{
int err;
picl_obj_t *propp;
picl_obj_t *nodep;
(void) rw_rdlock(&ptree_rwlock); /* lock ptree */
nodep = propp = NULL;
/*
* proph could be a table handle or a table entry handle
* Look it up as a table entry handle first, check error code
* to see if it is a table handle
*/
err = lookup_and_lock_tableprop_node(RDLOCK_NODE, proph, &nodep,
&propp);
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock);
return (err);
}
if (propp->next_col)
*nextcolh = propp->next_col->ptree_hdl;
else
err = PICL_ENDOFLIST;
unlock_node(nodep); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
/*
* This function creates node object and adds its handle to the Ptree
*/
int
ptree_create_node(const char *name, const char *clname, picl_nodehdl_t *nodeh)
{
picl_obj_t *pobj;
ptree_propinfo_t propinfo;
picl_prophdl_t phdl;
picl_prophdl_t cphdl;
int err;
if ((name == NULL) || (*name == '\0') ||
(clname == NULL) || (*clname == '\0'))
return (PICL_INVALIDARG);
if ((strlen(name) >= PICL_PROPNAMELEN_MAX) ||
(strlen(clname) >= PICL_CLASSNAMELEN_MAX))
return (PICL_VALUETOOBIG);
/*
* Create the picl object for node
*/
pobj = malloc(sizeof (picl_obj_t));
if (pobj == NULL)
return (PICL_FAILURE);
pobj->obj_type = PICL_OBJ_NODE;
pobj->first_prop = NULL;
pobj->ptree_hdl = PICL_INVALID_PICLHDL;
pobj->picl_hdl = PICL_INVALID_PICLHDL;
pobj->parent_node = NULL;
pobj->sibling_node = NULL;
pobj->child_node = NULL;
pobj->node_classname = strdup(clname);
if (pobj->node_classname == NULL) {
free(pobj);
return (PICL_FAILURE);
}
(void) rwlock_init(&pobj->node_lock, USYNC_THREAD, NULL);
alloc_and_add_to_ptree(pobj); /* commit the node */
/*
* create name property
*/
propinfo.version = PTREE_PROPINFO_VERSION_1;
propinfo.piclinfo.type = PICL_PTYPE_CHARSTRING;
propinfo.piclinfo.accessmode = PICL_READ;
propinfo.piclinfo.size = strlen(name) + 1;
(void) strcpy(propinfo.piclinfo.name, PICL_PROP_NAME);
propinfo.read = NULL;
propinfo.write = NULL;
err = ptree_create_prop(&propinfo, (const void *)name, &phdl);
if (err != PICL_SUCCESS) {
(void) ptree_destroy_node(pobj->ptree_hdl);
return (err);
}
err = ptree_add_prop(pobj->ptree_hdl, phdl);
if (err != PICL_SUCCESS) {
(void) ptree_destroy_prop(phdl);
(void) ptree_destroy_node(pobj->ptree_hdl);
return (err);
}
/*
* create picl classname property
*/
propinfo.piclinfo.size = strlen(clname) + 1;
(void) strcpy(propinfo.piclinfo.name, PICL_PROP_CLASSNAME);
propinfo.read = NULL;
propinfo.write = NULL;
err = ptree_create_prop(&propinfo, (const void *)clname, &cphdl);
if (err != PICL_SUCCESS) {
(void) ptree_destroy_node(pobj->ptree_hdl);
return (err);
}
err = ptree_add_prop(pobj->ptree_hdl, cphdl);
if (err != PICL_SUCCESS) {
(void) ptree_destroy_prop(cphdl);
(void) ptree_destroy_node(pobj->ptree_hdl);
return (err);
}
*nodeh = pobj->ptree_hdl;
return (PICL_SUCCESS);
}
/*
* Destroy a node/subtree freeing up space
* Removed destroyed objects' handles from PTree table
*/
static void
destroy_subtree(picl_obj_t *nodep)
{
picl_obj_t *iterp;
picl_obj_t *freep;
picl_obj_t *chdp;
if (nodep == NULL)
return;
chdp = nodep->child_node;
while (chdp != NULL) {
freep = chdp;
chdp = chdp->sibling_node;
destroy_subtree(freep);
}
/*
* Lock the node
*/
(void) lock_obj(WRLOCK_NODE, nodep);
/*
* destroy all properties associated with this node
*/
iterp = nodep->first_prop;
while (iterp != NULL) {
freep = iterp;
iterp = iterp->next_prop;
destroy_propobj(freep);
}
(void) hash_remove(&ptreetbl, nodep->ptree_hdl);
(void) rwlock_destroy(&nodep->node_lock);
free(nodep->node_classname);
free(nodep);
}
/*
* This function destroys a previously deleted node/subtree. All the properties
* are freed and removed from the PTree table.
* Only one destroy is in progress at any time.
*/
int
ptree_destroy_node(picl_nodehdl_t nodeh)
{
picl_obj_t *nodep;
picl_obj_t *parp;
picl_obj_t *np;
int err;
(void) rw_wrlock(&ptree_rwlock); /* exclusive wrlock ptree */
nodep = NULL;
err = lookup_verify_node_handle(nodeh, &nodep);
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
/*
* Has this node/subtree been deleted?
*/
if (IS_PICLIZED(nodep)) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (PICL_CANTDESTROY);
}
/*
* update parent's child list to repair the tree when
* parent is not null
*/
parp = nodep->parent_node;
if (parp == NULL) { /* root */
destroy_subtree(nodep);
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (PICL_SUCCESS);
}
np = parp->child_node;
if (np == nodep) { /* first child */
parp->child_node = nodep->sibling_node;
} else {
while ((np != NULL) && (np->sibling_node != nodep))
np = np->sibling_node;
if (np != NULL)
np->sibling_node = nodep->sibling_node;
}
destroy_subtree(nodep);
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (PICL_SUCCESS);
}
/*
* This function deletes a node/subtree from the tree and removes the handles
* from PICL table
*/
int
ptree_delete_node(picl_nodehdl_t nodeh)
{
picl_obj_t *nodep;
picl_obj_t *parp;
picl_obj_t *np;
int err;
(void) rw_wrlock(&ptree_rwlock); /* exclusive wrlock ptree */
nodep = NULL;
err = lookup_verify_node_handle(nodeh, &nodep);
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
/*
* unparent it
*/
parp = nodep->parent_node;
if (parp != NULL) {
np = parp->child_node;
if (np == nodep) /* first child */
parp->child_node = nodep->sibling_node;
else {
while ((np != NULL) && (np->sibling_node != nodep))
np = np->sibling_node;
if (np != NULL)
np->sibling_node = nodep->sibling_node;
}
}
nodep->parent_node = NULL;
nodep->sibling_node = NULL;
unpiclize_node(nodep);
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (PICL_SUCCESS);
}
/*
* This function adds a node as a child of another node
*/
int
ptree_add_node(picl_nodehdl_t parh, picl_nodehdl_t chdh)
{
picl_obj_t *pnodep;
picl_obj_t *cnodep;
picl_obj_t *nodep;
int err;
(void) rw_wrlock(&ptree_rwlock); /* exclusive lock ptree */
pnodep = cnodep = NULL;
err = lookup_verify_node_handle(parh, &pnodep);
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
err = lookup_verify_node_handle(chdh, &cnodep);
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
/* is chdh already a child? */
if (cnodep->parent_node != NULL) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (PICL_CANTPARENT);
}
/*
* append child to children list
*/
cnodep->parent_node = pnodep;
if (pnodep->child_node == NULL)
pnodep->child_node = cnodep;
else {
for (nodep = pnodep->child_node; nodep->sibling_node != NULL;
nodep = nodep->sibling_node)
continue;
nodep->sibling_node = cnodep;
}
/* piclize */
if (IS_PICLIZED(pnodep))
piclize_node(cnodep);
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (PICL_SUCCESS);
}
static void
copy_propinfo_ver_1(ptree_propinfo_t *pinfo, picl_obj_t *propp)
{
pinfo->version = propp->pinfo_ver;
pinfo->piclinfo.type = propp->prop_type;
pinfo->piclinfo.accessmode = propp->prop_mode;
pinfo->piclinfo.size = propp->prop_size;
(void) strcpy(pinfo->piclinfo.name, propp->prop_name);
pinfo->read = propp->read_func;
pinfo->write = propp->write_func;
}
static void
copy_reserved_propinfo_ver_1(ptree_propinfo_t *pinfo, const char *pname)
{
pinfo->version = PTREE_PROPINFO_VERSION_1;
pinfo->piclinfo.type = PICL_PTYPE_REFERENCE;
pinfo->piclinfo.accessmode = PICL_READ;
pinfo->piclinfo.size = sizeof (picl_nodehdl_t);
(void) strcpy(pinfo->piclinfo.name, pname);
pinfo->read = NULL;
pinfo->write = NULL;
}
/*
* This function returns the property information to a plug-in
*/
int
ptree_get_propinfo(picl_prophdl_t proph, ptree_propinfo_t *pinfo)
{
int err;
picl_obj_t *nodep;
picl_obj_t *propp;
(void) rw_rdlock(&ptree_rwlock); /* lock ptree */
nodep = propp = NULL;
err = lookup_and_lock_propnode(RDLOCK_NODE, proph, &nodep, &propp);
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
if (propp->pinfo_ver == PTREE_PROPINFO_VERSION_1)
copy_propinfo_ver_1(pinfo, propp);
else
err = PICL_FAILURE;
unlock_node(nodep); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
/*
* This function returns the property information to a plug-in
*/
int
xptree_get_propinfo_by_name(picl_nodehdl_t nodeh, const char *pname,
ptree_propinfo_t *pinfo)
{
int err;
picl_obj_t *nodep;
picl_obj_t *propp;
(void) rw_rdlock(&ptree_rwlock); /* lock ptree */
nodep = propp = NULL;
err = lookup_and_lock_node(RDLOCK_NODE, nodeh, &nodep); /* lock node */
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
err = lookup_prop_by_name(nodep, pname, &propp);
if (err != PICL_SUCCESS) {
unlock_node(nodep);
(void) rw_unlock(&ptree_rwlock);
return (err);
}
if (picl_restricted(pname))
copy_reserved_propinfo_ver_1(pinfo, pname);
else if (propp->pinfo_ver == PTREE_PROPINFO_VERSION_1)
copy_propinfo_ver_1(pinfo, propp);
else
err = PICL_FAILURE;
unlock_node(nodep); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
/*
* This function must be called only after a lookup_prop_by_name() returns
* success and only if picl_restricted() returns true.
*/
static int
read_reserved_propval_and_unlock(picl_obj_t *nodep, const char *pname,
void *vbuf, size_t size)
{
void *srcp;
if (size != sizeof (picl_nodehdl_t))
return (PICL_VALUETOOBIG);
if (strcmp(pname, PICL_PROP_PARENT) == 0)
srcp = &nodep->parent_node->ptree_hdl;
else if (strcmp(pname, PICL_PROP_CHILD) == 0)
srcp = &nodep->child_node->ptree_hdl;
else if (strcmp(pname, PICL_PROP_PEER) == 0)
srcp = &nodep->sibling_node->ptree_hdl;
else
return (PICL_FAILURE);
(void) memcpy(vbuf, srcp, sizeof (picl_nodehdl_t));
unlock_node(nodep);
(void) rw_unlock(&ptree_rwlock);
return (PICL_SUCCESS);
}
/*
* Returns the property value in the buffer and releases the node and
* ptree locks.
* For volatile properties, this function releases the locks on ptree
* table and the node before calling the plug-in provided access function
*/
static int
read_propval_and_unlock(picl_obj_t *nodep, picl_obj_t *propp, void *vbuf,
door_cred_t cred)
{
int err;
int (*volrd)(ptree_rarg_t *arg, void *buf);
err = PICL_SUCCESS;
if (propp->prop_mode & PICL_VOLATILE) {
ptree_rarg_t rarg;
if (nodep)
rarg.nodeh = nodep->ptree_hdl;
else
rarg.nodeh = PICL_INVALID_PICLHDL;
rarg.proph = propp->ptree_hdl;
rarg.cred = cred;
volrd = propp->read_func;
unlock_node(nodep); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
if (volrd == NULL)
err = PICL_FAILURE;
else
err = (volrd)(&rarg, vbuf);
return (err);
} else if (propp->prop_type == PICL_PTYPE_CHARSTRING)
(void) strlcpy(vbuf, propp->prop_val, propp->prop_size);
else
(void) memcpy(vbuf, propp->prop_val, propp->prop_size);
unlock_node(nodep);
(void) rw_unlock(&ptree_rwlock);
return (err);
}
int
xptree_get_propval_with_cred(picl_prophdl_t proph, void *vbuf, size_t size,
door_cred_t cred)
{
picl_obj_t *propp;
picl_obj_t *nodep;
int err;
(void) rw_rdlock(&ptree_rwlock); /* lock ptree */
nodep = propp = NULL;
err = lookup_and_lock_propnode(RDLOCK_NODE, proph, &nodep, &propp);
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
err = check_propsize(PROP_READ, propp, size);
if (err != PICL_SUCCESS) {
unlock_node(nodep); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
return (read_propval_and_unlock(nodep, propp, vbuf, cred));
}
/*
* This function gets the credentials and calls get_propval_with_cred.
*/
int
ptree_get_propval(picl_prophdl_t proph, void *vbuf, size_t size)
{
return (xptree_get_propval_with_cred(proph, vbuf, size, picld_cred));
}
/*
* This function retrieves a property's value by by its name
* For volatile properties, the locks on ptree and node are released
* before calling the plug-in provided access function
*/
int
xptree_get_propval_by_name_with_cred(picl_nodehdl_t nodeh, const char *pname,
void *vbuf, size_t size, door_cred_t cred)
{
picl_obj_t *nodep;
picl_obj_t *propp;
int err;
(void) rw_rdlock(&ptree_rwlock); /* lock ptree */
nodep = NULL;
err = lookup_and_lock_node(RDLOCK_NODE, nodeh, &nodep); /* lock node */
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
err = lookup_prop_by_name(nodep, pname, &propp);
if (err != PICL_SUCCESS) {
unlock_node(nodep);
(void) rw_unlock(&ptree_rwlock);
return (err);
}
if (picl_restricted(pname))
return (read_reserved_propval_and_unlock(nodep, pname, vbuf,
size));
err = check_propsize(PROP_READ, propp, size);
if (err != PICL_SUCCESS) {
unlock_node(nodep);
(void) rw_unlock(&ptree_rwlock);
return (err);
}
return (read_propval_and_unlock(nodep, propp, vbuf, cred));
}
/*
* This function is used by plugins to get a value of a property
* looking it up by its name.
*/
int
ptree_get_propval_by_name(picl_nodehdl_t nodeh, const char *pname, void *vbuf,
size_t size)
{
return (xptree_get_propval_by_name_with_cred(nodeh, pname, vbuf, size,
picld_cred));
}
/*
* This function updates a property's value.
* For volatile properties, the locks on the node and the ptree table
* are released before calling the plug-in provided access function.
*/
static int
write_propval_and_unlock(picl_obj_t *nodep, picl_obj_t *propp, const void *vbuf,
size_t size, door_cred_t cred)
{
int err;
int (*volwr)(ptree_warg_t *arg, const void *buf);
err = PICL_SUCCESS;
if (propp->prop_mode & PICL_VOLATILE) {
ptree_warg_t warg;
if (nodep)
warg.nodeh = nodep->ptree_hdl;
else
warg.nodeh = PICL_INVALID_PICLHDL;
warg.proph = propp->ptree_hdl;
warg.cred = cred;
volwr = propp->write_func;
unlock_node(nodep); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
if (volwr == NULL)
err = PICL_FAILURE;
else
err = (volwr)(&warg, vbuf);
return (err);
} else
(void) memcpy(propp->prop_val, vbuf, size);
unlock_node(nodep); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
int
xptree_update_propval_with_cred(picl_prophdl_t proph, const void *vbuf,
size_t size, door_cred_t cred)
{
picl_obj_t *nodep;
picl_obj_t *propp;
int err;
(void) rw_rdlock(&ptree_rwlock); /* lock ptree */
nodep = propp = NULL;
err = lookup_and_lock_propnode(WRLOCK_NODE, proph, &nodep, &propp);
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
err = check_propsize(PROP_WRITE, propp, size);
if (err != PICL_SUCCESS) {
unlock_node(nodep); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
return (write_propval_and_unlock(nodep, propp, vbuf, size, cred));
}
/*
* Ptree function used by plug-ins to update a property's value
* calls update_propval_with_cred(), which releases locks for volatile props
*/
int
ptree_update_propval(picl_prophdl_t proph, const void *vbuf, size_t size)
{
return (xptree_update_propval_with_cred(proph, vbuf, size, picld_cred));
}
/*
* This function writes/updates a property's value by looking it up
* by its name.
* For volatile properties this function releases the locks on the
* node and the ptree table.
*/
int
xptree_update_propval_by_name_with_cred(picl_nodehdl_t nodeh, const char *pname,
const void *vbuf, size_t size, door_cred_t cred)
{
picl_obj_t *nodep;
picl_obj_t *propp;
int err;
(void) rw_rdlock(&ptree_rwlock); /* lock ptree */
nodep = NULL;
err = lookup_and_lock_node(WRLOCK_NODE, nodeh, &nodep); /* lock node */
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
if (picl_restricted(pname)) {
unlock_node(nodep);
(void) rw_unlock(&ptree_rwlock);
return (PICL_RESERVEDNAME);
}
err = lookup_prop_by_name(nodep, pname, &propp);
if (err != PICL_SUCCESS) {
unlock_node(nodep);
(void) rw_unlock(&ptree_rwlock);
return (err);
}
err = check_propsize(PROP_WRITE, propp, size);
if (err != PICL_SUCCESS) {
unlock_node(nodep);
(void) rw_unlock(&ptree_rwlock);
return (err);
}
return (write_propval_and_unlock(nodep, propp, vbuf, size, cred));
}
/*
* This function updates the value of a property specified by its name
*/
int
ptree_update_propval_by_name(picl_nodehdl_t nodeh, const char *pname,
const void *vbuf, size_t size)
{
return (xptree_update_propval_by_name_with_cred(nodeh, pname, vbuf,
size, picld_cred));
}
/*
* This function retrieves the handle of a property by its name
*/
int
ptree_get_prop_by_name(picl_nodehdl_t nodeh, const char *pname,
picl_prophdl_t *proph)
{
picl_obj_t *nodep;
picl_obj_t *propp;
int err;
(void) rw_rdlock(&ptree_rwlock); /* lock ptree */
nodep = NULL;
err = lookup_and_lock_node(RDLOCK_NODE, nodeh, &nodep); /* lock node */
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
if (picl_restricted(pname)) {
err = PICL_RESERVEDNAME;
unlock_node(nodep); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
err = lookup_prop_by_name(nodep, pname, &propp);
if (err == PICL_SUCCESS)
*proph = propp->ptree_hdl;
unlock_node(nodep); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
/*
* This function returns the handle of the first property
*/
int
ptree_get_first_prop(picl_nodehdl_t nodeh, picl_prophdl_t *proph)
{
picl_obj_t *pobj;
int err;
(void) rw_rdlock(&ptree_rwlock); /* lock ptree */
pobj = NULL;
err = lookup_and_lock_node(RDLOCK_NODE, nodeh, &pobj); /* lock node */
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
if (pobj->first_prop)
*proph = pobj->first_prop->ptree_hdl;
else
err = PICL_ENDOFLIST;
unlock_node(pobj); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
/*
* This function returns the handle of next property in the list
*/
int
ptree_get_next_prop(picl_prophdl_t proph, picl_prophdl_t *nextproph)
{
picl_obj_t *nodep;
picl_obj_t *propp;
int err;
(void) rw_rdlock(&ptree_rwlock); /* lock ptree */
nodep = propp = NULL;
err = lookup_and_lock_propnode(RDLOCK_NODE, proph, &nodep, &propp);
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
if (propp->next_prop) {
*nextproph = propp->next_prop->ptree_hdl;
} else
err = PICL_ENDOFLIST;
unlock_node(nodep); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (err);
}
/*
* These functions are called by ptree_get_node_by_path()
* Append a prop expression entry to the list
*/
static prop_list_t *
append_entry_to_list(prop_list_t *el, prop_list_t *list)
{
prop_list_t *ptr;
if (el == NULL)
return (list);
if (list == NULL) {
list = el;
return (list);
}
/*
* Add it to the end of list
*/
ptr = list;
while (ptr->next != NULL)
ptr = ptr->next;
ptr->next = el;
return (list);
}
/*
* Free the property expression list
*/
static void
free_list(prop_list_t *list)
{
prop_list_t *ptr;
prop_list_t *tmp;
for (ptr = list; ptr != NULL; ptr = tmp) {
tmp = ptr->next;
free(ptr);
}
}
static int
parse_prl(char *prl, char **name, char **baddr, prop_list_t **plist)
{
char *propptr;
char *ptr;
char *pname;
char *pval;
prop_list_t *el;
if (prl == NULL)
return (PICL_FAILURE);
if ((prl[0] == '@') || (prl[0] == '?'))
return (PICL_FAILURE);
*name = prl;
/*
* get property expression
*/
ptr = strchr(prl, '?');
if (ptr != NULL) {
*ptr = '\0';
propptr = ptr + 1;
} else
propptr = NULL;
/*
* get bus value
*/
ptr = strchr(prl, '@');
if (ptr != NULL) {
*ptr = '\0';
*baddr = ptr + 1;
if (strlen(*baddr) == 0) /* no bus value after @ */
return (PICL_FAILURE);
}
/*
* create the prop list
*/
while (propptr != NULL) {
pname = propptr;
pval = NULL;
ptr = strchr(propptr, '?');
if (ptr != NULL) { /* more ?<prop>=<propval> */
*ptr = '\0';
propptr = ptr + 1;
} else
propptr = NULL;
if (strlen(pname) == 0) /* no prop exp after ? */
return (PICL_FAILURE);
ptr = strchr(pname, '=');
if (ptr != NULL) { /* not void prop */
*ptr = '\0';
pval = ptr + 1;
/*
* <prop>= is treated as void property
*/
if (strlen(pval) == 0)
pval = NULL;
}
el = (prop_list_t *)malloc(sizeof (prop_list_t));
el->pname = pname;
el->pval = pval;
el->next = NULL;
*plist = append_entry_to_list(el, *plist);
}
return (PICL_SUCCESS);
}
static int
prop_match(ptree_propinfo_t pinfo, void *vbuf, char *val)
{
int8_t cval;
uint8_t ucval;
int16_t sval;
uint16_t usval;
int32_t intval;
uint32_t uintval;
int64_t llval;
uint64_t ullval;
float fval;
double dval;
switch (pinfo.piclinfo.type) {
case PICL_PTYPE_CHARSTRING:
if (strcasecmp(pinfo.piclinfo.name, PICL_PROP_CLASSNAME) == 0) {
if (strcmp(val, PICL_CLASS_PICL) == 0)
return (1);
}
if (strcmp(val, (char *)vbuf) == 0)
return (1);
else
return (0);
case PICL_PTYPE_INT:
switch (pinfo.piclinfo.size) {
case sizeof (int8_t):
cval = (int8_t)strtol(val, (char **)NULL, 0);
return (cval == *(char *)vbuf);
case sizeof (int16_t):
sval = (int16_t)strtol(val, (char **)NULL, 0);
return (sval == *(int16_t *)vbuf);
case sizeof (int32_t):
intval = (int32_t)strtol(val, (char **)NULL, 0);
return (intval == *(int32_t *)vbuf);
case sizeof (int64_t):
llval = strtoll(val, (char **)NULL, 0);
return (llval == *(int64_t *)vbuf);
default:
return (0);
}
case PICL_PTYPE_UNSIGNED_INT:
switch (pinfo.piclinfo.size) {
case sizeof (uint8_t):
ucval = (uint8_t)strtoul(val, (char **)NULL, 0);
return (ucval == *(uint8_t *)vbuf);
case sizeof (uint16_t):
usval = (uint16_t)strtoul(val, (char **)NULL, 0);
return (usval == *(uint16_t *)vbuf);
case sizeof (uint32_t):
uintval = (uint32_t)strtoul(val, (char **)NULL, 0);
return (uintval == *(uint32_t *)vbuf);
case sizeof (uint64_t):
ullval = strtoull(val, (char **)NULL, 0);
return (ullval == *(uint64_t *)vbuf);
default:
return (0);
}
case PICL_PTYPE_FLOAT:
switch (pinfo.piclinfo.size) {
case sizeof (float):
fval = (float)strtod(val, (char **)NULL);
return (fval == *(float *)vbuf);
case sizeof (double):
dval = strtod(val, (char **)NULL);
return (dval == *(double *)vbuf);
default:
return (0);
}
case PICL_PTYPE_VOID:
case PICL_PTYPE_TIMESTAMP:
case PICL_PTYPE_TABLE:
case PICL_PTYPE_REFERENCE:
case PICL_PTYPE_BYTEARRAY:
case PICL_PTYPE_UNKNOWN:
default:
return (0);
}
}
static int
check_propval(picl_nodehdl_t nodeh, char *pname, char *pval)
{
int err;
picl_prophdl_t proph;
ptree_propinfo_t pinfo;
void *vbuf;
err = ptree_get_prop_by_name(nodeh, pname, &proph);
if (err != PICL_SUCCESS)
return (err);
err = ptree_get_propinfo(proph, &pinfo);
if (err != PICL_SUCCESS)
return (err);
if (pval == NULL) { /* void type */
if (pinfo.piclinfo.type != PICL_PTYPE_VOID)
return (PICL_FAILURE);
} else {
vbuf = alloca(pinfo.piclinfo.size);
if (vbuf == NULL)
return (PICL_FAILURE);
err = ptree_get_propval(proph, vbuf,
pinfo.piclinfo.size);
if (err != PICL_SUCCESS)
return (err);
if (!prop_match(pinfo, vbuf, pval))
return (PICL_FAILURE);
}
return (PICL_SUCCESS);
}
static int
get_child_by_path(picl_nodehdl_t rooth, char *prl,
picl_nodehdl_t *nodeh, char *pname)
{
picl_nodehdl_t chdh;
int err;
char *nameval;
char *nodename;
char *path;
char *baddr;
char *busval;
prop_list_t *plist;
prop_list_t *ptr;
if (prl == NULL)
return (PICL_FAILURE);
path = alloca(strlen(prl) + 1);
if (path == NULL)
return (PICL_FAILURE);
(void) strcpy(path, prl);
plist = NULL;
nodename = NULL;
baddr = NULL;
err = parse_prl(path, &nodename, &baddr, &plist);
if (err != PICL_SUCCESS) {
free_list(plist);
return (err);
}
if (nodename == NULL)
return (PICL_FAILURE);
nameval = alloca(strlen(nodename) + 1);
if (nameval == NULL) {
free_list(plist);
return (PICL_FAILURE);
}
if (baddr != NULL) {
busval = alloca(strlen(baddr) + 1);
if (busval == NULL) {
free_list(plist);
return (PICL_FAILURE);
}
}
for (err = ptree_get_propval_by_name(rooth, PICL_PROP_CHILD, &chdh,
sizeof (picl_nodehdl_t)); err != PICL_PROPNOTFOUND;
err = ptree_get_propval_by_name(chdh, PICL_PROP_PEER, &chdh,
sizeof (picl_nodehdl_t))) {
if (err != PICL_SUCCESS) {
free_list(plist);
return (PICL_FAILURE);
}
/*
* compare name
*/
if ((strcmp(pname, PICL_PROP_CLASSNAME) != 0) ||
(strcmp(nodename, PICL_CLASS_PICL) != 0)) {
err = ptree_get_propval_by_name(chdh, pname,
nameval, (strlen(nodename) + 1));
if (err != PICL_SUCCESS)
continue;
if (strcmp(nameval, nodename) != 0)
continue;
}
/*
* compare device address with bus-addr prop first
* then with UnitAddress property
*/
if (baddr != NULL) { /* compare bus-addr prop */
if ((ptree_get_propval_by_name(chdh, PICL_PROP_BUS_ADDR,
busval, (strlen(baddr) + 1)) != PICL_SUCCESS) &&
(ptree_get_propval_by_name(chdh,
PICL_PROP_UNIT_ADDRESS, busval,
(strlen(baddr) + 1)) != PICL_SUCCESS))
continue;
if (strcmp(busval, baddr) != 0)
continue; /* not match */
}
if (plist == NULL) { /* no prop expression */
*nodeh = chdh;
return (PICL_SUCCESS);
}
/*
* compare the property expression list
*/
ptr = plist;
while (ptr != NULL) {
err = check_propval(chdh, ptr->pname, ptr->pval);
if (err != PICL_SUCCESS)
break;
ptr = ptr->next;
}
if (ptr == NULL) {
*nodeh = chdh;
free_list(plist);
return (PICL_SUCCESS);
}
}
free_list(plist);
return (PICL_NOTNODE);
}
/*
* This functions returns the handle of node specified by its path
*/
int
ptree_get_node_by_path(const char *piclprl, picl_nodehdl_t *handle)
{
picl_nodehdl_t rooth;
picl_nodehdl_t chdh;
char *path;
char *ptr;
char *defprop;
char *tokindex;
int err;
int len;
int npflg; /* namepath flag */
path = alloca(strlen(piclprl) + 1);
if (path == NULL)
return (PICL_FAILURE);
(void) strcpy(path, piclprl);
npflg = 1; /* default */
defprop = path;
if (path[0] == '/') {
ptr = &path[1];
} else if ((tokindex = strchr(path, ':')) != NULL) {
*tokindex = '\0';
++tokindex;
if (*tokindex == '/')
ptr = tokindex + 1;
else
return (PICL_NOTNODE);
npflg = 0;
} else
return (PICL_NOTNODE);
err = ptree_get_root(&rooth);
if (err != PICL_SUCCESS)
return (err);
for (chdh = rooth, tokindex = strchr(ptr, '/');
tokindex != NULL;
ptr = tokindex + 1, tokindex = strchr(ptr, '/')) {
*tokindex = '\0';
if (npflg)
err = get_child_by_path(chdh, ptr, &chdh,
PICL_PROP_NAME);
else
err = get_child_by_path(chdh, ptr, &chdh,
defprop);
if (err != PICL_SUCCESS)
return (err);
}
/*
* check if last token is empty or not
* eg. /a/b/c/ or /a/b/c
*/
if (*ptr == '\0') {
*handle = chdh;
return (PICL_SUCCESS);
}
len = strcspn(ptr, " \t\n");
if (len == 0) {
*handle = chdh;
return (PICL_SUCCESS);
}
ptr[len] = '\0';
if (npflg)
err = get_child_by_path(chdh, ptr, &chdh, PICL_PROP_NAME);
else
err = get_child_by_path(chdh, ptr, &chdh, defprop);
if (err != PICL_SUCCESS)
return (err);
*handle = chdh;
return (PICL_SUCCESS);
}
/*
* Initialize propinfo
*/
int
ptree_init_propinfo(ptree_propinfo_t *infop, int version, int ptype, int pmode,
size_t psize, char *pname, int (*readfn)(ptree_rarg_t *, void *),
int (*writefn)(ptree_warg_t *, const void *))
{
if (version != PTREE_PROPINFO_VERSION_1)
return (PICL_NOTSUPPORTED);
if ((infop == NULL) || (pname == NULL))
return (PICL_INVALIDARG);
infop->version = version;
infop->piclinfo.type = ptype;
infop->piclinfo.accessmode = pmode;
infop->piclinfo.size = psize;
infop->read = readfn;
infop->write = writefn;
(void) strlcpy(infop->piclinfo.name, pname, PICL_PROPNAMELEN_MAX);
return (PICL_SUCCESS);
}
/*
* Creates a property, adds it to the node, and returns the property
* handle to the caller if successful and proph is not NULL
*/
int
ptree_create_and_add_prop(picl_nodehdl_t nodeh, ptree_propinfo_t *infop,
void *vbuf, picl_prophdl_t *proph)
{
int err;
picl_prophdl_t tmph;
err = ptree_create_prop(infop, vbuf, &tmph);
if (err != PICL_SUCCESS)
return (err);
err = ptree_add_prop(nodeh, tmph);
if (err != PICL_SUCCESS) {
(void) ptree_destroy_prop(tmph);
return (err);
}
if (proph)
*proph = tmph;
return (PICL_SUCCESS);
}
/*
* Creates a node, adds it to its parent node, and returns the node
* handle to the caller if successful
*/
int
ptree_create_and_add_node(picl_nodehdl_t rooth, const char *name,
const char *classname, picl_nodehdl_t *nodeh)
{
picl_nodehdl_t tmph;
int err;
err = ptree_create_node(name, classname, &tmph);
if (err != PICL_SUCCESS)
return (err);
err = ptree_add_node(rooth, tmph);
if (err != PICL_SUCCESS) {
(void) ptree_destroy_node(tmph);
return (err);
}
*nodeh = tmph;
return (PICL_SUCCESS);
}
/*
* recursively visit all nodes
*/
static int
do_walk(picl_nodehdl_t rooth, const char *classname,
void *c_args, int (*callback_fn)(picl_nodehdl_t hdl, void *args))
{
int err;
picl_nodehdl_t chdh;
char classval[PICL_CLASSNAMELEN_MAX];
err = ptree_get_propval_by_name(rooth, PICL_PROP_CHILD, &chdh,
sizeof (chdh));
while (err == PICL_SUCCESS) {
err = ptree_get_propval_by_name(chdh, PICL_PROP_CLASSNAME,
classval, sizeof (classval));
if (err != PICL_SUCCESS)
return (err);
if ((classname == NULL) || (strcmp(classname, classval) == 0)) {
err = callback_fn(chdh, c_args);
if (err != PICL_WALK_CONTINUE)
return (err);
}
if ((err = do_walk(chdh, classname, c_args, callback_fn)) !=
PICL_WALK_CONTINUE)
return (err);
err = ptree_get_propval_by_name(chdh, PICL_PROP_PEER, &chdh,
sizeof (chdh));
}
if (err == PICL_PROPNOTFOUND) /* end of a branch */
return (PICL_WALK_CONTINUE);
return (err);
}
/*
* This function visits all the nodes in the subtree rooted at <rooth>.
* For each node that matches the class name specified, the callback
* function is invoked.
*/
int
ptree_walk_tree_by_class(picl_nodehdl_t rooth, const char *classname,
void *c_args, int (*callback_fn)(picl_nodehdl_t hdl, void *args))
{
int err;
if (callback_fn == NULL)
return (PICL_INVALIDARG);
err = do_walk(rooth, classname, c_args, callback_fn);
if ((err == PICL_WALK_CONTINUE) || (err == PICL_WALK_TERMINATE))
return (PICL_SUCCESS);
return (err);
}
static int
compare_propval(picl_nodehdl_t nodeh, char *pname, picl_prop_type_t ptype,
void *pval, size_t valsize)
{
int err;
picl_prophdl_t proph;
ptree_propinfo_t propinfo;
void *vbuf;
err = ptree_get_prop_by_name(nodeh, pname, &proph);
if (err != PICL_SUCCESS)
return (0);
err = ptree_get_propinfo(proph, &propinfo);
if (err != PICL_SUCCESS)
return (0);
if (propinfo.piclinfo.type != ptype)
return (0);
if (propinfo.piclinfo.type == PICL_PTYPE_VOID)
return (1);
if (pval == NULL)
return (0);
if (valsize > propinfo.piclinfo.size)
return (0);
vbuf = alloca(propinfo.piclinfo.size);
if (vbuf == NULL)
return (0);
err = ptree_get_propval(proph, vbuf, propinfo.piclinfo.size);
if (err != PICL_SUCCESS)
return (0);
if (memcmp(vbuf, pval, valsize) == 0)
return (1);
return (0);
}
/*
* This function traverses the subtree and finds a node that has a property
* of the specified name and type with the specified value.
* The matched node in the tree is returned in retnodeh. If there is
* no node with that property, then PICL_NODENOTFOUND is returned.
*/
int
ptree_find_node(picl_nodehdl_t rooth, char *pname, picl_prop_type_t ptype,
void *pval, size_t valsize, picl_nodehdl_t *retnodeh)
{
int err;
picl_nodehdl_t chdh;
if (pname == NULL)
return (PICL_INVALIDARG);
err = ptree_get_propval_by_name(rooth, PICL_PROP_CHILD, &chdh,
sizeof (chdh));
while (err == PICL_SUCCESS) {
if (compare_propval(chdh, pname, ptype, pval, valsize)) {
if (retnodeh)
*retnodeh = chdh;
return (PICL_SUCCESS);
}
err = ptree_find_node(chdh, pname, ptype, pval, valsize,
retnodeh);
if (err != PICL_NODENOTFOUND)
return (err);
err = ptree_get_propval_by_name(chdh, PICL_PROP_PEER, &chdh,
sizeof (chdh));
}
if (err == PICL_PROPNOTFOUND)
return (PICL_NODENOTFOUND);
return (err);
}
/*
* This function gets the frutree parent for a given node.
* Traverse up the tree and look for the following properties:
* Frutree parent reference properties:
* _fru_parent
* _location_parent
* _port_parent
* If the frutree reference property is found, return its value.
* Else, return the handle of /frutree/chassis.
*/
int
ptree_get_frutree_parent(picl_nodehdl_t nodeh, picl_nodehdl_t *fruh)
{
int err;
picl_nodehdl_t nparh;
picl_nodehdl_t fruparh;
err = PICL_SUCCESS;
nparh = nodeh;
while (err == PICL_SUCCESS) {
err = ptree_get_propval_by_name(nparh, PICL_REFPROP_FRU_PARENT,
&fruparh, sizeof (fruparh));
if (err == PICL_SUCCESS) {
*fruh = fruparh;
return (PICL_SUCCESS);
}
err = ptree_get_propval_by_name(nparh,
PICL_REFPROP_LOC_PARENT, &fruparh, sizeof (fruparh));
if (err == PICL_SUCCESS) {
*fruh = fruparh;
return (PICL_SUCCESS);
}
err = ptree_get_propval_by_name(nparh, PICL_REFPROP_PORT_PARENT,
&fruparh, sizeof (fruparh));
if (err == PICL_SUCCESS) {
*fruh = fruparh;
return (PICL_SUCCESS);
}
err = ptree_get_propval_by_name(nparh, PICL_PROP_PARENT, &nparh,
sizeof (nparh));
}
if (err == PICL_PROPNOTFOUND) { /* return /frutree/chassis handle */
err = ptree_get_node_by_path(PICL_FRUTREE_CHASSIS, &fruparh);
if (err == PICL_SUCCESS) {
*fruh = fruparh;
return (PICL_SUCCESS);
}
}
return (err);
}
/*
* This function is called by plug-ins to register with the daemon
*/
int
picld_plugin_register(picld_plugin_reg_t *regp)
{
picld_plugin_reg_list_t *el;
picld_plugin_reg_list_t *tmp;
if (regp == NULL)
return (PICL_FAILURE);
if (regp->version != PICLD_PLUGIN_VERSION_1)
return (PICL_NOTSUPPORTED);
el = malloc(sizeof (picld_plugin_reg_list_t));
if (el == NULL)
return (PICL_FAILURE);
el->reg.version = regp->version;
el->reg.critical = regp->critical;
if (regp->name)
el->reg.name = strdup(regp->name);
if (el->reg.name == NULL)
return (PICL_FAILURE);
el->reg.plugin_init = regp->plugin_init;
el->reg.plugin_fini = regp->plugin_fini;
el->next = NULL;
if (plugin_reg_list == NULL) {
plugin_reg_list = el;
} else { /* add to end */
tmp = plugin_reg_list;
while (tmp->next != NULL)
tmp = tmp->next;
tmp->next = el;
}
return (PICL_SUCCESS);
}
/*
* Call fini routines of the registered plugins
*/
static void
plugin_fini(picld_plugin_reg_list_t *p)
{
if (p == NULL)
return;
plugin_fini(p->next);
if (p->reg.plugin_fini)
(p->reg.plugin_fini)();
}
/*
* Create PICL Tree
*/
static void
init_plugin_reg_list(void)
{
plugin_reg_list = NULL;
}
static int
picltree_set_root(picl_nodehdl_t rooth)
{
picl_obj_t *pobj;
int err;
(void) rw_rdlock(&ptree_rwlock); /* lock ptree */
pobj = NULL;
err = lookup_and_lock_node(RDLOCK_NODE, rooth, &pobj); /* lock node */
if (err != PICL_SUCCESS) {
(void) rw_unlock(&ptree_rwlock);
return (PICL_FAILURE);
}
piclize_node(pobj);
picl_root_obj = pobj;
ptree_root_hdl = pobj->ptree_hdl;
unlock_node(pobj); /* unlock node */
(void) rw_unlock(&ptree_rwlock); /* unlock ptree */
return (PICL_SUCCESS);
}
static int
picltree_init(void)
{
(void) rwlock_init(&ptree_rwlock, USYNC_THREAD, NULL);
(void) rwlock_init(&picltbl_rwlock, USYNC_THREAD, NULL);
if (hash_init(&picltbl) < 0)
return (PICL_FAILURE);
if (hash_init(&ptreetbl) < 0)
return (PICL_FAILURE);
if (pthread_mutex_init(&ptreehdl_lock, NULL) != 0)
return (PICL_FAILURE);
if (pthread_mutex_init(&piclhdl_lock, NULL) != 0)
return (PICL_FAILURE);
if (pthread_mutex_init(&evtq_lock, NULL) != 0)
return (PICL_FAILURE);
if (pthread_cond_init(&evtq_cv, NULL) != 0)
return (PICL_FAILURE);
if (pthread_mutex_init(&evthandler_lock, NULL) != 0)
return (PICL_FAILURE);
picl_root_obj = NULL;
eventqp = NULL;
evt_handlers = NULL;
ptree_root_hdl = PICL_INVALID_PICLHDL;
return (PICL_SUCCESS);
}
static void
add_unique_plugin_to_list(char *path, char *name)
{
char *buf;
picld_plugin_desc_t *pl;
picld_plugin_desc_t *tmp;
pl = plugin_desc;
while (pl != NULL) {
if (strcmp(pl->libname, name) == 0)
return;
else
pl = pl->next;
}
pl = malloc(sizeof (picld_plugin_desc_t));
if (pl == NULL)
return;
pl->libname = strdup(name);
if (pl->libname == NULL)
return;
buf = alloca(strlen(name) + strlen(path) + 2);
if (buf == NULL)
return;
(void) strcpy(buf, path);
(void) strcat(buf, name);
pl->pathname = strdup(buf);
if (pl->pathname == NULL)
return;
pl->next = NULL;
if (plugin_desc == NULL)
plugin_desc = pl;
else {
tmp = plugin_desc;
while (tmp->next != NULL)
tmp = tmp->next;
tmp->next = pl;
}
}
static void
get_plugins_from_dir(char *dirname)
{
struct dirent *ent;
DIR *dir;
int len;
int solen = strlen(SO_VERS) + 1;
if ((dir = opendir(dirname)) == NULL)
return;
while ((ent = readdir(dir)) != NULL) {
if ((strcmp(ent->d_name, ".") == 0) ||
(strcmp(ent->d_name, "..") == 0))
continue;
len = strlen(ent->d_name) + 1;
if (len < solen)
continue;
if (strcmp(ent->d_name + (len - solen), SO_VERS) == 0)
add_unique_plugin_to_list(dirname, ent->d_name);
}
(void) closedir(dir);
}
static void
init_plugin_list(void)
{
char nmbuf[SYS_NMLN];
char pname[PATH_MAX];
plugin_desc = NULL;
if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) != -1) {
(void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf);
if (access(pname, R_OK) == 0)
get_plugins_from_dir(pname);
}
if (sysinfo(SI_MACHINE, nmbuf, sizeof (nmbuf)) != -1) {
(void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf);
if (access(pname, R_OK) == 0)
get_plugins_from_dir(pname);
}
(void) snprintf(pname, PATH_MAX, "%s/", PICLD_COMMON_PLUGIN_DIR);
if (access(pname, R_OK) == 0)
get_plugins_from_dir(pname);
}
static void
load_plugins(void)
{
picld_plugin_desc_t *pl;
pl = plugin_desc;
while (pl != NULL) {
pl->dlh = dlopen(pl->pathname, RTLD_LAZY|RTLD_LOCAL);
if (pl->dlh == NULL) {
syslog(LOG_CRIT, dlerror());
return;
}
pl = pl->next;
}
}
static int
add_root_props(picl_nodehdl_t rooth)
{
int err;
picl_prophdl_t proph;
ptree_propinfo_t pinfo;
float picl_vers;
#define PICL_PROP_PICL_VERSION "PICLVersion"
#define PICL_VERSION 1.1
err = ptree_init_propinfo(&pinfo, PTREE_PROPINFO_VERSION_1,
PICL_PTYPE_FLOAT, PICL_READ, sizeof (picl_vers),
PICL_PROP_PICL_VERSION, NULL, NULL);
if (err != PICL_SUCCESS)
return (err);
picl_vers = PICL_VERSION;
err = ptree_create_and_add_prop(rooth, &pinfo, &picl_vers, &proph);
return (err);
}
static int
construct_picltree(void)
{
int err;
picld_plugin_reg_list_t *iter;
picl_nodehdl_t rhdl;
/*
* Create "/" node
*/
if ((err = ptree_create_node(PICL_NODE_ROOT, PICL_CLASS_PICL,
&rhdl)) != PICL_SUCCESS) {
return (err);
}
if (picltree_set_root(rhdl) != PICL_SUCCESS) {
return (PICL_FAILURE);
}
err = add_root_props(rhdl);
if (err != PICL_SUCCESS)
return (err);
/*
* Initialize the registered plug-in modules
*/
iter = plugin_reg_list;
while (iter != NULL) {
if (iter->reg.plugin_init)
(iter->reg.plugin_init)();
iter = iter->next;
}
return (PICL_SUCCESS);
}
void
xptree_destroy(void)
{
dbg_print(1, "xptree_destroy: picl_root_obj = %s\n",
(picl_root_obj == NULL ? "NULL" : "not-NULL"));
if (picl_root_obj == NULL)
return;
dbg_print(1, "xptree_destroy: call plugin_fini\n");
plugin_fini(plugin_reg_list);
dbg_print(1, "xptree_destroy: plugin_fini DONE\n");
(void) ptree_delete_node(picl_root_obj->ptree_hdl);
(void) ptree_destroy_node(picl_root_obj->ptree_hdl);
(void) rw_wrlock(&ptree_rwlock);
picl_root_obj = NULL;
(void) rw_unlock(&ptree_rwlock);
}
/*ARGSUSED*/
int
xptree_initialize(int flg)
{
int err;
pthread_attr_t attr;
pthread_t tid;
picld_pid = getpid();
picld_cred.dc_euid = geteuid();
picld_cred.dc_egid = getegid();
picld_cred.dc_ruid = getuid();
picld_cred.dc_rgid = getgid();
picld_cred.dc_pid = getpid();
picl_hdl_hi = 1;
ptree_hdl_hi = 1;
ptree_generation = 1;
qempty_wait = 0;
if (pthread_mutex_init(&ptree_refresh_mutex, NULL) != 0)
return (PICL_FAILURE);
if (picltree_init() != PICL_SUCCESS)
return (PICL_FAILURE);
init_plugin_reg_list();
init_plugin_list();
load_plugins();
err = construct_picltree();
if (err != PICL_SUCCESS)
return (err);
/*
* Dispatch events after all plug-ins have initialized
*/
if (pthread_attr_init(&attr) != 0)
return (PICL_FAILURE);
(void) pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
if (pthread_create(&tid, &attr, ptree_event_thread, NULL))
return (PICL_FAILURE);
return (PICL_SUCCESS);
}
int
xptree_reinitialize(void)
{
int err;
/*
* Wait for eventq to become empty
*/
dbg_print(1, "xptree_reinitialize: wait for evtq empty\n");
(void) pthread_mutex_lock(&evtq_lock);
qempty_wait = 1;
while (eventqp != NULL)
(void) pthread_cond_wait(&evtq_empty, &evtq_lock);
qempty_wait = 0;
(void) pthread_mutex_unlock(&evtq_lock);
dbg_print(1, "xptree_reinitialize: evtq empty is EMPTY\n");
(void) rw_wrlock(&ptree_rwlock);
picl_root_obj = NULL;
ptree_root_hdl = PICL_INVALID_PICLHDL;
(void) rw_unlock(&ptree_rwlock);
(void) pthread_mutex_lock(&ptree_refresh_mutex);
++ptree_generation;
(void) pthread_mutex_unlock(&ptree_refresh_mutex);
err = construct_picltree();
(void) pthread_mutex_lock(&ptree_refresh_mutex);
(void) pthread_cond_broadcast(&ptree_refresh_cond);
(void) pthread_mutex_unlock(&ptree_refresh_mutex);
(void) pthread_mutex_lock(&evtq_lock);
(void) pthread_cond_broadcast(&evtq_cv);
(void) pthread_mutex_unlock(&evtq_lock);
return (err);
}
/*
* This function is called by the PICL daemon on behalf of clients to
* wait for a tree refresh
*/
int
xptree_refresh_notify(uint32_t secs)
{
int curgen;
int ret;
timespec_t to;
if (pthread_mutex_lock(&ptree_refresh_mutex) != 0)
return (PICL_FAILURE);
curgen = ptree_generation;
while (curgen == ptree_generation) {
if (secs == 0) /* wait forever */
(void) pthread_cond_wait(&ptree_refresh_cond,
&ptree_refresh_mutex);
else {
to.tv_sec = secs;
to.tv_nsec = 0;
ret = pthread_cond_reltimedwait_np(&ptree_refresh_cond,
&ptree_refresh_mutex, &to);
if (ret == ETIMEDOUT)
break;
}
}
(void) pthread_mutex_unlock(&ptree_refresh_mutex);
return (PICL_SUCCESS);
}
/*VARARGS2*/
void
dbg_print(int level, const char *fmt, ...)
{
if (verbose_level >= level) {
va_list ap;
va_start(ap, fmt);
(void) vprintf(fmt, ap);
va_end(ap);
}
}
/*ARGSUSED*/
void
dbg_exec(int level, void (*fn)(void *args), void *args)
{
if (verbose_level > level)
(*fn)(args);
}