diff options
Diffstat (limited to 'cmd/zpool/zpool_iter.c')
-rw-r--r-- | cmd/zpool/zpool_iter.c | 266 |
1 files changed, 238 insertions, 28 deletions
diff --git a/cmd/zpool/zpool_iter.c b/cmd/zpool/zpool_iter.c index 7ce0ccf9e..94777f076 100644 --- a/cmd/zpool/zpool_iter.c +++ b/cmd/zpool/zpool_iter.c @@ -36,6 +36,7 @@ #include <libzfs.h> #include <sys/zfs_context.h> +#include <sys/wait.h> #include "zpool_util.h" @@ -321,41 +322,229 @@ for_each_vdev(zpool_handle_t *zhp, pool_vdev_iter_f func, void *data) return (for_each_vdev_cb(zhp, nvroot, func, data)); } +/* + * Process the vcdl->vdev_cmd_data[] array to figure out all the unique column + * names and their widths. When this function is done, vcdl->uniq_cols, + * vcdl->uniq_cols_cnt, and vcdl->uniq_cols_width will be filled in. + */ +static void +process_unique_cmd_columns(vdev_cmd_data_list_t *vcdl) +{ + char **uniq_cols = NULL, **tmp = NULL; + int *uniq_cols_width; + vdev_cmd_data_t *data; + int cnt = 0; + int k; + + /* For each vdev */ + for (int i = 0; i < vcdl->count; i++) { + data = &vcdl->data[i]; + /* For each column the vdev reported */ + for (int j = 0; j < data->cols_cnt; j++) { + /* Is this column in our list of unique column names? */ + for (k = 0; k < cnt; k++) { + if (strcmp(data->cols[j], uniq_cols[k]) == 0) + break; /* yes it is */ + } + if (k == cnt) { + /* No entry for column, add to list */ + tmp = realloc(uniq_cols, sizeof (*uniq_cols) * + (cnt + 1)); + if (tmp == NULL) + break; /* Nothing we can do... */ + uniq_cols = tmp; + uniq_cols[cnt] = data->cols[j]; + cnt++; + } + } + } + + /* + * We now have a list of all the unique column names. Figure out the + * max width of each column by looking at the column name and all its + * values. + */ + uniq_cols_width = safe_malloc(sizeof (*uniq_cols_width) * cnt); + for (int i = 0; i < cnt; i++) { + /* Start off with the column title's width */ + uniq_cols_width[i] = strlen(uniq_cols[i]); + /* For each vdev */ + for (int j = 0; j < vcdl->count; j++) { + /* For each of the vdev's values in a column */ + data = &vcdl->data[j]; + for (k = 0; k < data->cols_cnt; k++) { + /* Does this vdev have a value for this col? */ + if (strcmp(data->cols[k], uniq_cols[i]) == 0) { + /* Is the value width larger? */ + uniq_cols_width[i] = + MAX(uniq_cols_width[i], + strlen(data->lines[k])); + } + } + } + } + + vcdl->uniq_cols = uniq_cols; + vcdl->uniq_cols_cnt = cnt; + vcdl->uniq_cols_width = uniq_cols_width; +} + + +/* + * Process a line of command output + * + * When running 'zpool iostat|status -c' the lines of output can either be + * in the form of: + * + * column_name=value + * + * Or just: + * + * value + * + * Process the column_name (if any) and value. + * + * Returns 0 if line was processed, and there are more lines can still be + * processed. + * + * Returns 1 if this was the last line to process, or error. + */ +static int +vdev_process_cmd_output(vdev_cmd_data_t *data, char *line) +{ + char *col = NULL; + char *val = line; + char *equals; + char **tmp; + + if (line == NULL) + return (1); + + equals = strchr(line, '='); + if (equals != NULL) { + /* + * We have a 'column=value' type line. Split it into the + * column and value strings by turning the '=' into a '\0'. + */ + *equals = '\0'; + col = line; + val = equals + 1; + } else { + val = line; + } + + /* Do we already have a column by this name? If so, skip it. */ + if (col != NULL) { + for (int i = 0; i < data->cols_cnt; i++) { + if (strcmp(col, data->cols[i]) == 0) + return (0); /* Duplicate, skip */ + } + } + + if (val != NULL) { + tmp = realloc(data->lines, + (data->lines_cnt + 1) * sizeof (*data->lines)); + if (tmp == NULL) + return (1); + + data->lines = tmp; + data->lines[data->lines_cnt] = strdup(val); + data->lines_cnt++; + } + + if (col != NULL) { + tmp = realloc(data->cols, + (data->cols_cnt + 1) * sizeof (*data->cols)); + if (tmp == NULL) + return (1); + + data->cols = tmp; + data->cols[data->cols_cnt] = strdup(col); + data->cols_cnt++; + } + + if (val != NULL && col == NULL) + return (1); + + return (0); +} + +/* + * Run the cmd and store results in *data. + */ +static void +vdev_run_cmd(vdev_cmd_data_t *data, char *cmd) +{ + int rc; + char *argv[2] = {cmd, 0}; + char *env[5] = {"PATH=/bin:/sbin:/usr/bin:/usr/sbin", NULL, NULL, NULL, + NULL}; + char **lines = NULL; + int lines_cnt = 0; + int i; + + /* Setup our custom environment variables */ + rc = asprintf(&env[1], "VDEV_PATH=%s", + data->path ? data->path : ""); + if (rc == -1) + goto out; + + rc = asprintf(&env[2], "VDEV_UPATH=%s", + data->upath ? data->upath : ""); + if (rc == -1) + goto out; + + rc = asprintf(&env[3], "VDEV_ENC_SYSFS_PATH=%s", + data->vdev_enc_sysfs_path ? + data->vdev_enc_sysfs_path : ""); + if (rc == -1) + goto out; + + /* Run the command */ + rc = libzfs_run_process_get_stdout_nopath(cmd, argv, env, &lines, + &lines_cnt); + if (rc != 0) + goto out; + + /* Process the output we got */ + for (i = 0; i < lines_cnt; i++) + if (vdev_process_cmd_output(data, lines[i]) != 0) + break; + +out: + if (lines != NULL) + libzfs_free_str_array(lines, lines_cnt); + + /* Start with i = 1 since env[0] was statically allocated */ + for (i = 1; i < ARRAY_SIZE(env); i++) + if (env[i] != NULL) + free(env[i]); +} + /* Thread function run for each vdev */ static void vdev_run_cmd_thread(void *cb_cmd_data) { vdev_cmd_data_t *data = cb_cmd_data; - char *pos = NULL; - FILE *fp; - size_t len = 0; - char cmd[_POSIX_ARG_MAX]; - - /* Set our VDEV_PATH and VDEV_UPATH env vars and run command */ - if (snprintf(cmd, sizeof (cmd), "VDEV_PATH=%s && VDEV_UPATH=\"%s\" && " - "VDEV_ENC_SYSFS_PATH=\"%s\" && %s", data->path ? data->path : "", - data->upath ? data->upath : "", - data->vdev_enc_sysfs_path ? data->vdev_enc_sysfs_path : "", - data->cmd) >= sizeof (cmd)) { - /* Our string was truncated */ - return; - } + const char *sep = ","; + char *cmd = NULL, *cmddup, *rest; + char fullpath[MAXPATHLEN]; - fp = popen(cmd, "r"); - if (fp == NULL) + cmddup = strdup(data->cmd); + if (cmddup == NULL) return; - data->line = NULL; + rest = cmddup; + while ((cmd = strtok_r(rest, sep, &rest))) { + if (snprintf(fullpath, sizeof (fullpath), "%s/%s", + ZPOOL_SCRIPTS_DIR, cmd) == -1) + continue; - /* Save the first line of output from the command */ - if (getline(&data->line, &len, fp) != -1) { - /* Success. Remove newline from the end, if necessary. */ - if ((pos = strchr(data->line, '\n')) != NULL) - *pos = '\0'; - } else { - data->line = NULL; + /* Does the script exist in our zpool scripts dir? */ + if (access(fullpath, X_OK) == 0) + vdev_run_cmd(data, fullpath); } - pclose(fp); + free(cmddup); } /* For each vdev in the pool run a command */ @@ -412,6 +601,8 @@ for_each_vdev_run_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_vcdl) data->path = strdup(path); data->upath = zfs_get_underlying_path(path); data->cmd = vcdl->cmd; + data->lines = data->cols = NULL; + data->lines_cnt = data->cols_cnt = 0; if (vdev_enc_sysfs_path) data->vdev_enc_sysfs_path = strdup(vdev_enc_sysfs_path); else @@ -463,6 +654,7 @@ all_pools_for_each_vdev_run_vcdl(vdev_cmd_data_list_t *vcdl) taskq_wait(t); taskq_destroy(t); thread_fini(); + } /* @@ -495,6 +687,13 @@ all_pools_for_each_vdev_run(int argc, char **argv, char *cmd, /* Run command on all vdevs in all pools */ all_pools_for_each_vdev_run_vcdl(vcdl); + /* + * vcdl->data[] now contains all the column names and values for each + * vdev. We need to process that into a master list of unique column + * names, and figure out the width of each column. + */ + process_unique_cmd_columns(vcdl); + return (vcdl); } @@ -504,12 +703,23 @@ all_pools_for_each_vdev_run(int argc, char **argv, char *cmd, void free_vdev_cmd_data_list(vdev_cmd_data_list_t *vcdl) { - int i; - for (i = 0; i < vcdl->count; i++) { + free(vcdl->uniq_cols); + free(vcdl->uniq_cols_width); + + for (int i = 0; i < vcdl->count; i++) { free(vcdl->data[i].path); free(vcdl->data[i].pool); free(vcdl->data[i].upath); - free(vcdl->data[i].line); + + for (int j = 0; j < vcdl->data[i].lines_cnt; j++) + free(vcdl->data[i].lines[j]); + + free(vcdl->data[i].lines); + + for (int j = 0; j < vcdl->data[i].cols_cnt; j++) + free(vcdl->data[i].cols[j]); + + free(vcdl->data[i].cols); free(vcdl->data[i].vdev_enc_sysfs_path); } free(vcdl->data); |