#!/bin/bash
#< Wrapper for "vi /etc/named.conf" with lock file editing protection

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

# Executables
CHMOD="/bin/chmod"
ECHO="/bin/echo"
ID="/usr/bin/id"
RM="/bin/rm"
TOUCH="/usr/bin/touch"
VI="/usr/bin/vi"

# Paths and Files
NAMED_CONF="/etc/named.conf"
LOCKFILE="/var/lock/vinamed.lock"

# Error Codes
SUCCESS=0
ENOCONF=1
ELOCKFILE=1

# 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_named_conf
# Arguments:	None
# Returns:	Nothing - will exit on failure
# Description:	Exits if the named.conf file cannot be found

function check_named_conf {
   if [ ! -e "${NAMED_CONF}" ]; then
      print_error "${NAMED_CONF} does not exist"
      exit ${ENOCONF}
   fi
}

# Function: 	remove_lockfile
# Arguments:	None
# Returns:	Nothing
# Description:	Removes the lockfile

function remove_lockfile {
   print_msg "Removing lockfile..."
   ${RM} -f ${LOCKFILE} 
   print_msg "Lockfile removed"
}

# Function: 	check_lockfile
# Arguments:	None - accepts user input
# Returns:	Nothing - exits on failure
# Description:	Prompts the user, if the lock file exists, whether or not
#		they would like to continue with the edit

function check_lockfile {
   if [ -e "${LOCKFILE}" ]; then
      print_msg "${LOCKFILE} exists! Do you want to remove it?"
      ${ECHO} -e "<-- Type YeS (Case Sensitive) to confirm: \c"
      read RESPONSE
      case ${RESPONSE} in
         YeS)    remove_lockfile
                 ;;
         *)      print_error "Lock file NOT removed"
                 exit ${ELOCKFILE}
                 ;;
      esac
   fi
}

# Function: 	create_lockfile
# Arguments:	None
# Returns:	Nothing
# Description:	Creates the lockfile and changes its mode to 400

function create_lockfile {
   print_msg "Creating lockfile..."
   ${TOUCH} ${LOCKFILE}
   ${CHMOD} 400 ${LOCKFILE}
   print_msg "Lockfile created"
}

# Function: 	edit_named_conf
# Arguments:	None
# Returns:	Nothing
# Description:	Starts vi to edit the named.conf file

function edit_named_conf {
   print_msg "Starting ${VI} to edit ${NAMED_CONF}..."
   ${VI} ${NAMED_CONF}
}

#
# main
#
#

check_root_user
check_named_conf
check_lockfile
create_lockfile
edit_named_conf
remove_lockfile

exit ${SUCCESS}