portsnap.sh revision 159062
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: head/usr.sbin/portsnap/portsnap/portsnap.sh 159062 2006-05-30 07:08:41Z cperciva $
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			;;
143148871Scperciva		cron | extract | fetch | update)
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
202148871Scperciva	fi
203148871Scperciva}
204148871Scperciva
205148871Scperciva# If parameters have not been set, use default values
206148871Scpercivadefault_params() {
207148871Scperciva	_QUIETREDIR="/dev/null"
208148871Scperciva	_QUIETFLAG="-q"
209148871Scperciva	_STATSREDIR="/dev/stdout"
210148871Scperciva	_WORKDIR="/var/db/portsnap"
211148871Scperciva	_PORTSDIR="/usr/ports"
212148871Scperciva	_NDEBUG="-n"
213158523Scperciva	_LOCALDESC="/dev/null"
214158523Scperciva	for X in QUIETREDIR QUIETFLAG STATSREDIR WORKDIR PORTSDIR	\
215158523Scperciva	    NDEBUG LOCALDESC; do
216148871Scperciva		eval _=\$${X}
217148871Scperciva		eval __=\$_${X}
218148871Scperciva		if [ -z "${_}" ]; then
219148871Scperciva			eval ${X}=${__}
220148871Scperciva		fi
221148871Scperciva	done
222148871Scperciva}
223148871Scperciva
224148871Scperciva# Perform sanity checks and set some final parameters
225148871Scperciva# in preparation for fetching files.  Also chdir into
226148871Scperciva# the working directory.
227148871Scpercivafetch_check_params() {
228148871Scperciva	export HTTP_USER_AGENT="portsnap (${COMMAND}, `uname -r`)"
229148871Scperciva
230148871Scperciva	_SERVERNAME_z=\
231148871Scperciva"SERVERNAME must be given via command line or configuration file."
232148871Scperciva	_KEYPRINT_z="Key must be given via -k option or configuration file."
233148871Scperciva	_KEYPRINT_bad="Invalid key fingerprint: "
234148871Scperciva	_WORKDIR_bad="Directory does not exist or is not writable: "
235148871Scperciva
236148871Scperciva	if [ -z "${SERVERNAME}" ]; then
237148871Scperciva		echo -n "`basename $0`: "
238148871Scperciva		echo "${_SERVERNAME_z}"
239148871Scperciva		exit 1
240148871Scperciva	fi
241148871Scperciva	if [ -z "${KEYPRINT}" ]; then
242148871Scperciva		echo -n "`basename $0`: "
243148871Scperciva		echo "${_KEYPRINT_z}"
244148871Scperciva		exit 1
245148871Scperciva	fi
246148871Scperciva	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
247148871Scperciva		echo -n "`basename $0`: "
248148871Scperciva		echo -n "${_KEYPRINT_bad}"
249148871Scperciva		echo ${KEYPRINT}
250148871Scperciva		exit 1
251148871Scperciva	fi
252148871Scperciva	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
253148871Scperciva		echo -n "`basename $0`: "
254148871Scperciva		echo -n "${_WORKDIR_bad}"
255148871Scperciva		echo ${WORKDIR}
256148871Scperciva		exit 1
257148871Scperciva	fi
258148871Scperciva	cd ${WORKDIR} || exit 1
259148871Scperciva
260148871Scperciva	BSPATCH=/usr/bin/bspatch
261148871Scperciva	SHA256=/sbin/sha256
262148871Scperciva	PHTTPGET=/usr/libexec/phttpget
263148871Scperciva}
264148871Scperciva
265148871Scperciva# Perform sanity checks and set some final parameters
266148871Scperciva# in preparation for extracting or updating ${PORTSDIR}
267154088Scperciva# Complain if ${PORTSDIR} exists but is not writable,
268154088Scperciva# but don't complain if ${PORTSDIR} doesn't exist.
269148871Scpercivaextract_check_params() {
270148871Scperciva	_WORKDIR_bad="Directory does not exist: "
271154088Scperciva	_PORTSDIR_bad="Directory is not writable: "
272148871Scperciva
273148871Scperciva	if ! [ -d "${WORKDIR}" ]; then
274148871Scperciva		echo -n "`basename $0`: "
275148871Scperciva		echo -n "${_WORKDIR_bad}"
276148871Scperciva		echo ${WORKDIR}
277148871Scperciva		exit 1
278148871Scperciva	fi
279154088Scperciva	if [ -d "${PORTSDIR}" ] && ! [ -w "${PORTSDIR}" ]; then
280148871Scperciva		echo -n "`basename $0`: "
281148871Scperciva		echo -n "${_PORTSDIR_bad}"
282148871Scperciva		echo ${PORTSDIR}
283148871Scperciva		exit 1
284148871Scperciva	fi
285148871Scperciva
286148871Scperciva	if ! [ -d "${WORKDIR}/files" -a -r "${WORKDIR}/tag"	\
287148871Scperciva	    -a -r "${WORKDIR}/INDEX" -a -r "${WORKDIR}/tINDEX" ]; then
288148871Scperciva		echo "No snapshot available.  Try running"
289148871Scperciva		echo "# `basename $0` fetch"
290148871Scperciva		exit 1
291148871Scperciva	fi
292148871Scperciva
293148871Scperciva	MKINDEX=/usr/libexec/make_index
294148871Scperciva}
295148871Scperciva
296148871Scperciva# Perform sanity checks and set some final parameters
297148871Scperciva# in preparation for updating ${PORTSDIR}
298148871Scpercivaupdate_check_params() {
299148871Scperciva	extract_check_params
300148871Scperciva
301148871Scperciva	if ! [ -r ${PORTSDIR}/.portsnap.INDEX ]; then
302148871Scperciva		echo "${PORTSDIR} was not created by portsnap."
303148871Scperciva		echo -n "You must run '`basename $0` extract' before "
304148871Scperciva		echo "running '`basename $0` update'."
305148871Scperciva		exit 1
306148871Scperciva	fi
307148871Scperciva
308148871Scperciva}
309148871Scperciva
310148871Scperciva#### Core functionality -- the actual work gets done here
311148871Scperciva
312148871Scperciva# Use an SRV query to pick a server.  If the SRV query doesn't provide
313148871Scperciva# a useful answer, use the server name specified by the user.
314148871Scperciva# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
315148871Scperciva# from that; or if no servers are returned, use ${SERVERNAME}.
316148871Scperciva# This allows a user to specify "portsnap.freebsd.org" (in which case
317148871Scperciva# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
318148871Scperciva# (in which case portsnap will use that particular server, since there
319148871Scperciva# won't be an SRV entry for that name).
320148871Scperciva#
321158273Scperciva# We ignore the Port field, since we are always going to use port 80.
322158273Scperciva
323158273Scperciva# Fetch the mirror list, but do not pick a mirror yet.  Returns 1 if
324158273Scperciva# no mirrors are available for any reason.
325158273Scpercivafetch_pick_server_init() {
326158273Scperciva	: > serverlist_tried
327158273Scperciva
328150159Scperciva# Check that host(1) exists (i.e., that the system wasn't built with the
329156813Sru# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
330150159Scperciva	if ! which -s host; then
331158273Scperciva		: > serverlist_full
332158273Scperciva		return 1
333150159Scperciva	fi
334150159Scperciva
335158273Scperciva	echo -n "Looking up ${SERVERNAME} mirrors... "
336148871Scperciva
337148871Scperciva# Issue the SRV query and pull out the Priority, Weight, and Target fields.
338158245Scperciva# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
339158245Scperciva# "$name server selection ..."; we allow either format.
340158245Scperciva	MLIST="_http._tcp.${SERVERNAME}"
341158245Scperciva	host -t srv "${MLIST}" |
342158245Scperciva	    sed -nE "s/${MLIST} (has SRV record|server selection) //p" |
343158245Scperciva	    cut -f 1,2,4 -d ' ' |
344158273Scperciva	    sed -e 's/\.$//' |
345158273Scperciva	    sort > serverlist_full
346148871Scperciva
347148871Scperciva# If no records, give up -- we'll just use the server name we were given.
348158273Scperciva	if [ `wc -l < serverlist_full` -eq 0 ]; then
349158273Scperciva		echo "none found."
350158273Scperciva		return 1
351158273Scperciva	fi
352158273Scperciva
353158273Scperciva# Report how many mirrors we found.
354158273Scperciva	echo `wc -l < serverlist_full` "mirrors found."
355158274Scperciva
356158274Scperciva# Generate a random seed for use in picking mirrors.  If HTTP_PROXY
357158274Scperciva# is set, this will be used to generate the seed; otherwise, the seed
358158274Scperciva# will be random.
359158301Scperciva	if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
360158301Scperciva		RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
361158274Scperciva		    tr -d 'a-f' |
362158274Scperciva		    cut -c 1-9`
363158274Scperciva	else
364158274Scperciva		RANDVALUE=`jot -r 1 0 999999999`
365158274Scperciva	fi
366158273Scperciva}
367158273Scperciva
368158273Scperciva# Pick a mirror.  Returns 1 if we have run out of mirrors to try.
369158273Scpercivafetch_pick_server() {
370158273Scperciva# Generate a list of not-yet-tried mirrors
371158273Scperciva	sort serverlist_tried |
372158273Scperciva	    comm -23 serverlist_full - > serverlist
373158273Scperciva
374158273Scperciva# Have we run out of mirrors?
375148871Scperciva	if [ `wc -l < serverlist` -eq 0 ]; then
376158273Scperciva		echo "No mirrors remaining, giving up."
377158273Scperciva		return 1
378148871Scperciva	fi
379148871Scperciva
380148871Scperciva# Find the highest priority level (lowest numeric value).
381148871Scperciva	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
382148871Scperciva
383148871Scperciva# Add up the weights of the response lines at that priority level.
384148871Scperciva	SRV_WSUM=0;
385148871Scperciva	while read X; do
386148871Scperciva		case "$X" in
387148871Scperciva		${SRV_PRIORITY}\ *)
388148871Scperciva			SRV_W=`echo $X | cut -f 2 -d ' '`
389148871Scperciva			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
390148871Scperciva			;;
391148871Scperciva		esac
392148871Scperciva	done < serverlist
393148871Scperciva
394148871Scperciva# If all the weights are 0, pretend that they are all 1 instead.
395148871Scperciva	if [ ${SRV_WSUM} -eq 0 ]; then
396148871Scperciva		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
397148871Scperciva		SRV_W_ADD=1
398148871Scperciva	else
399148871Scperciva		SRV_W_ADD=0
400148871Scperciva	fi
401148871Scperciva
402158274Scperciva# Pick a value between 0 and the sum of the weights - 1
403158274Scperciva	SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
404148871Scperciva
405158273Scperciva# Read through the list of mirrors and set SERVERNAME.  Write the line
406158273Scperciva# corresponding to the mirror we selected into serverlist_tried so that
407158273Scperciva# we won't try it again.
408148871Scperciva	while read X; do
409148871Scperciva		case "$X" in
410148871Scperciva		${SRV_PRIORITY}\ *)
411148871Scperciva			SRV_W=`echo $X | cut -f 2 -d ' '`
412148871Scperciva			SRV_W=$(($SRV_W + $SRV_W_ADD))
413158274Scperciva			if [ $SRV_RND -lt $SRV_W ]; then
414148871Scperciva				SERVERNAME=`echo $X | cut -f 3 -d ' '`
415158273Scperciva				echo "$X" >> serverlist_tried
416148871Scperciva				break
417148871Scperciva			else
418148871Scperciva				SRV_RND=$(($SRV_RND - $SRV_W))
419148871Scperciva			fi
420148871Scperciva			;;
421148871Scperciva		esac
422148871Scperciva	done < serverlist
423148871Scperciva}
424148871Scperciva
425148871Scperciva# Check that we have a public key with an appropriate hash, or
426158273Scperciva# fetch the key if it doesn't exist.  Returns 1 if the key has
427158273Scperciva# not yet been fetched.
428148871Scpercivafetch_key() {
429148871Scperciva	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
430158273Scperciva		return 0
431148871Scperciva	fi
432148871Scperciva
433158273Scperciva	echo -n "Fetching public key from ${SERVERNAME}... "
434148871Scperciva	rm -f pub.ssl
435148871Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/pub.ssl \
436148871Scperciva	    2>${QUIETREDIR} || true
437148871Scperciva	if ! [ -r pub.ssl ]; then
438148871Scperciva		echo "failed."
439148871Scperciva		return 1
440148871Scperciva	fi
441148871Scperciva	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
442148871Scperciva		echo "key has incorrect hash."
443148871Scperciva		rm -f pub.ssl
444148871Scperciva		return 1
445148871Scperciva	fi
446148871Scperciva	echo "done."
447148871Scperciva}
448148871Scperciva
449148871Scperciva# Fetch a snapshot tag
450148871Scpercivafetch_tag() {
451148871Scperciva	rm -f snapshot.ssl tag.new
452148871Scperciva
453158273Scperciva	echo ${NDEBUG} "Fetching snapshot tag from ${SERVERNAME}... "
454158273Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/$1.ssl		\
455148871Scperciva	    2>${QUIETREDIR} || true
456148871Scperciva	if ! [ -r $1.ssl ]; then
457148871Scperciva		echo "failed."
458148871Scperciva		return 1
459148871Scperciva	fi
460148871Scperciva
461148871Scperciva	openssl rsautl -pubin -inkey pub.ssl -verify		\
462148871Scperciva	    < $1.ssl > tag.new 2>${QUIETREDIR} || true
463148871Scperciva	rm $1.ssl
464148871Scperciva
465148871Scperciva	if ! [ `wc -l < tag.new` = 1 ] ||
466148871Scperciva	    ! grep -qE "^portsnap\|[0-9]{10}\|[0-9a-f]{64}" tag.new; then
467148871Scperciva		echo "invalid snapshot tag."
468148871Scperciva		return 1
469148871Scperciva	fi
470148871Scperciva
471148871Scperciva	echo "done."
472148871Scperciva
473148871Scperciva	SNAPSHOTDATE=`cut -f 2 -d '|' < tag.new`
474148871Scperciva	SNAPSHOTHASH=`cut -f 3 -d '|' < tag.new`
475148871Scperciva}
476148871Scperciva
477148871Scperciva# Sanity-check the date on a snapshot tag
478148871Scpercivafetch_snapshot_tagsanity() {
479148871Scperciva	if [ `date "+%s"` -gt `expr ${SNAPSHOTDATE} + 31536000` ]; then
480148871Scperciva		echo "Snapshot appears to be more than a year old!"
481148871Scperciva		echo "(Is the system clock correct?)"
482149868Scperciva		echo "Cowardly refusing to proceed any further."
483148871Scperciva		return 1
484148871Scperciva	fi
485148871Scperciva	if [ `date "+%s"` -lt `expr ${SNAPSHOTDATE} - 86400` ]; then
486148871Scperciva		echo -n "Snapshot appears to have been created more than "
487148871Scperciva		echo "one day into the future!"
488148871Scperciva		echo "(Is the system clock correct?)"
489148871Scperciva		echo "Cowardly refusing to proceed any further."
490148871Scperciva		return 1
491148871Scperciva	fi
492148871Scperciva}
493148871Scperciva
494148871Scperciva# Sanity-check the date on a snapshot update tag
495148871Scpercivafetch_update_tagsanity() {
496148871Scperciva	fetch_snapshot_tagsanity || return 1
497148871Scperciva
498148871Scperciva	if [ ${OLDSNAPSHOTDATE} -gt ${SNAPSHOTDATE} ]; then
499148871Scperciva		echo -n "Latest snapshot on server is "
500148871Scperciva		echo "older than what we already have!"
501148871Scperciva		echo -n "Cowardly refusing to downgrade from "
502148871Scperciva		date -r ${OLDSNAPSHOTDATE}
503148879Scperciva		echo "to `date -r ${SNAPSHOTDATE}`."
504148871Scperciva		return 1
505148871Scperciva	fi
506148871Scperciva}
507148871Scperciva
508148871Scperciva# Compare old and new tags; return 1 if update is unnecessary
509148871Scpercivafetch_update_neededp() {
510148871Scperciva	if [ ${OLDSNAPSHOTDATE} -eq ${SNAPSHOTDATE} ]; then
511148871Scperciva		echo -n "Latest snapshot on server matches "
512148871Scperciva		echo "what we already have."
513148871Scperciva		echo "No updates needed."
514148871Scperciva		rm tag.new
515148871Scperciva		return 1
516148871Scperciva	fi
517148871Scperciva	if [ ${OLDSNAPSHOTHASH} = ${SNAPSHOTHASH} ]; then
518148871Scperciva		echo -n "Ports tree hasn't changed since "
519148871Scperciva		echo "last snapshot."
520148871Scperciva		echo "No updates needed."
521148871Scperciva		rm tag.new
522148871Scperciva		return 1
523148871Scperciva	fi
524148871Scperciva
525148871Scperciva	return 0
526148871Scperciva}
527148871Scperciva
528148871Scperciva# Fetch snapshot metadata file
529148871Scpercivafetch_metadata() {
530148871Scperciva	rm -f ${SNAPSHOTHASH} tINDEX.new
531148871Scperciva
532148871Scperciva	echo ${NDEBUG} "Fetching snapshot metadata... "
533148871Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/t/${SNAPSHOTHASH}
534148871Scperciva	    2>${QUIETREDIR} || return
535148871Scperciva	if [ `${SHA256} -q ${SNAPSHOTHASH}` != ${SNAPSHOTHASH} ]; then
536148871Scperciva		echo "snapshot metadata corrupt."
537148871Scperciva		return 1
538148871Scperciva	fi
539148871Scperciva	mv ${SNAPSHOTHASH} tINDEX.new
540148871Scperciva	echo "done."
541148871Scperciva}
542148871Scperciva
543148871Scperciva# Warn user about bogus metadata
544148871Scpercivafetch_metadata_freakout() {
545148871Scperciva	echo
546148871Scperciva	echo "Portsnap metadata is correctly signed, but contains"
547148871Scperciva	echo "at least one line which appears bogus."
548148871Scperciva	echo "Cowardly refusing to proceed any further."
549148871Scperciva}
550148871Scperciva
551148871Scperciva# Sanity-check a snapshot metadata file
552148871Scpercivafetch_metadata_sanity() {
553148871Scperciva	if grep -qvE "^[0-9A-Z.]+\|[0-9a-f]{64}$" tINDEX.new; then
554148871Scperciva		fetch_metadata_freakout
555148871Scperciva		return 1
556148871Scperciva	fi
557148871Scperciva	if [ `look INDEX tINDEX.new | wc -l` != 1 ]; then
558148871Scperciva		echo
559148871Scperciva		echo "Portsnap metadata appears bogus."
560148871Scperciva		echo "Cowardly refusing to proceed any further."
561148871Scperciva		return 1
562148871Scperciva	fi
563148871Scperciva}
564148871Scperciva
565148871Scperciva# Take a list of ${oldhash}|${newhash} and output a list of needed patches
566148871Scpercivafetch_make_patchlist() {
567148871Scperciva	grep -vE "^([0-9a-f]{64})\|\1$" | 
568148871Scperciva		while read LINE; do
569148871Scperciva			X=`echo ${LINE} | cut -f 1 -d '|'`
570148871Scperciva			Y=`echo ${LINE} | cut -f 2 -d '|'`
571148871Scperciva			if [ -f "files/${Y}.gz" ]; then continue; fi
572148871Scperciva			if [ ! -f "files/${X}.gz" ]; then continue; fi
573148871Scperciva			echo "${LINE}"
574148871Scperciva		done
575148871Scperciva}
576148871Scperciva
577148871Scperciva# Print user-friendly progress statistics
578148871Scpercivafetch_progress() {
579148871Scperciva	LNC=0
580148871Scperciva	while read x; do
581148871Scperciva		LNC=$(($LNC + 1))
582148871Scperciva		if [ $(($LNC % 10)) = 0 ]; then
583149023Scperciva			echo -n $LNC
584148871Scperciva		elif [ $(($LNC % 2)) = 0 ]; then
585148871Scperciva			echo -n .
586148871Scperciva		fi
587148871Scperciva	done
588148871Scperciva	echo -n " "
589148871Scperciva}
590148871Scperciva
591148871Scperciva# Sanity-check an index file
592148871Scpercivafetch_index_sanity() {
593148871Scperciva	if grep -qvE "^[-_+./@0-9A-Za-z]+\|[0-9a-f]{64}$" INDEX.new ||
594148871Scperciva	    fgrep -q "./" INDEX.new; then
595148871Scperciva		fetch_metadata_freakout
596148871Scperciva		return 1
597148871Scperciva	fi
598148871Scperciva}
599148871Scperciva
600148871Scperciva# Verify a list of files
601148871Scpercivafetch_snapshot_verify() {
602148871Scperciva	while read F; do
603148871Scperciva		if [ `gunzip -c snap/${F} | ${SHA256} -q` != ${F} ]; then
604148871Scperciva			echo "snapshot corrupt."
605148871Scperciva			return 1
606148871Scperciva		fi
607148871Scperciva	done
608148871Scperciva	return 0
609148871Scperciva}
610148871Scperciva
611148871Scperciva# Fetch a snapshot tarball, extract, and verify.
612148871Scpercivafetch_snapshot() {
613158273Scperciva	while ! fetch_tag snapshot; do
614158273Scperciva		fetch_pick_server || return 1
615158273Scperciva	done
616148871Scperciva	fetch_snapshot_tagsanity || return 1
617148871Scperciva	fetch_metadata || return 1
618148871Scperciva	fetch_metadata_sanity || return 1
619148871Scperciva
620148871Scperciva	rm -rf snap/
621148871Scperciva
622148871Scperciva# Don't ask fetch(1) to be quiet -- downloading a snapshot of ~ 35MB will
623148871Scperciva# probably take a while, so the progrees reports that fetch(1) generates
624148871Scperciva# will be useful for keeping the users' attention from drifting.
625148871Scperciva	echo "Fetching snapshot generated at `date -r ${SNAPSHOTDATE}`:"
626154693Scperciva	fetch -r http://${SERVERNAME}/s/${SNAPSHOTHASH}.tgz || return 1
627148871Scperciva
628148871Scperciva	echo -n "Extracting snapshot... "
629148871Scperciva	tar -xzf ${SNAPSHOTHASH}.tgz snap/ || return 1
630148871Scperciva	rm ${SNAPSHOTHASH}.tgz
631148871Scperciva	echo "done."
632148871Scperciva
633148871Scperciva	echo -n "Verifying snapshot integrity... "
634148871Scperciva# Verify the metadata files
635148871Scperciva	cut -f 2 -d '|' tINDEX.new | fetch_snapshot_verify || return 1
636148871Scperciva# Extract the index
637148871Scperciva	rm -f INDEX.new
638148871Scperciva	gunzip -c snap/`look INDEX tINDEX.new |
639148871Scperciva	    cut -f 2 -d '|'`.gz > INDEX.new
640148871Scperciva	fetch_index_sanity || return 1
641148871Scperciva# Verify the snapshot contents
642148871Scperciva	cut -f 2 -d '|' INDEX.new | fetch_snapshot_verify || return 1
643148871Scperciva	echo "done."
644148871Scperciva
645148871Scperciva# Move files into their proper locations
646148871Scperciva	rm -f tag INDEX tINDEX
647148871Scperciva	rm -rf files
648148871Scperciva	mv tag.new tag
649148871Scperciva	mv tINDEX.new tINDEX
650148871Scperciva	mv INDEX.new INDEX
651148871Scperciva	mv snap/ files/
652148871Scperciva
653148871Scperciva	return 0
654148871Scperciva}
655148871Scperciva
656148871Scperciva# Update a compressed snapshot
657148871Scpercivafetch_update() {
658148871Scperciva	rm -f patchlist diff OLD NEW filelist INDEX.new
659148871Scperciva
660148871Scperciva	OLDSNAPSHOTDATE=`cut -f 2 -d '|' < tag`
661148871Scperciva	OLDSNAPSHOTHASH=`cut -f 3 -d '|' < tag`
662148871Scperciva
663158273Scperciva	while ! fetch_tag latest; do
664158273Scperciva		fetch_pick_server || return 1
665158273Scperciva	done
666148871Scperciva	fetch_update_tagsanity || return 1
667148871Scperciva	fetch_update_neededp || return 0
668148871Scperciva	fetch_metadata || return 1
669148871Scperciva	fetch_metadata_sanity || return 1
670148871Scperciva
671148871Scperciva	echo -n "Updating from `date -r ${OLDSNAPSHOTDATE}` "
672148871Scperciva	echo "to `date -r ${SNAPSHOTDATE}`."
673148871Scperciva
674148871Scperciva# Generate a list of wanted metadata patches
675148871Scperciva	join -t '|' -o 1.2,2.2 tINDEX tINDEX.new |
676148871Scperciva	    fetch_make_patchlist > patchlist
677148871Scperciva
678148871Scperciva# Attempt to fetch metadata patches
679148871Scperciva	echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
680148871Scperciva	echo ${NDEBUG} "metadata patches.${DDSTATS}"
681148871Scperciva	tr '|' '-' < patchlist |
682148871Scperciva	    lam -s "tp/" - -s ".gz" |
683148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
684148871Scperciva	    2>${STATSREDIR} | fetch_progress
685148871Scperciva	echo "done."
686148871Scperciva
687148871Scperciva# Attempt to apply metadata patches
688148871Scperciva	echo -n "Applying metadata patches... "
689148871Scperciva	while read LINE; do
690148871Scperciva		X=`echo ${LINE} | cut -f 1 -d '|'`
691148871Scperciva		Y=`echo ${LINE} | cut -f 2 -d '|'`
692148871Scperciva		if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
693148871Scperciva		gunzip -c < ${X}-${Y}.gz > diff
694148871Scperciva		gunzip -c < files/${X}.gz > OLD
695148871Scperciva		cut -c 2- diff | join -t '|' -v 2 - OLD > ptmp
696148871Scperciva		grep '^\+' diff | cut -c 2- |
697148871Scperciva		    sort -k 1,1 -t '|' -m - ptmp > NEW
698148871Scperciva		if [ `${SHA256} -q NEW` = ${Y} ]; then
699148871Scperciva			mv NEW files/${Y}
700148871Scperciva			gzip -n files/${Y}
701148871Scperciva		fi
702148871Scperciva		rm -f diff OLD NEW ${X}-${Y}.gz ptmp
703148871Scperciva	done < patchlist 2>${QUIETREDIR}
704148871Scperciva	echo "done."
705148871Scperciva
706148871Scperciva# Update metadata without patches
707148871Scperciva	join -t '|' -v 2 tINDEX tINDEX.new |
708148871Scperciva	    cut -f 2 -d '|' /dev/stdin patchlist |
709148871Scperciva		while read Y; do
710148871Scperciva			if [ ! -f "files/${Y}.gz" ]; then
711148871Scperciva				echo ${Y};
712148871Scperciva			fi
713148871Scperciva		done > filelist
714148871Scperciva	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
715148871Scperciva	echo ${NDEBUG} "metadata files... "
716148871Scperciva	lam -s "f/" - -s ".gz" < filelist |
717148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
718148871Scperciva	    2>${QUIETREDIR}
719148871Scperciva
720148871Scperciva	while read Y; do
721148871Scperciva		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
722148871Scperciva			mv ${Y}.gz files/${Y}.gz
723148871Scperciva		else
724148871Scperciva			echo "metadata is corrupt."
725148871Scperciva			return 1
726148871Scperciva		fi
727148871Scperciva	done < filelist
728148871Scperciva	echo "done."
729148871Scperciva
730148871Scperciva# Extract the index
731148871Scperciva	gunzip -c files/`look INDEX tINDEX.new |
732148871Scperciva	    cut -f 2 -d '|'`.gz > INDEX.new
733148871Scperciva	fetch_index_sanity || return 1
734148871Scperciva
735149824Scperciva# If we have decided to refuse certain updates, construct a hybrid index which
736149824Scperciva# is equal to the old index for parts of the tree which we don't want to
737149824Scperciva# update, and equal to the new index for parts of the tree which gets updates.
738149824Scperciva# This means that we should always have a "complete snapshot" of the ports
739149824Scperciva# tree -- with the caveat that it isn't actually a snapshot.
740149824Scperciva	if [ ! -z "${REFUSE}" ]; then
741149824Scperciva		echo "Refusing to download updates for ${REFUSE}"	\
742149824Scperciva		    >${QUIETREDIR}
743149824Scperciva
744149824Scperciva		grep -Ev "${REFUSE}" INDEX.new > INDEX.tmp
745149824Scperciva		grep -E "${REFUSE}" INDEX |
746149824Scperciva		    sort -m -k 1,1 -t '|' - INDEX.tmp > INDEX.new
747149824Scperciva		rm -f INDEX.tmp
748149824Scperciva	fi
749149824Scperciva
750148871Scperciva# Generate a list of wanted ports patches
751148871Scperciva	join -t '|' -o 1.2,2.2 INDEX INDEX.new |
752148871Scperciva	    fetch_make_patchlist > patchlist
753148871Scperciva
754148871Scperciva# Attempt to fetch ports patches
755148871Scperciva	echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
756148871Scperciva	echo ${NDEBUG} "patches.${DDSTATS}"
757148871Scperciva	tr '|' '-' < patchlist | lam -s "bp/" - |
758148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
759148871Scperciva	    2>${STATSREDIR} | fetch_progress
760148871Scperciva	echo "done."
761148871Scperciva
762148871Scperciva# Attempt to apply ports patches
763148871Scperciva	echo -n "Applying patches... "
764148871Scperciva	while read LINE; do
765148871Scperciva		X=`echo ${LINE} | cut -f 1 -d '|'`
766148871Scperciva		Y=`echo ${LINE} | cut -f 2 -d '|'`
767148871Scperciva		if [ ! -f "${X}-${Y}" ]; then continue; fi
768148871Scperciva		gunzip -c < files/${X}.gz > OLD
769148871Scperciva		${BSPATCH} OLD NEW ${X}-${Y}
770148871Scperciva		if [ `${SHA256} -q NEW` = ${Y} ]; then
771148871Scperciva			mv NEW files/${Y}
772148871Scperciva			gzip -n files/${Y}
773148871Scperciva		fi
774148871Scperciva		rm -f diff OLD NEW ${X}-${Y}
775148871Scperciva	done < patchlist 2>${QUIETREDIR}
776148871Scperciva	echo "done."
777148871Scperciva
778148871Scperciva# Update ports without patches
779148871Scperciva	join -t '|' -v 2 INDEX INDEX.new |
780148871Scperciva	    cut -f 2 -d '|' /dev/stdin patchlist |
781148871Scperciva		while read Y; do
782148871Scperciva			if [ ! -f "files/${Y}.gz" ]; then
783148871Scperciva				echo ${Y};
784148871Scperciva			fi
785148871Scperciva		done > filelist
786148871Scperciva	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
787148871Scperciva	echo ${NDEBUG} "new ports or files... "
788148871Scperciva	lam -s "f/" - -s ".gz" < filelist |
789148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
790148871Scperciva	    2>${QUIETREDIR}
791148871Scperciva
792148871Scperciva	while read Y; do
793148871Scperciva		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
794148871Scperciva			mv ${Y}.gz files/${Y}.gz
795148871Scperciva		else
796148871Scperciva			echo "snapshot is corrupt."
797148871Scperciva			return 1
798148871Scperciva		fi
799148871Scperciva	done < filelist
800148871Scperciva	echo "done."
801148871Scperciva
802148871Scperciva# Remove files which are no longer needed
803148871Scperciva	cut -f 2 -d '|' tINDEX INDEX | sort > oldfiles
804148871Scperciva	cut -f 2 -d '|' tINDEX.new INDEX.new | sort | comm -13 - oldfiles |
805148871Scperciva	    lam -s "files/" - -s ".gz" | xargs rm -f
806148871Scperciva	rm patchlist filelist oldfiles
807148871Scperciva
808148871Scperciva# We're done!
809148871Scperciva	mv INDEX.new INDEX
810148871Scperciva	mv tINDEX.new tINDEX
811148871Scperciva	mv tag.new tag
812148871Scperciva
813148871Scperciva	return 0
814148871Scperciva}
815148871Scperciva
816148871Scperciva# Do the actual work involved in "fetch" / "cron".
817148871Scpercivafetch_run() {
818158273Scperciva	fetch_pick_server_init && fetch_pick_server
819148871Scperciva
820158273Scperciva	while ! fetch_key; do
821158273Scperciva		fetch_pick_server || return 1
822158273Scperciva	done
823148871Scperciva
824148871Scperciva	if ! [ -d files -a -r tag -a -r INDEX -a -r tINDEX ]; then
825148871Scperciva		fetch_snapshot || return 1
826148871Scperciva	fi
827148871Scperciva	fetch_update || return 1
828148871Scperciva}
829148871Scperciva
830148871Scperciva# Build a ports INDEX file
831148871Scpercivaextract_make_index() {
832148871Scperciva	gunzip -c "${WORKDIR}/files/`look $1 ${WORKDIR}/tINDEX |
833158523Scperciva	    cut -f 2 -d '|'`.gz" |
834158523Scperciva	    cat - ${LOCALDESC} |
835158523Scperciva	    ${MKINDEX} /dev/stdin > ${PORTSDIR}/$2
836148871Scperciva}
837148871Scperciva
838148871Scperciva# Create INDEX, INDEX-5, INDEX-6
839148871Scpercivaextract_indices() {
840148871Scperciva	echo -n "Building new INDEX files... "
841148871Scperciva	extract_make_index DESCRIBE.4 INDEX || return 1
842148871Scperciva	extract_make_index DESCRIBE.5 INDEX-5 || return 1
843148871Scperciva	extract_make_index DESCRIBE.6 INDEX-6 || return 1
844148871Scperciva	echo "done."
845148871Scperciva}
846148871Scperciva
847149824Scperciva# Create .portsnap.INDEX; if we are REFUSEing to touch certain directories,
848149824Scperciva# merge the values from any exiting .portsnap.INDEX file.
849148871Scpercivaextract_metadata() {
850149824Scperciva	if [ -z "${REFUSE}" ]; then
851149984Scperciva		sort ${WORKDIR}/INDEX > ${PORTSDIR}/.portsnap.INDEX
852149824Scperciva	elif [ -f ${PORTSDIR}/.portsnap.INDEX ]; then
853149824Scperciva		grep -E "${REFUSE}" ${PORTSDIR}/.portsnap.INDEX	\
854149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX.tmp
855149824Scperciva		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort |
856149824Scperciva		    sort -m - ${PORTSDIR}/.portsnap.INDEX.tmp	\
857149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX
858149824Scperciva		rm -f ${PORTSDIR}/.portsnap.INDEX.tmp
859149824Scperciva	else
860149824Scperciva		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort \
861149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX
862149824Scperciva	fi
863148871Scperciva}
864148871Scperciva
865148871Scperciva# Do the actual work involved in "extract"
866148871Scpercivaextract_run() {
867154088Scperciva	mkdir -p ${PORTSDIR} || return 1
868154088Scperciva
869149824Scperciva	if !
870149824Scperciva		if ! [ -z "${EXTRACTPATH}" ]; then
871149824Scperciva			grep "^${EXTRACTPATH}" ${WORKDIR}/INDEX
872149824Scperciva		elif ! [ -z "${REFUSE}" ]; then
873149824Scperciva			grep -vE "${REFUSE}" ${WORKDIR}/INDEX
874149824Scperciva		else
875149824Scperciva			cat ${WORKDIR}/INDEX
876159062Scperciva		fi | tr '|' ' ' | while read FILE HASH; do
877148871Scperciva		echo ${PORTSDIR}/${FILE}
878148871Scperciva		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
879148871Scperciva			echo "files/${HASH}.gz not found -- snapshot corrupt."
880148871Scperciva			return 1
881148871Scperciva		fi
882148871Scperciva		case ${FILE} in
883148871Scperciva		*/)
884159062Scperciva			rm -rf ${PORTSDIR}/${FILE%/}
885148871Scperciva			mkdir -p ${PORTSDIR}/${FILE}
886148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
887148871Scperciva			    -C ${PORTSDIR}/${FILE}
888148871Scperciva			;;
889148871Scperciva		*)
890148871Scperciva			rm -f ${PORTSDIR}/${FILE}
891148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
892148871Scperciva			    -C ${PORTSDIR} ${FILE}
893148871Scperciva			;;
894148871Scperciva		esac
895149041Scperciva	done; then
896149041Scperciva		return 1
897149041Scperciva	fi
898148871Scperciva	if [ ! -z "${EXTRACTPATH}" ]; then
899148871Scperciva		return 0;
900148871Scperciva	fi
901148871Scperciva
902148871Scperciva	extract_metadata
903148871Scperciva	extract_indices
904148871Scperciva}
905148871Scperciva
906148871Scperciva# Do the actual work involved in "update"
907148871Scpercivaupdate_run() {
908148871Scperciva	if ! [ -z "${INDEXONLY}" ]; then
909148871Scperciva		extract_indices >/dev/null || return 1
910148871Scperciva		return 0
911148871Scperciva	fi
912148871Scperciva
913148981Scperciva	if sort ${WORKDIR}/INDEX |
914149042Scperciva	    cmp -s ${PORTSDIR}/.portsnap.INDEX -; then
915148958Scperciva		echo "Ports tree is already up to date."
916148958Scperciva		return 0
917148958Scperciva	fi
918148958Scperciva
919149824Scperciva# If we are REFUSEing to touch certain directories, don't remove files
920149824Scperciva# from those directories (even if they are out of date)
921148871Scperciva	echo -n "Removing old files and directories... "
922149824Scperciva	if ! [ -z "${REFUSE}" ]; then 
923149824Scperciva		sort ${WORKDIR}/INDEX |
924149824Scperciva		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
925149824Scperciva		    grep -vE "${REFUSE}" |
926158473Scperciva		    lam -s "${PORTSDIR}/" - |
927158473Scperciva		    sed -e 's|/$||' | xargs rm -rf
928149824Scperciva	else
929149824Scperciva		sort ${WORKDIR}/INDEX |
930149824Scperciva		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
931158473Scperciva		    lam -s "${PORTSDIR}/" - |
932158473Scperciva		    sed -e 's|/$||' | xargs rm -rf
933149824Scperciva	fi
934148871Scperciva	echo "done."
935148871Scperciva
936148871Scperciva# Install new files
937148871Scperciva	echo "Extracting new files:"
938149824Scperciva	if !
939149824Scperciva		if ! [ -z "${REFUSE}" ]; then
940149824Scperciva			grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort
941149824Scperciva		else
942149824Scperciva			sort ${WORKDIR}/INDEX
943149824Scperciva		fi |
944149041Scperciva	    comm -13 ${PORTSDIR}/.portsnap.INDEX - |
945148871Scperciva	    while read LINE; do
946148871Scperciva		FILE=`echo ${LINE} | cut -f 1 -d '|'`
947148871Scperciva		HASH=`echo ${LINE} | cut -f 2 -d '|'`
948148871Scperciva		echo ${PORTSDIR}/${FILE}
949148871Scperciva		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
950148871Scperciva			echo "files/${HASH}.gz not found -- snapshot corrupt."
951148871Scperciva			return 1
952148871Scperciva		fi
953148871Scperciva		case ${FILE} in
954148871Scperciva		*/)
955148871Scperciva			mkdir -p ${PORTSDIR}/${FILE}
956148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
957148871Scperciva			    -C ${PORTSDIR}/${FILE}
958148871Scperciva			;;
959148871Scperciva		*)
960148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
961148871Scperciva			    -C ${PORTSDIR} ${FILE}
962148871Scperciva			;;
963148871Scperciva		esac
964149041Scperciva	done; then
965149041Scperciva		return 1
966149041Scperciva	fi
967148871Scperciva
968148871Scperciva	extract_metadata
969148871Scperciva	extract_indices
970148871Scperciva}
971148871Scperciva
972148871Scperciva#### Main functions -- call parameter-handling and core functions
973148871Scperciva
974148871Scperciva# Using the command line, configuration file, and defaults,
975148871Scperciva# set all the parameters which are needed later.
976148871Scpercivaget_params() {
977148871Scperciva	init_params
978148871Scperciva	parse_cmdline $@
979148871Scperciva	sanity_conffile
980148871Scperciva	default_conffile
981148871Scperciva	parse_conffile
982148871Scperciva	default_params
983148871Scperciva}
984148871Scperciva
985148871Scperciva# Fetch command.  Make sure that we're being called
986148871Scperciva# interactively, then run fetch_check_params and fetch_run
987148871Scpercivacmd_fetch() {
988148871Scperciva	if [ ! -t 0 ]; then
989148871Scperciva		echo -n "`basename $0` fetch should not "
990148871Scperciva		echo "be run non-interactively."
991148871Scperciva		echo "Run `basename $0` cron instead."
992148871Scperciva		exit 1
993148871Scperciva	fi
994148871Scperciva	fetch_check_params
995148871Scperciva	fetch_run || exit 1
996148871Scperciva}
997148871Scperciva
998148871Scperciva# Cron command.  Make sure the parameters are sensible; wait
999148871Scperciva# rand(3600) seconds; then fetch updates.  While fetching updates,
1000148871Scperciva# send output to a temporary file; only print that file if the
1001148871Scperciva# fetching failed.
1002148871Scpercivacmd_cron() {
1003148871Scperciva	fetch_check_params
1004148871Scperciva	sleep `jot -r 1 0 3600`
1005148871Scperciva
1006148871Scperciva	TMPFILE=`mktemp /tmp/portsnap.XXXXXX` || exit 1
1007148871Scperciva	if ! fetch_run >> ${TMPFILE}; then
1008148871Scperciva		cat ${TMPFILE}
1009148871Scperciva		rm ${TMPFILE}
1010148871Scperciva		exit 1
1011148871Scperciva	fi
1012148871Scperciva
1013148871Scperciva	rm ${TMPFILE}
1014148871Scperciva}
1015148871Scperciva
1016148871Scperciva# Extract command.  Make sure the parameters are sensible,
1017148871Scperciva# then extract the ports tree (or part thereof).
1018148871Scpercivacmd_extract() {
1019148871Scperciva	extract_check_params
1020148871Scperciva	extract_run || exit 1
1021148871Scperciva}
1022148871Scperciva
1023148871Scperciva# Update command.  Make sure the parameters are sensible,
1024148871Scperciva# then update the ports tree.
1025148871Scpercivacmd_update() {
1026148871Scperciva	update_check_params
1027148871Scperciva	update_run || exit 1
1028148871Scperciva}
1029148871Scperciva
1030148871Scperciva#### Entry point
1031148871Scperciva
1032148871Scperciva# Make sure we find utilities from the base system
1033148871Scpercivaexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
1034148871Scperciva
1035148871Scpercivaget_params $@
1036149027Scpercivafor COMMAND in ${COMMANDS}; do
1037149027Scperciva	cmd_${COMMAND}
1038149027Scpercivadone
1039