1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
|
#!/usr/bin/env bash
# runmirrors script for Debian
# Based losely on existing scripts, written by an unknown number of
# different people over the years.
#
# Copyright (C) 2008-2016 Joerg Jaspert <joerg@debian.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
set -e
set -u
set -o pipefail
BINDIR=$(dirname $(readlink -f "$0")); . "${BINDIR}/include-git" ## INCLUDE COMMON
NAME="$(basename $0)"
HELP="$0\n
Usage:\n\n
1.) a single parameter with NO leading -.\n
\t This will will then be used as the addition for our configfile. Ie. \`$0 security\` will\n
\t have us look for ${NAME}-security.{conf,mirror} files.\n\n
2.) using getopt style parameters:\n
\t -a [NAME] - Same as 1.) above, used for the config files. Default empty.\n
\t -k [TYPE] - Type of push. all, stage2, mhop. Default mhop.\n
\t -f - Run from within the mirrorscript ftpsync. Don't use from commandline!\n
\t -h - Print this help and exit
"
# If we got options, lets see if we use newstyle options, or oldstyle. If oldstyle
# it will not start with a -. If we find oldstyle we assume its only one, the config
# name we run on.
if [[ $# -gt 0 ]]; then
if [[ ${1:0:1} != - ]]; then
# Yes, does not start with a -, so use it for the config name.
CONF=${1:-""}
if [[ -n ${CONF} ]]; then
NAME="${NAME}-${CONF}"
fi
else
# Yeah well, new style, starting with - for getopts
while getopts ':a:k:fh' OPTION ; do
case $OPTION in
a) CONF="${OPTARG}"
if [[ -n ${CONF} ]]; then
NAME="${NAME}-${CONF}"
fi
;;
k) PUSHKIND="${OPTARG}"
;;
f) FROMFTPSYNC="true"
;;
h) echo -e $HELP
exit 0
;;
*) echo "Invalid usage"
echo -e $HELP
exit 1
;;
esac
done
fi
fi
# Make sure the values are always defined, even if there was no commandline option
# for them
# Default config is empty
CONF=${CONF:-""}
# Set the default to all, if we didnt get told about it. Currently
# valid: all - normal push. mhop - multi-hop multi-stage push, this is stage1,
# stage2 - staged push, second phase. Default is mhop.
PUSHKIND=${PUSHKIND:-"mhop"}
# If we are pushed from within ftpsync. Default false.
FROMFTPSYNC=${FROMFTPSYNC:-"false"}
########################################################################
# Read our config file
read_config "${NAME}.conf"
# Make sure we have our log and lock directories
create_logdir
create_lockdir
########################################################################
# Config defaults #
########################################################################
MAILTO=${MAILTO:-${LOGNAME:?Environment variable LOGNAME unset, please check your system or specify MAILTO}}
KEYFILE=${KEYFILE:-".ssh/pushmirror"}
# Log options
LOG=${LOG:-"${LOGDIR}/${NAME}.log"}
LOGROTATE=${LOGROTATE:-14}
# Other options
MIRRORS=${MIRRORS:-$(search_config "${NAME}.mirror")}
SSH_OPTS=${SSH_OPTS:-"-o StrictHostKeyChecking=no"}
PUSHARCHIVE=${PUSHARCHIVE:-"${CONF}"}
PUSHDELAY=${PUSHDELAY:-600}
# Hooks
HOOK1=${HOOK1:-""}
HOOK2=${HOOK2:-""}
HOOK3=${HOOK3:-""}
########################################################################
########################################################################
# Which ssh key to use?
KEYFILE_ABS=$(cd && readlink -f "$KEYFILE" || :)
# used by log()
PROGRAM=${PROGRAM:-"${NAME}"}
REGEX_URL='^([a-z.+]+)://(([^@/]+)@)?([^/]+)(/.*)?$'
# start a new log
savelog "${LOG}" > /dev/null
if ! [[ -f ${KEYFILE_ABS} ]]; then
error "SSH Key ${KEYFILE} does not exist" >> "${LOG}"
exit 5
fi
# Hooks
########################################################################
# Some sane defaults
cd "${BASEDIR}"
umask 022
open_log $LOG
trap 'log "Mirrorpush done"' EXIT
log "Pushing leaf mirrors. Inside ftpsync: ${FROMFTPSYNC}. Pushkind: ${PUSHKIND}"
HOOK=(
HOOKNR=1
HOOKSCR=${HOOK1}
)
hook $HOOK
# From here on we do *NOT* want to exit on errors. We don't want to
# stop pushing mirrors just because we can't reach one of them.
set +e
# Built up our list of 2-stage mirrors.
PUSHLOCKS=""
PUSHLOCKS=$(get2stage)
# In case we have it - remove. It is used to synchronize multi-stage mirroring
rm -f "${LOCKDIR}/all_stage1"
# Now read our mirrorfile and push the mirrors defined in there.
# We use grep to easily sort out all lines having a # in front of them or are empty.
egrep -vs '^[[:space:]]*(#|$)' "${MIRRORS}" |
while read MTYPE MLNAME MHOSTNAME MUSER MSSHOPT; do
if [[ ${MTYPE} = DELAY ]]; then
# We should wait a bit.
if [ -z ${MLNAME} ]; then
MLNAME=600
fi
if [ "${MHOSTNAME:-}" = "stage1" ]; then
log "Delay of ${MLNAME} or until staging is finished requested"
if ! wait_for_pushlocks ${MLNAME}; then
log "Staged delay ran into timeout of ${MLNAME} seconds"
else
log "Staged delay finished"
fi
else
log "Delay of ${MLNAME} requested, sleeping"
sleep ${MLNAME}
fi
continue
fi
# If we are told we have a mhop sync to do and are called from within ftpsync,
# we will only look at staged/mhop entries and ignore the rest.
if [[ ${PUSHKIND} = mhop ]] && [[ ${FROMFTPSYNC} = true ]]; then
if [[ ${MTYPE} != staged ]] && [[ ${MTYPE} != mhop ]]; then
continue
fi
fi
MPROTO=2
MKEYFILE="${KEYFILE_ABS}"
SSHOPT=""
MPUSHARCHIVE=${PUSHARCHIVE}
if [[ $MHOSTNAME =~ $REGEX_URL ]]; then
MSCHEME=${BASH_REMATCH[1]}
MUSER=${BASH_REMATCH[3]}
MHOSTNAME=${BASH_REMATCH[4]}
URLPATH=${BASH_REMATCH[5]}
if [[ $MSCHEME == ssh+ftpsync ]]; then
if [[ $URLPATH ]]; then
MPUSHARCHIVE=${URLPATH#/}
fi
else
log "Trigger ${MLNAME}: ${MHOSTNAME} have wrong scheme, ignoring"
continue
fi
# Now, MSSHOPT may start with a -. In that case the whole rest of the line is taken
# as a set of options to give to ssh, we pass it without doing anything with it.
# If it starts with a 1 or 2 then it will tell us about the ssh protocol version to use,
# and also means we look if there is one value more after a space. That value would then
# be the ssh keyfile we use with -i. That gives us full flexibility for all
# ssh options but doesn't destroy backwards compatibility.
# If it is empty we assume proto 2 and the default keyfile.
#
# There is one bug in here. We will give out the master keyfile, even if there is a
# "-i /bla/bla" in the options. ssh stuffs them together and presents two keys to the
# target server. In the case both keys do some actions- the first one presented wins.
# And this might not be what one wants.
#
# The only sane way to go around this, i think, is by dropping backward compability.
# Which I don't really like.
elif [[ -n ${MSSHOPT} ]]; then
# So its not empty, lets check if it starts with a - and as such is a "new-style"
# ssh options set.
if [[ ${MSSHOPT:0:1} = - ]]; then
# Yes we start with a -
SSHOPT="${MSSHOPT}"
MPROTO="99"
elif [[ ${MSSHOPT:0:1} -eq 1 ]] || [[ ${MSSHOPT:0:1} -eq 2 ]]; then
# We do seem to have oldstyle options here.
MPROTO=${MSSHOPT:0:1}
MKEYFILE=${MSSHOPT:2}
else
error "I don't know what is configured for mirror ${MLNAME}"
continue
fi
fi
# Built our array
SIGNAL_OPTS=(
MIRROR="${MLNAME}"
HOSTNAME="${MHOSTNAME}"
USERNAME="${MUSER}"
SSHPROTO="${MPROTO}"
SSHKEY="${MKEYFILE}"
SSHOPTS="${SSHOPT/ /#}"
PUSHLOCKOWN="${LOCKDIR}/${MLNAME}.stage1"
PUSHTYPE="${MTYPE}"
PUSHARCHIVE=${MPUSHARCHIVE}
PUSHKIND=${PUSHKIND}
FROMFTPSYNC=${FROMFTPSYNC}
)
# And finally, push the mirror
log "Trigger ${MLNAME}"
signal "${SIGNAL_OPTS}" &
log "Trigger for ${MLNAME} done"
HOOK=(
HOOKNR=2
HOOKSCR=${HOOK2}
)
hook $HOOK
set +e
done
# If we are run from within ftpsync *and* have an mhop push to send on, we have
# to wait until the push is gone through and they all returned, or we will exit
# much too early.
# As the signal routine touches $LOCKDIR/all_stage1 when all are done, its
# easy enough just to wait for that to appear. Of course we do the same game
# with PUSHDELAY to not wait forever.
if [[ true = ${FROMFTPSYNC} ]] && [[ mhop = ${PUSHKIND} ]]; then
tries=0
# We do not wait forever
while [[ ${tries} -lt ${PUSHDELAY} ]]; do
if [[ -f ${LOCKDIR}/all_stage1 ]]; then
break
fi
tries=$(( tries + 5 ))
sleep 5
done
if [[ ${tries} -ge ${PUSHDELAY} ]]; then
error "Failed to wait for our mirrors when sending mhop push down." >> "${LOG}"
fi
fi
HOOK=(
HOOKNR=3
HOOKSCR=${HOOK3}
)
hook $HOOK
exit 0
|