2 # Copyright 1999-2013 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4 # Author: Karl Trygve Kalleberg <karltk@gentoo.org>
5 # Rewritten from the old, Perl-based emerge-webrsync script
6 # Author: Alon Bar-Lev <alon.barlev@gmail.com>
7 # Major rewrite from Karl's scripts.
10 # - all output should prob be converted to e* funcs
11 # - add support for ROOT
16 # gpg --homedir /etc/portage/gnupg --keyserver subkeys.pgp.net --recv-keys $KEY_ID
17 # gpg --homedir /etc/portage/gnupg --edit-key $KEY_ID trust
20 # Only echo if in verbose mode
21 vvecho() { [[ ${do_verbose} -eq 1 ]] && echo "$@" ; }
22 # Only echo if not in verbose mode
23 nvecho() { [[ ${do_verbose} -eq 0 ]] && echo "$@" ; }
25 wecho() { echo "${argv0##*/}: warning: $*" 1>&2 ; }
27 eecho() { echo "${argv0##*/}: error: $*" 1>&2 ; }
31 # Use portageq from the same directory/prefix as the current script, so
32 # that we don't have to rely on PATH including the current EPREFIX.
33 scriptpath=${BASH_SOURCE[0]}
34 if [ -x "${scriptpath%/*}/portageq" ]; then
35 portageq=${scriptpath%/*}/portageq
36 elif type -P portageq > /dev/null ; then
39 eecho "could not find 'portageq'; aborting"
42 eval "$("${portageq}" envvar -v DISTDIR EPREFIX FEATURES \
43 FETCHCOMMAND GENTOO_MIRRORS \
44 PORTAGE_BIN_PATH PORTAGE_CONFIGROOT PORTAGE_GPG_DIR \
45 PORTAGE_NICENESS PORTAGE_REPOSITORIES PORTAGE_RSYNC_EXTRA_OPTS \
46 PORTAGE_RSYNC_OPTS PORTAGE_TMPDIR \
47 USERLAND http_proxy ftp_proxy)"
48 export http_proxy ftp_proxy
50 source "${PORTAGE_BIN_PATH}"/isolated-functions.sh || exit 1
53 repo_location=$(__repo_key "${repo_name}" location)
54 if [[ -z ${repo_location} ]]; then
55 eecho "Repository '${repo_name}' not found"
58 repo_sync_type=$(__repo_key "${repo_name}" sync-type)
60 # If PORTAGE_NICENESS is overriden via the env then it will
61 # still pass through the portageq call and override properly.
62 if [ -n "${PORTAGE_NICENESS}" ]; then
63 renice $PORTAGE_NICENESS $$ > /dev/null
70 if has webrsync-gpg ${FEATURES} ; then
71 WEBSYNC_VERIFY_SIGNATURE=1
73 WEBSYNC_VERIFY_SIGNATURE=0
75 if [ ${WEBSYNC_VERIFY_SIGNATURE} != 0 -a -z "${PORTAGE_GPG_DIR}" ]; then
76 eecho "please set PORTAGE_GPG_DIR in make.conf"
84 *.xz) decompressor="xzcat" ;;
85 *.bz2) decompressor="bzcat" ;;
86 *.gz) decompressor="zcat" ;;
87 *) decompressor="cat" ;;
89 ${decompressor} "${file}" | tar "$@"
90 _pipestatus=${PIPESTATUS[*]}
91 [[ ${_pipestatus// /} -eq 0 ]]
94 get_utc_date_in_seconds() {
99 local utc_time_in_secs="$1"
102 if [[ ${USERLAND} == BSD ]] ; then
103 date -r ${utc_time_in_secs} -u +"${part}"
105 date -d @${utc_time_in_secs} -u +"${part}"
109 get_utc_second_from_string() {
111 if [[ ${USERLAND} == BSD ]] ; then
112 # Specify zeros for the least significant digits, or else those
113 # digits are inherited from the current system clock time.
114 date -juf "%Y%m%d%H%M.%S" "${s}0000.00" +"%s"
116 date -d "${s:0:4}-${s:4:2}-${s:6:2}" -u +"%s"
120 get_portage_timestamp() {
121 local portage_current_timestamp=0
123 if [ -f "${repo_location}/metadata/timestamp.x" ]; then
124 portage_current_timestamp=$(cut -f 1 -d " " "${repo_location}/metadata/timestamp.x" )
127 echo "${portage_current_timestamp}"
135 if [ "${FETCHCOMMAND/wget/}" != "${FETCHCOMMAND}" ]; then
136 opts="--continue $(nvecho -q)"
137 elif [ "${FETCHCOMMAND/curl/}" != "${FETCHCOMMAND}" ]; then
138 opts="--continue-at - $(nvecho -s -f)"
140 rm -f "${DISTDIR}/${FILE}"
143 __vecho "Fetching file ${FILE} ..."
144 # already set DISTDIR=
145 eval "${FETCHCOMMAND} ${opts}"
146 if [[ $? -eq 0 && -s ${DISTDIR}/${FILE} ]] ; then
149 rm -f "${DISTDIR}/${FILE}"
154 check_file_digest() {
159 __vecho "Checking digest ..."
161 if type -P md5sum > /dev/null; then
162 local md5sum_output=$(md5sum "${file}")
163 local digest_content=$(< "${digest}")
164 [ "${md5sum_output%%[[:space:]]*}" = "${digest_content%%[[:space:]]*}" ] && r=0
165 elif type -P md5 > /dev/null; then
166 [ "$(md5 -q "${file}")" == "$(cut -d ' ' -f 1 "${digest}")" ] && r=0
168 eecho "cannot check digest: no suitable md5/md5sum binaries found"
174 check_file_signature() {
179 if [ ${WEBSYNC_VERIFY_SIGNATURE} != 0 ]; then
181 __vecho "Checking signature ..."
183 if type -P gpg > /dev/null; then
184 gpg --homedir "${PORTAGE_GPG_DIR}" --verify "$signature" "$file" && r=0
186 eecho "cannot check signature: gpg binary not found"
196 get_snapshot_timestamp() {
199 do_tar "${file}" --to-stdout -xf - portage/metadata/timestamp.x | cut -f 1 -d " "
205 __vecho "Syncing local tree ..."
207 local ownership="portage:portage"
208 if has usersync ${FEATURES} ; then
209 case "${USERLAND}" in
211 ownership=$(stat -f '%Su:%Sg' "${repo_location}")
214 ownership=$(stat -c '%U:%G' "${repo_location}")
219 if type -P tarsync > /dev/null ; then
220 local chown_opts="-o ${ownership%:*} -g ${ownership#*:}"
221 chown ${ownership} "${repo_location}" > /dev/null 2>&1 || chown_opts=""
222 if ! tarsync $(vvecho -v) -s 1 ${chown_opts} \
223 -e /distfiles -e /packages -e /local "${file}" "${repo_location}"; then
224 eecho "tarsync failed; tarball is corrupt? (${file})"
228 if ! do_tar "${file}" xf -; then
229 eecho "tar failed to extract the image. tarball is corrupt? (${file})"
235 ${keep} || rm -f "${file}"
237 local rsync_opts="${PORTAGE_RSYNC_OPTS} ${PORTAGE_RSYNC_EXTRA_OPTS}"
238 if chown ${ownership} portage > /dev/null 2>&1; then
239 chown -R ${ownership} portage
240 rsync_opts+=" --owner --group"
243 rsync ${rsync_opts} . "${repo_location%%/}"
246 __vecho "Cleaning up ..."
250 if has metadata-transfer ${FEATURES} ; then
251 __vecho "Updating cache ..."
252 "${PORTAGE_BIN_PATH}/emerge" --metadata
254 local post_sync=${PORTAGE_CONFIGROOT}etc/portage/bin/post_sync
255 [ -x "${post_sync}" ] && "${post_sync}"
256 # --quiet suppresses output if there are no relevant news items
257 has news ${FEATURES} && "${PORTAGE_BIN_PATH}/emerge" --check-news --quiet
262 local ignore_timestamp="$1"
267 local base_file="portage-${date}.tar"
272 local compressions=""
273 # xz is not supported in app-arch/tarsync, so use
274 # bz2 format if we have tarsync.
275 if ! type -P tarsync > /dev/null ; then
276 type -P xzcat > /dev/null && compressions="${compressions} xz"
278 type -P bzcat > /dev/null && compressions="${compressions} bz2"
279 type -P zcat > /dev/null && compressions="${compressions} gz"
280 if [[ -z ${compressions} ]] ; then
281 eecho "unable to locate any decompressors (xzcat or bzcat or zcat)"
285 for mirror in ${GENTOO_MIRRORS} ; do
288 __vecho "Trying to retrieve ${date} snapshot from ${mirror} ..."
290 for compression in ${compressions} ; do
291 local file="portage-${date}.tar.${compression}"
292 local digest="${file}.md5sum"
293 local signature="${file}.gpgsig"
295 if [ -s "${DISTDIR}/${file}" -a -s "${DISTDIR}/${digest}" -a -s "${DISTDIR}/${signature}" ] ; then
296 check_file_digest "${DISTDIR}/${digest}" "${DISTDIR}/${file}" && \
297 check_file_signature "${DISTDIR}/${signature}" "${DISTDIR}/${file}" && \
301 if [ ${have_files} -eq 0 ] ; then
302 fetch_file "${mirror}/snapshots/${digest}" "${digest}" && \
303 fetch_file "${mirror}/snapshots/${signature}" "${signature}" && \
304 fetch_file "${mirror}/snapshots/${file}" "${file}" && \
305 check_file_digest "${DISTDIR}/${digest}" "${DISTDIR}/${file}" && \
306 check_file_signature "${DISTDIR}/${signature}" "${DISTDIR}/${file}" && \
311 # If timestamp is invalid
312 # we want to try and retrieve
313 # from a different mirror
315 if [ ${have_files} -eq 1 ]; then
317 __vecho "Getting snapshot timestamp ..."
318 local snapshot_timestamp=$(get_snapshot_timestamp "${DISTDIR}/${file}")
320 if [ ${ignore_timestamp} == 0 ]; then
321 if [ ${snapshot_timestamp} -lt $(get_portage_timestamp) ]; then
322 wecho "portage is newer than snapshot"
326 local utc_seconds=$(get_utc_second_from_string "${date}")
329 # Check that this snapshot
330 # is what it claims to be ...
332 if [ ${snapshot_timestamp} -lt ${utc_seconds} ] || \
333 [ ${snapshot_timestamp} -gt $((${utc_seconds}+ 2*86400)) ]; then
335 wecho "snapshot timestamp is not in acceptable period"
341 if [ ${have_files} -eq 1 ]; then
345 # Remove files and use a different mirror
347 rm -f "${DISTDIR}/${file}" "${DISTDIR}/${digest}" "${DISTDIR}/${signature}"
351 [ ${have_files} -eq 1 ] && break
354 if [ ${have_files} -eq 1 ]; then
355 sync_local "${DISTDIR}/${file}" && r=0
357 __vecho "${date} snapshot was not found"
360 ${keep} || rm -f "${DISTDIR}/${file}" "${DISTDIR}/${digest}" "${DISTDIR}/${signature}"
364 do_latest_snapshot() {
368 __vecho "Fetching most recent snapshot ..."
370 # The snapshot for a given day is generated at 00:45 UTC on the following
371 # day, so the current day's snapshot (going by UTC time) hasn't been
372 # generated yet. Therefore, always start by looking for the previous day's
373 # snapshot (for attempts=1, subtract 1 day from the current UTC time).
375 # Timestamps that differ by less than 2 hours
376 # are considered to be approximately equal.
377 local min_time_diff=$(( 2 * 60 * 60 ))
379 local existing_timestamp=$(get_portage_timestamp)
380 local timestamp_difference
381 local timestamp_problem
382 local approx_snapshot_time
383 local start_time=$(get_utc_date_in_seconds)
384 local start_hour=$(get_date_part ${start_time} "%H")
386 # Daily snapshots are created at 00:45 and are not
387 # available until after 01:00. Don't waste time trying
388 # to fetch a snapshot before it's been created.
389 if [ ${start_hour} -lt 1 ] ; then
390 (( start_time -= 86400 ))
392 local snapshot_date=$(get_date_part ${start_time} "%Y%m%d")
393 local snapshot_date_seconds=$(get_utc_second_from_string ${snapshot_date})
395 while (( ${attempts} < 40 )) ; do
397 (( snapshot_date_seconds -= 86400 ))
398 # snapshots are created at 00:45
399 (( approx_snapshot_time = snapshot_date_seconds + 86400 + 2700 ))
400 (( timestamp_difference = existing_timestamp - approx_snapshot_time ))
401 [ ${timestamp_difference} -lt 0 ] && (( timestamp_difference = -1 * timestamp_difference ))
402 snapshot_date=$(get_date_part ${snapshot_date_seconds} "%Y%m%d")
405 if [ ${timestamp_difference} -eq 0 ]; then
406 timestamp_problem="is identical to"
407 elif [ ${timestamp_difference} -lt ${min_time_diff} ]; then
408 timestamp_problem="is possibly identical to"
409 elif [ ${approx_snapshot_time} -lt ${existing_timestamp} ] ; then
410 timestamp_problem="is newer than"
413 if [ -n "${timestamp_problem}" ]; then
414 ewarn "Latest snapshot date: ${snapshot_date}"
416 ewarn "Approximate snapshot timestamp: ${approx_snapshot_time}"
417 ewarn " Current local timestamp: ${existing_timestamp}"
419 echo -e "The current local timestamp" \
420 "${timestamp_problem} the" \
421 "timestamp of the latest" \
422 "snapshot. In order to force sync," \
423 "use the --revert option or remove" \
424 "the timestamp file located at" \
425 "'${repo_location}/metadata/timestamp.x'." | fmt -w 70 | \
426 while read -r line ; do
433 if do_snapshot 0 "${snapshot_date}"; then
447 --revert=yyyymmdd Revert to snapshot
448 -k, --keep Keep snapshots in DISTDIR (don't delete)
449 -q, --quiet Only output errors
450 -v, --verbose Enable verbose output
451 -x, --debug Enable debug output
452 -h, --help This help screen (duh!)
454 if [[ -n $* ]] ; then
455 printf "\nError: %s\n" "$*" 1>&2
470 -k|--keep) keep=true ;;
471 -q|--quiet) PORTAGE_QUIET=1 ;;
472 -v|--verbose) do_verbose=1 ;;
473 -x|--debug) do_debug=1 ;;
474 --revert=*) revert_date=${v} ;;
475 *) usage "Invalid option '${arg}'" ;;
479 [[ -d ${repo_location} ]] || mkdir -p "${repo_location}"
480 if [[ ! -w ${repo_location} ]] ; then
481 eecho "Repository '${repo_name}' is not writable: ${repo_location}"
485 [[ -d ${PORTAGE_TMPDIR}/portage ]] || mkdir -p "${PORTAGE_TMPDIR}/portage"
486 TMPDIR=$(mktemp -d "${PORTAGE_TMPDIR}/portage/webrsync-XXXXXX")
487 if [[ ! -w ${TMPDIR} ]] ; then
488 eecho "TMPDIR is not writable: ${TMPDIR}"
491 trap 'cd / ; rm -rf "${TMPDIR}"' EXIT
492 cd "${TMPDIR}" || exit 1
494 ${keep} || DISTDIR=${TMPDIR}
495 [ ! -d "${DISTDIR}" ] && mkdir -p "${DISTDIR}"
497 if ${keep} && [[ ! -w ${DISTDIR} ]] ; then
498 eecho "DISTDIR is not writable: ${DISTDIR}"
502 # This is a sanity check to help prevent people like funtoo users
503 # from accidentally wiping out their git tree.
504 if [[ -n ${repo_sync_type} && ${repo_sync_type} != rsync ]] ; then
505 echo "The current sync-type attribute of repository 'gentoo' is not set to 'rsync':" >&2
507 echo " sync-type=${repo_sync_type}" >&2
509 echo "If you intend to use emerge-webrsync then please" >&2
510 echo "adjust sync-type and sync-uri attributes to refer to rsync." >&2
511 echo "emerge-webrsync exiting due to abnormal sync-type setting." >&2
515 [[ ${do_debug} -eq 1 ]] && set -x
517 if [[ -n ${revert_date} ]] ; then
518 do_snapshot 1 "${revert_date}"