#!/bin/bash # Copyright: 2023 Virtuozzo International GmbH # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. [ -n "${CORELIB_VERSION:-}" ] && return 0; CORELIB_VERSION="0.1"; declare -x OVERRIDE_DIR="/var/lib/jelastic/overrides/" declare -x CUSTOMIZATIONS_DIR="/var/lib/jelastic/customizations/" DEBUG=${DEBUG:=0}; PROGRAM='searchProg'; declare -a __CORE_INCLUDE_LIST; function setSudoCommand(){ if [ "$UID" != '0' ]; then groups|grep -qw ssh-access && SUDO_COMMAND="sudo" || SUDO_COMMAND="" else SUDO_COMMAND="" fi } setSudoCommand; # checkDo # check if the function is exists and run it # params: # $1 - function name # $@ - function params function checkDo() { local function="${1}"; shift; if isFunction "${function}" ; then ${function} "$@"; else die "No function ${function}"; fi; } # defineBigInline # Example of usage: # defineBigInline myVar <<'EOF' # FirstLine '"1234"' # secondline @2 # $(this never be executed) # bla-bla-bla # EOF function defineBigInline() { IFS='\n' read -r -d '' ${1} || true; } # render bash template # render bash template # params: # $* - templates to render # result: # rendered template # return code is 0 if success 1 othervise function renderTemplate() { local lines=$* end_offset=${#lines} while [[ "${lines:0:end_offset}" =~ (.*)\$(\{([a-zA-Z_][a-zA-Z_0-9]*)\}|([a-zA-Z_][a-zA-Z_0-9]*))(.*) ]] ; do pre=${BASH_REMATCH[1]} post=${BASH_REMATCH[5]}${lines:end_offset} [[ -n ${BASH_REMATCH[3]} ]] && varName=${BASH_REMATCH[3]} || varName=${BASH_REMATCH[4]} if [[ $pre =~ \\+$ ]] && (( ${#BASH_REMATCH} % 2 )); then : else lines=${pre}${!varName}${post} fi end_offset=${#pre} done printf %s "$lines" } function checkParams() { local params=($*) local result="" for p in "${params[*]}" ; do eval "[[ -z \"\$$p\" ]] && { result=\"$p $result\";}" done [[ -z "$result" ]] && return 0; return 1; } # indexOf # return an index of specified element in given array # params: # $1 - source array # $2 - value to search # result: # item position # return code is 0 if success 1 othervise function indexOf() { local array=(${1}) value=${2}; local index=0; while [ "${index}" -lt "${#array[@]}" ]; do [[ "${array[${index}]}" = "${value}" ]] && { echo ${index}; return 0; } let "index++"; done; echo ""; return 1; } # removeValueFromArray # remove array element by it's value # params: # $1 - source array # $2 - value to remove # result: # stdout - modified array function removeValueFromArray() { : ${1:?"Missing param: array"}; : ${2:?"Missing param: value to remove"}; local array=(${1}) value=${2}; local index=$(indexOf "${array[*]}" "$value"); if [[ ! -z ${index} ]] ; then array[$index]=''; array=(${array[*]}) fi echo "${array[*]}" } # removeIdxFromArray # remove array element by it's position # params: # $1 - source array # $2 - item position # result: # stdout - modified array function removeIdxFromArray() { : ${1:?"Missing param: array"}; : ${2:?"Missing param: item position"}; local array=(${1}) index=${2}; array[$index]=''; echo "${array[*]}" } # searchProg # searches for a full name of given program # params: # $1 - program to find # result: # function exports an environment variable. Name of variable is uppercased program name. # The value of variable is the full path to function searchProg() { : ${1:?"Missing param: program name"}; # include exceptor; # ensureDiskSpaceIsOK; local prgName=${1}; local cmdVar=$(sed -re "s/(.*)/\U\1/;s/-/_/g" <<< $prgName); declare -p $cmdVar >/dev/null 2>&1 && return 1; eval "command -v $prgName >/dev/null 2>&1 && $cmdVar=\$(which $prgName) || $cmdVar='die \"$prgName not found\"';"; declare -r ${cmdVar}; return 0; } # die # exits from the script function die() { local item funcname="" sourcefile="" lineno="" n e s="yes"; if isFunction "writeErrorMsg" ; then e="writeErrorMsg" else e="echo" fi [[ ${1} == "-q" ]] && { s=""; shift; } [ ! -n "${@}" ] && ${e} "${@}" [[ -n "${s}" ]] && { ${e} "Call stack:" printStack; } kill ${MANAGE_KILL_TARGET} 2>/dev/null; [ -w "$resultFile" -a -n "$result" ] && { echo $result >$resultFile } exit 249 } function printStack() { for i in $(seq 0 $((${#FUNCNAME[@]} - 2))); do echo "${FUNCNAME[$i]} called from ${BASH_SOURCE[$i+1]} line ${BASH_LINENO[$i]}: "; sed -n "${BASH_LINENO[$i]}{s/^/ /;p}" "${BASH_SOURCE[$i+1]}" done } function startJemThreadQueueManager(){ include exceptor; # local jempidfile="/var/run/jem.pid"; jempidfile="/var/run/jem.pid"; local sleep_period="1s" ensureDiskSpaceIsOK || exit local subaction=$1; if [ "x$action" == "xvcs" -a "x$subaction" == "xautoupdate" ] ; then [[ $(kill -0 $(cat $jempidfile 2>/dev/null) > /dev/null 2>&1; echo $?) -eq 0 ]] && { echo "$(date +%D.%k:%M:%S.%N): $subaction - other process is running" >> $JEM_CALLS_LOG writeJSONResponseOut "result=>0" "message=>$subaction: other process is running" exit 0; } fi if [ "x$action" == "xbalancer" -a "x$1" == "xUpdatePortMapping" ] ; then jempidfile="/var/run/jem.294e8f74.pid"; elif [ "x$action" == "xhn" -a "x$subaction" == "xstat" ] ; then jempidfile="/var/run/jem.42c7a29c9.pid"; elif [ "x$action" == "xuser" -a "x$subaction" == "xadd" ] ; then while [ "$1" != "" ] do case "$1" in --eid) TQctid=${2//-/}; jempidfile="/var/run/jem.${TQctid}.pid"; break ;; esac shift; done elif [ "x$action" == "xauth" ] ; then if [ "x$subaction" == "xadd" -o "x$subaction" == "xremove" ]; then while [ "$1" != "" ] do case "$1" in --ctid) TQctid=$2; break ;; esac shift; done sleep 0.$(( RANDOM % 10)) jempidfile="/var/run/jem.5283f514f.$TQctid.pid"; fi elif [ "x$action" == "xstorage" -a "x$1" != "xmount" ] ; then jempidfile="/var/run/jem.86a06296$$.pid"; elif [ "x$action" == "xfilemanager" ] ; then jempidfile="/var/run/jem.dc6cbef1.pid" elif [ "x$action" == "xinstall" ] ; then jempidfile="/var/run/jem.fd9de7ac314ec$$.pid" elif [[ "$action" == "isolation" || "$action" == "hnfw" ]] ; then jempidfile="/var/run/jem.23b9ec1f564e4.pid" elif [[ "$action" == "network" ]] ; then jempidfile="/var/run/jem.network.pid" elif [ "x$action" == "xdocker" -o "x$action" == "xcommand" -o "x$action" == "xstorage" -o "x$action" == "xfirewall" ] ; then while [ "$1" != "" ] do case "$1" in --ctid) TQctid=$2; break ;; 'getpcstemplate') setgetTPL=$1; ;; --name) nameTPL=$2; ;; --version) versionTPL=$2; ;; esac shift; done if [ -z "$TQctid" ] ; then if [ "x$action" == "xfirewall" ] ; then TQctid="a543228b28.$action" else TQctid="${$}$action" fi fi [ ! -z "$setgetTPL" ] && { [ ! -z "$nameTPL" -o ! -z "$versionTPL" ] && TQctid=$(md5sum <<< "${nameTPL}${versionTPL}X" | awk '{print $1}' ) } if [ "x$subaction" == "xaftercreate" -o "x$subaction" == "xmount" ] ; then jempidfile="/var/run/jem.f55b442${TQctid}.pid"; else jempidfile="/var/run/jem.${TQctid}.pid"; fi fi [ -f $jempidfile ] && { echo -n `date +%D.%k:%M:%S.%N` >> ${JEM_CALLS_LOG} 2>&1; _jempid=$(cat $jempidfile 2>/dev/null) echo ":[WAITING] [PID: $_jempid] Current PID: $$. Action: $action Subaction: $subaction" >> ${JEM_CALLS_LOG} 2>&1; while [[ $(kill -0 $(cat $jempidfile 2>/dev/null) > /dev/null 2>&1; echo $?) -eq 0 ]]; do sleep $sleep_period; done echo -n `date +%D.%k:%M:%S.%N` >> ${JEM_CALLS_LOG} 2>&1; echo ":[PROCESSING][PID: $_jempid] Current PID: $$. Action: $action Subaction: $subaction" >> ${JEM_CALLS_LOG} 2>&1; } ${SUDO_COMMAND} touch $jempidfile; ${SUDO_COMMAND} chmod 666 $jempidfile; echo "$$" > $jempidfile; } function stopJemThreadQueueManager(){ [ -f $jempidfile ] && { echo -n `date +%D.%k:%M:%S.%N` >> ${JEM_CALLS_LOG} 2>&1; echo ":[CLEANING UP][PID: $(cat $jempidfile 2>/dev/null)]" >> ${JEM_CALLS_LOG} 2>&1; ${SUDO_COMMAND} rm -f $jempidfile } } function sourceOverride(){ [ -d "$OVERRIDE_DIR" ] && [ $(ls -1 $OVERRIDE_DIR/*.lib 2>/dev/null | wc -l ) -gt 0 ] && { for override_lib in $(ls $OVERRIDE_DIR/*.lib) do source $override_lib done } [ -d "$CUSTOMIZATIONS_DIR" ] && [ $(ls -1 $CUSTOMIZATIONS_DIR/*.lib 2>/dev/null | wc -l ) -gt 0 ] && { for override_lib in $(ls $CUSTOMIZATIONS_DIR/*.lib) do source $override_lib done } } function _getTimeout() { while :; do case $1 in --timeout) [ "$2" ] && { value=$2 ; shift ; } ;; --timeout=?*) value=${1#*=} ;; *) [ -z "$1" ] && break esac shift done [ -z "$value" ] && return 1 echo $value return 0 } function doAction() { local action="${1##--}" modfile="" subaction="${2##--}" ; local _subaction="$subaction"; [[ -z ${action} ]] && die "Usage: doAction "; shift; shift; MANAGE_MODULE_NAME="${action}"; MANAGE_COMMAND="${MANAGE_PROGRAM_NAME} ${MANAGE_MODULE_NAME}"; [[ ${MANAGE_BINARY_NAME##*/} != ${MANAGE_PROGRAM_NAME} ]] && { MANAGE_COMMAND="${MANAGE_BINARY_NAME##*/}"; } declare -x result #default timeout for all actions is 1hr actiontimeout=3600 timeoutParam=$(_getTimeout $*) if [ $? -ne 0 ] ; then _moduleaction="${action}${subaction}" #read the timeouts from config file eval "$($SED -re '/^#/d;/^(\s*)$/d;s/(.*)/declare -x \L\1/g' '/etc/jelastic/timeouts.conf')" eval "actiontimeout_cfg=\$${_moduleaction}" if [ ! -z "${actiontimeout_cfg}" ] ; then actiontimeout="${actiontimeout_cfg}" fi else actiontimeout=$timeoutParam fi r=1 for arg do shift [[ "$arg" = "--timeout" ]] && { r=0; continue; } [[ "$arg" == --timeout=* ]] && continue [[ "$r" -eq 0 ]] && { r=1 ; continue ; } set -- "$@" "$arg" done modfile=$(_manageFindModule "${action}"); resultFile=$(mktemp) ACTION_STATE_FILE=$(mktemp) export resultFile timeout $actiontimeout cat <( source "$MANAGE_DEFAULT_ACTIONS" 2>/dev/null || die "Couldn't source ${MANAGE_DEFAULT_ACTIONS}"; source "${modfile}" 2>/dev/null || die "Couldn't source ${modfile}"; res=0 if isFunction "onModLoadCallback" ; then "onModLoadCallback" "$@"; res=$? fi if [[ $res -eq 0 ]] ; then [[ -z ${subaction} ]] && { checkDo "do${DEFAULT_ACTION:-usage}" "$@"; } || { isFunction "do${subaction}" || subaction=$(sed 's/\([a-zA-Z0-9]\)\([a-zA-Z0-9]*\)/\u\1\2/g' <<< ${subaction}); isFunction "do${subaction}" || subaction=$(declare -F | grep -iom 1 "${subaction}"); [[ -z ${subaction} ]] && { local message="Action ${_subaction} unknown"; >&2 echo "$message"; die -q "$message"; } # [[ "$@" == "help" ]] && { "showActionHelp" "$subaction"; return 0; } [[ "$@" == "help" ]] && { isFunction "showActionHelp" && "showActionHelp" "$subaction"; return 0; } sourceOverride; isFunction "pre${subaction}Callback" && "pre${subaction}Callback" "$@"; checkDo "do${subaction}" "$@"; result=$?; if [[ $result -eq 0 ]] ; then if isFunction "post${subaction}Callback" ; then "post${subaction}Callback" "$@" ; result=$?; fi fi [ -w "$resultFile" -a -n "$result" ] && { echo $result >$resultFile } exit $result } else echo "`date +%D.%k:%M:%S.%N`: $action:$subaction onModLoadCallback failed with result $res" >> ${JEM_CALLS_LOG}; [ -w "$resultFile" ] && echo "4181" >$resultFile fi ); [[ $? -eq 124 ]] && { actionResult="124"; local ppid=$! [ -z "$ppid" ] || { local ppids=$(pstree -p $ppid | tr -d '\n' | sed -re 's/[^0-9]/\ /g' -e 's/\s{2,}/ /g' ) [ -z "$ppids" ] || { echo -n `date +%D.%k:%M:%S.%N` >> ${JEM_CALLS_LOG} 2>&1; echo ":[KILLING] [PID: $$] Parrent PID is: $ppid. Killing after timeout $ppids" >> ${JEM_CALLS_LOG} 2>&1 kill -9 $ppids >>${JEM_CALLS_LOG} 2>&1 } } } [ -e "$resultFile" ] && { [ -z "${actionResult}" ] && actionResult=$(cat $resultFile ); rm -f "$resultFile"; } #call cleanup if onModLoadCallback has no errors if [[ "$actionResult" -ne 4181 ]] ; then source "${modfile}" 2>/dev/null || die "Couldn't source ${modfile}"; if isFunction "cleanup${subaction^}Callback" ; then "cleanup${subaction^}Callback" "$@" fi fi [ -e "$ACTION_STATE_FILE" ] && rm -f $ACTION_STATE_FILE echo -n `date +%D.%k:%M:%S.%N` >> ${JEM_CALLS_LOG} 2>&1; [[ $actionResult -gt 0 ]] && { echo ":[FAIL] [PID: $$]" >> ${JEM_CALLS_LOG} 2>&1; # stopJemThreadQueueManager; # [[ $actionResult -gt 5000 ]] && _manageDoHelp; [[ $actionResult -eq 124 ]] && writeJSONResponseErr "result=>4132" "message=>Execution timed out"; return ${actionResult}; }; echo ":[SUCCESS] [PID: $$]" >> ${JEM_CALLS_LOG} 2>&1; return 0; ## For cases if no other problems occured in doAction subshell } function inherit() { local x e; for x in "$@"; do [[ -e "${MANAGE_CORE_PATH}/${x}.lib" ]] || die "Couldn't find ${x}.lib"; include ${x}; done } function inherit_check() { local x e; for x in "$@"; do [[ -e "${MANAGE_CORE_PATH}/${x}.lib" ]] || return 1; inherit ${x}; done } function include() { local x e; for x in "$@"; do [[ -n "$(indexOf "${__CORE_INCLUDE_LIST[*]}" ${x})" ]] && continue; [[ -e "${MANAGE_CORE_PATH}/${x}.lib" ]] && { source "${MANAGE_CORE_PATH}/${x}.lib" || die "Couldn't source ${x}.lib"; } __CORE_INCLUDE_LIST=(${__CORE_INCLUDE_LIST[@]} ${x}); done } function inject() { local fn=$1 local address=$2 local code=$3 eval "$(declare -f "$fn" | sed -re "${address}${code}" )" } function trapError () { local err=$1 # error status local line=$2 # LINENO local linecallfunc=$3 local command="$4" local funcstack="$5" echo "<---" echo "ERROR: line $line - command '$command' exited with status: $err" echo echo "--->" }