#!/bin/bash
# copies cert files to the right locations and restarts services

SPATH="$(dirname "$(readlink -f ${BASH_SOURCE[0]})")"    # dir of the script
SNAME="$(basename ${BASH_SOURCE[0]})"                    # name of the script

ASCEDSCONF='local site hostconf'
ASCEDSLIBS='utils hosts postfix openssl'

if [ ! -r "${SPATH}/asceds-head" ] ; then
   echo "No header ${SPATH}/asceds-head. Exiting"
   exit 9
fi

. ${SPATH}/asceds-head

asceds-service-reconfig-usage()
{
  cat << ENDUSAGE

Usage: asceds-service-reconfig [-q] [-t] [-h] [-s <service>]

Updates the site config file if needed.
Copies cert filess to the right locations and restarts services using them.

-q --> no question asked; sends output to the logs in ${ASCEDSLOGDIR}/
-t --> reconfigure services only if trigger file .asceds-reconf exists
-s service --> reconfigures the <service> only (one service);
               in cron mode, -s options are ignored
-h --> display usage and exit

Updates the site config file using the trigger ${ASCEDSHOMEDIR}/.site-reconf;
Checks if the available certificate is not stale 
   (expired or expiring in the next 5 days);
   if the certificate is stale, refreshes the site configuration file 
   to allow pushing new certificate files which are blocked when 
   the asceds ssh keys change on the certificate manager;
   if it is almost expired, send message to ALARM;
Reads parameters (CLIENT_DOMAIN_NAME, CLIENT_SANS_LIST) <-- ~asceds/cert.conf;
Copies cert/key from ~asceds/certs/ --> /etc/ssl ; fix permissions;
Reconfigures/restarts services through ${ASCEDSSERVCONF}/*.sh ;
     to avoid sourcing: rename so it doesn't match *.sh;
     available service templates: ${ASCEDSSERVDIR}/*.sh.proto;
Removes the reconfig trigger file ${ASCEDSHOMEDIR}/.asceds-reconf

ENDUSAGE
exit 0
}

# output to terminal by default
QUIETRUN=""

# initialize service to be restarted
RESTARTSERV=""

# by default use interactive mode
CRONMODE=''

# general validation and parse of the command line
for ARG in ${ARGS} ; do
   #echo "ARG=${ARG}"
   if [ "${ARG}" != "${ARG//[^-_a-zA-Z0-9.]/}" ] ; then
      asceds-error "Malformed argument ${ARG}"
   elif [ "${ARG}" == "-h" ] ; then
      asceds-service-reconfig-usage
   elif [ "${ARG}" == "-t" ] ; then
      shift
      CRONMODE='yes'
   elif [ "${ARG}" == "-q" ] ; then
      asceds-exec-noninteractive "$*"
      # bring other scripts in non-interactive mode
      QUIETRUN="y"
      shift
   elif [ "${ARG}" == "-s" ] ; then
      shift
      RESTARTSERV="$1"
      if [ -z "${RESTARTSERV}" ] || [[ "${RESTARTSERV}" = "-"* ]] ; then 
         asceds-echo "Service name not provided."
         asceds-service-reconfig-usage
      fi
   else
      shift
   fi
done

# run as root only
asceds-run-asroot

# put filesystem in rw mode
if [ -x /usr/bin/rw ] ; then 
   sudo /usr/bin/rw
fi

# create a new site config file if the trigger ~asceds/.site-reconf is set
if [ -f "${ASCEDSHOMEDIR}/.site-reconf" ] ; then
   if [ -r "${ASCEDSHOMEDIR}/asceds-site.conf" ] ; then
      asceds-parse-siteconf "${ASCEDSHOMEDIR}/asceds-site.conf"
      asceds-source-etcconf "${ASCEDSETCDIR}" "${ASCEDSCONF}"
      if [ ! -r "${ASCEDSHOMEDIR}/.ssh/authorized_keys" ] ; then
         touch ${ASCEDSHOMEDIR}/.ssh/authorized_keys
         chown asceds:asceds ${ASCEDSHOMEDIR}/.ssh/authorized_keys
         chmod 600 ${ASCEDSHOMEDIR}/.ssh/authorized_keys
      fi
      cat <<< "${ASCEDSSSHPUBKEY}" > ${ASCEDSHOMEDIR}/.ssh/authorized_keys
      ${RM} -f ${ASCEDSHOMEDIR}/.site-reconf
   else
      asceds-error "No ${ASCEDSHOMEDIR}/asceds-site.conf to use for update."
   fi
fi

# check if certificate exists and unexpired
if [ -d "${ASCEDSHOMEDIR}/certs" ] ; then
   if [ -r "${ASCEDSHOMEDIR}/certs/cert.pem" ] ; then
      # if certificates are expired, refresh the site config file and exit
      asceds-expiration-test "${ASCEDSHOMEDIR}/certs/cert.pem"
      EXPTEST=$?
      if [ "${EXPTEST}" == "1" ] ; then
         asceds-update-siteconfig -q -c
         exit 1
      elif [ "${EXPTEST}" == "2" ] ; then
         asceds-error "Certificate in ${CERTFILE} on $( hostname -f ) is (almost) expired"
      fi
   else
      # no certificate files
      asceds-error "No certificate ${ASCEDSHOMEDIR}/certs/cert.pem"
   fi 
else
   # no dir for certs
   asceds-error "No dir ${ASCEDSHOMEDIR}/certs for certificates"
fi  
     
# if in cron mode, exit if no trigger file
if [ -n "${CRONMODE}" ] ; then
   if [ -n "${RESTARTSERV}" ] ; then
      asceds-warning "In cron mode, -s options are ignored"
      RESTARTSERV=''
   fi
   if [ ! -f "${ASCEDSHOMEDIR}/.asceds-reconf" ] ; then      
      # no trigger file, nothing to be done
      exit 0
   fi
fi

# read parameters (CLIENT_DOMAIN_NAME, CLIENT_SANS_LIST) <-- ~asceds/cert.conf
asceds-parse-certconf "${ASCEDSHOMEDIR}/cert.conf"

# check/validate CLIENT_DOMAIN_NAME
DOMAIN_NAME0=$( hostname -f | tr '[:upper:]' '[:lower:]' )
if [ -z "${CLIENT_DOMAIN_NAME}" ] ; then
   asceds-warning "Empty domain name. Replacing with host name"
   CLIENT_DOMAIN_NAME="${DOMAIN_NAME0}"
elif [ "${CLIENT_DOMAIN_NAME}" != "${DOMAIN_NAME0}" ] ; then
   asceds-warning "CLIENT_DOMAIN_NAME configured in ${ASCEDSHOMEDIR}/cert.conf
      doesn't match FQDN ${DOMAIN_NAME0} of the computer"
fi

PRVKEY="${ASCEDS_SSLDIR}/private/${CLIENT_DOMAIN_NAME}.key"
CERTCHAIN="${ASCEDS_SSLDIR}/certs/${CLIENT_DOMAIN_NAME}.pem"

# check cert files in ~asceds/certs/
if [ -r "${ASCEDSHOMEDIR}/certs/privkey.pem" ] ; then
   PRVKEYNEW="yes"
   if [ -r "${PRVKEY}" ] ; then
      PRVKEYNEW="$( diff ${PRVKEY} ${ASCEDSHOMEDIR}/certs/privkey.pem )"
   fi
   #PRVKEYNEW=""
   if [ -n "${PRVKEYNEW}" ] ; then
      # copy key to the right location, set ownership/permission
      ${CP} ${ASCEDSHOMEDIR}/certs/privkey.pem ${PRVKEY}
      if [ -n "$( ${GREP} '^ssl-cert:' /etc/group )" ] ; then
         chgrp ssl-cert ${PRVKEY}
         chmod 640 ${PRVKEY}
      else
         chmod 600 ${PRVKEY}
      fi
   else
      asceds-echo "Key already at the latest version"
   fi
else
   asceds-error "No key file found. Exiting"
fi

if [ -r "${ASCEDSHOMEDIR}/certs/fullchain.pem" ] ; then
   CERTCHAINNEW="yes"
   if [ -r "${CERTCHAIN}" ] ; then
      CERTCHAINNEW="$( diff ${CERTCHAIN} ${ASCEDSHOMEDIR}/certs/fullchain.pem )"
   fi
   #CERTCHAINNEW=""
   if [ -n "${CERTCHAINNEW}" ] ; then
      # copy cert files to the right location, set ownership/permission
      ${CP} ${ASCEDSHOMEDIR}/certs/fullchain.pem ${CERTCHAIN}
      chmod 644 ${CERTCHAIN}
   else
      asceds-echo "Certificate already at the latest version"
   fi
else
   asceds-error "No fullchain certificate file found. Exiting"
fi

# SANS tests
if [ -n "${OPENSSLEXT}" ] ; then
   if [ -r "${ASCEDSHOMEDIR}/certs/cert.pem" ] && \
      [ -n "${CLIENT_SANS_LIST}" ] ; then

      asceds-echo "Checking SANS ..."
      HST_LIST=$( eval ${OPENSSLEXT} < \
                 ${ASCEDSHOMEDIR}/certs/cert.pem | \
                 ${GREP} -v "Subject Alternative Name" | sed -e "s/,//g" | \
                 sed -e "s/DNS://g" | xargs )
      CERT_HST_COUNT=$( echo ${HST_LIST} | wc -w )
      SANS_LIST_COUNT=$( echo ${CLIENT_SANS_LIST}  | sed -e "s/,/ /g" | wc -w )
      SANS_LIST_COUNT=$(( ${SANS_LIST_COUNT} + 1 ))
      # certificate and ~asceds/cert.conf must have the same number of hostnames
      if [ "${CERT_HST_COUNT}" != "${SANS_LIST_COUNT}" ] ; then
         asceds-warning "Number of SANS in certificate doesn't match ~asceds/cert.conf"
      fi
   
      # find the host IP address
      asceds-ip-resolve "${CLIENT_DOMAIN_NAME}"
      HSTIPADDR="${ASCEDS_IP}"

      for HST in ${HST_LIST} ; do
         # each hostname should be in /etc/hosts
         asceds-hosts-check "${HST}"  || asceds-hosts-add "${HST}" "${HSTIPADDR}"
      
         # each hostname should be in /etc/postfix/main.cf
         asceds-postfix-check "${HST}"  || asceds-postfix-add "${HST}"
     
         # each hostname should be in openssl.cnf
         asceds-openssl-check "${HST}" || \
            asceds-warning "${HST} is not in ${ASCEDS_OPENSSL} but it should be"
         # || asceds-openssl-add "${HST}"
      
         # each hostname in certificate must be in the list from ~asceds/cert.conf
         if [ -z "$( echo ${CLIENT_SANS_LIST} |  ${GREP} ${HST} )" ] && \
            [ "${HST}" != "${CLIENT_DOMAIN_NAME}" ] ; then
            asceds-wrning "SANS ${HST} from cert is not in ~asceds/cert.conf" 
         fi
      done
   fi
fi

# reconfigure/restart services through ${ASCEDSSERVCONF}/*.sh
# if local service dir exists, and it contains active scripts for services
if [ -d "${ASCEDSSERVCONF}" ] && \
     [ "$( echo ${ASCEDSSERVCONF}/*.sh )" != "${ASCEDSSERVCONF}/*.sh" ]  ; then

   if [ -z "${RESTARTSERV}" ] ; then
      # if no service name was passed through -s in the command line or cron mode,
      # restart all services
      for SERVSCRIPT in ${ASCEDSSERVCONF}/*.sh ; do
         if [ -r "${SERVSCRIPT}" ]; then
            . ${SERVSCRIPT}
            asceds-echo "Restarting ${SERVSCRIPT}"
         else
            asceds-echo "No readable script for ${SERVSCRIPT}
               Please add .sh files for services to be reconfigured"
         fi
      done
      if [ -f "${ASCEDSHOMEDIR}/.asceds-reconf" ] ; then
         asceds-echo "Removing the reconf trigger ${ASCEDSHOMEDIR}/.asceds-reconf"
         ${RM} -f ${ASCEDSHOMEDIR}/.asceds-reconf
      fi
   else
      # check if there is a script to restart this service
      if [ -r "${ASCEDSSERVCONF}/${RESTARTSERV}.sh" ] ; then
         . ${ASCEDSSERVCONF}/${RESTARTSERV}.sh
      else
         asceds-echo "No readable script in ${ASCEDSSERVCONF} for ${RESTARTSERV};
               Please add ${RESTARTSERV}.sh for reconfiguring this service."
      fi         
   fi
elif [ ! -d "${ASCEDSSERVCONF}" ] ; then
   asceds-error "No dir for services: ${ASCEDSSERVCONF}"
else
   asceds-warning "No services configured in ${ASCEDSSERVCONF}"
fi

# switch filesystem to rw state for pikvm systems
if [ -x /usr/bin/ro ] ; then 
   sudo /usr/bin/ro 
fi
