[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Несколько вопросов вразброс



Приложил скрипт, который у меня получился.
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


Reply to: