#!/bin/tcsh -f
# JLdL 21Jan11.
#
# Copyright (C) 2006-2011 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 edits the text quota file of a given filesystem and
# _ updates the corresponding binary quota file using the program
# _ update-quotas; in the case of the home filesystem(s) it also
# _ updates the corresponding backup-home filesystem(s).
#
# Record the name this script was called with.
set name = `basename $0`
#
# Initialize variables for the configuration file.
set conflag = 0
#
# Set the default configuration file.
set confile = "/etc/quotas.conf"
#
# Set a flag for the single argument needed.
set argflag = 0
#
# Initialize a variable for passing options to the programs called
# _ from within this one; include the key "-t" so that those
# _ programs will use the temporary text quota file.
set passopt = "-t"
#
# 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>] [-u|-g] [-q] </path/to/fs/root>"
	    echo "       -C: use alternate configuration file <config>"
	    echo "       -u: manipulate user quotas"
	    echo "       -g: manipulate group quotas"
	    echo "       -q: run quietly, no progress reporting"
	    exit 0
	    breaksw
	case "-C":
	case "--Config-file":
	    #
	    # Raise the flag.
	    set conflag = 1
	    breaksw
	case "-u":
	case "--usrquota":
	    #
	    # Set the type of quota.
	    set qtype = u
	    #
	    # Set the file extension.
	    set fext = user
	    breaksw
	case "-g":
	case "--grpquota":
	    #
	    # Set the type of quota.
	    set qtype = g
	    #
	    # Set the file extension.
	    set fext = group
	    breaksw
	case "-q":
	case "--quiet":
	    #
	    # Pass the option.
	    set passopt = ( $passopt $cla )
	    breaksw
	default:
	    #
	    # Print an error message.
	    echo "${name}: ERROR: unknown option $cla"
	    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 filesystem from the command line argument.
	    if ( $argflag ) then
		echo "${name}: ERROR: too many arguments"
		exit 1
	    else
		set fs = $cla
		#
		# Lower the flag.
		set argflag = 1
	    endif
	endif
    endif
end
#
# If we got here with conflag up, there is an error.
if ( $conflag == 1 ) then
    echo "${name}: ERROR: option -C requires an argument"
    exit 1
endif
#
# Check for the single argument needed.
if ( ! $argflag ) then
    echo "${name}: ERROR: needs 1 argument: the filesystem"
    exit 1
endif
#
# Source the configuration file; this must define the following variables:
# _ quotas_path; default_type; cluster_dir; default_editor;
# _ backup_flag; home_path.
if ( -r $confile ) then
    source $confile
else
    echo "${name}: ERROR: cannot read configuration file $confile"
    exit 1
endif
#
# Set the location of the text quota files.
set qpath = "$quotas_path"
#
# If no quota-type options were entered, set the default type
# _ of quota, as defined in the configuration file.
if ( ! $?qtype ) then
    #
    # Set the type of quota.
    set qtype = `echo "$default_type" | cut -c 1`
    #
    # Set the file extension.
    set fext = "$default_type"
endif    
#
# Set the cluster-wide quota file directory.
set cldir = "$cluster_dir"
#
# Set the default editor and its options.
set dedit = "$default_editor"
#
# Set the automatic home backup flag.
set bflag = "$backup_flag"
#
# Set the path(s) to the home filesystem(s).
set hpaths = "$home_path"
#
# Strip away any "/"'s at the extremes of the filesystem path.
set fs = `echo $fs | sed -e 's|/\+$||g' -e 's|^/\+||g'`
#
# Define the basic name of the text quota file.
set qname = `echo $fs | tr "/" "_"`
#
# Construct the quota filename.
set fname = $qname.$fext
set pname = $qpath/$fname
#
# Check for the existence of the quota file.
if ( ! -f $pname ) then
    echo "${name}: ERROR: no file $pname"
    exit 1
endif
#
# Define a lock file.
set lockfile = /var/lock/$name-$fname
#
# Check for a lock file.
if ( -f $lockfile ) then
    echo "${name}: this script is already running"
    exit 0
endif
#
# Make a lock file.
touch $lockfile
#
# Alias defining a macro for exiting cleanly.
alias cleanexit "\rm -f $lockfile >& /dev/null ; exit \!*"
#
# What to do on interrupts: do a clean exit.
onintr normalexit
#
# In order to protect the text quota configuration files, make an
# _ additional lock, to prevent the two editors from interfering
# _ with one another; in case there is a common text quota file
# _ directory for a cluster of servers, put the lock file there,
# _ so that the lock will be valid for all the servers.
if ( "$cldir" == "" ) then
    set editlock = /var/lock/quota-files-being-edited.lock
else
    set editlock = $cluster_dir/quota-files-being-edited.lock
endif
#
# Check for the second lock file.
if ( -f $editlock ) then
    echo "${name}: quotas are already being edited; lock file found:"
    echo "$editlock"
    cleanexit 0
endif
#
# Make the second lock file; on error, exit cleanly.
touch $editlock
if ( $status != 0 ) then
    cleanexit 1
endif
#
# Redefine the macro for exiting cleanly.
alias cleanexit "\rm -f $lockfile $editlock >& /dev/null ; exit \!*"
#
# Copy the current text quota file as a temporar NEW file; note:
# _ do _not_ use mv, in order not to change the soft links.
cp -pf $pname $pname.NEW
#
# Edit the new file; but first we must define and find the text editor
# _ to be used: try the configuration file, then the environment.
#
# Get the name of the editing command from the configuration file.
set ecomm = `echo "$dedit" | cut -d' ' -f1`
#
# Verify that it is available for execution.
which "$ecomm" >& /dev/null
#
# If it is available, use it.
if ( $status == 0 ) then
    $dedit $pname.NEW
else
    #
    # Now try the environment.
    if ( $?EDITOR ) then
	#
	# Get the name of the editing command from the environment.
	set ecomm = `echo "$EDITOR" | cut -d' ' -f1`
	#
	# Verify that it is available for execution.
	which "$ecomm" >& /dev/null
	#
	# If it is available, use it.
	if ( $status == 0 ) then
	    $EDITOR $pname.NEW
	else
	    echo "${name}: ERROR: environment editor not usable: $EDITOR"
	    exit 1
	endif
    else
	echo "${name}: ERROR: configured editor not usable: $dedit"
	echo "${name}:        EDITOR environment variable not set"
	exit 1
    endif
endif
#
# Silently remove the emacs backup file, if any.
rm -f $pname.NEW~ >& /dev/null
#
# Get the new version of the modified lines and write a temporary text
# _ quota file with only those lines, so that the update-quotas program
# _ will only update the entries in the binary quota file for the lines
# _ that have been modified.
diff -b $pname $pname.NEW | grep '^> ' | cut -d " " -f 2- >! $pname.TMP
#
# Use update-quotas to update the binary quota file; pass the
# _ key "-t" so it will use the temporary text quota file.
update-quotas $passopt -$qtype /$fs
#
# Verify whether automatic home backup is in effect; in the case of
# _ the home filesystem(s), use update-bckp-quotas to update the text
# _ and binary quota files of the backup filesystem(s); pass the
# _ key "-t" so it will use the temporary text quota file.
if ( $bflag ) then
    #
    # Set a flag on the low state.
    set ishome = 0
    #
    # Loop over the home filesystem(s).
    foreach hpath ( $hpaths )
	#
	# Strip away any "/"'s at the extremes of the path.
	set hpath = `echo "$hpath" | sed -e 's|/\+$||g' -e 's|^/\+||g'`
	#
	# If the filesystem is a home filesystem, then raise the flag.
	if ( $fs == $hpath ) then
	    set ishome = 1
	endif
	#
    end
    #
    # If the flag was raised, update the backup-home quotas.
    if ( $ishome ) then
	update-bckp-quotas $passopt -$qtype
    endif
    #
endif
#
# Remove the temporary text quota file.
rm -f $pname.TMP
#
# Recover the quota information in the NEW file into the current
# _ text quota file; note: also do _not_ use mv here, because
# _ this will mess up symlinks which may be in use.
cp -pf $pname.NEW $pname
#
# Remove the new text quota file.
rm -f $pname.NEW
#
# Normal exit: remove the lock file.
normalexit:
cleanexit 0
