ctdb-scripts: Avoid shellcheck warning SC2124 (string=array)
[sfrench/samba-autobuild/.git] / ctdb / tools / onnode
index 4bd824315394bac448306abe3a19af3c4173550b..e7317b75f300cc7bb4a8c61fb37028d3d38a8563 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 ()
 {
@@ -31,15 +31,15 @@ usage ()
 Usage: onnode [OPTION] ... <NODES> <COMMAND> ...
   options:
     -c          Run in current working directory on specified nodes.
+    -f          Specify nodes file, overrides CTDB_NODES_FILE.
+    -i          Keep standard input open - the default is to close it.
+    -n          Allow nodes to be specified by name.
     -o <prefix> Save standard output from each node to file <prefix>.<ip>
     -p          Run command in parallel on specified nodes.
+    -P          Push given files to nodes instead of running commands.
     -q          Do not print node addresses (overrides -v).
-    -n          Allow nodes to be specified by name.
-    -f          Specify nodes file, overrides CTDB_NODES_FILE.
     -v          Print node address even for a single node.
-  <NODES>       "all", "any", "ok" (or "healthy"), "con" (or "connected"),
-                "rm" (or "recmaster"), "lvs" (or "lvsmaster"),
-                "natgw" (or "natgwlist"); or
+  <NODES>       "all", "any", "ok" (or "healthy"), "con" (or "connected") ; or
                 a node number (0 base); or
                 a hostname (if -n is specified); or
                 list (comma separated) of <NODES>; or
@@ -62,8 +62,15 @@ verbose=false
 quiet=false
 prefix=""
 names_ok=false
+push=false
+stdin=false
 
-ctdb_base="${CTDB_BASE:-/etc/ctdb}"
+if [ -z "$CTDB_BASE" ] ; then
+    CTDB_BASE="/usr/local/etc/ctdb"
+fi
+
+. "${CTDB_BASE}/functions"
+loadconfig "ctdb"
 
 parse_options ()
 {
@@ -72,7 +79,7 @@ parse_options ()
     # options ot onnode.
     local temp
     # Not on the previous line - local returns 0!
-    temp=$(POSIXLY_CORRECT=1 getopt -n "$prog" -o "cf:hno:pqv" -l help -- "$@")
+    temp=$(POSIXLY_CORRECT=1 getopt -n "$prog" -o "cf:hno:pqvPi" -l help -- "$@")
 
     [ $? != 0 ] && usage
 
@@ -87,6 +94,8 @@ parse_options ()
            -p) parallel=true ; shift ;;
            -q) quiet=true ; shift ;;
            -v) verbose=true ; shift ;;
+           -P) push=true ; shift ;;
+           -i) stdin=true ; shift ;;
            --) shift ; break ;;
            -h|--help|*) usage ;; # Shouldn't happen, so this is reasonable.
        esac
@@ -95,18 +104,18 @@ parse_options ()
     [ $# -lt 2 ] && usage
 
     nodespec="$1" ; shift
-    command="$@"
+    command="$*"
 }
 
 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
@@ -121,12 +130,10 @@ parse_nodespec ()
        for i in $1 ; do
            case "$i" in
                *-*) seq "${i%-*}" "${i#*-}" 2>/dev/null || invalid_nodespec ;;
-               # Separate lines for readability.
                all|any|ok|healthy|con|connected) echo "$i" ;;
-               rm|recmaster|lvs|lvsmaster|natgw|natgwlist) 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
     )
@@ -139,7 +146,7 @@ get_nodes_with_status ()
     local status="$2"
 
     if [ -z "$ctdb_status_output" ] ; then
-       ctdb_status_output=$(ctdb -Y status 2>&1)
+       ctdb_status_output=$(ctdb -X status 2>&1)
        if [ $? -ne 0 ] ; then
            echo "${prog}: unable to get status of CTDB nodes" >&2
            echo "$ctdb_status_output" >&2
@@ -152,20 +159,22 @@ get_nodes_with_status ()
 
     (
        local i
-       IFS="${IFS}:"
+       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
-           local ip="$1" ; shift
+           shift # ignore IP address but need status bits below
 
            case "$status" in
                healthy)
-                   # If any bit is not 0, don't match this address.
+                   # If any bit is 1, don't match this address.
                    local s
                    for s ; do
-                       [ "$s" = "0" ] || continue 2
+                       [ "$s" != "1" ] || continue 2
                    done
                    ;;
                connected)
@@ -176,67 +185,25 @@ get_nodes_with_status ()
                    invalid_nodespec
            esac
 
+           # Intentional multi-word expansion
+           # shellcheck disable=SC2086
            echo_nth "$pnn" $all_nodes
        done <<<"$ctdb_status_output"
     )
 }
 
-ctdb_props="" # cache
-get_node_with_property ()
-{
-    local all_nodes="$1"
-    local prop="$2"
-
-    local prop_node=""
-    if [ "${ctdb_props##:${prop}:}" = "$ctdb_props" ] ; then
-       # Not in cache.
-       prop_node=$(ctdb "$prop" -Y 2>/dev/null)
-       if [ $? -eq 0 ] ; then
-           if [ "$prop" = "natgwlist" ] ; then
-               prop_node="${prop_node%% *}" # 1st word
-               if [ "$prop_node" = "-1" ] ; then
-                   # This works around natgwlist returning 0 even
-                   # when there's no natgw.
-                   prop_node=""
-               fi
-           else
-               # We only want the first line.
-               local nl="
-"
-               prop_node="${prop_node%%${nl}*}"
-           fi
-       else
-           prop_node=""
-       fi
-
-       if [ -n "$prop_node" ] ; then
-           # Add to cache.
-           ctdb_props="${ctdb_props}${ctdb_props:+ }:${prop}:${prop_node}"
-       fi
-    else
-       # Get from cache.
-       prop_node="${ctdb_props##:${prop}:}"
-       prop_node="${prop_node%% *}"
-    fi
-
-    if [ -n "$prop_node" ] ; then
-       echo_nth "$prop_node" $all_nodes
-    else
-       echo "${prog}: No ${prop} available" >&2
-       exit 1
-    fi
-}
-
 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 line
+    local out line
+    out=$("$0" -pq all ctdb pnn 2>&1)
     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
@@ -252,13 +219,15 @@ get_nodes ()
     if [ -n "$CTDB_NODES_SOCKETS" ] ; then 
        all_nodes="$CTDB_NODES_SOCKETS"
     else
-       local f="${ctdb_base}/nodes"
+       local f="${CTDB_BASE}/nodes"
        if [ -n "$CTDB_NODES_FILE" ] ; then
            f="$CTDB_NODES_FILE"
            if [ ! -e "$f" -a "${f#/}" = "$f" ] ; then
-               # $f is relative, try in $ctdb_base
-               f="${ctdb_base}/${f}"
+               # $f is relative, try in $CTDB_BASE
+               f="${CTDB_BASE}/${f}"
            fi
+       elif [ -n "$CTDB_NODES" ] ; then
+           f="$CTDB_NODES"
        fi
 
        if [ ! -r "$f" ] ; then
@@ -286,21 +255,29 @@ get_nodes ()
            con|connected) 
                get_nodes_with_status "$all_nodes" "connected" || exit 1
                ;;
-           rm|recmaster)
-               get_node_with_property "$all_nodes" "recmaster" || exit 1
-               ;;
-           lvs|lvsmaster)
-               get_node_with_property "$all_nodes" "lvsmaster" || exit 1
-               ;;
-           natgw|natgwlist)
-               get_node_with_property "$all_nodes" "natgwlist" || 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
+}
+
+push()
+{
+    local host="$1"
+    local files="$2"
+
+    local f
+    for f in $files ; do
+        $verbose && echo "Pushing $f"
+        case "$f" in
+           /*) rsync "$f" "[${host}]:${f}" ;;
+           *)  rsync "${PWD}/${f}" "[${host}]:${PWD}/${f}" ;;
        esac
     done
 }
@@ -334,20 +311,27 @@ stderr_filter ()
 
 parse_options "$@"
 
-$current && command="cd $PWD && $command"
-
 ssh_opts=
-if [ -n "$CTDB_NODES_SOCKETS" ] ; then
-    SSH=fakessh
+if $push ; then
+    SSH=push
     EXTRA_SSH_OPTS=""
-else 
-    # Could "2>/dev/null || true" but want to see errors from typos in file.
-    [ -r "${ctdb_base}/onnode.conf" ] && . "${ctdb_base}/onnode.conf"
-    [ -n "$SSH" ] || SSH=ssh
-    if [ "$SSH" = "ssh" ] ; then
-       ssh_opts="-n"
-    else
-       : # rsh? All bets are off!
+else
+    $current && command="cd $PWD && $command"
+
+    if [ -n "$CTDB_NODES_SOCKETS" ] ; then
+       SSH=fakessh
+       EXTRA_SSH_OPTS=""
+    else 
+       # Could "2>/dev/null || true" but want to see errors from typos in file.
+       [ -r "${CTDB_BASE}/onnode.conf" ] && . "${CTDB_BASE}/onnode.conf"
+       [ -n "$SSH" ] || SSH=ssh
+       if [ "$SSH" = "ssh" ] ; then
+           if $parallel || ! $stdin ; then
+               ssh_opts="-n"
+           fi
+       else
+           : # rsh? All bets are off!
+       fi
     fi
 fi
 
@@ -366,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,
@@ -374,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
 }