xref: /titanic_41/usr/src/cmd/lvm/metassist/common/volume_devconfig.c (revision 6185db853e024a486ff8837e6784dd290d866112)
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 2003 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 "volume_devconfig.h"
30 
31 #include <string.h>
32 #include <ctype.h>
33 #include <meta.h>
34 #include "volume_nvpair.h"
35 #include "volume_error.h"
36 #include "volume_output.h"
37 #include "volume_string.h"
38 
39 /*
40  * Methods which manipulate a devconfig_t struct
41  */
42 
43 /*
44  * Constructor: Create a devconfig_t struct.  This devconfig_t must be
45  * freed with free_devconfig().
46  *
47  * @param       devconfig
48  *              RETURN: a new devconfig_t
49  *
50  * @param       type
51  *              the type of devconfig_t to create
52  *
53  * @return      0
54  *              if successful
55  *
56  * @return      non-zero
57  *              if an error occurred.  Use get_error_string() to
58  *              retrieve the associated error message.
59  */
60 int
61 new_devconfig(
62 	devconfig_t **devconfig,
63 	component_type_t type)
64 {
65 	int error;
66 
67 	*devconfig = (devconfig_t *)calloc(1, sizeof (devconfig_t));
68 	if (*devconfig == NULL) {
69 	    volume_set_error(gettext("new_devconfig() calloc() failed\n"));
70 	    return (-1);
71 	}
72 
73 	/* Create attribute list */
74 	if ((error = nvlist_alloc(&((*devconfig)->attributes),
75 	    NV_UNIQUE_NAME_TYPE, 0)) != 0) {
76 	    volume_set_error(gettext("devconfig_t nvlist_alloc() failed\n"));
77 	    free_devconfig(*devconfig);
78 	    return (error);
79 	}
80 
81 	if ((error = devconfig_set_type(*devconfig, type)) != 0) {
82 	    free_devconfig(*devconfig);
83 	    return (error);
84 	}
85 
86 	return (0);
87 }
88 
89 /*
90  * Free memory (recursively) allocated to a devconfig_t struct
91  *
92  * @param       arg
93  *              pointer to the devconfig_t to be freed
94  */
95 void
96 free_devconfig(
97 	void *arg)
98 {
99 	devconfig_t *devconfig = (devconfig_t *)arg;
100 
101 	if (devconfig == NULL) {
102 	    return;
103 	}
104 
105 	/* Free the attributes nvlist */
106 	if (devconfig->attributes != NULL) {
107 	    nvlist_free(devconfig->attributes);
108 	}
109 
110 	/* Free available devices */
111 	if (devconfig->available != NULL) {
112 	    free_string_array(devconfig->available);
113 	}
114 
115 	/* Free unavailable devices */
116 	if (devconfig->unavailable != NULL) {
117 	    free_string_array(devconfig->unavailable);
118 	}
119 
120 	/* Free the components */
121 	if (devconfig->components != NULL) {
122 	    dlist_free_items(devconfig->components, free_devconfig);
123 	}
124 
125 	/* Free the devconfig itself */
126 	free(devconfig);
127 }
128 
129 /*
130  * Check the type of the given device.
131  *
132  * @param       device
133  *              the device whose type to check
134  *
135  * @param       type
136  *              the type of the device against which to compare
137  *
138  * @return      B_TRUE if the device is of the given type, B_FALSE
139  *              otherwise
140  */
141 boolean_t
142 devconfig_isA(
143 	devconfig_t *device,
144 	component_type_t type)
145 {
146 	component_type_t curtype;
147 
148 	if (device == NULL) {
149 	    return (B_FALSE);
150 	}
151 
152 	if (devconfig_get_type(device, &curtype) != 0) {
153 	    return (B_FALSE);
154 	}
155 
156 	if (curtype != type) {
157 	    return (B_FALSE);
158 	}
159 
160 	return (B_TRUE);
161 }
162 
163 /*
164  * Get the first component of the given type from the given
165  * devconfig_t.  Create the component if create is B_TRUE.
166  *
167  * @return      ENOENT
168  *              if the requested component does not exist and its
169  *              creation was not requested
170  *
171  * @return      0
172  *              if the requested component exists or was created
173  *
174  * @return      non-zero
175  *              if the requested component did not exist and could not
176  *              be created
177  */
178 int
179 devconfig_get_component(
180 	devconfig_t *device,
181 	component_type_t type,
182 	devconfig_t **component,
183 	boolean_t create)
184 {
185 	dlist_t *list;
186 	int error = 0;
187 	char *typestr = devconfig_type_to_str(type);
188 
189 	oprintf(OUTPUT_DEBUG, gettext("Searching for singleton %s\n"), typestr);
190 
191 	/* For each component of this device... */
192 	for (list = devconfig_get_components(device);
193 	    list != NULL; list = list->next) {
194 
195 	    *component = (devconfig_t *)list->obj;
196 
197 	    /* Is this subcomponent an instance of the given type? */
198 	    if (*component != NULL && devconfig_isA(*component, type)) {
199 		oprintf(OUTPUT_DEBUG, gettext("Found %s\n"), typestr);
200 		return (0);
201 	    }
202 	}
203 
204 	/* No component found */
205 	error = ENOENT;
206 	*component = NULL;
207 
208 	oprintf(OUTPUT_DEBUG, gettext("%s not found\n"), typestr);
209 
210 	if (create == B_TRUE) {
211 	    oprintf(OUTPUT_DEBUG, gettext("Creating %s\n"), typestr);
212 
213 		/*
214 		 * An existing singleton component of the given type was
215 		 * not found under the given disk set.  So, create one.
216 		 */
217 	    if ((error = new_devconfig(component, type)) == 0) {
218 		/* Attach new component to given device */
219 		devconfig_set_components(
220 		    device, dlist_append(dlist_new_item(*component),
221 		    devconfig_get_components(device), AT_TAIL));
222 	    }
223 	}
224 
225 	return (error);
226 }
227 
228 /*
229  * Set the available devices for use in creating this device
230  *
231  * @param       device
232  *              a devconfig_t representing the device to modify
233  *
234  * @param       available
235  *              A NULL-terminated array of device names
236  */
237 void
238 devconfig_set_available(
239 	devconfig_t *device,
240 	char **available)
241 {
242 	device->available = available;
243 }
244 
245 /*
246  * Get the available devices for use in creating this device
247  *
248  * @param       device
249  *              a devconfig_t representing the device to examine
250  *
251  * @return      available
252  *              A NULL-terminated array of device names
253  */
254 char **
255 devconfig_get_available(
256 	devconfig_t *device)
257 {
258 	return (device->available);
259 }
260 
261 /*
262  * Set the unavailable devices which may not be used in creating this
263  * device
264  *
265  * @param       device
266  *              a devconfig_t representing the device to modify
267  *
268  * @param       available
269  *              A NULL-terminated array of device names
270  */
271 void
272 devconfig_set_unavailable(
273 	devconfig_t *device,
274 	char **unavailable)
275 {
276 	device->unavailable = unavailable;
277 }
278 
279 /*
280  * Get the unavailable devices for use in creating this device
281  *
282  * @param       device
283  *              a devconfig_t representing the device to examine
284  *
285  * @return      unavailable
286  *              A NULL-terminated array of device names
287  */
288 char **
289 devconfig_get_unavailable(
290 	devconfig_t *device)
291 {
292 	return (device->unavailable);
293 }
294 
295 /*
296  * Set the subcomponent devices of a given device
297  *
298  * @param       device
299  *              a devconfig_t representing the device to examine
300  *
301  * @param       components
302  *              A dlist_t containing devconfig_t devices
303  */
304 void
305 devconfig_set_components(
306 	devconfig_t *device,
307 	dlist_t *components)
308 {
309 	device->components = components;
310 }
311 
312 /*
313  * Get the subcomponent devices of a given device
314  *
315  * @param       device
316  *              a devconfig_t representing the device to examine
317  *
318  * @return      A dlist_t containing devconfig_t devices
319  */
320 dlist_t *
321 devconfig_get_components(
322 	devconfig_t *device)
323 {
324 	return (device->components);
325 }
326 
327 /*
328  * Set the device name
329  *
330  * @param       device
331  *              a devconfig_t representing the device to modify
332  *
333  * @param       name
334  *              the value to set as the device name
335  *
336  * @return      0
337  *              if successful
338  *
339  * @return      non-zero
340  *              if an error occurred.  Use get_error_string() to
341  *              retrieve the associated error message.
342  */
343 int
344 devconfig_set_name(
345 	devconfig_t *device,
346 	char *name)
347 {
348 	return (set_string(device->attributes, ATTR_NAME, name));
349 }
350 
351 /*
352  * Set the disk set name
353  *
354  * @param       diskset
355  *              a devconfig_t representing the diskset to modify
356  *
357  * @param       name
358  *              the value to set as the device name
359  *
360  * @return      0
361  *              if successful
362  *
363  * @return      non-zero
364  *              if an error occurred.  Use get_error_string() to
365  *              retrieve the associated error message.
366  */
367 int
368 devconfig_set_diskset_name(
369 	devconfig_t *diskset,
370 	char *name)
371 {
372 	md_error_t error = mdnullerror;
373 
374 	/* Verify syntax of disk set name */
375 	if (meta_set_checkname(name, &error)) {
376 	    volume_set_error(gettext("invalid disk set name: %s"), name);
377 	    return (-1);
378 	}
379 
380 	return (devconfig_set_name(diskset, name));
381 }
382 
383 /*
384  * Set the device name
385  *
386  * @param       hsp
387  *              a devconfig_t representing the hsp to modify
388  *
389  * @param       name
390  *              the value to set as the device name
391  *
392  * @return      0
393  *              if successful
394  *
395  * @return      non-zero
396  *              if an error occurred.  Use get_error_string() to
397  *              retrieve the associated error message.
398  */
399 int
400 devconfig_set_hsp_name(
401 	devconfig_t *hsp,
402 	char *name)
403 {
404 	/* Validate name */
405 	if (!is_hspname(name)) {
406 	    volume_set_error(gettext("invalid hot spare pool name: %s"), name);
407 	    return (-1);
408 	}
409 
410 	return (devconfig_set_name(hsp, name));
411 }
412 
413 /*
414  * Set the device name
415  *
416  * @param       volume
417  *              a devconfig_t representing the volume to modify
418  *
419  * @param       name
420  *              the value to set as the device name
421  *
422  * @return      0
423  *              if successful
424  *
425  * @return      non-zero
426  *              if an error occurred.  Use get_error_string() to
427  *              retrieve the associated error message.
428  */
429 int
430 devconfig_set_volume_name(
431 	devconfig_t *volume,
432 	char *name)
433 {
434 	/* Validate name */
435 	if (!is_metaname(name)) {
436 	    volume_set_error(gettext("invalid volume name: %s"), name);
437 	    return (-1);
438 	}
439 
440 	return (devconfig_set_name(volume, name));
441 }
442 
443 /*
444  * Get the device name
445  *
446  * @param       volume
447  *              a devconfig_t representing the volume to examine
448  *
449  * @param       name
450  *              RETURN: the device name
451  *
452  * @return      0
453  *              if successful
454  *
455  * @return      non-zero
456  *              if an error occurred.  Use get_error_string() to
457  *              retrieve the associated error message.
458  */
459 int
460 devconfig_get_name(
461 	devconfig_t *device,
462 	char **name)
463 {
464 	int error = get_string(device->attributes, ATTR_NAME, name);
465 
466 	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
467 	if (error == ENOENT) {
468 	    volume_set_error(gettext("device name not set"));
469 	    error = ERR_ATTR_UNSET;
470 	}
471 
472 	return (error);
473 }
474 
475 /*
476  * Set the device type
477  *
478  * @param       device
479  *              a devconfig_t representing the device to modify
480  *
481  * @param       type
482  *              the value to set as the device type
483  *
484  * @return      0
485  *              if successful
486  *
487  * @return      non-zero
488  *              if an error occurred.  Use get_error_string() to
489  *              retrieve the associated error message.
490  */
491 int
492 devconfig_set_type(
493 	devconfig_t *device,
494 	component_type_t type)
495 {
496 	return (set_uint16(device->attributes, ATTR_TYPE, (uint16_t)type));
497 }
498 
499 /*
500  * Get the device type
501  *
502  * @param       device
503  *              a devconfig_t representing the device to examine
504  *
505  * @param       type
506  *              RETURN: the device type
507  *
508  * @return      0
509  *              if successful
510  *
511  * @return      non-zero
512  *              if an error occurred.  Use get_error_string() to
513  *              retrieve the associated error message.
514  */
515 int
516 devconfig_get_type(
517 	devconfig_t *device,
518 	component_type_t *type)
519 {
520 	uint16_t val;
521 	int error = get_uint16(device->attributes, ATTR_TYPE, &val);
522 
523 	switch (error) {
524 	    /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
525 	    case ENOENT:
526 		volume_set_error(gettext("device type not set"));
527 		error = ERR_ATTR_UNSET;
528 	    break;
529 
530 	    /* Success */
531 	    case 0:
532 		*type = (component_type_t)val;
533 	}
534 
535 	return (error);
536 }
537 
538 /*
539  * Set the device size (for volume, mirror, stripe, concat) in bytes
540  *
541  * Note that size in bytes in a 64-bit field cannot hold the size that
542  * can be accessed in a 16 byte CDB.  Since CDBs operate on blocks,
543  * the max capacity is 2^73 bytes with 512 byte blocks.
544  *
545  * @param       device
546  *              a devconfig_t representing the device to modify
547  *
548  * @param       size_in_bytes
549  *              the value to set as the device size in bytes
550  *
551  * @return      0
552  *              if successful
553  *
554  * @return      non-zero
555  *              if an error occurred.  Use get_error_string() to
556  *              retrieve the associated error message.
557  */
558 int
559 devconfig_set_size(
560 	devconfig_t *device,
561 	uint64_t size_in_bytes)
562 {
563 
564 	/* Validate against limits */
565 	/* LINTED -- MIN_SIZE may be 0 */
566 	if (size_in_bytes < MIN_SIZE) {
567 	    volume_set_error(gettext("size (in bytes) too small: %llu"),
568 		(unsigned long long)size_in_bytes);
569 	    return (-1);
570 	}
571 
572 	return (set_uint64(device->attributes,
573 	    ATTR_SIZEINBYTES, size_in_bytes));
574 }
575 
576 /*
577  * Get the device size (for volume, mirror, stripe, concat) in bytes
578  *
579  * Note that size in bytes in a 64-bit field cannot hold the size that
580  * can be accessed in a 16 byte CDB.  Since CDBs operate on blocks,
581  * the max capacity is 2^73 bytes with 512 byte blocks.
582  *
583  * @param       device
584  *              a devconfig_t representing the device to examine
585  *
586  * @param       size_in_bytes
587  *              RETURN: the device size in bytes
588  *
589  * @return      0
590  *              if successful
591  *
592  * @return      non-zero
593  *              if an error occurred.  Use get_error_string() to
594  *              retrieve the associated error message.
595  */
596 int
597 devconfig_get_size(
598 	devconfig_t *device,
599 	uint64_t *size_in_bytes)
600 {
601 	int error = get_uint64(
602 	    device->attributes, ATTR_SIZEINBYTES, size_in_bytes);
603 
604 	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
605 	if (error == ENOENT) {
606 	    volume_set_error(gettext("size (in bytes) not set"));
607 	    error = ERR_ATTR_UNSET;
608 	}
609 
610 	return (error);
611 }
612 
613 /*
614  * Set the device size in blocks
615  *
616  * @param       device
617  *              a devconfig_t representing the device to modify
618  *
619  * @param       type
620  *              the value to set as the device size in blocks
621  *
622  * @return      0
623  *              if successful
624  *
625  * @return      non-zero
626  *              if an error occurred.  Use get_error_string() to
627  *              retrieve the associated error message.
628  */
629 int
630 devconfig_set_size_in_blocks(
631 	devconfig_t *device,
632 	uint64_t size_in_blocks)
633 {
634 	/* Validate against limits */
635 	/* LINTED -- MIN_SIZE_IN_BLOCKS may be 0 */
636 	if (size_in_blocks < MIN_SIZE_IN_BLOCKS) {
637 	    volume_set_error(gettext("size (in blocks) too small: %llu"),
638 		(unsigned long long)size_in_blocks);
639 	    return (-1);
640 	}
641 
642 	return (set_uint64(device->attributes,
643 	    ATTR_SIZEINBLOCKS, size_in_blocks));
644 }
645 
646 /*
647  * Get the device size in blocks
648  *
649  * @param       device
650  *              a devconfig_t representing the device to examine
651  *
652  * @param       size_in_blocks
653  *              RETURN: the device size in blocks
654  *
655  * @return      0
656  *              if successful
657  *
658  * @return      non-zero
659  *              if an error occurred.  Use get_error_string() to
660  *              retrieve the associated error message.
661  */
662 int
663 devconfig_get_size_in_blocks(
664 	devconfig_t *device,
665 	uint64_t *size_in_blocks)
666 {
667 	int error = get_uint64(
668 	    device->attributes, ATTR_SIZEINBLOCKS, size_in_blocks);
669 
670 	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
671 	if (error == ENOENT) {
672 	    volume_set_error(gettext("size (in blocks) not set"));
673 	    error = ERR_ATTR_UNSET;
674 	}
675 
676 	return (error);
677 }
678 
679 /*
680  * Set the the slice index
681  *
682  * @param       slice
683  *              a devconfig_t representing the slice to modify
684  *
685  * @param       index
686  *              the value to set as the the slice index
687  *
688  * @return      0
689  *              if successful
690  *
691  * @return      non-zero
692  *              if an error occurred.  Use get_error_string() to
693  *              retrieve the associated error message.
694  */
695 int
696 devconfig_set_slice_index(
697 	devconfig_t *slice,
698 	uint16_t index)
699 {
700 	return (set_uint16(slice->attributes, ATTR_SLICE_INDEX, index));
701 }
702 
703 /*
704  * Get the slice index
705  *
706  * @param       device
707  *              a devconfig_t representing the device to examine
708  *
709  * @param       index
710  *              RETURN: the slice index
711  *
712  * @return      0
713  *              if successful
714  *
715  * @return      non-zero
716  *              if an error occurred.  Use get_error_string() to
717  *              retrieve the associated error message.
718  */
719 int
720 devconfig_get_slice_index(
721 	devconfig_t *slice,
722 	uint16_t *index)
723 {
724 	int error = get_uint16(slice->attributes, ATTR_SLICE_INDEX, index);
725 
726 	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
727 	if (error == ENOENT) {
728 	    volume_set_error(gettext("slice index not set"));
729 	    error = ERR_ATTR_UNSET;
730 	}
731 
732 	return (error);
733 }
734 
735 /*
736  * Set the the slice start block
737  *
738  * @param       slice
739  *              a devconfig_t representing the slice to modify
740  *
741  * @param       start_block
742  *              the value to set as the the slice start block
743  *
744  * @return      0
745  *              if successful
746  *
747  * @return      non-zero
748  *              if an error occurred.  Use get_error_string() to
749  *              retrieve the associated error message.
750  */
751 int
752 devconfig_set_slice_start_block(
753 	devconfig_t *slice,
754 	uint64_t start_block)
755 {
756 	return (set_uint64(slice->attributes,
757 	    ATTR_SLICE_STARTSECTOR, start_block));
758 }
759 
760 /*
761  * Get the slice start block
762  *
763  * @param       device
764  *              a devconfig_t representing the device to examine
765  *
766  * @param       start_block
767  *              RETURN: the slice start block
768  *
769  * @return      0
770  *              if successful
771  *
772  * @return      non-zero
773  *              if an error occurred.  Use get_error_string() to
774  *              retrieve the associated error message.
775  */
776 int
777 devconfig_get_slice_start_block(
778 	devconfig_t *slice,
779 	uint64_t *start_block)
780 {
781 	int error = get_uint64(
782 	    slice->attributes, ATTR_SLICE_STARTSECTOR, start_block);
783 
784 	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
785 	if (error == ENOENT) {
786 	    volume_set_error(gettext("slice start block not set"));
787 	    error = ERR_ATTR_UNSET;
788 	}
789 
790 	return (error);
791 }
792 
793 /*
794  * Set the number of subcomponents in mirror
795  *
796  * @param       mirror
797  *              a devconfig_t representing the mirror to modify
798  *
799  * @param       nsubs
800  *              the value to set as the number of subcomponents in
801  *              mirror
802  *
803  * @return      0
804  *              if successful
805  *
806  * @return      non-zero
807  *              if an error occurred.  Use get_error_string() to
808  *              retrieve the associated error message.
809  */
810 int
811 devconfig_set_mirror_nsubs(
812 	devconfig_t *mirror,
813 	uint16_t nsubs)
814 {
815 	/* Validate against limits */
816 	if (nsubs < 1 || nsubs > NMIRROR) {
817 	    volume_set_error(
818 		gettext("number of submirrors (%d) out of valid range (%d-%d)"),
819 		nsubs, 1, NMIRROR);
820 	    return (-1);
821 	}
822 
823 	return (set_uint16(mirror->attributes, ATTR_MIRROR_NSUBMIRRORS, nsubs));
824 }
825 
826 /*
827  * Get number of subcomponents in mirror
828  *
829  * @param       device
830  *              a devconfig_t representing the device to examine
831  *
832  * @param       nsubs
833  *              RETURN: number of subcomponents in mirror
834  *
835  * @return      0
836  *              if successful
837  *
838  * @return      non-zero
839  *              if an error occurred.  Use get_error_string() to
840  *              retrieve the associated error message.
841  */
842 int
843 devconfig_get_mirror_nsubs(
844 	devconfig_t *mirror,
845 	uint16_t *nsubs)
846 {
847 	int error = get_uint16(
848 	    mirror->attributes, ATTR_MIRROR_NSUBMIRRORS, nsubs);
849 
850 	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
851 	if (error == ENOENT) {
852 	    volume_set_error(gettext("number or submirrors not set"));
853 	    error = ERR_ATTR_UNSET;
854 	}
855 
856 	return (error);
857 }
858 
859 /*
860  * Set the read strategy for mirror
861  *
862  * @param       mirror
863  *              a devconfig_t representing the mirror to modify
864  *
865  * @param       read
866  *              the value to set as the read strategy for mirror
867  *
868  * @return      0
869  *              if successful
870  *
871  * @return      non-zero
872  *              if an error occurred.  Use get_error_string() to
873  *              retrieve the associated error message.
874  */
875 int
876 devconfig_set_mirror_read(
877 	devconfig_t *mirror,
878 	mirror_read_strategy_t read)
879 {
880 	return (set_uint16(mirror->attributes,
881 	    ATTR_MIRROR_READ, (uint16_t)read));
882 }
883 
884 /*
885  * Get read strategy for mirror
886  *
887  * @param       device
888  *              a devconfig_t representing the device to examine
889  *
890  * @param       read
891  *              RETURN: read strategy for mirror
892  *
893  * @return      0
894  *              if successful
895  *
896  * @return      non-zero
897  *              if an error occurred.  Use get_error_string() to
898  *              retrieve the associated error message.
899  */
900 int
901 devconfig_get_mirror_read(
902 	devconfig_t *mirror,
903 	mirror_read_strategy_t *read)
904 {
905 	uint16_t val;
906 	int error = get_uint16(mirror->attributes, ATTR_MIRROR_READ, &val);
907 
908 	switch (error) {
909 	    /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
910 	    case ENOENT:
911 		volume_set_error(gettext("mirror read strategy not set"));
912 		error = ERR_ATTR_UNSET;
913 	    break;
914 
915 	    /* Success */
916 	    case 0:
917 		*read = (mirror_read_strategy_t)val;
918 	}
919 
920 	return (error);
921 }
922 
923 /*
924  * Set the write strategy for mirror
925  *
926  * @param       mirror
927  *              a devconfig_t representing the mirror to modify
928  *
929  * @param       write
930  *              the value to set as the write strategy for mirror
931  *
932  * @return      0
933  *              if successful
934  *
935  * @return      non-zero
936  *              if an error occurred.  Use get_error_string() to
937  *              retrieve the associated error message.
938  */
939 int
940 devconfig_set_mirror_write(
941 	devconfig_t *mirror,
942 	mirror_write_strategy_t write)
943 {
944 	return (set_uint16(mirror->attributes,
945 	    ATTR_MIRROR_WRITE, (uint16_t)write));
946 }
947 
948 /*
949  * Get write strategy for mirror
950  *
951  * @param       device
952  *              a devconfig_t representing the device to examine
953  *
954  * @param       write
955  *              RETURN: write strategy for mirror
956  *
957  * @return      0
958  *              if successful
959  *
960  * @return      non-zero
961  *              if an error occurred.  Use get_error_string() to
962  *              retrieve the associated error message.
963  */
964 int
965 devconfig_get_mirror_write(
966 	devconfig_t *mirror,
967 	mirror_write_strategy_t *write)
968 {
969 	uint16_t val;
970 	int error = get_uint16(mirror->attributes, ATTR_MIRROR_WRITE, &val);
971 
972 	switch (error) {
973 	    /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
974 	    case ENOENT:
975 		volume_set_error(gettext("mirror write strategy not set"));
976 		error = ERR_ATTR_UNSET;
977 	    break;
978 
979 	    /* Success */
980 	    case 0:
981 		*write = (mirror_write_strategy_t)val;
982 	}
983 
984 	return (error);
985 }
986 
987 /*
988  * Set the resync pass for mirror
989  *
990  * @param       mirror
991  *              a devconfig_t representing the mirror to modify
992  *
993  * @param       pass
994  *              the value to set as the resync pass for mirror
995  *
996  * @return      0
997  *              if successful
998  *
999  * @return      non-zero
1000  *              if an error occurred.  Use get_error_string() to
1001  *              retrieve the associated error message.
1002  */
1003 int
1004 devconfig_set_mirror_pass(
1005 	devconfig_t *mirror,
1006 	uint16_t pass)
1007 {
1008 	/* Validate against max value */
1009 	if (pass > MD_PASS_MAX) {
1010 	    volume_set_error(
1011 		gettext("mirror pass number (%d) out of valid range (0-%d)"),
1012 		pass, MD_PASS_MAX);
1013 	    return (-1);
1014 	}
1015 
1016 	return (set_uint16(mirror->attributes, ATTR_MIRROR_PASSNUM, pass));
1017 }
1018 
1019 /*
1020  * Get resync pass for mirror
1021  *
1022  * @param       device
1023  *              a devconfig_t representing the device to examine
1024  *
1025  * @param       pass
1026  *              RETURN: resync pass for mirror
1027  *
1028  * @return      0
1029  *              if successful
1030  *
1031  * @return      non-zero
1032  *              if an error occurred.  Use get_error_string() to
1033  *              retrieve the associated error message.
1034  */
1035 int
1036 devconfig_get_mirror_pass(
1037 	devconfig_t *mirror,
1038 	uint16_t *pass)
1039 {
1040 	int error = get_uint16(mirror->attributes, ATTR_MIRROR_PASSNUM, pass);
1041 
1042 	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
1043 	if (error == ENOENT) {
1044 	    volume_set_error(gettext("mirror pass number not set"));
1045 	    error = ERR_ATTR_UNSET;
1046 	}
1047 
1048 	return (error);
1049 }
1050 
1051 /*
1052  * Set the minimum number of components in stripe
1053  *
1054  * @param       stripe
1055  *              a devconfig_t representing the stripe to modify
1056  *
1057  * @param       mincomp
1058  *              the value to set as the minimum number of components
1059  *              in stripe
1060  *
1061  * @return      0
1062  *              if successful
1063  *
1064  * @return      non-zero
1065  *              if an error occurred.  Use get_error_string() to
1066  *              retrieve the associated error message.
1067  */
1068 int
1069 devconfig_set_stripe_mincomp(
1070 	devconfig_t *stripe,
1071 	uint16_t mincomp)
1072 {
1073 	/* Validate against minimum value */
1074 	if (mincomp < MIN_NSTRIPE_COMP) {
1075 	    volume_set_error(gettext(
1076 		"minimum stripe components (%d) below minimum allowable (%d)"),
1077 		mincomp, MIN_NSTRIPE_COMP);
1078 	    return (-1);
1079 	}
1080 
1081 	return (set_uint16(stripe->attributes, ATTR_STRIPE_MINCOMP, mincomp));
1082 }
1083 
1084 /*
1085  * Get minimum number of components in stripe
1086  *
1087  * @param       device
1088  *              a devconfig_t representing the device to examine
1089  *
1090  * @param       mincomp
1091  *              RETURN: minimum number of components in stripe
1092  *
1093  * @return      0
1094  *              if successful
1095  *
1096  * @return      non-zero
1097  *              if an error occurred.  Use get_error_string() to
1098  *              retrieve the associated error message.
1099  */
1100 int
1101 devconfig_get_stripe_mincomp(
1102 	devconfig_t *stripe,
1103 	uint16_t *mincomp)
1104 {
1105 	int error = get_uint16(
1106 	    stripe->attributes, ATTR_STRIPE_MINCOMP, mincomp);
1107 
1108 	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
1109 	if (error == ENOENT) {
1110 	    volume_set_error(
1111 		gettext("minimum number of stripe components not set"));
1112 	    error = ERR_ATTR_UNSET;
1113 	}
1114 
1115 	return (error);
1116 }
1117 
1118 /*
1119  * Set the maximum number of components in stripe
1120  *
1121  * @param       stripe
1122  *              a devconfig_t representing the stripe to modify
1123  *
1124  * @param       maxcomp
1125  *              the value to set as the maximum number of components
1126  *              in stripe
1127  *
1128  * @return      0
1129  *              if successful
1130  *
1131  * @return      non-zero
1132  *              if an error occurred.  Use get_error_string() to
1133  *              retrieve the associated error message.
1134  */
1135 int
1136 devconfig_set_stripe_maxcomp(
1137 	devconfig_t *stripe,
1138 	uint16_t maxcomp)
1139 {
1140 	/* Validate against minimum value */
1141 	if (maxcomp < MIN_NSTRIPE_COMP) {
1142 	    volume_set_error(gettext(
1143 		"maximum stripe components (%d) below minimum allowable (%d)"),
1144 		maxcomp, MIN_NSTRIPE_COMP);
1145 	    return (-1);
1146 	}
1147 
1148 	return (set_uint16(stripe->attributes, ATTR_STRIPE_MAXCOMP, maxcomp));
1149 }
1150 
1151 /*
1152  * Get maximum number of components in stripe
1153  *
1154  * @param       device
1155  *              a devconfig_t representing the device to examine
1156  *
1157  * @param       maxcomp
1158  *              RETURN: maximum number of components in stripe
1159  *
1160  * @return      0
1161  *              if successful
1162  *
1163  * @return      non-zero
1164  *              if an error occurred.  Use get_error_string() to
1165  *              retrieve the associated error message.
1166  */
1167 int
1168 devconfig_get_stripe_maxcomp(
1169 	devconfig_t *stripe,
1170 	uint16_t *maxcomp)
1171 {
1172 	int error = get_uint16(
1173 	    stripe->attributes, ATTR_STRIPE_MAXCOMP, maxcomp);
1174 
1175 	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
1176 	if (error == ENOENT) {
1177 	    volume_set_error(
1178 		gettext("maximum number of stripe components not set"));
1179 	    error = ERR_ATTR_UNSET;
1180 	}
1181 
1182 	return (error);
1183 }
1184 
1185 /*
1186  * Set the stripe interlace
1187  *
1188  * @param       stripe
1189  *              a devconfig_t representing the stripe to modify
1190  *
1191  * @param       interlace
1192  *              the value to set as the stripe interlace
1193  *
1194  * @return      0
1195  *              if successful
1196  *
1197  * @return      non-zero
1198  *              if an error occurred.  Use get_error_string() to
1199  *              retrieve the associated error message.
1200  */
1201 int
1202 devconfig_set_stripe_interlace(
1203 	devconfig_t *stripe,
1204 	uint64_t interlace)
1205 {
1206 	if (interlace < MININTERLACE || interlace > MAXINTERLACE) {
1207 	    char *intstr = NULL;
1208 	    char *minstr = NULL;
1209 	    char *maxstr = NULL;
1210 
1211 	    /* Get string representations of interlaces */
1212 	    bytes_to_sizestr(interlace, &intstr, universal_units, B_FALSE);
1213 	    bytes_to_sizestr(MININTERLACE, &minstr, universal_units, B_FALSE);
1214 	    bytes_to_sizestr(MAXINTERLACE, &maxstr, universal_units, B_FALSE);
1215 
1216 	    volume_set_error(
1217 		gettext("interlace (%s) out of valid range (%s - %s)"),
1218 		intstr, minstr, maxstr);
1219 
1220 	    free(intstr);
1221 	    free(minstr);
1222 	    free(maxstr);
1223 
1224 	    return (-1);
1225 	}
1226 
1227 	return (set_uint64(stripe->attributes,
1228 	    ATTR_STRIPE_INTERLACE, interlace));
1229 }
1230 
1231 /*
1232  * Get stripe interlace
1233  *
1234  * @param       device
1235  *              a devconfig_t representing the device to examine
1236  *
1237  * @param       interlace
1238  *              RETURN: stripe interlace
1239  *
1240  * @return      0
1241  *              if successful
1242  *
1243  * @return      non-zero
1244  *              if an error occurred.  Use get_error_string() to
1245  *              retrieve the associated error message.
1246  */
1247 int
1248 devconfig_get_stripe_interlace(
1249 	devconfig_t *stripe,
1250 	uint64_t *interlace)
1251 {
1252 	int error = get_uint64(
1253 	    stripe->attributes, ATTR_STRIPE_INTERLACE, interlace);
1254 
1255 	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
1256 	if (error == ENOENT) {
1257 	    volume_set_error(gettext("stripe interlace not set"));
1258 	    error = ERR_ATTR_UNSET;
1259 	}
1260 
1261 	return (error);
1262 }
1263 
1264 /*
1265  * Set the redundancy level for a volume.
1266  *
1267  * @param       volume
1268  *              a devconfig_t representing the volume to modify
1269  *
1270  * @param       rlevel
1271  *              If 0, a stripe will be created.  If > 0, a mirror with
1272  *              this number of submirrors will be created.
1273  *
1274  * @return      0
1275  *              if successful
1276  *
1277  * @return      non-zero
1278  *              if an error occurred.  Use get_error_string() to
1279  *              retrieve the associated error message.
1280  */
1281 int
1282 devconfig_set_volume_redundancy_level(
1283 	devconfig_t *volume,
1284 	uint16_t rlevel)
1285 {
1286 	/* Validate against limits */
1287 	if (rlevel > NMIRROR) {
1288 	    volume_set_error(gettext(
1289 		"volume redundancy level (%d) out of valid range (%d-%d)"),
1290 		rlevel, 0, NMIRROR);
1291 	    return (-1);
1292 	}
1293 
1294 	return (set_uint16(volume->attributes, ATTR_VOLUME_REDUNDANCY, rlevel));
1295 }
1296 
1297 /*
1298  * Get the redundancy level for a volume.
1299  *
1300  * @param       device
1301  *              a devconfig_t representing the device to examine
1302  *
1303  * @param       rlevel
1304  *              RETURN: the redundancy level for a volume
1305  *
1306  * @return      0
1307  *              if successful
1308  *
1309  * @return      non-zero
1310  *              if an error occurred.  Use get_error_string() to
1311  *              retrieve the associated error message.
1312  */
1313 int
1314 devconfig_get_volume_redundancy_level(
1315 	devconfig_t *volume,
1316 	uint16_t *rlevel)
1317 {
1318 	int error = get_uint16(
1319 	    volume->attributes, ATTR_VOLUME_REDUNDANCY, rlevel);
1320 
1321 	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
1322 	if (error == ENOENT) {
1323 	    volume_set_error(gettext("volume redundancy level not set"));
1324 	    error = ERR_ATTR_UNSET;
1325 	}
1326 
1327 	return (error);
1328 }
1329 
1330 /*
1331  * Set the number of paths in volume
1332  *
1333  * @param       volume
1334  *              a devconfig_t representing the volume to modify
1335  *
1336  * @param       npaths
1337  *              the value to set as the number of paths in volume
1338  *
1339  * @return      0
1340  *              if successful
1341  *
1342  * @return      non-zero
1343  *              if an error occurred.  Use get_error_string() to
1344  *              retrieve the associated error message.
1345  */
1346 int
1347 devconfig_set_volume_npaths(
1348 	devconfig_t *volume,
1349 	uint16_t npaths)
1350 {
1351 	/* Validate against limits */
1352 	if (npaths < MIN_NDATAPATHS || npaths > MAX_NDATAPATHS) {
1353 	    volume_set_error(
1354 		gettext("number of data paths (%d) out of valid range (%d-%d)"),
1355 		npaths, MIN_NDATAPATHS, MAX_NDATAPATHS);
1356 	    return (-1);
1357 	}
1358 
1359 	return (set_uint16(volume->attributes, ATTR_VOLUME_DATAPATHS, npaths));
1360 }
1361 
1362 /*
1363  * Get number of paths in volume
1364  *
1365  * @param       device
1366  *              a devconfig_t representing the device to examine
1367  *
1368  * @param       npaths
1369  *              RETURN: number of paths in volume
1370  *
1371  * @return      0
1372  *              if successful
1373  *
1374  * @return      non-zero
1375  *              if an error occurred.  Use get_error_string() to
1376  *              retrieve the associated error message.
1377  */
1378 int
1379 devconfig_get_volume_npaths(
1380 	devconfig_t *volume,
1381 	uint16_t *npaths)
1382 {
1383 	int error = get_uint16(
1384 	    volume->attributes, ATTR_VOLUME_DATAPATHS, npaths);
1385 
1386 	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
1387 	if (error == ENOENT) {
1388 	    volume_set_error(gettext("number of data paths not set"));
1389 	    error = ERR_ATTR_UNSET;
1390 	}
1391 
1392 	return (error);
1393 }
1394 
1395 /*
1396  * Set the HSP creation option (for volume, stripe, concat, mirror)
1397  *
1398  * @param       volume
1399  *              a devconfig_t representing the volume to modify
1400  *
1401  * @param       usehsp
1402  *              the value to set as the HSP creation option
1403  *
1404  * @return      0
1405  *              if successful
1406  *
1407  * @return      non-zero
1408  *              if an error occurred.  Use get_error_string() to
1409  *              retrieve the associated error message.
1410  */
1411 int
1412 devconfig_set_volume_usehsp(
1413 	devconfig_t *volume,
1414 	boolean_t usehsp)
1415 {
1416 	return (set_boolean(volume->attributes, ATTR_VOLUME_USEHSP, usehsp));
1417 }
1418 
1419 /*
1420  * Get HSP creation option (for volume, stripe, concat, mirror)
1421  *
1422  * @param       device
1423  *              a devconfig_t representing the device to examine
1424  *
1425  * @param       usehsp
1426  *              RETURN: HSP creation option (for volume, stripe,
1427  *              concat, mirror)
1428  *
1429  * @return      0
1430  *              if successful
1431  *
1432  * @return      non-zero
1433  *              if an error occurred.  Use get_error_string() to
1434  *              retrieve the associated error message.
1435  */
1436 int
1437 devconfig_get_volume_usehsp(
1438 	devconfig_t *volume,
1439 	boolean_t *usehsp)
1440 {
1441 	int error = get_boolean(
1442 	    volume->attributes, ATTR_VOLUME_USEHSP, usehsp);
1443 
1444 	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
1445 	if (error == ENOENT) {
1446 	    volume_set_error(gettext("volume usehsp not set"));
1447 	    error = ERR_ATTR_UNSET;
1448 	}
1449 
1450 	return (error);
1451 }
1452 
1453 /*
1454  * Get the string representation of the volume's type
1455  *
1456  * @param       type
1457  *              a valid component_type_t
1458  *
1459  * @return      an internationalized string representing the given
1460  *              type
1461  */
1462 char *
1463 devconfig_type_to_str(
1464 	component_type_t type)
1465 {
1466 	char *str;
1467 
1468 	switch (type) {
1469 	    case TYPE_CONCAT:	    str = gettext("Concat");	    break;
1470 	    case TYPE_CONTROLLER:   str = gettext("Controller");    break;
1471 	    case TYPE_DISKSET:	    str = gettext("Diskset");	    break;
1472 	    case TYPE_DRIVE:	    str = gettext("Disk");	    break;
1473 	    case TYPE_EXTENT:	    str = gettext("Extent");	    break;
1474 	    case TYPE_HOST:	    str = gettext("Host");	    break;
1475 	    case TYPE_HSP:	    str = gettext("Hot Spare Pool"); break;
1476 	    case TYPE_MIRROR:	    str = gettext("Mirror");	    break;
1477 	    case TYPE_RAID5:	    str = gettext("Raid5");	    break;
1478 	    case TYPE_SLICE:	    str = gettext("Slice");	    break;
1479 	    case TYPE_SOFTPART:	    str = gettext("Soft Partition"); break;
1480 	    case TYPE_STRIPE:	    str = gettext("Stripe");	    break;
1481 	    case TYPE_TRANS:	    str = gettext("Trans");	    break;
1482 	    case TYPE_VOLUME:	    str = gettext("Volume");	    break;
1483 	    default:
1484 	    case TYPE_UNKNOWN:	    str = gettext("Unknown");	    break;
1485 	}
1486 
1487 	return (str);
1488 }
1489 
1490 /*
1491  * Get the string representation of the mirror's read strategy
1492  *
1493  * @param       read
1494  *              a valid mirror_read_strategy_t
1495  *
1496  * @return      an internationalized string representing the given
1497  *              read strategy
1498  */
1499 char *
1500 devconfig_read_strategy_to_str(
1501 	mirror_read_strategy_t read)
1502 {
1503 	char *str;
1504 
1505 	switch (read) {
1506 	    case MIRROR_READ_ROUNDROBIN: str = gettext("ROUNDROBIN");	break;
1507 	    case MIRROR_READ_GEOMETRIC:	 str = gettext("GEOMETRIC");	break;
1508 	    case MIRROR_READ_FIRST:	 str = gettext("FIRST");	break;
1509 	    default:			 str = "";
1510 	}
1511 
1512 	return (str);
1513 }
1514 
1515 /*
1516  * Get the string representation of the mirror's write strategy
1517  *
1518  * @param       write
1519  *              a valid mirror_write_strategy_t
1520  *
1521  * @return      an internationalized string representing the given
1522  *              write strategy
1523  */
1524 char *
1525 devconfig_write_strategy_to_str(
1526 	mirror_write_strategy_t write)
1527 {
1528 	char *str;
1529 
1530 	switch (write) {
1531 	    case MIRROR_WRITE_PARALLEL:	str = gettext("PARALLEL");	break;
1532 	    case MIRROR_WRITE_SERIAL:	str = gettext("SERIAL");	break;
1533 	    default:			str = "";
1534 	}
1535 
1536 	return (str);
1537 }
1538 
1539 #ifdef DEBUG
1540 /*
1541  * Dump the contents of a devconfig_t struct to stdout.
1542  *
1543  * @param       device
1544  *              the devconfig_t to examine
1545  *
1546  * @param       prefix
1547  *              a prefix string to print before each line
1548  */
1549 void
1550 devconfig_dump(
1551 	devconfig_t *device,
1552 	char *prefix)
1553 {
1554 	dlist_t *comps = NULL;
1555 	char **array = NULL;
1556 	char *str = NULL;
1557 	int i = 0;
1558 
1559 	component_type_t type = TYPE_UNKNOWN;
1560 	boolean_t bool = B_FALSE;
1561 	uint16_t val16 = 0;
1562 	uint64_t val64 = 0;
1563 	mirror_read_strategy_t read;
1564 	mirror_write_strategy_t write;
1565 
1566 	if (device == NULL) {
1567 	    return;
1568 	}
1569 
1570 	/* Type */
1571 	if (devconfig_get_type(device, &type) == 0) {
1572 	    printf("%s%s\n", prefix, devconfig_type_to_str(type));
1573 	}
1574 
1575 	/* Name */
1576 	if (devconfig_get_name(device, &str) == 0) {
1577 	    printf("%s  name: %s\n", prefix, str);
1578 	}
1579 
1580 	/* Size in bytes */
1581 	if (devconfig_get_size(device, &val64) == 0) {
1582 	    printf("%s  size in bytes: %llu\n", prefix, val64);
1583 	}
1584 
1585 	/* Size in blocks */
1586 	if (devconfig_get_size_in_blocks(device, &val64) == 0) {
1587 	    printf("%s  size in blocks: %llu\n", prefix, val64);
1588 	}
1589 
1590 	/* Use HSP */
1591 	if (devconfig_get_volume_usehsp(device, &bool) == 0) {
1592 	    printf("%s  usehsp: %s\n", prefix, bool? "TRUE" : "FALSE");
1593 	}
1594 
1595 	switch (type) {
1596 	    case TYPE_VOLUME:
1597 		/* Volume rlevel */
1598 		if (devconfig_get_volume_redundancy_level(
1599 		    device, &val16) == 0) {
1600 		    printf("%s  volume redundancy level: %d\n", prefix, val16);
1601 		}
1602 
1603 		/* Volume npaths */
1604 		if (devconfig_get_volume_npaths(device, &val16) == 0) {
1605 		    printf("%s  volume npaths: %d\n", prefix, val16);
1606 		}
1607 	    break;
1608 
1609 	    case TYPE_MIRROR:
1610 
1611 		/* Mirror nsubs */
1612 		if (devconfig_get_mirror_nsubs(device, &val16) == 0) {
1613 		    printf("%s  mirror nsubs: %d\n", prefix, val16);
1614 		}
1615 
1616 		/* Mirror read */
1617 		if (devconfig_get_mirror_read(device, &read) == 0) {
1618 		    printf("%s  mirror read: %s\n", prefix,
1619 			devconfig_read_strategy_to_str(read));
1620 		}
1621 
1622 		/* Mirror write */
1623 		if (devconfig_get_mirror_write(device, &write) == 0) {
1624 		    printf("%s  mirror write: %s\n", prefix,
1625 			devconfig_write_strategy_to_str(write));
1626 		}
1627 
1628 		/* Mirror pass */
1629 		if (devconfig_get_mirror_pass(device, &val16) == 0) {
1630 		    printf("%s  mirror pass: %d\n", prefix, val16);
1631 		}
1632 	    break;
1633 
1634 	    case TYPE_STRIPE:
1635 		/* Stripe mincomp */
1636 		if (devconfig_get_stripe_mincomp(device, &val16) == 0) {
1637 		    printf("%s  stripe mincomp: %d\n", prefix, val16);
1638 		}
1639 
1640 		/* Stripe maxcomp */
1641 		if (devconfig_get_stripe_maxcomp(device, &val16) == 0) {
1642 		    printf("%s  stripe maxcomp: %d\n", prefix, val16);
1643 		}
1644 
1645 		/* Stripe interlace */
1646 		if (devconfig_get_stripe_interlace(device, &val64) == 0) {
1647 		    printf("%s  stripe interlace: %lld\n", prefix, val64);
1648 		}
1649 	    break;
1650 
1651 	    case TYPE_SLICE:
1652 		/* Slice index */
1653 		if (devconfig_get_slice_index(device, &val16) == 0) {
1654 		    printf("%s  slice index: %d\n", prefix, val16);
1655 		}
1656 
1657 		/* Slice start block */
1658 		if (devconfig_get_slice_start_block(device, &val64) == 0) {
1659 		    printf("%s  slice start block: %llu\n", prefix, val64);
1660 		}
1661 	    break;
1662 	}
1663 
1664 	array = devconfig_get_available(device);
1665 	if (array != NULL) {
1666 	    printf("%s  available:\n", prefix);
1667 	    for (i = 0; array[i] != NULL; i++) {
1668 		printf("%s    %s\n", prefix, array[i]);
1669 	    }
1670 	}
1671 
1672 	array = devconfig_get_unavailable(device);
1673 	if (array != NULL) {
1674 	    printf("%s  unavailable:\n", prefix);
1675 	    for (i = 0; array[i] != NULL; i++) {
1676 		printf("%s    %s\n", prefix, array[i]);
1677 	    }
1678 	}
1679 
1680 	printf("\n");
1681 
1682 	comps = devconfig_get_components(device);
1683 	if (comps != NULL) {
1684 	    char buf[128];
1685 	    snprintf(buf, 128, "%s%s", prefix, "    ");
1686 	    for (; comps != NULL; comps = comps->next) {
1687 		devconfig_dump((devconfig_t *)comps->obj, buf);
1688 	    }
1689 	}
1690 }
1691 #endif /* DEBUG */
1692