1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include "xml_convert.h" 30 31 #include <errno.h> 32 #include <string.h> 33 #include <libintl.h> 34 #include <libxslt/xslt.h> 35 #include <libxslt/xsltInternals.h> 36 #include <libxslt/transform.h> 37 #include <libxslt/xsltutils.h> 38 #include <locale.h> 39 #include <unistd.h> 40 #include "volume_error.h" 41 #include "volume_output.h" 42 #include "volume_string.h" 43 44 /* 45 * IDs for localized messages in the generated command script 46 */ 47 48 #define CMD_MSG_ENVIRONMENT "Environment" 49 #define CMD_MSG_AMEND_PATH "Amend PATH" 50 #define CMD_MSG_DISK_SET_NAME "Disk set name" 51 #define CMD_MSG_FUNCTIONS "Functions" 52 /* CSTYLED */ 53 #define CMD_MSG_ECHO_AND_EXEC "Echo (verbose) and exec given command, exit on error" 54 #define CMD_MSG_GET_FULL_PATH "Get full /dev/rdsk path of given slice" 55 /* CSTYLED */ 56 #define CMD_MSG_FMTHARD_SPECIAL "Run fmthard, ignore partboot error, error if output" 57 #define CMD_MSG_MAIN "Main" 58 #define CMD_MSG_VERIFY_ROOT "Verify root" 59 #define CMD_MSG_RUN_AS_ROOT "This script must be run as root." 60 #define CMD_MSG_CHECK_FOR_VERBOSE "Check for verbose option" 61 #define CMD_MSG_DOES_DISK_SET_EXIST "Does the disk set exist?" 62 #define CMD_MSG_TAKE_DISK_SET "Take control of disk set" 63 #define CMD_MSG_CREATE_THE_DISK_SET "Create the disk set" 64 #define CMD_MSG_ADD_DISKS_TO_SET "Add disks to set" 65 #define CMD_MSG_FORMAT_SLICES "Format slices" 66 #define CMD_MSG_CREATE "Create {1} {2}" 67 #define CMD_MSG_DOES_EXIST "Does {1} exist?" 68 #define CMD_MSG_ADD_SLICES_TO "Add slices to {1}" 69 /* CSTYLED */ 70 #define CMD_MSG_ASSOCIATE_WITH_HSP "Associate {1} {2} with hot spare pool {3}" 71 72 /* 73 * ****************************************************************** 74 * 75 * Data types 76 * 77 * ****************************************************************** 78 */ 79 80 /* 81 * Encapsulates the parsing of an XML attribute 82 */ 83 typedef struct { 84 85 /* The name of the attribute */ 86 char *name; 87 88 /* 89 * A function to validate and set the XML attribute value in 90 * the given devconfig_t structure. 91 * 92 * @param name 93 * the name of the XML attribute 94 * 95 * @param value 96 * the value of the XML attribute 97 * 98 * @return 0 if the given value was valid and set 99 * successfully, non-zero otherwise. 100 */ 101 int (*validate_set)(devconfig_t *device, char *name, char *value); 102 103 /* 104 * A function to get the XML attribute value in the given 105 * devconfig_t structure. 106 * 107 * @param name 108 * the name of the XML attribute 109 * 110 * @param value 111 * the value of the XML attribute 112 * 113 * @return 0 if the given value was retrieved 114 * successfully, non-zero otherwise. 115 */ 116 int (*get_as_string)(devconfig_t *device, char *name, char **value); 117 } attr_t; 118 119 /* 120 * Encapsulates the parsing of an XML element 121 */ 122 typedef struct { 123 /* The name of the element */ 124 char *name; 125 126 /* The type of element to set in the devconfig_t */ 127 component_type_t type; 128 129 /* 130 * When converting from XML to a devconfig_t hierarchy, 131 * indicates whether to create a new devconfig_t structure in 132 * the hierarchy when this XML element is encountered. 133 */ 134 boolean_t is_hierarchical; 135 136 /* 137 * If is_hierarchical is B_TRUE, whether to use an existing 138 * devconfig_t structure of this type when this element is 139 * encountered 140 */ 141 boolean_t singleton; 142 143 /* The valid XML attributes for this element */ 144 attr_t *attributes; 145 } element_t; 146 147 typedef struct { 148 char *msgid; 149 char *message; 150 } l10nmessage_t; 151 152 /* 153 * ****************************************************************** 154 * 155 * Function prototypes 156 * 157 * ****************************************************************** 158 */ 159 160 static int validate_doc(xmlDocPtr doc, const char *name, const char *systemID); 161 static int devconfig_to_xml( 162 xmlNodePtr parent, element_t elements[], devconfig_t *device); 163 static int xml_to_devconfig( 164 xmlNodePtr cur, element_t elements[], devconfig_t *device); 165 static int compare_is_a_diskset(void *obj1, void *obj2); 166 static xmlNodePtr xml_find_node( 167 xmlNodePtr node, xmlChar *element, xmlChar *name); 168 static xmlDocPtr create_localized_message_doc(); 169 static int create_localized_message_file(char **tmpfile); 170 static int strtobool(char *str, boolean_t *value); 171 static int ofprintf_terse(void *unused, char *fmt, ...); 172 static int ofprintf_verbose(void *unused, char *fmt, ...); 173 174 static int validate_set_size( 175 devconfig_t *volume, char *attr, char *value); 176 static int validate_set_size_in_blocks( 177 devconfig_t *slice, char *attr, char *value); 178 static int validate_set_diskset_name( 179 devconfig_t *diskset, char *attr, char *name); 180 static int validate_add_available_name( 181 devconfig_t *device, char *attr, char *name); 182 static int validate_add_unavailable_name( 183 devconfig_t *device, char *attr, char *name); 184 static int validate_set_hsp_name( 185 devconfig_t *hsp, char *attr, char *name); 186 static int validate_set_disk_name( 187 devconfig_t *disk, char *attr, char *name); 188 static int validate_set_slice_name( 189 devconfig_t *slice, char *attr, char *name); 190 static int validate_set_slice_start_block( 191 devconfig_t *slice, char *attr, char *value); 192 static int validate_set_volume_name( 193 devconfig_t *volume, char *attr, char *name); 194 static int validate_set_stripe_interlace( 195 devconfig_t *stripe, char *attr, char *value); 196 static int validate_set_stripe_mincomp( 197 devconfig_t *stripe, char *attr, char *value); 198 static int validate_set_stripe_maxcomp( 199 devconfig_t *stripe, char *attr, char *value); 200 static int validate_set_volume_usehsp( 201 devconfig_t *volume, char *attr, char *value); 202 static int validate_set_mirror_nsubmirrors( 203 devconfig_t *mirror, char *attr, char *value); 204 static int validate_set_mirror_read( 205 devconfig_t *mirror, char *attr, char *value); 206 static int validate_set_mirror_write( 207 devconfig_t *mirror, char *attr, char *value); 208 static int validate_set_mirror_passnum( 209 devconfig_t *mirror, char *attr, char *value); 210 static int validate_set_volume_redundancy( 211 devconfig_t *volume, char *attr, char *value); 212 static int validate_set_volume_datapaths( 213 devconfig_t *volume, char *attr, char *value); 214 215 static int get_as_string_name( 216 devconfig_t *device, char *attr, char **value); 217 static int get_as_string_mirror_passnum( 218 devconfig_t *mirror, char *attr, char **value); 219 static int get_as_string_mirror_read( 220 devconfig_t *mirror, char *attr, char **value); 221 static int get_as_string_mirror_write( 222 devconfig_t *mirror, char *attr, char **value); 223 static int get_as_string_size_in_blocks( 224 devconfig_t *device, char *attr, char **value); 225 static int get_as_string_slice_start_block( 226 devconfig_t *slice, char *attr, char **value); 227 static int get_as_string_stripe_interlace( 228 devconfig_t *stripe, char *attr, char **value); 229 230 /* 231 * ****************************************************************** 232 * 233 * Data 234 * 235 * ****************************************************************** 236 */ 237 238 /* Valid units for the size attribute */ 239 units_t size_units[] = { 240 {UNIT_KILOBYTES, BYTES_PER_KILOBYTE}, 241 {UNIT_MEGABYTES, BYTES_PER_MEGABYTE}, 242 {UNIT_GIGABYTES, BYTES_PER_GIGABYTE}, 243 {UNIT_TERABYTES, BYTES_PER_TERABYTE}, 244 {NULL, 0} 245 }; 246 247 /* Valid units for the interlace attribute */ 248 units_t interlace_units[] = { 249 {UNIT_BLOCKS, BYTES_PER_BLOCK}, 250 {UNIT_KILOBYTES, BYTES_PER_KILOBYTE}, 251 {UNIT_MEGABYTES, BYTES_PER_MEGABYTE}, 252 {NULL, 0} 253 }; 254 255 /* <diskset> attributes */ 256 static attr_t diskset_attrs[] = { 257 { ATTR_NAME, validate_set_diskset_name, get_as_string_name }, 258 { NULL, NULL, NULL } 259 }; 260 261 /* <available> attributes */ 262 static attr_t available_attrs[] = { 263 { ATTR_NAME, validate_add_available_name, NULL }, 264 { NULL, NULL, NULL } 265 }; 266 267 /* <unavailable> attributes */ 268 static attr_t unavailable_attrs[] = { 269 { ATTR_NAME, validate_add_unavailable_name, NULL }, 270 { NULL, NULL, NULL } 271 }; 272 273 /* <hsp> attributes */ 274 static attr_t hsp_attrs[] = { 275 { ATTR_NAME, validate_set_hsp_name, get_as_string_name }, 276 { NULL, NULL, NULL } 277 }; 278 279 /* <disk> attributes */ 280 static attr_t disk_attrs[] = { 281 { ATTR_NAME, validate_set_disk_name, get_as_string_name }, 282 { NULL, NULL, NULL } 283 }; 284 285 /* <slice> attributes */ 286 static attr_t slice_attrs[] = { 287 { ATTR_NAME, validate_set_slice_name, get_as_string_name }, 288 { ATTR_SIZEINBLOCKS, validate_set_size_in_blocks, 289 get_as_string_size_in_blocks }, 290 { ATTR_SLICE_STARTSECTOR, validate_set_slice_start_block, 291 get_as_string_slice_start_block }, 292 { NULL, NULL, NULL } 293 }; 294 295 /* <stripe> attributes */ 296 static attr_t stripe_attrs[] = { 297 { ATTR_NAME, validate_set_volume_name, get_as_string_name }, 298 { ATTR_SIZEINBYTES, validate_set_size, NULL }, 299 { ATTR_STRIPE_MINCOMP, validate_set_stripe_mincomp, NULL }, 300 { ATTR_STRIPE_MAXCOMP, validate_set_stripe_maxcomp, NULL }, 301 { ATTR_STRIPE_INTERLACE, validate_set_stripe_interlace, 302 get_as_string_stripe_interlace }, 303 { ATTR_VOLUME_USEHSP, validate_set_volume_usehsp, NULL }, 304 { NULL, NULL, NULL } 305 }; 306 307 /* <concat> attributes */ 308 static attr_t concat_attrs[] = { 309 { ATTR_NAME, validate_set_volume_name, get_as_string_name }, 310 { ATTR_SIZEINBYTES, validate_set_size, NULL }, 311 { ATTR_VOLUME_USEHSP, validate_set_volume_usehsp, NULL }, 312 { NULL, NULL, NULL } 313 }; 314 315 /* <mirror> attributes */ 316 static attr_t mirror_attrs[] = { 317 { ATTR_NAME, validate_set_volume_name, get_as_string_name }, 318 { ATTR_MIRROR_NSUBMIRRORS, validate_set_mirror_nsubmirrors, NULL }, 319 { ATTR_SIZEINBYTES, validate_set_size, NULL }, 320 { ATTR_MIRROR_READ, validate_set_mirror_read, 321 get_as_string_mirror_read }, 322 { ATTR_MIRROR_WRITE, validate_set_mirror_write, 323 get_as_string_mirror_write }, 324 { ATTR_MIRROR_PASSNUM, validate_set_mirror_passnum, 325 get_as_string_mirror_passnum }, 326 { ATTR_VOLUME_USEHSP, validate_set_volume_usehsp, NULL }, 327 { NULL, NULL, NULL } 328 }; 329 330 /* <volume> attributes */ 331 static attr_t volume_attrs[] = { 332 { ATTR_NAME, validate_set_volume_name, get_as_string_name }, 333 { ATTR_SIZEINBYTES, validate_set_size, NULL }, 334 { ATTR_VOLUME_REDUNDANCY, validate_set_volume_redundancy, NULL }, 335 { ATTR_VOLUME_FAULTRECOVERY, validate_set_volume_usehsp, NULL }, 336 { ATTR_VOLUME_DATAPATHS, validate_set_volume_datapaths, NULL }, 337 { NULL, NULL, NULL } 338 }; 339 340 /* volume-request elements */ 341 static element_t request_elements[] = { 342 { ELEMENT_DISKSET, TYPE_DISKSET, B_FALSE, B_FALSE, diskset_attrs }, 343 { ELEMENT_AVAILABLE, TYPE_UNKNOWN, B_FALSE, B_FALSE, available_attrs }, 344 { ELEMENT_UNAVAILABLE, TYPE_UNKNOWN, B_FALSE, B_FALSE, 345 unavailable_attrs }, 346 { ELEMENT_HSP, TYPE_HSP, B_TRUE, B_FALSE, hsp_attrs }, 347 { ELEMENT_SLICE, TYPE_SLICE, B_TRUE, B_FALSE, slice_attrs }, 348 { ELEMENT_STRIPE, TYPE_STRIPE, B_TRUE, B_FALSE, stripe_attrs }, 349 { ELEMENT_CONCAT, TYPE_CONCAT, B_TRUE, B_FALSE, concat_attrs }, 350 { ELEMENT_MIRROR, TYPE_MIRROR, B_TRUE, B_FALSE, mirror_attrs }, 351 { ELEMENT_VOLUME, TYPE_VOLUME, B_TRUE, B_FALSE, volume_attrs }, 352 { NULL, NULL, B_FALSE, B_FALSE, NULL } 353 }; 354 355 /* volume-defaults elements */ 356 static element_t default_elements[] = { 357 { ELEMENT_DISKSET, TYPE_DISKSET, B_TRUE, B_FALSE, diskset_attrs }, 358 { ELEMENT_AVAILABLE, TYPE_UNKNOWN, B_FALSE, B_TRUE, available_attrs }, 359 { ELEMENT_UNAVAILABLE, TYPE_UNKNOWN, B_FALSE, B_TRUE, 360 unavailable_attrs }, 361 { ELEMENT_HSP, TYPE_HSP, B_TRUE, B_TRUE, hsp_attrs }, 362 { ELEMENT_SLICE, TYPE_SLICE, B_TRUE, B_TRUE, slice_attrs }, 363 { ELEMENT_STRIPE, TYPE_STRIPE, B_TRUE, B_TRUE, stripe_attrs }, 364 { ELEMENT_CONCAT, TYPE_CONCAT, B_TRUE, B_TRUE, concat_attrs }, 365 { ELEMENT_MIRROR, TYPE_MIRROR, B_TRUE, B_TRUE, mirror_attrs }, 366 { ELEMENT_VOLUME, TYPE_VOLUME, B_TRUE, B_TRUE, volume_attrs }, 367 { NULL, NULL, B_FALSE, B_FALSE, NULL } 368 }; 369 370 /* volume-config elements */ 371 static element_t config_elements[] = { 372 { ELEMENT_DISKSET, TYPE_DISKSET, B_FALSE, B_FALSE, diskset_attrs }, 373 { ELEMENT_DISK, TYPE_DRIVE, B_TRUE, B_FALSE, disk_attrs }, 374 { ELEMENT_SLICE, TYPE_SLICE, B_TRUE, B_FALSE, slice_attrs }, 375 { ELEMENT_HSP, TYPE_HSP, B_TRUE, B_FALSE, hsp_attrs }, 376 { ELEMENT_STRIPE, TYPE_STRIPE, B_TRUE, B_FALSE, stripe_attrs }, 377 { ELEMENT_CONCAT, TYPE_CONCAT, B_TRUE, B_FALSE, concat_attrs }, 378 { ELEMENT_MIRROR, TYPE_MIRROR, B_TRUE, B_FALSE, mirror_attrs }, 379 { NULL, NULL, B_FALSE, B_FALSE, NULL } 380 }; 381 382 /* 383 * ****************************************************************** 384 * 385 * External functions 386 * 387 * ****************************************************************** 388 */ 389 390 /* 391 * Initialize the XML parser, setting defaults across all XML 392 * routines. 393 */ 394 void 395 init_xml() 396 { 397 /* COMPAT: Do not generate nodes for formatting spaces */ 398 LIBXML_TEST_VERSION 399 xmlKeepBlanksDefault(0); 400 401 /* Turn on line numbers for debugging */ 402 xmlLineNumbersDefault(1); 403 404 /* Substitute entities as files are parsed */ 405 xmlSubstituteEntitiesDefault(1); 406 407 /* Don't load external entity subsets */ 408 xmlLoadExtDtdDefaultValue = 0; 409 410 /* Don't validate against DTD by default */ 411 xmlDoValidityCheckingDefaultValue = 0; 412 413 /* Set up output handlers for XML parsing */ 414 xmlDefaultSAXHandler.warning = (warningSAXFunc)ofprintf_verbose; 415 xmlDefaultSAXHandler.error = (errorSAXFunc)ofprintf_terse; 416 xmlDefaultSAXHandler.fatalError = (fatalErrorSAXFunc)ofprintf_terse; 417 } 418 419 /* 420 * Clean up any remaining structures before exiting. 421 */ 422 void 423 cleanup_xml() 424 { 425 xsltCleanupGlobals(); 426 xmlCleanupParser(); 427 } 428 429 /* 430 * Converts a volume-request XML document into a request_t. 431 * 432 * @param doc 433 * an existing volume-request XML document 434 * 435 * @param request 436 * RETURN: a new request_t which must be freed via 437 * free_request 438 * 439 * @return 0 on success, non-zero otherwise. 440 */ 441 int 442 xml_to_request( 443 xmlDocPtr doc, 444 request_t **request) 445 { 446 int error = 0; 447 448 *request = NULL; 449 450 /* Validate doc against known DTD */ 451 if ((error = validate_doc( 452 doc, ELEMENT_VOLUMEREQUEST, VOLUME_REQUEST_DTD_LOC)) == 0) { 453 454 /* Create a request */ 455 if ((error = new_request(request)) == 0) { 456 457 /* Convert the XML doc into a request_t */ 458 error = xml_to_devconfig(xmlDocGetRootElement(doc), 459 request_elements, request_get_diskset_req(*request)); 460 } 461 } 462 463 return (error); 464 } 465 466 /* 467 * Converts a volume-defaults XML document into a defaults_t. 468 * 469 * @param doc 470 * an existing volume-defaults XML document 471 * 472 * @param defaults 473 * RETURN: a new defaults_t which must be freed via 474 * free_defaults 475 * 476 * @return 0 on success, non-zero otherwise. 477 */ 478 int 479 xml_to_defaults( 480 xmlDocPtr doc, 481 defaults_t **defaults) 482 { 483 int error = 0; 484 485 *defaults = NULL; 486 487 /* Validate doc against known DTD */ 488 if ((error = validate_doc(doc, ELEMENT_VOLUMEDEFAULTS, 489 VOLUME_DEFAULTS_DTD_LOC)) == 0) { 490 491 /* Create request defaults */ 492 if ((error = new_defaults(defaults)) == 0) { 493 494 devconfig_t *global; 495 496 /* Get defaults for all disk sets */ 497 if ((error = defaults_get_diskset_by_name( 498 *defaults, NULL, &global)) == 0) { 499 500 /* Populate the global devconfig_t from the XML doc */ 501 if ((error = xml_to_devconfig(xmlDocGetRootElement(doc), 502 default_elements, global)) == 0) { 503 504 /* Get the components of the global devconfig_t */ 505 dlist_t *list = devconfig_get_components(global); 506 507 /* 508 * Move all named disk set settings out from 509 * under global settings 510 */ 511 /* CONSTANTCONDITION */ 512 while (1) { 513 dlist_t *removed = NULL; 514 devconfig_t *component; 515 516 /* Remove named disk set from under global */ 517 list = dlist_remove_equivalent_item( 518 list, NULL, compare_is_a_diskset, &removed); 519 520 if (removed == NULL) { 521 /* No named disk set found */ 522 break; 523 } 524 525 component = removed->obj; 526 527 /* Append named disk set to disk set list */ 528 defaults_set_disksets(*defaults, 529 dlist_append(dlist_new_item(component), 530 defaults_get_disksets(*defaults), AT_TAIL)); 531 } 532 } 533 } 534 } 535 } 536 537 return (error); 538 } 539 540 /* 541 * Converts a volume-config XML document into a devconfig_t. 542 * 543 * @param doc 544 * an existing volume-config XML document 545 * 546 * @param config 547 * RETURN: a new devconfig_t which must be freed via 548 * free_devconfig 549 * 550 * @return 0 on success, non-zero otherwise. 551 */ 552 int 553 xml_to_config( 554 xmlDocPtr doc, 555 devconfig_t **config) 556 { 557 int error = 0; 558 559 *config = NULL; 560 561 /* Validate doc against known DTD */ 562 if ((error = validate_doc( 563 doc, ELEMENT_VOLUMECONFIG, VOLUME_CONFIG_DTD_LOC)) == 0) { 564 565 /* Create a devconfig_t */ 566 if ((error = new_devconfig(config, TYPE_DISKSET)) == 0) { 567 568 /* Populate the devconfig_t from the XML doc */ 569 error = xml_to_devconfig( 570 xmlDocGetRootElement(doc), config_elements, *config); 571 } 572 } 573 574 return (error); 575 } 576 577 /* 578 * Converts a devconfig_t into a volume-config XML document. 579 * 580 * @param config 581 * an existing devconfig_t representing a volume 582 * configuration. 583 * 584 * @param doc 585 * RETURN: a new volume-config XML document which must be 586 * freed via xmlFreeDoc 587 * 588 * @return 0 on success, non-zero otherwise. 589 */ 590 int 591 config_to_xml( 592 devconfig_t *config, 593 xmlDocPtr *doc) 594 { 595 xmlNodePtr root; 596 int error = 0; 597 598 /* Create the XML document */ 599 *doc = xmlNewDoc((xmlChar *)"1.0"); 600 601 /* Create the root node */ 602 root = xmlNewDocNode( 603 *doc, NULL, (xmlChar *)ELEMENT_VOLUMECONFIG, NULL); 604 xmlAddChild((xmlNodePtr)*doc, (xmlNodePtr)root); 605 606 /* Create sub-nodes from the config devconfig_t */ 607 if ((error = devconfig_to_xml(root, config_elements, config)) == 0) { 608 609 /* Add DTD node and validate */ 610 error = validate_doc( 611 *doc, ELEMENT_VOLUMECONFIG, VOLUME_CONFIG_DTD_LOC); 612 } 613 614 if (error) { 615 xmlFreeDoc(*doc); 616 } 617 618 return (error); 619 } 620 621 /* 622 * Converts a volume-config XML document into a Bourne shell script. 623 * 624 * @param doc 625 * an existing volume-config XML document 626 * 627 * @param commands 628 * RETURN: a new char* which must be freed 629 * 630 * @return 0 on success, non-zero otherwise. 631 */ 632 int 633 xml_to_commands( 634 xmlDocPtr doc, 635 char **commands) 636 { 637 char *tmpfile = NULL; 638 int error = 0; 639 xsltStylesheetPtr style = NULL; 640 641 /* Read in XSL stylesheet as a normal XML document */ 642 xmlDocPtr xsl_doc = xmlSAXParseFile((xmlSAXHandlerPtr) 643 &xmlDefaultSAXHandler, VOLUME_COMMAND_XSL_LOC, 0); 644 645 if (xsl_doc != NULL && xsl_doc->xmlChildrenNode != NULL) { 646 647 /* 648 * Find the "msgfile" variable node. This is where 649 * we'll set the location of the file we'll create 650 * containing the localized messages. 651 */ 652 xmlNodePtr msgfile_node = xml_find_node( 653 xmlDocGetRootElement(xsl_doc), (xmlChar *)ELEMENT_VARIABLE, 654 (xmlChar *)NAME_L10N_MESSAGE_FILE); 655 656 /* 657 * Find the "lang" node. This is where we'll set the 658 * current locale. 659 */ 660 xmlNodePtr lang_node = xml_find_node(xmlDocGetRootElement(xsl_doc), 661 (xmlChar *)ELEMENT_PARAM, (xmlChar *)NAME_LANG); 662 663 /* 664 * Ignore if the nodes are not found -- the script 665 * will default to the C locale. 666 */ 667 if (msgfile_node != NULL && lang_node != NULL) { 668 /* Get/set current locale in the "lang" node */ 669 char *locale = setlocale(LC_MESSAGES, NULL); 670 xmlNodeSetContent(lang_node, (xmlChar *)locale); 671 672 /* Write localized messages to a temporary file */ 673 if ((error = create_localized_message_file(&tmpfile)) == 0) { 674 675 char *newsel; 676 677 /* Clear current value of select attribute, if any */ 678 xmlChar *cursel = xmlGetProp( 679 msgfile_node, (xmlChar *)ATTR_SELECT); 680 if (cursel != NULL) { 681 xmlFree(cursel); 682 } 683 684 /* 685 * The select attribute calls the XSLT function 686 * document() to load an external XML file 687 */ 688 newsel = stralloccat(3, "document('", tmpfile, "')"); 689 690 if (newsel == NULL) { 691 volume_set_error(gettext("out of memory")); 692 error = -1; 693 } else { 694 695 /* Set the new value of the select attribute */ 696 xmlSetProp(msgfile_node, 697 (xmlChar *)ATTR_SELECT, (xmlChar *)newsel); 698 699 free(newsel); 700 } 701 } 702 } 703 704 if (error == 0) { 705 style = xsltParseStylesheetDoc(xsl_doc); 706 } 707 } 708 709 if (style == NULL) { 710 volume_set_error( 711 gettext("could not load stylesheet from %s"), 712 VOLUME_COMMAND_XSL_LOC); 713 error = -1; 714 } else { 715 716 xmlDocPtr result = xsltApplyStylesheet(style, doc, NULL); 717 718 if (result == NULL) { 719 volume_set_error( 720 gettext("could not apply stylesheet to volume-config")); 721 error = -1; 722 } else { 723 int length; 724 725 if (xsltSaveResultToString((xmlChar **)commands, 726 &length, result, style) == -1) { 727 error = ENOMEM; 728 } 729 } 730 731 xsltFreeStylesheet(style); 732 } 733 734 if (tmpfile != NULL) { 735 /* Ignore failure */ 736 unlink(tmpfile); 737 738 free(tmpfile); 739 } 740 741 return (error); 742 } 743 744 /* 745 * ****************************************************************** 746 * 747 * Static functions 748 * 749 * ****************************************************************** 750 */ 751 752 /* 753 * Sets the external DTD node in the given XML document and then 754 * validates it. 755 * 756 * @param doc 757 * an existing XML document 758 * 759 * @param name 760 * the expected root element name of the XML document 761 * 762 * @param systemID 763 * the location of the DTD 764 * 765 * @return 0 on success, non-zero otherwise. 766 */ 767 static int 768 validate_doc( 769 xmlDocPtr doc, 770 const char *name, 771 const char *systemID) 772 { 773 xmlValidCtxt context; 774 xmlDtdPtr dtd; 775 776 if (doc == NULL) { 777 volume_set_error(gettext("NULL %s document"), name); 778 return (-1); 779 } 780 781 /* 782 * Assume that we can't trust any DTD but our own. 783 */ 784 785 /* Was a DTD (external or internal) included in the document? */ 786 if ((dtd = xmlGetIntSubset(doc)) != NULL) { 787 /* Remove the DTD node */ 788 oprintf(OUTPUT_DEBUG, gettext("Removing DTD from %s\n"), name); 789 xmlUnlinkNode((xmlNodePtr)dtd); 790 xmlFreeDtd(dtd); 791 } 792 793 /* Create the (external) DTD node */ 794 oprintf(OUTPUT_DEBUG, 795 gettext("Creating new external DTD for %s\n"), name); 796 dtd = xmlCreateIntSubset( 797 doc, (xmlChar *)name, NULL, (xmlChar *)systemID); 798 if (dtd == NULL) { 799 volume_set_error( 800 gettext("could not create DTD node from %s"), systemID); 801 return (-1); 802 } 803 804 /* Validate against DTD */ 805 oprintf(OUTPUT_DEBUG, gettext("Validating %s against DTD\n"), name); 806 context.userData = NULL; 807 context.error = (xmlValidityErrorFunc)ofprintf_terse; 808 context.warning = (xmlValidityWarningFunc)ofprintf_terse; 809 if (!xmlValidateDocument(&context, doc)) { 810 volume_set_error(gettext("invalid %s"), name); 811 return (-1); 812 } 813 814 return (0); 815 } 816 817 /* 818 * Converts a devconfig_t into an XML node subject to the rules in 819 * the given element_t array. 820 * 821 * @param parent 822 * the XML node to which to add new XML nodes resulting 823 * from conversion of the given devconfig_t 824 * 825 * @param elements 826 * the element_ts that describe the structure of the XML 827 * document and govern the conversion of the given 828 * devconfig_t 829 * 830 * @param device 831 * the devconfig_t to convert 832 * 833 * @return 0 on success, non-zero otherwise. 834 */ 835 static int 836 devconfig_to_xml( 837 xmlNodePtr parent, 838 element_t elements[], 839 devconfig_t *device) 840 { 841 int i; 842 int error = 0; 843 xmlNodePtr node = NULL; 844 845 /* Get device type */ 846 component_type_t type; 847 if ((error = devconfig_get_type(device, &type)) != 0) { 848 return (error); 849 } 850 851 /* Search for this element definition */ 852 for (i = 0; elements[i].name != NULL; i++) { 853 element_t *element = &(elements[i]); 854 855 if (element->type == type) { 856 int j; 857 char **array; 858 dlist_t *components; 859 860 oprintf(OUTPUT_DEBUG, gettext("Element: %s\n"), 861 devconfig_type_to_str(type)); 862 863 /* Create the XML node */ 864 node = xmlNewChild( 865 parent, NULL, (xmlChar *)element->name, NULL); 866 867 /* For each attribute defined for this element... */ 868 for (j = 0; element->attributes[j].name != NULL; j++) { 869 attr_t *attribute = &(element->attributes[j]); 870 char *value; 871 872 /* Is there a valid accessor for this attribute? */ 873 if (attribute->get_as_string != NULL) { 874 875 /* Get the attribute value from the device */ 876 switch (error = attribute->get_as_string( 877 device, attribute->name, &value)) { 878 879 /* Attribute is set in this device */ 880 case 0: 881 oprintf(OUTPUT_DEBUG, " %s: %s\n", 882 attribute->name, value); 883 884 /* Set the value in the XML node */ 885 xmlSetProp(node, (uchar_t *)attribute->name, 886 (uchar_t *)value); 887 free(value); 888 889 /* FALLTHROUGH */ 890 891 /* Attribute is not set in this device */ 892 case ERR_ATTR_UNSET: 893 894 error = 0; 895 break; 896 897 /* Error */ 898 default: 899 return (error); 900 } 901 } 902 } 903 904 /* Is this node hierarchical? */ 905 if (element->is_hierarchical == B_FALSE) { 906 node = parent; 907 } 908 909 /* Create <available> nodes */ 910 array = devconfig_get_available(device); 911 if (array != NULL) { 912 for (j = 0; array[j] != NULL; j++) { 913 xmlNodePtr child = xmlNewChild( 914 node, NULL, (xmlChar *)ELEMENT_AVAILABLE, NULL); 915 xmlSetProp(child, 916 (xmlChar *)ATTR_NAME, (xmlChar *)array[j]); 917 } 918 } 919 920 /* Create <unavailable> nodes */ 921 array = devconfig_get_unavailable(device); 922 if (array != NULL) { 923 for (j = 0; array[j] != NULL; j++) { 924 xmlNodePtr child = xmlNewChild( 925 node, NULL, (xmlChar *)ELEMENT_UNAVAILABLE, NULL); 926 xmlSetProp(child, 927 (xmlChar *)ATTR_NAME, (xmlChar *)array[j]); 928 } 929 } 930 931 /* 932 * Recursively convert subcomponents of this device to 933 * XML, taking care to encode them in the order 934 * specified in the element_t list (which should 935 * mirror what's expected by the DTD). 936 */ 937 938 /* For each element type... */ 939 for (j = 0; elements[j].name != NULL; j++) { 940 941 /* For each component of this device... */ 942 for (components = devconfig_get_components(device); 943 components != NULL && error == 0; 944 components = components->next) { 945 946 devconfig_t *component = (devconfig_t *)components->obj; 947 component_type_t t; 948 949 /* Are the types the same? */ 950 if ((error = devconfig_get_type(component, &t)) != 0) { 951 return (error); 952 } else { 953 if (elements[j].type == t) { 954 /* Encode child */ 955 error = devconfig_to_xml( 956 node, elements, component); 957 } 958 } 959 } 960 } 961 962 /* Element found */ 963 break; 964 } 965 } 966 967 /* Was this device successfully converted? */ 968 if (node == NULL) { 969 volume_set_error( 970 gettext("can't convert device of type \"%s\" to XML element"), 971 devconfig_type_to_str(type)); 972 error = -1; 973 } 974 975 return (error); 976 } 977 978 /* 979 * Converts an XML node into a devconfig_t subject to the rules in 980 * the given element_t array. 981 * 982 * @param cure 983 * the existing XML node to convert 984 * 985 * @param elements 986 * the element_ts that describe the structure of the XML 987 * document and govern the conversion of the given XML 988 * node 989 * 990 * @param device 991 * the devconfig_t node to which to add new devconfig_ts 992 * resulting from conversion of the given XML node 993 * 994 * @return 0 on success, non-zero otherwise. 995 */ 996 static int 997 xml_to_devconfig( 998 xmlNodePtr cur, 999 element_t elements[], 1000 devconfig_t *device) 1001 { 1002 int error = 0; 1003 1004 /* For each child node... */ 1005 for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { 1006 int i; 1007 boolean_t parsed_elem = B_FALSE; 1008 1009 /* Search for this element definition */ 1010 for (i = 0; elements[i].name != NULL; i++) { 1011 element_t *element = &(elements[i]); 1012 1013 if (xmlStrcmp(cur->name, (xmlChar *)element->name) == 0) { 1014 int j; 1015 devconfig_t *component = NULL; 1016 1017 /* Flag that this element has been parsed */ 1018 parsed_elem = B_TRUE; 1019 1020 oprintf(OUTPUT_DEBUG, gettext("line %d: Element <%s>\n"), 1021 XML_GET_LINE(cur), cur->name); 1022 1023 /* Should a new device be created for this element? */ 1024 if (element->is_hierarchical == B_TRUE) { 1025 1026 /* Should we use an existing device of this type? */ 1027 if (element->singleton) { 1028 devconfig_get_component( 1029 device, element->type, &component, B_FALSE); 1030 } 1031 1032 if (component == NULL) { 1033 oprintf(OUTPUT_DEBUG, 1034 gettext("Creating new device\n")); 1035 1036 /* Create device of this type */ 1037 if ((error = new_devconfig( 1038 &component, element->type)) != 0) { 1039 return (error); 1040 } 1041 1042 /* Add component to the toplevel device */ 1043 devconfig_set_components( 1044 device, dlist_append(dlist_new_item(component), 1045 devconfig_get_components(device), AT_TAIL)); 1046 } 1047 } else { 1048 component = device; 1049 } 1050 1051 /* For each attribute defined for this element... */ 1052 for (j = 0; element->attributes[j].name != NULL; j++) { 1053 attr_t *attribute = &(element->attributes[j]); 1054 1055 /* Get the value of this attribute */ 1056 char *value = (char *) 1057 xmlGetProp(cur, (xmlChar *)attribute->name); 1058 1059 /* Was this attribute specified? */ 1060 if (value != NULL) { 1061 oprintf(OUTPUT_DEBUG, 1062 gettext("line %d:\tAttribute %s=%s\n"), 1063 XML_GET_LINE(cur), attribute->name, value); 1064 1065 /* Set this value in the device */ 1066 if ((error = attribute->validate_set( 1067 component, attribute->name, value)) != 0) { 1068 return (error); 1069 } 1070 } 1071 } 1072 1073 /* Get recursive sub-elements */ 1074 if ((error = xml_to_devconfig( 1075 cur, elements, component)) != 0) { 1076 return (error); 1077 } 1078 1079 /* Element found */ 1080 break; 1081 } 1082 } 1083 1084 1085 /* Make sure all non-text/comment elements were parsed */ 1086 if (parsed_elem == B_FALSE && 1087 xmlStrcmp(cur->name, (xmlChar *)ELEMENT_TEXT) != 0 && 1088 xmlStrcmp(cur->name, (xmlChar *)ELEMENT_COMMENT) != 0) { 1089 1090 oprintf(OUTPUT_DEBUG, gettext("Element <%s> NOT PARSED!!!\n"), 1091 cur->name); 1092 } 1093 } 1094 1095 return (0); 1096 } 1097 1098 /* 1099 * Returns 0 if obj2 (devconfig_t *) is a disk set, 1 otherwise. 1100 */ 1101 static int 1102 compare_is_a_diskset( 1103 void *obj1, 1104 void *obj2) 1105 { 1106 return (devconfig_isA( 1107 (devconfig_t *)obj2, TYPE_DISKSET) == B_TRUE ? 0 : 1); 1108 } 1109 1110 /* 1111 * Recursively searches the given xmlNodePtr for an element of the 1112 * specified type and name. 1113 * 1114 * @param node 1115 * the root node to search 1116 * 1117 * @param element 1118 * the name of the element type 1119 * 1120 * @param name 1121 * the value of the name attribute 1122 * 1123 * @return a valid xmlNodePtr if an element of the specified 1124 * type and name was found, NULL otherwise. 1125 */ 1126 static xmlNodePtr 1127 xml_find_node( 1128 xmlNodePtr node, 1129 xmlChar *element, 1130 xmlChar *name) 1131 { 1132 xmlNodePtr child; 1133 1134 /* Is the element the right type? */ 1135 if (xmlStrcmp(element, node->name) == 0 && 1136 1137 /* Does this element's name attribute match? */ 1138 xmlStrcmp(name, xmlGetProp(node, (xmlChar *)ATTR_NAME)) == 0) { 1139 1140 return (node); 1141 } 1142 1143 /* Check child nodes */ 1144 for (child = node->xmlChildrenNode; child != NULL; 1145 child = child->next) { 1146 xmlNodePtr found = xml_find_node(child, element, name); 1147 1148 if (found != NULL) { 1149 return (found); 1150 } 1151 } 1152 1153 return (NULL); 1154 } 1155 1156 /* 1157 * Creates an XML document containing all of the localized message 1158 * strings for the generated command script. 1159 * 1160 * @return a xmlDocPtr which must be freed via xmlFreeDoc 1161 */ 1162 static xmlDocPtr 1163 create_localized_message_doc() 1164 { 1165 int i; 1166 char *locale; 1167 xmlDocPtr doc; 1168 xmlNodePtr root; 1169 l10nmessage_t _cmd_messages[21]; 1170 1171 /* Create the XML document */ 1172 doc = xmlNewDoc((xmlChar *)"1.0"); 1173 1174 /* Create the root node */ 1175 root = xmlNewDocNode( 1176 doc, NULL, (xmlChar *)ELEMENT_L10N, NULL); 1177 xmlAddChild((xmlNodePtr) doc, (xmlNodePtr)root); 1178 1179 _cmd_messages[0].msgid = CMD_MSG_ENVIRONMENT; 1180 _cmd_messages[0].message = gettext(CMD_MSG_ENVIRONMENT); 1181 _cmd_messages[1].msgid = CMD_MSG_AMEND_PATH; 1182 _cmd_messages[1].message = gettext(CMD_MSG_AMEND_PATH); 1183 _cmd_messages[2].msgid = CMD_MSG_DISK_SET_NAME; 1184 _cmd_messages[2].message = gettext(CMD_MSG_DISK_SET_NAME); 1185 _cmd_messages[3].msgid = CMD_MSG_FUNCTIONS; 1186 _cmd_messages[3].message = gettext(CMD_MSG_FUNCTIONS); 1187 _cmd_messages[4].msgid = CMD_MSG_ECHO_AND_EXEC; 1188 _cmd_messages[4].message = gettext(CMD_MSG_ECHO_AND_EXEC); 1189 _cmd_messages[5].msgid = CMD_MSG_FMTHARD_SPECIAL; 1190 _cmd_messages[5].message = gettext(CMD_MSG_FMTHARD_SPECIAL); 1191 _cmd_messages[6].msgid = CMD_MSG_GET_FULL_PATH; 1192 _cmd_messages[6].message = gettext(CMD_MSG_GET_FULL_PATH); 1193 _cmd_messages[7].msgid = CMD_MSG_MAIN; 1194 _cmd_messages[7].message = gettext(CMD_MSG_MAIN); 1195 _cmd_messages[8].msgid = CMD_MSG_VERIFY_ROOT; 1196 _cmd_messages[8].message = gettext(CMD_MSG_VERIFY_ROOT); 1197 _cmd_messages[9].msgid = CMD_MSG_RUN_AS_ROOT; 1198 _cmd_messages[9].message = gettext(CMD_MSG_RUN_AS_ROOT); 1199 _cmd_messages[10].msgid = CMD_MSG_CHECK_FOR_VERBOSE; 1200 _cmd_messages[10].message = gettext(CMD_MSG_CHECK_FOR_VERBOSE); 1201 _cmd_messages[11].msgid = (CMD_MSG_DOES_DISK_SET_EXIST); 1202 _cmd_messages[11].message = gettext(CMD_MSG_DOES_DISK_SET_EXIST); 1203 _cmd_messages[12].msgid = (CMD_MSG_TAKE_DISK_SET); 1204 _cmd_messages[12].message = gettext(CMD_MSG_TAKE_DISK_SET); 1205 _cmd_messages[13].msgid = (CMD_MSG_CREATE_THE_DISK_SET); 1206 _cmd_messages[13].message = gettext(CMD_MSG_CREATE_THE_DISK_SET); 1207 _cmd_messages[14].msgid = (CMD_MSG_ADD_DISKS_TO_SET); 1208 _cmd_messages[14].message = gettext(CMD_MSG_ADD_DISKS_TO_SET); 1209 _cmd_messages[15].msgid = (CMD_MSG_FORMAT_SLICES); 1210 _cmd_messages[15].message = gettext(CMD_MSG_FORMAT_SLICES); 1211 _cmd_messages[16].msgid = (CMD_MSG_CREATE); 1212 _cmd_messages[16].message = gettext(CMD_MSG_CREATE); 1213 _cmd_messages[17].msgid = (CMD_MSG_DOES_EXIST); 1214 _cmd_messages[17].message = gettext(CMD_MSG_DOES_EXIST); 1215 _cmd_messages[18].msgid = (CMD_MSG_ADD_SLICES_TO); 1216 _cmd_messages[18].message = gettext(CMD_MSG_ADD_SLICES_TO); 1217 _cmd_messages[19].msgid = (CMD_MSG_ASSOCIATE_WITH_HSP); 1218 _cmd_messages[19].message = gettext(CMD_MSG_ASSOCIATE_WITH_HSP); 1219 _cmd_messages[20].msgid = NULL; 1220 1221 /* Get/set current locale in the "lang" node */ 1222 locale = setlocale(LC_MESSAGES, NULL); 1223 1224 /* Add localized <message> elements to stylesheet */ 1225 for (i = 0; _cmd_messages[i].msgid != NULL; i++) { 1226 xmlNsPtr ns = xmlNewNs(NULL, NULL, NULL); 1227 1228 xmlNodePtr node = xmlNewTextChild( 1229 root, ns, (xmlChar *)ELEMENT_MESSAGE, 1230 (xmlChar *)_cmd_messages[i].message); 1231 /* Lang attribute */ 1232 xmlSetProp(node, 1233 (xmlChar *)ATTR_LANG, (xmlChar *)locale); 1234 1235 /* Message ID attribute */ 1236 xmlSetProp(node, (xmlChar *)ATTR_MESSAGEID, 1237 (xmlChar *)_cmd_messages[i].msgid); 1238 } 1239 1240 if (get_max_verbosity() >= OUTPUT_DEBUG) { 1241 xmlChar *text; 1242 /* Get the text dump */ 1243 xmlDocDumpFormatMemory(doc, &text, NULL, 1); 1244 oprintf(OUTPUT_DEBUG, 1245 gettext("Generated message file:\n%s"), text); 1246 xmlFree(text); 1247 } 1248 1249 return (doc); 1250 } 1251 1252 /* 1253 * Creates a temporary XML file containing all of the localized 1254 * message strings for the generated command script. 1255 * 1256 * @param tmpfile 1257 * RETURN: the name of the temporary XML file 1258 * 1259 * @return 0 on success, non-zero otherwise. 1260 */ 1261 static int 1262 create_localized_message_file( 1263 char **tmpfile) 1264 { 1265 int error = 0; 1266 1267 /* 1268 * Create temporary file name -- "XXXXXX" is replaced with 1269 * unique char sequence by mkstemp() 1270 */ 1271 *tmpfile = stralloccat(3, "/tmp/", ELEMENT_L10N, "XXXXXX"); 1272 1273 if (*tmpfile == NULL) { 1274 volume_set_error(gettext("out of memory")); 1275 error = -1; 1276 } else { 1277 int fildes; 1278 FILE *msgfile = NULL; 1279 1280 /* Open temp file */ 1281 if ((fildes = mkstemp(*tmpfile)) != -1) { 1282 msgfile = fdopen(fildes, "w"); 1283 } 1284 1285 if (msgfile == NULL) { 1286 volume_set_error(gettext( 1287 "could not open file for writing: %s"), *tmpfile); 1288 error = -1; 1289 } else { 1290 1291 xmlChar *text; 1292 xmlDocPtr message_doc = create_localized_message_doc(); 1293 xmlDocDumpFormatMemory(message_doc, &text, NULL, 1); 1294 1295 if (fprintf(msgfile, "%s", text) < 0) { 1296 volume_set_error(gettext( 1297 "could not create localized message file: %s"), 1298 *tmpfile); 1299 error = -1; 1300 } 1301 1302 xmlFree(text); 1303 xmlFreeDoc(message_doc); 1304 } 1305 1306 fclose(msgfile); 1307 } 1308 1309 return (error); 1310 } 1311 1312 /* 1313 * Converts the given string into a boolean. The string must be 1314 * either VALID_ATTR_TRUE or VALID_ATTR_FALSE. 1315 * 1316 * @param str 1317 * the string to convert 1318 * 1319 * @param bool 1320 * the addr of the boolean_t 1321 * 1322 * @return 0 if the given string could be converted to a boolean 1323 * non-zero otherwise. 1324 */ 1325 static int 1326 strtobool( 1327 char *str, 1328 boolean_t *value) 1329 { 1330 int error = 0; 1331 1332 if (strcmp(str, VALID_ATTR_TRUE) == 0) { 1333 *value = B_TRUE; 1334 } else 1335 1336 if (strcmp(str, VALID_ATTR_FALSE) == 0) { 1337 *value = B_FALSE; 1338 } else 1339 1340 error = -1; 1341 1342 return (error); 1343 } 1344 1345 /* 1346 * Wrapper for oprintf with a OUTPUT_TERSE level of verbosity. 1347 * Provides an fprintf-like syntax to enable use as substitute output 1348 * handler for man of the XML commands. 1349 * 1350 * @param unused 1351 * unused, in favor of the FILE* passed to 1352 * set_max_verbosity(). 1353 * 1354 * @param fmt 1355 * a printf-style format string 1356 * 1357 * @return the number of characters output 1358 */ 1359 static int 1360 ofprintf_terse( 1361 void *unused, 1362 char *fmt, 1363 ...) 1364 { 1365 int ret; 1366 va_list ap; 1367 1368 va_start(ap, fmt); 1369 ret = oprintf_va(OUTPUT_TERSE, fmt, ap); 1370 va_end(ap); 1371 1372 return (ret); 1373 } 1374 1375 /* 1376 * Wrapper for oprintf with a OUTPUT_VERBOSE level of verbosity. 1377 * Provides an fprintf-like syntax to enable use as substitute output 1378 * handler for man of the XML commands. 1379 * 1380 * @param unused 1381 * unused, in favor of the FILE* passed to 1382 * set_max_verbosity(). 1383 * 1384 * @param fmt 1385 * a printf-style format string 1386 * 1387 * @return the number of characters output 1388 */ 1389 static int 1390 ofprintf_verbose( 1391 void *unused, 1392 char *fmt, 1393 ...) 1394 { 1395 int ret; 1396 va_list ap; 1397 1398 va_start(ap, fmt); 1399 ret = oprintf_va(OUTPUT_VERBOSE, fmt, ap); 1400 va_end(ap); 1401 1402 return (ret); 1403 } 1404 1405 /* 1406 * ****************************************************************** 1407 * 1408 * XML attribute validators/mutators 1409 * 1410 * These functions convert the given XML attribute string to the 1411 * appropriate data type, and then pass it on to the appropriate 1412 * devconfig_t mutator. A non-zero status is returned if the given 1413 * string could not be converted or was invalid. 1414 * 1415 * ****************************************************************** 1416 */ 1417 1418 /* 1419 * Validate and set the size attribute in the given volume 1420 * devconfig_t. 1421 * 1422 * @param volume 1423 * the devconfig_t in which to set the size 1424 * 1425 * @param attr 1426 * the name of the XML attribute 1427 * 1428 * @param value 1429 * the value of the XML attribute 1430 * 1431 * @return 0 on success, non-zero otherwise. 1432 */ 1433 static int 1434 validate_set_size( 1435 devconfig_t *volume, 1436 char *attr, 1437 char *value) 1438 { 1439 int error; 1440 uint64_t size = 0; 1441 1442 /* Convert size string to bytes */ 1443 if ((error = sizestr_to_bytes(value, &size, size_units)) != 0) { 1444 return (error); 1445 } 1446 1447 /* Set size in volume */ 1448 return (devconfig_set_size(volume, size)); 1449 } 1450 1451 /* 1452 * Validate and set the size_in_blocks attribute in the given slice 1453 * devconfig_t. 1454 * 1455 * @param volume 1456 * the devconfig_t in which to set the size_in_blocks 1457 * 1458 * @param attr 1459 * the name of the XML attribute 1460 * 1461 * @param value 1462 * the value of the XML attribute 1463 * 1464 * @return 0 on success, non-zero otherwise. 1465 */ 1466 static int 1467 validate_set_size_in_blocks( 1468 devconfig_t *slice, 1469 char *attr, 1470 char *value) 1471 { 1472 long long size; 1473 1474 /* Convert string to long long */ 1475 if (sscanf(value, "%lld", &size) != 1) { 1476 volume_set_error(gettext("%s: invalid size in blocks"), value); 1477 return (-1); 1478 } 1479 1480 /* Set the number of submirrors in the slice */ 1481 return (devconfig_set_size_in_blocks(slice, (uint64_t)size)); 1482 } 1483 1484 /* 1485 * Validate and set the name attribute in the given diskset 1486 * devconfig_t. 1487 * 1488 * @param volume 1489 * the devconfig_t in which to set the name 1490 * 1491 * @param attr 1492 * the name of the XML attribute 1493 * 1494 * @param name 1495 * the value of the XML attribute 1496 * 1497 * @return 0 on success, non-zero otherwise. 1498 */ 1499 static int 1500 validate_set_diskset_name( 1501 devconfig_t *diskset, 1502 char *attr, 1503 char *name) 1504 { 1505 return (devconfig_set_diskset_name(diskset, name)); 1506 } 1507 1508 /* 1509 * Validate and add the given name to the list of available devices in 1510 * the given volume devconfig_t. 1511 * 1512 * @param device 1513 * the devconfig_t whose available device list to modify 1514 * 1515 * @param attr 1516 * the name of the XML attribute 1517 * 1518 * @param name 1519 * the value of the XML attribute 1520 * 1521 * @return 0 on success, non-zero otherwise. 1522 */ 1523 static int 1524 validate_add_available_name( 1525 devconfig_t *device, 1526 char *attr, 1527 char *name) 1528 { 1529 char **available; 1530 1531 /* Get available devices for this device */ 1532 available = devconfig_get_available(device); 1533 1534 /* Try to add name to array via realloc */ 1535 if ((available = append_to_string_array(available, name)) == NULL) { 1536 return (ENOMEM); 1537 } 1538 1539 /* Set available devices in the device */ 1540 devconfig_set_available(device, available); 1541 1542 return (0); 1543 } 1544 1545 /* 1546 * Validate and add the given name to the list of unavailable devices 1547 * in the given volume devconfig_t. 1548 * 1549 * @param device 1550 * the devconfig_t whose unavailable device list to modify 1551 * 1552 * @param attr 1553 * the name of the XML attribute 1554 * 1555 * @param name 1556 * the value of the XML attribute 1557 * 1558 * @return 0 on success, non-zero otherwise. 1559 */ 1560 static int 1561 validate_add_unavailable_name( 1562 devconfig_t *device, 1563 char *attr, 1564 char *name) 1565 { 1566 char **unavailable; 1567 1568 /* Get unavailable devices for this device */ 1569 unavailable = devconfig_get_unavailable(device); 1570 1571 /* Try to add name to array via realloc */ 1572 if ((unavailable = append_to_string_array(unavailable, name)) == NULL) { 1573 return (ENOMEM); 1574 } 1575 1576 /* Set unavailable devices in the device */ 1577 devconfig_set_unavailable(device, unavailable); 1578 1579 return (0); 1580 } 1581 1582 /* 1583 * Validate and set the name attribute in the given hsp devconfig_t. 1584 * 1585 * @param volume 1586 * the devconfig_t in which to set the name 1587 * 1588 * @param attr 1589 * the name of the XML attribute 1590 * 1591 * @param name 1592 * the value of the XML attribute 1593 * 1594 * @return 0 on success, non-zero otherwise. 1595 */ 1596 static int 1597 validate_set_hsp_name( 1598 devconfig_t *hsp, 1599 char *attr, 1600 char *name) 1601 { 1602 return (devconfig_set_hsp_name(hsp, name)); 1603 } 1604 1605 /* 1606 * Validate and set the name attribute in the given disk devconfig_t. 1607 * 1608 * @param volume 1609 * the devconfig_t in which to set the name 1610 * 1611 * @param attr 1612 * the name of the XML attribute 1613 * 1614 * @param name 1615 * the value of the XML attribute 1616 * 1617 * @return 0 on success, non-zero otherwise. 1618 */ 1619 static int 1620 validate_set_disk_name( 1621 devconfig_t *disk, 1622 char *attr, 1623 char *name) 1624 { 1625 return (devconfig_set_name(disk, name)); 1626 } 1627 1628 /* 1629 * Validate and set the name attribute in the given slice devconfig_t. 1630 * 1631 * @param volume 1632 * the devconfig_t in which to set the name 1633 * 1634 * @param attr 1635 * the name of the XML attribute 1636 * 1637 * @param name 1638 * the value of the XML attribute 1639 * 1640 * @return 0 on success, non-zero otherwise. 1641 */ 1642 static int 1643 validate_set_slice_name( 1644 devconfig_t *slice, 1645 char *attr, 1646 char *name) 1647 { 1648 return (devconfig_set_name(slice, name)); 1649 } 1650 1651 /* 1652 * Validate and set the start_block attribute in the given slice 1653 * devconfig_t. 1654 * 1655 * @param volume 1656 * the devconfig_t in which to set the start_block 1657 * 1658 * @param attr 1659 * the name of the XML attribute 1660 * 1661 * @param value 1662 * the value of the XML attribute 1663 * 1664 * @return 0 on success, non-zero otherwise. 1665 */ 1666 static int 1667 validate_set_slice_start_block( 1668 devconfig_t *slice, 1669 char *attr, 1670 char *value) 1671 { 1672 long long startsector; 1673 1674 /* Convert string to long long */ 1675 if (sscanf(value, "%lld", &startsector) != 1) { 1676 volume_set_error(gettext("%s: invalid start sector"), value); 1677 return (-1); 1678 } 1679 1680 /* Set the number of submirrors in the slice */ 1681 return (devconfig_set_slice_start_block(slice, (uint64_t)startsector)); 1682 } 1683 1684 /* 1685 * Validate and set the name attribute in the given volume 1686 * devconfig_t. 1687 * 1688 * @param volume 1689 * the devconfig_t in which to set the name 1690 * 1691 * @param attr 1692 * the name of the XML attribute 1693 * 1694 * @param name 1695 * the value of the XML attribute 1696 * 1697 * @return 0 on success, non-zero otherwise. 1698 */ 1699 static int 1700 validate_set_volume_name( 1701 devconfig_t *volume, 1702 char *attr, 1703 char *name) 1704 { 1705 return (devconfig_set_volume_name(volume, name)); 1706 } 1707 1708 /* 1709 * Validate and set the interlace attribute in the given stripe 1710 * devconfig_t. 1711 * 1712 * @param volume 1713 * the devconfig_t in which to set the interlace 1714 * 1715 * @param attr 1716 * the name of the XML attribute 1717 * 1718 * @param value 1719 * the value of the XML attribute 1720 * 1721 * @return 0 on success, non-zero otherwise. 1722 */ 1723 static int 1724 validate_set_stripe_interlace( 1725 devconfig_t *stripe, 1726 char *attr, 1727 char *value) 1728 { 1729 int error; 1730 uint64_t interlace = 0; 1731 1732 /* Convert interlace string to bytes */ 1733 if ((error = sizestr_to_bytes( 1734 value, &interlace, interlace_units)) != 0) { 1735 return (error); 1736 } 1737 1738 /* Set interlace in stripe */ 1739 return (devconfig_set_stripe_interlace(stripe, interlace)); 1740 } 1741 1742 /* 1743 * Validate and set the mincomp attribute in the given stripe 1744 * devconfig_t. 1745 * 1746 * @param volume 1747 * the devconfig_t in which to set the mincomp 1748 * 1749 * @param attr 1750 * the name of the XML attribute 1751 * 1752 * @param value 1753 * the value of the XML attribute 1754 * 1755 * @return 0 on success, non-zero otherwise. 1756 */ 1757 static int 1758 validate_set_stripe_mincomp( 1759 devconfig_t *stripe, 1760 char *attr, 1761 char *value) 1762 { 1763 uint16_t mincomp; 1764 1765 /* Convert string to a uint16_t */ 1766 if (str_to_uint16(value, &mincomp) != 0) { 1767 volume_set_error( 1768 gettext("invalid minimum stripe components (%s): %s"), 1769 attr, value); 1770 return (-1); 1771 } 1772 1773 /* Set in stripe */ 1774 return (devconfig_set_stripe_mincomp(stripe, mincomp)); 1775 } 1776 1777 /* 1778 * Validate and set the maxcomp attribute in the given stripe 1779 * devconfig_t. 1780 * 1781 * @param volume 1782 * the devconfig_t in which to set the maxcomp 1783 * 1784 * @param attr 1785 * the name of the XML attribute 1786 * 1787 * @param value 1788 * the value of the XML attribute 1789 * 1790 * @return 0 on success, non-zero otherwise. 1791 */ 1792 static int 1793 validate_set_stripe_maxcomp( 1794 devconfig_t *stripe, 1795 char *attr, 1796 char *value) 1797 { 1798 uint16_t maxcomp; 1799 1800 /* Convert string to a uint16_t */ 1801 if (str_to_uint16(value, &maxcomp) != 0) { 1802 volume_set_error( 1803 gettext("invalid maximum stripe components (%s): %s"), 1804 attr, value); 1805 return (-1); 1806 } 1807 1808 /* Set in stripe */ 1809 return (devconfig_set_stripe_maxcomp(stripe, maxcomp)); 1810 } 1811 1812 /* 1813 * Validate and set the usehsp attribute in the given volume 1814 * devconfig_t. 1815 * 1816 * @param volume 1817 * the devconfig_t in which to set the usehsp 1818 * 1819 * @param attr 1820 * the name of the XML attribute 1821 * 1822 * @param value 1823 * the value of the XML attribute 1824 * 1825 * @return 0 on success, non-zero otherwise. 1826 */ 1827 static int 1828 validate_set_volume_usehsp( 1829 devconfig_t *volume, 1830 char *attr, 1831 char *value) 1832 { 1833 boolean_t usehsp; 1834 1835 /* Get boolean value */ 1836 if (strtobool(value, &usehsp) != 0) { 1837 volume_set_error( 1838 gettext("%s: invalid boolean value for \"%s\" attribute"), 1839 value, attr); 1840 return (-1); 1841 } 1842 1843 /* Set in volume */ 1844 return (devconfig_set_volume_usehsp(volume, usehsp)); 1845 } 1846 1847 /* 1848 * Validate and set the nsubmirrors attribute in the given mirror 1849 * devconfig_t. 1850 * 1851 * @param volume 1852 * the devconfig_t in which to set the nsubmirrors 1853 * 1854 * @param attr 1855 * the name of the XML attribute 1856 * 1857 * @param value 1858 * the value of the XML attribute 1859 * 1860 * @return 0 on success, non-zero otherwise. 1861 */ 1862 static int 1863 validate_set_mirror_nsubmirrors( 1864 devconfig_t *mirror, 1865 char *attr, 1866 char *value) 1867 { 1868 uint16_t nsubmirrors; 1869 1870 /* Convert string to a uint16_t */ 1871 if (str_to_uint16(value, &nsubmirrors) != 0) { 1872 volume_set_error( 1873 gettext("invalid number of submirrors (%s): %s"), 1874 attr, value); 1875 return (-1); 1876 } 1877 1878 /* Set in stripe */ 1879 return (devconfig_set_mirror_nsubs(mirror, nsubmirrors)); 1880 } 1881 1882 /* 1883 * Validate and set the read attribute in the given mirror 1884 * devconfig_t. 1885 * 1886 * @param volume 1887 * the devconfig_t in which to set the read 1888 * 1889 * @param attr 1890 * the name of the XML attribute 1891 * 1892 * @param value 1893 * the value of the XML attribute 1894 * 1895 * @return 0 on success, non-zero otherwise. 1896 */ 1897 static int 1898 validate_set_mirror_read( 1899 devconfig_t *mirror, 1900 char *attr, 1901 char *value) 1902 { 1903 mirror_read_strategy_t strategy; 1904 1905 if (strcmp(value, VALID_MIRROR_READ_ROUNDROBIN) == 0) { 1906 strategy = MIRROR_READ_ROUNDROBIN; 1907 } else 1908 1909 if (strcmp(value, VALID_MIRROR_READ_GEOMETRIC) == 0) { 1910 strategy = MIRROR_READ_GEOMETRIC; 1911 } else 1912 1913 if (strcmp(value, VALID_MIRROR_READ_FIRST) == 0) { 1914 strategy = MIRROR_READ_FIRST; 1915 } else 1916 1917 { 1918 volume_set_error(gettext("%s: invalid mirror read value"), value); 1919 return (-1); 1920 } 1921 1922 return (devconfig_set_mirror_read(mirror, strategy)); 1923 } 1924 1925 /* 1926 * Validate and set the write attribute in the given mirror 1927 * devconfig_t. 1928 * 1929 * @param volume 1930 * the devconfig_t in which to set the write 1931 * 1932 * @param attr 1933 * the name of the XML attribute 1934 * 1935 * @param value 1936 * the value of the XML attribute 1937 * 1938 * @return 0 on success, non-zero otherwise. 1939 */ 1940 static int 1941 validate_set_mirror_write( 1942 devconfig_t *mirror, 1943 char *attr, 1944 char *value) 1945 { 1946 mirror_write_strategy_t strategy; 1947 1948 if (strcmp(value, VALID_MIRROR_WRITE_PARALLEL) == 0) { 1949 strategy = MIRROR_WRITE_PARALLEL; 1950 } else 1951 1952 if (strcmp(value, VALID_MIRROR_WRITE_SERIAL) == 0) { 1953 strategy = MIRROR_WRITE_SERIAL; 1954 } else 1955 1956 { 1957 volume_set_error(gettext("%s: invalid mirror write value"), value); 1958 return (-1); 1959 } 1960 1961 return (devconfig_set_mirror_write(mirror, strategy)); 1962 } 1963 1964 /* 1965 * Validate and set the passnum attribute in the given mirror 1966 * devconfig_t. 1967 * 1968 * @param volume 1969 * the devconfig_t in which to set the passnum 1970 * 1971 * @param attr 1972 * the name of the XML attribute 1973 * 1974 * @param value 1975 * the value of the XML attribute 1976 * 1977 * @return 0 on success, non-zero otherwise. 1978 */ 1979 static int 1980 validate_set_mirror_passnum( 1981 devconfig_t *mirror, 1982 char *attr, 1983 char *value) 1984 { 1985 uint16_t passnum; 1986 1987 /* Convert string to a uint16_t */ 1988 if (str_to_uint16(value, &passnum) != 0) { 1989 volume_set_error( 1990 gettext("invalid mirror pass number (%s): %s"), 1991 attr, value); 1992 return (-1); 1993 } 1994 1995 /* Set in stripe */ 1996 return (devconfig_set_mirror_pass(mirror, passnum)); 1997 } 1998 1999 /* 2000 * Validate and set the redundancy attribute in the given volume 2001 * devconfig_t. 2002 * 2003 * @param volume 2004 * the devconfig_t in which to set the redundancy 2005 * 2006 * @param attr 2007 * the name of the XML attribute 2008 * 2009 * @param value 2010 * the value of the XML attribute 2011 * 2012 * @return 0 on success, non-zero otherwise. 2013 */ 2014 static int 2015 validate_set_volume_redundancy( 2016 devconfig_t *volume, 2017 char *attr, 2018 char *value) 2019 { 2020 uint16_t redundancy; 2021 2022 /* Convert string to a uint16_t */ 2023 if (str_to_uint16(value, &redundancy) != 0) { 2024 volume_set_error( 2025 gettext("invalid redundancy level (%s): %s"), 2026 attr, value); 2027 return (-1); 2028 } 2029 2030 /* Set in stripe */ 2031 return (devconfig_set_volume_redundancy_level(volume, redundancy)); 2032 } 2033 2034 /* 2035 * Validate and set the datapaths attribute in the given volume 2036 * devconfig_t. 2037 * 2038 * @param volume 2039 * the devconfig_t in which to set the datapaths 2040 * 2041 * @param attr 2042 * the name of the XML attribute 2043 * 2044 * @param value 2045 * the value of the XML attribute 2046 * 2047 * @return 0 on success, non-zero otherwise. 2048 */ 2049 static int 2050 validate_set_volume_datapaths( 2051 devconfig_t *volume, 2052 char *attr, 2053 char *value) 2054 { 2055 uint16_t redundancy; 2056 2057 /* Convert string to a uint16_t */ 2058 if (str_to_uint16(value, &redundancy) != 0) { 2059 volume_set_error( 2060 gettext("invalid number of data paths (%s): %s"), 2061 attr, value); 2062 return (-1); 2063 } 2064 2065 /* Set in stripe */ 2066 return (devconfig_set_volume_npaths(volume, redundancy)); 2067 } 2068 2069 /* 2070 * ****************************************************************** 2071 * 2072 * XML attribute accessors/converters 2073 * 2074 * These functions get a value from the appropriate devconfig_t 2075 * accessor, and then convert it to a string. 2076 * 2077 * ****************************************************************** 2078 */ 2079 2080 /* 2081 * Get, as a string, the value of the name attribute of the given 2082 * devconfig_t. This data must be freed. 2083 * 2084 * @param device 2085 * the devconfig_t from which to retrieve the name 2086 * 2087 * @param attr 2088 * the name of the XML attribute 2089 * 2090 * @param value 2091 * RETURN: the value of the XML attribute 2092 * 2093 * @return 0 on success, non-zero otherwise. 2094 */ 2095 static int 2096 get_as_string_name( 2097 devconfig_t *device, 2098 char *attr, 2099 char **value) 2100 { 2101 int error; 2102 char *name; 2103 2104 /* Get name */ 2105 if ((error = devconfig_get_name(device, &name)) == 0) { 2106 if ((*value = strdup(name)) == NULL) { 2107 error = ENOMEM; 2108 } 2109 } 2110 2111 return (error); 2112 } 2113 2114 /* 2115 * Get, as a string, the value of the passnum attribute of the given 2116 * mirror devconfig_t. This data must be freed. 2117 * 2118 * @param device 2119 * the devconfig_t from which to retrieve the passnum 2120 * 2121 * @param attr 2122 * the name of the XML attribute 2123 * 2124 * @param value 2125 * RETURN: the value of the XML attribute 2126 * 2127 * @return 0 on success, non-zero otherwise. 2128 */ 2129 static int 2130 get_as_string_mirror_passnum( 2131 devconfig_t *mirror, 2132 char *attr, 2133 char **value) 2134 { 2135 int error; 2136 uint16_t passnum; 2137 2138 /* Get mirror pass number */ 2139 if ((error = devconfig_get_mirror_pass(mirror, &passnum)) == 0) { 2140 error = ll_to_str(passnum, value); 2141 } 2142 2143 return (error); 2144 } 2145 2146 /* 2147 * Get, as a string, the value of the read attribute of the given 2148 * mirror devconfig_t. This data must be freed. 2149 * 2150 * @param device 2151 * the devconfig_t from which to retrieve the read 2152 * 2153 * @param attr 2154 * the name of the XML attribute 2155 * 2156 * @param value 2157 * RETURN: the value of the XML attribute 2158 * 2159 * @return 0 on success, non-zero otherwise. 2160 */ 2161 static int 2162 get_as_string_mirror_read( 2163 devconfig_t *mirror, 2164 char *attr, 2165 char **value) 2166 { 2167 int error; 2168 mirror_read_strategy_t read; 2169 2170 /* Get mirror read strategy */ 2171 if ((error = devconfig_get_mirror_read(mirror, &read)) == 0) { 2172 if ((*value = strdup( 2173 devconfig_read_strategy_to_str(read))) == NULL) { 2174 error = ENOMEM; 2175 } 2176 } 2177 2178 return (error); 2179 } 2180 2181 /* 2182 * Get, as a string, the value of the write attribute of the given 2183 * mirror devconfig_t. This data must be freed. 2184 * 2185 * @param device 2186 * the devconfig_t from which to retrieve the write 2187 * 2188 * @param attr 2189 * the name of the XML attribute 2190 * 2191 * @param value 2192 * RETURN: the value of the XML attribute 2193 * 2194 * @return 0 on success, non-zero otherwise. 2195 */ 2196 static int 2197 get_as_string_mirror_write( 2198 devconfig_t *mirror, 2199 char *attr, 2200 char **value) 2201 { 2202 int error; 2203 mirror_write_strategy_t write; 2204 2205 /* Get mirror write strategy */ 2206 if ((error = devconfig_get_mirror_write(mirror, &write)) == 0) { 2207 if ((*value = strdup( 2208 devconfig_write_strategy_to_str(write))) == NULL) { 2209 error = ENOMEM; 2210 } 2211 } 2212 2213 return (error); 2214 } 2215 2216 /* 2217 * Get, as a string, the value of the in_blocks attribute of the given 2218 * device devconfig_t. This data must be freed. 2219 * 2220 * @param device 2221 * the devconfig_t from which to retrieve the in_blocks 2222 * 2223 * @param attr 2224 * the name of the XML attribute 2225 * 2226 * @param value 2227 * RETURN: the value of the XML attribute 2228 * 2229 * @return 0 on success, non-zero otherwise. 2230 */ 2231 static int 2232 get_as_string_size_in_blocks( 2233 devconfig_t *device, 2234 char *attr, 2235 char **value) 2236 { 2237 int error; 2238 uint64_t size; 2239 2240 /* Get size in blocks */ 2241 if ((error = devconfig_get_size_in_blocks(device, &size)) == 0) { 2242 error = ll_to_str(size, value); 2243 } 2244 2245 return (error); 2246 } 2247 2248 /* 2249 * Get, as a string, the value of the start_block attribute of the 2250 * given slice devconfig_t. This data must be freed. 2251 * 2252 * @param device 2253 * the devconfig_t from which to retrieve the start_block 2254 * 2255 * @param attr 2256 * the name of the XML attribute 2257 * 2258 * @param value 2259 * RETURN: the value of the XML attribute 2260 * 2261 * @return 0 on success, non-zero otherwise. 2262 */ 2263 static int 2264 get_as_string_slice_start_block( 2265 devconfig_t *slice, 2266 char *attr, 2267 char **value) 2268 { 2269 int error; 2270 uint64_t start; 2271 2272 /* Get slice start block */ 2273 if ((error = devconfig_get_slice_start_block(slice, &start)) == 0) { 2274 error = ll_to_str(start, value); 2275 } 2276 2277 return (error); 2278 } 2279 2280 /* 2281 * Get, as a string, the value of the interlace attribute of the given 2282 * stripe devconfig_t. This data must be freed. 2283 * 2284 * @param device 2285 * the devconfig_t from which to retrieve the interlace 2286 * 2287 * @param attr 2288 * the name of the XML attribute 2289 * 2290 * @param value 2291 * RETURN: the value of the XML attribute 2292 * 2293 * @return 0 on success, non-zero otherwise. 2294 */ 2295 static int 2296 get_as_string_stripe_interlace( 2297 devconfig_t *stripe, 2298 char *attr, 2299 char **value) 2300 { 2301 int error; 2302 uint64_t interlace; 2303 2304 /* Get interlace */ 2305 if ((error = devconfig_get_stripe_interlace( 2306 stripe, &interlace)) == 0) { 2307 error = bytes_to_sizestr(interlace, value, interlace_units, B_TRUE); 2308 } 2309 2310 return (error); 2311 } 2312