aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/zpool/zpool_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/zpool/zpool_main.c')
-rw-r--r--cmd/zpool/zpool_main.c287
1 files changed, 251 insertions, 36 deletions
diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c
index cc6c18eed..4b67bfde0 100644
--- a/cmd/zpool/zpool_main.c
+++ b/cmd/zpool/zpool_main.c
@@ -45,6 +45,7 @@
#include <unistd.h>
#include <pwd.h>
#include <zone.h>
+#include <sys/wait.h>
#include <zfs_prop.h>
#include <sys/fs/zfs.h>
#include <sys/stat.h>
@@ -52,6 +53,7 @@
#include <sys/fm/util.h>
#include <sys/fm/protocol.h>
#include <sys/zfs_ioctl.h>
+
#include <math.h>
#include <libzfs.h>
@@ -314,10 +316,10 @@ get_usage(zpool_help_t idx)
"[-R root] [-F [-n]]\n"
"\t <pool | id> [newpool]\n"));
case HELP_IOSTAT:
- return (gettext("\tiostat [-c CMD] [-T d | u] [-ghHLpPvy] "
- "[[-lq]|[-r|-w]]\n"
- "\t [[pool ...]|[pool vdev ...]|[vdev ...]] "
- "[interval [count]]\n"));
+ return (gettext("\tiostat [[[-c [script1,script2,...]"
+ "[-lq]]|[-rw]] [-T d | u] [-ghHLpPvy]\n"
+ "\t [[pool ...]|[pool vdev ...]|[vdev ...]]"
+ " [interval [count]]\n"));
case HELP_LABELCLEAR:
return (gettext("\tlabelclear [-f] <vdev>\n"));
case HELP_LIST:
@@ -337,8 +339,8 @@ get_usage(zpool_help_t idx)
case HELP_SCRUB:
return (gettext("\tscrub [-s] <pool> ...\n"));
case HELP_STATUS:
- return (gettext("\tstatus [-c CMD] [-gLPvxD] [-T d|u] [pool]"
- " ... [interval [count]]\n"));
+ return (gettext("\tstatus [-c [script1,script2,...]] [-gLPvxD]"
+ "[-T d|u] [pool] ... [interval [count]]\n"));
case HELP_UPGRADE:
return (gettext("\tupgrade\n"
"\tupgrade -v\n"
@@ -1542,17 +1544,68 @@ typedef struct status_cbdata {
vdev_cmd_data_list_t *vcdl;
} status_cbdata_t;
-/* Print output line for specific vdev in a specific pool */
+/* Return 1 if string is NULL, empty, or whitespace; return 0 otherwise. */
+static int
+is_blank_str(char *str)
+{
+ while (str != NULL && *str != '\0') {
+ if (!isblank(*str))
+ return (0);
+ str++;
+ }
+ return (1);
+}
+
+/* Print command output lines for specific vdev in a specific pool */
static void
zpool_print_cmd(vdev_cmd_data_list_t *vcdl, const char *pool, char *path)
{
- int i;
+ vdev_cmd_data_t *data;
+ int i, j;
+ char *val;
+
for (i = 0; i < vcdl->count; i++) {
- if ((strcmp(vcdl->data[i].path, path) == 0) &&
- (strcmp(vcdl->data[i].pool, pool) == 0)) {
- printf("%s", vcdl->data[i].line);
- break;
+ if ((strcmp(vcdl->data[i].path, path) != 0) ||
+ (strcmp(vcdl->data[i].pool, pool) != 0)) {
+ /* Not the vdev we're looking for */
+ continue;
}
+
+ data = &vcdl->data[i];
+ /* Print out all the output values for this vdev */
+ for (j = 0; j < vcdl->uniq_cols_cnt; j++) {
+ val = NULL;
+ /* Does this vdev have values for this column? */
+ for (int k = 0; k < data->cols_cnt; k++) {
+ if (strcmp(data->cols[k],
+ vcdl->uniq_cols[j]) == 0) {
+ /* yes it does, record the value */
+ val = data->lines[k];
+ break;
+ }
+ }
+ /*
+ * Mark empty values with dashes to make output
+ * awk-able.
+ */
+ if (is_blank_str(val))
+ val = "-";
+
+ printf("%*s", vcdl->uniq_cols_width[j], val);
+ if (j < vcdl->uniq_cols_cnt - 1)
+ printf(" ");
+ }
+
+ /* Print out any values that aren't in a column at the end */
+ for (j = data->cols_cnt; j < data->lines_cnt; j++) {
+ /* Did we have any columns? If so print a spacer. */
+ if (vcdl->uniq_cols_cnt > 0)
+ printf(" ");
+
+ val = data->lines[j];
+ printf("%s", val ? val : "");
+ }
+ break;
}
}
@@ -2799,9 +2852,54 @@ print_iostat_labels(iostat_cbdata_t *cb, unsigned int force_column_width,
}
}
- printf("\n");
}
+
+/*
+ * print_cmd_columns - Print custom column titles from -c
+ *
+ * If the user specified the "zpool status|iostat -c" then print their custom
+ * column titles in the header. For example, print_cmd_columns() would print
+ * the " col1 col2" part of this:
+ *
+ * $ zpool iostat -vc 'echo col1=val1; echo col2=val2'
+ * ...
+ * capacity operations bandwidth
+ * pool alloc free read write read write col1 col2
+ * ---------- ----- ----- ----- ----- ----- ----- ---- ----
+ * mypool 269K 1008M 0 0 107 946
+ * mirror 269K 1008M 0 0 107 946
+ * sdb - - 0 0 102 473 val1 val2
+ * sdc - - 0 0 5 473 val1 val2
+ * ---------- ----- ----- ----- ----- ----- ----- ---- ----
+ */
+void
+print_cmd_columns(vdev_cmd_data_list_t *vcdl, int use_dashes)
+{
+ int i, j;
+ vdev_cmd_data_t *data = &vcdl->data[0];
+
+ if (vcdl->count == 0 || data == NULL)
+ return;
+
+ /*
+ * Each vdev cmd should have the same column names unless the user did
+ * something weird with their cmd. Just take the column names from the
+ * first vdev and assume it works for all of them.
+ */
+ for (i = 0; i < vcdl->uniq_cols_cnt; i++) {
+ printf(" ");
+ if (use_dashes) {
+ for (j = 0; j < vcdl->uniq_cols_width[i]; j++)
+ printf("-");
+ } else {
+ printf("%*s", vcdl->uniq_cols_width[i],
+ vcdl->uniq_cols[i]);
+ }
+ }
+}
+
+
/*
* Utility function to print out a line of dashes like:
*
@@ -2870,7 +2968,6 @@ print_iostat_dashes(iostat_cbdata_t *cb, unsigned int force_column_width,
"--------------------");
}
}
- printf("\n");
}
@@ -2912,12 +3009,22 @@ print_iostat_header_impl(iostat_cbdata_t *cb, unsigned int force_column_width,
print_iostat_labels(cb, force_column_width, iostat_top_labels);
+ printf("\n");
printf("%-*s", namewidth, title);
print_iostat_labels(cb, force_column_width, iostat_bottom_labels);
+ if (cb->vcdl != NULL)
+ print_cmd_columns(cb->vcdl, 0);
+
+ printf("\n");
print_iostat_separator_impl(cb, force_column_width);
+
+ if (cb->vcdl != NULL)
+ print_cmd_columns(cb->vcdl, 1);
+
+ printf("\n");
}
static void
@@ -3451,11 +3558,8 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv,
char *path;
if (nvlist_lookup_string(newnv, ZPOOL_CONFIG_PATH,
&path) == 0) {
- if (!(cb->cb_flags & IOS_ANYHISTO_M))
- printf(" ");
+ printf(" ");
zpool_print_cmd(cb->vcdl, zpool_get_name(zhp), path);
- if (cb->cb_flags & IOS_ANYHISTO_M)
- printf("\n");
}
}
@@ -3602,6 +3706,10 @@ print_iostat(zpool_handle_t *zhp, void *data)
if ((ret != 0) && !(cb->cb_flags & IOS_ANYHISTO_M) &&
!cb->cb_scripted && cb->cb_verbose && !cb->cb_vdev_names_count) {
print_iostat_separator(cb);
+ if (cb->vcdl != NULL) {
+ print_cmd_columns(cb->vcdl, 1);
+ }
+ printf("\n");
}
return (ret);
@@ -3990,11 +4098,72 @@ fsleep(float sec)
nanosleep(&req, NULL);
}
+/*
+ * Run one of the zpool status/iostat -c scripts with the help (-h) option and
+ * print the result.
+ *
+ * name: Short name of the script ('iostat').
+ * path: Full path to the script ('/usr/local/etc/zfs/zpool.d/iostat');
+ */
+static void
+print_zpool_script_help(char *name, char *path)
+{
+ char *argv[] = {path, "-h", NULL};
+ char **lines = NULL;
+ int lines_cnt = 0;
+ int rc;
+
+ rc = libzfs_run_process_get_stdout_nopath(path, argv, NULL, &lines,
+ &lines_cnt);
+ if (rc != 0 || lines == NULL || lines_cnt <= 0)
+ return;
+
+ for (int i = 0; i < lines_cnt; i++)
+ if (!is_blank_str(lines[i]))
+ printf(" %-14s %s\n", name, lines[i]);
+
+ libzfs_free_str_array(lines, lines_cnt);
+}
+
/*
- * zpool iostat [-c CMD] [-ghHLpPvy] [[-lq]|[-r|-w]] [-n name] [-T d|u]
- * [[ pool ...]|[pool vdev ...]|[vdev ...]]
- * [interval [count]]
+ * Go though the list of all the zpool status/iostat -c scripts, run their
+ * help option (-h), and print out the results.
+ */
+static void
+print_zpool_script_list(void)
+{
+ DIR *dir;
+ struct dirent *ent;
+ char fullpath[MAXPATHLEN];
+ struct stat dir_stat;
+
+ if ((dir = opendir(ZPOOL_SCRIPTS_DIR)) != NULL) {
+ printf("\n");
+ /* print all the files and directories within directory */
+ while ((ent = readdir(dir)) != NULL) {
+ sprintf(fullpath, "%s/%s", ZPOOL_SCRIPTS_DIR,
+ ent->d_name);
+
+ /* Print the scripts */
+ if (stat(fullpath, &dir_stat) == 0)
+ if (dir_stat.st_mode & S_IXUSR &&
+ S_ISREG(dir_stat.st_mode))
+ print_zpool_script_help(ent->d_name,
+ fullpath);
+ }
+ printf("\n");
+ closedir(dir);
+ } else {
+ fprintf(stderr, gettext("Can't open %s scripts dir\n"),
+ ZPOOL_SCRIPTS_DIR);
+ }
+}
+
+/*
+ * zpool iostat [[-c [script1,script2,...]] [-lq]|[-rw]] [-ghHLpPvy] [-n name]
+ * [-T d|u] [[ pool ...]|[pool vdev ...]|[vdev ...]]
+ * [interval [count]]
*
* -c CMD For each vdev, run command CMD
* -g Display guid for individual vdev name.
@@ -4046,7 +4215,20 @@ zpool_do_iostat(int argc, char **argv)
while ((c = getopt(argc, argv, "c:gLPT:vyhplqrwH")) != -1) {
switch (c) {
case 'c':
+ if (cmd != NULL) {
+ fprintf(stderr,
+ gettext("Can't set -c flag twice\n"));
+ exit(1);
+ }
+ if ((getuid() <= 0 || geteuid() <= 0) &&
+ !libzfs_envvar_is_set("ZPOOL_SCRIPTS_AS_ROOT")) {
+ fprintf(stderr, gettext(
+ "Can't run -c with root privileges "
+ "unless ZPOOL_SCRIPTS_AS_ROOT is set.\n"));
+ exit(1);
+ }
cmd = optarg;
+ verbose = B_TRUE;
break;
case 'g':
guid = B_TRUE;
@@ -4089,8 +4271,11 @@ zpool_do_iostat(int argc, char **argv)
break;
case '?':
if (optopt == 'c') {
- fprintf(stderr,
- gettext("Missing CMD for -c\n"));
+ fprintf(stderr, gettext(
+ "Current scripts in %s:\n"),
+ ZPOOL_SCRIPTS_DIR);
+ print_zpool_script_list();
+ exit(0);
} else {
fprintf(stderr,
gettext("invalid option '%c'\n"), optopt);
@@ -4185,10 +4370,10 @@ zpool_do_iostat(int argc, char **argv)
return (1);
}
- if ((l_histo || rq_histo) && (queues || latency)) {
+ if ((l_histo || rq_histo) && (cmd != NULL || latency || queues)) {
pool_list_free(list);
(void) fprintf(stderr,
- gettext("[-r|-w] isn't allowed with [-q|-l]\n"));
+ gettext("[-r|-w] isn't allowed with [-c|-l|-q]\n"));
usage(B_FALSE);
return (1);
}
@@ -4276,6 +4461,15 @@ zpool_do_iostat(int argc, char **argv)
if (timestamp_fmt != NODATE)
print_timestamp(timestamp_fmt);
+ if (cmd != NULL && cb.cb_verbose &&
+ !(cb.cb_flags & IOS_ANYHISTO_M)) {
+ cb.vcdl = all_pools_for_each_vdev_run(argc,
+ argv, cmd, g_zfs, cb.cb_vdev_names,
+ cb.cb_vdev_names_count, cb.cb_name_flags);
+ } else {
+ cb.vcdl = NULL;
+ }
+
/*
* If it's the first time and we're not skipping it,
* or either skip or verbose mode, print the header.
@@ -4294,16 +4488,9 @@ zpool_do_iostat(int argc, char **argv)
continue;
}
- if (cmd != NULL && cb.cb_verbose)
- cb.vcdl = all_pools_for_each_vdev_run(argc,
- argv, cmd, g_zfs, cb.cb_vdev_names,
- cb.cb_vdev_names_count, cb.cb_name_flags);
pool_list_iter(list, B_FALSE, print_iostat, &cb);
- if (cb.vcdl != NULL)
- free_vdev_cmd_data_list(cb.vcdl);
-
/*
* If there's more than one pool, and we're not in
* verbose mode (which prints a separator for us),
@@ -4318,7 +4505,14 @@ zpool_do_iostat(int argc, char **argv)
cb.cb_vdev_names_count)) &&
!cb.cb_scripted) {
print_iostat_separator(&cb);
+ if (cb.vcdl != NULL)
+ print_cmd_columns(cb.vcdl, 1);
+ printf("\n");
}
+
+ if (cb.vcdl != NULL)
+ free_vdev_cmd_data_list(cb.vcdl);
+
}
/*
@@ -6036,9 +6230,14 @@ status_callback(zpool_handle_t *zhp, void *data)
cbp->cb_namewidth = 10;
(void) printf(gettext("config:\n\n"));
- (void) printf(gettext("\t%-*s %-8s %5s %5s %5s\n"),
+ (void) printf(gettext("\t%-*s %-8s %5s %5s %5s"),
cbp->cb_namewidth, "NAME", "STATE", "READ", "WRITE",
"CKSUM");
+
+ if (cbp->vcdl != NULL)
+ print_cmd_columns(cbp->vcdl, 0);
+
+ printf("\n");
print_status_config(zhp, cbp, zpool_get_name(zhp), nvroot, 0,
B_FALSE);
@@ -6098,7 +6297,8 @@ status_callback(zpool_handle_t *zhp, void *data)
}
/*
- * zpool status [-c CMD] [-gLPvx] [-T d|u] [pool] ... [interval [count]]
+ * zpool status [-c [script1,script2,...]] [-gLPvx] [-T d|u] [pool] ...
+ * [interval [count]]
*
* -c CMD For each vdev, run command CMD
* -g Display guid for individual vdev name.
@@ -6125,6 +6325,18 @@ zpool_do_status(int argc, char **argv)
while ((c = getopt(argc, argv, "c:gLPvxDT:")) != -1) {
switch (c) {
case 'c':
+ if (cmd != NULL) {
+ fprintf(stderr,
+ gettext("Can't set -c flag twice\n"));
+ exit(1);
+ }
+ if ((getuid() <= 0 || geteuid() <= 0) &&
+ !libzfs_envvar_is_set("ZPOOL_SCRIPTS_AS_ROOT")) {
+ fprintf(stderr, gettext(
+ "Can't run -c with root privileges "
+ "unless ZPOOL_SCRIPTS_AS_ROOT is set.\n"));
+ exit(1);
+ }
cmd = optarg;
break;
case 'g':
@@ -6150,8 +6362,11 @@ zpool_do_status(int argc, char **argv)
break;
case '?':
if (optopt == 'c') {
- fprintf(stderr,
- gettext("Missing CMD for -c\n"));
+ fprintf(stderr, gettext(
+ "Current scripts in %s:\n"),
+ ZPOOL_SCRIPTS_DIR);
+ print_zpool_script_list();
+ exit(0);
} else {
fprintf(stderr,
gettext("invalid option '%c'\n"), optopt);