#!/bin/bash
#
# __copy1__
# __copy2__
#
# bin/__TOOLKIT__-functions.sh - common functions for bash scripts
# v2.22 (2025-03-15)

# early hooks
cleanup() { :; }

# abort on undefined vars (mostly typos)
#
set -u

trap 'echo -e "\n*INTR*\n"; cleanup; exit 255' 1 2 3
trap 'echo -e "\nunexpected error $? at $LINENO of $0\n"; cleanup' ERR
trap 'cleanup' EXIT

# set distro env vars
export DISTRIB_ID DISTRIB_RELEASE DISTRIB_CODENAME DISTRIB_DESCRIPTION DISTRIB_FULL_ID
export DISTRIB_HAS_UPSTART DISTRIB_HAS_APPARMOR
export DISTRIB_HAS_SYSTEMD DISTRIB_USES_SYSTEMD
kusa-bootstrap --check >/dev/null || exit 1
eval $(ku-distroenv)

# hardcoded here
#
export TOOLKIT="kusa"
export LIBDIR="/usr/lib/$TOOLKIT"
export PRJNAME="${TOOLKIT}"
export CONFDIR="/etc/${TOOLKIT}"

# reset this at any inclusion of this file
#
export SEARCH_PATH_ADD=


#------------------------------------------------------------------------------
# error handling
#------------------------------------------------------------------------------

## == error handling ==

## === exit_err status [message] ===
##
## interrompe l'esecuzione dello script, uscendo con lo stato di errore
## ''status'' e con l'eventuale messaggio ''message''
##
exit_err()
{
	local status=$1 ; shift
	local message="$@"

	[ X"$message" != X ] && {
		error "$message"
	}
	exit $status
}

## === exit_missing_define varname ===
##
## interrompe l'esecuzione dello script uscendo con stato di errore "1"
## ed indicando che la variabile ''varname'' non e` definita nel database
## di installazione, l'uso tipico e` questo:
## {{{
## myvar=$(jtconf mysection.myvar) || exit_missing_define mysection.myvar
## }}}
##
exit_missing_define()
{
	local define="$1"
	exit_err 1 "missing declaration of '$define'\n\n" \
		"(you should define it in $CONFDIR/conf.d/* files)"
}

## === error message ===
##
## scrive messaggio di errore su stderr, preceduto dal tag ''error!''
## in grassetto
##
error()
{
	local message="$@"
	echo -e "\n${KU_BOLD}error!${KU_NORM} $message" >&2
}



## == decho string(s) ==
##
## echoes on stderr, if $F_DEBUG=true
##
decho()
{
	$F_DEBUG || return 0
	echo -e "D# " "$@" >&2
}


#------------------------------------------------------------------------------
# temps, dirs
#------------------------------------------------------------------------------

## == files and dirs handling ==

## === removetemp path ===
##
## rimuove recursivamente i files e/o le directories indicati da ''path'',
## solo se la variabile di environment F_REMOVE_TEMPS=true (il default,
## questa variabile e` false solo se si passa l'opzione '''-k''' al lancio)
##
removetemp()
{
	local path="$1"
	$F_REMOVE_TEMPS && rm -rf "$path"
	return 0
}

#------------------------------------------------------------------------------
# ritorna il path completo di un file cercandolo nel searchpath
#------------------------------------------------------------------------------
## === filepath filename [mode1 [mode2]] ===
##
## cerca nella directory del modulo e/o in quella corrente il file ''filename''
## e ritorna su stdout il path trovato, oppure nulla se non lo trova, l'uso
## normale e` questo e con queste regole:
## {{{
## myfile=$(filepath myfilename) || exit_err 1 "file 'myfilename' non trovato"
## }}}
##
##  * il file viene cercato in:
##   * custom dirs (variabile common.search_path se definita)
##   * __CONF__/files
##   * directory corrente
##   * $MODRUNDIR, la directory del modulo corrente in $RUNDIR (la directory
##   temp, copia di __LIB__/modules/$MODNAME, scrivibile, dove gli script possono
##   quindi creare files -- dovrebbe essere identica alla directory corrente, a
##   meno che non ci sia spostati)
##
##  * se non sono passati altri parametri viene cercato un file che sia
##  leggibile; e` possibile passare oltre al filename anche uno o due attributi
##  che devono essere soddisfatti perche` la ricerca sia considerata valida,
##  gli attributi sono quelli standard della shell (r=read, x=execute, d=dir),
##  quindi ad esempio per cercare una directory invece di un file, usare
##  {{{
## filepath nomedir -d
##  }}}
##
filepath()
{
	local file="$1"
	local flag1=${2:-"-r"}
	local flag2=${3:-"-r"}

	local search=
	local dir=
	local subdir=
	local init_subdir=
	local path=
	local extension=
	
	search="$SEARCH_PATH"
	search="$search $CONFDIR/files"
	search="$search $(pwd)"

	[ "$MODNAME" != "" -a $MODRUNDIR != $(pwd) ] && {
		search="$search $MODRUNDIR"
	}
	[ "X$SEARCH_PATH_ADD" != "X" ] && {
		search="$search $SEARCH_PATH_ADD"
	}
	[ -f $MODRUNDIR/__search_path_append ] && {
		search="$search $(cat $MODRUNDIR/__search_path_append)"
	}

	if $DISTRIB_HAS_UPSTART
	then
		init_subdir="upstart"
	else
		init_subdir="init"
	fi

	for dir in $search
	do
		#for subdir in /$DISTRIB_FULL_ID /$DISTRIB_ID-$DISTRIB_CODENAME $DISTRIB_CODENAME /$DISTRIB_ID /$init_subdir ""
		for subdir in $(list_distro_variants "/%s") /$init_subdir ""
		do
			#for extension in .$DISTRIB_FULL_ID .$DISTRIB_ID-$DISTRIB_CODENAME .$DISTRIB_CODENAME .$DISTRIB_ID ""
			for extension in $(list_distro_variants ".%s") ""
			do
				path="$dir$subdir/$file$extension"

				decho "filepath(): searching '$file' as '$path'"
				[ -f "$path" -o -d "$path" ] && {
					[ $flag1 "$path" -a $flag2 "$path" ] || continue
					echo "$path"
					decho "filepath(): found '$path'"
					return 0
				}
			done
		done
	done
	decho "filepath(): '$file' not found"
	return 1	# file/dir not found
}


search_path_append()
{
	local file="$MODRUNDIR/__search_path_append"
	echo "$@" >>$file
	return 0
}





## === installfile filename dest [owner [mode [do_parse_true_false]]] ===
##
## installa un templatefile ricercandolo nel searchpath,
## eseguendo eventuale backup del file originale se esiste, e
## operando la sostituzione delle variabili (solo se il file
## e` di testo, altrimento viene copiato senza espansioni)
##
## i files vengono installati solo se risultano differenti da
## quelli eventualmente gia` presenti sul sistema (in questo
## modo non si generano falsi positivi)
##
## in caso di installazione la variabile di environment FILES_HAS_INSTALLED
## viene impostata a true, e FILES_INSTALLED (una lista separata da spazi)
## viene aggiornata aggiungendo il nome del file, queste due variabili
## possono essere controllate nello script di '''run-install''' per
## decidere se compiere o meno certe operazioni, assieme alla piu` generica
## SOMETHING_HAS_CHANGED, anch'essa posta a true in caso di installazione
##
installfile()
{
	local file="$1"
	local to="$2"
	local own=${3:-""}
	local perms=${4:-""}
	local do_parse=${5:-true}

	local from=
	local tmpfile=
	local backup=
	local dir=
	local tempdefines=

	# sanityze args
	case $do_parse in
	 true|false)	;;
	 *)	echo >&2
	 	echo " wrong parm '$do_parse'" >&2
		echo " on installfile $*" >&2
		echo >&2
		echo " usage: installfile filename dest [owner [mode [do_parse_true_false]]]\n" >&2
		echo >&2
		return 1
	 	;;
	esac

	case "$file" in
		./*|/*) from="$file"
			[ -e "$file" ] || {
				error "file '$file' not exists"
				return 1
			}
			;;
		*)	from=$(filepath "$file")	|| {
				error "filepath(): can't find file '$file'"
				return 1
			}
			;;
	esac

	# se la destinazione finisce con "/" ed esiste la directory
	# appende il nome del file originale alla directory
	#
	if [ -d $to ]
	then
		if echo "$to" | grep -q "/$"
		then
			to="$to$(basename $file)"
		else
			error "destination '$to' is a directory"
			return 1
		fi
	else
		if echo "$to" | grep -q "/$"
		then
			error	"error: you asked to install '$file' into '$to'\n" \
				"but destination is not a directory"
			return 1
		fi
	fi

	#$VERBOSE && printf "\r\033[K checking $to ... \r" >&2
	echocr " checking $to ... \r"

	# se non ho i permessi di installazione, esco senza errori
	# se non esisteva gia` da prima lo rimuovo per evitare falsi backup
	#
	###if [ -f "$to" ]
	###then
		###[ -w "$to" ] || return 0
	###else
		###touch "$to" 2>/dev/null || return 0
		###rm -f "$to"
	###fi

	backup="$BACKUPDIR/$to.$(date '+%Y%m%d')"
	tmpfile=$(mktemp $RUNDIR/tmp-XXXXXXXXXX) || return 1

	$do_parse && {
		case $(file --dereference "$from") in
			*text*)	;;
			*)	do_parse=false ;;
		esac
	}

	# temporary database definitions can be placed into this file by run-pre
	# scripts; this file is per-module, and is cleaned before module run
	#
	##[ -f $MODLOCALDB ] && {
		##tempdefines="--include $MODLOCALDB"
	##}

	if $do_parse
	then
		jtconf-parse $DFLAG --simple $tempdefines "$from" > "$tmpfile" || {
			error "install of '$to' failed, error parsing"
			return 1
		}
	else
		cp "$from" "$tmpfile" || {
			error "install of '$to' failed, copy error on $tmpfile"
			return 1
		}
	fi
	[ -f "$to" ] && {
		# both have zero size?
		[ ! -s "$tmpfile" -a ! -s "$to" ] && {
			removetemp "$tmpfile"
			__installfile_fixperms true "$to" "$own" "$perms"
			#$VERBOSE && printf "\r\033[K\r" >&2
			echocr
			return 0
		}
		# are equal? (not changed)?
		[ $(stat -c '%s' "$tmpfile") = $(stat -c '%s' "$to") ] && \
		   cmp "$tmpfile" "$to" >/dev/null && {
			removetemp "$tmpfile"
			__installfile_fixperms true "$to" "$own" "$perms"
			#$VERBOSE && printf "\r\033[K\r" >&2
			echocr
			return 0
		}
	}
	#$VERBOSE && printf "\r\033[K\r" >&2
	echocr

	echo " $DUMMYTAG install '$from' $perms $own" >&2
	echo " $DUMMYTAG     --> '$to'" >&2

	[ -f "$to" -a ! -f "$backup" ] && {
		echo " $DUMMYTAG  backup '$backup'" >&2
		$F_EXEC && {
			dir=$(dirname $backup)
			[ -d "$dir" ] || mkdir -p "$dir"
			mv "$to" "$backup" || return 1
		}
	}

	$F_EXEC || return 0

	mv "$tmpfile" "$to" || {
		error "installing file"
		removetemp "$tmpfile"
		return 1
	}
	# updates global vars
	#
	FILES_HAS_INSTALLED=true
	FILES_INSTALLED="$FILES_INSTALLED $to"
	SOMETHING_CHANGED=true

	echo "$(date '+%y%m%d%H%M%S') in_file $MODNAME $to" >>$CONFDIR/installhistory

	__installfile_fixperms false "$to" "$own" "$perms" "$backup"

	return 0
}

__installfile_fixperms()
{
	local verbose=$1
	local to=$2
	local own=$3
	local perms=$4
	local backup=${5:-""}
	local before=$(ls -l "$to")

	if [ "$own" = "" ]
	then
		if [ "$backup" != "" -a -f "$backup" ]
		then
			chown --reference="$backup" "$to"
		else
			chown root:root "$to"
		fi
	else
		chown $own "$to"
	fi

	if [ "$perms" = "" ]
	then
		if [ -f "$backup" ]
		then
			chmod --reference="$backup" "$to"
		else
			chmod 664 "$to"
		fi
	else
		chmod $perms "$to"
	fi

	$verbose || return 0

	local after=$(ls -l "$to")
	[ X"$before" != X"$after" ] && {
		echo "  warning: $before"
		echo "  newmode: $after"
	}

	return 0
}

## === uninstallfiles [--remove] file(s)
##
## annulla un'operazione di installfile(file): per ogni filname passato
## controlla se esiste un backup, e nel caso utilizza l'ultimo per sovascrivere
## quello corrente, altrimenti lo rimuove
##
uninstallfiles() {
	local force_remove=false
	local file=
	local backup=
	local fname=
	local now=$(date +%y%m%d%H%M%S)
	local last_action=

	[ "X$1" = "X--remove" ] && {
		force_remove=true
		shift
	}

	for file
	do
		[ -f "$file" ] || continue
		last_action=$(
			fgrep " $MODNAME " $INSTALLHISTORY | while read d a m f
			do
				 [ $m != $MODNAME ] && continue
				 [ "$f" = "$file" ] && echo "$d $a $m $f"
			done | tail -1
		)
		decho "uninstallfiles: last_action='$last_action'"
		last_action=$(echo "$last_action" | cut -d' ' -f2)
		case $last_action in
		  restore|rm_file)
		  	decho "uninstallfiles: '$file' already done ($last_action)"
			continue
			;;
		esac

		fname=$(echo "$file" | sed -e 's#^/##')
		$force_remove || {
			backup=$( (cd $BACKUPDIR; ls "$fname".20[0-9][0-9][0-9][0-9][0-9][0-9] 2>/dev/null | tail -1) )
		}

		if [ "$backup" != "" ]
		then
			echo "  [$MODNAME] uninstall '$file' (restored $(basename $backup))"
			(cd $BACKUPDIR; cp -a "$backup" "$file")
			(cd $BACKUPDIR; rm -f "$backup")
			SOMETHING_CHANGED=true
			echo "$now restore $MODNAME $file" >> $INSTALLHISTORY
		else
			echo "  [$MODNAME] uninstall '$file' (removed)"
			rm -f "$file"
			SOMETHING_CHANGED=true
			echo "$now rm_file $MODNAME $file" >> $INSTALLHISTORY
		fi
	done
}


## === purgefiles file(s)
##
## controlla se esistono i files passati come argomenti, e se esistono
## li rimuove, loggando l'operazione ed impostando true la variabile
## SOMETHIN_CHANGED
## 
purgefiles()
{
	local files=$(ls -d $@ 2>/dev/null) || :
	[ "$files" != "" ] && {
		echo "  purging: " $files
		rm -f $files
		SOMETHING_CHANGED=true
	}
	files=$(ls -d $@ 2>/dev/null) || :
	[ "$files" != "" ] && {
		putwarning "purgefiles" \
			"some files was not removed: $files"
	}
	return 0
}



## === wgetfile filename [wget options] ===
##
## lancia wget per prelevare il file 'filename', in modo
## quiet: il comando memorizza l'output diagnostico su un
## file temporaneo che viene mostrato solo in caso di errore
##
## inoltre se ''filename'' non e` un url ma un path
## invece di chiamare wgwet esegue una copia con /bin/cp
## nella directory corrente;
## ovviamente in questo caso NON vanno passate opzioni
## tipiche di wget
##
wgetfile()
{
	local file="$1"	; shift
	local outfile=
	local temphome=$(mktemp -d /tmp/wget-home-XXXXXX)
	local errfile="$temphome/errfile"
	local statfile="$temphome/statfile"

	# start a subshell so we can safely fake HOME
	(

	export HOME=$temphome

	[ ${WGET_USER:-""} != "" -a ${WGET_PASSWORD:-""} != "" ] && {
		:> $HOME/.wgetrc
		echo "user=$WGET_USER"		>>$HOME/.wgetrc
		echo "password=$WGET_PASSWORD"	>>$HOME/.wgetrc
	}

	echo -en "  getting $file ... "
	case "$file" in
	   *://*)	wget $* $file >$errfile 2>&1 || echo $? >$statfile
			;;
	   file:*)	file=$(echo $file | sed -e 's/^file://')
	   		/bin/cp -af $file . 2>$errfile || echo $? >$statfile
			;;
	   *)		/bin/cp -af $file . 2>$errfile || echo $? >$statfile
			;;
	esac

	if [ -s $statfile ]
	then
		echo "ERROR!"
		cat $errfile
	else
		echo 0 >$statfile
		echo "ok"
	fi

	) # subshell end

	local stat=$(cat $statfile)
	rm -rf "$temphome"
	return $stat
}

## === is_patched file [tag] ===
##
## controlla se il file ''file'' contiene o meno il tag ''tag''
## (oppure se non passato, quello indicato da $PATCH_TAG)
##
## ritorna true (0) se il file e` gia` patchato, altrimenti false
##
## <!> attenzione, il tag viene cercato in modo literal, quindi
## evitare di usare stringhe ambigue, ad esempio troppo corto, in
## quanto possono generare falsi positivi
##
is_patched()
{
	local file="$1"
	local tag="${2:-$PATCH_TAG}"
	fgrep -iq "$tag" "$file"
}


## === updatelink source target ===
##
## aggiorna il link simbolico da ''source'' a ''target'', se
## target non e` definito usa, come di norma per symlink, il
## basename di ''source''
##
## ''source'' puo` essere indifferentemente un file o una directory
##
## se ''target'' esiste, e ''source'' non esiste, la situazione
## verra` corretta rinominando (spostando fisicamente) target
## in source prima di creare il link; attenzione, se i due si
## trovano su filesystems differenti l'operazione puo` impiegare
## parecchio tempo e fallire per problemi di spazio
##
## se ''target'' esiste e ''source'' esiste, target viene rinominato
## in ''target.old'' (l'eventuale ''target.old'' non deve esistere)
## prima di eseguire il link
##
updatelink()
{
	local source="$1"
	local target="$2"
	local msg="create"

	if [ -L $target ]
	then
		actual=$(ls -ld "$target" | sed -e 's/.*-> //')
		if [ "$actual" != "$source" ]
		then
			msg="modify"
			rm -f "$target"
		else
			return 0	# already done
		fi
	else
		[ -d "$target" -o -f "$target" ] && {
			if [ -d "$source" -o -f "$source" ]
			then
				echo "  warning: renaming actual '$target' to '$target.old'"
				[ -e $target.old ] && {
					echo "  error: '$target.old' exists, can't rename"
					return 1
				}
				mv "$target" "$target.old" || return $?
			else
				echo "  warning: moving '$target' to '$source'"
				echo -n "	(this may take long time) ..."
				mv "$target" "$source" || return $?
				echo "ok"
			fi
		}
	fi

	echo "  $msg link $target -> $source"
	ln -s "$source" "$target" || return $?
	return 0
}


## === create_dir [--fixperms] path user:group [mode] ===
##
## crea directory indicata da path se non esiste, creando anche
## il path completo, in questo caso le directories intermedie
## se non esistono vengono create (vedi jtmkpath)
##
create_dir()
{
	local verbose=
	local fixperms=false
	[ X$1 = X--fixperms ] && {
		fixperms=true
		shift
	}
	local path=$1
	local user=${2:-}
	local mode=${3:-}
	local before=
	local after=

	$VERBOSE && verbose="-v"

	# primo giro, crea dir se non esiste, con relativo path
	# secondo giro, fix sempre dei permessi (se richiesto), memorizza stato
	#	prima per notificare se cambiato qualcosa
	# se il path non esiste (e si presume quindi venga creato)
	#	segna sempre cambiamento avvenuto
	#
	if [ -d "$path" ]
	then
		before=$(ls -ld "$path")
	else
		SOMETHING_CHANGED=true
	fi

	jtmkpath $verbose $path $user $mode || {
		local st=$?
		echo "error $st executing: jtmkpath $verbose $path $user $mode" >&2
		return $st
	}
	$fixperms && {
		jtmkpath --fixperms $path $user $mode || {
			local st=$?
			echo "error $st executing: jtmkpath --fixperms $path $user $mode" >&2
			return $st
		}
	}

	after=$(ls -ld "$path")
	[ "$before" != "" -a "$before" != "$after" ] && {
		echo "  warn! fix perms from: $before"
		echo "                    to: $after"
		SOMETHING_CHANGED=true
	}

	return 0
}





## == packages handling ==

## === is_installed package ===
##
## ritorna 0 se il package passato e' installato, 1 se non lo e'
##
is_installed()
{
	local pkg="$1"
	local nodeinstall=${2:-""}
	local stat=$(dpkg -s "$pkg" 2>&1 | grep "^Status")	|| return 1

	[ "$stat" = "" ]				&& return 1
	echo -e "$stat" | grep -q "not.installed"	&& return 1
	[ "$nodeinstall" != "nodeinstall" ] && {
		echo -e "$stat" | grep -q "deinstall"	&& return 1
	}
	return 0
}



## === restart_service service [prog] ===
##
## esegue restart del servizio ''service'' (/etc/init.d) oppure
## start se il servizio non e` attivo e se viene passato il
## nome del programma da controllare ''prog''
##
## esce senza fare nulla se si e` in una jail chroot, dato che
## quasi sicuramente si sta lavorando su un filesystem di comodo
## e quindi i servizi del sistema ospite non devono essere toccati
##
restart_service()
{
	local service=$1
	local prog=${2:-""}
	local action="restart"

	in_chroot && {
		echo "  (CHROOT env detected, don't restart services)"
		return 0
	}

	$DISTRIB_HAS_UPSTART && {
		[ -f /etc/init/$service.conf ] && {
			if [ $action = "start" ]
			then
				status -q $service || start $service && :
				return $?
			else
				status -q $service && restart $service || start $service
				return $?
			fi
		}
	}

	[ -f /etc/init.d/$service ] || {
		echo "service '$service' not found (neither in /etc/init.d or /etc/init)"
		return 1
	}

	[ "$prog" != "" ] && {
		pidof $prog 2>&1 >/dev/null || action="start"
	}
	/etc/init.d/$service $action
}


## === reload_service service [prog] ===
##
## esegue reload del servizio ''service'' (/etc/init.d) o lo
## start se il servizio non e` attivo e se viene passato il
## nome del programma da controllare ''prog''
##
## esce senza fare nulla se si e` in una jail chroot, dato che
## quasi sicuramente si sta lavorando su un filesystem di comodo
## e quindi i servizi del sistema ospite non devono essere toccati
##
reload_service()
{
	local service=$1
	local prog=${2:-""}
	local action="reload"

	in_chroot && {
		echo "  (CHROOT env detected, don't reload services)"
		return 0
	}

	$DISTRIB_HAS_UPSTART && {
		[ -f /etc/init/$service.conf ] && {
			if [ $action = "start" ]
			then
				status -q $service || start $service && :
				return $?
			else
				status -q $service && reload $service || start $service
				return $?
			fi
		}
	}

	[ -f /etc/init.d/$service ] || {
		echo "service '$service' not found (neither in /etc/init.d or /etc/init)"
		exit 1
	}

	[ "$prog" != "" ] && {
		pidof $prog 2>&1 >/dev/null || action="start"
	}
	/etc/init.d/$service $action
}


## === reload_apparmor_profile profile ===
##
reload_apparmor_profile()
{
	local profile=$1
	local aadir="/etc/apparmor.d"
	local flagfile="$CONFDIR/apparmor_mode"
	local aa_mode=

	$DISTRIB_HAS_APPARMOR || return 0

	if [ -s "$flagfile" ]
	then
		aa_mode=$(cat "$flagfile")
	else
		aa_mode=$(jtconf common.apparmor_mode 2>/dev/null)
	fi
	case "$aa_mode" in
	  enabled|complain)	;;		# OK
	  *)			return 0 ;;	# assuming disabled
	esac
	[ x$(which apparmor_parser) = x ] && return 0
	apparmor_parser -r $aadir/$profile
}

## === patchfile patch_tag original_file addon_file ===
##
## check if original_file already contains the patch_tag, and if not,
## append the content of addon_file to original_file
##
patchfile()
{
	local tag=$1
	local orig=$2
	local addon=$3
	grep -q "$tag" $orig || {
		echo "  applying $tag on $orig"
		cat $addon >>$orig || exit $?
	}
}



## === add_init_script level name [runlevel] ===
##
## aggiunge script /etc/init.d/name ai runlevel 2 3 4 5 (oppure a quello
## specificato) posizionandolo nel livello "level" il link di tipo "S"
## ed il corrispondente link di tipo "K" nel run level 6 (shutdown)
## al livello complementare (100 - level)
##
add_init_script()
{
	local level="$1"; shift
	local name="$1"; shift
	local runlevels=$*
	local here=$(pwd)
	local script=$name

	[ -x /etc/init.d/$script ] || {
		script=$script.sh
		[ -x /etc/init.d/$script ] || return 0
	}

	S=S$level
	if [ $level != "00" ]
	then
		K=$(expr 100 - $level)
		K=K$(printf "%02d" $K)
	else
		K="K99"
	fi

	runlevels=${runlevels:-"2 3 4 5"}

	for i in $runlevels
	do
		cd /etc/rc$i.d
		[ -L $S$name ] || {
			rm -f K??$name S??$name
			ln -s ../init.d/$script $S$name
			echo "  installed rc$i.d/$S$name"
			SOMETHING_CHANGED=true
		}
	done
	if [ "X$runlevels" = "XS" ]
	then
		dir="rc0.d"
	else
		dir="rc6.d"
	fi

	cd /etc/$dir

	[ -L $K$name ] || {
		rm -f S??$name K??$name
		ln -s ../init.d/$script $K$name
		echo "  installed $dir/$K$name"
		SOMETHING_CHANGED=true
	}

	cd $here
	return 0
}


## === install_pkgs packages ===
##
## installa i packages richiesti, solo se non sono gia` instalati
## (quindi non vengon aggiornati se gia` installati);
## imposta PKGS_HAS_INSTALLED=true in caso almeno un package si
## stato installato
##
install_pkgs()
{
	local pkg=
	local mustinstall=$RUNDIR/missing-pkgs.tmp
	local installed=$RUNDIR/installed-pkgs.tmp
	local now=$(date +%y%m%d%H%M%S)

	# before do anything, clean unused packages, to make room?
	#
	$APT_AUTOREMOVE_PREINSTALL && apt_get_autoremove

	:> $mustinstall

	for pkg in $*
	do
		is_installed $pkg || echo $pkg >>$mustinstall
	done
	[ -s $mustinstall ] || { # nothing to install
		echo "  all pkgs already installed"
		return 0
	}

	# install missing packages
	#
	echo -n " $DUMMYTAG installing pkgs ..."
	if $F_EXEC
	then
		$APT_NEEDS_UPDATE && {
			update_repos || exit_err $?
			APT_NEEDS_UPDATE=false
		}
		# updates the install history
		#
		# 2018.08.08 lc
		#  - MODNAME can be undefined if called outside modules cycle
		#
		sed -e "s/^/$now install ${MODNAME:-(self)} /" $mustinstall >> $INSTALLHISTORY

		echo

		# install
		#
		echo "  install pkgs: " $(cat $mustinstall)
		case $APT_AUTOINSTALL in
		  true)	apt-get -y $APT_PARMS install $(cat $mustinstall) || exit_err $? ;;
		  ask)	apt-get $APT_PARMS install $(cat $mustinstall) || exit_err $? ;;
		esac

		# clean cache after install
		#
		case $APT_AUTOCLEAN in
		  true)	apt-get clean ;;
		esac
	else
		echo "  $DUMMYTAG install pkgs: " $(cat $mustinstall)
		echo
	fi
	PKGS_HAS_INSTALLED=true
	SOMETHING_CHANGED=true
	return 0
}


update_repos()
{
	local must_update=false
	local flagfile="$CONFDIR/apt-last-update"
	local changed=

	if [ -f $flagfile ]
	then
		changed=$(find /etc/apt -newer $flagfile 2>/dev/null)
		[ "X$changed" != "X" ] && must_update=true
	else
		must_update=true
	fi

	$must_update || {
		echo -n " (up to date) "
		return 0
	}

	echo -n " (updating repos) ... "
	dpkg --configure --pending 2>&1 >$RUNDIR/err.tmp || {
		cat $RUNDIR/err.tmp
		exit_err 1 "running 'dpkg --configure --pending'"
	}
	apt-get update >$RUNDIR/err.tmp || {
		cat $RUNDIR/err.tmp
		exit_err 1 "running 'apt-get update'"
	}
	touch $flagfile
	return 0
}

## === remove_pkgs packages ===
##
## rimuove i packages richiesti, solo se sono installati
remove_pkgs()
{
	local pkgs=
	local pkg=
	local now=$(date +%y%m%d%H%M%S)

       	for pkg in $*
	do
		#is_installed $pkg 'nodeinstall' && pkgs="$pkgs $pkg"
		# 2017.09.28 kanna
		# - uses apt-get regexp functions to select packages
		pkgs="$pkgs "$(apt-get -s remove "$pkg" 2>&1 | grep '^Remv ' | sed -e 's/^Remv //' -e 's/ .*//')
	done
	pkgs=$(echo $pkgs)	# 'serialize' names (removes extra spaces)

	[ "$pkgs" ] && {
		echo " $DUMMYTAG removing pkgs " $pkgs " ..."
		$F_EXEC && {
			# update install history
			#
			for pkg in $pkgs
			do
				echo "$now remove $MODNAME $pkg" >> $INSTALLHISTORY
			done

			apt_get_autoremove $pkgs
			dpkg --purge $pkgs
		}
	}
	return 0
}



#------------------------------------------------------------------------------
## == text and user interaction handling ==
#------------------------------------------------------------------------------

## === putwarning tag text ===
##
## scrive il messaggio ''text'' su standard error, preceduto dall'avviso
## ''warning!'' e dal tag ''tag'', entrambi in grassetto; il testo viene
## formattato sommariamente per andare a capo in modo opportuno, la
## sequenza di escape ''\n'' viene correttamente intepretata
##
putwarning()
{
	local tag="$1" ; shift
	local i
	local buf
	local nbuf
	local line="------------------------------------------------------------------------------"

	echo -e "WARN: $tag\n$line\n$*\n$line\n" >>$WARNFILE

	echo
	echo -e "$(tput rev) $tag $(tput sgr0)"
	echo -e "$line"
	buf=
	for i in $(echo $@ | sed -e 's/\\n/ #CR# /g')
	do
		[ X"$i" = X"#CR#" ] && {
			echo "    $buf"
			buf=
			continue
		}
		nbuf="$buf $i"
		if [ ${#nbuf} -gt 70 ]
		then
			echo "    $buf"
			buf=" $i"
		else
			buf="$nbuf"
		fi
	done
	[ X"$buf" != X ] && echo "    $buf"
	echo -e "$line"
	echo
	sleep 1
	return 0
}





## === confirm [message] ===
##
## ask user form confirmation
## uses 'message' as prompt, or default 'Confirm?'
## returns true (0) for ok, false (1) for not ok
##
## may alternatives answer are accepted: case ignored, Y or YES,
## 1 for yes, 0 for no, etc
##
confirm()
{
	local prompt=${1:-"Confirm?"}
	local ans

	while :
	do
		echo -en "$prompt [s/n]: "
		read ans || return 1
		ans=$(echo "$ans" | tr '[A-Z]' '[a-z]')
		case "$ans" in
			s|si|y|yes|1)	return 0 ;;
			n|no|0)		return 1 ;;
		esac
	done
}

## === showfilecount file ===
##
## scrive su standard output il nome del file e il conteggio delle
## righe che contiene
##
showfilecount()
{
	local file="$1"
	local msg="${2:-$file}"
	set $(wc -l "$file")
	printf " %-30s %d\n" "$msg" $1
}


#-------------------------------------------------------------------------
## == flow control, conditionals ==
#-------------------------------------------------------------------------

## === getconfirm config_var ===
##
## ritorna true (0) o false (1) se la variabile di configurazione
## ''config_var'' e` definita e ha una valore adeguato, vengono
## considerati true i valori "true", "yes", "si", "1" in qualsiasi
## combinazione upper/lowercase, qualsiasi altro valore o un
## valore undefined viene considerato false
##
getconfirm()
{
	local val=$(jtconf $1 2>/dev/null) || return 1	# not def, false
	val=$(echo $val | tr '[A-Z]' '[a-z]')
	case "$val" in
		yes|y|1|si|true|ok|on)	return 0;;	# true
	esac
	return 1 # default or unknown word, false
}


## === service_requested svcname ===
##
## ritorna true (0) se il servizio ''svcname'' e` tra quelli richiesti
## altrimenti ritorna false (1)
##
service_requested()
{
	grep -q "^${1}$" $SVCLIST
}


## === class_requested classname ===
##
## ritorna true (0) se la classe ''classname'' e` tra quelle richieste,
## anche tra quelle superiori, altrimenti ritorna false (1)
##
class_requested()
{
	local match=$1
	local class=
	for class in $CLASS
	do
		case $class in
		  $match)	return 0 ;;
		  $match.*)	return 0 ;;
		  *.$match.*)	return 0 ;;
		esac
	done
	return 1
}


## === module_requested module ===
##
## ritorna true (0) se il modulo ''module'' e` tra quelli richieste
## altrimenti ritorna false (1)
##
module_requested()
{
	[ -d $MODULESDIR/$1 ]
}




#-------------------------------------------------------------------------
# (internals - undocumented)
#-------------------------------------------------------------------------

# esegue script (o mostra esecuzione se dummy mode)
#
exec_script()
{
	local script="$1"
	local f_exec="${2:-$F_EXEC}"

	echo "$script" | grep -q "/" || script="./$script"

	if $f_exec
	then
		bash -e $script || return $?
	else
		echo " $DUMMYTAG exec $script"
	fi
	return 0
}

#---------------------------------------------------------------------
# aggiornamento automatico definizioni
#---------------------------------------------------------------------


get_and_install_config_files()
{
	local urls=$*
	local url=
	local file=
	local dest="$CONFDIR/conf.d"
	local disabled="$CONFDIR/disabled.d"
	local here=$(pwd)
	local tmpdir=$RUNDIR/cfgdir.tmp
	local num=
	local must_update=false
	local why_update=
	local temp=
	local do_glob=
	local precedence=0
	local wdir=
	local out=

	# check: ho i permessi di scrittura? altrimenti esco senza
	#	 fare nulla
	#
	[ -w $dest ] || return 0

	[ -d $disabled ] || {
		mkdir $disabled || return $?
	}


	cd $CONFDIR	|| return $?

	# controlla se ci sono aggiornamenti pendenti
	#
	urls=$(echo $urls)
	if [ -f config-urls ]
	then
		temp=$(cat config-urls)
		[ "$urls" != "$temp" ] && {
			rm -f config-timestamp # force update
			why_update="config-urls changed"
		}
	else
		rm -f config-timestamp
		why_update="config-urls unknown or new"
	fi

	for url in $urls
	do
		echo $url | grep -q "/timestamp$" && {
			case "$url" in
		  	  /*|*://*)	;;
		  	  *)		url="ftp://$url" ;;
			esac
			echo -en "\n  checking for config updates ... "
			out=$(wgetfile "$url" -N --no-cache 2>&1)
			break
		}
	done

	if [ -f timestamp ]
	then
		if [ -f config-timestamp ]
		then
			[ "$(find timestamp -newer config-timestamp)" ] && {
				must_update=true
				why_update="timestamp is newer than local"
			}
		else
			why_update="last update time unknown"
			must_update=true
		fi
	else
		[ "X$out" != "X" ] && {
			echo "$out" | grep -q "No such file" || {
				echo -e "\nERROR, can't get 'timestamp' file:\n"
				echo "$out"
				return 1
			}
		}
		rm -f config-timestamp
		must_update=true
		why_update="remote timestamp not defined"
		echo
	fi

	if $must_update
	then
		echo "update needed: $why_update"
	else
		echo "(up do date)"
	fi

	$must_update	|| return 0


	# crea dir temporanea dove scaricare i files, in modo che se
	# durante lo scaricamento avviene un errore l'intera procedura
	# viene abortita senza toccare la configurazione attuale
	#
	cd $here
	rm -rf $tmpdir
	mkdir $tmpdir	|| return $?
	cd $tmpdir

	# per ogni url definito tenta scaricamento dei files
	#
	precedence=1
	for url in $urls
	do
		echo $url | grep -q "/timestamp$" && continue

		case "$url" in
		  /*)		do_glob=true	;;	# filesystem
		  ftp://*)	do_glob=true	;;	# ftp
		  *://*)	do_glob=false	;;	# other protocols
		  *)		do_glob=true		# default: ftp
		  		url="ftp://$url"
				;;
		esac

		wdir=$(printf "%02d" $precedence)
		precedence=$(expr $precedence + 1)
		echo "  getting config $wdir from $url ..."
		mkdir $wdir || return $?
		cd $wdir
		echo "# URL: $url" >'+___url___+'

		# 1. try compact/compressed version
		# 2. try access as single file
		# 3. try access as glob (works only ftp and files)
		#
		log="log.tmp"
		wgetfile "$url/${TOOLKIT}-bundle.bz2" --no-cache -N >$log 2>&1 || {
			wgetfile "$url" --no-cache -N >$log 2>&1 || {
				status=$?
				$do_glob && {
					wgetfile "$url/*" --no-cache -N >>$log 2>&1
					status=$?
				}
				[ $status != 0 ] && {
					cat $log
					return $status
				}
			}
		}
		rm -f $log

		[ -f ${TOOLKIT}-bundle.bz2 ] && {
			echo -n "  extracting compressed files ... "
			tar xfpj ${TOOLKIT}-bundle.bz2 || return $?
			rm -f ${TOOLKIT}-bundle.bz2
			echo "ok"
		}

		# to avoid false positives, set timestamp of url-file to
		# the older file in this work directory
		touch --reference $(ls -t | tail -1) '+___url___+'

		cd ..
	done

	# fix perms (per ora cablato): se il nome del file matcha la stringa "adm*"
	# allora contiene info riservate, quindi lo mette leggibile solo da root:adm
	# altrimenti leggibile da tutti
	#
	# FIXME ovviamente fa schifo come hack, trovare un sistema piu` decente!
	#
	chown -R root:root *
	for file in $(ls [0-9][0-9]/*)
	do
		case $file in
		  */adm*)	decho " set ADM $file"; chmod 440 $file; chgrp adm $file ;;
		  *)		decho " set pub $file"; chmod 444 $file ;;
		esac
	done

	# installa files di config
	# i files vengono installati con il prefisso "auto." per
	# distinguerli da quelli configurati manualmente
	# allo stesso tempo se esiste un file manuale con lo stesso
	# nome, questo viene disabilitato per evitare sovrapposizioni
	#
	# elimina eventuali definizioni automatiche precedenti (tiene
	# pero` quelle con sequenza 00, automatici ma creati da
	# kusa-reconf, non scaricati)
	#
	rm -f $CONFDIR/conf.d/auto.[0-9][1-9].* $CONFDIR/conf.d/auto.[1-9][0-9].*

	echo -n "   install auto: "
	for wdir in $(ls -d [0-9][0-9])
	do
		for file in $(ls $wdir)
		do
			mv $wdir/$file $dest/auto.$wdir.$file || return $?
			echo -n "$wdir.$file "
		done
	done
	echo

	[ -f $CONFDIR/timestamp ] && {
		mv $CONFDIR/timestamp $CONFDIR/config-timestamp
	}
	echo $urls >$CONFDIR/config-urls

	return 0
}




#-------------------------------------------------------------------------
## == state checks ==
#-------------------------------------------------------------------------


## state_check_running_release
##

## state_check_dm_running
##
## controlla se c'e` una sessione grafica aperta, imposta
## in modo opportuno $KUSA_STATE_DM_RUNNING se c'e`
## almeno un desktop manager attivo, e la variabile
## $KUSA_STATE_DESKTOP_RUNNING se c'e` un utente
## connesso in sessione grafica
##
## nel caso vi sia un dm attivo ma nessun utente connesso,
## il dm viene fermato (perche` alcuni moduli non possono
## essere installati/aggiornati in presenza di una sessione
## grafica attiva)
##
export KUSA_STATE_DESKTOP_RUNNING=false
export KUSA_STATE_DM_RUNNING=false

state_check_dm_running()
{
	local dm=
	local prog=

	in_chroot && return 0

	KUSA_STATE_DESKTOP_RUNNING=false
	KUSA_STATE_DM_RUNNING=false

	# dm running?
	#
	# 2010.05.23 lc01 (1004)
	# aggiunto gdm-binary
	#
	for prog in kdm gdm gdm-binary
	do
		pid=$(pidof $prog)
		[ "$pid" ] && {
			case $prog in
		  		gdm*)	dm="gdm" ;;
		  		*)	dm=$prog ;;
			esac
			break
		}
	done
	[ "$dm" != "" ] || return 0
	
	# dm detected
	#
	KUSA_STATE_DM_RUNNING=true

	# logged in?
	# eurystic probes on a list of programs used in desktop sessions
	#
	# 2010.05.23 lc01 (1004)
	# tolto gnome-session perche` lo lancia anche sulla login window,
	# trova comunque gnome-panel
	#
	for prog in kdeinit xterm aterm gnome-panel WindowMaker
	do
		[ "$(pidof $prog)" ] && {
			KUSA_STATE_DESKTOP_RUNNING=true
			break
		}
	done

	$KUSA_STATE_DESKTOP_RUNNING || {
		[ "$dm" != "" ] && {
			if [ -f /etc/init.d/$dm ]
			then
				sh /etc/init.d/$dm stop
			else
				echo -n "  stopping dm '$dm' ..."
				killall $dm ; sleep 2 ; killall -9 $dm
				echo "ok"
			fi
		}
		KUSA_STATE_DM_RUNNING=false

		return 0	# all ok
	}


	# desktop is running, can continue?
	#
	getconfirm allow_running_desktop || {
		putwarning "GRAPHICAL DESKTOP RUNNING" \
		  "You are logged in a graphical session, so I will stop" \
		  "because is not safe to proceed. Exit your session or" \
		  "set 'allow_running_desktop yes' in your configuration" \
		  "to override this check (at your risk)."
		return 1
	}

	putwarning "GRAPHICAL DESKTOP RUNNING" \
	  "You are logged in a graphical session, and you have set" \
	  "'allow_running_desktop yes' in your configuration," \
	  "so I will continue at your risk."
	return 0
}



#-------------------------------------------------------------------------
## == misc functions ==
#-------------------------------------------------------------------------


## in_list string list...
##
## returns 0 if string 'string' is contained as element in list 'list'
## returns 1 if not
#
in_list()
{
	local string=$1	; shift
	local str=
	for str
	do
		[ "$string" = "$str" ] && return 0
	done
	return 1
}

# fancy output, set basic functions if ku-base is not yet installed
#
if [ -f /lib/ku-base/echo.sh ]
then
	. /lib/ku-base/echo.sh
else
	echo "  ku-base package not installed, using simplified text output" >&2
	export KU_TTY=
	export KU_CLEAR=
	export KU_CLEOL=
	export KU_BOLD=
	export KU_REV=
	export KU_NORM=

	# dummy functions
	echocl() { return 0; }
	echocr() { return 0; }
fi


# jtconf (kusa-conf) wrappers
#
export JTCONF=
export JTCONF_PARSE=

jtconf()
{
	local localfile=${MODLOCALDB:-''}
	local includeparm=

	JTCONF=${JTCONF:-$(which jtconf)}

	[ "X$localfile" != "X" -a -f "$localfile" ] && {
		includeparm="--include $localfile"
	}
	$JTCONF $includeparm "$@"
}

jtconf-parse()
{
	local localfile=${MODLOCALDB:-''}
	local includeparm=

	JTCONF_PARSE=${JTCONF_PARSE:-$(which jtconf-parse)}

	[ "X$localfile" != "X" -a -f "$localfile" ] && {
		includeparm="--include $localfile"
	}
	$JTCONF_PARSE $includeparm "$@"
}


# wrapper that returns 0 on undefined var, and 1 on any other
# error, with output on stderr
# note that you must pass only one argument (the varname)
#
safe_jtconf()
{
	local out=
	local errfile=$(mktemp /tmp/$CMD-XXXXXXXX)
	out=$(jtconf "$1" 2>$errfile)
	case $out in
	  undefined*var*\'$1\')
	  	rm -f $errfile
		return 0
		;;
	  undefined*)
		echo -e "\nERROR on jtconf $1\n" >&2; cat $errfile >&2
	  	rm -f $errfile
		return 1
		;;
	esac
	rm -f $errfile
	echo "$out"
	return 0
}



## apparmor_installfile file
##
## install or deinstall file based on $DISTRIB_HAS_APPARMOR value
##
##
apparmor_installfile()
{
	local src=$1
	local dest=${2:-/etc/apparmor.d/$(basename "$1")}

	if $DISTRIB_HAS_APPARMOR
	then
		installfile "$src" "$dest" root:root 444
	else
		uninstallfiles "$dest"
	fi
	return 0
}


## build a list of variants using $DISTRIB_* vars and $KUSA_MODEL
## you need to pass a printf format containing at least '%s' as parm
##
list_distro_variants()
{
	local format=$1; shift
	local model=
	local distro=
	local token=
	local out=

	for model in "+$KUSA_MODEL" ""
	do
		# DISTRIB_ID='Devuan'
		# DISTRIB_RELEASE='Devuan'
		# DISTRIB_CODENAME='chimaera'
		# DISTRIB_FULL_ID='Devuan-4'		(id + release)
		#
		for distro in $DISTRIB_FULL_ID $DISTRIB_ID-$DISTRIB_CODENAME $DISTRIB_CODENAME $DISTRIB_ID
		do
			token="$distro$model"
			out="$out "$(printf "$format" "$token")
		done
	done
	echo $out
	return 0
}

# this uses the same logic of filepath (call it at the end), but
# if a file with the bare filename exists, list it before any other
#
# in other ways, can returns a list (a couple, to be precise) of
# filenames, the first is the generic one, the second is the
# distro specific
#
list_install_files()
{
	local fname=
	local found=

	for fname
	do
		[ -f $fname ] && echo $fname
		if $DISTRIB_HAS_UPSTART
		then
			[ -f upstart/$fname ] && echo upstart/$fname
		else
			[ -f init/$fname ] && echo init/$fname
		fi
		out=$(filepath "$fname" || :)
		[ "X$out" != "X" ] && {
			[ "X$out" != "X$PWD/$fname" ] && echo $out
		}
	done
	return 0
}



parse_installfiles()
{
	local file=
	local tempdefines=

	# per-module temporary database definitions (build by run-pre scripts)
	#
	##[ -f $MODLOCALDB ] && {
		##tempdefines="--include $MODLOCALDB"
	##}

	for file
	do
		sed -e 's/[[:space:]]*#.*//' "$file" | jtconf-parse $DFLAG $tempdefines -
	done
}

apt_get_autoremove()
{
	local err=0
	local dummy=

	$F_EXEC || dummy="echo (dummy)"

	case $APT_AUTOREMOVE in
	  true)	echo "  running apt-get autoremove (auto) ..."; $dummy apt-get -y autoremove "$@" || err=$? ;;
	  ask)	echo "  running apt-get autoremove (ask) ..."; $dummy apt-get autoremove "$@" || err=$? ;;
	esac
	return $err
}



#---------------------------------------------------------------------
# apache2
#---------------------------------------------------------------------

## apache2_enable_module module
##
apache2_enable_module()
{
	local dir=/etc/apache2/mods-enabled
	local mod=$1

	[ -L $dir/$mod.load ] || {
		a2enmod $mod || return $?
		SOMETHING_CHANGED=true
	}
	return 0
}

## apache2_disable_module module
##
apache2_disable_module()
{
	local dir=/etc/apache2/mods-enabled
	local mod=$1

	[ -L $dir/$mod.load ] && {
		a2dismod $mod || return $?
		SOMETHING_CHANGED=true
	}
	return 0
}



## apache2_enable_site site [linkname]
##
apache2_enable_site()
{
	local dir=/etc/apache2/sites-enabled
	local site=$1
	local link=${2:-$site}

	[ -L $dir/$link ] || {
		a2ensite $site || return $?
		SOMETHING_CHANGED=true
	}
	return 0
}

## apache2_disable_site site [linkname]
##
apache2_disable_site()
{
	local dir=/etc/apache2/sites-enabled
	local site=$1
	local link=${2:-$site}

	[ -L $dir/$link ] && {
		a2dissite $site || return $?
		SOMETHING_CHANGED=true
	}
	return 0
}



## apache2_enable_conf conf
##
apache2_enable_conf()
{
	local dir=/etc/apache2/conf-enabled
	local conf=$1

	[ -L $dir/$conf.conf ] || {
		a2enconf $conf || return $?
		SOMETHING_CHANGED=true
	}
	return 0
}

## apache2_disable_conf conf
##
apache2_disable_conf()
{
	local edir="/etc/apache2/conf-enabled"
	local ddir="/etc/apache2/conf-disabled"
	local cdir="/etc/apache2/conf.d"
	local conf=$1

	if [ -d $edir ]	# apache >= 2.4
	then	
		[ -L $edir/$conf.conf ] && {
			a2disconf $conf || return $?
			SOMETHING_CHANGED=true
		}
	else
		[ -d $ddir ] || mkdir $ddir
		[ -f $cdir/$conf.conf ] && {
			mv $cdir/$conf.conf $ddir/
			echo "  apache2: disabled $conf.conf"
			SOMETHING_CHANGED=true
		}
	fi
	return 0
}



## apache2_add_allow [ip|All] ...
##
apache2_add_allow()
{
	local ip=
	local version=$(apache2_version)

	for ip
	do
		case $ip in
		  [aA][lL][lL])
		    case $version in
		      2.2.*)	echo -e "\t\tOrder allow,deny"
				echo -e "\t\tAllow from all"
				;;
		      2.4.*)	echo -e "\t\tRequire all granted"
				;;
		    esac
		    ;;
		  *)
		    case $version in
		      2.2.*)	echo -e "\t\tAllow from $ip" ;;
		      2.4.*)	echo -e "\t\tRequire host $ip" ;;
		    esac
		    ;;
		esac
	done
	return 0
}


## apache2_add_deny apache_version [ip|All] ...
##
apache2_add_deny()
{
	local ip=
	local version=$(apache2_version)

	for ip
	do
		case $ip in
		  [aA][lL][lL])
		    case $version in
		      2.2.*)	echo -e "\t\tOrder deny,allow"
				echo -e "\t\tDeny from all"
				;;
		      2.4.*)	;;
		    esac
		    ;;
		  *)
		    echo "FIXME: NOT YET IMPLEMENTED apache2_add_deny $version $ip"
		    return 1
		    #case $version in
		      #2.2.*)	echo -e "\t\t......
		      #2.4.*)	echo -e "\t\t......
		    #esac
		    ;;
		esac
	done
	return 0
}

apache2_version()
{
	# env override?
	[ "X${A2_VERSION:-}" != "X" ] && {
		echo "$A2_VERSION"
		return 0
	}

	local ver=$(dpkg -l apache2 | grep "^ii " | sed -e 's/^ii  apache2 *//' -e 's/ .*//')
	[ "X$ver" = "X" ] && {
		echo "ERROR: apache2 not installed?" >&2
		return 1
	}
	echo "$ver"
	return 0
}

# bin/__TOOLKIT__-functions.sh (EOF)
