summaryrefslogtreecommitdiffstats
path: root/cmd/zpool/zpool_iter.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/zpool/zpool_iter.c')
-rw-r--r--cmd/zpool/zpool_iter.c266
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);