diff --git a/Makefile.am b/Makefile.am index 689816bdca23..4fd3f9b3d62e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,262 +1,255 @@ +include $(top_srcdir)/config/Shellcheck.am + ACLOCAL_AMFLAGS = -I config SUBDIRS = include if BUILD_LINUX SUBDIRS += rpm endif if CONFIG_USER SUBDIRS += man scripts lib tests cmd etc contrib if BUILD_LINUX SUBDIRS += udev endif endif if CONFIG_KERNEL SUBDIRS += module extradir = $(prefix)/src/zfs-$(VERSION) extra_HEADERS = zfs.release.in zfs_config.h.in if BUILD_LINUX kerneldir = $(prefix)/src/zfs-$(VERSION)/$(LINUX_VERSION) nodist_kernel_HEADERS = zfs.release zfs_config.h module/$(LINUX_SYMBOLS) endif endif AUTOMAKE_OPTIONS = foreign EXTRA_DIST = autogen.sh copy-builtin EXTRA_DIST += config/config.awk config/rpm.am config/deb.am config/tgz.am EXTRA_DIST += AUTHORS CODE_OF_CONDUCT.md COPYRIGHT LICENSE META NEWS NOTICE EXTRA_DIST += README.md RELEASES.md EXTRA_DIST += module/lua/README.zfs module/os/linux/spl/README.md # Include all the extra licensing information for modules EXTRA_DIST += module/icp/algs/skein/THIRDPARTYLICENSE EXTRA_DIST += module/icp/algs/skein/THIRDPARTYLICENSE.descrip EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman.descrip EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl.descrip EXTRA_DIST += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.cryptogams EXTRA_DIST += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.cryptogams.descrip EXTRA_DIST += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.openssl EXTRA_DIST += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.openssl.descrip EXTRA_DIST += module/os/linux/spl/THIRDPARTYLICENSE.gplv2 EXTRA_DIST += module/os/linux/spl/THIRDPARTYLICENSE.gplv2.descrip EXTRA_DIST += module/zfs/THIRDPARTYLICENSE.cityhash EXTRA_DIST += module/zfs/THIRDPARTYLICENSE.cityhash.descrip @CODE_COVERAGE_RULES@ GITREV = include/zfs_gitrev.h PHONY = gitrev gitrev: $(AM_V_GEN)$(top_srcdir)/scripts/make_gitrev.sh $(GITREV) all: gitrev # Double-colon rules are allowed; there are multiple independent definitions. maintainer-clean-local:: -$(RM) $(GITREV) distclean-local:: -$(RM) -R autom4te*.cache build -find . \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS \ -o -name .pc -o -name .hg -o -name .git \) -prune -o \ \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ -o -name '.*.rej' -o -size 0 -o -name '*%' -o -name '.*.cmd' \ -o -name 'core' -o -name 'Makefile' -o -name 'Module.symvers' \ -o -name '*.order' -o -name '*.markers' -o -name '*.gcda' \ -o -name '*.gcno' \) \ -type f -print | xargs $(RM) all-local: -[ -x ${top_builddir}/scripts/zfs-tests.sh ] && \ ${top_builddir}/scripts/zfs-tests.sh -c dist-hook: $(AM_V_GEN)$(top_srcdir)/scripts/make_gitrev.sh -D $(distdir) $(GITREV) $(SED) ${ac_inplace} -e 's/Release:[[:print:]]*/Release: $(RELEASE)/' \ $(distdir)/META if BUILD_LINUX # For compatibility, create a matching spl-x.y.z directly which contains # symlinks to the updated header and object file locations. These # compatibility links will be removed in the next major release. if CONFIG_KERNEL install-data-hook: rm -rf $(DESTDIR)$(prefix)/src/spl-$(VERSION) && \ mkdir $(DESTDIR)$(prefix)/src/spl-$(VERSION) && \ cd $(DESTDIR)$(prefix)/src/spl-$(VERSION) && \ ln -s ../zfs-$(VERSION)/include/spl include && \ ln -s ../zfs-$(VERSION)/$(LINUX_VERSION) $(LINUX_VERSION) && \ ln -s ../zfs-$(VERSION)/zfs_config.h.in spl_config.h.in && \ ln -s ../zfs-$(VERSION)/zfs.release.in spl.release.in && \ cd $(DESTDIR)$(prefix)/src/zfs-$(VERSION)/$(LINUX_VERSION) && \ ln -fs zfs_config.h spl_config.h && \ ln -fs zfs.release spl.release endif endif PHONY += codecheck codecheck: cstyle shellcheck checkbashisms flake8 mancheck testscheck vcscheck PHONY += checkstyle checkstyle: codecheck commitcheck PHONY += commitcheck commitcheck: @if git rev-parse --git-dir > /dev/null 2>&1; then \ ${top_srcdir}/scripts/commitcheck.sh; \ fi PHONY += cstyle cstyle: @find ${top_srcdir} -name build -prune \ -o -type f -name '*.[hc]' \ ! -name 'zfs_config.*' ! -name '*.mod.c' \ ! -name 'opt_global.h' ! -name '*_if*.h' \ ! -path './module/zstd/lib/*' \ -exec ${top_srcdir}/scripts/cstyle.pl -cpP {} \+ filter_executable = -exec test -x '{}' \; -print -PHONY += shellcheck -shellcheck: - @if type shellcheck > /dev/null 2>&1; then \ - shellcheck --exclude=SC1090,SC1117,SC1091 --format=gcc \ - $$(find ${top_srcdir} -name "config*" -prune -name tests -prune \ - -o -name "*.sh" -o -name "*.sh.in" -type f) \ - $$(find ${top_srcdir}/cmd/zpool/zpool.d/* \ - -type f ${filter_executable}); \ - else \ - echo "skipping shellcheck because shellcheck is not installed"; \ - fi +SHELLCHECKDIRS = cmd contrib etc scripts tests +SHELLCHECKSCRIPTS = autogen.sh PHONY += checkabi storeabi checkabi: lib $(MAKE) -C lib checkabi storeabi: lib $(MAKE) -C lib storeabi PHONY += checkbashisms checkbashisms: @if type checkbashisms > /dev/null 2>&1; then \ checkbashisms -n -p -x \ $$(find ${top_srcdir} \ -name '.git' -prune \ -o -name 'build' -prune \ -o -name 'tests' -prune \ -o -name 'config' -prune \ -o -name 'zed-functions.sh*' -prune \ -o -name 'zfs-import*' -prune \ -o -name 'zfs-mount*' -prune \ -o -name 'zfs-zed*' -prune \ -o -name 'smart' -prune \ -o -name 'paxcheck.sh' -prune \ -o -name 'make_gitrev.sh' -prune \ -o -name '90zfs' -prune \ -o -path '*initramfs/hooks' -prune \ -o -type f ! -name 'config*' \ ! -name 'libtool' \ -exec awk 'FNR==1 && /^#!.*bin\/sh/ {print FILENAME}' {} \+); \ else \ echo "skipping checkbashisms because checkbashisms is not installed"; \ fi PHONY += mancheck mancheck: @if type mandoc > /dev/null 2>&1; then \ find ${top_srcdir}/man ${top_srcdir}/tests/test-runner/man -type f -name '*[1-9]*' \ -exec mandoc -Tlint -Werror {} \+; \ else \ echo "skipping mancheck because mandoc is not installed"; \ fi if BUILD_LINUX stat_fmt = -c '%A %n' else stat_fmt = -f '%Sp %N' endif PHONY += testscheck testscheck: @find ${top_srcdir}/tests/zfs-tests -type f \ \( -name '*.ksh' -not ${filter_executable} \) -o \ \( -name '*.kshlib' ${filter_executable} \) -o \ \( -name '*.shlib' ${filter_executable} \) -o \ \( -name '*.cfg' ${filter_executable} \) | \ xargs -r stat ${stat_fmt} | \ awk '{c++; print} END {if(c>0) exit 1}' PHONY += vcscheck vcscheck: @if git rev-parse --git-dir > /dev/null 2>&1; then \ git ls-files . --exclude-standard --others | \ awk '{c++; print} END {if(c>0) exit 1}' ; \ fi PHONY += lint lint: cppcheck paxcheck CPPCHECKDIRS = cmd lib module PHONY += cppcheck cppcheck: $(CPPCHECKDIRS) @if test -n "$(CPPCHECK)"; then \ set -e ; for dir in $(CPPCHECKDIRS) ; do \ $(MAKE) -C $$dir cppcheck ; \ done \ else \ echo "skipping cppcheck because cppcheck is not installed"; \ fi PHONY += paxcheck paxcheck: @if type scanelf > /dev/null 2>&1; then \ ${top_srcdir}/scripts/paxcheck.sh ${top_builddir}; \ else \ echo "skipping paxcheck because scanelf is not installed"; \ fi PHONY += flake8 flake8: @if type flake8 > /dev/null 2>&1; then \ flake8 ${top_srcdir}; \ else \ echo "skipping flake8 because flake8 is not installed"; \ fi PHONY += ctags ctags: $(RM) tags find $(top_srcdir) -name '.?*' -prune \ -o -type f -name '*.[hcS]' -print | xargs ctags -a PHONY += etags etags: $(RM) TAGS find $(top_srcdir) -name '.?*' -prune \ -o -type f -name '*.[hcS]' -print | xargs etags -a PHONY += cscopelist cscopelist: find $(top_srcdir) -name '.?*' -prune \ -o -type f -name '*.[hc]' -print >cscope.files PHONY += tags tags: ctags etags PHONY += pkg pkg-dkms pkg-kmod pkg-utils pkg: @DEFAULT_PACKAGE@ pkg-dkms: @DEFAULT_PACKAGE@-dkms pkg-kmod: @DEFAULT_PACKAGE@-kmod pkg-utils: @DEFAULT_PACKAGE@-utils include config/rpm.am include config/deb.am include config/tgz.am .PHONY: $(PHONY) diff --git a/cmd/Makefile.am b/cmd/Makefile.am index a3db316791ab..5fc9e83971d8 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -1,21 +1,27 @@ +include $(top_srcdir)/config/Shellcheck.am + SUBDIRS = zfs zpool zdb zhack zinject zstream ztest SUBDIRS += fsck_zfs vdev_id raidz_test zfs_ids_to_path SUBDIRS += zpool_influxdb CPPCHECKDIRS = zfs zpool zdb zhack zinject zstream ztest CPPCHECKDIRS += raidz_test zfs_ids_to_path zpool_influxdb +# TODO: #12084: SHELLCHECKDIRS = fsck_zfs vdev_id zpool +SHELLCHECKDIRS = fsck_zfs zpool + if USING_PYTHON SUBDIRS += arcstat arc_summary dbufstat endif if BUILD_LINUX SUBDIRS += mount_zfs zed zgenhostid zvol_id zvol_wait CPPCHECKDIRS += mount_zfs zed zgenhostid zvol_id +SHELLCHECKDIRS += zed endif PHONY = cppcheck cppcheck: $(CPPCHECKDIRS) set -e ; for dir in $(CPPCHECKDIRS) ; do \ $(MAKE) -C $$dir cppcheck ; \ done diff --git a/cmd/fsck_zfs/Makefile.am b/cmd/fsck_zfs/Makefile.am index 67583ac75924..f8139f117ff2 100644 --- a/cmd/fsck_zfs/Makefile.am +++ b/cmd/fsck_zfs/Makefile.am @@ -1,5 +1,6 @@ include $(top_srcdir)/config/Substfiles.am +include $(top_srcdir)/config/Shellcheck.am dist_sbin_SCRIPTS = fsck.zfs SUBSTFILES += $(dist_sbin_SCRIPTS) diff --git a/cmd/vdev_id/Makefile.am b/cmd/vdev_id/Makefile.am index fb815faad084..4071c6d5ed6b 100644 --- a/cmd/vdev_id/Makefile.am +++ b/cmd/vdev_id/Makefile.am @@ -1 +1,3 @@ +include $(top_srcdir)/config/Shellcheck.am + dist_udev_SCRIPTS = vdev_id diff --git a/cmd/zed/Makefile.am b/cmd/zed/Makefile.am index 884760415f53..7b662994d1c6 100644 --- a/cmd/zed/Makefile.am +++ b/cmd/zed/Makefile.am @@ -1,51 +1,53 @@ include $(top_srcdir)/config/Rules.am +include $(top_srcdir)/config/Shellcheck.am AM_CFLAGS += $(LIBUDEV_CFLAGS) $(LIBUUID_CFLAGS) SUBDIRS = zed.d +SHELLCHECKDIRS = $(SUBDIRS) sbin_PROGRAMS = zed ZED_SRC = \ zed.c \ zed.h \ zed_conf.c \ zed_conf.h \ zed_disk_event.c \ zed_disk_event.h \ zed_event.c \ zed_event.h \ zed_exec.c \ zed_exec.h \ zed_file.c \ zed_file.h \ zed_log.c \ zed_log.h \ zed_strings.c \ zed_strings.h FMA_SRC = \ agents/zfs_agents.c \ agents/zfs_agents.h \ agents/zfs_diagnosis.c \ agents/zfs_mod.c \ agents/zfs_retire.c \ agents/fmd_api.c \ agents/fmd_api.h \ agents/fmd_serd.c \ agents/fmd_serd.h zed_SOURCES = $(ZED_SRC) $(FMA_SRC) zed_LDADD = \ $(abs_top_builddir)/lib/libzfs/libzfs.la \ $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \ $(abs_top_builddir)/lib/libnvpair/libnvpair.la \ $(abs_top_builddir)/lib/libuutil/libuutil.la zed_LDADD += -lrt $(LIBATOMIC_LIBS) $(LIBUDEV_LIBS) $(LIBUUID_LIBS) zed_LDFLAGS = -pthread EXTRA_DIST = agents/README.md include $(top_srcdir)/config/CppCheck.am diff --git a/cmd/zed/zed.d/Makefile.am b/cmd/zed/zed.d/Makefile.am index 8b2d0c200286..3eece353ef90 100644 --- a/cmd/zed/zed.d/Makefile.am +++ b/cmd/zed/zed.d/Makefile.am @@ -1,53 +1,54 @@ include $(top_srcdir)/config/Rules.am include $(top_srcdir)/config/Substfiles.am +include $(top_srcdir)/config/Shellcheck.am EXTRA_DIST += README zedconfdir = $(sysconfdir)/zfs/zed.d dist_zedconf_DATA = \ zed-functions.sh \ zed.rc zedexecdir = $(zfsexecdir)/zed.d dist_zedexec_SCRIPTS = \ all-debug.sh \ all-syslog.sh \ data-notify.sh \ generic-notify.sh \ resilver_finish-notify.sh \ scrub_finish-notify.sh \ statechange-led.sh \ statechange-notify.sh \ vdev_clear-led.sh \ vdev_attach-led.sh \ pool_import-led.sh \ resilver_finish-start-scrub.sh \ trim_finish-notify.sh nodist_zedexec_SCRIPTS = history_event-zfs-list-cacher.sh SUBSTFILES += $(nodist_zedexec_SCRIPTS) zedconfdefaults = \ all-syslog.sh \ data-notify.sh \ history_event-zfs-list-cacher.sh \ resilver_finish-notify.sh \ scrub_finish-notify.sh \ statechange-led.sh \ statechange-notify.sh \ vdev_clear-led.sh \ vdev_attach-led.sh \ pool_import-led.sh \ resilver_finish-start-scrub.sh install-data-hook: $(MKDIR_P) "$(DESTDIR)$(zedconfdir)" for f in $(zedconfdefaults); do \ test -f "$(DESTDIR)$(zedconfdir)/$${f}" -o \ -L "$(DESTDIR)$(zedconfdir)/$${f}" || \ ln -s "$(zedexecdir)/$${f}" "$(DESTDIR)$(zedconfdir)"; \ done chmod 0600 "$(DESTDIR)$(zedconfdir)/zed.rc" diff --git a/cmd/zpool/Makefile.am b/cmd/zpool/Makefile.am index 4446cf04a085..aad45d4f7497 100644 --- a/cmd/zpool/Makefile.am +++ b/cmd/zpool/Makefile.am @@ -1,187 +1,188 @@ include $(top_srcdir)/config/Rules.am +include $(top_srcdir)/config/Shellcheck.am AM_CFLAGS += $(LIBBLKID_CFLAGS) $(LIBUUID_CFLAGS) DEFAULT_INCLUDES += -I$(srcdir) sbin_PROGRAMS = zpool zpool_SOURCES = \ zpool_iter.c \ zpool_main.c \ zpool_util.c \ zpool_util.h \ zpool_vdev.c if BUILD_FREEBSD zpool_SOURCES += os/freebsd/zpool_vdev_os.c endif if BUILD_LINUX zpool_SOURCES += os/linux/zpool_vdev_os.c endif zpool_LDADD = \ $(abs_top_builddir)/lib/libzfs/libzfs.la \ $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \ $(abs_top_builddir)/lib/libnvpair/libnvpair.la \ $(abs_top_builddir)/lib/libuutil/libuutil.la zpool_LDADD += $(LTLIBINTL) if BUILD_FREEBSD zpool_LDADD += -lgeom endif zpool_LDADD += -lm $(LIBBLKID_LIBS) $(LIBUUID_LIBS) include $(top_srcdir)/config/CppCheck.am zpoolconfdir = $(sysconfdir)/zfs/zpool.d zpoolexecdir = $(zfsexecdir)/zpool.d EXTRA_DIST = zpool.d/README compatibility.d dist_zpoolexec_SCRIPTS = \ zpool.d/dm-deps \ zpool.d/enc \ zpool.d/encdev \ zpool.d/fault_led \ zpool.d/iostat \ zpool.d/iostat-1s \ zpool.d/iostat-10s \ 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/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/nvme_err \ zpool.d/pwr_cyc \ zpool.d/upath \ zpool.d/vendor \ zpool.d/smart_test \ zpool.d/test_type \ zpool.d/test_status \ zpool.d/test_progress \ zpool.d/test_ended zpoolconfdefaults = \ dm-deps \ enc \ encdev \ fault_led \ iostat \ iostat-1s \ iostat-10s \ label \ locate_led \ lsblk \ media \ model \ serial \ ses \ size \ 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 \ nvme_err \ pwr_cyc \ upath \ vendor \ smart_test \ test_type \ test_status \ test_progress \ test_ended zpoolcompatdir = $(pkgdatadir)/compatibility.d dist_zpoolcompat_DATA = \ compatibility.d/compat-2018 \ compatibility.d/compat-2019 \ compatibility.d/compat-2020 \ compatibility.d/compat-2021 \ compatibility.d/freebsd-11.0 \ compatibility.d/freebsd-11.2 \ compatibility.d/freebsd-11.3 \ compatibility.d/freenas-9.10.2 \ compatibility.d/grub2 \ compatibility.d/openzfsonosx-1.7.0 \ compatibility.d/openzfsonosx-1.8.1 \ compatibility.d/openzfsonosx-1.9.3 \ compatibility.d/openzfs-2.0-freebsd \ compatibility.d/openzfs-2.0-linux \ compatibility.d/openzfs-2.1-freebsd \ compatibility.d/openzfs-2.1-linux \ compatibility.d/zol-0.6.1 \ compatibility.d/zol-0.6.4 \ compatibility.d/zol-0.6.5 \ compatibility.d/zol-0.7 \ compatibility.d/zol-0.8 # canonical <- alias symbolic link pairs # eg: "2018" is a link to "compat-2018" zpoolcompatlinks = \ "compat-2018 2018" \ "compat-2019 2019" \ "compat-2020 2020" \ "compat-2021 2021" \ "freebsd-11.0 freebsd-11.1" \ "freebsd-11.0 freenas-11.0" \ "freebsd-11.2 freenas-11.2" \ "freebsd-11.3 freebsd-11.4" \ "freebsd-11.3 freebsd-12.0" \ "freebsd-11.3 freebsd-12.1" \ "freebsd-11.3 freebsd-12.2" \ "freebsd-11.3 freenas-11.3" \ "freenas-11.0 freenas-11.1" \ "openzfsonosx-1.9.3 openzfsonosx-1.9.4" \ "openzfs-2.0-freebsd truenas-12.0" \ "zol-0.7 ubuntu-18.04" \ "zol-0.8 ubuntu-20.04" install-data-hook: $(MKDIR_P) "$(DESTDIR)$(zpoolconfdir)" for f in $(zpoolconfdefaults); do \ test -f "$(DESTDIR)$(zpoolconfdir)/$${f}" -o \ -L "$(DESTDIR)$(zpoolconfdir)/$${f}" || \ ln -s "$(zpoolexecdir)/$${f}" "$(DESTDIR)$(zpoolconfdir)"; \ done for l in $(zpoolcompatlinks); do \ (cd "$(DESTDIR)$(zpoolcompatdir)"; ln -sf $${l} ); \ done diff --git a/cmd/zpool/zpool.d/smart b/cmd/zpool/zpool.d/smart index f8854b75227c..b71591abbc61 100755 --- a/cmd/zpool/zpool.d/smart +++ b/cmd/zpool/zpool.d/smart @@ -1,243 +1,243 @@ #!/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. nvme_err: Show SMART NVMe errors (NVMe). smart_test: Show SMART self-test results summary. test_type: Show SMART self-test type (short, long... ). test_status: Show SMART self-test status. test_progress: Show SMART self-test percentage done. test_ended: Show when the last SMART self-test ended (if supported). " # Hack for developer testing # # If you set $samples to a directory containing smartctl output text files, # we will use them instead of running smartctl on the vdevs. This can be # useful if you want to test a bunch of different smartctl outputs. Also, if # $samples is set, and additional 'file' column is added to the zpool output # showing the filename. samples= # get_filename_from_dir DIR # # Look in directory DIR and return a filename from it. The filename returned # is chosen quasi-sequentially (based off our PID). This allows us to return # a different filename every time this script is invoked (which we do for each # vdev), without having to maintain state. get_filename_from_dir() { dir=$1 pid="$$" num_files=$(find "$dir" -maxdepth 1 -type f | wc -l) mod=$((pid % num_files)) i=0 - find "$dir" -type f -printf "%f\n" | while read -r file ; do + find "$dir" -type f -printf '%f\n' | while read -r file ; do if [ "$mod" = "$i" ] ; then echo "$file" break fi i=$((i+1)) done } script=$(basename "$0") if [ "$1" = "-h" ] ; then echo "$helpstr" | grep "$script:" | tr -s '\t' | cut -f 2- exit fi smartctl_path=$(command -v smartctl) # shellcheck disable=SC2015 if [ -b "$VDEV_UPATH" ] && [ -x "$smartctl_path" ] || [ -n "$samples" ] ; then if [ -n "$samples" ] ; then # cat a smartctl output text file instead of running smartctl # on a vdev (only used for developer testing). file=$(get_filename_from_dir "$samples") echo "file=$file" raw_out=$(cat "$samples/$file") else raw_out=$(eval "sudo $smartctl_path -a $VDEV_UPATH") fi # What kind of drive are we? Look for the right line in smartctl: # # SAS: # Transport protocol: SAS # # SATA: # ATA Version is: 8 # # NVMe: # SMART/Health Information (NVMe Log 0xnn, NSID 0xnn) # 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 /SAS/{type="sas"} /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; hours_on=int($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 /SATA/{type="sata"} /Temperature_Celsius/{print "temp="$10} /Airflow_Temperature_Cel/{print "temp="$10} /Current Temperature:/{print "temp="$3} /SMART overall-health self-assessment test result:/{print "health="$6} /Power_On_Hours/{print "hours_on="$10; hours_on=int($10)} /Serial Number:/{print "serial="$3} # NVMe common /NVMe/{type="nvme"} /Temperature:/{print "temp="$2} /SMART overall-health self-assessment test result:/{print "health="$6} /Power On Hours:/{gsub("[^0-9]","",$4); print "hours_on="$4} /Serial Number:/{print "serial="$3} /Power Cycles:/{print "pwr_cyc="$3} # NVMe specific /Media and Data Integrity Errors:/{print "nvme_err="$6} # SMART self-test info /Self-test execution status:/{progress=tolower($4)} # SAS /SMART Self-test log/{test_seen=1} # SAS /SMART Extended Self-test Log/{test_seen=1} # SATA /# 1/{ test_type=tolower($3"_"$4); # Status could be one word ("Completed") or multiple ("Completed: read # failure"). Look for the ":" to see if we need to grab more words. if ($5 ~ ":") status=tolower($5""$6"_"$7) else status=tolower($5) if (status=="self") status="running"; if (type == "sas") { hours=int($(NF-4)) } else { hours=int($(NF-1)) # SATA reports percent remaining, rather than percent done # Convert it to percent done. progress=(100-int($(NF-2)))"%" } # When we int()-ify "hours", it converts stuff like "NOW" and "-" into # 0. In those cases, set it to hours_on, so they will cancel out in # the "hours_ago" calculation later on. if (hours == 0) hours=hours_on if (test_seen) { print "test="hours_on print "test_type="test_type print "test_status="status print "test_progress="progress } # Not all drives report hours_on if (hours_on && hours) { total_hours_ago=(hours_on-hours) days_ago=int(total_hours_ago/24) hours_ago=(total_hours_ago % 24) if (days_ago != 0) ago_str=days_ago"d" if (hours_ago !=0) ago_str=ago_str""hours_ago"h" print "test_ended="ago_str } } END {print "type="type; ORS="\n"; print ""} '); fi type=$(echo "$out" | grep '^type=' | cut -d '=' -f 2) # 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="sata" 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" = "sata" ] ; then scripts="temp|health|ata_err|realloc|rep_ucor|cmd_to|pend_sec|off_ucor" elif [ "$type" = "nvme" ] ; then scripts="temp|health|nvme_err" fi ;; smartx) # Print some other interesting stats if [ "$type" = "sas" ] ; then scripts="hours_on|defect|nonmed|r_proc|w_proc" elif [ "$type" = "sata" ] ; then scripts="hours_on|pwr_cyc" elif [ "$type" = "nvme" ] ; then scripts="hours_on|pwr_cyc" fi ;; smart_test) scripts="test_type|test_status|test_progress|test_ended" ;; *) scripts="$script" esac with_vals=$(echo "$out" | grep -E "$scripts") if [ -n "$with_vals" ]; then echo "$with_vals" - without_vals=$(echo "$scripts" | tr "|" "\n" | + 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"="}') + without_vals=$(echo "$scripts" | tr '|' '\n' | awk '{print $0"="}') fi if [ -n "$without_vals" ]; then echo "$without_vals" fi diff --git a/cmd/zvol_wait/Makefile.am b/cmd/zvol_wait/Makefile.am index 564031c9799d..2e5bf3323389 100644 --- a/cmd/zvol_wait/Makefile.am +++ b/cmd/zvol_wait/Makefile.am @@ -1 +1,3 @@ +include $(top_srcdir)/config/Shellcheck.am + dist_bin_SCRIPTS = zvol_wait diff --git a/config/Shellcheck.am b/config/Shellcheck.am new file mode 100644 index 000000000000..a22333ac45e7 --- /dev/null +++ b/config/Shellcheck.am @@ -0,0 +1,8 @@ +.PHONY: shellcheck +shellcheck: $(SCRIPTS) $(SHELLCHECKSCRIPTS) +if HAVE_SHELLCHECK + [ -z "$(SCRIPTS)$(SHELLCHECKSCRIPTS)" ] && exit; shellcheck $$([ -n "$(SHELLCHECK_SHELL)" ] && echo "--shell=$(SHELLCHECK_SHELL)") --exclude=SC1090,SC1091$(SHELLCHECK_IGNORE) --format=gcc $(SCRIPTS) $(SHELLCHECKSCRIPTS) +else + @[ -z "$(SCRIPTS)$(SHELLCHECKSCRIPTS)" ] && exit; echo "skipping shellcheck of" $(SCRIPTS) $(SHELLCHECKSCRIPTS) "because shellcheck is not installed" +endif + @set -e; for dir in $(SHELLCHECKDIRS); do $(MAKE) -C $$dir shellcheck; done diff --git a/config/always-shellcheck.m4 b/config/always-shellcheck.m4 new file mode 100644 index 000000000000..1e154e1c5552 --- /dev/null +++ b/config/always-shellcheck.m4 @@ -0,0 +1,7 @@ +dnl # +dnl # Check if shellcheck is available. +dnl # +AC_DEFUN([ZFS_AC_CONFIG_ALWAYS_SHELLCHECK], [ + AC_CHECK_PROG([SHELLCHECK], [shellcheck], [yes]) + AM_CONDITIONAL([HAVE_SHELLCHECK], [test "x$SHELLCHECK" = "xyes"]) +]) diff --git a/config/zfs-build.m4 b/config/zfs-build.m4 index cee5c87e770c..c4fe07c810b0 100644 --- a/config/zfs-build.m4 +++ b/config/zfs-build.m4 @@ -1,607 +1,608 @@ AC_DEFUN([ZFS_AC_LICENSE], [ AC_MSG_CHECKING([zfs author]) AC_MSG_RESULT([$ZFS_META_AUTHOR]) AC_MSG_CHECKING([zfs license]) AC_MSG_RESULT([$ZFS_META_LICENSE]) ]) AC_DEFUN([ZFS_AC_DEBUG_ENABLE], [ DEBUG_CFLAGS="-Werror" DEBUG_CPPFLAGS="-DDEBUG -UNDEBUG" DEBUG_LDFLAGS="" DEBUG_ZFS="_with_debug" WITH_DEBUG="true" AC_DEFINE(ZFS_DEBUG, 1, [zfs debugging enabled]) KERNEL_DEBUG_CFLAGS="-Werror" KERNEL_DEBUG_CPPFLAGS="-DDEBUG -UNDEBUG" ]) AC_DEFUN([ZFS_AC_DEBUG_DISABLE], [ DEBUG_CFLAGS="" DEBUG_CPPFLAGS="-UDEBUG -DNDEBUG" DEBUG_LDFLAGS="" DEBUG_ZFS="_without_debug" WITH_DEBUG="" KERNEL_DEBUG_CFLAGS="" KERNEL_DEBUG_CPPFLAGS="-UDEBUG -DNDEBUG" ]) dnl # dnl # When debugging is enabled: dnl # - Enable all ASSERTs (-DDEBUG) dnl # - Promote all compiler warnings to errors (-Werror) dnl # AC_DEFUN([ZFS_AC_DEBUG], [ AC_MSG_CHECKING([whether assertion support will be enabled]) AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [Enable compiler and code assertions @<:@default=no@:>@])], [], [enable_debug=no]) AS_CASE(["x$enable_debug"], ["xyes"], [ZFS_AC_DEBUG_ENABLE], ["xno"], [ZFS_AC_DEBUG_DISABLE], [AC_MSG_ERROR([Unknown option $enable_debug])]) AC_SUBST(DEBUG_CFLAGS) AC_SUBST(DEBUG_CPPFLAGS) AC_SUBST(DEBUG_LDFLAGS) AC_SUBST(DEBUG_ZFS) AC_SUBST(WITH_DEBUG) AC_SUBST(KERNEL_DEBUG_CFLAGS) AC_SUBST(KERNEL_DEBUG_CPPFLAGS) AC_MSG_RESULT([$enable_debug]) ]) AC_DEFUN([ZFS_AC_DEBUGINFO_ENABLE], [ DEBUG_CFLAGS="$DEBUG_CFLAGS -g -fno-inline $NO_IPA_SRA" KERNEL_DEBUG_CFLAGS="$KERNEL_DEBUG_CFLAGS -fno-inline $NO_IPA_SRA" KERNEL_MAKE="$KERNEL_MAKE CONFIG_DEBUG_INFO=y" DEBUGINFO_ZFS="_with_debuginfo" ]) AC_DEFUN([ZFS_AC_DEBUGINFO_DISABLE], [ DEBUGINFO_ZFS="_without_debuginfo" ]) AC_DEFUN([ZFS_AC_DEBUGINFO], [ AC_MSG_CHECKING([whether debuginfo support will be forced]) AC_ARG_ENABLE([debuginfo], [AS_HELP_STRING([--enable-debuginfo], [Force generation of debuginfo @<:@default=no@:>@])], [], [enable_debuginfo=no]) AS_CASE(["x$enable_debuginfo"], ["xyes"], [ZFS_AC_DEBUGINFO_ENABLE], ["xno"], [ZFS_AC_DEBUGINFO_DISABLE], [AC_MSG_ERROR([Unknown option $enable_debuginfo])]) AC_SUBST(DEBUG_CFLAGS) AC_SUBST(DEBUGINFO_ZFS) AC_SUBST(KERNEL_DEBUG_CFLAGS) AC_SUBST(KERNEL_MAKE) AC_MSG_RESULT([$enable_debuginfo]) ]) dnl # dnl # Disabled by default, provides basic memory tracking. Track the total dnl # number of bytes allocated with kmem_alloc() and freed with kmem_free(). dnl # Then at module unload time if any bytes were leaked it will be reported dnl # on the console. dnl # AC_DEFUN([ZFS_AC_DEBUG_KMEM], [ AC_MSG_CHECKING([whether basic kmem accounting is enabled]) AC_ARG_ENABLE([debug-kmem], [AS_HELP_STRING([--enable-debug-kmem], [Enable basic kmem accounting @<:@default=no@:>@])], [], [enable_debug_kmem=no]) AS_IF([test "x$enable_debug_kmem" = xyes], [ KERNEL_DEBUG_CPPFLAGS="${KERNEL_DEBUG_CPPFLAGS} -DDEBUG_KMEM" DEBUG_KMEM_ZFS="_with_debug_kmem" ], [ DEBUG_KMEM_ZFS="_without_debug_kmem" ]) AC_SUBST(KERNEL_DEBUG_CPPFLAGS) AC_SUBST(DEBUG_KMEM_ZFS) AC_MSG_RESULT([$enable_debug_kmem]) ]) dnl # dnl # Disabled by default, provides detailed memory tracking. This feature dnl # also requires --enable-debug-kmem to be set. When enabled not only will dnl # total bytes be tracked but also the location of every kmem_alloc() and dnl # kmem_free(). When the module is unloaded a list of all leaked addresses dnl # and where they were allocated will be dumped to the console. Enabling dnl # this feature has a significant impact on performance but it makes finding dnl # memory leaks straight forward. dnl # AC_DEFUN([ZFS_AC_DEBUG_KMEM_TRACKING], [ AC_MSG_CHECKING([whether detailed kmem tracking is enabled]) AC_ARG_ENABLE([debug-kmem-tracking], [AS_HELP_STRING([--enable-debug-kmem-tracking], [Enable detailed kmem tracking @<:@default=no@:>@])], [], [enable_debug_kmem_tracking=no]) AS_IF([test "x$enable_debug_kmem_tracking" = xyes], [ KERNEL_DEBUG_CPPFLAGS="${KERNEL_DEBUG_CPPFLAGS} -DDEBUG_KMEM_TRACKING" DEBUG_KMEM_TRACKING_ZFS="_with_debug_kmem_tracking" ], [ DEBUG_KMEM_TRACKING_ZFS="_without_debug_kmem_tracking" ]) AC_SUBST(KERNEL_DEBUG_CPPFLAGS) AC_SUBST(DEBUG_KMEM_TRACKING_ZFS) AC_MSG_RESULT([$enable_debug_kmem_tracking]) ]) AC_DEFUN([ZFS_AC_DEBUG_INVARIANTS_DETECT_FREEBSD], [ AS_IF([sysctl -n kern.conftxt | fgrep -qx $'options\tINVARIANTS'], [enable_invariants="yes"], [enable_invariants="no"]) ]) AC_DEFUN([ZFS_AC_DEBUG_INVARIANTS_DETECT], [ AM_COND_IF([BUILD_FREEBSD], [ZFS_AC_DEBUG_INVARIANTS_DETECT_FREEBSD], [enable_invariants="no"]) ]) dnl # dnl # Detected for the running kernel by default, enables INVARIANTS features dnl # in the FreeBSD kernel module. This feature must be used when building dnl # for a FreeBSD kernel with "options INVARIANTS" in the KERNCONF and must dnl # not be used when the INVARIANTS option is absent. dnl # AC_DEFUN([ZFS_AC_DEBUG_INVARIANTS], [ AC_MSG_CHECKING([whether FreeBSD kernel INVARIANTS checks are enabled]) AC_ARG_ENABLE([invariants], [AS_HELP_STRING([--enable-invariants], [Enable FreeBSD kernel INVARIANTS checks [[default: detect]]])], [], [ZFS_AC_DEBUG_INVARIANTS_DETECT]) AS_IF([test "x$enable_invariants" = xyes], [WITH_INVARIANTS="true"], [WITH_INVARIANTS=""]) AC_SUBST(WITH_INVARIANTS) AC_MSG_RESULT([$enable_invariants]) ]) AC_DEFUN([ZFS_AC_CONFIG_ALWAYS], [ AX_COUNT_CPUS([]) AC_SUBST(CPU_COUNT) ZFS_AC_CONFIG_ALWAYS_CC_NO_UNUSED_BUT_SET_VARIABLE ZFS_AC_CONFIG_ALWAYS_CC_NO_BOOL_COMPARE ZFS_AC_CONFIG_ALWAYS_CC_FRAME_LARGER_THAN ZFS_AC_CONFIG_ALWAYS_CC_NO_FORMAT_TRUNCATION ZFS_AC_CONFIG_ALWAYS_CC_NO_FORMAT_ZERO_LENGTH ZFS_AC_CONFIG_ALWAYS_CC_NO_OMIT_FRAME_POINTER ZFS_AC_CONFIG_ALWAYS_CC_NO_IPA_SRA ZFS_AC_CONFIG_ALWAYS_CC_ASAN ZFS_AC_CONFIG_ALWAYS_TOOLCHAIN_SIMD ZFS_AC_CONFIG_ALWAYS_SYSTEM ZFS_AC_CONFIG_ALWAYS_ARCH ZFS_AC_CONFIG_ALWAYS_PYTHON ZFS_AC_CONFIG_ALWAYS_PYZFS ZFS_AC_CONFIG_ALWAYS_SED ZFS_AC_CONFIG_ALWAYS_CPPCHECK + ZFS_AC_CONFIG_ALWAYS_SHELLCHECK ]) AC_DEFUN([ZFS_AC_CONFIG], [ dnl # Remove the previous build test directory. rm -Rf build ZFS_CONFIG=all AC_ARG_WITH([config], AS_HELP_STRING([--with-config=CONFIG], [Config file 'kernel|user|all|srpm']), [ZFS_CONFIG="$withval"]) AC_ARG_ENABLE([linux-builtin], [AS_HELP_STRING([--enable-linux-builtin], [Configure for builtin in-tree kernel modules @<:@default=no@:>@])], [], [enable_linux_builtin=no]) AC_MSG_CHECKING([zfs config]) AC_MSG_RESULT([$ZFS_CONFIG]); AC_SUBST(ZFS_CONFIG) ZFS_AC_CONFIG_ALWAYS AM_COND_IF([BUILD_LINUX], [ AC_ARG_VAR([TEST_JOBS], [simultaneous jobs during configure]) if test "x$ac_cv_env_TEST_JOBS_set" != "xset"; then TEST_JOBS=$CPU_COUNT fi AC_SUBST(TEST_JOBS) ]) case "$ZFS_CONFIG" in kernel) ZFS_AC_CONFIG_KERNEL ;; user) ZFS_AC_CONFIG_USER ;; all) ZFS_AC_CONFIG_USER ZFS_AC_CONFIG_KERNEL ;; srpm) ;; *) AC_MSG_RESULT([Error!]) AC_MSG_ERROR([Bad value "$ZFS_CONFIG" for --with-config, user kernel|user|all|srpm]) ;; esac AM_CONDITIONAL([CONFIG_USER], [test "$ZFS_CONFIG" = user -o "$ZFS_CONFIG" = all]) AM_CONDITIONAL([CONFIG_KERNEL], [test "$ZFS_CONFIG" = kernel -o "$ZFS_CONFIG" = all] && [test "x$enable_linux_builtin" != xyes ]) AM_CONDITIONAL([CONFIG_QAT], [test "$ZFS_CONFIG" = kernel -o "$ZFS_CONFIG" = all] && [test "x$qatsrc" != x ]) AM_CONDITIONAL([WANT_DEVNAME2DEVID], [test "x$user_libudev" = xyes ]) AM_CONDITIONAL([WANT_MMAP_LIBAIO], [test "x$user_libaio" = xyes ]) AM_CONDITIONAL([PAM_ZFS_ENABLED], [test "x$enable_pam" = xyes]) ]) dnl # dnl # Check for rpm+rpmbuild to build RPM packages. If these tools dnl # are missing it is non-fatal but you will not be able to build dnl # RPM packages and will be warned if you try too. dnl # dnl # By default the generic spec file will be used because it requires dnl # minimal dependencies. Distribution specific spec files can be dnl # placed under the 'rpm/' directory and enabled using dnl # the --with-spec= configure option. dnl # AC_DEFUN([ZFS_AC_RPM], [ RPM=rpm RPMBUILD=rpmbuild AC_MSG_CHECKING([whether $RPM is available]) AS_IF([tmp=$($RPM --version 2>/dev/null)], [ RPM_VERSION=$(echo $tmp | $AWK '/RPM/ { print $[3] }') HAVE_RPM=yes AC_MSG_RESULT([$HAVE_RPM ($RPM_VERSION)]) ],[ HAVE_RPM=no AC_MSG_RESULT([$HAVE_RPM]) ]) AC_MSG_CHECKING([whether $RPMBUILD is available]) AS_IF([tmp=$($RPMBUILD --version 2>/dev/null)], [ RPMBUILD_VERSION=$(echo $tmp | $AWK '/RPM/ { print $[3] }') HAVE_RPMBUILD=yes AC_MSG_RESULT([$HAVE_RPMBUILD ($RPMBUILD_VERSION)]) ],[ HAVE_RPMBUILD=no AC_MSG_RESULT([$HAVE_RPMBUILD]) ]) RPM_DEFINE_COMMON='--define "$(DEBUG_ZFS) 1"' RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "$(DEBUGINFO_ZFS) 1"' RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "$(DEBUG_KMEM_ZFS) 1"' RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "$(DEBUG_KMEM_TRACKING_ZFS) 1"' RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "$(ASAN_ZFS) 1"' RPM_DEFINE_UTIL=' --define "_initconfdir $(initconfdir)"' dnl # Make the next three RPM_DEFINE_UTIL additions conditional, since dnl # their values may not be set when running: dnl # dnl # ./configure --with-config=srpm dnl # AS_IF([test -n "$dracutdir" ], [ RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' --define "_dracutdir $(dracutdir)"' ]) AS_IF([test -n "$udevdir" ], [ RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' --define "_udevdir $(udevdir)"' ]) AS_IF([test -n "$udevruledir" ], [ RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' --define "_udevruledir $(udevruledir)"' ]) RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' $(DEFINE_SYSTEMD)' RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' $(DEFINE_PYZFS)' RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' $(DEFINE_PAM)' RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' $(DEFINE_PYTHON_VERSION)' RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' $(DEFINE_PYTHON_PKG_VERSION)' dnl # Override default lib directory on Debian/Ubuntu systems. The dnl # provided /usr/lib/rpm/platform//macros files do not dnl # specify the correct path for multiarch systems as described dnl # by the packaging guidelines. dnl # dnl # https://wiki.ubuntu.com/MultiarchSpec dnl # https://wiki.debian.org/Multiarch/Implementation dnl # AS_IF([test "$DEFAULT_PACKAGE" = "deb"], [ MULTIARCH_LIBDIR="lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)" RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' --define "_lib $(MULTIARCH_LIBDIR)"' AC_SUBST(MULTIARCH_LIBDIR) ]) dnl # Make RPM_DEFINE_KMOD additions conditional on CONFIG_KERNEL, dnl # since the values will not be set otherwise. The spec files dnl # provide defaults for them. dnl # RPM_DEFINE_KMOD='--define "_wrong_version_format_terminate_build 0"' AM_COND_IF([CONFIG_KERNEL], [ RPM_DEFINE_KMOD=${RPM_DEFINE_KMOD}' --define "kernels $(LINUX_VERSION)"' RPM_DEFINE_KMOD=${RPM_DEFINE_KMOD}' --define "ksrc $(LINUX)"' RPM_DEFINE_KMOD=${RPM_DEFINE_KMOD}' --define "kobj $(LINUX_OBJ)"' ]) RPM_DEFINE_DKMS='' SRPM_DEFINE_COMMON='--define "build_src_rpm 1"' SRPM_DEFINE_UTIL= SRPM_DEFINE_KMOD= SRPM_DEFINE_DKMS= RPM_SPEC_DIR="rpm/generic" AC_ARG_WITH([spec], AS_HELP_STRING([--with-spec=SPEC], [Spec files 'generic|redhat']), [RPM_SPEC_DIR="rpm/$withval"]) AC_MSG_CHECKING([whether spec files are available]) AC_MSG_RESULT([yes ($RPM_SPEC_DIR/*.spec.in)]) AC_SUBST(HAVE_RPM) AC_SUBST(RPM) AC_SUBST(RPM_VERSION) AC_SUBST(HAVE_RPMBUILD) AC_SUBST(RPMBUILD) AC_SUBST(RPMBUILD_VERSION) AC_SUBST(RPM_SPEC_DIR) AC_SUBST(RPM_DEFINE_UTIL) AC_SUBST(RPM_DEFINE_KMOD) AC_SUBST(RPM_DEFINE_DKMS) AC_SUBST(RPM_DEFINE_COMMON) AC_SUBST(SRPM_DEFINE_UTIL) AC_SUBST(SRPM_DEFINE_KMOD) AC_SUBST(SRPM_DEFINE_DKMS) AC_SUBST(SRPM_DEFINE_COMMON) ]) dnl # dnl # Check for dpkg+dpkg-buildpackage to build DEB packages. If these dnl # tools are missing it is non-fatal but you will not be able to build dnl # DEB packages and will be warned if you try too. dnl # AC_DEFUN([ZFS_AC_DPKG], [ DPKG=dpkg DPKGBUILD=dpkg-buildpackage AC_MSG_CHECKING([whether $DPKG is available]) AS_IF([tmp=$($DPKG --version 2>/dev/null)], [ DPKG_VERSION=$(echo $tmp | $AWK '/Debian/ { print $[7] }') HAVE_DPKG=yes AC_MSG_RESULT([$HAVE_DPKG ($DPKG_VERSION)]) ],[ HAVE_DPKG=no AC_MSG_RESULT([$HAVE_DPKG]) ]) AC_MSG_CHECKING([whether $DPKGBUILD is available]) AS_IF([tmp=$($DPKGBUILD --version 2>/dev/null)], [ DPKGBUILD_VERSION=$(echo $tmp | \ $AWK '/Debian/ { print $[4] }' | cut -f-4 -d'.') HAVE_DPKGBUILD=yes AC_MSG_RESULT([$HAVE_DPKGBUILD ($DPKGBUILD_VERSION)]) ],[ HAVE_DPKGBUILD=no AC_MSG_RESULT([$HAVE_DPKGBUILD]) ]) AC_SUBST(HAVE_DPKG) AC_SUBST(DPKG) AC_SUBST(DPKG_VERSION) AC_SUBST(HAVE_DPKGBUILD) AC_SUBST(DPKGBUILD) AC_SUBST(DPKGBUILD_VERSION) ]) dnl # dnl # Until native packaging for various different packing systems dnl # can be added the least we can do is attempt to use alien to dnl # convert the RPM packages to the needed package type. This is dnl # a hack but so far it has worked reasonable well. dnl # AC_DEFUN([ZFS_AC_ALIEN], [ ALIEN=alien AC_MSG_CHECKING([whether $ALIEN is available]) AS_IF([tmp=$($ALIEN --version 2>/dev/null)], [ ALIEN_VERSION=$(echo $tmp | $AWK '{ print $[3] }') ALIEN_MAJOR=$(echo ${ALIEN_VERSION} | $AWK -F'.' '{ print $[1] }') ALIEN_MINOR=$(echo ${ALIEN_VERSION} | $AWK -F'.' '{ print $[2] }') ALIEN_POINT=$(echo ${ALIEN_VERSION} | $AWK -F'.' '{ print $[3] }') HAVE_ALIEN=yes AC_MSG_RESULT([$HAVE_ALIEN ($ALIEN_VERSION)]) ],[ HAVE_ALIEN=no AC_MSG_RESULT([$HAVE_ALIEN]) ]) AC_SUBST(HAVE_ALIEN) AC_SUBST(ALIEN) AC_SUBST(ALIEN_VERSION) AC_SUBST(ALIEN_MAJOR) AC_SUBST(ALIEN_MINOR) AC_SUBST(ALIEN_POINT) ]) dnl # dnl # Using the VENDOR tag from config.guess set the default dnl # package type for 'make pkg': (rpm | deb | tgz) dnl # AC_DEFUN([ZFS_AC_DEFAULT_PACKAGE], [ AC_MSG_CHECKING([os distribution]) AC_ARG_WITH([vendor], [AS_HELP_STRING([--with-vendor], [Distribution vendor @<:@default=check@:>@])], [with_vendor=$withval], [with_vendor=check]) AS_IF([test "x$with_vendor" = "xcheck"],[ if test -f /etc/toss-release ; then VENDOR=toss ; elif test -f /etc/fedora-release ; then VENDOR=fedora ; elif test -f /etc/redhat-release ; then VENDOR=redhat ; elif test -f /etc/gentoo-release ; then VENDOR=gentoo ; elif test -f /etc/arch-release ; then VENDOR=arch ; elif test -f /etc/SuSE-release ; then VENDOR=sles ; elif test -f /etc/slackware-version ; then VENDOR=slackware ; elif test -f /etc/lunar.release ; then VENDOR=lunar ; elif test -f /etc/lsb-release ; then VENDOR=ubuntu ; elif test -f /etc/debian_version ; then VENDOR=debian ; elif test -f /etc/alpine-release ; then VENDOR=alpine ; elif test -f /bin/freebsd-version ; then VENDOR=freebsd ; else VENDOR= ; fi], [ test "x${with_vendor}" != x],[ VENDOR="$with_vendor" ], [ VENDOR= ; ] ) AC_MSG_RESULT([$VENDOR]) AC_SUBST(VENDOR) AC_MSG_CHECKING([default package type]) case "$VENDOR" in toss) DEFAULT_PACKAGE=rpm ;; redhat) DEFAULT_PACKAGE=rpm ;; fedora) DEFAULT_PACKAGE=rpm ;; gentoo) DEFAULT_PACKAGE=tgz ;; alpine) DEFAULT_PACKAGE=tgz ;; arch) DEFAULT_PACKAGE=tgz ;; sles) DEFAULT_PACKAGE=rpm ;; slackware) DEFAULT_PACKAGE=tgz ;; lunar) DEFAULT_PACKAGE=tgz ;; ubuntu) DEFAULT_PACKAGE=deb ;; debian) DEFAULT_PACKAGE=deb ;; freebsd) DEFAULT_PACKAGE=pkg ;; *) DEFAULT_PACKAGE=rpm ;; esac AC_MSG_RESULT([$DEFAULT_PACKAGE]) AC_SUBST(DEFAULT_PACKAGE) AC_MSG_CHECKING([default init directory]) case "$VENDOR" in freebsd) initdir=$sysconfdir/rc.d ;; *) initdir=$sysconfdir/init.d;; esac AC_MSG_RESULT([$initdir]) AC_SUBST(initdir) AC_MSG_CHECKING([default init script type and shell]) case "$VENDOR" in toss) DEFAULT_INIT_SCRIPT=redhat ;; redhat) DEFAULT_INIT_SCRIPT=redhat ;; fedora) DEFAULT_INIT_SCRIPT=fedora ;; gentoo) DEFAULT_INIT_SCRIPT=openrc ;; alpine) DEFAULT_INIT_SCRIPT=openrc ;; arch) DEFAULT_INIT_SCRIPT=lsb ;; sles) DEFAULT_INIT_SCRIPT=lsb ;; slackware) DEFAULT_INIT_SCRIPT=lsb ;; lunar) DEFAULT_INIT_SCRIPT=lunar ;; ubuntu) DEFAULT_INIT_SCRIPT=lsb ;; debian) DEFAULT_INIT_SCRIPT=lsb ;; freebsd) DEFAULT_INIT_SCRIPT=freebsd;; *) DEFAULT_INIT_SCRIPT=lsb ;; esac # On gentoo, it's possible that OpenRC isn't installed. Check if # /sbin/openrc-run exists, and if not, fall back to generic defaults. DEFAULT_INIT_SHELL="/bin/sh" AS_IF([test "$DEFAULT_INIT_SCRIPT" = "openrc"], [ AS_IF([test -x "/sbin/openrc-run"], [DEFAULT_INIT_SHELL="/sbin/openrc-run"], [DEFAULT_INIT_SCRIPT=lsb]) ]) AC_MSG_RESULT([$DEFAULT_INIT_SCRIPT:$DEFAULT_INIT_SHELL]) AC_SUBST(DEFAULT_INIT_SCRIPT) AC_SUBST(DEFAULT_INIT_SHELL) AC_MSG_CHECKING([default nfs server init script]) AS_IF([test "$VENDOR" = "debian"], [DEFAULT_INIT_NFS_SERVER="nfs-kernel-server"], [DEFAULT_INIT_NFS_SERVER="nfs"] ) AC_MSG_RESULT([$DEFAULT_INIT_NFS_SERVER]) AC_SUBST(DEFAULT_INIT_NFS_SERVER) AC_MSG_CHECKING([default init config directory]) case "$VENDOR" in alpine) initconfdir=/etc/conf.d ;; gentoo) initconfdir=/etc/conf.d ;; toss) initconfdir=/etc/sysconfig ;; redhat) initconfdir=/etc/sysconfig ;; fedora) initconfdir=/etc/sysconfig ;; sles) initconfdir=/etc/sysconfig ;; ubuntu) initconfdir=/etc/default ;; debian) initconfdir=/etc/default ;; freebsd) initconfdir=$sysconfdir/rc.conf.d;; *) initconfdir=/etc/default ;; esac AC_MSG_RESULT([$initconfdir]) AC_SUBST(initconfdir) AC_MSG_CHECKING([whether initramfs-tools is available]) if test -d /usr/share/initramfs-tools ; then RPM_DEFINE_INITRAMFS='--define "_initramfs 1"' AC_MSG_RESULT([yes]) else RPM_DEFINE_INITRAMFS='' AC_MSG_RESULT([no]) fi AC_SUBST(RPM_DEFINE_INITRAMFS) ]) dnl # dnl # Default ZFS package configuration dnl # AC_DEFUN([ZFS_AC_PACKAGE], [ ZFS_AC_DEFAULT_PACKAGE AS_IF([test x$VENDOR != xfreebsd], [ ZFS_AC_RPM ZFS_AC_DPKG ZFS_AC_ALIEN ]) ]) diff --git a/contrib/Makefile.am b/contrib/Makefile.am index 9547878d07d0..5ec13ece5327 100644 --- a/contrib/Makefile.am +++ b/contrib/Makefile.am @@ -1,8 +1,12 @@ +include $(top_srcdir)/config/Shellcheck.am + SUBDIRS = bash_completion.d pyzfs zcp if BUILD_LINUX SUBDIRS += bpftrace dracut initramfs endif if PAM_ZFS_ENABLED SUBDIRS += pam_zfs_key endif DIST_SUBDIRS = bash_completion.d bpftrace dracut initramfs pam_zfs_key pyzfs zcp + +SHELLCHECKDIRS = bash_completion.d bpftrace dracut initramfs diff --git a/contrib/bash_completion.d/Makefile.am b/contrib/bash_completion.d/Makefile.am index 8fbe03688b48..8c8d1acebe15 100644 --- a/contrib/bash_completion.d/Makefile.am +++ b/contrib/bash_completion.d/Makefile.am @@ -1,8 +1,13 @@ include $(top_srcdir)/config/Substfiles.am +include $(top_srcdir)/config/Shellcheck.am bashcompletiondir = $(sysconfdir)/bash_completion.d noinst_DATA = zfs EXTRA_DIST += $(noinst_DATA) SUBSTFILES += $(noinst_DATA) + +SHELLCHECKSCRIPTS = $(noinst_DATA) +SHELLCHECK_SHELL = bash +SHELLCHECK_IGNORE = ,SC2207 diff --git a/contrib/bash_completion.d/zfs.in b/contrib/bash_completion.d/zfs.in index f650c8f50009..41ce2f871e34 100644 --- a/contrib/bash_completion.d/zfs.in +++ b/contrib/bash_completion.d/zfs.in @@ -1,479 +1,484 @@ # Copyright (c) 2010-2016, Aneurin Price # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation # files (the "Software"), to deal in the Software without # restriction, including without limitation the rights to use, # copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following # conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. __ZFS_CMD="@sbindir@/zfs" __ZPOOL_CMD="@sbindir@/zpool" # Disable bash's built-in hostname completion, as this makes it impossible to # provide completions containing an @-sign, which is necessary for completing # snapshot names. If bash_completion is in use, this will already be disabled # and replaced with better completions anyway. shopt -u hostcomplete __zfs_get_commands() { $__ZFS_CMD 2>&1 | awk '/^\t[a-z]/ {print $1}' | cut -f1 -d '|' | uniq } __zfs_get_properties() { $__ZFS_CMD get 2>&1 | awk '$2 == "YES" || $2 == "NO" {print $1}'; echo all name space } __zfs_get_editable_properties() { $__ZFS_CMD get 2>&1 | awk '$2 == "YES" {print $1"="}' } __zfs_get_inheritable_properties() { $__ZFS_CMD get 2>&1 | awk '$3 == "YES" {print $1}' } __zfs_list_datasets() { $__ZFS_CMD list -H -o name -s name -t filesystem,volume "$@" } __zfs_list_filesystems() { $__ZFS_CMD list -H -o name -s name -t filesystem } __zfs_match_snapshot() { local base_dataset="${cur%@*}" if [ "$base_dataset" != "$cur" ] then $__ZFS_CMD list -H -o name -s name -t snapshot -d 1 "$base_dataset" else if [ "$cur" != "" ] && __zfs_list_datasets "$cur" &> /dev/null then $__ZFS_CMD list -H -o name -s name -t filesystem -r "$cur" | tail -n +2 # We output the base dataset name even though we might be # completing a command that can only take a snapshot, because it # prevents bash from considering the completion finished when it # ends in the bare @. echo "$cur" echo "$cur@" else local datasets datasets="$(__zfs_list_datasets)" # As above echo "$datasets" if [[ "$cur" == */ ]] then # If the current command ends with a slash, then the only way # it can be completed with a single tab press (ie. in this pass) # is if it has exactly one child, so that's the only time we # need to offer a suggestion with an @ appended. local num_children # This is actually off by one as zfs list includes the named # dataset in addition to its children num_children=$(__zfs_list_datasets -d 1 "${cur%/}" 2> /dev/null | wc -l) if [[ $num_children != 2 ]] then return 0 fi fi echo "$datasets" | awk '{print $1 "@"}' fi fi } __zfs_match_snapshot_or_bookmark() { local base_dataset="${cur%[#@]*}" if [ "$base_dataset" != "$cur" ] then if [[ $cur == *@* ]] then $__ZFS_CMD list -H -o name -s name -t snapshot -d 1 "$base_dataset" else $__ZFS_CMD list -H -o name -s name -t bookmark -d 1 "$base_dataset" fi else $__ZFS_CMD list -H -o name -s name -t filesystem,volume if [ -e "$cur" ] && $__ZFS_CMD list -H -o name -s name -t filesystem,volume "$cur" &> /dev/null then echo "$cur@" echo "$cur#" fi fi } __zfs_match_multiple_snapshots() { local existing_opts existing_opts="$(expr "$cur" : '\(.*\)[%,]')" if [ -e "$existing_opts" ] then local base_dataset="${cur%@*}" if [ "$base_dataset" != "$cur" ] then local cur="${cur##*,}" if [[ $cur =~ ^%|%.*% ]] then # correct range syntax is start%end return 1 fi local range_start range_start="$(expr "$cur" : '\(.*%\)')" + # shellcheck disable=SC2016 $__ZFS_CMD list -H -o name -s name -t snapshot -d 1 "$base_dataset" | sed 's$.*@$'"$range_start"'$g' fi else __zfs_match_snapshot_or_bookmark fi } __zfs_list_volumes() { $__ZFS_CMD list -H -o name -s name -t volume } __zfs_argument_chosen() { local word property for word in $(seq $((COMP_CWORD-1)) -1 2) do local prev="${COMP_WORDS[$word]}" if [[ ${COMP_WORDS[$word-1]} != -[tos] ]] then if [[ "$prev" == [^,]*,* ]] || [[ "$prev" == *[@:\#]* ]] then return 0 fi - for property in $@ + for property in "$@" do if [[ $prev == "$property"* ]] then return 0 fi done fi done return 1 } __zfs_complete_ordered_arguments() { local list1=$1 local list2=$2 local cur=$3 local extra=$4 + # shellcheck disable=SC2086 if __zfs_argument_chosen $list1 then COMPREPLY=($(compgen -W "$list2 $extra" -- "$cur")) else COMPREPLY=($(compgen -W "$list1 $extra" -- "$cur")) fi } __zfs_complete_multiple_options() { local options=$1 local cur=$2 + local existing_opts COMPREPLY=($(compgen -W "$options" -- "${cur##*,}")) - local existing_opts=$(expr "$cur" : '\(.*,\)') + existing_opts=$(expr "$cur" : '\(.*,\)') if [[ $existing_opts ]] then COMPREPLY=( "${COMPREPLY[@]/#/${existing_opts}}" ) fi } __zfs_complete_switch() { local options=$1 if [[ ${cur:0:1} == - ]] then COMPREPLY=($(compgen -W "-{$options}" -- "$cur")) return 0 else return 1 fi } __zfs_complete_nospace() { # Google indicates that there may still be bash versions out there that # don't have compopt. if type compopt &> /dev/null then compopt -o nospace fi } __zfs_complete() { local cur prev cmd cmds COMPREPLY=() if type _get_comp_words_by_ref &> /dev/null then # Don't split on colon _get_comp_words_by_ref -n : -c cur -p prev -w COMP_WORDS -i COMP_CWORD else cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" fi cmd="${COMP_WORDS[1]}" if [[ ${prev##*/} == zfs ]] then cmds=$(__zfs_get_commands) COMPREPLY=($(compgen -W "$cmds -?" -- "$cur")) return 0 fi case "${cmd}" in bookmark) if __zfs_argument_chosen then COMPREPLY=($(compgen -W "${prev%@*}# ${prev/@/#}" -- "$cur")) else COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) fi ;; clone) case "${prev}" in -o) COMPREPLY=($(compgen -W "$(__zfs_get_editable_properties)" -- "$cur")) __zfs_complete_nospace ;; *) if ! __zfs_complete_switch "o,p" then if __zfs_argument_chosen then COMPREPLY=($(compgen -W "$(__zfs_list_datasets)" -- "$cur")) else COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) fi fi ;; esac ;; get) case "${prev}" in -d) COMPREPLY=($(compgen -W "" -- "$cur")) ;; -t) __zfs_complete_multiple_options "filesystem volume snapshot bookmark all" "$cur" ;; -s) __zfs_complete_multiple_options "local default inherited temporary received none" "$cur" ;; -o) __zfs_complete_multiple_options "name property value source received all" "$cur" ;; *) if ! __zfs_complete_switch "H,r,p,d,o,t,s" then + # shellcheck disable=SC2046 if __zfs_argument_chosen $(__zfs_get_properties) then COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) else __zfs_complete_multiple_options "$(__zfs_get_properties)" "$cur" fi fi ;; esac ;; inherit) if ! __zfs_complete_switch "r" then - __zfs_complete_ordered_arguments "$(__zfs_get_inheritable_properties)" "$(__zfs_match_snapshot)" $cur + __zfs_complete_ordered_arguments "$(__zfs_get_inheritable_properties)" "$(__zfs_match_snapshot)" "$cur" fi ;; list) case "${prev}" in -d) COMPREPLY=($(compgen -W "" -- "$cur")) ;; -t) __zfs_complete_multiple_options "filesystem volume snapshot bookmark all" "$cur" ;; -o) __zfs_complete_multiple_options "$(__zfs_get_properties)" "$cur" ;; -s|-S) COMPREPLY=($(compgen -W "$(__zfs_get_properties)" -- "$cur")) ;; *) if ! __zfs_complete_switch "H,r,d,o,t,s,S" then COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) fi ;; esac ;; promote) COMPREPLY=($(compgen -W "$(__zfs_list_filesystems)" -- "$cur")) ;; rollback) if ! __zfs_complete_switch "r,R,f" then COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) fi ;; send) if ! __zfs_complete_switch "D,n,P,p,R,v,e,L,i,I" then if __zfs_argument_chosen then COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) else if [[ $prev == -*i* ]] then COMPREPLY=($(compgen -W "$(__zfs_match_snapshot_or_bookmark)" -- "$cur")) else COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) fi fi fi ;; snapshot) case "${prev}" in -o) COMPREPLY=($(compgen -W "$(__zfs_get_editable_properties)" -- "$cur")) __zfs_complete_nospace ;; *) if ! __zfs_complete_switch "o,r" then COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) __zfs_complete_nospace fi ;; esac ;; set) - __zfs_complete_ordered_arguments "$(__zfs_get_editable_properties)" "$(__zfs_match_snapshot)" $cur + __zfs_complete_ordered_arguments "$(__zfs_get_editable_properties)" "$(__zfs_match_snapshot)" "$cur" __zfs_complete_nospace ;; upgrade) case "${prev}" in -a|-V|-v) COMPREPLY=($(compgen -W "" -- "$cur")) ;; *) if ! __zfs_complete_switch "a,V,v,r" then COMPREPLY=($(compgen -W "$(__zfs_list_filesystems)" -- "$cur")) fi ;; esac ;; destroy) if ! __zfs_complete_switch "d,f,n,p,R,r,v" then - __zfs_complete_multiple_options "$(__zfs_match_multiple_snapshots)" $cur + __zfs_complete_multiple_options "$(__zfs_match_multiple_snapshots)" "$cur" __zfs_complete_nospace fi ;; *) COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) ;; esac if type __ltrim_colon_completions &> /dev/null then __ltrim_colon_completions "$cur" fi return 0 } __zpool_get_commands() { $__ZPOOL_CMD 2>&1 | awk '/^\t[a-z]/ {print $1}' | uniq } __zpool_get_properties() { $__ZPOOL_CMD get 2>&1 | awk '$2 == "YES" || $2 == "NO" {print $1}'; echo all } __zpool_get_editable_properties() { $__ZPOOL_CMD get 2>&1 | awk '$2 == "YES" {print $1"="}' } __zpool_list_pools() { $__ZPOOL_CMD list -H -o name } __zpool_complete() { - local cur prev cmd cmds + local cur prev cmd cmds pools COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" cmd="${COMP_WORDS[1]}" if [[ ${prev##*/} == zpool ]] then cmds=$(__zpool_get_commands) COMPREPLY=($(compgen -W "$cmds" -- "$cur")) return 0 fi case "${cmd}" in get) - __zfs_complete_ordered_arguments "$(__zpool_get_properties)" "$(__zpool_list_pools)" $cur + __zfs_complete_ordered_arguments "$(__zpool_get_properties)" "$(__zpool_list_pools)" "$cur" return 0 ;; import) if [[ $prev == -d ]] then _filedir -d else COMPREPLY=($(compgen -W "$(__zpool_list_pools) -d" -- "$cur")) fi return 0 ;; set) - __zfs_complete_ordered_arguments "$(__zpool_get_editable_properties)" "$(__zpool_list_pools)" $cur + __zfs_complete_ordered_arguments "$(__zpool_get_editable_properties)" "$(__zpool_list_pools)" "$cur" __zfs_complete_nospace return 0 ;; add|attach|clear|create|detach|offline|online|remove|replace) - local pools="$(__zpool_list_pools)" + pools="$(__zpool_list_pools)" + # shellcheck disable=SC2086 if __zfs_argument_chosen $pools then _filedir else COMPREPLY=($(compgen -W "$pools" -- "$cur")) fi return 0 ;; *) COMPREPLY=($(compgen -W "$(__zpool_list_pools)" -- "$cur")) return 0 ;; esac } complete -F __zfs_complete zfs complete -F __zpool_complete zpool diff --git a/contrib/bpftrace/Makefile.am b/contrib/bpftrace/Makefile.am index 327bc64425d8..05e4f1c507ce 100644 --- a/contrib/bpftrace/Makefile.am +++ b/contrib/bpftrace/Makefile.am @@ -1,3 +1,7 @@ +include $(top_srcdir)/config/Shellcheck.am + EXTRA_DIST = \ taskqlatency.bt \ zfs-trace.sh + +SHELLCHECKSCRIPTS = zfs-trace.sh diff --git a/contrib/dracut/02zfsexpandknowledge/Makefile.am b/contrib/dracut/02zfsexpandknowledge/Makefile.am index d31d389a0e5e..b1bbb6bd3aac 100644 --- a/contrib/dracut/02zfsexpandknowledge/Makefile.am +++ b/contrib/dracut/02zfsexpandknowledge/Makefile.am @@ -1,7 +1,8 @@ include $(top_srcdir)/config/Substfiles.am +include $(top_srcdir)/config/Shellcheck.am pkgdracutdir = $(dracutdir)/modules.d/02zfsexpandknowledge pkgdracut_SCRIPTS = \ module-setup.sh SUBSTFILES += $(pkgdracut_SCRIPTS) diff --git a/contrib/dracut/90zfs/Makefile.am b/contrib/dracut/90zfs/Makefile.am index a4843827ede3..19b206ddd7c9 100644 --- a/contrib/dracut/90zfs/Makefile.am +++ b/contrib/dracut/90zfs/Makefile.am @@ -1,19 +1,20 @@ include $(top_srcdir)/config/Substfiles.am +include $(top_srcdir)/config/Shellcheck.am pkgdracutdir = $(dracutdir)/modules.d/90zfs pkgdracut_SCRIPTS = \ export-zfs.sh \ module-setup.sh \ mount-zfs.sh \ parse-zfs.sh \ zfs-generator.sh \ zfs-load-key.sh \ zfs-needshutdown.sh \ zfs-lib.sh pkgdracut_DATA = \ zfs-env-bootfs.service \ zfs-snapshot-bootfs.service \ zfs-rollback-bootfs.service SUBSTFILES += $(pkgdracut_SCRIPTS) $(pkgdracut_DATA) diff --git a/contrib/dracut/90zfs/module-setup.sh.in b/contrib/dracut/90zfs/module-setup.sh.in index b0318de92ee6..6122551c3ae7 100755 --- a/contrib/dracut/90zfs/module-setup.sh.in +++ b/contrib/dracut/90zfs/module-setup.sh.in @@ -1,129 +1,129 @@ #!/usr/bin/env bash # shellcheck disable=SC2154 check() { # We depend on udev-rules being loaded [ "${1}" = "-d" ] && return 0 # Verify the zfs tool chain for tool in "@sbindir@/zgenhostid" "@sbindir@/zpool" "@sbindir@/zfs" "@mounthelperdir@/mount.zfs" ; do test -x "$tool" || return 1 done return 0 } depends() { echo udev-rules return 0 } installkernel() { instmods zfs instmods zcommon instmods znvpair instmods zavl instmods zunicode instmods zlua instmods icp instmods spl instmods zlib_deflate instmods zlib_inflate } install() { inst_rules @udevruledir@/90-zfs.rules inst_rules @udevruledir@/69-vdev.rules inst_rules @udevruledir@/60-zvol.rules dracut_install hostid dracut_install grep dracut_install @sbindir@/zgenhostid dracut_install @sbindir@/zfs dracut_install @sbindir@/zpool # Workaround for https://github.com/openzfs/zfs/issues/4749 by # ensuring libgcc_s.so(.1) is included if ldd @sbindir@/zpool | grep -qF 'libgcc_s.so'; then # Dracut will have already tracked and included it :; elif command -v gcc-config >/dev/null 2>&1; then # On systems with gcc-config (Gentoo, Funtoo, etc.): # Use the current profile to resolve the appropriate path s="$(gcc-config -c)" dracut_install "/usr/lib/gcc/${s%-*}/${s##*-}/libgcc_s.so.1" elif ls /usr/lib/libgcc_s.so* >/dev/null 2>&1; then # Try a simple path first dracut_install /usr/lib/libgcc_s.so* else # Fallback: Guess the path and include all matches dracut_install /usr/lib/gcc/*/*/libgcc_s.so* fi # shellcheck disable=SC2050 if [ @LIBFETCH_DYNAMIC@ != 0 ]; then for d in $libdirs; do - [ -e "$d"/@LIBFETCH_SONAME@ ] && dracut_install "$d"/@LIBFETCH_SONAME@ + [ -e "$d/"@LIBFETCH_SONAME@ ] && dracut_install "$d/"@LIBFETCH_SONAME@ done fi dracut_install @mounthelperdir@/mount.zfs dracut_install @udevdir@/vdev_id dracut_install awk dracut_install basename dracut_install cut dracut_install head dracut_install @udevdir@/zvol_id inst_hook cmdline 95 "${moddir}/parse-zfs.sh" if [ -n "$systemdutildir" ] ; then inst_script "${moddir}/zfs-generator.sh" "$systemdutildir"/system-generators/dracut-zfs-generator fi inst_hook pre-mount 90 "${moddir}/zfs-load-key.sh" inst_hook mount 98 "${moddir}/mount-zfs.sh" inst_hook cleanup 99 "${moddir}/zfs-needshutdown.sh" inst_hook shutdown 20 "${moddir}/export-zfs.sh" inst_simple "${moddir}/zfs-lib.sh" "/lib/dracut-zfs-lib.sh" if [ -e @sysconfdir@/zfs/zpool.cache ]; then inst @sysconfdir@/zfs/zpool.cache type mark_hostonly >/dev/null 2>&1 && mark_hostonly @sysconfdir@/zfs/zpool.cache fi if [ -e @sysconfdir@/zfs/vdev_id.conf ]; then inst @sysconfdir@/zfs/vdev_id.conf type mark_hostonly >/dev/null 2>&1 && mark_hostonly @sysconfdir@/zfs/vdev_id.conf fi # Synchronize initramfs and system hostid if [ -f @sysconfdir@/hostid ]; then inst @sysconfdir@/hostid type mark_hostonly >/dev/null 2>&1 && mark_hostonly @sysconfdir@/hostid elif HOSTID="$(hostid 2>/dev/null)" && [ "${HOSTID}" != "00000000" ]; then zgenhostid -o "${initdir}@sysconfdir@/hostid" "${HOSTID}" type mark_hostonly >/dev/null 2>&1 && mark_hostonly @sysconfdir@/hostid fi if dracut_module_included "systemd"; then mkdir -p "${initdir}/$systemdsystemunitdir/zfs-import.target.wants" for _item in scan cache ; do dracut_install @systemdunitdir@/zfs-import-$_item.service if ! [ -L "${initdir}/$systemdsystemunitdir/zfs-import.target.wants"/zfs-import-$_item.service ]; then ln -s ../zfs-import-$_item.service "${initdir}/$systemdsystemunitdir/zfs-import.target.wants"/zfs-import-$_item.service type mark_hostonly >/dev/null 2>&1 && mark_hostonly @systemdunitdir@/zfs-import-$_item.service fi done inst "${moddir}"/zfs-env-bootfs.service "${systemdsystemunitdir}"/zfs-env-bootfs.service ln -s ../zfs-env-bootfs.service "${initdir}/${systemdsystemunitdir}/zfs-import.target.wants"/zfs-env-bootfs.service type mark_hostonly >/dev/null 2>&1 && mark_hostonly @systemdunitdir@/zfs-env-bootfs.service dracut_install systemd-ask-password dracut_install systemd-tty-ask-password-agent mkdir -p "${initdir}/$systemdsystemunitdir/initrd.target.wants" dracut_install @systemdunitdir@/zfs-import.target if ! [ -L "${initdir}/$systemdsystemunitdir/initrd.target.wants"/zfs-import.target ]; then ln -s ../zfs-import.target "${initdir}/$systemdsystemunitdir/initrd.target.wants"/zfs-import.target type mark_hostonly >/dev/null 2>&1 && mark_hostonly @systemdunitdir@/zfs-import.target fi for _service in zfs-snapshot-bootfs.service zfs-rollback-bootfs.service ; do inst "${moddir}"/$_service "${systemdsystemunitdir}"/$_service if ! [ -L "${initdir}/$systemdsystemunitdir/initrd.target.wants"/$_service ]; then ln -s ../$_service "${initdir}/$systemdsystemunitdir/initrd.target.wants"/$_service fi done fi } diff --git a/contrib/dracut/Makefile.am b/contrib/dracut/Makefile.am index 1065e5e94f0e..8c9a6be0899a 100644 --- a/contrib/dracut/Makefile.am +++ b/contrib/dracut/Makefile.am @@ -1,3 +1,6 @@ +include $(top_srcdir)/config/Shellcheck.am + SUBDIRS = 02zfsexpandknowledge 90zfs +SHELLCHECKDIRS = $(SUBDIRS) EXTRA_DIST = README.dracut.markdown diff --git a/contrib/initramfs/Makefile.am b/contrib/initramfs/Makefile.am index 5eac6e2155db..931ceb1316a5 100644 --- a/contrib/initramfs/Makefile.am +++ b/contrib/initramfs/Makefile.am @@ -1,9 +1,12 @@ +include $(top_srcdir)/config/Shellcheck.am + initrddir = /usr/share/initramfs-tools dist_initrd_SCRIPTS = \ zfsunlock SUBDIRS = conf.d conf-hooks.d hooks scripts +SHELLCHECKDIRS = hooks scripts EXTRA_DIST = \ README.initramfs.markdown diff --git a/contrib/initramfs/hooks/Makefile.am b/contrib/initramfs/hooks/Makefile.am index f303e995b9c7..0cd1aafcd359 100644 --- a/contrib/initramfs/hooks/Makefile.am +++ b/contrib/initramfs/hooks/Makefile.am @@ -1,9 +1,10 @@ include $(top_srcdir)/config/Substfiles.am +include $(top_srcdir)/config/Shellcheck.am hooksdir = /usr/share/initramfs-tools/hooks hooks_SCRIPTS = \ zfs \ zfsunlock SUBSTFILES += $(hooks_SCRIPTS) diff --git a/contrib/initramfs/scripts/Makefile.am b/contrib/initramfs/scripts/Makefile.am index 2a142096e449..444a5f374bfe 100644 --- a/contrib/initramfs/scripts/Makefile.am +++ b/contrib/initramfs/scripts/Makefile.am @@ -1,6 +1,11 @@ +include $(top_srcdir)/config/Shellcheck.am + scriptsdir = /usr/share/initramfs-tools/scripts -dist_scripts_DATA = \ +dist_scripts_SCRIPTS = \ zfs SUBDIRS = local-top + +SHELLCHECKDIRS = $(SUBDIRS) +SHELLCHECK_SHELL = sh diff --git a/contrib/initramfs/scripts/local-top/Makefile.am b/contrib/initramfs/scripts/local-top/Makefile.am index 1523a907c860..897f9b2e2124 100644 --- a/contrib/initramfs/scripts/local-top/Makefile.am +++ b/contrib/initramfs/scripts/local-top/Makefile.am @@ -1,4 +1,6 @@ +include $(top_srcdir)/config/Shellcheck.am + localtopdir = /usr/share/initramfs-tools/scripts/local-top dist_localtop_SCRIPTS = \ zfs diff --git a/contrib/initramfs/scripts/zfs b/contrib/initramfs/scripts/zfs index b112ac0d2854..38122594b943 100644 --- a/contrib/initramfs/scripts/zfs +++ b/contrib/initramfs/scripts/zfs @@ -1,989 +1,993 @@ # ZFS boot stub for initramfs-tools. # # In the initramfs environment, the /init script sources this stub to # override the default functions in the /scripts/local script. # # Enable this by passing boot=zfs on the kernel command line. # +# $quiet, $root, $rpool, $bootfs come from the cmdline: +# shellcheck disable=SC2154 # Source the common functions . /etc/zfs/zfs-functions # Start interactive shell. # Use debian's panic() if defined, because it allows to prevent shell access # by setting panic in cmdline (e.g. panic=0 or panic=15). # See "4.5 Disable root prompt on the initramfs" of Securing Debian Manual: # https://www.debian.org/doc/manuals/securing-debian-howto/ch4.en.html shell() { if command -v panic > /dev/null 2>&1; then panic else /bin/sh fi } # This runs any scripts that should run before we start importing # pools and mounting any filesystems. pre_mountroot() { if command -v run_scripts > /dev/null 2>&1 then if [ -f "/scripts/local-top" ] || [ -d "/scripts/local-top" ] then [ "$quiet" != "y" ] && \ zfs_log_begin_msg "Running /scripts/local-top" run_scripts /scripts/local-top [ "$quiet" != "y" ] && zfs_log_end_msg fi if [ -f "/scripts/local-premount" ] || [ -d "/scripts/local-premount" ] then [ "$quiet" != "y" ] && \ zfs_log_begin_msg "Running /scripts/local-premount" run_scripts /scripts/local-premount [ "$quiet" != "y" ] && zfs_log_end_msg fi fi } # If plymouth is available, hide the splash image. disable_plymouth() { if [ -x /bin/plymouth ] && /bin/plymouth --ping then /bin/plymouth hide-splash >/dev/null 2>&1 fi } # Get a ZFS filesystem property value. get_fs_value() { fs="$1" value=$2 "${ZFS}" get -H -ovalue "$value" "$fs" 2> /dev/null } # Find the 'bootfs' property on pool $1. # If the property does not contain '/', then ignore this # pool by exporting it again. find_rootfs() { pool="$1" # If 'POOL_IMPORTED' isn't set, no pool imported and therefore # we won't be able to find a root fs. [ -z "${POOL_IMPORTED}" ] && return 1 # If it's already specified, just keep it mounted and exit # User (kernel command line) must be correct. [ -n "${ZFS_BOOTFS}" ] && return 0 # Not set, try to find it in the 'bootfs' property of the pool. # NOTE: zpool does not support 'get -H -ovalue bootfs'... ZFS_BOOTFS=$("${ZPOOL}" list -H -obootfs "$pool") # Make sure it's not '-' and that it starts with /. if [ "${ZFS_BOOTFS}" != "-" ] && \ get_fs_value "${ZFS_BOOTFS}" mountpoint | grep -q '^/$' then # Keep it mounted POOL_IMPORTED=1 return 0 fi # Not boot fs here, export it and later try again.. "${ZPOOL}" export "$pool" POOL_IMPORTED= ZFS_BOOTFS= return 1 } # Support function to get a list of all pools, separated with ';' find_pools() { - CMD="$*" - - pools=$($CMD 2> /dev/null | \ + pools=$("$@" 2> /dev/null | \ grep -E "pool:|^[a-zA-Z0-9]" | \ sed 's@.*: @@' | \ - while read -r pool; do \ - printf "%s" "$pool;" - done) + tr '\n' ';') echo "${pools%%;}" # Return without the last ';'. } # Get a list of all available pools get_pools() { if [ -n "${ZFS_POOL_IMPORT}" ]; then echo "$ZFS_POOL_IMPORT" return 0 fi # Get the base list of available pools. available_pools=$(find_pools "$ZPOOL" import) # Just in case - seen it happen (that a pool isn't visible/found # with a simple "zpool import" but only when using the "-d" # option or setting ZPOOL_IMPORT_PATH). if [ -d "/dev/disk/by-id" ] then npools=$(find_pools "$ZPOOL" import -d /dev/disk/by-id) if [ -n "$npools" ] then # Because we have found extra pool(s) here, which wasn't # found 'normally', we need to force USE_DISK_BY_ID to # make sure we're able to actually import it/them later. USE_DISK_BY_ID='yes' if [ -n "$available_pools" ] then # Filter out duplicates (pools found with the simple # "zpool import" but which is also found with the # "zpool import -d ..."). npools=$(echo "$npools" | sed "s,$available_pools,,") # Add the list to the existing list of # available pools available_pools="$available_pools;$npools" else available_pools="$npools" fi fi fi # Filter out any exceptions... if [ -n "$ZFS_POOL_EXCEPTIONS" ] then found="" apools="" OLD_IFS="$IFS" ; IFS=";" for pool in $available_pools do for exception in $ZFS_POOL_EXCEPTIONS do [ "$pool" = "$exception" ] && continue 2 found="$pool" done if [ -n "$found" ] then if [ -n "$apools" ] then apools="$apools;$pool" else apools="$pool" fi fi done IFS="$OLD_IFS" available_pools="$apools" fi # Return list of available pools. echo "$available_pools" } # Import given pool $1 import_pool() { pool="$1" # Verify that the pool isn't already imported # Make as sure as we can to not require '-f' to import. "${ZPOOL}" get name,guid -o value -H 2>/dev/null | grep -Fxq "$pool" && return 0 # For backwards compatibility, make sure that ZPOOL_IMPORT_PATH is set # to something we can use later with the real import(s). We want to # make sure we find all by* dirs, BUT by-vdev should be first (if it # exists). if [ -n "$USE_DISK_BY_ID" ] && [ -z "$ZPOOL_IMPORT_PATH" ] then - dirs="$(for dir in $(echo /dev/disk/by-*) + dirs="$(for dir in /dev/disk/by-* do # Ignore by-vdev here - we want it first! echo "$dir" | grep -q /by-vdev && continue [ ! -d "$dir" ] && continue printf "%s" "$dir:" done | sed 's,:$,,g')" if [ -d "/dev/disk/by-vdev" ] then # Add by-vdev at the beginning. ZPOOL_IMPORT_PATH="/dev/disk/by-vdev:" fi # ... and /dev at the very end, just for good measure. ZPOOL_IMPORT_PATH="$ZPOOL_IMPORT_PATH$dirs:/dev" fi # Needs to be exported for "zpool" to catch it. [ -n "$ZPOOL_IMPORT_PATH" ] && export ZPOOL_IMPORT_PATH [ "$quiet" != "y" ] && zfs_log_begin_msg \ "Importing pool '${pool}' using defaults" ZFS_CMD="${ZPOOL} import -N ${ZPOOL_FORCE} ${ZPOOL_IMPORT_OPTS}" ZFS_STDERR="$($ZFS_CMD "$pool" 2>&1)" ZFS_ERROR="$?" if [ "${ZFS_ERROR}" != 0 ] then [ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}" if [ -f "${ZPOOL_CACHE}" ] then [ "$quiet" != "y" ] && zfs_log_begin_msg \ "Importing pool '${pool}' using cachefile." ZFS_CMD="${ZPOOL} import -c ${ZPOOL_CACHE} -N ${ZPOOL_FORCE} ${ZPOOL_IMPORT_OPTS}" ZFS_STDERR="$($ZFS_CMD "$pool" 2>&1)" ZFS_ERROR="$?" fi if [ "${ZFS_ERROR}" != 0 ] then [ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}" disable_plymouth echo "" echo "Command: ${ZFS_CMD} '$pool'" echo "Message: $ZFS_STDERR" echo "Error: $ZFS_ERROR" echo "" echo "Failed to import pool '$pool'." echo "Manually import the pool and exit." shell fi fi [ "$quiet" != "y" ] && zfs_log_end_msg POOL_IMPORTED=1 return 0 } # Load ZFS modules # Loading a module in a initrd require a slightly different approach, # with more logging etc. load_module_initrd() { [ -n "$ROOTDELAY" ] && ZFS_INITRD_PRE_MOUNTROOT_SLEEP="$ROOTDELAY" if [ "$ZFS_INITRD_PRE_MOUNTROOT_SLEEP" -gt 0 ] 2>/dev/null then if [ "$quiet" != "y" ]; then zfs_log_begin_msg "Sleeping for" \ "$ZFS_INITRD_PRE_MOUNTROOT_SLEEP seconds..." fi sleep "$ZFS_INITRD_PRE_MOUNTROOT_SLEEP" [ "$quiet" != "y" ] && zfs_log_end_msg fi # Wait for all of the /dev/{hd,sd}[a-z] device nodes to appear. if command -v wait_for_udev > /dev/null 2>&1 ; then wait_for_udev 10 elif command -v wait_for_dev > /dev/null 2>&1 ; then wait_for_dev fi # zpool import refuse to import without a valid /proc/self/mounts [ ! -f /proc/self/mounts ] && mount proc /proc # Load the module load_module "zfs" || return 1 if [ "$ZFS_INITRD_POST_MODPROBE_SLEEP" -gt 0 ] 2>/dev/null then if [ "$quiet" != "y" ]; then zfs_log_begin_msg "Sleeping for" \ "$ZFS_INITRD_POST_MODPROBE_SLEEP seconds..." fi sleep "$ZFS_INITRD_POST_MODPROBE_SLEEP" [ "$quiet" != "y" ] && zfs_log_end_msg fi return 0 } # Mount a given filesystem mount_fs() { fs="$1" # Check that the filesystem exists "${ZFS}" list -oname -tfilesystem -H "${fs}" > /dev/null 2>&1 || return 1 # Skip filesystems with canmount=off. The root fs should not have # canmount=off, but ignore it for backwards compatibility just in case. if [ "$fs" != "${ZFS_BOOTFS}" ] then canmount=$(get_fs_value "$fs" canmount) [ "$canmount" = "off" ] && return 0 fi # Need the _original_ datasets mountpoint! mountpoint=$(get_fs_value "$fs" mountpoint) + ZFS_CMD="mount -o zfsutil -t zfs" if [ "$mountpoint" = "legacy" ] || [ "$mountpoint" = "none" ]; then # Can't use the mountpoint property. Might be one of our # clones. Check the 'org.zol:mountpoint' property set in # clone_snap() if that's usable. mountpoint=$(get_fs_value "$fs" org.zol:mountpoint) if [ "$mountpoint" = "legacy" ] || [ "$mountpoint" = "none" ] || [ "$mountpoint" = "-" ] then if [ "$fs" != "${ZFS_BOOTFS}" ]; then # We don't have a proper mountpoint and this # isn't the root fs. return 0 else # Last hail-mary: Hope 'rootmnt' is set! mountpoint="" fi fi + # If it's not a legacy filesystem, it can only be a + # native one... if [ "$mountpoint" = "legacy" ]; then ZFS_CMD="mount -t zfs" - else - # If it's not a legacy filesystem, it can only be a - # native one... - ZFS_CMD="mount -o zfsutil -t zfs" fi - else - ZFS_CMD="mount -o zfsutil -t zfs" fi # Possibly decrypt a filesystem using native encryption. decrypt_fs "$fs" [ "$quiet" != "y" ] && \ zfs_log_begin_msg "Mounting '${fs}' on '${rootmnt}/${mountpoint}'" [ -n "${ZFS_DEBUG}" ] && \ zfs_log_begin_msg "CMD: '$ZFS_CMD ${fs} ${rootmnt}/${mountpoint}'" ZFS_STDERR=$(${ZFS_CMD} "${fs}" "${rootmnt}/${mountpoint}" 2>&1) ZFS_ERROR=$? if [ "${ZFS_ERROR}" != 0 ] then [ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}" disable_plymouth echo "" echo "Command: ${ZFS_CMD} ${fs} ${rootmnt}/${mountpoint}" echo "Message: $ZFS_STDERR" echo "Error: $ZFS_ERROR" echo "" echo "Failed to mount ${fs} on ${rootmnt}/${mountpoint}." echo "Manually mount the filesystem and exit." shell else [ "$quiet" != "y" ] && zfs_log_end_msg fi return 0 } # Unlock a ZFS native encrypted filesystem. decrypt_fs() { fs="$1" # If pool encryption is active and the zfs command understands '-o encryption' if [ "$(zpool list -H -o feature@encryption "$(echo "${fs}" | awk -F/ '{print $1}')")" = 'active' ]; then # Determine dataset that holds key for root dataset ENCRYPTIONROOT="$(get_fs_value "${fs}" encryptionroot)" KEYLOCATION="$(get_fs_value "${ENCRYPTIONROOT}" keylocation)" echo "${ENCRYPTIONROOT}" > /run/zfs_fs_name # If root dataset is encrypted... if ! [ "${ENCRYPTIONROOT}" = "-" ]; then KEYSTATUS="$(get_fs_value "${ENCRYPTIONROOT}" keystatus)" # Continue only if the key needs to be loaded [ "$KEYSTATUS" = "unavailable" ] || return 0 # Do not prompt if key is stored noninteractively, if ! [ "${KEYLOCATION}" = "prompt" ]; then $ZFS load-key "${ENCRYPTIONROOT}" # Prompt with plymouth, if active elif /bin/plymouth --ping 2>/dev/null; then echo "plymouth" > /run/zfs_console_askpwd_cmd for _ in 1 2 3; do plymouth ask-for-password --prompt "Encrypted ZFS password for ${ENCRYPTIONROOT}" | \ $ZFS load-key "${ENCRYPTIONROOT}" && break done # Prompt with systemd, if active elif [ -e /run/systemd/system ]; then echo "systemd-ask-password" > /run/zfs_console_askpwd_cmd for _ in 1 2 3; do systemd-ask-password "Encrypted ZFS password for ${ENCRYPTIONROOT}" --no-tty | \ $ZFS load-key "${ENCRYPTIONROOT}" && break done # Prompt with ZFS tty, otherwise else # Temporarily setting "printk" to "7" allows the prompt to appear even when the "quiet" kernel option has been used echo "load-key" > /run/zfs_console_askpwd_cmd storeprintk="$(awk '{print $1}' /proc/sys/kernel/printk)" echo 7 > /proc/sys/kernel/printk $ZFS load-key "${ENCRYPTIONROOT}" echo "$storeprintk" > /proc/sys/kernel/printk fi fi fi return 0 } # Destroy a given filesystem. destroy_fs() { fs="$1" [ "$quiet" != "y" ] && \ zfs_log_begin_msg "Destroying '$fs'" ZFS_CMD="${ZFS} destroy $fs" ZFS_STDERR="$(${ZFS_CMD} 2>&1)" ZFS_ERROR="$?" if [ "${ZFS_ERROR}" != 0 ] then [ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}" disable_plymouth echo "" echo "Command: $ZFS_CMD" echo "Message: $ZFS_STDERR" echo "Error: $ZFS_ERROR" echo "" echo "Failed to destroy '$fs'. Please make sure that '$fs' is not available." echo "Hint: Try: zfs destroy -Rfn $fs" echo "If this dryrun looks good, then remove the 'n' from '-Rfn' and try again." shell else [ "$quiet" != "y" ] && zfs_log_end_msg fi return 0 } # Clone snapshot $1 to destination filesystem $2 # Set 'canmount=noauto' and 'mountpoint=none' so that we get to keep # manual control over it's mounting (i.e., make sure it's not automatically # mounted with a 'zfs mount -a' in the init/systemd scripts). clone_snap() { snap="$1" destfs="$2" mountpoint="$3" [ "$quiet" != "y" ] && zfs_log_begin_msg "Cloning '$snap' to '$destfs'" # Clone the snapshot into a dataset we can boot from # + We don't want this filesystem to be automatically mounted, we # want control over this here and nowhere else. # + We don't need any mountpoint set for the same reason. # We use the 'org.zol:mountpoint' property to remember the mountpoint. ZFS_CMD="${ZFS} clone -o canmount=noauto -o mountpoint=none" ZFS_CMD="${ZFS_CMD} -o org.zol:mountpoint=${mountpoint}" ZFS_CMD="${ZFS_CMD} $snap $destfs" ZFS_STDERR="$(${ZFS_CMD} 2>&1)" ZFS_ERROR="$?" if [ "${ZFS_ERROR}" != 0 ] then [ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}" disable_plymouth echo "" echo "Command: $ZFS_CMD" echo "Message: $ZFS_STDERR" echo "Error: $ZFS_ERROR" echo "" echo "Failed to clone snapshot." echo "Make sure that the any problems are corrected and then make sure" echo "that the dataset '$destfs' exists and is bootable." shell else [ "$quiet" != "y" ] && zfs_log_end_msg fi return 0 } # Rollback a given snapshot. rollback_snap() { snap="$1" [ "$quiet" != "y" ] && zfs_log_begin_msg "Rollback $snap" ZFS_CMD="${ZFS} rollback -Rf $snap" ZFS_STDERR="$(${ZFS_CMD} 2>&1)" ZFS_ERROR="$?" if [ "${ZFS_ERROR}" != 0 ] then [ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}" disable_plymouth echo "" echo "Command: $ZFS_CMD" echo "Message: $ZFS_STDERR" echo "Error: $ZFS_ERROR" echo "" echo "Failed to rollback snapshot." shell else [ "$quiet" != "y" ] && zfs_log_end_msg fi return 0 } # Get a list of snapshots, give them as a numbered list # to the user to choose from. ask_user_snap() { fs="$1" - i=1 # We need to temporarily disable debugging. Set 'debug' so we # remember to enabled it again. if [ -n "${ZFS_DEBUG}" ]; then unset ZFS_DEBUG set +x debug=1 fi # Because we need the resulting snapshot, which is sent on # stdout to the caller, we use stderr for our questions. echo "What snapshot do you want to boot from?" > /dev/stderr - while read -r snap; do - echo " $i: ${snap}" > /dev/stderr - eval "$(echo SNAP_$i=$snap)" - i=$((i + 1)) - done < /dev/stderr - read -r snapnr + i=1 + for snap in "$@"; do + echo " $i: $snap" + i=$((i + 1)) + done > /dev/stderr + + # expr instead of test here because [ a -lt 0 ] errors out, + # but expr falls back to lexicographical, which works out right + snapnr=0 + while expr "$snapnr" "<" 1 > /dev/null || + expr "$snapnr" ">" "$#" > /dev/null + do + printf "%s" "Snap nr [1-$#]? " > /dev/stderr + read -r snapnr + done # Re-enable debugging. if [ -n "${debug}" ]; then ZFS_DEBUG=1 set -x fi - echo "$(eval echo '$SNAP_'$snapnr)" + eval echo '$'"$snapnr" } setup_snapshot_booting() { snap="$1" retval=0 # Make sure that the snapshot specified actually exists. if [ ! "$(get_fs_value "${snap}" type)" ] then # Snapshot does not exist (...@ ?) # ask the user for a snapshot to use. snap="$(ask_user_snap "${snap%%@*}")" fi # Separate the full snapshot ('$snap') into it's filesystem and # snapshot names. Would have been nice with a split() function.. rootfs="${snap%%@*}" snapname="${snap##*@}" ZFS_BOOTFS="${rootfs}_${snapname}" if ! grep -qiE '(^|[^\\](\\\\)* )(rollback)=(on|yes|1)( |$)' /proc/cmdline then # If the destination dataset for the clone # already exists, destroy it. Recursively if [ "$(get_fs_value "${rootfs}_${snapname}" type)" ]; then filesystems=$("${ZFS}" list -oname -tfilesystem -H \ -r -Sname "${ZFS_BOOTFS}") for fs in $filesystems; do destroy_fs "${fs}" done fi fi # Get all snapshots, recursively (might need to clone /usr, /var etc # as well). for s in $("${ZFS}" list -H -oname -tsnapshot -r "${rootfs}" | \ grep "${snapname}") do if grep -qiE '(^|[^\\](\\\\)* )(rollback)=(on|yes|1)( |$)' /proc/cmdline then # Rollback snapshot rollback_snap "$s" || retval=$((retval + 1)) else # Setup a destination filesystem name. # Ex: Called with 'rpool/ROOT/debian@snap2' # rpool/ROOT/debian@snap2 => rpool/ROOT/debian_snap2 # rpool/ROOT/debian/boot@snap2 => rpool/ROOT/debian_snap2/boot # rpool/ROOT/debian/usr@snap2 => rpool/ROOT/debian_snap2/usr # rpool/ROOT/debian/var@snap2 => rpool/ROOT/debian_snap2/var subfs="${s##$rootfs}" subfs="${subfs%%@$snapname}" destfs="${rootfs}_${snapname}" # base fs. [ -n "$subfs" ] && destfs="${destfs}$subfs" # + sub fs. # Get the mountpoint of the filesystem, to be used # with clone_snap(). If legacy or none, then use # the sub fs value. mountpoint=$(get_fs_value "${s%%@*}" mountpoint) if [ "$mountpoint" = "legacy" ] || \ [ "$mountpoint" = "none" ] then if [ -n "${subfs}" ]; then mountpoint="${subfs}" else mountpoint="/" fi fi # Clone the snapshot into its own # filesystem clone_snap "$s" "${destfs}" "${mountpoint}" || \ retval=$((retval + 1)) fi done # If we haven't return yet, we have a problem... return "${retval}" } # ================================================================ # This is the main function. mountroot() { # ---------------------------------------------------------------- # I N I T I A L S E T U P # ------------ # Run the pre-mount scripts from /scripts/local-top. pre_mountroot # ------------ # Source the default setup variables. [ -r '/etc/default/zfs' ] && . /etc/default/zfs # ------------ # Support debug option if grep -qiE '(^|[^\\](\\\\)* )(zfs_debug|zfs\.debug|zfsdebug)=(on|yes|1)( |$)' /proc/cmdline then ZFS_DEBUG=1 mkdir /var/log #exec 2> /var/log/boot.debug set -x fi # ------------ # Load ZFS module etc. if ! load_module_initrd; then disable_plymouth echo "" echo "Failed to load ZFS modules." echo "Manually load the modules and exit." shell fi # ------------ # Look for the cache file (if any). - [ ! -f ${ZPOOL_CACHE} ] && unset ZPOOL_CACHE + [ ! -f "${ZPOOL_CACHE}" ] && unset ZPOOL_CACHE # ------------ # Compatibility: 'ROOT' is for Debian GNU/Linux (etc), # 'root' is for Redhat/Fedora (etc), # 'REAL_ROOT' is for Gentoo if [ -z "$ROOT" ] then [ -n "$root" ] && ROOT=${root} [ -n "$REAL_ROOT" ] && ROOT=${REAL_ROOT} fi # ------------ # Where to mount the root fs in the initrd - set outside this script # Compatibility: 'rootmnt' is for Debian GNU/Linux (etc), # 'NEWROOT' is for RedHat/Fedora (etc), # 'NEW_ROOT' is for Gentoo if [ -z "$rootmnt" ] then [ -n "$NEWROOT" ] && rootmnt=${NEWROOT} [ -n "$NEW_ROOT" ] && rootmnt=${NEW_ROOT} fi # ------------ # No longer set in the defaults file, but it could have been set in # get_pools() in some circumstances. If it's something, but not 'yes', # it's no good to us. [ -n "$USE_DISK_BY_ID" ] && [ "$USE_DISK_BY_ID" != 'yes' ] && \ unset USE_DISK_BY_ID # ---------------------------------------------------------------- # P A R S E C O M M A N D L I N E O P T I O N S # This part is the really ugly part - there's so many options and permutations # 'out there', and if we should make this the 'primary' source for ZFS initrd # scripting, we need/should support them all. # # Supports the following kernel command line argument combinations # (in this order - first match win): # # rpool= (tries to finds bootfs automatically) # bootfs=/ (uses this for rpool - first part) # rpool= bootfs=/ # -B zfs-bootfs=/ (uses this for rpool - first part) # rpool=rpool (default if none of the above is used) # root=/ (uses this for rpool - first part) # root=ZFS=/ (uses this for rpool - first part, without 'ZFS=') # root=zfs:AUTO (tries to detect both pool and rootfs # root=zfs:/ (uses this for rpool - first part, without 'zfs:') # # Option could also be # Option could also be # ------------ # Support force option # In addition, setting one of zfs_force, zfs.force or zfsforce to # 'yes', 'on' or '1' will make sure we force import the pool. # This should (almost) never be needed, but it's here for # completeness. ZPOOL_FORCE="" if grep -qiE '(^|[^\\](\\\\)* )(zfs_force|zfs\.force|zfsforce)=(on|yes|1)( |$)' /proc/cmdline then ZPOOL_FORCE="-f" fi # ------------ # Look for 'rpool' and 'bootfs' parameter [ -n "$rpool" ] && ZFS_RPOOL="${rpool#rpool=}" [ -n "$bootfs" ] && ZFS_BOOTFS="${bootfs#bootfs=}" # ------------ # If we have 'ROOT' (see above), but not 'ZFS_BOOTFS', then use # 'ROOT' [ -n "$ROOT" ] && [ -z "${ZFS_BOOTFS}" ] && ZFS_BOOTFS="$ROOT" # ------------ # Check for the `-B zfs-bootfs=%s/%u,...` kind of parameter. # NOTE: Only use the pool name and dataset. The rest is not # supported by OpenZFS (whatever it's for). if [ -z "$ZFS_RPOOL" ] then # The ${zfs-bootfs} variable is set at the kernel command # line, usually by GRUB, but it cannot be referenced here # directly because bourne variable names cannot contain a # hyphen. # # Reassign the variable by dumping the environment and # stripping the zfs-bootfs= prefix. Let the shell handle - # quoting through the eval command. + # quoting through the eval command: + # shellcheck disable=SC2046 eval ZFS_RPOOL=$(set | sed -n -e 's,^zfs-bootfs=,,p') fi # ------------ # No root fs or pool specified - do auto detect. if [ -z "$ZFS_RPOOL" ] && [ -z "${ZFS_BOOTFS}" ] then # Do auto detect. Do this by 'cheating' - set 'root=zfs:AUTO' # which will be caught later ROOT='zfs:AUTO' fi # ---------------------------------------------------------------- # F I N D A N D I M P O R T C O R R E C T P O O L # ------------ if [ "$ROOT" = "zfs:AUTO" ] then # Try to detect both pool and root fs. # If we got here, that means we don't have a hint so as to # the root dataset, but with root=zfs:AUTO on cmdline, # this says "zfs:AUTO" here and interferes with checks later ZFS_BOOTFS= [ "$quiet" != "y" ] && \ zfs_log_begin_msg "Attempting to import additional pools." # Get a list of pools available for import if [ -n "$ZFS_RPOOL" ] then # We've specified a pool - check only that POOLS=$ZFS_RPOOL else POOLS=$(get_pools) fi OLD_IFS="$IFS" ; IFS=";" for pool in $POOLS do [ -z "$pool" ] && continue IFS="$OLD_IFS" import_pool "$pool" IFS="$OLD_IFS" find_rootfs "$pool" && break done IFS="$OLD_IFS" [ "$quiet" != "y" ] && zfs_log_end_msg $ZFS_ERROR else # No auto - use value from the command line option. # Strip 'zfs:' and 'ZFS='. ZFS_BOOTFS="${ROOT#*[:=]}" # Strip everything after the first slash. ZFS_RPOOL="${ZFS_BOOTFS%%/*}" fi # Import the pool (if not already done so in the AUTO check above). if [ -n "$ZFS_RPOOL" ] && [ -z "${POOL_IMPORTED}" ] then [ "$quiet" != "y" ] && \ zfs_log_begin_msg "Importing ZFS root pool '$ZFS_RPOOL'" import_pool "${ZFS_RPOOL}" find_rootfs "${ZFS_RPOOL}" [ "$quiet" != "y" ] && zfs_log_end_msg fi if [ -z "${POOL_IMPORTED}" ] then # No pool imported, this is serious! disable_plymouth echo "" echo "Command: $ZFS_CMD" echo "Message: $ZFS_STDERR" echo "Error: $ZFS_ERROR" echo "" echo "No pool imported. Manually import the root pool" echo "at the command prompt and then exit." echo "Hint: Try: zpool import -N ${ZFS_RPOOL}" shell fi # In case the pool was specified as guid, resolve guid to name pool="$("${ZPOOL}" get name,guid -o name,value -H | \ awk -v pool="${ZFS_RPOOL}" '$2 == pool { print $1 }')" if [ -n "$pool" ]; then # If $ZFS_BOOTFS contains guid, replace the guid portion with $pool ZFS_BOOTFS=$(echo "$ZFS_BOOTFS" | \ sed -e "s/$("${ZPOOL}" get guid -o value "$pool" -H)/$pool/g") ZFS_RPOOL="${pool}" fi # ---------------------------------------------------------------- # P R E P A R E R O O T F I L E S Y S T E M if [ -n "${ZFS_BOOTFS}" ] then # Booting from a snapshot? # Will overwrite the ZFS_BOOTFS variable like so: # rpool/ROOT/debian@snap2 => rpool/ROOT/debian_snap2 echo "${ZFS_BOOTFS}" | grep -q '@' && \ setup_snapshot_booting "${ZFS_BOOTFS}" fi if [ -z "${ZFS_BOOTFS}" ] then # Still nothing! Let the user sort this out. disable_plymouth echo "" echo "Error: Unknown root filesystem - no 'bootfs' pool property and" echo " not specified on the kernel command line." echo "" echo "Manually mount the root filesystem on $rootmnt and then exit." echo "Hint: Try: mount -o zfsutil -t zfs ${ZFS_RPOOL-rpool}/ROOT/system $rootmnt" shell fi # ---------------------------------------------------------------- # M O U N T F I L E S Y S T E M S # * Ideally, the root filesystem would be mounted like this: # # zpool import -R "$rootmnt" -N "$ZFS_RPOOL" # zfs mount -o mountpoint=/ "${ZFS_BOOTFS}" # # but the MOUNTPOINT prefix is preserved on descendent filesystem # after the pivot into the regular root, which later breaks things # like `zfs mount -a` and the /proc/self/mounts refresh. # # * Mount additional filesystems required # Such as /usr, /var, /usr/local etc. # NOTE: Mounted in the order specified in the # ZFS_INITRD_ADDITIONAL_DATASETS variable so take care! # Go through the complete list (recursively) of all filesystems below # the real root dataset filesystems="$("${ZFS}" list -oname -tfilesystem -H -r "${ZFS_BOOTFS}")" OLD_IFS="$IFS" ; IFS=" " for fs in $filesystems; do IFS="$OLD_IFS" mount_fs "$fs" done IFS="$OLD_IFS" for fs in $ZFS_INITRD_ADDITIONAL_DATASETS; do mount_fs "$fs" done touch /run/zfs_unlock_complete if [ -e /run/zfs_unlock_complete_notify ]; then - read -r zfs_unlock_complete_notify < /run/zfs_unlock_complete_notify + read -r < /run/zfs_unlock_complete_notify fi # ------------ # Debugging information if [ -n "${ZFS_DEBUG}" ] then #exec 2>&1- echo "DEBUG: imported pools:" "${ZPOOL}" list -H echo echo "DEBUG: mounted ZFS filesystems:" mount | grep zfs echo echo "=> waiting for ENTER before continuing because of 'zfsdebug=1'. " printf "%s" " 'c' for shell, 'r' for reboot, 'ENTER' to continue. " read -r b [ "$b" = "c" ] && /bin/sh [ "$b" = "r" ] && reboot -f set +x fi # ------------ # Run local bottom script if command -v run_scripts > /dev/null 2>&1 then if [ -f "/scripts/local-bottom" ] || [ -d "/scripts/local-bottom" ] then [ "$quiet" != "y" ] && \ zfs_log_begin_msg "Running /scripts/local-bottom" run_scripts /scripts/local-bottom [ "$quiet" != "y" ] && zfs_log_end_msg fi fi } diff --git a/etc/Makefile.am b/etc/Makefile.am index ac71da9445d8..aa9ff182c863 100644 --- a/etc/Makefile.am +++ b/etc/Makefile.am @@ -1,5 +1,9 @@ +include $(top_srcdir)/config/Shellcheck.am + SUBDIRS = zfs sudoers.d +SHELLCHECKDIRS = zfs if BUILD_LINUX +SHELLCHECKDIRS += default $(ZFS_INIT_SYSV) SUBDIRS += default $(ZFS_INIT_SYSTEMD) $(ZFS_INIT_SYSV) $(ZFS_MODULE_LOAD) endif DIST_SUBDIRS = default init.d zfs systemd modules-load.d sudoers.d diff --git a/etc/default/Makefile.am b/etc/default/Makefile.am index 0ec868e13484..b88eb549493b 100644 --- a/etc/default/Makefile.am +++ b/etc/default/Makefile.am @@ -1,5 +1,9 @@ include $(top_srcdir)/config/Substfiles.am +include $(top_srcdir)/config/Shellcheck.am initconf_SCRIPTS = zfs SUBSTFILES += $(initconf_SCRIPTS) + +SHELLCHECK_SHELL = sh +SHELLCHECK_IGNORE = ,SC2034 diff --git a/etc/init.d/Makefile.am b/etc/init.d/Makefile.am index 9285a995a1cf..f93af1fd77e0 100644 --- a/etc/init.d/Makefile.am +++ b/etc/init.d/Makefile.am @@ -1,7 +1,10 @@ include $(top_srcdir)/config/Substfiles.am +include $(top_srcdir)/config/Shellcheck.am EXTRA_DIST += README.md init_SCRIPTS = zfs-import zfs-mount zfs-share zfs-zed SUBSTFILES += $(init_SCRIPTS) + +SHELLCHECK_SHELL = dash # local variables diff --git a/etc/init.d/zfs-import.in b/etc/init.d/zfs-import.in index 6b1b2f243610..e4bc7b8339fc 100755 --- a/etc/init.d/zfs-import.in +++ b/etc/init.d/zfs-import.in @@ -1,335 +1,338 @@ #!@DEFAULT_INIT_SHELL@ # # zfs-import This script will import ZFS pools # # chkconfig: 2345 01 99 # description: This script will perform a verbatim import of ZFS pools # during system boot. # probe: true # ### BEGIN INIT INFO # Provides: zfs-import # Required-Start: mtab # Required-Stop: $local_fs mtab # Default-Start: S # Default-Stop: 0 1 6 # X-Start-Before: checkfs # X-Stop-After: zfs-mount # Short-Description: Import ZFS pools # Description: Run the `zpool import` command. ### END INIT INFO # # NOTE: Not having '$local_fs' on Required-Start but only on Required-Stop # is on purpose. If we have '$local_fs' in both (and X-Start-Before=checkfs) # we get conflicts - import needs to be started extremely early, # but not stopped too late. # # Released under the 2-clause BSD license. # # This script is based on debian/zfsutils.zfs.init from the # Debian GNU/kFreeBSD zfsutils 8.1-3 package, written by Aurelien Jarno. # Source the common init script . @sysconfdir@/zfs/zfs-functions # ---------------------------------------------------- do_depend() { before swap after sysfs udev keyword -lxc -openvz -prefix -vserver } # Use the zpool cache file to import pools do_verbatim_import() { if [ -f "$ZPOOL_CACHE" ] then zfs_action "Importing ZFS pool(s)" \ "$ZPOOL" import -c "$ZPOOL_CACHE" -N -a fi } # Support function to get a list of all pools, separated with ';' find_pools() { local pools pools=$("$@" 2> /dev/null | \ grep -E "pool:|^[a-zA-Z0-9]" | \ sed 's@.*: @@' | \ sort | \ tr '\n' ';') echo "${pools%%;}" # Return without the last ';'. } # Find and import all visible pools, even exported ones do_import_all_visible() { local already_imported available_pools pool npools local exception dir ZPOOL_IMPORT_PATH RET=0 r=1 # In case not shutdown cleanly. + # shellcheck disable=SC2154 [ -n "$init" ] && rm -f /etc/dfs/sharetab # Just simplify code later on. if [ -n "$USE_DISK_BY_ID" ] && [ "$USE_DISK_BY_ID" != 'yes' ] then # It's something, but not 'yes' so it's no good to us. unset USE_DISK_BY_ID fi # Find list of already imported pools. already_imported=$(find_pools "$ZPOOL" list -H -oname) available_pools=$(find_pools "$ZPOOL" import) # Just in case - seen it happen (that a pool isn't visible/found # with a simple "zpool import" but only when using the "-d" # option or setting ZPOOL_IMPORT_PATH). if [ -d "/dev/disk/by-id" ] then npools=$(find_pools "$ZPOOL" import -d /dev/disk/by-id) if [ -n "$npools" ] then # Because we have found extra pool(s) here, which wasn't # found 'normally', we need to force USE_DISK_BY_ID to # make sure we're able to actually import it/them later. USE_DISK_BY_ID='yes' if [ -n "$available_pools" ] then # Filter out duplicates (pools found with the simpl # "zpool import" but which is also found with the # "zpool import -d ..."). npools=$(echo "$npools" | sed "s,$available_pools,,") # Add the list to the existing list of # available pools available_pools="$available_pools;$npools" else available_pools="$npools" fi fi fi # Filter out any exceptions... if [ -n "$ZFS_POOL_EXCEPTIONS" ] then local found="" local apools="" OLD_IFS="$IFS" ; IFS=";" for pool in $available_pools do for exception in $ZFS_POOL_EXCEPTIONS do [ "$pool" = "$exception" ] && continue 2 found="$pool" done if [ -n "$found" ] then if [ -n "$apools" ] then apools="$apools;$pool" else apools="$pool" fi fi done IFS="$OLD_IFS" available_pools="$apools" fi # For backwards compatibility, make sure that ZPOOL_IMPORT_PATH is set # to something we can use later with the real import(s). We want to # make sure we find all by* dirs, BUT by-vdev should be first (if it # exists). if [ -n "$USE_DISK_BY_ID" ] && [ -z "$ZPOOL_IMPORT_PATH" ] then local dirs dirs="$(for dir in $(echo /dev/disk/by-*) do # Ignore by-vdev here - we want it first! echo "$dir" | grep -q /by-vdev && continue [ ! -d "$dir" ] && continue - echo -n "$dir:" + printf "%s" "$dir:" done | sed 's,:$,,g')" if [ -d "/dev/disk/by-vdev" ] then # Add by-vdev at the beginning. ZPOOL_IMPORT_PATH="/dev/disk/by-vdev:" fi # Help with getting LUKS partitions etc imported. if [ -d "/dev/mapper" ]; then if [ -n "$ZPOOL_IMPORT_PATH" ]; then ZPOOL_IMPORT_PATH="$ZPOOL_IMPORT_PATH:/dev/mapper:" else ZPOOL_IMPORT_PATH="/dev/mapper:" fi fi # ... and /dev at the very end, just for good measure. ZPOOL_IMPORT_PATH="$ZPOOL_IMPORT_PATH$dirs:/dev" fi # Needs to be exported for "zpool" to catch it. [ -n "$ZPOOL_IMPORT_PATH" ] && export ZPOOL_IMPORT_PATH # Mount all available pools (except those set in ZFS_POOL_EXCEPTIONS. # # If not interactive (run from init - variable init='/sbin/init') # we get ONE line for all pools being imported, with just a dot # as status for each pool. # Example: Importing ZFS pool(s)... [OK] # # If it IS interactive (started from the shell manually), then we # get one line per pool importing. # Example: Importing ZFS pool pool1 [OK] # Importing ZFS pool pool2 [OK] # [etc] [ -n "$init" ] && zfs_log_begin_msg "Importing ZFS pool(s)" OLD_IFS="$IFS" ; IFS=";" for pool in $available_pools do [ -z "$pool" ] && continue # We have pools that haven't been imported - import them if [ -n "$init" ] then # Not interactive - a dot for each pool. # Except on Gentoo where this doesn't work. zfs_log_progress_msg "." else # Interactive - one 'Importing ...' line per pool zfs_log_begin_msg "Importing ZFS pool $pool" fi # Import by using ZPOOL_IMPORT_PATH (either set above or in # the config file) _or_ with the 'built in' default search # paths. This is the preferred way. + # shellcheck disable=SC2086 "$ZPOOL" import -N ${ZPOOL_IMPORT_OPTS} "$pool" 2> /dev/null r="$?" ; RET=$((RET + r)) if [ "$r" -eq 0 ] then # Output success and process the next pool [ -z "$init" ] && zfs_log_end_msg 0 continue fi # We don't want a fail msg here, we're going to try import # using the cache file soon and that might succeed. [ ! -f "$ZPOOL_CACHE" ] && zfs_log_end_msg "$RET" if [ "$r" -gt 0 ] && [ -f "$ZPOOL_CACHE" ] then # Failed to import without a cache file. Try WITH... if [ -z "$init" ] && check_boolean "$VERBOSE_MOUNT" then # Interactive + Verbose = more information zfs_log_progress_msg " using cache file" fi + # shellcheck disable=SC2086 "$ZPOOL" import -c "$ZPOOL_CACHE" -N ${ZPOOL_IMPORT_OPTS} \ "$pool" 2> /dev/null r="$?" ; RET=$((RET + r)) if [ "$r" -eq 0 ] then [ -z "$init" ] && zfs_log_end_msg 0 continue 3 # Next pool fi zfs_log_end_msg "$RET" fi done [ -n "$init" ] && zfs_log_end_msg "$RET" IFS="$OLD_IFS" [ -n "$already_imported" ] && [ -z "$available_pools" ] && return 0 return "$RET" } do_import() { if check_boolean "$ZPOOL_IMPORT_ALL_VISIBLE" then do_import_all_visible else # This is the default option do_verbatim_import fi } # Output the status and list of pools do_status() { check_module_loaded "zfs" || exit 0 "$ZPOOL" status && echo "" && "$ZPOOL" list } do_start() { if check_boolean "$VERBOSE_MOUNT" then zfs_log_begin_msg "Checking if ZFS userspace tools present" fi if checksystem then check_boolean "$VERBOSE_MOUNT" && zfs_log_end_msg 0 check_boolean "$VERBOSE_MOUNT" && \ zfs_log_begin_msg "Loading kernel ZFS infrastructure" if ! load_module "zfs" then check_boolean "$VERBOSE_MOUNT" && zfs_log_end_msg 1 return 5 fi check_boolean "$VERBOSE_MOUNT" && zfs_log_end_msg 0 do_import && udev_trigger # just to make sure we get zvols. return 0 else return 1 fi } # ---------------------------------------------------- if [ ! -e /sbin/openrc-run ] then case "$1" in start) do_start ;; stop) # no-op ;; status) do_status ;; force-reload|condrestart|reload|restart) # no-op ;; *) [ -n "$1" ] && echo "Error: Unknown command $1." echo "Usage: $0 {start|status}" exit 3 ;; esac exit $? else # Create wrapper functions since Gentoo don't use the case part. depend() { do_depend; } start() { do_start; } status() { do_status; } fi diff --git a/etc/init.d/zfs-mount.in b/etc/init.d/zfs-mount.in index cb571faf92a9..000619b6717c 100755 --- a/etc/init.d/zfs-mount.in +++ b/etc/init.d/zfs-mount.in @@ -1,224 +1,224 @@ #!@DEFAULT_INIT_SHELL@ # # zfs-mount This script will mount/umount the zfs filesystems. # # chkconfig: 2345 06 99 # description: This script will mount/umount the zfs filesystems during # system boot/shutdown. Configuration of which filesystems # should be mounted is handled by the zfs 'mountpoint' and # 'canmount' properties. See the zfs(8) man page for details. # It is also responsible for all userspace zfs services. # probe: true # ### BEGIN INIT INFO # Provides: zfs-mount # Required-Start: $local_fs zfs-import # Required-Stop: $local_fs zfs-import # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # X-Stop-After: zfs-zed # Short-Description: Mount ZFS filesystems and volumes # Description: Run the `zfs mount -a` or `zfs umount -a` commands. ### END INIT INFO # # Released under the 2-clause BSD license. # # This script is based on debian/zfsutils.zfs.init from the # Debian GNU/kFreeBSD zfsutils 8.1-3 package, written by Aurelien Jarno. # Source the common init script . @sysconfdir@/zfs/zfs-functions # ---------------------------------------------------- chkroot() { while read -r _ mp _; do if [ "$mp" = "/" ]; then return 0 fi done < /proc/self/mounts return 1 } do_depend() { # Try to allow people to mix and match fstab with ZFS in a way that makes sense. if [ "$(mountinfo -s /)" = 'zfs' ] then before localmount else after localmount fi # bootmisc will log to /var which may be a different zfs than root. before bootmisc logger after zfs-import sysfs use mtab keyword -lxc -openvz -prefix -vserver } # Mount all datasets/filesystems do_mount() { local verbose overlay i mntpt check_boolean "$VERBOSE_MOUNT" && verbose=v check_boolean "$DO_OVERLAY_MOUNTS" && overlay=O zfs_action "Mounting ZFS filesystem(s)" \ "$ZFS" mount -a$verbose$overlay "$MOUNT_EXTRA_OPTIONS" # Require each volume/filesystem to have 'noauto' and no fsck # option. This shouldn't really be necessary, as long as one # can get zfs-import to run sufficiently early on in the boot # process - before local mounts. This is just here in case/if # this isn't possible. check_boolean "$VERBOSE_MOUNT" && \ zfs_log_begin_msg "Mounting volumes and filesystems registered in fstab" read_mtab "^/dev/(zd|zvol)" read_fstab "^/dev/(zd|zvol)" - i=0; var=$(eval echo "FSTAB_$i") - while [ -n "$(eval echo "$""$var")" ] + i=0; var="FSTAB_0" + while [ -n "$(eval echo "\$$var")" ] do - mntpt=$(eval echo "$""$var") - dev=$(eval echo "$"FSTAB_dev_$i) + mntpt=$(eval echo "\$$var") + dev=$(eval echo "\$FSTAB_dev_$i") if ! in_mtab "$mntpt" && ! is_mounted "$mntpt" && [ -e "$dev" ] then check_boolean "$VERBOSE_MOUNT" && \ zfs_log_progress_msg "$mntpt " fsck "$dev" && mount "$mntpt" fi i=$((i + 1)) - var=$(eval echo FSTAB_$i) + var=$(eval echo "FSTAB_$i") done read_mtab "[[:space:]]zfs[[:space:]]" read_fstab "[[:space:]]zfs[[:space:]]" - i=0; var=$(eval echo FSTAB_$i) - while [ -n "$(eval echo "$""$var")" ] + i=0; var=$(eval echo "FSTAB_$i") + while [ -n "$(eval echo "\$$var")" ] do - mntpt=$(eval echo "$""$var") + mntpt=$(eval echo "\$$var") if ! in_mtab "$mntpt" && ! is_mounted "$mntpt" then check_boolean "$VERBOSE_MOUNT" && \ zfs_log_progress_msg "$mntpt " mount "$mntpt" fi i=$((i + 1)) - var=$(eval echo FSTAB_$i) + var=$(eval echo "FSTAB_$i") done check_boolean "$VERBOSE_MOUNT" && zfs_log_end_msg 0 return 0 } # Unmount all filesystems do_unmount() { local i var mntpt # This shouldn't really be necessary, as long as one can get # zfs-import to run sufficiently late in the shutdown/reboot process # - after unmounting local filesystems. This is just here in case/if # this isn't possible. zfs_action "Unmounting ZFS filesystems" "$ZFS" unmount -a check_boolean "$VERBOSE_MOUNT" && \ zfs_log_begin_msg "Unmounting volumes and filesystems registered in fstab" read_mtab "^/dev/(zd|zvol)" read_fstab "^/dev/(zd|zvol)" - i=0; var=$(eval echo FSTAB_$i) - while [ -n "$(eval echo "$""$var")" ] + i=0; var="FSTAB_0" + while [ -n "$(eval echo "\$$var")" ] do - mntpt=$(eval echo "$""$var") - dev=$(eval echo "$"FSTAB_dev_$i) + mntpt=$(eval echo "\$$var") + dev=$(eval echo "\$FSTAB_dev_$i") if in_mtab "$mntpt" then check_boolean "$VERBOSE_MOUNT" && \ zfs_log_progress_msg "$mntpt " umount "$mntpt" fi i=$((i + 1)) - var=$(eval echo FSTAB_$i) + var=$(eval echo "FSTAB_$i") done read_mtab "[[:space:]]zfs[[:space:]]" read_fstab "[[:space:]]zfs[[:space:]]" - i=0; var=$(eval echo FSTAB_$i) - while [ -n "$(eval echo "$""$var")" ] + i=0; var="FSTAB_0" + while [ -n "$(eval echo "\$$var")" ] do - mntpt=$(eval echo "$""$var") + mntpt=$(eval echo "\$$var") if in_mtab "$mntpt"; then check_boolean "$VERBOSE_MOUNT" && \ zfs_log_progress_msg "$mntpt " umount "$mntpt" fi i=$((i + 1)) - var=$(eval echo FSTAB_$i) + var=$(eval echo "FSTAB_$i") done check_boolean "$VERBOSE_MOUNT" && zfs_log_end_msg 0 return 0 } do_start() { check_boolean "$ZFS_MOUNT" || exit 0 check_module_loaded "zfs" || exit 0 # Ensure / exists in /proc/self/mounts. # This should be handled by rc.sysinit but lets be paranoid. if ! chkroot then mount -f / fi do_mount } do_stop() { check_boolean "$ZFS_UNMOUNT" || exit 0 check_module_loaded "zfs" || exit 0 do_unmount } # ---------------------------------------------------- if [ ! -e /sbin/openrc-run ] then case "$1" in start) do_start ;; stop) do_stop ;; force-reload|condrestart|reload|restart|status) # no-op ;; *) [ -n "$1" ] && echo "Error: Unknown command $1." echo "Usage: $0 {start|stop}" exit 3 ;; esac exit $? else # Create wrapper functions since Gentoo don't use the case part. depend() { do_depend; } start() { do_start; } stop() { do_stop; } fi diff --git a/etc/init.d/zfs-zed.in b/etc/init.d/zfs-zed.in index e5550e500050..e5256cbc6280 100755 --- a/etc/init.d/zfs-zed.in +++ b/etc/init.d/zfs-zed.in @@ -1,128 +1,129 @@ #!@DEFAULT_INIT_SHELL@ # # zfs-zed # # chkconfig: 2345 29 99 # description: This script will start and stop the ZFS Event Daemon. # probe: true # ### BEGIN INIT INFO # Provides: zfs-zed # Required-Start: zfs-mount # Required-Stop: zfs-mount # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # X-Stop-After: zfs-share # Short-Description: ZFS Event Daemon # Description: zed monitors ZFS events. When a zevent is posted, zed # will run any scripts that have been enabled for the # corresponding zevent class. ### END INIT INFO # # Released under the 2-clause BSD license. # # This script is based on debian/zfsutils.zfs.init from the # Debian GNU/kFreeBSD zfsutils 8.1-3 package, written by Aurelien Jarno. # Source the common init script . @sysconfdir@/zfs/zfs-functions ZED_NAME="zed" ZED_PIDFILE="@runstatedir@/$ZED_NAME.pid" +# shellcheck disable=SC2034 extra_started_commands="reload" # Exit if the package is not installed [ -x "$ZED" ] || exit 0 # ---------------------------------------------------- do_depend() { after zfs-mount localmount } do_start() { check_module_loaded "zfs" || exit 0 ZED_ARGS="$ZED_ARGS -p $ZED_PIDFILE" zfs_action "Starting ZFS Event Daemon" zfs_daemon_start \ "$ZED_PIDFILE" "$ZED" "$ZED_ARGS" return "$?" } do_stop() { local pools check_module_loaded "zfs" || exit 0 zfs_action "Stopping ZFS Event Daemon" zfs_daemon_stop \ "$ZED_PIDFILE" "$ZED" "$ZED_NAME" || return "$?" # Let's see if we have any pools imported pools=$("$ZPOOL" list -H -oname) if [ -z "$pools" ] then # No pools imported, it is/should be safe/possible to # unload modules. zfs_action "Unloading modules" rmmod zfs zunicode \ zavl zcommon znvpair zlua spl return "$?" fi } do_status() { check_module_loaded "zfs" || exit 0 zfs_daemon_status "$ZED_PIDFILE" "$ZED" "$ZED_NAME" return "$?" } do_reload() { check_module_loaded "zfs" || exit 0 zfs_action "Reloading ZFS Event Daemon" zfs_daemon_reload \ "$ZED_PIDFILE" "$ZED_NAME" return "$?" } # ---------------------------------------------------- if [ ! -e /sbin/openrc-run ]; then case "$1" in start) do_start ;; stop) do_stop ;; status) do_status ;; reload|force-reload) do_reload ;; restart) do_stop do_start ;; *) [ -n "$1" ] && echo "Error: Unknown command $1." echo "Usage: $0 {start|stop|status|reload|restart}" exit 1 ;; esac exit $? else # Create wrapper functions since Gentoo don't use the case part. depend() { do_depend; } start() { do_start; } stop() { do_stop; } status() { do_status; } reload() { do_reload; } fi diff --git a/etc/systemd/Makefile.am b/etc/systemd/Makefile.am index 7b47b93fc105..66232a5ff197 100644 --- a/etc/systemd/Makefile.am +++ b/etc/systemd/Makefile.am @@ -1 +1,4 @@ +include $(top_srcdir)/config/Shellcheck.am + SUBDIRS = system system-generators +SHELLCHECKDIRS = system-generators diff --git a/etc/zfs/Makefile.am b/etc/zfs/Makefile.am index b9123c176b93..3dee81c75860 100644 --- a/etc/zfs/Makefile.am +++ b/etc/zfs/Makefile.am @@ -1,15 +1,18 @@ include $(top_srcdir)/config/Substfiles.am +include $(top_srcdir)/config/Shellcheck.am pkgsysconfdir = $(sysconfdir)/zfs dist_pkgsysconf_DATA = \ vdev_id.conf.alias.example \ vdev_id.conf.sas_direct.example \ vdev_id.conf.sas_switch.example \ vdev_id.conf.multipath.example \ vdev_id.conf.scsi.example pkgsysconf_SCRIPTS = \ zfs-functions SUBSTFILES += $(pkgsysconf_SCRIPTS) + +SHELLCHECK_SHELL = dash # local variables diff --git a/etc/zfs/zfs-functions.in b/etc/zfs/zfs-functions.in index a07cce60d97b..2fb065afdb9a 100644 --- a/etc/zfs/zfs-functions.in +++ b/etc/zfs/zfs-functions.in @@ -1,426 +1,431 @@ # This is a script with common functions etc used by zfs-import, zfs-mount, # zfs-share and zfs-zed. # # It is _NOT_ to be called independently # # Released under the 2-clause BSD license. # # This script is based on debian/zfsutils.zfs.init from the # Debian GNU/kFreeBSD zfsutils 8.1-3 package, written by Aurelien Jarno. PATH=/sbin:/bin:/usr/bin:/usr/sbin # Source function library if [ -f /etc/rc.d/init.d/functions ]; then # RedHat and derivatives . /etc/rc.d/init.d/functions elif [ -L /etc/init.d/functions.sh ]; then # Gentoo . /etc/init.d/functions.sh elif [ -f /lib/lsb/init-functions ]; then # LSB, Debian, and derivatives . /lib/lsb/init-functions fi # Of course the functions we need are called differently # on different distributions - it would be way too easy # otherwise!! if type log_failure_msg > /dev/null 2>&1 ; then # LSB functions - fall through zfs_log_begin_msg() { log_begin_msg "$1"; } zfs_log_end_msg() { log_end_msg "$1"; } zfs_log_failure_msg() { log_failure_msg "$1"; } zfs_log_progress_msg() { log_progress_msg "$1"; } elif type success > /dev/null 2>&1 ; then # Fedora/RedHat functions zfs_set_ifs() { # For some reason, the init function library have a problem # with a changed IFS, so this function goes around that. local tIFS="$1" if [ -n "$tIFS" ] then TMP_IFS="$IFS" IFS="$tIFS" fi } - zfs_log_begin_msg() { echo -n "$1 "; } + zfs_log_begin_msg() { printf "%s" "$1 "; } zfs_log_end_msg() { zfs_set_ifs "$OLD_IFS" if [ "$1" -eq 0 ]; then success else failure fi echo zfs_set_ifs "$TMP_IFS" } zfs_log_failure_msg() { zfs_set_ifs "$OLD_IFS" failure echo zfs_set_ifs "$TMP_IFS" } - zfs_log_progress_msg() { echo -n "$""$1"; } + zfs_log_progress_msg() { printf "%s" "$""$1"; } elif type einfo > /dev/null 2>&1 ; then # Gentoo functions zfs_log_begin_msg() { ebegin "$1"; } zfs_log_end_msg() { eend "$1"; } zfs_log_failure_msg() { eend "$1"; } -# zfs_log_progress_msg() { echo -n "$1"; } - zfs_log_progress_msg() { echo -n; } +# zfs_log_progress_msg() { printf "%s" "$1"; } + zfs_log_progress_msg() { :; } else # Unknown - simple substitutes. - zfs_log_begin_msg() { echo -n "$1"; } + zfs_log_begin_msg() { printf "%s" "$1"; } zfs_log_end_msg() { ret=$1 if [ "$ret" -ge 1 ]; then echo " failed!" else echo " success" fi return "$ret" } zfs_log_failure_msg() { echo "$1"; } - zfs_log_progress_msg() { echo -n "$1"; } + zfs_log_progress_msg() { printf "%s" "$1"; } fi # Paths to what we need ZFS="@sbindir@/zfs" ZED="@sbindir@/zed" ZPOOL="@sbindir@/zpool" ZPOOL_CACHE="@sysconfdir@/zfs/zpool.cache" # Sensible defaults ZFS_MOUNT='yes' ZFS_UNMOUNT='yes' ZFS_SHARE='yes' ZFS_UNSHARE='yes' # Source zfs configuration, overriding the defaults if [ -f @initconfdir@/zfs ]; then . @initconfdir@/zfs fi # ---------------------------------------------------- export ZFS ZED ZPOOL ZPOOL_CACHE ZFS_MOUNT ZFS_UNMOUNT ZFS_SHARE ZFS_UNSHARE zfs_action() { local MSG="$1"; shift local CMD="$*" local ret zfs_log_begin_msg "$MSG " $CMD ret=$? if [ "$ret" -eq 0 ]; then zfs_log_end_msg $ret else zfs_log_failure_msg $ret fi return $ret } # Returns # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started # 3 if unsupported # zfs_daemon_start() { local PIDFILE="$1"; shift local DAEMON_BIN="$1"; shift - local DAEMON_ARGS="$*" if type start-stop-daemon > /dev/null 2>&1 ; then # LSB functions start-stop-daemon --start --quiet --pidfile "$PIDFILE" \ --exec "$DAEMON_BIN" --test > /dev/null || return 1 - start-stop-daemon --start --quiet --exec "$DAEMON_BIN" -- \ - $DAEMON_ARGS || return 2 + # shellcheck disable=SC2086 + start-stop-daemon --start --quiet --exec "$DAEMON_BIN" -- \ + "$@" || return 2 # On Debian, there's a 'sendsigs' script that will # kill basically everything quite early and zed is stopped # much later than that. We don't want zed to be among them, # so add the zed pid to list of pids to ignore. if [ -f "$PIDFILE" ] && [ -d /run/sendsigs.omit.d ] then ln -sf "$PIDFILE" /run/sendsigs.omit.d/zed fi elif type daemon > /dev/null 2>&1 ; then - # Fedora/RedHat functions - daemon --pidfile "$PIDFILE" "$DAEMON_BIN" $DAEMON_ARGS + # Fedora/RedHat functions + # shellcheck disable=SC2086 + daemon --pidfile "$PIDFILE" "$DAEMON_BIN" "$@" return $? else # Unsupported return 3 fi return 0 } # Returns # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # 3 if unsupported # zfs_daemon_stop() { local PIDFILE="$1" local DAEMON_BIN="$2" local DAEMON_NAME="$3" if type start-stop-daemon > /dev/null 2>&1 ; then # LSB functions start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 \ --pidfile "$PIDFILE" --name "$DAEMON_NAME" ret="$?" [ "$ret" = 0 ] && rm -f "$PIDFILE" return "$ret" elif type killproc > /dev/null 2>&1 ; then # Fedora/RedHat functions killproc -p "$PIDFILE" "$DAEMON_NAME" ret="$?" [ "$ret" = 0 ] && rm -f "$PIDFILE" return "$ret" else # Unsupported return 3 fi return 0 } # Returns status zfs_daemon_status() { local PIDFILE="$1" local DAEMON_BIN="$2" local DAEMON_NAME="$3" if type status_of_proc > /dev/null 2>&1 ; then # LSB functions status_of_proc "$DAEMON_NAME" "$DAEMON_BIN" return $? elif type status > /dev/null 2>&1 ; then # Fedora/RedHat functions status -p "$PIDFILE" "$DAEMON_NAME" return $? else # Unsupported return 3 fi return 0 } zfs_daemon_reload() { local PIDFILE="$1" local DAEMON_NAME="$2" if type start-stop-daemon > /dev/null 2>&1 ; then # LSB functions start-stop-daemon --stop --signal 1 --quiet \ --pidfile "$PIDFILE" --name "$DAEMON_NAME" return $? elif type killproc > /dev/null 2>&1 ; then # Fedora/RedHat functions - killproc -p "$PIDFILE" "$DAEMON_NAME" -HUP + killproc -p "$PIDFILE" "$DAEMON_NAME" -HUP return $? else # Unsupported return 3 fi return 0 } zfs_installed() { if [ ! -x "$ZPOOL" ]; then return 1 else # Test if it works (will catch missing/broken libs etc) "$ZPOOL" -? > /dev/null 2>&1 return $? fi if [ ! -x "$ZFS" ]; then return 2 else # Test if it works (will catch missing/broken libs etc) "$ZFS" -? > /dev/null 2>&1 return $? fi return 0 } # Trigger udev and wait for it to settle. udev_trigger() { if [ -x /sbin/udevadm ]; then /sbin/udevadm trigger --action=change --subsystem-match=block /sbin/udevadm settle elif [ -x /sbin/udevsettle ]; then /sbin/udevtrigger /sbin/udevsettle fi } # Do a lot of checks to make sure it's 'safe' to continue with the import. checksystem() { if grep -qiE '(^|[^\\](\\\\)* )zfs=(off|no|0)( |$)' /proc/cmdline; then # Called with zfs=(off|no|0) - bail because we don't # want anything import, mounted or shared. # HOWEVER, only do this if we're called at the boot up # (from init), not if we're running interactively (as in # from the shell - we know what we're doing). + # shellcheck disable=SC2154 [ -n "$init" ] && exit 3 fi # Check if ZFS is installed. zfs_installed || return 5 # Just make sure that /dev/zfs is created. udev_trigger return 0 } get_root_pool() { + # shellcheck disable=SC2046 set -- $(mount | grep ' on / ') [ "$5" = "zfs" ] && echo "${1%%/*}" } # Check if a variable is 'yes' (any case) or '1' # Returns TRUE if set. check_boolean() { local var="$1" echo "$var" | grep -Eiq "^yes$|^on$|^true$|^1$" && return 0 || return 1 } check_module_loaded() { module="$1" [ -r "/sys/module/${module}/version" ] && return 0 || return 1 } load_module() { module="$1" # Load the zfs module stack if ! check_module_loaded "$module"; then if ! /sbin/modprobe "$module"; then return 5 fi fi return 0 } # first parameter is a regular expression that filters mtab read_mtab() { local match="$1" - local fs mntpnt fstype opts rest TMPFILE + local fs mntpnt fstype opts rest # Unset all MTAB_* variables + # shellcheck disable=SC2046 unset $(env | grep ^MTAB_ | sed 's,=.*,,') while read -r fs mntpnt fstype opts rest; do if echo "$fs $mntpnt $fstype $opts" | grep -qE "$match"; then # * Fix problems (!?) in the mounts file. It will record # 'rpool 1' as 'rpool\0401' instead of 'rpool\00401' # which seems to be the correct (at least as far as # 'printf' is concerned). # * We need to use the external echo, because the # internal one would interpret the backslash code # (incorrectly), giving us a  instead. - mntpnt=$(/bin/echo "$mntpnt" | sed "s,\\\0,\\\00,g") - fs=$(/bin/echo "$fs" | sed "s,\\\0,\\\00,") + mntpnt=$(/bin/echo "$mntpnt" | sed 's,\\0,\\00,g') + fs=$(/bin/echo "$fs" | sed 's,\\0,\\00,') # Remove 'unwanted' characters. mntpnt=$(printf '%b\n' "$mntpnt" | sed -e 's,/,,g' \ -e 's,-,,g' -e 's,\.,,g' -e 's, ,,g') fs=$(printf '%b\n' "$fs") # Set the variable. eval export "MTAB_$mntpnt=\"$fs\"" fi done < /proc/self/mounts } in_mtab() { local mntpnt="$1" # Remove 'unwanted' characters. mntpnt=$(printf '%b\n' "$mntpnt" | sed -e 's,/,,g' \ -e 's,-,,g' -e 's,\.,,g' -e 's, ,,g') local var var="$(eval echo "MTAB_$mntpnt")" [ "$(eval echo "$""$var")" != "" ] return "$?" } # first parameter is a regular expression that filters fstab read_fstab() { local match="$1" local i var # Unset all FSTAB_* variables + # shellcheck disable=SC2046 unset $(env | grep ^FSTAB_ | sed 's,=.*,,') i=0 while read -r fs mntpnt fstype opts; do echo "$fs" | grep -qE '^#|^$' && continue echo "$mntpnt" | grep -qE '^none|^swap' && continue echo "$fstype" | grep -qE '^swap' && continue if echo "$fs $mntpnt $fstype $opts" | grep -qE "$match"; then eval export "FSTAB_dev_$i=$fs" fs=$(printf '%b\n' "$fs" | sed 's,/,_,g') eval export "FSTAB_$i=$mntpnt" i=$((i + 1)) fi done < /etc/fstab } in_fstab() { local var var="$(eval echo "FSTAB_$1")" [ "${var}" != "" ] return $? } is_mounted() { local mntpt="$1" local mp while read -r _ mp _; do [ "$mp" = "$mntpt" ] && return 0 done < /proc/self/mounts return 1 } diff --git a/scripts/Makefile.am b/scripts/Makefile.am index f59826fe6bc8..f338cdc39195 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -1,81 +1,89 @@ +include $(top_srcdir)/config/Shellcheck.am + pkgdatadir = $(datadir)/@PACKAGE@ dist_pkgdata_SCRIPTS = \ zimport.sh \ zfs.sh \ zfs-tests.sh \ zloop.sh \ zfs-helpers.sh -EXTRA_DIST = \ +EXTRA_SCRIPTS = \ commitcheck.sh \ common.sh.in \ - cstyle.pl \ dkms.mkconf \ dkms.postbuild \ - enum-extract.pl \ kmodtool \ make_gitrev.sh \ man-dates.sh \ - paxcheck.sh \ + paxcheck.sh + +EXTRA_DIST = \ + cstyle.pl \ + enum-extract.pl \ zfs2zol-patch.sed \ - zol2zfs-patch.sed + zol2zfs-patch.sed \ + $(EXTRA_SCRIPTS) + +SHELLCHECK_IGNORE = ,SC1117 +SHELLCHECKSCRIPTS = $(EXTRA_SCRIPTS) define EXTRA_ENVIRONMENT # Only required for in-tree use export INTREE="yes" export GDB="libtool --mode=execute gdb" export LDMOD=/sbin/insmod export CMD_DIR=@abs_top_builddir@/cmd export UDEV_RULE_DIR=@abs_top_builddir@/udev/rules.d export ZEDLET_ETC_DIR=$$CMD_DIR/zed/zed.d export ZEDLET_LIBEXEC_DIR=$$CMD_DIR/zed/zed.d export ZPOOL_SCRIPT_DIR=$$CMD_DIR/zpool/zpool.d export ZPOOL_SCRIPTS_PATH=$$CMD_DIR/zpool/zpool.d export ZPOOL_COMPAT_DIR=$$CMD_DIR/zpool/compatibility.d export CONTRIB_DIR=@abs_top_builddir@/contrib export LIB_DIR=@abs_top_builddir@/lib export SYSCONF_DIR=@abs_top_builddir@/etc export INSTALL_UDEV_DIR=@udevdir@ export INSTALL_UDEV_RULE_DIR=@udevruledir@ export INSTALL_MOUNT_HELPER_DIR=@mounthelperdir@ export INSTALL_SYSCONF_DIR=@sysconfdir@ export INSTALL_PYTHON_DIR=@pythonsitedir@ export KMOD_SPL=@abs_top_builddir@/module/spl/spl.ko export KMOD_ZAVL=@abs_top_builddir@/module/avl/zavl.ko export KMOD_ZNVPAIR=@abs_top_builddir@/module/nvpair/znvpair.ko export KMOD_ZUNICODE=@abs_top_builddir@/module/unicode/zunicode.ko export KMOD_ZCOMMON=@abs_top_builddir@/module/zcommon/zcommon.ko export KMOD_ZLUA=@abs_top_builddir@/module/lua/zlua.ko export KMOD_ICP=@abs_top_builddir@/module/icp/icp.ko export KMOD_ZFS=@abs_top_builddir@/module/zfs/zfs.ko export KMOD_FREEBSD=@abs_top_builddir@/module/openzfs.ko export KMOD_ZZSTD=@abs_top_builddir@/module/zstd/zzstd.ko endef export EXTRA_ENVIRONMENT all-local: -$(SED) -e '\|^export BIN_DIR=|s|$$|@abs_top_builddir@/bin|' \ -e '\|^export SBIN_DIR=|s|$$|@abs_top_builddir@/bin|' \ -e '\|^export LIBEXEC_DIR=|s|$$|@abs_top_builddir@/bin|' \ -e '\|^export ZTS_DIR=|s|$$|@abs_top_srcdir@/tests|' \ -e '\|^export SCRIPT_DIR=|s|$$|@abs_top_srcdir@/scripts|' \ $(abs_top_srcdir)/scripts/common.sh.in >common.sh -echo "$$EXTRA_ENVIRONMENT" >>common.sh clean-local: -$(RM) common.sh install-data-hook: -$(SED) -e '\|^export BIN_DIR=|s|$$|@bindir@|' \ -e '\|^export SBIN_DIR=|s|$$|@sbindir@|' \ -e '\|^export LIBEXEC_DIR=|s|$$|@zfsexecdir@|' \ -e '\|^export ZTS_DIR=|s|$$|@datadir@/@PACKAGE@|' \ -e '\|^export SCRIPT_DIR=|s|$$|@datadir@/@PACKAGE@|' \ $(abs_top_srcdir)/scripts/common.sh.in \ >$(DESTDIR)$(datadir)/@PACKAGE@/common.sh diff --git a/scripts/dkms.mkconf b/scripts/dkms.mkconf index 8649b93183a2..9d12a8c3b3fd 100755 --- a/scripts/dkms.mkconf +++ b/scripts/dkms.mkconf @@ -1,121 +1,123 @@ #!/bin/sh PROG=$0 pkgcfg=/etc/sysconfig/zfs while getopts "n:v:c:f:" opt; do case $opt in - n) pkgname=$OPTARG ;; - v) pkgver=$OPTARG ;; - c) pkgcfg=$OPTARG ;; + n) pkgname=$OPTARG ;; + v) pkgver=$OPTARG ;; + c) pkgcfg=$OPTARG ;; f) filename=$OPTARG ;; + *) err=1 ;; esac done -if [ -z "${pkgname}" ] || [ -z "${pkgver}" ] || [ -z "${filename}" ]; then +if [ -z "${pkgname}" ] || [ -z "${pkgver}" ] || [ -z "${filename}" ] || + [ -n "${err}" ]; then echo "Usage: $PROG -n -v -c -f " exit 1 fi -cat >${filename} <"${filename}" < -k -n " \ - "-t -v " + "-t -v " exit 1 fi -cp "${tree}/${pkgname}/${pkgver}/build/zfs_config.h" \ +exec cp "${tree}/${pkgname}/${pkgver}/build/zfs_config.h" \ "${tree}/${pkgname}/${pkgver}/build/module/Module.symvers" \ "${tree}/${pkgname}/${pkgver}/${kver}/${arch}/" diff --git a/scripts/kmodtool b/scripts/kmodtool index 35d54bad2ba5..26bacf5991d2 100755 --- a/scripts/kmodtool +++ b/scripts/kmodtool @@ -1,623 +1,625 @@ #!/usr/bin/env bash +# shellcheck disable=SC2086 # kmodtool - Helper script for building kernel module RPMs # Copyright (c) 2003-2012 Ville Skyttä , # Thorsten Leemhuis # Nicolas Chauvet # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. shopt -s extglob myprog="kmodtool-${repo}" myver="0.12.1" kmodname= build_kernels="current" kernels_known_variants= kernel_versions= kernel_versions_to_build_for= prefix= filterfile= target= buildroot= +dashvariant= error_out() { local errorlevel=${1} shift - echo "Error: $@" >&2 + echo "Error: $*" >&2 # the next line is not multi-line safe -- not needed *yet* - echo "%global kmodtool_check echo \"kmodtool error: $@\"; exit ${errorlevel};" - exit ${errorlevel} + echo "%global kmodtool_check echo \"kmodtool error: $*\"; exit ${errorlevel};" + exit "${errorlevel}" } print_rpmtemplate_header() { echo echo '%global kmodinstdir_prefix '${prefix}/lib/modules/ echo '%global kmodinstdir_postfix '/extra/${kmodname}/ echo '%global kernel_versions '${kernel_versions} echo } print_akmodtemplate () { echo cat <= %{?epoch:%{epoch}:}%{version} Provides: ${kmodname}-kmod = %{?epoch:%{epoch}:}%{version}-%{release} EOF if [[ ${obsolete_name} ]]; then echo "Provides: akmod-${obsolete_name} = ${obsolete_version}" echo "Obsoletes: akmod-${obsolete_name} < ${obsolete_version}" fi cat < /dev/null & %files -n akmod-${kmodname} %defattr(-,root,root,-) %{_usrsrc}/akmods/* EOF } print_akmodmeta () { cat <= %{?epoch:%{epoch}:}%{version} %if 0%{?rhel} == 6 || 0%{?centos} == 6 Requires(post): module-init-tools Requires(postun): module-init-tools %else Requires(post): kmod Requires(postun): kmod %endif EOF if [[ ${obsolete_name} ]]; then echo "Provides: kmod-${obsolete_name}-${kernel_uname_r} = ${obsolete_version}" echo "Obsoletes: kmod-${obsolete_name}-${kernel_uname_r} < ${obsolete_version}" fi # second part if [[ ! "${customkernel}" ]]; then cat < /dev/null || : elif [[ -f "/lib/modules/${kernel_uname_r}/System.map" ]]; then ${prefix}${depmod_path} -aeF /lib/modules/${kernel_uname_r}/System.map ${kernel_uname_r} > /dev/null || : else ${prefix}${depmod_path} -ae ${kernel_uname_r} &> /dev/null || : fi %postun -n kmod-${kmodname}-${kernel_uname_r} if [[ -f "/boot/System.map-${kernel_uname_r}" ]]; then ${prefix}${depmod_path} -aF /boot/System.map-${kernel_uname_r} ${kernel_uname_r} &> /dev/null || : elif [[ -f "/lib/modules/${kernel_uname_r}/System.map" ]]; then ${prefix}${depmod_path} -aF /lib/modules/${kernel_uname_r}/System.map ${kernel_uname_r} &> /dev/null || : else ${prefix}${depmod_path} -a ${kernel_uname_r} &> /dev/null || : fi EOF else cat < /dev/null || : %postun -n kmod-${kmodname}-${kernel_uname_r} [[ "\$(uname -r)" == "${kernel_uname_r}" ]] && ${prefix}${depmod_path} -a > /dev/null || : EOF fi # third part cat <= %{?epoch:%{epoch}:}%{version}-%{release}" fi if [[ ${obsolete_name} ]]; then echo "Provides: kmod-${obsolete_name}-devel = ${obsolete_version}" echo "Obsoletes: kmod-${obsolete_name}-devel < ${obsolete_version}" fi cat < objects for the newest kernel. %files -n kmod-${kmodname}-devel %defattr(644,root,root,755) %{_usrsrc}/${kmodname}-%{version} EOF if [[ ${obsolete_name} ]]; then echo "%{_usrsrc}/${obsolete_name}-%{version}" fi for kernel in ${1}; do local kernel_uname_r=${kernel} echo "%exclude %{_usrsrc}/${kmodname}-%{version}/${kernel_uname_r}" if [[ ${obsolete_name} ]]; then echo "%exclude %{_usrsrc}/${obsolete_name}-%{version}/${kernel_uname_r}" fi done echo echo } print_rpmtemplate_per_kmoddevelpkg () { if [[ "${1}" == "--custom" ]]; then shift local customkernel=true elif [[ "${1}" == "--redhat" ]]; then # this is needed for akmods shift local redhatkernel=true fi local kernel_uname_r=${1} local kernel_variant="${2:+-${2}}" # first part cat <= %{?epoch:%{epoch}:}%{version}-%{release} %{?KmodsMetaRequires:Requires: %{?KmodsMetaRequires}} EOF if [[ ${obsolete_name} ]]; then echo "Provides: kmod-${obsolete_name}${kernel_variant} = ${obsolete_version}" echo "Obsoletes: kmod-${obsolete_name}${kernel_variant} < ${obsolete_version}" fi cat < -- filter the results with grep --file " echo " --for-kernels -- created templates only for these kernels" echo " --kmodname -- name of the kmod (required)" echo " --devel -- make kmod-devel package" echo " --noakmod -- no akmod package" echo " --repo -- use buildsys-build--kerneldevpkgs" echo " --target -- target-arch (required)" echo " --buildroot -- Build root (place to look for build files)" } while [ "${1}" ] ; do case "${1}" in --filterfile) shift if [[ ! "${1}" ]] ; then error_out 2 "Please provide path to a filter-file together with --filterfile" >&2 elif [[ ! -e "${1}" ]]; then error_out 2 "Filterfile ${1} not found" >&2 fi filterfile="${1}" shift ;; --kmodname) shift if [[ ! "${1}" ]] ; then error_out 2 "Please provide the name of the kmod together with --kmodname" >&2 fi # strip pending -kmod kmodname="${1%%-kmod}" shift ;; --devel) shift devel="true" ;; --prefix) shift if [[ ! "${1}" ]] ; then error_out 2 "Please provide a prefix with --prefix" >&2 fi prefix="${1}" shift ;; --repo) shift if [[ ! "${1}" ]] ; then error_out 2 "Please provide the name of the repo together with --repo" >&2 fi repo=${1} shift ;; --for-kernels) shift if [[ ! "${1}" ]] ; then error_out 2 "Please provide the name of the kmod together with --kmodname" >&2 fi for_kernels="${1}" shift ;; --noakmod) shift noakmod="true" ;; --obsolete-name) shift if [[ ! "${1}" ]] ; then error_out 2 "Please provide the name of the kmod to obsolete together with --obsolete-name" >&2 fi obsolete_name="${1}" shift ;; --obsolete-version) shift if [[ ! "${1}" ]] ; then error_out 2 "Please provide the version of the kmod to obsolete together with --obsolete-version" >&2 fi obsolete_version="${1}" shift ;; --target) shift target="${1}" shift ;; --akmod) shift build_kernels="akmod" ;; --newest) shift build_kernels="newest" ;; --current) shift build_kernels="current" ;; --buildroot) shift buildroot="${1}" shift ;; --help) myprog_help exit 0 ;; --version) echo "${myprog} ${myver}" exit 0 ;; *) echo "Error: Unknown option '${1}'." >&2 usage >&2 exit 2 ;; esac done if [[ -e ./kmodtool-kernel-variants ]]; then kernels_known_variants="$(cat ./kmodtool-kernel-variants)" elif [[ -e /usr/share/kmodtool/kernel-variants ]] ; then kernels_known_variants="$(cat /usr/share/kmodtool/kernel-variants)" else kernels_known_variants="@(smp?(-debug)|PAE?(-debug)|debug|kdump|xen|kirkwood|highbank|imx|omap|tegra)" fi # general sanity checks if [[ ! "${target}" ]]; then error_out 2 "please pass target arch with --target" elif [[ ! "${kmodname}" ]]; then error_out 2 "please pass kmodname with --kmodname" elif [[ ! "${kernels_known_variants}" ]] ; then error_out 2 "could not determine known variants" -elif ( [[ "${obsolete_name}" ]] && [[ ! "${obsolete_version}" ]] ) || ( [[ ! "${obsolete_name}" ]] && [[ "${obsolete_version}" ]] ) ; then +elif { [[ "${obsolete_name}" ]] && [[ ! "${obsolete_version}" ]]; } || { [[ ! "${obsolete_name}" ]] && [[ "${obsolete_version}" ]]; } ; then error_out 2 "you need to provide both --obsolete-name and --obsolete-version" fi # go if [[ "${for_kernels}" ]]; then # this is easy: print_customrpmtemplate "${for_kernels}" elif [[ "${build_kernels}" == "akmod" ]]; then # do only a akmod package print_akmodtemplate print_akmodmeta else # seems we are on out own to decide for which kernels to build # we need more sanity checks in this case if [[ ! "${repo}" ]]; then error_out 2 "please provide repo name with --repo" - elif ! $(which buildsys-build-${repo}-kerneldevpkgs &> /dev/null) ; then + elif ! command -v "buildsys-build-${repo}-kerneldevpkgs" &> /dev/null ; then error_out 2 "buildsys-build-${repo}-kerneldevpkgs not found" fi # call buildsys-build-${repo}-kerneldevpkgs to get the list of kernels cmdoptions="--target ${target}" # filterfile to filter list of kernels? if [[ "${filterfile}" ]] ; then cmdoptions="${cmdoptions} --filterfile ${filterfile}" fi kernel_versions_to_build_for="$(buildsys-build-${repo}-kerneldevpkgs --${build_kernels} ${cmdoptions})" returncode=$? - if (( ${returncode} != 0 )); then + if (( returncode != 0 )); then error_out 2 "buildsys-build-${repo}-kerneldevpkgs failed: $(buildsys-build-${repo}-kerneldevpkgs --${build_kernels} ${cmdoptions})" fi if [[ "${build_kernels}" == "current" ]] && [[ ! "${noakmod}" ]]; then print_akmodtemplate fi print_rpmtemplate fi diff --git a/tests/Makefile.am b/tests/Makefile.am index f13b19613b01..4bdde9c4508a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,3 +1,8 @@ +include $(top_srcdir)/config/Shellcheck.am + SUBDIRS = runfiles test-runner zfs-tests EXTRA_DIST = README.md + +SHELLCHECKSCRIPTS = $$(find -name '*.sh') +.PHONY: $(SHELLCHECKSCRIPTS) diff --git a/tests/zfs-tests/tests/perf/scripts/prefetch_io.sh b/tests/zfs-tests/tests/perf/scripts/prefetch_io.sh index d8181f2e8792..07688ef21b59 100755 --- a/tests/zfs-tests/tests/perf/scripts/prefetch_io.sh +++ b/tests/zfs-tests/tests/perf/scripts/prefetch_io.sh @@ -1,82 +1,82 @@ #!/usr/bin/env bash # shellcheck disable=SC1004 # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright (c) 2016 by Intel, Corp. # # # Linux platform placeholder for collecting prefetch I/O stats # TBD if we can add additional kstats to achieve the desired results # zfs_kstats="/proc/spl/kstat/zfs" function get_prefetch_ios { typeset -l data_misses="$(awk '$1 == "prefetch_data_misses" \ { print $3; exit }' "$zfs_kstats/arcstats")" typeset -l metadata_misses="$(awk '$1 == "prefetch_metadata_misses" \ { print $3; exit }' "$zfs_kstats/arcstats")" typeset -l total_misses=$(( data_misses + metadata_misses )) echo "$total_misses" } function get_prefetched_demand_reads { typeset -l demand_reads="$(awk '$1 == "demand_hit_predictive_prefetch" \ { print $3; exit }' "$zfs_kstats/arcstats")" echo "$demand_reads" } function get_async_upgrade_sync { typeset -l sync_wait="$(awk '$1 == "async_upgrade_sync" \ { print $3; exit }' "$zfs_kstats/arcstats")" echo "$sync_wait" } if [ $# -ne 2 ] then echo "Usage: ${0##*/} poolname interval" >&2 exit 1 fi interval=$2 prefetch_ios=$(get_prefetch_ios) prefetched_demand_reads=$(get_prefetched_demand_reads) async_upgrade_sync=$(get_async_upgrade_sync) while true do new_prefetch_ios=$(get_prefetch_ios) - printf "%u\n%-24s\t%u\n" "$(date +%s)" "prefetch_ios" \ + printf '%u\n%-24s\t%u\n' "$(date +%s)" "prefetch_ios" \ $(( new_prefetch_ios - prefetch_ios )) prefetch_ios=$new_prefetch_ios new_prefetched_demand_reads=$(get_prefetched_demand_reads) - printf "%-24s\t%u\n" "prefetched_demand_reads" \ + printf '%-24s\t%u\n' "prefetched_demand_reads" \ $(( new_prefetched_demand_reads - prefetched_demand_reads )) prefetched_demand_reads=$new_prefetched_demand_reads new_async_upgrade_sync=$(get_async_upgrade_sync) - printf "%-24s\t%u\n" "async_upgrade_sync" \ + printf '%-24s\t%u\n' "async_upgrade_sync" \ $(( new_async_upgrade_sync - async_upgrade_sync )) async_upgrade_sync=$new_async_upgrade_sync sleep "$interval" done