#!/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