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>
# 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=""
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
fi
if [ -n "$CTDB_MAX_OPEN_FILES" ]; then
- ulimit -n $CTDB_MAX_OPEN_FILES
+ ulimit -n "$CTDB_MAX_OPEN_FILES"
fi
_d=$(dirname "$pidfile")
_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"
_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
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
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
fi
_meminfo=$(get_proc "meminfo")
+ # Intentional word splitting here
+ # shellcheck disable=SC2046
set -- $(echo "$_meminfo" | awk '
$1 == "MemAvailable:" { memavail += $2 }
$1 == "MemFree:" { memfree += $2 }
$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
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)
}
ip="$2"
_maskbits_in="$3"
+ # Intentional word splitting here
+ # shellcheck disable=SC2046
set -- $(ip_maskbits_iface "$ip")
if [ -n "$1" ] ; then
maskbits="$1"
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"
_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.
# 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
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
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
{
_ip="$1"
- [ -n "$(get_config_for_ip $_ip)" ]
+ _conf=$(get_config_for_ip "$_ip")
+ [ -n "$_conf" ]
}
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
# 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
;;
monitor)
- ctdb_check_unix_socket ${CTDB_CLAMD_SOCKET} || exit $?
+ ctdb_check_unix_socket "$CTDB_CLAMD_SOCKET" || exit $?
;;
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
}
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
_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"
_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:
_rc="${2:-1}"
echo "$_msg" >&2
- exit $_rc
+ exit "$_rc"
}
# Log given message or stdin to either syslog or a CTDB log file
_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)
_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
*) _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
{
_addr="${1%/*}" # Remove optional maskbits
+ # Intentional word splitting here
+ # shellcheck disable=SC2046
set -- $(ip_maskbits_iface "$_addr")
if [ -n "$1" ] ; then
_maskbits="$1"
# 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"
#!/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
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
# 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}"
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"
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 () {
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"
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
}
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
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
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"
{
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"
# 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 ()
{
{
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
*-*) 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
)
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
invalid_nodespec
esac
+ # Intentional multi-word expansion
+ # shellcheck disable=SC2086
echo_nth "$pnn" $all_nodes
done <<<"$ctdb_status_output"
)
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
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
}
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,
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
}