#!/bin/tcsh -f
# JLdL 08Dec13.
#
# Copyright (C) 2005-2013 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 arbitrary dpkg operations on all the nodes of a cluster,
# _ using a remote shell; it passes all but the help, configuration file and
# _ node selection options unchanged to the dpkg program running on the
# _ nodes; it will try to access only nodes that seem to be up and
# _ running according to the program cruptime.
#
# NOTE: the dpkg option -L is captured, so if it is ever needed
# _ one should use the long version --listfiles.
#
# 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 variable for the list of dpkg options and arguments.
set dpoparg = ""
#
# Initialize the variable for the selection of nodes with its default value.
set nselect = local
#
# Initialize variables for pausing at the end.
set pseflag = 0
set pseargm = 0
set psetime = 0
#
# Process the command-line arguments.
foreach cla ( $* )
    #
    # Detect options.
    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
	else if ( $pseargm == 1 ) then
	    #
	    # In this case we just assume that the argument is
	    # _ missing and attribute the default value of 0.
	    set psetime = 0
	    #
	    # Lower the pause argument flag.
	    set pseargm = 0
	endif
	#
	# Now process the options.
	switch ( $cla )
	case "-h":
	case "--help":
	    #
	    # Print a usage message.
	    echo "usage: $name [-C <config>] [-L|-F] [-E|-P [n]] <dpkg-options-and-arguments>"
	    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 "       -E: exit immediately after executing the task"
	    echo "       -P: pause for n seconds after execution and then exit"
	    echo "               If n=0 then wait for [Enter] after execution"
	    echo "       run dpkg operations on the nodes of a cluster,"
	    echo "       using rsh or ssh to access only the running nodes;"
	    echo "       you can use any dpkg options and arguments, run"
	    echo "       the commands 'dpkg -h' or 'man dpkg' to look up"
	    echo "       all 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 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 "-E":
	case "--Exit":
	    #
	    # Lower the pause flag.
	    set pseflag = 0
	    #
	    # Zero the pause time.
	    set psetime = 0
	    breaksw
	case "-P":
	case "--Pause":
	    #
	    # Raise the pause flag.
	    set pseflag = 1
	    #
	    # Raise the pause argument flag.
	    set pseargm = 1
	    breaksw
	default:
	    #
	    # Accumulate apt-get options.
	    set dpoparg = ( $dpoparg $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 flag.
	    set conflag = 0
	else if ( $pseargm == 1 ) then
	    #
	    # Check whether the argument is a number.
	    echo $cla | grep -q '^[0-9]*$'
	    #
	    # If it is, then set the pause time; otherwise, set the
	    # _ time to the default value and pass on the argument.
	    if ( $status == 0 ) then
		set psetime = $cla
	    else
		set psetime = 0
		set hlfsdirs = ( $hlfsdirs $cla )
	    endif
	    #
	    # Lower the pause argument flag.
	    set pseargm = 0
	else
	    #
	    # Accumulate apt-get arguments.
	    set dpoparg = ( $dpoparg $cla )
	endif
    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
else if ( $pseargm == 1 ) then
    #
    # In this case we just assume that the argument is
    # _ missing and attribute the default value of 0.
    set psetime = 0
    #
    # Lower the argument flag.
    set pseargm = 0
endif
#
# Source the configuration file; this must define the following variables:
# _ nick_name; cluster_root.
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
if ( ! $?cluster_root ) then
    echo "${name}: ERROR: cluster_root not defined in configuration file"
    exit 1
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
#
# Define a separator line.
set sep = "--------------------------------------------------------------------------------"
#
# 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`
#
# 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 list of node hostnames.
    set hnodes = ""
    #
    # 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 selected node hostnames.
	    set hnodes = ( $hnodes $nick_name$node )
	endif
    end
endif
#
# Detect an empty set of nodes.
if ( "$hnodes" == "" ) then
    #
    # Print out a message.
    echo $sep
    echo "${name}: no nodes were selected"
else
    #
    # Loop over the nodes.
    foreach hnode ( $hnodes )
	#
	# Print out a progress report separator.
	echo $sep
	echo current node is: $hnode
	#
	# Do a remote shell to the node and execute dpkg, passing
	# _ all the necessary command-line arguments.
	$rem_shell $hnode dpkg $dpoparg
    end
endif
#
# Print a final separator.
echo $sep
#
# If the pause flag is up, pause before exiting.
if ( $pseflag ) then
    #
    # If there is no pause time, wait for ever;
    # _ else wait for the given time.
    if ( "$psetime" == 0 ) then
	echo -n "Hit [Enter] to exit: "
	set iwait = $<
    else
	sleep $psetime
    endif
endif
