#!/bin/sh # EvoMaintenance script # Dependencies (all OS): git postgresql-client # Dependencies (Debian): sudo # version 0.5.0 # Copyright 2007-2019 Evolix , Gregory Colpart , # Jérémy Lecour and others. VERSION="0.5.0" show_version() { cat <, Gregory Colpart , Jérémy Lecour and others. evomaintenance comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. See the GNU General Public Licence for details. END } show_help() { cat <> "${LOGFILE}" fi fi fi } hook_db() { SQL_DETAILS=$(echo "${MESSAGE}" | sed "s/'/''/g") PG_QUERY="INSERT INTO evomaint(hostname,userid,ipaddress,begin_date,end_date,details) VALUES ('${HOSTNAME}','${USER}','${IP}','${BEGIN_DATE}',now(),'${SQL_DETAILS}')" if [ "${VERBOSE}" = "1" ]; then printf "\n********** DB query **************\n%s\n***********************************\n" "${PG_QUERY}" fi if [ "${DRY_RUN}" != "1" ] && [ -x "${PSQL_BIN}" ]; then echo "${PG_QUERY}" | ${PSQL_BIN} "${PGDB}" "${PGTABLE}" -h "${PGHOST}" fi } hook_api() { if [ "${VERBOSE}" = "1" ]; then printf "\n********** API call **************\n" printf "curl -s -X POST %s -F action=insertEvoMaintenance -F hostname=%s -F userid=%s -F ipaddress=%s -F begin_date=%s -F end_date='now()' -F details=%s" \ "${API_ENDPOINT}" "${HOSTNAME}" "${USER}" "${IP}" "${BEGIN_DATE}" "${MESSAGE}" printf "\n***********************************\n" fi if [ "${DRY_RUN}" != "1" ] && [ -x "${CURL_BIN}" ]; then curl -s -X POST \ "${API_ENDPOINT}" -k \ -F api_key="${API_KEY}" \ -F action=insertEvoMaintenance \ -F hostname="${HOSTNAME}" \ -F userid="${USER}" \ -F ipaddress="${IP}" \ -F begin_date="${BEGIN_DATE}" \ -F end_date='now()' \ -F details="${MESSAGE}" > /dev/null fi } format_mail() { cat <> "${LOGFILE}" fi } # load configuration if present. test -f /etc/evomaintenance.cf && . /etc/evomaintenance.cf HOSTNAME=${HOSTNAME:-$(get_fqdn)} EVOMAINTMAIL=${EVOMAINTMAIL:-"evomaintenance-$(echo "${HOSTNAME}" | cut -d- -f1)@${REALM}"} LOGFILE=${LOGFILE:-"/var/log/evomaintenance.log"} HOOK_COMMIT=${HOOK_COMMIT:-"1"} HOOK_DB=${HOOK_DB:-"1"} HOOK_API=${HOOK_API:-"1"} HOOK_MAIL=${HOOK_MAIL:-"1"} DRY_RUN=${DRY_RUN:-"0"} VERBOSE=${VERBOSE:-"0"} AUTO=${AUTO:-"0"} EVOCHECK=${EVOCHECK:-"0"} GIT_STATUS_MAX_LINES=${GIT_STATUS_MAX_LINES:-20} API_ENDPOINT=${API_ENDPOINT:-""} # initialize variables MESSAGE="" # GIT_COMMITS_SHORT="" GIT_COMMITS="" # Parse options # based on https://gist.github.com/deshion/10d3cb5f88a21671e17a while :; do case $1 in -h|-\?|--help) show_help exit 0 ;; --version) show_version exit 0 ;; -m|--message) # message options, with value speparated by space if [ -n "$2" ]; then MESSAGE=$2 shift else printf 'ERROR: "--message" requires a non-empty option argument.\n' >&2 exit 1 fi ;; --message=?*) # message options, with value speparated by = MESSAGE=${1#*=} ;; --message=) # message options, without value printf 'ERROR: "--message" requires a non-empty option argument.\n' >&2 exit 1 ;; --no-commit) # disable commit hook HOOK_COMMIT=0 ;; --commit) # enable commit hook HOOK_COMMIT=1 ;; --no-db) # disable DB hook HOOK_DB=0 ;; --db) # enable DB hook HOOK_DB=1 ;; --no-api) # disable API hook HOOK_API=0 ;; --api) # enable API hook HOOK_API=1 ;; --no-mail) # disable mail hook HOOK_MAIL=0 ;; --mail) # enable mail hook HOOK_MAIL=1 ;; --no-auto) # use "manual" mode AUTO=0 ;; --auto) # use "auto" mode AUTO=1 ;; -n|--dry-run) # disable actual commands DRY_RUN=1 ;; -v|--verbose) # print verbose information VERBOSE=1 ;; --) # End of all options. shift break ;; -?*|[[:alnum:]]*) # ignore unknown options printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2 ;; *) # Default case: If no more options then break out of the loop. break ;; esac shift done # Treat unset variables as an error when substituting. # Only after this line, because some config variables might be missing. set -u # Gather information HOSTNAME_TEXT=$(get_complete_hostname) # TTY=$(get_tty) # WHO=$(get_who) IP=$(get_ip) BEGIN_DATE=$(get_begin_date) END_DATE=$(get_end_date) USER=$(logname) PATH=${PATH}:/usr/sbin SENDMAIL_BIN=$(command -v sendmail) readonly SENDMAIL_BIN if [ -z "${SENDMAIL_BIN}" ]; then echo "No \`sendmail' command has been found, can't send mail." 2>&1 fi GIT_BIN=$(command -v git) readonly GIT_BIN if [ -z "${GIT_BIN}" ]; then echo "No \`git' command has been found, can't commit changes" 2>&1 fi PSQL_BIN=$(command -v psql) readonly PSQL_BIN if [ -z "${PSQL_BIN}" ]; then echo "No \`psql' command has been found, can't save to the database." 2>&1 fi CURL_BIN=$(command -v curl) readonly CURL_BIN if [ -z "${CURL_BIN}" ]; then echo "No \`curl' command has been found, can't call the API." 2>&1 fi if [ -z "${API_ENDPOINT}" ]; then echo "No API endpoint specified, can't call the API." 2>&1 fi EVOCHECK_BIN="/usr/share/scripts/evocheck.sh" GIT_REPOSITORIES="/etc /etc/bind" # initialize variable GIT_STATUSES="" # git statuses if [ -x "${GIT_BIN}" ]; then # loop on possible directories managed by GIT for dir in ${GIT_REPOSITORIES}; do RESULT=$(get_repository_status "${dir}") if [ -n "${RESULT}" ]; then # append diff data, without empty lines GIT_STATUSES=$(printf "%s\n%s\n" "${GIT_STATUSES}" "${RESULT}" | sed -e '/^$/d') fi unset RESULT done fi # find out if running in interactive mode, or not if [ -t 0 ]; then INTERACTIVE=1 else INTERACTIVE=0 fi readonly INTERACTIVE if [ "${INTERACTIVE}" = "1" ] && [ "${EVOCHECK}" = "1" ]; then get_evocheck fi if [ -n "${GIT_STATUSES}" ] && [ "${INTERACTIVE}" = "1" ]; then printf "/!\ There are some uncommited changes.\n%s\n\n" "${GIT_STATUSES}" fi if [ -z "${MESSAGE}" ]; then if [ "${INTERACTIVE}" = "1" ]; then printf "> Please, enter details about your maintenance:\n" fi read -r MESSAGE fi if [ -z "${MESSAGE}" ]; then echo "no value..." exit 1 fi print_session_data if [ "${INTERACTIVE}" = "1" ] && [ "${AUTO}" = "0" ]; then if [ "${HOOK_COMMIT}" = "1" ] || [ "${HOOK_MAIL}" = "1" ] || [ "${HOOK_DB}" = "1" ]; then printf "\nActions to execute:\n" if [ "${HOOK_COMMIT}" = "1" ]; then printf "* commit changes in repositories\n" fi if [ "${HOOK_MAIL}" = "1" ]; then printf "* send mail to %s\n" "${EVOMAINTMAIL}" fi if [ "${HOOK_DB}" = "1" ]; then printf "* save metadata to the database\n" fi if [ "${HOOK_API}" = "1" ]; then printf "* send metadata to the API\n" fi echo "" answer="" while :; do printf "> Let's continue? [Y,n,i,?] " read -r answer case $answer in [Yy]|"" ) # force "auto" mode, but keep hooks settings AUTO=1 break ;; [Nn] ) # force "auto" mode, and disable all hooks HOOK_COMMIT=0 HOOK_MAIL=0 HOOK_DB=0 HOOK_API=0 AUTO=1 break ;; [Ii] ) # force "manual" mode AUTO=0 break ;; * ) printf "y - yes, execute actions and exit\n" printf "n - no, don't execute actions and exit\n" printf "i - switch to interactive mode\n" printf "? - print this help\n" ;; esac done fi fi if [ "${INTERACTIVE}" = "1" ] && [ "${AUTO}" = "0" ]; then # Commit hook if [ -n "${GIT_STATUSES}" ] && [ "${HOOK_COMMIT}" = "1" ]; then printf "/!\ There are some uncommited changes.\n%s\n\n" "${GIT_STATUSES}" y="Y"; n="n" answer="" while :; do printf "> Do you want to commit the changes? [%s] " "${y},${n}" read -r answer case $answer in [Yy] ) hook_commit; break ;; [Nn] ) break ;; "" ) if [ "${HOOK_COMMIT}" = "1" ]; then hook_commit fi break ;; * ) echo "answer with a valid choice" ;; esac done fi # Mail hook if [ "${HOOK_MAIL}" = "1" ]; then y="Y"; n="n" else y="y"; n="N" fi answer="" while :; do printf "> Do you want to send an email to <%s>? [%s] " "${EVOMAINTMAIL}" "${y},${n},e" read -r answer case $answer in [Yy] ) hook_mail; break ;; [Nn] ) break ;; [Ee] ) printf "> To: [%s] " "${EVOMAINTMAIL}" read -r mail_recipient if [ -n "${mail_recipient}" ]; then EVOMAINTMAIL="${mail_recipient}" fi ;; "" ) if [ "${HOOK_MAIL}" = "1" ]; then hook_mail fi break ;; * ) echo "answer with a valid choice" ;; esac done # Database hook if [ "${HOOK_DB}" = "1" ]; then y="Y"; n="n" else y="y"; n="N" fi answer="" while :; do printf "> Do you want to insert your message into the database? [%s] " "${y},${n}" read -r answer case $answer in [Yy] ) hook_db; break ;; [Nn] ) break ;; "" ) if [ "${HOOK_DB}" = "1" ]; then hook_db fi break ;; * ) echo "answer with a valid choice" ;; esac done # API hook if [ "${HOOK_API}" = "1" ]; then y="Y"; n="n" else y="y"; n="N" fi answer="" while :; do printf "> Do you want to send the metadata to the API? [%s] " "${y},${n}" read -r answer case $answer in [Yy] ) hook_api; break ;; [Nn] ) break ;; "" ) if [ "${HOOK_API}" = "1" ]; then hook_api fi break ;; * ) echo "answer with a valid choice" ;; esac done fi # Log hook hook_log if [ "${INTERACTIVE}" = "0" ] || [ "${AUTO}" = "1" ]; then if [ "${HOOK_COMMIT}" = "1" ]; then hook_commit fi if [ "${HOOK_MAIL}" = "1" ]; then hook_mail fi if [ "${HOOK_DB}" = "1" ]; then hook_db fi if [ "${HOOK_API}" = "1" ]; then hook_api fi fi exit 0