#!/bin/tcsh -f
# JLdL 25Sep12.
#
# Copyright (C) 2006-2012 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 updates the binary and text quota files of the backup-home
# _ filesystem; it doubles the data for the soft limits found in the text
# _ file of the home filesystem and increases the hard limits 20% over
# _ that; it does all this very surgically, only for the users that
# _ had some quota data changed, and only for the pieces of data
# _ that have been changed.
#
# Record the name this script was called with.
set name = `basename $0`
#
# Define a variable with the tab character.
set tab = "`echo -n '\t'`"
#
# Initialize variables for the configuration file.
set conflag = 0
#
# Set the default configuration file.
set confile = "/etc/quotas.conf"
#
# Set a flag for the use of the temporary text file.
set tmpflag = 0
#
# Set a flag for the progress-report output, default is up.
set proflag = 1
#
# 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] [-t] [-q]"
	    echo "       -C: use alternate configuration file <config>"
	    echo "       -u: manipulate user quotas"
	    echo "       -g: manipulate group quotas"
	    echo "       -t: use the temporary input text quota file"
	    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 "-t":
	case "--temporary-file":
	    #
	    # Set the temporary flag.
	    set tmpflag = 1
	    breaksw
	case "-q":
	case "--quiet":
	    #
	    # Unset the progress-report flag.
	    set proflag = 0
	    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
	#
	# This script takes no arguments.
	else
	    #
	    # Print an error message.
	    echo "${name}: ERROR: takes no arguments"
	    exit 1
	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
#
# Source the configuration file; this must define the following variables:
# _ quotas_path; default_type; 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 automatic home backup flag.
set bflag = "$backup_flag"
#
# This program should operate only if the automatic
# _ home backup strategy is in effect.
if ( ! $bflag ) then
    echo "${name}: automatic home backup is not active"
    exit 0
endif
#
# Set the path the root of the backup tree; strip away
# _ any "/"'s at the extremes of the path.
set bpath = `echo "$backup_root" | sed -e 's|/\+$||g' -e 's|^/\+||g'`
#
# 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
#
# Loop over the path(s) to the home filesystem(s).
foreach hpath ( $home_path )
    #
    # 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 fs = $hpath
    #
    # Define the basic name of the home text quota file.
    set qname = `echo $fs | tr "/" "_"`
    #
    # Construct the corresponding quota filename.
    if ( $tmpflag ) then
	set fname = $qname.$fext.TMP
    else
	set fname = $qname.$fext
    endif
    set pname = $qpath/$fname
    #
    # Check for the existence of the home quota file.
    if ( ! -f $pname ) then
	echo "${name}: ERROR: no file $pname"
	cleanexit 1
    endif
    #
    # Set the name of the backup-home filesystem.
    set bfs = $bpath/$hpath
    #
    # Get the device containing the backup-home filesystem from
    # _ the fstab file; one must exclude comment lines.
    set fsdev = `grep "[ $tab]\+/${bfs}[ $tab]\+" /etc/fstab \
		| grep -v "^[ $tab]*#" | tr "$tab" ' ' \
		| sed -e 's|^ *||g' | cut -d' ' -f1`
    #
    # This is needed in case the fstab file has been migrated to
    # _ the new UUID-based device identification format.
    #
    # Check for a UUID entry in place of the device node.
    if ( "`echo $fsdev| grep UUID=`" != "" ) then
	#
	# Extract the UUID number, without the keyword
	# _ "UUID=" and the punctuation marks "-".
	set uidev = `echo $fsdev | sed -e 's|\-||g' | cut -d= -f2`
	#
	# Get the device node from the output of blkid.
	set fsdev = `blkid | sed -e 's|\-||g' | grep "$uidev" | cut -d: -f1`
	#
    endif
    #
    # Define the basic name of the backup-home text quota file.
    set bqname = `echo $bfs | tr "/" "_"`
    #
    # Construct the corresponding quota filename.
    set bfname = $bqname.$fext
    set bpname = $qpath/$bfname
    #
    # Check for the existence of the backup-home quota file.
    if ( ! -f $bpname ) then
	echo "${name}: ERROR: no file $bpname"
	cleanexit 1
    endif
    #
    # Put the whole home text quota file, except headers,
    # _ on a single record.
    set frec = `cat $pname | grep -v "^[ $tab]*#"`
    #
    # The number of elements on the record.
    set nc = $#frec
    #
    # The number of data lines in the quota file.
    @ nl = $#frec / 5
    #
    # Print out an informative message.
    if ( $proflag ) then
	if ( $nl == 1 ) then
	    echo -n \
		"/$fs to /${bfs}: there is $nl $fext to verify for update: "
	else
	    echo -n \
	    "/$fs to /${bfs}: there are $nl ${fext}s to verify for update: "
	endif
    endif
    #
    # Start a counter; inm is always the index of a name.
    set inm = 1
    #
    # Loop over the elements.
    while ( $inm <= $nc )
	#
	# Check whether the user or group exists in the system.
	#
	# The user case.
	if ( $qtype == u ) then
	    #
	    # The local case.
	    grep "^${frec[$inm]}:" /etc/passwd >& /dev/null
	    set stat1 = $status
	    #
	    # The NIS case.
	    #
	    # Check whether NIS is available.
	    if ( -x /usr/bin/ypmatch ) then
		ypmatch $frec[$inm] passwd >& /dev/null
		set stat2 = $status
	    else
		set stat2 = 1
	    endif
	#
	# The group case.
	else
	    #
	    # The local case.
	    grep "^${frec[$inm]}:" /etc/group >& /dev/null
	    set stat1 = $status
	    #
	    # The NIS case.
	    #
	    # Check whether NIS is available.
	    if ( -x /usr/bin/ypmatch ) then
		ypmatch $frec[$inm] group >& /dev/null
		set stat2 = $status
	    else
		set stat2 = 1
	    endif
	endif
	#
	# The user or group must be found somewhere; if not, then
	# _ write out a warning message and skip the entry.
	if ( $stat1 != 0 && $stat2 != 0 ) then 
	    #
	    # The user case.
	    if ( $qtype == u ) then
		echo ""
		echo \
		"${name}: warning: user $frec[$inm] does not exist in system"
	    #
	    # The group case.
	    else
		echo ""
		echo \
		"${name}: warning: group $frec[$inm] does not exist in system"
	    endif
	else
	    #
	    # The soft block quota is the first element after the name.
	    @ isb = $inm + 1
	    #
	    # The hard block quota is the second element after the name.
	    @ ihb = $inm + 2
	    #
	    # The soft inode quota is the third element after the name.
	    @ isi = $inm + 3
	    #
	    # The hard inode quota is the fourth element after the name.
	    @ ihi = $inm + 4
	    #
	    # Fix the username.
	    if ( `echo -n $frec[$inm] | wc -c` < 8 ) then
		set quser = "$frec[$inm]$tab"
	    else
		set quser = $frec[$inm]
	    endif
	    #
	    # Double the soft quotas.
	    @ qsb = $frec[$isb] * 2
	    @ qsi = $frec[$isi] * 2
	    #
	    # Double the hard quotas.
	    @ qhb = $frec[$ihb] * 2
	    @ qhi = $frec[$ihi] * 2
	    #
	    # Add 20% more to the hard quotas.
	    @ qhb = $qhb + $qsb / 5
	    @ qhi = $qhi + $qsi / 5
	    #
	    # Update the text file of the backup-home filesystem.
	    #
	    # Make sure the grep search target ends with a space or a tab.
	    set suser = \'"$frec[$inm][ $tab]"\'
	    #
	    # Define the sed search text.
	    set st = "`grep ^$suser $bpname`"
	    #
	    # Check for a search error to avoid an error in sed later;
	    # _ that error could cause the loss of the target file.
	    if ( $status != 0 || "$st" == "" ) then
		echo -n "${name}: ERROR looking for ${fext}name"
		echo " $frec[$inm] in file $bpname"
		cleanexit 1
	    endif
	    #
	    # Define the sed replace text.
	    set rt = "$quser$tab$qsb$tab$tab$qhb$tab$tab$qsi$tab$tab$qhi"
	    #
	    # Use sed to edit the text quota file; note: do _not_
	    # _ use mv, in order not to change the soft links.
	    cat $bpname | sed -e "s|$st|$rt|g" >! $bpname.TMP
	    cp -pf $bpname.TMP $bpname
	    #
	    # Get the quota entries for the user at the device containing
	    # _ the backup-home filesystem.
	    set entries = `quota -v -l -$qtype $frec[$inm] \
			    | grep $fsdev | tr '*' ' '`
	    #
	    # The third and fourth entries are the soft and hard block limits.
	    set bsoft = $entries[3]
	    set bhard = $entries[4]
	    #
	    # Verify whether the fifth entry has any letters within it.
	    echo $entries[5] | grep -q '[a-z]'
	    set thestatus = $status
	    #
	    # If it does not, then there is no grace-period entry and hence the
	    # _ sixth and seventh entries are the soft and hard inode limits.
	    if ( $thestatus ) then
		set isoft = $entries[6]
		set ihard = $entries[7]
	    #
	    # If it does, then there is a grace-period entry and in this case
	    # _ the seventh and eigth entries are the soft and hard inode
	    # _ limits.
	    else
		set isoft = $entries[7]
		set ihard = $entries[8]
	    endif
	    #
	    # Verify whether any quota data was changed, and update only the
	    # _ data which was in fact changed; first set an output control
	    # _ flag.
	    set outflag = 1
	    #
	    # Soft blocks.
	    if ( $bsoft != $qsb ) then
		if ( $proflag ) then
		    echo -n " $frec[$inm] (soft block) "
		endif
		quotatool -$qtype $frec[$inm] -b -q $qsb /$bfs
		#
		# If there is an error, exit cleanly with that error.
		if ( $status != 0 ) then
		    cleanexit $status
		endif
		#
		set outflag = 0
	    endif
	    #
	    # Hard blocks.
	    if ( $bhard != $qhb ) then
		if ( $proflag ) then
		    echo -n " $frec[$inm] (hard block) "
		endif
		quotatool -$qtype $frec[$inm] -b -l $qhb /$bfs
		#
		# If there is an error, exit cleanly.
		if ( $status != 0 ) then
		    cleanexit $status
		endif
		#
		set outflag = 0
	    endif
	    #
	    # Soft inodes.
	    if ( $isoft != $qsi ) then
		if ( $proflag ) then
		    echo -n " $frec[$inm] (soft inode) "
		endif
		quotatool -$qtype $frec[$inm] -i -q $qsi /$bfs
		#
		# If there is an error, exit cleanly.
		if ( $status != 0 ) then
		    cleanexit $status
		endif
		#
		set outflag = 0
	    endif
	    #
	    # Hard inodes.
	    if ( $ihard != $qhi ) then
		if ( $proflag ) then
		    echo -n " $frec[$inm] (hard inode) "
		endif
		quotatool -$qtype $frec[$inm] -i -l $qhi /$bfs
		#
		# If there is an error, exit cleanly.
		if ( $status != 0 ) then
		    cleanexit $status
		endif
		#
		set outflag = 0
	    endif
	    #
	    # Print out some progress report.
	    if ( $proflag && $outflag ) then
		echo -n "."
	    endif
	    #
	endif
	#
	# Increment the counter to the next username.
	@ inm = $inm + 5
    end
    #
    # Remove the temporary file.
    rm -f $bpname.TMP
    #
    # Finish the progress-report output.
    if ( $proflag ) then
	echo " done."
    endif
    #
end
#
# Normal exit: remove the lock file.
normalexit:
cleanexit 0
