evomaintenance/evomaintenance.sh
Jérémy Lecour b9da112b6d Add real interactive mode
The message can be given as an option, as stdin or interactively.
2019-03-09 14:01:38 +01:00

315 lines
9.6 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
}
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 [ "${DRY_RUN}" = "1" ]; then
echo "\n\n********** Commits ****************\n${GIT_COMMITS}\n***********************************"
else
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 [ "${DRY_RUN}" = "1" ]; then
echo "\n\n********** DB query **************\n${PG_QUERY}\n***********************************"
else
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=$(cat /usr/share/scripts/evomaintenance.tpl | \
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}/")
if [ "${DRY_RUN}" = "1" ]; then
echo "\n\n********** Mail *******************\n${MAIL_CONTENT}\n***********************************"
else
echo "${MAIL_CONTENT}" | ${SENDMAIL_BIN} -oi -t -f ${FROM}
fi
}
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
# 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) # Takes an option argument, ensuring it has been specified.
if [ -n "$2" ]; then
MESSAGE=$2
shift
else
printf 'ERROR: "--message" requires a non-empty option argument.\n' >&2
exit 1
fi
;;
--message=?*)
MESSAGE=${1#*=} # Delete everything up to "=" and assign the remainder.
;;
--message=) # Handle the case of an empty --file=
printf 'ERROR: "--message" requires a non-empty option argument.\n' >&2
exit 1
;;
--no-commit) # Takes an option argument
OPT_COMMIT=0
;;
--commit) # Takes an option argument
OPT_COMMIT=1
;;
--no-db) # Takes an option argument
OPT_DB=0
;;
--db) # Takes an option argument
OPT_DB=1
;;
--no-mail) # Takes an option argument
OPT_MAIL=0
;;
--mail) # Takes an option argument
OPT_MAIL=1
;;
-n|--dry-run) # Takes an option argument
DRY_RUN=1
;;
--) # End of all options.
shift
break
;;
-?*)
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
MESSAGE=""
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"
# 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
if test -n "${GIT_STATUSES}"; then
echo "/!\ There are some uncommited changes. If you proceed, everything will be commited."
echo "${GIT_STATUSES}"
echo ""
fi
fi
if [ -t 0 ]; then
INTERACTIVE=1
else
INTERACTIVE=0
fi
if [ -z "${MESSAGE}" -a "${INTERACTIVE}" = "1" ]; then
# get input from stdin
echo "> Please, enter details about your maintenance"
read MESSAGE
fi
if test -z "${MESSAGE}"; then
echo "no value..."
exit 1
fi
# recapitulatif
BLOB=$(cat <<END
Host : $HOSTNAME_TEXT
User : $USER
IP : $IP
Begin : $BEGIN_DATE
End : $END_DATE
Message : $MESSAGE
END
)
echo ""
echo "${BLOB}"
echo ""
echo "> Press <Enter> to submit, or <Ctrl+c> to cancel."
read enter
# write log
if [ "${DRY_RUN}" = "1" ]; then
echo "\n\n********** Log ********************\n${BLOB}\n***********************************"
else
echo "----------- $(get_now) ---------------" >> "${LOGFILE}"
echo "${BLOB}" >> "${LOGFILE}"
fi
# Hooks
# git commit
[ "${OPT_COMMIT}" = "1" ] && hook_commit
# insert into PG
[ "${OPT_DB}" = "1" ] && hook_db
# send mail
[ "${OPT_MAIL}" = "1" ] && hook_mail
exit 0