From ffe5c9ac16b73ee429f853494c9b355b296e6cb4 Mon Sep 17 00:00:00 2001 From: Von Random Date: Mon, 29 Dec 2014 18:13:47 +0300 Subject: [PATCH 01/10] some refactoring and err function fixes --- backup.zsh | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/backup.zsh b/backup.zsh index 5d7e64a..77a8980 100755 --- a/backup.zsh +++ b/backup.zsh @@ -2,6 +2,8 @@ self_name=$0 default_cfg='/usr/local/etc/backup.cfg' default_postfix=$(date +%F-%H%M) +default_ftp_port='21' +default_ssh_port='22' function err { @@ -26,10 +28,18 @@ function usage function apply_config { source $cfg || err 15 'Config file does not exist' - [[ -z $source_dirs ]] && { cfg_err 'Backup source'; exit 5 } - [[ -z $remote_host ]] && { cfg_err 'Remote host'; exit 5 } - [[ -z $protocol ]] && { cfg_err 'Backup protocol'; exit 5 } - [[ -z $backup_dir && $protocol != 'ssh' ]] && { cfg_err 'Target directory'; exit 5 } + if [[ -z $source_dirs ]]; then + cfg_err 'Backup source' + exit 5 + fi + if [[ -z $remote_host ]]; then + cfg_err 'Remote host' + exit 5 + fi + if [[ -z $backup_dir && $protocol != 'ssh' ]]; then + cfg_err 'Target directory' + exit 5 + fi if [[ -z $local_host ]]; then local_host=$HOST fi @@ -39,33 +49,34 @@ function apply_config else postfix=$outfile_postfix fi - if [[ -z $remote_port ]]; then - case $protocol in - ('ftp'|'ftps') remote_port='21';; - ('sftp'|'ssh') remote_port='22';; - ('local') unset remote_port;; - (*) err 1 "$protocol is not a valid value for the protocol option.";; - esac + case $protocol in + ('ftp'|'ftps') port=${remote_port:-$default_ftp_port};; + ('sftp'|'ssh') port=${remote_port:-$default_ssh_port};; + ('local') unset remote_port;; + (*) cfg_err 'Backup protocol'; exit 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 1 "$compress_format is not a valid value for the compression format option.";; + (*) err "$compress_format is not a valid value for the compression format option."; exit 5;; esac if [[ -n $exclude_list ]]; then if [[ -r $exclude_list ]]; then exclude_option='-X' else - err 1 "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 if [[ -n $snap_file ]]; then if printf '' >> $snap_file; then snapshot_option='-g' else - err 1 "Snapshot file $snap_file cannot be written." + err "Snapshot file $snap_file cannot be written." fi fi } @@ -96,9 +107,8 @@ function store # store to local or remote { case $protocol in ('local') dd of=$outfile ;; - ('ssh') ssh -p$remote_port $remote_user@$remote_host "dd of=$outfile" ;; - ('sftp'|'ftp'|'ftps') curl -ksS -T - $protocol://$remote_host:$remote_port/$outfile -u $remote_user:$remote_pass ;; - (*) err 1 'Wrong protocol!' ;; + ('ssh') ssh -p$port $remote_user@$remote_host "dd of=$outfile" ;; + ('sftp'|'ftp'|'ftps') curl -ksS -T - $protocol://$remote_host:$port/$outfile -u $remote_user:$remote_pass ;; esac } From 0aabfe44bfc7eddab5167f644a6db8e9dbe12f84 Mon Sep 17 00:00:00 2001 From: Von Random Date: Mon, 5 Jan 2015 18:41:59 +0300 Subject: [PATCH 02/10] 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 From ec4a6119f717f1c0e667e146ae6cf5f14f4fcd45 Mon Sep 17 00:00:00 2001 From: Von Random Date: Mon, 12 Jan 2015 09:31:40 +0300 Subject: [PATCH 03/10] some small fixes to notification messages --- backup.zsh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backup.zsh b/backup.zsh index e99edbb..1c3e8ca 100755 --- a/backup.zsh +++ b/backup.zsh @@ -82,7 +82,7 @@ function apply_config if [[ -r $exclude_list ]]; then exclude_option='-X' else - err "Exclusion list $exclude_list is either unreadable or does not exist." + err "Exclusion list $exclude_list is either unreadable or does not exist. Proceeding without it." fi fi } @@ -110,7 +110,7 @@ function compress # compress to stdout if printf '' >> $snapshot_file; then snapshot_option='-g' else - err "Snapshot file $snapshot_file cannot be written." + err "Snapshot file $snapshot_file cannot be written. Proceeding with full backup." fi fi tar -c$compress_flag $snapshot_option $snapshot_file $exclude_option $exclude_list --ignore-failed-read -C $src_basedir $src_basename From 4eaaf32b10f7f9db6998ff6983f500a19f4b5ead Mon Sep 17 00:00:00 2001 From: Von Random Date: Mon, 12 Jan 2015 17:46:10 +0300 Subject: [PATCH 04/10] added comments to the script, fixed a couple of lines of code --- backup.zsh | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/backup.zsh b/backup.zsh index 1c3e8ca..9b912be 100755 --- a/backup.zsh +++ b/backup.zsh @@ -5,16 +5,19 @@ default_postfix=$(date +%F-%H%M) default_ftp_port='21' default_ssh_port='22' +# echo to stderr function err { [[ -n $1 ]] && echo $1 >&2 } +# standard configuration error message to stderr function cfg_err { [[ -n $1 ]] && echo "$1 is not set in configuration, but is required by $self_name to work." >&2 } +# print help function usage { echo "usage: $self_name [--help|--conf /path/to/config] @@ -24,9 +27,10 @@ function usage 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 +# read the configuration file and spit out some exceptions if stuff is missing function apply_config { + # testing remote settings only needed for non-local backups function test_remote_settings { if [[ -z $remote_host ]]; then @@ -46,7 +50,9 @@ function apply_config return 5 fi } + # import contents of the config (including functions if present) source $cfg || { err "Config file $cfg is unreadable or does not exist"; return 15 } + # do the tests if [[ -z $source_dirs ]]; then cfg_err 'source_dirs' return 5 @@ -59,18 +65,19 @@ function apply_config err 'local_host is not set, using hostname.' local_host=$HOST fi - # date postfix if [[ -z $outfile_postfix ]]; then postfix=$default_postfix else postfix=$outfile_postfix fi + # set defaults and / or fail to run if something is missing case $protocol in ('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 'protocol'; return 5;; esac + # set variables for tar command case $compress_format in ('xz') compress_flag='J' ;; ('bz2') compress_flag='j' ;; @@ -104,8 +111,10 @@ function generate_fullpath fi } -function compress # compress to stdout +# compress to stdout +function compress { + # snapshot file is per directory so we cannot test it within apply_config() if [[ -n $snapshot_file ]]; then if printf '' >> $snapshot_file; then snapshot_option='-g' @@ -113,11 +122,14 @@ function compress # compress to stdout err "Snapshot file $snapshot_file cannot be written. Proceeding with full backup." fi fi + # do the magic and spit to stdout 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 +# store to local or remote +function store { + # take from stdin and do the magic case $protocol in ('local') dd of=$outfile ;; ('ssh') ssh -p$port $remote_user@$remote_host "dd of=$outfile" ;; @@ -125,32 +137,41 @@ function store # store to local or remote esac } +# self explanatory, using case statement, so no one dash multiple opts supported function parse_opts { while [[ -n $1 ]]; do case $1 in ('--help'|'-h') usage; exit 0;; ('--conf'|'-c') shift; opt_cfg=$1; shift;; - ('') opt_cfg=$default_cfg;; (*) err "unknown parameter $1"; exit 127;; esac done } +# the main logic function main { + # parse options parse_opts $@ + # set default config if $opt_cfg is not defined via an option cfg=${opt_cfg:-$default_cfg} + # run config tests and fill in defaults apply_config + # fail in case something goes wrong local apply_config_returns=$? [[ $apply_config_returns -ne 0 ]] && return $apply_config_returns + # run backups per directory for i in $source_dirs; do + # prepare the set of variables unset src_basename src_basedir outfile IFS=':' read source_dir snapshot_file <<< $i src_basename=${source_dir:t} src_basedir=${source_dir:h} + # generate the backups path generate_fullpath - echo "Creating a backup of $source_dir. Using protocol $protocol to store it in $outfile." + err "Creating a backup of $source_dir via $protocol to store it in $outfile." + # pipe magic into magic compress | store done return 0 From 175c7979f9d5d8ac91e61ce4f82f747f9b8d2531 Mon Sep 17 00:00:00 2001 From: Von Random Date: Mon, 12 Jan 2015 19:50:23 +0300 Subject: [PATCH 05/10] fixes, rolling out into production --- backup.zsh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/backup.zsh b/backup.zsh index 9b912be..ea3847f 100755 --- a/backup.zsh +++ b/backup.zsh @@ -72,8 +72,8 @@ function apply_config fi # set defaults and / or fail to run if something is missing case $protocol in - ('ftp'|'ftps') port=${remote_port:-$default_ftp_port}; test_remote_settings; return $?;; - ('sftp'|'ssh') port=${remote_port:-$default_ssh_port}; test_remote_settings; return $?;; + ('ftp'|'ftps') port=${remote_port:-$default_ftp_port}; test_remote_settings; exit_code=$?; [[ $exit_code -ne 0 ]] && return $exit_code;; + ('sftp'|'ssh') port=${remote_port:-$default_ssh_port}; test_remote_settings; exit_code=$?; [[ $exit_code -ne 0 ]] && return $exit_code;; ('local') unset remote_port;; (*) cfg_err 'protocol'; return 5;; esac @@ -82,7 +82,6 @@ function apply_config ('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."; return 5;; esac if [[ -n $exclude_list ]]; then @@ -123,7 +122,7 @@ function compress fi fi # do the magic and spit to stdout - tar -c$compress_flag $snapshot_option $snapshot_file $exclude_option $exclude_list --ignore-failed-read -C $src_basedir $src_basename + tar -C $src_basedir -c$compress_flag $snapshot_option $snapshot_file $exclude_option $exclude_list --ignore-failed-read $src_basename } # store to local or remote From b5d0158936bdd7d2664581b7404fe380c3f36791 Mon Sep 17 00:00:00 2001 From: Von Random Date: Tue, 13 Jan 2015 12:53:59 +0300 Subject: [PATCH 06/10] added encryption support and fixed a couple of comments --- backup.cfg | 4 ++++ backup.zsh | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/backup.cfg b/backup.cfg index 982df09..c852c98 100644 --- a/backup.cfg +++ b/backup.cfg @@ -31,6 +31,10 @@ source_dirs=( '/home/user/source1:/var/backup/snapshot.list' ## externally. #backup_filename='somebackup' +## GPG key to encrypt backups, uses name of the private +## key in your keyring +#gnupg_key='keyname' + #### remote options #### ## Remote host. remote_host='hostname.tld' diff --git a/backup.zsh b/backup.zsh index ea3847f..34454af 100755 --- a/backup.zsh +++ b/backup.zsh @@ -1,5 +1,7 @@ #!/usr/bin/env zsh self_name=$0 + +# some hardcoded defaults default_cfg='/etc/backup.zsh.cfg' default_postfix=$(date +%F-%H%M) default_ftp_port='21' @@ -104,7 +106,7 @@ function generate_fullpath backup_type='full' fi if [[ -z $backup_filename ]]; then - outfile="$backup_dir/${local_host}-${src_basename}_${postfix}_${backup_type}.t${compress_format:-'ar'}" + outfile="$backup_dir/${local_host}-${src_basename}_${postfix}_${backup_type}${gnupg_key:+'.gpg.'}.t${compress_format:-'ar'}" else outfile=$backup_filename fi @@ -136,7 +138,13 @@ function store esac } -# self explanatory, using case statement, so no one dash multiple opts supported +# encrypt asymmetrically via gpg +function encrypt +{ + gpg -r $gnupg_key -e - +} + +# self explanatory, using case statement, so one dash multiple opts is not supported function parse_opts { while [[ -n $1 ]]; do @@ -171,7 +179,11 @@ function main generate_fullpath err "Creating a backup of $source_dir via $protocol to store it in $outfile." # pipe magic into magic - compress | store + if [[ -n $gnupg_key ]]; then + compress | encrypt | store + else + compress | store + fi done return 0 } From d7ec2ce02f58676a92c0f7bda66b15bfaeca56a4 Mon Sep 17 00:00:00 2001 From: Von Random Date: Thu, 15 Jan 2015 17:24:29 +0300 Subject: [PATCH 07/10] some refactoring + fixed substitution --- backup.zsh | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/backup.zsh b/backup.zsh index 34454af..074d40d 100755 --- a/backup.zsh +++ b/backup.zsh @@ -48,42 +48,34 @@ function apply_config return 5 fi if [[ -n $port && ! $port =~ ^[0-9]+$ ]]; then - err 'Remote port is not a numeric value.' + err 'remote_port is not a numeric value.' return 5 fi } # import contents of the config (including functions if present) source $cfg || { err "Config file $cfg is unreadable or does not exist"; return 15 } + hostname=${local_host:-$HOST} + postfix=${outfile_postfix:-$default_postfix} # do the tests if [[ -z $source_dirs ]]; then cfg_err 'source_dirs' return 5 fi - if [[ -z $backup_dir && $protocol != 'ssh' ]]; then - cfg_err 'backup_dir' - return 5 - fi - if [[ -z $local_host ]]; then - err 'local_host is not set, using hostname.' - local_host=$HOST - fi - if [[ -z $outfile_postfix ]]; then - postfix=$default_postfix - else - postfix=$outfile_postfix - fi # set defaults and / or fail to run if something is missing + local exit_code case $protocol in ('ftp'|'ftps') port=${remote_port:-$default_ftp_port}; test_remote_settings; exit_code=$?; [[ $exit_code -ne 0 ]] && return $exit_code;; ('sftp'|'ssh') port=${remote_port:-$default_ssh_port}; test_remote_settings; exit_code=$?; [[ $exit_code -ne 0 ]] && return $exit_code;; ('local') unset remote_port;; (*) cfg_err 'protocol'; return 5;; esac + unset exit_code # set variables for tar command case $compress_format in ('xz') compress_flag='J' ;; ('bz2') compress_flag='j' ;; ('gz') compress_flag='z' ;; + ('') unset compress_flag ;; (*) err "$compress_format is not a valid value for the compression format option."; return 5;; esac if [[ -n $exclude_list ]]; then @@ -106,7 +98,7 @@ function generate_fullpath backup_type='full' fi if [[ -z $backup_filename ]]; then - outfile="$backup_dir/${local_host}-${src_basename}_${postfix}_${backup_type}${gnupg_key:+'.gpg.'}.t${compress_format:-'ar'}" + outfile="${backup_dir}${backup_dir:+/}${hostname}-${src_basename}_${postfix}_${backup_type}${gnupg_key:+.gpg.}.t${compress_format:-ar}" else outfile=$backup_filename fi @@ -150,7 +142,7 @@ function parse_opts while [[ -n $1 ]]; do case $1 in ('--help'|'-h') usage; exit 0;; - ('--conf'|'-c') shift; opt_cfg=$1; shift;; + ('--conf'|'-c') shift; opt_cfg=$1; return 0;; (*) err "unknown parameter $1"; exit 127;; esac done @@ -166,8 +158,9 @@ function main # run config tests and fill in defaults apply_config # fail in case something goes wrong - local apply_config_returns=$? - [[ $apply_config_returns -ne 0 ]] && return $apply_config_returns + local exit_code=$? + [[ $exit_code -ne 0 ]] && return $exit_code + unset exit_code # run backups per directory for i in $source_dirs; do # prepare the set of variables From f515160bd643c9e79426c08623b700ac0d22ae22 Mon Sep 17 00:00:00 2001 From: Von Random Date: Wed, 18 Feb 2015 18:59:23 +0300 Subject: [PATCH 08/10] swap some string tests to math tests --- backup.zsh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backup.zsh b/backup.zsh index 074d40d..6022da3 100755 --- a/backup.zsh +++ b/backup.zsh @@ -47,7 +47,7 @@ function apply_config cfg_err 'remote_pass' return 5 fi - if [[ -n $port && ! $port =~ ^[0-9]+$ ]]; then + if ! (( port )); then err 'remote_port is not a numeric value.' return 5 fi @@ -64,8 +64,8 @@ function apply_config # set defaults and / or fail to run if something is missing local exit_code case $protocol in - ('ftp'|'ftps') port=${remote_port:-$default_ftp_port}; test_remote_settings; exit_code=$?; [[ $exit_code -ne 0 ]] && return $exit_code;; - ('sftp'|'ssh') port=${remote_port:-$default_ssh_port}; test_remote_settings; exit_code=$?; [[ $exit_code -ne 0 ]] && return $exit_code;; + ('ftp'|'ftps') port=${remote_port:-$default_ftp_port}; test_remote_settings; exit_code=$?; (( exit_code )) && return $exit_code;; + ('sftp'|'ssh') port=${remote_port:-$default_ssh_port}; test_remote_settings; exit_code=$?; (( exit_code )) && return $exit_code;; ('local') unset remote_port;; (*) cfg_err 'protocol'; return 5;; esac @@ -159,7 +159,7 @@ function main apply_config # fail in case something goes wrong local exit_code=$? - [[ $exit_code -ne 0 ]] && return $exit_code + (( exit_code )) && return $exit_code unset exit_code # run backups per directory for i in $source_dirs; do From 6f2158be7c11313063709f2179e032aae2a880f8 Mon Sep 17 00:00:00 2001 From: Von Random Date: Fri, 20 Feb 2015 17:45:52 +0300 Subject: [PATCH 09/10] replace echo with printf --- backup.zsh | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/backup.zsh b/backup.zsh index 6022da3..99c9a63 100755 --- a/backup.zsh +++ b/backup.zsh @@ -10,23 +10,22 @@ default_ssh_port='22' # echo to stderr function err { - [[ -n $1 ]] && echo $1 >&2 + [[ -n $1 ]] && printf "%s\n" $1 >&2 } # standard configuration error message to stderr function cfg_err { - [[ -n $1 ]] && echo "$1 is not set in configuration, but is required by $self_name to work." >&2 + [[ -n $1 ]] && printf "%s is not set in configuration, but is required by %s to work.\n" $1 $self_name >&2 } # print help function usage { - 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 $default_cfg will be used if invoked without options" + printf "usage: %s [--help|--conf /path/to/config]\n" $self_name + printf " --help -h show this message\n" + printf " --conf -c use config from the specified path\n\n" + printf "Default config path %s will be used if invoked without options\n" $default_cfg } # read the configuration file and spit out some exceptions if stuff is missing From a681b52517e9308010fd433e4352c24bc6d86778 Mon Sep 17 00:00:00 2001 From: Von Random Date: Fri, 20 Feb 2015 21:42:10 +0300 Subject: [PATCH 10/10] rename the config for it to be coherent with the script itself, add the license text and some comment fixes --- backup.zsh | 24 ++++++++++++++++++++++++ backup.cfg => backup.zsh.cfg | 10 ++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) rename backup.cfg => backup.zsh.cfg (82%) diff --git a/backup.zsh b/backup.zsh index 99c9a63..6b13d12 100755 --- a/backup.zsh +++ b/backup.zsh @@ -1,4 +1,28 @@ #!/usr/bin/env zsh +# +# The MIT License (MIT) +# +# Copyright (c) 2014 Von Random +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + self_name=$0 # some hardcoded defaults diff --git a/backup.cfg b/backup.zsh.cfg similarity index 82% rename from backup.cfg rename to backup.zsh.cfg index c852c98..b773677 100644 --- a/backup.cfg +++ b/backup.zsh.cfg @@ -3,12 +3,12 @@ ## Can be ftp, sftp, ftps, ssh or local. protocol='ssh' -## The directory store backups in, locally or remotely. +## The directory to 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' +#exclude_list='/usr/local/etc/backup/excludes.list' ## The compression algorithm for backups. ## Can be gz, bz2, xz or empty (for non-compressed). @@ -32,7 +32,9 @@ source_dirs=( '/home/user/source1:/var/backup/snapshot.list' #backup_filename='somebackup' ## GPG key to encrypt backups, uses name of the private -## key in your keyring +## key in your keyring. It is also entirely possible to +## add the GNUPGHOME environment variable export here in +## order to use the private key from a specific location. #gnupg_key='keyname' #### remote options #### @@ -45,7 +47,7 @@ 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. +## for ssh keys... If I ever decide to make it happen. remote_pass='PassWd' ## Port is optional, the defaults are hardcoded.