#!/bin/sh
### BEGIN INIT INFO
# Provides:	initcontainers
# 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:	VPS instances launcher
# Description:		Starts OpenVZ/LXC containers sequentially
# X-Interactive: false
### END INIT INFO
# RHL fields:
# chkconfig: 2345 80 30
# description: VPS instances launcher

# Service version: 1.4.10
# Copyright: (GNU GPL) 2018-2024 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.6.18
# Copyright: (GNU GPL) 2014-2024 Narcis Garcia
# Compatible with init systems: System-V since y.2000, upstart, systemd

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

# Service development ToDo:
#	- Register some cron.hourly task to monitor VPS health and restart them if are frozen or overloading.
#	- Configurable NotifyTo parameter to send an e-mail on start completion.
#	- Unify SequenceList creation in a single function.
#	- Stops_LXC: On wildcards line, all matching containers should be selected to fork parallel stop tasks.
#	- Wildcards for LXC in containers.list should restrict to only VPS with onboot:yes
#	- Implement "+" prefix to type (+ovz +lxc +chroot) to indicate forking of start/stop task instead of serialize it.
#	- "tosequence" action to 'move' all vzctl-enabled containers to initcontainers service +onboot:no
#	- "toparallel" action to 'move' all initcontainers containers to vzctl-enabled +onboot:yes
#	- Implement "rm -f /var/lib/lxc/$CTID/lxc.log" and logrotate.
#	- Implement parallel containers launch for LXC
#	- Merge to ctctl


##### 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="initcontainers"
# 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"
RecommendedSoftware=""
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=0
# StopStartOnUpgrade: If old service process is to be stopped when upgrading program (and after started again if it was running)
StopStartOnUpgrade=0
# RootRequired: Prevent to run without superuser permissions? (1=Yes 0=No). This also determines program FHS location (sbin/ or bin/).
RootRequired=1
# GentleStopTimeoutS: Maximum seconds to wait daemons at stop signal, before forcing kill processes. Intended for childs and {main x ChildsNr}
GentleStopTimeoutS=300
# 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=-1
# 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=""
# 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=0

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

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"
	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)"
	fREVERSEC="$(tput rev)"
	fLOW="$(tput dim)"
	fUNDERL="$(tput smul)"
	fUNDERLx="$(tput rmul)"
#	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}"
	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=''
	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"
# 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
			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
			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 an existing 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')"
	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
}

ExecuteString ()
# Description: Writes specified parameters to a shell script, and runs the script.
# Notes: 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
}

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
				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
{
	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
			DatetimeStamp="$(date '+%Y-%m-%dT%T%z') "
		fi
		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
{
	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
		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
			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
}

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
#	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=','
		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
	# 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"
}

DependenciasFaltan ()
# Sintaxis como función: $(DependenciasFaltan "$ListaDependencias")
# 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:
#	- 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=''
	
	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=''
		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' >> "$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' >> "$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' >> "$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' >> "$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
	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
#	$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
	return $StatusCode
}

LoadVarsValues ()
# Syntax as a sentence: LoadVarsValues $FileOrContent $VariablesNames $VarsRequired $DefaultsFileOrContent
# Descripcion: Runs 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.
#	$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)
#	$3	(optional or empty) Variable names to filter (case sensitive, space separated). If not specified, all file variables will be loaded.
#	$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
		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
				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
}

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)
#		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:
#	- No headers are included
#	- Columns are separated by only one space character
#	- Every unknown value is returned as '?'
# Depends on functions: Is_IntegerNr
# Depends on software packages: grep sed
{
	local OutputColumns="$1"
	local FilterPids="$2"
	local CurValue=''
	local CurPid=''
	local CurColumn=''
	local CurLine=''
	local PidMaxLen=0
	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
		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
			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')"
		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')"
				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.
						* )
							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
}

ParentPstree ()
# Alternative to pstree -p -s (syntax not available in 1990's or 2000's distributions)
# Depends on functions: Is_IntegerNr PsOutputValues
# Depends on software packages: sed, procps
{
	local ChildPid="$1"
	local CurrentPid=''
	local Value=''
	
	CurrentPid=$ChildPid
	while Is_IntegerNr "$CurrentPid" && [ "$CurrentPid" != "0" ] ; do
		Value="$CurrentPid $Value"
		CurrentPid=$(echo TrimAndSingle $(PsOutputValues ppid $CurrentPid | tail -n 1) | 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
}

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] [$IOWait_Limit2]
# 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 or 0) 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
#	$5	(optional or .) Independent percent for additional to solely IOWait
#	
# 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 IOWait_Limit2="$5"
	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
	if ! Is_IntegerNr "$IOWait_Limit2" ; then IOWait_Limit2=$MaxPercent ; 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 ] || [ $IOWait_Cur -gt $IOWait_Limit2 ] || [ $IOWait_Pre -gt $IOWait_Limit2 ] ; then
			Calm=0
		fi
		if [ $Load1 -le 0 ] && [ $IOWait_Cur -le 0 ] ; then
			Calm=1
		fi
		LastLoad1=$Load1
		OldLoad1=$LastLoad1
	done
}

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, pstree/psmisc
{
	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 [ "$(pstree $PID)" != "" ] ; 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, pstree/psmisc
{
	local CurrentPid=''
	local Value=''
	
	for CurrentPid in $* ; do
		if [ "$(pstree $CurrentPid)" != "" ] ; 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 -p $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=''
		for CurrentPid in $TempValue ; do
			if Is_IntegerNr $CurrentPid ; then PidTree="$PidTree $CurrentPid" ; fi
		done
		NextPidTree=''
		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)"
		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
	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 -p $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=''
			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 -p $MainPid)" != "" ] && [ $TimeoutCount -gt 0 ] ; do
				sleep 1
				TimeoutCount=$((TimeoutCount - 1))
			done
			PidTree="$(ExistingPIDs $PidTree)"
			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

	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, pstree/psmisc
{
	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
			# 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
	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 DaemonizeCommand StopPidFile PidfileStatus
# 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
		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
	return $StatusCode
}


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

LogService_DEPRECATED ()
# Syntax as a sentence: LogService $ThisLevel $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.
# Depends on functions: Dirname
# Depends on software packages: (none)
# Depends on environment variables: LogLevel MainControllerLog INIT_SCRIPT_InitCall
{
	local ThisLevel="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local CallType=''
	local DatetimeStamp=''
	local AskedText=''
	local CurString=''
	
	if [ $ThisLevel -ge 0 ] ; then
		DatetimeStamp="$(printf '%(%Y-%m-%dT%T%z)T')"
	fi
	ThisLevel="$(printf '%s\n' "$ThisLevel" | sed -e 's|^-||g')"
	if [ "$*" != "" ] && [ $LogLevel -ge $ThisLevel ] && [ $ThisLevel -gt 0 ] ; then
		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 [ $INIT_SCRIPT_InitCall -ne 1 ] ; then
			CallType=" ${ParO} Manual ${ParC}"
		fi
		if [ $ThisLevel -eq 3 ] ; then
			printf '%s\n' "$AskedText"
		else
			if [ $ThisLevel -eq 2 ] ; then
				printf '%s\n' "${sWARN}${AskedText}${fRESET}" 1>&2
			else
				if [ $ThisLevel -eq 1 ] ; then
					printf '%s\n' "${sERROR}${AskedText}${fRESET}" 1>&2
				else
					printf '%s\n' "$AskedText" 1>&2
				fi
			fi
		fi
		if [ "$MainControllerLog" != "" ] ; then
			mkdir -p "$(Dirname "$MainControllerLog")"
			printf '%s\n' "${DatetimeStamp}${CallType} $AskedText" >> "$MainControllerLog"
		fi
	fi
}

LogService_Begin ()
# Syntax as a sentence: LogService_Begin $Message
{
	local Message="$1"
	local CallType=""
	
	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
	mkdir -p "$(Dirname "$MainControllerLog")"
#	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=""
}

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 ()
{
	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 ",${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
		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')"
	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')"
	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')"
	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

	
	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')"
		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.
				printf '%s\n' "ExecStart=-$ProgramExecutablePath start${ChildParameter}" >> "$InitProfileFile"
				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')"
				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 ()
# 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
							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
							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
							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 ()
{
	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 LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" = "" ] ; then
		PidFile="$MainControllerPidFile"
		StopFile="$MainControllerStopFile"
		ProcessName="$ShortDescription"
		if [ ! -d "$(Dirname "$MainControllerPidFile")" ] ; then
			mkdir -p "$(Dirname "$MainControllerPidFile")"
			chmod u=rwX,g=rX,o= "$(Dirname "$MainControllerPidFile")"
		fi
	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
		if [ ! -d "$ChildsPidsDir" ] ; then
			mkdir -p "$ChildsPidsDir"
			chmod u=rwX,g=rX,o= "$ChildsPidsDir"
		fi
	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 10
						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
		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=""
		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
		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 LastStatus=0
	local StatusCode=0

	if [ "$DaemonName" = "" ] ; then DaemonName="program" ; fi
	if [ ! -d "$(Dirname "$PidFile")" ] ; then
		mkdir -p "$(Dirname "$PidFile")"
		chmod u=rwX,g=rX,o= "$(Dirname "$PidFile")"
	fi
	if [ "$StopFile" != "" ] ; then
		if [ ! -d "$(Dirname "$StopFile")" ] ; then
			mkdir -p "$(Dirname "$StopFile")"
			chmod u=rwX,g=rX,o= "$(Dirname "$StopFile")"
		fi
#		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 LastStatus=0
	local StatusCode=0

	if [ "$ChildsPluralName" != "" ] && [ $# -gt 0 ] ; then
		# Only stop specified childs - No matter if they are daemons or foregrounds
		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
		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
				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
					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 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
					CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
					CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
#					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
		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
		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
		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=''
	local SpecificActions=''
	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 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:')" = "" ] ; 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
				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 "" $(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 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 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 ] ; 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}."*
	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 [ ! -d "$SystemConfigDir" ] && [ $ReadOnly -eq 0 ] && [ -w "$(Dirname "$SystemConfigDir")" ] ; then
		mkdir -p "$SystemConfigDir"
		chmod u=rwX,go=rX "$SystemConfigDir"
	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 [ "$(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 ] && [ ! -d "$UserConfigDir" ] ; then
		mkdir -p "$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 [ ! -d "$UserConfigDir" ] && [ "$RootRequired" != "1" ] && [ $ReadOnly -eq 0 ] ; then
			mkdir -p "$UserConfigDir"
			chmod u=rwX,g=rX,o= "$UserConfigDir"
		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 [ ! -d "$UserTempDir" ] && [ $ReadOnly -eq 0 ] ; then
		mkdir -p "$UserTempDir"
		chmod u=rwX,g=rX,o= "$UserTempDir"
	fi
	UserTempDirX="${DirTempX}/${ServiceName}.$(id -u)"
	if [ ! -d "$UserTempDirX" ] && [ $ReadOnly -eq 0 ] ; then
		mkdir -p "$UserTempDirX"
		chmod u=rwX,g=rX,o= "$UserTempDirX"
	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
			mkdir -p "$ChildsTempDir"
		fi
		Childs_path_available="${CurrentConfigDir}/${ChildsPluralName}_available"
		if [ ! -d "$Childs_path_available" ] && [ $ReadOnly -eq 0 ] ; then
			mkdir -p "$Childs_path_available"
			chmod u=rwX,g=rX,o= "$Childs_path_available"
		fi
		Childs_path_enabled="${CurrentConfigDir}/${ChildsPluralName}_enabled"
		if [ ! -d "$Childs_path_enabled" ] && [ $ReadOnly -eq 0 ] ; then
			mkdir -p "$Childs_path_enabled"
			chmod u=rwX,g=rX,o= "$Childs_path_enabled"
		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
				mkdir -p "$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 0:2024.05.15 #####

#[initcontainers]
LsbRunlevel ()
# Syntax as function: $(LsbRunlevel)
# Description: Returns (stdout) number of init stage defined on Linux Standard Base
# https://en.wikipedia.org/wiki/Runlevel
# Depends on functions: (none)
# Depends on software packages: systemd-sysv|sysvinit grep
# Notes:
#	- SuperUser (root) permissions required.
{
	local Value=''
	local CommandsRunning=''
	local LastStatus=0
	local StatusCode=0
	
	Value="$(runlevel)"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		Value="$(printf '%s' "$Value" | tr -s ' ' '\n' | tail -n 1)"
		if [ "$Value" = "S" ] || [ "$Value" = "s" ] ; then Value=1 ; fi
		if [ "$Value" = "4" ] ; then Value=5 ; fi
		if [ "$Value" = "0" ] || [ "$Value" = "1" ] || [ "$Value" = "2" ] || [ "$Value" = "3" ] || [ "$Value" = "5" ] || [ "$Value" = "6" ] ; then
			if [ "$Value" = "2" ] || [ "$Value" = "3" ] ; then
				CommandsRunning="$(cat /proc/*/cmdline 2>/dev/null)"
				if [ "$DISPLAY" != "" ] || [ "$(printf '%s' "$CommandsRunning" | grep -ae '^gnome-session' -ae '/gnome-session' -ae '^gnome-panel' -ae '/gnome-panel'  -ae 'startkde' -ae 'kdeinit' -ae 'startkde' -ae 'mate-settings' -ae 'mate-panel' -ae 'lxsession' -ae 'lxpanel' -ae 'lxqt-session' -ae 'lxqt-panel' -ae 'xfce.*session' -ae 'xfce.*panel' -ae 'cinnamon' -ae 'unity-settings' -ae 'unity.*-panel' -ae '^hildon-desktop' -ae '/hildon-desktop')" ] ; then
					# If running some desktop/GUI, then it's runlevel 5 (Debian up to v6 and Ubuntu up to v14 were reporting runlevel 2)
					Value=5
				fi
				if [ "$Value" = "2" ] && [ "$(ip route 2>/dev/null | grep -e '^default' -e ' dev ')" != "" ] ; then
					# If network is configured, then it's runlevel 3 (Debian up to v6 and Ubuntu up to v14 were reporting runlevel 2)
					Value=3
				fi
			fi
			if [ "$Value" != "" ] ; then
				printf '%s\n' "$Value"
			fi
		else
			printf '%s\n' "${sERROR}E: Could not get or calculate runlevel.${fRESET}" 1>&2
			LastStatus=105 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

#[/initcontainers]


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

#[initcontainers]
LinuxValue ()
# Syntax as a function: $(LinuxValue "$File" "$VariableName" "$NotFoundValue")
# Description: Returns a setting value from specified file (last matching one)
# THIS FUNCTION is developed in software project: ctctl
# THIS FUNCTION is also embedded to: initcontainers
# Expected file syntax is: "VarName = Value" (allowing spaces before and after =)
# Note: This can be also be treated with IniVarValue() by specifying NameValueSeparator=" = "
{
	local File="$1"
	local VariableName="$2"
	local NotFoundValue="$3"
	local VariableRegexp=''
	local Value=''
	
	VariableRegexp="$(printf '%s\n' "$VariableName" | sed -e 's|\.|\\.|g')"
	Text1="$(cat "$File" 2>/dev/null)"
	if [ "$(printf '%s\n' "$Text1" | grep -ie "^${VariableRegexp}=" -ie "^${VariableRegexp} =")" != "" ] ; then
		Value="$(cat "$File" | tr -s '\t' ' ' | grep -ie "^${VariableRegexp}=" -ie "^${VariableRegexp} =" | cut -f 2 -d '=' | tail -n 1)"
	else
		Value="$NotFoundValue"
	fi
	if [ "$Value" != "" ] ; then
		echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g'
	fi
}

ContainerBridge_LXC ()
{
	local UnprivilegedUser="$1"
	local VEID="$2"
	local ThisContainerBridge=''
	
	VpsCommonStoreTree="$(IniVarValue /etc/ctctl/system.conf VpsCommonStoreTree '' "/var/lib/lxc.u" =)"
	VpsStructureDir="${VpsCommonStoreTree}/${UnprivilegedUser}/${VEID}"
	LxcKey_NetDevice='lxc.network'
	if Is_IntegerNr "$LxcSyntaxLevel" && [ $LxcSyntaxLevel -ge 201 ] ; then
		LxcKey_NetDevice='lxc.net.0'
	fi
	ThisContainerBridge="$(LinuxValue "${VpsStructureDir}/config" ${LxcKey_NetDevice}.link '')"
	if [ "$ThisContainerBridge" = "" ] ; then
	UnprivilegedHome="$(sudo -iu "$UnprivilegedUser" pwd 2>/dev/null)"
		if [ "$UnprivilegedHome" = "" ] || [ "$UnprivilegedHome" = "/" ] ; then
			UnprivilegedHome="/home/${UnprivilegedUser}"
		fi
		LxcDefaultsFile="${UnprivilegedHome}/.config/lxc/default.conf"
		ThisContainerBridge="$(LinuxValue "$LxcDefaultsFile" ${LxcKey_NetDevice}.link '')"
	fi
	if [ "$ThisContainerBridge" != "" ] ; then printf '%s\n' "$ThisContainerBridge" ; fi
}

Launches_OpenVZ ()
{
	local SequenceList=''
	local NewSequenceList=''
	local CurContainer=''
	local CTID=''
	local VpsName=''
	local VpsMention=''
	local WaitForMaxLoad=''
	local WaitForMaxLoad_Elastic=0
	local CurWaitForMaxLoad=''
	local ReserveSecondsBeforeNext=''
	local MaxSecondsToWaitLoad=''
	local CtStatus=''
	local BeginTime=0
	local ReservedToTime=0
	local RemainingReservedSeconds=0
	local ThePercentLoad1=''
	local ContainerNr=0
	local ContainersLaunched=0
	local ContainersAlreadyRunning=0
	local ExplicitContainersNumbers=''
	local ExplicitContainersNames=''
	local ImplicitContainers=''
	local ImplicitContainersLine=''
	local ImplicitContainersSequence=''
	local ExistingContainers=''
	local LastStatus=0
	local StatusCode=0

	if [ $# -gt 0 ] ; then
		for CurContainer in "$@" ; do
			CurLine="$(cat "${CurrentConfigDir}/containers.list" 2>/dev/null | grep -ie "^ovz|${CurContainer}$" -ie "^ovz|${CurContainer}|")"
			if [ "$CurLine" = "" ] ; then
				if [ "$UserAccount" = "" ] ; then
					UserAccount="$(cat /etc/passwd 2>/dev/null | grep -ie ^ovz: | cut -f 1 -d ':')"
					UserAccount="$(IniVarValue /etc/ctctl/system.conf UnprivilegedUser '' "$UserAccount" = '')"
				fi
				CurLine="ovz|${CurContainer}|${UserAccount}|-1|1|5||"
			fi
			SequenceList="$(printf '%s\n' "$SequenceList" ; printf '%s\n' "$CurLine")"
		done
	else
		SequenceList="$(cat "${CurrentConfigDir}/containers.list" 2>/dev/null | grep -ie '^ovz|' -ie '^!ovz|')"
		IFS="$(printf '\n\b')" ; for CurContainer in $SequenceList ; do unset IFS
			CTID="$(printf '%s\n' "$CurContainer" | cut -sf 2 -d '|')"
			CTID="$(expr "$CTID" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
			if [ "$CTID" != "*" ] ; then
				if [ "$ExplicitContainersNumbers" != "" ] ; then ExplicitContainersNumbers="${ExplicitContainersNumbers}|" ; fi
				if [ "$ExplicitContainersNames" != "" ] ; then ExplicitContainersNames="${ExplicitContainersNames}|" ; fi
				if Is_IntegerNr "$CTID" ; then
					VpsName="$(sudo vzlist -a -H -o name $CTID)"
					VpsName="$(expr "$VpsName" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
					ExplicitContainersNumbers="${ExplicitContainersNumbers}${CTID}"
					if [ "$VpsName" != "" ] && [ "$VpsName" != "-" ] ; then
						ExplicitContainersNames="${ExplicitContainersNames}${VpsName}"
					fi
				else
					VpsName="$(sudo vzlist -a -H -o ctid $CTID)"
					VpsName="$(expr "$VpsName" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
					if [ "$VpsName" != "" ] && [ "$VpsName" != "-" ] ; then
						ExplicitContainersNumbers="${ExplicitContainersNumbers}${VpsName}"
					fi
					ExplicitContainersNames="${ExplicitContainersNames}${CTID}"
				fi
			else
				# Yes, there is a wildcard use
				ImplicitContainersLine="$CurContainer"
			fi
		done
		if [ "$ImplicitContainersLine" != "" ] ; then
			# Let's identify all unespecified containers
			ExistingContainers="$(vzlist -a -H -o ctid)"
			for CTID in $ExistingContainers ; do
				VpsName="$(sudo vzlist -a -H -o name $CTID)"
				VpsName="$(expr "$VpsName" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
				if [ "$(printf '%s' "|${ExplicitContainersNumbers}|" | grep -e "|${CTID}|")" = "" ] && [ "$(printf '%s' "|${ExplicitContainersNames}|" | grep -ie "|${VpsName}|")" = "" ] ; then
					if [ "$ImplicitContainers" != "" ] ; then ImplicitContainers="${ImplicitContainers}|" ; fi
					ImplicitContainers="${ImplicitContainers}${CTID}"
				fi
			done
			# Let's build complete sequence, filling implicit lines with other's lines
			IFS="$(printf '\n\b')" ; for CurContainer in $SequenceList ; do unset IFS
				if [ "$(printf '%s' "$CurContainer" | grep -e '^!')" = "" ] ; then
					CTID="$(printf '%s\n' "$CurContainer" | cut -sf 2 -d '|')"
					CTID="$(expr "$CTID" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
					if [ "$CTID" != "*" ] ; then
						NewSequenceList="$(printf '%s\n' "$NewSequenceList" ; printf '%s\n' "$CurContainer")"
					else
						IFS="|" ; for CTID in $ImplicitContainers ; do unset IFS
							# Using current line as a template for everyone
							ImplicitContainersLine="$(printf '%s' "$CurContainer" | cut -f 1 -d '|')|${CTID}|$(printf '%s' "$CurContainer" | cut -f 3- -d '|')"
							NewSequenceList="$(printf '%s\n' "$NewSequenceList" ; printf '%s\n' "$ImplicitContainersLine")"
						done
					fi
				fi
			done
			SequenceList="$NewSequenceList"
		fi
	fi
	if ! Is_Executable vzctl ; then
		LogProgram 1 "E: vzctl utility not found. OpenVZ may not be installed and working." 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		IFS="$(printf '\n\b')" ; for CurContainer in $SequenceList ; do unset IFS
			if [ ! -f "$MainControllerStopFile" ] && [ "$(printf '%s' "$CurContainer" | grep -e '^!')" = "" ] ; then
				ContainerNr=$(($ContainerNr + 1))
				# Syntax for OpenVZ container: ovz|CTID|WaitForMaxLoad|ReserveSecondsBeforeNext|WaitForFile\ExistingFileToComplete
				#ovz|777|50|10
				CTID="$(printf '%s\n' "$CurContainer" | cut -sf 2 -d '|')"
				CTID="$(expr "$CTID" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
				VpsName="$CTID"
				if Is_IntegerNr "$VpsName" ; then
					VpsName="$(sudo vzlist -a -H -o name $VpsName)"
				else
					VpsName="$(sudo vzlist -a -H -o ctid $VpsName)"
				fi
				VpsName="$(expr "$VpsName" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
				VpsMention="$CTID"
				if [ "$CTID" != "$VpsName" ] && [ "$VpsName" != "-" ] && [ "$VpsName" != "" ] ; then VpsMention="$CTID ${ParO}${VpsName}${ParC}" ; fi
				WaitForMaxLoad="$(printf '%s\n' "$CurContainer" | cut -sf 3 -d '|')"
				WaitForMaxLoad="$(echo TrimAndSingle $WaitForMaxLoad | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				if [ "$(printf '%s' "$WaitForMaxLoad" | grep -e '+$')" != "" ] ; then
					if [ "$(printf '%s' "$WaitForMaxLoad" | grep -e '++$')" != "" ] ; then
						WaitForMaxLoad_Elastic=2
					else
						WaitForMaxLoad_Elastic=1
					fi
					WaitForMaxLoad="$(printf '%s' "$WaitForMaxLoad" | cut -f 1 -d '+')"
				fi
				ReserveSecondsBeforeNext="$(printf '%s\n' "$CurContainer" | cut -sf 4 -d '|')"
				ReserveSecondsBeforeNext="$(echo TrimAndSingle $ReserveSecondsBeforeNext | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				MaxSecondsToWaitLoad="$(printf '%s' "$ReserveSecondsBeforeNext" | cut -sf 2 -d '-')"
				ReserveSecondsBeforeNext="$(printf '%s' "$ReserveSecondsBeforeNext" | cut -f 1 -d '-')"
				WaitForFile="$(printf '%s\n' "$CurContainer" | cut -sf 7 -d '|')"
				ExistingFileToComplete="$(printf '%s\n' "$CurContainer" | cut -sf 8 -d '|')"
				if [ $ContainerNr -eq 1 ] && [ "$(ps ax | grep -e 'vzmond' | grep -ve 'grep')" = "" ] ; then
					# Perhaps vzmond is pending to launch
					LogProgram 2 "Waiting for [vzmond] be present" 1>&2
					if Is_IntegerNr "$WaitForMaxLoad" ; then
						WaitForCalm $WaitForMaxLoad "$MainControllerStopFile" 0 10 10
					fi
					RemainingReservedSeconds=60
					while [ "$(ps ax | grep -e 'vzmond' | grep -ve 'grep')" = "" ] && [ ! -f "$MainControllerStopFile" ] && [ $RemainingReservedSeconds -gt 0 ] ; do
						sleep 2
						RemainingReservedSeconds=$(($RemainingReservedSeconds - 2))
					done
				fi
				CtStatus="$(vzctl status $CTID 2>/dev/null)"
				if [ "$(printf '%s\n' " $CtStatus " | grep -ie ' exist ')" != "" ] ; then
					if [ "$(printf '%s\n' " $CtStatus " | grep -ie ' running ')" = "" ] ; then
						if [ $StatusCode -eq 0 ] && [ ! -f "$MainControllerStopFile" ] && [ "$WaitForFile" != "" ] ; then
							if [ ! -e "$WaitForFile" ] && [ ! -L "$WaitForFile" ] && [ ! -f "$MainControllerStopFile" ] ; then
								LogProgram 3 "Waiting for file existance before: $WaitForFile"
								while [ ! -e "$WaitForFile" ] && [ ! -L "$WaitForFile" ] && [ ! -f "$MainControllerStopFile" ] ; do
									sleep 1
								done
								LogProgram 3 "Previous file found: $WaitForFile"
							else
								LogProgram 3 "Previous file found: $WaitForFile"
							fi
						fi
#						if Is_IntegerNr "$WaitForMaxLoad" ; then
#							CurWaitForMaxLoad=$WaitForMaxLoad
#							ThePercentLoad1=$(PercentLoad 1)
#							while [ $ThePercentLoad1 -ge $CurWaitForMaxLoad  ] ; do
#								LogProgram 3 "Waiting for system load ${ParO}${ThePercentLoad1}%${ParC} to be descending and under ${CurWaitForMaxLoad}% before launching $VpsMention"
#								WaitForCalm $CurWaitForMaxLoad "$MainControllerStopFile" "$MaxSecondsToWaitLoad" 10 10
#								ThePercentLoad1=$(PercentLoad 1)
#								CurWaitForMaxLoad=$(($CurWaitForMaxLoad + $WaitForMaxLoad))
#							done
#						else
#							LogProgram 2 "W: $WaitForMaxLoad is not an integer number for WaitForMaxLoad. Ignoring." 1>&2
#						fi
						if Is_IntegerNr "$WaitForMaxLoad" ; then
							CurWaitForMaxLoad=$WaitForMaxLoad
							if [ $WaitForMaxLoad_Elastic -ge 2 ] ; then
								CurIncrease_WaitForMaxLoad=$WaitForMaxLoad
							else
								CurIncrease_WaitForMaxLoad=$((WaitForMaxLoad / 2))
							fi
							ThePercentLoad1=$(PercentLoad 1)
							while [ $ThePercentLoad1 -ge $CurWaitForMaxLoad  ] ; do
								LogProgram 3 "Waiting for system load ${ParO}${ThePercentLoad1}%${ParC} to be descending and under ${CurWaitForMaxLoad}% before launching $VpsMention"
								if [ $WaitForMaxLoad_Elastic -ge 1 ] ; then
									WaitForCalm $CurWaitForMaxLoad "$MainControllerStopFile" "$MaxSecondsToWaitLoad" 10 10
									CurWaitForMaxLoad=$(($CurWaitForMaxLoad + $CurIncrease_WaitForMaxLoad))
									CurIncrease_WaitForMaxLoad=$((CurIncrease_WaitForMaxLoad / 2))
									if [ $WaitForMaxLoad_Elastic -ge 2 ] ; then
										if [ $CurIncrease_WaitForMaxLoad -lt $MaxSecondsToWaitLoad ] ; then
											CurIncrease_WaitForMaxLoad=$MaxSecondsToWaitLoad
											if [ $CurWaitForMaxLoad -lt $((WaitForMaxLoad * 2)) ] ; then
												CurWaitForMaxLoad=$((WaitForMaxLoad * 2))
											fi
										fi
									else
										if [ $CurIncrease_WaitForMaxLoad -lt 2 ] ; then
											CurIncrease_WaitForMaxLoad=2
											if [ $CurWaitForMaxLoad -lt $((WaitForMaxLoad * 2)) ] ; then
												CurWaitForMaxLoad=$((WaitForMaxLoad * 2))
											fi
										fi
									fi
								else
									WaitForCalm $CurWaitForMaxLoad "$MainControllerStopFile" 0 10 10
								fi
								ThePercentLoad1=$(PercentLoad 1)
							done
							LogProgram 3 "Going to launch $VpsMention with a system load of ${ThePercentLoad1}%"
						else
							LogProgram 2 "W: $WaitForMaxLoad is not an integer number for WaitForMaxLoad ${ParO}${VpsMention}${ParC}. Ignoring." 1>&2
						fi
						if [ ! -f "$MainControllerStopFile" ] ; then
							BeginTime="$(date +%s)"
							LogProgram 3 "Launching OpenVZ container: $VpsMention"
							vzctl start $CTID
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ $LastStatus -eq 0 ] ; then ContainersLaunched=$(($ContainersLaunched + 1)) ; fi
						fi
						if [ $StatusCode -eq 0 ] && [ ! -f "$MainControllerStopFile" ] && [ "$ExistingFileToComplete" != "" ] ; then
							if [ ! -e "$ExistingFileToComplete" ] && [ ! -L "$ExistingFileToComplete" ] && [ ! -f "$MainControllerStopFile" ] ; then
								LogProgram 3 "Waiting for file existance to complete: $ExistingFileToComplete"
								while [ ! -e "$ExistingFileToComplete" ] && [ ! -L "$ExistingFileToComplete" ] && [ ! -f "$MainControllerStopFile" ] ; do
									sleep 1
								done
								LogProgram 3 "Posterior file found: $ExistingFileToComplete"
							else
								LogProgram 3 "Posterior file found: $ExistingFileToComplete"
							fi
						fi
						if [ $StatusCode -eq 0 ] && [ ! -f "$MainControllerStopFile" ] ; then
							if Is_IntegerNr "$ReserveSecondsBeforeNext" ; then
								ReservedToTime=$(($BeginTime + $ReserveSecondsBeforeNext))
								RemainingReservedSeconds=$(($ReservedToTime - $(date +%s)))
								if [ $RemainingReservedSeconds -gt 0 ] ; then
									LogProgram 3 "Waiting $RemainingReservedSeconds more seconds to complete ${ReserveSecondsBeforeNext}s reservation."
								fi
								while [ $(date +%s) -lt $ReservedToTime ] && [ ! -f "$MainControllerStopFile" ] ; do
									sleep 1
								done
							else
								LogProgram 2 "W: $ReserveSecondsBeforeNext is not an integer number for ReserveSecondsBeforeNext. Ignoring." 1>&2
							fi
						fi
					else
						LogProgram 3 "I: Container $VpsMention already running. Ignoring."
						ContainersAlreadyRunning=$(($ContainersAlreadyRunning + 1))
					fi
				else
					LogProgram 1 "E: Container \"${CTID}\" not found." 1>&2
					LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		done
		if [ $ContainersAlreadyRunning -eq 0 ] ; then
			LogProgram 3 "Summary of $ContainerNr OpenVZ containers: $ContainersLaunched launched."
		else
			LogProgram 3 "Summary of $ContainerNr OpenVZ containers: $ContainersLaunched launched, $ContainersAlreadyRunning were already running."
		fi
	fi
	if [ $StatusCode -ne 0 ] ; then
		LogProgram 4 "Launches_OpenVZ() completes with exit-code: $StatusCode"
	fi
	return $StatusCode
}

Launches_LXC ()
{
	local SequenceList=''
	local NewSequenceList=''
	local CurContainer=''
	local CTID=''
	local VpsMention=''
	local UserAccount=''
	local WaitForMaxLoad=''
	local WaitForMaxLoad_Elastic=0
	local CurWaitForMaxLoad=''
	local CurIncrease_WaitForMaxLoad=''
	local ReserveSecondsBeforeNext=''
	local ReserveSecondsBeforeNext=''
	local WaitForFile=''
	local ExistingFileToComplete=''
	local CtStatus=''
	local BeginTime=0
	local ReservedToTime=0
	local RemainingReservedSeconds=0
	local ContainerNr=0
	local ContainersLaunched=0
	local ContainersAlreadyRunning=0
	local ExplicitContainers=''
	local ImplicitContainers=''
	local ImplicitContainersLine=''
	local ImplicitContainersSequence=''
	local ExistingContainers=''
	local CurContainer_Looking=''
	local CurSpecifiedLine=''
	local CurBuiltLine=''
	local CTID_Looking=''
	local UserAccount_Looking=''
	local ExploredAccounts=''
	local ThisContainerBridge=''
	local VpsEngine=''
	local LastStatus=0
	local StatusCode=0

	if ! Is_Executable lxc-start ; then
		LogProgram 1 "E: lxc-start utility not found. LinuX Containers may not be installed and working." 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$(printf '%s\n' "$SequenceList" | cut -sf 3 -d '|' | grep -e '.')" != "" ] && ! Is_Executable sudo ; then
		LogProgram 1 "E: sudo utility not found. It's required to execute unprivileged LXC containers." 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		while [ $# -gt 0 ] ; do
			if [ "$1" = "" ] ; then shift ; fi
		done
		if [ $# -gt 0 ] ; then
			for CurContainer in "$@" ; do
				CurLine="$(cat "${CurrentConfigDir}/containers.list" 2>/dev/null | grep -ie "^lxc|${CurContainer}$" -ie "^lxc|${CurContainer}|")"
				if [ "$CurLine" = "" ] ; then
					if [ "$UserAccount" = "" ] ; then
						UserAccount="$(cat /etc/passwd 2>/dev/null | grep -ie ^lxc: | cut -f 1 -d ':')"
						UserAccount="$(IniVarValue /etc/ctctl/system.conf UnprivilegedUser '' "$UserAccount" = '')"
					fi
					CurLine="lxc|${CurContainer}|${UserAccount}|-1|1|5||"
				fi
				LogProgram 4 "Parameter specified for \"${CurContainer}\" container. Adding to sequence: Engine=lxc CTID=$CurContainer UserAccount=$UserAccount Rest=$(printf '%s' "$CurLine" | cut -f 4- -d '|')"
				SequenceList="$(printf '%s\n' "$SequenceList" ; printf '%s\n' "$CurLine")"
			done
		else
			SequenceList="$(cat "${CurrentConfigDir}/containers.list" 2>/dev/null | grep -ie '^lxc|' | grep -ive '^!lxc|')"
			# Let's build complete sequence, filling implicit lines with other's lines
			NewSequenceList=''
			IFS="$(printf '\n\b')" ; for CurSpecifiedLine in $SequenceList ; do unset IFS
				if [ "$(printf '%s' "$CurSpecifiedLine" | grep -e '^!')" = "" ] ; then
					VpsEngine="$(printf '%s' "$CurSpecifiedLine" | cut -f 1 -d '|')"
					CTID="$(printf '%s\n' "$CurSpecifiedLine" | cut -sf 2 -d '|')"
					CTID="$(expr "$CTID" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
					UserAccount="$(printf '%s\n' "$CurSpecifiedLine" | cut -sf 3 -d '|')"
					UserAccount="$(expr "$UserAccount" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
					if [ "$UserAccount" = "*" ] ; then
						LogProgram 4 "Processing containers line ${ParO}all users${ParC}: $CurSpecifiedLine"
						# All but root (because it can find all others in privileged path /var/lib/lxc)
						ExploredAccounts="$(cat /etc/passwd | cut -f 1,7 -d ':' | grep -ive '^root:' -ive '/nologin$' | cut -f 1 -d ':')"
					else
						LogProgram 4 "Processing containers line ${ParO}user ${UserAccount}${ParC}: $CurSpecifiedLine"
						ExploredAccounts="$UserAccount"
					fi
					IFS="$(printf '\n\b')" ; for UserAccount in $ExploredAccounts ; do unset IFS
						ExistingContainers="$(sudo -iu "$UserAccount" -- lxc-ls -1 -f -F NAME 2>/dev/null | awk 'NR >= 2' | sed -e 's|[ ]*$||g')"
						LastStatus=$?
						if [ $LastStatus -eq 0 ] ; then
							if [ "$VerifiedAccounts" != "" ] ; then
								VerifiedAccounts="$(printf '%s\n' "$VerifiedAccounts" ; printf '%s' "$UserAccount")"
							else
								VerifiedAccounts="$UserAccount"
							fi
							if [ "$ExistingContainers" != "" ] ; then
								LogProgram 4 "LXC containers found for user ${UserAccount}: $(echo $ExistingContainers)"
							else
								LogProgram 4 "No LXC containers found for user $UserAccount"
							fi
						else
							# On accounts without shell, this reports "This account is currently not available." to stdout.
							ExistingContainers=''
							LogProgram 4 "No LXC containers possible for user $UserAccount"
						fi
						IFS="$(printf '\n\b')" ; for CTID_Looking in $ExistingContainers ; do unset IFS
							if [ "$CTID_Looking" = "$CTID" ] || [ "$CTID" = "*" ] ; then
								if [ "$(printf '%s\n' "$NewSequenceList" | cut -f 2,3 -d '|' | grep -e "^${CTID_Looking}|${UserAccount}$")" = "" ] ; then
									LogProgram 4 "Adding to sequence: Engine=$VpsEngine CTID=$CTID_Looking UserAccount=$UserAccount Rest=$(printf '%s' "$CurSpecifiedLine" | cut -f 4- -d '|')"
									ImplicitContainersLine="${VpsEngine}|${CTID_Looking}|${UserAccount}|$(printf '%s' "$CurSpecifiedLine" | cut -f 4- -d '|')"
									NewSequenceList="$(printf '%s\n' "$NewSequenceList" ; printf '%s\n' "$ImplicitContainersLine")"
								else
									LogProgram 4 "Container $CTID_Looking already sequenced for account $UserAccount"
								fi
							fi
						done
					done
				else
					LogProgram 4 "Ignoring containers line: $CurSpecifiedLine"
				fi
			done
			SequenceList="$NewSequenceList"
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		SequenceList="$(printf '%s\n' "$SequenceList" | grep -ve '^$')"
		LogProgram 4 "LXC containers sequence will be:\n$SequenceList"
		IFS="$(printf '\n\b')" ; for CurBuiltLine in $SequenceList ; do unset IFS
			if [ ! -f "$MainControllerStopFile" ] ; then
				ContainerNr=$(($ContainerNr + 1))
				# Syntax for LXC container: lxc|CTID|UserAccount|WaitForMaxLoad|ReserveSecondsBeforeNext|StopTimeoutSeconds|WaitForFile\ExistingFileToComplete
				#lxc|myweb|lxc|60|8|120
				CTID="$(printf '%s\n' "$CurBuiltLine" | cut -sf 2 -d '|')"
				CTID="$(expr "$CTID" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
				VpsMention="$CTID"
				UserAccount="$(printf '%s\n' "$CurBuiltLine" | cut -sf 3 -d '|')"
				UserAccount="$(expr "$UserAccount" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
				if [ "$UserAccount" != "" ] ; then
					CtStatus="$(sudo -iu "$UserAccount" -- lxc-info -n "$CTID" 2>/dev/null | grep -ie '^Stat.*:' | sed -e 's| ||g')"
				else
					CtStatus="$(lxc-info -n "$CTID" 2>/dev/null | grep -ie '^Stat.*:' | sed -e 's| ||g')"
				fi
				WaitForMaxLoad="$(printf '%s\n' "$CurBuiltLine" | cut -sf 4 -d '|')"
				WaitForMaxLoad="$(echo TrimAndSingle $WaitForMaxLoad | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				if [ "$(printf '%s' "$WaitForMaxLoad" | grep -e '+$')" != "" ] ; then
					if [ "$(printf '%s' "$WaitForMaxLoad" | grep -e '++$')" != "" ] ; then
						WaitForMaxLoad_Elastic=2
					else
						WaitForMaxLoad_Elastic=1
					fi
					WaitForMaxLoad="$(printf '%s' "$WaitForMaxLoad" | cut -f 1 -d '+')"
				fi
				ReserveSecondsBeforeNext="$(printf '%s\n' "$CurBuiltLine" | cut -sf 5 -d '|')"
				ReserveSecondsBeforeNext="$(echo TrimAndSingle $ReserveSecondsBeforeNext | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				MaxSecondsToWaitLoad="$(printf '%s' "$ReserveSecondsBeforeNext" | cut -sf 2 -d '-')"
				ReserveSecondsBeforeNext="$(printf '%s' "$ReserveSecondsBeforeNext" | cut -f 1 -d '-')"
				StopTimeoutSeconds="$(printf '%s\n' "$CurBuiltLine" | cut -sf 6 -d '|')"
				StopTimeoutSeconds="$(echo TrimAndSingle $StopTimeoutSeconds | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				WaitForFile="$(printf '%s\n' "$CurBuiltLine" | cut -sf 7 -d '|')"
				ExistingFileToComplete="$(printf '%s\n' "$CurBuiltLine" | cut -sf 8 -d '|')"
				LogProgram 4 "Turn=$ContainerNr CTID=$CTID UserAccount=$UserAccount CtStatus=$(printf '%s' "$CtStatus" | cut -f 2- -d ':') ReserveSecondsBeforeNext=$ReserveSecondsBeforeNext MaxSecondsToWaitLoad=$MaxSecondsToWaitLoad WaitForFile=$WaitForFile ExistingFileToComplete=$ExistingFileToComplete"

				if [ "$CtStatus" != "" ] ; then
					if [ "$(printf '%s' "$CtStatus" | grep -ie 'State:RUNNING$')" = "" ] ; then
						if [ $StatusCode -eq 0 ] && [ ! -f "$MainControllerStopFile" ] && [ "$WaitForFile" != "" ] ; then
							if [ ! -e "$WaitForFile" ] && [ ! -L "$WaitForFile" ] && [ ! -f "$MainControllerStopFile" ] ; then
								LogProgram 3 "Waiting for file existance before: $WaitForFile"
								while [ ! -e "$WaitForFile" ] && [ ! -L "$WaitForFile" ] && [ ! -f "$MainControllerStopFile" ] ; do
									sleep 1
								done
								LogProgram 3 "Previous file found: $WaitForFile"
							else
								LogProgram 3 "Previous file found: $WaitForFile"
							fi
						fi
						if Is_IntegerNr "$WaitForMaxLoad" ; then
							CurWaitForMaxLoad=$WaitForMaxLoad
							if [ $WaitForMaxLoad_Elastic -ge 2 ] ; then
								CurIncrease_WaitForMaxLoad=$WaitForMaxLoad
							else
								CurIncrease_WaitForMaxLoad=$((WaitForMaxLoad / 2))
							fi
							ThePercentLoad1=$(PercentLoad 1)
							while [ $ThePercentLoad1 -ge $CurWaitForMaxLoad  ] ; do
								LogProgram 3 "Waiting for system load ${ParO}${ThePercentLoad1}%${ParC} to be descending and under ${CurWaitForMaxLoad}% before launching $VpsMention"
								if [ $WaitForMaxLoad_Elastic -ge 1 ] ; then
									WaitForCalm $CurWaitForMaxLoad "$MainControllerStopFile" "$MaxSecondsToWaitLoad" 10
									CurWaitForMaxLoad=$(($CurWaitForMaxLoad + $CurIncrease_WaitForMaxLoad))
									CurIncrease_WaitForMaxLoad=$((CurIncrease_WaitForMaxLoad / 2))
									if [ $WaitForMaxLoad_Elastic -ge 2 ] ; then
										if [ $CurIncrease_WaitForMaxLoad -lt $MaxSecondsToWaitLoad ] ; then
											CurIncrease_WaitForMaxLoad=$MaxSecondsToWaitLoad
											if [ $CurWaitForMaxLoad -lt $((WaitForMaxLoad * 2)) ] ; then
												CurWaitForMaxLoad=$((WaitForMaxLoad * 2))
											fi
										fi
									else
										if [ $CurIncrease_WaitForMaxLoad -lt 2 ] ; then
											CurIncrease_WaitForMaxLoad=2
											if [ $CurWaitForMaxLoad -lt $((WaitForMaxLoad * 2)) ] ; then
												CurWaitForMaxLoad=$((WaitForMaxLoad * 2))
											fi
										fi
									fi
								else
									WaitForCalm $CurWaitForMaxLoad "$MainControllerStopFile" 0 10
								fi
								ThePercentLoad1=$(PercentLoad 1)
							done
							LogProgram 3 "Going to launch $VpsMention with a system load of ${ThePercentLoad1}%"
						else
							LogProgram 2 "W: $WaitForMaxLoad is not an integer number for WaitForMaxLoad ${ParO}${VpsMention}${ParC}. Ignoring." 1>&2
						fi
						if [ ! -f "$MainControllerStopFile" ] ; then
							BeginTime="$(date +%s)"
							LogProgram 3 "Launching LXC container: $VpsMention"
							ThisContainerBridge="$(ContainerBridge_LXC "$UserAccount" "$CTID")"
							if [ "$ThisContainerBridge" != "" ] && [ "$(cat /etc/network/interfaces /etc/network/interfaces.d/* 2>/dev/null | grep -e "^iface $ThisContainerBridge inet static")" != "" ] && Is_Executable ifdown && Is_Executable ifup ; then
								BridgeCIDR="$(cat /etc/network/interfaces /etc/network/interfaces.d/* 2>/dev/null | cut -f 1 -d'#' | sed -ne '/^iface lxcbr0 inet static/,//p' | tr -s '\t' ' ' | grep -e '^ address ' | head -n 1 | cut -f 3 -d ' ')"
								if [ "$BridgeCIDR" != "" ] ; then
									# Some times LXC forces different IP to bridge than statically configured~desired.
									Value1="$(ip -4 address show dev $ThisContainerBridge | grep -e "inet .*scope .* ${ThisContainerBridge}$" | tr -s '\t' ' ' | cut -f 3 -d ' ')"
									if [ "$(printf '%s' "$BridgeCIDR" | grep -e '/')" = "" ] ; then BridgeCIDR="${BridgeCIDR}/$(printf '%s' "$Value1" | cut -sf 2 -d '/')" ; fi
									if [ "$Value1" != "$BridgeCIDR" ] ; then
										printf '%s\n' "W: Network bridge unexpectedly changed IP configuration to: $Value1" 1>&2
										printf '%s\n' "   Re-enabling bridge so it gets: $BridgeCIDR"
										ifdown $ThisContainerBridge >/dev/null 2>&1
										ifup $ThisContainerBridge
									fi
								fi
							fi
							if [ "$LxcStartProgram" = "lxc-unpriv-start" ] ; then
								# https://discuss.linuxcontainers.org/t/an-lxc-container-does-not-start-on-debian-11-currently-testing-version/10474
								# https://unix.stackexchange.com/questions/655898/cannot-start-unprivileged-lxc-containers-on-debian-11-bullseye
								loginctl enable-linger "$UserAccount"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								if [ $LastStatus -ne 0 ] ; then
									LogProgram 1 "Command was: loginctl enable-linger \"$UserAccount\""
								fi
								while [ $LastStatus -eq 0 ] && [ "$(ps -u "$UserAccount" -o cmd | grep -e '/systemd .*--user')" = "" ] ; do
									# Wait for lingering be working
									sleep 1
								done
							fi
							if [ "$UserAccount" != "" ] ; then
								PreRunningContainers="$(sudo -iu "$UserAccount" -- lxc-ls --running -1 -F NAME)"
								LogProgram 4 '$' sudo -iu "$UserAccount" -- $LxcStartProgram -d -n "$CTID"
								sudo -iu "$UserAccount" -- $LxcStartProgram -d -n "$CTID"
								LastStatus=$?
								if [ $LastStatus -ne 0 ] && Is_Executable ctctl ; then
									# Workaround while initcontainers is not merged to ctctl
									LogProgram 4 "External call: ctctl start $CTID"
									ctctl start "$CTID"
									LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								else
									if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								fi
								if [ $LastStatus -ne 0 ] ; then
									LogProgram 1 "Command was: sudo -iu \"$UserAccount\" -- $LxcStartProgram -d -n \"$CTID\""
								fi
								if [ $LastStatus -ne 0 ] && [ "$PreRunningContainers" != "" ] ; then
									PostRunningContainers="$(sudo -iu "$UserAccount" -- lxc-ls --running -1 -F NAME)"
									if [ "$PostRunningContainers" = "" ] ; then
										LogProgram 2 "W: $VEID container failed to start and this made break other containers: $(echo $PreRunningContainers)"
									fi
								fi
							else
								PreRunningContainers="$(lxc-ls --running -1 -F NAME)"
								LogProgram 4 '$' $LxcStartProgram -d -n "$CTID"
								$LxcStartProgram -d -n "$CTID"
								LastStatus=$?
								if [ $LastStatus -ne 0 ] && Is_Executable ctctl ; then
									# Workaround while initcontainers is not merged to ctctl
									LogProgram 4 "External call: ctctl start $CTID"
									ctctl start "$CTID"
									LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								else
									if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								fi
								if [ $LastStatus -ne 0 ] ; then
									LogProgram 1 "Command was: $LxcStartProgram -d -n \"$CTID\""
								fi
								if [ $LastStatus -ne 0 ] && [ "$PreRunningContainers" != "" ] ; then
									PostRunningContainers="$(lxc-ls --running -1 -F NAME)"
									if [ "$PostRunningContainers" = "" ] ; then
										LogProgram 2 "W: $VEID container failed to start and this made break other containers: $(echo $PreRunningContainers)"
									fi
								fi
							fi
							if [ $LastStatus -eq 0 ] ; then ContainersLaunched=$(($ContainersLaunched + 1)) ; fi
						fi
						if [ $StatusCode -eq 0 ] && [ ! -f "$MainControllerStopFile" ] && [ "$ExistingFileToComplete" != "" ] ; then
							if [ ! -e "$ExistingFileToComplete" ] && [ ! -L "$ExistingFileToComplete" ] && [ ! -f "$MainControllerStopFile" ] ; then
								LogProgram 3 "Waiting for file existance to complete: $ExistingFileToComplete"
								while [ ! -e "$ExistingFileToComplete" ] && [ ! -L "$ExistingFileToComplete" ] && [ ! -f "$MainControllerStopFile" ] ; do
									sleep 1
								done
								LogProgram 3 "Posterior file found: $ExistingFileToComplete"
							else
								LogProgram 3 "Posterior file found: $ExistingFileToComplete"
							fi
						fi
						if [ $StatusCode -eq 0 ] && [ ! -f "$MainControllerStopFile" ] ; then
							if Is_IntegerNr "$ReserveSecondsBeforeNext" ; then
								ReservedToTime=$(($BeginTime + $ReserveSecondsBeforeNext))
								RemainingReservedSeconds=$(($ReservedToTime - $(date +%s)))
								if [ $RemainingReservedSeconds -gt 0 ] ; then
									LogProgram 3 "Waiting $RemainingReservedSeconds more seconds to complete ${ReserveSecondsBeforeNext}s reservation."
								fi
								while [ $(date +%s) -lt $ReservedToTime ] && [ ! -f "$MainControllerStopFile" ] ; do
									sleep 1
								done
							else
								LogProgram 2 "W: $ReserveSecondsBeforeNext is not an integer number for ReserveSecondsBeforeNext. Ignoring."
							fi
						fi
					else
						LogProgram 3 "I: Container \"${VpsMention}\" already running. Ignoring."
						ContainersAlreadyRunning=$(($ContainersAlreadyRunning + 1))
					fi
				else
					if [ "$UserAccount" != "" ] ; then
						LogProgram 1 "E: Container \"${VpsMention}\" not found."
					else
						LogProgram 1 "E: Container \"${VpsMention}\" not found for user account \"${UserAccount}\""
					fi
					LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		done
		if [ $ContainersAlreadyRunning -eq 0 ] ; then
			LogProgram 3 "Summary of $ContainerNr LXC containers: $ContainersLaunched launched."
		else
			LogProgram 3 "Summary of $ContainerNr LXC containers: $ContainersLaunched launched, $ContainersAlreadyRunning were already running."
		fi
	fi
	if [ $StatusCode -ne 0 ] ; then
		LogProgram 4 "Launches_LXC() completes with exit-code: $StatusCode"
	fi
	return $StatusCode
}

Launches_Chroot ()
{
	local LastStatus=0
	local StatusCode=0

	# Syntax for chroot container: chroot|CT-Path|UserAccount|WaitForMaxLoad|ReserveSecondsBeforeNext|StopTimeoutSeconds|WaitForFile|ExistingFileToComplete
	#chroot|/home/user/myguest|root|60|8|120
	LogProgram 1 "E: Chroot containers launcher not yet implemented.\nMake sure chroot is not specified at TypesOrder configuration parameter." 1>&2
	LastStatus=63 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -ne 0 ] ; then
		LogProgram 4 "Launches_Chroot() completes with exit-code: $StatusCode"
	fi
	return $StatusCode
}

ContainerVeidFromGuestPid_LXC ()
# When PID does not exist or it's not detected to be in a Guest tree, it returns no string ""
# Function developed at ctctl project; other programs can be using it. Version 0:2020.01.25
{
	local CurAskedPid="$1"
	local CurAskedPid_MonitorPid=''
	local CurPidsTree=''
	local CurCmd=''
	local Value=''

	if [ "$(pstree $CurAskedPid)" != "" ] ; then
		CurAskedPid_MonitorPid=''
		CurPidsTree="$(ParentPstree $CurAskedPid)"
		for CurPid in $CurPidsTree ; do
			if [ "$CurAskedPid_MonitorPid" = "" ] ; then
				CurCmd="$(ps -p $CurPid -o cmd | grep -ie '^\[lxc monitor\] ')"
				if [ "$CurCmd" != "" ] ; then
					CurAskedPid_MonitorPid=$CurPid
					# Think in UnprivilegedUser containing spaces... Pending to support privileged containers.
					Value="$(printf '%s' "$CurCmd" | sed -e "s|^\[lxc monitor\] /home/${UnprivilegedUser}||g" | cut -f 2- -d ' ')"
				fi
			fi
		done
	fi
	if [ "$Value" != "" ] ; then printf '%s' "$Value" ; fi
}

Stops_LXC ()
{
	local SequenceList=''
	local CurContainer=''
	local CurSpecifiedLine=''
	local CurBuiltLine=''
	local CurLine=''
	local CTID=''
	local UserAccount=''
	local WaitForMaxLoad=''
	local ReserveSecondsBeforeNext=''
	local StopTimeoutSeconds=''
	local Parms_StopTimeoutOrWait=''
	local WaitForFile=''
	local ExistingFileToComplete=''
	local CtStatus=''
	local BeginTime=0
	local ReservedToTime=0
	local RemainingReservedSeconds=0
	local StopAllForUsers=''
	local TimeBeginS=''
	local TimeEndS=''
	local CurContainerPid=''
	local SignaledContainersPids=''
	local TempPidsCollection=''
	local AliveContainersPids=''
	local CurPid=''
	local PidsNamesPath=''
	local GlobalWaited=0
	local CurrentRunlevel=''
	local VerifiedAccounts=''
	local OtherActiveVps_Nr=0
	local LastStatus=0
	local StatusCode=0

	if ! Is_Executable lxc-stop ; then
		LogProgram 1 "E: lxc-stop utility not found. LinuX Containers may not be installed and working." 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$(printf '%s\n' "$SequenceList" | cut -sf 3 -d '|' | grep -e '.')" != "" ] && ! Is_Executable sudo ; then
		LogProgram 1 "E: sudo utility not found. It's required to execute unprivileged LXC containers." 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	
	# BUILDING CONTAINERS LIST TO STOP
	if [ $StatusCode -eq 0 ] ; then
		PidsNamesPath="${DirTemp}/${ServiceName}/pids-vps-names"
		mkdir -p "$PidsNamesPath"
		if [ $# -gt 0 ] ; then
			for CurContainer in "$@" ; do
				CurLine="$(cat "${CurrentConfigDir}/containers.list" 2>/dev/null | grep -ie "^lxc|${CurContainer}$" -ie "^lxc|${CurContainer}|")"
				if [ "$CurLine" = "" ] ; then
					if [ "$UserAccount" = "" ] ; then
						UserAccount="$(cat /etc/passwd 2>/dev/null | grep -ie ^lxc: | cut -f 1 -d ':')"
						UserAccount="$(IniVarValue /etc/ctctl/system.conf UnprivilegedUser '' "$UserAccount" = '')"
					fi
					CurLine="lxc|${CurContainer}|${UserAccount}|-1|1|5||"
				fi
				SequenceList="$(printf '%s\n' "$SequenceList" ; printf '%s\n' "$CurLine")"
			done
		else
			CurrentRunlevel="$(LsbRunlevel)"
			SequenceList="$(cat "${CurrentConfigDir}/containers.list" 2>/dev/null | grep -ie '^lxc|' -ie '^!lxc|')"
			# Let's build complete sequence, filling implicit lines with other's lines
			IFS="$(printf '\n\b')" ; for CurSpecifiedLine in $SequenceList ; do unset IFS
				CTID="$(printf '%s\n' "$CurSpecifiedLine" | cut -sf 2 -d '|')"
				CTID="$(expr "$CTID" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
				UserAccount="$(printf '%s\n' "$CurSpecifiedLine" | cut -sf 3 -d '|')"
				UserAccount="$(expr "$UserAccount" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
				if [ "$CTID" != "*" ] ; then
					if [ "$(printf '%s' "$CurSpecifiedLine" | grep -e '^!')" = "" ] ; then
						NewSequenceList="$(printf '%s\n' "$NewSequenceList" ; printf '%s\n' "$CurSpecifiedLine")"
					fi
				else
					ExplicitContainers=''
					if [ "$UserAccount" = "*" ] ; then
						# All but root (because it can find all others in privileged path /var/lib/lxc)
						ExploredAccounts="$(cat /etc/passwd | cut -f 1,7 -d ':' | grep -ive '^root:' -ive '/nologin$' | cut -f 1 -d ':')"
						StopAllForUsers="*"
					else
						ExploredAccounts="$UserAccount"
						if [ "$StopAllForUsers" != "*" ] ; then
							StopAllForUsers="$(printf '%s\n' "$StopAllForUsers" ; printf '%s\n' "$UserAccount")"
						fi
					fi
					IFS="$(printf '\n\b')" ; for UserAccount in $ExploredAccounts ; do unset IFS
						IFS="$(printf '\n\b')" ; for CurContainer_Looking in $SequenceList ; do unset IFS
							CTID_Looking="$(printf '%s\n' "$CurContainer_Looking" | cut -sf 2 -d '|')"
							CTID_Looking="$(expr "$CTID_Looking" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
							if [ "$(printf '%s' "$CurContainer_Looking" | grep -e '^!')" != "" ] && [ "$CurrentRunlevel" != "0" ] && [ "$CurrentRunlevel" != "6" ] ; then
								# Exclusion only to work if system is not stopping nor rebooting
								CTID_Looking="!${CTID_Looking}"
							fi
							UserAccount_Looking="$(printf '%s\n' "$CurContainer_Looking" | cut -sf 3 -d '|')"
							UserAccount_Looking="$(expr "$UserAccount_Looking" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
							if [ "$CTID_Looking" != "*" ] && [ "$(printf '%s' "$CTID_Looking" | grep -e '^!')" = "" ] ; then
								if [ "$UserAccount_Looking" = "$UserAccount" ] || [ "$UserAccount_Looking" = "*" ] ; then
									# Explicit container for matching account
									if [ "$ExplicitContainers" != "" ] ; then ExplicitContainers="${ExplicitContainers}|" ; fi
									ExplicitContainers="${ExplicitContainers}${CTID_Looking}"
								fi
							fi
						done
						ExistingContainers="$(sudo -iu "$UserAccount" -- lxc-ls -1 -f -F NAME 2>/dev/null | awk 'NR >= 2' | sed -e 's|[ ]*$||g')"
						LastStatus=$?
						if [ $LastStatus -eq 0 ] ; then
							if [ "$VerifiedAccounts" != "" ] ; then
								VerifiedAccounts="$(printf '%s\n' "$VerifiedAccounts" ; printf '%s' "$UserAccount")"
							else
								VerifiedAccounts="$UserAccount"
							fi
						else
							# On accounts without shell, this reports "This account is currently not available." to stdout.
							ExistingContainers=''
						fi
						IFS="$(printf '\n\b')" ; for CTID_Looking in $ExistingContainers ; do unset IFS
							if [ "$(printf '%s' "|${ExplicitContainers}|" | grep -e "|${CTID_Looking}|")" = "" ] ; then
								ImplicitContainersLine="$(printf '%s' "$CurSpecifiedLine" | cut -f 1 -d '|')|${CTID_Looking}|${UserAccount}|$(printf '%s' "$CurSpecifiedLine" | cut -f 4- -d '|')"
								NewSequenceList="$(printf '%s\n' "$NewSequenceList" ; printf '%s\n' "$ImplicitContainersLine")"
							fi
						done
					done
				fi
			done
			SequenceList="$NewSequenceList"
			SequenceList="$(printf '%s\n' "$SequenceList" | tac)"
		fi
	fi

	if [ $StatusCode -eq 0 ] ; then
		# First round: NORMAL STOPS SIGNALING FOR LISTED CONTAINERS
		TimeBeginS=$(date +%s)
		IFS="$(printf '\n\b')" ; for CurBuiltLine in $SequenceList ; do unset IFS
			# Syntax for LXC container: lxc|CTID|UserAccount|WaitForMaxLoad|ReserveSecondsBeforeNext|StopTimeoutSeconds|WaitForFile|ExistingFileToComplete
			#lxc|myweb|lxc|60|8|120
			CTID="$(printf '%s' "$CurBuiltLine" | cut -sf 2 -d '|')"
			CTID="$(expr "$CTID" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
			UserAccount="$(printf '%s\n' "$CurBuiltLine" | cut -sf 3 -d '|')"
			UserAccount="$(expr "$UserAccount" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
			if [ "$UserAccount" != "" ] ; then
				CtStatus="$(sudo -iu "$UserAccount" -- lxc-info -n "$CTID" 2>/dev/null)"
			else
				CtStatus="$(lxc-info -n "$CTID" 2>/dev/null)"
			fi
			WaitForMaxLoad="$(printf '%s\n' "$CurBuiltLine" | cut -sf 4 -d '|')"
			WaitForMaxLoad="$(echo TrimAndSingle $WaitForMaxLoad | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
			ReserveSecondsBeforeNext="$(printf '%s\n' "$CurBuiltLine" | cut -sf 5 -d '|')"
			ReserveSecondsBeforeNext="$(echo TrimAndSingle $ReserveSecondsBeforeNext | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
			StopTimeoutSeconds="$(printf '%s\n' "$CurBuiltLine" | cut -sf 6 -d '|')"
			StopTimeoutSeconds="$(echo TrimAndSingle $StopTimeoutSeconds | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
			if ! Is_IntegerNr "$StopTimeoutSeconds" ; then
				LogProgram 1 "E: Bad value \"${StopTimeoutSeconds}\" for 6th field: StopTimeoutSeconds ${ParO}lxc|${CTID}|${UserAccount}${ParC}. Assuming -1." 1>&2
				LastStatus=65 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				StopTimeoutSeconds=-1
			fi
			Parms_StopTimeoutOrWait=''
			if [ $StopTimeoutSeconds -ge 0 ] ; then Parms_StopTimeoutOrWait="-t $StopTimeoutSeconds" ; fi
			if [ -f "$MainControllerStopFile" ] ; then
				# Force fastest action
				Parms_StopTimeoutOrWait='--nowait'
				StopTimeoutSeconds=0
				ParallelizeStops=1
			fi
			WaitForFile="$(printf '%s\n' "$CurBuiltLine" | cut -sf 7 -d '|')"
			ExistingFileToComplete="$(printf '%s\n' "$CurBuiltLine" | cut -sf 8 -d '|')"
			if [ "$(printf '%s\n' " $CtStatus " | grep -ie 'State:')" != "" ] ; then
				if [ "$(printf '%s\n' " $CtStatus " | sed -e 's| ||g' | grep -ie 'State:RUNNING$')" != "" ] ; then
					if [ "$UserAccount" != "" ] ; then
						LogProgram 3 "Stopping LXC container for ${UserAccount}: $CTID"
						if [ "$LxcStartProgram" = "lxc-unpriv-start" ] ; then
#echo DEBUG: sudo -iu "$UnprivilegedUser" -- lxc-ls --active -1 -F NAME 1>&2
#							OtherActiveVps_Nr="$(sudo -iu "$UnprivilegedUser" -- lxc-ls --active -1 -F NAME)"
							OtherActiveVps_Nr="$(sudo -iu "$UserAccount" -- lxc-ls --active -1 -F NAME)"
							LastStatus=$?
							if [ $LastStatus -ne 0 ] ; then
#								LogProgram 1 "Command was: sudo -iu \"$UnprivilegedUser\" -- lxc-ls --active -1 -F NAME"
								LogProgram 1 "Command was: sudo -iu \"$UserAccount\" -- lxc-ls --active -1 -F NAME"
							fi
							OtherActiveVps_Nr=$(printf '%s' "$OtherActiveVps_Nr" | grep -ve "^${CTID}$" | wc -l)
							if [ $OtherActiveVps_Nr -eq 0 ] ; then
								# Accelerates process releases. Only if this is the last one to be stopped.
								# THIS CAN MEAN VPS IS KILLED WHEN LINGER RELEASES! THIS CAN BE A BAD ACTION.
#								loginctl disable-linger "$UnprivilegedUser"
								loginctl disable-linger "$UserAccount"
							fi
						fi
#echo DEBUG: sudo -iu "$UserAccount" -- lxc-info -p -H -n "$CTID" 1>&2
						CurContainerPid="$(sudo -iu "$UserAccount" -- lxc-info -p -H -n "$CTID")"
						LastStatus=$?
						if [ $LastStatus -ne 0 ] ; then
							LogProgram 1 "Command was: sudo -iu \"$UserAccount\" -- lxc-info -p -H -n \"$CTID\""
						fi
						if Is_IntegerNr $CurContainerPid ; then printf '%s' "$CTID" > "${PidsNamesPath}/${CurContainerPid}.txt" ; fi
						if [ $ParallelizeStops -gt 0 ] ; then
							LogProgram 4 "Fork pidfile: ${ContainersPidsDir}/${UserAccount}/${CTID}.stop.pid"
							LogProgram 4 "[fork][${UserAccount}]\$" sudo -iu "$UserAccount" -- lxc-stop $Parms_StopTimeoutOrWait -n "$CTID"
#echo DEBUG: "$(WhereProgram sudo)" -iu "$UserAccount" -- lxc-stop $Parms_StopTimeoutOrWait -n "$CTID" 1>&2
							DaemonizeCommand "${ContainersPidsDir}/${UserAccount}/${CTID}.stop.pid" "$(WhereProgram sudo)" -iu "$UserAccount" -- lxc-stop $Parms_StopTimeoutOrWait -n "$CTID"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						else
#echo DEBUG: sudo -iu "$UserAccount" -- lxc-stop $Parms_StopTimeoutOrWait -n "$CTID" 1>&2
							LogProgram 4 '$' sudo -iu "$UserAccount" -- lxc-stop $Parms_StopTimeoutOrWait -n "$CTID"
							if [ $StopTimeoutSeconds -ge 0 ] ; then
#echo DEBUG: sudo -iu "$UserAccount" -- lxc-stop $Parms_StopTimeoutOrWait -n "$CTID" 1>&2
								TimedExecution $(($StopTimeoutSeconds + 2)) 1 "${ContainersPidsDir}/${UserAccount}/${CTID}.stop.pid" sudo -iu "$UserAccount" -- lxc-stop $Parms_StopTimeoutOrWait -n "$CTID"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							else
#echo DEBUG: sudo -iu "$UserAccount" -- lxc-stop $Parms_StopTimeoutOrWait -n "$CTID" 1>&2
								sudo -iu "$UserAccount" -- lxc-stop $Parms_StopTimeoutOrWait -n "$CTID"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							fi
						fi
					else
						LogProgram 3 "Stopping LXC container: $CTID"
						UserAccount=root
						if [ "$LxcStartProgram" = "lxc-unpriv-start" ] ; then
#echo DEBUG: sudo -iu "$UnprivilegedUser" -- lxc-ls --active -1 -F NAME 1>&2
							OtherActiveVps_Nr=$(sudo -iu "$UnprivilegedUser" -- lxc-ls --active -1 -F NAME | grep -ve "^${CTID}$" | wc -l)
							if [ $OtherActiveVps_Nr -eq 0 ] ; then
								# Accelerates process releases. Only if this is the last one to be stopped.
								# THIS CAN MEAN VPS IS KILLED WHEN LINGER RELEASES! THIS CAN BE A BAD ACTION.
								loginctl disable-linger "$UnprivilegedUser"
							fi
						fi
						CurContainerPid="$(lxc-info -p -H -n "$CTID")"
						if Is_IntegerNr $CurContainerPid ; then printf '%s' "$CTID" > "${PidsNamesPath}/${CurContainerPid}.txt" ; fi
						if [ $ParallelizeStops -gt 0 ] ; then
							LogProgram 4 "Fork pidfile: ${ContainersPidsDir}/${UserAccount}/${CTID}.stop.pid"
							LogProgram 4 "[fork][${UserAccount}]\$" lxc-stop $Parms_StopTimeoutOrWait -n "$CTID"
							DaemonizeCommand "${ContainersPidsDir}/${UserAccount}/${CTID}.stop.pid" "$(WhereProgram lxc-stop)" $Parms_StopTimeoutOrWait -n "$CTID"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						else
							LogProgram 4 '$' lxc-stop $Parms_StopTimeoutOrWait -n "$CTID"
							if [ $StopTimeoutSeconds -ge 0 ] ; then
								lxc-stop $Parms_StopTimeoutOrWait -n "$CTID"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							else
								TimedExecution $(($StopTimeoutSeconds + 2)) 1 "${ContainersPidsDir}/${UserAccount}/${CTID}.stop.pid" lxc-stop $Parms_StopTimeoutOrWait -n "$CTID"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							fi
						fi
					fi
					SignaledContainersPids="$SignaledContainersPids $CurContainerPid"
				else
					LogProgram 3 "I: Container \"${CTID}\" not running. Ignoring."
				fi
			else
				LogProgram 1 "E: Container \"${CTID}\" not found." 1>&2
				LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		done

		# First round: OTHER/UNKNOWN CONTAINERS TO STOP NORMALLY + WAIT GLOBAL ParallelizeStops TIME
		if [ "$StopAllForUsers" != "" ] || [ "$StopAllWhenStop" -gt 0 ] ; then
			LogProgram 3 "Stopping undiscovered containers"
			if [ -f "$MainControllerStopFile" ] ; then
				# Force fastest action
				ParallelizeStops=1
				StopAllWhenStop=1
			fi
			if [ "$StopAllForUsers" = "*" ] || [ "$StopAllWhenStop" -gt 0 ] ; then
				# All but root (because it can find all others in privileged path /var/lib/lxc)
				ExploredAccounts="$(cat /etc/passwd | cut -f 1,7 -d ':' | grep -ive '^root:' -ive '/nologin$' | cut -f 1 -d ':')"
			else
				ExploredAccounts="$StopAllForUsers"
			fi
			VerifiedAccounts=''
			IFS="$(printf '\n\b')" ; for UserAccount in $ExploredAccounts ; do unset IFS
				TempPidsCollection="$(sudo -iu "$UserAccount" -- lxc-ls -1 -f -F PID 2>/dev/null)"
				LastStatus=$?
				if [ $LastStatus -eq 0 ] ; then  # On accounts without shell, this reports "This account is currently not available." to stdout.
					if [ "$VerifiedAccounts" != "" ] ; then
						VerifiedAccounts="$(printf '%s\n' "$VerifiedAccounts" ; printf '%s' "$UserAccount")"
					else
						VerifiedAccounts="$UserAccount"
					fi
					for CurContainerPid in $TempPidsCollection ; do
						if Is_IntegerNr "$CurContainerPid" ; then
							if [ "$(printf '%s' " $SignaledContainersPids " | grep -e " $CurContainerPid ")" = "" ] ; then SignaledContainersPids="$SignaledContainersPids $CurContainerPid" ; fi
							if [ ! -f "${PidsNamesPath}/${CurContainerPid}.txt" ] ; then
								CTID="$(ContainerVeidFromGuestPid_LXC $CurPid)"
								if [ "$CTID" != "" ] ; then printf '%s' "$CTID" > "${PidsNamesPath}/${CurContainerPid}.txt" ; fi
							fi
						fi
					done
					if [ $ParallelizeStops -gt 0 ] ; then
						LogProgram 4 "Fork pidfile: ${ContainersPidsDir}/${UserAccount}/ALL.VPS.stop.pid"
						LogProgram 4 "[fork][${UserAccount}]\$" sudo -iu "$UserAccount" -- lxc-autostart -a -s
						DaemonizeCommand "${ContainersPidsDir}/${UserAccount}/ALL.VPS.stop.pid" "$(WhereProgram sudo)" -iu "$UserAccount" -- lxc-autostart -a -s
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						if [ "$StopAllWhenStop" -gt 0 ] ; then
							TimedExecution $StopAllWhenStop 1 "${ContainersPidsDir}/${UserAccount}/${CTID}.stop.pid" sudo -iu "$UserAccount" -- lxc-autostart -a -s
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						else
							sudo -iu "$UserAccount" -- lxc-autostart -a -s
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					fi
				fi
			done
			if [ "$StopAllForUsers" = "*" ] || [ "$StopAllWhenStop" -gt 0 ] ; then
				if [ "$(printf '%s\n' "$VerifiedAccounts" | grep -e '^root$')" = "" ] ; then
					# root
					UserAccount=root
					TempPidsCollection="$(lxc-ls -1 -f -F PID 2>/dev/null)"
					LastStatus=$?
					if [ $LastStatus -eq 0 ] ; then  # On accounts without shell, this reports "This account is currently not available." to stdout.
						for CurContainerPid in $TempPidsCollection ; do
							if Is_IntegerNr "$CurContainerPid" ; then
								if [ "$(printf '%s' " $SignaledContainersPids " | grep -e " $CurContainerPid ")" = "" ] ; then SignaledContainersPids="$SignaledContainersPids $CurContainerPid" ; fi
								if [ ! -f "${PidsNamesPath}/${CurContainerPid}.txt" ] ; then
									CTID="$(ContainerVeidFromGuestPid_LXC $CurPid)"
									if [ "$CTID" != "" ] ; then printf '%s' "$CTID" > "${PidsNamesPath}/${CurContainerPid}.txt" ; fi
								fi
							fi
						done
					fi
					if [ $ParallelizeStops -gt 0 ] ; then
						LogProgram 4 "Fork pidfile: ${ContainersPidsDir}/${UserAccount}/ALL.VPS.stop.pid"
						LogProgram 4 "[fork][${UserAccount}]\$" lxc-autostart -a -s
						DaemonizeCommand "${ContainersPidsDir}/${UserAccount}/ALL.VPS.stop.pid" "$(WhereProgram lxc-autostart)" -a -s
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						if [ "$StopAllWhenStop" -gt 0 ] ; then
							TimedExecution $StopAllWhenStop 1 "${ContainersPidsDir}/${UserAccount}/${CTID}.stop.pid" lxc-autostart -a -s
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						else
							lxc-autostart -a -s
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					fi
				fi
			fi
			if [ $ParallelizeStops -gt 0 ] ; then
				TimeEndS=$(($TimeBeginS + $ParallelizeStops))
				RemainingTimeS=$(($TimeEndS - $(date +%s)))
				AliveContainersPids="$SignaledContainersPids"
				TempPidsCollection=''
				for CurPid in $AliveContainersPids ; do
					if [ "$(pstree $CurPid)" != "" ] ; then
						TempPidsCollection="$TempPidsCollection $CurPid"
					fi
				done
				AliveContainersPids="$TempPidsCollection"
				if [ $RemainingTimeS -gt 0 ] && [ ! -f "$MainControllerStopFile" ] && [ "$(ps -A -o cmd | grep -ie '^\[lxc monitor\] ')${AliveContainersPids}" != "" ] ; then
					LogProgram 2 "Waiting up to $RemainingTimeS more seconds before killing alive containers" 1>&2
					LogProgram 4 "AliveContainersPids: $AliveContainersPids" 1>&2
					GlobalWaited=1
				fi
				while [ $RemainingTimeS -gt 0 ] && [ ! -f "$MainControllerStopFile" ] && [ "$(ps -A -o cmd | grep -ie '^\[lxc monitor\] ')${AliveContainersPids}" != "" ] ; do
					sleep 2
					TempPidsCollection=''
					for CurPid in $AliveContainersPids ; do
						if [ "$(pstree $CurPid)" != "" ] ; then
							TempPidsCollection="$TempPidsCollection $CurPid"
						fi
					done
					AliveContainersPids="$TempPidsCollection"
					RemainingTimeS=$(($RemainingTimeS - 2))
				done
			fi
		else
			if [ $ParallelizeStops -gt 0 ] ; then
				TimeEndS=$(($TimeBeginS + $ParallelizeStops))
				RemainingTimeS=$(($TimeEndS - $(date +%s)))
				AliveContainersPids="$SignaledContainersPids"
				TempPidsCollection=''
				for CurPid in $AliveContainersPids ; do
					if [ "$(pstree $CurPid)" != "" ] ; then
						TempPidsCollection="$TempPidsCollection $CurPid"
					fi
				done
				AliveContainersPids="$TempPidsCollection"
				if [ $RemainingTimeS -gt 0 ] && [ ! -f "$MainControllerStopFile" ] && [ "$AliveContainersPids" != "" ] ; then
					LogProgram 2 "Waiting up to $RemainingTimeS more seconds before killing alive containers" 1>&2
					GlobalWaited=1
				fi
				while [ $RemainingTimeS -gt 0 ] && [ ! -f "$MainControllerStopFile" ] && [ "$AliveContainersPids" != "" ] ; do
					sleep 2
					TempPidsCollection=''
					for CurPid in $AliveContainersPids ; do
						if [ "$(pstree $CurPid)" != "" ] ; then
							TempPidsCollection="$TempPidsCollection $CurPid"
						fi
					done
					AliveContainersPids="$TempPidsCollection"
					RemainingTimeS=$(($RemainingTimeS - 2))
				done
			fi
		fi

		# Second round: KILLING LISTED CONTAINERS
		IFS="$(printf '\n\b')" ; for CurContainer in $SequenceList ; do unset IFS
			# Syntax for LXC container: lxc|CTID|UserAccount|WaitForMaxLoad|ReserveSecondsBeforeNext|StopTimeoutSeconds|WaitForFile|ExistingFileToComplete
			#lxc|myweb|lxc|60|8|120
			CTID="$(printf '%s' "$CurContainer" | cut -sf 2 -d '|')"
			CTID="$(expr "$CTID" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
			UserAccount="$(printf '%s\n' "$CurContainer" | cut -sf 3 -d '|')"
			UserAccount="$(expr "$UserAccount" : '[ ]*\(.*[^ ]\)[ ]*$')"  # Only trim
			if [ "$UserAccount" != "" ] ; then
				CtStatus="$(sudo -iu "$UserAccount" -- lxc-info -n "$CTID" 2>/dev/null)"
			else
				CtStatus="$(lxc-info -n "$CTID" 2>/dev/null)"
			fi
			# Force fastest action
			Parms_StopTimeoutOrWait='--nowait'
			StopTimeoutSeconds=0
			if [ "$(printf '%s\n' " $CtStatus " | grep -ie 'State:')" != "" ] ; then
				if [ "$(printf '%s\n' " $CtStatus " | sed -e 's| ||g' | grep -ie 'State:STOPPED$')" = "" ] ; then
					if [ "$UserAccount" != "" ] ; then
						LogProgram 2 "Killing LXC container for ${UserAccount}: $CTID" 1>&2
						if [ "$LxcStartProgram" = "lxc-unpriv-start" ] ; then
#							OtherActiveVps_Nr=$(sudo -iu "$UnprivilegedUser" -- lxc-ls --active -1 -F NAME | grep -ve "^${CTID}$" | wc -l)
							OtherActiveVps_Nr=$(sudo -iu "$UserAccount" -- lxc-ls --active -1 -F NAME | grep -ve "^${CTID}$" | wc -l)
							if [ $OtherActiveVps_Nr -eq 0 ] ; then
								# Only if this is the last one to be stopped.
#								loginctl disable-linger "$UnprivilegedUser"
								loginctl disable-linger "$UserAccount"
							fi
						fi
						LogProgram 4 "Fork pidfile: ${ContainersPidsDir}/${UserAccount}/${CTID}.stop.pid"
						LogProgram 4 "[fork][${UserAccount}]\$" sudo -iu "$UserAccount" -- lxc-stop --kill $Parms_StopTimeoutOrWait -n "$CTID"
						DaemonizeCommand "${ContainersPidsDir}/${UserAccount}/${CTID}.stop.pid" "$(WhereProgram sudo)" -iu "$UserAccount" -- lxc-stop --kill $Parms_StopTimeoutOrWait -n "$CTID"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						LogProgram 2 "Killing LXC container: $CTID" 1>&2
						UserAccount=root
						if [ "$LxcStartProgram" = "lxc-unpriv-start" ] ; then
							OtherActiveVps_Nr=$(sudo -iu "$UnprivilegedUser" -- lxc-ls --active -1 -F NAME | grep -ve "^${CTID}$" | wc -l)
							if [ $OtherActiveVps_Nr -eq 0 ] ; then
								# Only if this is the last one to be stopped.
								loginctl disable-linger "$UnprivilegedUser"
							fi
						fi
						LogProgram 4 "Fork pidfile: ${ContainersPidsDir}/${UserAccount}/${CTID}.stop.pid"
						LogProgram 4 "[fork][${UserAccount}]\$" lxc-stop --kill $Parms_StopTimeoutOrWait -n "$CTID"
						DaemonizeCommand "${ContainersPidsDir}/${UserAccount}/${CTID}.stop.pid" "$(WhereProgram lxc-stop)" --kill $Parms_StopTimeoutOrWait -n "$CTID"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			fi
		done

		# Third round: OTHER/UNKNOWN CONTAINERS TO KILL
		if [ "$StopAllForUsers" != "" ] || [ "$StopAllWhenStop" -gt 0 ] ; then
			AliveContainersPids=''
			for CurPid in $SignaledContainersPids ; do
				if [ "$(pstree $CurPid)" != "" ] ; then AliveContainersPids="$AliveContainersPids $CurPid" ; fi
			done
			if [ "$(ps -A -o cmd | grep -ie '^\[lxc monitor\] ')${AliveContainersPids}" != "" ] ; then
				LogProgram 2 "Killing undiscovered containers" 1>&2
				IFS="$(printf '\n\b')" ; for UserAccount in $VerifiedAccounts ; do unset IFS
					LogProgram 4 "Fork pidfile: ${ContainersPidsDir}/${UserAccount}/ALL.VPS.stop.pid"
					LogProgram 4 "[fork][${UserAccount}]\$" sudo -iu "$UserAccount" -- lxc-autostart -a --kill
					DaemonizeCommand "${ContainersPidsDir}/${UserAccount}/ALL.VPS.stop.pid" "$(WhereProgram sudo)" -iu "$UserAccount" -- lxc-autostart -a --kill
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				done
				if [ "$StopAllForUsers" = "*" ] || [ "$StopAllWhenStop" -gt 0 ] ; then
					if [ "$(printf '%s\n' "$VerifiedAccounts" | grep -e '^root$')" = "" ] ; then
						# root
						UserAccount=root
						LogProgram 4 "Fork pidfile: ${ContainersPidsDir}/${UserAccount}/ALL.VPS.stop.pid"
						LogProgram 4 "[fork][${UserAccount}]\$" lxc-autostart -a --kill
						DaemonizeCommand "${ContainersPidsDir}/${UserAccount}/ALL.VPS.stop.pid" "$(WhereProgram lxc-autostart)" -a --kill
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			fi
		fi

		# Forth round: KILLING PROCESSES
		AliveContainersPids=''
		for CurPid in $SignaledContainersPids ; do
			if [ "$(pstree $CurPid)" != "" ] ; then AliveContainersPids="$AliveContainersPids $CurPid" ; fi
		done
		if [ "$AliveContainersPids" != "" ] ; then
			# Previous "lxc-autostart -a --kill" where daemonized and can be doing some good.
			sleep 1
			for CurPid in $AliveContainersPids ; do
				CurContainer="$(cat "${PidsNamesPath}/${CurPid}.txt" 2>/dev/null)"
				if [ "$CurContainer" != "" ] ; then CurContainer=" ${ParO}${CurContainer}${ParC}" ; fi
				LogProgram 2 "Killing PID: ${CurPid}${CurContainer}" 1>&2
				KillPid $CurPid 1
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			done
		else
			if [ $GlobalWaited -gt 0 ] ; then 
				LogProgram 3 "Global stop was completed in time."
			fi
		fi
	fi
	if [ $StatusCode -ne 0 ] ; then
		LogProgram 4 "Stops_LXC() completes with exit-code: $StatusCode"
	fi
	return $StatusCode
}

#[/initcontainers]


##### 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
		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

	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

	return $StatusCode
}

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

	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

#[initcontainers]
	local CurType=''
	
	if [ $StatusCode -eq 0 ] && [ "$ChildId" = "" ] && [ -d /etc/initcontainers/pre-stop.d ] ; then
		run-parts /etc/initcontainers/pre-stop.d
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	for CurType in $(printf '%s\n' "$TypesOrder" | tr -s ',' '\n' | tac) ; do	# Inverse order
		if [ ! -f "$MainControllerStopFile" ] ; then
			CurType="$(Lowercase "$CurType")"
			case "$CurType" in
				"ovz" )
					LogProgram 2 "W: Stop sequence left to $CurType environment." 1>&2
					;;
				"lxc" )
					Stops_LXC "$@"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					;;
				"chroot" )
					LogProgram 1 "E: Shutdown not implemented for $CurType containers.\nMake sure chroot is not specified at TypesOrder configuration parameter." 1>&2
					LastStatus=63 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					;;
				* )
					LogProgram 1 "E: Unsupported containerization system: $CurType" 1>&2
					LastStatus=50 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					;;
			esac
		fi
	done
	if [ $StatusCode -eq 0 ] && [ "$ChildId" = "" ] && [ -d /etc/initcontainers/post-stop.d ] ; then
		run-parts /etc/initcontainers/post-stop.d
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
#[/initcontainers]
	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

#[initcontainers]
	local CurType=''
	
	if [ $StatusCode -eq 0 ] && [ "$ChildId" = "" ] && [ -d /etc/initcontainers/pre-start.d ] ; then
		run-parts /etc/initcontainers/pre-start.d
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	for CurType in $(printf '%s\n' "$TypesOrder" | tr -s ',' ' ') ; do
		if [ ! -f "$MainControllerStopFile" ] ; then
			CurType="$(Lowercase "$CurType")"
			case "$CurType" in
				"ovz" )
					Launches_OpenVZ "$@"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					;;
				"lxc" )
					Launches_LXC "$@"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					;;
				"chroot" )
					Launches_Chroot "$@"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					;;
				* )
					LogProgram 1 "E: Unsupported containerization system: $CurType" 1>&2
					LastStatus=50 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					;;
			esac
		fi
	done
	if [ $StatusCode -eq 0 ] && [ "$ChildId" = "" ] && [ -d /etc/initcontainers/post-start.d ] ; then
		run-parts /etc/initcontainers/post-start.d
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
#[/initcontainers]
	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
#[initcontainers]
	local TypesOrder_Default=''
	local CpuCoresNr=''
	if [ $ReadOnly -eq 0 ] ; then
		if [ ! -f "${CurrentConfigDir}/containers.list" ] ; then
			printf '%s\n' "Creating empty ${CurrentConfigDir}/containers.list"
			printf '%s\n' "# VPS ordered launch sequence" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# " >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# CTID: ConTainer IDentifier. \"*\" wildcard can be specified instead, to mean all unspecified containers of same class." >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# CT-Path: ConTainer Path: Abrolute directory where is there OS root filesystem" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# WaitForMaxLoad: To wait until host CPUs load is under specified percent. Continue launching this container after this accomplishes. -1 to not wait this." >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "#                 A plus \"+\" sign can be appended to flexibilize CPUs load limit by slightly increasing WaitForMaxLoad after a while" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "#                 Double plus \"++\" sign can be appended to flexibilize CPUs load limit by increasing WaitForMaxLoad progressively" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# ReserveSecondsBeforeNext: Number of seconds to guarantee for this container startup before continue to next one." >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "#                           Optionally, this can be a pair of numbers separed by \"-\". Second number will mean a time limit for WaitForMaxLoad (each period will increase WaitForMaxLoad in +WaitForMaxLoad percent)" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# StopTimeoutSeconds: Number of seconds to kill a container if it does not stop normally. -1 to not kill for this line." >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# WaitForFile: If not empty, do not Not launch this container until specified file is found." >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# ExistingFileToComplete: If not empty, wait to specified file exists before continue next container startup." >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# UserAccount: To execute launch as specified user" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# " >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# Syntax for OpenVZ container: ovz|CTID|WaitForMaxLoad|ReserveSecondsBeforeNext|WaitForFile|ExistingFileToComplete" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "#ovz|777|80|10" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# Syntax for LXC container: lxc|CTID|UserAccount|WaitForMaxLoad|ReserveSecondsBeforeNext|StopTimeoutSeconds|WaitForFile|ExistingFileToComplete" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# Note: A line can begin with negation ! sign to mark that container as excluded of wildcard matches." >> "${CurrentConfigDir}/containers.list"
			UnprivilegedUser="$(IniVarValue "/etc/ctctl/system.conf" UnprivilegedUser '' "lxc" = "$EndVariableSymbol" "/etc/ctctl/system.defaults")"
			printf '%s\n' "#lxc|myweb|${UnprivilegedUser}|100|8|120" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "# Syntax for chroot container: chroot|CT-Path|UserAccount|WaitForMaxLoad|ReserveSecondsBeforeNext|StopTimeoutSeconds|WaitForFile|ExistingFileToComplete" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "#chroot|/home/user/myguest|root|100|8|120" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "" >> "${CurrentConfigDir}/containers.list"
#			CpuCoresNr=$(ArgumentsNumber () { printf '%s' $#; }; ArgumentsNumber $(grep "processor" /proc/cpuinfo | cut -f 2 -d ':'))
#			if ! Is_IntegerNr "$CpuCoresNr" ; then CpuCoresNr=1 ; fi
#			if [ $CpuCoresNr -lt 2 ] ; then CpuCoresNr=2 ; fi
#			printf '%s\n' "#ovz|*|$(($CpuCoresNr * 100))|5" >> "${CurrentConfigDir}/containers.list"
#			printf '%s\n' "#lxc|*|*|$(($CpuCoresNr * 100))|5|120" >> "${CurrentConfigDir}/containers.list"
#			printf '%s\n' "#chroot|*|*|$(($CpuCoresNr * 100))|5|120" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "#ovz|*|100|5" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "#lxc|*|*|100|5-60|120" >> "${CurrentConfigDir}/containers.list"
			printf '%s\n' "#chroot|*|*|100|5|120" >> "${CurrentConfigDir}/containers.list"
		fi
		mkdir -p /etc/initcontainers/pre-start.d
		mkdir -p /etc/initcontainers/post-start.d
		mkdir -p /etc/initcontainers/pre-stop.d
		mkdir -p /etc/initcontainers/post-stop.d
	fi
	if Is_Executable vzctl ; then
		if [ "$TypesOrder_Default" != "" ] ; then TypesOrder_Default="${TypesOrder_Default}," ; fi
		TypesOrder_Default="${TypesOrder_Default}ovz"
	fi
	if Is_Executable lxc-start ; then
		if [ "$TypesOrder_Default" != "" ] ; then TypesOrder_Default="${TypesOrder_Default}," ; fi
		TypesOrder_Default="${TypesOrder_Default}lxc"
	fi
	if [ "$TypesOrder_Default" = "" ] ; then TypesOrder_Default='chroot' ; fi
	TypesOrder="$(GetSetLocalConfig "$ReadOnly" TypesOrder '' "$TypesOrder_Default" "\n# Ordered sequence to launch containers of each hypervisor type ${ParO}comma-separated${ParC}.\n#TypesOrder=ovz,lxc,chroot")"
	ParallelizeStops="$(GetSetLocalConfig "$ReadOnly" ParallelizeStops '' 0 "\n# Seconds number to wait for whole set of containers on a global stop. Set to zero for serial stops instead of parallel.\n# ${ParO}With hypervisors like OpenVZ this is ignored${ParC}")"  #do
	if ! Is_IntegerNr "$ParallelizeStops" ; then
		LogProgram 2 "W: Bad value for ParallelizeStops. Assuming 0."
		#LastStatus=65 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ParallelizeStops=0
	fi
	StopAllWhenStop="$(GetSetLocalConfig "$ReadOnly" StopAllWhenStop '' 0 "\n# On a global stop: If to stop any other running container ${ParO}Timeout seconds per each${ParC} or only listed ones ${ParO}0${ParC}.\n# ${ParO}With hypervisors like OpenVZ zero is assumed${ParC}")"
	if ! Is_IntegerNr "$ParallelizeStops" ; then
		LogProgram 2 "W: Bad value for StopAllWhenStop. Assuming 0."
		#LastStatus=65 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		StopAllWhenStop=0
	fi
	if [ -d /run ] ; then
		ContainersPidsDir="/run/${ServiceName}.d"
	else
		# Old FHS
		ContainersPidsDir="/var/run/${ServiceName}.d"
	fi
	if [ $(id -u) -ne 0 ] ; then
		ContainersPidsDir="${UserTempDir}/containers.run"
	fi
	if Is_Executable lxc-unpriv-start ; then
		# https://discuss.linuxcontainers.org/t/an-lxc-container-does-not-start-on-debian-11-currently-testing-version/10474
		# https://unix.stackexchange.com/questions/655898/cannot-start-unprivileged-lxc-containers-on-debian-11-bullseye
		LxcStartProgram='lxc-unpriv-start'
	else
		LxcStartProgram='lxc-start'
	fi
	LxcVersion="$(lxc-start --version 2>/dev/null | cut -f 2 -d ' ' | cut -f 1-2 -d '.')"
	if Is_IntegerNr "$(printf '%s' "$LxcVersion" | cut -f 1 -d '.')" ; then
		# Reformat version level to appear as "201" for LXC 2.1
		# https://discuss.linuxcontainers.org/t/lxc-2-1-has-been-released/487
		LxcSyntaxLevel="$(printf '%s' "$LxcVersion" | cut -sf 2 -d '.')"
		if ! Is_IntegerNr "$LxcSyntaxLevel" ; then LxcSyntaxLevel='00' ; fi
		while [ ${#LxcSyntaxLevel} -lt 2 ] ; do LxcSyntaxLevel="0${LxcSyntaxLevel}" ; done
		LxcSyntaxLevel="$(printf '%s' "$LxcVersion" | cut -f 1 -d '.')${LxcSyntaxLevel}"
	else
		LxcVersion=''
		LxcSyntaxLevel=''
	fi
#[/initcontainers]
	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
	
	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.
		if [ -f "$MainControllerStopFile" ] ; then LogProgram 2 "W: File $MainControllerStopFile avoids main bucle." ; fi
		while [ ! -f "$MainControllerStopFile" ] ; do
			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
		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 ;;
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
		;;
	"" )
		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
#[initcontainers]
if [ $StatusCode -ne 0 ] ; then
	LogProgram 4 "initcontainers completes with exit-code: $StatusCode"
fi
#[/initcontainers]
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
