more refactoring and fixing, now fully functional and with a documented config
This commit is contained in:
parent
ffe5c9ac16
commit
0aabfe44bf
2 changed files with 102 additions and 49 deletions
57
backup.cfg
57
backup.cfg
|
@ -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
|
||||||
|
|
94
backup.zsh
94
backup.zsh
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue