1eda14cbcSMatt Macy /*
2eda14cbcSMatt Macy  * CDDL HEADER START
3eda14cbcSMatt Macy  *
4eda14cbcSMatt Macy  * The contents of this file are subject to the terms of the
5eda14cbcSMatt Macy  * Common Development and Distribution License (the "License").
6eda14cbcSMatt Macy  * You may not use this file except in compliance with the License.
7eda14cbcSMatt Macy  *
8eda14cbcSMatt Macy  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9eda14cbcSMatt Macy  * or http://www.opensolaris.org/os/licensing.
10eda14cbcSMatt Macy  * See the License for the specific language governing permissions
11eda14cbcSMatt Macy  * and limitations under the License.
12eda14cbcSMatt Macy  *
13eda14cbcSMatt Macy  * When distributing Covered Code, include this CDDL HEADER in each
14eda14cbcSMatt Macy  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15eda14cbcSMatt Macy  * If applicable, add the following below this CDDL HEADER, with the
16eda14cbcSMatt Macy  * fields enclosed by brackets "[]" replaced with your own identifying
17eda14cbcSMatt Macy  * information: Portions Copyright [yyyy] [name of copyright owner]
18eda14cbcSMatt Macy  *
19eda14cbcSMatt Macy  * CDDL HEADER END
20eda14cbcSMatt Macy  */
21eda14cbcSMatt Macy /*
22eda14cbcSMatt Macy  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23eda14cbcSMatt Macy  * Use is subject to license terms.
24eda14cbcSMatt Macy  */
25eda14cbcSMatt Macy 
26eda14cbcSMatt Macy /*
27eda14cbcSMatt Macy  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>.
28eda14cbcSMatt Macy  */
29eda14cbcSMatt Macy 
30eda14cbcSMatt Macy #include <libintl.h>
31eda14cbcSMatt Macy #include <libuutil.h>
32eda14cbcSMatt Macy #include <stddef.h>
33eda14cbcSMatt Macy #include <stdio.h>
34eda14cbcSMatt Macy #include <stdlib.h>
35eda14cbcSMatt Macy #include <strings.h>
36eda14cbcSMatt Macy #include <thread_pool.h>
37eda14cbcSMatt Macy 
38eda14cbcSMatt Macy #include <libzfs.h>
39eda14cbcSMatt Macy #include <libzutil.h>
40eda14cbcSMatt Macy #include <sys/zfs_context.h>
41eda14cbcSMatt Macy #include <sys/wait.h>
42eda14cbcSMatt Macy 
43eda14cbcSMatt Macy #include "zpool_util.h"
44eda14cbcSMatt Macy 
45eda14cbcSMatt Macy /*
46eda14cbcSMatt Macy  * Private interface for iterating over pools specified on the command line.
47eda14cbcSMatt Macy  * Most consumers will call for_each_pool, but in order to support iostat, we
48eda14cbcSMatt Macy  * allow fined grained control through the zpool_list_t interface.
49eda14cbcSMatt Macy  */
50eda14cbcSMatt Macy 
51eda14cbcSMatt Macy typedef struct zpool_node {
52eda14cbcSMatt Macy 	zpool_handle_t	*zn_handle;
53eda14cbcSMatt Macy 	uu_avl_node_t	zn_avlnode;
54eda14cbcSMatt Macy 	int		zn_mark;
55eda14cbcSMatt Macy } zpool_node_t;
56eda14cbcSMatt Macy 
57eda14cbcSMatt Macy struct zpool_list {
58eda14cbcSMatt Macy 	boolean_t	zl_findall;
597877fdebSMatt Macy 	boolean_t	zl_literal;
60eda14cbcSMatt Macy 	uu_avl_t	*zl_avl;
61eda14cbcSMatt Macy 	uu_avl_pool_t	*zl_pool;
62eda14cbcSMatt Macy 	zprop_list_t	**zl_proplist;
63eda14cbcSMatt Macy };
64eda14cbcSMatt Macy 
65eda14cbcSMatt Macy /* ARGSUSED */
66eda14cbcSMatt Macy static int
67eda14cbcSMatt Macy zpool_compare(const void *larg, const void *rarg, void *unused)
68eda14cbcSMatt Macy {
69eda14cbcSMatt Macy 	zpool_handle_t *l = ((zpool_node_t *)larg)->zn_handle;
70eda14cbcSMatt Macy 	zpool_handle_t *r = ((zpool_node_t *)rarg)->zn_handle;
71eda14cbcSMatt Macy 	const char *lname = zpool_get_name(l);
72eda14cbcSMatt Macy 	const char *rname = zpool_get_name(r);
73eda14cbcSMatt Macy 
74eda14cbcSMatt Macy 	return (strcmp(lname, rname));
75eda14cbcSMatt Macy }
76eda14cbcSMatt Macy 
77eda14cbcSMatt Macy /*
78eda14cbcSMatt Macy  * Callback function for pool_list_get().  Adds the given pool to the AVL tree
79eda14cbcSMatt Macy  * of known pools.
80eda14cbcSMatt Macy  */
81eda14cbcSMatt Macy static int
82eda14cbcSMatt Macy add_pool(zpool_handle_t *zhp, void *data)
83eda14cbcSMatt Macy {
84eda14cbcSMatt Macy 	zpool_list_t *zlp = data;
85eda14cbcSMatt Macy 	zpool_node_t *node = safe_malloc(sizeof (zpool_node_t));
86eda14cbcSMatt Macy 	uu_avl_index_t idx;
87eda14cbcSMatt Macy 
88eda14cbcSMatt Macy 	node->zn_handle = zhp;
89eda14cbcSMatt Macy 	uu_avl_node_init(node, &node->zn_avlnode, zlp->zl_pool);
90eda14cbcSMatt Macy 	if (uu_avl_find(zlp->zl_avl, node, NULL, &idx) == NULL) {
91eda14cbcSMatt Macy 		if (zlp->zl_proplist &&
927877fdebSMatt Macy 		    zpool_expand_proplist(zhp, zlp->zl_proplist,
937877fdebSMatt Macy 		    zlp->zl_literal)
947877fdebSMatt Macy 		    != 0) {
95eda14cbcSMatt Macy 			zpool_close(zhp);
96eda14cbcSMatt Macy 			free(node);
97eda14cbcSMatt Macy 			return (-1);
98eda14cbcSMatt Macy 		}
99eda14cbcSMatt Macy 		uu_avl_insert(zlp->zl_avl, node, idx);
100eda14cbcSMatt Macy 	} else {
101eda14cbcSMatt Macy 		zpool_close(zhp);
102eda14cbcSMatt Macy 		free(node);
103eda14cbcSMatt Macy 		return (-1);
104eda14cbcSMatt Macy 	}
105eda14cbcSMatt Macy 
106eda14cbcSMatt Macy 	return (0);
107eda14cbcSMatt Macy }
108eda14cbcSMatt Macy 
109eda14cbcSMatt Macy /*
110eda14cbcSMatt Macy  * Create a list of pools based on the given arguments.  If we're given no
111eda14cbcSMatt Macy  * arguments, then iterate over all pools in the system and add them to the AVL
112eda14cbcSMatt Macy  * tree.  Otherwise, add only those pool explicitly specified on the command
113eda14cbcSMatt Macy  * line.
114eda14cbcSMatt Macy  */
115eda14cbcSMatt Macy zpool_list_t *
1167877fdebSMatt Macy pool_list_get(int argc, char **argv, zprop_list_t **proplist,
1177877fdebSMatt Macy     boolean_t literal, int *err)
118eda14cbcSMatt Macy {
119eda14cbcSMatt Macy 	zpool_list_t *zlp;
120eda14cbcSMatt Macy 
121eda14cbcSMatt Macy 	zlp = safe_malloc(sizeof (zpool_list_t));
122eda14cbcSMatt Macy 
123eda14cbcSMatt Macy 	zlp->zl_pool = uu_avl_pool_create("zfs_pool", sizeof (zpool_node_t),
124eda14cbcSMatt Macy 	    offsetof(zpool_node_t, zn_avlnode), zpool_compare, UU_DEFAULT);
125eda14cbcSMatt Macy 
126eda14cbcSMatt Macy 	if (zlp->zl_pool == NULL)
127eda14cbcSMatt Macy 		zpool_no_memory();
128eda14cbcSMatt Macy 
129eda14cbcSMatt Macy 	if ((zlp->zl_avl = uu_avl_create(zlp->zl_pool, NULL,
130eda14cbcSMatt Macy 	    UU_DEFAULT)) == NULL)
131eda14cbcSMatt Macy 		zpool_no_memory();
132eda14cbcSMatt Macy 
133eda14cbcSMatt Macy 	zlp->zl_proplist = proplist;
134eda14cbcSMatt Macy 
1357877fdebSMatt Macy 	zlp->zl_literal = literal;
1367877fdebSMatt Macy 
137eda14cbcSMatt Macy 	if (argc == 0) {
138eda14cbcSMatt Macy 		(void) zpool_iter(g_zfs, add_pool, zlp);
139eda14cbcSMatt Macy 		zlp->zl_findall = B_TRUE;
140eda14cbcSMatt Macy 	} else {
141eda14cbcSMatt Macy 		int i;
142eda14cbcSMatt Macy 
143eda14cbcSMatt Macy 		for (i = 0; i < argc; i++) {
144eda14cbcSMatt Macy 			zpool_handle_t *zhp;
145eda14cbcSMatt Macy 
146eda14cbcSMatt Macy 			if ((zhp = zpool_open_canfail(g_zfs, argv[i])) !=
147eda14cbcSMatt Macy 			    NULL) {
148eda14cbcSMatt Macy 				if (add_pool(zhp, zlp) != 0)
149eda14cbcSMatt Macy 					*err = B_TRUE;
150eda14cbcSMatt Macy 			} else {
151eda14cbcSMatt Macy 				*err = B_TRUE;
152eda14cbcSMatt Macy 			}
153eda14cbcSMatt Macy 		}
154eda14cbcSMatt Macy 	}
155eda14cbcSMatt Macy 
156eda14cbcSMatt Macy 	return (zlp);
157eda14cbcSMatt Macy }
158eda14cbcSMatt Macy 
159eda14cbcSMatt Macy /*
160eda14cbcSMatt Macy  * Search for any new pools, adding them to the list.  We only add pools when no
161eda14cbcSMatt Macy  * options were given on the command line.  Otherwise, we keep the list fixed as
162eda14cbcSMatt Macy  * those that were explicitly specified.
163eda14cbcSMatt Macy  */
164eda14cbcSMatt Macy void
165eda14cbcSMatt Macy pool_list_update(zpool_list_t *zlp)
166eda14cbcSMatt Macy {
167eda14cbcSMatt Macy 	if (zlp->zl_findall)
168eda14cbcSMatt Macy 		(void) zpool_iter(g_zfs, add_pool, zlp);
169eda14cbcSMatt Macy }
170eda14cbcSMatt Macy 
171eda14cbcSMatt Macy /*
172eda14cbcSMatt Macy  * Iterate over all pools in the list, executing the callback for each
173eda14cbcSMatt Macy  */
174eda14cbcSMatt Macy int
175eda14cbcSMatt Macy pool_list_iter(zpool_list_t *zlp, int unavail, zpool_iter_f func,
176eda14cbcSMatt Macy     void *data)
177eda14cbcSMatt Macy {
178eda14cbcSMatt Macy 	zpool_node_t *node, *next_node;
179eda14cbcSMatt Macy 	int ret = 0;
180eda14cbcSMatt Macy 
181eda14cbcSMatt Macy 	for (node = uu_avl_first(zlp->zl_avl); node != NULL; node = next_node) {
182eda14cbcSMatt Macy 		next_node = uu_avl_next(zlp->zl_avl, node);
183eda14cbcSMatt Macy 		if (zpool_get_state(node->zn_handle) != POOL_STATE_UNAVAIL ||
184eda14cbcSMatt Macy 		    unavail)
185eda14cbcSMatt Macy 			ret |= func(node->zn_handle, data);
186eda14cbcSMatt Macy 	}
187eda14cbcSMatt Macy 
188eda14cbcSMatt Macy 	return (ret);
189eda14cbcSMatt Macy }
190eda14cbcSMatt Macy 
191eda14cbcSMatt Macy /*
192eda14cbcSMatt Macy  * Remove the given pool from the list.  When running iostat, we want to remove
193eda14cbcSMatt Macy  * those pools that no longer exist.
194eda14cbcSMatt Macy  */
195eda14cbcSMatt Macy void
196eda14cbcSMatt Macy pool_list_remove(zpool_list_t *zlp, zpool_handle_t *zhp)
197eda14cbcSMatt Macy {
198eda14cbcSMatt Macy 	zpool_node_t search, *node;
199eda14cbcSMatt Macy 
200eda14cbcSMatt Macy 	search.zn_handle = zhp;
201eda14cbcSMatt Macy 	if ((node = uu_avl_find(zlp->zl_avl, &search, NULL, NULL)) != NULL) {
202eda14cbcSMatt Macy 		uu_avl_remove(zlp->zl_avl, node);
203eda14cbcSMatt Macy 		zpool_close(node->zn_handle);
204eda14cbcSMatt Macy 		free(node);
205eda14cbcSMatt Macy 	}
206eda14cbcSMatt Macy }
207eda14cbcSMatt Macy 
208eda14cbcSMatt Macy /*
209eda14cbcSMatt Macy  * Free all the handles associated with this list.
210eda14cbcSMatt Macy  */
211eda14cbcSMatt Macy void
212eda14cbcSMatt Macy pool_list_free(zpool_list_t *zlp)
213eda14cbcSMatt Macy {
214eda14cbcSMatt Macy 	uu_avl_walk_t *walk;
215eda14cbcSMatt Macy 	zpool_node_t *node;
216eda14cbcSMatt Macy 
217eda14cbcSMatt Macy 	if ((walk = uu_avl_walk_start(zlp->zl_avl, UU_WALK_ROBUST)) == NULL) {
218eda14cbcSMatt Macy 		(void) fprintf(stderr,
219eda14cbcSMatt Macy 		    gettext("internal error: out of memory"));
220eda14cbcSMatt Macy 		exit(1);
221eda14cbcSMatt Macy 	}
222eda14cbcSMatt Macy 
223eda14cbcSMatt Macy 	while ((node = uu_avl_walk_next(walk)) != NULL) {
224eda14cbcSMatt Macy 		uu_avl_remove(zlp->zl_avl, node);
225eda14cbcSMatt Macy 		zpool_close(node->zn_handle);
226eda14cbcSMatt Macy 		free(node);
227eda14cbcSMatt Macy 	}
228eda14cbcSMatt Macy 
229eda14cbcSMatt Macy 	uu_avl_walk_end(walk);
230eda14cbcSMatt Macy 	uu_avl_destroy(zlp->zl_avl);
231eda14cbcSMatt Macy 	uu_avl_pool_destroy(zlp->zl_pool);
232eda14cbcSMatt Macy 
233eda14cbcSMatt Macy 	free(zlp);
234eda14cbcSMatt Macy }
235eda14cbcSMatt Macy 
236eda14cbcSMatt Macy /*
237eda14cbcSMatt Macy  * Returns the number of elements in the pool list.
238eda14cbcSMatt Macy  */
239eda14cbcSMatt Macy int
240eda14cbcSMatt Macy pool_list_count(zpool_list_t *zlp)
241eda14cbcSMatt Macy {
242eda14cbcSMatt Macy 	return (uu_avl_numnodes(zlp->zl_avl));
243eda14cbcSMatt Macy }
244eda14cbcSMatt Macy 
245eda14cbcSMatt Macy /*
246eda14cbcSMatt Macy  * High level function which iterates over all pools given on the command line,
247eda14cbcSMatt Macy  * using the pool_list_* interfaces.
248eda14cbcSMatt Macy  */
249eda14cbcSMatt Macy int
250eda14cbcSMatt Macy for_each_pool(int argc, char **argv, boolean_t unavail,
2517877fdebSMatt Macy     zprop_list_t **proplist, boolean_t literal, zpool_iter_f func, void *data)
252eda14cbcSMatt Macy {
253eda14cbcSMatt Macy 	zpool_list_t *list;
254eda14cbcSMatt Macy 	int ret = 0;
255eda14cbcSMatt Macy 
2567877fdebSMatt Macy 	if ((list = pool_list_get(argc, argv, proplist, literal, &ret)) == NULL)
257eda14cbcSMatt Macy 		return (1);
258eda14cbcSMatt Macy 
259eda14cbcSMatt Macy 	if (pool_list_iter(list, unavail, func, data) != 0)
260eda14cbcSMatt Macy 		ret = 1;
261eda14cbcSMatt Macy 
262eda14cbcSMatt Macy 	pool_list_free(list);
263eda14cbcSMatt Macy 
264eda14cbcSMatt Macy 	return (ret);
265eda14cbcSMatt Macy }
266eda14cbcSMatt Macy 
267eda14cbcSMatt Macy static int
268eda14cbcSMatt Macy for_each_vdev_cb(zpool_handle_t *zhp, nvlist_t *nv, pool_vdev_iter_f func,
269eda14cbcSMatt Macy     void *data)
270eda14cbcSMatt Macy {
271eda14cbcSMatt Macy 	nvlist_t **child;
272eda14cbcSMatt Macy 	uint_t c, children;
273eda14cbcSMatt Macy 	int ret = 0;
274eda14cbcSMatt Macy 	int i;
275eda14cbcSMatt Macy 	char *type;
276eda14cbcSMatt Macy 
277eda14cbcSMatt Macy 	const char *list[] = {
278eda14cbcSMatt Macy 	    ZPOOL_CONFIG_SPARES,
279eda14cbcSMatt Macy 	    ZPOOL_CONFIG_L2CACHE,
280eda14cbcSMatt Macy 	    ZPOOL_CONFIG_CHILDREN
281eda14cbcSMatt Macy 	};
282eda14cbcSMatt Macy 
283eda14cbcSMatt Macy 	for (i = 0; i < ARRAY_SIZE(list); i++) {
284eda14cbcSMatt Macy 		if (nvlist_lookup_nvlist_array(nv, list[i], &child,
285eda14cbcSMatt Macy 		    &children) == 0) {
286eda14cbcSMatt Macy 			for (c = 0; c < children; c++) {
287eda14cbcSMatt Macy 				uint64_t ishole = 0;
288eda14cbcSMatt Macy 
289eda14cbcSMatt Macy 				(void) nvlist_lookup_uint64(child[c],
290eda14cbcSMatt Macy 				    ZPOOL_CONFIG_IS_HOLE, &ishole);
291eda14cbcSMatt Macy 
292eda14cbcSMatt Macy 				if (ishole)
293eda14cbcSMatt Macy 					continue;
294eda14cbcSMatt Macy 
295eda14cbcSMatt Macy 				ret |= for_each_vdev_cb(zhp, child[c], func,
296eda14cbcSMatt Macy 				    data);
297eda14cbcSMatt Macy 			}
298eda14cbcSMatt Macy 		}
299eda14cbcSMatt Macy 	}
300eda14cbcSMatt Macy 
301eda14cbcSMatt Macy 	if (nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) != 0)
302eda14cbcSMatt Macy 		return (ret);
303eda14cbcSMatt Macy 
304eda14cbcSMatt Macy 	/* Don't run our function on root vdevs */
305eda14cbcSMatt Macy 	if (strcmp(type, VDEV_TYPE_ROOT) != 0) {
306eda14cbcSMatt Macy 		ret |= func(zhp, nv, data);
307eda14cbcSMatt Macy 	}
308eda14cbcSMatt Macy 
309eda14cbcSMatt Macy 	return (ret);
310eda14cbcSMatt Macy }
311eda14cbcSMatt Macy 
312eda14cbcSMatt Macy /*
313eda14cbcSMatt Macy  * This is the equivalent of for_each_pool() for vdevs.  It iterates thorough
314eda14cbcSMatt Macy  * all vdevs in the pool, ignoring root vdevs and holes, calling func() on
315eda14cbcSMatt Macy  * each one.
316eda14cbcSMatt Macy  *
317eda14cbcSMatt Macy  * @zhp:	Zpool handle
318eda14cbcSMatt Macy  * @func:	Function to call on each vdev
319eda14cbcSMatt Macy  * @data:	Custom data to pass to the function
320eda14cbcSMatt Macy  */
321eda14cbcSMatt Macy int
322eda14cbcSMatt Macy for_each_vdev(zpool_handle_t *zhp, pool_vdev_iter_f func, void *data)
323eda14cbcSMatt Macy {
324eda14cbcSMatt Macy 	nvlist_t *config, *nvroot = NULL;
325eda14cbcSMatt Macy 
326eda14cbcSMatt Macy 	if ((config = zpool_get_config(zhp, NULL)) != NULL) {
327eda14cbcSMatt Macy 		verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
328eda14cbcSMatt Macy 		    &nvroot) == 0);
329eda14cbcSMatt Macy 	}
330eda14cbcSMatt Macy 	return (for_each_vdev_cb(zhp, nvroot, func, data));
331eda14cbcSMatt Macy }
332eda14cbcSMatt Macy 
333eda14cbcSMatt Macy /*
334eda14cbcSMatt Macy  * Process the vcdl->vdev_cmd_data[] array to figure out all the unique column
335eda14cbcSMatt Macy  * names and their widths.  When this function is done, vcdl->uniq_cols,
336eda14cbcSMatt Macy  * vcdl->uniq_cols_cnt, and vcdl->uniq_cols_width will be filled in.
337eda14cbcSMatt Macy  */
338eda14cbcSMatt Macy static void
339eda14cbcSMatt Macy process_unique_cmd_columns(vdev_cmd_data_list_t *vcdl)
340eda14cbcSMatt Macy {
341eda14cbcSMatt Macy 	char **uniq_cols = NULL, **tmp = NULL;
342eda14cbcSMatt Macy 	int *uniq_cols_width;
343eda14cbcSMatt Macy 	vdev_cmd_data_t *data;
344eda14cbcSMatt Macy 	int cnt = 0;
345eda14cbcSMatt Macy 	int k;
346eda14cbcSMatt Macy 
347eda14cbcSMatt Macy 	/* For each vdev */
348eda14cbcSMatt Macy 	for (int i = 0; i < vcdl->count; i++) {
349eda14cbcSMatt Macy 		data = &vcdl->data[i];
350eda14cbcSMatt Macy 		/* For each column the vdev reported */
351eda14cbcSMatt Macy 		for (int j = 0; j < data->cols_cnt; j++) {
352eda14cbcSMatt Macy 			/* Is this column in our list of unique column names? */
353eda14cbcSMatt Macy 			for (k = 0; k < cnt; k++) {
354eda14cbcSMatt Macy 				if (strcmp(data->cols[j], uniq_cols[k]) == 0)
355eda14cbcSMatt Macy 					break; /* yes it is */
356eda14cbcSMatt Macy 			}
357eda14cbcSMatt Macy 			if (k == cnt) {
358eda14cbcSMatt Macy 				/* No entry for column, add to list */
359eda14cbcSMatt Macy 				tmp = realloc(uniq_cols, sizeof (*uniq_cols) *
360eda14cbcSMatt Macy 				    (cnt + 1));
361eda14cbcSMatt Macy 				if (tmp == NULL)
362eda14cbcSMatt Macy 					break; /* Nothing we can do... */
363eda14cbcSMatt Macy 				uniq_cols = tmp;
364eda14cbcSMatt Macy 				uniq_cols[cnt] = data->cols[j];
365eda14cbcSMatt Macy 				cnt++;
366eda14cbcSMatt Macy 			}
367eda14cbcSMatt Macy 		}
368eda14cbcSMatt Macy 	}
369eda14cbcSMatt Macy 
370eda14cbcSMatt Macy 	/*
371eda14cbcSMatt Macy 	 * We now have a list of all the unique column names.  Figure out the
372eda14cbcSMatt Macy 	 * max width of each column by looking at the column name and all its
373eda14cbcSMatt Macy 	 * values.
374eda14cbcSMatt Macy 	 */
375eda14cbcSMatt Macy 	uniq_cols_width = safe_malloc(sizeof (*uniq_cols_width) * cnt);
376eda14cbcSMatt Macy 	for (int i = 0; i < cnt; i++) {
377eda14cbcSMatt Macy 		/* Start off with the column title's width */
378eda14cbcSMatt Macy 		uniq_cols_width[i] = strlen(uniq_cols[i]);
379eda14cbcSMatt Macy 		/* For each vdev */
380eda14cbcSMatt Macy 		for (int j = 0; j < vcdl->count; j++) {
381eda14cbcSMatt Macy 			/* For each of the vdev's values in a column */
382eda14cbcSMatt Macy 			data = &vcdl->data[j];
383eda14cbcSMatt Macy 			for (k = 0; k < data->cols_cnt; k++) {
384eda14cbcSMatt Macy 				/* Does this vdev have a value for this col? */
385eda14cbcSMatt Macy 				if (strcmp(data->cols[k], uniq_cols[i]) == 0) {
386eda14cbcSMatt Macy 					/* Is the value width larger? */
387eda14cbcSMatt Macy 					uniq_cols_width[i] =
388eda14cbcSMatt Macy 					    MAX(uniq_cols_width[i],
389eda14cbcSMatt Macy 					    strlen(data->lines[k]));
390eda14cbcSMatt Macy 				}
391eda14cbcSMatt Macy 			}
392eda14cbcSMatt Macy 		}
393eda14cbcSMatt Macy 	}
394eda14cbcSMatt Macy 
395eda14cbcSMatt Macy 	vcdl->uniq_cols = uniq_cols;
396eda14cbcSMatt Macy 	vcdl->uniq_cols_cnt = cnt;
397eda14cbcSMatt Macy 	vcdl->uniq_cols_width = uniq_cols_width;
398eda14cbcSMatt Macy }
399eda14cbcSMatt Macy 
400eda14cbcSMatt Macy 
401eda14cbcSMatt Macy /*
402eda14cbcSMatt Macy  * Process a line of command output
403eda14cbcSMatt Macy  *
404eda14cbcSMatt Macy  * When running 'zpool iostat|status -c' the lines of output can either be
405eda14cbcSMatt Macy  * in the form of:
406eda14cbcSMatt Macy  *
407eda14cbcSMatt Macy  *	column_name=value
408eda14cbcSMatt Macy  *
409eda14cbcSMatt Macy  * Or just:
410eda14cbcSMatt Macy  *
411eda14cbcSMatt Macy  *	value
412eda14cbcSMatt Macy  *
413eda14cbcSMatt Macy  * Process the column_name (if any) and value.
414eda14cbcSMatt Macy  *
415eda14cbcSMatt Macy  * Returns 0 if line was processed, and there are more lines can still be
416eda14cbcSMatt Macy  * processed.
417eda14cbcSMatt Macy  *
418eda14cbcSMatt Macy  * Returns 1 if this was the last line to process, or error.
419eda14cbcSMatt Macy  */
420eda14cbcSMatt Macy static int
421eda14cbcSMatt Macy vdev_process_cmd_output(vdev_cmd_data_t *data, char *line)
422eda14cbcSMatt Macy {
423eda14cbcSMatt Macy 	char *col = NULL;
424eda14cbcSMatt Macy 	char *val = line;
425eda14cbcSMatt Macy 	char *equals;
426eda14cbcSMatt Macy 	char **tmp;
427eda14cbcSMatt Macy 
428eda14cbcSMatt Macy 	if (line == NULL)
429eda14cbcSMatt Macy 		return (1);
430eda14cbcSMatt Macy 
431eda14cbcSMatt Macy 	equals = strchr(line, '=');
432eda14cbcSMatt Macy 	if (equals != NULL) {
433eda14cbcSMatt Macy 		/*
434eda14cbcSMatt Macy 		 * We have a 'column=value' type line.  Split it into the
435eda14cbcSMatt Macy 		 * column and value strings by turning the '=' into a '\0'.
436eda14cbcSMatt Macy 		 */
437eda14cbcSMatt Macy 		*equals = '\0';
438eda14cbcSMatt Macy 		col = line;
439eda14cbcSMatt Macy 		val = equals + 1;
440eda14cbcSMatt Macy 	} else {
441eda14cbcSMatt Macy 		val = line;
442eda14cbcSMatt Macy 	}
443eda14cbcSMatt Macy 
444eda14cbcSMatt Macy 	/* Do we already have a column by this name?  If so, skip it. */
445eda14cbcSMatt Macy 	if (col != NULL) {
446eda14cbcSMatt Macy 		for (int i = 0; i < data->cols_cnt; i++) {
447eda14cbcSMatt Macy 			if (strcmp(col, data->cols[i]) == 0)
448eda14cbcSMatt Macy 				return (0); /* Duplicate, skip */
449eda14cbcSMatt Macy 		}
450eda14cbcSMatt Macy 	}
451eda14cbcSMatt Macy 
452eda14cbcSMatt Macy 	if (val != NULL) {
453eda14cbcSMatt Macy 		tmp = realloc(data->lines,
454eda14cbcSMatt Macy 		    (data->lines_cnt + 1) * sizeof (*data->lines));
455eda14cbcSMatt Macy 		if (tmp == NULL)
456eda14cbcSMatt Macy 			return (1);
457eda14cbcSMatt Macy 
458eda14cbcSMatt Macy 		data->lines = tmp;
459eda14cbcSMatt Macy 		data->lines[data->lines_cnt] = strdup(val);
460eda14cbcSMatt Macy 		data->lines_cnt++;
461eda14cbcSMatt Macy 	}
462eda14cbcSMatt Macy 
463eda14cbcSMatt Macy 	if (col != NULL) {
464eda14cbcSMatt Macy 		tmp = realloc(data->cols,
465eda14cbcSMatt Macy 		    (data->cols_cnt + 1) * sizeof (*data->cols));
466eda14cbcSMatt Macy 		if (tmp == NULL)
467eda14cbcSMatt Macy 			return (1);
468eda14cbcSMatt Macy 
469eda14cbcSMatt Macy 		data->cols = tmp;
470eda14cbcSMatt Macy 		data->cols[data->cols_cnt] = strdup(col);
471eda14cbcSMatt Macy 		data->cols_cnt++;
472eda14cbcSMatt Macy 	}
473eda14cbcSMatt Macy 
474eda14cbcSMatt Macy 	if (val != NULL && col == NULL)
475eda14cbcSMatt Macy 		return (1);
476eda14cbcSMatt Macy 
477eda14cbcSMatt Macy 	return (0);
478eda14cbcSMatt Macy }
479eda14cbcSMatt Macy 
480eda14cbcSMatt Macy /*
481eda14cbcSMatt Macy  * Run the cmd and store results in *data.
482eda14cbcSMatt Macy  */
483eda14cbcSMatt Macy static void
484eda14cbcSMatt Macy vdev_run_cmd(vdev_cmd_data_t *data, char *cmd)
485eda14cbcSMatt Macy {
486eda14cbcSMatt Macy 	int rc;
487eda14cbcSMatt Macy 	char *argv[2] = {cmd, 0};
488eda14cbcSMatt Macy 	char *env[5] = {"PATH=/bin:/sbin:/usr/bin:/usr/sbin", NULL, NULL, NULL,
489eda14cbcSMatt Macy 	    NULL};
490eda14cbcSMatt Macy 	char **lines = NULL;
491eda14cbcSMatt Macy 	int lines_cnt = 0;
492eda14cbcSMatt Macy 	int i;
493eda14cbcSMatt Macy 
494eda14cbcSMatt Macy 	/* Setup our custom environment variables */
495eda14cbcSMatt Macy 	rc = asprintf(&env[1], "VDEV_PATH=%s",
496eda14cbcSMatt Macy 	    data->path ? data->path : "");
497eda14cbcSMatt Macy 	if (rc == -1)
498eda14cbcSMatt Macy 		goto out;
499eda14cbcSMatt Macy 
500eda14cbcSMatt Macy 	rc = asprintf(&env[2], "VDEV_UPATH=%s",
501eda14cbcSMatt Macy 	    data->upath ? data->upath : "");
502eda14cbcSMatt Macy 	if (rc == -1)
503eda14cbcSMatt Macy 		goto out;
504eda14cbcSMatt Macy 
505eda14cbcSMatt Macy 	rc = asprintf(&env[3], "VDEV_ENC_SYSFS_PATH=%s",
506eda14cbcSMatt Macy 	    data->vdev_enc_sysfs_path ?
507eda14cbcSMatt Macy 	    data->vdev_enc_sysfs_path : "");
508eda14cbcSMatt Macy 	if (rc == -1)
509eda14cbcSMatt Macy 		goto out;
510eda14cbcSMatt Macy 
511eda14cbcSMatt Macy 	/* Run the command */
512eda14cbcSMatt Macy 	rc = libzfs_run_process_get_stdout_nopath(cmd, argv, env, &lines,
513eda14cbcSMatt Macy 	    &lines_cnt);
514eda14cbcSMatt Macy 	if (rc != 0)
515eda14cbcSMatt Macy 		goto out;
516eda14cbcSMatt Macy 
517eda14cbcSMatt Macy 	/* Process the output we got */
518eda14cbcSMatt Macy 	for (i = 0; i < lines_cnt; i++)
519eda14cbcSMatt Macy 		if (vdev_process_cmd_output(data, lines[i]) != 0)
520eda14cbcSMatt Macy 			break;
521eda14cbcSMatt Macy 
522eda14cbcSMatt Macy out:
523eda14cbcSMatt Macy 	if (lines != NULL)
524eda14cbcSMatt Macy 		libzfs_free_str_array(lines, lines_cnt);
525eda14cbcSMatt Macy 
526eda14cbcSMatt Macy 	/* Start with i = 1 since env[0] was statically allocated */
527eda14cbcSMatt Macy 	for (i = 1; i < ARRAY_SIZE(env); i++)
528eda14cbcSMatt Macy 		if (env[i] != NULL)
529eda14cbcSMatt Macy 			free(env[i]);
530eda14cbcSMatt Macy }
531eda14cbcSMatt Macy 
532eda14cbcSMatt Macy /*
533eda14cbcSMatt Macy  * Generate the search path for zpool iostat/status -c scripts.
534eda14cbcSMatt Macy  * The string returned must be freed.
535eda14cbcSMatt Macy  */
536eda14cbcSMatt Macy char *
537eda14cbcSMatt Macy zpool_get_cmd_search_path(void)
538eda14cbcSMatt Macy {
539eda14cbcSMatt Macy 	const char *env;
540eda14cbcSMatt Macy 	char *sp = NULL;
541eda14cbcSMatt Macy 
542eda14cbcSMatt Macy 	env = getenv("ZPOOL_SCRIPTS_PATH");
543eda14cbcSMatt Macy 	if (env != NULL)
544eda14cbcSMatt Macy 		return (strdup(env));
545eda14cbcSMatt Macy 
546eda14cbcSMatt Macy 	env = getenv("HOME");
547eda14cbcSMatt Macy 	if (env != NULL) {
548eda14cbcSMatt Macy 		if (asprintf(&sp, "%s/.zpool.d:%s",
549eda14cbcSMatt Macy 		    env, ZPOOL_SCRIPTS_DIR) != -1) {
550eda14cbcSMatt Macy 			return (sp);
551eda14cbcSMatt Macy 		}
552eda14cbcSMatt Macy 	}
553eda14cbcSMatt Macy 
554eda14cbcSMatt Macy 	if (asprintf(&sp, "%s", ZPOOL_SCRIPTS_DIR) != -1)
555eda14cbcSMatt Macy 		return (sp);
556eda14cbcSMatt Macy 
557eda14cbcSMatt Macy 	return (NULL);
558eda14cbcSMatt Macy }
559eda14cbcSMatt Macy 
560eda14cbcSMatt Macy /* Thread function run for each vdev */
561eda14cbcSMatt Macy static void
562eda14cbcSMatt Macy vdev_run_cmd_thread(void *cb_cmd_data)
563eda14cbcSMatt Macy {
564eda14cbcSMatt Macy 	vdev_cmd_data_t *data = cb_cmd_data;
565eda14cbcSMatt Macy 	char *cmd = NULL, *cmddup, *cmdrest;
566eda14cbcSMatt Macy 
567eda14cbcSMatt Macy 	cmddup = strdup(data->cmd);
568eda14cbcSMatt Macy 	if (cmddup == NULL)
569eda14cbcSMatt Macy 		return;
570eda14cbcSMatt Macy 
571eda14cbcSMatt Macy 	cmdrest = cmddup;
572eda14cbcSMatt Macy 	while ((cmd = strtok_r(cmdrest, ",", &cmdrest))) {
573eda14cbcSMatt Macy 		char *dir = NULL, *sp, *sprest;
574eda14cbcSMatt Macy 		char fullpath[MAXPATHLEN];
575eda14cbcSMatt Macy 
576eda14cbcSMatt Macy 		if (strchr(cmd, '/') != NULL)
577eda14cbcSMatt Macy 			continue;
578eda14cbcSMatt Macy 
579eda14cbcSMatt Macy 		sp = zpool_get_cmd_search_path();
580eda14cbcSMatt Macy 		if (sp == NULL)
581eda14cbcSMatt Macy 			continue;
582eda14cbcSMatt Macy 
583eda14cbcSMatt Macy 		sprest = sp;
584eda14cbcSMatt Macy 		while ((dir = strtok_r(sprest, ":", &sprest))) {
585eda14cbcSMatt Macy 			if (snprintf(fullpath, sizeof (fullpath),
586eda14cbcSMatt Macy 			    "%s/%s", dir, cmd) == -1)
587eda14cbcSMatt Macy 				continue;
588eda14cbcSMatt Macy 
589eda14cbcSMatt Macy 			if (access(fullpath, X_OK) == 0) {
590eda14cbcSMatt Macy 				vdev_run_cmd(data, fullpath);
591eda14cbcSMatt Macy 				break;
592eda14cbcSMatt Macy 			}
593eda14cbcSMatt Macy 		}
594eda14cbcSMatt Macy 		free(sp);
595eda14cbcSMatt Macy 	}
596eda14cbcSMatt Macy 	free(cmddup);
597eda14cbcSMatt Macy }
598eda14cbcSMatt Macy 
599eda14cbcSMatt Macy /* For each vdev in the pool run a command */
600eda14cbcSMatt Macy static int
601eda14cbcSMatt Macy for_each_vdev_run_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_vcdl)
602eda14cbcSMatt Macy {
603eda14cbcSMatt Macy 	vdev_cmd_data_list_t *vcdl = cb_vcdl;
604eda14cbcSMatt Macy 	vdev_cmd_data_t *data;
605eda14cbcSMatt Macy 	char *path = NULL;
606eda14cbcSMatt Macy 	char *vname = NULL;
607eda14cbcSMatt Macy 	char *vdev_enc_sysfs_path = NULL;
608eda14cbcSMatt Macy 	int i, match = 0;
609eda14cbcSMatt Macy 
610eda14cbcSMatt Macy 	if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
611eda14cbcSMatt Macy 		return (1);
612eda14cbcSMatt Macy 
613eda14cbcSMatt Macy 	nvlist_lookup_string(nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH,
614eda14cbcSMatt Macy 	    &vdev_enc_sysfs_path);
615eda14cbcSMatt Macy 
616eda14cbcSMatt Macy 	/* Spares show more than once if they're in use, so skip if exists */
617eda14cbcSMatt Macy 	for (i = 0; i < vcdl->count; i++) {
618eda14cbcSMatt Macy 		if ((strcmp(vcdl->data[i].path, path) == 0) &&
619eda14cbcSMatt Macy 		    (strcmp(vcdl->data[i].pool, zpool_get_name(zhp)) == 0)) {
620eda14cbcSMatt Macy 			/* vdev already exists, skip it */
621eda14cbcSMatt Macy 			return (0);
622eda14cbcSMatt Macy 		}
623eda14cbcSMatt Macy 	}
624eda14cbcSMatt Macy 
625eda14cbcSMatt Macy 	/* Check for selected vdevs here, if any */
626eda14cbcSMatt Macy 	for (i = 0; i < vcdl->vdev_names_count; i++) {
627eda14cbcSMatt Macy 		vname = zpool_vdev_name(g_zfs, zhp, nv, vcdl->cb_name_flags);
628eda14cbcSMatt Macy 		if (strcmp(vcdl->vdev_names[i], vname) == 0) {
629eda14cbcSMatt Macy 			free(vname);
630eda14cbcSMatt Macy 			match = 1;
631eda14cbcSMatt Macy 			break; /* match */
632eda14cbcSMatt Macy 		}
633eda14cbcSMatt Macy 		free(vname);
634eda14cbcSMatt Macy 	}
635eda14cbcSMatt Macy 
636eda14cbcSMatt Macy 	/* If we selected vdevs, and this isn't one of them, then bail out */
637eda14cbcSMatt Macy 	if (!match && vcdl->vdev_names_count)
638eda14cbcSMatt Macy 		return (0);
639eda14cbcSMatt Macy 
640eda14cbcSMatt Macy 	/*
641eda14cbcSMatt Macy 	 * Resize our array and add in the new element.
642eda14cbcSMatt Macy 	 */
643eda14cbcSMatt Macy 	if (!(vcdl->data = realloc(vcdl->data,
644eda14cbcSMatt Macy 	    sizeof (*vcdl->data) * (vcdl->count + 1))))
645eda14cbcSMatt Macy 		return (ENOMEM);	/* couldn't realloc */
646eda14cbcSMatt Macy 
647eda14cbcSMatt Macy 	data = &vcdl->data[vcdl->count];
648eda14cbcSMatt Macy 
649eda14cbcSMatt Macy 	data->pool = strdup(zpool_get_name(zhp));
650eda14cbcSMatt Macy 	data->path = strdup(path);
651eda14cbcSMatt Macy 	data->upath = zfs_get_underlying_path(path);
652eda14cbcSMatt Macy 	data->cmd = vcdl->cmd;
653eda14cbcSMatt Macy 	data->lines = data->cols = NULL;
654eda14cbcSMatt Macy 	data->lines_cnt = data->cols_cnt = 0;
655eda14cbcSMatt Macy 	if (vdev_enc_sysfs_path)
656eda14cbcSMatt Macy 		data->vdev_enc_sysfs_path = strdup(vdev_enc_sysfs_path);
657eda14cbcSMatt Macy 	else
658eda14cbcSMatt Macy 		data->vdev_enc_sysfs_path = NULL;
659eda14cbcSMatt Macy 
660eda14cbcSMatt Macy 	vcdl->count++;
661eda14cbcSMatt Macy 
662eda14cbcSMatt Macy 	return (0);
663eda14cbcSMatt Macy }
664eda14cbcSMatt Macy 
665eda14cbcSMatt Macy /* Get the names and count of the vdevs */
666eda14cbcSMatt Macy static int
667eda14cbcSMatt Macy all_pools_for_each_vdev_gather_cb(zpool_handle_t *zhp, void *cb_vcdl)
668eda14cbcSMatt Macy {
669eda14cbcSMatt Macy 	return (for_each_vdev(zhp, for_each_vdev_run_cb, cb_vcdl));
670eda14cbcSMatt Macy }
671eda14cbcSMatt Macy 
672eda14cbcSMatt Macy /*
673eda14cbcSMatt Macy  * Now that vcdl is populated with our complete list of vdevs, spawn
674eda14cbcSMatt Macy  * off the commands.
675eda14cbcSMatt Macy  */
676eda14cbcSMatt Macy static void
677eda14cbcSMatt Macy all_pools_for_each_vdev_run_vcdl(vdev_cmd_data_list_t *vcdl)
678eda14cbcSMatt Macy {
679eda14cbcSMatt Macy 	tpool_t *t;
680eda14cbcSMatt Macy 
681eda14cbcSMatt Macy 	t = tpool_create(1, 5 * sysconf(_SC_NPROCESSORS_ONLN), 0, NULL);
682eda14cbcSMatt Macy 	if (t == NULL)
683eda14cbcSMatt Macy 		return;
684eda14cbcSMatt Macy 
685eda14cbcSMatt Macy 	/* Spawn off the command for each vdev */
686eda14cbcSMatt Macy 	for (int i = 0; i < vcdl->count; i++) {
687eda14cbcSMatt Macy 		(void) tpool_dispatch(t, vdev_run_cmd_thread,
688eda14cbcSMatt Macy 		    (void *) &vcdl->data[i]);
689eda14cbcSMatt Macy 	}
690eda14cbcSMatt Macy 
691eda14cbcSMatt Macy 	/* Wait for threads to finish */
692eda14cbcSMatt Macy 	tpool_wait(t);
693eda14cbcSMatt Macy 	tpool_destroy(t);
694eda14cbcSMatt Macy }
695eda14cbcSMatt Macy 
696eda14cbcSMatt Macy /*
697eda14cbcSMatt Macy  * Run command 'cmd' on all vdevs in all pools in argv.  Saves the first line of
698eda14cbcSMatt Macy  * output from the command in vcdk->data[].line for all vdevs.  If you want
699eda14cbcSMatt Macy  * to run the command on only certain vdevs, fill in g_zfs, vdev_names,
700eda14cbcSMatt Macy  * vdev_names_count, and cb_name_flags.  Otherwise leave them as zero.
701eda14cbcSMatt Macy  *
702eda14cbcSMatt Macy  * Returns a vdev_cmd_data_list_t that must be freed with
703eda14cbcSMatt Macy  * free_vdev_cmd_data_list();
704eda14cbcSMatt Macy  */
705eda14cbcSMatt Macy vdev_cmd_data_list_t *
706eda14cbcSMatt Macy all_pools_for_each_vdev_run(int argc, char **argv, char *cmd,
707eda14cbcSMatt Macy     libzfs_handle_t *g_zfs, char **vdev_names, int vdev_names_count,
708eda14cbcSMatt Macy     int cb_name_flags)
709eda14cbcSMatt Macy {
710eda14cbcSMatt Macy 	vdev_cmd_data_list_t *vcdl;
711eda14cbcSMatt Macy 	vcdl = safe_malloc(sizeof (vdev_cmd_data_list_t));
712eda14cbcSMatt Macy 	vcdl->cmd = cmd;
713eda14cbcSMatt Macy 
714eda14cbcSMatt Macy 	vcdl->vdev_names = vdev_names;
715eda14cbcSMatt Macy 	vcdl->vdev_names_count = vdev_names_count;
716eda14cbcSMatt Macy 	vcdl->cb_name_flags = cb_name_flags;
717eda14cbcSMatt Macy 	vcdl->g_zfs = g_zfs;
718eda14cbcSMatt Macy 
719eda14cbcSMatt Macy 	/* Gather our list of all vdevs in all pools */
7207877fdebSMatt Macy 	for_each_pool(argc, argv, B_TRUE, NULL, B_FALSE,
721eda14cbcSMatt Macy 	    all_pools_for_each_vdev_gather_cb, vcdl);
722eda14cbcSMatt Macy 
723eda14cbcSMatt Macy 	/* Run command on all vdevs in all pools */
724eda14cbcSMatt Macy 	all_pools_for_each_vdev_run_vcdl(vcdl);
725eda14cbcSMatt Macy 
726eda14cbcSMatt Macy 	/*
727eda14cbcSMatt Macy 	 * vcdl->data[] now contains all the column names and values for each
728eda14cbcSMatt Macy 	 * vdev.  We need to process that into a master list of unique column
729eda14cbcSMatt Macy 	 * names, and figure out the width of each column.
730eda14cbcSMatt Macy 	 */
731eda14cbcSMatt Macy 	process_unique_cmd_columns(vcdl);
732eda14cbcSMatt Macy 
733eda14cbcSMatt Macy 	return (vcdl);
734eda14cbcSMatt Macy }
735eda14cbcSMatt Macy 
736eda14cbcSMatt Macy /*
737eda14cbcSMatt Macy  * Free the vdev_cmd_data_list_t created by all_pools_for_each_vdev_run()
738eda14cbcSMatt Macy  */
739eda14cbcSMatt Macy void
740eda14cbcSMatt Macy free_vdev_cmd_data_list(vdev_cmd_data_list_t *vcdl)
741eda14cbcSMatt Macy {
742eda14cbcSMatt Macy 	free(vcdl->uniq_cols);
743eda14cbcSMatt Macy 	free(vcdl->uniq_cols_width);
744eda14cbcSMatt Macy 
745eda14cbcSMatt Macy 	for (int i = 0; i < vcdl->count; i++) {
746eda14cbcSMatt Macy 		free(vcdl->data[i].path);
747eda14cbcSMatt Macy 		free(vcdl->data[i].pool);
748eda14cbcSMatt Macy 		free(vcdl->data[i].upath);
749eda14cbcSMatt Macy 
750eda14cbcSMatt Macy 		for (int j = 0; j < vcdl->data[i].lines_cnt; j++)
751eda14cbcSMatt Macy 			free(vcdl->data[i].lines[j]);
752eda14cbcSMatt Macy 
753eda14cbcSMatt Macy 		free(vcdl->data[i].lines);
754eda14cbcSMatt Macy 
755eda14cbcSMatt Macy 		for (int j = 0; j < vcdl->data[i].cols_cnt; j++)
756eda14cbcSMatt Macy 			free(vcdl->data[i].cols[j]);
757eda14cbcSMatt Macy 
758eda14cbcSMatt Macy 		free(vcdl->data[i].cols);
759eda14cbcSMatt Macy 		free(vcdl->data[i].vdev_enc_sysfs_path);
760eda14cbcSMatt Macy 	}
761eda14cbcSMatt Macy 	free(vcdl->data);
762eda14cbcSMatt Macy 	free(vcdl);
763eda14cbcSMatt Macy }
764