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