From fbefb989620610e3b838b61e1604cda6e98929ba Mon Sep 17 00:00:00 2001 From: efnet-nl Date: Mon, 7 Sep 2009 19:07:46 +0000 Subject: [PATCH] Added bitcron(1) and a cron to update and HUP the ircd git-svn-id: svn+ssh://svn.ipng.nl/usr/share/subversion/repositories/irc.efnet.nl@7 0a436592-62d7-4152-98a5-b7e29e440240 --- cron/bitcron | 329 +++++++++++++++++++++++++++++++++++++++++++++++ cron/update.cron | 28 ++++ 2 files changed, 357 insertions(+) create mode 100755 cron/bitcron create mode 100644 cron/update.cron diff --git a/cron/bitcron b/cron/bitcron new file mode 100755 index 0000000..8b35afc --- /dev/null +++ b/cron/bitcron @@ -0,0 +1,329 @@ +#!/usr/local/bin/bash + +# Original author: Pim van Pelt +# Taken from OSS publication upstream: +# debit.bit.nl:/debian/dists/stable/main/binary-i386/bit-cron-1.9-20060627.02.deb +# and modified for use at Google. +# +# Author: Pim van Pelt + +# This is the master cron file format. It is designed to be quiet +# and not shout anything at all to stdout/stderr in normal use. +# +# Usage: bitcron [-n] /path/to/crondefinition.cron [arg [arg ..]] +# +# This program eats either one or two arguments from the commandline. +# If -n is given as the first argument, output is sent to stdout rather +# than MAILTO or ESCALATE_MAILTO (this is a `dryrun'). The next argument +# is a filename consisting of the cron definition. The file is sourced +# and executed, but within a framework that makes crons much easier to +# live with. You SHOULD have the filename of your crondefinition end +# in ".cron", so it is apparent to other people that this is indeed +# a cron definition. All other arguments are passed to the crontab's +# main function, called bitcron_main; see below. +# +# The cron definition file consists of the following shell variables: +# +# PATH: Optional. You can set a standard path for your cron to search +# programs in. Note that cron sets a minimal path, so you probably need +# this if you are going to call programs from weird places. +# Example: PATH=$PATH +# +# AUTHOR: Mandatory. Your full name, so we can track the author in case +# something goes horribly wrong. +# Example: AUTHOR="Pim van Pelt" +# +# NAME: Mandatory. The name of your script. This MUST be a terse +# descriptive name consisting of [A-Za-z0-9\-\_], and in particular +# no whitespace. It's used in logs and mails. +# Example: NAME="example" +# +# MASTERLOG: Optional. If this is set, the transcript of the log is copied +# to this file upon completion. If it is not set, the transcript is silently +# deleted. Please make sure that this filename is writable for the calling +# user, and that its name reveals who wrote it (including $NAME is a good +# idea. +# Example: MASTERLOG="/var/log/cron-${NAME}.log" +# +# MAILTO: Optional. If this is set, the transcript of the log is mailed to +# this user using /usr/sbin/sendmail -t, in the event of no warnings, +# errors, or fatal errors, aka 'if all went well'. If it is not set, the +# mail is not sent on succesful completion of this cronjob. Please use +# with care if your cron runs often. +# Example: MAILTO="pim+noise@google.com" +# +# ESCALATE_MAILTO: Mandatory. If this is set, the transcript of the log +# is mailed to this user using /usr/sbin/sendmail -t, in the event of +# any warning, error or fatal error, aka 'if anything borked'. Please +# make sure that this address is not a single person, but rather a role +# account. This ensures better closure and continuity. +# Example: ESCALATE_MAILTO="maps-alerts@google.com" +# +# Variables your script MUST NOT change are: +# ERR, WRN, HOSTNAME, FATAL, FQDN +# +# Your cron-definition does not shebang. It begins with these variables. +# It then has at least one function called bitcron_main(), in which you write +# your cron just like you normally would, but each step is prepended by a +# short oneliner describing what you are doing and appended with a line +# stating you are done doing it, followed by an empty line. +# +# In your bitcron_main() script, you have three functions at your disposal: +# warning $TEXT +# error $TEXT +# fatal $TEXT +# +# where warning() prints the TEXT and increments the warning counter, error() +# increments the error counter and fatal() increments the error counter and +# immediately halts execution of the cron. +# +# For example: +# bitcron_main() +# { +# echo "Running rsync" +# rsync blabla || warning "Could not rsync" +# echo "Done (rsync)" +# echo "" +# +# echo "Doing something crucial" +# /usr/local/bin/crucial.sh blabla || \ +# fatal "Could not do the crucial stuff, aborting!" +# echo "Done (crucial)" +# echo "" +# +# echo "Succesfully executed the cron" +# } +# +# In the event of a fatal error (retval of crucial.sh != 0), the line +# 'echo "Done (crucial)"' will not be executed, rather the cronscript +# stops and mails to the escalation mail address that there was a serious +# error which needs human intervention. +# +# Return values of bitcron depend on the presence of errors or warnings. +# If there are no errors nor warnings, 0 is returned to the calling shell. +# If there are errors, 127 is returned. If there are no errors, but there +# are warnings, 126 is returned. If you somewhere set the RETVAL variable +# (which is optional), that value is returned instead. +# +# Please note that your bitcron_main() function MUST NOT use exit. This messes up +# the clean execution of this script, prohibiting it from mailing or +# returning a decent exitcode to the calling shell. +# + +# BITCRON_SEARCHPATH="/usr/local/cronscripts/ /etc/cronscripts/ ./" +BITCRON_SEARCHPATH="~/cronscripts/ ./" +BITCRON_REPORTTOOL="/usr/sbin/sendmail -t" + +function fatal() +{ +echo "" +echo "===== FATAL =====" +echo $1 +echo "===== FATAL =====" +ERR=$(($ERR + 1)) +FATAL=1; +bitcron_end +} + +function error() +{ +echo "" +echo "===== ERROR =====" +echo $1 +echo "===== ERROR =====" +ERR=$(($ERR + 1)) +} + +function warning() +{ +echo "" +echo "===== WARNING =====" +echo $1 +echo "===== WARNING =====" +WRN=$(($WRN + 1)) +} + +function bitcron_report() +{ +if [ $WRN -eq 0 -a $ERR -eq 0 ]; then + if [ ! -z "$MAILTO" ]; then + ( + echo "From: $HOSTNAME $NAME <`whoami`@$FQDN>" + echo "To: $MAILTO" + echo "Subject: bitcron $NAME - succesful" + echo "Date: `date '+%a, %d %b %Y %T %z'`" + echo "Errors-To: $MAILTO" + echo "Reply-To: $MAILTO" + echo "X-bitcron: $NAME" + echo "X-Precedence: automatic" + echo "" + echo "Dear reader," + echo "" + echo "This is the $0 program on $HOSTNAME informing" + echo "you I have succesfully executed the bitcron $NAME." + echo "" + echo "The log that I have for this cronjob follows:" + cat $LOG + echo "" + echo "This mail is purely FYI, user intervention is not required." + echo "" + echo "-- " + echo "Cheers," + echo " $HOSTNAME" + ) | ${BITCRON_REPORTTOOL} + fi +else + ( + echo "From: $HOSTNAME $NAME <`whoami`@$FQDN>" + echo "To: $ESCALATE_MAILTO" + if [ ! -z "$MAILTO" ]; then + echo "CC: $MAILTO" + fi + if [ $FATAL -eq 0 ]; then + echo "Subject: bitcron $NAME - $ERR error(s), $WRN warning(s)" + else + echo "Subject: bitcron $NAME - FATAL - $ERR error(s), $WRN warning(s)" + fi + echo "Date: `date '+%a, %d %b %Y %T %z'`" + echo "Errors-To: $ESCALATE_MAILTO" + echo "Reply-To: $ESCALATE_MAILTO" + echo "X-Precedence: automatic" + echo "X-bitcron: $NAME" + echo "X-Errors: $ERR" + echo "X-Warnings: $WRN" + echo "" + echo "Dear reader," + echo "" + echo "This is the $0 program on $HOSTNAME informing" + echo "you that there was a problem running the bitcron $NAME." + echo "" + echo "The logfile that lead up to this:" + cat $LOG + echo "" + echo "I hope you can deal with this situation ASAP." + echo "" + echo "-- " + echo "Cheers," + echo " $HOSTNAME" + ) | ${BITCRON_REPORTTOOL} +fi +} + +function bitcron_begin() +{ +if [ -z "$MASTERLOG" ]; then + LOG="/tmp/bitcron-$NAME.$$" +else + LOG="${MASTERLOG}.$$" +fi +rm -f -- $LOG +exec 3>&1 4>&2 1>$LOG 2>&1 +WRN=0 +ERR=0 +FATAL=0 +FQDN=$(hostname) +HOSTNAME=$(hostname -s) +if [ "A`echo $FQDN | grep '\.'`" = "A" ]; then + FQDN="${FQDN}.corp.google.com" +fi + +echo "Beginning $NAME cronjob at `date`" +echo "" +} + +function bitcron_end() +{ +echo "" +echo "Done with $NAME cronjob at `date`" +echo "" +echo "There were $ERR error(s) and $WRN warning(s)" +if [ $FATAL -ne 0 ]; then + echo "This script had a fatal error!" +fi + +exec 1>&3 2>&4 + +bitcron_report + +if [ ! -z "$MASTERLOG" ]; then + cp -f -- "$LOG" "$MASTERLOG" +fi +rm -f -- $LOG >/dev/null 2>&1 +if [ ! -z $RETVAL ]; then + exit $RETVAL +elif [ $ERR -ne 0 ]; then + exit 127 +elif [ $WRN -ne 0 ]; then + exit 126 +fi +exit 0 +} + +function bitcron_usage() +{ + echo "" + echo "Usage: bitcron [-n] /path/to/crondefinition.cron [arg [arg ..]]" + echo "" + echo "Please read the bitcron script. It has documentation at the" + echo "top of the file. Look in $0 for more details." + echo "-- Author: Pim van Pelt " +} + +# Here goes! +if [ $# -lt 1 ]; then + bitcron_usage + exit 128 +fi + +# See if we are in dryrun mode. +if [ $1 = "-n" ]; then + BITCRON_REPORTTOOL="/bin/cat" + shift +fi + +BITCRON_DEFINITION=$1 +shift; +# +# Search through paths if the cron definition was not an absolute path. +# +if [ ${BITCRON_DEFINITION%%/*} ]; then + for BITCRON_DIR in ${BITCRON_SEARCHPATH}; do + if [ -r ${BITCRON_DIR}${BITCRON_DEFINITION} ]; then + break; + fi + done +else + BITCRON_DIR="" +fi + +if [ ! -r ${BITCRON_DIR}${BITCRON_DEFINITION} ]; then + echo "bitcron: Unreadable cron definition: ${BITCRON_DIR}${BITCRON_DEFINITION}" + if [ ${BITCRON_DEFINITION%%/*} ]; then + echo "I tried in: ${BITCRON_SEARCHPATH}" + fi + bitcron_usage + exit 128 +fi + + +. ${BITCRON_DIR}${BITCRON_DEFINITION} + +if [ -z "$NAME" ]; then + echo "bitcron: Missing mandatory variable NAME in ${BITCRON_DIR}${BITCRON_DEFINITION}" + bitcron_usage + exit 128 +fi +if [ -z "$AUTHOR" ]; then + echo "bitcron: Missing mandatory variable AUTHOR in ${BITCRON_DIR}${BITCRON_DEFINITION}" + bitcron_usage + exit 128 +fi +if [ -z "$ESCALATE_MAILTO" ]; then + echo "bitcron: Missing mandatory variable ESCALATE_MAILTO in ${BITCRON_DIR}${BITCRON_DEFINITION}" + bitcron_usage + exit 128 +fi + +bitcron_begin +bitcron_main $* +bitcron_end diff --git a/cron/update.cron b/cron/update.cron new file mode 100644 index 0000000..045a7ca --- /dev/null +++ b/cron/update.cron @@ -0,0 +1,28 @@ +NAME="ircd_update" +AUTHOR="Pim van Pelt" +MAILTO="pim@ipng.nl" +ESCALATE_MAILTO="pim@ipng.nl" +MASTERLOG="/home/ircd/cronscripts/logs/${NAME}.log" + +PATH=/usr/local/bin:/usr/bin:/bin:/home/ircd/bin + +function bitcron_main() +{ + DIFF="/tmp/${NAME}.diff.$$" + + cd ~/ircd/etc || fatal "No config directory" + + svn diff -r HEAD > $DIFF || fatal "Could not read the SVN repository" + + [ ! -r $DIFF ] && fatal "Cannot read diff file" + + [ -s $DIFF ] && { + echo "Incorporating these diffs from the repository" + cat $DIFF + svn update || error "Could not update local copy" + echo "Sending -HUP to the ircd" + pkill -HUP ircd || error "Could not send HUP to ircd" + } + + rm -f -- $DIFF +}