635 lines
22 KiB
Bash
Executable File
635 lines
22 KiB
Bash
Executable File
#!/bin/sh
|
||
#
|
||
# Purpose {{{
|
||
# This script will …
|
||
# 1. List all users directories with a minimum size.
|
||
# 2. For each, get the list of backup directories.
|
||
# 3. For each backup directories.
|
||
# a. Ignore those that contain a .ignore.me hidden file.
|
||
# b. Get current backup directory size.
|
||
# c. Compare current size with a previous value (if available) from db.
|
||
# d. Increase informations iterations for this backup (N-1 become N-2,…).
|
||
# e. Store current backup dir size to N-1.
|
||
# f. Prepare email's body if difference is not significant.
|
||
# 4. Send email to user if a content exists.
|
||
# 5. Remove all backup informations with iterations ≤ -10.
|
||
# …
|
||
#
|
||
# 2022-12-07
|
||
# }}}
|
||
# Sqlite3 database info {{{
|
||
# Table name : SQLITE_TABLE_NAME (default : "backups_info").
|
||
# 6 fields :
|
||
# name TEXT (eg. username/duplicati_mr013370)
|
||
# user TEXT (eg. username)
|
||
# size INT (eg. 16000)
|
||
# iteration INT (eg. -2)
|
||
# date TEXT (eg. 2022-12-05)
|
||
# emailed TEXT (eg True)
|
||
#
|
||
# A primary key is built from those 6 fields to avoid duplicate entries.
|
||
#
|
||
# The script will try to get the last iteration of a backup (iteration = -1) for
|
||
# a comparison with the current backup directory size.
|
||
# The script will decrease all iterations number of a backup (iteration - 1).
|
||
# The script will finally remove all entries with an iteration below -10.
|
||
# }}}
|
||
# Vars {{{
|
||
PROGNAME=$(basename "${0}"); readonly PROGNAME
|
||
PROGDIR=$(readlink -m $(dirname "${0}")); readonly PROGDIR
|
||
ARGS="${*}"; readonly ARGS
|
||
readonly NBARGS="${#}"
|
||
[ -z "${DEBUG}" ] && DEBUG=1
|
||
## Export DEBUG for sub-script
|
||
export DEBUG
|
||
|
||
## Default values for some vars
|
||
readonly DUP_DIR_DEFAULT="/home"
|
||
readonly SQLITE_TABLE_NAME="backups_info"
|
||
## Temp files
|
||
DUP_TMP_DIR=$(mktemp -d -t duplicati-monitor-backup-XXXXXX.tmp) ; readonly DUP_TMP_DIR
|
||
readonly DUP_USERS_DIR_LIST="${DUP_TMP_DIR}/users.dir.list"
|
||
## Minimum size for a user|backup directory (for du comparison)
|
||
readonly MIN_SIZE_INT_DEFAULT="10"
|
||
readonly MIN_SIZE_UNIT_DEFAULT="MB"
|
||
readonly MIN_SIZE_DEFAULT="${MIN_SIZE_INT_DEFAULT}${MIN_SIZE_UNIT_DEFAULT}"
|
||
## Default domain for email addresses
|
||
USER_EMAIL_DOMAIN_DEFAULT=$(hostname -d) ; readonly USER_EMAIL_DOMAIN_DEFAULT
|
||
SENDER_EMAIL_ADDRESS_DEFAULT="duplicati@$(hostname --fqdn --)" ; readonly SENDER_EMAIL_ADDRESS_DEFAULT
|
||
|
||
## Colors
|
||
readonly PURPLE='\033[1;35m'
|
||
readonly RED='\033[0;31m'
|
||
readonly RESET='\033[0m'
|
||
readonly COLOR_DEBUG="${PURPLE}"
|
||
# }}}
|
||
usage() { # {{{
|
||
|
||
cat <<- HELP
|
||
usage: $PROGNAME [-d|-h|-m|-s]
|
||
|
||
Monitor users backups for a Duplicati backend.
|
||
Store some informations in a sqlite3 db file.
|
||
And send an email if the size between two backups is not significant.
|
||
|
||
EXAMPLES :
|
||
- Apply to default directory (${DUP_DIR_DEFAULT})
|
||
${PROGNAME}
|
||
|
||
- Apply to a specific directory (/mnt/remote.duplicati)
|
||
${PROGNAME} --dir /mnt/remote.duplicati
|
||
|
||
- Use a specific email address for sender
|
||
${PROGNAME} --sender "admin@domain.tld"
|
||
|
||
- Use a specific email address and name for sender
|
||
${PROGNAME} --sender "Duplicati no-reply <admin@domain.tld>"
|
||
|
||
OPTIONS :
|
||
-d,--dir,--directory
|
||
Path where users home directory are stored
|
||
(default: ${DUP_DIR_DEFAULT}).
|
||
|
||
--db,--sqlite3
|
||
Path to sqlite3 database file to store backups informations
|
||
(default relative to --directory: ${DUP_DIR_DEFAULT}/.duplicati/monitor.db).
|
||
|
||
--domain,--mail-domain
|
||
Domain used to build user's email address.
|
||
(default: ${USER_EMAIL_DOMAIN_DEFAULT}).
|
||
|
||
--sender,--mail-sender,--sender-mail
|
||
Email address used to send mails to users.
|
||
(default: ${SENDER_EMAIL_ADDRESS_DEFAULT}).
|
||
|
||
--debug
|
||
Enable debug messages.
|
||
|
||
-h,--help
|
||
Print this help message.
|
||
|
||
-m,--mail,--mail-template,--template
|
||
Template used to build email content for users.
|
||
(default relative to --directory: ${DUP_DIR_DEFAULT}/.duplicati/mail.template).
|
||
|
||
-s,--size,--min-size
|
||
Minimal size for a user directory.
|
||
This value is also used to compare backup directory size between two runs.
|
||
Expect to have both an integer AND a unit. If one is missing, the default
|
||
one will be used.
|
||
(default: ${MIN_SIZE_DEFAULT}).
|
||
HELP
|
||
}
|
||
# }}}
|
||
debug_message() { # {{{
|
||
|
||
local_debug_message="${1}"
|
||
|
||
## Print message if DEBUG is enable (=0)
|
||
[ "${DEBUG}" -eq "0" ] && printf '\e[1;35m%-6b\e[m\n' "DEBUG − ${PROGNAME} : ${local_debug_message}"
|
||
|
||
unset local_debug_message
|
||
|
||
return 0
|
||
}
|
||
# }}}
|
||
error_message() { # {{{
|
||
|
||
local_error_message="${1}"
|
||
local_error_code="${2}"
|
||
|
||
## Print message
|
||
printf '%b\n' "ERROR − ${PROGNAME} : ${RED}${local_error_message}${RESET}"
|
||
|
||
unset local_error_message
|
||
|
||
exit "${local_error_code:=66}"
|
||
}
|
||
# }}}
|
||
define_vars() { # {{{
|
||
|
||
## If dup_dir wasn't defined (argument) {{{
|
||
if [ -z "${dup_dir}" ]; then
|
||
## Use default value
|
||
readonly dup_dir="${DUP_DIR_DEFAULT}"
|
||
fi
|
||
## }}}
|
||
## If dup_info wasn't defined {{{
|
||
if [ -z "${dup_info}" ]; then
|
||
## Store it in previous define dup_dir
|
||
readonly dup_info="${dup_dir}/.duplicati/monitor_backup"
|
||
fi
|
||
## }}}
|
||
## If mail_template wasn't defined {{{
|
||
if [ -z "${mail_template}" ]; then
|
||
## Store it in previous define dup_dir
|
||
readonly mail_template="${dup_info}/mail.template"
|
||
fi
|
||
## }}}
|
||
## If mail_template_bottom wasn't defined {{{
|
||
if [ -z "${mail_template_bottom}" ]; then
|
||
## Store it in previous define dup_dir
|
||
readonly mail_template_bottom="${mail_template}.bottom"
|
||
fi
|
||
## }}}
|
||
## If db_path wasn't defined {{{
|
||
if [ -z "${db_path}" ]; then
|
||
## Store it in previous dup_info directory
|
||
readonly db_path="${dup_info}/monitor.db"
|
||
fi
|
||
## }}}
|
||
## If min_size wasn't defined (argument) {{{
|
||
if [ -z "${min_size}" ]; then
|
||
## Use default value
|
||
readonly min_size="${MIN_SIZE_DEFAULT}"
|
||
fi
|
||
## }}}
|
||
## Define min_size_int {{{
|
||
min_size_int=$(printf -- '%s' "${min_size}" \
|
||
| tr --delete --complement -- '0-9')
|
||
if [ -z "${min_size_int}" ]; then
|
||
min_size_int="${MIN_SIZE_INT_DEFAULT}"
|
||
readonly min_size_int
|
||
fi
|
||
## }}}
|
||
## Define min_size_unit {{{
|
||
min_size_unit=$(printf -- '%s' "${min_size}" \
|
||
| tr --delete --complement -- 'a-zA-Z')
|
||
if [ -z "${min_size_unit}" ]; then
|
||
min_size_unit="${MIN_SIZE_UNIT_DEFAULT}"
|
||
readonly min_size_unit
|
||
fi
|
||
## }}}
|
||
## If user_email_domain wasn't defined (argument) {{{
|
||
if [ -z "${user_email_domain}" ]; then
|
||
## Use default value
|
||
readonly user_email_domain="${USER_EMAIL_DOMAIN_DEFAULT}"
|
||
fi
|
||
## }}}
|
||
## If sender_email_address wasn't defined (argument) {{{
|
||
if [ -z "${sender_email_address}" ]; then
|
||
## Use default value
|
||
readonly sender_email_address="${SENDER_EMAIL_ADDRESS_DEFAULT}"
|
||
fi
|
||
## }}}
|
||
|
||
}
|
||
# }}}
|
||
|
||
is_directory_absent() { # {{{
|
||
|
||
local_directory_absent="${1}"
|
||
debug_prefix="${2:-}"
|
||
|
||
## Directory doesn't exists by default
|
||
return_is_directory_absent="0"
|
||
|
||
### Check if the directory exists
|
||
# shellcheck disable=SC2086
|
||
if test -d "${local_directory_absent}"; then
|
||
return_is_directory_absent="1"
|
||
debug_message "${debug_prefix}is_directory_absent − \
|
||
The directory ${RED}${local_directory_absent}${COLOR_DEBUG} exists."
|
||
else
|
||
return_is_directory_absent="0"
|
||
debug_message "${debug_prefix}is_directory_absent − \
|
||
The directory ${RED}${local_directory_absent}${COLOR_DEBUG} doesn't exist."
|
||
fi
|
||
|
||
unset local_directory_absent
|
||
unset debug_prefix
|
||
|
||
return "${return_is_directory_absent}"
|
||
}
|
||
# }}}
|
||
is_file_absent() { # {{{
|
||
|
||
local_file_absent="${1}"
|
||
debug_prefix="${2:-}"
|
||
|
||
## File exists by default
|
||
return_is_file_absent="1"
|
||
|
||
### Check if the file exists
|
||
# shellcheck disable=SC2086
|
||
if find ${local_file_absent} > /dev/null 2>&1; then
|
||
return_is_file_absent="1"
|
||
debug_message "${debug_prefix}is_file_absent − \
|
||
The file ${RED}${local_file_absent}${COLOR_DEBUG} exists."
|
||
else
|
||
return_is_file_absent="0"
|
||
debug_message "${debug_prefix}is_file_absent − \
|
||
The file ${RED}${local_file_absent}${COLOR_DEBUG} doesn't exist."
|
||
fi
|
||
|
||
unset local_file_absent
|
||
unset debug_prefix
|
||
|
||
return "${return_is_file_absent}"
|
||
}
|
||
# }}}
|
||
is_file_present() { # {{{
|
||
|
||
local_file_present="${1}"
|
||
debug_prefix="${2:-}"
|
||
|
||
## File doesn't exist by default
|
||
return_is_file_present="1"
|
||
|
||
### Check if the file exists
|
||
# shellcheck disable=SC2086
|
||
if find ${local_file_present} > /dev/null 2>&1; then
|
||
return_is_file_present="0"
|
||
debug_message "${debug_prefix}is_file_present − \
|
||
The file ${RED}${local_file_present}${COLOR_DEBUG} exists."
|
||
else
|
||
return_is_file_present="1"
|
||
debug_message "${debug_prefix}is_file_present − \
|
||
The file ${RED}${local_file_present}${COLOR_DEBUG} doesn't exist."
|
||
fi
|
||
|
||
unset local_file_present
|
||
unset debug_prefix
|
||
|
||
return "${return_is_file_present}"
|
||
}
|
||
# }}}
|
||
clean_temp_files() { # {{{
|
||
|
||
## Remove temp files if DEBUG is not set
|
||
if [ ! "${DEBUG}" -eq "0" ]; then
|
||
rm --recursive --force -- "${DUP_TMP_DIR}"
|
||
else
|
||
debug_message "| Temp files are available in ${RED}${DUP_TMP_DIR}${COLOR_DEBUG} for any debug."
|
||
fi
|
||
|
||
return "0"
|
||
}
|
||
# }}}
|
||
|
||
main() { # {{{
|
||
|
||
# Define all vars
|
||
define_vars
|
||
|
||
debug_message "--- MAIN BEGIN"
|
||
|
||
debug_message "| Temp files are availabe here : ${RED}${DUP_TMP_DIR}${COLOR_DEBUG} ."
|
||
|
||
# Requirements {{{
|
||
if [ ! $(command -v sqlite3) ]; then
|
||
debug_message "| Need to install script dependency (${RED}sqlite3${COLOR_DEBUG})."
|
||
sudo aptitude install sqlite3
|
||
fi
|
||
# }}}
|
||
|
||
# If dup_dir doesn't exists {{{
|
||
# AND exit with error
|
||
is_directory_absent "${dup_dir}" "| " \
|
||
&& error_message "The directory (${dup_dir}) doesn't exists. Check your configuration or use -d|--dir option to give an existing directory. Use --help for more informations." 11
|
||
# }}}
|
||
# If dup_info doesn't exists {{{
|
||
# AND exit with error
|
||
is_directory_absent "${dup_info}" "| " \
|
||
&& debug_message "| Create the ${RED}${dup_info}${COLOR_DEBUG} directory." \
|
||
&& mkdir --parent -- "${dup_info}"
|
||
# }}}
|
||
# If mail_template doesn't exists {{{
|
||
# AND exit with error
|
||
is_file_absent "${mail_template}" "| " \
|
||
&& error_message "Email template (${mail_template}) doesn't exists. Please provide a path to a valid file (--template option) or create a template in appropriate directory (${dup_info}). See --help for more informations." 12
|
||
# }}}
|
||
# If db_path doesn't exists {{{
|
||
# Init a table in a new sqlite3 database file
|
||
if is_file_absent "${db_path}" "| "; then
|
||
debug_message "| Init table (${RED}${SQLITE_TABLE_NAME}${COLOR_DEBUG}) in a new SQLite database file (${RED}${db_path}${COLOR_DEBUG})."
|
||
sqlite3 "${db_path}" "CREATE table ${SQLITE_TABLE_NAME}( name TEXT, user TEXT, size INT, iteration INT, date TEXT, emailed TEXT, PRIMARY KEY (name, user, size, iteration, date, emailed) );" \
|
||
|| error_message "Can't init a new TABLE (${SQLITE_TABLE_NAME}) in SQLite database (${db_path})." 13
|
||
fi
|
||
# }}}
|
||
|
||
# Move to dup_dir
|
||
debug_message "| Move to users backup base path (${RED}${dup_dir}${COLOR_DEBUG})."
|
||
cd -- "${dup_dir}" >/dev/null \
|
||
|| error_message "Can't move to directory (${dup_dir}). Check the permissions or try with 'sudo'." 14
|
||
|
||
# Get the sorted list of users directories. {{{
|
||
find . -mindepth 1 -maxdepth 1 -type d -not -name '.*' -printf "%f\n" | sort > "${DUP_USERS_DIR_LIST}"
|
||
|
||
# Test if the users directories list exists and is not empty
|
||
# OR exit with error
|
||
is_file_present "${DUP_USERS_DIR_LIST}" "| " \
|
||
|| error_message "The users list (${DUP_USERS_DIR_LIST}) seems empty or doesn't exists." 15
|
||
# }}}
|
||
|
||
# While loop for each user directory
|
||
while IFS= read -r user_dir; do
|
||
## Define path for an email content
|
||
user_email_template="${DUP_TMP_DIR}/${user_dir}.mail"
|
||
|
||
## Check the size of user directory {{{
|
||
if ! du --summarize --block-size="${min_size}" -- "${user_dir}" >/dev/null 2>&1; then
|
||
error_message "Can't get size of ${user_dir} user directory. Check the permissions or try with 'sudo'" 21
|
||
fi
|
||
user_dir_size=$(du --summarize --block-size="${min_size}" -- "${user_dir}" \
|
||
| cut --fields=1 -- )
|
||
### Multiply by min_size_int to have the correct size order
|
||
: $(( user_dir_size=user_dir_size*min_size_int ))
|
||
|
||
if [ "${user_dir_size}" -gt "${min_size_int}" ]; then
|
||
debug_message "|-- Manage ${RED}${user_dir}${COLOR_DEBUG} directory BEGIN"
|
||
else
|
||
debug_message "| ${RED}Skip ${user_dir}${COLOR_DEBUG} directory because it's below the minimum size (~${user_dir_size} ${min_size_unit} ≤ ${min_size})."
|
||
unset user_dir_size
|
||
## Continue the while loop with next argument
|
||
continue
|
||
fi
|
||
unset user_dir_size
|
||
## }}}
|
||
## Get the list of user backups {{{
|
||
DUP_USER_BACKUP_LIST="${DUP_TMP_DIR}/${user_dir}.backup.list"
|
||
find "${user_dir}" -mindepth 1 -maxdepth 1 -type d -not -name '.*' -printf "%f\n" | sort > "${DUP_USER_BACKUP_LIST}"
|
||
|
||
## Test if the user backups list exists and is not empty
|
||
## OR exit with error
|
||
is_file_present "${DUP_USER_BACKUP_LIST}" "|| " \
|
||
|| error_message "Backups list (${DUP_USER_BACKUP_LIST}) for user (${user_dir}) seems empty or doesn't exists." 22
|
||
## }}}
|
||
|
||
## While loop for each backup directory
|
||
while IFS= read -r backup_dir; do
|
||
### Ignore directory if a .ignore.me file exists {{{
|
||
find "${user_dir}/${backup_dir}/.ignore.me" >/dev/null 2>&1 \
|
||
&& debug_message "|| ${RED}Skip ${user_dir}/${backup_dir}${COLOR_DEBUG} directory because a .ignore.me file exists." \
|
||
&& continue
|
||
### }}}
|
||
debug_message "||- Manage ${RED}${user_dir}/${backup_dir}${COLOR_DEBUG} directory BEGIN"
|
||
### Get current size {{{
|
||
if ! du --summarize --block-size="${min_size}" -- "${user_dir}/${backup_dir}" >/dev/null 2>&1; then
|
||
error_message "Can't get size of ${user_dir}/${backup_dir} backup directory. Check the permissions or try with 'sudo'" 31
|
||
fi
|
||
backup_dir_current_size=$(du --summarize --block-size="${min_size}" -- "${user_dir}/${backup_dir}" \
|
||
| cut --fields=1 -- )
|
||
### Multiply by min_size_int to have the correct size order
|
||
: $(( backup_dir_current_size=backup_dir_current_size*min_size_int ))
|
||
debug_message "||| Get backup current size (~${RED}${backup_dir_current_size}${COLOR_DEBUG} ${min_size_unit})."
|
||
### }}}
|
||
### Get previous size {{{
|
||
if [ $(sqlite3 "${db_path}" "SELECT size FROM ${SQLITE_TABLE_NAME} WHERE name='${user_dir}/${backup_dir}' AND iteration='-1'" | wc --lines --) -eq "1" ] ; then
|
||
backup_dir_previous_size=$(sqlite3 "${db_path}" "SELECT size FROM ${SQLITE_TABLE_NAME} WHERE name='${user_dir}/${backup_dir}' AND iteration='-1'")
|
||
debug_message "||| Use backup previous size (${RED}${backup_dir_previous_size}${COLOR_DEBUG} ${min_size_unit}) from SQLite database."
|
||
else
|
||
backup_dir_previous_size="0"
|
||
debug_message "||| Set backup previous size to zero (${RED}${backup_dir_previous_size}${COLOR_DEBUG} ${min_size_unit})."
|
||
fi
|
||
### }}}
|
||
### Compare current and previous sizes {{{
|
||
: $(( backup_dir_compare_size=backup_dir_current_size-backup_dir_previous_size ))
|
||
### Don't take care for negative value (remove minus sign)
|
||
if [ "${backup_dir_compare_size#-}" -lt "${min_size_int}" ] ; then
|
||
debug_message "||| The differences between current and previous sizes is not enough (~${RED}${backup_dir_compare_size#-}${COLOR_DEBUG} ${min_size_unit})."
|
||
backup_dir_email="True"
|
||
else
|
||
debug_message "||| The differences between current and previous sizes is correct (~${RED}${backup_dir_compare_size#-}${COLOR_DEBUG} ${min_size_unit})."
|
||
backup_dir_email="False"
|
||
fi
|
||
### }}}
|
||
### Increase iterations for registred informations {{{
|
||
sqlite3 "${db_path}" "UPDATE ${SQLITE_TABLE_NAME} SET iteration = iteration - 1 WHERE name='${user_dir}/${backup_dir}'" \
|
||
|| error_message "Can't increase iterations on SQLite db file (${db_path}) for backup dir (${user_dir}/${backup_dir})." 32
|
||
### }}}
|
||
### Add a new record {{{
|
||
sqlite3 "${db_path}" "INSERT INTO ${SQLITE_TABLE_NAME} values( \
|
||
'${user_dir}/${backup_dir}', \
|
||
'${user_dir}', \
|
||
${backup_dir_current_size}, \
|
||
-1, \
|
||
'$(date +%Y/%m/%d-%H:%M)', \
|
||
'${backup_dir_email}' \
|
||
)" || error_message "Can't add a new on SQLite db file (${db_path}) for backup dir (${user_dir}/${backup_dir})." 33
|
||
### }}}
|
||
### Build email if required {{{
|
||
if [ "${backup_dir_email}" = "True" ]; then
|
||
### Use default email template to build email body for this user
|
||
is_file_absent "${user_email_template}" "| " \
|
||
&& cp -- "${mail_template}" "${user_email_template}"
|
||
|
||
### Add informations specific to this backup {{{
|
||
printf '%b' "## ${backup_dir}
|
||
Taille précédente pour cette sauvegarde/Previous size for this backup : ~${backup_dir_previous_size} ${min_size_unit} (compress).
|
||
Taille actuelle pour cette sauvegarde/Current size for this backup : ~${backup_dir_current_size} ${min_size_unit} (compress).
|
||
La différence de taille (~${backup_dir_compare_size#-} ${min_size_unit}) est inférieure au minimum attendu (${min_size}).
|
||
|
||
---
|
||
|
||
The difference (~${backup_dir_compare_size#-} ${min_size_unit}) is bellow the minimum expected size (${min_size}).
|
||
" >> "${user_email_template}"
|
||
### }}}
|
||
### Add all recorded informations from SQLite db {{{
|
||
if [ $(sqlite3 "${db_path}" "SELECT * FROM ${SQLITE_TABLE_NAME} WHERE name='${user_dir}/${backup_dir}'" | wc --lines --) -ge "1" ] ; then
|
||
printf '%b' "\nInformations issues des tests précédents :
|
||
Recorded informations from previous tests :
|
||
" >> "${user_email_template}"
|
||
|
||
printf ".mode box\nSELECT * FROM ${SQLITE_TABLE_NAME} WHERE name='${user_dir}/${backup_dir}' ORDER BY name, iteration\n" | sqlite3 "${db_path}" >> "${user_email_template}"
|
||
#sqlite3 "${db_path}" "SELECT * FROM ${SQLITE_TABLE_NAME} WHERE name='${user_dir}/${backup_dir}' ORDER BY name, iteration"
|
||
fi
|
||
printf '%b\n' "" >> "${user_email_template}"
|
||
### }}}
|
||
|
||
fi
|
||
### }}}
|
||
## Unset vars {{{
|
||
unset backup_dir_current_size
|
||
unset backup_dir_previous_size
|
||
unset backup_dir_email
|
||
#unset
|
||
## }}}
|
||
debug_message "||- Manage ${RED}${user_dir}/${backup_dir}${COLOR_DEBUG} directory END"
|
||
done < "${DUP_USER_BACKUP_LIST}"
|
||
## Done while loop for each backup directory
|
||
|
||
## Send email if a content exists {{{
|
||
if is_file_present "${user_email_template}" "| "; then
|
||
user_email_address="${user_dir}@${user_email_domain}"
|
||
### Add final informations if available {{{
|
||
is_file_present "${mail_template_bottom}" "| " \
|
||
&& debug_message "| Add extra content (${RED}${mail_template_bottom}${COLOR_DEBUG}) at the end of email for user." \
|
||
&& cat "${mail_template_bottom}" >> "${user_email_template}"
|
||
### }}}
|
||
### Send email {{{
|
||
debug_message "| Send email content (${user_email_template}) to user email address (${RED}${user_email_address}${COLOR_DEBUG})."
|
||
mail -a "From:${sender_email_address}" -s "Duplicati backup warning - ${user_dir}" "${user_email_address}" < "${user_email_template}" \
|
||
|| error_message "Can't send content (${user_email_template}) by mail to user (${user_email_address}). See --debug option for more options." 23
|
||
### }}}
|
||
fi
|
||
## }}}
|
||
|
||
## Remove temp list if DEBUG is not set
|
||
[ ! "${DEBUG}" -eq "0" ] && rm --force -- "${DUP_USER_BACKUP_LIST}"
|
||
## Unset vars {{{
|
||
unset DUP_USER_BACKUP_LIST
|
||
unset backup_dir
|
||
unset user_email_template
|
||
## }}}
|
||
debug_message "|-- Manage ${RED}${user_dir}${COLOR_DEBUG} directory END"
|
||
done < "${DUP_USERS_DIR_LIST}"
|
||
# Done while loop for each user directory
|
||
|
||
# Remove SQLite records with an iterations ≤ -10
|
||
sqlite3 "${db_path}" "DELETE FROM ${SQLITE_TABLE_NAME} WHERE iteration<=-10" \
|
||
|| error_message "Can't remove from SQLite database (${db_path}) records with iterations ≤ -10." 15
|
||
|
||
debug_message "| Move back to previous directory (${RED}before ${dup_dir}${COLOR_DEBUG})."
|
||
cd -- - >/dev/null \
|
||
|| error_message "Can't move back to previous directory (before ${dup_dir}). Check the permissions or try with 'sudo'." 16
|
||
|
||
## Remove temp files if DEBUG is not set {{{
|
||
if [ ! "${DEBUG}" -eq "0" ]; then
|
||
debug_message "| ${RED}Clean temp files.${COLOR_DEBUG}"
|
||
clean_temp_files
|
||
else
|
||
mv -- "${DUP_TMP_DIR}" "${dup_info}/$(date +%Y%m%d).logs"
|
||
debug_message "| Temp files are available in ${RED}${dup_info}/$(date +%Y%m%d).logs${COLOR_DEBUG} for any debug."
|
||
fi
|
||
## }}}
|
||
|
||
debug_message "--- MAIN END"
|
||
}
|
||
# }}}
|
||
|
||
# Manage arguments # {{{
|
||
# This code can't be in a function due to argument management
|
||
|
||
if [ ! "${NBARGS}" -eq "0" ]; then
|
||
|
||
manage_arg="0"
|
||
|
||
## If the first argument is not an option
|
||
if ! printf -- '%s' "${1}" | grep -q -E -- "^-+";
|
||
then
|
||
## Print help message and exit
|
||
printf '%b\n' "${RED}Invalid option: ${1}${RESET}"
|
||
printf '%b\n' "---"
|
||
usage
|
||
|
||
exit 1
|
||
fi
|
||
|
||
# Parse all options (start with a "-") one by one
|
||
while printf -- '%s' "${1}" | grep -q -E -- "^-+"; do
|
||
|
||
case "${1}" in
|
||
-d|--dir|--directory ) ## Define dup_dir
|
||
## Move to the next argument
|
||
shift
|
||
## Define var
|
||
readonly dup_dir="${1}"
|
||
;;
|
||
--db|--sqlite3 ) ## Define db_path
|
||
## Move to the next argument
|
||
shift
|
||
## Define var
|
||
readonly db_path="${1}"
|
||
;;
|
||
--domain|--mail-domain ) ## Define user_email_domain
|
||
## Move to the next argument
|
||
shift
|
||
## Define var
|
||
readonly user_email_domain="${1}"
|
||
;;
|
||
--sender|--mail-sender|--sender-mail )## Define sender_email_address
|
||
## Move to the next argument
|
||
shift
|
||
## Define var
|
||
readonly sender_email_address="${1}"
|
||
;;
|
||
--debug ) ## debug
|
||
DEBUG=0
|
||
debug_message "--- Manage argument BEGIN"
|
||
;;
|
||
-h|--help ) ## help
|
||
usage
|
||
## Exit after help informations
|
||
exit 0
|
||
;;
|
||
-m,--mail,--mail-template,--template ) ## Define mail_template
|
||
## Move to the next argument
|
||
shift
|
||
## Define var
|
||
readonly mail_template="${1}"
|
||
;;
|
||
-s,--size,--min-size ) ## Define min_size
|
||
## Move to the next argument
|
||
shift
|
||
## Define var
|
||
readonly min_size="${1}"
|
||
;;
|
||
* ) ## unknow option
|
||
printf '%b\n' "${RED}Invalid option: ${1}${RESET}"
|
||
printf '%b\n' "---"
|
||
usage
|
||
exit 1
|
||
;;
|
||
esac
|
||
|
||
debug_message "| ${RED}${1}${COLOR_DEBUG} option managed."
|
||
|
||
## Move to the next argument
|
||
shift
|
||
manage_arg=$((manage_arg+1))
|
||
|
||
done
|
||
|
||
debug_message "| ${RED}${manage_arg}${COLOR_DEBUG} argument(s) successfully managed."
|
||
else
|
||
debug_message "| No arguments/options to manage."
|
||
fi
|
||
|
||
debug_message "--- Manage argument END"
|
||
# }}}
|
||
|
||
main
|
||
|
||
exit 0
|