xref: /titanic_41/usr/src/cmd/lvm/metassist/xml/xml_convert.c (revision 99ebb4ca412cb0a19d77a3899a87c055b9c30fa8)
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