NSLU2-Linux
view · edit · print · history
#!/bin/bash

# #######################################
# Constants
# #######################################
ROOT_UID=0;
OK=0;
KO=1;

# source and dest directory must not end by /
BACKUP_DIRECTORY="/backup";
BACKUP_DIRECTORY_PATTERN="Backup";

BACKUP_PARTITION="/dev/sdb2";

STANDARD_CONFIG_FILE="/etc/backup"

# #######################################
# Global variables
# #######################################
G_BackupDirectory="Not yet assigned";
G_DatedBackupDirectory="Not yet assigned";

G_Date="Not yet assigned";
G_NbrFilesAlreadyExist=0;
G_NbrDirAlreadyExist=0;

# #######################################
# _Report
# #######################################
function _Report
{
	CurrentTime=`/bin/date | /usr/bin/awk '{print $4}'`;
	/bin/echo "$G_Date $CurrentTime $1 : $2";
}

# #######################################
# _ReportDiskSpace
# #######################################
function _ReportDiskSpace
{
	Message="$BACKUP_PARTITION full by ";
	Message=$Message"`/bin/df | grep $BACKUP_PARTITION | /usr/bin/awk '{print $5}'`";
	Message=$Message" (available=";
	Message=$Message"`/bin/df | grep $BACKUP_PARTITION | /usr/bin/awk '{print $4}'`";
	Message=$Message" - used =";
	Message=$Message"`/bin/df | grep $BACKUP_PARTITION | /usr/bin/awk '{print $3}'`";
	Message=$Message")";

	_Report "INFO" "$Message" ;
}

# #######################################
# _IsRootUser test function
# #######################################
function _IsRootUser
{
	if [ `/usr/bin/id -u` -ne $ROOT_UID ]; then
	{
		return $KO;
	}
	else
	{
		return $OK;
	}
	fi;
}

# #######################################
# _UpdateGlobalVariables
# #######################################
function _UpdateDateGlobalVariables
{
	# G_Date
	# ======
	# Year
	G_Date=`/bin/date | /usr/bin/awk '{print $6}'`;

	# Month
	case "`/bin/date | /usr/bin/awk '{print $2}'`" in
		"Jan")	G_Date="$G_Date"01;;
		"Feb")	G_Date="$G_Date"02;;
		"Mar")	G_Date="$G_Date"03;;
		"Apr")	G_Date="$G_Date"04;;
		"May")	G_Date="$G_Date"05;;
		"Jun")	G_Date="$G_Date"06;;
		"Jul")	G_Date="$G_Date"07;;
		"Aug")	G_Date="$G_Date"08;;
		"Sep")	G_Date="$G_Date"09;;
		"Oct")	G_Date="$G_Date"10;;
		"Nov")	G_Date="$G_Date"11;;
		"Dec")	G_Date="$G_Date"12;;
		*) 	_Report "ERROR"  "Improper value";
			exit;;
	esac

	# Day
	DayNumber=`/bin/date | /usr/bin/awk '{print $3}'`;
	if [ $DayNumber -lt 10 ]; then
	{
		G_Date="$G_Date"0"$DayNumber";
	}
	else
	{
		G_Date="$G_Date$DayNumber";
	}
	fi;
}

# #######################################
# _UpdateDestinationDirectoriesGlobalVariable
# $1 : Backup Keyword
# #######################################
function _UpdateDestinationDirectoriesGlobalVariables
{
	if [ -z "$1" ]; then
	{
		_Report "ERROR" "The backup keyword is empty";
		exit $KO;
	}
	else
	{
		G_BackupDirectory="$BACKUP_DIRECTORY/$BACKUP_DIRECTORY_PATTERN$1";
		G_DatedBackupDirectory="$BACKUP_DIRECTORY/$BACKUP_DIRECTORY_PATTERN$1$G_Date";
	}
	fi;
}

# #######################################
# _RecursiveHardLink
# $1 is the source directory to copy
# $2 is the destination directory
# #######################################
function _RecursiveHardLink
{
	if [ ! -e $1 ]; then
	{
		_Report "WARNING" "Source directory $1 does not exist"; # can happen if run for the very first time
		return $KO;
	}
	elif [ -f $1 ]; then
	{
		_Report "ERROR" "Source directory $1 is a file";
		exit $KO;
	}
	fi;

	if [ -e "$2" ]; then
	{
		if [ -d "$2" ]; then
		{
			let "G_NbrDirAlreadyExist=$G_NbrDirAlreadyExist+1";
		}
		else
		{
			_Report "ERROR" "$2 already exists and is a file";
			exit $KO;
		}
		fi;
	}
	else	
	{
		/bin/mkdir "$2";
	}
	fi;

	IFS=$'\n';
	for inode in $(ls $1); do

		if [ -f "$1/$inode" ]; then
		{
			if [ -e "$2/$inode" ]; then
			{
				if [ -d "$2/$inode" ]; then
				{
					_Report "ERROR" "$2/$inode already exists and is a directory";
					exit $KO;
				}
				else
				{
					let "G_NbrFilesAlreadyExist=$G_NbrFilesAlreadyExist+1";
				}
				fi;
			}
			else
			{
				/bin/ln "$1/$inode" "$2/$inode";
			}
			fi;
		}
		elif [ -d "$1/$inode" ]; then
		{
			_RecursiveHardLink "$1/$inode" "$2/$inode";
		}
		fi;
	done
}

# #######################################
# _Backup
# $1 is the source directory to backup
# $2 is the destination backup directory
# #######################################
function _Backup
{
	# test parameters
	# ---------------
	if [ ! -e "$1" ]; then
	{
		_Report "ERROR" "The source directory to backup $1 does not exist";
		exit $KO;
	}
	elif [ -f "$1" ]; then
	{
		_Report "ERROR" "The source directory to backup $1 is a file";
		exit $KO;
	}
	fi;

	if [ ! -e "$2" ]; then
	{
		_Report "WARNING" "The backup directory $2 does not exist";
		/bin/mkdir "$2";
		_Report "INFO" "Directory $2 created";
	}
	elif [ -f "$2" ]; then
	{
		_Report "ERROR" "The backup directory $2 is a file";
		exit $KO;
	}
	fi;


	# rsync the directories
	# ---------------------
	/usr/bin/rsync -v -a --delete $1/ $2/
}

# #######################################
# _DeleteDatedBackups
# $1 is the max number of dated backups to keep
# #######################################
function _DeleteDatedBackups
{
	# compute the directories filter for ls
	# =====================================
	let Lgth=`expr length $G_DatedBackupDirectory`-8;
	Filter="`expr substr $G_DatedBackupDirectory 1 $Lgth`*";

	# delete all files except NbrFiles
	# ================================
	IFS=$'\n';
	let FilesNbr=`ls -d $Filter | wc -l`-1;
	let FilesToRemove=`ls -d $Filter | wc -l`-$1;

	_Report "INFO" "Found $FilesNbr directories for $1 allowed"

	if [ $FilesToRemove -gt 0 ]; then
	{
		_Report "INFO" "Will remove $FilesToRemove directories"

		FilesList="`ls -d $Filter | tail -$FilesNbr | head -$FilesToRemove`";
		for File in $FilesList; do
			_Report "INFO" "Removing $File...";
			rm -r $File;
		done
	}
	fi;
}

# #######################################
# _BackupADirectory
# $1 is the source directory to backup
# $2 is the keyword for the backup
# $3 indicates the max number of dated backup to keep
# #######################################
function _BackupADirectory
{
	# check parameters
	# ================
	if [ ! -e "$1" ]; then
	{
		_Report "ERROR" "The source directory to backup $1 does not exist";
		exit $KO;
	}
	elif [ -f "$1" ]; then
	{
		_Report "ERROR" "The source directory to backup $1 is a file";
		exit $KO;
	}
	fi;

	if [ -z "$2" ]; then
	{
		_Report "ERROR" "The backup keyword is empty";
		exit $KO;
	}
	fi;

	if [ -z "$3" ]; then
	{
		_Report "ERROR" "No backup length parameter";
		exit $KO;
	}
	elif [ $3 -lt 0 ]; then
	{
		_Report "ERROR" "The backup length ($3) must be positive or nul";
		exit $KO;
	}
	fi;


	# Update directories name global variables
	# ========================================
	_UpdateDestinationDirectoriesGlobalVariables $2;

	_Report "INFO" "The source to backup is $1";
	_ReportDiskSpace;

	# delete the unnecessary dated backups
	# ====================================
	_DeleteDatedBackups $3


	# Hard links creation
	# ===================
	if [ $3 -gt 0 ]; then
	{
		_Report "INFO" "The dated backup directory is $G_DatedBackupDirectory";
		_Report "INFO" "Dated backup in progress...";
		G_NbrFilesAlreadyExist=0;
		G_NbrDirAlreadyExist=0;

		_RecursiveHardLink $G_BackupDirectory $G_DatedBackupDirectory;

		if [ $G_NbrFilesAlreadyExist -gt 0 -o $G_NbrDirAlreadyExist -gt 0 ]; then
		{
			_Report "WARNING" "$G_NbrFilesAlreadyExist files and $G_NbrDirAlreadyExist directories were already existing : those files have not been overwritten";
		}	
		fi;

		_Report "INFO" "Dated backup completed...";
		_ReportDiskSpace;
	}
	else
	{
	_Report "INFO" "No dated backup as requested"
	}
	fi;


	# start the backup
	# ================
	_Report "INFO" "The current backup directory is $G_BackupDirectory";
	_Report "INFO" "Backup in progress...";
	_Backup $1 $G_BackupDirectory;
	_Report "INFO" "Backup completed...";
	_ReportDiskSpace;
}

# #######################################
# _ScheduleBackupsFromFile
# $1 is the config file name
# #######################################
function _ScheduleBackupsFromFile
{
	# test if the file exists
	# -----------------------
	if [ ! -e $1 ]; then
	{
		_Report "ERROR" "The config file $1 does not exist";
		exit $KO;
	}
	elif [ ! -f $1 ]; then
	{
		_Report "ERROR" "$1 is not a file";
		exit $KO;
	}
	fi;

	# read the file and run the backup
	# --------------------------------
	IFS="	";  # tab
	cat $1 | while read SourceDir Keyword MaxDatedBackups; do 
		_BackupADirectory $SourceDir $Keyword $MaxDatedBackups;
		IFS="	"; # tab, overwritten by _RecursiveHardLinks
	done
}

# #######################################
# _DisplaySyntax
# #######################################
function _DisplaySyntax
{
	_Report "" "Syntax : $0";
	_Report "" "1 argument  -> use this argument as a config file";
	_Report "" "3 arguments -> source directory to backup";
	_Report "" "            -> backup keyword";
	_Report "" "            -> backup length (can be 0 or more)";
}

# #######################################
# Main script
# can be used with 1 parameter (config file name)
# or with 3 parameters (source directory, backup keyword, backup length)
# #######################################

# Update global var
# =================
_UpdateDateGlobalVariables;

# initial tests
# =============

# make sure we are root
if ( ! _IsRootUser );  then 
{
	_Report "ERROR" "Must be root to use the script";
	exit;
}
fi;

# test parameters
# ===============
case $# in
1)	_ScheduleBackupsFromFile $1;;
3)	_BackupADirectory $1 $2 $3;;
*)	_DisplaySyntax;;
esac

view · edit · print · history · Last edited by JNC.
Originally by JNC.
Page last modified on June 10, 2008, at 01:02 PM