#!/bin/tcsh -f
# JLdL 26Jun07.
#
# Copyright (C) 2005-2007 by Jorge L. deLyra <delyra@fma.if.usp.br>.
# This program may be copied and/or distributed freely. See the
# _ terms and conditions in /usr/share/doc/<package>/copyright.
#
# This program runs commands on the nodes of a cluster, using dsh; it passes
# _ all but the help, configuration file and node selection options to dsh
# _ in two groups, one with the options for dsh itself, and the other with
# _ the command to run and its options and arguments; it will try to access
# _ only nodes that seem to be up and running according to cruptime.
#
# Record the name this script was called with.
set name = `basename $0`
#
# Initialize variables for the configuration file.
set conflag = 0
set confile = "/etc/cluster.conf"
#
# Initialize a flag for the preliminary options and arguments.
set preflag = 1
#
# Initialize a variable for the list of preliminary options and arguments.
set preopts = ""
#
# Initialize a variable for the command and its list of options and arguments.
set comargs = ""
#
# Initialize the variable for the selection of nodes with its default value.
set nselect = full
#
# Process the command-line arguments; note that a foreach ( $* ) will not work
# _ here, because there might be shell metacharacters among the arguments.
#
# Start a counter for command-line arguments.
set iarg = 0
#
# Loop over the command-line arguments.
while ( $iarg < $#argv )
    #
    # Increment the counter.
    @ iarg = $iarg + 1
    #
    # Pass the value of the argument.
    set cla = "$argv[$iarg]"
    #
    # Check the preliminary options flag and act accordingly.
    if ( $preflag ) then
	#
	# Detect options.
	# IMPORTANT: the variable $cla inside the reversed quotes must be
	# _ protected as shown because it may cointain a ';', which would
	# _ cause the erroneous execution, right here, of the part of the
	# _ argument that comes after it; this could cause the accidental
	# _ reboot of the server, for example.
	if ( "`echo -n '$cla' | cut -c 1`" == "-" ) then
	    #
	    # If we got here with the argument flag up, there is an error.
	    if ( $conflag == 1 ) then
		echo "${name}: ERROR: option -C requires an argument"
		exit 1
	    endif
	    #
	    # Now process the options.
	    switch ( "$cla" )
	    case "-h":
	    case "--help":
		#
		# Print a usage message.
		echo "usage: $name [-C <config>] [-L|-F] [<dsh-opts>] -- <comm> [<opts-and-args>]"
		echo "       -C: use alternate configuration file <config>"
		echo "       -L: select only the local nodes to act on"
		echo "       -F: select the full set of nodes to act on"
		echo "       run commands on the nodes of a cluster, using"
		echo "       dsh to access only the running nodes; you can"
		echo "       use any options and arguments of dsh, run the"
		echo "       commands 'dsh -h' or 'man dsh' to look up all"
		echo "       the possible options and arguments; in order"
		echo "       to get the details run 'man $name'"
		exit 0
		breaksw
	    case "-C":
	    case "--Config-file":
		#
		# Raise the configuration file flag.
		set conflag = 1
		breaksw
	    case "-L":
	    case "--Local-nodes":
		#
		# Set the node-selection variable to local nodes only.
		set nselect = local
		breaksw
	    case "-F":
	    case "--Full-cluster":
		#
		# Set the node-selection variable to the full cluster.
		set nselect = full
		breaksw
	    case "--":
		#
		# Lower the preliminary options flag.
		set preflag = 0
		breaksw
	    default:
		#
		# Accumulate the options for dsh.
		set preopts = ( $preopts $cla )
		breaksw
	    endsw
	#
	# Process non-option arguments.
	else
	    #
	    # Get the arguments of options.
	    if ( $conflag == 1 ) then
		#
		# Set the configuration file.
		set confile = $cla
		#
		# Lower the configuration file flag.
		set conflag = 0
	    else
		#
		# Accumulate the arguments for dsh.
		set preopts = ( $preopts $cla )
	    endif
	endif
    else
	#
	# Accumulate the command, its options and its arguments.
	set comargs = ( $comargs $cla )
    endif
end
#
# If we got here with the argument flag up, there is an error.
if ( $conflag == 1 ) then
    echo "${name}: ERROR: option -C requires an argument"
    exit 1
endif
#
# Source the configuration file; this must define the following variables:
# _ nick_name.
if ( -r $confile ) then
    source $confile
else
    echo "${name}: ERROR: cannot read configuration file $confile"
    exit 1
endif
#
# Do some simple error detection: check that the necessary
# _ variables are defined in the configuration file.
if ( ! $?nick_name ) then
    echo "${name}: ERROR: nick_name not defined in configuration file"
    exit 1
endif
#
# The cluster_root variable is only needed if nselect is set to "local".
if ( ! $?cluster_root ) then
    if ( $nselect == local ) then
	echo "${name}: ERROR: cluster_root not defined in configuration file"
	echo "Warning: options -L or --Local-nodes can only be used"
	echo "         in a cluster server, and not in the nodes"
	exit 1
    endif
endif
#
# Give the default value to the optional configuration variable.
if ( ! $?rem_shell ) then
    set rem_shell = rsh
endif
#
# Define the location of the programs.
set bindir = /usr/bin
#
# Select the set of nodes to act on.
if ( $nselect == full ) then
    #
    # Full set: get the list of the hostnames of the nodes which are up.
    set hnodes = `$bindir/cruptime -C $confile | grep " up " | cut -d" " -f1`
    #
    # Initialize the set of -m options for dsh.
    set margs = ""
    #
    # Loop over the nodes and build a set of -m options for dsh.
    foreach hnode ( $hnodes )
	set margs = ( -m $hnode $margs )
    end
#
# Local set: set the list of the hostnames of the nodes which are up and
# _ which have a directory within the cluster root in this machine.
else if ( $nselect == local ) then
    #
    # Get the number of digits in the nickname, plus one.
    @ ndig = `echo -n $nick_name | wc -c` + 1
    #
    # First initialize to the full set: get the list of nodes which are up.
    set nodes = `$bindir/cruptime -C $confile | grep " up " | cut -d" " -f1 | cut -c $ndig-`
    #
    # Initialize the set of -m options for dsh.
    set margs = ""
    #
    # Loop over the nodes which are up and running.
    foreach node ( $nodes )
	#
	# For each node, check if the corresponding root directory exists.
	if ( -d $cluster_root/$node ) then
	    #
	    # Build the set of -m options for dsh, using the hostnames.
	    set margs = ( -m $nick_name$node $margs )
	endif
    end
endif
#
# Use dsh to access the nodes, passing all the
# _ necessary command-line arguments.
dsh -r $rem_shell $preopts $margs -- "$comargs"
