Isolate ftpsync into its own branch.
[mirror-admin] / bin / websync
CommitLineData
57ab47fa
MW
1#! /bin/bash
2# No, we can not deal with sh alone.
3
4set -e
5set -u
6# ERR traps should be inherited from functions too. (And command
7# substitutions and subshells and whatnot, but for us the function is
8# the important part here)
9set -E
10
11# websync script for Debian
12# Based losely on the old websync written by an
13# unknown number of different people over the years and ftpsync.
14#
15# Copyright (C) 2008,2009 Joerg Jaspert <joerg@debian.org>
16#
17# This program is free software; you can redistribute it and/or
18# modify it under the terms of the GNU General Public License as
19# published by the Free Software Foundation; version 2.
20#
21# This program is distributed in the hope that it will be useful, but
22# WITHOUT ANY WARRANTY; without even the implied warranty of
23# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24# General Public License for more details.
25#
26# You should have received a copy of the GNU General Public License
27# along with this program; if not, write to the Free Software
28# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29
30# In case the admin somehow wants to have this script located someplace else,
31# he can set BASEDIR, and we will take that. If it is unset we take ${HOME}
32# How the admin sets this isn't our place to deal with. One could use a wrapper
33# for that. Or pam_env. Or whatever fits in the local setup. :)
34BASEDIR=${BASEDIR:-"${HOME}"}
35
36# Script version. DO NOT CHANGE, *unless* you change the master copy maintained
37# by Joerg Jaspert and the Debian mirroradm group.
38# This is used to track which mirror is using which script version.
39VERSION="0815"
40
41# Source our common functions
42. "${BASEDIR}/etc/common"
43
44########################################################################
45########################################################################
46## functions ##
47########################################################################
48########################################################################
49# All the stuff we want to do when we exit, no matter where
50cleanup() {
51 trap - ERR TERM HUP INT QUIT EXIT
52 # all done. Mail the log, exit.
53 log "Mirrorsync done";
54 if [ -n "${MAILTO}" ]; then
55 # In case rsync had something on stderr
56 if [ -s "${LOGDIR}/rsync-${NAME}.error" ]; then
57 mail -e -s "[${PROGRAM}@$(hostname -s)] ($$) rsync ERROR on $(date +"%Y.%m.%d-%H:%M:%S")" ${MAILTO} < "${LOGDIR}/rsync-${NAME}.error"
58 fi
59 if [ "x${ERRORSONLY}x" = "xfalsex" ]; then
60 # And the normal log
61 MAILFILES="${LOG}"
62 if [ "x${FULLLOGS}x" = "xtruex" ]; then
63 # Someone wants full logs including rsync
64 MAILFILES="${MAILFILES} ${LOGDIR}/rsync-${NAME}.log"
65 fi
66 cat ${MAILFILES} | mail -e -s "[${PROGRAM}@$(hostname -s)] web sync finished on $(date +"%Y.%m.%d-%H:%M:%S")" ${MAILTO}
67 fi
68 fi
69
70 savelog "${LOGDIR}/rsync-${NAME}.log"
71 savelog "${LOGDIR}/rsync-${NAME}.error"
72 savelog "$LOG" > /dev/null
73
74 rm -f "${LOCK}"
75}
76
77
78# Check rsyncs return value
79check_rsync() {
80
81 ret=$1
82 msg=$2
83
84 # 24 - vanished source files. Ignored, that should be the target of $UPDATEREQUIRED
85 # and us re-running. If it's not, uplink is broken anyways.
86 case "${ret}" in
87 0) return 0;;
88 24) return 0;;
89 23) return 2;;
90 30) return 2;;
91 *)
92 error "ERROR: ${msg}"
93 return 1
94 ;;
95 esac
96}
97
98########################################################################
99########################################################################
100
101# As what are we called?
102NAME="`basename $0`"
103
104# Now source the config.
105. "${BASEDIR}/etc/${NAME}.conf"
106
107########################################################################
108# Config options go here. Feel free to overwrite them in the config #
109# file if you need to. #
110# On debian.org machines the defaults should be ok. #
111########################################################################
112
113########################################################################
114# There should be nothing to edit here, use the config file #
115########################################################################
116MIRRORNAME=${MIRRORNAME:-`hostname -f`}
117# Where to put logfiles in
118LOGDIR=${LOGDIR:-"${BASEDIR}/log"}
119# Our own logfile
120LOG=${LOG:-"${LOGDIR}/${NAME}.log"}
121
122# Where should we put all the mirrored files?
123TO=${TO:-"/org/www.debian.org/www"}
124
125# used by log() and error()
126PROGRAM=${PROGRAM:-"${NAME}-$(hostname -s)"}
127
128# Where to send mails about mirroring to?
129if [ "x$(hostname -d)x" != "xdebian.orgx" ]; then
130 # We are not on a debian.org host
131 MAILTO=${MAILTO:-"root"}
132else
133 # Yay, on a .debian.org host
134 MAILTO=${MAILTO:-"mirrorlogs@debian.org"}
135fi
136# Want errors only or every log?
137ERRORSONLY=${ERRORSONLY:-"true"}
138# Want full logs, ie. including the rsync one?
139FULLLOGS=${FULLLOGS:-"false"}
140
141# How many logfiles to keep
142LOGROTATE=${LOGROTATE:-14}
143
144# Our lockfile
145LOCK=${LOCK:-"${TO}/Website-Update-in-Progress-${MIRRORNAME}"}
146# Do we need another rsync run?
147UPDATEREQUIRED="${TO}/Website-Update-Required-${MIRRORNAME}"
148# Trace file for mirror stats and checks (make sure we get full hostname)
149TRACE=${TRACE:-".project/trace/${MIRRORNAME}"}
150
151# rsync program
152RSYNC=${RSYNC:-rsync}
153# Rsync filter rules. Used to protect various files we always want to keep, even if we otherwise delete
154# excluded files
155RSYNC_FILTER=${RSYNC_FILTER:-"--filter=protect_Website-Update-in-Progress-${MIRRORNAME} --filter=protect_${TRACE} --filter=protect_Website-Update-Required-${MIRRORNAME}"}
156# Default rsync options for *every* rsync call
157RSYNC_OPTIONS=${RSYNC_OPTIONS:-"-prltvHSB8192 --timeout 3600 --stats ${RSYNC_FILTER}"}
158RSYNC_OPTIONS2=${RSYNC_OPTIONS2:-"--max-delete=40000 --delay-updates --delete --delete-after --delete-excluded"}
159# Which rsync share to use on our upstream mirror?
160RSYNC_PATH=${RSYNC_PATH:-"web.debian.org"}
161
162# our username for the rsync share
163RSYNC_USER=${RSYNC_USER:-""}
164# the password
165RSYNC_PASSWORD=${RSYNC_PASSWORD:-""}
166
167# a possible proxy
168RSYNC_PROXY=${RSYNC_PROXY:-""}
169
170# General excludes.
171EXCLUDE=${EXCLUDE:-"--exclude ${HOSTNAME}"}
172
173# The temp directory used by rsync --delay-updates is not
174# world-readable remotely. Always exclude it to avoid errors.
175EXCLUDE="${EXCLUDE} --exclude .~tmp~/"
176
177# And site specific excludes, by default its the sponsor stuff that should be local to all (except templates)
178SITE_FILTER=${SITE_FILTER:-"--include sponsor.deb.* --exclude sponsor_img.* --exclude sponsor.html --exclude sponsor.*.html --filter=protect_sponsor_img.* --filter=protect_sponsor.html --filter=protect_sponsor.*.html"}
179
180# Hooks
181HOOK1=${HOOK1:-""}
182HOOK2=${HOOK2:-""}
183HOOK3=${HOOK3:-""}
184HOOK4=${HOOK4:-""}
185
186# Are we a hub?
187HUB=${HUB:-"false"}
188
189# Some sane defaults
190cd "${BASEDIR}"
191umask 022
192
193# If we are here for the first time, create the
194# destination and the trace directory
195mkdir -p "${TO}/.project/trace"
196
197# Used to make sure we will have the archive fully and completly synced before
198# we stop, even if we get multiple pushes while this script is running.
199# Otherwise we can end up with a half-synced archive:
200# - get a push
201# - sync, while locked
202# - get another push. Of course no extra sync run then happens, we are locked.
203# - done. Archive not correctly synced, we don't have all the changes from the second push.
204touch "${UPDATEREQUIRED}"
205
206# Check to see if another sync is in progress
207if ! ( set -o noclobber; echo "$$" > "${LOCK}") 2> /dev/null; then
208 if ! $(kill -0 $(cat ${LOCK}) 2>/dev/null); then
209 # Process does either not exist or is not owned by us.
210 echo "$$" > "${LOCK}"
211 else
212 echo "Unable to start rsync, lock file still exists, PID $(cat ${LOCK})"
213 exit 1
214 fi
215fi
216
217trap cleanup EXIT ERR TERM HUP INT QUIT
218
219# Start log by redirecting everything there.
220exec >"$LOG" 2>&1 </dev/null
221
222# Look who pushed us and note that in the log.
223log "Mirrorsync start"
224PUSHFROM="${SSH_CONNECTION%%\ *}"
225if [ -n "${PUSHFROM}" ]; then
226 log "We got pushed from ${PUSHFROM}"
227fi
228log "Acquired main lock"
229
230HOOK=(
231 HOOKNR=1
232 HOOKSCR=${HOOK1}
233)
234hook $HOOK
235
236# Now, we might want to sync from anonymous too.
237# This is that deep in this script so hook1 could, if wanted, change things!
238if [ -z ${RSYNC_USER} ]; then
239 RSYNCPTH="${RSYNC_HOST}"
240else
241 RSYNCPTH="${RSYNC_USER}@${RSYNC_HOST}"
242fi
243
244# Now do the actual mirroring, and run as long as we have an updaterequired file.
245export RSYNC_PASSWORD
246export RSYNC_PROXY
247
248while [ -e "${UPDATEREQUIRED}" ]; do
249 log "Running mirrorsync, update is required, ${UPDATEREQUIRED} exists"
250
251 rm -f "${UPDATEREQUIRED}"
252 log "Syncing: ${RSYNC} ${RSYNC_OPTIONS} ${RSYNC_OPTIONS2} ${EXCLUDE} ${SITE_FILTER} ${RSYNCPTH}::${RSYNC_PATH} ${TO}"
253
254 set +e
255 ${RSYNC} ${RSYNC_OPTIONS} ${RSYNC_OPTIONS2} ${EXCLUDE} ${SITE_FILTER} \
256 ${RSYNCPTH}::${RSYNC_PATH} "${TO}" >"${LOGDIR}/rsync-${NAME}.log" 2>"${LOGDIR}/rsync-${NAME}.error"
257 result=$?
258 set -e
259
260 log "Back from rsync with returncode ${result}"
261
262 set +e
263 check_rsync $result "Sync went wrong, got errorcode ${result}. Logfile: ${LOG}"
264 GO=$?
265 set -e
266
267 if [ ${GO} -eq 2 ] && [ -e "${UPDATEREQUIRED}" ]; then
268 log "We got error ${result} from rsync, but a second push went in hence ignoring this error for now"
269 elif [ ${GO} -ne 0 ]; then
270 exit 3
271 fi
272
273 HOOK=(
274 HOOKNR=2
275 HOOKSCR=${HOOK2}
276 )
277 hook $HOOK
278
279done
280
281mkdir -p "${TO}/.project/trace"
282LC_ALL=POSIX LANG=POSIX date -u > "${TO}/${TRACE}"
283echo "Used websync version: ${VERSION}" >> "${TO}/${TRACE}"
284echo "Running on host: $(hostname -f)" >> "${TO}/${TRACE}"
285
286HOOK=(
287 HOOKNR=3
288 HOOKSCR=${HOOK3}
289)
290hook $HOOK
291
292if [ x${HUB} = "xtrue" ]; then
293 log "Trigger slave mirrors"
294 ${BASEDIR}/bin/runmirrors "websync"
295 log "Trigger slave done"
296
297 HOOK=(
298 HOOKNR=4
299 HOOKSCR=${HOOK4}
300 )
301 hook $HOOK
302fi
303
304# All done, rest is done by cleanup hook.