Приложил скрипт, который у меня получился. mkbackup - сам скрипт. mks - парсер, убирающий тесты. ready - готовый скрипт после обработки.
Вроде, работает, хотя и не всё доделано. Ощущения: получилось очень "тяжеловесно". Просто запутаться в тестах. Но тесты ловят случайные ошибки (лучше, чем без тестов совсем). Что скажете?
#!/bin/sh # # Backup script, used rdiff-backup. Created by Artiom N.. # # Using: mkbackup <path to config> # Configuration options. OPT_SRC_DIR="/" OPT_BACKUP_DIR="/mnt/backup/backup_uc" OPT_BTMP_DIR="/mnt/backup/btmp" OPT_INC_LIST="include.cfg" OPT_EXC_LIST="exclude.cfg" OPT_MYSQL_LOGIN="root" OPT_MYSQL_PASSWORD="" OPT_LUKS_PARTS="" OPT_KERNEL_CONFIG="/usr/src/linux/.config" OPT_REMOVE_PERIOD="1M" OPT_FBACKUP_KCONF="true" OPT_FBACKUP_MYSQL="true" OPT_FBACKUP_PACKAGES="true" OPT_FBACKUP_PARTS="true" # Verbosity: 0 - no diagnostics, 1 - error messages, 2 - verbose, 3 - debug. V_LEVEL="3" # Resulting filename constants. FN_MYSQL_DUMP="mysql.dump" FN_PKGS_DUMP="pagkages.dump" FN_LVM_TEMPL="lvbk_%s" FN_LUKS_TEMPL="luksbk_%s" FN_KERNEL_CONF="linux_config" # Directories in $OPT_BACKUP_DIR for backup system tree and special files. FN_DIR_TREE="tree" FN_DIR_SPECIAL="special" # File or directory, used as flag, for selecting backup type. FN_FLAG="rdiff-backup-data" # Binaries path. CMD_DPKG="dpkg" CMD_VGBCK="vgcfgbackup" CMD_DUMP="mysqldump" CMD_CRYPTSETUP="cryptsetup" CMD_RDBCKP="rdiff-backup" CMD_NOTIFIER="logger" ###TESTING assert() # $1 - message; # $2 - comannd. { local RT=0 echo "Command: $2" eval $2 || { RT="$?"; printf "$1 (ret: 0x%x)" $RT && exit 1; } return 0; } ###/TESTING #------------------------------------------------------------------------------- dbg() # $1 - verbosity level; # $2 - message. { [ "$V_LEVEL" -ge "$1" ] && echo "$2" > /dev/stderr } #------------------------------------------------------------------------------- err() # $1 - message. { dbg 1 "$1" dbg 0 "Backup failed..." # failed...&& exit 1 return 1 } ###TESTING #------------------------------------------------------------------------------- check_deps() # Check for the using executables. # $1..$n - list of executables. { local s while [ ! -z "$1" ]; do s="`type $1`" && dbg 3 "Checking dep $s" || { dbg 1 "$s" && return 1; } shift done; return 0; } ###/TESTING #------------------------------------------------------------------------------- get_execs() # Set full path of the executables in the variables. # $1..$n - list of executables. { local s while [ ! -z "$1" ]; do eval "s=\$$1" dbg 3 "Exec filename: $s" s="$(whereis -b $s|awk '{print $2}')" dbg 3 "Exec fullpath: $s" [ -z "$s" ] && { err "Cant't find \"eval \$$1\""; return 1; } eval $1=$s shift done return 0; } #------------------------------------------------------------------------------- load_config() # $1 - config filename. { dbg 3 "$LINENO ${FUNCNAME}(): Loading file \"$1\"" dbg 3 "PWD: $PWD" [ -r "$1" ] || { err "Can't open file \"$1\"..."; return 1; } . "$1" || return $? return $?; } #------------------------------------------------------------------------------- notify() # Notify user, about backup process. # $1 - message. { "$CMD_NOTIFIER" "$1" return 0; } #------------------------------------------------------------------------------- backup_special() # Backup special system data. # Return values: # $? & 0x000f - backup_kernel_conf result; # $? & 0x00f0 - backup_mysql result; # $? & 0x0f00 - backup_packages result; # $? & 0xf000 - backup_parted_data result. { # # Interfaces. # backup_mysql() # $1 - user name; # $2 - password; # $3 - options. # Return 0, if all good, 1 if error. { dbg 3 "$LINENO ${FUNCNAME}(): Loading MySQL config..." dbg 2 "Dumping databases..." dbg 3 "Dump command: \"$CMD_DUMP\" -u\"$1\" -p\"$2\" --compact -f --all-databases $3" "$CMD_DUMP" -u"$1" -p"$2" --compact -f --all-databases $3 || \ { err "MySQL backup failed!"; return 1; } return 0; } backup_packages() { dbg 3 "$LINENO ${FUNCNAME}(): Dumping package list..." "$CMD_DPKG" --get-selections return $?; } get_part_string() # Return partition string, created from template, # in the "part=file_to_save; ..." format. # $1 - template, where %s - partition name (exmpl.: luksbk_%s). # $2 - partition list ("part_1; part_2; ... ; part_n"). { dbg 3 "$LINENO ${FUNCNAME}(): \$1 = \"$1\"; \$2 = \"$2\"" export TEMPL="$1" local IFS=";" for i in $2; do # dbg 3 "\$i = $i" echo "$i"|awk \ '/.*[^\/]+/ { n = split($0, pcomps, "/") printf("%s="ENVIRON["TEMPL"]";", $0, pcomps[n]); }' done return 0; } backup_parted_data() # $1 - template for the saving LVM backups; # $2 - LUKS "partition=file_to_save; ..." list. # Return values: # 1 - LVM backup error; # 2 - LUKS backup error; # 3 - other. { dbg 2 "Dumping partition data..." # LVM. dbg 3 "$LINENO ${FUNCNAME}(): Dumping LVM headers to the files \"$1\"..." "$CMD_VGBCK" -f"$1" --ignorelockingfailure || \ { err "LVM backup failed!"; return 1; } # LUKS. #OIFS="$IFS" # Local field separator. local IFS=';' dbg 3 "LUKS parts (\$2) = $2" for i in $2; do dbg 3 "$LINENO ${FUNCNAME}(): LUKS \$i = $i" eval $(echo "$i"|awk 'BEGIN { FS="=" } { printf("LSB_HB_PART=\"%s\"; LSB_HB_FILE=\"%s\"", $1, $2); }') dbg 2 "$LINENO ${FUNCNAME}(): Dumping LUKS header from partition \"$LSB_HB_PART\" to the file \"$LSB_HB_FILE\"" dbg 3 "$CMD_CRYPTSETUP" --header-backup-file="$LSB_HB_FILE" luksHeaderBackup "$LSB_HB_PART" "$CMD_CRYPTSETUP" --header-backup-file="$LSB_HB_FILE" luksHeaderBackup "$LSB_HB_PART" || \ { err "LUKS backup of \"$LSB_HB_PART\" to \"$LSB_HB_FILE\" failed!"; return 2; } done #IFS="$OIFS" local RT=$? [ $RT -ne 0 ] && RT=3 return $RT; } backup_kernel_conf() # $1 - config filename; { dbg 3 "$LINENO ${FUNCNAME}(): Saving kernel configuration from \"$1\"..." [ -s "$1" ] && cat "$1" || err "Kernel config [$1] backup failed!" return $?; } # # End of interfaces. # if [ ! -d "$OPT_BTMP_DIR" ]; then mkdir -p "$OPT_BTMP_DIR" || return 1 else rm -rf "$OPT_BTMP_DIR"/* fi touch "$OPT_BTMP_DIR/backup_directory" || return 2 ###TESTING OPT_MYSQL_LOGIN=root OPT_MYSQL_UPSW=rootsqld OPT_MYSQL_OPTS="--no-data" assert "backup_mysql error 0" \ "backup_mysql $OPT_MYSQL_LOGIN $OPT_MYSQL_UPSW $OPT_MYSQL_OPTS > \"$OPT_BTMP_DIR/_mysql.log\"" assert "backup_packages error" "backup_packages > \"$OPT_BTMP_DIR/_pkg.log\"" assert "backup_parted_data error 0" \ '(backup_parted_data "_lvm bk_%s" "/dev/vol1=vol1.lbck;/dev/_volxxx2=vol2.lbck"; x=$?; [ $x -ne 0 ])' assert "backup_kernel_conf error 0" \ '(backup_kernel_conf bla-bla-bla... '"$OPT_BTMP_DIR/_config"'; x=$?; [ $x -ne 0 ]; )' assert "backup_kernel_conf error 1" \ "backup_kernel_conf \"$OPT_KERNEL_CONFIG\" > \"$OPT_BTMP_DIR/_config\"" assert "config compare error" "cmp \"$OPT_KERNEL_CONFIG\" \"$OPT_BTMP_DIR/_config\"" assert "get_part_string error 1" \ "([ 'part1=bck_part1_test;/etc/part2=bck_part2_test;/dev/part3=bck_part3_test;' = '`get_part_string bck_%s_test 'part1;;/etc/part2;/dev/part3;/'`' ])" if [ `id -u` -ne 0 ]; then echo "backup_parted_data not tested! Need to be root." assert "backup_parted_data error 1" \ '(backup_parted_data "$OPT_BTMP_DIR/_lvm bk_%s" "`get_part_string $OPT_BTMP_DIR/_%s.lbck /dev/sdb1`;/dev/mapper/gv_main-luks=$OPT_BTMP_DIR/_main - luks.lbck"; x=$?; [ $x -ne 0 ])' else rm '_lvm bk'* _*.lbck assert "backup_parted_data error 1" \ 'backup_parted_data "$OPT_BTMP_DIR/_lvm bk_%s" "`get_part_string $OPT_BTMP_DIR/_%s.lbck /dev/sdb1`/dev/mapper/gv_main-luks=$OPT_BTMP_DIR/_main - luks.lbck"' assert "backup_parted_data error 2" \ '(cd "$OPT_BTMP_DIR" && for i in "_lvm bk_"*; do echo Test size of "$i"; [ -s "$i" ] || exit 1; done && [ -s _sdb1.lbck ] && [ -s "_main - luks.lbck" ])' fi rm -rf "$OPT_BTMP_DIR" && mkdir "$OPT_BTMP_DIR" [ "x$1" = "xtst" ] && return 0; : ###/TESTING # For error code used one hex digit on function. local RET_CODE=0 local RT=0 if [ x"$OPT_FBACKUP_KCONF" = xtrue ]; then backup_kernel_conf \ "$OPT_KERNEL_CONFIG" > "$OPT_BTMP_DIR/$FN_KERNEL_CONF" # Error code == 1 is in use (can't make directory). RT=$? [ $RT -ne 0 ] && RET_CODE=$((0x2)) dbg 3 "Kernel backup errcode: $RT" fi if [ x"$OPT_FBACKUP_MYSQL" = xtrue ]; then backup_mysql \ "$OPT_MYSQL_LOGIN" "$OPT_MYSQL_PASSWORD" "$OPT_MYSQL_OPTIONS" \ > "$OPT_BTMP_DIR/$FN_MYSQL_DUMP" RT=$? RET_CODE=$(($RT << 4 | $RET_CODE)) dbg 3 "MySQL backup errcode: $RT" fi if [ x"$OPT_FBACKUP_PACKAGES" = xtrue ]; then backup_packages > "$OPT_BTMP_DIR/$FN_PKGS_DUMP" RT=$? RET_CODE=$(($RT << 8 | $RET_CODE)) dbg 3 "Packages backup errcode: $RT" fi if [ x"$OPT_FBACKUP_PARTS" = xtrue -a `id -u` -eq 0 ]; then backup_parted_data \ "$FN_LVM_TEMPL" "`get_part_string \"$FN_LUKS_TEMPL\" \"$OPT_LUKS_PARTS\"`" RT=$? RET_CODE=$(($RT << 12 | $RET_CODE)) dbg 3 "Partitions backup errcode: $RT" fi dbg 3 "backup_special errcode: $RET_CODE" return $RET_CODE; } #------------------------------------------------------------------------------- backup_tree() # $1 - tree; # $2 - dest; # $3 - incl globbing file list (optional); # $4 - excl list (optional); { local RD_OPTS; RD_OPTS="--create-full-path --exclude-device-files --exclude-fifos " RD_OPTS="$RD_OPTS --exclude-sockets --exclude-symbolic-links --no-compression" RD_OPTS="$RD_OPTS -v$((V_LEVEL * 3))" if [ -n "$3" -o -n "$4" ]; then [ -r "$3" ] || return 1 RD_OPTS="$RD_OPTS --include-globbing-filelist $3" [ -r "$4" ] || return 2 RD_OPTS="$RD_OPTS --exclude-globbing-filelist $4" fi dbg 3 "Backup command: \"$CMD_RDBCKP\" $RD_OPTS \"$1\" \"$2\"" "$CMD_RDBCKP" $RD_OPTS "$1" "$2" return $?; } #------------------------------------------------------------------------------- full_backup() # Full backup function. # Return values: # $? & 0x00000f - backup_tree for main tree code; # $? & 0x0000f0 - backup_tree for special files code; # $? & 0xffff00 - backup_special code. { local RET_CODE=0 local RT=0 dbg 3 "$LINENO ${FUNCNAME}(): Backup special files..." backup_special || { RET_CODE="$?"; err "Backup special return non null error code ($RET_CODE)!"; notify "$0 -> backup_special error code: $RET_CODE"; } if [ ! -d "$FN_DIR_SPECIAL" ]; then mkdir -p "$FN_DIR_SPECIAL" || return 2 fi if [ ! -d "$FN_DIR_TREE" ]; then mkdir -p "$FN_DIR_TREE" || return 3 fi backup_tree "$OPT_BTMP_DIR" "$FN_DIR_SPECIAL"; RT=$? RET_CODE=$(($RET_CODE << 4 | $RT)) rm -rf "$OPT_BTMP_DIR" dbg 3 "$LINENO ${FUNCNAME}(): Backup main tree..." backup_tree "$OPT_SRC_DIR" "$FN_DIR_TREE" "$OPT_INC_LIST" "$OPT_EXC_LIST" RT=$? RET_CODE=$(($RET_CODE << 4 | $RT)) return $RET_CODE; } #------------------------------------------------------------------------------- diff_backup() # Function for the differential backup making. # Return values: # $? & 0x000000f - del_old_diffs code; # $? & 0xffff000 - full_backup code. { local RET_CODE=0 local RT=0 del_old_diffs() { local CMD_REMOVE CMD_REMOVE="$CMD_RDBCKP --remove-older-than $OPT_REMOVE_PERIOD" dbg 2 "Deleting old differentials..." dbg 3 "$LINENO ${FUNCNAME}(): $CMD_REMOVE \"$FN_DIR_TREE\" && $CMD_REMOVE \"$FN_DIR_SPECIAL\"" $CMD_REMOVE "$FN_DIR_TREE" && $CMD_REMOVE "$FN_DIR_SPECIAL" return $?; } del_old_diffs RET_CODE=$? full_backup RT=$? RET_CODE=$(($RET_CODE << 4 | $RT)) return $RET_CODE; } #------------------------------------------------------------------------------- main() # $1 - config. { # Setting executables paths. get_execs CMD_DPKG CMD_DUMP CMD_VGBCK CMD_CRYPTSETUP CMD_RDBCKP CMD_NOTIFIER # Loading configuration file. [ -z "$1" ] && { err "Syntax: $0 <config>" && return 1; } load_config "$1" notify "Backup started..." FN_DIR_TREE="$OPT_BACKUP_DIR/$FN_DIR_TREE" FN_DIR_SPECIAL="$OPT_BACKUP_DIR/$FN_DIR_SPECIAL" FN_LUKS_TEMPL="$OPT_BTMP_DIR/$FN_LUKS_TEMPL" FN_LVM_TEMPL="$OPT_BTMP_DIR/$FN_LVM_TEMPL" # Selecting backup type and do work. if [ -e "$FN_DIR_TREE/$FN_FLAG" ]; then diff_backup else full_backup fi local RT=$(($? << 4)) notify "Backup ended with code $RT..." return $RT } ###TESTING #------------------------------------------------------------------------------- # Unit testing. test_code() { mock_backup() { return 0; } assert "get_execs error 0" \ "get_execs CMD_DPKG CMD_DUMP CMD_VGBCK CMD_CRYPTSETUP CMD_RDBCKP" assert "get_execs error 1" \ '(for i in "$CMD_DPKG" "$CMD_DUMP" "$CMD_VGBCK" "$CMD_CRYPTSETUP" "$CMD_RDBCKP"; do echo $i; [ -z "$i" ] && exit 1; done; exit 0;)' assert "check_deps error 0" "check_deps ls dpkg" assert "check_deps error 1" '(check_deps a b c; x=$?; [ $x -ne 0 ])' assert "check deps error: real files" \ "check_deps \"$CMD_DPKG\" \"$CMD_DUMP\" \"$CMD_VGBCK\" \"$CMD_CRYPTSETUP\" \"$CMD_RDBCKP\"" rm -f "/tmp/lala la" assert "load_config error 0" '(load_config "/tmp/lala la"; x=$?; [ $x -ne 0 ])' echo "XPORT=1">'/tmp/lala la' || exit 1 assert "load_config error 1" "load_config '/tmp/lala la'" assert "load_config error 2" '[ x1 = x$XPORT ]' rm -f "/tmp/lala la" OPT_MYSQL_PASSWORD="rootsqld" OPT_BACKUP_DIR="$PWD/_tst" OPT_BTMP_DIR="$PWD/_tmp" FN_DIR_TREE="$OPT_BACKUP_DIR/$FN_DIR_TREE" FN_DIR_SPECIAL="$OPT_BACKUP_DIR/$FN_DIR_SPECIAL" FN_LUKS_TEMPL="$OPT_BTMP_DIR/$FN_LUKS_TEMPL" FN_LVM_TEMPL="$OPT_BTMP_DIR/$FN_LVM_TEMPL" assert "backup_special error" "backup_special tst" echo 1 > /tmp/test_backup echo "/tmp/test_backup" > il rm -rf "$OPT_BACKUP_DIR" && mkdir "$OPT_BACKUP_DIR" assert "backup_tree error 0" '(backup_tree; x=$?; [ $x -ne 0 ];)' _CMD_RDBCKP="$CMD_RDBCKP" CMD_RDBCKP=mock_backup rm -rf "$OPT_BACKUP_DIR" && mkdir "$OPT_BACKUP_DIR" assert 'backup_tree error 1' \ "(backup_tree / \"$OPT_BACKUP_DIR\" \"$PWD/not_existent\" not_existent"';x=$?; [ $x -ne 0 ])' CMD_RDBCKP="$_CMD_RDBCKP" unset _CMD_RDBCKP rm -rf "$OPT_BACKUP_DIR" && mkdir "$OPT_BACKUP_DIR" assert "backup_tree error 2" \ "(backup_tree / \"$OPT_BACKUP_DIR\" $PWD/il"';x=$?; [ $x -ne 0 ])' rm -rf "$OPT_BACKUP_DIR" echo "*" > el assert "backup_tree error 3" "backup_tree / _tst $PWD/il $PWD/el" assert "cat tst error 1" "[ `cat _tst/tmp/test_backup` -eq 1 ]" rm -rf "$OPT_BACKUP_DIR" && mkdir "$OPT_BACKUP_DIR" rm -rf _tmp_old && mv "$OPT_BTMP_DIR" _tmp_old OPT_INC_LIST="$PWD/il" OPT_EXC_LIST="$PWD/el" OPT_LUKS_PARTS='/dev/mapper/gv_main-luks' assert "full_backup error 0" "full_backup" dbg 3 "Wait 1 second..." sleep 1 assert "diff_backup error 0" "diff_backup" exit 0; } main() { test_code } ###/TESTING #------------------------------------------------------------------------------- main; #------------------------------------------------------------------------------- # EOF
#!/bin/sh fl="$1" fl=${-:-$fl} awk ' BEGIN { prf = 0; } /^#!\/bin\/bash/ { print "#!/bin/sh"; next; } /###TESTING/ { prf = pfm + 1; next; } /###\/TESTING/ { if (prf == 0) { print "Error: unexpected ###\/TESTING" > "/dev/stderr"; exit 1; } prf = prf - 1; next; } { if (prf == 0) print; } END { if (prf > 0) { print "Error: unclosed ###TESTING"; exit 1; } } ' "$fl"
#!/bin/sh # # Backup script, used rdiff-backup. Created by Artiom N.. # # Using: mkbackup <path to config> # Configuration options. OPT_SRC_DIR="/" OPT_BACKUP_DIR="/mnt/backup/backup_uc" OPT_BTMP_DIR="/mnt/backup/btmp" OPT_INC_LIST="include.cfg" OPT_EXC_LIST="exclude.cfg" OPT_MYSQL_LOGIN="root" OPT_MYSQL_PASSWORD="" OPT_LUKS_PARTS="" OPT_KERNEL_CONFIG="/usr/src/linux/.config" OPT_REMOVE_PERIOD="1M" OPT_FBACKUP_KCONF="true" OPT_FBACKUP_MYSQL="true" OPT_FBACKUP_PACKAGES="true" OPT_FBACKUP_PARTS="true" # Verbosity: 0 - no diagnostics, 1 - error messages, 2 - verbose, 3 - debug. V_LEVEL="3" # Resulting filename constants. FN_MYSQL_DUMP="mysql.dump" FN_PKGS_DUMP="pagkages.dump" FN_LVM_TEMPL="lvbk_%s" FN_LUKS_TEMPL="luksbk_%s" FN_KERNEL_CONF="linux_config" # Directories in $OPT_BACKUP_DIR for backup system tree and special files. FN_DIR_TREE="tree" FN_DIR_SPECIAL="special" # File or directory, used as flag, for selecting backup type. FN_FLAG="rdiff-backup-data" # Binaries path. CMD_DPKG="dpkg" CMD_VGBCK="vgcfgbackup" CMD_DUMP="mysqldump" CMD_CRYPTSETUP="cryptsetup" CMD_RDBCKP="rdiff-backup" CMD_NOTIFIER="logger" #------------------------------------------------------------------------------- dbg() # $1 - verbosity level; # $2 - message. { [ "$V_LEVEL" -ge "$1" ] && echo "$2" > /dev/stderr } #------------------------------------------------------------------------------- err() # $1 - message. { dbg 1 "$1" dbg 0 "Backup failed..." # failed...&& exit 1 return 1 } #------------------------------------------------------------------------------- get_execs() # Set full path of the executables in the variables. # $1..$n - list of executables. { local s while [ ! -z "$1" ]; do eval "s=\$$1" dbg 3 "Exec filename: $s" s="$(whereis -b $s|awk '{print $2}')" dbg 3 "Exec fullpath: $s" [ -z "$s" ] && { err "Cant't find \"eval \$$1\""; return 1; } eval $1=$s shift done return 0; } #------------------------------------------------------------------------------- load_config() # $1 - config filename. { dbg 3 "$LINENO ${FUNCNAME}(): Loading file \"$1\"" dbg 3 "PWD: $PWD" [ -r "$1" ] || { err "Can't open file \"$1\"..."; return 1; } . "$1" || return $? return $?; } #------------------------------------------------------------------------------- notify() # Notify user, about backup process. # $1 - message. { "$CMD_NOTIFIER" "$1" return 0; } #------------------------------------------------------------------------------- backup_special() # Backup special system data. # Return values: # $? & 0x000f - backup_kernel_conf result; # $? & 0x00f0 - backup_mysql result; # $? & 0x0f00 - backup_packages result; # $? & 0xf000 - backup_parted_data result. { # # Interfaces. # backup_mysql() # $1 - user name; # $2 - password; # $3 - options. # Return 0, if all good, 1 if error. { dbg 3 "$LINENO ${FUNCNAME}(): Loading MySQL config..." dbg 2 "Dumping databases..." dbg 3 "Dump command: \"$CMD_DUMP\" -u\"$1\" -p\"$2\" --compact -f --all-databases $3" "$CMD_DUMP" -u"$1" -p"$2" --compact -f --all-databases $3 || \ { err "MySQL backup failed!"; return 1; } return 0; } backup_packages() { dbg 3 "$LINENO ${FUNCNAME}(): Dumping package list..." "$CMD_DPKG" --get-selections return $?; } get_part_string() # Return partition string, created from template, # in the "part=file_to_save; ..." format. # $1 - template, where %s - partition name (exmpl.: luksbk_%s). # $2 - partition list ("part_1; part_2; ... ; part_n"). { dbg 3 "$LINENO ${FUNCNAME}(): \$1 = \"$1\"; \$2 = \"$2\"" export TEMPL="$1" local IFS=";" for i in $2; do # dbg 3 "\$i = $i" echo "$i"|awk \ '/.*[^\/]+/ { n = split($0, pcomps, "/") printf("%s="ENVIRON["TEMPL"]";", $0, pcomps[n]); }' done return 0; } backup_parted_data() # $1 - template for the saving LVM backups; # $2 - LUKS "partition=file_to_save; ..." list. # Return values: # 1 - LVM backup error; # 2 - LUKS backup error; # 3 - other. { dbg 2 "Dumping partition data..." # LVM. dbg 3 "$LINENO ${FUNCNAME}(): Dumping LVM headers to the files \"$1\"..." "$CMD_VGBCK" -f"$1" --ignorelockingfailure || \ { err "LVM backup failed!"; return 1; } # LUKS. #OIFS="$IFS" # Local field separator. local IFS=';' dbg 3 "LUKS parts (\$2) = $2" for i in $2; do dbg 3 "$LINENO ${FUNCNAME}(): LUKS \$i = $i" eval $(echo "$i"|awk 'BEGIN { FS="=" } { printf("LSB_HB_PART=\"%s\"; LSB_HB_FILE=\"%s\"", $1, $2); }') dbg 2 "$LINENO ${FUNCNAME}(): Dumping LUKS header from partition \"$LSB_HB_PART\" to the file \"$LSB_HB_FILE\"" dbg 3 "$CMD_CRYPTSETUP" --header-backup-file="$LSB_HB_FILE" luksHeaderBackup "$LSB_HB_PART" "$CMD_CRYPTSETUP" --header-backup-file="$LSB_HB_FILE" luksHeaderBackup "$LSB_HB_PART" || \ { err "LUKS backup of \"$LSB_HB_PART\" to \"$LSB_HB_FILE\" failed!"; return 2; } done #IFS="$OIFS" local RT=$? [ $RT -ne 0 ] && RT=3 return $RT; } backup_kernel_conf() # $1 - config filename; { dbg 3 "$LINENO ${FUNCNAME}(): Saving kernel configuration from \"$1\"..." [ -s "$1" ] && cat "$1" || err "Kernel config [$1] backup failed!" return $?; } # # End of interfaces. # if [ ! -d "$OPT_BTMP_DIR" ]; then mkdir -p "$OPT_BTMP_DIR" || return 1 else rm -rf "$OPT_BTMP_DIR"/* fi touch "$OPT_BTMP_DIR/backup_directory" || return 2 # For error code used one hex digit on function. local RET_CODE=0 local RT=0 if [ x"$OPT_FBACKUP_KCONF" = xtrue ]; then backup_kernel_conf \ "$OPT_KERNEL_CONFIG" > "$OPT_BTMP_DIR/$FN_KERNEL_CONF" # Error code == 1 is in use (can't make directory). RT=$? [ $RT -ne 0 ] && RET_CODE=$((0x2)) dbg 3 "Kernel backup errcode: $RT" fi if [ x"$OPT_FBACKUP_MYSQL" = xtrue ]; then backup_mysql \ "$OPT_MYSQL_LOGIN" "$OPT_MYSQL_PASSWORD" "$OPT_MYSQL_OPTIONS" \ > "$OPT_BTMP_DIR/$FN_MYSQL_DUMP" RT=$? RET_CODE=$(($RT << 4 | $RET_CODE)) dbg 3 "MySQL backup errcode: $RT" fi if [ x"$OPT_FBACKUP_PACKAGES" = xtrue ]; then backup_packages > "$OPT_BTMP_DIR/$FN_PKGS_DUMP" RT=$? RET_CODE=$(($RT << 8 | $RET_CODE)) dbg 3 "Packages backup errcode: $RT" fi if [ x"$OPT_FBACKUP_PARTS" = xtrue -a `id -u` -eq 0 ]; then backup_parted_data \ "$FN_LVM_TEMPL" "`get_part_string \"$FN_LUKS_TEMPL\" \"$OPT_LUKS_PARTS\"`" RT=$? RET_CODE=$(($RT << 12 | $RET_CODE)) dbg 3 "Partitions backup errcode: $RT" fi dbg 3 "backup_special errcode: $RET_CODE" return $RET_CODE; } #------------------------------------------------------------------------------- backup_tree() # $1 - tree; # $2 - dest; # $3 - incl globbing file list (optional); # $4 - excl list (optional); { local RD_OPTS; RD_OPTS="--create-full-path --exclude-device-files --exclude-fifos " RD_OPTS="$RD_OPTS --exclude-sockets --exclude-symbolic-links --no-compression" RD_OPTS="$RD_OPTS -v$((V_LEVEL * 3))" if [ -n "$3" -o -n "$4" ]; then [ -r "$3" ] || return 1 RD_OPTS="$RD_OPTS --include-globbing-filelist $3" [ -r "$4" ] || return 2 RD_OPTS="$RD_OPTS --exclude-globbing-filelist $4" fi dbg 3 "Backup command: \"$CMD_RDBCKP\" $RD_OPTS \"$1\" \"$2\"" "$CMD_RDBCKP" $RD_OPTS "$1" "$2" return $?; } #------------------------------------------------------------------------------- full_backup() # Full backup function. # Return values: # $? & 0x00000f - backup_tree for main tree code; # $? & 0x0000f0 - backup_tree for special files code; # $? & 0xffff00 - backup_special code. { local RET_CODE=0 local RT=0 dbg 3 "$LINENO ${FUNCNAME}(): Backup special files..." backup_special || { RET_CODE="$?"; err "Backup special return non null error code ($RET_CODE)!"; notify "$0 -> backup_special error code: $RET_CODE"; } if [ ! -d "$FN_DIR_SPECIAL" ]; then mkdir -p "$FN_DIR_SPECIAL" || return 2 fi if [ ! -d "$FN_DIR_TREE" ]; then mkdir -p "$FN_DIR_TREE" || return 3 fi backup_tree "$OPT_BTMP_DIR" "$FN_DIR_SPECIAL"; RT=$? RET_CODE=$(($RET_CODE << 4 | $RT)) rm -rf "$OPT_BTMP_DIR" dbg 3 "$LINENO ${FUNCNAME}(): Backup main tree..." backup_tree "$OPT_SRC_DIR" "$FN_DIR_TREE" "$OPT_INC_LIST" "$OPT_EXC_LIST" RT=$? RET_CODE=$(($RET_CODE << 4 | $RT)) return $RET_CODE; } #------------------------------------------------------------------------------- diff_backup() # Function for the differential backup making. # Return values: # $? & 0x000000f - del_old_diffs code; # $? & 0xffff000 - full_backup code. { local RET_CODE=0 local RT=0 del_old_diffs() { local CMD_REMOVE CMD_REMOVE="$CMD_RDBCKP --remove-older-than $OPT_REMOVE_PERIOD" dbg 2 "Deleting old differentials..." dbg 3 "$LINENO ${FUNCNAME}(): $CMD_REMOVE \"$FN_DIR_TREE\" && $CMD_REMOVE \"$FN_DIR_SPECIAL\"" $CMD_REMOVE "$FN_DIR_TREE" && $CMD_REMOVE "$FN_DIR_SPECIAL" return $?; } del_old_diffs RET_CODE=$? full_backup RT=$? RET_CODE=$(($RET_CODE << 4 | $RT)) return $RET_CODE; } #------------------------------------------------------------------------------- main() # $1 - config. { # Setting executables paths. get_execs CMD_DPKG CMD_DUMP CMD_VGBCK CMD_CRYPTSETUP CMD_RDBCKP CMD_NOTIFIER # Loading configuration file. [ -z "$1" ] && { err "Syntax: $0 <config>" && return 1; } load_config "$1" notify "Backup started..." FN_DIR_TREE="$OPT_BACKUP_DIR/$FN_DIR_TREE" FN_DIR_SPECIAL="$OPT_BACKUP_DIR/$FN_DIR_SPECIAL" FN_LUKS_TEMPL="$OPT_BTMP_DIR/$FN_LUKS_TEMPL" FN_LVM_TEMPL="$OPT_BTMP_DIR/$FN_LVM_TEMPL" # Selecting backup type and do work. if [ -e "$FN_DIR_TREE/$FN_FLAG" ]; then diff_backup else full_backup fi local RT=$(($? << 4)) notify "Backup ended with code $RT..." return $RT } #------------------------------------------------------------------------------- main; #------------------------------------------------------------------------------- # EOF