/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include "xml_convert.h" #include #include #include #include #include #include #include #include #include #include "volume_error.h" #include "volume_output.h" #include "volume_string.h" /* * IDs for localized messages in the generated command script */ #define CMD_MSG_ENVIRONMENT "Environment" #define CMD_MSG_AMEND_PATH "Amend PATH" #define CMD_MSG_DISK_SET_NAME "Disk set name" #define CMD_MSG_FUNCTIONS "Functions" /* CSTYLED */ #define CMD_MSG_ECHO_AND_EXEC "Echo (verbose) and exec given command, exit on error" #define CMD_MSG_GET_FULL_PATH "Get full /dev/rdsk path of given slice" /* CSTYLED */ #define CMD_MSG_FMTHARD_SPECIAL "Run fmthard, ignore partboot error, error if output" #define CMD_MSG_MAIN "Main" #define CMD_MSG_VERIFY_ROOT "Verify root" #define CMD_MSG_RUN_AS_ROOT "This script must be run as root." #define CMD_MSG_CHECK_FOR_VERBOSE "Check for verbose option" #define CMD_MSG_DOES_DISK_SET_EXIST "Does the disk set exist?" #define CMD_MSG_TAKE_DISK_SET "Take control of disk set" #define CMD_MSG_CREATE_THE_DISK_SET "Create the disk set" #define CMD_MSG_ADD_DISKS_TO_SET "Add disks to set" #define CMD_MSG_FORMAT_SLICES "Format slices" #define CMD_MSG_CREATE "Create {1} {2}" #define CMD_MSG_DOES_EXIST "Does {1} exist?" #define CMD_MSG_ADD_SLICES_TO "Add slices to {1}" /* CSTYLED */ #define CMD_MSG_ASSOCIATE_WITH_HSP "Associate {1} {2} with hot spare pool {3}" /* * ****************************************************************** * * Data types * * ****************************************************************** */ /* * Encapsulates the parsing of an XML attribute */ typedef struct { /* The name of the attribute */ char *name; /* * A function to validate and set the XML attribute value in * the given devconfig_t structure. * * @param name * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 if the given value was valid and set * successfully, non-zero otherwise. */ int (*validate_set)(devconfig_t *device, char *name, char *value); /* * A function to get the XML attribute value in the given * devconfig_t structure. * * @param name * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 if the given value was retrieved * successfully, non-zero otherwise. */ int (*get_as_string)(devconfig_t *device, char *name, char **value); } attr_t; /* * Encapsulates the parsing of an XML element */ typedef struct { /* The name of the element */ char *name; /* The type of element to set in the devconfig_t */ component_type_t type; /* * When converting from XML to a devconfig_t hierarchy, * indicates whether to create a new devconfig_t structure in * the hierarchy when this XML element is encountered. */ boolean_t is_hierarchical; /* * If is_hierarchical is B_TRUE, whether to use an existing * devconfig_t structure of this type when this element is * encountered */ boolean_t singleton; /* The valid XML attributes for this element */ attr_t *attributes; } element_t; typedef struct { char *msgid; char *message; } l10nmessage_t; /* * ****************************************************************** * * Function prototypes * * ****************************************************************** */ static int validate_doc(xmlDocPtr doc, const char *name, const char *systemID); static int devconfig_to_xml( xmlNodePtr parent, element_t elements[], devconfig_t *device); static int xml_to_devconfig( xmlNodePtr cur, element_t elements[], devconfig_t *device); static int compare_is_a_diskset(void *obj1, void *obj2); static xmlNodePtr xml_find_node( xmlNodePtr node, xmlChar *element, xmlChar *name); static xmlDocPtr create_localized_message_doc(); static int create_localized_message_file(char **tmpfile); static int strtobool(char *str, boolean_t *value); static int ofprintf_terse(void *unused, char *fmt, ...); static int ofprintf_verbose(void *unused, char *fmt, ...); static int validate_set_size( devconfig_t *volume, char *attr, char *value); static int validate_set_size_in_blocks( devconfig_t *slice, char *attr, char *value); static int validate_set_diskset_name( devconfig_t *diskset, char *attr, char *name); static int validate_add_available_name( devconfig_t *device, char *attr, char *name); static int validate_add_unavailable_name( devconfig_t *device, char *attr, char *name); static int validate_set_hsp_name( devconfig_t *hsp, char *attr, char *name); static int validate_set_disk_name( devconfig_t *disk, char *attr, char *name); static int validate_set_slice_name( devconfig_t *slice, char *attr, char *name); static int validate_set_slice_start_block( devconfig_t *slice, char *attr, char *value); static int validate_set_volume_name( devconfig_t *volume, char *attr, char *name); static int validate_set_stripe_interlace( devconfig_t *stripe, char *attr, char *value); static int validate_set_stripe_mincomp( devconfig_t *stripe, char *attr, char *value); static int validate_set_stripe_maxcomp( devconfig_t *stripe, char *attr, char *value); static int validate_set_volume_usehsp( devconfig_t *volume, char *attr, char *value); static int validate_set_mirror_nsubmirrors( devconfig_t *mirror, char *attr, char *value); static int validate_set_mirror_read( devconfig_t *mirror, char *attr, char *value); static int validate_set_mirror_write( devconfig_t *mirror, char *attr, char *value); static int validate_set_mirror_passnum( devconfig_t *mirror, char *attr, char *value); static int validate_set_volume_redundancy( devconfig_t *volume, char *attr, char *value); static int validate_set_volume_datapaths( devconfig_t *volume, char *attr, char *value); static int get_as_string_name( devconfig_t *device, char *attr, char **value); static int get_as_string_mirror_passnum( devconfig_t *mirror, char *attr, char **value); static int get_as_string_mirror_read( devconfig_t *mirror, char *attr, char **value); static int get_as_string_mirror_write( devconfig_t *mirror, char *attr, char **value); static int get_as_string_size_in_blocks( devconfig_t *device, char *attr, char **value); static int get_as_string_slice_start_block( devconfig_t *slice, char *attr, char **value); static int get_as_string_stripe_interlace( devconfig_t *stripe, char *attr, char **value); /* * ****************************************************************** * * Data * * ****************************************************************** */ /* Valid units for the size attribute */ units_t size_units[] = { {UNIT_KILOBYTES, BYTES_PER_KILOBYTE}, {UNIT_MEGABYTES, BYTES_PER_MEGABYTE}, {UNIT_GIGABYTES, BYTES_PER_GIGABYTE}, {UNIT_TERABYTES, BYTES_PER_TERABYTE}, {NULL, 0} }; /* Valid units for the interlace attribute */ units_t interlace_units[] = { {UNIT_BLOCKS, BYTES_PER_BLOCK}, {UNIT_KILOBYTES, BYTES_PER_KILOBYTE}, {UNIT_MEGABYTES, BYTES_PER_MEGABYTE}, {NULL, 0} }; /* attributes */ static attr_t diskset_attrs[] = { { ATTR_NAME, validate_set_diskset_name, get_as_string_name }, { NULL, NULL, NULL } }; /* attributes */ static attr_t available_attrs[] = { { ATTR_NAME, validate_add_available_name, NULL }, { NULL, NULL, NULL } }; /* attributes */ static attr_t unavailable_attrs[] = { { ATTR_NAME, validate_add_unavailable_name, NULL }, { NULL, NULL, NULL } }; /* attributes */ static attr_t hsp_attrs[] = { { ATTR_NAME, validate_set_hsp_name, get_as_string_name }, { NULL, NULL, NULL } }; /* attributes */ static attr_t disk_attrs[] = { { ATTR_NAME, validate_set_disk_name, get_as_string_name }, { NULL, NULL, NULL } }; /* attributes */ static attr_t slice_attrs[] = { { ATTR_NAME, validate_set_slice_name, get_as_string_name }, { ATTR_SIZEINBLOCKS, validate_set_size_in_blocks, get_as_string_size_in_blocks }, { ATTR_SLICE_STARTSECTOR, validate_set_slice_start_block, get_as_string_slice_start_block }, { NULL, NULL, NULL } }; /* attributes */ static attr_t stripe_attrs[] = { { ATTR_NAME, validate_set_volume_name, get_as_string_name }, { ATTR_SIZEINBYTES, validate_set_size, NULL }, { ATTR_STRIPE_MINCOMP, validate_set_stripe_mincomp, NULL }, { ATTR_STRIPE_MAXCOMP, validate_set_stripe_maxcomp, NULL }, { ATTR_STRIPE_INTERLACE, validate_set_stripe_interlace, get_as_string_stripe_interlace }, { ATTR_VOLUME_USEHSP, validate_set_volume_usehsp, NULL }, { NULL, NULL, NULL } }; /* attributes */ static attr_t concat_attrs[] = { { ATTR_NAME, validate_set_volume_name, get_as_string_name }, { ATTR_SIZEINBYTES, validate_set_size, NULL }, { ATTR_VOLUME_USEHSP, validate_set_volume_usehsp, NULL }, { NULL, NULL, NULL } }; /* attributes */ static attr_t mirror_attrs[] = { { ATTR_NAME, validate_set_volume_name, get_as_string_name }, { ATTR_MIRROR_NSUBMIRRORS, validate_set_mirror_nsubmirrors, NULL }, { ATTR_SIZEINBYTES, validate_set_size, NULL }, { ATTR_MIRROR_READ, validate_set_mirror_read, get_as_string_mirror_read }, { ATTR_MIRROR_WRITE, validate_set_mirror_write, get_as_string_mirror_write }, { ATTR_MIRROR_PASSNUM, validate_set_mirror_passnum, get_as_string_mirror_passnum }, { ATTR_VOLUME_USEHSP, validate_set_volume_usehsp, NULL }, { NULL, NULL, NULL } }; /* attributes */ static attr_t volume_attrs[] = { { ATTR_NAME, validate_set_volume_name, get_as_string_name }, { ATTR_SIZEINBYTES, validate_set_size, NULL }, { ATTR_VOLUME_REDUNDANCY, validate_set_volume_redundancy, NULL }, { ATTR_VOLUME_FAULTRECOVERY, validate_set_volume_usehsp, NULL }, { ATTR_VOLUME_DATAPATHS, validate_set_volume_datapaths, NULL }, { NULL, NULL, NULL } }; /* volume-request elements */ static element_t request_elements[] = { { ELEMENT_DISKSET, TYPE_DISKSET, B_FALSE, B_FALSE, diskset_attrs }, { ELEMENT_AVAILABLE, TYPE_UNKNOWN, B_FALSE, B_FALSE, available_attrs }, { ELEMENT_UNAVAILABLE, TYPE_UNKNOWN, B_FALSE, B_FALSE, unavailable_attrs }, { ELEMENT_HSP, TYPE_HSP, B_TRUE, B_FALSE, hsp_attrs }, { ELEMENT_SLICE, TYPE_SLICE, B_TRUE, B_FALSE, slice_attrs }, { ELEMENT_STRIPE, TYPE_STRIPE, B_TRUE, B_FALSE, stripe_attrs }, { ELEMENT_CONCAT, TYPE_CONCAT, B_TRUE, B_FALSE, concat_attrs }, { ELEMENT_MIRROR, TYPE_MIRROR, B_TRUE, B_FALSE, mirror_attrs }, { ELEMENT_VOLUME, TYPE_VOLUME, B_TRUE, B_FALSE, volume_attrs }, { NULL, NULL, B_FALSE, B_FALSE, NULL } }; /* volume-defaults elements */ static element_t default_elements[] = { { ELEMENT_DISKSET, TYPE_DISKSET, B_TRUE, B_FALSE, diskset_attrs }, { ELEMENT_AVAILABLE, TYPE_UNKNOWN, B_FALSE, B_TRUE, available_attrs }, { ELEMENT_UNAVAILABLE, TYPE_UNKNOWN, B_FALSE, B_TRUE, unavailable_attrs }, { ELEMENT_HSP, TYPE_HSP, B_TRUE, B_TRUE, hsp_attrs }, { ELEMENT_SLICE, TYPE_SLICE, B_TRUE, B_TRUE, slice_attrs }, { ELEMENT_STRIPE, TYPE_STRIPE, B_TRUE, B_TRUE, stripe_attrs }, { ELEMENT_CONCAT, TYPE_CONCAT, B_TRUE, B_TRUE, concat_attrs }, { ELEMENT_MIRROR, TYPE_MIRROR, B_TRUE, B_TRUE, mirror_attrs }, { ELEMENT_VOLUME, TYPE_VOLUME, B_TRUE, B_TRUE, volume_attrs }, { NULL, NULL, B_FALSE, B_FALSE, NULL } }; /* volume-config elements */ static element_t config_elements[] = { { ELEMENT_DISKSET, TYPE_DISKSET, B_FALSE, B_FALSE, diskset_attrs }, { ELEMENT_DISK, TYPE_DRIVE, B_TRUE, B_FALSE, disk_attrs }, { ELEMENT_SLICE, TYPE_SLICE, B_TRUE, B_FALSE, slice_attrs }, { ELEMENT_HSP, TYPE_HSP, B_TRUE, B_FALSE, hsp_attrs }, { ELEMENT_STRIPE, TYPE_STRIPE, B_TRUE, B_FALSE, stripe_attrs }, { ELEMENT_CONCAT, TYPE_CONCAT, B_TRUE, B_FALSE, concat_attrs }, { ELEMENT_MIRROR, TYPE_MIRROR, B_TRUE, B_FALSE, mirror_attrs }, { NULL, NULL, B_FALSE, B_FALSE, NULL } }; /* * ****************************************************************** * * External functions * * ****************************************************************** */ /* * Initialize the XML parser, setting defaults across all XML * routines. */ void init_xml() { /* COMPAT: Do not generate nodes for formatting spaces */ LIBXML_TEST_VERSION xmlKeepBlanksDefault(0); /* Turn on line numbers for debugging */ xmlLineNumbersDefault(1); /* Substitute entities as files are parsed */ xmlSubstituteEntitiesDefault(1); /* Don't load external entity subsets */ xmlLoadExtDtdDefaultValue = 0; /* Don't validate against DTD by default */ xmlDoValidityCheckingDefaultValue = 0; /* Set up output handlers for XML parsing */ xmlDefaultSAXHandler.warning = (warningSAXFunc)ofprintf_verbose; xmlDefaultSAXHandler.error = (errorSAXFunc)ofprintf_terse; xmlDefaultSAXHandler.fatalError = (fatalErrorSAXFunc)ofprintf_terse; } /* * Clean up any remaining structures before exiting. */ void cleanup_xml() { xsltCleanupGlobals(); xmlCleanupParser(); } /* * Converts a volume-request XML document into a request_t. * * @param doc * an existing volume-request XML document * * @param request * RETURN: a new request_t which must be freed via * free_request * * @return 0 on success, non-zero otherwise. */ int xml_to_request( xmlDocPtr doc, request_t **request) { int error = 0; *request = NULL; /* Validate doc against known DTD */ if ((error = validate_doc( doc, ELEMENT_VOLUMEREQUEST, VOLUME_REQUEST_DTD_LOC)) == 0) { /* Create a request */ if ((error = new_request(request)) == 0) { /* Convert the XML doc into a request_t */ error = xml_to_devconfig(xmlDocGetRootElement(doc), request_elements, request_get_diskset_req(*request)); } } return (error); } /* * Converts a volume-defaults XML document into a defaults_t. * * @param doc * an existing volume-defaults XML document * * @param defaults * RETURN: a new defaults_t which must be freed via * free_defaults * * @return 0 on success, non-zero otherwise. */ int xml_to_defaults( xmlDocPtr doc, defaults_t **defaults) { int error = 0; *defaults = NULL; /* Validate doc against known DTD */ if ((error = validate_doc(doc, ELEMENT_VOLUMEDEFAULTS, VOLUME_DEFAULTS_DTD_LOC)) == 0) { /* Create request defaults */ if ((error = new_defaults(defaults)) == 0) { devconfig_t *global; /* Get defaults for all disk sets */ if ((error = defaults_get_diskset_by_name( *defaults, NULL, &global)) == 0) { /* Populate the global devconfig_t from the XML doc */ if ((error = xml_to_devconfig(xmlDocGetRootElement(doc), default_elements, global)) == 0) { /* Get the components of the global devconfig_t */ dlist_t *list = devconfig_get_components(global); /* * Move all named disk set settings out from * under global settings */ /* CONSTANTCONDITION */ while (1) { dlist_t *removed = NULL; devconfig_t *component; /* Remove named disk set from under global */ list = dlist_remove_equivalent_item( list, NULL, compare_is_a_diskset, &removed); if (removed == NULL) { /* No named disk set found */ break; } component = removed->obj; /* Append named disk set to disk set list */ defaults_set_disksets(*defaults, dlist_append(dlist_new_item(component), defaults_get_disksets(*defaults), AT_TAIL)); } } } } } return (error); } /* * Converts a volume-config XML document into a devconfig_t. * * @param doc * an existing volume-config XML document * * @param config * RETURN: a new devconfig_t which must be freed via * free_devconfig * * @return 0 on success, non-zero otherwise. */ int xml_to_config( xmlDocPtr doc, devconfig_t **config) { int error = 0; *config = NULL; /* Validate doc against known DTD */ if ((error = validate_doc( doc, ELEMENT_VOLUMECONFIG, VOLUME_CONFIG_DTD_LOC)) == 0) { /* Create a devconfig_t */ if ((error = new_devconfig(config, TYPE_DISKSET)) == 0) { /* Populate the devconfig_t from the XML doc */ error = xml_to_devconfig( xmlDocGetRootElement(doc), config_elements, *config); } } return (error); } /* * Converts a devconfig_t into a volume-config XML document. * * @param config * an existing devconfig_t representing a volume * configuration. * * @param doc * RETURN: a new volume-config XML document which must be * freed via xmlFreeDoc * * @return 0 on success, non-zero otherwise. */ int config_to_xml( devconfig_t *config, xmlDocPtr *doc) { xmlNodePtr root; int error = 0; /* Create the XML document */ *doc = xmlNewDoc((xmlChar *)"1.0"); /* Create the root node */ root = xmlNewDocNode( *doc, NULL, (xmlChar *)ELEMENT_VOLUMECONFIG, NULL); xmlAddChild((xmlNodePtr)*doc, (xmlNodePtr)root); /* Create sub-nodes from the config devconfig_t */ if ((error = devconfig_to_xml(root, config_elements, config)) == 0) { /* Add DTD node and validate */ error = validate_doc( *doc, ELEMENT_VOLUMECONFIG, VOLUME_CONFIG_DTD_LOC); } if (error) { xmlFreeDoc(*doc); } return (error); } /* * Converts a volume-config XML document into a Bourne shell script. * * @param doc * an existing volume-config XML document * * @param commands * RETURN: a new char* which must be freed * * @return 0 on success, non-zero otherwise. */ int xml_to_commands( xmlDocPtr doc, char **commands) { char *tmpfile = NULL; int error = 0; xsltStylesheetPtr style = NULL; /* Read in XSL stylesheet as a normal XML document */ xmlDocPtr xsl_doc = xmlSAXParseFile((xmlSAXHandlerPtr) &xmlDefaultSAXHandler, VOLUME_COMMAND_XSL_LOC, 0); if (xsl_doc != NULL && xsl_doc->xmlChildrenNode != NULL) { /* * Find the "msgfile" variable node. This is where * we'll set the location of the file we'll create * containing the localized messages. */ xmlNodePtr msgfile_node = xml_find_node( xmlDocGetRootElement(xsl_doc), (xmlChar *)ELEMENT_VARIABLE, (xmlChar *)NAME_L10N_MESSAGE_FILE); /* * Find the "lang" node. This is where we'll set the * current locale. */ xmlNodePtr lang_node = xml_find_node(xmlDocGetRootElement(xsl_doc), (xmlChar *)ELEMENT_PARAM, (xmlChar *)NAME_LANG); /* * Ignore if the nodes are not found -- the script * will default to the C locale. */ if (msgfile_node != NULL && lang_node != NULL) { /* Get/set current locale in the "lang" node */ char *locale = setlocale(LC_MESSAGES, NULL); xmlNodeSetContent(lang_node, (xmlChar *)locale); /* Write localized messages to a temporary file */ if ((error = create_localized_message_file(&tmpfile)) == 0) { char *newsel; /* Clear current value of select attribute, if any */ xmlChar *cursel = xmlGetProp( msgfile_node, (xmlChar *)ATTR_SELECT); if (cursel != NULL) { xmlFree(cursel); } /* * The select attribute calls the XSLT function * document() to load an external XML file */ newsel = stralloccat(3, "document('", tmpfile, "')"); if (newsel == NULL) { volume_set_error(gettext("out of memory")); error = -1; } else { /* Set the new value of the select attribute */ xmlSetProp(msgfile_node, (xmlChar *)ATTR_SELECT, (xmlChar *)newsel); free(newsel); } } } if (error == 0) { style = xsltParseStylesheetDoc(xsl_doc); } } if (style == NULL) { volume_set_error( gettext("could not load stylesheet from %s"), VOLUME_COMMAND_XSL_LOC); error = -1; } else { xmlDocPtr result = xsltApplyStylesheet(style, doc, NULL); if (result == NULL) { volume_set_error( gettext("could not apply stylesheet to volume-config")); error = -1; } else { int length; if (xsltSaveResultToString((xmlChar **)commands, &length, result, style) == -1) { error = ENOMEM; } } xsltFreeStylesheet(style); } if (tmpfile != NULL) { /* Ignore failure */ unlink(tmpfile); free(tmpfile); } return (error); } /* * ****************************************************************** * * Static functions * * ****************************************************************** */ /* * Sets the external DTD node in the given XML document and then * validates it. * * @param doc * an existing XML document * * @param name * the expected root element name of the XML document * * @param systemID * the location of the DTD * * @return 0 on success, non-zero otherwise. */ static int validate_doc( xmlDocPtr doc, const char *name, const char *systemID) { xmlValidCtxt context; xmlDtdPtr dtd; if (doc == NULL) { volume_set_error(gettext("NULL %s document"), name); return (-1); } /* * Assume that we can't trust any DTD but our own. */ /* Was a DTD (external or internal) included in the document? */ if ((dtd = xmlGetIntSubset(doc)) != NULL) { /* Remove the DTD node */ oprintf(OUTPUT_DEBUG, gettext("Removing DTD from %s\n"), name); xmlUnlinkNode((xmlNodePtr)dtd); xmlFreeDtd(dtd); } /* Create the (external) DTD node */ oprintf(OUTPUT_DEBUG, gettext("Creating new external DTD for %s\n"), name); dtd = xmlCreateIntSubset( doc, (xmlChar *)name, NULL, (xmlChar *)systemID); if (dtd == NULL) { volume_set_error( gettext("could not create DTD node from %s"), systemID); return (-1); } /* Validate against DTD */ oprintf(OUTPUT_DEBUG, gettext("Validating %s against DTD\n"), name); context.userData = NULL; context.error = (xmlValidityErrorFunc)ofprintf_terse; context.warning = (xmlValidityWarningFunc)ofprintf_terse; if (!xmlValidateDocument(&context, doc)) { volume_set_error(gettext("invalid %s"), name); return (-1); } return (0); } /* * Converts a devconfig_t into an XML node subject to the rules in * the given element_t array. * * @param parent * the XML node to which to add new XML nodes resulting * from conversion of the given devconfig_t * * @param elements * the element_ts that describe the structure of the XML * document and govern the conversion of the given * devconfig_t * * @param device * the devconfig_t to convert * * @return 0 on success, non-zero otherwise. */ static int devconfig_to_xml( xmlNodePtr parent, element_t elements[], devconfig_t *device) { int i; int error = 0; xmlNodePtr node = NULL; /* Get device type */ component_type_t type; if ((error = devconfig_get_type(device, &type)) != 0) { return (error); } /* Search for this element definition */ for (i = 0; elements[i].name != NULL; i++) { element_t *element = &(elements[i]); if (element->type == type) { int j; char **array; dlist_t *components; oprintf(OUTPUT_DEBUG, gettext("Element: %s\n"), devconfig_type_to_str(type)); /* Create the XML node */ node = xmlNewChild( parent, NULL, (xmlChar *)element->name, NULL); /* For each attribute defined for this element... */ for (j = 0; element->attributes[j].name != NULL; j++) { attr_t *attribute = &(element->attributes[j]); char *value; /* Is there a valid accessor for this attribute? */ if (attribute->get_as_string != NULL) { /* Get the attribute value from the device */ switch (error = attribute->get_as_string( device, attribute->name, &value)) { /* Attribute is set in this device */ case 0: oprintf(OUTPUT_DEBUG, " %s: %s\n", attribute->name, value); /* Set the value in the XML node */ xmlSetProp(node, (uchar_t *)attribute->name, (uchar_t *)value); free(value); /* FALLTHROUGH */ /* Attribute is not set in this device */ case ERR_ATTR_UNSET: error = 0; break; /* Error */ default: return (error); } } } /* Is this node hierarchical? */ if (element->is_hierarchical == B_FALSE) { node = parent; } /* Create nodes */ array = devconfig_get_available(device); if (array != NULL) { for (j = 0; array[j] != NULL; j++) { xmlNodePtr child = xmlNewChild( node, NULL, (xmlChar *)ELEMENT_AVAILABLE, NULL); xmlSetProp(child, (xmlChar *)ATTR_NAME, (xmlChar *)array[j]); } } /* Create nodes */ array = devconfig_get_unavailable(device); if (array != NULL) { for (j = 0; array[j] != NULL; j++) { xmlNodePtr child = xmlNewChild( node, NULL, (xmlChar *)ELEMENT_UNAVAILABLE, NULL); xmlSetProp(child, (xmlChar *)ATTR_NAME, (xmlChar *)array[j]); } } /* * Recursively convert subcomponents of this device to * XML, taking care to encode them in the order * specified in the element_t list (which should * mirror what's expected by the DTD). */ /* For each element type... */ for (j = 0; elements[j].name != NULL; j++) { /* For each component of this device... */ for (components = devconfig_get_components(device); components != NULL && error == 0; components = components->next) { devconfig_t *component = (devconfig_t *)components->obj; component_type_t t; /* Are the types the same? */ if ((error = devconfig_get_type(component, &t)) != 0) { return (error); } else { if (elements[j].type == t) { /* Encode child */ error = devconfig_to_xml( node, elements, component); } } } } /* Element found */ break; } } /* Was this device successfully converted? */ if (node == NULL) { volume_set_error( gettext("can't convert device of type \"%s\" to XML element"), devconfig_type_to_str(type)); error = -1; } return (error); } /* * Converts an XML node into a devconfig_t subject to the rules in * the given element_t array. * * @param cure * the existing XML node to convert * * @param elements * the element_ts that describe the structure of the XML * document and govern the conversion of the given XML * node * * @param device * the devconfig_t node to which to add new devconfig_ts * resulting from conversion of the given XML node * * @return 0 on success, non-zero otherwise. */ static int xml_to_devconfig( xmlNodePtr cur, element_t elements[], devconfig_t *device) { int error = 0; /* For each child node... */ for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { int i; boolean_t parsed_elem = B_FALSE; /* Search for this element definition */ for (i = 0; elements[i].name != NULL; i++) { element_t *element = &(elements[i]); if (xmlStrcmp(cur->name, (xmlChar *)element->name) == 0) { int j; devconfig_t *component = NULL; /* Flag that this element has been parsed */ parsed_elem = B_TRUE; oprintf(OUTPUT_DEBUG, gettext("line %d: Element <%s>\n"), XML_GET_LINE(cur), cur->name); /* Should a new device be created for this element? */ if (element->is_hierarchical == B_TRUE) { /* Should we use an existing device of this type? */ if (element->singleton) { devconfig_get_component( device, element->type, &component, B_FALSE); } if (component == NULL) { oprintf(OUTPUT_DEBUG, gettext("Creating new device\n")); /* Create device of this type */ if ((error = new_devconfig( &component, element->type)) != 0) { return (error); } /* Add component to the toplevel device */ devconfig_set_components( device, dlist_append(dlist_new_item(component), devconfig_get_components(device), AT_TAIL)); } } else { component = device; } /* For each attribute defined for this element... */ for (j = 0; element->attributes[j].name != NULL; j++) { attr_t *attribute = &(element->attributes[j]); /* Get the value of this attribute */ char *value = (char *) xmlGetProp(cur, (xmlChar *)attribute->name); /* Was this attribute specified? */ if (value != NULL) { oprintf(OUTPUT_DEBUG, gettext("line %d:\tAttribute %s=%s\n"), XML_GET_LINE(cur), attribute->name, value); /* Set this value in the device */ if ((error = attribute->validate_set( component, attribute->name, value)) != 0) { return (error); } } } /* Get recursive sub-elements */ if ((error = xml_to_devconfig( cur, elements, component)) != 0) { return (error); } /* Element found */ break; } } /* Make sure all non-text/comment elements were parsed */ if (parsed_elem == B_FALSE && xmlStrcmp(cur->name, (xmlChar *)ELEMENT_TEXT) != 0 && xmlStrcmp(cur->name, (xmlChar *)ELEMENT_COMMENT) != 0) { oprintf(OUTPUT_DEBUG, gettext("Element <%s> NOT PARSED!!!\n"), cur->name); } } return (0); } /* * Returns 0 if obj2 (devconfig_t *) is a disk set, 1 otherwise. */ static int compare_is_a_diskset( void *obj1, void *obj2) { return (devconfig_isA( (devconfig_t *)obj2, TYPE_DISKSET) == B_TRUE ? 0 : 1); } /* * Recursively searches the given xmlNodePtr for an element of the * specified type and name. * * @param node * the root node to search * * @param element * the name of the element type * * @param name * the value of the name attribute * * @return a valid xmlNodePtr if an element of the specified * type and name was found, NULL otherwise. */ static xmlNodePtr xml_find_node( xmlNodePtr node, xmlChar *element, xmlChar *name) { xmlNodePtr child; /* Is the element the right type? */ if (xmlStrcmp(element, node->name) == 0 && /* Does this element's name attribute match? */ xmlStrcmp(name, xmlGetProp(node, (xmlChar *)ATTR_NAME)) == 0) { return (node); } /* Check child nodes */ for (child = node->xmlChildrenNode; child != NULL; child = child->next) { xmlNodePtr found = xml_find_node(child, element, name); if (found != NULL) { return (found); } } return (NULL); } /* * Creates an XML document containing all of the localized message * strings for the generated command script. * * @return a xmlDocPtr which must be freed via xmlFreeDoc */ static xmlDocPtr create_localized_message_doc() { int i; char *locale; xmlDocPtr doc; xmlNodePtr root; l10nmessage_t _cmd_messages[21]; /* Create the XML document */ doc = xmlNewDoc((xmlChar *)"1.0"); /* Create the root node */ root = xmlNewDocNode( doc, NULL, (xmlChar *)ELEMENT_L10N, NULL); xmlAddChild((xmlNodePtr) doc, (xmlNodePtr)root); _cmd_messages[0].msgid = CMD_MSG_ENVIRONMENT; _cmd_messages[0].message = gettext(CMD_MSG_ENVIRONMENT); _cmd_messages[1].msgid = CMD_MSG_AMEND_PATH; _cmd_messages[1].message = gettext(CMD_MSG_AMEND_PATH); _cmd_messages[2].msgid = CMD_MSG_DISK_SET_NAME; _cmd_messages[2].message = gettext(CMD_MSG_DISK_SET_NAME); _cmd_messages[3].msgid = CMD_MSG_FUNCTIONS; _cmd_messages[3].message = gettext(CMD_MSG_FUNCTIONS); _cmd_messages[4].msgid = CMD_MSG_ECHO_AND_EXEC; _cmd_messages[4].message = gettext(CMD_MSG_ECHO_AND_EXEC); _cmd_messages[5].msgid = CMD_MSG_FMTHARD_SPECIAL; _cmd_messages[5].message = gettext(CMD_MSG_FMTHARD_SPECIAL); _cmd_messages[6].msgid = CMD_MSG_GET_FULL_PATH; _cmd_messages[6].message = gettext(CMD_MSG_GET_FULL_PATH); _cmd_messages[7].msgid = CMD_MSG_MAIN; _cmd_messages[7].message = gettext(CMD_MSG_MAIN); _cmd_messages[8].msgid = CMD_MSG_VERIFY_ROOT; _cmd_messages[8].message = gettext(CMD_MSG_VERIFY_ROOT); _cmd_messages[9].msgid = CMD_MSG_RUN_AS_ROOT; _cmd_messages[9].message = gettext(CMD_MSG_RUN_AS_ROOT); _cmd_messages[10].msgid = CMD_MSG_CHECK_FOR_VERBOSE; _cmd_messages[10].message = gettext(CMD_MSG_CHECK_FOR_VERBOSE); _cmd_messages[11].msgid = (CMD_MSG_DOES_DISK_SET_EXIST); _cmd_messages[11].message = gettext(CMD_MSG_DOES_DISK_SET_EXIST); _cmd_messages[12].msgid = (CMD_MSG_TAKE_DISK_SET); _cmd_messages[12].message = gettext(CMD_MSG_TAKE_DISK_SET); _cmd_messages[13].msgid = (CMD_MSG_CREATE_THE_DISK_SET); _cmd_messages[13].message = gettext(CMD_MSG_CREATE_THE_DISK_SET); _cmd_messages[14].msgid = (CMD_MSG_ADD_DISKS_TO_SET); _cmd_messages[14].message = gettext(CMD_MSG_ADD_DISKS_TO_SET); _cmd_messages[15].msgid = (CMD_MSG_FORMAT_SLICES); _cmd_messages[15].message = gettext(CMD_MSG_FORMAT_SLICES); _cmd_messages[16].msgid = (CMD_MSG_CREATE); _cmd_messages[16].message = gettext(CMD_MSG_CREATE); _cmd_messages[17].msgid = (CMD_MSG_DOES_EXIST); _cmd_messages[17].message = gettext(CMD_MSG_DOES_EXIST); _cmd_messages[18].msgid = (CMD_MSG_ADD_SLICES_TO); _cmd_messages[18].message = gettext(CMD_MSG_ADD_SLICES_TO); _cmd_messages[19].msgid = (CMD_MSG_ASSOCIATE_WITH_HSP); _cmd_messages[19].message = gettext(CMD_MSG_ASSOCIATE_WITH_HSP); _cmd_messages[20].msgid = NULL; /* Get/set current locale in the "lang" node */ locale = setlocale(LC_MESSAGES, NULL); /* Add localized elements to stylesheet */ for (i = 0; _cmd_messages[i].msgid != NULL; i++) { xmlNsPtr ns = xmlNewNs(NULL, NULL, NULL); xmlNodePtr node = xmlNewTextChild( root, ns, (xmlChar *)ELEMENT_MESSAGE, (xmlChar *)_cmd_messages[i].message); /* Lang attribute */ xmlSetProp(node, (xmlChar *)ATTR_LANG, (xmlChar *)locale); /* Message ID attribute */ xmlSetProp(node, (xmlChar *)ATTR_MESSAGEID, (xmlChar *)_cmd_messages[i].msgid); } if (get_max_verbosity() >= OUTPUT_DEBUG) { xmlChar *text; /* Get the text dump */ xmlDocDumpFormatMemory(doc, &text, NULL, 1); oprintf(OUTPUT_DEBUG, gettext("Generated message file:\n%s"), text); xmlFree(text); } return (doc); } /* * Creates a temporary XML file containing all of the localized * message strings for the generated command script. * * @param tmpfile * RETURN: the name of the temporary XML file * * @return 0 on success, non-zero otherwise. */ static int create_localized_message_file( char **tmpfile) { int error = 0; /* * Create temporary file name -- "XXXXXX" is replaced with * unique char sequence by mkstemp() */ *tmpfile = stralloccat(3, "/tmp/", ELEMENT_L10N, "XXXXXX"); if (*tmpfile == NULL) { volume_set_error(gettext("out of memory")); error = -1; } else { int fildes; FILE *msgfile = NULL; /* Open temp file */ if ((fildes = mkstemp(*tmpfile)) != -1) { msgfile = fdopen(fildes, "w"); } if (msgfile == NULL) { volume_set_error(gettext( "could not open file for writing: %s"), *tmpfile); error = -1; } else { xmlChar *text; xmlDocPtr message_doc = create_localized_message_doc(); xmlDocDumpFormatMemory(message_doc, &text, NULL, 1); if (fprintf(msgfile, "%s", text) < 0) { volume_set_error(gettext( "could not create localized message file: %s"), *tmpfile); error = -1; } xmlFree(text); xmlFreeDoc(message_doc); } fclose(msgfile); } return (error); } /* * Converts the given string into a boolean. The string must be * either VALID_ATTR_TRUE or VALID_ATTR_FALSE. * * @param str * the string to convert * * @param bool * the addr of the boolean_t * * @return 0 if the given string could be converted to a boolean * non-zero otherwise. */ static int strtobool( char *str, boolean_t *value) { int error = 0; if (strcmp(str, VALID_ATTR_TRUE) == 0) { *value = B_TRUE; } else if (strcmp(str, VALID_ATTR_FALSE) == 0) { *value = B_FALSE; } else error = -1; return (error); } /* * Wrapper for oprintf with a OUTPUT_TERSE level of verbosity. * Provides an fprintf-like syntax to enable use as substitute output * handler for man of the XML commands. * * @param unused * unused, in favor of the FILE* passed to * set_max_verbosity(). * * @param fmt * a printf-style format string * * @return the number of characters output */ static int ofprintf_terse( void *unused, char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = oprintf_va(OUTPUT_TERSE, fmt, ap); va_end(ap); return (ret); } /* * Wrapper for oprintf with a OUTPUT_VERBOSE level of verbosity. * Provides an fprintf-like syntax to enable use as substitute output * handler for man of the XML commands. * * @param unused * unused, in favor of the FILE* passed to * set_max_verbosity(). * * @param fmt * a printf-style format string * * @return the number of characters output */ static int ofprintf_verbose( void *unused, char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = oprintf_va(OUTPUT_VERBOSE, fmt, ap); va_end(ap); return (ret); } /* * ****************************************************************** * * XML attribute validators/mutators * * These functions convert the given XML attribute string to the * appropriate data type, and then pass it on to the appropriate * devconfig_t mutator. A non-zero status is returned if the given * string could not be converted or was invalid. * * ****************************************************************** */ /* * Validate and set the size attribute in the given volume * devconfig_t. * * @param volume * the devconfig_t in which to set the size * * @param attr * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_size( devconfig_t *volume, char *attr, char *value) { int error; uint64_t size = 0; /* Convert size string to bytes */ if ((error = sizestr_to_bytes(value, &size, size_units)) != 0) { return (error); } /* Set size in volume */ return (devconfig_set_size(volume, size)); } /* * Validate and set the size_in_blocks attribute in the given slice * devconfig_t. * * @param volume * the devconfig_t in which to set the size_in_blocks * * @param attr * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_size_in_blocks( devconfig_t *slice, char *attr, char *value) { long long size; /* Convert string to long long */ if (sscanf(value, "%lld", &size) != 1) { volume_set_error(gettext("%s: invalid size in blocks"), value); return (-1); } /* Set the number of submirrors in the slice */ return (devconfig_set_size_in_blocks(slice, (uint64_t)size)); } /* * Validate and set the name attribute in the given diskset * devconfig_t. * * @param volume * the devconfig_t in which to set the name * * @param attr * the name of the XML attribute * * @param name * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_diskset_name( devconfig_t *diskset, char *attr, char *name) { return (devconfig_set_diskset_name(diskset, name)); } /* * Validate and add the given name to the list of available devices in * the given volume devconfig_t. * * @param device * the devconfig_t whose available device list to modify * * @param attr * the name of the XML attribute * * @param name * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_add_available_name( devconfig_t *device, char *attr, char *name) { char **available; /* Get available devices for this device */ available = devconfig_get_available(device); /* Try to add name to array via realloc */ if ((available = append_to_string_array(available, name)) == NULL) { return (ENOMEM); } /* Set available devices in the device */ devconfig_set_available(device, available); return (0); } /* * Validate and add the given name to the list of unavailable devices * in the given volume devconfig_t. * * @param device * the devconfig_t whose unavailable device list to modify * * @param attr * the name of the XML attribute * * @param name * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_add_unavailable_name( devconfig_t *device, char *attr, char *name) { char **unavailable; /* Get unavailable devices for this device */ unavailable = devconfig_get_unavailable(device); /* Try to add name to array via realloc */ if ((unavailable = append_to_string_array(unavailable, name)) == NULL) { return (ENOMEM); } /* Set unavailable devices in the device */ devconfig_set_unavailable(device, unavailable); return (0); } /* * Validate and set the name attribute in the given hsp devconfig_t. * * @param volume * the devconfig_t in which to set the name * * @param attr * the name of the XML attribute * * @param name * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_hsp_name( devconfig_t *hsp, char *attr, char *name) { return (devconfig_set_hsp_name(hsp, name)); } /* * Validate and set the name attribute in the given disk devconfig_t. * * @param volume * the devconfig_t in which to set the name * * @param attr * the name of the XML attribute * * @param name * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_disk_name( devconfig_t *disk, char *attr, char *name) { return (devconfig_set_name(disk, name)); } /* * Validate and set the name attribute in the given slice devconfig_t. * * @param volume * the devconfig_t in which to set the name * * @param attr * the name of the XML attribute * * @param name * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_slice_name( devconfig_t *slice, char *attr, char *name) { return (devconfig_set_name(slice, name)); } /* * Validate and set the start_block attribute in the given slice * devconfig_t. * * @param volume * the devconfig_t in which to set the start_block * * @param attr * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_slice_start_block( devconfig_t *slice, char *attr, char *value) { long long startsector; /* Convert string to long long */ if (sscanf(value, "%lld", &startsector) != 1) { volume_set_error(gettext("%s: invalid start sector"), value); return (-1); } /* Set the number of submirrors in the slice */ return (devconfig_set_slice_start_block(slice, (uint64_t)startsector)); } /* * Validate and set the name attribute in the given volume * devconfig_t. * * @param volume * the devconfig_t in which to set the name * * @param attr * the name of the XML attribute * * @param name * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_volume_name( devconfig_t *volume, char *attr, char *name) { return (devconfig_set_volume_name(volume, name)); } /* * Validate and set the interlace attribute in the given stripe * devconfig_t. * * @param volume * the devconfig_t in which to set the interlace * * @param attr * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_stripe_interlace( devconfig_t *stripe, char *attr, char *value) { int error; uint64_t interlace = 0; /* Convert interlace string to bytes */ if ((error = sizestr_to_bytes( value, &interlace, interlace_units)) != 0) { return (error); } /* Set interlace in stripe */ return (devconfig_set_stripe_interlace(stripe, interlace)); } /* * Validate and set the mincomp attribute in the given stripe * devconfig_t. * * @param volume * the devconfig_t in which to set the mincomp * * @param attr * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_stripe_mincomp( devconfig_t *stripe, char *attr, char *value) { uint16_t mincomp; /* Convert string to a uint16_t */ if (str_to_uint16(value, &mincomp) != 0) { volume_set_error( gettext("invalid minimum stripe components (%s): %s"), attr, value); return (-1); } /* Set in stripe */ return (devconfig_set_stripe_mincomp(stripe, mincomp)); } /* * Validate and set the maxcomp attribute in the given stripe * devconfig_t. * * @param volume * the devconfig_t in which to set the maxcomp * * @param attr * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_stripe_maxcomp( devconfig_t *stripe, char *attr, char *value) { uint16_t maxcomp; /* Convert string to a uint16_t */ if (str_to_uint16(value, &maxcomp) != 0) { volume_set_error( gettext("invalid maximum stripe components (%s): %s"), attr, value); return (-1); } /* Set in stripe */ return (devconfig_set_stripe_maxcomp(stripe, maxcomp)); } /* * Validate and set the usehsp attribute in the given volume * devconfig_t. * * @param volume * the devconfig_t in which to set the usehsp * * @param attr * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_volume_usehsp( devconfig_t *volume, char *attr, char *value) { boolean_t usehsp; /* Get boolean value */ if (strtobool(value, &usehsp) != 0) { volume_set_error( gettext("%s: invalid boolean value for \"%s\" attribute"), value, attr); return (-1); } /* Set in volume */ return (devconfig_set_volume_usehsp(volume, usehsp)); } /* * Validate and set the nsubmirrors attribute in the given mirror * devconfig_t. * * @param volume * the devconfig_t in which to set the nsubmirrors * * @param attr * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_mirror_nsubmirrors( devconfig_t *mirror, char *attr, char *value) { uint16_t nsubmirrors; /* Convert string to a uint16_t */ if (str_to_uint16(value, &nsubmirrors) != 0) { volume_set_error( gettext("invalid number of submirrors (%s): %s"), attr, value); return (-1); } /* Set in stripe */ return (devconfig_set_mirror_nsubs(mirror, nsubmirrors)); } /* * Validate and set the read attribute in the given mirror * devconfig_t. * * @param volume * the devconfig_t in which to set the read * * @param attr * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_mirror_read( devconfig_t *mirror, char *attr, char *value) { mirror_read_strategy_t strategy; if (strcmp(value, VALID_MIRROR_READ_ROUNDROBIN) == 0) { strategy = MIRROR_READ_ROUNDROBIN; } else if (strcmp(value, VALID_MIRROR_READ_GEOMETRIC) == 0) { strategy = MIRROR_READ_GEOMETRIC; } else if (strcmp(value, VALID_MIRROR_READ_FIRST) == 0) { strategy = MIRROR_READ_FIRST; } else { volume_set_error(gettext("%s: invalid mirror read value"), value); return (-1); } return (devconfig_set_mirror_read(mirror, strategy)); } /* * Validate and set the write attribute in the given mirror * devconfig_t. * * @param volume * the devconfig_t in which to set the write * * @param attr * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_mirror_write( devconfig_t *mirror, char *attr, char *value) { mirror_write_strategy_t strategy; if (strcmp(value, VALID_MIRROR_WRITE_PARALLEL) == 0) { strategy = MIRROR_WRITE_PARALLEL; } else if (strcmp(value, VALID_MIRROR_WRITE_SERIAL) == 0) { strategy = MIRROR_WRITE_SERIAL; } else { volume_set_error(gettext("%s: invalid mirror write value"), value); return (-1); } return (devconfig_set_mirror_write(mirror, strategy)); } /* * Validate and set the passnum attribute in the given mirror * devconfig_t. * * @param volume * the devconfig_t in which to set the passnum * * @param attr * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_mirror_passnum( devconfig_t *mirror, char *attr, char *value) { uint16_t passnum; /* Convert string to a uint16_t */ if (str_to_uint16(value, &passnum) != 0) { volume_set_error( gettext("invalid mirror pass number (%s): %s"), attr, value); return (-1); } /* Set in stripe */ return (devconfig_set_mirror_pass(mirror, passnum)); } /* * Validate and set the redundancy attribute in the given volume * devconfig_t. * * @param volume * the devconfig_t in which to set the redundancy * * @param attr * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_volume_redundancy( devconfig_t *volume, char *attr, char *value) { uint16_t redundancy; /* Convert string to a uint16_t */ if (str_to_uint16(value, &redundancy) != 0) { volume_set_error( gettext("invalid redundancy level (%s): %s"), attr, value); return (-1); } /* Set in stripe */ return (devconfig_set_volume_redundancy_level(volume, redundancy)); } /* * Validate and set the datapaths attribute in the given volume * devconfig_t. * * @param volume * the devconfig_t in which to set the datapaths * * @param attr * the name of the XML attribute * * @param value * the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int validate_set_volume_datapaths( devconfig_t *volume, char *attr, char *value) { uint16_t redundancy; /* Convert string to a uint16_t */ if (str_to_uint16(value, &redundancy) != 0) { volume_set_error( gettext("invalid number of data paths (%s): %s"), attr, value); return (-1); } /* Set in stripe */ return (devconfig_set_volume_npaths(volume, redundancy)); } /* * ****************************************************************** * * XML attribute accessors/converters * * These functions get a value from the appropriate devconfig_t * accessor, and then convert it to a string. * * ****************************************************************** */ /* * Get, as a string, the value of the name attribute of the given * devconfig_t. This data must be freed. * * @param device * the devconfig_t from which to retrieve the name * * @param attr * the name of the XML attribute * * @param value * RETURN: the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int get_as_string_name( devconfig_t *device, char *attr, char **value) { int error; char *name; /* Get name */ if ((error = devconfig_get_name(device, &name)) == 0) { if ((*value = strdup(name)) == NULL) { error = ENOMEM; } } return (error); } /* * Get, as a string, the value of the passnum attribute of the given * mirror devconfig_t. This data must be freed. * * @param device * the devconfig_t from which to retrieve the passnum * * @param attr * the name of the XML attribute * * @param value * RETURN: the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int get_as_string_mirror_passnum( devconfig_t *mirror, char *attr, char **value) { int error; uint16_t passnum; /* Get mirror pass number */ if ((error = devconfig_get_mirror_pass(mirror, &passnum)) == 0) { error = ll_to_str(passnum, value); } return (error); } /* * Get, as a string, the value of the read attribute of the given * mirror devconfig_t. This data must be freed. * * @param device * the devconfig_t from which to retrieve the read * * @param attr * the name of the XML attribute * * @param value * RETURN: the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int get_as_string_mirror_read( devconfig_t *mirror, char *attr, char **value) { int error; mirror_read_strategy_t read; /* Get mirror read strategy */ if ((error = devconfig_get_mirror_read(mirror, &read)) == 0) { if ((*value = strdup( devconfig_read_strategy_to_str(read))) == NULL) { error = ENOMEM; } } return (error); } /* * Get, as a string, the value of the write attribute of the given * mirror devconfig_t. This data must be freed. * * @param device * the devconfig_t from which to retrieve the write * * @param attr * the name of the XML attribute * * @param value * RETURN: the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int get_as_string_mirror_write( devconfig_t *mirror, char *attr, char **value) { int error; mirror_write_strategy_t write; /* Get mirror write strategy */ if ((error = devconfig_get_mirror_write(mirror, &write)) == 0) { if ((*value = strdup( devconfig_write_strategy_to_str(write))) == NULL) { error = ENOMEM; } } return (error); } /* * Get, as a string, the value of the in_blocks attribute of the given * device devconfig_t. This data must be freed. * * @param device * the devconfig_t from which to retrieve the in_blocks * * @param attr * the name of the XML attribute * * @param value * RETURN: the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int get_as_string_size_in_blocks( devconfig_t *device, char *attr, char **value) { int error; uint64_t size; /* Get size in blocks */ if ((error = devconfig_get_size_in_blocks(device, &size)) == 0) { error = ll_to_str(size, value); } return (error); } /* * Get, as a string, the value of the start_block attribute of the * given slice devconfig_t. This data must be freed. * * @param device * the devconfig_t from which to retrieve the start_block * * @param attr * the name of the XML attribute * * @param value * RETURN: the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int get_as_string_slice_start_block( devconfig_t *slice, char *attr, char **value) { int error; uint64_t start; /* Get slice start block */ if ((error = devconfig_get_slice_start_block(slice, &start)) == 0) { error = ll_to_str(start, value); } return (error); } /* * Get, as a string, the value of the interlace attribute of the given * stripe devconfig_t. This data must be freed. * * @param device * the devconfig_t from which to retrieve the interlace * * @param attr * the name of the XML attribute * * @param value * RETURN: the value of the XML attribute * * @return 0 on success, non-zero otherwise. */ static int get_as_string_stripe_interlace( devconfig_t *stripe, char *attr, char **value) { int error; uint64_t interlace; /* Get interlace */ if ((error = devconfig_get_stripe_interlace( stripe, &interlace)) == 0) { error = bytes_to_sizestr(interlace, value, interlace_units, B_TRUE); } return (error); }