Select Git revision
check_snmp_printer
-
Hahn Axel (hahn) authoredHahn Axel (hahn) authored
jobhelper.sh 13.96 KiB
#!/bin/bash
# ================================================================================
#
# JOBHELPER
# helper script to share functions for parsing and handlinmg backup jobs
#
# --------------------------------------------------------------------------------
# ah - Axel Hahn <axel.hahn@unibe.ch>
# ds - Daniel Schueler <daniel.schueler@iml.unibe.ch>
#
# 2016-11-10 ah,ds v1.0
# 2017-01-23 ah,ds v1.1 added j_getLastBackupAge
# 2017-02-16 ah,ds v1.2 added storage helper function
# 2018-02-13 ah,ds v1.3 detect samba shares based on a flag
# 2022-10-07 ah v1.4 unescape regex with space to prevent "grep: warning: stray \ before white space"
# 2023-03-17 ah v1.5 ignore required user on MS windows; source jobs/env if it exists; use varaiable FQDN
# 2023-04-12 ah v1.6 add desktop notification
# 2023-10-06 v1.7 source jobs/env_defaults
# ================================================================================
# ----------------------------------------------------------------------
# CONFIG
# ----------------------------------------------------------------------
DIR_SELF=$( dirname "$0" )
DIR_JOBS="${DIR_SELF}/jobs"
DIR_LOGS="${DIR_SELF}/logs"
JOBFILE="${DIR_JOBS}/backup.job"
DIRFILE="${DIR_JOBS}/dirs.job"
STORAGEFILE="${DIR_JOBS}/transfer.job"
# ----------------------------------------------------------------------
# METHODS
# ----------------------------------------------------------------------
# ------------------------------------------------------------
# init function
# ------------------------------------------------------------
function j_init(){
j_banner
. "${DIR_JOBS}/env_defaults"
if [ -r "${DIR_JOBS}/env" ];
then
echo "INFO: loading custom environment ${DIR_JOBS}/env"
. "${DIR_JOBS}/env"
fi
_getFqdn
if [ ! -d "${DIR_LOGS}" ]; then
mkdir -p "${DIR_LOGS}" && echo "INFO: dir created ${DIR_LOGS}"
fi
if [ ! -d "${DIR_JOBS}" ]; then
# mkdir -p ${DIR_JOBS} && echo "INFO: dir created ${DIR_JOBS}"
>&2 echo "ERROR: missing jobs directory. Aborting."
exit 1
fi
for myfile in "${JOBFILE}" "${DIRFILE}" "${STORAGEFILE}"
do
if [ ! -f "${myfile}" ]; then
echo "WARNING: missing a config file: $myfile"
# exit 1
fi
done
# for date definitions like weekdays
JOBDEF_LANG=$(_j_getvar "${JOBFILE}" "lang")
if [ -z "$JOBDEF_LANG" ]; then
JOBDEF_LANG="en_us"
fi
export LANG=$JOBDEF_LANG
j_requireBinary "cut"
j_requireBinary "date"
j_requireBinary "grep"
j_requireBinary "sleep"
j_requireBinary "tee"
j_requireBinary "touch"
# for notify-send in j_notify()
if [ -n "$SUDO_USER" ]; then
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u $SUDO_USER)/bus
fi
# j_read
}
# ------------------------------------------------------------
# draw an ascii banner
# ------------------------------------------------------------
function j_banner(){
cat << eofbanner
___ ___ ___ ___ _______ __
| | Y | | | _ .---.-.----| |--.--.--.-----.
|. |. |. | |. 1 | _ | __| <| | | _ |
|. |. \_/ |. |___ |. _ |___._|____|__|__|_____| __|
|: |: | |: 1 | |: 1 \ |__|
|::.|::.|:. |::.. . | |::.. . /
\`---\`--- ---\`-------' \`-------'
eofbanner
sed "s#^# #g" "$0.banner" 2>/dev/null || echo $0
echo '_______________________________________________________________________________ ___ __ _'
echo
}
# ------------------------------------------------------------
# get list of all directories to backup / restore
# ------------------------------------------------------------
function j_getDirs2Backup(){
STORAGE_SAMBASHARES=$(_j_getvar "${STORAGEFILE}" "sambashares")
_j_getvar "${JOBFILE}" dir-localdumps
_j_getvar "${JOBFILE}" dir-dbarchive
_j_getvar "${DIRFILE}" include
# get dirs of filesets, i.e.
# set-custom-[key]--dir = /home/ladmin
_j_getvar "${DIRFILE}" "set.*--dir"
# detect Samba shares (set sambashares = 1 for it)
if [ -z "${STORAGE_SAMBASHARES}" ] || [ "${STORAGE_SAMBASHARES}" -eq 0 ]; then
echo NO >/dev/null
else
if [ -f /etc/samba/smb.conf ]; then
for dirOfShare in $( grep "path.*=" "/etc/samba/smb.conf" | grep -v "#.*path" | cut -f 2 -d "=" )
do
echo "$dirOfShare"
done
fi
fi
}
# ------------------------------------------------------------
# get the id of a given backup dir (to find includes and
# excludes for it)
# param string path
# ------------------------------------------------------------
function j_getSetnameOfPath(){
grep "^set.*dir = $*$" "${DIRFILE}" | cut -f 1 -d "=" | sed "s#\-\-dir ##g"
}
# ------------------------------------------------------------
# get full target of the current host with a given
# backed up directory
# param string name of directory
# param string name of host (for future releases)
# ------------------------------------------------------------
function j_getFullTarget(){
sTmpSafeName=$(j_getSafename "$1")
sTmpHostname=$2
if [ -z "$sTmpHostname" ]; then
sTmpHostname=$FQDN
fi
if [ -z "${STORAGE_BASEDIR}" ]; then
STORAGE_BASEDIR=$(_j_getvar "${STORAGEFILE}" "storage")
fi
echo "${STORAGE_BASEDIR}/${sTmpHostname}/${sTmpSafeName}"
}
# ------------------------------------------------------------
# replace / to _ to get a save filename for a directory to
# backup or restore
# param string name of directory
# ------------------------------------------------------------
function j_getSafename(){
echo "${*//[\/\\:]/_}"
}
# ------------------------------------------------------------
# get a value from a job config file
# param string name of jobfile
# param string name of variable to fetch value from
# ------------------------------------------------------------
function _j_getvar(){
if [ ! -r "${1}" ]; then
>&2 echo "ERROR: cannot read file: ${1}. Abort."
exit 100
fi
grep "^${2} = " < "${1}"| cut -f 3- -d " "
}
# ------------------------------------------------------------
# read local jobdescription and set as variables
# ------------------------------------------------------------
# ------------------------------------------------------------
# execute hook skripts in a given directory in alphabetic order
# param string name of hook directory
# param string optional: integer of existcode or "" for non-on-result hook
# ------------------------------------------------------------
function _j_runHooks(){
local _hookbase="$1"
local _exitcode="$2"
local _hookdir="$( dirname $0 )/hooks/$_hookbase"
if [ -z "$_exitcode" ]; then
_hookdir="$_hookdir/always"
elif [ "$_exitcode" = "0" ]; then
_hookdir="$_hookdir/on-ok"
else
_hookdir="$_hookdir/on-error"
fi
for hookscript in $( ls -1a "$_hookdir" | grep -v "^\." | sort )
do
if [ -x "$_hookdir/$hookscript" ]; then
h3 "HOOK: start $_hookdir/$hookscript ..."
$_hookdir/$hookscript
else
h3 "HOOK: SKIP $_hookdir/$hookscript (not executable) ..."
fi
done
# if an exitcode was given as param then run hooks without exitcode
# (in subdir "always")
if [ -n "$_exitcode" ]; then
_j_runHooks "$_hookbase"
fi
echo
}
# ------------------------------------------------------------
# parse day of week and day of month and echo 0 or 1
#
# full = DOW:Mon,Tue,Wed
# full = DOM:07,08,09,
#
# It is used for inc = ... and full = ...
#
# param string date to compare
# param string value of full|inc in backup.job
# ------------------------------------------------------------
function _j_runToday(){
typeset -i local bToday=0
local sCompDate="$1"
shift 1
local value="$*"
# grep weekday
echo "$value" | grep "^DOW:" | grep $(date +%a -d "$sCompDate") >/dev/null && bToday=1
# grep day of month
echo "$value" | grep "^DOM:" | grep $(date +%d -d "$sCompDate") >/dev/null && bToday=1
# grep nth weekday of a month
echo "$value" | grep "^WDM:" >/dev/null
if [ $? -eq 0 ]; then
typeset -i local iDayOfMonth
iDayOfMonth=$(date +%e -d "$sCompDate")
typeset -i local iWeekday
iWeekday=$(date +%u -d "$sCompDate")
# `date +%u` - weekday as int; Sun = 0
# `date +%e` - day in date
typeset -i local iWeekInMonth
iWeekInMonth=$(echo $(( ( ${iDayOfMonth} - ${iWeekday} + 6 ) / 7 )) )
typeset -i local n
n=$(echo "$value" | grep "^WDM:" | cut -f 2- -d ":" | cut -c 1)
local sDay
sDay=$(echo "$value" | grep "^WDM:" | cut -f 2- -d ":" | cut -f 2 -d " ")
if [ ${n} -eq ${iWeekInMonth} ] && [ ${sDay} = $(date +%a -d "$sCompDate") ]; then
bToday=1
fi
fi
echo $bToday
}
# ------------------------------------------------------------
# parse day of week and day of month and echo 0 or 1
#
# full = DOW:Mon,Tue,Wed
# full = DOM:07,08,09,
#
# It is used for inc = ... and full = ...
#
# param string value of full|inc in backup.job
# ------------------------------------------------------------
function _j_isToday(){
sCmpDate=$(date +%s)
_j_runToday "@$sCmpDate" "$*"
}
# ------------------------------------------------------------
# read local jobdescription and set as variables
# ------------------------------------------------------------
function j_read(){
# mytime=$(date +%H%M)
# --- parse something
BACKUP_TARGETDIR=$(_j_getvar "${JOBFILE}" "dir-local-dumps")
export BACKUP_TARGETDIR
JOBDEF_TYPE=$(_j_getvar ${STORAGEFILE} "type")
export JOBDEF_TYPE
if [ "$JOBDEF_TYPE" != "auto" ]; then
# ----- detect if current date matches a definition "full = ..."
local cfg_full; cfg_full=$(_j_getvar "${STORAGEFILE}" "full")
local bIsTodayFull; bIsTodayFull=$(_j_isToday "$cfg_full")
# ... if "1" ... then verify time with "start-time-full = ...""
if [ $bIsTodayFull -eq 1 ]; then
local sStart
sStart=$(_j_getvar "${STORAGEFILE}" "start-time-full")
test -z "$sStart}" && sStart="."
if date +%H:%M | grep "$sStart" >/dev/null; then
JOBDEF_TYPE=full
fi
fi
fi
if [ -z "$JOBDEF_TYPE" ]; then
JOBDEF_TYPE=auto
fi
test "${JOBDEF_TYPE}" = "auto" && JOBDEF_AUTO=$(_j_getvar ${STORAGEFILE} "auto")
export JOBDEF_AUTO
_j_setLogfile
}
# ------------------------------------------------------------
# read local jobdescription and set as variables
# ------------------------------------------------------------
function _j_setLogfile(){
JOB_DONEFILE=${DIR_LOGS}/${JOBDEF_TYPE}-$(date +%Y%m%d-%H%M%S)
JOB_LOGFILE="${JOB_DONEFILE}.log"
export JOB_LOGFILE
}
# ------------------------------------------------------------
# check if a binary exists - and abort if not
# param string name of file
# param bool flag to skip; default: none = abort on miss
# ------------------------------------------------------------
function j_requireBinary(){
# echo "CHECK binary $1"
which "$1" >/dev/null 2>&1
rcself=$?
if [ $rcself -ne 0 ]; then
rc=$rc+$rcself
echo "INFO: missing binary $1"
if [ -z "$2" ]; then
exit 3
fi
return 1
fi
return 0
}
# ------------------------------------------------------------
# check if a process name exisats
# param string regex to find in ps -ef output
# param bool flag to skip; default: none = abort on miss
# ------------------------------------------------------------
function j_requireProcess(){
# echo "CHECK process $1"
# ps -ef | grep -v grep | grep -E "$1" >/dev/null
pgrep -l "$1" >/dev/null
rcself=$?
if [ $rcself -ne 0 ]; then
rc=$rc+$rcself
echo "INFO: missing process $1"
if [ -z "$2" ]; then
exit 4
fi
return 1
fi
return 0
}
# detect ms windows by cygwin, mingw, msys ...
# see https://en.wikipedia.org/wiki/Uname
# return code is 0 for YES
function _isMswindows(){
uname | grep -iE "(CYGWIN_NT|MINGW|MSYS_NT|Windows_NT|WindowsNT)" >/dev/null
}
# get fqdn and put it into FQDN; called in j_init
# - with gnu core utils: hostname -f
# - on ms windows: grep from output of ipconfig and %COMPUTERNAME%
function _getFqdn(){
if [ -z "$FQDN" ]; then
if ! FQDN=$( hostname -f 2>/dev/null ); then
echo "INFO: hostname -f is not available"
if _isMswindows; then
if [ -n "$COMPUTERNAME" ]; then
local _domain
_domain=$( ipconfig -all | grep "Primary Dns" | cut -f 2 -d ':' | tr -d ' ' )
FQDN="${COMPUTERNAME}.${_domain}"
fi
fi
fi
if [ -z "$FQDN" ]; then
echo "ERROR: unable to detect fqdn. Aborting."
exit 1
fi
fi
# TODO force fqdn?
# if ! echo "$FQDN" | grep "\..*" >/dev/null; then
# echo "ERROR: hostname [$FQDN] is not a fqdn and does not contain a domain name. Aborting."
# exit 1
# fi
# echo "INFO: FQDN is [$FQDN]"
}
# show a desktop notification using notify-send
# param string summary (aka title)
# param string message text
# paran integer optional: exitcode; if set it adds a prefix OK or ERRROR on summary and sets urgency on error
function j_notify(){
local _summary="IML BACKUP :: $1"
local _body="$( date +%H:%M:%S ) $2"
local _rc="$3"
if which notify-send >/dev/null 2>&1; then
if [ -n "$DBUS_SESSION_BUS_ADDRESS" ]; then
local _urgency="normal"
if [ -n "$_rc" ]; then
if [ "$_rc" = "0" ]; then
_summary="OK: ${_summary}"
else
_summary="ERROR: ${_summary}"
_urgency="critical"
fi
fi
su "$SUDO_USER" -c "notify-send --urgency=${_urgency} '${_summary}' '${_body}'"
fi
fi
}
# ------------------------------------------------------------
# check if it was startet with a given user
# This is skipped if MS windows was detected with "mingw".
# param string username, i.e. root
# ------------------------------------------------------------
function j_requireUser(){
if _isMswindows; then
echo "SKIP: j_requireUser $1 is not handled on MS Windows."
else
sUser=$(id | cut -f 2 -d "(" | cut -f 1 -d ")")
if [[ "$sUser" != "$1" ]]; then
>&2 echo "ERROR: user $1 is reqired."
exit 5
fi
fi
}
# ----------------------------------------------------------------------
# INIT
# ----------------------------------------------------------------------
j_init