From 1008ddf928b93fd1a853ab23b42286b4fa139f79 Mon Sep 17 00:00:00 2001 From: "Hahn Axel (hahn)" <axel.hahn@unibe.ch> Date: Tue, 20 Feb 2024 17:20:56 +0100 Subject: [PATCH] Sqlite Handle profiles; restore into existing db file; restore file owner and perms --- localdump.sh | 168 ++++++++++++++++++++++++------------ plugins/localdump/sqlite.sh | 83 +++++++----------- 2 files changed, 145 insertions(+), 106 deletions(-) diff --git a/localdump.sh b/localdump.sh index f5e72fb..09691c3 100755 --- a/localdump.sh +++ b/localdump.sh @@ -66,22 +66,32 @@ # ---------- GETTER +# write debug output if DBD_DEBUG is enabled +# param string text to show function dbdetect._wd(){ test "$DBD_DEBUG" -eq "1" && echo "DEBUG: $*" >&2 } +# get a list of all config files +# param string full path of ini file function dbdetect.getConfigs(){ find ${DBD_BASEDIR} -type f -name "*.ini" } +# get type from given ini file +# param string full path of ini file function dbdetect.getType(){ basename "$1" | cut -d "_" -f 1 | sed "s,\.ini$,," } + +# get profile from given ini file +# param string full path of ini file function dbdetect.getProfile(){ basename "$1" | cut -d "_" -f 1- | sed "s,\.ini$,," } - +# check if the requirements for a database match +# param string full path of ini file to check function dbdetect.exists(){ local _config="$1" @@ -93,14 +103,17 @@ function dbdetect.exists(){ ini.set "$_config" "detect" # --- check tcp - local tcpport; tcpport=$( ini.value "tcp" ) - local tcptarget; tcptarget=$( ini.value "tcp-target" ) - if { ! >/dev/tcp/$tcptarget/$tcpport; } > /dev/null 2>&1; then - # echo "No port tcp $tcpport available" - dbdetect._wd "... No port tcp $tcpport available on $tcptarget" - return 1 + local tcpport; tcpport=$( ini.value "tcp" ) + if [ -n "$tcpport" ]; then + local tcptarget; tcptarget=$( ini.value "tcp-target" ) + tcptarget=${tcptarget:-localhost} + if { ! >/dev/tcp/$tcptarget/$tcpport; } > /dev/null 2>&1; then + # echo "No port tcp $tcpport available" + dbdetect._wd "... No port tcp $tcpport available on $tcptarget" + return 1 + fi + dbdetect._wd "... Found tcp $tcpport on $tcptarget." fi - dbdetect._wd "... Found tcp $tcpport on $tcptarget." # --- check tcp process local tcpprocess; tcpprocess=$( ini.value "tcp-process" ) @@ -139,21 +152,45 @@ function dbdetect.exists(){ dbdetect._wd "... Process ${process} was found" fi + # --- check db files + local filetype; filetype=$( ini.value "type" ) + + if [ -n "${filetype}" ]; then + local myfiles; declare -a myfiles + for myfile in $( ini.value "file[]" ) + do + if ! file -b "${myfile}" | grep -i "$filetype" >/dev/null; then + dbdetect._wd "... File ${myfile} is no type $filetype" + return 1 + fi + dbdetect._wd "... File ${myfile} is type $filetype" + myfiles+="${myfile}|" + done + fi + + # --- OK, everything was found ... we initialize it + dbdetect._wd "OK, match: $_config" + ini.set "$_config" "set" local value local dbuser=$( ini.value "dbuser" ) local dbpassword=$( ini.value "dbpassword" ) - for mykey in $( ini.keys ) + + for mykey in su env params do - # params = '--port={{tcp}} --password={{dbpassword}} --user={{dbuser}} --host={{tcp-target}}' value="$( ini.value "$mykey" )" value="${value//\{\{tcp\}\}/$tcpport}" value="${value//\{\{tcp-target\}\}/$tcptarget}" value="${value//\{\{dbuser\}\}/$dbuser}" value="${value//\{\{dbpassword\}\}/$dbpassword}" + value="${value//\{\{file\[\]\}\}/$myfiles}" + DBD_PARAMS[$mykey]="$value" + dbdetect._wd ">>> $mykey = $value" done + dbdetect._wd ">>> files = $myfiles" + DBD_PARAMS[files]="$myfiles" return 0 } @@ -167,6 +204,13 @@ function dbdetect.getParams(){ echo "${DBD_PARAMS['params']}" } + +# for backup scripts: get checked files from [detect] -> file[] +# +function dbdetect.getFiles(){ + echo "${DBD_PARAMS['files']}" | tr "|" "\n" +} + # set variables in [set] -> env = ... # USAGE: # eval $( dbdetect.setenv ) @@ -189,26 +233,6 @@ function dbdetect.runas(){ # FUNCTIONS 4 DB-WRAPPER # ---------------------------------------------------------------------- - function db.init(){ - - BACKUP_BASEDIR=$(_j_getvar ${JOBFILE} "dir-localdumps") - - # check - if [ -z "$BACKUP_BASEDIR" ]; then - color error - echo ERROR: missing config for backup target. - echo There must be an entry dir-localdumps in ${JOBFILE} - color reset - exit 1 - fi - BACKUP_PLUGINDIR=$(dirname $0)/plugins/localdump - 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) - } # helpfer function for SERVICENAME.backup # it is called after the service specific dump was done. @@ -346,7 +370,7 @@ function dbdetect.runas(){ if [ -z $1 ]; then find -type f | sed "s#__[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9].*##g" | grep -v "\.meta" | sort -ud| sed "s#^\./##g" else - ls -ltr "$*__"* | sed "s#^\./##g" + ls -ltr "$*__"* | sed "s#^\./##g" | grep -v "\.meta" fi cd - >/dev/null else @@ -376,7 +400,7 @@ function dbdetect.runas(){ # the metafile is written in sqlite backup to store full path metafile=${BACKUP_TARGETDIR}/${dumpfile}.meta if [ -f $metafile ]; then - cat $metafile + 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") @@ -391,6 +415,29 @@ function dbdetect.runas(){ } + # 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 + } + # ------------------------------------------------------------ # show help # ------------------------------------------------------------ @@ -419,7 +466,26 @@ function dbdetect.runas(){ # INIT # ---------------------------------------------------------------------- - db.init + # ----- db init + BACKUP_BASEDIR=$(_j_getvar ${JOBFILE} "dir-localdumps") + + # check + if [ -z "$BACKUP_BASEDIR" ]; then + color error + echo ERROR: missing config for backup target. + echo There must be an entry dir-localdumps in ${JOBFILE} + color reset + exit 1 + fi + 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 @@ -455,7 +521,14 @@ function dbdetect.runas(){ case "$mode" in # ------------------------------------------------------------ check) - . $BACKUP_SCRIPT $mode + DBD_DEBUG=1 + for PROFILENAME in $(dbdetect.getConfigs) + do + echo "----- $PROFILENAME" + dbdetect.exists "${PROFILENAME}" + echo + done + # . $BACKUP_SCRIPT $mode ;; # ------------------------------------------------------------ backup) @@ -469,23 +542,6 @@ function dbdetect.runas(){ services=$* fi - # ----- check all params - for SERVICENAME in $services - do - BACKUP_SCRIPT=$( get_service_script ${SERVICENAME} ) - if [ ! -f $BACKUP_SCRIPT ]; then - color error - echo ERROR: parameter $SERVICENAME seems to be wrong. - echo The backup script does not exist: $BACKUP_SCRIPT - color reset - echo - echo services in this folder are: - get_services - exit 2 - fi - done - - # ----- GO # PROFILENAME mysql_localhost_13306 # SERVICENAME mysql @@ -505,23 +561,25 @@ function dbdetect.runas(){ h2 "START SCRIPT FOR [${PROFILENAME}] -> ${SERVICENAME}" # ------ set env + # echo "BACKUP_PARAMS = $BACKUP_PARAMS" # dbdetect.setenv eval $( dbdetect.setenv ) - # echo "BACKUP_PARAMS = $BACKUP_PARAMS" _j_runHooks "200-before-db-service" - . $BACKUP_SCRIPT $mode + 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 - # ------ unset env - eval $( dbdetect.unssetenv ) else diff --git a/plugins/localdump/sqlite.sh b/plugins/localdump/sqlite.sh index b6bbb85..02de27a 100755 --- a/plugins/localdump/sqlite.sh +++ b/plugins/localdump/sqlite.sh @@ -12,6 +12,7 @@ # 2018-02-02 ah,ds v1.0 first lines # 2018-02-09 ah,ds v1.1 write a .meta file after successful backup # 2022-03-17 v1.2 WIP: add lines with prefix __DB__ +# 2024-02-20 ah v1.3 Handle profiles; restore into existing db file; restore file owner and perms # ================================================================================ if [ -z "$BACKUP_TARGETDIR" ]; then @@ -23,22 +24,17 @@ fi # CONFIG # -------------------------------------------------------------------------------- -FILEDEFS=${DIR_JOBS}/backup-dbfiles.job - # -------------------------------------------------------------------------------- # FUNCTIONS # -------------------------------------------------------------------------------- -# make sqlite3 backups of all sqlite = ... in backup-dbfiles.job +# start multiple sqlite3 backups +# files are taken from loaded profiles/sqlite*.ini - section [detect] -> files[] function doSqliteBackup(){ - if ! _j_getvar ${FILEDEFS} "sqlite" | grep . ; then - echo "__DB__$SERVICENAME SKIP: no entries found for sqlite." - return 0 - fi create_targetdir - for DATABASE_FILE in $(_j_getvar ${FILEDEFS} "sqlite") + for DATABASE_FILE in $( dbdetect.getFiles ) do echo -n "__DB__${SERVICENAME} backup $DATABASE_FILE " if [ ! -f "$DATABASE_FILE" ]; then @@ -47,24 +43,17 @@ function doSqliteBackup(){ color reset rc=$rc+1 else - file "$DATABASE_FILE" | cut -f 2 -d ":" | grep -i "sqlite" >/dev/null - if [ $? -ne 0 ]; then - color error - echo "ERROR: given database file is not a sqlite database" - color reset - rc=$rc+1 - else - TARGET=$(get_outfile ${DATABASE_FILE}) - TARGET=${BACKUP_TARGETDIR}/$(echo ${TARGET} | sed "s#/#_#g").sql - META=${TARGET}.gz.meta - # echo -n " to $TARGET " - sqlite3 "$DATABASE_FILE" .dump >"${TARGET}" - fetchrc >/dev/null - db._compressDumpfile "${TARGET}" && echo "$DATABASE_FILE" >"${META}" - - ls -l ${TARGET}* - - fi + + TARGET=$(get_outfile ${DATABASE_FILE}) + TARGET=${BACKUP_TARGETDIR}/$(echo ${TARGET} | sed "s#/#_#g").sql + META=${TARGET}.gz.meta + # echo -n " to $TARGET " + sqlite3 "$DATABASE_FILE" .dump >"${TARGET}" + fetchrc >/dev/null + db._compressDumpfile "${TARGET}" && stat "$DATABASE_FILE" >"${META}" + + ls -l ${TARGET}* + fi done } @@ -73,29 +62,27 @@ function doSqliteBackup(){ # param string database dump file (gzipped) # param string optional: database to import; default: database is parsed from file function restoreByFile(){ - sMyfile=$1 - sMyDb=$2 + local sMyfile=$1 + local sMyDb=$2 if [ -f "${sMyDb}" ]; then - color error - echo ERROR: target file already exists. Remove or rename it first. + # echo -n "" > "${sMyDb}" + echo "Deleting ${sMyDb} before restore..." + rm -f "${sMyDb}" + fi + + echo -n "Restore ${sMyfile} ... " + color cmd + zcat "${sMyfile}" | sqlite3 "${sMyDb}" + fetchrc + if [ $myrc -eq 0 ]; then + restorePermissions "${sMyfile}" "${sMyDb}" ls -l "${sMyDb}" - color reset - rc=$rc+1 + echo else - color cmd - zcat "${sMyfile}" | sqlite3 "${sMyDb}" - fetchrc - if [ $myrc -eq 0 ]; then - color ok - echo OK, restore was successful - color reset - ls -l "${sMyDb}" - else - color error - echo ERROR while restoring backup. - color reset - fi + color error + echo ERROR while restoring backup. + color reset fi } @@ -108,12 +95,6 @@ function restoreByFile(){ # ----- check requirements j_requireBinary "sqlite3" 1 -if [ ! -f "$FILEDEFS" ]; then - echo "INFO: file definitions $FILEDEFS do not exist." - rc=$rc+1 -fi - - if [ $rc -ne 0 ]; then rc=0 echo "SKIP: sqlite seems not to be here" -- GitLab