#!/bin/bash

# Create slapd config for the NorduGrid/ARC information system

ARC_LOCATION=${ARC_LOCATION:-/usr}
if [ ! -d "$ARC_LOCATION" ]; then
    echo "ARC_LOCATION ($ARC_LOCATION) not found"
    exit 1
fi

# Source the config parsing routines
if [ -r "$ARC_LOCATION/share/arc/config_parser_compat.sh" ]; then
    . $ARC_LOCATION/share/arc/config_parser_compat.sh || exit $?
else
    echo "Could not find $ARC_LOCATION/share/arc/config_parser_compat.sh"
    exit 1
fi

ARC_CONFIG=${ARC_CONFIG:-/etc/arc.conf}
if [ ! -r "$ARC_CONFIG" ]; then
    echo "ARC configuration file ($ARC_CONFIG) not found"
    echo "If this file is in a non-standard place it can be set with the"
    echo "  ARC_CONFIG environment variable"
    exit 1
fi

# Read arc.conf
config_parse_file $ARC_CONFIG || exit $?

# Check for infosys-block
if ! config_match_section infosys; then
    echo "Missing infosys configuration block"
    exit 1
fi

config_hide_all
config_import_section common

# These options need to come from the infosys-block, not from common
unset CONFIG_logfile
unset CONFIG_user
unset CONFIG_port

config_import_section infosys

# Get ldap user from passwd
ldap_user=`getent passwd | grep ldap | sed 's/\([az]*\):.*/\1/g'`
if [ ! "xldap" = "x$ldap_user" ] && [ ! "xopenldap" = "x$ldap_user" ]; then
    echo "Could not find ldap or openldap user, using root"
    ldap_user=root
fi
bdii_user=${CONFIG_user:-$ldap_user}

# These values may be set in arc.conf, otherwise use sensible defaults
slapd_loglevel=${CONFIG_slapd_loglevel:-0}
slapd_hostnamebind=${CONFIG_slapd_hostnamebind:-"*"}
slapd_port=${CONFIG_port:-2135}
ldap_schema_dir=${CONFIG_ldap_schema_dir}
threads=${CONFIG_threads:-32}
timelimit=${CONFIG_timelimit:-2400}

bdii_location=${CONFIG_bdii_location:-/usr}
bdii_update_cmd=${CONFIG_bdii_update_cmd:-${bdii_location}/sbin/bdii-update}

arc_runtime_config=/var/run/arc/infosys
mkdir -p ${arc_runtime_config}
chown ${bdii_user}: ${arc_runtime_config}

giis_location=${CONFIG_giis_location:-$ARC_LOCATION}
giis_fifo=${CONFIG_giis_fifo:-$arc_runtime_config/giis-fifo}

bdii_tmp_dir=${CONFIG_bdii_tmp_dir:-/var/tmp/arc/bdii}
if [ ! -x $bdii_update_cmd ] || grep -q BDII_PID_FILE $bdii_update_cmd; then
    bdii_var_dir=${CONFIG_bdii_var_dir:-/var/lib/arc/bdii}
    bdii_run_dir=${CONFIG_bdii_run_dir:-/var/run/arc/bdii}
else
    bdii_var_dir=${CONFIG_bdii_var_dir:-/var/run/arc/bdii}
    bdii_run_dir=$bdii_var_dir
fi

bdii_db_config=${CONFIG_bdii_db_config:-"/etc/bdii/DB_CONFIG"}
bdii_database=${CONFIG_bdii_database:-"hdb"}

# Check for existance of core ldap schema
coreschema=$(find /etc/openldap /etc/ldap ${ldap_schema_dir} -name core.schema \
    -printf "%h/%f\n" 2>/dev/null)
if [ "x" = "x$coreschema" ]; then
    echo "Could not find ldap core schema file"
    exit 1
fi

# Check for existance of Glue schemas.
glueschemadir=$(find /etc/openldap /etc/ldap ${ldap_schema_dir} -name Glue-CORE.schema \
    -printf "%h\n" 2>/dev/null)
if [ "x" = "x$glueschemadir" ]; then
    echo "Error, could not find glue schema directory under /etc"
    exit 1
fi

# Check for existence of a system ldap, this command will be used by bdii
slapd_cmd=
slapd_env=
if [ "x" = "x$CONFIG_slapd" ]; then
    O_IFS=$IFS
    IFS=:
    # slapd2.4 search added to overcome SL5 problems
    # can be removed once redhat5 is not supported anymore
    for dir in $PATH; do
        if [ -x "$dir/slapd2.4" ]; then
            slapd_cmd="$dir/slapd2.4"
            break
        fi
    done
    if [ -z "$slapd_cmd" ]; then
        for dir in $PATH; do
            if [ -x "$dir/slapd" ]; then
                slapd_cmd="$dir/slapd"
                break
            fi
        done
    fi
    IFS=$O_IFS
else
    slapd_cmd=$CONFIG_slapd
fi

if [ -z "$slapd_cmd" ] || [ ! -x "$slapd_cmd" ]; then
    echo "Could not find ldap server binary, usually /usr/sbin/slapd"
    exit 1
fi

find_ldap_database_module() {
    # First try to find a separate module
    # openldap2.4 search added to overcome SL5 problems
    # can be removed once redhat5 is not supported anymore
    if [[ "$slapd_cmd" =~ '2.4' ]]; then
       ldapdir=$(find /usr/lib64/openldap2.4 /usr/lib/openldap2.4 \
        -name "back_${database}.la" -printf ":%h/" 2>/dev/null)
    else 
       ldapdir=$(find /usr/lib64/openldap /usr/lib/openldap /usr/lib64/ldap \
	/usr/lib/ldap -name "back_${database}.la" -printf ":%h/" 2>/dev/null)
    fi
    if [ -n "$ldapdir" ]; then
	# Separate module found
	ldapmodule="moduleload      back_${database}"
	grep -E -q "(^|:)${ldapdir}(:|$)" <<< ${ldapdirs} || \
	    ldapdirs=${ldapdirs}${ldapdir}
    else
	# Separate module not found - check for preloaded module
	ldapmodule=
	if [ $(grep -Ec "${database}_db_init|${database}_back_db_init" "$slapd_cmd") -eq 0 ]; then
	    # Module not found
	    database=
	fi
    fi
}

find_ldap_overlay_module() {
    # Try to find a separate module
    # openldap2.4 search added to overcome SL5 problems
    # can be removed once redhat5 is not supported anymore
    if [[ "$slapd_cmd" =~ '2.4' ]]; then
       ldapdir=$(find /usr/lib64/openldap2.4 /usr/lib/openldap2.4 \
        -name "${overlay}.la" -printf ":%h/" 2>/dev/null)
    else
    	ldapdir=$(find /usr/lib64/openldap /usr/lib/openldap /usr/lib64/ldap \
	/usr/lib/ldap -name "${overlay}.la" -printf ":%h/" 2>/dev/null)
    fi
    if [ -n "$ldapdir" ]; then
	# Separate module found
	ldapmodule="moduleload      ${overlay}"
	grep -E -q "(^|:)${ldapdir}(:|$)" <<< ${ldapdirs} || \
	    ldapdirs=${ldapdirs}${ldapdir}
    else
	# Module not found
	ldapmodule=
	overlay=
    fi
}

ldapdirs=

database=${bdii_database}
find_ldap_database_module
if [ -z "${database}" ]; then
    echo "Could not find ldap ${bdii_database} database module"
    exit 1
fi
moduleload_bdii="${ldapmodule}"

database=relay
find_ldap_database_module
if [ -z "${database}" ]; then
    echo "Could not find ldap relay database module, top-bdii integration is disabled."
fi
moduleload_relay="${ldapmodule}"

overlay=rwm
find_ldap_overlay_module
if [ -z "$overlay" ]; then
    echo "Could not find ldap rwm overlay module, top-bdii integration is disabled."
fi
moduleload_rwm="${ldapmodule}"


if [ "$(config_subsections infosys/index)" ]; then
    database=shell
    find_ldap_database_module
    if [ -z "${database}" ]; then
	echo "Could not find ldap shell database module"
	exit 1
    fi
    moduleload_shell="${ldapmodule}"

    if [ -n "${ldapmodule}" ]; then
    ldapdir=`sed 's/^://;s,/:.*,,' <<< "$ldapdir"`
	dlname=`( . ${ldapdir}/back_shell.la ; echo ${dlname} )`
	slapd_env="ARC_LDAPLIB_SHELL=${ldapdir}/${dlname}"
    fi

    if [ -r ${giis_location}/lib64/arc/arc-infoindex-slapd-wrapper.so ]; then
	pkglibdir=${giis_location}/lib64/arc
    elif [ -r ${giis_location}/lib/arc/arc-infoindex-slapd-wrapper.so ]; then
	pkglibdir=${giis_location}/lib/arc
    else
	error_echo "Error, could not find infoindex slapd wrapper"
	exit 1
    fi
    ldapdirs=${ldapdirs}:${pkglibdir}
    moduleload_index="moduleload      arc-infoindex-slapd-wrapper"

    if [ -z "${slapd_env}" ] ; then
	slapd_env="LD_PRELOAD=${pkglibdir}/arc-infoindex-slapd-wrapper.so"
    fi
else
    moduleload_shell=
    moduleload_index=
fi

ldapdirs=`sed 's/^://' <<< $ldapdirs`
#ldapdirs=`sed 's/:$//' <<< $ldapdirs`
if [ -n "$ldapdirs" ]; then
    modulepath="modulepath      $ldapdirs"
else
    modulepath=
fi

for i in "/etc/bdii/BDII.schema" "${bdii_location}/etc/BDII.schema"; do
    if [ -r $i ]; then
	bdii_schema="include $i"
	break
    fi
done

bdii_slapd_conf=$arc_runtime_config/bdii-slapd.conf
rm -f $bdii_slapd_conf
bdii_slapd_cmd=$arc_runtime_config/bdii-slapd.cmd
rm -f $bdii_slapd_cmd

# Ensure the configuration file is not world-readable,
# as it contains the slapd database password
(umask 077; > $bdii_slapd_conf)

pass=`/usr/bin/mkpasswd -s 0 2> /dev/null` || pass=$RANDOM$RANDOM

cat <<-EOF >> $bdii_slapd_conf
	# This file was automatically generated by $0."
	# Do not modify.

	include ${coreschema}

	${bdii_schema}

	#glue schemas
	include ${glueschemadir}/Glue-CORE.schema
	include ${glueschemadir}/Glue-CE.schema
	include ${glueschemadir}/Glue-CESEBind.schema
	include ${glueschemadir}/Glue-MDS.schema
	#glue2 schema
	include ${glueschemadir}/GLUE20.schema
	#nordugrid specific schemas
	include ${ARC_LOCATION}/share/arc/ldap-schema/nordugrid.schema

	$modulepath
	$moduleload_bdii
	$moduleload_relay
	$moduleload_rwm
	$moduleload_shell
	$moduleload_index

	allow bind_v2

	pidfile         $bdii_run_dir/db/slapd.pid
	argsfile        $bdii_run_dir/db/slapd.args
	loglevel        $slapd_loglevel
	threads         $threads
	idletimeout     120
	sizelimit       unlimited
	timelimit       $timelimit
	EOF

for vo in `config_subsections infosys/index`; do
    (
	CONFIG_name=
	config_import_section infosys/index/$vo
	indexname=${CONFIG_name:-$vo}

	cat <<-EOF >> $bdii_slapd_conf

		# Index Service: $vo
		database        shell
		suffix          "Mds-Vo-name=$indexname,o=Grid"
		bind            $giis_location/sbin/arc-infoindex-relay $giis_fifo
		add             $giis_location/sbin/arc-infoindex-relay $giis_fifo
		search          $giis_location/sbin/arc-infoindex-relay $giis_fifo
		access to * by * write
		EOF
    )
done

if [ -n "${moduleload_rwm}" ]; then 
    (
    admindomain="UNDEFINEDVALUE"
    CONFIG_name=
    config_import_section infosys/admindomain
    admindomain="urn:ad:${CONFIG_name:-$admindomain}"

    cat <<-EOF >> $bdii_slapd_conf

	# Relay to allow top-bdii to parse info as the CE was a site-bdii
	database        relay
	suffix		"GLUE2GroupID=resource,o=glue"
	overlay		rwm
	rwm-rewriteEngine	on
	rwm-rewriteContext	default
	rwm-rewriteRule	"GLUE2GroupID=resource,o=glue" "GLUE2GroupID=services,o=glue" ":"
	rwm-rewriteContext	searchFilter
	rwm-rewriteContext	searchEntryDN
	rwm-rewriteContext	searchAttrDN
	rwm-rewriteContext	matchedDN

	database        relay
	suffix          "GLUE2GroupID=resource,GLUE2DomainID=$admindomain,o=glue"
	overlay         rwm
	rwm-rewriteEngine       on
	rwm-rewriteContext      default
	rwm-rewriteRule "GLUE2GroupID=resource,GLUE2DomainID=$admindomain,o=glue" "GLUE2GroupID=services,o=glue" ":"
	rwm-rewriteContext      searchFilter
	rwm-rewriteContext      searchEntryDN
	rwm-rewriteRule "(.*[^ ],)?[ ]?GLUE2GroupID=services,o=glue" "\$1GLUE2GroupID=services,GLUE2DomainID=$admindomain,o=glue" ":"
	rwm-rewriteContext      searchAttrDN
	rwm-rewriteContext      matchedDN

	database        relay
	suffix          "GLUE2GroupID=services,GLUE2DomainID=$admindomain,o=glue"
	overlay         rwm
	suffixmassage   "GLUE2GroupID=services,o=glue"
	EOF
    )
fi

cat <<-EOF >> $bdii_slapd_conf

	# ${bdii_database} database definitions for o=grid
	database        ${bdii_database}
	cachesize       150000
	dbnosync
	suffix          "o=grid"
	checkpoint      131072 60
	rootdn          "o=grid"
	rootpw          $pass
	directory       $bdii_var_dir/db/arc

	# ${bdii_database} database definitions for o=glue
	database        ${bdii_database}
	cachesize       150000
	dbnosync
	suffix          "o=glue"
	checkpoint      131072 60
	rootdn          "o=glue"
	rootpw          $pass
	directory       $bdii_var_dir/db/glue2

	# ${bdii_database} database definitions for o=infosys
	database        ${bdii_database}
	cachesize       60
	dbnosync
	suffix          "o=infosys"
	checkpoint      131072 60
	rootdn          "o=infosys"
	rootpw          $pass
	directory       $bdii_var_dir/db/stats
	EOF

chown $bdii_user: $bdii_slapd_conf
[ -x /sbin/restorecon ] && /sbin/restorecon $bdii_slapd_conf

if [ "x$slapd_hostnamebind" = "x*" ]; then
    echo ${slapd_env} ${slapd_cmd} -f ${bdii_slapd_conf} -h \"ldap://${slapd_hostnamebind}:${slapd_port}\" -u ${bdii_user} > ${bdii_slapd_cmd}
else
    echo ${slapd_env} ${slapd_cmd} -f ${bdii_slapd_conf} -h \"ldap://localhost:${slapd_port} ldap://${slapd_hostnamebind}:${slapd_port}\" -u ${bdii_user} > ${bdii_slapd_cmd}
fi
chmod +x ${bdii_slapd_cmd}

# Initialize the database directories

mkdir -p $bdii_run_dir/db
mkdir -p $bdii_run_dir/archive
chown $bdii_user: $bdii_run_dir
chown $bdii_user: $bdii_run_dir/db
chown $bdii_user: $bdii_run_dir/archive
[ -x /sbin/restorecon ] && /sbin/restorecon -R $bdii_run_dir/db
[ -x /sbin/restorecon ] && /sbin/restorecon -R $bdii_run_dir/archive
mkdir -p $bdii_var_dir/archive
mkdir -p $bdii_var_dir/db/arc
mkdir -p $bdii_var_dir/db/glue2
mkdir -p $bdii_var_dir/db/stats
rm -f $bdii_var_dir/db/arc/* 2>/dev/null
rm -f $bdii_var_dir/db/glue2/* 2>/dev/null
rm -f $bdii_var_dir/db/stats/* 2>/dev/null
chown $bdii_user: $bdii_var_dir/db
chown $bdii_user: $bdii_var_dir/archive
chown $bdii_user: $bdii_var_dir/db/arc
chown $bdii_user: $bdii_var_dir/db/glue2
chown $bdii_user: $bdii_var_dir/db/stats
[ -x /sbin/restorecon ] && /sbin/restorecon -R $bdii_var_dir/db
[ -x /sbin/restorecon ] && /sbin/restorecon -R $bdii_var_dir/archive

# Workaround for BDII DB_CONFIG cachesize bigger than actual memory
set_cachesize_line=`egrep '^[[:space:]]*'set_cachesize ${bdii_db_config}`
if [ -n "${set_cachesize_line}" ]; then
    if [ -e /proc/meminfo ]; then
	memsize=$(grep MemFree /proc/meminfo | awk '{print $2 * 1000}')
	default_set_cachesize=$(echo ${set_cachesize_line} | awk '{print $2 * 1073741824 + $3}')
	half_memsize=$(( ${memsize} / 2 ))
	if [ $default_set_cachesize -ge $half_memsize ]; then
	    echo "The system does not fulfill BDII optimal memory requirements"
	    echo "ARC will try to fix it anyway..."
	    new_set_cachesize=$(( $memsize / 16 ))
	    TEMPBDIIDBCONFIG=`mktemp -q /tmp/DB_CONFIG.XXXXXX`
	    chmod 644 $TEMPBDIIDBCONFIG
	    sed "s/^set_cachesize.*$/set_cachesize 0 $new_set_cachesize 1/" ${bdii_db_config} > $TEMPBDIIDBCONFIG
	    bdii_db_config=${TEMPBDIIDBCONFIG}
	    echo "DB_CONFIG set_cachesize is now: 0 $new_set_cachesize 1"
	fi
    else 
	echo "/proc/meminfo does not exist. Cannot apply BDII memory workaround"
	echo "slapd might fail to start"
    fi
fi
# End of BDII set_cachesize workaround

# copy BDII DB_CONFIG in ARC locations
cp ${bdii_db_config} ${bdii_var_dir}/db/arc/DB_CONFIG
cp ${bdii_db_config} ${bdii_var_dir}/db/glue2/DB_CONFIG
cp ${bdii_db_config} ${bdii_var_dir}/db/stats/DB_CONFIG
chown $bdii_user: ${bdii_var_dir}/db/arc/DB_CONFIG
chown $bdii_user: ${bdii_var_dir}/db/glue2/DB_CONFIG
chown $bdii_user: ${bdii_var_dir}/db/stats/DB_CONFIG

# if the BDII low memory workaround has been applied, remove the temp file
if [ -r $TEMPBDIIDBCONFIG ]; then
    rm -f $TEMPBDIIDBCONFIG
fi

# Create cron configuration for slapd checkpointing cron

db_archive=${CONFIG_db_archive:-/usr/sbin/slapd_db_archive}
db_checkpoint=${CONFIG_db_checkpoint:-/usr/sbin/slapd_db_checkpoint}

slapd_cron_checkpoint=${CONFIG_slapd_cron_checkpoint:-disable}

if [ "x${slapd_cron_checkpoint}" = "xenable" ]; then
    if [ -n $db_checkpoint ] && [ -x $db_checkpoint ] && \
       [ -n $db_archive ]    && [ -x $db_archive ]    && \
       [ -d "/etc/cron.d" ]; then
	cat <<-EOF > /etc/cron.d/nordugrid-arc-aris
0-59/5 * * * * $bdii_user $db_checkpoint -1 -h $bdii_var_dir/db/arc && $db_archive -a -h $bdii_var_dir/db/arc|xargs rm -f
EOF
    else
	echo "You have enabled slapd cron checkpointing." 
	echo "However, it could not be configured."
	echo "Please check db_checkpoint and db_archive in ${ARC_CONFIG}."
	exit 1
    fi
fi
