#!/bin/sh -efu

. shell-quote


#########################################################
## Consider to extract this part into alterator-bind's ##
## bind-sh-functions                                   ##
#########################################################

. bind-sh-functions
. alterator-service-functions

# The list of reserved local conf files
bind_local_conf_reserved='/etc/bind/rfc1912.conf'

# usage: bind_local_conf_exclude_all_but path...
# exclude all but specified files from bind local conf
bind_local_conf_exclude_all_but()
{
    local args
	local var_args

	for path in "$@"; do
		var_args="$var_args -e '/^[[:space:]]*include[[:space:]]\\+\"$(quote_sed_regexp "$path")\"/ n'"
	done

	var_args="$var_args -e '/^[[:space:]]*include[[:space:]]\\+/ d'"
	var_args="$var_args '$bind_local_conf_file'"

	quote_shell_args args "$var_args"
	eval "sed -i$args"
}

# usage: bind_local_conf_exclude_all_but_reserved
# exclude from the local conf all but the files specified in
# bind_local_conf_reserved list
bind_local_conf_exclude_all_but_reserved()
{
	bind_local_conf_exclude_all_but $bind_local_conf_reserved
}

############################################
## FreeIPA installation and configuration ##
############################################

# The installer run/status directory
ipa_install_rundir=/var/run/ipa_install

# The IPA server installation directory
ipa_home_dir=/var/lib/ipa

# The IPA client installation directory
ipa_client_home_dir=/var/lib/ipa-client

# The IPA install start timeout. Default is 10 s
ipa_install_start_timeout=10

# The IPA install stop timeout. Default is 60 s
ipa_install_stop_timeout=60

# Make at most this number of tries to uninstall
ipa_uninstall_maxtries=2

# The ipa-server-install command
ipa_server_install="$(which ipa-server-install 2>/dev/null ||:)"

# The full list of IPA services
ipa_service_list='control httpd named pki-tomcatd dirsrv krb5kdc ipa_memcached ipa-custodia ipa-otpd kadmin oddjobd ntpd ipa certmap.conf custodia named.conf sidgen winsync nis'

# Prepares the system for IPA installation
#
# usage: ipa_install_prepare
ipa_install_prepare()
{
	# Clear the bind local configuration
	bind_local_conf_exclude_all_but_reserved
	
	# Reload the bind daemon
	service_control 'bind' restart
}

# Runs the ipa-server-install command in the background
#
# usage: ipa_install_run <domain name> [other options]
# exit status: 100 -- already started
ipa_install_run()
{
	local name="$1"; shift
	local realm="$(echo "$name" | tr '[:lower:]' '[:upper:]')"
	_ipa_install_run --hostname="$(hostname)" -r "$realm" -n "$name" "$@"
}

# Runs the ipa-server-install command with arbitrary options
# in the background
#
# usage: _ipa_install_run [options]
_ipa_install_run()
{
	[ ! -e "$ipa_install_rundir/pid" ] || return 100

	if [ -z "$ipa_server_install" ]; then
		echo "Command 'ipa-server-install' not found. Please install the freeipa-server package" >&2
		return 123
	fi
	
	mkdir -p "$ipa_install_rundir"
	(
		## Lock
		##
		flock -E 100 9

		rm -f "$ipa_install_rundir/ret"
		
		## Check and uninstall, if not completely uninstalled
		##
		ucount=$ipa_uninstall_maxtries
		ret=0
		while ! ipa_uninstalled && [ $ucount -gt 0 ]; do
			ucount=$((ucount - 1))
			echo "UNINSTALL" >"$ipa_install_rundir/log"
			$ipa_server_install -U --uninstall 1>>"$ipa_install_rundir/log" 2>&1 &
			pid=$!
			echo "$pid" >"$ipa_install_rundir/pid"
			ret=0
			wait $pid || ret=$?
			[ $ret -ne 143 ] || break # break if interrupted
		done

		## Run the installer
		##
		if [ $ret -eq 0 -a "${1:-}" != "--uninstall" ]; then
			echo "INSTALL" >"$ipa_install_rundir/log"
			$ipa_server_install -U "$@" 1>>"$ipa_install_rundir/log" 2>&1 &
			pid=$!
			echo "$pid" >"$ipa_install_rundir/pid"
			ret=0
			wait $pid || ret=$?
		fi

		## Remove the PID file when both operations are finished
		echo "$ret" >"$ipa_install_rundir/ret"
		rm -f "$ipa_install_rundir/pid"
		exit $ret
	) 9>"$ipa_install_rundir/lock" &

	## Wait for the process to start
	##
	wcount=$ipa_install_start_timeout
	while [ ! -e "$ipa_install_rundir/pid" -a $wcount -gt 0 ]; do
		sleep 0.1
		wcount=$((wcount - 1))
	done

	## Check it's finally running
	##
	[ -e "$ipa_install_rundir/pid" ]
}

# Runs the ipa-server-install command with default options
# in the background
#
# usage: ipa_install_run_default <domain name> <password>
# exit status: 100 -- already started
ipa_install_run_default()
{
	# FIXME: Password in the command line
	ipa_install_run "$1" -p "$2" -a "$2" \
					--setup-dns --no-forwarders --no-reverse
}

# Stops a running ipa-server-install command
#
# usage: ipa_install_stop [signal]
ipa_install_stop()
{
	local signal="${1:-TERM}"
	local pid="$(cat "$ipa_install_rundir/pid" 2>/dev/null ||:)"
	
	if [ -n "$pid" ] && kill -0 $pid 2>/dev/null; then
		kill -$signal $pid
		local counter=$((ipa_install_stop_timeout * 10))
		while [ $counter -gt 0 ] && kill -0 $pid 2>/dev/null; do
			sleep 0.1
			counter=$((counter - 1))
			kill -$signal $pid ## try more kills :-]
		done

		if kill -0 $pid 2>/dev/null; then
			return 1
		else
			return 0
		fi
	else
		rm -f "$ipa_install_rundir/pid"
		return 0
	fi
}

# Runs the ipa-server-install --uninstall command in the background
#
# usage: ipa_uninstall_run [other options]
# exit status: 100 -- already started
ipa_uninstall_run()
{
	_ipa_install_run --uninstall	
}

# Outputs the given background ipa-server-install status values
#
# usage: _print_ipa_install_status <I|U> <stage> <progress> <message>
# output: <I|U>\t<percent>\t<message>
_print_ipa_install_status()
{
	local target="$1"; shift
	local stage="$1"; shift
	local progress="$1"; shift
	local message="$1"; shift

	local step=${progress%%/*}
	local total=${progress##*/}
	
	local min=0
	local max=0

	case "$stage" in
		# Install
		none|start)
			min=0; max=0
			;;
		ntpd)
			min=1; max=7
			;;
		dirsrv)
			min=8; max=14
			;;
		pki-tomcatd)
			min=15; max=21
			;;
		dirsrv2)
			min=22; max=28
			;;
		krb5kdc)
			min=29; max=35
			;;
		kadmin)
			min=36; max=42
			;;
		ipa_memcached)
			min=43; max=49
			;;
		ipa-otpd)
			min=50; max=56
			;;
		ipa-custodia)
			min=57; max=63
			;;
		httpd)
			min=64; max=70
			;;
		ldap-updates)
			min=71; max=72
			;;
		upgrade)
			min=73; max=77
			;;
		named)
			min=78; max=84
			;;
		client)
			min=86; max=99
			;;
		complete)
			min=100; max=100
			;;
		# Uninstall
		uninstall)
			min=0; max=0
			;;
		shutdown-all)
			min=6; max=6
			;;
		no-ntpd)
			min=12; max=12
			;;
		stop-kra)
			min=18; max=18
			;;
		stop-ca)
			min=24; max=24
			;;
		no-ca)
			min=30; max=30
			;;
		no-named)
			min=36; max=36
			;;
		no-web)
			min=42; max=42
			;;
		no-krb5kdc)
			min=48; max=48
			;;
		no-kadmin)
			min=54; max=54
			;;
		no-directory)
			min=60; max=60
			;;
		no-custodia)
			min=66; max=66
			;;
		no-memcached)
			min=72; max=72
			;;
		no-otpd)
			min=78; max=78
			;;
		rm-krb)
			min=84; max=84
			;;
		disable-client)
			min=90; max=90
			;;
		no-client)
			min=100; max=100
			;;
	esac

	if [ $total = 0 ]; then
		total=1
	fi

	local percent=$(((min*(100 - 100*step/total) + max*(100*step/total)) / 100))
	
	printf '%c\t%i%%\t%s\n' $target $percent "$message"
}

# Outputs the stage, progress and message values read
# from the ipa-server-install transcript log file.
#
# usage: _read_ipa_install_status [log-file]
# output: <I|U>\t<stage>\t<step/total>\t<message>
_read_ipa_install_status()
{
	local log="${1:-$ipa_install_rundir/log}"
	
	if [ ! -e "$log" ]; then
		printf '%s\t%i/%i\t%s\n' "none" 0 0 "Not started"
		return
	fi
		
	gawk -e '
function newstage(name) {
	  stage = name;
	  step = 0;
	  total = 0;
	  message = $0;
	  stepmessage = "";
}

function single(name) {
	  newstage(name);
	  step = 1;
	  total = 1;
}

BEGIN {
	  newstage("start");
	  message = "Starting setup...";
	  target = "I";
}

/^UNINSTALL$/ {
	  newstage("uninstall");
	  message = "Starting uninstall...";
	  target = "U";
}

/\.\s+.*$/ {
	  sub(/\.\s+.*$/, "", $0);
}

/^Configuring.*(ntpd)/ { newstage("ntpd"); }
/^Configuring.*(dirsrv)/ {
    if (dirsrv == "") {
	   newstage("dirsrv");
	   dirsrv = 1;
    } else {
       newstage("dirsrv2");
    }
}
/^Configuring.*(pki-tomcatd)/ { newstage("pki-tomcatd"); }
/^Configuring.*(krb5kdc)/ { newstage("krb5kdc"); }
/^Configuring\s+kadmin/ { newstage("kadmin"); }
/^Configuring\s+ipa_memcached/ { newstage("ipa_memcached"); }
/^Configuring\s+ipa-otpd/ { newstage("ipa-otpd"); }
/^Configuring\s+ipa-custodia/ { newstage("ipa-custodia"); }
/^Configuring.*(httpd)/ { newstage("httpd"); }
/^Applying\s+LDAP\s+updates/ { single("ldap-updates"); }
/^Upgrading\s+IPA/ { newstage("upgrade"); }
/^Configuring.*(named)/ { newstage("named"); }
/^Configuring\s+client\s+side/ { newstage("client"); }
/^Client\s+configuration\s+complete/ { single("client"); }
/^Setup\s+complete/ { single("complete"); }

/^Shutting\s+down\s+all\s+IPA\s+services/ { single("shutdown-all"); }
/^Unconfiguring\s+ntpd/ { single("no-ntpd"); }
/^Configuring\s+certmonger\s+to\s+stop\s+.*KRA/ { single("stop-kra"); }
/^Configuring\s+certmonger\s+to\s+stop\s+.*CA/ { single("stop-ca"); }
/^Unconfiguring\s+CA/ { single("no-ca"); }
/^Unconfiguring\s+named/ { single("no-named"); }
/^Unconfiguring\s+web/ { single("no-web"); }
/^Unconfiguring\s+krb5kdc/ { single("no-krb5kdc"); }
/^Unconfiguring\s+kadmin/ { single("no-kadmin"); }
/^Unconfiguring\s+directory/ { single("no-directory"); }
/^Unconfiguring\s+ipa-custodia/ { single("no-custodia"); }
/^Unconfiguring\s+ipa_memcached/ { single("no-memcached"); }
/^Unconfiguring\s+ipa-otpd/ { single("no-otpd"); }
/^Removing\s+Kerberos\s+service\s+principals/ { single("rm-krb"); }
/^Disabling\s+client/ { single("disable-client"); }
/^Client\s+uninstall\s+complete/ { single("no-client"); }
/^Removing\s+IPA\s+client\s+configuration/ { single("no-client"); }

/^ipa-server-install:\s*error:/ {
	message = "ERROR";
	stepmessage = $0;
	sub(/^ipa-server-install:\s*error:\s*/, "", stepmessage);
	sub(/^option\s+[^:]*:\s*/, "", stepmessage);
}

/^\s+\[[0-9]+\/[0-9]+\]:/ {
	split($0, progressmessage, ":");

	stepmessage=progressmessage[2];
	sub(/^\s+/, "", stepmessage);
	sub(/\s+$/, "", stepmessage);

	sub(/^\s+\[/, "", progressmessage[1]);
	sub(/\]$/, "", progressmessage[1]);
	split(progressmessage[1], steptotal, "/");
	step = steptotal[1];
	total = steptotal[2];
}

/^WARNING:/ {
	stepmessage = $0;
}

END {
	if ( stepmessage != "" ) {
	   message = message ": " stepmessage;
	}
	printf("%c\t%s\t%i/%i\t%s\n", target, stage, step, total, message);
}
' "$log"
}

# Outputs the background ipa-server-install status
#
# usage: ipa_install_status [log]
# output: <I|U>\t<percent>\t<message>
# returns: 0 -- running, 1 -- not running
ipa_install_status()
{
	local log="${1:-$ipa_install_rundir/log}"
	_read_ipa_install_status "$log" | (
		IFS='	'
		read target stage progress message
		_print_ipa_install_status "$target" "$stage" "$progress" "$message"
		ipa_install_running "$log"
	)
}

# Checks if ipa-server-install is running
#
# usage: ipa_install_running
# returns: 0 -- running, 1 -- not running
ipa_install_running()
{
	local log="${1:-$ipa_install_rundir/log}"
	[ -e "$log" -a -e "$ipa_install_rundir/pid" ]
}

# Returns the last known exit status of ipa-server-install
# or 0.
#
# usage: ipa_install_exitcode
ipa_install_exitcode() {
	local ret="$(cat "$ipa_install_rundir/ret" 2>/dev/null ||:)"
	if [ -n "$ret" ]; then
		echo "$ret"
	else
		echo "0"
	fi
}

# Clears the last known exit status -- set to 0.
#
# useage: ipa_install_clear_code
ipa_install_clear_code() {
	rm -f "$ipa_install_rundir/ret"
}

# Checks if all default (or specified) IPA services installed
#
# usage: ipa_check_services [service...]
# returns: 0 -- success, 1 -- failed
ipa_check_services()
{
	local srvlist="$ipa_service_list"
	[ $# -eq 0 ] || srvlist="$*"

	(
		[ ! -e "$ipa_home_dir/sysrestore/sysrestore.state" ] || cat "$ipa_home_dir/sysrestore/sysrestore.state"
		[ ! -e "$ipa_home_dir/sysupgrade/sysupgrade.state" ] || cat "$ipa_home_dir/sysupgrade/sysupgrade.state"
	) | gawk -v SRVLIST="$srvlist" -e '
BEGIN {
	  split(SRVLIST, srvlist);
}

/^\[.*\]\s*$/ {
	  sub(/^\[/, "", $0);
	  sub(/\]\s*$/, "", $0);
	  for (i in srvlist) {
	  	  if ( srvlist[i] == $0 ) {
		  	   delete srvlist[i];
			   break;
		  }
	  }
}

END {
	exit (length(srvlist) == 0 ? 0 : 1);
}'
}

# Checks the IPA server install status
#
# usage: ipa_installed
# returns: 0 -- fully installed, 1 -- not installed, 2 -- semi installed
ipa_installed()
{
	if [ -e "$ipa_home_dir/sysrestore/sysrestore.state" -o \
			-e "$ipa_home_dir/sysupgrade/sysupgrade.state" -o \
			-e "$ipa_home_dir/sysrestore/sysrestore.index" -o \
			-e "$ipa_home_dir/sysupgrade/sysupgrade.index" ]
	then
		if ipa_check_services; then
			return 0
		else
			return 2
		fi
	else
			return 1
	fi
}

# Checks the IPA server uninstall status
#
# usage: ipa_uninstalled
# returns: 0 -- fully uninstalled, 1 -- fully installed, 2 -- semi installed
ipa_uninstalled()
{
	local ret=0
	ipa_installed || ret=$?
	case "$ret" in
		0)
			ret=1
			;;
		1)
			ret=0
			;;
	esac

	return $ret
}
