]> git.decadent.org.uk Git - dak.git/blob - config/debian/cronscript
Only hardcode path to config if variable isn't set
[dak.git] / config / debian / cronscript
1 #!/bin/bash
2 # No way I try to deal with a crippled sh just for POSIX foo.
3
4 # Copyright (C) 2009-2015 Joerg Jaspert <joerg@debian.org>
5 #
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.
9 #
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.
14 #
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.
18
19 # Homer: Are you saying you're never going to eat any animal again? What
20 #        about bacon?
21 # Lisa: No.
22 # Homer: Ham?
23 # Lisa: No.
24 # Homer: Pork chops?
25 # Lisa: Dad, those all come from the same animal.
26 # Homer: Heh heh heh. Ooh, yeah, right, Lisa. A wonderful, magical animal.
27
28 # exit on errors
29 set -e
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
32 # successfully.
33 set -o pipefail
34 # make sure to only use defined variables
35 set -u
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)
39 set -E
40
41 # The extglob shell option is enabled using the shopt builtin, several
42 # extended pattern matching operators are recognized. We use it for
43 # the POSSIBLEARGS and the first case ${ARGS} matching
44 shopt -s extglob
45
46 # And use one locale, no matter what the caller has set
47 export LANG=C.UTF-8
48 export LC_ALL=C.UTF-8
49
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
56 . $SCRIPTVARS
57
58 # One arg please
59 declare -lr ARG=${1:-"meh"}
60
61 # program name is the (lower cased) first argument.
62 PROGRAM="${ARG}"
63
64 # And the following types of cronscripts exists
65 declare -lr POSSIBLEARGS='+(unchecked|dinstall|hourly|daily|weekly|monthly|yearly)'
66
67 # set DEBUG if you want to see a little more logs (needs to be used more)
68 DEBUG=${DEBUG:-0}
69
70 # Check if the argument is a known one. If so, lock us so that only
71 # one copy of the type of cronscript runs. The $type.tasks file is
72 # mandantory, so use that for locking.
73 case ${ARG} in
74     ${POSSIBLEARGS})
75         # Only one of me should ever run.
76         FLOCKER=${FLOCKER:-""}
77         [  "${FLOCKER}"  != "${configdir}/${PROGRAM}.tasks" ] && exec env FLOCKER="${configdir}/${PROGRAM}.tasks" flock -E 0 -en "${configdir}/${PROGRAM}.tasks" "$0" "$@" || :
78         ;;
79     *)
80         cat - <<EOF
81 This is the FTPMaster cronscript. It needs an argument or it won't do
82 anything for you.
83
84 Currently accepted Arguments:
85
86    unchecked - Process the unchecked queue
87    dinstall  - Run a dinstall
88    yearly, hourly, daily, weekly - Run that part
89
90 EOF
91         exit 0
92         ;;
93 esac
94
95 (
96     LOCKFREE=0
97     flock --shared --nonblock 42 || LOCKFREE=1
98
99     # Did we get the lock? (It's shared, so usually we will. But DSA
100     # can take an exclusive one in preparation for a reboot)
101     if [[ ${LOCKFREE} -gt 0 ]]; then
102         echo "Couldn't get (shared) reboot lock"
103         exit 1
104     fi
105
106     # common functions are "outsourced"
107     . "${configdir}/common"
108
109     # Timestamp when we started
110     NOW=$(date "+%Y.%m.%d-%H:%M:%S")
111
112     # Which list of tasks should we run?
113     declare -r TASKLIST="${configdir}/${PROGRAM}.tasks"
114
115     # A logfile for every cron script
116     LOGFILE="${logdir}/${PROGRAM}_${NOW}.log"
117
118     # Each "cronscript" may have a variables and a functions file
119     # that we source
120     for what in variables functions; do
121         if [[ -f ${configdir}/${PROGRAM}.${what} ]]; then
122             . ${configdir}/${PROGRAM}.${what}
123         fi
124     done
125
126     # Get rid of tempfiles at the end
127     trap cleanup EXIT TERM HUP INT QUIT
128
129     case ${ARG} in
130         unchecked)
131             # Do not run during dinstall
132             if [[ -e ${LOCK_DAILY} ]]; then
133                 exit 0;
134             fi
135             # only run one cron.unchecked and also lock against hourly (newoverview)
136             if ! lockfile -r8 ${LOCK_UNCHECKED} 2> /dev/null; then
137                 # log "aborting cron.unchecked because $LOCK_UNCHECKED has already been locked"
138                 exit 0
139             fi
140             TMPFILES="${TMPFILES} ${LOCK_UNCHECKED}"
141             ;;
142         dinstall)
143             ;;
144         hourly)
145             ;;
146         daily)
147             ;;
148         weekly)
149             ;;
150         monthly)
151             ;;
152         yearly)
153             ;;
154         *)
155             error "Unknown arg ${ARG}"
156             exit 42
157             ;;
158     esac
159
160     # An easy access by name for the current log
161     ln -sf ${LOGFILE} ${logdir}/${PROGRAM}
162
163     # And from here, all output to the log please
164     exec >> "$LOGFILE" 2>&1
165
166     # The stage function uses this directory
167     # This amends the stagedir variable from "vars"
168     stagedir="${stagedir}/${PROGRAM}"
169     # Ensure the dir exists
170     mkdir -p ${stagedir}
171
172     # This loop simply wants to be fed by a list of values (see below)
173     # made out of 5 columns.
174     # The first four are the array values for the stage function, the
175     # fifth tells us if we should background the stage call.
176     #
177     #  - FUNC - the function name to call
178     #  - ARGS - Possible arguments to hand to the function. Can be the empty string
179     #  - TIME - The timestamp name. Can be the empty string
180     #  - ERR  - if this is the string false, then the call will be surrounded by
181     #           set +e ... set -e calls, so errors in the function do not exit
182     #           dinstall. Can be the empty string, meaning true.
183     #  - BG   - Background the function stage?
184     #
185     # ATTENTION: Spaces in arguments or timestamp names need to be escaped by \
186     #
187     # NOTE 1: There are two special values for the first column (FUNC).
188     #         STATE   - do not call stage function, call the state
189     #                   function to update the public statefile "where is dinstall"
190     #         NOSTAGE - do not call stage function, call the command directly.
191     #
192     # Note 2: If you want to hand an empty value to the stage function,
193     #         use the word "none" in the list below.
194     while read FUNC ARGS TIME ERR BACKGROUND; do
195         debug "FUNC: $FUNC ARGS: $ARGS TIME: $TIME ERR: $ERR BG: $BACKGROUND"
196
197         # Empty values in the value list are the string "none" (or the
198         # while read loop won't work). Here we ensure that variables that
199         # can be empty, are empty if the string none is set for them.
200         for var in ARGS TIME; do
201             if [[ ${!var} == none ]]; then
202                 typeset ${var}=''
203             fi
204         done
205
206         # ERR/BACKGROUND are boolean, check that they are.
207         for var in ERR BACKGROUND; do
208             if [[ ${!var} != false ]] && [[ ${!var} != true ]]; then
209                 error "Illegal value ${!var} for ${var} (should be true or false), line for function ${FUNC}"
210             fi
211         done
212
213         case ${FUNC} in
214             STATE)
215                 state ${ARGS}
216                 ;;
217             NOSTAGE)
218                 ${ARGS}
219                 ;;
220             *)
221                 GO=(
222                     FUNC=${FUNC}
223                     TIME=${TIME}
224                     ARGS=${ARGS}
225                     ERR=${ERR}
226                 )
227                 if [[ ${BACKGROUND} == true ]]; then
228                     stage $GO &
229                 else
230                     stage $GO
231                 fi
232                 ;;
233         esac
234     done < <(grep -v '^#' ${TASKLIST} )
235
236     # we need to wait for the background processes before the end of the cron script
237     wait
238
239
240     # Common to all cron scripts
241     log "Cron script successful, all done"
242     # Redirect output to another file, as we want to compress our logfile
243     # and ensure its no longer used
244     exec > "$logdir/after${PROGRAM}.log" 2>&1
245
246     case ${ARG} in
247         unchecked)
248         ;;
249         dinstall)
250             logstats ${LOGFILE}
251             state "all done"
252             touch "${DINSTALLEND}"
253             ;;
254         hourly)
255             ;;
256         daily)
257             ;;
258         weekly)
259             ;;
260         monthly)
261             ;;
262         yearly)
263             ;;
264     esac
265
266     # Now, at the very (successful) end of this run, make sure we remove
267     # our stage files, so the next dinstall run will do it all again.
268     rm -f ${stagedir}/*
269     bzip2 -9 ${LOGFILE}
270
271     # Logfile should be gone, remove the symlink
272     [[ -L ${logdir}/${PROGRAM} ]] && [[ ! -f ${logdir}/${PROGRAM} ]] && rm -f ${logdir}/${PROGRAM} || log "Logfile still exists or symlink gone already? Something fishy going on"
273
274     # FIXME: Mail the log when its non-empty
275     [[ -s "${logdir}/after${PROGRAM}.log" ]] || rm "${logdir}/after${PROGRAM}.log"
276
277 # And end the reboot-locked part
278 ) 42</var/run/reboot-lock