#!/bin/tcsh -f
# JLdL 15Sep16.
#
# Copyright (C) 2004-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.
#
# Do automatic backups on disk of a list of chosen directories;
# _ this is meant for the backup of user home directories;
# _ this script is meant to be executed from a cron job,
# _ but it may be executed manually as well.
#
# 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"
#
# 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>]"
	    echo "       -C: use alternate configuration file <config>"
	    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"
	    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
#
# Define a variable with a tab character.
set tab = "`echo '\t'`"
#
# Source the configuration file; this must define the following
# _ variables: home_path; backup_root.
if ( -r $confile ) then
    source $confile
else
    echo "${name}: ERROR: cannot read configuration file $confile"
    exit 1
endif
#
# Define the home area, or set of home areas.
set homedrs = "$home_path"
#
# Define the backup root.
set bckprt = $backup_root
#
# Define the snap and log file area.
set snapdr = /var/lib/quotas-config
#
# Create the directory, if it is missing.
if ( ! -Ld $snapdr ) then
    mkdir -p $snapdr
endif
#
# Define the lock file.
set lockfile = /var/lock/`echo $name | cut -d. -f1`
#
# Check for a lock file.
if ( -f $lockfile ) then
    echo "${name}: this script is already running"
    exit 1
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
#
# Define the global log file.
set logfile = /var/log/$name.log
#
# Start a log file.
cat /dev/null >! $logfile
#
# Determine the NIS status; start by assuming that
# _ there is no NIS running.
set nistat = none
#
# Check whether this system is bound to some NIS server.
if ( -x /usr/bin/ypwhich ) then
    ypwhich >& /dev/null
    if ( $status == 0 ) then
	set nistat = client
    endif
endif
#
# Check whether this system is a NIS server,
# _ and if it is then determine its type.
if ( -f /etc/default/nis ) then
    set nisserver = `grep NISSERVER /etc/default/nis | cut -d= -f2`
    if ( "$nisserver" == slave ) then
	set nistat = slave
    else if ( "$nisserver" == master ) then
	set nistat = master
    endif
endif
#
# Define the library directory.
set libdir = /usr/lib/quotas-config
#
# Define the lock shell-substitute.
set locksh = /usr/local/cbin/lock
#
# Loop over the set of home areas.
foreach homedr ( $homedrs )
    #
    # Define the corresponding backup area.
    set bckpdr = $bckprt$homedr
    #
    # Check whether the home and backup directories are present;
    # _ if not, exit in error with a warning; note that this must
    # _ be a directory, and not a symlink to somewhere else.
    if ( ! -Ld $homedr ) then
	echo "${name}: ERROR: cannot find home directory $homedr"
	cleanexit 1
    endif
    #
    # Note that in this case we allow for a symlink, in order to
    # _ allow the use of a remote home backup filesystem.
    if ( ! -d $bckpdr ) then
	echo "${name}: ERROR: cannot find backup directory $bckpdr"
	cleanexit 1
    endif
    #
    # Strip away any "/"'s at the extremes of the filesystem path.
    set homedr = `echo $homedr | sed -e 's|/\+$||g' -e 's|^/\+||g'`
    #
    # Get the device containing the home filesystem from
    # _ the fstab file; one must exclude comment lines.
    set homedv = `grep "[ $tab]\+/${homedr}[ $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 $homedv| grep UUID=`" != "" ) then
	#
	# Extract the UUID number, without the keyword
	# _ "UUID=" and the punctuation marks "-".
	set uidev = `echo $homedv | sed -e 's|\-||g' | cut -d= -f2`
	#
	# Get the device node from the output of blkid.
	set homedv = `blkid | sed -e 's|\-||g' | grep "$uidev" | cut -d: -f1`
	#
    endif
    #
    # Strip away any "/"'s at the extremes of the filesystem path.
    set bckpdr = `echo $bckpdr | sed -e 's|/\+$||g' -e 's|^/\+||g'`
    #
    # Get the device containing the backup-home filesystem from
    # _ the fstab file; one must exclude comment lines.
    set bckpdv = `grep "[ $tab]\+/${bckpdr}[ $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 $bckpdv| grep UUID=`" != "" ) then
	#
	# Extract the UUID number, without the keyword
	# _ "UUID=" and the punctuation marks "-".
	set uidev = `echo $bckpdv | sed -e 's|\-||g' | cut -d= -f2`
	#
	# Get the device node from the output of blkid.
	set bckpdv = `blkid | sed -e 's|\-||g' | grep "$uidev" | cut -d: -f1`
	#
    endif
    #
    # Check for a network mount of the backup filesystem.
    echo $bckpdv | grep -q : > /dev/null
    #
    # If it is, force the automounting of the directory.
    if ( $status == 0 ) then
	ls /$bckpdr/ > /dev/null
    endif
    #
    # Get the list of user directories to backup, with exclusions.
    set excl = "^/$homedr/lost\+found"'$'
    set excl = "$excl|^/$homedr/ftp"'$'
    set excl = "$excl|^/$homedr/\.\.\."'$'
    set excl = "$excl|"'BCKP'
    set excl = "$excl|"'BACKUP'
    set excl = "$excl|"'OLD-WAIT'
    set excl = "$excl|"'LOST-USERS'
    set directs = `find /$homedr -maxdepth 1 -mindepth 1 -type d | \
			sort | egrep -v "$excl"`
    #
    # Loop over directories to backup.
    foreach direct ( $directs )
	#
	# Get the username of the user; filter out _XXX terminations.
	set direnm = `basename $direct`
	set usernm = `echo $direnm | cut -d_ -f1`
	#
	# Check whether the account is locked; if there is no NIS,
	# _ use the local passwd file, otherwise use the NIS map.
	if ( $nistat == none ) then
	    set locked = `grep "^$usernm" /etc/passwd | cut -d: -f7`
	else
	    set locked = `ypcat passwd | sort -u | \
			grep "^$usernm" | cut -d: -f7`
	endif
	#
	# Only proceed if the account is not locked.
	if ( "$locked" == "$locksh" ) then
	    #
	    # Log the event.
	    echo "Account $usernm was ignored: it was already locked." \
		    >> $logfile
	else
	    #
	    # Check whether the user is overquota in the home area.
	    set ovrqth = `quota -lgq $usernm | grep $homedv`
	    #
	    # Check whether the user is overquota in the backup area.
	    set ovrqtb = `quota -lgq $usernm | grep $bckpdv`
	    #
	    # If the backup is over the hard limit or the overquota
	    # _ grace period is over, then lock the account.
	    if ( "$ovrqtb" == "Block limit reached on $bckpdv" \
		|| "$ovrqtb" == "Over block quota on $bckpdv" ) then
		#
		# Lock the account.
		$libdir/myrror-user-lock-the-account.sub \
		    $nistat $libdir $locksh $direct $usernm
		#
		# Log the event.
		echo "Account $usernm was LOCKED: overquota in /$bckpdr." \
			>> $logfile
	    #
	    # If the user is in the overquota grace period, send a warning mail.
	    else if ( "$ovrqtb" == "In block grace period on $bckpdv" ) then
		#
		# But only send mail if the home is not overquota.
		if ( "$ovrqth" == "" ) then
		    #
		    # Send a warning mail to the user.
		    $libdir/myrror-user-mail-over-quota.sub \
			$bckprt $libdir $direct $usernm
		    #
		    # Log the event.
		    echo "Warning mail about quota was sent to $usernm." \
			    >> $logfile
		else
		    #
		    # Log the event.
		    echo "Warning mail about quota was NOT sent to $usernm." \
			    >> $logfile
		endif
	    #
	    # If the backup area is within quota, proceed with the backup.
	    else
		#
		# But only do the backup if the home is not overquota.
		if ( "$ovrqth" == "" ) then
		    #
		    # Do the backup.
		    $libdir/myrror-user-do-the-backup.sub \
			$bckprt $snapdr $direct $usernm
		else
		    #
		    # Log the event: account 
		    echo "Account $usernm was ignored: overquota in /$homedr." \
			    >> $logfile
		endif
	    endif
	endif
    end
end
#
# Execute local site-specific commands.
if ( -x $libdir/myrror-user.local ) then
    $libdir/myrror-user.local $nistat $libdir $locksh
endif
#
# Normal exit: remove the lock file.
normalexit:
cleanexit 0
