#!/bin/sh # EvoMaintenance script # Dependencies (all OS): git postgresql-client # Dependencies (Debian): sudo # version 0.5.0.beta1 # Copyright 2007-2019 Evolix , Gregory Colpart , # Jérémy Lecour and others. VERSION="0.5.0.beta1" show_version() { printf "%s\n" "evomaintenance version ${VERSION}" } show_help() { 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. evomaintenance is a program that helps reporting what you've done on a server Usage: evomaintenance or evomaintenance --message="add new host" or evomaintenance --no-db --no-mail --no-commit or echo "add new vhost | evomaintenance Options -m, --message=MESSAGE set the message from the command line --mail enable the mail hook --no-mail disable the mail hook --db enable the database hook --no-db disable the database hook --commit enable the commit hook --no-commit disable the commit hook -v, --verbose increase verbosity -n, --dry-run actions are not executed --help print this message and exit --version print version and exit END } get_system() { uname -s } get_fqdn() { if [ "$(get_system)" = "Linux" ]; then hostname --fqdn elif [ "$(get_system)" = "OpenBSD" ]; then hostname else echo "OS not detected!" exit 1 fi } get_tty() { if [ "$(get_system)" = "Linux" ]; then ps -o tty= | tail -1 elif [ "$(get_system)" = "OpenBSD" ]; then env | grep SSH_TTY | cut -d"/" -f3 else echo "OS not detected!" exit 1 fi } get_who() { who=$(LC_ALL=C who -m) if [ -n "${who}" ]; then echo "${who}" else LC_ALL=C who | grep $(get_tty) | tr -s ' ' fi } get_begin_date() { echo "$(date "+%Y") $(echo $(get_who) | cut -d" " -f3,4,5)" } get_ip() { ip=$(echo $(get_who) | cut -d" " -f6 | sed -e "s/^(// ; s/)$//") [ -z "${ip}" ] && ip="unknown (no tty)" [ "${ip}" = ":0" ] && ip="localhost" echo "${ip}" } get_end_date() { date +"%Y %b %d %H:%M" } get_now() { date +"%Y-%m-%dT%H:%M:%S%z" } get_complete_hostname() { REAL_HOSTNAME=$(get_fqdn) if [ "${HOSTNAME}" = "${REAL_HOSTNAME}" ]; then echo "${HOSTNAME}" else echo "${HOSTNAME} (${REAL_HOSTNAME})" fi } get_repository_status() { dir=$1 # tell Git where to find the repository and the work tree (no need to `cd …` there) export GIT_DIR="${dir}/.git" GIT_WORK_TREE="${dir}" # If the repository and the work tree exist, try to commit changes if test -d "${GIT_DIR}" && test -d "${GIT_WORK_TREE}"; then CHANGED_LINES=$(${GIT_BIN} status --porcelain | wc -l | tr -d ' ') if [ "${CHANGED_LINES}" != "0" ]; then STATUS=$(${GIT_BIN} status --short | tail -n 10) printf "%s\n%s\n" "${GIT_DIR} (last 10 lines)" "${STATUS}" | sed -e '/^$/d' fi fi # unset environment variables to prevent accidental influence on other git commands unset GIT_DIR GIT_WORK_TREE } print_summary() { BLOB=$(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\n********** DB query **************\n%s\n***********************************\n" "${PG_QUERY}" fi if [ "${DRY_RUN}" != "1" ]; then echo "${PG_QUERY}" | psql "${PGDB}" "${PGTABLE}" -h "${PGHOST}" fi } hook_mail() { MAIL_TEXTE=$(echo "${MESSAGE}" | sed -e "s@/@\\\\\/@g ; s@&@\\\\&@") MAIL_GIT_COMMITS=$(echo "${GIT_COMMITS}" | sed -e "s@/@\\\\\/@g ; s@&@\\\\&@") MAIL_CONTENT=$(sed -e "s/__TO__/${EVOMAINTMAIL}/" \ -e "s/__HOSTNAME__/${HOSTNAME_TEXT}/" \ -e "s/__USER__/${USER}/" \ -e "s/__BEGIN_DATE__/${BEGIN_DATE}/" \ -e "s/__END_DATE__/${END_DATE}/" \ -e "s/__GIT_COMMITS__/${MAIL_GIT_COMMITS}/" \ -e "s/__TEXTE__/${MAIL_TEXTE}/" \ -e "s/__IP__/${IP}/" \ -e "s/__FULLFROM__/${FULLFROM}/" \ -e "s/__FROM__/${FROM}/" \ -e "s/__URGENCYFROM__/${URGENCYFROM}/" \ -e "s/__URGENCYTEL__/${URGENCYTEL}/" \ /usr/share/scripts/evomaintenance.tpl) if [ "${VERBOSE}" = "1" ]; then printf "\n\n********** Mail *******************\n%s\n***********************************\n" "${MAIL_CONTENT}" fi if [ "${DRY_RUN}" != "1" ]; then echo "${MAIL_CONTENT}" | ${SENDMAIL_BIN} -oi -t -f "${FROM}" fi } hook_log() { echo "----------- $(get_now) ---------------" >> "${LOGFILE}" echo "${BLOB}" >> "${LOGFILE}" } # load configuration if present. test -f /etc/evomaintenance.cf && . /etc/evomaintenance.cf [ -n "${HOSTNAME}" ] || HOSTNAME=$(get_fqdn) [ -n "${EVOMAINTMAIL}" ] || EVOMAINTMAIL=evomaintenance-$(echo "${HOSTNAME}" | cut -d- -f1)@${REALM} [ -n "${LOGFILE}" ] || LOGFILE=/var/log/evomaintenance.log [ -n "${OPT_COMMIT}" ] || OPT_COMMIT=1 [ -n "${OPT_DB}" ] || OPT_DB=1 [ -n "${OPT_MAIL}" ] || OPT_MAIL=1 [ -n "${DRY_RUN}" ] || DRY_RUN=0 [ -n "${VERBOSE}" ] || VERBOSE=0 # initialize variables MESSAGE="" 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 OPT_COMMIT=0 ;; --commit) # enable commit hook OPT_COMMIT=1 ;; --no-db) # disable DB hook OPT_DB=0 ;; --db) # enable DB hook OPT_DB=1 ;; --no-mail) # disable mail hook OPT_MAIL=0 ;; --mail) # enable mail hook OPT_MAIL=1 ;; -n|--dry-run) # disable actual commands DRY_RUN=1 ;; -v|--verbose) # print verbose information VERBOSE=1 ;; --) # End of all options. shift break ;; -?*) # 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 if [ "${VERBOSE}" = "1" ]; then print_options fi # 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) GIT_BIN=$(command -v git) GIT_REPOSITORIES="/etc /etc/bind" # initialize variable GIT_STATUSES="" # git statuses if test -x "${GIT_BIN}"; then # loop on possible directories managed by GIT for dir in ${GIT_REPOSITORIES}; do RESULT=$(get_repository_status "${dir}") if test -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 if [ -z "${MESSAGE}" ] && [ "${INTERACTIVE}" = "1" ]; then # get input from stdin printf "> Please, enter details about your maintenance:\n" read -r MESSAGE fi if test -z "${MESSAGE}"; then echo "no value..." exit 1 fi print_summary # Log hook if [ "${DRY_RUN}" != "1" ]; then hook_log fi # Commit hook if [ "${INTERACTIVE}" = "1" ]; then if test -n "${GIT_STATUSES}" && [ "${OPT_COMMIT}" = "1" ]; then echo "/!\ There are some uncommited changes." echo "${GIT_STATUSES}" echo "" y="Y"; n="n" answer="" while true; 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 [ "${OPT_COMMIT}" = "1" ]; then hook_commit fi break ;; * ) echo "answer with a valid choice" ;; esac done fi else if [ "${OPT_COMMIT}" = "1" ]; then hook_commit fi fi # Database hook if [ "${INTERACTIVE}" = "1" ]; then if [ "${OPT_DB}" = "1" ]; then y="Y"; n="n" else y="y"; n="N" fi answer="" while true; 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 [ "${OPT_DB}" = "1" ]; then hook_db fi break ;; * ) echo "answer with a valid choice" ;; esac done else if [ "${OPT_DB}" = "1" ]; then hook_db fi fi # Mail hook if [ "${INTERACTIVE}" = "1" ]; then if [ "${OPT_MAIL}" = "1" ]; then y="Y"; n="n" else y="y"; n="N" fi answer="" while true; 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 [ "${OPT_MAIL}" = "1" ]; then hook_mail fi break ;; * ) echo "answer with a valid choice" ;; esac done else if [ "${OPT_MAIL}" = "1" ]; then hook_mail fi fi exit 0