#!/bin/sh

# Restarts enabled services when they are dead
# Version 0:2026.10.02
# Program development at projectes_publics/ctctl/reload-services_cron-hourly

Services="mariadb|mysqld apache2 dovecot asterisk named lxc-monitord tryton-server postgresql"
ServicesPerChilds="openvpn-server openvpn-client postfix"


# Program development ToDo:
#	- Legate Services_Report() behaviour from motd.sh
#	- Implement service:ports to check port listening instead of trusting system service check
#	- Implement test method for each service (eg. query DNS for named)

InstallationTarget=/etc/cron.hourly/reload-services
ConfigFile=/etc/cron.hourly/reload-services.conf

##### IMPORTED FUNCTIONS from @-funcions 0:2025.01.13 #####

ParO='(' ; ParC=')' ; Tab="$(printf '\t')"

Is_Executable ()
# Syntax as a function: Is_Executable $Command
# Description: Returns (exitcode 0) TRUE if specified command's argument is a directly available executable file, internal program or function; or FALSE otherwise.
# Use example (without brackets []):
#	if Is_Executable nano ; then echo "Program exists." ; fi
# Depends on functions: (none)
# Depends on software packages: (none)
{
	ie__Program="$1"
	ie__TrueCode=254  # 254=FALSE
	ie__TestValue=''
	
	ie__TestValue="$(command -v "$ie__Program" 2>/dev/null)"
	IsEmptyString () { WordsNr () { printf '%s' $#; }; return $(WordsNr $*); }
	if ! IsEmptyString "$ie__TestValue" ; then
		# Some scenarios "command" returns directories as executables.
		if [ ! -d "$ie__TestValue" ] || [ "$(printf '%s' "$ie__TestValue" | tr '/' ' ')" = "$ie__TestValue"  ] ; then
			ie__TrueCode=0
		fi
	fi
	return $ie__TrueCode
}

Is_IntegerNr ()
# Syntax as a function: Is_IntegerNr $Valor
# Description: Returns (exitcode 0) TRUE if specified value is an integer number, or FALSE otherwise. (IsNumeric/EsNumerico)
# Use example (without brackets []):
#	if Is_IntegerNr $Respuesta ; then echo "Correcto." ; fi
# Use example verifying also there is only 1 word:
#	if Is_IntegerNr "$Respuesta" ; then echo "Correcto." ; fi
# Notes:
#	- Only first parameter is evaluated. No matter next parameters contain.
# Depends on functions: (none)
# Depends on software packages: (none)
{
	iin__TestValue="$1"
	iin__TrueCode=254  # 254=FALSE
	iin__LastStatus=0
	
	iin__TestValue="$(expr "$iin__TestValue" : '[ ]*\(.*[^ ]\)[ ]*$')"	# Trim spaces
	if [ "$iin__TestValue" = "" ] ; then iin__TestValue='.' ; fi
	[ "$iin__TestValue" -eq "$iin__TestValue" ] > /dev/null 2>&1
	iin__LastStatus=$?
	if [ $iin__LastStatus -eq 0 ] ; then	iin__TrueCode=0 ; fi
	return $iin__TrueCode
}

Dirname ()
# Independent version of GNU dirname
# Useful for Busybox and other minimalistic environments
# Notes:
#	- Only allows 1 path specified
#	- Does not allow any option (such as -z or --version)
# Depends on functions: (none)
# Depends on software packages: (none)
{
	local Path="$1"
	local NrElement=0
	local TotalElements=0
	local CurrentElement=''
	local PreviousElement=''
	local Value=''
	
	if [ "$Path" != "" ] ; then
		if [ "$Path" != "/" ] ; then
			IFS="/" ; for CurrentElement in $Path ; do unset IFS
				NrElement=$((NrElement + 1))
				if [ $NrElement -gt 1 ] ; then
					if [ $NrElement -gt 2 ] ; then
						if [ "$Value" != "/" ] ; then Value="${Value}/" ; fi
					fi
					Value="${Value}${PreviousElement}"
				else
					if [ "$CurrentElement" = "" ] ; then Value="/" ; fi
				fi
				PreviousElement="$CurrentElement"
			done
			unset IFS
			if [ $NrElement -eq 1 ] && [ "$Value" = "" ] ; then Value="." ; fi
		else
			Value="/"
		fi
	else
		Value="."
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

ReadlinkF ()
# Old compatibility replacement for modern "readlink -f"
# "canonicalize by following every symlink in every component of the given name recursively; all but the last component must exist"
# Depends on functions: Dirname
# Depends on software packages: grep, sed
{
	local Element="$1"
	local PreviousDir=''
	local CurrentElement=''
	local CurrentPath=''
	local TmpElement=''
	local BaseElement=''
	local ReadlinkStatus=0
	local LastPath=''
	local CurStep=''
	local Value=''

	if [ "$Element" != "" ] ; then
		PreviousDir="$(pwd)"
		CurrentRelElement="$Element"
		if [ "$(ls "$CurrentRelElement" 2>/dev/null)" = "" ] && [ -d "$(Dirname "$CurrentRelElement")" ] ; then
			# Same as "readlink -f" behaviour: If element doesn't exist (but path yes), return as if existed.
			cd "$(Dirname "$CurrentRelElement")"
			Value="$(pwd | sed -e 's|/$||g')"
			Value="${Value}/$(printf '%s\n' "$CurrentRelElement" | tr -s '/' '\n' | tail -n 1)"
		fi
		while [ "$(ls "$CurrentRelElement" 2>/dev/null)" != "" ] ; do	# A broken link is not detected with -e
			if [ -d "$(Dirname "$CurrentRelElement")" ] ; then
				cd "$(Dirname "$CurrentRelElement")"
				CurrentPath="$(pwd)"
			else
				CurrentPath="$(Dirname "$CurrentRelElement")"
				if [ "$(printf '%s\n' "$CurrentPath" | grep -e '^/')" = "" ] ; then
					# Relative path
					LastPath="$(pwd)"
					IFS="/" ; for CurStep in $CurrentPath ; do unset IFS
						if [ "$CurStep" = ".." ] ; then
							LastPath="$(Dirname "$LastPath")"
						else
							if [ "$CurStep" = "." ] ; then
								LastPath="$LastPath"
							else
								if [ "$CurStep" = "" ] ; then
									LastPath="$LastPath"
								else
									LastPath="$(printf '%s\n' "$LastPath" | sed -e 's|/$||g')/${CurStep}"
								fi
							fi
						fi
					done
					CurrentPath="$LastPath"
				fi
				CurrentPath="$(printf '%s\n' "$CurrentPath" | sed -e 's|/$||g')"
			fi
			BaseElement="$(printf '%s\n' "$CurrentRelElement" | tr -s '/' '\n' | tail -n 1)"	# Problems with a path begun with "-" in old GNU basename versions
			if [ "$CurrentPath" = "/" ] ; then CurrentPath="" ; fi
			Value="${CurrentPath}/${BaseElement}"
			NextRelElement="$(readlink "$BaseElement" 2>/dev/null)"
			ReadlinkStatus=$?
			if [ "$NextRelElement" != "" ] ; then
				CurrentRelElement="$NextRelElement"
			else
				if [ $ReadlinkStatus -eq 0 ] || [ $ReadlinkStatus -eq 1 ] ; then
					# Old readlink (such as v1.13.3 in Potato debianutils) returns exitcode 1 if element is not a link.
					CurrentRelElement=""
				else
					NextRelElement="$(stat "$BaseElement" | grep -ie 'File:' | cut -f 4 -d '"')"	#'
					if [ "$NextRelElement" != "" ] ; then
						CurrentRelElement="$NextRelElement"
					else
						CurrentRelElement="$(stat "$BaseElement" | grep -ie 'File:' | cut -f 4 -d '"')"	#'
					fi
				fi
			fi
			if [ "$CurrentRelElement" != "" ] && [ "$(ls "$CurrentRelElement" 2>/dev/null)" = "" ] ; then
				# Broken link
				BaseElement="$(printf '%s\n' "$CurrentRelElement" | tr -s '/' '\n' | tail -n 1)"	# Problems with a path begun with "-" in old GNU basename versions
				CurrentPath="$(Dirname "$CurrentRelElement")"
				if [ "$(printf '%s\n' "$CurrentPath" | grep -e '^/')" = "" ] ; then
					# Relative path
					LastPath="$(pwd)"
					IFS="/" ; for CurStep in $CurrentPath ; do unset IFS
						if [ "$CurStep" = ".." ] ; then
							LastPath="$(Dirname "$LastPath")"
						else
							if [ "$CurStep" = "." ] ; then
								LastPath="$LastPath"
							else
								if [ "$CurStep" = "" ] ; then
									LastPath="$LastPath"
								else
									LastPath="$(printf '%s\n' "$LastPath" | sed -e 's|/$||g')/${CurStep}"
								fi
							fi
						fi
					done
					CurrentPath="$LastPath"
				fi
				CurrentPath="$(printf '%s\n' "$CurrentPath" | sed -e 's|/$||g')"
				Value="${CurrentPath}/${BaseElement}"
			fi
		done
		cd "$PreviousDir"
		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	fi
}

SystemMemTotal ()
# Syntax as a function: $(SystemMemTotal $Unit)
# Description: Returns (stdout) total usable RAM (i.e. physical RAM minus a few reserved bits and the kernel binary code)
# Expected parameters:
#	$1	(optional) Same as "free" command allows for unit: -b -k -m -g (bytes, KiB, MiB, GiB). Default is KiB.
# Notes:
#	- https://www.kernel.org/doc/html/latest/filesystems/proc.html?highlight=meminfo
# Depends on functions: Is_IntegerNr
# Depends on software packages: grep
{
	local Unit="$1"
	local Value=''
	Value="$(cat /proc/meminfo | grep -ie '^MemTotal:' | tr -s ' ' | cut -f 2 -d ' ')"
	if Is_IntegerNr "$Value" ; then
		case "$Unit" in
			"-b" ) printf '%s\n' $((Value * 1024)) ;;
			"-k" ) printf '%s\n' "$Value" ;;
			"-m" ) printf '%s\n' $((Value / 1024)) ;;
			"-g" ) printf '%s\n' $((Value / 1024 / 1024)) ;;
			"" )   printf '%s\n' "$Value" ;;
		esac
	fi
}

MemoriaRamDisponible ()
# Syntax as a function: $(MemoriaRamDisponible $Unit)
# Descripcio: Retorna la quantitat de bytes no ocupats per processos, és a dir, que es podrien arribar a utilitzar.
# Expected parameters:
#	$1	(optional) Same as "free" command allows for unit: -b -k -m -g (bytes, KiB, MiB, GiB)
#		Alternatively can be specified percent sign "%" to get integer (low rounded) percentage
# Depends on functions: Is_IntegerNr
# Depends on software packages: grep, sed, free/procps
{
	local Unit="$1"
	local Factor=1
	local SystemReport=''
	local Percent=''
	local MemTotal=''
	local Value=''
	local LastStatus=0
	
	if [ "$Unit" = "%" ] ; then
		Percent=1
		Unit=''
	fi
	free $Unit >/dev/null 2>&1
	LastStatus=$?
	if [ $LastStatus -ne 0 ] ; then
		# Buxybox's (v1) doesn't support unit parameter for integrated free command
		case "$Unit" in
			"-b" ) Factor=0 ;;
			"-k" ) Factor=1 ;;
			"-m" ) Factor=1024 ;;
			"-g" ) Factor=$((1024*1024)) ;;
		esac
		Unit=''
	fi
	SystemReport="$(env LANG=en free $Unit | tr -s '\t' ' ')"
	if [ "$(printf '%s\n' "$SystemReport" | grep -e 'buf.*cache.*avail')" != "" ] ; then
		# Columns format since procps-ng ~ 3.3.12
		Value="$(printf '%s\n' "$SystemReport" | grep -ie '^Mem:' | cut -f 2- -d ':' | sed -e 's|^ ||g' | cut -f 6 -d ' ')"
#		Value="$(printf '%s' $(SomeWord () { printf '%s' $6; }; SomeWord $Value))"
	else
		# Columns format until procps-ng ~ 3.3.10
		if [ "$(printf '%s\n' "$SystemReport" | grep -ie 'buf.*/cache:')" != "" ] ; then
			# Columns format at some implementations
			Value="$(printf '%s\n' "$SystemReport" | grep -ie 'buf.*/cache:' | cut -f 2- -d ':' | sed -e 's|^ ||g' | cut -f 2 -d ' ')"
#			Value="$(printf '%s' $(SomeWord () { printf '%s' $2; }; SomeWord $Value))"
		else
			# Columns format at some implementations
			Value="$(printf '%s\n' "$SystemReport" | grep -ie 'Mem:' | cut -f 2- -d ':' | sed -e 's|^ ||g' | cut -f 3 -d ' ')"
#			Value="$(printf '%s' $(SomeWord () { printf '%s' $3; }; SomeWord $Value))"
		fi
	fi
	if Is_IntegerNr $Value ; then
		if [ $Factor -eq 0 ] ; then
			Value=$((Value * 1024))
		else
			Value=$((Value / Factor))
		fi
		if [ "$Percent" = "1" ] ; then
			# Percent
			MemTotal="$(cat /proc/meminfo | grep -ie '^MemTotal:' | tr -s ' ' | cut -f 2 -d ' ')"
			MemTotal=$((MemTotal / 100))
			if [ $MemTotal -eq 0 ] ; then
				if [ $Value -gt 0 ] ; then
					echo 100
				else
					echo 0
				fi
			else
				echo $((Value / MemTotal))
			fi
		else
			# Amount
			printf '%s\n' "$Value"
		fi
	fi
}

NumeroResumit ()
# Syntax as a function: $(NumeroResumit $Numero $Estil)
# Retorna el número humanament llegible, segons $Estil:
# 	"2" :	Amb factors de 1024 (10,5b; 10,5k; 10,5m; 10,5g)
# 	"2L" :	Amb factors de 1024 i notació llarga (10,5B; 10,5KiB; 10,5MiB; 10,5GiB)
# 	"1" :	Amb factors de 1000 (10,5B; 10,5K; 10,5M; 10,5G) (SI)
# 	"." :	Utilitzar el separador decimal "." en comptes del predeterminat ","
#	Si s'afegeix un espai, se separa el número de la notació amb un espai:
#		"2 "   10,5 g
#		"2L "  9,5 MiB
#		"1 "   8300,5 B
#	Si s'afegeix una "A", aleshores s'alineen les dades:
#		"A2"     10,5g
#		"A2L"     9,5MiB
#		"A1"   8300,5B
#	Si s'afegeix un "0", aleshores no s'expressen decimals (només l'enter arrodonit a la baixa):
#		"02"  10g
#		"02L" 9MiB
#		"01"  8300B
#	"X" elimina notació:
#		"02X"  10
#		"01X"  8300
#	Tot es pot combinar:
#		"A02L "     9 MiB
#		"A01L "  8300 B
#		"2L."     9.5MiB
# Nota: el $Numero s'espera en bytes (octets)
# ToDo:
#	- Try to not depend on bc
# Depends on functions: (none)
# Depends on software packages: grep, bc
{
	local Numero=$1
	local Estil="$2"
	local LlindarResum=2000
	local DecimalSeparator=''
	local CurLocale=''
	local EnterVisible=''
	local DecimalVisible=''
	local Sufix=''
	local Factor=0
	
	if [ "$(printf '%s' "$Estil" | grep "\.")" != "" ] ; then DecimalSeparator="." ; fi
	if [ "$(printf '%s' "$Estil" | grep ",")" != "" ] ; then DecimalSeparator="," ; fi
	if [ "$(printf '%s' "$Estil" | grep "'")" != "" ] ; then DecimalSeparator="'" ; fi	#"
	if [ "$DecimalSeparator" = "" ] ; then
		DecimalSeparator=','
		unset IFS ; for CurLocale in "$LC_NUMERIC" "$LC_ALL" "$LANG" "$LANGUAGE" ; do
			CurLanguage="$(printf '%s' "$LC_NUMERIC" | cut -f 1 -d '_' | cut -f 1 -d '-')"
			if [ "$CurLanguage" = "en" ] || [ "$CurLanguage" = "EN" ] ; then
				DecimalSeparator='.'
			fi
		done
	fi
	if [ "$(printf '%s' "$Estil" | grep "2")" != "" ] ; then
		Factor=1024
	else
		if [ "$(printf '%s' "$Estil" | grep "1")" != "" ] ; then
			Factor=1000
		else
			return 1
		fi
	fi
	EnterVisible="$Numero"
	Sufix="B"
	# Resum
	if [ $Numero -ge $LlindarResum ] || [ $(printf '%s\n' "$Numero" | sed -e 's|.....$||g') -ge $LlindarResum ] ; then
		EnterVisible="$(echo "$Numero / $Factor" | bc)"
		DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
		Numero=$(echo "$Numero / $Factor" | bc)
		Sufix="K"
		if [ $Numero -ge $LlindarResum ] ; then
			EnterVisible="$(echo "$Numero / $Factor" | bc)"
			DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
			Numero=$(echo "$Numero / $Factor" | bc)
			Sufix="M"
			if [ $Numero -ge $LlindarResum ] ; then
				EnterVisible="$(echo "$Numero / $Factor" | bc)"
				DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
				Numero=$(echo "$Numero / $Factor" | bc)
				Sufix="G"
				if [ $Numero -ge $LlindarResum ] ; then
					EnterVisible="$(echo "$Numero / $Factor" | bc)"
					DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
					Numero=$(echo "$Numero / $Factor" | bc)
					Sufix="T"
					if [ $Numero -ge $LlindarResum ] ; then
						EnterVisible="$(echo "$Numero / $Factor" | bc)"
						DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
						Numero=$(echo "$Numero / $Factor" | bc)
						Sufix="P"
						if [ $Numero -ge $LlindarResum ] ; then
							EnterVisible="$(echo "$Numero / $Factor" | bc)"
							DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
							Numero=$(echo "$Numero / $Factor" | bc)
							Sufix="E"
							if [ $Numero -ge $LlindarResum ] ; then
								EnterVisible="$(echo "$Numero / $Factor" | bc)"
								DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
								Numero=$(echo "$Numero / $Factor" | bc)
								Sufix="Z"
								if [ $Numero -ge $LlindarResum ] ; then
									EnterVisible="$(echo "$Numero / $Factor" | bc)"
									DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
									Numero=$(echo "$Numero / $Factor" | bc)
									Sufix="Y"
								fi
							fi
						fi
					fi
				fi
			fi
		fi
	fi
	# Decimal invisible
	if [ "$(printf '%s' "$Estil" | grep "0")" != "" ] ; then
		DecimalVisible=''
	else
		if [ "$DecimalVisible" = "" ] ; then DecimalVisible="0" ; fi
		DecimalVisible="$DecimalSeparator$DecimalVisible"
	fi
	# Notació llarga
	if [ "$(printf '%s' "$Estil" | grep "2")" != "" ] ; then  # factor binari, sufix en minuscula
		case "$Sufix" in
			"B" )	Sufix="b"	;;
			"K" )	Sufix="k"	;;
			"M" )	Sufix="m"	;;
			"G" )	Sufix="g"	;;
			"T" )	Sufix="t"	;;
			"P" )	Sufix="p"	;;
			"E" )	Sufix="e"	;;
			"Z" )	Sufix="z"	;;
			"Y" )	Sufix="y"	;;
		esac
		if [ "$(printf '%s' "$Estil" | grep "L")" != "" ] ; then
			case "$Sufix" in
				"b" )	if [ "$(printf '%s' "$Estil" | grep "A")" != "" ] ; then
						Sufix="B  "	# Amb aliniació
					else
						Sufix="B"
					fi
					;;
				"k" )	Sufix="KiB"	;;
				"m" )	Sufix="MiB"	;;
				"g" )	Sufix="GiB"	;;
				"t" )	Sufix="TiB"	;;
				"p" )	Sufix="PiB"	;;
				"e" )	Sufix="EiB"	;;
				"z" )	Sufix="ZiB"	;;
				"y" )	Sufix="YiB"	;;
			esac
		fi
	fi
	# Sense notació
	if [ "$(printf '%s' "$Estil" | grep -e 'X')" != "" ] ; then
		Sufix=''
	fi
	# Espai
	if [ "$(printf '%s' "$Estil" | grep -e ' ')" != "" ] ; then
		Sufix=" $Sufix"
	fi
	# Aliniació
	if [ "$(printf '%s' "$Estil" | grep "A")" != "" ] ; then
		while [ ${#EnterVisible} -lt 4 ] ; do
			EnterVisible=" $EnterVisible"
		done
	fi
	printf '%s\n' "$EnterVisible$DecimalVisible$Sufix"
}


##### SPECIFIC FUNCTIONS TO THIS SCRIPT #####

Is_SystemProgramEnabled ()
{
	local TheProgram="$1"
	local TrueCode=254 # 254=FALSE
	if Is_Executable systemctl ; then
		if systemctl is-enabled $TheProgram >/dev/null 2>&1 ; then
			TrueCode=0
		fi
	else
		if Is_Executable $CurService ; then
			TrueCode=0
		fi
	fi
	return $TrueCode
}

Is_SystemProgramFailed ()
{
	local TheProgram="$1"
	local TrueCode=254 # 254=FALSE
	local SystemctlString=''
	local SystemctlFailedExitcode=''
	
	if Is_Executable systemctl ; then
		SystemctlString="$(systemctl is-failed $TheProgram 2>&1)"
		SystemctlFailedExitcode=$?
		if [ $SystemctlFailedExitcode -eq 0 ] ; then
			TrueCode=0
		else
			if Is_SystemProgramEnabled $TheProgram && [ "$SystemctlString" = "inactive" ] ; then
				# Probably killed (eg. OOM)
				TrueCode=0
			fi
		fi
	else
		if [ "$(ps -A o comm | grep -e "^${CurService}$")" = "" ] ; then
			TrueCode=0
#		else
#			echo 'running'
		fi
	fi
	return $TrueCode
}

RestartSystemProgram ()
{
	local TheService="$1"
	local LastStatus=0
	local StatusCode=0
	
	if Is_Executable systemctl ; then
		systemctl restart $TheService
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		service $CurService stop
		sleep 1
		service $CurService start
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

ServiceStatus ()
{
	local TheService="$1"
	local LastStatus=0
	local StatusCode=0
	
	if Is_Executable systemctl ; then
		systemctl status $TheService
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		service $CurService status
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

ApacheWarnings ()
{
	local MaxRequestWorkersFile='/etc/apache2/mods-enabled/mpm_prefork.conf'
	local MaxRequestWorkers=''
	local WorkersNr=''
	local WorkersWarn=''
	local AvailableRAM=''
	local TotalRAM=''
	
	MaxRequestWorkers="$(grep -ie '^MaxRequestWorkers' "$MaxRequestWorkersFile" 2>/dev/null | tr -s '\t' ' ' | cut -f 2 -d ' ')"
	if Is_IntegerNr "$MaxRequestWorkers" ; then
		WorkersWarn=$MaxRequestWorkers
		WorkersNr=$(ps -A -o comm | grep -e '^apache2' | wc -l)
		if [ $WorkersNr -ge $WorkersWarn ] ; then
			AvailableRAM=$(MemoriaRamDisponible -b)
			AvailableRAM="$(NumeroResumit $AvailableRAM '02L')"
			TotalRAM=$(SystemMemTotal -b)
			TotalRAM="$(NumeroResumit $TotalRAM '02L')"
			echo "W: $WorkersNr Apache2 workers with a limit of ${MaxRequestWorkers}. You should do something with MaxRequestWorkers at ${MaxRequestWorkersFile} to waiting visitors queue.\nI: Available memory is $AvailableRAM of a total of $TotalRAM at this time." | mail -s "${WorkersNr}/${MaxRequestWorkers} Apache2 workers" $NotifyTo
		fi
	fi
}

MariadbWarnings ()
{
	local MaxConnectionsFile=''
	local MaxConnections=''
	local ThreadsConnected=''
	local ThreadsWarn=90  # Percent
	local AvailableRAM=''
	local TotalRAM=''
	
	MaxConnectionsFile="$(grep -Isirnce '^max_connections.*=.*[0-9]' /etc/mysql/* | grep -ve ':0$' | sort | sed -e 's|:[0-9*$]||g')"
	if [ -f "$MaxConnectionsFile" ] ; then
		MaxConnections="$(cat "$MaxConnectionsFile" | grep -ie '^max_connections.*=.[0-9]' | tail -n 1 | cut -f 2 -d '=')"
		MaxConnections="$(echo TrimAndSingle $MaxConnections | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	fi
	if Is_IntegerNr "$MaxConnections" ; then
		ThreadsWarn=$((MaxConnections * ThreadsWarn / 100))
		ThreadsConnected="$(mysql --quick --silent --skip-column-names --protocol=SOCKET -e 'SHOW STATUS WHERE Variable_name = "Threads_connected";' | tr -s '\t' ' ' | cut -f 2 -d ' ')"
		if Is_IntegerNr "$ThreadsConnected" ; then
			if [ $ThreadsConnected -ge $ThreadsWarn ] ; then
				AvailableRAM=$(MemoriaRamDisponible -b)
				AvailableRAM="$(NumeroResumit $AvailableRAM '02L')"
				TotalRAM=$(SystemMemTotal -b)
				TotalRAM="$(NumeroResumit $TotalRAM '02L')"
				echo "W: $ThreadsConnected MariaDB/MySQL threads with a limit of ${MaxConnections}. You should do something with max_connections at ${MaxConnectionsFile} to avoid database connection errors.\nI: Available memory is $AvailableRAM of a total of $TotalRAM at this time." | mail -s "${WorkersNr}/${MaxRequestWorkers} MariaDB connections" $NotifyTo
			fi
		fi
	fi
}

SpecialWarnings ()
{
	if Is_SystemProgramEnabled apache2  ; then
		ApacheWarnings
	fi
	if Is_SystemProgramEnabled mariadb || Is_SystemProgramEnabled mysqld ; then
		MariadbWarnings
	fi
}
##### MAIN SCRIPT #####

LastStatus=0 ; StatusCode=0
if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
PreviousDir="$(pwd)"
MeCallFile="$0"
MeDir="$(Dirname "$MeCallFile")"
cd "$MeDir"
MeDir="$(pwd)"
cd "$PreviousDir"
MeCallFile="${MeDir}/$(printf '%s\n' "$MeCallFile" | tr -s '/' '\n' | tail -n 1)"
MeExecutable="$(ReadlinkF "$MeCallFile")"

if [ $(id -u) -ne 0 ] ; then
	printf '%s\n' "${sERROR}E: This program needs to be run with superuser ${ParO}root${ParC} permissions.${fRESET}" 1>&2
	exit 45
fi

if [ "$1" = "install" ] ; then
	if [ "$0" != "$InstallationTarget" ] ; then
		echo "Installing as: $InstallationTarget"
		cp "$0" "$InstallationTarget"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		chown root:root "$InstallationTarget"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		chmod u=rwx,go=r "$InstallationTarget"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ ! -f "$ConfigFile" ] ; then
			printf '%s\n' "#NotifyTo=sysadmin@example.net" > "$ConfigFile"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		echo "Configurations at: $ConfigFile"
		exit $StatusCode
	else
		printf '%s\n' "${sERROR}E: Can't install to itself.${fRESET}" 1>&2
		exit 1
	fi
fi

if [ "$1" = "uninstall" ] ; then
	echo '$' rm -f "$InstallationTarget"
	rm -f "$InstallationTarget"
	exit $?
fi

if [ ! -f "$ConfigFile" ] ; then
	printf '%s\n' "#NotifyTo=sysadmin@example.net" > "$ConfigFile"
fi
NotifyTo="$(cat "$ConfigFile" | grep -ie '^NotifyTo=' -ie '^NotifyTo =' | cut -f 2 -d '=')"

unset IFS ; for CurService in $ServicesPerChilds ; do
	Services="$Services $(systemctl 2>&1 | tr -s ' ' | sed -e 's|^ ||g' -e 's|^. ||g' | cut -f 1 -d ' ' | grep -e "${CurService}@..*.service$" | sed -e 's|\.service$||g')"
done
unset IFS ; for CurService in $Services ; do
	OptionFound=0
	IFS='|' ; for CurService_Option in $CurService ; do unset IFS
		if [ $OptionFound -eq 0 ] && Is_SystemProgramEnabled $CurService_Option ; then
			OptionFound=1
			printf "${CurService_Option}: "
			if Is_SystemProgramFailed $CurService_Option ; then
				CurStatus="$(systemctl status $CurService_Option | tr -s ' ' | sed -e 's|^ ||g' | grep -ie '^Active:' | cut -f 2 -d ':' | cut -f 1 -d '(' | sed -e 's|^ ||g' -e 's| $||g')"
				printf "${CurStatus}. Trying to restart... "
				FailedLog="$(ServiceStatus $CurService_Option 2>&1)"
				RestartSystemProgram $CurService_Option
				printf "${CurStatus}. Waiting 30s to check"
				Counter=15
				while [ $Counter -gt 0 ] ; do
					sleep 2
					printf '.'
					Counter=$((Counter - 1))
				done
				if [ "$NotifyTo" != "" ] ; then
					if Is_SystemProgramFailed $CurService_Option ; then
						printf " Failed. Sending report to ${NotifyTo}... "
						echo "$FailedLog" | mail -s "Service $CurService_Option down" $NotifyTo
						LastStatus=$?
						if [ $LastStatus -eq 0 ] ; then
							echo "Mail sent."
						else
							echo "Mail failed."
						fi
					else
						printf " Succeeded. Sending report to ${NotifyTo}... "
						NewLog="$(ServiceStatus $CurService_Option 2>&1)"
						printf "PREVIOUS STATUS:\n\n${FailedLog}\n\nAFTER RESTARTING IT, SERVICE SEEMS TO RUN AGAIN:\n\n${NewLog}" | mail -s "Service $CurService_Option restarted" $NotifyTo
						LastStatus=$?
						if [ $LastStatus -eq 0 ] ; then
							echo "Mail sent."
						else
							echo "Mail failed."
						fi
					fi
				else
					if Is_SystemProgramFailed $CurService_Option ; then
						echo " Failed."
					else
						echo " Succeeded."
					fi
					printf '%s\n' "W: No notification e-mail configured at $ConfigFile" 1>&2
				fi
			else
				CurStatus="$(systemctl status $CurService_Option | tr -s ' ' | sed -e 's|^ ||g' | grep -ie '^Active:' | cut -f 2 -d '(' | cut -f 1 -d ')')"
				if [ "$CurStatus" = "exited" ] ; then
					CurStatus="$(systemctl status $CurService_Option | tr -s ' ' | sed -e 's|^ ||g' | grep -ie '^Active:' | cut -f 2 -d ':' | cut -f 1 -d '(' | sed -e 's|^ ||g' -e 's| $||g')"
				fi
				echo "$CurStatus"
			fi
		fi
	done
done
SpecialWarnings

exit $StatusCode

