2 # No way I try to deal with a crippled sh just for POSIX foo.
4 # Copyright (C) 2009-2016 Joerg Jaspert <joerg@debian.org>
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License as
8 # published by the Free Software Foundation; version 2.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 # Homer: Are you saying you're never going to eat any animal again? What
25 # Lisa: Dad, those all come from the same animal.
26 # Homer: Heh heh heh. Ooh, yeah, right, Lisa. A wonderful, magical animal.
30 # A pipeline's return status is the value of the last (rightmost)
31 # command to exit with a non-zero status, or zero if all commands exit
34 # make sure to only use defined variables
36 # ERR traps should be inherited from functions too. (And command
37 # substitutions and subshells and whatnot, but for us the functions is
38 # the important part here)
41 # If the extglob shell option is enabled using the shopt builtin,
42 # several extended pattern matching operators are recognized. We use
43 # it for the POSSIBLEARGS and the first case ${ARGS} matching.
46 # And use one locale, no matter what the caller has set
50 # If run from crontab, CONFIGDIR will point to the correct dir
51 # where we find the vars file
52 configdir=${configdir:-"/srv/ftp-master.debian.org/dak/config/debian"}
53 # import the general variable set. (This will overwrite configdir, but
54 # it is expected to have the same value)
55 export SCRIPTVARS=${configdir}/vars
59 declare -lr ARG=${1:-"meh"}
61 # program name is the (lower cased) first argument.
64 # set DEBUG if you want to see a little more logs (needs to be used more)
67 # Check if the argument is a known one. If so, lock us so that only
68 # one copy of the type of cronscript runs. The $type.tasks file is
69 # mandantory, so use that for locking.
72 # Only one of me should ever run.
73 FLOCKER=${FLOCKER:-""}
74 [ "${FLOCKER}" != "${configdir}/${PROGRAM}.tasks" ] && exec env FLOCKER="${configdir}/${PROGRAM}.tasks" flock -E 0 -en "${configdir}/${PROGRAM}.tasks" "$0" "$@" || :
78 This is the cronscript. It needs an argument or it won't do anything
81 Currently accepted Arguments: ${POSSIBLEARGS}
83 To see what they do, you want to look at the files
84 \$ARGUMENT.{tasks,functions,variables} in ${configdir}.
93 flock --shared --nonblock 42 || LOCKFREE=1
95 # Did we get the lock? (It's shared, so usually we will. But DSA
96 # can take an exclusive one in preparation for a reboot)
97 if [[ ${LOCKFREE} -gt 0 ]]; then
98 echo "Couldn't get (shared) reboot lock"
102 # common functions are "outsourced"
103 . "${configdir}/common"
105 # Timestamp when we started
106 NOW=$(date "+%Y.%m.%d-%H:%M:%S")
108 # Which list of tasks should we run?
109 declare -r TASKLIST="${configdir}/${PROGRAM}.tasks"
111 # A logfile for every cron script
112 LOGFILE="${logdir}/${PROGRAM}_${NOW}.log"
114 # Each "cronscript" may have a variables and a functions file
116 for what in variables functions; do
117 if [[ -f ${configdir}/${PROGRAM}.${what} ]]; then
118 . ${configdir}/${PROGRAM}.${what}
122 # Get rid of tempfiles at the end
123 trap cleanup EXIT TERM HUP INT QUIT
125 # If there is a precronscript function, we run it.
126 prefunc=$(type -t precronscript || echo "")
127 if [[ -n ${prefunc} ]] && [[ ${prefunc} = function ]]; then
131 # An easy access by name for the current log
132 ln -sf ${LOGFILE} ${logdir}/${PROGRAM}
134 # And from here, all output to the log please
135 exec >> "$LOGFILE" 2>&1
137 # The stage function uses this directory
138 # This amends the stagedir variable from "vars"
139 stagedir="${stagedir}/${PROGRAM}"
140 # Ensure the dir exists
143 # This loop simply wants to be fed by a list of values (see below)
144 # made out of 5 columns.
145 # The first four are the array values for the stage function, the
146 # fifth tells us if we should background the stage call.
148 # - FUNC - the function name to call
149 # - ARGS - Possible arguments to hand to the function. Can be the empty string
150 # - TIME - The timestamp name. Can be the empty string
151 # - ERR - if this is the string false, then the call will be surrounded by
152 # set +e ... set -e calls, so errors in the function do not exit
153 # dinstall. Can be the empty string, meaning true.
154 # - BG - Background the function stage?
156 # ATTENTION: Spaces in arguments or timestamp names need to be escaped by \
158 # NOTE 1: There are two special values for the first column (FUNC).
159 # STATE - do not call stage function, call the state
160 # function to update the public statefile "where is dinstall"
161 # NOSTAGE - do not call stage function, call the command directly.
163 # Note 2: If you want to hand an empty value to the stage function,
164 # use the word "none" in the list below.
165 while read FUNC ARGS TIME ERR BACKGROUND; do
166 debug "FUNC: $FUNC ARGS: $ARGS TIME: $TIME ERR: $ERR BG: $BACKGROUND"
168 # Empty values in the value list are the string "none" (or the
169 # while read loop won't work). Here we ensure that variables that
170 # can be empty, are empty if the string none is set for them.
171 for var in ARGS TIME; do
172 if [[ ${!var} == none ]]; then
177 # ERR/BACKGROUND are boolean, check that they are.
178 for var in ERR BACKGROUND; do
179 if [[ ${!var} != false ]] && [[ ${!var} != true ]]; then
180 error "Illegal value ${!var} for ${var} (should be true or false), line for function ${FUNC}"
198 if [[ ${BACKGROUND} == true ]]; then
205 done < <(grep -v '^#' ${TASKLIST} )
207 # we need to wait for the background processes before the end of the cron script
210 # Common to all cron scripts
211 log "Cron script successful, all done"
212 # Redirect output to another file, as we want to compress our logfile
213 # and ensure its no longer used
214 exec > "$logdir/after${PROGRAM}.log" 2>&1
216 # If there is a postcronscript function, we run it.
217 postfunc=$(type -t postcronscript || echo "")
218 if [[ -n ${postfunc} ]] && [[ ${postfunc} = function ]]; then
222 # Now, at the very (successful) end of this run, make sure we remove
223 # our stage files, so the next dinstall run will do it all again.
227 # Logfile should be gone, remove the symlink
228 [[ -L ${logdir}/${PROGRAM} ]] && [[ ! -f ${logdir}/${PROGRAM} ]] && rm -f ${logdir}/${PROGRAM} || log "Logfile still exists or symlink gone already? Something fishy going on"
230 # FIXME: Mail the log when its non-empty
231 [[ -s "${logdir}/after${PROGRAM}.log" ]] || rm "${logdir}/after${PROGRAM}.log"
233 # And end the reboot-locked part
234 ) 42</var/run/reboot-lock