more refactoring and fixing, now fully functional and with a documented config

This commit is contained in:
Von Random 2015-01-05 18:41:59 +03:00
parent ffe5c9ac16
commit 0aabfe44bf
2 changed files with 102 additions and 49 deletions

View file

@ -1,11 +1,50 @@
protocol=ssh # ftp, sftp, ftps, ssh or local #### general options ####
remote_host=hostname.tld ## The protocol we want to use to store our backups.
backup_dir=relative_or_full_path ## Can be ftp, sftp, ftps, ssh or local.
#snap_file=/var/backup/snapshot.list protocol='ssh'
#exclude_list=/usr/local/etc/backup/excludes.list
compress_format=xz # gz, bz2, xz or empty for non-compressed ## The directory store backups in, locally or remotely.
remote_user=username backup_dir='relative_or_full_path'
remote_pass=PassWd
source_dirs=( '/home/user/source1:/var/backup/snapshot.list' ) ## The list of patterns to exclude from backups, for
## more details look into tar -X option.
#exclude_list='/usr/local/etc/backup/excludes.list'
## The compression algorithm for backups.
## Can be gz, bz2, xz or empty (for non-compressed).
compress_format='xz'
## An array with the set of directories within it.
## Optionally snapshot file can be added to store
## incremental diffs (tar -g option used).
## You'll have to deal with snapshots on your own:
## backup.zsh only handles backups (i.e. you can remove
## snapshot via cron on regular basis to ensure that
## full backups are created from time to time.)
source_dirs=( '/home/user/source1:/var/backup/snapshot.list'
'/etc' '/var/spool/mail:/var/backup/spool_snapshot.list' )
## Use with caution, the file existance is not checked
## on execution.
## Since this config is sourced, I advise adding some
## logic for that, or you can handle filename collisions
## externally.
#backup_filename='somebackup'
#### remote options ####
## Remote host.
remote_host='hostname.tld'
## Remote user.
remote_user='username'
## Password, due to how openssh handles security it only
## works for *ftp* protocols; backups via ssh protocol
## work interactively. Later versions will have support
## for ssh keys.
remote_pass='PassWd'
## Port is optional, the defaults are hardcoded.
#remote_port='443'
# vim: ft=zsh # vim: ft=zsh

View file

@ -1,6 +1,6 @@
#!/usr/bin/env zsh #!/usr/bin/env zsh
self_name=$0 self_name=$0
default_cfg='/usr/local/etc/backup.cfg' default_cfg='/etc/backup.zsh.cfg'
default_postfix=$(date +%F-%H%M) default_postfix=$(date +%F-%H%M)
default_ftp_port='21' default_ftp_port='21'
default_ssh_port='22' default_ssh_port='22'
@ -12,35 +12,51 @@ function err
function cfg_err function cfg_err
{ {
[[ -n $1 ]] && echo "$1 is not set in configuration, but is required by $0 to work." >&2 [[ -n $1 ]] && echo "$1 is not set in configuration, but is required by $self_name to work." >&2
} }
function usage function usage
{ {
echo "usage: $self_name [--help|--config] echo "usage: $self_name [--help|--conf /path/to/config]
--help -h - show this message --help -h show this message
--config -c - use config from the specified path --conf -c use config from the specified path
Default config path /usr/local/etc/backup.cfg will be used if invoked without options" Default config path $default_cfg will be used if invoked without options"
} }
# function to read the configuration file and spit out some exceptions if stuff is missing # function to read the configuration file and spit out some exceptions if stuff is missing
function apply_config function apply_config
{ {
source $cfg || err 15 'Config file does not exist' function test_remote_settings
{
if [[ -z $remote_host ]]; then
cfg_err 'remote_host'
return 5
fi
if [[ -z $remote_user ]]; then
cfg_err 'remote_user'
return 5
fi
if [[ -z $remote_pass ]]; then
cfg_err 'remote_pass'
return 5
fi
if [[ -n $port && ! $port =~ ^[0-9]+$ ]]; then
err 'Remote port is not a numeric value.'
return 5
fi
}
source $cfg || { err "Config file $cfg is unreadable or does not exist"; return 15 }
if [[ -z $source_dirs ]]; then if [[ -z $source_dirs ]]; then
cfg_err 'Backup source' cfg_err 'source_dirs'
exit 5 return 5
fi
if [[ -z $remote_host ]]; then
cfg_err 'Remote host'
exit 5
fi fi
if [[ -z $backup_dir && $protocol != 'ssh' ]]; then if [[ -z $backup_dir && $protocol != 'ssh' ]]; then
cfg_err 'Target directory' cfg_err 'backup_dir'
exit 5 return 5
fi fi
if [[ -z $local_host ]]; then if [[ -z $local_host ]]; then
err 'local_host is not set, using hostname.'
local_host=$HOST local_host=$HOST
fi fi
# date postfix # date postfix
@ -50,20 +66,17 @@ function apply_config
postfix=$outfile_postfix postfix=$outfile_postfix
fi fi
case $protocol in case $protocol in
('ftp'|'ftps') port=${remote_port:-$default_ftp_port};; ('ftp'|'ftps') port=${remote_port:-$default_ftp_port}; test_remote_settings; return $?;;
('sftp'|'ssh') port=${remote_port:-$default_ssh_port};; ('sftp'|'ssh') port=${remote_port:-$default_ssh_port}; test_remote_settings; return $?;;
('local') unset remote_port;; ('local') unset remote_port;;
(*) cfg_err 'Backup protocol'; exit 5;; (*) cfg_err 'protocol'; return 5;;
esac esac
if [[ ! $remote_port =~ ^[0-9]+$ ]]; then
err 'Remote port is not a numeric value.'
fi
case $compress_format in case $compress_format in
('xz') compress_flag='J' ;; ('xz') compress_flag='J' ;;
('bz2') compress_flag='j' ;; ('bz2') compress_flag='j' ;;
('gz') compress_flag='z' ;; ('gz') compress_flag='z' ;;
('') unset compress_flag; unset compress_format ;; ('') unset compress_flag; unset compress_format ;;
(*) err "$compress_format is not a valid value for the compression format option."; exit 5;; (*) err "$compress_format is not a valid value for the compression format option."; return 5;;
esac esac
if [[ -n $exclude_list ]]; then if [[ -n $exclude_list ]]; then
if [[ -r $exclude_list ]]; then if [[ -r $exclude_list ]]; then
@ -72,13 +85,6 @@ function apply_config
err "Exclusion list $exclude_list is either unreadable or does not exist." err "Exclusion list $exclude_list is either unreadable or does not exist."
fi fi
fi fi
if [[ -n $snap_file ]]; then
if printf '' >> $snap_file; then
snapshot_option='-g'
else
err "Snapshot file $snap_file cannot be written."
fi
fi
} }
# generate the full backup path # generate the full backup path
@ -86,7 +92,7 @@ function generate_fullpath
{ {
local backup_type local backup_type
# increment or full backup # increment or full backup
if [[ -s $snap_file ]]; then if [[ -s $snapshot_file ]]; then
backup_type='incr' backup_type='incr'
else else
backup_type='full' backup_type='full'
@ -100,7 +106,14 @@ function generate_fullpath
function compress # compress to stdout function compress # compress to stdout
{ {
tar cf$compress_flag - -C $src_basedir $src_basename $snapshot_option $snapshot_file $exclude_option $exclude_list --ignore-failed-read if [[ -n $snapshot_file ]]; then
if printf '' >> $snapshot_file; then
snapshot_option='-g'
else
err "Snapshot file $snapshot_file cannot be written."
fi
fi
tar -c$compress_flag $snapshot_option $snapshot_file $exclude_option $exclude_list --ignore-failed-read -C $src_basedir $src_basename
} }
function store # store to local or remote function store # store to local or remote
@ -116,27 +129,28 @@ function parse_opts
{ {
while [[ -n $1 ]]; do while [[ -n $1 ]]; do
case $1 in case $1 in
('--help'|'-h') usage; return 0;; ('--help'|'-h') usage; exit 0;;
('--config'|'-c') shift; opt_cfg=$1; shift;; ('--conf'|'-c') shift; opt_cfg=$1; shift;;
('') opt_cfg=$default_cfg;;
(*) err "unknown parameter $1"; exit 127;;
esac esac
done done
} }
function main function main
{ {
parse_opts parse_opts $@
if [[ -z $opt_cfg ]]; then cfg=${opt_cfg:-$default_cfg}
cfg=$default_cfg
else
cfg=$opt_cfg
fi
apply_config apply_config
local apply_config_returns=$?
[[ $apply_config_returns -ne 0 ]] && return $apply_config_returns
for i in $source_dirs; do for i in $source_dirs; do
unset src_basename src_basedir outfile unset src_basename src_basedir outfile
IFS=':' read source_dir snap_file <<< $i IFS=':' read source_dir snapshot_file <<< $i
src_basename=${source_dir:t} src_basename=${source_dir:t}
src_basedir=${source_dir:h} src_basedir=${source_dir:h}
generate_fullpath generate_fullpath
echo "Creating a backup of $source_dir. Using protocol $protocol to store it in $outfile."
compress | store compress | store
done done
return 0 return 0