#!/bin/bash # Copyright 1999-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Id: emerge-websync,v 1.4 2007/04/07 11:37:18 andrea Exp $ # Author: Karl Trygve Kalleberg # Rewritten from the old, Perl-based emerge-webrsync script # Author: Alon Bar-Lev # Major rewrite from Karl's scripts. # # gpg key import # KEY_ID=0x7DDAD20D # gpg --homedir /etc/portage/gnupg --keyserver subkeys.pgp.net --recv-keys $KEY_ID # gpg --homedir /etc/portage/gnupg --edit-key $KEY_ID trust # type portageq > /dev/null || exit $? eval $(portageq envvar -v FEATURES FETCHCOMMAND GENTOO_MIRRORS \ PORTAGE_BIN_PATH PORTAGE_INST_UID PORTAGE_INST_GID PORTAGE_NICENESS \ GENTOO_MIRRORS PORTAGE_INST_UID PORTAGE_INST_GID PORTAGE_NICENESS \ PORTAGE_TMPDIR PORTDIR WEBSYNC_VERIFY_SIGNATURE PORTAGE_GPG_DIR) DISTDIR="${PORTAGE_TMPDIR}/emerge-webrsync" # 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 source /usr/lib/portage/bin/isolated-functions.sh || exit 1 # ${USERLAND} is unreliable since the portage tree might be empty, so test # success of the -r option to distinguish between gnu and bsd date. date -r 0 >&/dev/null && DATE_ARGS="BSD" || DATE_ARGS="GNU" do_verbose=0 if [ -z "${WEBSYNC_VERIFY_SIGNATURE}" ]; then WEBSYNC_VERIFY_SIGNATURE=0 fi if [ ${WEBSYNC_VERIFY_SIGNATURE} != 0 -a -z "${PORTAGE_GPG_DIR}" ]; then echo "Error: Please set PORTAGE_GPG_DIR in make.conf" exit 1 fi get_utc_date_in_seconds() { date -u +"%s" } get_date_part() { local utc_time_in_secs="$1" local part="$2" if [ "${DATE_ARGS}" = "BSD" ]; then date -r ${utc_time_in_secs} -u +"${part}" else date -d @${utc_time_in_secs} -u +"${part}" fi } get_utc_from_string() { local s="$1" seconds=$(date -d "${s:0:4}-${s:4:2}-${s:6:2}" -u +"%s") } get_portage_timestamp() { local portage_current_timestamp=0 if [ -f "${PORTDIR}/metadata/timestamp.x" ]; then portage_current_timestamp=$(cut -f 1 -d " " "${PORTDIR}/metadata/timestamp.x" ) fi echo "${portage_current_timestamp}" } fetch_file() { local url="$1" local file="$2" local opts if [ "${FETCHCOMMAND/wget/}" != "${FETCHCOMMAND}" ]; then opts="--continue" [ "${do_verbose}" == 0 ] && opts="$opts -q" elif [ "${FETCHCOMMAND/curl/}" != "${FETCHCOMMAND}" ]; then opts="--continue-at -" [ "${do_verbose}" == 0 ] && opts="$opts -s -f" else rm -f "${file}" fi echo "Fetching file ${file}..." #already set DISTDIR= if [ "${do_verbose}" == 0 ] ; then URI="${url}" eval "${FETCHCOMMAND}" ${opts} > /dev/null && [ -s "${file}" ] else URI="${url}" eval "${FETCHCOMMAND}" ${opts} && [ -s "${file}" ] fi } check_file_digest() { local digest="$1" local file="$2" local r=1 echo "Checking digest..." if type -p md5sum > /dev/null; then md5sum -c $digest && r=0 elif type -p md5 > /dev/null; then [ "$(md5 -q $file)" == "$(cut -d \ -f 1 \"$digest\")" ] && r=0 else echo "Error: Cannot check digest" echo "No suitable md5/md5sum binaries found" fi return "${r}" } check_file_signature() { local signature="$1" local file="$2" local r=1 if [ ${WEBSYNC_VERIFY_SIGNATURE} != 0 ]; then echo "Checking signature..." if type -p gpg > /dev/null; then gpg --homedir "${PORTAGE_GPG_DIR}" --verify "$signature" "$file" && r=0 else echo "Error: Cannot check signature" echo "gpg binary not found" fi else r=0 fi return "${r}" } get_snapshot_timestamp() { local file="$1" tar --to-stdout -xf "${file}" portage/metadata/timestamp.x | cut -f 1 -d " " } sync_local() { local file="$1" echo "Syncing local tree..." if type -p tarsync &> /dev/null; then if [ "${do_verbose}" != 0 ] ; then tarsync_verbose=-v fi if ! tarsync $tarsync_verbose -s 1 -o portage -g portage -e /distfiles -e /packages -e /local "${file}" "${PORTDIR}"; then echo "Error: tarsync failed; tarball is corrupt? (${file})" return 1 fi else echo "Note: tarsync was not found, you may consider emerge it..." if ! tar jxf "${file}"; then echo "Error: tar failed to extract the image. tarball is corrupt? (${file})" rm -fr portage return 1 fi # Free disk space rm -f "${file}" # Make sure user and group file ownership is ${PORTAGE_INST_UID}:${PORTAGE_INST_GID} chown -R ${PORTAGE_INST_UID:-0}:${PORTAGE_INST_GID:-0} portage cd portage rsync -av --progress --stats --delete --delete-after \ --exclude='/distfiles' --exclude='/packages' \ --exclude='/local' . "${PORTDIR%%/}" cd .. echo "Cleaning up..." rm -fr portage fi if hasq metadata-transfer ${FEATURES} ; then echo "Updating cache..." emerge --metadata fi [ -x /etc/portage/bin/post_sync ] && /etc/portage/bin/post_sync } do_snapshot() { local ignore_timestamp="$1" local date="$2" local r=1 local file="portage-${date}.tar.bz2" local digest="${file}.md5sum" local signature="${file}.gpgsig" local have_files=0 local mirror echo "Trying to retrieve ${date} snapshot..." for mirror in ${GENTOO_MIRRORS} ; do [ -s "${file}" -a -s "${digest}" -a -s "${signature}" ] && \ check_file_digest "${digest}" "${file}" && \ check_file_signature "${signature}" "${file}" && \ have_files=1 [ ${have_files} == 0 ] && \ fetch_file "${mirror}/snapshots/${digest}" "${digest}" && \ fetch_file "${mirror}/snapshots/${signature}" "${signature}" && \ fetch_file "${mirror}/snapshots/${file}" "${file}" && \ check_file_digest "${digest}" "${file}" && \ check_file_signature "${signature}" "${file}" && \ have_files=1 # # If timestamp is invalid # we want to try and retieve # from a different mirror # if [ ${have_files} != 0 ]; then echo "Getting snapshot timetasmp..." local snapshot_timestamp=$(get_snapshot_timestamp "${file}") if [ ${ignore_timestamp} == 0 ]; then if [ ${snapshot_timestamp} -lt $(get_portage_timestamp) ]; then echo "Warning: Portage is newer than snapshot" have_files=0 fi else utc_date=$(get_utc_from_string "${date}") # # Check that this snapshot # is what it claims to be... # if [ ${snapshot_timestamp} -lt ${seconds} ] || \ [ ${snapshot_timestamp} -gt $((${seconds}+ 2*86400)) ]; then echo "Warning: Snapshot timestamp is not in acceptable period." have_files=0 fi fi fi if [ ${have_files} != 0 ]; then break; else # # Remove files and use # a diffeernt mirror # rm -f "${file}" "${digest}" "${signature}" fi done if [ ${have_files} != 0 ]; then sync_local "${file}" && r=0 else echo "Warning: ${date} snapshot was not found." fi rm -f "${file}" "${digest}" "${signature}" return "${r}" } do_latest_snapshot() { local attempts=-1 local r=1 echo "Fetching most recent snapshot..." while (( ${attempts} < 40 )) ; do local day local month local year local seconds attempts=$(( ${attempts} + 1 )) utc_attempt=$(expr $(get_utc_date_in_seconds) - 86400 \* ${attempts}) day=$(get_date_part ${utc_attempt} "%d") month=$(get_date_part ${utc_attempt} "%m") year=$(get_date_part ${utc_attempt} "%Y") utc_midnight=$(get_date_part $(expr ${utc_attempt} - ${utc_attempt} % 86400) "%s") if [ ${utc_midnight} -lt $(($(get_portage_timestamp)-86400)) ]; then echo "Note: Portage content is newer than available snapshots" echo "use --revert option to overide." r=0 break; fi if do_snapshot 0 "${year}${month}${day}"; then r=0 break; fi done return "${r}" } main() { local arg local do_revert=0 local revert_date [ ! -d "${DISTDIR}" ] && mkdir -p "${DISTDIR}" cd "${DISTDIR}" for arg in $*; do local v=${arg#*=} case ${arg} in --help) echo "usage: $0 [options]" echo " --verbose (-v) - verbose" echo " --revert=yyyymmdd - revert to snapshot" exit 0 ;; --verbose|-v) do_verbose=1 ;; --revert=*) do_revert=1 revert_date=${v} ;; *) echo "Error: Invalid arguments" exit 1 ;; esac done if [ ${do_revert} != 0 ]; then do_snapshot 1 "${revert_date}" else do_latest_snapshot fi } main $*