evomaintenance/evomaintenance.sh

462 lines
13 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh
# EvoMaintenance script
# Dependencies (all OS): git postgresql-client
# Dependencies (Debian): sudo
# version 0.4.1
# Copyright 2007-2018 Gregory Colpart <reg@evolix.fr>, Jérémy Lecour <jlecour@evolix.fr>, Evolix <info@evolix.fr>
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 <<END
Host : $HOSTNAME_TEXT
User : $USER
IP : $IP
Begin : $BEGIN_DATE
End : $END_DATE
Message : $MESSAGE
END
)
echo "${BLOB}"
echo ""
}
print_options() {
echo "********** Options ****************"
echo "MESSAGE: ${MESSAGE}"
echo "OPT_COMMIT: ${OPT_COMMIT}"
echo "OPT_DB: ${OPT_DB}"
echo "OPT_MAIL: ${OPT_MAIL}"
echo "DRY_RUN: ${DRY_RUN}"
echo "***********************************\n"
}
hook_commit() {
GIT_COMMITS=""
if test -x "${GIT_BIN}"; then
# loop on possible directories managed by GIT
for dir in ${GIT_REPOSITORIES}; do
# 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
if [ "${DRY_RUN}" = "1" ]; then
STATS=$(${GIT_BIN} diff --stat | tail -1)
GIT_COMMITS=$(printf "%s\n%s : %s" "${GIT_COMMITS}" "${GIT_DIR}" "${STATS}" | sed -e '/^$/d')
else
${GIT_BIN} add --all
${GIT_BIN} commit --message "${MESSAGE}" --author="${USER} <${USER}@evolix.net>" --quiet
# Add the SHA to the log file if something has been committed
SHA=$(${GIT_BIN} rev-parse --short HEAD)
STATS=$(${GIT_BIN} show --stat | tail -1)
# append commit data, without empty lines
GIT_COMMITS=$(printf "%s\n%s : %s %s" "${GIT_COMMITS}" "${GIT_DIR}" "${SHA}" "${STATS}" | sed -e '/^$/d')
fi
fi
fi
# unset environment variables to prevent accidental influence on other git commands
unset GIT_DIR GIT_WORK_TREE
done
if [ -n "${GIT_COMMITS}" ]; then
if [ "${VERBOSE}" = "1" ]; then
echo "\n\n********** Commits ****************\n${GIT_COMMITS}\n***********************************"
fi
if [ "${DRY_RUN}" != "1" ]; then
echo "${GIT_COMMITS}" >> "${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
echo "\n\n********** DB query **************\n${PG_QUERY}\n***********************************"
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}/ ; s/__HOSTNAME__/${HOSTNAME_TEXT}/ ; s/__USER__/${USER}/ ; s/__BEGIN_DATE__/${BEGIN_DATE}/ ; s/__END_DATE__/${END_DATE}/ ; s/__GIT_COMMITS__/${MAIL_GIT_COMMITS}/ ; s/__TEXTE__/${MAIL_TEXTE}/ ; s/__IP__/${IP}/ ; s/__FULLFROM__/${FULLFROM}/ ; s/__FROM__/${FROM}/ ; s/__URGENCYFROM__/${URGENCYFROM}/ ; s/__URGENCYTEL__/${URGENCYTEL}/" /usr/share/scripts/evomaintenance.tpl)
if [ "${VERBOSE}" = "1" ]; then
echo "\n\n********** Mail *******************\n${MAIL_CONTENT}\n***********************************"
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 variable
MESSAGE=""
# Parse options
# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case $1 in
# -h|-\?|--help) # Call a "show_help" function to display a synopsis, then exit.
# show_help
# exit
# ;;
-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
echo "> Please, enter details about your maintenance"
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"
question="Do you want to commit the changes? [${y}${n}] "
answer=""
while true; do
echo "${question}"
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
question="\nDo you want to insert your message into the database? [${y}${n}] "
answer=""
while true; do
echo "${question}"
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
echo "Do you want to send an email to <${EVOMAINTMAIL}>? [${y}${n}e] "
read -r answer
case $answer in
[Yy] )
hook_db;
break
;;
[Nn] )
break
;;
[Ee] )
echo "To: [${EVOMAINTMAIL}] "
read -r mail_recipient
if [ -n "${mail_recipient}" ]; then
EVOMAINTMAIL="${mail_recipient}"
echo "changed to ${EVOMAINTMAIL}"
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