#!/bin/tcsh -f
# JLdL 21Jan11.
#
# Copyright (C) 2006-2001 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 files for a given user or group
# _ and updates the corresponding binary files 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`
#
# Define a variable with the tab character.
set tab = "`echo -n '\t'`"
#
# Define the temporary files for the consolidated
# _ quota information for the user or group.
set oldtf = "/tmp/$name.tmp-dat.OLD"
set newtf = "/tmp/$name.tmp-dat.NEW"
#
# 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 conflag 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] <user|group>"
	    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 user or group from the command line argument.
	    if ( $argflag ) then
		echo "${name}: ERROR: too many arguments"
		exit 1
	    else
		set uname = $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 user or group"
    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; backup_root.
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"
#
# Set the the path the root of the backup tree; strip away
# _ any "/"'s at the extremes of the filesystem.
set bpath = `echo "$backup_root" | sed -e 's|/\+$||g' -e 's|^/\+||g'`
#
# Set the name of the backup root.
set bname = `echo $bpath | tr "/" "_"`
#
# Get the list of text quota files for this host; filter out the
# _ home-backup filesystem(s), since usually their quotas should
# _ be set automatically, in correspondence to the quotas of the
# _ original home filesystem(s); however, if the automatic home
# _ backup strategy is _not_ in effect, then include them back
# _ in the list, if they are present.
set qfiles = `\ls $qpath/*.$fext | grep -v "^$qpath/${bname}_"`
if ( ! $bflag ) then
    #
    # 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'`
	#
	# Set the name of the home filesystem.
	set hname = `echo $hpath | tr "/" "_"`
	#
	# Check whether the file exists and if so re-include it.
	if ( -f $qpath/${bname}_$hname.$fext ) then
	    set qfiles = ( $qfiles $qpath/${bname}_$hname.$fext )
	endif
	#
    end
    #
endif
#
# Define a lock file.
set lockfile = /var/lock/$name
#
# 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 \!*"
#
# Start a temporary file with the consolidated quota
# _ information for this particular user or group.
echo "Disk quotas for $fext ${uname} (block quotas in kBytes):" > $oldtf
echo "Filesystem\tblock,soft\tblock,hard\tinode,soft\tinode,hard" >> $oldtf
#
# Loop over the quota files and collect the information.
foreach qfile ( $qfiles )
    #
    # Check that the user or group does exist in the quota file.
    grep -q "^${uname}[ $tab]" $qfile
    if ( $status != 0 ) then
    	echo -n "${name}: ERROR looking for ${fext}name"
	echo " $uname in file $qfile"
	\rm -f $oldtf
	cleanexit 1
    endif
    #
    # Identify the filesystem from the name of the quota file.
    set qname = `basename $qfile`
    set fs = `echo $qname | cut -d: -f2 | cut -d. -f1 | tr '_' '/'`
    #
    # Extract the line for the user or group.
    grep "^${uname}[ $tab]" $qfile | \
	sed -e "s|^${uname}\([ $tab]\)|$fs\1|g" >> $oldtf
end
#
# Copy the current information into a new file.
cp -pf $oldtf $newtf
#
# 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 $newtf
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 $newtf
	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 $newtf~
#
# Loop over the quota files; for each one, check for changes.
foreach qfile ( $qfiles )
    #
    # Identify the filesystem from the name of the quota file.
    set qname = `basename $qfile`
    set fs = `echo $qname | cut -d: -f2 | cut -d. -f1 | tr '_' '/'`
    #
    # Get the old line, if it was changed;
    # _ this will be the search target.
    set trg = "`diff $oldtf $newtf | grep '^< ${fs}[ $tab]'`"
    #
    # Fix the format of the line.
    set trg = "`echo '$trg' | sed -e 's|^< ${fs}\([ $tab]\)|$uname\1|g'`"
    #
    # Get the new line, if there was a change;
    # _ this will be the substitution source.
    set src = "`diff $oldtf $newtf | grep '^> ${fs}[ $tab]'`"
    #
    # Fix the format of the line.
    set src = "`echo '$src' | sed -e 's|^> ${fs}\([ $tab]\)|$uname\1|g'`"
    #
    # If the lines are not empty, then let us fix the
    # _ corresponding text quota file.
    if ( "$trg" != "" && "$src" != "" ) then
	#
	# Edit the current file and create a new file.
	cat $qfile | sed -e "s|^$trg|$src|g" >! $qfile.NEW
	#
	# Recover the quota information in the NEW file into the current
	# _ text quota file; note: do _not_ use mv here, in order not
	# _ to change the soft links.
	cp -pf $qfile.NEW $qfile
	#
	# Remove the new text quota file.
	rm -f $qfile.NEW
	#
	# Burn a temporary text file with only the new line, so that
	# _ the update-quotas program will only update the entries in
	# _ the binary quota file for that user or group.
	echo "$src" >! $qfile.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 $qfile.TMP
    endif
end
#
# Remove the consolidated-data temporary files.
rm -f $oldtf $newtf
#
# Normal exit: remove the lock file.
normalexit:
cleanexit 0
