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