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