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