#!/bin/bash

#< Installs the newly transferred slave named.conf file after consistency checks

# This script is part of my DNS named.conf transfer scripts. The scripts
# are designed to be used for the transferral of a frequently changing 
# named.conf file from a primary nameserver to a secondary nameserver.

# The scripts assume a lot. The zone files used in my test environment
# were very "bare-bones" and simple. Example:

# Any additional configuration options present in the file would
# require script modifications (sync_to_slave.sh) to accomodate parsing
# the other configuration options. This script is designed to be easily
# modified, and should be considered a template for named.conf transfers.

# The scripts revolve around a central lock file mechanism, which aims to
# ensure that no incomplete named.conf files are transferred whilst they are
# being edited - although it will not parse a file for syntactical correctness.
# Also, this assumes that all edits to the named.conf file are made using the
# vinamed wrapper, or a custom tool that uses the lockfile mechanism used in
# these scripts.

# The scripts provided are:
#   vinamed                     Lock file protected named.conf editing wrapper
#   sync_to_slave.sh            Intended to be scheduled via cron on the 
#                               primary nameserver, BEFORE the script below 
#                               runs on the slave nameserver.
#   install_slave_config.sh     Schedule this to run a few minutes after
#                               the sync_to_slave.sh script runs on the 
#                               primary nameserver. This script contains
#                               various checks (lockfile, confirms whether
#                               the newly transferred named.conf file is
#                               modified, etc.) This script also creates 
#                               backups of named.conf prior to, and after
#                               modification. Then it restarts BIND.

# Example cron jobs (depending on the frequency of your named.conf file
# edits). 

# master# crontab -l | grep sync_to_slave 
# 0,30 * * * * /usr/local/dns/bin/sync_to_slave.sh
# slave# crontab -l | grep install_slave_config
# 15,45 * * * * /usr/local/dns/bin/install_slave_config.sh

# You will also need to check the paths presented in the scripts, and ensure
# that the appropriate backup directories are created, or the paths
# referenced in the scripts are changed.

# For the key-based file transfer to work, I'm using a "namesync" user on each
# server.

# Executable paths
CAT="/bin/cat"
CHOWN="/bin/chown"
CHMOD="/bin/chmod"
CP="/bin/cp"
DATE="/bin/date"
DIFF="/usr/bin/diff"
ECHO="/bin/echo"
ID="/usr/bin/id"
LS="/bin/ls"
MV="/bin/mv"
RM="/bin/rm"
TAIL="/usr/bin/tail"

OUTPUT_SUFFIX="$( ${DATE} +%Y%m%d ).$$"

# Files
SYSCONFDIR="/etc"
NAMED_INIT="${SYSCONFDIR}/init.d/named"
NAMED_CONF="${SYSCONFDIR}/named.conf"
NAMED_BACKUP_DIR="${SYSCONFDIR}/named.bak"
NAMED_BAK="${NAMED_BACKUP_DIR}/${NAMED_CONF##*/}.${OUTPUT_SUFFIX}"
LOCKFILE="/var/lock/vinamed.lock"
NAMESYNC_HOME="/home/namesync"
INCOMING_DIR="${NAMESYNC_HOME}/named.xfer"
UPDATE_FILE="${NAMESYNC_HOME}/named.conf.latest"
BACKUP_DIR="${NAMESYNC_HOME}/named.bak"

# Errors
SUCCESS="0"
ENOTROOT="1"
ELOCKFILE="1"

# ToDo: Check that the appropriate backup directories exist

# Function:     print_error
# Arguments:    $@
# Returns:      Nothing
# Description:  echo wrapper to output to STDERR

function print_error {
   ${ECHO} "Error: $@" >&2 
}

# Function:     print_msg
# Arguments:    $@
# Returns:      Nothing
# Description:  echo wrapper to output to STDOUT with prefix

function print_msg {
   ${ECHO} "--> $@"
}

# Function:     check_root_user
# Arguments:    None
# Returns:      Nothing - will exit on failure
# Description:  Checks whether the script is being executed as root user

function check_root_user {
   WHOAMI=$( ${ID} -u )
   if [ "${WHOAMI}" -ne "0" ]; then
      print_error "You must be root to run this program"
      exit ${ENOTROOT}
   fi
}

# Function:     check_lockfile
# Arguments:    None
# Returns:      Nothing - exits on failure
# Description:  Exits if the lockfile exists

function check_lockfile {
   if [ -e "${LOCKFILE}" ]; then
       print_msg "Lockfile exists - is ${NAMED_CONF} being edited?"
       exit ${ELOCKFILE}
   fi
}

# Function:	create_backup
# Arguments:	None
# Returns:	Nothing
# Description:	Creates backup of current named.conf file
# ToDo:		Check if backup directory exists

function create_backup {
  ${CP} -p ${NAMED_CONF} ${NAMED_BAK}
  print_msg "Backup named.conf at ${NAMED_BAK}"
}

# Function:	diff_conf_file
# Arguments:	None
# Returns:	Nothing
# Description:	Performs a diff of the newly transferred file against the
#		currently installed file, and exits if the file hasn't been
#		changed. Nethertheless, a backup of the incoming file is
#		created.
# ToDo:		Optionally backup the file - allow a command-line argument
#		to optionally disable the backup functionality

function diff_conf_file {
  # Here we are just checking whether or not the incoming
  # file is different from the current installed file
  ${DIFF} ${NAMED_CONF} ${INCOMING_DIR}/${LATEST} >/dev/null 2>&1
  if [ "$?" -eq "0" ]; then
     print_msg "No differences in new config file"
     # Yes, this is also good, but still backup incoming file
     # discarding /etc/named.conf backup as the file hasn't
     # been modified
     print_msg "Removing ${NAMED_BAK}"
     ${RM} ${NAMED_BAK}
     ${MV} ${INCOMING_DIR}/${LATEST} ${BACKUP_DIR}
     exit ${SUCCESS}
  fi
}

# Function:	install_config
# Arguments:	None
# Returns:	Nothing
# Description:	Installs the new named.conf file, creating a backup of
#		the current "good" configuration.

function install_config {
  print_msg "Last modified file version will be installed"
  LATEST="$( ${LS} -1rt ${INCOMING_DIR} | ${TAIL} -n 1 )"
  if [ ! -f "${INCOMING_DIR}/${LATEST}" ]; then
     print_msg "No new config file to install"
     # Again, this is a good exit
     exit "${SUCCESS}"
  fi
  print_msg "  ${LATEST}"
  diff_conf_file
  ${CP} ${INCOMING_DIR}/${LATEST} ${BACKUP_DIR}
  ${MV} ${INCOMING_DIR}/${LATEST} ${SYSCONFDIR}
  ${MV} ${SYSCONFDIR}/${LATEST} ${NAMED_CONF}
  ${CHOWN} -R namesync:users ${BACKUP_DIR}
  ${CHOWN} root:root ${NAMED_CONF}
  ${CHMOD} 644 ${NAMED_CONF}
  print_msg "New /etc/named.conf installed"
  ${CAT} ${NAMED_CONF}
}

# Function:	named_restart
# Arguments:	None
# Returns:	Nothing
# Description:	Restarts BIND to ensure the changes take effect
# ToDo:		Check for errors when restarting BIND that would indicate
#		possible configuration file corruption

function named_restart {
  print_msg "Issuing ${NAMED_INIT} restart"
  ${NAMED_INIT} restart
}

check_root_user
check_lockfile
create_backup
install_config
named_restart

exit ${SUCCESS}