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