Files
secondo/CM-Scripts/libutil.sh
2026-01-23 17:03:45 +08:00

887 lines
17 KiB
Bash

#!/bin/sh
#
# Jan 2005, M. Spiekermann. This is a small library
# of functions useful for several shell scripts.
#
# July 2005, M. Spiekermann. Improvements for killing processes.
# Since kill does not kill child processes the functions findChilds
# and killProcess were introduced. Moreover the timeOut function was
# revised to work without active waiting.
#
# Jan 2006, M. Spiekermann. Function sendMail revised. Mails and theirs attachment
# will now be backed up in a configurable directory. Moreover a new function
# ~isCmdPresent~ was added.
#
# Sept 2006, M. Spiekermann. Function ~checkVersion~ introduced. The tests are
# relocated into a new file called libutil-test.sh.
# recognize aliases also in a non interactive shell
shopt -s expand_aliases
# global variables used to store results
LU_LOG=""
LU_LOG_INIT=""
# write to screen if a log file can not be accessed
LU_LOG_SCREEN="true"
LU_VARVALUE=""
LU_TMP=""
LU_USERTMP=""
LU_CHILDS=""
LU_MAPSTR=""
LU_xPID=""
declare -i LU_STEPCTR=1
declare -i LU_RC=0
declare -i LU_ERRORS=0
# global constants which influence the
# behavior of some functions
LU_TESTMODE=""
LU_SENDMAIL="true"
LU_RULER="--------------------------------------------------------------------"
# colors
LU_normal="\033[0m"
LU_red="\033[31m"
LU_green="\033[32m"
LU_blue="\033[34m"
###################################################################
###
### Start of function definitions
###
###################################################################
# getTimeStamp
function getTimeStamp {
date_TimeStamp=$(date "+%y%m%d-%H%M%S")
date_ymd=${date_TimeStamp%-*}
date_HMS=${date_TimeStamp#*-}
}
# show the value of a given variable name
#
# $1 variable name
function showValue
{
local var=$1
eval echo $var' = \<$'$var'\>'
}
# $1 variable name
# $2 value
function showValue2
{
local var=$1
echo "$var = <$2>"
}
# print to log-file
#
function printl {
if [ -n "$LU_LOG_INIT" ]; then
printf "$1" "$2" >> $LU_LOG
else
if [ "$LU_LOG_SCREEN" != "false" ]; then
printf "$1" "$2"
fi
fi
}
# print to screen and into log-file if $LU_LOG is nonzero
#
function printx {
printf "$1" "$2"
printl "$1" "$2"
}
#
#
function printlr {
printl "%s\n" $LU_RULER
}
function printxr {
printx "%s\n" $LU_RULER
}
# $1 variable name
function varValue {
LU_VARVALUE=""
if [ $# -eq 0 ]; then
return 1
fi
LU_VARVALUE="\"$(env | grep ^$1=)\""
return 0
}
# $1 mode = [err, warn, info]
# $2 msg
function showMsg {
local col=$LU_normal
local msg=""
if [ $# == 2 ]; then
if [ "$1" == "err" ]; then
col=$LU_red
msg="ERROR: "
fi
if [ "$1" == "warn" ]; then
col=$LU_blue
msg="WARNING: "
fi
if [ "$1" == "info" ]; then
col=$LU_green
fi
if [ "$1" == "em" ]; then
col=$LU_blue
fi
shift
fi
echo -e "${col}${msg}${1}${LU_normal}\n"
if [ -n "$LU_LOG_INIT" ]; then
echo -e "${1}\n" >> $LU_LOG
fi
}
# check if we are running in a bash
if [ -z "$BASH" ]; then
showMsg "ERROR: You need a bash shell to run this script!"
exit 1
fi
# assert - check if a command was successful
#
# $* cmd
function assert {
if ! $*; then
showMsg "err" "Assertion failed!"
printx "%s\n" "pwd: $PWD"
printx "%s\n" "cmd: $*"
exit 1
fi
return 0
}
# check if /tmp is present and writable and create a user specific
# subdir variables LU_TMP and LU_USERTMP are defined afterwards
#
function createTempDir {
LU_TMP=/tmp
if [ ! -w /tmp ]; then
showMsg "warn" "Directory \"/tmp\" not present or not writable!"
if [ ! -w "$HOME" ]; then
varValue HOME
showMsg "err" "Directory $LU_VARVALUE not present or not writeable!"
exit 1
fi
LU_TMP=$HOME/0tmp$!
if ! mkdir -p $LU_TMP; then
showMsg "err" "Could not create directory \"$LU_TMP\""
fi
fi
LU_USERTMP=$LU_TMP/shlog-$USER
if [ ! -d "$LU_USERTMP" ]; then
if ! mkdir -p $LU_USERTMP; then
showMsg "err" "Could not create directory \"$LU_USERTMP\""
exit 1
fi
fi
return 0
}
# create a given directory if it does not exist
#
# $1 dir
function createDir {
local dir=$1
if [ ! -d $dir ]; then
assert mkdir -p $dir
fi
}
# write startup information into logfile
# $1 writable log-file
#
function initLogFile {
if [ -n "$1" ]; then
LU_LOG=$1
else
createTempDir
LU_LOG=$LU_USERTMP/sh_$$.log
fi
if [ -n "$LU_LOG" ]; then
touch $LU_LOG
if [ $? -ne 0 ]; then
varValue LU_LOG
showMsg "err" "Can't touch log-file $LU_LOG!"
exit 1
fi
else
showMsg "err" "No log-file defined!"
exit 1
fi
LU_LOG_INIT="true"
# set native language support to US English in order to
# avoid exotic messages in th log file
export LANG="en_US"
printl "%s\n" "############################################"
printl "%s\n" "# Log of $(date)"
printl "%s\n" "############################################"
printl "%s\n" "Environment settings:"
env 2>&1 >> $LU_LOG
printl "%s\n" "############################################"
}
if [ "$OSTYPE" == "msys" ]; then
prefix=/c
platform="win32"
elif [ "$OSTYPE" == "mac_osx" ]; then
prefix=$HOME
platform="mac_osx"
else
# assuming linux
prefix=$HOME
platform="linux"
fi
function win32Host {
if [ "$OSTYPE" == "msys" ]; then
return 0
fi
return 1
}
# printSep $1
#
# $1 message
#
# print a separator with a number and a message
function printSep {
printx "\n%s\n" "Step ${LU_STEPCTR}: ${1}"
printx "%s\n" "$LU_RULER"
let LU_STEPCTR++
}
# checkCmd $*
# $* command
#
# execute a command. In case of an error display the
# return code
function checkCmd {
printlr
printl "%s\n" "pwd: $PWD"
printl "%s\n" "cmd: $*"
if [ "$LU_TESTMODE" != "true" ]; then
# call command using eval
if [ -z "$LU_LOG_INIT" ]; then
eval "$*"
else
eval "{ $*; } >> $LU_LOG 2>&1"
fi
LU_RC=$? # save return code
if [ $LU_RC -ne 0 ]; then
showMsg "err" "Command {$*} returned with value ${LU_RC}"
let LU_ERRORS+=1
fi
fi
printlr
return $LU_RC
}
# findChilds
#
# search recursively for child processes. Result is stored
# in global variable LU_CHILDS
LU_TAB=$(echo -e "\t")
function findChilds {
if [ "$OSTYPE" == "msys" ]; then
# the msys sed implementation has problems with \t.
# the next variable holds a TAB value
#ps -f | sed -ne "s#\([^ $t]*\)[ $t]\+\([0-9]\+\)[ $t]\+$1[ $t].*#\2#p" | cat
local nextChilds=$(ps -f | sed -ne "s#\([^ $LU_TAB]*\)[ $LU_TAB]\+\([0-9]\+\)[ $LU_TAB]\+$1[ $LU_TAB].*#\2#p" | cat)
else
local nextChilds=$(ps h -o pid --ppid $1 2>/dev/null | cat)
fi
local nc=""
LU_CHILDS="$nextChilds $LU_CHILDS"
for nc in $nextChilds; do
findChilds $nc
done
}
# isRunning $1
#
# PID to check
#
# checks if the given process is still running
function isRunning {
if [ "$1" != "" ]; then
if [ "$platform" != "linux" ]; then
ps -f | sed -ne 's#\([^ \t]*\)[ \t]*\([0-9]\+\).*#_\2_#p' | grep "_$1_" > /dev/null
rc=$?
else
ps -p $1 > /dev/null
rc=$?
fi
return $rc
else
return 1
fi
}
# killProcess $1
#
# $1 PID to kill
#
# this function kills the process and its childs
function killProcess {
printl "\n%s\n" " Killing process $1"
findChilds $1
if [ -z $2 ]; then
sig=-9
else
sig=$2
fi
printl "%s\n" " Killing child processes: $LU_CHILDS"
kill $sig $1 $LU_CHILDS >/dev/null 2>&1
return 0
}
# killAfterTimeout $1 $2
#
# $1 PID to kill
# $2 seconds to wait
#
# function sending SIGINT to $2 when timeout of $1 is reached.
# Should only be started in background!
function killAfterTimeOut {
sleep $2
# check if process is still running
if isRunning $1; then
printx "\n%s\n" "Timeout for process $1 reached!"
killProcess $1
LU_RC=500
return $LU_RC
fi
exit 0
}
# timeOut $1 $2 ....
#
# $1 max seconds to wait
# $2 ... command and args
#
# runs checkCmd and kills the process after timeout
function timeOut() {
echo -e "${FUNCNAME}: args = $*\n"
local seconds=$1
shift
# start backgound process for command
# and store its process id
eval $*&
local TIMEOUT_PID=$!
# start function (in backgound) which will
# send kill the process which executes the comamnd
# after timeout
killAfterTimeOut $TIMEOUT_PID $seconds&
local FUNC_PID=$!
echo -e "${FUNCNAME}: command PID=$TIMEOUT_PID, killfunc PID=$FUNC_PID\n"
# wait for termination of the command
wait $TIMEOUT_PID
local rc=$?
if [ $LU_RC -eq 500 ]; then
rc=$LU_RC
else
LU_RC=$rc
fi
# if the command finished before timeout
# kill the sleeping process
if isRunning $FUNC_PID; then
echo -e "${FUNCNAME}: Command finished before time-out!"
killProcess $FUNC_PID -15 >/dev/null 2>&1
fi
return $rc
}
# lastRC
#
# returns $LU_RC
function lastRC {
return $LU_RC
}
# isCmdPresent $1
#
# $1 command name
#
# checks if a given command is known
function isCmdPresent()
{
type -t $1 >& /dev/null
return $?
}
# sendMail $1 $2 $3 [$4 $5]
#
# $1 subject
# $2 recipients
# $3 body
# $4 backup dir
# $5 attached file
#
# Sends a mail with a given attachment (only 1 file) to the
# list of recipients. A copy of the mail text and its attachments
# will be kept in a backup directory.
function sendMail() {
# check mandatory arguments
if [ $# -lt 3 ]; then
echo -e "\n ${FUNCNAME}: Error, less than 3 arguments!\n"
return 1
fi
if [ "$2" == "" ]; then
echo -e "\n ${FUNCNAME}: Error, 2nd argument is empty!\n"
return 1
fi
local subject=$1
local recipients=$2
local body=$3
# check optional arguments
if [ "$4" != "" ]; then
local backupDir="$4"
fi
if [ "$5" != "" ]; then
local attachOpt="-a $5"
local attachFile="$5"
fi
# build a comma separated list of recipients
local reclist=""
for name in $recipients; do
reclist="$reclist"$name","
done
reclist=${reclist%,*}
if [ "$LU_SENDMAIL_FROM" != "" ]; then
local ropt="-r $LU_SENDMAIL_FROM"
fi
# send mail
if [ "$LU_SENDMAIL" == "true" ]; then
printf "%s\n" "${FUNCNAME}: mail $ropt -s \"$subject\" $attachOpt \"$reclist\" <body>"
mail -r $LU_SENDMAIL_FROM $ropt -s"$subject" ${attachOpt} "$reclist" <<-EOFM
$body
EOFM
if [ "$backupDir" != "" ]; then
if [ ! -d $backupDir ]; then
assert mkdir -p $backupDir
fi
# redirect stdout
exec 6>&1
exec >> "$backupDir/Mails.txt"
echo -e "Sender : $LU_SENDMAIL_FROM"
echo -e "Subject : $subject"
echo -e "Recipients : $reclist"
if [ "$attachFile" != "" ]; then
echo -e "Attachments: $attachFile"
cp $attachFile $backupDir
fi
echo -e "$body"
echo -e "------------------------------"
# restore stdout
exec 1>&6 6>&-
fi
else
printf "%s\n" "${FUNCNAME}: Test mode!"
printf "%s\n" "----------------------"
showValue2 subject "$subject"
showValue2 recipients "$recipients"
showValue2 reclist "$reclist"
showValue2 body "$body"
showValue2 backupDir "$backupDir"
showValue2 attachFile "$attachFile"
printf "%s\n" "cmd: mail $ropt -s \"$subject\" $attachOpt \"$reclist\" <body>"
printf "%s\n\n" "----------------------"
fi
return 0
}
# showGPL
#
# Prints out the GPL disclaimer.
function showGPL() {
printf "%s\n" "Copyright (C) 2004-2007, University in Hagen,"
printf "%s\n" "Department of Computer Science,"
printf "%s\n\n" "Database Systems for New Applications."
printf "%s\n" "This is free software; see the source for copying conditions."
printf "%s\n" "There is NO warranty; not even for MERCHANTABILITY or FITNESS"
printf "%s\n" "FOR A PARTICULAR PURPOSE."
}
# uncompressFolders
#
# $1 list of directories
#
# For each directory all *.gz files are assumed to be a tar archive and
# all *.zip files a zip archive
function uncompressFolders {
local err=""
for folder in $*; do
local files=$(find $folder -maxdepth 1 -iname "*.zip" -or -iname "*.*gz")
for file in $files; do
uncompress $file
if [ $? -ne 0 ]; then
err="true"
fi
done
done
if [ -n "$err" ]; then
return 1
else
return 0
fi
}
# $1 extraction dir
# $2, .. $n files
function uncompressFiles {
local dir=$1
local err=""
# change dir
if ! cd $1; then
return $?
fi
shift
# check if files are present
if [ -z "$*" ]; then
return 1;
fi
# uncompress files
for file in $*; do
uncompress $file
if [ $? -ne 0 ]; then
err="true"
fi
done
if [ -n "$err" ]; then
return 1
else
return 0
fi
}
# $1 file
# $2 target dir
function uncompress {
local storedPWD=$PWD
local rc=0
local run=""
if [ -z $1 ]; then
return 0
fi
if [ -n "$2" ]; then
if [ ! -d $2 ]; then
showMsg "err" "uncompress: Directory $2 does not exist!"
return 1;
else
assert cd $2
fi
fi
local suffix=${1##*.}
if [ "$suffix" == "gz" -o "$suffix" == "GZ" -o "$suffix" == "tgz" -o "$suffix" == "TGZ" ]; then
checkCmd "tar -xzf $1"
rc=$?
run="true"
fi
if [ "$suffix" == "bz2" -o "$suffix" == "BZ2" ]; then
checkCmd "tar -xjf $1"
rc=$?
run="true"
fi
if [ "$suffix" == "zip" -o "$suffix" == "ZIP" ]; then
checkCmd "unzip -q -o $1"
rc=$?
run="true"
fi
if [ -n "$run" ]; then
assert cd $storedPWD
return $rc
fi
assert cd $storedPWD
showMsg "warn" "uncompress: Don't know how to handle suffix \"$suffix\"."
return 1;
}
# mapStr
#
# $1 file
# $2 name1
# $3 separator
#
# reads file $1 which contains a list of "name1 name2" entries
# and returns name2 if "$1"=0"name1". The parameter name1 should
# be unique otherwise the first occurrence will be used.
function mapStr() {
local sep=$3
if [ "$sep" == "" ]; then
sep=" "
fi
local line=$(grep $2 $1)
local name1=${line%%${sep}*}
local name2=""
if [ "$name1" == "$2" ]; then
#cut off name1
name2=${line#*${sep}}
# remove trailing blanks
name2=${name2%% *}
else
name2=""
fi
LU_MAPSTR=$name2
}
# $1 title
# $* options after xterm -e
function startupXterm {
local title=$1
shift
if [ "$*" == "" ]; then
return 0
fi
if [ -n "$LU_xterm" ]; then
## in some situations xhost access control must be disabled
xhost + > /dev/null 2>&1
$LU_xterm -title "$title" -e $* &
LU_xPID=""
sleep 1
xhost - > /dev/null 2>&1
if ! isRunning $!; then
showMsg "err" "Could not start \"$LU_xterm -e $*\" in backgound."
return 1
fi
LU_xPID=$!
else
showMsg "info" "No graphical console present! Child window will not be started."
return 1
fi
return 0
}
# $1 command
# $2 version given as "x.y"
function checkVersion {
#echo "args: $1, $2 ?"
local version1=$($1 | sed -nr '1s#.* ([0-9]+)[.]([0-9]+)[.]?([0-9]+)?.*#\1x\2y\3#p')
local version2=$(echo "$2" | sed -nr '1s#(.*)-?([0-9]+)[.]([0-9]+)[.]?([0-9]+)?.*#\2x\3y\4#p')
#echo "$version1 >= $version2 ?"
local -i n1=$[${version1%x*}]
local rest=${version1#*x}
#showValue rest
local -i n2=$[${rest%y*}]
local -i n3=$[${rest#*y}]
local -i k1=$[${version2%x*}]
local rest=${version2#*x}
#showValue rest
local -i k2=$[${rest%y*}]
local -i k3=$[${rest#*y}]
#echo "$n1 $n2 $n3 >= $k1 $k2 $k3 ?"
LU_Version1="${n1}.${n2}.${n3}"
LU_Version2="${k1}.${k2}.${k3}"
if let $[$n1 > $k1]; then
return 0
fi
if let $[$n1 < $k1]; then
return 1
fi
# major number is equal, compare 2nd level
if let $[$n2 > $k2]; then
return 0
fi
if let $[$n2 < $k2]; then
return 1
fi
# 2nd level is equal, compare 3rd level
if let $[$n3 >= $k3]; then
return 0
fi
return 1
}
# createSecondoDatabase $1 $2
#
# $1 database name
# $2 database file
function createSecondoDatabase() {
local db=$1
local file=$2
cd ${buildDir}/bin
local cmd1="create database ${db};"
local cmd2="restore database ${db} from '"${file}"';"
SecondoTTYBDB < "$cmd1; $cmd2; q;"
local rc=$?
if [ $rc -ne 0 ]; then
printf "\n%s\n" "Warning could not restore database ${db}! Some subsequent tests may fail."
fi
return $rc
}
###################################################################
###
### End of function definitions
###
###################################################################
# define some environment variables
createTempDir
TEMP=$LU_TMP
# check if a graphical console is present
type -p rxvt > /dev/null 2>&1
if [ $? -ne 0 ]; then
type -p xterm > /dev/null 2>&1
if [ $? -ne 0 ]; then
showMsg "warn" "No graphical console like rxvt or xterm available."
LU_xterm=""
else
LU_xterm="xterm"
fi
else
LU_xterm="rxvt"
fi
# some important directories in SECONDO's source tree
buildDir=${SECONDO_BUILD_DIR}
scriptDir=${buildDir}/CM-Scripts
binDir=${buildDir}/bin
optDir=${buildDir}/Optimizer
# extend PATH variables for using SECONDO inside shell scripts
PATH="${PATH}:${binDir}:${optDir}:${scriptDir}"
#initialize date_ variables
getTimeStamp