#!/bin/sh
@%@UCRWARNING=# @%@
# pidfile: /var/run/slapd/slapd.pid
### BEGIN INIT INFO
# Provides:			 slapd
# Required-Start:	 $remote_fs $network $syslog
# Required-Stop:	 $remote_fs $network $syslog
# Default-Start:	 2 3 4 5
# Default-Stop:		 0 1 6
# Short-Description: OpenLDAP standalone server (Lightweight Directory Access Protocol)
### END INIT INFO
#
# Written by Miquel van Smoorenburg <miquels@drinkel.ow.org>.
# Modified for Debian GNU/Linux by Ian Murdock <imurdock@gnu.ai.mit.edu>.
# Modified for Debian by Christoph Lameter <clameter@debian.org>
# Modified for OpenLDAP by Ben Collins <bcollins@debian.org>
# Modified for UCS by Univention <packages@univention.de>


PATH=/bin:/usr/bin:/sbin:/usr/sbin
DAEMON=/usr/sbin/slapd
SLAPDCONF=/etc/ldap/slapd.conf

. /lib/lsb/init-functions

getpid ()
{
	# Bug #50616: without further args, pgrep would find any slapd process,
	# including those in docker containers. We care about only that one
	# process that is a direct child of PID 1. We also added that
	# restriction to start-stop-daemon. Should this script get an overhaul,
	# we need to keep docker processes in mind. Granted, pidfile should
	# work better.
	pgrep -f "^$DAEMON" -P 1
}

exit_if_start_is_running ()
{
	local pid
	pid=$(pgrep -cf "^/bin/sh /etc/init.d/slapd start$")
	if [ -n "$pid" ] && [ "$pid" -gt 1 ]; then
		log_action_msg "WARNING: Another /etc/init.d/slapd start is already in progress."
		exit 1
	fi
}

bdbrecover ()
{
	local back_bdb_version
	local DBRECOVER
	local DBSTAT
	local dbstat_output
	local dbstat_rc
	local bdb_version
	local stale_locks
	local logger_tag
	local line

	log_action_begin_msg "Check database: "
	# determine libdb version that back-bdb.so was linked against
	back_bdb_version="$(/usr/bin/ldd /usr/lib/ldap/back_bdb.so | sed -n 's|.*/usr/lib/.*/libdb-\([0-9.]*\).so.*|\1|p')"
	DBRECOVER="/usr/bin/db${back_bdb_version}_recover"
	DBSTAT="/usr/bin/db${back_bdb_version}_stat"
	if [ -e "${DBRECOVER}" ]; then
		# check if the db_stat version works on the database environment
		dbstat_output=$("${DBSTAT}" -h /var/lib/univention-ldap/ldap -Ne 2>&1)
		dbstat_rc=$?
		if [ "$dbstat_rc" = 0 ]; then
			bdb_version=$(echo "$dbstat_output" | sed -rn 's/^([^\s]+)\s+Environment version$/\1/p')
			if [ -n "$bdb_version" ]; then
				# check for stale locks or stale futexes
				stale_locks=$("${DBSTAT}" -h /var/lib/univention-ldap/ldap -Nc 2>&1 | sed -rn 's/([0-9]*)\t*Number of current locks/\1/p')
				if [ "$stale_locks" != 0 ]; then
					exit_if_start_is_running
					log_action_msg "WARNING"
					log_action_msg "WARNING: There are $stale_locks stale locks in LDAP backend Berkeley DB (version $bdb_version)"
					log_action_msg "WARNING: If slapd does not respond, manual LDAP dump/restore may be necessary"
					log_action_begin_msg "Continuing BDB database check: "
				fi
				logger_tag="slapd db${back_bdb_version}_recover"
				dbrecover_output=$("${DBRECOVER}" -h /var/lib/univention-ldap/ldap -e 2>&1)
				if [ "$?" != 0 ]; then
					exit_if_start_is_running
				else
					echo "$dbrecover_output" | while read line; do logger -t "$logger_tag" "$line"; done
				fi
				log_action_end_msg 0
			else
				log_action_msg "/var/lib/univention-ldap/ldap BDB Version does not seem to match the one back-bdb uses"
				log_action_msg "Skipping ${DBRECOVER} to avoid damage"

				logger_tag="slapd db${back_bdb_version}_stat"
				logger -t "$logger_tag" "BDB file version detection failed:"
				echo "$dbstat_output" | while read line; do logger -t "$logger_tag" "$line"; done
			fi
		else
			log_action_msg "Could not determine BDB version of /var/lib/univention-ldap/ldap"
			exit_if_start_is_running
			log_action_msg "Skipping ${DBRECOVER} to avoid damage"

			logger_tag="slapd db${back_bdb_version}_stat"
			logger -t "$logger_tag" "command failed ($dbstat_rc):"
			echo "$dbstat_output" | while read line; do logger -t "$logger_tag" "$line"; done
		fi
	else
		log_action_msg "back-bdb was linked against ${back_bdb_version}, but db${back_bdb_version}-util package does not seem to be installed"
		log_action_msg "Skipping ${DBRECOVER}"
	fi
}

check_subschema ()
{
	tmpfile=`mktemp`
	res=1
	count=0
	while [ $res != 0 ] ; do
		ldapsearch -x -H ldapi:/// -s base -b cn=Subschema 'objectClass=subschema' objectClasses attributeTypes matchingRules matchingRuleUse dITStructureRules dITContentRules nameForms ldapSyntaxes >$tmpfile
		res=$?
		if [ $res != 0 ]; then
			count=$((count+1))
			if [ $count -ge 5 ]; then
				echo "Failed to search schema"
				exit 1
			fi
			sleep 2
		fi
	done
@!@
if configRegistry.is_true('ldap/schema/export'):
		print('\tcp $tmpfile /var/www/ldap-schema.txt')
		print('\tchmod a+r /var/www/ldap-schema.txt')
@!@
	md5=`md5sum $tmpfile | awk '{print $1}'`
	rm -f "$tmpfile"

	if [ ! -d /var/lib/univention-ldap/schema ]; then
		mkdir /var/lib/univention-ldap/schema
	fi
	if [ ! -e /var/lib/univention-ldap/schema/md5 ]; then
		touch /var/lib/univention-ldap/schema/md5
	fi
	md5_old=`cat /var/lib/univention-ldap/schema/md5`
	if [ "$md5" != "$md5_old" ]; then
		if [ ! -d /var/lib/univention-ldap/schema/id ]; then
			mkdir /var/lib/univention-ldap/schema/id
		fi
		if [ ! -e /var/lib/univention-ldap/schema/id/id ]; then
			touch /var/lib/univention-ldap/schema/id/id
		fi
		id=`cat /var/lib/univention-ldap/schema/id/id`
		if [ -z "$id" ]; then
			id=0
		fi
		id=$((id+1))
		echo "$md5" >/var/lib/univention-ldap/schema/md5
		echo "$id" >/var/lib/univention-ldap/schema/id/id
		chown listener /var/lib/univention-ldap/schema/id/id
	else
		id=`cat /var/lib/univention-ldap/schema/id/id`
		if [ -z "$id" ]; then
			echo "1" >/var/lib/univention-ldap/schema/id/id
			chown listener /var/lib/univention-ldap/schema/id/id
		fi
	fi
}


if [ -f "$SLAPDCONF" ]; then
	pidfile=`grep ^pidfile $SLAPDCONF | awk '{print $2}'`
	if [ -z "$pidfile" ]; then
		pidfile="/var/run/slapd/slapd.pid"
	fi
	pidfiledir="$(dirname $pidfile)"
	test ! -e "$pidfiledir" && mkdir -p "$pidfiledir"
else
	exit 0
fi

test -f $DAEMON || exit 0

case "$1" in
  start)
	# check ucr autostart setting
	if [ -f "/usr/share/univention-config-registry/init-autostart.lib" ]; then
		. "/usr/share/univention-config-registry/init-autostart.lib"
		check_autostart ldap ldap/autostart
	fi
	# check that no slapd is running
	if ! start-stop-daemon --start --ppid 1 --quiet --exec $DAEMON --test > /dev/null; then
		log_action_msg "LDAP server already running"
		exit 0
	fi
	logger "/etc/init.d/slapd $1 (pid: $$, ppid:$(ps -p $PPID -o pid= -o comm=))"
@!@
if configRegistry['ldap/database/type'] == 'bdb':
	print('\t# check backend database')
	print('\tbdbrecover')
@!@

	log_action_begin_msg "Starting ldap server(s): slapd "

@!@
uris=['ldapi:///']
for p in configRegistry.get('slapd/port', '7389').split(','):
	uris.append('ldap://:%s/' % p)
for p in configRegistry.get('slapd/port/ldaps', '7636').split(','):
	uris.append('ldaps://:%s/' % p)
print('\turis="%s"' % ' '.join(uris))
if configRegistry.get('ldap/maxopenfiles'):
	print('\t\tulimit -n %s' % configRegistry['ldap/maxopenfiles'])
@!@		start-stop-daemon --start --ppid 1 --quiet \
			--exec $DAEMON -- -h "${uris}" -f "${SLAPDCONF}"
	rvalue=$?
	fail_msg=""
	# Bug #33993
	if [ "i686" = "$(uname -m)" ]; then
		for i in $(seq 1 3); do
			[ $rvalue -eq 0 ] && break
			sleep 1
			start-stop-daemon --start --ppid 1 --quiet --exec $DAEMON -- -h "${uris}" -f "${SLAPDCONF}"
			rvalue=$?
		done
		[ $rvalue -ne 0 ] && fail_msg="slapd start failed, check if ldap/database/mdb/maxsize is supported on this architecture"
	fi
	log_action_end_msg $rvalue "$fail_msg"
	if [ $rvalue != 0 ]; then
		exit_if_start_is_running
		[ -e /usr/sbin/slapschema ] && log_action_msg $(/usr/sbin/slapschema 2>&1)
		exit $rvalue
	fi

	test -e /var/run/slapd/ldapi && ln -sf /var/run/slapd/ldapi /var/run/ldapi

@!@
if configRegistry['ldap/server/type']=="master":
	print('\tlog_action_begin_msg "Checking Schema ID: "')
	print('\tcheck_subschema 2>&1')
	print('\tlog_action_end_msg 0')
@!@

	if [ -e /var/lib/univention-directory-replication/failed.ldif ]; then
		log_action_begin_msg "Found failed.ldif. Importing "
		test -x /usr/sbin/univention-directory-replication-resync && /usr/sbin/univention-directory-replication-resync /var/lib/univention-directory-replication/failed.ldif >>/var/log/univention/listener.log 2>&1
		if [ $? = 0 ]; then
			log_action_end_msg 0
		else
			log_action_end_msg 1
			log_action_msg "Please check /var/log/univention/listener.log"
			exit 1
		fi
	fi

	;;
  stop)
	logger "/etc/init.d/slapd $1 (pid: $$, ppid:$(ps -p $PPID -o pid= -o comm=))"
	log_action_begin_msg "Stopping ldap server(s): slapd "
	## first try with pidfile
	start-stop-daemon --stop --ppid 1 --quiet --oknodo --retry 10 \
		--pidfile "$pidfile" -v | sed -rn 's/.*, (retry #|refused to die)/\1/p' \
		| while read line; do log_action_cont_msg "$line"; done
	## check process (instead of [ $? -eq 2 ])
	pid=$(getpid)
	if [ -n "$pid" ]; then
		log_action_end_msg 1
		## try via executable name
		log_action_begin_msg "Slapd still running ($pid), terminating via executable name"
		start-stop-daemon --stop --ppid 1 --quiet --oknodo --retry 10 \
			--exec $DAEMON -v | sed -rn 's/.*, (retry #|refused to die)/\1/p' \
			| while read line; do log_action_cont_msg "$line"; done
		rm -f "$pidfile"
	fi
	log_action_end_msg 0
	;;
  restart|force-reload)
	logger "/etc/init.d/slapd $1 (pid: $$, ppid:$(ps -p $PPID -o pid= -o comm=))"
	log_action_msg "Restarting ldap server(s)"
	$0 stop
	$0 start
	;;
  force-stop)
	logger "/etc/init.d/slapd $1 (pid: $$, ppid:$(ps -p $PPID -o pid= -o comm=))"
	log_action_begin_msg "Stopping ldap servers (forced) "
	start-stop-daemon --stop --ppid 1 --quiet --oknodo --retry forever/KILL \
		--exec $DAEMON
	rm -f "$pidfile"
	log_action_end_msg 0
	;;
  crestart)
	logger "/etc/init.d/slapd $1 (pid: $$, ppid:$(ps -p $PPID -o pid= -o comm=))"
	if pidof slapd > /dev/null 2>&1
	then
		$0 restart
	else
		log_action_msg "No slapd process found, no restart needed"
	fi
	;;
  graceful-stop)
	$0 stop
	# logger "/etc/init.d/slapd $1 (pid: $$, ppid:$(ps -p $PPID -o pid= -o comm=))"
	# log_action_begin_msg "Sending HUP to ldap server(s): slapd "
	# signal_plan="HUP/10/TERM/5/KILL/1"
	# ## first try with pidfile
	# start-stop-daemon --stop --ppid 1 --quiet --oknodo --retry "$signal_plan" \
	# 	--pidfile "$pidfile" -v | sed -rn 's/.*, (retry #|refused to die)/\1/p' \
	# 	| while read line; do log_action_cont_msg "$line"; done
	# ## check process (instead of [ $? -eq 2 ])
	# pid=$(getpid)
	# if [ -n "$pid" ]; then
	# 	log_action_end_msg 1
	# 	## try via executable name
	# 	log_action_begin_msg "Slapd still running ($pid), sending HUP via executable name"
	# 	start-stop-daemon --stop --ppid 1 --quiet --oknodo --retry "$signal_plan" \
	# 		--exec $DAEMON -v | sed -rn 's/.*, (retry #|refused to die)/\1/p' \
	# 		| while read line; do log_action_cont_msg "$line"; done
	# 	rm -f "$pidfile"
	# fi
	# log_action_end_msg 0
	;;
  graceful-restart)
	$0 restart
	# logger "/etc/init.d/slapd $1 (pid: $$, ppid:$(ps -p $PPID -o pid= -o comm=))"
	# log_action_msg "Initiating graceful reload of ldap server(s)"
	# $0 graceful-stop
	# $0 start
	;;
  *)
	echo "Usage: $0 {start|stop|restart|force-stop|crestart|graceful-stop|graceful-restart}"
	exit 1
	;;
esac

exit 0
