From 56b798afb54ca9bbdae40a04dd29e05261ccbc26 Mon Sep 17 00:00:00 2001
From: "Hahn Axel (hahn)" <axel.hahn@unibe.ch>
Date: Thu, 14 Mar 2024 13:18:28 +0100
Subject: [PATCH] reformat code; fix restore permissions

---
 localdump.sh | 1078 +++++++++++++++++++++++++-------------------------
 1 file changed, 544 insertions(+), 534 deletions(-)

diff --git a/localdump.sh b/localdump.sh
index 585c553..6230bef 100755
--- a/localdump.sh
+++ b/localdump.sh
@@ -16,9 +16,11 @@
 # 2022-02-18  .....  WIP: use class like functions
 # 2022-03-17  .....  WIP: add lines with prefix __DB__
 # 2022-11-04  ah     rename hooks
+# 2024-03-14  ah     v2.0: use profiles for local and remote databases
 # ======================================================================
 
 # --- variables:
+# ARCHIVE_BASEDIR     {string}  base directory for db archive (couchdb2 only)
 # BACKUP_BASEDIR      {string}  base directory for db dumps
 # BACKUP_DATE         {string}  string with current timestamp; will be part of filename for backups
 # BACKUP_KEEP_DAYS    {int}     count of days how long to keep db dumps below $BACKUP_BASEDIR
@@ -31,606 +33,614 @@
 # CONFIG VARS
 # ----------------------------------------------------------------------
 
-  . $(dirname $0)/vendor/ini.class.sh    || exit 1  
-  . $(dirname $0)/vendor/color.class.sh  || exit 1
+    . $(dirname $0)/vendor/ini.class.sh    || exit 1
+    . $(dirname $0)/vendor/color.class.sh  || exit 1
 
-  . $(dirname $0)/includes/jobhelper.sh  || exit 1
-  . $(dirname $0)/includes/inc_bash.sh   || exit 1
+    . $(dirname $0)/includes/jobhelper.sh  || exit 1
+    . $(dirname $0)/includes/inc_bash.sh   || exit 1
 
-  . $(dirname $0)/includes/dbdetect.class.sh || exit 1
+    . $(dirname $0)/includes/dbdetect.class.sh || exit 1
 
-  if [ ! -r "${JOBFILE}" ]; then
-    color.echo error "ERROR: missing config file ${JOBFILE}."
-    exit 1
-  fi
+    if [ ! -r "${JOBFILE}" ]; then
+        color.echo error "ERROR: missing config file ${JOBFILE}."
+        exit 1
+    fi
 
-  LOCALDUMP_LOADED=1
+    LOCALDUMP_LOADED=1
 
-  ARCHIVE_BASEDIR=
-  BACKUP_BASEDIR=
-  BACKUP_PLUGINDIR=
+    ARCHIVE_BASEDIR=
+    BACKUP_BASEDIR=
+    BACKUP_PLUGINDIR=
 
-  DBD_DEBUG=0
+    DBD_DEBUG=0
 
-  # Cleanup local dumps older N days
-  typeset -i BACKUP_KEEP_DAYS=0
+    # Cleanup local dumps older N days
+    typeset -i BACKUP_KEEP_DAYS=0
 
-  BACKUP_DATE=
+    BACKUP_DATE=
+    LASTINPUT=
 
 # ----------------------------------------------------------------------
 # FUNCTIONS 4 DB-WRAPPER
 # ----------------------------------------------------------------------
 
 
-  # helpfer function for SERVICENAME.backup
-  # it is called after the service specific dump was done.
-  # param  {string}  filename of created dump file
-  function db._compressDumpfile(){
-    local _outfile=$1
+    # helpfer function for SERVICENAME.backup
+    # it is called after the service specific dump was done.
+    # param  {string}  filename of created dump file
+    function db._compressDumpfile(){
+        local _outfile=$1
 
-    # $myrc is last returncode - set in fetchrc
-    if [ $myrc -eq 0 ]; then
-      echo -n "gzip $_outfile ... "
-      gzip -9 -f "${_outfile}"
-      fetchrc
-    else
-      color.echo error "ERROR occured while dumping - no gzip of $_outfile"
-    fi
-    # echo -n "__DB__$SERVICENAME INFO: backup to " 
-    # ls -l "$_outfile"* 2>&1
-    # echo
-  }
+        # $myrc is last returncode - set in fetchrc
+        if [ $myrc -eq 0 ]; then
+            echo -n "gzip $_outfile ... "
+            gzip -9 -f "${_outfile}"
+            fetchrc
+        else
+            color.echo error "ERROR occured while dumping - no gzip of $_outfile"
+        fi
+        # echo -n "__DB__$SERVICENAME INFO: backup to " 
+        # ls -l "$_outfile"* 2>&1
+        # echo
+    }
 
 # ----------------------------------------------------------------------
 # FUNCTIONS 4 BACKUP
 # ----------------------------------------------------------------------
 
 
-  # ------------------------------------------------------------
-  # cleanup a backup dir: remove old files and delete empty dirs
-  function cleanup_backup_target(){
-    if [ -d "${BACKUP_TARGETDIR}" ]; then
-      h3 "CLEANUP ${BACKUP_TARGETDIR} older $BACKUP_KEEP_DAYS days ..."
-
-      echo find "${BACKUP_TARGETDIR}" -mtime +$BACKUP_KEEP_DAYS -delete -print
-      color.preset cmd
-      find "${BACKUP_TARGETDIR}" -mtime +$BACKUP_KEEP_DAYS -delete -print
-      color.reset
-
-      if [ $(find "${BACKUP_TARGETDIR}" -type f | wc -l) -eq 0 ]; then
-        echo "INFO: the directory is empty - deleting it"
-        rm -rf "${BACKUP_TARGETDIR}"
-      fi
-    fi
-  }
-
-  # ------------------------------------------------------------
-  # compress a file
-  # shared function in localdump_*
-  # param  string  filename of uncompressed output file
-  function compress_file(){
-    echo -n compressing $1 ...
-    gzip -9 -f "${1}"
-    fetchrc
-  }
-
-  # ------------------------------------------------------------
-  # create a backup directory with name of service
-  # shared function in localdump_*
-  function create_targetdir(){
-    mkdir -p "${BACKUP_TARGETDIR}" 2>/dev/null
-    if [ ! -d "${BACKUP_TARGETDIR}" ]; then
-      color.echo "error" "FATAL ERROR: directory ${BACKUP_TARGETDIR} was not created"
-      exit 1
-    fi
-  }
-
-
-  # ------------------------------------------------------------
-  # generate a base filename for backup dump based on on db name
-  # ... and added timestamp
-  # param  string  name of database schema
-  # --> see listBackupedDBs() and guessDB() - these function must be able to split this
-  function get_outfile(){
-    echo $*__${BACKUP_DATE}
-  }
-
-  # ------------------------------------------------------------
-  # get name of a service script
-  # param  string  name of a service 
-  function get_service_script(){
-    local _service=$1
-    local _type; _type=$( dbdetect.getType "$_service" )
-    ls -1 ${BACKUP_PLUGINDIR}/${_type}.sh 2>/dev/null
-  }
-
-  # ------------------------------------------------------------
-  # get a list of existing database profiles
-  function get_database_profiles(){
-    for config in $(dbdetect.getConfigs); do
-        if dbdetect.exists "$config"; then
-            echo "$( dbdetect.getProfile $config )"
+    # ------------------------------------------------------------
+    # cleanup a backup dir: remove old files and delete empty dirs
+    function cleanup_backup_target(){
+        if [ -d "${BACKUP_TARGETDIR}" ]; then
+            h3 "CLEANUP ${BACKUP_TARGETDIR} older $BACKUP_KEEP_DAYS days ..."
+
+            echo find "${BACKUP_TARGETDIR}" -mtime +$BACKUP_KEEP_DAYS -delete -print
+            color.preset cmd
+            find "${BACKUP_TARGETDIR}" -mtime +$BACKUP_KEEP_DAYS -delete -print
+            color.reset
+
+            if [ $(find "${BACKUP_TARGETDIR}" -type f | wc -l) -eq 0 ]; then
+                echo "INFO: the directory is empty - deleting it"
+                rm -rf "${BACKUP_TARGETDIR}"
+            fi
         fi
-    done
-  }
-
-  # ------------------------------------------------------------
-  # show directory infos with count of files and used space
-  # show used space and count of files and dirs
-  function show_info_backup_target(){
-    if [ -d "${BACKUP_TARGETDIR}" ]; then
-      h3 "INFO about backup target ${BACKUP_TARGETDIR}"
-
-      echo -n "used space: "
-      du -hs "${BACKUP_TARGETDIR}"
-
-      echo -n "subdirs   : "
-      find "${BACKUP_TARGETDIR}" -type d | wc -l
-
-      echo -n "files     : "
-      find "${BACKUP_TARGETDIR}" -type f | wc -l
-
-      echo -n "free space: "
-      df -h "${BACKUP_TARGETDIR}" | tail -1 | awk '{ print $4 }'
-      echo
-    fi
-  }
-
-# ----------------------------------------------------------------------
-# FUNCTIONS 4 RESTORE
-# ----------------------------------------------------------------------
-
-  # ------------------------------------------------------------
-  # restore: show profiles from that exist backups
-  # global  string  BACKUP_BASEDIR  base directory of all backups
-  function listBackupedServices(){
-    (
-      find "${BACKUP_BASEDIR}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;
-      test -n "${ARCHIVE_BASEDIR}" && find "${ARCHIVE_BASEDIR}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;
-    ) | sort -u
-  }
-
-  # ------------------------------------------------------------
-  # restore: show databases or dumps of a given database that can be restored
-  # global  string  BACKUP_BASEDIR  base directory of all backups of selected dbprofile
-  # param  string  optional: DB-Name for file filter to select from existing dumps;
-  function listBackupedDBs(){
-    if [ -d "${BACKUP_TARGETDIR}" ]; then
-
-      if [ -z $1 ]; then
-        # list all databases
-        find "${BACKUP_TARGETDIR}" -mindepth 1 -maxdepth 1 -type f -exec basename {} \; \
-          | sed "s#__[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9].*##g" \
-          | sed "s#\..*##g" \
-          | sort -ud| sed "s#^\./##g"
-      else
-        # list dumps of a database
-        ls -ltr ${BACKUP_TARGETDIR}/${1}*gz | sed "s,${BACKUP_TARGETDIR}/,,g"
-      fi
+    }
 
-    else
-      color.echo error "ERROR: ${BACKUP_TARGETDIR} does not exist - here are no backups to restore."
-      echo
-      echo "You can try to restore dumps:"
-      echo "1) Restore dump files from a backup set"
-      echo "     $(dirname $0)/restore.sh $BACKUP_BASEDIR"
-      echo "2) Copy restored dumps into $BACKUP_TARGETDIR"
-      echo "3) Start database restore again"
-      echo "     $(dirname $0)/localdump.sh restore [profile]"
-      echo
- 
-      exit 1
-    fi
-  }
-
-
-  # ------------------------------------------------------------
-  # guess name of the database file
-  # param  string  filename of db dump; can be full path or not
-  function guessDB(){
-    dumpfile=$1
-
-    # the metafile is written in sqlite backup to store full path
-    metafile=${BACKUP_TARGETDIR}/${dumpfile}.meta
-    if [ -f $metafile ]; then
-      grep "^/" "$metafile" || grep "^  File: " "$metafile" | cut -c 9-
-    else
-      sBasename=$(basename $1)
-      sDb=$(echo ${sBasename} | sed "s#__[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9].*##g")
-      if [ -z $sDb ]; then
-        color.echo error "ERROR: db name was not detected from file $1"
-        exit 1
-      fi
-      echo $sDb
-    fi
-  }
-
-
-  # read .meta file (that contains output of stats) and restore last owner and file permissions
-  # param  string  filename of db dump
-  # param  string  restored database file
-  function restorePermissions(){
-    local sMyMeta="${1}.meta"
-    local sTargetfile="$2"
-    if [ -f "${sMyMeta}" ]; then
-
-      # Access: (0674/-rw-rwxr--)  Uid: ( 1000/    axel)   Gid: ( 1000/    axel)
-      #          ^                                 ^                       ^
-      #          _sPerm                            _sUser                  _sGroup
-      local _sPerm=$(  grep "^Access: (" "${sMyMeta}" | cut -f 2 -d '(' | cut -f 1 -d '/')
-      if [ -n "$_sPerm" ]; then
-        local _sUser=$(  grep "^Access: (" "${sMyMeta}" | cut -f 3 -d '/' | cut -f 1 -d ')' | tr -d ' ')
-        local _sGroup=$( grep "^Access: (" "${sMyMeta}" | cut -f 4 -d '/' | cut -f 1 -d ')' | tr -d ' ')
-
-        echo -n "Restoring file owner $_sUser:$_sGroup and permissions $_sPerm ... "
-        chown "$_sUser:$_sGroup" "${sTargetfile}" && chmod "$_sPerm" "${sTargetfile}"
+    # ------------------------------------------------------------
+    # compress a file
+    # shared function in localdump_*
+    # param  string  filename of uncompressed output file
+    function compress_file(){
+        echo -n compressing $1 ...
+        gzip -9 -f "${1}"
         fetchrc
-      fi
-    fi    
-  }
-
-  # ------------------------------------------------------------
-  # show help
-  # ------------------------------------------------------------
-  function showhelp(){
-    local _self
-    _self=$( basename "$0" )
-    echo
-    echo "LOCALDUMP detects existing local databases and dumps them locally."
-    echo "It is included in the backup.sh to dump all before a file backup will store them."
-    echo
-    echo "It can be started seperately for manual database backups or for restore"
-    echo
-    echo "SYNTAX:"
-    echo "  $_self [OPTIONS] <operation> <profile [more_profiles]>"
-    echo
-    echo "OPTIONS:"
-    echo "  -h|--help   show this help"
-    echo
-    echo "PARAMETERS:"
-    echo "  operation     - one of check|backup|restore; optional parameter"
-    echo "                    backup   dump all databases/ schemes of a given service"
-    echo "                    check    show info only if the service is available"
-    echo "                    restore  import a dump into same or new database"
-    echo "                             Without a filename it starts an interactive mode"
-    echo "  profile     - name of database profiles"
-    echo "                You get a list of all available services without parameter"
-    echo "                Use ALL for bulk command"
-    echo "  file        - filename of db dump to restore to origin database scheme"
-    echo
-    echo "EXAMPLES:"
-    echo "  $_self backup"
-    echo "  $_self backup ALL"
-    echo "               Backup all databases of all found services"
-    echo
-    echo "  $_self backup mysql"
-    echo "               Backup all Mysql databases."
-    echo
-    echo "  $_self restore"
-    echo "               Start interactive restore of a database of any service."
-    echo
-    echo "  $_self restore sqlite"
-    echo "               Start interactive restore of an sqlite database."
-    echo
-    echo "  $_self restore <file-to-restore> [<database-name>]"
-    echo "               Restore a given dump file to the origin database scheme or"
-    echo "               to a new/ other database with the given name."
-  }
+    }
 
-
-# ----------------------------------------------------------------------
-# INIT
-# ----------------------------------------------------------------------
-
-  while [[ "$#" -gt 0 ]]; do case $1 in
-      -h|--help)      showhelp; exit 0;;
-      *) if grep "^-" <<< "$1" >/dev/null ; then
-          echo; echo "ERROR: Unknown parameter: $1"; echo; showhelp; exit 2
+    # ------------------------------------------------------------
+    # create a backup directory with name of service
+    # shared function in localdump_*
+    function create_targetdir(){
+        mkdir -p "${BACKUP_TARGETDIR}" 2>/dev/null
+        if [ ! -d "${BACKUP_TARGETDIR}" ]; then
+            color.echo "error" "FATAL ERROR: directory ${BACKUP_TARGETDIR} was not created"
+            exit 1
         fi
-        break;
-        ;;
-  esac; done
-
-  mode=""
-  case "$1" in
-    backup|check|restore|shell)
-      mode=$1
-      shift 1
-      ;;
-  esac
-
-  if [ -z "$mode" ]; then
-    color.echo error "ERROR: missing parameter for operation."
-    echo
-    showhelp
-    echo
-    echo "Hint: On this machine working profiles:"
-    get_database_profiles | nl
-    echo
-    exit 1
-  fi
-
-
-  # ----- init vars
-  BACKUP_BASEDIR=$(_j_getvar "${JOBFILE}" "dir-localdumps")
-
-  # check
-  if [ -z "$BACKUP_BASEDIR" ]; then
-    color.echo error "ERROR: missing config for backup target."
-    echo There must be an entry dir-localdumps in ${JOBFILE}
-    exit 1
-  fi
-  ARCHIVE_BASEDIR=$(_j_getvar "${JOBFILE}" dir-dbarchive)
-
-  BACKUP_PLUGINDIR=$(dirname $0)/plugins/localdump
-  DBD_BASEDIR=$BACKUP_PLUGINDIR/profiles
-
-  BACKUP_KEEP_DAYS=$(_j_getvar ${JOBFILE} "keep-days")
-
-  if [ $BACKUP_KEEP_DAYS -eq 0 ]; then
-    BACKUP_KEEP_DAYS=7
-  fi
-  BACKUP_DATE=$(/bin/date +%Y%m%d-%H%M)
-
-  # ----- checks
-
-  # . /usr/local/bin/inc_cronfunctions.sh
-  j_requireUser "root"
-
-  h1 $(date) IML BACKUP :: LOCALDUMP :: $*
+    }
 
 
-  export SERVICENAME=$1
-  # BACKUP_TARGETDIR=${BACKUP_BASEDIR}/${SERVICENAME}
-  # BACKUP_SCRIPT=$( get_service_script ${SERVICENAME} )
+    # ------------------------------------------------------------
+    # generate a base filename for backup dump based on on db name
+    # ... and added timestamp
+    # param  string  name of database schema
+    # --> see listBackupedDBs() and guessDB() - these function must be able to split this
+    function get_outfile(){
+        echo $*__${BACKUP_DATE}
+    }
 
-  case "$mode" in
     # ------------------------------------------------------------
-    check)
-      DBD_DEBUG=1
-      for PROFILENAME in $(dbdetect.getConfigs)
-      do
-        echo "----- $PROFILENAME"
-        dbdetect.exists "${PROFILENAME}"
-        echo
-      done
-      # . $BACKUP_SCRIPT $mode
-      ;;
+    # get name of a service script
+    # param  string  name of a service 
+    function get_service_script(){
+        local _service=$1
+        local _type; _type=$( dbdetect.getType "$_service" )
+        ls -1 ${BACKUP_PLUGINDIR}/${_type}.sh 2>/dev/null
+    }
+
     # ------------------------------------------------------------
-    backup)
-      if [ "$1" = "ALL" ] || [ -z "$1" ]; then
+    # get a list of existing database profiles
+    function get_database_profiles(){
+        for config in $(dbdetect.getConfigs); do
+            if dbdetect.exists "$config"; then
+                echo "$( dbdetect.getProfile $config )"
+            fi
+        done
+    }
 
-        profiles2run=$(get_database_profiles)
-        echo AUTO: calling local backup scripts for all active profiles
-        echo "$profiles2run" | nl
+    # ------------------------------------------------------------
+    # show directory infos with count of files and used space
+    # show used space and count of files and dirs
+    function show_info_backup_target(){
+      if [ -d "${BACKUP_TARGETDIR}" ]; then
+        h3 "INFO about backup target ${BACKUP_TARGETDIR}"
+  
+        echo -n "used space: "
+        du -hs "${BACKUP_TARGETDIR}"
+  
+        echo -n "subdirs   : "
+        find "${BACKUP_TARGETDIR}" -type d | wc -l
+  
+        echo -n "files     : "
+        find "${BACKUP_TARGETDIR}" -type f | wc -l
+  
+        echo -n "free space: "
+        df -h "${BACKUP_TARGETDIR}" | tail -1 | awk '{ print $4 }'
         echo
-      else
-        profiles2run=$*
       fi
+    }
 
-      # ----- GO
-      # PROFILENAME    mysql_localhost_13306
-      # SERVICENAME    mysql
-      #
-      for PROFILENAME in $profiles2run
-      do
-
-        
-        if dbdetect.setProfile "${PROFILENAME}"; then
-          h2 "START PROFILE [${PROFILENAME}]"
-
-          SERVICENAME=$( dbdetect.getType "$PROFILENAME" )
-          BACKUP_PARAMS=$( dbdetect.getParams )
-
-          BACKUP_TARGETDIR=${BACKUP_BASEDIR}/${PROFILENAME}
-          ARCHIVE_DIR=${ARCHIVE_BASEDIR}/${PROFILENAME}
-          BACKUP_SCRIPT=$( get_service_script ${SERVICENAME} )
-  
-
-          # ------ set env
-          # echo "BACKUP_PARAMS = $BACKUP_PARAMS"
-          # dbdetect.setenv
-          eval $( dbdetect.setenv )
-
-          _j_runHooks "200-before-db-service"
-
-          h3 "BACKUP [${PROFILENAME}] -> ${SERVICENAME}"
-          . $BACKUP_SCRIPT $mode
-
-          test $rc -gt 0 && j_notify "db ${SERVICENAME}" "$BACKUP_SCRIPT $mode was finished with rc=$rc" $rc
-          _j_runHooks "230-after-db-service" "$rc"
-
-          # ------ unset env
-          eval $( dbdetect.unssetenv )
-
-          # ----- post jobs: cleanup
-          cleanup_backup_target
-          show_info_backup_target
+# ----------------------------------------------------------------------
+# FUNCTIONS 4 RESTORE
+# ----------------------------------------------------------------------
 
+    # ------------------------------------------------------------
+    # restore: show profiles from that exist backups
+    # global  string  BACKUP_BASEDIR  base directory of all backups
+    function listBackupedServices(){
+        (
+            find "${BACKUP_BASEDIR}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;
+            test -n "${ARCHIVE_BASEDIR}" && find "${ARCHIVE_BASEDIR}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;
+        ) | sort -u
+    }
 
+    # ------------------------------------------------------------
+    # restore: show databases or dumps of a given database that can be restored
+    # global  string  BACKUP_BASEDIR  base directory of all backups of selected dbprofile
+    # param  string  optional: DB-Name for file filter to select from existing dumps;
+    function listBackupedDBs(){
+        if [ -d "${BACKUP_TARGETDIR}" ]; then
+            if [ -z $1 ]; then
+                # list all databases
+                find "${BACKUP_TARGETDIR}" -mindepth 1 -maxdepth 1 -type f -exec basename {} \; \
+                    | sed "s#__[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9].*##g" \
+                    | sed "s#\..*##g" \
+                    | sort -ud| sed "s#^\./##g"
+            else
+                # list dumps of a database
+                ls -ltr ${BACKUP_TARGETDIR}/${1}*gz | sed "s,${BACKUP_TARGETDIR}/,,g"
+            fi
         else
-
-          echo "SKIP: profile '$PROFILENAME' "
-
-          # see why it is not active
-          DBD_DEBUG=1; dbdetect.setProfile "${PROFILENAME}"; echo; DBD_DEBUG=0
-
+            color.echo error "ERROR: ${BACKUP_TARGETDIR} does not exist - here are no backups to restore."
+            echo
+            echo "You can try to restore dumps:"
+            echo "1) Restore dump files from a backup set"
+            echo "     $(dirname $0)/restore.sh $BACKUP_BASEDIR"
+            echo "2) Copy restored dumps into $BACKUP_TARGETDIR"
+            echo "3) Start database restore again"
+            echo "     $(dirname $0)/localdump.sh restore [profile]"
+            echo
+            exit 1
         fi
+    }
 
-        # just to have it in the output
-        dbdetect.validate
-
-      done
-      ;;
 
     # ------------------------------------------------------------
-    restore)
-
-      h1 "RESTORE DATABASE"
-
-      if ! listBackupedServices | grep -q . ; then
-        color.echo error "ERROR: No database dump was found in [${BACKUP_BASEDIR}] nor [${ARCHIVE_BASEDIR}]."
-        exit 1
-      fi
-
-      if [ -z $1 ] || [ ! -f "$1" ]; then
-
-        parService="$1"
-
-        # ----- interactive selections
-
-        h2 "Select profile that has a dump"
-
-        if [ -z "${parService}" ]; then
-          if [ "$( listBackupedServices | wc -l )" -eq 1 ]; then
-            parService="$( listBackupedServices )"
-            echo "Selecting the only existing profile: $parService"
-          fi
-        fi
-
-        if [ -z "${parService}" ]; then
-          listBackupedServices
-          color.print input "Restore for profile name >"
-          read -r parService
-          test -z "$parService" && exit 1
+    # guess name of the database file
+    # param  string  filename of db dump; can be full path or not
+    function guessDB(){
+        dumpfile=$1
+  
+        # the metafile is written in sqlite backup to store full path
+        metafile=${BACKUP_TARGETDIR}/${dumpfile}.meta
+        if [ -f $metafile ]; then
+            grep "^/" "$metafile" || grep "^  File: " "$metafile" | cut -c 9-
         else
-          echo "Taken from command line: $parService"
+            sBasename=$(basename $1)
+            sDb=$(echo ${sBasename} | sed "s#__[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9].*##g" | sed "s#\.couchdbdump\.gz$##g" )
+            #                                   ^                                                                   ^
+            #        timestamp in backup file __/                               for couchdb2 restore from archive __/
+            if [ -z $sDb ]; then
+                color.echo error "ERROR: db name was not detected from file $1"
+                exit 1
+            fi
+            echo $sDb
         fi
+    }
 
-        # ----- check if profile exists
-        if ! dbdetect.setProfile "${parService}"; then
-          color.echo error "ERROR: profile [${parService}] is not known here (or database service is stopped)."
-          echo
-          echo "Existing database profiles:"
-          get_database_profiles | nl
-          echo
+    # ------------------------------------------------------------
+    # show a selection + a prompt and read the input
+    # - If the selection is just 1 line it will be returned
+    # - If the user presses just return the script will exit
+    # param  string  selection of items to select from
+    # param  string  prompt to show
+    function showSelectAndInput(){
+      local _selection="$1"
+      local _prompt="$2"
+  
+      local _lines
+      typeset -i _lines; _lines=$( grep -c "." <<< "$_selection" )
+  
+      if [ $_lines -eq "1" ]; then
+        echo "INFO: No interaction on a single choice. Using '$_selection'"
+        LASTINPUT="$_selection"
+        return 0
+      else
+  
+        echo "$_selection"
+        color.print input "${_prompt} >"
+        read -r LASTINPUT
+        if [ -z "$LASTINPUT" ]; then
+          echo "No input given. Aborting."
           exit 1
         fi
+      fi
+    }
 
-        # ----- check if dump exists in archive and in backup
-        if [ -d "${BACKUP_BASEDIR}/${parService}" ] && [ -d "${ARCHIVE_BASEDIR}/${parService}" ]; then
-          echo
-          echo "Backup   ${BACKUP_BASEDIR}"
-          echo "Archive  ${ARCHIVE_BASEDIR}"
-          color.print input "Select a source directory >"
-          read -r backupsource
-          test -z "$backupsource" && exit 1
-          BACKUP_BASEDIR="${backupsource}"
-        else
-          # just one test needed because BACKUP_BASEDIR is BACKUP_BASEDIR
-          test -d "${ARCHIVE_BASEDIR}/${parService}" && BACKUP_BASEDIR="${ARCHIVE_BASEDIR}"
+    # ------------------------------------------------------------
+    # read .meta file (that contains output of stats) and restore last owner and file permissions
+    # param  string  filename of db dump
+    # param  string  restored database file
+    function restorePermissions(){
+        local sMyMeta="${1}.meta"
+        local sTargetfile="$2"
+        if [ -f "${sMyMeta}" ]; then
+
+            # Access: (0674/-rw-rwxr--)  Uid: ( 1000/    axel)   Gid: ( 1000/    axel)
+            #          ^                                 ^                       ^
+            #          _sPerm                            _sUser                  _sGroup
+            local _sPerm=$(  grep "^Access: (" "${sMyMeta}" | cut -f 2 -d '(' | cut -f 1 -d '/')
+            if [ -n "$_sPerm" ]; then
+
+                local _sUser=$(  grep "^Access: (" "${sMyMeta}" | cut -f 3 -d '(' | cut -f 1 -d '/' | tr -d ' ')
+                local _sGroup=$( grep "^Access: (" "${sMyMeta}" | cut -f 4 -d '(' | cut -f 1 -d '/' | tr -d ' ')
+
+                echo -n "Restoring file owner $_sUser:$_sGroup and permissions $_sPerm ... "
+                chown "$_sUser:$_sGroup" "${sTargetfile}" && chmod "$_sPerm" "${sTargetfile}"
+                fetchrc
+            fi
         fi
+    }
 
-        # ----- check if target dir with profile exists
-        if [ ! -d "${BACKUP_BASEDIR}/${parService}" ]; then
-          color.echo error "ERROR: Directory does not exist '${BACKUP_BASEDIR}/${parService}'."
-          exit 1
-        fi
+    # ------------------------------------------------------------
+    # show help
+    # ------------------------------------------------------------
+    function showhelp(){
+        local _self
+        _self=$( basename "$0" )
+        cat <<EOH
+
+LOCALDUMP detects existing local databases and dumps them locally.
+It is included in the backup.sh to dump all before a file backup will store 
+them. It can be started seperately for manual database backups or for restore.
+
+SYNTAX:
+    $_self [OPTIONS] <operation> <profile [more_profiles]>
+
+OPTIONS:
+    -h|--help   show this help
+
+PARAMETERS:"
+    operation   - one of check|backup|restore; optional parameter
+                      backup   dump all databases/ schemes of a given service
+                      check    show info only if the service is available
+                      restore  import a dump into same or new database
+                               Without a filename it starts an interactive mode
+    profile     - name of database profiles
+                  You get a list of all available services without parameter
+                  Use ALL for bulk command
+    file        - filename of db dump to restore to origin database scheme
+
+EXAMPLES:
+    $_self backup
+    $_self backup ALL
+                 Backup all databases of all found services
+    $_self backup mysql
+                 Backup all Mysql databases.
+
+    $_self restore
+                 Start interactive restore of a database of any service.
+    $_self restore sqlite
+                 Start interactive restore of an sqlite database.
+    $_self restore <file-to-restore> [<database-name>]
+                 Restore a given dump file to the origin database scheme or
+                 to a new/ other database with the given name.
+
+EOH
+    }
 
-        BACKUP_TARGETDIR="${BACKUP_BASEDIR}/${parService}"
 
-        h2 "Select database"
-        listBackupedDBs
-        color.print input "name of db to restore >"
-        read -r fileprefix
-        test -z "$fileprefix" && exit 1
-        echo
+# ----------------------------------------------------------------------
+# INIT
+# ----------------------------------------------------------------------
 
-        h2 "Select a specific dump for that database"
-        listBackupedDBs $fileprefix
-        color.print input "backupset to import >"
-        read -r dbfile
-        test -z "$dbfile" && exit 1
+    while [[ "$#" -gt 0 ]]; do case $1 in
+        -h|--help)      showhelp; exit 0;;
+        *) if grep "^-" <<< "$1" >/dev/null ; then
+                echo; echo "ERROR: Unknown parameter: $1"; echo; showhelp; exit 2
+            fi
+            break;
+            ;;
+    esac; done
+
+    mode=""
+    case "$1" in
+        backup|check|restore|shell)
+            mode=$1
+            shift 1
+            ;;
+    esac
+
+    if [ -z "$mode" ]; then
+        color.echo error "ERROR: missing parameter for operation."
         echo
-
-        sTargetDb=$(guessDB ${dbfile})
-        color.print input "New database name [$sTargetDb] >"
-        read -r sTargetDb
-        if [ -z $sTargetDb ]; then
-          sTargetDb=$(guessDB ${dbfile})
-        fi
+        showhelp
+        echo
+        echo "Hint: On this machine working profiles:"
+        get_database_profiles | nl
         echo
+        exit 1
+    fi
 
-        sDumpfile="${BACKUP_TARGETDIR}/${dbfile}"
-      else
-        sDumpfile=$1
-        sTargetDb=$2
-      fi
-      shift 2
+    # ----- init vars
+    BACKUP_BASEDIR=$(_j_getvar "${JOBFILE}" "dir-localdumps")
 
-      # ----- start restore
+    # check
+    if [ -z "$BACKUP_BASEDIR" ]; then
+        color.echo error "ERROR: missing config for backup target."
+        echo There must be an entry dir-localdumps in ${JOBFILE}
+        exit 1
+    fi
+    ARCHIVE_BASEDIR=$(_j_getvar "${JOBFILE}" dir-dbarchive)
 
-      if [ ! -f "${sDumpfile}" ]; then
-        color.echo error "ERROR: [${sDumpfile}] is not a file"
-        rc=$rc+1
-      else
+    BACKUP_PLUGINDIR=$(dirname $0)/plugins/localdump
+    DBD_BASEDIR=$BACKUP_PLUGINDIR/profiles
 
-          PROFILENAME="${sDumpfile//${BACKUP_BASEDIR}/}"
-          PROFILENAME="$( echo $PROFILENAME | sed "s,^/*,," | cut -f 1 -d '/')"
+    BACKUP_KEEP_DAYS=$(_j_getvar ${JOBFILE} "keep-days")
 
-          if dbdetect.setProfile "${PROFILENAME}"; then
+    if [ $BACKUP_KEEP_DAYS -eq 0 ]; then
+        BACKUP_KEEP_DAYS=7
+    fi
+    BACKUP_DATE=$(/bin/date +%Y%m%d-%H%M)
+
+    # ----- checks
 
-            SERVICENAME=$( dbdetect.getType "$PROFILENAME" )
+    # . /usr/local/bin/inc_cronfunctions.sh
+    j_requireUser "root"
 
-            BACKUP_TARGETDIR=${BACKUP_BASEDIR}/${PROFILENAME}
-            BACKUP_SCRIPT=$( get_service_script ${SERVICENAME} )
-            ARCHIVE_DIR=${ARCHIVE_BASEDIR}/${PROFILENAME}
+    h1 $(date) IML BACKUP :: LOCALDUMP :: $*
 
-            BACKUP_PARAMS=$( dbdetect.getParams )
-            eval $( dbdetect.setenv )
-            . $BACKUP_SCRIPT $mode "${sDumpfile}" "${sTargetDb}"
 
-            if [ $? -ne 0 -o $rc -ne 0 ]; then
-              color.echo error "ERROR: $mode failed. See ouput above. :-/"
+    export SERVICENAME=$1
+    # BACKUP_TARGETDIR=${BACKUP_BASEDIR}/${SERVICENAME}
+    # BACKUP_SCRIPT=$( get_service_script ${SERVICENAME} )
+  
+    case "$mode" in
+        # ------------------------------------------------------------
+        check)
+            DBD_DEBUG=1
+            for PROFILENAME in $(dbdetect.getConfigs)
+            do
+                echo "----- $PROFILENAME"
+                dbdetect.exists "${PROFILENAME}"
+                echo
+            done
+            # . $BACKUP_SCRIPT $mode
+            ;;
+      # ------------------------------------------------------------
+      backup)
+        if [ "$1" = "ALL" ] || [ -z "$1" ]; then
+  
+            profiles2run=$(get_database_profiles)
+            echo AUTO: calling local backup scripts for all active profiles
+            echo "$profiles2run" | nl
+            echo
+        else
+            profiles2run=$*
+        fi
+  
+        # ----- GO
+        # PROFILENAME    mysql_localhost_13306
+        # SERVICENAME    mysql
+        #
+        for PROFILENAME in $profiles2run
+        do
+  
+          
+            if dbdetect.setProfile "${PROFILENAME}"; then
+                h2 "START PROFILE [${PROFILENAME}]"
+  
+                SERVICENAME=$( dbdetect.getType "$PROFILENAME" )
+                BACKUP_PARAMS=$( dbdetect.getParams )
+  
+                BACKUP_TARGETDIR=${BACKUP_BASEDIR}/${PROFILENAME}
+                ARCHIVE_DIR=${ARCHIVE_BASEDIR}/${PROFILENAME}
+                BACKUP_SCRIPT=$( get_service_script ${SERVICENAME} )
+    
+  
+                # ------ set env
+                # echo "BACKUP_PARAMS = $BACKUP_PARAMS"
+                # dbdetect.setenv
+                eval $( dbdetect.setenv )
+  
+                _j_runHooks "200-before-db-service"
+  
+                h3 "BACKUP [${PROFILENAME}] -> ${SERVICENAME}"
+                . $BACKUP_SCRIPT $mode
+  
+                test $rc -gt 0 && j_notify "db ${SERVICENAME}" "$BACKUP_SCRIPT $mode was finished with rc=$rc" $rc
+                _j_runHooks "230-after-db-service" "$rc"
+  
+                # ------ unset env
+                eval $( dbdetect.unssetenv )
+  
+                # ----- post jobs: cleanup
+                cleanup_backup_target
+                show_info_backup_target
+    
             else
-              color.echo ok "OK, $mode was successful."
+  
+                echo "SKIP: profile '$PROFILENAME' "
+  
+                # see why it is not active
+                DBD_DEBUG=1; dbdetect.setProfile "${PROFILENAME}"; echo; DBD_DEBUG=0
+  
             fi
+  
+            # just to have it in the output
+            dbdetect.validate
+  
+        done
+        ;;
+  
+      # ------------------------------------------------------------
+      restore)
+  
+        h1 "RESTORE DATABASE"
+  
+        if ! listBackupedServices | grep -q . ; then
+            color.echo error "ERROR: No database dump was found in [${BACKUP_BASEDIR}] nor [${ARCHIVE_BASEDIR}]."
+            exit 1
+        fi
+  
+        if [ -z $1 ] || [ ! -f "$1" ]; then
+  
+            parService="$1"
+  
+          # ----- interactive selections
+  
+            h2 "Select profile that has a dump"
+  
+            if [ -z "${parService}" ]; then
+                showSelectAndInput "$( listBackupedServices )" "Restore for profile name"
+                parService="$LASTINPUT"
+            else
+                echo "Taken from command line: $parService"
+            fi
+  
+            # ----- check if profile exists
+            if ! dbdetect.setProfile "${parService}"; then
+                color.echo error "ERROR: profile [${parService}] is not known here (or database service is stopped)."
+                echo
+                exit 1
+            fi
+  
+            # ----- check if dump exists in archive and in backup
+            if [ -d "${BACKUP_BASEDIR}/${parService}" ] && [ -d "${ARCHIVE_BASEDIR}/${parService}" ]; then
+                echo
+                showSelectAndInput "$(echo "${BACKUP_BASEDIR}"; echo "${ARCHIVE_BASEDIR}")" "Select a source directory"
+                BACKUP_BASEDIR="$LASTINPUT"
+            else
+                # just one test needed because BACKUP_BASEDIR is BACKUP_BASEDIR
+                test -d "${ARCHIVE_BASEDIR}/${parService}" && BACKUP_BASEDIR="${ARCHIVE_BASEDIR}"
+            fi
+  
+            # ----- check if target dir with profile exists
+            if [ ! -d "${BACKUP_BASEDIR}/${parService}" ]; then
+                color.echo error "ERROR: Directory does not exist '${BACKUP_BASEDIR}/${parService}'."
+                exit 1
+            fi
+  
+            BACKUP_TARGETDIR="${BACKUP_BASEDIR}/${parService}"
+  
+            h2 "Select a database schema"
+            showSelectAndInput "$(listBackupedDBs)" "Name of database to restore"
+            fileprefix="$LASTINPUT"
+            echo
+  
+            h2 "Select a specific dump for that database"
+            showSelectAndInput "$(listBackupedDBs $fileprefix)" "Backupset to import"
+            dbfile="$LASTINPUT"
+            echo
+  
+            sTargetDb=$(guessDB ${dbfile})
+            color.print input "New database name [$sTargetDb] >"
+            read -r sTargetDb
+            if [ -z $sTargetDb ]; then
+                sTargetDb=$(guessDB ${dbfile})
+            fi
+            echo
+  
+            sDumpfile="${BACKUP_TARGETDIR}/${dbfile}"
+        else
+            sDumpfile=$1
+            sTargetDb=$2
+        fi
+        shift 2
+  
+        # ----- start restore
+  
+        if [ ! -f "${sDumpfile}" ]; then
+            color.echo error "ERROR: [${sDumpfile}] is not a file"
+            rc=$rc+1
+        else
+  
+            PROFILENAME="${sDumpfile//${BACKUP_BASEDIR}/}"
+            PROFILENAME="$( echo $PROFILENAME | sed "s,^/*,," | cut -f 1 -d '/')"
+  
+            if dbdetect.setProfile "${PROFILENAME}"; then
+  
+                SERVICENAME=$( dbdetect.getType "$PROFILENAME" )
 
-            # ------ unset env
-            eval $( dbdetect.unssetenv )
-          else
-            color.echo error "ERROR: Profile $PROFILENAME was detected but its database service is not available."
-          fi
-
-      fi
-      ;;
-    # ------------------------------------------------------------
-    # shell)
-
-    #   export BACKUP_TARGETDIR
-    #   . $BACKUP_SCRIPT
-    #   ( 
-    #     mycmd=
-    #     echo
-    #     echo "Starting interactive shell..."
-    #     echo
-    #     echo "STATUS: STILL ALPHA as long existing db plugins are not rewritten."
-    #     echo
-    #     echo "INFO: Try ${SERVICENAME}.help to see database specific commands."
-    #     echo "INFO: Type exit and return to leave the shell."
-    #     echo
-    #     while [ ! "$mycmd" = "exit" ]; do
-    #       echo -n "[${SERVICENAME}]"
-    #       color.print input " $( pwd )"
-    #       echo -n " % "
-    #       read -r mycmd
-    #       if [ ! "$mycmd" = "exit" ];then
-    #         color.preset cmd
-    #         eval $mycmd
-    #         color.reset
-    #       fi
-    #     done
-    #   )
-    #   ;;
+                BACKUP_TARGETDIR=${BACKUP_BASEDIR}/${PROFILENAME}
+                BACKUP_SCRIPT=$( get_service_script ${SERVICENAME} )
+                ARCHIVE_DIR=${ARCHIVE_BASEDIR}/${PROFILENAME}
+  
+                BACKUP_PARAMS=$( dbdetect.getParams )
+                eval $( dbdetect.setenv )
+                . $BACKUP_SCRIPT $mode "${sDumpfile}" "${sTargetDb}"
+  
+                if [ $? -ne 0 -o $rc -ne 0 ]; then
+                    color.echo error "ERROR: $mode failed. See ouput above. :-/"
+                else
+                    color.echo ok "OK, $mode was successful."
+                fi
+  
+                # ------ unset env
+                eval $( dbdetect.unssetenv )
+            else
+                color.echo error "ERROR: Profile $PROFILENAME was detected but its database service is not available."
+            fi
+  
+        fi
+        ;;
+        # ------------------------------------------------------------
+        # shell)
+    
+        #   export BACKUP_TARGETDIR
+        #   . $BACKUP_SCRIPT
+        #   ( 
+        #     mycmd=
+        #     echo
+        #     echo "Starting interactive shell..."
+        #     echo
+        #     echo "STATUS: STILL ALPHA as long existing db plugins are not rewritten."
+        #     echo
+        #     echo "INFO: Try ${SERVICENAME}.help to see database specific commands."
+        #     echo "INFO: Type exit and return to leave the shell."
+        #     echo
+        #     while [ ! "$mycmd" = "exit" ]; do
+        #       echo -n "[${SERVICENAME}]"
+        #       color.print input " $( pwd )"
+        #       echo -n " % "
+        #       read -r mycmd
+        #       if [ ! "$mycmd" = "exit" ];then
+        #         color.preset cmd
+        #         eval $mycmd
+        #         color.reset
+        #       fi
+        #     done
+        #   )
+        #   ;;
+        
+        # ----- start restore
+        *)
+            color.echo error "ERROR: unknown command [$mode]"
+            ;;
+    esac
+  
+    echo _______________________________________________________________________________
+    echo STATUS $0 exit with final returncode rc=$rc
+    exit $rc
     
-    # ----- start restore
-    *)
-      color.echo error "ERROR: unknown command [$mode]"
-      ;;
-  esac
-
-  echo _______________________________________________________________________________
-  echo STATUS $0 exit with final returncode rc=$rc
-  exit $rc
-
 # ----------------------------------------------------------------------
+  
\ No newline at end of file
-- 
GitLab