summaryrefslogtreecommitdiff
path: root/usr/src/lib/libpool/common/pool.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libpool/common/pool.c')
-rw-r--r--usr/src/lib/libpool/common/pool.c2944
1 files changed, 2944 insertions, 0 deletions
diff --git a/usr/src/lib/libpool/common/pool.c b/usr/src/lib/libpool/common/pool.c
new file mode 100644
index 0000000..bcd8c9a
--- /dev/null
+++ b/usr/src/lib/libpool/common/pool.c
@@ -0,0 +1,2944 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <thread.h>
+#include <pthread.h>
+#include <synch.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <fcntl.h>
+#include <note.h>
+#include <errno.h>
+#include <ctype.h>
+#include <libintl.h>
+#include <libscf.h>
+#include <pool.h>
+#include <signal.h>
+
+#include <sys/pool.h>
+#include <sys/priocntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "pool_internal.h"
+#include "pool_impl.h"
+
+/*
+ * libpool Interface Routines
+ *
+ * pool.c implements (most of) the external interface to libpool
+ * users. Some of the interface is implemented in pool_internal.c for
+ * reasons of internal code organisation. The core requirements for
+ * pool.c are:
+ *
+ * Data Abstraction
+ *
+ * The abstraction of the actual datastore so that no details of the
+ * underlying data representation mechanism are revealed to users of
+ * the library. For instance, the fact that we use the kernel or files
+ * to store our configurations is completely abstracted via the
+ * various libpool APIs.
+ *
+ * External Interaction
+ *
+ * libpool users manipulate configuration components via the API
+ * defined in pool.h. Most functions in this file act as interceptors,
+ * validating parameters before redirecting the request into a
+ * specific datastore implementation for the actual work to be done.
+ *
+ * These main sets of requirements have driven the design so that it
+ * is possible to replace the entire datastore type without having to
+ * modify the external (or internal provider) APIs. It is possible to
+ * modify the storage technology used by libpool by implementing a new
+ * set of datastore provider operations. Simply modify the
+ * pool_conf_open() routine to establish a new datastore as the
+ * provider for a configuration.
+ *
+ * The key components in a libpool configuration are :
+ * pool_conf_t - This represents a complete configuration instance
+ * pool_t - A pool inside a configuration
+ * pool_resource_t - A resource inside a configuration
+ * pool_component_t - A component of a resource
+ *
+ */
+
+/*
+ * Used to control transfer setup.
+ */
+#define XFER_FAIL PO_FAIL
+#define XFER_SUCCESS PO_SUCCESS
+#define XFER_CONTINUE 1
+
+#define SMF_SVC_INSTANCE "svc:/system/pools:default"
+#define E_ERROR 1 /* Exit status for error */
+
+#ifndef TEXT_DOMAIN
+#define TEXT_DOMAIN "SYS_TEST"
+#endif /* TEXT_DOMAIN */
+
+const char pool_info_location[] = "/dev/pool";
+
+/*
+ * Static data
+ */
+static const char static_location[] = "/etc/pooladm.conf";
+static const char dynamic_location[] = "/dev/poolctl";
+static thread_key_t errkey = THR_ONCE_KEY;
+
+/*
+ * libpool error code
+ */
+static int pool_errval = POE_OK;
+
+/*
+ * libpool version
+ */
+static uint_t pool_workver = POOL_VER_CURRENT;
+
+static const char *data_type_tags[] = {
+ "uint",
+ "int",
+ "float",
+ "boolean",
+ "string"
+};
+
+/*
+ * static functions
+ */
+static int pool_elem_remove(pool_elem_t *);
+static int is_valid_prop_name(const char *);
+static int prop_buf_build_cb(pool_conf_t *, pool_elem_t *, const char *,
+ pool_value_t *, void *);
+static char *pool_base_info(const pool_elem_t *, char_buf_t *, int);
+static int choose_components(pool_resource_t *, pool_resource_t *, uint64_t);
+static int pool_conf_check(const pool_conf_t *);
+static void free_value_list(int, pool_value_t **);
+static int setup_transfer(pool_conf_t *, pool_resource_t *, pool_resource_t *,
+ uint64_t, uint64_t *, uint64_t *);
+
+/*
+ * Return the "static" location string for libpool.
+ */
+const char *
+pool_static_location(void)
+{
+ return (static_location);
+}
+
+/*
+ * Return the "dynamic" location string for libpool.
+ */
+const char *
+pool_dynamic_location(void)
+{
+ return (dynamic_location);
+}
+
+/*
+ * Return the status for a configuration. If the configuration has
+ * been successfully opened, then the status will be POF_VALID or
+ * POF_DESTROY. If the configuration failed to open properly or has
+ * been closed or removed, then the status will be POF_INVALID.
+ */
+pool_conf_state_t
+pool_conf_status(const pool_conf_t *conf)
+{
+ return (conf->pc_state);
+}
+
+/*
+ * Bind idtype id to the pool name.
+ */
+int
+pool_set_binding(const char *pool_name, idtype_t idtype, id_t id)
+{
+ pool_conf_t *conf;
+ int result;
+
+ if ((conf = pool_conf_alloc()) == NULL)
+ return (PO_FAIL);
+
+ if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY) < 0) {
+ pool_conf_free(conf);
+ pool_seterror(POE_INVALID_CONF);
+ return (PO_FAIL);
+ }
+
+ result = conf->pc_prov->pc_set_binding(conf, pool_name, idtype, id);
+
+ (void) pool_conf_close(conf);
+ pool_conf_free(conf);
+ return (result);
+}
+
+/*
+ * pool_get_resource_binding() returns the binding for a pid to the supplied
+ * type of resource. If a binding cannot be determined, NULL is returned.
+ */
+char *
+pool_get_resource_binding(const char *sz_type, pid_t pid)
+{
+ pool_conf_t *conf;
+ char *result;
+ pool_resource_elem_class_t type;
+
+ if ((type = pool_resource_elem_class_from_string(sz_type)) ==
+ PREC_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ if ((conf = pool_conf_alloc()) == NULL)
+ return (NULL);
+
+ if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY)
+ != PO_SUCCESS) {
+ pool_seterror(POE_INVALID_CONF);
+ pool_conf_free(conf);
+ return (NULL);
+ }
+ result = conf->pc_prov->pc_get_resource_binding(conf, type, pid);
+ (void) pool_conf_close(conf);
+ pool_conf_free(conf);
+ return (result);
+}
+
+/*
+ * pool_get_binding() returns the binding for a pid to a pool. If a
+ * binding cannot be determined, NULL is returned.
+ */
+char *
+pool_get_binding(pid_t pid)
+{
+ pool_conf_t *conf;
+ char *result;
+
+ if ((conf = pool_conf_alloc()) == NULL)
+ return (NULL);
+
+ if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY)
+ != PO_SUCCESS) {
+ pool_seterror(POE_INVALID_CONF);
+ pool_conf_free(conf);
+ return (NULL);
+ }
+ result = conf->pc_prov->pc_get_binding(conf, pid);
+ (void) pool_conf_close(conf);
+ pool_conf_free(conf);
+ return (result);
+}
+
+/*ARGSUSED*/
+int
+prop_buf_build_cb(pool_conf_t *UNUSED, pool_elem_t *pe, const char *name,
+ pool_value_t *pval, void *user)
+{
+ uint64_t u;
+ int64_t i;
+ uchar_t bool;
+ const char *str;
+ double d;
+ char_buf_t *cb = (char_buf_t *)user;
+ int type = pool_value_get_type(pval);
+
+ /*
+ * Ignore "type" and "<type>.name" properties as these are not
+ * to be displayed by this function
+ */
+ if (strcmp(name, c_type) == 0 ||
+ strcmp(property_name_minus_ns(pe, name), c_name) == 0)
+ return (PO_SUCCESS);
+ if (append_char_buf(cb, "\n%s\t%s\t%s ", cb->cb_tab_buf,
+ data_type_tags[type], name) == PO_FAIL)
+ return (PO_FAIL);
+ switch (type) {
+ case POC_UINT:
+ (void) pool_value_get_uint64(pval, &u);
+ if (append_char_buf(cb, "%llu", (u_longlong_t)u) == PO_FAIL)
+ return (PO_FAIL);
+ break;
+ case POC_INT:
+ (void) pool_value_get_int64(pval, &i);
+ if (append_char_buf(cb, "%lld", (longlong_t)i) == PO_FAIL)
+ return (PO_FAIL);
+ break;
+ case POC_STRING:
+ (void) pool_value_get_string(pval, &str);
+ if (append_char_buf(cb, "%s", str) == PO_FAIL)
+ return (PO_FAIL);
+ break;
+ case POC_BOOL:
+ (void) pool_value_get_bool(pval, &bool);
+ if (bool == 0) {
+ if (append_char_buf(cb, "%s", "false") == PO_FAIL)
+ return (PO_FAIL);
+ } else {
+ if (append_char_buf(cb, "%s", "true") == PO_FAIL)
+ return (PO_FAIL);
+ }
+ break;
+ case POC_DOUBLE:
+ (void) pool_value_get_double(pval, &d);
+ if (append_char_buf(cb, "%g", d) == PO_FAIL)
+ return (PO_FAIL);
+ break;
+ case POC_INVAL: /* Do nothing */
+ break;
+ default:
+ return (PO_FAIL);
+ }
+ return (PO_SUCCESS);
+}
+
+/*
+ * Return a buffer which describes the element
+ * pe is a pointer to the element
+ * deep is PO_TRUE/PO_FALSE to indicate whether children should be included
+ */
+char *
+pool_base_info(const pool_elem_t *pe, char_buf_t *cb, int deep)
+{
+ const char *sres;
+ uint_t i;
+ uint_t nelem;
+
+ pool_value_t val = POOL_VALUE_INITIALIZER;
+ pool_resource_t **rs;
+ pool_elem_t *elem;
+ pool_conf_t *conf = TO_CONF(pe);
+
+ if (cb == NULL) {
+ char *ret = NULL;
+
+ if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL)
+ return (NULL);
+
+ /*
+ * Populate the buffer with element details
+ */
+ (void) pool_base_info(pe, cb, deep);
+ if (cb->cb_buf)
+ ret = strdup(cb->cb_buf);
+ free_char_buf(cb);
+ return (ret);
+ }
+
+ if (append_char_buf(cb, "\n%s%s", cb->cb_tab_buf,
+ pool_elem_class_string(pe)) == PO_FAIL) {
+ return (NULL);
+ }
+
+ if (pool_get_ns_property(pe, c_name, &val) == POC_STRING) {
+ (void) pool_value_get_string(&val, &sres);
+ if (append_char_buf(cb, " %s", sres) == PO_FAIL) {
+ return (NULL);
+ }
+ }
+
+ /*
+ * Add in some details about the element
+ */
+ if (pool_walk_properties(conf, (pool_elem_t *)pe, cb,
+ prop_buf_build_cb) == PO_FAIL) {
+ (void) append_char_buf(cb, "\n%s%s\n", cb->cb_tab_buf,
+ "Cannot access the properties of this element.");
+ return (NULL);
+ }
+ if (append_char_buf(cb, "%s", "\n") == PO_FAIL)
+ return (NULL);
+
+ if (pe->pe_class == PEC_POOL) {
+ /*
+ * A shallow display of a pool only lists the resources by name
+ */
+
+ if ((rs = pool_query_pool_resources(conf, pool_elem_pool(pe),
+ &nelem, NULL)) == NULL) {
+ return (NULL);
+ }
+
+ for (i = 0; i < nelem; i++) {
+ const char *str;
+
+ elem = TO_ELEM(rs[i]);
+
+ if (append_char_buf(cb, "\t%s%s", cb->cb_tab_buf,
+ pool_elem_class_string(elem)) == PO_FAIL) {
+ free(rs);
+ return (NULL);
+ }
+
+ if (pool_get_ns_property(elem, c_name, &val) !=
+ POC_STRING) {
+ free(rs);
+ pool_seterror(POE_INVALID_CONF);
+ return (NULL);
+ }
+ (void) pool_value_get_string(&val, &str);
+ if (append_char_buf(cb, "\t%s\n", str) == PO_FAIL) {
+ free(rs);
+ return (NULL);
+ }
+ }
+ free(rs);
+ }
+ if (deep == PO_TRUE) {
+ pool_t **ps;
+ pool_component_t **cs;
+
+ if (strlcat(cb->cb_tab_buf, "\t", CB_TAB_BUF_SIZE)
+ >= CB_TAB_BUF_SIZE) {
+ pool_seterror(POE_SYSTEM);
+ return (NULL);
+ }
+ switch (pe->pe_class) {
+ case PEC_SYSTEM:
+ if ((ps = pool_query_pools(conf, &nelem, NULL)) !=
+ NULL) { /* process the pools */
+ for (i = 0; i < nelem; i++) {
+ elem = TO_ELEM(ps[i]);
+ if (pool_base_info(elem, cb,
+ PO_FALSE) == NULL) {
+ free(ps);
+ return (NULL);
+ }
+ }
+ free(ps);
+ }
+ if ((rs = pool_query_resources(conf, &nelem, NULL)) !=
+ NULL) {
+ for (i = 0; i < nelem; i++) {
+ elem = TO_ELEM(rs[i]);
+ if (pool_base_info(elem, cb,
+ PO_TRUE) == NULL) {
+ free(rs);
+ return (NULL);
+ }
+ }
+ free(rs);
+ }
+ break;
+ case PEC_POOL:
+ if ((rs = pool_query_pool_resources(conf,
+ pool_elem_pool(pe), &nelem, NULL)) == NULL)
+ return (NULL);
+ for (i = 0; i < nelem; i++) {
+ elem = TO_ELEM(rs[i]);
+ if (pool_base_info(elem, cb, PO_TRUE) == NULL) {
+ free(rs);
+ return (NULL);
+ }
+ }
+ free(rs);
+ break;
+ case PEC_RES_COMP:
+ if ((cs = pool_query_resource_components(conf,
+ pool_elem_res(pe), &nelem, NULL)) != NULL) {
+ for (i = 0; i < nelem; i++) {
+ elem = TO_ELEM(cs[i]);
+ if (pool_base_info(elem, cb,
+ PO_FALSE) == NULL) {
+ free(cs);
+ return (NULL);
+ }
+ }
+ free(cs);
+ }
+ break;
+ case PEC_RES_AGG:
+ case PEC_COMP:
+ break;
+ default:
+ /*NOTREACHED*/
+ break;
+ }
+ if (cb->cb_tab_buf[0] != 0)
+ cb->cb_tab_buf[strlen(cb->cb_tab_buf) - 1] = 0;
+ }
+ return (cb->cb_buf);
+}
+
+/*
+ * Returns The information on the specified pool or NULL.
+ *
+ * Errors If the status of the conf is INVALID or the supplied
+ * value of deep is illegal, POE_BADPARAM.
+ *
+ * The caller is responsible for free(3c)ing the string returned.
+ */
+char *
+pool_info(const pool_conf_t *conf, const pool_t *pool, int deep)
+{
+ pool_elem_t *pe;
+
+ pe = TO_ELEM(pool);
+
+ if (TO_CONF(pe) != conf) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ if (pool_conf_status(conf) == POF_INVALID || (deep & ~1)) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ return (pool_base_info(pe, NULL, deep));
+}
+
+/*
+ * Returns The information on the specified resource or NULL.
+ *
+ * Errors If the status of the conf is INVALID or the supplied
+ * value of deep is illegal, POE_BADPARAM.
+ *
+ * The caller is responsible for free(3c)ing the string returned.
+ */
+char *
+pool_resource_info(const pool_conf_t *conf, const pool_resource_t *res,
+ int deep)
+{
+ pool_elem_t *pe;
+
+ pe = TO_ELEM(res);
+
+ if (TO_CONF(pe) != conf) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ if (pool_conf_status(conf) == POF_INVALID || (deep & ~1)) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ return (pool_base_info(pe, NULL, deep));
+}
+
+/*
+ * Returns The information on the specified component or NULL.
+ *
+ * Errors If the status of the conf is INVALID or the supplied
+ * value of deep is illegal, POE_BADPARAM.
+ *
+ * The caller is responsible for free(3c)ing the string returned.
+ */
+char *
+pool_component_info(const pool_conf_t *conf, const pool_component_t *comp,
+ int deep)
+{
+ pool_elem_t *pe;
+
+ pe = TO_ELEM(comp);
+
+ if (TO_CONF(pe) != conf) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ if (pool_conf_status(conf) == POF_INVALID || (deep & ~1)) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ return (pool_base_info(pe, NULL, deep));
+}
+
+/*
+ * Returns The information on the specified conf or NULL.
+ *
+ * Errors If the status of the conf is INVALID or the supplied
+ * value of deep is illegal, POE_BADPARAM.
+ *
+ * The caller is responsible for free(3c)ing the string returned.
+ */
+char *
+pool_conf_info(const pool_conf_t *conf, int deep)
+{
+ pool_elem_t *pe;
+
+ if (pool_conf_status(conf) == POF_INVALID || (deep & ~1)) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+ if ((pe = pool_conf_to_elem(conf)) == NULL) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+ return (pool_base_info(pe, NULL, deep));
+}
+
+
+/*
+ * Set the thread specific error value.
+ */
+void
+pool_seterror(int errval)
+{
+ if (thr_main()) {
+ pool_errval = errval;
+ return;
+ }
+ (void) thr_keycreate_once(&errkey, 0);
+ (void) thr_setspecific(errkey, (void *)(intptr_t)errval);
+}
+
+/*
+ * Return the current value of the error code.
+ * Returns: int error code
+ */
+int
+pool_error(void)
+{
+ if (thr_main())
+ return (pool_errval);
+ if (errkey == THR_ONCE_KEY)
+ return (POE_OK);
+ return ((uintptr_t)pthread_getspecific(errkey));
+}
+
+/*
+ * Return the text represenation for the current value of the error code.
+ * Returns: const char * error string
+ */
+const char *
+pool_strerror(int error)
+{
+ char *str;
+
+ switch (error) {
+ case POE_OK:
+ str = dgettext(TEXT_DOMAIN, "Operation successful");
+ break;
+ case POE_BAD_PROP_TYPE:
+ str = dgettext(TEXT_DOMAIN,
+ "Attempted to retrieve the wrong property type");
+ break;
+ case POE_INVALID_CONF:
+ str = dgettext(TEXT_DOMAIN, "Invalid configuration");
+ break;
+ case POE_NOTSUP:
+ str = dgettext(TEXT_DOMAIN, "Operation is not supported");
+ break;
+ case POE_INVALID_SEARCH:
+ str = dgettext(TEXT_DOMAIN, "Invalid search");
+ break;
+ case POE_BADPARAM:
+ str = dgettext(TEXT_DOMAIN, "Bad parameter supplied");
+ break;
+ case POE_PUTPROP:
+ str = dgettext(TEXT_DOMAIN, "Error putting property");
+ break;
+ case POE_DATASTORE:
+ str = dgettext(TEXT_DOMAIN, "Pools repository error");
+ break;
+ case POE_SYSTEM:
+ str = dgettext(TEXT_DOMAIN, "System error");
+ break;
+ case POE_ACCESS:
+ str = dgettext(TEXT_DOMAIN, "Permission denied");
+ break;
+ default:
+ errno = ESRCH;
+ str = NULL;
+ }
+ return (str);
+}
+
+int
+pool_get_status(int *state)
+{
+ int fd;
+ pool_status_t status;
+
+ if ((fd = open(pool_info_location, O_RDONLY)) < 0) {
+ pool_seterror(POE_SYSTEM);
+ return (PO_FAIL);
+ }
+ if (ioctl(fd, POOL_STATUSQ, &status) < 0) {
+ (void) close(fd);
+ pool_seterror(POE_SYSTEM);
+ return (PO_FAIL);
+ }
+ (void) close(fd);
+
+ *state = status.ps_io_state;
+
+ return (PO_SUCCESS);
+}
+
+int
+pool_set_status(int state)
+{
+ int old_state;
+
+ if (pool_get_status(&old_state) != PO_SUCCESS) {
+ pool_seterror(POE_SYSTEM);
+ return (PO_FAIL);
+ }
+
+ if (old_state != state) {
+ int fd;
+ pool_status_t status;
+ char *fmri;
+
+ /*
+ * Changing the status of pools is performed by enabling
+ * or disabling the pools service instance. If this
+ * function has not been invoked by startd then we simply
+ * enable/disable the service and return success.
+ *
+ * There is no way to specify that state changes must be
+ * synchronous using the library API as yet, so we use
+ * the -s option provided by svcadm.
+ */
+ fmri = getenv("SMF_FMRI");
+ if (fmri == NULL) {
+ FILE *p;
+ char *cmd;
+
+ if (state != 0) {
+ cmd = "/usr/sbin/svcadm enable -s " \
+ SMF_SVC_INSTANCE;
+ } else {
+ cmd = "/usr/sbin/svcadm disable -s " \
+ SMF_SVC_INSTANCE;
+ }
+ if ((p = popen(cmd, "wF")) == NULL || pclose(p) != 0) {
+ pool_seterror(POE_SYSTEM);
+ return (PO_FAIL);
+ }
+ return (PO_SUCCESS);
+ }
+
+ if ((fd = open(pool_dynamic_location(), O_RDWR | O_EXCL)) < 0) {
+ pool_seterror(POE_SYSTEM);
+ return (PO_FAIL);
+ }
+
+ /*
+ * If pools are being enabled/disabled by another smf service,
+ * enable the smf service instance. This must be done
+ * asynchronously as one service cannot synchronously
+ * enable/disable another.
+ */
+ if (strcmp(fmri, SMF_SVC_INSTANCE) != 0) {
+ int res;
+
+ if (state != 0)
+ res = smf_enable_instance(SMF_SVC_INSTANCE, 0);
+ else
+ res = smf_disable_instance(SMF_SVC_INSTANCE, 0);
+
+ if (res != 0) {
+ (void) close(fd);
+ pool_seterror(POE_SYSTEM);
+ return (PO_FAIL);
+ }
+ }
+ status.ps_io_state = state;
+
+ if (ioctl(fd, POOL_STATUS, &status) < 0) {
+ (void) close(fd);
+ pool_seterror(POE_SYSTEM);
+ return (PO_FAIL);
+ }
+
+ (void) close(fd);
+
+ }
+ return (PO_SUCCESS);
+}
+
+/*
+ * General Data Provider Independent Access Methods
+ */
+
+/*
+ * Property manipulation code.
+ *
+ * The pool_(get|rm|set)_property() functions consult the plugins before
+ * looking at the actual configuration. This allows plugins to provide
+ * "virtual" properties that may not exist in the configuration file per se,
+ * but behave like regular properties. This also allows plugins to reserve
+ * certain properties as read-only, non-removable, etc.
+ *
+ * A negative value returned from the plugin denotes error, 0 means that the
+ * property request should be forwarded to the backend, and 1 means the request
+ * was satisfied by the plugin and should not be processed further.
+ *
+ * The (get|rm|set)_property() functions bypass the plugin layer completely,
+ * and hence should not be generally used.
+ */
+
+/*
+ * Return true if the string passed in matches the pattern
+ * [A-Za-z][A-Za-z0-9,._-]*
+ */
+int
+is_valid_name(const char *name)
+{
+ int i;
+ char c;
+
+ if (name == NULL)
+ return (PO_FALSE);
+ if (!isalpha(name[0]))
+ return (PO_FALSE);
+ for (i = 1; (c = name[i]) != '\0'; i++) {
+ if (!isalnum(c) && c != ',' && c != '.' && c != '_' && c != '-')
+ return (PO_FALSE);
+ }
+ return (PO_TRUE);
+}
+
+/*
+ * Return true if the string passed in matches the pattern
+ * [A-Za-z_][A-Za-z0-9,._-]*
+ * A property name starting with a '_' is an "invisible" property that does not
+ * show up in a property walk.
+ */
+int
+is_valid_prop_name(const char *prop_name)
+{
+ int i;
+ char c;
+
+ if (prop_name == NULL)
+ return (PO_FALSE);
+ if (!isalpha(prop_name[0]) && prop_name[0] != '_')
+ return (PO_FALSE);
+ for (i = 1; (c = prop_name[i]) != '\0'; i++) {
+ if (!isalnum(c) && c != ',' && c != '.' && c != '_' && c != '-')
+ return (PO_FALSE);
+ }
+ return (PO_TRUE);
+}
+
+/*
+ * Return the specified property value.
+ *
+ * POC_INVAL is returned if an error is detected and the error code is updated
+ * to indicate the cause of the error.
+ */
+pool_value_class_t
+pool_get_property(const pool_conf_t *conf, const pool_elem_t *pe,
+ const char *name, pool_value_t *val)
+{
+ const pool_prop_t *prop_info;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (POC_INVAL);
+ }
+ if (pool_value_set_name(val, name) != PO_SUCCESS) {
+ return (POC_INVAL);
+ }
+ /*
+ * Check to see if this is a property we are managing. If it
+ * is and it has an interceptor installed for property
+ * retrieval, use it.
+ */
+ if ((prop_info = provider_get_prop(pe, name)) != NULL &&
+ prop_info->pp_op.ppo_get_value != NULL) {
+ if (prop_info->pp_op.ppo_get_value(pe, val) == PO_FAIL)
+ return (POC_INVAL);
+ else
+ return (pool_value_get_type(val));
+ }
+ return (pe->pe_get_prop(pe, name, val));
+}
+
+/*
+ * Return the specified property value with the namespace prepended.
+ * e.g. If this function is used to get the property "name" on a pool, it will
+ * attempt to retrieve "pool.name".
+ *
+ * POC_INVAL is returned if an error is detected and the error code is updated
+ * to indicate the cause of the error.
+ */
+pool_value_class_t
+pool_get_ns_property(const pool_elem_t *pe, const char *name, pool_value_t *val)
+{
+ int ret;
+ char_buf_t *cb;
+
+ if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL)
+ return (POC_INVAL);
+ if (set_char_buf(cb, "%s.%s", pool_elem_class_string(pe), name) ==
+ PO_FAIL) {
+ free_char_buf(cb);
+ return (POC_INVAL);
+ }
+ ret = pool_get_property(TO_CONF(pe), pe, cb->cb_buf, val);
+ free_char_buf(cb);
+ return (ret);
+}
+
+/*
+ * Update the specified property value.
+ *
+ * PO_FAIL is returned if an error is detected and the error code is updated
+ * to indicate the cause of the error.
+ */
+int
+pool_put_property(pool_conf_t *conf, pool_elem_t *pe, const char *name,
+ const pool_value_t *val)
+{
+ const pool_prop_t *prop_info;
+
+ if (pool_conf_check(conf) != PO_SUCCESS)
+ return (PO_FAIL);
+
+ if (TO_CONF(pe) != conf) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ /* Don't allow (re)setting of the "temporary" property */
+ if (!is_valid_prop_name(name) || strstr(name, ".temporary") != NULL) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+
+ /* Don't allow rename of temporary pools/resources */
+ if (strstr(name, ".name") != NULL && elem_is_tmp(pe)) {
+ boolean_t rename = B_TRUE;
+ pool_value_t *pv = pool_value_alloc();
+
+ if (pe->pe_get_prop(pe, name, pv) != POC_INVAL) {
+ const char *s1 = NULL;
+ const char *s2 = NULL;
+
+ (void) pool_value_get_string(pv, &s1);
+ (void) pool_value_get_string(val, &s2);
+ if (s1 != NULL && s2 != NULL && strcmp(s1, s2) == 0)
+ rename = B_FALSE;
+ }
+ pool_value_free(pv);
+
+ if (rename) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ }
+
+ /*
+ * Check to see if this is a property we are managing. If it is,
+ * ensure that we are happy with what the user is doing.
+ */
+ if ((prop_info = provider_get_prop(pe, name)) != NULL) {
+ if (prop_is_readonly(prop_info) == PO_TRUE) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ if (prop_info->pp_op.ppo_set_value &&
+ prop_info->pp_op.ppo_set_value(pe, val) == PO_FAIL)
+ return (PO_FAIL);
+ }
+
+ return (pe->pe_put_prop(pe, name, val));
+}
+
+/*
+ * Set temporary property to flag as a temporary element.
+ *
+ * PO_FAIL is returned if an error is detected and the error code is updated
+ * to indicate the cause of the error.
+ */
+int
+pool_set_temporary(pool_conf_t *conf, pool_elem_t *pe)
+{
+ int res;
+ char name[128];
+ pool_value_t *val;
+
+ if (pool_conf_check(conf) != PO_SUCCESS)
+ return (PO_FAIL);
+
+ if (TO_CONF(pe) != conf) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+
+ /* create property name based on element type */
+ if (snprintf(name, sizeof (name), "%s.temporary",
+ pool_elem_class_string(pe)) > sizeof (name)) {
+ pool_seterror(POE_SYSTEM);
+ return (PO_FAIL);
+ }
+
+ if ((val = pool_value_alloc()) == NULL)
+ return (PO_FAIL);
+
+ pool_value_set_bool(val, (uchar_t)1);
+
+ res = pe->pe_put_prop(pe, name, val);
+
+ pool_value_free(val);
+
+ return (res);
+}
+
+/*
+ * Update the specified property value with the namespace prepended.
+ * e.g. If this function is used to update the property "name" on a pool, it
+ * will attempt to update "pool.name".
+ *
+ * PO_FAIL is returned if an error is detected and the error code is updated
+ * to indicate the cause of the error.
+ */
+int
+pool_put_ns_property(pool_elem_t *pe, const char *name,
+ const pool_value_t *val)
+{
+ char_buf_t *cb;
+ int ret;
+
+ if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL)
+ return (PO_FAIL);
+ if (set_char_buf(cb, "%s.%s", pool_elem_class_string(pe), name) ==
+ PO_FAIL) {
+ free_char_buf(cb);
+ return (PO_FAIL);
+ }
+ ret = pool_put_property(TO_CONF(pe), pe, cb->cb_buf, val);
+ free_char_buf(cb);
+ return (ret);
+}
+
+/*
+ * Update the specified property value. Do not use the property
+ * protection mechanism. This function should only be used for cases
+ * where the library must bypass the normal property protection
+ * mechanism. The only known use is to update properties in the static
+ * configuration when performing a commit.
+ *
+ * PO_FAIL is returned if an error is detected and the error code is
+ * updated to indicate the cause of the error.
+ */
+int
+pool_put_any_property(pool_elem_t *pe, const char *name,
+ const pool_value_t *val)
+{
+ if (!is_valid_prop_name(name)) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+
+ return (pe->pe_put_prop(pe, name, val));
+}
+
+/*
+ * Update the specified property value with the namespace prepended.
+ * e.g. If this function is used to update the property "name" on a pool, it
+ * will attempt to update "pool.name".
+ *
+ * PO_FAIL is returned if an error is detected and the error code is updated
+ * to indicate the cause of the error.
+ */
+int
+pool_put_any_ns_property(pool_elem_t *pe, const char *name,
+ const pool_value_t *val)
+{
+ char_buf_t *cb;
+ int ret;
+
+ if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL)
+ return (PO_FAIL);
+ if (set_char_buf(cb, "%s.%s", pool_elem_class_string(pe), name) ==
+ PO_FAIL) {
+ free_char_buf(cb);
+ return (PO_FAIL);
+ }
+ ret = pool_put_any_property(pe, cb->cb_buf, val);
+ free_char_buf(cb);
+ return (ret);
+}
+
+/*
+ * Remove the specified property value. Note that some properties are
+ * mandatory and thus failure to remove these properties is inevitable.
+ * PO_FAIL is returned if an error is detected and the error code is updated
+ * to indicate the cause of the error.
+ */
+int
+pool_rm_property(pool_conf_t *conf, pool_elem_t *pe, const char *name)
+{
+ const pool_prop_t *prop_info;
+
+ if (pool_conf_check(conf) != PO_SUCCESS)
+ return (PO_FAIL);
+
+ if (TO_CONF(pe) != conf) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ /* Don't allow removal of the "temporary" property */
+ if (strstr(name, ".temporary") != NULL) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+
+ /*
+ * Check to see if this is a property we are managing. If it is,
+ * ensure that we are happy with what the user is doing.
+ */
+ if ((prop_info = provider_get_prop(pe, name)) != NULL) {
+ if (prop_is_optional(prop_info) == PO_FALSE) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ }
+ return (pe->pe_rm_prop(pe, name));
+}
+
+/*
+ * Check if the supplied name is a namespace protected property for the supplied
+ * element, pe. If it is, return the prefix, otherwise just return NULL.
+ */
+const char *
+is_ns_property(const pool_elem_t *pe, const char *name)
+{
+ const char *prefix;
+
+ if ((prefix = pool_elem_class_string(pe)) != NULL) {
+ if (strncmp(name, prefix, strlen(prefix)) == 0)
+ return (prefix);
+ }
+ return (NULL);
+}
+
+/*
+ * Check if the supplied name is a namespace protected property for the supplied
+ * element, pe. If it is, return the property name with the namespace stripped,
+ * otherwise just return the name.
+ */
+const char *
+property_name_minus_ns(const pool_elem_t *pe, const char *name)
+{
+ const char *prefix;
+ if ((prefix = is_ns_property(pe, name)) != NULL) {
+ return (name + strlen(prefix) + 1);
+ }
+ return (name);
+}
+
+/*
+ * Create an element to represent a pool and add it to the supplied
+ * configuration.
+ */
+pool_t *
+pool_create(pool_conf_t *conf, const char *name)
+{
+ pool_elem_t *pe;
+ pool_value_t val = POOL_VALUE_INITIALIZER;
+ const pool_prop_t *default_props;
+
+ if (pool_conf_check(conf) != PO_SUCCESS)
+ return (NULL);
+
+ if (!is_valid_name(name) || pool_get_pool(conf, name) != NULL) {
+ /*
+ * A pool with the same name exists. Reject.
+ */
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+ if ((pe = conf->pc_prov->pc_elem_create(conf, PEC_POOL, PREC_INVALID,
+ PCEC_INVALID)) == NULL) {
+ pool_seterror(POE_INVALID_CONF);
+ return (NULL);
+ }
+ if ((default_props = provider_get_props(pe)) != NULL) {
+ int i;
+ for (i = 0; default_props[i].pp_pname != NULL; i++) {
+ if (prop_is_init(&default_props[i]) &&
+ (pool_put_any_property(pe,
+ default_props[i].pp_pname,
+ &default_props[i].pp_value) == PO_FAIL)) {
+ (void) pool_destroy(conf, pool_elem_pool(pe));
+ return (NULL);
+ }
+ }
+ }
+ if (pool_value_set_string(&val, name) != PO_SUCCESS) {
+ (void) pool_destroy(conf, pool_elem_pool(pe));
+ pool_seterror(POE_SYSTEM);
+ return (NULL);
+ }
+ if (pool_put_property(conf, pe, "pool.name", &val) == PO_FAIL) {
+ (void) pool_destroy(conf, pool_elem_pool(pe));
+ pool_seterror(POE_PUTPROP);
+ return (NULL);
+ }
+
+ /*
+ * If we are creating a temporary pool configuration, flag the pool.
+ */
+ if (conf->pc_prov->pc_oflags & PO_TEMP) {
+ if (pool_set_temporary(conf, pe) == PO_FAIL) {
+ (void) pool_destroy(conf, pool_elem_pool(pe));
+ return (NULL);
+ }
+ }
+
+ return (pool_elem_pool(pe));
+}
+
+/*
+ * Create an element to represent a res.
+ */
+pool_resource_t *
+pool_resource_create(pool_conf_t *conf, const char *sz_type, const char *name)
+{
+ pool_elem_t *pe;
+ pool_value_t val = POOL_VALUE_INITIALIZER;
+ const pool_prop_t *default_props;
+ pool_resource_t **resources;
+ int is_default = 0;
+ uint_t nelem;
+ pool_elem_class_t elem_class;
+ pool_resource_elem_class_t type;
+ pool_value_t *props[] = { NULL, NULL };
+
+ if (pool_conf_check(conf) != PO_SUCCESS)
+ return (NULL);
+
+ if ((type = pool_resource_elem_class_from_string(sz_type)) ==
+ PREC_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ if (strcmp(sz_type, "pset") != 0) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ if (!is_valid_name(name) || pool_get_resource(conf, sz_type, name) !=
+ NULL) {
+ /*
+ * Resources must be unique by name+type.
+ */
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ props[0] = &val;
+
+ if (pool_value_set_string(props[0], sz_type) != PO_SUCCESS ||
+ pool_value_set_name(props[0], c_type) != PO_SUCCESS) {
+ return (NULL);
+ }
+
+ if ((resources = pool_query_resources(conf, &nelem, props)) == NULL) {
+ /*
+ * This is the first representative of this type; when it's
+ * created it should be created with 'default' = 'true'.
+ */
+ is_default = 1;
+ } else {
+ free(resources);
+ }
+ /*
+ * TODO: If Additional PEC_RES_COMP types are added to
+ * pool_impl.h, this would need to be extended.
+ */
+ switch (type) {
+ case PREC_PSET:
+ elem_class = PEC_RES_COMP;
+ break;
+ default:
+ elem_class = PEC_RES_AGG;
+ break;
+ }
+ if ((pe = conf->pc_prov->pc_elem_create(conf, elem_class, type,
+ PCEC_INVALID)) == NULL) {
+ pool_seterror(POE_INVALID_CONF);
+ return (NULL);
+ }
+
+ /*
+ * The plugins contain a list of default properties and their values
+ * for resources. The resource returned, hence, is fully initialized.
+ */
+ if ((default_props = provider_get_props(pe)) != NULL) {
+ int i;
+ for (i = 0; default_props[i].pp_pname != NULL; i++) {
+ if (prop_is_init(&default_props[i]) &&
+ pool_put_any_property(pe, default_props[i].pp_pname,
+ &default_props[i].pp_value) == PO_FAIL) {
+ (void) pool_resource_destroy(conf,
+ pool_elem_res(pe));
+ return (NULL);
+ }
+ }
+ }
+ if (pool_value_set_string(&val, name) != PO_SUCCESS ||
+ pool_put_ns_property(pe, "name", &val) != PO_SUCCESS) {
+ (void) pool_resource_destroy(conf, pool_elem_res(pe));
+ return (NULL);
+ }
+ if (is_default) {
+ pool_value_set_bool(&val, PO_TRUE);
+ if (pool_put_any_ns_property(pe, "default", &val) !=
+ PO_SUCCESS) {
+ (void) pool_resource_destroy(conf, pool_elem_res(pe));
+ return (NULL);
+ }
+ }
+
+ /*
+ * If we are creating a temporary pool configuration, flag the resource.
+ */
+ if (conf->pc_prov->pc_oflags & PO_TEMP) {
+ if (pool_set_temporary(conf, pe) != PO_SUCCESS) {
+ (void) pool_resource_destroy(conf, pool_elem_res(pe));
+ return (NULL);
+ }
+ }
+
+ return (pool_elem_res(pe));
+}
+
+/*
+ * Create an element to represent a resource component.
+ */
+pool_component_t *
+pool_component_create(pool_conf_t *conf, const pool_resource_t *res,
+ int64_t sys_id)
+{
+ pool_elem_t *pe;
+ pool_value_t val = POOL_VALUE_INITIALIZER;
+ const pool_prop_t *default_props;
+ char refbuf[KEY_BUFFER_SIZE];
+
+ if ((pe = conf->pc_prov->pc_elem_create(conf, PEC_COMP,
+ PREC_INVALID, PCEC_CPU)) == NULL) {
+ pool_seterror(POE_INVALID_CONF);
+ return (NULL);
+ }
+ /*
+ * TODO: If additional PEC_COMP types are added in pool_impl.h,
+ * this would need to be extended.
+ */
+ pe->pe_component_class = PCEC_CPU;
+ /* Now set the container for this comp */
+ if (pool_set_container(TO_ELEM(res), pe) == PO_FAIL) {
+ (void) pool_component_destroy(pool_elem_comp(pe));
+ return (NULL);
+ }
+ /*
+ * The plugins contain a list of default properties and their values
+ * for resources. The resource returned, hence, is fully initialized.
+ */
+ if ((default_props = provider_get_props(pe)) != NULL) {
+ int i;
+ for (i = 0; default_props[i].pp_pname != NULL; i++) {
+ if (prop_is_init(&default_props[i]) &&
+ pool_put_any_property(pe,
+ default_props[i].pp_pname,
+ &default_props[i].pp_value) == PO_FAIL) {
+ (void) pool_component_destroy(
+ pool_elem_comp(pe));
+ return (NULL);
+ }
+ }
+ }
+ /*
+ * Set additional attributes/properties on component.
+ */
+ pool_value_set_int64(&val, sys_id);
+ if (pool_put_any_ns_property(pe, c_sys_prop, &val) != PO_SUCCESS) {
+ (void) pool_component_destroy(pool_elem_comp(pe));
+ return (NULL);
+ }
+ if (snprintf(refbuf, KEY_BUFFER_SIZE, "%s_%lld",
+ pool_elem_class_string(pe), sys_id) > KEY_BUFFER_SIZE) {
+ (void) pool_component_destroy(pool_elem_comp(pe));
+ return (NULL);
+ }
+ if (pool_value_set_string(&val, refbuf) != PO_SUCCESS) {
+ (void) pool_component_destroy(pool_elem_comp(pe));
+ return (NULL);
+ }
+ if (pool_put_any_ns_property(pe, c_ref_id, &val) != PO_SUCCESS) {
+ (void) pool_component_destroy(pool_elem_comp(pe));
+ return (NULL);
+ }
+ return (pool_elem_comp(pe));
+}
+
+/*
+ * Return the location of a configuration.
+ */
+const char *
+pool_conf_location(const pool_conf_t *conf)
+{
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+ return (conf->pc_location);
+}
+/*
+ * Close a configuration, freeing all associated resources. Once a
+ * configuration is closed, it can no longer be used.
+ */
+int
+pool_conf_close(pool_conf_t *conf)
+{
+ int rv;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ rv = conf->pc_prov->pc_close(conf);
+ conf->pc_prov = NULL;
+ free((void *)conf->pc_location);
+ conf->pc_location = NULL;
+ conf->pc_state = POF_INVALID;
+ return (rv);
+}
+
+/*
+ * Remove a configuration, freeing all associated resources. Once a
+ * configuration is removed, it can no longer be accessed and is forever
+ * gone.
+ */
+int
+pool_conf_remove(pool_conf_t *conf)
+{
+ int rv;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ rv = conf->pc_prov->pc_remove(conf);
+ conf->pc_state = POF_INVALID;
+ return (rv);
+}
+
+/*
+ * pool_conf_alloc() allocate the resources to represent a configuration.
+ */
+pool_conf_t *
+pool_conf_alloc(void)
+{
+ pool_conf_t *conf;
+
+ if ((conf = calloc(1, sizeof (pool_conf_t))) == NULL) {
+ pool_seterror(POE_SYSTEM);
+ return (NULL);
+ }
+ conf->pc_state = POF_INVALID;
+ return (conf);
+}
+
+/*
+ * pool_conf_free() frees the resources associated with a configuration.
+ */
+void
+pool_conf_free(pool_conf_t *conf)
+{
+ free(conf);
+}
+
+/*
+ * pool_conf_open() opens a configuration, establishing all required
+ * connections to the data source.
+ */
+int
+pool_conf_open(pool_conf_t *conf, const char *location, int oflags)
+{
+ /*
+ * Since you can't do anything to a pool configuration without opening
+ * it, this represents a good point to intialise structures that would
+ * otherwise need to be initialised in a .init section.
+ */
+ internal_init();
+
+ if (pool_conf_status(conf) != POF_INVALID) {
+ /*
+ * Already opened configuration, return PO_FAIL
+ */
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ if (oflags & ~(PO_RDONLY | PO_RDWR | PO_CREAT | PO_DISCO | PO_UPDATE |
+ PO_TEMP)) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+
+ /*
+ * Creating a configuration implies read-write access, so make
+ * sure that PO_RDWR is set in addition if PO_CREAT is set.
+ */
+ if (oflags & PO_CREAT)
+ oflags |= PO_RDWR;
+
+ /* location is ignored when creating a temporary configuration */
+ if (oflags & PO_TEMP)
+ location = "";
+
+ if ((conf->pc_location = strdup(location)) == NULL) {
+ pool_seterror(POE_SYSTEM);
+ return (PO_FAIL);
+ }
+ /*
+ * This is the crossover point into the actual data provider
+ * implementation, allocate a data provider of the appropriate
+ * type for your data storage medium. In this case it's either a kernel
+ * or xml data provider. To use a different data provider, write some
+ * code to implement all the required interfaces and then change the
+ * following code to allocate a data provider which uses your new code.
+ * All data provider routines can be static, apart from the allocation
+ * routine.
+ *
+ * For temporary pools (PO_TEMP) we start with a copy of the current
+ * dynamic configuration and do all of the updates in-memory.
+ */
+ if (oflags & PO_TEMP) {
+ if (pool_knl_connection_alloc(conf, PO_TEMP) != PO_SUCCESS) {
+ conf->pc_state = POF_INVALID;
+ return (PO_FAIL);
+ }
+ /* set rdwr flag so we can updated the in-memory config. */
+ conf->pc_prov->pc_oflags |= PO_RDWR;
+
+ } else if (strcmp(location, pool_dynamic_location()) == 0) {
+ if (pool_knl_connection_alloc(conf, oflags) != PO_SUCCESS) {
+ conf->pc_state = POF_INVALID;
+ return (PO_FAIL);
+ }
+ } else {
+ if (pool_xml_connection_alloc(conf, oflags) != PO_SUCCESS) {
+ conf->pc_state = POF_INVALID;
+ return (PO_FAIL);
+ }
+ }
+ return (PO_SUCCESS);
+}
+
+/*
+ * Rollback a configuration. This will undo all changes to the configuration
+ * since the last time pool_conf_commit was called.
+ */
+int
+pool_conf_rollback(pool_conf_t *conf)
+{
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ return (conf->pc_prov->pc_rollback(conf));
+}
+
+/*
+ * Commit a configuration. This will apply all changes to the
+ * configuration to the permanent data store. The active parameter
+ * indicates whether the configuration should be used to update the
+ * dynamic configuration from the supplied (static) configuration or
+ * whether it should be written back to persistent store.
+ */
+int
+pool_conf_commit(pool_conf_t *conf, int active)
+{
+ int retval;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ if (active) {
+ int oflags;
+
+ if (conf_is_dynamic(conf) == PO_TRUE) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ /*
+ * Pretend that the configuration was opened PO_RDWR
+ * so that a configuration which was opened PO_RDONLY
+ * can be committed. The original flags are preserved
+ * in oflags and restored after pool_conf_commit_sys()
+ * returns.
+ */
+ oflags = conf->pc_prov->pc_oflags;
+ conf->pc_prov->pc_oflags |= PO_RDWR;
+ retval = pool_conf_commit_sys(conf, active);
+ conf->pc_prov->pc_oflags = oflags;
+ } else {
+ /*
+ * Write the configuration back to the backing store.
+ */
+ retval = conf->pc_prov->pc_commit(conf);
+ }
+ return (retval);
+}
+
+/*
+ * Export a configuration. This will export a configuration in the specified
+ * format (fmt) to the specified location.
+ */
+int
+pool_conf_export(const pool_conf_t *conf, const char *location,
+ pool_export_format_t fmt)
+{
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ return (conf->pc_prov->pc_export(conf, location, fmt));
+}
+
+/*
+ * Validate a configuration. This will validate a configuration at the
+ * specified level.
+ */
+int
+pool_conf_validate(const pool_conf_t *conf, pool_valid_level_t level)
+{
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ return (conf->pc_prov->pc_validate(conf, level));
+}
+
+/*
+ * Update the snapshot of a configuration. This can only be used on a
+ * dynamic configuration.
+ */
+int
+pool_conf_update(const pool_conf_t *conf, int *changed)
+{
+ if (pool_conf_status(conf) == POF_INVALID ||
+ conf_is_dynamic(conf) == PO_FALSE) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ /*
+ * Since this function only makes sense for dynamic
+ * configurations, just call directly into the appropriate
+ * function. This could be added into the pool_connection_t
+ * interface if it was ever required.
+ */
+ if (changed)
+ *changed = 0;
+ return (pool_knl_update((pool_conf_t *)conf, changed));
+}
+
+/*
+ * Walk the properties of the supplied elem, calling the user supplied
+ * function repeatedly as long as the user function returns
+ * PO_SUCCESS.
+ */
+int
+pool_walk_properties(pool_conf_t *conf, pool_elem_t *elem, void *arg,
+ int (*prop_callback)(pool_conf_t *, pool_elem_t *, const char *,
+ pool_value_t *, void *))
+{
+ return (pool_walk_any_properties(conf, elem, arg, prop_callback, 0));
+}
+
+void
+free_value_list(int npvals, pool_value_t **pvals)
+{
+ int j;
+
+ for (j = 0; j < npvals; j++) {
+ if (pvals[j])
+ pool_value_free(pvals[j]);
+ }
+ free(pvals);
+}
+
+/*
+ * Walk the properties of the supplied elem, calling the user supplied
+ * function repeatedly as long as the user function returns
+ * PO_SUCCESS.
+ * The list of properties to be walked is retrieved from the element
+ */
+int
+pool_walk_any_properties(pool_conf_t *conf, pool_elem_t *elem, void *arg,
+ int (*prop_callback)(pool_conf_t *, pool_elem_t *, const char *,
+ pool_value_t *, void *), int any)
+{
+ pool_value_t **pvals;
+ int i;
+ const pool_prop_t *props = provider_get_props(elem);
+ uint_t npvals;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+
+ if (props == NULL) {
+ pool_seterror(POE_INVALID_CONF);
+ return (PO_FAIL);
+ }
+
+ if ((pvals = elem->pe_get_props(elem, &npvals)) == NULL)
+ return (PO_FAIL);
+
+ /*
+ * Now walk the managed properties. As we find managed
+ * properties removed them from the list of all properties to
+ * prevent duplication.
+ */
+ for (i = 0; props[i].pp_pname != NULL; i++) {
+ int j;
+
+ /*
+ * Special processing for type
+ */
+ if (strcmp(props[i].pp_pname, c_type) == 0) {
+ pool_value_t val = POOL_VALUE_INITIALIZER;
+
+ if (pool_value_set_name(&val, props[i].pp_pname) ==
+ PO_FAIL) {
+ free_value_list(npvals, pvals);
+ return (PO_FAIL);
+ }
+ if (props[i].pp_op.ppo_get_value(elem, &val) ==
+ PO_FAIL) {
+ free_value_list(npvals, pvals);
+ return (PO_FAIL);
+ }
+ if (any == 1 || prop_is_hidden(&props[i]) == PO_FALSE) {
+ if (prop_callback(conf, elem, props[i].pp_pname,
+ &val, arg) != PO_SUCCESS) {
+ free_value_list(npvals, pvals);
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ }
+ continue;
+ }
+
+ for (j = 0; j < npvals; j++) {
+ if (pvals[j] && strcmp(pool_value_get_name(pvals[j]),
+ props[i].pp_pname) == 0)
+ break;
+ }
+ /*
+ * If we have found the property, then j < npvals. Process it
+ * according to our property attributes. Otherwise, it's not
+ * a managed property, so just ignore it until later.
+ */
+ if (j < npvals) {
+ if (any == 1 || prop_is_hidden(&props[i]) == PO_FALSE) {
+ if (props[i].pp_op.ppo_get_value) {
+ if (pool_value_set_name(pvals[j],
+ props[i].pp_pname) == PO_FAIL) {
+ free_value_list(npvals, pvals);
+ return (PO_FAIL);
+ }
+ if (props[i].pp_op.ppo_get_value(elem,
+ pvals[j]) == PO_FAIL) {
+ free_value_list(npvals, pvals);
+ return (PO_FAIL);
+ }
+ }
+ if (prop_callback(conf, elem, props[i].pp_pname,
+ pvals[j], arg) != PO_SUCCESS) {
+ free_value_list(npvals, pvals);
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ }
+ pool_value_free(pvals[j]);
+ pvals[j] = NULL;
+ }
+ }
+ for (i = 0; i < npvals; i++) {
+ if (pvals[i]) {
+ const char *name = pool_value_get_name(pvals[i]);
+ char *qname = strrchr(name, '.');
+ if ((qname && qname[1] != '_') ||
+ (!qname && name[0] != '_')) {
+ if (prop_callback(conf, elem, name, pvals[i],
+ arg) != PO_SUCCESS) {
+ free_value_list(npvals, pvals);
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ }
+ pool_value_free(pvals[i]);
+ pvals[i] = NULL;
+ }
+ }
+ free(pvals);
+ return (PO_SUCCESS);
+}
+
+/*
+ * Return a pool, searching the supplied configuration for a pool with the
+ * supplied name. The search is case sensitive.
+ */
+pool_t *
+pool_get_pool(const pool_conf_t *conf, const char *name)
+{
+ pool_value_t *props[] = { NULL, NULL };
+ pool_t **rs;
+ pool_t *ret;
+ uint_t size = 0;
+ pool_value_t val = POOL_VALUE_INITIALIZER;
+
+ props[0] = &val;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ if (pool_value_set_name(props[0], "pool.name") != PO_SUCCESS ||
+ pool_value_set_string(props[0], name) != PO_SUCCESS) {
+ return (NULL);
+ }
+ rs = pool_query_pools(conf, &size, props);
+ if (rs == NULL) { /* Can't find a pool to match the name */
+ return (NULL);
+ }
+ if (size != 1) {
+ free(rs);
+ pool_seterror(POE_INVALID_CONF);
+ return (NULL);
+ }
+ ret = rs[0];
+ free(rs);
+ return (ret);
+}
+
+/*
+ * Return a result set of pools, searching the supplied configuration
+ * for pools which match the supplied property criteria. props is a null
+ * terminated list of properties which will be used to match qualifying
+ * pools. size is updated with the size of the pool
+ */
+pool_t **
+pool_query_pools(const pool_conf_t *conf, uint_t *size, pool_value_t **props)
+{
+ pool_result_set_t *rs;
+ pool_elem_t *pe;
+ pool_t **result = NULL;
+ int i = 0;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+ rs = pool_exec_query(conf, NULL, NULL, PEC_QRY_POOL, props);
+ if (rs == NULL) {
+ return (NULL);
+ }
+ if ((*size = pool_rs_count(rs)) == 0) {
+ (void) pool_rs_close(rs);
+ return (NULL);
+ }
+ if ((result = malloc(sizeof (pool_t *) * (*size + 1))) == NULL) {
+ pool_seterror(POE_SYSTEM);
+ (void) pool_rs_close(rs);
+ return (NULL);
+ }
+ (void) memset(result, 0, sizeof (pool_t *) * (*size + 1));
+ for (pe = rs->prs_next(rs); pe != NULL; pe = rs->prs_next(rs)) {
+ if (pool_elem_class(pe) != PEC_POOL) {
+ pool_seterror(POE_INVALID_CONF);
+ free(result);
+ (void) pool_rs_close(rs);
+ return (NULL);
+ }
+ result[i++] = pool_elem_pool(pe);
+ }
+ (void) pool_rs_close(rs);
+ return (result);
+}
+
+/*
+ * Return an res, searching the supplied configuration for an res with the
+ * supplied name. The search is case sensitive.
+ */
+pool_resource_t *
+pool_get_resource(const pool_conf_t *conf, const char *sz_type,
+ const char *name)
+{
+ pool_value_t *props[] = { NULL, NULL, NULL };
+ pool_resource_t **rs;
+ pool_resource_t *ret;
+ uint_t size = 0;
+ char_buf_t *cb = NULL;
+ pool_value_t val0 = POOL_VALUE_INITIALIZER;
+ pool_value_t val1 = POOL_VALUE_INITIALIZER;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ if (sz_type == NULL) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ props[0] = &val0;
+ props[1] = &val1;
+
+ if (pool_value_set_string(props[0], sz_type) != PO_SUCCESS ||
+ pool_value_set_name(props[0], c_type) != PO_SUCCESS)
+ return (NULL);
+
+ if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL) {
+ return (NULL);
+ }
+ if (set_char_buf(cb, "%s.name", sz_type) != PO_SUCCESS) {
+ free_char_buf(cb);
+ return (NULL);
+ }
+ if (pool_value_set_name(props[1], cb->cb_buf) != PO_SUCCESS) {
+ free_char_buf(cb);
+ return (NULL);
+ }
+ if (pool_value_set_string(props[1], name) != PO_SUCCESS) {
+ free_char_buf(cb);
+ return (NULL);
+ }
+ free_char_buf(cb);
+ rs = pool_query_resources(conf, &size, props);
+ if (rs == NULL) {
+ return (NULL);
+ }
+ if (size != 1) {
+ free(rs);
+ pool_seterror(POE_INVALID_CONF);
+ return (NULL);
+ }
+ ret = rs[0];
+ free(rs);
+ return (ret);
+}
+
+/*
+ * Return a result set of res (actually as pool_elem_ts), searching the
+ * supplied configuration for res which match the supplied property
+ * criteria. props is a null terminated list of properties which will be used
+ * to match qualifying res.
+ */
+pool_resource_t **
+pool_query_resources(const pool_conf_t *conf, uint_t *size,
+ pool_value_t **props)
+{
+ pool_result_set_t *rs;
+ pool_elem_t *pe;
+ pool_resource_t **result = NULL;
+ int i = 0;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ *size = 0;
+
+ rs = pool_exec_query(conf, NULL, NULL, PEC_QRY_RES, props);
+ if (rs == NULL) {
+ return (NULL);
+ }
+ if ((*size = pool_rs_count(rs)) == 0) {
+ (void) pool_rs_close(rs);
+ return (NULL);
+ }
+ if ((result = malloc(sizeof (pool_resource_t *) * (*size + 1)))
+ == NULL) {
+ pool_seterror(POE_SYSTEM);
+ (void) pool_rs_close(rs);
+ return (NULL);
+ }
+ (void) memset(result, 0, sizeof (pool_resource_t *) * (*size + 1));
+ for (pe = rs->prs_next(rs); pe != NULL; pe = rs->prs_next(rs)) {
+ if (pool_elem_class(pe) != PEC_RES_COMP &&
+ pool_elem_class(pe) != PEC_RES_AGG) {
+ pool_seterror(POE_INVALID_CONF);
+ free(result);
+ (void) pool_rs_close(rs);
+ return (NULL);
+ }
+ result[i++] = pool_elem_res(pe);
+ }
+ (void) pool_rs_close(rs);
+ return (result);
+}
+
+/*
+ * Return a result set of comp (actually as pool_elem_ts), searching the
+ * supplied configuration for comp which match the supplied property
+ * criteria. props is a null terminated list of properties which will be used
+ * to match qualifying comp.
+ */
+pool_component_t **
+pool_query_components(const pool_conf_t *conf, uint_t *size,
+ pool_value_t **props)
+{
+ return (pool_query_resource_components(conf, NULL, size, props));
+}
+
+/*
+ * Destroy a pool. If the pool cannot be found or removed an error is
+ * returned. This is basically a wrapper around pool_elem_remove to ensure
+ * some type safety for the pool subtype.
+ */
+int
+pool_destroy(pool_conf_t *conf, pool_t *pp)
+{
+ pool_elem_t *pe;
+
+ if (pool_conf_check(conf) != PO_SUCCESS)
+ return (PO_FAIL);
+
+ pe = TO_ELEM(pp);
+
+ /*
+ * Cannot destroy the default pool.
+ */
+ if (elem_is_default(pe) == PO_TRUE) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ if (pool_elem_remove(pe) != PO_SUCCESS)
+ return (PO_FAIL);
+ return (PO_SUCCESS);
+}
+
+/*
+ * Destroy an res. If the res cannot be found or removed an error is
+ * returned. This is basically a wrapper around pool_elem_remove to ensure
+ * some type safety for the res subtype.
+ */
+int
+pool_resource_destroy(pool_conf_t *conf, pool_resource_t *prs)
+{
+ pool_elem_t *pe;
+ pool_component_t **rl;
+ uint_t res_size;
+ pool_t **pl;
+ uint_t npool;
+ int i;
+
+ if (pool_conf_check(conf) != PO_SUCCESS)
+ return (PO_FAIL);
+
+ pe = TO_ELEM(prs);
+
+ if (resource_is_system(prs) == PO_TRUE) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ /*
+ * Walk all the pools and dissociate any pools which are using
+ * this resource.
+ */
+ if ((pl = pool_query_pools(conf, &npool, NULL)) != NULL) {
+ for (i = 0; i < npool; i++) {
+ pool_resource_t **rl;
+ uint_t nres;
+ int j;
+
+ if ((rl = pool_query_pool_resources(conf, pl[i], &nres,
+ NULL)) != NULL) {
+ for (j = 0; j < nres; j++) {
+ if (rl[j] == prs) {
+ if (pool_dissociate(conf, pl[i],
+ rl[j]) != PO_SUCCESS) {
+ free(rl);
+ free(pl);
+ return (PO_FAIL);
+ }
+ break;
+ }
+ }
+ free(rl);
+ }
+ }
+ free(pl);
+ }
+ if (pe->pe_class == PEC_RES_COMP) {
+ pool_resource_t *default_set_res;
+
+ /*
+ * Use the xtransfer option to move comp around
+ */
+ default_set_res = (pool_resource_t *)get_default_resource(prs);
+
+ if ((rl = pool_query_resource_components(conf, prs, &res_size,
+ NULL)) != NULL) {
+ int ostate = conf->pc_state;
+ conf->pc_state = POF_DESTROY;
+ if (pool_resource_xtransfer(conf, prs, default_set_res,
+ rl) == PO_FAIL) {
+ free(rl);
+ conf->pc_state = ostate;
+ return (PO_FAIL);
+ }
+ conf->pc_state = ostate;
+ free(rl);
+ }
+ }
+ if (pool_elem_remove(pe) != PO_SUCCESS)
+ return (PO_FAIL);
+ return (PO_SUCCESS);
+}
+
+/*
+ * Destroy a comp. If the comp cannot be found or removed an error is
+ * returned. This is basically a wrapper around pool_elem_remove to ensure
+ * some type safety for the comp subtype.
+ */
+int
+pool_component_destroy(pool_component_t *pr)
+{
+ pool_elem_t *pe = TO_ELEM(pr);
+
+ if (pool_elem_remove(pe) != PO_SUCCESS)
+ return (PO_FAIL);
+ return (PO_SUCCESS);
+}
+
+/*
+ * Remove a pool_elem_t from a configuration. This has been "hidden" away as
+ * a static routine since the only elements which are currently being removed
+ * are pools, res & comp and the wrapper functions above provide type-safe
+ * access. However, if there is a need to remove other types of elements
+ * then this could be promoted to pool_impl.h or more wrappers could
+ * be added to pool_impl.h.
+ */
+int
+pool_elem_remove(pool_elem_t *pe)
+{
+ return (pe->pe_remove(pe));
+}
+
+/*
+ * Execute a query to search for a qualifying set of elements.
+ */
+pool_result_set_t *
+pool_exec_query(const pool_conf_t *conf, const pool_elem_t *src,
+ const char *src_attr, pool_elem_class_t classes, pool_value_t **props)
+{
+ return (conf->pc_prov->pc_exec_query(conf, src, src_attr, classes,
+ props));
+}
+
+/*
+ * Get the next result from a result set of elements.
+ */
+pool_elem_t *
+pool_rs_next(pool_result_set_t *set)
+{
+ return (set->prs_next(set));
+}
+
+/*
+ * Get the previous result from a result set of elements.
+ */
+pool_elem_t *
+pool_rs_prev(pool_result_set_t *set)
+{
+ return (set->prs_prev(set));
+}
+
+/*
+ * Get the first result from a result set of elements.
+ */
+pool_elem_t *
+pool_rs_first(pool_result_set_t *set)
+{
+ return (set->prs_first(set));
+}
+
+/*
+ * Get the last result from a result set of elements.
+ */
+pool_elem_t *
+pool_rs_last(pool_result_set_t *set)
+{
+ return (set->prs_last(set));
+}
+
+
+/*
+ * Get the count for a result set of elements.
+ */
+int
+pool_rs_count(pool_result_set_t *set)
+{
+ return (set->prs_count(set));
+}
+
+/*
+ * Get the index for a result set of elements.
+ */
+int
+pool_rs_get_index(pool_result_set_t *set)
+{
+ return (set->prs_get_index(set));
+}
+
+/*
+ * Set the index for a result set of elements.
+ */
+int
+pool_rs_set_index(pool_result_set_t *set, int index)
+{
+ return (set->prs_set_index(set, index));
+}
+
+/*
+ * Close a result set of elements, freeing all associated resources.
+ */
+int
+pool_rs_close(pool_result_set_t *set)
+{
+ return (set->prs_close(set));
+}
+
+/*
+ * When transferring resource components using pool_resource_transfer,
+ * this function is invoked to choose which actual components will be
+ * transferred.
+ */
+int
+choose_components(pool_resource_t *src, pool_resource_t *dst, uint64_t size)
+{
+ pool_component_t **components = NULL, *moved[] = { NULL, NULL };
+ int i;
+ uint_t ncomponent;
+ pool_conf_t *conf = TO_CONF(TO_ELEM(src));
+
+ if (size == 0)
+ return (PO_SUCCESS);
+ /*
+ * Get the component list from our src component.
+ */
+ if ((components = pool_query_resource_components(conf, src, &ncomponent,
+ NULL)) == NULL) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ qsort(components, ncomponent, sizeof (pool_elem_t *),
+ qsort_elem_compare);
+ /*
+ * Components that aren't specifically requested by the resource
+ * should be transferred out first.
+ */
+ for (i = 0; size > 0 && components[i] != NULL; i++) {
+ if (!cpu_is_requested(components[i])) {
+ moved[0] = components[i];
+ if (pool_resource_xtransfer(conf, src, dst, moved) ==
+ PO_SUCCESS) {
+ size--;
+ }
+ }
+ }
+
+ /*
+ * If we couldn't find enough "un-requested" components, select random
+ * requested components.
+ */
+ for (i = 0; size > 0 && components[i] != NULL; i++) {
+ if (cpu_is_requested(components[i])) {
+ moved[0] = components[i];
+ if (pool_resource_xtransfer(conf, src, dst, moved) ==
+ PO_SUCCESS) {
+ size--;
+ }
+ }
+ }
+
+ free(components);
+ /*
+ * If we couldn't transfer out all the resources we asked for, then
+ * return error.
+ */
+ return (size == 0 ? PO_SUCCESS : PO_FAIL);
+}
+
+/*
+ * Common processing for a resource transfer (xfer or xxfer).
+ *
+ * - Return XFER_CONTINUE if the transfer should proceeed
+ * - Return XFER_FAIL if the transfer should be stopped in failure
+ * - Return XFER_SUCCESS if the transfer should be stopped in success
+ */
+int
+setup_transfer(pool_conf_t *conf, pool_resource_t *src, pool_resource_t *tgt,
+ uint64_t size, uint64_t *src_size, uint64_t *tgt_size)
+{
+ uint64_t src_min;
+ uint64_t tgt_max;
+
+ if (pool_conf_check(conf) != PO_SUCCESS)
+ return (XFER_FAIL);
+
+ /*
+ * Makes sure the two resources are of the same type
+ */
+ if (pool_resource_elem_class(TO_ELEM(src)) !=
+ pool_resource_elem_class(TO_ELEM(tgt))) {
+ pool_seterror(POE_BADPARAM);
+ return (XFER_FAIL);
+ }
+
+ /*
+ * Transferring to yourself is a no-op
+ */
+ if (src == tgt)
+ return (XFER_SUCCESS);
+
+ /*
+ * Transferring nothing is a no-op
+ */
+ if (size == 0)
+ return (XFER_SUCCESS);
+
+ if (resource_get_min(src, &src_min) != PO_SUCCESS ||
+ resource_get_size(src, src_size) != PO_SUCCESS ||
+ resource_get_max(tgt, &tgt_max) != PO_SUCCESS ||
+ resource_get_size(tgt, tgt_size) != PO_SUCCESS) {
+ pool_seterror(POE_BADPARAM);
+ return (XFER_FAIL);
+ }
+ if (pool_conf_status(conf) != POF_DESTROY) {
+ /*
+ * src_size - donating >= src.min
+ * size + receiving <= tgt.max (except for default)
+ */
+#ifdef DEBUG
+ dprintf("conf is %s\n", pool_conf_location(conf));
+ dprintf("setup_transfer: src_size %llu\n", *src_size);
+ pool_elem_dprintf(TO_ELEM(src));
+ dprintf("setup_transfer: tgt_size %llu\n", *tgt_size);
+ pool_elem_dprintf(TO_ELEM(tgt));
+#endif /* DEBUG */
+ if (*src_size - size < src_min ||
+ (resource_is_default(tgt) == PO_FALSE &&
+ *tgt_size + size > tgt_max)) {
+ pool_seterror(POE_INVALID_CONF);
+ return (XFER_FAIL);
+ }
+ }
+ return (XFER_CONTINUE);
+}
+
+/*
+ * Transfer resource quantities from one resource set to another.
+ */
+int
+pool_resource_transfer(pool_conf_t *conf, pool_resource_t *src,
+ pool_resource_t *tgt, uint64_t size)
+{
+ uint64_t src_size;
+ uint64_t tgt_size;
+ int ret;
+
+ if ((ret = setup_transfer(conf, src, tgt, size, &src_size, &tgt_size))
+ != XFER_CONTINUE)
+ return (ret);
+ /*
+ * If this resource is a res_comp we must call move components
+ */
+ if (pool_elem_class(TO_ELEM(src)) == PEC_RES_COMP)
+ return (choose_components(src, tgt, size));
+ /*
+ * Now do the transfer.
+ */
+ ret = conf->pc_prov->pc_res_xfer(src, tgt, size);
+ /*
+ * Modify the sizes of the resource sets if the process was
+ * successful
+ */
+ if (ret == PO_SUCCESS) {
+ pool_value_t val = POOL_VALUE_INITIALIZER;
+
+ src_size -= size;
+ tgt_size += size;
+ pool_value_set_uint64(&val, src_size);
+ (void) pool_put_any_ns_property(TO_ELEM(src), c_size_prop,
+ &val);
+ pool_value_set_uint64(&val, tgt_size);
+ (void) pool_put_any_ns_property(TO_ELEM(tgt), c_size_prop,
+ &val);
+ }
+ return (ret);
+}
+
+/*
+ * Transfer resource components from one resource set to another.
+ */
+int
+pool_resource_xtransfer(pool_conf_t *conf, pool_resource_t *src,
+ pool_resource_t *tgt,
+ pool_component_t **rl)
+{
+ int i;
+ uint64_t src_size;
+ uint64_t tgt_size;
+ uint64_t size;
+ int ret;
+
+ /*
+ * Make sure the components are all contained in 'src'. This
+ * processing must be done before setup_transfer so that size
+ * is known.
+ */
+ for (i = 0; rl[i] != NULL; i++) {
+#ifdef DEBUG
+ dprintf("resource xtransfer\n");
+ dprintf("in conf %s\n", pool_conf_location(conf));
+ dprintf("transferring component\n");
+ pool_elem_dprintf(TO_ELEM(rl[i]));
+ dprintf("from\n");
+ pool_elem_dprintf(TO_ELEM(src));
+ dprintf("to\n");
+ pool_elem_dprintf(TO_ELEM(tgt));
+#endif /* DEBUG */
+
+ if (pool_get_owning_resource(conf, rl[i]) != src) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ }
+
+ size = (uint64_t)i;
+
+ if ((ret = setup_transfer(conf, src, tgt, size, &src_size, &tgt_size))
+ != XFER_CONTINUE)
+ return (ret);
+
+ ret = conf->pc_prov->pc_res_xxfer(src, tgt, rl);
+ /*
+ * Modify the sizes of the resource sets if the process was
+ * successful
+ */
+ if (ret == PO_SUCCESS) {
+ pool_value_t val = POOL_VALUE_INITIALIZER;
+
+#ifdef DEBUG
+ dprintf("src_size %llu\n", src_size);
+ dprintf("tgt_size %llu\n", tgt_size);
+ dprintf("size %llu\n", size);
+#endif /* DEBUG */
+ src_size -= size;
+ tgt_size += size;
+ pool_value_set_uint64(&val, src_size);
+ (void) pool_put_any_ns_property(TO_ELEM(src), c_size_prop,
+ &val);
+ pool_value_set_uint64(&val, tgt_size);
+ (void) pool_put_any_ns_property(TO_ELEM(tgt), c_size_prop,
+ &val);
+ }
+ return (ret);
+}
+
+/*
+ * Find the owning resource for a resource component.
+ */
+pool_resource_t *
+pool_get_owning_resource(const pool_conf_t *conf, const pool_component_t *comp)
+{
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+ return (pool_elem_res(pool_get_container(TO_ELEM(comp))));
+}
+
+/*
+ * pool_get_container() returns the container of pc.
+ */
+pool_elem_t *
+pool_get_container(const pool_elem_t *pc)
+{
+ return (pc->pe_get_container(pc));
+}
+
+/*
+ * pool_set_container() moves pc so that it is contained by pp.
+ *
+ * Returns PO_SUCCESS/PO_FAIL
+ */
+int
+pool_set_container(pool_elem_t *pp, pool_elem_t *pc)
+{
+ return (pc->pe_set_container(pp, pc));
+}
+
+/*
+ * Conversion routines for converting to and from elem and it's various
+ * subtypes of system, pool, res and comp.
+ */
+pool_elem_t *
+pool_system_elem(const pool_system_t *ph)
+{
+ return ((pool_elem_t *)ph);
+}
+
+pool_elem_t *
+pool_conf_to_elem(const pool_conf_t *conf)
+{
+ pool_system_t *sys;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+ if ((sys = pool_conf_system(conf)) == NULL) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+ return (pool_system_elem(sys));
+}
+
+pool_elem_t *
+pool_to_elem(const pool_conf_t *conf, const pool_t *pp)
+{
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+ return ((pool_elem_t *)pp);
+}
+
+pool_elem_t *
+pool_resource_to_elem(const pool_conf_t *conf, const pool_resource_t *prs)
+{
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+ return ((pool_elem_t *)prs);
+}
+
+pool_elem_t *
+pool_component_to_elem(const pool_conf_t *conf, const pool_component_t *pr)
+{
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+ return ((pool_elem_t *)pr);
+}
+
+/*
+ * Walk all the pools of the configuration calling the user supplied function
+ * as long as the user function continues to return PO_TRUE
+ */
+int
+pool_walk_pools(pool_conf_t *conf, void *arg,
+ int (*callback)(pool_conf_t *conf, pool_t *pool, void *arg))
+{
+ pool_t **rs;
+ int i;
+ uint_t size;
+ int error = PO_SUCCESS;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+
+ if ((rs = pool_query_pools(conf, &size, NULL)) == NULL) /* None */
+ return (PO_SUCCESS);
+ for (i = 0; i < size; i++)
+ if (callback(conf, rs[i], arg) != PO_SUCCESS) {
+ error = PO_FAIL;
+ break;
+ }
+ free(rs);
+ return (error);
+}
+
+/*
+ * Walk all the comp of the res calling the user supplied function
+ * as long as the user function continues to return PO_TRUE
+ */
+int
+pool_walk_components(pool_conf_t *conf, pool_resource_t *prs, void *arg,
+ int (*callback)(pool_conf_t *conf, pool_component_t *pr, void *arg))
+{
+ pool_component_t **rs;
+ int i;
+ uint_t size;
+ int error = PO_SUCCESS;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+
+ if ((rs = pool_query_resource_components(conf, prs, &size, NULL)) ==
+ NULL)
+ return (PO_SUCCESS); /* None */
+ for (i = 0; i < size; i++)
+ if (callback(conf, rs[i], arg) != PO_SUCCESS) {
+ error = PO_FAIL;
+ break;
+ }
+ free(rs);
+ return (error);
+}
+
+/*
+ * Return an array of all matching res for the supplied pool.
+ */
+pool_resource_t **
+pool_query_pool_resources(const pool_conf_t *conf, const pool_t *pp,
+ uint_t *size, pool_value_t **props)
+{
+ pool_result_set_t *rs;
+ pool_elem_t *pe;
+ pool_resource_t **result = NULL;
+ int i = 0;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+
+ pe = TO_ELEM(pp);
+
+ rs = pool_exec_query(conf, pe, "res", PEC_QRY_RES, props);
+ if (rs == NULL) {
+ return (NULL);
+ }
+ if ((*size = pool_rs_count(rs)) == 0) {
+ (void) pool_rs_close(rs);
+ return (NULL);
+ }
+ if ((result = malloc(sizeof (pool_resource_t *) * (*size + 1)))
+ == NULL) {
+ pool_seterror(POE_SYSTEM);
+ (void) pool_rs_close(rs);
+ return (NULL);
+ }
+ (void) memset(result, 0, sizeof (pool_resource_t *) * (*size + 1));
+ for (pe = rs->prs_next(rs); pe != NULL; pe = rs->prs_next(rs)) {
+ if (pool_elem_class(pe) != PEC_RES_COMP &&
+ pool_elem_class(pe) != PEC_RES_AGG) {
+ pool_seterror(POE_INVALID_CONF);
+ free(result);
+ (void) pool_rs_close(rs);
+ return (NULL);
+ }
+ result[i++] = pool_elem_res(pe);
+ }
+ (void) pool_rs_close(rs);
+ return (result);
+}
+
+/*
+ * Walk all the res of the pool calling the user supplied function
+ * as long as the user function continues to return PO_TRUE
+ */
+int
+pool_walk_resources(pool_conf_t *conf, pool_t *pp, void *arg,
+ int (*callback)(pool_conf_t *, pool_resource_t *, void *))
+{
+ pool_resource_t **rs;
+ int i;
+ uint_t size;
+ int error = PO_SUCCESS;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ if ((rs = pool_query_pool_resources(conf, pp, &size, NULL)) == NULL)
+ return (PO_SUCCESS); /* None */
+ for (i = 0; i < size; i++)
+ if (callback(conf, rs[i], arg) != PO_SUCCESS) {
+ error = PO_FAIL;
+ break;
+ }
+ free(rs);
+ return (error);
+}
+
+/*
+ * Return a result set of all comp for the supplied res.
+ */
+pool_component_t **
+pool_query_resource_components(const pool_conf_t *conf,
+ const pool_resource_t *prs, uint_t *size, pool_value_t **props)
+{
+ pool_result_set_t *rs;
+ pool_elem_t *pe;
+ pool_component_t **result = NULL;
+ int i = 0;
+
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (NULL);
+ }
+ pe = TO_ELEM(prs);
+
+ rs = pool_exec_query(conf, pe, NULL, PEC_QRY_COMP, props);
+ if (rs == NULL) {
+ return (NULL);
+ }
+ if ((*size = pool_rs_count(rs)) == 0) {
+ (void) pool_rs_close(rs);
+ return (NULL);
+ }
+ if ((result = malloc(sizeof (pool_component_t *) * (*size + 1)))
+ == NULL) {
+ pool_seterror(POE_SYSTEM);
+ (void) pool_rs_close(rs);
+ return (NULL);
+ }
+ (void) memset(result, 0, sizeof (pool_component_t *) * (*size + 1));
+ for (pe = rs->prs_next(rs); pe != NULL; pe = rs->prs_next(rs)) {
+ if (pool_elem_class(pe) != PEC_COMP) {
+ pool_seterror(POE_INVALID_CONF);
+ free(result);
+ (void) pool_rs_close(rs);
+ return (NULL);
+ }
+ result[i++] = pool_elem_comp(pe);
+ }
+ (void) pool_rs_close(rs);
+ return (result);
+}
+
+/*
+ * pool_version() returns the version of this library, depending on the supplied
+ * parameter.
+ *
+ * Returns: library version depening on the supplied ver parameter.
+ */
+uint_t
+pool_version(uint_t ver)
+{
+ switch (ver) {
+ case POOL_VER_NONE:
+ break;
+ case POOL_VER_CURRENT:
+ pool_workver = ver;
+ break;
+ default:
+ return (POOL_VER_NONE);
+ }
+ return (pool_workver);
+}
+
+/*
+ * pool_associate() associates the supplied resource to the supplied pool.
+ *
+ * Returns: PO_SUCCESS/PO_FAIL
+ */
+int
+pool_associate(pool_conf_t *conf, pool_t *pool, const pool_resource_t *res)
+{
+ if (pool_conf_check(conf) != PO_SUCCESS)
+ return (PO_FAIL);
+
+ return (pool->pp_associate(pool, res));
+}
+
+/*
+ * pool_dissociate() dissociates the supplied resource from the supplied pool.
+ *
+ * Returns: PO_SUCCESS/PO_FAIL
+ */
+int
+pool_dissociate(pool_conf_t *conf, pool_t *pool, const pool_resource_t *res)
+{
+ if (pool_conf_check(conf) != PO_SUCCESS)
+ return (PO_FAIL);
+
+ if (elem_is_default(TO_ELEM(res)))
+ return (PO_SUCCESS);
+ return (pool->pp_dissociate(pool, res));
+}
+
+/*
+ * Compare two elements for purposes of ordering.
+ * Return:
+ * < 0 if e1 is "before" e2
+ * 0 if e1 "equals" e2
+ * > 0 if e1 comes after e2
+ */
+int
+pool_elem_compare_name(const pool_elem_t *e1, const pool_elem_t *e2)
+{
+ char *name1, *name2;
+ pool_value_t val = POOL_VALUE_INITIALIZER;
+ int retval;
+
+ /*
+ * We may be asked to compare two elements from different classes.
+ * They are different so return (1).
+ */
+ if (pool_elem_same_class(e1, e2) != PO_TRUE)
+ return (1);
+
+ /*
+ * If the class is PEC_SYSTEM, always match them
+ */
+ if (pool_elem_class(e1) == PEC_SYSTEM)
+ return (0);
+
+ /*
+ * If we are going to compare components, then use sys_id
+ */
+ if (pool_elem_class(e1) == PEC_COMP) {
+ int64_t sys_id1, sys_id2;
+
+ if (pool_get_ns_property(e1, c_sys_prop, &val) == POC_INVAL) {
+ return (-1);
+ }
+ (void) pool_value_get_int64(&val, &sys_id1);
+ if (pool_get_ns_property(e2, c_sys_prop, &val) == POC_INVAL) {
+ return (-1);
+ }
+ (void) pool_value_get_int64(&val, &sys_id2);
+ retval = (sys_id1 - sys_id2);
+ } else {
+ if (pool_get_ns_property(e1, "name", &val) == POC_INVAL) {
+ return (-1);
+ }
+ (void) pool_value_get_string(&val, (const char **)&name1);
+ if ((name1 = strdup(name1)) == NULL) {
+ return (-1);
+ }
+
+ if (pool_get_ns_property(e2, "name", &val) == POC_INVAL) {
+ return (-1);
+ }
+
+ (void) pool_value_get_string(&val, (const char **)&name2);
+ retval = strcmp(name1, name2);
+ free(name1);
+ }
+ return (retval);
+}
+
+/*
+ * Compare two elements for purposes of ordering.
+ * Return:
+ * < 0 if e1 is "before" e2
+ * 0 if e1 "equals" e2
+ * > 0 if e1 comes after e2
+ */
+int
+pool_elem_compare(const pool_elem_t *e1, const pool_elem_t *e2)
+{
+ pool_value_t val = POOL_VALUE_INITIALIZER;
+ int64_t sys_id1, sys_id2;
+
+ /*
+ * We may be asked to compare two elements from different classes.
+ * They are different so return the difference in their classes
+ */
+ if (pool_elem_same_class(e1, e2) != PO_TRUE)
+ return (1);
+
+ /*
+ * If the class is PEC_SYSTEM, always match them
+ */
+ if (pool_elem_class(e1) == PEC_SYSTEM)
+ return (0);
+
+ /*
+ * Compare with sys_id
+ */
+ if (pool_get_ns_property(e1, c_sys_prop, &val) == POC_INVAL) {
+ assert(!"no sys_id on e1\n");
+ }
+ (void) pool_value_get_int64(&val, &sys_id1);
+ if (pool_get_ns_property(e2, c_sys_prop, &val) == POC_INVAL) {
+ assert(!"no sys_id on e2\n");
+ }
+ (void) pool_value_get_int64(&val, &sys_id2);
+ return (sys_id1 - sys_id2);
+}
+
+/*
+ * Return PO_TRUE if the supplied elems are of the same class.
+ */
+int
+pool_elem_same_class(const pool_elem_t *e1, const pool_elem_t *e2)
+{
+ if (pool_elem_class(e1) != pool_elem_class(e2))
+ return (PO_FALSE);
+
+ /*
+ * Check to make sure the fundamental class of the elements match
+ */
+ if (pool_elem_class(e1) == PEC_RES_COMP ||
+ pool_elem_class(e1) == PEC_RES_AGG)
+ if (pool_resource_elem_class(e1) !=
+ pool_resource_elem_class(e2))
+ return (PO_FALSE);
+ if (pool_elem_class(e1) == PEC_COMP)
+ if (pool_component_elem_class(e1) !=
+ pool_component_elem_class(e2))
+ return (PO_FALSE);
+ return (PO_TRUE);
+}
+
+/*
+ * pool_conf_check() checks that the configuration state isn't invalid
+ * and that the configuration was opened for modification.
+ */
+int
+pool_conf_check(const pool_conf_t *conf)
+{
+ if (pool_conf_status(conf) == POF_INVALID) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+
+ if ((conf->pc_prov->pc_oflags & PO_RDWR) == 0) {
+ pool_seterror(POE_BADPARAM);
+ return (PO_FAIL);
+ }
+ return (PO_SUCCESS);
+}