#!/usr/bin/env bash
# shellcheck disable=SC2004
# SC2004: $ not required in arithmentic expressions
#
#######################################################################################################################
#
# Create and restore a backup of a Raspberry running Raspbian
#
# Visit http://www.linux-tips-and-tricks.de/raspiBackup for latest code and other details
#
# Smart recycle backup strategy inspired by https://opensource.com/article/18/8/automate-backups-raspberry-pi and
# enhanced to support multiple backups in a given timeframe of days, weeks, months and years
#
# Credits to following people for their translation work
# FI - teemue
# FR - mgrafr
#
#######################################################################################################################
#
# Copyright (c) 2013-2025 framp at linux-tips-and-tricks dot de
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
#######################################################################################################################
set -o pipefail
if [ -z "$BASH" ] ;then
echo "??? ERROR: Unable to execute script. bash interpreter missing"
echo "??? DEBUG: $(lsof -a -p $$ -d txt | tail -n 1)"
exit 127
fi
MYSELF="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")" # use linked script name if the link is used
MYNAME=${MYSELF%.*}
VERSION="0.7.2" # -beta, -hotfix or -dev suffixes possible
VERSION_SCRIPT_CONFIG="0.1.10" # required config version for script
VERSION_VARNAME="VERSION" # has to match above var names
VERSION_CONFIG_VARNAME="VERSION_.*CONF.*" # used to lookup VERSION_CONFIG in config files
[ "$(kill -l | grep -c SIG)" -eq 0 ] && printf "\n\033[1;35m Don't call script with leading \"sh\"! \033[m\n\n" >&2 && exit 255
[ -z "${BASH_VERSINFO[0]}" ] && printf "\n\033[1;35m Make sure you're using \"bash\"! \033[m\n\n" >&2 && exit 255
[ "${BASH_VERSINFO[0]}" -lt 3 ] && printf "\n\033[1;35m Minimum requirement is bash 3.2. You have %s \033[m\n\n" "$BASH_VERSION" >&2 && exit 255
[ "${BASH_VERSINFO[0]}" -le 3 ] && [ "${BASH_VERSINFO[1]}" -le 1 ] && printf "\n\033[1;35m Minimum requirement is bash 3.2. You have %s \033[m\n\n" "$BASH_VERSION" >&2 && exit 255
declare -r PS4='|${LINENO}> \011${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
# add pathes if not already set (usually not set in crontab)
DEFAULT_PATHES="/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin"
if [[ -e /bin/grep ]]; then
pathElements=("${PATH//:/ }")
for p in $DEFAULT_PATHES; do
if [[ ! " ${pathElements[*]} " =~ ${p} ]]; then
[[ -z $PATH ]] && PATH=$p || PATH="$p:$PATH"
fi
done
export PATH="$PATH"
fi
IS_BETA=$(( ! $(grep -iqE "alpha|beta" <<< "$VERSION"; echo $?) ))
IS_DEV=$(( ! $(grep -iq dev <<< "$VERSION"; echo $?) ))
IS_HOTFIX=$(( ! $(grep -iqE "hotfix|-m_" <<< "$VERSION"; echo $?) ))
# Expressions don't expand in single quotes, use double quotes for that.
# shellcheck disable=SC2016
GIT_DATE='$Date$'
GIT_DATE_ONLY=${GIT_DATE/: /}
GIT_DATE_ONLY=$(cut -f 2 -d ' ' <<< "$GIT_DATE")
GIT_TIME_ONLY=$(cut -f 3 -d ' ' <<< "$GIT_DATE" | sed 's/\$//')
# Expressions don't expand in single quotes, use double quotes for that.
# shellcheck disable=SC2016
GIT_COMMIT='$Sha1$'
GIT_COMMIT_ONLY=$(cut -f 2 -d ' ' <<< "$GIT_COMMIT" | sed 's/\$//')
GIT_CODEVERSION="$MYSELF $VERSION, $GIT_DATE_ONLY/$GIT_TIME_ONLY - $GIT_COMMIT_ONLY"
function findUser() {
local u
if [[ -n "$SUDO_USER" ]]; then
u="$SUDO_USER"
else
u="$USER"
fi
echo "$u"
}
# some general constants
readonly MYHOMEURL="https://www.linux-tips-and-tricks.de"
DATE=$(date +%Y%m%d-%H%M%S)
HOSTNAME=$(hostname)
NL=$'\n'
CURRENT_DIR=$(pwd)
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}")"; pwd | xargs readlink -f)
# Smileys used in eMail subject to notify about news/events
SMILEY_WARNING="O.o"
SMILEY_UPDATE_POSSIBLE=";-)"
SMILEY_BETA_AVAILABLE=":-D"
SMILEY_RESTORETEST_REQUIRED="8-)"
SMILEY_VERSION_DEPRECATED=":-("
# URLs and temp filenames used
# URLTARGET allows to use deployment of new code versions, example: use "beta" to test beta code as if it was published just before it's published
if [[ -n $URLTARGET ]]; then
echo "===> URLTARGET: $URLTARGET"
URLTARGET="/$URLTARGET"
fi
DOWNLOAD_URL="$MYHOMEURL/raspiBackup${URLTARGET}/raspiBackup.sh"
CONFIG_URL="$MYHOMEURL/raspiBackup${URLTARGET}/raspiBackup_\$lang\.conf" # used in eval for late binding of URLTAGRET
BETA_DOWNLOAD_URL="$MYHOMEURL/raspiBackup${URLTARGET}/beta/raspiBackup.sh"
BETA_CONFIG_URL="$MYHOMEURL/raspiBackup${URLTARGET}/beta/raspiBackup_\$lang\.conf" # used in eval for late binding of URLTAGRET
#INSTALLER_DOWNLOAD_URL="$MYHOMEURL/raspiBackup${URLTARGET}/raspiBackupInstallUI.sh"
#INSTALLER_BETA_DOWNLOAD_URL="$MYHOMEURL/raspiBackup${URLTARGET}/beta/raspiBackupInstallUI.sh"
PROPERTIES_DOWNLOAD_URL="$MYHOMEURL/raspiBackup${URLTARGET}/raspiBackup.properties"
VERSION_PAGE="https://github.com/framps/raspiBackup/releases"
# dd warning website
DD_WARNING_URL_DE="$MYHOMEURL/de/raspibackupcategorie/579-raspibackup-warum-sollte-man-dd-als-backupmethode-besser-nicht-benutzen/"
DD_WARNING_URL_EN="$MYHOMEURL/en/all-pages-about-raspibackup/581-raspibackup-why-shouldn-t-you-use-dd-as-backup-method/"
CALLING_USER="$(findUser)"
CALLING_HOME="$(eval echo "~${CALLING_USER}")"
PROPERTY_FILE="$MYNAME.properties"
LATEST_TEMP_PROPERTY_FILE="/tmp/$PROPERTY_FILE"
VAR_LIB_DIRECTORY="/var/lib/$MYNAME"
RESTORE_REMINDER_FILE="restore.reminder"
REPORT_COUNTER_FILE="report.counter"
VARS_FILE="/tmp/$MYNAME.vars"
TEMPORARY_MOUNTPOINT_ROOT="/tmp/${MYNAME}_mnt"
LOGFILE_EXT=".log"
LOGFILE_NAME="${MYNAME}${LOGFILE_EXT}"
LOGFILE_RESTORE_EXT=".logr"
MSGFILE_EXT=".msg"
MSGFILE_RESTORE_EXT=".msgr"
MSGFILE_NAME="${MYNAME}${MSGFILE_EXT}"
TEMP_LOG_FILE="/tmp/$LOGFILE_NAME"
TEMP_MSG_FILE="/tmp/$MSGFILE_NAME"
FINISH_LOG_FILE="/tmp/${MYNAME}.logf"
MODIFIED_SFDISK="/tmp/$$.sfdisk"
# timeouts
DOWNLOAD_TIMEOUT=30 # seconds
#DOWNLOAD_RETRIES=3
# debug option constants
LOG_NONE=0
LOG_DEBUG=1
POSSIBLE_LOG_LEVEL_NUMBERs="[$LOG_NONE$LOG_DEBUG]"
declare -A LOG_LEVELs=( [$LOG_NONE]="Off" [$LOG_DEBUG]="Debug" )
POSSIBLE_LOG_LEVELs=""
for K in "${!LOG_LEVELs[@]}"; do
POSSIBLE_LOG_LEVELs="$POSSIBLE_LOG_LEVELs|${LOG_LEVELs[$K]}"
done
POSSIBLE_LOG_LEVELs="$(cut -c 2- <<< "$POSSIBLE_LOG_LEVELs")"
declare -A LOG_LEVEL_ARGs
for K in "${!LOG_LEVELs[@]}"; do
k=$(tr '[:lower:]' '[:upper:]' <<< "${LOG_LEVELs[$K]}")
LOG_LEVEL_ARGs[$k]="$K"
done
MSG_LEVEL_MINIMAL=0
MSG_LEVEL_DETAILED=1
POSSIBLE_MSG_LEVEL_NUMBERs="[$MSG_LEVEL_MINIMAL$MSG_LEVEL_DETAILED]"
declare -A MSG_LEVELs=( [$MSG_LEVEL_MINIMAL]="Minimal" [$MSG_LEVEL_DETAILED]="Detailed" )
POSSIBLE_MSG_LEVELs=""
for K in "${!MSG_LEVELs[@]}"; do
POSSIBLE_MSG_LEVELs="$POSSIBLE_MSG_LEVELs|${MSG_LEVELs[$K]}"
done
POSSIBLE_MSG_LEVELs="$(cut -c 2- <<< "$POSSIBLE_MSG_LEVELs")"
declare -A MSG_LEVEL_ARGs
for K in "${!MSG_LEVELs[@]}"; do
k=$(tr '[:lower:]' '[:upper:]' <<< "${MSG_LEVELs[$K]}")
MSG_LEVEL_ARGs[$k]="$K"
done
LOG_OUTPUT_VARLOG=1
LOG_OUTPUT_BACKUPLOC=2
LOG_OUTPUT_HOME=3
POSSIBLE_LOG_OUTPUT_NUMBERs="^[$LOG_OUTPUT_BACKUPLOC|$LOG_OUTPUT_HOME|$LOG_OUTPUT_VARLOG]\$"
LOG_OUTPUT_IS_NO_USERDEFINEDFILE_REGEX="[$LOG_OUTPUT_VARLOG$LOG_OUTPUT_BACKUPLOC$LOG_OUTPUT_HOME]"
#declare -A LOG_OUTPUT_LOCs=( [$LOG_OUTPUT_VARLOG]="/var/log/raspiBackup/.log" [$LOG_OUTPUT_BACKUPLOC]="" [$LOG_OUTPUT_HOME]="~/raspiBackup.log")
declare -A LOG_OUTPUTs=( [$LOG_OUTPUT_VARLOG]="Varlog" [$LOG_OUTPUT_BACKUPLOC]="Backup" [$LOG_OUTPUT_HOME]="Current")
declare -A LOG_OUTPUT_ARGs
for K in "${!LOG_OUTPUTs[@]}"; do
k=$(tr '[:lower:]' '[:upper:]' <<< "${LOG_OUTPUTs[$K]}")
LOG_OUTPUT_ARGs[$k]="$K"
done
POSSIBLE_LOG_OUTPUTs=""
for K in "${!LOG_OUTPUTs[@]}"; do
POSSIBLE_LOG_OUTPUTs="$POSSIBLE_LOG_OUTPUTs|${LOG_OUTPUTs[$K]}"
done
POSSIBLE_LOG_OUTPUTs="$(cut -c 2- <<< "$POSSIBLE_LOG_OUTPUTs")"
# message option constants
LOG_TYPE_MSG=0
LOG_TYPE_DEBUG=1
declare -A LOG_TYPEs=( [$LOG_TYPE_MSG]="MSG" [$LOG_TYPE_DEBUG]="DBG")
BACKUPTYPE_DD="dd"
BACKUPTYPE_DDZ="ddz"
BACKUPTYPE_TAR="tar"
BACKUPTYPE_TGZ="tgz"
BACKUPTYPE_RSYNC="rsync"
POSSIBLE_BACKUP_TYPES_REGEX="$BACKUPTYPE_DD|$BACKUPTYPE_DDZ|$BACKUPTYPE_RSYNC|$BACKUPTYPE_TAR|$BACKUPTYPE_TGZ"
declare -A FILE_EXTENSION=( [$BACKUPTYPE_DD]=".img" [$BACKUPTYPE_DDZ]=".img.gz" [$BACKUPTYPE_RSYNC]="" [$BACKUPTYPE_TGZ]=".tgz" [$BACKUPTYPE_TAR]=".tar" )
# map dd/tar to ddz/tgz extension if -z switch is used
declare -A Z_TYPE_MAPPING=( [$BACKUPTYPE_DD]=$BACKUPTYPE_DDZ [$BACKUPTYPE_TAR]=$BACKUPTYPE_TGZ )
readarray -t SORTED < <(for a in "${!FILE_EXTENSION[@]}"; do echo "$a"; done | sort)
ALLOWED_TYPES=""
POSSIBLE_TYPES=""
POSSIBLE_TYPES_ARRAY=()
for K in "${SORTED[@]}"; do
POSSIBLE_TYPES_ARRAY+=("$K")
[[ -z $POSSIBLE_TYPES ]] && POSSIBLE_TYPES=$K || POSSIBLE_TYPES="$POSSIBLE_TYPES|$K"
lastChar="${K: -1}"
if [[ $lastChar == "z" ]]; then # skip tgz and ddz as allowed types, now handled with -z invocation parameter, still accept old types for backward compatibility
continue
fi
[[ -z $ALLOWED_TYPES ]] && ALLOWED_TYPES=$K || ALLOWED_TYPES="$ALLOWED_TYPES|$K"
done
TAR_COMPRESSION_TOOLS_SUPPORTED=( "bzip2" "gzip" "lzip" "lzma" "lzop" "xz" "zstd")
TAR_COMPRESSION_EXTENSIONS_SUPPORTED=(".bz2" ".gz" ".lz" ".lzma" ".lzo" ".xz" ".zst")
# grep regex used to extract partition numbers from partition oriented backups
TAR_COMPRESSION_EXTENSIONS_SUPPORTED_GREP=""
for K in "${!TAR_COMPRESSION_EXTENSIONS_SUPPORTED[@]}"; do
ext="${TAR_COMPRESSION_EXTENSIONS_SUPPORTED[$K]}"
if [[ -z "$TAR_COMPRESSION_EXTENSIONS_SUPPORTED_GREP" ]]; then
TAR_COMPRESSION_EXTENSIONS_SUPPORTED_GREP="tar${ext}"
else
TAR_COMPRESSION_EXTENSIONS_SUPPORTED_GREP="${TAR_COMPRESSION_EXTENSIONS_SUPPORTED_GREP}|tar${ext}"
fi
done
TAR_COMPRESSION_TOOLS_SUPPORTED_LIST=""
for K in "${!TAR_COMPRESSION_TOOLS_SUPPORTED[@]}"; do
ext="${TAR_COMPRESSION_TOOLS_SUPPORTED[$K]}"
if [[ -z "$TAR_COMPRESSION_TOOLS_SUPPORTED_LIST" ]]; then
TAR_COMPRESSION_TOOLS_SUPPORTED_LIST="${ext}"
else
TAR_COMPRESSION_TOOLS_SUPPORTED_LIST="${TAR_COMPRESSION_TOOLS_SUPPORTED_LIST},${ext}"
fi
done
declare -A mountPoints
# variables exported to pass on to extensions
export BACKUP_TARGETDIR
export BACKUP_TARGETFILE
export MSG_FILE
export LOG_FILE
# Telegram options
TELEGRAM_NOTIFY_SUCCESS="S"
TELEGRAM_NOTIFY_FAILURE="F"
TELEGRAM_NOTIFY_MESSAGES="M"
TELEGRAM_NOTIFY_MESSAGES2="m"
TELEGRAM_POSSIBLE_NOTIFICATIONS="$TELEGRAM_NOTIFY_SUCCESS$TELEGRAM_NOTIFY_FAILURE$TELEGRAM_NOTIFY_MESSAGES$TELEGRAM_NOTIFY_MESSAGES2"
TELEGRAM_URL="https://api.telegram.org/bot"
EMOJI_OK="$(echo -ne "\xe2\x9c\x94\xef\xb8\x8f\x0a")" # ✔️
EMOJI_WARNING="$(echo -ne "\xe2\x9a\xa0\xef\xb8\x8f\x0a")" # ⚠️
EMOJI_FAILED="$(echo -ne "\xe2\x9d\x8c\x0a")" # ❌
EMOJI_UPDATE_POSSIBLE="$(echo -ne "\xf0\x9f\x98\x89\x0a")" # 😉
EMOJI_BETA_AVAILABLE="$(echo -ne "\xf0\x9f\x98\x83\x0a")" # 😃
EMOJI_RESTORETEST_REQUIRED="$(echo -ne "\xf0\x9f\x94\x94\x0a")" # 🔔
EMOJI_VERSION_DEPRECATED="$(echo -ne "\xf0\x9f\x92\x80\x0a")" # 💀
# convert emoji into hex
#printf "%s" "$EMOJI_WARNING"
#echo $(xxd -pu <<< "$EMOJI_WARNING")
#exit
# Pushover options
PUSHOVER_NOTIFY_SUCCESS="S"
PUSHOVER_NOTIFY_FAILURE="F"
PUSHOVER_NOTIFY_MESSAGES="M"
PUSHOVER_POSSIBLE_NOTIFICATIONS="$PUSHOVER_NOTIFY_SUCCESS$PUSHOVER_NOTIFY_FAILURE$PUSHOVER_NOTIFY_MESSAGES"
PUSHOVER_URL="https://api.pushover.net/1/messages.json"
# Slack options
SLACK_NOTIFY_SUCCESS="S"
SLACK_NOTIFY_FAILURE="F"
SLACK_NOTIFY_MESSAGES="M"
SLACK_POSSIBLE_NOTIFICATIONS="$SLACK_NOTIFY_SUCCESS$SLACK_NOTIFY_FAILURE$SLACK_NOTIFY_MESSAGES"
SLACK_EMOJI_OK=":white_check_mark:" # ✔️
SLACK_EMOJI_WARNING=":warning:" # ⚠️
SLACK_EMOJI_FAILED=":x:" # ❌
SLACK_EMOJI_UPDATE_POSSIBLE=":smirk:" # 😉
SLACK_EMOJI_BETA_AVAILABLE=":laughing:" # 😃
SLACK_EMOJI_RESTORETEST_REQUIRED=":bell:" # 🔔
SLACK_EMOJI_VERSION_DEPRECATED=":skull:" # 💀
# various other constants
PRE_BACKUP_EXTENSION="pre"
POST_BACKUP_EXTENSION="post"
READY_BACKUP_EXTENSION="ready"
NOTIFICATION_BACKUP_EXTENSION="notify"
EMAIL_EXTENSION="mail"
PRE_RESTORE_EXTENSION="$PRE_BACKUP_EXTENSION"
POST_RESTORE_EXTENSION="$POST_BACKUP_EXTENSION"
PRE_BACKUP_EXTENSION_CALLED=0
PRE_RESTORE_EXTENSION_CALLED=0
EMAIL_EXTENSION_PROGRAM="mailext"
EMAIL_MAILX_PROGRAM="mail"
EMAIL_SSMTP_PROGRAM="ssmtp"
EMAIL_MSMTP_PROGRAM="msmtp"
EMAIL_SENDEMAIL_PROGRAM="sendEmail"
SUPPORTED_EMAIL_PROGRAM_REGEX="^($EMAIL_MAILX_PROGRAM|$EMAIL_SSMTP_PROGRAM|$EMAIL_MSMTP_PROGRAM|$EMAIL_SENDEMAIL_PROGRAM|$EMAIL_EXTENSION_PROGRAM)$"
SUPPORTED_MAIL_PROGRAMS=$(echo $SUPPORTED_EMAIL_PROGRAM_REGEX | sed 's:^..\(.*\)..$:\1:' | sed 's/|/,/g')
EMAIL_COLORING_SUBJECT="SUBJECT"
EMAIL_COLORING_OPTION="OPTION"
SUPPORTED_EMAIL_COLORING_REGEX="^($EMAIL_COLORING_OPTION|$EMAIL_COLORING_SUBJECT)$"
SUPPORTED_EMAIL_COLORING=$(echo $SUPPORTED_EMAIL_COLORING_REGEX | sed 's:^..\(.*\)..$:\1:' | sed 's/|/,/g')
PARTITIONS_TO_BACKUP_ALL="*"
MASQUERADE_STRING="@@@@"
COLORING_CONSOLE="C"
COLORING_MAIL="M"
COLORING_VALID_OPTIONS="$COLORING_CONSOLE$COLORING_MAIL"
NEWS_AVAILABLE=0
BETA_AVAILABLE=0
LOG_INDENT=0
WARNING_MESSAGE_WRITTEN=0
PROPERTY_REGEX='.*="([^"]*)"'
NOOP_AO_ARG_REGEX="^[[:space:]]*:"
STOPPED_SERVICES=0
SHARED_BOOT_DIRECTORY=0
BOOT_TAR_EXT="tmg"
BOOT_DD_EXT="img"
CONFIG_DIR="/usr/local/etc"
ORIG_CONFIG="$CONFIG_DIR/raspiBackup.conf"
NEW_CONFIG="$CONFIG_DIR/raspiBackup.conf.new"
MERGED_CONFIG="$CONFIG_DIR/raspiBackup.conf.merged"
BACKUP_CONFIG="$CONFIG_DIR/raspiBackup.conf.bak"
PERSISTENT_JOURNAL="var/log/journal"
NEW_OPTION_TRAILER="# >>>>> NEW OPTION added in config version %s <<<<< "
DELETED_OPTION_TRAILER="# >>>>> OPTION DELETED in config version %s <<<<< "
TWO_TB=$((1024*1024*1024*1024*2)) # disks > 2TB reuquire gpt instead of mbr
SHA_PLACEHOLDER="$(base64 -d <<< "JFNoYTEkCg==")"
DATE_PLACEHOLDER="$(base64 -d <<< "JERhdGUkCg==")"
# Commands used by raspiBackup and which have to be available
# [command]=package
declare -A REQUIRED_COMMANDS=( \
["parted"]="parted" \
["fsck.vfat"]="dosfstools" \
["e2label"]="e2fsprogs" \
["dosfslabel"]="dosfstools" \
["fdisk"]="fdisk" \
["blkid"]="util-linux" \
["curl"]="curl" \
["sfdisk"]="fdisk" \
)
declare -A REQUIRED_COMMANDS_BTRFS=( \
["btrfs"]="btrfs-tools"
)
declare -A REQUIRED_COMMANDS_F2FS=( \
["f2fstat"]="f2fs-tools"
)
# possible script exit codes
RC_ASSERTION=101
RC_MISC_ERROR=102
RC_CTRLC=103
#RC_EXTENSION_ERROR=104
RC_STOP_SERVICES_ERROR=105
RC_START_SERVICES_ERROR=106
RC_PARAMETER_ERROR=107
RC_MISSING_FILES=108
RC_NATIVE_BACKUP_FAILED=109
RC_LINK_FILE_FAILED=110
RC_COLLECT_PARTITIONS_FAILED=111
RC_CREATE_PARTITIONS_FAILED=112
#RC_=113
RC_DD_IMG_FAILED=114
RC_SDCARD_ERROR=115
RC_RESTORE_FAILED=116
RC_NATIVE_RESTORE_FAILED=117
RC_DEVICES_NOTFOUND=118
RC_CREATE_ERROR=119
RC_MISSING_COMMANDS=120
RC_NO_BOOT_FOUND=121
RC_BEFORE_START_SERVICES_ERROR=122
RC_BEFORE_STOP_SERVICES_ERROR=123
RC_EMAILPROG_ERROR=124
RC_MISSING_PARTITION=125
#RC_UUIDS_NOT_UNIQUE=126
#RC_INCOMPLETE_PARMS=127
#RC_CONFIGVERSION_MISMATCH=128
#RC_TELEGRAM_ERROR=129
RC_FILE_OPERATION_ERROR=130
RC_MOUNT_FAILED=131
RC_UNSUPPORTED_ENVIRONMENT=132
RC_RESTORE_EXTENSION_FAILS=133
RC_BACKUP_EXTENSION_FAILS=134
RC_DOWNLOAD_FAILED=135
RC_BACKUP_DIRNAME_ERROR=136
RC_RESTORE_IMPOSSIBLE=137
RC_INVALID_BOOTDEVICE=138
RC_ENVIRONMENT_ERROR=139
RC_CLEANUP_ERROR=140
#RC_EXTENSION_ERROR=141
#RC_UNPROTECTED_CONFIG=142
RC_NOT_SUPPORTED=143
RC_TEMPMOVE_FAILED=144
RC_RESIZE_ERROR=145
#RC_NOT_ALL_PREVIOUS_PARTITIONS_SAVED=146
RC_UUID_UPDATE_IMPOSSIBLE=147
tty -s
# Check exit code directly with e.g. if mycmd;, not indirectly with $?
# shellcheck disable=SC2181
INTERACTIVE=$((!$?))
# defaults
MSG_LEVEL="$MSG_LEVEL_DETAILED"
LOG_LEVEL="$LOG_DEBUG"
LOG_OUTPUT="$LOG_OUTPUT_BACKUPLOC"
# borrowed from http://stackoverflow.com/questions/3685970/check-if-an-array-contains-a-value
function containsElement() { # element ${array[@]}
local e match="$1"
shift
for e; do
[[ "$e" == "$match" ]] && return 0;
done
return 1
}
# return index of element in array (0-n) and true, -1 and false otherwise
function getIndexInArray() { # element ${array[@]}
logEntry "$1 $2"
local e match="$1" i=0
shift
for e; do
if [[ "$e" == "$match" ]]; then
echo "$i"
logExit "$i"
return 0
else
(( i++ ))
fi
done
echo "-1"
logExit "-1"
return "1"
}
#
# NLS: Either use system language if language is supported and use fallback language English otherwise
#
SUPPORTED_LANGUAGES=("EN" "DE" "FI" "FR")
FALLBACK_LANGUAGE="EN"
# use LANG variable to determine language
[[ -z "${LANG}" ]] && LANG="en_US.UTF-8" # if no LANG set use English
LANG_EXT="${LANG,,*}"
LANG_SYSTEM="${LANG_EXT:0:2}" # extract language id
if ! containsElement "${LANG_SYSTEM^^*}" "${SUPPORTED_LANGUAGES[@]}"; then # if language is not supported use English
LANG_SYSTEM=$FALLBACK_LANGUAGE
fi
#
# Messages
#
# To add a new language just execute following steps:
# 1) Add new language id LL (e.g. FI for Finnish) in variable SUPPORTED_LANGUAGES (see above)
# 2) For every MSG_ add a new message MSG_LL, e.g. MSG_FI for Finnish
# 3) For every MSG_ add a new declare -A in following line, e.g. MSG_FI for Finnish
# 4) Optionally add a help function usageLL, e.g. usageFI
# 5) Note: If a message definition or help function (MSG_LL or usageLL) is missing in a supported language the fallback language English will be selected by the code (MSG_EN or usageEN)
#
declare -A MSG_EN MSG_DE MSG_FI MSG_FR
LANGUAGE="${LANG_SYSTEM^^*}" # that's the language until it's overwritten with an option or config entry
MSG_UNDEFINED=0
MSG_EN[$MSG_UNDEFINED]="RBK0000E: Undefined messageid"
MSG_DE[$MSG_UNDEFINED]="RBK0000E: Unbekannte Meldungsid"
MSG_FI[$MSG_UNDEFINED]="RBK0000E: Määrittämätön viestitunnus"
MSG_FR[$MSG_UNDEFINED]="RBK0000E: Id du message non défini"
MSG_ASSERTION_FAILED=1
MSG_EN[$MSG_ASSERTION_FAILED]="RBK0001E: Unexpected program error occured. (%s), Linenumber: %s, Error: %s"
MSG_DE[$MSG_ASSERTION_FAILED]="RBK0001E: Unerwarteter Programmfehler trat auf. (%s), Zeile: %s, Fehler: %s"
MSG_FI[$MSG_ASSERTION_FAILED]="RBK0001E: Tapahtui odottamaton virhe. (%s), Rivinumero: %s, Virhe: %s"
MSG_FR[$MSG_ASSERTION_FAILED]="RBK0001E: Une erreur inattendue s'est produite. (%s), à la ligne n°: %s, Erreur: %s"
MSG_RUNASROOT=2
MSG_EN[$MSG_RUNASROOT]="RBK0002E: $MYSELF has to be started as root. Try 'sudo %s%s'"
MSG_DE[$MSG_RUNASROOT]="RBK0002E: $MYSELF muss als root gestartet werden. Benutze 'sudo %s%s'"
MSG_FI[$MSG_RUNASROOT]="RBK0002E: $MYSELF tulee käynnistää root-oikeuksin. Suorita 'sudo %s%s'"
MSG_FR[$MSG_RUNASROOT]="RBK0002E: $MYSELF doit être démarré en tant que root.Essayez 'sudo %s%s'"
MSG_TRUNCATING_TO_USED_PARTITIONS_ONLY=3
MSG_EN[$MSG_TRUNCATING_TO_USED_PARTITIONS_ONLY]="RBK0003I: Backup size will be truncated from %s to %s"
MSG_DE[$MSG_TRUNCATING_TO_USED_PARTITIONS_ONLY]="RBK0003I: Backupgröße wird von %s auf %s reduziert"
MSG_FI[$MSG_TRUNCATING_TO_USED_PARTITIONS_ONLY]="RBK0003I: Varmuuskopion koko typistetään koosta %s kokoon %s"
MSG_FR[$MSG_TRUNCATING_TO_USED_PARTITIONS_ONLY]="RBK0003I: La taille de la sauvegarde sera diminuée de %s à %s"
MSG_ADJUSTING_SECOND=4
MSG_EN[$MSG_ADJUSTING_SECOND]="RBK0004I: Adjusting second partition from %s to %s"
MSG_DE[$MSG_ADJUSTING_SECOND]="RBK0004I: Zweite Partition wird von %s auf %s angepasst"
MSG_FI[$MSG_ADJUSTING_SECOND]="RBK0004I: Säädetään toinen osio %s osioksi %s"
MSG_FR[$MSG_ADJUSTING_SECOND]="RBK0004I: Redimensionnement de la deuxième partition de %s à %s"
MSG_BACKUP_FAILED=5
MSG_EN[$MSG_BACKUP_FAILED]="RBK0005E: Backup failed. Check previous error messages for details"
MSG_DE[$MSG_BACKUP_FAILED]="RBK0005E: Backup fehlerhaft beendet. Siehe vorhergehende Fehlermeldungen"
MSG_FI[$MSG_BACKUP_FAILED]="RBK0005E: Varmuuskopiointi epäonnistui. Katso lisätiedot edellisistä virheilmoituksista"
MSG_FR[$MSG_BACKUP_FAILED]="RBK0005E: La sauvegarde a echoué. Consultez les messages d'erreur pour plus d'information"
MSG_ADJUSTING_WARNING=6
MSG_EN[$MSG_ADJUSTING_WARNING]="RBK0006W: Target %s with %s is smaller than backup source with %s. root partition will be truncated accordingly. NOTE: Restore may fail if the root partition will become too small"
MSG_DE[$MSG_ADJUSTING_WARNING]="RBK0006W: Ziel %s mit %s ist kleiner als die Backupquelle mit %s. Die root Partition wird entsprechend verkleinert. HINWEIS: Der Restore kann fehlschlagen wenn sie zu klein wird"
MSG_FI[$MSG_ADJUSTING_WARNING]="RBK0006W: Kohde %s kooltaan %s on pienempi kuin varmuuskopion lähde kooltaan %s. Juuriosio typistetään sen mukaiseksi. HUOM: Palautus saattaa epäonnistua, jos juuriosiosta tulee liian pieni"
MSG_FR[$MSG_ADJUSTING_WARNING]="RBK0006W: La cible %s avec %s est plus petite que la source avec %s. la partition racine sera diminuée en proportion. REMARQUE : La restauration peut échouer si la partition root devient trop petite"
MSG_STARTING_SERVICES=7
MSG_EN[$MSG_STARTING_SERVICES]="RBK0007I: Starting services: '%s'"
MSG_DE[$MSG_STARTING_SERVICES]="RBK0007I: Services werden gestartet: '%s'"
MSG_FI[$MSG_STARTING_SERVICES]="RBK0007I: Käynnistetään palvelut: '%s'"
MSG_FR[$MSG_STARTING_SERVICES]="RBK0007I: Démarrage des services: '%s'"
MSG_STOPPING_SERVICES=8
MSG_EN[$MSG_STOPPING_SERVICES]="RBK0008I: Stopping services: '%s'"
MSG_DE[$MSG_STOPPING_SERVICES]="RBK0008I: Services werden gestoppt: '%s'"
MSG_FI[$MSG_STOPPING_SERVICES]="RBK0008I: Pysäytetään palvelut: '%s'"
MSG_FR[$MSG_STOPPING_SERVICES]="RBK0008I: Arrêt des services: '%s'"
MSG_STARTED=9
MSG_EN[$MSG_STARTED]="RBK0009I: %s: %s V%s - %s (%s) started at %s"
MSG_DE[$MSG_STARTED]="RBK0009I: %s: %s V%s - %s (%s) %s gestartet"
MSG_FI[$MSG_STARTED]="RBK0009I: %s: %s V%s - %s (%s) käynnistyi %s"
MSG_FR[$MSG_STARTED]="RBK0009I: %s: %s V%s - %s (%s) Début à %s"
MSG_STOPPED=10
MSG_EN[$MSG_STOPPED]="RBK0010I: %s: %s V%s - %s (%s) stopped at %s with rc %s"
MSG_DE[$MSG_STOPPED]="RBK0010I: %s: %s V%s - %s (%s) %s beendet mit Returncode %s"
MSG_FI[$MSG_STOPPED]="RBK0010I: %s: %s V%s - %s (%s) pysäytettiin %s, vastauskoodi %s"
MSG_FR[$MSG_STOPPED]="RBK0010I: %s: %s V%s - %s (%s) terminé avec le code de retour %s"
MSG_NO_BOOT_PARTITION=11
MSG_EN[$MSG_NO_BOOT_PARTITION]="RBK0011E: No boot partition ${BOOT_PARTITION_PREFIX}1 found"
MSG_DE[$MSG_NO_BOOT_PARTITION]="RBK0011E: Keine boot Partition ${BOOT_PARTITION_PREFIX}1 gefunden"
MSG_FI[$MSG_NO_BOOT_PARTITION]="RBK0011E: Käynnistysosiota ${BOOT_PARTITION_PREFIX}1 ei löytynyt"
MSG_FR[$MSG_NO_BOOT_PARTITION]="RBK0011E: Pas de partition boot ${BOOT_PARTITION_PREFIX}1 ei löytynyt"
MSG_DD_BACKUP_NOT_POSSIBLE_FOR_PARTITIONBASED_BACKUP=12
MSG_EN[$MSG_DD_BACKUP_NOT_POSSIBLE_FOR_PARTITIONBASED_BACKUP]="RBK0012E: DD backup not supported for partition based backup. Use normal mode instead"
MSG_DE[$MSG_DD_BACKUP_NOT_POSSIBLE_FOR_PARTITIONBASED_BACKUP]="RBK0012E: DD Backup nicht unterstützt bei partitionsbasiertem Backup. Benutze den normalen Modus dafür"
MSG_FI[$MSG_DD_BACKUP_NOT_POSSIBLE_FOR_PARTITIONBASED_BACKUP]="RBK0012E: DD-varmuuskopiota ei tueta osioperustaiselle varmuuskopiolle. Käytä normaalimoodia"
MSG_FR[$MSG_DD_BACKUP_NOT_POSSIBLE_FOR_PARTITIONBASED_BACKUP]="RBK0012E: DD Sauvegarde non prise en charge avec le mode basée sur les partitions. Utilisez le mode normal"
MSG_MULTIPLE_PARTITIONS_FOUND=13
MSG_EN[$MSG_MULTIPLE_PARTITIONS_FOUND]="RBK0013E: More than two partitions detected which can be saved only with backuptype DD or DDZ, with option -P or with option --ignoreAdditionalPartitions"
MSG_DE[$MSG_MULTIPLE_PARTITIONS_FOUND]="RBK0013E: Es existieren mehr als zwei Partitionen, die nur mit dem Backuptype DD oder DDZ, mit der Option -P oder der Option --ignoreAdditionalPartitions gesichert werden können"
MSG_FI[$MSG_MULTIPLE_PARTITIONS_FOUND]="RBK0013E: Enemmän kuin kansi osiota löytyi, jotka voidaan tallentaa vain DD- tai DDZ-varmuuskopiona. Käytä valintaa -P tai --ignoreAdditionalPartitions"
MSG_FR[$MSG_MULTIPLE_PARTITIONS_FOUND]="RBK0013E: Il y a plus de deux partitions elles ne peuvent être sauvegardées qu'avec le type de sauvegarde DD ou DDZ, avec l'option -P ou l'option --ignoreAdditionalPartitions"
MSG_EMAIL_PROG_NOT_SUPPORTED=14
MSG_EN[$MSG_EMAIL_PROG_NOT_SUPPORTED]="RBK0014E: eMail program %s not supported. Supported are %s"
MSG_DE[$MSG_EMAIL_PROG_NOT_SUPPORTED]="RBK0014E: eMail Programm %s ist nicht unterstützt. Möglich sind %s"
MSG_FI[$MSG_EMAIL_PROG_NOT_SUPPORTED]="RBK0014E: Sähköpostisovellusta %s ei tueta. Tuettuja ovat %s"
MSG_FR[$MSG_EMAIL_PROG_NOT_SUPPORTED]="RBK0014E: Le programme de messagerie %s n'est pas pris en charge. Sont pris en charge %s"
MSG_INSTANCE_ACTIVE=15
MSG_EN[$MSG_INSTANCE_ACTIVE]="RBK0015E: There is already an instance of $MYNAME up and running"
MSG_DE[$MSG_INSTANCE_ACTIVE]="RBK0015E: Es ist schon eine Instanz von $MYNAME aktiv"
MSG_FI[$MSG_INSTANCE_ACTIVE]="RBK0015E: $MYNAME on jo tällä hetkellä käynnissä"
MSG_FR[$MSG_INSTANCE_ACTIVE]="RBK0015E: Une instance de $MYNAME est déjà active"
MSG_NO_SDCARD_FOUND=16
MSG_EN[$MSG_NO_SDCARD_FOUND]="RBK0016E: No sd card %s found"
MSG_DE[$MSG_NO_SDCARD_FOUND]="RBK0016E: Keine SD Karte %s gefunden"
MSG_FI[$MSG_NO_SDCARD_FOUND]="RBK0016E: SD-korttia %s ei löytynyt"
MSG_FR[$MSG_NO_SDCARD_FOUND]="RBK0016E: Aucune carte SD %s trouvée"
MSG_BACKUP_OK=17
MSG_EN[$MSG_BACKUP_OK]="RBK0017I: Backup finished successfully"
MSG_DE[$MSG_BACKUP_OK]="RBK0017I: Backup erfolgreich beendet"
MSG_FI[$MSG_BACKUP_OK]="RBK0017I: Varmuuskopiointi suoritettu onnistuneesti"
MSG_FR[$MSG_BACKUP_OK]="RBK0017I: Sauvegarde terminée avec succès"
#MSG_ADJUSTING_WARNING2=18
#MSG_EN[$MSG_ADJUSTING_WARNING2]="RBK0018W: Target %s with %s is larger than backup source with %s. root partition will be expanded accordingly to use the whole space"
#MSG_DE[$MSG_ADJUSTING_WARNING2]="RBK0018W: Ziel %s mit %s ist größer als die Backupquelle mit %s. Die root Partition wird entsprechend vergrößert um den ganzen Platz zu benutzen"
#MSG_FI[$MSG_ADJUSTING_WARNING2]="RBK0018W: Kohde %s kooltaan %s, on suurempi kuin varmuuskopion lähde kooltaan %s. Juuriosio laajennetaan sen mukaisesti käyttämään koko tila"
#MSG_FR[$MSG_ADJUSTING_WARNING2]="RBK0018W: La cible %s avec %s, est plus grande que la source avec %s. la partition rootfs sera étendue pour utiliser tout l'espace"
MSG_MISSING_START_STOP=19
MSG_EN[$MSG_MISSING_START_STOP]="RBK0019E: Missing option -a and -o"
MSG_DE[$MSG_MISSING_START_STOP]="RBK0019E: Option -a und -o nicht angegeben"
MSG_FI[$MSG_MISSING_START_STOP]="RBK0019E: Valinnat -a ja -o puuttuvat"
MSG_FR[$MSG_MISSING_START_STOP]="RBK0019E: Options -a et -o non spécifiées"
MSG_FILESYSTEM_INCORRECT=20
MSG_EN[$MSG_FILESYSTEM_INCORRECT]="RBK0020E: Filesystem of rsync backup directory %s does not support %s"
MSG_DE[$MSG_FILESYSTEM_INCORRECT]="RBK0020E: Dateisystem des rsync Backupverzeichnisses %s unterstützt keine %s"
MSG_FI[$MSG_FILESYSTEM_INCORRECT]="RBK0020E: Rsync-varmuuskopiohakemiston %s tiedostojärjestelmä ei tue %s"
MSG_FR[$MSG_FILESYSTEM_INCORRECT]="RBK0020E: Le système des fichiers utilisés avec rsync %s n'est pas pris en charge %s"
MSG_BACKUP_PROGRAM_ERROR=21
MSG_EN[$MSG_BACKUP_PROGRAM_ERROR]="RBK0021E: Backupprogram for type %s failed with RC %s"
MSG_DE[$MSG_BACKUP_PROGRAM_ERROR]="RBK0021E: Backupprogramm des Typs %s beendete sich mit RC %s"
MSG_FI[$MSG_BACKUP_PROGRAM_ERROR]="RBK0021E: Tyypin %s varmuuskopiointisovellus epäonnistui, RC %s"
MSG_FR[$MSG_BACKUP_PROGRAM_ERROR]="RBK0021E: Sauvegarde de type %s terminé avec un Code Retour %s"
MSG_UNKNOWN_BACKUPTYPE=22
MSG_EN[$MSG_UNKNOWN_BACKUPTYPE]="RBK0022E: Unknown backuptype %s"
MSG_DE[$MSG_UNKNOWN_BACKUPTYPE]="RBK0022E: Unbekannter Backuptyp %s"
MSG_FI[$MSG_UNKNOWN_BACKUPTYPE]="RBK0022E: Tuntematon varmuuskopiotyyppi %s"
MSG_FR[$MSG_UNKNOWN_BACKUPTYPE]="RBK0022E: Type de sauvegarde inconnu %s"
MSG_KEEPBACKUP_INVALID=23
MSG_EN[$MSG_KEEPBACKUP_INVALID]="RBK0023E: Invalid parameter %s for %s detected"
MSG_DE[$MSG_KEEPBACKUP_INVALID]="RBK0023E: Ungültiger Parameter %s für -k eingegeben"
MSG_FI[$MSG_KEEPBACKUP_INVALID]="RBK0023E: Havaittu epäkelpo parametri %s kohteelle %s"
MSG_FR[$MSG_KEEPBACKUP_INVALID]="RBK0023E: Paramètre %s non valide pour -k %s"
MSG_TOOL_ERROR=24
MSG_EN[$MSG_TOOL_ERROR]="RBK0024E: Backup tool %s received error %s. Errormessages:$NL%s"
MSG_DE[$MSG_TOOL_ERROR]="RBK0024E: Backupprogramm %s hat einen Fehler %s bekommen. Fehlermeldungen:$NL%s"
MSG_FI[$MSG_TOOL_ERROR]="RBK0024E: Varmuuskopiointityökalu %s vastaanotti virheen %s. Virheviestit:$NL%s"
MSG_FR[$MSG_TOOL_ERROR]="RBK0024E: Une erreur lors de la sauvegarde %s s'est produite %s. Message:$NL%s"
MSG_DIR_TO_BACKUP_DOESNOTEXIST=25
MSG_EN[$MSG_DIR_TO_BACKUP_DOESNOTEXIST]="RBK0025E: Backupdirectory %s does not exist"
MSG_DE[$MSG_DIR_TO_BACKUP_DOESNOTEXIST]="RBK0025E: Backupverzeichnis %s existiert nicht"
MSG_FI[$MSG_DIR_TO_BACKUP_DOESNOTEXIST]="RBK0025E: Varmuuskopiohakemistoa %s ei ole"
MSG_FR[$MSG_DIR_TO_BACKUP_DOESNOTEXIST]="RBK0025E: Le répertoire de sauvegarde %s n'existe pas"
MSG_SAVED_LOG=26
MSG_EN[$MSG_SAVED_LOG]="RBK0026I: Debug logfile saved in %s"
MSG_DE[$MSG_SAVED_LOG]="RBK0026I: Debug Logdatei wurde in %s gesichert"
MSG_FI[$MSG_SAVED_LOG]="RBK0026I: Vianmäärityksen lokitiedosto tallennettu kohteeseen %s"
MSG_FR[$MSG_SAVED_LOG]="RBK0026I: Le fichier journal de débogage a été enregistré sous %s"
MSG_NO_DEVICEMOUNTED=27
MSG_EN[$MSG_NO_DEVICEMOUNTED]="RBK0027E: No external device mounted on %s. root partition would be used for the backup"
MSG_DE[$MSG_NO_DEVICEMOUNTED]="RBK0027E: Kein externes Gerät an %s verbunden. Die root Partition würde für das Backup benutzt werden"
MSG_FI[$MSG_NO_DEVICEMOUNTED]="RBK0027E: Ulkoista laitetta ei ole otettu käyttöön kohteessa %s. Juuriosiota käytetään varmuuskopiointiin"
MSG_FR[$MSG_NO_DEVICEMOUNTED]="RBK0027E: Aucun périphérique externe monté sur %s. la partition racine sera utilisée pour la sauvegarde"
MSG_RESTORE_DIRECTORY_NO_DIRECTORY=28
MSG_EN[$MSG_RESTORE_DIRECTORY_NO_DIRECTORY]="RBK0028E: %s is no backup directory of $MYNAME"
MSG_DE[$MSG_RESTORE_DIRECTORY_NO_DIRECTORY]="RBK0028E: %s ist kein Wiederherstellungsverzeichnis von $MYNAME"
MSG_FI[$MSG_RESTORE_DIRECTORY_NO_DIRECTORY]="RBK0028E: %s ei ole kohteen $MYNAME varmuuskopiohakemisto"
MSG_FR[$MSG_RESTORE_DIRECTORY_NO_DIRECTORY]="RBK0028E: %s n'est pas un répertoire de restauration pour $MYNAME"
MSG_MPACK_NOT_INSTALLED=29
MSG_EN[$MSG_MPACK_NOT_INSTALLED]="RBK0029E: Mail program mpack not installed to send emails. No log can be attached to the eMail"
MSG_DE[$MSG_MPACK_NOT_INSTALLED]="RBK0029E: Mail Program mpack is nicht installiert. Es kann kein Log an die eMail angehängt werden"
MSG_FI[$MSG_MPACK_NOT_INSTALLED]="RBK0029E: Sähköpostisovellusta mpack ei ole asennettu sähköpostien lähetykseen. Lokitiedostoa ei voitu liittää sähköpostiin"
MSG_FR[$MSG_MPACK_NOT_INSTALLED]="RBK0029E: Le programme de messagerie mpack n'est pas installé. Aucune pièce jointe ne peut être ajoutée à l'e-mail"
MSG_IMG_DD_FAILED=30
MSG_EN[$MSG_IMG_DD_FAILED]="RBK0030E: %s file creation with dd failed with RC %s"
MSG_DE[$MSG_IMG_DD_FAILED]="RBK0030E: %s Datei Erzeugung mit dd endet fehlerhaft mit RC %s"
MSG_FI[$MSG_IMG_DD_FAILED]="RBK0030E: Tiedoston %s luonti dd:llä epäonnistui, RC %s"
MSG_FR[$MSG_IMG_DD_FAILED]="RBK0030E: La création du fichier %s avec dd s'est terminé avec un code d'erreur: %s"
MSG_CHECKING_FOR_NEW_VERSION=31
MSG_EN[$MSG_CHECKING_FOR_NEW_VERSION]="RBK0031I: Checking whether a new version of $MYSELF is available"
MSG_DE[$MSG_CHECKING_FOR_NEW_VERSION]="RBK0031I: Prüfe ob eine neue Version von $MYSELF verfügbar ist"
MSG_FI[$MSG_CHECKING_FOR_NEW_VERSION]="RBK0031I: Tarkistetaan, onko $MYSELF uusia versioita saatavilla"
MSG_FR[$MSG_CHECKING_FOR_NEW_VERSION]="RBK0031I: Vérifiez si une nouvelle version de $MYSELF est disponible"
MSG_INVALID_LOG_LEVEL=32
MSG_EN[$MSG_INVALID_LOG_LEVEL]="RBK0032E: Invalid parameter '%s' for option -l detected"
MSG_DE[$MSG_INVALID_LOG_LEVEL]="RBK0032E: Ungültiger Parameter '%s' für Option -l eingegeben"
MSG_FI[$MSG_INVALID_LOG_LEVEL]="RBK0032E: Havaittu epäkelpo parametri '%s' valinnalle -l"
MSG_FR[$MSG_INVALID_LOG_LEVEL]="RBK0032E: Paramètre non valide '%s' pour l'option -l"
MSG_CLEANING_UP=33
MSG_EN[$MSG_CLEANING_UP]="RBK0033I: Please wait until cleanup has finished"
MSG_DE[$MSG_CLEANING_UP]="RBK0033I: Bitte warten bis aufgeräumt wurde"
MSG_FI[$MSG_CLEANING_UP]="RBK0033I: Ole hyvä ja odota, kunnes puhdistus on valmistunut"
MSG_FR[$MSG_CLEANING_UP]="RBK0033I: Veuillez patienter jusqu'à la fin du nettoyage"
MSG_FILE_NOT_FOUND=34
MSG_EN[$MSG_FILE_NOT_FOUND]="RBK0034E: File %s not found"
MSG_DE[$MSG_FILE_NOT_FOUND]="RBK0034E: Datei %s nicht gefunden"
MSG_FI[$MSG_FILE_NOT_FOUND]="RBK0034E: Tiedostoa %s ei löytynyt"
MSG_FR[$MSG_FILE_NOT_FOUND]="RBK0034E: Fichier %s introuvable"
MSG_RESTORE_PROGRAM_ERROR=35
MSG_EN[$MSG_RESTORE_PROGRAM_ERROR]="RBK0035E: Backupprogram %s failed during restore with RC %s"
MSG_DE[$MSG_RESTORE_PROGRAM_ERROR]="RBK0035E: Backupprogramm %s endete beim Restore mit RC %s"
MSG_FI[$MSG_RESTORE_PROGRAM_ERROR]="RBK0035E: Varmuuskopiointisovellus %s epäonnistui palautuksen aikana, RC %s"
MSG_FR[$MSG_RESTORE_PROGRAM_ERROR]="RBK0035E: La sauvegarde %s a été interrompue avec le code erreur %s"
MSG_BACKUP_CREATING_PARTITION_INFO=36
MSG_EN[$MSG_BACKUP_CREATING_PARTITION_INFO]="RBK0036I: Saving partition layout"
MSG_DE[$MSG_BACKUP_CREATING_PARTITION_INFO]="RBK0036I: Partitionslayout wird gesichert"
MSG_FI[$MSG_BACKUP_CREATING_PARTITION_INFO]="RBK0036I: Tallennetaan osioasettelua"
MSG_FR[$MSG_BACKUP_CREATING_PARTITION_INFO]="RBK0036I: Sauvegarde de la disposition de la partition"
MSG_ANSWER_CHARS_YES=37
MSG_EN[$MSG_ANSWER_CHARS_YES]="Yy"
MSG_DE[$MSG_ANSWER_CHARS_YES]="Jj"
MSG_FI[$MSG_ANSWER_CHARS_YES]="Kk"
MSG_FR[$MSG_ANSWER_CHARS_YES]="Oo"
MSG_ARE_YOU_SURE=38
MSG_EN[$MSG_ARE_YOU_SURE]="RBK0038I: Are you sure? %s "
MSG_DE[$MSG_ARE_YOU_SURE]="RBK0038I: Bist Du sicher? %s "
MSG_FI[$MSG_ARE_YOU_SURE]="RBK0038I: Oletko varma? %s "
MSG_FR[$MSG_ARE_YOU_SURE]="RBK0038I: Etes vous sûre? %s "
MSG_MAILPROGRAM_NOT_INSTALLED=39
MSG_EN[$MSG_MAILPROGRAM_NOT_INSTALLED]="RBK0039E: Mail program %s not installed to send emails"
MSG_DE[$MSG_MAILPROGRAM_NOT_INSTALLED]="RBK0039E: Mail Program %s ist nicht installiert um eMail zu senden"
MSG_FI[$MSG_MAILPROGRAM_NOT_INSTALLED]="RBK0039E: Sähköpostisovellusta %s ei ole asennettu sähkökpostien lähettämiseen"
MSG_FR[$MSG_MAILPROGRAM_NOT_INSTALLED]="RBK0039E: Le programme de messagerie %s n'est pas installé pour envoyer des e-mails"
MSG_INCOMPATIBLE_UPDATE=40
MSG_EN[$MSG_INCOMPATIBLE_UPDATE]="RBK0040W: New version %s has some incompatibilities to previous versions. Please read %s and use option -S together with option -U to update script"
MSG_DE[$MSG_INCOMPATIBLE_UPDATE]="RBK0040W: Die neue Version %s hat inkompatible Änderungen zu vorhergehenden Versionen. Bitte %s lesen und dann die Option -S zusammen mit -U benutzen um das Script zu updaten"
MSG_FI[$MSG_INCOMPATIBLE_UPDATE]="RBK0040W: Uusi versio %s ei ole täysin yhteensopiva edellisen version kanssa. Ole hyvä ja lue %s ja käytä valintaa -S yhdessä valinnan -U kanssa päivittääksesi skriptin"
MSG_FR[$MSG_INCOMPATIBLE_UPDATE]="RBK0040W: La nouvelle version %s présente des incompatibilités avec les versions précédentes. Veuillez lire %s et utilisez les options -S et -U pour mettre à jour le script"
MSG_TITLE_OK=41
MSG_EN[$MSG_TITLE_OK]="%s: Backup finished successfully"
MSG_DE[$MSG_TITLE_OK]="%s: Backup erfolgreich beendet"
MSG_FI[$MSG_TITLE_OK]="%s: Varmuuskopiointi suoritettu onnistuneesti"
MSG_FR[$MSG_TITLE_OK]="%s: Sauvegarde terminée avec succès"
MSG_TITLE_ERROR=42
MSG_EN[$MSG_TITLE_ERROR]="%s: Backup failed !!!"
MSG_DE[$MSG_TITLE_ERROR]="%s: Backup nicht erfolgreich !!!"
MSG_FI[$MSG_TITLE_ERROR]="%s: Varmuuskopiointi epäonnistui !!!"
MSG_FR[$MSG_TITLE_ERROR]="%s: Échec de la sauvegarde !!!"
MSG_REMOVING_BACKUP=43
MSG_EN[$MSG_REMOVING_BACKUP]="RBK0043I: Removing incomplete backup in %s. This may take some time. Please be patient"
MSG_DE[$MSG_REMOVING_BACKUP]="RBK0043I: Unvollständiges Backup in %s wird gelöscht. Das kann etwas dauern. Bitte Geduld"
MSG_FI[$MSG_REMOVING_BACKUP]="RBK0043I: Poistetaan keskeneräinen varmuuskopio kohteessa %s. Tämä saattaa kestää jonkin aikaa. Ole hyvä ja odota"
MSG_FR[$MSG_REMOVING_BACKUP]="RBK0043I: Suppression en cours des sauvegardes incomplètes %s. Cela peut prendre du temps, SVP soyez patient"
MSG_CREATING_BOOT_BACKUP=44
MSG_EN[$MSG_CREATING_BOOT_BACKUP]="RBK0044I: Creating backup of boot partition in %s"
MSG_DE[$MSG_CREATING_BOOT_BACKUP]="RBK0044I: Backup der Bootpartition wird in %s erstellt"
MSG_FI[$MSG_CREATING_BOOT_BACKUP]="RBK0044I: Luodaan varmuuskopiota kohteeseen %s"
MSG_FR[$MSG_CREATING_BOOT_BACKUP]="RBK0044I: La partition de boot sera sauvegardée en %s"
MSG_CREATING_PARTITION_BACKUP=45
MSG_EN[$MSG_CREATING_PARTITION_BACKUP]="RBK0045I: Creating backup of partition layout in %s"
MSG_DE[$MSG_CREATING_PARTITION_BACKUP]="RBK0044I: Backup des Partitionlayouts wird in %s erstellt"
MSG_FI[$MSG_CREATING_PARTITION_BACKUP]="RBK0045I: Luodaan varmuuskopiota osioasettelusta kohteeseen %s"
MSG_FR[$MSG_CREATING_PARTITION_BACKUP]="RBK0045I: La disposition de la partition sera sauvegardée sous %s"
MSG_CREATING_MBR_BACKUP=46
MSG_EN[$MSG_CREATING_MBR_BACKUP]="RBK0046I: Creating backup of master boot record in %s"
MSG_DE[$MSG_CREATING_MBR_BACKUP]="RBK0046I: Backup des Masterbootrecords wird in %s erstellt"
MSG_FI[$MSG_CREATING_MBR_BACKUP]="RBK0046I: Luodaan varmuuskopiota Master Boot Recordista kohteeseen %s"
MSG_FR[$MSG_CREATING_MBR_BACKUP]="RBK0046I: Le MBR, Master Boot Record, est sauvegardé sous %s"
MSG_START_SERVICES_FAILED=47
MSG_EN[$MSG_START_SERVICES_FAILED]="RBK0047W: Error occured when starting services. RC %s"
MSG_DE[$MSG_START_SERVICES_FAILED]="RBK0047W: Ein Fehler trat beim Starten von Services auf. RC %s"
MSG_FI[$MSG_START_SERVICES_FAILED]="RBK0047W: Virhe palveluita käynnistäessä. RC %s"
MSG_FR[$MSG_START_SERVICES_FAILED]="RBK0047W: Une erreur avec le code %s s'est produite lors du démarrage des services"
MSG_STOP_SERVICES_FAILED=48
MSG_EN[$MSG_STOP_SERVICES_FAILED]="RBK0048E: Error occured when stopping services. RC %s"
MSG_DE[$MSG_STOP_SERVICES_FAILED]="RBK0048E: Ein Fehler trat beim Beenden von Services auf. RC %s"
MSG_FI[$MSG_STOP_SERVICES_FAILED]="RBK0048E: Virhe palveluita pysäytettäessä. RC %s"
MSG_FR[$MSG_STOP_SERVICES_FAILED]="RBK0048E: Une erreur code %s s'est produite lors de l'arrêt des services"
#MSG_SAVED_LOG_SYSLOG=49
#MSG_EN[$MSG_SAVED_LOG_SYSLOG]="RBK0049I: Messages saved in %s"
#MSG_DE[$MSG_SAVED_LOG_SYSLOG]="RBK0049I: Meldungen wurden in %s gesichert"
#MSG_FI[$MSG_SAVED_LOG_SYSLOG]="RBK0049I: Viestit tallennettu kohteeseen %s"
#MSG_FR[$MSG_SAVED_LOG_SYSLOG]="RBK0049I: Les messages ont été enregistrés sous %s"
MSG_RESTORING_FILE=50
MSG_EN[$MSG_RESTORING_FILE]="RBK0050I: Restoring backup from %s"
MSG_DE[$MSG_RESTORING_FILE]="RBK0050I: Backup wird von %s zurückgespielt"
MSG_FI[$MSG_RESTORING_FILE]="RBK0050I: Palautetaan varmuuskopiota kohteesta %s"
MSG_FR[$MSG_RESTORING_FILE]="RBK0050I: Restauration en cours à partir de %s"
MSG_TARGET_REQUIRES_GPT=51
MSG_EN[$MSG_TARGET_REQUIRES_GPT]="RBK0051W: Target %s with %s is larger than 2TB and requires gpt instead of mbr. Otherwise only 2TB will be used"
MSG_DE[$MSG_TARGET_REQUIRES_GPT]="RBK0051W: Ziel %s mit %s ist größer als 2TB und erfordert gpt statt mbr. Ansonsten werden nur 2TB genutzt"
MSG_FI[$MSG_TARGET_REQUIRES_GPT]="RBK0051W: Kohde %s kooltaan %s, on suurempi kuin 2Tt ja vaatii mbr:n sijasta gpt:n. Muutoin vain 2Tt voidaan käyttää"
MSG_FR[$MSG_TARGET_REQUIRES_GPT]="RBK0051W: La cible %s avec %s, est supérieure à 2 To et nécessite GPT au lieu de MBR. Sinon, seuls 2 To seront utilisés"
MSG_CREATING_PARTITIONS=52
MSG_EN[$MSG_CREATING_PARTITIONS]="RBK0052W: Creating partitions on %s"
MSG_DE[$MSG_CREATING_PARTITIONS]="RBK0052W: Partitionen werden auf %s erstellt"
MSG_FI[$MSG_CREATING_PARTITIONS]="RBK0052W: Luodaan osioita kohteelle %s"
MSG_FR[$MSG_CREATING_PARTITIONS]="RBK0052W: Les partitions seront créées sur %s"
MSG_RESTORING_FIRST_PARTITION=53
MSG_EN[$MSG_RESTORING_FIRST_PARTITION]="RBK0053I: Restoring first partition (boot partition) to %s"
MSG_DE[$MSG_RESTORING_FIRST_PARTITION]="RBK0053I: Erste Partition (Bootpartition) wird auf %s zurückgespielt"
MSG_FI[$MSG_RESTORING_FIRST_PARTITION]="RBK0053I: Palautetaan ensimmäistä osoita (käynnistysosio) kohteesen %s"
MSG_FR[$MSG_RESTORING_FIRST_PARTITION]="RBK0053I: La première partition (boot) sera restaurée vers %s"
MSG_FORMATTING_SECOND_PARTITION=54
MSG_EN[$MSG_FORMATTING_SECOND_PARTITION]="RBK0054I: Formating second partition (root partition) %s"
MSG_DE[$MSG_FORMATTING_SECOND_PARTITION]="RBK0054I: Zweite Partition (Rootpartition) %s wird formatiert"
MSG_FI[$MSG_FORMATTING_SECOND_PARTITION]="RBK0054I: Alustetaan toista osiota (juuriosio) %s"
MSG_FR[$MSG_FORMATTING_SECOND_PARTITION]="RBK0054I: La deuxième partition (partition root) %s sera formatée"
MSG_RESTORING_SECOND_PARTITION=55
MSG_EN[$MSG_RESTORING_SECOND_PARTITION]="RBK0055I: Restoring second partition (root partition) to %s"
MSG_DE[$MSG_RESTORING_SECOND_PARTITION]="RBK0055I: Zweite Partition (Rootpartition) wird auf %s zurückgespielt"
MSG_FI[$MSG_RESTORING_SECOND_PARTITION]="RBK0055I: Palautetaan toista osiota (juuriosio) kohteeseen %s"
MSG_FR[$MSG_RESTORING_SECOND_PARTITION]="RBK0055I: La deuxième partition (partition root) sera restaurée sur %s"
MSG_DEPLOYMENT_PARMS_ERROR=56
MSG_EN[$MSG_DEPLOYMENT_PARMS_ERROR]="RBK0056E: Incorrect deployment parameters. Use @"
MSG_DE[$MSG_DEPLOYMENT_PARMS_ERROR]="RBK0056E: Ungültige Deploymentparameter. Erforderliches Format: @"
MSG_FI[$MSG_DEPLOYMENT_PARMS_ERROR]="RBK0056E: Virheelliset käyttöönottoparametrit. Käytä @"
MSG_FR[$MSG_DEPLOYMENT_PARMS_ERROR]="RBK0056E: Paramètres de déploiement invalides. Format requis : @"
MSG_DOWNLOADING=57
MSG_EN[$MSG_DOWNLOADING]="RBK0057I: Downloading file %s from %s"
MSG_DE[$MSG_DOWNLOADING]="RBK0057I: Datei %s wird von %s downloaded"
MSG_FI[$MSG_DOWNLOADING]="RBK0057I: Ladataan tiedosta %s kohteesta %s"
MSG_FR[$MSG_DOWNLOADING]="RBK0057I: Téléchargement du fichier %s depuis %s"
MSG_INVALID_MSG_LEVEL=58
MSG_EN[$MSG_INVALID_MSG_LEVEL]="RBK0058E: Invalid parameter '%s' for option -m detected"
MSG_DE[$MSG_INVALID_MSG_LEVEL]="RBK0058E: Ungültiger Parameter '%s' für Option -m eingegeben"
MSG_FI[$MSG_INVALID_MSG_LEVEL]="RBK0058E: Havaittu epäkelpo parametri '%s' valinnalle -m"
MSG_FR[$MSG_INVALID_MSG_LEVEL]="RBK0058E: Paramètre invalide '%s' entré pour l'option -m"
#MSG_INVALID_LOG_OUTPUT=59
#MSG_EN[$MSG_INVALID_LOG_OUTPUT]="RBK0059W: Invalid parameter '%s' for option -L detected"
#MSG_DE[$MSG_INVALID_LOG_OUTPUT]="RBK0059W: Ungültiger Parameter '%s' für Option -L eingegeben"
#MSG_FI[$MSG_INVALID_LOG_OUTPUT]="RBK0059W: Havaittu epäkelpo parametri '%s' valinnalle -L"
#MSG_FR[$MSG_INVALID_LOG_OUTPUT]="RBK0059W: Paramètre invalide '%s' entré pour l'option -L"
MSG_NO_YES=60
MSG_EN[$MSG_NO_YES]="no yes"
MSG_DE[$MSG_NO_YES]="nein ja"
MSG_FI[$MSG_NO_YES]="ei kyllä"
MSG_FR[$MSG_NO_YES]="non oui"
MSG_BOOTPATITIONFILES_NOT_FOUND=61
MSG_EN[$MSG_BOOTPATITIONFILES_NOT_FOUND]="RBK0061E: Unable to find bootpartition files %s starting with %s"
MSG_DE[$MSG_BOOTPATITIONFILES_NOT_FOUND]="RBK0061E: Keine Bootpartitionsdateien in %s gefunden die mit %s beginnen"
MSG_FI[$MSG_BOOTPATITIONFILES_NOT_FOUND]="RBK0061E: Ei voida löytää käynnsitysosion tiedostoja %s, jotka alkavat %s"
MSG_FR[$MSG_BOOTPATITIONFILES_NOT_FOUND]="RBK0061E: Fichiers de partition de boot %s , commençant par %s introuvables"
MSG_NO_RESTOREDEVICE_DEFINED=62
MSG_EN[$MSG_NO_RESTOREDEVICE_DEFINED]="RBK0062E: No restoredevice defined (Example: /dev/sda)"
MSG_DE[$MSG_NO_RESTOREDEVICE_DEFINED]="RBK0062E: Kein Zurückspielgerät ist definiert (Beispiel: /dev/sda)"
MSG_FI[$MSG_NO_RESTOREDEVICE_DEFINED]="RBK0062E: Palautuslaitetta ei ole määritetty (Esimerkki: /dev/sda)"
MSG_FR[$MSG_NO_RESTOREDEVICE_DEFINED]="RBK0062E: Aucun périphérique de lecture défini (exemple:/dev/sda)"
MSG_NO_RESTOREDEVICE_FOUND=63
MSG_EN[$MSG_NO_RESTOREDEVICE_FOUND]="RBK0063E: Restoredevice %s not found (Example: /dev/sda)"
MSG_DE[$MSG_NO_RESTOREDEVICE_FOUND]="RBK0063E: Zurückspielgerät %s existiert nicht (Beispiel: /dev/sda)"
MSG_FI[$MSG_NO_RESTOREDEVICE_FOUND]="RBK0063E: Palautuslaitetta %s ei löytynyt (Esimerkki: /dev/sda)"
MSG_FR[$MSG_NO_RESTOREDEVICE_FOUND]="RBK0063E: Périphérique de restauration %s introuvable (ex:/dev/sda)"
MSG_ROOT_PARTTITION_NOT_FOUND=64
MSG_EN[$MSG_ROOT_PARTTITION_NOT_FOUND]="RBK0064E: Partition for rootpartition %s not found (Example: /dev/sdb1)"
MSG_DE[$MSG_ROOT_PARTTITION_NOT_FOUND]="RBK0064E: Partition für die Rootpartition %s nicht gefunden (Beispiel: /dev/sda)"
MSG_FI[$MSG_ROOT_PARTTITION_NOT_FOUND]="RBK0064E: Osiota juuriosiolle %s ei löytynyt (Esimerkki: /dev/sdb1)"
MSG_FR[$MSG_ROOT_PARTTITION_NOT_FOUND]="RBK0064E: La partition Root %s est introuvable (exemple:/dev/sdb1)"
MSG_REPARTITION_WARNING=65
MSG_EN[$MSG_REPARTITION_WARNING]="RBK0065W: Device %s will be repartitioned and all data will be lost"
MSG_DE[$MSG_REPARTITION_WARNING]="RBK0065W: Gerät %s wird repartitioniert und die gesamten Daten werden gelöscht"
MSG_FI[$MSG_REPARTITION_WARNING]="RBK0065W: Laite %s osioidaan uudelleen ja kaikki tieto hävitetään"
MSG_FR[$MSG_REPARTITION_WARNING]="RBK0065W: Le périphérique %s sera repartitionné, toutes les données seront perdues"
MSG_WARN_RESTORE_DEVICE_OVERWRITTEN=66
MSG_EN[$MSG_WARN_RESTORE_DEVICE_OVERWRITTEN]="RBK0066I: Device %s will be overwritten with the saved boot and root partition"
MSG_DE[$MSG_WARN_RESTORE_DEVICE_OVERWRITTEN]="RBK0066I: Gerät %s wird überschrieben mit der gesicherten Boot- und Rootpartition"
MSG_FI[$MSG_WARN_RESTORE_DEVICE_OVERWRITTEN]="RBK0066I: Laite %s ylikirjoitetaan tallennetuilla käynnistys- ja juuriosioilla"
MSG_FR[$MSG_WARN_RESTORE_DEVICE_OVERWRITTEN]="RBK0066I: Le périphérique %s sera écrasé par la procédure de boot et la partition Root"
MSG_CURRENT_PARTITION_TABLE=67
MSG_EN[$MSG_CURRENT_PARTITION_TABLE]="RBK0067I: Current partitions on %s:"
MSG_DE[$MSG_CURRENT_PARTITION_TABLE]="RBK0067I: Momentane Partitionen auf %s:"
MSG_FI[$MSG_CURRENT_PARTITION_TABLE]="RBK0067I: Nykyiset osiot kohteella %s:"
MSG_FR[$MSG_CURRENT_PARTITION_TABLE]="RBK0067I: Partitions actuelles sur %s:"
MSG_BOOTPATITIONFILES_FOUND=68
MSG_EN[$MSG_BOOTPATITIONFILES_FOUND]="RBK0068I: Using bootpartition backup files starting with %s from directory %s"
MSG_DE[$MSG_BOOTPATITIONFILES_FOUND]="RBK0068I: Bootpartitionsdateien des Backups aus dem Verzeichnis %s die mit %s beginnen werden benutzt"
MSG_FI[$MSG_BOOTPATITIONFILES_FOUND]="RBK0068I: Käytetään käynnistysosion %s alkavia varmuuskopiointitiedostoja hakemistosta %s"
MSG_FR[$MSG_BOOTPATITIONFILES_FOUND]="RBK0068I: Les fichiers sauvegardés pour le boot commençant par %s dans le répertoire %s sont utilisés"
MSG_WARN_BOOT_PARTITION_OVERWRITTEN=69
MSG_EN[$MSG_WARN_BOOT_PARTITION_OVERWRITTEN]="RBK0069I: Bootpartition %s will be formatted and will get the restored Boot partition"
MSG_DE[$MSG_WARN_BOOT_PARTITION_OVERWRITTEN]="RBK0069I: Bootpartition %s wird formatiert und erhält die zurückgespielte Bootpartition"
MSG_FI[$MSG_WARN_BOOT_PARTITION_OVERWRITTEN]="RBK0069I: Käynnistysosio %s alustetaan ja sille palautetaan varmuuskopioitu käynnistysosio"
MSG_FR[$MSG_WARN_BOOT_PARTITION_OVERWRITTEN]="RBK0069I: La partition de boot %s sera formatée pour recevoir la partition de boot restaurée"
MSG_WARN_ROOT_PARTITION_OVERWRITTEN=70
MSG_EN[$MSG_WARN_ROOT_PARTITION_OVERWRITTEN]="RBK0070I: Rootpartition %s will be formatted and will get the restored Root partition"
MSG_DE[$MSG_WARN_ROOT_PARTITION_OVERWRITTEN]="RBK0070I: Rootpartition %s wird formatiert und erhält die zurückgespielte Rootpartition"
MSG_FI[$MSG_WARN_ROOT_PARTITION_OVERWRITTEN]="RBK0070I: Juuriosio %s alustetaan ja sille palautetaan varmuuskopioitu juuriosio"
MSG_FR[$MSG_WARN_ROOT_PARTITION_OVERWRITTEN]="RBK0070I: La partition Root %s sera formatée et restaurée avec la partition sauvegardée"
MSG_QUERY_CHARS_YES_NO=71
MSG_EN[$MSG_QUERY_CHARS_YES_NO]="y/N"
MSG_DE[$MSG_QUERY_CHARS_YES_NO]="j/N"
MSG_FI[$MSG_QUERY_CHARS_YES_NO]="k/E"
MSG_FR[$MSG_QUERY_CHARS_YES_NO]="o/N"
MSG_SCRIPT_UPDATE_OK=72
MSG_EN[$MSG_SCRIPT_UPDATE_OK]="RBK0072I: %s updated from version %s to version %s. Previous version saved as %s. Don't forget to test backup and restore with the new version now"
MSG_DE[$MSG_SCRIPT_UPDATE_OK]="RBK0072I: %s von Version %s durch die aktuelle Version %s ersetzt. Die vorherige Version wurde als %s gesichert. Nicht vergessen den Backup und Restore mit der neuen Version zu testen"
MSG_FI[$MSG_SCRIPT_UPDATE_OK]="RBK0072I: %s on päivitetty versiosta %s versioon %s. Edellinen versio on tallennettu nimellä %s. Muista testata varmuuskopiointi ja palautus uudella versiolla"
MSG_FR[$MSG_SCRIPT_UPDATE_OK]="RBK0072I: %s mis à jour de la version %s à la version %s La version précédente a été sauvée sous %s. N'oubliez pas de tester une sauvegarde suivi d'une restauration avec cette version"
MSG_SCRIPT_UPDATE_NOT_NEEDED=73
MSG_EN[$MSG_SCRIPT_UPDATE_NOT_NEEDED]="RBK0073I: %s already current with version %s"
MSG_DE[$MSG_SCRIPT_UPDATE_NOT_NEEDED]="RBK0073I: %s bereits auf der aktuellen Version %s"
MSG_FI[$MSG_SCRIPT_UPDATE_NOT_NEEDED]="RBK0073I: %s on jo ajantasalla version %s kanssa"
MSG_FR[$MSG_SCRIPT_UPDATE_NOT_NEEDED]="RBK0073I: %s est déjà à jour avec la version %s"
MSG_SCRIPT_UPDATE_FAILED=74
MSG_EN[$MSG_SCRIPT_UPDATE_FAILED]="RBK0074E: Failed to update %s"
MSG_DE[$MSG_SCRIPT_UPDATE_FAILED]="RBK0074E: %s konnte nicht ersetzt werden"
MSG_FI[$MSG_SCRIPT_UPDATE_FAILED]="RBK0074E: %s päivitys epäonnistui"
MSG_FR[$MSG_SCRIPT_UPDATE_FAILED]="RBK0074E: Échec de la mise à jour de %s"
MSG_LINK_BOOTPARTITIONFILES=75
MSG_EN[$MSG_LINK_BOOTPARTITIONFILES]="RBK0075I: Using hardlinks to reuse bootpartition backups"
MSG_DE[$MSG_LINK_BOOTPARTITIONFILES]="RBK0075I: Hardlinks werden genutzt um Bootpartitionsbackups wiederzuverwenden"
MSG_FI[$MSG_LINK_BOOTPARTITIONFILES]="RBK0075I: Käytetään hardlink-tietoja käynnistysosion varmuuskopioihin. "
MSG_FR[$MSG_LINK_BOOTPARTITIONFILES]="RBK0075I: Les liens physiques sont utilisés pour réutiliser les sauvegardes de la partition de Boot"
MSG_RESTORE_OK=76
MSG_EN[$MSG_RESTORE_OK]="RBK0076I: Restore finished successfully"
MSG_DE[$MSG_RESTORE_OK]="RBK0076I: Restore erfolgreich beendet"
MSG_FI[$MSG_RESTORE_OK]="RBK0076I: Palautus suoritettu onnistuneesti"
MSG_FR[$MSG_RESTORE_OK]="RBK0076I: Restauration terminée avec succès"
MSG_RESTORE_FAILED=77
MSG_EN[$MSG_RESTORE_FAILED]="RBK0077E: Restore failed. Check previous error messages"
MSG_DE[$MSG_RESTORE_FAILED]="RBK0077E: Restore wurde fehlerhaft beendet. Siehe vorhergehende Fehlermeldungen"
MSG_FI[$MSG_RESTORE_FAILED]="RBK0077E: Palautus epäonnistui. Katso edelliest virheilmoitukset"
MSG_FR[$MSG_RESTORE_FAILED]="RBK0077E: La restauration a échoué. Vérifier les messages d'erreur"
MSG_BACKUP_TIME=78
MSG_EN[$MSG_BACKUP_TIME]="RBK0078I: Backup time: %s:%s:%s"
MSG_DE[$MSG_BACKUP_TIME]="RBK0078I: Backupzeit: %s:%s:%s"
MSG_FI[$MSG_BACKUP_TIME]="RBK0078I: Varmuuskopiointiin kulunut aika: %s:%s:%s"
MSG_FR[$MSG_BACKUP_TIME]="RBK0078I: Temps de sauvegarde: %s:%s:%s"
MSG_UNKNOWN_BACKUPTYPE_FOR_ZIP=79
MSG_EN[$MSG_UNKNOWN_BACKUPTYPE_FOR_ZIP]="RBK0079E: Option -z not allowed with backuptype %s"
MSG_DE[$MSG_UNKNOWN_BACKUPTYPE_FOR_ZIP]="RBK0079E: Option -z ist für Backuptyp %s nicht erlaubt"
MSG_FI[$MSG_UNKNOWN_BACKUPTYPE_FOR_ZIP]="RBK0079E: Valintaa -z ei voi käyttää varmuuskopiointityyppi %s:n kanssa"
MSG_FR[$MSG_UNKNOWN_BACKUPTYPE_FOR_ZIP]="RBK0079E: Option -z non autorisée avec ce type de sauvegarde %s"
MSG_NEW_VERSION_AVAILABLE=80
MSG_EN[$MSG_NEW_VERSION_AVAILABLE]="RBK0080I: $SMILEY_UPDATE_POSSIBLE There is a new version %s of $MYNAME available for download. You are running version %s and now can use option -U to upgrade your local version"
MSG_DE[$MSG_NEW_VERSION_AVAILABLE]="RBK0080I: $SMILEY_UPDATE_POSSIBLE Es gibt eine neue Version %s von $MYNAME zum downloaden. Die momentan benutze Version ist %s und es kann mit der Option -U die lokale Version aktualisiert werden"
MSG_FI[$MSG_NEW_VERSION_AVAILABLE]="RBK0080I: $SMILEY_UPDATE_POSSIBLE Uusi versio %s kohteesta $MYNAME on saatavilla. Käytät versiota %s ja voit käyttää valintaa -U päivittääksesi paikallisen version"
MSG_FR[$MSG_NEW_VERSION_AVAILABLE]="RBK0080I: $SMILEY_UPDATE_POSSIBLE Une nouvelle version %s de $MYNAME est disponible en téléchargement. vous exécutez la version %s et pouvez maintenant utiliser l'option -U pour la mettre à niveau"
MSG_BACKUP_TARGET=81
MSG_EN[$MSG_BACKUP_TARGET]="RBK0081I: Creating backup of type %s in %s"
MSG_DE[$MSG_BACKUP_TARGET]="RBK0081I: Backup vom Typ %s wird in %s erstellt"
MSG_FI[$MSG_BACKUP_TARGET]="RBK0081I: Luodaan %s-tyypin varmuuskopio kohteeseen %s"
MSG_FR[$MSG_BACKUP_TARGET]="RBK0081I: Création d'une sauvegarde de type %s dans %s"
MSG_EXISTING_BOOT_BACKUP=82
MSG_EN[$MSG_EXISTING_BOOT_BACKUP]="RBK0082I: Backup of boot partition alreday exists in %s"
MSG_DE[$MSG_EXISTING_BOOT_BACKUP]="RBK0082I: Backup der Bootpartition in %s existiert schon"
MSG_FI[$MSG_EXISTING_BOOT_BACKUP]="RBK0082I: Käynnistysosion varmuuskopio on jo olemassa kohteessa %s"
MSG_FR[$MSG_EXISTING_BOOT_BACKUP]="RBK0082I: La sauvegarde de la partition de Boot existe déjà dans %s"
MSG_EXISTING_PARTITION_BACKUP=83
MSG_EN[$MSG_EXISTING_PARTITION_BACKUP]="RBK0083I: Backup of partition layout already exists in %s"
MSG_DE[$MSG_EXISTING_PARTITION_BACKUP]="RBK0083I: Backup des Partitionlayouts in %s existiert schon"
MSG_FI[$MSG_EXISTING_PARTITION_BACKUP]="RBK0083I: Osioasettelun varmuuskopio on jo olemassa kohteessa %s"
MSG_FR[$MSG_EXISTING_PARTITION_BACKUP]="RBK0083I: La sauvegarde de la disposition de la partition existe déjà dans %s"
MSG_EXISTING_MBR_BACKUP=84
MSG_EN[$MSG_EXISTING_MBR_BACKUP]="RBK0084I: Backup of master boot record already exists in %s"
MSG_DE[$MSG_EXISTING_MBR_BACKUP]="RBK0084I: Backup des Masterbootrecords in %s existiert schon"
MSG_FI[$MSG_EXISTING_MBR_BACKUP]="RBK0084I: Master Boot Record-varmuuskopio on jo olemassa kohteessa %s"
MSG_FR[$MSG_EXISTING_MBR_BACKUP]="RBK0084I: La sauvegarde du MBR ,master boot record, existe déjà dans %s"
MSG_BACKUP_STARTED=85
MSG_EN[$MSG_BACKUP_STARTED]="RBK0085I: Backup of type %s started. Please be patient"
MSG_DE[$MSG_BACKUP_STARTED]="RBK0085I: Backuperstellung vom Typ %s gestartet. Bitte Geduld"
MSG_FI[$MSG_BACKUP_STARTED]="RBK0085I: %s-tyypin varmuuskopiointi on aloitettu. Ole hyvä ja odota"
MSG_FR[$MSG_BACKUP_STARTED]="RBK0085I: Démarrage de la sauvegarde de type %s SVP soyez patient"
MSG_RESTOREDEVICE_IS_PARTITION=86
MSG_EN[$MSG_RESTOREDEVICE_IS_PARTITION]="RBK0086E: Restore device has trailing partition number but cannot be a partition"
MSG_DE[$MSG_RESTOREDEVICE_IS_PARTITION]="RBK0086E: Wiederherstellungsgerät hat eine Partitionsnummer am Ende aber darf keine Partition sein"
MSG_FI[$MSG_RESTOREDEVICE_IS_PARTITION]="RBK0086E: Palautuslaitteella on osionumero, palautusta ei voida tehdä osiolle. "
MSG_FR[$MSG_RESTOREDEVICE_IS_PARTITION]="RBK0086E: Le périphérique de restauration a un numéro de partition la restauration ne peut pas être effectuée"
MSG_RESTORE_DIRECTORY_INVALID=87
MSG_EN[$MSG_RESTORE_DIRECTORY_INVALID]="RBK0087E: Restore directory %s was not created by $MYNAME"
MSG_DE[$MSG_RESTORE_DIRECTORY_INVALID]="RBK0087E: Wiederherstellungsverzeichnis %s wurde nicht von $MYNAME erstellt"
MSG_FI[$MSG_RESTORE_DIRECTORY_INVALID]="RBK0087E: $MYNAME ei luonut palautushakemistoa %s"
MSG_FR[$MSG_RESTORE_DIRECTORY_INVALID]="RBK0087E: Le répertoire de restauration %s n'a pas été créé par.$MYNAME"
MSG_RESTORE_DEVICE_NOT_ALLOWED=88
MSG_EN[$MSG_RESTORE_DEVICE_NOT_ALLOWED]="RBK0088E: -R option not supported for partitionbased backup"
MSG_DE[$MSG_RESTORE_DEVICE_NOT_ALLOWED]="RBK0088E: Option -R wird nicht beim partitionbasierten Backup unterstützt"
MSG_FI[$MSG_RESTORE_DEVICE_NOT_ALLOWED]="RBK0088E: Valintaa -R ei tueta osiopohjaisille varmuuskopioille"
MSG_FR[$MSG_RESTORE_DEVICE_NOT_ALLOWED]="RBK0088E: L'option -R n'est pas prise en charge pour une sauvegarde basée sur une partition"
MSG_UNKNOWN_OPTION=89
MSG_EN[$MSG_UNKNOWN_OPTION]="RBK0089E: Unknown option %s"
MSG_DE[$MSG_UNKNOWN_OPTION]="RBK0089E: Unbekannte Option %s"
MSG_FI[$MSG_UNKNOWN_OPTION]="RBK0089E: Tuntematon valinta %s"
MSG_FR[$MSG_UNKNOWN_OPTION]="RBK0089E: Option inconnue %s"
MSG_OPTION_REQUIRES_PARAMETER=90
MSG_EN[$MSG_OPTION_REQUIRES_PARAMETER]="RBK0090E: Option %s requires a parameter. If parameter starts with '-' start with '\-' instead"
MSG_DE[$MSG_OPTION_REQUIRES_PARAMETER]="RBK0090E: Option %s erwartet einen Parameter. Falls der Parameter mit '-' beginnt beginne stattdessen mit '\-'"
MSG_FI[$MSG_OPTION_REQUIRES_PARAMETER]="RBK0090E: Valinta %s vaatii parametrin. Jos parametri alkaa merkillä '-', korvaa se merkeillä '\-'"
MSG_FR[$MSG_OPTION_REQUIRES_PARAMETER]="RBK0090E: L'option %s requiert un paramètre. Si le paramètre commence par '-', commencez par '\-' à la place"
MSG_MENTION_HELP=91
MSG_EN[$MSG_MENTION_HELP]="RBK0091I: Invoke '%s -h' to get more detailed information of all script invocation parameters"
MSG_DE[$MSG_MENTION_HELP]="RBK0091I: '%s -h' liefert eine detailierte Beschreibung aller Scriptaufrufoptionen"
MSG_FI[$MSG_MENTION_HELP]="RBK0091I: Suorita '%s -h' saadaksesi lisätietoa skriptin parametreista"
MSG_FR[$MSG_MENTION_HELP]="RBK0091I: '%s -h' fournit une description détaillée de toutes les options du script"
MSG_PROCESSING_PARTITION=92
MSG_EN[$MSG_PROCESSING_PARTITION]="RBK0092I: Saving partition %s (%s/%s)"
MSG_DE[$MSG_PROCESSING_PARTITION]="RBK0092I: Partition %s (%s/%s) wird gesichert"
MSG_FI[$MSG_PROCESSING_PARTITION]="RBK0092I: Tallennetaan osiota %s (%s/%s)"
MSG_FR[$MSG_PROCESSING_PARTITION]="RBK0092I: Sauvegarde de la partition %s (%s/%s)"
MSG_PARTITION_NOT_FOUND=93
MSG_EN[$MSG_PARTITION_NOT_FOUND]="RBK0093E: Partition %s specified with option -T not found"
MSG_DE[$MSG_PARTITION_NOT_FOUND]="RBK0093E: Angegebene Partition %s der Option -T existiert nicht"
MSG_FI[$MSG_PARTITION_NOT_FOUND]="RBK0093E: Valinnalla -T tarkennettua osiota %s ei löytynyt"
MSG_FR[$MSG_PARTITION_NOT_FOUND]="RBK0093E: La partition %s spécifiée avec l'option -T est introuvable"
MSG_PARTITION_NUMBER_INVALID=94
MSG_EN[$MSG_PARTITION_NUMBER_INVALID]="RBK0094E: Parameter '%s' specified in option -T is not a number"
MSG_DE[$MSG_PARTITION_NUMBER_INVALID]="RBK0094E: Angegebener Parameter '%s' der Option -T ist keine Zahl"
MSG_FI[$MSG_PARTITION_NUMBER_INVALID]="RBK0094E: Valinnan -T parametri '%s' ei ole numero"
MSG_FR[$MSG_PARTITION_NUMBER_INVALID]="RBK0094E: Le paramètre '%s' spécifié pour l'option -T n'est pas un nombre"
MSG_RESTORING_PARTITIONFILE=95
MSG_EN[$MSG_RESTORING_PARTITIONFILE]="RBK0095I: Restoring partition %s"
MSG_DE[$MSG_RESTORING_PARTITIONFILE]="RBK0095I: Backup wird auf Partition %s zurückgespielt"
MSG_FI[$MSG_RESTORING_PARTITIONFILE]="RBK0095I: Palautetaan osiota %s"
MSG_FR[$MSG_RESTORING_PARTITIONFILE]="RBK0095I: Restauration de la partition %s"
MSG_LANGUAGE_NOT_SUPPORTED=96
MSG_EN[$MSG_LANGUAGE_NOT_SUPPORTED]="RBK0096I: Language %s not supported"
MSG_DE[$MSG_LANGUAGE_NOT_SUPPORTED]="RBK0096I: Die Sprache %s wird nicht unterstützt"
MSG_FI[$MSG_LANGUAGE_NOT_SUPPORTED]="RBK0096I: Kieli %s ei ole tuettu"
MSG_FR[$MSG_LANGUAGE_NOT_SUPPORTED]="RBK0096I: Langue %s non prise en charge"
#MSG_PARTITIONING_RESTORE_DEVICE=97
#MSG_EN[$MSG_PARTITIONING_RESTORE_DEVICE]="RBK0097W: Partitioning %s"
#MSG_DE[$MSG_PARTITIONING_RESTORE_DEVICE]="RBK0097W: Partitioniere %s"
#MSG_FI[$MSG_PARTITIONING_RESTORE_DEVICE]="RBK0097W: Osioidaan ja alustetaan %s"
#MSG_FR[$MSG_PARTITIONING_RESTORE_DEVICE]="RBK0097W: Partitionnement et formatage %s"
MSG_FORMATTING=98
MSG_EN[$MSG_FORMATTING]="RBK0098I: Formatting partition %s with %s"
MSG_DE[$MSG_FORMATTING]="RBK0098I: Formatiere Partition %s mit %s"
MSG_FI[$MSG_FORMATTING]="RBK0098I: Alustetaan osio %s tiedostojärjestelmälle %s"
MSG_FR[$MSG_FORMATTING]="RBK0098I: Formatage de la partition %s avec %s"
MSG_RESTORING_FILE_PARTITION_DONE=99
MSG_EN[$MSG_RESTORING_FILE_PARTITION_DONE]="RBK0099I: Restore of partition %s finished"
MSG_DE[$MSG_RESTORING_FILE_PARTITION_DONE]="RBK0099I: Zurückspielen des Backups auf Partition %s beendet"
MSG_FI[$MSG_RESTORING_FILE_PARTITION_DONE]="RBK0099I: Osio %s palautettu"
MSG_FR[$MSG_RESTORING_FILE_PARTITION_DONE]="RBK0099I: Restauration de la partition %s terminée"
#MSG_WARN_RESTORE_PARTITION_DEVICE_OVERWRITTEN=100
#MSG_EN[$MSG_WARN_RESTORE_PARTITION_DEVICE_OVERWRITTEN]="RBK0100W: Device %s will be overwritten with the backup"
#MSG_DE[$MSG_WARN_RESTORE_PARTITION_DEVICE_OVERWRITTEN]="RBK0100W: Gerät %s wird mit dem Backup beschrieben"
#MSG_FI[$MSG_WARN_RESTORE_PARTITION_DEVICE_OVERWRITTEN]="RBK0100W: Palautus ylikirjoittaa laitteen %s"
#MSG_FR[$MSG_WARN_RESTORE_PARTITION_DEVICE_OVERWRITTEN]="RBK0100W: Le périphérique %s sera écrasé par la sauvegarde"
MSG_VERSION_HISTORY_PAGE=101
MSG_EN[$MSG_VERSION_HISTORY_PAGE]="$VERSION_PAGE"
#MSG_DE[$MSG_VERSION_HISTORY_PAGE]="$MYHOMEURL/de/versionshistorie/"
#MSG_FI[$MSG_VERSION_HISTORY_PAGE]="$MYHOMEURL/en/versionhistory/" # Defaults to en
#MSG_FR[$MSG_VERSION_HISTORY_PAGE]="$MYHOMEURL/en/versionhistory/" # Defaults to en
MSG_UPDATING_UUID=102
MSG_EN[$MSG_UPDATING_UUID]="RBK0102I: Updating %s from %s to %s in %s"
MSG_DE[$MSG_UPDATING_UUID]="RBK0102I: %s wird von %s auf %s in %s geändert"
MSG_FI[$MSG_UPDATING_UUID]="RBK0102I: Päivitetään %s arvosta %s arvoon %s kohteessa %s"
MSG_FR[$MSG_UPDATING_UUID]="RBK0102I: mise à jour %s de %s à %s en %s"
MSG_UNABLE_TO_WRITE=103
MSG_EN[$MSG_UNABLE_TO_WRITE]="RBK0103E: Unable to create backup on %s because of missing write permission"
MSG_DE[$MSG_UNABLE_TO_WRITE]="RBK0103E: Ein Backup kann nicht auf %s erstellt werden da die Schreibberechtigung fehlt"
MSG_FI[$MSG_UNABLE_TO_WRITE]="RBK0103E: Varmuuskopion luominen kohteeseen %s ei onnistu puuttuvien kirjoitusoikeuksien vuoksi"
MSG_FR[$MSG_UNABLE_TO_WRITE]="RBK0103E: Impossible de créer une sauvegarde sur %s en raison d'un manque d'autorisation en écriture"
MSG_LABELING=104
MSG_EN[$MSG_LABELING]="RBK0104I: Labeling partition %s with label %s"
MSG_DE[$MSG_LABELING]="RBK0104I: Partition %s erhält das Label %s"
MSG_FI[$MSG_LABELING]="RBK0104I: Nimetään osio %s nimikkeellä %s"
MSG_FR[$MSG_LABELING]="RBK0104I: L'étiquette de la partition est %s"
MSG_REMOVING_BACKUP_FAILED=105
MSG_EN[$MSG_REMOVING_BACKUP_FAILED]="RBK0105E: Removing incomplete backup in %s failed with RC %s. Directory has to be cleaned up manually"
MSG_DE[$MSG_REMOVING_BACKUP_FAILED]="RBK0105E: Löschen des unvollständigen Backups in %s schlug fehl mit RC: %s. Das Verzeichnis muss manuell gelöscht werden"
MSG_FI[$MSG_REMOVING_BACKUP_FAILED]="RBK0105E: Keskeneräisen varmuuskopion poistaminen kohteesta %s epäonnistui, RC %s. Hakemisto tulee tyhjentää manuaalisesti"
MSG_FR[$MSG_REMOVING_BACKUP_FAILED]="RBK0105E: Échec de la suppression de la sauvegarde incomplète dans %s , code erreur %s. Supprimez manuellement le répertoire"
MSG_DEPLOYMENT_FAILED=106
MSG_EN[$MSG_DEPLOYMENT_FAILED]="RBK0106E: Installation of $MYNAME failed on server %s for user %s"
MSG_DE[$MSG_DEPLOYMENT_FAILED]="RBK0106E: Installation von $MYNAME auf Server %s für Benutzer %s fehlgeschlagen"
MSG_FI[$MSG_DEPLOYMENT_FAILED]="RBK0106E: $MYNAME asennus epäonnistui palvelimella %s käyttäjälle %s"
MSG_FR[$MSG_DEPLOYMENT_FAILED]="RBK0106E: L'installation de $MYNAME a échoué sur le serveur %s pour l'utilisateur %s"
MSG_EXTENSION_FAILED=107
MSG_EN[$MSG_EXTENSION_FAILED]="RBK0107W: Extension %s failed with RC %s"
MSG_DE[$MSG_EXTENSION_FAILED]="RBK0107W: Erweiterung %s fehlerhaft beendet mit RC %s"
MSG_FI[$MSG_EXTENSION_FAILED]="RBK0107W: Lisäosa %s epäonnistui, RC %s"
MSG_FR[$MSG_EXTENSION_FAILED]="RBK0107W: Échec de l'extension , code erreur %s"
MSG_SKIPPING_UNFORMATTED_PARTITION=108
MSG_EN[$MSG_SKIPPING_UNFORMATTED_PARTITION]="RBK0108W: Unformatted partition %s (%s) not saved"
MSG_DE[$MSG_SKIPPING_UNFORMATTED_PARTITION]="RBK0108W: Unformatierte Partition %s (%s) wird nicht gesichert"
MSG_FI[$MSG_SKIPPING_UNFORMATTED_PARTITION]="RBK0108W: Alustamatonta osiota %s (%s) ei tallennettu"
MSG_FR[$MSG_SKIPPING_UNFORMATTED_PARTITION]="RBK0108W: Partition non formatée %s (%s) non enregistrée"
MSG_UNSUPPORTED_FILESYSTEM_FORMAT=109
MSG_EN[$MSG_UNSUPPORTED_FILESYSTEM_FORMAT]="RBK0109E: Unsupported filesystem %s detected on partition %s"
MSG_DE[$MSG_UNSUPPORTED_FILESYSTEM_FORMAT]="RBK0109E: Nicht unterstütztes Filesystem %s auf Partition %s"
MSG_FI[$MSG_UNSUPPORTED_FILESYSTEM_FORMAT]="RBK0109E: Tiedostojärjestelmää %s joka havaittiin osiolla %s, ei tueta"
MSG_FR[$MSG_UNSUPPORTED_FILESYSTEM_FORMAT]="RBK0109E: Système de fichiers non pris en charge %s sur la partition %s"
MSG_UNABLE_TO_COLLECT_PARTITIONINFO=110
MSG_EN[$MSG_UNABLE_TO_COLLECT_PARTITIONINFO]="RBK0110E: Unable to collect partition data with %s. RC %s"
MSG_DE[$MSG_UNABLE_TO_COLLECT_PARTITIONINFO]="RBK0110E: Partitionsdaten können nicht mit %s gesammelt werden. RC %s"
MSG_FI[$MSG_UNABLE_TO_COLLECT_PARTITIONINFO]="RBK0110E: Osiotietojen kerääminen epäonnistui käyttämällä komentoa %s. RC %s"
MSG_FR[$MSG_UNABLE_TO_COLLECT_PARTITIONINFO]="RBK0110E: Impossible de collecter les données de la partition %s. Code erreur %s"
MSG_UNABLE_TO_CREATE_PARTITIONS=111
MSG_EN[$MSG_UNABLE_TO_CREATE_PARTITIONS]="RBK0111E: Error occured when partitions were created. RC %s - %s"
MSG_DE[$MSG_UNABLE_TO_CREATE_PARTITIONS]="RBK0111E: Fehler beim Erstellen der Partitionen. RC %s - %s"
MSG_FI[$MSG_UNABLE_TO_CREATE_PARTITIONS]="RBK0111E: Virhe osioiden luomisen yhteydessä. RC %s - %s"
MSG_FR[$MSG_UNABLE_TO_CREATE_PARTITIONS]="RBK0111E: Erreur pendant la création des partitions. code erreur %s - %s"
MSG_PROCESSED_PARTITION=112
MSG_EN[$MSG_PROCESSED_PARTITION]="RBK0112I: Partition %s was saved"
MSG_DE[$MSG_PROCESSED_PARTITION]="RBK0112I: Partition %s wurde gesichert"
MSG_FI[$MSG_PROCESSED_PARTITION]="RBK0112I: Osio % tallennettiin"
MSG_FR[$MSG_PROCESSED_PARTITION]="RBK0112I: La partition %s a été enregistrée"
MSG_YES_NO_DEVICE_MISMATCH=113
MSG_EN[$MSG_YES_NO_DEVICE_MISMATCH]="RBK0113E: Restore device %s doesn't match %s"
MSG_DE[$MSG_YES_NO_DEVICE_MISMATCH]="RBK0113E: Wiederherstellungsgerät %s ähnelt nicht %s"
MSG_FI[$MSG_YES_NO_DEVICE_MISMATCH]="RBK0113E: Palautuslaite %s ja %s eivät täsmää"
MSG_FR[$MSG_YES_NO_DEVICE_MISMATCH]="RBK0113E: Le périphérique de restauration %s ne correspond pas à %s"
MSG_VISIT_VERSION_HISTORY_PAGE=114
MSG_EN[$MSG_VISIT_VERSION_HISTORY_PAGE]="RBK0114I: Visit %s to read about the changes in the new version"
MSG_DE[$MSG_VISIT_VERSION_HISTORY_PAGE]="RBK0114I: Besuche %s um die Änderungen in der neuen Version kennenzulernen"
MSG_FI[$MSG_VISIT_VERSION_HISTORY_PAGE]="RBK0114I: Käy sivulla %s lukeaksesi uuden version muutoksista"
MSG_FR[$MSG_VISIT_VERSION_HISTORY_PAGE]="RBK0114I: Visitez %s pour être informé des changements dans la nouvelle version"
MSG_DEPLOYED_HOST=115
MSG_EN[$MSG_DEPLOYED_HOST]="RBK0115I: $MYNAME $VERSION ($GIT_COMMIT_ONLY) installed on host %s for user %s"
MSG_DE[$MSG_DEPLOYED_HOST]="RBK0115I: $MYNAME $VERSION ($GIT_COMMIT_ONLY) wurde auf Server %s für Benutzer %s installiert"
MSG_FI[$MSG_DEPLOYED_HOST]="RBK0115I: $MYNAME $VERSION ($GIT_COMMIT_ONLY) asennettu isäntälaitteen %s käyttäjälle %s"
MSG_FR[$MSG_DEPLOYED_HOST]="RBK0115I: $MYNAME $VERSION ($GIT_COMMIT_ONLY) a été installé sur le serveur %s pour l'utilisateur %s"
MSG_INCLUDED_CONFIG=116
MSG_EN[$MSG_INCLUDED_CONFIG]="RBK0116I: Using config file %s"
MSG_DE[$MSG_INCLUDED_CONFIG]="RBK0116I: Konfigurationsdatei %s wird benutzt"
MSG_FI[$MSG_INCLUDED_CONFIG]="RBK0116I: Käytetään asetustiedostoa %s"
MSG_FR[$MSG_INCLUDED_CONFIG]="RBK0116I: Utilisation en cours du fichier de configuration %s"
MSG_CURRENT_SCRIPT_VERSION=117
MSG_EN[$MSG_CURRENT_SCRIPT_VERSION]="RBK0117I: Current script version: %s"
MSG_DE[$MSG_CURRENT_SCRIPT_VERSION]="RBK0117I: Aktuelle Scriptversion: %s"
MSG_FI[$MSG_CURRENT_SCRIPT_VERSION]="RBK0117I: Nykyisen skriptin vesio: %s"
MSG_FR[$MSG_CURRENT_SCRIPT_VERSION]="RBK0117I: Version actuelle du script: %s"
MSG_AVAILABLE_VERSIONS_HEADER=118
MSG_EN[$MSG_AVAILABLE_VERSIONS_HEADER]="RBK0118I: Available versions:"
MSG_DE[$MSG_AVAILABLE_VERSIONS_HEADER]="RBK0118I: Verfügbare Scriptversionen:"
MSG_FI[$MSG_AVAILABLE_VERSIONS_HEADER]="RBK0118I: Saatavilla olevat versiot:"
MSG_FR[$MSG_AVAILABLE_VERSIONS_HEADER]="RBK0118I: Versions disponibles:"
MSG_AVAILABLE_VERSIONS=119
MSG_EN[$MSG_AVAILABLE_VERSIONS]="RBK0119I: %s: %s"
MSG_DE[$MSG_AVAILABLE_VERSIONS]="RBK0119I: %s: %s"
MSG_FI[$MSG_AVAILABLE_VERSIONS]="RBK0119I: %s: %s"
MSG_FR[$MSG_AVAILABLE_VERSIONS]="RBK0119I: %s: %s"
MSG_SAVING_ACTUAL_VERSION=120
MSG_EN[$MSG_SAVING_ACTUAL_VERSION]="RBK0120I: Saving current version %s to %s"
MSG_DE[$MSG_SAVING_ACTUAL_VERSION]="RBK0120I: Aktuelle Version %s wird in %s gesichert"
MSG_FI[$MSG_SAVING_ACTUAL_VERSION]="RBK0120I: Tallennetaan nykyinen versio %s nimellä %s"
MSG_FR[$MSG_SAVING_ACTUAL_VERSION]="RBK0120I: Enregistrement de la version actuelle %s dans %s"
MSG_RESTORING_PREVIOUS_VERSION=121
MSG_EN[$MSG_RESTORING_PREVIOUS_VERSION]="RBK0121I: Restoring previous version %s to %s"
MSG_DE[$MSG_RESTORING_PREVIOUS_VERSION]="RBK0121I: Vorherige Version %s wird in %s wiederhergestellt"
MSG_FI[$MSG_RESTORING_PREVIOUS_VERSION]="RBK0121I: Palautetaan edellinen versio %s nimellä %s"
MSG_FR[$MSG_RESTORING_PREVIOUS_VERSION]="RBK0121I: Restauration de la version précédente %s vers %s"
MSG_SELECT_VERSION=122
MSG_EN[$MSG_SELECT_VERSION]="RBK0122I: Select version to restore (%s-%s)"
MSG_DE[$MSG_SELECT_VERSION]="RBK0122I: Auswahl der Version die wiederhergestellt werden soll (%s-%s)"
MSG_FI[$MSG_SELECT_VERSION]="RBK0122I: Valitse palautettava versio (%s-%s)"
MSG_FR[$MSG_SELECT_VERSION]="RBK0122I: Sélectionnez la version à restaurer (%s-%s)"
MSG_NO_PREVIOUS_VERSIONS_AVAILABLE=123
MSG_EN[$MSG_NO_PREVIOUS_VERSIONS_AVAILABLE]="RBK0123E: No version to restore available"
MSG_DE[$MSG_NO_PREVIOUS_VERSIONS_AVAILABLE]="RBK0123E: Keine Version zum Restore verfügbar"
MSG_FI[$MSG_NO_PREVIOUS_VERSIONS_AVAILABLE]="RBK0123E: Ei aiempia versioita palautettavaksi"
MSG_FR[$MSG_NO_PREVIOUS_VERSIONS_AVAILABLE]="RBK0123E: Aucune version à restaurer disponible"
MSG_FAKE_MODE_ON=124
MSG_EN[$MSG_FAKE_MODE_ON]="RBK0124W: Fake mode on"
MSG_DE[$MSG_FAKE_MODE_ON]="RBK0124W: Simulationsmodus an"
MSG_FI[$MSG_FAKE_MODE_ON]="RBK0124W: Simulaatiotila päällä"
MSG_FR[$MSG_FAKE_MODE_ON]="RBK0124W: Mode simulation activé"
MSG_UNUSED_PARAMETERS=125
MSG_EN[$MSG_UNUSED_PARAMETERS]="RBK0125W: Unused option(s) \"%s\" detected. There may be quotes missing in option arguments"
MSG_DE[$MSG_UNUSED_PARAMETERS]="RBK0125W: Unbenutzte Option(en) \" %s\" entdeckt. Es scheinen Anführungszeichen bei Optionsargumenten zu fehlen"
MSG_FI[$MSG_UNUSED_PARAMETERS]="RBK0125W: Havaittu käyttämättömiä valintoja \"%s\". Lainausmerkkejä saattaa puuttua valintojen argumenteista"
MSG_FR[$MSG_UNUSED_PARAMETERS]="RBK0125W: Option(s) non utilisable(s) \"%s\" . Les guillemets semblent manquer dans les arguments d'option."
MSG_REPLACING_FILE_BY_HARDLINK=126
MSG_EN[$MSG_REPLACING_FILE_BY_HARDLINK]="RBK0126I: Replacing %s with hardlink to %s"
MSG_DE[$MSG_REPLACING_FILE_BY_HARDLINK]="RBK0126I: Datei %s wird durch einem Hardlink auf %s ersetzt"
MSG_FI[$MSG_REPLACING_FILE_BY_HARDLINK]="RBK0126I: Korvataan %s hardlink-tiedolla kohteeseen %s"
MSG_FR[$MSG_REPLACING_FILE_BY_HARDLINK]="RBK0126I: Remplacement de %s par un lien physique vers %s"
MSG_DEPLOYING_HOST_OFFLINE=127
MSG_EN[$MSG_DEPLOYING_HOST_OFFLINE]="RBK0127E: Server %s offline"
MSG_DE[$MSG_DEPLOYING_HOST_OFFLINE]="RBK0127E: Server %s ist nicht erreichbar"
MSG_FI[$MSG_DEPLOYING_HOST_OFFLINE]="RBK0127E: Palvelin %s on offline-tilassa"
MSG_FR[$MSG_DEPLOYING_HOST_OFFLINE]="RBK0127E: Serveur %s hors ligne"
#MSG_USING_LOGFILE=128
#MSG_EN[$MSG_USING_LOGFILE]="RBK0128I: Using logfile %s"
#MSG_DE[$MSG_USING_LOGFILE]="RBK0128I: Logdatei ist %s"
#MSG_FI[$MSG_USING_LOGFILE]="RBK0128I: Käytetään lokitiedostoa %s"
#MSG_FR[$MSG_USING_LOGFILE]="RBK0128I: Le fichier journal : %s"
MSG_EMAIL_EXTENSION_NOT_FOUND=129
MSG_EN[$MSG_EMAIL_EXTENSION_NOT_FOUND]="RBK0129W: email extension %s not found"
MSG_DE[$MSG_EMAIL_EXTENSION_NOT_FOUND]="RBK0129W: Email Erweiterung %s nicht gefunden"
MSG_FI[$MSG_EMAIL_EXTENSION_NOT_FOUND]="RBK0129W: Sähköpostilisäosaa %s ei löytynyt"
MSG_FR[$MSG_EMAIL_EXTENSION_NOT_FOUND]="RBK0129W: Extension de l'e-mail %s absente"
MSG_MISSING_FILEPARAMETER=130
MSG_EN[$MSG_MISSING_FILEPARAMETER]="RBK0130E: Missing backup- or restorepath parameter"
MSG_DE[$MSG_MISSING_FILEPARAMETER]="RBK0130E: Backup- oder Restorepfadparameter fehlt"
MSG_FI[$MSG_MISSING_FILEPARAMETER]="RBK0130E: Varmuuskopiointi- tai palautushakemistoparametri puuttuu"
MSG_FR[$MSG_MISSING_FILEPARAMETER]="RBK0130E: Paramêtre manquant: chemin de sauvegarde ou de restauration"
MSG_MISSING_INSTALLED_FILE=131
MSG_EN[$MSG_MISSING_INSTALLED_FILE]="RBK0131E: Program %s not found. Use 'sudo apt-get update; sudo apt-get install %s' to install the missing program"
MSG_DE[$MSG_MISSING_INSTALLED_FILE]="RBK0131E: Programm %s nicht gefunden. Mit 'sudo apt-get update; sudo apt-get install %s' wird das fehlende Programm installiert"
MSG_FI[$MSG_MISSING_INSTALLED_FILE]="RBK0131E: Sovellusta %s ei löytynyt. Suorita 'sudo apt-get update; sudo apt-get install %s' asentaaksesi puuttuvan sovelluksen"
MSG_FR[$MSG_MISSING_INSTALLED_FILE]="RBK0131E: Programme %s introuvable. Utilisez 'sudo apt-get update ; sudo apt-get install %s' pour installer le programme manquant"
#MSG_SKIPPING_CREATING_PARTITIONS=132
#MSG_EN[$MSG_SKIPPING_CREATING_PARTITIONS]="RBK0132W: No partitions are created. Reusing existing partitions"
#MSG_DE[$MSG_SKIPPING_CREATING_PARTITIONS]="RBK0132W: Es werden keine Partitionen erstellt sondern die existierenden Partitionen benutzt"
#MSG_FI[$MSG_SKIPPING_CREATING_PARTITIONS]="RBK0132W: Osioita ei luotu. Käytetään olemassaolevia osioita"
#MSG_FR[$MSG_SKIPPING_CREATING_PARTITIONS]="RBK0132W: Aucune partition n'est créée. Réutiliser des partitions existantes"
MSG_HARDLINK_DIRECTORY_USED=133
MSG_EN[$MSG_HARDLINK_DIRECTORY_USED]="RBK0133I: Using directory %s for hardlinks"
MSG_DE[$MSG_HARDLINK_DIRECTORY_USED]="RBK0133I: Verzeichnis %s wird für Hardlinks benutzt"
MSG_FI[$MSG_HARDLINK_DIRECTORY_USED]="RBK0133I: Käytetään hakemistoa %s hardlink-tiedoille"
MSG_FR[$MSG_HARDLINK_DIRECTORY_USED]="RBK0133I: Le répertoire %s est utilisé pour les liens physiques"
MSG_UNABLE_TO_USE_HARDLINKS=134
MSG_EN[$MSG_UNABLE_TO_USE_HARDLINKS]="RBK0134E: Unable to use hardlinks on %s for bootpartition files. RC %s"
MSG_DE[$MSG_UNABLE_TO_USE_HARDLINKS]="RBK0134E: Hardlinkslinks können nicht auf %s für Bootpartitionsdateien benutzt werden. RC %s"
MSG_FI[$MSG_UNABLE_TO_USE_HARDLINKS]="RBK0134E: Hardlink-tietoja kohteessa %s ei voitu käyttäää käynnistysosion tiedostoille. RC %s"
MSG_FR[$MSG_UNABLE_TO_USE_HARDLINKS]="RBK0134E: Les liens physiques non utilisables sur %s pour les fichiers de partition de Boot. Code erreur %s"
MSG_SCRIPT_IS_DEPRECATED=135
MSG_EN[$MSG_SCRIPT_IS_DEPRECATED]="RBK0135W: ==> Current script version %s has a severe bug and should be updated immediately <==="
MSG_DE[$MSG_SCRIPT_IS_DEPRECATED]="RBK0135W: ==> Aktuelle Scriptversion %s enthält einen gravierenden Fehler und sollte sofort aktualisiert werden <==="
MSG_FI[$MSG_SCRIPT_IS_DEPRECATED]="RBK0135W: ==> Nykyisessä skriptiversiossa %s on vakava bugi ja se tulee päivittää välittömästi <==="
MSG_FR[$MSG_SCRIPT_IS_DEPRECATED]="RBK0135W: ==> La version actuelle du script %s a un bogue grave qui impose une mise à jour immédiatement <==="
MSG_MISSING_START_OR_STOP=136
MSG_EN[$MSG_MISSING_START_OR_STOP]="RBK0136E: Missing mandatory option %s"
MSG_DE[$MSG_MISSING_START_OR_STOP]="RBK0136E: Es fehlt die obligatorische Option %s"
MSG_FI[$MSG_MISSING_START_OR_STOP]="RBK0136E: Pakollinen valinta %s puuttuu"
MSG_FR[$MSG_MISSING_START_OR_STOP]="RBK0136E: Option obligatoire manquante %s"
MSG_NO_ROOTBACKUPFILE_FOUND=137
MSG_EN[$MSG_NO_ROOTBACKUPFILE_FOUND]="RBK0137E: Rootbackupfile for type %s not found"
MSG_DE[$MSG_NO_ROOTBACKUPFILE_FOUND]="RBK0137E: Rootbackupdatei für den Typ %s nicht gefunden"
MSG_FI[$MSG_NO_ROOTBACKUPFILE_FOUND]="RBK0137E: Juurivarmuuskopiota ei löytynyt tyypille %s"
MSG_FR[$MSG_NO_ROOTBACKUPFILE_FOUND]="RBK0137E: Fichier de sauvegarde Root pour le type %s introuvable"
MSG_USING_ROOTBACKUPFILE=138
MSG_EN[$MSG_USING_ROOTBACKUPFILE]="RBK0138I: Using rootbackup %s"
MSG_DE[$MSG_USING_ROOTBACKUPFILE]="RBK0138I: Rootbackup %s wird benutzt"
MSG_FI[$MSG_USING_ROOTBACKUPFILE]="RBK0138I: Käytetään käynnistysvarmuuskopiota %s"
MSG_FR[$MSG_USING_ROOTBACKUPFILE]="RBK0138I: La sauvegarde Root %s est en cours d'utilisation"
MSG_FORCING_CREATING_PARTITIONS=139
MSG_EN[$MSG_FORCING_CREATING_PARTITIONS]="RBK0139W: Partition creation ignores errors"
MSG_DE[$MSG_FORCING_CREATING_PARTITIONS]="RBK0139W: Partitionserstellung ignoriert Fehler"
MSG_FI[$MSG_FORCING_CREATING_PARTITIONS]="RBK0139W: Osion luonti ohittaa virheet"
MSG_FR[$MSG_FORCING_CREATING_PARTITIONS]="RBK0139W: Les erreurs sont ignorées lors de la création de partition"
#MSG_LABELS_NOT_SUPPORTED=140
#MSG_EN[$MSG_LABELS_NOT_SUPPORTED]="RBK0140E: LABEL definitions in /etc/fstab not supported. Use PARTUUID instead"
#MSG_DE[$MSG_LABELS_NOT_SUPPORTED]="RBK0140E: LABEL Definitionen sind in /etc/fstab nicht unterstützt. Benutze stattdessen PARTUUID"
#MSG_FI[$MSG_LABELS_NOT_SUPPORTED]="RBK0140E: LABEL määrityksiä tiedostossa /etc/fstab ei tueta. Käytä PARTUUID-määrityksiä"
#MSG_FR[$MSG_LABELS_NOT_SUPPORTED]="RBK0140E: LABEL Les définitions dans /etc/fstab ne sont pas prises en charge. Utilisez PARTUUID"
MSG_SAVING_USED_PARTITIONS_ONLY=141
MSG_EN[$MSG_SAVING_USED_PARTITIONS_ONLY]="RBK0141I: Saving space of defined partitions only"
MSG_DE[$MSG_SAVING_USED_PARTITIONS_ONLY]="RBK0141I: Nur der von den definierten Partitionen belegte Speicherplatz wird gesichert"
MSG_FI[$MSG_SAVING_USED_PARTITIONS_ONLY]="RBK0141I: Tilaa säästetään vain määritellyillä osioilla"
MSG_FR[$MSG_SAVING_USED_PARTITIONS_ONLY]="RBK0141I: Seul l'espace occupé par les partitions définies est sauvegardé"
MSG_NO_BOOTDEVICE_FOUND=142
MSG_EN[$MSG_NO_BOOTDEVICE_FOUND]="RBK0142E: Unable to detect boot device. Please report this issue on https://github.com/framps/raspiBackup/issues or https://www.linux-tips-and-tricks.de/en/rmessages"
MSG_DE[$MSG_NO_BOOTDEVICE_FOUND]="RBK0142E: Bootgerät kann nicht erkannt werden. Bitte das Problem auf https://github.com/framps/raspiBackup/issues oder auf https://www.linux-tips-and-tricks.de/de/fehlermeldungen melden"
MSG_FI[$MSG_NO_BOOTDEVICE_FOUND]="RBK0142E: Käynnistyslaitetta ei havaittu. Ole hyvä ja raportoi ongelmasta osoitteessa https://github.com/framps/raspiBackup/issues tai https://www.linux-tips-and-tricks.de/en/rmessages" #The 2nd link refers defaults to to en-vesion
MSG_FR[$MSG_NO_BOOTDEVICE_FOUND]="RBK0142E: Le périphérique de Boot n'est pas reconnu. Veuillez signaler le problème sur https://github.com/framps/raspiBackup/issues ou sur https://www.linux-tips-and-tricks.de/en/rmessages" #Le 2eme lien renvoie vers la version anglaise
MSG_FORCE_SFDISK=143
MSG_EN[$MSG_FORCE_SFDISK]="RBK0143W: Target %s does not match with backup. Partitioning forced"
MSG_DE[$MSG_FORCE_SFDISK]="RBK0143W: Ziel %s passt nicht zu dem Backup. Partitionierung wird trotzdem vorgenommen"
MSG_FI[$MSG_FORCE_SFDISK]="RBK0143W: Kohde %s ei täsmää varmuuskopion kanssa. Pakotetaan osiointi"
MSG_FR[$MSG_FORCE_SFDISK]="RBK0143W: La cible %s ne correspond pas à la sauvegarde. Partitionnement forcé"
MSG_SKIP_SFDISK=144
MSG_EN[$MSG_SKIP_SFDISK]="RBK0144W: Target %s will not be partitioned. Using existing partitions"
MSG_DE[$MSG_SKIP_SFDISK]="RBK0144W: Ziel %s wird nicht partitioniert. Existierende Partitionen werden benutzt"
MSG_FI[$MSG_SKIP_SFDISK]="RBK0144W: Kohdetta %s ei osioida. Käytetään olemassaolevia osioita"
MSG_FR[$MSG_SKIP_SFDISK]="RBK0144W: La cible %s ne sera pas partitionné. Les partitions existantes sont utilisées"
MSG_SKIP_CREATING_PARTITIONS=145
MSG_EN[$MSG_SKIP_CREATING_PARTITIONS]="RBK0145W: Partition creation skipped. Using existing partitions"
MSG_DE[$MSG_SKIP_CREATING_PARTITIONS]="RBK0145W: Partitionen werden nicht erstellt. Existierende Partitionen werden benutzt"
MSG_FI[$MSG_SKIP_CREATING_PARTITIONS]="RBK0145W: Osion luonti ohitettu. Käytetään olemassaolevia osioita"
MSG_FR[$MSG_SKIP_CREATING_PARTITIONS]="RBK0145W: Création de partition ignorée. Les partitions existantes sont utilisées"
MSG_NO_PARTITION_TABLE_DEFINED=146
MSG_EN[$MSG_NO_PARTITION_TABLE_DEFINED]="RBK0146I: No partitiontable found on %s"
MSG_DE[$MSG_NO_PARTITION_TABLE_DEFINED]="RBK0146I: Keine Partitionstabelle auf %s gefunden"
MSG_FI[$MSG_NO_PARTITION_TABLE_DEFINED]="RBK0146I: Osiotaulukkoa ei löytynyt laitteella %s"
MSG_FR[$MSG_NO_PARTITION_TABLE_DEFINED]="RBK0146I: Aucune table de partition trouvée sur %s"
MSG_BACKUP_PARTITION_FAILED=147
MSG_EN[$MSG_BACKUP_PARTITION_FAILED]="RBK0147E: Backup of partition %s failed with RC %s"
MSG_DE[$MSG_BACKUP_PARTITION_FAILED]="RBK0147E: Sicherung der Partition %s schlug fehl mit RC %s"
MSG_FI[$MSG_BACKUP_PARTITION_FAILED]="RBK0147E: Osion %s varmuuskopiointi epäonnistui, RC %s"
MSG_FR[$MSG_BACKUP_PARTITION_FAILED]="RBK0147E: La sauvegarde de la partition %s a échoué, code erreur %s"
MSG_STACK_TRACE=148
MSG_EN[$MSG_STACK_TRACE]="RBK0148E: @@@@@@@@@@@@@@@@@@@@ Stacktrace @@@@@@@@@@@@@@@@@@@@"
MSG_DE[$MSG_STACK_TRACE]="RBK0148E: @@@@@@@@@@@@@@@@@@@@ Stacktrace @@@@@@@@@@@@@@@@@@@@"
MSG_FI[$MSG_STACK_TRACE]="RBK0148E: @@@@@@@@@@@@@@@@@@@@ Stacktrace @@@@@@@@@@@@@@@@@@@@"
MSG_FR[$MSG_STACK_TRACE]="RBK0148E: @@@@@@@@@@@@@@@@@@@@ Stacktrace @@@@@@@@@@@@@@@@@@@@"
MSG_FILE_ARG_NOT_FOUND=149
MSG_EN[$MSG_FILE_ARG_NOT_FOUND]="RBK0149E: File %s does not exist"
MSG_DE[$MSG_FILE_ARG_NOT_FOUND]="RBK0149E: Datei %s existiert nicht"
MSG_FI[$MSG_FILE_ARG_NOT_FOUND]="RBK0149E: Tiedostoa %s ei ole"
MSG_FR[$MSG_FILE_ARG_NOT_FOUND]="RBK0149E: Le fichier %s n'existe pas"
MSG_MAX_4GB_LIMIT=150
MSG_EN[$MSG_MAX_4GB_LIMIT]="RBK0150W: Maximum file size in backup directory %s is limited to 4GB"
MSG_DE[$MSG_MAX_4GB_LIMIT]="RBK0150W: Maximale Dateigröße im Backupverzeichnis %s ist auf 4GB begrenzt"
MSG_FI[$MSG_MAX_4GB_LIMIT]="RBK0150W: Suurin tiedoston koko varmuuskopion hakemistossa %s on 4Gt"
MSG_FR[$MSG_MAX_4GB_LIMIT]="RBK0150W: La taille maximale du fichier dans le répertoire de sauvegarde %s est limitée à 4 Go"
MSG_USING_BACKUPPATH=151
MSG_EN[$MSG_USING_BACKUPPATH]="RBK0151I: Using backuppath %s with partition type %s"
MSG_DE[$MSG_USING_BACKUPPATH]="RBK0151I: Backuppfad %s mit Partitionstyp %s wird benutzt"
MSG_FI[$MSG_USING_BACKUPPATH]="RBK0151I: Käytetään varmuuskopiointihakemistoa %s osiotyypin %s kanssa"
MSG_FR[$MSG_USING_BACKUPPATH]="RBK0151I: Le chemin de sauvegarde %s du type de partition %s est utilisé"
MSG_MKFS_FAILED=152
MSG_EN[$MSG_MKFS_FAILED]="RBK0152E: Unable to create filesystem: '%s' - RC: %s"
MSG_DE[$MSG_MKFS_FAILED]="RBK0152E: Dateisystem kann nicht erstellt werden: '%s' - RC: %s"
MSG_FI[$MSG_MKFS_FAILED]="RBK0152E: Ei voitu luoda tiedostojärjestelmää: '%s' - RC: %s"
MSG_FR[$MSG_MKFS_FAILED]="RBK0152E: Création du fichiers système '%s' impossible - Code erreur: %s"
MSG_LABELING_FAILED=153
MSG_EN[$MSG_LABELING_FAILED]="RBK0153E: Unable to label partition: '%s' - RC: %s"
MSG_DE[$MSG_LABELING_FAILED]="RBK0153E: Partition kann nicht mit einem Label versehen werden: '%s' - RC: %s"
MSG_FI[$MSG_LABELING_FAILED]="RBK0153E: Ei voitu nimetä osiota: '%s' - RC: %s"
MSG_FR[$MSG_LABELING_FAILED]="RBK0153E: Impossible d'étiqueter la partition : '%s' - Code erreur: %s"
MSG_RESTORE_DEVICE_MOUNTED=154
MSG_EN[$MSG_RESTORE_DEVICE_MOUNTED]="RBK0154E: Restore is not possible when a partition of device %s is mounted"
MSG_DE[$MSG_RESTORE_DEVICE_MOUNTED]="RBK0154E: Ein Restore ist nicht möglich wenn eine Partition von %s gemounted ist"
MSG_FI[$MSG_RESTORE_DEVICE_MOUNTED]="RBK0154E: Palautus ei ole mahdollista laitteen %s osion ollessa käyttöönotettuna"
MSG_FR[$MSG_RESTORE_DEVICE_MOUNTED]="RBK0154E: Restauration impossible si une partition du périphérique %s est montée"
MSG_INVALID_RESTORE_ROOT_PARTITION=155
MSG_EN[$MSG_INVALID_RESTORE_ROOT_PARTITION]="RBK0155E: Restore root partition %s is no partition"
MSG_DE[$MSG_INVALID_RESTORE_ROOT_PARTITION]="RBK0155E: Ziel Rootpartition %s ist keine Partition"
MSG_FI[$MSG_INVALID_RESTORE_ROOT_PARTITION]="RBK0155E: Palautettava juuriosio %s ei ole osio"
MSG_FR[$MSG_INVALID_RESTORE_ROOT_PARTITION]="RBK0155E: La partition Root cible %s n'est pas une partition"
MSG_SKIP_STARTING_SERVICES=156
MSG_EN[$MSG_SKIP_STARTING_SERVICES]="RBK0156W: No services to start"
MSG_DE[$MSG_SKIP_STARTING_SERVICES]="RBK0156W: Keine Systemd Services sind zu starten"
MSG_FI[$MSG_SKIP_STARTING_SERVICES]="RBK0156W: Ei käynnistettäviä palveluita"
MSG_FR[$MSG_SKIP_STARTING_SERVICES]="RBK0156W: Pas de service système à démarrer"
MSG_SKIP_STOPPING_SERVICES=157
MSG_EN[$MSG_SKIP_STOPPING_SERVICES]="RBK0157W: No services to stop"
MSG_DE[$MSG_SKIP_STOPPING_SERVICES]="RBK0157W: Keine Systemd Services sind zu stoppen"
MSG_FI[$MSG_SKIP_STOPPING_SERVICES]="RBK0157W: Ei pysäytettäviä palveluita"
MSG_FR[$MSG_SKIP_STOPPING_SERVICES]="RBK0157W: Aucun service système à arrêter"
MSG_MAIN_BACKUP_PROGRESSING=158
MSG_EN[$MSG_MAIN_BACKUP_PROGRESSING]="RBK0158I: Creating native %s backup %s"
MSG_DE[$MSG_MAIN_BACKUP_PROGRESSING]="RBK0158I: %s Backup %s wird erstellt"
MSG_FI[$MSG_MAIN_BACKUP_PROGRESSING]="RBK0158I: Luodaan natiivi %s-varmuuskopio kohteeseen %s"
MSG_FR[$MSG_MAIN_BACKUP_PROGRESSING]="RBK0158I: Création en cours de la sauvegarde %s"
MSG_BACKUPS_KEPT=159
MSG_EN[$MSG_BACKUPS_KEPT]="RBK0159I: %s backups kept for %s backup type. Please be patient"
MSG_DE[$MSG_BACKUPS_KEPT]="RBK0159I: %s Backups werden für den Backuptyp %s aufbewahrt. Bitte Geduld"
MSG_FI[$MSG_BACKUPS_KEPT]="RBK0159I: %s varmuuskopiota pidetään %s-varmuuskopiotyypille. Ole hyvä ja odota"
MSG_FR[$MSG_BACKUPS_KEPT]="RBK0159I: %s sauvegardes sont conservées pour le type de sauvegarde %s SVP patientez"
#MSG_TARGETSD_SIZE_TOO_SMALL=160
#MSG_EN[$MSG_TARGETSD_SIZE_TOO_SMALL]="RBK0160E: Target %s with %s is smaller than backup source with %s"
#MSG_DE[$MSG_TARGETSD_SIZE_TOO_SMALL]="RBK0160E: Ziel %s mit %s ist kleiner als die Backupquelle mit %s"
#MSG_FI[$MSG_TARGETSD_SIZE_TOO_SMALL]="RBK0160E: Kohde %s koollaan %s on pienempi kuin varmuuskopion lähde kooltaan %s"
#MSG_FR[$MSG_TARGETSD_SIZE_TOO_SMALL]="RBK0160E: La cible %s avec %s est plus petite que la source de sauvegarde avec %s"
#MSG_TARGETSD_SIZE_BIGGER=161
#MSG_EN[$MSG_TARGETSD_SIZE_BIGGER]="RBK0161W: Target %s with %s is larger than backup source with %s. You waste %s"
#MSG_DE[$MSG_TARGETSD_SIZE_BIGGER]="RBK0161W: Ziel %s mit %s ist größer als die Backupquelle mit %s. %s sind ungenutzt"
#MSG_FI[$MSG_TARGETSD_SIZE_BIGGER]="RBK0161W: Kohde %s koollaan %s on suurempi kuin varmuuskopion lähde kooltaan %s. %s jää hyödyntämättä"
#MSG_FR[$MSG_TARGETSD_SIZE_BIGGER]="RBK0161W: La cible %s avec %s est plus grande que la source de sauvegarde avec %s. %s sont inutilisés"
MSG_RESTORE_ABORTED=162
MSG_EN[$MSG_RESTORE_ABORTED]="RBK0162I: Restore aborted"
MSG_DE[$MSG_RESTORE_ABORTED]="RBK0162I: Restore abgebrochen"
MSG_FI[$MSG_RESTORE_ABORTED]="RBK0162I: Palautus keskeytetty"
MSG_FR[$MSG_RESTORE_ABORTED]="RBK0162I: Restauration annulée"
MSG_CTRLC_DETECTED=163
MSG_EN[$MSG_CTRLC_DETECTED]="RBK0163E: Script execution canceled with CTRL C"
MSG_DE[$MSG_CTRLC_DETECTED]="RBK0163E: Scriptausführung mit CTRL C abgebrochen"
MSG_FI[$MSG_CTRLC_DETECTED]="RBK0163E: Skriptin suoritus peruutettu CTRL C-näppäinyhdistelmällä"
MSG_FR[$MSG_CTRLC_DETECTED]="RBK0163E: Exécution du script interrompue avec CTRL C"
MSG_HARDLINK_ERROR=164
MSG_EN[$MSG_HARDLINK_ERROR]="RBK0164E: Unable to create hardlinks on %s. RC %s"
MSG_DE[$MSG_HARDLINK_ERROR]="RBK0164E: Es können keine Hardlinks auf %s erstellt werden. RC %s"
MSG_FI[$MSG_HARDLINK_ERROR]="RBK0164E: Hardlink-tietojen luonti epäonnistui %s. RC %s"
MSG_FR[$MSG_HARDLINK_ERROR]="RBK0164E: Les liens physiques ne peuvent pas être créés %s. Code erreur %s"
MSG_INTRO_BETA_MESSAGE=165
MSG_EN[$MSG_INTRO_BETA_MESSAGE]="RBK0165W: =========> NOTE <========= \
${NL}!!! RBK0165W: This is a betaversion and should not be used in production. \
${NL}!!! RBK0165W: =========> NOTE <========="
MSG_DE[$MSG_INTRO_BETA_MESSAGE]="RBK0165W: =========> HINWEIS <========= \
${NL}!!! RBK0165W: Dieses ist eine Betaversion welche nicht in Produktion benutzt werden sollte. \
${NL}!!! RBK0165W: =========> HINWEIS <========="
MSG_FI[$MSG_INTRO_BETA_MESSAGE]="RBK0165W: =========> HUOM <========= \
${NL}!!! RBK0165W: Tämä on betaversio, jota ei tule käyttää tuotannossa. \
${NL}!!! RBK0165W: =========> HUOM <========= (FI)"
MSG_FR[$MSG_INTRO_BETA_MESSAGE]="RBK0165W: =========> REMARQUE <========= \
${NL}!!! RBK0165W: Ceci est une version bêta qui ne doit pas être utilisée en production. \
${NL}!!! RBK0165W: =========> REMARQUE <========= (FI)"
MSG_UMOUNT_ERROR=166
MSG_EN[$MSG_UMOUNT_ERROR]="RBK0166E: Umount for %s failed. RC %s. Maybe mounted somewhere else?"
MSG_DE[$MSG_UMOUNT_ERROR]="RBK0166E: Umount für %s fehlerhaft. RC %s. Vielleicht noch woanders gemounted?"
MSG_FI[$MSG_UMOUNT_ERROR]="RBK0166E: Osion %s käytöstä poisto (umount) epäonnistui. RC %s. Jokin muu käyttää sitä mahdollisesti?"
MSG_FR[$MSG_UMOUNT_ERROR]="RBK0166E: Umount incorrect pour %s Code erreur %s. Peut-elle être montée ailleurs ?"
MSG_SENDING_EMAIL=167
MSG_EN[$MSG_SENDING_EMAIL]="RBK0167I: Sending email"
MSG_DE[$MSG_SENDING_EMAIL]="RBK0167I: Eine eMail wird versendet"
MSG_FI[$MSG_SENDING_EMAIL]="RBK0167I: Lähetetään sähköposti"
MSG_FR[$MSG_SENDING_EMAIL]="RBK0167I: Envoi d'un e-mail"
MSG_BETAVERSION_AVAILABLE=168
MSG_EN[$MSG_BETAVERSION_AVAILABLE]="RBK0168I: $SMILEY_BETA_AVAILABLE $MYSELF beta version %s is available. Any help to test this beta is appreciated. Just upgrade to the new beta version with option -U. Restore to the previous version with option -V"
MSG_DE[$MSG_BETAVERSION_AVAILABLE]="RBK0168I: $SMILEY_BETA_AVAILABLE $MYSELF Beta Version %s ist verfügbar. Hilfe beim Testen dieser Beta ist sehr willkommen. Einfach auf die neue Beta Version mit der Option -U upgraden. Die vorhergehende Version kann mit der Option -V wiederhergestellt werden"
MSG_FI[$MSG_BETAVERSION_AVAILABLE]="RBK0168I: $SMILEY_BETA_AVAILABLE $MYSELF betaversio %s on saatavilla. Betatestaajien apua arvostetaan. Päivitä uuteen betaversioon valinnalla -U. Palaa edelliseen versioon valinnalla -V"
MSG_FR[$MSG_BETAVERSION_AVAILABLE]="RBK0168I: $SMILEY_BETA_AVAILABLE $MYSELF La version bêta %s est disponible. Une aide pour tester cette version bêta est la bienvenue. Passez simplement à la nouvelle version bêta avec l'option -U. La version précédente peut être restaurée avec l'option -V "
MSG_ROOT_PARTITION_NOT_FOUND=169
MSG_EN[$MSG_ROOT_PARTITION_NOT_FOUND]="RBK0169E: Target root partition %s does not exist"
MSG_DE[$MSG_ROOT_PARTITION_NOT_FOUND]="RBK0169E: Ziel Rootpartition %s existiert nicht"
MSG_FI[$MSG_ROOT_PARTITION_NOT_FOUND]="RBK0169E: Kohdejuuriosiota %s ei ole"
MSG_FR[$MSG_ROOT_PARTITION_NOT_FOUND]="RBK0169E: La partition Root cible %s n'existe pas"
MSG_MISSING_R_OPTION=170
MSG_EN[$MSG_MISSING_R_OPTION]="RBK0170E: Backup uses an external root partition. -R option missing"
MSG_DE[$MSG_MISSING_R_OPTION]="RBK0170E: Backup benutzt eine externe root Partition. Die Option -R fehlt"
MSG_FI[$MSG_MISSING_R_OPTION]="RBK0170E: Varmuuskopiointi käyttää ulkoista juurihakemistoa. Valinta -R puuttuu-"
MSG_FR[$MSG_MISSING_R_OPTION]="RBK0170E: La sauvegarde utilise une partition Root externe. L'option -R est manquante"
MSG_NOPARTITIONS_TOBACKUP_FOUND=171
MSG_EN[$MSG_NOPARTITIONS_TOBACKUP_FOUND]="RBK0171E: Unable to detect any partitions to backup"
MSG_DE[$MSG_NOPARTITIONS_TOBACKUP_FOUND]="RBK0171E: Es können keine zu sichernde Partitionen gefunden werden"
MSG_FI[$MSG_NOPARTITIONS_TOBACKUP_FOUND]="RBK0171E: Varmuuskopioitavia osioita ei havaittu"
MSG_FR[$MSG_NOPARTITIONS_TOBACKUP_FOUND]="RBK0171E: Aucune partition à sauvegarder n'a été trouvée"
MSG_UNABLE_TO_CREATE_DIRECTORY=172
MSG_EN[$MSG_UNABLE_TO_CREATE_DIRECTORY]="RBK0172E: Unable to create directory %s"
MSG_DE[$MSG_UNABLE_TO_CREATE_DIRECTORY]="RBK0172E: Verzeichnis %s kann nicht erstellt werden"
MSG_FI[$MSG_UNABLE_TO_CREATE_DIRECTORY]="RBK0172E: Hakemistoa %s ei voida luoda"
MSG_FR[$MSG_UNABLE_TO_CREATE_DIRECTORY]="RBK0172E: Impossible de créer le répertoire %s"
MSG_INTRO_HOTFIX_MESSAGE=173
MSG_EN[$MSG_INTRO_HOTFIX_MESSAGE]="RBK0173W: =========> NOTE <========= \
${NL}!!! RBK0173W: This is a temporary hotfix and has to be upgraded to next available version as soon as one is available. \
${NL}!!! RBK0173W: =========> NOTE <========="
MSG_DE[$MSG_INTRO_HOTFIX_MESSAGE]="RBK0173W: =========> HINWEIS <========= \
${NL}!!! RBK0173W: Dieses ist ein temporärer Hotfix der auf die nächste Version upgraded werden muss sobald eine verfügbar ist. \
${NL}!!! RBK0173W: =========> HINWEIS <========="
MSG_FI[$MSG_INTRO_HOTFIX_MESSAGE]="RBK0173W: =========> HUOM <========= \
${NL}!!! RBK0173W: Tämä on väliaikainen pikakorjaus ja tulee päivittää heti, kun uudempi versio on saatavilla. \
${NL}!!! RBK0173W: =========> HUOM <========="
MSG_FR[$MSG_INTRO_HOTFIX_MESSAGE]="RBK0173W: =========> REMARQUE <========= \
${NL}!!! RBK0173W: Il s'agit d'un correctif temporaire qui doit être mis à niveau dans la prochaine version dès qu'elle sera disponible. \
${NL}!!! RBK0173W: =========> REMARQUE <========="
MSG_TOOL_ERROR_SKIP=174
MSG_EN[$MSG_TOOL_ERROR_SKIP]="RBK0174I: Backup tool %s error %s ignored. For errormessages see log file"
MSG_DE[$MSG_TOOL_ERROR_SKIP]="RBK0174I: Backupprogramm %s Fehler %s wurde ignoriert. Fehlermeldungen finden sich im Logfile"
MSG_FI[$MSG_TOOL_ERROR_SKIP]="RBK0174I: Varmuuskopiotyökalun %s virhe %s ohitettiin. Lue virheviestit lokitiedostosta"
MSG_FR[$MSG_TOOL_ERROR_SKIP]="RBK0174I: L'erreur %s lors de la sauvegarde a été ignorée. Les messages peuvent être consultés dans le fichier journal"
MSG_SCRIPT_UPDATE_NOT_REQUIRED=175
MSG_EN[$MSG_SCRIPT_UPDATE_NOT_REQUIRED]="RBK0175I: %s version %s is newer than version %s"
MSG_DE[$MSG_SCRIPT_UPDATE_NOT_REQUIRED]="RBK0175I: %s Version %s ist aktueller als Version %s"
MSG_FI[$MSG_SCRIPT_UPDATE_NOT_REQUIRED]="RBK0175I: %s:n versio %s on uudempi kuin versio %s"
MSG_FR[$MSG_SCRIPT_UPDATE_NOT_REQUIRED]="RBK0175I: %s version %s est plus récent que cette version %s"
MSG_RSYNC_DOES_NOT_SUPPORT_PROGRESS=176
MSG_EN[$MSG_RSYNC_DOES_NOT_SUPPORT_PROGRESS]="RBK0173E: rsync version %s doesn't support progress information"
MSG_DE[$MSG_RSYNC_DOES_NOT_SUPPORT_PROGRESS]="RBK0173E: rsync Version %s unterstüzt keine Fortschrittsanzeige"
MSG_FI[$MSG_RSYNC_DOES_NOT_SUPPORT_PROGRESS]="RBK0173E: rsyncin versio %s ei tue edistymisen seurantaa"
MSG_FR[$MSG_RSYNC_DOES_NOT_SUPPORT_PROGRESS]="RBK0173E: rsync version %s ne prend pas en charge l'affichage de la progression"
MSG_ALL_BACKUPS_KEPT=177
MSG_EN[$MSG_ALL_BACKUPS_KEPT]="RBK0177W: All backups kept for backup type %s"
MSG_DE[$MSG_ALL_BACKUPS_KEPT]="RBK0177W: Alle Backups werden für den Backuptyp %s aufbewahrt"
MSG_FI[$MSG_ALL_BACKUPS_KEPT]="RBK0177W: Kaikki varmuuskopiot pidetään varmuuskopiointityypille %s"
MSG_FR[$MSG_ALL_BACKUPS_KEPT]="RBK0177W: Toutes les sauvegardes sont conservées pour le type %s"
MSG_IMG_BOOT_BACKUP_FAILED=178
MSG_EN[$MSG_IMG_BOOT_BACKUP_FAILED]="RBK0178E: Creation of %s failed with RC %s. Usually the SD card is buggy. Option -B may help"
MSG_DE[$MSG_IMG_BOOT_BACKUP_FAILED]="RBK0178E: Erzeugung von %s Datei endet fehlerhaft mit RC %s. Normalerweise ist die SD Karte fehlerhaft. Option -B kann helfen"
MSG_FI[$MSG_IMG_BOOT_BACKUP_FAILED]="RBK0178E: %s:n luominen epäonnistui, RC %s"
MSG_FR[$MSG_IMG_BOOT_BACKUP_FAILED]="RBK0178E: La création de %s a échoué avec le code erreur %s"
MSG_IMG_BOOT_RESTORE_FAILED=179
MSG_EN[$MSG_IMG_BOOT_RESTORE_FAILED]="RBK0179E: Restore of %s file failed with RC %s"
MSG_DE[$MSG_IMG_BOOT_RESTORE_FAILED]="RBK0179E: Wiederherstellung von %s Datei endet fehlerhaft mit RC %s"
MSG_FI[$MSG_IMG_BOOT_RESTORE_FAILED]="RBK0179E: %s-tiedoston palautus epäonnistui, RC %s"
MSG_FR[$MSG_IMG_BOOT_RESTORE_FAILED]="RBK0179E: La restauration du fichier %s a échoué acec le code erreur %s"
MSG_FORMATTING_FIRST_PARTITION=180
MSG_EN[$MSG_FORMATTING_FIRST_PARTITION]="RBK0180I: Formating first partition (boot partition) %s"
MSG_DE[$MSG_FORMATTING_FIRST_PARTITION]="RBK0180I: Erste Partition (Bootpartition) %s wird formatiert"
MSG_FI[$MSG_FORMATTING_FIRST_PARTITION]="RBK0180I: Alustetaan ensimmäinen osio (käynnistysosio) %s"
MSG_FR[$MSG_FORMATTING_FIRST_PARTITION]="RBK0180I: Formatage de la première partition (partition de Boot) %s"
MSG_AFTER_STARTING_SERVICES=181
MSG_EN[$MSG_AFTER_STARTING_SERVICES]="RBK0181I: Executing commands post backup: '%s'"
MSG_DE[$MSG_AFTER_STARTING_SERVICES]="RBK0181I: Nach dem Backup ausgeführte Befehle: '%s'"
MSG_FI[$MSG_AFTER_STARTING_SERVICES]="RBK0181I: Suoritetaan varmuuskopioinnin jälkeiset komennot: '%s'"
MSG_FR[$MSG_AFTER_STARTING_SERVICES]="RBK0181I: Commandes exécutées après la sauvegarde: '%s'"
MSG_BEFORE_STOPPING_SERVICES=182
MSG_EN[$MSG_BEFORE_STOPPING_SERVICES]="RBK0182I: Executing commands pre backup: '%s'"
MSG_DE[$MSG_BEFORE_STOPPING_SERVICES]="RBK0182I: Vor dem Backup ausgeführte Befehle: '%s'"
MSG_FI[$MSG_BEFORE_STOPPING_SERVICES]="RBK0182I: Suoritetaan varmuuskopiointia edeltävät komennot: '%s'"
MSG_FR[$MSG_BEFORE_STOPPING_SERVICES]="RBK0182I: Commandes exécutées avant la sauvegarde: '%s'"
#MSG_IMG_ROOT_CHECK_FAILED=183
#MSG_EN[$MSG_IMG_ROOT_CHECK_FAILED]="RBK0183E: Rootpartition check failed with RC %s"
#MSG_DE[$MSG_IMG_ROOT_CHECK_FAILED]="RBK0183E: Rootpartitionscheck endet fehlerhaft mit RC %s"
#MSG_FI[$MSG_IMG_ROOT_CHECK_FAILED]="RBK0183E: Juuriosion tarkistus epäonnistui, RC %s"
#MSG_FR[$MSG_IMG_ROOT_CHECK_FAILED]="RBK0183E: La vérification de la partition Root a échoué ,code erreur %s"
#MSG_IMG_ROOT_CHECK_STARTED=184
#MSG_EN[$MSG_IMG_ROOT_CHECK_STARTED]="RBK0184I: Rootpartition check started"
#MSG_DE[$MSG_IMG_ROOT_CHECK_STARTED]="RBK0184I: Rootpartitionscheck gestartet"
#MSG_FI[$MSG_IMG_ROOT_CHECK_STARTED]="RBK0184I: Juuriosion tarkistus aloitettu"
#MSG_FR[$MSG_IMG_ROOT_CHECK_STARTED]="RBK0184I: Début de la vérification de la partition Root"
MSG_IMG_BOOT_CREATE_PARTITION_FAILED=185
MSG_EN[$MSG_IMG_BOOT_CREATE_PARTITION_FAILED]="RBK0185E: Bootpartition creation failed with RC %s"
MSG_DE[$MSG_IMG_BOOT_CREATE_PARTITION_FAILED]="RBK0185E: Bootpartitionserstellung endet fehlerhaft mit RC %s"
MSG_FI[$MSG_IMG_BOOT_CREATE_PARTITION_FAILED]="RBK0185E: Käynnistysosion luonti epäonnistui, RC %s"
MSG_FR[$MSG_IMG_BOOT_CREATE_PARTITION_FAILED]="RBK0185E: La création de la partition Boot a échoué , code erreur %s"
MSG_IMG_ROOT_CREATE_PARTITION_FAILED=186
MSG_EN[$MSG_IMG_ROOT_CREATE_PARTITION_FAILED]="RBK0185E: Rootpartition creation failed with RC %s"
MSG_DE[$MSG_IMG_ROOT_CREATE_PARTITION_FAILED]="RBK0185E: Rootpartitionserstellung endet fehlerhaft mit RC %s"
MSG_FI[$MSG_IMG_ROOT_CREATE_PARTITION_FAILED]="RBK0185E: Juuriosion luonti epäonnistui, RC %s"
MSG_FR[$MSG_IMG_ROOT_CREATE_PARTITION_FAILED]="RBK0185E: La création de la partition Root a échoué , code erreur %s"
MSG_DETAILED_ROOT_CHECKING=187
MSG_EN[$MSG_DETAILED_ROOT_CHECKING]="RBK0187W: Rootpartition %s will be checked for bad blocks during formatting. This will take some time. Please be patient"
MSG_DE[$MSG_DETAILED_ROOT_CHECKING]="RBK0187W: Rootpartitionsformatierung für %s prüft auf fehlerhafte Blocks. Das wird länger dauern. Bitte Geduld"
MSG_FI[$MSG_DETAILED_ROOT_CHECKING]="RBK0187W: Juuriosio %s tarkistetaan viallisten lohkojen varalta alustuksen aikana. Tämä vie jonkin aikaa. Ole hyvä ja odota"
MSG_FR[$MSG_DETAILED_ROOT_CHECKING]="RBK0187W: La partition Root %s sera vérifiée pour détecter les blocs lors du formatage. SVP soyez patient.."
MSG_UPDATE_TO_BETA=188
MSG_EN[$MSG_UPDATE_TO_BETA]="RBK0188I: There is a Beta version of $MYSELF available. Upgrading current version %s to %s"
MSG_DE[$MSG_UPDATE_TO_BETA]="RBK0188I: Es ist eine Betaversion von $MYSELF verfügbar. Die momentane Version %s auf %s upgraden"
MSG_FI[$MSG_UPDATE_TO_BETA]="RBK0188I: $MYSELF betaversio on saatavilla. Päivitä nykyinen versio %s versioon %s"
MSG_FR[$MSG_UPDATE_TO_BETA]="RBK0188I: Une version bêta de $MYSELF est disponible. Mettez à niveau la version actuelle %s vers %s"
MSG_UPDATE_ABORTED=189
MSG_EN[$MSG_UPDATE_ABORTED]="RBK0189I: Version upgrade aborted"
MSG_DE[$MSG_UPDATE_ABORTED]="RBK0189I: Versionsupgrade abgebrochen"
MSG_FI[$MSG_UPDATE_ABORTED]="RBK0189I: Versiopäivitys keskeytetty"
MSG_FR[$MSG_UPDATE_ABORTED]="RBK0189I: Mise à niveau de version annulée"
MSG_UPDATE_TO_VERSION=190
MSG_EN[$MSG_UPDATE_TO_VERSION]="RBK0190I: Upgrading $MYSELF from version %s to %s"
MSG_DE[$MSG_UPDATE_TO_VERSION]="RBK0190I: Es wird $MYSELF von Version %s auf Version %s upgraded"
MSG_FI[$MSG_UPDATE_TO_VERSION]="RBK0190I: Päivitetään $MYSELF versiosta %s versioon %s"
MSG_FR[$MSG_UPDATE_TO_VERSION]="RBK0190I: Mise à niveau de $MYSELF de la version %s à la version %s"
MSG_ADJUSTING_DISABLED=191
MSG_EN[$MSG_ADJUSTING_DISABLED]="RBK0191E: Target %s with %s is smaller than backup source with %s. root partition resizing is disabled"
MSG_DE[$MSG_ADJUSTING_DISABLED]="RBK0191E: Ziel %s mit %s ist kleiner als die Backupquelle mit %s. Verkleinern der root Partition ist ausgeschaltet"
MSG_FI[$MSG_ADJUSTING_DISABLED]="RBK0191E: Kohde %s kooltaan %s on pienempi kuin varmuuskopion lähde kooltaan %s. Juuriosion pienentäminen on pois käytöstä"
MSG_FR[$MSG_ADJUSTING_DISABLED]="RBK0191E: La cible %s avec %s est plus petite que la source sauvegardée avec %s. La réduction de la partition Root est désactivée"
MSG_INTRO_DEV_MESSAGE=192
MSG_EN[$MSG_INTRO_DEV_MESSAGE]="RBK0192W: =========> NOTE <========= \
${NL}!!! RBK0192W: This is a development version and should not be used in production. \
${NL}!!! RBK0192W: =========> NOTE <========="
MSG_DE[$MSG_INTRO_DEV_MESSAGE]="RBK0192W: =========> HINWEIS <========= \
${NL}!!! RBK0192W: Dieses ist eine Entwicklerversion welcher nicht in Produktion benutzt werden sollte. \
${NL}!!! RBK0192W: =========> HINWEIS <========="
MSG_FI[$MSG_INTRO_DEV_MESSAGE]="RBK0192W: =========> HUOM <========= \
${NL}!!! RBK0192W: Tämä on kehitysversio, jota ei tule käyttää tuotannossa. \
${NL}!!! RBK0192W: =========> HUOM <========="
MSG_FR[$MSG_INTRO_DEV_MESSAGE]="RBK0192W: =========> MESSAGE <========= \
${NL}!!! RBK0192W: Il s'agit d'une version de développement qui ne doit pas être utilisée en production. \
${NL}!!! RBK0192W: =========> MESSAGE <========="
MSG_MISSING_COMMANDS=193
MSG_EN[$MSG_MISSING_COMMANDS]="RBK0193E: Missing required commands '%s'"
MSG_DE[$MSG_MISSING_COMMANDS]="RBK0193E: Erforderliche Befehle '%s' nicht vorhanden"
MSG_FI[$MSG_MISSING_COMMANDS]="RBK0193E: Vaadittavia komentoja puuttuu '%s'"
MSG_FR[$MSG_MISSING_COMMANDS]="RBK0193E: Commandes requises '%s' absentes"
MSG_MISSING_PACKAGES=194
MSG_EN[$MSG_MISSING_PACKAGES]="RBK0194E: Missing required packages. Install them with 'sudo apt-get install %s'"
MSG_DE[$MSG_MISSING_PACKAGES]="RBK0194E: Erforderliche Pakete nicht installiert. Installiere sie mit 'sudo apt-get install %s'"
MSG_FI[$MSG_MISSING_PACKAGES]="RBK0194E: Vaadittavia paketteja puuttuu. Asenna ne suorittamalla 'sudo apt-get install %s'"
MSG_FR[$MSG_MISSING_PACKAGES]="RBK0194E: Package requis non installés. Installez-le avec 'sudo apt-get install %s'"
MSG_FORCE_UPDATE=195
MSG_EN[$MSG_FORCE_UPDATE]="RBK0195I: Update $MYSELF to version %s"
MSG_DE[$MSG_FORCE_UPDATE]="RBK0195I: $MYSELF auf %s aktualisieren"
MSG_FI[$MSG_FORCE_UPDATE]="RBK0195I: Päivitä $MYSELF versioon %s"
MSG_FR[$MSG_FORCE_UPDATE]="RBK0195I: Mettez à jour $MYSELF vers la version %s"
#MSG_NO_HARDLINKS_USED=196
#MSG_EN[$MSG_NO_HARDLINKS_USED]="RBK0196W: No hardlinks supported on %s"
#MSG_DE[$MSG_NO_HARDLINKS_USED]="RBK0196W: %s unterstützt keine Hardlinks"
#MSG_FI[$MSG_NO_HARDLINKS_USED]="RBK0196W: Hardlink-tietoja ei tueta kohteella %s"
#MSG_FR[$MSG_NO_HARDLINKS_USED]="RBK0196W: Aucun lien physique pris en charge sur %s"
MSG_EMAIL_SEND_FAILED=197
MSG_EN[$MSG_EMAIL_SEND_FAILED]="RBK0197W: eMail send command %s failed with RC %s"
MSG_DE[$MSG_EMAIL_SEND_FAILED]="RBK0197W: eMail mit %s versenden endet fehlerhaft mit RC %s"
MSG_FI[$MSG_EMAIL_SEND_FAILED]="RBK0197W: Sähköpostin lähettäminen komennolla %s epäonnistui, RC %s"
MSG_FR[$MSG_EMAIL_SEND_FAILED]="RBK0197W: L'envoi d'un e-mail avec %s a échoué ,code erreur %s"
MSG_BEFORE_START_SERVICES_FAILED=198
MSG_EN[$MSG_BEFORE_START_SERVICES_FAILED]="RBK0198E: Pre backup commands failed with %s"
MSG_DE[$MSG_BEFORE_START_SERVICES_FAILED]="RBK0198E: Fehler in vor dem Backup ausgeführten Befehlen %s"
MSG_FI[$MSG_BEFORE_START_SERVICES_FAILED]="RBK0198E: Varmuuskopiota edeltävät komennot epäonnistuivat, RC %s"
MSG_FR[$MSG_BEFORE_START_SERVICES_FAILED]="RBK0198E: Les commandes de pré-sauvegarde ont échoué ,Code erreur %s"
MSG_MISSING_RESTOREDEVICE_OPTION=199
MSG_EN[$MSG_MISSING_RESTOREDEVICE_OPTION]="RBK0199E: Option -R requires also option -d"
MSG_DE[$MSG_MISSING_RESTOREDEVICE_OPTION]="RBK0199E: Option -r benötigt auch Option -d"
MSG_FI[$MSG_MISSING_RESTOREDEVICE_OPTION]="RBK0199E: Valinta -R vaatii myös valinnan -d"
MSG_FR[$MSG_MISSING_RESTOREDEVICE_OPTION]="RBK0199E: L'option -r requiert également l'option -d"
MSG_SHARED_BOOT_DEVICE=200
MSG_EN[$MSG_SHARED_BOOT_DEVICE]="RBK0200I: /boot and / located on same partition %s"
MSG_DE[$MSG_SHARED_BOOT_DEVICE]="RBK0200I: /boot und / befinden sich auf derselben Partition %s"
MSG_FI[$MSG_SHARED_BOOT_DEVICE]="RBK0200I: /boot ja / ovat samalla osiolla %s"
MSG_FR[$MSG_SHARED_BOOT_DEVICE]="RBK0200I: /boot et / sont sur la même partition %s"
MSG_BEFORE_STOP_SERVICES_FAILED=201
MSG_EN[$MSG_BEFORE_STOP_SERVICES_FAILED]="RBK0201E: Post backup commands failed with %s"
MSG_DE[$MSG_BEFORE_STOP_SERVICES_FAILED]="RBK0201E: Fehler in nach dem Backup ausgeführten Befehlen %s"
MSG_FI[$MSG_BEFORE_STOP_SERVICES_FAILED]="RBK0201E: Varmuuskopion jälkeiset komennot epäonnistuivat, RC %s"
MSG_FR[$MSG_BEFORE_STOP_SERVICES_FAILED]="RBK0201E: Echec des commandes après la sauvegarde, code erreur %s"
MSG_RESTORETEST_REQUIRED=202
MSG_EN[$MSG_RESTORETEST_REQUIRED]="RBK0202W: $SMILEY_RESTORETEST_REQUIRED Friendly reminder: Execute now a restore test. You will be reminded %s times again"
MSG_DE[$MSG_RESTORETEST_REQUIRED]="RBK0201W: $SMILEY_RESTORETEST_REQUIRED Freundlicher Hinweis: Führe einen Restoretest durch. Du wirst noch %s mal erinnert werden"
MSG_FI[$MSG_RESTORETEST_REQUIRED]="RBK0202W: $SMILEY_RESTORETEST_REQUIRED Ystävällinen muistutus: Suorita palautustestaus nyt. Sinua muistutetaan vielä %s kertaa"
MSG_FR[$MSG_RESTORETEST_REQUIRED]="RBK0202W: $SMILEY_RESTORETEST_REQUIRED Rappel amical: effectuez un test de restauration. Vous serez à nouveau rappelé %s fois"
MSG_NO_BOOT_DEVICE_DISOVERED=203
MSG_EN[$MSG_NO_BOOT_DEVICE_DISOVERED]="RBK0203E: Unable to discover boot device. Please report this issue with a debug log created with option '-l debug'"
MSG_DE[$MSG_NO_BOOT_DEVICE_DISOVERED]="RBK0203E: Boot device kann nicht erkannt werden. Bitte das Problem mit einem Debuglog welches mit Option '-l debug' erstellt wird berichten"
MSG_FI[$MSG_NO_BOOT_DEVICE_DISOVERED]="RBK0203E: Käynnistyslaitetta ei löydetty. Ole hyvä ja raportoi ongelmasta valinnalla '-l debug' luodun vianmäärityslokin kanssa"
MSG_FR[$MSG_NO_BOOT_DEVICE_DISOVERED]="RBK0203E: Le périphérique de boot n'est pas reconnu. Veuillez signaler le problème avec un journal de débogage créé avec l'option '-l debug'"
MSG_TRUNCATING_ERROR=204
MSG_EN[$MSG_TRUNCATING_ERROR]="RBK0204E: Unable to calculate truncation backup size"
MSG_DE[$MSG_TRUNCATING_ERROR]="RBK0204E: Verkleinerte Backupgröße kann nicht berechnet werden"
MSG_FI[$MSG_TRUNCATING_ERROR]="RBK0204E: Typistetyn varmuuskopion kokoa ei voitu laskea"
MSG_FR[$MSG_TRUNCATING_ERROR]="RBK0204E: Impossible de calculer la taille réduite de la sauvegarde"
MSG_CLEANUP_BACKUP_VERSION=205
MSG_EN[$MSG_CLEANUP_BACKUP_VERSION]="RBK0205I: Deleting oldest backup in %s. This may take some time. Please be patient"
MSG_DE[$MSG_CLEANUP_BACKUP_VERSION]="RBK0205I: Ältestes Backup in %s wird gelöscht. Das kann etwas dauern. Bitte Geduld"
MSG_FI[$MSG_CLEANUP_BACKUP_VERSION]="RBK0205I: Poistetaan vanhin varmuuskopio hakemistosta %s. Tämä saattaa kestää jonkin aikaa. Ole hyvä ja odota"
MSG_FR[$MSG_CLEANUP_BACKUP_VERSION]="RBK0205I: Suppression de la sauvegarde la plus ancienne dans %s. Cela peut prendre du temps. SVP soyez patient"
MSG_CREATING_UUID=206
MSG_EN[$MSG_CREATING_UUID]="RBK0206I: Creating new %s %s on %s"
MSG_DE[$MSG_CREATING_UUID]="RBK0206I: Erzeuge neue %s %s auf %s"
MSG_FI[$MSG_CREATING_UUID]="RBK0206I: Luodaan uusi %s %s kohteelle %s"
MSG_FR[$MSG_CREATING_UUID]="RBK0206I: Création nouvelle %s %s pour %s"
MSG_MISSING_PARTITION=207
MSG_EN[$MSG_MISSING_PARTITION]="RBK0207E: Missing partitions on %s"
MSG_DE[$MSG_MISSING_PARTITION]="RBK0207E: Keine Partitionen auf %s gefunden"
MSG_FI[$MSG_MISSING_PARTITION]="RBK0207E: Osioita puuttuu laitteelta %s"
MSG_FR[$MSG_MISSING_PARTITION]="RBK0207E: Aucune partition trouvée sur %s"
MSG_NO_UUID_SYNCHRONIZED=208
MSG_EN[$MSG_NO_UUID_SYNCHRONIZED]="RBK0208W: No UUID updated in %s for %s. Backup may not boot correctly"
MSG_DE[$MSG_NO_UUID_SYNCHRONIZED]="RBK0208W: Es konnte keine UUID in %s für %s erneuert werden. Das Backup könnte nicht starten"
MSG_FI[$MSG_NO_UUID_SYNCHRONIZED]="RBK0208W: %s ei päivittänyt UUID-tunnusta kohteelle %s. Varmuuskopio ei välttämättä käynnisty oikein"
MSG_FR[$MSG_NO_UUID_SYNCHRONIZED]="RBK0208W: Un UUID dans %s pour %s n'a pas pu être renouvelé. La sauvegarde n'a pas pu démarrer"
#MSG_UUIDS_NOT_UNIQUE=209
#MSG_EN[$MSG_UUIDS_NOT_UNIQUE]="RBK0209W: UUIDs are not unique on devices and/or partitions and may cause issues. In case of error messages check them with 'sudo blkid' and make them unique"
#MSG_DE[$MSG_UUIDS_NOT_UNIQUE]="RBK0209W: UUIDs sind nicht eindeutig auf den Geräten und/oder Partitionen und kann Probleme bereiten. Falls Fehlermeldungen auftreten sollten sie mit 'sudo blkid' überprüft und dann eindeutig gemacht werden"
#MSG_FI[$MSG_UUIDS_NOT_UNIQUE]="RBK0209W: UUID:t eivot ole uniikkeja laittella ja/tai osioilla ja saattavat aiheuttaa ongelmia. Virheiden ilmaantuessa tarkista ne komennolla 'sudo blkid' ja muuta ne yksilöllisiksi"
#MSG_FR[$MSG_UUIDS_NOT_UNIQUE]="RBK0209W: Les UUID ne sont pas uniques sur les appareils et/ou les partitions et peuvent causer des problèmes. Lors de messages d'erreurs vérifiez les UUID avec 'sudo blkid' et rendez-les uniques"
MSG_MULTIPLE_PARTITIONS_FOUND_BUT_2_PARTITIONS_SAVED_ONLY=210
MSG_EN[$MSG_MULTIPLE_PARTITIONS_FOUND_BUT_2_PARTITIONS_SAVED_ONLY]="RBK0210W: More than two partitions detected. Only first two partitions are saved"
MSG_DE[$MSG_MULTIPLE_PARTITIONS_FOUND_BUT_2_PARTITIONS_SAVED_ONLY]="RBK0210W: Es existieren mehr als zwei Partitionen. Nur die ersten beiden Partitionen werden gesichert"
MSG_FI[$MSG_MULTIPLE_PARTITIONS_FOUND_BUT_2_PARTITIONS_SAVED_ONLY]="RBK0210W: Havaittu enemmän kuin kaksi osiota. Vain kaksi ensimmäistä osiota tallennetaan"
MSG_FR[$MSG_MULTIPLE_PARTITIONS_FOUND_BUT_2_PARTITIONS_SAVED_ONLY]="RBK0210W: Il y a plus de deux partitions. Seules les deux premières partitions sont sauvegardées"
#MSG_EXTERNAL_PARTITION_NOT_SAVED=211
#MSG_EN[$MSG_EXTERNAL_PARTITION_NOT_SAVED]="RBK0211E: External partition %s mounted on %s will not be saved with option -P"
#MSG_DE[$MSG_EXTERNAL_PARTITION_NOT_SAVED]="RBK0211E: Externe Partition %s die an %s gemounted ist wird mit Option -P nicht gesichert"
#MSG_FI[$MSG_EXTERNAL_PARTITION_NOT_SAVED]="RBK0211E: Ulkoinsta osiota %s, joka on otettu käyttöön kohteessa %s, ei tallenneta valinnalla -P"
#MSG_FR[$MSG_EXTERNAL_PARTITION_NOT_SAVED]="RBK0211E:La partition externe %s montée sur %s n'est pas sauvegardée avec l'option -P"
MSG_BACKUP_WARNING=212
MSG_EN[$MSG_BACKUP_WARNING]="RBK0212W: Backup finished with warnings. Check previous warning messages for details"
MSG_DE[$MSG_BACKUP_WARNING]="RBK0212W: Backup endete mit Warnungen. Siehe vorhergehende Warnmeldungen"
MSG_FI[$MSG_BACKUP_WARNING]="RBK0212W: Varmuuskopiointi valmistui sisältäen varoituksia. Katso lisätiedot edellisistä varoitusviesteistä"
MSG_FR[$MSG_BACKUP_WARNING]="RBK0212W: La sauvegarde s'est terminée avec des avertissements. Consultez ces messages pour plus de détails"
MSG_MOUNT_CHECK_ERROR=213
MSG_EN[$MSG_MOUNT_CHECK_ERROR]="RBK0213E: Mount %s to %s failed. RC %s"
MSG_DE[$MSG_MOUNT_CHECK_ERROR]="RBK0213E: Mount von %s an %s ist fehlerhaft"
MSG_FI[$MSG_MOUNT_CHECK_ERROR]="RBK0213E: Kohteen %s käyttöönotto kohteeseen %s epäonnistui. RC %s"
MSG_FR[$MSG_MOUNT_CHECK_ERROR]="RBK0213E: Échec du montage de %s sur %s. Code d'erreur %s"
#MSG_MISSING_SMART_RECYCLE_PARMS=214
#MSG_EN[$MSG_MISSING_SMART_RECYCLE_PARMS]="RBK0214E: Missing smart recycle parms in %s. Have to be four:Daily Weekly Monthly Yearly"
#MSG_DE[$MSG_MISSING_SMART_RECYCLE_PARMS]="RBK0214E: Missing smart recycle parms in %s. Es müssen vier sein: Täglich Wöchentlich Monatlich Jährlich"
#MSG_FI[$MSG_MISSING_SMART_RECYCLE_PARMS]="RBK0214E: Älykkään varmuuskopion parametrejä puuttuu parametreistä %s. Niitä tulee olla neljä: Päivittäinen Viikoittainen Kuukausittainen Vuosittainen"
#MSG_FR[$MSG_MISSING_SMART_RECYCLE_PARMS]="RBK0214E: Paramètres du cycle de statégie des sauvegardes manquants en %s. Il doit y en avoir quatre : Quotidien Hebdomadaire Mensuel Annuel"
MSG_SMART_RECYCLE_PARM_INVALID=215
MSG_EN[$MSG_SMART_RECYCLE_PARM_INVALID]="RBK0215E: Invalid smart recycle parameter %s in option '%s'"
MSG_DE[$MSG_SMART_RECYCLE_PARM_INVALID]="RBK0215E: Ungültiger smart recycle Parameter %s in Option '%s'"
MSG_FI[$MSG_SMART_RECYCLE_PARM_INVALID]="RBK0215E: Virheellinen älykkään varmuuskopion parametri %s valinnassa '%s'"
MSG_FR[$MSG_SMART_RECYCLE_PARM_INVALID]="RBK0215E: Paramètre du cycle intelligent des sauvegardes %s non valide dans l'option '%s'. "
MSG_APPLYING_BACKUP_STRATEGY_ONLY=216
MSG_EN[$MSG_APPLYING_BACKUP_STRATEGY_ONLY]="RBK0216W: Applying backup strategy in %s only"
MSG_DE[$MSG_APPLYING_BACKUP_STRATEGY_ONLY]="RBK0216W: Wende nur Backupstrategie in %s an"
MSG_FI[$MSG_APPLYING_BACKUP_STRATEGY_ONLY]="RBK0216W: Sovelletaan varmuuskopiointistrategiaa vain kohteeseen %s"
MSG_FR[$MSG_APPLYING_BACKUP_STRATEGY_ONLY]="RBK0216W: Utilisez uniquement la stratégie de sauvegarde en %s"
MSG_SMART_RECYCLE_FILES=217
MSG_EN[$MSG_SMART_RECYCLE_FILES]="RBK0217I: %s backups will be smart recycled. %s backups will be kept. Please be patient"
MSG_DE[$MSG_SMART_RECYCLE_FILES]="RBK0217I: %s Backups werden smart recycled. %s Backups werden aufgehoben. Bitte Geduld"
MSG_FI[$MSG_SMART_RECYCLE_FILES]="RBK0217I: %s varmuuskopiota käsitellään älykkäästi. %s varmuuskopiota säilytetään. Ole hyvä ja odota"
MSG_FR[$MSG_SMART_RECYCLE_FILES]="RBK0217I: %s sauvegardes de %s sont intelligemment recyclées. %s sauvegardes seront annulées. SVP soyez patient"
MSG_SMART_APPLYING_BACKUP_STRATEGY=218
MSG_EN[$MSG_SMART_APPLYING_BACKUP_STRATEGY]="RBK0218I: Applying smart backup strategy. Daily:%s Weekly:%s Monthly:%s Yearly:%s"
MSG_DE[$MSG_SMART_APPLYING_BACKUP_STRATEGY]="RBK0218I: Wende smarte Backupstrategie an. Täglich:%s Wöchentlich:%s Monatlich:%s Jährlich:%s"
MSG_FI[$MSG_SMART_APPLYING_BACKUP_STRATEGY]="RBK0218I: Sovelletaan älykästä varmuuskopiointistrategiaa. Päivittäinen:%s Viikoittainen:%s Kuukausittainen:%s Vuosittainen:%s"
MSG_FR[$MSG_SMART_APPLYING_BACKUP_STRATEGY]="RBK0218I: Appliquez une stratégie de sauvegarde intelligente: Quotidiennement : %s Hebdomadaire : %s Mensuellement : %s Annuellement : %s"
MSG_SMART_RECYCLE_NO_FILES=219
MSG_EN[$MSG_SMART_RECYCLE_NO_FILES]="RBK0219I: No backups will be smart recycled"
MSG_DE[$MSG_SMART_RECYCLE_NO_FILES]="RBK0219I: Keine Backups werden smart recycled"
MSG_FI[$MSG_SMART_RECYCLE_NO_FILES]="RBK0219I: Ei älykkään varmuuskopiolla käsiteltäviä varmuuskopioita"
MSG_FR[$MSG_SMART_RECYCLE_NO_FILES]="RBK0219I: Aucune sauvegarde dans le cycle intelligent des sauvegardes"
MSG_SMART_RECYCLE_FILE_WOULD_BE_DELETED=220
MSG_EN[$MSG_SMART_RECYCLE_FILE_WOULD_BE_DELETED]="RBK0220W: Smart backup strategy would delete %s"
MSG_DE[$MSG_SMART_RECYCLE_FILE_WOULD_BE_DELETED]="RBK0220W: Smart Backup Strategie würde %s Backup löschen"
MSG_FI[$MSG_SMART_RECYCLE_FILE_WOULD_BE_DELETED]="RBK0220W: Älykäs varmuuskopiointistrategia poistaisi kohteen %s"
MSG_FR[$MSG_SMART_RECYCLE_FILE_WOULD_BE_DELETED]="RBK0220W: La stratégie de sauvegarde intelligente supprimerait %s"
MSG_SMART_RECYCLE_FILE_DELETE=221
MSG_EN[$MSG_SMART_RECYCLE_FILE_DELETE]="RBK0221I: Smart backup strategy deletes %s"
MSG_DE[$MSG_SMART_RECYCLE_FILE_DELETE]="RBK0220I: Smart Backup Strategie löscht Backup %s"
MSG_FI[$MSG_SMART_RECYCLE_FILE_DELETE]="RBK0221I: Älykäs varmuuskopiointistrategia poistaa kohteen %s"
MSG_FR[$MSG_SMART_RECYCLE_FILE_DELETE]="RBK0221I: La stratégie de sauvegarde intelligente supprime %s"
MSG_SMART_RECYCLE_FILE_WOULD_BE_KEPT=222
MSG_EN[$MSG_SMART_RECYCLE_FILE_WOULD_BE_KEPT]="RBK0222W: Smart backup strategy would keep %s"
MSG_DE[$MSG_SMART_RECYCLE_FILE_WOULD_BE_KEPT]="RBK0222W: Smart Backup Strategie würde %s Backup behalten"
MSG_FI[$MSG_SMART_RECYCLE_FILE_WOULD_BE_KEPT]="RBK0222W: Älykäs varmuuskopiointistrategia pitäisi kohteen %s"
MSG_FR[$MSG_SMART_RECYCLE_FILE_WOULD_BE_KEPT]="RBK0222W: La stratégie de sauvegarde intelligente conserverait %s"
#MSG_UMOUNT_CHECK_ERROR=223
#MSG_EN[$MSG_UMOUNT_CHECK_ERROR]="RBK0223E: Umount %s to %s failed. RC %s"
#MSG_DE[$MSG_UMOUNT_CHECK_ERROR]="RBK0223E: Umount von %s an %s ist fehlerhaft"
#MSG_FI[$MSG_UMOUNT_CHECK_ERROR]="RBK0223E: %s käytöstä poisto %s (umount) epäonnistui. RC %s. "
#MSG_FR[$MSG_UMOUNT_CHECK_ERROR]="RBK0223E: Échec du démontage de %s sur %s , Code Erreur %s"
MSG_FILE_CONTAINS_SPACES=224
MSG_EN[$MSG_FILE_CONTAINS_SPACES]="RBK0224E: Spaces are not allowed in \"%s\""
MSG_DE[$MSG_FILE_CONTAINS_SPACES]="RBK0224E: Leerzeichen sind nicht in \"%s\" erlaubt"
MSG_FI[$MSG_FILE_CONTAINS_SPACES]="RBK0224E: Välilyönnit eivät ole sallittuja nimessä \"%s\""
MSG_FR[$MSG_FILE_CONTAINS_SPACES]="RBK0224E: Les espaces ne sont pas autorisés dans \"%s\""
MSG_UNABLE_TO_CREATE_FILE=225
MSG_EN[$MSG_UNABLE_TO_CREATE_FILE]="RBK0225E: Unable to create file %s"
MSG_DE[$MSG_UNABLE_TO_CREATE_FILE]="RBK0225E: Datei %s kann nicht erstellt werden"
MSG_FI[$MSG_UNABLE_TO_CREATE_FILE]="RBK0225E: Tiedoston %s luonti epäonnistui"
MSG_FR[$MSG_UNABLE_TO_CREATE_FILE]="RBK0225E: Impossible de créer le fichier %s"
#MSG_CONFIG_VERSION_DOES_NOT_MATCH=226
#MSG_EN[$MSG_CONFIG_VERSION_DOES_NOT_MATCH]="RBK0226W: Found unexpected config version %s in %s. Expected version %s"
#MSG_DE[$MSG_CONFIG_VERSION_DOES_NOT_MATCH]="RBK0226W: Unerwartete Konfigurationsversion %s in %s gefunden. %s wird erwartet"
#MSG_FI[$MSG_CONFIG_VERSION_DOES_NOT_MATCH]="RBK0226W: Odottamaton asetustiedoston versio %s löytyi kohteessa %s. Oletettiin versiota %s"
#MSG_FR[$MSG_CONFIG_VERSION_DOES_NOT_MATCH]="RBK0226W: Version de configuration inattendue %s trouvée dans %s. Version attendue %s"
MSG_TITLE_STARTED=227
MSG_EN[$MSG_TITLE_STARTED]="%s: Backup started"
MSG_DE[$MSG_TITLE_STARTED]="%s: Backup gestarted"
MSG_FI[$MSG_TITLE_STARTED]="%s: Varmuuskopiointi aloitettu"
MSG_FR[$MSG_TITLE_STARTED]="%s: Sauvegarde démarrée"
MSG_TELEGRAM_SEND_FAILED=228
MSG_EN[$MSG_TELEGRAM_SEND_FAILED]="RBK0228W: Sent to telegram failed. curl RC: %s - HTTP CODE: %s - Error description: %s"
MSG_DE[$MSG_TELEGRAM_SEND_FAILED]="RBK0228W: Senden an Telegram fehlerhaft. curl RC: %s - HTTP CODE: %s - Fehlerbeschreibung: %s"
MSG_FI[$MSG_TELEGRAM_SEND_FAILED]="RBK0228W: Yhteys Telegramiin epäonnistui. curl RC: %s - HTTP-koodi: %s - Virheen kuvaus: %s"
MSG_FR[$MSG_TELEGRAM_SEND_FAILED]="RBK0228W: Échec de l'envoi à Telegram. erreur curl: %s - CODE HTTP : %s - Description de l'erreur : %s"
MSG_TELEGRAM_SEND_OK=229
MSG_EN[$MSG_TELEGRAM_SEND_OK]="RBK0229I: Telegram notified"
MSG_DE[$MSG_TELEGRAM_SEND_OK]="RBK0229I: Telegram benachrichtigt"
MSG_FI[$MSG_TELEGRAM_SEND_OK]="RBK0229I: Telegram-ilmoitus lähetetty"
MSG_FR[$MSG_TELEGRAM_SEND_OK]="RBK0229I: Telegram notifié"
MSG_TELEGRAM_OPTIONS_INCOMPLETE=230
MSG_EN[$MSG_TELEGRAM_OPTIONS_INCOMPLETE]="RBK0230E: Telegram options not complete"
MSG_DE[$MSG_TELEGRAM_OPTIONS_INCOMPLETE]="RBK0230E: Telegramoptionen nicht vollständig"
MSG_FI[$MSG_TELEGRAM_OPTIONS_INCOMPLETE]="RBK0230E: Telegramin asetukset ovat puutteellliset"
MSG_FR[$MSG_TELEGRAM_OPTIONS_INCOMPLETE]="RBK0230E: Options de Telegram incomplètes"
MSG_TELEGRAM_SEND_LOG_FAILED=231
MSG_EN[$MSG_TELEGRAM_SEND_LOG_FAILED]="RBK0231W: Unable to send messages to Telegram. curl RC: %s"
MSG_DE[$MSG_TELEGRAM_SEND_LOG_FAILED]="RBK0231W: Meldungen an Telegram konnten nicht gesendet werden. curl RC: %s"
MSG_FI[$MSG_TELEGRAM_SEND_LOG_FAILED]="RBK0231W: Viestien lähettäminen Telegramiin ei onnistunut. curl RC: %s"
MSG_FR[$MSG_TELEGRAM_SEND_LOG_FAILED]="RBK0231W: Impossible d'envoyer des messages à Telegram. Erreur curl: %s"
MSG_TELEGRAM_SEND_LOG_OK=232
MSG_EN[$MSG_TELEGRAM_SEND_LOG_OK]="RBK0232I: Messages sent to Telegram"
MSG_DE[$MSG_TELEGRAM_SEND_LOG_OK]="RBK0232I: Meldungen an Telegram gesendet"
MSG_FI[$MSG_TELEGRAM_SEND_LOG_OK]="RBK0232I: Viestit lähetetty Telegramiin"
MSG_FR[$MSG_TELEGRAM_SEND_LOG_OK]="RBK0232I: Messages envoyés à Telegram"
MSG_TELEGRAM_INVALID_NOTIFICATION=233
MSG_EN[$MSG_TELEGRAM_INVALID_NOTIFICATION]="RBK0233E: Invalid Telegram notification %s detected. Valid notifications are %s"
MSG_DE[$MSG_TELEGRAM_INVALID_NOTIFICATION]="RBK0233E: Ungültige Telegram Notification %s eingegeben. Mögliche Notifikationen sind %s"
MSG_FI[$MSG_TELEGRAM_INVALID_NOTIFICATION]="RBK0233E: Epäkelpo Telegram-ilmoitus %s havaittu. Kelvollisia ilmoituksia ovat %s"
MSG_FR[$MSG_TELEGRAM_INVALID_NOTIFICATION]="RBK0233E: Notification de Telegram non valide %s . Les notifications valides sont %s"
MSG_INVALID_COLORING_OPTION=234
MSG_EN[$MSG_INVALID_COLORING_OPTION]="RBK0234E: Invalid coloring option %s detected"
MSG_DE[$MSG_INVALID_COLORING_OPTION]="RBK0234E: Ungültige Färbungsoption %s entdeckt"
MSG_FI[$MSG_INVALID_COLORING_OPTION]="RBK0234E: Epäkelpo väriasetus %s havaittu"
MSG_FR[$MSG_INVALID_COLORING_OPTION]="RBK0234E: Option de coloration non valide %s détectée"
MSG_INVALID_TRUE_FALSE_OPTION=235
MSG_EN[$MSG_INVALID_TRUE_FALSE_OPTION]="RBK0235E: Invalid true/false option %s for %s detected. Should be on, off, 0 or 1"
MSG_DE[$MSG_INVALID_TRUE_FALSE_OPTION]="RBK0235E: Ungültige an/aus Option %s für %s entdeckt. Es sollte an, aus, 0 oder 1 sein"
MSG_FI[$MSG_INVALID_TRUE_FALSE_OPTION]="RBK0235E: Virheellinen päälle/pois-valinta %s havaittu kohteelle %s. Valinnan tulee olla on, off, 0 tai 1" #on and off are OK for finnish language
MSG_FR[$MSG_INVALID_TRUE_FALSE_OPTION]="RBK0235E: Option on/off non valide %s détectée pour %s. Il doit être activé, désactivé, 0 ou 1"
#MSG_PARTITION_MODE_NO_LONGER_SUPPORTED=236
#MSG_EN[$MSG_PARTITION_MODE_NO_LONGER_SUPPORTED]="RBK0236W: Partition oriented backup will not be maintained any more and disable somewhere in the future"
#MSG_DE[$MSG_PARTITION_MODE_NO_LONGER_SUPPORTED]="RBK0236W: Partitionsorientierter Modus wird nicht mehr weiter gewartet und irgendwann in Zukunft nicht mehr verfügbar sein"
#MSG_FI[$MSG_PARTITION_MODE_NO_LONGER_SUPPORTED]="RBK0236W: Osio-orientoitua varmuuskopiota ei enää tueta ja poistetaan kokonaan käytöstä tulevaisuudessa"
#MSG_FR[$MSG_PARTITION_MODE_NO_LONGER_SUPPORTED]="RBK0236W: La sauvegarde orientée partition ne sera plus maintenue et sera désactivée quelque part dans le futur""
MSG_UPDATE_TO_LATEST_BETA=237
MSG_EN[$MSG_UPDATE_TO_LATEST_BETA]="RBK0237I: Upgrading current version %s to latest version"
MSG_DE[$MSG_UPDATE_TO_LATEST_BETA]="RBK0237I: Die momentane Version %s auf die aktuellste Version upgraden"
MSG_FI[$MSG_UPDATE_TO_LATEST_BETA]="RBK0237I: Päivitetään nykyinen versio %s viimeisimpään versioon"
MSG_FR[$MSG_UPDATE_TO_LATEST_BETA]="RBK0237I: Mise à niveau de la version actuelle %s vers la dernière version"
#MSG_JUST_TEXT=238
#MSG_EN[$MSG_JUST_TEXT]="%s"
#MSG_DE[$MSG_JUST_TEXT]="%s"
#MSG_FI[$MSG_JUST_TEXT]="%s"
#MSG_FR[$MSG_JUST_TEXT]="%s"
MSG_DOWNLOAD_FAILED=239
MSG_EN[$MSG_DOWNLOAD_FAILED]="RBK0239E: Download of %s failed. HTTP code: %s. RC: %s"
MSG_DE[$MSG_DOWNLOAD_FAILED]="RBK0239E: %s kann nicht aus dem Netz geladen werden. HTTP code: %s. RC: %s"
MSG_FI[$MSG_DOWNLOAD_FAILED]="RBK0239E: Kohteen %s lataus epäonnistui. HTTP-koodi: %s. RC: %s"
MSG_FR[$MSG_DOWNLOAD_FAILED]="RBK0239E: Le téléchargement de %s a échoué. Code HTTP : %s. RC:%s"
MSG_SAVING_CURRENT_CONFIGURATION=240
MSG_EN[$MSG_SAVING_CURRENT_CONFIGURATION]="RBK0240I: Saving current configuration %s to %s"
MSG_DE[$MSG_SAVING_CURRENT_CONFIGURATION]="RBK0240I: Aktuelle Konfiguration %s wird in %s gesichert"
MSG_FI[$MSG_SAVING_CURRENT_CONFIGURATION]="RBK0240I: Tallennetaan nykyiset asetukset %s kohteeseen %s"
MSG_FR[$MSG_SAVING_CURRENT_CONFIGURATION]="RBK0240I: Enregistrement de la configuration actuelle %s dans %s"
MSG_MERGING_VERSION=241
MSG_EN[$MSG_MERGING_VERSION]="RBK0241I: Merging current configuration %s with new configuration %s into %s"
MSG_DE[$MSG_MERGING_VERSION]="RBK0241I: Aktuelle Konfiguration %s wird mit der neuen Konfiguration %s in %s zusammengefügt"
MSG_FI[$MSG_MERGING_VERSION]="RBK0241I: Yhdistetään nykyiset asetukset %s uusiin asetuksiin %s kohteeksi %s"
MSG_FR[$MSG_MERGING_VERSION]="RBK0241I: La configuration actuelle %s sera fusionnée avec la nouvelle configuration %s dans %s"
MSG_MERGE_SUCCESSFULL=242
MSG_EN[$MSG_MERGE_SUCCESSFULL]="RBK0242I: Configuration merge finished successfully but not activated."
MSG_DE[$MSG_MERGE_SUCCESSFULL]="RBK0242I: Konfigurationszusammenfügung wurde erfolgreich beendet aber nicht aktiviert."
MSG_FI[$MSG_MERGE_SUCCESSFULL]="RBK0242I: Asetukset yhdistetty onnistuneesti, mutta niitä ei ole aktivoitu."
MSG_FR[$MSG_MERGE_SUCCESSFULL]="RBK0242I: La fusion de la configuration s'est terminée avec succès mais n'a pas été activée."
MSG_COPIED_FILE=243
MSG_EN[$MSG_COPIED_FILE]="RBK0243I: Merged configuration %s copied to %s and activated."
MSG_DE[$MSG_COPIED_FILE]="RBK0243I: Zusammengefügte Konfiguration %s nach %s kopiert und aktiviert."
MSG_FI[$MSG_COPIED_FILE]="RBK0243I: Yhdistetyt asetukset %s kopioitiin kohteeseen %s ja ne aktivoitiin."
MSG_FR[$MSG_COPIED_FILE]="RBK0243I: Configuration fusionnée %s copiée dans %s et activée."
MSG_UPDATE_CONFIG=244
MSG_EN[$MSG_UPDATE_CONFIG]="RBK0244W: Backup current configuration in %s and activate updated configuration? %s "
MSG_DE[$MSG_UPDATE_CONFIG]="RBK0244W: Soll die aktuelle Konfiguration in %s gesichert werden und die aktualisierte Konfiguration aktiviert werden? %s "
MSG_FI[$MSG_UPDATE_CONFIG]="RBK0244W: Varmuuskopioidaanko nykyiset asetukset kohteeseen %s ja aktivoidaan päivitetyt asetukset? %s "
MSG_FR[$MSG_UPDATE_CONFIG]="RBK0244W: Sauvegarder la configuration actuelle dans %s et activer la configuration mise à jour ? %s "
MSG_NO_CONFIGUPDATE_REQUIRED=245
MSG_EN[$MSG_NO_CONFIGUPDATE_REQUIRED]="RBK0245I: Local configuration version v%s does not require an update."
MSG_DE[$MSG_NO_CONFIGUPDATE_REQUIRED]="RBK0245I: Die lokale Konfigurationsversion v%s benötigt keine Aktualisierung."
MSG_FI[$MSG_NO_CONFIGUPDATE_REQUIRED]="RBK0245I: Paikallinen asetustiedoston versio v%s ei vaadi päivitystä."
MSG_FR[$MSG_NO_CONFIGUPDATE_REQUIRED]="RBK0245I: La version de configuration locale v%s ne nécessite pas de mise à jour."
#MSG_CONFIG_VERSIONS=246
#MSG_EN[$MSG_CONFIG_VERSIONS]="RBK0246I: Current configuration version: v%s. Required configuration version: v%s"
#MSG_DE[$MSG_CONFIG_VERSIONS]="RBK0246I: Lokale Konfigurationsversion: v%s. Erforderliche Konfigurationsversion: v%s"
#MSG_FI[$MSG_CONFIG_VERSIONS]="RBK0246I: Nykyisten asetusten versio: v%s. Vaaditaan versio v%s"
#MSG_FR[$MSG_CONFIG_VERSIONS]="RBK0246I: Version de configuration actuelle : v%s. Version de configuration requise : v%s"
MSG_ACTIVATE_CONFIG=247
MSG_EN[$MSG_ACTIVATE_CONFIG]="RBK0247I: Now review %s and copy the configuration file to %s to finish the configuration update"
MSG_DE[$MSG_ACTIVATE_CONFIG]="RBK0247I: Nun die zusammengefügte Konfigurationsdatei %s überprüfen und nach %s kopieren um den Konfigurationsupdate zu beenden"
MSG_FI[$MSG_ACTIVATE_CONFIG]="RBK0247I: Tarkista %s ja kopioi asetustiedosto kohteeseen %s viimeistelläksesi asetusten päivitykset"
MSG_FR[$MSG_ACTIVATE_CONFIG]="RBK0247I: Vérifiez maintenant le fichier de configuration fusionné %s et copiez-le dans %s pour terminer la mise à jour de la configuration"
MSG_ADDED_CONFIG_OPTION=248
MSG_EN[$MSG_ADDED_CONFIG_OPTION]="RBK0248I: Added option %s=%s"
MSG_DE[$MSG_ADDED_CONFIG_OPTION]="RBK0248I: Option %s=%s wurde zugefügt"
MSG_FI[$MSG_ADDED_CONFIG_OPTION]="RBK0248I: Lisättiin valinta %s=%s"
MSG_FR[$MSG_ADDED_CONFIG_OPTION]="RBK0248I: Ajout de l'option %s=%s"
MSG_DELETED_CONFIG_OPTION=249
MSG_EN[$MSG_DELETED_CONFIG_OPTION]="RBK0249I: Deleted option %s=%s"
MSG_DE[$MSG_DELETED_CONFIG_OPTION]="RBK0249I: Option %s=%s wurde gelöscht"
MSG_FI[$MSG_DELETED_CONFIG_OPTION]="RBK0249I: Poistettiin valinta %s=%s"
MSG_FR[$MSG_DELETED_CONFIG_OPTION]="RBK0249I: Option supprimée %s=%s"
MSG_CONFIG_BACKUP_FAILED=250
MSG_EN[$MSG_CONFIG_BACKUP_FAILED]="RBK0250E: Backup creation of %s failed"
MSG_DE[$MSG_CONFIG_BACKUP_FAILED]="RBK0250E: Backuperstellung von %s fehlerhaft"
MSG_FI[$MSG_CONFIG_BACKUP_FAILED]="RBK0250E: Varmuuskopion luonti kohteesta %s epäonnistui"
MSG_FR[$MSG_CONFIG_BACKUP_FAILED]="RBK0250E: La création de la sauvegarde de %s a échoué"
MSG_CHMOD_FAILED=251
MSG_EN[$MSG_CHMOD_FAILED]="RBK0251E: chmod of %s failed"
MSG_DE[$MSG_CHMOD_FAILED]="RBK0251E: chmod von %s nicht möglich"
MSG_FI[$MSG_CHMOD_FAILED]="RBK0251E: Kohteen %s chmod epäonnistui"
MSG_FR[$MSG_CHMOD_FAILED]="RBK0251E: chmod pour %s a échoué"
MSG_EMAIL_COLORING_NOT_SUPPORTED=252
MSG_EN[$MSG_EMAIL_COLORING_NOT_SUPPORTED]="RBK0252E: Invalid eMail coloring %s. Using $EMAIL_COLORING_SUBJECT. Supported are %s"
MSG_DE[$MSG_EMAIL_COLORING_NOT_SUPPORTED]="RBK0252E: Ungültige eMailKolorierung %s. Benutze $EMAIL_COLORING_SUBJECT. Unterstützt sind %s"
MSG_FI[$MSG_EMAIL_COLORING_NOT_SUPPORTED]="RBK0252E: Epäkelpo sähköpostin väritys %s. Käytetään $EMAIL_COLORING_SUBJECT. Tuettuja ovat %s"
MSG_FR[$MSG_EMAIL_COLORING_NOT_SUPPORTED]="RBK0252E: Coloration de l'e-mail %s non valide. Utiliser $EMAIL_COLORING_SUBJECT. %s qui est pris en charge"
MSG_SD_TOO_SMALL=253
MSG_EN[$MSG_SD_TOO_SMALL]="RBK0253E: Target device %s too small. Available bytes: %s. Required bytes: %s"
MSG_DE[$MSG_SD_TOO_SMALL]="RBK0253E: Zielgerät %s ist zu klein. Verfügbare Bytes: %s. Erforderliche Bytes: %s"
MSG_FI[$MSG_SD_TOO_SMALL]="RBK0253E: Kohdelaite %s on liian pieni. Käytetävissä %s tavua. Vaaditaan %s tavua"
MSG_FR[$MSG_SD_TOO_SMALL]="RBK0253E: Périphérique cible %s trop petit. Octets disponibles : %s. Octets requis : %s"
MSG_SENSITIVE_SEPARATOR=254
MSG_EN[$MSG_SENSITIVE_SEPARATOR]="+================================================================================================================================================+"
MSG_DE[$MSG_SENSITIVE_SEPARATOR]="+================================================================================================================================================+"
MSG_FI[$MSG_SENSITIVE_SEPARATOR]="+================================================================================================================================================+"
MSG_FR[$MSG_SENSITIVE_SEPARATOR]="+================================================================================================================================================+"
MSG_SENSITIVE_WARNING=255
MSG_EN[$MSG_SENSITIVE_WARNING]="| ===> A lot of sensitive information is masqueraded in this log file. Nevertheless please check the log carefully before you distribute it <=== |"
MSG_DE[$MSG_SENSITIVE_WARNING]="| ===> Viele sensitive Informationen werden in dieser Logdatei maskiert. Vor dem Verteilen des Logs sollte es trotzdem ueberprueft werden <=== |"
MSG_FI[$MSG_SENSITIVE_WARNING]="| ===> Sensitiivisiä tietoja on piilotettu tästä lokitiedostosta. Tarkista lisäksi loki huolellisesti ennen sen jakoa <=== |"
MSG_FR[$MSG_SENSITIVE_WARNING]="| ===>De nombreuses informations sensibles sont masquées dans ce fichier journal. Avant de distribuer le log, il faut quand même le vérifier<=== |"
MSG_RESTORE_WARNING=256
MSG_EN[$MSG_RESTORE_WARNING]="RBK0256W: Restore finished with warnings. Check previous warning messages for details"
MSG_DE[$MSG_RESTORE_WARNING]="RBK0256W: Restore endete mit Warnungen. Siehe vorhergehende Warnmeldungen"
MSG_FI[$MSG_RESTORE_WARNING]="RBK0256W: Palautus onnistui sisältäen vaoituksia. Katso lisätiedot edellisistä varoitusviesteistä"
MSG_FR[$MSG_RESTORE_WARNING]="RBK0256W: Restauration terminée avec des avertissements. Consultez les messages pour plus de détails"
MSG_DEPRECATED_OPTION=257
MSG_EN[$MSG_DEPRECATED_OPTION]="RBK0257W: Option %s is deprecated and will be removed in a future release"
MSG_DE[$MSG_DEPRECATED_OPTION]="RBK0257W: Option %s ist veraltet und wird in einer zukünftigen Release entfernt werden"
MSG_FI[$MSG_DEPRECATED_OPTION]="RBK0257W: Valintaa %s ei enää tueta ja se poistetaan kokonaan tulevissa julkaisuissa"
MSG_FR[$MSG_DEPRECATED_OPTION]="RBK0257W: L'option %s est obsolète et sera supprimée dans une prochaine version"
MSG_DYNAMIC_MOUNT_FAILED=258
MSG_EN[$MSG_DYNAMIC_MOUNT_FAILED]="RBK0258E: Dynamic mount of %s failed with rc %s"
MSG_DE[$MSG_DYNAMIC_MOUNT_FAILED]="RBK0258E: Dynamischer mount von %s bekommt Fehler %s"
MSG_FI[$MSG_DYNAMIC_MOUNT_FAILED]="RBK0258E: Kohteen %s dynaaminen käyttöönotto epäonnistui, RC %s"
MSG_FR[$MSG_DYNAMIC_MOUNT_FAILED]="RBK0258E: Le montage dynamique de %s a échoué avec le Code erreur %s"
MSG_DYNAMIC_MOUNT_OK=259
MSG_EN[$MSG_DYNAMIC_MOUNT_OK]="RBK0259I: Dynamic mount of %s successfull"
MSG_DE[$MSG_DYNAMIC_MOUNT_OK]="RBK0259I: Dynamischer mount von %s erfolgreich"
MSG_FI[$MSG_DYNAMIC_MOUNT_OK]="RBK0259I: Kohteen %s dynaaminen käyttöönotto onnistui"
MSG_FR[$MSG_DYNAMIC_MOUNT_OK]="RBK0259I: Montage dynamique de %s réussi"
MSG_DYNAMIC_UMOUNT_SCHEDULED=260
MSG_EN[$MSG_DYNAMIC_UMOUNT_SCHEDULED]="RBK0260I: Dynamic umount of %s will be executed"
MSG_DE[$MSG_DYNAMIC_UMOUNT_SCHEDULED]="RBK0260I: Dynamischer umount von %s wird vorgenommen"
MSG_FI[$MSG_DYNAMIC_UMOUNT_SCHEDULED]="RBK0260I: Suoritetaan kohteen %s dynaaminen käytöstäpoista"
MSG_FR[$MSG_DYNAMIC_UMOUNT_SCHEDULED]="RBK0260I: Le démontage dynamique de %s est exécuté"
#MSG_NO_SKIP_OR_FORCE_ALLOWED=261
#MSG_EN[$MSG_NO_SKIP_OR_FORCE_ALLOWED]="RBK0261E: Option -0 and -1 are not supported with option -P"
#MSG_DE[$MSG_NO_SKIP_OR_FORCE_ALLOWED]="RBK0261E: Option -0 und -1 sind nicht mit der Option -P unterstützt"
#MSG_FI[$MSG_NO_SKIP_OR_FORCE_ALLOWED]="RBK0261E: Valintaa -0 ja -1 ei tueta valinnan -P kanssa"
#MSG_FR[$MSG_NO_SKIP_OR_FORCE_ALLOWED]="RBK0261E: Les options -0 et -1 ne sont pas prises en charge avec l'option -P"
MSG_DYNAMIC_MOUNT_NOT_REQUIRED=262
MSG_EN[$MSG_DYNAMIC_MOUNT_NOT_REQUIRED]="RBK0262I: Dynamic mount of %s skipped because it's already mounted"
MSG_DE[$MSG_DYNAMIC_MOUNT_NOT_REQUIRED]="RBK0262I: Dynamischer mount von %s nicht ausgeführt da es schon gemounted ist"
MSG_FI[$MSG_DYNAMIC_MOUNT_NOT_REQUIRED]="RBK0262I: Kohteen %s dynaaminen käyttöönotto ohitettiin, koska kohde on jo käytössä"
MSG_FR[$MSG_DYNAMIC_MOUNT_NOT_REQUIRED]="RBK0262I: Le montage dynamique de %s a été ignoré car il est déjà monté"
MSG_NO_FILEATTRIBUTESUPPORT=263
MSG_EN[$MSG_NO_FILEATTRIBUTESUPPORT]="RBK0263E: Filesystem %s on %s does not support Linux fileattributes"
MSG_DE[$MSG_NO_FILEATTRIBUTESUPPORT]="RBK0263E: Dateisystem %s auf %s unterstützt keine Linux Dateiattribute"
MSG_FI[$MSG_NO_FILEATTRIBUTESUPPORT]="RBK0263E: Tiedostojärjestelmä %s kohteessa %s ei tue Linuxin tiedostoattribuutteja"
MSG_FR[$MSG_NO_FILEATTRIBUTESUPPORT]="RBK0263E: Le système de fichiers %s sur %s ne prend pas en charge les attributs de fichiers Linux"
MSG_ROOT_PARTITION_NOT_DIFFERENT=264
MSG_EN[$MSG_ROOT_PARTITION_NOT_DIFFERENT]="RBK0264E: Partition used with option -R cannot be located on same device %s used with option -d"
MSG_DE[$MSG_ROOT_PARTITION_NOT_DIFFERENT]="RBK0264E: Die mit Option -R genutzte Partition darf nicht auf demselben Gerät %s welches mit Option -d angegeben wurde liegen"
MSG_FI[$MSG_ROOT_PARTITION_NOT_DIFFERENT]="RBK0264E: Valinnalla -R määritelty osio ei voi sijaita samalla laitteella %s, joka on määritelty valinnalla -d"
MSG_FR[$MSG_ROOT_PARTITION_NOT_DIFFERENT]="RBK0264E: La partition utilisée avec l'option -R ne doit pas être sur le même périphérique %s que celui spécifié avec l'option-d"
MSG_DD_WARNING=265
MSG_EN[$MSG_DD_WARNING]="RBK0265W: It's not recommended to use the dd backup method. For details read $DD_WARNING_URL_EN"
MSG_DE[$MSG_DD_WARNING]="RBK0265W: dd als Backupmethode wird nicht empfohlen. Details dazu finden sich auf $DD_WARNING_URL_DE"
MSG_FI[$MSG_DD_WARNING]="RBK0265W: DD-varmuuskopiota ei suositella. Lue lisätietoja osoitteesta $DD_WARNING_URL_EN" #Defaults to EN link.
MSG_FR[$MSG_DD_WARNING]="RBK0265W: Il n'est pas recommandé d'utiliser la méthode de sauvegarde dd. Pour plus de détails, lisez $DD_WARNING_URL_EN" #Defaults to EN link.
MSG_NO_FILEATTRIBUTE_RIGHTS=266
MSG_EN[$MSG_NO_FILEATTRIBUTE_RIGHTS]="RBK0266E: Access rights missing to create fileattributes on %s (Filesystem: %s)"
MSG_DE[$MSG_NO_FILEATTRIBUTE_RIGHTS]="RBK0266E: Es fehlt die Berechtigung um Linux Dateiattribute auf %s zu erstellen (Dateisystem: %s)"
#shellcheck disable=SC2034
MSG_FI[$MSG_NO_FILEATTRIBUTE_RIGHTS]="RBK0266E: Käyttöoikeudet tiedostoattribuuttien luomiseen puuttuvat kohteesta %s (Tiedostojärjestelmä: %s)"
#shellcheck disable=SC2034
MSG_FR[$MSG_NO_FILEATTRIBUTE_RIGHTS]="RBK0266E: Droits d'accès manquants pour créer des attributs de fichier sur %s (système de fichiers : %s)"
#
# Non NLS messages
#
MSG_EXTENSION_CALLED=267
MSG_EN[$MSG_EXTENSION_CALLED]="RBK0267I: Extension %s called"
MSG_DE[$MSG_EXTENSION_CALLED]="RBK0267I: Erweiterung %s wird aufgerufen"
MSG_UNSUPPORTED_ENVIRONMENT=268
MSG_EN[$MSG_UNSUPPORTED_ENVIRONMENT]="RBK0268E: Only Raspberries running Raspberry PI OS are supported. Use option --unsupportedEnvironment to invoke $MYNAME WITHOUT ANY SUPPORT"
MSG_DE[$MSG_UNSUPPORTED_ENVIRONMENT]="RBK0268E: Es werden nur Raspberries mit Raspberry PI OS unterstützt. Mit der Option --unsupportedEnvironment kann man $MYNAME OHNE JEGLICHE UNTERSTÜTZUNG aufrufen"
MSG_UNSUPPORTED_ENVIRONMENT_CONFIRMED=269
MSG_EN[$MSG_UNSUPPORTED_ENVIRONMENT_CONFIRMED]="\
RBK0269W: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@${NL}\
!!! RBK0268W: @@@ Unsupported environment @@@${NL}\
!!! RBK0269W: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@${NL}"
MSG_DE[$MSG_UNSUPPORTED_ENVIRONMENT_CONFIRMED]="\
RBK0269W: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@${NL}\
!!! RBK0268W: @@@ Nicht unterstützte Umgebung @@@${NL}\
!!! RBK0269W: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@${NL}"
MSG_REBOOT_SYSTEM=270
MSG_EN[$MSG_REBOOT_SYSTEM]="RBK0270I: System will be rebooted at the end of the backup run"
MSG_DE[$MSG_REBOOT_SYSTEM]="RBK0270I: Das System wird am Ende des Backuplaufes neu gestartet"
MSG_SMART_RECYCLE_WILL_BE_APPLIED=271
MSG_EN[$MSG_SMART_RECYCLE_WILL_BE_APPLIED]="RBK0271I: Smart recycle strategy will be applied"
MSG_DE[$MSG_SMART_RECYCLE_WILL_BE_APPLIED]="RBK0271I: Wende smarte Backupstrategie an"
MSG_REBOOT_SYSTEM=272
MSG_EN[$MSG_REBOOT_SYSTEM]="RBK0272I: System will be rebooted at the end of the backup run"
MSG_DE[$MSG_REBOOT_SYSTEM]="RBK0272I: Das System wird am Ende des Backuplaufes neu gestartet"
MSG_INVALID_BACKUPNAMES_DETECTED=273
MSG_EN[$MSG_INVALID_BACKUPNAMES_DETECTED]="RBK0273E: %s invalid backup directorie(s) or files found in %s"
MSG_DE[$MSG_INVALID_BACKUPNAMES_DETECTED]="RBK0273E: %s ungültige Backupverzeichnis(se) oder Dateien in %s gefunden"
MSG_RESTORE_PARTITION_MOUNTED=274
MSG_EN[$MSG_RESTORE_PARTITION_MOUNTED]="RBK0274E: Restore device %s has mounted partitions. Note: Restore to the active system is not possible"
MSG_DE[$MSG_RESTORE_PARTITION_MOUNTED]="RBK0274E: Das Restoregerät %s hat gemountete Partitionen. Hinweis: Ein Restore auf das aktive System ist nicht möglich"
MSG_RESTORE_DEVICE_NOT_VALID=275
MSG_EN[$MSG_RESTORE_DEVICE_NOT_VALID]="RBK0275E: Restore device %s is no valid device"
MSG_DE[$MSG_RESTORE_DEVICE_NOT_VALID]="RBK0275E: Das Restoregerät %s ist kein gültiges Gerät"
MSG_INVALID_BOOT_DEVICE=276
MSG_EN[$MSG_INVALID_BOOT_DEVICE]="RBK0276E: Boot device %s is not supported"
MSG_DE[$MSG_INVALID_BOOT_DEVICE]="RBK0276E: Das Bootgerät %s ist nicht unterstützt"
#MSG_USBMOUNT_INSTALLED=277
#MSG_EN[$MSG_USBMOUNT_INSTALLED]="RBK0277E: Restore not possible when %s is active"
#MSG_DE[$MSG_USBMOUNT_INSTALLED]="RBK0277E: Restore ist nicht möglich wenn %s active ist"
MSG_BACKUP_CLEANUP_FAILED=278
MSG_EN[$MSG_BACKUP_CLEANUP_FAILED]="RBK0278E: Cleanup of backupdirectories failed. Manual deletion of the last backup directory is strongly recommended !"
MSG_DE[$MSG_BACKUP_CLEANUP_FAILED]="RBK0278E: Fehler bei den Aufräumarbeiten am Backupverzeichnis. Das letzte Backupverzeichnis sollte dringend manuell gelöscht werden !"
MSG_FINAL_COMMAND_FAILED=279
MSG_EN[$MSG_FINAL_COMMAND_FAILED]="RBK0279W: Error occured executing final command. RC %s"
MSG_DE[$MSG_FINAL_COMMAND_FAILED]="RBK0279W: Ein Fehler trat beim Ausführen der finalen Befehle auf. RC %s"
MSG_FINAL_COMMAND_EXECUTED=280
MSG_EN[$MSG_FINAL_COMMAND_EXECUTED]="RBK0280I: Executing final command: '%s'"
MSG_DE[$MSG_FINAL_COMMAND_EXECUTED]="RBK0280I: Finaler Befehl wird ausgeführt: '%s'"
MSG_UNSUPPORTED_VERSION=281
MSG_EN[$MSG_UNSUPPORTED_VERSION]="RBK0281W: Unsupported version of $MYSELF"
MSG_DE[$MSG_UNSUPPORTED_VERSION]="RBK0281W: Nicht unterstützte Version von $MYSELF"
MSG_PUSHOVER_SEND_FAILED=282
MSG_EN[$MSG_PUSHOVER_SEND_FAILED]="RBK0282W: Sent to pushover failed. curl RC: %s - HTTP CODE: %s - Error description: %s"
MSG_DE[$MSG_PUSHOVER_SEND_FAILED]="RBK0282W: Senden an Pushover fehlerhaft. curl RC: %s - HTTP CODE: %s - Fehlerbeschreibung: %s"
MSG_PUSHOVER_SEND_OK=283
MSG_EN[$MSG_PUSHOVER_SEND_OK]="RBK0283I: Pushover notified"
MSG_DE[$MSG_PUSHOVER_SEND_OK]="RBK0283I: Pushover benachrichtigt"
MSG_PUSHOVER_OPTIONS_INCOMPLETE=284
MSG_EN[$MSG_PUSHOVER_OPTIONS_INCOMPLETE]="RBK0284E: Pushover options not complete"
MSG_DE[$MSG_PUSHOVER_OPTIONS_INCOMPLETE]="RBK0284E: Pushoveroptionen nicht vollständig"
MSG_PUSHOVER_INVALID_NOTIFICATION=285
MSG_EN[$MSG_PUSHOVER_INVALID_NOTIFICATION]="RBK0285E: Invalid Pushover notification %s detected. Valid notifications are %s"
MSG_DE[$MSG_PUSHOVER_INVALID_NOTIFICATION]="RBK0285E: Ungültige Pushover Notification %s eingegeben. Mögliche Notifikationen sind %s"
MSG_SLACK_SEND_FAILED=286
MSG_EN[$MSG_SLACK_SEND_FAILED]="RBK0286W: Sent to Slack failed. curl RC: %s - HTTP CODE: %s - Error description: %s"
MSG_DE[$MSG_SLACK_SEND_FAILED]="RBK0286W: Senden an Slack fehlerhaft. curl RC: %s - HTTP CODE: %s - Fehlerbeschreibung: %s"
MSG_SLACK_SEND_OK=287
MSG_EN[$MSG_SLACK_SEND_OK]="RBK0287I: Slack notified"
MSG_DE[$MSG_SLACK_SEND_OK]="RBK0287I: Slack benachrichtigt"
#MSG_SLACK_OPTIONS_INCOMPLETE=288
#MSG_EN[$MSG_SLACK_OPTIONS_INCOMPLETE]="RBK0288E: Slack options not complete"
#MSG_DE[$MSG_SLACK_OPTIONS_INCOMPLETE]="RBK0288E: Slackoptionen nicht vollständig"
MSG_SLACK_INVALID_NOTIFICATION=289
MSG_EN[$MSG_SLACK_INVALID_NOTIFICATION]="RBK0289E: Invalid Slack notification %s detected. Valid notifications are %s"
MSG_DE[$MSG_SLACK_INVALID_NOTIFICATION]="RBK0289E: Ungültige Slack Notification %s eingegeben. Mögliche Notifikationen sind %s"
MSG_UNPROTECTED_PROPERTIESFILE=290
MSG_EN[$MSG_UNPROTECTED_PROPERTIESFILE]="RBK0290W: Configuration file %s is unprotected"
MSG_DE[$MSG_UNPROTECTED_PROPERTIESFILE]="RBK0290W: Konfigurationsdatei %s ist nicht geschützt"
MSG_IMG_BOOT_FSCHECK_FAILED=291
MSG_EN[$MSG_IMG_BOOT_FSCHECK_FAILED]="RBK0291E: Bootpartition check failed with RC %s"
MSG_DE[$MSG_IMG_BOOT_FSCHECK_FAILED]="RBK0291E: Bootpartitioncheck endet fehlerhaft mit RC %s"
MSG_IMG_BOOT_CHECK_STARTED=292
MSG_EN[$MSG_IMG_BOOT_CHECK_STARTED]="RBK0292I: Bootpartition check started"
MSG_DE[$MSG_IMG_BOOT_CHECK_STARTED]="RBK0292I: Bootpartitionscheck gestartet"
MSG_NO_PARTUUID_SYNCHRONIZED=293
MSG_EN[$MSG_NO_PARTUUID_SYNCHRONIZED]="RBK0293W: No PARTUUID updated in %s for %s. Backup may not boot correctly"
MSG_DE[$MSG_NO_PARTUUID_SYNCHRONIZED]="RBK0293W: Es konnte keine PARTUUID in %s für %s erneuert werden. Das Backup könnte nicht starten"
MSG_CURRENT_CONFIGURATION_UPDATE_REQUIRED=294
MSG_EN[$MSG_CURRENT_CONFIGURATION_UPDATE_REQUIRED]="RBK0294I: Current configuration version %s has to be be updated to %s"
MSG_DE[$MSG_CURRENT_CONFIGURATION_UPDATE_REQUIRED]="RBK0294I: Aktuelle Konfigurationsversion %s muss auf Version %s upgraded werden"
MSG_SYNC_CMDLINE_FSTAB=295
MSG_EN[$MSG_SYNC_CMDLINE_FSTAB]="RBK0295I: Synchronizing %s and %s"
MSG_DE[$MSG_SYNC_CMDLINE_FSTAB]="RBK0295I: %s und %s werden synchronisiert"
OVERLAY_FILESYSTEM_NOT_SUPPORTED=296
MSG_EN[$OVERLAY_FILESYSTEM_NOT_SUPPORTED]="RBK0296E: Overlay filesystem is not supported"
MSG_DE[$OVERLAY_FILESYSTEM_NOT_SUPPORTED]="RBK0296E: Overlayfilesystem wird nicht unterstützt"
MSG_PARTITIONS_EXTEND_DISK_SIZE=297
MSG_EN[$MSG_PARTITIONS_EXTEND_DISK_SIZE]="RBK0297E: Partitioning exceeds disk size"
MSG_DE[$MSG_PARTITIONS_EXTEND_DISK_SIZE]="RBK0297E: Partitionierung größer als Diskgröße"
#MSG_UNSUPPORTED_PARTITIONING=298
#MSG_EN[$MSG_UNSUPPORTED_PARTITIONING]="RBK0298E: Filesystem %1 on boot and/or %2 on root not supported"
#MSG_DE[$MSG_UNSUPPORTED_PARTITIONING]="RBK0298E: Filesystem %1 auf boot und/oder %2 auf root ist nicht unterstützt"
MSG_BACKUPDIR_CREATED=299
MSG_EN[$MSG_BACKUPDIR_CREATED]="RBK0299I: Backup directory %s created"
MSG_DE[$MSG_BACKUPDIR_CREATED]="RBK0299I: Backupverzeichnis %s erstellt"
MSG_ADJUSTING_LAST=300
MSG_EN[$MSG_ADJUSTING_LAST]="RBK0300I: Adjusting last partition from %s to %s"
MSG_DE[$MSG_ADJUSTING_LAST]="RBK0300I: Letzte Partition wird von %s auf %s angepasst"
MSG_TEMPMOVE_FAILED=301
MSG_EN[$MSG_TEMPMOVE_FAILED]="RBK0301E: Move of temporary backup directory failed with RC %s"
MSG_DE[$MSG_TEMPMOVE_FAILED]="RBK0301E: Move des temporären Backupverzeichnisses fehlerhaft beendet mit RC %s"
MSG_RESIZED_PARTITION_TOO_SMALL=302
MSG_EN[$MSG_RESIZED_PARTITION_TOO_SMALL]="RBK0302E: Partition %s on %s with %s is too small. Missing at least %s"
MSG_DE[$MSG_RESIZED_PARTITION_TOO_SMALL]="RBK0302E: Zu resizende Partition %s auf %s mit %s is zu klein. Es fehlen mindestens %s"
MSG_SKIP_PARTITION_RESTORE=303
MSG_EN[$MSG_SKIP_PARTITION_RESTORE]="RBK0303W: Partition %s was not restored"
MSG_DE[$MSG_SKIP_PARTITION_RESTORE]="RBK0303W: Partition %s wurde nicht zurückgespielt"
MSG_PARTITION_RESTORE_NO_BOOT_POSSIBLE=304
MSG_EN[$MSG_PARTITION_RESTORE_NO_BOOT_POSSIBLE]="RBK0304W: Not all OS partitions were restored. System may not boot"
MSG_DE[$MSG_PARTITION_RESTORE_NO_BOOT_POSSIBLE]="RBK0304W: Nicht alle OS Partitionen wurden zurückgespielt. Das System könnte nicht booten"
MSG_RESTORING_PARTITIONS=305
MSG_EN[$MSG_RESTORING_PARTITIONS]="RBK0305W: Restoring partitions %s to %s"
MSG_DE[$MSG_RESTORING_PARTITIONS]="RBK0305W: Partitionen %s werden auf %s zurüchgespielt"
#MSG_ANSWER_ALL=306
#MSG_EN[$MSG_ANSWER_ALL]="all"
#MSG_DE[$MSG_ANSWER_ALL]="Alle"
MSG_UPDATING_UUIDS=319
MSG_EN[$MSG_UPDATING_UUIDS]="RBK0319I: Generating new UUIDs"
MSG_DE[$MSG_UPDATING_UUIDS]="RBK0319I: Neue UUIDs werden generiert"
MSG_REMOVING_BACKUP_NO_FILE=320
MSG_EN[$MSG_REMOVING_BACKUP_NO_FILE]="RBK0320I: Removing incomplete backup. This may take some time. Please be patient"
MSG_DE[$MSG_REMOVING_BACKUP_NO_FILE]="RBK0320I: Unvollständiges Backup wird gelöscht. Das kann etwas dauern. Bitte Geduld"
#MSG_SAVING_LOG=321
#MSG_EN[$MSG_SAVING_LOG]="RBK0321I: Saving logfile"
#MSG_DE[$MSG_SAVING_LOG]="RBK0321I: Logdatei wird gesichert"
MSG_ADJUSTING_WARNING_P=322
MSG_EN[$MSG_ADJUSTING_WARNING_P]="RBK0322W: Target %s with %s is smaller than backup source with %s. Last partition will be truncated accordingly. NOTE: Restore may fail if the root partition will become too small"
MSG_DE[$MSG_ADJUSTING_WARNING_P]="RBK0322W: Ziel %s mit %s ist kleiner als die Backupquelle mit %s. Die letzte Partition wird entsprechend verkleinert. HINWEIS: Der Restore kann fehlschlagen wenn sie zu klein wird"
MSG_ADJUSTING_WARNING_P2=323
MSG_EN[$MSG_ADJUSTING_WARNING_P2]="RBK0323I: Target %s with %s is larger than backup source with %s. Last partition will be expanded accordingly to use the whole available space"
MSG_DE[$MSG_ADJUSTING_WARNING_P2]="RBK0323I: Ziel %s mit %s ist größer als die Backupquelle mit %s. Die letzte Partition wird entsprechend vergrößert um den ganzen verfügbaren Speicherplatz zu benutzen"
MSG_NOT_ALL_OS_PARTITIONS_SAVED=324
MSG_EN[$MSG_NOT_ALL_OS_PARTITIONS_SAVED]="RBK0324W: Not all OS partitions saved. Backup will not boot"
MSG_DE[$MSG_NOT_ALL_OS_PARTITIONS_SAVED]="RBK0324W: Es werden nicht alle OS Partition gesichert und das Backup wird nicht starten"
#MSG_WARN_RESTORE_PARTITION_DEVICE_UPDATED=325
#MSG_EN[$MSG_WARN_RESTORE_PARTITION_DEVICE_UPDATED]="RBK0325W: Device %s will be updated"
#MSG_DE[$MSG_WARN_RESTORE_PARTITION_DEVICE_UPDATED]="RBK0325W: Gerät %s wird aktualisiert"
MSG_SKIP_FORMATING=326
MSG_EN[$MSG_SKIP_FORMATING]="RBK0326W: Partitions will not be formatted"
MSG_DE[$MSG_SKIP_FORMATING]="RBK0326W: Partitionen werden nicht formatiert"
MSG_NOT_ALL_PREVIOUS_PARTITIONS_SAVED=327
MSG_EN[$MSG_NOT_ALL_PREVIOUS_PARTITIONS_SAVED]="RBK0327E: Not all partitions which were saved in the previous backup are included in option -T. Missing \"%s\""
MSG_DE[$MSG_NOT_ALL_PREVIOUS_PARTITIONS_SAVED]="RBK0327E: Nicht alle Partitionen die im vorhergehenden Backup gesichert wurden werden mit der Option -T gesichert. Es fehlen \"%s\""
MSG_NO_BOOTDEVICE_MOUNTED=328
MSG_EN[$MSG_NO_BOOTDEVICE_MOUNTED]="RBK0328E: Boot device %s not mounted"
MSG_DE[$MSG_NO_BOOTDEVICE_MOUNTED]="RBK0328E: Bootgerät %s ist nicht gemounted"
MSG_PARTITIONS_FORMATED=329
MSG_EN[$MSG_PARTITIONS_FORMATED]="RBK0329W: Partitions %1 will be formatted"
MSG_DE[$MSG_PARTITIONS_FORMATED]="RBK0329W: Partitionen %1 werden formatiert"
MSG_MISSING_PARTITIONS_NOT_SAVED=330
MSG_EN[$MSG_MISSING_PARTITIONS_NOT_SAVED]="RBK0330W: Not all partitions which were saved in the previous backup are included. Missing \"%s\""
MSG_DE[$MSG_MISSING_PARTITIONS_NOT_SAVED]="RBK0330W: Nicht alle Partitionen die im vorhergehenden Backup gesichert wurden werden gesichert. Es fehlen \"%s\""
MSG_NO_SKIP_FORMAT_POSSIBLE=331
MSG_EN[$MSG_NO_SKIP_FORMAT_POSSIBLE]="RBK0331E: Option -00 is only available with rsync backuptype and a partition oriented backup"
MSG_DE[$MSG_NO_SKIP_FORMAT_POSSIBLE]="RBK0331E: Option -00 ist nur mit dem rsync Backuptyp und einem partitionsorientierten Backup möglich"
MSG_GENERIC_WARNING=332
MSG_EN[$MSG_GENERIC_WARNING]="RBK0332W: %s"
MSG_DE[$MSG_GENERIC_WARNING]="RBK0332W: %s"
MSG_EXISTING_BACKUP=333
MSG_EN[$MSG_EXISTING_BACKUP]="RBK0333I: Existing backup: %s"
MSG_DE[$MSG_EXISTING_BACKUP]="RBK0333I: Existierendes Backup: %s"
MSG_NORMAL_RECYCLE_FILE_WOULD_BE_DELETED=334
MSG_EN[$MSG_NORMAL_RECYCLE_FILE_WOULD_BE_DELETED]="RBK0334W: Backup strategy would delete %s"
MSG_DE[$MSG_NORMAL_RECYCLE_FILE_WOULD_BE_DELETED]="RBK0334W: Backup Strategie würde %s Backup löschen"
MSG_BACKUP_NAMING_CHANGE=335
MSG_EN[$MSG_BACKUP_NAMING_CHANGE]="RBK0335W: With raspiBackup version %s the naming of the backup directories changed!"
MSG_DE[$MSG_BACKUP_NAMING_CHANGE]="RBK0335W: Ab raspiBackup Version %s hat sich die Bezeichnung der Backup-Verzeichnisse geändert!"
MSG_OLD_NAME_BACKUPS_FOUND=336
MSG_EN[$MSG_OLD_NAME_BACKUPS_FOUND]="RBK0336W: Old-named backups found without OS info in its directory name:"
MSG_DE[$MSG_OLD_NAME_BACKUPS_FOUND]="RBK0336W: Backups mit alter Bezeichnung ohne OS-Info im Verzeichnisnamen gefunden:"
MSG_OLD_NAME_BACKUPS_HANDLING_INFO=337
MSG_EN[$MSG_OLD_NAME_BACKUPS_HANDLING_INFO]="RBK0337W: They are not included in the backup recycle process and have to be deleted manually"
MSG_DE[$MSG_OLD_NAME_BACKUPS_HANDLING_INFO]="RBK0337W: Diese werden nicht im Backuprecycleprozess berücksichtigt und müssen manuell gelöscht werden"
MSG_OLD_NAME_BACKUPS_COUNTER_INFO=338
MSG_EN[$MSG_OLD_NAME_BACKUPS_COUNTER_INFO]="RBK0338W: Note: This message will be shown again %s times"
MSG_DE[$MSG_OLD_NAME_BACKUPS_COUNTER_INFO]="RBK0338W: Hinweis: Diese Meldung wird weitere %s Mal angezeigt werden"
MSG_OPTION_T_NOT_ALLOWED=339
MSG_EN[$MSG_OPTION_T_NOT_ALLOWED]="RBK0339E: Option -T not allowed for normal mode backup"
MSG_DE[$MSG_OPTION_T_NOT_ALLOWED]="RBK0339E: Option -T ist für einen normales Backup nicht erlaubt"
MSG_RESTORE_TIME=340
MSG_EN[$MSG_RESTORE_TIME]="RBK0340I: Restore time: %s:%s:%s"
MSG_DE[$MSG_RESTORE_TIME]="RBK0340I: Restorezeit: %s:%s:%s"
MSG_TAR_BOOT_BACKUP_NOT_POSSIBLE=341
MSG_DE[$MSG_TAR_BOOT_BACKUP_NOT_POSSIBLE]="RBK0341E: Option -B ist für einen partitionsorientierten Backup nicht erlaubt"
MSG_EN[$MSG_TAR_BOOT_BACKUP_NOT_POSSIBLE]="RBK0341E: Option -B not allowed for partition orientierted backup"
MSG_PARTITION_CHECK_EXECUTED=342
MSG_EN[$MSG_PARTITION_CHECK_EXECUTED]="RBK0342I: Executing filesystem check on %s"
MSG_DE[$MSG_PARTITION_CHECK_EXECUTED]="RBK0342I: Dateisystemcheck wird auf %s durchgeführt"
MSG_PARTITION_CHECK_FAILED=343
MSG_EN[$MSG_PARTITION_CHECK_FAILED]="RBK0343E: Filesystem check failed on %s with RC %s"
MSG_DE[$MSG_PARTITION_CHECK_FAILED]="RBK0343E: Dateisystemcheck auf %s fehlerhaft beendet mit RC %s"
MSG_NO_BOOTPARTITION_MOUNTED=344
MSG_EN[$MSG_NO_BOOTPARTITION_MOUNTED]="RBK0344E: No mounted boot partition detected"
MSG_DE[$MSG_NO_BOOTPARTITION_MOUNTED]="RBK0344E: Keine gemountete Bootpartition gefunden"
MSG_BACKUPDIR_MOVED=345
MSG_EN[$MSG_BACKUPDIR_MOVED]="RBK0345I: Temporary backup directory %s moved to %s"
MSG_DE[$MSG_BACKUPDIR_MOVED]="RBK0345I: Temporäres Backupverzeichnis %s wird in %s verschoben"
MSG_SYNCING_PARTITIONFILE=346
MSG_EN[$MSG_SYNCING_PARTITIONFILE]="RBK0346I: Syncing partition %s"
MSG_DE[$MSG_SYNCING_PARTITIONFILE]="RBK0346I: Synchronisiere Partition %s"
MSG_PARTITIONS_BACKUP_STARTED=347
MSG_EN[$MSG_PARTITIONS_BACKUP_STARTED]="RBK0347I: Partition oriented backup of type %s started for partitions %s"
MSG_DE[$MSG_PARTITIONS_BACKUP_STARTED]="RBK0347I: Partitionsorientierte Backup vom Typ %s started für die Partitionen %s"
MSG_UMOUNT_MOUNTED_PARTITIONS=348
MSG_EN[$MSG_UMOUNT_MOUNTED_PARTITIONS]="RBK0348W: Umounting all mounted partitions of %s"
MSG_DE[$MSG_UMOUNT_MOUNTED_PARTITIONS]="RBK0348W: Sollen alle gemounteten Partitionen von %s umounted werden"
MSG_UMOUNT_MOUNTED_PARTITIONS_FAILED=349
MSG_EN[$MSG_UMOUNT_MOUNTED_PARTITIONS_FAILED]="RBK0349E: Umounting mounted partitions of %s failed"
MSG_DE[$MSG_UMOUNT_MOUNTED_PARTITIONS_FAILED]="RBK0349E: Umount von gemounteten Paritionen von %s nicht möglich"
MSG_UNSUPPORTED_TAR_COMPRESS_TOOL=350
MSG_EN[$MSG_UNSUPPORTED_TAR_COMPRESS_TOOL]="RBK0350E: Unsupported tar compression tool %si. Supported tools are bzip2,gzip,lzip,lzma,lzop,xz and zstd"
MSG_DE[$MSG_UNSUPPORTED_TAR_COMPRESS_TOOL]="RBK0350E: Nicht unterstütztes tar Kompressionstool %s. Unterstützte Tools sind bzip2,gzip,lzip,lzma,lzop,xz und zstd"
MSG_TAR_COMPRESS_TOOL_USED=351
MSG_EN[$MSG_TAR_COMPRESS_TOOL_USED]="RBK0351I: Using custom tar compression tool %s"
MSG_DE[$MSG_TAR_COMPRESS_TOOL_USED]="RBK0351I: Konfigurierbares tar Kompressionstool %s wird genutzt"
MSG_TAR_COMPRESS_TOOL_NOT_FOUND=352
MSG_EN[$MSG_TAR_COMPRESS_TOOL_NOT_FOUND]="RBK0352E: Custom tar compression tool %s not installed"
MSG_DE[$MSG_TAR_COMPRESS_TOOL_NOT_FOUND]="RBK0352E: Konfigurierbares tar Kompressionstool %s nicht installiert"
MSG_OPTION_TAR_COMPRESS_TOOL_NOT_SUPPORTED=353
MSG_EN[$MSG_OPTION_TAR_COMPRESS_TOOL_NOT_SUPPORTED]="RBK0353E: Custom tar compression not possible for backuptype %s"
MSG_DE[$MSG_OPTION_TAR_COMPRESS_TOOL_NOT_SUPPORTED]="RBK0353E: Konfigurierbare tar Kompression nicht für Backuptyp %s möglich"
MSG_EXTERNAL_ROOTPARTITION_UNSUPPORTED=354
MSG_EN[$MSG_EXTERNAL_ROOTPARTITION_UNSUPPORTED]="RBK0354E: External root partition not supported with option \"-P\""
MSG_DE[$MSG_EXTERNAL_ROOTPARTITION_UNSUPPORTED]="RBK0354E: Externe Rootpartition ist mit Option -P nicht unterstützt"
MSG_OPTION_ACLS_DISABLED=355
MSG_EN[$MSG_OPTION_ACLS_DISABLED]="RBK0355I: ACLs are not copied"
#shellcheck disable=SC2034
MSG_DE[$MSG_OPTION_ACLS_DISABLED]="RBK0355I: ACLs werden nicht kopiert"
#MSG_BACKUP_DIRECTORY_HAS_DEFAULT_ACLS=356
#MSG_EN[$MSG_BACKUP_DIRECTORY_HAS_DEFAULT_ACLS]="RBK0356E: Default ACLs not allowed on backuppartition %s"
#MSG_DE[$MSG_BACKUP_DIRECTORY_HAS_DEFAULT_ACLS]="RBK0356E: Default ACLs sind an der Backuppartition %s nicht erlaubt"
declare -A MSG_HEADER=( ['I']="---" ['W']="!!!" ['E']="???" )
# setup trap function
# trap function then will be called with trap as argument
#
# borrowed from # from http://stackoverflow.com/a/2183063/804678
function trapWithArg() { # function trap1 trap2 ... trapn
logEntry "$*"
local func="$1" ; shift
for sig ; do
# Use single quotes, otherwise this expands now rather than when signalled.
# shellcheck disable=SC2064
trap "$func $sig" "$sig"
done
logExit
}
LOG_INDENT_INC=4
# Create message and substitute parameters
function getMessageText() { # messagenumber parm1 parm2 ...
local msg p i s
msgVar="MSG_${LANGUAGE}"
if [[ -n ${SUPPORTED_LANGUAGES[$LANGUAGE]} ]]; then
# Use braces when expanding arrays, e.g. ${array[idx]} (or ${var}[.. to quiet).
#shellcheck disable=SC1087
msgVar="$msgVar[$1]"
msg=${!msgVar}
if [[ -z $msg ]]; then # no translation found
msg="${MSG_EN[$1]}" # fallback into english
fi
else
msg="${MSG_EN[$1]}" # fallback into english
fi
if [[ -z $msg ]]; then
msg="${MSG_EN[$MSG_UNDEFINED]} $1"
fi
shift
# Change messages with old message format using %s, %s ... to new format using %1, %2 ...
i=1
while [[ "$msg" =~ %s ]]; do
# See if you can use ${variable//search/replace} instead
#shellcheck disable=SC2001
msg="$(sed "s|%s|%$i|" <<<"$msg" 2>/dev/null)" # have to use explicit command name
(( i++ ))
done
for ((i = 1; $i <= $#; i++)); do # substitute all message parameters
p=${!i}
# See if you can use ${variable//search/replace} instead
#shellcheck disable=SC2001
p="$(sed 's/\&/\\\&/g' <<< "$p")" # escape &
(( s=$i ))
s="%$s"
# See if you can use ${variable//search/replace} instead
#shellcheck disable=SC2001
msg="$(sed "s|$s|$p|" <<<"$msg" 2>/dev/null)" # have to use explicit command name
done
# See if you can use ${variable//search/replace} instead
#shellcheck disable=SC2001
msg="$(sed "s/%[0-9]+//g" <<<"$msg" 2>/dev/null)" # delete trailing %n definitions
local msgPref=${msg:0:3}
if [[ $msgPref == "RBK" ]]; then # RBK0001E
local severity=${msg:7:1}
if [[ "$severity" =~ [EWI] ]]; then
local msgHeader=${MSG_HEADER[$severity]}
echo "$msgHeader $msg"
else
echo "$msg"
fi
else
echo "$msg"
fi
}
# --- Helper function to extract the message text in German or English and insert message parameters
function getMessage() { # messageNumber parm1 parm2
local msg
msg="$(getMessageText "$@")"
echo "$msg"
}
function logItem() { # message
logIntoOutput "$LOG_TYPE_DEBUG" "---" "" "$@"
}
function logEntry() { # message
logIntoOutput "$LOG_TYPE_DEBUG" "-->" "" "${FUNCNAME[1]} $*"
(( LOG_INDENT+=LOG_INDENT_INC ))
}
function logExit() { # message
(( LOG_INDENT-=LOG_INDENT_INC ))
logIntoOutput "$LOG_TYPE_DEBUG" "<--" "" "${FUNCNAME[1]} $*"
}
function logSystem() {
logEntry
logCommand "uname -a"
[[ -f /etc/os-release ]] && logCommand "cat /etc/os-release"
[[ -f /etc/debian_version ]] && logCommand "cat /etc/debian_version"
[[ -f /etc/fstab ]] && logCommand "cat /etc/fstab"
[[ -f /proc/cpuinfo ]] && logCommand "grep ^Model /proc/cpuinfo"
logCommand "locale"
logExit
}
function logCommand() { # command
(( LOG_INDENT+=LOG_INDENT_INC ))
local callerLineNo=${BASH_LINENO[0]}
logIntoOutput $LOG_TYPE_DEBUG "***" "$callerLineNo" "$1"
local r
r="$($1 2>&1)"
logIntoOutput $LOG_TYPE_DEBUG " " "$callerLineNo" "$r"
(( LOG_INDENT-=LOG_INDENT_INC ))
}
function logSystemServices() {
logEntry
if (( $SYSTEMSTATUS )); then
logCommand 'service --status-all'
fi
logExit
}
function logIntoOutput() { # logtype prefix lineno message
[[ "$LOG_DEBUG" != "$LOG_LEVEL" ]] && return
local dte type prefix lineno indent line
type="${LOG_TYPEs[$1]}"
shift
prefix="$1"
shift
lineno="$1"
shift
[[ -z $lineno ]] && lineno=${BASH_LINENO[1]}
dte=$(date +%Y%m%d-%H%M%S)
# This format string has 2 variables, but is passed 1 argument.
#shellcheck disable=SC2183
indent=$(printf '%*s' "$LOG_INDENT")
local m
while IFS= read -r line; do
printf -v m "%s %04d: %s %s %s" "$type" "$lineno" "$indent" "$prefix" "$line"
case $LOG_OUTPUT in
"$LOG_OUTPUT_VARLOG" | "$LOG_OUTPUT_BACKUPLOC" | "$LOG_OUTPUT_HOME" )
echo "$dte $m" >> "$LOG_FILE"
;;
*)
echo "$dte $m" >> "$LOG_FILE"
;;
esac
done <<< "$@"
}
# log everything written to stdout or stderr into log file
function logEnable() {
LOG_FILE="$TEMP_LOG_FILE"
MSG_FILE="$TEMP_MSG_FILE"
rm -f "$LOG_FILE" &>/dev/null
rm -f "$MSG_FILE" &>/dev/null
logItem "Logfiles used: $LOG_FILE and $MSG_FILE"
touch "$LOG_FILE"
touch "$MSG_FILE"
# save file descriptors, see https://unix.stackexchange.com/questions/80988/how-to-stop-redirection-in-bash
exec 3>&1 4>&2
# see https://stackoverflow.com/questions/3173131/redirect-copy-of-stdout-to-log-file-from-within-bash-script-itself
exec 1> >(stdbuf -i0 -o0 -e0 tee -ia "$LOG_FILE")
exec 2> >(stdbuf -i0 -o0 -e0 tee -ia "$LOG_FILE" >&2)
logItem "$GIT_CODEVERSION"
local sep=
sep="$(getMessage $MSG_SENSITIVE_SEPARATOR)"
local warn
warn="$(getMessage $MSG_SENSITIVE_WARNING)"
logItem "$sep"
logItem "$warn"
logItem "$sep"
}
# move temporary log file to it's destination
function logFinish() {
logEntry
local DEST_LOGFILE DEST_MSGFILE
rm -f "$FINISH_LOG_FILE"
if [[ $LOG_LEVEL != "$LOG_NONE" ]]; then
# 1) error occured and logoutput is backup location which was deleted or fake mode
# 2) fake
# 3) backup location was already deleted by SR
if [[ "$LOG_OUTPUT" =~ $LOG_OUTPUT_IS_NO_USERDEFINEDFILE_REGEX ]]; then # no -L used
logItem "$rc $LOG_OUTPUT $FAKE"
if [[ (( $rc != 0 )) && (( $LOG_OUTPUT == "$LOG_OUTPUT_BACKUPLOC" )) ]] \
|| (( $FAKE )) \
|| [[ ! -e $BACKUPTARGET_DIR ]]; then
LOG_OUTPUT=$LOG_OUTPUT_HOME # save log in home directory
logItem "LOG_OUTPUT=$LOG_OUTPUT"
fi
fi
logItem "LOG_OUTPUT: $LOG_OUTPUT"
case $LOG_OUTPUT in
"$LOG_OUTPUT_VARLOG" )
LOG_BASE="/var/log/$MYNAME"
if [[ ! -d "${LOG_BASE}" ]]; then
if ! mkdir -p "${LOG_BASE}" &>> "$FINISH_LOG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_CREATE_DIRECTORY "${LOG_BASE}"
exitError $RC_CREATE_ERROR
fi
fi
DEST_LOGFILE="$LOG_BASE/$HOSTNAME$LOGFILE_EXT"
DEST_MSGFILE="$LOG_BASE/$HOSTNAME$MSGFILE_EXT"
;;
"$LOG_OUTPUT_HOME")
DEST_LOGFILE="$CALLING_HOME/${MYNAME}$LOGFILE_EXT"
DEST_MSGFILE="$CALLING_HOME/${MYNAME}$MSGFILE_EXT"
;;
"$LOG_OUTPUT_BACKUPLOC")
DEST_LOGFILE="$BACKUPTARGET_DIR/${MYNAME}$LOGFILE_EXT"
DEST_MSGFILE="$BACKUPTARGET_DIR/${MYNAME}$MSGFILE_EXT"
;;
*) # option -L
DEST_LOGFILE="$LOG_OUTPUT$LOGFILE_EXT"
DEST_MSGFILE="$LOG_OUTPUT$MSGFILE_EXT"
esac
logItem "DEST_LOGFILE: $DEST_LOGFILE"
logItem "DEST_MSGFILE: $DEST_MSGFILE"
if [[ "$LOG_FILE" != "$DEST_LOGFILE" ]]; then
if [[ "$LOG_OUTPUT" == "$LOG_OUTPUT_VARLOG" ]]; then
logItem "Appending Logfile: $LOG_FILE to $DEST_LOGFILE"
cat "$LOG_FILE" 1>> "$DEST_LOGFILE" 2>>"$FINISH_LOG_FILE"
rm "$LOG_FILE" &>>"$FINISH_LOG_FILE"
else
logItem "Moving Logfile: $LOG_FILE to $DEST_LOGFILE"
mv "$LOG_FILE" "$DEST_LOGFILE" &>>"$FINISH_LOG_FILE"
fi
LOG_FILE="$DEST_LOGFILE" # now final log location was established. log anything else in final log file
logItem "Logfile used: $LOG_FILE"
fi
if [[ "$MSG_FILE" != "$DEST_MSGFILE" ]]; then
if [[ "$LOG_OUTPUT" == "$LOG_OUTPUT_VARLOG" ]]; then
logItem "Appending Msgfile: $MSG_FILE to $DEST_MSGFILE"
cat "$MSG_FILE" 1>> "$DEST_MSGFILE" 2>>"$FINISH_LOG_FILE"
rm "$MSG_FILE" &>>"$FINISH_LOG_FILE"
else
logItem "Moving Msgfile: $MSG_FILE to $DEST_MSGFILE"
mv "$MSG_FILE" "$DEST_MSGFILE" &>>"$FINISH_LOG_FILE"
fi
MSG_FILE="$DEST_MSGFILE" # now final msg location was established. log anything else in final log file
logItem "Msgfile used: $MSG_FILE"
fi
chown "$CALLING_USER:$CALLING_USER" "$DEST_LOGFILE" &>>"$FINISH_LOG_FILE" # make sure logfile is owned by caller
chown "$CALLING_USER:$CALLING_USER" "$DEST_MSGFILE" &>>"$FINISH_LOG_FILE" # make sure msgfile is owned by caller
if [[ -e $FINISH_LOG_FILE ]]; then # append optional final messages
logCommand "cat $FINISH_LOG_FILE"
cat "$FINISH_LOG_FILE" &>> "$DEST_LOGFILE"
rm -f "$FINISH_LOG_FILE" &>> "$DEST_LOGFILE"
fi
if (( !$INCLUDE_ONLY )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SAVED_LOG "$LOG_FILE"
fi
if [[ $TEMP_LOG_FILE != "$DEST_LOGFILE" ]]; then # logfile was copied somewhere, delete temp logfile
rm -f "$TEMP_LOG_FILE" &>> "$LOG_FILE"
fi
fi
logExit
}
# Borrowed from http://stackoverflow.com/questions/85880/determine-if-a-function-exists-in-bash
fn_exists() {
[[ $(type -t "$1") == function ]]
}
# Borrowed from http://blog.yjl.im/2012/01/printing-out-call-stack-in-bash.html
function logStack () {
writeToConsole $MSG_LEVEL_MINIMAL $MSG_STACK_TRACE
local i=0
local FRAMES=${#BASH_LINENO[@]}
# FRAMES-2 skips main, the last one in arrays
for ((i=FRAMES-2; i>=0; i--)); do
echo ' File' \""${BASH_SOURCE[i+1]}"\", line "${BASH_LINENO[i]}", in "${FUNCNAME[i+1]}"
# Grab the source code of the line
sed -n "${BASH_LINENO[i]}{s/^/ /;p}" "${BASH_SOURCE[i+1]}"
done
}
function callExtensions() { # extensionplugpoint rc
logEntry "$1"
local extension rc=0
if [[ "$1" == "$EMAIL_EXTENSION" ]]; then
local extensionFileName="${MYNAME}_${EMAIL_EXTENSION}.sh"
shift 1
local args=( "$@" )
if command -v "$extensionFileName" &>/dev/null; then
writeToConsole $MSG_LEVEL_DETAILED $MSG_EXTENSION_CALLED "$extensionFileName"
$extensionFileName "${args[@]}"
rc=$?
logItem "Extension RC: $rc"
if (( $rc != 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_EXTENSION_FAILED "$extensionFileName" "$rc"
fi
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_EMAIL_EXTENSION_NOT_FOUND "$extensionFileName"
fi
else
local extensions="$EXTENSIONS"
local xEnabled
(( $RESTORE )) && extensions="$RESTORE_EXTENSIONS"
for extension in $extensions; do
if [[ "$1" == "$NOTIFICATION_BACKUP_EXTENSION" ]]; then
local extensionFileName="${MYNAME}_${extension}.sh" # notification has no pre, post and ready
else
local extensionFileName="${MYNAME}_${extension}_$1.sh"
fi
if command -v "$extensionFileName" &>/dev/null; then
logItem "Calling $extensionFileName $2"
local extension_call=0
writeToConsole $MSG_LEVEL_DETAILED $MSG_EXTENSION_CALLED "$extensionFileName"
if [[ ${extension} == "$NOTIFICATION_BACKUP_EXTENSION" ]]; then
extension_call=1
fi
if (( extension_call )); then
xEnabled=0
if [ -o xtrace ]; then # disable xtrace
xEnabled=1
set +x
fi
fi
executeShellCommand ". $extensionFileName $2"
rc=$?
if (( extension_call )); then
if (( $xEnabled )); then # enable xtrace again
set -x
fi
fi
logItem "Extension RC: $rc"
if (( $rc != 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_EXTENSION_FAILED "$extensionFileName" "$rc"
fi
else
logItem "$extensionFileName not found - skipping"
fi
done
fi
logExit
return $rc
}
# usage
function usage() {
local activeLang=$FALLBACK_LANGUAGE # fallback language
[[ -n ${SUPPORTED_LANGUAGES[$LANGUAGE]} ]] && activeLang="$LANGUAGE"
# Prefer mapfile or read -a to split command output (or quote to avoid splitting).
#shellcheck disable=SC2207
NO_YES=( $(getMessage $MSG_NO_YES) )
local func="usage${activeLang}"
if ! fn_exists "$func"; then
func="usage$FALLBACK_LANGUAGE"
fi
$func
}
# Write message
function writeToConsole() { # msglevel messagenumber message
local msg level timestamp
(( $noNL )) && noNL="-n"
level=$1
shift
msg="$(getMessageText "$@")"
local msgNumber
msgNumber=$(cut -f 2 -d ' ' <<< "$msg")
local msgSev=
msgSev=${msgNumber:7:1}
if [[ $msgSev == "W" ]]; then
WARNING_MESSAGE_WRITTEN=1
NEWS_AVAILABLE=1
fi
if (( $level <= $MSG_LEVEL )); then
# --- RBK0105I: Deleting new backup directory /backup/obelix/obelix-rsync-backup-20180912-215541.
# ??? RBK0005E: Backup failed. Check previous error messages for details.
if (( $TIMESTAMPS )); then
timestamp="$(date +'%m-%d-%Y %T') "
fi
if (( $INTERACTIVE )); then
local consoleMsg="$timestamp$msg"
if [[ "$COLORING" =~ $COLORING_CONSOLE ]]; then
consoleMsg="$(colorAnnotation $COLOR_TYPE_VT100 "$consoleMsg")"
fi
if [[ $msgSev == "E" ]]; then
# Double quote to prevent globbing and word splitting.
#shellcheck disable=SC2086
echo $noNL -e "$consoleMsg" >&2
else
# Double quote to prevent globbing and word splitting.
#shellcheck disable=SC2086
echo $noNL -e "$consoleMsg" >&1
fi
fi
if (( "$LOG_LEVEL" != "$LOG_NONE" )); then
echo $noNL -e "$timestamp$msg" >> "$MSG_FILE"
fi
fi
if (( ! $INTERACTIVE || $RESTORE )); then # don't write message twice into log for backup but once for restore
local line
while IFS= read -r line; do
logIntoOutput $LOG_TYPE_MSG "$line"
done <<< "$msg"
fi
unset noNL
}
function isUnsupportedVersion() {
logEntry
local rc=0
[[ "$GIT_COMMIT" != "$SHA_PLACEHOLDER" && "$GIT_DATE" != "$DATE_PLACEHOLDER" ]] && rc=1
logExit $rc
return $rc
}
function isSupportedEnvironment() {
logEntry
if (( $REGRESSION_TEST )); then
logExit 0
return 0
fi
local MODELPATH=/sys/firmware/devicetree/base/model
local OSRELEASE=/etc/os-release
local RPI_ISSUE=/etc/rpi-issue
if [[ ! -e "$OSRELEASE" ]]; then
logItem "$OSRELEASE not found"
logExit 1
return 1
fi
logItem "$(<"$OSRELEASE")"
grep -q -E -i "^(NAME|ID)=.*ubuntu" "$OSRELEASE"
local rc=$?
IS_UBUNTU=$(( ! $rc ))
logItem "IS_UBUNTU: $IS_UBUNTU"
# Check it's Raspberry HW
if [[ ! -e "$MODELPATH" ]]; then
logItem "$MODELPATH not found"
logExit 1
return 1
fi
# See if you can use ${variable//search/replace} instead
#shellcheck disable=SC2001
logItem "Modelpath: $(sed 's/\x0/\n/g' <<< "$MODELPATH")"
! grep -q -i "raspberry" "$MODELPATH" && return 1
# OS was built for a Raspberry (RaspbainOS only)
if [[ -e "$RPI_ISSUE" ]]; then
logItem "$RPI_ISSUE: $(< $RPI_ISSUE)"
logExit 0
return 0
fi
logItem "$RPI_ISSUE not found"
logExit $rc
return $rc
}
# Create a backupfile FILE.bak from FILE. If this file already exists rename this file to FILE.n.bak when n is next backup number
# return filename created and error if a backup file cannot be created
function createBackupVersion() { # file
logEntry
local file="$1"
local rc
if [[ ! -f "$file" ]]; then
logExit 1
return 1
fi
if [[ -f "$file.bak" ]]; then # .bak exists already
local versions
# Double quote to prevent globbing and word splitting.
# shellcheck disable=SC2086
versions="$(ls $file\.*\.bak -1 2>/dev/null)"
if [[ -z $versions ]]; then # no backup version detected
versionNumber=1 # start with version 1
else
local last lastFile lastVersionNumber
last="$(tail -n 1 <<< "$versions")" # extract highest version number
lastFile="$(basename "$last")"
lastVersionNumber="$(sed -E 's/.*([0-9]+)\.bak$/\1/' <<< $lastFile )"
(( versionNumber = lastVersionNumber+1 )) # use next version number
fi
local backupFile="$file.${versionNumber}.bak"
logItem "Create version $backupFile"
if ! mv "$file.bak" "$backupFile"; then
logExit 1
return 1
fi
fi
cp -a "$file" "$file.bak"
rc=$?
echo "$file.bak"
logExit "rc: $rc - $file.bak"
return $rc # return status of cp command
}
# Borrowed from http://unix.stackexchange.com/questions/44040/a-standard-tool-to-convert-a-byte-count-into-human-kib-mib-etc-like-du-ls1
function bytesToHuman() {
local b d s S
local sign=1
b=${1:-0}; d=''; s=0; S=(Bytes {K,M,G,T,E,P,Y,Z}iB)
if (( b < 0 )); then
sign=-1
(( b=-b ))
fi
while ((b > 1024)); do
d="$(printf ".%02d" $((b % 1024 * 100 / 1024)))"
b=$((b / 1024))
let s++
done
if (( sign < 0 )); then
(( b=-b ))
fi
echo "$b$d ${S[$s]}"
}
function assertionFailed() { # lineno message
writeToConsole $MSG_LEVEL_MINIMAL $MSG_ASSERTION_FAILED "$GIT_COMMIT_ONLY" "$1" "$2"
rc=$RC_ASSERTION
logStack
exit 127
}
function exitNormal() {
saveVars
rc=0
exit 0
}
function saveVars() {
if (( $UID == 0 )); then
echo "BACKUP_TARGETDIR=\"$BACKUPTARGET_DIR\"" > "$VARS_FILE"
echo "BACKUP_TARGETFILE=\"$BACKUPTARGET_FILE\"" >> "$VARS_FILE"
echo "MSG_FILE=\"$MSG_FILE\"" >> "$VARS_FILE"
echo "LOG_FILE=\"$LOG_FILE\"" >> "$VARS_FILE"
fi
}
function exitError() { # {rc}
logEntry "$1"
if [[ -n "$1" ]]; then
rc="$1"
else
assertionFailed $LINENO "Unkown exit error $rc"
fi
logExit "$rc"
exit "$rc"
}
# ignore tool error if configured
function ignoreErrorRC() { # rc errors_to_ignore
logEntry
local rc="$1"
if (( $rc != 0 )); then
#Double quote array expansions to avoid re-splitting elements.
#shellcheck disable=SC2068
for i in ${@:2}; do
if (( $i == $rc )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TOOL_ERROR_SKIP "$BACKUPTYPE" "$rc"
rc=0
break
fi
done
fi
logExit "$rc"
return "$rc"
}
function executeDD() { # cmd silent
logEntry
local rc cmd
cmd="LC_ALL=C $1"
logItem "$cmd"
( eval "$cmd" 2>&1 1>&5 | grep -viE "records [in|out]| copied," | tee -a $MSG_FILE; exit ${PIPESTATUS[0]} ) 5>&1
ignoreErrorRC $? "$2"
rc=$?
logExit "$rc"
return "$rc"
}
# ((sh test.sh 2>&1 1>&3 | tee errors.log) 3>&1 | tee output.log) > /dev/null 2>&1
function executeRsync() { # cmd flagsToIgnore
logEntry
local rc cmd
cmd="$1"
logItem "$cmd"
( eval "$cmd" 2>&1 1>&5 | tee -a $MSG_FILE $LOG_FILE) 5>&1
ignoreErrorRC $? "$2"
rc=$?
logExit $rc
return $rc
}
# Removing leading `/' from member names message is annoying. Use grep -v "Removing" to remove the message
# and use $PIPESTATUS and catch and return the tar RC
function executeTar() { # cmd flagsToIgnore
logEntry
local rc cmd
cmd="LC_ALL=C $1"
logItem "$cmd"
( eval "$cmd" 2>&1 1>&5 | grep -iv " Removing" | tee -a $MSG_FILE $LOG_FILE; exit ${PIPESTATUS[0]} ) 5>&1
ignoreErrorRC $? "$2"
rc=$?
logExit $rc
return $rc
}
function executeShellCommand() { # command
logEntry "$@"
if (( $INTERACTIVE )); then
eval "$1"
else
eval "$1 &>> $LOG_FILE"
fi
local rc=$?
logExit "$rc"
return $rc
}
# return 0 for ==, -1 for <, and 1 for >
# version format 0.1.2.3-ext, -ext will be discarded
function compareVersions() { # v1 v2
logEntry "$1 $2"
local v1 v2
v1="$(sed 's/-.*$//' <<< "$1")"
v2="$(sed 's/-.*$//' <<< "$2")"
local v1e v2e IFS rc
#Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a.
#shellcheck disable=SC2206
IFS="." v1e=( $v1 0 0 0 0 )
#Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a.
#shellcheck disable=SC2206
IFS="." v2e=( $v2 0 0 0 0 )
local rc=0
for (( i=0; i<=3; i++ )); do
if (( ${v1e[$i]} < ${v2e[$i]} )); then
rc=-1
break
fi
if (( ${v1e[$i]} > ${v2e[$i]} )); then
rc=1
break
fi
done
echo $rc
logExit $rc
return
}
function repeat() { # char num
local s
s=$( yes "$1" | head -"$2" | tr -d "\n" )
echo $s
}
# calculate time difference, return array with days, hours, minutes and seconds
function duration() { # startTime endTime
factors=(86400 3600 60 1)
local diff=$(( $2 - $1 ))
local d i q
i=0
for f in "${factors[@]}"; do
q=$(( diff / f ))
diff=$(( diff - q * f ))
d[i]=$(printf "%02d" $q)
((i++))
done
echo "${d[@]}"
}
function logOptions() { # option state
logEntry "$1"
logItem "Options: $INVOCATIONPARMS"
logItem "AFTER_STARTSERVICES=$AFTER_STARTSERVICES"
logItem "APPEND_LOG=$APPEND_LOG"
logItem "APPEND_LOG_OPTION=$APPEND_LOG_OPTION"
logItem "BACKUPPATH=$BACKUPPATH"
logItem "BACKUPTYPE=$BACKUPTYPE"
logItem "BEFORE_STOPSERVICES=$BEFORE_STOPSERVICES"
logItem "BOOT_DEVICE=$BOOT_DEVICE"
logItem "CHECK_FOR_BAD_BLOCKS=$CHECK_FOR_BAD_BLOCKS"
logItem "COLOR_CODES=${COLOR_CODES[*]}"
logItem "COLORING=$COLORING"
logItem "CONFIG_FILE=$CONFIG_FILE"
logItem "DD_BACKUP_SAVE_USED_PARTITIONS_ONLY=$DD_BACKUP_SAVE_USED_PARTITIONS_ONLY"
logItem "DD_BLOCKSIZE=$DD_BLOCKSIZE"
logItem "DD_PARMS=$DD_PARMS"
logItem "DD_WARNING=$DD_WARNING"
logItem "DEPLOYMENT_HOSTS=$DEPLOYMENT_HOSTS"
logItem "DYNAMIC_MOUNT=$DYNAMIC_MOUNT"
logItem "EMAIL=$EMAIL"
logItem "EMAIL_COLORING=$EMAIL_COLORING"
logItem "EMAIL_PARMS=$EMAIL_PARMS"
logItem "EXCLUDE_LIST=$EXCLUDE_LIST"
logItem "EXTENSIONS=$EXTENSIONS"
logItem "FAKE=$FAKE"
logItem "FINAL_COMMAND=$FINAL_COMMAND"
logItem "HANDLE_DEPRECATED=$HANDLE_DEPRECATED"
logItem "IGNORE_ADDITIONAL_PARTITIONS=$IGNORE_ADDITIONAL_PARTITIONS"
logItem "IGNORE_MISSING_PARTITIONS=$IGNORE_MISSING_PARTITIONS"
logItem "KEEPBACKUPS=$KEEPBACKUPS"
logItem "KEEPBACKUPS_DD=$KEEPBACKUPS_DD"
logItem "KEEPBACKUPS_DDZ=$KEEPBACKUPS_DDZ"
logItem "KEEPBACKUPS_TAR=$KEEPBACKUPS_TAR"
logItem "KEEPBACKUPS_TGZ=$KEEPBACKUPS_TGZ"
logItem "KEEPBACKUPS_RSYNC=$KEEPBACKUPS_RSYNC"
logItem "LANGUAGE=$LANGUAGE"
logItem "LINK_BOOTPARTITIONFILES=$LINK_BOOTPARTITIONFILES"
logItem "LOG_LEVEL=$LOG_LEVEL"
logItem "LOG_OUTPUT=$LOG_OUTPUT"
logItem "MAIL_ON_ERROR_ONLY=$MAIL_ON_ERROR_ONLY"
logItem "MAIL_PROGRAM=$EMAIL_PROGRAM"
logItem "MSG_LEVEL=$MSG_LEVEL"
logItem "NOTIFY_START=$NOTIFY_START"
logItem "OLD_REMINDER_REPEAT=$OLD_REMINDER_REPEAT"
logItem "PARTITIONBASED_BACKUP=$PARTITIONBASED_BACKUP"
logItem "PARTITIONS_TO_BACKUP=$PARTITIONS_TO_BACKUP"
logItem "PARTITIONS_TO_RESTORE=$PARTITIONS_TO_RESTORE"
logItem "PUSHOVER_ADDITIONAL_OPTIONS=$PUSHOVER_ADDITIONAL_OPTIONS"
logItem "PUSHOVER_DEVICE=$PUSHOVER_DEVICE"
logItem "PUSHOVER_TOKEN=$PUSHOVER_TOKEN"
logItem "PUSHOVER_USER=$PUSHOVER_USER"
logItem "PUSHOVER_NOTIFICATIONS=$PUSHOVER_NOTIFICATIONS"
logItem "PUSHOVER_SOUND_SUCCESS=$PUSHOVER_SOUND_SUCCESS"
logItem "PUSHOVER_SOUND_FAILURE=$PUSHOVER_SOUND_FAILURE"
logItem "PUSHOVER_PRIORITY_SUCCESS=$PUSHOVER_PRIORITY_SUCCESS"
logItem "PUSHOVER_PRIORITY_FAILURE=$PUSHOVER_PRIORITY_FAILURE"
logItem "REBOOT_SYSTEM=$REBOOT_SYSTEM"
logItem "RESIZE_ROOTFS=$RESIZE_ROOTFS"
logItem "RESTORE_DEVICE=$RESTORE_DEVICE"
logItem "RESTORE_EXTENSIONS=$RESTORE_EXTENSIONS"
logItem "RESTORE_REMINDER_INTERVAL=$RESTORE_REMINDER_INTERVAL"
logItem "RESTORE_REMINDER_REPEAT=$RESTORE_REMINDER_REPEAT"
logItem "ROOT_PARTITION=$ROOT_PARTITION"
logItem "RSYNC_BACKUP_ADDITIONAL_OPTIONS=$RSYNC_BACKUP_ADDITIONAL_OPTIONS"
logItem "RSYNC_BACKUP_OPTION_EXCLUDE_ACLS"="$RSYNC_BACKUP_OPTION_EXCLUDE_ACLS"
logItem "RSYNC_BACKUP_OPTIONS=$RSYNC_BACKUP_OPTIONS"
logItem "RSYNC_IGNORE_ERRORS=$RSYNC_IGNORE_ERRORS"
logItem "SENDER_EMAIL=$SENDER_EMAIL"
logItem "SKIP_DEPRECATED=$SKIP_DEPRECATED"
logItem "SKIPLOCALCHECK=$SKIPLOCALCHECK"
logItem "SLACK_WEBHOOK_URL=$SLACK_WEBHOOK_URL"
logItem "SLACK_NOTIFICATIONS=$SLACK_NOTIFICATIONS"
logItem "SMART_RECYCLE=$SMART_RECYCLE"
logItem "SMART_RECYCLE_DRYRUN=$SMART_RECYCLE_DRYRUN"
logItem "SMART_RECYCLE_OPTIONS=$SMART_RECYCLE_OPTIONS"
logItem "STARTSERVICES=$STARTSERVICES"
logItem "STOPSERVICES=$STOPSERVICES"
logItem "SYSTEMSTATUS=$SYSTEMSTATUS"
logItem "TAR_BACKUP_ADDITIONAL_OPTIONS=$TAR_BACKUP_ADDITIONAL_OPTIONS"
logItem "TAR_BACKUP_OPTIONS=$TAR_BACKUP_OPTIONS"
logItem "TAR_BOOT_PARTITION_ENABLED=$TAR_BOOT_PARTITION_ENABLED"
logItem "TAR_COMPRESSION_TOOL=$TAR_COMPRESSION_TOOL"
logItem "TAR_COMPRESSION_TOOL_OPTIONS=$TAR_COMPRESSION_TOOL_OPTIONS"
logItem "TAR_IGNORE_ERRORS=$TAR_IGNORE_ERRORS"
logItem "TAR_RESTORE_ADDITIONAL_OPTIONS=$TAR_RESTORE_ADDITIONAL_OPTIONS"
logItem "TELEGRAM_TOKEN=$TELEGRAM_TOKEN"
logItem "TELEGRAM_CHATID=$TELEGRAM_CHATID"
logItem "TELEGRAM_THREADID=$TELEGRAM_THREADID"
logItem "TELEGRAM_NOTIFICATIONS=$TELEGRAM_NOTIFICATIONS"
logItem "TIMESTAMPS=$TIMESTAMPS"
logItem "UPDATE_UUIDS=$UPDATE_UUIDS"
logItem "VERBOSE=$VERBOSE"
logItem "YES_NO_RESTORE_DEVICE=$YES_NO_RESTORE_DEVICE"
logItem "ZIP_BACKUP=$ZIP_BACKUP"
logExit
}
function initializeDefaultConfigVariables() {
############# Begin default config section #############
# Part or whole of the following section can be put into
# /usr/local/etc/raspiBackup.conf, ~/.raspiBackup.conf or $(pwd)/.raspiBackup.conf
# and will take precedence over the following default definitions
# path to store the backupfile
DEFAULT_BACKUPPATH="/backup"
# how many backups to keep of all backup types
DEFAULT_KEEPBACKUPS=3
# how many backups to keep of the specific backup type. If zero DEFAULT_KEEPBACKUPS is used
DEFAULT_KEEPBACKUPS_DD=0
DEFAULT_KEEPBACKUPS_DDZ=0
DEFAULT_KEEPBACKUPS_TAR=0
DEFAULT_KEEPBACKUPS_TGZ=0
DEFAULT_KEEPBACKUPS_RSYNC=0
# type of backup: dd, tar or rsync
DEFAULT_BACKUPTYPE="rsync"
# zip tar or dd backup (0 = false, 1 = true)
DEFAULT_ZIP_BACKUP=0
# dd backup will save space used by partitions only
DEFAULT_DD_BACKUP_SAVE_USED_PARTITIONS_ONLY=0
# commands to stop services before backup separated by &&
DEFAULT_STOPSERVICES=""
# commands to start services after backup separated by &&
DEFAULT_STARTSERVICES=""
# commands to execute before backup stops separated by &&
DEFAULT_BEFORE_STOPSERVICES=""
# commands to execute after backup start separated by &&
DEFAULT_AFTER_STARTSERVICES=""
# commands to execute just before terminating
DEFAULT_FINAL_COMMAND=""
# HTML color and VT100 color for warning and error, yellow red
DEFAULT_COLOR_CODES=("#FF8000 33" "#FF0000 31")
# email to send completion status
DEFAULT_EMAIL=""
# sender email used with ssmtp
DEFAULT_SENDER_EMAIL=""
# Additional parameters for email program (optional)
DEFAULT_EMAIL_PARMS=""
# log level (0 = none, 1 = debug)
DEFAULT_LOG_LEVEL=1
# log output ( 1 = /var/log, 2 = backuppath, 3 = ./raspiBackup.log, )
DEFAULT_LOG_OUTPUT=2
# msg level (0 = minimal, 1 = detailed)
DEFAULT_MSG_LEVEL=0
# mailprogram
DEFAULT_MAIL_PROGRAM="mail"
# restore device
DEFAULT_RESTORE_DEVICE=""
# default append log (0 = false, 1 = true)
DEFAULT_APPEND_LOG=0
# option used by mail program to append log (for example -a or -A)
DEFAULT_APPEND_LOG_OPTION="-a"
# default verbose log (0 = false, 1 = true)
DEFAULT_VERBOSE=0
# skip check for remote mount of backup path (0 = false, 1 = true)
DEFAULT_SKIPLOCALCHECK=0
# blocksize used for dd
DEFAULT_DD_BLOCKSIZE=1MB
# addition parms used for dd
DEFAULT_DD_PARMS=""
# dd warning
DEFAULT_DD_WARNING=0
# exclude list
DEFAULT_EXCLUDE_LIST=""
# backup extensions to call
DEFAULT_EXTENSIONS=""
# restore extensions to call
DEFAULT_RESTORE_EXTENSIONS=""
# partition based backup (0 = false, 1 = true)
DEFAULT_PARTITIONBASED_BACKUP=0
# backup and restore first two partitions only
DEFAULT_PARTITIONS_TO_BACKUP="1 2"
DEFAULT_PARTITIONS_TO_RESTORE="1 2"
# language (DE or EN)
DEFAULT_LANGUAGE=""
# hosts which will get the updated backup script with parm -y - non pwd access with keys has to be enabled
# Example: "root@raspberrypi root@fhem root@openhab root@magicmirror"
DEFAULT_DEPLOYMENT_HOSTS=""
# Use with care !
DEFAULT_YES_NO_RESTORE_DEVICE="loop"
# Use hardlinks for partitionbootfiles
DEFAULT_LINK_BOOTPARTITIONFILES=0
# save boot partition with tar
DEFAULT_TAR_BOOT_PARTITION_ENABLED=0
# Compression tool to use for tar. Supported: bzip2 gzip lzip lzma lzop xz zstd
DEFAULT_TAR_COMPRESSION_TOOL=""
# Options to be passed to compression tool
DEFAULT_TAR_COMPRESSION_TOOL_OPTIONS=""
# reboot system at end of backup
DEFAULT_REBOOT_SYSTEM=0
# Change these options only if you know what you are doing !!!
DEFAULT_RSYNC_BACKUP_OPTIONS="-aHAx --delete --force --sparse" # -a <=> -rlptgoD, H = preserve hardlinks, x = one filesystem, A = preserver ACLs
DEFAULT_RSYNC_BACKUP_ADDITIONAL_OPTIONS=""
DEFAULT_RSYNC_BACKUP_OPTION_EXCLUDE_ACLS=0
DEFAULT_RSYNC_IGNORE_ERRORS="" # See https://bugzilla.samba.org/show_bug.cgi?id=3653
DEFAULT_TAR_BACKUP_OPTIONS="-cpi --one-file-system"
DEFAULT_TAR_BACKUP_ADDITIONAL_OPTIONS=""
DEFAULT_TAR_RESTORE_ADDITIONAL_OPTIONS=""
DEFAULT_TAR_IGNORE_ERRORS=""
# Send email only in case of errors. Use with care !
DEFAULT_MAIL_ON_ERROR_ONLY=0
# Smart recycle
DEFAULT_SMART_RECYCLE=0
# Smart recycle dryrun
DEFAULT_SMART_RECYCLE_DRYRUN=1
# Smart recycle parameters (daily, weekly, monthly and yearly)
DEFAULT_SMART_RECYCLE_OPTIONS="7 4 12 1"
# Check for back blocks when formating restore device (Will take a long time)
DEFAULT_CHECK_FOR_BAD_BLOCKS=0
# Resize root filesystem during restore
DEFAULT_RESIZE_ROOTFS=1
# add timestamps in front of messages
DEFAULT_TIMESTAMPS=0
# add system status in debug log (Attention: may expose sensible information)
DEFAULT_SYSTEMSTATUS=0
# reminder to test restore (unit: months)
DEFAULT_RESTORE_REMINDER_INTERVAL=6
# Number of times restore reminder bothers you
DEFAULT_RESTORE_REMINDER_REPEAT=3
# update device UUIDs
DEFAULT_UPDATE_UUIDS=1
# send stats
DEFAULT_SEND_STATS=1
# ignore partitions > 2 in normal mode
DEFAULT_IGNORE_ADDITIONAL_PARTITIONS=0
# Allow to save not all previously saved partitions
DEFAULT_IGNORE_MISSING_PARTITIONS=0
# notify in email and telegram when backup starts
DEFAULT_NOTIFY_START=0
# Telegram token
DEFAULT_TELEGRAM_TOKEN=""
# Telegram target chatid
DEFAULT_TELEGRAM_CHATID=""
# Telegram thread ID for the target chat (optional)
DEFAULT_TELEGRAM_THREADID=""
# Telegram notifications to send. S(uccess), F(ailure), M(messages as file), m(essages as text)
DEFAULT_TELEGRAM_NOTIFICATIONS="F"
# Pushover additional options
# Note: All additional API keys have to be prefixed with --form-string. Example: "--form-string ttl=3600 --form-string priority=0"
DEFAULT_PUSHOVER_ADDITIONAL_OPTIONS=""
# Pushover device
DEFAULT_PUSHOVER_DEVICE=""
# Pushover token
DEFAULT_PUSHOVER_TOKEN=""
# Pushover user
DEFAULT_PUSHOVER_USER=""
# Pushover notifications to send. S(uccess), F(ailure), M(essages)
DEFAULT_PUSHOVER_NOTIFICATIONS="F"
# Pushover sound for success
DEFAULT_PUSHOVER_SOUND_SUCCESS=""
# Pushover sound for failure
DEFAULT_PUSHOVER_SOUND_FAILURE=""
# Pushover priorities
DEFAULT_PUSHOVER_PRIORITY_SUCCESS="0"
DEFAULT_PUSHOVER_PRIORITY_FAILURE="1"
# Slack
DEFAULT_SLACK_WEBHOOK_URL=""
DEFAULT_SLACK_NOTIFICATIONS=""
# Colorize console output (C) and/or email (E)
DEFAULT_COLORING="CM"
# mail coloring scheme (SUBJECT or OPTION)
DEFAULT_EMAIL_COLORING="$EMAIL_COLORING_SUBJECT"
# Name of backup partition to dynamically mount (e.g. /dev/sda1 or /backup)
DEFAULT_DYNAMIC_MOUNT=""
# Define bootdevice (e.g. /dev/mmcblk0, /dev/nvme0n1 or /dev/sda) and turn off boot device autodiscovery
DEFAULT_BOOT_DEVICE=""
# How often inform about possible old-named backups
DEFAULT_OLD_REMINDER_REPEAT="5"
############# End default config section #############
}
function copyDefaultConfigVariables() {
APPEND_LOG="$DEFAULT_APPEND_LOG"
APPEND_LOG_OPTION="$DEFAULT_APPEND_LOG_OPTION"
BACKUPPATH="$DEFAULT_BACKUPPATH"
BACKUPTYPE="$DEFAULT_BACKUPTYPE"
BOOT_DEVICE="$DEFAULT_BOOT_DEVICE"
AFTER_STARTSERVICES="$DEFAULT_AFTER_STARTSERVICES"
BEFORE_STOPSERVICES="$DEFAULT_BEFORE_STOPSERVICES"
CHECK_FOR_BAD_BLOCKS="$DEFAULT_CHECK_FOR_BAD_BLOCKS"
COLOR_CODES=("${DEFAULT_COLOR_CODES[0]}" "${DEFAULT_COLOR_CODES[1]}")
COLORING="$DEFAULT_COLORING"
DD_BACKUP_SAVE_USED_PARTITIONS_ONLY="$DEFAULT_DD_BACKUP_SAVE_USED_PARTITIONS_ONLY"
DD_BLOCKSIZE="$DEFAULT_DD_BLOCKSIZE"
DD_PARMS="$DEFAULT_DD_PARMS"
DD_WARNING="$DEFAULT_DD_WARNING"
DEPLOYMENT_HOSTS="$DEFAULT_DEPLOYMENT_HOSTS"
EMAIL="$DEFAULT_EMAIL"
EMAIL_COLORING="$DEFAULT_EMAIL_COLORING"
EMAIL_PARMS="$DEFAULT_EMAIL_PARMS"
EMAIL_PROGRAM="$DEFAULT_MAIL_PROGRAM"
EXCLUDE_LIST="$DEFAULT_EXCLUDE_LIST"
EXTENSIONS="$DEFAULT_EXTENSIONS"
FINAL_COMMAND="$DEFAULT_FINAL_COMMAND"
IGNORE_ADDITIONAL_PARTITIONS="$DEFAULT_IGNORE_ADDITIONAL_PARTITIONS"
IGNORE_MISSING_PARTITIONS="$DEFAULT_IGNORE_MISSING_PARTITIONS"
KEEPBACKUPS="$DEFAULT_KEEPBACKUPS"
KEEPBACKUPS_DD="$DEFAULT_KEEPBACKUPS_DD"
KEEPBACKUPS_DDZ="$DEFAULT_KEEPBACKUPS_DDZ"
KEEPBACKUPS_TAR="$DEFAULT_KEEPBACKUPS_TAR"
KEEPBACKUPS_TGZ="$DEFAULT_KEEPBACKUPS_TGZ"
KEEPBACKUPS_RSYNC="$DEFAULT_KEEPBACKUPS_RSYNC"
LINK_BOOTPARTITIONFILES="$DEFAULT_LINK_BOOTPARTITIONFILES"
LOG_LEVEL="$DEFAULT_LOG_LEVEL"
LOG_OUTPUT="$DEFAULT_LOG_OUTPUT"
MAIL_ON_ERROR_ONLY="$DEFAULT_MAIL_ON_ERROR_ONLY"
MSG_LEVEL="$DEFAULT_MSG_LEVEL"
NOTIFY_START="$DEFAULT_NOTIFY_START"
OLD_REMINDER_REPEAT="$DEFAULT_OLD_REMINDER_REPEAT"
PARTITIONBASED_BACKUP="$DEFAULT_PARTITIONBASED_BACKUP"
PARTITIONS_TO_BACKUP="$DEFAULT_PARTITIONS_TO_BACKUP"
PARTITIONS_TO_RESTORE="$DEFAULT_PARTITIONS_TO_RESTORE"
PUSHOVER_ADDITIONAL_OPTIONS="$DEFAULT_PUSHOVER_ADDITIONAL_OPTIONS"
PUSHOVER_DEVICE="$DEFAULT_PUSHOVER_DEVICE"
PUSHOVER_TOKEN="$DEFAULT_PUSHOVER_TOKEN"
PUSHOVER_USER="$DEFAULT_PUSHOVER_USER"
PUSHOVER_NOTIFICATIONS="$DEFAULT_PUSHOVER_NOTIFICATIONS"
PUSHOVER_SOUND_SUCCESS="$DEFAULT_PUSHOVER_SOUND_SUCCESS"
PUSHOVER_SOUND_FAILURE="$DEFAULT_PUSHOVER_SOUND_FAILURE"
PUSHOVER_PRIORITY_SUCCESS="$DEFAULT_PUSHOVER_PRIORITY_SUCCESS"
PUSHOVER_PRIORITY_FAILURE="$DEFAULT_PUSHOVER_PRIORITY_FAILURE"
REBOOT_SYSTEM="$DEFAULT_REBOOT_SYSTEM"
RESIZE_ROOTFS="$DEFAULT_RESIZE_ROOTFS"
RESTORE_DEVICE="$DEFAULT_RESTORE_DEVICE"
RESTORE_EXTENSIONS="$DEFAULT_RESTORE_EXTENSIONS"
RESTORE_REMINDER_INTERVAL="$DEFAULT_RESTORE_REMINDER_INTERVAL"
RESTORE_REMINDER_REPEAT="$DEFAULT_RESTORE_REMINDER_REPEAT"
RSYNC_BACKUP_ADDITIONAL_OPTIONS="$DEFAULT_RSYNC_BACKUP_ADDITIONAL_OPTIONS"
RSYNC_BACKUP_OPTION_EXCLUDE_ACLS="$DEFAULT_RSYNC_BACKUP_OPTION_EXCLUDE_ACLS"
RSYNC_IGNORE_ERRORS="$DEFAULT_RSYNC_IGNORE_ERRORS"
RSYNC_BACKUP_OPTIONS="$DEFAULT_RSYNC_BACKUP_OPTIONS"
SENDER_EMAIL="$DEFAULT_SENDER_EMAIL"
SKIPLOCALCHECK="$DEFAULT_SKIPLOCALCHECK"
SLACK_WEBHOOK_URL="$DEFAULT_SLACK_WEBHOOK_URL"
SLACK_NOTIFICATIONS="$DEFAULT_SLACK_NOTIFICATIONS"
SMART_RECYCLE="$DEFAULT_SMART_RECYCLE"
SMART_RECYCLE_DRYRUN="$DEFAULT_SMART_RECYCLE_DRYRUN"
SMART_RECYCLE_OPTIONS="$DEFAULT_SMART_RECYCLE_OPTIONS"
STARTSERVICES="$DEFAULT_STARTSERVICES"
SEND_STATS="$DEFAULT_SEND_STATS"
STOPSERVICES="$DEFAULT_STOPSERVICES"
SYSTEMSTATUS="$DEFAULT_SYSTEMSTATUS"
TAR_BACKUP_ADDITIONAL_OPTIONS="$DEFAULT_TAR_BACKUP_ADDITIONAL_OPTIONS"
TAR_BACKUP_OPTIONS="$DEFAULT_TAR_BACKUP_OPTIONS"
TAR_BOOT_PARTITION_ENABLED="$DEFAULT_TAR_BOOT_PARTITION_ENABLED"
TAR_COMPRESSION_TOOL="$DEFAULT_TAR_COMPRESSION_TOOL"
TAR_COMPRESSION_TOOL_OPTIONS="$DEFAULT_TAR_COMPRESSION_TOOL_OPTIONS"
TAR_RESTORE_ADDITIONAL_OPTIONS="$DEFAULT_TAR_RESTORE_ADDITIONAL_OPTIONS"
TAR_IGNORE_ERRORS="$DEFAULT_TAR_IGNORE_ERRORS"
TELEGRAM_CHATID="$DEFAULT_TELEGRAM_CHATID"
TELEGRAM_THREADID="$DEFAULT_TELEGRAM_THREADID"
TELEGRAM_NOTIFICATIONS="$DEFAULT_TELEGRAM_NOTIFICATIONS"
TELEGRAM_TOKEN="$DEFAULT_TELEGRAM_TOKEN"
TIMESTAMPS="$DEFAULT_TIMESTAMPS"
UPDATE_UUIDS="$DEFAULT_UPDATE_UUIDS"
VERBOSE="$DEFAULT_VERBOSE"
YES_NO_RESTORE_DEVICE="$DEFAULT_YES_NO_RESTORE_DEVICE"
ZIP_BACKUP="$DEFAULT_ZIP_BACKUP"
DYNAMIC_MOUNT="$DEFAULT_DYNAMIC_MOUNT"
checkImportantParameters
}
function isSpecialBlockDevice() { # either device (mmcblk, sd) or device name (/dev/mmcblk, /dev/sd )
logEntry "$1"
local rc
[[ $1 =~ mmcblk|loop|nvme ]]
rc=$?
logExit "$rc"
return $rc
}
function createPartitionName() { # either device (mmcblk0, sda) or device name (/dev/mmcblk0, /dev/sda ) and partition number (may be empty)
logEntry "$1 $2"
local result="$1"
if isSpecialBlockDevice "$1"; then
result="${result}p"
fi
if [[ -n "$2" ]]; then
result="${result}$2"
fi
logExit "$result"
echo "$result"
}
# Input:
# mmcblk0
# sda
# nvme0n1
# Output:
# mmcblk0p
# sda
# nvme0n1p
function getPartitionPrefix() { # device
logEntry "$1"
local pref="$1"
if isSpecialBlockDevice "$1"; then
pref="${1}p"
fi
logExit "$pref"
echo "$pref"
}
# Input:
# /dev/mmcblk0p1
# /dev/sda2
# /dev/nvme0n1p1
# Output:
# 1
# 2
function getPartitionNumber() { # deviceName
logEntry "$1"
local id
if [[ $1 =~ ^/dev/(mmcblk|loop)[0-9]+p([0-9]+) || $1 =~ ^/dev/((s|v)d[a-z])([0-9]+) || $1 =~ ^/dev/(nvme)[0-9]+n[0-9]+p([0-9]+) ]]; then
id=${BASH_REMATCH[2]}
else
assertionFailed $LINENO "Unable to retrieve partition number from deviceName $1"
fi
echo "$id"
logExit "$id"
}
function hasSpaces() { # file- or directory name
[[ $1 = *" "* ]]
return
}
# borrowed from https://gist.github.com/cdown/1163649#file-gistfile1-sh
function urlencode() {
local old_lc_collate=$LC_COLLATE
LC_COLLATE=C
local length="${#1}"
local i
for (( i = 0; i < length; i++ )); do
local c="${1:$i:1}"
case $c in
[a-zA-Z0-9.~_-]) printf '%s' "$c" ;;
*) printf '%%%02X' "'$c" ;;
esac
done
LC_COLLATE=$old_lc_collate
}
# borrowed from https://gist.github.com/cdown/1163649#file-gistfile1-sh
function urldecode() {
local url_encoded="${1//+/ }"
printf '%b' "${url_encoded//%/\\x}"
}
function dynamic_mount() { # mountpoint
logEntry "$1"
local rc=0
if ! isMounted $1; then
mount "$1" &>> $LOG_FILE
rc=$?
if (( $rc != 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DYNAMIC_MOUNT_FAILED "$1" "$rc"
exitError "$RC_MOUNT_FAILED"
else
DYNAMIC_MOUNT_EXECUTED=1
writeToConsole $MSG_LEVEL_DETAILED $MSG_DYNAMIC_MOUNT_OK "$1"
fi
else
writeToConsole $MSG_LEVEL_DETAILED $MSG_DYNAMIC_MOUNT_NOT_REQUIRED "$1"
fi
logCommand "mount"
logExit
}
function isUpdatePossible() {
logEntry
local versions version_rc latestVersion newVersion oldVersion
# Prefer mapfile or read -a to split command output (or quote to avoid splitting).
#shellcheck disable=SC2207
versions=( $(isNewVersionAvailable) )
version_rc=$?
if [[ $version_rc == 0 ]]; then
NEWS_AVAILABLE=1
UPDATE_POSSIBLE=1
latestVersion="${versions[0]}"
newVersion="${versions[1]}"
oldVersion="${versions[2]}"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NEW_VERSION_AVAILABLE "$newVersion" "$oldVersion"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_VISIT_VERSION_HISTORY_PAGE "$(getMessage $MSG_VERSION_HISTORY_PAGE)"
fi
logExit
}
function downloadPropertiesFile() { # FORCE
logEntry "$1"
NEW_PROPERTIES_FILE=0
if shouldRenewDownloadPropertiesFile "$1" && (( ! $REGRESSION_TEST && ! $IS_DEV )); then # don't execute any update checks
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CHECKING_FOR_NEW_VERSION
if (( $SEND_STATS )); then
local mode="N"; (( $PARTITIONBASED_BACKUP )) && mode="P"
local type=$BACKUPTYPE
local keep=$KEEPBACKUPS
local func="B"; (( $RESTORE )) && func="R"
local srOptions
srOptions="$(urlencode "$SMART_RECYCLE_OPTIONS")"
local srs=""; [[ -n $SMART_RECYCLE_DRYRUN ]] && (( ! $SMART_RECYCLE_DRYRUN )) && srs="$srOptions"
local os="rsp"; (( $IS_UBUNTU )) && os="ubu"
local downloadURL="${PROPERTIES_DOWNLOAD_URL}?version=$VERSION&type=$type&mode=$mode&keep=$keep&func=$func&srs=$srs&os=$os"
else
local downloadURL="$PROPERTIES_DOWNLOAD_URL"
fi
local dlHttpCode dlRC
dlHttpCode=$(downloadFile "$downloadURL" "$LATEST_TEMP_PROPERTY_FILE")
dlRC=$?
if (( $dlRC != 0 )); then
if [[ $1 == "FORCE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DOWNLOAD_FAILED "$(sed "s/\?.*$//" <<< "$downloadURL")" "$dlHttpCode" $dlRC
exitError $RC_DOWNLOAD_FAILED
else
: # silently ignore download error or property file
fi
else
NEW_PROPERTIES_FILE=1
parsePropertiesFile "$LATEST_TEMP_PROPERTY_FILE"
fi
fi
logExit "$NEW_PROPERTIES_FILE"
return
}
#VERSION="0.6.3.1"
#INCOMPATIBLE=""
#DEPRECATED=""
#BETA="0.6.3.2"
function parsePropertiesFile() { # propertyFileName
logEntry
local properties
properties="$(grep "^VERSION=" "$1" 2>/dev/null)"
[[ $properties =~ $PROPERTY_REGEX ]] && VERSION_PROPERTY=${BASH_REMATCH[1]}
properties="$(grep "^INCOMPATIBLE=" "$1" 2>/dev/null)"
[[ $properties =~ $PROPERTY_REGEX ]] && INCOMPATIBLE_PROPERTY=${BASH_REMATCH[1]}
properties="$(grep "^DEPRECATED=" "$1" 2>/dev/null)"
[[ $properties =~ $PROPERTY_REGEX ]] && DEPRECATED_PROPERTY=${BASH_REMATCH[1]}
properties="$(grep "^BETA=" "$1" 2>/dev/null)"
[[ $properties =~ $PROPERTY_REGEX ]] && BETA_PROPERTY=${BASH_REMATCH[1]}
logItem "Properties: v: $VERSION_PROPERTY i: $INCOMPATIBLE_PROPERTY d: $DEPRECATED_PROPERTY b: $BETA_PROPERTY"
logExit
}
function isVersionDeprecated() { # versionNumber
logEntry
local rc=1 # no/failure
local deprecatedVersions=( "$DEPRECATED_PROPERTY" )
if containsElement "$1" "${deprecatedVersions[@]}"; then
rc=0
logItem "Version $1 is deprecated"
fi
local skip=( "$SKIP_DEPRECATED" )
if containsElement "$1" "${skip[@]}"; then
rc=1
logItem "Version $1 is deprecated but message is disabled"
fi
logExit "$rc"
return $rc
}
function shouldRenewDownloadPropertiesFile() { # FORCE
logEntry
local rc currentTime lastCheckTime
if [[ -e $LATEST_TEMP_PROPERTY_FILE ]]; then
lastCheckTime=$(stat -c %y $LATEST_TEMP_PROPERTY_FILE | cut -d ' ' -f 1 | sed 's/-//g')
else
lastCheckTime="00000000"
fi
currentTime=$(date +%Y%m%d)
logItem "$currentTime : $lastCheckTime"
if [[ $currentTime == "$lastCheckTime" && "$1" != "FORCE" ]]; then
logItem "Skip download"
rc=1 # download already done today
else
rc=0
fi
logExit "$rc"
return $rc
}
function verifyIsOnOff() { # arg
local v=${!1}
local uc="${v^^}"
case "$uc" in
ON|TRUE|AN|1) : echo "1"
return
;;
OFF|FALSE|AUS|0) : echo "0"
return
;;
esac
writeToConsole $MSG_LEVEL_MINIMAL $MSG_INVALID_TRUE_FALSE_OPTION "$v" "$1"
exitError $RC_PARAMETER_ERROR
}
function downloadFile() { # url, targetFileName
local url
url="$(sed -E 's/\?.*$//' <<< $1)"
logEntry "URL: $url, file: $2"
local httpCode rc f
local url="$1"
local file="$2"
f=$(mktemp)
local httpCode rc
# This {/} is literal. Check if ; is missing or quote the expression.
#shellcheck disable=SC1083
httpCode=$(curl -sSL -o "$f" -m $DOWNLOAD_TIMEOUT -w %{http_code} -L "$url" 2>>"$LOG_FILE")
rc=$?
logItem "httpCode: $httpCode RC: $rc"
# Some nasty code required because download plugin doesn't return 404 if file not found but a HTML doc
if (( $rc == 0 )); then
if [[ ! -f "$f" ]]; then
httpCode="404"
rc=101
elif [[ ${httpCode:0:1} == "2" ]]; then
if head -n 1 "$f" | grep -q "^"; then
httpCode="404"
rc=101
fi
else
rc=101
fi
fi
if (( $rc != 0 )); then
[[ -f $f ]] && rm $f &>>$LOG_FILE
echo "$httpCode"
logExit "$rc $httpCode"
return $rc
fi
[[ -f $f ]] && mv $f $file &>>$LOG_FILE
echo "200"
logExit 0
return 0
}
function askYesNo() { # message message_parms
local yes_no yes
yes_no=$(getMessage $MSG_QUERY_CHARS_YES_NO)
local addtlMsg=0
if (( $# > 1 )); then
local m="$1"
shift
addtlMsg=1
# Variable was used as an array but is now assigned a string.
#shellcheck disable=SC2178,SC2124
local args="$@"
fi
local answer
if (( $addtlMsg )); then
noNL=1
#Expanding an array without an index only gives the element in the index 0.
#shellcheck disable=SC2128
writeToConsole $MSG_LEVEL_MINIMAL "$m" "$args" "$yes_no"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_ARE_YOU_SURE "$yes_no"
fi
if (( $NO_YES_QUESTION )); then
answer=$(getMessage $MSG_ANSWER_CHARS_YES)
else
read answer
fi
answer=${answer:0:1} # first char only
answer=${answer:-"n"} # set default no
yes=$(getMessage $MSG_ANSWER_CHARS_YES)
if [[ ! $yes =~ $answer ]]; then
return 1
else
return 0
fi
}
function isNewVersionAvailable() {
logEntry
local newVersion="0.0"
local latestVersion="0.0"
local rc=99 # update not possible
local version="$VERSION"
local suffix=""
if [[ "$VERSION" =~ ^([^-]*)(-(.*))?$ ]]; then
version=${BASH_REMATCH[1]}
suffix=${BASH_REMATCH[3]}
fi
logItem "Versionsplit: $version - $suffix"
if (( $NEW_PROPERTIES_FILE )); then
local newVersion=$VERSION_PROPERTY
latestVersion=$(echo -e "$newVersion\n$version" | sort -V | tail -1)
logItem "new: $newVersion runtime: $version latest: $latestVersion"
if (( $(compareVersions $version $newVersion) < 0 )); then # $version < $newVersion
rc=0 # new version available
elif (( $(compareVersions $version $newVersion) > 0 )); then # $version > $newVersion
rc=2 # current version is a newer version
else # versions are identical
if [[ -z $suffix ]]; then
rc=1 # no suffix, current version is latest version
else
if (( $IS_BETA || $IS_DEV )); then
rc=0 # current is beta or development version, replace with final version
elif (( $IS_HOTFIX )); then
rc=2 # current version is hotfix, keep it until new version is available
else
rc=1 # current version is latest version
fi
fi
fi
fi
result="$latestVersion $newVersion $VERSION"
echo "$result"
logItem "Returning: $result"
logExit "$rc"
return $rc
}
function stopServices() {
logEntry
if [[ -n "$STOPSERVICES" ]]; then
if [[ "$STOPSERVICES" =~ $NOOP_AO_ARG_REGEX ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SKIP_STOPPING_SERVICES
else
writeToConsole $MSG_LEVEL_DETAILED $MSG_STOPPING_SERVICES "$STOPSERVICES"
logItem "$STOPSERVICES"
STOPPED_SERVICES=1
executeShellCommand "$STOPSERVICES"
local rc=$?
if [[ $rc != 0 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_STOP_SERVICES_FAILED "$rc"
exitError $RC_STOP_SERVICES_ERROR
fi
fi
fi
logSystemServices
logExit
}
function executeBeforeStopServices() {
logEntry
if [[ -n "$BEFORE_STOPSERVICES" ]]; then
writeToConsole $MSG_LEVEL_DETAILED $MSG_BEFORE_STOPPING_SERVICES "$BEFORE_STOPSERVICES"
logItem "$BEFORE_STOPSERVICES"
executeShellCommand "$BEFORE_STOPSERVICES"
local rc=$?
if [[ $rc != 0 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BEFORE_STOP_SERVICES_FAILED "$rc"
exitError $RC_BEFORE_STOP_SERVICES_ERROR
fi
fi
logExit
}
function finalCommand() {
logEntry
if [[ -n "$FINAL_COMMAND" ]]; then
writeToConsole $MSG_LEVEL_DETAILED $MSG_FINAL_COMMAND_EXECUTED "$FINAL_COMMAND"
logItem "$FINAL_COMMAND"
executeShellCommand "$FINAL_COMMAND"
local rc=$?
if [[ $rc != 0 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FINAL_COMMAND_FAILED "$rc"
fi
fi
logExit
}
function startServices() {
logEntry
logSystemServices
if (( $STOPPED_SERVICES )); then
if [[ -n "$STARTSERVICES" ]]; then
if (( ! $RESTORE && $REBOOT_SYSTEM && ! $FAKE )); then
: # just ignore STARTSERVICES
elif [[ "$STARTSERVICES" =~ $NOOP_AO_ARG_REGEX ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SKIP_STARTING_SERVICES
else
writeToConsole $MSG_LEVEL_DETAILED $MSG_STARTING_SERVICES "$STARTSERVICES"
logItem "$STARTSERVICES"
executeShellCommand "$STARTSERVICES"
local rc=$?
if [[ $rc != 0 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_START_SERVICES_FAILED "$rc"
if [[ "$1" != "noexit" ]]; then
exitError $RC_START_SERVICES_ERROR
fi
fi
STOPPED_SERVICES=0
fi
fi
fi
logExit
}
function executeAfterStartServices() {
logEntry
if [[ -n "$AFTER_STARTSERVICES" ]]; then
writeToConsole $MSG_LEVEL_DETAILED $MSG_AFTER_STARTING_SERVICES "$AFTER_STARTSERVICES"
logItem "$AFTER_STARTSERVICES"
executeShellCommand "$AFTER_STARTSERVICES"
local rc=$?
if [[ $rc != 0 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BEFORE_START_SERVICES_FAILED "$rc"
if [[ "$1" != "noexit" ]]; then
exitError $RC_BEFORE_START_SERVICES_ERROR
fi
fi
fi
logExit
}
function extractVersionFromFile() { # fileName type (VERSION|VERSION_CONFIG)
logEntry "$@"
local v
v="$(grep -E "^$2=" "$1" | cut -f 2 -d = | sed -e 's/[[:space:]]*#.*$//g' -e 's/\"//g')"
echo "$v"
logExit "$v"
}
# update script with latest version
function updateScript() {
logEntry
local rc versions latestVersion newVersion oldVersion newName betaVersion properties
local updateNow=0
if (( $NEW_PROPERTIES_FILE )) ; then
# Prefer mapfile or read -a to split command output (or quote to avoid splitting).
#shellcheck disable=SC2207
versions=( $(isNewVersionAvailable) )
rc=$? # need rc so no map usage possible
latestVersion=${versions[0]}
newVersion=${versions[1]}
oldVersion=${versions[2]}
logItem "$rc - $latestVersion - $newVersion - $oldVersion"
if (( ! $FORCE_UPDATE )) ; then
local incompatibleVersions=( "$INCOMPATIBLE_PROPERTY" )
if containsElement "$newVersion" "${incompatibleVersions[@]}"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_INCOMPATIBLE_UPDATE "$newVersion" "$(getMessage $MSG_VERSION_HISTORY_PAGE)"
exitNormal
fi
fi
betaVersion=$(isBetaAvailable)
if [[ -n $betaVersion ]]; then
if (( ! $FORCE_UPDATE )) && [[ "${betaVersion}-beta" > $oldVersion ]]; then # beta version available
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UPDATE_TO_BETA "$oldVersion" "${betaVersion}-beta"
if askYesNo; then
DOWNLOAD_URL="$BETA_DOWNLOAD_URL"
newVersion="${betaVersion}-beta"
updateNow=1
fi
elif (( $FORCE_UPDATE )) && [[ "${betaVersion}-beta" == "$oldVersion" ]]; then # refresh current beta with latest version
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UPDATE_TO_LATEST_BETA "${betaVersion}-beta"
if askYesNo; then
DOWNLOAD_URL="$BETA_DOWNLOAD_URL"
newVersion="${betaVersion}-beta"
updateNow=1
fi
fi
fi
if [[ $rc == 0 ]] && (( ! $updateNow )); then # new version available
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UPDATE_TO_VERSION "$oldVersion" "$newVersion"
if ! askYesNo; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UPDATE_ABORTED
exitNormal
fi
updateNow=1
elif [[ $rc == 1 || $rc == 2 ]] && (( ! $IS_BETA )) && (( ! $updateNow && $FORCE_UPDATE )); then # no beta version, same or upper version (maybe development version) but force update
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FORCE_UPDATE "$newVersion"
if askYesNo; then
updateNow=1
fi
fi
if (( $updateNow )); then
local tmpFile="/tmp/${MYSELF}~"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DOWNLOADING "$MYSELF" "$MYHOMEURL"
local dlHttpCode dlRC
dlHttpCode="$(downloadFile "$DOWNLOAD_URL" "${tmpFile}")"
dlRC=$?
if (( $dlRC != 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DOWNLOAD_FAILED "$DOWNLOAD_URL" "$dlHttpCode" $dlRC
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SCRIPT_UPDATE_FAILED "$MYSELF"
exitError $RC_DOWNLOAD_FAILED
fi
newName="$SCRIPT_DIR/$MYNAME.$oldVersion.sh"
mv $SCRIPT_DIR/$MYSELF $newName
mv $tmpFile $SCRIPT_DIR/$MYSELF
chown --reference=$newName $SCRIPT_DIR/$MYSELF
chmod --reference=$newName $SCRIPT_DIR/$MYSELF
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SCRIPT_UPDATE_OK "$SCRIPT_DIR/$MYSELF" "$oldVersion" "$newVersion" "$newName"
# refresh version information from updated script
properties="$(grep "^VERSION=" "$SCRIPT_DIR/$MYSELF" 2>/dev/null)"
[[ $properties =~ $PROPERTY_REGEX ]] && VERSION=${BASH_REMATCH[1]}
logItem "Updating VERSION from updated script to $VERSION"
properties="$(grep "^VERSION_SCRIPT_CONFIG=" "$SCRIPT_DIR/$MYSELF" 2>/dev/null)"
[[ $properties =~ $PROPERTY_REGEX ]] && VERSION_SCRIPT_CONFIG=${BASH_REMATCH[1]}
logItem "Updating VERSION_SCRIPT_CONFIG from updated script to $VERSION_SCRIPT_CONFIG"
else
rm $MYSELF~ &>/dev/null
if [[ $rc == 1 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SCRIPT_UPDATE_NOT_NEEDED "$SCRIPT_DIR/$MYSELF" "$newVersion"
elif [[ $rc == 2 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SCRIPT_UPDATE_NOT_REQUIRED "$SCRIPT_DIR/$MYSELF" "$oldVersion" "$newVersion"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SCRIPT_UPDATE_FAILED "$MYSELF"
fi
fi
fi
logExit $updateNow
return $updateNow
}
function hasDefaultACLs() { # directory
local rc=2 # getfacl not found
logEntry "$1"
if hash "getfacl" 2>/dev/null; then
getfacl -p $1 | grep -q -E "^default" &>>$LOG_FILE
rc=$?
else
logItem "getfacl not found"
fi
logExit "$rc"
return $rc
}
# 0 = yes, no otherwise
function supportsFileAttributes() { # directory
logEntry "$1"
local attrs owner group r x
local attrsT ownerT groupT
local result=1 # no
local MAXRETRY=3 # retries
local retryCount=$(( MAXRETRY + 1 ))
touch /tmp/$MYNAME.fileattributes &>>$LOG_FILE
chown 65534:65534 /tmp/$MYNAME.fileattributes &>>$LOG_FILE
chmod 057 /tmp/$MYNAME.fileattributes &>>$LOG_FILE
# ls -la output
# ----r-xrwx 1 nobody nogroup 0 Oct 30 19:06 /tmp/supportsFileattributes.fileattributes
read -r attrs x owner group r <<< "$(ls -la "/tmp/$MYNAME.fileattributes")"
logItem "$attrs # $owner # $group"
while (( retryCount-- > 0 && result == 1 )); do
# following command will return an error and message
# cp: failed to preserve ownership for '/mnt/supportsFileattributes.fileattributes': Operation not permitted
cp -a "/tmp/$MYNAME.fileattributes" "/$1" &>>"$LOG_FILE"
local rc=$?
if (( $rc )); then
logItem "cp failed with rc $rc - retryCount"
else
# SC2034: x appears unused. Verify it or export it.
# shellcheck disable=SC2034
read -r attrsT x ownerT groupT r <<< "$(ls -la "/$1/$MYNAME.fileattributes")"
logItem "Remote: $attrsT # $ownerT # $groupT"
attrsT="$(sed 's/+$//' <<< $attrsT)" # delete + sign present if there are any extended attributes
# check fileattributes and ownerships are identical
if [[ "$attrs" == "$attrsT" && "$owner" == "$ownerT" && "$group" == "$groupT" ]]; then
result=0
break
fi
fi
sleep 3s
done
rm /tmp/$MYNAME.fileattributes &>>$LOG_FILE
rm /$1/$MYNAME.fileattributes &>>$LOG_FILE
logExit $result
return $result
}
function disableACLsIfRequested() {
if (( $RSYNC_BACKUP_OPTION_EXCLUDE_ACLS )); then
RSYNC_BACKUP_OPTIONS="$(sed 's/A//' <<< "$RSYNC_BACKUP_OPTIONS")"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_OPTION_ACLS_DISABLED
fi
}
# 0 = yes, no otherwise
function supportsHardlinks() { # directory
logEntry "$1"
local links
local result=1 # no
touch /$1/$MYNAME.hlinkfile &>>$LOG_FILE
local MAXRETRY=3 # retries
local retryCount=$(( MAXRETRY + 1 ))
while (( retryCount-- > 0 && result == 1 )); do
cp -l /$1/$MYNAME.hlinkfile /$1/$MYNAME.hlinklink &>>$LOG_FILE
links=$(ls -la /$1/$MYNAME.hlinkfile | cut -f 2 -d ' ')
logItem "Links: $links"
if (( $links == 2 )); then
result=0
break
fi
done
rm -f /$1/$MYNAME.hlinkfile &>/dev/null
rm -f /$1/$MYNAME.hlinklink &>/dev/null
logExit "$result"
return $result
}
# 0 = yes, no otherwise
function supportsSymlinks() { # directory
logEntry "$1"
local result=1 # no
touch /$1/$MYNAME.slinkfile &>>$LOG_FILE
ln -s /$1/$MYNAME.slinkfile /$1/$MYNAME.slinklink &>>$LOG_FILE
[[ -L /$1/$MYNAME.slinklink ]] && result=0
rm -f /$1/$MYNAME.slinkfile &>/dev/null
rm -f /$1/$MYNAME.slinklink &>/dev/null
logExit "$result"
return $result
}
function isMounted() { # dir
local rc
logEntry "$1"
if [[ -n "$1" ]]; then
#logCommand "cat /proc/mounts"
grep -qs "$1" /proc/mounts
rc=$?
else
rc=1
fi
logExit "$rc"
return $rc
}
function getFsType() { # file or path
logEntry "$1"
local mp
mp="$(findMountPath "$1")"
logItem "Mountpoint: $mp"
local df
df="$(LC_ALL=C df --output=fstype,target | grep -E " ${mp}$" | cut -f 1 -d " ")"
logItem "df -T: $df"
echo $df
logExit "$df"
}
# sfdisk sanity check to make sure last partition does not extend device size
# label: dos
# label-id: 0x3c3f4bdb
# device: /dev/mmcblk0
# unit: sectors
# sector-size: 512
#
# /dev/mmcblk0p1 : start= 8192, size= 524288, type=c
# /dev/mmcblk0p2 : start= 532480, size= 15196160, type=83
function checkSfdiskOK() { # device, e.g. /dev/mmcblk0
logEntry "$1"
local rc
local deviceSize
deviceSize=$(blockdev --getsz $1)
logCommand "sfdisk -d $1"
logItem "DeviceSize: $deviceSize"
# Prefer mapfile or read -a to split command output (or quote to avoid splitting).
#shellcheck disable=SC2207
local sourceValues=( $(awk '/[0-9]+ :/ { v=$4 $6; gsub(","," ",v); printf "%s",v }' <<< "$(sfdisk -d $1)") )
local s=${#sourceValues[@]}
logItem "Size: $s"
if (( $s < 4 )); then
assertionFailed $LINENO "Expected at least 2 partitions on $1"
fi
local sm1=$((s-1))
local sm2=$((s-2))
logItem "${sourceValues[$sm2]} - ${sourceValues[$sm1]}"
local usedSize=$(( ${sourceValues[$sm2]} + ${sourceValues[$sm1]} ))
logItem "usedSize: $usedSize"
local freeSize=$(( $deviceSize - $usedSize ))
logItem "freeSize: $freeSize"
rc=$(( ( $deviceSize / 512 ) > $usedSize ))
logExit $rc
return $rc
}
# check if directory is located on a mounted device
function isPathMounted() {
logEntry "$1"
local path
local rc=1
path=$1
# backup path has to be mount point of the file system (second field fs_file in /etc/fstab) and NOT fs_spec otherwise test algorithm will create endless loop
if [[ "${1:0:1}" == "/" ]]; then
while [[ "$path" != "" ]]; do
logItem "Path: $path"
if mountpoint -q "$path"; then
rc=0
break
fi
path=${path%/*}
done
fi
logExit "$rc"
return $rc
}
# find path of mount of file or directory
function findMountPath() {
logEntry "$1"
local path
path="$1"
# path has to be mount point of the file system (second field fs_file in /etc/fstab) and NOT fs_spec otherwise test algorithm will create endless loop
if [[ "${1:0:1}" == "/" ]]; then
while [[ "$path" != "" ]]; do
logItem "Path: $path"
if mountpoint -q "$path"; then
break
fi
path=${path%/*}
done
fi
echo "$path"
logExit "$path"
return
}
function readConfigParameters() {
logEntry
local attrs
ETC_CONFIG_FILE="/usr/local/etc/${MYNAME}.conf"
HOME_CONFIG_FILE="$CALLING_HOME/.${MYNAME}.conf"
CURRENTDIR_CONFIG_FILE="$CURRENT_DIR/.${MYNAME}.conf"
local file
local files=( "$ETC_CONFIG_FILE" "$HOME_CONFIG_FILE" "$CURRENTDIR_CONFIG_FILE" )
local warnedFiles=""
for file in "${files[@]}"; do
if [[ -e $file ]]; then
attrs="$(stat -c %a $file)"
if (( ( 0$attrs & 077 ) != 0 )); then
if ! grep -q $file <<< "$warnedFiles"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNPROTECTED_PROPERTIESFILE $file
warnedFiles="$warnedFiles $file"
fi
fi
fi
done
# Override default parms with parms in global config file
ETC_CONFIG_FILE_INCLUDED=0
if [ -f "$ETC_CONFIG_FILE" ]; then
set -e
# Can't follow non-constant source. Use a directive to specify location
#shellcheck disable=SC1090
. "$ETC_CONFIG_FILE"
set +e
ETC_CONFIG_FILE_INCLUDED=1
ETC_CONFIG_FILE_VERSION="$(extractVersionFromFile "$ETC_CONFIG_FILE" "$VERSION_CONFIG_VARNAME" )"
logItem "Read config ${ETC_CONFIG_FILE} : ${ETC_CONFIG_FILE_VERSION}$NL$(egrep -v '^\s*$|^#' $ETC_CONFIG_FILE)"
fi
# Override default parms with parms in user config file
HOME_CONFIG_FILE_INCLUDED=0
if [ -f "$HOME_CONFIG_FILE" ]; then
set -e
# Can't follow non-constant source. Use a directive to specify location
#shellcheck disable=SC1090
. "$HOME_CONFIG_FILE"
set +e
HOME_CONFIG_FILE_INCLUDED=1
HOME_CONFIG_FILE_VERSION="$(extractVersionFromFile "$HOME_CONFIG_FILE" "$VERSION_CONFIG_VARNAME" )"
logItem "Read config ${HOME_CONFIG_FILE} : ${HOME_CONFIG_FILE_VERSION}$NL$(egrep -v '^\s*$|^#' $HOME_CONFIG_FILE)"
fi
# Override default parms with parms in current directory config file
CURRENTDIR_CONFIG_FILE_INCLUDED=0
if [[ "$HOME_CONFIG_FILE" != "$CURRENTDIR_CONFIG_FILE" ]]; then
if [ -f "$CURRENTDIR_CONFIG_FILE" ]; then
set -e
# Can't follow non-constant source. Use a directive to specify location
#shellcheck disable=SC1090
. "$CURRENTDIR_CONFIG_FILE"
set +e
CURRENTDIR_CONFIG_FILE_INCLUDED=1
CURRENTDIR_CONFIG_FILE_VERSION="$(extractVersionFromFile "$CURRENTDIR_CONFIG_FILE" "$VERSION_CONFIG_VARNAME" )"
logItem "Read config ${CURRENTDIR_CONFIG_FILE} : ${HOME_CONFIG_FILE_VERSION}$NL$(egrep -v '^\s*$|^#' $CURRENTDIR_CONFIG_FILE)"
fi
fi
logExit
}
function getOSRelease() {
local os_release_file
local os_release
for os_release_file in /etc/os-release /usr/lib/os-release /dev/null ; do
[[ -e "$os_release_file" ]] && break
done
# the prefix "osr_" prevents a lonely "local" with its output below when grep is unsuccessful
unset osr_ID osr_VERSION_ID # unset possible values used from global scope then
#Quote this to prevent word splitting.
#var is referenced but not assigned.
#shellcheck disable=SC2154,SC2046
local osr_$(grep -E "^ID=" "$os_release_file")
#Quote this to prevent word splitting.
#var is referenced but not assigned.
#shellcheck disable=SC2154,SC2046
local osr_$(grep -E "^VERSION_ID=" "$os_release_file")
#var is referenced but not assigned.
#shellcheck disable=SC2154
os_release="${osr_ID}${osr_VERSION_ID}" # e.g. debian12 or even debian"12"
os_release="${os_release//\"/}" # remove any double quotes
echo "${os_release:-unknownOS}" # handle empty result
}
function setupEnvironment() {
logEntry
if (( ! $RESTORE )); then
if (( $ZIP_BACKUP )); then
TAR_COMPRESSION_TOOL="" # zip has higher prio than tarcompressiontool
if [[ $BACKUPTYPE == "$BACKUPTYPE_DD" || $BACKUPTYPE == "$BACKUPTYPE_TAR" ]]; then
BACKUPTYPE=${Z_TYPE_MAPPING[${BACKUPTYPE}]} # tar-> tgz and dd -> ddz
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNKNOWN_BACKUPTYPE_FOR_ZIP $BACKUPTYPE
exitError $RC_PARAMETER_ERROR
fi
fi
if [[ -n "$DYNAMIC_MOUNT" ]]; then
dynamic_mount "$DYNAMIC_MOUNT"
fi
BACKUPFILES_PARTITION_DATE="$HOSTNAME-backup"
# For including the OS release into the name of the backup directory
os_release=$(getOSRelease)
# Note: Sanitize the os_release to be usable as (part of) directory name.
# But don't allow or use "-" or "_" as replacement character!
# Both characters are already used as dividers/markers later on!
# The "~" seems to be okay. Even safer would be "", a.k.a. nothing.
HOSTNAME_OSR="${HOSTNAME}@${os_release//[ \/\\\:\.\-_]/\~}"
if [[ -z "$BACKUP_DIRECTORY_NAME" ]]; then
BACKUPFILE="${HOSTNAME_OSR}-${BACKUPTYPE}-backup-$DATE"
else
BACKUPFILE="${HOSTNAME_OSR}-${BACKUPTYPE}-backup-${DATE}_${BACKUP_DIRECTORY_NAME}"
fi
BACKUPTARGET_ROOT="$BACKUPPATH/$HOSTNAME"
BACKUPTARGET_FINAL_DIR="$BACKUPTARGET_ROOT/$BACKUPFILE" # final directory for backup if backup was successful
BACKUP_TEMP_ROOT_DIR="$BACKUPTARGET_ROOT/tmp" # temporary backup root directory
BACKUPTARGET_TEMP_DIR="$BACKUP_TEMP_ROOT_DIR/$BACKUPFILE" # temporary backup directory for current backup
BACKUPTARGET_DIR="$BACKUPTARGET_TEMP_DIR" # use temporary backup directory, will be renamend to BACKUPTARGET_FINAL_DIR if backup succeeded
local targetExtension="${FILE_EXTENSION[$BACKUPTYPE]}"
if [[ -n $TAR_COMPRESSION_TOOL ]] || (( $ZIP_BACKUP )); then
TAR_COMPRESSION_EXTENSION="${FILE_EXTENSION[$BACKUPTYPE]}" # assume for now -z option is used
if [[ -n $TAR_COMPRESSION_TOOL ]]; then
local i
i=$(getIndexInArray "$TAR_COMPRESSION_TOOL" "${TAR_COMPRESSION_TOOLS_SUPPORTED[@]}")
if (( ! $? )); then
TAR_COMPRESSION_EXTENSION=".tar${TAR_COMPRESSION_EXTENSIONS_SUPPORTED[$i]}"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNSUPPORTED_TAR_COMPRESS_TOOL "$TAR_COMPRESSION_TOOL"
exitError $RC_PARAMETER_ERROR
fi
fi
logItem "Use tar extension $TAR_COMPRESSION_EXTENSION"
targetExtension="$TAR_COMPRESSION_EXTENSION"
fi
BACKUPTARGET_FILE="$BACKUPTARGET_DIR/$BACKUPFILE${targetExtension}"
BACKUPTARGET_FINAL_FILE="$BACKUPTARGET_FINAL_DIR/$BACKUPFILE${targetExtension}"
logItem "BACKUPTARGET_FILE=$BACKUPTARGET_FILE"
logItem "BACKUPTARGET_FINAL_FILE=$BACKUPTARGET_FINAL_FILE"
if [[ ! -d "${BACKUPTARGET_DIR}" ]]; then
if (( $FAKE || ( $SMART_RECYCLE && $SMART_RECYCLE_DRYRUN ) )); then
: # don't create backupdirectory
else
if ! mkdir -p "${BACKUPTARGET_DIR}" &>>"$LOG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_CREATE_DIRECTORY "${BACKUPTARGET_DIR}"
exitError $RC_CREATE_ERROR
fi
fi
fi
BACKUPPATH="$(sed -E 's@/+$@@g' <<< "$BACKUPPATH")"
if [[ ! -d "$BACKUPPATH" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_ARG_NOT_FOUND "$BACKUPPATH"
exitError $RC_MISSING_FILES
fi
if ! touch "$BACKUPPATH/$MYNAME.tmp" &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_WRITE "$BACKUPPATH"
exitError $RC_MISC_ERROR
else
rm -f "$BACKUPPATH/$MYNAME.tmp" &>/dev/null
fi
if (( $FAKE )) && [[ "$LOG_OUTPUT" =~ $LOG_OUTPUT_IS_NO_USERDEFINEDFILE_REGEX ]]; then
LOG_OUTPUT=$LOG_OUTPUT_HOME
logItem "LOG_OUTPUT=$LOG_OUTPUT"
fi
fi
logItem "BACKUPTARGET_DIR: $BACKUPTARGET_DIR"
logItem "BACKUPTARGET_FINAL_DIR: $BACKUPTARGET_FINAL_DIR"
logItem "BACKUPTARGET_FILE: $BACKUPTARGET_FILE"
logItem "BACKUPTARGET_FINAL_FILE: $BACKUPTARGET_FINAL_FILE"
logExit
}
# deploy script on my local PIs
function deployMyself() {
logEntry
for hostLogon in $DEPLOYMENT_HOSTS; do
host=$(cut -d '@' -f 2 <<< $hostLogon)
user=$(cut -d '@' -f 1 <<< $hostLogon)
if [[ -z "$host" || -z "$user" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DEPLOYMENT_PARMS_ERROR
exitError $RC_PARAMETER_ERROR
fi
if ping -c 1 $host &>/dev/null; then
if [[ $user == "root" ]]; then
scp -p $MYSELF $hostLogon:/usr/local/bin > /dev/null
else
scp -p $MYSELF $hostLogon:/home/$user > /dev/null
fi
if [[ $? == 0 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DEPLOYED_HOST "$host" "$user"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DEPLOYMENT_FAILED "$host" "$user"
fi
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DEPLOYING_HOST_OFFLINE "$host"
fi
done
logExit
}
# calculate last used sector
function calcSumSizeFromSFDISK() { # sfdisk file name
logEntry "$1"
local file="$1"
# /dev/mmcblk0p1 : start= 8192, size= 83968, Id= c
# or
# /dev/sdb1 : start= 63, size= 1953520002, type=83
# or
# /dev/nvme0n1p1 : start= 8192, size= 1048576, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, uuid=A1D7A9AA-D2CD-4A61-A709-096793B25EEC, name="bootfs"
local partitionregex="/dev/.*([0-9]+) [^=]+=[^0-9]*([0-9]+)[^=]+=[^0-9]*([0-9]+)[^=]+=[^0-9a-zA-Z\\-]*([0-9a-zA-Z\\-]+)"
local sectorSize=512 # default
[[ -v RESIZE_FSDISK ]] && logCommand "cat $file"
local sectorSize=512 # default
if grep -q "^sector-size:" $file; then
sectorSize=$(cut -f 2 -d ' ' <<< "$sectorSize")
if [[ -z $sectorSize ]]; then
assertionFailed $LINENO "Unable to retrieve sectorsize"
fi
fi
local line
local lineNo=0
local sumSize=0
while IFS="" read line; do
(( lineNo++ ))
if [[ -z $line || $line =~ ^[^#]*# ]]; then
continue
fi
if [[ $line =~ $partitionregex ]]; then
local p=${BASH_REMATCH[1]}
local start=${BASH_REMATCH[2]}
local size=${BASH_REMATCH[3]}
local id=${BASH_REMATCH[4]}
local end
(( end = start + size ))
logItem "Processing $p - Start: $start - Size: $((size*512)) - End: $end - id: $id"
if [[ "$id" != "83" && "$id" != "5" && "$id" != "c" && \
"$id" != "0FC63DAF-8483-4772-8E79-3D69D8477DE4" && \
"$id" != "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7" ]]; then
continue
fi
(( sumSize = (start + size) * sectorSize ))
[[ -v RESIZE_FSDISK ]] && logItem "$p: Start: $start - Size: $((size*512)) : $(bytesToHuman $((size*512))) - SumSize: $sumSize : $(bytesToHuman $sumSize)"
fi
done < $file
if (( sumSize == 0 )) || { [[ "$id" != "83" ]] && [[ "$id" != "0FC63DAF-8483-4772-8E79-3D69D8477DE4" ]]; }; then
assertionFailed $LINENO "No matching last partition found"
fi
echo "$sumSize"
logExit "$sumSize - $(bytesToHuman $sumSize)"
}
# Return oldParttiionSize and newPartitionsize of modified last partition together with partition number
# if last partition is too small to shrink the newPartitionSize == -1
function createResizedSFDisk() { # sfdisk_source_filename targetDeviceSize sfdisk_target_filename -> oldPartitionSize newPartitionSize
logEntry "$@"
local sourceFile="$1"
local targetDeviceSize="$2"
local targetFile="$3"
local oldPartitionSize newPartitionSize
local partitionregex="/dev/.*([0-9]+) [^=]+=[^0-9]*([0-9]+)[^=]+=[^0-9]*([0-9]+)[^=]+=[^0-9a-zA-Z\\-]*([0-9a-zA-Z\\-]+)"
local sectorSize=512
if grep -q "^sector-size:" $sourceFile; then
sectorSize=$(cut -f 2 -d ' ' <<< "$sectorSize")
if [[ -z $sectorSize ]]; then
assertionFailed $LINENO "Unable to retrieve sector size"
fi
fi
logCommand "cat $sourceFile"
local sourceDeviceSize
sourceDeviceSize=$(calcSumSizeFromSFDISK "$sourceFile")
cp "$sourceFile" "$targetFile"
local line newSize diffSize
logItem "sourceDeviceSize: $sourceDeviceSize ($(bytesToHuman $(($sourceDeviceSize)))) targetDeviceSize: $targetDeviceSize ($(bytesToHuman $(($targetDeviceSize))))"
while IFS="" read line; do
(( lineNo++ ))
if [[ -z $line || $line =~ ^[^#]*# ]]; then
continue
fi
if [[ $line =~ $partitionregex ]]; then
local p=${BASH_REMATCH[1]}
local start=${BASH_REMATCH[2]}
local size=${BASH_REMATCH[3]}
local id=${BASH_REMATCH[4]}
local end
(( end = start + size ))
logItem "Processing $p - Start: $start - Size: $((size*512)) - End: $end - id: $id"
if [[ "$id" == "5" ]]; then
logItem "Extended partition detected"
local p5=$p
local size5=$size
continue
fi
if [[ "$id" != "83" ]] && [[ "$id" != "0FC63DAF-8483-4772-8E79-3D69D8477DE4" ]]; then
continue
fi
logItem "--- Processing partition $p ---"
(( oldPartitionSize = size * sectorSize ))
logItem "OldPartitionSize: $oldPartitionSize ($(bytesToHuman $oldPartitionSize)))"
if (( sourceDeviceSize > targetDeviceSize )); then
[[ -v RESIZE_FSDISK ]] && logItem "newSize = ( $size - ( $sourceDeviceSize - $targetDeviceSize ) / $sectorSize )"
(( newSize = ( size - ( sourceDeviceSize - targetDeviceSize ) / sectorSize ) ))
elif (( sourceDeviceSize < targetDeviceSize )); then
[[ -v RESIZE_FSDISK ]] && logItem "newSize = ( $size + ( $targetDeviceSize - $sourceDeviceSize ) / $sectorSize )"
(( newSize = ( size + ( targetDeviceSize - sourceDeviceSize ) / sectorSize ) ))
else
[[ -v RESIZE_FSDISK ]] && logItem "newSize = $size"
(( newSize = size ))
fi
(( diffSize = newSize - size ))
logItem "NewSize: $newSize ($(bytesToHuman $((newSize*512)))) DiffSize: $diffSize ($(bytesToHuman $((diffSize*512))))"
if (( newSize > 0 )); then
[[ -v RESIZE_FSDISK ]] && logItem "(( newPartitionSize = ( $newSize * $sectorSize )))"
(( newPartitionSize = ( newSize * sectorSize )))
else
[[ -v RESIZE_FSDISK ]] && logItem "((newPartitionSize =($newSize-1) * $sectorSize ))" # too small, adjust for 512 division truncation gap
(( newPartitionSize = (newSize-1) * sectorSize )) # too small, adjust for 512 division truncation gap
fi
logItem "NewPartitionSize: $newPartitionSize ($(bytesToHuman $newPartitionSize)))"
logItem "$p - Start: $start - Size: $((size*512)) - id: $id"
logItem "- newSize: $newSize ($(bytesToHuman $(($newSize*512)))) oldPartitionSize: $(bytesToHuman $oldPartitionSize) newPartitionSize ($(bytesToHuman $newPartitionSize))"
fi
done < $sourceFile
if [[ -z $newSize ]]; then
assertionFailed $LINENO "No last Linux partition found which can be resized"
fi
if (( newPartitionSize <= 0 )); then # last partition too small to shrink, return missing size
logItem "Partition too small: Missing $(bytesToHuman $newPartitionSize)"
fi
local sfDiskFileUpdated
if (( newSize > 0 )); then
logItem "Update partition sectorsize to $newSize"
sed -E -i "s/($p :.+size=[ ]*)([0-9]+)/\1${newSize}/" $targetFile
if (( $? )); then
assertionFailed $LINENO "SFDISK sed unsuccessfull"
fi
if [[ -n $p5 ]]; then
local newP5Size
[[ -v RESIZE_FSDISK ]] && logItem "(( newP5Size = $size5 + $diffSize ))"
(( newP5Size = size5 + diffSize ))
logItem "Update extended partition sectorsize to $newP5Size"
sed -E -i "s/(p$p5 :.+size=[ ]*)([0-9]+)/\1${newP5Size}/" $targetFile
fi
sfDiskFileUpdated=true
fi
# Remove the "last-lba" header, it may or may not match the real target GPT partition, depending on its size, and is optional anyway
if sed -E -i '/^last-lba:/d' "$targetFile"; then
if ! grep -q '^last-lba:' "$targetFile"; then
sfDiskFileUpdated=true
fi
fi
if $sfDiskFileUpdated; then
logItem "Updated sfdisk file"
logCommand "cat $targetFile"
fi
logItem "Old: $oldPartitionSize ($(bytesToHuman $oldPartitionSize)) - New: $newPartitionSize $(bytesToHuman $newPartitionSize))"
local ret="$oldPartitionSize $newPartitionSize $p"
echo "$ret"
logExit "$ret"
}
# colorAnnotation
# html vt100
COLOR_WARNING=0
COLOR_ERROR=1
COLOR_TYPE_HTML=0
COLOR_TYPE_VT100=1
COLOR_ON=("" "\e[1;%sm")
COLOR_OFF=("
" "\e[0m")
function colorOn() { # colortype color
local on="${COLOR_ON[$1]}"
local color="${COLOR_CODES[$2]}"
# Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a.
#shellcheck disable=SC2206
color=( $color )
printf -v r "$on" "${color[$1]}"
echo -e -n "$r"
}
function colorOff() { # colortype color
local off="${COLOR_OFF[$1]}"
echo -e -n "$off"
}
# add color annotations for console and/or email
function colorAnnotation() { # colortype text
# logEntry "$1"
colorType="$1"
shift
local line
while IFS= read -r line; do
if [[ "$line" =~ RBK....W ]]; then
colorOn $colorType $COLOR_WARNING
echo -n "$line"
colorOff $colorType
echo
elif [[ "$line" =~ RBK....E ]]; then
colorOn $colorType $COLOR_ERROR
echo -n "$line"
colorOff $colorType
echo
else
if [[ $colorType == "$COLOR_TYPE_HTML" ]]; then
echo "$line
"
else
echo "$line"
fi
fi
done <<< "$@"
#logExit
}
function sendTelegramDocument() { # filename
logEntry "$1"
local rsp curlRC error_code error_description
local curl_params=(
"-F" "chat_id=$TELEGRAM_CHATID"
"-F" "document=@$MSG_FILE"
)
if [[ -n "$TELEGRAM_THREADID" ]]; then
curl_params+=("-F" "message_thread_id=$TELEGRAM_THREADID")
fi
# Build log message
local log_cmd="curl -s -X GET '$TELEGRAM_URL$TELEGRAM_TOKEN/sendDocument' ${curl_params[*]}"
logItem "Telegram curl call: $log_cmd"
# Execute the curl command
rsp="$(curl -s -X GET "$TELEGRAM_URL$TELEGRAM_TOKEN/sendDocument" "${curl_params[@]}")"
curlRC=$?
if (( $curlRC )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TELEGRAM_SEND_LOG_FAILED $curlRC "N/A" "N/A"
else
logItem "Telegram response:${NL}${rsp}"
ok=$(jq .ok <<< "$rsp")
if [[ $ok == "true" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TELEGRAM_SEND_LOG_OK
else
error_code="$(jq .error_code <<< "$rsp")"
error_description="$(jq .description <<< "$rsp")"
logItem "Error sending msg: $rsp"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TELEGRAM_SEND_FAILED "$curlRC" "$error_code" "$error_description"
fi
fi
logExit
}
# Send message, exit
function sendTelegramMessage() { # message html(yes/no)
logEntry "$1"
local rsp error_code error_description
local curl_params=(
"--data-urlencode" "chat_id=$TELEGRAM_CHATID"
"--data-urlencode" "text=$1"
)
if [[ -n "$TELEGRAM_THREADID" ]]; then
curl_params+=("--data-urlencode" "message_thread_id=$TELEGRAM_THREADID")
fi
if [[ -n $2 ]]; then
curl_params+=("--data" "parse_mode=html")
fi
# Build log message
local log_cmd="curl -s -X POST '$TELEGRAM_URL$TELEGRAM_TOKEN/sendMessage' ${curl_params[*]}"
logItem "Telegram curl call: $log_cmd"
# Execute the curl command
rsp="$(curl -s -X POST "$TELEGRAM_URL$TELEGRAM_TOKEN/sendMessage" "${curl_params[@]}")"
local curlRC=$?
if (( $curlRC )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TELEGRAM_SEND_FAILED "$curlRC" "N/A" "N/A"
else
#logItem "Telegram response:${NL}${rsp}"
local ok
ok=$(jq .ok <<< "$rsp")
if [[ $ok == "true" ]]; then
logItem "Message sent"
if [[ -n $2 ]]; then # write message only for html, not for messages
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TELEGRAM_SEND_OK
fi
else
error_code="$(jq .error_code <<< "$rsp")"
error_description="$(jq .description <<< "$rsp")"
logItem "Error sending msg: $rsp"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TELEGRAM_SEND_FAILED "$curlRC" "$error_code" "$error_description"
fi
fi
logExit
}
function sendTelegramm() { # subject
logEntry "$1"
if [[ -n "$TELEGRAM_TOKEN" ]] ; then
if ! which jq &>/dev/null; then # suppress error message when jq is not installed
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_INSTALLED_FILE "jq" "jq"
else
local smiley
if (( $WARNING_MESSAGE_WRITTEN )); then
smiley="$EMOJI_WARNING ${smiley}"
fi
if (( $UPDATE_POSSIBLE )); then
smiley="$EMOJI_UPDATE_POSSIBLE ${smiley}"
fi
if (( $BETA_AVAILABLE )); then
smiley="$EMOJI_BETA_AVAILABLE ${smiley}"
fi
if (( $RESTORETEST_REQUIRED )); then
smiley="$EMOJI_RESTORETEST_REQUIRED ${smiley}"
fi
if (( $VERSION_DEPRECATED )); then
smiley="$EMOJI_VERSION_DEPRECATED ${smiley}"
fi
sendTelegramMessage "${smiley}$1" 1 # html
fi
fi
logExit
}
# M -> add messages inline, m -> attach messages in a file
function sendTelegrammLogMessages() {
logEntry
if [[ -n "$TELEGRAM_TOKEN" ]] ; then
if ! which jq &>/dev/null; then # suppress error message when jq is not installed
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_INSTALLED_FILE "jq" "jq"
else
if [[ "$TELEGRAM_NOTIFICATIONS" =~ $TELEGRAM_NOTIFY_MESSAGES ]]; then
sendTelegramDocument "$MSG_FILE"
elif [[ "$TELEGRAM_NOTIFICATIONS" =~ $TELEGRAM_NOTIFY_MESSAGES2 ]]; then
sendTelegramMessage "$(<$MSG_FILE)" # no html
fi
fi
fi
logExit
}
function sendPushover() { # subject sucess/failure
logEntry "$1"
if [[ -n "$PUSHOVER_TOKEN" ]] ; then
if ! which jq &>/dev/null; then # suppress error message when jq is not installed
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_INSTALLED_FILE "jq" "jq"
else
sendPushoverMessage "$1" "$2"
fi
fi
logExit
}
# Send message, exit
function sendPushoverMessage() { # message 0/1->success/failure sound
logEntry "$1"
local sound prio o msg msgEnd cmd httpCode curlRC ok
if [[ -n $2 && "$2" == "1" ]]; then
sound="$PUSHOVER_SOUND_FAILURE"
prio="$PUSHOVER_PRIORITY_FAILURE"
else
sound="$PUSHOVER_SOUND_SUCCESS"
prio="$PUSHOVER_PRIORITY_SUCCESS"
fi
o=$(mktemp)
msg="$(grep -o "RBK0009.\+" $MSG_FILE)" # assume NOTIFY_START is set
msgEnd="$(grep -o "RBK0010.\+" $MSG_FILE)" # no, script finished
[[ -n "$msgEnd" ]] && msg="$msgEnd"
if [[ "$PUSHOVER_NOTIFICATIONS" =~ $PUSHOVER_NOTIFY_MESSAGES ]]; then
msg="$(tail -c 1024 $MSG_FILE)"
fi
# The = here is literal. To assign by index, use ( [index]=value ) with no spaces. To keep as literal, quote it.
#shellcheck disable=SC2191
cmd=(--form-string message="$1")
cmd+=(--form-string "token=$PUSHOVER_TOKEN" \
--form-string "user=$PUSHOVER_USER"\
--form-string "priority=$prio"\
--form-string "html=1"\
--form-string "message=$msg"\
--form-string "title=$1"\
--form-string "sound=$sound")
[[ -n $PUSHOVER_DEVICE ]] && cmd+=(--form-string "device=$PUSHOVER_DEVICE" )
#shellcheck disable=SC2206
# Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a.
[[ -n $PUSHOVER_ADDITIONAL_OPTIONS ]] && cmd+=( $PUSHOVER_ADDITIONAL_OPTIONS )
logItem "Pushover curl call: ${cmd[*]}"
# This {/} is literal. Check if ; is missing or quote the expression.
#shellcheck disable=SC1083
httpCode="$(curl -s -w %{http_code} -o $o "${cmd[@]}" "$PUSHOVER_URL")"
curlRC=$?
logItem "Pushover response:${NL}$(<$o)"
if (( $curlRC )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PUSHOVER_SEND_FAILED "$curlRC" "$httpCode" "$rsp"
else
ok=$(jq .status "$o")
if [[ $ok == "1" ]]; then
logItem "Message sent"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PUSHOVER_SEND_OK
else
error_description="$(jq .errors "$o" | tr -d '\n[]')"
logItem "Error sending msg: $rsp"
logItem "ErrorDescription: $error_description"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PUSHOVER_SEND_FAILED "$curlRC" "$httpCode" "$error_description"
fi
fi
[[ -n $o ]] && rm $o
logExit
}
function sendSlack() { # subject sucess/failure
logEntry "$1"
if [[ -n "$SLACK_WEBHOOK_URL" ]] ; then
local smiley
if (( $WARNING_MESSAGE_WRITTEN )); then
smiley="$SLACK_EMOJI_WARNING ${smiley}"
fi
if (( $UPDATE_POSSIBLE )); then
smiley="$SLACK_EMOJI_UPDATE_POSSIBLE ${smiley}"
fi
if (( $BETA_AVAILABLE )); then
smiley="$SLACK_EMOJI_BETA_AVAILABLE ${smiley}"
fi
if (( $RESTORETEST_REQUIRED )); then
smiley="$SLACK_EMOJI_RESTORETEST_REQUIRED ${smiley}"
fi
if (( $VERSION_DEPRECATED )); then
smiley="$SLACK_EMOJI_VERSION_DEPRECATED ${smiley}"
fi
sendSlackMessage "${smiley}$1" "$2"
fi
logExit
}
# Send message, exit
function sendSlackMessage() { # message 0/1->success/failure
logEntry "$1"
local msg_json statusMsg error_description msg msgEnd curlRC httpCode
local o
o=$(mktemp)
if [[ -n $2 && "$2" == "1" ]]; then
statusMsg="${SLACK_EMOJI_FAILED}$1"
else
statusMsg="${SLACK_EMOJI_OK}$1"
fi
msg="$(grep -o "RBK0009.\+" $MSG_FILE)" # assume NOTIFY_START is set
msgEnd="$(grep -o "RBK0010.\+" $MSG_FILE)" # no, script finished
[[ -n "$msgEnd" ]] && msg="$msgEnd"
if [[ "$SLACK_NOTIFICATIONS" =~ $SLACK_NOTIFY_MESSAGES ]]; then
msg="$(tail -n 32 $MSG_FILE)"
fi
read -r -d '' msg_json < /tmp/$$
mpack -s "$subject" -d /tmp/$$ "$LOG_FILE" "$EMAIL"
rm /tmp/$$ &>/dev/null
else
local sender=${SENDER_EMAIL:-root@$(hostname -f)}
logItem "Sendig email with s/msmtp"
logItem "echo -e To: $EMAIL${NL}From: $sender${NL}Subject: $subject${NL}${NL}$content | $EMAIL_PROGRAM $msmtp_default $EMAIL"
echo -e "To: $EMAIL${NL}From: $sender${NL}Subject: $subject${NL}${NL}$content" | "$EMAIL_PROGRAM" $msmtp_default "$EMAIL"
rc=$?
logItem "$EMAIL_PROGRAM: RC: $rc"
fi
;;
"$EMAIL_EXTENSION_PROGRAM")
local append=""
(( $APPEND_LOG )) && append="$LOG_FILE"
args=( "$EMAIL" "$subject" "$content" "$EMAIL_PARMS" "$append" )
callExtensions $EMAIL_EXTENSION "${args[@]}"
rc=$?
;;
*) assertionFailed $LINENO "Unsupported email programm $EMAIL_PROGRAM detected"
;;
esac
fi
if (( $rc )) ; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_EMAIL_SEND_FAILED $EMAIL_PROGRAM $rc
fi
fi
logExit
}
function cleanupBackupDirectory() {
logEntry
logItem "Checking $BACKUP_TEMP_ROOT_DIR for clean up"
if [[ -d "$BACKUP_TEMP_ROOT_DIR" ]]; then
if [[ -n $(ls "$BACKUP_TEMP_ROOT_DIR") ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_REMOVING_BACKUP_NO_FILE "$BACKUP_TEMP_ROOT_DIR"
rm -rfd $BACKUP_TEMP_ROOT_DIR &>> "$LOG_FILE"# delete temp backupdir with all incomplete contents
local rmrc=$?
if (( $rmrc != 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_REMOVING_BACKUP_FAILED "$BACKUP_TEMP_ROOT_DIR" "$rmrc"
fi
else
logItem "Removing $BACKUP_TEMP_ROOT_DIR"
rmdir "$BACKUP_TEMP_ROOT_DIR"
fi
logItem "Deleting $BACKUP_TEMP_ROOT_DIR"
rmdir "$BACKUP_TEMP_ROOT_DIR" &>>$LOG_FILE
fi
logExit
}
# return text masqueraded
#
# Algorithm:
#
# if string lenght < 8 return @@@@()
# if string lenght < 16 return first char followed by @* followed by last char
# otherwise return first char followed by @@@@ followed by last char and ()
function masquerade() { # text
[[ -z "$1" ]] && return 1
local t="$1"
local l="${#t}"
local lm="\($l\)"
local m
if (( $l < 8 )); then
echo "$MASQUERADE_STRING$l"
return 0
fi
local s=${t:0:1}
local e=${t: -1}
if (( $l < 16 )); then
m="$(yes ${MASQUERADE_STRING:0:1} | head -n $(($l-2)) | tr -d "\n" )"
echo "$s$m$e"
else
echo "$s$MASQUERADE_STRING$e$lm"
fi
return 0
}
# Escape slashes for masquerading with sed which will fail if slashes are included in the string
function escapeSlashes() { # string
local i r="" p
local s="$1"
for (( i=0; i<${#s}; i++ )); do
p="${s:$i:1}"
if [[ "$p" == "/" ]]; then
r+='\/'
else
r+="$p"
fi
done
echo "$r"
}
function masqueradeSensitiveInfoInLog() {
local xEnabled=0 e
if [ -o xtrace ]; then # disable xtrace
xEnabled=1
set +x
fi
# no logging any more
local m
# receiver email
if [[ -n "$EMAIL" ]]; then
logItem "Masquerading eMail"
m="$(masquerade "$EMAIL")"
e="$(escapeSlashes "$EMAIL")"
sed -i -E "s/$e/${m}/g" $LOG_FILE
fi
# sender email
if [[ -n "$SENDER_EMAIL" ]]; then
logItem "Masquerading sender eMail"
m="$(masquerade "$SENDER_EMAIL")"
e="$(escapeSlashes "$SENDER_EMAIL")"
sed -i -E "s/$e/${m}/g" $LOG_FILE
fi
# email parms usually also contain eMails and passwords
if [[ -n "$EMAIL_PARMS" ]]; then
logItem "Masquerading eMail parameters"
m="$(masquerade "$EMAIL_PARMS")"
e="$(escapeSlashes "$EMAIL_PARMS")"
sed -i -E "s/$e/${m}/" "$LOG_FILE" # may contain passwords
fi
# some mount options
logItem "Masquerading some mount options"
sed -i -E "s/username=[^,]+\,/username=${MASQUERADE_STRING},/" $LOG_FILE # used in cifs mount options
sed -i -E "s/password=[^,]+\,/password=${MASQUERADE_STRING},/" $LOG_FILE
sed -i -E "s/domain=[^,]+\,/domain=${MASQUERADE_STRING},/" $LOG_FILE
# slack
if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
logItem "Masquerading slack webhook"
m="$(masquerade $SLACK_WEBHOOK_URL)"
e="$(escapeSlashes "$SLACK_WEBHOOK_URL")"
sed -i -E "s/${e}/${m}/g" $LOG_FILE
fi
# telegram token, chatid and threadid
if [[ -n "$TELEGRAM_TOKEN" ]]; then
logItem "Masquerading telegram token"
m="$(masquerade $TELEGRAM_TOKEN)"
e="$(escapeSlashes "$TELEGRAM_TOKEN")"
sed -i -E "s/${e}/${m}/g" $LOG_FILE
fi
if [[ -n "$TELEGRAM_CHATID" ]]; then
logItem "Masquerading telegram chatid"
m="$(masquerade $TELEGRAM_CHATID)"
e="$(escapeSlashes "$TELEGRAM_CHATID")"
sed -i -E "s/${e}/${m}/g" $LOG_FILE
fi
if [[ -n "$TELEGRAM_THREADID" ]]; then
logItem "Masquerading telegram threadid"
m="$(masquerade $TELEGRAM_THREADID)"
e="$(escapeSlashes "$TELEGRAM_THREADID")"
sed -i -E "s/${e}/${m}/g" $LOG_FILE
fi
# pushover token and user
if [[ -n "$PUSHOVER_USER" ]]; then
logItem "Masquerading pushover user"
m="$(masquerade $PUSHOVER_USER)"
e="$(escapeSlashes "$PUSHOVER_USER")"
sed -i -E "s/${e}/${m}/g" $LOG_FILE
fi
if [[ -n "$PUSHOVER_TOKEN" ]]; then
logItem "Masquerading pushover token"
m="$(masquerade $PUSHOVER_TOKEN)"
e="$(escapeSlashes "$PUSHOVER_TOKEN")"
sed -i -E "s/${PUSHOVER_TOKEN}/${m}/g" $LOG_FILE
fi
# In home directories usually first names are used
logItem "Masquerading home directory name"
sed -i -E "s/\/home\/([^\\/])+\/(.)/\/home\/@USER@\/\2/g" $LOG_FILE
# hostname may expose domain names
logItem "Masquerading hostname"
e="$(escapeSlashes "$HOSTNAME")"
sed -i -E "s/$e/@HOSTNAME@/g" $LOG_FILE
# any non local IPs used somewhere (mounts et al)
logItem "Masquerading sensitive non local IPs"
masqueradeNonlocalIPs $LOG_FILE
# now delete console color annotation ESC sequences
sed -i 's/\x1b\[1;33m//g' $LOG_FILE
sed -i 's/\x1b\[1;31m//g' $LOG_FILE
sed -i 's/\x1b\[0m//g' $LOG_FILE
if (( $xEnabled )); then # enable xtrace again
set -x
fi
}
# Following regex was optimized by __blackjack__ (https://forum-raspberrypi.de/user/50585-blackjack) from a ChatGPT generated regex ;-)
function masqueradeNonlocalIPs() {
LC_ALL=C perl -i -pe 's{
(\b
(?! # exclude following local ips
(10\.\d{1,3} # 10er net
|172\.(1[6-9]|2[0-9]|3[01]) # 172er net
|192\.168) # 192er net
|169\.254 # link local net
|0\.\d{1,3} # skip any net with leading 0 to ignore raspiBackup release info
(\.\d{1,3}){2}) # followed by two trailing nibbles
# now catch external ips
\d{1,3}\.\d{1,3} # accept 2 leading nibbles
((\.\d{1,3}){2}) # followed by 2 trailing nibbles
\b)
}
{"%%%.%%%$5"}gex' $1
}
function callNotificationExtension() { # rc
logEntry "$1"
local xEnabled=0
if [ -o xtrace ]; then # disable xtrace
xEnabled=1
set +x
fi
callExtensions $NOTIFICATION_BACKUP_EXTENSION $1
local rc=$?
logItem "NotificationExtension rc: $rc"
if (( $xEnabled )); then # enable xtrace again
set -x
fi
logExit $rc
return $rc
}
function cleanupStartup() { # trap
logEntry
rc=${rc:-42} # some failure during startup of script (RT error, option validation, ...)
if [[ $1 == "SIGINT" ]]; then
# ignore CTRL-C now
trap '' SIGINT SIGTERM SIGHUP
rc=$RC_CTRLC
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CTRLC_DETECTED
fi
cleanupTempFiles
logFinish
if (( $LOG_LEVEL == $LOG_DEBUG )); then
masqueradeSensitiveInfoInLog # and now masquerade sensitive details in log file
fi
logExit
if [[ -n "$DYNAMIC_MOUNT" ]] && (( $DYNAMIC_MOUNT_EXECUTED )); then
writeToConsole $MSG_LEVEL_DETAILED $MSG_DYNAMIC_UMOUNT_SCHEDULED "$DYNAMIC_MOUNT"
umount -l $DYNAMIC_MOUNT &>>$LOG_FILE
fi
exit $rc
}
function lockMe() {
logEntry
exlock_now
logExit
}
function unLockMe() {
logEntry
unlock
logExit
}
function cleanup() { # trap
logEntry
rc=${rc:-42} # some failure during startup of script (RT error, option validation, ...)
if [[ $1 == "SIGINT" ]]; then
# ignore CTRL-C now
trap '' SIGINT SIGTERM EXIT
rc=$RC_CTRLC
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CTRLC_DETECTED
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CLEANING_UP
logSystemServices
CLEANUP_RC=$rc
if (( $RESTORE )); then
cleanupRestore $1
else
if (( $PRE_BACKUP_EXTENSION_CALLED )); then
callExtensions $POST_BACKUP_EXTENSION $rc
fi
startServices "noexit"
executeAfterStartServices "noexit"
if [[ $rc -eq 0 ]]; then # don't apply BS if SR dryrun a second time, BS was done already previously
logItem "BACKUPTARGET_DIR: $BACKUPTARGET_DIR"
if [[ -d "${BACKUPTARGET_DIR}" ]]; then # does not exists if raspiBackup7412Test runs
local rc
mv "${BACKUPTARGET_DIR}" "${BACKUPTARGET_FINAL_DIR}"
rc=$?
if (( $rc )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TEMPMOVE_FAILED $rc
CLEANUP_RC=$RC_TEMPMOVE_FAILED
else
if [[ $MSG_LEVEL == "$MSG_LEVEL_MINIMAL" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUPDIR_CREATED "$BACKUPTARGET_FINAL_DIR"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUPDIR_MOVED "$BACKUPTARGET_DIR" "$BACKUPTARGET_FINAL_DIR"
fi
BACKUPTARGET_FILE="$BACKUPTARGET_FINAL_FILE"
fi
else
logItem "??? BACKUPTARGET_DIR: $BACKUPTARGET_DIR not found"
fi
if (( ! $CLEANUP_RC && ! $IS_SNAPSHOT )); then # smartrecycle only if mv succeeded and no snapshot was created
BACKUPTARGET_DIR="$BACKUPTARGET_FINAL_DIR"
if (( \
( $SMART_RECYCLE && ! $SMART_RECYCLE_DRYRUN ) \
|| ! $SMART_RECYCLE \
)); then
applyBackupStrategy
fi
fi
reportOldBackups
fi
fi
cleanupBackup
cleanupTempFiles
finalCommand "$CLEANUP_RC"
logItem "Terminate now with rc $CLEANUP_RC"
if (( $rc != 0 )); then
if (( ! $MAIL_ON_ERROR_ONLY )); then
if (( $WARNING_MESSAGE_WRITTEN )); then
if (( $RESTORE )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_WARNING
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_WARNING
fi
fi
fi
if (( $rc != $RC_CTRLC )); then
if (( $RESTORE )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_FAILED
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_FAILED
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_STOPPED "$HOSTNAME" "$MYSELF" "$VERSION" "$GIT_DATE_ONLY" "$GIT_COMMIT_ONLY" "$(date)" "$rc"
logger -t $MYNAME "Stopped $VERSION ($GIT_COMMIT_ONLY). rc $rc"
if (( ! $RESTORE )); then
if (( $rc != $RC_EMAILPROG_ERROR )); then
msgTitle=$(getMessage $MSG_TITLE_ERROR $HOSTNAME)
sendEMail "$msg" "$msgTitle"
fi
if [[ -n "$TELEGRAM_TOKEN" ]]; then
msg=$(getMessage $MSG_TITLE_ERROR $HOSTNAME)
if [[ "$TELEGRAM_NOTIFICATIONS" =~ $TELEGRAM_NOTIFY_FAILURE ]]; then
sendTelegramm "${EMOJI_FAILED} $msg " # add warning icon to message
sendTelegrammLogMessages
fi
fi
if [[ -n "$PUSHOVER_TOKEN" ]]; then
msg=$(getMessage $MSG_TITLE_ERROR $HOSTNAME)
if [[ "$PUSHOVER_NOTIFICATIONS" =~ $PUSHOVER_NOTIFY_FAILURE_NOTIFY_FAILURE ]]; then
sendPushover "${EMOJI_FAILED} $msg" 1 # add warning icon to message
fi
fi
if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
msg=$(getMessage $MSG_TITLE_ERROR $HOSTNAME)
if [[ "$SLACK_NOTIFICATIONS" =~ $SLACK_NOTIFY_FAILURE_NOTIFY_FAILURE ]]; then
sendSlack "$msg" 1 # add warning icon to message
fi
fi
fi # ! RESTORE
fi
else # success
if (( $RESTORE )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_OK
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_OK
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_STOPPED "$HOSTNAME" "$MYSELF" "$VERSION" "$GIT_DATE_ONLY" "$GIT_COMMIT_ONLY" "$(date)" "$rc"
logger -t $MYNAME "Stopped $VERSION ($GIT_COMMIT_ONLY). rc $rc"
if (( ! $RESTORE )); then
if [[ -n "$TELEGRAM_TOKEN" ]]; then
msg=$(getMessage $MSG_TITLE_OK $HOSTNAME)
if [[ "$TELEGRAM_NOTIFICATIONS" =~ $TELEGRAM_NOTIFY_SUCCESS ]]; then
sendTelegramm "${EMOJI_OK} $msg"
sendTelegrammLogMessages
fi
fi
if [[ -n "$PUSHOVER_TOKEN" ]]; then
msg=$(getMessage $MSG_TITLE_OK $HOSTNAME)
if [[ "$PUSHOVER_NOTIFICATIONS" =~ $PUSHOVER_NOTIFY_SUCCESS ]]; then
sendPushover "${EMOJI_OK} $msg" 0
fi
fi
if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
msg=$(getMessage $MSG_TITLE_OK $HOSTNAME)
if [[ "$SLACK_NOTIFICATIONS" =~ $SLACK_NOTIFY_SUCCESS ]]; then
sendSlack "${EMOJI_OK} $msg" 0
fi
fi
msg=$(getMessage $MSG_TITLE_OK $HOSTNAME)
sendEMail "" "$msg"
fi # ! $RESTORE
fi
if (( $LOG_LEVEL == $LOG_DEBUG )); then
masqueradeSensitiveInfoInLog # and now masquerade sensitive details in log file
fi
logFinish
saveVars
callNotificationExtension $rc
logExit
unLockMe
if [[ -n "$DYNAMIC_MOUNT" ]] && (( $DYNAMIC_MOUNT_EXECUTED )); then
writeToConsole $MSG_LEVEL_DETAILED $MSG_DYNAMIC_UMOUNT_SCHEDULED "$DYNAMIC_MOUNT"
umount -l $DYNAMIC_MOUNT &>>$LOG_FILE
fi
if (( ! $RESTORE && $REBOOT_SYSTEM )); then
shutdown -r +3 # wait some time to allow eMail to be sent
fi
exit $rc
}
function cleanupRestore() { # trap
logEntry
local error=0
logItem "Got trap $1"
logItem "rc: $rc"
rm $MODIFIED_SFDISK &>/dev/null
if (( $PRE_RESTORE_EXTENSION_CALLED )); then
callExtensions $POST_RESTORE_EXTENSION $rc
fi
if [[ -n "$MNT_POINT" ]]; then
umountPartition "$MNT_POINT"
logItem "Deleting dir $MNT_POINT"
rmdir "$MNT_POINT" &>>"$LOG_FILE"
fi
if [[ -d $TEMPORARY_MOUNTPOINT_ROOT ]]; then
logItem "Deleting dir $TEMPORARY_MOUNTPOINT_ROOT"
rmdir "$TEMPORARY_MOUNTPOINT_ROOT" &>>"$LOG_FILE"
fi
if (( ! $PARTITIONBASED_BACKUP )); then
umountPartition "$BOOT_PARTITION"
umountPartition "$ROOT_PARTITION"
fi
cleanupTempFiles
logExit "$rc"
}
function revertScriptVersion() {
logEntry
local currentVersion version sortedVersions existingVersionFiles
#shellcheck disable=SC2207
# (warning): Prefer mapfile or read -a to split command output (or quote to avoid splitting).
local existingVersionFiles=( $(ls $SCRIPT_DIR/$MYNAME.*sh) )
if [[ ! -e "$SCRIPT_DIR/$MYSELF" ]]; then
assertionFailed $LINENO "$SCRIPT_DIR/$MYSELF not found"
fi
currentVersion="$(extractVersionFromFile "$SCRIPT_DIR/$MYSELF" "$VERSION_VARNAME")"
if [[ -z "$currentVersion" ]]; then
assertionFailed $LINENO "Current version not found"
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CURRENT_SCRIPT_VERSION "$currentVersion"
declare -A versionsOfFiles
local version
for versionFile in "${existingVersionFiles[@]}"; do
version="$(extractVersionFromFile "$versionFile" "$VERSION_VARNAME" )"
if [[ -n "$version" ]]; then
if [[ $version != "$currentVersion" ]]; then
versionsOfFiles+=([$version]=$versionFile)
fi
fi
done
for version in "${!versionsOfFiles[@]}"; do
logItem "$version: ${versionsOfFiles[$version]}"
done
#shellcheck disable=SC2207
# (warning): Prefer mapfile or read -a to split command output (or quote to avoid splitting).
local sortedVersions=( $(echo -e "${!versionsOfFiles[@]}" | sed -e 's/ /\n/g' | sort) )
local min=0
local max=$(( ${#sortedVersions[@]} - 1))
if [[ $max == -1 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_PREVIOUS_VERSIONS_AVAILABLE
return
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_AVAILABLE_VERSIONS_HEADER
for version in "${!sortedVersions[@]}"; do
writeToConsole $MSG_LEVEL_MINIMAL $MSG_AVAILABLE_VERSIONS "$version" "${sortedVersions[$version]}"
done
local selection
local valid=0
while (( ! $valid )); do
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SELECT_VERSION "$min" "$max"
read selection
if (( "$selection" < $min || "$selection" > $max )); then
continue
fi
version=${sortedVersions[$selection]}
valid=1
done
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SAVING_ACTUAL_VERSION "$currentVersion" "$MYNAME.$currentVersion.sh"
logItem "mv $SCRIPT_DIR/$MYNAME.sh $SCRIPT_DIR/$MYNAME.$currentVersion.sh"
mv "$SCRIPT_DIR/$MYNAME.sh" "$SCRIPT_DIR/$MYNAME.$currentVersion.sh"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORING_PREVIOUS_VERSION "$version" "$MYNAME.sh"
logItem "cp -a ${versionsOfFiles[$version]} $SCRIPT_DIR/$MYNAME.sh"
cp -a "${versionsOfFiles[$version]}" "$SCRIPT_DIR/$MYNAME.sh"
logExit
}
function isBetaAvailable() {
logEntry
local betaVersion=""
if (( $NEW_PROPERTIES_FILE )); then
betaVersion=${BETA_PROPERTY//\"/}
fi
echo $betaVersion
logExit "$betaVersion"
}
function cleanupBackup() {
logEntry
if (( $PARTITIONBASED_BACKUP )); then
umountPartitions "$TEMPORARY_MOUNTPOINT_ROOT"
fi
cleanupBackupDirectory
cleanupTempFiles
logExit
}
function cleanupTempFiles() {
logEntry
if [[ -f $MYSELF~ ]]; then
logItem "Removing new version $MYSELF~"
rm -f $MYSELF~ &>/dev/null
fi
if [[ -d "$TEMPORARY_MOUNTPOINT_ROOT" ]]; then
logItem "Removing $TEMPORARY_MOUNTPOINT_ROOT"
rmdir -p "$TEMPORARY_MOUNTPOINT_ROOT" &>/dev/null
fi
logExit
}
function checkImportantParameters() {
local ll lla pll org
org="$LOG_LEVEL"
ll="${LOG_LEVEL^^}"
pll="^${POSSIBLE_LOG_LEVELs^^}\$"
if [[ "$ll" =~ $pll ]]; then
lla="$(tr '[:lower:]' '[:upper:]'<<< ${LOG_LEVEL_ARGs[$ll]+abc})"
if [[ "$lla" == "ABC" ]]; then
LOG_LEVEL=${LOG_LEVEL_ARGs[$ll]}
else
LOG_LEVEL=$LOG_DEBUG
writeToConsole $MSG_LEVEL_MINIMAL $MSG_INVALID_LOG_LEVEL "$org"
exitError $RC_PARAMETER_ERROR
fi
fi
if [[ ! "$LOG_LEVEL" =~ $POSSIBLE_LOG_LEVEL_NUMBERs ]]; then
LOG_LEVEL=$LOG_DEBUG
writeToConsole $MSG_LEVEL_MINIMAL $MSG_INVALID_LOG_LEVEL "$org"
exitError $RC_PARAMETER_ERROR
fi
local ml mla mll
org="$MSG_LEVEL"
ml="${MSG_LEVEL^^}"
mll="^${POSSIBLE_MSG_LEVELs^^}\$"
if [[ "$ml" =~ $mll ]]; then
mla="$(tr '[:lower:]' '[:upper:]'<<< ${MSG_LEVEL_ARGs[$ml]+abc})"
if [[ "$mla" == "ABC" ]]; then
MSG_LEVEL=${MSG_LEVEL_ARGs[$ml]}
else
MSG_LEVEL=$MSG_LEVEL_DETAILED
writeToConsole $MSG_LEVEL_MINIMAL $MSG_INVALID_MSG_LEVEL "$org"
exitError $RC_PARAMETER_ERROR
fi
fi
if [[ ! "$MSG_LEVEL" =~ $POSSIBLE_MSG_LEVEL_NUMBERs ]]; then
MSG_LEVEL=$MSG_LEVEL_DETAILED
writeToConsole $MSG_LEVEL_MINIMAL $MSG_INVALID_MSG_LEVEL "$org"
exitError $RC_PARAMETER_ERROR
fi
local lo loa plo
lo="${LOG_OUTPUT^^}"
plo="^${POSSIBLE_LOG_OUTPUTs^^}\$"
if [[ "$lo" =~ $plo ]]; then
loa="$(tr '[:lower:]' '[:upper:]'<<< ${LOG_OUTPUT_ARGs[$lo]+abc})"
if [[ "$loa" == "ABC" ]]; then
LOG_OUTPUT=${LOG_OUTPUT_ARGs[$lo]}
logItem "LOG_OUTPUT=$LOG_OUTPUT"
fi
fi
if [[ ! "$LOG_OUTPUT" =~ $POSSIBLE_LOG_OUTPUT_NUMBERs ]]; then
if [[ ${LOG_OUTPUT:0:1} != "/" ]]; then
LOG_OUTPUT="$CURRENT_DIR/$LOG_OUTPUT"
logItem "LOG_OUTPUT=$LOG_OUTPUT"
fi
if ! touch "$LOG_OUTPUT" &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_CREATE_FILE "$LOG_OUTPUT"
LOG_OUTPUT=$LOG_OUTPUT_HOME
logItem "LOG_OUTPUT=$LOG_OUTPUT"
exitError $RC_PARAMETER_ERROR
fi
fi
if ! containsElement "${LANGUAGE}" "${SUPPORTED_LANGUAGES[@]}"; then
local l=$LANGUAGE
LANGUAGE=$FALLBACK_LANGUAGE
writeToConsole $MSG_LEVEL_MINIMAL $MSG_LANGUAGE_NOT_SUPPORTED "$l"
fi
}
function createLinks() { # backuptargetroot extension newfile
logEntry "$1 $2 $3"
local file rc possibleLinkTargetDirectory possibleLinkTarget
possibleLinkTargetDirectory=$(ls -d $1/${HOSTNAME_OSR}-$BACKUPTYPE-backup-* 2>/dev/null | tail -2 | head -1)
if [[ -z "$possibleLinkTargetDirectory" || "$possibleLinkTargetDirectory" == "$BACKUPTARGET_DIR" ]]; then
logItem "No possible link target directory found"
return
fi
logItem "PossibleLinkTargetDirectory: $possibleLinkTargetDirectory"
possibleLinkTarget=$(find "$possibleLinkTargetDirectory" -maxdepth 1 -name "$HOSTNAME-backup.$2")
logItem "Possible link target: $possibleLinkTarget"
if [[ -z $possibleLinkTarget ]]; then
logItem "No possible link target found"
return
fi
if cmp -s $3 $possibleLinkTarget; then
rm $3 &>/dev/null
writeToConsole $MSG_LEVEL_DETAILED $MSG_REPLACING_FILE_BY_HARDLINK "$3" "$possibleLinkTarget"
cp -l "$possibleLinkTarget" "$3" &>/dev/null
rc=$?
if [[ $rc != 0 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_HARDLINK_ERROR "$1" "$rc"
exitError $RC_LINK_FILE_FAILED
fi
local links
links="$(stat -c %h -- "$3")"
if (( links < 2 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_HARDLINK_ERROR "$1" "$rc"
exitError $RC_LINK_FILE_FAILED
fi
fi
logExit
}
function bootPartitionBackup() {
logEntry
local p rc cmd
logItem "Starting boot partition backup..."
if (( ! $FAKE && ! $EXCLUDE_DD && ! $SHARED_BOOT_DIRECTORY )); then
local ext=$BOOT_DD_EXT
(( $TAR_BOOT_PARTITION_ENABLED )) && ext=$BOOT_TAR_EXT
if [[ ! -e "$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.$ext" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CREATING_BOOT_BACKUP "$BACKUPTARGET_FINAL_DIR/$BACKUPFILES_PARTITION_DATE.$ext"
if (( $TAR_BOOT_PARTITION_ENABLED )); then
local bootMountpoint
[[ -d /boot/firmware ]] && bootMountpoint="/boot/firmware" || bootMountpoint="/boot"
cmd="cd $bootMountpoint; tar $TAR_BACKUP_OPTIONS -f \"$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.$ext\" ."
executeTar "$cmd"
else
cmd="dd if=/dev/${BOOT_PARTITION_PREFIX}1 of=\"$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.$ext\" bs=$DD_BLOCKSIZE"
executeDD "$cmd"
fi
rc=$?
if [ $rc != 0 ]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_IMG_BOOT_BACKUP_FAILED "$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.$ext" "$rc"
exitError $RC_DD_IMG_FAILED
fi
if (( ! $TAR_BOOT_PARTITION_ENABLED )); then
local loopDev
loopDev="$(losetup -f)"
logItem "Loop device: $loopDev"
losetup -P $loopDev $BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.$ext &>>$LOG_FILE
rc=$?
logItem "losetup rc: $rc"
if (( $rc != 0 )); then
losetup -d $loopDev &>>$LOG_FILE
logItem "Mount of boot partition backup file failed with rc $rc" # silently ignore losetup error
else
writeToConsole $MSG_LEVEL_DETAILED $MSG_IMG_BOOT_CHECK_STARTED
fsck -fp $loopDev &>>$LOG_FILE
rc=$?
logItem "fsck rc: $rc"
losetup -d $loopDev &>>$LOG_FILE
if (( $rc > 1 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_IMG_BOOT_FSCHECK_FAILED "$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.$ext" "$rc"
exitError $RC_DD_IMG_FAILED
fi
fi
fi
if (( $LINK_BOOTPARTITIONFILES )); then
createLinks "$BACKUPTARGET_ROOT" $ext "$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.$ext"
fi
else
logItem "Found existing backup of boot partition $BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.$ext ..."
writeToConsole $MSG_LEVEL_DETAILED $MSG_EXISTING_BOOT_BACKUP "$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.$ext"
fi
if [[ ! -e "$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.sfdisk" ]]; then
writeToConsole $MSG_LEVEL_DETAILED $MSG_CREATING_PARTITION_BACKUP "$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.sfdisk"
local stripMultiple
if (( $IGNORE_ADDITIONAL_PARTITIONS )); then
logItem "Stripping partitions > 2"
stripMultiple='| grep -v -E "[3-9] :"'
fi
eval "sfdisk -d $BOOT_DEVICENAME $stripMultiple" > "$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.sfdisk" 2>>$LOG_FILE
local rc=$?
if [ $rc != 0 ]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_COLLECT_PARTITIONINFO "sfdisk" "$rc"
exitError $RC_COLLECT_PARTITIONS_FAILED
fi
logCommand "cat $BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.sfdisk"
if (( $LINK_BOOTPARTITIONFILES )); then
createLinks "$BACKUPTARGET_ROOT" "sfdisk" "$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.sfdisk"
fi
else
logItem "Found existing backup of partition layout $BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.sfdisk ..."
writeToConsole $MSG_LEVEL_DETAILED $MSG_EXISTING_PARTITION_BACKUP "$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.sfdisk"
fi
if [[ ! -e "$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.mbr" ]]; then
writeToConsole $MSG_LEVEL_DETAILED $MSG_CREATING_MBR_BACKUP "$BACKUPTARGET_FINAL_DIR/$BACKUPFILES_PARTITION_DATE.mbr"
cmd="dd if=$BOOT_DEVICENAME of=\"$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.mbr\" bs=512 count=1"
executeDD "$cmd"
local rc=$?
if [ $rc != 0 ]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_IMG_DD_FAILED ".mbr" "$rc"
exitError $RC_COLLECT_PARTITIONS_FAILED
fi
if (( $LINK_BOOTPARTITIONFILES )); then
createLinks "$BACKUPTARGET_ROOT" "mbr" "$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.mbr"
fi
else
logItem "Found existing backup of master boot record $BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.mbr ..."
writeToConsole $MSG_LEVEL_DETAILED $MSG_EXISTING_MBR_BACKUP "$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.mbr"
fi
fi
logItem "Finished boot partition backup..."
logExit
}
function partitionLayoutBackup() {
logEntry
local p rc
writeToConsole $MSG_LEVEL_DETAILED $MSG_BACKUP_CREATING_PARTITION_INFO
SF_FILE="$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.sfdisk"
MBR_FILE="$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.mbr"
BLKID_FILE="$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.blkid"
PARTED_FILE="$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.parted"
FDISK_FILE="$BACKUPTARGET_DIR/$BACKUPFILES_PARTITION_DATE.fdisk"
writeToConsole $MSG_LEVEL_DETAILED $MSG_CREATING_PARTITION_BACKUP "$SF_FILE"
sfdisk -d $BOOT_DEVICENAME > "$SF_FILE" 2>>$LOG_FILE
local rc=$?
if [ $rc != 0 ]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_COLLECT_PARTITIONINFO "sfdisk" "$rc"
exitError $RC_COLLECT_PARTITIONS_FAILED
fi
logItem "sfdisk"
logItem "$(<"$SF_FILE")"
writeToConsole $MSG_LEVEL_DETAILED $MSG_CREATING_PARTITION_BACKUP "$BLKID_FILE"
logItem "Saving blkid"
blkid > "$BLKID_FILE"
local rc=$?
if [ $rc != 0 ]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_COLLECT_PARTITIONINFO "blkid" "$rc"
exitError $RC_COLLECT_PARTITIONS_FAILED
fi
logItem "blkid"
logItem "$(<"$BLKID_FILE")"
writeToConsole $MSG_LEVEL_DETAILED $MSG_CREATING_PARTITION_BACKUP "$PARTED_FILE"
logItem "Saving parted"
parted -m $BOOT_DEVICENAME print > "$PARTED_FILE"
local rc=$?
if [ $rc != 0 ]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_COLLECT_PARTITIONINFO "parted" "$rc"
exitError $RC_COLLECT_PARTITIONS_FAILED
fi
logItem "parted"
logItem "$(<"$PARTED_FILE")"
writeToConsole $MSG_LEVEL_DETAILED $MSG_CREATING_PARTITION_BACKUP "$FDISK_FILE"
logItem "Saving fdisk"
fdisk -l $BOOT_DEVICENAME > "$FDISK_FILE"
local rc=$?
if [ $rc != 0 ] ; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_COLLECT_PARTITIONINFO "fdisk" "$rc"
exitError $RC_COLLECT_PARTITIONS_FAILED
fi
logItem "fdisk"
logItem "$(<"$FDISK_FILE")"
writeToConsole $MSG_LEVEL_DETAILED $MSG_CREATING_MBR_BACKUP "$MBR_FILE"
cmd="dd if=$BOOT_DEVICENAME of=$MBR_FILE bs=512 count=1"
executeDD "$cmd"
rc=$?
if [ $rc != 0 ]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_IMG_DD_FAILED ".mbr" "$rc"
exitError $RC_COLLECT_PARTITIONS_FAILED
fi
logExit
}
function backupDD() {
logEntry
local cmd verbose partition
(( $VERBOSE )) && verbose="-v" || verbose=""
local progressFlag=""
(( $PROGRESS && $INTERACTIVE )) && progressFlag="status=progress"
if (( $PARTITIONBASED_BACKUP )); then
partition="${BOOT_DEVICENAME}p$1"
partitionName="${BOOT_PARTITION_PREFIX}$1"
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_DDZ" ]]; then
cmd="dd if=$partition bs=$DD_BLOCKSIZE $progressFlag $DD_PARMS | gzip ${verbose} > \"${BACKUPTARGET_DIR}/$partitionName${FILE_EXTENSION[$BACKUPTYPE]}\""
else
cmd="dd if=$partition bs=$DD_BLOCKSIZE $progressFlag $DD_PARMS of=\"${BACKUPTARGET_DIR}/$partitionName${FILE_EXTENSION[$BACKUPTYPE]}\""
fi
else
if (( ! $DD_BACKUP_SAVE_USED_PARTITIONS_ONLY )); then
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_DDZ" ]]; then
cmd="dd if=$BOOT_DEVICENAME bs=$DD_BLOCKSIZE $progressFlag $DD_PARMS | gzip ${verbose} > \"$BACKUPTARGET_FILE\""
else
cmd="dd if=$BOOT_DEVICENAME bs=$DD_BLOCKSIZE $progressFlag $DD_PARMS of=\"$BACKUPTARGET_FILE\""
fi
else
local lastByte spaceUsedHuman sdcardSize sdcardSizeHuman count blocksize
logCommand "fdisk -l $BOOT_DEVICENAME"
lastByte=$(lastUsedPartitionByte $BOOT_DEVICENAME)
if (( lastByte == 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TRUNCATING_ERROR "$sdcardSizeHuman" "$spaceUsedHuman"
exitError $RC_MISC_ERROR
fi
spaceUsedHuman=$(bytesToHuman $lastByte)
sdcardSize=$(blockdev --getsize64 $BOOT_DEVICENAME)
sdcardSizeHuman=$(bytesToHuman $sdcardSize)
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TRUNCATING_TO_USED_PARTITIONS_ONLY "$sdcardSizeHuman" "$spaceUsedHuman"
(( blocksize=1024*1024 )) # 1MB hard coded
(( count = lastByte / blocksize ))
logItem "Count: $count"
if (( count * blocksize < lastByte )); then
(( count++ ))
logItem "Updated count: $count"
fi
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_DDZ" ]]; then
if (( $PROGRESS && $INTERACTIVE )); then
cmd="dd if=$BOOT_DEVICENAME bs=$blocksize count=$count | pv -fs $(fdisk -l "$BOOT_DEVICENAME" | grep "Disk.*$BOOT_DEVICENAME" | cut -d ' ' -f 5) | gzip ${verbose} > \"$BACKUPTARGET_FILE\""
else
cmd="dd if=$BOOT_DEVICENAME bs=$blocksize count=$count | gzip ${verbose} > \"$BACKUPTARGET_FILE\""
fi
else
if (( $PROGRESS && $INTERACTIVE )); then
cmd="dd if=$BOOT_DEVICENAME bs=$blocksize count=$count | pv -fs $(fdisk -l "$BOOT_DEVICENAME" | grep "Disk.*$BOOT_DEVICENAME" | cut -d ' ' -f 5) | dd of=\"$BACKUPTARGET_FILE\""
else
cmd="dd if=$BOOT_DEVICENAME bs=$blocksize count=$count of=\"$BACKUPTARGET_FILE\""
fi
fi
fi
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_STARTED "$BACKUPTYPE"
if (( ! $EXCLUDE_DD )); then
if (( ! $FAKE)); then
executeDD "$cmd"
rc=$?
else
rc=0
fi
fi
logExit "$rc"
return $rc
}
function backupTar() {
local verbose="" zip="" cmd partition source target devroot sourceDir journalExclude="" extension
logEntry
(( $VERBOSE )) && verbose="-v"
if [[ -n "$TAR_COMPRESSION_TOOL" ]]; then
zip="-I\"${TAR_COMPRESSION_TOOL} $TAR_COMPRESSION_TOOL_OPTIONS\""
extension="$TAR_COMPRESSION_EXTENSION"
else
extension="${FILE_EXTENSION[$BACKUPTYPE]}"
[[ "$BACKUPTYPE" == "$BACKUPTYPE_TGZ" ]] && zip="-z"
fi
if (( $PARTITIONBASED_BACKUP )); then
partition="${BOOT_PARTITION_PREFIX}$1"
source="."
devroot="."
sourceDir="$TEMPORARY_MOUNTPOINT_ROOT/$partition"
target="${BACKUPTARGET_DIR}/${partition}${extension}"
else
bootPartitionBackup
source="/"
devroot=""
target="\"$BACKUPTARGET_FILE\""
fi
if [[ -e /$PERSISTENT_JOURNAL ]]; then
logItem "Excluding /$PERSISTENT_JOURNAL"
journalExclude="--exclude=$devroot/${PERSISTENT_JOURNAL}"
fi
writeToConsole $MSG_LEVEL_DETAILED $MSG_MAIN_BACKUP_PROGRESSING $BACKUPTYPE "${target//\\/}"
local log_file="${LOG_FILE/\//}" # remove leading /
local msg_file="${MSG_FILE/\//}" # remove leading /
cmd="tar \
$TAR_BACKUP_OPTIONS \
$TAR_BACKUP_ADDITIONAL_OPTIONS \
${zip} \
${verbose} \
-f $target \
--warning=no-xdev \
--numeric-owner \
--exclude=\"$BACKUPPATH_PARAMETER/*\" \
--exclude=\"$devroot/$log_file\" \
--exclude=\"$devroot/$msg_file\" \
--exclude='.gvfs' \
--exclude=$devroot/proc/* \
--exclude=$devroot/lost+found/* \
--exclude=$devroot/sys/* \
--exclude=$devroot/dev/* \
--exclude=$devroot/tmp/* \
--exclude=$devroot/var/swap \
--exclude=$devroot/swapfile \
--exclude=$devroot/run/* \
--exclude=$devroot/media/* \
$journalExclude \
$EXCLUDE_LIST \
$source"
if (( $PARTITIONBASED_BACKUP )); then
if ! pushd $sourceDir &>>"$LOG_FILE"; then
assertionFailed $LINENO "push to $sourceDir failed"
fi
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_STARTED "$BACKUPTYPE"
if [[ -n $TAR_COMPRESSION_TOOL ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TAR_COMPRESS_TOOL_USED "$TAR_COMPRESSION_TOOL"
fi
if (( ! $FAKE )); then
executeTar "${cmd}" "$TAR_IGNORE_ERRORS"
rc=$?
fi
if (( $PARTITIONBASED_BACKUP )); then
if ! popd &>>"$LOG_FILE"; then
assertionFailed $LINENO "pop failed"
fi
fi
logExit "$rc"
return $rc
}
function waitForPartitionDefsChanged {
logEntry
sync
sleep 3
logItem "--- partprobe ---"
partprobe -s &>>$LOG_FILE
sleep 3
logItem "--- udevadm ---"
udevadm settle &>>$LOG_FILE
logExit
}
function updateUUIDs() {
logEntry
if (( $UPDATE_UUIDS )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UPDATING_UUIDS
logItem "Old blkid"
logCommand "blkid"
updatePartUUID
updateUUID
logItem "blkid after UUID update${NL}$(blkid)"
fi
logExit
}
function updatePartUUID() {
logEntry
local newUUID
if (( $UPDATE_UUIDS )); then
newUUID=$(od -A n -t x -N 4 /dev/urandom | tr -d " ")
writeToConsole $MSG_LEVEL_DETAILED $MSG_CREATING_UUID "PARTUUID" "$newUUID" "$RESTORE_DEVICE"
echo -ne "x\ni\n0x$newUUID\nr\nw\nq\n" | fdisk "$RESTORE_DEVICE" &>> "$LOG_FILE"
waitForPartitionDefsChanged
fi
logExit
}
function updateUUID() {
logEntry
if (( $UPDATE_UUIDS )); then
logItem "BOOT_PARTITION: $BOOT_PARTITION - ROOT_PARTITION: $ROOT_PARTITION"
local newUUID
if (( ! $SHARED_BOOT_DIRECTORY )); then
newUUID="$(od -A n -t x -N 4 /dev/urandom | tr -d " " | sed -r 's/(.{4})/\1-/')"
newUUID="${newUUID^^*}"
writeToConsole $MSG_LEVEL_DETAILED $MSG_CREATING_UUID "UUID" "$newUUID" "$BOOT_PARTITION"
printf "\x${newUUID:7:2}\x${newUUID:5:2}\x${newUUID:2:2}\x${newUUID:0:2}" \
| dd bs=1 seek=67 count=4 conv=notrunc of=$BOOT_PARTITION &>>"$LOG_FILE" # 39 for fat16, 67 for fat32
waitForPartitionDefsChanged
fi
newUUID="$(> "$LOG_FILE"
tune2fs -U "$newUUID" $ROOT_PARTITION &>>"$LOG_FILE"
waitForPartitionDefsChanged
fi
logExit
}
function collectPartitionsInBackup() { # lastBackupDir
logEntry "$1"
local result
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
#shellcheck disable=SC2010
result=$(ls -l "$1" | grep "^d" | grep -Eo "[0-9]+$")
echo "$result"
logExit "$result"
}
function checkIfAllPreviousPartitionsAreIncludedInBackup() { # lastBackupDir
logEntry "$1"
local rc partitionsInBackup partitionsToBackup
# Prefer mapfile or read -a to split command output (or quote to avoid splitting).
#shellcheck disable=SC2207
partitionsInBackup=( $(collectAvailableBackupPartitions "$1") )
# Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a.
#shellcheck disable=SC2206
partitionsToBackup=( ${PARTITIONS_TO_BACKUP[@]} )
local missingPartition=()
for (( i=0; i<${#partitionsInBackup[@]}; i++ )); do
if ! containsElement "${partitionsInBackup[i]}" "${partitionsToBackup[@]}"; then
logItem "Missing ${partitionsInBackup[i]}"
missingPartition+=( "${partitionsInBackup[i]}" )
fi
done
echo "${missingPartition[@]}"
(( ${#missingPartition[@]} == 0 ))
rc=$?
logExit "$rc - ${missingPartition[*]}"
return $rc
}
function backupRsync() { # partition number (for partition based backup)
local verbose partition target source cmd cmdParms excludeMeta journalExclude
logEntry
(( $PROGRESS )) && VERBOSE=0
verbose="--info=NAME0"
(( $VERBOSE )) && verbose="-v"
if [[ -e "/$PERSISTENT_JOURNAL" ]]; then
logItem "Excluding /$PERSISTENT_JOURNAL"
journalExclude="--exclude=/${PERSISTENT_JOURNAL}"
fi
logCommand "ls $BACKUPTARGET_ROOT"
if (( $PARTITIONBASED_BACKUP )); then
partition="${BOOT_PARTITION_PREFIX}$1"
target="${BACKUPTARGET_DIR}/$partition"
source="$TEMPORARY_MOUNTPOINT_ROOT/$partition/"
else
target="\"${BACKUPTARGET_DIR}\""
source="/"
bootPartitionBackup
excludeMeta="--exclude=/$BACKUPFILES_PARTITION_DATE.img --exclude=/$BACKUPFILES_PARTITION_DATE.tmg --exclude=/$BACKUPFILES_PARTITION_DATE.sfdisk --exclude=/$BACKUPFILES_PARTITION_DATE.blkid --exclude=/$BACKUPFILES_PARTITION_DATE.fdisk --exclude=/$BACKUPFILES_PARTITION_DATE.parted --exclude=/$BACKUPFILES_PARTITION_DATE.mbr --exclude=/$MYNAME.log --exclude=/$MYNAME.msg"
fi
lastBackupDir=$(find "$BACKUPTARGET_ROOT" -maxdepth 1 -type d -name "${HOSTNAME_OSR}-$BACKUPTYPE-*" ! -name $BACKUPFILE 2>>/dev/null | sort | tail -n 1)
logItem "lastBackupDir: $lastBackupDir"
if [[ -n "$lastBackupDir" ]]; then
if [[ -d "$lastBackupDir/$partition" ]]; then
lastBackupDir="$lastBackupDir/${partition}"
else
lastBackupDir=""
fi
fi
local LINK_DEST=""
if [[ -n "$lastBackupDir" ]]; then
LINK_DEST="--link-dest=\"$lastBackupDir\""
logItem "LinkDest: $LINK_DEST"
writeToConsole $MSG_LEVEL_DETAILED $MSG_HARDLINK_DIRECTORY_USED "$lastBackupDir"
fi
writeToConsole $MSG_LEVEL_DETAILED $MSG_MAIN_BACKUP_PROGRESSING $BACKUPTYPE "${target//\\/}"
local log_file="${LOG_FILE/\//}" # remove leading /
local msg_file="${MSG_FILE/\//}" # remove leading /
cmdParms="--exclude=\"$BACKUPPATH_PARAMETER/*\" \
--exclude=\"$log_file\" \
--exclude=\"$msg_file\" \
--exclude='.gvfs' \
--exclude=/proc/* \
--exclude=/lost+found/* \
--exclude=/sys/* \
--exclude=/dev/* \
--exclude=/var/swap \
--exclude=/swapfile \
--exclude=/tmp/* \
--exclude=/run/* \
--exclude=/media/* \
$journalExclude \
$excludeMeta \
$EXCLUDE_LIST \
$LINK_DEST \
--numeric-ids \
$RSYNC_BACKUP_OPTIONS \
$RSYNC_BACKUP_ADDITIONAL_OPTIONS \
$verbose \
$source \
$target \
"
if (( $PROGRESS && $INTERACTIVE )); then
cmd="rsync --info=progress2 $cmdParms"
else
cmd="rsync $cmdParms"
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_STARTED "$BACKUPTYPE"
if (( ! $FAKE )); then
executeRsync "$cmd" "$RSYNC_IGNORE_ERRORS"
rc=$?
fi
logExit "$rc"
}
function areDevicesUnique() {
logEntry
local -A UUID
local -A PARTUUID
local line
local unique=0
local uuid uuidsub partuuid
logCommand "blkid -o udev"
while read line; do
if grep -q ID_FS_UUID= <<< "$line"; then
uuid="$(cut -f2 -d= <<< "$line")"
fi
if grep -q ID_FS_UUID_SUB= <<< "$line"; then
uuidsub="$(cut -f2 -d= <<< "$line")"
uuid="${uuid}_${uuidsub}"
fi
if grep -q ID_FS_PARTUUID= <<< "$line"; then
partuuid="$(cut -f2 -d= <<< "$line")"
if [[ ${PARTUUID[$partuuid]}+abc != "+abc" ]]; then
logItem "PARTUUID $partuuid is not unique"
unique=1
else
PARTUUID[$partuuid]=1
fi
fi
if [[ -z "$line" ]]; then # groups are separated by empty lines thus one group parsed now
if [[ -n $uuid ]]; then
if [[ ${UUID[$uuid]}+abc != "+abc" ]]; then
logItem "UUID $uuid is not unique"
unique=1
else
UUID[$uuid]=1
fi
fi
uuid=""
uuidsub=""
fi
done < <(blkid -o udev)
if [[ -n $uuid && ${UUID[$uuid]}+abc != "+abc" ]]; then # check last group in output with no trailing empty line
logItem "UUID $uuid is not unique"
unique=1
fi
logExit $unique
return $unique
}
function logSystemDiskState() {
logEntry
logCommand "blkid"
logCommand "fdisk -l"
logCommand "mount"
logCommand "df -h -l"
logExit
}
function partitionRestoredeviceIfRequested() {
logEntry
if (( $SKIP_SFDISK )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SKIP_CREATING_PARTITIONS
if ! askYesNo; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_ABORTED
exitError $RC_RESTORE_FAILED
fi
if (( $NO_YES_QUESTION )); then
echo "Y"
fi
if (( $SKIP_FORMAT )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SKIP_FORMATING
if ! askYesNo; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_ABORTED
exitError $RC_RESTORE_FAILED
fi
if (( $NO_YES_QUESTION )); then
echo "Y"
fi
else
if [[ "${PARTITIONS_TO_RESTORE}" == "$PARTITIONS_TO_BACKUP_ALL" ]]; then
# Prefer mapfile or read -a to split command output (or quote to avoid splitting).
#shellcheck disable=SC2207
local partitions=( $(collectPartitionsInBackup $RESTOREFILE) )
local partitionsString="${partitions[*]}"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PARTITIONS_FORMATED "\"$partitionsString\""
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PARTITIONS_FORMATED "\"$PARTITIONS_TO_RESTORE\""
fi
if ! askYesNo; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_ABORTED
exitError $RC_RESTORE_FAILED
fi
if (( $NO_YES_QUESTION )); then
echo "Y"
fi
fi
elif [[ "$BACKUPTYPE" != "$BACKUPTYPE_DD" && "$BACKUPTYPE" != "$BACKUPTYPE_DDZ" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CREATING_PARTITIONS $RESTORE_DEVICE
if ! askYesNo; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_ABORTED
exitError $RC_RESTORE_FAILED
fi
if (( $NO_YES_QUESTION )); then
echo "Y"
fi
if (( $FORCE_SFDISK )); then
local tmpSF
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FORCING_CREATING_PARTITIONS
sfdisk -f "$RESTORE_DEVICE" < "$SF_FILE" &>>"$LOG_FILE"
rc=$?
if (( $rc )); then
if (( $rc == 1 )); then
tmpSF="$(basename $SF_FILE)"
cp "$SF_FILE" "/tmp/$tmpSF"
sed -i 's/sector-size/d' "/tmp/$tmpSF"
sfdisk -f "$RESTORE_DEVICE" < "/tmp/$tmpSF" &>>"$LOG_FILE"
rc=$?
rm "/tmp/$tmpSF"
fi
fi
if (( $rc )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_CREATE_PARTITIONS $rc "sfdisk error"
exitError $RC_CREATE_PARTITIONS_FAILED
fi
waitForPartitionDefsChanged
else
if (( ! $ROOT_PARTITION_DEFINED )) && (( $RESIZE_ROOTFS )); then
local sourceSDSize targetSDSize
sourceSDSize=$(calcSumSizeFromSFDISK "$SF_FILE")
local partitionLabel totalSize
partitionLabel=$(awk -F': ' '/^label:/ {print $2}' "$SF_FILE")
totalSize=$(blockdev --getsize64 "$RESTORE_DEVICE")
if [[ "$partitionLabel" == "gpt" ]]; then
# Subtract 33 sectors for GPT metadata at the end of the disk as these are unusable (for example, see https://wiki.archlinux.org/title/GPT_fdisk)
local sectorSize
sectorSize=$(awk -F': ' '/^sector-size:/ {print $2}' "$SF_FILE")
sectorSize=${sectorSize:-512} # fallback if missing
targetSDSize=$((totalSize - (33 * sectorSize)))
else
targetSDSize=$totalSize
fi
logItem "sourceSDSize: $sourceSDSize - targetSDSize: $targetSDSize"
if (( sourceSDSize != targetSDSize )); then
if (( sourceSDSize > targetSDSize )); then
if (( $RESIZE_ROOTFS )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_ADJUSTING_WARNING_P "$RESTORE_DEVICE" "$(bytesToHuman "$targetSDSize")" "$(bytesToHuman "$sourceSDSize")"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_ADJUSTING_DISABLED "$RESTORE_DEVICE" "$(bytesToHuman "$targetSDSize")" "$(bytesToHuman "$sourceSDSize")"
exitError $RC_PARAMETER_ERROR
fi
else
if (( $RESIZE_ROOTFS )); then
if (( $targetSDSize >= $TWO_TB )); then # target should have gpt in order to use space > 2TB during expansion
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TARGET_REQUIRES_GPT "$RESTORE_DEVICE" "$(bytesToHuman "$targetSDSize")"
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_ADJUSTING_WARNING_P2 "$RESTORE_DEVICE" "$(bytesToHuman "$targetSDSize")" "$(bytesToHuman "$sourceSDSize")"
fi
fi
# label: dos
# label-id: 0x3c3f4bdb
# device: /dev/mmcblk0
# unit: sectors
# sector-size: 512
#
# /dev/mmcblk0p1 : start= 8192, size= 524288, type=c
# /dev/mmcblk0p2 : start= 532480, size= 15196160, type=83
local sourceValues
mapfile -d " " -t sourceValues < <( awk '/[0-9] :/ { v=$4 $6; gsub(","," ",v); printf "%s",v }' "$SF_FILE" )
if (( ${#sourceValues[@]} < 4 )); then
logCommand "cat $SF_FILE"
assertionFailed $LINENO "Expected at least 2 partitions in $SF_FILE"
fi
if (( ! PARTITIONBASED_BACKUP && ( ${sourceValues[2]} == 0 || ${sourceValues[3]} == 0 ) )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_R_OPTION
exitError $RC_MISC_ERROR
fi
local partitionSizes
mapfile -d " " -t partitionSizes < <( createResizedSFDisk "$SF_FILE" "$targetSDSize" "$MODIFIED_SFDISK" )
local oldPartitionSourceSize=${partitionSizes[0]}
local newPartitionTargetSize=${partitionSizes[1]}
local resizedPartitionNumber=${partitionSizes[2]}
if (( newPartitionTargetSize <= 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESIZED_PARTITION_TOO_SMALL "$resizedPartitionNumber" "$RESTORE_DEVICE" "$(bytesToHuman $oldPartitionSourceSize)" "$(bytesToHuman -${newPartitionTargetSize})"
exitError "$RC_RESIZE_ERROR"
fi
if (( ${#sourceValues[@]} == 4 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_ADJUSTING_SECOND "$(bytesToHuman $oldPartitionSourceSize)" "$(bytesToHuman $newPartitionTargetSize)"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_ADJUSTING_LAST "$(bytesToHuman $oldPartitionSourceSize)" "$(bytesToHuman $newPartitionTargetSize)"
fi
else
cp "$SF_FILE" "$MODIFIED_SFDISK" # no repartitioning required
fi
else
cp "$SF_FILE" "$MODIFIED_SFDISK" # just use unmodified sfdisk when option -R is used for a hybrid system
fi
sfdisk -f "$RESTORE_DEVICE" < "$MODIFIED_SFDISK" &>>"$LOG_FILE"
rc=$?
if (( $rc )); then
logItem "sfdisk first attempt fails with rc $rc"
if (( $rc == 1 )); then # sector-size is new in bullseye and breaks restore with older OS
sed -i '/sector-size/d' "$MODIFIED_SFDISK" # remove sector-size
logCommand "cat $MODIFIED_SFDISK"
sfdisk -f "$RESTORE_DEVICE" < "$MODIFIED_SFDISK" &>>"$LOG_FILE"
rc=$?
fi
fi
rm $MODIFIED_SFDISK &>/dev/null
if (( $rc )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_CREATE_PARTITIONS $rc "sfdisk error"
exitError "$RC_CREATE_PARTITIONS_FAILED"
fi
waitForPartitionDefsChanged
fi
logItem "Targetpartitionlayout$NL$(fdisk -l "$RESTORE_DEVICE")"
fi
logExit
}
# return all partitionnumbers available in backup
function collectAvailableBackupPartitions() { # lastBackupDir
logEntry "$1"
local lastBackupDir="$1"
local directories partitionNo availablePartitions=""
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_TAR" || "$BACKUPTYPE" == "$BACKUPTYPE_TGZ" ]]; then
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
#shellcheck disable=SC2010
directories="$(ls -l ${lastBackupDir} | grep -E "\.($BACKUPTYPE_TAR|$BACKUPTYPE_TGZ)" | sed -E 's/\..+//' )" # delete trailing .tar or .tgz)
elif [[ "$BACKUPTYPE" == "$BACKUPTYPE_RSYNC" ]]; then
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
#shellcheck disable=SC2010
directories="$(ls -l ${lastBackupDir} | grep '^d')"
else
assertionFailed $LINENO "Unsupported partitions backup type"
fi
logItem "Directories: $directories"
directories="$(grep -Po "(((s|v)d[a-z]|(mmcblk|loop)[0-9]p)|nvme[0-9]n[0-9]p)[0-9]+$" <<< $directories )" # extract valid backup partitions
partitionNo="$(grep -Eo "[0-9]+$" <<< $directories )"
availablePartitions=$(tr '\n' ' ' <<< "$partitionNo") # substitute nl from ls with space
availablePartitions="${availablePartitions::-1}" # delete last space
logItem "Found available partitions: $availablePartitions in $lastBackupDir"
echo "${availablePartitions}"
logExit "-${availablePartitions}-"
}
function restoreNormalBackupType() {
logEntry
rc=0
local verbose zip
(( $VERBOSE )) && verbose="-v" || verbose=""
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORING_FILE "$RESTOREFILE"
logCommand "ls -la $RESTOREFILE"
START_TIME=$(date +%s)
rc=$RC_NATIVE_RESTORE_FAILED
logSystemDiskState
callExtensions "$PRE_RESTORE_EXTENSION" "0"
rc=$?
PRE_RESTORE_EXTENSION_CALLED=1
if (( $rc )); then
exitError "$RC_RESTORE_EXTENSION_FAILS"
fi
case $BACKUPTYPE in
"$BACKUPTYPE_DD"|"$BACKUPTYPE_DDZ")
local progressFlag=""
(( $PROGRESS && $INTERACTIVE )) && progressFlag="status=progress"
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_DD" ]]; then
cmd="dd if=\"$ROOT_RESTOREFILE\" $progressFlag of=$RESTORE_DEVICE bs=$DD_BLOCKSIZE $DD_PARMS"
else
cmd="gunzip -c \"$ROOT_RESTOREFILE\" | dd of=$RESTORE_DEVICE $progressFlag bs=$DD_BLOCKSIZE $DD_PARMS"
fi
executeDD "$cmd"
rc=$?
;;
*) MNT_POINT="$TEMPORARY_MOUNTPOINT_ROOT"
umountPartition "$MNT_POINT"
logItem "Creating mountpoint $MNT_POINT"
if ! mkdir -p "$MNT_POINT" &>>"$LOG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_CREATE_DIRECTORY "${MNT_POINT}"
exitError $RC_CREATE_ERROR
fi
partitionRestoredeviceIfRequested
if [[ -e $TAR_FILE ]]; then
writeToConsole $MSG_LEVEL_DETAILED $MSG_FORMATTING_FIRST_PARTITION "$BOOT_PARTITION"
mkfs.vfat $BOOT_PARTITION &>>$LOG_FILE
rc=$?
if [ $rc != 0 ]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_IMG_BOOT_CREATE_PARTITION_FAILED "$rc"
exitError $RC_NATIVE_RESTORE_FAILED
fi
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORING_FIRST_PARTITION "$BOOT_PARTITION"
local ext=$BOOT_DD_EXT
if [[ -e "$DD_FILE" ]]; then
logItem "Restoring boot partition from $DD_FILE"
local progressFlag=""
(( $PROGRESS && $INTERACTIVE )) && progressFlag="status=progress"
local cmd="dd if=$DD_FILE $progressFlag of=$BOOT_PARTITION bs=$DD_BLOCKSIZE"
executeDD "$cmd"
rc=$?
else
ext=$BOOT_TAR_EXT
logItem "Restoring boot partition from $TAR_FILE to $BOOT_PARTITION"
mountAndCheck $BOOT_PARTITION "$MNT_POINT"
if ! pushd "$MNT_POINT" &>>"$LOG_FILE"; then
assertionFailed $LINENO "push to $MNT_POINT failed"
fi
if (( $PROGRESS && $INTERACTIVE )); then
local cmd="pv -f $TAR_FILE | tar -xf -"
else
local cmd="tar -xf \"$TAR_FILE\""
fi
executeTar "$cmd"
rc=$?
popd &>>"$LOG_FILE"
fi
if (( $rc != 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_IMG_BOOT_RESTORE_FAILED ".$ext" "$rc"
exitError $RC_NATIVE_RESTORE_FAILED
fi
executeFilesystemCheck "$BOOT_PARTITION"
writeToConsole $MSG_LEVEL_DETAILED $MSG_FORMATTING_SECOND_PARTITION "$ROOT_PARTITION"
local check=""
(( $REGRESSION_TEST )) && check="-F "
if (( $CHECK_FOR_BAD_BLOCKS )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DETAILED_ROOT_CHECKING "$ROOT_PARTITION"
check="-c"
mkfs.ext4 $check "$ROOT_PARTITION"
else
mkfs.ext4 $check "$ROOT_PARTITION" &>>"$LOG_FILE"
fi
rc=$?
if (( $rc != 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_IMG_ROOT_CREATE_PARTITION_FAILED "$rc"
exitError $RC_NATIVE_RESTORE_FAILED
fi
waitForPartitionDefsChanged
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORING_SECOND_PARTITION "$ROOT_PARTITION"
mountAndCheck $ROOT_PARTITION "$MNT_POINT"
case $BACKUPTYPE in
"$BACKUPTYPE_TAR"|"$BACKUPTYPE_TGZ")
local archiveFlags="--same-owner --same-permissions --numeric-owner ${TAR_RESTORE_ADDITIONAL_OPTIONS}"
if ! pushd "$MNT_POINT" &>>"$LOG_FILE"; then
assertionFailed $LINENO "push to $MNT_POINT failed"
fi
[[ "$BACKUPTYPE" == "$BACKUPTYPE_TGZ" ]] && zip="-z" || zip=""
if (( $PROGRESS && $INTERACTIVE )); then
local cmd="pv -f $ROOT_RESTOREFILE | tar ${archiveFlags} -x ${verbose} ${zip} -f -"
else
local cmd="tar ${archiveFlags} -x ${verbose} ${zip} -f \"$ROOT_RESTOREFILE\""
fi
executeTar "$cmd"
rc=$?
popd &>>"$LOG_FILE"
;;
"$BACKUPTYPE_RSYNC")
local excludePattern="--exclude=/$HOSTNAME-backup.*"
logItem "Excluding excludePattern"
local progressFlag=""
(( $PROGRESS && $INTERACTIVE )) && progressFlag="--info=progress2"
local cmd="rsync $progressFlag --numeric-ids ${RSYNC_BACKUP_OPTIONS} ${verbose} ${RSYNC_BACKUP_ADDITIONAL_OPTIONS} $excludePattern \"$ROOT_RESTOREFILE/\" $MNT_POINT"
executeRsync "$cmd"
rc=$?
;;
*) assertionFailed $LINENO "Invalid backuptype $BACKUPTYPE"
;;
esac
if [[ $rc != 0 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_PROGRAM_ERROR $BACKUPTYPE $rc
exitError $RC_NATIVE_RESTORE_FAILED
fi
umountPartition $ROOT_PARTITION
executeFilesystemCheck "$ROOT_PARTITION"
updateUUIDs
mountAndCheck "$ROOT_PARTITION" "$MNT_POINT"
logItem "Updating hw clock"
date -u +"%Y-%m-%d %T" > "$MNT_POINT/etc/fake-hwclock.data"
#logItem "Force fsck on reboot"
#touch $MNT_POINT/forcefsck
logCommand "parted -s $RESTORE_DEVICE print"
if isSpecialBlockDevice "$RESTORE_DEVICE"; then
ROOT_DEVICE=$(sed -E 's/p[0-9]+$//' <<< "$ROOT_PARTITION")
else
ROOT_DEVICE=$(sed -E 's/[0-9]+$//' <<< "$ROOT_PARTITION")
fi
if [[ "$ROOT_DEVICE" != "$RESTORE_DEVICE" ]]; then
logCommand "parted -s $ROOT_DEVICE print"
fi
esac
END_TIME="$(date +%s)"
logItem "Syncing filesystems"
sync
umountPartition "$MNT_POINT"
logSystemDiskState
logExit "$rc"
}
function applyBackupStrategy() {
logEntry "$BACKUP_TARGETDIR"
if (( $SMART_RECYCLE )); then
local dir_to_delete dir_to_keep
logItem "SR Parms: ${SMART_RECYCLE_PARMS[*]}"
SR_DAILY="${SMART_RECYCLE_PARMS[0]}"
SR_WEEKLY="${SMART_RECYCLE_PARMS[1]}"
SR_MONTHLY="${SMART_RECYCLE_PARMS[2]}"
SR_YEARLY="${SMART_RECYCLE_PARMS[3]}"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SMART_APPLYING_BACKUP_STRATEGY $SR_DAILY $SR_WEEKLY $SR_MONTHLY $SR_YEARLY
logCommand "ls -d $BACKUPPATH/*"
local keptBackups numKeptBackups
keptBackups="$(SR_listUniqueBackups $BACKUPTARGET_ROOT)"
numKeptBackups="$(countLines "$keptBackups")"
logItem "Keptbackups $numKeptBackups: $keptBackups"
local tobeDeletedBackups
tobeDeletedBackups="$(SR_listBackupsToDelete "$BACKUPTARGET_ROOT")"
local numTobeDeletedBackups
numTobeDeletedBackups="$(countLines "$tobeDeletedBackups")"
logItem "TobeDeletedBackups $numTobeDeletedBackups: $tobeDeletedBackups"
if [[ -n "$tobeDeletedBackups" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SMART_RECYCLE_FILES "$numTobeDeletedBackups" "$numKeptBackups"
echo "$tobeDeletedBackups" | while read -r dir_to_delete; do
logItem "Recycling $BACKUPTARGET_ROOT/${dir_to_delete}"
if (( ! $SMART_RECYCLE_DRYRUN && ( ! $FAKE || $REGRESSION_TEST ) )); then
writeToConsole $MSG_LEVEL_DETAILED $MSG_SMART_RECYCLE_FILE_DELETE "$BACKUPTARGET_ROOT/${dir_to_delete}"
[[ -n "$dir_to_delete" ]] && rm -rf "$BACKUPTARGET_ROOT/${dir_to_delete:?}" # guard against whole backup dir deletion
else
[[ -n "$dir_to_delete" ]] && writeToConsole $MSG_LEVEL_MINIMAL $MSG_SMART_RECYCLE_FILE_WOULD_BE_DELETED "$BACKUPTARGET_ROOT/${dir_to_delete}"
fi
done
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SMART_RECYCLE_NO_FILES
fi
if (( $SMART_RECYCLE_DRYRUN || $FAKE )); then
echo "$keptBackups" | while read dir_to_keep; do
[[ -n $dir_to_keep ]] && writeToConsole $MSG_LEVEL_MINIMAL $MSG_SMART_RECYCLE_FILE_WOULD_BE_KEPT "$BACKUPTARGET_ROOT/${dir_to_keep}"
done
fi
else
local bt="${BACKUPTYPE^^}"
local v="KEEPBACKUPS_${bt}"
local keepOverwrite="${!v}"
local dir_to_delete
local dir_to_check
local tobeDeletedBackups
local tobeCheckedBackups
local keepBackups=$KEEPBACKUPS
(( $keepOverwrite != 0 )) && keepBackups=$keepOverwrite
if (( $keepBackups != -1 )); then
logItem "Deleting oldest directory in $BACKUPPATH"
logCommand "ls -d $BACKUPPATH/*"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUPS_KEPT "$keepBackups" "$BACKUPTYPE"
if (( $FAKE )); then
fakeKeepBackups=$(( keepBackups - 1 )) # because in FAKE mode no real current backup has been created
writeToConsole $MSG_LEVEL_DETAILED $MSG_CLEANUP_BACKUP_VERSION "$BACKUPPATH"
if ! pushd "$BACKUPPATH" &>>"$LOG_FILE"; then
assertionFailed $LINENO "push to $BACKUPPATH failed"
fi
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
#shellcheck disable=SC2010
tobeCheckedBackups=$(ls -d ${HOSTNAME_OSR}-${BACKUPTYPE}-backup-* 2>>"$LOG_FILE" | grep -vE "_")
echo "$tobeCheckedBackups" | while read dir_to_check; do
[[ -n $dir_to_check ]] && writeToConsole $MSG_LEVEL_MINIMAL $MSG_EXISTING_BACKUP $BACKUPTARGET_ROOT/${dir_to_check}
done
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
#shellcheck disable=SC2010
tobeDeletedBackups=$(ls -d ${HOSTNAME_OSR}-${BACKUPTYPE}-backup-* 2>>"$LOG_FILE" | grep -vE "_" | head -n -$fakeKeepBackups 2>>"$LOG_FILE")
echo "$tobeDeletedBackups" | while read dir_to_delete; do
[[ -n $dir_to_delete ]] && writeToConsole $MSG_LEVEL_MINIMAL $MSG_NORMAL_RECYCLE_FILE_WOULD_BE_DELETED "$BACKUPTARGET_ROOT/${dir_to_delete}"
done
if ! popd &>>"$LOG_FILE"; then
assertionFailed $LINENO "pop failed"
fi
else
writeToConsole $MSG_LEVEL_DETAILED $MSG_CLEANUP_BACKUP_VERSION "$BACKUPPATH"
if ! pushd "$BACKUPPATH" &>>"$LOG_FILE"; then
assertionFailed $LINENO "push to $BACKUPPATH failed"
fi
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
#shellcheck disable=SC2010
ls -d ${HOSTNAME_OSR}-${BACKUPTYPE}-backup-* 2>>"$LOG_FILE" | grep -vE "_" | head -n -$keepBackups | xargs -I {} rm -rf "{}" &>>"$LOG_FILE";
if ! popd &>>"$LOG_FILE"; then
assertionFailed $LINENO "pop failed"
fi
local rmRC=$?
if (( $rmRC != 0 )); then
logItem "rmRC: $rmRC"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_CLEANUP_FAILED
exitError $RC_CLEANUP_ERROR
fi
local regex="\-([0-9]{8}\-[0-9]{6})\.(img|mbr|sfdisk|log)$"
local regexDD="\-dd\-backup\-([0-9]{8}\-[0-9]{6})\.img$"
if ! pushd "$BACKUPPATH" 1>/dev/null; then
assertionFailed $LINENO "push to $BACKUPPATH failed"
fi
for imgFile in *.img *.mbr *.sfdisk *.log *.msg; do
[[ ! -e $imgFile ]] && continue
if [[ $imgFile =~ $regexDD ]]; then
logItem "Skipping DD file $imgFile"
continue
fi
if [[ ! $imgFile =~ $regex ]]; then
logItem "Skipping $imgFile"
continue
else
logItem "Processing $imgFile"
fi
local date=${BASH_REMATCH[1]}
logItem "Extracted date: $date"
if [[ -z "$date" ]]; then
assert $LINENO "Unable to extract date from backup files"
fi
local file
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
#shellcheck disable=SC2010
file=$(ls -d *-*-backup-$date* 2>/dev/null| grep -Ev "\.(log|msg|img|mbr|sfdisk)$");
if [[ -n "$file" ]]; then
logItem "Found backup for $imgFile"
else
logItem "Found NO backup for $imgFile - removing"
rm -f "$imgFile" &>>"$LOG_FILE"
fi
done
popd > /dev/null
logItem "post - ls$NL$(ls -d $BACKUPPATH/* 2>/dev/null)"
fi
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_ALL_BACKUPS_KEPT "$BACKUPTYPE"
fi
fi
logExit
}
function reportOldBackups() {
logEntry
local dir_to_list
if ! pushd "$BACKUPPATH" &>>"$LOG_FILE"; then
assertionFailed $LINENO "push to $BACKUPPATH failed"
fi
if ls -d ${HOSTNAME}-${BACKUPTYPE}-backup-* &>/dev/null; then
# Double quote to prevent globbing and word splitting.
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
#shellcheck disable=SC2010,SC2086
tobeListedOldBackups=$(ls -d ${HOSTNAME}-${BACKUPTYPE}-backup-* 2>>"$LOG_FILE")
if [[ -n $tobeListedOldBackups ]]; then
local report_counter_file="$VAR_LIB_DIRECTORY/$REPORT_COUNTER_FILE"
# create directory to save counter
if [[ ! -d "$VAR_LIB_DIRECTORY" ]]; then
if ! mkdir -p "$VAR_LIB_DIRECTORY" &>>"$LOG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_CREATE_DIRECTORY "${VAR_LIB_DIRECTORY}"
exitError $RC_CREATE_ERROR
fi
fi
# initialize counter
if [[ ! -e "$report_counter_file" ]]; then
echo "$OLD_REMINDER_REPEAT" > "$report_counter_file"
fi
# retrieve counter
local rf
rf=$(<$report_counter_file)
if [[ -z "${rf}" ]]; then # counter file exists but is empty
echo "$OLD_REMINDER_REPEAT" > "$report_counter_file"
return
fi
rf=$(<$report_counter_file)
# only print report if counter says so
if (( $rf > 0 )); then
# update counter, but only if not in FAKE mode
local rfn=$(( ${rf} - 1 ))
if (( ! $FAKE )); then
echo "${rfn}" > "$report_counter_file"
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_GENERIC_WARNING "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_NAMING_CHANGE "0.7"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_OLD_NAME_BACKUPS_FOUND
echo "$tobeListedOldBackups" | while read -r dir_to_list; do
[[ -n $dir_to_list ]] && writeToConsole $MSG_LEVEL_MINIMAL $MSG_GENERIC_WARNING " - $BACKUPTARGET_ROOT/${dir_to_list}"
done
writeToConsole $MSG_LEVEL_MINIMAL $MSG_OLD_NAME_BACKUPS_HANDLING_INFO
writeToConsole $MSG_LEVEL_MINIMAL $MSG_OLD_NAME_BACKUPS_COUNTER_INFO "${rfn}"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_GENERIC_WARNING "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
# only print report if counter says so
if (( $rf > 0 )); then
# update counter, but only if not in FAKE mode
local rfn=$(( ${rf} - 1 ))
if (( ! $FAKE )); then
echo "${rfn}" > "$report_counter_file"
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_GENERIC_WARNING "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_NAMING_CHANGE "0.7"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_OLD_NAME_BACKUPS_FOUND
echo "$tobeListedOldBackups" | while read -r dir_to_list; do
[[ -n $dir_to_list ]] && writeToConsole $MSG_LEVEL_MINIMAL $MSG_GENERIC_WARNING " - $BACKUPTARGET_ROOT/${dir_to_list}"
done
writeToConsole $MSG_LEVEL_MINIMAL $MSG_OLD_NAME_BACKUPS_HANDLING_INFO
writeToConsole $MSG_LEVEL_MINIMAL $MSG_OLD_NAME_BACKUPS_COUNTER_INFO "${rfn}"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_GENERIC_WARNING "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
fi
fi
fi
else
logItem "No old backups found"
fi
if ! popd &>>"$LOG_FILE"; then
assertionFailed $LINENO "pop failed"
fi
logExit
}
function backup() {
logEntry
logSystemDiskState
executeBeforeStopServices
stopServices
callExtensions $PRE_BACKUP_EXTENSION "0"
rc=$?
PRE_BACKUP_EXTENSION_CALLED=1
if (( $rc )); then
exitError "$RC_BACKUP_EXTENSION_FAILS"
fi
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_RSYNC" || (( $PARTITIONBASED_BACKUP )) ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_TARGET "$BACKUPTYPE" "$BACKUPTARGET_FINAL_DIR"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_TARGET "$BACKUPTYPE" "$BACKUPTARGET_FILE"
fi
logItem "Storing backup in backuppath $BACKUPPATH"
if [[ -f $BOOT_DEVICENAME ]]; then
logCommand "fdisk -l $BOOT_DEVICENAME"
fi
logItem "Starting $BACKUPTYPE backup..."
rc=0
callExtensions "$READY_BACKUP_EXTENSION" "$rc"
START_TIME=$(date +%s)
if (( ! $FAKE )); then
if (( ! $PARTITIONBASED_BACKUP )); then
case "$BACKUPTYPE" in
"$BACKUPTYPE_DD"|"$BACKUPTYPE_DDZ") backupDD
;;
"$BACKUPTYPE_TAR"|"$BACKUPTYPE_TGZ") backupTar
;;
"$BACKUPTYPE_RSYNC") backupRsync
;;
*) assertionFailed $LINENO "Invalid backuptype $BACKUPTYPE"
;;
esac
if [[ $rc != 0 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_PROGRAM_ERROR $BACKUPTYPE "$rc"
exitError $RC_NATIVE_BACKUP_FAILED
fi
else
backupPartitions
fi
fi
END_TIME=$(date +%s)
mapfile -d " " -t BACKUP_TIME < <( duration "$START_TIME" "$END_TIME" )
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_TIME "${BACKUP_TIME[1]}" "${BACKUP_TIME[2]}" "${BACKUP_TIME[3]}"
logItem "Syncing"
sync
logItem "Finished $BACKUPTYPE backup"
logItem "Backup created with return code: $rc"
logItem "Current directory: $(pwd)"
if [[ -z $BACKUPPATH || "$BACKUPPATH" == *"*"* ]]; then
assertionFailed $LINENO "Unexpected backup path $BACKUPPATH"
fi
logSystemDiskState
logExit
}
function mountPartitions() { # sourcePath
local partition partitionName
logEntry "${PARTITIONS_TO_BACKUP[@]}"
if (( ! $FAKE )); then
logItem "BEFORE: mount $(mount)"
# Double quote array expansions to avoid re-splitting elements.
#shellcheck disable=SC2068
for partition in ${PARTITIONS_TO_BACKUP[@]}; do
partitionName="$BOOT_PARTITION_PREFIX$partition"
logItem "mkdir $1/$partitionName"
if ! mkdir -p "$1/$partitionName" &>>"$LOG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_CREATE_DIRECTORY "$1/$partitionName"
exitError $RC_CREATE_ERROR
fi
logItem "mount /dev/$partitionName to $1/$partitionName"
mountAndCheck "/dev/$partitionName" "$1/$partitionName"
done
logItem "AFTER: mount $(mount)"
fi
logExit
}
function umountPartitions() { # sourcePath
local partitionName partition
logEntry
if (( ! $FAKE )); then
logItem "BEFORE: mount $(mount)"
for partition in "${PARTITIONS_TO_BACKUP[@]}"; do
partitionName="$BOOT_PARTITION_PREFIX$partition"
umountPartition "$1/$partitionName"
if [[ -d "$1/$partitionName" ]]; then
logItem "rmdir $1/$partitionName"
rmdir "$1/$partitionName" &>>"$LOG_FILE"
fi
done
logItem "AFTER: mount $(mount)"
fi
logExit
}
function umountPartition() { # partition
logEntry "$1"
local retry=3 rc
if isMounted "$1"; then
umount "$1" &>>"$LOG_FILE"
rc=$?
if (( $rc )); then
if (( --retry > 0 )); then
logItem "RC $rc - Retrying umount $retry"
sleep 3
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UMOUNT_ERROR "$1" "$rc"
exitError "$RC_MISC_ERROR"
fi
fi
fi
logExit
}
function array2String() {
local IFS="$1"
shift
echo "$*"
}
function backupPartitions() {
logEntry
local partition
logItem "PARTITIONS_TO_BACKUP: $(echo "${PARTITIONS_TO_BACKUP[@]}")"
local parts
parts="$(array2String " " "${PARTITIONS_TO_BACKUP[@]}")"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PARTITIONS_BACKUP_STARTED "$BACKUPTYPE" "$parts"
if ! containsElement "1" "${PARTITIONS_TO_BACKUP[@]}" || ! containsElement "2" "${PARTITIONS_TO_BACKUP[@]}"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NOT_ALL_OS_PARTITIONS_SAVED
fi
if (( ! $FAKE )); then
partitionLayoutBackup
fi
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_RSYNC" || "$BACKUPTYPE" == "$BACKUPTYPE_TAR" || "$BACKUPTYPE" == "$BACKUPTYPE_TGZ" ]]; then
mountPartitions "$TEMPORARY_MOUNTPOINT_ROOT"
fi
for partition in "${PARTITIONS_TO_BACKUP[@]}"; do
logItem "Processing partition $partition"
local fileSystem
fileSystem="$(getBackupPartitionFilesystem "$partition")"
if [[ -z $fileSystem ]]; then
local unusedSize
unusedSize="$(extractDataFromBackupPartedFile "${BOOT_PARTITION_PREFIX}$partition" "4")"
unusedSize="$(bytesToHuman "$unusedSize")"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SKIPPING_UNFORMATTED_PARTITION "${BOOT_PARTITION_PREFIX}$partition" "$unusedSize"
else
local pref
pref="$(getPartitionPrefix $BOOT_DEVICENAME)"
local fileSystemSize
fileSystemSize="$(getBackupPartitionFilesystemSize "${pref}$partition")"
local fileSystemUsed
fileSystemUsed="$(getBackupPartitionFilesystemUsed "${pref}$partition")"
logItem "fileSystem: $fileSystem - fileSystemSize: $fileSystemSize - fileSystemUsed: $fileSystemUsed"
local fileSystemSize
fileSystemSize="$(bytesToHuman $fileSystemSize)"
local fileSystemUsed
fileSystemUsed="$(bytesToHuman $fileSystemUsed)"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PROCESSING_PARTITION "${BOOT_PARTITION_PREFIX}$partition" "$fileSystemUsed" "$fileSystemSize"
case "$BACKUPTYPE" in
"$BACKUPTYPE_DD"|"$BACKUPTYPE_DDZ") backupDD "$partition"
;;
"$BACKUPTYPE_TAR"|"$BACKUPTYPE_TGZ") backupTar "$partition"
;;
"$BACKUPTYPE_RSYNC") backupRsync "$partition"
;;
*) assertionFailed $LINENO "Invalid backuptype $BACKUPTYPE"
;;
esac
if (( $rc != 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_PARTITION_FAILED "${BOOT_PARTITION_PREFIX}$partition" $rc
exitError "$RC_NATIVE_RESTORE_FAILED"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PROCESSED_PARTITION "${BOOT_PARTITION_PREFIX}$partition"
fi
fi
done
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_RSYNC" || "$BACKUPTYPE" == "$BACKUPTYPE_TAR" || "$BACKUPTYPE" == "$BACKUPTYPE_TGZ" ]]; then
umountPartitions "$TEMPORARY_MOUNTPOINT_ROOT"
fi
logExit "$rc"
}
function doit() {
logEntry
local msg
logItem "Startingdirectory: $(pwd)"
logCommand "fdisk -l | grep -v "^$""
logCommand "mount"
if isUnsupportedVersion; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNSUPPORTED_VERSION
fi
if (( $RESTORE )); then
doitRestore
else
if (( $FAKE )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FAKE_MODE_ON
fi
doitBackup
fi
logItem "Enddirectory: $(pwd)"
logExit
}
# blkid
# /dev/mmcblk0p2: UUID="ea98d3bf-9345-4bd7-b365-5cc7c543079f" TYPE="ext4" PARTUUID="d888a167-02"
# Args: /dev/mmcblk0p2, /PARTUUID=xxxxx, LABEL=xxxxx or UUID=xxxxxx
# Result: $1, /dev/sda1, /dev/mmcblk0p1, /dev/sdb1 or "" if not found in blkid
function getPartitionName() { # /etc/fstab first col
logEntry "$1"
local prfx
prfx="$(cut -f 1 -d '=' <<< $1)"
local id
id="$(cut -f 2 -d '=' <<< $1)"
local b
b="$(blkid)"
local match
match="$(grep "$prfx=\"$id\"" <<< "$b")"
local result="$1"
if [[ -n "$match" ]]; then
result="$(cut -f 1 -d ":" <<< $match)"
fi
echo "$result"
logExit "$result"
}
function collectPartitions() {
logEntry
# raspbian:
# /dev/mmcblk0p1 : start= 8192, size= 114688, Id= c
# /dev/mmcblk0p2 : start= 122880, size= 1069056, Id=83
# ubuntu:
#/dev/mmcblk0p1 : start= 2048, size= 131072, type=c, bootable
#/dev/mmcblk0p2 : start= 133120, size= 20971520, type=83
#/dev/mmcblk0p3 : start= 21104640, size= 10485760, type=83
#/dev/mmcblk0p4 : start= 31590400, size= 29161472, type=83
local regexPartitionLine="(${BOOT_DEVICENAME}[a-z0-9]+).*start[^0-9]+([0-9]+).*size[^0-9]+([0-9]+).*(Id|type)=[ ]?([^,]+)"
# /dev/mmvblk0p1 on /media/Log1 type ext2 (rw,nosuid,nodev,uhelper=udisks)
local regexMountLine="(${BOOT_DEVICENAME}[a-z0-9]+).*on ([^ ]+)"
logItem "PARTITIONS_TO_BACKUP - 1: $(echo "${PARTITIONS_TO_BACKUP[@]}")"
local backupAllPartitions
if [[ "$PARTITIONS_TO_BACKUP" == "$PARTITIONS_TO_BACKUP_ALL" ]]; then
backupAllPartitions=1
PARTITIONS_TO_BACKUP=()
else
# Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a.
#shellcheck disable=SC2206
PARTITIONS_TO_BACKUP=( ${PARTITIONS_TO_BACKUP[@]} )
backupAllPartitions=0
fi
logItem "backupAllPartitions: $backupAllPartitions"
local mountLine partition size type
local line
while read -r line; do
if [[ "$line" =~ $regexPartitionLine ]]; then
partition=${BASH_REMATCH[1]}
size=${BASH_REMATCH[3]}
type=${BASH_REMATCH[5]}
logItem "partition: $partition - size: $size - type: $type"
if [[ "$type" != "5" && "$type" != "85" && "$size" -gt 0 ]]; then # skip empty and extended partitions
logItem "mount: $(mount)"
logItem "Partition: $partition"
mountLine=$(mount | grep "$partition" )
if ! (( $? )); then
logItem "mountline: $mountLine"
logItem "regexMountLIne: $regexMountLine"
if [[ "$mountLine" =~ $regexMountLine ]]; then
local mountPoint=${BASH_REMATCH[2]}
logItem "partition $partition mounted on $mountPoint"
mountPoints[$partition]="$mountPoint"
else
assertionFailed $LINENO "Unable to find mountpoint for $partition"
fi
else
if [[ "$partition" == "$ROOT_PARTITION" ]]; then
mountPoints[$partition]="/"
logItem "partition $partition mounted on /"
else
logItem "partition $partition not mounted"
mountPoints[$partition]=""
fi
fi
if (( $backupAllPartitions )); then
id=$(getPartitionNumber "$partition")
logItem "Adding partition $id to list of partitions to backup"
PARTITIONS_TO_BACKUP["$id"]="$id"
fi
fi
fi
done < <(sfdisk -d "$BOOT_DEVICENAME" 2>>"$LOG_FILE")
logItem "PARTITIONS_TO_BACKUP - 2: $(echo "${PARTITIONS_TO_BACKUP[@]}")"
logItem "mountPoints: $(echo "${mountPoints[@]}")"
if (( ${#PARTITIONS_TO_BACKUP[@]} == 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NOPARTITIONS_TOBACKUP_FOUND
exitError $RC_MISC_ERROR
fi
logExit
}
function checksForPartitionBasedBackup() {
local partition
logEntry
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_RSYNC" || "$BACKUPTYPE" == "$BACKUPTYPE_TAR" || "$BACKUPTYPE" == "$BACKUPTYPE_TGZ" ]]; then
local lastBackupDir
lastBackupDir=$(find "$BACKUPTARGET_ROOT" -maxdepth 1 -type d -name "${HOSTNAME}@*-$BACKUPTYPE-*" ! -name "$BACKUPFILE" 2>>/dev/null | sort | tail -n 1)
logItem "lastBackupDir: $lastBackupDir"
if [[ -n "$lastBackupDir" ]]; then
local missing
missing="$(checkIfAllPreviousPartitionsAreIncludedInBackup "$lastBackupDir")"
local rc=$?
logItem "Missing: $missing"
if (( $rc )); then
if (( $IGNORE_MISSING_PARTITIONS )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_PARTITIONS_NOT_SAVED "$missing"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NOT_ALL_PREVIOUS_PARTITIONS_SAVED "$missing"
exitError $MSG_NOT_ALL_PREVIOUS_PARTITIONS_SAVED
fi
fi
fi
fi
logItem "PARTITIONS_TO_BACKUP: ${PARTITIONS_TO_BACKUP[*]}"
SUPPORTED_PARTITIONBACKUP_PARTITIONTYPE_REGEX='^(ext[234]|fat(16|32)|btrfs|f2fs|.*swap.*)$'
local error=0
for partition in "${PARTITIONS_TO_BACKUP[@]}"; do
local fileSystem
fileSystem=$(getPartitionBootFilesystem "$partition")
if [[ -n $fileSystem && ! $fileSystem =~ $SUPPORTED_PARTITIONBACKUP_PARTITIONTYPE_REGEX ]]; then
error=1
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNSUPPORTED_FILESYSTEM_FORMAT "$fileSystem" "${BOOT_PARTITION_PREFIX}$partition"
fi
done
if (( error )) ; then
exitError $RC_PARAMETER_ERROR
fi
error=0
logItem "mountPoints: ${mountPoints[*]}"
logItem "mountPoints - keys: ${!mountPoints[*]}"
# Double quote array expansions to avoid re-splitting elements.
#shellcheck disable=SC2068
for partition in ${PARTITIONS_TO_BACKUP[@]}; do
logItem "Checking partition $partition"
if ! [[ $partition =~ ^[0-9]+ ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PARTITION_NUMBER_INVALID "$partition"
error=1
else
local key="/dev/${BOOT_PARTITION_PREFIX}${partition}"
logItem "Checking partition $partition against $key: ${mountPoints[$key]+isset}"
if [[ ! ${mountPoints[$key]+isset} ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PARTITION_NOT_FOUND "$partition"
error=1
fi
fi
done
if hasExternalRootpartition; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_EXTERNAL_ROOTPARTITION_UNSUPPORTED "$r"
exitError $RC_PARAMETER_ERROR
fi
logExit
}
function hasExternalRootpartition() {
local b r
b="$(grep -E "^[^#]+\s/boot(/firmware)?\s.*" /etc/fstab | cut -f 1 -d ' ')"
r="$(grep -E "^[^#]+\s/\s.*" /etc/fstab | cut -f 1 -d ' ')"
[[ "$r" = "$b" ]]
}
function commonChecks() {
logEntry
if hasSpaces "$CUSTOM_CONFIG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_CONTAINS_SPACES "$CUSTOM_CONFIG_FILE"
exitError $RC_MISC_ERROR
fi
if [[ -n "$EMAIL" ]]; then
if [[ ! $EMAIL_PROGRAM =~ $SUPPORTED_EMAIL_PROGRAM_REGEX ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_EMAIL_PROG_NOT_SUPPORTED "$EMAIL_PROGRAM" "$SUPPORTED_MAIL_PROGRAMS"
exitError $RC_EMAILPROG_ERROR
fi
if [[ ! $(which "$EMAIL_PROGRAM") && ( "$EMAIL_PROGRAM" != "$EMAIL_EXTENSION_PROGRAM" ) ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MAILPROGRAM_NOT_INSTALLED "$EMAIL_PROGRAM"
exitError "$RC_EMAILPROG_ERROR"
fi
if [[ "$EMAIL_PROGRAM" == "$EMAIL_SSMTP_PROGRAM" || "$EMAIL_PROGRAM" == "$EMAIL_MSMTP_PROGRAM" ]] && (( $APPEND_LOG )); then
if ! which mpack &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MPACK_NOT_INSTALLED
APPEND_LOG=0
fi
fi
fi
local co
co="$(tr -d "$COLORING_VALID_OPTIONS" <<< "$COLORING")"
if [[ -n "$COLORING" && -n "$co" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_INVALID_COLORING_OPTION "$co"
exitError "$RC_PARAMETER_ERROR"
fi
logExit
}
# retrieve various information for a partition, e.g. /dev/mmcblk0p1 or /dev/sda2
#
# 1: device (mmcblk0 or sda)
# 2: partition number (1 or 2)
#
function deviceInfo() { # device, e.g. /dev/mmcblk1p2 or /dev/sda3 or /dev/nvme0n1p1 or /dev/nvme0n1p1, returns 0:device (mmcblk0), 1: partition number
logEntry "$1"
local r=""
if [[ $1 =~ ^/dev/([^0-9]+)([0-9]+)$ || $1 =~ ^/dev/([^0-9]+[0-9]+)p([0-9]+)$ || $1 =~ ^/dev/([^0-9]+[0-9]+n[0-9])+p([0-9]+)$ ]]; then
r="${BASH_REMATCH[1]} ${BASH_REMATCH[2]}"
else
assertionFailed $LINENO "Unable to extract device info from $1"
fi
echo "$r"
logExit "$r"
}
function inspect4Backup() {
logEntry
logCommand "ls -1 /dev/mmcblk*"
logCommand "ls -1 /dev/sd*"
logCommand "ls -1 /dev/nvme*"
if mount | grep -q "^overlay.* on / type"; then
writeToConsole $MSG_LEVEL_MINIMAL $OVERLAY_FILESYSTEM_NOT_SUPPORTED
exitError $RC_NOT_SUPPORTED
fi
if ! isMounted "/boot"; then
if ! isMounted "/boot/firmware"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_BOOTPARTITION_MOUNTED # otherwise an empty bootpartition will be saved
exitError $RC_NO_BOOT_FOUND
fi
fi
if [[ -n "$BOOT_DEVICE" ]]; then
local updatedBootdeviceName=${BOOT_DEVICE#"/dev/"}
BOOT_DEVICE="$updatedBootdeviceName"
logItem "Using configured bootdevice $BOOT_DEVICE"
elif (( $REGRESSION_TEST )); then
[[ -e /dev/vda ]] && BOOT_DEVICE="vda"
[[ -e /dev/sda ]] && BOOT_DEVICE="sda"
[[ -e /dev/mmcblk0 ]] && BOOT_DEVICE="mmcblk0"
[[ -e /dev/nvme0n1 ]] && BOOT_DEVICE="nvme0n1"
logItem "Force BOOT_DEVICE to $BOOT_DEVICE"
elif (( $RESTORE )); then
BOOT_DEVICE="mmcblk0"
logItem "Force BOOT_DEVICE to $BOOT_DEVICE"
elif [[ -z $BOOT_DEVICE ]]; then
logItem "Starting boot discovery"
# test whether boot device is mounted
local bootMountpoint="/boot"
local bootPartition
bootPartition=$(findmnt "$bootMountpoint" -o source -n) # /dev/mmcblk0p1, /dev/loop01p or /dev/sda1 or /dev/nvme0n1p1
logItem "bootMountpoint1: $bootMountpoint mounted? $bootPartition"
if [[ -z "$bootPartition" ]]; then
bootMountpoint="/boot/firmware"
local bootPartition
bootPartition=$(findmnt $bootMountpoint -o source -n) # /dev/mmcblk0p1, /dev/loop01p or /dev/sda1 or /dev/nvme0n1p1
logItem "bootMountpoint2: $bootMountpoint mounted? $bootPartition"
fi
# test whether some other /boot path is mounted
if [[ -z $bootPartition ]]; then
bootPartition=$(mount | grep "\s/boot" | cut -f 1 -d ' ')
bootMountpoint=$(mount | grep "\s/boot" | cut -f 3 -d ' ')
logItem "Some path in /boot mounted? $bootPartition on $bootMountpoint"
fi
logItem "bootMountpoint: $bootMountpoint, bootPartition: $bootPartition"
logItem "Starting root discovery"
# find root partition
local rootPartition rp
rootPartition=$(findmnt / -o source -n) # /dev/root or /dev/sda1 or /dev/mmcblk1p2 or /dev/nvme0n1p2
logItem "rootPartition: / mounted? $rootPartition"
if [[ $rootPartition == "/dev/root" ]]; then
rp=$(grep -E -o "root=[^ ]+" /proc/cmdline)
rootPartition=${rp#/root=/}
logItem "/ mounted as /dev/root: $rootPartition"
fi
# check for /boot on root partition
if [[ -z "$bootPartition" ]]; then
if ! find "$bootMountpoint" -name cmdline.txt &>/dev/null; then
logItem "No cmdline.txt found in $bootMountpoint"
# no RaspbianOS
if [[ -n $rootPartition ]] && (( UNSUPPORTED_ENVIRONMENT )) && (( IS_UBUNTU )); then # for example ubuntu on orange
bootPartition="$rootPartition"
logItem "Assuming bootpartition is located on rootpartition $rootPartition"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_BOOTDEVICE_FOUND
exitError $RC_MISC_ERROR
fi
else
bootPartition="$rootPartition"
logItem "Bootpartition is located on rootpartition $bootPartition"
fi
fi
mapfile -d " " -t boot < <( deviceInfo "$bootPartition" )
mapfile -d " " -t root < <( deviceInfo "$rootPartition" )
logItem "boot: ${boot[0]} - ${boot[1]}"
logItem "root: ${root[0]} - ${root[1]}"
if (( "${#boot[@]}" == 0 || "${#root[@]}" == 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_BOOT_DEVICE_DISOVERED
exitError $RC_NO_BOOT_FOUND
fi
BOOT_DEVICE="${boot[0]}"
#Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @)
#shellcheck disable=SC2199
if [[ "${boot[@]}" == "${root[@]}" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SHARED_BOOT_DEVICE "/dev/$BOOT_DEVICE"
SHARED_BOOT_DIRECTORY=1
fi
fi
if [[ ! "$BOOT_DEVICE" =~ ^mmcblk[0-9]+$|^(s|v)d[a-z]$|^loop[0-9]+|^nvme[0-9]+n[0-9]+$ ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_INVALID_BOOT_DEVICE "$BOOT_DEVICE"
exitError $RC_INVALID_BOOTDEVICE
fi
if (( ! $FAKE )); then # make sure boot partition is mounted. Otherwise an empty bootpartition will be created in the backup :-(
local bootPref
bootPref="$(getPartitionPrefix $BOOT_DEVICE)"
logItem "bootPref: $bootPref - /dev/${bootPref}${boot[1]}"
if ! findmnt "/dev/${bootPref}1" &>/dev/null; then
if ! mount | grep "^/dev/${bootPref}${boot[1]}" &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_BOOTDEVICE_MOUNTED "/dev/${bootPref}${boot[1]}"
exitError $RC_INVALID_BOOTDEVICE
fi
fi
fi
logItem "BOOT_DEVICE: $BOOT_DEVICE"
BACKUP_BOOT_DEVICE="$BOOT_DEVICE"
BOOT_DEVICENAME="/dev/$BOOT_DEVICE"
logItem "BOOT_DEVICENAME: $BOOT_DEVICENAME"
BACKUP_BOOT_DEVICENAME="$BOOT_DEVICENAME"
BOOT_PARTITION_PREFIX="$(getPartitionPrefix $BOOT_DEVICE)" # mmcblk0p - sda
logItem "BOOT_PARTITION_PREFIX: $BOOT_PARTITION_PREFIX"
BACKUP_BOOT_PARTITION_PREFIX="$BOOT_PARTITION_PREFIX"
logExit
}
function inspect4Restore() {
logEntry
if [[ "$BACKUPTYPE" != "$BACKUPTYPE_DD" && "$BACKUPTYPE" != "$BACKUPTYPE_DDZ" ]]; then
SF_FILE=$(ls -1 "$RESTOREFILE/${HOSTNAME}-backup.sfdisk")
if [[ -z "$SF_FILE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_NOT_FOUND "$RESTOREFILE/${HOSTNAME}-backup.sfdisk"
exitError "$RC_MISSING_FILES"
fi
MBR_FILE=$(ls -1 "$RESTOREFILE/${HOSTNAME}-backup.mbr")
if [[ -z "$MBR_FILE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_NOT_FOUND "$RESTOREFILE/${HOSTNAME}-backup.mbr"
exitError "$RC_MISSING_FILES"
fi
fi
if (( $PARTITIONBASED_BACKUP )); then
BLKID_FILE=$(ls -1 "$RESTOREFILE/${HOSTNAME}-backup.blkid")
if [[ -z "$BLKID_FILE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_NOT_FOUND "$RESTOREFILE/${HOSTNAME}-backup.blkid"
exitError "$RC_MISSING_FILES"
fi
PARTED_FILE=$(ls -1 "$RESTOREFILE/${HOSTNAME}-backup.parted")
if [[ -z "$PARTED_FILE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_NOT_FOUND "$RESTOREFILE/${HOSTNAME}-backup.parted"
exitError "$RC_MISSING_FILES"
fi
FDISK_FILE=$(ls -1 "$RESTOREFILE/${HOSTNAME}-backup.fdisk")
if [[ -z "$FDISK_FILE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_NOT_FOUND "$RESTOREFILE/${HOSTNAME}-backup.fdisk"
exitError "$RC_MISSING_FILES"
fi
fi
#sfdisk from util-linux 2.25.2
## partition table of /dev/mmcblk0
#unit: sectors
#
#/dev/mmcblk0p1 : start= 8192, size= 83968, Id= c
#/dev/mmcblk0p2 : start= 92160, size= 31330304, Id=83
#/dev/mmcblk0p3 : start= 0, size= 0, Id= 0
#/dev/mmcblk0p4 : start= 0, size= 0, Id= 0
#sfdisk from util-linux 2.27.1
#label: dos
#label-id: 0x6c96114a
#device: /dev/sdb
#unit: sectors
#
#/dev/sdb1 : start= 63, size= 1953520002, type=83
if [[ "$BACKUPTYPE" != "$BACKUPTYPE_DD" && "$BACKUPTYPE" != "$BACKUPTYPE_DDZ" ]]; then
BACKUP_BOOT_DEVICE=$(grep "partition table" -m 1 "$SF_FILE" | cut -f 5 -d ' ' | sed 's#/dev/##')
if [[ -z $BACKUP_BOOT_DEVICE ]]; then
BACKUP_BOOT_DEVICE=$(grep "^device" -m 1 "$SF_FILE" | cut -f 2 -d ':' | sed 's#[[:space:]]*/dev/##')
fi
if [[ -z $BACKUP_BOOT_DEVICE ]]; then
logCommand"cat $SF_FILE"
assertionFailed $LINENO "Unable to discover boot device from $SF_FILE"
fi
logItem "BACKUP_BOOT_DEVICE: $BACKUP_BOOT_DEVICE"
BACKUP_BOOT_DEVICENAME="/dev/$BACKUP_BOOT_DEVICE"
logItem "BACKUP_BOOT_DEVICENAME: $BACKUP_BOOT_DEVICENAME"
BACKUP_BOOT_PARTITION_PREFIX="$(getPartitionPrefix $BACKUP_BOOT_DEVICE)"
logItem "BACKUP_BOOT_PARTITION_PREFIX: $BACKUP_BOOT_PARTITION_PREFIX"
fi
logExit
}
function reportNews() {
logEntry
isUpdatePossible
if (( ! $IS_BETA )); then
local betaVersion
betaVersion=$(isBetaAvailable)
if [[ -n "$betaVersion" && "$VERSION" != "$betaVersion" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BETAVERSION_AVAILABLE "$betaVersion"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_VISIT_VERSION_HISTORY_PAGE "$(getMessage $MSG_VERSION_HISTORY_PAGE)"
NEWS_AVAILABLE=1
BETA_AVAILABLE=1
fi
fi
logExit
}
function doitBackup() {
logEntry "$PARTITIONBASED_BACKUP"
checkImportantParameters
inspect4Backup
if ! checkSfdiskOK "$BOOT_DEVICENAME"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PARTITIONS_EXTEND_DISK_SIZE
exit $RC_COLLECT_PARTITIONS_FAILED
fi
commonChecks
if hasSpaces "$BACKUPPATH"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_CONTAINS_SPACES "$BACKUPPATH"
exitError $RC_MISC_ERROR
fi
if [[ ! -d "$BACKUPPATH" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DIR_TO_BACKUP_DOESNOTEXIST "$BACKUPPATH"
exitError $RC_MISSING_FILES
fi
if [[ $(getFsType "$BACKUPPATH") == "vfat" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MAX_4GB_LIMIT "$BACKUPPATH"
fi
if (( ! $EXCLUDE_DD )); then
if [[ ! -b $BOOT_DEVICENAME ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_SDCARD_FOUND "$BOOT_DEVICENAME"
exitError "$RC_PARAMETER_ERROR"
fi
if ! fdisk -l $BOOT_DEVICENAME | grep "${BOOT_PARTITION_PREFIX}1" > /dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_BOOT_PARTITION
exitError "$RC_SDCARD_ERROR"
fi
local partitionsFound
partitionsFound=$(fdisk -l "$BOOT_DEVICENAME" | grep "^/dev/$BOOT_PARTITION_PREFIX" | wc -l)
logItem "Found partitions on $BOOT_DEVICENAME: $partitionsFound"
if (( $partitionsFound > 2 )) && [[ "$BACKUPTYPE" != "$BACKUPTYPE_DD" && "$BACKUPTYPE" != "$BACKUPTYPE_DDZ" ]]; then
if ! (( $PARTITIONBASED_BACKUP || $IGNORE_ADDITIONAL_PARTITIONS )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MULTIPLE_PARTITIONS_FOUND
exitError $RC_SDCARD_ERROR
fi
if (( $IGNORE_ADDITIONAL_PARTITIONS && ! $PARTITIONBASED_BACKUP )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MULTIPLE_PARTITIONS_FOUND_BUT_2_PARTITIONS_SAVED_ONLY
fi
fi
fi
if [[ ! "$KEEPBACKUPS" =~ ^-?[0-9]+$ ]] || (( $KEEPBACKUPS < -1 || $KEEPBACKUPS > 365 || $KEEPBACKUPS == 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_KEEPBACKUP_INVALID "$KEEPBACKUPS" "-k"
mentionHelp
exitError "$RC_PARAMETER_ERROR"
fi
local t
local keepBackups
for t in "${POSSIBLE_TYPES_ARRAY[@]}"; do
local bt="${t^^}"
local v="KEEPBACKUPS_${bt}"
local keepOverwrite="${!v}"
if [[ ! $keepOverwrite =~ ^-?[0-9]+$ ]] || (( $keepOverwrite < -1 || $keepOverwrite > 365 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_KEEPBACKUP_INVALID "$keepOverwrite" "$v"
mentionHelp
exitError "$RC_PARAMETER_ERROR"
fi
done
if (( $SMART_RECYCLE )); then
if [[ ! "$SMART_RECYCLE_OPTIONS" =~ ^[0-9]+[[:space:]]*+[0-9]+[[:space:]]+[0-9]+[[:space:]]+[0-9]+$ ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SMART_RECYCLE_PARM_INVALID "" "$SMART_RECYCLE_OPTIONS"
mentionHelp
exitError "$RC_PARAMETER_ERROR"
fi
eval "SMART_RECYCLE_PARMS=( $SMART_RECYCLE_OPTIONS )"
logItem "SMART_RECYCLE_PARMS: ${SMART_RECYCLE_PARMS[*]}"
logItem "smart recycle parms: ${#SMART_RECYCLE_PARMS[@]}"
local sb
if (( $SMART_RECYCLE )); then
for sb in "${SMART_RECYCLE_PARMS[@]}"; do
if (( $sb > 365 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SMART_RECYCLE_PARM_INVALID "$sb" "$SMART_RECYCLE_OPTIONS"
mentionHelp
exitError $RC_PARAMETER_ERROR
fi
done
fi
fi
logItem "Backuptype selected: $BACKUPTYPE"
if [[ ! $BACKUPTYPE =~ ^(${POSSIBLE_TYPES})$ ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNKNOWN_BACKUPTYPE "$BACKUPTYPE"
mentionHelp
exitError $RC_PARAMETER_ERROR
fi
if [[ -n $TAR_COMPRESSION_TOOL ]]; then
if [[ "$BACKUPTYPE" != "$BACKUPTYPE_TAR" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_OPTION_TAR_COMPRESS_TOOL_NOT_SUPPORTED "$BACKUPTYPE"
mentionHelp
exitError $RC_PARAMETER_ERROR
fi
local i
i=$(getIndexInArray "$TAR_COMPRESSION_TOOL" "${TAR_COMPRESSION_TOOLS_SUPPORTED[@]}")
if (( $? )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNSUPPORTED_TAR_COMPRESS_TOOL "$TAR_COMPRESSION_TOOL"
exitError $RC_PARAMETER_ERROR
else
logItem "tar compression of $TAR_COMPRESSION_TOOL is supported. Using extension $TAR_COMPRESSION_EXTENSION ($i)"
fi
if ! which "$TAR_COMPRESSION_TOOL" &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL "$MSG_TAR_COMPRESS_TOOL_NOT_FOUND" "$TAR_COMPRESSION_TOOL"
exitError $RC_MISSING_COMMANDS
fi
fi
if [[ -n "$TELEGRAM_CHATID" && -z "$TELEGRAM_TOKEN" ]] || [[ -z "$TELEGRAM_CHATID" && -n "$TELEGRAM_TOKEN" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TELEGRAM_OPTIONS_INCOMPLETE
exitError $RC_PARAMETER_ERROR
fi
if [[ -n "$TELEGRAM_CHATID" && -n "$TELEGRAM_TOKEN" ]]; then
if ! which jq &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_INSTALLED_FILE "jq" "jq"
exitError $RC_MISSING_COMMANDS
fi
local invalidNotification
invalidNotification="$(tr -d "$TELEGRAM_POSSIBLE_NOTIFICATIONS" <<< "$TELEGRAM_NOTIFICATIONS")"
if [[ -n "$invalidNotification" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TELEGRAM_INVALID_NOTIFICATION "$invalidNotification" "$TELEGRAM_POSSIBLE_NOTIFICATIONS"
exitError $RC_PARAMETER_ERROR
fi
fi
if [[ -n "$PUSHOVER_USER" && -z "$PUSHOVER_TOKEN" ]] || [[ -z "$PUSHOVER_USER" && -n "$PUSHOVER_TOKEN" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PUSHOVER_OPTIONS_INCOMPLETE
exitError $RC_PARAMETER_ERROR
fi
if [[ -n "$PUSHOVER_USER" && -n "$PUSHOVER_TOKEN" ]]; then
if ! which jq &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_INSTALLED_FILE "jq" "jq"
exitError $RC_MISSING_COMMANDS
fi
local invalidNotification
invalidNotification="$(tr -d "$PUSHOVER_POSSIBLE_NOTIFICATIONS" <<< "$PUSHOVER_NOTIFICATIONS")"
if [[ -n "$invalidNotification" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PUSHOVER_INVALID_NOTIFICATION "$invalidNotification" "$PUSHOVER_POSSIBLE_NOTIFICATIONS"
exitError $RC_PARAMETER_ERROR
fi
fi
if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
local invalidNotification
invalidNotification="$(tr -d "$SLACK_POSSIBLE_NOTIFICATIONS" <<< "$SLACK_NOTIFICATIONS")"
if [[ -n "$invalidNotification" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SLACK_INVALID_NOTIFICATION "$invalidNotification" "$SLACK_POSSIBLE_NOTIFICATIONS"
exitError $RC_PARAMETER_ERROR
fi
fi
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_DD" || "$BACKUPTYPE" == "$BACKUPTYPE_DDZ" ]]; then
(( $DD_WARNING )) && writeToConsole $MSG_LEVEL_MINIMAL $MSG_DD_WARNING
fi
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_RSYNC" ]]; then
if ! which rsync &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_INSTALLED_FILE "rsync" "rsync"
exitError $RC_MISSING_COMMANDS
fi
disableACLsIfRequested
if ! supportsHardlinks "$BACKUPPATH"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_HARDLINK_ERROR "$BACKUPPATH" "$RC_MISC_ERROR"
exitError $RC_MISC_ERROR
fi
local fs
fs="$(getFsType "$BACKUPPATH")"
logItem "Filesystem: $fs"
if ! supportsFileAttributes "$BACKUPPATH"; then
if [[ $fs =~ ^nfs* ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_FILEATTRIBUTE_RIGHTS "$(findMountPath "$BACKUPPATH")" "$fs"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_FILEATTRIBUTESUPPORT "$fs" "$(findMountPath "$BACKUPPATH")"
fi
exitError $RC_MISC_ERROR
fi
if ! supportsSymlinks "$BACKUPPATH"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILESYSTEM_INCORRECT "$(findMountPath "$BACKUPPATH")" "softlinks"
exitError $RC_MISC_ERROR
fi
if hasDefaultACLs "$BACKUPPATH"; then
logItem "DEFAULT ACLs detected on $BACKUPPATH"
fi
# if (( ! $RSYNC_BACKUP_OPTION_EXCLUDE_ACLS )); then
# if hasDefaultACLs "$BACKUPPATH"; then
# writeToConsole $MSG_LEVEL_MINIMAL $MSG_BACKUP_DIRECTORY_HAS_DEFAULT_ACLS "$(findMountPath "$BACKUPPATH")"
# exitError $RC_MISC_ERROR
# fi
# fi
local rsyncVersion
rsyncVersion=$(rsync --version | head -n 1 | awk '{ print $3 }')
logItem "rsync version: $rsyncVersion"
# Decimals are not supported. Either use integers only, or use bc or awk to compare.
#shellcheck disable=SC2072
if (( $PROGRESS && $INTERACTIVE )) && [[ "$rsyncVersion" < "3.1" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RSYNC_DOES_NOT_SUPPORT_PROGRESS "$rsyncVersion"
exitError $RC_PARAMETER_ERROR
fi
fi
if [[ -z "$STARTSERVICES" && -z "$STOPSERVICES" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_START_STOP
exitError $RC_PARAMETER_ERROR
fi
if [[ -z "$STARTSERVICES" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_START_OR_STOP "-a"
exitError $RC_PARAMETER_ERROR
fi
if [[ -z "$STOPSERVICES" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_START_OR_STOP "-o"
exitError $RC_PARAMETER_ERROR
fi
if [[ ( -n "$STARTSERVICES" || -n "$STOPSERVICES" ) && ! ( -n "$STARTSERVICES" && -n "$STOPSERVICES" ) ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_START_OR_STOP
exitError $RC_PARAMETER_ERROR
fi
if (( $PROGRESS && $INTERACTIVE )); then
if ! which pv &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_INSTALLED_FILE "pv" "pv"
exitError ""RC_MISSING_COMMANDS
fi
fi
if (( $PARTITIONBASED_BACKUP )); then
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_DD" || "$BACKUPTYPE" == "$BACKUPTYPE_DDZ" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DD_BACKUP_NOT_POSSIBLE_FOR_PARTITIONBASED_BACKUP
exitError "$RC_PARAMETER_ERROR"
fi
if (( $TAR_BOOT_PARTITION_ENABLED )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_TAR_BOOT_BACKUP_NOT_POSSIBLE
exitError "$RC_PARAMETER_ERROR"
fi
if (( ! $FAKE )); then
collectPartitions
checksForPartitionBasedBackup
fi
fi
if (( $LINK_BOOTPARTITIONFILES )) && [[ "$BACKUPTYPE" != "$BACKUPTYPE_DD" ]] && [[ "$BACKUPTYPE" != "$BACKUPTYPE_DDZ" ]]; then
touch "$BACKUPPATH/47.$$"
cp -l "$BACKUPPATH/47.$$" "$BACKUPPATH/11.$$" &>/dev/null
local rc=$?
rm "$BACKUPPATH/47.$$" &>/dev/null
rm "$BACKUPPATH/11.$$" &>/dev/null
if [[ $rc != 0 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_USE_HARDLINKS "$BACKUPPATH" "$rc"
exitError "$RC_LINK_FILE_FAILED"
fi
fi
if (( $SYSTEMSTATUS )) && ! which lsof &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_INSTALLED_FILE "lsof" "lsof"
exitError "$RC_MISSING_COMMANDS"
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_USING_BACKUPPATH "$BACKUPPATH" "$(getFsType "$BACKUPPATH")"
if (( ! $SKIPLOCALCHECK )); then
if ! isPathMounted "$BACKUPPATH"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_DEVICEMOUNTED "$BACKUPPATH"
exitError "$RC_MISC_ERROR"
fi
# check if backup partition is a local mount
logItem "BOOT_DEVICENAME: $BOOT_DEVICENAME"
local lsblkResult
lsblkResult="$(lsblk -l -o name,mountpoint | grep "${BACKUPPATH}" | grep "$BOOT_DEVICENAME")"
logItem "lsblkResult: $lsblkResult"
if [[ -n "$lsblkResult" ]]; then
local di
mapfile -d " " -t di < <( deviceInfo "/dev/$lsblkResult" )
logItem "di: ${di[*]}"
if [[ "$BOOT_DEVICE" == "${di[0]}" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_DEVICEMOUNTED "$BACKUPPATH"
exitError $RC_MISC_ERROR
fi
fi
fi
# all tests succeeded
BACKUPPATH_PARAMETER="$BACKUPPATH"
BACKUPPATH="$BACKUPPATH/$HOSTNAME"
if [[ ! -d "$BACKUPPATH" ]]; then
if ! mkdir -p "${BACKUPPATH}" &>>"$LOG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_CREATE_DIRECTORY "$BACKUPPATH"
exitError "$RC_CREATE_ERROR"
fi
fi
logCommand "ls -1 ${BACKUPPATH}"
# Note: The new optional part (@.*?)* in the regex below saves possible older backups without the OS release in the name from being deleted as nonRaspiGeneratedDirs!
local nonRaspiGeneratedDirs
# Double quote to prevent globbing and word splitting.
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
#shellcheck disable=SC2010,SC2086
nonRaspiGeneratedDirs=$(ls -1 ${BACKUPPATH} | grep -Ev "$HOSTNAME(@.*?)*\-($POSSIBLE_BACKUP_TYPES_REGEX)\-backup\-([0-9]){8}.([0-9]){6}" | grep -E "\-backup\-" | wc -l)
logItem "nonRaspiGeneratedDirs: $nonRaspiGeneratedDirs"
if (( $nonRaspiGeneratedDirs > 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_INVALID_BACKUPNAMES_DETECTED "$nonRaspiGeneratedDirs" "$BACKUPPATH"
exitError "$RC_BACKUP_DIRNAME_ERROR"
fi
# just inform about options enabled
if [[ "$BACKUPTYPE" != "$BACKUPTYPE_DD" && "$BACKUPTYPE" != "$BACKUPTYPE_DDZ" ]]; then
if (( $LINK_BOOTPARTITIONFILES )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_LINK_BOOTPARTITIONFILES
fi
fi
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_DD" || "$BACKUPTYPE" == "$BACKUPTYPE_DDZ" ]]; then
if (( $DD_BACKUP_SAVE_USED_PARTITIONS_ONLY )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SAVING_USED_PARTITIONS_ONLY
fi
fi
if (( ! $RESTORE && $REBOOT_SYSTEM && ! $FAKE )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_REBOOT_SYSTEM
fi
# now either execute a SR dryrun or start backup
if (( $SMART_RECYCLE_DRYRUN && $SMART_RECYCLE )); then # just apply backup strategy to test smart recycle
writeToConsole $MSG_LEVEL_MINIMAL $MSG_APPLYING_BACKUP_STRATEGY_ONLY "$BACKUPPATH"
applyBackupStrategy
rc=0
else
if (( $SMART_RECYCLE && !$SMART_RECYCLE_DRYRUN )); then
writeToConsole $MSG_LEVEL_DETAILED $MSG_SMART_RECYCLE_WILL_BE_APPLIED
fi
backup
fi
logExit
}
function listDeviceInfo() { # device (/dev/sda)
logEntry "$1"
local result
result="$(IFS='' lsblk $1 --tree -o NAME,SIZE,FSTYPE,LABEL)"
echo "$result"
logExit
}
function getPartitionTable() { # device
logEntry "$1"
local result table
# Possible results of partprobe
# See https://stackoverflow.com/questions/26873289/how-to-check-a-disk-for-partitions-for-use-in-a-script-in-linux
# /dev/sdb: gpt partitions 1 -> partition table exists and one partition
# /dev/sdb: gpt partitions -> partition table exists but no partition
# -> no partition table
if [[ -n "$(partprobe -d -s "$1" | cut -f 4 -d ' ')" ]]; then
table="$(IFS='' parted "$1" unit MB p 2>>"$LOG_FILE" | sed -r '/^($|[MSDP])/d')"
if (( $(wc -l <<< "$table") < 2 )); then
result=""
else
result="$table"
fi
else
result=""
fi
echo "$result"
logExit "$result"
}
function checkAndSetBootPartitionFiles() { # directory extension
logEntry "$1 - $2"
local prefix="$1/$2"
DD_FILE="$prefix.$BOOT_DD_EXT"
logItem "DD_FILE: $DD_FILE"
TAR_FILE="$prefix.$BOOT_TAR_EXT"
logItem "TAR_FILE: $TAR_FILE"
SF_FILE="$prefix.sfdisk"
logItem "SF_FILE: $SF_FILE"
MBR_FILE="$prefix.mbr"
logItem "MBR_FILE: $MBR_FILE"
local errorCnt=0
if [[ "$BACKUPTYPE" != "$BACKUPTYPE_DD" && "$BACKUPTYPE" != "$BACKUPTYPE_DDZ" ]]; then
if [[ ! -e "$SF_FILE" ]]; then
logItem "$SF_FILE not found"
(( errorCnt++ ))
else
logItem "$(<"$SF_FILE")"
fi
if [[ ! -e "$DD_FILE" && ! -e "$TAR_FILE" ]]; then
logItem "$DD_FILE/$TAR_FILE not found"
(( errorCnt++ ))
fi
if [[ ! -e "$MBR_FILE" ]]; then
logItem "$MBR_FILE not found"
(( errorCnt++ ))
fi
fi
logExit "$errorCnt"
return $errorCnt
}
function findNonpartitionBackupBootAndRootpartitionFiles() {
logEntry
# search precedence for root files
# 1) if directory search corresponding file in directory
# 2) if file passed just use this file
if [[ -f "$RESTOREFILE" || $BACKUPTYPE == "$BACKUPTYPE_RSYNC" ]]; then
ROOT_RESTOREFILE="$RESTOREFILE"
else
logItem "${RESTOREFILE}/${HOSTNAME}*-*-backup*"
# Double quote to prevent globbing and word splitting.
#shellcheck disable=SC2086
ROOT_RESTOREFILE="$(ls ${RESTOREFILE}/${HOSTNAME}*-*-backup*)"
logItem "ROOT_RESTOREFILE: $ROOT_RESTOREFILE"
if [[ -z "$ROOT_RESTOREFILE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_ROOTBACKUPFILE_FOUND "$BACKUPTYPE"
exitError $RC_MISC_ERROR
fi
fi
writeToConsole $MSG_LEVEL_DETAILED $MSG_USING_ROOTBACKUPFILE "$ROOT_RESTOREFILE"
# search precedence for boot files
# 1) individual backup dir and no date (added in this version)
# 2) backup dir and date (added when boot backup all the time was added in 0.6.1.1)
# 3) backup dir and no date (initial location when ony one single backup was created, pre 0.6.1.1)
local bootpartitionDirectory=( "$RESTOREFILE" "$BASE_DIR" "$BASE_DIR" )
local bootpartitionExtension=( "$HOSTNAME-backup" "$HOSTNAME-backup-$DATE" "$HOSTNAME-backup" )
local i=0
for (( i=0; i<${#bootpartitionDirectory[@]}; i++ )); do
checkAndSetBootPartitionFiles "${bootpartitionDirectory[$i]}" "${bootpartitionExtension[$i]}"
local errorCnt=$?
if [[ $errorCnt == 0 ]]; then
writeToConsole $MSG_LEVEL_DETAILED $MSG_BOOTPATITIONFILES_FOUND "${bootpartitionDirectory[$i]}" "${bootpartitionExtension[$i]}"
logExit
return
fi
done
for (( i=0; i<${#bootpartitionDirectory[@]}; i++ )); do
writeToConsole $MSG_LEVEL_MINIMAL $MSG_BOOTPATITIONFILES_NOT_FOUND "${bootpartitionDirectory[$i]}" "${bootpartitionExtension[$i]}"
done
logExit
exitError "$RC_MISC_ERROR"
}
function initRestoreVariables () {
logEntry
if [[ -z $(fdisk -l "$RESTORE_DEVICE" 2>/dev/null) ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_RESTOREDEVICE_FOUND "$RESTORE_DEVICE"
exitError "$RC_PARAMETER_ERROR"
fi
BOOT_PARTITION="$(createPartitionName "$RESTORE_DEVICE" 1)"
logItem "BOOT_PARTITION : $BOOT_PARTITION"
ROOT_PARTITION_DEFINED=1
if [[ -z "$ROOT_PARTITION" ]]; then
ROOT_PARTITION="$(createPartitionName "$RESTORE_DEVICE" 2)"
ROOT_PARTITION_DEFINED=0
else
if [[ ! -e "$ROOT_PARTITION" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_ROOT_PARTTITION_NOT_FOUND "$ROOT_PARTITION"
exitError "$RC_PARAMETER_ERROR"
fi
fi
logItem "ROOT_PARTITION : $ROOT_PARTITION"
logExit
}
function restoreNonPartitionBasedBackup() {
logEntry
initRestoreVariables
if (( ! $SKIP_SFDISK )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_REPARTITION_WARNING "$RESTORE_DEVICE"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SKIP_CREATING_PARTITIONS
fi
if (( ! $ROOT_PARTITION_DEFINED )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_WARN_RESTORE_DEVICE_OVERWRITTEN "$RESTORE_DEVICE"
current_partition_table="$(listDeviceInfo "$RESTORE_DEVICE")"
if [[ -n "$current_partition_table" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CURRENT_PARTITION_TABLE "$RESTORE_DEVICE"
logItem "$current_partition_table"
echo "$current_partition_table"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_PARTITION_TABLE_DEFINED "$RESTORE_DEVICE"
fi
else
if isSpecialBlockDevice "$ROOT_DEVICE"; then
ROOT_DEVICE=$(sed -E 's/p[0-9]+$//' <<< "$ROOT_PARTITION")
else
ROOT_DEVICE=$(sed -E 's/[0-9]+$//' <<< "$ROOT_PARTITION")
fi
if [[ $ROOT_DEVICE != "$RESTORE_DEVICE" ]]; then
current_partition_table="$(listDeviceInfo "$ROOT_DEVICE")"
if [[ -n "$current_partition_table" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CURRENT_PARTITION_TABLE "$ROOT_DEVICE"
logItem "$current_partition_table"
echo "$current_partition_table"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_PARTITION_TABLE_DEFINED "$ROOT_DEVICE"
if (( $SKIP_SFDISK )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_PARTITION "$ROOT_DEVICE"
exitError $RC_MISSING_PARTITION
fi
fi
fi
fi
if (( ! $SKIP_SFDISK )); then
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_DD" || "$BACKUPTYPE" == "$BACKUPTYPE_DDZ" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_REPARTITION_WARNING "$BOOT_PARTITION"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_WARN_BOOT_PARTITION_OVERWRITTEN "$BOOT_PARTITION"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_WARN_ROOT_PARTITION_OVERWRITTEN "$ROOT_PARTITION"
fi
fi
if ! askYesNo; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_ABORTED
exitError $RC_RESTORE_FAILED
fi
restoreNormalBackupType
logExit "$rc"
}
function restorePartitionBasedBackup() {
logEntry
local partition sourceSDSize targetSDSize
if [[ "${RESTOREFILE: -1}" != "/" ]]; then
RESTOREFILE="$RESTOREFILE/"
fi
if mount | grep -q "$RESTORE_DEVICE"; then
logItem "Umounting partitions on $RESTORE_DEVICE"
logItem "$(mount | grep "$RESTORE_DEVICE")"
local dev
while read -r dev; do echo "$dev" | cut -d ' ' -f 1; done < <(mount | grep "$RESTORE_DEVICE") | xargs umount
logItem "$(mount | grep "$RESTORE_DEVICE")"
fi
current_partition_table="$(listDeviceInfo "$RESTORE_DEVICE")"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CURRENT_PARTITION_TABLE "$RESTORE_DEVICE"
logItem "$current_partition_table"
echo "$current_partition_table"
if [[ "${PARTITIONS_TO_RESTORE}" == "$PARTITIONS_TO_BACKUP_ALL" ]]; then
local partitions
partitions=( "$(collectAvailableBackupPartitions "$RESTOREFILE" )" )
local partitionsString="${partitions[*]}"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORING_PARTITIONS "\"$partitionsString\"" "$RESTORE_DEVICE"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORING_PARTITIONS "\"$PARTITIONS_TO_RESTORE\"" "$RESTORE_DEVICE"
fi
if ! askYesNo; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_ABORTED
exitError "$RC_RESTORE_FAILED"
fi
if (( $NO_YES_QUESTION )); then
echo "Y"
fi
initRestoreVariables
MNT_POINT="$TEMPORARY_MOUNTPOINT_ROOT"
if isMounted "$MNT_POINT"; then
logItem "$MNT_POINT mounted - unmouting"
if ! umount -f "$MNT_POINT" &>>"$LOG_FILE"; then
assertionFailed $LINENO "Unable to unmount $MNT_POINT"
fi
fi
START_TIME="$(date +%s)"
logItem "Creating mountpoint $MNT_POINT"
if ! mkdir -p "$MNT_POINT" &>>"$LOG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_CREATE_DIRECTORY "$MNT_POINT"
exitError "$RC_CREATE_ERROR"
fi
# handle partitions
local partitionsToRestore=("${PARTITIONS_TO_RESTORE[@]}")
if (( ${#partitionsToRestore[@]} > 0 )); then
if [[ ! -e "$SF_FILE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_NOT_FOUND "$SF_FILE"
exitError $RC_MISSING_FILES
fi
logItem "SF_FILE: $SF_FILE$NL$(<"$SF_FILE")"
if [[ ! -e "$MBR_FILE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_NOT_FOUND "$MBR_FILE"
exitError $RC_MISSING_FILES
fi
if [[ ! -e "$BLKID_FILE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_NOT_FOUND "$BLKID_FILE"
exitError $RC_MISSING_FILES
fi
logItem "BLKID_FILE: $BLKID_FILE$NL$(<"$BLKID_FILE")"
if [[ ! -e "$PARTED_FILE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_NOT_FOUND "$PARTED_FILE"
exitError $RC_MISSING_FILES
fi
logItem "PARTED_FILE: $PARTED_FILE$NL$(<"$PARTED_FILE")"
partitionRestoredeviceIfRequested
local partitionBackupFile
# Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a.
#shellcheck disable=SC2206
local partitionsToRestore=(${PARTITIONS_TO_RESTORE[@]})
local partitionsRestored=()
logItem "RESTOREFILE_BACKUP_BOOT_PARTITION_PREFIX: ${RESTOREFILE}${BACKUP_BOOT_PARTITION_PREFIX}"
for partitionBackupFile in "${RESTOREFILE}${BACKUP_BOOT_PARTITION_PREFIX}"*; do
logItem "partitionBackupFile: $partitionBackupFile"
local partitionNo
partitionNo="$(grep -Eo "[0-9]+(\.($BACKUPTYPE_TAR|$BACKUPTYPE_TGZ|$TAR_COMPRESSION_EXTENSIONS_SUPPORTED_GREP))?$" <<< "$partitionBackupFile" | sed -E 's/(\.tar)?\..+$//' )" # delete trailing extension and optional .tar
logItem "Found partition no: $partitionNo"
if [[ -z "$partitionNo" ]]; then
assertionFailed $LINENO "Unable to retrieve partition number from $partitionBackupFile"
fi
if [[ "${PARTITIONS_TO_RESTORE}" == "$PARTITIONS_TO_BACKUP_ALL" ]] || containsElement "$partitionNo" "${partitionsToRestore[@]}"; then
restorePartitionBasedPartition "$partitionBackupFile"
partitionsRestored+=("$partitionNo")
else
local skippedPartition="${RESTORE_DEVICE}${partitionNo}"
if isSpecialBlockDevice "${RESTORE_DEVICE}"; then
skippedPartition="${RESTORE_DEVICE}p${partitionNo}"
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SKIP_PARTITION_RESTORE "$skippedPartition"
fi
done
updateUUIDs
synchronizeCmdlineAndfstab
if ! containsElement "1" "${partitionsRestored[@]}" || ! containsElement "2" "${partitionsRestored[@]}"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PARTITION_RESTORE_NO_BOOT_POSSIBLE
fi
fi
END_TIME="$(date +%s)"
logCommand "fdisk -l $RESTORE_DEVICE"
logExit
}
# MBR partition blkid examples:
# /dev/mmcblk0p1: LABEL="RECOVERY" UUID="B383-E246" TYPE="vfat"
# /dev/mmcblk0p3: LABEL="SETTINGS" UUID="9b35a9e6-d81f-4eff-9096-633297a5410b" TYPE="ext4"
# /dev/mmcblk0p5: LABEL="boot0" UUID="035A-9F64" TYPE="vfat"
# /dev/mmcblk0p6: LABEL="root" UUID="53df0f2a-3f9c-4b76-afc9-17c60989988d" TYPE="ext4"
# /dev/mmcblk0p7: LABEL="boot" UUID="56A8-F127" TYPE="vfat"
# /dev/mmcblk0p8: LABEL="root0" UUID="aa2fec4f-70ac-49b5-bc59-be0cf74b76d7" TYPE="ext4"
# GPT partition blkid examples:
# /dev/mmcblk0p1: LABEL_FATBOOT="bootfs" LABEL="bootfs" UUID="EC36-4DE1" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="Microsoft basic data" PARTUUID="df06e23f-4069-4fb4-a5f2-65bd5c9ac229"
function getBackupPartitionLabel() { # partition
logEntry "$1"
local partition=$1
local blkid
local label=""
blkid=$(grep "$partition" "$BLKID_FILE")
logItem "BLKID: $1 - $blkid"
regexFormatLineLabel="^.* LABEL=\"([^\"]+)\".*$"
if [[ $blkid =~ $regexFormatLineLabel ]]; then
label=${BASH_REMATCH[1]}
# else
# label=$(sed -E 's/\/dev\///' <<< $partition) # strip /dev/
fi
echo "$label"
logExit "$label"
}
# parted -l -m
#BYT;
#/dev/mmcblk0:7969MB:sd/mmc:512:512:msdos:SD SD08G;
#1:1049kB:857MB:856MB:fat32::lba;
#2:860MB:7936MB:7076MB:::;
#5:1845MB:1895MB:49.3MB:fat32::lba;
#6:1896MB:3899MB:2003MB:::;
#7:3901MB:4438MB:537MB:ext4::;
#8:4442MB:4505MB:62.9MB:fat16::lba;
#9:4509MB:7926MB:3417MB:ext4::;
#3:7936MB:7969MB:33.6MB:ext4::;
function extractDataFromBackupPartedFile() { # partition fieldnumber
logEntry "$1 $2"
local partitionNo
partitionNo=$(sed -E "s%${BACKUP_BOOT_PARTITION_PREFIX}%%" <<< "$1")
logItem "PartitionNo: $partitionNo"
local parted element
logItem "PARTED: $1 - $(<"$PARTED_FILE")"
parted=$(grep "^$partitionNo" "$PARTED_FILE")
logItem "PARTED: $1 - $parted"
element=$(cut -d ":" -f "$2" <<< "$parted")
echo "$element"
logExit "$element"
}
function getBackupPartitionFilesystemUsed() { # partition
logEntry "$1"
mount $1 /mnt
local used
used=$(df "$1" -B 1 | tail -1 | awk '{ printf "%.0f\n", $3 }')
umount /mnt
echo "$used"
logExit "$used"
}
function getBackupPartitionFilesystemSize() { # partition
logEntry "$1"
local size
size=$(df "$1" -B 1 | tail -1 | awk '{ printf "%.0f\n", $3 + $4 }')
echo "$size"
logExit "$size"
}
function getBackupPartitionFilesystem() { # partition
logEntry "$1"
local fileSystem
fileSystem=$(extractDataFromBackupPartedFile "$1" "5")
echo "$fileSystem"
logExit "$fileSystem"
}
function getPartitionBootFilesystem() { # partition_no
logEntry "$1"
local partitionNo=$1
logItem "BOOT_DEVICENAME: $BOOT_DEVICENAME"
local parted format
logItem "PARTED: $1 - $(parted -m "$BOOT_DEVICENAME" print 2>/dev/null)"
parted=$(grep "^${partitionNo}:" <(parted -m "$BOOT_DEVICENAME" print 2>/dev/null))
logItem "PARTED: $1 - $parted"
format=$(cut -d ":" -f 5 <<< "$parted")
echo "$format"
logExit "$format"
}
function lastUsedPartitionByte() { # device
logEntry "$1"
local partitionregex="/dev/.*[p]?([0-9]+).*start=[^0-9]*([0-9]+).*size=[^0-9]*([0-9]+).*(Id|type)=[^0-9a-z]*([0-9a-z]+)"
local lastUsedPartitionByte=0
local line
while read -r line; do
if [[ -z $line ]]; then
continue
fi
logItem "$line"
if [[ $line =~ $partitionregex ]]; then
local p=${BASH_REMATCH[1]}
local start=${BASH_REMATCH[2]}
local size=${BASH_REMATCH[3]}
local id=${BASH_REMATCH[5]}
logItem "$p - $start - $size - $id"
if [[ $id == 85 || $id == 5 ]]; then
continue
fi
if (( $start > 0 )); then
lastUsedPartitionByte=$((start+size))
fi
fi
done < <(sfdisk -d "$1" 2>>"$LOG_FILE")
(( lastUsedPartitionByte*=512 ))
echo "$lastUsedPartitionByte"
logExit "$lastUsedPartitionByte"
}
function makeFilesystemAndLabel() { # partition filesystem label
logEntry "$1 $2"
local partition="$1"
local partitionFilesystem="$2"
local partitionLabel="$3"
if [[ ! "$partitionFilesystem" =~ $SUPPORTED_PARTITIONBACKUP_PARTITIONTYPE_REGEX ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNSUPPORTED_FILESYSTEM_FORMAT "$partitionFilesystem" "$partition"
exitError $RC_MISC_ERROR
fi
logItem "partitionFilesystem: \"$partitionFilesystem\""
local fs="$partitionFilesystem"
local fatSize=""
local fatCmd=""
local swapDetected=0
if [[ "$partitionFilesystem" =~ ^fat.* ]]; then
fs="vfat"
# See if you can use ${variable//search/replace} instead
#shellcheck disable=SC2001
fatSize=$(sed 's/fat//' <<< "$partitionFilesystem")
fatCmd="-I -F $fatSize"
logItem "fs: $fs - fatSize: $fatSize - fatCmd: $fatCmd"
cmd="mkfs -t $fs $fatCmd"
elif [[ "$partitionFilesystem" =~ swap ]]; then
cmd="mkswap"
swapDetected=1
logItem "Swap partition"
else
logItem "Normal partition with $partitionFilesystem"
if [[ $partitionFilesystem == "btrfs" ]]; then
check4RequiredCommands btrfs
cmd="mkfs.btrfs -f"
elif [[ $partitionFilesystem == "f2fs" ]]; then
check4RequiredCommands f2fs
if [[ -n $partitionLabel ]]; then
cmd="mkfs.f2fs -f -l $partitionLabel "
else
cmd="mkfs.f2fs -f"
fi
else
cmd="mkfs -t $fs"
fi
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FORMATTING "$partition" "$partitionFilesystem"
logItem "$cmd $partition"
$cmd "$partition" &>>"$LOG_FILE"
rc=$?
if (( $rc )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MKFS_FAILED "$cmd $partition" "$rc"
exitError $RC_MISC_ERROR
fi
if (( ! $swapDetected )); then
# Keep SUPPORTED_PARTITIONBACKUP_PARTITIONTYPE_REGEX in sync
if [[ -n $partitionLabel ]]; then
writeToConsole $MSG_LEVEL_DETAILED $MSG_LABELING "$partition" "$partitionLabel"
case $partitionFilesystem in
ext2|ext3|ext4) cmd="e2label"
;;
fat16|fat32) cmd="dosfslabel"
;;
btrfs) cmd="btrfs filesystem label"
;;
f2fs) cmd=": noop until f2fs 1.5 is available on Raspberries # "
;;
esac
logItem "$cmd $partition $partitionLabel"
$cmd "$partition" "$partitionLabel" &>>"$LOG_FILE"
rc=$?
if (( $rc )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_LABELING_FAILED "$cmd $partition $partitionLabel" "$rc"
exitError "$RC_LABEL_ERROR"
fi
else
logItem "Partition $partition not labeled"
fi
fi
logExit
}
function restorePartitionBasedPartition() { # restorefile
logEntry "$1"
rc=0
local verbose zip partitionLabel cmd restoreFile restorePartition
restoreFile="$1"
restorePartition="$(basename "$restoreFile")"
restorePartition="$(sed -E 's%(\.tar)?\..+$%%' <<< ${restorePartition})" # remove backup extension and optional .tar
logItem "RestorePartition: $restorePartition"
local partitionNumber
partitionNumber="$(sed -E 's%.+([0-9]+)$%\1%' <<< "$restorePartition")" # extract partition number
logItem "Partitionnumber: $partitionNumber"
partitionLabel=$(getBackupPartitionLabel "$restorePartition")
partitionFilesystem=$(getBackupPartitionFilesystem "$restorePartition")
logItem "Label: $partitionLabel - Filesystem: $partitionFilesystem"
local restoreDevice
restoreDevice=${RESTORE_DEVICE%dev%%}
restoreDevice="$(createPartitionName "$restoreDevice")"
logItem "RestoreDevice: $restoreDevice"
local mappedRestorePartition
# See if you can use ${variable//search/replace} instead
#shellcheck disable=SC2001
mappedRestorePartition=$(sed "s%${BACKUP_BOOT_PARTITION_PREFIX}%${restoreDevice}%" <<< "$restorePartition")
if [[ ! "$partitionFilesystem" =~ $SUPPORTED_PARTITIONBACKUP_PARTITIONTYPE_REGEX ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNSUPPORTED_FILESYSTEM_FORMAT "$partitionFilesystem" "$mappedRestorePartition"
exitError "$RC_MISC_ERROR"
elif [[ -n "$partitionFilesystem" ]]; then
logItem "partitionFilesystem: \"$partitionFilesystem\""
if (( ! $SKIP_FORMAT )); then
makeFilesystemAndLabel "$mappedRestorePartition" "$partitionFilesystem" "$partitionLabel"
fi
if [[ ! "$partitionFilesystem" =~ swap ]]; then
logItem "mount $mappedRestorePartition $MNT_POINT"
mount "$mappedRestorePartition" "$MNT_POINT"
logItem "Restoring file $restoreFile"
if (( $SKIP_FORMAT )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SYNCING_PARTITIONFILE "$mappedRestorePartition"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORING_PARTITIONFILE "$mappedRestorePartition"
fi
(( $VERBOSE )) && verbose="v" || verbose=""
logItem "Backuptype: $BACKUPTYPE"
rc=$RC_NATIVE_BACKUP_FAILED
case $BACKUPTYPE in
"$BACKUPTYPE_TAR"|"$BACKUPTYPE_TGZ")
local archiveFlags=""
if [[ -n $fatSize ]]; then
local archiveFlags="--same-owner --same-permissions --numeric-owner ${TAR_RESTORE_ADDITIONAL_OPTIONS}" # fat32 doesn't know about this
fi
if ! pushd "$MNT_POINT" &>>"$LOG_FILE"; then
assertionFailed $LINENO "push to $MNT_POINT failed"
fi
[[ "$BACKUPTYPE" == "$BACKUPTYPE_TGZ" ]] && zip="z" || zip=""
if (( $PROGRESS && $INTERACTIVE )); then
cmd="pv -f $restoreFile | tar ${archiveFlags} -x${verbose}${zip}f -"
else
cmd="tar ${archiveFlags} -x${verbose}${zip}f \"$restoreFile\""
fi
executeTar "$cmd"
rc=$?
popd &>>"$LOG_FILE"
;;
"$BACKUPTYPE_RSYNC")
local archiveFlags="aH" # -a <=> -rlptgoD, H = preserve hardlinks
[[ -n $fatSize ]] && archiveFlags="rltD" # no Hopg flags for fat fs
cmdParms="--numeric-ids ${RSYNC_BACKUP_OPTIONS} -${archiveFlags}X$verbose \"$restoreFile/\" $MNT_POINT"
if (( $PROGRESS && $INTERACTIVE )); then
cmd="rsync --info=progress2 $cmdParms"
else
cmd="rsync $cmdParms"
fi
executeRsync "$cmd"
rc=$?
;;
*) logItem "Invalid backupo type $BACKUPTYPE found"
assertionFailed $LINENO "Invalid backup type $BACKUPTYPE detected"
;;
esac
if [[ $rc != 0 ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_PROGRAM_ERROR "$BACKUPTYPE" "$rc"
exitError "$RC_NATIVE_RESTORE_FAILED"
fi
sleep 1s # otherwise umount fails
umountPartition "$mappedRestorePartition"
if isMounted "$MNT_POINT"; then
logItem "umount $MNT_POINT"
if ! umount -f "$MNT_POINT" &>>"$LOG_FILE"; then
assertionFailed $LINENO "Unable to umount $MNT_POINT"
fi
fi
writeToConsole $MSG_LEVEL_DETAILED $MSG_RESTORING_FILE_PARTITION_DONE "$mappedRestorePartition"
executeFilesystemCheck "$mappedRestorePartition"
fi # is not swap partition
else
assertionFailed $LINENO "This error should not occur"
fi
logExit
}
function executeFilesystemCheck { # partition
logEntry "$1"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PARTITION_CHECK_EXECUTED "$1"
fsck -fpv "$1" &>>"$LOG_FILE"
rc_fsck=$?
logItem "fsck rc: $rc_fsck"
if (( $rc_fsck > 1 )); then # 1: => Filesystem errors corrected
writeToConsole $MSG_LEVEL_MINIMAL $MSG_PARTITION_CHECK_FAILED "$1" "$rc_fsck"
exitError $RC_NATIVE_RESTORE_FAILED
fi
logExit
}
function doitRestore() {
logEntry
local current_partition_table
logCommand "blkid"
commonChecks
if hasSpaces "$RESTOREFILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_CONTAINS_SPACES "$RESTOREFILE"
exitError $RC_MISC_ERROR
fi
if [[ ! -d "$RESTOREFILE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_DIRECTORY_NO_DIRECTORY "$RESTOREFILE"
exitError $RC_MISSING_FILES
fi
if [[ ! -b "$RESTORE_DEVICE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_DEVICE_NOT_VALID "$RESTORE_DEVICE"
exitError $RC_RESTORE_IMPOSSIBLE
fi
logItem "ls $RESTOREFILE$NL$(ls "$RESTOREFILE")"
local regex=""
for type in $POSSIBLE_TYPES; do
[[ -z $regex ]] && regex="$type" || regex="$regex|$type"
done
regex="\-($regex)\-backup\-"
logItem "Basename: $(basename "$RESTOREFILE")"
logItem "regex: $regex"
if [[ ! $(basename "$RESTOREFILE") =~ $regex ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_DIRECTORY_INVALID "$RESTOREFILE"
exitError $RC_MISSING_FILES
fi
if isMounted "$RESTORE_DEVICE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_DEVICE_MOUNTED "$RESTORE_DEVICE"
exitError $RC_MISC_ERROR
fi
logItem "Checking for partitionbasedbackup in $RESTOREFILE/*"
logCommand "ls -1 $RESTOREFILE*"
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
#shellcheck disable=SC2010
if ls -1 "$RESTOREFILE"* | grep -E "\.blkid$" &>>"$LOG_FILE" ; then
PARTITIONBASED_BACKUP=1
if [[ -n $ROOT_PARTITION ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_DEVICE_NOT_ALLOWED
exitError $RC_MISSING_FILES
fi
else
PARTITIONBASED_BACKUP=0
fi
if (( ! $PARTITIONBASED_BACKUP && $OPTION_T_USED )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_OPTION_T_NOT_ALLOWED
exitError $RC_PARAMETER_ERROR
fi
logItem "PartitionbasedBackup detected? $PARTITIONBASED_BACKUP"
if [[ -z $RESTORE_DEVICE ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_RESTOREDEVICE_DEFINED
exitError $RC_PARAMETER_ERROR
fi
if (( $PROGRESS && $INTERACTIVE )); then
if ! which pv &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_INSTALLED_FILE "pv" "pv"
exitError $RC_PARAMETER_ERROR
fi
fi
if ! (( $FAKE )); then
RESTORE_DEVICE=${RESTORE_DEVICE%/} # delete trailing /
if [[ ! ( $RESTORE_DEVICE =~ ^/dev/mmcblk[0-9]+$ ) && ! ( $RESTORE_DEVICE =~ /dev/loop[0-9]+ ) && ! ( $RESTORE_DEVICE =~ /dev/nvme[0-9]+n[0-9]+ )]]; then
if ! [[ "$RESTORE_DEVICE" =~ ^/dev/[a-zA-Z]+$ ]] ; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTOREDEVICE_IS_PARTITION "$RESTORE_DEVICE"
exitError $RC_PARAMETER_ERROR
fi
fi
if [[ -z $(fdisk -l "$RESTORE_DEVICE" 2>/dev/null) ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_RESTOREDEVICE_FOUND "$RESTORE_DEVICE"
exitError $RC_PARAMETER_ERROR
fi
fi
if (( $ROOT_PARTITION_DEFINED )); then
if ! [[ "$ROOT_PARTITION" =~ ^/dev/[a-z]+[0-9]$ ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_INVALID_RESTORE_ROOT_PARTITION "$ROOT_PARTITION"
exitError $RC_DEVICES_NOTFOUND
fi
if ! [[ -e "$ROOT_PARTITION" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_ROOT_PARTITION_NOT_FOUND "$ROOT_PARTITION"
exitError $RC_DEVICES_NOTFOUND
fi
local rc rr
rd=$(sed -E 's#/dev/([a-z]+)(.+)?#\1#' <<< "$RESTORE_DEVICE")
rr=$(sed -E 's#/dev/([a-z]+)(.+)?#\1#' <<< "$ROOT_PARTITION")
logItem "Restore devices: -d: $rd - -R: $rr"
if [[ "$rd" == "$rr" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_ROOT_PARTITION_NOT_DIFFERENT "$RESTORE_DEVICE"
exitError $RC_DEVICES_NOTFOUND
fi
fi
if mount | grep "^${RESTORE_DEVICE%/}"; then # delete trailing / if it's present
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_PARTITION_MOUNTED "$RESTORE_DEVICE"
current_partition_table="$(listDeviceInfo "$RESTORE_DEVICE")"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CURRENT_PARTITION_TABLE "$RESTORE_DEVICE"
echo "$current_partition_table"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UMOUNT_MOUNTED_PARTITIONS "$RESTORE_DEVICE"
if ! askYesNo; then
exitError $RC_RESTORE_FAILED
else
umount ${RESTORE_DEVICE}* &>>"$LOG_FILE"
if mount | grep "^${RESTORE_DEVICE%/}"; then # delete trailing / if it's present
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UMOUNT_MOUNTED_PARTITIONS_FAILED "$RESTORE_DEVICE"
exitError $RC_RESTORE_IMPOSSIBLE
fi
fi
fi
BASE_DIR=$(dirname "$RESTOREFILE")
logItem "Basedir: $BASE_DIR"
# Note: Handle old (without) and new (with OS release info) backup directory names
HOSTNAME=$(basename "$RESTOREFILE" | sed -r 's/(.*)(@[A-Za-z0-9]+)*-[A-Za-z]+-backup-[0-9]+-[0-9]+.*/\1/')
HOSTNAME=${HOSTNAME%@*}
logItem "Hostname: $HOSTNAME"
BACKUPTYPE=$(basename "$RESTOREFILE" | sed -r 's/.*-([A-Za-z]+)-backup-[0-9]+-[0-9]+.*/\1/')
logItem "Backuptype: $BACKUPTYPE"
DATE=$(basename "$RESTOREFILE" | sed -r 's/.*-[A-Za-z]+-backup-([0-9]+-[0-9]+).*/\1/')
logItem "Date: $DATE"
if (( $SKIP_FORMAT )); then
if (( $PARTITIONBASED_BACKUP )); then
if [[ "$BACKUPTYPE" != "$BACKUPTYPE_RSYNC" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_SKIP_FORMAT_POSSIBLE
exitError $RC_PARAMETER_ERROR
fi
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_SKIP_FORMAT_POSSIBLE
exitError $RC_PARAMETER_ERROR
fi
fi
if (( $PROGRESS && $INTERACTIVE )); then
if ! which pv &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_INSTALLED_FILE "pv" "pv"
exitError $RC_MISSING_COMMANDS
fi
fi
if [[ "$BACKUPTYPE" == "$BACKUPTYPE_RSYNC" ]]; then
if ! which rsync &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_INSTALLED_FILE "rsync" "rsync"
exitError $RC_MISSING_COMMANDS
fi
local rsyncVersion
rsyncVersion=$(rsync --version | head -n 1 | awk '{ print $3 }')
logItem "rsync version: $rsyncVersion"
# Decimals are not supported. Either use integers only, or use bc or awk to compare.
#shellcheck disable=SC2072
if (( $PROGRESS && $INTERACTIVE )) && [[ "$rsyncVersion" < "3.1" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RSYNC_DOES_NOT_SUPPORT_PROGRESS "$rsyncVersion"
exitError $RC_PARAMETER_ERROR
fi
disableACLsIfRequested
fi
if ! which dosfslabel &>/dev/null; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_INSTALLED_FILE "dosfslabel" "dosfstools"
exitError $RC_MISSING_COMMANDS
fi
if (( ! $PARTITIONBASED_BACKUP )); then
findNonpartitionBackupBootAndRootpartitionFiles
fi
inspect4Restore
if (( $FORCE_SFDISK )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FORCE_SFDISK "$RESTORE_DEVICE"
fi
if (( $SKIP_SFDISK )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SKIP_SFDISK "$RESTORE_DEVICE"
fi
#if [[ "$BACKUPTYPE" == "$BACKUPTYPE_DD" ]]; then
# local sdSize="$(fdisk -l "$RESTORE_DEVICE" | grep "Disk.*${RESTORE_DEVICE}" | cut -d ' ' -f 5)"
# local imgSize="$(stat -c "%s" "$ROOT_RESTOREFILE")"
# if [[ $sdSize < $imgSize ]]; then
# writeToConsole $MSG_LEVEL_MINIMAL $MSG_SD_TOO_SMALL "$RESTORE_DEVICE" "$sdSize" "$imgSize"
# exitError $RC_RESTORE_FAILED
# fi
#elif [[ "$BACKUPTYPE" == "$BACKUPTYPE_DDZ" ]]; then
# local c
# read c sdSize r < <(gzip -l "$RESTOREFILE" | tail -n 1)
# imgSize="$(stat -c "%s" "$ROOT_RESTOREFILE")"
# if [[ $sdSize < $imgSize ]]; then
# writeToConsole $MSG_LEVEL_MINIMAL $MSG_SD_TOO_SMALL "$RESTORE_DEVICE" "$sdSize" "$imgSize"
# exitError $RC_RESTORE_FAILED
# fi
#fi
rc=0
if ! (( $PARTITIONBASED_BACKUP )); then
restoreNonPartitionBasedBackup
if [[ "$BACKUPTYPE" != "$BACKUPTYPE_DD" && "$BACKUPTYPE" != "$BACKUPTYPE_DDZ" ]]; then
synchronizeCmdlineAndfstab
fi
else
restorePartitionBasedBackup
fi
mapfile -d " " -t RESTORE_TIME < <( duration "$START_TIME" "$END_TIME" )
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORE_TIME "${RESTORE_TIME[1]}" "${RESTORE_TIME[2]}" "${RESTORE_TIME[3]}"
logCommand "blkid"
logExit
}
# calculate diff in months of two dates (yyyymm)
function calculateMonthDiff() { # fromDate toDate
local y1=${1:0:4}
local m1=${1:4:2}
local y2=${2:0:4}
local m2=${2:4:2}
m1=${m1/#0}
m2=${m2/#0}
local diff=$(( ($y2 - $y1) * 12 + ($m2 - $m1) ))
echo $diff
}
function updateRestoreReminder() {
logEntry
local reminder_file="$VAR_LIB_DIRECTORY/$RESTORE_REMINDER_FILE"
if (( $RESTORE_REMINDER_INTERVAL > 0 )); then
# create directory to save state
if [[ ! -d "$VAR_LIB_DIRECTORY" ]]; then
if ! mkdir -p "$VAR_LIB_DIRECTORY" &>>"$LOG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_CREATE_DIRECTORY "$VAR_LIB_DIRECTORY"
exitError "$RC_CREATE_ERROR"
fi
fi
# initialize reminder state
if [[ ! -e "$reminder_file" ]]; then
echo "$(date +%Y%m) 0" > "$reminder_file"
return
fi
# retrieve reminder state
local now
now=$(date +%Y%m)
local rf
# Double quote to prevent globbing and word splitting.
#shellcheck disable=SC2086
rf="$(<$reminder_file)"
if [[ -z "${rf}" ]]; then # issue #316: reminder file exists but is empty
echo "$(date +%Y%m) 0" > "$reminder_file"
return
fi
# Prefer mapfile or read -a to split command output (or quote to avoid splitting).
#shellcheck disable=SC2207
rf=( $(<$reminder_file) )
local diffMonths
diffMonths=$(calculateMonthDiff "$now" "${rf[0]}" )
# check if reminder should be send
if (( $diffMonths <= -$RESTORE_REMINDER_INTERVAL )); then
if (( ${rf[1]} < $RESTORE_REMINDER_REPEAT )); then
# update reminder state
local nr=$(( ${rf[1]} + 1 ))
echo "${rf[0]} $nr" > "$reminder_file"
local left=$(( $RESTORE_REMINDER_REPEAT - $nr ))
NEWS_AVAILABLE=1
RESTORETEST_REQUIRED=1
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RESTORETEST_REQUIRED $left
else
logItem "Reset reminder"
# reset reminder state
echo "$(date +%Y%m) 0" > "$reminder_file"
fi
fi
fi
logExit
}
function mountAndCheck() { # device mountpoint
logEntry "$1 - $2"
umountPartition "$2"
mount "$1" "$2" &>>"$LOG_FILE"
local rc=$?
if (( $rc )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MOUNT_CHECK_ERROR "$1" "$2" "$rc"
logExit $rc
exitError $RC_MISC_ERROR
fi
logCommand "findmnt $2"
logExit $rc
}
function remount() { # device mountpoint
logEntry "$1 - $2"
if isMounted "$1"; then
logItem "$1 mounted - unmouting"
umount "$1" &>>"$LOG_FILE"
else
logItem "$1 not mounted"
fi
logItem "Creating mountpoint $2"
if ! mkdir -p "$2" &>>"$LOG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNABLE_TO_CREATE_DIRECTORY "$2"
exitError "$RC_CREATE_ERROR"
fi
mountAndCheck "$1" "$2" &>>"$LOG_FILE"
logExit $rc
}
function updateConfig() {
logEntry "$CUSTOM_CONFIG_FILE"
local customFile="$CUSTOM_CONFIG_FILE"
local etcConfigFileVersion="$ETC_CONFIG_FILE_VERSION"
# use fileparameter as new config file
if [[ -n $customFile ]]; then
if [[ -f $customFile ]]; then
logItem "Using config file $customFile"
# See if you can use ${variable//search/replace} instead
#shellcheck disable=SC2001
NEW_CONFIG="$(sed -e "s@$ORIG_CONFIG@$customFile@" <<< "$NEW_CONFIG")"
# See if you can use ${variable//search/replace} instead
#shellcheck disable=SC2001
MERGED_CONFIG="$(sed -e "s@$ORIG_CONFIG@$customFile@" <<< "$MERGED_CONFIG")"
# See if you can use ${variable//search/replace} instead
#shellcheck disable=SC2001
BACKUP_CONFIG="$(sed -e "s@$ORIG_CONFIG@$customFile@" <<< "$BACKUP_CONFIG")"
etcConfigFileVersion="$CUSTOM_CONFIG_FILE_VERSION"
# See if you can use ${variable//search/replace} instead
#shellcheck disable=SC2001
ORIG_CONFIG="$(sed -e "s@$ORIG_CONFIG@$customFile@" <<< "$ORIG_CONFIG")"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_NOT_FOUND "$NEW_CONFIG"
exitError $RC_MISSING_FILES
fi
fi
if [[ ! -e $ORIG_CONFIG ]]; then
logItem "$ORIG_CONFIG does not exist"
logExit
return
fi
logItem "Current config version: $etcConfigFileVersion - Required config version: $VERSION_SCRIPT_CONFIG" # VERSION_SCRIPT_CONFIG is from updated script
if (( $(compareVersions "$etcConfigFileVersion" "$VERSION_SCRIPT_CONFIG") >= 0 )) ; then # ETC_CONFIG >= SCRIPT_CONFIG
logExit "Config version ok"
if (( $UPDATE_CONFIG )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_CONFIGUPDATE_REQUIRED "$VERSION_SCRIPT_CONFIG"
fi
logExit
return
fi
# lang appears unused. Verify use (or export if used externally). ... lang used in following eval for late binding of config files to use the correct language !!!
# shellcheck disable=SC2034
local lang=${LANGUAGE,,}
if grep -iqE "alpha|beta" <<< "$VERSION"; then
eval "DL_URL=$BETA_CONFIG_URL"
else
eval "DL_URL=$CONFIG_URL"
fi
# download new config file
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DOWNLOADING "$NEW_CONFIG" "$DL_URL"
local dlHttpCode dlRC
dlHttpCode="$(downloadFile "$DL_URL" "$NEW_CONFIG")"
dlRC=$?
if (( $dlRC != 0 )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DOWNLOAD_FAILED "$DL_URL" "$dlHttpCode" $dlRC
exitError $RC_DOWNLOAD_FAILED
fi
# make sure new config file is readable by owner only
if ! chmod 600 "$NEW_CONFIG" &>>"$LOG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CHMOD_FAILED "$NEW_CONFIG"
exitError $RC_FILE_OPERATION_ERROR
fi
local newConfigVersion
newConfigVersion="$(extractVersionFromFile "$NEW_CONFIG" "$VERSION_CONFIG_VARNAME")"
logItem "New config version of downloaded file: $newConfigVersion"
# Should not occure but make sure config is not downgraded for some reasons
if (( $(compareVersions "$newConfigVersion" "$VERSION_SCRIPT_CONFIG") < 0 )); then # newConfigVersion < SCRIPT_CONFIG
if (( $UPDATE_CONFIG )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_CONFIGUPDATE_REQUIRED "$VERSION_SCRIPT_CONFIG"
logExit
return
fi
fi
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CURRENT_CONFIGURATION_UPDATE_REQUIRED "$etcConfigFileVersion" "$VERSION_SCRIPT_CONFIG"
rm -f "$MERGED_CONFIG" &>/dev/null
# process NEW CONFIG FILE
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MERGING_VERSION "v$etcConfigFileVersion" "v$VERSION_SCRIPT_CONFIG" "$MERGED_CONFIG"
local merged=0
local deleted=0
# make sure config file is readable by owner only
if ! chmod 600 "$ORIG_CONFIG" &>>"$LOG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CHMOD_FAILED "$ORIG_CONFIG"
exitError $RC_FILE_OPERATION_ERROR
fi
# process new config file and merge old options
logItem "Merging $NEW_CONFIG and $ORIG_CONFIG"
while read -r line; do
if [[ -n "$line" && ! "$line" =~ ^[[:space:]]*# ]]; then # skip comment or empty lines
local KW VAL CONFIG_VERSION
KW="$(cut -d= -f1 <<< "$line")" # retrieve keyword
VAL="$(cut -d= -f2 <<< "$line" )" # retrieve value
logItem "KW: $KW - VAL: $VAL"
if [[ "$KW" =~ VERSION_.*CONF ]]; then # add new version number
echo "$line" >> "$MERGED_CONFIG"
CONFIG_VERSION="$VAL"
continue
fi
local OC_line r
OC_line="$(grep "^$KW=" "$ORIG_CONFIG")" # retrieve old option line
r=$?
logItem "grep old file rc:$s - contents: $OC_line"
if (( ! $r )); then # new option found
local OW
OW="$(cut -d= -f2- <<< "$OC_line" )" # retrieve old option value
echo "$KW=$OW" >> "$MERGED_CONFIG" # use old option value
else
printf "$NEW_OPTION_TRAILER\n" "$CONFIG_VERSION" >> $MERGED_CONFIG
echo "$line" >> $MERGED_CONFIG # add new option
writeToConsole $MSG_LEVEL_MINIMAL $MSG_ADDED_CONFIG_OPTION "$KW" "$VAL"
(( merged ++ ))
fi
else
echo "$line" >> "$MERGED_CONFIG" # copy comment or empty line
fi
done < "$NEW_CONFIG"
# check in old config file which options were deleted in new config file
logItem "Checking for deleted options"
while read -r line; do
if [[ -n "$line" && ! "$line" =~ ^.*# ]]; then # skip comment or empty lines
local KW VAL
KW="$(cut -d= -f1 <<< "$line")" # retrieve keyword
VAL="$(cut -d= -f2 <<< "$line" )" # retrieve value
if [[ "$KW" =~ VERSION_.*CONF ]]; then # skip version number
continue
fi
local r
grep -q "^$KW=" "$NEW_CONFIG" # check if it's still the new config file
r=$?
logItem "grep old file for deleted $KW rc:$r - contents: $OC_line"
if (( $r )) && [[ $KW != "UUID" ]]; then # option not found, it was deleted
{
echo ""
printf "$DELETED_OPTION_TRAILER\n" "$CONFIG_VERSION"
echo "# $line" # insert deleted config line as comment
} >> "$MERGED_CONFIG"
(( deleted ++ ))
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DELETED_CONFIG_OPTION "$KW" "$VAL"
fi
fi
done < "$ORIG_CONFIG"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MERGE_SUCCESSFULL
if ! chmod 600 "$MERGED_CONFIG" &>>"$LOG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CHMOD_FAILED "$MERGED_CONFIG"
exitError $RC_FILE_OPERATION_ERROR
fi
logItem "Merged: $merged - deleted: $deleted"
rm -f "$NEW_CONFIG" &>/dev/null
if askYesNo "$MSG_UPDATE_CONFIG" "$BACKUP_CONFIG"; then
local new_file
# save old config
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SAVING_CURRENT_CONFIGURATION "$ORIG_CONFIG" "$BACKUP_CONFIG"
new_file=$(createBackupVersion "$ORIG_CONFIG")
local r=$?
if (( $rc )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CONFIG_BACKUP_FAILED "$ORIG_CONFIG"
exitError $RC_FILE_OPERATION_ERROR
fi
if ! chmod 600 "$new_file" &>>"$LOG_FILE"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_CHMOD_FAILED "$new_file"
exitError $RC_FILE_OPERATION_ERROR
fi
mv "$MERGED_CONFIG" "$ORIG_CONFIG"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_COPIED_FILE "$MERGED_CONFIG" "$ORIG_CONFIG"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_ACTIVATE_CONFIG "$MERGED_CONFIG" "$ORIG_CONFIG"
fi
logExit
}
function synchronizeCmdlineAndfstab() {
logEntry
local CMDLINE FSTAB newPartUUID oldPartUUID BOOT_MP ROOT_MP newUUID oldUUID BOOT_PARTITION oldLABEL
BOOT_PARTITION="$(createPartitionName "$RESTORE_DEVICE" 1)"
if (( $PARTITIONBASED_BACKUP )); then
# See if you can use ${variable//search/replace} instead
#shellcheck disable=SC2001
ROOT_PARTITION="$(sed 's/1$/2/' <<< "$BOOT_PARTITION")"
fi
logEntry "BOOT_PARTITION: $BOOT_PARTITION - ROOT_PARTITION: $ROOT_PARTITION"
ROOT_MP="$TEMPORARY_MOUNTPOINT_ROOT/root"
BOOT_MP="$TEMPORARY_MOUNTPOINT_ROOT/boot"
logEntry "ROOT_MP: $ROOT_MP - BOOT_MP: $BOOT_MP"
remount "$BOOT_PARTITION" "$BOOT_MP"
remount "$ROOT_PARTITION" "$ROOT_MP"
CMDLINE="$BOOT_MP/cmdline.txt" # absolute path in mount, don't use firmware subdir for Ubuntu, boot partition is mounted there at ubuntu startup
FSTAB="$ROOT_MP/etc/fstab" # absolute path in mount
local cmdline # path for message
[[ -d $TEMPORARY_MOUNTPOINT_ROOT/root/boot/firmware ]] && cmdline="/boot/firmware/cmdline.txt" || cmdline="/boot/cmdline.txt"
local fstab="/etc/fstab" # path for message
logEntry "CMDLINE: $CMDLINE - FSTAB: $FSTAB"
partprobe "$BOOT_PARTITION" # reload partition table
partprobe "$ROOT_PARTITION" # reload partition table
logCommand "blkid -o udev $ROOT_PARTITION"
local rootLabelCreated=0
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SYNC_CMDLINE_FSTAB "$cmdline" "$fstab"
if [[ -f "$CMDLINE" ]]; then
logItem "Org $CMDLINE"
logCommand "cat $CMDLINE"
if [[ $(cat "$CMDLINE") =~ root=PARTUUID=([a-z0-9\-]+) ]]; then
local oldPartUUID=${BASH_REMATCH[1]}
local newPartUUID
newPartUUID=$(blkid -o udev "$ROOT_PARTITION" | grep ID_FS_PARTUUID= | cut -d= -f2)
logItem "CMDLINE - newPartUUID: $newPartUUID, oldPartUUID: $oldPartUUID"
if [[ -z $newPartUUID ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_UUID_SYNCHRONIZED "$cmdline" "root="
elif [[ "$oldPartUUID" != "$newPartUUID" ]]; then
writeToConsole $MSG_LEVEL_DETAILED $MSG_UPDATING_UUID "PARTUUID" "$oldPartUUID" "$newPartUUID" "$cmdline"
sed -i "s/$oldPartUUID/$newPartUUID/" "$(realpath "$CMDLINE")" &>> "$LOG_FILE"
fi
elif [[ $(cat "$CMDLINE") =~ root=UUID=([a-z0-9\-]+) ]]; then
local oldUUID
oldUUID=${BASH_REMATCH[1]}
local newUUID
newUUID=$(blkid -o udev "$ROOT_PARTITION" | grep ID_FS_UUID= | cut -d= -f2)
logItem "CMDLINE - newUUID: $newUUID, oldUUID: $oldUUID"
if [[ -z $newUUID ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_UUID_SYNCHRONIZED "$cmdline" "root="
elif [[ "$oldUUID" != "$newUUID" ]]; then
writeToConsole $MSG_LEVEL_DETAILED $MSG_UPDATING_UUID "UUID" "$oldUUID" "$newUUID" "$cmdline"
sed -i "s/$oldUUID/$newUUID/" "$(realpath "$CMDLINE")" &>> "$LOG_FILE"
fi
elif [[ $(cat "$CMDLINE") =~ root=LABEL=([a-z0-9\-]+) ]]; then
local oldLABEL=${BASH_REMATCH[1]}
logItem "Writing label $oldLABEL on $ROOT_PARTITION"
writeToConsole $MSG_LEVEL_DETAILED $MSG_LABELING "$ROOT_PARTITION" "$oldLABEL"
e2label "$ROOT_PARTITION" "$oldLABEL" &>> "$LOG_FILE"
local rc=$?
if (( $rc )); then
local cmd="e2label $ROOT_PARTITION $oldLABEL"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_LABELING_FAILED "$cmd" "$rc"
else
rootLabelCreated=1
fi
elif grep "root=/dev/" "$CMDLINE"; then
logItem "/dev detected in $CMDLINE"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_UUID_SYNCHRONIZED "$cmdline" "root="
fi
else
logCommand "ls -la $BOOT_MP"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_NOT_FOUND "$cmdline"
exitError $RC_UUID_UPDATE_IMPOSSIBLE
fi
if [[ -f "$FSTAB" ]]; then
logItem "Org $FSTAB"
# Double quote to prevent globbing and word splitting.
#shellcheck disable=SC2086
logItem "$(cat $FSTAB)"
if [[ $(cat "$FSTAB") =~ PARTUUID=([a-z0-9\-]+)[[:space:]]+/[[:space:]] ]]; then
local oldPartUUID=${BASH_REMATCH[1]}
local newPartUUID
newPartUUID=$(blkid -o udev "$ROOT_PARTITION" | grep ID_FS_PARTUUID= | cut -d= -f2)
logItem "FSTAB root - newRootPartUUID: $newPartUUID, oldRootPartUUID: $oldPartUUID"
if [[ -z $newPartUUID ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_PARTUUID_SYNCHRONIZED "$fstab" "/"
elif [[ "$oldPartUUID" != "$newPartUUID" ]]; then
local oldpartuuidID
oldpartuuidID="$(sed -E 's/-[0-9]+//' <<< "$oldPartUUID")"
local newpartuuidID
newpartuuidID="$(sed -E 's/-[0-9]+//' <<< "$newPartUUID")"
writeToConsole $MSG_LEVEL_DETAILED $MSG_UPDATING_UUID "PARTUUID" "$oldPartUUID" "$newPartUUID" "$fstab"
sed -i "s/$oldpartuuidID/$newpartuuidID/g" "$FSTAB" &>> "$LOG_FILE"
fi
elif [[ $(cat "$FSTAB") =~ UUID=([a-z0-9\-]+)[[:space:]]+/[[:space:]] ]]; then
local oldUUID=${BASH_REMATCH[1]}
local newUUID
newUUID=$(blkid -o udev "$ROOT_PARTITION" | grep ID_FS_UUID= | cut -d= -f2)
logItem "FSTAB root - newRootUUID: $newUUID, oldRootUUID: $oldUUID"
if [[ -z $newUUID ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_UUID_SYNCHRONIZED "$fstab" "/"
elif [[ "$oldUUID" != "$newUUID" ]]; then
local olduuidID
olduuidID="$(sed -E 's/-[0-9]+//' <<< "$oldUUID")"
local newuuidID
newuuidID="$(sed -E 's/-[0-9]+//' <<< "$newUUID")"
writeToConsole $MSG_LEVEL_DETAILED $MSG_UPDATING_UUID "PARTUUID" "$olduuidID" "$newuuidID" "$fstab"
sed -i "s/$olduuidID/$newuuidID/g" "$FSTAB" &>> "$LOG_FILE"
fi
elif [[ $(cat "$FSTAB") =~ LABEL=([a-z0-9\-]+)[[:space:]]+/[[:space:]] ]]; then
if (( ! $rootLabelCreated )) ; then
local oldLABEL=${BASH_REMATCH[1]}
logItem "Writing label $oldLABEL on $ROOT_PARTITION"
writeToConsole $MSG_LEVEL_DETAILED $MSG_LABELING "$ROOT_PARTITION" "$oldLABEL"
e2label "$ROOT_PARTITION" "$oldLABEL" &>> "$LOG_FILE"
local rc=$?
if (( $rc )); then
local cmd="e2label $ROOT_PARTITION $oldLABEL"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_LABELING_FAILED "$cmd" "$rc"
else
rootLabelCreated=1
fi
fi
elif grep "^/dev/" "$FSTAB"; then
logItem "/dev detected in $FSTAB"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_UUID_SYNCHRONIZED "$fstab" "/"
fi
else
logCommand "ls -la $ROOT_MP"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_NOT_FOUND "$fstab"
exitError $RC_UUID_UPDATE_IMPOSSIBLE
fi
if [[ -f "$FSTAB" ]]; then
logItem "Org $FSTAB"
logItem "$(cat "$FSTAB")"
if [[ $(cat "$FSTAB") =~ PARTUUID=([a-z0-9\-]+)[[:space:]]+/boot ]]; then
local oldPartUUID=${BASH_REMATCH[1]}
local newPartUUID
newPartUUID=$(blkid -o udev "$BOOT_PARTITION" | grep ID_FS_PARTUUID= | cut -d= -f2)
logItem "FSTAB boot - newPartUUID: $newPartUUID, oldPartUUID: $oldPartUUID"
if [[ -z $newPartUUID ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_UUID_SYNCHRONIZED "$fstab" "/boot"
elif [[ "$oldPartUUID" != "$newPartUUID" ]]; then
writeToConsole $MSG_LEVEL_DETAILED $MSG_UPDATING_UUID "PARTUUID" "$oldPartUUID" "$newPartUUID" "$fstab"
sed -i "s/$oldPartUUID/$newPartUUID/" "$FSTAB" &>> "$LOG_FILE"
fi
elif [[ $(cat "$FSTAB") =~ UUID=([a-z0-9\-]+)[[:space:]]+/boot ]]; then
local oldUUID=${BASH_REMATCH[1]}
local newUUID
newUUID=$(blkid -o udev "$BOOT_PARTITION" | grep ID_FS_UUID= | cut -d= -f2)
logItem "FSTAB boot - newBootUUID: $newUUID, oldBootUUID: $oldUUID"
if [[ -z $newUUID ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_UUID_SYNCHRONIZED "$fstab" "/boot"
elif [[ "$oldUUID" != "$newUUID" ]]; then
writeToConsole $MSG_LEVEL_DETAILED $MSG_UPDATING_UUID "PARTUUID" "$oldUUID" "$newUUID" "$fstab"
sed -i "s/$oldUUID/$newUUID/" "$FSTAB" &>> "$LOG_FILE"
fi
elif [[ $(cat "$FSTAB") =~ LABEL=([a-z0-9\-]+)[[:space:]]+/boot ]]; then
local oldLABEL=${BASH_REMATCH[1]}
logItem "Writing label $oldLABEL on $BOOT_PARTITION"
writeToConsole $MSG_LEVEL_DETAILED $MSG_LABELING "$BOOT_PARTITION" "$oldLABEL"
dosfslabel "$BOOT_PARTITION" "$oldLABEL" &>> "$LOG_FILE"
local rc=$?
if (( $rc )); then
local cmd="dosfslabel $BOOT_PARTITION $oldLABEL"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_LABELING_FAILED "$cmd" "$rc"
fi
elif grep "^/dev/" "$FSTAB"; then
logItem "/dev detected in $FSTAB"
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_UUID_SYNCHRONIZED "$fstab" "/boot"
fi
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_NO_UUID_SYNCHRONIZED "$fstab" "/boot"
fi
if [[ -f "$CMDLINE" ]]; then
logItem "Upd $CMDLINE"
logCommand "cat $CMDLINE"
fi
if [[ -f "$FSTAB" ]]; then
logItem "Upd $FSTAB"
logCommand "cat $FSTAB"
fi
umount "$BOOT_MP" &>>"$LOG_FILE"
umount "$ROOT_MP" &>>"$LOG_FILE"
rmdir "$BOOT_MP" &>>"$LOG_FILE"
rmdir "$ROOT_MP" &>>"$LOG_FILE"
logExit
}
# count number of lines in string and return 0 if line is empty (wc -l will return 1 :-( )
function countLines() { # string
logEntry "$1"
local c
if [[ -z "$1" ]]; then
c=0
else
c=$(wc -l <<< "$1")
fi
echo "$c"
logExit "$c"
}
# Smart recycle strategy was inspired by Manuel Dewalds excelent article "Automating backups on a Raspberry Pi NAS"
# on https://opensource.com/article/18/8/automate-backups-raspberry-pi
function SR_listYearlyBackups() { # directory
logEntry "$SR_YEARLY" "$1"
if (( $SR_YEARLY > 0 )); then
local i d
for ((i=0;i<=$(( $SR_YEARLY-1 ));i++)); do
# today is 20191117
# date +%Y -d "0 year ago" -> 2019
d=$(date +%Y -d "${i} year ago")
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
# shellcheck disable=SC2155,SC2010,SC1087
ls -1 "$1" | grep -E "${HOSTNAME_OSR}\-${BACKUPTYPE}\-backup\-$d[0-9]{2}[0-9]{2}" | grep -Ev "_" | sort -ur | tail -n 1 # find earliest yearly backup
done
fi
logExit
}
function SR_listMonthlyBackups() { # directory
logEntry "$SR_MONTHLY" "$1"
if (( $SR_MONTHLY > 0 )); then
local i d
for ((i=0;i<=$(( $SR_MONTHLY-1 ));i++)); do
# ... error in date ... see http://bashworkz.com/linux-date-problem-1-month-ago-date-bug/
# ls ${BACKUPPATH} | egrep "\-backup\-$(date +%Y%m -d "${i} month ago")[0-9]{2}" | sort -u | head -n 1
# today is 20191117
# date -d "$(date +%Y%m15) -0 month" +%Y%m -> 201911
d=$(date -d "$(date +%Y%m15) -${i} month" +%Y%m) # get month
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
# shellcheck disable=SC2155,SC2010,SC1087
ls -1 "$1" | grep -E "${HOSTNAME_OSR}\-${BACKUPTYPE}\-backup\-$d[0-9]{2}" | grep -Ev "_" | sort -ur | tail -n 1 # find earliest monthly backup
done
fi
logExit
}
function SR_listWeeklyBackups() { # directory
logEntry "$SR_WEEKLY" "$1"
local d
if (( $SR_WEEKLY > 0 )); then
local i mon
for ((i=0;i<=$(( $SR_WEEKLY-1));i++)); do
# assume today is 20191119 (tue) or wed-sun
# last monday is date +%Y%m%d -d "last monday -1...n-1 weeks" -> 20191111
local last="last"
# assume today is 20191118 (mon)
# last monday is date +%Y%m%d -d "monday -1...n-1 weeks" -> 20191111
if (( $(date +"%u") == 1 )); then
last=""
fi
mon=$(date +%Y%m%d -d "$last monday -${i} weeks") # calculate monday of week
local dl=""
for ((d=0;d<=6;d++)); do # now build list of week days of week (mon-sun)
dl="${HOSTNAME_OSR}\-${BACKUPTYPE}\-backup\-$(date +%Y%m%d -d "$mon + $d day") $dl"
done
# Double quote to prevent globbing and word splitting.
# shellcheck disable=SC2155,SC2010,SC2086
ls -1 "$1" | grep -e "$(echo -n $dl | sed "s/ /\\\|/g")" | grep -Ev "_" | sort -ur | tail -n 1 # use earliest backup of this week
done
fi
logExit
}
function SR_listDailyBackups() { # directory
logEntry "$SR_DAILY" "$1"
if (( $SR_DAILY > 0 )); then
local i
for ((i=0;i<=$(( $SR_DAILY-1));i++)); do
# today is 20191117
# date +%Y%m%d -d "-1 day" -> 20191116
local d
d=$(date +%Y%m%d -d "-${i} day") # get day
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
# shellcheck disable=SC2155,SC2010
ls -1 "$1" | grep "${HOSTNAME_OSR}\-${BACKUPTYPE}\-backup\-$d" | grep -Ev "_" | sort -ur | head -n 1 # find most current backup of this day
done
fi
logExit
}
function SR_getAllBackups() { # directory
logEntry "$1"
local yb mb wb db
yb="$(SR_listYearlyBackups "$1")"
logItem "$yb"
#local ybc="$(countLines "$yb")"
[[ -n "$yb" ]] && echo "$yb"
# shellcheck disable=SC2155
mb="$(SR_listMonthlyBackups "$1")"
logItem "$mb"
#local mbc="$(countLines "$mb")"
[[ -n "$mb" ]] && echo "$mb"
# shellcheck disable=SC2155
wb="$(SR_listWeeklyBackups "$1")"
logItem "$wb"
#local wbc="$(countLines "$wb")"
[[ -n "$wb" ]] && echo "$wb"
# shellcheck disable=SC2155
db="$(SR_listDailyBackups "$1")"
logItem "$db"
#local dbc="$(countLines "$db")"
[[ -n "$db" ]] && echo "$db"
logExit
}
function SR_listUniqueBackups() { #directory
logEntry "$1"
local r
r="$(SR_getAllBackups "$1" | grep -Ev "_" | sort -u )"
local rc
rc="$(countLines "$r")"
logItem "$r"
echo "$r"
logExit "$rc"
}
function SR_listBackupsToDelete() { # directory
logEntry "$1"
local r
# Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
# shellcheck disable=SC2155,SC2010
r="$(ls -1 "$1" | grep -v "$(echo -n "$(SR_listUniqueBackups "$1")" )" | sed "s/ /\\\|/g" | grep "${HOSTNAME_OSR}\-${BACKUPTYPE}\-backup\-" | grep -v "_" )" # make sure to delete only backup type files and no snapshots
local rc
rc="$(countLines "$r")"
logItem "$r"
echo "$r"
logExit "$rc"
}
function check4RequiredCommands() { # btrfs | f2fs
logEntry
local missing_commands missing_packages
if [[ -z $1 ]]; then
for cmd in "${!REQUIRED_COMMANDS[@]}"; do
if ! hash "$cmd" 2>/dev/null; then
missing_commands="$cmd $missing_commands "
missing_packages="${REQUIRED_COMMANDS[$cmd]} $missing_packages "
fi
done
elif [[ "$1" == "btrfs" ]]; then
for cmd in "${!REQUIRED_COMMANDS_BTRFS[@]}"; do
if ! hash "$cmd" 2>/dev/null; then
missing_commands="$cmd $missing_commands "
missing_packages="${REQUIRED_COMMANDS_BTRFS[$cmd]} $missing_packages "
fi
done
elif [[ "$1" == "f2fs" ]]; then
for cmd in "${!REQUIRED_COMMANDS_F2FS[@]}"; do
if ! hash "$cmd" 2>/dev/null; then
missing_commands="$cmd $missing_commands "
missing_packages="${REQUIRED_COMMANDS_F2FS[$cmd]} $missing_packages "
fi
done
else
assertionFailed $LINENO "Invalid arg: '$1'"
fi
if [[ -n "$missing_commands" ]]; then
shopt -s extglob
missing_commands="${missing_commands%%*( )}"
missing_packages="${missing_packages%%*( )}"
shopt -u extglob
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_COMMANDS "$missing_commands"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_PACKAGES "$missing_packages"
exitError $RC_MISSING_COMMANDS
fi
logExit
}
function lockingFramework() {
# Copyright (C) 2009 Przemyslaw Pawelczyk
# 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.
LOCKFILE="/var/lock/$MYNAME"
LOCKFD=99
# PRIVATE
# Double quote to prevent globbing and word splitting.
# shellcheck disable=SC2086
_lock() { flock -$1 $LOCKFD; }
_no_more_locking() { _lock u; _lock xn && rm -f "$LOCKFILE" ; }
# _prepare_locking() { eval "exec $LOCKFD>\"$LOCKFILE\""; trap _no_more_locking EXIT; }
_prepare_locking() { eval "exec $LOCKFD>\"$LOCKFILE\"" ; }
# ON START
_prepare_locking
# PUBLIC
exlock_now() { _lock xn; } # obtain an exclusive lock immediately or fail
exlock() { _lock x; } # obtain an exclusive lock
shlock() { _lock s; } # obtain a shared lock
unlock() { _lock u; } # drop a lock
}
function usageEN() {
echo "$GIT_CODEVERSION"
echo "Usage: $MYSELF [option]* {backupDirectory}"
echo ""
echo "-General options-"
[ -z "$DEFAULT_EMAIL" ] && DEFAULT_EMAIL="no"
echo "-b {dd block size} (Default: $DEFAULT_DD_BLOCKSIZE)"
[ -z "$DEFAULT_DD_PARMS" ] && DEFAULT_DD_PARMS="no"
echo "-D {additional dd parameters} (Default: $DEFAULT_DD_PARMS)"
echo "-e {email address} (Default: $DEFAULT_EMAIL)"
[ -z "$DEFAULT_EMAIL_PARMS" ] && DEFAULT_EMAIL_PARMS="no"
echo "-E {additional email call parameters} (Default: $DEFAULT_EMAIL_PARMS)"
echo "-f {config filename}"
echo "-g Display progress bar"
echo "-G {message language} (${SUPPORTED_LANGUAGES[*]}) (Default: $LANGUAGE)"
echo "-h display this help text"
echo "-l {log level} ($POSSIBLE_LOG_LEVELs) (Default: ${LOG_LEVELs[$DEFAULT_LOG_LEVEL]})"
echo "-L {log targetdirectory} ($POSSIBLE_LOG_OUTPUTs) (Default: ${LOG_OUTPUTs[$DEFAULT_LOG_OUTPUT]})"
echo "-m {message level} ($POSSIBLE_MSG_LEVELs) (Default: ${MSG_LEVELs[$DEFAULT_MSG_LEVEL]})"
echo "-M {backup description of snapshot}"
echo "-s {email program to use} ($SUPPORTED_MAIL_PROGRAMS) (Default: $DEFAULT_MAIL_PROGRAM)"
echo "--timestamps Prefix messages with timestamps (Default: ${NO_YES[$DEFAULT_TIMESTAMPS]})"
echo "-u {excludeList} List of directories to exclude from tar and rsync backup"
echo "-U current script version will be replaced by the most recent version. Current version will be saved and can be restored with parameter -V"
echo "-v verbose output of backup tools (Default: ${NO_YES[$DEFAULT_VERBOSE]})"
echo "-V restore a previous version"
echo ""
echo "-Backup options-"
[ -z "$DEFAULT_STARTSERVICES" ] && DEFAULT_STARTSERVICES="no"
echo "-a {commands to execute after Backup} (Default: $DEFAULT_STARTSERVICES)"
echo "-B Save bootpartition in tar file (Default: $DEFAULT_TAR_BOOT_PARTITION_ENABLED)"
echo "-F Backup is simulated"
echo "--ignoreAdditionalPartitions (Default: ${NO_YES[$DEFAULT_IGNORE_ADDITIONAL_PARTITIONS]}) - Partitionnumbers > 2 will not be saved"
echo "--ignoreMissingPartitions (Default: ${NO_YES[$DEFAULT_IGNORE_MISSING_PARTITIONS]}) - Allow to backup not all previously saved paritions"
echo "-k {backupsToKeep} (Default: $DEFAULT_KEEPBACKUPS)"
[ -z "$DEFAULT_STOPSERVICES" ] && DEFAULT_STOPSERVICES="no"
echo "-o {commands to execute before Backup} (Default: $DEFAULT_STOPSERVICES)"
echo "-P use partitionoriented backup mode to backup the first two partitions (Default: ${DEFAULT_PARTITIONS_TO_BACKUP})"
echo "-t {backupType} ($ALLOWED_TYPES) (Default: $DEFAULT_BACKUPTYPE)"
echo "-T {List of partitions to backup in partition oriented mode} (Partition numbers, e.g. \"1 2 3\" or \"*\" for all) (Default: ${DEFAULT_PARTITIONS_TO_BACKUP})"
echo "--tarCompressionTool {tar Comressiontool} ($TAR_COMPRESSION_TOOLS_SUPPORTED_LIST)"
echo "--tarCompressionToolOptions {Options for tar compressiontool}"
echo "-z compress DD and TAR backup file with gzip (Default: ${NO_YES[$DEFAULT_ZIP_BACKUP]})"
echo ""
echo "-Restore options-"
echo "-0 Restore device will not be partitioned"
echo "-00 Restored partitions will not be formatted (only for backup type rsync)"
echo "-1 Formatting errors on restore device will be ignored"
[ -z "$DEFAULT_RESTORE_DEVICE" ] && DEFAULT_RESTORE_DEVICE="no"
echo "-C Formating of the restorepartitions will check for badblocks (Default: ${NO_YES[$DEFAULT_CHECK_FOR_BAD_BLOCKS]})"
echo "-d {restoreDevice} (Default: $DEFAULT_RESTORE_DEVICE) (Example: /dev/sda)"
echo "-R {rootPartition} (Default: restoreDevice) (Example: /dev/sdb1)"
echo "-T {List of partitions to restore from a partition oriented backup} (Partition numbers, e.g. \"1 2 3\" or \"*\" for all) (Default: ${DEFAULT_PARTITIONS_TO_RESTORE})"
echo "--resizeRootFS|resizeLastPartitionFS (Default: ${NO_YES[$DEFAULT_RESIZE_ROOTFS]})"
}
function usageDE() {
echo "$GIT_CODEVERSION"
echo "Aufruf: $MYSELF [Option]* {Backupverzeichnis}"
echo ""
echo "-Allgemeine Optionen-"
[ -z "$DEFAULT_EMAIL" ] && DEFAULT_EMAIL="nein"
echo "-b {dd Blockgröße} (Standard: $DEFAULT_DD_BLOCKSIZE)"
[ -z "$DEFAULT_DD_PARMS" ] && DEFAULT_DD_PARMS="nein"
echo "-D {Zusätzliche dd Parameter} (Standard: $DEFAULT_DD_PARMS)"
echo "-e {eMail Addresse} (Standard: $DEFAULT_EMAIL)"
[ -z "$DEFAULT_EMAIL_PARMS" ] && DEFAULT_EMAIL_PARMS="nein"
echo "-E {Zusätzliche eMail Aufrufparameter} (Standard: $DEFAULT_EMAIL_PARMS)"
echo "-f {Konfig Dateiname}"
echo "-g Anzeige des Fortschritts"
echo "-G {Meldungssprache} (${SUPPORTED_LANGUAGES[*]}) (Standard: $LANGUAGE)"
echo "-h Anzeige dieses Hilfstextes"
echo "-l {log Genauigkeit} ($POSSIBLE_LOG_LEVELs) (Standard: ${LOG_LEVELs[$DEFAULT_LOG_LEVEL]})"
echo "-L {log Zielverzeichnis} ($POSSIBLE_LOG_OUTPUTs) (Standard: ${LOG_OUTPUTs[$DEFAULT_LOG_OUTPUT]})"
echo "-m {Meldungsgenauigkeit} ($POSSIBLE_MSG_LEVELs) (Standard: ${MSG_LEVELs[$DEFAULT_MSG_LEVEL]})"
echo "-M {Backup Beschreibung des Snapshots}"
echo "-s {Benutztes eMail Program} ($SUPPORTED_MAIL_PROGRAMS) (Standard: $DEFAULT_MAIL_PROGRAM)"
echo "--timestamps Meldungen werden mit einen Zeitstempel ausgegeben (Standard: ${NO_YES[$DEFAULT_TIMESTAMPS]})"
echo "-u {excludeList} Liste von Verzeichnissen, die vom tar und rsync Backup auszunehmen sind"
echo "-U Scriptversion wird durch die aktuelle Version ersetzt. Die momentane Version wird gesichert und kann mit dem Parameter -V wiederhergestellt werden"
echo "-v Detailierte Ausgaben der Backup Tools (Standard: ${NO_YES[$DEFAULT_VERBOSE]})"
echo "-V Aktivierung einer älteren Skriptversion"
echo ""
echo "-Backup Optionen-"
[ -z "$DEFAULT_STARTSERVICES" ] && DEFAULT_STARTSERVICES="keine"
echo "-a {Befehle die nach dem Backup ausgeführt werden} (Standard: $DEFAULT_STARTSERVICES)"
echo "-B Sicherung der Bootpartition als tar file (Standard: $DEFAULT_TAR_BOOT_PARTITION_ENABLED)"
echo "-F Backup wird nur simuliert"
echo "--ignoreAdditionalPartitions (Standard: ${NO_YES[$DEFAULT_IGNORE_ADDITIONAL_PARTITIONS]}) - Partitionsnummern > 2 werden nicht gesichert"
echo "--ignoreMissingPartitions (default: ${NO_YES[$DEFAULT_IGNORE_MISSING_PARTITIONS]}) - Es können weniger als die vorher gesicherten Partitionen gesichert werden"
echo "-k {Anzahl Backups} (Standard: $DEFAULT_KEEPBACKUPS)"
[ -z "$DEFAULT_STOPSERVICES" ] && DEFAULT_STOPSERVICES="keine"
echo "-o {Befehle die vor dem Backup ausgeführt werden} (Standard: $DEFAULT_STOPSERVICES)"
echo "-P Nutzung des partitionsorientierten Backupmode"
echo "-t {Backuptyp} ($ALLOWED_TYPES) (Standard: $DEFAULT_BACKUPTYPE)"
echo "-T Liste der Partitionen die im partitionsorientierten Mode zu sichern sind} (Partitionsnummern, z.B. \"1 2 3\" oder \"*\" für alle). (Standard: ${DEFAULT_PARTITIONS_TO_BACKUP})"
echo "--tarCompressionTool {tar Kompressionstool} ($TAR_COMPRESSION_TOOLS_SUPPORTED_LIST)"
echo "--tarCompressionToolOptions {Optionen für das tar Kompressionstool}"
echo "-z DD und TAR Backup verkleinern mit gzip (Standard: ${NO_YES[$DEFAULT_ZIP_BACKUP]})"
echo ""
echo "-Restore Optionen-"
echo "-0 Keine Partitionierung des Restoregerätes"
echo "-00 Keine Formatierung der restorten Partitionen (nur bei Backuptyp rsync)"
echo "-1 Fehler bei der Formatierung des Restoregerätes werden ignoriert"
[ -z "$DEFAULT_RESTORE_DEVICE" ] && DEFAULT_RESTORE_DEVICE="keiner"
echo "-C Beim Formatieren der Restorepartitionen wird auf Badblocks geprüft (Standard: ${NO_YES[$DEFAULT_CHECK_FOR_BAD_BLOCKS]})"
echo "-d {restoreGerät} (Standard: $DEFAULT_RESTORE_DEVICE) (Beispiel: /dev/sda)"
echo "-R {rootPartition} (Standard: restoreDevice) (Beispiel: /dev/sdb1)"
echo "-T {Liste der Partitionen die vom partitionsorientierten Backup zu restoren sind} (Partitionsnummern, z.B. \"1 2 3\" oder \"*\" für alle). (Standard: ${DEFAULT_PARTITIONS_TO_BACKUP})"
echo "--resizeRootFS|resizeLastPartitionFS (Standard: ${NO_YES[$DEFAULT_RESIZE_ROOTFS]})"
}
function usageFI() {
echo "$GIT_CODEVERSION"
echo "Käyttö: $MYSELF [valinta]* {varmuuskopionPolku}"
echo ""
echo "-Yleiset asetukset-"
[ -z "$DEFAULT_EMAIL" ] && DEFAULT_EMAIL="ei"
echo "-b {dd lohkon koko} (oletus: $DEFAULT_DD_BLOCKSIZE)"
[ -z "$DEFAULT_DD_PARMS" ] && DEFAULT_DD_PARMS="ei"
echo "-D {dd lisäparametrit} (oletus: $DEFAULT_DD_PARMS)"
echo "-e {sähköpostiosoite} (oletus: $DEFAULT_EMAIL)"
[ -z "$DEFAULT_EMAIL_PARMS" ] && DEFAULT_EMAIL_PARMS="ei"
echo "-E {sähköpostitoiminnon lisäparametrit} (oletus: $DEFAULT_EMAIL_PARMS)"
echo "-f {asetustiedoston tiedostonimi}"
echo "-g Näytä edistymispalkki"
echo "-G {viestien kieli} (${SUPPORTED_LANGUAGES[*]}) (oletus: $LANGUAGE)"
echo "-h Näytä tämä ohje"
echo "-l {lokitaso} ($POSSIBLE_LOG_LEVELs) (oletus: ${LOG_LEVELs[$DEFAULT_LOG_LEVEL]})"
echo "-m {viestitaso} ($POSSIBLE_MSG_LEVELs) (oletus: ${MSG_LEVELs[$DEFAULT_MSG_LEVEL]})"
echo "-M {varmuuskopion selite}"
echo "-s {käytettävä sähköpostiohjelma} ($SUPPORTED_MAIL_PROGRAMS) (oletus: $DEFAULT_MAIL_PROGRAM)"
echo "--timestamps Lisää aikaleima viestien alkuun (oletus: ${NO_YES[$DEFAULT_TIMESTAMPS]})"
echo "-u {excludeList} Lista hakemistoista, jotka ohitetaan tar- ja rsync-varmuuskopioissa"
echo "-U Nykyinen skriptin versio korvataan uusimmalla versiolla. Nykyinen versio varmuuskopioidaan ja sen voi palauttaa parametrillä -V"
echo "-v Sanallista varmuuskopiotyökalujen tilatiedot (oletus: ${NO_YES[$DEFAULT_VERBOSE]})"
echo "-V Palauta skriptin edellinen versio"
echo "-z Pakkaa varmuuskopiotiedosto käyttäen gzip:iä (oletus: ${NO_YES[$DEFAULT_ZIP_BACKUP]})"
echo ""
echo "-Varmuuskopioinnin valinnat-"
[ -z "$DEFAULT_STARTSERVICES" ] && DEFAULT_STARTSERVICES="ei"
echo "-a {varmuuskopion jläkeen suoritettavat komennot} (oletus: $DEFAULT_STARTSERVICES)"
echo "-B Tee käynnistysosiosta kopio tar tiedostoon (oletus: $DEFAULT_TAR_BOOT_PARTITION_ENABLED)"
echo "-F Varmuuskopioinnin simulointi"
echo "-k {säilytettävien varmuuskopioiden lkm} (oletus: $DEFAULT_KEEPBACKUPS)"
[ -z "$DEFAULT_STOPSERVICES" ] && DEFAULT_STOPSERVICES="ei"
echo "-o {ennen varmuuskopiointia suoritettavat komennot} (oletus: $DEFAULT_STOPSERVICES)"
echo "-t {varmuuskopion tyyppi} ($ALLOWED_TYPES) (oletus: $DEFAULT_BACKUPTYPE)"
echo "-T {Lista kopioitavista osioista} (Osionumerot, esim. \"1 2 3\"). Valinta käytettävissä vain parametrin -P kanssa (oletus: ${DEFAULT_PARTITIONS_TO_BACKUP})"
echo ""
echo "-Palautuksen valinnat-"
echo "-0 SD-korttia ei alusteta"
echo "-1 SD-kortin alustuksen virheet ohitetaan"
[ -z "$DEFAULT_RESTORE_DEVICE" ] && DEFAULT_RESTORE_DEVICE="ei"
echo "-C Tarkistetaan palautettavien osioiden epäkelvot lohkot (oletus: $DEFAULT_CHECK_FOR_BAD_BLOCKS)"
echo "-d {palautuslaite} (oletus: $DEFAULT_RESTORE_DEVICE) (Esimerkki: /dev/sda)"
echo "-R {juuriosio} (oletus: restoreDevice) (Esimerkki: /dev/sdb1)"
echo "--resizeRootFS|resizeLastPartitionFS (oletus: ${NO_YES[$DEFAULT_RESIZE_ROOTFS]})"
}
function mentionHelp() {
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MENTION_HELP "$MYSELF"
}
# there is an issue when a parameter starts with "-" which may a new option
# Workaround1: if parameter contains at least one space it's considered as a parameter and not an option even the string starts with '-'
# Workaround2: prefix parameter with \ (has to be \\ in bash commandline)
function checkOptionParameter() { # option parameter
logEntry "$@"
local nospaces="${2/ /}"
if [[ "$nospaces" != "$2" ]]; then
echo "$2"
logExit "$2"
return 0
fi
if [[ "${2:0:1}" == "\\" ]]; then
echo "${2:1}"
logExit "${2:1}"
return 0
elif [[ "$2" =~ ^(\-|\+|\-\-|\+\+) || -z "$2" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_OPTION_REQUIRES_PARAMETER "$1"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MENTION_HELP "$MYSELF"
echo ""
logExit ""
return 1
fi
echo "$2"
logExit "$2"
return 0
}
# -x and -x+ enables, -x- disables flag
# --opt and --opt+ enables, --opt- disables flag
# 0 -> disabled, 1 -> enabled
function getEnableDisableOption() { # option
case "$1" in
-*-) echo 0;;
-*+|-*) echo 1;;
*) writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNKNOWN_OPTION "$1"
mentionHelp
exitError $RC_PARAMETER_ERROR
;;
esac
}
# misc other vars
BACKUP_DIRECTORY_NAME=""
BACKUPFILE=""
CUSTOM_CONFIG_FILE_INCLUDED=0
DEPLOY=0
DYNAMIC_MOUNT_EXECUTED=0
EXCLUDE_DD=0
FAKE=0
FORCE_SFDISK=0
FORCE_UPDATE=0
[[ "${BASH_SOURCE[0]}" -ef "$0" ]]
INCLUDE_ONLY=$?
IS_SNAPSHOT=0
IS_UBUNTU=0
NO_YES_QUESTION=0
OPTION_T_USED=0
PROGRESS=0
REGRESSION_TEST=0
RESTORE=0
RESTOREFILE=""
RESTORETEST_REQUIRED=0
REVERT=0
ROOT_PARTITION_DEFINED=0
SHARED_BOOT_DIRECTORY=0
SKIP_SFDISK=0
SKIP_FORMAT=0
UPDATE_MYSELF=0
UPDATE_POSSIBLE=0
VERSION_DEPRECATED=0
WARNING_MESSAGE_WRITTEN=0
CLEANUP_RC=0
UPDATE_CONFIG=0
UNSUPPORTED_ENVIRONMENT="${UNSUPPORTED_ENVIRONMENT:=0}"
rc=0
PARAMS=""
# initialize default config
initializeDefaultConfigVariables
# assign default config to variables
copyDefaultConfigVariables
##### Now do your job
ARG_BAK=("$@") # save invocation options
# handle options which don't require root access, use system language
if containsElement "-h" "${ARG_BAK[@]}" || containsElement "--help" "${ARG_BAK[@]}" || containsElement "-?" "${ARG_BAK[@]}" || containsElement "--version" "${ARG_BAK[@]}"; then
case "$1" in
--version)
echo "Version: $VERSION CommitSHA: $GIT_COMMIT_ONLY CommitDate: $GIT_DATE_ONLY CommitTime: $GIT_TIME_ONLY"
exitNormal
;;
*) usage
exitNormal
;;
esac
fi
logEnable
if (( $UID != 0 && ! INCLUDE_ONLY )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_RUNASROOT "$0" "$INVOCATIONPARMS"
exitError $RC_MISC_ERROR
fi
lockingFramework
trapWithArg cleanupStartup SIGINT SIGTERM EXIT
INVOCATIONPARMS="" # save passed opts for logging
for (( i=1; i<=$#; i++ )); do
p=${!i}
INVOCATIONPARMS="$INVOCATIONPARMS $p"
done
readConfigParameters # overwrite defaults with settings in config files
copyDefaultConfigVariables # and update variables with config file contents
logOptions "Standard option files"
# check if language was overwritten by config option
if [[ -n $DEFAULT_LANGUAGE ]]; then
if ! containsElement "${DEFAULT_LANGUAGE}" "${SUPPORTED_LANGUAGES[@]}"; then
DEFAULT_LANGUAGE="$MSG_LANG_FALLBACK" # unsupported language, fall back to English
else
DEFAULT_LANGUAGE="${DEFAULT_LANGUAGE^^*}"
fi
LANGUAGE=$DEFAULT_LANGUAGE # redefine language now
fi
while (( "$#" )); do # check if option -f was used
case "$1" in
-f)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
CUSTOM_CONFIG_FILE="$o"; shift 2
if [[ ! -f "$CUSTOM_CONFIG_FILE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_ARG_NOT_FOUND "$CUSTOM_CONFIG_FILE"
exitError $RC_MISSING_FILES
fi
CUSTOM_CONFIG_FILE="$(readlink -f "$CUSTOM_CONFIG_FILE")"
set -e
# Can't follow non-constant source. Use a directive to specify location
# shellcheck disable=SC1090
. "$CUSTOM_CONFIG_FILE"
set +e
CUSTOM_CONFIG_FILE_INCLUDED=1
CUSTOM_CONFIG_FILE_VERSION="$(extractVersionFromFile "$CUSTOM_CONFIG_FILE" "$VERSION_CONFIG_VARNAME" )"
logItem "Read config ${CUSTOM_CONFIG_FILE} : ${CUSTOM_CONFIG_FILE_VERSION}$NL$(grep -E -v '^\s*$|^#' "$CUSTOM_CONFIG_FILE")"
copyDefaultConfigVariables # update variables with custom file contents
logOptions "Custome option file"
;;
*) shift # skip option
;;
esac
done
set -- "${ARG_BAK[@]}" # restore all options for second options pass
while (( "$#" )); do
case "$1" in
-0|-0[-+])
SKIP_SFDISK=$(getEnableDisableOption "$1"); shift 1
;;
-00|-00[-+])
SKIP_FORMAT=$(getEnableDisableOption "$1"); shift 1
SKIP_SFDISK=$SKIP_FORMAT
;;
-1|-1[-+])
FORCE_SFDISK=$(getEnableDisableOption "$1"); shift 1
;;
-a)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
STARTSERVICES="$o"; shift 2
;;
-A|-A[-+])
APPEND_LOG=$(getEnableDisableOption "$1"); shift 1
writeToConsole $MSG_LEVEL_MINIMAL $MSG_DEPRECATED_OPTION "-A"
;;
-b)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
DD_BLOCKSIZE="$o"; shift 2
;;
-B|-B[-+])
TAR_BOOT_PARTITION_ENABLED=$(getEnableDisableOption "$1"); shift 1
;;
--bootDevice)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
BOOT_DEVICE="$o"; shift 2
;;
-c|-c[-+])
SKIPLOCALCHECK=$(getEnableDisableOption "$1"); shift 1
;;
-C|-C[-+])
CHECK_FOR_BAD_BLOCKS=$(getEnableDisableOption "$1"); shift 1
;;
--coloring)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
COLORING="$o"; shift 2
;;
-d)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
RESTORE_DEVICE="$o"; RESTORE=1; shift 2
;;
-D)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
DD_PARMS="$o"; shift 2
;;
--dynamicMount)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
DYNAMIC_MOUNT="$o"; shift 2
;;
-e)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
EMAIL="$o"; shift 2
;;
-E)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
EMAIL_PARMS="$o"; shift 2
;;
--eMailColoring)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
EMAIL_COLORING="${o^^}"; shift 2
;;
-f) shift 2
;;
-F|-F[-+])
FAKE=$(getEnableDisableOption "$1"); shift 1
;;
-g|-g[-+])
PROGRESS=$(getEnableDisableOption "$1"); shift 1
;;
-G)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
LANGUAGE="$o"; shift 2
LANGUAGE=${LANGUAGE^^*}
if ! containsElement "${LANGUAGE^^*}" "${SUPPORTED_LANGUAGES[@]}"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_LANGUAGE_NOT_SUPPORTED "$LANGUAGE"
exitError $RC_PARAMETER_ERROR
fi
;;
--ignoreAdditionalPartitions|--ignoreAdditionalPartitions[+-])
IGNORE_ADDITIONAL_PARTITIONS=$(getEnableDisableOption "$1"); shift 1
;;
--ignoreMissingPartitions)
IGNORE_MISSING_PARTITIONS=1; shift 1
;;
--include|--include[+-])
INCLUDE_ONLY=$(getEnableDisableOption "$1"); shift 1
;;
-k)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
KEEPBACKUPS="$o"; shift 2
;;
--keep_dd)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
KEEPBACKUPS_DD="$o"; shift 2
;;
--keep_ddz)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
KEEPBACKUPS_DDZ="$o"; shift 2
;;
--keep_tar)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
KEEPBACKUPS_TAR="$o"; shift 2
;;
--keep_tgz)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
KEEPBACKUPS_TGZ="$o"; shift 2
;;
--keep_rsync)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
KEEPBACKUPS_RSYNC="$o"; shift 2
;;
-l)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
LOG_LEVEL="$o"; shift 2
checkImportantParameters
;;
-L)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
LOG_OUTPUT="$o"; shift 2
checkImportantParameters
;;
-m)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
MSG_LEVEL="$o"; shift 2
checkImportantParameters
;;
-M)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
BACKUP_DIRECTORY_NAME="$o"; shift 2
BACKUP_DIRECTORY_NAME=${BACKUP_DIRECTORY_NAME//[ \/\\\:\.\-]/_}
BACKUP_DIRECTORY_NAME=${BACKUP_DIRECTORY_NAME//[\"]/}
IS_SNAPSHOT=1
;;
-N)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
EXTENSIONS="$o"; shift 2
;;
--notifyStart|--notifyStart[-+])
NOTIFY_START=$(getEnableDisableOption "$1"); shift 1
;;
-o)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
STOPSERVICES="$o"; shift 2
;;
-p)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
BACKUPPATH="$o"; shift 2
if [[ ! -d "$BACKUPPATH" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_ARG_NOT_FOUND "$BACKUPPATH"
exitError $RC_MISSING_FILES
fi
BACKUPPATH="$(readlink -f "$BACKUPPATH")"
;;
-P|-P[-+])
PARTITIONBASED_BACKUP=$(getEnableDisableOption "$1"); shift 1
;;
-r)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
RESTOREFILE="$o"; shift 2
if [[ ! -d "$RESTOREFILE" && ! -f "$RESTOREFILE" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_ARG_NOT_FOUND "$RESTOREFILE"
exitError $RC_MISSING_FILES
fi
RESTOREFILE="$(readlink -f "$RESTOREFILE")"
;;
-R)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
ROOT_PARTITION="$o"; shift 2
ROOT_PARTITION_DEFINED=1
;;
--resizeRootFS|--resizeRootFS[+-]|--resizeLastPartitionFS|--resizeLastPartitionFS[+-])
RESIZE_ROOTFS=$(getEnableDisableOption "$1"); shift 1
;;
--rebootSystem|--rebootSystem[+-])
REBOOT_SYSTEM=$(getEnableDisableOption "$1"); shift 1
;;
-s)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
EMAIL_PROGRAM="$o"; shift 2
;;
-S|-S[-+])
FORCE_UPDATE=$(getEnableDisableOption "$1"); shift 1
;;
--smartRecycle|--smartRecycle[+-])
SMART_RECYCLE=$(getEnableDisableOption "$1"); shift 1
;;
--smartRecycleDryrun|--smartRecycleDryrun[+-])
SMART_RECYCLE_DRYRUN=$(getEnableDisableOption "$1"); shift 1
;;
--smartRecycleOptions)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
SMART_RECYCLE_OPTIONS="$o"; shift 2
;;
--systemstatus|--systemstatus[+-])
SYSTEMSTATUS=$(getEnableDisableOption "$1"); shift 1
;;
-t)
if ! o=$(checkOptionParameter "$1" "$2"); then
exitError $RC_PARAMETER_ERROR
fi
BACKUPTYPE="$o"; shift 2
;;
--timestamps|--timestamps[+-])
TIMESTAMPS=$(getEnableDisableOption "$1"); shift 1
;;
-T)
if ! o="$(checkOptionParameter "$1" "$2")"; then
exitError $RC_PARAMETER_ERROR
fi
# Variable was used as an array but is now assigned a string.
# shellcheck disable=SC2178
PARTITIONS_TO_BACKUP="$o"; shift 2
# Expanding an array without an index only gives the first element.
# shellcheck disable=SC2128
PARTITIONS_TO_RESTORE=$PARTITIONS_TO_BACKUP
PARTITIONBASED_BACKUP=1
OPTION_T_USED=1
;;
--tarCompressionTool|--tct)
if ! o="$(checkOptionParameter "$1" "$2")"; then
exitError $RC_PARAMETER_ERROR
fi
TAR_COMPRESSION_TOOL="$o"; shift 2
;;
--tarCompressionToolOptions|--tcto)
if ! o="$(checkOptionParameter "$1" "$2")"; then
exitError $RC_PARAMETER_ERROR
fi
TAR_COMPRESSION_TOOL_OPTIONS="$o"; shift 2
;;
--telegramToken)
if ! o="$(checkOptionParameter "$1" "$2")"; then
exitError $RC_PARAMETER_ERROR
fi
TELEGRAM_TOKEN="$o"; shift 2
;;
--telegramChatID)
if ! o="$(checkOptionParameter "$1" "$2")"; then
exitError $RC_PARAMETER_ERROR
fi
TELEGRAM_CHATID="$o"; shift 2
;;
--telegramThreadID)
if ! o="$(checkOptionParameter "$1" "$2")"; then
exitError $RC_PARAMETER_ERROR
fi
TELEGRAM_THREADID="$o"; shift 2
;;
--telegramNotifications)
if ! o="$(checkOptionParameter "$1" "$2")"; then
exitError $RC_PARAMETER_ERROR
fi
TELEGRAM_NOTIFICATIONS="$o"; shift 2
;;
-u)
if ! o="$(checkOptionParameter "$1" "$2")"; then
exitError $RC_PARAMETER_ERROR
fi
EXCLUDE_LIST="$o"; shift 2
;;
-U)
UPDATE_MYSELF=1; shift 1
;;
--unsupportedEnvironment|--use)
UNSUPPORTED_ENVIRONMENT=$(getEnableDisableOption "$1"); shift 1
;;
--updateConfig|--updateConfig[+-])
UPDATE_CONFIG=$(getEnableDisableOption "$1"); shift 1
;;
--updateUUIDs|--updateUUIDs[+-])
UPDATE_UUIDS=$(getEnableDisableOption "$1"); shift 1
;;
-v|-v[-+])
VERBOSE=$(getEnableDisableOption "$1"); shift 1
;;
-V)
REVERT=1; shift 1
;;
-x|-x[-+])
EXCLUDE_DD=$(getEnableDisableOption "$1"); shift 1
;;
-y|-y[-+])
DEPLOY=$(getEnableDisableOption "$1"); shift 1
;;
-Y)
NO_YES_QUESTION=1; shift 1
;;
-z|-z[-+])
ZIP_BACKUP=$(getEnableDisableOption "$1"); shift 1
;;
-Z|-Z[-+]) # flag to enable regession test pathes
REGRESSION_TEST=$(getEnableDisableOption "$1"); shift 1
;;
--) # end argument parsing
shift
break
;;
-*|+*) # unknown option
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNKNOWN_OPTION "$1"
mentionHelp
exitError $RC_PARAMETER_ERROR
;;
*) # preserve positional arguments
[[ -z "$PARAMS" ]] && PARAMS="$1" || PARAMS="$PARAMS $1"
shift
;;
esac
done
if (( ! $INCLUDE_ONLY )); then
# set positional arguments in argument list $@
set -- "$PARAMS"
if (( $RESTORE )); then
rstFileName="${LOG_FILE/$LOGFILE_EXT/$LOGFILE_RESTORE_EXT}"
LOG_FILE="$rstFileName"
LOGFILE_EXT="$LOGFILE_RESTORE_EXT"
rstFileName="${MSG_FILE/$MSGFILE_EXT/$MSGFILE_RESTORE_EXT}"
MSG_FILE="$rstFileName"
MSGFILE_EXT="$MSGFILE_RESTORE_EXT"
fi
if (( ! $RESTORE )); then
exlock_now
# Check exit code directly with e.g. if mycmd;, not indirectly with $?
#shellcheck disable=SC2181
if (( $? )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_INSTANCE_ACTIVE
exitError $RC_MISC_ERROR
fi
fi
fileParameter="$1"
if hasSpaces "$fileParameter"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_CONTAINS_SPACES "$fileParameter"
exitError $RC_MISC_ERROR
fi
if [[ -n "$1" ]]; then
shift 1
if [[ ! -d "$fileParameter" && ! -f "$fileParameter" ]]; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_FILE_ARG_NOT_FOUND "$fileParameter"
exitError $RC_MISSING_FILES
else
fileParameter="$(readlink -f "$fileParameter")"
fi
fi
unusedParms="$*"
if [[ -n "$unusedParms" ]]; then
usage
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNUSED_PARAMETERS "$unusedParms"
exitError $RC_PARAMETER_ERROR
fi
if ! isSupportedEnvironment; then
if (( $UNSUPPORTED_ENVIRONMENT )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNSUPPORTED_ENVIRONMENT_CONFIRMED
else
writeToConsole $MSG_LEVEL_MINIMAL $MSG_UNSUPPORTED_ENVIRONMENT
exitError $RC_UNSUPPORTED_ENVIRONMENT
fi
fi
if (( $DEPLOY )); then
deployMyself
exitNormal
fi
if (( $REVERT )); then
revertScriptVersion
exitNormal
fi
if (( $UPDATE_MYSELF )); then
downloadPropertiesFile FORCE
updateScript
if (( $? )); then
updateConfig
fi
exitNormal
fi
if (( $RESTORE && $NO_YES_QUESTION )); then # WARNING: dangerous option !!!
if [[ ! $RESTORE_DEVICE =~ $YES_NO_RESTORE_DEVICE ]]; then # make sure we're not killing a disk by accident
writeToConsole $MSG_LEVEL_MINIMAL $MSG_YES_NO_DEVICE_MISMATCH "$RESTORE_DEVICE" "$YES_NO_RESTORE_DEVICE"
exitError $RC_MISC_ERROR
fi
fi
check4RequiredCommands
if (( $UPDATE_CONFIG )); then
updateConfig
exitNormal
fi
logItem "RESTORE: $RESTORE - fileParameter: $fileParameter"
if [[ -n $fileParameter ]]; then
if (( $RESTORE )); then
RESTOREFILE="$(readlink -f "$fileParameter")"
else
BACKUPPATH="$(readlink -f "$fileParameter")"
fi
fi
if ( (( $RESTORE )) && [[ -z "$RESTOREFILE" ]] ) || ( (( ! $RESTORE )) && [[ -z "$BACKUPPATH" ]] ); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_FILEPARAMETER
mentionHelp
exitError $RC_MISSING_FILES
fi
if [[ -z $RESTORE_DEVICE ]] && (( $ROOT_PARTITION_DEFINED )); then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_MISSING_RESTOREDEVICE_OPTION
exitError $RC_PARAMETER_ERROR
fi
_prepare_locking
logItem "Enabling trap handler"
trapWithArg cleanup SIGINT SIGTERM EXIT
lockMe
writeToConsole $MSG_LEVEL_MINIMAL $MSG_STARTED "$HOSTNAME" "$MYSELF" "$VERSION" "$GIT_DATE_ONLY" "$GIT_COMMIT_ONLY" "$(date)"
logger -t "$MYSELF" "Started $VERSION ($GIT_COMMIT_ONLY)"
(( $IS_BETA )) && writeToConsole $MSG_LEVEL_MINIMAL $MSG_INTRO_BETA_MESSAGE
(( $IS_DEV )) && writeToConsole $MSG_LEVEL_MINIMAL $MSG_INTRO_DEV_MESSAGE
(( $IS_HOTFIX )) && writeToConsole $MSG_LEVEL_MINIMAL $MSG_INTRO_HOTFIX_MESSAGE
setupEnvironment
if (( $NOTIFY_START )); then
if (( ! $RESTORE )); then
msg="$(getMessage $MSG_TITLE_STARTED "$HOSTNAME")"
if [[ -n "$EMAIL" ]]; then
sendEMail "" "$msg"
fi
if [[ -n "$TELEGRAM_TOKEN" ]]; then
sendTelegramm "$msg"
fi
if [[ -n "$PUSHOVER_USER" ]]; then
sendPushover "$msg"
fi
if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
sendSlack "$msg"
fi
callNotificationExtension $rc
fi
fi
if (( $ETC_CONFIG_FILE_INCLUDED )); then
writeToConsole $MSG_LEVEL_DETAILED $MSG_INCLUDED_CONFIG "$ETC_CONFIG_FILE" # "$ETC_CONFIG_FILE_VERSION"
logItem "Read config ${ETC_CONFIG_FILE} : ${ETC_CONFIG_FILE_VERSION}$NL$(grep -E -v '^\s*$|^#' "$ETC_CONFIG_FILE")"
fi
if (( $HOME_CONFIG_FILE_INCLUDED )); then
writeToConsole $MSG_LEVEL_DETAILED $MSG_INCLUDED_CONFIG "$HOME_CONFIG_FILE" # "$HOME_CONFIG_FILE_VERSION"
logItem "Read config ${HOME_CONFIG_FILE} : ${HOME_CONFIG_FILE_VERSION}$NL$(grep -E -v '^\s*$|^#' "$HOME_CONFIG_FILE")"
fi
if (( $CURRENTDIR_CONFIG_FILE_INCLUDED )); then
writeToConsole $MSG_LEVEL_DETAILED $MSG_INCLUDED_CONFIG "$CURRENTDIR_CONFIG_FILE" # "$CURRENTDIR_CONFIG_FILE_VERSION"
logItem "Read ${CURRENTDIR_CONFIG_FILE} : ${CURRENTDIR_CONFIG_FILE_VERSION}$NL$(grep -E -v '^\s*$|^#' "$CURRENTDIR_CONFIG_FILE")"
fi
if (( $CUSTOM_CONFIG_FILE_INCLUDED )); then
writeToConsole $MSG_LEVEL_DETAILED $MSG_INCLUDED_CONFIG "$CUSTOM_CONFIG_FILE" # "$CUSTOM_CONFIG_FILE_VERSION"
logItem "Read ${CUSTOM_CONFIG_FILE} : ${CUSTOM_CONFIG_FILE_VERSION}$NL$(grep -E -v '^\s*$|^#' "$CUSTOM_CONFIG_FILE")"
fi
logOptions "Invocation options"
logSystem
downloadPropertiesFile
updateRestoreReminder
reportNews
if isVersionDeprecated "$VERSION"; then
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SCRIPT_IS_DEPRECATED "$VERSION"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SCRIPT_IS_DEPRECATED "$VERSION"
writeToConsole $MSG_LEVEL_MINIMAL $MSG_SCRIPT_IS_DEPRECATED "$VERSION"
VERSION_DEPRECATED=1
NEWS_AVAILABLE=1
fi
doit # no return
fi # ! INCLUDE_ONLY