#!/bin/sh
### BEGIN INIT INFO
# Provides:	dhlan
# Required-Start:	$network
# Should-Start:	$syslog
# Required-Stop:	$network
# Should-Stop:	$syslog
# Default-Start:	2 3 4 5
# Default-Stop:	0 1 6
# Short-Description:	Dynamic hosts in LAN
# Description:		Live updates to /etc/hosts with a MAC:name table
# X-Interactive: false
### END INIT INFO
# RHL fields:
# chkconfig: 2345 80 30
# description: Dynamic hosts in LAN

# Service version: 1.5.12
# Copyright: (GNU GPL) 2015-2025 Narcis Garcia

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# .
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# .
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/

# Init script template version: 2.7.16
# Copyright: (GNU GPL) 2014-2025 Narcis Garcia
# Compatible with init systems: System-V since y.2000, upstart, systemd

# Service development at projectes_publics/dhlan/dhlan
# Software releases can be downloaded from: https://...##

# Service development ToDo:
#	- Option for "scan" --os or something to exclude deep analysis from default bahaviour and make it an explicit option
#	  Alternative: Option for "scan" --fast or something to avoid deep analysis
#	- Review if we are still digging cache; if not, use MacFromIp() before digging cache
#	- Quan la xarxa té molts dispositius (ex:pararols) i alguns han canviat d'IP, poden quedar incoherències a /etc/hosts mentre no s'ha completat tot el rastreig i actualització. Exemple:
#		192.168.1.101 pavilion.lan
#		192.168.1.102 zip.lan
#		192.168.1.101 tipsa.lan
#		^ «pavilion» ja s'ha actualitzat però fins que no s'hagi det tota l'avaluació no s'actualitzarà «tipsa» a la seva nova IP, i en un cas així hi ha incoherències d'accessos (VNC, ssh, etc.).
#		Una possible solució pot ser que en comptes de fer totes les tasques en una ronda hi hagi 2 nivells de rastreig: a base de cache (arp) i a base d'anàlisi (nmap?)
#		aleshores sempre començar amb una ronda ràpida sense anàlisi (només fping i cache), i després fer la ronda detallada. Així a la majoria de casos es guanyaria velocitat d'actualització.
#	- Update manpage to repository
#	- "status" should return each host status {Reachable | Last IP | Name}
#	- "getmac" and "getip" actions, to resolve now mac<->ip
#	- "--feed" option for "scan" action, to write new found MACs to /etc/ethers
#	- run-parts to /etc/dhlan/events.d/ parsing parameters: $event $name $mac $ip
#	  Examples: /etc/dhlan/events.d/myscript found myserver.lan 00:11:22:33:44:55 192.168.1.5
#	            /etc/dhlan/events.d/myscript lost myserver.lan 00:11:22:33:44:55 192.168.1.5
#	- Sure to allow shared name between 2 MACs: p.e. to point to a notebook with alternating wlan/eth but prioritizing eth when present.


##### PARAMETERS ABOUT SERVICE NATURE #####

# ServiceName: Brief and compact code name that needs to be unique in the software world. Will be used for filenames and some directories.
ServiceName="dhlan"
# DependsOnSoftware: Space separated list of required programs. Each item's syntaxes: executable/package ExecutableOption1:ExecutableOption2/PackageOption1|PackageOption2...
DependsOnSoftware="cut/coreutils|debianutils grep sed perl/perl-base bc pstree/psmisc start-stop-daemon:daemonize:screen/dpkg|daemonize|screen top/procps ip:ifconfig/iproute|iproute2|net-tools|netbase nmap:fping/nmap|fping arp/net-tools mii-tool/net-tools"
RecommendedSoftware="wakeonlan ip:brctl/iproute2|bridge-utils"
SuggestedSoftware=""
SystemConfigDir="/etc/${ServiceName}"
# ProgramInstaller: If integrated install/uninstall functions will be available (1) or not (0)
ProgramInstaller=1
# DisabledAtFirst: If installer will disable the loading as a system service at first installation (1=Yes 0=No)
DisabledAtFirst=1
# DaemonizeOnStart: If main program will fork in a background process on start and return control inmediately (1), or will run in the foreground and return control when completed (0)
DaemonizeOnStart=1
# MainControllerStays: If main program remains running until is ordered to stop (1), or ends itself when one-shot task is completed (0)
MainControllerStays=1
# StopStartOnUpgrade: If old service process is to be stopped when upgrading program (and after started again if it was running)
StopStartOnUpgrade=1
# RootRequired: Prevent to run without superuser permissions? (1=Yes 0=No). This also determines program FHS location (sbin/ or bin/).
RootRequired=0
# TimeoutStartSec: When starting main program, if it doesn't complete ond/or fork, after this number of seconds systemd/upstart will shut down the service. -1 for infinite (recommended on MainControllerStays==0)
TimeoutStartSec=-1
# TimeoutStopSec: When stopping service, if it doesn't complete, after this number of seconds systemd will kill main process. -1 for infinite
TimeoutStopSec=30
# GentleStopTimeoutS: Maximum seconds to wait daemons at stop signal, before forcing kill processes. Intended for childs and {main x ChildsNr}
GentleStopTimeoutS=2
# StopOnFailure: Allow (1) Systemd to call "stop" service on start failure, or let service remain as is (0)
StopOnFailure=0
# Leave empty to not treat child objects
ChildSingularName=""
# Leave empty to not treat child objects. Plural name has implies profiles directory names.
ChildsPluralName=""
# ChildProfileExtension: Leave empty to assume profiles in subdirectories instead of files
ChildProfileExtension="conf"
# DaemonizeChilds: If child objects are background processes (1) or are to run serialized tasks in the foreground (0)
DaemonizeChilds=1
# ServiceDocumentation: A space-separated list of URIs as http:// https:// file: info: man:
ServiceDocumentation="https://git.actiu.net/libre/dhlan"
# Configuration default value for _LoadWithCalm
Child_LoadWithCalm_Default=-1
# Configuration default value for _MoreLoadsInterval
Childs_MoreLoadsInterval_Default=0
# If "status" action shows (1) processes and memory state or not (0). If not, only customized Status_More and ProcessStatus_More functions will be called.
StatusProcecessShow=1
# DataIsPublic: If Configuration and logs can be read by others (1) or not (0)
DataIsPublic=1
# LogRotation: Logrotate frequency to setup (see man logrotate). Empty to not configure rotation of log files.
LogRotation="size 1M"

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

ParO='(' ; ParC=')' ; Tab="$(printf '\t')"
if [ "$TERM" != "" ] ; then
	perl -e "exit${ParO}-p STDOUT ? 1 : 0${ParC};"
	RedirectedStdout=$?
fi
if [ "$TERM" != "" ] && [ $RedirectedStdout -eq 0 ] ; then
	# TO DO: Replace all tput calls by printf. Many documentation tell how to escape through "echo -e"
	# https://stackoverflow.com/questions/2924697/how-does-one-output-bold-text-in-bash
	# https://gist.github.com/izabera/9903f9d942e2667ef2cb
	cbDGRAY="$(tput setab 0)"
	cbRED="$(tput setab 196)"
	cbGREEN="$(tput setab 46)"
	cbCBROWN="$(tput setab 3)"
	cbVIOLET="$(tput setab 5)"
	cbBLUEGREEN="$(tput setab 6)"
	cbCGRAY="$(tput setab 7)"
	cbGRAY="$(tput setab 8)"
	cbCGREEN="$(tput setab 10)"
	cbCYELLOW="$(tput setab 11)"  # Darker
	cbYELLOW="$(tput setab 226)"
	cbCBLUE="$(tput setab 12)"
	cbCVIOLET="$(tput setab 13)"
	cbCYAN="$(tput setab 51)"
	cbWHITE="$(tput setab 15)"
	cbBLACK="$(tput setab 16)"
	cbBLUE="$(tput setab 21)"
	cbPINK="$(tput setab 26)"
	
	cfDGRAY="$(tput setaf 0)"
	cfRED="$(tput setaf 196)"
	cfGREEN="$(tput setaf 46)"
	cfCBROWN="$(tput setaf 3)"
	cfVIOLET="$(tput setaf 5)"
	cfBLUEGREEN="$(tput setaf 6)"
	cfCGRAY="$(tput setaf 250)"
	cfGRAY="$(tput setaf 8)"
	cfCGREEN="$(tput setaf 10)"
	cfCYELLOW="$(tput setaf 11)"  # Darker
	cfYELLOW="$(tput setaf 226)"
	cfCBLUE="$(tput setaf 12)"
	cfCVIOLET="$(tput setaf 13)"
	cfCYAN="$(tput setaf 51)"
	cfWHITE="$(tput setaf 15)"
	cfBLACK="$(tput setaf 16)"
	cfBLUE="$(tput setaf 21)"
	cfPINK="$(tput setaf 26)"
	
	fBOLD="$(tput bold)"
	fBOLD_="$(printf '\033[22m')"  # not bold	https://stackoverflow.com/questions/15579739/in-an-xterm-can-i-turn-off-bold-or-underline-without-resetting-the-current-colo
	fREVERSEC="$(tput rev)"
	fLOW="$(tput dim)"
	fLOW_="$(printf '\033[22m')"  # not dark
	fITALIC="$(tput sitm)"
	fITALIC_="$(printf '\033[23m')"  # not italic
	fUNDERL="$(tput smul)"
	fUNDERL_="$(printf '\033[24m')"  # not underline
	fUNDERLx="$(tput rmul)"  # not underline
#	fNOSTANDOUT="$(tput rmso)"  # No bold, no lines  # Not working in GNU/Linux
#	fNOSTANDOUT="$(echo -e '\033[0m' | sed -e 's|-e||')"  # No bold, no lines, no italic, etc. # Dash echo is buggy with escaped sequences.
#	fNOSTANDOUT="$(/bin/echo -e '\033[0m')"  # Workaround to use Bash if installed
	fNOSTANDOUT="$(printf '\033[0m')"  # No bold, no lines, no italic, etc. # Dash echo is buggy with escaped sequences.
	fBELL="$(tput bel)"
	fRESET="$(tput sgr0)"
	
	sPROMPT="${fBELL}${cfWHITE}${cbDGRAY}${fBOLD}"
	sHEAD0="${cfCYAN}${cbBLACK}${fBOLD}${fUNDERL}"
	sHEAD1="${cfCYAN}${cbDGRAY}${fBOLD}"
	sWARN="${cfYELLOW}${cbDGRAY}${fBOLD}"
	sERROR="${fBELL}${cfRED}${cbBLACK}${fBOLD}"
	sINFO="${cfWHITE}${cbDGRAY}"
	sPROGRESS="${cfCGREEN}${cbDGRAY}"
	sVALUE="${cfVIOLET}${cbDGRAY}${fBOLD}"
	sGOOD="${cfWHITE}${cbBLUEGREEN}${fBOLD}"
	sGOODEM="$sGOOD"
	sGOODNORMAL="${cbBLACK}${cfGREEN}${fBOLD}"
	sDISABLED="${fLOW}"
else
	cbDGRAY=''
	cbRED=''
	cbGREEN=''
	cbCBROWN=''
	cbVIOLET=''
	cbBLUEGREEN=''
	cbCGRAY=''
	cbGRAY=''
	cbCGREEN=''
	cbCYELLOW=''
	cbYELLOW=''
	cbCBLUE=''
	cbCVIOLET=''
	cbCYAN=''
	cbWHITE=''
	cbBLACK=''
	cbBLUE=''
	cbPINK=''
	
	cfDGRAY=''
	cfRED=''
	cfGREEN=''
	cfCBROWN=''
	cfVIOLET=''
	cfBLUEGREEN=''
	cfCGRAY=''
	cfGRAY=''
	cfCGREEN=''
	cfCYELLOW=''
	cfYELLOW=''
	cfCBLUE=''
	cfCVIOLET=''
	cfCYAN=''
	cfWHITE=''
	cfBLACK=''
	cfBLUE=''
	cfPINK=''
	
	fBOLD=''
	fREVERSEC=''
	fLOW=''
	fUNDERL=''
	fUNDERLx=''
	fNOSTANDOUT=''
	fRESET=''
	
	sPROMPT=''
	sHEAD0=''
	sHEAD1=''
	sWARN=''
	sERROR=''
	sINFO=''
	sPROGRESS=''
	sVALUE=''
	sGOOD=''
	sGOODEM=''
	sGOODNORMAL=''
	sDISABLED=''
fi

WhereProgram ()
# Syntax as a function: $(WhereProgram $Command)
# Description: Returns existing program filepath from specified command, or internal shell program or function
# Notes:
#	- Similar to the debianutils' "which" but also walks through typical OS binaries paths.
# Depends on functions: (none)
# Depends on software packages: (none)
{
	wp__Program="$1"
	wp__CurDir=''
	wp__Value=''
	
	wp__Value="$(command -v "$wp__Program" 2>/dev/null)"
	if [ "$wp__Value" = "" ] && [ "$(printf '%s' "$wp__Program" | tr '/' ' ')" = "$wp__Program"  ] ; then
		# Walk through typical path
		if [ $(id -u) -eq 0 ] ; then
			unset IFS ; for wp__CurDir in /root/.local/bin /usr/local/bin /usr/local/sbin /usr/bin /usr/sbin /bin /sbin /system/bin /system/xbin ; do
				if [ "$wp__Value" = "" ] && [ -x "{wp__CurDir}/${wp__Program}" ] ; then
					wp__Value="{wp__CurDir}/${wp__Program}"
				fi
			done
		else
			unset IFS ; for wp__CurDir in ~/.local/bin /usr/local/bin /usr/bin /bin /usr/local/sbin /usr/sbin /sbin /system/bin /system/xbin ; do
				if [ "$wp__Value" = "" ] && [ -x "{wp__CurDir}/${wp__Program}" ] ; then
					wp__Value="{wp__CurDir}/${wp__Program}"
				fi
			done
		fi
	fi
	if [ "$wp__Value" != "" ] ; then
		# Some scenarios "command" returns directories as executables.
		if [ -d "$wp__Value" ] && [ "$(printf '%s' "$wp__Value" | tr '/' ' ')" != "$wp__Value"  ] ; then wp__Value='' ; fi
	fi
	if [ "$wp__Value" != "" ] ; then
		printf '%s\n' "$wp__Value"
	fi
}

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
}

PrepareExitcodeToFile ()
# Syntax as a sentence: PrepareExitcodeToFile
# Description: Prepares environment variables to catch exitcode from a file. Useful when pipelining functions output. Supports cascade calls and recursion.
# NOTES:
#	- Use this function just before desired execution. It's expected that execution runs some function that saves exitcode to $NozeroStatusToFile file.
#	- If called execution does not generate any file. Exitcode 0 will be assumed.
#	- Exports done in a subshell $(...) do not survive out of it, but most of times this technique works to catch exitcode from subshell executions.
{
	petf__LastStatus=0
	petf__LegatedIndex=0
	petf__CurLegatedName=''
	if [ "$NozeroStatusToFile" != "" ] ; then
		petf__CurLegatedName="NozeroStatusToFile_Legated_${petf__LegatedIndex}"
		while [ "$(eval "printf '%s' \"\$$petf__CurLegatedName\"")" != "" ] ; do
			petf__LegatedIndex=$((petf__LegatedIndex + 1))
			petf__CurLegatedName="NozeroStatusToFile_Legated_${petf__LegatedIndex}"
		done
		eval "$petf__CurLegatedName=\"$NozeroStatusToFile\""
		NozeroStatusToFile=''
	fi
	if [ -d "/run/shm" ] ; then
		NozeroStatusToFile="$(mktemp -u -q -p /run/shm 2>/dev/null)"
		petf__LastStatus=$?
	else
		NozeroStatusToFile="$(mktemp -u 2>/dev/null)"
		petf__LastStatus=$?
	fi
	if [ $petf__LastStatus -ne 0 ] ; then
		NozeroStatusToFile="$(mktemp 2>/dev/null)"
		petf__LastStatus=$?
	fi
	if [ $petf__LastStatus -ne 0 ] || [ "$NozeroStatusToFile" = "" ] ; then
		NozeroStatusToFile=/tmp/exitcode.$$
	fi
	export NozeroStatusToFile="$NozeroStatusToFile"
}

GetFiledExitcode ()
# Syntax as a sentence: GetFiledExitcode $?
# Description: Prepares environment variables to catch exitcode from a file. Useful when pipelining functions output. Supports cascade calls and recursion.
# NOTES:
#	- Use this function just after desired execution. It's expected that execution runs some function that saves exitcode to $NozeroStatusToFile file.
#	- If called execution does not generate any file. Exitcode 0 is assumed.
#	- This also accumulates pipelining exitcode.
#	- Exports done in a subshell $(...) do not survive out of it, but most of times this technique works to catch exitcode from subshell executions.
# Depends on functions: Is_IntegerNr
# Depends on software packages: (none)
{
	gfe__LastStatus="$1"
	gfe__StatusCode=0
	gfe__LegatedIndex=0
	gfe__CurLegatedName=''
	gfe__LastLegatedName=''
	gfe__LastLegatedValue=''
	
	# Get value
	gfe__StatusCode=$(cat "$NozeroStatusToFile" 2>/dev/null)
	if ! Is_IntegerNr "$gfe__StatusCode" ; then gfe__StatusCode=0 ; fi
	rm -f "$NozeroStatusToFile"
	if [ $gfe__StatusCode -eq 0 ] && Is_IntegerNr "$gfe__LastStatus" ; then gfe__StatusCode=$gfe__LastStatus ; fi
	# Pop legacies array stack
	gfe__CurLegatedName="NozeroStatusToFile_Legated_${gfe__LegatedIndex}"
	while [ "$(eval "printf '%s' \"\$$gfe__CurLegatedName\"")" != "" ] ; do
		gfe__LastLegatedName="$gfe__CurLegatedName"
		gfe__LegatedIndex=$((gfe__LegatedIndex + 1))
		gfe__CurLegatedName="NozeroStatusToFile_Legated_${gfe__LegatedIndex}"
	done
	if [ "$gfe__LastLegatedName" != "" ] ; then
		eval "gfe__LastLegatedValue=\"\$$gfe__LastLegatedName\""
	fi
	if [ "$gfe__LastLegatedValue" != "" ] ; then
		export NozeroStatusToFile="$gfe__LastLegatedValue"
	else
		if [ "$(help export 2>/dev/null)" ] ; then
			# Bashism to remove variable
			export -n NozeroStatusToFile
		else
			# Other POSIX store empty variables
			export NozeroStatusToFile=
		fi
	fi
	return $gfe__StatusCode
}

NuevoTemporalSeguro ()
# Sintaxis como función: $(NuevoTemporalSeguro "$NombreBase" "$Extension" "$DirTemp")
# Descripción:
#	Crea y devuelve (stdout) la ruta de un fichero de tipo
#	/run/shm/usuario/NombreBase/20120821-091624.qBG8.ext con permisos rwX------
#	/tmp/usuario/NombreBase/20120821-091624.qBG8.ext con permisos rwX------
# Parámetros esperados:
#	$1	(opcional) Etiqueta ilustrativa
#	$2	(opcional) Extensión a añadir al nombre del fichero
#	$3	(opcional) Directorio-madre preferido en lugar de /run/shm o /tmp
# Notas:
#	- Si necesita ejecutar el fichero, debe evitar su creación en puntos como /run (noexec)
#	  especificando DirTemp
#	- Tambien es útil porque salva la falta de opciones --xx de versiones antiguas de mktemp
#	- Para directorios es mejor usar: TempName="${DirTemp}/${ProgramName}.$(id -un).$$.funcion" ; MkdirPP "$TempName" '' u=rw,g=r,o=
{
	local NombreBase="$1"
	local Extension="$2"
	local DirTemp="$3"
	local RutaDir=''
	local Tiempo=''
	local Valor=''
	local ModernTool=''
	local LastStatus=0
	local StatusCode=0
	
	RutaDir="${DirTemp}/$(id -un)"
	if [ "$NombreBase" != "" ] ; then
		RutaDir="${RutaDir}/${NombreBase}"
	fi
	mkdir -p "$RutaDir"
	chmod u=rwX,go= "${DirTemp}/$(id -un)"
	Tiempo="$(date +'%Y%m%d-%H%M%S')"
#	Tiempo="$(printf '%(%Y%m%d-%H%M%S)T')"  BASHISM
	if [ "$Extension" = "" ] ; then
		Valor="$(env TMPDIR="$RutaDir" mktemp -t ${Tiempo}.XXXX)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		ModernTool="$(mktemp --help 2>&1 | grep -e "--suffix")"
		if [ "$ModernTool" != "" ] ; then
			Valor="$(env TMPDIR="$RutaDir" mktemp -t ${Tiempo}.XXXX --suffix=.${Extension})"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			Valor="$(env TMPDIR="$RutaDir" mktemp -t ${Tiempo}.XXXX)"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				mv "$Valor" "${Valor}.${Extension}"
				Valor="${Valor}.${Extension}"
			fi
		fi
	fi
	if [ "$Valor" != "" ] ; then
		if [ -f "$Valor" ] ; then
			printf '%s\n' "$Valor"
		fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

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"
}

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
}

Lowercase ()
# Description: Converts specified string to lower letter case, using found tools.
# Depends on functions: Is_Executable
# Depends on software packages: grep, tr|awk|sed
{
	local OriginalString="$1"
	
	if [ "$OriginalString" != "" ] ; then
		# Busybox tr has no upper/lower conversion
		if Is_Executable tr && [ "$(tr --help 2>&1 | grep -e ':lower:')" != "" ] ; then
			printf '%s\n' "$OriginalString" | tr '[:upper:]' '[:lower:]'
		else
			if Is_Executable awk ; then
				printf '%s\n' "$OriginalString" | awk '{print tolower($0)}'
			else
				if Is_Executable sed && [ "$(sed --help 2>&1 | grep -e 'GNU')" != "" ] ; then
					printf '%s\n' "$OriginalString" | sed -re 's/([[:upper:]]?)/\L\1/g'
				fi
			fi
		fi
	fi
}

Uppercase ()
# Description: Converts specified string to upper letter case, using found tools.
# Depends on functions: Is_Executable
# Depends on software packages: grep, tr|awk|sed
{
	local OriginalString="$1"
	
	if [ "$OriginalString" != "" ] ; then
		# Busybox tr has no upper/lower conversion
		if Is_Executable tr && [ "$(tr --help 2>&1 | grep -e ':lower:')" != "" ] ; then
			printf '%s\n' "$OriginalString" | tr '[:lower:]' '[:upper:]'
		else
			if Is_Executable awk ; then
				printf '%s\n' "$OriginalString" | awk '{print toupper($0)}'
			else
				if Is_Executable sed && [ "$(sed --help 2>&1 | grep -e 'GNU')" != "" ] ; then
					printf '%s\n' "$OriginalString" | sed -re 's/([[:lower:]]?)/\U\1/g'
				fi
			fi
		fi
	fi
}

LogToFile ()
# Syntax as a sentence: LogToFile $LogFile $DatetimeStamp $MaxLogLines $Message
# Description: Appends message to text file
# Expected parameters:
#	$1	File path to write to
#	$2	"0" to not prepend line with date-time stamp. Any other value (1) to yes write it.
#	$3	"0" to infinite log lines or a greater number to leave limited log tail
#	(rest)	String for stdout/stderr/log
#		Escaped \n are replaced by line breaks.
# Notes:
#	- This function can be called without message to only cut file to last $MaxLogLines
# Depends on functions: Is_IntegerNr Dirname
# Depends on software packages: sed
# Supports environment variables: LogTime
{
	local LogFile="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local DatetimeStamp="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local MaxLogLines="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local AskedText=''
	local TextLinesNr=0
	local CurString=''
	local LoggingLog=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$LogFile" != "" ] ; then
		if [ "$DatetimeStamp" = "0" ] ; then
			DatetimeStamp=''
		else
#			DatetimeStamp="$(printf '%(%Y-%m-%dT%T%z)T')"  BASHISM
			if Is_IntegerNr "$LogTime" ; then
				DatetimeStamp="$(date -d @$LogTime '+%Y-%m-%dT%T%z') "
			else
				DatetimeStamp="$(date '+%Y-%m-%dT%T%z') "
			fi
		fi
		unset IFS ; for CurString in "$@" ; do	# Simple $* sometimes results in comma-separated words
			if [ "$AskedText" != "" ] ; then AskedText="${AskedText} " ; fi
			AskedText="${AskedText}$(printf '%s\n' "$CurString" | sed -e 's|\\n|\n|g')"
		done
		if [ "$AskedText" != "" ] ; then
			TextLinesNr=$(printf '%s\n' "$AskedText" | wc -l)
		else
			TextLinesNr=0
		fi
		mkdir -p "$(Dirname "$LogFile")"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			if Is_IntegerNr "$MaxLogLines" && [ $MaxLogLines -gt 0 ] && [ $(cat "$LogFile" 2>&1 | wc -l) -gt $((MaxLogLines+$TextLinesNr)) ] ; then
				LoggingLog="$(cp --attributes-only -a "$LogFile" "${LogFile}.tmp" 2>&1)"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					cat "$LogFile" | tail -n $((MaxLogLines-TextLinesNr)) > "${LogFile}.tmp"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				if [ $StatusCode -eq 0 ] ; then
					mv "${LogFile}.tmp" "$LogFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				rm -f "${LogFile}.tmp"
				if [ $StatusCode -eq 0 ] ; then
					if [ $TextLinesNr -gt 0 ] ; then
						printf '%s\n' "${DatetimeStamp}${AskedText}" >> "$LogFile"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				else
					# If it's out of space, then try to overwrite previous log.
					printf '%s\n' "$LoggingLog" > "$LogFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $TextLinesNr -gt 0 ] ; then
						printf '%s\n' "${DatetimeStamp}${AskedText}" >> "$LogFile"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			else
				if [ $TextLinesNr -gt 0 ] ; then
					printf '%s\n' "${DatetimeStamp}${AskedText}" >> "$LogFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
	else
		printf '%s\n' "${sERROR}E: Log file not specified${fRESET}" 1>&2
		LastStatus=97 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

LogProgram ()
# Syntax as a sentence: LogProgram $ThisMessageLevel $Message
# Description: Writes message to configured log file, depending on configured LogLevel vs ThisMessageLevel for message.
# Expected parameters:
#	$1	Level number of the message:
#		0=NothingToSay 1=Error 2=Warning 3=NormalInfo 4=DebugInfo
#		(same numbers in negative sign to not write date-time stamp)
#	(rest)	String for stdout/stderr/log
#		Escaped \n are replaced by line breaks.
# Notes:
#	- TTY warning/error/debug formats are already put
#	- stderr is already used for warning/error/debug messages
#	- I:/W:/E: prefixes are NOT added automatically.
# Depends on functions: Is_IntegerNr Dirname LogToFile
# Depends on software packages: perl-base sed
# Supports environment variables: LogLevel MainLog MainControllerLog INIT_SCRIPT_InitCall MaxLogLines DataIsPublic LogTime
{
	local ThisMessageLevel="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local CallType=''
	local LogFile=''
	local DatetimeStamp=0
	local AskedText=''
	local CurString=''
	local ProgramLogLevel=3
	local RedirectedStdout=''
	local LoggingLog=''
	local LastStatus=0
	local StatusCode=0
	
	perl -e "exit${ParO}-p STDOUT ? 1 : 0${ParC};"  #'
	RedirectedStdout=$?
	if [ $RedirectedStdout -ne 0 ] ; then
		sWARN=''
		fRESET=''
		sERROR=''
		sGOOD=''
	fi
	if [ $ThisMessageLevel -ge 0 ] ; then
		DatetimeStamp=1
	fi
	ThisMessageLevel="$(printf '%s\n' "$ThisMessageLevel" | sed -e 's|^-||g')"
	if Is_IntegerNr "$LogLevel" ; then ProgramLogLevel=$LogLevel ; fi
	if [ "$*" != "" ] && [ $ProgramLogLevel -ge $ThisMessageLevel ] && [ $ThisMessageLevel -gt 0 ] ; then
		unset IFS ; for CurString in "$@" ; do	# Simple $* sometimes results in comma-separated words
			if [ "$AskedText" != "" ] ; then AskedText="${AskedText} " ; fi
			AskedText="${AskedText}$(printf '%s\n' "$CurString" | sed -e 's|\\n|\n|g')"
		done
		if [ $ThisMessageLevel -ge 4 ] ; then
			printf '%s\n' "${sINFO}${AskedText}${fRESET}" 1>&2
		else
			if [ $ThisMessageLevel -eq 3 ] ; then
				printf '%s\n' "$AskedText"
			else
				if [ $ThisMessageLevel -eq 2 ] ; then
					printf '%s\n' "${sWARN}${AskedText}${fRESET}" 1>&2
				else
					if [ $ThisMessageLevel -eq 1 ] ; then
						printf '%s\n' "${sERROR}${AskedText}${fRESET}" 1>&2
					else
						printf '%s\n' "$AskedText" 1>&2
					fi
				fi
			fi
		fi
		LogFile="$MainLog"
		if [ "$LogFile" = "" ] && [ "$MainControllerLog" != "" ] ; then
			LogFile="$MainControllerLog"
			if [ "$INIT_SCRIPT_InitCall" != "1" ] ; then
				CallType=" ${ParO} Manual ${ParC} "
			fi
		fi
		if [ "$LogFile" != "" ] ; then
			if [ "$DataIsPublic" != "" ] && [ ! -f "$LogFile" ] ; then
				if [ ! -d "$(Dirname "$LogFile")" ] ; then
					mkdir -p "$(Dirname "$LogFile")"
					if [ "$DataIsPublic" = "1" ] ; then
						chmod a+rX "$(Dirname "$LogFile")"
					else
						chmod g-w,o= "$(Dirname "$LogFile")"
					fi
				fi
				touch "$LogFile"
				if [ "$DataIsPublic" = "1" ] ; then
					chmod a+r "$LogFile"
				else
					chmod g-w,o= "$LogFile"
				fi
			fi
			LoggingLog="$(LogToFile "$LogFile" "$DatetimeStamp" "$MaxLogLines" "${CallType}${AskedText}")"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] ; then
				printf '%s\n' "${sERROR}${LoggingLog}${fRESET}" 1>&2
			fi
		fi
	fi
	return $StatusCode
}

DependenciasFaltan ()
# Sintaxis como función: $(DependenciasFaltan "$ListaDependencias" [SenyalarEjecutable])
# Descripción:
#	Comprueba la disponibilidad de los programas especificados y
#	devuelve (stdout) una lista de los que faltan. Si se encuentra todo
#	no devuelve nada.
# Parámetros esperados:
#	$1	Lista separada por espacios de cada ejecutable y paquete respectivo,
#		separados entre si por "/"
#		Para varias opciones de ejecutable, separarlos entre ":"
#	$2	(opcional) Especifique "0" para evitar que a cada mención de paquete se [añada] el nombre del ejecutable que falta.
# Ejemplo1:	$(DependenciasFaltan "cat/coreutils grep/grep dpkg-deb/dpkg")
#		puede devolver "grep dpkg[dpkg-deb]" si faltan grep y dpkg-deb
# Ejemplo2:	$(DependenciasFaltan "insserv:update-rc.d/sysv-rc cat/coreutils")
#		puede devolver "sysv-rc[update-rc.d]" si tanto falta insserv como update-rc.d
# ToDo:
#	- Cambiar la sintaxis para permitir rutas de ejecutable: /bin/gzip
#	  Alternativas a la barra /: ? ! ;
#	- Para un ejecutable que condiciona requerir otro, separarlos entre "¡"
#	  Por ejemplo: postfix¡policyd-spf/postfix-policyd-spf-python
#	  significaría: SI existe "postfix" entonces debe existir "policyd-spf" y ello depende del paquete postfix-policyd-spf-python
#	- Para un ejecutable que es incompatible con otro, separarlos entre "!"
#	  Por ejemplo: postfix!exigrep/exim4-base
#	  significaría: SI existe "postfix" entonces NO debe existir "exigrep" y ello se resuelve desinstalando el paquete exim4-base
# Depends on functions: Is_Executable
# Depends on software packages: grep, sed
{
	local ListaDependencias="$1"
	local SenyalarEjecutable="$2"
	local DependenciaActual=''
	local EjecutablesActuales=''
	local EjecutableActual=''
	local EncontradoActual=''
	local PaqueteActual=''
	local Value=''
	
	unset IFS ; for DependenciaActual in $ListaDependencias ; do
		EjecutablesActuales="$(printf '%s' "$DependenciaActual" | cut -f 1 -d '/')"
		PaqueteActual="$(printf '%s' "$DependenciaActual" | cut -f 2 -d '/')"
		EjecutablesActuales="$(printf '%s' "$EjecutablesActuales" | tr -s ':' ' ')"
		EncontradoActual=''
		unset IFS ; for EjecutableActual in $EjecutablesActuales ; do
			if Is_Executable "$EjecutableActual" ; then
				EncontradoActual="1"
			fi
		done
		if [ "$EncontradoActual" = "" ] ; then
			if [ "$(printf '%s' " $Value " | grep -e " $PaqueteActual\[")" = "" ] || [ "$(printf '%s' " $Value " | grep -e " $PaqueteActual ")" = "" ] ; then
				if [ "$EjecutableActual" = "$PaqueteActual" ] || [ "$EjecutableActual" != "$EjecutablesActuales" ] || [ "$SenyalarEjecutable" = "0" ] ; then
					Value="$Value $PaqueteActual"
				else
					Value="$Value $PaqueteActual[$EjecutableActual]"
				fi
			fi
		fi
	done
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

IniSectionContent ()
# Syntax as a function: $(IniSectionContent "$FileOrContent" "$SectionName")
# Expected parameters:
#	$1	Path/name of file to query. If not exists as a file, string will be treated as content to look into.
#	$2	[section name] without brackets []. Example: "global"
#		specifying "[]" returns file content before any section declaration.
# Notes:
#	- Includes section header 
# Depends on functions:	(none)
# Depends on software packages: grep, sed
{
	local FileOrContent="$1"
	local SectionName="$2"
	local LinesNr=0
	local Value=''
	
	if [ "$FileOrContent" != "" ] && [ "$SectionName" != "" ] ; then
		if [ -f "$FileOrContent" ] ; then
			Value="$(cat "$FileOrContent")"
		else
			Value="$FileOrContent"
		fi
		if [ "$SectionName" != "[]" ] ; then
			Value="$(printf '%s\n' "$Value" | sed -rne "/^[[:blank:]]*[[]${SectionName}[]]/,//p")"
			if [ "$Value" != "" ] ; then
				printf '%s\n' "$Value" | head -n 1	# Print before being filtered
				LinesNr="$(printf '%s\n' "$Value" | wc -l)"
				# Omit current section header
				Value="$(printf '%s\n' "$Value" | tail -n $((LinesNr - 1)))"
				Value="$(printf '%s\n' "$Value" | sed -re '/^[[:blank:]]*[[].*[]]/q')"
				LinesNr="$(printf '%s\n' "$Value" | wc -l)"
				if [ "$(printf '%s\n' "$Value" | tail -n 1 | grep -e '^ [[].*[]]' -e '^[[].*[]]')" != "" ] && [ $LinesNr -ge 2 ] ; then
					# Omit next section header
					Value="$(printf '%s\n' "$Value" | head -n $((LinesNr - 1)))"
				else
					# Omit when desired section is empty and first line is next section's header
					Value="$(printf '%s\n' "$Value" | grep -ve '^ [[].*[]]' -ve '^[[].*[]]')"
				fi
			fi
		else
			Value="$(printf '%s\n' "$Value" | sed -re '/^[[:blank:]]*[[].*[]]/q' | grep -ve '^ [[].*[]]' -ve '^[[].*[]]')"
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

IniVarValue ()
# Syntax as a function: $(IniVarValue "$FileOrContent" "$VariableName" "$SectionName" "$NotFoundValue" "$NameValueSeparator" "$EndVariableSymbol" "$DefaultsFile")
# Expected parameters:
#	$1	Path/name of file to query. If not exists as a file, string will be treated as content to look into.
#	$2	Variable name to query (case insensitive)
#	$3	(optional or empty) [section name] without brackets []. Default (empty): no section consideration. Examples: "global" ""
#	$4	(optional or empty) Value to return in case of not finding the variable in any file. Default; empty string.
#	$5	(optional or empty) Separator between variable name and assigned value. Default: "=". Examples: "=" ":="
#	$6	(optional or empty) End variable/assignation mark, to be omited from value. Default (empty): No end mark. Example: ";"
#	$7	(optional or empty) File path where to get DefaultValue from, if found. A found value prevails over 4th parameter (DefaultValue)
# To Do:
#	- This (or another function) to load more than one variable at once.
# Notes:
#	- if section is not specified or empty, returns last match from whole file content.
#	- if section is "[]", queries only file content before any [section] declaration.
# Depends on functions: IniSectionContent
# Depends on software packages: grep, sed
{
	local FileOrContent="$1"
	local VariableName="$2"
	local SectionName="$3"
	local NotFoundValue="$4"
	local NameValueSeparator="$5"
	local EndVariableSymbol="$6"
	local DefaultsFile="$7"
	local LinesNr=0
	local SectionContent=''
	local SeparatorMask='IVVtmpbHckF2LMB4tmpWz2coasdb3tmpX7LuyGTvrW'
	local NotFoundKey='IVVtmpStZrypNMzntmpgKqLEd5E5ttmpIWW5wemyCW'
	local Value=''
	
	if [ "$FileOrContent" != "" ] ; then
		if [ "$NameValueSeparator" = "" ] ; then NameValueSeparator='=' ; fi
		if [ "$SectionName" = "" ] ; then
			if [ -f "$FileOrContent" ] ; then
				SectionContent="$(cat "$FileOrContent")"
			else
				SectionContent="$FileOrContent"
			fi
		else
			SectionContent="$(IniSectionContent "$FileOrContent" "$SectionName")"
		fi
		Value="$(printf '%s\n' "$SectionContent" | tr -s '\t' ' ' | grep -ie "^${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName} ${NameValueSeparator}" | tail -n 1 | sed -e "s|${NameValueSeparator}|${SeparatorMask}|")"
		if [ "$Value" != "" ] ; then
			# Variable found; let's separate value.
			Value="$(printf '%s\n' "$Value" | sed -e "s|.*${SeparatorMask}||")"
			Value="$(expr "$Value" : "[ ]*\(.*[^ ]\)[ ]*$")"	# Trim
			if [ "$EndVariableSymbol" != "" ] ; then
				Value="$(printf '%s\n' "$Value" | sed -e "s|${EndVariableSymbol}$||")"
				Value="$(expr "$Value" : "[ ]*\(.*[^ ]\)[ ]*$")"	# Trim
			fi
			if [ "$(printf '%s\n' "$Value" | grep -e '^"' | grep -e '"$')" != "" ] ; then
				# Double quotes
				Value="$(printf '%s\n' "$Value" | cut -f 2- -d '"' | sed -e 's|"$||')"
			else
				if [ "$(printf '%s\n' "$Value" | grep -e "^'" | grep -e "'$")" != "" ] ; then
					# Single quotes
					Value="$(printf '%s\n' "$Value" | cut -f 2- -d "'" | sed -e "s|'$||")"
				fi
			fi
		else
			Value="$NotFoundKey"
		fi
	else
		Value="$NotFoundKey"
	fi
	if [ "$Value" = "$NotFoundKey" ] ; then
		if [ "$DefaultsFile" != "$FileOrContent" ] && [ -f "$DefaultsFile" ] ; then
			Value="$(IniVarValue "$DefaultsFile" "$VariableName" "$SectionName" "$NotFoundValue" "$NameValueSeparator" "$EndVariableSymbol" '')"
		else
			Value="$NotFoundValue"
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

SetIniVarValue ()
# Syntax as a sentence: SetIniVarValue "$File" "$VariableName" "$SectionName" "$NewValue" "$NameValueSeparator" "$PreComment"
# Expected parameters:
#	$1	Path/name of file to query
#	$2	Variable name to query
#	$3	(optional or empty) [section name] without brackets []. Examples: "global" ""
#	$4	(optional or empty) Value to write for the variable entry.
#	$5	(optional or empty) Separator between variable name and assigned value. Examples: "=" ":=". Default is "="
#	$6	(optional or empty) Line to precede variable's line if it must be added. Comment mark should be included.
#		PreComment can contain escaped \n to be converted into line breaks.
# To Do:
#	- Process content separated by section (treat different variable as in different section)
#	- This (or another function) to set more than one variable at once.
# Notes:
#	- if section is not specified or empty, sets matches in the whole file.
#	- if section is "[]", sets variable only before any [section] declaration in the file.
#	- Some blank lines can be lost in resulting file content
# Depends on functions: Dirname IniSectionContent
# Depends on software packages: grep, sed
{
	local File="$1"
	local VariableName="$2"
	local SectionName="$3"	# Optional or empty (examples: "global" "")
	local NewValue="$4"	# If quotes are needed ("value") must be already contained in the supplied value ("\"value\""). Same with EndVariableSymbol.
	local NameValueSeparator="$5"	# Optional or empty (example: ":=") Default is "="
	local PreComment="$6"	# Optional line to precede variable's line if it must be added. Comment mark should be included.
	local OldContent=''
	local Part1=''
	local SectionContent=''
	local Part2=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$File" != "" ] && [ "$VariableName" != "" ] ; then
		if [ ! -f "$File" ] ; then
			mkdir -p "$(Dirname "$File")"
			touch "$File"
		fi
		if [ -f "$File" ] ; then
			if [ "$NameValueSeparator" = "" ] ; then NameValueSeparator="=" ; fi
			OldContent="$(cat "$File")"
			if [ "$SectionName" != "" ] ; then
				OldContentLinesNr=$(printf '%s\n' "$OldContent" | wc -l)
				if [ "$SectionName" = "[]" ] ; then
					SectionContent="$(printf '%s\n' "$OldContent" | sed -re '/^[[:blank:]]*[[].*[]]/q' | grep -ve '^[[].*[]]' -ve '^ [[].*[]]')"
					Part1LinesNr=$(printf '%s\n' "$SectionContent" | wc -l)
					cat /dev/null > "$File"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ "$(printf '%s\n' "$SectionContent" | tr -s '\t' ' ' | tr -s ' ' | grep -ie "^${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName} ${NameValueSeparator}")" = "" ] ; then
						if [ "$SectionContent" != "" ] ; then
							printf '%s\n' "$SectionContent" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$PreComment" != "" ] ; then
							if [ "$(cat "$File" 2>/dev/null)" != "" ] && [ "$(cat "$File" | tail -n 1)" != "" ] ; then
								printf '%s\n' "" >> "$File"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							fi
							printf '%s\n' "$PreComment" | sed -e 's|\\n|\n|g' -e 's|\\t|\t|g' >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						printf '%s\n' "${VariableName}${NameValueSeparator}${NewValue}" >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						printf '%s\n' "$SectionContent" | sed -re "s|^([[:blank:]]*)(${VariableName})([[:blank:]]*)(${NameValueSeparator})([[:blank:]]*).*|\1\2\3\4\5${NewValue}|" >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					printf '%s\n' "$OldContent" | tail -n $((OldContentLinesNr - Part1LinesNr)) >> "$File"
				else
					if [ "$(printf '%s\n' "$OldContent" | grep -e "^[[]${SectionName}[]]" -e "^ [[]${SectionName}[]]")" != "" ] ; then
						# Case sensitive to match sed that cannot be insensitive with q/p commands
						Part1="$(printf '%s\n' "$OldContent" | sed -re "/^[[:blank:]]*[[]${SectionName}[]]/q")"
						Part1LinesNr=$(printf '%s\n' "$Part1" | wc -l)
						Part1LinesNr=$((Part1LinesNr - 1))
						Part1="$(printf '%s\n' "$Part1" | grep -ive "^[[]${SectionName}[]]" -ive "^ [[]${SectionName}[]]")"
						SectionContent="$(IniSectionContent "$File" "$SectionName")"
						SectionLinesNr=$(printf '%s\n' "$SectionContent" | wc -l)
						Part2="$(printf '%s\n' "$OldContent" | tail -n $((OldContentLinesNr - Part1LinesNr - SectionLinesNr)))"
						cat /dev/null > "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$Part1" != "" ] ; then
							printf '%s\n' "$Part1" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$(printf '%s\n' "$SectionContent" | tr -s '\t' ' ' | tr -s ' '| grep -ie "^${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName} ${NameValueSeparator}")" = "" ] ; then
							printf '%s\n' "$SectionContent" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ "$PreComment" != "" ] ; then
								printf '%s\n' "$PreComment" | sed -e 's|\\n|\n|g' -e 's|\\t|\t|g' >> "$File"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							fi
							printf '%s\n' "${VariableName}${NameValueSeparator}${NewValue}" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						else
							printf '%s\n' "$SectionContent" | sed -re "s|^([[:blank:]]*)(${VariableName})([[:blank:]]*)(${NameValueSeparator})([[:blank:]]*).*|\1\2\3\4\5${NewValue}|" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$Part2" != "" ] ; then
							printf '%s\n' "$Part2" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					else
						printf '%s\n' "[${SectionName}]" >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$PreComment" != "" ] ; then
							printf '%s\n' "$PreComment" | sed -e 's|\\n|\n|g' -e 's|\\t|\t|g' >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						printf '%s\n' "${VariableName}${NameValueSeparator}${NewValue}" >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			else
				if [ "$(printf '%s\n' "$OldContent" | tr -s '\t' ' ' | tr -s ' '| grep -ie "^${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName} ${NameValueSeparator}")" = "" ] ; then
					if [ "$PreComment" != "" ] ; then
						printf '%s\n' "$PreComment" | sed -e 's|\\n|\n|g' -e 's|\\t|\t|g' >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					printf '%s\n' "${VariableName}${NameValueSeparator}${NewValue}" >> "$File"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					printf '%s\n' "$OldContent" | sed -re "s|^([[:blank:]]*)(${VariableName})([[:blank:]]*)(${NameValueSeparator})([[:blank:]]*).*|\1\2\3\4\5${NewValue}|i" > "$File"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
	else
		printf '%s\n' "${sERROR}E: SetIniVarValue: File and/or VariableName not specified.${fRESET}" 1>&2
		LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

GetOrSetIniVarValue ()
# Syntax as a function: $(GetOrSetIniVarValue "$File" "$VariableName" "$SectionName" "$DefaultValue" "$NameValueSeparator" "$EndVariableSymbol" "$PreComment" "$ReadOnly" "$DefaultsFile")
# Expected parameters:
#	$1	Path/name of file to query
#	$2	Variable name to query (case insensitive)
#	$3	(optional or empty) [section name] without brackets []. Examples: "global" ""
#	$4	(optional or empty) Value to return in case of not finding the variable in any file. It's used to write the new variable entry too.
#	$5	(optional or empty) Separator between variable name and assigned value. Examples: "=" ":=". Default is "="
#	$6	(optional or empty) End variable/assignation mark, to be omited from value and to be written when needed. Example: ";"
#	$7	(optional or empty) Line to precede variable's line if it must be added. Comment mark should be included.
#	$8	(optional or empty) "1" or "ro" if $File must not be modified. Assumed no by default (0)
#	$9	(optional or empty) File path where to get DefaultValue from, if found. A found value prevails over 4th parameter (DefaultValue)
# Notes:
#	- if section is not specified or empty, treats whole file content.
#	- if section is "[]", treats only file content before any [section] declaration.
#	- PreComment can contain escaped \n to be converted into line breaks.
# Depends on functions: IniVarValue SetIniVarValue
# Depends on software packages: grep, sed
{
	local File="$1"
	local VariableName="$2"
	local SectionName="$3"
	local DefaultValue="$4"	# If quotes are needed ("value") must be already contained in the supplied value ("\"value\""). Same with EndVariableSymbol.
	local NameValueSeparator="$5"
	local EndVariableSymbol="$6"
	local PreComment="$7"
	local ReadOnly="$8"
	local DefaultsFile="$9"
	local NotFoundKey='GOSIVVtmpStZrypNMzntmpgKqLEd5E5ttmpIWW5wemyCW'
	local Value=''
	local LastStatus=0
	local StatusCode=0
	
	Value="$(IniVarValue "$File" "$VariableName" "$SectionName" "$NotFoundKey" "$NameValueSeparator" "$EndVariableSymbol" "$DefaultsFile")"
	if [ "$Value" = "$NotFoundKey" ] ; then
		if [ "$ReadOnly" != "1" ] && [ "$ReadOnly" != "ro" ] ; then
			SetIniVarValue "$File" "$VariableName" "$SectionName" "${DefaultValue}${EndVariableSymbol}" "$NameValueSeparator" "$PreComment"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		Value="$DefaultValue"
		# EndVariableSymbol does not come with DefaultValue
		if [ "$(printf '%s\n' "$Value" | grep -e '^"' | grep -e '"$')" != "" ] ; then
			# Double quotes
			Value="$(printf '%s\n' "$Value" | cut -f 2- -d '"' | sed -e 's|"$||')"
		else
			if [ "$(printf '%s\n' "$Value" | grep -e "^'" | grep -e "'$")" != "" ] ; then
				# Single quotes
				Value="$(printf '%s\n' "$Value" | cut -f 2- -d "'" | sed -e "s|'$||")"
			fi
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

LoadVarsValues ()
# Syntax as a sentence: LoadVarsValues $FileOrContent $VariablesNames $VarsRequired $DefaultsFileOrContent
# Descripcion: Runs global variables assignation same as specified in $FileOrContent
# Expected parameters:
#	$1	Path/name of file to query. If not exists as a file, string will be treated as content to look into.
#	$3	Variable names to filter (case sensitive, space separated). If empty "", all file variables will be loaded.
#	$2	0 = No error on variables lack. 1 = Only error if all variables lack. 2 = Result error on any variable lack (it only returns exitcode 104; no messages)
#	$4	(optional or empty) File path (or vars string) to do a previous load from, if found.
# To Do:
#	- 
# Notes:
#	- Indentation are supported (initial tabs and/or spaces).
# Depends on functions: (none)
# Depends on software packages: grep, sed
{
	lvv__FileOrContent="$1"
	lvv__VariablesNames="$2"
	lvv__VarsRequired="$3"
	lvv__DefaultsFileOrContent="$4"
	lvv__CurVarName=''
	lvv__VarsFilter=''
	lvv__LoadedContent=''
	lvv__LastStatus=0
	lvv__StatusCode=0
	
	if [ "$lvv__VariablesNames" != "" ] ; then
		unset IFS ; for lvv__CurVarName in $lvv__VariablesNames ; do
			lvv__VarsFilter="$lvv__VarsFilter -e ^${lvv__CurVarName}="
		done
	else
		lvv__VarsFilter=" -ie ^[a-z].*="
	fi
	if [ "$lvv__DefaultsFileOrContent" != "" ] ; then
		if [ -f "$lvv__DefaultsFileOrContent" ] ; then
			lvv__DefaultsFileOrContent="$(cat "$lvv__DefaultsFileOrContent")"
			lvv__LastStatus=$? ; if [ $lvv__StatusCode -eq 0 ] ; then lvv__StatusCode=$lvv__LastStatus ; fi
		fi
		lvv__LoadedContent="$(printf '%s\n' "$lvv__DefaultsFileOrContent" | sed -e 's|^[ \t]*||g' | grep $lvv__VarsFilter)"
		if [ "$lvv__LoadedContent" != "" ] ; then
			eval $lvv__LoadedContent
		fi
	fi
	if [ "$lvv__FileOrContent" != "" ] ; then
		if [ -f "$lvv__FileOrContent" ] ; then
			lvv__FileOrContent="$(cat "$lvv__FileOrContent")"
			lvv__LastStatus=$? ; if [ $lvv__StatusCode -eq 0 ] ; then lvv__StatusCode=$lvv__LastStatus ; fi
		fi
		lvv__LoadedContent="$(printf '%s\n' "$lvv__FileOrContent" | sed -e 's|^[ \t]*||g' | grep $lvv__VarsFilter)"
		if [ "$lvv__LoadedContent" != "" ] ; then
			eval $lvv__LoadedContent
			if [ "$lvv__VarsRequired" = "2" ] && [ "$lvv__VariablesNames" != "" ] ; then
				unset IFS ; for lvv__CurVarName in $lvv__VariablesNames ; do
					if [ "$(printf '%s' "$lvv__LoadedContent" | grep -e "^${lvv__CurVarName}=")" = "" ] ; then
						lvv__LastStatus=104 ; if [ $lvv__StatusCode -eq 0 ] ; then lvv__StatusCode=$lvv__LastStatus ; fi
					fi
				done
			fi
		else
			if [ "$lvv__VarsRequired" = "1" ] || [ "$lvv__VarsRequired" = "2" ] ; then
				lvv__LastStatus=104 ; if [ $lvv__StatusCode -eq 0 ] ; then lvv__StatusCode=$lvv__LastStatus ; fi
			fi
		fi
	fi
	return $lvv__StatusCode
}

ParentPstree ()
# Alternative to pstree -p -s (syntax not available in 1990's or 2000's distributions)
# Depends on functions: Is_IntegerNr
# Depends on software packages: sed, procps
{
	local ChildPid="$1"
	local CurrentPid=''
	local Stat=''
	local StatRest=''
	local Value=''
	
	CurrentPid=$ChildPid
	while Is_IntegerNr "$CurrentPid" && [ "$CurrentPid" != "0" ] ; do
		Value="$CurrentPid $Value"
		Stat="$(cat /proc/${CurrentPid}/stat 2>/dev/null | tr -s '\t' ' ' | sed -e 's|^ ||g')"
		StatRest="$(printf '%s\n' "$Stat" | tr -s ' ' | cut -f 2- -d ')' | sed -e 's|^ ||g')"
		CurrentPid="$(printf '%s\n' "$StatRest" | cut -f 2 -d ' ' | tail -n 1)"
#		CurrentPid=$(echo TrimAndSingle $(PsOutputValues ppid $CurrentPid | tail -n 1) | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')
		CurrentPid="$(echo TrimAndSingle $CurrentPid | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		if ! Is_IntegerNr $CurrentPid ; then CurrentPid="" ; fi
	done
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

PsOutputValues ()
# Syntax as a function: "$(PsOutputValues "$OutputColumns" "$FilterPid1 $FilterPid2 $FilterPid3...")"
# Description: Parser for ps command, that emulates -A and -o options for old versions without support for it. Only returns (stdout) asked output values.
# Expected parameters:
#	$1	Comma-separated list of keywords to compose columns. Following keywords are the only supported:
#		cmaj_flt,cmd,cmin_flt,comm,cstime,cutime,egid,euid,f,flag,flags,gid,maj_flt,min_flt,ni,nice,nlwp,pgid,pgrp,pid,ppid,pri,priority,rss,rssize,rsz,s,sess,session,sid,state,stime,T,tname,tpgid,tt,tty,uid,utime,vsize,vsz
#		+ specific "sort_pid" (sortable PID: 0001, 0002), uid_map (LXC base nr.), gid_map (LXC base nr.), rootfs (its container path)
#		Unexpected keywords are directly parsed to ps with modern syntax. Them return '?' in case of error.
#	$2	(optional) Space-separated list of PIDs to restrict to. If not provided, all PIDs will be selected.
# Notes:
#	- VERY SLOW! Better use directly "ps" if you don't need modern options.
#	- No headers are included
#	- Columns are separated by only one space character
#	- Every unknown value is returned as '?'
# Depends on functions: Is_IntegerNr ParentPstree
# Depends on software packages: grep sed
{
	local OutputColumns="$1"
	local FilterPids="$2"
	local CurValue=''
	local CurPid=''
	local CurColumn=''
	local CurLine=''
	local PidMaxLen=0
	local PidsToScan=''
	local ScanningPid=''
	local LastStatus=$?
	
	if ! Is_IntegerNr $FilterPids ; then
		FilterPids="$(ls -1 /proc/*/cmdline 2>/dev/null | grep -ve 'self' | cut -f 3 -d '/')"
	fi
	if [ "$OutputColumns" = "pid" ] ; then
		# To accelerate simple pids listing
		unset IFS ; for CurPid in $FilterPids ; do
			if [ -d /proc/$CurPid ] ; then printf '%s\n' "$CurPid" ; fi
		done
	else
		if [ "$(printf '%s\n' ",${OutputColumns}," | grep -e ',sort_pid,')" != "" ] ; then
			PidMaxLen=0
			unset IFS ; for CurPid in $FilterPids ; do
				if [ ${#CurPid} -gt $PidMaxLen ] ; then PidMaxLen=${#CurPid} ; fi
			done
		fi
		OutputColumns="$(printf '%s\n' "$OutputColumns" | tr -s ',' ' ')"
		OutputColumns="$(printf '%s\n' " $OutputColumns " | sed -e 's| state | s |g' | sed -e 's| uid | euid |g' | sed -e 's| gid | egid |g' | sed -e 's| vsize | vsz |g' | sed -e 's| rssize | rss |g' | sed -e 's| rsz | rss |g' | sed -e 's| pgrp | pgid |g' | sed -e 's| session | sess |g')"
		OutputColumns="$(printf '%s\n' " $OutputColumns " | sed -e 's| sid | sess |g' | sed -e 's| tname | tty |g' | sed -e 's| tt | tty |g' | sed -e 's| flags | f |g' | sed -e 's| flag | f |g' | sed -e 's| priority | pri |g' | sed -e 's| nice | ni |g' | sed -e 's| thcount | nlwp |g')"
		unset IFS ; for CurPid in $FilterPids ; do
			# A pid called "self" points to analyzer, such as "ls" or "cat". Discarding this.
			if Is_IntegerNr $CurPid ; then
				CurLine=''
				Stat="$(cat /proc/${CurPid}/stat 2>/dev/null | tr -s '\t' ' ' | sed -e 's|^ ||g')"
				StatRest="$(printf '%s\n' "$Stat" | tr -s ' ' | cut -f 2- -d "${ParC}" | sed -e 's|^ ||g')"
				Status="$(cat /proc/${CurPid}/status 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g')"
				unset IFS ; for CurColumn in $OutputColumns ; do
					case "$CurColumn" in
						"sort_pid" ) CurValue="$(printf "%${PidMaxLen}s\n" "$CurPid" | sed -e 's| |0|g')" ;;
						"euid" ) CurValue="$(printf '%s\n' "$Status" | grep -ie '^Uid:' | cut -f 2 -d ':' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" ;;
						"egid" ) CurValue="$(printf '%s\n' "$Status" | grep -ie '^Gid:' | cut -f 2 -d ':' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" ;;
						"vsz" ) CurValue="$(printf '%s\n' "$Status" | grep -ie '^VmSize:' | cut -f 2 -d ':' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" ;;
						"rss" ) CurValue="$(printf '%s\n' "$Status" | grep -ie '^VmRSS:' | cut -f 2 -d ':' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" ;;
						"cmd" )
							CurValue="$(cat /proc/${CurPid}/cmdline 2>/dev/null | tr -s '\0' ' ' | tr -s ' ' | sed -e 's|$ ||g' | sed -e 's| $||g')"	# Command-line arguments come separated by null byles ('\0') and whole line ended too.
							if [ "$CurValue" = "" ] ; then	# comm
								CurValue="$(printf '%s\n' "$Stat" | cut -sf 2 -d "${ParO}" | cut -f 1 -d "${ParC}")"
							fi
							;;
						"pid" )
							CurValue="$(printf '%s\n' "$Stat" | cut -f 1 -d "${ParO}" | cut -f 1 -d ' ')"
							if [ "$CurValue" = "" ] ; then
								CurValue=$CurPid
							fi
							;;
						"comm" ) CurValue="$(printf '%s\n' "$Stat" | cut -sf 2 -d "${ParO}" | cut -f 1 -d "${ParC}")" ;;
						"s" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 1 -d ' ')" ;;
						"ppid" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 2 -d ' ')" ;;
						"pgid" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 3 -d ' ')" ;;
						"sess" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 4 -d ' ')" ;;
						"tpgid" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 6 -d ' ')" ;;
						"f" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 7 -d ' ')" ;;
						"min_flt" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 8 -d ' ')" ;;
						"cmin_flt" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 9 -d ' ')" ;;
						"maj_flt" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 10 -d ' ')" ;;
						"cmaj_flt" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 11 -d ' ')" ;;
						"utime" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 12 -d ' ')" ;;
						"stime" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 13 -d ' ')" ;;
						"cutime" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 14 -d ' ')" ;;
						"cstime" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 15 -d ' ')" ;;
						"pri" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 16 -d ' ')" ;;
						"ni" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 17 -d ' ')" ;;
						"nlwp" )
							CurValue="$(printf '%s\n' "$StatRest" | cut -f 17 -d ' ')"	# W: Linux before 2.6 always returns "0"
							if [ "$CurValue" = "0" ] ; then
								CurValue="$(ps h p $CurPid -o $CurColumn 2>/dev/null)"
								if ! Is_IntegerNr $CurValue ; then CurValue='0' ; fi
							fi
							;;
						"T" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 19 -d ' ')" ;;	# Old starttime
#						"tty" ) CurValue="$(ps h p $CurPid -o tty 2>/dev/null)" ;;	# Old "tty" is not the same as modern one.
						"uid_map" )
							CurValue="$(cat /proc/${CurPid}/uid_map)"
							CurValue="$(SomeWord () { printf '%s' $2; }; SomeWord $CurValue)"
							;;
						"gid_map" )
							CurValue="$(cat /proc/${CurPid}/gid_map)"
							CurValue="$(SomeWord () { printf '%s' $2; }; SomeWord $CurValue)"
							;;
						"rootfs" )
							CurValue="$(cat /proc/${CurPid}/mountinfo 2>/dev/null)"
							if [ "$CurValue" != "" ] ; then
								CurValue="$(printf '%s' "$CurValue" | grep -e ' / ' | grep -ve ' / /' | cut -f 4- -d ' ' | sed -e 's| / .*||g')"
								if [ "$CurValue" = "" ] && [ -d "/proc/${CurPid}" ] ; then CurValue='/' ; fi
							fi
							if [ "$CurValue" = "" ] ; then
								# It occurs on containerized defunct processes too
								PidsToScan="$(pstree -p $CurPid | tr ')' '\n' | grep -e '([0-9]' | sed -e 's|.*(||g')"
								unset IFS ; for ScanningPid in $PidsToScan ; do
									if [ "$CurValue" = "" ] ; then
										CurValue="$(cat /proc/${ScanningPid}/mountinfo 2>/dev/null | grep -e ' / ' | grep -ve ' / /' | cut -f 4- -d ' ' | sed -e 's| / .*||g')"
									fi
								done
							fi
							if [ "$CurValue" = "" ] ; then
								PidsToScan="$(ParentPstree $CurPid | tr ' ' '\n' | tac)"
								unset IFS ; for ScanningPid in $PidsToScan ; do
									if [ "$CurValue" = "" ] && [ $ScanningPid -ne 1 ] ; then
										CurValue="$(cat /proc/${ScanningPid}/mountinfo 2>/dev/null | grep -e ' / ' | grep -ve ' / /' | cut -f 4- -d ' ' | sed -e 's| / .*||g')"
									fi
								done
							fi
							if [ "$CurValue" = "" ] && [ -d "/proc/${CurPid}" ] ; then CurValue='/' ; fi  # Not in a container
							;;
						* )
							CurValue="$(ps h p $CurPid -o $CurColumn 2>&1 >/dev/null)"
							LastStatus=$?
							if [ $LastStatus -eq 0 ] && [ "$CurValue" = "" ] ; then
								# No error, No stderr
								CurValue="$(ps h p $CurPid -o $CurColumn)"
							else
								CurValue=''
							fi
							;;
					esac
					if [ "$CurValue" = "" ] ; then CurValue='?' ; fi
					CurLine="$CurLine $CurValue"
				done
				if [ -d "/proc/${CurPid}" ] ; then
					# Only if process has not disappeared while collecting data.
					printf '%s\n' "$CurLine" | sed -e 's|^ ||'
				fi
			fi
		done
	fi
}

PidfileStatus ()
# Only returns exit code about status
# Exit codes same as start-stop-daemon:
#	0: Program is running
#	1: Program is not running and the pid file exists
#	3: Program is not running
#	4: Unable to determine program status
# Depends on functions: Is_Executable Is_IntegerNr
# Depends on software packages: grep, sed, ps/procps
{
	local PidFile="$1"
	local Value=''
	local PID=''
	local SSD="start-stop-daemon"

	if ! Is_Executable "$SSD" && Is_Executable /sbin/start-stop-daemon ; then SSD="/sbin/start-stop-daemon" ; fi
	if [ "$("$SSD" --help 2>&1 | grep -e '--status')" != "" ] ; then
		# Some old versions haven't status parameter.  #'
		"$SSD" --pidfile "$PidFile" --status
		Value=$?
	else
		if [ -f "$PidFile" ] ; then
			PID=$(cat "$PidFile" | sed -e 's| ||g')
			if Is_IntegerNr $PID ; then
				if [ $(ps $PID | wc -l) -ge 2 ] ; then
					Value=0
				else
					Value=1
				fi
			else
				Value=4
			fi
		else
			Value=3
		fi
	fi
	return $Value
}

ExistingPIDs ()
# Description: From PIDs specified, returns (stdout) the existing ones as processes.
# Depends on functions: (none)
# Depends on software packages: sed, ps/procps
{
	local CurrentPid=''
	local Value=''
	
	unset IFS ; for CurrentPid in $* ; do
#		if [ "$(pstree $CurrentPid)" != "" ] ; then
		if [ $(ps $CurrentPid | wc -l) -ge 2 ] ; then
			Value="$Value $CurrentPid"
		fi
	done
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

KillPid ()
# Description: Scales signals and walks PID subtree to force a termination.
# Note: Specified PID must exist to find subtree.
# Depends on functions: ExistingPIDs Is_IntegerNr
# Depends on software packages: grep, pstree/psmisc
{
	local Pid=$1
	local TimeoutS=$2
	local PidTree=''
	local CurrentPid=''
	local TimeoutCount=0
	local TempValue=''
	local OldSleep=''
	local MultipleCount=2
	local FractionSecond=0.5
	local LastStatus=0
	local StatusCode=0

	PidTree="$(env COLUMNS=999 pstree -cpt $Pid)"
	if [ "$PidTree" != "" ] ; then
		# Sed upto v3 doesn't support newlines as replacement  #'
		#PidTree="$(echo "$PidTree" | tr -s '(' '\n' | grep -e ')' | cut -f 1 -d ')')"
		TempValue="$(printf '%s\n' "$PidTree" | tr -s '()' ' ')"
		PidTree=''
		unset IFS ; for CurrentPid in $TempValue ; do
			if Is_IntegerNr $CurrentPid ; then PidTree="$PidTree $CurrentPid" ; fi
		done
		NextPidTree=''
		unset IFS ; for CurrentPid in $PidTree ; do
			if [ "$(pstree $CurrentPid)" != "" ] ; then
				kill -15 $CurrentPid 2>/dev/null
			fi
		done
		TimeoutCount=$((TimeoutS / 2)) ; if [ $TimeoutCount -lt 0 ] ; then TimeoutCount=0 ; fi
		# Sleep upto v2 (and maybe -v4) only support integers
		OldSleep="$(sleep --version | grep -ie sleep | head -n 1 | grep -e ' 0\.' -e ' 1\.' -e ' 2\.' -e ' 3\.' -e ' 4\.' -ie 'invalid')"
		if [ "$OldSleep" != "" ] ; then
			MultipleCount=1
			FractionSecond=1
		fi
		TimeoutCount=$((TimeoutCount * MultipleCount))
		while [ "$(ExistingPIDs $PidTree)" != "" ] && [ $TimeoutCount -gt 0 ] ; do
			sleep $FractionSecond
			TimeoutCount=$((TimeoutCount - 1))
		done
		PidTree="$(ExistingPIDs $PidTree)"
		unset IFS ; for CurrentPid in $PidTree ; do
			if [ "$(pstree $CurrentPid)" != "" ] ; then
				kill -9 $CurrentPid 2>/dev/null
			fi
		done
		if [ "$PidTree" != "" ] ; then
			TimeoutCount=$((TimeoutS / 2)) ; if [ $TimeoutCount -lt 0 ] ; then TimeoutCount=0 ; fi
			TimeoutCount=$((TimeoutS - TimeoutCount)) ; if [ $TimeoutCount -lt 0 ] ; then TimeoutCount=0 ; fi
			TimeoutCount=$((TimeoutCount * MultipleCount))
			while [ "$(ExistingPIDs $PidTree)" != "" ] && [ $TimeoutCount -gt 0 ] ; do
				sleep $FractionSecond
				TimeoutCount=$((TimeoutCount - 1))
			done
			if [ "$(ExistingPIDs $PidTree)" != "" ] ; then
				LastStatus=110 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

StopPidFile ()
# Description: Stops daemon and walks whole PID subtree to kill signals.
# Depends on functions: Is_Executable Is_IntegerNr PidfileStatus ExistingPIDs KillPid
# Depends on software packages: pstree/psmisc
{
	local PidFile="$1"
	local TimeoutEachTry=$2
	local TimeoutCount=0
	local MainPid=''
	local PidTree=''
	local CurrentPid=''
	local TempValue=''
	local LastStatus=0
	local SSD="start-stop-daemon"
	local LastStatus=0
	local StatusCode=0

	if [ -f "$PidFile" ] ; then
		if ! Is_Executable "$SSD" && Is_Executable /sbin/start-stop-daemon ; then SSD="/sbin/start-stop-daemon" ; fi
		if [ "$TimeoutEachTry" = "" ] ; then TimeoutEachTry=1 ; fi
		MainPid="$(cat "$PidFile" | sed -e 's| ||g')"
		PidTree="$(env COLUMNS=999 pstree -cpt $MainPid)"
		if [ "$PidTree" != "" ] ; then
			# Sed upto v3 doesn't support newlines as replacement  #'
			#PidTree="$(echo "$PidTree" | tr -s '(' '\n' | grep -e ')' | cut -f 1 -d ')')"
			TempValue="$(printf '%s\n' "$PidTree" | tr -s '()' ' ')"
			PidTree=''
			unset IFS ; for CurrentPid in $TempValue ; do
				if Is_IntegerNr $CurrentPid ; then PidTree="$PidTree $CurrentPid" ; fi
			done
			if Is_Executable "$SSD" ; then
				PidfileStatus "$PidFile"
				LastStatus=$?
				"$SSD" --stop --pidfile "$PidFile"
				LastStatus=$?
				if [ $LastStatus -ne 1 ] && [ $LastStatus -ne 3 ] ; then
					# May be running before; we can pass error status result.
					if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
			TimeoutCount=$TimeoutEachTry
			while [ "$(pstree -cpt $MainPid)" != "" ] && [ $TimeoutCount -gt 0 ] ; do
				sleep 1
				TimeoutCount=$((TimeoutCount - 1))
			done
			PidTree="$(ExistingPIDs $PidTree)"
			unset IFS ; for CurrentPid in $PidTree ; do
				# This walk is necessary because MainPid can be already killed, but not all the tree.
				KillPid $CurrentPid $TimeoutEachTry
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			done
		fi
		if [ "$(ExistingPIDs $PidTree)" = "" ] ; then
			if [ -f "$PidFile" ] ; then
				rm "$PidFile"
			fi
		else
			LastStatus=110 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi

	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

DaemonizeCommand ()
# Syntax as a sentence: DaemonizeCommand "$PidFile" "$ExecutablePath" $parameters
# Description: 
# Expected parameters:
#	$1	File where to register the forked process PID, useful to control its execution
#	$2	Program pathname as expected by --startas parameter for start-stop-daemon, but necessary in any mode for execution  #do
#	(rest)	Parameters to pass to the executable
# Notes:
#	- Forks with any of start-stop-daemon[Debian] or daemonize[RHEL] or screen
#	- If no daemonize software is available, ampersand method will be used, BUT: Never call DaemonizeCommand inside a $(subshell) call because the $(subshell) only ends when childs end.
#	- An environment variable can be set: DaemonizeWith=screen to force use of "screen" if available.
# PENDENT:
#	- Preveure ús de ampersand
# Depends on functions: Is_Executable Is_IntegerNr Dirname PsOutputValues
# Depends on software packages: grep, sed, dpkg>=1.6|daemonize|screen, ps/procps
{
	local PidFile="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local ExecutablePath="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local PidFileDir=''
	local Pid=''
	local SSD="start-stop-daemon"
	local Regexp1=''
	local Regexp2=''
	local ProcessName=''
	local LastStatus=0
	local StatusCode=0

	Pid="$(cat "$PidFile" 2>/dev/null)"
	if Is_IntegerNr $Pid ; then
#		if [ "$(pstree $Pid)" = "" ] ; then
		if [ $(ps $Pid | wc -l) -le 1 ] ; then
			# Dead PID
			rm -f "$PidFile"
		fi
	else
		# Bad $PidFile or not exist
		rm -f "$PidFile"
	fi
	if [ "$DaemonizeWith" != "" ] && ! Is_Executable "$DaemonizeWith" ; then DaemonizeWith='' ; fi
	if [ ! -f "$PidFile" ] ; then
		if ! Is_Executable "$SSD" && Is_Executable /sbin/start-stop-daemon ; then SSD="/sbin/start-stop-daemon" ; fi
		PidFileDir="$(Dirname "$PidFile")"
		if [ ! -d "$PidFileDir" ] ; then
			mkdir -p "$PidFileDir"
			chmod u=rwX,g=rX,o= "$PidFileDir"
		fi
		if Is_Executable "$SSD" && [ "$("$SSD" --help 2>&1 | grep -e '--background')" != "" ] && [ "$("$SSD" --help 2>&1 | grep -e '--make-pidfile')" != "" ] && [ "$DaemonizeWith" = "" ] ; then
			# Without --background option, start-stop-daemon doesn't fork.  #'
			# Without --make-pidfile, PID is difficult to locate (a sub-script should be created).
			"$SSD" --start --pidfile "$PidFile" --make-pidfile --background --startas "$ExecutablePath" -- "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if Is_Executable daemonize && [ "$DaemonizeWith" = "" ] ; then
				daemonize -p "$PidFile" "$ExecutablePath" "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				if Is_Executable screen ; then
					ProcessName="$(printf '%s' "$PidFile" | tr -s '/' '\n' | tail -n 1 | cut -f 1 -d '.')"
					if [ "$ProcessName" != "" ] ; then ProcessName="${ProcessName}-" ; fi
					ProcessName="${ProcessName}$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d ' ')"
					screen -d -m -S "$ProcessName" "$ExecutablePath" "$@"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#					Pid="$(echo Trim$(screen -list | sed -e 's|	| |g' | tr -s ' ' | grep -e "\.${ProcessName} (" | cut -f 1 -d '.') | sed -e 's|^Trim||g' | sed -e 's|^ ||g' | sed -e 's| ||g')"
					Pid="$(echo TrimAndSingle $(screen -list | tr -s '\t' ' ' | tr -s ' ' | grep -e "\.${ProcessName} (" | cut -f 1 -d '.') | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | sed -e 's| ||g')"
					if Is_IntegerNr $Pid ; then
						printf '%s\n' "$Pid" > "$PidFile"
					fi
				else
#					printf '%s\n' "${sERROR}E: No program to launch command in the background ${ParO}start-stop-daemon/daemonize/screen${ParC}" 1>&2
#					LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					"$ExecutablePath" "$@" &
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					Pid=$!
					if ! Is_IntegerNr $Pid ; then
						# Careful for quotes in arguments
						if [ $# -gt 0 ] ; then
							Regexp1="$(printf '%s\n' "$1" | sed -e 's|\.|\\.|g' | sed -e 's|\*|\\*|g')"
							if [ $# -gt 1 ] ; then
								Regexp2="$(printf '%s\n' "$1" | sed -e 's|\.|\\.|g' | sed -e 's|\*|\\*|g')"
								Pid="$(PsOutputValues sort_pid,pid,cmd | sort | cut -f 2- -d ' ' | grep -e ".* .*$ExecutablePath.* .*${Regexp1}.* .*${Regexp2}" | tail -n 1 | cut -f 1 -d ' ')"
							else
								Pid="$(PsOutputValues sort_pid,pid,cmd | sort | cut -f 2- -d ' ' | grep -e ".* .*$ExecutablePath.* .*${Regexp1}" | tail -n 1 | cut -f 1 -d ' ')"
							fi
						else
							Pid="$(PsOutputValues sort_pid,pid,cmd | sort | cut -f 2- -d ' ' | grep -e ".* .*$ExecutablePath" | tail -n 1 | cut -f 1 -d ' ')"
						fi
					fi
					if Is_IntegerNr $Pid ; then
						printf '%s\n' "$Pid" > "$PidFile"
					fi
				fi
			fi
		fi
	else
		printf '%s\n' "${sERROR}E: Cannot launch a new process over an existing pidfile ${ParO}${PidFile}${ParC} with running PID ${Pid}${fRESET}" 1>&2
		LastStatus=113 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

TimedExecution ()
# Syntax as a function: $(TimedExecution $TimeoutSeconds $OpportunitiesNr "$StopFile" $command)
# Description: Executes a command with a hard timeout. Returns (stdout) execution's output.  #'
# Expected parameters:
#	$1	Number of seconds to wait the command completes (pased this time, process is killed)
#	$2	Maximum number of executions to try getting a clean exit of the command
#	$3	(optional or empty) File that will be a force signal to terminate inmediately, when exists (will not be removed).
#	(rest)	Command to execute
# Depends on functions: Is_Executable Is_IntegerNr PidfileStatus StopPidFile DaemonizeCommand
# Depends on software packages: grep, dpkg|daemonize
{
	local TimeoutSeconds=$1
	if [ $# -gt 0 ] ; then shift ; fi
	local OpportunitiesNr=$1
	if [ $# -gt 0 ] ; then shift ; fi
	local StopFile="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local TimedTempDir=''
	local RemainingCount=0
	local RemainingOpportunities=0
	local SuccessTry=0
	local CurrentPid=''
	local PidFile=''
	local ReposeTime=0
	local CurrentParameter=''
	local ParameterNr=-1
#	local MultipleCount=2
#	local FractionSecond=0.5
	local StatusCode_Tmp=0
	local Indentation1='	'
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	if [ $# -le 0 ] ; then
		printf '%s\n' "${sERROR}E: No command specified for TimedExecution.${fRESET}" 1>&2
		LastStatus=86 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if ! Is_IntegerNr "$TimeoutSeconds" || ! Is_IntegerNr "$OpportunitiesNr" ; then
		printf '%s\n' "${sERROR}E: Bad TimeoutSeconds or OpportunitiesNr for TimedExecution.${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		TimedTempDir="${DirTempX}/TimedExecution.$$"
		rm -fr "$TimedTempDir"
		mkdir "$TimedTempDir"
		chmod u=rwX,g=rX,o= "$TimedTempDir"
		printf '%s\n' '#!/bin/sh' > "${TimedTempDir}/logger.sh"
		chmod u=rwx,go= "${TimedTempDir}/logger.sh" # Closed permissions to secure passwords there
		if ! Is_Executable timeout ; then
			Indentation1=''
		fi
		if [ "$Indentation1" != "" ] ; then
			printf '%s\n' 'if [ "$1" != "timeout" ] ; then' >> "${TimedTempDir}/logger.sh"
			printf '%s\n' "	timeout --signal=9 --kill-after=0 $TimeoutSeconds \"\$0\" timeout" >> "${TimedTempDir}/logger.sh"
			printf '%s\n' '	StatusCode=$?' >> "${TimedTempDir}/logger.sh"
			printf '%s\n' 'else' >> "${TimedTempDir}/logger.sh"
		fi
		unset IFS ; for CurrentParameter in "$@" ; do
			ParameterNr=$((ParameterNr + 1))
			if [ $ParameterNr -gt 0 ] ; then
				printf '%s' ' ' >> "${TimedTempDir}/logger.sh"
			else
				printf '%s' "$Indentation1" >> "${TimedTempDir}/logger.sh"
			fi
			if [ "$CurrentParameter" = "" ] ; then
				printf '%s' "''" >> "${TimedTempDir}/logger.sh"
			else
				if [ "$(printf '%s\n' "$CurrentParameter" | grep -e ' ')" != "" ] ; then
					printf '%s' "\"${CurrentParameter}\"" >> "${TimedTempDir}/logger.sh"
				else
					printf '%s' "$CurrentParameter" >> "${TimedTempDir}/logger.sh"
				fi
			fi
		done
		printf '%s\n' " > \"${TimedTempDir}/log.txt\" 2>&1" >> "${TimedTempDir}/logger.sh"
		printf '%s\n' "${Indentation1}StatusCode=\$?" >> "${TimedTempDir}/logger.sh"
		printf '%s\n' "${Indentation1}printf '%s\n' \$StatusCode > \"${TimedTempDir}/StatusCode.num\"" >> "${TimedTempDir}/logger.sh"
		if [ "$Indentation1" != "" ] ; then
			printf '%s\n' 'fi' >> "${TimedTempDir}/logger.sh"
		fi
		printf '%s\n' 'exit $StatusCode' >> "${TimedTempDir}/logger.sh"
		RemainingOpportunities=$OpportunitiesNr
		while [ $RemainingOpportunities -gt 0 ] && [ $SuccessTry -eq 0 ] && [ ! -f "${TimedTempDir}/StatusCode.num" ] && [ ! -f "$StopFile" ] ; do
			PidFile="${TimedTempDir}/logger.pid"
			DaemonizeCommand "$PidFile" "${TimedTempDir}/logger.sh"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			# Sleep upto v2 (and maybe -v4) only support integers
#			OldSleep="$(sleep --version | grep -ie sleep | head -n 1 | grep -e ' 0\.' -e ' 1\.' -e ' 2\.' -e ' 3\.' -e ' 4\.' -ie 'invalid')"
#			if [ "$OldSleep" != "" ] ; then
#				MultipleCount=1
#				FractionSecond=1
#			fi
#			RemainingCount=$((TimeoutSeconds * MultipleCount))
			RemainingCount=$TimeoutSeconds
			while [ $RemainingCount -gt 0 ] && [ $StatusCode -eq 0 ] && [ ! -f "${TimedTempDir}/StatusCode.num" ] && [ ! -f "$StopFile" ] ; do
#				sleep $FractionSecond
				sleep 1
				RemainingCount=$((RemainingCount - 1))
			done
			if [ -f "$PidFile" ] ; then
				PidfileStatus "$PidFile"
				StatusCode_Tmp=$?
				if [ $StatusCode_Tmp -eq 0 ] ; then
					# 0: Program is running
					StatusCode_Tmp=109
				else
					StatusCode_Tmp=0
				fi
				StopPidFile "$PidFile" $TimeoutSeconds
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -eq 0 ] ; then LastStatus=$StatusCode_Tmp ; fi
			fi
			if [ -f "${TimedTempDir}/StatusCode.num" ] ; then
				LastStatus="$(cat "${TimedTempDir}/StatusCode.num")"
				if Is_IntegerNr $LastStatus ; then
					if [ $LastStatus -eq 0 ] ; then SuccessTry=1 ; fi
				else
					LastStatus=110
				fi
			else
				if [ $LastStatus -eq 0 ] ; then LastStatus=109 ; fi
			fi
			RemainingOpportunities=$((RemainingOpportunities - 1))
			if [ $SuccessTry -eq 0 ] && [ $RemainingOpportunities -gt 0 ] ; then
				ReposeTime=$((TimeoutSeconds / 2))
				if [ $ReposeTime -gt 15 ] ; then ReposeTime=15 ; fi
				RemainingCount=$ReposeTime
				while [ $RemainingCount -gt 0 ] && [ ! -f "$StopFile" ] ; do
					sleep 1
					RemainingCount=$((RemainingCount - 1))
				done
			fi
		done
		if [ $SuccessTry -eq 0 ] ; then
#			if [ $LastStatus -eq 0 ] ; then LastStatus=109 ; fi
#			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -eq 0 ] && [ $StatusCode -eq 0 ] ; then
				printf '%s\n' "Timeout:${OpportunitiesNr}x${TimeoutSeconds}s." 1>&2
				LastStatus=109
				if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ -f "${TimedTempDir}/log.txt" ] ; then
			cat "${TimedTempDir}/log.txt"
			rm "${TimedTempDir}/log.txt"
		fi
		rm -r "$TimedTempDir"
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

IOWait ()
# Syntax as a function: "$(IOWait)"
# Description: Returns (stdout) the percentage of time that the processor is waiting for disk or network I/O to complete.
# Depends on functions: (none)
# Depends on software packages: grep sed top/procps
# Notes:
#	- Returns an integer number. Decimals are discarded.
{
	local TopReport=''
#	local TempFile=''
	local Value=''
	
	TopReport="$(timeout --foreground 1 top 2>/dev/null | sed -e 's|\x1B[^m]*m||g' | cat -v | sed -e 's|\[.||g' -e 's|\^.||g')"
	Value="$(echo TrimAndSingle $TopReport | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
#	TempFile="/tmp/IOWait.$$"
#	printf '%s' "$Value" > "$TempFile"
#	Value="$(cat -v "$TempFile")"
#	rm -f "$TempFile"
	Value="$(printf '%s' "$Value" | sed -e 's|, |\n|g' | grep -e ' wa$' | cut -f 1 -d ',' | cut -f 1 -d '.')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

PercentLoad ()
# Returns the load average last 1|5|15 minutes x 100 / CPU cores
# Depends on functions: Is_IntegerNr
# Depends on software packages: grep, sed, perl-base
{
	local Minutes="$1"
	local MinutesColumn=0
	local CpuCoresNr=0
	local Value=''
	
	case "$Minutes" in
		1 ) MinutesColumn=1 ;;
		5 ) MinutesColumn=2 ;;
		15 ) MinutesColumn=3 ;;
	esac
	if [ $MinutesColumn -ne 0 ] ; then
		CpuCoresNr=$(ArgumentsNumber () { printf '%s' $#; }; ArgumentsNumber $(grep "processor" /proc/cpuinfo | cut -f 2 -d ':'))
		if ! Is_IntegerNr "$CpuCoresNr" ; then CpuCoresNr=1 ; fi
		Value="$(echo TrimAndSingle $(env LANG=en w | head -n 1 | perl -pe 's|load.*:|\n|g' | tail -n 1) | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f $MinutesColumn -d ' ' | sed -e 's|,||g' | sed -e 's|\.||g')"
		while [ "$(printf '%s\n' "$Value" | grep -e '^0')" != "" ] && [ "$Value" != "0" ] ; do
			Value="$(printf '%s\n' "$Value" | cut -c 2-)"
		done
		if Is_IntegerNr "$Value" ; then
			printf '%s\n' "$((Value / CpuCoresNr))"
		fi
	fi
}

WaitForCalm ()
# Syntax as a sentence: WaitForCalm $MaxPercent [$StopFile] [$MaxSeconds] [$PrecisionSeconds]
# Description: Sleeps until load average & IOWait are descending and under specified percent.
# Expected parameters:
#	$1	Average load expecting to be lower (x100)
#	$2	(optional) Signal file to end unconditionally when it's present.
#	$3	(optional) Maximum seconds number to wait. If it's specified and $MaxPercent is not reached down, after this seconds wait will end.
#	$4	(optional) Number of seconds for waiting check period. Default is 1
#	
# Depends on functions: PercentLoad Is_IntegerNr IOWait
# Depends on software packages: (none)
{
	local MaxPercent="$1"
	local StopFile="$2"
	local MaxSeconds="$3"
	local PrecisionSeconds="$4"
	local WaitFractionS=0
	local CurSecondsLeft=0
	local Load1=''
	local LastLoad1=0
	local OldLoad1=0
	local Load5=''
	local IOWait_Cur=''
	local IOWait_Pre=''
	local Max5=''
	local Calm=''
	local TotalSeconds=0
	
	if ! Is_IntegerNr "$MaxSeconds" || [ $MaxSeconds -le 0 ] ; then MaxSeconds=999999999999999999 ; fi
	if ! Is_IntegerNr "$PrecisionSeconds" ; then PrecisionSeconds=1 ; fi
	WaitFractionS=$((PrecisionSeconds / 2))
	if [ $WaitFractionS -lt 1 ] ; then WaitFractionS=1 ; fi
	if [ $WaitFractionS -gt 2 ] ; then WaitFractionS=2 ; fi
	if [ "$StopFile" = "" ] ; then WaitFractionS=$PrecisionSeconds ; fi
	Max5=$MaxPercent
	if [ $Max5 -lt 100 ] ; then Max5=100 ; fi
	Load1=$(PercentLoad 1)
	Load5=$(PercentLoad 5)
	if [ $Load1 -le $Load5 ] && [ $Load1 -le $MaxPercent ] && [ $Load5 -lt $Max5 ] ; then
		Calm=1
	fi
	if [ $Load1 -le $((MaxPercent / 2)) ] && [ $Load5 -le $((Max5 / 2)) ] ; then
		Calm=1
	fi
	while [ "$Calm" != "1" ] && [ $TotalSeconds -lt $MaxSeconds ] && [ ! -f "$StopFile" ] ; do
		CurSecondsLeft=$PrecisionSeconds
		while [ $CurSecondsLeft -gt 0 ] && [ ! -f "$StopFile" ] ; do
			sleep $WaitFractionS
			CurSecondsLeft=$((CurSecondsLeft - WaitFractionS))
			TotalSeconds=$((TotalSeconds + WaitFractionS))
		done
		Load1=$(PercentLoad 1)
		Load5=$(PercentLoad 5)
		IOWait_Pre=$IOWait_Cur
		IOWait_Cur=$(IOWait)
		if [ $Load1 -le $LastLoad1 ] &&  [ $LastLoad1 -le $OldLoad1 ] &&  [ $Load1 -lt $OldLoad1 ] && [ $Load1 -le $MaxPercent ] && [ $Load5 -lt $Max5 ] ; then
			# If load is currently descending and indicators (1,5) aren't over MaxPercent, calm assumable.
			Calm=1
		fi
		if [ $Load1 -le $Load5 ] && [ $Load1 -le $MaxPercent ] && [ $Load5 -lt $Max5 ] ; then
			# If load is generally descending and indicators (1,5) aren't over MaxPercent, calm assumable.
			Calm=1
		fi
		if [ $Load1 -le $((MaxPercent / 2)) ] && [ $Load5 -le $((Max5 / 2)) ] ; then
			# If none of indicators (1,5,15) are over half MaxPercent, calm assumable too.
			if [ $(PercentLoad 15) -le $((Max5 / 2)) ] ; then
				Calm=1
			fi
		fi
		if ! Is_IntegerNr "$IOWait_Pre" || [ $IOWait_Cur -ge $IOWait_Pre ] || [ $IOWait_Pre -ge $MaxPercent ] ; then
			Calm=0
		fi
		if [ $Load1 -le 0 ] && [ $IOWait_Cur -le 0 ] ; then
			Calm=1
		fi
		LastLoad1=$Load1
		OldLoad1=$LastLoad1
	done
}

ExecuteString ()
# Description: Writes specified parameters to a shell script, and runs the script.
# Notes:
#	- Defined functions are not copied to temporay script, so don't make it call them
#	- In some rare case, neither eval/command/exec may work well
# Depends on functions: NuevoTemporalSeguro
# Depends on software packages: (none)
{
	local ScriptFile=""
	local ArgumentNr=0
	local CurrentArgument=""
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	ScriptFile="$(NuevoTemporalSeguro '' sh "$DirTempX")"
	printf '%s\n' '#!/bin/sh' > "$ScriptFile"
	while [ $# -gt 0 ] ; do
		CurrentArgument="$1"
		ArgumentNr=$((ArgumentNr + 1))
		if [ $ArgumentNr -gt 1 ] ; then
			printf ' ' >> "$ScriptFile"
		fi
		if [ "$CurrentArgument" = "" ] ; then CurrentArgument="''" ; fi
		if [ "$CurrentArgument" != "\"\"" ] && [ "$CurrentArgument" != "''" ] ; then
			EvalArgument="$(eval printf '%s' "$CurrentArgument" 2>/dev/null)"
			LastStatus=$?
			if [ $LastStatus -eq 0 ] ; then CurrentArgument="$EvalArgument" ; fi
		fi
		printf '%s' "$CurrentArgument" >> "$ScriptFile"
		shift
	done
	printf '\n' >> "$ScriptFile"
	printf '%s\n' 'exit $?' >> "$ScriptFile"
	chmod u+x "$ScriptFile"
	"$ScriptFile"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm "$ScriptFile"
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}


##### INITSCRIPT TEMPLATE BASE FUNCTIONS #####

LogService_Begin ()
# Syntax as a sentence: LogService_Begin $Message
{
	local Message="$1"
	local CallType=""
	local LogDir=''
	
	if [ $INIT_SCRIPT_InitCall -ne 1 ] ; then
		CallType=" ${ParO} Manual ${ParC}"
#	else
#		CallType=" ${ParO}INIT_SCRIPT_InitCall${ParC}"
	fi
	if [ -r /lib/lsb/init-functions ] ; then
		if [ "$(cat /lib/lsb/init-functions | grep -e '^log_daemon_msg ()')" != "" ] ; then
			log_daemon_msg "$Message" "$ServiceName"
		else
			LogService_Pending="${LogService_Pending}${Message}"
		fi
	else
		printf '%s' "$Message"
	fi
	LogDir="$(Dirname "$MainControllerLog")"
	MkdirAndOrPublic "$LogDir"
#	printf "%(%Y-%m-%dT%T%z)T${CallType} $Message" >> "$MainControllerLog"
	printf "$(date '+%Y-%m-%dT%T%z')${CallType} $Message" >> "$MainControllerLog"
}

LogService_StatusEnd ()
# Syntax as a sentence: LogService_StatusEnd $StatusCode
{
	local StatusCode=$1
	
	if [ -r /lib/lsb/init-functions ] ; then
		if [ "$(cat /lib/lsb/init-functions | grep -e '^log_end_msg ()')" != "" ] ; then
			if [ "$LogService_Pending" != "" ] ; then printf '%s' "$LogService_Pending" ; fi
			log_end_msg $StatusCode
		else
			if [ $StatusCode -eq 0 ] ; then
				log_success_msg "$LogService_Pending [ OK ]"
			else
				log_failure_msg "$LogService_Pending [fail]"
			fi
		fi
	else
		if [ "$LogService_Pending" != "" ] ; then printf '%s' "$LogService_Pending" ; fi
		if [ $StatusCode -eq 0 ] ; then
			printf '%s\n' ' [ OK ]'
		else
			printf '%s\n' ' [fail]'
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' ' [ OK ]' >> "$MainControllerLog"
	else
		printf '%s\n' " [fail] $StatusCode" >> "$MainControllerLog"
	fi
	LogService_Pending=""
}

MkdirAndOrPublic ()
# Syntax as a sentence: MkdirAndOrPublic $NewPath [$OwningForNewElements] [$PermissionsForNewElements] [$MorePermissions]
# THIS FUNCTION IS DEVELOPED AT PROJECT: init_script
# THIS FUNCTION IS COPIED TO PROJECTS: script.sh
# Description: Similar job as "mkdir -p" but is allows owner setting and permissions and different behaviour by $DataIsPublic variable
# Expected parameters:
#	$1	Directory path to create as necessary
#	$2	(optional) User, :Group or User:Group specification as allowed by chown.
#	$3	(optional) Permissions specification as allowed by chmod. Environment variable $DataIsPublic will override permissions to others can read or not
#	$4	(optional) Additional permissions specification to set (useful for g+s not being applied by MkdirAndOrPublic)
# Notes:
#	- Owners/permissions are only set when creating subdirectories; not when they already exist.
#	- Purpose on not using MkdirPP() is to avoid its large dependencies.
# Depends on functions: (none)
# Depends on software packages: (none)
{
	local NewPath="$1"
	local OwningForNewElements="$2"
	local PermissionsForNewElements="$3"
	local MorePermissions="$4"
	local CurrentElement=''
	local PreviousDir=''
	local LastStatus=0
	local StatusCode=0
	
	PreviousDir="$(pwd)"
	IFS='/' ; for CurrentElement in $NewPath ; do unset IFS
#	IFS="$(printf "/")" ; for CurrentElement in $NewPath ; do unset IFS
		if [ $StatusCode -eq 0 ] ; then
			if [ "$CurrentElement" = "" ] ; then
				cd /
			else
				if [ -e "$CurrentElement" ] ; then
					cd "$CurrentElement"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					mkdir "$CurrentElement"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $StatusCode -eq 0 ] ; then
						if [ "$OwningForNewElements" != "" ] && [ "$OwningForNewElements" != "." ] ; then
							chown "$OwningForNewElements" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$PermissionsForNewElements" != "" ] && [ "$PermissionsForNewElements" != "." ] ; then
							chmod "$PermissionsForNewElements" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$MorePermissions" != "" ] && [ "$MorePermissions" != "." ] ; then
							chmod "$MorePermissions" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$DataIsPublic" = "1" ] ; then
							chmod a+rX "$CurrentElement"
						else
							chmod g-w,o= "$CurrentElement"
						fi
						cd "$CurrentElement"
					fi
				fi
			fi
		fi
	done
	cd "$PreviousDir"
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

ServiceVersion ()
{
	local Value=""
	
	Value="$(cat "$MeExecutable" | grep -ie '^# .*Service.* version:' | head -n 1 | cut -f 2- -d ':')"
	if [ "$Value" = "" ] && [ "$ServiceName" != "" ] ; then
		Value="$(cat "$MeExecutable" | grep -ie "^# .*${ServiceName}.* version:" | head -n 1 | cut -f 2- -d ':')"
	fi
	if [ "$Value" != "" ] ; then
			echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g'
	fi
}

ServiceCopyright ()
{
	local Value=""
	
	Value="$(cat "$MeExecutable" | grep -ie '^#Copyright' -ie '^# Copyright' | head -n 1 | cut -f 2- -d ' ')"
	if [ "$Value" != "" ] ; then
			echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g'
	fi
}

TemplateVersion ()
{
	local Value=""
	
	Value="$(cat "$MeExecutable" | grep -ie '^# .*template.* version: ' | head -n 1 | cut -f 2- -d ':')"
	if [ "$Value" = "" ] ; then
		Value="$(cat "$MeExecutable" | grep -ie "^# .*init.*script.* version:" | head -n 1 | cut -f 2- -d ':')"
	fi
	if [ "$Value" != "" ] ; then
			echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g'
	fi
}

TemplateCopyright ()
{
	local Value=""
	
	Value="$(cat "$MeExecutable" | grep -ie '^#Copyright' | grep -ie '^# Copyright' | head -n 2 | tail -n 1 | cut -f 2- -d ' ')"
	if [ "$Value" = "$(ServiceCopyright)" ] ; then Value="" ; fi
	if [ "$Value" != "" ] ; then
			echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g'
	fi
}

DeleteIfNotMe ()
{
	local FileToDelete="$1"
	local StatusCode=0
	
	if [ "$FileToDelete" != "" ] && [ "$FileToDelete" != "$MeCallFile" ] && [ "$(ReadlinkF "$FileToDelete")" != "$MeCallFile" ] && [ "$FileToDelete" != "$MeExecutable" ] && [ "$(ReadlinkF "$FileToDelete")" != "$MeExecutable" ] ; then
		rm -fr "$FileToDelete"
		LastStatus=$?
	fi
	return $StatusCode
}

FoundServiceScript ()
{
	local Value=''
	if [ -x "/etc/init.d/${ServiceName}" ] ; then Value="/etc/init.d/${ServiceName}" ; fi
	if [ -x "/etc/rc.d/init.d/${ServiceName}" ] ; then Value="/etc/rc.d/init.d/${ServiceName}" ; fi
	if [ -x "/bin/${ServiceName}" ] ; then Value="/bin/${ServiceName}" ; fi
	if [ -x "/sbin/${ServiceName}" ] ; then Value="/sbin/${ServiceName}" ; fi
	if [ -x "/usr/bin/${ServiceName}" ] ; then Value="/usr/bin/${ServiceName}" ; fi
	if [ -x "/usr/sbin/${ServiceName}" ] ; then Value="/usr/sbin/${ServiceName}" ; fi
	if [ -x "/usr/local/bin/${ServiceName}" ] ; then Value="/usr/local/bin/${ServiceName}" ; fi
	if [ -x "/usr/local/sbin/${ServiceName}" ] ; then Value="/usr/local/sbin/${ServiceName}" ; fi
	if [ -x "/etc/init.d/${ServiceName}.sh" ] ; then Value="/etc/init.d/${ServiceName}.sh" ; fi
	if [ -x "/etc/rc.d/init.d/${ServiceName}.sh" ] ; then Value="/etc/rc.d/init.d/${ServiceName}.sh" ; fi
	if [ -x "/bin/${ServiceName}.sh" ] ; then Value="/bin/${ServiceName}.sh" ; fi
	if [ -x "/sbin/${ServiceName}.sh" ] ; then Value="/sbin/${ServiceName}.sh" ; fi
	if [ -x "/usr/bin/${ServiceName}.sh" ] ; then Value="/usr/bin/${ServiceName}.sh" ; fi
	if [ -x "/usr/sbin/${ServiceName}.sh" ] ; then Value="/usr/sbin/${ServiceName}.sh" ; fi
	if [ -x "/usr/local/bin/${ServiceName}.sh" ] ; then Value="/usr/local/bin/${ServiceName}.sh" ; fi
	if [ -x "/usr/local/sbin/${ServiceName}.sh" ] ; then Value="/usr/local/sbin/${ServiceName}.sh" ; fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

LsbHeaderValue ()
# See http://refspecs.linuxfoundation.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/initscrcomconv.html
{
	local HeaderKey="$1"
	local VariableKey=""
	local Value=""
	
	if [ "$(printf '%s\n' "$HeaderKey" | grep -e '-')" != "" ] ; then
		VariableKey="$(printf '%s\n' "$HeaderKey" | sed -e 's|-||g')"
	else
		VariableKey="${HeaderKey}-"
	fi
	Value="$(cat "$MeExecutable" | grep -ie "^# ${HeaderKey}:" | grep -ve "$VariableKey" | head -n 1 | cut -f 2- -d ':')"
	Value="$(echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

BreakingControls ()
# THIS FUNCTION IS DEVELOPED AT PROJECT: init_script
# THIS FUNCTION IS COPIED TO PROJECTS: script.sh
{
	local ReservedWords="reload,create,delete,list,details,uninstall,purge,start,stop,restart,status,enable,disable"
	
	if [ $(id -g) -ne 0 ] && [ "$RootRequired" != "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 [ "$(printf '%s\n' ",${ReservedWords}," | grep -ie ",${ServiceName},")" != "" ] ; then
		printf '%s\n' "${sERROR}E: ServiceName cannot match the reserved word \"${ServiceName}\"${fRESET}" 1>&2
		exit 92
	fi
	if [ "$(printf '%s\n' ",${ReservedWords}," | grep -ie ",${ProgramName},")" != "" ] ; then
		printf '%s\n' "${sERROR}E: ProgramName cannot match the reserved word \"${ProgramName}\"${fRESET}" 1>&2
		exit 92
	fi
	if [ "$(printf '%s\n' ",${ReservedWords}," | grep -ie ",${ChildSingularName},")" != "" ] ; then
		printf '%s\n' "${sERROR}E: ChildSingularName cannot match the reserved word \"${ChildSingularName}\"${fRESET}" 1>&2
		exit 92
	fi
	if [ "$(printf '%s\n' ",${ReservedWords}," | grep -ie ",${ChildsPluralName},")" != "" ] ; then
		printf '%s\n' "${sERROR}E: ChildsPluralName cannot match the reserved word \"${ChildsPluralName}\"${fRESET}" 1>&2
		exit 92
	fi
}

ListeningNetPortsByPids ()
{
	local ListeningData=""
	local CurrentPid=""
	local CurrentValues=""
	local LastStatus=$?
	local Value=''
	
	# Very old netstat versions don't have options -l -p and aren't useful
	ListeningData="$(env LANG=en netstat -tulnp 2>/dev/null | tr -s ' ' | grep -e ' LISTEN ' -e ' LISTEN$' -e ' ESTABLISHED ' -e ' ESTABLISHED$' | cut -f 1,4,7 -d ' ')"
	LastStatus=$?
	if [ $LastStatus -eq 0 ] ; then
		unset IFS ; for CurrentPid in $* ; do
			CurrentValues="$(printf '%s\n' "$ListeningData" | grep -e " ${CurrentPid}/" | cut -f 1,2 -d ' ' | sed -e 's| .*:|/|g')"
			if [ "$CurrentValues" != "" ] ; then Value="$Value $CurrentValues" ; fi
		done
		Value="$(echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

SystemdUnitsFromSystemv ()
# See https://www.freedesktop.org/software/systemd/man/systemd.special.html
{
	local SystemvFacilities="$1"
	local CurrentUnit=""
	local Value=""
	
	SystemvFacilities="$(printf '%s\n' "$SystemvFacilities" | sed -e 's|\$||g')"
	unset IFS ; for CurrentUnit in $SystemvFacilities ; do
		case "$CurrentUnit" in
			"local_fs" ) Value="$Value local-fs.target" ;;
			"network" )
				# When NIC is declared as manual or ethernet cable is unplugged or manual/unplugged is a bridge member, networking.service this fails:
				# "A dependency job for myservicename.service failed."
				if Is_Executable systemctl ; then
					if [ "$(systemctl list-unit-files | grep -e '^networking.service ')" != "" ] ; then
#						Value="$Value networking.service network-online.target"
						Value="$Value network-online.target"
					else
						Value="$Value network.target network-online.target"
					fi
				else
#					Value="$Value networking.service network-online.target"
					Value="$Value network-online.target"
				fi
				;;
			"named" ) Value="$Value nss-lookup.target" ;;
			"portmap" ) Value="$Value rpcbind.target" ;;
			"remote_fs" ) Value="$Value remote-fs.target" ;;
			"syslog" )
				if Is_SystemdUnitExisting syslog.service ; then
					Value="$Value syslog.service"
				else
					if Is_SystemdUnitExisting rsyslog.service ; then
						Value="$Value rsyslog.service"
					else
						Value="$Value systemd-journald.service"
					fi
				fi
				;;
			"time" ) Value="$Value time-sync.target" ;;
#			"all" ) Value="$Value runlevel2.target networking.service" ;;
			"all" ) Value="$Value runlevel2.target network-online.target" ;;
			"0" ) Value="$Value runlevel0.target" ;;
			"1" ) Value="$Value runlevel1.target" ;;
			"S" ) Value="$Value runlevel1.target" ;;
			"s" ) Value="$Value runlevel1.target" ;;
			"2" ) Value="$Value runlevel2.target" ;;
			"3" ) Value="$Value runlevel3.target" ;;
			"4" ) Value="$Value runlevel4.target" ;;
			"5" ) Value="$Value runlevel5.target" ;;
			"6" ) Value="$Value runlevel6.target" ;;
			* )	if [ -f "/lib/systemd/system/${CurrentUnit}.service" ] || [ -f /etc/systemd/system/${CurrentUnit}.service ] ; then
					CurrentUnit="${CurrentUnit}.service"
				fi
				Value="$Value $CurrentUnit"
				;;
		esac
	done
	Value="$(echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

SystemdUnitsFromLsbHeader ()
{
	local HeaderKey="$1"
	local Value=""
	
	Value="$(LsbHeaderValue "$HeaderKey")"
	Value="$(SystemdUnitsFromSystemv "$Value")"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

UpstartJobsFromSystemv ()
# See https://www.freedesktop.org/software/systemd/man/systemd.special.html
{
	local SystemvFacilities="$1"
	local CurrentJob=""
	local Value=""
	
	SystemvFacilities="$(printf '%s\n' "$SystemvFacilities" | sed -e 's|\$||g')"
	unset IFS ; for CurrentJob in $SystemvFacilities ; do
		case "$CurrentJob" in
			"local_fs" ) Value="$Value local-filesystems" ;;
			"network" ) Value="$Value net-device-up" ;;
			"named" ) Value="$Value net-device-up IFACE!=lo" ;;
			"portmap" ) Value="$Value portmap" ;;
			"remote_fs" ) Value="$Value remote-filesystems" ;;
			"syslog" ) Value="$Value rsyslog" ;;
			"time" ) Value="$Value runlevel__[12345]" ;;
			"all" ) Value="$Value runlevel__[2345] net-device-up__IFACE!=lo" ;;
			* )	if [ -f "/etc/init/${CurrentJob}.conf" ] ; then
					CurrentJob="${CurrentJob}"
				fi
				Value="$Value $CurrentJob"
				;;
		esac
	done
	if [ "$Value" != "" ] ; then
		echo $Value | sed -e 's|^ ||g'
	fi
}

UpstartJobsFromLsbHeader ()
{
	local HeaderKey="$1"
	local Value=""
	
	Value="$(LsbHeaderValue "$HeaderKey")"
	Value="$(UpstartJobsFromSystemv "$Value")"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

UpstartRunlevelsFromSystemv ()
# See https://www.freedesktop.org/software/systemd/man/systemd.special.html
{
	local SystemvRunlevels="$1"
	local CurrentRunlevel=""
	local Value=""
	
	SystemvRunlevels="$(printf '%s\n' "$SystemvRunlevels" | sed -e 's|\$||g')"
	unset IFS ; for CurrentRunlevel in $SystemvRunlevels ; do
		if [ "$(printf '%s\n' "$CurrentRunlevel" | grep -e '^.$')" != "" ] ; then
			if [ "$CurrentRunlevel" = "S" ] || [ "$CurrentRunlevel" = "s" ] ; then CurrentRunlevel="1" ; fi
			Value="${Value}${CurrentRunlevel}"
		fi
	done
	if [ "$Value" != "" ] ; then
		printf '%s\n' "runlevel [${Value}]"
	fi
}

UpstartRunlevelsFromLsbHeader ()
{
	local HeaderKey="$1"
	local Value=""
	
	Value="$(LsbHeaderValue "$HeaderKey")"
	Value="$(UpstartRunlevelsFromSystemv "$Value")"
	if [ "$Value" != "" ] ; then printf '%s\n' $Value ; fi
}

Is_SystemdUnitExisting ()
# Syntax as a function: Is_SystemdUnitExisting $systemd_unit_name
# Description: Returns (exitcode 0) TRUE if specified unit can be listed with systemctl; or FALSE otherwise.
# Use example (without brackets []):
#	if Is_SystemdUnitExisting syslog.service ; then echo "Unit exists." ; fi
# Depends on functions: (none)
# Depends on software packages: (none)
{
	local UnitName="$1"
	local TrueCode=254  # 254=FALSE
	
	systemctl list-unit-files $UnitName >/dev/null
	if [ $? -eq 0 ] ; then TrueCode=0 ; fi
	return $TrueCode
}

AddParsedToSystemdProperty ()
# Syntax as a sentence: AddParsedToSystemdProperty $LsbHeaders $UnitProperty $UnitFile $IncludeNotFoundUnits
# Expected parameters:
#	$1	Space-separated list of LSB headers to parse, such as "Required-Start" or "Should-Start Should-Stop"
#	$2	Systemd unit file property to write, such as "Requires"
#	$3	File to append line(s)
#	$4	What to do for parsed unit names but not found ar Systemd: 0=Omit 1=Include anyway
{
	local LsbHeaders="$1"
	local CurLsbHeader=''
	local UnitProperty="$2"
	local UnitFile="$3"
	local IncludeNotFoundUnits="$4"
	local ParsedUnits=''
	local CurUnit=''
	local UnitsToWrite=''
	local CommentedPropertyWithOmmited=''
	local LastStatus=0
	local StatusCode=0

	
	unset IFS ; for CurLsbHeader in $LsbHeaders ; do
		ParsedUnits="$ParsedUnits $(SystemdUnitsFromLsbHeader "$CurLsbHeader")"
	done
	if [ "$ParsedUnits" != "" ] ; then
		ParsedUnits="$(printf '%s' "$ParsedUnits" | tr -s ' ' '\n' | awk '!seen[$0]++')"
		ParsedUnits="$(echo TrimAndSingle $ParsedUnits | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		unset IFS ; for CurUnit in $ParsedUnits ; do
			if ! Is_SystemdUnitExisting $CurUnit ; then
				if [ "$IncludeNotFoundUnits" = "0" ] ; then
					printf '%s\n' "${sWARN}W: Unit $CurUnit not found in Systemd and omitting for $UnitProperty property${fRESET}" 1>&2
					CommentedPropertyWithOmmited="#${UnitProperty}=${ParsedUnits}"
				else
					printf '%s\n' "${sWARN}W: Unit $CurUnit not found in Systemd but registering at $UnitProperty property anyway!${fRESET}" 1>&2
					if [ "$UnitsToWrite" != "" ] ; then UnitsToWrite="$UnitsToWrite " ; fi
					UnitsToWrite="${UnitsToWrite}${CurUnit}"
				fi
			else
				if [ "$UnitsToWrite" != "" ] ; then UnitsToWrite="$UnitsToWrite " ; fi
				UnitsToWrite="${UnitsToWrite}${CurUnit}"
			fi
		done
		if [ "$CommentedPropertyWithOmmited" != "" ] ; then
			printf '%s\n' "$CommentedPropertyWithOmmited" >> "$UnitFile"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ "$UnitsToWrite" != "" ] ; then
			printf '%s\n' "${UnitProperty}=${UnitsToWrite}" >> "$UnitFile"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

InstallSystemService ()
# Description: Registers system init profile for this service
# Example: InstallSystemService /tmp/myservice.sh myservice
{
	local SourceScript="$1"
	local ServiceName="$2"
	local InitProfileFile=""
	local ParsedUnits=""
	local CurUnit=''
	local UnitsToWrite=''
	local MoreUnits=""
	local PreviousUmask=""
	local TheTemplateVersion=""
	local ChildParameter=""
	local LastStatus=0
	local StatusCode=0

	if [ ! -f "$SourceScript" ] ; then
		printf '%s\n' "${sERROR}E: File not found: ${SourceScript}${fRESET}" 1>&2
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$InitSoftware" != "systemd" ] && [ "$InitSoftware" != "upstart" ] && [ "$InitSoftware" != "systemv" ] ; then
			printf '%s\n' "${sERROR}E: Unknown init software detected: ${InitSoftware}${fRESET}" 1>&2
			LastStatus=50 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$ServiceEnabler" = "" ] && [ "$InitProfilesDir" = "" ] ; then
			printf '%s\n' "${sERROR}E: $InitSoftware init enablement utility not found ${ParO}systemctl, insserv, update-rc.d,...${ParC}.${fRESET}" 1>&2
			LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$InitExecutableParser" != "" ] && [ "$InitExecutableParser" != "$ProgramExecutablePath" ] ; then
		PreviousUmask="$(umask)"
		rm -f "$InitExecutableParser"	# Previous could be a link
		printf '%s\n' '#!/bin/sh' > "$InitExecutableParser"
		cat "$MeCallFile" | sed -ne '/BEGIN INIT INFO/,//p' | sed -e '/END INIT INFO/q' >> "$InitExecutableParser"
#		ln -s "$ProgramExecutablePath" "$InitExecutableParser"
		printf '%s\n' "\"${ProgramExecutablePath}\" \"\$@\"" >> "$InitExecutableParser"
		printf '%s\n' "exit \$?" >> "$InitExecutableParser"
		chmod u=rwx,go=rx "$InitExecutableParser"
	fi
	if [ $StatusCode -eq 0 ] && [ "$InitProfilesDir" != "" ] ; then
		TheTemplateVersion="$(TemplateVersion)"
		case "$InitSoftware" in
			"systemd" )
				# Documentation at https://www.freedesktop.org/software/systemd/man/systemd.unit.html and https://www.freedesktop.org/software/systemd/man/systemd.service.html
				InitProfileFile="${InitProfilesDir}/${ServiceName}.service"
				if [ "$InitProfilesDir_0" != "" ] ; then
					InitProfileFile_0="${InitProfilesDir_0}/${ServiceName}.service"
				fi
				cat /dev/null > "$InitProfileFile"
				if [ "$ChildSingularName" != "" ] ; then
					ChildParameter=" %i"
				fi
				printf '%s\n' "# $ServiceName service parameters for systemd" >> "$InitProfileFile"
				printf '%s\n' "# ${ParO}${LongDescription}${ParC}" >> "$InitProfileFile"
				printf '%s\n' "# Note: Many parameters have been parsed from LSB headers of SystemV init script. For example, runlevel*.target" >> "$InitProfileFile"
				printf '%s\n' "" >> "$InitProfileFile"
				printf '%s\n' "[Unit]" >> "$InitProfileFile"
				printf '%s\n' "Description=$ShortDescription" >> "$InitProfileFile"
				if [ "$ServiceDocumentation" != "" ] ; then
					printf '%s\n' "Documentation=$ServiceDocumentation" >> "$InitProfileFile"
				fi
				
				
				AddParsedToSystemdProperty 'Default-Start' 'Wants' "$InitProfileFile" 1
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				AddParsedToSystemdProperty 'Should-Start Should-Stop' 'After' "$InitProfileFile" 0
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				AddParsedToSystemdProperty 'Required-Start Required-Stop' 'Requires' "$InitProfileFile" 1
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				AddParsedToSystemdProperty 'X-Start-Before' 'Before' "$InitProfileFile" 0
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				AddParsedToSystemdProperty 'Default-Stop Should-Stop' '#PartOf' "$InitProfileFile" 1
				# Unlike documentation says, PartOf it's not only limited to stopping and restarting of units but starting too.
				AddParsedToSystemdProperty 'Should-Stop' '#PartOf' "$InitProfileFile" 1
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				AddParsedToSystemdProperty 'Default-Stop' 'Conflicts' "$InitProfileFile" 1
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi

				printf '%s\n' "" >> "$InitProfileFile"
				printf '%s\n' "[Service]" >> "$InitProfileFile"
				if [ "$DaemonizeOnStart" = "0" ] ; then
					printf '%s\n' "Type=oneshot" >> "$InitProfileFile"
				else
					printf '%s\n' "Type=forking" >> "$InitProfileFile"
				fi
				printf '%s\n' "RemainAfterExit=yes" >> "$InitProfileFile" # This avoids that systemd triggers ExecStop just after ExecStart.
				if [ "$StopOnFailure" = "1" ] ; then
					printf '%s\n' "ExecStart=$ProgramExecutablePath start${ChildParameter}" >> "$InitProfileFile"
				else
					printf '%s\n' "ExecStart=-$ProgramExecutablePath start${ChildParameter}" >> "$InitProfileFile"
				fi
				printf '%s\n' "ExecReload=$ProgramExecutablePath reload${ChildParameter}" >> "$InitProfileFile"
				if [ "$MainControllerStays" != "0" ] ; then
					# Otherwise: Systemd logs a failure if pid file disappears (one-shot case)
					printf '%s\n' "PIDFile=$MainControllerPidFile" >> "$InitProfileFile"
				fi
					# Systemd doesn't run ExecStop if service didn't start and/or isn't loaded
#					printf '%s\n' "ExecStop=$ProgramExecutablePath stop${ChildParameter}" >> "$InitProfileFile"
#				else
					# A case as eventoj shows that ExecStopPost is not called. Let's see if this ExecStop workaround implies a repeated call for other cases.
					printf '%s\n' "ExecStop=$ProgramExecutablePath stop${ChildParameter}" >> "$InitProfileFile"
					printf '%s\n' "ExecStopPost=$ProgramExecutablePath stop${ChildParameter}" >> "$InitProfileFile"
#				fi
				if [ "$TimeoutStartSec" != "" ] ; then
					if [ $TimeoutStartSec -ge 0 ] ; then
						printf '%s\n' "TimeoutStartSec=$TimeoutStartSec" >> "$InitProfileFile"
					else
						printf '%s\n' "TimeoutStartSec=infinity" >> "$InitProfileFile"
					fi
				fi
				if [ "$TimeoutStopSec" != "" ] ; then
					if [ $TimeoutStopSec -ge 0 ] ; then
						printf '%s\n' "TimeoutStopSec=$(($TimeoutStopSec + 1))" >> "$InitProfileFile"
					else
						printf '%s\n' "TimeoutStopSec=infinity" >> "$InitProfileFile"
					fi
				fi
				printf '%s\n' "" >> "$InitProfileFile"
				printf '%s\n' "[Install]" >> "$InitProfileFile"
				printf '%s\n' "WantedBy=default.target" >> "$InitProfileFile"
				ParsedUnits="$(SystemdUnitsFromLsbHeader 'Provides')"
				unset IFS ; for CurUnit in $ParsedUnits ; do
					CurUnit="$(printf '%s' "$CurUnit" | cut -f 1 -d '.')"
					if [ "$CurUnit" != "$ServiceName" ] ; then
						if [ "$(printf '%s\n' "$CurUnit" | grep -e '\.')" = "" ] ; then
							CurUnit="${CurUnit}.service"
						fi
						printf '%s\n' "Alias=$CurUnit" >> "$InitProfileFile"
					fi
				done
				chown root:root "$InitProfileFile"
				chmod u=rw,go=r "$InitProfileFile"
				rm -f "${InitProfilesDir}/${ServiceName}@"*".service"
				if [ "$InitProfilesDir_0" != "" ] ; then
					rm -f "$InitProfileFile_0"
					rm -f "${InitProfilesDir_0}/${ServiceName}@"*".service"
				fi
				if [ "$ChildSingularName" != "" ] ; then
					if [ "$DaemonizeChilds" != "0" ] ; then
#						cat "$InitProfileFile" | perl -pe 's|^Type=.*|Type=forking|gi' | perl -pe "s|^PIDFile=.*|PIDFile=${ChildsPidsDir}/%i.pid|gi" | perl -pe 's|^ExecStopPost=|ExecStop=|gi' > "${InitProfilesDir}/${ServiceName}@.service"
						cat "$InitProfileFile" | perl -pe 's|^Type=.*|Type=forking|gi' | perl -pe "s|^PIDFile=.*|PIDFile=${ChildsPidsDir}/%i.pid|gi" > "${InitProfilesDir}/${ServiceName}@.service"
						# Workaround for Systemd bug https://github.com/systemd/systemd/pull/4992
						SetIniVarValue "${InitProfilesDir}/${ServiceName}@.service" DefaultInstance Install '@' = '#Instance(@) means: no instance'
					else
						if [ "$MainControllerStays" = "0" ] ; then
							# If childs act in foreground, they only need own unit template if it's not the main controller who calls them.
#							cat "$InitProfileFile" | perl -pe 's|^Type=.*|Type=oneshot|gi' | perl -pe "s|^PIDFile=.*|PIDFile=${ChildsPidsDir}/%i.pid|gi" | perl -pe 's|^ExecStop=|ExecStopPost=|gi' > "${InitProfilesDir}/${ServiceName}@.service"
							cat "$InitProfileFile" | perl -pe 's|^Type=.*|Type=oneshot|gi' | perl -pe "s|^PIDFile=.*|PIDFile=${ChildsPidsDir}/%i.pid|gi" > "${InitProfilesDir}/${ServiceName}@.service"
							# Workaround for Systemd bug https://github.com/systemd/systemd/pull/4992
							SetIniVarValue "${InitProfilesDir}/${ServiceName}@.service" DefaultInstance Install '@' = '#Instance(@) means: no instance'
						fi
					fi
					if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
						chown root:root "${InitProfilesDir}/${ServiceName}@.service"
						chmod u=rw,go=r "${InitProfilesDir}/${ServiceName}@.service"
					fi
					sed -ie 's| %i||g' "$InitProfileFile"
					sed -ie 's|%i||g' "$InitProfileFile"
				fi
				;;
			"upstart" )
				# Upstart considers service enabled when service profile file is present; better to only create with EnableSystemService()
				sleep 0
				;;
			"systemv" )
				printf '%s\n' "${sERROR}E: No procedure to write a init service profile at ${InitProfilesDir}${fRESET}" 1>&2
				LastStatus=59 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"" )
				# Nothing to do.
				sleep 0
				;;
			* )
				printf '%s\n' "${sERROR}E: Unknown init software: ${InitSoftware}${fRESET}" 1>&2
				LastStatus=50 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
		esac
	fi
	return $StatusCode
}

UnInstallSystemService ()
# Description: Disables and removes system init profile for this service
# Example: UnInstallSystemService "$0"
{
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		if [ "$InitSoftware" != "systemd" ] && [ "$InitSoftware" != "upstart" ] && [ "$InitSoftware" != "systemv" ] ; then
			printf '%s\n' "${sERROR}E: Unknown init software detected: ${InitSoftware}${fRESET}" 1>&2
			LastStatus=50 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$ServiceEnabler" = "" ] && [ "$InitProfilesDir" = "" ] ; then
			printf '%s\n' "${sERROR}E: $InitSoftware utility for init disable not found ${ParO}systemctl, insserv, update-rc.d,...${ParC}.${fRESET}" 1>&2
			LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		DisableSystemService 0
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$InitProfilesDir" != "" ] ; then
		case "$InitSoftware" in
			"systemd" )
				rm -f "${InitProfilesDir}/${ServiceName}.service"
				rm -f "${InitProfilesDir}/${ServiceName}@*.service"
				if [ "$InitProfilesDir_0" != "" ] ; then
					rm -f "${InitProfilesDir_0}/${ServiceName}.service"
					rm -f "${InitProfilesDir_0}/${ServiceName}@*.service"
				fi
				;;
			"upstart" )
				rm -f "${InitProfilesDir}/${ServiceName}.conf"
				if [ "$InitProfilesDir_0" != "" ] ; then
					rm -f "${InitProfilesDir_0}/${ServiceName}.conf"
				fi
				;;
			"systemv" )
				sleep 0
				;;
			"" )
				sleep 0
				;;
			* )
				printf '%s\n' "${sERROR}E: Unknown init software: ${InitSoftware}${fRESET}" 1>&2
				LastStatus=50 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
		esac
	fi
	if [ $StatusCode -eq 0 ] && [ "$InitExecutableParser" != "" ] && [ "$InitExecutableParser" != "$ProgramExecutablePath" ] ; then
		rm -f "$InitExecutableParser"
	fi
	return $StatusCode
}

SystemProgramIsEnabled ()
# THIS FUNCTION IS DEVELOPED AT PROJECT: init_script
# THIS FUNCTION IS COPIED TO PROJECTS: gesdonis
# Returns (exitcode) true if the main service is fully enabled to start on system boot. Otherwise false.
# Syntax (without brackets []):
#	if SystemProgramIsEnabled ; then printf '%s\n' "Yes." ; fi
{
	local RcBase=''
	local InRcLocal=0
	local ValueBoolean=1
	
	if [ "$(cat "$RcLocal" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | sed -e '/^exit/q' | grep -E "^(${ProgramExecutablePath}|${ServiceName}) start")" != "" ] ; then InRcLocal=1 ; fi
	if [ "$(cat /etc/rc.d/rc.local 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | sed -e '/^exit/q' | grep -E "^(${ProgramExecutablePath}|${ServiceName}) start")" != "" ] ; then InRcLocal=1 ; fi
	if [ "$(cat /etc/rc.local 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | sed -e '/^exit/q' | grep -E "^(${ProgramExecutablePath}|${ServiceName}) start")" != "" ] ; then InRcLocal=1 ; fi
	SystemvEnablementData="$(cat "/etc/default/${ServiceName}" 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ie '^ENABLED' -ie '^RUN=')"
	if [ "$(printf '%s\n' "$SystemvEnablementData" | grep -ie '^ENABLED=')" != "" ] ; then
		SystemvEnablementData="$(printf '%s\n' "$SystemvEnablementData" | grep -ie '^ENABLED=' | tail -n 1)"
	else
		SystemvEnablementData="$(printf '%s\n' "$SystemvEnablementData" | grep -ie '^RUN=' | tail -n 1)"
	fi
	if [ $InRcLocal -eq 1 ] ; then
		# Init system independent way ~ hard way to invoke program
		ValueBoolean=0
		if [ "$SystemvEnablementData" != "" ] ; then
			# Manual filter present
			if [ "$(printf '%s\n' "$SystemvEnablementData" | grep -iE '=(1|y)')" = "" ] ; then
				# Manual filter says something but "enabled"
				ValueBoolean=1
			fi
		fi
	else
		if [ "$SystemvEnablementData" != "" ] ; then
			# Manual filter present
			if [ "$(printf '%s\n' "$SystemvEnablementData" | grep -iE '=(1|y)')" != "" ] ; then
				# Manual filter says "enabled"
				ValueBoolean=0
			fi
		else
			# No manual filter; enabled depends only on init system
			ValueBoolean=0
		fi
		if [ $ValueBoolean -eq 0 ] ; then
			ValueBoolean=1
			case "$InitSoftware" in
				"systemd" )
					systemctl --quiet --system is-enabled "${ServiceName}.service" 2>/dev/null
					LastStatus=$?
					if [ $LastStatus -eq 0 ] ; then ValueBoolean=0 ; fi
					if [ $ValueBoolean -eq 1 ] && [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] && [ "$(command -v /sbin/runlevel 2>/dev/null)$(command -v runlevel 2>/dev/null)" != "" ] ; then
						# It seems there is a SystemV compatibility layer; must check for upgraded environments from SystemV to Systemd
						if [ -d /etc/rc1.d ] ; then RcBase=/etc ; fi
						if [ -d /etc/rc.d/rc1.d ] ; then RcBase=/etc/rc.d ; fi
						if [ -d "$RcBase" ] ; then
							unset IFS ; for CurrentRc in 1 2 3 4 5 ; do
								if [ $ValueBoolean -ne 0 ] && [ -d "${RcBase}/rc${CurrentRc}.d" ] ; then
									IFS="$(printf '\n\b')" ; for CurrentLink in $(ls -1 "${RcBase}/rc${CurrentRc}.d"/ 2>/dev/null | grep -ie '^S') ; do unset IFS	#"
										if [ "$(ReadlinkF "${RcBase}/rc${CurrentRc}.d/${CurrentLink}" | grep -e "/${ServiceName}$")" != "" ] ; then ValueBoolean=0 ; fi	#"
									done
								fi
							done
						fi
					fi
					;;
				"upstart" )
					# Upstart considers it enabled when service profile file is present
					if [ "$InitProfilesDir" != "" ] ; then
						if [ -f "${InitProfilesDir}/${ServiceName}.conf" ] ; then ValueBoolean=0 ; fi
						if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}.conf" ] ; then ValueBoolean=0 ; fi
					fi
					if [ $ValueBoolean -eq 1 ] && [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] && [ "$(command -v /sbin/runlevel 2>/dev/null)$(command -v runlevel 2>/dev/null)" != "" ] ; then
						# It seems there is a SystemV compatibility layer; must check for upgraded environments from SystemV to Upspart
						if [ -d /etc/rc1.d ] ; then RcBase=/etc ; fi
						if [ -d /etc/rc.d/rc1.d ] ; then RcBase=/etc/rc.d ; fi
						if [ -d "$RcBase" ] ; then
							unset IFS ; for CurrentRc in 1 2 3 4 5 ; do
								if [ $ValueBoolean -ne 0 ] && [ -d "${RcBase}/rc${CurrentRc}.d" ] ; then
									IFS="$(printf '\n\b')" ; for CurrentLink in $(ls -1 "${RcBase}/rc${CurrentRc}.d"/ 2>/dev/null | grep -ie '^S') ; do unset IFS	#"
										if [ "$(ReadlinkF "${RcBase}/rc${CurrentRc}.d/${CurrentLink}" | grep -e "/${ServiceName}$")" != "" ] ; then ValueBoolean=0 ; fi	#"
									done
								fi
							done
						fi
					fi
					;;
				"systemv" )
					if [ "$ServiceEnabler" = "chkconfig" ] ; then
						if [ "$(chkconfig --list "$ServiceName" | grep -iE '(1|2|3|4|5):on')" ] ; then ValueBoolean=0 ; fi
					else
						RcBase=""
						if [ -d /etc/rc1.d ] ; then RcBase=/etc ; fi
						if [ -d /etc/rc.d/rc1.d ] ; then RcBase=/etc/rc.d ; fi
						if [ -d "$RcBase" ] ; then
							unset IFS ; for CurrentRc in 1 2 3 4 5 ; do
								if [ $ValueBoolean -ne 0 ] && [ -d "${RcBase}/rc${CurrentRc}.d" ] ; then
									IFS="$(printf '\n\b')" ; for CurrentLink in $(ls -1 "${RcBase}/rc${CurrentRc}.d"/ 2>/dev/null | grep -ie '^S') ; do unset IFS	#"
										if [ "$(ReadlinkF "${RcBase}/rc${CurrentRc}.d/${CurrentLink}" | grep -e "/${ServiceName}$")" != "" ] ; then ValueBoolean=0 ; fi	#"
									done
								fi
							done
						else
							if [ -x "/etc/init.d/${ServiceName}" ] ; then ValueBoolean=0 ; fi
						fi
					fi
					;;
			esac
		fi
	fi
	return $ValueBoolean
}

RecommendedInvocation ()
# THIS FUNCTION IS DEVELOPED AT PROJECT: init_script
# THIS FUNCTION IS COPIED TO PROJECTS: gesdonis
{
	local Action="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local Object="$1"
	local ChildsObjects=""
	local SudoPrefix=''
	local SudoSuffix=''
	local Option="$Action"
	local Value=""
	
	if [ "$RootRequired" != "0" ] && [ $(id -u) -ne 0 ] ; then
		if [ "$(command -v sudo 2>/dev/null)" != "" ] ; then
			SudoPrefix="sudo "
		else
			SudoPrefix='su -c "'
			SudoSuffix='"'
		fi
	fi
	if [ "$ChildsPluralName" != "" ] && [ "$Object" != "" ] ; then
		ChildsObjects=" $@"
	fi
	case "$Option" in
		"start" ) Option="start-stop" ;;
		"stop" ) Option="start-stop" ;;
		"restart" ) Option="start-stop" ;;
		"reload" ) Option="start-stop" ;;
	esac
	if [ "$Object" = "" ] || [ "$ChildsPluralName" != "" ] ; then
		# Child treatment is allowed if childs are supported.
		if [ $(id -u) -eq 0 ] && SystemProgramIsEnabled ; then	# Pending to replace the use of SystemProgramIsEnabled() by a function like ServiceIsRegistered()
			case "$Option" in
				"enable" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} enable${ChildsObjects}${SudoSuffix}" ;;
				"disable" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} disable${ChildsObjects}${SudoSuffix}" ;;
				"start-stop" )
					if [ "$InitProfilesDir" != "" ] ; then
						case "$InitSoftware" in
							"systemd" )
								if [ "$Object" = "" ] ; then
#									Value="systemctl $Action $ServiceName"
									if [ -f "${InitProfilesDir}/${ServiceName}.service" ] ; then
										Value="systemctl $Action $ServiceName"
									else
										if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}.service" ] ; then
											Value="systemctl $Action $ServiceName"
										else
											Value="$ServiceName $Action"
										fi
									fi
								else
									if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
										Value="systemctl $Action \"${ServiceName}@${Object}\""
									else
										if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
											Value="systemctl $Action \"${ServiceName}@${Object}\""
										else
											Value="$ServiceName ${Action}${ChildsObjects}"
										fi
									fi
								fi
								;;
							"upstart" )
								if [ "$Object" = "" ] ; then
									if [ ! -f "/etc/init/${ServiceName}.conf" ] && [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] && [ "$(command -v /sbin/runlevel 2>/dev/null)$(command -v runlevel 2>/dev/null)" != "" ] ; then
										if [ -x "/etc/init.d/${ServiceName}" ] || [ -x "/etc/rc.d/init.d/${ServiceName}" ] ; then
											# Let's use SystemV compatibility layer; it can be an upgraded environment from SystemV to Systemd
											Value="${SudoPrefix}${ServicePrefix}${ServiceName} ${Action}${ChildsObjects}${SudoSuffix}"
										else
											Value="initctl $Action $ServiceName"
										fi
									else
										Value="initctl $Action $ServiceName"
									fi
								else
									Value="$ServiceName ${Action}${ChildsObjects}"
								fi
								;;
							* ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} ${Action}${ChildsObjects}${SudoSuffix}" ;;
						esac
					else
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} ${Action}${ChildsObjects}${SudoSuffix}"
					fi
					;;
				"status" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} status${ChildsObjects}${SudoSuffix}" ;;
				"create" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} create${ChildsObjects}${SudoSuffix}" ;;
				"delete" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} delete${ChildsObjects}${SudoSuffix}" ;;
				"list" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} list${ChildsObjects}${SudoSuffix}" ;;
				"details" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} details${ChildsObjects}${SudoSuffix}" ;;
				"install" )
					if [ "$ProgramInstaller" = "1" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} install${SudoSuffix}"
					fi
					;;
				"uninstall" )
					if [ "$ProgramInstaller" = "1" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} uninstall${SudoSuffix}"
					fi
					;;
				"purge" )
					if [ "$ProgramInstaller" = "1" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} purge${SudoSuffix}"
					fi
					;;
			esac
		else
			case "$Option" in
				"enable" )
					if [ "$Object" = "" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} enable${ChildsObjects}${SudoSuffix}"
					else
						Value="${ServiceName} enable${ChildsObjects}"
					fi
					;;
				"disable" )
					if [ "$Object" = "" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} disable${ChildsObjects}${SudoSuffix}"
					else
						Value="${ServiceName} disable${ChildsObjects}"
					fi
					;;
				"start-stop" ) Value="${ServiceName} ${Action}${ChildsObjects}" ;;
				"status" ) Value="${ServiceName} status${ChildsObjects}" ;;
				"create" ) Value="${ServiceName} create${ChildsObjects}" ;;
				"delete" ) Value="${ServiceName} delete${ChildsObjects}" ;;
				"list" ) Value="${ServiceName} list${ChildsObjects}" ;;
				"details" ) Value="${ServiceName} details${ChildsObjects}" ;;
				"install" )
					if [ "$ProgramInstaller" = "1" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} install${SudoSuffix}"
					fi
					;;
				"uninstall" )
					if [ "$ProgramInstaller" = "1" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} uninstall${SudoSuffix}"
					fi
					;;
				"purge" )
					if [ "$ProgramInstaller" = "1" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} purge${SudoSuffix}"
					fi
					;;
			esac
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

WaitForProcesses ()
{
	local ProcessesToWaitFor="$1"
	local StopFile="$2"
	local CurrentProcess=""
	
	IFS=',' ; for CurrentProcess in $ProcessesToWaitFor ; do unset IFS
		CurrentProcess=$(echo aaa$CurrentProcess | sed -e 's|^aaa||g' | sed -e 's|^ ||g')
		LogProgram 3 "Waiting for process: $CurrentProcess"
		while [ "$(grep -sce "/${CurrentProcess}$" -sce "^${CurrentProcess}$" /proc/*/cmdline | grep -ve ':0$')" = "" ] && [ ! -f "$StopFile" ] ; do
			sleep 1
		done
	done
}

WaitForCleanExecution ()
{
	local ExecutionsIntervalS="$1"
	local CommandLine="$2"
	local StopFile="$3"
	local LastStatus=0
	
	LogProgram 3 "Waiting for command succeeds: $CommandLine"
	eval $CommandLine
	LastStatus=$?
	while [ $LastStatus -ne 0 ] && [ ! -f "$StopFile" ] ; do
		WaitStoppable $ExecutionsIntervalS "$StopFile" 1
		if [ ! -f "$StopFile" ] ; then
			eval $CommandLine
			LastStatus=$?
		fi
	done
}

Start1Process ()
# To launch the main controller or any child.
{
	local ChildId="$1" # Only if it's for a child process
	local MaxChildLoopsNr="$2"  # -1 for unlimited (default)
	local PidFile=''
	local StopFile=''
	local ProcessName=''
#	local StartStatusNum=''
	local StartLastLog=''
	local StartText=''
	local ThePercentLoad1=''
	local ExecutionsIntervalS=''
	local CommandLine=''
	local RunningPid=''
	local CurPidDir=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" = "" ] ; then
		PidFile="$MainControllerPidFile"
		StopFile="$MainControllerStopFile"
		ProcessName="$ShortDescription"
		MkdirAndOrPublic "$(Dirname "$MainControllerPidFile")" '' u=rwX,g=rX,o=
	else
		PidFile="${ChildsPidsDir}/${ChildId}.pid"
		StopFile="${ChildsPidsDir}/${ChildId}.stop"
		if [ "$ChildId" != "@" ] ; then
			ProcessName="$ChildSingularName $ChildId"
		else
			# Workaround for Systemd bug https://github.com/systemd/systemd/pull/4992 (@ = no instance)
			ProcessName="@@ workaround for Systemd bug #4992"
		fi
		MkdirAndOrPublic "$ChildsPidsDir" '' u=rwX,g=rX,o=
	fi
	rm -f "$StopFile"
	if [ $INIT_SCRIPT_InitCall -eq 1 ] && [ "$ChildId" = "" ] ; then
		# Main controller probably called by init boot
		WaitForProcesses "$FirstProcessesToWaitFor" "$StopFile"
		if [ "$CleanExecutionToWaitFor" != "" ] ; then
			ExecutionsIntervalS="$(printf '%s\n' "$CleanExecutionToWaitFor" | cut -sf 1 -d '|')"
			if ! Is_IntegerNr "$ExecutionsIntervalS" ; then ExecutionsIntervalS=15 ; fi
			CommandLine="$(printf '%s\n' "$CleanExecutionToWaitFor" | cut -f 2- -d '|')"
			WaitForCleanExecution "$ExecutionsIntervalS" "$CommandLine" "$StopFile"
		fi
	fi
#	StartStatusNum="${PidFile}.start.status"
	StartLastLog="${PidFile}.start.text"
	PidfileStatus "$PidFile" # 0:Program is running 1:Program is not running and the pid file exists 3:Program is not running 4:Unable to determine program status
	LastStatus=$?
	if [ $LastStatus -eq 0 ] ; then
		RunningPid=$(cat "$PidFile" 2>/dev/null)
		if [ "$RunningPid" = "$$" ] ; then LastStatus=3 ; fi  # It's me
	fi
	if [ $LastStatus -eq 3 ] ; then LastStatus=1 ; fi  # Result Conversion; Clearly not running.
	case $LastStatus in
		0 )	LogProgram 1 "E: $ProcessName is already running with PID ${RunningPid}. Cannot start a parallel process."
			LastStatus=107 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
		1 ) # Includes 3
			rm -f "$PidFile"
			if [ "$ChildId" = "" ] ; then
				# Main controller
				if [ "$DaemonizeOnStart" = "1" ] ; then
					# Background
#					if [ "$DaemonizeOnStart" = "1" ] && [ "${InitSoftware}-${INIT_SCRIPT_InitCall}" != "upstart-0" ] ; then
#					# Background - Upstart already daemonizes and controls PID for stops
					LogProgram 4 '$$' DaemonizeCommand "$PidFile" "$MeExecutable" main "$MaxChildLoopsNr"
					LogService_Begin "Starting $ProcessName"
#					DaemonizeCommand "$PidFile" "$MeExecutable" main "$MaxChildLoopsNr"
#					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#					StartText="$(DaemonizeCommand "$PidFile" "$MeExecutable" main "$MaxChildLoopsNr" 2>&1 ; printf '%s' $? > "$StartStatusNum")"	$(This) never ends when fork method is ampersand.
					DaemonizeCommand "$PidFile" "$MeExecutable" main "$MaxChildLoopsNr" > "$StartLastLog" 2>&1
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					StartText="$(cat "$StartLastLog" 2>/dev/null)"
					rm -f "$StartLastLog"
					PidfileStatus "$PidFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					LogService_StatusEnd $StatusCode
					LogProgram 3 "$StartText" > /dev/null
				else
					# Foreground
					printf '%s\n' "$$" > "$PidFile"
					LogProgram 3 "${ServiceName}: $ProcessName"
#					"$MeExecutable" main "$MaxChildLoopsNr"
					LogProgram 4 '$$' MainController "$MaxChildLoopsNr"
					MainController "$MaxChildLoopsNr"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					rm -f "$PidFile"
				fi
			else
				# Child
				if Is_IntegerNr $Child_LoadWithCalm ; then
					if [ $Child_LoadWithCalm -gt 0 ] ; then
						ThePercentLoad1=$(PercentLoad 1)
						if [ $ThePercentLoad1 -ge $Child_LoadWithCalm  ] ; then
							LogProgram 3 "Waiting for system load ${ParO}${ThePercentLoad1}%${ParC} to be descending and under ${Child_LoadWithCalm}% before load $ProcessName"
							WaitForCalm $Child_LoadWithCalm "$StopFile" 0 4
						else
							LogProgram 3 "System load ${ParO}${ThePercentLoad1}%${ParC} is already under calm level ${Child_LoadWithCalm}% to load $ProcessName"
						fi
					fi
				fi
				if [ "$DaemonizeChilds" = "1" ] ; then
					# Background
					LogProgram 4 '$$' DaemonizeCommand "$PidFile" "$MeExecutable" "${LowerChildSingularName}-foreground" "$ChildId" "$MaxChildLoopsNr"
					LogService_Begin "Loading $ProcessName"
#					DaemonizeCommand "$PidFile" "$MeExecutable" "${LowerChildSingularName}-foreground" "$ChildId" "$MaxChildLoopsNr"
#					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#					StartText="$(DaemonizeCommand "$PidFile" "$MeExecutable" "${LowerChildSingularName}-foreground" "$ChildId" "$MaxChildLoopsNr" 2>&1 ; printf '%s' $? > "$StartStatusNum")"	$(This) never ends when fork method is ampersand.
					DaemonizeCommand "$PidFile" "$MeExecutable" "${LowerChildSingularName}-foreground" "$ChildId" "$MaxChildLoopsNr" > "$StartLastLog" 2>&1
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					StartText="$(cat "$StartLastLog" 2>/dev/null)"
					rm -f "$StartLastLog"
					PidfileStatus "$PidFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					LogService_StatusEnd $StatusCode
					LogProgram 3 "$StartText" > /dev/null
				else
					# Foreground
					printf '%s\n' "$$" > "$PidFile"
					LogProgram 3 "$ProcessName"
#					"$MeExecutable" "${LowerChildSingularName}-foreground" "$ChildId" "$MaxChildLoopsNr"
					LogProgram 4 '$$' ChildController "$ChildId" "$MaxChildLoopsNr"
					ChildController "$ChildId" "$MaxChildLoopsNr"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					rm -f "$PidFile"
				fi
			fi
			;;
		* )	printf '%s\n' "Unable to determine $ProcessName status${ParO}${LastStatus}${ParC}. Sure to stop it before starting." 1>&2
			LastStatus=106 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
	return $StatusCode
}

Start ()
{
	local MainControllerPid=""
	local ChildsToStop=""
	local CurrentChild=""
	local CurrentPidFile=""
	local CurrentStopFile=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildsPluralName" != "" ] && [ $# -gt 0 ] ; then
		# Only start specified childs
		unset IFS ; for CurrentChild in "$@" ; do
			if [ "$InitSoftware" = "systemd" ] ; then CurrentChild="$(printf '%s\n' "$CurrentChild" | sed -e 's|\\x20| |g')" ; fi	# systemd parses spaces as: \x20
			if [ $Child_LoadWithCalm -ge 0 ] ; then sleep 1 ; fi # Sure slower sequence to evaluate new system load.
			CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
			CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
			Start1Process "$CurrentChild"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		done
	else
		# Main controller. It will re/load childs as necessary
#		if [ "$RestartCall" != "1" ] && [ $(id -u) -eq 0 ] && [ $INIT_SCRIPT_InitCall -eq 0 ] && SystemProgramIsEnabled ; then
#			printf '%s\n' "${sWARN}W: It's better for init system to use its program to start services:${fRESET}" 1>&2
#			printf '%s\n' "         $(RecommendedInvocation start)" 1>&2
#		fi
		if [ $(id -u) -ne 0 ] || SystemProgramIsEnabled ; then # Only for main controller
			# Main controller will load childs as necessary. Both if it stays or not, it can be foreground or background.
			Start1Process "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ $INIT_SCRIPT_InitCall -eq 0 ] ; then
				printf '%s\n' "Service $ServiceName not enabled for system start." 1>&2
				printf '%s\n' "It can be enabled with: $(RecommendedInvocation enable)" 1>&2
				LastStatus=58 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	return $StatusCode
}

WaitStoppable ()
# Syntax as a sentence: WaitStoppable $IntegerMaxSeconds "$StopFile" $SecondsResolution
# Expected parameters:
#	$1	Seconds to wait while no StopFile (no fractional)
#	$2	(optional) File path which existence makes stop waiting.
#	$3	(optional) Number of seconds to wait between each StopFile checking. It can be decimal expressed (eg 0.5). Default: 1
# Depends on functions: Is_IntegerNr
# Depends on other software: grep
{
	local IntegerMaxSeconds=$1
	local StopFile="$2"
	local SleepResolution="$3"
	local InitialSecond=''
	local FinalSecond=''
	
	if Is_IntegerNr "$IntegerMaxSeconds" && [ $IntegerMaxSeconds -gt 0 ] ; then
		InitialSecond=$(printf '%(%s)T')
		FinalSecond=$(($InitialSecond + $IntegerMaxSeconds))
		if [ "$SleepResolution" = "" ] ; then SleepResolution=1 ; fi
		# Sleep upto v2 (and maybe -v4) only support integers
		OldSleep="$(sleep --version | grep -ie sleep | head -n 1 | grep -E ' (1|2|3|4)\.')"
		if [ "$OldSleep" != "" ] ; then
			SleepResolution=1
		fi
		while [ ! -f "$StopFile" ] && [ $(printf '%(%s)T') -lt $FinalSecond ] ; do
			sleep $SleepResolution
		done
	fi
}

RunningChilds_IDs ()
{
	local FoundPidfiles=""
	local CurrentPidFile=""
	local CurrentName=""
	local CurrentStatus=0
	local Value=""
	
	FoundPidfiles="$(ls -1A "$ChildsPidsDir" 2>/dev/null | grep -e '\.pid$')"
	IFS="$(printf '\n\b')" ; for CurrentPidFile in $FoundPidfiles ; do unset IFS
		PidfileStatus "${ChildsPidsDir}/${CurrentPidFile}" # 0:Program is running 1:Program is not running and the pid file exists 3:Program is not running 4:Unable to determine program status
		CurrentStatus=$?
		if [ $CurrentStatus -eq 0 ] ; then
#			CurrentName="$(basename "$CurrentPidFile" | sed -e 's|\.pid$||g')"	Problems with a path begun with "-" in old basename versions
			CurrentName="$(printf '%s\n' "$CurrentPidFile" | tr -s '/' '\n' | tail -n 1)"
			CurrentName="$(printf '%s\n' "$CurrentName" | sed -e 's|\.pid$||g')"
			if [ "$Value" = "" ] ; then
				Value="$CurrentName"
			else
				Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentName")"
			fi
		fi
	done
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value" | sort
	fi
}

RunningChilds_Pids ()
{
	local FoundPidfiles=""
	local CurrentPidFile=""
	local CurrentPid=""
	local CurrentStatus=0
	local Value=""
	
	FoundPidfiles="$(ls -1A "$ChildsPidsDir" 2>/dev/null | grep -e '\.pid$')"
	IFS="$(printf '\n\b')" ; for CurrentPidFile in $FoundPidfiles ; do unset IFS
		PidfileStatus "${ChildsPidsDir}/${CurrentPidFile}" # 0:Program is running 1:Program is not running and the pid file exists 3:Program is not running 4:Unable to determine program status
		CurrentStatus=$?
		if [ $CurrentStatus -eq 0 ] ; then
			CurrentPid="$(cat "${ChildsPidsDir}/${CurrentPidFile}" | sed -e 's| ||g')"
			if [ "$Value" = "" ] ; then
				Value="$CurrentPid"
			else
				Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentPid")"
			fi
		fi
	done
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

EnabledChilds_IDs ()
{
	local ProfilesPath="$1"
	local FoundItems=""
	local CurrentItem=""
	local Operator=""
	local Value=""
	
	if [ "$ProfilesPath" = "" ] ; then ProfilesPath="$Childs_path_enabled" ; fi
	if [ "$ChildProfileExtension" != "" ] ; then
		FoundItems="$(ls -1A "$ProfilesPath" 2>/dev/null | grep -e "\.${ChildProfileExtension}$")"
		Operator="-f"
	else
		FoundItems="$(ls -1A "$ProfilesPath" 2>/dev/null)"
		Operator="-d"
	fi
	IFS="$(printf '\n\b')" ; for CurrentItem in $FoundItems ; do unset IFS
		if [ $Operator "${ProfilesPath}/${CurrentItem}" ] ; then
			if [ "$ChildProfileExtension" != "" ] ; then
				CurrentItem="$(printf '%s\n' "$CurrentItem" | sed -e "s|\.${ChildProfileExtension}$||g")"
			fi
			Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentItem")"
		fi
	done
	Value="$(printf '%s\n' "$Value" | grep -ve '^$')"
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value" | sort
	fi
}

AvailableChilds_IDs ()
{
	local Value=""
	
	Value="$(EnabledChilds_IDs "$Childs_path_available")"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

ThreadsAccumulationSummary ()
{
	local Separation=""
	local Headers=""
	local Values=""
	local CurrentHeader=""
	local CurrentValue=""
	local AskedPidStatus=""
	
	if [ "$ChildSingularName" != "" ] ; then
		CurrentHeader=" $ChildsPluralName "
		CurrentValue=" $Accumulated_Childs "
		while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
			CurrentValue=" $CurrentValue"
		done
		while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
			CurrentHeader=" $CurrentHeader"
		done
		Headers="${Headers}${CurrentHeader}"
		Values="${Values}${CurrentValue}"
	fi
	if [ "$ChildSingularName" != "" ] ; then
		CurrentHeader=" $(printf '%s' "$ChildsPluralName" | cut -c 1).running "
		CurrentValue=" $Accumulated_ChildsRunning "
		while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
			CurrentValue=" $CurrentValue"
		done
		while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
			CurrentHeader=" $CurrentHeader"
		done
		Headers="${Headers}${CurrentHeader}"
		Values="${Values}${CurrentValue}"
	fi
	CurrentHeader=" T.PIDs "
	CurrentValue=" $Accumulated_pid "
	while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
		CurrentValue=" $CurrentValue"
	done
	while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
		CurrentHeader=" $CurrentHeader"
	done
	Headers="${Headers}${CurrentHeader}"
	Values="${Values}${CurrentValue}"
	CurrentHeader=" First.start "
	if [ "$Accumulated_start" = "now" ] && [ "$MainControllerPidFile" != "" ] ; then
		PidfileStatus "$MainControllerPidFile"
		AskedPidStatus=$?
		if [ $AskedPidStatus -eq 0 ] ; then
			CurrentValue="$(env LANG=en stat "$MainControllerPidFile" 2>/dev/null | grep -e 'Modify:..*' | sed -e 's|.*Modify:||g')"
			CurrentValue="$(echo aaa$CurrentValue | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
			if [ ${#CurrentValue} -ge 5 ] ; then
				if [ "$(date -d "$CurrentValue" '+%F')" = "$(printf '%(%F)T')" ] ; then
					CurrentValue=" $(LANG=en date -d "$CurrentValue" '+%T') "
				else
					CurrentValue=" $(LANG=en date -d "$CurrentValue" '+%b %d') "
				fi
				CurrentHeader=" Main.start "
			else
				CurrentValue=" - "
			fi
		else
			CurrentValue=" - "
		fi
	else
		CurrentValue=" $Accumulated_start "
	fi
	while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
		CurrentValue="$CurrentValue "
	done
	while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
		CurrentHeader="$CurrentHeader "
	done
	Headers="${Headers}${CurrentHeader}"
	Values="${Values}${CurrentValue}"
	CurrentHeader=" %CPU "
	CurrentValue=" $Accumulated_pcpu10 "
	if [ "$Accumulated_pcpu10" != "?" ] ; then
		CurrentValue=" $(printf '%s\n' "$CurrentValue" | sed -e 's|\(.\) $|.\1 |g' | sed -e 's| \.| 0.|g') "
	fi
	while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
		CurrentValue=" $CurrentValue"
	done
	while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
		CurrentHeader=" $CurrentHeader"
	done
	Headers="${Headers}${CurrentHeader}"
	Values="${Values}${CurrentValue}"
	CurrentHeader=" Memory "
	CurrentValue=$(($Accumulated_rss * 1024))
	CurrentValue=" $(NumeroResumit "$CurrentValue" '2L.') "
	while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
		CurrentValue=" $CurrentValue"
	done
	while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
		CurrentHeader=" $CurrentHeader"
	done
	Headers="${Headers}${CurrentHeader}"
	Values="${Values}${CurrentValue}"
	CurrentHeader=" T.threads "
	if [ "$Accumulated_Threads" = "0" ] && [ "$Accumulated_pid" != "0" ] ; then Accumulated_Threads='?' ; fi
	CurrentValue=" $Accumulated_Threads "
	while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
		CurrentValue=" $CurrentValue"
	done
	while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
		CurrentHeader=" $CurrentHeader"
	done
	Headers="${Headers}${CurrentHeader}"
	Values="${Values}${CurrentValue}"
	if [ "$MainControllerPidFile" != "" ] ; then
		PidfileStatus "$MainControllerPidFile"
		AskedPidStatus=$?
		if [ $AskedPidStatus -eq 3 ] && [ "$MainControllerStays" = "0" ] && [ $Accumulated_pid -eq 0 ] ; then
			# No childs, no daemons, not running.
			AskedPidStatus=$AskedPidStatus
		else
			printf '%s\n' "${Headers}_" | sed -e 's|.|_|g'
			printf '%s\n' "All processes summary:"
			printf '%s\n' "$Headers"
			printf '%s\n' "$Values"
		fi
	else
		printf '%s\n' "${Headers}_" | sed -e 's|.|_|g'
		printf '%s\n' "All processes summary:"
		printf '%s\n' "$Headers"
		printf '%s\n' "$Values"
	fi
}

PsValueOrDefault ()
# Returns the ps key value, or $DefaultValue if pid has disappeared.
{
	local Pid=$1
	local Key="$2"
	local DefaultValue="$3"
	local Value=""
	
	Value=$(PsOutputValues "$Key" $Pid)
	if [ "$Value" != "" ] && [ "$Value" != "?" ] ; then
		printf '%s\n' "$Value"
	else
		if [ "$DefaultValue" != "" ] ; then printf '%s\n' "$DefaultValue" ; fi
	fi
}

AccumulateThreadsInfo ()
{
	local ThreadsInfo="$1"
	local CurrentPid=""
	local CurrentValue=""
	local CurrentLine=""
	
	IFS="$(printf '\n\b')" ; for CurrentLine in $ThreadsInfo ; do unset IFS
		CurrentPid=$(OneWord () { printf '%s' $1; }; OneWord $CurrentLine)
		if Is_IntegerNr "$CurrentPid" ; then
			Accumulated_pid=$(($Accumulated_pid + 1))
			CurrentValue=$(PsValueOrDefault $CurrentPid start now)
			if [ $(date -d "$CurrentValue" '+%s') -lt $(date -d "$Accumulated_start" '+%s') ] ; then
				Accumulated_start="$CurrentValue"
			fi
			CurrentValue=$(PsValueOrDefault $CurrentPid pcpu '?' | sed -e 's|\.||g' | sed -e 's| ||g' | sed -e 's|^0\(..*\)|\1|g')
			if Is_IntegerNr "$CurrentValue" ; then
				if [ "$Accumulated_pcpu10" = "?" ] ; then Accumulated_pcpu10=0 ; fi
				Accumulated_pcpu10=$(($Accumulated_pcpu10 + $CurrentValue))
			fi
			CurrentValue=$(PsValueOrDefault $CurrentPid rss '?')
			if Is_IntegerNr "$CurrentValue" ; then
				Accumulated_rss=$(($Accumulated_rss + $CurrentValue))
			fi
			CurrentValue=$(PsValueOrDefault $CurrentPid $PsThreadsKey 1)
			if Is_IntegerNr "$CurrentValue" ; then
				Accumulated_Threads=$(($Accumulated_Threads + $CurrentValue))
			fi
		fi
	done
}

ProcessStatus ()
{
	local ChildId="$1" # Only if it's for a child process
	local OnlyOneLine"$2" # Specify "1" to avoid PIDs analysis
	local AskedPidStatus=""
	local ProcessPid=""
	local Tail=""
	local Description=""
	local FullRunning=0
	local AnalyzePids=""
	local ListeningPorts=""
	local CurrentPid=""
	local TempValue=""
	local ThreadsInfo=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$PsThreadsKey" = "" ] ; then
		# Global variable
		ps -p $$ -o thcount > /dev/null 2>&1
		LastStatus=$?
		if [ $LastStatus -eq 0 ] ; then
			PsThreadsKey=thcount
		else
			PsThreadsKey=nlwp
		fi
	fi
	if [ "$ChildId" != "" ] ; then
		if [ $DaemonizeChilds != "0" ] ; then
			PidfileStatus "${ChildsPidsDir}/${ChildId}.pid"
			AskedPidStatus=$?
			case $AskedPidStatus in
				0 )	ProcessPid=$(cat "${ChildsPidsDir}/${ChildId}.pid" | sed -e 's| ||g')
					if [ "$(pstree $ProcessPid)" != "" ] ; then
						Tail="concerned running pid $ProcessPid"
						FullRunning=1
					else
						Tail="running, but concerned PID $ProcessPid is not found"
					fi
					;;
				1 )	Tail="not running but the pid file exists" ;;
				3 )	Tail="not running" ;;
				4 )	Tail="unable to determine status." ;;
				* )
					Tail="unknown code${ParO}${AskedPidStatus}${ParC} from status query"
					LastStatus=$AskedPidStatus ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					;;
			esac
			Description="$ChildSingularName ${ChildId}: $Tail"
		fi
	else
		PidfileStatus "$MainControllerPidFile"
		AskedPidStatus=$?
		if [ "$MainControllerStays" != "0" ] ; then
			if [ "$ChildSingularName" = "" ] ; then
				MainName="Service"
				RunningWord="running"
			else
				MainName="Service controller"
				RunningWord="running"
			fi
		else
			if [ "$ChildSingularName" = "" ] ; then
				MainName="Program"
				RunningWord="launching at this moment"
			else
				MainName="$ChildsPluralName trigger"
				RunningWord="launching at this moment"
			fi
		fi
		case $AskedPidStatus in
			0 )	ProcessPid=$(cat "$MainControllerPidFile")
				if [ "$(pstree $ProcessPid)" != "" ] ; then
					Tail="$MainName is $RunningWord with main PID $ProcessPid"
					FullRunning=1
				else
					Tail="$MainName is ${RunningWord}, but main PID $ProcessPid is not found."
				fi
				;;
			1 )	Tail="$MainName is not $RunningWord and the pid file exists." ;;
			3 )	Tail="$MainName is not ${RunningWord}." ;;
			4 )	Tail="Unable to determine $MainName status." ;;
			* )
				Tail="Unknown code${ParO}${AskedPidStatus}${ParC} from $MainName status query"
				LastStatus=$AskedPidStatus ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
		esac
		Description="$(Uppercase "$ServiceName"): $Tail"
	fi
	Description="${Description}$(ProcessStatus_More $AskedPidStatus "$@")"
	if [ "$Description" != "" ] ; then
		if [ $StatusCode -eq 0 ] ; then
			printf '%s\n' "$Description"
		else
			printf '%s\n' "$Description" 1>&2
		fi
	fi
	if [ $FullRunning -eq 1 ] && [ "$OnlyOneLine" != "1" ] ; then
		# Sed upto v3 doesn't support newlines as replacement
		#AnalyzePids="$(echo $(pstree -p $ProcessPid | sed -e 's|(|@\n|g' | sed -e 's|).*|\n@|g' | grep -ve @ | grep -ve ' ') | tr -s ' ' ',')"
		TempValue="$(env COLUMNS=999 pstree -p $ProcessPid | tr -s '()' ' ')"
		AnalyzePids=""
		unset IFS ; for CurrentPid in $TempValue ; do
			if Is_IntegerNr $CurrentPid ; then AnalyzePids="$AnalyzePids $CurrentPid" ; fi
		done
		ListeningPorts="$(ListeningNetPortsByPids $AnalyzePids)"
		if [ "$ListeningPorts" != "" ] ; then
			printf '%s\n' "Listening net ports: $(echo aaa$ListeningPorts | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
		fi
		AnalyzePids=$(echo aaa$AnalyzePids | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | tr -s ' ' ',')
		# Prefer modern ps columns alignment
		ThreadsInfo="$(ps p $AnalyzePids -o pid,comm,start,pcpu,pmem,rss=Mem.KiB,${PsThreadsKey}=Threads 2>/dev/null)"
		if [ "$ThreadsInfo" = "" ] ; then
			# Fallback to compatible call: slow and unaligned columns
			ThreadsInfo="PID COMMAND          STARTED %CPU %MEM Mem.KiB Threads"
			AnalyzePids=$(printf '%s\n' "$AnalyzePids" | tr -s ',' ' ')
			ThreadsInfo="$(printf '%s\n' "$ThreadsInfo" ; PsOutputValues pid,comm,start,pcpu,pmem,rss,${PsThreadsKey} $AnalyzePids)"
		fi
		printf '%s\n' "$ThreadsInfo"
		AccumulateThreadsInfo "$ThreadsInfo"
		if [ "$ChildId" != "" ] ; then
			Accumulated_ChildsRunning=$(($Accumulated_ChildsRunning + 1))
		fi
	fi
	if [ "$ChildId" != "" ] ; then
		Accumulated_Childs=$(($Accumulated_Childs + 1))
	fi
	return $StatusCode
}

Status ()
{
	local ChildId="$1"
	local AskedPidStatus=0
	local CurrentChild=""
	local TheChilds=""
	local LastStatus=0
	local StatusCode=0
	local InfoTxt=""
	
	if [ "$ChildId" != "" ] && [ "$ChildSingularName" != "" ] ; then
		# Child status
		unset IFS ; for CurrentChild in "$@" ; do
			if [ "$DaemonizeChilds" = "1" ] ; then
				if [ "$StatusProcecessShow" = "1" ] ; then
					ProcessStatus "$CurrentChild"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					PidfileStatus "${ChildsPidsDir}/${ChildId}.pid"
					AskedPidStatus=$?
					ProcessStatus_More $AskedPidStatus "$@"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			else
				AskedPidStatus=4
			fi
			Status_More $AskedPidStatus "$CurrentChild"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		done
	else
		# Whole service status
		if [ "$StatusProcecessShow" = "1" ] ; then
			ProcessStatus
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			PidfileStatus "$MainControllerPidFile"
			AskedPidStatus=$?
			ProcessStatus_More $AskedPidStatus "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		TheChilds="$(AvailableChilds_IDs)"
		TheChilds="$(printf '%s\n' "$TheChilds" ; EnabledChilds_IDs)"
		TheChilds="$(printf '%s\n' "$TheChilds" ; RunningChilds_IDs)"
		TheChilds="$(printf '%s\n' "$TheChilds" | grep -ve '^$' | sort -u)"
		IFS="$(printf '\n\b')" ; for CurrentChild in $TheChilds ; do unset IFS
			if [ "$StatusProcecessShow" = "1" ] ; then
				#InfoTxt="$(ProcessStatus "$CurrentChild" 1)"	This (subshell) losses global variables modifications
				ProcessStatus "$CurrentChild" 1 > "${DirTemp}/Status.$$"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				InfoTxt="$(cat "${DirTemp}/Status.$$")"
				rm -f "${DirTemp}/Status.$$"
				if [ "$InfoTxt" != "" ] ; then
					printf '%s\n' ""
					printf '%s\n' "$InfoTxt"
				fi
			else
				PidfileStatus "${ChildsPidsDir}/${ChildId}.pid"
				AskedPidStatus=$?
				ProcessStatus_More $AskedPidStatus "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		done
	fi
	if [ "$StatusProcecessShow" = "1" ] ; then
		if [ "$ChildId" = "" ] || [ "$DaemonizeChilds" = "1" ] ; then
			TheThreadsAccumulationSummary="$(ThreadsAccumulationSummary)"
			if [ "$TheThreadsAccumulationSummary" != "" ] ; then printf '%s\n' "$TheThreadsAccumulationSummary" ; fi
		fi
	fi
	if [ "$ChildId" = "" ] || [ "$ChildSingularName" = "" ] ; then
		if SystemProgramIsEnabled ; then
			if [ "$TheThreadsAccumulationSummary" != "" ] ; then
				printf '%s\n' "$ServiceName is enabled to start as system service. Service will start on boot."
			else
				printf '%s\n' "$ServiceName is enabled to start as system service." 1>&2
				printf '%s\n' "Service will start on boot, and manually with command: $(RecommendedInvocation start)"
			fi
		else
			printf '%s\n' "$ServiceName is disabled, to not start as system service. Service will not start on boot."
		fi
		Status_More $AskedPidStatus "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

CleanChildsPids ()
{
	local FoundPidfiles=""
	local CurrentStatus=""
	
	FoundPidfiles="$(ls -1A "$ChildsPidsDir" 2>/dev/null | grep -e '\.pid$')"
	IFS="$(printf '\n\b')" ; for CurrentPidFile in $FoundPidfiles ; do unset IFS
		PidfileStatus "${ChildsPidsDir}/${CurrentPidFile}" # 0:Program is running 1:Program is not running and the pid file exists 3:Program is not running 4:Unable to determine program status
		CurrentStatus=$?
		if [ $CurrentStatus -eq 1 ] || [ $CurrentStatus -eq 3 ] ; then
			rm "${ChildsPidsDir}/${CurrentPidFile}"
		fi
	done
}

Stop1Daemon ()
{
	local PidFile="$1"
	local StopFile="$2"
	local DaemonName="$3"
	local ExpectedToStay="$4" # If it's supposed to be running (1) or not (0); this only affects messages.
	local LeaveStopFile="$5" # If to not remove $StopFile (1) or remove $StopFile when success (0)
	# Rest of parameters will be written as text to StopFile
	local Pid=""
	local TimeoutCount=0
	local StopStatus=""
	local StopLastLog=""
	local MultipleCount=2
	local FractionSecond=0.5
	local CurPidDir=''
	local LastStatus=0
	local StatusCode=0

	if [ "$DaemonName" = "" ] ; then DaemonName="program" ; fi
	MkdirAndOrPublic "$(Dirname "$PidFile")" '' u=rwX,g=rX,o=
	if [ "$StopFile" != "" ] ; then
		MkdirAndOrPublic "$(Dirname "$StopFile")" '' u=rwX,g=rX,o=
#		printf "%(%Y-%m-%dT%T%z)T [$$] Stop1Daemon $*" > "$StopFile" 2>&1
		printf "$(date '+%Y-%m-%dT%T%z') [$$] Stop1Daemon $*" > "$StopFile" 2>&1
		StopStatus="${StopFile}.status"
	else
		printf '%s\n' "${sWARN}W: No StopFile specified to Stop1Daemon ${PidFile}${fRESET}" 1>&2
		StopStatus="${PidFile}.stop.status"
	fi
	if [ -f "$PidFile" ] ; then
		Pid="$(cat "$PidFile")"
		if [ "$(pstree $Pid)" = "" ] ; then
			if [ "$ExpectedToStay" = "1" ] ; then
				printf '%s\n' "${sWARN}W: $DaemonName was not running.${fRESET}" 1>&2
			fi
			rm "$PidFile"
		else
			LogService_Begin "Stopping $DaemonName"
			# Sleep upto v2 (and maybe -v4) only support integers
			OldSleep="$(sleep --version | grep -ie sleep | head -n 1 | grep -E ' (1|2|3|4)\.')"
			if [ "$OldSleep" != "" ] ; then
				MultipleCount=1
				FractionSecond=1
			fi
			if [ $GentleStopTimeoutS -lt 0 ] ; then GentleStopTimeoutS=9999 ; fi
			TimeoutCount=$(($GentleStopTimeoutS * $MultipleCount))
			while [ "$(pstree $Pid)" != "" ] && [ $TimeoutCount -gt 0 ] ; do
				sleep $FractionSecond
				TimeoutCount=$(($TimeoutCount - 1))
			done
#			if [ "$(pstree $Pid)" != "" ] || [ "$ExpectedToStay" = "1" ] ; then
			if [ "$(pstree $Pid)" != "" ] ; then
				TimeoutCount=$(($GentleStopTimeoutS / 2)) ; if [ $TimeoutCount -lt 1 ] ; then TimeoutCount=1 ; fi
				StopLastLog="$(StopPidFile "$PidFile" $TimeoutCount 2>&1 ; printf '%s' $? > "$StopStatus")"
				LastStatus=$(cat "$StopStatus") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				rm -f "$StopStatus"
			fi
			if [ "$(pstree $Pid)" = "" ] ; then
				rm -f "$PidFile"
			fi
			LogService_StatusEnd $StatusCode
			LogProgram 3 "$StopLastLog" > /dev/null
		fi
	else
		if [ "$ExpectedToStay" = "1" ] && [ $INIT_SCRIPT_InitCall -eq 0 ] ; then
			printf '%s\n' "$DaemonName was not running."
		fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$LeaveStopFile" != "1" ] ; then rm -f "$StopFile" ; fi
	return $StatusCode
}

Stop ()
{
	local MainControllerPid=""
	local ChildsToStop=""
	local CurrentChild=""
	local CurrentPidFile=""
	local CurrentStopFile=""
	local CurWait=''
	local CurPid=''
	local LastStatus=0
	local StatusCode=0

	if [ "$ChildsPluralName" != "" ] && [ $# -gt 0 ] ; then
		# Only stop specified childs - No matter if they are daemons or foregrounds
		unset IFS ; for CurrentChild in "$@" ; do
			# Previous .stop signals blast to accelerate task
			Stop_Pre "$CurrentChild"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ "$InitSoftware" = "systemd" ] ; then CurrentChild="$(printf '%s\n' "$CurrentChild" | sed -e 's|\\x20| |g')" ; fi	# systemd parses spaces as: \x20
			CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
			touch "$CurrentStopFile" 2>/dev/null
		done
		unset IFS ; for CurrentChild in "$@" ; do
			if [ "$InitSoftware" = "systemd" ] ; then CurrentChild="$(printf '%s\n' "$CurrentChild" | sed -e 's|\\x20| |g')" ; fi	# systemd parses spaces as: \x20
			CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
			CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
#			Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 1 "${ParO}Stop called for specified ${ChildsPluralName}${ParC}"
			Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 0 "${ParO}Stop called for specified ${ChildsPluralName}${ParC}"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			Stop_Post "$CurrentChild"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		done
	else
		Stop_Pre "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ "$RestartCall" != "1" ] && [ $(id -u) -eq 0 ] && [ "$INIT_SCRIPT_InitCall" = "0" ] ; then
			# To avoid warning when installer forces stop
			PidfileStatus "$MainControllerPidFile"
			LastStatus=$?
#			if [ $LastStatus -eq 0 ] && SystemProgramIsEnabled ; then
#				printf '%s\n' "${sWARN}W: It's better for init system to use its program to start services:" 1>&2
#				printf '%s\n' "   $(RecommendedInvocation stop)${fRESET}" 1>&2
#			fi
		fi
		if [ $# -eq 0 ] ; then
			# Main controller before all childs, to avoid it reloads dead childs. - No matter if it's a daemon or foreground
#			Stop1Daemon "$MainControllerPidFile" "$MainControllerStopFile" "$ServiceName controller" $MainControllerStays 1 "${ParO}Stop called for whole service${ParC}"
			Stop1Daemon "$MainControllerPidFile" "$MainControllerStopFile" "$ServiceName controller" $MainControllerStays 0 "${ParO}Stop called for whole service${ParC}"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ "$ChildsPluralName" != "" ] ; then
			# Stop all running childs - No matter if they are daemons or foregrounds
			ChildsToStop="$(RunningChilds_IDs)"
			IFS="$(printf '\n\b')" ; for CurrentChild in $ChildsToStop ; do unset IFS
				# Previous .stop signals blast to accelerate task
				Stop_Pre "$CurrentChild"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
				touch "$CurrentStopFile" 2>/dev/null
			done
			IFS="$(printf '\n\b')" ; for CurrentChild in $ChildsToStop ; do unset IFS
				CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
				CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
				CurPid="$(cat "$CurrentPidFile")"
				TempTree="$(env COLUMNS=999 pstree -p $CurPid | tr -s '()' ' ')"
				unset IFS ; for CurTemp in $TempTree ; do
					if Is_IntegerNr $CurTemp ; then CurPids="$CurPids $CurTemp" ; fi
				done
				if [ "$InitSoftware" = "systemd" ] ; then
					# systemctl use only when concrete childs aren't specified.
					if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
						printf '%s\n' "Stopping $ChildSingularName $CurrentChild"
						systemctl stop "${ServiceName}@${CurrentChild}"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						CurWait=$GentleStopTimeoutS
						while [ "$(ExistingPIDs $CurPids)" != "" ] && [ $CurWait -gt 0 ] ; do
							sleep 1
							CurWait=$((CurWait - 1))
						done
						if [ "$(ExistingPIDs $CurPids)" != "" ] ; then
							unset IFS ; for CurTemp in $CurPids ; do
								KillPid $CurTemp $GentleStopTimeoutS
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							done
						fi
					else
						if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
							printf '%s\n' "Stopping $ChildSingularName $CurrentChild"
							systemctl stop "${ServiceName}@${CurrentChild}"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							CurWait=$GentleStopTimeoutS
							while [ "$(ExistingPIDs $CurPids)" != "" ] && [ $CurWait -gt 0 ] ; do
								sleep 1
								CurWait=$((CurWait - 1))
							done
							if [ "$(ExistingPIDs $CurPids)" != "" ] ; then
								unset IFS ; for CurTemp in $CurPids ; do
									KillPid $CurTemp $GentleStopTimeoutS
									LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								done
							fi
						else
#							Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 1 "${ParO}Stop called${ParC}"
							Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 0 "${ParO}Stop called${ParC}"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					fi
				else
#					Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 1 "${ParO}Stop called${ParC}"
					Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 0 "${ParO}Stop called${ParC}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					Stop_Post "$CurrentChild"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			done
			CleanChildsPids
			rmdir "$ChildsPidsDir" >/dev/null 2>&1
		fi
		Stop_Post "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

MyIdChild ()
# Returns the child ID if own process is a child one.
{
	local FoundPidfiles=""
	local CurrentPidFile=""
	local CurrentName=""
	local CurrentPid=""
	local Value=""
	
	FoundPidfiles="$(ls -1A "$ChildsPidsDir" 2>/dev/null | grep -e '\.pid$')"
	IFS="$(printf '\n\b')" ; for CurrentPidFile in $FoundPidfiles ; do unset IFS
		CurrentPid="$(cat "${ChildsPidsDir}/${CurrentPidFile}" | sed -e 's| ||g')"
		if [ "$CurrentPid" = "$$" ] ; then
#			CurrentName="$(basename "$CurrentPidFile" | sed -e 's|\.pid$||g')"	Problems with a path begun with "-" in old basename versions
			CurrentName="$(printf '%s\n' "$CurrentPidFile" | tr -s '/' '\n' | tail -n 1)"
			CurrentName="$(printf '%s\n' "$CurrentName" | sed -e 's|\.pid$||g')"
			printf '%s\n' "$CurrentName"
		fi
	done
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

MeInstalled ()
# Returns 1 if this script is placed as system installed path.
{
	local Places=''
	local Value=0
	
	Places="/etc/init.d/${ServiceName}
/etc/rc.d/init.d/${ServiceName}
/bin/${ServiceName}
/sbin/${ServiceName}
/usr/bin/${ServiceName}
/usr/sbin/${ServiceName}
/usr/local/bin/${ServiceName}
/usr/local/sbin/${ServiceName}
/etc/init.d/${ServiceName}.sh
/etc/rc.d/init.d/${ServiceName}.sh
/bin/${ServiceName}.sh
/sbin/${ServiceName}.sh
/usr/bin/${ServiceName}.sh
/usr/sbin/${ServiceName}.sh
/usr/local/bin/${ServiceName}.sh
/usr/local/sbin/${ServiceName}.sh"
	if [ "$(printf '%s\n' "$Places" | grep -e "^${MeExecutable}$")" != "" ] ; then
		Value=1
	fi
	printf '%s\n' "$Value"
}

CleanTempFiles ()
# Deletes only own (child or main controller) items.
{
	local TheNameChild=""
	local CurrentPid=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$(cat "$MainControllerPidFile" 2>/dev/null | sed -e 's| ||g')" = "$$" ] ; then
		# Being the main controller process
		rmdir "$ChildsTempDir" 2>/dev/null
		rm -fr "${UserTempDir}/main"
		rmdir "$UserTempDir" 2>/dev/null
	else
		TheNameChild="$(MyIdChild)"
		if [ "$TheNameChild" != "" ] ; then
			# Being a child daemon
			rm -fr "${ChildsTempDir}/${TheNameChild}"
		else
			# Being another call
			if [ $(MeInstalled) -eq 1 ] ; then
				CurrentPid="$(cat "$MainControllerPidFile" 2>/dev/null | sed -e 's| ||g')"
				if [ "$CurrentPid" = "" ] || [ "$(pstree $CurrentPid)" = "" ] ; then
					# Main controller is not running
					if [ "$(RunningChilds_IDs)" = "" ] ; then
						# No childs running
						rm -fr "$UserTempDir"
					fi
				fi
			fi
		fi
	fi
	return $StatusCode
}

LoadChilds ()
# Background childs: Start enabled childs that are not running and not manually stopped.
# Foreground childs: Run childs.
{
	local LoadStopped="$1" # If manually stopped childs must be loaded anyway (1) or not (0)
	local MaxChildLoopsNr="$2"  # -1 for unlimited (default)
	local TheEnabledChilds=""
	local TheRunningChilds=""
	local CurrentChild=""
	local CurrentStopFile=""
	local LastStatus=0
	local StatusCode=0
	
	TheEnabledChilds="$(EnabledChilds_IDs)"
	TheRunningChilds="$(RunningChilds_IDs)"
	IFS="$(printf '\n\b')" ; for CurrentChild in $TheEnabledChilds ; do unset IFS
		CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
		if [ "$LoadStopped" = "1" ] ; then rm -f "$CurrentStopFile" ; fi
		if [ "$(printf '%s\n' "$TheRunningChilds" | grep -e "^${CurrentChild}$")" = "" ] && [ ! -f "$CurrentStopFile" ] ; then
			if [ "$InitSoftware" = "systemd" ] && [ "$SystemdChilds" != "0" ] ; then
				# systemctl use only when concrete childs aren't specified.
				if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
					LogProgram 3 "Starting $ChildSingularName $CurrentChild"
					PrepareExitcodeToFile
					ExecuteString systemctl start "${ServiceName}@${CurrentChild}" 2>&1 | tee -a "$MainControllerLog"
					GetFiledExitcode $?
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
						LogProgram 3 "Starting $ChildSingularName $CurrentChild"
						PrepareExitcodeToFile
						ExecuteString systemctl start "${ServiceName}@${CurrentChild}" 2>&1 | tee -a "$MainControllerLog"
						GetFiledExitcode $?
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						Start1Process "$CurrentChild" "$MaxChildLoopsNr"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			else
				Start1Process "$CurrentChild" "$MaxChildLoopsNr"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	done
	return $StatusCode
}

ReloadChilds ()
# Background childs: Stop childs that are not enabled, and start enabled childs that are not running.
# Foreground childs: Run childs.
{
	local LoadStopped="$1" # If manually stopped childs must be loaded anyway (1) or not (0)
	local MaxChildLoopsNr="$2"  # -1 for unlimited (default)
	local TheEnabledChilds=""
	local TheRunningChilds=""
	local TheAvailableChilds=""
	local CurrentChild=""
	local CurrentPidFile=""
	local CurrentStopFile=""
	local LastStatus=0
	local StatusCode=0
	
	TheEnabledChilds="$(EnabledChilds_IDs)"
	TheRunningChilds="$(RunningChilds_IDs)"
	IFS="$(printf '\n\b')" ; for CurrentChild in $TheRunningChilds ; do unset IFS
		if [ "$(printf '%s\n' "$TheEnabledChilds" | grep -e "^${CurrentChild}$")" = "" ] ; then
			if [ "$InitSoftware" = "systemd" ] && [ "$SystemdChilds" != "0" ] ; then
				# systemctl use only when concrete childs aren't specified.
				if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
					printf '%s\n' "Stopping $ChildSingularName $CurrentChild"
					systemctl stop "${ServiceName}@${CurrentChild}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
						printf '%s\n' "Stopping $ChildSingularName $CurrentChild"
						systemctl stop "${ServiceName}@${CurrentChild}"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
						CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
						Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 0 "${ParO}ReloadChilds called${ParC}"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			else
				CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
				CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
				Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 0 "${ParO}ReloadChilds called${ParC}"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	done
	TheAvailableChilds="$(AvailableChilds_IDs)"
	IFS="$(printf '\n\b')" ; for CurrentChild in $TheAvailableChilds ; do unset IFS
		if [ "$(printf '%s\n' "$TheEnabledChilds" | grep -e "^${CurrentChild}$")" = "" ] ; then
			if [ "$InitSoftware" = "systemd" ] && [ "$SystemdChilds" != "0" ] ; then
				# systemctl use only when concrete childs aren't specified.
				if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
					printf '%s\n' "Stopping $ChildSingularName $CurrentChild"
					systemctl stop "${ServiceName}@${CurrentChild}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
						printf '%s\n' "Stopping $ChildSingularName $CurrentChild"
						systemctl stop "${ServiceName}@${CurrentChild}"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
						CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
						Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 0 0 "${ParO}ReloadChilds called${ParC}"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			else
				CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
				CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
				Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 0 0 "${ParO}ReloadChilds called${ParC}"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	done
	LoadChilds "$LoadStopped" "$MaxChildLoopsNr"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

UpstartInitProfile ()
{
	local TheTemplateVersion=""
	local CurrentUnits=""
	local MoreUnits=""
	local StartStopOnEvents=""
	
	# TO DO: Manage runlevels taking some good reference to combine them with "start on others"
	TheTemplateVersion="$(TemplateVersion)"
	if [ "$TheTemplateVersion" != "" ] ; then
		printf '%s\n' "# $ServiceName $(ServiceVersion) service parameters for upstart"
	else
		printf '%s\n' "# $ServiceName service parameters for upstart"
	fi
	printf '%s\n' "# ${ParO}${LongDescription}${ParC}"
	printf '%s\n' "# Note: Many parameters have been parsed from LSB headers of SystemV init script. For example, runlevel* and net-device-up"
	printf '%s\n' ""
	printf '%s\n' "description	\"${ShortDescription}\""
	if [ "$TheTemplateVersion" != "" ] ; then
		printf '%s\n' "version	\"${TheTemplateVersion}\""
	else
		printf '%s\n' "version	\"$(ServiceVersion)\""
	fi
	printf '%s\n' ""
#	CurrentUnits="$(UpstartJobsFromLsbHeader 'Required-Start' | sed -e 's| | and started |')"
	CurrentUnits="$(UpstartJobsFromLsbHeader 'Required-Start' | sed -e 's| | and |' | sed -e 's|__| |')"
#	if [ "$CurrentUnits" != "" ] ; then CurrentUnits="started $CurrentUnits" ; fi
	MoreUnits="$(UpstartRunlevelsFromLsbHeader 'Default-Start')"
	if [ "$MoreUnits" != "" ] ; then
		if [ "$CurrentUnits" != "" ] ; then CurrentUnits="$CurrentUnits and " ; fi
		CurrentUnits="${CurrentUnits}${MoreUnits}"
	fi
	if [ "$CurrentUnits" != "" ] ; then
		printf '%s\n' "start on ${ParO}${CurrentUnits}${ParC}"
		StartStopOnEvents="$StartStopOnEvents $CurrentUnits"
	fi
	# Cannot convert the "Should-Start" logic.
	CurrentUnits="$(echo aaa$(UpstartJobsFromLsbHeader 'Required-Stop') $(UpstartJobsFromLsbHeader 'Should-Stop') | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | sed -e 's| | or stopping |' | sed -e 's|__| |')"
	if [ "$CurrentUnits" != "" ] ; then CurrentUnits="stopping $CurrentUnits" ; fi
	MoreUnits="$(UpstartRunlevelsFromLsbHeader 'Default-Stop')"
	if [ "$MoreUnits" != "" ] ; then
		if [ "$CurrentUnits" != "" ] ; then CurrentUnits="$CurrentUnits or " ; fi
		CurrentUnits="${CurrentUnits}${MoreUnits}"
	fi
	if [ "$CurrentUnits" != "" ] ; then
		printf '%s\n' "stop on ${ParO}${CurrentUnits}${ParC}"
		StartStopOnEvents="$StartStopOnEvents $CurrentUnits"
	fi
	printf '%s\n' ""
#	echo "pre-start exec \"$ProgramExecutablePath\" start"
#	echo "exec \"$ProgramExecutablePath\" start"
	printf '%s\n' "exec \"$ProgramExecutablePath\" start-foreground"
	printf '%s\n' "pre-stop exec \"$ProgramExecutablePath\" stop"
	printf '%s\n' ""
	if [ "$TimeoutStartSec" != "" ] ; then
		if [ $TimeoutStartSec -ge 0 ] ; then
			printf '%s\n' "kill timeout $TimeoutStartSec"
		else
			printf '%s\n' "kill timeout 9999"
		fi
	fi
#	if [ "$DaemonizeOnStart" = "1" ] ; then
#		No way to declare pidfile; upstart assumes foreground pid and cannot do "stop" action.
#		echo "expect fork"
#	fi
	if [ "$StartStopOnEvents" = "" ] ; then
		printf '%s\n' "manual"
	fi
}

EnableSystemService ()
{
	local ExpectedDisabled="$1" # If it's supposed to be disabled (1) or not (0); this only affects messages.
	local LastStatus=0
	local StatusCode=0
	
	if SystemProgramIsEnabled ; then
		if [ $ExpectedDisabled -eq 1 ] ; then
			printf '%s\n' "${sWARN}W: $ServiceName is already enabled to start as system service.${fRESET}" 1>&2
		fi
	else
		case "$ServiceEnabler" in
			"systemctl" )
				systemctl --system enable "${ServiceName}.service"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ "$ChildSingularName" != "" ] ; then
					if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
						systemctl --system enable "${ServiceName}@.service"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
							systemctl --system enable "${ServiceName}@.service"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					fi
				fi
				;;
			"insserv" )
				insserv --default "$ServiceName"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"update-rc.d" )
				update-rc.d "$ServiceName" defaults
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"chkconfig" )
				chkconfig --add "$ServiceName"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"$RcLocal" )
				if [ "$RcLocal" != "" ] ; then
					if [ -f "$RcLocal" ] ; then
						if [ "$(cat "$RcLocal" | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -E '^exit( |$)')" != "" ] ; then
							RcLinesNr=$(cat "$RcLocal" | wc -l)
							ExitLineNr=$(cat "$RcLocal" | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | sed -e '/^exit/q' | wc -l)
							RcPart1="$(cat "$RcLocal" | head -n $(($ExitLineNr - 1)))"
							RcPart2="$(cat "$RcLocal" | tail -n $(($RcLinesNr - $ExitLineNr)))"
							printf '%s\n' "$RcPart1" > "$RcLocal"
							printf '%s\n' "$ProgramExecutablePath start" >> "$RcLocal"
							printf '%s\n' "$RcPart2" >> "$RcLocal"
						else
							if [ "$(cat "$RcLocal")" != "" ] && [ "$(cat "$RcLocal" | tail -n 1)" != "" ] ; then	#"
								printf '\n' >> "$RcLocal"
							fi
							printf '%s\n' "$ProgramExecutablePath start" >> "$RcLocal"
						fi
					else
						printf '%s\n' "${sERROR}E: File not found to write boot execution: ${RcLocal}${fRESET}" 1>&2
						LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				else
					printf '%s\n' "${sERROR}E: Unknown init enablement utility${fRESET}" 1>&2
					LastStatus=57 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				;;
			"/etc/init" )
				InitProfileFile="${InitProfilesDir}/${ServiceName}.conf"
				UpstartInitProfile > "$InitProfileFile"
				chown root:root "$InitProfileFile"
				chmod u=rw,go=r "$InitProfileFile"
				;;
			"" )
				if [ "$InitProfilesDir" = "" ] ; then
					printf '%s\n' "${sERROR}E: $InitSoftware utility for init enablement not found ${ParO}systemctl, insserv, update-rc.d,...${ParC}.${fRESET}" 1>&2
					LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					printf '%s\n' "${sERROR}E: No $InitSoftware utility to enable service profile at ${InitProfilesDir}${fRESET}" 1>&2
					LastStatus=57 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				;;
			* )
				printf '%s\n' "${sERROR}E: Unsupported init enablement utility: ${ServiceEnabler}${fRESET}" 1>&2
				LastStatus=51 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
		esac
		if [ $StatusCode -eq 0 ] ; then
			if [ "$(cat "/etc/default/${ServiceName}" 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ie '^ENABLED' -ie '^RUN=')" != "" ] ; then
				NewSystemvEnablementData="$(cat "/etc/default/${ServiceName}" 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ive '^ENABLED' -ive '^RUN=')"
				if [ "$(printf '%s\n' "$NewSystemvEnablementData" | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ve '')" != "" ] ; then
					printf '%s\n' "$NewSystemvEnablementData" > "/etc/default/${ServiceName}"
				else
					rm "/etc/default/${ServiceName}"
				fi
			fi
		fi
		if SystemProgramIsEnabled ; then
			printf '%s\n' "$ServiceName enabled to start as system service."
			printf '%s\n' "Service will start on boot, and manually with command: $(RecommendedInvocation start)"
		fi
	fi
	return $StatusCode
}

DisableSystemService ()
{
	local ExpectedEnabled="$1" # If it's supposed to be enabled (1) or not (0); this only affects messages.
	local LastStatus=0
	local StatusCode=0
	
	if ! SystemProgramIsEnabled ; then
		if [ $ExpectedEnabled -eq 1 ] ; then
			printf '%s\n' "${sWARN}W: $ServiceName is already disabled, and is not starting as system service.${fRESET}" 1>&2
		fi
	else
		case "$ServiceEnabler" in
			"systemctl" )
				systemctl --system --force disable "${ServiceName}.service"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ "$(command -v insserv 2>/dev/null)" != "" ] ; then
					# Old template versions enabled services with insserv
					insserv --remove "$ServiceName" > /dev/null 2>&1
				fi
				if [ "$ChildSingularName" != "" ] ; then
					if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
						systemctl --system --force disable "${ServiceName}@.service"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
							systemctl --system --force disable "${ServiceName}@.service"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					fi
				fi
				;;
			"insserv" )
				insserv --remove "$ServiceName"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"update-rc.d" )
				update-rc.d -f "$ServiceName" remove
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"chkconfig" )
				chkconfig --del "$ServiceName"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"$RcLocal" )
				if [ "$RcLocal" != "" ] ; then
					if [ ! -f "$RcLocal" ] ; then
						printf '%s\n' "${sERROR}E: File not found to clean boot execution from: ${RcLocal}${fRESET}" 1>&2
					fi
				else
					printf '%s\n' "${sERROR}E: Unknown init enablement utility${fRESET}" 1>&2
					LastStatus=57 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				;;
			"/etc/init" )
				InitProfileFile="${InitProfilesDir}/${ServiceName}.conf"
				rm -f "$InitProfileFile"
				;;
			"" )
				if [ "$InitProfilesDir" = "" ] ; then
					printf '%s\n' "${sERROR}E: $InitSoftware utility for init disablement not found ${ParO}systemctl, insserv, update-rc.d,...${ParC}.${fRESET}" 1>&2
					LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					printf '%s\n' "${sERROR}E: No $InitSoftware utility to disable service profile at ${InitProfilesDir}${fRESET}" 1>&2
					LastStatus=57 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				;;
			* )
				printf '%s\n' "${sERROR}E: Unsupported init enablement utility: ${ServiceEnabler}${fRESET}" 1>&2
				LastStatus=51 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
		esac
		if [ "$(cat "$RcLocal" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | sed -e '/^exit/q' | grep -E "^(${ProgramExecutablePath}|${ServiceName}) start")" != "" ] ; then
			NewRcContent="$(cat "$RcLocal" | grep -ivE '^[[:blank:]]*(${ProgramExecutablePath}|${ServiceName}) start')"
			printf '%s\n' "$NewRcContent" > "$RcLocal"
		fi
		if [ "$(cat /etc/rc.d/rc.local 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | sed -e '/^exit/q' | grep -E "^(${ProgramExecutablePath}|${ServiceName}) start")" != "" ] ; then
			NewRcContent="$(cat /etc/rc.d/rc.local | grep -ivE '^[[:blank:]]*(${ProgramExecutablePath}|${ServiceName}) start')"
			printf '%s\n' "$NewRcContent" > /etc/rc.d/rc.local
		fi
		if [ "$(cat /etc/rc.local 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | sed -e '/^exit/q' | grep -E "^(${ProgramExecutablePath}|${ServiceName}) start")" != "" ] ; then
			NewRcContent="$(cat /etc/rc.local | grep -ivE '^[[:blank:]]*(${ProgramExecutablePath}|${ServiceName}) start')"
			printf '%s\n' "$NewRcContent" > /etc/rc.local
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$(cat "/etc/default/${ServiceName}" 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ie '^ENABLED' -ie '^RUN=')" != "" ] ; then
				NewSystemvEnablementData="$(cat "/etc/default/${ServiceName}" 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ive '^ENABLED' -ive '^RUN=')"
				if [ "$(printf '%s\n' "$NewSystemvEnablementData" | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ve '')" != "" ] ; then
					printf '%s\n' "$NewSystemvEnablementData" > "/etc/default/${ServiceName}"
				else
					rm "/etc/default/${ServiceName}"
				fi
			fi
		fi
		if ! SystemProgramIsEnabled ; then
			printf '%s\n' "$ServiceName disabled; it will not start as system service."
		fi
	fi
	return $StatusCode
}

EnableObject ()
{
	local ExpectedDisabled="$1" # If it's supposed to be disabled (1) or not (0); this only affects messages.
	if [ $# -gt 0 ] ; then shift ; fi
	local ChildId="$1"
	local TempContent=""
	local ChildProfile_Available=""
	local ChildProfile_Enabled=""
	local CurrentChild=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" != "" ] && [ "$ChildSingularName" != "" ] ; then
		# Enable child
		if [ "$ChildProfileExtension" != "" ] ; then
			TheExtension=".${ChildProfileExtension}"
			Operator="-f"
		else
			TheExtension=""
			Operator="-d"
		fi
		unset IFS ; for CurrentChild in "$@" ; do
			ChildProfile_Available="${Childs_path_available}/${CurrentChild}${TheExtension}"
			if [ $Operator "$ChildProfile_Available" ] ; then
				ChildProfile_Enabled="${Childs_path_enabled}/${CurrentChild}${TheExtension}"
				if [ $Operator "$ChildProfile_Enabled" ] && [ "$(ReadlinkF "$ChildProfile_Enabled")" = "$ChildProfile_Available" ] ; then
					if [ $ExpectedDisabled -eq 1 ] ; then
						printf '%s\n' "${sWARN}W: $ChildSingularName $CurrentChild is already enabled for $ServiceName load.${fRESET}" 1>&2
					fi
				else
					rm -fr "$ChildProfile_Enabled"
					if [ "$(ln --help | grep -e '--relative')" != "" ] ; then
						ln -s -r "$ChildProfile_Available" "$ChildProfile_Enabled"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						ln -s "$ChildProfile_Available" "$ChildProfile_Enabled"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					if [ $StatusCode -eq 0 ] ; then
						printf '%s\n' "$ChildSingularName $CurrentChild enabled for $ServiceName load."
					fi
				fi
			else
				printf '%s\n' "${sERROR}E: $ChildSingularName $CurrentChild not found at ${Childs_path_available}${fRESET}" 1>&2
				LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		done
	else
		# Enable main service for system operation
		if [ $(id -u) -eq 0 ] ; then
			EnableSystemService $ExpectedDisabled
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			printf '%s\n' "${sERROR}E: This program needs superuser ${ParO}root${ParC} permissions to enable $ServiceName as system service${fRESET}" 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

DisableObject ()
{
	local ExpectedEnabled="$1" # If it's supposed to be enabled (1) or not (0); this only affects messages.
	if [ $# -gt 0 ] ; then shift ; fi
	local ChildId="$1"
	local TempContent=""
	local ChildProfile_Available=""
	local ChildProfile_Enabled=""
	local CurrentChild=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" != "" ] && [ "$ChildSingularName" != "" ] ; then
		# Disable child
		if [ "$ChildProfileExtension" != "" ] ; then
			TheExtension=".${ChildProfileExtension}"
			Operator="-f"
		else
			TheExtension=""
			Operator="-d"
		fi
		unset IFS ; for CurrentChild in "$@" ; do
			ChildProfile_Enabled="${Childs_path_enabled}/${CurrentChild}${TheExtension}"
			if [ $Operator "$ChildProfile_Enabled" ] ; then
				rm -fr "$ChildProfile_Enabled"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					printf '%s\n' "$ChildSingularName $CurrentChild disabled; it will not load with ${ServiceName}."
				fi
			else
				# rm anyway because it could be a broken link to undetectable file/dir
				rm -f "$ChildProfile_Enabled"
				ChildProfile_Available="${Childs_path_available}/${CurrentChild}${TheExtension}"
				if [ $ExpectedEnabled -eq 1 ] ; then
					if [ $Operator "$ChildProfile_Available" ] ; then
						printf '%s\n' "${sWARN}W: $ChildSingularName $CurrentChild is already disabled, and is not loading with ${ServiceName}.${fRESET}" 1>&2
					else
						printf '%s\n' "${sERROR}E: $ChildSingularName $CurrentChild not found.${fRESET}" 1>&2
						LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			fi
		done
	else
		# Disable main service for system operation
		if [ $(id -u) -eq 0 ] ; then
			DisableSystemService $ExpectedEnabled
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			printf '%s\n' "${sERROR}E: This program needs superuser ${ParO}root${ParC} permissions to disable $ServiceName as system service${fRESET}" 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

DeleteChild ()
{
	local ChildId="$1"
	local TheExtension=""
	local PidFile=""
	local StopFile=""
	local CurrentChild=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" != "" ] ; then
		if [ "$ChildProfileExtension" != "" ] ; then
			TheExtension=".${ChildProfileExtension}"
			Operator="-f"
		else
			TheExtension=""
			Operator="-d"
		fi
		unset IFS ; for CurrentChild in "$@" ; do
			ChildProfile="${Childs_path_enabled}/${CurrentChild}${TheExtension}"
			if [ ! $Operator "$ChildProfile" ] ; then
				ChildProfile="${Childs_path_available}/${CurrentChild}${TheExtension}"
			fi
			if [ $Operator "$ChildProfile" ] ; then
				PidFile="${ChildsPidsDir}/${CurrentChild}.pid"
				StopFile="${ChildsPidsDir}/${CurrentChild}.stop"
				if [ -f "$PidFile" ] ; then
					Stop1Daemon "$PidFile" "$StopFile" "$ChildSingularName $CurrentChild" 1 1 "${ParO}DeleteChild called${ParC}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				DisableObject 0 "$CurrentChild"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					rm -f "$StopFile"
					rm -fr "${Childs_path_available}/${CurrentChild}${TheExtension}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				if [ $StatusCode -eq 0 ] ; then
					printf '%s\n' "$ChildSingularName $CurrentChild erased." 1>&2
				fi
			else
				printf '%s\n' "${sERROR}E: $ChildSingularName \"$CurrentChild\" not found at ${Childs_path_available}${fRESET}" 1>&2
				LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		done
	else
		printf '%s\n' "${sERROR}E: $ChildSingularName not specified.${fRESET}" 1>&2
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

ListChilds ()
{
	local TheChilds=""
	local CurrentChild=""

	TheChilds="$(AvailableChilds_IDs)"
	TheChilds="$(printf '%s\n' "$TheChilds" ; EnabledChilds_IDs)"
	TheChilds="$(printf '%s\n' "$TheChilds" ; RunningChilds_IDs)"
	TheChilds="$(printf '%s\n' "$TheChilds" | grep -ve '^$' | sort -u)"
	IFS="$(printf '\n\b')" ; for CurrentChild in $TheChilds ; do unset IFS
		printf '%s\n' "$CurrentChild"
	done
}

ReportChilds ()
{
	local TheChilds=""
	local CurrentChild=""

	TheChilds="$(AvailableChilds_IDs)"
	TheChilds="$(printf '%s\n' "$TheChilds" ; EnabledChilds_IDs)"
	TheChilds="$(printf '%s\n' "$TheChilds" ; RunningChilds_IDs)"
	TheChilds="$(printf '%s\n' "$TheChilds" | grep -ve '^$' | sort -u)"
	IFS="$(printf '\n\b')" ; for CurrentChild in $TheChilds ; do unset IFS
		ListChildReport "$CurrentChild"
	done
}

ServiceHelp ()
{
	local SudoPrefix=''
	local SudoSuffix=''
	local ChildActions=''
	local InstallerActions=''
#[dhlan]
	local SpecificActions='hardware|scan|scan Scope|wol Names|mactoip|remote-monitor'
#[/dhlan]
	local BaseName=''
	
	BaseName="$(printf '%s\n' "$MeExecutable" | tr -s '/' '\n' | tail -n 1)"
	printf '%s\n' "${ShortDescription} ${ParO}${ServiceName}${ParC} $(ServiceVersion)"
	printf '%s\n' "$ServiceDocumentation"
	printf '%s\n' ""
	if [ $(MeInstalled) -eq 1 ] ; then
		if [ "$RootRequired" != "0" ] && [ $(id -u) -ne 0 ] ; then
			if [ "$(command -v sudo 2>/dev/null)" != "" ] ; then
				SudoPrefix="sudo "
			else
				SudoPrefix='su -c "'
				SudoSuffix='"'
			fi
		fi
		if [ "$ChildsPluralName" != "" ] ; then
			ChildActions="|reload|create|delete|list|report|details"
		fi
		if [ "$ProgramInstaller" = "1" ] ; then
			InstallerActions="|uninstall|purge"
		fi
		printf '%s\n' "Usage: ${SudoPrefix}${ServicePrefix}${ServiceName} {start|stop|restart|status|enable|disable${ChildActions}|${SpecificActions}${InstallerActions}}${SudoSuffix}"
		printf '%s\n' ""
		if [ $(id -u) -eq 0 ] && ! SystemProgramIsEnabled ; then
			printf '%s\n' "Example to enable the service:"
			printf '%s\n' "	$(RecommendedInvocation enable)"
		fi
		PidfileStatus "$MainControllerPidFile"
		AskedPidStatus=$?
		if [ $AskedPidStatus -ne 1 ] ; then
			printf '%s\n' "Example to start the service:"
			printf '%s\n' "	$(RecommendedInvocation start)"
		else
			printf '%s\n' "Example to stop the service:"
			printf '%s\n' "	$(RecommendedInvocation stop)"
		fi
		if [ "$ChildActions" != "" ] ; then
			printf '%s\n' "Example to see details about one ${ChildSingularName}:"
			printf '%s\n' "	$(RecommendedInvocation details ${ChildSingularName})"
			if [ "$DaemonizeChilds" = "1" ] ; then
				printf '%s\n' "Examples to run 1 shot only per each ${ChildSingularName}, and in a foreground sequential way:"
				printf '%s\n' "	$(printf '%s\n' "$MeCallFile" | tr -s '/' '\n' | tail -n 1) main 1"
				printf '%s\n' "	$(printf '%s\n' "$MeCallFile" | tr -s '/' '\n' | tail -n 1) childs-foreground 1"
			fi
		fi
	else
		if [ $(id -u) -ne 0 ] ; then
			if [ "$(command -v sudo 2>/dev/null)" != "" ] ; then
				SudoPrefix="sudo "
			else
				SudoPrefix='su -c "'
				SudoSuffix='"'
			fi
		fi
		if [ "$ProgramInstaller" = "1" ] ; then
			printf '%s\n' "To install as a system service:"
			printf '%s\n' "${SudoPrefix}${BaseName} install${SudoSuffix}"
			printf '%s\n' ""
			printf '%s\n' "To remove the service:"
			printf '%s\n' "${SudoPrefix}${ServicePrefix}${ServiceName} uninstall${SudoSuffix}"
		else
			printf '%s\n' "${sWARN}W: Program not installed. Use your package manager to install ${ServiceName}.${fRESET}"
		fi
	fi
}

ChildController_Kernel ()
{
	local ChildId="$1"
	local ChildProfile="$2"
	local MaxLoopsNr="$3"  # -1 for unlimited (default)
	local RemainLoopsNr=-1
	local ChildStopFile=""
	local ChildLog="${Childs_logs}/${ChildId}.log"	# Note: ChildController_Kernel() is already logged by caller
	local LastStatus=0
	local StatusCode=0
	
	ChildStopFile="${ChildsPidsDir}/${ChildId}.stop"
	if [ -f "$ChildStopFile" ] ; then printf '%s\n' "${sWARN}W: File $ChildStopFile avoids $ChildSingularName bucle.${fRESET}" 1>&2 ; fi
	if ! Is_IntegerNr "$MaxLoopsNr" ; then MaxLoopsNr=-1 ; fi
	if [ $MaxLoopsNr -ge 0 ] ; then RemainLoopsNr=$MaxLoopsNr ; fi
	while [ ! -f "$ChildStopFile" ] && [ $RemainLoopsNr -ne 0 ] ; do
		printf '%s\n' "$ChildsPluralName controller not yet developed." 1>&2
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		WaitStoppable 10 "$ChildStopFile" 0.5
		RemainLoopsNr=$((RemainLoopsNr - 1))
	done
	if [ -f "$ChildStopFile" ] ; then printf '%s\n' "I: $ChildSingularName $ChildId terminates with stop signal." 1>&2 ; fi
	Pipelined_Status=$StatusCode
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

ChildController ()
{
	local ChildId="$1"
	local MaxLoopsNr="$2"  # -1 for unlimited (default)
	local ChildProfile=""
	local Operator=""
	local TheExtension=""
	local LogLinesNr=""
	local PidFile=""
	local RunningPid=""
	local ChildLog="${Childs_logs}/${ChildId}.log"
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" != "" ] ; then
		if [ "$InitSoftware" = "systemd" ] ; then ChildId="$(printf '%s\n' "$ChildId" | sed -e 's|\\x20| |g')" ; fi	# systemd parses spaces as: \x20
		if [ $INIT_SCRIPT_InitCall -ne 1 ] ; then
			CallType=" ${ParO} Manual ${ParC}"
#		else
#			CallType=" ${ParO}INIT_SCRIPT_InitCall${ParC}"
		fi
		if [ "$ChildProfileExtension" != "" ] ; then
			TheExtension=".${ChildProfileExtension}"
			Operator="-f"
		else
			TheExtension=""
			Operator="-d"
		fi
		ChildProfile="${Childs_path_enabled}/${ChildId}${TheExtension}"
		if [ ! $Operator "$ChildProfile" ] ; then
			ChildProfile="${Childs_path_available}/${ChildId}${TheExtension}"
		fi
		if [ $Operator "$ChildProfile" ] ; then
			PidFile="${ChildsPidsDir}/${ChildId}.pid"
			PidfileStatus "$PidFile" # 0:Program is running 1:Program is not running and the pid file exists 3:Program is not running 4:Unable to determine program status
			LastStatus=$?
			if [ $LastStatus -eq 0 ] ; then RunningPid=$(cat "${ChildsPidsDir}/${ChildId}.pid" | sed -e 's| ||g') ; fi
#			if [ $LastStatus -ne 0 ] || [ $RunningPid -eq $$ ] ; then
			if [ $LastStatus -ne 0 ] || [ "$(ParentPstree $$ | sed -e 's|^| |g' | sed -e 's|$| |g' | grep -e " $RunningPid ")" != "" ] ; then
				# Child not running or it's me. This function can be called directly through "child-foreground" action.
				Pipelined_Status=""
#				printf "%(%Y-%m-%dT%T%z)T${CallType} Launching $ServiceName task for $ChildSingularName $ChildId" | tee -a "$ChildLog"
				printf "$(date '+%Y-%m-%dT%T%z')${CallType} Launching $ServiceName task for $ChildSingularName $ChildId" | tee -a "$ChildLog"
				if [ $Child_MaxLogLines -gt 0 ] ; then
					# Limited log lines number
					if [ -f "${Childs_logs}/${ChildId}.log.last" ] ; then
						printf '%s\n' "Recovering previous broken log from ${Childs_logs}/${ChildId}.log.last"
						LogLinesNr=$(cat "${Childs_logs}/${ChildId}.log.last" | wc -l)
						if [ $LogLinesNr -gt $Child_MaxLogLines ] ; then
							cat "${Childs_logs}/${ChildId}.log.last" | head -n $(($Child_MaxLogLines / 2)) >> "$ChildLog"
							printf '%s\n' "${ParO}...${ParC} $(($LogLinesNr - $Child_MaxLogLines)) log lines omitted here." >> "$ChildLog"
							cat "${Childs_logs}/${ChildId}.log.last" | tail -n $(($Child_MaxLogLines / 2)) >> "$ChildLog"
						else
							cat "${Childs_logs}/${ChildId}.log.last" >> "$ChildLog"
						fi
						rm "${Childs_logs}/${ChildId}.log.last"
					fi
					# Pipelines use makes threading one more process as own beginner's child.
					PrepareExitcodeToFile
					ChildController_Kernel "$ChildId" "$ChildProfile" "$MaxLoopsNr" 2>&1 | tee -a "${Childs_logs}/${ChildId}.log.last"
					GetFiledExitcode $?
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					if [ -f "${Childs_logs}/${ChildId}.log.last" ] ; then
						printf '%s\n' "Recovering previous broken log from ${Childs_logs}/${ChildId}.log.last"
						cat "${Childs_logs}/${ChildId}.log.last" >> "$ChildLog"
						rm "${Childs_logs}/${ChildId}.log.last"
					fi
					if [ $Child_MaxLogLines -eq 0 ] ; then
						# No log
						Start_Pre "$ChildId"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						ChildController_Kernel "$ChildId" "$ChildProfile" "$MaxLoopsNr"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						Start_Post "$ChildId"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						# Unlimited log
						# Pipelines use makes threading one more process as own beginner's child.
						PrepareExitcodeToFile
						Start_Pre "$ChildId" 2>&1 | tee -a "$ChildLog"
						GetFiledExitcode $?
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						PrepareExitcodeToFile
						ChildController_Kernel "$ChildId" "$ChildProfile" "$MaxLoopsNr" 2>&1 | tee -a "$ChildLog"
						GetFiledExitcode $?
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						PrepareExitcodeToFile
						Start_Post "$ChildId" 2>&1 | tee -a "$ChildLog"
						GetFiledExitcode $?
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
				if [ "$Pipelined_Status" != "" ] ; then
					LastStatus=$Pipelined_Status ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				if [ -f "${Childs_logs}/${ChildId}.log.last" ] ; then
					printf '%s\n' "Appending session log to ${Childs_logs}/${ChildId}.log"
					if [ $Child_MaxLogLines -gt 0 ] ; then
						LogLinesNr=$(cat "${Childs_logs}/${ChildId}.log.last" | wc -l)
						if [ $LogLinesNr -gt $Child_MaxLogLines ] ; then
							cat "${Childs_logs}/${ChildId}.log.last" | head -n $(($Child_MaxLogLines / 2)) >> "$ChildLog"
							printf '%s\n' "${ParO}...${ParC} $(($LogLinesNr - $Child_MaxLogLines)) log lines omitted here." >> "$ChildLog"
							cat "${Childs_logs}/${ChildId}.log.last" | tail -n $(($Child_MaxLogLines / 2)) >> "$ChildLog"
						else
							cat "${Childs_logs}/${ChildId}.log.last" >> "$ChildLog"
						fi
					else
						cat "${Childs_logs}/${ChildId}.log.last" >> "$ChildLog"
					fi
					rm "${Childs_logs}/${ChildId}.log.last"
				fi
			else
				LogProgram 1 "E: $ChildSingularName $ChildId is already running with PID ${RunningPid}."
				LastStatus=107 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			printf '%s\n' "${sERROR}E: $ChildSingularName $ChildId not found at ${Childs_path_available}${fRESET}" 1>&2
			LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		printf '%s\n' "${sERROR}E: $ChildSingularName not specified.${fRESET}" 1>&2
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Configuration_InitSystem ()
# THIS FUNCTION IS DEVELOPED AT PROJECT: init_script
# THIS FUNCTION IS COPIED TO PROJECTS: gesdonis
# This function must not write anything to disk. Mainly focused to detect InitSystem
{
	local ReadOnly=1
	local LastStatus=0
	local StatusCode=0
	
	if [ "$1" = "ro" ] ; then
		ReadOnly=1
		shift
	fi
	if [ "$1" = "pmc" ] ; then
		PackageManager_Call="$2"
		PackageManager_Mode="$3"
		PackageManager_ForVersion="$4"
	else
		PackageManager_Call=''
	fi
	if [ $(id -u) -eq 0 ] && [ "$(printf '%s\n' ":${PATH}:" | grep -e ':/sbin:' -e ':/usr/sbin:')" = "" ] ; then
		# Some old distribution's root (ie RHL 7) legate $PATH from normal user when called by "su"
		printf '%s\n' "${sERROR}E: You have not /sbin in PATH environment variable, you may be using a normal user environment ${ParO}${LOGNAME}${ParC}${fRESET}" 1>&2
		LastStatus=61 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	InitSoftware=""
	if [ "$(command -v /bin/systemctl 2>/dev/null)$(command -v systemctl 2>/dev/null)" != "" ] ; then
		# [ "$(ls /run/systemd/ 2>/dev/null)" != "" ]	Condition could not work in a chroot
		InitSoftware="systemd"
	else
		if [ "$(command -v /sbin/initctl 2>/dev/null)$(command -v initctl 2>/dev/null)" != "" ] ; then
			# (All GNU operating systems with Upstart have replaced it by systemd later)
			# Used by default in Ubuntu from version 6.10 to version 14.10
			# Used by default in Fedora from version 9 to version 14
			# Used by default in RHEL 6 (Red Hat, CentOS, Scientific Linux, Oracle Linux)
			# Used by default in Maemo 5 and MeeGo
			InitSoftware="upstart"
		else
			if [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] ; then
				# update-rc.d seems to be the LSB-compliant tool for System-V
				CurrentFile="$(command -v /sbin/runlevel 2>/dev/null)"
				if [ "$CurrentFile" = "" ] ; then CurrentFile="$(command -v runlevel 2>/dev/null)" ; fi
				if [ "$CurrentFile" != "" ] ; then
					if [ "$("$CurrentFile" --version 2>/dev/null | grep -ie upstart)" = "" ] ; then
						InitSoftware="systemv"
					else
						InitSoftware="upstart"
					fi
				fi
			else
				if [ -f /etc/inittab ] ; then
					# System-V
					InitSoftware="systemv"
				else
					InitSoftware=""
				fi
			fi
		fi
	fi
	ServiceEnabler=""
	InitProfilesDir=""
	InitProfilesDir_0=""
	ProgramExecutablePath=""
	InitExecutableParser=""
	if [ "$INIT_SCRIPT_InitCall" = "" ] ; then INIT_SCRIPT_InitCall=0 ; fi
	if [ $INIT_SCRIPT_InitCall -eq 0 ] ; then
		CallTree="$(ParentPstree $$)"
		if [ "$CallTree" != "" ] ; then
			CallTreeLevels="$(WordsNr () { printf '%s' $#; }; WordsNr $CallTree)"
			if [ $CallTreeLevels -le 2 ] ; then INIT_SCRIPT_InitCall=1 ; fi
		fi
	fi
	case "$InitSoftware" in
		"systemd" )
			ServiceEnabler="systemctl"
			if [ -d "/etc/systemd/system" ] ; then
				InitProfilesDir="/etc/systemd/system"
			fi
			if [ -d "/lib/systemd/system" ] ; then
				InitProfilesDir_0="/lib/systemd/system"
			fi
			if [ "$RootRequired" = "0" ] ; then
				ProgramExecutablePath="/bin/${ServiceName}"
			else
				ProgramExecutablePath="/sbin/${ServiceName}"
			fi
#			if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
				ProgramExecutablePath="/usr${ProgramExecutablePath}"
#			fi
			InitExecutableParser=""
			ServicePrefix=""
			if [ $INIT_SCRIPT_InitCall -eq 0 ] && [ "$_SYSTEMCTL_SKIP_REDIRECT" != "" ] ; then
				INIT_SCRIPT_InitCall=1
			fi
			;;
		"upstart" )
			if [ -d /etc/init ] ; then
				# Directory present in Ubuntu from version 9.10 to version 16.04 or more (with and without Upstart)
				ServiceEnabler="/etc/init" # upstart jobs are all enabled when profile is present at /etc/init/
				InitProfilesDir="/etc/init"
			else
				# Not all upstart versions had own /etc/init to place native job profiles
				if [ "$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)" != "" ] ; then
					ServiceEnabler="update-rc.d"
				else
					if [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)" != "" ] ; then
						ServiceEnabler="insserv"
					else
						if [ "$(command -v /sbin/chkconfig 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] ; then
							ServiceEnabler="chkconfig"
						fi
					fi
				fi
			fi
			if [ -d "/etc/init.d" ] ; then
				InitExecutableParser="/etc/init.d/${ServiceName}"
				if [ "$RootRequired" = "0" ] ; then
					ProgramExecutablePath="/bin/${ServiceName}"
				else
					ProgramExecutablePath="/sbin/${ServiceName}"
				fi
#				if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
					ProgramExecutablePath="/usr${ProgramExecutablePath}"
#				fi
			else
				if [ -d /etc/rc.d/init.d ] ; then
					InitExecutableParser="/etc/rc.d/init.d/${ServiceName}"
					if [ "$RootRequired" = "0" ] ; then
						ProgramExecutablePath="/bin/${ServiceName}"
					else
						ProgramExecutablePath="/sbin/${ServiceName}"
					fi
#					if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
						ProgramExecutablePath="/usr${ProgramExecutablePath}"
#					fi
				else
					if [ "$RootRequired" = "0" ] ; then
						ProgramExecutablePath="/bin/${ServiceName}"
					else
						ProgramExecutablePath="/sbin/${ServiceName}"
					fi
#					if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
						ProgramExecutablePath="/usr${ProgramExecutablePath}"
#					fi
					InitExecutableParser=""
				fi
			fi
			ServicePrefix=""
			if [ ! -f "/etc/init/${ServiceName}.conf" ] && [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] && [ "$(command -v /sbin/runlevel 2>/dev/null)$(command -v runlevel 2>/dev/null)" != "" ] ; then
				if [ -x "/etc/init.d/${ServiceName}" ] || [ -x "/etc/rc.d/init.d/${ServiceName}" ] ; then
					# Let's use SystemV compatibility layer; it can be an upgraded environment from SystemV to Systemd
					if [ "$(command -v service 2>/dev/null)" != "" ] ; then
						ServicePrefix="service "
					else
						if [ "$(command -v invoke-rc.d 2>/dev/null)" != "" ] ; then
							ServicePrefix="invoke-rc.d "
						fi
					fi
				fi
			fi
			if [ $INIT_SCRIPT_InitCall -eq 0 ] && [ "$UPSTART_JOB" != "" ] ; then
				INIT_SCRIPT_InitCall=1
			fi
			;;
		"systemv" )
			if [ "$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)" != "" ] ; then
				ServiceEnabler="update-rc.d"
			else
				if [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)" != "" ] ; then
					ServiceEnabler="insserv"
				else
					if [ "$(command -v /sbin/chkconfig 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] ; then
						ServiceEnabler="chkconfig"
					fi
				fi
			fi
			InitProfilesDir="" # System-V does not use job profile files
			if [ -d "/etc/init.d" ] ; then
				InitExecutableParser="/etc/init.d/${ServiceName}"
				if [ "$RootRequired" = "0" ] ; then
					ProgramExecutablePath="/bin/${ServiceName}"
				else
					ProgramExecutablePath="/sbin/${ServiceName}"
				fi
#				if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
					ProgramExecutablePath="/usr${ProgramExecutablePath}"
#				fi
			else
				if [ -d /etc/rc.d/init.d ] ; then
					InitExecutableParser="/etc/rc.d/init.d/${ServiceName}"
					if [ "$RootRequired" = "0" ] ; then
						ProgramExecutablePath="/bin/${ServiceName}"
					else
						ProgramExecutablePath="/sbin/${ServiceName}"
					fi
#					if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
						ProgramExecutablePath="/usr${ProgramExecutablePath}"
#					fi
				else
					if [ "$RootRequired" = "0" ] ; then
						ProgramExecutablePath="/bin/${ServiceName}"
					else
						ProgramExecutablePath="/sbin/${ServiceName}"
					fi
#					if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
						ProgramExecutablePath="/usr${ProgramExecutablePath}"
#					fi
					InitExecutableParser=""
				fi
			fi
			# Must be useful for any action (install|--help|etc)
			if [ "$(command -v service 2>/dev/null)" != "" ] ; then
				ServicePrefix="service "
			else
				if [ "$(command -v invoke-rc.d 2>/dev/null)" != "" ] ; then
					ServicePrefix="invoke-rc.d "
				fi
			fi
			if [ $INIT_SCRIPT_InitCall -eq 0 ] && [ "${RUNLEVEL}${runlevel}${INIT_VERSION}" != "" ] ; then
				INIT_SCRIPT_InitCall=1
			fi
			if [ $INIT_SCRIPT_InitCall -eq 0 ] && [ "$PWD" = "/" ] && [ "$HOME" = "/root" ] ; then
				# E.g. Debian 3.1 needs this
				unset IFS ; for CurPid in $CallTree ; do
					if [ "$(ps $CurPid | grep -e ' service' -e '/service' -e ' invoke' -e '/invoke')" != "" ] ; then
						INIT_SCRIPT_InitCall=1
					fi
				done
			fi
			;;
		* )
			# Must be useful for any action (install|--help|etc)
			if [ "$(command -v service 2>/dev/null)" != "" ] ; then
				ServicePrefix="service "
			else
				if [ "$(command -v invoke-rc.d 2>/dev/null)" != "" ] ; then
					ServicePrefix="invoke-rc.d "
				fi
			fi
			if [ $INIT_SCRIPT_InitCall -eq 0 ] ; then
				if [ "${RUNLEVEL}${runlevel}${UPSTART_JOB}${UPSTART_EVENTS}${_SYSTEMCTL_SKIP_REDIRECT}${INIT_VERSION}" != "" ] || [ "$TERM" = "linux" ] ; then
					INIT_SCRIPT_InitCall=1
				fi
			fi
			;;
	esac
	InitToolForStartStop="$ServicePrefix"
	if [ "$InitToolForStartStop" = "" ] && [ "$InitSoftware" = "systemd" ] ; then InitToolForStartStop="systemctl" ; fi
	if [ "$InitToolForStartStop" = "" ] && [ "$InitSoftware" = "upstart" ] ; then InitToolForStartStop="initctl" ; fi
	RcLocal=""
	if [ -f /etc/rc.d/rc.local ] ; then RcLocal=/etc/rc.d/rc.local ; fi
	if [ -f /etc/rc.local ] ; then RcLocal=/etc/rc.local ; fi
	if [ "$ServiceEnabler" = "" ] && [ ! -d /etc/rc1.d ] && [ ! -d /etc/rc.d/rc1.d ] && [ -f "$RcLocal" ] ; then
		ServiceEnabler="$RcLocal"
	fi
	export INIT_SCRIPT_InitCall=$INIT_SCRIPT_InitCall
	return $StatusCode
}

Install_precp ()
# WARNING: Package manager cannot invoke this action before this script file is installed. Install_postcp() calls this with argument "postcp" if didn't run.
{
	local Mode="$1"
	local Mode2="$2"
	local InitCall_Previous="$INIT_SCRIPT_InitCall"
	local PreStopTimeout=0
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		Install_precp_Pre "$(Dirname "$MeExecutable")" "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	TheFoundServiceScript="$(FoundServiceScript)"
	if [ $StatusCode -eq 0 ] && [ -x "$TheFoundServiceScript" ] ; then
		if [ "$PackageManager_Call" = "" ] && [ "$Mode" != "postcp" ] && [ "$Mode" != "force" ] && [ "$Mode2" != "force" ] ; then
#			if [ "$(IniVarValue "$(cat "$TheFoundServiceScript" | grep -e '^ProgramInstaller=' | head -n 1)" ProgramInstaller '' '' = '' '')" = "0" ] ; then
			if [ "$(cat "$TheFoundServiceScript" | grep -e '^ProgramInstaller=' | head -n 1 | grep -e '^ProgramInstaller=0')" != "" ] ; then
				printf '%s\n' "${sERROR}E: Program already installed. Use your package manager to un/install $(printf '%s\n' "$TheFoundServiceScript" | tr -s '/' '\n' | tail -n 1).${fRESET}" 1>&2
				LastStatus=99 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			# /PreviousService* records used here on manual script upgrade
			if [ ! -f "${DirTemp}/PreviousServiceEnabled.${ServiceName}" ] ; then
				if [ "$PreviousServiceEnabled" = "" ] ; then
					if SystemProgramIsEnabled ; then
						PreviousServiceEnabled=1
					else
						PreviousServiceEnabled=0
					fi
				fi
				echo $PreviousServiceEnabled > "${DirTemp}/PreviousServiceEnabled.${ServiceName}"
			fi
			if [ $TimeoutStartSec -ge 1 ] ; then
				PreStopTimeout=$TimeoutStartSec
			else
				PreStopTimeout=5
			fi
			if [ $GentleStopTimeoutS -ge 1 ] ; then
				PreStopTimeout=$((PreStopTimeout + $GentleStopTimeoutS))
			else
				PreStopTimeout=$((PreStopTimeout + 5))
			fi
			AllChildsStopTimeoutS=$(ls -1A "${CurrentConfigDir}/${ChildsPluralName}_available" 2>/dev/null | wc -l)
			if [ $GentleStopTimeoutS -ge 1 ] ; then
				AllChildsStopTimeoutS=$(($AllChildsStopTimeoutS * $GentleStopTimeoutS))
				if [ $AllChildsStopTimeoutS -lt $GentleStopTimeoutS ] ; then AllChildsStopTimeoutS=$GentleStopTimeoutS ; fi
			fi
			PreStopTimeout=$((PreStopTimeout + $AllChildsStopTimeoutS))
			if [ "$StopStartOnUpgrade" = "1" ] ; then
				PidfileStatus "$MainControllerPidFile"
				LastStatus=$?
				if [ $LastStatus -eq 0 ] ; then
					printf '%s\n' "Trying to stop previous $ServiceName"
					PreviousServiceRunning=1
					echo $PreviousServiceRunning > "${DirTemp}/PreviousServiceRunning.${ServiceName}"
				fi
				TimedExecution $((PreStopTimeout * 2)) 2 "" $(RecommendedInvocation stop) 2>&1 | grep -ive 'Unknown'
			else
				rm -f "${DirTemp}/PreviousServiceRunning.${ServiceName}"
			fi
			if [ "$Mode" != "postcp" ] ; then
				printf '%s\n' "Trying to uninstall previous $ServiceName"
				# Sub-execution will also spend time trying to stop service
				TimedExecution $((PreStopTimeout * 2)) 1 "" "$TheFoundServiceScript" uninstall "$@" 2>&1 | grep -ive 'Trying to stop' | grep -ive 'not running' | grep -ive 'Unknown'
				#LastStatus=$?	pipelines override execution exitcode.
				#if [ $LastStatus -eq 87 ] || [ $LastStatus -eq 127 ] ; then # Unknown action
				#	rm "$TheFoundServiceScript"
				#	LastStatus=$?
				#fi
				#if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ -x "$TheFoundServiceScript" ] ; then
					printf '%s\n' "${sWARN}W: old executable $TheFoundServiceScript not removed.${fRESET}" 1>&2
					printf '%s\n' "Actions recommended:"
					printf '%s\n' "	1. Sure service ${ServiceName} is stopped"
					printf '%s\n' "	2. Retry un/installation"
					printf '%s\n' "	3. Remove old program manually if necessary"
					LastStatus=110 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			# Important to do this after uninstalling previous version.
			Configuration_Saved "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ -d "$SystemConfigDir" ] ; then
				printf '%s\n' "System level configurations directory is: $SystemConfigDir"
			fi
			printf '%s\n' "System level parameters are at: $SystemConfigFile"
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Install_precp_Post "$(Dirname "$MeExecutable")" "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		# Old executables were a duplicity of service script.
		DeleteIfNotMe "/etc/rc.d/init.d/${ServiceName}"
		DeleteIfNotMe "/etc/init.d/${ServiceName}"
		DeleteIfNotMe "/bin/${ServiceName}"
		DeleteIfNotMe "/sbin/${ServiceName}"
		DeleteIfNotMe "/usr/bin/${ServiceName}"
		DeleteIfNotMe "/usr/sbin/${ServiceName}"
		DeleteIfNotMe "/usr/local/bin/${ServiceName}"
		DeleteIfNotMe "/usr/local/sbin/${ServiceName}"
		DeleteIfNotMe "/etc/rc.d/init.d/${ServiceName}.sh"
		DeleteIfNotMe "/etc/init.d/${ServiceName}.sh"
		DeleteIfNotMe "/bin/${ServiceName}.sh"
		DeleteIfNotMe "/sbin/${ServiceName}.sh"
		DeleteIfNotMe "/usr/bin/${ServiceName}.sh"
		DeleteIfNotMe "/usr/sbin/${ServiceName}.sh"
		DeleteIfNotMe "/usr/local/bin/${ServiceName}.sh"
		DeleteIfNotMe "/usr/local/sbin/${ServiceName}.sh"
		DeleteIfNotMe "$InitExecutableParser"
		DeleteIfNotMe "$ProgramExecutablePath"
	fi
	touch "${DirTemp}/${ServiceName}.install-precp.done"
	return $StatusCode
}

Install_cp ()
{
	local SourceScript="$1"
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		if [ "$SourceScript" != "$ProgramExecutablePath" ] && [ "$SourceScript" != "$InitExecutableParser" ] ; then
			PreviousUmask="$(umask)"
			umask u=rwx,go=rx
			mkdir -p "$(Dirname "$ProgramExecutablePath")"
			umask "$PreviousUmask"
			cp "$SourceScript" "$ProgramExecutablePath"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			chown root:root "$ProgramExecutablePath"
			chmod u=rwx,go=rx "$ProgramExecutablePath"
#			if [ $StatusCode -eq 0 ] && [ "$(printf '%s\n' "$ProgramExecutablePath" | grep -e '^/usr/' -e '^/bin/' -e '^/sbin/')" = "" ] && [ "$(printf '%s\n' "$InitExecutableParser" | grep -e '^/usr/' -e '^/bin/' -e '^/sbin/')" != "" ] ; then
#				# Program has been installed in an init place such as /etc/init.d/ and link is intended to allow normal PATH access.
#				PreviousUmask="$(umask)"
#				umask u=rwx,go=rx
#				rm -f "$InitExecutableParser"
#				ln -s "$ProgramExecutablePath" "$InitExecutableParser"
#				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				umask "$PreviousUmask"
#			fi
		else
			printf '%s\n' "${sERROR}E: Cannot install from destination path.${fRESET}" 1>&2
			LastStatus=94 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

Install_postcp ()
{
	local MinAgeLine=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -f "${DirTemp}/${ServiceName}.install-precp.done" ] ; then
		Install_precp postcp
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		rm -f "${DirTemp}/${ServiceName}.install-precp.done"
	fi
	if [ $StatusCode -eq 0 ] ; then
		InstallSystemService "$ProgramExecutablePath" "$ServiceName" "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$PreviousServiceEnabled" = "" ] ; then PreviousServiceEnabled="$(cat "${DirTemp}/PreviousServiceEnabled.${ServiceName}" 2>/dev/null)" ; fi
		rm -f "${DirTemp}/PreviousServiceEnabled.${ServiceName}"
		if [ "$PreviousServiceEnabled" = "1" ] ; then
			# Keep enablement status
			EnableSystemService 0
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ "$PreviousServiceEnabled" = "0" ] ; then
				# Keep enablement status
				DisableSystemService 0 > /dev/null 2>&1
			else
				# Set first enablement status
				if [ "$DisabledAtFirst" = "0" ] ; then
					EnableSystemService 0
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					DisableSystemService 0 > /dev/null 2>&1
				fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Install_postcp_More "$(Dirname "$MeExecutable")" "$@"	#"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		# Writing system-wide defaults
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$LogRotation" != "" ] && [ -d /etc/logrotate.d ] && [ $(id -u) -eq 0 ] ; then
		if [ "$LogRotation" != "hourly" ] ; then MinAgeLine='minage 1' ; fi
		if [ "$Childs_logs" != "" ] ; then
			printf '%s\n' "\"${MainControllerLog}\" \"${Childs_logs}\"/* {
	compress
	notifempty
	nocreate
	rotate 5
	$LogRotation
	$MinAgeLine
	missingok
}" > "/etc/logrotate.d/${ServiceName}"
		else
			printf '%s\n' "\"${MainControllerLog}\" {
	compress
	notifempty
	nocreate
	rotate 5
	$LogRotation
	$MinAgeLine
	missingok
}" > "/etc/logrotate.d/${ServiceName}"
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$PreviousServiceRunning" = "" ] ; then PreviousServiceRunning="$(cat "${DirTemp}/PreviousServiceRunning.${ServiceName}" 2>/dev/null)" ; fi
		rm -f "${DirTemp}/PreviousServiceRunning.${ServiceName}"
		if [ "$PreviousServiceRunning" = "1" ] ; then
			printf '%s\n' "Starting $ServiceName $(ServiceVersion)"
			if SystemProgramIsEnabled ; then
				eval $(RecommendedInvocation start)
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				Start "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	return $StatusCode
}

Uninstall_predel ()
{
	local LastStatus=0
	local StatusCode=0
	
	# /PreviousService* records used here on package manager upgrade
	if [ ! -f "${DirTemp}/PreviousServiceEnabled.${ServiceName}" ] ; then
		if [ "$PreviousServiceEnabled" = "" ] ; then
			if SystemProgramIsEnabled ; then
				PreviousServiceEnabled=1
			else
				PreviousServiceEnabled=0
			fi
		fi
		echo $PreviousServiceEnabled > "${DirTemp}/PreviousServiceEnabled.${ServiceName}"
	fi
	TheFoundServiceScript="$(FoundServiceScript)"
	if [ "$StopStartOnUpgrade" = "1" ] ; then
		PidfileStatus "$MainControllerPidFile"
		LastStatus=$?
		if [ $LastStatus -eq 0 ] ; then
			PreviousServiceRunning=1
			echo $PreviousServiceRunning > "${DirTemp}/PreviousServiceRunning.${ServiceName}"
		fi
		if [ -x "$TheFoundServiceScript" ] ; then
			printf '%s\n' "Trying to stop $ServiceName"
			eval $(RecommendedInvocation stop)
			sleep 1
		fi
	fi
	Uninstall_predel_More "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	UnInstallSystemService "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "System service removed."
	fi
	return $StatusCode
}

Uninstall_del ()
{
	local MeVersion''
	MeVersion="$(ServiceVersion)"
#	DeleteIfNotMe "$InitExecutableParser"
#	DeleteIfNotMe "$ProgramExecutablePath"
#	DeleteIfNotMe "/etc/rc.d/init.d/${ServiceName}"
#	DeleteIfNotMe "/etc/init.d/${ServiceName}"
#	DeleteIfNotMe "/bin/${ServiceName}"
#	DeleteIfNotMe "/sbin/${ServiceName}"
#	DeleteIfNotMe "/usr/bin/${ServiceName}"
#	DeleteIfNotMe "/usr/sbin/${ServiceName}"
#	DeleteIfNotMe "/usr/local/bin/${ServiceName}"
#	DeleteIfNotMe "/usr/local/sbin/${ServiceName}"
	rm -f "/bin/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/sbin/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/bin/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/sbin/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/bin/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/sbin/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/etc/init.d/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/etc/rc.d/init.d/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/bin/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/sbin/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/bin/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/sbin/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/bin/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/sbin/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/etc/init.d/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/etc/rc.d/init.d/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "$InitExecutableParser"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "$ProgramExecutablePath"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "$ProgramName $MeVersion : Program files removed."
	fi
}

Uninstall_postdel ()
{
	local LastStatus=0
	local StatusCode=0
	
	Uninstall_Postdel_More "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

Uninstall_purge ()
{
	local LastStatus=0
	local StatusCode=0
	
	Uninstall_purge_MorePre "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ -f "$SystemConfigFile" ] ; then
		printf '%s\n' "Removing: $SystemConfigFile"
		rm "$SystemConfigFile"
	fi
	if [ -d "$SystemConfigDir" ] ; then
		printf '%s\n' "Removing: $SystemConfigDir"
		rm -r "$SystemConfigDir"
	fi
	if [ "$ChildsPluralName" != "" ] ; then
		rm -fr "$Childs_logs"
	fi
	rm -f "$MainControllerLog"
	rm -f "/var/log/upstart/${ServiceName}."*
	rm -f "/etc/logrotate.d/${ServiceName}"
	Uninstall_purge_MorePost "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "${DirTemp}/${ServiceName}.uninstall.tmp" "/var/tmp/${ServiceName}.uninstall.tmp"
	if [ $LastStatus -eq 0 ] ; then
		printf '%s\n' "Configurations and logs cleaned."
	fi
	return $StatusCode
}

GetSetLocalConfig ()
# Syntax as a function: $(GetSetLocalConfig "$ReadOnly" "$VariableName" "$SectionName" "$DefaultValue" "$PreComment")

# Function taken from script.sh template.

# Description: Gets the most local value for a configuration (1:user 2:system 3:filedefault 4:specifieddefault) and optionally writes if nowhere found.
# Expected parameters:
#	$1	"1" to not write anything at files; "0" to save variable when not found.
#	$2	Variable name to query
#	$3	(optional or empty) [section name] without brackets []. Examples: "global" ""
#	$4	(optional or empty) Value to return in case of not finding the variable in any file. It's used to write the new variable entry too.
#	$5	(optional or empty) Line to precede variable's line if it must be added. Comment mark should be included.
# Notes:
#	- NOT USEFUL when default value can be invalid for unprivileged user, such as file/dir paths to write: [system.conf=]/etc/custom.list vs [user.conf=]~/custom.list
#	  NOT USEFUL for other config files but SystemConfigFile & UserConfigFile
#	  In these cases, it's convenient to use GetOrSetIniVarValue()
#	- USEFUL when need to use system-level value on user-level lack.
#	- $SystemDefaults is used as DefaultsFile. A found value here prevails over 4th parameter (DefaultValue)
#	- If DefaultValue is to be written (because no value found anywhere and ReadOnly==0), root user will write to $SystemConfigFile and others will write to $UserConfigFile
#	- if section is not specified or empty, treats whole file content.
#	- if section is "[]", treats only file content before any [section] declaration.
#	- Separator between variable name and assigned value is assumed to be '='
#	- No end variable/assignation mark assumed.
# Depends on functions: IniVarValue SetIniVarValue
# Depends on other software: grep
{
	local ReadOnly="$1"
	local VariableName="$2"
	local SectionName="$3"
	local DefaultValue="$4"	# If quotes are needed ("value") must be already contained in the supplied value ("\"value\""). Same with EndVariableSymbol.
	local PreComment="$5"
	local DefaultsFile="$SystemDefaults"
	local NotFoundKey=''
	local Value=''
	local LastStatus=0
	local StatusCode=0
	
	NotFoundKey="#GOSIVV$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d ' ')#"
	Value="$(IniVarValue "$UserConfigFile" "$VariableName" "$SectionName" "$NotFoundKey" = '' '')"
	if [ "$Value" = "$NotFoundKey" ] ; then
		Value="$(IniVarValue "$SystemConfigFile" "$VariableName" "$SectionName" "$NotFoundKey" = '' "$DefaultsFile")"
		if [ "$Value" = "$NotFoundKey" ] ; then
			if [ $ReadOnly -eq 0 ] ; then
				if [ $(id -u) -eq 0 ] ; then
					SetIniVarValue "$SystemConfigFile" "$VariableName" "$SectionName" "$DefaultValue" '=' "$PreComment"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					SetIniVarValue "$UserConfigFile" "$VariableName" "$SectionName" "$DefaultValue" '=' "$PreComment"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
			Value="$DefaultValue"
			if [ "$(printf '%s\n' "$Value" | grep -e '^".*"$')" != "" ] ; then Value="$(printf '%s\n' "$Value" | cut -f 2 -d '"')" ; fi	#'
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s' "$Value" ; fi
	return $StatusCode
}

Configuration_Early ()
# To minimize load on fast fork. Convenient to not write to disk.
{
	local LastStatus=0
	local StatusCode=0
	
	MeExecutable="$(ReadlinkF "$MeCallFile")"
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	if [ -d /run ] ; then
		MainControllerPidFile="/run/${ServiceName}.pid"
	else
		# Old FHS
		MainControllerPidFile="/var/run/${ServiceName}.pid"
	fi
	if [ "$RootRequired" = "0" ] && [ $(id -u) -ne 0 ] ; then
		MainControllerPidFile="${DirTemp}/${ServiceName}.$(id -u)/${ServiceName}.pid"
	else
		MainControllerLog="/var/log/${ServiceName}.log"
	fi
	Configuration_Early_More "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

Configuration_Saved ()
# Settings that can be saved to disk, unless "ro" parameter is passed
{
	local ReadOnly=0
	local CallTree=''
	local CallTreeLevels=''
	local CurrentFile=''
	local VarDescription=''
	local BaseName=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$1" = "ro" ] ; then ReadOnly=1 ; shift ; fi
	BreakingControls
	Configuration_Early "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	ShortDescription="$(LsbHeaderValue 'Short-Description')"
	LongDescription="$(LsbHeaderValue 'Description')"
	Accumulated_Childs=0
	Accumulated_ChildsRunning=0
	Accumulated_pid=0
	Accumulated_start=now
	Accumulated_pcpu10=0
	Accumulated_rss=0
	Accumulated_Threads=0
	Configuration_Saved_Pre "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	# Global stop file is an external signal to abandon any program activity (start/wait/stop) and it's never invoked by program itself.
	if [ -d /run ] ; then
		MainControllerStopFile="/run/${ServiceName}.stop"
	else
		# Old FHS
		MainControllerStopFile="/var/run/${ServiceName}.stop"
	fi
	if [ $ReadOnly -eq 0 ] && [ -w "$(Dirname "$SystemConfigDir")" ] ; then
		MkdirAndOrPublic "$SystemConfigDir" '' u=rwX,go=rX
	fi
	SystemDefaults="${SystemConfigDir}/system.defaults"	# This file is never written by program; only provided by package manager and updates
	SystemConfigFile="${SystemConfigDir}/system.conf"
	if [ -f "/etc/default/${ServiceName}" ] ; then
		# Files structure upgrade
		if [ $ReadOnly -eq 0 ] ; then
			mv "/etc/default/${ServiceName}" "$SystemConfigFile"
		else
			SystemConfigFile="/etc/default/${ServiceName}"
		fi
	fi
	if [ -f "${SystemConfigDir}/default.conf" ] ; then
		# Files structure upgrade
		if [ $ReadOnly -eq 0 ] && [ $(id -u) -eq 0 ] ; then
			mv "${SystemConfigDir}/default.conf" "$SystemConfigFile"
		else
			SystemConfigFile="${SystemConfigDir}/default.conf"
		fi
	fi
	BaseName="$(printf '%s\n' "$SystemConfigDir" | tr -s '/' '\n' | tail -n 1)"
	UserDir="$HOME"
	if [ ! -d "$UserDir" ] ; then
		# Sometimes Systemd does not set HOME variable
		UserDir="$(cat /etc/passwd | grep -e "^$(id -un):" | cut -f 6 -d ':')"
		if [ ! -d "$UserDir" ] && [ "$HOME" != "" ] ; then
			UserDir="$HOME"
		fi
	fi
	if [ "$(printf '%s\n' "$BaseName" | grep -e '^\.')" != "" ] ; then
		UserConfigDir="${UserDir}/${BaseName}"
	else
		UserConfigDir="${UserDir}/.${BaseName}"
	fi
	if [ ! -d "$UserDir" ] ; then
		UserDir="$(pwd)"
		UserConfigDir="${UserDir}/${BaseName}"
	fi
	if [ ! -w "$UserDir" ] ; then
		UserDir="$(pwd)"
		UserConfigDir="${UserDir}/${BaseName}"
	fi
	UserDefaults="${UserConfigDir}/user.defaults"	# This file is never written by program; only provided by package manager/skel
	UserConfigFile="${UserConfigDir}/user.conf"
	if [ $ReadOnly -eq 0 ] ; then MkdirAndOrPublic "$UserConfigDir" ; fi
	if [ -f "${UserConfigDir}/default.conf" ] ; then
		# Files structure upgrade
		if [ $ReadOnly -eq 0 ] ; then
			mv "${UserConfigDir}/default.conf" "$UserConfigFile"
		else
			UserConfigFile="${UserConfigDir}/default.conf"
		fi
	fi
	CurrentConfigDir="$SystemConfigDir"
	CurrentConfigFile="$SystemConfigFile"
	if [ "$RootRequired" = "0" ] && [ $(id -u) -ne 0 ] ; then
		CurrentConfigDir="$UserConfigDir"
		CurrentConfigFile="$UserConfigFile"
		# Global stop file is an external signal to abandon any program activity (start/wait/stop) and it's never invoked by program itself.
		MainControllerStopFile="${DirTemp}/${ServiceName}.$(id -u)/${ServiceName}.stop"
	fi
	if [ -f "${SystemConfigDir}/${ServiceName}.conf" ] && [ "$SystemConfigFile" != "${SystemConfigDir}/${ServiceName}.conf" ] ; then
		# Old config file present
		if [ $ReadOnly -eq 0 ] ; then
			# Migrate old file to new path
			if [ ! -f "$SystemConfigFile" ] ; then
				mv "${SystemConfigDir}/${ServiceName}.conf" "$SystemConfigFile"
			else
				cat "${SystemConfigDir}/${ServiceName}.conf" >> "$SystemConfigFile"
				mv "${SystemConfigDir}/${ServiceName}.conf" "${SystemConfigDir}/${ServiceName}.conf.bak"
			fi
		else
			# Assume old file as the good one
			if [ ! -f "$SystemConfigFile" ] ; then SystemConfigFile="${SystemConfigDir}/${ServiceName}.conf" ; fi
		fi
	fi
	if [ $(id -u) -eq 0 ] && [ $ReadOnly -eq 0 ] ; then
		if [ -f "/etc/default/${ServiceName}" ] ; then
			# Remove old file only dedicated to enablement variable.
			SystemvEnablementData="$(cat "/etc/default/${ServiceName}" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ive '^ENABLED=' -ive '^RUN=' | grep -ve '^$')"
			if [ "$SystemvEnablementData" = "" ] ; then rm "/etc/default/${ServiceName}" ; fi
		fi
	fi
	if [ "$ChildsPluralName" != "" ] ; then
		# Only when working with unprivileged childs, user config directory is sure necessary
		if [ "$RootRequired" != "1" ] && [ $ReadOnly -eq 0 ] ; then
			MkdirAndOrPublic "$UserConfigDir" '' u=rwX,g=rX,o=
		fi
	fi
	if [ $ReadOnly -eq 0 ] && [ -w "$(Dirname "$SystemConfigFile")" ] ; then
		if [ ! -f "$SystemConfigFile" ] ; then
			printf '%s\n' "# $LongDescription" >> "$SystemConfigFile"
			if [ "$RootRequired" = "0" ] ; then
				printf '%s\n' "# $ServiceName system-wide and user-default working parameters" >> "$SystemConfigFile"
			else
				printf '%s\n' "# $ServiceName working parameters" >> "$SystemConfigFile"
			fi
			printf '%s\n' "" >> "$SystemConfigFile"
			printf '%s\n' "# Main log is written to: $MainControllerLog" >> "$SystemConfigFile"
			printf '%s\n' "# You can create this file as a signal to program abandons any activity (start/wait/stop): $MainControllerStopFile" >> "$SystemConfigFile"
			printf '%s\n' "" >> "$SystemConfigFile"
		fi
	fi
	if [ $(id -u) -ne 0 ] ; then
		MainControllerLog="${UserConfigDir}/${ServiceName}.log"
	fi
	UserTempDir="${DirTemp}/${ServiceName}.$(id -u)"
	if [ $ReadOnly -eq 0 ] ; then MkdirAndOrPublic "$UserTempDir" '' u=rwX,g=rX,o= ; fi
	UserTempDirX="${DirTempX}/${ServiceName}.$(id -u)"
	if [ $ReadOnly -eq 0 ] ; then MkdirAndOrPublic "$UserTempDirX" '' u=rwX,g=rX,o= ; fi
	if [ "$ChildsPluralName" != "" ] ; then
		if [ -d /run ] ; then
			ChildsPidsDir="/run/${ServiceName}.d"
		else
			# Old FHS
			ChildsPidsDir="/var/run/${ServiceName}.d"
		fi
		if [ $(id -u) -ne 0 ] ; then
			ChildsPidsDir="${UserTempDir}/${ChildsPluralName}.run"
		fi
		ChildsTempDir="${UserTempDir}/${ChildsPluralName}.d"
		if [ $ReadOnly -eq 0 ] ; then MkdirAndOrPublic "$ChildsTempDir" ; fi
		Childs_path_available="${CurrentConfigDir}/${ChildsPluralName}_available"
		if [ $ReadOnly -eq 0 ] ; then MkdirAndOrPublic "$Childs_path_available" ; fi
		Childs_path_enabled="${CurrentConfigDir}/${ChildsPluralName}_enabled"
		if [ $ReadOnly -eq 0 ] ; then MkdirAndOrPublic "$Childs_path_enabled" '' u=rwX,g=rX,o= ; fi
		LoadVarsValues "$UserConfigFile" "${ChildsPluralName}_logs ${ChildSingularName}_LoadWithCalm ${ChildSingularName}_MaxLogLines ${ChildsPluralName}_MoreLoadsInterval SystemdChilds" 2 "$SystemConfigFile"
		LastStatus=$?
		VarsTranslation="Childs_logs=\"\$${ChildsPluralName}_logs\" Child_LoadWithCalm=\"\$${ChildSingularName}_LoadWithCalm\" Child_MaxLogLines=\"\$${ChildSingularName}_MaxLogLines\" Childs_MoreLoadsInterval=\"\$${ChildsPluralName}_MoreLoadsInterval\""
		eval $VarsTranslation
		if [ $LastStatus -eq 104 ] ; then
			Childs_logs="/var/log/${ServiceName}_${ChildsPluralName}"
			if [ $(id -u) -ne 0 ] ; then
				Childs_logs="${UserConfigDir}/log"
			fi
			Childs_logs="$(GetSetLocalConfig "$ReadOnly" "${ChildsPluralName}_logs" '' "\"${Childs_logs}\"" "\n# Directory for all logs from $ChildSingularName tasks")"	#in
			if [ $ReadOnly -eq 0 ] ; then MkdirAndOrPublic "$Childs_logs" ; fi
			VarDescription="\n# Percent of ${ParO}descending${ParC} load average to wait for, before each $ChildSingularName automatic launch. Set it to -1 for not \"waiting to calm\""
			if [ "$DaemonizeOnStart" = "0" ] && [ $TimeoutStartSec -ge 0 ] ; then
				VarDescription="${VarDescription}\n# WARNING: Due to main controller is not forking as a daemon, service start can be killed after $TimeoutStartSec seconds."
			fi
			Child_LoadWithCalm="$(GetSetLocalConfig "$ReadOnly" "${ChildSingularName}_LoadWithCalm" '' "$Child_LoadWithCalm_Default" "$VarDescription")"
			if ! Is_IntegerNr "$Child_LoadWithCalm" ; then
				Child_LoadWithCalm=-1
				printf '%s\n' "${sWARN}W: Invalid ${ChildSingularName}_LoadWithCalm value at ${SystemConfigFile}${fRESET}" 1>&2
			fi
			Child_MaxLogLines="$(GetSetLocalConfig "$ReadOnly" "${ChildSingularName}_MaxLogLines" '' -1 "\n# ${ChildSingularName}_MaxLogLines: Set it to -1 for unlimited $ChildSingularName log, 0 for no $ChildsPluralName log, and great that 0 to limit log lines number to archive from each $ChildSingularName session.")"	#in
			if ! Is_IntegerNr "$Child_MaxLogLines" ; then
				Child_MaxLogLines=-1
				printf '%s\n' "${sWARN}W: Invalid ${ChildSingularName}_MaxLogLines value at ${SystemConfigFile}${fRESET}" 1>&2
			fi
			Childs_MoreLoadsInterval="$(GetSetLocalConfig "$ReadOnly" "${ChildsPluralName}_MoreLoadsInterval" '' $Childs_MoreLoadsInterval_Default "\n# ${ChildsPluralName}_MoreLoadsInterval: Seconds of interval to retry load the $ChildsPluralName that are enabled but not running. Zero for not more automatic loads after service start.")"	#in
			if ! Is_IntegerNr "$Childs_MoreLoadsInterval" ; then
				Childs_MoreLoadsInterval=0
				printf '%s\n' "${sWARN}W: Invalid ${ChildsPluralName}_MoreLoadsInterval value at ${SystemConfigFile}${fRESET}" 1>&2
			fi
			if [ "$DaemonizeChilds" != "0" ] && [ "$InitSoftware" = "systemd" ] ; then
				SystemdChilds="$(GetSetLocalConfig "$ReadOnly" SystemdChilds '' 0 "\n# Systemd is limiting the number of child processes to fork; 1=Use Systemd to launch $ChildsPluralName; 0=Launch $ChildsPluralName with independent method.")"
				# (this issue is not affecting to "stop" actions)
			fi
		else
			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$LogLevel" = "4" ] ; then ForcedLogLevel=$LogLevel ; fi  # Debugging probably a nested call (exported LogLevel)
	LoadVarsValues "$UserConfigFile" 'FirstProcessesToWaitFor CleanExecutionToWaitFor LogLevel MaxLogLines' 2 "$SystemConfigFile"
	LastStatus=$?
	if [ $LastStatus -eq 104 ] ; then
		VarDescription="\n# FirstProcessesToWaitFor: Wait until named processes are running before start actions. ${ParO}blank = no wait${ParC}.\n#FirstProcessesToWaitFor=NetworkManager,dbus-daemon"
		FirstProcessesToWaitFor="$(GetSetLocalConfig "$ReadOnly" FirstProcessesToWaitFor '' "" "$VarDescription")"
		VarDescription="\n# CleanExecutionToWaitFor: Wait until command exits with status code 0 before start actions. ${ParO}blank = no wait${ParC}.\n# Syntax is interval seconds to repeat execution|Command\n#CleanExecutionToWaitFor=\"10|nc -v -w 4 -z 216.146.35.35 53\"\n#CleanExecutionToWaitFor=\"60|/bin/true\""
		CleanExecutionToWaitFor="$(GetSetLocalConfig "$ReadOnly" CleanExecutionToWaitFor '' '""' "$VarDescription")"
		LogLevel="$(GetSetLocalConfig "$ReadOnly" LogLevel '' 3 '\n# LogLevel: 0=Nothing 1=Errors 2=Warnings+E 3=Info+W+E 4=Debug')"
		MaxLogLines="$(GetSetLocalConfig "$ReadOnly" MaxLogLines '' 0 '\n# MaxLogLines: 0=Unlimited')"
	else
		if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if ! Is_IntegerNr "$LogLevel" ; then LogLevel=3 ; fi
	if [ "$ForcedLogLevel" != "" ] ; then  # Debugging probably a nested call (exported LogLevel)
		LogLevel=$ForcedLogLevel
	fi
	if [ $ReadOnly -eq 0 ] ; then
		Configuration_Saved_Post "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		Configuration_Saved_Post ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Configuration ()
# Full configuration
{
	local ReadOnly=0
	local LastStatus=0
	local StatusCode=0
	
	if [ "$1" = "ro" ] ; then ReadOnly=1 ; fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$FastConfiguration" = "1" ] ; then
			Configuration_Early "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			Configuration_InitSystem "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			Configuration_Saved "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}


##### SERVICE FUNCTIONS from @-funcions x.x.x #####

#[dhlan]

EsIP ()
# Sintaxis como función: $(EsIP $Address)
# Descripción: Devuelve (stdout) "1" en caso de que el dato proporcionado valga como dirección IPv4
# Notas:
#	- Direcciones validas desde 0.0.0.0 hasta 255.255.255.255
# Depends on functions: Is_IntegerNr
# Depends on software packages: grep
{
	local Address="$1"
#	local NumeroActual=''
	local Address_b1=''
	local Address_b2=''
	local Address_b3=''
	local Address_b4=''
	local Value=0
	
	# Expresión encontrada por la web, pero que no funciona bien con todos los extremos numéricos.
	# Puede que sea un problema de desbordamiento de expresiones cuando hay pocos recursos.
	# echo "$Value" | grep -E "\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
	# PENDIENTE: Puede que sea más sencillo con la técnica de Is_IntegerNr()
	
	if [ "$Address" != "" ] && [ "$(printf '%s' "$Address" | grep -e ' ')" = "" ] ; then
#		Value="$(printf '%s' "$Address" | tr -s '.' ' ')"
#		NumeroActual="$(UnaPalabra () { printf '%s' $1; }; UnaPalabra $Value)"
#		NumeroActual="$(printf '%s' "$NumeroActual" | grep -E "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")"
#		if [ "$NumeroActual" != "" ] ; then
#			NumeroActual="$(UnaPalabra () { printf '%s' $2; }; UnaPalabra $Value)"
#			NumeroActual="$(printf '%s' "$NumeroActual" | grep -E "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")"
#			if [ "$NumeroActual" != "" ] ; then
#				NumeroActual="$(UnaPalabra () { printf '%s' $3; }; UnaPalabra $Value)"
#				NumeroActual="$(printf '%s' "$NumeroActual" | grep -E "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")"
#				if [ "$NumeroActual" != "" ] ; then
#					NumeroActual="$(UnaPalabra () { printf '%s' $4; }; UnaPalabra $Value)"
#					NumeroActual="$(printf '%s' "$NumeroActual" | grep -E "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")"
#				fi
#			fi
#		fi
		Address_b1="$(printf '%s\n' "$Address" | cut -f 1 -d '.')"
		Address_b2="$(printf '%s\n' "$Address" | cut -sf 2 -d '.')"
		Address_b3="$(printf '%s\n' "$Address" | cut -sf 3 -d '.')"
		Address_b4="$(printf '%s\n' "$Address" | cut -sf 4 -d '.')"
		if Is_IntegerNr $Address_b1 && Is_IntegerNr $Address_b2 && Is_IntegerNr $Address_b3 && Is_IntegerNr $Address_b4 ; then
			if [ $Address_b1 -ge 0 ] && [ $Address_b2 -ge 0 ] && [ $Address_b3 -ge 0 ] && [ $Address_b4 -ge 0 ] ; then
				if [ $Address_b1 -le 255 ] && [ $Address_b2 -le 255 ] && [ $Address_b3 -le 255 ] && [ $Address_b4 -le 255 ] ; then
					Value=1
				fi
			fi
		fi
	fi
	printf '%s\n' "$Value"
}

EsCIDR ()
# Sintaxis como función: $(EsCIDR "$DireccionRed")
# Descripción:
#	Devuelve (stdout) "1" en caso de que el dato proporcionado valga como especificación
#	de red CIDR, es decir, compuesta por una IP, una barra "/" y un número del 0 al 32
#	Ejemplo de validez: "192.168.1.0/24"
# Depends on functions: EsIP
# Depends on software packages: grep
{
	local DireccionRed="$1"
	local Mascara=''
	local Numeros="0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32"
	local Valor=''
	
	if [ "$(printf '%s' "$DireccionRed" | grep -e "/")" != "" ] ; then
		if [ "$(EsIP "$(printf '%s' "$DireccionRed" | cut -f 1 -d '/')")" = "1" ] ; then
			Mascara="$(printf '%s' "$DireccionRed" | cut -f 2 -d '/')"
			if [ "$(printf '%s' ",${Numeros}," | grep -e ",${Mascara},")" != "" ] ; then
				Valor="1"
			fi
		fi
	fi
	if [ "$Valor" = "" ] ; then
		printf '%s\n' "0"
	else
		printf '%s\n' "1"
	fi
}

IPv4ToDecimal ()
# Syntax as a function: $(IPv4ToDecimal "$IP")
# Description: Returns (stdout) the decimal numeric representation of IPv4 address (unsigned number)
# Depends on functions: EsIP
# Depends on software packages: bc
# Suggests software packages: awk/gawk
{
	local IP="$1"
	local Nr1=''
	local Nr2=''
	local Nr3=''
	local Nr4=''
	local Value=''

	if [ "$(EsIP "$IP")" = "1" ] ; then
		Nr1="$(printf '%s' "$IP" | cut -f 1 -d '.')"
		Nr2="$(printf '%s' "$IP" | cut -f 2 -d '.')"
		Nr3="$(printf '%s' "$IP" | cut -f 3 -d '.')"
		Nr4="$(printf '%s' "$IP" | cut -f 4 -d '.')"
		# Sometimes awk does not get stdin as expected, and can cache & repeat data
#		Value="$(printf '%s' "$IP" | awk -F '.' '{printf "%d\n", ($1 * 2^24) + ($2 * 2^16) + ($3 * 2^8) + $4}')"
		Value="$(printf '%s\n' "($Nr1 * 2^24) + ($Nr2 * 2^16) + ($Nr3 * 2^8) + $Nr4" | bc)"
		printf '%s\n' "$Value"
	fi
}

IPv4ToBinary ()
# Syntax as a function: $(IPv4ToBinary "$IP")
# Description: Returns (stdout) the binary representation of IPv4 address (unsigned numbers)
# Depends on functions: IPv4ToDecimal
# Depends on software packages: bc
{
	local IP="$1"
	local Value=''

	Value="$(IPv4ToDecimal "$IP")"
	if [ "$Value" != "" ] ; then
		Value="$(echo "obase=2;$Value" | bc)"
		if [ "$Value" != "" ] ; then
			Value="$(printf '%32s' "$Value" | tr ' ' '0')"
			printf '%s\n' "$Value"
		fi
	fi
}

BinaryToIPv4 ()
# Syntax as a function: $(BinaryToIPv4 "$BinaryDigits")
# Description: Returns (stdout) the IPv4 address of a 32-digit binary representation
# Note: Specified string MUST be 32 characters long.
# Depends on functions: (none)
# Depends on software packages: bc
{
	local BinaryDigits="$1"
	local Nr1=''
	local Nr2=''
	local Nr3=''
	local Nr4=''
	local Value=''

	if [ ${#BinaryDigits} -eq 32 ] && [ "$(printf '%s' "$BinaryDigits" | tr -s '1' '0')" = "0" ] ; then
		Nr1="$(printf '%s' "$BinaryDigits" | cut -c 1-8)"
		Nr1="$(echo "obase=10;ibase=2;${Nr1}" | bc)"
		Value="$Nr1"
		Nr2="$(printf '%s' "$BinaryDigits" | cut -c 9-16)"
		Nr2="$(echo "obase=10;ibase=2;${Nr2}" | bc)"
		Value="${Value}.${Nr2}"
		Nr3="$(printf '%s' "$BinaryDigits" | cut -c 17-24)"
		Nr3="$(echo "obase=10;ibase=2;${Nr3}" | bc)"
		Value="${Value}.${Nr3}"
		Nr4="$(printf '%s' "$BinaryDigits" | cut -c 25-32)"
		Nr4="$(echo "obase=10;ibase=2;${Nr4}" | bc)"
		Value="${Value}.${Nr4}"
		printf '%s\n' "$Value"
	fi
}

ListaDispositivosRed ()
# Sintaxis como funcion: $(ListaDispositivosRed)
# Descripcion: Devuelve (stdout) una lista de todos los dispositivos de red disponibles en el sistema, separados por espacios.
# Depends on functions:	WhereProgram
# Depends on software packages: grep, sed, iproute|net-tools
{
	local Value=''
	local IpRoute=''
	
	IpRoute="$(WhereProgram ip)"
	if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
	if [ "$IpRoute" != "" ] ; then
		export LANG=en
#		Value="$($IpRoute link 2>/dev/null | grep -ve '^ ' | grep -e '..*: ..*: ' | cut -f 2 -d ':')"
		Value="$($IpRoute link show 2>/dev/null | grep -ve '^ ' | cut -f 2 -d ':' | sed -e 's|.* ||g' | cut -f 1 -d '@')"
		if [ "$Value" = "" ] ; then
			Value="$(env LANG=en netstat --interfaces --all 2>/dev/null | grep -ive "^Iface" | grep -ive "^Kernel" | cut -f 1 -d ' ')"
		fi
		Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	fi
}

IPv4NetworkAddress ()
# Syntax as a function: $(IPv4NetworkAddress "$CIDR")
# Description: Returns (stdout) the base address of CIDR specification
# Example: 192.168.1.150/24 -> 192.168.1.0
# Depends on functions: Is_IntegerNr IPv4ToBinary BinaryToIPv4
# Depends on software packages: bc
{
	local CIDR="$1"
	local Address=''
	local Binary=''
	local Mask=''

	Address="$(printf '%s' "$CIDR" | cut -f 1 -d '/')"
	Mask="$(printf '%s' "$CIDR" | cut -f 2 -d '/')"
	if [ "$Address" != "$Mask" ] && [ "$Mask" != "" ] && Is_IntegerNr "$Mask" && [ $Mask -ge 0 ] && [ $Mask -le 32 ] ; then
		Binary="$(IPv4ToBinary "$Address")"
		if [ "$Binary" != "" ] ; then
			if [ $Mask -gt 0 ] ; then
				Binary="$(printf '%s' "$Binary" | cut -c 1-${Mask})"
			else
				Binary=''
			fi
			Binary="$(printf '%-32s' "$Binary" | tr ' ' '0')"
			BinaryToIPv4 "$Binary"
		fi
	fi
}

DispositivoDeIP ()
# Sintaxis como funcion: $(DispositivoDeIP $DireccionIP)
# Descripcion: Devuelve (stdout) el nombre de dispositivo que tiene asignada la dirección IP especificada
# Depends on functions: WhereProgram
# Depends on software packages: sed, iproute
{
	local DireccionIP="$1"
	local IpRoute=''
	local IpRegexp=''
	local Value=''
	
	IpRoute="$(WhereProgram ip)"
	if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
	if [ "$IpRoute" != "" ] ; then
		export LANG=en
		IpRegexp="$(printf '%s\n' "$DireccionIP" | sed -e 's|\.|\\.|g')"
		Value="$($IpRoute address show | tr -s '\t' ' ' | grep -e " ${IpRegexp}/" | sed -e 's| |\n|g' | sed -e 's|@if..*||g' | tail -n 1)"
		if [ "$Value" = "" ] ; then
			Value="$($IpRoute address show | tr -s '\t' ' ' | grep -e " $IpRegexp " | sed -e 's| |\n|g' | sed -e 's|@if..*||g' | tail -n 1)"
		fi
		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	fi
}

IPCDIRFromDevice ()
# Sintaxis como funcion: $(IPCDIRFromDevice $NIC)
# Descripcion: Devuelve (stdout) todas las IPv4 asignadas al dispositivo especificado, una dirección por línea, en formato CDIR (192.168.1.33/24)
# Depends on functions: WhereProgram
# Depends on software packages: grep, sed, iproute
{
	local NIC="$1"
	local IpRoute=''
	local SelectedLines=''
	local CurLine=''
	local CurIP=''
	local CurMask=''
	
	if [ "$NIC" != "" ] ; then
		IpRoute="$(WhereProgram ip)"
		if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
		if [ "$IpRoute" != "" ] ; then
			export LANG=en
#			Value="$($IpRoute address show $NIC | grep -e 'inet ..*\...*\...*\...*/..* .')"
#			Value="$(printf '%s\n' "$Value" | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 2 -d ' ')"
#			if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
#			$IpRoute address show dev "$NIC" | sed -e 's|.* inet |inet |g' | grep -e '^inet ' | cut -f 2 -d ' '
			SelectedLines="$($IpRoute address show dev "$NIC" | sed -e 's|.* inet |inet |g' | grep -e '^inet ')"
			IFS="$(printf '\n\b')" ; for CurLine in $SelectedLines ; do unset IFS
				CurIP="$(printf '%s' "$CurLine" | tr ' ' '\n' | grep -e '..*\...*\...*\..' | cut -f 1 -d '/' | head -n 1)"
				# Debian 7 in Netwerkvereniging Coloclue returns IP with no mask but peer with mask at same line.
				CurMask="$(printf '%s' "$CurLine" | tr ' ' '\n' | grep -e '..*\...*\...*\...*/.' | head -n 1 | cut -f 2 -d '/')"
				if [ "$CurIP" != "" ] ; then
					if [ "$CurMask" != "" ] ; then
						printf '%s\n' "${CurIP}/${CurMask}"
					else
						printf '%s\n' "$CurIP"
					fi
				fi
			done
		fi
	fi
}

LlistarIPpropies ()
# Sintaxi: LlistarIPpropies $LimitNr "$NIC" "$ExcludedAddresses"
# Descripcio: Returns (stdout) active IP addresses on system (one per line)
# Parametres esperats:
#	$1	Numero màxim d'adreces a llistar. Especifiqueu 0 per a totes.
#	$2	Nom de dispositiu de xarxa a analitzar. "eth" es valid per tots els "eth.."; "." o "*" serveix per a tots
#	$3	(opcional) Patro d'adreces a excloure de la llista. Per exemple "^127\."
# Notes:
#	- Only detects 1 IP per device. To get everyone use AllIPv4()
#	- Tracta adreces IPv4
# Depends on functions: WhereProgram Is_IntegerNr EsIP
# Depends on software packages: grep (>= 2.0), sed, ip/iproute2 (>= 1.0.0) | ip/iproute (>= 20020101) | netbase (>= 3.0) | busybox-symlinks-net-tools (>= 1)
{
	# Variables locales
	local LimitNr=$1
	local NIC="$2"
	local ExcludedAddresses="$3"
	local CurIPs=''
	local CurIP=''
	local FoundNr=0
	local IfConfig=''
	local IpRoute=''
	local CurDevice=''
	local NICs=''
	local LastStatus=0
	local StatusCode=0
	
	if ! Is_IntegerNr "$LimitNr" ; then LimitNr=0 ; fi
	IpRoute="$(WhereProgram ip)"
	if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
	if [ "$IpRoute" != "" ] ; then
		export LANG=en
		if [ "$NIC" = "." ] || [ "$NIC" = "*" ] ; then NIC="" ; fi
		NICs="$($IpRoute address show | grep -ve '^ ' | grep -e ': .*: <' | cut -f 2 -d ':' | sed -e 's|@if..*||g')"
		IFS="$(printf '\n\b')" ; for CurDevice in $NICs ; do unset IFS
			CurDevice="$(echo TrimAndSingle $CurDevice | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
			if [ "$(printf '%s\n' "$CurDevice" | grep -e "^${NIC}")" != "" ] ; then
				CurIPs="$($IpRoute address show dev "$CurDevice")"
				if [ "$(printf '%s' "$CurIPs" | grep -e 'inet ..*\...*\...*\...*/..* brd ')" != "" ] ; then
					CurIPs="$(printf '%s' "$CurIPs" | grep -e 'inet ..*\...*\...*\...*/..* brd ')"
				else
					CurIPs="$(printf '%s' "$CurIPs" | grep -e 'inet ..*\...*\...*\...*/..* ')"
				fi
				CurIPs="$(printf '%s' "$CurIPs" | tr -s '\t' ' ' | tr ' ' '\n' | grep -e '..*\...*\...*\...*/.' | cut -f 1 -d '/')"
				CurIPs="$(printf '%s' "$CurIPs" | head -n 1)"  # LIMIT TO 1 ADDRESS PER NIC, to be consistent with ifconfig alternative
				unset IFS ; for CurIP in $CurIPs ; do
#					if [ "$(EsIP "$CurIP")" != "1" ] ; then
#						CurIP="$($IpRoute address show dev "$CurDevice" | grep -e 'inet ..*\...*\...*\...*/..* ' | head -n 1 | tr -s '\t' ' ')"
#						CurIP="$(echo TrimAndSingle $CurIP | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2 -d ' ' | cut -f 1 -d '/')"
#					fi
					if [ "$(EsIP "$CurIP")" = "1" ] ; then
						if [ "$ExcludedAddresses" = "" ] || [ "$(printf '%s' "$CurIP" | grep -ve "$ExcludedAddresses")" != "" ] ; then
							FoundNr=$((FoundNr + 1))
							if [ $FoundNr -le $LimitNr ] || [ $LimitNr -eq 0 ] ; then
								printf '%s\n' "$CurIP"
							fi
						fi
					fi
				done
			fi
		done
	else
		IfConfig="$(WhereProgram ifconfig)"
		if [ "$IfConfig" = "" ] && [ -x /sbin/ifconfig ] ; then IfConfig="/sbin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /bin/ifconfig ] ; then IfConfig="/bin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /usr/sbin/ifconfig ] ; then IfConfig="/usr/sbin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /usr/bin/ifconfig ] ; then IfConfig="/usr/bin/ifconfig" ; fi
		if [ "$IfConfig" != "" ] ; then
			if [ "$LimitNr" = "" ] ; then LimitNr=0 ; fi
			if [ "$NIC" = "." ] || [ "$NIC" = "*" ] ; then NIC="" ; fi  # El comodi no serveix a ifconfig
			CurIPs="$(env LANG=en $IfConfig $NIC | grep -e 'inet ' -e ' ip ' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 2 -d ' ')"
			IFS="$(printf '\n\b')" ; for CurIP in $CurIPs ; do unset IFS
#				CurIP="$(SomeWord () { printf '%s' $2; }; SomeWord $CurIP)"
				if [ "$(EsIP "$CurIP")" != "1" ] ; then
					CurIP="$(printf '%s' "$CurIP" | tr -s ':' ' ')"
					CurIP="$(expr "$CurIP" : "[ ]*\(.*[^ ]\)[ ]*$")"  # Trim
					CurIP="$(SomeWord () { printf '%s' $2; }; SomeWord $CurIP)"
				fi
				if [ "$ExcludedAddresses" = "" ] || [ "$(printf '%s' "$CurIP" | grep -ve "$ExcludedAddresses")" != "" ] ; then
					FoundNr=$((FoundNr + 1))
					if [ $FoundNr -le $LimitNr ] || [ $LimitNr -eq 0 ] ; then
						printf '%s\n' "$CurIP"
					fi
				fi
			done
		else
			printf '%s\n' "${sERROR}E: Neither ipconfig/net-tools nor ip/iproute have been found.${fRESET}" 1>&2
			LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

DefaultNICs ()
# Sintaxis como funcion: $(DefaultNICs)
# Descripcion: Devuelve (stdout) los nombres de dispositivo que se usan para salir a internet/WAN, uno por línea
# Notas:
#	- Sólo se detecta un dispositivo si está habilitado (up).
#	- Function previously named DispositivoRedPredeterminado when only returned 1 NIC
#	- Alternativa con iproute2:
#	  ip r l | grep -ie '^default ' | head -n 1 | sed -e 's|.* dev ||g' | cut -f 1 -d ' '
# To do:
#	- Use alternative from iproute2: ss
# Depends on functions: WhereProgram Is_Executable
# Depends on software packages: grep, sed, net-tools|iproute
{
	local IPPrincipal=''
	local ListaDispositivos=''
	local IpRoute=''
	local List=''
	local OneValue=''
	
	if Is_Executable netstat ; then
		List="$(netstat -rn 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | grep -e '^0\.0\.0\.0 ')"
		IFS="$(printf '\n\b')" ; for CurLine in $List ; do unset IFS
			OneValue="$(printf '%s\n' "$CurLine" | tr -s ' ' '\n' | tail -n 1)"
			if [ "$OneValue" != "" ] && [ "$(cat /proc/net/dev | tr -s ' ' | sed -e 's|^ ||g' | grep -e "^${OneValue}:")" != "" ] ; then printf '%s\n' "$OneValue" ; fi
		done
	fi
	if [ "$OneValue" = "" ] ; then
		IpRoute="$(WhereProgram ip 2)"
		if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
		if [ "$IpRoute" != "" ] ; then
			export LANG=en
			List="$($IpRoute route | tr -s '\t' ' ' | tr -s ' ' | grep -ie '^default via .* dev ')"
			IFS="$(printf '\n\b')" ; for CurLine in $List ; do unset IFS
				OneValue="$(printf '%s\n' "$CurLine" | tr -s ' ' '\n' | tail -n 1)"
				if [ "$OneValue" != "" ] && [ "$(ip link show "$OneValue" 2>/dev/null)" != "" ] ; then printf '%s\n' "$OneValue" ; fi
			done
			if [ "$OneValue" = "" ] ; then
				IPPrincipal="$($IpRoute route | tr -s '\t' ' ' | tr -s ' ' | grep -e '^default ' | grep -e ' src ' | head -n 1 | sed -re 's| |\n|g' | grep -ve '^$' | tail -n 1)"
				if [ "$IPPrincipal" != "" ] ; then
					Value="$($IpRoute address show | tr -s '\t' ' ' | tr -s ' ' | grep -e " ${IPPrincipal}/" | head -n 1 | sed -e 's| |\n|g' | grep -ve '^$' | sed -e 's|@if..*||g' | tail -n 1)"
				else
					Value="$($IpRoute route | tr -s '\t' ' ' | tr -s ' ' | grep -e '^default ' | grep -e ' dev ' | head -n 1 | sed -e 's| |\n|g' | grep -ve '^$' | head -n 5 | tail -n 1)"
				fi
				Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				if [ "$Value" != "" ] && [ "$(ip link show "$OneValue" 2>/dev/null)" != "" ] ; then printf '%s\n' "$Value" ; fi
			fi
		fi
	fi
}

NicIpAcquirement ()
# Syntax as a function: $(NicIpAcquirement $Nic)
# Description: Returns configuration method applied to a network interface.
# Possible values returned are: dhcp static none unknown
# Expected parameters:
#	$1	Existing NIC name
# Depends on functions: WhereProgram PsOutputValues
# Depends on software packages: grep, sed, iproute
{
	local Nic="$1"
	local DhcpNics=''
	local DhcpControllers=''
	local CurrentController=''
	local CurrentNic=''
	local Ip=''
	local IpRoute=''
	local Value='unknown'
	
	# h = newer --no-headers
	DhcpControllers="$(PsOutputValues cmd | grep -e '/dhclient .' -e ' dhclient .' | grep -ve 'grep -')"
	IFS="$(printf '\n\b')" ; for CurrentController in $DhcpControllers ; do unset IFS
		CurrentNic="$(echo TrimAndSingle $CurrentController | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | tr -s ' ' '\n' | tail -n 1)"
		if [ "$CurrentNic" = "$Nic" ] ; then
			Value="dhcp"
		fi
	done
	if [ "$Value" = "unknown" ] ; then
		IpRoute="$(WhereProgram ip)"
		if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
		if [ "$IpRoute" != "" ] ; then
			export LANG=en
			Ip="$($IpRoute address show dev "$Nic" scope global | tr -s '\t' ' ' | grep -e 'inet ' | sed -e 's|.* inet ||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ' | cut -f 1 -d '/')"
#			Ip="$(printf '%s\n' "$(SomeWord () { printf '%s' $1; }; SomeWord $Ip)" | cut -f 1 -d '/')"
			if [ "$Ip" != "" ] ; then
				if [ "$($IpRoute address show dev "$Nic" scope global | grep -e ' dynamic ')" != "" ] ; then
					Value="dhcp"
				else
					Value="static"
				fi
			else
				Value="none"
			fi
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

PrimeraIPEquip ()
# Syntax as a function: $(PrimeraIPEquip)
# Dóna l'adreça IP de la sortida predeterminada a internet/WAN; si n'hi ha vàries en dóna la primera.
# Alternativa con iproute2 para obtener la NIC predeterminada:
#	NIC="$(ip r l | grep -ie '^default ' | head -n 1 | sed -e 's|.* dev ||g' | cut -f 1 -d ' ')"
# Alternativa con iproute2 para obtener la IP predeterminada de esa NIC:
#	ip r l | grep -e "..*\..*\...*\..* dev $NIC .* link src " | sed -e 's|.* src ||g' | cut -f 1 -d ' '
# Depends on functions: WhereProgram DefaultNICs LlistarIPpropies
# Depends on software packages: grep, sed, iproute
{
	local Devs=''
	local IpRoute=''
	local CurDev=''
	local Value=''
	
	IpRoute="$(WhereProgram ip)"
	if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
	if [ "$IpRoute" != "" ] ; then
		export LANG=en
		Value="$($IpRoute route 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | grep -ie '^default .* src ' | sed -e 's| |\n|g' | grep -ve '^$' | tail -n 1)"
	fi
	if [ "$Value" = "" ] ; then
		Devs="$(DefaultNICs)"
		IFS="$(printf '\n\b')" ; for CurDev in $Devs ; do unset IFS
			if [ "$Value" = "" ] ; then
				Value="$(LlistarIPpropies 1 "$CurDev" "^127\.")"
			fi
		done
		if [ "$Value" = "" ] ; then
			# Entonces sin excluir loopback
			IFS="$(printf '\n\b')" ; for CurDev in $Devs ; do unset IFS
				if [ "$Value" = "" ] ; then
					Value="$(LlistarIPpropies 1 "$CurDev" "^127\.")"
				fi
			done
		fi
	fi
	if [ "$Value" = "" ] ; then
		Value="$(LlistarIPpropies 1 . "^127\.")"
	fi
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value"
	fi
}

PasarelasRed ()
# Sintaxis como funcion: $(PasarelasRed)
# Descripcion: Devuelve (stdout) una lista de puertas de enlace configuradas en el sistema, separadas por espacios
# Alternativa con iproute2 para obtener la predeterminada:
#	ip r l | grep -ie '^default ' | tr -s '\t' ' ' | tr -s ' ' '\n' | tail -n 1
# To do:
#	- Use alternative from iproute2: ss
# Depends on functions: WhereProgram Is_Executable
# Depends on software packages: grep, sed, net-tools|iproute
{
	local ListaOriginal=''
	local PasarelaActual=''
	local List=''
	local CurLine=''
	local IpRoute=''
	local Value=''
	
#	netstat -rn 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | grep -e '^0\.0\.0\.0 ' | cut -f 2 -d ' '

	if Is_Executable netstat ; then
		List="$(netstat -rn 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | grep -e '^0\.0\.0\.0 ')"
		IFS="$(printf '\n\b')" ; for CurLine in $List ; do unset IFS
			Value="$Value $(printf '%s\n' "$CurLine" | cut -f 2 -d ' ')"
		done
	fi
	if [ "$Value" = "" ] ; then
		IpRoute="$(WhereProgram ip)"
		if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
		if [ "$IpRoute" != "" ] ; then
			export LANG=en
			List="$($IpRoute route | tr -s '\t' ' ' | tr -s ' ' | grep -ie '^default via .* dev ')"
			IFS="$(printf '\n\b')" ; for CurLine in $List ; do unset IFS
				Value="$Value $(printf '%s\n' "$CurLine" | cut -f 3 -d ' ')"
			done
		fi
	fi
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

NetdevMac ()
# Syntax as a function: $(NetdevMac $Netdev)
# Description: Returns (stdout) MAC address of specified (local) network device
# Depends on functions: WhereProgram
# Depends on software packages: grep, sed, net-tools|iproute
{
	local Netdev="$1"
	local IfConfig=''
	local IpRoute=''
	local Value=''
	
	if [ "$Netdev" != "" ] ; then
		IfConfig="$(WhereProgram ifconfig)"
		if [ "$IfConfig" = "" ] && [ -x /sbin/ifconfig ] ; then IfConfig="/sbin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /bin/ifconfig ] ; then IfConfig="/bin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /usr/sbin/ifconfig ] ; then IfConfig="/usr/sbin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /usr/bin/ifconfig ] ; then IfConfig="/usr/bin/ifconfig" ; fi
		if [ "$IfConfig" != "" ] ; then
			Value="$(env LANG=en $IfConfig $Netdev 2>/dev/null | grep -e '..:..:..:..:..:..' | head -n 1 | sed -e 's|.*\(..:..:..:..:..:..\).*|\1|g')"
		fi
		if [ "$Value" = "" ] ; then
			IpRoute="$(WhereProgram ip)"
			if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
			if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
			if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
			if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
			if [ "$IpRoute" != "" ] ; then
				export LANG=en
#				Value="$($IpRoute address show dev $Netdev 2>/dev/null | grep -e '..:..:..:..:..:...*..:..:..:..:..:..' | head -n 1 | sed -e 's|.*\(..:..:..:..:..:..\).*\(..:..:..:..:..:..\).*|\1|g')"
#				if [ "$Value" = "" ] ; then
#					Value="$($IpRoute address show dev $Netdev 2>/dev/null | grep -e '..:..:..:..:..:..' | head -n 1 | sed -e 's|.*\(..:..:..:..:..:..\).*|\1|g')"
#				fi
				Value="$($IpRoute link show dev "$Netdev" | sed -e 's|.* link/|link/|g' | grep -e '^link/' | sed -e 's| brd .*||g' | tr ' ' '\n' | grep -e '^..:..:..:..:..:..$')"
			fi
		fi
		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	fi
}

MacEquipIP ()
# Syntax as a function: $(MacEquipIP "$AdresaIP")
# Dóna l'adreça física MAC que es correspon a la IP especificada.
# Nota: només funciona amb adreces IP actives a l'equip local
# Depends on functions: WhereProgram
# Depends on software packages: grep, sed, net-tools
{
	local AdresaIP="$1"
	local Value=''
	local IfConfig=''
	local IpRoute=''
	local NIC=''
	
	if [ "$AdresaIP" != "" ] ; then
		IpRegexp="$(printf '%s\n' "$AdresaIP" | sed -e 's|\.|\\.|g')"
		IpRoute="$(WhereProgram ip)"
		if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
		if [ "$IpRoute" != "" ] ; then
			export LANG=en
			NIC="$($IpRoute address show | grep -e "inet ${IpRegexp} " -e "inet ${IpRegexp}/" | sed -e 's|.* ||g' | sed -e 's|@if..*||g')"
			if [ "$NIC" != "" ] ; then
				Value="$($IpRoute address show dev "$NIC" | grep -e "link/.* ..:..:..:..:..:.. " | head -n 1)"
				Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | tr -s ' ' '\n' | grep -e '^..:..:..:..:..:..$' | head -n 1)"
			fi
		else
			IfConfig="$(WhereProgram ifconfig)"
			if [ "$IfConfig" = "" ] && [ -x /sbin/ifconfig ] ; then IfConfig="/sbin/ifconfig" ; fi
			if [ "$IfConfig" = "" ] && [ -x /bin/ifconfig ] ; then IfConfig="/bin/ifconfig" ; fi
			if [ "$IfConfig" = "" ] && [ -x /usr/sbin/ifconfig ] ; then IfConfig="/usr/sbin/ifconfig" ; fi
			if [ "$IfConfig" = "" ] && [ -x /usr/bin/ifconfig ] ; then IfConfig="/usr/bin/ifconfig" ; fi
			if [ "$IfConfig" != "" ] ; then
				NICs="$($IfConfig | grep -ve '^ ' -ve '^$' | cut -f 1 -d ' ' | cut -f 1 -d ':')"
			fi
			unset IFS ; for CurNIC in $NICs ; do
				NicInfo="$(env LANG=en $IfConfig $CurNIC)"
				NicInfo="$(echo TrimAndSingle $NicInfo | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				if [ "$(printf '%s\n' "$NicInfo" | grep -ie "inet addr.${IpRegexp}" -ie "inet ${IpRegexp}")" != "" ] ; then
					Value="$(printf '%s\n' "$NicInfo" | perl -pe 's|.* HWaddr.||gi' | perl -pe 's|.* ether ||gi' | cut -f 1 -d ' ')"
				fi
			done
		fi

		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	fi
}

BitsMascara ()
# Syntax as a function: $(BitsMascara "$SubnetMask")
# Description: Converts old subnet mask into CIDR bits number.
# $(BitsMascara "255.255.255.0") => 24
# Depends on functions: Is_IntegerNr
# Depends on software packages: grep
{
	local SubnetMask="$1"
	local CurByte=''
	local Value=0
	
	if [ "$(printf '%s\n' "$SubnetMask" | grep -e '..*\...*\...*\...*')" != "" ] ; then
#		SubnetMask="$(printf '%s' "$SubnetMask" | tr -s '.' ' ')"
#		for CurByte in $(echo $SubnetMask) ; do
		IFS='.' ; for CurByte in $SubnetMask ; do unset IFS
			if Is_IntegerNr "$CurByte" && [ "$Value" != "" ] ; then 
				if [ $CurByte -ge 128 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 192 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 224 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 240 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 248 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 252 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 254 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 255 ] ; then Value=$((Value + 1)) ; fi
			else
				Value=''
			fi
		done
		if [ "$Value" != "" ] ; then
			printf '%s\n' "$Value"
		else
			printf '%s\n' "${sERROR}E: Bad IPv4 numbers for BitsMascara()${fRESET}" 1>&2
		fi
	fi
}

MaskBits ()
# Syntax as a function: $(MaskBits $NrBits $TotalBytes)
# Example: $(MaskBits 24 4) => "255.255.255.0"
# Depends on functions:	(none)
# Depends on software packages: sed
{
	NrBits=$1
	TotalBytes=$2
	RemainTotalBits=$NrBits
	RemainTotalBytes=$TotalBytes
	ByteBits=0
	SpaceInByte=8
	CurrentHostsRest=256
	CurrentByte=0
	Value=''
	while [ $RemainTotalBits -gt 0 ] ; do
		if [ $SpaceInByte -gt 0 ] ; then
			CurrentHostsRest=$((CurrentHostsRest / 2))
			CurrentByte=$((CurrentByte + CurrentHostsRest))
			SpaceInByte=$((SpaceInByte - 1))
		else
			Value="${Value}.${CurrentByte}"
			CurrentHostsRest=$((256 / 2))
			CurrentByte=$CurrentHostsRest
			RemainTotalBytes=$((RemainTotalBytes - 1))
			SpaceInByte=$((8 - 1))
		fi
		RemainTotalBits=$((RemainTotalBits - 1))
	done
	if [ $CurrentByte -ne 0 ] ; then
		Value="${Value}.${CurrentByte}"
		RemainTotalBytes=$((RemainTotalBytes - 1))
	fi
	while [ $RemainTotalBytes -gt 0 ] ; do
		Value="${Value}.0"
		RemainTotalBytes=$((RemainTotalBytes - 1))
	done
	printf '%s\n' "$Value" | sed -e 's|^\.||g'
}

MascaraEquipIP ()
# Syntax as a function: $(MascaraEquipIP "$AdresaIP")
# Description: Dóna la màscara (com 255.255.255.0) que es correspon a la IP especificada de l'equip
# Nota: només funciona amb adreces IP actives a l'equip local
# Depends on functions: WhereProgram MaskBits
# Depends on software packages: grep, sed, iproute|net-tools
{
	local AdresaIP="$1"
	local Dades=''
	local DadaActual=''
	local IpRoute=''
	local IfConfig=''
	local IpRegexp=''
	local Value=''
	
	if [ "$AdresaIP" != "" ] ; then
		IpRegexp="$(printf '%s\n' "$AdresaIP" | sed -e 's|\.|\\.|g')"
		IpRoute="$(WhereProgram ip)"
		if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
		if [ "$IpRoute" != "" ] ; then
			export LANG=en
			Value="$("$IpRoute" addr 2>/dev/null | grep -e " ${IpRegexp}/" | sed -e "s|.* ${IpRegexp}/||g" | cut -f 2 -d '/' | cut -f 1 -d ' ')"
		fi
		if [ "$Value" != "" ] ; then
			Value="$(MaskBits "$Value" 4)"
		else
			IfConfig="$(WhereProgram ifconfig)"
			if [ "$IfConfig" = "" ] && [ -x /sbin/ifconfig ] ; then IfConfig="/sbin/ifconfig" ; fi
			if [ "$IfConfig" = "" ] && [ -x /bin/ifconfig ] ; then IfConfig="/bin/ifconfig" ; fi
			if [ "$IfConfig" = "" ] && [ -x /usr/sbin/ifconfig ] ; then IfConfig="/usr/sbin/ifconfig" ; fi
			if [ "$IfConfig" = "" ] && [ -x /usr/bin/ifconfig ] ; then IfConfig="/usr/bin/ifconfig" ; fi
			if [ "$IfConfig" != "" ] ; then
				Value="$(ifconfig -a 2>/dev/null | tr -s ':' ' ' | grep -e " ${IpRegexp} " | sed -e 's|.*mask||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
			fi
		fi
		if [ "$Value" = "255.255.255.255" ] ; then Value='' ; fi # Some venet in VPS return 32bits mask.
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

IPDeMAC ()
# Sintaxis como funcion: $(IPDeMAC "$MACAddress" $MinScanMaskBits $OnlyReachable)
# Descripcion: Devuelve las direcciones IP asignadas a una direccion MAC conectada a la red, una por línea.
# Nota: La información se obtiene de la cache de comunicaciones con esa MAC.
# IMPORTANT NOTES:
#	- Resolves IP addresses in same LAN and mostly if they are Ping-alive.
#	- Si se ejecuta con permisos de superusuario (root) aumenta la velocidad (no hay que explorar) y fiabilidad (se hace verificación inversa con nmap).
#	- Si la interfaz MAC de un equipo tiene varias IP (por ejemplo de servidores
#	  virtuales) se pueden devolver las diferentes IP conocidas, por líneas.
#	- The lookup for an already cached address can spend less that 1 second.
#	- The lookup for an unknown address in a 24bit network can spend upto 30 seconds.
# Parametros esperados:
#	$1	Direccion MAC del dispositivo buscado
#	$2	Minimum bits to mask the IP range when scanning network; set less bits for slower scan but wider nets. Default if empty: 24.
#	$3	(optional) "1" if only must return reachable/alive IPs. Otherwise, cached data can be trusted.
# Depends on functions: Is_Executable Is_IntegerNr LlistarIPpropies MascaraEquipIP BitsMascara IPv4NetworkAddress
# Depends on software packages: grep, sed, net-tools, iputils-ping, nmap|fping
{
	local MACAddress="$1"
	local MinScanMaskBits="$2"
	local OnlyReachable="$3"
	local OwnIPs=''
	local CurrentMask=''
	local CurrentBits=0
	local FirstCachedIPs=''
	local SecondCachedIPs=''
	local CurrentIP=''
	local CurrentMAC=''
	local StatusCode_Ping=0
	local DiscoveredIPs=''
	local IpRegexp=''
	local NmapNoMac=0
	local Value=''
	
	if ! Is_IntegerNr "$MinScanMaskBits" ; then MinScanMaskBits=24 ; fi
	MACAddress="$(printf '%s' "$MACAddress" | tr -s '-' ':')"
	# TRY 1: USE CACHED DATA (SELECTED)
	FirstCachedIPs="$(env LANG=en arp -n | tr -s '\t' ' ' | grep -ie " $MACAddress " | cut -f 1 -d ' ')"
	# Warning: For a host with more than 1 NIC&MAC, ARP can cache (incorrectly) a same MAC for all target IPs.
	if [ $(id -u) -eq 0 ] && Is_Executable nmap ; then
		# Validate only if permissions allow verify
		unset IFS ; for CurrentIP in $FirstCachedIPs ; do
			if [ "$NmapNoMac" != "1" ] ; then
				CurrentMAC="$(env LANG=en nmap -sP -n "$CurrentIP")"
				if [ "$(printf '%s\n' "$CurrentMAC" | grep -ie '^MAC Address:')" != "" ] ; then
					CurrentMAC="$(echo TrimAndSingle $(printf '%s\n' "$CurrentMAC" | grep -ie '^MAC Address:' | cut -f 2- -d ':') | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 1 -d ' ')"
					if [ "$(printf '%s' "$CurrentMAC" | grep -ie "^${MACAddress}$")" != "" ] ; then
						Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentIP")"
					fi
				else
					if [ "$(printf '%s\n' "$CurrentMAC" | grep -ie '^Host .* up\.' -ie '^Host .* up ')" != "" ] ; then
						# Peer alive. This version of nmap doesn't report MAC Address.
						NmapNoMac=1
					fi
				fi
			fi
		done
	fi
	# TRY 2: EXPLORE NETWORK TO REFRESH ARP CACHE
	if [ "$Value" = "" ] ; then
		# Refresh ARP cache (broadcast packets) can take some time.
		OwnIPs="$(LlistarIPpropies 0 . "^127\.")"
		SecondCachedIPs=''
		unset IFS ; for CurrentIP in $OwnIPs ; do
			CurrentMask="$(MascaraEquipIP "$CurrentIP")"	# Get 255.255.0.0
			CurrentBits="$(BitsMascara "$CurrentMask")"	# Get 16
			if ! Is_IntegerNr "$CurrentBits" || [ $CurrentBits -lt $MinScanMaskBits ] ; then CurrentBits=$MinScanMaskBits ; fi	# Get 24
#			CurrentIP="$(IPBaseXarxa "$CurrentIP" $CurrentBits)"
			CurrentIP="$(IPv4NetworkAddress "${CurrentIP}/${CurrentBits}")"
			if [ "$CurrentIP" != "" ] ; then
				if [ "$(fping --help 2>&1 | grep -e '-g .*list')" != "" ] ; then
					DiscoveredIPs="$(printf '%s\n' "$DiscoveredIPs" ; fping -a -r1 -t50 -q -g "${CurrentIP}/${CurrentBits}" 2>&1 | cut -f 1 -d ' ')"
					if [ "$DiscoveredIPs" != "" ] ; then SecondCachedIPs=1 ; fi	# Sometimes (such as in a VM) fping doesn't reach any host (neither own IP)
				fi
				if [ "$SecondCachedIPs" != "1" ] && Is_Executable nmap ; then
					DiscoveredIPs="$(printf '%s\n' "$DiscoveredIPs" ; env LANG=en nmap -sP "${CurrentIP}/${CurrentBits}" 2>&1 | grep -ie 'scan .* for ' | sed -e 's|.* for ||g' | cut -f 2 -d '(' | cut -f 1 -d ')')"
					SecondCachedIPs=1
				fi
			fi
		done
		if [ "$SecondCachedIPs" = "1" ] ; then
			SecondCachedIPs="$(env LANG=en arp -n | tr -s '\t' ' ' | grep -ie " $MACAddress " | cut -f 1 -d ' ')"
		fi
		# + USE NEW CACHE (SELECTED)
		if [ $(id -u) -eq 0 ] && Is_Executable nmap && [ "$NmapNoMac" != "1" ] ; then
			# Verify only if permissions allow
			unset IFS ; for CurrentIP in $SecondCachedIPs ; do
				if [ "$NmapNoMac" != "1" ] || [ "$OnlyReachable" = "1" ] ; then
					CurrentMAC="$(env LANG=en nmap -sP -n "$CurrentIP")"
					if [ "$(printf '%s\n' "$CurrentMAC" | grep -ie '^MAC Address:')" != "" ] ; then
						CurrentMAC="$(echo TrimAndSingle $(printf '%s\n' "$CurrentMAC" | grep -ie '^MAC Address:' | cut -f 2- -d ':') | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 1 -d ' ')"
						if [ "$(printf '%s' "$CurrentMAC" | grep -ie "^${MACAddress}$")" != "" ] ; then
							Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentIP")"
						fi
					else
						if [ "$(printf '%s\n' "$CurrentMAC" | grep -ie '^Host .* up\.' -ie '^Host .* up ')" != "" ] ; then
							# Peer alive. This version of nmap doesn't report MAC Address.
							NmapNoMac=1
							if [ "$OnlyReachable" = "1" ] ; then
								Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentIP")"
							fi
						fi
					fi
				fi
			done
		else
			# Verify only if IP is alive
			if Is_Executable fping ; then
				unset IFS ; for CurrentIP in $SecondCachedIPs ; do
					fping -a -r1 -t 50 -q "$CurrentIP" > /dev/null 2>&1
					StatusCode_Ping=$?
					if [ $StatusCode_Ping -eq 0 ] ; then
						Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentIP")"
					fi
				done
			fi
		fi
	fi
	# TRY 3: TRUST CACHED DATA FOR NON-ALIVE HOST
	if [ "$OnlyReachable" != "1" ] || [ "$NmapNoMac" = "1" ] ; then
		if [ "$Value" = "" ] ; then
			Value="$SecondCachedIPs"
		fi
	fi
	if [ "$OnlyReachable" != "1" ] ; then
		if [ "$Value" = "" ] ; then
			Value="$FirstCachedIPs"
		fi
	fi
	Value="$(printf '%s' "$Value" | grep -ve '^$' | awk '!seen[$0]++')"
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value"
	fi
}

Is_RemoteIcmpAlive ()
# Syntax as a function: Is_RemoteIcmpAlive "$RemoteHost" $TryTimeoutS $MaxTriesNr
# Description: Returns (exitcode 0) TRUE if specified host returns ICMP packets response (ping)
# Use example (without brackets []):
#	if Is_RemoteIcmpAlive 192.168.1.2 ; then echo "Host available" ; fi
# Expected parameters:
#	$1	target host IP or name
#	$2	(optional, defaults to 2) Max seconds to wait for a result, per each ping test
#	$3	(optional, defaults to 1) Max tries to do to get a success result
# Depends on functions: Is_Executable Is_IntegerNr TimedExecution
# Depends on software packages: fping|iputils-ping
{
	local RemoteHost="$1"
	local TryTimeoutS=$2
	local MaxTriesNr=$3
	local TriedNr=0
	local Value=254
	local TestText=''
	local TempFile=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	TempFile="${DirTemp}/Is_RemoteIcmpAlive.${$}.txt"
	if ! Is_IntegerNr "$TryTimeoutS" ; then TryTimeoutS=2 ; fi
	if ! Is_IntegerNr "$MaxTriesNr" ; then MaxTriesNr=1 ; fi
	if [ $Value -ne 0 ] && [ $TriedNr -lt $MaxTriesNr ] && Is_Executable fping ; then
#		TimedExecution $TryTimeoutS 1 '' fping -c 2 -r $((MaxTriesNr-1)) "$RemoteHost" > "$TempFile" 2>&1
		TimedExecution $TryTimeoutS 1 '' fping -c 1 -r $((MaxTriesNr-1)) "$RemoteHost" > "$TempFile" 2>&1
		LastStatus=$?
		TestText="$(cat "$TempFile" 2>/dev/null)"
		rm -f "$TempFile"
		if [ $LastStatus -eq 0 ] || [ "$(printf '%s\n' "$TestText" | grep -ie ' is alive$')" != "" ] ; then
			if [ $LastStatus -ne 3 ] && [ $LastStatus -ne 4 ] ; then
				# 3: invalid command line arguments
				# 4: system call failure
				Value=0
				TriedNr=$((TriedNr + 1))
			fi
		else
			TriedNr=$((TriedNr + 1))
		fi
	fi
	if Is_Executable ping ; then
		while [ $Value -ne 0 ] && [ $TriedNr -lt $MaxTriesNr ] ; do
			TimedExecution $((TryTimeoutS+1)) 1 '' ping -c 1 -W $TryTimeoutS "$RemoteHost" > "$TempFile" 2>&1
			LastStatus=$?
			TestText="$(cat "$TempFile" 2>/dev/null)"
			rm -f "$TempFile"
			if [ $LastStatus -eq 0 ] ; then
				Value=0
			fi
			TriedNr=$((TriedNr + 1))
		done
	fi
	if [ $TriedNr -le 0 ] ; then
		printf '%s\n' "${sERROR}E: Is_RemoteIcmpAlive: Could not test ICMP with ${RemoteHost}${fRESET}" 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		StatusCode=$Value
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

RemoteIcmpLatency ()
# Syntax as a function: RemoteIcmpLatency "$RemoteHost" $TryTimeoutS $MaxTriesNr
# Description: Returns (stdout) PING response time in miliseconds; decimal format.
# Expected parameters:
#	$1	target host IP or name
#	$2	(optional, defaults to 2) Max seconds to wait for a result, per each ping test
#	$3	(optional, defaults to 1) Max tries to do to get a success/lowest result
# Notes:
#	- If there is no remote response, nothing is returned but exitcode 254.
# Depends on functions: Is_Executable Is_IntegerNr TimedExecution
# Depends on software packages: fping|iputils-ping
{
	local RemoteHost="$1"
	local TryTimeoutS=$2
	local MaxTriesNr=$3
	local TriedNr=0
	local Alive=254
	local TestText=''
	local TempFile=''
	local Value=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	TempFile="${DirTemp}/Is_RemoteIcmpAlive.${$}.txt"
	if ! Is_IntegerNr "$TryTimeoutS" ; then TryTimeoutS=2 ; fi
	if ! Is_IntegerNr "$MaxTriesNr" ; then MaxTriesNr=1 ; fi
	if [ $Alive -ne 0 ] && [ $TriedNr -lt $MaxTriesNr ] && Is_Executable fping ; then
		TimedExecution $TryTimeoutS 1 '' fping -c 2 -r $((MaxTriesNr-1)) "$RemoteHost" > "$TempFile" 2>&1
		LastStatus=$?
		TestText="$(cat "$TempFile" 2>/dev/null)"
		rm -f "$TempFile"
		if [ $LastStatus -eq 0 ] || [ "$(printf '%s\n' "$TestText" | grep -ie ' is alive$')" != "" ] ; then
			if [ $LastStatus -ne 3 ] && [ $LastStatus -ne 4 ] ; then
				# 3: invalid command line arguments
				# 4: system call failure
				Alive=0
				Value="$(printf '%s\n' "$TestText" | sed -e 's| \([0-9]\)|\n\1|g' | grep -e ' ms' | cut -f 1 -d ' ' | sort -n | head -n 1)"
				TriedNr=$((TriedNr + 1))
			fi
		else
			TriedNr=$((TriedNr + 1))
		fi
	fi
	if Is_Executable ping ; then
		while [ $Alive -ne 0 ] && [ $TriedNr -lt $MaxTriesNr ] ; do
			TimedExecution $((TryTimeoutS+1)) 1 '' ping -c 2 -W $TryTimeoutS "$RemoteHost" > "$TempFile" 2>&1
			LastStatus=$?
			TestText="$(cat "$TempFile" 2>/dev/null)"
			rm -f "$TempFile"
			if [ $LastStatus -eq 0 ] ; then
				Alive=0
				Value="$(printf '%s\n' "$TestText" | sed -e 's|=\([0-9]\)|\n=\1|g' | grep -e '^=.* ms' | cut -f 1 -d ' ' | cut -f 2 -d '=' | sort -n | head -n 1)"
			fi
			TriedNr=$((TriedNr + 1))
		done
	fi
	if [ $TriedNr -le 0 ] ; then
		printf '%s\n' "${sERROR}E: Is_RemoteIcmpAlive: Could not test ICMP with ${RemoteHost}${fRESET}" 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		StatusCode=$Alive
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

Is_RemoteTcpAlive ()
# Syntax as a function: Is_RemoteTcpAlive "$RemoteHostPort" $TryTimeoutS $MaxTriesNr
# Description: Returns (exitcode 0) TRUE if specified host attends TCP call
# Use example (without brackets []):
#	if Is_RemoteTcpAlive 192.168.1.2:80 ; then echo "Service available" ; fi
# Expected parameters:
#	$1	target host IP or name & ':' separator & TCP port number
#	$2	(optional, defaults to 2) Max seconds to wait for a result, per each telnet/netcat test
#	$3	(optional, defaults to 1) Max tries to do to get a success result
# Depends on functions: WhereProgram Is_Executable Is_IntegerNr TimedExecution
# Depends on software packages: telnet|netcat
{
	local RemoteHostPort="$1"
	local TryTimeoutS=$2
	local MaxTriesNr=$3
	local RemoteHost=''
	local RemotePort=''
	local TriedNr=0
	local Value=254
	local TestText=''
	local TempFile=''
	local NetCat=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	TempFile="${DirTemp}/Is_RemoteTcpAlive.${$}.txt"
	RemoteHost="$(printf '%s' "$RemoteHostPort" | cut -f 1 -d ':')"
	RemotePort="$(printf '%s' "$RemoteHostPort" | cut -sf 2 -d ':')"
	if ! Is_IntegerNr "$TryTimeoutS" ; then TryTimeoutS=2 ; fi
	if ! Is_IntegerNr "$MaxTriesNr" ; then MaxTriesNr=1 ; fi
	if [ "$RemoteHost" != "" ] && Is_IntegerNr $RemotePort ; then
		if Is_Executable telnet ; then
			while [ $Value -ne 0 ] && [ $TriedNr -lt $MaxTriesNr ] ; do
				TimedExecution $((TryTimeoutS+1)) 1 '' telnet "$RemoteHost" $RemotePort > "$TempFile" 2>&1
				LastStatus=$?	# telnet usually returns exitcode 1 unless user ends with "quit" command.
				TestText="$(cat "$TempFile" 2>/dev/null)"
				rm -f "$TempFile"
				if [ "$TestText" != "" ] ; then
					# In some unexpected circumstance, we cannot catch telnet output.
					if [ "$(printf '%s\n' "$TestText" | grep -ie '^Connected to ')" != "" ] ; then
						Value=0
					fi
				fi
				TriedNr=$((TriedNr + 1))
			done
		fi
		NetCat="$(WhereProgram netcat)"
		if [ "$NetCat" = "" ] && [ "$(nc -h 2>&1 | grep -ie 'netcat')" != "" ] ; then
			NetCat="nc"
		fi
		if Is_Executable $NetCat ; then
			while [ $Value -ne 0 ] && [ $TriedNr -lt $MaxTriesNr ] ; do
				TimedExecution $((TryTimeoutS+1)) 1 '' $NetCat -v -w $TryTimeoutS -z "$RemoteHost" $RemotePort > "$TempFile" 2>&1
				LastStatus=$?	# telnet usually returns exitcode 1 unless user ends with "quit" command.
				TestText="$(cat "$TempFile" 2>/dev/null)"
				rm -f "$TempFile"
				if [ $LastStatus -eq 0 ] || [ "$(printf '%s\n' "$TestText" | grep -ie 'succeeded' -ie ' open$')" != "" ] ; then
					Value=0
				fi
				TriedNr=$((TriedNr + 1))
			done
		fi
		if [ $TriedNr -le 0 ] ; then
			printf '%s\n' "${sERROR}E: Is_RemoteTcpAlive: Could not test TCP port with ${RemoteHost}${fRESET}" 1>&2
			LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			StatusCode=$Value
		fi
	else
		printf '%s\n' "${sERROR}E: Is_RemoteTcpAlive: Bad host:port syntax \"$RemoteHostPort\"${fRESET}" 1>&2
		LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

BridgeMembers ()
# Syntax as a function: "$(BridgeMembers "$TheBridge")"
# Description: Returns (stdout) names of attached NICs; one name per line
# Expected parameters:
#	$1	Existing bridge name
# Depends on functions: WhereProgram Is_Executable
# Depends on software packages: grep, sed, iproute2|net-tools&bridge-utils
{
	local TheBridge="$1"
	local CurrentLine=''
	local CurrentMac=''
	local CurrentNIC=''
	local IfConfig=''
	local BrCtl=''
	local Value=''
	
	if Is_Executable bridge && [ "$(ip -V 2>/dev/null | grep -e iproute2)" != "" ] ; then
		Value="$(bridge link show | grep -e ": .*:.*<.*>.* master $TheBridge " | cut -f 2 -d ' ' | cut -f 1 -d ':' | cut -f 1 -d '@')"
	else
		IfConfig="$(WhereProgram ifconfig)"
		if [ "$IfConfig" = "" ] && [ -x /sbin/ifconfig ] ; then IfConfig="/sbin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /bin/ifconfig ] ; then IfConfig="/bin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /usr/sbin/ifconfig ] ; then IfConfig="/usr/sbin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /usr/bin/ifconfig ] ; then IfConfig="/usr/bin/ifconfig" ; fi
		BrCtl="$(WhereProgram brctl)"
		if [ "$BrCtl" = "" ] && [ -x /sbin/brctl ] ; then BrCtl="/sbin/brctl" ; fi
		if [ "$BrCtl" = "" ] && [ -x /bin/brctl ] ; then BrCtl="/bin/brctl" ; fi
		if [ "$BrCtl" = "" ] && [ -x /usr/sbin/brctl ] ; then BrCtl="/usr/sbin/brctl" ; fi
		if [ "$BrCtl" = "" ] && [ -x /usr/bin/brctl ] ; then BrCtl="/usr/bin/brctl" ; fi
		if [ "$IfConfig" != "" ] ; then
			NICs="$($IfConfig 2>/dev/null | grep -ve '^ ' -ve '^$' | cut -f 1 -d ' ' | cut -f 1 -d ':')"
		fi
		IFS="$(printf '\n\b')" ; for CurrentLine in $($BrCtl showmacs "$TheBridge" 2>/dev/null | tail -n +2 | tr -s '\t' ' ') ; do unset IFS
			IsLocal="$(SomeWord () { printf '%s' $3; }; SomeWord $CurrentLine)"
			if [ "$IsLocal" = "yes" ] ; then
				CurrentMac="$(SomeWord () { printf '%s' $2; }; SomeWord $CurrentLine)"
				unset IFS ; for CurNIC in $NICs ; do
					NicInfo="$(env LANG=en $IfConfig $CurNIC 2>/dev/null)"
					NicInfo="$(echo TrimAndSingle $NicInfo | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					if [ "$(printf '%s\n' "$NicInfo" | grep -ie " HWaddr.${CurrentMac}" -ie " ether ${CurrentMac}")" != "" ] ; then
						#printf '%s\n' "$CurNIC"
						if [ "$(printf '%s' "$Value" | grep -e "^${CurNIC}$")" = "" ] ; then
							Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurNIC")"
						fi
					fi
				done
			fi
		done
	fi
	if [ "$Value" != "" ] ; then
		printf '%s' "$Value" | grep -ve '^$'
	fi
}

NetBridges ()
# Syntax as a function: "$(NetBridges)"
# Description: Returns (stdout) a list of NICS identified as network bridges, One per line.
# Depends on functions: (none)
# Depends on software packages: grep, sed, iproute2|bridge-utils
{
	if [ "$(ip -V 2>/dev/null | grep -e iproute2)" != "" ] ; then
		ip link show type bridge | tr -s '\t' ' ' | grep -ve '^ ' | cut -f 2 -d ':' | sed -e 's|^ ||' | sed -e 's| $||'
	else
		brctl show | grep -ive '^bridge name' | tr -s '\t' ' ' | grep -ve '^ ' | cut -f 1 -d ' '
	fi
}

Is_NetBridge ()
# Syntax as a function: Is_Bridge $NetDevice
# Description: Returns (exitcode 0) TRUE if $NetDevice exists and is a network bridge
# Use example (without brackets []):
#	if Is_NetBridge br0 ; then echo "Yes" ; fi
# Expected parameters:
#	$1	Network device name
# Depends on functions: Is_Executable
# Depends on software packages: grep, sed, iproute2|bridge-utils
{
	local NetDevice="$1"
	local Value=254

	if Is_Executable bridge && [ "$(ip -V 2>/dev/null | grep -e iproute2)" != "" ] ; then
		if [ "$(bridge fdb show dev $NetDevice 2>/dev/null | grep -e ":.. master $NetDevice " -e ":.. master ${NetDevice}$")" != "" ] || [ "$(ip link show type bridge dev $NetDevice 2>/dev/null)" != "" ] ; then
			Value=0
		fi
	else
		if [ "$(brctl show $NetDevice | grep -ie 'Operation not supported' -ie 'No such device')" = "" ] ; then
			Value=0
		fi
	fi
	return $Value
}

ReportNetworkHosts ()
# Syntax as a sentence: ReportNetworkHosts [Scope]
# Descripcio:
#	Mostra una llista comentada d'adreces IP connectades a les mateixes xarxes que aquest equip.
#	Si s'executa amb permisos de superusuari (root), aleshores s'informa del nom dels equips i sistema operatiu.
# Expected parameters:
#	$1	(optional)
#			Syntax1: Own IP address to scan its network. Example: 192.168.1.33
#			Syntax2: IP network to scan. Example: 192.168.1.0/24
#			Syntax3: Minimum bits to mask the IP range when scanning each network; set less bits for slower scan but wider nets. Default: 24.
#	$2	(optional) (obsolete) Own IP address to scan its network. Default: each configured IP except localhost.
# TO DO:
#	- Hauria de guardar cache del S.O. detectat per una MAC, per reutilitzar quan no es descobreixi posteriorment.
#	- No identifica bé la NIC d'un contenidor veth
#	- Use TimedExecution for nmap (for some targets it never ends)
# Depends on functions: Is_Executable Is_IntegerNr PasarelasRed PrimeraIPEquip LlistarIPpropies MascaraEquipIP BitsMascara IPv4NetworkAddress EsIP EsCIDR DispositivoDeIP MacEquipIP Lowercase
# Depends on software packages: grep, sed, fping, wget
# Recommends other software: nmap xprobe openssh-client wget host/bind9-host|knot-host
{
	local Scope="$1"
	local OwnIPs="$2"
	local MinScanMaskBits=''
	local CurrentOwnIP=''
	local CurrentRemoteIP=''
	local IpRegexp=''
	local CurMask=''
	local CurrentBits=''
	local CurrentBaseIP=''
	local DiscoveredIPs=''
	local Gateways=''
	local IpWan=''
	local CurDevice=''
	local Log_Nmap=''
	local Log_Xprobe=''
	local Log_Ssh=''
	local CurrentName=''
	local CurSoftware=''
	local CurrentMAC=''
	local CurVendor=''
	local CurVendor2=''
	local PreviousDir=''
	local StatusCode_Oui=0
	local LocalContainer=''
	local RemoteCondition=''
	local MacFileName=''
	local CurrentNetwork_Regexp=''
	local DoneNetworks=''
	local ProgressTxt=''
	local ParO='('
	local ParC=')'
	
	if [ "$(EsCIDR "$Scope")" = "1" ] ; then
		CurrentBaseIP="$(printf '%s' "$Scope" | cut -f 1 -d '/')"
		CurrentBits="$(printf '%s' "$Scope" | cut -sf 2 -d '/')"
		OwnIPs="$CurrentBaseIP"
		MinScanMaskBits=$CurrentBits
	else
		if [ "$(EsIP "$Scope")" = "1" ] ; then
			OwnIPs="$Scope"
		else
			OwnIPs="$(printf '%s\n' "$OwnIPs" | cut -f 1 -d '/')"
		fi
	fi
	if ! Is_IntegerNr "$MinScanMaskBits" ; then
		if Is_IntegerNr "$Scope" ; then
			MinScanMaskBits=$Scope
		else
			MinScanMaskBits=24
		fi
	fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	if ! Is_Executable fping || ! Is_Executable grep || ! Is_Executable wget ; then
		printf '%s\n' "${sERROR}E: Some of this required software is not available: fping grep wget${fRESET}" 1>&2
		printf '%s\n' "   It's also recommended: nmap xprobe" 1>&2
	fi
	if Is_Executable nmap ; then
		if [ "$(nmap --version | grep -ie 'version' | cut -f 3 -d ' ')" = "6.00" ] ; then
			printf '%s\n' "${sWARN}W: nmap version 6.00 does not work with net bridges.${fRESET}" 1>&2
			printf '%s\n' "   You can upgrade nmap from some backports repository.${fRESET}" 1>&2
		fi
	fi
	if ! Is_Executable nmap ; then
		if [ "$ProgressTxt" = "" ] ; then
			ProgressTxt=" ${ParO}without nmap"
		else
			ProgressTxt="${ProgressTxt}, nmap"
		fi
	fi
	if ! Is_Executable xprobe2 ; then
		if [ "$ProgressTxt" = "" ] ; then
			ProgressTxt=" ${ParO}without xprobe"
		else
			ProgressTxt="${ProgressTxt}, xprobe"
		fi
	fi
	if [ "$ProgressTxt" != "" ] ; then
		ProgressTxt="${ProgressTxt}${ParC}"
	fi
	Gateways="$(PasarelasRed | sed -e 's/ /\n/g')"
	IpWan="$(PrimeraIPEquip)"
	if [ "$(EsIP "$OwnIPs")" != "1" ] ; then
		OwnIPs="$(LlistarIPpropies 0 . '^127\.')"
	fi
	unset IFS ; for CurrentOwnIP in $OwnIPs ; do
		if [ "$CurrentBaseIP" = "" ] || [ "$CurrentBits" = "" ] ; then
			CurMask="$(MascaraEquipIP $CurrentOwnIP)"
			if [ "$CurrentBits" = "" ] ; then CurrentBits="$(BitsMascara $CurMask)" ; fi
			if ! Is_IntegerNr "$CurrentBits" || [ $CurrentBits -lt $MinScanMaskBits ] ; then CurrentBits=$MinScanMaskBits ; fi
#			CurrentBaseIP="$(IPBaseXarxa "$CurrentOwnIP" $CurrentBits)"
			CurrentBaseIP="$(IPv4NetworkAddress "${CurrentOwnIP}/${CurrentBits}")"
		fi
		CurrentNetwork_Regexp="$(printf '%s\n' "${CurrentBaseIP}/${CurrentBits}" | sed -e 's|\.|\\.|g')"
		if [ "$(printf '%s' "$DoneNetworks" | grep -e "^${CurrentNetwork_Regexp}$")" = "" ] ; then
			DoneNetworks="$(printf '%s\n' "$DoneNetworks" ; printf '%s' "${CurrentBaseIP}/${CurrentBits}")"
			printf '%s\n' "Scanning local network ${CurrentBaseIP}/${CurrentBits}${ProgressTxt}..." 1>&2
#			DiscoveredIPs="$(fping -a -r 1 -t 50 -q -g ${CurrentBaseIP}/${CurrentBits} 2>&1 | grep -ive "Unreachable")"
			if [ "$(fping --help 2>&1 | grep -e '-g .*list')" != "" ] ; then
#				DiscoveredIPs="$(printf '%s\n' "$DiscoveredIPs" ; fping -a -r1 -t50 -q -g "${CurrentBaseIP}/${CurrentBits}" 2>&1 | cut -f 1 -d ' ')"
				DiscoveredIPs="$(fping -a -r1 -t50 -q -g "${CurrentBaseIP}/${CurrentBits}" 2>&1 | cut -f 1 -d ' ')"
			else
				if [ "$SecondCachedIPs" != "1" ] && Is_Executable nmap ; then
					DiscoveredIPs="$(env LANG=en nmap -sP "${CurrentBaseIP}/${CurrentBits}" 2>&1 | grep -ie 'scan .* for ' | sed -e 's|.* for ||g' | cut -f 2 -d '(' | cut -f 1 -d ')')"
				fi
			fi
			printf '%s\n' "Analyzing $(WordsNr () { printf '%s' $#; }; WordsNr $DiscoveredIPs) found hosts..." 1>&2
			unset IFS ; for CurrentRemoteIP in $DiscoveredIPs ; do
				if [ "$(EsIP "$CurrentRemoteIP")" = "1" ] ; then
					IpRegexp="$(printf '%s\n' "$CurrentRemoteIP" | sed -e 's|\.|\\.|g')"
					LocalContainer=''
					CurVendor=''
					CurrentMAC="$(env LANG=en arp -n | tr -s '\t' ' ' | grep -e "^${IpRegexp} .*..:..:..:..:..:.." | tail -n 1)"
					if [ "$CurrentMAC" = "" ] ; then
						if Is_Executable fping ; then
							fping -a -r1 -t50 $CurrentRemoteIP >/dev/null 2>&1
						else
							if Is_Executable ping ; then
								ping -c 1 $CurrentRemoteIP >/dev/null 2>&1
							fi
						fi
						CurrentMAC="$(env LANG=en arp -n | tr -s '\t' ' ' | grep -e "^${IpRegexp} .*..:..:..:..:..:.." | tail -n 1)"
					fi
					CurrentMAC="$(printf '%s\n' "$CurrentMAC" | tr -s ' ' '\n' | grep -e "..:..:..:..:..:..")"
					if [ "$CurrentMAC" = "" ] ; then
						CurrentMAC="$(MacEquipIP $CurrentRemoteIP)"
						if [ "$CurrentRemoteIP" = "$CurrentOwnIP" ] ; then
							LocalContainer="$(uname -o) $(uname -r | cut -f 1 -d '-')"
						fi
					fi
					if [ "$CurrentMAC" != "" ] ; then
						if [ ! -f /var/cache/ieee_oui.txt ] && [ $(id -u) -eq 0 ] && [ $StatusCode_Oui -eq 0 ] ; then
							printf '%s\n' "Downloading NIC vendors database..." 1>&2
							PreviousDir="$(pwd)"
							rm -fr "${DirTemp}/ieee_oui"
							mkdir -p "${DirTemp}/ieee_oui"
							cd "${DirTemp}/ieee_oui"
#							wget http://standards.ieee.org/develop/regauth/oui/oui.txt 1>&2
							wget http://standards-oui.ieee.org/oui/oui.txt 1>&2
							StatusCode_Oui=$?
							cd "$PreviousDir"
							if [ $StatusCode_Oui -eq 0 ] ; then
								mv "${DirTemp}/ieee_oui/oui.txt" /var/cache/ieee_oui.txt
								StatusCode_Oui=$?
								chmod u=rw,go=r /var/cache/ieee_oui.txt
								rm -fr "${DirTemp}/ieee_oui"
							fi
						fi
						if [ -f /var/cache/ieee_oui.txt ] ; then
							CurVendor="$(printf '%s\n' "$CurrentMAC" | tr -s ':' '-' | cut -f 1-3 -d '-')"
							CurVendor="$(Lowercase "$CurVendor")"
							CurVendor="$(cat /var/cache/ieee_oui.txt | grep --after-context=5 -ie "^$CurVendor " | grep -ve '^$')"
							if [ "$CurVendor" != "" ] ; then
								CurVendor2="$(printf '%s\n' "$CurVendor" | tail -n 1 | tr -s '\r\n' '.' | cut -f 1 -d '.' | cut -f 1 -d ',')"
								CurVendor="$(printf '%s\n' "$CurVendor" | head -n 1 | cut -f 2- -d ')' | tr -s '\r\n' '.' | cut -f 1 -d '.' | cut -f 1 -d ',')"
								CurVendor="$(echo TrimAndSingle $CurVendor | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
								CurVendor2="$(echo TrimAndSingle $CurVendor2 | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
								if [ "$CurVendor2" != "" ] ; then CurVendor="${CurVendor}, ${CurVendor2}" ; fi
								CurVendor="$(TwoWords () { printf '%s' "$1 $2"; }; TwoWords $CurVendor)"
								CurVendor="${ParO}NIC: $(echo TrimAndSingle $CurVendor | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')${ParC}"
							fi
						fi
						MacFileName="$(printf '%s' "$CurrentMAC" | tr -s ':' '-')"
						MacFileName="$(Lowercase "$MacFileName")"
					fi
					CurrentName=''
					CurSoftware=''
					if [ "$(printf '%s' "$OwnIPs" | grep -e "^${IpRegexp}$")" = "" ] ; then
						if Is_Executable ssh-keyscan ; then
							Log_Ssh="$(ssh-keyscan -t rsa1,rsa,dsa $CurrentRemoteIP 2>&1 | grep -e '^# ' | tr -s ' ' '\n' | tail -n 1 | cut -f 1 -d '-')"
						fi
						if [ $(id -u) -eq 0 ] ; then
							if Is_Executable xprobe2 ; then
								# Xprobe es más dificil de interceptar que nmap, pues mejor lanzarlo primero.
								Log_Xprobe="$(xprobe2 -m 1 $CurrentRemoteIP 2>&1)"
							fi
							if [ "$CurrentName" = "" ] || [ "$CurSoftware" = "" ] ; then
								if Is_Executable nmap ; then
									Log_Nmap="$(nmap -P0 -O $CurrentRemoteIP 2>&1)"
									if [ "$CurrentName" = "" ] ; then
										CurrentName="$(printf '%s' "$Log_Nmap" | grep -ie 'Nmap scan report for')"
										CurrentName="$(echo TrimAndSingle $(printf '%s' "$CurrentName" | cut -f 1 -d "$ParO") | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
										CurrentName="$(printf '%s' "$CurrentName" | cut -f 5- -d ' ')"
										CurrentName="$(printf '%s' "$CurrentName" | sed -e 's/ /\n/g' | tail -n 1)"
										if [ "$(EsIP "$CurrentName")" = "1" ] ; then
											CurrentName=''
										fi
									fi
									if [ "$CurrentName" = "" ] && Is_Executable host ; then
										CurrentName="$(host $CurrentRemoteIP 2>&1 | grep -ive 'NXDOMAIN' | sed -e 's|\.$||g' | tr -s '\t' ' ' | tr -s ' ' '\n' | tail -n 1)"
									fi
									if [ "$CurrentName" = "" ] && Is_Executable dig ; then
										CurrentName="$(dig +short -x $CurrentRemoteIP 2>/dev/null | sed -e 's|\.$||g')"
									fi
									if [ "$CurrentName" = "" ] && Is_Executable dnsget ; then
										CurrentName="$(dnsget $CurrentRemoteIP 2>&1 | grep -ive 'not exist' | sed -e 's|\.$||g' | tr -s '\t' ' ' | tr -s ' ' '\n' | tail -n 1)"
									fi
									if [ "$CurrentName" = "" ] && Is_Executable nslookup ; then
										CurrentName="$(nslookup $CurrentRemoteIP 2>&1)"
										if [ "$(printf '%s\n' "$CurrentName" | grep -ie 'NXDOMAIN')" = "" ] ; then
											CurrentName="$(printf '%s\n' "$CurrentName" | tr -s '\t' ' ' | grep -e ' name = ' | grep -ve ' name = .* ' | tr -s ' ' '\n' | tail -n 1)"
										else
											CurrentName=''
										fi
									fi
									if [ "$CurSoftware" = "" ] ; then
										CurSoftware="$(printf '%s' "$Log_Nmap" | grep -ie "^Running:")"
										if [ "$(printf '%s\n' "$CurSoftware" | grep -e '\^.*\^')" != "" ] ; then CurSoftware='' ; fi  # Clean garbage like: ^E^O^^^CV
										if [ "$CurSoftware" = "" ] ; then
											CurSoftware="$(printf '%s' "$Log_Nmap" | grep -ie "^OS details:")"
											if [ "$(printf '%s\n' "$CurSoftware" | grep -e '\^.*\^')" != "" ] ; then CurSoftware='' ; fi  # Clean garbage like: ^E^O^^^CV
										fi
										CurSoftware="$(echo TrimAndSingle $(printf '%s' "$CurSoftware" | cut -f 2 -d ':') | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
									fi
								fi
							fi
							if [ "$CurrentName" = "" ] || [ "$CurSoftware" = "" ] ; then
								if [ "$Log_Xprobe" != "" ] ; then
									if [ "$CurrentName" = "" ] ; then
										CurrentName=''
									fi
									if [ "$CurSoftware" = "" ] ; then
										CurSoftware="$(printf '%s' "$Log_Xprobe" | cat -v | grep -iE '^\[\+].*Running OS.*probability' | head -n 1 | sed -e 's|.*Running OS:||gi' | tr -c '[:print:]\n' '?' | cut -f 2 -d '"')"	#'
									fi
								fi
							fi
						fi
						CurSoftware="$(printf '%s' "$CurSoftware" | sed -e 's|(Guess.*||gi')"
						CurSoftware="$(echo TrimAndSingle $(printf '%s' "$CurSoftware" | sed -e 's/Kernel//g') | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
						if [ "$Log_Ssh" != "" ] ; then
							if [ "$CurSoftware" != "" ] ; then CurSoftware="${CurSoftware} " ; fi
							CurSoftware="${CurSoftware}${Log_Ssh}"
						fi
						if [ "$(printf '%s' "$CurSoftware" | grep -e '`' -e '\^' -e '{' -e '}' -e '!' -e '>' -e '<')" != "" ] ; then  #'`"
							# Characters garbage; bad detection
							CurSoftware=''
						fi
						if [ "$CurSoftware" = "" ] ; then
							if [ -f "/var/cache/hosts-os/${MacFileName}.txt" ] ; then
								CurSoftware="$(cat "/var/cache/hosts-os/${MacFileName}.txt")"
							else
								if [ "$LocalContainer" != "" ] ; then
									CurSoftware="$LocalContainer"
								fi
							fi
						else
							if [ ! -d /var/cache/hosts-os ] ; then
								mkdir -p /var/cache/hosts-os
								printf '%s\n' 'Operating system cache information for funcion ReportNetworkHosts${ParO}${ParC}' > /var/cache/hosts-os/hosts-os.txt
							fi
							printf '%s\n' "$CurSoftware" > "/var/cache/hosts-os/${MacFileName}.txt"
						fi
						if [ "$CurrentMAC" = "" ] || [ "$(printf '%s\n' "$CurrentMAC" | grep -ie '^00:16:3E:')" != "" ] ; then
							CurVendor="[Container]"
							RemoteCondition='[remote] '
						else
							if [ "$CurVendor" = "" ] && [ "$LocalContainer" != "" ] ; then
								CurVendor="[Container]"
								RemoteCondition='[local]  '
							else
								RemoteCondition='[remote] '
							fi
						fi
						#CurSoftware="${ParO}${CurSoftware}${ParC}"
						if [ "$CurrentName" = "" ] && [ "$CurrentMAC" != "" ] ; then
							CurrentName="$(cat /etc/ethers 2>/dev/null | tr -s '\t' ' ' | cut -f 1 -d '#' | grep -ie "^${CurrentMAC} " | cut -f 2 -d ' ' | grep -ve '^$' | tail -n 1)"
						fi
						if [ "$CurrentName" = "" ] && [ "$CurrentRemoteIP" != "" ] ; then
							CurrentName="$(cat /etc/hosts 2>/dev/null | tr -s '\t' ' ' | cut -f 1 -d '#' | grep -e "^${IpRegexp} " | cut -f 2 -d ' ' | grep -ve '^$' | tail -n 1)"
						fi
						if [ "$(printf '%s' "$Gateways" | grep -e "^${IpRegexp}$")" = "" ] ; then
							printf '%s\n' "$CurrentRemoteIP	$RemoteCondition	$CurrentMAC	$CurrentName	$CurSoftware	$CurVendor"
						else
							printf '%s\n' "$CurrentRemoteIP	[gateway]	$CurrentMAC	$CurrentName	$CurSoftware	$CurVendor"
						fi
					else
						if [ "$CurrentMAC" = "" ] || [ "$(printf '%s\n' "$CurrentMAC" | grep -ie '^00:16:3E:')" != "" ] ; then
							CurVendor="[Container]"
							RemoteCondition='[local]  '
						fi
						CurDevice="$(DispositivoDeIP $CurrentRemoteIP)"
						if [ "$CurrentName" = "" ] && [ "$CurrentMAC" != "" ] ; then
							CurrentName="$(cat /etc/ethers 2>/dev/null | tr -s '\t' ' ' | cut -f 1 -d '#' | grep -ie "^${CurrentMAC} " | cut -f 2 -d ' ' | grep -ve '^$' | tail -n 1)"
						fi
						if [ "$CurrentName" = "" ] && [ "$CurrentRemoteIP" != "" ] ; then
							CurrentName="$(cat /etc/hosts 2>/dev/null | tr -s '\t' ' ' | cut -f 1 -d '#' | grep -e "^${IpRegexp} " | cut -f 2 -d ' ' | grep -ve '^$' | tail -n 1)"
						fi
						if [ "$(printf '%s' "$IpWan" | grep -e "^${IpRegexp}$")" = "" ] ; then
							printf '%s\n' "$CurrentRemoteIP	[own]    	$CurrentMAC	dev $CurDevice	$CurVendor"
						else
							printf '%s\n' "$CurrentRemoteIP	[own-WAN]	$CurrentMAC	dev $CurDevice	$CurVendor"
						fi
					fi
				fi
			done
		fi
	done
}

#[/dhlan]

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

#[dhlan]
HostsfileStatusForName ()
# Returns
# Status is expressed as a "name ip" table.
{
	local HostsFile="$1"
	local Name="$2"
	local NameRegexp=''
	local Value=''
	
	NameRegexp="$(printf '%s\n' "$Name" | sed -e 's|\.|\\.|g')"
	Value="$(cat "$HostsFile" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | cut -f 1,2 -d ' ')"
	Value="$(printf '%s\n' "$Value" | grep -e "^.*\..*\..*\..* ${NameRegexp}$")"
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value" | sort
#	else
#		printf '%s\n' "$Name "
	fi
}

HostsWithoutName ()
# Returns specified HostsContent discarding lines that contain specified host Name
{
	local Name="$1"
	local HostsContent="$2"
	local BlankLineMask=''
	local CurLine=''
	local CurName=''
	
	BlankLineMask="$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d ' ')BLANK$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d ' ')blank$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d ' ')"
	IFS="$(printf '\n\b')" ; for CurLine in $(printf '%s' "$HostsContent" | sed -e "s|^$|${BlankLineMask}|g") ; do unset IFS
		if [ "$CurLine" = "$BlankLineMask" ] ; then CurLine='' ; fi
		CurName="$(printf '%s\n' "$CurLine" | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's| $||g' | grep -e '^.*\..*\..*\..* ..*')"
		CurName="$(printf '%s\n' "$CurName" | cut -sf 2 -d ' ')"
		if [ "$CurName" != "$Name" ] ; then
			printf '%s\n' "$CurLine"
		fi
	done
}

HostsfileNamedEntries ()
# Returns a clean and sorted "ip name" table with entries that match specified hostname (single space as field separator).
{
	local HostsFile="$1"
	local Name="$2"
	local Entries=""
	local CurrentEntry=""
	local CurrentIP=""
	local CurrentName=""
	local Value=""
	
	Entries="$(cat "$HostsFile" | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's| $||g' | grep -ve '^$' | grep -ve '^ $')"
	Entries="$(printf '%s\n' "$Entries" | grep -E '^.*\..*\..*\..* ..*')"
	IFS="$(printf '\n\b')" ; for CurrentEntry in $Entries ; do unset IFS
		CurrentEntry="$(echo $CurrentEntry | cut -f 1,2 -d ' ')"
		CurrentName="$(printf '%s\n' "$CurrentEntry" | cut -f 2 -d ' ')"
		CurrentIP="$(printf '%s\n' "$CurrentEntry" | cut -f 1 -d ' ')"
		if [ "$CurrentName" = "$Name" ] ; then
			if [ "$(EsIP "$CurrentIP")" = "1" ] ; then
				Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentIP $CurrentName")"
			fi
		fi
	done
	Value="$(printf '%s\n' "$Value" | grep -ve '^$')"
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value" | sort
	fi
}

HostsfileEntries ()
# Teturns a clean and sorted "ip name" table (single space as field separator).
{
	local HostsFile="$1"
	local Entries=""
	local CurrentEntry=""
	local CurrentIP=""
	local CurrentName=""
	local Value=""
	
	Entries="$(cat "$HostsFile" | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's| $||g' | grep -ve '^$' | grep -ve '^ $')"
	Entries="$(printf '%s\n' "$Entries" | grep -E '^.*\..*\..*\..* ..*')"
	IFS="$(printf '\n\b')" ; for CurrentEntry in $Entries ; do unset IFS
		CurrentEntry="$(echo $CurrentEntry | cut -f 1,2 -d ' ')"
		CurrentName="$(printf '%s\n' "$CurrentEntry" | cut -f 2 -d ' ')"
		CurrentIP="$(printf '%s\n' "$CurrentEntry" | cut -f 1 -d ' ')"
		if [ "$(EsIP "$CurrentIP")" = "1" ] ; then
			Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentIP $CurrentName")"
		fi
	done
	Value="$(printf '%s\n' "$Value" | grep -ve '^$')"
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value" | sort
	fi
}

UpdateHostsFile ()
{
	local HostsFile="$1"
	local OldNamesIpsStatus="$2"
	local NewNamesIpsStatus="$3"
	local AllNames=''
	local CurName=''
	local HostsContent=''
	local SaveResultTxt=''
	local CurLine=''
	local LineRegexp=''
	local LastStatus=0
	local StatusCode=0
	
	HostsContent="$(cat "$HostsFile" 2>/dev/null)"
	AllNames="$(printf '%s\n' "$OldNamesIpsStatus" ; printf '%s\n' "$NewNamesIpsStatus")"
	AllNames="$(printf '%s\n' "$AllNames" | cut -sf 2 -d ' ' | sort -u)"
	for CurName in $AllNames ; do
		if [ "$(printf '%s\n' "$OldNamesIpsStatus" | grep -e " ${CurName}$" | sort)" != "$(printf '%s\n' "$NewNamesIpsStatus" | grep -e " ${CurName}$" | sort)" ] ; then
			IFS="$(printf '\n\b')" ; for CurLine in $(printf '%s\n' "$OldNamesIpsStatus" | grep -e " ${CurName}$") ; do unset IFS
				# Bucle to only report lines removals
				LineRegexp="$(printf '%s\n' "$CurLine" | sed -e 's|\.|\\.|g')"
				if [ "$(printf '%s\n' "$NewNamesIpsStatus" | grep -e "^${LineRegexp}$")" = "" ] ; then
					LogProgram 3 "$(printf '%s\n' "$CurLine" | sed -e 's| | for name |g' | sed -e 's|^|Removing IP |g')"
				fi
			done
			HostsContent="$(HostsWithoutName "$CurName" "$HostsContent")"	# Removal
			IFS="$(printf '\n\b')" ; for CurLine in $(printf '%s\n' "$NewNamesIpsStatus" | grep -e " ${CurName}$") ; do unset IFS
				# Bucle to only report lines additions
				LineRegexp="$(printf '%s\n' "$CurLine" | sed -e 's|\.|\\.|g')"
				if [ "$(printf '%s\n' "$OldNamesIpsStatus" | grep -e "^${LineRegexp}$")" = "" ] ; then
					LogProgram 3 "$(printf '%s\n' "$CurLine" | sed -e 's| | for name |g' | sed -e 's|^|Adding IP |g')"
				fi
			done
			if [ "$(printf '%s\n' "$NewNamesIpsStatus" | grep -e " ${CurName}$")" != "" ] ; then
				# Addition
				HostsContent="$(printf '%s\n' "$HostsContent" ; printf '%s\n' "$NewNamesIpsStatus" | grep -e " ${CurName}$")"
			fi
		fi
	done
	SaveResultTxt="$(printf '%s\n' "$HostsContent" > "$HostsFile" 2>&1)"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -ne 0 ] ; then
		if [ "$SaveResultTxt" = "" ] ; then SaveResultTxt="E: Some error occurred when writing to $HostsFile" ; fi
		LogProgram 1 "$SaveResultTxt"
	else
		if [ "$SaveResultTxt" = "" ] ; then SaveResultTxt="I: Updated $HostsFile" ; fi
		if [ "$SaveResultTxt" = "" ] ; then
			LogProgram 4 "$SaveResultTxt"
		fi
	fi
	return $StatusCode
}

InternetIsUp ()
# Same function in services: quepasa dhlan
{
	local StopFile="$1"
	local ExplicitInternetDns="$2"
	local LastStatus=''
	
	if [ "$ExplicitInternetDns" = "" ] ; then ExplicitInternetDns="$Dns" ; fi
	nc -v -w 4 -z $ExplicitInternetDns 53 >/dev/null 2>&1
	LastStatus=$?
	if [ $LastStatus -ne 0 ] && [ ! -f "$StopFile" ] ; then
		sleep 1
		nc -v -w 4 -z $ExplicitInternetDns 53 >/dev/null 2>&1
		LastStatus=$?
	fi
	if [ $LastStatus -ne 0 ] && [ ! -f "$StopFile" ] ; then
		sleep 2
		nc -v -w 4 -z $ExplicitInternetDns 53 >/dev/null 2>&1
		LastStatus=$?
	fi
	return $LastStatus
}

ReportLocalHardware ()
# Specific NICs to report can be specified optionally
{
	local NICs="$1"
	local CurNIC=''
	local TheDefaultNICs=''
	local LinkStatus=''
	local LinkMax=''
	local CurIP=''
	local NicDriver=''
	local NicModel=''
	local DevTree=''
	local DriverSpecifiedAt=''
	local ModelSpecifiedAt=''
	local CurGateway=''
	local InternetNowUp=''
	local GatewayUp=''
	local CurDescription=''
	local CurDescription0=''
	local DeviceReport=''
	local PhyName=''
	local MiiPath=''
	local IwPath=''
	local DevMembers=''
	local AllBridges=''
	local BridgedNICs=''
	local CurBridge=''
	local LastStatus=0
	local StatusCode=0
	
	printf '%s\n' "LOCAL NETWORK INTERFACES DESCRIPTION"
	printf '%s\n' ""
	if [ "$NICs" = "" ] ; then
		NICs="$(ListaDispositivosRed | tr -s ' ' '\n' | grep -ve '.@.')"  # Ignore names like veth1002_nr8c@if2
	else
		NICs="$(printf '%s' "$NICs" | tr -s ',' ' ')"
	fi
	TheDefaultNICs="$(DefaultNICs)"
	printf '%s\n' "NIC/link Model Driver MAC IP Acquirement Description"
	MiiPath="$(WhereProgram mii-tool)"
	if [ "$MiiPath" = "" ] ; then MiiPath="$(WhereProgram /sbin/mii-tool)" ; fi
	if [ "$MiiPath" = "" ] ; then MiiPath="$mii-tool" ; fi
	IwPath="$(WhereProgram iw)"
	if [ "$IwPath" = "" ] ; then IwPath="$(WhereProgram /sbin/iw)" ; fi
	if [ "$IwPath" = "" ] ; then IwPath="$iw" ; fi
	AllBridges="$(NetBridges)"
	IFS="$(printf '\n\b')" ; for CurBridge in $AllBridges ; do unset IFS
		BridgedNICs="$(printf '%s' "$BridgedNICs" ; BridgeMembers "$CurBridge" | sed -e "s/$/|${CurBridge}/g")"
	done
	BridgedNICs="$(printf '%s' "$BridgedNICs" | grep -ve '^$' | sort -u)"
	for CurNIC in $NICs ; do
		printf '%s' "${sINFO}${CurNIC}${fRESET}"
		NicModel=''
		CurDescription=''
		LinkStatus=''
		LinkMax=''
		IpAcquirement=''
		BroadcastNic=''
		CidrNic=''
		NicDriver=''
		DevTree=''
		DriverSpecifiedAt=''
		DeviceReport="$(LANG=en $MiiPath -v "$CurNIC" 2>&1)"
		LastStatus=$?
		if [ $LastStatus -eq 0 ] && [ "$(printf '%s\n' "$DeviceReport" | grep -e "^${CurNIC}:")" != "" ] && [ "$(printf '%s\n' "$DeviceReport" | grep -e 'failed:')" = "" ] ; then
			LinkStatus="$(printf '%s\n' "$DeviceReport" | grep -e "^${CurNIC}:" | tr -s ' ' | sed -e 's|negotiated||g' | sed -e 's|flow-control||g' | sed -e 's|: |:|g' | sed -e 's|,||g' | sed -e 's| link$|-link|g' | sed -e 's|link |link-|g' | tr -s ' ' ':')"
			LinkStatus=":${sGOODNORMAL}$(printf '%s' "$LinkStatus" | cut -f 2- -d ':')${fRESET}"
			LinkMax="$(printf '%s\n' "$DeviceReport" | grep -ie 'capabilities:' | cut -f 2 -d ':')"
			LinkMax="$(echo TrimAndSingle $LinkMax | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
			LinkMax=$(OneWord () { printf '%s' $1; }; OneWord $LinkMax)
			if [ "$LinkMax" != "" ] ; then
				LinkStatus="${LinkStatus}|Max:${LinkMax}"
			fi
		else
			DeviceReport="$(LANG=en iwconfig "$CurNIC" 2>/dev/null)"
			if [ "$(printf '%s\n' "$DeviceReport" | grep -ie 'no wireless extensions')" = "" ] && [ "$(printf '%s\n' "$DeviceReport" | grep -ie 'ESSID')" != "" ] ; then
				# Wireless device
				LinkStatus="$(printf '%s\n' "$DeviceReport" | grep -ie 'Bit.*Rate=' | sed -e 's|.*Bit.*Rate=||gi' | cut -f 1 -d ' ')"
				if [ "$LinkStatus" != "" ] ; then
					CurDescription="$(printf '%s\n' "$DeviceReport" | grep -ie 'ESSID:' | sed -e 's|ESSID:||gi' | cut -f 2 -d '"' | cut -f 2 -d '"' | tr -s ' ' '_')"
#					if [ "$CurDescription" != "" ] ; then CurDescription="\"${CurDescription}\"" ; fi
					LinkStatus=":${LinkStatus}mbps:${sGOODNORMAL}${CurDescription}${fRESET}"
				else
					LinkStatus=":no-link"
				fi
				PhyName="$(LANG=en $IwPath dev)"
				PhyName="$(echo TrimAndSingle $PhyName | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				PhyName="$(printf '%s\n' "$PhyName" | sed -e 's|phy\#|\nphy|gi' | grep -ie "phy. Interface ${CurNIC} " -ie "phy.. Interface ${CurNIC} " | cut -f 1 -d ' ')"
				if [ "$PhyName" != "" ] ; then
					LinkMax="$(LANG=en $IwPath phy "$PhyName" info | grep -ie '* ..* Mbps'| grep -ie '* ..* Mbps' | sed -e 's|.*\* ||' | cut -f 1 -d '.' | sort -n | tail -n 1)"
					if [ "$LinkMax" != "" ] ; then
						LinkStatus="${LinkStatus}|Max:${LinkMax}mbps"
					fi
				fi
			else
				LinkStatus=''
			fi
		fi
		CurMAC="${sVALUE}$(NetdevMac "$CurNIC")${fRESET}"
#		CurIP="$(IpDeDispositivo "$CurNIC")"
		CurIP="$(IPCDIRFromDevice "$CurNIC")"
		if [ "$CurIP" != "" ] ; then
			IpAcquirement="$(NicIpAcquirement "$CurNIC")"
			BroadcastNic="$(ip address show dev "$CurNIC" | grep -E '(^| )inet .* brd ' | sed -e 's|.* brd |brd |g' | cut -f 2 -d ' ')"
			CidrNic="$(ip address show dev "$CurNIC" | grep -E '(^| )inet .* scope ' | sed -e 's|.*inet |inet |g' | cut -f 2 -d ' ')"
			CidrNic="${sVALUE}$(echo $CidrNic | sed -e 's| | + |g')${fRESET}"
		else
			IpAcquirement='none'
			CurBridge="$(printf '%s' "$BridgedNICs" | grep -e "^${CurNIC}$" -e "^${CurNIC}|")"
			if [ "$CurBridge" != "" ] ; then
				CurBridge="$(printf '%s' "$CurBridge" | cut -sf 2 -d '|')"
				CurBridge="$(echo TrimAndSingle $CurBridge | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				CurBridge="$(printf '%s' "$CurBridge" | sed -e 's| |,|')"
				BroadcastNic="${ParO}bridged${ParC}"
				CidrNic="${ParO}${fUNDERL}bridged:${sVALUE}${CurBridge}${fUNDERL_}${fRESET}${ParC}"
			else
				BroadcastNic="${ParO}none${ParC}"
				CidrNic="${ParO}${sVALUE}none${fRESET}${ParC}"
			fi
		fi
		DevTree="$(find /sys/devices | grep -e "/net/${CurNIC}$" | head -n 1)"
		ModelSpecifiedAt="$(grep -Isirnce '^PCI_ID=.' "$DevTree"/* | grep -ve ':0$' | sed -e 's|:..$||g' | sed -e 's|:.$||g' | head -n 1)"
		if [ -f "$ModelSpecifiedAt" ] ; then
			NicModel="$(cat "$ModelSpecifiedAt" | grep -ie '^PCI_ID=.' | head -n 1 | cut -f 2- -d '=')"
		else
			ModelSpecifiedAt="$(grep -Isirnce '^usb:v.' "$DevTree"/* | grep -ve ':0$' | sed -e 's|:..$||g' | sed -e 's|:.$||g' | head -n 1)"
			if [ -f "$ModelSpecifiedAt" ] ; then
				NicModel="$(cat "$ModelSpecifiedAt" | grep -ie '^usb:v.' | head -n 1 | cut -f 2 -d ':' | sed -e 's|^v||' | cut -c 1-9 | sed -e 's|p|:|')"
			fi
		fi
		DriverSpecifiedAt="$(grep -Isirnce '^DRIVER=.' "$DevTree"/* | grep -ve ':0$' | sed -e 's|:..$||g' | sed -e 's|:.$||g' | head -n 1)"
		if [ -f "$DriverSpecifiedAt" ] ; then
			NicDriver="$(cat "$DriverSpecifiedAt" | grep -ie '^DRIVER=.' | head -n 1 | cut -f 2- -d '=')"
		else
			NicDriver=''
		fi
#		if [ "$LinkStatus" = "" ] ; then LinkStatus="$CurNIC" ; fi
		if [ "$NicModel" != "" ] ; then
			CurDescription0="$(lspci -d "$NicModel" 2>/dev/null | head -n 1 | cut -f 2- -d ' ' | cut -f 2- -d ':' | cut -f 1 -d '(')"
			CurDescription0="$(echo TrimAndSingle $CurDescription0 | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
			if [ "$CurDescription0" != "" ] ; then CurDescription="${fITALIC}${CurDescription0}${fITALIC_}" ; fi
			if [ "$CurDescription" = "" ] ; then
				CurDescription0="$(lsusb -d "$NicModel" 2>/dev/null | head -n 1 | sed -e "s|.* $NicModel ||gi" | cut -f 2- -d ':' | cut -f 1 -d '(')"
				CurDescription0="$(echo TrimAndSingle $CurDescription0 | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				if [ "$CurDescription0" != "" ] ; then CurDescription="${fITALIC}${CurDescription0}${fITALIC_}" ; fi
			fi
		fi
		if [ "$CurDescription" = "" ] ; then
			# Without PCI/USB model
			CurDescription0="$(cat "${DevTree}/device/interface" 2>/dev/null | head -n 1)"
			CurDescription0="$(echo TrimAndSingle $CurDescription0 | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
			if [ "$CurDescription0" != "" ] ; then CurDescription="$CurDescription0" ; fi
		fi
		if [ "$NicModel" = "" ] ; then
#			CurDescription=''
#			if [ "$LinkStatus" = "$CurNIC" ] && [ "$NicDriver" = "" ] ; then
			if [ "$LinkStatus" = "" ] && [ "$NicDriver" = "" ] ; then
				if Is_NetBridge "$CurNIC" ; then
					DevMembers="$(BridgeMembers "$CurNIC")"
					DevMembers="$(echo TrimAndSingle $DevMembers | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					NicModel="${ParO}${sGOODNORMAL}${fUNDERL}LogicalBridge${fUNDERL_}${fRESET}${fUNDERL}:$(printf '%s' "$DevMembers" | tr ' ' '+')${fUNDERL_}${ParC}"
					CurDescription='Virtual master device of other network links'
				else
					NicModel="${ParO}${sGOODNORMAL}logical${fRESET}${ParC}"
					CurDescription='Virtual network interface'
				fi
				NicDriver="${ParO}$(uname -s)${ParC}"
			else
				NicModel="${ParO}?${ParC}"
			fi
		fi
		if [ "$NicDriver" = "" ] ; then NicDriver="${ParO}?${ParC}" ; fi
		if [ "$(printf '%s\n' "$TheDefaultNICs" | grep -e "^${CurNIC}$")" != "" ] ; then
			printf '%s\n' "$LinkStatus $NicModel $NicDriver $CurMAC $CidrNic $IpAcquirement [default] \"${CurDescription}\""
		else
			printf '%s\n' "$LinkStatus $NicModel $NicDriver $CurMAC $CidrNic $IpAcquirement \"${CurDescription}\""
		fi
	done
	printf '%s\n' ""
	CurGateway="$(PasarelasRed | cut -f 1 -d ' ')"
	if InternetIsUp "$MainControllerStopFile" ; then
		InternetNowUp="${sGOODEM}Yes${fRESET}"
		GatewayUp="${sGOODEM}Alive${fRESET}"
	else
		if [ "$CurGateway" != "" ] ; then
			InternetNowUp="${sWARN}No${fRESET}"
#			ping -c 1 $CurGateway >/dev/null
#			LastStatus=$?
#			if [ $LastStatus -eq 0 ] ; then
			if Is_RemoteIcmpAlive $CurGateway ; then
				GatewayUp="${sGOODEM}Alive${fRESET}"
			else
				GatewayUp="${sERROR}Fails${fRESET}"
			fi
		else
			InternetNowUp="${sWARN}Unroutable${fRESET}"
			GatewayUp="${sWARN}No${fRESET}"
		fi
	fi
	printf '%s\n' "GATEWAY:${sVALUE}${CurGateway}${fRESET}:${GatewayUp} INTERNET:${InternetNowUp}"
	return $LastStatus
}

RemoteMonitor ()
{
	local RemoteHost="$1"
	local WaitIntervalS="$2"
	local LogFile="$3"
	local ProblemsLog="$4"
	local CurLatency=''
	local CurLatencyInteger=''
	local CurLatencyS=''
	local CurTime=''
	local CurTimeStamp=''
	local PreviousLevel1=3
	local PreviousLevel2=3
	local HistoricLatencyI1=0
	local HistoricLatencyI2=0
	local HistoricLatencyI3=0
	local HistoricLatencyI4=0
	local HistoricLatencyI5=0
	local HistoricLatencyI6=0
	local HistoricLatencyI7=0
	local HistoricLatencyI8=0
	local HistoricLatencyI9=0
	local HistoricLatencyI10=0
	local HistoricLatencyI11=0
	local HistoricLatencyI12=0
	local HistoricLatencyI13=0
	local HistoricLatencyI14=0
	local HistoricLatencyI15=0
	local HistoricLatencyI16=0
	local LastLatencyI=0
	local AverageLatency=0
	local TryTimeoutS=10
	local LengthIntegerAlign=''
	local CurLatency_Aligned=''
	local Average_Aligned=''
	local MinLengthWithDecimals=0
	local TestNr_Divider=0
	local LastNoResponses=16
	local LastProblemTime=''
	local LastProblemReported=''
	local LastProblem_Show=''
	local BeginTime_Show=''
	local ProblemsNr=0
	local ProblemsNr_Suffix=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$RemoteHost" = "" ] ; then
		printf '%s\n' "${sERROR}E: No remote host specified${fRESET}" 1>&2
		printf '%s\n' "Syntax: $ServiceName remote-monitor RemoteHost [WaitIntervalS] [LogFile] [ProblemsLog]" 1>&2
		printf '%s\n' "Example 1: $ServiceName remote-monitor 192.168.1.1" 1>&2
		printf '%s\n' "Example 2: $ServiceName remote-monitor 192.168.1.1 60" 1>&2
		printf '%s\n' "Example 3: $ServiceName remote-monitor 192.168.1.1 30 /tmp/remote-monitor.log" 1>&2
		printf '%s\n' "Example 4: $ServiceName remote-monitor 192.168.1.1 '' /tmp/remote-monitor.log" 1>&2
		printf '%s\n' "Example 5: $ServiceName remote-monitor 192.168.1.1 '' /tmp/remote-monitor.log /tmp/errors.log" 1>&2
		LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$ProblemsLog" != "" ] ; then touch "$ProblemsLog" ; fi
		if ! Is_IntegerNr $WaitIntervalS ; then WaitIntervalS=3 ; fi
		LengthIntegerAlign=${#TryTimeoutS}
		LengthIntegerAlign=$((LengthIntegerAlign + 3))
		BeginTime_Show="$(date '+d%_d %H:%Mh')"
		while true ; do
			TestNr_Divider=$((TestNr_Divider + 1))
			CurTime=$(date '+%s')
			CurTimeStamp="$(date -d @$CurTime '+%F %T')"
			CurLatency="$(RemoteIcmpLatency "$RemoteHost" $TryTimeoutS 2)"
			CurLatencyInteger="$(printf '%s' "$CurLatency" | cut -f 1 -d '.')"
			if Is_IntegerNr $CurLatencyInteger ; then
				LastNoResponses=0
				LastLatencyI=$CurLatencyInteger
			else
				LastNoResponses=$((LastNoResponses + 1))
				if [ $LastNoResponses -ge 16 ] ; then
					TestNr_Divider=0
				fi
				if [ $TestNr_Divider -gt 0 ] ; then
					if [ $AverageLatency -gt 0 ] ; then
						LastLatencyI=$((LastLatencyI + $((AverageLatency * 2))))
					else
						if [ $LastLatencyI -gt 0 ] ; then
							LastLatencyI=$((LastLatencyI * 2))
						else
							LastLatencyI=160
						fi
					fi
				else
					if [ $LastNoResponses -lt 16 ] ; then
						LastLatencyI=$((TryTimeoutS * 1000))
					fi
				fi
			fi
			HistoricLatencyI16=$HistoricLatencyI15
			HistoricLatencyI15=$HistoricLatencyI14
			HistoricLatencyI14=$HistoricLatencyI13
			HistoricLatencyI13=$HistoricLatencyI12
			HistoricLatencyI12=$HistoricLatencyI11
			HistoricLatencyI11=$HistoricLatencyI10
			HistoricLatencyI10=$HistoricLatencyI9
			HistoricLatencyI9=$HistoricLatencyI8
			HistoricLatencyI8=$HistoricLatencyI7
			HistoricLatencyI7=$HistoricLatencyI6
			HistoricLatencyI6=$HistoricLatencyI5
			HistoricLatencyI5=$HistoricLatencyI4
			HistoricLatencyI4=$HistoricLatencyI3
			HistoricLatencyI3=$HistoricLatencyI2
			HistoricLatencyI2=$HistoricLatencyI1
			HistoricLatencyI1=$LastLatencyI
			AverageLatency=$((HistoricLatencyI1 + HistoricLatencyI2 + HistoricLatencyI3 + HistoricLatencyI4 + HistoricLatencyI5 + HistoricLatencyI6 + HistoricLatencyI7 + HistoricLatencyI8 + HistoricLatencyI9 + HistoricLatencyI10 + HistoricLatencyI11 + HistoricLatencyI12 + HistoricLatencyI13 + HistoricLatencyI14 + HistoricLatencyI15 + HistoricLatencyI16))
			if [ $TestNr_Divider -ge 16 ] ; then
				AverageLatency=$((AverageLatency / 16))
			else
				if [ $TestNr_Divider -ge 1 ] ; then
					AverageLatency=$((AverageLatency / TestNr_Divider))
				else
					AverageLatency=$AverageLatency
				fi
			fi
			Average_Aligned="$AverageLatency"
			while [ ${#Average_Aligned} -lt $((LengthIntegerAlign + 1)) ] ; do Average_Aligned=" $Average_Aligned" ; done
			if Is_IntegerNr $CurLatencyInteger ; then
				CurLatency_Aligned="$(printf '%s' "$CurLatency" | cut -f 1 -d '.')"
				while [ ${#CurLatency_Aligned} -lt $LengthIntegerAlign ] ; do CurLatency_Aligned=" $CurLatency_Aligned" ; done
				if [ "$(printf '%s' "$CurLatency" | grep -e '\.')" != "" ] ; then
					CurLatency_Aligned="${CurLatency_Aligned}.$(printf '%s' "$CurLatency" | cut -f 2- -d '.')"
				fi
				if [ $MinLengthWithDecimals -lt ${#CurLatency_Aligned} ] ; then MinLengthWithDecimals=${#CurLatency_Aligned} ; fi
				if [ ${#CurLatency_Aligned} -lt $MinLengthWithDecimals ] && [ "$(printf '%s' "$CurLatency_Aligned" | grep -e '\.')" = "" ] ; then
					CurLatency_Aligned="${CurLatency_Aligned}."
				fi
				while [ ${#CurLatency_Aligned} -lt $MinLengthWithDecimals ] ; do CurLatency_Aligned="${CurLatency_Aligned}0" ; done
				while [ ${#CurLatency_Aligned} -lt $((LengthIntegerAlign + 4)) ] ; do CurLatency_Aligned="$CurLatency_Aligned " ; done
				CurLatencyS=$((CurLatencyInteger / 1000))
				if [ $CurLatencyS -eq 0 ] ; then
					if [ $PreviousLevel2 -ne 3 ] ; then
						ProblemsNr=$((ProblemsNr + 1))
						if [ $ProblemsNr -eq 1 ] ; then
							ProblemsNr_Suffix='st'
						else
							if [ $ProblemsNr -eq 2 ] ; then
								ProblemsNr_Suffix='nd'
							else
								if [ $ProblemsNr -eq 3 ] ; then
									ProblemsNr_Suffix='rd'
								else
									ProblemsNr_Suffix='th'
								fi
							fi
						fi
					fi
					if [ "$LastProblemTime" != "" ] ; then
						LastProblem_Show=" ; ${ProblemsNr}${ProblemsNr_Suffix} problem was $(date -d @$LastProblemTime '+d%_d %H:%Mh')"
					else
						LastProblem_Show=" ; Fine since $BeginTime_Show"
					fi
					printf '%s\n' "$CurTimeStamp ${sINFO}${RemoteHost}${fRESET} ${sGOODNORMAL}${CurLatency_Aligned} ms${fRESET} ; Average $Average_Aligned ms${LastProblem_Show}"
					if [ "$LogFile" != "" ] ; then
						printf '%s\n' "$CurTimeStamp $RemoteHost ${CurLatency_Aligned} ms ; Average $Average_Aligned ms" >> "$LogFile"
					fi
					if [ $PreviousLevel1 -ne 3 ] || [ $PreviousLevel2 -ne 3 ] ; then
						printf "$fBELL"
					fi
					PreviousLevel1=$PreviousLevel2
					PreviousLevel2=3
				else
					LastProblemTime=$CurTime
					printf '%s\n' "$CurTimeStamp ${sINFO}${RemoteHost}${fRESET} ${sWARN}${CurLatency_Aligned} ms > $CurLatencyS seconds${fRESET} ; Average $Average_Aligned ms"
					if [ "$LogFile" != "" ] ; then
						printf '%s\n' "$CurTimeStamp $RemoteHost ${CurLatency_Aligned} ms > $CurLatencyS seconds ; Average $Average_Aligned ms" >> "$LogFile"
					fi
					if [ $PreviousLevel1 -ne 2 ] || [ $PreviousLevel2 -ne 2 ] ; then
						printf "$fBELL"
					fi
					if [ "$LastProblemTime" != "$LastProblemReported" ] && [ "$ProblemsLog" != "" ] ; then
						LastProblemReported=$LastProblemTime
						printf '%s\n' "$CurTimeStamp Unix:$LastProblemReported $RemoteHost Late" >> "$ProblemsLog"
					fi
					PreviousLevel1=$PreviousLevel2
					PreviousLevel2=2
				fi
			else
				LastProblemTime=$CurTime
				CurLatency_Aligned='No response'
				while [ ${#CurLatency_Aligned} -lt $((LengthIntegerAlign + 4 + 3)) ] ; do CurLatency_Aligned="$CurLatency_Aligned " ; done
				sERROR="${cfRED}${cbBLACK}${fBOLD}"
				printf '%s\n' "$CurTimeStamp ${sINFO}${RemoteHost}${fRESET} ${sERROR}${CurLatency_Aligned}${fRESET}"
				if [ "$LogFile" != "" ] ; then
					printf '%s\n' "$CurTimeStamp $RemoteHost ${CurLatency_Aligned}" >> "$LogFile"
				fi
				if [ $PreviousLevel1 -ne 1 ] || [ $PreviousLevel2 -ne 1 ] ; then
					printf "$fBELL"
				fi
				if [ "$LastProblemTime" != "$LastProblemReported" ] && [ "$ProblemsLog" != "" ] ; then
					LastProblemReported=$LastProblemTime
					printf '%s\n' "$CurTimeStamp Unix:$LastProblemReported $RemoteHost No response" >> "$ProblemsLog"
				fi
				PreviousLevel1=$PreviousLevel2
				PreviousLevel2=1
			fi
			if Is_IntegerNr $CurLatencyInteger ; then
				sleep $WaitIntervalS
			fi
		done
	fi
	return $LastStatus
}

NicMonitor ()
{
	local LastStatus=0
	local StatusCode=0
	
	return $LastStatus
}

WakeOnLan ()
{
	local CurrentEther=''
	local EthersRecords=''
	local CurMatchedMac=''
	local MACsDone=''
	local CurAskedName=''
	local CurAskedRegexp=''
	local MatchesNr=0
	local CurMatchedName=''
	local LastStatus=0
	local StatusCode=0
	
	LackDependencies="$(DependenciasFaltan wakeonlan)"	#"
	if [ "$LackDependencies" != "" ] ; then
		printf '%s\n' "E: Following software must be installed before this dependent action:" 1>&2
		printf '%s\n' "   $LackDependencies" 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $# -le 0 ] ; then
		printf '%s\n' "E: Some host name must be specified to find it's MAC address at $EthersFile2 and/or $EthersFile1" 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		EthersRecords="$(cat "$EthersFile2" "$EthersFile1" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | grep -ve '^$' | grep -E '^..:..:..:..:..:.. ..*')"
		for CurAskedName in "$@" ; do
			IFS="$(printf '\n\b')" ; for CurrentEther in $EthersRecords ; do unset IFS
				CurMatchedMac=''
				CurMatchedName=''
				if [ "$(printf '%s' "$CurAskedName" | grep -e '\.')" != "" ] ; then
					# Strict FQDN
					CurAskedRegexp="$(printf '%s\n' "$CurAskedName" | sed -e 's|\.|\\.|g')"
					if [ "$(printf '%s' "$CurrentEther" | grep -e " $CurAskedRegexp " -e " ${CurAskedRegexp}$")" != "" ] ; then
						CurMatchedMac="$(printf '%s' "$CurrentEther" | cut -f 1 -d ' ')"
						CurMatchedName="$(printf '%s' "$CurrentEther" | cut -f 2- -d ' ' | tr -s ' ' '\n' | grep -e "^${CurAskedRegexp}$" | head -n 1)"
					fi
				else
					# Hostname only
					if [ "$(printf '%s' "$CurrentEther" | grep -ie " $CurAskedName " -ie " ${CurAskedName}$" -ie " ${CurAskedName}\.")" != "" ] ; then
						CurMatchedMac="$(printf '%s' "$CurrentEther" | cut -f 1 -d ' ')"
						CurMatchedName="$(printf '%s' "$CurrentEther" | cut -f 2- -d ' ' | tr -s ' ' '\n' | grep -ie "^${CurAskedName}$" -ie "^${CurAskedName}\." | head -n 1)"
					fi
				fi
				if [ "$CurMatchedMac" != "" ] ; then
					MatchesNr=$((MatchesNr + 1))
					if [ "$(printf '%s' " $MACsDone " | grep -ie " $CurMatchedMac ")" = "" ] ; then
						printf '%s' "$CurMatchedName -> "
						wakeonlan $CurMatchedMac
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ $LastStatus -eq 0 ] ; then MACsDone="$MACsDone $CurMatchedMac" ; fi
					fi
				fi
			done
		done
		if [ $MatchesNr -gt 0 ] ; then
			printf '%s\n' "I: Total of $MatchesNr remote hosts matched."
		else
			printf '%s\n' "E: Asked names not found at $EthersFile2 or $EthersFile1"
			LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	
	return $LastStatus
}

#[/dhlan]


##### TEMPLATE FUNCTIONS TO CUSTOMIZE #####

ListChildReport ()
# Used to get a better line with child brief for a list.
{
	local ChildId="$1"
	local ChildStatus=''
	local CurrentPidFile=""
	local CurrentStatus=0
	
	if [ "$(EnabledChilds_IDs | grep -e "^${ChildId}$")" != "" ] ; then ChildStatus="${ChildStatus}E" ; fi
	PidfileStatus "${ChildsPidsDir}/${ChildId}.pid" # 0:Program is running 1:Program is not running and the pid file exists 3:Program is not running 4:Unable to determine program status
	CurrentStatus=$?
	if [ $CurrentStatus -eq 0 ] ; then ChildStatus="${ChildStatus}R" ; fi
	printf '%s\n' "$ChildId [${ChildStatus}]"
}

ChildDetails ()
{
	local ChildId="$1"
	local CurrentChild=""
	local ChildProfile_Available=''
	local ChildProfile_Enabled=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" != "" ] ; then
		if [ "$ChildProfileExtension" != "" ] ; then
			TheExtension=".${ChildProfileExtension}"
		else
			TheExtension=""
		fi
		unset IFS ; for CurrentChild in "$@" ; do
			ChildProfile_Available="${Childs_path_available}/${CurrentChild}${TheExtension}"
			ChildProfile_Enabled="${Childs_path_enabled}/${CurrentChild}${TheExtension}"
			if [ -e "$ChildProfile_Enabled" ] ; then
				printf '%s\n' "$ChildSingularName \"${CurrentChild}\" ${ParO}enabled${ParC} has configuration profile at: $ChildProfile_Available"
			else
				if [ -e "$ChildProfile_Available" ] ; then
					printf '%s\n' "$ChildSingularName \"${CurrentChild}\" ${ParO}disabled${ParC} has configuration profile at: $ChildProfile_Available"
				else
					printf '%s\n'  "${sERROR}E: ${ParO}$(id -un)${ParC} no $ChildSingularName with name \"${CurrentChild}\" found.${fRESET}" 1>&2
					LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
			if [ $LastStatus -eq 0 ] ; then
				Status "$CurrentChild"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				Details_More "$CurrentChild"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			printf '%s\n' ""
		done
	else
		printf '%s\n' "${sERROR}E: $ChildSingularName must be specified.${fRESET}" 1>&2
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Install_precp_Pre ()
# Example: Install_precp_Pre /cdrom/service
# Will be run before stopping/uninstalling previous version (all before installing new program files).
# Useful for old programs migration/uninstall before doing anything about this one.
# WARNING: Package manager cannot invoke this action before this script file is installed. Recommended to copy actions to preinst script or redo them later.
{
	local SoftwareDir="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Install_precp_Post ()
# Example: Install_precp_Post /cdrom/service
# Will be run after stopping/uninstalling previous version and before copying new program files.
# WARNING: Package manager cannot invoke this action before this script file is installed. Recommended to copy actions to preinst script or redo them later.
{
	local SoftwareDir="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Install_postcp_More ()
# Example: Install_postcp_More /cdrom/service
# Will be run once program files are already installed, service registered, and just before re-starting service if it was a live-upgrade.
{
	local SoftwareDir="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0

#[dhlan]
	local HostsBasename=''
	
	if [ -f "$HostsFile" ] ; then
		HostsBasename="$(printf '%s\n' "$HostsFile" | tr -s '/' '\n' | tail -n 1)"	# Problems with a path begun with "-" in old GNU basename versions
		if [ ! -f "${SystemConfigDir}/${HostsBasename}.bak" ] ; then
			mkdir -p "$SystemConfigDir"
			cp -a "$HostsFile" "${SystemConfigDir}/${HostsBasename}.bak"
		fi
	fi
#[/dhlan]
	return $StatusCode
}

Uninstall_predel_More ()
# Will be run after stopping service and before program files removal.
{
	sleep 0
}

Uninstall_Postdel_More ()
# Will be run after program files removal.
{
	sleep 0
}

Uninstall_purge_MorePre ()
# Will be run after Uninstall_Postdel_More and just before removing config files/dirs and logs.
{
	local LastStatus=0
	local StatusCode=0

#[dhlan]
	local HostsBasename=''
	
	if [ -f "$HostsFile" ] ; then
		HostsBasename="$(printf '%s\n' "$HostsFile" | tr -s '/' '\n' | tail -n 1)"	# Problems with a path begun with "-" in old GNU basename versions
		if [ -f "${SystemConfigDir}/${HostsBasename}.bak" ] && [ ! -f "${HostsFile}.bak" ] ; then
			mv "${SystemConfigDir}/${HostsBasename}.bak" "${HostsFile}.bak"
		fi
	fi
#[/dhlan]
	return $StatusCode
}

Uninstall_purge_MorePost ()
# Will be run just after removing config files/dirs and logs.
{
	local LastStatus=0
	local StatusCode=0

#[dhlan]
	if [ "$(cat "$EthersFile1" 2>/dev/null | head -n 1 | grep -e "#.*dhlan")" != "" ] ; then
		printf '%s\n' "Removing: $EthersFile1"
		rm "$EthersFile1"
	fi
	if [ "$(cat "$EthersFile2" 2>/dev/null | head -n 1 | grep -e "#.*dhlan")" != "" ] ; then
		printf '%s\n' "Removing: $EthersFile2"
		rm "$EthersFile2"
	fi
	if [ -d "$HostsCacheDir" ] ; then
		printf '%s\n' "Removing: $HostsCacheDir"
		rm -fr "$HostsCacheDir"
	fi
	if [ -f "$HostsFile" ] ; then
		printf '%s\n' "Not touching: $HostsFile"
	fi
#[/dhlan]
	return $StatusCode
}

ProcessStatus_More ()
# Called in main service report; and for each child if DaemonizeChilds==1 && StatusProcecessShow==1
{
	local AskedPidStatus=$1
	if [ $# -gt 0 ] ; then shift ; fi
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Status_More ()
# Called at end of report for whole service; and after each child report when reporting childs.
{
	local AskedPidStatus=$1
	if [ $# -gt 0 ] ; then shift ; fi
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Details_More ()
# Called at end of each ChildDetails() report. Not to inform about processes.
{
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Stop_Pre ()

# TEMPLATE WARNING: This function is now called also for each Child stop

{
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

Stop_Post ()

# TEMPLATE WARNING: This function is now called also for each Child stop

{
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

Start_Pre ()

# TEMPLATE WARNING: This function is now called also for each Child start

{
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

Start_Post ()
# Start_Pre() and Start_Post() will be the only calls on main start if service is for one-shot (MainControllerStays==0)

# TEMPLATE WARNING: This function is now called also for each Child start

{
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

Configuration_Early_More ()
# To minimize load on fast fork. Convenient to not write to disk.
{
	sleep 0
}

Configuration_Saved_Pre ()
# Preliminar configurations at beginning of Configuration(). Useful to migrate old configurations and data before assuming them for production.
{
	local ReadOnly=0
	if [ "$1" = "ro" ] ; then ReadOnly=1 ; shift ; fi
}

Configuration_Saved_Post ()
# More configurations at end of Configuration()
# Use Configuration_Early_More() for any configuration needed in forking operation.
{
	local ReadOnly=0
	local LastStatus=0
	local StatusCode=0
	if [ "$1" = "ro" ] ; then ReadOnly=1 ; fi
	
	Configuration_Early_More "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#[dhlan]
	local TempText=""

	if [ -f "${SystemConfigDir}/service.conf" ] && [ "$SystemConfigFile" != "${SystemConfigDir}/service.conf" ] ; then
		if [ $(id -u) -eq 0 ] && [ $ReadOnly -eq 0 ] ; then
			printf '%s\n' "" >> "$SystemConfigFile"
			printf '%s\n' "# Configuration migrated from old service.conf" >> "$SystemConfigFile"
			cat "${SystemConfigDir}/service.conf" >> "$SystemConfigFile"
			mv "${SystemConfigDir}/service.conf" "${SystemConfigDir}/service.conf.bak"
		else
			SystemConfigFile="${SystemConfigDir}/service.conf"
		fi
	fi
	EthersFile1="$(GetSetLocalConfig "$ReadOnly" EthersFile1 '' "/etc/ethers" '# EthersFile1: Main MAC-Name table' '')"
	EthersFile2="$(GetSetLocalConfig "$ReadOnly" EthersFile2 '' "${CurrentConfigDir}/ethers" '# EthersFile2: Additional MAC-Name table' '')"
	HostsFile="$(GetSetLocalConfig "$ReadOnly" HostsFile '' "/etc/hosts" '# HostsFile: Table to be updated as /etc/hosts' '')"
	if [ "$EthersFile1" = "$EthersFile2" ] ; then EthersFile2="" ; fi
	if [ ! -f "$HostsFile" ] && [ $ReadOnly -eq 0 ] ; then
		mkdir -p "$(dirname "$HostsFile")" 2>/dev/null
		touch "$HostsFile" 2>/dev/null
		LastStatus=$?
		if [ $LastStatus -eq 0 ] ; then
			printf '%s\n' "# $ServiceName" >> "$HostsFile"
			printf '%s\n' "# $ShortDescription" >> "$HostsFile"
			printf '%s\n' "# This file is updated by ethers availability." >> "$HostsFile"
			printf '%s\n' "# Note: If one name is used for different MACs, last available IP will be parsed." >> "$HostsFile"
			printf '%s\n' "" >> "$HostsFile"
			printf '%s\n' "127.0.0.1	localhost" >> "$HostsFile"
			chmod u=rw,go=r "$HostsFile"
		fi
	fi
	if [ ! -f "$EthersFile1" ] && [ $ReadOnly -eq 0 ] ; then
		mkdir -p "$(dirname "$EthersFile1")" 2>/dev/null
		touch "$EthersFile1" 2>/dev/null
		LastStatus=$?
		if [ $LastStatus -eq 0 ] ; then
			printf '%s\n' "# $ServiceName" >> "$EthersFile1"
			printf '%s\n' "# $ShortDescription" >> "$EthersFile1"
			printf '%s\n' "# This file is automatically reloaded every WaitInterval." >> "$EthersFile1"
			printf '%s\n' "# Note: If one name is used for different MACs, only last available IP will be parsed." >> "$EthersFile1"
			printf '%s\n' "" >> "$EthersFile1"
			printf '%s\n' "#FF:00:FF:AB:CD:01	desktop1.example.lan" >> "$EthersFile1"
			printf '%s\n' "#FF:00:FF:AB:CD:02	printer1.example.lan" >> "$EthersFile1"
			printf '%s\n' "#FF:00:FF:AB:CD:03	router1.example.lan" >> "$EthersFile1"
			chmod u=rw,go=r "$EthersFile1"
		fi
	fi
	if [ -f /etc/dhlan/hosts ] && [ $ReadOnly -eq 0 ] ; then
		touch "$EthersFile1" 2>/dev/null
		LastStatus=$?
		if [ $LastStatus -eq 0 ] ; then
			TempText="$(cat /etc/dhlan/hosts | grep -ve '^$')"
			rm /etc/dhlan/hosts 2>/dev/null
			LastStatus=$?
			if [ $LastStatus -eq 0 ] ; then
				printf '%s\n' "" >> "$EthersFile1"
				printf '%s\n' "# RECOVERED CONTENT FROM OLD /etc/dhlan/hosts" >> "$EthersFile1"
				printf '%s\n' "$TempText" >> "$EthersFile1"
			else
				printf '%s\n' "W: Cannot move content from old /etc/dhlan/hosts" 1>&2
			fi
		else
			printf '%s\n' "W: Cannot create file $EthersFile1" 1>&2
		fi
	fi
	if [ ! -f "$EthersFile2" ] && [ "$EthersFile2" != "" ] && [ $ReadOnly -eq 0 ] ; then
		mkdir -p "$(dirname "$EthersFile2")" 2>/dev/null
		touch "$EthersFile2" 2>/dev/null
		LastStatus=$?
		if [ $LastStatus -eq 0 ] ; then
			printf '%s\n' "# $ServiceName" >> "$EthersFile2"
			printf '%s\n' "# $ShortDescription" >> "$EthersFile2"
			printf '%s\n' "# This file is automatically reloaded every WaitInterval." >> "$EthersFile2"
			printf '%s\n' "# Note: If one name is used for different MACs, only last available IP will be parsed." >> "$EthersFile2"
			printf '%s\n' "" >> "$EthersFile2"
			printf '%s\n' "#FF:00:FF:AB:CD:04	desktop2.example.lan" >> "$EthersFile2"
			printf '%s\n' "#FF:00:FF:AB:CD:05	printer2.example.lan" >> "$EthersFile2"
			printf '%s\n' "#FF:00:FF:AB:CD:06	router2.example.lan" >> "$EthersFile2"
			chmod u=rw,go=r "$EthersFile2"
		fi
	fi
	WaitInterval="$(GetSetLocalConfig "$ReadOnly" WaitInterval '' 60 '# WaitInterval: Sleep time in seconds, to reload dhlan hosts and check if some host or availability has changed.' '')"
	MinScanMaskBits="$(GetSetLocalConfig "$ReadOnly" MinScanMaskBits '' 24 '# MinScanMaskBits: Minimum bits to mask the IP range when scanning network; set less bits for slower scan but wider nets.' '')"
	KeepLastAddresses="$(GetSetLocalConfig "$ReadOnly" KeepLastAddresses '' 1 '# KeepLastAddresses: 0 to clean host entries when IP are not reachable. 1 to not remove those entries.' '')"
	MultipleMacIpBehavior="$(GetSetLocalConfig "$ReadOnly" MultipleMacIpBehavior '' all "# MultipleMacIpBehavior: What hosts to register when more than one IP for same MAC is found ${ParO}all|first|last${ParC}." '')"	#do
	MultipleMacIpBehavior="$(printf '%s\n' "$MultipleMacIpBehavior" | tr "[:upper:]" "[:lower:]")"
	MultipleNameIpBehavior="$(GetSetLocalConfig "$ReadOnly" MultipleNameIpBehavior '' all "# MultipleNameIpBehavior: What hosts to register when more than one IP for same name is found ${ParO}all|first|last${ParC}." '')"	#do
	MultipleNameIpBehavior="$(printf '%s\n' "$MultipleNameIpBehavior" | tr "[:upper:]" "[:lower:]")"
	if [ $(id -u) -eq 0 ] ; then
		HostsCacheDir="$(GetSetLocalConfig "$ReadOnly" HostsCacheDir '' "/var/cache/hosts-os" '# HostsCacheDir: Directory to save a backup of detected remote data.' '')"
	else
		HostsCacheDir="$(GetSetLocalConfig "$ReadOnly" HostsCacheDir '' "${UserConfigDir}/hosts-os" '# HostsCacheDir: Directory to save a backup of detected remote data.' '')"
	fi
	Dns="$(GetSetLocalConfig "$ReadOnly" Dns '' '"94.247.43.254"' '\n# Dns: Single IP address of external service to test.')"
#[/dhlan]
	return $StatusCode
}

CreateChild ()
{
	sleep 0
}

MainController ()
# If service is for one-shot (MainControllerStays==0), is better to put Main-kernel code at Start_Post() instead; childs are loaded here anyway.
{
	local MaxChildLoopsNr="$1"  # -1 for unlimited (default)
	local DebugLog='/dev/null'
	local LastStatus=0
	local StatusCode=0
#[dhlan]
	local EthersRecords=""
	local CurrentEther=""
	local CurrentName=""
	local CurrentMAC=""
	local CurrentIPs=""
	local CurrentIP=""
	local NewNamesIpsStatus=""
	local ComparableNewHosts=''
	local HostsLinesTmp=''
	local NewMacsIpsStatus=""
	local OldNamesIpsStatus=""
	local ComparableOldHosts=''
	local PreviousEthersRecords="00"
	local UpdateResultTxt=''
	local CurrentHost=''
	local NameRegexp=''
	local AllNames=''
#[/dhlan]
	
	if [ $LogLevel -ge 4 ] ; then DebugLog="$MainControllerLog" ; fi
	Start_Pre "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ "$ChildsPluralName" != "" ] ; then
		LoadChilds 1 "$MaxChildLoopsNr"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $Childs_MoreLoadsInterval -gt 0 ] && [ "$MainControllerStays" != "0" ] ; then
			printf '%s\n' "Begin to control enabled $ChildsPluralName in intervals of $Childs_MoreLoadsInterval seconds." | tee -a "$DebugLog"
			while [ ! -f "$MainControllerStopFile" ] ; do
				# This loop will be the main task
				WaitStoppable $Childs_MoreLoadsInterval "$MainControllerStopFile" 1
				LoadChilds 0 "$MaxChildLoopsNr"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			done
		fi
	fi
	if [ "$ChildsPluralName" = "" ] && [ "$MainControllerStays" != "0" ] ; then
		# Traditional main loop if not previous one.
#[dhlan]
		printf '%s\n' "Maintaining hosts file: $HostsFile"
		printf '%s\n' "by watching following ethers files in ${WaitInterval}s intervals:"
		printf '%s\n' "$EthersFile1"
		printf '%s\n' "$EthersFile2"
		printf '%s\n' ""
#[/dhlan]
		if [ -f "$MainControllerStopFile" ] ; then LogProgram 2 "W: File $MainControllerStopFile avoids main bucle." ; fi
		while [ ! -f "$MainControllerStopFile" ] ; do
#[dhlan]
#			printf '%s\n' "main task not yet developed." | tee -a "$DebugLog" 1>&2
#			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			WaitStoppable 10 "$MainControllerStopFile" 0.5
			EthersRecords=""
			OldNamesIpsStatus=""
			NewNamesIpsStatus=""
			NewMacsIpsStatus=""
			EthersRecords="$(printf '%s\n' "$EthersRecords" ; cat "$EthersFile1" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's| $||g')"
			EthersRecords="$(printf '%s\n' "$EthersRecords" ; cat "$EthersFile2" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's| $||g')"
			EthersRecords="$(printf '%s\n' "$EthersRecords" | grep -E '^..:..:..:..:..:.. ..*' | cut -f 1,2 -d ' ' | sort)"
			if [ "$EthersRecords" != "$PreviousEthersRecords" ] ; then
				if [ "$EthersRecords" = "" ] ; then
					LogProgram 3 "Controlling now 0 ethers entries."
				else
					LogProgram 3 "$(printf '%s\n' "Controlling now $(echo $(printf '%s\n' "$EthersRecords" | wc -l)) ethers entries:" ; printf '%s\n' "$EthersRecords")"
				fi
			fi
			IFS="$(printf '\n\b')" ; for CurrentEther in $EthersRecords ; do unset IFS
				if [ ! -f "$MainControllerStopFile" ] ; then
					CurrentMAC="$(printf '%s\n' "$CurrentEther" | cut -f 1 -d ' ')"
					CurrentName="$(printf '%s\n' "$CurrentEther" | cut -sf 2 -d ' ')"
					if [ "$CurrentName" != "" ] ; then
						OldNamesIpsStatus="$(printf '%s\n' "$OldNamesIpsStatus" ; HostsfileStatusForName "$HostsFile" "$CurrentName")"
						CurrentIPs="$(IPDeMAC "$CurrentMAC" "$MinScanMaskBits" 1)"
						if [ "$MultipleMacIpBehavior" = "first" ] ; then
							# What hosts to register when more than one IP for same MAC is found
							CurrentIPs="$(printf '%s\n' "$CurrentIPs" | head -n 1)"
						else
							if [ "$MultipleMacIpBehavior" = "last" ] ; then
								CurrentIPs="$(printf '%s\n' "$CurrentIPs" | tail -n 1)"
							fi
							# Default: all
						fi
						for CurrentIP in $CurrentIPs ; do
							NewNamesIpsStatus="$(printf '%s\n' "$NewNamesIpsStatus" ; printf '%s\n' "$CurrentIP $CurrentName")"
						done
					else
						LogProgram 2 "W: Entry with MAC $CurrentMAC has no name to assign an IP."
					fi
				fi
			done
			OldNamesIpsStatus="$(printf '%s\n' "$OldNamesIpsStatus" | grep -ve '^$')"
			ComparableOldHosts="$(printf '%s\n' "$OldNamesIpsStatus" | sort)"
			NewNamesIpsStatus="$(printf '%s\n' "$NewNamesIpsStatus" | grep -ve '^$')"
			if [ "$KeepLastAddresses" != "0" ] ; then
				# Clean or keep host entries when IP are not reachable: Add lost entries.
				HostsLinesTmp="$NewNamesIpsStatus"
				AllNames="$(printf '%s\n' "$EthersRecords" | cut -sf 2 -d ' ' | sort -u)"
				for CurrentName in $AllNames ; do
					NameRegexp="$(printf '%s\n' "$CurrentName" | sed -e 's|\.|\\.|g')"
					if [ "$(printf '%s\n' "$NewNamesIpsStatus" | grep -e " ${NameRegexp}$")" = "" ] && [ "$(printf '%s\n' "$OldNamesIpsStatus" | grep -e " ${NameRegexp}$")" != "" ] ; then
						# Name has not been resolved on new detection, but it was present before
						LogProgram 4 "$(printf '%s\n' "$OldNamesIpsStatus" | grep -e " ${NameRegexp}$" | sed -e 's| | for name |g' | sed -e 's|^|Keeping IP |g')"
						HostsLinesTmp="$(printf '%s\n' "$OldNamesIpsStatus" | grep -e " ${NameRegexp}$" ; printf '%s\n' "$HostsLinesTmp")"
					fi
				done
				NewNamesIpsStatus="$(printf '%s\n' "$HostsLinesTmp" | grep -ve '^$')"
			fi
			if [ "$MultipleNameIpBehavior" = "first" ] || [ "$MultipleNameIpBehavior" = "last" ] ; then
				# What hosts to register when more than one IP for same name is found
				HostsLinesTmp=''
				IFS="$(printf '\n\b')" ; for CurrentHost in $NewNamesIpsStatus ; do unset IFS
					CurrentIP="$(printf '%s\n' "$CurrentHost" | cut -f 1 -d ' ')"
					CurrentName="$(printf '%s\n' "$CurrentHost" | cut -sf 2 -d ' ')"
					NameRegexp="$(printf '%s\n' "$CurrentName" | sed -e 's|\.|\\.|g')"
					if [ "$(printf '%s\n' "$HostsLinesTmp" | grep -e " ${NameRegexp}$")" = "" ] ; then
						HostsLinesTmp="$(printf '%s\n' "$HostsLinesTmp" ; printf '%s\n' "$CurrentHost")"
					else
						if [ "$MultipleNameIpBehavior" = "last" ] ; then
							HostsLinesTmp="$(HostsWithoutName "$CurrentName" "$HostsLinesTmp")"
							HostsLinesTmp="$(printf '%s\n' "$HostsLinesTmp" ; printf '%s\n' "$CurrentHost")"
						fi
					fi
				done
				NewNamesIpsStatus="$(printf '%s\n' "$HostsLinesTmp" | grep -ve '^$')"
			fi
			ComparableNewHosts="$(printf '%s\n' "$NewNamesIpsStatus" | sort)"
			if [ ! -f "$MainControllerStopFile" ] && [ "$ComparableNewHosts" != "$ComparableOldHosts" ] ; then
				UpdateHostsFile "$HostsFile" "$OldNamesIpsStatus" "$NewNamesIpsStatus"
				StatusCode=$?	# Last result will be last exitcode
			fi
			WaitStoppable $WaitInterval "$MainControllerStopFile"
			PreviousEthersRecords="$EthersRecords"
#[/dhlan]
		done
	fi
	Start_Post "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}


##### MAIN SCRIPT #####

LastStatus=0
StatusCode=0
if [ $(id -u) -eq 0 ] && [ "$USER" != "root" ] && [ "$USER" != "" ] && [ "$(env | grep -ie LXC -ie '^container=')" != "" ] ; then
	# Patch to not legate some LXC unprivileged variables on lxc-attach/calls
	export USER=root ; export HOME=/root ; export LOGNAME=root
	if [ -f /etc/default/locale ] ; then
		IFS="$(printf '\n\b')" ; for CurVar in $(cat /etc/default/locale | cut -f 1 -d '#' | grep -e '=') ; do unset IFS
			eval export $CurVar
		done
	fi
fi
if [ -r /lib/lsb/init-functions ] ; then
	. /lib/lsb/init-functions
fi
MeCallFile="$0"
PreviousDir="$(pwd)"
MeDir="$(Dirname "$MeCallFile")"
cd "$MeDir"
MeDir="$(pwd)"
cd "$PreviousDir"
MeCallFile="${MeDir}/$(printf '%s\n' "$MeCallFile" | tr -s '/' '\n' | tail -n 1)"
cd / # avoid blocking mount points from being unmounted
if [ "$LogLevel" = "" ] ; then LogLevel=3 ; fi	# A nested call can export LogLevel
LogProgram 4 '$' "$0" "$@"
Action="$1"
if [ $# -gt 0 ] ; then shift ; fi

# Uppercase actions allow passthrough init manager parsers (eg. systemctl, service)
Action="$(Lowercase "$Action")"
LowerChildSingularName="$(Lowercase "$ChildSingularName")"
LowerChildsPluralName="$(Lowercase "$ChildsPluralName")"
FastConfiguration=0
case "$Action" in
	"remove" ) Action="uninstall" ; ActionMode="remove" ;;
	"purge" ) Action="uninstall" ; ActionMode="purge" ;;
	"force-reload" ) Action="reload" ;;
	"-h" ) Action="--help" ;;
	"-V" ) Action="--version" ;;
	"$LowerChildSingularName" ) if [ "$ChildSingularName" != "" ] ; then Action="child-foreground" ; fi ;;
	"${LowerChildSingularName}-foreground" ) if [ "$ChildSingularName" != "" ] ; then Action="child-foreground" ; fi ;;
	"$LowerChildsPluralName" ) if [ "$ChildsPluralName" != "" ] ; then Action="childs-foreground" ; fi ;;
	"${LowerChildsPluralName}-foreground" ) if [ "$ChildsPluralName" != "" ] ; then Action="childs-foreground" ; fi ;;
	"child" ) if [ "$ChildSingularName" = "" ] ; then Action="'${Action}'" ; else Action="child-foreground" ; fi ;;
	"child-foreground" ) if [ "$ChildSingularName" = "" ] ; then Action="'${Action}'" ; fi ;;
	"create" ) if [ "$ChildSingularName" = "" ] ; then Action="'${Action}'" ; fi ;;
	"delete" ) if [ "$ChildSingularName" = "" ] ; then Action="'${Action}'" ; fi ;;
	"ls" ) Action='list' ; if [ "$ChildSingularName" = "" ] ; then Action="'${Action}'" ; fi ;;
	"list" ) if [ "$ChildSingularName" = "" ] ; then Action="'${Action}'" ; fi ;;
	"details" ) if [ "$ChildSingularName" = "" ] ; then Action="'${Action}'" ; fi ;;
#[dhlan]
	"explore" ) Action="scan" ;;
	"hw" ) Action="hardware" ;;
	"local" ) Action="hardware" ;;
	"wake" ) Action="wakeonlan" ;;
	"wakeup" ) Action="wakeonlan" ;;
	"wol" ) Action="wakeonlan" ;;
	"WoL" ) Action="wakeonlan" ;;
#[/dhlan]
esac
case "$Action" in
	"main" )  # For a foreground execution, use 'start-foreground' action
		if [ "$INIT_SCRIPT_StartForked" = "1" ] ; then LogProgram 4 "I: Forked process for start and load" ; fi
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			MainController "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"childs-foreground" )
		# Everything foreground and sequential
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			DaemonizeChilds=0
			MainController "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"child-foreground" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			ChildController "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"create" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			CreateChild "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"delete" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			DeleteChild "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"list" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			ListChilds "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"report" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			ReportChilds "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"details" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			ChildDetails "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"enable" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			EnableObject 1 "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"disable" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			DisableObject 1 "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"start" )
		if [ "$INIT_SCRIPT_StartForked" != "1" ] ; then
			Configuration "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ "$INIT_SCRIPT_InitCall" != "0" ] ; then rm -f "$MainControllerStopFile" ; fi
			if [ $(id -u) -eq 0 ] && [ "$INIT_SCRIPT_InitCall" = "0" ] && [ "$InitToolForStartStop" != "" ] && SystemProgramIsEnabled ; then
				printf '%s\n' "${sWARN}W: It's better for init system to use its program to start services:" 1>&2
				printf '%s\n' "   $(RecommendedInvocation start "$@")${fRESET}" 1>&2	#"
				if [ "$MainControllerStays" = "0" ] && [ "$DaemonizeOnStart" != "0" ] ; then
					printf '%s\n' "Launching main process foreground." 1>&2
					DaemonizeOnStart=0
				fi
			fi
			if [ $# -eq 1 ] ; then
				# Don't wait for system descending load when an explicit single child is launched
				Child_LoadWithCalm=-1
			fi
			if [ $StatusCode -eq 0 ] ; then
				Start "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			# Fork inmediately to not delay Init wait
			FastConfiguration=1
			Configuration "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ "$INIT_SCRIPT_InitCall" != "0" ] ; then rm -f "$MainControllerStopFile" ; fi
			LogProgram 4 "I: Early forking for start and load"
			export INIT_SCRIPT_InitCall=$INIT_SCRIPT_InitCall
			export INIT_SCRIPT_StartForked=1
			DaemonizeCommand "$MainControllerPidFile" "$MeCallFile" main "$@"
		fi
		;;
	"start-foreground" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$1" != "@" ] ; then
				DaemonizeOnStart=0
				Start "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				# Workaround for Systemd bug https://github.com/systemd/systemd/pull/4992 (@ = no instance)
				if [ "$DaemonizeChilds" = "1" ] ; then
					CurrentStopFile="${ChildsPidsDir}/@.stop"
					while [ ! -f "$CurrentStopFile" ] ; do
						sleep 2
					done
				fi
			fi
		fi
		;;
	"stop" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $(id -u) -eq 0 ] && [ $INIT_SCRIPT_InitCall -eq 0 ] && [ "$InitToolForStartStop" != "" ] && SystemProgramIsEnabled ; then
			printf '%s\n' "${sWARN}W: It's better for init system to use its program to stop services:" 1>&2
			printf '%s\n' "   $(RecommendedInvocation stop "$@")${fRESET}" 1>&2	#"
		fi
		Stop "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"restart" )
		Configuration "$@"
		if [ $(id -u) -eq 0 ] && [ $INIT_SCRIPT_InitCall -eq 0 ] && [ "$InitToolForStartStop" != "" ] && SystemProgramIsEnabled ; then
			printf '%s\n' "${sWARN}W: It's better for init system to use its program to restart services:" 1>&2
			printf '%s\n' "   $(RecommendedInvocation restart "$@")${fRESET}" 1>&2	#"
		fi
		if [ $StatusCode -eq 0 ] ; then
			RestartCall=1
			Stop "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			sleep 1
			Start "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"reload" )
		if [ "$1" != "@" ] ; then				# Workaround for Systemd bug https://github.com/systemd/systemd/pull/4992 (@ = no instance)
			Configuration "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				if [ "$ChildsPluralName" != "" ] ; then
					ReloadChilds 1
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					"$MeExecutable" restart "$@"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
		;;
	"status" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Status "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"install" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "" ] ; then
			if [ "$ProgramInstaller" != "1" ] ; then
				LastStatus=93 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				ServiceHelp 1>&2
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				LackDependencies="$(DependenciasFaltan "$DependsOnSoftware")"	#"
				if [ "$LackDependencies" != "" ] ; then
					printf '%s\n' "${sERROR}E: Following software must be installed before this dependent service:" 1>&2
					printf '%s\n' "   ${LackDependencies}${fRESET}" 1>&2
					LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				LackDependencies="$(DependenciasFaltan "$RecommendedSoftware")"	#"
				if [ "$LackDependencies" != "" ] ; then
					printf '%s\n' "${sWARN}W: Following software is also recommended for this service:" 1>&2
					printf '%s\n' "   ${LackDependencies}${fRESET}" 1>&2
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ $(id -g) -ne 0 ] ; then
			printf '%s\n' "${sERROR}E: Install actions need to be run with superuser ${ParO}root${ParC} permissions.${fRESET}" 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			LogProgram 3 "Installing $ServiceName $(ServiceVersion)"
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "precp" ] || [ "$PackageManager_Call" = "whole" ] ; then
				# WARNING: Package manager cannot invoke this action before this script file is installed. Recommended to copy actions to preinst script.
				Install_precp "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "cp" ] || [ "$PackageManager_Call" = "whole" ] ; then
#				printf '%s\n' "Installing $ServiceName"
				Install_cp "$MeExecutable" "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "postcp" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Install_postcp "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					if ! SystemProgramIsEnabled ; then
						printf '%s\n' ""
						printf '%s\n' "${sWARN}W: $ServiceName will not start as system service until you run: $(RecommendedInvocation enable)${fRESET}" 1>&2
					fi
					if [ "$PackageManager_Call" = "" ] ; then
						printf '%s\n' ""
						printf '%s\n' "For more options, run: ${ServiceName} --help"
					fi
				fi
			fi
		fi
		;;
	"uninstall" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ "$ActionMode" = "remove" ] ; then
			if [ "$ProgramInstaller" = "1" ] || [ "$PackageManager_Call" != "" ] ; then
				printf '%s\n' "${sWARN}W: To uninstall $ServiceName you must use the action \"uninstall\"${fRESET}" 1>&2
				LastStatus=80 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ "$ChildSingularName" != "" ] ; then
				printf '%s\n' "${sWARN}W: To erase a $ChildSingularName you must use the action \"delete\"${fRESET}" 1>&2
				LastStatus=80 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				ServiceHelp 1>&2
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "" ] ; then
			if [ "$ProgramInstaller" != "1" ] ; then
				LastStatus=93 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				ServiceHelp 1>&2
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ $(id -g) -ne 0 ] ; then
			printf '%s\n' "${sERROR}E: Uninstall actions need to be run with superuser ${ParO}root${ParC} permissions.${fRESET}" 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			LogProgram 3 "Uninstalling $ServiceName $(ServiceVersion)"
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "predel" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Uninstall_predel
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "predel" ] && [ -f "$MeExecutable" ] ; then
					cp "$MeExecutable" "/var/tmp/${ServiceName}.uninstall.tmp"
					cp "$MeExecutable" "${DirTempX}/${ServiceName}.uninstall.tmp"	# Compatibility with old package script: postrm
					chmod u=rx,g=r,o= "/var/tmp/${ServiceName}.uninstall.tmp"
					chmod u=rx,g=r,o= "${DirTempX}/${ServiceName}.uninstall.tmp"
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "del" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Uninstall_del
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "postdel" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Uninstall_postdel
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ "$ActionMode" = "purge" ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "purge" ] ; then
				Uninstall_purge
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		;;
	"debug" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		export LogLevel=4
		"$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"--version" )
		Configuration ro "$@"
		printf '%s\n' "$ServiceName $(ServiceVersion)"
		ServiceCopyright
		printf '%s\n' "$ServiceDocumentation"
		TheTemplateVersion="$(TemplateVersion)"
		if [ "$TheTemplateVersion" != "" ] ; then
			printf '%s\n' "Init script $(TemplateVersion)"
		fi
		TemplateCopyright
		;;
	"--help" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ServiceHelp
		;;
#[dhlan]
	"scan" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ReportNetworkHosts "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"hardware" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ReportLocalHardware "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"mactoip" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		IPDeMAC "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"wakeonlan" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		WakeOnLan "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"nic-monitor" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		NicMonitor "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"remote-monitor" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		RemoteMonitor "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
#[/dhlan]
	"" )
		printf '%s\n' "${sERROR}E: No action specified.${fRESET}" 1>&2
		LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ServiceHelp 1>&2
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	* )
		printf '%s\n' "${sERROR}E: Syntax error; Unknown action ${Action}${fRESET}" 1>&2
		LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ServiceHelp 1>&2
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
esac
CleanTempFiles
if [ "$(cat "$MainControllerPidFile" 2>/dev/null | sed -e 's| ||g')" = "$$" ] && [ "$DaemonizeOnStart" != "1" ] ; then
	rm "$MainControllerPidFile"
fi
cd "$PreviousDir" 2>/dev/null
if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
exit $StatusCode
