/* * 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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 mutex_t keylock; static thread_key_t errkey; static int keyonce = 0; /* * 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 ".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; } if (keyonce == 0) { (void) mutex_lock(&keylock); if (keyonce == 0) { (void) thr_keycreate(&errkey, 0); keyonce++; } (void) mutex_unlock(&keylock); } (void) thr_setspecific(errkey, (void *)(intptr_t)errval); } /* * Return the current value of the error code. * Returns: int error code */ int pool_error(void) { void *errval; if (thr_main()) return (pool_errval); if (keyonce == 0) return (POE_OK); (void) thr_getspecific(errkey, &errval); return ((intptr_t)errval); } /* * 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); }