| #!/usr/bin/env bash |
| # Copyright 1999-2023 Gentoo Authors |
| # Distributed under the terms of the GNU General Public License v2 |
| # Author: Karl Trygve Kalleberg <karltk@gentoo.org> |
| # Rewritten from the old, Perl-based emerge-webrsync script |
| # Author: Alon Bar-Lev <alon.barlev@gmail.com> |
| # Major rewrite from Karl's scripts. |
| |
| # TODO: |
| # - all output should prob be converted to e* funcs |
| # - add support for ROOT |
| |
| # repos.conf configuration for use with emerge --sync and emaint sync |
| # using keyring from app-crypt/openpgp-keys-gentoo-release: |
| # [gentoo] |
| # sync-type = webrsync |
| # sync-webrsync-verify-signature = true |
| # sync-openpgp-key-path = /usr/share/openpgp-keys/gentoo-release.asc |
| # |
| # Alternative (legacy) PORTAGE_GPG_DIR configuration: |
| # gpg key import |
| # KEY_ID=0x96D8BF6D |
| # gpg --homedir /etc/portage/gnupg --keyserver subkeys.pgp.net --recv-keys ${KEY_ID} |
| # gpg --homedir /etc/portage/gnupg --edit-key ${KEY_ID} trust |
| # |
| |
| # Opportunistically use gentoo-functions for nicer output |
| functions_script="${EPREFIX}/lib/gentoo/functions.sh" |
| source "${functions_script}" || { |
| echo "${argv0}: Could not source ${functions_script}!" 1>&2 |
| |
| ebegin() { |
| printf '%s*%s %s ... ' "${GOOD}" "${NORMAL}" "$*" |
| } |
| |
| eend() { |
| local r=${1:-0} |
| shift |
| if [[ $r -eq 0 ]] ; then |
| printf '[ %sok%s ]\n' "${GOOD}" "${NORMAL}" |
| else |
| printf '%s [ %s!!%s ]\n' "$*" "${BAD}" "${NORMAL}" |
| fi |
| return "${r}" |
| } |
| |
| einfo() { |
| echo "${argv0##*/}: $*" |
| } |
| |
| ewarn() { |
| echo "${argv0##*/}: warning: $*" 1>&2 |
| } |
| |
| eerror() { |
| echo "${argv0##*/}: error: $*" 1>&2 |
| } |
| |
| } |
| |
| # Only echo if in normal mode |
| vvecho() { [[ ${PORTAGE_QUIET} != 1 ]] && echo "$@" ; } |
| # Only echo if in quiet mode |
| nvecho() { [[ ${PORTAGE_QUIET} == 1 ]] && echo "$@" ; } |
| |
| # Unfortunately, gentoo-functions doesn't yet have a die() (bug #878505) |
| die() { |
| eerror "$@" |
| exit 1 |
| } |
| |
| argv0=$0 |
| |
| # Use emerge and portageq from the same directory/prefix as the current script, |
| # so that we don't have to rely on PATH including the current EPREFIX. |
| emerge=$(PATH="${BASH_SOURCE[0]%/*}:${PATH}" type -P emerge) |
| [[ -n ${emerge} ]] || die "could not find 'emerge'; aborting" |
| portageq=$(PATH="${BASH_SOURCE[0]%/*}:${PATH}" type -P portageq) |
| [[ -n ${portageq} ]] || die "could not find 'portageq'; aborting" |
| |
| eval "$("${portageq}" envvar -v DISTDIR EPREFIX FEATURES \ |
| FETCHCOMMAND GENTOO_MIRRORS \ |
| PORTAGE_BIN_PATH PORTAGE_CONFIGROOT PORTAGE_GPG_DIR \ |
| PORTAGE_NICENESS PORTAGE_REPOSITORIES PORTAGE_RSYNC_EXTRA_OPTS \ |
| PORTAGE_RSYNC_OPTS PORTAGE_TEMP_GPG_DIR PORTAGE_TMPDIR \ |
| USERLAND http_proxy https_proxy ftp_proxy)" |
| export http_proxy https_proxy ftp_proxy |
| |
| source "${PORTAGE_BIN_PATH}"/isolated-functions.sh || exit 1 |
| |
| repo_name=gentoo |
| repo_location=$(__repo_attr "${repo_name}" location) |
| if [[ -z ${repo_location} ]]; then |
| die "Repository '${repo_name}' not found" |
| fi |
| repo_sync_type=$(__repo_attr "${repo_name}" sync-type) |
| |
| # If PORTAGE_NICENESS is overriden via the env then it will |
| # still pass through the portageq call and override properly. |
| if [[ -n "${PORTAGE_NICENESS}" ]]; then |
| renice "${PORTAGE_NICENESS}" $$ > /dev/null |
| fi |
| |
| do_debug=0 |
| keep=false |
| |
| handle_pgp_setup() { |
| # WEBRSYNC_VERIFY_SIGNATURE=0: disable PGP verification |
| # WEBRSYNC_VERIFY_SIGNATURE=1: use gemato for verification, fallback to regular gpg |
| # WEBRSYNC_VERIFY_SIGNATURE=2: use legacy FEATURES="webrsync-gpg" |
| WEBRSYNC_VERIFY_SIGNATURE=1 |
| |
| has webrsync-gpg ${FEATURES} && webrsync_gpg=1 || webrsync_gpg=0 |
| |
| repo_has_webrsync_verify=$( |
| has $(__repo_attr "${repo_name}" sync-webrsync-verify-signature | LC_ALL=C tr '[:upper:]' '[:lower:]') true yes |
| ) |
| |
| if [[ -n ${PORTAGE_TEMP_GPG_DIR} ]] || [[ ${repo_has_webrsync_verify} -eq 1 ]]; then |
| # If FEATURES=webrsync-gpg is enabled then allow direct emerge-webrsync |
| # calls for backward compatibility (this triggers a deprecation warning |
| # above). Since direct emerge-webrsync calls do not use gemato for secure |
| # key refresh, this behavior will not be supported in a future release. |
| if [[ ! ( -d ${PORTAGE_GPG_DIR} && ${webrsync_gpg} -eq 1 ) && -z ${PORTAGE_TEMP_GPG_DIR} ]]; then |
| die "Do not call ${argv0##*/} directly, instead call emerge --sync or emaint sync." |
| fi |
| |
| # Use gemato for the standard Portage-calling-us case w/ sync-type='webrsync'. |
| WEBRSYNC_VERIFY_SIGNATURE=1 |
| elif [[ ${webrsync_gpg} -eq 1 ]]; then |
| # We only warn if FEATURES="webrsync-gpg" is in make.conf, not if |
| # Portage is calling us for 'sync-type=webrsync' with verification, because |
| # that path uses gemato now (plus the user can't help it, obviously). |
| ewarn "FEATURES=webrsync-gpg is deprecated, see the make.conf(5) man page." |
| WEBRSYNC_VERIFY_SIGNATURE=2 |
| elif [[ -n ${no_pgp_verify} ]]; then |
| WEBRSYNC_VERIFY_SIGNATURE=0 |
| else |
| # The default at the beginning of handle_pgp_setup is WEBRSYNC_VERIFY_SIGNATURE=1 |
| # i.e. gemato. |
| :; |
| fi |
| |
| case "${WEBRSYNC_VERIFY_SIGNATURE}" in |
| 0) |
| [[ ${PORTAGE_QUIET} -eq 1 ]] || ewarn "PGP verification method: disabled" |
| ;; |
| 1) |
| [[ ${PORTAGE_QUIET} -eq 1 ]] || einfo "PGP verification method: gemato" |
| ;; |
| 2) |
| ewarn "PGP verification method: legacy gpg path" |
| ;; |
| *) |
| die "Unknown WEBRSYNC_VERIFY_SIGNATURE state: \${WEBRSYNC_VERIFY_SIGNATURE}=${WEBRSYNC_VERIFY_SIGNATURE}" |
| ;; |
| esac |
| |
| if [[ -n ${PORTAGE_TEMP_GPG_DIR} ]]; then |
| PORTAGE_GPG_DIR=${PORTAGE_TEMP_GPG_DIR} |
| fi |
| |
| if [[ ${WEBRSYNC_VERIFY_SIGNATURE} == 2 && -z "${PORTAGE_GPG_DIR}" ]]; then |
| die "Please set PORTAGE_GPG_DIR in make.conf!" |
| fi |
| } |
| |
| do_tar() { |
| local file=$1 |
| shift |
| local decompressor |
| |
| case ${file} in |
| *.xz) decompressor="xzcat" ;; |
| *.bz2) decompressor="bzcat" ;; |
| *.gz) decompressor="zcat" ;; |
| *) decompressor="cat" ;; |
| esac |
| ${decompressor} "${file}" | tar "$@" |
| _pipestatus=${PIPESTATUS[*]} |
| [[ ${_pipestatus// /} -eq 0 ]] |
| } |
| |
| get_utc_date_in_seconds() { |
| date -u +"%s" |
| } |
| |
| get_date_part() { |
| local utc_time_in_secs="$1" |
| local part="$2" |
| |
| if [[ ${USERLAND} == BSD ]] ; then |
| date -r "${utc_time_in_secs}" -u +"${part}" |
| else |
| date -d "@${utc_time_in_secs}" -u +"${part}" |
| fi |
| } |
| |
| get_utc_second_from_string() { |
| local s="$1" |
| |
| if [[ ${USERLAND} == BSD ]] ; then |
| # Specify zeros for the least significant digits, or else those |
| # digits are inherited from the current system clock time. |
| date -juf "%Y%m%d%H%M.%S" "${s}0000.00" +"%s" |
| else |
| date -d "${s:0:4}-${s:4:2}-${s:6:2}" -u +"%s" |
| fi |
| } |
| |
| get_repository_timestamp() { |
| local portage_current_timestamp=0 |
| |
| if [[ -f "${repo_location}/metadata/timestamp.x" ]]; then |
| portage_current_timestamp=$(cut -f 1 -d " " "${repo_location}/metadata/timestamp.x" ) |
| fi |
| |
| echo "${portage_current_timestamp}" |
| } |
| |
| fetch_file() { |
| local URI="$1" |
| local FILE="$2" |
| local opts |
| |
| if [[ "${FETCHCOMMAND/wget/}" != "${FETCHCOMMAND}" ]]; then |
| opts="--continue $(nvecho -q)" |
| elif [[ "${FETCHCOMMAND/curl/}" != "${FETCHCOMMAND}" ]]; then |
| opts="--continue-at - $(nvecho -s -f)" |
| else |
| rm -f "${DISTDIR}/${FILE}" |
| fi |
| |
| [[ ${PORTAGE_QUIET} -eq 1 ]] || einfo "Fetching file ${FILE} ..." |
| # Already set DISTDIR= |
| eval "${FETCHCOMMAND} ${opts}" |
| |
| if [[ $? -eq 0 && -s ${DISTDIR}/${FILE} ]] ; then |
| return 0 |
| else |
| rm -f "${DISTDIR}/${FILE}" |
| return 1 |
| fi |
| } |
| |
| check_file_digest() { |
| local digest="$1" |
| local file="$2" |
| local r=1 |
| |
| [[ ${PORTAGE_QUIET} -eq 1 ]] || einfo "Checking digest ..." |
| |
| if type -P md5sum > /dev/null; then |
| local md5sum_output=$(md5sum "${file}") |
| local digest_content=$(< "${digest}") |
| [[ "${md5sum_output%%[[:space:]]*}" = "${digest_content%%[[:space:]]*}" ]] && r=0 |
| elif type -P md5 > /dev/null; then |
| [[ "$(md5 -q "${file}")" == "$(cut -d ' ' -f 1 "${digest}")" ]] && r=0 |
| else |
| die "cannot check digest: no suitable md5/md5sum binaries found" |
| fi |
| |
| return "${r}" |
| } |
| |
| check_file_signature_gemato() { |
| local signature="$1" |
| local file="$2" |
| local r=1 |
| |
| if type -P gemato > /dev/null; then |
| if [[ -n ${PORTAGE_GPG_KEY} ]] ; then |
| local key="${PORTAGE_GPG_KEY}" |
| else |
| local key="${EPREFIX}/usr/share/openpgp-keys/gentoo-release.asc" |
| fi |
| |
| if [[ ! -f "${key}" ]] ; then |
| eerror "${key} not available. Is sec-keys/openpgp-keys-gentoo-release installed?" |
| die "Needed keys unavailable! Install its package or set PORTAGE_GPG_KEY to the right path." |
| fi |
| |
| local gemato_args=( |
| openpgp-verify-detached |
| -K "${key}" |
| ) |
| |
| if [[ -n ${http_proxy} || -n ${https_proxy} ]] ; then |
| gemato_args+=( |
| --proxy "${http_proxy:-${https_proxy}}" |
| ) |
| fi |
| |
| [[ -n ${PORTAGE_GPG_KEY_SERVER} ]] && gemato_args+=( --keyserver "${PORTAGE_GPG_KEY_SERVER}" ) |
| [[ ${PORTAGE_QUIET} == 1 ]] && gemato_args+=( --quiet ) |
| [[ ${do_debug} == 1 ]] && gemato_args+=( --debug ) |
| |
| gemato "${gemato_args[@]}" -- "${signature}" "${file}" |
| r=$? |
| |
| if [[ ${r} -ne 0 ]]; then |
| # Exit early since it's typically inappropriate to |
| # try other mirrors in this case (it may indicate |
| # a keyring problem). |
| die "signature verification failed" |
| fi |
| else |
| return 127 |
| fi |
| |
| return "${r}" |
| } |
| |
| check_file_signature_gpg_unwrapped() { |
| local signature="$1" |
| local file="$2" |
| |
| if type -P gpg > /dev/null; then |
| if [[ -n ${PORTAGE_GPG_KEY} ]] ; then |
| local key="${PORTAGE_GPG_KEY}" |
| else |
| local key="${EPREFIX}/usr/share/openpgp-keys/gentoo-release.asc" |
| fi |
| |
| if [[ ! -f "${key}" ]] ; then |
| eerror "${key} not available. Is sec-keys/openpgp-keys-gentoo-release installed?" |
| die "Needed keys unavailable! Install its package or set PORTAGE_GPG_KEY to the right path." |
| fi |
| |
| local gpgdir="${PORTAGE_GPG_DIR}" |
| if [[ -z ${gpgdir} ]] ; then |
| gpgdir=$(mktemp -d "${PORTAGE_TMPDIR}/portage/webrsync-XXXXXX") |
| if [[ ! -w ${gpgdir} ]] ; then |
| die "gpgdir is not writable: ${gpgdir}" |
| fi |
| |
| # If we're created our own temporary directory, it's okay for us |
| # to import the keyring by ourselves. But we'll avoid doing it |
| # if the user has set PORTAGE_GPG_DIR by themselves. |
| gpg --no-default-keyring --homedir "${gpgdir}" --batch --import "${key}" |
| fi |
| |
| if gnupg_status=$(gpg --no-default-keyring --homedir "${gpgdir}" --batch \ |
| --status-fd 1 --verify "${signature}" "${file}"); then |
| while read -r line; do |
| if [[ ${line} == "[GNUPG:] GOODSIG"* ]]; then |
| r=0 |
| break |
| fi |
| done <<< "${gnupg_status}" |
| fi |
| |
| if [[ ${r} -ne 0 ]]; then |
| # Exit early since it's typically inappropriate to |
| # try other mirrors in this case (it may indicate |
| # a keyring problem). |
| die "signature verification failed" |
| fi |
| else |
| die "cannot check signature: gpg binary not found" |
| fi |
| } |
| |
| check_file_signature() { |
| local signature="$1" |
| local file="$2" |
| local r=1 |
| local gnupg_status line |
| |
| if [[ ${WEBRSYNC_VERIFY_SIGNATURE} != 0 ]]; then |
| [[ ${PORTAGE_QUIET} -eq 1 ]] || einfo "Checking signature ..." |
| |
| case ${WEBRSYNC_VERIFY_SIGNATURE} in |
| 1) |
| check_file_signature_gemato "${signature}" "${file}" |
| r=$? |
| |
| if [[ ${r} -eq 127 ]] ; then |
| ewarn "Falling back to gpg as gemato is not installed" |
| check_file_signature_gpg_unwrapped "${signature}" "${file}" |
| r=$? |
| fi |
| |
| ;; |
| 2) |
| check_file_signature_gpg_unwrapped "${signature}" "${file}" |
| r=$? |
| ;; |
| esac |
| |
| if [[ ${r} != 0 ]] ; then |
| eerror "Error occurred in check_file_signature: ${r}. Aborting." |
| die "Verification error occured." |
| fi |
| else |
| r=0 |
| fi |
| |
| return "${r}" |
| } |
| |
| get_snapshot_timestamp() { |
| local file="$1" |
| |
| do_tar "${file}" --to-stdout -f - --wildcards -x '*/metadata/timestamp.x' | cut -f 1 -d " " |
| } |
| |
| sync_local() { |
| local file="$1" |
| |
| [[ ${PORTAGE_QUIET} -eq 1 ]] || einfo "Syncing local repository ..." |
| |
| local ownership="portage:portage" |
| if has usersync ${FEATURES} ; then |
| case "${USERLAND}" in |
| BSD) |
| ownership=$(stat -f '%Su:%Sg' "${repo_location}") |
| ;; |
| *) |
| ownership=$(stat -c '%U:%G' "${repo_location}") |
| ;; |
| esac |
| fi |
| |
| if type -P tarsync > /dev/null ; then |
| local chown_opts="-o ${ownership%:*} -g ${ownership#*:}" |
| chown ${ownership} "${repo_location}" > /dev/null 2>&1 || chown_opts="" |
| |
| if ! tarsync $(vvecho -v) -s 1 ${chown_opts} \ |
| -e /distfiles -e /packages -e /local "${file}" "${repo_location}"; then |
| |
| eerror "tarsync failed; tarball is corrupt? (${file})" |
| return 1 |
| fi |
| else |
| if ! do_tar "${file}" -x --strip-components=1 -f -; then |
| eerror "tar failed to extract the image. tarball is corrupt? (${file})" |
| return 1 |
| fi |
| |
| # Free disk space |
| ${keep} || rm -f "${file}" |
| |
| local rsync_opts="${PORTAGE_RSYNC_OPTS} ${PORTAGE_RSYNC_EXTRA_OPTS} $(nvecho -q)" |
| if chown ${ownership} . > /dev/null 2>&1; then |
| chown -R ${ownership} . |
| rsync_opts+=" --owner --group" |
| fi |
| |
| chmod 755 . |
| rsync ${rsync_opts} . "${repo_location%%/}" || { |
| eerror "rsync failed: $?" |
| die "Aborting because of rsync failure" |
| } |
| |
| [[ ${PORTAGE_QUIET} == 1 ]] || einfo "Cleaning up ..." |
| fi |
| |
| if has metadata-transfer ${FEATURES} ; then |
| einfo "Updating cache ..." |
| "${emerge}" --metadata |
| fi |
| |
| local post_sync=${PORTAGE_CONFIGROOT%/}/etc/portage/bin/post_sync |
| [[ -x "${post_sync}" ]] && "${post_sync}" |
| |
| # --quiet suppresses output if there are no relevant news items |
| has news ${FEATURES} && "${emerge}" --check-news --quiet |
| return 0 |
| } |
| |
| do_snapshot() { |
| local ignore_timestamp="$1" |
| local date="$2" |
| |
| local r=1 |
| |
| local compression |
| |
| local have_files=0 |
| local mirror |
| |
| local compressions="" |
| |
| type -P xzcat > /dev/null && compressions="${compressions} ${repo_name}:xz portage:xz" |
| type -P bzcat > /dev/null && compressions="${compressions} ${repo_name}:bz2 portage:bz2" |
| type -P zcat > /dev/null && compressions="${compressions} ${repo_name}:gz portage:gz" |
| if [[ -z ${compressions} ]] ; then |
| die "unable to locate any decompressors (xzcat or bzcat or zcat)" |
| fi |
| |
| for mirror in ${GENTOO_MIRRORS} ; do |
| mirror=${mirror%/} |
| [[ ${PORTAGE_QUIET} -eq 1 ]] || einfo "Trying to retrieve ${date} snapshot from ${mirror} ..." |
| |
| for compression in ${compressions} ; do |
| local name=${compression%%:*} |
| |
| compression=${compression#*:} |
| |
| local file="${name}-${date}.tar.${compression}" |
| local digest="${file}.md5sum" |
| local signature="${file}.gpgsig" |
| |
| if [[ -s "${DISTDIR}/${file}" && -s "${DISTDIR}/${digest}" && -s "${DISTDIR}/${signature}" ]] ; then |
| check_file_digest "${DISTDIR}/${digest}" "${DISTDIR}/${file}" && \ |
| check_file_signature "${DISTDIR}/${signature}" "${DISTDIR}/${file}" && \ |
| have_files=1 |
| fi |
| |
| if [[ ${have_files} -eq 0 ]] ; then |
| fetch_file "${mirror}/snapshots/${digest}" "${digest}" && \ |
| fetch_file "${mirror}/snapshots/${signature}" "${signature}" && \ |
| fetch_file "${mirror}/snapshots/${file}" "${file}" && \ |
| check_file_digest "${DISTDIR}/${digest}" "${DISTDIR}/${file}" && \ |
| check_file_signature "${DISTDIR}/${signature}" "${DISTDIR}/${file}" && \ |
| |
| have_files=1 |
| fi |
| |
| # |
| # If timestamp is invalid |
| # we want to try and retrieve |
| # from a different mirror |
| # |
| if [[ ${have_files} -eq 1 ]]; then |
| [[ ${PORTAGE_QUIET} -eq 1 ]] || einfo "Getting snapshot timestamp ..." |
| |
| local snapshot_timestamp |
| snapshot_timestamp=$(get_snapshot_timestamp "${DISTDIR}/${file}") |
| |
| if [[ ${ignore_timestamp} == 0 ]]; then |
| if [[ ${snapshot_timestamp} -lt $(get_repository_timestamp) ]]; then |
| ewarn "Repository (age) is newer than fetched snapshot" |
| have_files=0 |
| fi |
| else |
| local utc_seconds |
| utc_seconds=$(get_utc_second_from_string "${date}") |
| |
| # Check that this snapshot is what the age it claims to be |
| if [[ ${snapshot_timestamp} -lt ${utc_seconds} || \ |
| ${snapshot_timestamp} -gt $((${utc_seconds}+ 2*86400)) ]]; then |
| |
| ewarn "Snapshot timestamp is not within acceptable period!" |
| have_files=0 |
| fi |
| fi |
| fi |
| |
| if [[ ${have_files} -eq 1 ]]; then |
| break |
| else |
| # Remove files and use a different mirror |
| rm -f "${DISTDIR}/${file}" "${DISTDIR}/${digest}" "${DISTDIR}/${signature}" |
| fi |
| done |
| |
| [[ ${have_files} -eq 1 ]] && break |
| done |
| |
| if [[ ${have_files} -eq 1 ]]; then |
| sync_local "${DISTDIR}/${file}" && r=0 |
| else |
| ewarn "${date} snapshot was not found" |
| fi |
| |
| ${keep} || rm -f "${DISTDIR}/${file}" "${DISTDIR}/${digest}" "${DISTDIR}/${signature}" |
| return "${r}" |
| } |
| |
| do_latest_snapshot() { |
| local attempts=0 |
| local r=1 |
| |
| [[ ${PORTAGE_QUIET} -eq 1 ]] || einfo "Fetching most recent snapshot ..." |
| |
| # The snapshot for a given day is generated at 00:45 UTC on the following |
| # day, so the current day's snapshot (going by UTC time) hasn't been |
| # generated yet. Therefore, always start by looking for the previous day's |
| # snapshot (for attempts=1, subtract 1 day from the current UTC time). |
| |
| # Timestamps that differ by less than 2 hours |
| # are considered to be approximately equal. |
| local min_time_diff=$(( 2 * 60 * 60 )) |
| |
| local existing_timestamp |
| local timestamp_difference |
| local timestamp_problem |
| local approx_snapshot_time |
| local start_time |
| local start_hour |
| local snapshot_date |
| local snapshot_date_seconds |
| |
| existing_timestamp=$(get_repository_timestamp) |
| start_time=$(get_utc_date_in_seconds) |
| start_hour=$(get_date_part "${start_time}" "%H") |
| |
| # Daily snapshots are created at 00:45 and are not |
| # available until after 01:00. Don't waste time trying |
| # to fetch a snapshot before it's been created. |
| if [[ ${start_hour#0} -lt 1 ]] ; then |
| (( start_time -= 86400 )) |
| fi |
| |
| snapshot_date=$(get_date_part "${start_time}" "%Y%m%d") |
| snapshot_date_seconds=$(get_utc_second_from_string "${snapshot_date}") |
| |
| while (( ${attempts} < 40 )) ; do |
| (( attempts++ )) |
| (( snapshot_date_seconds -= 86400 )) |
| # snapshots are created at 00:45 |
| (( approx_snapshot_time = snapshot_date_seconds + 86400 + 2700 )) |
| (( timestamp_difference = existing_timestamp - approx_snapshot_time )) |
| |
| [[ ${timestamp_difference} -lt 0 ]] && (( timestamp_difference = -1 * timestamp_difference )) |
| snapshot_date=$(get_date_part "${snapshot_date_seconds}" "%Y%m%d") |
| |
| timestamp_problem="" |
| if [[ ${timestamp_difference} -eq 0 ]]; then |
| timestamp_problem="is identical to" |
| elif [[ ${timestamp_difference} -lt ${min_time_diff} ]]; then |
| timestamp_problem="is possibly identical to" |
| elif [[ ${approx_snapshot_time} -lt ${existing_timestamp} ]] ; then |
| timestamp_problem="is newer than" |
| fi |
| |
| if [[ -n "${timestamp_problem}" ]]; then |
| ewarn "Latest snapshot date: ${snapshot_date}" |
| ewarn |
| ewarn "Approximate snapshot timestamp: ${approx_snapshot_time}" |
| ewarn " Current local timestamp: ${existing_timestamp}" |
| ewarn |
| echo -e "The current local timestamp" \ |
| "${timestamp_problem} the" \ |
| "timestamp of the latest" \ |
| "snapshot. In order to force sync," \ |
| "use the --revert option or remove" \ |
| "the timestamp file located at" \ |
| "'${repo_location}/metadata/timestamp.x'." | fmt -w 70 | \ |
| while read -r line ; do |
| ewarn "${line}" |
| done |
| r=0 |
| break |
| fi |
| |
| if do_snapshot 0 "${snapshot_date}"; then |
| r=0 |
| break; |
| fi |
| done |
| |
| return "${r}" |
| } |
| |
| usage() { |
| cat <<-EOF |
| Usage: $0 [options] |
| |
| Options: |
| --revert=yyyymmdd Revert to snapshot |
| --no-pgp-verify Disable PGP verification of snapshot |
| -k, --keep Keep snapshots in DISTDIR (don't delete) |
| -q, --quiet Only output errors |
| -v, --verbose Enable verbose output (no-op) |
| -x, --debug Enable debug output |
| -h, --help This help screen (duh!) |
| EOF |
| if [[ -n $* ]] ; then |
| printf "\nError: %s\n" "$*" 1>&2 |
| exit 1 |
| else |
| exit 0 |
| fi |
| } |
| |
| main() { |
| local arg |
| local revert_date |
| |
| for arg in "$@" ; do |
| local v=${arg#*=} |
| case ${arg} in |
| -h|--help) usage ;; |
| -k|--keep) keep=true ;; |
| -q|--quiet) PORTAGE_QUIET=1 ;; |
| -v|--verbose) unset PORTAGE_QUIET ;; |
| -x|--debug) do_debug=1 ;; |
| --revert=*) revert_date=${v} ;; |
| --no-pgp-verify) no_pgp_verify=1 ;; |
| *) usage "Invalid option '${arg}'" ;; |
| esac |
| done |
| |
| handle_pgp_setup |
| |
| [[ -d ${repo_location} ]] || mkdir -p "${repo_location}" |
| if [[ ! -w ${repo_location} ]] ; then |
| die "Repository '${repo_name}' is not writable: ${repo_location}" |
| fi |
| |
| [[ -d ${PORTAGE_TMPDIR}/portage ]] || mkdir -p "${PORTAGE_TMPDIR}/portage" |
| TMPDIR=$(mktemp -d "${PORTAGE_TMPDIR}/portage/webrsync-XXXXXX") |
| if [[ ! -w ${TMPDIR} ]] ; then |
| die "TMPDIR is not writable: ${TMPDIR}" |
| fi |
| trap 'set -u ; cd / ; rm -rf "${TMPDIR}"' EXIT |
| cd "${TMPDIR}" || exit 1 |
| |
| ${keep} || DISTDIR=${TMPDIR} |
| [[ ! -d "${DISTDIR}" ]] && mkdir -p "${DISTDIR}" |
| |
| if ${keep} && [[ ! -w ${DISTDIR} ]] ; then |
| die "DISTDIR is not writable: ${DISTDIR}" |
| fi |
| |
| # This is a sanity check to help prevent people like funtoo users |
| # from accidentally wiping out their git tree. |
| if [[ -n ${repo_sync_type} && ${repo_sync_type} != rsync && ${repo_sync_type} != webrsync ]] ; then |
| eerror "The current sync-type attribute of repository 'gentoo' is not set to 'rsync' or 'webrsync':" |
| eerror |
| eerror " sync-type=${repo_sync_type}" |
| eerror |
| eerror "If you intend to use emerge-webrsync then please" |
| eerror "adjust sync-type and sync-uri attributes to refer to rsync." |
| eerror "emerge-webrsync exiting due to abnormal sync-type setting." |
| die |
| fi |
| |
| [[ ${do_debug} -eq 1 ]] && set -x |
| |
| if [[ -n ${revert_date} ]] ; then |
| do_snapshot 1 "${revert_date}" |
| else |
| do_latest_snapshot |
| fi |
| } |
| |
| main "$@" |