#!/bin/sh
### BEGIN INIT INFO
# Provides:	matromus
# Required-Start:	$local_fs
# Should-Start:	$syslog ssh
# Required-Stop:	$local_fs
# Should-Stop:	$syslog ssh
# Default-Start:	2 3 4 5
# Default-Stop:	0 1 6
# Short-Description:	Matromu instances hypervisor
# Description:		Launches and monitors multiple virtual machines running Matromu in background
# X-Interactive: false
### END INIT INFO
# RHL fields:
# chkconfig: 2345 80 30
# description: Matromu instances hypervisor

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

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

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

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

# Service development ToDo:
#	- "matromus start ABC" acts as if ABC machine exists when not.
#	- matromus 2.1.3-2 : "sudo matromus list" shows both "bell3 [E]" and "bell3-matromus [R]"
#	  also "lxc4 [E]" and "lxc4-matromus [R]"
#	- Sure this is caused due to some .service dependency (bell and denia affected):
#		de maig 25 10:41:33 mont systemd[1]: Dependency failed for Matromu instances hypervisor.
#		de maig 25 10:41:33 mont systemd[1]: matromus.service: Job matromus.service/start failed with result 'dependency'.
#	- (Re-test with updated init template) "matromus start UnknownVM" does not complain about non-existing child.
#	- Cal que Matromu sigui completament síncron per controlar l'aturada VMs. Matromu ha de tenir Qemu com a procés principal, i bifurcar visualitzador quan calgui visualitzador.
#	  A més a més, cal explorar les possibilitats de qemu-guest-agent i dels paràmetres StopCommand&StopTimeoutSec previstos per matromu (a ser executats per matromus)
#	- "stop" action must trigger VMs shutdown: either with qemu-guest-agent and with matromus-guest-agent (write/read stop file signal)
#	- Unprivileged VM: Cuenta de usuario «kvm» establecida en configuración para las ejecuciones desde sistema (Init)
#	- Implementar anclaje de tipo «Init» a nivel de usuario, para que las sesiones de usuario arranquen sus VM (Anclaje a través de .bashrc y .profile como hace peinddd)
#	- Quizás conviene como opción que Matromus se quede funcionando para relanzar VMs con "respawn" establecido.
#	- sudo journalctl -b -p warning
#	  systemd[1]: matromus@@.service: Failed with result 'exit-code'.


##### 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="matromus"
# 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 matromu qemu-img/qemu|qemu-utils nc/netcat"
RecommendedSoftware="diskimg-resize"
SuggestedSoftware=""
#[matromus]
SystemConfigDir="/etc/matromu"
#[/matromus]
# 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=0
# 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=0
# TimeoutStartSec: When starting main program, if it doesn't complete ond/or fork, after this number of seconds systemd/upstart will shut down the service. -1 for infinite (recommended on MainControllerStays==0)
TimeoutStartSec=-1
# TimeoutStopSec: When stopping service, if it doesn't complete, after this number of seconds systemd will kill main process. -1 for infinite
TimeoutStopSec=300
# GentleStopTimeoutS: Maximum seconds to wait daemons at stop signal, before forcing kill processes. Intended for childs and {main x ChildsNr}
GentleStopTimeoutS=4
# StopOnFailure: Allow (1) Systemd to call "stop" service on start failure, or let service remain as is (0)
StopOnFailure=0
# Leave empty to not treat child objects
ChildSingularName="VM"
# Leave empty to not treat child objects. Plural name has implies profiles directory names.
ChildsPluralName="machines"
# ChildProfileExtension: Leave empty to assume profiles in subdirectories instead of files
ChildProfileExtension="matromu"
# 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=90
# Configuration default value for _MoreLoadsInterval
Childs_MoreLoadsInterval_Default=1
# If "status" action shows (1) processes and memory state or not (0). If not, only customized Status_More and ProcessStatus_More functions will be called.
StatusProcecessShow=1
# DataIsPublic: If Configuration and logs can be read by others (1) or not (0)
DataIsPublic=1
# LogRotation: Logrotate frequency to setup (see man logrotate). Empty to not configure rotation of log files.
LogRotation="size 1M"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ServiceHelp ()
{
	local SudoPrefix=''
	local SudoSuffix=''
	local ChildActions=''
	local InstallerActions=''
	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"
#[matromus]
#	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
	matromu "$ChildProfile"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#[/matromus]
	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
			if [ "$ChildId" = "@" ] ; then
				printf '%s\n' "${sWARN}W: $ChildSingularName $ChildId called but it's a Systemd key to specify main service.${fRESET}" 1>&2
			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
		fi
	else
		printf '%s\n' "${sERROR}E: $ChildSingularName not specified.${fRESET}" 1>&2
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

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

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

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

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

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

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

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

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

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

# Function taken from script.sh template.

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

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

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

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


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


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


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

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

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

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

	return $StatusCode
}

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

#[matromus]
	if [ -d /etc/matromus ] ; then
		# Old path; that was the time when simple Matromu didn't use anything in /etc
		mv /etc/matromus "$SystemConfigDir"
	fi
#[/matromus]
	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

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

Stop_Post ()

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

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

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

Start_Pre ()

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

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

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

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

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

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

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

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

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

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

CreateChild ()
{
#[matromus]
	local ChildId="$1"
	local ChildProfile=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" != "" ] ; then
		ChildProfile="${Childs_path_available}/${ChildId}.${ChildProfileExtension}"
		if [ ! -f "$ChildProfile" ] ; then
			# .img causa que Matromu predetermini DisplayChannels="vnc" i bifurca la crida a qemu.
			touch "${Childs_path_available}/${ChildId}.iso"
			TimedExecution 2 1 "" matromu "${Childs_path_available}/${ChildId}.iso" > /dev/null 2>&1
			rm -f "${Childs_path_available}/${ChildId}.iso"
			if [ -f "$ChildProfile" ] ; then
				echo "Profile example created as: $ChildProfile"
				echo "Please, edit this file to setup your virtual machine."
			else
				echo "E: Profile file NOT created." 1>&2
				LastStatus=100 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			echo "ERROR: $ChildSingularName \"${ChildId}\" already exists." 1>&2
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		echo "ERROR: $ChildSingularName identifier not specified." 1>&2
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
#[/matromus]
}

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