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