From 0aabfe44bfc7eddab5167f644a6db8e9dbe12f84 Mon Sep 17 00:00:00 2001 From: Von Random Date: Mon, 5 Jan 2015 18:41:59 +0300 Subject: more refactoring and fixing, now fully functional and with a documented config --- backup.cfg | 57 +++++++++++++++++++++++++++++++------ backup.zsh | 94 ++++++++++++++++++++++++++++++++++++-------------------------- 2 files changed, 102 insertions(+), 49 deletions(-) diff --git a/backup.cfg b/backup.cfg index a676cf2..982df09 100644 --- a/backup.cfg +++ b/backup.cfg @@ -1,11 +1,50 @@ -protocol=ssh # ftp, sftp, ftps, ssh or local -remote_host=hostname.tld -backup_dir=relative_or_full_path -#snap_file=/var/backup/snapshot.list -#exclude_list=/usr/local/etc/backup/excludes.list -compress_format=xz # gz, bz2, xz or empty for non-compressed -remote_user=username -remote_pass=PassWd -source_dirs=( '/home/user/source1:/var/backup/snapshot.list' ) +#### general options #### +## The protocol we want to use to store our backups. +## Can be ftp, sftp, ftps, ssh or local. +protocol='ssh' + +## The directory store backups in, locally or remotely. +backup_dir='relative_or_full_path' + +## 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 diff --git a/backup.zsh b/backup.zsh index 77a8980..e99edbb 100755 --- a/backup.zsh +++ b/backup.zsh @@ -1,6 +1,6 @@ #!/usr/bin/env zsh self_name=$0 -default_cfg='/usr/local/etc/backup.cfg' +default_cfg='/etc/backup.zsh.cfg' default_postfix=$(date +%F-%H%M) default_ftp_port='21' default_ssh_port='22' @@ -12,35 +12,51 @@ function 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 { - echo "usage: $self_name [--help|--config] - --help -h - show this message - --config -c - use config from the specified path + echo "usage: $self_name [--help|--conf /path/to/config] + --help -h show this message + --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 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 - cfg_err 'Backup source' - exit 5 - fi - if [[ -z $remote_host ]]; then - cfg_err 'Remote host' - exit 5 + cfg_err 'source_dirs' + return 5 fi if [[ -z $backup_dir && $protocol != 'ssh' ]]; then - cfg_err 'Target directory' - exit 5 + cfg_err 'backup_dir' + return 5 fi if [[ -z $local_host ]]; then + err 'local_host is not set, using hostname.' local_host=$HOST fi # date postfix @@ -50,20 +66,17 @@ function apply_config postfix=$outfile_postfix fi case $protocol in - ('ftp'|'ftps') port=${remote_port:-$default_ftp_port};; - ('sftp'|'ssh') port=${remote_port:-$default_ssh_port};; + ('ftp'|'ftps') port=${remote_port:-$default_ftp_port}; test_remote_settings; return $?;; + ('sftp'|'ssh') port=${remote_port:-$default_ssh_port}; test_remote_settings; return $?;; ('local') unset remote_port;; - (*) cfg_err 'Backup protocol'; exit 5;; + (*) cfg_err 'protocol'; return 5;; esac - if [[ ! $remote_port =~ ^[0-9]+$ ]]; then - err 'Remote port is not a numeric value.' - fi case $compress_format in ('xz') compress_flag='J' ;; ('bz2') compress_flag='j' ;; ('gz') compress_flag='z' ;; ('') 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 if [[ -n $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." 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 @@ -86,7 +92,7 @@ function generate_fullpath { local backup_type # increment or full backup - if [[ -s $snap_file ]]; then + if [[ -s $snapshot_file ]]; then backup_type='incr' else backup_type='full' @@ -100,7 +106,14 @@ function generate_fullpath 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 @@ -116,27 +129,28 @@ function parse_opts { while [[ -n $1 ]]; do case $1 in - ('--help'|'-h') usage; return 0;; - ('--config'|'-c') shift; opt_cfg=$1; shift;; + ('--help'|'-h') usage; exit 0;; + ('--conf'|'-c') shift; opt_cfg=$1; shift;; + ('') opt_cfg=$default_cfg;; + (*) err "unknown parameter $1"; exit 127;; esac done } function main { - parse_opts - if [[ -z $opt_cfg ]]; then - cfg=$default_cfg - else - cfg=$opt_cfg - fi + parse_opts $@ + cfg=${opt_cfg:-$default_cfg} apply_config + local apply_config_returns=$? + [[ $apply_config_returns -ne 0 ]] && return $apply_config_returns for i in $source_dirs; do 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_basedir=${source_dir:h} generate_fullpath + echo "Creating a backup of $source_dir. Using protocol $protocol to store it in $outfile." compress | store done return 0 -- cgit v1.2.3