#!/bin/bash

#====================================================================
# CLI script for OpenLDAP
# (http://www.openldap.org).
# 
# chkconfig: 2345 27 73
# description: OpenLDAP
#
# Copyright (C) 2017 David COUTADEUR
# Copyright (C) 2008 Jonathan CLARKE
# Copyright (C) 2007 Olivier LI-KIANG-CHEONG
# Copyright (C) 2007 Thomas CHEMINEAU
# Copyright (C) 2005 Sebastien BAHLOUL 
# Copyright (C) 2005 Raphael OUAZANA 
# Copyright (C) 2005 Clement OUDOT
# Copyright (C) 2010 LTB-project.org
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# GPL License: http://www.gnu.org/licenses/gpl.txt
#
#====================================================================

#====================================================================
# Version
#====================================================================
VERSION="2.5"

#====================================================================
# Default parameters (unless /usr/local/openldap/etc/openldap/{script_name}.conf)
#====================================================================
# IP and port to listen (use wildcard * in IP to listen on all interfaces)
IP="*"
SSLIP="*"
PORT="389"
SSLPORT="636"
LDAPI_SOCKETDIR=""
LDAPI_SOCKETURL=""

# OpenLDAP directory and files
SLAPD_PATH="/usr/local/openldap"
SLAPD_PID_FILE="$SLAPD_PATH/var/run/slapd.pid"
SLAPD_CONF="$SLAPD_PATH/etc/openldap/slapd.conf"
SLAPD_CONF_DIR=""
SLAPD_SERVICES="ldap://$IP:$PORT ldaps://$SSLIP:$SSLPORT"
SLAPD_PARAMS=""
SLAPD_BIN="$SLAPD_PATH/libexec/slapd"
SLAPD_USER=""
SLAPD_GROUP=""
SLAPD_SYSLOG_LOCAL_USER="local4"

DATA_PATH="$SLAPD_PATH/var/openldap-data"

SLAPADD_BIN="$SLAPD_PATH/sbin/slapadd"
SLAPADD_PARAMS="-q"
SLAPCAT_BIN="$SLAPD_PATH/sbin/slapcat"
SLAPCAT_PARAMS="-o ldif-wrap=no"
SLAPINDEX_BIN="$SLAPD_PATH/sbin/slapindex"
SLAPTEST_BIN="$SLAPD_PATH/sbin/slaptest"

SLURPD_ACTIVATE="0"
SLURPD_PID_FILE="$SLAPD_PATH/var/run/slurpd.pid"
SLURPD_PARAMS=""
SLURPD_BIN="$SLAPD_PATH/libexec/slurpd"

# BerkeleyDB directory and files
BDB_PATH="/usr/local/berkeleydb"
DB_ARCHIVE_BIN="$BDB_PATH/bin/db_archive"
DB_RECOVER_BIN="$BDB_PATH/bin/db_recover"
RECOVER_AT_STARTUP="1" # 0 for OpenLDAP 2.3.x

# Backup
BACKUP_AT_SHUTDOWN="1"
BACKUP_PATH="/tmp/openldap"
BACKUP_SUFFIX="`date +%Y%m%d%H%M%S`.ldif"
BACKUP_COMPRESS_EXT="" # gz, bz2, ...
BACKUP_COMPRESS_BIN="" # /bin/gzip, /bin/bzip2, ...
BACKUP_UNCOMPRESS_BIN="" # /bin/gunzip, /bin/bunzip2, ...

# Other
TIMEOUT="60"      # Max time to stop process
FD_LIMIT="2048"   # Max file descriptor
DEBUG_LEVEL="256" # Debug loglevel

# Script specific
PROG_NAME=`basename $0 | sed 's/^[KS][0-9][0-9]//'` # For nice messages
OS=`uname -s`   # To adapt message printing
MYUID=`id -u`     # For UNIX compatibility => modify this command
MYGID=`id -g`     # For UNIX compatibility => modify this command
PS_COMMAND="ps -efww"	# This ensures full width for ps output but doesn't work on Solaris - use "ps -ef"
declare -a PROVIDER

# Return functions' value
RETVAL=""

#====================================================================
# Message function
#====================================================================
message() {
	# $1: syslog level
	# $2: message

	# Log to syslog
	logger -p "$SLAPD_SYSLOG_LOCAL_USER.$1" -t $PROG_NAME -i "$2"

	# Output to console
	if [ "$1" = "alert" ]
	then
		echo "$PROG_NAME: $2">&2
	else
		echo "$PROG_NAME: $2">&1
	fi
}

#====================================================================
# Specific functions
#====================================================================

get_confvalues() {
	# $1: parameter
	# $RETVAL: list of values

        # Search in conffile or backconfig
	if [ -n "$SLAPD_CONF_DIR" ]; then
        case $1 in
	directory)
		backconfig_get_values "olcDbDirectory"
	;;
	suffix)
		backconfig_get_values "olcSuffix" "(|(objectclass=olcBdbConfig)(objectclass=olcHdbConfig)(objectclass=olcMdbConfig))"
	;;
        *)
		RETVAL=""
	;;
	esac
        else
		conffile_get_values $1
	fi
}

conffile_get_values() {
	# $1: parameter in slapd.conf
	# $RETVAL: list of values

	list=`sed "s/\r//" $SLAPD_CONF | grep "^$1[[:space:]]" | grep -v '^#' | sed "s/$1[[:space:]]*//" | sed "s/ /#20/g"| sed -e 's/"//g'`

	if [ "$list" ]; then
		RETVAL="$list"
	else
		RETVAL=""
	fi
}

backconfig_get_values() {
	# $1: parameter
	# $2: LDAP filter (optional)
	# $RETVAL: list of returned values

	if [ -z "$SLAPD_CONF_DIR" -o ! -d "$SLAPD_CONF_DIR" ]
	then
		message "alert" "[ALERT] Could not parse configuration directory"
		RETVAL=""
		return
	fi

	slapcat_cmd="$SLAPCAT_BIN $SLAPCAT_PARAMS -F $SLAPD_CONF_DIR -b cn=config"
	if [ -n "$2" ]
	then
		if [ -n "$SU" ]; then
			slapcat_cmd="$slapcat_cmd -a '$2'"
		else
			slapcat_cmd="$slapcat_cmd -a $2"
		fi
	fi
	if [ -z "$SU" ]
	then
		list=`$slapcat_cmd | perl -p0e 's/\n //g' | grep "^$1:" | sed "s/$1: //" | sed "s/ /#20/g"`
	else
		list=`$SU "$slapcat_cmd" | perl -p0e 's/\n //g' | grep "^$1:" | sed "s/$1: //" | sed "s/ /#20/g"`
	fi

	if [ -n "$list" ]; then
		RETVAL="$list"
	else
		RETVAL=""
	fi
}

syncrepl_get_value() {
	# $1: syncrepl value
	# $2: parameter in syncrepl value
	# $RETVAL: value

	syncrepl=$1
	parameter=$2

	# Check if parameter value is surrounded with quotes
	echo "$syncrepl" | grep -e $parameter'="' > /dev/null
	noquotes=$?

	if [ $noquotes -gt 0 ]
	then
		RETVAL=$( echo "$syncrepl" | sed -e 's/.*'$parameter'[ \t]*=[ \t]*\([^ \t]\+\).*$/\1/' | sed -e 's/["]//g' | sed -e "s/[']//g")
	else
		RETVAL=$( echo "$syncrepl" | sed -e 's/.*'$parameter'[ \t]*=[ \t]*"\([^"\t]\+\)".*$/\1/' | sed -e 's/["]//g' | sed -e "s/[']//g");
	fi
}

#====================================================================
# Load specific parameters
#====================================================================
CLI_CONF_FILE=/usr/local/openldap/etc/openldap/${PROG_NAME}.conf
if [ -f ${CLI_CONF_FILE} ]
then
	. ${CLI_CONF_FILE}
	message "info" "[INFO] Using ${CLI_CONF_FILE} for configuration"
else
	message "info" "[INFO] Using built-in configuration - this may cause some problems"
fi

# Use systemd to manage the service if it's present
_use_systemctl=0

# When systemd runs a shell script, bash sets PPID to 1 (init)
if [ $PPID -ne 1 ] && \
    # This variable lets the user disable systemd redirection
    [ -z "$SYSTEMCTL_SKIP_REDIRECT" ] && \
    # This tests whether systemd is currently managing the systemd
    [ -d /run/systemd/system ] && \
    # This variable is set when systemd >=232 runs a service
    # We need this test in case systemd does not run the slapd-cli script
    # directly (PPID>1) but runs it through /etc/init.d/slapd
    [ -z "$INVOCATION_ID" ]  ;
then
	SYSTEMD_SERVICE_NAME=slapd
	_use_systemctl=1
fi

#====================================================================
# Initiate 'su' command
#====================================================================
if [ "$SLAPD_USER" -a $MYUID -eq 0 ]
then
	SU="su -s /bin/bash - $SLAPD_USER -c "
fi

#====================================================================
# Initial checks
#====================================================================

# Make sure the pidfile directory exists with correct permissions
piddir=`dirname "$SLAPD_PID_FILE"`
if [ ! -d "$piddir" ]; then
	mkdir -p "$piddir"
	[ -z "$SLAPD_USER" ] || chown -R "$SLAPD_USER" "$piddir"
	[ -z "$SLAPD_GROUP" ] || chgrp -R "$SLAPD_GROUP" "$piddir"
fi

# Rights to execute binaries
for i in "$SLAPD_BIN" "$SLAPCAT_BIN" "$SLAPINDEX_BIN" "$SLAPTEST_BIN"
do
	if [ ! -x $i ]
	then
		message "alert" "[ALERT] Can't execute $i"
		exit 1
	fi
done

# Rights to read configuration
if [ "$SLAPD_CONF" -a ! -r "$SLAPD_CONF" ]
then
	message "alert" "[ALERT] Can't read $SLAPD_CONF"
	exit 1
fi

# Activate slurpd? (get from configuration file)
if [ -n "$SLAPD_CONF" -a ! -d "$SLAPD_CONF_DIR" ]; then
	SLURPD_ACTIVATE=`grep "^replica" $SLAPD_CONF | wc -l`
fi

# Right to execute slurpd, if used
if [ $SLURPD_ACTIVATE -ne 0 -a ! -x "$SLURPD_BIN" ]
then
	message "alert" "[ALERT] Can't execute $SLURPD_BIN"
	exit 1
fi

# Is there a configuration directory ?
if [ "$SLAPD_CONF_DIR" -a ! -w "$SLAPD_CONF_DIR" ]
then
	message "alert" "[ALERT] Can't write to configuration directory $SLAPD_CONF_DIR"
	exit 1
fi

# Are you root (for port < 1024)?
if [ $PORT -lt 1024 -a $MYUID -ne 0 ]
then
	message "alert" "[ALERT] Only root can launch OpenLDAP on port $PORT"
	exit 1
fi

# Create LDAPI socket
if [ "$LDAPI_SOCKETDIR" -a ! -r "$LDAPI_SOCKETDIR" ]
then
    message "info" "[INFO] Create LDAPI socket dir $LDAPI_SOCKETDIR"
    mkdir -p "$LDAPI_SOCKETDIR"
    [ -z "$SLAPD_USER" ] || chown -R "$SLAPD_USER" "$LDAPI_SOCKETDIR"
    [ -z "$SLAPD_GROUP" ] || chgrp -R "$SLAPD_GROUP" "$LDAPI_SOCKETDIR"
fi

#====================================================================
# Functions
#====================================================================
start_slapd() {

	# $1: debug level
	
	# Exit 0 if slapd is already running
	# LSB compliance
	slapd_status
	
	if [ $? -eq 0 ]
	then 
		message "info" "[OK] OpenLDAP is already running"
		exit 0
	fi   

	# Check if db_recover is required
	if [ $RECOVER_AT_STARTUP -eq 1 ]
	then
		db_recover
	else
		message "info" "[INFO] No db_recover done"	
	fi

	# Start message
	message "info" "[INFO] Launching OpenLDAP..."

	# File descriptor limit, only for root
	if [ $MYUID -eq 0 ]
	then
		ulimit -n $FD_LIMIT
		if [ $? -eq 0 ]
		then
			message "info" "[OK] File descriptor limit set to $FD_LIMIT"
		else
			message "warning" "[WARNING] Fail to set file descriptor limit to $FD_LIMIT, going to next step"
		fi
	else
		message "info" "[INFO] File descriptor limit not modified (require root privileges)"
	fi

	# Parameters
	if [ "$SLAPD_CONF_DIR" ]
	then
		SLAPD_PARAMS="$SLAPD_PARAMS -F $SLAPD_CONF_DIR"
	elif [ "$SLAPD_CONF" ]
	then
		SLAPD_PARAMS="$SLAPD_PARAMS -f $SLAPD_CONF"
	fi

	if [  "$SLAPD_USER" -a $MYUID -eq 0 ]
	then
		SLAPD_PARAMS="$SLAPD_PARAMS -u $SLAPD_USER"
	fi

	if [ "$SLAPD_GROUP" -a $MYGID -eq 0 ]
	then
		SLAPD_PARAMS="$SLAPD_PARAMS -g $SLAPD_GROUP"
	fi

	if [ "$SLAPD_SYSLOG_LOCAL_USER" ]
	then
		SLAPD_PARAMS="$SLAPD_PARAMS -l $SLAPD_SYSLOG_LOCAL_USER" 
	fi

	# It's time to start slapd

	if [ -n "$1" ]; then
		$SLAPD_BIN -h "$SLAPD_SERVICES" $SLAPD_PARAMS -d $1
	else
		if [ $_use_systemctl -eq 1 ] ; then
			systemctl start "$SYSTEMD_SERVICE_NAME"
		else
			$SLAPD_BIN -h "$SLAPD_SERVICES" $SLAPD_PARAMS
		fi
		sleep 1

		# Presence of PID file
		if [ ! -r $SLAPD_PID_FILE ]
		then
			message "alert" "[ALERT] No PID file for OpenLDAP"
			exit 1
		fi

		# Is slapd launched?
		PID=`cat $SLAPD_PID_FILE`
		if [ ! -e /proc/$PID ]
		then
			message "alert" "[ALERT] OpenLDAP not running"
			exit 1
		else
			message "info" "[OK] OpenLDAP started"
		fi
	fi
}

start_slurpd() {
	if [ $SLURPD_ACTIVATE -eq 0 ]
	then
		return 1
	fi

	# Start message
	message "info" "[INFO] Launching OpenLDAP replication..."

	# Parameters
	if [ "$SLAPD_CONF_DIR" ]
	then
		SLAPD_PARAMS="$SLAPD_PARAMS -F $SLAPD_CONF_DIR"
	elif [ "$SLAPD_CONF" ]
	then
		SLAPD_PARAMS="$SLAPD_PARAMS -f $SLAPD_CONF"
	fi

	# It's time to start slurpd
	if [ -z "$SU" ]
	then
		$SLURPD_BIN $SLURPD_PARAMS
	else
		$SU "$SLURPD_BIN $SLURPD_PARAMS"
	fi
	sleep 1

	# Presence of PID file
	if [ ! -r $SLURPD_PID_FILE ]
	then
		message "alert" "[ALERT] No PID file for slurpd"
		exit 1
	fi

	# Is slurpd launched?
	PID=`cat $SLURPD_PID_FILE`
	if [ ! -e /proc/$PID ]
	then
		message "alert" "[ALERT] slurpd not running"
		exit 1
	else
		message "info" "[OK] OpenLDAP replication started"
	fi
}

stop_slapd() {
	# Stop message
	message "info" "[INFO] Halting OpenLDAP..."

	# Presence of PID file
	if [ ! -r $SLAPD_PID_FILE ]
	then
		message "info" "[INFO] Can't read PID file, to stop OpenLDAP try: $0 forcestop"
		return 1
	else
		if [ $_use_systemctl -eq 1 ] ; then
			systemctl stop "$SYSTEMD_SERVICE_NAME"
			message "info" "[OK] OpenLDAP stopped"
		else
			PID=`cat $SLAPD_PID_FILE`
			kill -INT $PID
			# Waiting loop
			i=0
			while [ -e /proc/$PID ]
			do
				if [ $i -eq $TIMEOUT ]
				then
					# Timeout
					message "alert" "[ALERT] OpenLDAP still running (PID $PID), try: $0 forcestop"
					exit 1
				fi
				i=`expr $i + 1`
				sleep 1
			done

			message "info" "[OK] OpenLDAP stopped after $i seconds"
		fi

	fi

	# Backup if necessary
	if [ $BACKUP_AT_SHUTDOWN -eq 1 ]
	then
		backup
	else
		message "info" "[INFO] No data backup done"
	fi
}

stop_slurpd() {
	# Desactivate slurpd?
	if [ $SLURPD_ACTIVATE -eq 0 ]
	then
		return 1
	fi

	# Stop message
	message "info" "[INFO] Halting OpenLDAP replication..."

	# Presence of PID file
	if [ ! -r $SLURPD_PID_FILE ]
	then
		message "warning" "[WARNING] Can't read PID file, to stop slurpd try: $0 forcestop"
	else
		PID=`cat $SLURPD_PID_FILE`
		kill -INT $PID

		# Waiting loop
		i=0
		while [ -e /proc/$PID ]
		do
			if [ $i -eq $TIMEOUT ]
			then
				# Timeout, need to kill
				message "alert" "[ALERT] slurpd still running (PID $PID), try: $0 forcestop"
				return 1
			fi
			i=`expr $i + 1`
			sleep 1
		done

		message "info" "[OK] OpenLDAP replication stopped after $i seconds"
	fi
}

forcestop() {
	# Stop message
	message "info" "[INFO] Killing OpenLDAP with force..."

	# Presence of PID file
	if [ ! -r $SLAPD_PID_FILE ]
	then
		# Escape special characters into $SLAPD_SERVICES
		slapd_services="`echo "$SLAPD_SERVICES" | sed 's/\*/\\\*/g'`"

		# Check if any slapd process are running
		if [ `$PS_COMMAND | grep $SLAPD_BIN | grep "$slapd_services" | grep -v grep | wc -l` -eq 0 ]
		then
			message "info" "[INFO] Found no OpenLDAP process running with $SLAPD_SERVICES"
		else
			# Try a killall
			/usr/bin/killall -KILL $SLAPD_BIN

			if [ $? -eq 0 ]
			then
				message "info" "[OK] All OpenLDAP process killed with force"
			else
				message "alert" "[ALERT] Unable to kill OpenLDAP with force"
				exit 1
			fi
		fi
	else
		PID=`cat $SLAPD_PID_FILE`
		kill -KILL $PID

		if [ $? -eq 0 ]
		then
			message "info" "[OK] OpenLDAP process killed with force (PID $PID)"
		else
			message "alert" "[ALERT] Unable to kill OpenLDAP with force (PID $PID)"
			exit 1
		fi
	fi

	# Stop message
	message "info" "[INFO] Killing OpenLDAP replication with force..."

	# Presence of PID file
	if [ ! -r $SLURPD_PID_FILE ]
	then
		# Check if any slapd process are running
		if [ `$PS_COMMAND | grep $SLURPD_BIN | grep -v grep | wc -l` -eq 0 ]
		then
			message "info" "[INFO] Found no slurpd process running"
		else
			# Try a killall
			/usr/bin/killall -KILL $SLURPD_BIN

			if [ $? -eq 0 ]
			then
				message "info" "[OK] slurpd process killed with force"
			else
				message "alert" "[ALERT] Unable to kill slurpd with force"
				exit 1
			fi
		fi
	else
		PID=`cat $SLURPD_PID_FILE`
		kill -KILL $PID

		if [ $? -eq 0 ]
		then
			message "info" "[OK] slurpd process killed with force (PID $PID)"
		else
			message "alert" "[ALERT] Unable to kill slurpd with force (PID $PID)"
			exit 1
		fi
	fi
}

slapd_status() {
	if [ $_use_systemctl -eq 1 ]
	then
		if systemctl --quiet is-active "$SYSTEMD_SERVICE_NAME"
		then
			return 0
		else
			return 1
		fi
	fi

	# Return 0 if slapd is running, 1 if slapd is stopped, 2 if we can't say
	if [ ! -r $SLAPD_PID_FILE ]
	then
		# Escape special characters into $SLAPD_SERVICES
		slapd_services="`echo "$SLAPD_SERVICES" | sed 's/\*/\\\*/g'`"

		# Check if any slapd process are running
		if [ `$PS_COMMAND | grep $SLAPD_BIN | grep "$slapd_services" | grep -v grep | wc -l` -eq 0 ]
		then
			return 1
		else
			return 2
		fi
	else
		PID=`cat $SLAPD_PID_FILE`
	fi

	if [ ! -e /proc/$PID ]
	then
		return 1
	else
		return 0
	fi
}

display_status() {

	# Print script version
	message "info" "[INFO] LDAP Tool Box OpenLDAP init script version $VERSION" 

	# Get status
	slapd_status

	status=$?

	if [ $status -eq 0 ]
	then
		PID=`cat $SLAPD_PID_FILE`

		message "info" "[INFO] Process OpenLDAP is running (PID $PID)"
		message "info" "[INFO] Listening to services $SLAPD_SERVICES"

		CPU=`ps -p $PID -o %cpu=`
		MEM=`ps -p $PID -o %mem=`

		message "info" "[INFO] Process usage: $CPU% CPU / $MEM% MEM"


	fi

	if [ $status -eq 1 ]
	then
		message "info" "[INFO] Process OpenLDAP is not running"
	fi

	if [ $status -eq 2 ]
	then
		message "info" "[INFO] Unable to determine OpenLDAP status"
	fi

	# Get detected suffix
	get_confvalues "directory"
	dbdirs=$RETVAL
	get_confvalues "suffix"
	dbsufs=$RETVAL

	if [ ! -z "$dbdirs" -o ! -z "$dbsufs" ]
	then
		i=1
		for dbdir in $dbdirs
		do
			# Table is not allowed, so we use awk
			suf=`echo $dbsufs | awk -v j="$i" 'BEGIN{OFS=" "} {print $j}'`
			sufprint=`echo $suf | sed "s/#20/ /"`
			if [ ! -z $suf ]
			then
				message "info" "[INFO] Detected suffix: $sufprint"
			fi
			i=`expr $i + 1`
		done
	fi

	exit $status
}

configtest() {
	# Start message
	message "info" "[INFO] Launching OpenLDAP configuration test..."

	SLAPTEST_PARAMS="-u"	

	if [ "$SLAPD_CONF_DIR" ]
	then
		SLAPTEST_PARAMS="$SLAPTEST_PARAMS -F $SLAPD_CONF_DIR"
	elif [ "$SLAPD_CONF" ]
	then
		SLAPTEST_PARAMS="$SLAPTEST_PARAMS -f $SLAPD_CONF"
	fi

	# slaptest
	$SLAPTEST_BIN $SLAPTEST_PARAMS > /dev/null 2>&1

	if [ $? -eq 0 ]
	then
		message "info" "[OK] OpenLDAP configuration test successful"
	else
		message "alert" "[ALERT] OpenLDAP configuration test failed"
		exit 1
	fi
}

db_recover() {
	# Start message
	message "info" "[INFO] Launching OpenLDAP database recovery..."

	if [ ! -x $DB_RECOVER_BIN ]
	then
		message "alert" "[ALERT] Cannot execute $DB_RECOVER_BIN, aborting database recovery"
		exit 1
	fi

	# slapd must be stopped
	slapd_status

	if [ $? -ne 1 ]
	then
		message "alert" "[ALERT] OpenLDAP is running or was not correctly shut down, aborting database recovery"
		exit 1
	fi

	dbdirs="$DATA_PATH"

	if [ "$DATA_PATH" = "auto" ]
	then
		get_confvalues "directory"
		dbdirs=$RETVAL

		if [ -z "$dbdirs" ]
		then
			message "alert" "[ALERT] No database directories found"
			exit 1
		fi
	fi

	for dbdir in $dbdirs
	do

		# db_recover
		if [ -z "$SU" ]
		then
			$DB_RECOVER_BIN -h "$dbdir"
		else
			$SU "$DB_RECOVER_BIN -h \"$dbdir\""
		fi

		if [ $? -eq 0 ]
		then
			message "info" "[OK] OpenLDAP $dbdir database recovery successful"
		else
			message "alert" "[ALERT] OpenLDAP $dbdir database recovery failed"
			exit 1
		fi

	done
}

reindex() {
	# Start message
	message "info" "[INFO] Launching OpenLDAP database reindexing..."

	if [ "$SLAPD_CONF_DIR" ]
	then
		SLAPINDEX_PARAMS="-F $SLAPD_CONF_DIR"
	elif [ "$SLAPD_CONF" ]
	then
		SLAPINDEX_PARAMS="-f $SLAPD_CONF"
	fi

	# slapd must be stopped
	slapd_status

	if [ $? -ne 1 ]
	then
		message "alert" "[ALERT] OpenLDAP is running or was not correctly shut down, aborting reindexing"
		exit 1
	else
		# slapindex
		if [ -z "$SU" ]
		then
			$SLAPINDEX_BIN $SLAPINDEX_PARAMS
		else
			$SU "$SLAPINDEX_BIN $SLAPINDEX_PARAMS"
		fi

		if [ $? -eq 0 ]
		then
			message "info" "[OK] OpenLDAP database reindexing successful"
		else
			message "alert" "[ALERT] OpenLDAP database reindexing failed"
			exit 1
		fi
	fi
}

removelogs() {
	# Start message
	message "info" "[INFO] Launching OpenLDAP database logs archiving..."

	if [ ! -x $DB_ARCHIVE_BIN ]
	then
		message "alert" "[ALERT] Can't execute $DB_ARCHIVE_BIN, aborting database archiving"
		exit 1
	fi


	# slapd must be stopped
	slapd_status

	if [ $? -ne 1 ]
	then
		message "alert" "[ALERT] OpenLDAP is running or was not correctly shut down, aborting archiving"
		exit 1
	fi

	dbdirs="$DATA_PATH"

	if [ "$DATA_PATH" = "auto" ]
	then
		get_confvalues "directory"
		dbdirs=$RETVAL

		if [ -z "$dbdirs" ]
		then
			message "alert" "[ALERT] No database directories found"
			exit 1
		fi
	fi

	for dbdir in $dbdirs
	do

		# db_archive
		if [ -z "$SU" ]
		then
			$DB_ARCHIVE_BIN -h "$dbdir" -d
		else
			$SU "$DB_ARCHIVE_BIN -h \"$dbdir\" -d"
		fi

		if [ $? -eq 0 ]
		then
			message "info" "[OK] OpenLDAP $dbdir database logs archiving successful"
		else
			message "alert" "[ALERT] OpenLDAP $dbdir database logs archiving failed"
			exit 1
		fi

	done
}

backup() {
	# Start message
	message "info" "[INFO] Launching OpenLDAP database backup..."

	# Backup directory
	mkdir -p "$BACKUP_PATH"

	if [ "$SLAPD_CONF_DIR" ]
	then
		SLAPCAT_PARAMS="$SLAPCAT_PARAMS -F $SLAPD_CONF_DIR"
	elif [ "$SLAPD_CONF" ]
	then
		SLAPCAT_PARAMS="$SLAPCAT_PARAMS -f $SLAPD_CONF"
	fi

	# Do backup for all databases
	dbdirs="$DATA_PATH"
	get_confvalues "suffix"
	dbsufs=$RETVAL

	if [ "$DATA_PATH" = "auto" ]
	then
		get_confvalues "directory"
		dbdirs=$RETVAL

		if [ -z "$dbdirs" -o -z "$dbsufs" ]
		then
			message "alert" "[ALERT] No database directories found"
			exit 1
		fi
	fi

	i=1
	for dbdir in $dbdirs
	do
		# Table is not allowed, so we use awk
		suf=`echo $dbsufs | awk -v j="$i" 'BEGIN{OFS=" "} {print $j}'`

		if [ -z $suf ]; then
			message "info" "[INFO] No suffix for $dbdir"
		else
			sufprint=`echo $suf | sed "s/#20/ /"`
			dir=`basename $dbdir`
			file="$BACKUP_PATH/$dir-$BACKUP_SUFFIX"

			# slapcat
			if [ -z "$SU" ]
			then
				$SLAPCAT_BIN -b "$sufprint" $SLAPCAT_PARAMS -l "$file"
				ret=$?
			else
				$SU "$SLAPCAT_BIN -b \"$sufprint\" $SLAPCAT_PARAMS > \"$file\""
				ret=$?
				chown $SLAPD_USER:$SLAPD_GROUP $file
			fi

			# alert
			if [ $ret -ne 0 ]
			then
				message "alert" "[ALERT] OpenLDAP database backup failed"
				exit 1
			fi

			# compress
			if [ -z $BACKUP_COMPRESS_EXT ]
			then
				message "info" "[OK] data saved in $file"
			else
				if [ -z "$SU" ]
				then
					$BACKUP_COMPRESS_BIN "$file"
				else
					$SU "$BACKUP_COMPRESS_BIN $file"
				fi

				# alert
				if [ $? -ne 0 ]
				then
					message "alert" "[ALERT] OpenLDAP database backup compression failed"
					exit 1
				fi
				message "info" "[OK] Data saved in ${file}.$BACKUP_COMPRESS_EXT"
			fi
		fi

		i=`expr $i + 1`
	done

}

backupconfig() {
	# Start message
	message "info" "[INFO] Launching OpenLDAP configuration backup..."

	# Backup directory
	mkdir -p "$BACKUP_PATH"

	file=""

	if [ "$SLAPD_CONF_DIR" ]
	then
		file="$BACKUP_PATH/config-$BACKUP_SUFFIX"

		# slapcat
		if [ -z "$SU" ]
		then
			$SLAPCAT_BIN $SLAPCAT_PARAMS -n0 -F $SLAPD_CONF_DIR -l "$file"
			ret=$?
		else
			$SU "$SLAPCAT_BIN $SLAPCAT_PARAMS -n0 -F $SLAPD_CONF_DIR > \"$file\""
			ret=$?
			chown $SLAPD_USER:$SLAPD_GROUP $file
		fi

	elif [ "$SLAPD_CONF" ]
	then
		file="$BACKUP_PATH/slapd-`date +%Y%m%d%H%M%S`.conf"
		cp $SLAPD_CONF $file
		ret=$?
	fi

	# alert
	if [ $ret -ne 0 ]
	then
		message "alert" "[ALERT] OpenLDAP configuration backup failed"
		exit 1
	fi

	message "info" "[OK] Configuration saved in $file"

}

restore() {
	# Start message
	message "info" "[INFO] Launching OpenLDAP database restore..."

	if [ "$SLAPD_CONF_DIR" ]
	then
		SLAPADD_PARAMS="$SLAPADD_PARAMS -F $SLAPD_CONF_DIR"
	elif [ "$SLAPD_CONF" ]
	then
		SLAPADD_PARAMS="$SLAPADD_PARAMS -f $SLAPD_CONF"
	fi

	# Do restore for all databases
	dbdirs="$DATA_PATH"
	get_confvalues "suffix"
	dbsufs=$RETVAL

	if [ "$DATA_PATH" = "auto" ]
	then
		get_confvalues "directory"
		dbdirs=$RETVAL

		if [ -z "$dbdirs" -o -z "$dbsufs" ]
		then
			message "alert" "[ALERT] No database directories found"
			exit 1
		fi
	fi

	i=1
	for dbdir in $dbdirs
	do
		# Table is not allowed, so we use awk
		suf=`echo $dbsufs | awk -v j="$i" 'BEGIN{OFS=" "} {print $j}'`

		if [ -z $suf ]; then
			message "info" "[INFO] No suffix for $dbdir"
		else
			sufprint=`echo $suf | sed "s/#20/ /"`
			dir=`basename $dbdir`

			# Get the most recent backup for this database
			file=`ls -1t "$BACKUP_PATH/$dir-"* 2>/dev/null | head -1`
			
			if [ -z $file ]; then
				message "info" "[INFO] No backup file for $sufprint, skipping..."
			else
				message "info" "[INFO] Restore file $file for $sufprint"

				# uncompress
				if [ "$BACKUP_COMPRESS_EXT" ]
				then
					if [ -z "$SU" ]
					then
						$BACKUP_UNCOMPRESS_BIN "$file"
					else
						$SU "$BACKUP_UNCOMPRESS_BIN $file"
					fi

					# alert
					if [ $? -ne 0 ]
					then
						message "alert" "[ALERT] OpenLDAP database backup uncompression failed"
						exit 1
					fi

					file=${file%\.*}
				fi

				SLAPADD_PARAMS="$SLAPADD_PARAMS -l $file"

				# Delete current data
				if [ -z "$SU" ]
				then
					rm -rf "$dbdir/"*
				else
					$SU "rm -rf $dbdir/"*
				fi

				# Import backup
				if [ -z "$SU" ]
				then
					$SLAPADD_BIN -b "$sufprint" $SLAPADD_PARAMS
				else
					$SU "$SLAPADD_BIN -b \"$sufprint\" $SLAPADD_PARAMS"
				fi
				
				# alert
				if [ $? -ne 0 ]
				then
					message "alert" "[ALERT] OpenLDAP database restore failed for $sufprint"
					exit 1
				fi
				message "info" "[OK] Data restored for $sufprint"

				# compress backup again if needed
				if [ "$BACKUP_COMPRESS_EXT" ]
				then
					if [ -z "$SU" ]
					then
						$BACKUP_COMPRESS_BIN "$file"
					else
						$SU "$BACKUP_COMPRESS_BIN $file"
					fi

					# alert
					if [ $? -ne 0 ]
						then
						message "alert" "[ALERT] OpenLDAP database backup compression failed"
						exit 1
					fi
				fi
			fi
		fi
		i=`expr $i + 1`
	done

}

restoreconfig() {
	# Start message
	message "info" "[INFO] Launching OpenLDAP configuration restore..."

	if [ "$SLAPD_CONF_DIR" ]
	then
		# Get the most recent backup of cn=config
		file=`ls -1t "$BACKUP_PATH/config-"* 2>/dev/null | head -1`

		if [ -z $file ]; then
			message "info" "[INFO] No configuration backup found, skipping..."
		else
			message "info" "[INFO] Restore configuration file $file"

			# Delete current data
			if [ -z "$SU" ]
			then
				rm -rf "$SLAPD_CONF_DIR/"*
			else
				$SU "rm -rf $SLAPD_CONF_DIR/"*
			fi

			# Import backup
			if [ -z "$SU" ]
			then
				$SLAPADD_BIN -n0 -F $SLAPD_CONF_DIR -l $file
			else
				$SU "$SLAPADD_BIN -n0 -F $SLAPD_CONF_DIR -l $file"
			fi
		fi

	elif [ "$SLAPD_CONF" ]
	then
		# Get the most recent backup of slapd.conf
		file=`ls -1t "$BACKUP_PATH/slapd-"*.conf 2>/dev/null | head -1`

		if [ -z $file ]; then
			message "info" "[INFO] No configuration backup found, skipping..."
		else
			message "info" "[INFO] Restore configuration file $file"
			cp -f $file $SLAPD_CONF		
		fi
	fi

	# alert
	if [ $? -ne 0 ]
	then
		message "alert" "[ALERT] OpenLDAP configuration restore failed"
		exit 1
	fi
	message "info" "[OK] Configuration restored"
}

get_provider_config() {
	i=0
	if [ "$SLAPD_CONF_DIR" ]
	then
		# Get list of provider
		PROVIDERS=$( $SLAPCAT_BIN -n0 -F "$SLAPD_CONF_DIR" -o ldif-wrap=no | grep -E '^olcSyncrepl' )
		while echo $PROVIDERS | grep -q -E '^olcSyncrepl'
		do
			# deletes first syncrepl keyword
			PROVIDERS=$( echo $PROVIDERS | sed -e 's/^olcSyncrepl//' )
			# store first provider found
			PROVIDER[$i]=$( echo $PROVIDERS | sed -e 's/olcSyncrepl.*$//' )
			# delete first provider from list of providers
			PROVIDERS=$( echo $PROVIDERS | sed -e 's/^.*\(olcSyncrepl.*\)$/\1/' )

			i=$[i+1]
		done

	elif [ "$SLAPD_CONF" ]
	then
		# Get list of provider
		PROVIDERS=$( sed -n '/^syncrepl/,/^([^ \t]|$)/p' $SLAPD_CONF )
		while echo $PROVIDERS | grep -q -E '^syncrepl'
		do
			# deletes first syncrepl keyword
			PROVIDERS=$( echo $PROVIDERS | sed -e 's/^syncrepl//' )
			# store first provider found
			PROVIDER[$i]=$( echo $PROVIDERS | sed -e 's/syncrepl.*$//' )
			# delete first provider from list of providers
			PROVIDERS=$( echo $PROVIDERS | sed -e 's/^.*\(syncrepl.*\)$/\1/')

			i=$[i+1]
		done
	fi
}

check_sync() {
	message "info" "[INFO] Checking synchronization..."
	get_provider_config
	get_confvalues "suffix"
	SUFFIX=$RETVAL
	RES=0 # result OK by default (if nothing to check, sync=OK)

	# Is there any provider configuration?
	if [[ ${#PROVIDER[@]} -eq "0" ]]; then
		message "info" "[OK] No provider found in configuration - sync status not checked"
		exit $RES
	fi

	# Parse PROVIDERS
	for index in "${!PROVIDER[@]}"
	do
		PROVIDER[$index]=$( echo "${PROVIDER[index]}" | sed -e 's/#.*$//g' )
		# get provider config parameters
		syncrepl_get_value "${PROVIDER[$index]}" "provider"
		PHOST=$RETVAL
		syncrepl_get_value "${PROVIDER[$index]}" "binddn"
		PBINDDN=$RETVAL
		syncrepl_get_value "${PROVIDER[$index]}" "credentials"
		PBINDPW=$RETVAL
		syncrepl_get_value "${PROVIDER[$index]}" "searchbase"
		PBASEDN=$RETVAL
		echo "Checking host ${PHOST}"
		# Get remote provider contextCSN (one contextCSN per line)
		CONTEXTCSN=$( ${SLAPD_PATH}/bin/ldapsearch -x -H "${PHOST}" -D "${PBINDDN}" -w "${PBINDPW}" -b "${PBASEDN}" -o nettimeout=5 -s base contextCSN -LLL 2>/dev/null )

		# Checking status of previous command
		if [ $? -ne 0 ]; then
			echo "    ERROR: search on remote host impossible!"
			let "RES=-1"
			break
		fi
		CONTEXTCSN=$( echo "$CONTEXTCSN" | grep contextCSN | sed -e 's/^contextCSN: //i' )

		# get local contextCSN
		# TODO: improve this test to check via ldapsearch (need manager account)
		if [ "$SLAPD_CONF_DIR" ]; then
			localContextCSN=$( $SLAPCAT_BIN -b $SUFFIX -F "$SLAPD_CONF_DIR" -o ldif-wrap=no 2>/dev/null | head -n 100 | sed -e '/^$/,$d' | grep 'contextCSN' | sed -e 's/^contextCSN: //' )
		elif [ "$SLAPD_CONF" ]; then
			localContextCSN=$( $SLAPCAT_BIN -b $SUFFIX -f "$SLAPD_CONF" -o ldif-wrap=no 2>/dev/null | head -n 100 | sed -e '/^$/,$d' | grep 'contextCSN' | sed -e 's/^contextCSN: //' )
		else
			echo "No acceptable configuration found"
			exit 1
		fi

		# compare contextCSN values
		# for each provider contextCSN
		for CSN in $CONTEXTCSN; do
			#  identify sid part
			#  contextCSN = 20171128170017.708386Z#000000#001#000000
			#                                             ^^^
			SID=$( echo ${CSN} | sed -e 's/^.*\(#[0-9]\+#\)[^#]*$/\1/' )
			echo "  Checking SID ${SID}"
			
			#  check matching local slave ContextCSN
			if echo "$localContextCSN" | grep -q "$SID" ; then
				# get corresponding local slave contextCSN
				matchingLocalContextCSN=$( echo -e "$localContextCSN" | grep "$SID" )
				# Check if corresponding local slave contextCSN is identical to remote provider contextCSN
				if echo "$matchingLocalContextCSN" | grep -q "$CSN" ; then
					echo "    INFO: remote ($CSN) and local ($matchingLocalContextCSN) contextCSN found and synchronized"
				else
					echo "    ERROR: remote ($CSN) and local ($matchingLocalContextCSN) contextCSN found, but not synchronized!"
					let "RES=RES+1"
				fi
			else
				echo "    ERROR: no matching local contextCSN ($localContextCSN) found for remote ($CSN) contextCSN!"
				let "RES=RES+1"
			fi
		done

		# return result (OK|KO)
	done
	if [[ "$RES" -eq "0" ]]; then
		message "info" "[OK] Local directory synchronized to every declared provider"
	elif [[ "$RES" -eq "-1" ]]; then
		message "error" "[KO] Error while checking synchronization"
	else
		message "error" "[KO] Local directory not synchronized to one of its declared providers"
	fi
	exit $RES
}

#====================================================================
# Action switch
#====================================================================
case $1 in
	start)
	configtest
	start_slurpd
	start_slapd
	;;
	stop)
	stop_slapd
	stop_slurpd
	;;
	forcestop)
	forcestop
	;;
	restart)
	configtest
	stop_slapd
	stop_slurpd
	start_slurpd
	start_slapd
	;;
	debug)
	stop_slapd
	stop_slurpd
	start_slurpd
	start_slapd $DEBUG_LEVEL
	;;
	force-reload)
	configtest
	forcestop
	start_slurpd
	start_slapd
	;;
	status)
	display_status
	;;
	configtest)
	configtest
	;;
	db_recover)
	db_recover
	;;
	reindex)
	reindex
	;;
	removelogs)
	removelogs
	;;
	backup)
	backup
	;;
	restore)
	stop_slapd
	stop_slurpd
	restore
	start_slurpd
	start_slapd
	;;
	backupconfig)
	backupconfig
	;;
	restoreconfig)
	stop_slapd
	stop_slurpd
	restoreconfig
	configtest
	start_slurpd
	start_slapd
	;;
	checksync)
	check_sync
	;;
	*)
	echo "Usage: $0 {start|stop|forcestop|restart|debug|force-reload|status|configtest|db_recover|reindex|removelogs|backup|restore|backupconfig|restoreconfig|checksync}"
	exit 1
	;;
esac

#====================================================================
# Exit
#====================================================================
exit 0

