X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=config%2Fdebian%2Fcronscript;h=2418d79a75249ad441c8e6e8e4ad303a391a574d;hb=8f72f604a89bb3ff3dba82521dd9fc474208f189;hp=fec8f93e14236632e9e27efed674674f16157220;hpb=9639afdb34ea7c5fdaf62e05a78ca0aab288f6de;p=dak.git diff --git a/config/debian/cronscript b/config/debian/cronscript index fec8f93e..2418d79a 100755 --- a/config/debian/cronscript +++ b/config/debian/cronscript @@ -27,6 +27,9 @@ # exit on errors set -e +# A pipeline's return status is the value of the last (rightmost) +# command to exit with a non-zero status, or zero if all commands exit +# successfully. set -o pipefail # make sure to only use defined variables set -u @@ -35,19 +38,46 @@ set -u # the important part here) set -E +# The extglob shell option is enabled using the shopt builtin, several +# extended pattern matching operators are recognized. We use it for +# the POSSIBLEARGS and the first case ${ARGS} matching +shopt -s extglob + # And use one locale, no matter what the caller has set -export LANG=C -export LC_ALL=C +export LANG=C.UTF-8 +export LC_ALL=C.UTF-8 + +# If run from crontab, CONFIGDIR will point to the correct dir +# where we find the vars file +configdir=${configdir:-"/srv/ftp-master.debian.org/dak/config/debian"} +# import the general variable set. (This will overwrite configdir, but +# it is expected to have the same value) +export SCRIPTVARS=${configdir}/vars +. $SCRIPTVARS -ARG=${1:-"meh"} +# One arg please +declare -lr ARG=${1:-"meh"} + +# program name is the (lower cased) first argument. +PROGRAM="${ARG}" + +# And the following types of cronscripts exists +declare -lr POSSIBLEARGS='+(unchecked|dinstall|hourly|daily|weekly|monthly|yearly)' # set DEBUG if you want to see a little more logs (needs to be used more) DEBUG=${DEBUG:-0} -# While this check can be done in the following case, some assumptions -# down there are easier if we sorted out calls without an arg before. -if [[ ${ARG} == meh ]]; then - cat - < /dev/null; then - # log "aborting cron.unchecked because $LOCK_UNCHECKED has already been locked" - exit 0 - fi - TMPFILES="${TMPFILES} ${LOCK_UNCHECKED}" - ;; - dinstall) - ;; - hourly) - # Only one of me should ever run. - FLOCKER=${FLOCKER:-""} - [ "${FLOCKER}" != "${configdir}/${PROGRAM}.variables" ] && exec env FLOCKER="${configdir}/${PROGRAM}.variables" flock -E 0 -en "${configdir}/${PROGRAM}.variables" "$0" "$@" || : - ;; - daily) - ;; - weekly) - ;; - monthly) - ;; - *) - error "Unknown arg ${ARG}" - exit 42 - ;; -esac + # common functions are "outsourced" + . "${configdir}/common" -# An easy access by name for the current log -ln -sf ${LOGFILE} ${logdir}/${PROGRAM} + # Timestamp when we started + NOW=$(date "+%Y.%m.%d-%H:%M:%S") -# And from here, all output to the log please -exec >> "$LOGFILE" 2>&1 + # Which list of tasks should we run? + declare -r TASKLIST="${configdir}/${PROGRAM}.tasks" -# The stage function uses this directory -# This amends the stagedir variable from "vars" -stagedir="${stagedir}/${PROGRAM}" -# Ensure the dir exists -mkdir -p ${stagedir} + # A logfile for every cron script + LOGFILE="${logdir}/${PROGRAM}_${NOW}.log" -# This loop simply wants to be fed by a list of values (see below) -# made out of 5 columns. -# The first four are the array values for the stage function, the -# fifth tells us if we should background the stage call. -# -# - FUNC - the function name to call -# - ARGS - Possible arguments to hand to the function. Can be the empty string -# - TIME - The timestamp name. Can be the empty string -# - ERR - if this is the string false, then the call will be surrounded by -# set +e ... set -e calls, so errors in the function do not exit -# dinstall. Can be the empty string, meaning true. -# - BG - Background the function stage? -# -# ATTENTION: Spaces in arguments or timestamp names need to be escaped by \ -# -# NOTE 1: There are two special values for the first column (FUNC). -# STATE - do not call stage function, call the state -# function to update the public statefile "where is dinstall" -# NOSTAGE - do not call stage function, call the command directly. -# -# Note 2: If you want to hand an empty value to the stage function, -# use the word "none" in the list below. -while read FUNC ARGS TIME ERR BACKGROUND; do - debug "FUNC: $FUNC ARGS: $ARGS TIME: $TIME ERR: $ERR BG: $BACKGROUND" - - # Empty values in the value list are the string "none" (or the - # while read loop won't work). Here we ensure that variables that - # can be empty, are empty if the string none is set for them. - for var in ARGS TIME; do - if [[ ${!var} == none ]]; then - typeset ${var}='' + # Each "cronscript" may have a variables and a functions file + # that we source + for what in variables functions; do + if [[ -f ${configdir}/${PROGRAM}.${what} ]]; then + . ${configdir}/${PROGRAM}.${what} fi done - # ERR/BACKGROUND are boolean, check that they are. - for var in ERR BACKGROUND; do - if [[ ${!var} != false ]] && [[ ${!var} != true ]]; then - error "Illegal value ${!var} for ${var} (should be true or false), line for function ${FUNC}" - fi - done + # Get rid of tempfiles at the end + trap cleanup EXIT TERM HUP INT QUIT - case ${FUNC} in - STATE) - state ${ARGS} - ;; - NOSTAGE) - ${ARGS} - ;; + case ${ARG} in + unchecked) + # Do not run during dinstall + if [[ -e ${LOCK_DAILY} ]]; then + exit 0; + fi + # only run one cron.unchecked and also lock against hourly (newoverview) + if ! lockfile -r8 ${LOCK_UNCHECKED} 2> /dev/null; then + # log "aborting cron.unchecked because $LOCK_UNCHECKED has already been locked" + exit 0 + fi + TMPFILES="${TMPFILES} ${LOCK_UNCHECKED}" + ;; + dinstall) + ;; + hourly) + ;; + daily) + ;; + weekly) + ;; + monthly) + ;; + yearly) + ;; *) - GO=( - FUNC=${FUNC} - TIME=${TIME} - ARGS=${ARGS} - ERR=${ERR} - ) - if [[ ${BACKGROUND} == true ]]; then - stage $GO & - else - stage $GO + error "Unknown arg ${ARG}" + exit 42 + ;; + esac + + # An easy access by name for the current log + ln -sf ${LOGFILE} ${logdir}/${PROGRAM} + + # And from here, all output to the log please + exec >> "$LOGFILE" 2>&1 + + # The stage function uses this directory + # This amends the stagedir variable from "vars" + stagedir="${stagedir}/${PROGRAM}" + # Ensure the dir exists + mkdir -p ${stagedir} + + # This loop simply wants to be fed by a list of values (see below) + # made out of 5 columns. + # The first four are the array values for the stage function, the + # fifth tells us if we should background the stage call. + # + # - FUNC - the function name to call + # - ARGS - Possible arguments to hand to the function. Can be the empty string + # - TIME - The timestamp name. Can be the empty string + # - ERR - if this is the string false, then the call will be surrounded by + # set +e ... set -e calls, so errors in the function do not exit + # dinstall. Can be the empty string, meaning true. + # - BG - Background the function stage? + # + # ATTENTION: Spaces in arguments or timestamp names need to be escaped by \ + # + # NOTE 1: There are two special values for the first column (FUNC). + # STATE - do not call stage function, call the state + # function to update the public statefile "where is dinstall" + # NOSTAGE - do not call stage function, call the command directly. + # + # Note 2: If you want to hand an empty value to the stage function, + # use the word "none" in the list below. + while read FUNC ARGS TIME ERR BACKGROUND; do + debug "FUNC: $FUNC ARGS: $ARGS TIME: $TIME ERR: $ERR BG: $BACKGROUND" + + # Empty values in the value list are the string "none" (or the + # while read loop won't work). Here we ensure that variables that + # can be empty, are empty if the string none is set for them. + for var in ARGS TIME; do + if [[ ${!var} == none ]]; then + typeset ${var}='' fi + done + + # ERR/BACKGROUND are boolean, check that they are. + for var in ERR BACKGROUND; do + if [[ ${!var} != false ]] && [[ ${!var} != true ]]; then + error "Illegal value ${!var} for ${var} (should be true or false), line for function ${FUNC}" + fi + done + + case ${FUNC} in + STATE) + state ${ARGS} + ;; + NOSTAGE) + ${ARGS} + ;; + *) + GO=( + FUNC=${FUNC} + TIME=${TIME} + ARGS=${ARGS} + ERR=${ERR} + ) + if [[ ${BACKGROUND} == true ]]; then + stage $GO & + else + stage $GO + fi + ;; + esac + done < <(grep -v '^#' ${TASKLIST} ) + + # we need to wait for the background processes before the end of the cron script + wait + + + # Common to all cron scripts + log "Cron script successful, all done" + # Redirect output to another file, as we want to compress our logfile + # and ensure its no longer used + exec > "$logdir/after${PROGRAM}.log" 2>&1 + + case ${ARG} in + unchecked) ;; + dinstall) + logstats ${LOGFILE} + state "all done" + touch "${DINSTALLEND}" + ;; + hourly) + ;; + daily) + ;; + weekly) + ;; + monthly) + ;; + yearly) + ;; esac -done < <(grep -v '^#' ${TASKLIST} ) - -# we need to wait for the background processes before the end of the cron script -wait + # Now, at the very (successful) end of this run, make sure we remove + # our stage files, so the next dinstall run will do it all again. + rm -f ${stagedir}/* + bzip2 -9 ${LOGFILE} -# Common to all cron scripts -log "Cron script successful, all done" -# Redirect output to another file, as we want to compress our logfile -# and ensure its no longer used -exec > "$logdir/after${PROGRAM}.log" 2>&1 + # Logfile should be gone, remove the symlink + [[ -L ${logdir}/${PROGRAM} ]] && [[ ! -f ${logdir}/${PROGRAM} ]] && rm -f ${logdir}/${PROGRAM} || log "Logfile still exists or symlink gone already? Something fishy going on" -case ${ARG,,} in - unchecked) - ;; - dinstall) - logstats ${LOGFILE} - state "all done" - touch "${DINSTALLEND}" - ;; - hourly) - ;; - daily) - ;; - weekly) - ;; - monthly) - ;; -esac + # FIXME: Mail the log when its non-empty + [[ -s "${logdir}/after${PROGRAM}.log" ]] || rm "${logdir}/after${PROGRAM}.log" -# Now, at the very (successful) end of this run, make sure we remove -# our stage files, so the next dinstall run will do it all again. -rm -f ${stagedir}/* -bzip2 -9 ${LOGFILE} -# FIXME: Mail the log when its non-empty -[[ -s "${logdir}/after${PROGRAM}.log" ]] || rm "${logdir}/after${PROGRAM}.log" +# And end the reboot-locked part +) 42