ctdb-scripts: Avoid shellcheck warnings SC2046, SC2086 (double-quoting)
authorMartin Schwenke <martin@meltin.net>
Wed, 6 Jul 2016 07:31:51 +0000 (17:31 +1000)
committerAmitay Isaacs <amitay@samba.org>
Thu, 21 Jul 2016 00:24:26 +0000 (02:24 +0200)
SC2046: Quote this to prevent word splitting.
SC2086: Double quote to prevent globbing and word splitting.

Add some quoting where it makes sense.  Use shellcheck directives for
false-positives.

Signed-off-by: Martin Schwenke <martin@meltin.net>
Reviewed-by: Amitay Isaacs <amitay@gmail.com>
17 files changed:
ctdb/config/ctdb.init
ctdb/config/ctdbd_wrapper
ctdb/config/debug_locks.sh
ctdb/config/events.d/05.system
ctdb/config/events.d/10.external
ctdb/config/events.d/10.interface
ctdb/config/events.d/11.natgw
ctdb/config/events.d/13.per_ip_routing
ctdb/config/events.d/31.clamd
ctdb/config/events.d/41.httpd
ctdb/config/events.d/50.samba
ctdb/config/events.d/60.nfs
ctdb/config/functions
ctdb/config/statd-callout
ctdb/tools/ctdb_diagnostics
ctdb/tools/ctdb_natgw
ctdb/tools/onnode

index 0e0d379873d876303157b269035a76dbd8e2040c..83c1493c13021a4cd8ce69b96c03818bb7bafbb2 100755 (executable)
@@ -121,7 +121,8 @@ check_status ()
     # status.  Note that this probably won't work if
     # $CTDB_VALGRIND="yes" but this doesn't need full backward
     # compatibility because it is a debug option.
-    if [ -d $(dirname "$pidfile") ] ; then
+    _d=$(dirname "$pidfile")
+    if [ -d "$_d" ] ; then
        _pf_opt="-p $pidfile"
     else
        _pf_opt=""
index 29b015437a11b84c9161ba64057628eb9083765e..4d0dde7e0a9181ddae90b89c6aa5fbaad07f025d 100755 (executable)
@@ -62,13 +62,15 @@ ctdbd_is_running ()
            echo "$_pid"
 
            # Return value of kill is used
-           kill -0 $_pid 2>/dev/null
+           kill -0 "$_pid" 2>/dev/null
        else
            # Missing/empty PID file
            return 1
        fi
     else
        if _pid=$(pgrep -f "${ctdbd}\>") ; then
+               # Use word splitting to squash whitespace
+               # shellcheck disable=SC2086
            echo $_pid | sed -e 's@ @,@g'
            return 0
        else
@@ -211,7 +213,7 @@ start()
     fi
 
     if [ -n "$CTDB_MAX_OPEN_FILES" ]; then
-       ulimit -n $CTDB_MAX_OPEN_FILES
+       ulimit -n "$CTDB_MAX_OPEN_FILES"
     fi
 
     _d=$(dirname "$pidfile")
@@ -244,7 +246,7 @@ start()
     _pid=""
     _timeout="${CTDB_STARTUP_TIMEOUT:-10}"
     _count=0
-    while [ $_count -lt $_timeout ] ; do
+    while [ "$_count" -lt "$_timeout" ] ; do
        # If we don't have the PID then try to read it.
        [ -n "$_pid" ] || read _pid 2>/dev/null <"$pidfile"
 
@@ -286,7 +288,7 @@ stop()
     _timeout=${CTDB_SHUTDOWN_TIMEOUT:-30}
     _count=0
     _terminated=false
-    while [ $_count -lt $_timeout ] ; do
+    while [ "$_count" -lt "$_timeout" ] ; do
        if ! pkill -0 -s "$_session" 2>/dev/null ; then
            _terminated=true
            break
index dee719adcc256cfc801c7f367859b8bd85c93ff9..9726247a6aef95438a88f2d8b8bdecff5f0d836e 100755 (executable)
@@ -37,7 +37,7 @@ loadconfig ctdb
     awk '{ if($2 == "->") { print $6, $7, $8, $9, "W" } else { print $5, $6, $7, $8 } }' |
     while read pid rest ; do
        pname=$(readlink "/proc/${pid}/exe")
-       echo $pid $pname $rest
+       echo "$pid $pname $rest"
     done | sed -e "$sed_cmd" | grep "\.tdb" )
 
     if [ -n "$out" ]; then
@@ -51,6 +51,8 @@ loadconfig ctdb
            pids=$(echo "$out" | grep -v "W$" | grep "$db" | grep -v ctdbd | awk '{print $1}')
            all_pids="$all_pids $pids"
        done
+       # Use word splitting to squash whitespace
+       # shellcheck disable=SC2086
        pids=$(echo $all_pids | tr " " "\n" | sort -u)
 
        # For each process waiting, log stack trace
index 780b6b98a53ae07f69a1dd9c8af5104110a5d671..43b472807eabcc3f54016588a18ecd601d617c77 100755 (executable)
@@ -135,6 +135,8 @@ monitor_memory_usage ()
     fi
 
     _meminfo=$(get_proc "meminfo")
+    # Intentional word splitting here
+    # shellcheck disable=SC2046
     set -- $(echo "$_meminfo" | awk '
 $1 == "MemAvailable:" { memavail += $2 }
 $1 == "MemFree:"      { memfree  += $2 }
index 4b22dc7681b0720139da77eac62d08ef2cf84ed1..7be301602f840b2229c958ab9fd7258a8cbb8201 100644 (file)
@@ -32,7 +32,8 @@ takeover_assigned_ips ()
     $CTDB -X ip |
     awk -F'|' '{print $2}' |
     while read ip ; do
-       if [ -n "$(ip_maskbits_iface $ip)" ] ; then
+       _ip_details=$(ip_maskbits_iface "$ip")
+       if [ -n "$_ip_details" ] ; then
            echo "Assigning $ip to this node ($pnn)"
            $CTDB moveip "$ip" "$pnn"
        fi
index c6938d12ed9579c6cfed0ec8f00ceccbab9511e7..51abc4453ab2cae894a2aed91124d20542f76f2a 100755 (executable)
@@ -39,6 +39,8 @@ get_all_interfaces ()
     ctdb_ifaces=$($CTDB -X ifaces | sed -e '1d' -e 's@^|@@' -e 's@|.*@@')
 
     # Add $ctdb_interfaces and uniquify
+    # Use word splitting to squash whitespace
+    # shellcheck disable=SC2086
     all_interfaces=$(echo $all_interfaces $ctdb_ifaces | tr ' ' '\n' | sort -u)
 }
 
@@ -84,6 +86,8 @@ get_iface_ip_maskbits ()
     ip="$2"
     _maskbits_in="$3"
 
+    # Intentional word splitting here
+    # shellcheck disable=SC2046
     set -- $(ip_maskbits_iface "$ip")
     if [ -n "$1" ] ; then
        maskbits="$1"
index bf99ad5a05f0bfff97f8624bde642834027378af..61c93a3fa0946cab1fb9cb83dcf6bc98089e39ff 100755 (executable)
@@ -157,6 +157,8 @@ natgw_set_slave ()
 
 natgw_ensure_master ()
 {
+    # Intentional word splitting here
+    # shellcheck disable=SC2046
     set -- $("${CTDB_HELPER_BINDIR}/ctdb_natgw" master)
     natgwmaster="${1:--1}" # Default is -1, for failure above
     natgwip="$2"
index b14c95e5b992827776ff57a51b3ae8f43b44e2f1..cb08856db8f4deb18f39bc584d24425cf57bdd6c 100755 (executable)
@@ -44,6 +44,8 @@ ipv4_is_valid_addr()
 
     _count=0
     # Get the shell to break up the address into 1 word per octet 
+    # Intentional word splitting here
+    # shellcheck disable=SC2086
     for _o in $(export IFS="." ; echo $_ip) ; do
        # The 2>/dev/null stops output from failures where an "octet"
        # is not numeric.  The test will still fail.
@@ -76,6 +78,8 @@ ipv4_host_addr_to_net ()
     # Convert the host address to an unsigned long by splitting out
     # the octets and doing the math.
     _host_ul=0
+    # Intentional word splitting here
+    # shellcheck disable=SC2086
     for _o in $(export IFS="." ; echo $_host) ; do
        _host_ul=$(( (_host_ul << 8) + _o)) # work around Emacs color bug
     done
@@ -130,7 +134,7 @@ ensure_table_id_for_ip ()
        flock --timeout 30 0 || \
            die "ensure_table_id_for_ip: failed to lock file $rt_tables"
 
-       _new=$CTDB_PER_IP_ROUTING_TABLE_ID_LOW
+       _new="$CTDB_PER_IP_ROUTING_TABLE_ID_LOW"
        while read _t _l ; do
            # Skip comments
            case "$_t" in
@@ -142,15 +146,15 @@ ensure_table_id_for_ip ()
            fi
            # Potentially update the new table id to be used.  The
            # redirect stops error spam for a non-numeric value.
-           if [ $_new -le $_t -a \
-               $_t -le $CTDB_PER_IP_ROUTING_TABLE_ID_HIGH ] 2>/dev/null ; then
+           if [ "$_new" -le "$_t" -a \
+               "$_t" -le "$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH" ] 2>/dev/null ; then
                _new=$((_t + 1))
            fi
        done
 
        # If the new table id is legal then add it to the file and
        # print it.
-       if [ $_new -le $CTDB_PER_IP_ROUTING_TABLE_ID_HIGH ] ; then
+       if [ "$_new" -le "$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH" ] ; then
            printf "%d\t%s\n" "$_new" "$_label" >>"$rt_tables"
            return 0
        else
@@ -215,7 +219,8 @@ ip_has_configuration ()
 {
     _ip="$1"
 
-    [ -n "$(get_config_for_ip $_ip)" ]
+    _conf=$(get_config_for_ip "$_ip")
+    [ -n "$_conf" ]
 }
 
 add_routing_for_ip ()
@@ -241,6 +246,8 @@ add_routing_for_ip ()
     get_config_for_ip "$_ip" |
     while read _i _dest _gw ; do
        _r="$_dest ${_gw:+via} $_gw dev $_iface table $_table_id"
+       # Intentionally unquoted multi-word value here
+       # shellcheck disable=SC2086
        ip route add $_r || \
            die "add_routing_for_ip: failed to add route: $_r"
     done
@@ -306,9 +313,9 @@ add_missing_routes ()
        # not.
        while IFS="|" read _x _ip _x _iface _x ; do
            [ -n "$_iface" ] || continue
-           
+
            _table_id="${table_id_prefix}${_ip}"
-           if [ -z "$(ip route show table $_table_id 2>/dev/null)" -o \
+           if [ -z "$(ip route show table "$_table_id" 2>/dev/null)" -o \
                "$1" = "force" ]  ; then
                add_routing_for_ip "$_iface" "$_ip"
            fi
index c7b542e214823d14a798d4297c6fc438cdf1b3ff..2d301ebfbdb184654cd62d6c9b8aee2c2db66fdc 100755 (executable)
@@ -45,7 +45,7 @@ shutdown)
         ;;
 
 monitor)
-        ctdb_check_unix_socket ${CTDB_CLAMD_SOCKET} || exit $?
+        ctdb_check_unix_socket "$CTDB_CLAMD_SOCKET" || exit $?
         ;;
 esac
 
index 9e0f6f65857510d048b6dd88e12434f727f4fd34..4d04b39418a1ccae6603ea3ef9f4f416a1d647f1 100755 (executable)
@@ -28,7 +28,7 @@ esac
 cleanup_httpd_semaphore_leak() {
     killall -q -0 "$service_name" ||
     for i in $(ipcs -s | awk '$3 == "apache" { print $2 }') ; do
-       ipcrm -s $i
+       ipcrm -s "$i"
     done
 }
 
index b2a5fa460139dc4002bd0f1688cf604a3415cea2..245566ef27ab342ca938e5759073e261229ad52e 100755 (executable)
@@ -172,6 +172,8 @@ monitor)
            smb_ports=$(list_samba_ports)
            [ -n "$smb_ports" ] || die "Failed to set smb ports"
        fi
+       # Intentionally unquoted multi-word value here
+       # shellcheck disable=SC2086
        ctdb_check_tcp_ports $smb_ports || exit $?
 
        if [ "$CTDB_SAMBA_SKIP_SHARE_CHECK" != "yes" ] ; then
index 3bb6e65b88d4dfc91057b6c0d0c6e94e1c0a6fb7..ff31303243267f7caeafba4a367ec67d14905f3c 100755 (executable)
@@ -148,14 +148,14 @@ nfs_check_service ()
        _failcount=$(ctdb_counter_get "$_service_name")
 
        _unhealthy=false
-       if [ $unhealthy_after -gt 0 ] ; then
-           if [ $_failcount -ge $unhealthy_after ] ; then
+       if [ "$unhealthy_after" -gt 0 ] ; then
+           if [ "$_failcount" -ge "$unhealthy_after" ] ; then
                _unhealthy=true
                echo "ERROR: $_err"
            fi
        fi
 
-       if [ $restart_every -gt 0 ] ; then
+       if [ "$restart_every" -gt 0 ] ; then
            if [ $((_failcount % restart_every)) -eq 0 ] ; then
                if ! $_unhealthy ; then
                    echo "WARNING: $_err"
@@ -208,6 +208,8 @@ ctdb_check_rpc ()
            _localhost="${CTDB_RPCINFO_LOCALHOST:-127.0.0.1}"
     esac
 
+    # $_version is not quoted because it is optional
+    # shellcheck disable=SC2086
     if ! ctdb_check_rpc_out=$(rpcinfo -T "$_family" "$_localhost" \
                                      "$_progname" $_version 2>&1) ; then
        ctdb_check_rpc_out="$_progname failed RPC check:
index 1f0ac8d6926e99a1333dd1b92f7fd47daf6f8e5f..bbf29e73d234a312f3e8135325747c303ec7f1cb 100755 (executable)
@@ -123,7 +123,7 @@ die ()
     _rc="${2:-1}"
 
     echo "$_msg" >&2
-    exit $_rc
+    exit "$_rc"
 }
 
 # Log given message or stdin to either syslog or a CTDB log file
@@ -305,7 +305,7 @@ program_stack_traces ()
 
     _count=1
     for _pid in $(pidof "$_prog") ; do
-       [ $_count -le $_max ] || break
+       [ "$_count" -le "$_max" ] || break
 
        # Do this first to avoid racing with process exit
        _stack=$(get_proc "${_pid}/stack" 2>/dev/null)
@@ -496,7 +496,7 @@ kill_tcp_connections ()
 
        _remaining=$(get_tcp_connections_for_ip "$_ip" | wc -l)
 
-       if [ $_remaining -eq 0 ] ; then
+       if [ "$_remaining" -eq 0 ] ; then
                echo "Killed $_killcount TCP connections to released IP $_ip"
                return
        fi
@@ -563,6 +563,8 @@ add_ip_to_iface ()
        *)   _bcast="brd +" ;;
     esac
 
+    # Intentionally unquoted multi-word value here
+    # shellcheck disable=SC2086
     ip addr add "$_ip/$_maskbits" $_bcast dev "$_iface" || {
        echo "Failed to add $_ip/$_maskbits on dev $_iface"
        return 1
@@ -632,6 +634,8 @@ drop_ip ()
 {
     _addr="${1%/*}"  # Remove optional maskbits
 
+    # Intentional word splitting here
+    # shellcheck disable=SC2046
     set -- $(ip_maskbits_iface "$_addr")
     if [ -n "$1" ] ; then
        _maskbits="$1"
@@ -897,6 +901,8 @@ ctdb_replay_monitor_status ()
     # Output looks like this:
     # |monitor|60.nfs|1|ERROR|1314764004.030861|1314764004.035514|foo bar|
     # This is the cheapest way of getting fields in the middle.
+    # Intentional word splitting here
+    # shellcheck disable=SC2046,2086
     set -- $(IFS="|" ; echo $_out)
     _code="$3"
     _status="$4"
index 04e80f4dd4f4e601c362b564de3c53d4c19e2b60..a300255968dd29da0edd6a0c5761802107e478ea 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # This must run as root as CTDB tool commands need to access CTDB socket
-[ $(id -u) -eq 0 ] || exec sudo "$0" "$@"
+[ "$(id -u)" -eq 0 ] || exec sudo "$0" "$@"
 
 # this script needs to be installed so that statd points to it with the -H 
 # command line argument. The easiest way to do that is to put something like this in 
@@ -90,6 +90,8 @@ case "$1" in
            awk -v pnn="$pnn" 'pnn == $2 { \
                 ip = $1; gsub(/\./, "\\.", ip); \
                 printf "/statd-state@%s@/p\n", ip }')
+       # Intentional multi-word expansion for multiple files
+       # shellcheck disable=SC2086
         if cat $files | sed -n "$sed_expr" | $CTDB ptrans "ctdb.tdb" ; then
             rm $files
        fi
index a8c8c4281d601e5ec18ee08181a3886f7db5fd63..6b33c7ce537b60bc6d8332637b57d3846e8f89d3 100755 (executable)
@@ -57,7 +57,7 @@ esac
 # them to $bad_nodes.
 _nodes=""
 for _i in $nodes ; do
-    if onnode $_i true >/dev/null 2>&1 ; then
+    if onnode "$_i" true >/dev/null 2>&1 ; then
        _nodes="${_nodes}${_nodes:+ }${_i}"
     else
        bad_nodes="${bad_nodes}${bad_nodes:+,}${_i}"
@@ -65,7 +65,7 @@ for _i in $nodes ; do
 done
 nodes="$_nodes"
 
-nodes_comma=$(echo $nodes | sed -e 's@[[:space:]]@,@g')
+nodes_comma=$(echo "$nodes" | sed -e 's@[[:space:]]@,@g')
 
 PATH="$PATH:/sbin:/usr/sbin:/usr/lpp/mmfs/bin"
 
@@ -97,21 +97,22 @@ error() {
     msg="$1"
     echo "ERROR: $msg"
     NUM_ERRORS=`expr $NUM_ERRORS + 1`
-    echo " ERROR[$NUM_ERRORS]: $msg" >> $ERRORS
+    echo " ERROR[$NUM_ERRORS]: $msg" >> "$ERRORS"
 }
 
 show_file() {
     fname="$1"
+    _fdetails=$(ls -l "$fname" 2>&1)
     echo "  ================================"
     echo "  File: $fname"
-    echo "  `ls -l $fname 2>&1`"
+    echo "  $_fdetails"
     cat "$fname" 2>&1 | sed 's/^/  /'
     echo "  ================================"
 }
 
 show_all() {
     echo "running $1 on nodes $nodes_comma"
-    onnode $nodes_comma "hostname; date; $1 2>&1 | sed 's/^/  /'" 2>&1
+    onnode "$nodes_comma" "hostname; date; $1 2>&1 | sed 's/^/  /'" 2>&1
 }
 
 show_and_compare_files () {
@@ -119,19 +120,20 @@ show_and_compare_files () {
     fmt="$1" ; shift
 
     for f ; do
+       _bf=$(basename "$f")
        first=true
 
        for n in $nodes ; do
 
            if $first ; then
-               onnode $n [ -r "$f" ] || {
-                   msg=$(printf "$fmt" "$f" $n)
+               onnode "$n" [ -r "$f" ] || {
+                   msg=$(printf "$fmt" "$f" "$n")
                    error "$msg"
                    continue 2;
                }
 
-               fstf=$tmpdir/`basename $f`.node$n
-               onnode $n cat $f > $fstf 2>&1
+               fstf="${tmpdir}/${_bf}.node${n}"
+               onnode "$n" cat "$f" >"$fstf" 2>&1
 
                echo "  ================================"
                echo "  File (on node $n): $f"
@@ -141,17 +143,19 @@ show_and_compare_files () {
                first=false
            else
                echo "Testing for same config file $f on node $n"
-               tmpf=$tmpdir/`basename $f`.node$n
-               onnode $n cat $f > $tmpf 2>&1
-               diff $diff_opts $fstf $tmpf >/dev/null 2>&1 || {
+               tmpf="${tmpdir}/${_bf}.node${n}"
+               onnode "$n" cat "$f" >"$tmpf" 2>&1
+               # Intentional multi-word splitting on diff_opts
+               # shellcheck disable=SC2086
+               diff $diff_opts "$fstf" "$tmpf" >/dev/null 2>&1 || {
                    error "File $f is different on node $n"
-                   diff -u $diff_opts $fstf $tmpf
+                   diff -u $diff_opts "$fstf" "$tmpf"
                }
-               rm -f $tmpf
+               rm -f "$tmpf"
            fi
        done
 
-       rm -f $fstf
+       rm -f "$fstf"
     done
 }
 
@@ -188,10 +192,14 @@ cat <<EOF
 Comping critical config files on nodes $nodes_comma
 EOF
 
+# Intentional multi-word splitting on CONFIG_FILES_MUST
+# shellcheck disable=SC2086
 show_and_compare_files \
     "%s is missing on node %d" \
     $CONFIG_FILES_MUST
 
+# Intentional multi-word splitting on CONFIG_FILES_MAY
+# shellcheck disable=SC2086
 show_and_compare_files \
     "Optional file %s is not present on node %d" \
     $CONFIG_FILES_MAY
@@ -202,9 +210,9 @@ Checking for clock drift
 EOF
 t=`date +%s`
 for i in $nodes; do
-    t2=`onnode $i date +%s`
+    t2=`onnode "$i" date +%s`
     d=`expr $t2 - $t`
-    if [ $d -gt 30 -o $d -lt -30 ]; then
+    if [ "$d" -gt 30 -o "$d" -lt -30 ]; then
        error "time on node $i differs by $d seconds"
     fi
 done
@@ -324,9 +332,9 @@ show_all "smbd -b"
 date
 echo "Diagnostics finished with $NUM_ERRORS errors"
 
-[ -r $ERRORS ] && {
-    cat $ERRORS
-    rm -f $ERRORS
+[ -r "$ERRORS" ] && {
+    cat "$ERRORS"
+    rm -f "$ERRORS"
 }
 
 rm -rf "$tmpdir"
index 28c304d8484b103d880cf214d2ce2c92569c3c44..54e416dfda72704f9699633673ef5fae9786cb77 100755 (executable)
@@ -137,6 +137,8 @@ nodes_list ()
 {
     get_natgw_nodes || \
        die "${prog}: NAT gateway nodes file \"$CTDB_NATGW_NODES\" not found"
+    # Intentional word splitting here
+    # shellcheck disable=SC2046
     set -- $(find_master)  || \
        die "${prog}: Unable to determine NAT gateway master node"
     _master_ip="$2"
index 626261669a4c8c8b0b44cacddc2b0385e0d92047..eb91a365576cdd872cb34d68a1ed082a7f227b77 100755 (executable)
@@ -23,7 +23,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
 
-prog=$(basename $0)
+prog=$(basename "$0")
 
 usage ()
 {
@@ -111,11 +111,11 @@ echo_nth ()
 {
     local n="$1" ; shift
 
-    shift $n
+    shift "$n"
     local node="$1"
 
     if [ -n "$node" -a "$node" != "#DEAD" ] ; then
-       echo $node
+       echo "$node"
     else
        echo "${prog}: \"node ${n}\" does not exist" >&2
        exit 1
@@ -132,8 +132,8 @@ parse_nodespec ()
                *-*) seq "${i%-*}" "${i#*-}" 2>/dev/null || invalid_nodespec ;;
                all|any|ok|healthy|con|connected) echo "$i" ;;
                *)
-                   [ $i -gt -1 ] 2>/dev/null || $names_ok || invalid_nodespec
-                   echo $i
+                   [ "$i" -gt -1 ] 2>/dev/null || $names_ok || invalid_nodespec
+                   echo "$i"
            esac
        done
     )
@@ -162,6 +162,8 @@ get_nodes_with_status ()
        IFS="${IFS}|"
        while IFS="" read i ; do
 
+           # Intentional word splitting
+           # shellcheck disable=SC2086
            set -- $i # split line on colons
            shift     # line starts with : so 1st field is empty
            local pnn="$1" ; shift
@@ -183,6 +185,8 @@ get_nodes_with_status ()
                    invalid_nodespec
            esac
 
+           # Intentional multi-word expansion
+           # shellcheck disable=SC2086
            echo_nth "$pnn" $all_nodes
        done <<<"$ctdb_status_output"
     )
@@ -193,11 +197,13 @@ get_any_available_node ()
     local all_nodes="$1"
 
     # We do a recursive onnode to find which nodes are up and running.
-    local out=$($0 -pq all ctdb pnn 2>&1)
+    local out=$("$0" -pq all ctdb pnn 2>&1)
     local line
     while read line ; do 
        local pnn="${line#PNN:}"
        if [ "$pnn" != "$line" ] ; then
+           # Intentional multi-word expansion
+           # shellcheck disable=SC2086
            echo_nth "$pnn" $all_nodes
            return 0
        fi
@@ -250,11 +256,13 @@ get_nodes ()
                get_nodes_with_status "$all_nodes" "connected" || exit 1
                ;;
            [0-9]|[0-9][0-9]|[0-9][0-9][0-9])
-               echo_nth $n $all_nodes
+               # Intentional multi-word expansion
+               # shellcheck disable=SC2086
+               echo_nth "$n" $all_nodes
                ;;
            *)
                $names_ok || invalid_nodespec
-               echo $n
+               echo "$n"
        esac
     done
 }
@@ -342,6 +350,8 @@ else
 fi
 
 pids=""
+# Intentional multi-word expansion
+# shellcheck disable=SC2086
 trap 'kill -TERM $pids 2>/dev/null' INT TERM
 # There's a small race here where the kill can fail if no processes
 # have been added to $pids and the script is interrupted.  However,
@@ -350,21 +360,21 @@ retcode=0
 for n in $nodes ; do
     set -o pipefail 2>/dev/null
     if $parallel ; then
-       { exec 3>&1 ; { $SSH $ssh_opts $EXTRA_SSH_OPTS $n "$command" | stdout_filter >&3 ; } 2>&1 | stderr_filter ; } &
+       { exec 3>&1 ; { $SSH $ssh_opts $EXTRA_SSH_OPTS "$n" "$command" | stdout_filter >&3 ; } 2>&1 | stderr_filter ; } &
        pids="${pids} $!"
     else
        if $verbose ; then
            echo >&2 ; echo ">> NODE: $n <<" >&2
        fi
 
-       { exec 3>&1 ; { $SSH $ssh_opts $EXTRA_SSH_OPTS $n "$command" | stdout_filter >&3 ; } 2>&1 | stderr_filter ; }
+       { exec 3>&1 ; { $SSH $ssh_opts $EXTRA_SSH_OPTS "$n" "$command" | stdout_filter >&3 ; } 2>&1 | stderr_filter ; }
        [ $? = 0 ] || retcode=$?
     fi
 done
 
 $parallel && {
     for p in $pids; do
-       wait $p
+       wait "$p"
        [ $? = 0 ] || retcode=$?
     done
 }