#!/bin/ksh93

typeset -r VERSION='1.0' FPROG=${.sh.file} PROG=${FPROG##*/} SDIR=${FPROG%/*}

function showUsage {
	[[ -n $1 ]] && X='-?' ||  X='--man'
	getopts -a "${PROG}" "${ print ${USAGE} ; }" OPT $X
}

function copyMissing {
	typeset X PKG PKG_BASE="http://${PKG_SRV}/LNF/linux/ubuntu"

	# We need ksh93 there and since bogus setup doesn't install what it has been
	# told to ....
	if [[ ! -f ${ZROOT}/bin/ksh93 ]]; then
		if (( IS_LOCAL )); then
			apt-get install --no-install-recommends -y ksh
		else
			cp -p /bin/ksh93 ${ZROOT}/bin/
		fi
	fi

	# resize is helpful
	if (( ! IS_LOCAL )) && [[ ! -e ${ZROOT}/usr/bin/resize ]]; then
		X=${ whence resize ; }
		[[ -n $X ]] && cp -p $X ${ZROOT}/usr/bin/resize
	fi

	# add + package
	for PKG in root sendmail ; do
		[[ -e ${ZROOT}/usr/sbin/${PKG} ]] && continue
		# just try default repos
		(( IS_LOCAL )) && apt-get install --no-install-recommends -y ${PKG}
		[[ -e ${ZROOT}/usr/sbin/${PKG} ]] && continue

		# try manually
		wget -O /tmp/${PKG}.deb ${PKG_BASE}/${LSB_REL}/${PKG}.deb
		read -n 7 X </tmp/${PKG}.deb
		if [[ $X != '!<arch>' ]]; then
			wget -O /tmp/${PKG}.deb ${PKG_BASE}/${PKG}.deb
			read -n 7 X </tmp/${PKG}.deb
		fi
		if [[ $X == '!<arch>' ]]; then
			if (( IS_LOCAL )); then
				dpkg -i /tmp/${PKG}.deb
			elif (( IS_LXC )); then
				dpkg -i --root=${ZROOT} /tmp/${PKG}.deb
			else
				cp /tmp/${PKG}.deb ${ZROOT}/tmp/
				${CHROOT} /usr/bin/dpkg -i /tmp/${PKG}.deb
				rm ${ZROOT}/tmp/${PKG}.deb
			fi
		fi
	done

	[[ ! -e ${ZROOT}/etc/root.conf ]] && \
		print 'admin\t*,default:root' >${ZROOT}/etc/root.conf
	chmod 0600 ${ZROOT}/etc/root.conf

	# sendmail needs to be installed first, otherwise mailx would pull in
	# postfix and other crap like that.
	if [[ -x ${ZROOT}/usr/sbin/sendmail && ! -x ${ZROOT}/usr/bin/mailx ]]; then
		# backup
		(( ! IS_LOCAL )) && [[ -e ${ZROOT}/etc/apt/apt.conf.d/99proxy ]] && \
				mv ${ZROOT}/etc/apt/apt.conf.d/99proxy ${ZROOT}/etc/apt/
		${CHROOT} /usr/bin/apt-get install --no-install-recommends -y bsd-mailx
		# restore
		(( ! IS_LOCAL )) && [[ -e ${ZROOT}/etc/apt/99proxy ]] && \
			mv ${ZROOT}/etc/apt/99proxy ${ZROOT}/etc/apt/apt.conf.d/
	fi

	# 'til we have NFS
	wget -O /tmp/ksh.kshrc --no-verbose http://${PKG_SRV}/public/ksh.kshrc
	read -n 1 X </tmp/ksh.kshrc
	[[ $X == '#' ]] && mv /tmp/ksh.kshrc ${ZROOT}/etc/



	# debian brain damaged stuff
	rm -rf ${ZROOT}/etc/csh*

	# don't overwrite term titles by default
	sed -i -e '/user@host:dir/,/esac/ s,^,#,' ${ZROOT}/etc/skel/.bashrc
	sed -i -e '/sudo hint/,/^fi/ d' ${ZROOT}/etc/bash.bashrc
	print 'LANG="en_US.UTF-8"' >${ZROOT}/etc/default/locale
}

function fixVim {
	typeset X
	# don't waste hours to find out, why vim is not working as documented or
	# expected. As usual it is most likely the typical debian kiddy 
	# "i don't know, what I do" crap ...
	[[ ! -d ${ZROOT}/usr/share/vim ]] && return
	for X in ${ZROOT}/usr/share/vim/*/debian.vim \
		${ZROOT}/usr/share/vim/vimrc.tiny ${ZROOT}/etc/vim
	do
		[[ -e $X ]] && mv $X ${X}.debian-crap
	done
}

function copyConfig {
	typeset -n NCFG=$1
	typeset X
	typeset -a A
	integer I S
	if (( IS_LXC )) || [[ ! -r /preseed.cfg ]]; then
		X=
		# not ahostsv4 because we are looking for the FQDN!
		# 
		[[ -n ${ZDOMAIN} ]] && X="${ZHNAME}.${ZDOMAIN}" || X="${ZNAME}"
		A=( ${ getent hosts "$X" 2>/dev/null ; } )
		X=
		S=${#A[@]}
		for (( I=1; I < S ; I++ ));do
			[[ ${A[I]} =~ \.cs\.(ovgu|uni-magdeburg)\.de$ ]] || continue
			X="${A[I]}"
			break
		done
		[[ -z $X ]] && (( S > 1 )) && X="${A[S-1]}"
		if [[ -z $X ]] && (( IS_LXC + IS_LOCAL == 0 )) ; then
			X=${ dnsdomainname ; }			# busybox builtin
			[[ -n $X ]] && X="${ZHNAME}.$X"
		fi
		print "Using '$X'"
		# hook - allow the zone setup script to overwrite defaults
		if [[ -n $X && ! -f ${ZROOT}/etc/default/node.cfg ]]; then
			rm -f /tmp/preseed.cfg
			wget -O /tmp/preseed.cfg --no-verbose \
				"http://${PKG_SRV}/cgi-bin/ubuntu?hname=$X"
			I=${ stat -c '%s' /tmp/preseed.cfg ; }
			X=
			if (( I > 19 )); then
				read -N 19 X </tmp/preseed.cfg
				[[ $X == '#PRESEED.CS.OVGU.DE' || \
					$X == $'\n#!/bin/ksh93\nprint' ]] && X='/tmp/preseed.cfg'
			fi
		else
			X=
		fi
	else
		X='/preseed.cfg'
	fi

	if [[ -z $X ]]; then
		if [[ -e ${ZROOT}/etc/default/node.cfg ]]; then
			while read LINE ; do
				[[ ${LINE:0:2} == 'C_' || ${LINE:0:2} == 'S_' ]] || continue
				VAL="${LINE#*=}"
				KEY="${.sh.match%=}"
				[[ -z ${KEY} ]] && continue
				NCFG[${KEY}]="${VAL}"
			done <${ZROOT}/etc/default/node.cfg
			return 0
		fi
		print -u2 'WARNUNG: /preseed.cfg nicht gefunden' \
			'- kann post install vars nicht setzen'
		return 1
	fi

	typeset CFG= LINE X KEY VAL
	while read LINE ; do
		[[ ${LINE:0:3} == '#C_' || ${LINE:0:3} == '#S_' ]] || continue
		X="${LINE:1}"
		VAL="${X#*=}"
		KEY="${.sh.match%=}"
		[[ ${VAL:1} == "'" ]] && VAL=${VAL:1:${#VAL}-2}
		NCFG[${KEY}]="${VAL}"
		#CFG+="$X\n"
		CFG+="${KEY}='${VAL}'\n"
	done <$X
	print -n "${CFG}" > ${ZROOT}/etc/default/node.cfg
}

# copy over the bay* and ifname setup scripts from our enhanced boot image
function fixUdev {
	# old crap, which requires groups no one needs
	rm -f ${ZROOT}/lib/udev/rules.d/80-debian-compat.rules

	# reduce group usage
	typeset X=${ZROOT}/lib/udev/rules.d/80-ifupdown.rules
	[[ -e $X ]] || X=
	sed -i -e '/GROUP="input"/ s,^,#,' -e '/GROUP="tape"/ s,^,#,' \
		-e '/GROUP="netdev"/ s,^,#,' -e '/GROUP="dialout"/ s,dialout,tty,' \
		${ZROOT}/lib/udev/rules.d/50-udev-default.rules $X

	(( IS_LXC || IS_STRATO )) && return 0		# global zone managed

	if (( IS_LOCAL )); then
		# might be modified
		[[ -e /lib/udev/baymap ]] || /lib/udev/bayLinks.sh -rR
		[[ -e /lib/udev/baymapNvme ]] || /lib/udev/bayLinksNvme.sh -rR
		# /lib/udev/net.ifnames -rR		# use *.link instead
		return 0
	fi

	# /dev/chassis/SYS/{SS,HD}Dn support
	cp -p /lib/udev/bay* ${ZROOT}/lib/udev/
	${CHROOT} /lib/udev/bayLinks.sh -rR
	${CHROOT} /lib/udev/bayLinksNvme.sh -rR

	# IKS naming conventions - since 2018-04 we use *.link and *.netdev, however
	# for setting up the *.link we use the net.ifnames script
	cp -p /lib/udev/net.ifnames ${ZROOT}/lib/udev/
	#${CHROOT} /lib/udev/net.ifnames -rR
	#cp -p /lib/udev/add-vnic.sh ${ZROOT}/lib/udev/
}

function fixShadow {
	typeset NP=/usr/bin SP=/usr/sbin:/usr/bin
	sed -e '/^password/ s,sha256,sha512,' -i ${ZROOT}/etc/pam.d/common-password
	[[ -h ${ZROOT}/bin ]] || NP+=':/bin' SP='/sbin:/usr/sbin:/bin:/usr/bin'
	[[ -d ${ZROOT}/etc/sudoers.d ]] && \
		touch ${ZROOT}/var/log/sssd/sssd_sudo.log && \
		print "Defaults secure_path=\"${SP}\"" >${ZROOT}/etc/sudoers.d/path
	sed -i -e '/^USERGROUPS_ENAB/ s,yes,no,' \
		-e '/^ENCRYPT_METHOD/ s/_METHOD.*/_METHOD SHA512/' \
		-e "/^ENV_P/ s,=.*,=${NP}," \
		-e "/^ENV_SUP/ s,=.*,=${SP}," \
		${ZROOT}/etc/login.defs
	[[ -f ${ZROOT}/etc/sudoers ]] && \
		sed -r -e 's,/usr/local/s?bin:,,g' -i ${ZROOT}/etc/sudoers
	sed -i -r -e 's,/usr/local/(s?bin|games)?(:|$),,g' \
		-e '/^USERGROUPS_ENAB/ s,yes,no,' \
		${ZROOT}/etc/{login.defs,crontab}

	# PAM putenv(), getenv() ist zur "Kommunikation" zwischen PAM-Modulen
	# gedacht. Irgendein Eierkopp meinte wohl, das es hilfreich ist, gewisse
	# Variablen via "init" Datei populieren zu können und nahm dafür
	# /etc/environment . Irgendein Schwachmat kam dann wohl auf die Idee, da
	# einfachmal PATH reinzutun und obwohl PAM pluggable autentication mech.
	# heißt und überhaupt nix mit den Einstellungen einer Login-Shell zu tun
	# haben sollte, wird PATH in dieser entsprechend gesetzt. Totale Hirnies.
	# Haben nix begriffen. Bzgl. fix gibt's 2 Varianten: konsistent mit 
	# den ENV_PATH value aus /etc/login.defs machen (die Ubuntu Vollidioten
	# nehmen total hirnlos den ENV_SUPATH), oder man läßt gar keine Inkonsistenz
	# zu, indem einfach die Datei oder zumindest der PATH Eintrag gelöscht wird.
	# Wir nutzen die zweite saubere Variante und modifizieren sicherheitshalber
	# noch die pam_env.so Einträge.
	#typeset X Y Z
	#while read X Y Z ; do
	#	[[ $X == 'ENV_PATH' ]] && print "$Y" >${ZROOT}/etc/environment && break
	#done < ${ZROOT}/etc/login.defs
	rm -f ${ZROOT}/etc/environment
	# desktop related stuff
	for F in atd lightdm lightdm-autologin lightdm-greeter polkit-1 \
		login su sudo sshd cron
	do
		[[ -e ${ZROOT}/etc/pam.d/$F ]] || continue
		sed -i -r \
			-e '/envfile=/ s|user_readenv=. *||' \
			-e '/envfile=/ s|\.so|.so conffile=/dev/null|' \
			-e '/pam_env.so *(readenv=.)? *(u|#|$)/ s/^/#/' \
			${ZROOT}/etc/pam.d/$F
	done
	# Debian Schwachmaten haben /lib/x86_64-linux-gnu/security/pam_lastlog.so
	# rausgeschmissen, aber die config Zeile in /etc/pam.d/login drin gelassen
	# Final wollen sie /usr/bin/lastlog komplett rausschmeißen.
	sed -i '/pam_lastlog/ s/^/#/' ${ZROOT}/etc/pam.d/login

	sed -i -r -e 's,(compat|nis),files,g' -e 's,db ,,g' \
		${ZROOT}/etc/nsswitch.conf
}

function fixApt {
	typeset X A PKG= PKG_BASE="http://${PKG_SRV}/LNF/linux/ubuntu"

	if [[ ${ZDOMAIN: -10:10} == 'cs.ovgu.de' ]]; then
		print -n 'Acquire::http::Proxy "http://fp.cs.ovgu.de:3210/";
Acquire::https::Proxy "http://fp.cs.ovgu.de:3210/";
'		>${ZROOT}/etc/apt/apt.conf.d/99proxy
	fi

	# fp is not in the iks.cs.ovgu.de domain and therefore needs to access
	# pkg.iks.cs.ovgu.de via pkg.cs.ovgu.de. So adjust apt prefes accordingly.
	print "Using  PKG_BASE=${PKG_BASE//.iks}"
	X=${ZROOT}/etc/apt/sources.list.d/csmd.list
	if [[ ! -f $X ]]; then
		print "deb [trusted=yes] ${PKG_BASE//.iks.cs./.cs.} ${LSB_REL}/" >$X
		PKG=1
	elif fgrep -qs .iks.cs. "$X" ; then
		sed -i -e 's/\.iks\.cs\./.cs./g' $X
	fi
	A=$(<$X)
	X=${ZROOT}/etc/apt/preferences.d/csmd.pref
	if [[ ! -f $X ]]; then
		# extract host part from URI
		A=( ${A//\[*([^\]])\]} )
		A="${A[1]}"
		if [[ ${A:0:7} == 'http://' || ${A:0:8} == 'https://' ]]; then
			A="${A:7}"
			[[ ${A:0:1} == '/' ]] && A="${A:1}"
			A="${A%%/*}"
			A="${A%%:*}"
		else
			A="${PKG_SRV}"
		fi
		[[ $A == pkg{0,1}(.iks).cs.ovgu.de ]] && A='/pkg(\.iks)?\.cs\.ovgu\.de/'
		print '
Explanation: _Always_ prefer packages from our local pkg repo even if the
Explanation: package has a lower version than the one in the distro repo!
Package: *
Pin: origin '"$A"'
Pin-Priority: 999
'			>$X
		PKG+="1"
	fi
	[[ -n ${PKG} ]] && (( IS_LOCAL )) && apt-get update

	typeset D="${ZROOT}/etc/apt/apt.conf.d"
	[[ -e $D/00InstallRecommends ]] || \
		print 'APT::Install-Recommends "false";' >$D/00InstallRecommends
	[[ -e $D/10periodic ]] && \
		sed -i -e '/^APT::Periodic::Update-Package-Lists/ s,1,0,' \
			-e '/^APT::Periodic::Download-Upgradeable-Packages/ s,1,0,' \
			-e '/^APT::Periodic::AutocleanInterval s,1,0,' \
			$D/10periodic
	[[ -e $D/20archive ]] && \
		sed -i -e '/^APT::Archives::MaxAge/ s,30,7,' $D/20archive
}

function fixSystemd {
	[[ -d ${ZROOT}/etc/systemd ]] || return 0
	[[ -d ${ZROOT}/etc/init.d/OLD ]] || mkdir ${ZROOT}/etc/init.d/OLD

	sed -i -r -e '/^#DefaultEnvironment=/ { s,^#,, ; s,=.*,=PATH=/usr/sbin:/usr/bin:/sbin:/bin, ; }' \
		-e '/^#?Default(CPU|IO|Memory|Tasks)Accounting/ {s/=.*/=yes/;s/^#//}' \
		${ZROOT}/etc/systemd/system.conf
	sed -i -e 's/" disable/" remove/' ${ZROOT}/lib/systemd/systemd-sysv-install

	# Den SysV generator bullshit wollen wir nicht haben
	[[ -d ${ZROOT}/etc/systemd/system-generators ]] || \
		mkdir ${ZROOT}/etc/systemd/system-generators
	ln -s /dev/null \
		${ZROOT}/etc/systemd/system-generators/systemd-sysv-generator
	# and ignore those idiots which wanna run sysv init scripts
	sed -i -e '/^SKIP_SYSTEMD_NATIVE=/ s/=.*/=yes/' ${ZROOT}/sbin/invoke-rc.d

	# der ureadahead Scheiß bring überhaupt nix außer trouble und "kaputtes" log
	typeset L Y X=${ZROOT}/etc/systemd/system O
	rm -f $X/default.target.wants/ureadahead.service
	ln -sf /dev/null $X/ureadahead.service

	# another really stupid debian/ubuntu bullshit since ubuntu focal
	X="${ZROOT}/etc/systemd/system"
	if [[ -e ${ZROOT}/usr/lib/systemd/system/systemd-sysusers.service ]]; then
		ln -sf /dev/null $X/systemd-sysusers.service
		mkdir ${ZROOT}/etc/sysusers.d
		for F in ~(N)${ZROOT}/usr/lib/sysusers.d/*.conf ; do
			# this bullshit actually causes postinstall scripts to create "missing"
			# users and groups we do NOT want and do NOT need (even so the
			# systemd-sysusers.service is disabled/masked!).
			ln -sf /dev/null ${ZROOT}/etc/sysusers.d/${F##*/}
		done
	fi
	X="${ZROOT}/usr/lib/tmpfiles.d/static-nodes-permissions.conf"
	[[ -e $X ]] && sed -i -e '/kvm/ s,^,#,' $X
	sed -i -e 's/:kvm:/:uuidd:/' ${ZROOT}/etc/group

	(( IS_LOCAL )) && X= || X="--root=${ZROOT}"
	# server used: apparmor autofs grub-common kexec-load ntp ondemand sysstat
	#	zfs-share
	# + desktop used: apport speech-dispatcher

	(( IS_LXC )) && O=ondemand || O=
	# first services to disable and cleanup sysv stuff
	for Y in rsync bluetooth brltty pppd-dns unattended-upgrades whoopsie mysql cups-browsed avahi-daemon debug-shell ModemManager accounts-daemon snapd wpa_supplicant binfmt-support $O
	do
		${CHROOT} /bin/systemctl disable $Y 2>&1 | grep -v 'No such file'
		[[ -e ${ZROOT}/etc/init.d/$Y ]] && L+=" ${ZROOT}/etc/init.d/$Y"
	done
	[[ -n $L ]] && mv $L ${ZROOT}/etc/init.d/OLD/

	# only cleanup sysv stuff
	L=
	for Y in acpid atd binfmt-support bootmisc.sh checkfs.sh checkroot-bootclean.sh checkroot.sh console-setup cron dbus halt hostname.sh hwclock.sh kdump-tools kexec keyboard-setup killprocs kmod mountall-bootclean.sh mountall.sh mountdevsubfs.sh mountkernfs.sh mountnfs-bootclean.sh mountnfs.sh networking procps rc rc.local rcS reboot rpcbind rsyslog ssh sendsigs single sysstat udev ulogd2 umountfs umountnfs.sh umountroot urandom \
		alsa-utils anacron cups gdm3 kerneloops lxcfs network-manager plymouth plymouth-log resolvconf saned uuidd x11-common
	do
		[[ -e ${ZROOT}/etc/init.d/$Y ]] || continue
		${CHROOT} /usr/sbin/update-rc.d -f $Y remove
		[[ $Y =~ ^(kdump-tools|sysstat)$ ]] || L+=" ${ZROOT}/etc/init.d/$Y"
	done
	[[ -n $L ]] && mv $L ${ZROOT}/etc/init.d/OLD/
	# TBD: ntp rpcbind
	sed -i -e '/^[a-zA-Z]/ s,^,#,' ${ZROOT}/usr/lib/tmpfiles.d/home.conf
	sed -i -e '/kvm/ s,^,#,' \
		${ZROOT}/usr/lib/tmpfiles.d/static-nodes-permissions.conf
	rmdir ${ZROOT}/srv
	X=${ZROOT}/usr/lib/tmpfiles.d/man-db.conf
	[[ -e $X ]] &&  sed -i -e '/^[a-zA-Z]/ s,^,#,' $X
	rm -rf /var/cache/man
	# X=${ZROOT}/usr/lib/tmpfiles.d/tmp.conf		# TBD
	sed -i -e '/^d \/var\/log / s,^,#,' ${ZROOT}/usr/lib/tmpfiles.d/var.conf
}

function fixLogs {
	# this paranoid stuff is counterproductive
	typeset F X T
	for X in ${ZROOT}/var/log/{dmesg,dmesg.*,syslog} ${ZROOT}/var/log/upstart/*
	do
		[[ -e $X ]] && F+=" $X"
	done
	[[ -n $F ]] && chmod a+r $F
	chmod 0640 ${ZROOT}/var/log/{faillog,btmp}
	rm -f ${ZROOT}/var/log/{auth,kern}.log ${ZROOT}/etc/logrotate.d/ulogd2

	[[ -e ${ZROOT}/var/adm ]] || ln -s log ${ZROOT}/var/adm
	[[ -d  ${ZROOT}/var/log/old ]] || mkdir ${ZROOT}/var/log/old

	if [[ -e ${ZROOT}/etc/ulogd.conf ]]; then
		X=${ZROOT}/etc/ulogd.conf
		[[ -e ${X}.orig ]] || cp -p $X ${X}.orig
		# We use it per default for iptables monitoring/logging
		print '# ulogd options:
[global]
# where ulogd2 should log its own messages: "stdout"|"syslog"|"${filename}"
logfile="syslog"
# ulogd2 loglevel: debug(1), info(3), notice(5 =>default), error(7) or fatal(8)
loglevel=3
# required plugins:
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_inppkt_NFLOG.so"
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_IFINDEX.so"
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_IP2STR.so"
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_PRINTPKT.so"
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_output_SYSLOG.so"
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_raw2packet_BASE.so"
# the stack we use for iptables logging (-j NFLOG)
stack=ipmonInOpts:NFLOG,base1:BASE,ifi1:IFINDEX,ip2str1:IP2STR,print1:PRINTPKT,ipmonOutOpts:SYSLOG

# input related options (NFLOG)
[ipmonInOpts]
# netlink multicast group (the same as the iptables --nflog-group param)
#group=0

# output plugin related options (SYSLOG)
[ipmonOutOpts]
facility=LOG_LOCAL0
level=LOG_NOTICE
'			>$X
		[[ -d ${ZROOT}/var/log/ulog ]] && rm -rf ${ZROOT}/var/log/ulog
	fi

	X=${ZROOT}/etc/rsyslog.conf
	[[ -e ${X}.orig ]] || cp -p $X ${X}.orig
	sed -i -e '/^\$FileCreateMode/ s,0.*,0644,' -e '/^\$FileGroup/ s,adm,sys,' \
		$X
	# 1) %msgid% is always empty, so we use always 0 to have a compatible output
	# 2) too long timestamps - use trad. style: human readable, 18 chars less
	if grep /ActionFileDefaultTemplate/ $X ; then
		sed -i -e '/ActionFileDefaultTemplate/ { s,_.*,_Full, ; i\template(name="RSYSLOG_Full" type="string" string="%TIMESTAMP% %HOSTNAME% %syslogtag% [ID 0 %syslogfacility-text%.%syslogpriority-text%]%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\\n")
}' \
		$X
	else
		# Ubuntu 24.04.4 removed the 'ActionFileDefaultTemplate' directive =8-(
		sed -i -e '/GLOBAL DIRECTIVES/ { N; a\template(name="RSYSLOG_Full" type="string" string="%TIMESTAMP% %HOSTNAME% %syslogtag% [ID 0 %syslogfacility-text%.%syslogpriority-text%]%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\\n")\n$ActionFileDefaultTemplate RSYSLOG_Full
}' \
		$X
	fi
	if (( LSB_VERS >= 1604 )); then
		sed -i -e '/imklog/ s, support,,' \
 -e '/module(load="imklog")/ { s,), permitnonkernelfacility="on"),; s/^#// }' \
 -e '/Enable non-kernel facility/,+2 d' \
			$X
	fi

	X=${ZROOT}/etc/systemd/journald.conf
	[[ -e $X ]] && sed -i -e '/ForwardToSyslog=/ s,^.*,ForwardToSyslog=true,' \
		-e '/SplitMode=/ s,^.*,SplitMode=none,' \
		-e '/Compress=/ s,^.*,Compress=false,' \
		-e '/MaxFileSec=/ s,^.*,MaxFileSec=1d,' \
		-e '/MaxRetentionSec=/ s,^.*,MaxRetentionSec=365day,' \
		$X

	X=${ZROOT}/etc/rsyslog.d/50-default.conf
	[[ -e ${X}.orig ]] || cp -p $X ${X}.orig
	print '# see rsyslog.conf(5)
# facilities:
#	auth,authpriv,cron,daemon,kern,mail,syslog,user,local0..7,news,uucp,lpr,mark
# priorities:
#	debug,info,notice,warn,err,crit,alert,emerg

$FileCreateMode 0640
auth.debug,authpriv.debug						/var/log/authlog
$FileCreateMode 0644
mail.debug										/var/log/syslog
cron.debug										/var/log/cronlog
local0.debug									/var/log/ipmon.log
# usually we would send kern.* to messages as well. However, since it may
# contain msg from the GZ, we redirect to a separate file - chmod as you want.
kern.debug										/var/log/klog
*.info;auth.none;authpriv.none;mail.none;kern.none;cron.none;local0.none;*.err	/var/log/messages
'		>$X

	# what the heck - we want human readable log messages
	X=${ZROOT}/etc/init/dmesg.conf
	[[ -s ${X} ]] && sed -i -e 's,dmesg -s,dmesg -e -s,' $X

	X=${ZROOT}/etc/logrotate.conf
	[[ -e ${X}.orig ]] || cp -p $X ${X}.orig
	print '# see logrotate(8)

#defaults
weekly
su root syslog
rotate 13
dateformat .%Y-%m-%d
create 0644
compress
notifempty
compressoptions "-9"
delaycompress
missingok

tabooext + .orig .ok
include /etc/logrotate.d

# we never rotate wtmp (last forever)
'		>$X

	X=${ZROOT}/etc/logrotate.d/btmp
	print '/var/log/btmp
{
	olddir old
	monthly
	create 0660 root utmp
	rotate 60
}
'		>$X

	X=${ZROOT}/etc/logrotate.d/rsyslog
	[[ -d ${ZROOT}/etc/systemd/system ]] && \
		T='systemctl kill -s HUP rsyslog' || T='service rsyslog rotate'
	[[ -e ${X}.orig ]] || cp -p $X ${X}.orig
	print '# see /etc/logrotate.d/rsyslog
/var/log/syslog
/var/log/klog
/var/log/messages
/var/log/cronlog
{
	olddir old
	sharedscripts
	postrotate
		'"$T"' >/dev/null 2>&1 || true
	endscript
}

/var/log/ipmon.log
/var/log/authlog
{
	olddir old
	daily
	rotate 1825
	sharedscripts
	postrotate
		'"$T"' >/dev/null 2>&1 || true
	endscript
}
'		>$X

	touch ${ZROOT}/var/log/ipmon.log
	chown 47:47 ${ZROOT}/var/log/ipmon.log	# owner=syslog required
	X=${ZROOT}/etc/logrotate.d/upstart
	[[ -e ${X} ]] && sed -i -e 's,daily,size 1M,' $X
	sed -i -e 's,rotate 12,rotate 60,' ${ZROOT}/etc/logrotate.d/{apt,dpkg}

	# and finally the appamor bullshit, which prevents syslogd from start
	X=${ZROOT}/lib/init/apparmor-profile-load
	if [[ -e $X ]]; then
		grep -q 'true' $X || sed -i -e '/profile-load/ s,$, || exit 0,' $X
	fi
}

function fixCron {
	# The debian brain damaged stuff is not allowed here just moving it out of
	# the way is not sufficient, because they don't know, how to implement
	# stuff properly. They just crew up things, again.
	typeset X
	[[ -e ${ZROOT}/etc/crontab ]] && \
		print '# This as well as the other stupid /etc/cron.d* debian' \
			'\n# brain damaged crap should not be used. Use proper crontab(8)' \
			'\n# or systemd timer services.' \
			>${ZROOT}/etc/crontab
	rm -f ${ZROOT}/etc/cron.{daily,weekly}/man-db

	# and make it standard compliant
	X=${ZROOT}/var/spool/cron/crontabs/root
	if ! grep -q 'logrotate' $X 2>/dev/null ; then
		print '25 6 * * *\t/etc/cron.daily/dpkg' \
			'\n0 0 * * *\t/usr/sbin/logrotate /etc/logrotate.conf' \
			'\n#30 3 1 * *\t/usr/lib/zfs-linux/scrub' \
			>>$X
	fi
	chmod 0600 $X
	# dpkg job gets used within the normal cronjob - see above
	rm -f ${ZROOT}/etc/cron.daily/{logrotate,apt-compat}
	# Also fuck off stupid systemd cron bloat
	if (( IS_LOCAL )); then
		for X in ~(N)/etc/systemd/system/timers.target.wants/*.timer ; do
			systemctl stop ${X##*/}
			systemctl disable ${X##*/}
		done
	fi
	#rm -f ${ZROOT}/etc/systemd/system/timers.target.wants/{logrotate,motd-news,apt-daily-upgrade,apt-daily}.timer
	rm -f ${ZROOT}/etc/systemd/system/timers.target.wants/*.timer

	X=${ZROOT}/etc/default/cron
	if ! grep -q 'system/cron.service' $X ; then
		print '# env vars to use in /lib/systemd/system/cron.service' >$X
	fi

	# cron usually cleans up cached man pages. However, caching man pages
	# is a unneeded thing from the previous century, which had even security
	# issues. So rather disable caching ...
	typeset X=${ZROOT}/etc/manpath.config
	[[ -e $X ]] && sed -i -e '/^#NOCACHE/ s,^#,,' $X && \
		rm -rf ${ZROOT}/var/cache/man
}

function fixMdAdm {
	(( IS_LXC || IS_STRATO )) && return 0
	# actually if pkg mdadm is installed
	(( ! IS_LOCAL )) && [[ -e /usr/share/mdadm/mkconf ]] && \
		[[ -e ${ZROOT}/usr/share/mdadm ]] && \
		cp -p /usr/share/mdadm/mkconf ${ZROOT}/usr/share/mdadm/mkconf
	# just in case
	[[ -d ${ZROOT}/etc/mdadm ]] && \
		print 'BOOT_DEGRADED=true' >>${ZROOT}/etc/mdadm/mdadm.conf
}

function fixSendmail {
	[[ -d ${ZROOT}/usr/lib/mail/cf ]] || return 0   # no sendmail installed
	cp ${ZROOT}/usr/lib/mail/cf/linux-client.mc ${ZROOT}/etc/mail/sendmail.mc
	cp ${ZROOT}/usr/lib/mail/cf/submit2.mc ${ZROOT}/etc/mail/submit.mc

	${CHROOT} /bin/systemctl enable sendmail
	${CHROOT} /bin/systemctl enable sendmail-client
	# Zone gets rebooted anyway, so no need to restart sendmail[-client]
}

# NTPd config
function adjustNTP {
	(( IS_STRATO )) && return 0
	# max. one zone is allowed to modify time and this zone has ntp running
	typeset A M X=${ZROOT}/etc/ntp.conf
	# actually we do no want systemd-timesync no matter in what zone.
	if (( IS_LOCAL )); then
		[[ -e /usr/lib/sysusers.d/systemd-timesync.conf ]] && \
			apt-get purge -y systemd-timesyncd
	else
		[[ -e ${ZROOT}/usr/lib/sysusers.d/systemd-timesync.conf ]] && \
			${CHROOT}  /usr/bin/apt-get purge -y systemd-timesyncd
	fi
	if [[ ! -f $X ]]; then
		rm -f /etc/network/if-up.d/ntpdate
		return 0
	fi
	# Overwrite
	print 'broadcastclient
disable auth
driftfile /var/ntp/ntpstats/ntp.drift
logfile /var/ntp/ntpstats/ntp.log' >$X
	mkdir -p ${ZROOT}/var/ntp/ntpstats
	chown 35:3 ${ZROOT}/var/ntp/ntpstats
	X=${ZROOT}/etc/apparmor.d/usr.sbin.ntpd
	# since apparmor rerads _all_ files in this dir, we do not make a backup
	# of the file - otherwise it would "hide" the new one.
	sed -i -e '/^\s*\/etc\/ntp\.d/,/^\s*\/var\/log\/ntpstats\/sysstats/ s,^,#,'\
		-e '/ntpd.pid/ i\  /var/ntp/ntpstats/* rwl,' \
		-e '/^\s*capability sys_nice/ a\  capability mknod,' \
		-e '/^\s*capability sys_nice/ a\  capability net_admin,' ${X}
	(( IS_LOCAL )) && /sbin/apparmor_parser -r $X
	touch ${ZROOT}/var/ntp/ntpstats/ntp.log
}

# adjust sshd_config
function adjustSSH {
	typeset X=${ZROOT}/etc/ssh/sshd_config
	# remote login via ssh keys, only. On more recent openssh without-password
	# got replaced by prohibit-password, which better explains, what it does
	# (don't let root login using a password). However, they accept the old
	# one as well.
	if [[ -d ${X}.d ]]; then
		# since jammy
		print 'PermitRootLogin without-password\nX11UseLocalhost no' \
			'\nAllowUsers root admin' >${X}.d/defaults.conf
		(( LSB_VER > 2004 )) && \
		print 'PubkeyAcceptedAlgorithms +ssh-rsa\nHostKeyAlgorithms +ssh-rsa' \
			>$X.d/rsa.conf
	else
		sed -i -e '/^PermitRootLogin/ s,no,without-password,' \
			-e '/^X11Forwarding/ a\X11UseLocalhost no' \
			$X
		if ! grep -q AllowUser $X ; then
			(( IS_LXC )) && print 'AllowUsers root admin' >>$X
		fi
	fi
	# since noble ssh ist not enabled per default anymore and therefore prevents
	# lxc-install.kshlib:rebootZone() to catch a zone reboot and waits forever.
	systemctl enable ssh
	systemctl start ssh
}

function adjustNFS {
	typeset X="${ZROOT}/etc/idmapd.conf"
	chmod a+r $X		# there are no secrets here
	[[ -e ${X}.orig ]] || cp -p $X ${X}.orig
	sed -i -re '/^(# *)?Domain *=/ s/^.*/Domain = cs.ovgu.de/' $X

	print '# /etc/nfsmount.conf
[ NFSMount_Global_Options ]
proto=tcp
rsize=1048576
wsize=1048576
'		>${ZROOT}/etc/nfsmount.conf
	# angeblich wird's auch für NFS4 gebraucht (jel denkt aber nicht, anyway)
	print 'OPTIONS="-i 127.0.0.1"' >/etc/default/portmap
}

function fixAdminHome {
	typeset A P L X=${ZROOT}/local/home/admin/etc/post-install.sh

	if [[ ! -e ${ZROOT}/local/home/admin ]] && (( ! (IS_LXC && IS_LOCAL) )) && \
		[[ ${ stat -f -c '%T' "${ZROOT}" ; } == 'zfs' ]]
	then
		# this happens usually on GZs, only, because for lxc zone our
		# lxc-ubuntu-csmd takes care of it
		# ZFS might be used
		L=( ${ df -h ${ZROOT}/. ; } )
		L=${ zfs list -H -o name,mountpoint $L ; }
		if [[ ${L[1]} == "${ZROOT}" ]]; then
			# mountpoint == ${ZROOT}, so we can use zfs homes as well
			P=${A[1]}/local/home/admin
			L=( ${ zfs list -H -o name,mountpoint,mounted $P 2>/dev/null; } )
			if [[ -z $L ]]; then
				A=${POSIXLY_CORRECT}
				unset POSIXLY_CORRECT
				zfs create -p $P
				[[ -n $A ]] && POSIXLY_CORRECT="$A"
			fi
			# if it exists but is not mounted, the setup needs to be fixed
		fi
	fi

	[[ -e $X ]] || cp ${.sh.file} $X
	if [[ -e ${ZROOT}/local/home/admin/etc/log.kshlib ]]; then
		X=									# already installed
	elif [[ -f /lib/admin.txz ]]; then
		X=/lib/admin.txz
	else
		wget -O /tmp/admin.txz --no-verbose http://${PKG_SRV}/intern/admin.txz 
		X=${ file /tmp/admin.txz ; }
		if [[ $X =~ XZ\ compressed\ data ]]; then
			X=/tmp/admin.txz
			cp $X /local/home/admin/etc/
		else
			X=
		fi
	fi
	[[ -z $X && -f /local/home/admin/etc/admin.txz ]] && \
		X='/local/home/admin/etc/admin.txz'
	# use numbers since this runs usually from an "external" image
	if [[ -n $X ]]; then
		cp -p $X ${ZROOT}/local/home/admin/etc/
		cd ${ZROOT}/local/home
		# unxz, xzcat is busybox crap!
		xzcat $X | tar xf -
		cd -
	fi
	rm -f ${ZROOT}/local/home/admin/etc/gzip
	chown -R 101:10 ${ZROOT}/local/home/admin
	# We use bash for admin shell since the poor installer does not
	# always honor our package selection - We fix it later in the full blown
	# postinstall.
	sed -r -e 's,^admin1:,admin:,' \
		-e 's|:Administrator[^:]*:[^:]+|:Administrator,,,:/local/home/admin|' \
		-i ${ZROOT}/etc/passwd
	[[ -x ${ZROOT}/bin/tcsh ]] && \
		sed -e '/^admin:/ s,/admin:.*,/admin:/bin/tcsh,' -i ${ZROOT}/etc/passwd
	sed -r -e 's,admin1,admin,g' -i ${ZROOT}/etc/group ${ZROOT}/etc/shadow
	[[ -d ${ZROOT}/home/admin1 ]] && rm -rf ${ZROOT}/home/admin1
	if ! grep -q '^admin:' ${ZROOT}/etc/passwd ; then
		print 'admin:x:101:10:Administrator,,,:/local/home/admin:/bin/tcsh' \
			>>${ZROOT}/etc/passwd
		if (( IS_LOCAL || IS_LXC )); then
			print 'admin:LK:::::::' >>${ZROOT}/etc/shadow
		else
			print 'admin:$5$UTMGlUGi$XFitlLjRguGkufG14dp7wYkYHkY0da.YVhuhqNrRIoC:18003::::::' >>${ZROOT}/etc/shadow
		fi
	elif (( ! IS_LOCAL )); then
		sed -i -re '/^admin:/ s/:[^:]+:/:$5$UTMGlUGi$XFitlLjRguGkufG14dp7wYkYHkY0da.YVhuhqNrRIoC:/' ${ZROOT}/etc/shadow
		sed -i -e '/^admin:/ s|/bin/bash|/bin/tcsh|' ${ZROOT}/etc/passwd
	fi
}

function fixUidsGids {
	typeset X F

	# get the most recent script
	wget -O /tmp/fixUidGids.sh --no-verbose \
		http://${PKG_SRV}/intern/fixUidGids.sh
	read -n 12 X </tmp/fixUidGids.sh
	[[ $X == '#!/bin/ksh93' ]] && X=/tmp/fixUidGids.sh && \
		cp $X /local/home/admin/etc/

	X= F=/local/home/admin/etc/fixUidGids.sh
	if [[ -r $F ]]; then
		X=$F
	elif [[ -r /etc/fixUidGids.sh ]]; then
		X=/etc/fixUidGids.sh
	fi
	if [[ -z $X ]]; then
		print -u2 "WARNING: no fixUidGids.sh script found!"
	else
		[[ ! -x $X ]] && chmod 755 $X
		F=${ZROOT}/etc/passwd
		[[ -e ${F}.orig ]] && cp -p ${F}.orig $F || cp -p $F ${F}.orig 
		F=${ZROOT}/etc/shadow
		[[ -e ${F}.orig ]] && cp -p ${F}.orig $F || cp -p $F ${F}.orig 
		F=${ZROOT}/etc/group
		[[ -e ${F}.orig ]] && cp -p ${F}.orig $F || cp -p $F ${F}.orig 
		$X -R ${ZROOT} -v -s /tmp/fixPerms.sh
		cp -p $X ${ZROOT}/local/home/admin/etc/		# FWIIW
		if [[ -s ${ZROOT}/tmp/fixPerms.sh ]]; then
			print "Running /local/home/admin/etc/fixPerms.sh via ${CHROOT:-bash} ..."
			mv ${ZROOT}/tmp/fixPerms.sh ${ZROOT}/local/home/admin/etc/
			# busybox stuff has no chgrp/chown
			${CHROOT} /bin/ls -al /local/home/admin/etc/fixPerms.sh
			${CHROOT} /bin/bash /local/home/admin/etc/fixPerms.sh
		else
			print "${ZROOT}/tmp/fixPerms.sh not found."
		fi
	fi
	# Avoid clashes with our defaults (400..500 are usually not yet used)
	sed -e '/^#*SYS_GID_MIN/ s,^.*,SYS_GID_MIN\t400,' \
		-e '/^#*SYS_GID_MAX/ s,^.*,SYS_GID_MAX\t498,' \
		-e '/^#*SYS_UID_MIN/ s,^.*,SYS_UID_MIN\t400,' \
		-e '/^#*SYS_UID_MAX/ s,^.*,SYS_UID_MAX\t498,' \
		-e '/^ENCRYPT_METHOD/ s,SHA512,SHA256,' \
		-i ${ZROOT}/etc/login.defs
	# and the same, since inconsistency is the other name for ubuntu/debian ...
	sed -e '/^FIRST_SYSTEM_UID=/ s,=.*,=400,' \
		-e '/^LAST_SYSTEM_UID=/ s,=.*,=498,' \
		-e '/^FIRST_SYSTEM_GID=/ s,=.*,=400,' \
		-e '/^LAST_SYSTEM_GID=/ s,=.*,=498,' \
		-e '/^USERS_GID=/ s,=.*,=500,' \
		-e '/^DHOME=/ s,=.*,=/local/home,' \
		-e '/^USERGROUPS=/ s,=.*,=no,' \
		-i ${ZROOT}/etc/adduser.conf
	# and stop bogus install scripts to use the brain damaged
	# /usr/sbin/{adduser,addgroup} by replacing it with:
	cp -p ${ZROOT}/usr/sbin/adduser ${ZROOT}/usr/sbin/adduser.broken
	[[ -h ${ZROOT}/usr/sbin/addgroup ]] || \
		ln -sf adduser ${ZROOT}/usr/sbin/addgroup
	ln -sf adduser.broken ${ZROOT}/usr/sbin/addgroup.broken
	sed -i -e '/^# detect the right mode/ { N; s/addgroup/addgroup.broken/ }' \
		${ZROOT}/usr/sbin/addgroup.broken
	print '#!/bin/ksh93
LOG="/var/log/debian.skipped"
BN=${.sh.file##*/}

print "\\n${BN}" "$@" >> ${LOG}

print "#####################################################################"\\
 "\\nBogus Debian /usr/sbin/${BN} is disabled. Skipping:"\\
 "\\n\t${BN}" "$@"\\
 "\\nCommand was recorded in ${LOG} as well. Use"\\
 "\\n\\"${BN#add}add\\" or \\"${BN}.broken\\", if you really need it."\\
 "\\n#####################################################################"\\
'		>${ZROOT}/usr/sbin/adduser
	# now one has control and can create users/groups IF and _as really needed_!
}

function enableNetworkd {
	typeset ARG=
	(( IS_LOCAL )) && systemctl daemon-reload
	ln -sf /run/systemd/resolve/resolv.conf ${ZROOT}/etc/resolv.conf
	if (( IS_LOCAL )); then
		systemctl restart systemd-networkd
		# in der zone safe
		systemctl restart systemd-networkd-wait-online
		systemctl restart systemd-resolved
	fi

	${CHROOT} /bin/systemctl ${ARG} enable systemd-networkd
	(( IS_LXC | IS_LOCAL )) && \
		/bin/systemctl ${ARG} enable systemd-networkd-wait-online # not in GZ
	${CHROOT} /bin/systemctl ${ARG} enable systemd-resolved
}

# analog read_configuration() in /usr/share/lxc/templates/lxc-ubuntu-csmd
# called if IS_LXC, otherwise not.
function _fixLxcNetwork {
	typeset -n CFG=$1
	# S_MACPREFIX per Konvention == VendorPrefix == 3 byte
	typeset OUT= MAC IFNAME X=${ uname -n ; } KEY VAL TAIL T \
		NKEY='network.' UKEY='utsname'
	typeset -a A F
	typeset -A SEEN ZMACS
	integer I K N O
set -x
	[[ ${ZHNAME} =~ [0-9]$ ]] && IFNAME="${ZHNAME}_0" || IFNAME="${ZHNAME}0"
	print -n -u2 "Fixing network for ${ZNAME} ..."
	OUT="[Match]\nName=${IFNAME}\n\n[Network]\nLLMNR=false\n"
	OUT+='LLDP=no\nLinkLocalAddressing=no\nIPv6AcceptRouterAdvertisements=no\n'
	OUT+="Address=${CFG[C_IP]}${CFG[S_NETMASK]}\n"
	OUT+="Gateway=${CFG[S_DEFROUTER]}\n"
	OUT+="Domains=${CFG[S_SEARCHDOMAINS]}\n"
	OUT+="DNS=${CFG[S_DNS_SERVERS]}\n"
	print -n "${OUT}" >${ZROOT}/etc/systemd/network/40-${IFNAME}.network

	(( IS_LOCAL )) && return

	(( LXCVERS >= 30000 )) && NKEY='net.0.' && UKEY='uts.name'
	F=( ${CFG[S_MACPREFIX]//:/ } )
	for (( I=0; I < ${#F[@]} ; I++ )); do
		[[ ${#F[I]} < 2 ]] && F[$I]="0${F[I]}"
	done
	MAC="${F[@]}"
	CFG[S_MACPREFIX]="${MAC// /:}"
	MAC=${CFG[S_MACPREFIX]}
	
	A=( ${ getent ahostsv4 $X ; } )
	A=( ${A//./ } )
	MAC+=${ printf ':%02x:%02x' ${A[2]} ${A[3]} ; }
	
	# get zonepath basedirs from running zones
	SEEN["${ZROOT%/*/*}"]=1
	# auto detect zones
	while read -A A ; do
		X="${A[7]}"
		[[ ${X:0:1} == '@' && ${X: -8} == '/command' ]] || continue
		X="${X:1:${#X}-9}"
		[[ -f $X/config ]] && SEEN[${X%/*}]=1
	done </proc/net/unix

	# Count configured zones and record their macs. LXC is crap here, since we
	# cannot see how VNICs are configured within the NGZs and can't just LL
	# configure them in the GZ and inherit them LL pre-configured to the NGZ 
	N=0
	for T in ${!SEEN[@]} ; do
		for X in ~(N)$T/*/config ; do
			(( N++ ))
			[[ $X == */${ZNAME}/config ]] && continue
			while read KEY TAIL VAL TAIL ; do
				[[ ${KEY} =~ ^lxc\.net(work|\.[0-9]+)\.hwaddr$ ]] || continue
				F=( ${VAL//:/ } )
				# Zero pad
				for (( I=0; I < ${#F[@]} ; I++ )); do
					[[ ${#F[I]} < 2 ]] && F[$I]="0${F[I]}"
				done
				VAL="${F[@]}"
				ZMACS["${VAL// /:}"]=1
			done <"$X"
		done
	done
	# calc step width. i.e. max number of MACs per zone
	if (( N < 16 )); then
		O=16
	elif (( N < 32 )); then
		O=8
	elif (( N < 64 )); then
		O=4
	else
		(( K=(RANDOM & 255) ))
		# 11.1 accepts zero padded mac addresses, only
		MAC+=${ printf ':%02x' $K; }
		O=0
	fi
	# step through until we get a MAC, which is not yet used. This assumes
	# that the 1st MAC of a zone is always set width aligned!
	K=O
	if (( K )); then
		for (( I=0; I < N ; I++ )); do
			VAL=${MAC}:${ printf '%02x' $K; }
			[[ -z ${ZMACS[${VAL}]} ]] && MAC=${VAL} && break
			(( K+=O ))
		done
	fi

	# rewrite network config of the zone config
	OUT=
	unset SEEN ; typeset -A SEEN
	while read LINE ; do
		A=( ${LINE} )
		if [[ $A == lxc.${UKEY} ]]; then
			[[ ${A[2]} != ${ZNAME} ]] && continue
		elif [[ $A == "lxc.${NKEY}hwaddr" ]]; then
			[[ ${A[2]} != ${MAC} ]] && continue
		elif [[ $A == "lxc.${NKEY}ipv4" ]]; then
			X="${CFG[C_IP]}${CFG[S_NETMASK]}"
			[[ ${A[2]} != $X ]] && continue
		elif [[ $A == "lxc.${NKEY}name" ]]; then
			[[ ${A[2]} != ${IFNAME} ]] && continue
		elif [[ $A == "lxc.${NKEY}ipv4.gateway" ]]; then
			[[ ${A[2]} != ${CFG[S_DEFROUTER]} ]] && continue
		elif [[ $A == 'lxc.start.order' ]]; then
			continue	# we force it to be the last byte of the MAC
		fi
		[[ -n $A ]] && SEEN[$A]=1
		OUT+="${LINE}\n"
	done<${ZROOT%/*}/config

	[[ -z ${SEEN[lxc.${NKEY}type]} ]] && \
		OUT+="lxc.${NKEY}type = macvlan\nlxc.${NKEY}macvlan.mode = bridge\n"
	if [[ -z ${SEEN[lxc.${NKEY}link]} ]]; then
		X=${ uname -n ; }
		[[ $X == ~(E)[0-9]$ ]] && X+='_0' || X+='0'
		A=( ${ ip link show $X 2>/dev/null ; } )
		if [[ -n ${A[1]} ]]; then
			X=${A[1]%:}
			X=${X#*@}
		else
			X='mb0'
		fi
		OUT+="lxc.${NKEY}link = $X\n"
	fi
	[[ -z ${SEEN[lxc.${UKEY}]} ]] && OUT+="lxc.${UKEY} = ${ZNAME}\n"
	[[ -z ${SEEN[lxc.${NKEY}flags]} ]] && OUT+="lxc.${NKEY}flags = up\n"
	[[ -z ${SEEN[lxc.start.order]} ]] && \
		K=16#${MAC##*:} OUT+="lxc.start.order = $K\n"
	[[ -z ${SEEN[lxc.${NKEY}hwaddr]} ]] && OUT+="lxc.${NKEY}hwaddr = ${MAC}\n"
	[[ -z ${SEEN[lxc.${NKEY}ipv4]} ]] && \
		OUT+="#lxc.${NKEY}ipv4 = ${CFG[C_IP]}${CFG[S_NETMASK]}\n"
	[[ -z ${SEEN[lxc.${NKEY}name]} ]] && OUT+="lxc.${NKEY}name = ${IFNAME}\n"
	[[ -z ${SEEN[lxc.${NKEY}ipv4.gateway]} ]] && \
		OUT+="#lxc.${NKEY}ipv4.gateway = ${CFG[S_DEFROUTER]}\n"
	print -n "${OUT}" >${ZROOT%/*}/config
set +x
}

function _getNicName {
	typeset A= L F X Z=$1 ID=
	[[ -e /sys/class/net/$Z ]] || { print $1 ; return ; }
	integer C=0
	udevadm info /sys/class/net/$Z | while read F L X ; do
		if [[ $F == 'P:' ]]; then
			A=/sys/$L && (( C++ ))
		elif [[ ${L:0:8} == 'ID_PATH=' ]]; then
			ID=${L:8} && (( C++ ))
		fi
		(( C == 2 )) && break
	done
	if (( ! IS_LOCAL )) && [[ ! -x /usr/sbin/dmidecode ]]; then
		ln -sf ${ZROOT}/usr/sbin/dmidecode /usr/sbin/dmidecode
	fi 
	[[ -n $A ]] && X=${ ${ZROOT}/lib/udev/net.ifnames $A ; }
	[[ -z $X ]] && X=$Z
	[[ -n $X && -n ${ID} ]] && print "${X}@${ID}"
}

function fixNetwork {
	typeset -n CFG=$1
	typeset C=  EHOSTS='127.0.0.1   localhost loghost\n'
	integer I
	(( IS_STRATO )) && CFG[S_IPV6]='false'
	if [[ ${CFG[S_IPV6]} == 'false' ]] ; then
		print 'net.ipv6.conf.all.disable_ipv6 = 1' \
			>${ZROOT}/etc/sysctl.d/10-ipv6-disable.conf
		# prefer ipv4 (unfortunately linux ip stack/libc resolver is buggy and
		# even if all /proc/sys/net/ipv6/conf/*/disable_ipv6 are all set to 1
		# getent hosts heise.de  still return an IPv6 instead of an IPv4 addr
		# So this more or less documenting our wish.
		sed -re '/^#precedence.* [0-9]{2}$/ s,^#,,' -e '/:ffff:/ s,10$,60,' \
			-i ${ZROOT}/etc/gai.conf
		sed -i -e '/::/ s,^,#,' ${ZROOT}/etc/hosts
		C='#'
	fi
	(( IS_STRATO )) && return

	# Weg mit dem Ubuntu/Debian Dreck!
	systemctl --root=${ZROOT} disable networking resolvconf
	(( IS_LOCAL )) && systemctl stop networking resolvconf
	${ZROOT}/usr/bin/dpkg --root=${ZROOT} -P \
		netplan.io nplan libnetplan0 ifupdown resolvconf 2>&1

	if (( IS_LXC )); then
		_fixLxcNetwork CFG
		enableNetworkd
		return
	fi
	# per default ubuntu seems to configure the lo interface, only.
	typeset DEV= C X Y Z VNIC
	typeset -l L HNAME VMAC
	typeset -i16 H
	typeset -A NICS MACS NID

	# create nic2mac and mac2nic maps
	/sbin/ip link show | while read -A Y ; do
		[[ ${Y[2]:0:5} == '<LOOP' || ${Y[1]:0:3} == 'lxc' || ${Y[1]} =~ @ ]] \
			&& continue
		if [[ ${Y[2]:0:1} == '<' ]]; then
			Z=${Y[1]}							# current NIC name
		elif [[ $Y == 'link/ether' ]]; then
			L="${Y[1]}"							# MAC addr
			[[ -n $Z ]] && Z=${ _getNicName ${Z%:} ; }	# get possible new name
			if [[ -n $Z ]]; then
				X=${Z%@*} Y=${.sh.match:1}
				NICS[$X]="$L" MACS[$L]="$X" NID[$X]="$Y"
			fi
			Z=
		fi
	done

	for L in ${!NICS[@]} ; do
		X="/etc/systemd/network/10-${L}.link"
		print "# $X\n\n[Match]\nPath=${NID[$L]}\n\n[Link]\nName=$L" \
			"\nMACAddressPolicy=persistent\n" >"${ZROOT}$X"
	done

	L="${CFG[C_MAC]:-X}"		# configured MAC address via ai profile
	DEV="${MACS[$L]}"			# resulting NIC name
	[[ -z ${DEV} && -n ${NICS[mb0]} ]] && DEV='mb0'
	[[ -z ${DEV} && -n ${NICS[eth0]} ]] && DEV='eth0'
	HNAME="${CFG[C_NAMESHORT]}"
	[[ ${HNAME} =~ [0-9]$ ]] && VNIC=${HNAME}_0 || VNIC=${HNAME}0

	X="${ZROOT}/etc/systemd/network"
	if [[ -n ${DEV}  && -n ${CFG[C_IP]} && -n ${HNAME} ]]; then
		# VNIC mac
		VMAC=${CFG[S_MACPREFIX]}
		[[ -z ${VMAC} ]] && VMAC='00:01:01'
		Y=( ${CFG[C_IP]//./ } )
		H=${Y[2]}
		VMAC+=":${H:3}"
		H=${Y[3]}
		(( H < 16 )) && VMAC+=":0${H:3}:00" || VMAC+=":${H:3}:00"

		if (( IS_VBOX )) ; then
			# in VirtualBox macvlans do not work
			VNIC=${DEV}
		else
			[[ ${CFG[C_IP]} =~ 141.44.34.([0-9]|[12][0-9]|3[01]) ]] && \
				VNIC=( $VNIC 2 3 ) || VNIC=( $VNIC 28 )
			cat >$X/README<<EOF
1x-* physical NICs
2x-* aggregations, bridges + their and phys. NIC bindings to VLANs and vNICs
3x-* VLANs
4x-* vNICs
EOF

			cat >$X/20-oneag0.netdev<<EOF
# 20-oneag0.netdev

[NetDev]
Name=oneag0
Kind=bond
MACAddress=${VMAC%:*}:0f

[Bond]
Mode=802.3ad
TransmitHashPolicy=layer3+4
LACPTransmitRate=slow
MIIMonitorSec=1
AdSelect=count
EOF
			cat >$X/20-phys-2-oneag0.network<<EOF
# 20-phys-2-oneag0.network

[Match]
Name=mb[01]

[Link]
ARP=false

[Network]
Bond=oneag0
LinkLocalAddressing=no
LLMNR=false
IPv6AcceptRA=no
LLDP=true
EmitLLDP=nearest-bridge
EOF
			cat >$X/25-oneag0-2-vlans+vnics.network<<EOF
# 25-oneag0-2-vlans+vnics.network

[Match]
Name=oneag0

[Link]
ARP=false

[Network]
LinkLocalAddressing=no
LLMNR=false
IPv6AcceptRA=no
LLDP=no
MACVLAN=${VNIC}
EOF
			for (( I=0; I < ${#VNIC[@]}; I++ )); do
				if (( I > 0 )); then
					print "VLAN=vlan${VNIC[I]}" \
						>>$X/25-oneag0-2-vlans+vnics.network
					cat >$X/30-vlan${VNIC[I]}.netdev<<EOF
# 30-vlan${VNIC[I]}.netdev

[NetDev]
Name=vlan${VNIC[I]}
Kind=vlan

[VLAN]
Id=${VNIC[I]}
EOF
					cat >$X/30-vlan${VNIC[I]}-2-vnics.network<<EOF
# 30-vlan${VNIC[I]}-2-vnics.network

[Match]
Name=vlan${VNIC[I]}

[Link]
ARP=false

[Network]
LinkLocalAddressing=no
LLMNR=false
IPv6AcceptRA=no
LLDP=no
MACVLAN=${VNIC%0}$I
EOF
				fi
				cat >$X/40-${VNIC%0}${I}.netdev<<EOF
# 40-${VNIC%0}${I}.netdev

[NetDev]
Name=${VNIC%0}$I
Kind=macvlan
MACAddress=${VMAC%0}$I

[MACVLAN]
Mode=bridge
EOF
				(( I > 1 )) && C='#' || C=
				cat >$X/40-${VNIC%0}${I}.network<<EOF
# 40-${VNIC%0}${I}.network

[Match]
Name=${VNIC%0}$I

[Network]
LinkLocalAddressing=no
LLMNR=false
IPv6AcceptRA=no
LLDP=no
${C}Address=${CFG[C_IP]}${CFG[S_NETMASK]}
${C}Gateway=${CFG[S_DEFROUTER]}
${C}Domains=${CFG[S_SEARCHDOMAINS]}
${C}DNS=${CFG[S_DNS_SERVERS]}
EOF
				(( I > 0 )) && print '\n[Link]\n#Unmanaged=true\n' \
					>>$X/40-${VNIC%0}${I}.network
			done
		fi

		EHOSTS+="${CFG[C_IP]}	${HNAME}.${CFG[S_DOMAIN]} ${HNAME}\n"
	else
		HNAME=${ hostname ; }
		# use dhcp
		[[ -n ${NICS[mb0]} ]] && DEV='mb0'
		[[ -z ${DEV} && -n ${NICS['eth0']} ]] && DEV='eth0'
		
		if [[ -n ${DEV} ]]; then
			cat >$X/${DEV}.network<<EOF
[Match]
Name=${DEV}

[Network]
LLDP=yes
EmitLLDP=nearest-bridge
DHCP=ipv4
LinkLocalAddressing=no
LLMNR=false
IPv6AcceptRouterAdvertisements=no

[DHCP]
UseTimezone=true
EOF
		fi
	fi

	EHOSTS+="\n${C}::1	localhost loghost ip6-localhost ip6-loopback\n"
	EHOSTS+="${C}fe00::0 ip6-localnet\n"
	EHOSTS+="${C}ff00::0 ip6-mcastprefix\n"
	EHOSTS+="${C}ff02::1	ip6-allnodes\n"
	EHOSTS+="${C}ff02::2	ip6-allrouters\n"
	X=${ZROOT}/etc/hosts
	[[ -e ${X}.orig ]] || cp -p $X ${X}.orig
	print "${EHOSTS}" >${ZROOT}/etc/hosts
	[[ -n ${HNAME} ]] && print "${HNAME}" >${ZROOT}/etc/hostname
	enableNetworkd
}

function fixBootloader {
	(( IS_LXC || IS_STRATO )) && return 0	# there is no grub in a zone

	typeset -n CFG=$1

	typeset X BOOTOPTS='root=ZFS=rpool/ROOT/linux ro'
	# allow local NIC renames
	BOOTOPTS+=' net.ifnames=0'
	# use cgroup2, only
	BOOTOPTS+=' systemd.unified_cgroup_hierarchy=1 swapaccount=1'

	# RAM für kdump reservieren (vonRAM-bisRAM:wieviel,...). Aber nicht mehr
	# als 896M!!! Ist zu klein, wenn man folgende message bei kernel panics
	# sieht: "crashkernel reservation failed - memory is in use". Oder wenn der
	# kdump mit einer message like "running kernel uses more than 70% of the
	# space you reserved ...", Reservierte Größe steht kernel nicht zur
	# Verfügung, wird wirkl. nur für kdumps benutzt, d.h. on kernel panic wird
	# der kdump kernel geladen und ausgeführt, der nur den reservierten Bereich
	# für seine Zwecke benutzt. Wenn der geladen ist, macht der einen Abzug vom
	# panischem kernel und speichert den entsprechend ab (vmcore).
	# Rule of thumb: Slab bzw. SUnreclaim in /proc/meminfo bzgl. aktuell in use
	BOOTOPTS+=' noresume crashkernel=384M-2G:64M,2G-16G:128M,16G-128G:256M,128G-1024G:512M,1024G-:768M bootdegraded=true'
	# Damit die ZFS scripte auch die smarten Verzeichnisse durchsuchen
	BOOTOPTS+=' zdirs=/dev/chassis/SYS,/dev'
	BOOTOPTS+=' debug=y'
	if [[ -n ${CFG[C_CONS]} ]]; then
		X=( ${CFG[C_CONS]//:/ } )
		if [[ ${X[0]} =~ tty[a-d] ]]; then
			case "${X[0]:3}" in
				a) Y='ttyS0' ;;
				b) Y='ttyS1' ;;
				c) Y='ttyS2' ;;
				d) Y='ttyS3' ;;
			esac
		fi
		if [[ -n $Y ]]; then
			BOOTOPTS+=" console=$Y"
			[[ -n ${X[1]} ]] && BOOTOPTS+=",${X[1]}"
		fi
	fi

	# damit der zdirs "pseudo" kernel parameter Wirkung zeigt und zpool import
	# zuerst in diesen Verzeichnissen nach den devices für die Pools sucht
	# und zpool status einen vertändlicheren Output erzeugt:
	if ! grep -q JEL ${ZROOT}/usr/share/initramfs-tools/scripts/zfs ; then
		if grep ZPOOL_IMPORT_PATH ${ZROOT}/etc/default/zfs ; then
			if [[ -d /dev/chassis/SYS ]] ; then
				sed -i -e '/^#ZPOOL_IMPORT_PATH/ a\ZPOOL_IMPORT_PATH="/dev/chassis/SYS"' ${ZROOT}/etc/default/zfs
			else
				sed -i -e '/^#ZPOOL_IMPORT_PATH/ a\#ZPOOL_IMPORT_PATH="/dev/chassis/SYS"' ${ZROOT}/etc/default/zfs
			fi
		else
			cd ${ZROOT}/etc/grub.d/
			# older ZFS/ubuntu versions
			wget -O /tmp/a.patch http://${PKG_SRV}/intern/initramfs-zfs.patch
			(( IS_LOCAL )) || cp /tmp/a.patch ${ZROOT}/tmp/
			${CHROOT} patch -z.orig -p0 -b -i /tmp/a.patch
			cp -p /tmp/a.patch ${ZROOT}/local/home/admin/etc/initramfs-zfs.patch
			cd ~-
		fi
	fi
	rm -f ${ZROOT}/tmp/a.patch	# make it clean for tmpfs mounts

	_fixGrubCfg BOOTOPTS
	_fixEFI BOOTOPTS
	# ~ 94 MB less
	X="${ZROOT}/usr/share/initramfs-tools/hooks/framebuffer-nvidia"
	[[ -e $X ]] && sed -i -e '/^copy_modules_dir_filter / s/^/#/'  "$X"
	# ~ 43 MB less
	X="${ZROOT}/etc/initramfs-tools/initramfs.conf"
	[[ -e $X ]] && sed -i -e '/MODULES=/ s/=.*/=dep/' "$X"
	# ~ 22 MB less
	X="${ZROOT}/etc/initramfs-tools/hooks/optimze-size"
	print '#!/bin/bash
JWD="${PWD}"
cd ${DESTDIR} || exit 1
rm -rf lib/modules/*/kernel/drivers/gpu/drm/{amd,bochs,gma500,i915,radeon,vmwgfx}
rm -rf usr/lib/firmware/{amdgpu,i915,radeon}
cd "${JWD}"
'		>"$X"
	chmod 755 "$X"
}

function _fixEFI {
	[[ -d ${ZROOT}/boot/efi ]] || return 0

	typeset -n  BOOTOPTS=$1
	typeset X
	integer ERR=0 M=0
	if [[ ! -e ${ZROOT}/usr/sbin/update-efi ]]; then
		if [[ -e /usr/sbin/update-efi ]] ; then
			cp -p /usr/sbin/update-efi ${ZROOT}/usr/sbin/update-efi||(( ERR++ ))
		else
			wget -O /tmp/update-efi http://${PKG_SRV}/intern/update-efi
			X=$(</tmp/update-efi)
			if [[ ${X:0:12} == '#!/bin/ksh93' ]]; then
				cp /tmp/update-efi /usr/sbin/update-efi || (( ERR++ ))
				chmod 0755 /usr/sbin/update-efi
			else
				print -u2 'Failed to install /usr/sbin/update-efi!'
				(( ERR++ ))
			fi
		fi
	fi
	mkdir -p ${ZROOT}/etc/initramfs/post-update.d
	print '#!/bin/bash\n/usr/sbin/update-efi -a "$1" -k ${2%/*}\n' \
		>${ZROOT}/etc/initramfs/post-update.d/efi
	chmod 0755 ${ZROOT}/etc/initramfs/post-update.d/efi
	[[ -e ${ZROOT}/etc/machine-id ]] || ${CHROOT} /bin/systemd-machine-id-setup
	if [[ -n ${CHROOT} ]]; then
		[[ ! -d ${ZROOT}/sys/firmware/efi/vars ]] &&
			mount --rbind /sys ${ZROOT}/sys && M=1
		[[ ! -e ${ZROOT}/proc/self ]] &&
			mount --rbind /proc ${ZROOT}/proc && (( M+=2 ))
	fi

	if [[ -e ${ZROOT}/boot/efi/EFI/systemd/systemd-bootx64.efi ]]; then
		# for ancient UEFI systems
		cp -p ${ZROOT}/boot/efi/EFI/systemd/systemd-bootx64.efi \
			${ZROOT}/boot/efi/EFI/BOOT/BOOTX64.EFI
	else
		rm -rf ${ZROOT}/boot/efi/EFI
		${CHROOT} /usr/bin/bootctl --esp-path=/boot/efi install
		if [[ -n ${CHROOT} ]]; then
			(( M & 1 )) && umount ${ZROOT}/sys
			(( M & 2 )) && umount ${ZROOT}/proc
		fi
	fi
	${CHROOT} /usr/sbin/update-efi -a all -o "${BOOTOPTS}" || (( ERR++ ))
	sed -i -r -e '/#?timeout/ s/^.*/timeout 5/' \
		-e '/#?consol/ s/^.*/console-mode keep/' \
		${ZROOT}/boot/efi/loader/loader.conf
	return ${ERR}
}

function _fixGrubCfg {
	[[ -e ${ZROOT}/etc/grub.d/10_linux ]] || return 0

	typeset -n  BOOTOPTS=$1
	sed -e '/^GRUB_HIDDEN_TIMEOUT=/ s,^,#,' \
		-e '/^GRUB_HIDDEN_TIMEOUT_QUIET=/ s,=.*,=false,' \
		-e '/^GRUB_TIMEOUT=/ s,=.*,=5,' \
		-e '/^GRUB_TIMEOUT_STYLE/ s/=.*/=menu/' \
		-e '/^GRUB_CMDLINE_LINUX_DEFAULT=/ s,=.*,="",' \
		-e "/^GRUB_CMDLINE_LINUX=/ s|=.*|=\"${BOOTOPTS}\"|" \
		-e '/GRUB_TERMINAL=c/ s,^.*,GRUB_TERMINAL=console,' \
		-e '/GRUB_TERMINAL=s/ s,^.*,#GRUB_TERMINAL=serial,' \
		-e '/^GRUB_SERIAL_COMMAND=/ s,^,#,' \
		-i ${ZROOT}/etc/default/grub
	print 'GRUB_RECORDFAIL_TIMEOUT=5' >>"${ZROOT}/etc/default/grub"

	typeset X=${ grep GRUB_HIDDEN_TIMEOUT ${ZROOT}/etc/default/grub ; }

	# prepare /boot/grub/device.map
	[[ -e ${ZROOT}/boot/grub/device.map ]] || \
		${CHROOT} /usr/sbin/grub-mkdevicemap
	typeset L H D T
	typeset -i C=0
	typeset -A R
	while read H D T ; do
		[[ ${H:0:1} == '(' && ${H: -1:1} == ')' ]] || continue
		(( C++ ))
		R["$D"]=1
		L+=" $D"
	done <${ZROOT}/boot/grub/device.map
	if [[ -n $L ]]; then
		H=
		for D in $L ; do
			T=${ realpath $D ; }
			[[ -n ${R["$T"]} ]] && continue		# ist schon drin
			H+="(hd${C})\t$T\n"
			(( C++ ))
		done
		print -n "$H" >>${ZROOT}/boot/grub/device.map
	fi
	cat ${ZROOT}/boot/grub/device.map

	# grub-update ruft grub-mkconfig auf, was im Prinzip alle /etc/grub.d/*
	# nacheinander aufruft und deren output zu einer neuen /boot/grub/grub.cfg
	# zusammenkleistert.
	# Um den Namen des root pools auch ohne zfs tools rauszubekommen (die
	# funktionieren nicht im chroot /target env vom netinstall, da dort kein
	# /sys vom parent gemounted ist), wird der patch benötigt:
	if [[ -d ${ZROOT}/etc/grub.d ]] && \
		! grep -q JEL ${ZROOT}/etc/grub.d/10_linux
	then
		wget -O /tmp/a.patch http://${PKG_SRV}/intern/grub-cfg.patch
		(( IS_LOCAL )) || cp /tmp/a.patch ${ZROOT}/tmp/
		mkdir ${ZROOT}/etc/grub.d/old
		${CHROOT} patch -z.orig -p0 -b -i /tmp/a.patch
		# damit die *.orig nicht nochmal gesourct werden
		mv ${ZROOT}/etc/grub.d/*.orig ${ZROOT}/etc/grub.d/old/
		cp -p /tmp/a.patch ${ZROOT}/local/home/admin/etc/grub-cfg.patch
		rm -f ${ZROOT}/tmp/a.patch	# make it clean for tmpfs mounts
	fi
	return 0
}

function addIptables {
	typeset CFG=${ZROOT}/etc/default/node.cfg
	typeset -A IP
	integer IPF_NOW=0

	(( IS_LXC )) && return 0
	wget -O ${ZROOT}/tmp/iptables.sh http://${PKG_SRV}/intern/iptables.sh
	${CHROOT} ksh93 /tmp/iptables.sh -i
	while read L ; do
		if [[ ${L:0:9} == 'S_IPFNOW=' ]]; then
			[[ ${L:9} == 'true' ]] && IPF_NOW=1 && continue
		elif [[ $L =~ ^S_(ADMINHOSTS|NTP_SERVERS|BACKUP_SERVERS|SNMPHOSTS|NFSSERVERS|NFSCLIENTS|HTTPSERVERS)= ]]
		then
			X="${L%%=*}"
			IP[${X:2}]="${.sh.match:1}"
		fi
	done<${CFG}
	[[ -z ${IP[ADMINHOSTS]} ]] && IP[ADMINHOSTS]='0/0'	# any

	sed -i.old -r -e "/^\s*#*ADMINHOSTS=/ s|=.*|='${IP[ADMINHOSTS]// /,}'|" \
		-e "/^\s*#*NTP_SERVER=/ s|=.*|='${IP[NTP_SERVERS]// /,}'|" \
		-e "/^\s*#*BACKUP_SERVER=/ s|=.*|='${IP[BACKUP_SERVERS]// /,}'|" \
		-e "/^\s*#*NFS_SERVER=/ s|=.*|='${IP[NFSSERVERS]// /,}'|" \
		-e "/^\s*#*NFS_CLIENTS=/ s|=.*|='${IP[NFSCLIENTS]// /,}'|" \
		-e "/^\s*#*HTTP_SERVER=/ s|=.*|='${IP[HTTPSERVERS]// /,}'|" \
		${ZROOT}/etc/iptables.rules
	if ((IPF_NOW )); then
		${CHROOT} /bin/systemctl enable iptables
		print '/etc/iptables.rules adjusted + enabled, but svc not yet started.'
	else
		print '/etc/iptables.rules adjusted. You may need to enable' \
			'or restart the iptables service.'
	fi
}

function updateGrubRamfs {
	(( IS_LXC || IS_STRATO )) && return 0
	# w/o /sys zfs utils assume, that zfs kmods have not been loaded yet
	(( ! IS_LOCAL )) && [[ ! -e ${ZROOT}/sys/classes ]] && \
		mount --rbind /sys ${ZROOT}/sys
	${CHROOT} /usr/sbin/update-initramfs -k all -u
	[[ -x ${ZROOT}/usr/sbin/update-grub ]] && ${CHROOT} /usr/sbin/update-grub
}

function pkgCleanup {
	typeset CFG=${ZROOT}/etc/default/node.cfg
	typeset X P PKGS PDIR="${ZROOT}/var/lib/dpkg/info"
	typeset -Ai A B
	integer DC

	[[ -e ${CFG} ]] || return 0
	(( IS_LXC || IS_STRATO )) && return 0
	while read LINE ; do
		[[ ${LINE:0:10} == 'C_DELPKGS=' ]] && C_DELPKGS="${LINE:10}" && break
	done <${CFG}
	[[ -z ${C_DELPKGS} ]] && return 0

	cd ${PDIR}
	for P in *.list ; do
		P="${P%.list}"
		A["${P%:*}"]=1
	done
	for P in ${C_DELPKGS} ; do
		B["$P"]=1
	done
	(( B['dictionaries-common'] )) && B[wamerican]=1 && B[wbritish]=1 && \
		C_DELPKGS+=' wamerican wbritish'

	if [[ -e ${ZROOT}/boot/efi/loader/loader.conf ]]; then
		# dieser Mist läßt sich nicht deinstallieren, da das pre* script in
		# chroot envs fehlschlägt. Also gibt's was mit dem Holzhammer ...
		cp ${ZROOT}/bin/true ${ZROOT}/usr/sbin/grub-probe
		C_DELPKGS+=' grub-common'
	fi
	for P in ${C_DELPKGS} ; do
		(( A["$P"] )) && PKGS+=" $P"
	done

	[[ -z ${PKGS} ]] && return 0

	# pass 1: try to get rid off packages, which hang installation with
	# their poorly written postrm scripts. However, because of the debian
	# braindamaged bullshit, pkg removal will fail, so we install a 2nd Hammer:
	mv ${ZROOT}//usr/share/debconf/confmodule \
		${ZROOT}/usr/share/debconf/confmodule.orig

	${CHROOT} /usr/bin/dpkg --purge --auto-deconfigure ${PKGS}
	P=${ ${CHROOT} /usr/bin/dpkg -l |grep grub ; }
	# cleanup grub relicts
	[[ -z $P ]] && rm -rf ${ZROOT}/etc/grub.d ${ZROOT}/var/lib/grub \
		${ZROOT}/boot/grub ${ZROOT}/var/cache/apt/archives/grub*

	# pass 2: get rid off pkgs with deps
	unset A; typeset -Ai A
	for P in *.list ; do
		P="${P%.list}"
		A["${P%:*}"]=1
	done
	PKGS=
	for P in ${C_DELPKGS} ; do
		(( A["$P"] )) && PKGS+=" $P"
	done
	# Und für den sudo bullshit gibt's den nächsten Tritt in den ...
	# Ansonsten blockt der Müll mit exit code 1 den Rauswurf des anderen Mülls.
	rm -f ${ZROOT}/var/lib/dpkg/info/sudo.prerm
	[[ -n ${PKGS} ]] && print -u2 "Purging ${PKGS}" && \
		${CHROOT} /usr/bin/apt-get -q \
		-y --allow-change-held-packages --allow-remove-essential purge ${PKGS}
	# pass 3: remove artifacts
	PKGS=
	${CHROOT} /bin/dpkg -l | while read -A P ; do
		[[ ${P:0:1} == 'p' || ${P:0:1} == 'r' ]] && PKGS+=" ${P[1]}" && continue
		[[ ${P[1]:0:14} == 'linux-headers-' ]] && PKGS+=" ${P[1]}"
	done
	[[ -n ${PKGS} ]] && print -u2 "Purging ${PKGS}" && \
		${CHROOT} /usr/bin/apt-get -q \
		-y --allow-change-held-packages --allow-remove-essential purge ${PKGS}

	${CHROOT} /usr/bin/apt-get -q \
		-y --allow-change-held-packages --allow-remove-essential autoremove

	# finally undo the 2nd Hammer
	mv ${ZROOT}/usr/share/debconf/confmodule.orig \
		${ZROOT}/usr/share/debconf/confmodule
}

function fixFstab {
	integer I
	typeset F="${ZROOT}/etc/fstab" X

	(( IS_LXC || IS_VBOX || IS_STRATO )) && I=1 || I=4
	if ! grep -q /tmp $F ; then
		X="tmpfs\t\t\t/tmp\ttmpfs\tdefaults,size=${I}g\t0 0"
		(( IS_LXC )) && print "$X" >$F || print "$X" >>$F
	fi
	sed -i -r -e '/[[:space:]]zfs[[:space:]]/ s/^/#/' $F
	(( IS_LXC )) && return 0
	X=( ${ LD_LIBRARY_PATH=${ZROOT}/lib/x86_64-linux-gnu ${ZROOT}/usr/bin/df --output=source ${ZROOT}/var; } )
	[[ ${X[1]} == 'rpool/ROOT/linux/var' ]] && \
		print 'rpool/ROOT/linux/var\t/var\tzfs\tdefaults\t0 0' >>$F
	if [[ -d ${ZROOT}/boot/efi ]]; then
		X=( ${ LD_LIBRARY_PATH=${ZROOT}/lib/x86_64-linux-gnu ${ZROOT}/usr/bin/df --output=source ${ZROOT}/boot/efi; } )
		if [[ -n ${X[1]} && ${X[1]:0:1} == '/' ]]; then
			sed -i -r -e '/[[:space:]]\/boot\/efi[[:space:]]/ s/^/#/' $F
			print "${X[1]}\t/boot/efi\tvfat\tdefaults\t0 0" >>$F
		fi
	fi
}

function fixSwap {
	typeset F="${ZROOT}/etc/fstab" X

	sed -i -e '/^\/swap/ d' $F
	rm -f ${ZROOT}/swap*
	(( IS_LXC || IS_STRATO )) && return 0

	if ! grep -q '\sswap\s' $F ; then
		if [[ -e /dev/zvol/rpool/swap ]]; then
			mkswap /dev/zvol/rpool/swap	# busybox mkswap has no '-f'
			print '/dev/zvol/rpool/swap\tnone\tswap\tdefaults\t0 0' >>$F
			(( IS_LOCAL )) && swapon -a
		fi
	fi
	print 'vm.swappiness=10' >${ZROOT}/etc/sysctl.d/20-swapiness.conf
	
	return 0
}

function fixLdap {
	typeset X=${ZROOT}/etc/ldap/ldap.conf
	[[ -e $X && ! -e ${X}.orig ]] && cp -p $X ${X}.orig

	[[ -e $X ]] && sed -i -e '/^TLS_CACERT/ s,/.*,/var/ldap/ca-certs.pem,' $X
	[[ -d ${ZROOT}/var/ldap/ ]] || mkdir -p ${ZROOT}/var/ldap/
	[[ -d /var/ldap/ ]] || mkdir -p /var/ldap/
	if [[ ! -e /var/ldap/ca-certs.pem ]]; then
		[[ -d /var/ldap ]] || mkdir /var/ldap
		# Copy of http://finca.cs.uni-magdeburg.de/ca.crt
		wget -O /tmp/crt --no-verbose http://${PKG_SRV}/ubuntu/ca.crt
		if [[ -e /tmp/crt ]]; then
			read X < /tmp/crt
			[[ $X == '-----BEGIN CERTIFICATE-----' ]] && \
				cp /tmp/crt /var/ldap/ca-certs.pem
		fi
	fi
	if [[ -e /var/ldap/ca-certs.pem ]]; then
		(( ! IS_LOCAL )) && cp -p /var/ldap/ca-certs.pem ${ZROOT}/var/ldap/
	else
		print 'WARNING: No CA certificates for LDAP installed.'
	fi 
}

function fixAutomount {
	typeset X
	# we do NFSv4, only.
	X=${ZROOT}/etc/default/nfs-common
	if [[ -e $X ]]; then
		sed -i -e '/^NEED_STATD=/ s,=.*,=no,' -e '/^NEED_GSSD=/ s,=.*,=no,' $X 
		if ! grep -q '^NEED_STATD=' $X ; then
			print 'NEED_STATD=no' >>$X
		fi
		# see also /usr/lib/systemd/scripts/nfs-utils_env.sh
	fi

	if [[ ${ZHNAME:0:4} == 'kino' ]]; then
		print -n '# /etc/auto_master
/net	-hosts
/auto	auto_data
+auto.master
'			>${ZROOT}/etc/auto_master
		print -n '# /etc/auto_data
downloads	software.iks:/export/downloads
install		software.iks:/export/install
mirrors		software.iks:/export/mirrors
sfw			software.iks:/export/sfw
src			software.iks:/export/src
lnf			software.iks:/export/lnf
cluster		cld:/export/cluster
nvidia		cld:/export/nvidia
'			>${ZROOT}/etc/auto_data
	else
		print -n '# /etc/auto_master
+auto_master
/home\tauto_home
# does not work on linux
#/net\t-hosts
'			>${ZROOT}/etc/auto_master
		print '+auto_home' >${ZROOT}/etc/auto_home
	fi

	X=${ZROOT}/etc/default/autofs
	sed -i -e '/^MASTER_MAP_NAME/ s,auto.master,auto_master,' \
		-e '/MOUNT_NFS_DEFAULT_PROTOCOL=/ { s,^#,, ; s,=.*,=4, ; }' \
		$X
	if [[ -d ${ZROOT}/etc/nfs.conf.d ]]; then
		# since jammy
		sed -i -e '/^#OPTIONS=/ s/^.*/OPTIONS="-O vers=4.0"/' $X
		if ! grep -q '^OPTIONS=' $X ; then
			print 'OPTIONS="-O vers=4.0"' >>$X
		fi
	fi

	if ! grep -q '^MASTER_MAP_NAME=' $X ; then
		print 'MASTER_MAP_NAME="/etc/auto_master"' >>$X
	fi
	if ! grep -q '^/MOUNT_NFS_DEFAULT_PROTOCOL=' $X ; then
		print 'MOUNT_NFS_DEFAULT_PROTOCOL=4' >> $X
	fi

	#+ initctl restart autofs
	if (( IS_LXC || IS_STRATO )); then
		# disable autofs
		ln -sf /dev/null ${ZROOT}/etc/systemd/system/run-rpc_pipefs.mount
		# this is a sysv service
		if [[ -d ${ZROOT}/etc/systemd/system ]]; then
			${CHROOT} /bin/systemctl disable autofs
		else
			${CHROOT} /usr/sbin/update-rc.d -f autofs remove
		fi
	fi
}

function fixLxcHost {
	(( IS_LXC || IS_STRATO )) && return 0	# only GZ relevant (we do not nest)

	typeset F="${ZROOT}/etc/default/lxc" X NKEY='network.'
	[[ -e $F ]] || return 0		# assume not installed
	(( LXCVERS >= 30000 )) && NKEY='net.0.'
	
	[[ -e ${F}.orig  ]] || cp -p $F ${F}.orig
	print '
# Overwrite the default mirror URL possibly used by lxc-create templates (see
# /usr/share/lxc/templates/ ).
#MIRROR="http://archive.ubuntu.com/ubuntu"
#SECURITY_MIRROR="http://security.ubuntu.com/ubuntu"

# Start containers symlinked under /etc/lxc/auto on unattended [re]boot
LXC_AUTO="true"

USE_LXC_BRIDGE="false"
[ ${USE_LXC_BRIDGE} = "true" ] && [ -f /etc/default/lxc-net ] && \
	. /etc/default/lxc-net

# Max. time to wait for zones to get shutdowned
LXC_SHUTDOWN_TIMEOUT=120
'		>$F

	# benutzen kein briding sondern DA via VNIC am entsprechenden phys link
	F="${ZROOT}/etc/default/lxc-net"
	[[ -e $F && ! -e ${F}.orig  ]] && cp -p $F ${F}.orig
	print 'USE_LXC_BRIDGE="false"' >${F}
	rm -f ${ZROOT}/etc/systemd/system/multi-user.target.wants/lxc-net.service

	F="${ZROOT}/etc/lxc/default.conf"
	[[ -e ${F}.orig  ]] || cp -p $F ${F}.orig
	print "lxc.${NKEY}type = macvlan\nlxc.${NKEY}macvlan.mode =" \
		"bridge\nlxc.${NKEY}link = oneag0\nlxc.${NKEY}flags = up\n" \
		>$F

	F="${ZROOT}/etc/lxc/lxc.conf"
	[[ -e $F && ! -e ${F}.orig ]] && cp -p $F ${F}.orig
	print 'lxc.lxcpath = /zones' >${ZROOT}/etc/lxc/lxc.conf

	F="${ZROOT}/usr/share/lxc/templates/lxc-ubuntu-csmd"
	wget -O /tmp/lxc-csmd --no-verbose http://${PKG_SRV}/intern/lxc-ubuntu-csmd
	read -n 12 X < /tmp/lxc-csmd
	if [[ $X == '#!/bin/ksh93' ]]; then
		[[ -e $F && ! -e ${F}.orig ]] && cp -p $F ${F}.orig
		cp /tmp/lxc-csmd $F
		chmod 755 $F
	else
		print -u2 "'$F' is possibly out of date!"
	fi
		
	F="${ZROOT}/etc/apparmor.d/lxc/lxc-default-with-mounting"
	if [[ -e $F ]]; then
		[[ -e ${F}.orig ]] || cp -p $F ${F}.orig
		sed -i -e '/}/,$ d' $F
		print '	mount options=(rw, rbind),
	mount fstype=autofs,
	mount fstype=nfs,
	mount fstype=nfs4,
	mount fstype=rpc_pipefs,
}
'			>>$F
		# /etc/init.d/apparmor restart	# we reboot anyway
	fi
}

function fixProcps {
	(( IS_LXC )) || return 0
	typeset X=${ZROOT}/etc/sysctl.d/10-magic-sysrq.conf
	[[ -e $X ]] && sed -i -e '/^kernel.sysrq/ s,^,#,' $X
	X=${ZROOT}/etc/sysctl.d/10-kernel-hardening.conf
	[[ -e $X ]] && sed -i -e '/^kernel.kptr_restrict/ s,^,#,' $X
}

function fixSysctl {
	(( IS_LXC )) || return 0
	typeset ARG X
	(( IS_LOCAL )) && \
		X=${ systemctl -p FragmentPath show systemd-sysctl.service ; }
	X=${X#*=}
	[[ -f ${ZROOT}/$X ]] && X="${ZROOT}/$X" || \
		X='/lib/systemd/system/systemd-sysctl.service'
	grep -v ^ConditionPathIsReadWrite $X | sed -e '/^ExecStart=/ s,=-*,=-,' \
		>${ZROOT}/etc/systemd/system/systemd-sysctl.service
}

function fixMutt {
	# Does any debian user uses this crap? Debian devs are so tallented to make
	# software unusable ...
	[[ -e /etc/Muttrc ]] && mv /etc/Muttrc /etc/Muttrc.debian.crap
}

function fixHostname {
	(( IS_STRATO )) || return 0
	typeset T=${ hostname ; }
	cat >${ZROOT}/etc/default/hostname<<EOF
# Datei: /etc/default/hostname
#
# Strato überschreibt immer die /etc/hostname beim container start, was dazu
# führt, daß der hostname der Kiste immer der Strato-Default ist - also so was
# wie hNNNNNNN.stratoserver.net. Was für ein Unfug. Wird in dieser Datei die
# Variable HOSTNAME gesetzt, überschreibt der Dienst "hostname" den Inhalt der
# /etc/hostname mit diesem (Makulatur) und setzt den hostname(1) neu. Der neue
# HOSTNAME kann ein einfacher wie z.B. "www" oder "mail" sein, oder aber ein
# FQDN (als mit angehangener DNS-Domäne). Wichtig ist nur, daß diese Kombi in
# der /etc/hosts auftaucht (IP-Adresse FQDN Kurzname).
#
# ACHTUNG: Hat nix mit dem systemd-hostnamed.service zu tun. Der ist per
# default deaktiviert, weil der noch kdbus bullshit brauchen würde - vollkommen
# umsonsts.

HOSTNAME='${T%%.*}'
EOF

	T=${ZROOT}/lib/systemd/system/hostname.service
	rm -f $T
	print -n '[Unit]
Description=Strato Hostname Service
DefaultDependencies=no
Requires=sysinit.target
Before=sysinit.target
After=local-fs.target

[Service]
Type=oneshot
EnvironmentFile=-/etc/default/hostname
RemainAfterExit=no
ExecStart=-/lib/systemd/scripts/fixHostname

[Install]
WantedBy=sysinit.target
'		>$T
	
	T=${ZROOT}/lib/systemd/scripts/fixHostname
	cat >$T<<EOF
#!/bin/ksh93
[[ -z \${HOSTNAME} ]] && return 0

/bin/hostname \${HOSTNAME}
/bin/sed -n -i -e "1 { s/^.*/\${HOSTNAME}/; p }" /etc/hostname

# Die verfickte Strato-Scheiße schmiert bei jedem Boot in der /etc/hosts rum.
# Haun wir weg:
/bin/sed -i -e '/^# Auto-generated/,$ d' /etc/hosts

# Und die Debian/Ubuntu-Affen modifizieren Mail-Clients wie mutt, etc. so,
# daß die, wenn es /etc/mailname gibt, das darin als FQDN benutzen, statt den
# echten FQDN - WTF!
rm -f /etc/mailname

# Damit journald auch den neu gesetzten hostname verwendet:
/bin/systemctl restart systemd-journald
EOF
	chmod 0755 $T
	# Ist eigentlich immer lokal, also sparen wir uns den check
	systemctl daemon-reload
	systemctl enable hostname
	systemctl start hostname
}

# also part of the netimages 99custom script to get a proper stack on install
function NoAvahi {
	typeset X
	# verfickte avahi Scheiße will auch keiner haben
	# avahi-autoipd avahi-daemon avahi-utils
	#	libavahi-client3 libavahi-common-data libavahi-common3 libavahi-core7
	#	libavahi-glib1 libavahi-ui-gtk3-0
	for X in avahi-{daemon,cups,cups-reload} cups-browsed ; do
		[[ -e ${ZROOT}/etc/init/${X}.conf ]] && \
			print 'manual' >${ZROOT}/etc/init/${X}.override
	done

	X=${ZROOT}/etc/default/avahi-daemon
	[[ -e $X ]] && sed -i -e '/^AVAHI_DAEMON_DETECT_LOCAL=/ s,=.*,=0,' $X

	rm -f ${ZROOT}/etc/network/if-{up,down,post-down}.d/avahi-* \
		${ZROOT}/etc/dhcp/dhclient-{enter,exit}-hooks.d/*avahi-autoipd \
		${ZROOT}/resolvconf/update-libc.d/avahi-daemon \
		${ZROOT}/usr/share/dbus-1/system-services/org.freedesktop.Avahi.service

	${CHROOT} /usr/sbin/update-rc.d -f avahi-daemon remove
	${CHROOT} /usr/sbin/update-rc.d -f cups-browsed remove

	X=${ZROOT}/etc/systemd/system
	[[ -d $X ]] || return 0
	rm -f $X/bluetooth.target.wants/avahi-daemon.service \
		$X/dbus-org.freedesktop.Avahi.service \
		$X/multi-user.target.wants/avahi-daemon.service \
		$X/sockets.target.wants/avahi-daemon.socket
	# die Dienste aus /lib/systemd  ruhig stellen
	ln -sf /dev/null $X/avahi-daemon.socket
	ln -sf /dev/null $X/avahi-daemon.service
	sed -i -e '/^hosts:/ s,mdns4_minimal,,' ${ZROOT}/etc/nsswitch.conf
	return 0
}

function NoBluetooth {
	[[ -e ${ZROOT}/etc/init/bluetooth.conf ]] && \
		print 'manual' >${ZROOT}/etc/init/bluetooth.override
	${CHROOT} /usr/sbin/update-rc.d -f bluetooth remove

	typeset X=${ZROOT}/etc/systemd/system
	[[ -d $X ]] || return 0
	rm -f $X/bluetooth.target.wants/bluetooth.service $X/dbus-org.bluez.service
	return 0
}

function NoModemManager {
	typeset X=${ZROOT}/etc/systemd/system
	[[ -d $X ]] || return 0
	rm -f $X/multi-user.target.wants/ModemManager.service \
		$X/multi-user.target.wants/pppd-dns.service \
		$X/multi-user.target.wants/dns-clean.service \
		$X/dbus-org.freedesktop.ModemManager1.service 
}

function NoWhoopsie {
	typeset X=${ZROOT}/etc/init/whoopsie
	[[ -e ${X}.conf ]] && print 'manual' >${X}.override
	${CHROOT} /usr/sbin/update-rc.d -f whoopsie remove

	X=${ZROOT}/etc/systemd/system
	[[ -d $X ]] || return 0
	rm -f $X/multi-user.target.wants/whoopsie.service
	ln -sf /dev/null $X/whoopsie.service
}

function NoNetworkManager {
	# let NetworkManager (if enabled) manage /etc/network/interfaces otherwise
	# people may get crazy
	sed -i -e '/^managed=/ s,false,true,' \
		${ZROOT}/etc/NetworkManager/NetworkManager.conf

	# disable NM
	#	network-manager network-manager-gnome network-manager-pptp
	#	network-manager-pptp-gnome
	typeset X=${ZROOT}/etc/init/network-manager
	[[ -e ${X}.conf ]] && print 'manual' >${X}.override
	${CHROOT} /usr/sbin/update-rc.d -f network-manager remove

	X=${ZROOT}/etc/systemd/system
	[[ -d $X ]] || return 0

	ln -sf /dev/null $X/NetworkManager-dispatcher.service
	ln -sf /dev/null $X/NetworkManager-wait-online.service
	ln -sf /dev/null $X/NetworkManager.service

	rm -f $X/network-online.target.wants/NetworkManager-wait-online.service \
		$X/multi-user.target.wants/NetworkManager.service \
		$X/dbus-org.freedesktop.nm-dispatcher.service

	# enable network.service

	# Diese wird via systemd-networkd,service gemanagt.
	ln -sf /run/resolvconf/resolv.conf ${ZROOT}/etc/resolv.conf
	
	X=${ZROOT}/etc/systemd/resolved.conf
	[[ -e $X ]] && sed -i -e '/^#LLMNR=/ s,^.*,LLMNR=no,' $X
	X=${ZROOT}/etc/avahi/avahi-daemon.conf
	[[ -e $X ]] && sed -i -e '/^use-ipv6=/ s,=.*,=no,' $X

	X=${ZROOT}/etc/systemd/network/10-dhcp.network
	cat >$X<<EOF
[Match]
Name=${NIC:-mb*}

[Network]
DHCP=ipv4
IPv6AcceptRA=no
# ipv4 (169.254.0.0/16), ipv6 (fe80::/10), no (leave unconfigured)
LinkLocalAddressing=no
LLMNR=false
LLDP=yes
EmitLLDP=nearest-bridge

[DHCP]
UseHostname=yes
UseDNS=yes
UseDomains=yes
EOF
	# fix 'Could not set hostname: Interactive authentication required.'
	X=${ZROOT}/var/tmp/systemd-networkd-hostname.pkla
	cat >$X<<EOF
[Allow systemd-networkd to set transient hostname]
Identity=unix-user:systemd-network
Action=org.freedesktop.hostname1.set-hostname
ResultAny=yes
EOF
	#cp $X ${ZROOT}/var/lib/polkit-1/localauthority/50-local.d/
}

function NoParallelPrinters {
	typeset X=${ZROOT}/etc/modules-load.d/cups-filters.conf
	[[ -e $X ]] && sed -i -r -e '/^(lp|ppdev|parport_pc)/ s/^/#/' $X
}

function getLxcVersion {
	typeset -n RES=$1
	RES=0
	typeset X
	X=${ lxc-info --version 2>/dev/null; }	# nur xenial
	X=${X%%*([^0-9.])*}
	typeset -ia V
	V=( ${X//./ } 0 0 0 )
	integer N
	# we assume max. 2 digits for each version part number
	(( RES=${V[0]}*10000 + ${V[1]} * 100 + ${V[2]} ))
	if (( RES ==  0 )); then
		(( LSB_VERS < 1804 )) && RES=20008 || RES=30003
	fi
}

function fixCoredump {
	# mother fucking undocumented systemd crap - go away
	(( IS_LXC )) && return 0
	print 'kernel.core_pattern = /var/share/core/%e_%p_%i.core' \
		>${ZROOT}/etc/sysctl.d/50-coredump.conf
	[[ -d ${ZROOT}/var/share/core ]] || mkdir ${ZROOT}/var/share/core
	chmod 1755 ${ZROOT}/var/share/core
}

function fixInotify {
	(( IS_LXC )) && return 0
	print '# LXC needs ~ 20/zone, otherwise "Too many open files"
fs.inotify.max_user_instances=512
'		>${ZROOT}/etc/sysctl.d/20-inotify.conf
}

function fixSystemdAskPw {
	# this total bullshit may block services from starting/failing
	# and anyone, who uses encrypted volume stuff may re-enable it.
	typeset X SPATH="${ZROOT}/etc/systemd/system"
	for X in console wall ; do
		#systemctl mask systemd-ask-password-${X}.path
		ln -sf /dev/null ${SPATH}/systemd-ask-password-${X}.path
		#systemctl mask systemd-ask-password-${X}.service
		ln -sf /dev/null ${SPATH}/systemd-ask-password-${X}.service
	done
}

# Required global vars: ZROOT ZHNAME
function getIpFromSystemd {
	typeset F="${ZROOT}/etc/systemd/network/40-${ZHNAME}"

	[[ ${ZHNAME: -1:1} == [0-9] ]] && F+='_'
	F+='0.network'
	if [[ -s $F ]]; then
		while read K V ; do
			[[ ${K:0:8} == 'Address=' ]] || continue
			V=${K:8}
			print -n "${V%%/*}"
			break
		done<"$F"
	fi
}

function getIpFromGlobalScope {
	typeset TRY_LXC="$1" S T
	typeset -a A B
	typeset -A IP
	integer I

	if [[ -n ${TRY_LXC} ]]; then
		A=( ${ lxc-info -s ${ZNAME} ; } )
		[[ ${A[1]} == 'RUNNING' ]] || return

		A=( ${ lxc-attach -n ${ZNAME} -- ip -o -4 addr show scope global up; } )
	else
		A=( ${ ip -o -4 addr show scope global up ; } )
	fi
	for (( I=3; I < 255 ; I+=13 )); do
		S=${A[I]%%/*}
		[[ -z $S ]] && break
		[[ $S == {1,3}([0-9]){3}(.{1,3}([0-9])) ]] || continue
		T=${ printf '%03d.%03d.%03d.%03d' ${S//./ } ; }
		IP[_$T]="$S"
	done
	(( ${#IP[@]} )) || return
	# sort in reverse order
	set -s ${!IP[@]}
	B=( "$@" )
	S=
	for (( I=${#B[@]}-1; I >= 0; I-- )); do
		T=${B[I]}
		S+=" ${IP[$T]}"
	done
	print -n $S
}

function setPkgServer {
	typeset -n SRV=$1
	typeset A IP
	integer C=0

	SRV=
	for (( C=0 ; C < 3; C++ )); do
		# prefer the same network as the zone if any
		if (( C == 0 )); then
			(( IS_LXC )) || continue
			IP=${ getIpFromSystemd ; }
		elif (( C == 1 )); then
			(( IS_LXC )) || continue
			IP=${ getIpFromGlobalScope 1 ; }
		elif (( C == 1 )); then
			IP=${ getIpFromGlobalScope; }
		fi
		for A in ${IP} ; do
			case "$A" in
				# we relly prefer the public ones because the ones with private
				# IPs might be not reachable for zones having a public IP
				141.44.21.*)	SRV='pkg.cs.ovgu.de' ;;
				141.44.24.*)	SRV='pkg.iks.cs.ovgu.de' ;;
				141.44.28.*)	SRV='pkg2.iks.cs.ovgu.de' ;;
				141.44.29.*)	SRV='pkg3.iks.cs.ovgu.de' ;;
				141.44.34.*)	SRV='pkg.cs.ovgu.de' ;;
				10.0.16.*)	SRV='pkg-m4.iks.cs.ovgu.de' ;;
				10.0.1.*)	SRV='pkg-m2.iks.cs.ovgu.de' ;;
				10.*)	SRV='pkg.cs.ovgu.de' ;;
			esac
			# Check its reachability: the GZ may or may not have a vNIC in
			# the same network as a zone and for zone setups this script
			# usually runs in the GZ env ...
			if [[ -n $[SVR} ]]; then
				ping -4 -qn -c 3 -W 3 ${SRV} -i 1
				(( $? )) && SRV=
			fi
			[[ -n ${SRV} ]] && return
		done
	done
	# Still none found - use the one from the GZ
	[[ -z ${SRV} ]] && \
		SRV=( ${ getent ahostsv4 pkg 2>/dev/null | grep -w RAW ; } )
	# final fallback
	[[ -z ${SRV} ]] && SRV='pkg.cs.ovgu.de'
}


unset NODECFG FNLIST PKG_SRV ZROOT ZHNAME ZNAME IS_LXC LSB_REL LSB_VERS LXCVERS
integer IS_LXC=0 IS_LOCAL=0 IS_VBOX=0 LSB_VERS=-1 IS_STRATO=0 LXCVERS=0
typeset PKG_SRV= ZROOT= ZNAME= ZHNAME= LSB_REL= CHROOT=
typeset -A NODECFG FNLIST

# IS_LOCAL:
#	1: the script is running inside a global zone (GZ) or non-global zone (NGZ)
#	0: the script is invoked from outside the zone, so /proc might not be
#	   mounted. Happens e.g. if booted from an install media or via PXE and
#	   and GZ gets installed (usually under /target) or from the GZ as part of
#	   a NGZ installation, i.e. as part of customizeZone() in lxc-install.kshlib
# IS_LXC:
#	1: the target is a NGZ
#	0: the target is a GZ
#
# IS_LOCAL	IS_LXC	scenario
#	0		0		GZ installation invoked from an external boot image
#	0		1		NGZ installation invoked in the parent zone
#	1		0 		GZ invoked on demand, e.g. as part of a local post install
#	1		1		NGZ invoked on demand, e.g. as part of a local post install 
# IS_STRATO:
#	Like IS_LXC=1 and IS_LOCAL=1 + some specials wrt. networking.
function init {
	integer I

	typeset X='/sys/devices/virtual/dmi/id/bios_version' T
	[[ -e $X ]] && X=$(<$X) || X=
	[[ $X == 'VirtualBox' ]] && IS_VBOX=1
	T=${ df / ; }
	[[ $T =~ '/dev/ploop' ]] && IS_STRATO=1 && IS_LOCAL=1
	# since we do not support nested zones:
	X=${ systemd-detect-virt 2>/dev/null ; }
	[[ -z $X || $X == 'none' ]] || { IS_LOCAL=1; IS_LXC=1; }

	if (( IS_LOCAL )); then
		ZROOT='/'
		ZNAME=${ hostname ; }
		[[ -z ${ZNAME} ]] && ZNAME=${ uname -n ; }
		[[ -z ${ZNAME} ]] && print -u2 'zone-name nicht bestimmbar - Abbruch' \
			&& return 1
		if grep -q 'container=' /proc/1/environ 2>/dev/null ; then
			IS_LXC=1
		fi
	elif [[ -n $1 ]]; then
		IS_LXC=1
		ZROOT="${1%%/}"
		[[ ${ZROOT: -7:7} != '/rootfs' && -d ${ZROOT}/rootfs/dev ]] && \
		ZROOT+='/rootfs'
		ZNAME="${ZROOT%/rootfs}"
		if [[ -n ${.sh.match} ]]; then
			ZNAME=${ZNAME##*/}
		else
			print -u2 'zone-name nicht bestimmbar - Abbruch'
			return 1
		fi
		CHROOT="chroot ${ZROOT}"
	else
		# bare metal aka GZ install
		ZROOT=/target
		ZNAME=${ hostname ; }
		print '========================== POST ==============================='
		set
	fi
	ZHNAME=${ZNAME%%.*}
	setPkgServer PKG_SRV

	if [[ ! -d ${ZROOT}/dev ]]; then
		print "${ZROOT} scheint kein Linux root filesystem zu sein!"
		/bin/tcsh
		return 2
	fi
	while read X ; do
		[[ ${X:0:11} == 'VERSION_ID=' ]] || continue
		LSB_REL=${X:12:${#X}-13} && break		# don't copy quotes
	done <${ZROOT}/etc/os-release
	if [[ -z ${LSB_REL} ]]; then
		while read X ; do
			[[ ${X:0:16} == 'DISTRIB_RELEASE=' ]] || continue
			LSB_REL=${X:16} && break
		done < ${ZROOT}/etc/lsb-release
	fi
	[[ -n ${LSB_REL} ]] && LSB_VERS=${LSB_REL//.}

	# doesn't hurt
	I=0
	if (( ! IS_LXC )); then
		grep -q '^de_DE.UTF-8 ' ${ZROOT}/etc/locale.gen || (( I++ ))
		grep -q '^en_US.UTF-8 ' ${ZROOT}/etc/locale.gen || (( I++ ))
		(( I )) && ${CHROOT} /usr/sbin/locale-gen en_US.UTF-8 de_DE.UTF-8
	fi

	[[ ! -d ${ZROOT}/local/home/admin/etc ]] && \
		mkdir -p ${ZROOT}/local/home/admin/etc || true

	getLxcVersion LXCVERS
	typeset -r LXCVERS
}

function doMain {
	typeset X

	typeset FN DEFAULT_FN=(
		'fixApt'
		'copyMissing'
		'fixUidsGids'
		'fixSystemd'
		'fixLdap'
		'fixAutomount'
		'fixLogs'
		'fixCron'
		'copyConfig'
		'fixFstab'
		'fixUdev'
		'fixShadow'
		'fixMdAdm'
		'fixSendmail'
		'adjustNTP'
		'adjustNFS'
		'adjustSSH'
		'fixLxcHost'
		'fixVim'
		'fixAdminHome'
		'updateGrubRamfs'
		'fixNetwork'
		'fixBootloader'
		'fixProcps'
		'fixSysctl'
		'fixSwap'
		'fixHostname'
		'fixMutt'
		'pkgCleanup'
		'fixCoredump'
		'fixInotify'
		'fixSystemdAskPw'
		'addIptables'
	)

	# order could be important
	if (( ${#FNLIST[@]} )); then
		[[ -n ${FNLIST['fixNetwork']} || -n ${FNLIST['fixBootloader']} ]] && \
			FNLIST['copyConfig']=1
		for X in ${DEFAULT_FN[@]} ; do
			[[ -n ${FNLIST["$X"]} ]] && FN+=( $X )
		done
	else
		typeset -n FN=DEFAULT_FN
	fi
	
	typeset I LOG=${ZROOT}/var/tmp/post
	for I in {0..100} ; do
		[[ -e ${LOG}-${I}.log ]] && continue
		LOG+="-${I}.log" && break
	done
	LOG="${LOG%.log}.log"
	print "LOG goes to '$LOG'."
	
	for X in ${FN[@]} ; do
		print "### $X ###############################################" >>${LOG}
		if [[ $X == 'copyConfig' || $X == 'fixBootloader' ]]; then
			$X NODECFG >>${LOG} 2>&1 || return 2
		elif [[ $X == 'fixUidsGids' ]]; then
			print "$X log goes to /var/tmp/fix.log" >>${LOG}
			fixUidsGids >${ZROOT}/var/tmp/fix.log 2>&1
		elif [[ $X == 'fixNetwork' ]]; then
			print "$X log goes to /var/tmp/net.log" >>${LOG}
			fixNetwork NODECFG >${ZROOT}/var/tmp/net.log 2>&1
		else
			$X >>${LOG} 2>&1
		fi
	done
	(( IS_LOCAL )) || cp /tmp/*.log ${ZROOT}/var/tmp/ 2>/dev/null
	[[ -s ${LOG} ]] && print '============== Install log ==============\n' && \
		cat ${LOG}
	return 0
}

USAGE="[-?${VERSION}"' ]
[-copyright?Copyright (c) 2014 Jens Elkner. All rights reserved.]
[-license?CDDL 1.0]
[+NAME?'"${PROG}"' - post install für linux global und non-global zones]
[+DESCRIPTION?Script, das i.d.R. nach der Installation einer GZ oder NGZ ausgeführt wird, um die zone entsprechend unseren Standards anzupassen und notwendige Fixes/Konfigurationen vorzunehmen. Sollte idempotent sein, muß aber nicht, d.h. bei mehrfacher Ausführung sollte man genauer prüfen, ob noch alles i.o. ist. Ist kein \arootfs\a angegeben, geht dieses Script davon aus, daß es auf eine GZ angewendet wird, die an \b/target\b gemounted ist. Anderenfalls sollte \arootfs\a das format \azone_basedir\a/\azone_name\a\b/rootfs\b haben: \azone_basedir\a ist im Prinzip beliebig, bei uns aber i.d.R. \b/zones\b und \azone_name\a ist der Name der NGZ. \b/rootfs\b am Ende ist optional - wird automat. vom Script angehangen, wenn nicht present.]
[h:help?Diese Hilfe ausgegeben und Script beenden.]
[F:functions?Liste aller definierten Funktionen im Script ausgeben (\btypeset +f\b). Funktionen beginnend mit einem Unterstrich (_) sind Hilfsfunktionen, die durch andere aufgerufen werden. Funktionen beginnend mit einem Großbuchstaben sind Convinience-Funktionen, die per default nicht aufgerufen werden - i.d.R. cleanup Zeux, das auch in /scripts/casper-bottom/99custom unserer Netboot-LiveDVD images zum Einsatz kommt. Mit Kleinbuchstaben beginnende Funktionen werden per default abgearbeitet.]
[T:trace]:[fname_list?Eine Komma- oder Leerzeichen-separierte Liste von Funktionsnamen, die während ihrer Ausführung getracet werden sollen.]
[+?]
[e:exec]:[fn_list?Nur die in \afn_list\a angegebenen Funktionen ausführen. \afn_list\a ist eine Komma- oder Leerzeichen separierte Liste von Funktionsnamen (siehe -F).]
[l:local?Script in dieser, bereits "fertig" installierten Zone starten. Ob dies eine GZ oder NGZ ist, wird automat. ermittelt. Es wird erwartet, daß zumindest der hostname gesetzt ist und der IP-Netzzugang funktioniert. \arootfs\a wird in diesem Falle ignoriert.]
[+Environment?]{
	[+ZDOMAIN?If given, \azonename\a\b.\b\aZDOMAIN\a will be used to fetch the preseed.cfg for this zone. Otherwise the \azonename\a will be resolved using the resolver of the running zone and the preseed.cfg fetched for the first FQDN returned by the resolver.]
}
\n\n[\arootfs\a]
'
X="${ print ${USAGE} ; }"
while getopts "${X}" OPT ; do
	case ${OPT} in
		h) showUsage ; exit 0 ;;
		T)	if [[ ${OPTARG} == 'ALL' ]]; then
				typeset -ft ${ typeset +f ; }
				set -x
			else
				typeset -ft ${OPTARG//,/ }
			fi
			;;
		F) typeset +f && exit 0 ;;
		e) for T in ${OPTARG//,/ } ; do FNLIST["$T"]=1 ; done ;;
		l) IS_LOCAL=1 ;;
		*) showUsage 1 ; exit 1 ;;
	esac
done

X=$((OPTIND-1))
shift $X && OPTIND=1
unset X T

. init "$@" || return $?
doMain "$@"
