#!/bin/sh
#
### BEGIN INIT INFO
# Provides:          fliwi-per-service-ssh-server
# Should-Start:
# Required-Start:    $fliwi $syslog
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Provides a per service ssh-server
### END INIT INFO

set -e

test -x /usr/sbin/sshd || exit 0
( /usr/sbin/sshd -\? 2>&1 | grep -q OpenSSH ) 2>/dev/null || exit 0

umask 022

FLIWI_SVC_CONFIG_DIR="/etc/fliwi/services"
SSHD_ADDITIONAL_OPTS=""

if test -f /etc/default/fliwi-per-service-ssh; then
    . /etc/default/fliwi-per-service-ssh
fi

SSHD_OPTS="$SSHD_ADDITIONAL_OPTS"

ymcFullScriptName=$0

. /lib/lsb/init-functions

. /usr/lib/lib-fliwi/ymc-common.sh

NEWLINE='
'

if [ -n "$2" ]; then
    SSHD_OPTS="$SSHD_OPTS $2"
fi

export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"

if [ ! -d "/etc/fliwi-per-service-ssh" ]; then
  mkdir -p /etc/fliwi-per-service-ssh || exit 1
fi

# Are we running from init?
run_by_init()
{
    ([ "$previous" ] && [ "$runlevel" ]) || [ "$runlevel" = S ]
}

check_dev_null()
{
  if [ ! -c /dev/null ]; then
    if [ "$1" = log_end_msg ]; then
      log_end_msg 1 || true
    fi

    if ! run_by_init; then
      log_action_msg "/dev/null is not a character device!"
    fi
    return 1
  fi
  return 0
}

fliwi_per_service_ssh_calculate_services_from_dns()
{
  numbered_service_names=$(fliwi-get-my-services) || return 1
  for numbered_service_name in $numbered_service_names
  do
    echo $numbered_service_name | sed -r 's/-[0-9]+$//'
  done
  return 0
}

fliwi_per_service_ssh_calculate_services_from_generated_configs()
{
  for file in $(ls /etc/fliwi-per-service-ssh)
  do
    basename $file | sed -r 's/^sshd_config\.//'
  done
}

fliwi_per_service_ssh_calculate_services_from_pid_files()
{
  for file in $(ls /var/run/fliwi-per-service-ssh-*.pid 2>/dev/null)
  do
    basename $file | sed -r 's/^fliwi-per-service-ssh-(.*)\.pid$/\1/'
  done
}


#### MULTI //start
fliwi_per_service_ssh_generate_config()
{
  exit_code=0
  service_names=$(fliwi_per_service_ssh_calculate_services_from_dns)

  for previous_existing_service_name in $(fliwi_per_service_ssh_calculate_services_from_generated_configs)
  do
    if [ $(ymc_contains $previous_existing_service_name $service_names) -eq 0 ]; then
      rm -f /etc/fliwi-per-service-ssh/sshd_config.$previous_existing_service_name || return 1
    fi
  done

  for service_name in $service_names
  do
    fliwi_per_service_ssh_generate_config_single $service_name || exit_code=$?
  done
  return $exit_code
}

fliwi_per_service_ssh_check_config()
{
  exit_code=0
  service_names=$(fliwi_per_service_ssh_calculate_services_from_generated_configs)
  for service_name in $service_names
  do
    fliwi_per_service_ssh_check_config_single $service_name || exit_code=$?
  done
  return $exit_code
}

fliwi_per_service_ssh_start()
{
  exit_code=0
  service_names=$(fliwi_per_service_ssh_calculate_services_from_generated_configs)
  for service_name in $service_names
  do
    fliwi_per_service_ssh_start_single $service_name || exit_code=$?
  done
  return $exit_code
}

fliwi_per_service_ssh_stop()
{
  exit_code=0
  service_names=$(fliwi_per_service_ssh_calculate_services_from_pid_files)
  for service_name in $service_names
  do
    fliwi_per_service_ssh_stop_single $service_name || exit_code=$?
  done
  return $exit_code
}

fliwi_per_service_ssh_status()
{
  exit_code=1
  service_names=$(fliwi_per_service_ssh_calculate_services_from_generated_configs)
  if [ "$service_names" != "" ]; then
    exit_code=0
    for service_name in $service_names
    do
      if [ -r "/var/run/fliwi-per-service-ssh-$service_name.pid" ]; then
        status_of_proc -p /var/run/fliwi-per-service-ssh-$service_name.pid /usr/sbin/sshd "sshd for service $service_name" || exit_code=$?
      else
        exit_code=1
      fi
    done
  fi
  return $exit_code
}
#### MULTI //end


#### SINGLE //start
fliwi_per_service_ssh_generate_config_single()
{
  service_name="$1"
  service_base_config_dir="$FLIWI_SVC_CONFIG_DIR/$service_name"
  per_service_config_dir="$service_base_config_dir/per_service_ssh"
  per_service_config_file="$per_service_config_dir/sshd_config"
  rsa_host_key="$per_service_config_dir/ssh_host_rsa_key"
  rsa_host_key_pub="$per_service_config_dir/ssh_host_rsa_key.pub"
  dsa_host_key="$per_service_config_dir/ssh_host_dsa_key"
  dsa_host_key_pub="$per_service_config_dir/ssh_host_dsa_key.pub"

  system_sshd_config="/etc/ssh/sshd_config"
  generated_config_file="/etc/fliwi-per-service-ssh/sshd_config.$service_name"

  port_to_run_on=$(fliwi-get-ssh-port-for-service $service_name)
  if [ $? -ne 0 ] || \
     [ "$port_to_run_on" = "" ]; then
    if [ -e "$generated_config_file" ]; then
      rm -f $generated_config_file || return 1
    fi
    return 0
  fi

  if [ ! -d "$service_base_config_dir" ]; then
    return 0
  fi

  if [ ! -e "$system_sshd_config" ]; then
    return 1
  fi

  fliwi-config-update --service $service_name || return 1

  if [ ! -e "$rsa_host_key" ]; then
    log_action_msg "file '$rsa_host_key' not found"
    return 1
  else
    ### Always fix-up perms
    chmod 600 $rsa_host_key
  fi

  if [ ! -e "$dsa_host_key_pub" ]; then
    log_action_msg "file '$dsa_host_key' not found"
    return 1
  else
    ### Always fix-up perms
    chmod 600 $dsa_host_key
  fi

  echo "# Dynamic sshd_config(5) generated by $ymcFullScriptName" > $generated_config_file
  echo "#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN" >> $generated_config_file

  echo "Port $port_to_run_on" >> $generated_config_file
  echo "HostKey $rsa_host_key" >> $generated_config_file
  echo "HostKey $dsa_host_key" >> $generated_config_file

  config_keys_written="Port HostKey"

  ORG_IFS=$IFS
  IFS=$NEWLINE

  if [ -r "$per_service_config_file" ]; then
    for line in $(cat $per_service_config_file | grep -v -E -e '^[[:space:]]*#')
    do
      current_config_key="$(echo $line | sed -r 's/^[[:space:]]*//g' | cut -d ' ' -f 1)"
      if [ "$current_config_key" = "Match" ]; then
        break
      fi
      config_keys_written="$config_keys_written $current_config_key"
      echo $line >> $generated_config_file
    done
  fi

  for line in $(cat $system_sshd_config | grep -v -E -e '^[[:space:]]*#')
  do
    current_config_key="$(echo $line | sed -r 's/^[[:space:]]*//g' | cut -d ' ' -f 1)"
    if [ "$current_config_key" = "Match" ]; then
      break
    fi

    if [ $(ymc_contains $current_config_key $config_keys_written) -eq 0 ]; then
      echo $line >> $generated_config_file
    fi
  done

  if [ -r "$per_service_config_file" ]; then
    TEMP_MATCH_FOUND=0
    for line in $(cat $per_service_config_file | grep -v -E -e '^[[:space:]]*#')
    do
      current_config_key="$(echo $line | sed -r 's/^[[:space:]]*//g' | cut -d ' ' -f 1)"
      if [ $TEMP_MATCH_FOUND -eq 0 ]; then
        if [ "$current_config_key" != "Match" ]; then
          continue
        elif [ "$current_config_key" = "Match" ]; then
          TEMP_MATCH_FOUND=1
        fi
      fi
      config_keys_written="$config_keys_written $current_config_key"
      echo $line >> $generated_config_file
    done
  fi

  IFS=$ORG_IFS
  return 0
}

fliwi_per_service_ssh_check_config_single()
{
  service_name="$1"
  config_file_opts="-f /etc/fliwi-per-service-ssh/sshd_config.$service_name"
  /usr/sbin/sshd -o "PidFile /var/run/fliwi-per-service-ssh-$service_name.pid" $config_file_opts $SSHD_OPTS -t || return 1
  return 0
}

fliwi_per_service_ssh_start_single()
{
  service_name="$1"
  config_file_opts="-f /etc/fliwi-per-service-ssh/sshd_config.$service_name"

  # Create the PrivSep empty dir if necessary
  if [ ! -d /var/run/fliwi-per-service-ssh/$service_name ]; then
    mkdir -p /var/run/fliwi-per-service-ssh/$service_name
    chmod 0755 /var/run/fliwi-per-service-ssh/$service_name
  fi

  log_daemon_msg "Starting fliwi-per-service-ssh for '$service_name'" "fliwi-per-service-ssh"
  if start-stop-daemon --start --quiet --oknodo --pidfile /var/run/fliwi-per-service-ssh-$service_name.pid --exec /usr/sbin/sshd -- -o "PidFile /var/run/fliwi-per-service-ssh-$service_name.pid" $config_file_opts $SSHD_OPTS; then
    log_end_msg 0
    return 0
  else
    log_end_msg 1
    return 1
  fi
}

fliwi_per_service_ssh_stop_single()
{
  service_name="$1"
  log_daemon_msg "Stopping fliwi-per-service-ssh for '$service_name'" "fliwi-per-service-ssh"
  if start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/fliwi-per-service-ssh-$service_name.pid; then
    log_end_msg 0
    return 0
  else
    log_end_msg 1
    return 1
  fi
}
#### SINGLE //end


case "$1" in
  start)
    check_dev_null
    fliwi_per_service_ssh_generate_config || exit $?
    fliwi_per_service_ssh_check_config || exit $?
    fliwi_per_service_ssh_start
  ;;

  stop)
    fliwi_per_service_ssh_stop
  ;;

  restart)
    check_dev_null || exit $?
    fliwi_per_service_ssh_generate_config || exit $?
    fliwi_per_service_ssh_check_config || exit $?
    fliwi_per_service_ssh_stop
    fliwi_per_service_ssh_start
  ;;

  status)
    fliwi_per_service_ssh_status || exit $?
  ;;

  *)
    log_action_msg "Usage: /etc/init.d/fliwi-per-service-ssh {start|stop|restart|status}"
    exit 1
  ;;
esac

exit 0
