#!/usr/bin/ruby

require 'erb'
require 'fileutils'
require 'getoptlong'
require 'open3'
require 'tempfile'

def get_slapcat_datasize()
  stdout_str, stderr_str, status = Open3.capture3('slapcat')
  unless status.success? then
    raise 'failed to query the database contents'
  end

  datasize = stdout_str.length
  raise 'no output from slapcat' if datasize == 0

  datasize
end

use_force = false

opts = GetoptLong.new(
  [ '--help', '-h',         GetoptLong::NO_ARGUMENT ],
  [ '--force',              GetoptLong::NO_ARGUMENT ],
)

opts.each do |opt, arg|
  case opt
  when '--help'
    puts <<EOF
Usage: puavo-init-ldap-slave [OPTION]...

Configure and initialize slapd.

-h, --help    display this help and exit
    --force   clear and re-initialize the database

EOF
    exit(0)
  when '--force'
    use_force = true
  end
end

if ARGV.length != 0 then
  warn 'error: extra arguments'
  exit(1)
end

Sentinel_file = '/state/var/lib/ldap/init_ldap_slave_done'

if File.exist?(Sentinel_file) && !use_force then
  warn 'error: slapd is already initialized, use the --force, Luke!'
  exit(1)
end

system('service', 'slapd', 'stop')
sleep 3
system('pkill', '-9', '-x', 'slapd')

expected_datasize = get_slapcat_datasize() rescue nil

unless system('/usr/sbin/puavo-update-ldap-slave', '--within-full-init') then
  warn 'error: failed to update the configuration of the database'
  # Try to start slapd because we might have a working configuration
  # at this point despite a failed attempt.
  system('service', 'slapd', 'start')
  exit(1)
end

if !system('rm -rf /state/var/lib/ldap/*') then
  warn 'error: failed to remove the old LDAP database'
  exit(1)
end

if !system('chown -R openldap:openldap /state/var/lib/ldap') then
  warn 'error: failed to chown /state/var/lib/ldap to openldap'
  exit(1)
end

if !system('chmod -R 0750 /state/var/lib/ldap') then
  warn 'error: failed to chmod /var/lib/ldap'
  exit(1)
end

if !system('service slapd start') then
  warn 'error: failed to start slapd'
  exit(1)
end

FileUtils.touch(Sentinel_file)

puts 'Waiting for slapd to synchronize all data from the LDAP master.'
puts 'This may take a while.'

no_output_times = 0
previous_datasize = nil

sleep_amount = 1
until File.exist?('/var/lib/ldap/data.mdb') do
  sleep(sleep_amount)
  sleep_amount += 1
  if sleep_amount >= 6 then
    warn 'error: it appears slapd is doing nothing'
    exit 1
  end
end

while true do
  current_datasize = get_slapcat_datasize() rescue nil

  progress = nil

  if current_datasize then
    if expected_datasize then
      progress \
        = (100 * current_datasize / expected_datasize).round
      printf("  ... sync is in progress with about %3s%% done\n", progress)
    else
      printf("  ... sync has received %12s characters of data\n",
             current_datasize)
    end
  end

  if !current_datasize || previous_datasize == current_datasize then
    no_output_times += 1
  else
    no_output_times = 0
  end

  previous_datasize = current_datasize

  if (progress && progress >= 99.5) || no_output_times >= 4 then
    if !current_datasize then
      warn 'error: can not read ldap data, some errors in synchronization?'
      exit 1
    end
    break
  end

  sleep 5
end

sleep 2

puts()
puts('Synchronization is most probably complete now.')
puts()
