14004 libtd should discover zpools on entire partitions
authorMatt Keenan <matt.keenan@oracle.com>
Thu, 04 Nov 2010 10:08:30 +0000
changeset 916 baf7bb568bce
parent 915 9a4ad1c2edc4
child 917 64c14627472e
14004 libtd should discover zpools on entire partitions
usr/src/lib/libtd/Makefile
usr/src/lib/libtd/td_api.h
usr/src/lib/libtd/td_dd.c
usr/src/lib/libtd/td_dd.h
usr/src/lib/libtd/td_mg.c
usr/src/lib/libtd/td_zpool.c
usr/src/lib/libtd/td_zpool.h
usr/src/lib/libtd/test_td.c
--- a/usr/src/lib/libtd/Makefile	Tue Nov 02 18:25:00 2010 +0100
+++ b/usr/src/lib/libtd/Makefile	Thu Nov 04 10:08:30 2010 +0000
@@ -37,12 +37,14 @@
 	td_dd.o \
 	td_dd_svm.o \
 	td_iscsi.o \
+	td_zpool.o \
 	test_td.o
 
 PRIVHDRS = \
 	td_lib.h \
 	td_version.h \
-	td_dd.h
+	td_dd.h \
+	td_zpool.h
 EXPHDRS = \
 	td_api.h
 HDRS		= $(EXPHDRS) $(PRIVHDRS)
@@ -59,7 +61,7 @@
 LDFLAGS		+=
 SOFLAGS		+= -L$(ROOTADMINLIB) -R$(ROOTADMINLIB:$(ROOT)%=%) \
 		-L$(ROOTUSRLIB) -R$(ROOTUSRLIB:$(ROOT)%=%) \
-		-ldiskmgt -lfstyp -lnvpair -llogsvc -linstzones -lima
+		-ldiskmgt -lfstyp -lnvpair -llogsvc -linstzones -lima -lzfs -lm
 
 ROOT_TEST_PROGS	= $(TEST_PROGS:%=$(ROOTOPTINSTALLTESTBIN)/%)
 CLEANFILES	= $(TEST_PROGS)
@@ -74,7 +76,7 @@
 	$(LINK.c) -o tdmgtst tdmgtst.o \
 		-R$(ROOTADMINLIB:$(ROOT)%=%) \
 		-L$(ROOTADMINLIB) -Lpics/$(ARCH) \
-		-ltd -lfstyp -llogsvc -lnvpair
+		-ltd -lfstyp -llogsvc -lnvpair -lzfs -lm
 
 # statically built Target Discovery Manager test program
 tdmgtst_static:	static tdmgtst.o
@@ -84,14 +86,14 @@
 		-ltd -llogsvc \
 		-Bdynamic \
 		-ldiskmgt -lfstyp -lnvpair -ldevinfo -ladm \
-		-linstzones -lzonecfg -lcontract -lgen -lima
+		-linstzones -lzonecfg -lcontract -lgen -lima -lzfs -lm
 
 # Target Discovery test program
 test_td:	dynamic test_td.o
 	$(LINK.c) -o test_td test_td.o \
 		-R$(ROOTADMINLIB:$(ROOT)%=%) \
 		-L$(ROOTADMINLIB) -Lpics/$(ARCH) \
-		-ltd -lfstyp -llogsvc -lnvpair
+		-ltd -lfstyp -llogsvc -lnvpair -lzfs -lm
 
 # statically built Target Discovery test program
 test_td_static:	static test_td.o
@@ -101,7 +103,7 @@
 		-ltd -llogsvc \
 		-Bdynamic \
 		-ldiskmgt -lfstyp -lnvpair -ldevinfo -ladm \
-		-linstzones -lzonecfg -lcontract -lgen -lima
+		-linstzones -lzonecfg -lcontract -lgen -lima -lzfs -lm
 
 static: $(LIBS)
 
--- a/usr/src/lib/libtd/td_api.h	Tue Nov 02 18:25:00 2010 +0100
+++ b/usr/src/lib/libtd/td_api.h	Thu Nov 04 10:08:30 2010 +0000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _TD_API_H
@@ -38,7 +37,6 @@
 #endif
 
 /* type definitions */
-
 typedef uint64_t td_handle_t;
 
 typedef enum {
@@ -64,7 +62,8 @@
 	TD_OT_DISK = 0,		/* writeable disk - install candidate */
 	TD_OT_PARTITION,	/* disk partition */
 	TD_OT_SLICE,		/* ufs file system slice */
-	TD_OT_OS		/* Solaris OS instance/BE */
+	TD_OT_OS,		/* Solaris OS instance/BE */
+	TD_OT_ZPOOL		/* ZFS Pool */
 } td_object_type_t;
 
 #define	TD_IOCTL_TIMEOUT 10 /* seconds to timeout blocking ioctls */
@@ -238,6 +237,30 @@
 #define	INSTISCSI_MAX_LUN_LEN		32
 #define	INSTISCSI_MAX_INITIATOR_LEN	INSTISCSI_MAX_ISCSI_NAME_LEN
 
+/* nv attribute names for zpools */
+#define	TD_ZPOOL_ATTR_NAME	"zpool_name"
+#define	TD_ZPOOL_ATTR_HEALTH	"zpool_health"
+#define	TD_ZPOOL_ATTR_STATUS	"zpool_status"
+#define	TD_ZPOOL_ATTR_GUID	"zpool_guid"
+#define	TD_ZPOOL_ATTR_SIZE	"zpool_size"
+#define	TD_ZPOOL_ATTR_CAPACITY	"zpool_capacity"
+#define	TD_ZPOOL_ATTR_VERSION	"zpool_version"
+#define	TD_ZPOOL_ATTR_BOOTFS	"zpool_bootfs"
+#define	TD_ZPOOL_ATTR_IMPORT	"zpool_import"
+#define	TD_ZPOOL_ATTR_NUM_TARGETS	"zpool_num_targets"
+#define	TD_ZPOOL_ATTR_TARGETS	"zpool_targets"
+#define	TD_ZPOOL_ATTR_NUM_LOGS	"zpool_num_logs"
+#define	TD_ZPOOL_ATTR_LOGS	"zpool_logs"
+#define	TD_ZPOOL_ATTR_NUM_SPARES	"zpool_num_spares"
+#define	TD_ZPOOL_ATTR_SPARES	"zpool_spares"
+#define	TD_ZPOOL_ATTR_NUM_L2CACHE	"zpool_num_l2cache"
+#define	TD_ZPOOL_ATTR_L2CACHE	"zpool_l2cache"
+#define	TD_ZPOOL_ATTR_TARGET_NAME	"zpool_target_name"
+#define	TD_ZPOOL_ATTR_TARGET_HEALTH	"zpool_target_health"
+#define	TD_ZPOOL_ATTR_TARGET_READ_ERRORS	"zpool_target_read_errors"
+#define	TD_ZPOOL_ATTR_TARGET_WRITE_ERRORS	"zpool_target_write_errors"
+#define	TD_ZPOOL_ATTR_TARGET_CHECKSUM_ERRORS	"zpool_target_checksum_errors"
+
 /*
  * bitfields indicate reasons for upgrade failure
  */
--- a/usr/src/lib/libtd/td_dd.c	Tue Nov 02 18:25:00 2010 +0100
+++ b/usr/src/lib/libtd/td_dd.c	Thu Nov 04 10:08:30 2010 +0000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <assert.h>
@@ -624,7 +623,7 @@
 
 	/* if leading /a, strip it */
 	if (strncmp(fsp->fs_fsmnt, slasha, strlen(slasha)) == 0) {
-	    return (fsp->fs_fsmnt + 2);
+		return (fsp->fs_fsmnt + 2);
 	}
 
 	return (fsp->fs_fsmnt);
@@ -1320,6 +1319,33 @@
 	return (device_path);
 }
 
+/*
+ * ddm_count_handles()
+ *	Cycle through array of ddm_handles and return count
+ *
+ * Parameters:
+ *	ddm_handle_t *dh	:	Array of handles to count
+ * Return:
+ *	retcnt	:	Number of items in array, 0 for none.
+ * Scope:
+ *	private
+ */
+static int
+ddm_count_handles(ddm_handle_t *dh)
+{
+	int retcnt = 0;
+	ddm_handle_t *tmpdh;
+
+	if (dh == NULL) {
+		return (retcnt);
+	}
+
+	for (retcnt = 0, tmpdh = dh; *tmpdh != NULL;
+	    tmpdh++, retcnt++)
+		;
+
+	return (retcnt);
+}
 
 /* ----------------------- public functions --------------------------- */
 
@@ -1377,14 +1403,14 @@
  *	Disk discovery
  *
  * Parameters:
- *	none
+ *	ndisks	Return number of disks discovered
  * Return:
  *	ddm_handle_t * - list of drive handles
  * Status:
  *	public
  */
 ddm_handle_t *
-ddm_get_disks(void)
+ddm_get_disks(int *ndisks)
 {
 	ddm_handle_t	*df;
 	int		errn;
@@ -1400,6 +1426,10 @@
 
 	assert(ddm_drive_desc == NULL);
 
+	/* Initialize number of disks discovered to 0 */
+	if (ndisks != NULL)
+		*ndisks = 0;
+
 	ddm_drive_desc = dm_get_descriptors(DM_DRIVE, NULL, &errn);
 
 	if (ddm_drive_desc == NULL) {
@@ -1418,6 +1448,9 @@
 		    "Couldn't filter the disks\n");
 	}
 
+	if (ndisks != NULL)
+		*ndisks = ddm_count_handles(df);
+
 	return (df);
 }
 
@@ -1728,19 +1761,24 @@
 /*
  * ddm_get_partitions()
  * 	Discovers partitions for particular disk
- * Parameters: d	handle of drive, which is subject of partition discovery
- *			process. If set to DDM_DISCOVER_ALL, all partitions for
- *			all drives are reported.
+ * Parameters:
+ *	d	handle of drive, which is subject of partition discovery
+ *		process. If set to DDM_DISCOVER_ALL, all partitions for
+ *		all drives are reported.
+ *	nparts	Number of partitions discovered.
  */
 ddm_handle_t *
-ddm_get_partitions(ddm_handle_t d)
+ddm_get_partitions(ddm_handle_t d, int *nparts)
 {
 	dm_descriptor_t	*am;
 	dm_descriptor_t	*ddm_part_desc;
 	int		errn;
 
+	/* Initialize number discovered to 0 */
+	if (nparts != NULL)
+		*nparts = 0;
+
 	/* discover all partitions for all drives */
-
 	if (d == DDM_DISCOVER_ALL) {
 		ddm_part_desc = dm_get_descriptors(DM_PARTITION, NULL, &errn);
 
@@ -1752,6 +1790,10 @@
 			return (NULL);
 		}
 
+		if (nparts != NULL) {
+			*nparts =
+			    ddm_count_handles((ddm_handle_t *)ddm_part_desc);
+		}
 		return ((ddm_handle_t *)ddm_part_desc);
 	}
 
@@ -1791,6 +1833,8 @@
 	}
 
 	dm_free_descriptors(am);
+	if (nparts != NULL)
+		*nparts = ddm_count_handles((ddm_handle_t *)ddm_part_desc);
 	return ((ddm_handle_t *)ddm_part_desc);
 }
 
@@ -1900,21 +1944,24 @@
  * ddm_get_slices()
  * 	Discovers slices for particular disk/partition or discover all
  *	slices
- * Parameters:	h	handle of drive/partition, for which slices will be
- *			discovered. If NULL, all slices are reported.
+ * Parameters:
+ *	h	handle of drive/partition, for which slices will be
+ *		discovered. If NULL, all slices are reported.
+ *	nslices	Return number of slices discovered.
  */
 ddm_handle_t *
-ddm_get_slices(ddm_handle_t h)
+ddm_get_slices(ddm_handle_t h, int *nslices)
 {
 	dm_descriptor_t	*ddm_slice_desc;
 	dm_descriptor_t	*am;
 	dm_desc_type_t	desc_type;
-
 	int		errn;
 
+	/* Initialize number of slices found to 0 */
+	if (nslices != NULL)
+		*nslices = 0;
 
 	/* discover all slices */
-
 	if (h == DDM_DISCOVER_ALL) {
 		ddm_slice_desc = dm_get_descriptors(DM_SLICE, NULL, &errn);
 
@@ -1926,6 +1973,10 @@
 			return (NULL);
 		}
 
+		if (nslices != NULL) {
+			*nslices =
+			    ddm_count_handles((ddm_handle_t *)ddm_slice_desc);
+		}
 		return ((ddm_handle_t *)ddm_slice_desc);
 	}
 
@@ -1934,11 +1985,9 @@
 	 * first check, if slice type is associated with
 	 * the type provided by handle
 	 */
-
 	desc_type = dm_get_type((dm_descriptor_t)h);
 
 	/* slice can be only discovered for particular disk or partition */
-
 	if ((desc_type != DM_DRIVE) && (desc_type != DM_PARTITION)) {
 		DDM_DEBUG(DDM_DBGLVL_ERROR, "%s",
 		    "ddm_get_slices(): This handle is not assoc with slice\n");
@@ -1962,6 +2011,10 @@
 			return (NULL);
 		}
 
+		if (nslices != NULL) {
+			*nslices =
+			    ddm_count_handles((ddm_handle_t *)ddm_slice_desc);
+		}
 		return ((ddm_handle_t *)ddm_slice_desc);
 	}
 
@@ -1994,6 +2047,8 @@
 	}
 
 	dm_free_descriptors(am);
+	if (nslices != NULL)
+		*nslices = ddm_count_handles((ddm_handle_t *)ddm_slice_desc);
 	return ((ddm_handle_t *)ddm_slice_desc);
 }
 
@@ -2136,10 +2191,10 @@
 	dm_get_slice_stats(name, &slice_stats, &errn);
 
 	if (errn != 0) {
-	    DDM_DEBUG(DDM_DBGLVL_ERROR,
-		"ddm_get_slice_stats(): Can't get slice inuse data, "
-		"for %s, err=%d\n", name, errn);
-	    return (errn);
+		DDM_DEBUG(DDM_DBGLVL_ERROR,
+		    "ddm_get_slice_stats(): Can't get slice inuse data, "
+		    "for %s, err=%d\n", name, errn);
+		return (errn);
 	}
 
 	/*
@@ -2155,10 +2210,10 @@
 	 * No inuse data available
 	 */
 	if (used_by == NULL || used_name == NULL) {
-	    DDM_DEBUG(DDM_DBGLVL_NOTICE, "ddm_get_slice_inuse_stats(): "
-		"No inuse data available for %s\n", name);
-	    nvlist_free(slice_stats);
-	    return (0);
+		DDM_DEBUG(DDM_DBGLVL_NOTICE, "ddm_get_slice_inuse_stats(): "
+		    "No inuse data available for %s\n", name);
+		nvlist_free(slice_stats);
+		return (0);
 	}
 
 	/*
@@ -2166,10 +2221,10 @@
 	 */
 	if ((strcmp(nvpair_name(used_by), DM_USED_BY) != 0) ||
 	    (strcmp(nvpair_name(used_name), DM_USED_NAME) != 0)) {
-	    DDM_DEBUG(DDM_DBGLVL_ERROR, "ddm_get_slice_inuse_stats(): "
-		"Problem with inuse data for %s\n", name);
-	    nvlist_free(slice_stats);
-	    return (1);
+		DDM_DEBUG(DDM_DBGLVL_ERROR, "ddm_get_slice_inuse_stats(): "
+		    "Problem with inuse data for %s\n", name);
+		nvlist_free(slice_stats);
+		return (1);
 	}
 
 	/*
@@ -2179,10 +2234,10 @@
 	nvpair_value_string(used_name, &data);
 
 	if (by == NULL || data == NULL) {
-	    DDM_DEBUG(DDM_DBGLVL_ERROR, "ddm_get_slice_inuse_stats(): "
-		"NULL value for inuse data for %s\n", name);
-	    nvlist_free(slice_stats);
-	    return (1);
+		DDM_DEBUG(DDM_DBGLVL_ERROR, "ddm_get_slice_inuse_stats(): "
+		    "NULL value for inuse data for %s\n", name);
+		nvlist_free(slice_stats);
+		return (1);
 	}
 
 	DDM_DEBUG(DDM_DBGLVL_NOTICE,
@@ -2194,18 +2249,18 @@
 	 * the inuse data to the attribute list.
 	 */
 	for (i = 0; ddm_slice_inuse_conv_tbl[i][0] != NULL; i++) {
-	    if (strcmp(by, ddm_slice_inuse_conv_tbl[i][0]) == 0) {
-		name_src = ddm_slice_inuse_conv_tbl[i][0];
-		name_dst = ddm_slice_inuse_conv_tbl[i][1];
-		break;
-	    }
+		if (strcmp(by, ddm_slice_inuse_conv_tbl[i][0]) == 0) {
+			name_src = ddm_slice_inuse_conv_tbl[i][0];
+			name_dst = ddm_slice_inuse_conv_tbl[i][1];
+			break;
+		}
 	}
 
 	if (name_src == NULL) {
-	    DDM_DEBUG(DDM_DBGLVL_ERROR,
-		"ddm_get_slice_inuse_stats(): %s not in table\n", by);
-	    nvlist_free(slice_stats);
-	    return (1);
+		DDM_DEBUG(DDM_DBGLVL_ERROR,
+		    "ddm_get_slice_inuse_stats(): %s not in table\n", by);
+		nvlist_free(slice_stats);
+		return (1);
 	}
 
 	DDM_DEBUG(DDM_DBGLVL_NOTICE,
--- a/usr/src/lib/libtd/td_dd.h	Tue Nov 02 18:25:00 2010 +0100
+++ b/usr/src/lib/libtd/td_dd.h	Thu Nov 04 10:08:30 2010 +0000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _TD_DD_H
@@ -80,11 +79,11 @@
 
 
 /* function prototypes */
-extern ddm_handle_t	*ddm_get_disks(void);
+extern ddm_handle_t	*ddm_get_disks(int *ndisks);
 extern nvlist_t		*ddm_get_disk_attributes(ddm_handle_t d);
-extern ddm_handle_t	*ddm_get_partitions(ddm_handle_t d);
+extern ddm_handle_t	*ddm_get_partitions(ddm_handle_t d, int *nparts);
 extern nvlist_t		*ddm_get_partition_attributes(ddm_handle_t p);
-extern ddm_handle_t	*ddm_get_slices(ddm_handle_t h);
+extern ddm_handle_t	*ddm_get_slices(ddm_handle_t h, int *nslices);
 extern nvlist_t		*ddm_get_slice_attributes(ddm_handle_t s);
 extern void		ddm_free_handle_list(ddm_handle_t *h);
 extern void		ddm_free_attr_list(nvlist_t *attrs);
--- a/usr/src/lib/libtd/td_mg.c	Tue Nov 02 18:25:00 2010 +0100
+++ b/usr/src/lib/libtd/td_mg.c	Thu Nov 04 10:08:30 2010 +0000
@@ -54,6 +54,7 @@
 #include <td_api.h> /* TD user definitions */
 #include <td_dd.h> /* TD disk module definitions */
 #include <td_version.h> /* version info */
+#include <td_zpool.h> /* Zpool module definitions */
 
 #include <ls_api.h>	/* logging service */
 #include <assert.h>
@@ -77,6 +78,7 @@
 	ddm_handle_t handle;		/* disk module handle */
 	nvlist_t *attrib;		/* attribute list for disk */
 	boolean_t discovery_done;	/* discovery performed for object */
+	const char *compare_attr_name; /* nvpair comparison attribute */
 };
 
 /* class for TD objects */
@@ -87,21 +89,18 @@
 	struct td_obj *objcur;		/* current object */
 	ddm_handle_t *pddm;		/* disk module handle */
 	boolean_t issorted;		/* object list has been sorted */
-	int (*compare_routine)(const void *, const void *); /* sorting */
 };
 
-/* sort comparison routines for objects */
-static int compare_disk_objs(const void *p1, const void *p2);
-static int compare_partition_objs(const void *p1, const void *p2);
-static int compare_slice_objs(const void *p1, const void *p2);
-static int compare_os_objs(const void *p1, const void *p2);
+/* sort comparison routine for all td objects objects */
+static int compare_td_objs(const void *p1, const void *p2);
 
 /* object type declarations */
 static struct td_class objlist[] = {
-	{TD_OT_DISK, 0, NULL, NULL, NULL, B_FALSE, compare_disk_objs},
-	{TD_OT_PARTITION, 0, NULL, NULL, NULL, B_FALSE, compare_partition_objs},
-	{TD_OT_SLICE, 0, NULL, NULL, NULL, B_FALSE, compare_slice_objs},
-	{TD_OT_OS, 0, NULL, NULL, NULL, B_FALSE, compare_os_objs}
+	{TD_OT_DISK, 0, NULL, NULL, NULL, B_FALSE},
+	{TD_OT_PARTITION, 0, NULL, NULL, NULL, B_FALSE},
+	{TD_OT_SLICE, 0, NULL, NULL, NULL, B_FALSE},
+	{TD_OT_OS, 0, NULL, NULL, NULL, B_FALSE},
+	{TD_OT_ZPOOL, 0, NULL, NULL, NULL, B_FALSE}
 };
 #define	is_valid_td_object_type(ot) \
 	((ot) >= 0 && (ot) < sizeof (objlist) / sizeof (objlist[0]))
@@ -116,23 +115,27 @@
 #define	PDDMDISKS (objlist[TD_OT_DISK].pddm)
 #define	PDDMPARTS (objlist[TD_OT_PARTITION].pddm)
 #define	PDDMSLICES (objlist[TD_OT_SLICE].pddm)
+#define	PDDMZPOOLS (objlist[TD_OT_ZPOOL].pddm)
 
 /* arrays of TD objects shorthand */
 #define	PDISKARR (objlist[TD_OT_DISK].objarr)
 #define	PPARTARR (objlist[TD_OT_PARTITION].objarr)
 #define	PSLICEARR (objlist[TD_OT_SLICE].objarr)
+#define	PZPOOLARR (objlist[TD_OT_ZPOOL].objarr)
 
 /* count of objects per type shorthand */
 #define	NDISKS (objlist[TD_OT_DISK].objcnt)
 #define	NPARTS (objlist[TD_OT_PARTITION].objcnt)
 #define	NSLICES (objlist[TD_OT_SLICE].objcnt)
 #define	NOS (objlist[TD_OT_OS].objcnt)
+#define	NZPOOLS (objlist[TD_OT_ZPOOL].objcnt)
 
 /* user current object pointers shorthand */
 #define	CURDISK (objlist[TD_OT_DISK].objcur)
 #define	CURPART (objlist[TD_OT_PARTITION].objcur)
 #define	CURSLICE (objlist[TD_OT_SLICE].objcur)
 #define	CUROS (objlist[TD_OT_OS].objcur)
+#define	CURZPOOL (objlist[TD_OT_ZPOOL].objcur)
 
 static td_errno_t set_td_errno(int);
 static void clear_td_errno();
@@ -209,14 +212,12 @@
 	switch (otype) {
 	case TD_OT_DISK: /* get disks */
 		if (PDDMDISKS == NULL) {
-			PDDMDISKS = ddm_get_disks();
+			NDISKS = 0;
+			PDDMDISKS = ddm_get_disks(&NDISKS);
 			if (PDDMDISKS == NULL) {
 				return (set_td_errno(TD_E_NO_DEVICE));
 			}
 		}
-		for (NDISKS = 0, pddm = PDDMDISKS; *pddm != NULL;
-		    pddm++, NDISKS++)
-			;
 		if (number_found != NULL)
 			*number_found = NDISKS;
 
@@ -240,6 +241,7 @@
 			ptdobj->handle = *pddm;
 			ptdobj->attrib = NULL;
 			ptdobj->discovery_done = B_FALSE;
+			ptdobj->compare_attr_name = TD_DISK_ATTR_NAME;
 		}
 		/* mark end of array */
 		ptdobj->handle = NULL;
@@ -248,17 +250,15 @@
 		break;
 	case TD_OT_PARTITION:
 		if (PDDMPARTS == NULL) {
-			PDDMPARTS = ddm_get_partitions(DDM_DISCOVER_ALL);
+			NPARTS = 0;
+			PDDMPARTS =
+			    ddm_get_partitions(DDM_DISCOVER_ALL, &NPARTS);
 			if (PDDMPARTS == NULL) {
 				return (set_td_errno(TD_E_END));
 			}
 		}
 		if (TLI)
 			td_debug_print(LS_DBGLVL_INFO, "got partitions\n");
-
-		for (NPARTS = 0, pddm = PDDMPARTS; *pddm != NULL;
-		    pddm++, NPARTS++)
-			;
 		if (number_found != NULL)
 			*number_found = NPARTS;
 		if (TLI)
@@ -281,6 +281,7 @@
 			ptdobj->handle = *pddm;
 			ptdobj->attrib = NULL;
 			ptdobj->discovery_done = B_FALSE;
+			ptdobj->compare_attr_name = TD_PART_ATTR_NAME;
 		}
 		/* mark end of array */
 		ptdobj->handle = NULL;
@@ -289,16 +290,14 @@
 		break;
 	case TD_OT_SLICE:
 		if (PDDMSLICES == NULL) {
-			PDDMSLICES = ddm_get_slices(DDM_DISCOVER_ALL);
+			NSLICES = 0;
+			PDDMSLICES = ddm_get_slices(DDM_DISCOVER_ALL, &NSLICES);
 			if (PDDMSLICES == NULL)
 				return (set_td_errno(TD_E_END));
 		}
 		if (TLI)
 			td_debug_print(LS_DBGLVL_INFO, "got slices\n");
 
-		for (NSLICES = 0, pddm = PDDMSLICES; *pddm != NULL;
-		    pddm++, NSLICES++)
-			;
 		if (number_found != NULL)
 			*number_found = NSLICES;
 		if (TLI)
@@ -317,6 +316,7 @@
 			ptdobj->handle = *pddm;
 			ptdobj->attrib = NULL;
 			ptdobj->discovery_done = B_FALSE;
+			ptdobj->compare_attr_name = TD_SLICE_ATTR_NAME;
 		}
 		/* mark end of array */
 		ptdobj->handle = NULL;
@@ -325,22 +325,57 @@
 		break;
 	case TD_OT_OS: /* get OS instances */
 		if (PDDMSLICES == NULL) {
-			PDDMSLICES = ddm_get_slices(NULL); /* get all slices */
+			NSLICES = 0;
+			PDDMSLICES =
+			    ddm_get_slices(NULL, &NSLICES); /* get all slices */
 			if (PDDMSLICES == NULL)
 				return (set_td_errno(TD_E_END));
 		}
-		NSLICES = 0;
-		pddm = PDDMSLICES;
-		while (*pddm != NULL) { /* count slices */
-			pddm++;
-			NSLICES++;
-		}
 		NOS = 0; /* reset master count */
 		ret = os_discover();
 		if (number_found != NULL)
 			*number_found = NOS;
 		CUROS = NULL; /* reset current to first */
 		break;
+	case TD_OT_ZPOOL: /* get zpools */
+		if (PDDMZPOOLS == NULL) {
+			NZPOOLS = 0;
+			PDDMZPOOLS = ddm_get_zpools(&NZPOOLS); /* get zpools */
+			if (PDDMZPOOLS == NULL) {
+				return (set_td_errno(TD_E_END));
+			}
+		}
+		if (number_found != NULL)
+			*number_found = NZPOOLS;
+
+		if (TLI)
+			td_debug_print(LS_DBGLVL_INFO,
+			    "got zpools nfound=%d\n", NZPOOLS);
+
+		/* allocate space for all ZFS pools plus terminator element */
+		PZPOOLARR =
+		    realloc(PZPOOLARR, (NZPOOLS + 1) * sizeof (struct td_obj));
+		if (PZPOOLARR == NULL)
+			return (set_td_errno(TD_E_MEMORY));
+
+		pddm = PDDMZPOOLS;
+		ptdobj = PZPOOLARR;
+		for (iobj = 0; iobj < NZPOOLS; iobj++, pddm++, ptdobj++) {
+			if (TLI)
+				td_debug_print(LS_DBGLVL_INFO,
+				    "zpools %d nfound=%d\n", iobj, NZPOOLS);
+
+			ptdobj->handle = *pddm;
+			ptdobj->attrib = NULL;
+			ptdobj->discovery_done = B_FALSE;
+			ptdobj->compare_attr_name = TD_ZPOOL_ATTR_NAME;
+		}
+		/* mark end of array */
+		ptdobj->handle = NULL;
+		ptdobj->attrib = NULL;
+		CURZPOOL = NULL;
+		break;
+
 	default:
 		ret = TD_E_NO_OBJECT;
 		break;
@@ -510,6 +545,23 @@
 			return (NULL);
 		}
 		return (dup_attr_set_errno(CUROS));
+	case TD_OT_ZPOOL:
+		if (CURZPOOL == NULL || CURZPOOL->handle == NULL) {
+			(void) set_td_errno(TD_E_END);
+			return (NULL);
+		}
+		if (CURZPOOL->discovery_done)
+			return (dup_attr_set_errno(CURZPOOL));
+		/* discover attributes */
+		CURZPOOL->attrib = ddm_get_zpool_attributes(CURZPOOL->handle);
+		CURZPOOL->discovery_done = B_TRUE;
+		if (CURZPOOL->attrib == NULL) {
+			if (TLI)
+				td_debug_print(LS_DBGLVL_INFO,
+				    "zpool attribute not found\n");
+			return (NULL);
+		}
+		return (dup_attr_set_errno(CURZPOOL));
 	default:
 		break;
 	}
@@ -720,6 +772,7 @@
 	free_td_obj_list(TD_OT_PARTITION);
 	free_td_obj_list(TD_OT_SLICE);
 	free_td_obj_list(TD_OT_OS);
+	free_td_obj_list(TD_OT_ZPOOL);
 	if (TLI)
 		td_debug_print(LS_DBGLVL_INFO, "td_discovery_release ends \n");
 	return (TD_E_SUCCESS);
@@ -911,6 +964,23 @@
 	pobja->attrib = onvl;
 	pobja->handle = (ddm_handle_t)onvl;
 	pobja->discovery_done = B_TRUE;
+	switch (objtype) {
+		case TD_OT_DISK :
+			pobja->compare_attr_name = TD_DISK_ATTR_NAME;
+			break;
+		case TD_OT_PARTITION :
+			pobja->compare_attr_name = TD_PART_ATTR_NAME;
+			break;
+		case TD_OT_SLICE :
+			pobja->compare_attr_name = TD_SLICE_ATTR_NAME;
+			break;
+		case TD_OT_OS :
+			pobja->compare_attr_name = TD_OS_ATTR_SLICE_NAME;
+			break;
+		case TD_OT_ZPOOL :
+			pobja->compare_attr_name = TD_ZPOOL_ATTR_NAME;
+			break;
+	}
 	if (TLI)
 		td_debug_print(LS_DBGLVL_INFO, "added to td_obj list!!!\n");
 	objlist[objtype].objcnt++;
@@ -918,6 +988,7 @@
 	pobja->attrib = NULL;
 	pobja->handle = 0L;
 	pobja->discovery_done = B_FALSE;
+	pobja->compare_attr_name = NULL;
 	return (TD_E_SUCCESS);
 }
 
@@ -1325,7 +1396,8 @@
 
 	/* for each slice, evaluate it for OS instance */
 	if (PDDMSLICES == NULL) { /* get all slices */
-		PDDMSLICES = ddm_get_slices(DDM_DISCOVER_ALL);
+		NSLICES = 0;
+		PDDMSLICES = ddm_get_slices(DDM_DISCOVER_ALL, &NSLICES);
 		if (PDDMSLICES == NULL)
 			return (TD_E_END);
 	}
@@ -2149,8 +2221,12 @@
 	if (pobl->objarr != NULL) {
 		/* release attribute data */
 		for (pobj = pobl->objarr; pobj->handle != 0; pobj++)
-			if (pobj->attrib != NULL)
+			if (pobj->attrib != NULL) {
 				nvlist_free(pobj->attrib);
+				pobj->compare_attr_name = NULL;
+				pobj->discovery_done = B_FALSE;
+				pobj->attrib = NULL;
+			}
 		/* release object instance list */
 		free(pobl->objarr);
 		pobl->objarr = NULL;
@@ -2159,10 +2235,15 @@
 	pobl->objcur = NULL;
 	pobl->objcnt = 0;
 	pobl->issorted = B_FALSE;
-	/* free handle lists from lower-level modules */
-	if (pobl->pddm != NULL) {
-		(void) ddm_free_handle_list(pobl->pddm);
-		pobl->pddm = NULL;
+	if (ot != TD_OT_ZPOOL) {
+		/* free handle lists from lower-level modules */
+		if (pobl->pddm != NULL) {
+			(void) ddm_free_handle_list(pobl->pddm);
+			pobl->pddm = NULL;
+		}
+	} else {
+		/* free zpool handle list and internal zpool linked list */
+		(void) ddm_free_zpool_list(pobl->pddm);
 	}
 }
 
@@ -2316,87 +2397,37 @@
 	return (search_disks_for_slices(pslicepar));
 }
 
+/*
+ * Function:	compare_td_objs
+ * Description:	Compare two td objects based on the object attr name.
+ *          	Return values are similar to that of strcmp()
+ * Scope:	private
+ * Parameters:
+ *		const void *p1 : zpool object one
+ *		const void *p2 : zpool object two
+ * Return:
+ *		-1	:	pd1 < pd2
+ *		0	:	pd1 == pd2
+ *		1	:	pd1 > pd2
+ */
 static int
-compare_os_objs(const void *p1, const void *p2)
+compare_td_objs(const void *p1, const void *p2)
 {
-
 	struct td_obj *o1 = (struct td_obj *)p1;
 	struct td_obj *o2 = (struct td_obj *)p2;
 	char *pd1 = NULL, *pd2 = NULL;
 
 	if (o1->attrib != NULL)
-		nvlist_lookup_string(o1->attrib, TD_OS_ATTR_SLICE_NAME, &pd1);
+		nvlist_lookup_string(o1->attrib, o1->compare_attr_name, &pd1);
 	if (o2->attrib != NULL)
-		nvlist_lookup_string(o2->attrib, TD_OS_ATTR_SLICE_NAME, &pd2);
-	if (pd1 == NULL && pd2 == NULL)
-		return (0);
-	if (pd1 == NULL)
-		return (-1);
-	if (pd2 == NULL)
-		return (1);
-	return (strcmp(pd1, pd2));
-}
-
-static int
-compare_disk_objs(const void *p1, const void *p2)
-{
-
-	struct td_obj *o1 = (struct td_obj *)p1;
-	struct td_obj *o2 = (struct td_obj *)p2;
-	char *pd1 = NULL, *pd2 = NULL;
-
-	if (o1->attrib != NULL)
-		nvlist_lookup_string(o1->attrib, TD_DISK_ATTR_NAME, &pd1);
-	if (o2->attrib != NULL)
-		nvlist_lookup_string(o2->attrib, TD_DISK_ATTR_NAME, &pd2);
+		nvlist_lookup_string(o2->attrib, o2->compare_attr_name, &pd2);
 	if (pd1 == NULL && pd2 == NULL)
 		return (0);
 	if (pd1 == NULL)
 		return (-1);
 	if (pd2 == NULL)
 		return (1);
-	return (strcmp(pd1, pd2));
-}
 
-static int
-compare_slice_objs(const void *p1, const void *p2)
-{
-
-	struct td_obj *o1 = (struct td_obj *)p1;
-	struct td_obj *o2 = (struct td_obj *)p2;
-	char *pd1 = NULL, *pd2 = NULL;
-
-	if (o1->attrib != NULL)
-		nvlist_lookup_string(o1->attrib, TD_SLICE_ATTR_NAME, &pd1);
-	if (o2->attrib != NULL)
-		nvlist_lookup_string(o2->attrib, TD_SLICE_ATTR_NAME, &pd2);
-	if (pd1 == NULL && pd2 == NULL)
-		return (0);
-	if (pd1 == NULL)
-		return (-1);
-	if (pd2 == NULL)
-		return (1);
-	return (strcmp(pd1, pd2));
-}
-
-static int
-compare_partition_objs(const void *p1, const void *p2)
-{
-
-	struct td_obj *o1 = (struct td_obj *)p1;
-	struct td_obj *o2 = (struct td_obj *)p2;
-	char *pd1 = NULL, *pd2 = NULL;
-
-	if (o1->attrib != NULL)
-		nvlist_lookup_string(o1->attrib, TD_PART_ATTR_NAME, &pd1);
-	if (o2->attrib != NULL)
-		nvlist_lookup_string(o2->attrib, TD_PART_ATTR_NAME, &pd2);
-	if (pd1 == NULL && pd2 == NULL)
-		return (0);
-	if (pd1 == NULL)
-		return (-1);
-	if (pd2 == NULL)
-		return (1);
 	return (strcmp(pd1, pd2));
 }
 
@@ -2479,7 +2510,7 @@
 	if (pobjlist->issorted)
 		return;
 	qsort(pobjlist->objarr, pobjlist->objcnt,
-	    sizeof (struct td_obj), pobjlist->compare_routine);
+	    sizeof (struct td_obj), compare_td_objs);
 	pobjlist->issorted = B_TRUE;
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libtd/td_zpool.c	Thu Nov 04 10:08:30 2010 +0000
@@ -0,0 +1,1858 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * ZFS Pools discovery for Target Discovery module
+ */
+#include <unistd.h>
+#include <libnvpair.h>
+#include <strings.h>
+#include <td_lib.h>
+#include <td_api.h>
+#include <ls_api.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libdiskmgt.h>
+
+#include <td_dd.h>
+#include <td_zpool.h>
+
+/* Library wide variables */
+static libzfs_handle_t *g_zfs;
+static td_zpool_info_t *g_zpools_list = NULL;
+
+static void td_zpool_info_list_release(td_zpool_info_t *list);
+static ddm_handle_t *td_zpool_discover(int *nzpools);
+static td_zpool_target_t *td_zpool_target_allocate(zpool_handle_t *zhp,
+	nvlist_t *child, int *logcnt, int *errn, boolean_t do_logs,
+	boolean_t is_spare);
+static int td_zpool_iter_callback(zpool_handle_t *zhp, void *data);
+static void td_zpool_info_list_add(td_zpool_info_t **list,
+	td_zpool_info_t *new);
+static void td_zpool_target_free(td_zpool_target_t *zt);
+static void td_zpool_info_free(td_zpool_info_t *zi);
+static void td_zpool_allocate_target_nvlist(td_zpool_target_t **zts,
+	uint32_t num_nvtargets,
+	nvlist_t *nvtarget,
+	const char *attr_name,
+	const char *attr_num,
+	int *errn);
+static nvlist_t *td_zpool_get_attributes(td_zpool_info_t *zi, int *errn);
+static void td_zpool_target_print(td_zpool_target_t *zt,
+	int depth, boolean_t is_spare);
+static void td_zpool_info_print(td_zpool_info_t *zi, int num);
+static void td_zpool_info_print_ptrs(td_zpool_info_t **ptrs);
+static void td_zpool_info_print_list(td_zpool_info_t *start);
+static ddm_handle_t *td_zpool_info_ptrs_to_ddm_handle(td_zpool_info_t **ptrs);
+static td_zpool_info_t **td_zpool_info_get_ptrs(
+	td_zpool_info_t *zpools_list, int *nzpools);
+static void free_nvlist_array(nvlist_t **nv, int count);
+static nvlist_t *allocate_target_nvlist(td_zpool_target_t *zt, int *errn);
+static int td_zpool_import_find(libzfs_handle_t *zh,
+	td_zpool_info_t **zi_list);
+static int td_zpool_import_add(nvlist_t *pool,
+	td_zpool_info_t **zi_list);
+
+/*
+ * *******************************************************************
+ * Public Functions
+ * *******************************************************************
+ */
+
+/*
+ * Function:	ddm_free_zpool_list()
+ *
+ * Description:	Release zpool list for td_mg.c
+ *
+ * Parameters:
+ *		ddm_handle_t *dh : Handler list to free
+ *
+ * Returns:
+ *		Nothing
+ *
+ * Scope:
+ * 		Public
+ */
+void
+ddm_free_zpool_list(ddm_handle_t *dh)
+{
+	free(dh);
+	td_zpool_info_list_release(g_zpools_list);
+	g_zpools_list = NULL;
+}
+
+/*
+ * Function:	ddm_get_zpools()
+ *
+ * Description:	Get zpool list for td_mg.c
+ *
+ * Parameters:
+ *		int *nzpools	: Container for number of zpools
+ *
+ * Returns:
+ *		ddm_handle_t *	: Array of zpools handles discovered
+ *		int *nzpools	: Sets to number of zpools found
+ *
+ * Scope:
+ *		Public
+ */
+ddm_handle_t *
+ddm_get_zpools(int *nzpools)
+{
+	ddm_handle_t	*dh;
+
+	/* Initialize number of zpools found to 0 */
+	if (nzpools != NULL)
+		*nzpools = 0;
+
+	DDM_DEBUG(DDM_DBGLVL_NOTICE, "%s", "-> ddm_get_zpools()\n");
+
+	dh = (ddm_handle_t *)td_zpool_discover(nzpools);
+
+	if (dh == NULL) {
+		DDM_DEBUG(DDM_DBGLVL_ERROR, "%s",
+		    "Can't get zpool info\n");
+	}
+
+	return (dh);
+}
+
+/*
+ * Function:	ddm_get_zpool_attributes()
+ *
+ * Description:	Get attributes of Zpools.
+ *
+ * Parameters:
+ *		ddm_handle_t zpool : zpool handle to get attributes for
+ *
+ * Returns:
+ *		nvlist_t pointer containing attributes
+ *
+ * Scope:
+ *		Public
+ */
+nvlist_t *
+ddm_get_zpool_attributes(ddm_handle_t zpool)
+{
+	td_zpool_info_t *zi;
+	nvlist_t *attrs;
+	int errn = 0;
+
+	zi = (td_zpool_info_t *)(uintptr_t)zpool;
+
+	attrs = td_zpool_get_attributes(zi, &errn);
+	if (errn != 0) {
+		DDM_DEBUG(DDM_DBGLVL_ERROR, "ddm_get_zpool_attributes():"
+		    " Can't get attr. for Zpool, err=%d\n", errn);
+		return (NULL);
+	}
+
+	return (attrs);
+}
+
+/*
+ * ********************************************************************
+ * Private Functions
+ * ********************************************************************
+ */
+
+/*
+ * Function:	td_zpool_info_list_release()
+ *
+ * Description:	Release all internally allocated memory for
+ *      	already discovered zpools
+ *
+ * Parameters:
+ *		td_zpool_info_t *list : Internal linked list to be freed
+ *
+ * Returns:
+ *		void
+ *
+ * Scope:
+ *		Private
+ */
+static void
+td_zpool_info_list_release(td_zpool_info_t *list)
+{
+	td_zpool_info_t *cur;
+	td_zpool_info_t *tofree;
+
+	if (list == NULL) {
+		return;
+	}
+
+	for (cur = list; cur != NULL; ) {
+		tofree = cur;
+		cur = cur->next;
+		td_zpool_info_free(tofree);
+	}
+}
+
+/*
+ * Function:	td_zpool_discover()
+ *
+ * Description: Discover zpools in the system by using libzfs's
+ * 		zpool_iter() function. The attributes of the zpools are
+ * 		stored in the linked list of td_zpool_info_t.
+ *
+ * Parameters:
+ *		int *nzpools	: Return number of zpools found
+ *
+ * Returns:
+ *		ddm_handle_t *	: Array of ddm_handle_t for all pools
+ *		int *nzpools	: Number of zpools discovered
+ *
+ * Scope:
+ *		Private
+ */
+static ddm_handle_t *
+td_zpool_discover(int *nzpools)
+{
+	ddm_handle_t *df = NULL;
+	td_zpool_info_t **zpools_ptrs = NULL;
+
+	/* Initialize libzfs handle */
+	if ((g_zfs = libzfs_init()) == NULL) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_discover():"
+		    " failed to initialize ZFS library\n");
+		return (NULL);
+	}
+
+	if (g_zpools_list != NULL) {
+		td_zpool_info_list_release(g_zpools_list);
+		g_zpools_list = NULL;
+	}
+
+	if ((zpool_iter(g_zfs, td_zpool_iter_callback, &g_zpools_list)) != 0) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_discover()"
+		    " failed to iterate zpools\n");
+		libzfs_fini(g_zfs);
+		return (NULL);
+	}
+
+	if ((td_zpool_import_find(g_zfs, &g_zpools_list)) == 0) {
+		zpools_ptrs = td_zpool_info_get_ptrs(g_zpools_list, nzpools);
+		td_zpool_info_print_list(g_zpools_list);
+		td_zpool_info_print_ptrs(zpools_ptrs);
+		df = td_zpool_info_ptrs_to_ddm_handle(zpools_ptrs);
+		free(zpools_ptrs);
+	} else {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_discover()"
+		    " failed to iterate import candidates\n");
+		libzfs_fini(g_zfs);
+		return (NULL);
+	}
+
+	libzfs_fini(g_zfs);
+
+	return (df);
+}
+
+/*
+ * Function: td_zpool_target_allocate
+ *
+ * Description: Allocate a td_zpool_target_t for target if
+ *		this target has children recursively call this function
+ *		to allocate further targets.
+ *
+ * Parameters:
+ *		zpool_handle_t *zhp	: zpool handle
+ *		nvlist_t *child		: nvlist for this target
+ *		int *logcnt   		: Return log count
+ *		int  *errn  		: Return error number
+ *		boolean_t  do_logs	: Process logs or not
+ *		boolean_t  is_spare	: Is this a spare
+ *
+ * Returns:
+ *		td_zpool_target_t *	: Allocated td_zpool_target_t
+ *
+ * Scope:
+ * 		Private
+ */
+static td_zpool_target_t *
+td_zpool_target_allocate(zpool_handle_t *zhp,
+	nvlist_t *child,
+	int *logcnt,
+	int *errn,
+	boolean_t do_logs,
+	boolean_t is_spare)
+{
+	nvlist_t **nvchild;
+	td_zpool_target_t *top_target = NULL;
+	char *vname;
+	uint_t vsc;
+	vdev_stat_t *vs;
+	uint64_t islog = B_FALSE;
+	uint64_t ishole = B_FALSE;
+	int cnt1 = 0;
+
+	*errn = 0;
+
+	/*
+	 * Determine if this target device is a ZFS Log or a ZFS Hole.
+	 * If a ZFS Log device and we do not want to process logs then
+	 * return NULL.
+	 * ZFS Holes occur when you remove a slog after having add more
+	 * stripes, we always want to ignore these.
+	 */
+	(void) nvlist_lookup_uint64(child, ZPOOL_CONFIG_IS_LOG, &islog);
+	(void) nvlist_lookup_uint64(child, ZPOOL_CONFIG_IS_HOLE, &ishole);
+
+	if (ishole) {
+		return (NULL);
+	}
+
+	if (islog) {
+		*logcnt = *logcnt + 1;
+		if (!do_logs) {
+			return (NULL);
+		}
+	} else {
+		if (do_logs) {
+			return (NULL);
+		}
+	}
+
+	top_target = calloc(1, sizeof (td_zpool_target_t));
+	if (top_target == NULL) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_target_allocate():"
+		    " failed to allocate memory for top target,"
+		    " err=%d\n", ENOMEM);
+		*errn = ENOMEM;
+		return (NULL);
+	}
+
+	vname = zpool_vdev_name(g_zfs, zhp, child, B_TRUE);
+	if (vname == NULL) {
+		td_debug_print(LS_DBGLVL_WARN, "td_zpool_target_allocate():"
+		    " failed to get device name\n");
+		td_debug_print(LS_DBGLVL_WARN,
+		    "%d : %s\n", libzfs_errno(g_zfs),
+		    libzfs_error_description(g_zfs));
+		td_zpool_target_free(top_target);
+		*errn = -1;
+		return (NULL);
+	}
+	top_target->name = strdup(vname);
+
+	if (nvlist_lookup_uint64_array(child,
+	    ZPOOL_CONFIG_VDEV_STATS,
+	    (uint64_t **)&vs, &vsc) != 0) {
+		td_debug_print(LS_DBGLVL_WARN, "td_zpool_target_allocate():"
+		    " failed to get device stats\n");
+		top_target->health = strdup("UNKNOWN");
+		top_target->read_errors = 0;
+		top_target->write_errors = 0;
+		top_target->checksum_errors = 0;
+	} else {
+		if (is_spare) {
+			if (vs->vs_aux == VDEV_AUX_SPARED) {
+				top_target->health = strdup("INUSE");
+			} else if (vs->vs_state == VDEV_STATE_HEALTHY) {
+				top_target->health = strdup("AVAIL");
+			} else {
+				top_target->health = zpool_state_to_name(
+				    vs->vs_state, vs->vs_aux);
+			}
+			top_target->read_errors = 0;
+			top_target->write_errors = 0;
+			top_target->checksum_errors = 0;
+		} else {
+			top_target->health =
+			    zpool_state_to_name(vs->vs_state, vs->vs_aux);
+			top_target->read_errors = vs->vs_read_errors;
+			top_target->write_errors = vs->vs_write_errors;
+			top_target->checksum_errors = vs->vs_checksum_errors;
+		}
+	}
+
+	top_target->num_targets = 0;
+	(void) nvlist_lookup_nvlist_array(child,
+	    ZPOOL_CONFIG_CHILDREN,
+	    &nvchild, &top_target->num_targets);
+
+	if (top_target->num_targets > 0) {
+		top_target->targets =
+		    calloc(top_target->num_targets,
+		    sizeof (td_zpool_target_t *));
+		if (top_target->targets == NULL) {
+			td_debug_print(LS_DBGLVL_ERR,
+			    "td_zpool_target_allocate():"
+			    " failed to allocate memory for targets,"
+			    " err=%d\n", ENOMEM);
+			*errn = ENOMEM;
+			top_target->num_targets = 0;
+			td_zpool_target_free(top_target);
+			return (NULL);
+		}
+
+		for (cnt1 = 0; cnt1 < top_target->num_targets; cnt1++) {
+			top_target->targets[cnt1] =
+			    td_zpool_target_allocate(zhp, nvchild[cnt1],
+			    logcnt, errn, B_FALSE, is_spare);
+
+			if (*errn != 0) {
+				td_debug_print(LS_DBGLVL_ERR,
+				    "td_zpool_target_allocate():"
+				    " failed to allocate memory for targets,"
+				    " err=%d\n", *errn);
+				/* Free any previously allocated targets */
+				td_zpool_target_free(top_target);
+				return (NULL);
+			}
+		}
+	} else {
+		top_target->targets = NULL;
+	}
+
+	return (top_target);
+}
+
+
+/*
+ * Function: td_zpool_import_add
+ *
+ * Description: Found importable zpool, generate zpool_info_t structure
+ *		and add to linked list of discovered zpools.
+ *
+ * Parameters:
+ *		nvlist_t *pool          	: nvlist of importable pool
+ *		td_zpool_info_t *zi_list	: td_zpool_info_t linked list
+ *
+ * Returns:
+ *		0	: Success
+ *		1	: Failure
+ *
+ * Scope:
+ * 		Private
+ */
+static int
+td_zpool_import_add(nvlist_t *pool, td_zpool_info_t **zi_list)
+{
+	td_zpool_info_t *list = *zi_list;
+	int cnt1 = 0, logcnt = 0;
+	uint32_t targetcnt = 0;
+	nvlist_t **l2cache, **spares;
+	uint_t nl2cache, nspares;
+	char *msgid;
+	nvlist_t *nvroot, **child;
+	uint32_t num_children = 0;
+	uint_t vsc;
+	vdev_stat_t *vs;
+	td_zpool_info_t *zi = NULL;
+	char *name;
+	uint64_t guid;
+
+	/* name */
+	if (nvlist_lookup_string(pool, ZPOOL_CONFIG_POOL_NAME, &name) != 0) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_import_add():"
+		    " failed to get pool name.\n");
+		return (1);
+	}
+
+	/* GUID */
+	if (nvlist_lookup_uint64(pool, ZPOOL_CONFIG_POOL_GUID, &guid) != 0) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_import_add():"
+		    " failed to get pool GUID.\n");
+		return (1);
+	}
+
+	/* VDEV Tree */
+	if (nvlist_lookup_nvlist(pool,
+	    ZPOOL_CONFIG_VDEV_TREE, &nvroot) != 0) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_import_add():"
+		    " failed to get vdev tree\n");
+		return (1);
+	}
+
+	/* VDEV STATS */
+	if (nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS,
+	    (uint64_t **)&vs, &vsc) != 0) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_import_add():"
+		    " failed to get vdev stats\n");
+		return (1);
+	}
+
+	/* Construct td_zpool_info_t of attributes for this zpool */
+	zi = calloc(1, sizeof (td_zpool_info_t));
+	if (zi == NULL) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_import_add():"
+		    " failed to allocate memory for zpool,"
+		    " err=%d\n", ENOMEM);
+		return (1);
+	}
+
+	zi->attributes.name = strdup(name);
+	zi->attributes.health =
+	    zpool_state_to_name(vs->vs_state, vs->vs_aux);
+	zi->attributes.status = zpool_import_status(pool, &msgid);
+	zi->attributes.guid = guid;
+	zi->attributes.size = 0;
+	zi->attributes.capacity = 0;
+	zi->attributes.version = 0;
+	zi->attributes.bootfs = NULL;
+
+	/* Import or not */
+	/* As this zpool was reported via import search it is an import */
+	zi->attributes.import = B_TRUE;
+
+
+	if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
+	    &child, &num_children) != 0) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_import_add():"
+		    " failed to traverse vdev tree\n");
+		td_zpool_info_free(zi);
+		return (1);
+	}
+
+	zi->attributes.targets =
+	    calloc(num_children,
+	    sizeof (td_zpool_target_t *));
+
+	if (zi->attributes.targets == NULL) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_import_add():"
+		    " failed to allocate memory for toplevel targets,"
+		    " err=%d\n", ENOMEM);
+		td_zpool_info_free(zi);
+		return (1);
+	}
+
+	/* Process all normal non log/hole targets */
+	logcnt = 0;
+	targetcnt = 0;
+	for (cnt1 = 0; cnt1 < num_children; cnt1++) {
+		int errn = 0;
+		td_zpool_target_t *zt =
+		    td_zpool_target_allocate(NULL, child[cnt1],
+		    &logcnt, &errn, B_FALSE, B_FALSE);
+
+		if (errn != 0) {
+			/* Error already written via td_debug_print() */
+			/* So just return error from iter function */
+			td_zpool_info_free(zi);
+			return (1);
+		}
+
+		if (zt != NULL) {
+			zi->attributes.targets[targetcnt++] = zt;
+		}
+	}
+	zi->attributes.num_targets = targetcnt;
+
+	/* Process all logs and add them as targets */
+	if (logcnt > 0) {
+		zi->attributes.num_logs = logcnt;
+		zi->attributes.logs =
+		    calloc(zi->attributes.num_logs,
+		    sizeof (td_zpool_target_t *));
+		if (zi->attributes.logs == NULL) {
+			td_debug_print(LS_DBGLVL_ERR, "td_zpool_import_add():"
+			    " failed to allocate memory for zpool logs,"
+			    " err=%d\n", ENOMEM);
+			td_zpool_info_free(zi);
+			return (1);
+		}
+
+		targetcnt = 0;
+		for (cnt1 = 0; cnt1 < num_children; cnt1++) {
+			int errn = 0;
+			int dummycnt;
+			td_zpool_target_t *zt =
+			    td_zpool_target_allocate(NULL, child[cnt1],
+			    &dummycnt, &errn, B_TRUE, B_FALSE);
+
+			if (errn != 0) {
+				/* Error already written via td_debug_print() */
+				/* So just return error from iter function */
+				td_zpool_info_free(zi);
+				return (1);
+			}
+
+			if (zt != NULL) {
+				zi->attributes.logs[targetcnt++] = zt;
+			}
+		}
+	} else {
+		zi->attributes.num_logs = 0;
+		zi->attributes.logs = NULL;
+	}
+
+
+	/* Process All Cache and add them as targets */
+	if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
+	    &l2cache, &nl2cache) == 0) {
+		zi->attributes.num_l2cache = nl2cache;
+		zi->attributes.l2cache =
+		    calloc(zi->attributes.num_l2cache,
+		    sizeof (td_zpool_target_t *));
+		if (zi->attributes.l2cache == NULL) {
+			td_debug_print(LS_DBGLVL_ERR, "td_zpool_import_add():"
+			    " failed to allocate memory for zpool cache,"
+			    " err=%d\n", ENOMEM);
+			td_zpool_info_free(zi);
+			return (1);
+		}
+
+		targetcnt = 0;
+		for (cnt1 = 0; cnt1 < zi->attributes.num_l2cache; cnt1++) {
+			int errn = 0;
+			int dummycnt;
+			td_zpool_target_t *zt =
+			    td_zpool_target_allocate(NULL, l2cache[cnt1],
+			    &dummycnt, &errn, B_FALSE, B_FALSE);
+
+			if (errn != 0) {
+				/* Error already written via td_debug_print() */
+				/* So just return error from iter function */
+				td_zpool_info_free(zi);
+				return (1);
+			}
+
+			if (zt != NULL) {
+				zi->attributes.l2cache[targetcnt++] = zt;
+			}
+		}
+	} else {
+		zi->attributes.num_l2cache = 0;
+		zi->attributes.l2cache = NULL;
+	}
+
+	/* Process All spares and add them as targets */
+	if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
+	    &spares, &nspares) == 0) {
+		zi->attributes.num_spares = nspares;
+		zi->attributes.spares =
+		    calloc(zi->attributes.num_spares,
+		    sizeof (td_zpool_target_t *));
+		if (zi->attributes.l2cache == NULL) {
+			td_debug_print(LS_DBGLVL_ERR, "td_zpool_import_add():"
+			    " failed to allocate memory for zpool l2cache,"
+			    " err=%d\n", ENOMEM);
+			td_zpool_info_free(zi);
+			return (1);
+		}
+
+		targetcnt = 0;
+		for (cnt1 = 0; cnt1 < zi->attributes.num_spares; cnt1++) {
+			int errn = 0;
+			int dummycnt;
+			td_zpool_target_t *zt =
+			    td_zpool_target_allocate(NULL, spares[cnt1],
+			    &dummycnt, &errn, B_FALSE, B_TRUE);
+
+			if (errn != 0) {
+				/* Error already written via td_debug_print() */
+				/* So just return error from iter function */
+				td_zpool_info_free(zi);
+				return (1);
+			}
+
+			if (zt != NULL) {
+				zi->attributes.spares[targetcnt++] = zt;
+			}
+		}
+	} else {
+		zi->attributes.num_spares = 0;
+		zi->attributes.spares = NULL;
+	}
+
+	td_zpool_info_list_add(&list, zi);
+	if (*zi_list == NULL) {
+		*zi_list = list;
+	}
+	return (0);
+}
+
+/*
+ * Function: td_zpool_import_find
+ *
+ * Description: Find all importable zpools currently not imported.
+ *		Equivalent to calling CLI "zpool import" with no arguments.
+ *
+ * Parameters:
+ *		libzfs_handle_t *zh	: libzfs handle
+ *
+ * Returns:
+ *		0	: Success
+ *		1	: Fail
+ *
+ * Scope:
+ * 		Private
+ */
+static int
+td_zpool_import_find(libzfs_handle_t *zh, td_zpool_info_t **zi_list)
+{
+	char **searchdirs = NULL;
+	importargs_t idata = { 0 };
+	nvlist_t *nv_pools = NULL;
+	nvpair_t *elem;
+	nvlist_t *config;
+	uint64_t pool_state = -1ULL;
+
+	searchdirs = calloc(1, sizeof (char *));
+
+	if (searchdirs == NULL) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_import_find():"
+		    " failed to allocate memory for import search path,"
+		    " err=%d\n", ENOMEM);
+		return (1);
+	}
+
+	searchdirs[0] = "/dev/dsk";
+
+	idata.path = searchdirs;
+	idata.paths = 1;
+	idata.poolname = NULL;
+	idata.guid = 0;
+	idata.cachefile = NULL;
+
+	nv_pools = zpool_search_import(zh, &idata);
+
+	if (nv_pools != NULL) {
+		elem = NULL;
+		while ((elem = nvlist_next_nvpair(nv_pools, elem)) != NULL) {
+			if (nvpair_value_nvlist(elem, &config) != 0) {
+				td_debug_print(LS_DBGLVL_WARN,
+				    "td_zpool_import_find():"
+				    " nvpair_value_nvlist failed.\n");
+				printf("nvpair_valu_nvlist failed\n");
+				continue;
+			}
+			if (nvlist_lookup_uint64(config,
+			    ZPOOL_CONFIG_POOL_STATE, &pool_state) != 0) {
+				td_debug_print(LS_DBGLVL_WARN,
+				    "td_zpool_import_find():"
+				    " failed to get pool state.\n");
+				printf("pool state failed\n");
+				continue;
+			}
+
+			if (pool_state == POOL_STATE_DESTROYED) {
+				printf("pool is destroyed\n");
+				td_debug_print(LS_DBGLVL_INFO,
+				    "td_zpool_import_find():"
+				    " Skipping destroyed pool.\n");
+				continue;
+			}
+
+			if (td_zpool_import_add(config, zi_list) != 0) {
+				free(searchdirs);
+				return (1);
+			}
+		}
+	}
+
+	free(searchdirs);
+	return (0);
+}
+
+/*
+ * Function: td_zpool_iter_callback
+ *
+ * Description: Callback triggered by zpool_iter(), called for
+ *		each zpool discovered, pool is queried for various
+ *		properties and vdev structure, and results are stored
+ *		in internal linked list of td_zpool_info_t's.
+ *
+ * Parameters:
+ *		zpool_handle_t *zlp	: Current zpool handler
+ *		void *data      	: List of discovered zpools
+ *
+ * Returns:
+ *		0 : Success
+ *		1 : Fail
+ *
+ * Scope:
+ * 		Private
+ */
+static int
+td_zpool_iter_callback(zpool_handle_t *zhp, void *data)
+{
+	td_zpool_info_t **_list = data;
+	td_zpool_info_t *list = *_list;
+	char prop_buf[ZFS_MAXPROPLEN];
+	td_zpool_info_t *zi = NULL;
+	char *status;
+	nvlist_t *config, *nvroot, **child;
+	int cnt1 = 0, logcnt = 0, targetcnt = 0;
+	uint_t vsc;
+	vdev_stat_t *vs;
+	nvlist_t **l2cache, **spares;
+	uint_t nl2cache, nspares;
+	uint32_t num_children = 0;
+
+	/* Construct td_zpool_info_t of attributes for this zpool */
+	zi = calloc(1, sizeof (td_zpool_info_t));
+	if (zi == NULL) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_iter_callback():"
+		    " failed to allocate memory for zpool,"
+		    " err=%d\n", ENOMEM);
+		return (1);
+	}
+
+	/* name */
+	zi->attributes.name = strdup(zpool_get_name(zhp));
+	if (zi->attributes.name == NULL) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_iter_callback():"
+		    " failed to get pool name\n");
+		td_zpool_info_free(zi);
+		return (0);
+	}
+
+	/* Pool status */
+	zi->attributes.status = zpool_get_status(zhp, &status);
+
+	/* GUID */
+	zi->attributes.guid = zpool_get_prop_int(zhp, ZPOOL_PROP_GUID, NULL);
+
+	/* Health */
+	zpool_get_prop(zhp, ZPOOL_PROP_HEALTH, prop_buf, ZFS_MAXPROPLEN, NULL);
+	if (prop_buf == NULL) {
+		td_debug_print(LS_DBGLVL_WARN, "td_zpool_iter_callback():"
+		    " failed to get health property for pool: %s\n",
+		    zi->attributes.name);
+		zi->attributes.health = strdup("UNKNOWN");
+	} else {
+		zi->attributes.health = strdup(prop_buf);
+	}
+
+	/* Pool size */
+	zi->attributes.size =
+	    zpool_get_prop_int(zhp, ZPOOL_PROP_SIZE, NULL);
+
+	/* Pool capacity */
+	zi->attributes.capacity =
+	    zpool_get_prop_int(zhp, ZPOOL_PROP_CAPACITY, NULL);
+
+	/* Pool version */
+	zi->attributes.version =
+	    zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL);
+
+	/* Import or not */
+	/* As this zpool was reported via zpool_iter, not an import */
+	zi->attributes.import = B_FALSE;
+
+	/* Get vdev configuration for this pool */
+	if ((config = zpool_get_config(zhp, NULL)) == NULL) {
+		td_debug_print(LS_DBGLVL_WARN, "td_zpool_iter_callback():"
+		    " failed to get pool configuration for pool : %s\n",
+		    zi->attributes.name);
+		td_debug_print(LS_DBGLVL_WARN,
+		    "%d : %s\n", libzfs_errno(g_zfs),
+		    libzfs_error_description(g_zfs));
+		goto add_pool;
+	}
+
+	/* Boot Filesystem */
+	zpool_get_prop(zhp, ZPOOL_PROP_BOOTFS, prop_buf, ZFS_MAXPROPLEN, NULL);
+	if (prop_buf == NULL) {
+		td_debug_print(LS_DBGLVL_WARN, "td_zpool_iter_callback():"
+		    " failed to get boot filesystem property for pool: %s\n",
+		    zi->attributes.name);
+		zi->attributes.bootfs = NULL;
+	} else {
+		zi->attributes.bootfs = strdup(prop_buf);
+	}
+
+	if (nvlist_lookup_nvlist(config,
+	    ZPOOL_CONFIG_VDEV_TREE, &nvroot) != 0) {
+		td_debug_print(LS_DBGLVL_WARN, "td_zpool_iter_callback():"
+		    " failed to get vdev tree\n");
+		goto add_pool;
+	}
+
+	if (nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS,
+	    (uint64_t **)&vs, &vsc) != 0) {
+		td_debug_print(LS_DBGLVL_WARN, "td_zpool_iter_callback():"
+		    " failed to get vdev stats\n");
+		goto add_pool;
+	}
+
+	/* State reported via VDEV_STATS is more accurate, replace */
+	free(zi->attributes.health);
+	zi->attributes.health =
+	    zpool_state_to_name(vs->vs_state, vs->vs_aux);
+
+	if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
+	    &child, &num_children) != 0) {
+		td_debug_print(LS_DBGLVL_WARN, "td_zpool_iter_callback():"
+		    " failed to traverse vdev tree\n");
+		goto add_pool;
+	}
+
+	zi->attributes.targets =
+	    calloc(num_children,
+	    sizeof (td_zpool_target_t *));
+
+	if (zi->attributes.targets == NULL) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_iter_callback():"
+		    " failed to allocate memory for toplevel targets,"
+		    " err=%d\n", ENOMEM);
+		td_zpool_info_free(zi);
+		return (1);
+	}
+
+	/* Process all normal non log/hole targets */
+	logcnt = 0;
+	targetcnt = 0;
+	for (cnt1 = 0; cnt1 < num_children; cnt1++) {
+		int errn = 0;
+		td_zpool_target_t *zt =
+		    td_zpool_target_allocate(zhp, child[cnt1],
+		    &logcnt, &errn, B_FALSE, B_FALSE);
+
+		if (errn != 0) {
+			/* Error already written via td_debug_print() */
+			/* So just return error from iter function */
+			td_zpool_info_free(zi);
+			return (1);
+		}
+
+		if (zt != NULL) {
+			zi->attributes.targets[targetcnt++] = zt;
+		}
+	}
+	zi->attributes.num_targets = (uint32_t)targetcnt;
+
+	/* Process all logs and add them as targets */
+	if (logcnt > 0) {
+		zi->attributes.num_logs = logcnt;
+		zi->attributes.logs =
+		    calloc(zi->attributes.num_logs,
+		    sizeof (td_zpool_target_t *));
+		if (zi->attributes.l2cache == NULL) {
+			td_debug_print(LS_DBGLVL_ERR,
+			    "td_zpool_iter_callback():"
+			    " failed to allocate memory for zpool logs,"
+			    " err=%d\n", ENOMEM);
+			td_zpool_info_free(zi);
+			return (1);
+		}
+
+		targetcnt = 0;
+		for (cnt1 = 0; cnt1 < num_children; cnt1++) {
+			int errn = 0;
+			int dummycnt;
+			td_zpool_target_t *zt =
+			    td_zpool_target_allocate(zhp, child[cnt1],
+			    &dummycnt, &errn, B_TRUE, B_FALSE);
+
+			if (errn != 0) {
+				/* Error already written via td_debug_print() */
+				/* So just return error from iter function */
+				td_zpool_info_free(zi);
+				return (1);
+			}
+
+			if (zt != NULL) {
+				zi->attributes.logs[targetcnt++] = zt;
+			}
+		}
+	} else {
+		zi->attributes.num_logs = 0;
+		zi->attributes.logs = NULL;
+	}
+
+
+	/* Process All Cache and add them as targets */
+	if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
+	    &l2cache, &nl2cache) == 0) {
+		zi->attributes.num_l2cache = nl2cache;
+		zi->attributes.l2cache =
+		    calloc(zi->attributes.num_l2cache,
+		    sizeof (td_zpool_target_t *));
+		if (zi->attributes.l2cache == NULL) {
+			td_debug_print(LS_DBGLVL_ERR,
+			    "td_zpool_iter_callback():"
+			    " failed to allocate memory for zpool cache,"
+			    " err=%d\n", ENOMEM);
+			td_zpool_info_free(zi);
+			return (1);
+		}
+
+		targetcnt = 0;
+		for (cnt1 = 0; cnt1 < zi->attributes.num_l2cache; cnt1++) {
+			int errn = 0;
+			int dummycnt;
+			td_zpool_target_t *zt =
+			    td_zpool_target_allocate(zhp, l2cache[cnt1],
+			    &dummycnt, &errn, B_FALSE, B_FALSE);
+
+			if (errn != 0) {
+				/* Error already written via td_debug_print() */
+				/* So just return error from iter function */
+				td_zpool_info_free(zi);
+				return (1);
+			}
+
+			if (zt != NULL) {
+				zi->attributes.l2cache[targetcnt++] = zt;
+			}
+		}
+	} else {
+		zi->attributes.num_l2cache = 0;
+		zi->attributes.l2cache = NULL;
+	}
+
+	/* Process All spares and add them as targets */
+	if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
+	    &spares, &nspares) == 0) {
+		zi->attributes.num_spares = nspares;
+		zi->attributes.spares =
+		    calloc(zi->attributes.num_spares,
+		    sizeof (td_zpool_target_t *));
+		if (zi->attributes.l2cache == NULL) {
+			td_debug_print(LS_DBGLVL_ERR,
+			    "td_zpool_iter_callback():"
+			    " failed to allocate memory for zpool spares,"
+			    " err=%d\n", ENOMEM);
+			td_zpool_info_free(zi);
+			return (1);
+		}
+
+		targetcnt = 0;
+		for (cnt1 = 0; cnt1 < zi->attributes.num_spares; cnt1++) {
+			int errn = 0;
+			int dummycnt;
+			td_zpool_target_t *zt =
+			    td_zpool_target_allocate(zhp, spares[cnt1],
+			    &dummycnt, &errn, B_FALSE, B_TRUE);
+
+			if (errn != 0) {
+				/* Error already written via td_debug_print() */
+				/* So just return error from iter function */
+				td_zpool_info_free(zi);
+				return (1);
+			}
+
+			if (zt != NULL) {
+				zi->attributes.spares[targetcnt++] = zt;
+			}
+		}
+	} else {
+		zi->attributes.num_spares = 0;
+		zi->attributes.spares = NULL;
+	}
+
+add_pool:
+	td_zpool_info_list_add(&list, zi);
+	if (*_list == NULL) {
+		*_list = list;
+	}
+	zpool_close(zhp);
+	return (0);
+}
+
+/*
+ * Function: td_zpool_info_list_add
+ *
+ * Description: Adds a new td_zpool_info to global linked list of discovered
+ *		zpools.
+ *
+ * Parameters:
+ *		td_zpool_info_t **list	: List to add new item to
+ *		td_zpool_info_t *new	: New zpool to add to linked list
+ *
+ * Returns:
+ *		void
+ *
+ * Scope:
+ * 		Private
+ */
+static void
+td_zpool_info_list_add(td_zpool_info_t **list, td_zpool_info_t *new)
+{
+	td_zpool_info_t *cur;
+
+	if (new == NULL)
+		return;
+
+	if (*list == NULL) {
+		/* First item on list */
+		*list = new;
+	} else {
+		/* Traverse to end of list and add item */
+		for (cur = *list; cur->next != NULL; cur = cur->next)
+			;
+
+		/* Add new td_zpool_info to end of list */
+		cur->next = new;
+	}
+}
+
+
+/*
+ * Function: td_zpool_target_free
+ *
+ * Description: Frees memory allocated for td_zpool_target_t
+ *
+ * Parameters:
+ *		td_zpool_target_t *zt : td_zpool_target_t to be freed
+ *
+ * Returns:
+ *		void
+ *
+ * Scope:
+ * 		Private
+ */
+static void
+td_zpool_target_free(td_zpool_target_t *zt)
+{
+	int i = 0;
+
+	if (zt == NULL) {
+		return;
+	}
+
+	for (i = 0; i < zt->num_targets; i++) {
+		td_zpool_target_free(zt->targets[i]);
+	}
+	free(zt->targets);
+	free(zt->name);
+	free(zt->health);
+	free(zt);
+}
+
+/*
+ * Function: td_zpool_info_free
+ *
+ * Description: Frees memory allocated for td_zpool_info
+ *
+ * Parameters:
+ *		td_zpool_info_t *zi : td_zpool_info_t to be freed
+ *
+ * Returns:
+ *		void
+ *
+ * Scope:
+ * 		Private
+ */
+static void
+td_zpool_info_free(td_zpool_info_t *zi)
+{
+	int i = 0;
+
+	if (zi == NULL) {
+		return;
+	}
+
+	/* Free all targets */
+	for (i = 0; i < zi->attributes.num_targets; i++) {
+		td_zpool_target_free(zi->attributes.targets[i]);
+	}
+	free(zi->attributes.targets);
+
+	/* Free all logs */
+	for (i = 0; i < zi->attributes.num_logs; i++) {
+		td_zpool_target_free(zi->attributes.logs[i]);
+	}
+	free(zi->attributes.logs);
+
+	/* Free all l2cache */
+	for (i = 0; i < zi->attributes.num_l2cache; i++) {
+		td_zpool_target_free(zi->attributes.l2cache[i]);
+	}
+	free(zi->attributes.l2cache);
+
+	/* Free all spares */
+	for (i = 0; i < zi->attributes.num_spares; i++) {
+		td_zpool_target_free(zi->attributes.spares[i]);
+	}
+	free(zi->attributes.spares);
+
+	free(zi->attributes.name);
+	free(zi->attributes.health);
+	free(zi->attributes.bootfs);
+	free(zi);
+}
+/*
+ * Function: free_nvlist_array
+ *
+ * Description: Frees all elements of an nvlist array and the
+ *		main array pointer itself.
+ *
+ * Parameters:
+ *		nvlist **nv : nvlist_t array to free
+ *
+ * Returns:
+ *		void
+ *
+ * Scope:
+ * 		Private
+ */
+static void
+free_nvlist_array(nvlist_t **nv, int count)
+{
+	int i = 0;
+
+	if (nv == NULL) {
+		return;
+	}
+
+	for (i = 0; i < count; i++) {
+		nvlist_free(nv[i]);
+	}
+	free(nv);
+}
+
+/*
+ * Function: td_zpool_allocate_target_nvlist
+ *
+ * Description: Allocates nvlist for this target.
+ *		potentially called recursively to allocate targets
+ *		of this target.
+ *
+ * Parameters:
+ *		td_zpool_target_t **zts	: targets array
+ *      	uint32_t num_nvtargets	: Number of targets
+ *		nvlist_t *nvtarget  	: top level nvlist
+ *      	const char *attr_name	: NV attribute name
+ *      	const char *attr_num	: NV attribute number
+ *		int *errn           	: Error number to return
+ *
+ * Returns:
+ *		errn      		: sets to error value;
+ *
+ * Scope:
+ * 		Private
+ */
+static void
+td_zpool_allocate_target_nvlist(td_zpool_target_t **zts,
+	uint32_t num_nvtargets,
+	nvlist_t *nvtarget,
+	const char *attr_name,
+	const char *attr_num,
+	int *errn)
+{
+	int cnt1 = 0;
+	uint32_t num_targets = 0;
+	nvlist_t **targets;
+	nvlist_t *tmptarget = NULL;
+
+	if (num_nvtargets == 0) {
+		return;
+	}
+
+	/* Generate nvlist array of each target grouping */
+	targets = calloc(num_nvtargets, sizeof (nvlist_t *));
+	if (targets == NULL) {
+		td_debug_print(LS_DBGLVL_ERR,
+		    "td_zpool_allocate_target_nvlist():"
+		    " Failed to allocate target nvlist.\n");
+		*errn = ENOMEM;
+		return;
+	}
+
+	num_targets = 0;
+	for (cnt1 = 0; cnt1 < num_nvtargets; cnt1++) {
+		tmptarget = allocate_target_nvlist(zts[cnt1], errn);
+
+		if (*errn != 0) {
+			free_nvlist_array(targets, cnt1);
+			return;
+		}
+		targets[num_targets++] = tmptarget;
+	}
+
+	/* Add TD_ZPOOL_ATTR_NUM_TARGETS attribute */
+	if (nvlist_add_uint32(nvtarget,
+	    attr_num, num_targets) != 0) {
+		td_debug_print(LS_DBGLVL_ERR,
+		    "td_zpool_allocate_target_nvlist():"
+		    " Failed to add number of targets to target nvlist.\n");
+		free_nvlist_array(targets, cnt1);
+		*errn = ENOMEM;
+		return;
+	}
+
+	/* Add TD_ZPOOL_ATTR_TARGETS attribute */
+	if (nvlist_add_nvlist_array(nvtarget,
+	    attr_name, targets,
+	    num_targets) != 0) {
+		td_debug_print(LS_DBGLVL_ERR,
+		    "td_zpool_allocate_target_nvlist():"
+		    " Failed to add targets to target nvlist.\n");
+		free_nvlist_array(targets, cnt1);
+		*errn = ENOMEM;
+		return;
+	}
+
+	/* Free up targets nvlists */
+	free_nvlist_array(targets, num_targets);
+}
+
+/*
+ * Function: allocate_target_nvlist
+ *
+ * Description: Allocates nvlist for this target.
+ *		potentially called recursively to allocate targets
+ *		of this target.
+ *
+ * Parameters:
+ *		td_zpool_target_t *zt	: td_zpool_target_t to allocate
+ *		int *errn           	: Error number to return
+ *
+ * Returns:
+ *		nvlist_t *      	: nvpair list of attributes
+ *		errn            	: sets to error value;
+ *
+ * Scope:
+ * 		Private
+ */
+static nvlist_t *
+allocate_target_nvlist(td_zpool_target_t *zt, int *errn)
+{
+	nvlist_t *nvtarget = NULL;
+
+	if (zt == NULL) {
+		return (NULL);
+	}
+
+	/* Allocate NV_LIST for this target */
+	if (nvlist_alloc(&nvtarget, NV_UNIQUE_NAME, 0) != 0) {
+		td_debug_print(LS_DBGLVL_ERR, "allocate_target_nvlist():"
+		    " Failed to allocate target nvlist.\n");
+		*errn = ENOMEM;
+		return (NULL);
+	}
+
+	/* For each target build up nvlist */
+	/* Add TD_ZPOOL_ATTR_TARGET_NAME attribute */
+	if (nvlist_add_string(nvtarget,
+	    TD_ZPOOL_ATTR_TARGET_NAME, zt->name) != 0) {
+		td_debug_print(LS_DBGLVL_ERR, "allocate_target_nvlist():"
+		    " Failed to add target name to nvlist.\n");
+		nvlist_free(nvtarget);
+		*errn = ENOMEM;
+		return (NULL);
+	}
+
+	/* Add TD_ZPOOL_ATTR_TARGET_HEALTH attribute */
+	if (nvlist_add_string(nvtarget,
+	    TD_ZPOOL_ATTR_TARGET_HEALTH, zt->health) != 0) {
+		td_debug_print(LS_DBGLVL_ERR, "allocate_target_nvlist():"
+		    " Failed to add target health to nvlist.\n");
+		nvlist_free(nvtarget);
+		*errn = ENOMEM;
+		return (NULL);
+	}
+
+	/* Add TD_ZPOOL_ATTR_TARGET_READ_ERRORS attribute */
+	if (nvlist_add_uint64(nvtarget,
+	    TD_ZPOOL_ATTR_TARGET_READ_ERRORS, zt->read_errors) != 0) {
+		td_debug_print(LS_DBGLVL_ERR, "allocate_target_nvlist():"
+		    " Failed to add target read errors to nvlist.\n");
+		nvlist_free(nvtarget);
+		*errn = ENOMEM;
+		return (NULL);
+	}
+
+	/* Add TD_ZPOOL_ATTR_TARGET_WRITE_ERRORS attribute */
+	if (nvlist_add_uint64(nvtarget,
+	    TD_ZPOOL_ATTR_TARGET_WRITE_ERRORS, zt->write_errors) != 0) {
+		td_debug_print(LS_DBGLVL_ERR, "allocate_target_nvlist():"
+		    " Failed to add target write errors to nvlist.\n");
+		nvlist_free(nvtarget);
+		*errn = ENOMEM;
+		return (NULL);
+	}
+
+	/* Add TD_ZPOOL_ATTR_TARGET_CHECKSUM_ERRORS attribute */
+	if (nvlist_add_uint64(nvtarget,
+	    TD_ZPOOL_ATTR_TARGET_CHECKSUM_ERRORS,
+	    zt->checksum_errors) != 0) {
+		td_debug_print(LS_DBGLVL_ERR, "allocate_target_nvlist():"
+		    " Failed to add target checksum errors to nvlist.\n");
+		nvlist_free(nvtarget);
+		*errn = ENOMEM;
+		return (NULL);
+	}
+
+	td_zpool_allocate_target_nvlist(zt->targets,
+	    zt->num_targets, nvtarget, TD_ZPOOL_ATTR_TARGETS,
+	    TD_ZPOOL_ATTR_NUM_TARGETS, errn);
+	if (*errn != 0) {
+		td_debug_print(LS_DBGLVL_ERR, "allocate_target_nvlist():"
+		    " Failed to add targets to nvlist.\n");
+		nvlist_free(nvtarget);
+		return (NULL);
+	}
+
+	return (nvtarget);
+}
+
+/*
+ * Function: td_zpool_get_attributes
+ *
+ * Description: Gets the attributes of a specific Zpool.
+ *		nvlist_t is populated with all the attributes for this
+ *		td_zpool_info_t and returned.
+ *
+ * Parameters:
+ *		td_zpool_info_t *zi	: zpool to get attributes for
+ *		int *errn       	: Error number to return
+ *
+ * Returns:
+ *		nvlist_t *      	: nvpair list of attributes for zpool
+ *
+ * Scope:
+ * 		Private
+ */
+static nvlist_t *
+td_zpool_get_attributes(td_zpool_info_t *zi, int *errn)
+{
+	nvlist_t *attrs = NULL;
+
+	if (zi == NULL) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_get_attributes():"
+		    " zpool handle not set.\n");
+		*errn = ENODEV;
+		return (NULL);
+	}
+
+	/* Allocate NV_LIST */
+	if (nvlist_alloc(&attrs, NV_UNIQUE_NAME, 0) != 0) {
+		*errn = ENOMEM;
+		return (NULL);
+	}
+
+	/* Add TD_ZPOOL_ATTR_NAME attribute */
+	if (nvlist_add_string(attrs, TD_ZPOOL_ATTR_NAME,
+	    zi->attributes.name) != 0) {
+		*errn = ENOMEM;
+		nvlist_free(attrs);
+		return (NULL);
+	}
+
+	/* Add TD_ZPOOL_ATTR_HEALTH attribute */
+	if (nvlist_add_string(attrs, TD_ZPOOL_ATTR_HEALTH,
+	    zi->attributes.health) != 0) {
+		*errn = ENOMEM;
+		nvlist_free(attrs);
+		return (NULL);
+	}
+
+	/* Add TD_ZPOOL_ATTR_STATUS attribute */
+	if (nvlist_add_uint32(attrs, TD_ZPOOL_ATTR_STATUS,
+	    zi->attributes.status) != 0) {
+		*errn = ENOMEM;
+		nvlist_free(attrs);
+		return (NULL);
+	}
+
+	/* Add TD_ZPOOL_ATTR_GUID attribute */
+	if (nvlist_add_uint64(attrs, TD_ZPOOL_ATTR_GUID,
+	    zi->attributes.guid) != 0) {
+		*errn = ENOMEM;
+		nvlist_free(attrs);
+		return (NULL);
+	}
+
+	/* Add TD_ZPOOL_ATTR_SIZE attribute */
+	if (nvlist_add_uint64(attrs, TD_ZPOOL_ATTR_SIZE,
+	    zi->attributes.size) != 0) {
+		*errn = ENOMEM;
+		nvlist_free(attrs);
+		return (NULL);
+	}
+
+	/* Add TD_ZPOOL_ATTR_CAPACITY attribute */
+	if (nvlist_add_uint64(attrs, TD_ZPOOL_ATTR_CAPACITY,
+	    zi->attributes.capacity) != 0) {
+		*errn = ENOMEM;
+		nvlist_free(attrs);
+		return (NULL);
+	}
+
+	/* Add TD_ZPOOL_ATTR_VERSION attribute */
+	if (nvlist_add_uint32(attrs, TD_ZPOOL_ATTR_VERSION,
+	    zi->attributes.version) != 0) {
+		*errn = ENOMEM;
+		nvlist_free(attrs);
+		return (NULL);
+	}
+
+	/* Add TD_ZPOOL_ATTR_BOOTFS attribute */
+	if (zi->attributes.bootfs != NULL) {
+		if (nvlist_add_string(attrs, TD_ZPOOL_ATTR_BOOTFS,
+		    zi->attributes.bootfs) != 0) {
+			*errn = ENOMEM;
+			nvlist_free(attrs);
+			return (NULL);
+		}
+	}
+
+	/* Add TD_ZPOOL_ATTR_IMPORT attribute */
+	if (nvlist_add_boolean_value(attrs, TD_ZPOOL_ATTR_IMPORT,
+	    zi->attributes.import) != 0) {
+		*errn = ENOMEM;
+		nvlist_free(attrs);
+		return (NULL);
+	}
+
+	/* Generate nvlist array of each target grouping */
+	td_zpool_allocate_target_nvlist(zi->attributes.targets,
+	    zi->attributes.num_targets, attrs, TD_ZPOOL_ATTR_TARGETS,
+	    TD_ZPOOL_ATTR_NUM_TARGETS, errn);
+	if (*errn != 0) {
+		nvlist_free(attrs);
+		return (NULL);
+	}
+
+	/* Process logs */
+	td_zpool_allocate_target_nvlist(zi->attributes.logs,
+	    zi->attributes.num_logs, attrs, TD_ZPOOL_ATTR_LOGS,
+	    TD_ZPOOL_ATTR_NUM_LOGS, errn);
+	if (*errn != 0) {
+		nvlist_free(attrs);
+		return (NULL);
+	}
+
+
+	/* Process l2cache */
+	td_zpool_allocate_target_nvlist(zi->attributes.l2cache,
+	    zi->attributes.num_l2cache, attrs, TD_ZPOOL_ATTR_L2CACHE,
+	    TD_ZPOOL_ATTR_NUM_L2CACHE,
+	    errn);
+	if (*errn != 0) {
+		nvlist_free(attrs);
+		return (NULL);
+	}
+
+	/* Process spares */
+	td_zpool_allocate_target_nvlist(zi->attributes.spares,
+	    zi->attributes.num_spares, attrs, TD_ZPOOL_ATTR_SPARES,
+	    TD_ZPOOL_ATTR_NUM_SPARES,
+	    errn);
+	if (*errn != 0) {
+		nvlist_free(attrs);
+		return (NULL);
+	}
+
+	return (attrs);
+}
+
+/*
+ * Function: td_zpool_target_print
+ *
+ * Description: Print contents of td_zpool_target_t
+ *
+ * Parameters:
+ *		td_zpool_target_t *zt	: zpool_target to print
+ *		int depth           	: depth nesting to print
+ *		boolean_t is_spare  	: Is spare device
+ *
+ * Returns:
+ *		void
+ *
+ * Scope:
+ * 		Private
+ */
+static void
+td_zpool_target_print(td_zpool_target_t *zt,
+	int depth,
+	boolean_t is_spare)
+{
+	int i = 0;
+
+	if (zt == NULL) {
+		return;
+	}
+
+	if (is_spare) {
+		td_debug_print(LS_DBGLVL_INFO,
+		    "     |   %*s%-*s| "
+		    "%9s|         | %4s| %5s| %3s|\n",
+		    depth, "", 31 - depth,
+		    zt->name, zt->health, "", "", "");
+	} else {
+		td_debug_print(LS_DBGLVL_INFO,
+		    "     |   %*s%-*s| "
+		    "%9s|         | %4llu| %5llu| %3llu|\n",
+		    depth, "", 31 - depth,
+		    zt->name, zt->health,
+		    zt->read_errors, zt->write_errors,
+		    zt->checksum_errors);
+	}
+
+	for (i = 0; i < zt->num_targets; i++) {
+		td_zpool_target_print(zt->targets[i], depth+2, is_spare);
+	}
+}
+
+/*
+ * Function: td_zpool_info_print
+ *
+ * Description: Print contents of td_zpool_info_t
+ *
+ * Parameters:
+ *		td_zpool_info_t *zi	: zpool to print
+ *		int num 		: zpool count printed
+ *
+ * Returns:
+ *		void
+ *
+ * Scope:
+ * 		Private
+ */
+static void
+td_zpool_info_print(td_zpool_info_t *zi, int num)
+{
+	int i = 0;
+	double size_mb = 0;
+	double size_gb = 0;
+
+	size_mb = BYTES_TO_MB(zi->attributes.size);
+	if (size_mb > MB_IN_GB) {
+		size_gb = MB_TO_GB(size_mb);
+		size_mb = 0;
+	}
+
+	td_debug_print(LS_DBGLVL_INFO, " %3d | %-33s| "
+	    "%9s| %7.2lf%c| %4llu| %5d|  %2d|\n", num, zi->attributes.name,
+	    zi->attributes.health,
+	    size_mb > 0 ? size_mb : size_gb,
+	    size_mb > 0 ? 'M' : 'G',
+	    zi->attributes.capacity,
+	    zi->attributes.status,
+	    zi->attributes.version);
+
+	td_debug_print(LS_DBGLVL_INFO, " %3s | %33llu| "
+	    "%9s| %8s| %4s| %5s|  %2s|\n", "",
+	    zi->attributes.guid, "", "", "", "", "", "");
+
+	if (zi->attributes.bootfs != NULL) {
+		td_debug_print(LS_DBGLVL_INFO, " %3s | %33s| "
+		    "%9s| %8s| %4s| %5s|  %2s|\n", "",
+		    zi->attributes.bootfs, "", "", "", "", "", "");
+	}
+
+	if (zi->attributes.import) {
+		td_debug_print(LS_DBGLVL_INFO, " %3s | %33s| "
+		    "%9s| %8s| %4s| %5s|  %2s|\n", "",
+		    "Importable pool", "", "", "", "", "", "");
+	}
+
+	for (i = 0; i < zi->attributes.num_targets; i++) {
+		td_zpool_target_print(zi->attributes.targets[i], 0, B_FALSE);
+	}
+
+	if (zi->attributes.num_logs > 0) {
+		td_debug_print(LS_DBGLVL_INFO, " %3s | %-33s| "
+		    "%9s| %8s| %4s| %5s|  %2s|\n", "", "logs",
+		    "", "", "", "", "");
+		for (i = 0; i < zi->attributes.num_logs; i++) {
+			td_zpool_target_print(zi->attributes.logs[i],
+			    0, B_FALSE);
+		}
+	}
+
+	if (zi->attributes.num_l2cache > 0) {
+		td_debug_print(LS_DBGLVL_INFO, " %3s | %-33s| "
+		    "%9s| %8s| %4s| %5s|  %2s|\n", "", "cache",
+		    "", "", "", "", "");
+		for (i = 0; i < zi->attributes.num_l2cache; i++) {
+			td_zpool_target_print(zi->attributes.l2cache[i],
+			    0, B_FALSE);
+		}
+	}
+
+	if (zi->attributes.num_spares > 0) {
+		td_debug_print(LS_DBGLVL_INFO, " %3s | %-33s| "
+		    "%9s| %8s| %4s| %5s|  %2s|\n", "", "spares",
+		    "", "", "", "", "");
+		for (i = 0; i < zi->attributes.num_spares; i++) {
+			td_zpool_target_print(zi->attributes.spares[i],
+			    0, B_TRUE);
+		}
+	}
+}
+
+/*
+ * Function: td_zpool_info_print_ptrs
+ *
+ * Description:
+ *		Print all td_zpool_info_t's in ptr array
+ *
+ * Parameters:
+ *		td_zpool_info_t **ptrs : Array of pointers to print from
+ *
+ * Returns:
+ *		void
+ *
+ * Scope:
+ * 		Private
+ */
+static void
+td_zpool_info_print_ptrs(td_zpool_info_t **ptrs)
+{
+	int i = 0;
+	int num = 0;
+
+	if (ptrs == NULL || ptrs[0] == NULL) {
+		td_debug_print(LS_DBGLVL_INFO,
+		    "zpool ptrs array is empty.\n");
+		return;
+	}
+
+	for (i = 0; ptrs[i]; i++) {
+		td_zpool_info_print(ptrs[i], ++num);
+	}
+}
+
+/*
+ * Function: td_zpool_info_print_list
+ *
+ * Description: Runs through the linked list of zpool attributes printing all
+ *		the stored information.
+ *
+ * Parameters:
+ *		td_zpool_info_t start : Linked list of zpools to print
+ *
+ * Returns:
+ *		NULL
+ *
+ * Scope:
+ * 		Private
+ */
+static void
+td_zpool_info_print_list(td_zpool_info_t *start)
+{
+	int num = 0;
+	td_zpool_info_t *cur = start;
+
+	if (cur == NULL) {
+		td_debug_print(LS_DBGLVL_INFO,
+		    "zpool list is empty.\n");
+		return;
+	}
+
+	while (cur != NULL) {
+		td_zpool_info_print(cur, ++num);
+		cur = cur->next;
+	}
+}
+
+/*
+ * Function: td_zpool_info_ptrs_to_ddm_handle
+ *
+ * Description: Convert array of td_zpool_info_t ptrs to an
+ *      	array of ddm_handle_t's for returning to TD
+ *
+ * Parameters:
+ *      	td_zpool_info_t **ptrs	: Array of td_zpool_info_t pointers
+ *
+ * Returns:
+ *      	ddm_handle_t *  	: Array of ddm_handle_t pointers
+ *
+ * Scope:
+ *      	Private
+ */
+static ddm_handle_t *
+td_zpool_info_ptrs_to_ddm_handle(td_zpool_info_t **ptrs)
+{
+#ifdef _LP64
+	return ((ddm_handle_t *)ptrs);
+#else
+	/* convert the 32 bit ptrs to the 64 bit descriptors */
+	int	cnt;
+	int	i;
+	ddm_handle_t *dh;
+
+	if (ptrs == NULL) {
+		td_debug_print(LS_DBGLVL_WARN,
+		    "td_zpool_info_ptrs_to_ddm_handle():"
+		    " zpool ptr array NULL cannot convert to ddm_handle_t\n");
+		return (NULL);
+	}
+
+	for (cnt = 0; ptrs[cnt]; cnt++)
+		;
+
+	dh = (ddm_handle_t *)calloc(cnt + 1, sizeof (ddm_handle_t));
+	if (dh == NULL) {
+		td_debug_print(LS_DBGLVL_ERR,
+		    "td_zpool_info_ptrs_to_ddm_handle():"
+		    " Failed to allocate memory for ddm_handle_t array\n");
+		return (NULL);
+	}
+
+	for (i = 0; ptrs[i]; i++) {
+		dh[i] = (uintptr_t)ptrs[i];
+	}
+
+	return (dh);
+#endif
+}
+
+/*
+ * Function: td_zpool_info_get_ptrs
+ *
+ * Description: Take a linked list of zpools and generate an array
+ *      	of pointers for each element in the list.
+ *
+ * Parameters:
+ *      	td_zpool_info_t *	: Linked list of zpools
+ *      	int *nzpools     	: Num Zpools discovered
+ *
+ * Returns:
+ *      	td_zpool_info_t **	: Array of td_zpool_info_t pointers
+ *      	int *nzpools      	: Number of zpools discovered
+ *
+ * Scope:
+ *      	Private
+ */
+static td_zpool_info_t **
+td_zpool_info_get_ptrs(td_zpool_info_t *zpools_list, int *nzpools)
+{
+	td_zpool_info_t **ptrs;
+	td_zpool_info_t *cur;
+	int pos = 0;
+	int zpoolcnt = 0;
+
+	for (zpoolcnt = 0, cur = zpools_list;
+	    cur != NULL;
+	    cur = cur->next)
+		zpoolcnt++;
+
+	/* Set return paramater for number of zpools found */
+	if (nzpools != NULL)
+		*nzpools = zpoolcnt;
+
+	ptrs = (td_zpool_info_t **)
+	    calloc(zpoolcnt + 1, sizeof (td_zpool_info_t *));
+	if (ptrs == NULL) {
+		td_debug_print(LS_DBGLVL_ERR, "td_zpool_info_get_ptrs():"
+		    " Failed to allocate memory for zpool ptr array\n");
+		return (NULL);
+	}
+
+	for (pos = 0, cur = zpools_list; cur != NULL; cur = cur->next) {
+		ptrs[pos++] = cur;
+	}
+
+	return (ptrs);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libtd/td_zpool.h	Thu Nov 04 10:08:30 2010 +0000
@@ -0,0 +1,107 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _TD_ZPOOL_H
+#define	_TD_ZPOOL_H
+
+/*
+ * Module:	td_zpool.h
+ * Group:
+ * Description:	This module contains the Target Discovery ZFS Pool
+ *		module data structures, constants, and function
+ *              prototypes.
+ */
+
+#include <ctype.h>
+#include <libzfs.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/nvpair.h>
+
+#include "td_api.h"
+#include "td_dd.h"
+#include "ls_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* type definition / macros */
+#define	MB_IN_GB	1024
+#define	BYTES_TO_MB(size)	((size) / (MB_IN_GB * MB_IN_GB))
+#define	ONE_DECIMAL(val)	((round((val) * 10)) / 10.0)
+#define	MB_TO_GB(size_mb)	ONE_DECIMAL((size_mb) / MB_IN_GB)
+
+/* zpool targets */
+typedef struct td_zpool_target {
+	char *name;
+	char *health;
+	uint64_t read_errors;
+	uint64_t write_errors;
+	uint64_t checksum_errors;
+	uint32_t num_targets;
+	struct td_zpool_target **targets;
+} td_zpool_target_t;
+
+/*
+ * Structure for zpool attributes.
+ * Used for zpool_iter callback data.
+ */
+typedef struct td_zpool_attributes {
+	char *name;
+	char *health;
+	zpool_status_t status;
+	uint64_t guid;
+	uint64_t size; 	/* Size in Bytes */
+	uint64_t capacity;
+	uint32_t version;
+	boolean_t import;
+	char *bootfs;
+	uint32_t num_targets;
+	td_zpool_target_t **targets;
+	uint32_t num_logs;
+	td_zpool_target_t **logs;
+	uint32_t num_spares;
+	td_zpool_target_t **spares;
+	uint32_t num_l2cache;
+	td_zpool_target_t **l2cache;
+} td_zpool_attributes_t;
+
+/* Linked list of zpool attributes */
+typedef struct td_zpool_info {
+	td_zpool_attributes_t attributes;
+	struct td_zpool_info *next;
+} td_zpool_info_t;
+
+/* function prototypes */
+extern ddm_handle_t	*ddm_get_zpools(int *nzpools);
+extern void ddm_free_zpool_list(ddm_handle_t *dh);
+extern nvlist_t *ddm_get_zpool_attributes(ddm_handle_t zpool);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TD_ZPOOL_H */
--- a/usr/src/lib/libtd/test_td.c	Tue Nov 02 18:25:00 2010 +0100
+++ b/usr/src/lib/libtd/test_td.c	Thu Nov 04 10:08:30 2010 +0000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <assert.h>
@@ -41,6 +40,7 @@
 #include <sys/nvpair.h>
 #include <td_dd.h>
 #include <td_api.h>
+#include <td_zpool.h>
 
 #include <ls_api.h>
 
@@ -55,7 +55,8 @@
 	REPORT_DISK,
 	REPORT_PART,
 	REPORT_SLICE,
-	REPORT_OS
+	REPORT_OS,
+	REPORT_ZPOOL
 } rep_object_t;
 
 /*
@@ -193,7 +194,7 @@
 /*	"       name| idx| flg| tag| 1st block|#of blocks|size [MB]|  " */
 	"         - |  - |  - |  - |        - |        - |       - |"
 /*	"      inuse by|      inuse|  " */
-        "            - |         - |\n"
+	"            - |         - |\n"
 }
 };
 
@@ -227,6 +228,33 @@
 },
 };
 
+static const char *report_zpool[REPORT_PART_END][REPORT_VERB_END] = {
+/* header */
+{
+	"--------------------------------------------------------------------------------\n"
+	" num |           name/guid/bootfs/import|    health|     size| cap%| state| ver|\n"
+	"--------------------------------------------------------------------------------\n",
+
+	"--------------------------------------------------------------------------------\n"
+	" num |           name/guid/bootfs/import|    health|     size| cap%| state| ver|\n"
+	"--------------------------------------------------------------------------------\n",
+},
+
+/* footer */
+{
+	"--------------------------------------------------------------------------------\n",
+	"--------------------------------------------------------------------------------\n",
+},
+
+/* zpool instance w/o attributes */
+{
+/*	" num |           name/guid/bootfs/import|    health|     size| cap%| state| ver|\n" */
+	"     |                                  |          |         |     |      |    |\n",
+
+/*	" num |           name/guid/bootfs/import|    health|     size| cap%| state| ver|\n" */
+	"     |                                  |          |         |     |      |    |\n",
+},
+};
 /* local variables */
 
 static char	*root_slice_name = "";
@@ -240,10 +268,11 @@
 	/* discovery of fdisk partitions is not supported on Sparc */
 
 #ifdef sparc
-	(void) printf("usage: test_dd [-x level] [-v] [-d] [-s all]\n");
+	(void) printf("usage: test_dd [-x level] [-v] [-d] [-s all]"
+	    "[-o all] [-z all]\n");
 #else
 	(void) printf("usage: test_dd [-x level] [-v] [-d] [-p all]"
-	    "[-s all]\n");
+	    "[-o all] [-z all] [-s all]\n");
 #endif
 }
 
@@ -269,6 +298,10 @@
 	case REPORT_OS:
 		(void) printf("%s", report_os[part][verb]);
 		break;
+
+	case REPORT_ZPOOL:
+		(void) printf("%s", report_zpool[part][verb]);
+		break;
 	}
 }
 
@@ -869,6 +902,362 @@
 }
 
 /*
+ * td_zpool_show_target()
+ */
+static void
+td_zpool_show_target(nvlist_t *target,
+	int depth,
+	boolean_t is_spare)
+{
+	nvlist_t **targets;
+	int cnt1 = 0;
+	char *str;
+	uint64_t ui64;
+	uint32_t ui32;
+	uint_t ret;
+
+	if (target == NULL) {
+		return;
+	}
+
+	if (nvlist_lookup_string(target,
+	    TD_ZPOOL_ATTR_TARGET_NAME, &str) != 0) {
+		(void) printf(
+		    "     |   %*s%-*s",
+		    depth, "", 31 - depth, "- ");
+	} else {
+		(void) printf(
+		    "     |   %*s%-*s",
+		    depth, "", 31 - depth, str);
+	}
+
+	if (nvlist_lookup_string(target,
+	    TD_ZPOOL_ATTR_TARGET_HEALTH, &str) != 0) {
+		(void) printf(
+		    "| %9s|         ", "- ");
+	} else {
+		(void) printf(
+		    "| %9s|         ", str);
+	}
+
+	if (is_spare == B_TRUE) {
+		(void) printf("| %4s| %5s| %3s|\n", "- ", "- ", "- ");
+	} else {
+		if (nvlist_lookup_uint64(target,
+		    TD_ZPOOL_ATTR_TARGET_READ_ERRORS, &ui64) != 0) {
+			(void) printf("| %4s", "- ");
+		} else {
+			(void) printf("| %4llu", ui64);
+		}
+
+		if (nvlist_lookup_uint64(target,
+		    TD_ZPOOL_ATTR_TARGET_WRITE_ERRORS, &ui64) != 0) {
+			(void) printf("| %5s", "- ");
+		} else {
+			(void) printf("| %5llu", ui64);
+		}
+
+		if (nvlist_lookup_uint64(target,
+		    TD_ZPOOL_ATTR_TARGET_CHECKSUM_ERRORS, &ui64) != 0) {
+			(void) printf("| %3s|\n", "- ");
+		} else {
+			(void) printf("| %3llu|\n", ui64);
+		}
+	}
+
+	if (nvlist_lookup_uint32(target,
+	    TD_ZPOOL_ATTR_NUM_TARGETS,
+	    &ui32) != 0) {
+		return;
+	}
+
+	if (ui32 > 0) {
+		if (nvlist_lookup_nvlist_array(target,
+		    TD_ZPOOL_ATTR_TARGETS,
+		    &targets, &ret) != 0) {
+			return;
+		}
+
+		for (cnt1 = 0; cnt1 < ret; cnt1++) {
+			td_zpool_show_target(targets[cnt1],
+			    depth + 2, is_spare);
+		}
+	}
+}
+
+
+/*
+ * td_zpool_show_attr()
+ */
+static void
+td_zpool_show_attr(nvlist_t *attrs, rep_verbosity_t verbosity)
+{
+	char *str;
+	uint32_t ui32;
+	uint64_t ui64;
+	uint32_t num_targets = 0, num_logs = 0;
+	uint32_t num_l2cache = 0, num_spares = 0;
+	nvlist_t **targets, **logs, **l2cache, **spares;
+	int cnt1 = 0;
+	uint_t ret;
+	boolean_t import = B_FALSE;
+
+	if (nvlist_lookup_string(attrs, TD_ZPOOL_ATTR_NAME, &str) == 0) {
+		(void) printf(" %-33s| ", str);
+	} else {
+		(void) printf(" %-33s| ", "- ");
+	}
+
+	if (nvlist_lookup_string(attrs, TD_ZPOOL_ATTR_HEALTH, &str) == 0) {
+		(void) printf("%9s| ", str);
+	} else {
+		(void) printf("%9s| ", "- ");
+	}
+
+	if (nvlist_lookup_uint64(attrs, TD_ZPOOL_ATTR_SIZE, &ui64) == 0) {
+		double size_mb = 0, size_gb = 0;
+
+		if (ui64 > 0) {
+			size_mb = BYTES_TO_MB(ui64);
+		} else {
+			size_mb = 0;
+			size_gb = 0;
+		}
+
+		if (size_mb > MB_IN_GB) {
+			size_gb = MB_TO_GB(size_mb);
+			size_mb = 0;
+		} else {
+			size_gb = 0;
+		}
+
+		(void) printf("%7.2lf%c| ",
+		    size_mb > 0 ? size_mb : size_gb,
+		    size_mb > 0 ? 'M' : 'G');
+	} else {
+		(void) printf("%8s| ", "- ");
+	}
+
+	if (nvlist_lookup_uint64(attrs, TD_ZPOOL_ATTR_CAPACITY, &ui64) == 0) {
+		(void) printf("%3llu%%| ", ui64);
+	} else {
+		(void) printf("%4s| ", "- ");
+	}
+
+	if (nvlist_lookup_uint32(attrs, TD_ZPOOL_ATTR_STATUS, &ui32) == 0) {
+		(void) printf("%5d|", ui32);
+	} else {
+		(void) printf("%5s|", "- ");
+	}
+
+	if (nvlist_lookup_uint32(attrs, TD_ZPOOL_ATTR_VERSION, &ui32) == 0) {
+		(void) printf("  %2d|\n", ui32);
+	} else {
+		(void) printf("  %2s|\n", "h ");
+	}
+
+	if (nvlist_lookup_uint64(attrs, TD_ZPOOL_ATTR_GUID, &ui64) == 0) {
+		(void) printf("%4s |", "");
+		(void) printf("%34llu| ", ui64);
+		(void) printf("%9s| ", " ");
+		(void) printf("%8s| ", " ");
+		(void) printf("%4s| ", " ");
+		(void) printf("%5s|", " ");
+		(void) printf("  %2s|\n", " ");
+	} else {
+		(void) printf("%4s |", "");
+		(void) printf(" %34s| ", "- ");
+		(void) printf("%9s| ", " ");
+		(void) printf("%8s| ", " ");
+		(void) printf("%4s| ", " ");
+		(void) printf("%5s|", " ");
+		(void) printf("  %2s|\n", " ");
+	}
+
+	if (nvlist_lookup_string(attrs, TD_ZPOOL_ATTR_BOOTFS, &str) == 0) {
+		(void) printf("%4s |", "");
+		(void) printf(" %33s| ", str != NULL ? str : "m");
+		(void) printf("%9s| ", " ");
+		(void) printf("%8s| ", " ");
+		(void) printf("%4s| ", " ");
+		(void) printf("%5s|", " ");
+		(void) printf("  %2s|\n", " ");
+	}
+
+	if (nvlist_lookup_boolean_value(attrs,
+	    TD_ZPOOL_ATTR_IMPORT, &import) == 0) {
+		if (import) {
+			(void) printf("%4s |", "");
+			(void) printf(" %33s| ", "Importable pool");
+			(void) printf("%9s| ", " ");
+			(void) printf("%8s| ", " ");
+			(void) printf("%4s| ", " ");
+			(void) printf("%5s|", " ");
+			(void) printf("  %2s|\n", " ");
+		}
+	}
+
+	if (verbosity == REPORT_VERB_HIGH) {
+		/* High verbosity just print number of targets */
+		num_targets = 0;
+		if (nvlist_lookup_uint32(attrs, TD_ZPOOL_ATTR_NUM_TARGETS,
+		    &num_targets) != 0) {
+			num_targets = 0;
+		}
+
+		/* Print targets */
+		if (num_targets > 0) {
+			if (nvlist_lookup_nvlist_array(attrs,
+			    TD_ZPOOL_ATTR_TARGETS, &targets, &ret) != 0) {
+				(void) printf("     | %72s|\n",
+				    "Failed to retrieve targets");
+			} else {
+				for (cnt1 = 0; cnt1 < ret; cnt1++) {
+					td_zpool_show_target(targets[cnt1],
+					    0, B_FALSE);
+				}
+			}
+		}
+
+		/* Print logs */
+		num_logs = 0;
+		if (nvlist_lookup_uint32(attrs, TD_ZPOOL_ATTR_NUM_LOGS,
+		    &num_logs) != 0) {
+			num_logs = 0;
+		}
+
+		if (num_logs > 0) {
+			(void) printf("%4s |", "");
+			(void) printf(" %-33s| ", "logs");
+			(void) printf("%9s| ", " ");
+			(void) printf("%8s| ", " ");
+			(void) printf("%4s| ", " ");
+			(void) printf("%5s|", " ");
+			(void) printf("  %2s|\n", " ");
+
+			if (nvlist_lookup_nvlist_array(attrs,
+			    TD_ZPOOL_ATTR_LOGS, &logs, &ret) != 0) {
+				(void) printf("     | %72s|\n",
+				    "Failed to retrieve logs");
+			} else {
+				for (cnt1 = 0; cnt1 < ret; cnt1++) {
+					td_zpool_show_target(logs[cnt1],
+					    0, B_FALSE);
+				}
+			}
+		}
+
+		/* Print l2cache */
+		num_l2cache = 0;
+		if (nvlist_lookup_uint32(attrs, TD_ZPOOL_ATTR_NUM_L2CACHE,
+		    &num_l2cache) != 0) {
+			num_l2cache = 0;
+		}
+
+		if (num_l2cache > 0) {
+			(void) printf("%4s |", "");
+			(void) printf(" %-33s| ", "cache");
+			(void) printf("%9s| ", " ");
+			(void) printf("%8s| ", " ");
+			(void) printf("%4s| ", " ");
+			(void) printf("%5s|", " ");
+			(void) printf("  %2s|\n", " ");
+
+			if (nvlist_lookup_nvlist_array(attrs,
+			    TD_ZPOOL_ATTR_L2CACHE, &l2cache, &ret) != 0) {
+				(void) printf("     | %72s|\n",
+				    "Failed to retrieve l2cache");
+			} else {
+				for (cnt1 = 0; cnt1 < ret; cnt1++) {
+					td_zpool_show_target(l2cache[cnt1],
+					    0, B_FALSE);
+				}
+			}
+		}
+
+		/* Print spares */
+		num_spares = 0;
+		if (nvlist_lookup_uint32(attrs, TD_ZPOOL_ATTR_NUM_SPARES,
+		    &num_spares) != 0) {
+			num_spares = 0;
+		}
+
+		if (num_spares > 0) {
+			(void) printf("%4s |", "");
+			(void) printf(" %-33s| ", "spares");
+			(void) printf("%9s| ", " ");
+			(void) printf("%8s| ", " ");
+			(void) printf("%4s| ", " ");
+			(void) printf("%5s|", " ");
+			(void) printf("  %2s|\n", " ");
+
+			if (nvlist_lookup_nvlist_array(attrs,
+			    TD_ZPOOL_ATTR_SPARES, &spares, &ret) != 0) {
+				(void) printf("     | %72s|\n",
+				    "Failed to retrieve spares");
+			} else {
+				for (cnt1 = 0; cnt1 < ret; cnt1++) {
+					td_zpool_show_target(spares[cnt1],
+					    0, B_TRUE);
+				}
+			}
+		}
+	}
+}
+
+/*
+ * discover_zpool()
+ */
+static int
+discover_zpool(rep_verbosity_t verbosity)
+{
+	nvlist_t	*zpool_attr;
+	int		i;
+	int		nozpools;
+
+	if (td_discover(TD_OT_ZPOOL, &nozpools) != 0) {
+		(void) printf("Couldn't discover zpools\n");
+
+		return (-1);
+	}
+
+	/* if there is no Solaris, do not display empty table */
+	if (nozpools == 0)
+		return (0);
+
+	(void) printf("Total number of zpools: %d\n", nozpools);
+
+	/* list os attributes */
+	display_report(REPORT_ZPOOL, REPORT_HEADER, verbosity);
+
+	for (i = 0; i < nozpools; i++) {
+		if (td_get_next(TD_OT_ZPOOL) != 0) {
+			(void) printf("Couldn't get next zpool\n");
+
+			return (-1);
+		}
+
+		(void) printf("%4d |", i + 1);
+
+		zpool_attr = td_attributes_get(TD_OT_ZPOOL);
+
+		if (zpool_attr == NULL) {
+			display_report(REPORT_ZPOOL, REPORT_BODY_NOATTR,
+			    verbosity);
+		} else {
+			(void) td_zpool_show_attr(zpool_attr, verbosity);
+
+			nvlist_free(zpool_attr);
+		}
+		display_report(REPORT_ZPOOL, REPORT_BODY_NOATTR, verbosity);
+	}
+
+	display_report(REPORT_ZPOOL, REPORT_FOOTER, verbosity);
+
+	return (0);
+}
+
+/*
  * main()
  */
 int
@@ -884,6 +1273,8 @@
 	char		*slice_object_to_discover = NULL;
 	int		fl_discover_os = 0;
 	char		*os_object_to_discover = NULL;
+	int		fl_discover_zpools = 0;
+	char		*zpool_object_to_discover = NULL;
 
 	/* init logging/debugging service */
 
@@ -895,14 +1286,15 @@
 	 * s - slice discovery
 	 * o - discovery of Solaris instances
 	 * v - verbose output
+	 * z - zpool discovery
 	 */
 
 	/* -p is not supported for Sparc */
 
 #ifdef sparc
-	while ((opt = getopt(argc, argv, "x:vds:o:")) != EOF) {
+	while ((opt = getopt(argc, argv, "x:vds:o:z:")) != EOF) {
 #else
-	while ((opt = getopt(argc, argv, "x:vdp:s:o:")) != EOF) {
+	while ((opt = getopt(argc, argv, "x:vdp:s:o:z:")) != EOF) {
 #endif
 		switch (opt) {
 
@@ -946,6 +1338,12 @@
 				os_object_to_discover = optarg;
 			break;
 
+			case 'z':
+				fl_discover_zpools = 1;
+
+				zpool_object_to_discover = optarg;
+			break;
+
 			case 'v':
 				verbosity = REPORT_VERB_HIGH;
 			break;
@@ -956,7 +1354,7 @@
 	 * if there is nothing to discover, display help and exit
 	 */
 	if (!fl_discover_disks && !fl_discover_parts && !fl_discover_slices &&
-	    !fl_discover_os) {
+	    !fl_discover_os && !fl_discover_zpools) {
 		display_help();
 
 		return (0);
@@ -1018,5 +1416,19 @@
 		}
 	}
 
+	/* discover zpools */
+
+	if (fl_discover_zpools) {
+		/* determine if all zpools are to be discovered */
+
+		if (strncmp(zpool_object_to_discover, "all",
+		    sizeof ("all") - 1) == 0) {
+			(void) printf("\nZpool discovery\n");
+
+			(void) discover_zpool(verbosity);
+		} else {
+			(void) printf("\n-z <zpool> not supported for now\n");
+		}
+	}
 	return (0);
 }