diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..875f889 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +language: bash + +# Use container-based infrastructure for quicker build start-up +sudo: false + +addons: + apt: + sources: + - debian-sid + packages: + - shellcheck + +script: + - shellcheck -e SC2153 duplicity-backup.sh + - shellcheck -e SC2034 duplicity-backup.conf.example + +matrix: + fast_finish: true diff --git a/duplicity-backup.conf.example b/duplicity-backup.conf.example index 763a818..b48dd08 100644 --- a/duplicity-backup.conf.example +++ b/duplicity-backup.conf.example @@ -354,7 +354,7 @@ CLEAN_UP_VARIABLE="4" # just makes it easier for me to read them and delete them as needed. LOGDIR="/home/foobar_user_name/logs/test2/" -LOG_FILE="duplicity-`date +%Y-%m-%d_%H-%M`.txt" +LOG_FILE="duplicity-$(date +%Y-%m-%d_%H-%M).txt" LOG_FILE_OWNER="foobar_user_name:foobar_user_name" # Note that if the configured LOGDIR does not exist it will be created diff --git a/duplicity-backup.sh b/duplicity-backup.sh index 94e16db..cc28ebb 100755 --- a/duplicity-backup.sh +++ b/duplicity-backup.sh @@ -44,7 +44,7 @@ CONFIG="duplicity-backup.conf" usage(){ echo "USAGE: - `basename $0` [options] + $(basename "$0") [options] Options: -c, --config CONFIG_FILE specify the config file to use @@ -73,8 +73,8 @@ echo "USAGE: CURRENT SCRIPT VARIABLES: ======================== DEST (backup destination) = ${DEST} - INCLIST (directories included) = ${INCLIST[@]:0} - EXCLIST (directories excluded) = ${EXCLIST[@]:0} + INCLIST (directories included) = ${INCLIST[*]:0} + EXCLIST (directories excluded) = ${EXCLIST[*]:0} ROOT (root directory of backup) = ${ROOT} LOGFILE (log file path) = ${LOGFILE} " @@ -88,45 +88,45 @@ while getopts ":c:t:bfvlsnd-:" opt; do # parse long options (a bit tricky because builtin getopts does not # manage long options and I don't want to impose GNU getopt dependancy) -) - case "$OPTARG" in + case "${OPTARG}" in # --restore [restore dest] restore) - COMMAND=$OPTARG + COMMAND=${OPTARG} # We try to find the optional value [restore dest] if [ ! -z "${!OPTIND:0:1}" -a ! "${!OPTIND:0:1}" = "-" ]; then RESTORE_DEST=${!OPTIND} - OPTIND=$(( $OPTIND + 1 )) # we found it, move forward in arg parsing + OPTIND=$(( OPTIND + 1 )) # we found it, move forward in arg parsing fi ;; # --restore-file [file to restore] [restore dest] # --restore-dir [path to restore] [restore dest] restore-file|restore-dir) - COMMAND=$OPTARG + COMMAND=${OPTARG} # We try to find the first optional value [file to restore] if [ ! -z "${!OPTIND:0:1}" -a ! "${!OPTIND:0:1}" = "-" ]; then FILE_TO_RESTORE=${!OPTIND} - OPTIND=$(( $OPTIND + 1 )) # we found it, move forward in arg parsing + OPTIND=$(( OPTIND + 1 )) # we found it, move forward in arg parsing else continue # no value for the restore-file option, skip the rest fi # We try to find the second optional value [restore dest] if [ ! -z "${!OPTIND:0:1}" -a ! "${!OPTIND:0:1}" = "-" ]; then RESTORE_DEST=${!OPTIND} - OPTIND=$(( $OPTIND + 1 )) # we found it, move forward in arg parsing + OPTIND=$(( OPTIND + 1 )) # we found it, move forward in arg parsing fi ;; config) # set the config file from the command line # We try to find the config file if [ ! -z "${!OPTIND:0:1}" -a ! "${!OPTIND:0:1}" = "-" ]; then CONFIG=${!OPTIND} - OPTIND=$(( $OPTIND + 1 )) # we found it, move forward in arg parsing + OPTIND=$(( OPTIND + 1 )) # we found it, move forward in arg parsing fi ;; time) # set the restore time from the command line # We try to find the restore time if [ ! -z "${!OPTIND:0:1}" -a ! "${!OPTIND:0:1}" = "-" ]; then TIME=${!OPTIND} - OPTIND=$(( $OPTIND + 1 )) # we found it, move forward in arg parsing + OPTIND=$(( OPTIND + 1 )) # we found it, move forward in arg parsing fi ;; dry-run) @@ -136,13 +136,13 @@ while getopts ":c:t:bfvlsnd-:" opt; do ECHO=$(which echo) ;; *) - COMMAND=$OPTARG + COMMAND=${OPTARG} ;; esac ;; # here are parsed the short options - c) CONFIG=$OPTARG;; # set the config file from the command line - t) TIME=$OPTARG;; # set the restore time from the command line + c) CONFIG=${OPTARG};; # set the config file from the command line + t) TIME=${OPTARG};; # set the restore time from the command line b) COMMAND="backup";; f) COMMAND="full";; v) COMMAND="verify";; @@ -151,29 +151,29 @@ while getopts ":c:t:bfvlsnd-:" opt; do n) DRY_RUN="--dry-run ";; # dry run d) ECHO=$(which echo);; # debug :) - echo "Option -$OPTARG requires an argument." >&2 + echo "Option -${OPTARG} requires an argument." >&2 COMMAND="" ;; \?) - echo "Invalid option: -$OPTARG" >&2 + echo "Invalid option: -${OPTARG}" >&2 COMMAND="" ;; esac done # Read config file if specified -if [ ! -z "$CONFIG" -a -f "$CONFIG" ]; +if [ ! -z "${CONFIG}" -a -f "${CONFIG}" ]; then - . $CONFIG + . "${CONFIG}" else echo "ERROR: can't find config file! (${CONFIG})" >&2 usage exit 1 fi -STATIC_OPTIONS="$DRY_RUN$STATIC_OPTIONS" +STATIC_OPTIONS="${DRY_RUN}${STATIC_OPTIONS}" -SIGN_PASSPHRASE=$PASSPHRASE +SIGN_PASSPHRASE=${PASSPHRASE} export AWS_ACCESS_KEY_ID export AWS_SECRET_ACCESS_KEY @@ -186,11 +186,11 @@ export SWIFT_AUTHVERSION export PASSPHRASE export SIGN_PASSPHRASE -if [[ -n "$FTP_PASSWORD" ]]; then +if [[ -n "${FTP_PASSWORD}" ]]; then export FTP_PASSWORD fi -if [[ -n "$TMPDIR" ]]; then +if [[ -n "${TMPDIR}" ]]; then export TMPDIR fi @@ -204,24 +204,24 @@ DUPLICITY="$(which duplicity)" # the script is running at a time. LOCKFILE=${LOGDIR}backup.lock -if [ "$ENCRYPTION" = "yes" ]; then - if [ ! -z "$GPG_ENC_KEY" ] && [ ! -z "$GPG_SIGN_KEY" ]; then - if [ "$HIDE_KEY_ID" = "yes" ]; then +if [ "${ENCRYPTION}" = "yes" ]; then + if [ ! -z "${GPG_ENC_KEY}" ] && [ ! -z "${GPG_SIGN_KEY}" ]; then + if [ "${HIDE_KEY_ID}" = "yes" ]; then ENCRYPT="--hidden-encrypt-key=${GPG_ENC_KEY}" - if [ "$COMMAND" != "restore" -a "$COMMAND" != "restore-file" -a "$COMMAND" != "restore-dir" ]; then - ENCRYPT="$ENCRYPT --sign-key=${GPG_SIGN_KEY}" + if [ "${COMMAND}" != "restore" -a "${COMMAND}" != "restore-file" -a "${COMMAND}" != "restore-dir" ]; then + ENCRYPT="${ENCRYPT} --sign-key=${GPG_SIGN_KEY}" fi else ENCRYPT="--encrypt-key=${GPG_ENC_KEY} --sign-key=${GPG_SIGN_KEY}" fi - if [ ! -z "$SECRET_KEYRING" ]; then + if [ ! -z "${SECRET_KEYRING}" ]; then KEYRING="--secret-keyring ${SECRET_KEYRING}" ENCRYPT="${ENCRYPT} --encrypt-secret-keyring=${SECRET_KEYRING}" fi - elif [ ! -z "$PASSPHRASE" ]; then + elif [ ! -z "${PASSPHRASE}" ]; then ENCRYPT="" fi -elif [ "$ENCRYPTION" = "no" ]; then +elif [ "${ENCRYPTION}" = "no" ]; then ENCRYPT="--no-encryption" fi @@ -239,18 +239,18 @@ size information unavailable." README_TXT="In case you've long forgotten, this is a backup script that you used to backup some files (most likely remotely at Amazon S3). In order to restore these files, you first need to import your GPG private(s) key(s) (if you haven't already). The key(s) is/are in this directory and the following command(s) should do the trick:\n\nIf you were using the same key for encryption and signature:\n gpg --allow-secret-key-import --import duplicity-backup-encryption-and-sign-secret.key.txt\nOr if you were using two separate keys for encryption and signature:\n gpg --allow-secret-key-import --import duplicity-backup-encryption-secret.key.txt\n gpg --allow-secret-key-import --import duplicity-backup-sign-secret.key.txt\n\nAfter your key(s) has/have been succesfully imported, you should be able to restore your files.\n\nGood luck!" -if [ ! -x "$DUPLICITY" ]; then +if [ ! -x "${DUPLICITY}" ]; then echo "ERROR: duplicity not installed, that's gotta happen first!" >&2 exit 1 fi -if [ "`echo ${DEST} | cut -c 1,2`" = "gs" ]; then +if [ "$(echo "${DEST}" | cut -c 1,2)" = "gs" ]; then DEST_IS_GS=true GSCMD="$(which gsutil)" - if [ ! -x "$GSCMD" ]; then - echo $NO_GSCMD; GSCMD_AVAIL=false + if [ ! -x "${GSCMD}" ]; then + echo "${NO_GSCMD}"; GSCMD_AVAIL=false elif [ ! -f "${HOME}/.boto" ]; then - echo $NO_GSCMD_CFG; GSCMD_AVAIL=false + echo "${NO_GSCMD_CFG}"; GSCMD_AVAIL=false else GSCMD_AVAIL=true GSCMD="${GSCMD}" @@ -259,23 +259,23 @@ else DEST_IS_GS=false fi -if [ "`echo ${DEST} | cut -c 1,2`" = "s3" ]; then +if [ "$(echo "${DEST}" | cut -c 1,2)" = "s3" ]; then DEST_IS_S3=true S3CMD="$(which s3cmd)" - if [ ! -x "$S3CMD" ]; then - echo $NO_S3CMD; S3CMD_AVAIL=false - elif [ -z "$S3CMD_CONF_FILE" -a ! -f "${HOME}/.s3cfg" ]; then + if [ ! -x "${S3CMD}" ]; then + echo "${NO_S3CMD}"; S3CMD_AVAIL=false + elif [ -z "${S3CMD_CONF_FILE}" -a ! -f "${HOME}/.s3cfg" ]; then S3CMD_CONF_FOUND=false - echo $NO_S3CMD_CFG; S3CMD_AVAIL=false - elif [ ! -z "$S3CMD_CONF_FILE" -a ! -f "$S3CMD_CONF_FILE" ]; then + echo "${NO_S3CMD_CFG}"; S3CMD_AVAIL=false + elif [ ! -z "${S3CMD_CONF_FILE}" -a ! -f "${S3CMD_CONF_FILE}" ]; then S3CMD_CONF_FOUND=false echo "${S3CMD_CONF_FILE} not found, check S3CMD_CONF_FILE variable in duplicity-backup's configuration!"; - echo $NO_S3CMD_CFG; + echo "${NO_S3CMD_CFG}"; S3CMD_AVAIL=false else S3CMD_AVAIL=true S3CMD_CONF_FOUND=true - if [ ! -z "$S3CMD_CONF_FILE" -a -f "$S3CMD_CONF_FILE" ]; then + if [ ! -z "${S3CMD_CONF_FILE}" -a -f "${S3CMD_CONF_FILE}" ]; then # if conf file specified and it exists then add it to the command line for s3cmd S3CMD="${S3CMD} -c ${S3CMD_CONF_FILE}" fi @@ -307,14 +307,14 @@ check_variables () config_sanity_fail "An s3 DEST has been specified, but AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY have not been configured" [[ ( ${DEST_IS_GS} = true && (${GS_ACCESS_KEY_ID} = "foobar_gcs_key_id" || ${GS_SECRET_ACCESS_KEY} = "foobar_gcs_secret_id" )) ]] && \ config_sanity_fail "A Google Cloud Storage DEST has been specified, but GS_ACCESS_KEY_ID or GS_SECRET_ACCESS_KEY have not been configured" - [[ ! -z "$INCEXCFILE" && ! -f $INCEXCFILE ]] && config_sanity_fail "The specified INCEXCFILE $INCEXCFILE does not exists" + [[ ! -z "${INCEXCFILE}" && ! -f ${INCEXCFILE} ]] && config_sanity_fail "The specified INCEXCFILE ${INCEXCFILE} does not exists" } check_logdir() { - if [ ! -d ${LOGDIR} ]; then + if [ ! -d "${LOGDIR}" ]; then echo "Attempting to create log directory ${LOGDIR} ..." - if ! mkdir -p ${LOGDIR}; then + if ! mkdir -p "${LOGDIR}"; then echo "Log directory ${LOGDIR} could not be created by this user: ${USER}" echo "Aborting..." exit 1 @@ -322,14 +322,14 @@ check_logdir() echo "Directory ${LOGDIR} successfully created." fi echo "Attempting to change owner:group of ${LOGDIR} to ${LOG_FILE_OWNER} ..." - if ! chown ${LOG_FILE_OWNER} ${LOGDIR}; then - echo "User ${USER} could not change the owner:group of ${LOGDIR} to $LOG_FILE_OWNER" + if ! chown "${LOG_FILE_OWNER}" "${LOGDIR}"; then + echo "User ${USER} could not change the owner:group of ${LOGDIR} to ${LOG_FILE_OWNER}" echo "Aborting..." exit 1 else echo "Directory ${LOGDIR} successfully changed to owner:group of ${LOG_FILE_OWNER}" fi - elif [ ! -w ${LOGDIR} ]; then + elif [ ! -w "${LOGDIR}" ]; then echo "Log directory ${LOGDIR} is not writeable by this user: ${USER}" echo "Aborting..." exit 1 @@ -338,60 +338,60 @@ check_logdir() email_logfile() { - if [ ! -z "$EMAIL_TO" ]; then - MAILCMD=$(which $MAIL) - if [ ! -x "$MAILCMD" ]; then - echo -e "Email couldn't be sent. ${MAIL} not available." >> ${LOGFILE} + if [ ! -z "${EMAIL_TO}" ]; then + MAILCMD=$(which "${MAIL}") + if [ ! -x "${MAILCMD}" ]; then + echo -e "Email couldn't be sent. ${MAIL} not available." >> "${LOGFILE}" else EMAIL_SUBJECT=${EMAIL_SUBJECT:="duplicity-backup alert ${LOG_FILE}"} - if [ "$MAIL" = "ssmtp" ]; then - echo """Subject: ${EMAIL_SUBJECT}""" | cat - ${LOGFILE} | ${MAILCMD} -s ${EMAIL_TO} - elif [ "$MAIL" = "msmtp" ]; then - echo """Subject: ${EMAIL_SUBJECT}""" | cat - ${LOGFILE} | ${MAILCMD} ${EMAIL_TO} - elif [ "$MAIL" = "mailx" ]; then + if [ "${MAIL}" = "ssmtp" ]; then + echo """Subject: ${EMAIL_SUBJECT}""" | cat - "${LOGFILE}" | ${MAILCMD} -s "${EMAIL_TO}" + elif [ "${MAIL}" = "msmtp" ]; then + echo """Subject: ${EMAIL_SUBJECT}""" | cat - "${LOGFILE}" | ${MAILCMD} "${EMAIL_TO}" + elif [ "${MAIL}" = "mailx" ]; then EMAIL_FROM=${EMAIL_FROM:+"-r ${EMAIL_FROM}"} - cat ${LOGFILE} | ${MAILCMD} -s """${EMAIL_SUBJECT}""" $EMAIL_FROM ${EMAIL_TO} - elif [ "$MAIL" = "mail" ]; then - case `uname` in + ${MAILCMD} -s """${EMAIL_SUBJECT}""" "${EMAIL_FROM}" "${EMAIL_TO}" < "${LOGFILE}" + elif [ "${MAIL}" = "mail" ]; then + case $(uname) in FreeBSD|Darwin|DragonFly|OpenBSD) - cat ${LOGFILE} | ${MAILCMD} -s """${EMAIL_SUBJECT}""" ${EMAIL_TO} -- + ${MAILCMD} -s """${EMAIL_SUBJECT}""" "${EMAIL_TO}" -- < "${LOGFILE}" ;; *) - cat ${LOGFILE} | ${MAILCMD} -s """${EMAIL_SUBJECT}""" $EMAIL_FROM ${EMAIL_TO} -- -f ${EMAIL_FROM} + ${MAILCMD} -s """${EMAIL_SUBJECT}""" "${EMAIL_FROM}" "${EMAIL_TO}" -- -f "${EMAIL_FROM}" < "${LOGFILE}" ;; esac - elif [[ "$MAIL" = "sendmail" ]]; then - (echo """Subject: ${EMAIL_SUBJECT}""" ; cat ${LOGFILE}) | ${MAILCMD} -f ${EMAIL_FROM} ${EMAIL_TO} - elif [ "$MAIL" = "nail" ]; then - cat ${LOGFILE} | ${MAILCMD} -s """${EMAIL_SUBJECT}""" $EMAIL_FROM ${EMAIL_TO} + elif [[ "${MAIL}" = "sendmail" ]]; then + (echo """Subject: ${EMAIL_SUBJECT}""" ; cat "${LOGFILE}") | ${MAILCMD} -f "${EMAIL_FROM}" "${EMAIL_TO}" + elif [ "${MAIL}" = "nail" ]; then + ${MAILCMD} -s """${EMAIL_SUBJECT}""" "${EMAIL_FROM}" "${EMAIL_TO}" < "${LOGFILE}" else - cat ${LOGFILE} | ${MAILCMD} """${EMAIL_SUBJECT}""" ${EMAIL_FROM} ${EMAIL_TO} + ${MAILCMD} """${EMAIL_SUBJECT}""" "${EMAIL_FROM}" "${EMAIL_TO}" < "${LOGFILE}" fi - echo -e "Email alert sent to ${EMAIL_TO} using ${MAIL}" >> ${LOGFILE} + echo -e "Email alert sent to ${EMAIL_TO} using ${MAIL}" >> "${LOGFILE}" fi fi } send_notification() { - if [ ! -z "$NOTIFICATION_SERVICE" ]; then - if [ "$NOTIFICATION_SERVICE" = "slack" ]; then - curl -X POST -H 'Content-type: application/json' --data "{\"text\": \"$NOTIFICATION_CONTENT\", \"channel\": \"$SLACK_CHANNEL\", \"username\": \"$SLACK_USERNAME\", \"icon_emoji\": \":$SLACK_EMOJI:\"}" ${SLACK_HOOK_URL} + if [ ! -z "${NOTIFICATION_SERVICE}" ]; then + if [ "${NOTIFICATION_SERVICE}" = "slack" ]; then + curl -X POST -H 'Content-type: application/json' --data "{\"text\": \"${NOTIFICATION_CONTENT}\", \"channel\": \"${SLACK_CHANNEL}\", \"username\": \"${SLACK_USERNAME}\", \"icon_emoji\": \":${SLACK_EMOJI}:\"}" "${SLACK_HOOK_URL}" fi fi } get_lock() { - echo "Attempting to acquire lock ${LOCKFILE}" >> ${LOGFILE} + echo "Attempting to acquire lock ${LOCKFILE}" >> "${LOGFILE}" if ( set -o noclobber; echo "$$" > "${LOCKFILE}" ) 2> /dev/null; then # The lock succeeded. Create a signal handler to remove the lock file when the process terminates. trap 'EXITCODE=$?; echo "Removing lock. Exit code: ${EXITCODE}" >>${LOGFILE}; rm -f "${LOCKFILE}"' 0 - echo "successfully acquired lock." >> ${LOGFILE} + echo "successfully acquired lock." >> "${LOGFILE}" else # Write lock acquisition errors to log file and stderr - echo "lock failed, could not acquire ${LOCKFILE}" | tee -a ${LOGFILE} >&2 - echo "lock held by $(cat ${LOCKFILE})" | tee -a ${LOGFILE} >&2 + echo "lock failed, could not acquire ${LOCKFILE}" | tee -a "${LOGFILE}" >&2 + echo "lock held by $(cat "${LOCKFILE}")" | tee -a "${LOGFILE}" >&2 email_logfile exit 2 fi @@ -399,19 +399,19 @@ get_lock() get_source_file_size() { - echo "-----------[ Source Disk Use Information ]-----------" >> ${LOGFILE} + echo "-----------[ Source Disk Use Information ]-----------" >> "${LOGFILE}" # Patches to support spaces in paths- # Remove space as a field separator temporarily OLDIFS=$IFS IFS=$(echo -en "\t\n") - case `uname` in + case $(uname) in FreeBSD|Darwin|DragonFly) DUEXCFLAG="-I -" ;; OpenBSD) - echo "WARNING: OpenBSD du does not support exclusion, sizes may be off" >> ${LOGFILE} + echo "WARNING: OpenBSD du does not support exclusion, sizes may be off" >> "${LOGFILE}" DUEXCFLAG="" ;; *) @@ -419,25 +419,25 @@ get_source_file_size() ;; esac - for exclude in ${EXCLIST[@]}; do + for exclude in "${EXCLIST[@]}"; do DUEXCLIST="${DUEXCLIST}${exclude}\n" done # if INCLIST is not set or empty, add ROOT to it to be able to calculate disk usage - if [ -z "$INCLIST" ]; then - DUINCLIST=($ROOT) + if [ -z "${INCLIST}" ]; then + DUINCLIST=(${ROOT}) else DUINCLIST=("${INCLIST[@]}") fi - for include in ${DUINCLIST[@]}; do - echo -e "$DUEXCLIST" | \ - du -hs ${DUEXCFLAG} ${include} | \ + for include in "${DUINCLIST[@]}"; do + echo -e "${DUEXCLIST}" | \ + du -hs ${DUEXCFLAG} "${include}" | \ awk '{ FS="\t"; $0=$0; print $1"\t"$2 }' \ - >> ${LOGFILE} + >> "${LOGFILE}" done - echo >> ${LOGFILE} + echo >> "${LOGFILE}" # Restore IFS IFS=$OLDIFS @@ -445,44 +445,45 @@ get_source_file_size() get_remote_file_size() { - echo "---------[ Destination Disk Use Information ]--------" >> ${LOGFILE} + echo "---------[ Destination Disk Use Information ]--------" >> "${LOGFILE}" FRIENDLY_TYPE_NAME="" - dest_type=`echo ${DEST} | cut -c 1,2` + dest_type=$(echo "${DEST}" | cut -c 1,2) case $dest_type in "ss") FRIENDLY_TYPE_NAME="SSH" TMPDEST="${DEST#*://*/}" TMPDEST="${DEST%/${TMPDEST}}" - ssh_opt=`echo $STATIC_OPTIONS |awk -vo="--ssh-options=" '{s=index($0,o); if (s) {s=substr($0,s+length(o)); m=substr(s,0,1); for (i=2; i < length(s); i++) { if (substr(s,i,1) == m && substr(s,i-1,1) != "\\\\") break; } print substr(s,2,i-2)}}'` + ssh_opt=$(echo "${STATIC_OPTIONS}" |awk -vo="--ssh-options=" '{s=index($0,o); if (s) {s=substr($0,s+length(o)); m=substr(s,0,1); for (i=2; i < length(s); i++) { if (substr(s,i,1) == m && substr(s,i-1,1) != "\\\\") break; } print substr(s,2,i-2)}}') - SIZE=`${TMPDEST%://*} ${ssh_opt} ${TMPDEST#*//} du -hs ${DEST#${TMPDEST}/} | awk '{print $1}'` 2>> ${LOGFILE} - EMAIL_SUBJECT="$EMAIL_SUBJECT $SIZE `${TMPDEST%://*} ${ssh_opt} ${TMPDEST#*//} df -hP ${DEST#${TMPDEST}/} | awk '{tmp=$5 " used"}END{print tmp}'`" 2>> ${LOGFILE} + SIZE=$(${TMPDEST%://*} "${ssh_opt}" "${TMPDEST#*//}" du -hs "${DEST#${TMPDEST}/}" | awk '{print $1}') 2>> "${LOGFILE}" + EMAIL_SUBJECT="${EMAIL_SUBJECT} ${SIZE} $(${TMPDEST%://*} "${ssh_opt}" "${TMPDEST#*//}" df -hP "${DEST#${TMPDEST}/}" | awk '{tmp=$5 " used"}END{print tmp}')" 2>> "${LOGFILE}" ;; "fi") FRIENDLY_TYPE_NAME="File" - TMPDEST=`echo ${DEST} | cut -c 6-` - SIZE=`du -hs ${TMPDEST} | awk '{print $1}'` + TMPDEST=$(echo "${DEST}" | cut -c 6-) + SIZE=$(du -hs "${TMPDEST}" | awk '{print $1}') ;; "gs") FRIENDLY_TYPE_NAME="Google Cloud Storage" - if $GSCMD_AVAIL ; then - TMPDEST=`echo $DEST | sed -e "s/\/*$//" ` - SIZE=`gsutil du -hs ${TMPDEST} | awk '{print $1$2}'` + if ${GSCMD_AVAIL} ; then + #TMPDEST=$(echo "${DEST}" | sed -e "s/\/*$//" ) + TMPDEST=${DEST//\/*$/} + SIZE=$(gsutil du -hs "${TMPDEST}" | awk '{print $1$2}') fi ;; "s3") FRIENDLY_TYPE_NAME="Amazon S3" - if $S3CMD_AVAIL ; then - TMPDEST=$(echo ${DEST} | cut -c 11-) - dest_scheme=$(echo ${DEST} | cut -f -1 -d :) + if ${S3CMD_AVAIL} ; then + TMPDEST=$(echo "${DEST}" | cut -c 11-) + dest_scheme=$(echo "${DEST}" | cut -f -1 -d :) if [ "$dest_scheme" = "s3" ]; then # Strip off the host name, too. - TMPDEST=`echo $TMPDEST | cut -f 2- -d /` + TMPDEST=$(echo "${TMPDEST}" | cut -f 2- -d /) fi - SIZE=`${S3CMD} du -H s3://${TMPDEST} | awk '{print $1}'` + SIZE=$(${S3CMD} du -H s3://"${TMPDEST}" | awk '{print $1}') else - if ! $S3CMD_CONF_FOUND ; then + if ! ${S3CMD_CONF_FOUND} ; then SIZE="-s3cmd config not found-" else SIZE="-s3cmd not found in PATH-" @@ -495,13 +496,13 @@ get_remote_file_size() ;; esac - if [[ $FRIENDLY_TYPE_NAME ]] ; then - echo -e ""$SIZE"\t"$FRIENDLY_TYPE_NAME" type backend" >> ${LOGFILE} + if [[ ${FRIENDLY_TYPE_NAME} ]] ; then + echo -e "${SIZE}\t${FRIENDLY_TYPE_NAME} type backend" >> "${LOGFILE}" else - echo "Destination disk use information is currently only available for the following storage backends:" >> ${LOGFILE} - echo "File, SSH, Amazon S3 and Google Cloud" >> ${LOGFILE} + echo "Destination disk use information is currently only available for the following storage backends:" >> "${LOGFILE}" + echo "File, SSH, Amazon S3 and Google Cloud" >> "${LOGFILE}" fi - echo >> ${LOGFILE} + echo >> "${LOGFILE}" } include_exclude() @@ -512,31 +513,31 @@ include_exclude() IFS=$(echo -en "\t\n") # Exlcude device files? - if [ ! -z $EXDEVICEFILES ] && [ $EXDEVICEFILES -ne 0 ]; then + if [ ! -z "${EXDEVICEFILES}" ] && [ "${EXDEVICEFILES}" -ne 0 ]; then TMP=" --exclude-device-files" - EXCLUDE=$EXCLUDE$TMP + EXCLUDE=${EXCLUDE}${TMP} fi - for include in ${INCLIST[@]} + for include in "${INCLIST[@]}" do - TMP=" --include=""'"$include"'" - INCLUDE=$INCLUDE$TMP + TMP=" --include='$include'" + INCLUDE=${INCLUDE}${TMP} done - for exclude in ${EXCLIST[@]} + for exclude in "${EXCLIST[@]}" do - TMP=" --exclude ""'"$exclude"'" - EXCLUDE=$EXCLUDE$TMP + TMP=" --exclude '$exclude'" + EXCLUDE=${EXCLUDE}${TMP} done # Include/Exclude globbing filelist - if [ "$INCEXCFILE" != '' ]; then - TMP=" --include-globbing-filelist ""'"$INCEXCFILE"'" - INCLUDE=$INCLUDE$TMP + if [ "${INCEXCFILE}" != '' ]; then + TMP=" --include-globbing-filelist '${INCEXCFILE}'" + INCLUDE=${INCLUDE}${TMP} fi # INCLIST and globbing filelist is empty so every file needs to be saved - if [ "$INCLIST" == '' ] && [ "$INCEXCFILE" == '' ]; then + if [ "${INCLIST}" == '' ] && [ "${INCEXCFILE}" == '' ]; then EXCLUDEROOT='' else EXCLUDEROOT="--exclude=**" @@ -549,41 +550,41 @@ include_exclude() duplicity_cleanup() { - echo "----------------[ Duplicity Cleanup ]----------------" >> ${LOGFILE} + echo "----------------[ Duplicity Cleanup ]----------------" >> "${LOGFILE}" if [[ "${CLEAN_UP_TYPE}" != "none" && ! -z ${CLEAN_UP_TYPE} && ! -z ${CLEAN_UP_VARIABLE} ]]; then { - eval ${ECHO} ${DUPLICITY} ${CLEAN_UP_TYPE} ${CLEAN_UP_VARIABLE} ${STATIC_OPTIONS} --force \ - ${ENCRYPT} \ - ${DEST} >> ${LOGFILE} + eval "${ECHO}" "${DUPLICITY}" "${CLEAN_UP_TYPE}" "${CLEAN_UP_VARIABLE}" "${STATIC_OPTIONS}" --force \ + "${ENCRYPT}" \ + "${DEST}" >> "${LOGFILE}" } || { BACKUP_ERROR=1 } - echo >> ${LOGFILE} + echo >> "${LOGFILE}" fi - if [ ! -z ${REMOVE_INCREMENTALS_OLDER_THAN} ] && [[ ${REMOVE_INCREMENTALS_OLDER_THAN} =~ ^[0-9]+$ ]]; then + if [ ! -z "${REMOVE_INCREMENTALS_OLDER_THAN}" ] && [[ ${REMOVE_INCREMENTALS_OLDER_THAN} =~ ^[0-9]+$ ]]; then { - eval ${ECHO} ${DUPLICITY} remove-all-inc-of-but-n-full ${REMOVE_INCREMENTALS_OLDER_THAN} \ - ${STATIC_OPTIONS} --force \ - ${ENCRYPT} \ - ${DEST} >> ${LOGFILE} + eval "${ECHO}" "${DUPLICITY}" remove-all-inc-of-but-n-full "${REMOVE_INCREMENTALS_OLDER_THAN}" \ + "${STATIC_OPTIONS}" --force \ + "${ENCRYPT}" \ + "${DEST}" >> "${LOGFILE}" } || { BACKUP_ERROR=1 } - echo >> ${LOGFILE} + echo >> "${LOGFILE}" fi } duplicity_backup() { { - eval ${ECHO} ${DUPLICITY} ${OPTION} ${VERBOSITY} ${STATIC_OPTIONS} \ - ${STORAGECLASS} \ - ${ENCRYPT} \ - ${EXCLUDE} \ - ${INCLUDE} \ - ${EXCLUDEROOT} \ - ${ROOT} ${DEST} \ - >> ${LOGFILE} + eval "${ECHO}" "${DUPLICITY}" "${OPTION}" "${VERBOSITY}" "${STATIC_OPTIONS}" \ + "${STORAGECLASS}" \ + "${ENCRYPT}" \ + "${EXCLUDE}" \ + "${INCLUDE}" \ + "${EXCLUDEROOT}" \ + "${ROOT}" "${DEST}" \ + >> "${LOGFILE}" } || { BACKUP_ERROR=1 } @@ -591,11 +592,11 @@ duplicity_backup() setup_passphrase() { - if [ ! -z "$GPG_ENC_KEY" -a ! -z "$GPG_SIGN_KEY" -a "$GPG_ENC_KEY" != "$GPG_SIGN_KEY" ]; then + if [ ! -z "${GPG_ENC_KEY}" -a ! -z "${GPG_SIGN_KEY}" -a "${GPG_ENC_KEY}" != "${GPG_SIGN_KEY}" ]; then echo -n "Please provide the passphrase for decryption (GPG key 0x${GPG_ENC_KEY}): " builtin read -s ENCPASSPHRASE echo -ne "\n" - PASSPHRASE=$ENCPASSPHRASE + PASSPHRASE=${ENCPASSPHRASE} export PASSPHRASE fi } @@ -605,42 +606,42 @@ get_file_sizes() get_source_file_size get_remote_file_size - case `uname` in + case $(uname) in FreeBSD|Darwin|DragonFly) - sed -i '' -e '/^--*$/d' ${LOGFILE} + sed -i '' -e '/^--*$/d' "${LOGFILE}" ;; OpenBSD) - ed -s ${LOGFILE} <<-"EOF" + ed -s "${LOGFILE}" <<-"EOF" g/^--*$/d w q EOF ;; *) - sed -i -e '/^--*$/d' ${LOGFILE} + sed -i -e '/^--*$/d' "${LOGFILE}" ;; esac - [[ -n "${LOG_FILE_OWNER}" ]] && chown ${LOG_FILE_OWNER} ${LOGFILE} + [[ -n "${LOG_FILE_OWNER}" ]] && chown "${LOG_FILE_OWNER}" "${LOGFILE}" } backup_this_script() { - if [ `echo ${0} | cut -c 1` = "." ]; then - SCRIPTFILE=$(echo ${0} | cut -c 2-) + if [ "$(echo "${0}" | cut -c 1)" = "." ]; then + SCRIPTFILE=$(echo "${0}" | cut -c 2-) SCRIPTPATH=$(pwd)${SCRIPTFILE} else - SCRIPTPATH=$(which ${0}) + SCRIPTPATH=$(which "${0}") fi - TMPDIR=duplicity-backup-`date +%Y-%m-%d` + TMPDIR=duplicity-backup-$(date +%Y-%m-%d) TMPFILENAME=${TMPDIR}.tar.gpg README=${TMPDIR}/README echo "You are backing up: " echo " 1. ${SCRIPTPATH}" - if [ ! -z "$GPG_ENC_KEY" -a ! -z "$GPG_SIGN_KEY" ]; then - if [ "$GPG_ENC_KEY" = "$GPG_SIGN_KEY" ]; then + if [ ! -z "${GPG_ENC_KEY}" -a ! -z "${GPG_SIGN_KEY}" ]; then + if [ "${GPG_ENC_KEY}" = "${GPG_SIGN_KEY}" ]; then echo " 2. GPG Secret encryption and sign key: ${GPG_ENC_KEY}" else echo " 2. GPG Secret encryption key: ${GPG_ENC_KEY} and GPG secret sign key: ${GPG_SIGN_KEY}" @@ -649,52 +650,53 @@ backup_this_script() echo " 2. GPG Secret encryption and sign key: none (symmetric encryption)" fi - if [ ! -z "$CONFIG" -a -f "$CONFIG" ]; + if [ ! -z "${CONFIG}" -a -f "${CONFIG}" ]; then echo " 3. Config file: ${CONFIG}" fi - if [ ! -z "$INCEXCFILE" -a -f "$INCEXCFILE" ]; + if [ ! -z "${INCEXCFILE}" -a -f "${INCEXCFILE}" ]; then echo " 4. Include/Exclude globbing file: ${INCEXCFILE}" fi - echo "Backup tarball will be encrypted and saved to: `pwd`/${TMPFILENAME}" + echo "Backup tarball will be encrypted and saved to: $(pwd)/${TMPFILENAME}" echo echo ">> Are you sure you want to do that ('yes' to continue)?" read ANSWER - if [ "$ANSWER" != "yes" ]; then + if [ "${ANSWER}" != "yes" ]; then echo "You said << ${ANSWER} >> so I am exiting now." exit 1 fi - mkdir -p ${TMPDIR} - cp $SCRIPTPATH ${TMPDIR}/ + mkdir -p "${TMPDIR}" + cp "${SCRIPTPATH}" "${TMPDIR}"/ - if [ ! -z "$CONFIG" -a -f "$CONFIG" ]; + if [ ! -z "${CONFIG}" -a -f "${CONFIG}" ]; then - cp $CONFIG ${TMPDIR}/ + cp "${CONFIG}" "${TMPDIR}"/ fi - if [ ! -z "$INCEXCFILE" -a -f "$INCEXCFILE" ]; + if [ ! -z "${INCEXCFILE}" -a -f "${INCEXCFILE}" ]; then - cp $INCEXCFILE ${TMPDIR}/ + cp "${INCEXCFILE}" "${TMPDIR}"/ fi - if [ ! -z "$GPG_ENC_KEY" -a ! -z "$GPG_SIGN_KEY" ]; then - export GPG_TTY=`tty` - if [ "$GPG_ENC_KEY" = "$GPG_SIGN_KEY" ]; then - gpg -a --export-secret-keys ${KEYRING} ${GPG_ENC_KEY} > ${TMPDIR}/duplicity-backup-encryption-and-sign-secret.key.txt + if [ ! -z "${GPG_ENC_KEY}" -a ! -z "${GPG_SIGN_KEY}" ]; then + GPG_TTY=$(tty) + export GPG_TTY + if [ "${GPG_ENC_KEY}" = "${GPG_SIGN_KEY}" ]; then + gpg -a --export-secret-keys "${KEYRING}" "${GPG_ENC_KEY}" > "${TMPDIR}"/duplicity-backup-encryption-and-sign-secret.key.txt else - gpg -a --export-secret-keys ${KEYRING} ${GPG_ENC_KEY} > ${TMPDIR}/duplicity-backup-encryption-secret.key.txt - gpg -a --export-secret-keys ${KEYRING} ${GPG_SIGN_KEY} > ${TMPDIR}/duplicity-backup-sign-secret.key.txt + gpg -a --export-secret-keys "${KEYRING}" "${GPG_ENC_KEY}" > "${TMPDIR}"/duplicity-backup-encryption-secret.key.txt + gpg -a --export-secret-keys "${KEYRING}" "${GPG_SIGN_KEY}" > "${TMPDIR}"/duplicity-backup-sign-secret.key.txt fi fi - echo -e ${README_TXT} > ${README} + echo -e "${README_TXT}" > "${README}" echo "Encrypting tarball, choose a password you'll remember..." - tar -cf - ${TMPDIR} | gpg -aco ${TMPFILENAME} - rm -Rf ${TMPDIR} + tar -cf - "${TMPDIR}" | gpg -aco "${TMPFILENAME}" + rm -Rf "${TMPDIR}" echo -e "\nIMPORTANT!!" echo ">> To restore these files, run the following (remember your password):" echo "gpg -d ${TMPFILENAME} | tar -xf -" @@ -704,7 +706,7 @@ backup_this_script() check_variables check_logdir -echo -e "-------- START DUPLICITY-BACKUP SCRIPT for ${HOSTNAME} --------\n" >> ${LOGFILE} +echo -e "-------- START DUPLICITY-BACKUP SCRIPT for ${HOSTNAME} --------\n" >> "${LOGFILE}" get_lock @@ -712,7 +714,7 @@ INCLUDE= EXCLUDE= EXCLUDEROOT= -case "$COMMAND" in +case "${COMMAND}" in "backup-script") backup_this_script exit @@ -732,7 +734,7 @@ case "$COMMAND" in DEST=${OLDROOT} OPTION="verify" - echo -e "-------[ Verifying Source & Destination ]-------\n" >> ${LOGFILE} + echo -e "-------[ Verifying Source & Destination ]-------\n" >> "${LOGFILE}" include_exclude setup_passphrase echo -e "Attempting to verify now ..." @@ -748,26 +750,26 @@ case "$COMMAND" in ;; "restore") - ROOT=$DEST + ROOT=${DEST} OPTION="restore" - if [ ! -z "$TIME" ]; then - STATIC_OPTIONS="$STATIC_OPTIONS --time $TIME" + if [ ! -z "${TIME}" ]; then + STATIC_OPTIONS="${STATIC_OPTIONS} --time ${TIME}" fi - if [[ ! "$RESTORE_DEST" ]]; then + if [[ ! "${RESTORE_DEST}" ]]; then echo "Please provide a destination path (eg, /home/user/dir):" read -e NEWDESTINATION - DEST=$NEWDESTINATION + DEST=${NEWDESTINATION} echo ">> You will restore from ${ROOT} to ${DEST}" echo "Are you sure you want to do that ('yes' to continue)?" read ANSWER - if [[ "$ANSWER" != "yes" ]]; then + if [[ "${ANSWER}" != "yes" ]]; then echo "You said << ${ANSWER} >> so I am exiting now." - echo -e "User aborted restore process ...\n" >> ${LOGFILE} + echo -e "User aborted restore process ...\n" >> "${LOGFILE}" exit 1 fi else - DEST=$RESTORE_DEST + DEST=${RESTORE_DEST} fi setup_passphrase @@ -776,39 +778,39 @@ case "$COMMAND" in ;; "restore-file"|"restore-dir") - ROOT=$DEST + ROOT=${DEST} OPTION="restore" - if [ ! -z "$TIME" ]; then - STATIC_OPTIONS="$STATIC_OPTIONS --time $TIME" + if [ ! -z "${TIME}" ]; then + STATIC_OPTIONS="${STATIC_OPTIONS} --time ${TIME}" fi - if [[ ! "$FILE_TO_RESTORE" ]]; then + if [[ ! "${FILE_TO_RESTORE}" ]]; then echo "Which file or directory do you want to restore?" echo "(give the path relative to the root of the backup eg, mail/letter.txt):" read -e FILE_TO_RESTORE echo fi - if [[ "$RESTORE_DEST" ]]; then - DEST=$RESTORE_DEST + if [[ "${RESTORE_DEST}" ]]; then + DEST=${RESTORE_DEST} else - DEST=$(basename $FILE_TO_RESTORE) + DEST=$(basename "${FILE_TO_RESTORE}") fi echo -e "YOU ARE ABOUT TO..." - echo -e ">> RESTORE: $FILE_TO_RESTORE" + echo -e ">> RESTORE: ${FILE_TO_RESTORE}" echo -e ">> TO: ${DEST}" echo -e "\nAre you sure you want to do that ('yes' to continue)?" read ANSWER - if [ "$ANSWER" != "yes" ]; then + if [ "${ANSWER}" != "yes" ]; then echo "You said << ${ANSWER} >> so I am exiting now." - echo -e "--------------------- END ---------------------\n" >> ${LOGFILE} + echo -e "--------------------- END ---------------------\n" >> "${LOGFILE}" exit 1 fi - FILE_TO_RESTORE="'"$FILE_TO_RESTORE"'" - DEST="'"$DEST"'" + FILE_TO_RESTORE="'${FILE_TO_RESTORE}'" + DEST="'${DEST}'" setup_passphrase echo "Restoring now ..." @@ -820,25 +822,25 @@ case "$COMMAND" in "list-current-files") OPTION="list-current-files" - if [ ! -z "$TIME" ]; then - STATIC_OPTIONS="$STATIC_OPTIONS --time $TIME" + if [ ! -z "${TIME}" ]; then + STATIC_OPTIONS="${STATIC_OPTIONS} --time ${TIME}" fi eval \ - ${DUPLICITY} ${OPTION} ${VERBOSITY} ${STATIC_OPTIONS} \ - $ENCRYPT \ - ${DEST} | tee -a ${LOGFILE} - echo -e "--------------------- END ---------------------\n" >> ${LOGFILE} + "${DUPLICITY}" "${OPTION}" "${VERBOSITY}" "${STATIC_OPTIONS}" \ + ${ENCRYPT} \ + "${DEST}" | tee -a "${LOGFILE}" + echo -e "--------------------- END ---------------------\n" >> "${LOGFILE}" ;; "collection-status") OPTION="collection-status" eval \ - ${DUPLICITY} ${OPTION} ${VERBOSITY} ${STATIC_OPTIONS} \ - $ENCRYPT \ - ${DEST} | tee -a ${LOGFILE} - echo -e "--------------------- END ---------------------\n" >> ${LOGFILE} + "${DUPLICITY}" "${OPTION}" "${VERBOSITY}" "${STATIC_OPTIONS}" \ + ${ENCRYPT} \ + "${DEST}" | tee -a "${LOGFILE}" + echo -e "--------------------- END ---------------------\n" >> "${LOGFILE}" ;; "backup") @@ -849,14 +851,14 @@ case "$COMMAND" in ;; *) - echo -e "[Only show `basename $0` usage options]\n" >> ${LOGFILE} + echo -e "[Only show $(basename "$0") usage options]\n" >> "${LOGFILE}" usage ;; esac -echo -e "--------- END DUPLICITY-BACKUP SCRIPT ---------\n" >> ${LOGFILE} +echo -e "--------- END DUPLICITY-BACKUP SCRIPT ---------\n" >> "${LOGFILE}" -if [ "$EMAIL_FAILURE_ONLY" = "yes" ]; then +if [ "${EMAIL_FAILURE_ONLY}" = "yes" ]; then if [ ${BACKUP_ERROR} ]; then EMAIL_SUBJECT="BACKUP ERROR: ${EMAIL_SUBJECT}" email_logfile @@ -870,25 +872,25 @@ else email_logfile fi -if [ "$NOTIFICATION_FAILURE_ONLY" = "yes" ]; then +if [ "${NOTIFICATION_FAILURE_ONLY}" = "yes" ]; then if [ ${BACKUP_ERROR} ]; then - NOTIFICATION_CONTENT="BACKUP ERROR: ${HOSTNAME} - \`$LOGFILE\`" + NOTIFICATION_CONTENT="BACKUP ERROR: ${HOSTNAME} - \`${LOGFILE}\`" send_notification fi else if [ ${BACKUP_ERROR} ]; then - NOTIFICATION_CONTENT="BACKUP ERROR: ${HOSTNAME} - \`$LOGFILE\`" + NOTIFICATION_CONTENT="BACKUP ERROR: ${HOSTNAME} - \`${LOGFILE}\`" else - NOTIFICATION_CONTENT="BACKUP OK: ${HOSTNAME} - \`$LOGFILE\`" + NOTIFICATION_CONTENT="BACKUP OK: ${HOSTNAME} - \`${LOGFILE}\`" fi send_notification fi # remove old logfiles # stops them from piling up infinitely -[[ -n "${REMOVE_LOGS_OLDER_THAN}" ]] && find ${LOGDIR} -type f -mtime +"${REMOVE_LOGS_OLDER_THAN}" -delete +[[ -n "${REMOVE_LOGS_OLDER_THAN}" ]] && find "${LOGDIR}" -type f -mtime +"${REMOVE_LOGS_OLDER_THAN}" -delete -if [ ${ECHO} ]; then +if [ "${ECHO}" ]; then echo "TEST RUN ONLY: Check the logfile for command output." fi