portsnap.sh revision 226312
1148871Scperciva#!/bin/sh
2148871Scperciva
3148871Scperciva#-
4148871Scperciva# Copyright 2004-2005 Colin Percival
5148871Scperciva# All rights reserved
6148871Scperciva#
7148871Scperciva# Redistribution and use in source and binary forms, with or without
8148871Scperciva# modification, are permitted providing that the following conditions 
9148871Scperciva# are met:
10148871Scperciva# 1. Redistributions of source code must retain the above copyright
11148871Scperciva#    notice, this list of conditions and the following disclaimer.
12148871Scperciva# 2. Redistributions in binary form must reproduce the above copyright
13148871Scperciva#    notice, this list of conditions and the following disclaimer in the
14148871Scperciva#    documentation and/or other materials provided with the distribution.
15148871Scperciva#
16148871Scperciva# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17148871Scperciva# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18148871Scperciva# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19148871Scperciva# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20148871Scperciva# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21148871Scperciva# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22148871Scperciva# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23148871Scperciva# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24148871Scperciva# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25148871Scperciva# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26148871Scperciva# POSSIBILITY OF SUCH DAMAGE.
27148871Scperciva
28148871Scperciva# $FreeBSD: stable/9/usr.sbin/portsnap/portsnap/portsnap.sh 226312 2011-10-12 17:57:57Z jilles $
29148871Scperciva
30148871Scperciva#### Usage function -- called from command-line handling code.
31148871Scperciva
32148871Scperciva# Usage instructions.  Options not listed:
33148871Scperciva# --debug	-- don't filter output from utilities
34148871Scperciva# --no-stats	-- don't show progress statistics while fetching files
35148871Scpercivausage() {
36148871Scperciva	cat <<EOF
37149027Scpercivausage: `basename $0` [options] command ... [path]
38148871Scperciva
39148871ScpercivaOptions:
40148871Scperciva  -d workdir   -- Store working files in workdir
41148871Scperciva                  (default: /var/db/portsnap/)
42148871Scperciva  -f conffile  -- Read configuration options from conffile
43148871Scperciva                  (default: /etc/portsnap.conf)
44148871Scperciva  -I           -- Update INDEX only. (update command only)
45148871Scperciva  -k KEY       -- Trust an RSA key with SHA256 hash of KEY
46158523Scperciva  -l descfile  -- Merge the specified local describes file into the INDEX.
47148871Scperciva  -p portsdir  -- Location of uncompressed ports tree
48148871Scperciva                  (default: /usr/ports/)
49148871Scperciva  -s server    -- Server from which to fetch updates.
50148871Scperciva                  (default: portsnap.FreeBSD.org)
51148871Scperciva  path         -- Extract only parts of the tree starting with the given
52148871Scperciva                  string.  (extract command only)
53148871ScpercivaCommands:
54148871Scperciva  fetch        -- Fetch a compressed snapshot of the ports tree,
55148871Scperciva                  or update an existing snapshot.
56148871Scperciva  cron         -- Sleep rand(3600) seconds, and then fetch updates.
57148871Scperciva  extract      -- Extract snapshot of ports tree, replacing existing
58148871Scperciva                  files and directories.
59148871Scperciva  update       -- Update ports tree to match current snapshot, replacing
60148871Scperciva                  files and directories which have changed.
61148871ScpercivaEOF
62148871Scperciva	exit 0
63148871Scperciva}
64148871Scperciva
65148871Scperciva#### Parameter handling functions.
66148871Scperciva
67148871Scperciva# Initialize parameters to null, just in case they're
68148871Scperciva# set in the environment.
69148871Scpercivainit_params() {
70148871Scperciva	KEYPRINT=""
71148871Scperciva	EXTRACTPATH=""
72148871Scperciva	WORKDIR=""
73148871Scperciva	PORTSDIR=""
74148871Scperciva	CONFFILE=""
75148871Scperciva	COMMAND=""
76149027Scperciva	COMMANDS=""
77148871Scperciva	QUIETREDIR=""
78148871Scperciva	QUIETFLAG=""
79148871Scperciva	STATSREDIR=""
80148879Scperciva	XARGST=""
81148871Scperciva	NDEBUG=""
82148871Scperciva	DDSTATS=""
83148871Scperciva	INDEXONLY=""
84148871Scperciva	SERVERNAME=""
85149824Scperciva	REFUSE=""
86158523Scperciva	LOCALDESC=""
87148871Scperciva}
88148871Scperciva
89148871Scperciva# Parse the command line
90148871Scpercivaparse_cmdline() {
91148871Scperciva	while [ $# -gt 0 ]; do
92148871Scperciva		case "$1" in
93148871Scperciva		-d)
94148871Scperciva			if [ $# -eq 1 ]; then usage; fi
95148871Scperciva			if [ ! -z "${WORKDIR}" ]; then usage; fi
96148871Scperciva			shift; WORKDIR="$1"
97148871Scperciva			;;
98148871Scperciva		--debug)
99148871Scperciva			QUIETREDIR="/dev/stderr"
100148871Scperciva			STATSREDIR="/dev/stderr"
101148871Scperciva			QUIETFLAG=" "
102148871Scperciva			NDEBUG=" "
103148879Scperciva			XARGST="-t"
104148871Scperciva			DDSTATS=".."
105148871Scperciva			;;
106148871Scperciva		-f)
107148871Scperciva			if [ $# -eq 1 ]; then usage; fi
108148871Scperciva			if [ ! -z "${CONFFILE}" ]; then usage; fi
109148871Scperciva			shift; CONFFILE="$1"
110148871Scperciva			;;
111148871Scperciva		-h | --help | help)
112148871Scperciva			usage
113148871Scperciva			;;
114148871Scperciva		-I)
115148871Scperciva			INDEXONLY="YES"
116148871Scperciva			;;
117148871Scperciva		-k)
118148871Scperciva			if [ $# -eq 1 ]; then usage; fi
119148871Scperciva			if [ ! -z "${KEYPRINT}" ]; then usage; fi
120148871Scperciva			shift; KEYPRINT="$1"
121148871Scperciva			;;
122158523Scperciva		-l)
123158523Scperciva			if [ $# -eq 1 ]; then usage; fi
124158523Scperciva			if [ ! -z "${LOCALDESC}" ]; then usage; fi
125158523Scperciva			shift; LOCALDESC="$1"
126158523Scperciva			;;
127148871Scperciva		--no-stats)
128148871Scperciva			if [ -z "${STATSREDIR}" ]; then
129148871Scperciva				STATSREDIR="/dev/null"
130148871Scperciva				DDSTATS=".. "
131148871Scperciva			fi
132148871Scperciva			;;
133148871Scperciva		-p)
134148871Scperciva			if [ $# -eq 1 ]; then usage; fi
135148871Scperciva			if [ ! -z "${PORTSDIR}" ]; then usage; fi
136148871Scperciva			shift; PORTSDIR="$1"
137148871Scperciva			;;
138148871Scperciva		-s)
139148871Scperciva			if [ $# -eq 1 ]; then usage; fi
140148871Scperciva			if [ ! -z "${SERVERNAME}" ]; then usage; fi
141148871Scperciva			shift; SERVERNAME="$1"
142148871Scperciva			;;
143201251Scperciva		cron | extract | fetch | update | alfred)
144149027Scperciva			COMMANDS="${COMMANDS} $1"
145148871Scperciva			;;
146148871Scperciva		*)
147148871Scperciva			if [ $# -gt 1 ]; then usage; fi
148149027Scperciva			if echo ${COMMANDS} | grep -vq extract; then
149149027Scperciva				usage
150149027Scperciva			fi
151148871Scperciva			EXTRACTPATH="$1"
152148871Scperciva			;;
153148871Scperciva		esac
154148871Scperciva		shift
155148871Scperciva	done
156148871Scperciva
157149027Scperciva	if [ -z "${COMMANDS}" ]; then
158148871Scperciva		usage
159148871Scperciva	fi
160148871Scperciva}
161148871Scperciva
162148871Scperciva# If CONFFILE was specified at the command-line, make
163148871Scperciva# sure that it exists and is readable.
164148871Scpercivasanity_conffile() {
165148871Scperciva	if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
166148871Scperciva		echo -n "File does not exist "
167148871Scperciva		echo -n "or is not readable: "
168148871Scperciva		echo ${CONFFILE}
169148871Scperciva		exit 1
170148871Scperciva	fi
171148871Scperciva}
172148871Scperciva
173148871Scperciva# If a configuration file hasn't been specified, use
174148871Scperciva# the default value (/etc/portsnap.conf)
175148871Scpercivadefault_conffile() {
176148871Scperciva	if [ -z "${CONFFILE}" ]; then
177148871Scperciva		CONFFILE="/etc/portsnap.conf"
178148871Scperciva	fi
179148871Scperciva}
180148871Scperciva
181148871Scperciva# Read {KEYPRINT, SERVERNAME, WORKDIR, PORTSDIR} from the configuration
182148871Scperciva# file if they haven't already been set.  If the configuration
183148871Scperciva# file doesn't exist, do nothing.
184149824Scperciva# Also read REFUSE (which cannot be set via the command line) if it is
185149824Scperciva# present in the configuration file.
186148871Scpercivaparse_conffile() {
187148871Scperciva	if [ -r "${CONFFILE}" ]; then
188148871Scperciva		for X in KEYPRINT WORKDIR PORTSDIR SERVERNAME; do
189148871Scperciva			eval _=\$${X}
190148871Scperciva			if [ -z "${_}" ]; then
191148871Scperciva				eval ${X}=`grep "^${X}=" "${CONFFILE}" |
192148871Scperciva				    cut -f 2- -d '=' | tail -1`
193148871Scperciva			fi
194148871Scperciva		done
195149824Scperciva
196149824Scperciva		if grep -qE "^REFUSE[[:space:]]" ${CONFFILE}; then
197149824Scperciva			REFUSE="^(`
198149824Scperciva				grep -E "^REFUSE[[:space:]]" "${CONFFILE}" |
199149824Scperciva				    cut -c 7- | xargs echo | tr ' ' '|'
200149824Scperciva				`)"
201149824Scperciva		fi
202179073Scperciva
203179073Scperciva		if grep -qE "^INDEX[[:space:]]" ${CONFFILE}; then
204179073Scperciva			INDEXPAIRS="`
205179073Scperciva				grep -E "^INDEX[[:space:]]" "${CONFFILE}" |
206179073Scperciva				    cut -c 7- | tr ' ' '|' | xargs echo`"
207179073Scperciva		fi
208148871Scperciva	fi
209148871Scperciva}
210148871Scperciva
211148871Scperciva# If parameters have not been set, use default values
212148871Scpercivadefault_params() {
213148871Scperciva	_QUIETREDIR="/dev/null"
214148871Scperciva	_QUIETFLAG="-q"
215148871Scperciva	_STATSREDIR="/dev/stdout"
216148871Scperciva	_WORKDIR="/var/db/portsnap"
217148871Scperciva	_PORTSDIR="/usr/ports"
218148871Scperciva	_NDEBUG="-n"
219158523Scperciva	_LOCALDESC="/dev/null"
220158523Scperciva	for X in QUIETREDIR QUIETFLAG STATSREDIR WORKDIR PORTSDIR	\
221158523Scperciva	    NDEBUG LOCALDESC; do
222148871Scperciva		eval _=\$${X}
223148871Scperciva		eval __=\$_${X}
224148871Scperciva		if [ -z "${_}" ]; then
225148871Scperciva			eval ${X}=${__}
226148871Scperciva		fi
227148871Scperciva	done
228148871Scperciva}
229148871Scperciva
230148871Scperciva# Perform sanity checks and set some final parameters
231148871Scperciva# in preparation for fetching files.  Also chdir into
232148871Scperciva# the working directory.
233148871Scpercivafetch_check_params() {
234148871Scperciva	export HTTP_USER_AGENT="portsnap (${COMMAND}, `uname -r`)"
235148871Scperciva
236148871Scperciva	_SERVERNAME_z=\
237148871Scperciva"SERVERNAME must be given via command line or configuration file."
238148871Scperciva	_KEYPRINT_z="Key must be given via -k option or configuration file."
239148871Scperciva	_KEYPRINT_bad="Invalid key fingerprint: "
240148871Scperciva	_WORKDIR_bad="Directory does not exist or is not writable: "
241148871Scperciva
242148871Scperciva	if [ -z "${SERVERNAME}" ]; then
243148871Scperciva		echo -n "`basename $0`: "
244148871Scperciva		echo "${_SERVERNAME_z}"
245148871Scperciva		exit 1
246148871Scperciva	fi
247148871Scperciva	if [ -z "${KEYPRINT}" ]; then
248148871Scperciva		echo -n "`basename $0`: "
249148871Scperciva		echo "${_KEYPRINT_z}"
250148871Scperciva		exit 1
251148871Scperciva	fi
252148871Scperciva	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
253148871Scperciva		echo -n "`basename $0`: "
254148871Scperciva		echo -n "${_KEYPRINT_bad}"
255148871Scperciva		echo ${KEYPRINT}
256148871Scperciva		exit 1
257148871Scperciva	fi
258148871Scperciva	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
259148871Scperciva		echo -n "`basename $0`: "
260148871Scperciva		echo -n "${_WORKDIR_bad}"
261148871Scperciva		echo ${WORKDIR}
262148871Scperciva		exit 1
263148871Scperciva	fi
264148871Scperciva	cd ${WORKDIR} || exit 1
265148871Scperciva
266148871Scperciva	BSPATCH=/usr/bin/bspatch
267148871Scperciva	SHA256=/sbin/sha256
268148871Scperciva	PHTTPGET=/usr/libexec/phttpget
269148871Scperciva}
270148871Scperciva
271148871Scperciva# Perform sanity checks and set some final parameters
272148871Scperciva# in preparation for extracting or updating ${PORTSDIR}
273154088Scperciva# Complain if ${PORTSDIR} exists but is not writable,
274154088Scperciva# but don't complain if ${PORTSDIR} doesn't exist.
275148871Scpercivaextract_check_params() {
276148871Scperciva	_WORKDIR_bad="Directory does not exist: "
277154088Scperciva	_PORTSDIR_bad="Directory is not writable: "
278148871Scperciva
279148871Scperciva	if ! [ -d "${WORKDIR}" ]; then
280148871Scperciva		echo -n "`basename $0`: "
281148871Scperciva		echo -n "${_WORKDIR_bad}"
282148871Scperciva		echo ${WORKDIR}
283148871Scperciva		exit 1
284148871Scperciva	fi
285154088Scperciva	if [ -d "${PORTSDIR}" ] && ! [ -w "${PORTSDIR}" ]; then
286148871Scperciva		echo -n "`basename $0`: "
287148871Scperciva		echo -n "${_PORTSDIR_bad}"
288148871Scperciva		echo ${PORTSDIR}
289148871Scperciva		exit 1
290148871Scperciva	fi
291148871Scperciva
292148871Scperciva	if ! [ -d "${WORKDIR}/files" -a -r "${WORKDIR}/tag"	\
293148871Scperciva	    -a -r "${WORKDIR}/INDEX" -a -r "${WORKDIR}/tINDEX" ]; then
294148871Scperciva		echo "No snapshot available.  Try running"
295148871Scperciva		echo "# `basename $0` fetch"
296148871Scperciva		exit 1
297148871Scperciva	fi
298148871Scperciva
299148871Scperciva	MKINDEX=/usr/libexec/make_index
300148871Scperciva}
301148871Scperciva
302148871Scperciva# Perform sanity checks and set some final parameters
303148871Scperciva# in preparation for updating ${PORTSDIR}
304148871Scpercivaupdate_check_params() {
305148871Scperciva	extract_check_params
306148871Scperciva
307148871Scperciva	if ! [ -r ${PORTSDIR}/.portsnap.INDEX ]; then
308148871Scperciva		echo "${PORTSDIR} was not created by portsnap."
309148871Scperciva		echo -n "You must run '`basename $0` extract' before "
310148871Scperciva		echo "running '`basename $0` update'."
311148871Scperciva		exit 1
312148871Scperciva	fi
313148871Scperciva
314148871Scperciva}
315148871Scperciva
316148871Scperciva#### Core functionality -- the actual work gets done here
317148871Scperciva
318148871Scperciva# Use an SRV query to pick a server.  If the SRV query doesn't provide
319148871Scperciva# a useful answer, use the server name specified by the user.
320148871Scperciva# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
321148871Scperciva# from that; or if no servers are returned, use ${SERVERNAME}.
322148871Scperciva# This allows a user to specify "portsnap.freebsd.org" (in which case
323148871Scperciva# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
324148871Scperciva# (in which case portsnap will use that particular server, since there
325148871Scperciva# won't be an SRV entry for that name).
326148871Scperciva#
327158273Scperciva# We ignore the Port field, since we are always going to use port 80.
328158273Scperciva
329158273Scperciva# Fetch the mirror list, but do not pick a mirror yet.  Returns 1 if
330158273Scperciva# no mirrors are available for any reason.
331158273Scpercivafetch_pick_server_init() {
332158273Scperciva	: > serverlist_tried
333158273Scperciva
334150159Scperciva# Check that host(1) exists (i.e., that the system wasn't built with the
335156813Sru# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
336150159Scperciva	if ! which -s host; then
337158273Scperciva		: > serverlist_full
338158273Scperciva		return 1
339150159Scperciva	fi
340150159Scperciva
341158273Scperciva	echo -n "Looking up ${SERVERNAME} mirrors... "
342148871Scperciva
343148871Scperciva# Issue the SRV query and pull out the Priority, Weight, and Target fields.
344158245Scperciva# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
345158245Scperciva# "$name server selection ..."; we allow either format.
346158245Scperciva	MLIST="_http._tcp.${SERVERNAME}"
347158245Scperciva	host -t srv "${MLIST}" |
348158245Scperciva	    sed -nE "s/${MLIST} (has SRV record|server selection) //p" |
349158245Scperciva	    cut -f 1,2,4 -d ' ' |
350158273Scperciva	    sed -e 's/\.$//' |
351158273Scperciva	    sort > serverlist_full
352148871Scperciva
353148871Scperciva# If no records, give up -- we'll just use the server name we were given.
354158273Scperciva	if [ `wc -l < serverlist_full` -eq 0 ]; then
355158273Scperciva		echo "none found."
356158273Scperciva		return 1
357158273Scperciva	fi
358158273Scperciva
359158273Scperciva# Report how many mirrors we found.
360158273Scperciva	echo `wc -l < serverlist_full` "mirrors found."
361158274Scperciva
362158274Scperciva# Generate a random seed for use in picking mirrors.  If HTTP_PROXY
363158274Scperciva# is set, this will be used to generate the seed; otherwise, the seed
364158274Scperciva# will be random.
365158301Scperciva	if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
366158301Scperciva		RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
367158274Scperciva		    tr -d 'a-f' |
368158274Scperciva		    cut -c 1-9`
369158274Scperciva	else
370158274Scperciva		RANDVALUE=`jot -r 1 0 999999999`
371158274Scperciva	fi
372158273Scperciva}
373158273Scperciva
374158273Scperciva# Pick a mirror.  Returns 1 if we have run out of mirrors to try.
375158273Scpercivafetch_pick_server() {
376158273Scperciva# Generate a list of not-yet-tried mirrors
377158273Scperciva	sort serverlist_tried |
378158273Scperciva	    comm -23 serverlist_full - > serverlist
379158273Scperciva
380158273Scperciva# Have we run out of mirrors?
381148871Scperciva	if [ `wc -l < serverlist` -eq 0 ]; then
382158273Scperciva		echo "No mirrors remaining, giving up."
383158273Scperciva		return 1
384148871Scperciva	fi
385148871Scperciva
386148871Scperciva# Find the highest priority level (lowest numeric value).
387148871Scperciva	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
388148871Scperciva
389148871Scperciva# Add up the weights of the response lines at that priority level.
390148871Scperciva	SRV_WSUM=0;
391148871Scperciva	while read X; do
392148871Scperciva		case "$X" in
393148871Scperciva		${SRV_PRIORITY}\ *)
394148871Scperciva			SRV_W=`echo $X | cut -f 2 -d ' '`
395148871Scperciva			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
396148871Scperciva			;;
397148871Scperciva		esac
398148871Scperciva	done < serverlist
399148871Scperciva
400148871Scperciva# If all the weights are 0, pretend that they are all 1 instead.
401148871Scperciva	if [ ${SRV_WSUM} -eq 0 ]; then
402148871Scperciva		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
403148871Scperciva		SRV_W_ADD=1
404148871Scperciva	else
405148871Scperciva		SRV_W_ADD=0
406148871Scperciva	fi
407148871Scperciva
408158274Scperciva# Pick a value between 0 and the sum of the weights - 1
409158274Scperciva	SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
410148871Scperciva
411158273Scperciva# Read through the list of mirrors and set SERVERNAME.  Write the line
412158273Scperciva# corresponding to the mirror we selected into serverlist_tried so that
413158273Scperciva# we won't try it again.
414148871Scperciva	while read X; do
415148871Scperciva		case "$X" in
416148871Scperciva		${SRV_PRIORITY}\ *)
417148871Scperciva			SRV_W=`echo $X | cut -f 2 -d ' '`
418148871Scperciva			SRV_W=$(($SRV_W + $SRV_W_ADD))
419158274Scperciva			if [ $SRV_RND -lt $SRV_W ]; then
420148871Scperciva				SERVERNAME=`echo $X | cut -f 3 -d ' '`
421158273Scperciva				echo "$X" >> serverlist_tried
422148871Scperciva				break
423148871Scperciva			else
424148871Scperciva				SRV_RND=$(($SRV_RND - $SRV_W))
425148871Scperciva			fi
426148871Scperciva			;;
427148871Scperciva		esac
428148871Scperciva	done < serverlist
429148871Scperciva}
430148871Scperciva
431148871Scperciva# Check that we have a public key with an appropriate hash, or
432158273Scperciva# fetch the key if it doesn't exist.  Returns 1 if the key has
433158273Scperciva# not yet been fetched.
434148871Scpercivafetch_key() {
435148871Scperciva	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
436158273Scperciva		return 0
437148871Scperciva	fi
438148871Scperciva
439158273Scperciva	echo -n "Fetching public key from ${SERVERNAME}... "
440148871Scperciva	rm -f pub.ssl
441148871Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/pub.ssl \
442148871Scperciva	    2>${QUIETREDIR} || true
443148871Scperciva	if ! [ -r pub.ssl ]; then
444148871Scperciva		echo "failed."
445148871Scperciva		return 1
446148871Scperciva	fi
447148871Scperciva	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
448148871Scperciva		echo "key has incorrect hash."
449148871Scperciva		rm -f pub.ssl
450148871Scperciva		return 1
451148871Scperciva	fi
452148871Scperciva	echo "done."
453148871Scperciva}
454148871Scperciva
455148871Scperciva# Fetch a snapshot tag
456148871Scpercivafetch_tag() {
457148871Scperciva	rm -f snapshot.ssl tag.new
458148871Scperciva
459158273Scperciva	echo ${NDEBUG} "Fetching snapshot tag from ${SERVERNAME}... "
460158273Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/$1.ssl		\
461148871Scperciva	    2>${QUIETREDIR} || true
462148871Scperciva	if ! [ -r $1.ssl ]; then
463148871Scperciva		echo "failed."
464148871Scperciva		return 1
465148871Scperciva	fi
466148871Scperciva
467148871Scperciva	openssl rsautl -pubin -inkey pub.ssl -verify		\
468148871Scperciva	    < $1.ssl > tag.new 2>${QUIETREDIR} || true
469148871Scperciva	rm $1.ssl
470148871Scperciva
471148871Scperciva	if ! [ `wc -l < tag.new` = 1 ] ||
472148871Scperciva	    ! grep -qE "^portsnap\|[0-9]{10}\|[0-9a-f]{64}" tag.new; then
473148871Scperciva		echo "invalid snapshot tag."
474148871Scperciva		return 1
475148871Scperciva	fi
476148871Scperciva
477148871Scperciva	echo "done."
478148871Scperciva
479148871Scperciva	SNAPSHOTDATE=`cut -f 2 -d '|' < tag.new`
480148871Scperciva	SNAPSHOTHASH=`cut -f 3 -d '|' < tag.new`
481148871Scperciva}
482148871Scperciva
483148871Scperciva# Sanity-check the date on a snapshot tag
484148871Scpercivafetch_snapshot_tagsanity() {
485148871Scperciva	if [ `date "+%s"` -gt `expr ${SNAPSHOTDATE} + 31536000` ]; then
486148871Scperciva		echo "Snapshot appears to be more than a year old!"
487148871Scperciva		echo "(Is the system clock correct?)"
488149868Scperciva		echo "Cowardly refusing to proceed any further."
489148871Scperciva		return 1
490148871Scperciva	fi
491148871Scperciva	if [ `date "+%s"` -lt `expr ${SNAPSHOTDATE} - 86400` ]; then
492148871Scperciva		echo -n "Snapshot appears to have been created more than "
493148871Scperciva		echo "one day into the future!"
494148871Scperciva		echo "(Is the system clock correct?)"
495148871Scperciva		echo "Cowardly refusing to proceed any further."
496148871Scperciva		return 1
497148871Scperciva	fi
498148871Scperciva}
499148871Scperciva
500148871Scperciva# Sanity-check the date on a snapshot update tag
501148871Scpercivafetch_update_tagsanity() {
502148871Scperciva	fetch_snapshot_tagsanity || return 1
503148871Scperciva
504148871Scperciva	if [ ${OLDSNAPSHOTDATE} -gt ${SNAPSHOTDATE} ]; then
505148871Scperciva		echo -n "Latest snapshot on server is "
506148871Scperciva		echo "older than what we already have!"
507148871Scperciva		echo -n "Cowardly refusing to downgrade from "
508148871Scperciva		date -r ${OLDSNAPSHOTDATE}
509148879Scperciva		echo "to `date -r ${SNAPSHOTDATE}`."
510148871Scperciva		return 1
511148871Scperciva	fi
512148871Scperciva}
513148871Scperciva
514148871Scperciva# Compare old and new tags; return 1 if update is unnecessary
515148871Scpercivafetch_update_neededp() {
516148871Scperciva	if [ ${OLDSNAPSHOTDATE} -eq ${SNAPSHOTDATE} ]; then
517148871Scperciva		echo -n "Latest snapshot on server matches "
518148871Scperciva		echo "what we already have."
519148871Scperciva		echo "No updates needed."
520148871Scperciva		rm tag.new
521148871Scperciva		return 1
522148871Scperciva	fi
523148871Scperciva	if [ ${OLDSNAPSHOTHASH} = ${SNAPSHOTHASH} ]; then
524148871Scperciva		echo -n "Ports tree hasn't changed since "
525148871Scperciva		echo "last snapshot."
526148871Scperciva		echo "No updates needed."
527148871Scperciva		rm tag.new
528148871Scperciva		return 1
529148871Scperciva	fi
530148871Scperciva
531148871Scperciva	return 0
532148871Scperciva}
533148871Scperciva
534148871Scperciva# Fetch snapshot metadata file
535148871Scpercivafetch_metadata() {
536148871Scperciva	rm -f ${SNAPSHOTHASH} tINDEX.new
537148871Scperciva
538148871Scperciva	echo ${NDEBUG} "Fetching snapshot metadata... "
539226312Sjilles	fetch ${QUIETFLAG} http://${SERVERNAME}/t/${SNAPSHOTHASH} \
540148871Scperciva	    2>${QUIETREDIR} || return
541226312Sjilles	if [ "`${SHA256} -q ${SNAPSHOTHASH}`" != ${SNAPSHOTHASH} ]; then
542148871Scperciva		echo "snapshot metadata corrupt."
543148871Scperciva		return 1
544148871Scperciva	fi
545148871Scperciva	mv ${SNAPSHOTHASH} tINDEX.new
546148871Scperciva	echo "done."
547148871Scperciva}
548148871Scperciva
549148871Scperciva# Warn user about bogus metadata
550148871Scpercivafetch_metadata_freakout() {
551148871Scperciva	echo
552148871Scperciva	echo "Portsnap metadata is correctly signed, but contains"
553148871Scperciva	echo "at least one line which appears bogus."
554148871Scperciva	echo "Cowardly refusing to proceed any further."
555148871Scperciva}
556148871Scperciva
557148871Scperciva# Sanity-check a snapshot metadata file
558148871Scpercivafetch_metadata_sanity() {
559148871Scperciva	if grep -qvE "^[0-9A-Z.]+\|[0-9a-f]{64}$" tINDEX.new; then
560148871Scperciva		fetch_metadata_freakout
561148871Scperciva		return 1
562148871Scperciva	fi
563148871Scperciva	if [ `look INDEX tINDEX.new | wc -l` != 1 ]; then
564148871Scperciva		echo
565148871Scperciva		echo "Portsnap metadata appears bogus."
566148871Scperciva		echo "Cowardly refusing to proceed any further."
567148871Scperciva		return 1
568148871Scperciva	fi
569148871Scperciva}
570148871Scperciva
571148871Scperciva# Take a list of ${oldhash}|${newhash} and output a list of needed patches
572148871Scpercivafetch_make_patchlist() {
573148871Scperciva	grep -vE "^([0-9a-f]{64})\|\1$" | 
574148871Scperciva		while read LINE; do
575148871Scperciva			X=`echo ${LINE} | cut -f 1 -d '|'`
576148871Scperciva			Y=`echo ${LINE} | cut -f 2 -d '|'`
577148871Scperciva			if [ -f "files/${Y}.gz" ]; then continue; fi
578148871Scperciva			if [ ! -f "files/${X}.gz" ]; then continue; fi
579148871Scperciva			echo "${LINE}"
580148871Scperciva		done
581148871Scperciva}
582148871Scperciva
583148871Scperciva# Print user-friendly progress statistics
584148871Scpercivafetch_progress() {
585148871Scperciva	LNC=0
586148871Scperciva	while read x; do
587148871Scperciva		LNC=$(($LNC + 1))
588148871Scperciva		if [ $(($LNC % 10)) = 0 ]; then
589149023Scperciva			echo -n $LNC
590148871Scperciva		elif [ $(($LNC % 2)) = 0 ]; then
591148871Scperciva			echo -n .
592148871Scperciva		fi
593148871Scperciva	done
594148871Scperciva	echo -n " "
595148871Scperciva}
596148871Scperciva
597148871Scperciva# Sanity-check an index file
598148871Scpercivafetch_index_sanity() {
599148871Scperciva	if grep -qvE "^[-_+./@0-9A-Za-z]+\|[0-9a-f]{64}$" INDEX.new ||
600148871Scperciva	    fgrep -q "./" INDEX.new; then
601148871Scperciva		fetch_metadata_freakout
602148871Scperciva		return 1
603148871Scperciva	fi
604148871Scperciva}
605148871Scperciva
606148871Scperciva# Verify a list of files
607148871Scpercivafetch_snapshot_verify() {
608148871Scperciva	while read F; do
609226312Sjilles		if [ "`gunzip -c snap/${F} | ${SHA256} -q`" != ${F} ]; then
610148871Scperciva			echo "snapshot corrupt."
611148871Scperciva			return 1
612148871Scperciva		fi
613148871Scperciva	done
614148871Scperciva	return 0
615148871Scperciva}
616148871Scperciva
617148871Scperciva# Fetch a snapshot tarball, extract, and verify.
618148871Scpercivafetch_snapshot() {
619158273Scperciva	while ! fetch_tag snapshot; do
620158273Scperciva		fetch_pick_server || return 1
621158273Scperciva	done
622148871Scperciva	fetch_snapshot_tagsanity || return 1
623148871Scperciva	fetch_metadata || return 1
624148871Scperciva	fetch_metadata_sanity || return 1
625148871Scperciva
626148871Scperciva	rm -rf snap/
627148871Scperciva
628148871Scperciva# Don't ask fetch(1) to be quiet -- downloading a snapshot of ~ 35MB will
629148871Scperciva# probably take a while, so the progrees reports that fetch(1) generates
630148871Scperciva# will be useful for keeping the users' attention from drifting.
631148871Scperciva	echo "Fetching snapshot generated at `date -r ${SNAPSHOTDATE}`:"
632154693Scperciva	fetch -r http://${SERVERNAME}/s/${SNAPSHOTHASH}.tgz || return 1
633148871Scperciva
634148871Scperciva	echo -n "Extracting snapshot... "
635216575Ssimon	tar -xz --numeric-owner -f ${SNAPSHOTHASH}.tgz snap/ || return 1
636148871Scperciva	rm ${SNAPSHOTHASH}.tgz
637148871Scperciva	echo "done."
638148871Scperciva
639148871Scperciva	echo -n "Verifying snapshot integrity... "
640148871Scperciva# Verify the metadata files
641148871Scperciva	cut -f 2 -d '|' tINDEX.new | fetch_snapshot_verify || return 1
642148871Scperciva# Extract the index
643148871Scperciva	rm -f INDEX.new
644148871Scperciva	gunzip -c snap/`look INDEX tINDEX.new |
645148871Scperciva	    cut -f 2 -d '|'`.gz > INDEX.new
646148871Scperciva	fetch_index_sanity || return 1
647148871Scperciva# Verify the snapshot contents
648148871Scperciva	cut -f 2 -d '|' INDEX.new | fetch_snapshot_verify || return 1
649148871Scperciva	echo "done."
650148871Scperciva
651148871Scperciva# Move files into their proper locations
652148871Scperciva	rm -f tag INDEX tINDEX
653148871Scperciva	rm -rf files
654148871Scperciva	mv tag.new tag
655148871Scperciva	mv tINDEX.new tINDEX
656148871Scperciva	mv INDEX.new INDEX
657148871Scperciva	mv snap/ files/
658148871Scperciva
659148871Scperciva	return 0
660148871Scperciva}
661148871Scperciva
662148871Scperciva# Update a compressed snapshot
663148871Scpercivafetch_update() {
664148871Scperciva	rm -f patchlist diff OLD NEW filelist INDEX.new
665148871Scperciva
666148871Scperciva	OLDSNAPSHOTDATE=`cut -f 2 -d '|' < tag`
667148871Scperciva	OLDSNAPSHOTHASH=`cut -f 3 -d '|' < tag`
668148871Scperciva
669158273Scperciva	while ! fetch_tag latest; do
670158273Scperciva		fetch_pick_server || return 1
671158273Scperciva	done
672148871Scperciva	fetch_update_tagsanity || return 1
673148871Scperciva	fetch_update_neededp || return 0
674148871Scperciva	fetch_metadata || return 1
675148871Scperciva	fetch_metadata_sanity || return 1
676148871Scperciva
677148871Scperciva	echo -n "Updating from `date -r ${OLDSNAPSHOTDATE}` "
678148871Scperciva	echo "to `date -r ${SNAPSHOTDATE}`."
679148871Scperciva
680148871Scperciva# Generate a list of wanted metadata patches
681148871Scperciva	join -t '|' -o 1.2,2.2 tINDEX tINDEX.new |
682148871Scperciva	    fetch_make_patchlist > patchlist
683148871Scperciva
684148871Scperciva# Attempt to fetch metadata patches
685148871Scperciva	echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
686148871Scperciva	echo ${NDEBUG} "metadata patches.${DDSTATS}"
687148871Scperciva	tr '|' '-' < patchlist |
688148871Scperciva	    lam -s "tp/" - -s ".gz" |
689148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
690148871Scperciva	    2>${STATSREDIR} | fetch_progress
691148871Scperciva	echo "done."
692148871Scperciva
693148871Scperciva# Attempt to apply metadata patches
694148871Scperciva	echo -n "Applying metadata patches... "
695148871Scperciva	while read LINE; do
696148871Scperciva		X=`echo ${LINE} | cut -f 1 -d '|'`
697148871Scperciva		Y=`echo ${LINE} | cut -f 2 -d '|'`
698148871Scperciva		if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
699148871Scperciva		gunzip -c < ${X}-${Y}.gz > diff
700148871Scperciva		gunzip -c < files/${X}.gz > OLD
701148871Scperciva		cut -c 2- diff | join -t '|' -v 2 - OLD > ptmp
702148871Scperciva		grep '^\+' diff | cut -c 2- |
703148871Scperciva		    sort -k 1,1 -t '|' -m - ptmp > NEW
704148871Scperciva		if [ `${SHA256} -q NEW` = ${Y} ]; then
705148871Scperciva			mv NEW files/${Y}
706148871Scperciva			gzip -n files/${Y}
707148871Scperciva		fi
708148871Scperciva		rm -f diff OLD NEW ${X}-${Y}.gz ptmp
709148871Scperciva	done < patchlist 2>${QUIETREDIR}
710148871Scperciva	echo "done."
711148871Scperciva
712148871Scperciva# Update metadata without patches
713148871Scperciva	join -t '|' -v 2 tINDEX tINDEX.new |
714148871Scperciva	    cut -f 2 -d '|' /dev/stdin patchlist |
715148871Scperciva		while read Y; do
716148871Scperciva			if [ ! -f "files/${Y}.gz" ]; then
717148871Scperciva				echo ${Y};
718148871Scperciva			fi
719148871Scperciva		done > filelist
720148871Scperciva	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
721148871Scperciva	echo ${NDEBUG} "metadata files... "
722148871Scperciva	lam -s "f/" - -s ".gz" < filelist |
723148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
724148871Scperciva	    2>${QUIETREDIR}
725148871Scperciva
726148871Scperciva	while read Y; do
727148871Scperciva		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
728148871Scperciva			mv ${Y}.gz files/${Y}.gz
729148871Scperciva		else
730148871Scperciva			echo "metadata is corrupt."
731148871Scperciva			return 1
732148871Scperciva		fi
733148871Scperciva	done < filelist
734148871Scperciva	echo "done."
735148871Scperciva
736148871Scperciva# Extract the index
737148871Scperciva	gunzip -c files/`look INDEX tINDEX.new |
738148871Scperciva	    cut -f 2 -d '|'`.gz > INDEX.new
739148871Scperciva	fetch_index_sanity || return 1
740148871Scperciva
741149824Scperciva# If we have decided to refuse certain updates, construct a hybrid index which
742149824Scperciva# is equal to the old index for parts of the tree which we don't want to
743149824Scperciva# update, and equal to the new index for parts of the tree which gets updates.
744149824Scperciva# This means that we should always have a "complete snapshot" of the ports
745149824Scperciva# tree -- with the caveat that it isn't actually a snapshot.
746149824Scperciva	if [ ! -z "${REFUSE}" ]; then
747149824Scperciva		echo "Refusing to download updates for ${REFUSE}"	\
748149824Scperciva		    >${QUIETREDIR}
749149824Scperciva
750149824Scperciva		grep -Ev "${REFUSE}" INDEX.new > INDEX.tmp
751149824Scperciva		grep -E "${REFUSE}" INDEX |
752149824Scperciva		    sort -m -k 1,1 -t '|' - INDEX.tmp > INDEX.new
753149824Scperciva		rm -f INDEX.tmp
754149824Scperciva	fi
755149824Scperciva
756148871Scperciva# Generate a list of wanted ports patches
757148871Scperciva	join -t '|' -o 1.2,2.2 INDEX INDEX.new |
758148871Scperciva	    fetch_make_patchlist > patchlist
759148871Scperciva
760148871Scperciva# Attempt to fetch ports patches
761148871Scperciva	echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
762148871Scperciva	echo ${NDEBUG} "patches.${DDSTATS}"
763148871Scperciva	tr '|' '-' < patchlist | lam -s "bp/" - |
764148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
765148871Scperciva	    2>${STATSREDIR} | fetch_progress
766148871Scperciva	echo "done."
767148871Scperciva
768148871Scperciva# Attempt to apply ports patches
769148871Scperciva	echo -n "Applying patches... "
770148871Scperciva	while read LINE; do
771148871Scperciva		X=`echo ${LINE} | cut -f 1 -d '|'`
772148871Scperciva		Y=`echo ${LINE} | cut -f 2 -d '|'`
773148871Scperciva		if [ ! -f "${X}-${Y}" ]; then continue; fi
774148871Scperciva		gunzip -c < files/${X}.gz > OLD
775148871Scperciva		${BSPATCH} OLD NEW ${X}-${Y}
776148871Scperciva		if [ `${SHA256} -q NEW` = ${Y} ]; then
777148871Scperciva			mv NEW files/${Y}
778148871Scperciva			gzip -n files/${Y}
779148871Scperciva		fi
780148871Scperciva		rm -f diff OLD NEW ${X}-${Y}
781148871Scperciva	done < patchlist 2>${QUIETREDIR}
782148871Scperciva	echo "done."
783148871Scperciva
784148871Scperciva# Update ports without patches
785148871Scperciva	join -t '|' -v 2 INDEX INDEX.new |
786148871Scperciva	    cut -f 2 -d '|' /dev/stdin patchlist |
787148871Scperciva		while read Y; do
788148871Scperciva			if [ ! -f "files/${Y}.gz" ]; then
789148871Scperciva				echo ${Y};
790148871Scperciva			fi
791148871Scperciva		done > filelist
792148871Scperciva	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
793148871Scperciva	echo ${NDEBUG} "new ports or files... "
794148871Scperciva	lam -s "f/" - -s ".gz" < filelist |
795148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
796148871Scperciva	    2>${QUIETREDIR}
797148871Scperciva
798148871Scperciva	while read Y; do
799148871Scperciva		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
800148871Scperciva			mv ${Y}.gz files/${Y}.gz
801148871Scperciva		else
802148871Scperciva			echo "snapshot is corrupt."
803148871Scperciva			return 1
804148871Scperciva		fi
805148871Scperciva	done < filelist
806148871Scperciva	echo "done."
807148871Scperciva
808148871Scperciva# Remove files which are no longer needed
809148871Scperciva	cut -f 2 -d '|' tINDEX INDEX | sort > oldfiles
810148871Scperciva	cut -f 2 -d '|' tINDEX.new INDEX.new | sort | comm -13 - oldfiles |
811148871Scperciva	    lam -s "files/" - -s ".gz" | xargs rm -f
812148871Scperciva	rm patchlist filelist oldfiles
813148871Scperciva
814148871Scperciva# We're done!
815148871Scperciva	mv INDEX.new INDEX
816148871Scperciva	mv tINDEX.new tINDEX
817148871Scperciva	mv tag.new tag
818148871Scperciva
819148871Scperciva	return 0
820148871Scperciva}
821148871Scperciva
822148871Scperciva# Do the actual work involved in "fetch" / "cron".
823148871Scpercivafetch_run() {
824158273Scperciva	fetch_pick_server_init && fetch_pick_server
825148871Scperciva
826158273Scperciva	while ! fetch_key; do
827158273Scperciva		fetch_pick_server || return 1
828158273Scperciva	done
829148871Scperciva
830148871Scperciva	if ! [ -d files -a -r tag -a -r INDEX -a -r tINDEX ]; then
831148871Scperciva		fetch_snapshot || return 1
832148871Scperciva	fi
833148871Scperciva	fetch_update || return 1
834148871Scperciva}
835148871Scperciva
836148871Scperciva# Build a ports INDEX file
837148871Scpercivaextract_make_index() {
838179073Scperciva	if ! look $1 ${WORKDIR}/tINDEX > /dev/null; then
839179073Scperciva		echo -n "$1 not provided by portsnap server; "
840179073Scperciva		echo "$2 not being generated."
841179073Scperciva	else
842148871Scperciva	gunzip -c "${WORKDIR}/files/`look $1 ${WORKDIR}/tINDEX |
843158523Scperciva	    cut -f 2 -d '|'`.gz" |
844158523Scperciva	    cat - ${LOCALDESC} |
845158523Scperciva	    ${MKINDEX} /dev/stdin > ${PORTSDIR}/$2
846179073Scperciva	fi
847148871Scperciva}
848148871Scperciva
849148871Scperciva# Create INDEX, INDEX-5, INDEX-6
850148871Scpercivaextract_indices() {
851148871Scperciva	echo -n "Building new INDEX files... "
852179073Scperciva	for PAIR in ${INDEXPAIRS}; do
853179073Scperciva		INDEXFILE=`echo ${PAIR} | cut -f 1 -d '|'`
854179073Scperciva		DESCRIBEFILE=`echo ${PAIR} | cut -f 2 -d '|'`
855179073Scperciva		extract_make_index ${DESCRIBEFILE} ${INDEXFILE} || return 1
856179073Scperciva	done
857148871Scperciva	echo "done."
858148871Scperciva}
859148871Scperciva
860149824Scperciva# Create .portsnap.INDEX; if we are REFUSEing to touch certain directories,
861149824Scperciva# merge the values from any exiting .portsnap.INDEX file.
862148871Scpercivaextract_metadata() {
863149824Scperciva	if [ -z "${REFUSE}" ]; then
864149984Scperciva		sort ${WORKDIR}/INDEX > ${PORTSDIR}/.portsnap.INDEX
865149824Scperciva	elif [ -f ${PORTSDIR}/.portsnap.INDEX ]; then
866149824Scperciva		grep -E "${REFUSE}" ${PORTSDIR}/.portsnap.INDEX	\
867149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX.tmp
868149824Scperciva		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort |
869149824Scperciva		    sort -m - ${PORTSDIR}/.portsnap.INDEX.tmp	\
870149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX
871149824Scperciva		rm -f ${PORTSDIR}/.portsnap.INDEX.tmp
872149824Scperciva	else
873149824Scperciva		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort \
874149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX
875149824Scperciva	fi
876148871Scperciva}
877148871Scperciva
878148871Scperciva# Do the actual work involved in "extract"
879148871Scpercivaextract_run() {
880154088Scperciva	mkdir -p ${PORTSDIR} || return 1
881154088Scperciva
882149824Scperciva	if !
883149824Scperciva		if ! [ -z "${EXTRACTPATH}" ]; then
884149824Scperciva			grep "^${EXTRACTPATH}" ${WORKDIR}/INDEX
885149824Scperciva		elif ! [ -z "${REFUSE}" ]; then
886149824Scperciva			grep -vE "${REFUSE}" ${WORKDIR}/INDEX
887149824Scperciva		else
888149824Scperciva			cat ${WORKDIR}/INDEX
889159062Scperciva		fi | tr '|' ' ' | while read FILE HASH; do
890148871Scperciva		echo ${PORTSDIR}/${FILE}
891148871Scperciva		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
892148871Scperciva			echo "files/${HASH}.gz not found -- snapshot corrupt."
893148871Scperciva			return 1
894148871Scperciva		fi
895148871Scperciva		case ${FILE} in
896148871Scperciva		*/)
897159062Scperciva			rm -rf ${PORTSDIR}/${FILE%/}
898148871Scperciva			mkdir -p ${PORTSDIR}/${FILE}
899216575Ssimon			tar -xz --numeric-owner -f ${WORKDIR}/files/${HASH}.gz \
900148871Scperciva			    -C ${PORTSDIR}/${FILE}
901148871Scperciva			;;
902148871Scperciva		*)
903148871Scperciva			rm -f ${PORTSDIR}/${FILE}
904216575Ssimon			tar -xz --numeric-owner -f ${WORKDIR}/files/${HASH}.gz \
905148871Scperciva			    -C ${PORTSDIR} ${FILE}
906148871Scperciva			;;
907148871Scperciva		esac
908149041Scperciva	done; then
909149041Scperciva		return 1
910149041Scperciva	fi
911148871Scperciva	if [ ! -z "${EXTRACTPATH}" ]; then
912148871Scperciva		return 0;
913148871Scperciva	fi
914148871Scperciva
915148871Scperciva	extract_metadata
916148871Scperciva	extract_indices
917148871Scperciva}
918148871Scperciva
919148871Scperciva# Do the actual work involved in "update"
920148871Scpercivaupdate_run() {
921148871Scperciva	if ! [ -z "${INDEXONLY}" ]; then
922148871Scperciva		extract_indices >/dev/null || return 1
923148871Scperciva		return 0
924148871Scperciva	fi
925148871Scperciva
926148981Scperciva	if sort ${WORKDIR}/INDEX |
927149042Scperciva	    cmp -s ${PORTSDIR}/.portsnap.INDEX -; then
928148958Scperciva		echo "Ports tree is already up to date."
929148958Scperciva		return 0
930148958Scperciva	fi
931148958Scperciva
932149824Scperciva# If we are REFUSEing to touch certain directories, don't remove files
933149824Scperciva# from those directories (even if they are out of date)
934148871Scperciva	echo -n "Removing old files and directories... "
935149824Scperciva	if ! [ -z "${REFUSE}" ]; then 
936149824Scperciva		sort ${WORKDIR}/INDEX |
937149824Scperciva		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
938149824Scperciva		    grep -vE "${REFUSE}" |
939158473Scperciva		    lam -s "${PORTSDIR}/" - |
940158473Scperciva		    sed -e 's|/$||' | xargs rm -rf
941149824Scperciva	else
942149824Scperciva		sort ${WORKDIR}/INDEX |
943149824Scperciva		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
944158473Scperciva		    lam -s "${PORTSDIR}/" - |
945158473Scperciva		    sed -e 's|/$||' | xargs rm -rf
946149824Scperciva	fi
947148871Scperciva	echo "done."
948148871Scperciva
949148871Scperciva# Install new files
950148871Scperciva	echo "Extracting new files:"
951149824Scperciva	if !
952149824Scperciva		if ! [ -z "${REFUSE}" ]; then
953149824Scperciva			grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort
954149824Scperciva		else
955149824Scperciva			sort ${WORKDIR}/INDEX
956149824Scperciva		fi |
957149041Scperciva	    comm -13 ${PORTSDIR}/.portsnap.INDEX - |
958148871Scperciva	    while read LINE; do
959148871Scperciva		FILE=`echo ${LINE} | cut -f 1 -d '|'`
960148871Scperciva		HASH=`echo ${LINE} | cut -f 2 -d '|'`
961148871Scperciva		echo ${PORTSDIR}/${FILE}
962148871Scperciva		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
963148871Scperciva			echo "files/${HASH}.gz not found -- snapshot corrupt."
964148871Scperciva			return 1
965148871Scperciva		fi
966148871Scperciva		case ${FILE} in
967148871Scperciva		*/)
968148871Scperciva			mkdir -p ${PORTSDIR}/${FILE}
969216575Ssimon			tar -xz --numeric-owner -f ${WORKDIR}/files/${HASH}.gz \
970148871Scperciva			    -C ${PORTSDIR}/${FILE}
971148871Scperciva			;;
972148871Scperciva		*)
973216575Ssimon			tar -xz --numeric-owner -f ${WORKDIR}/files/${HASH}.gz \
974148871Scperciva			    -C ${PORTSDIR} ${FILE}
975148871Scperciva			;;
976148871Scperciva		esac
977149041Scperciva	done; then
978149041Scperciva		return 1
979149041Scperciva	fi
980148871Scperciva
981148871Scperciva	extract_metadata
982148871Scperciva	extract_indices
983148871Scperciva}
984148871Scperciva
985148871Scperciva#### Main functions -- call parameter-handling and core functions
986148871Scperciva
987148871Scperciva# Using the command line, configuration file, and defaults,
988148871Scperciva# set all the parameters which are needed later.
989148871Scpercivaget_params() {
990148871Scperciva	init_params
991148871Scperciva	parse_cmdline $@
992148871Scperciva	sanity_conffile
993148871Scperciva	default_conffile
994148871Scperciva	parse_conffile
995148871Scperciva	default_params
996148871Scperciva}
997148871Scperciva
998148871Scperciva# Fetch command.  Make sure that we're being called
999148871Scperciva# interactively, then run fetch_check_params and fetch_run
1000148871Scpercivacmd_fetch() {
1001148871Scperciva	if [ ! -t 0 ]; then
1002148871Scperciva		echo -n "`basename $0` fetch should not "
1003148871Scperciva		echo "be run non-interactively."
1004148871Scperciva		echo "Run `basename $0` cron instead."
1005148871Scperciva		exit 1
1006148871Scperciva	fi
1007148871Scperciva	fetch_check_params
1008148871Scperciva	fetch_run || exit 1
1009148871Scperciva}
1010148871Scperciva
1011148871Scperciva# Cron command.  Make sure the parameters are sensible; wait
1012148871Scperciva# rand(3600) seconds; then fetch updates.  While fetching updates,
1013148871Scperciva# send output to a temporary file; only print that file if the
1014148871Scperciva# fetching failed.
1015148871Scpercivacmd_cron() {
1016148871Scperciva	fetch_check_params
1017148871Scperciva	sleep `jot -r 1 0 3600`
1018148871Scperciva
1019148871Scperciva	TMPFILE=`mktemp /tmp/portsnap.XXXXXX` || exit 1
1020148871Scperciva	if ! fetch_run >> ${TMPFILE}; then
1021148871Scperciva		cat ${TMPFILE}
1022148871Scperciva		rm ${TMPFILE}
1023148871Scperciva		exit 1
1024148871Scperciva	fi
1025148871Scperciva
1026148871Scperciva	rm ${TMPFILE}
1027148871Scperciva}
1028148871Scperciva
1029148871Scperciva# Extract command.  Make sure the parameters are sensible,
1030148871Scperciva# then extract the ports tree (or part thereof).
1031148871Scpercivacmd_extract() {
1032148871Scperciva	extract_check_params
1033148871Scperciva	extract_run || exit 1
1034148871Scperciva}
1035148871Scperciva
1036148871Scperciva# Update command.  Make sure the parameters are sensible,
1037148871Scperciva# then update the ports tree.
1038148871Scpercivacmd_update() {
1039148871Scperciva	update_check_params
1040148871Scperciva	update_run || exit 1
1041148871Scperciva}
1042148871Scperciva
1043201251Scperciva# Alfred command.  Run 'fetch' or 'cron' depending on
1044201251Scperciva# whether stdin is a terminal; then run 'update' or
1045201251Scperciva# 'extract' depending on whether ${PORTSDIR} exists.
1046201251Scpercivacmd_alfred() {
1047201251Scperciva	if [ -t 0 ]; then
1048201251Scperciva		cmd_fetch
1049201251Scperciva	else
1050201251Scperciva		cmd_cron
1051201251Scperciva	fi
1052201251Scperciva	if [ -d ${PORTSDIR} ]; then
1053201251Scperciva		cmd_update
1054201251Scperciva	else
1055201251Scperciva		cmd_extract
1056201251Scperciva	fi
1057201251Scperciva}
1058201251Scperciva
1059148871Scperciva#### Entry point
1060148871Scperciva
1061148871Scperciva# Make sure we find utilities from the base system
1062148871Scpercivaexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
1063148871Scperciva
1064163564Scperciva# Set LC_ALL in order to avoid problems with character ranges like [A-Z].
1065163564Scpercivaexport LC_ALL=C
1066163564Scperciva
1067148871Scpercivaget_params $@
1068149027Scpercivafor COMMAND in ${COMMANDS}; do
1069149027Scperciva	cmd_${COMMAND}
1070149027Scpercivadone
1071