| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * 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 (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. |
| */ |
| |
| /* |
| * 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 */ |
| } |
| /* FALLTHROUGH */ |
| 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 |