From 099700d9dff46309cdd16f4c4331daddb70d8570 Mon Sep 17 00:00:00 2001 From: Giuseppe Di Natale Date: Mon, 5 Jun 2017 13:52:15 -0400 Subject: [PATCH] zpool iostat/status -c improvements Users can now provide their own scripts to be run with 'zpool iostat/status -c'. User scripts should be placed in ~/.zpool.d to be included in zpool's default search path. Provide a script which can be used with 'zpool iostat|status -c' that will return the type of device (hdd, sdd, file). Provide a script to get various values from smartctl when using 'zpool iostat/status -c'. Allow users to define the ZPOOL_SCRIPTS_PATH environment variable which can be used to override the default 'zpool iostat/status -c' search path. Allow the ZPOOL_SCRIPTS_ENABLED environment variable to enable or disable 'zpool status/iostat -c' functionality. Use the new smart script to provide the serial command. Install /etc/sudoers.d/zfs file which contains the sudoer rule for smartctl as a sample. Allow 'zpool iostat/status -c' tests to run in tree. Reviewed-by: Tony Hutter Reviewed-by: Brian Behlendorf Signed-off-by: Giuseppe Di Natale Closes #6121 Closes #6153 --- cmd/zpool/Makefile.am | 38 ++++++ cmd/zpool/zpool.d/ata_err | 1 + cmd/zpool/zpool.d/cmd_to | 1 + cmd/zpool/zpool.d/defect | 1 + cmd/zpool/zpool.d/health | 1 + cmd/zpool/zpool.d/hours_on | 1 + cmd/zpool/zpool.d/lsblk | 7 +- cmd/zpool/zpool.d/media | 27 ++++ cmd/zpool/zpool.d/nonmed | 1 + cmd/zpool/zpool.d/off_ucor | 1 + cmd/zpool/zpool.d/pend_sec | 1 + cmd/zpool/zpool.d/pwr_cyc | 1 + cmd/zpool/zpool.d/r_proc | 1 + cmd/zpool/zpool.d/r_ucor | 1 + cmd/zpool/zpool.d/realloc | 1 + cmd/zpool/zpool.d/rep_ucor | 1 + cmd/zpool/zpool.d/serial | 2 +- cmd/zpool/zpool.d/ses | 8 +- cmd/zpool/zpool.d/smart | 123 ++++++++++++++++++ cmd/zpool/zpool.d/smartx | 1 + cmd/zpool/zpool.d/temp | 1 + cmd/zpool/zpool.d/w_proc | 1 + cmd/zpool/zpool.d/w_ucor | 1 + cmd/zpool/zpool_iter.c | 58 +++++++-- cmd/zpool/zpool_main.c | 66 +++++++--- cmd/zpool/zpool_util.h | 5 + configure.ac | 1 + etc/Makefile.am | 4 +- etc/sudoers.d/Makefile.am | 5 + etc/sudoers.d/zfs | 8 ++ man/man8/zpool.8 | 42 +++--- rpm/generic/zfs.spec.in | 2 + scripts/zfs-helpers.sh | 6 + tests/runfiles/linux.run | 7 +- .../cli_root/zpool_status/Makefile.am | 5 +- .../zpool_status/zpool_status_-c_disable.ksh | 54 ++++++++ .../zpool_status/zpool_status_-c_homedir.ksh | 76 +++++++++++ .../zpool_status_-c_searchpath.ksh | 88 +++++++++++++ .../cli_user/zpool_iostat/Makefile.am | 5 +- .../zpool_iostat/zpool_iostat_-c_disable.ksh | 54 ++++++++ .../zpool_iostat/zpool_iostat_-c_homedir.ksh | 76 +++++++++++ .../zpool_iostat_-c_searchpath.ksh | 88 +++++++++++++ zfs-script-config.sh.in | 1 + 43 files changed, 812 insertions(+), 61 deletions(-) create mode 120000 cmd/zpool/zpool.d/ata_err create mode 120000 cmd/zpool/zpool.d/cmd_to create mode 120000 cmd/zpool/zpool.d/defect create mode 120000 cmd/zpool/zpool.d/health create mode 120000 cmd/zpool/zpool.d/hours_on create mode 100755 cmd/zpool/zpool.d/media create mode 120000 cmd/zpool/zpool.d/nonmed create mode 120000 cmd/zpool/zpool.d/off_ucor create mode 120000 cmd/zpool/zpool.d/pend_sec create mode 120000 cmd/zpool/zpool.d/pwr_cyc create mode 120000 cmd/zpool/zpool.d/r_proc create mode 120000 cmd/zpool/zpool.d/r_ucor create mode 120000 cmd/zpool/zpool.d/realloc create mode 120000 cmd/zpool/zpool.d/rep_ucor create mode 100755 cmd/zpool/zpool.d/smart create mode 120000 cmd/zpool/zpool.d/smartx create mode 120000 cmd/zpool/zpool.d/temp create mode 120000 cmd/zpool/zpool.d/w_proc create mode 120000 cmd/zpool/zpool.d/w_ucor create mode 100644 etc/sudoers.d/Makefile.am create mode 100644 etc/sudoers.d/zfs create mode 100755 tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_-c_disable.ksh create mode 100755 tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_-c_homedir.ksh create mode 100755 tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_-c_searchpath.ksh create mode 100755 tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_-c_disable.ksh create mode 100755 tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_-c_homedir.ksh create mode 100755 tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_-c_searchpath.ksh diff --git a/cmd/zpool/Makefile.am b/cmd/zpool/Makefile.am index f905fb73db..6eff1d1432 100644 --- a/cmd/zpool/Makefile.am +++ b/cmd/zpool/Makefile.am @@ -36,12 +36,31 @@ dist_zpoolexec_SCRIPTS = \ zpool.d/label \ zpool.d/locate_led \ zpool.d/lsblk \ + zpool.d/media \ zpool.d/model \ zpool.d/serial \ zpool.d/ses \ zpool.d/size \ zpool.d/slaves \ zpool.d/slot \ + zpool.d/smart \ + zpool.d/smartx \ + zpool.d/temp \ + zpool.d/health \ + zpool.d/r_proc \ + zpool.d/w_proc \ + zpool.d/r_ucor \ + zpool.d/w_ucor \ + zpool.d/nonmed \ + zpool.d/defect \ + zpool.d/hours_on \ + zpool.d/realloc \ + zpool.d/rep_ucor \ + zpool.d/cmd_to \ + zpool.d/pend_sec \ + zpool.d/off_ucor \ + zpool.d/ata_err \ + zpool.d/pwr_cyc \ zpool.d/upath \ zpool.d/vendor @@ -55,12 +74,31 @@ zpoolconfdefaults = \ label \ locate_led \ lsblk \ + media \ model \ serial \ ses \ size \ slaves \ slot \ + smart \ + smartx \ + temp \ + health \ + r_proc \ + w_proc \ + r_ucor \ + w_ucor \ + nonmed \ + defect \ + hours_on \ + realloc \ + rep_ucor \ + cmd_to \ + pend_sec \ + off_ucor \ + ata_err \ + pwr_cyc \ upath \ vendor diff --git a/cmd/zpool/zpool.d/ata_err b/cmd/zpool/zpool.d/ata_err new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/ata_err @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/cmd_to b/cmd/zpool/zpool.d/cmd_to new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/cmd_to @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/defect b/cmd/zpool/zpool.d/defect new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/defect @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/health b/cmd/zpool/zpool.d/health new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/health @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/hours_on b/cmd/zpool/zpool.d/hours_on new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/hours_on @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/lsblk b/cmd/zpool/zpool.d/lsblk index fc0394a3d3..1cdef40494 100755 --- a/cmd/zpool/zpool.d/lsblk +++ b/cmd/zpool/zpool.d/lsblk @@ -38,16 +38,15 @@ # DISC-ZERO discard zeroes data # # If the script is run as just 'lsblk' then print out disk size, vendor, -# model number and serial number. +# and model number. helpstr=" label: Show filesystem label. model: Show disk model number. -serial: Show disk serial number. size: Show the disk capacity. vendor: Show the disk vendor. -lsblk: Show the disk size, vendor, model number, and serial number." +lsblk: Show the disk size, vendor, and model number." script=$(basename "$0") @@ -57,7 +56,7 @@ if [ "$1" = "-h" ] ; then fi if [ "$script" = "lsblk" ] ; then - list="size vendor model serial" + list="size vendor model" else list=$(echo "$script" | tr '[:upper:]' '[:lower:]') fi diff --git a/cmd/zpool/zpool.d/media b/cmd/zpool/zpool.d/media new file mode 100755 index 0000000000..05bc15918b --- /dev/null +++ b/cmd/zpool/zpool.d/media @@ -0,0 +1,27 @@ +#!/bin/sh +# +# Print out the type of device +# + +if [ "$1" = "-h" ] ; then + echo "Show whether a vdev is a file, hdd, or ssd." + exit +fi + +if [ -b "$VDEV_UPATH" ]; then + device=$(basename "$VDEV_UPATH") + val=$(cat "/sys/block/$device/queue/rotational" 2>/dev/null) + if [ "$val" = "0" ]; then + MEDIA="ssd" + fi + + if [ "$val" = "1" ]; then + MEDIA="hdd" + fi +else + if [ -f "$VDEV_UPATH" ]; then + MEDIA="file" + fi +fi + +echo "media=$MEDIA" diff --git a/cmd/zpool/zpool.d/nonmed b/cmd/zpool/zpool.d/nonmed new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/nonmed @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/off_ucor b/cmd/zpool/zpool.d/off_ucor new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/off_ucor @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/pend_sec b/cmd/zpool/zpool.d/pend_sec new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/pend_sec @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/pwr_cyc b/cmd/zpool/zpool.d/pwr_cyc new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/pwr_cyc @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/r_proc b/cmd/zpool/zpool.d/r_proc new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/r_proc @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/r_ucor b/cmd/zpool/zpool.d/r_ucor new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/r_ucor @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/realloc b/cmd/zpool/zpool.d/realloc new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/realloc @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/rep_ucor b/cmd/zpool/zpool.d/rep_ucor new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/rep_ucor @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/serial b/cmd/zpool/zpool.d/serial index 7d1e766add..94f22861f0 120000 --- a/cmd/zpool/zpool.d/serial +++ b/cmd/zpool/zpool.d/serial @@ -1 +1 @@ -lsblk \ No newline at end of file +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/ses b/cmd/zpool/zpool.d/ses index 10d5dcfd35..f6b7520dfb 100755 --- a/cmd/zpool/zpool.d/ses +++ b/cmd/zpool/zpool.d/ses @@ -6,10 +6,10 @@ helpstr=" enc: Show disk enclosure w:x:y:z value. slot: Show disk slot number as reported by the enclosure. -encdev: Show the /dev/sg* device for the enclosure associated with the disk slot. -fault_led: Show the value of the disk enclosure slot fault LED. -locate_led: Show the value of the disk enclosure slot locate LED. -ses: Show disk's enclosure, enclosure dev, slot number, and fault/locate LED values." +encdev: Show /dev/sg* device associated with the enclosure disk slot. +fault_led: Show value of the disk enclosure slot fault LED. +locate_led: Show value of the disk enclosure slot locate LED. +ses: Show disk's enc, enc device, slot, and fault/locate LED values." script=$(basename "$0") if [ "$1" = "-h" ] ; then diff --git a/cmd/zpool/zpool.d/smart b/cmd/zpool/zpool.d/smart new file mode 100755 index 0000000000..3721f30edd --- /dev/null +++ b/cmd/zpool/zpool.d/smart @@ -0,0 +1,123 @@ +#!/bin/sh +# +# Show SMART stats +# + +helpstr=" +smart: Show SMART temperature and error stats (specific to drive type) +smartx: Show SMART extended drive stats (specific to drive type). +temp: Show SMART drive temperature in celsius (all drives). +health: Show reported SMART status (all drives). +r_proc: Show SMART read GBytes processed over drive lifetime (SAS). +w_proc: Show SMART write GBytes processed over drive lifetime (SAS). +r_ucor: Show SMART read uncorrectable errors (SAS). +w_ucor: Show SMART write uncorrectable errors (SAS). +nonmed: Show SMART non-medium errors (SAS). +defect: Show SMART grown defect list (SAS). +hours_on: Show number of hours drive powered on (all drives). +realloc: Show SMART reallocated sectors count (ATA). +rep_ucor: Show SMART reported uncorrectable count (ATA). +cmd_to: Show SMART command timeout count (ATA). +pend_sec: Show SMART current pending sector count (ATA). +off_ucor: Show SMART offline uncorrectable errors (ATA). +ata_err: Show SMART ATA errors (ATA). +pwr_cyc: Show SMART power cycle count (ATA). +serial: Show disk serial number. +" + +script=$(basename "$0") + +if [ "$1" = "-h" ] ; then + echo "$helpstr" | grep "$script:" | tr -s '\t' | cut -f 2- + exit +fi + +smartctl_path=$(which smartctl) + +if [ -b "$VDEV_UPATH" ] && [ -x "$smartctl_path" ]; then + raw_out=$(eval "sudo $smartctl_path -a $VDEV_UPATH") + + # Are we a SAS or ATA drive? Look for the right line in smartctl: + # + # SAS: + # Transport protocol: SAS + # + # SATA: + # ATA Version is: 8 + # + type=$(echo "$raw_out" | grep -m 1 -Eo '^ATA|SAS$') + out=$(echo "$raw_out" | awk ' +# SAS specific +/read:/{print "rrd="$4"\nr_cor="$5"\nr_proc="$7"\nr_ucor="$8} +/write:/{print "rwr="$4"\nw_cor="$5"\nw_proc="$7"\nw_ucor="$8} +/Non-medium error count/{print "nonmed="$4} +/Elements in grown defect list/{print "defect="$6} + +# SAS common +/Drive Temperature:/{print "temp="$4} +# Status can be a long string, substitute spaces for '_' +/SMART Health Status:/{printf "health="; for(i=4;i<=NF-1;i++){printf "%s_", $i}; printf "%s\n", $i} +/number of hours powered up/{print "hours_on="$7} +/Serial number:/{print "serial="$3} + +# SATA specific +/Reallocated_Sector_Ct/{print "realloc="$10} +/Reported_Uncorrect/{print "rep_ucor="$10} +/Command_Timeout/{print "cmd_to="$10} +/Current_Pending_Sector/{print "pend_sec="$10} +/Offline_Uncorrectable/{print "off_ucor="$10} +/ATA Error Count:/{print "ata_err="$4} +/Power_Cycle_Count/{print "pwr_cyc="$10} + +# SATA common +/Temperature_Celsius/{print "temp="$10} +/SMART overall-health self-assessment test result:/{print "health="$6} +/Power_On_Hours/{print "hours_on="$10} +/Serial Number:/{print "serial="$3} + +END {ORS="\n"; print ""} +'); +fi + +# if type is not set by now, either we don't have a block device +# or smartctl failed. Either way, default to ATA and set out to +# nothing +if [ -z "$type" ]; then + type="ATA" + out= +fi + +case $script in +smart) + # Print temperature plus common predictors of drive failure + if [ "$type" = "SAS" ] ; then + scripts="temp|health|r_ucor|w_ucor" + elif [ "$type" = "ATA" ] ; then + scripts="temp|health|ata_err|realloc|rep_ucor|cmd_to|pend_sec|off_ucor" + fi + ;; +smartx) + # Print some other interesting stats + if [ "$type" = "SAS" ] ; then + scripts="hours_on|defect|nonmed|r_proc|w_proc" + elif [ "$type" = "ATA" ] ; then + scripts="hours_on|pwr_cyc" + fi + ;; +*) + scripts="$script" +esac + +with_vals=$(echo "$out" | grep -E "$scripts") +if [ ! -z "$with_vals" ]; then + echo "$with_vals" + without_vals=$(echo "$scripts" | tr "|" "\n" | + grep -v -E "$(echo "$with_vals" | + awk -F "=" '{print $1}')" | awk '{print $0"="}') +else + without_vals=$(echo "$scripts" | tr "|" "\n" | awk '{print $0"="}') +fi + +if [ ! -z "$without_vals" ]; then + echo "$without_vals" +fi diff --git a/cmd/zpool/zpool.d/smartx b/cmd/zpool/zpool.d/smartx new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/smartx @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/temp b/cmd/zpool/zpool.d/temp new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/temp @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/w_proc b/cmd/zpool/zpool.d/w_proc new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/w_proc @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool.d/w_ucor b/cmd/zpool/zpool.d/w_ucor new file mode 120000 index 0000000000..94f22861f0 --- /dev/null +++ b/cmd/zpool/zpool.d/w_ucor @@ -0,0 +1 @@ +smart \ No newline at end of file diff --git a/cmd/zpool/zpool_iter.c b/cmd/zpool/zpool_iter.c index 94777f076c..abb1b17988 100644 --- a/cmd/zpool/zpool_iter.c +++ b/cmd/zpool/zpool_iter.c @@ -521,28 +521,66 @@ out: free(env[i]); } +/* + * Generate the search path for zpool iostat/status -c scripts. + * The string returned must be freed. + */ +char * +zpool_get_cmd_search_path(void) +{ + const char *env; + char *sp = NULL; + + env = getenv("ZPOOL_SCRIPTS_PATH"); + if (env != NULL) + return (strdup(env)); + + env = getenv("HOME"); + if (env != NULL) { + if (asprintf(&sp, "%s/.zpool.d:%s", + env, ZPOOL_SCRIPTS_DIR) != -1) { + return (sp); + } + } + + if (asprintf(&sp, "%s", ZPOOL_SCRIPTS_DIR) != -1) + return (sp); + + return (NULL); +} + /* Thread function run for each vdev */ static void vdev_run_cmd_thread(void *cb_cmd_data) { vdev_cmd_data_t *data = cb_cmd_data; - const char *sep = ","; - char *cmd = NULL, *cmddup, *rest; - char fullpath[MAXPATHLEN]; + char *cmd = NULL, *cmddup, *cmdrest; cmddup = strdup(data->cmd); if (cmddup == NULL) return; - rest = cmddup; - while ((cmd = strtok_r(rest, sep, &rest))) { - if (snprintf(fullpath, sizeof (fullpath), "%s/%s", - ZPOOL_SCRIPTS_DIR, cmd) == -1) + cmdrest = cmddup; + while ((cmd = strtok_r(cmdrest, ",", &cmdrest))) { + char *dir = NULL, *sp, *sprest; + char fullpath[MAXPATHLEN]; + + sp = zpool_get_cmd_search_path(); + if (sp == NULL) continue; - /* Does the script exist in our zpool scripts dir? */ - if (access(fullpath, X_OK) == 0) - vdev_run_cmd(data, fullpath); + sprest = sp; + while ((dir = strtok_r(sprest, ":", &sprest))) { + if (snprintf(fullpath, sizeof (fullpath), + "%s/%s", dir, cmd) == -1) + continue; + + if (access(fullpath, X_OK) == 0) { + vdev_run_cmd(data, fullpath); + break; + } + } + free(sp); } free(cmddup); } diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index f99145da96..5fccd754b4 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -4190,25 +4190,22 @@ print_zpool_script_help(char *name, char *path) libzfs_free_str_array(lines, lines_cnt); } - /* - * Go though the list of all the zpool status/iostat -c scripts, run their + * Go though the zpool status/iostat -c scripts in the user's path, run their * help option (-h), and print out the results. */ static void -print_zpool_script_list(void) +print_zpool_dir_scripts(char *dirpath) { DIR *dir; struct dirent *ent; char fullpath[MAXPATHLEN]; struct stat dir_stat; - if ((dir = opendir(ZPOOL_SCRIPTS_DIR)) != NULL) { - printf("\n"); + if ((dir = opendir(dirpath)) != NULL) { /* print all the files and directories within directory */ while ((ent = readdir(dir)) != NULL) { - sprintf(fullpath, "%s/%s", ZPOOL_SCRIPTS_DIR, - ent->d_name); + sprintf(fullpath, "%s/%s", dirpath, ent->d_name); /* Print the scripts */ if (stat(fullpath, &dir_stat) == 0) @@ -4217,14 +4214,33 @@ print_zpool_script_list(void) 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); } } +/* + * Print out help text for all zpool status/iostat -c scripts. + */ +static void +print_zpool_script_list(char *subcommand) +{ + char *dir, *sp; + + printf(gettext("Available 'zpool %s -c' commands:\n"), subcommand); + + sp = zpool_get_cmd_search_path(); + if (sp == NULL) + return; + + dir = strtok(sp, ":"); + while (dir != NULL) { + print_zpool_dir_scripts(dir); + dir = strtok(NULL, ":"); + } + + free(sp); +} + /* * zpool iostat [[-c [script1,script2,...]] [-lq]|[-rw]] [-ghHLpPvy] [-n name] * [-T d|u] [[ pool ...]|[pool vdev ...]|[vdev ...]] @@ -4285,6 +4301,15 @@ zpool_do_iostat(int argc, char **argv) gettext("Can't set -c flag twice\n")); exit(1); } + + if (getenv("ZPOOL_SCRIPTS_ENABLED") != NULL && + !libzfs_envvar_is_set("ZPOOL_SCRIPTS_ENABLED")) { + fprintf(stderr, gettext( + "Can't run -c, disabled by " + "ZPOOL_SCRIPTS_ENABLED.\n")); + exit(1); + } + if ((getuid() <= 0 || geteuid() <= 0) && !libzfs_envvar_is_set("ZPOOL_SCRIPTS_AS_ROOT")) { fprintf(stderr, gettext( @@ -4336,10 +4361,7 @@ zpool_do_iostat(int argc, char **argv) break; case '?': if (optopt == 'c') { - fprintf(stderr, gettext( - "Current scripts in %s:\n"), - ZPOOL_SCRIPTS_DIR); - print_zpool_script_list(); + print_zpool_script_list("iostat"); exit(0); } else { fprintf(stderr, @@ -6427,6 +6449,15 @@ zpool_do_status(int argc, char **argv) gettext("Can't set -c flag twice\n")); exit(1); } + + if (getenv("ZPOOL_SCRIPTS_ENABLED") != NULL && + !libzfs_envvar_is_set("ZPOOL_SCRIPTS_ENABLED")) { + fprintf(stderr, gettext( + "Can't run -c, disabled by " + "ZPOOL_SCRIPTS_ENABLED.\n")); + exit(1); + } + if ((getuid() <= 0 || geteuid() <= 0) && !libzfs_envvar_is_set("ZPOOL_SCRIPTS_AS_ROOT")) { fprintf(stderr, gettext( @@ -6459,10 +6490,7 @@ zpool_do_status(int argc, char **argv) break; case '?': if (optopt == 'c') { - fprintf(stderr, gettext( - "Current scripts in %s:\n"), - ZPOOL_SCRIPTS_DIR); - print_zpool_script_list(); + print_zpool_script_list("status"); exit(0); } else { fprintf(stderr, diff --git a/cmd/zpool/zpool_util.h b/cmd/zpool/zpool_util.h index ab7662cfa7..aef2cff27d 100644 --- a/cmd/zpool/zpool_util.h +++ b/cmd/zpool/zpool_util.h @@ -44,6 +44,11 @@ uint_t num_logs(nvlist_t *nv); uint64_t array64_max(uint64_t array[], unsigned int len); int isnumber(char *str); +/* + * Misc utility functions + */ +char *zpool_get_cmd_search_path(void); + /* * Virtual device functions */ diff --git a/configure.ac b/configure.ac index 06b6662fd0..c52d72e3dd 100644 --- a/configure.ac +++ b/configure.ac @@ -65,6 +65,7 @@ AC_CONFIG_FILES([ etc/zfs/Makefile etc/systemd/Makefile etc/systemd/system/Makefile + etc/sudoers.d/Makefile etc/modules-load.d/Makefile man/Makefile man/man1/Makefile diff --git a/etc/Makefile.am b/etc/Makefile.am index a62678b4e5..28b955106e 100644 --- a/etc/Makefile.am +++ b/etc/Makefile.am @@ -1,2 +1,2 @@ -SUBDIRS = zfs $(ZFS_INIT_SYSTEMD) $(ZFS_INIT_SYSV) $(ZFS_MODULE_LOAD) -DIST_SUBDIRS = init.d zfs systemd modules-load.d +SUBDIRS = zfs sudoers.d $(ZFS_INIT_SYSTEMD) $(ZFS_INIT_SYSV) $(ZFS_MODULE_LOAD) +DIST_SUBDIRS = init.d zfs systemd modules-load.d sudoers.d diff --git a/etc/sudoers.d/Makefile.am b/etc/sudoers.d/Makefile.am new file mode 100644 index 0000000000..ca9186a7ea --- /dev/null +++ b/etc/sudoers.d/Makefile.am @@ -0,0 +1,5 @@ +sudoersddir = $(sysconfdir)/sudoers.d +sudoersd_DATA = zfs + +EXTRA_DIST = \ + $(top_srcdir)/etc/sudoers.d/zfs diff --git a/etc/sudoers.d/zfs b/etc/sudoers.d/zfs new file mode 100644 index 0000000000..f66ebad216 --- /dev/null +++ b/etc/sudoers.d/zfs @@ -0,0 +1,8 @@ +## +## Allow any user to run `zpool iostat/status -c smart` in order +## to read basic SMART health statistics for a pool. +## +## CAUTION: Any syntax error introduced here will break sudo. +## + +# ALL ALL = (root) NOPASSWD: /usr/sbin/smartctl -a /dev/[hsv]d[a-z0-9]* diff --git a/man/man8/zpool.8 b/man/man8/zpool.8 index ff3db440da..190b9dfc0f 100644 --- a/man/man8/zpool.8 +++ b/man/man8/zpool.8 @@ -1550,14 +1550,13 @@ Run a script (or scripts) on each vdev and include the output in zpool iostat .sp The \fB-c\fR option allows you to run script(s) for each vdev and display the output in zpool iostat. For security reasons, a user can only execute scripts -found in the //zfs/zpool.d directory as an unprivileged user. However, a -privileged user can run \fB-c\fR if they have the ZPOOL_SCRIPTS_AS_ROOT -environment variable set. If a script requires the use of a privileged -command (like smartctl) then it's recommended you allow the user access to it in -/etc/sudoers. For example, to allow user "zfsuser" access to "smartctl -a", add -the following to /etc/sudoers: - -zfsuser ALL=NOPASSWD: /usr/sbin/smartctl -a /dev/sd[a-z]*, NOEXEC: /usr/sbin/smartctl -a /dev/sd[a-z]*` +as an unprivileged user. By default, a user may run a script from ~/.zpool.d +or /etc/zfs/zpool.d. The default search path can be overriden by setting +the \fBZPOOL_SCRIPTS_PATH\fR environment variable. A privileged user can run +\fB-c\fR if they have the \fBZPOOL_SCRIPTS_AS_ROOT\fR environment variable set. +If a script requires the use of a privileged command (like \fBsmartctl(8)\fR) +then it's recommended you allow the user access to it in /etc/sudoers or add +the user to the /etc/sudoers.d/zfs file. If \fB-c\fR is passed without a script name, it prints a list of all scripts. \fB-c\fR also sets verbose mode (\fB-v\fR). @@ -2148,15 +2147,14 @@ If a scrub or resilver is in progress, this command reports the percentage done Run a script (or scripts) on each vdev and include the output in zpool status .sp The \fB-c\fR option allows you to run script(s) for each vdev and display the -output in zpool iostat. For security reasons, a user can only execute scripts -found in the //zfs/zpool.d directory as an unprivileged user. However, a -privileged user can run \fB-c\fR if they have the ZPOOL_SCRIPTS_AS_ROOT -environment variable set. If a script requires the use of a privileged -command (like smartctl) then it's recommended you allow the user access to it in -/etc/sudoers. For example, to allow user "zfsuser" access to "smartctl -a", add -the following to /etc/sudoers: - -zfsuser ALL=NOPASSWD: /usr/sbin/smartctl -a /dev/sd[a-z]*, NOEXEC: /usr/sbin/smartctl -a /dev/sd[a-z]*` +output in zpool status. For security reasons, a user can only execute scripts +as an unprivileged user. By default, a user may run a script from ~/.zpool.d +or /etc/zfs/zpool.d. The default search path can be overriden by setting +the \fBZPOOL_SCRIPTS_PATH\fR environment variable. A privileged user can run +\fB-c\fR if they have the \fBZPOOL_SCRIPTS_AS_ROOT\fR environment variable set. +If a script requires the use of a privileged command (like \fBsmartctl(8)\fR) +then it's recommended you allow the user access to it in /etc/sudoers or add +the user to the /etc/sudoers.d/zfs file. If \fB-c\fR is passed without a script name, it prints a list of all scripts. @@ -2727,6 +2725,16 @@ them on \fBzpool create\fR or \fBzpool add\fR by setting ZFS_VDEV_DEVID_OPT_OUT. .B "ZPOOL_SCRIPTS_AS_ROOT" Allow a privilaged user to run the \fBzpool status/iostat\fR with the \fB-c\fR option. Normally, only unprivilaged users are allowed to run \fB-c\fR. +.TP +.B "ZPOOL_SCRIPTS_PATH" +The search path for scripts when running \fBzpool status/iostat\fR with the \fB-c\fR +option. This is a colon-separated list of directories and overrides the default +~/.zpool.d and /etc/zfs/zpool.d search paths. +.TP +.B "ZPOOL_SCRIPTS_ENABLED" +Allow a user to run \fBzpool status/iostat\fR with the \fB-c\fR option. If +ZPOOL_SCRIPTS_ENABLED is not set, it is assumed that the user is allowed to +run \fBzpool status/iostat -c\fR. .SH SEE ALSO .sp diff --git a/rpm/generic/zfs.spec.in b/rpm/generic/zfs.spec.in index 2ad4b693c1..c62d76346e 100644 --- a/rpm/generic/zfs.spec.in +++ b/rpm/generic/zfs.spec.in @@ -181,6 +181,7 @@ Requires: bc Requires: ksh Requires: fio Requires: acl +Requires: sudo Requires: sysstat %description test @@ -290,6 +291,7 @@ exit 0 %config(noreplace) %{_initconfdir}/zfs %endif %config(noreplace) %{_sysconfdir}/%{name} +%attr(440, root, root) %config(noreplace) %{_sysconfdir}/sudoers.d/* %files -n libzpool2 %{_libdir}/libzpool.so.* diff --git a/scripts/zfs-helpers.sh b/scripts/zfs-helpers.sh index 0354cc0ed0..d2da575f48 100755 --- a/scripts/zfs-helpers.sh +++ b/scripts/zfs-helpers.sh @@ -14,6 +14,7 @@ # --with-mounthelperdir=DIR install mount.zfs in dir [/sbin] # --with-udevdir=DIR install udev helpers [default=check] # --with-udevruledir=DIR install udev rules [default=UDEVDIR/rules.d] +# --sysconfdir=DIR install zfs configuration files [PREFIX/etc] # basedir="$(dirname $0)" @@ -96,6 +97,7 @@ if [ "$VERBOSE" ]; then echo "udevdir: $udevdir" echo "udevruledir: $udevruledir" echo "mounthelperdir: $mounthelperdir" + echo "sysconfdir: $sysconfdir" echo "DRYRUN: $DRYRUN" echo fi @@ -114,6 +116,7 @@ install() { msg "ln -s $src $dst" if [ ! "$DRYRUN" ]; then + mkdir -p $(dirname $dst) &>/dev/null ln -s $src $dst fi fi @@ -125,6 +128,7 @@ remove() { if [ -h $dst ]; then msg "rm $dst" rm $dst + rmdir $(dirname $dst) &>/dev/null fi } @@ -136,6 +140,7 @@ if [ ${INSTALL} ]; then install $UDEVRULEDIR/60-zvol.rules $udevruledir/60-zvol.rules install $UDEVRULEDIR/69-vdev.rules $udevruledir/69-vdev.rules install $UDEVRULEDIR/90-zfs.rules $udevruledir/90-zfs.rules + install $CMDDIR/zpool/zpool.d $sysconfdir/zfs/zpool.d else remove $mounthelperdir/mount.zfs remove $mounthelperdir/fsck.zfs @@ -144,6 +149,7 @@ else remove $udevruledir/60-zvol.rules remove $udevruledir/69-vdev.rules remove $udevruledir/90-zfs.rules + remove $sysconfdir/zfs/zpool.d fi exit 0 diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run index 674684f1fd..115b9b025f 100644 --- a/tests/runfiles/linux.run +++ b/tests/runfiles/linux.run @@ -279,7 +279,9 @@ pre = post = [tests/functional/cli_root/zpool_status] -tests = ['zpool_status_001_pos', 'zpool_status_002_pos','zpool_status_003_pos'] +tests = ['zpool_status_001_pos', 'zpool_status_002_pos','zpool_status_003_pos', + 'zpool_status_-c_disable', 'zpool_status_-c_homedir', + 'zpool_status_-c_searchpath'] user = [tests/functional/cli_root/zpool_sync] @@ -318,7 +320,8 @@ user = [tests/functional/cli_user/zpool_iostat] tests = ['zpool_iostat_001_neg', 'zpool_iostat_002_pos', 'zpool_iostat_003_neg', 'zpool_iostat_004_pos', - 'zpool_iostat_005_pos'] + 'zpool_iostat_005_pos', 'zpool_iostat_-c_disable', + 'zpool_iostat_-c_homedir', 'zpool_iostat_-c_searchpath'] user = [tests/functional/cli_user/zpool_list] diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_status/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zpool_status/Makefile.am index 747ec1dfa2..aab4de0e7c 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zpool_status/Makefile.am +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_status/Makefile.am @@ -4,4 +4,7 @@ dist_pkgdata_SCRIPTS = \ cleanup.ksh \ zpool_status_001_pos.ksh \ zpool_status_002_pos.ksh \ - zpool_status_003_pos.ksh + zpool_status_003_pos.ksh \ + zpool_status_-c_disable.ksh \ + zpool_status_-c_homedir.ksh \ + zpool_status_-c_searchpath.ksh diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_-c_disable.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_-c_disable.ksh new file mode 100755 index 0000000000..c8105fb4a5 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_-c_disable.ksh @@ -0,0 +1,54 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2017 by Lawrence Livermore National Security, LLC. +# + +# DESCRIPTION: +# Verify zpool status command mode (-c) respects ZPOOL_SCRIPTS_ENABLED. +# +# STRATEGY: +# 1. Set ZPOOL_SCRIPTS_ENABLED to 0, disabling zpool status -c +# 2. zpool status -c must not run successfully +# 3. Set ZPOOL_SCRIPTS_ENABLED to 1, enabling zpool status -c +# 4. zpool status -c must run successfully +# 5. Unset ZPOOL_SCRIPTS_ENABLED, enabling zpool status -c +# 6. zpool status -c must run successfully + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/include/zpool_script.shlib + +verify_runnable "both" + +log_assert "zpool status -c properly handles ZPOOL_SCRIPTS_ENABLED" + +export ZPOOL_SCRIPTS_ENABLED=0 +log_mustnot zpool status -c media + +export ZPOOL_SCRIPTS_ENABLED=1 +log_must zpool status -c media + +unset ZPOOL_SCRIPTS_ENABLED +log_must zpool status -c media + +log_pass "zpool status -c properly handles ZPOOL_SCRIPTS_ENABLED passed" diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_-c_homedir.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_-c_homedir.ksh new file mode 100755 index 0000000000..4cc3deb6da --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_-c_homedir.ksh @@ -0,0 +1,76 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2017 by Lawrence Livermore National Security, LLC. +# + +# DESCRIPTION: +# Verify zpool status command mode (-c) works with scripts in user's +# home directory. +# +# STRATEGY: +# 1. Change HOME to /var/tmp +# 2. Make a simple script that echos a key value pair +# in /var/tmp/.zpool.d +# 3. Make sure it can be run with -c +# 4. Remove the script we created + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/include/zpool_script.shlib + +verify_runnable "both" + +# In tree testing sets this variable, we need to unset it +# to restore zpool's search path. +unset ZPOOL_SCRIPTS_PATH + +# change HOME +export HOME="$TEST_BASE_DIR" +typeset USER_SCRIPT_FULL="$HOME/.zpool.d/userscript" + +function cleanup +{ + log_must rm -rf "$HOME/.zpool.d" +} + +log_assert "zpool status -c can run scripts from ~/.zpool.d" + +if [ -e "$USER_SCRIPT_FULL" ]; then + log_fail "$USER_SCRIPT_FULL already exists." +fi + +log_onexit cleanup + +# create simple script +log_must mkdir -p "$HOME/.zpool.d" +cat > "$USER_SCRIPT_FULL" << EOF +#!/bin/sh +echo "USRCOL=USRVAL" +EOF +log_must chmod +x "$USER_SCRIPT_FULL" + +# test that we can run the script +typeset USER_SCRIPT=$(basename "$USER_SCRIPT_FULL") +test_zpool_script "$USER_SCRIPT" "$TESTPOOL" "zpool status -P -c" + +log_pass "zpool status -c can run scripts from ~/.zpool.d passed" diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_-c_searchpath.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_-c_searchpath.ksh new file mode 100755 index 0000000000..a075b9a0c1 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_-c_searchpath.ksh @@ -0,0 +1,88 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2017 by Lawrence Livermore National Security, LLC. +# + +# DESCRIPTION: +# Verify zpool status command mode (-c) works with ZPOOL_SCRIPTS_PATH +# defined. +# +# STRATEGY: +# 1. Set ZPOOL_SCRIPTS_PATH to contain a couple of non-default dirs +# 2. Make a simple script that echos a key value pair in each dir +# 3. Make sure scripts can be run with -c +# 4. Remove the scripts we created + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/include/zpool_script.shlib + +verify_runnable "both" + +typeset SCRIPT_1="$TEST_BASE_DIR/scripts1/test1" +typeset SCRIPT_2="$TEST_BASE_DIR/scripts2/test2" + +function cleanup +{ + log_must rm -rf $(dirname "$SCRIPT_1") + log_must rm -rf $(dirname "$SCRIPT_2") +} + +log_assert "zpool status -c can run scripts from custom search path" + +if [ -e "$SCRIPT_1" ]; then + log_fail "$SCRIPT_1 already exists." +fi + +if [ -e "$SCRIPT_2" ]; then + log_fail "$SCRIPT_2 already exists." +fi + +log_onexit cleanup + +# change zpool status search path +export ZPOOL_SCRIPTS_PATH="$(dirname $SCRIPT_1):$(dirname $SCRIPT_2)" + +# create simple script in each dir +log_must mkdir -p $(dirname "$SCRIPT_1") +cat > "$SCRIPT_1" << EOF +#!/bin/sh +echo "USRCOL1=USRVAL1" +EOF +log_must chmod +x "$SCRIPT_1" + +log_must mkdir -p $(dirname "$SCRIPT_2") +cat > "$SCRIPT_2" << EOF +#!/bin/sh +echo "USRCOL2=USRVAL2" +EOF +log_must chmod +x "$SCRIPT_2" + +# test that we can run the scripts +typeset CMD_1=$(basename "$SCRIPT_1") +typeset CMD_2=$(basename "$SCRIPT_2") +test_zpool_script "$CMD_1" "$TESTPOOL" "zpool status -P -c" +test_zpool_script "$CMD_2" "$TESTPOOL" "zpool status -P -c" +test_zpool_script "$CMD_2,$CMD_1" "$TESTPOOL" "zpool status -P -c" + +log_pass "zpool status -c can run scripts from custom search path passed" diff --git a/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/Makefile.am b/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/Makefile.am index cadf9439a9..5ee30eafc3 100644 --- a/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/Makefile.am +++ b/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/Makefile.am @@ -6,4 +6,7 @@ dist_pkgdata_SCRIPTS = \ zpool_iostat_002_pos.ksh \ zpool_iostat_003_neg.ksh \ zpool_iostat_004_pos.ksh \ - zpool_iostat_005_pos.ksh + zpool_iostat_005_pos.ksh \ + zpool_iostat_-c_disable.ksh \ + zpool_iostat_-c_searchpath.ksh \ + zpool_iostat_-c_homedir.ksh diff --git a/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_-c_disable.ksh b/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_-c_disable.ksh new file mode 100755 index 0000000000..0d64eb0ec6 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_-c_disable.ksh @@ -0,0 +1,54 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2017 by Lawrence Livermore National Security, LLC. +# + +# DESCRIPTION: +# Verify zpool iostat command mode (-c) respects ZPOOL_SCRIPTS_ENABLED. +# +# STRATEGY: +# 1. Set ZPOOL_SCRIPTS_ENABLED to 0, disabling zpool iostat -c +# 2. zpool iostat -c must not run successfully +# 3. Set ZPOOL_SCRIPTS_ENABLED to 1, enabling zpool iostat -c +# 4. zpool iostat -c must run successfully +# 5. Unset ZPOOL_SCRIPTS_ENABLED, enabling zpool iostat -c +# 6. zpool iostat -c must run successfully + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/include/zpool_script.shlib + +verify_runnable "both" + +log_assert "zpool iostat -c properly handles ZPOOL_SCRIPTS_ENABLED" + +export ZPOOL_SCRIPTS_ENABLED=0 +log_mustnot zpool iostat -c media + +export ZPOOL_SCRIPTS_ENABLED=1 +log_must zpool iostat -c media + +unset ZPOOL_SCRIPTS_ENABLED +log_must zpool iostat -c media + +log_pass "zpool iostat -c properly handles ZPOOL_SCRIPTS_ENABLED passed" diff --git a/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_-c_homedir.ksh b/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_-c_homedir.ksh new file mode 100755 index 0000000000..5cb50fde6f --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_-c_homedir.ksh @@ -0,0 +1,76 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2017 by Lawrence Livermore National Security, LLC. +# + +# DESCRIPTION: +# Verify zpool iostat command mode (-c) works with scripts in user's +# home directory. +# +# STRATEGY: +# 1. Change HOME to /var/tmp +# 2. Make a simple script that echos a key value pair +# in /var/tmp/.zpool.d +# 3. Make sure it can be run with -c +# 4. Remove the script we created + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/include/zpool_script.shlib + +verify_runnable "both" + +# In tree testing sets this variable, we need to unset it +# to restore zpool's search path. +unset ZPOOL_SCRIPTS_PATH + +# change HOME +export HOME="$TEST_BASE_DIR" +typeset USER_SCRIPT_FULL="$HOME/.zpool.d/userscript" + +function cleanup +{ + log_must rm -rf "$HOME/.zpool.d" +} + +log_assert "zpool iostat -c can run scripts from ~/.zpool.d" + +if [ -e "$USER_SCRIPT_FULL" ]; then + log_fail "$USER_SCRIPT_FULL already exists." +fi + +log_onexit cleanup + +# create simple script +log_must mkdir -p "$HOME/.zpool.d" +cat > "$USER_SCRIPT_FULL" << EOF +#!/bin/sh +echo "USRCOL=USRVAL" +EOF +log_must chmod +x "$USER_SCRIPT_FULL" + +# test that we can run the script +typeset USER_SCRIPT=$(basename "$USER_SCRIPT_FULL") +test_zpool_script "$USER_SCRIPT" "$TESTPOOL" "zpool iostat -P -c" + +log_pass "zpool iostat -c can run scripts from ~/.zpool.d passed" diff --git a/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_-c_searchpath.ksh b/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_-c_searchpath.ksh new file mode 100755 index 0000000000..1197ea2d11 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_user/zpool_iostat/zpool_iostat_-c_searchpath.ksh @@ -0,0 +1,88 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2017 by Lawrence Livermore National Security, LLC. +# + +# DESCRIPTION: +# Verify zpool iostat command mode (-c) works with ZPOOL_SCRIPTS_PATH +# defined. +# +# STRATEGY: +# 1. Set ZPOOL_SCRIPTS_PATH to contain a couple of non-default dirs +# 2. Make a simple script that echos a key value pair in each dir +# 3. Make sure scripts can be run with -c +# 4. Remove the scripts we created + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/include/zpool_script.shlib + +verify_runnable "both" + +typeset SCRIPT_1="$TEST_BASE_DIR/scripts1/test1" +typeset SCRIPT_2="$TEST_BASE_DIR/scripts2/test2" + +function cleanup +{ + log_must rm -rf $(dirname "$SCRIPT_1") + log_must rm -rf $(dirname "$SCRIPT_2") +} + +log_assert "zpool iostat -c can run scripts from custom search path" + +if [ -e "$SCRIPT_1" ]; then + log_fail "$SCRIPT_1 already exists." +fi + +if [ -e "$SCRIPT_2" ]; then + log_fail "$SCRIPT_2 already exists." +fi + +log_onexit cleanup + +# change zpool iostat search path +export ZPOOL_SCRIPTS_PATH="$(dirname $SCRIPT_1):$(dirname $SCRIPT_2)" + +# create simple script in each dir +log_must mkdir -p $(dirname "$SCRIPT_1") +cat > "$SCRIPT_1" << EOF +#!/bin/sh +echo "USRCOL1=USRVAL1" +EOF +log_must chmod +x "$SCRIPT_1" + +log_must mkdir -p $(dirname "$SCRIPT_2") +cat > "$SCRIPT_2" << EOF +#!/bin/sh +echo "USRCOL2=USRVAL2" +EOF +log_must chmod +x "$SCRIPT_2" + +# test that we can run the scripts +typeset CMD_1=$(basename "$SCRIPT_1") +typeset CMD_2=$(basename "$SCRIPT_2") +test_zpool_script "$CMD_1" "$TESTPOOL" "zpool iostat -P -c" +test_zpool_script "$CMD_2" "$TESTPOOL" "zpool iostat -P -c" +test_zpool_script "$CMD_2,$CMD_1" "$TESTPOOL" "zpool iostat -P -c" + +log_pass "zpool iostat -c can run scripts from custom search path passed" diff --git a/zfs-script-config.sh.in b/zfs-script-config.sh.in index 593b4d00e0..f1006b3f7f 100644 --- a/zfs-script-config.sh.in +++ b/zfs-script-config.sh.in @@ -24,6 +24,7 @@ export UDEVRULEDIR=${BUILDDIR}/udev/rules.d export ZEDLET_ETC_DIR=${SRCDIR}/cmd/zed/zed.d export ZEDLET_LIBEXEC_DIR=${SRCDIR}/cmd/zed/zed.d export ZPOOL_SCRIPT_DIR=${SRCDIR}/cmd/zpool/zpool.d +export ZPOOL_SCRIPTS_PATH=${SRCDIR}/cmd/zpool/zpool.d export ZDB=${CMDDIR}/zdb/zdb export ZFS=${CMDDIR}/zfs/zfs