diff options
Diffstat (limited to 'usr/src/lib/libpool/common/pool.c')
-rw-r--r-- | usr/src/lib/libpool/common/pool.c | 2944 |
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); +} |