#!/bin/bash

# Define logging functions
send_systemd_notify() {
    # return if no systemd-notify found
    type systemd-notify >/dev/null 2>&1 || return
    systemd-notify "$@"
}

log_failure_msg() {
    send_systemd_notify --status "Error: $@"
    echo $@
}

# Create slapd config for the NorduGrid/ARC information system

ARC_LOCATION=${ARC_LOCATION:-/cvmfs/dirac.egi.eu/dirac/v8.0.47/Linux-x86_64}
if [ ! -d "$ARC_LOCATION" ]; then
    log_failure_msg "ARC_LOCATION ($ARC_LOCATION) not found"
    exit 1
fi

# ARC_CONFIG
if [ "x$ARC_CONFIG" = "x" ]; then
    if [ -r $ARC_LOCATION/etc/arc.conf ]; then
        ARC_CONFIG=$ARC_LOCATION/etc/arc.conf
    elif [ -r /etc/arc.conf ]; then
        ARC_CONFIG=/etc/arc.conf
    fi
    if [ ! -r "$ARC_CONFIG" ]; then
        log_failure_msg "arc.conf is missing at path: $ARC_CONFIG or no ARC_LOCATION is set"
        log_failure_msg "If this file is in a non-standard place it can be set"
        log_failure_msg "  with the ARC_CONFIG environment variable"
        exit 1
    fi
fi

# Define runtime config location for infosys LDAP
prefix=/cvmfs/dirac.egi.eu/dirac/v8.0.47/Linux-x86_64
runtime_config_dir=/run/arc
if [ ! -d "$runtime_config_dir" ]; then
    mkdir -p "$runtime_config_dir"
fi
export ARC_RUNCONFIG="$runtime_config_dir/arc-infosys-ldap.conf"
unset runtime_config_dir
unset prefix

# Define arcconfig-parser and dump running configuration
arcconfig_parser=${ARC_LOCATION}/libexec/arc/arcconfig-parser
${arcconfig_parser} -c ${ARC_CONFIG} --save -r ${ARC_RUNCONFIG}

# Check for infosys block
if ! ${arcconfig_parser} --load -r ${ARC_RUNCONFIG} -b infosys; then
    log_failure_msg "Missing [infosys] configuration block"
    exit 1
fi

# Check for infosys/ldap block
if ! ${arcconfig_parser} --load -r ${ARC_RUNCONFIG} -b infosys/ldap; then
    log_failure_msg "Missing [infosys/ldap] configuration block"
    exit 1
fi

eval $(${arcconfig_parser} --load -r ${ARC_RUNCONFIG} -b infosys/ldap -b infosys -b common -e bash)

bdii_user=$CONFIG_user
if [ -z "$bdii_user" ]; then
    # Get ldap user from passwd
    bdii_user=`getent passwd ldap openldap | sed 's/:.*//;q'`
    if [ -z "$bdii_user" ]; then
	echo "Warning, could not find ldap or openldap user"
	echo "resorting to using the root user"
	bdii_user=root
    fi
fi

# 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}

infosys_ldap_run_dir=${CONFIG_infosys_ldap_run_dir:-/run/arc/infosys}
mkdir -p ${infosys_ldap_run_dir}
chown ${bdii_user}: ${infosys_ldap_run_dir}

bdii_var_dir=${CONFIG_bdii_var_dir:-/var/lib/arc/bdii}
bdii_run_dir=${CONFIG_bdii_run_dir:-/run/arc/bdii}

# PIDFile location handling
slapd_pid_file=$( readlink -m ${bdii_run_dir}/db/slapd.pid )

# forced pidfile location instead of arc.conf-based (if FORCE_ARC_RUNDIR is set)
if [ -n "$FORCE_ARC_RUNDIR" ]; then
    pid_dir="${FORCE_ARC_RUNDIR}/bdii/db"
    mkdir -p "$pid_dir"
    chown -R ${bdii_user}: "$pid_dir"
    pid_file="$( readlink -m ${pid_dir}/slapd.pid )"
    if [ "x${slapd_pid_file}" != "x${pid_file}" ]; then
        custom_pid_file="${slapd_pid_file}"
        rm -f "$custom_pid_file"
        slapd_pid_file="${pid_file}"
    fi
    unset pid_dir pid_file
fi

rm -f "${slapd_pid_file}"

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
    log_failure_msg "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
    log_failure_msg "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=
if [ "x" = "x$CONFIG_slapd" ]; then
    O_IFS=$IFS
    IFS=:
    for dir in $PATH; do
	if [ -x "$dir/slapd" ]; then
	    slapd_cmd="$dir/slapd"
	    break
	fi
    done
    IFS=$O_IFS
else
    slapd_cmd=$CONFIG_slapd
fi

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

find_ldap_database_module() {
    # First try to find a separate module
    ldapdir=$(find /usr/lib64/openldap /usr/lib/openldap /usr/lib64/ldap \
	/usr/lib/ldap -name "back_${database}.la" -printf ":%h/" 2>/dev/null)
    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
    ldapdir=$(find /usr/lib64/openldap /usr/lib/openldap /usr/lib64/ldap \
	/usr/lib/ldap -name "${overlay}.la" -printf ":%h/" 2>/dev/null)
    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
    log_failure_msg "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 ${arcconfig_parser} --load -r ${ARC_RUNCONFIG} -b infosys/index; then
    database=shell
    find_ldap_database_module
    if [ -z "${database}" ]; then
        log_failure_msg "Could not find ldap shell database module"
        exit 1
    fi
    moduleload_shell="${ldapmodule}"
else
    moduleload_shell=
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=${infosys_ldap_run_dir}/bdii-slapd.conf
rm -f $bdii_slapd_conf

# Put SLAPD start helpers to known directory
helpers_dir=$infosys_ldap_run_dir
if [ -n "$FORCE_ARC_RUNDIR" ]; then
    helpers_dir="${FORCE_ARC_RUNDIR}/infosys"
    mkdir -p "${helpers_dir}"
fi

bdii_slapd_cmd=${helpers_dir}/bdii-slapd.cmd
rm -f $bdii_slapd_cmd
bdii_slapd_post_cmd=${helpers_dir}/bdii-slapd-post.cmd
rm -f $bdii_slapd_post_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

	allow bind_v2

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

if [ -n "${moduleload_rwm}" ]; then 
    admindomain=$(${arcconfig_parser} --load -r ${ARC_RUNCONFIG} -b infosys/glue2 -o admindomain_name)
    admindomain="urn:ad:${admindomain:-UNDEFINEDVALUE}"

    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

# Write slapd starting command
if [ "x$slapd_hostnamebind" = "x*" ]; then
    echo exec ${slapd_cmd} -f ${bdii_slapd_conf} -h \"ldap://${slapd_hostnamebind}:${slapd_port}\" -u ${bdii_user} > ${bdii_slapd_cmd}
else
    echo exec ${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}

# Write post-exec script to check slapd is up and running
cat <<-EOF > ${bdii_slapd_post_cmd}
iterlimit=30
while [ \$iterlimit -ge 0 ] && ! [ -r "${slapd_pid_file}" ]; do
  sleep 1
  iterlimit=\$(expr \$iterlimit - 1)
done
EOF

# copy forced pidfile to custom arc.conf pidfile (if forced pid was requested)
if [ -n "${custom_pid_file}" ]; then
    echo "mkdir -p \"${custom_pid_file%/*}\"" >> ${bdii_slapd_post_cmd}
    echo "cp -a \"${slapd_pid_file}\" \"${custom_pid_file}\"" >> ${bdii_slapd_post_cmd}
fi

# 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 '{printf "%.0f", $2 * 1024}')
	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