2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Utility functions used by all of libpower
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/pm.h>
2N/A#include <stdlib.h>
2N/A#include <strings.h>
2N/A#include <ctype.h>
2N/A#include <libnvpair.h>
2N/A#include <libpower.h>
2N/A#include <libpower_impl.h>
2N/A
2N/A
2N/Astatic pm_error_t pm_result_update(nvlist_t *, pm_authority_t, data_type_t,
2N/A void *, uint_t);
2N/Astatic pm_error_t pm_result_create(nvlist_t *, pm_authority_t, const char *,
2N/A const char *, data_type_t, void *, uint_t);
2N/Astatic int match_pgname(nvlist_t *, const char *);
2N/Astatic pm_error_t pm_result_merge(nvlist_t **, nvlist_t *, const char *,
2N/A pm_authority_t);
2N/A
2N/A
2N/A/*
2N/A * Add a property and value to a result set to be returned to a caller of
2N/A * libpower. The result list has the structure:
2N/A *
2N/A * Name Type Description
2N/A * ------------------- ------ -------------------------------------
2N/A * property_name nvlist The list of available values and
2N/A * other information about the
2N/A * property. See below for the structure
2N/A * of this list.
2N/A *
2N/A * Name Type Description
2N/A * ------------------- ------ -------------------------------------
2N/A * PM_PROP_PGNAME string The name of the SMF property
2N/A * group to which the property belongs
2N/A * other information about the
2N/A * property.
2N/A *
2N/A * PM_AUTHORITY_SMF_STR varies The value of the property from SMF.
2N/A * Optional.
2N/A *
2N/A * PM_AUTHORITY_CURRENT_STR
2N/A * varies The value of the property in use by
2N/A * the kernel.
2N/A * Optional.
2N/A *
2N/A * PM_AUTHORITY_PLATFORM_STR
2N/A * varies The value of the property contained
2N/A * in the platform.
2N/A * Optional.
2N/A */
2N/Apm_error_t
2N/Apm_result_add(nvlist_t **result, pm_authority_t authority, const char *pgname,
2N/A const char *propname, data_type_t proptype, void *propv, uint_t propc)
2N/A{
2N/A pm_error_t err;
2N/A nvlist_t *rp;
2N/A nvlist_t *nvl;
2N/A
2N/A errno = 0;
2N/A if (result == NULL || propname == NULL || propv == NULL) {
2N/A errno = EINVAL;
2N/A return (PM_ERROR_INVALID_ARGUMENT);
2N/A }
2N/A
2N/A /* Allocate a new result list if necessary */
2N/A rp = *result;
2N/A if (rp == NULL) {
2N/A errno = nvlist_alloc(result, NV_UNIQUE_NAME, 0);
2N/A if (errno != 0) {
2N/A return (PM_ERROR_NVLIST);
2N/A }
2N/A rp = *result;
2N/A }
2N/A
2N/A /* Get the list of values for this property from the result set */
2N/A err = PM_SUCCESS;
2N/A nvl = NULL;
2N/A errno = nvlist_lookup_nvlist(rp, propname, &nvl);
2N/A if (errno != 0 || nvl == NULL) {
2N/A /*
2N/A * This property does not exist in the result set. Add it
2N/A * to the result
2N/A */
2N/A err = pm_result_create(rp, authority, pgname, propname,
2N/A proptype, propv, propc);
2N/A } else {
2N/A /*
2N/A * The property exists. Update it with the data from the new
2N/A * authority.
2N/A */
2N/A err = pm_result_update(nvl, authority, proptype, propv, propc);
2N/A if (err == PM_ERROR_MISSING_PROPERTY_VALUE &&
2N/A authority == PM_AUTHORITY_SMF) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_NOTICE,
2N/A "%s property %s: %s\n", __FUNCTION__,
2N/A propname, pm_strerror(err));
2N/A
2N/A /*
2N/A * Some properties in SMF may have no value. This is
2N/A * valid, but the property is intentionally not added
2N/A * to the result set.
2N/A */
2N/A errno = 0;
2N/A err = PM_SUCCESS;
2N/A }
2N/A }
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Create a new property entry in a result set as specified by pm_result_add
2N/A */
2N/Astatic pm_error_t
2N/Apm_result_create(nvlist_t *nvl, pm_authority_t authority, const char *pgname,
2N/A const char *propname, data_type_t proptype, void *propv, uint_t propc)
2N/A{
2N/A pm_error_t err;
2N/A nvlist_t *plist;
2N/A
2N/A /* Create the new list and add required elements */
2N/A errno = nvlist_alloc(&plist, NV_UNIQUE_NAME, 0);
2N/A if (errno != 0) {
2N/A return (PM_ERROR_NVLIST);
2N/A }
2N/A
2N/A if (pgname != NULL) {
2N/A errno = nvlist_add_string(plist, PM_PROP_PGNAME, pgname);
2N/A if (errno != 0) {
2N/A nvlist_free(plist);
2N/A return (PM_ERROR_NVLIST);
2N/A }
2N/A }
2N/A
2N/A /* Add the new value to the list */
2N/A err = pm_result_update(plist, authority, proptype, propv, propc);
2N/A if (err == PM_SUCCESS) {
2N/A /* Add the property to the main list */
2N/A errno = nvlist_add_nvlist(nvl, propname, plist);
2N/A if (errno != 0) {
2N/A nvlist_free(nvl);
2N/A err = PM_ERROR_NVLIST;
2N/A }
2N/A }
2N/A if (plist != NULL) {
2N/A nvlist_free(plist);
2N/A }
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Add a new value to a property value list
2N/A */
2N/Astatic pm_error_t
2N/Apm_result_update(nvlist_t *nvl, pm_authority_t authority, data_type_t proptype,
2N/A void *propv, uint_t propc)
2N/A{
2N/A pm_error_t err;
2N/A boolean_t *bap;
2N/A uint64_t *uap;
2N/A char **sap;
2N/A const char *authname;
2N/A
2N/A if (propv == NULL || propc == 0) {
2N/A errno = EINVAL;
2N/A return (PM_ERROR_MISSING_PROPERTY_VALUE);
2N/A }
2N/A
2N/A authname = pm_authority_getname(authority);
2N/A if (authority == PM_AUTHORITY_INVALID) {
2N/A errno = EINVAL;
2N/A return (PM_ERROR_INVALID_AUTHORITY);
2N/A }
2N/A
2N/A /*
2N/A * We assume that a property cannot exist in multiple property
2N/A * groups and simply add the value from the authority.
2N/A */
2N/A errno = 0;
2N/A err = PM_ERROR_NVLIST;
2N/A switch (proptype) {
2N/A case DATA_TYPE_BOOLEAN_ARRAY:
2N/A bap = (boolean_t *)propv;
2N/A errno = nvlist_add_boolean_array(nvl, authname, bap, propc);
2N/A break;
2N/A
2N/A case DATA_TYPE_BOOLEAN_VALUE:
2N/A bap = (boolean_t *)propv;
2N/A errno = nvlist_add_boolean_value(nvl, authname, bap[0]);
2N/A break;
2N/A
2N/A case DATA_TYPE_UINT64_ARRAY:
2N/A uap = (uint64_t *)propv;
2N/A errno = nvlist_add_uint64_array(nvl, authname, uap, propc);
2N/A break;
2N/A
2N/A case DATA_TYPE_UINT64:
2N/A uap = (uint64_t *)propv;
2N/A errno = nvlist_add_uint64(nvl, authname, uap[0]);
2N/A break;
2N/A
2N/A case DATA_TYPE_STRING_ARRAY:
2N/A sap = (char **)propv;
2N/A errno = nvlist_add_string_array(nvl, authname, sap, propc);
2N/A break;
2N/A
2N/A case DATA_TYPE_STRING:
2N/A sap = (char **)propv;
2N/A errno = nvlist_add_string(nvl, authname, sap[0]);
2N/A break;
2N/A
2N/A default:
2N/A errno = EINVAL;
2N/A err = PM_ERROR_INVALID_TYPE;
2N/A break;
2N/A }
2N/A if (errno == 0) {
2N/A err = PM_SUCCESS;
2N/A }
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Given a result set built by pm_result_add, above, created a new result set
2N/A * with just the requested properties.
2N/A */
2N/Apm_error_t
2N/Apm_result_filter(nvlist_t **result, nvlist_t *nvl, pm_authority_t authority,
2N/A char **propv, uint_t propc)
2N/A{
2N/A pm_error_t err;
2N/A int i;
2N/A char *propname;
2N/A char *pgname;
2N/A char tok[BUFSIZ];
2N/A nvlist_t *el;
2N/A nvpair_t *nvp;
2N/A
2N/A if (result == NULL || nvl == NULL) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG, "%s invalid argument\n",
2N/A __FUNCTION__);
2N/A errno = EINVAL;
2N/A return (PM_ERROR_INVALID_ARGUMENT);
2N/A }
2N/A
2N/A /*
2N/A * Scan each requested property and find the matching entry in the
2N/A * result set.
2N/A */
2N/A err = PM_SUCCESS;
2N/A for (i = 0; i < propc && err == PM_SUCCESS; i++) {
2N/A if (propv[i] == NULL) {
2N/A /* This property entry is invalid. */
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s null property at index %d\n", __FUNCTION__, i);
2N/A errno = EINVAL;
2N/A err = PM_ERROR_INVALID_PROPERTY_NAME;
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * Get the property name and potential group name from the
2N/A * current token
2N/A */
2N/A (void) strncpy(tok, propv[i], sizeof (tok));
2N/A propname = pm_parse_propname(tok, &pgname);
2N/A
2N/A /*
2N/A * Get the unique property name from the list. There can be
2N/A * only one.
2N/A */
2N/A errno = nvlist_lookup_nvlist(nvl, propname, &el);
2N/A if (errno != 0) {
2N/A /* The requested property is not found. Error. */
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s property %s not found in result set\n",
2N/A __FUNCTION__, propname);
2N/A errno = ENOENT;
2N/A err = PM_ERROR_INVALID_PROPERTY_NAME;
2N/A break;
2N/A }
2N/A
2N/A /* Now match the property group name */
2N/A if (! match_pgname(el, pgname)) {
2N/A /*
2N/A * This is the only property with this name and the
2N/A * group does not match.
2N/A */
2N/A uu_dprintf(pm_log, UU_DPRINTF_NOTICE,
2N/A "%s property %s/%s not found in result set\n",
2N/A __FUNCTION__, pgname, propname);
2N/A errno = ENOENT;
2N/A err = PM_ERROR_PROPERTY_NOT_FOUND;
2N/A break;
2N/A }
2N/A
2N/A /*
2N/A * Does this property contain an nvpair with the requested
2N/A * authority?
2N/A */
2N/A errno = nvlist_lookup_nvpair(el,
2N/A pm_authority_getname(authority), &nvp);
2N/A if (errno != 0) {
2N/A if (authority == PM_AUTHORITY_ALL) {
2N/A /*
2N/A * This is the special match-everything
2N/A * authority. Declare success and add the value
2N/A */
2N/A errno = 0;
2N/A } else {
2N/A /*
2N/A * The specified authority does not contain the
2N/A * requested property name.
2N/A */
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s authority %s not found in value list "
2N/A "for property %s\n", __FUNCTION__,
2N/A pm_authority_getname(authority), propname);
2N/A errno = ENOENT;
2N/A err = PM_ERROR_PROPERTY_NOT_FOUND;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Merge the requested nvpair into the results and remove
2N/A * all the the requested authorities.
2N/A */
2N/A err = pm_result_merge(result, nvl, propname, authority);
2N/A }
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG, "%s exiting with %d (%s)\n",
2N/A __FUNCTION__, err, pm_strerror(err));
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Add a specified nvpair to an existing result set
2N/A */
2N/Astatic pm_error_t
2N/Apm_result_merge(nvlist_t **result, nvlist_t *nvl, const char *propname,
2N/A pm_authority_t authority)
2N/A{
2N/A pm_error_t err;
2N/A pm_authority_t auth;
2N/A nvpair_t *nvp;
2N/A nvlist_t *vallist;
2N/A
2N/A err = PM_SUCCESS;
2N/A if (result == NULL || nvl == NULL) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG, "%s invalid argument\n",
2N/A __FUNCTION__);
2N/A errno = EINVAL;
2N/A return (PM_ERROR_INVALID_ARGUMENT);
2N/A }
2N/A if (*result == NULL) {
2N/A errno = nvlist_alloc(result, NV_UNIQUE_NAME, 0);
2N/A if (errno != 0) {
2N/A return (PM_ERROR_NVLIST);
2N/A }
2N/A }
2N/A
2N/A /* Add the property to the result set and retrieve it's value list */
2N/A if ((errno = nvlist_lookup_nvpair(nvl, propname, &nvp)) != 0 ||
2N/A (errno = nvlist_add_nvpair(*result, nvp)) != 0 ||
2N/A (errno = nvpair_value_nvlist(nvp, &vallist)) != 0) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s property %s could not be added\n", __FUNCTION__,
2N/A propname);
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG,
2N/A "%s nvlist returned %d (%s)\n", __FUNCTION__, errno,
2N/A strerror(errno));
2N/A return (PM_ERROR_NVLIST);
2N/A }
2N/A
2N/A /*
2N/A * Remove any authorities that do not match the requested authority
2N/A * from the result set.
2N/A */
2N/A err = PM_SUCCESS;
2N/A for (auth = pm_authority_next(PM_AUTHORITY_INVALID);
2N/A auth != PM_AUTHORITY_INVALID && err == PM_SUCCESS;
2N/A auth = pm_authority_next(auth)) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG, "%s checking auth %s\n",
2N/A __FUNCTION__, pm_authority_getname(auth));
2N/A
2N/A if (authority == PM_AUTHORITY_ALL) {
2N/A /* The request is for all available authorities */
2N/A continue;
2N/A }
2N/A
2N/A if (authority == auth) {
2N/A /* The request is for this authority. Do not remove */
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * Remove this authority from the list of authorities if
2N/A * it exists.
2N/A */
2N/A if ((errno = nvlist_remove_all(vallist,
2N/A pm_authority_getname(auth))) != 0 && errno != ENOENT) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG,
2N/A "%s nvlist_remove_all returned %d for %s\n",
2N/A __FUNCTION__, errno, pm_authority_getname(auth));
2N/A err = PM_ERROR_NVLIST;
2N/A }
2N/A }
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Property name specifiers may also include a property group designation:
2N/A * property_group_name/property_name
2N/A *
2N/A * Given a buffer with a property name specifier, determine the location of
2N/A * the property name and the property group name. Modify the given buffer
2N/A * if necessary.
2N/A */
2N/Achar *
2N/Apm_parse_propname(char *buf, char **pgname)
2N/A{
2N/A char *propname;
2N/A char *sep;
2N/A
2N/A if (buf == NULL) {
2N/A errno = EINVAL;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* Find the potential separator in the buffer */
2N/A *pgname = NULL;
2N/A propname = NULL;
2N/A sep = strstr(buf, PM_SEP_STR);
2N/A if (sep == NULL) {
2N/A /* The buffer contains only a potential property name */
2N/A propname = buf;
2N/A } else {
2N/A /*
2N/A * The buffer contains both a potential property name and a
2N/A * potential property group name. Modify the buffer and
2N/A * returned both.
2N/A */
2N/A *sep = '\0';
2N/A propname = sep + 1;
2N/A *pgname = buf;
2N/A if (strlen(*pgname) == 0) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_WARNING,
2N/A "%s property group name has zero length\n",
2N/A __FUNCTION__);
2N/A *pgname = NULL;
2N/A }
2N/A }
2N/A
2N/A return (propname);
2N/A}
2N/A
2N/A/*
2N/A * Determine if the string in the given buffer is a valid boolean value
2N/A */
2N/Apm_error_t
2N/Apm_parse_boolean(const char *buf, boolean_t *result)
2N/A{
2N/A pm_error_t err;
2N/A
2N/A if (buf == NULL) {
2N/A errno = EINVAL;
2N/A return (PM_ERROR_INVALID_BOOLEAN);
2N/A }
2N/A
2N/A /*
2N/A * Compare the given buffer with the valid values for a boolean value.
2N/A */
2N/A err = PM_ERROR_INVALID_BOOLEAN;
2N/A if (strncasecmp(buf, PM_TRUE_STR, strlen(PM_TRUE_STR)) == 0) {
2N/A /* The value is valid and true. */
2N/A *result = B_TRUE;
2N/A err = PM_SUCCESS;
2N/A } else if (strncasecmp(buf, PM_FALSE_STR, strlen(PM_FALSE_STR)) == 0) {
2N/A /* The value is valid and false. */
2N/A *result = B_FALSE;
2N/A err = PM_SUCCESS;
2N/A }
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Determine if the string in the given buffer contains a valid integer value
2N/A */
2N/Apm_error_t
2N/Apm_parse_integer(const char *buf, int64_t *result)
2N/A{
2N/A const char *cp;
2N/A
2N/A if (buf == NULL) {
2N/A return (PM_ERROR_INVALID_INTEGER);
2N/A }
2N/A
2N/A /* Make sure that all the characters are numbers */
2N/A for (cp = buf; cp != NULL && *cp != '\0'; cp++) {
2N/A if (! isdigit(*cp)) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG,
2N/A "string \"%s\" is not a valid integer\n", buf);
2N/A errno = EINVAL;
2N/A return (PM_ERROR_INVALID_INTEGER);
2N/A }
2N/A }
2N/A
2N/A /* Convert the integer to an int64 value */
2N/A if (sscanf(buf, "%lld", result) != 1) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "integer \"%s\" could not be converted\n", buf);
2N/A errno = EINVAL;
2N/A return (PM_ERROR_INVALID_INTEGER);
2N/A }
2N/A
2N/A return (PM_SUCCESS);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Determine if the given property group name matches a given property value
2N/A * list by comparing the strings. An input property group name of null
2N/A * matches all values.
2N/A */
2N/Astatic int
2N/Amatch_pgname(nvlist_t *nvl, const char *pgname)
2N/A{
2N/A char *sp;
2N/A
2N/A if (pgname == NULL) {
2N/A /* Match. The request wants any possible property group */
2N/A return (1);
2N/A }
2N/A
2N/A sp = NULL;
2N/A if (nvlist_lookup_string(nvl, PM_PROP_PGNAME, &sp) != 0) {
2N/A /*
2N/A * No match. The property group name for this property list
2N/A * does not exist and therefore does not match.
2N/A */
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG,
2N/A "%s property group %s not found\n", __FUNCTION__, pgname);
2N/A
2N/A return (0);
2N/A }
2N/A if (strncmp(pgname, sp, strlen(sp)) != 0) {
2N/A /*
2N/A * No match. The property group name for this property list
2N/A * does not match the requested property group name.
2N/A */
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG,
2N/A "%s requested property group %s != %s\n", __FUNCTION__,
2N/A pgname, sp);
2N/A
2N/A return (0);
2N/A }
2N/A
2N/A /* Match. */
2N/A return (1);
2N/A}