#!/bin/tcsh -f
# JLdL 26Jun07.
#
# Copyright (C) 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 automatically edits and distributes to all the nodes
# _ some node-specific configuration files, taking as its basis the
# _ current configuration files of the virtual node; a particularly
# _ frequent example of this is the /etc/fstab file; the program is
# _ useful for updating all the files at once, based on actual
# _ manual editing work done solely in the virtual node.
#
# Store 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 variables for the file to edit and distribute.
set edtfile = "etc/fstab"
#
# 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
	endif
	#
	# Now process the options.
	switch ( $cla )
	case "-h":
	case "--help":
	    #
	    # Print a usage message.
	    echo "usage: $name [-C <config>] [<file>]"
	    echo "       -C: use alternate configuration file <config>"
	    echo "       edit and distribute new <file> files to all the nodes,"
	    echo "       based on the corresponding file of the virtual node;"
	    echo "       the default is the file etc/fstab; the argument <file>"
	    echo "       must be an acceptable alternative; enter 'help' or the"
	    echo "       name of some non-existent file in order to see the"
	    echo "       list of acceptable alternatives; to get all the"
	    echo "       details run the command 'man $name'"
	    exit 0
	    breaksw
	case "-C":
	case "--Config-file":
	    #
	    # Raise the flag.
	    set conflag = 1
	    breaksw
	default:
	    #
	    # Print an error message.
	    echo "${name}: ERROR: unknown option $cla; try -h to get help"
	    exit 1
	    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
	    #
	    # Get the file to edit and distribute.
	    set edtfile = $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
endif
#
# Source the configuration file; this must define the following variables:
# _ nick_name; virt_node; cluster_root, node_address; lan_domain.
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 ( ! $?virt_node ) then
    echo "${name}: ERROR: virt_node 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
if ( ! $?node_address ) then
    echo "${name}: ERROR: node_address not defined in configuration file"
    exit 1
endif
if ( ! $?lan_domain ) then
    echo "${name}: ERROR: lan_domain not defined in configuration file"
    exit 1
endif
#
# Give the default value to the optional configuration variable.
if ( ! $?address_offset ) then
    set address_offset = 0
endif
#
# Check that the file to be edited and distributed is acceptable.
if ( "$edtfile" != "etc/hostname" && \
     "$edtfile" != "etc/fstab" && \
     "$edtfile" != "etc/network/interfaces" && \
     "$edtfile" != "etc/hosts" && \
     "$edtfile" != "etc/exim/exim.conf" ) then
    echo "${name}: ERROR: unacceptable file $edtfile"
    echo "    the acceptable alternatives are:"
    echo "        etc/hostname"
    echo "        etc/fstab"
    echo "        etc/network/interfaces"
    echo "        etc/hosts"
    echo "        etc/exim/exim.conf (Exim version 3 only)"
    exit 1
endif
#
# Get the number of digits in the node numbers.
set ndig = `echo -n $virt_node | wc -c`
#
# Build the regular expression for the node numbers.
set node_digs = "[0-9]"
set idig = 1
while ( $idig < $ndig )
    set node_digs = "${node_digs}[0-9]"
    @ idig = $idig + 1
end
#
# Define the source directory.
set source = $virt_node
#
# Go to the cluster root.
cd $cluster_root
#
# Define the list of target directories, which are all
# _ the node directories except the virtual one.
set targets = `\ls -d $node_digs | grep -v $source`
#
# Do some simple error detection: check that there are some node
# _ directories within the current working directory.
if ( "$targets" == "" ) then
    echo "${name}: ERROR: cannot find node directories in $cwd"
    exit 1
endif
#
# Issue a warning and provide a safety exit.
echo "WARNING: this program is about to overwrite the /$edtfile"
echo "configuration files within all the nodes of this cluster."
echo -n "Are you sure you want to do this? [no]: "
set imsure = $<
#
# Require an explicit lowcase yes in order to continue.
if ( "$imsure" != yes ) then
    echo "${name}: quitting: you did not enter 'yes'"
    exit 0
endif
#
# Do the automatic editing and distributing of the files, in
# _ each supported case; the editing algorithms are the same
# _ as those found in the tool make-new-netboot-node.
#
# Choose the file to work on.
if ( "$edtfile" == "etc/hostname" ) then
    #
    # Loop over the non-virtual nodes.
    foreach target ( $targets )
	#
	# Remove the old file.
	rm -f $target/$edtfile
	#
	# Create the new file.
	cat $source/$edtfile | \
	    sed -e "s|$source|$target|g" \
		> $target/$edtfile
    end
else if ( "$edtfile" == "etc/fstab" ) then
    #
    # Define a variable with the tab character.
    set tab = "`echo -n '\t'`"
    #
    # Loop over the non-virtual nodes.
    foreach target ( $targets )
	#
	# Remove the old file.
	rm -f $target/$edtfile
	#
	# Create the new file.
	cat $source/$edtfile | \
	    sed -e "s|/$source\([ $tab]\)|/$target\1|g" \
		> $target/$edtfile
    end
else if ( "$edtfile" == "etc/network/interfaces" ) then
    #
    # Define a variable with the tab character.
    set tab = "`echo -n '\t'`"
    #
    # Calculate the address of the source node.
    #
    # For the virtual node there is no offset.
    set sadd = $node_address
    #
    # Check whether the source-node file is correct.
    grep -q "address[ $tab]*$sadd" $source/$edtfile
    if ( $status != 0 ) then
	echo "${name}: WARNING: the address in $source/$edtfile"
	echo "    does not match the value of node_address in the"
	echo "    configuration file; will NOT edit this file."
    else
	#
	# Extract the numbers from the base address.
	set bip0 = `echo -n $node_address | cut -d. -f4`
	set bip1 = `echo -n $node_address | cut -d. -f3`
	set bip2 = `echo -n $node_address | cut -d. -f2`
	set bip3 = `echo -n $node_address | cut -d. -f1`
	#
	# Loop over the non-virtual nodes.
	foreach target ( $targets )
	    #
	    # Calculate the address of the target node.
	    #
	    # For non-virtual nodes we must add the offset.
	    @ node = $target + $address_offset
	    #
	    # This is an addition in base 256.
	    @ rat0 = ( $node + $bip0 ) / 256
	    @ tip0 = ( $node + $bip0 ) - ( 256 * $rat0 )
	    @ rat1 = ( $rat0 + $bip1 ) / 256
	    @ tip1 = ( $rat0 + $bip1 ) - ( 256 * $rat1 )
	    @ rat2 = ( $rat1 + $bip2 ) / 256
	    @ tip2 = ( $rat1 + $bip2 ) - ( 256 * $rat2 )
	    @ rat3 = ( $rat2 + $bip3 ) / 256
	    @ tip3 = ( $rat2 + $bip3 ) - ( 256 * $rat3 )
	    set tadd = $tip3.$tip2.$tip1.$tip0
	    #
	    # Remove the old file.
	    rm -f $target/$edtfile
	    #
	    # Create the new file.
	    cat $source/$edtfile | \
		sed -e "s|address[ $tab]*$sadd|address $tadd|g" \
		    > $target/$edtfile
	end
    endif
else if ( "$edtfile" == "etc/hosts" ) then
    #
    # Define a variable with the tab character.
    set tab = "`echo -n '\t'`"
    #
    # Calculate the address of the source node.
    #
    # For the virtual node there is no offset.
    set sadd = $node_address
    #
    # Define the fully qualified domain name of the source node.
    set shost = $nick_name$source.$lan_domain
    #
    # Check whether the source-node file is correct.
    grep -q "^[ $tab]*${sadd}[ $tab]*$shost" $source/$edtfile
    if ( $status != 0 ) then
	echo "${name}: WARNING: the address in $source/$edtfile"
	echo "    does not match the value of node_address in the"
	echo "    configuration file; will NOT edit this file."
    else
	#
	# Extract the numbers from the base address.
	set bip0 = `echo -n $node_address | cut -d. -f4`
	set bip1 = `echo -n $node_address | cut -d. -f3`
	set bip2 = `echo -n $node_address | cut -d. -f2`
	set bip3 = `echo -n $node_address | cut -d. -f1`
	#
	# Loop over the non-virtual nodes.
	foreach target ( $targets )
	    #
	    # Calculate the address of the target node.
	    #
	    # For non-virtual nodes we must add the offset.
	    @ node = $target + $address_offset
	    #
	    # This is an addition in base 256.
	    @ rat0 = ( $node + $bip0 ) / 256
	    @ tip0 = ( $node + $bip0 ) - ( 256 * $rat0 )
	    @ rat1 = ( $rat0 + $bip1 ) / 256
	    @ tip1 = ( $rat0 + $bip1 ) - ( 256 * $rat1 )
	    @ rat2 = ( $rat1 + $bip2 ) / 256
	    @ tip2 = ( $rat1 + $bip2 ) - ( 256 * $rat2 )
	    @ rat3 = ( $rat2 + $bip3 ) / 256
	    @ tip3 = ( $rat2 + $bip3 ) - ( 256 * $rat3 )
	    set tadd = $tip3.$tip2.$tip1.$tip0
	    #
	    # Define the fully qualified domain name of the target node.
	    set thost = $nick_name$target.$lan_domain
	    #
	    # Check that the line does not already exist in the target file,
	    # _ in order to avoid creating duplicated lines in it; this is
	    # _ to detect the case in which the hosts file is the same in
	    # _ all nodes and already contains lines for all the nodes.
	    grep -q "^[ $tab]*${tadd}[ $tab]*$thost" $target/$edtfile
	    if ( $status != 0 ) then
		#
		# Remove the old file.
		rm -f $target/$edtfile
		#
		# Create the new file.
		cat $source/$edtfile | \
		    sed -e "s|$sadd|$tadd|g" \
			-e "s|$nick_name$source|$nick_name$target|g" \
			> $target/$edtfile
	    endif
	end
    endif
else if ( "$edtfile" == "etc/exim/exim.conf" ) then
    #
    # Define the fully qualified domain name of the source node.
    set shost = $nick_name$source.$lan_domain
    #
    # Loop over the non-virtual nodes.
    foreach target ( $targets )
	#
	# Define the fully qualified domain name of the target node.
	set thost = $nick_name$target.$lan_domain
	#
	# This is needed only for Exim version 3, not for Exim4.
	if ( -f $source/$edtfile ) then
	    #
	    # Remove the old file.
	    rm -f $target/$edtfile
	    #
	    # Create the new file.
	    cat $source/$edtfile | \
		sed -e "s|$shost|$thost|g" \
		    > $target/$edtfile
	endif
    end
else
    #
    # A safety check for internal error.
    echo "${name}: warning: not ready to do $edtfile"
endif
