portsnap.sh revision 158245
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 158245 2006-05-02 05:27:30Z 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
46148871Scperciva  -p portsdir  -- Location of uncompressed ports tree
47148871Scperciva                  (default: /usr/ports/)
48148871Scperciva  -s server    -- Server from which to fetch updates.
49148871Scperciva                  (default: portsnap.FreeBSD.org)
50148871Scperciva  path         -- Extract only parts of the tree starting with the given
51148871Scperciva                  string.  (extract command only)
52148871ScpercivaCommands:
53148871Scperciva  fetch        -- Fetch a compressed snapshot of the ports tree,
54148871Scperciva                  or update an existing snapshot.
55148871Scperciva  cron         -- Sleep rand(3600) seconds, and then fetch updates.
56148871Scperciva  extract      -- Extract snapshot of ports tree, replacing existing
57148871Scperciva                  files and directories.
58148871Scperciva  update       -- Update ports tree to match current snapshot, replacing
59148871Scperciva                  files and directories which have changed.
60148871ScpercivaEOF
61148871Scperciva	exit 0
62148871Scperciva}
63148871Scperciva
64148871Scperciva#### Parameter handling functions.
65148871Scperciva
66148871Scperciva# Initialize parameters to null, just in case they're
67148871Scperciva# set in the environment.
68148871Scpercivainit_params() {
69148871Scperciva	KEYPRINT=""
70148871Scperciva	EXTRACTPATH=""
71148871Scperciva	WORKDIR=""
72148871Scperciva	PORTSDIR=""
73148871Scperciva	CONFFILE=""
74148871Scperciva	COMMAND=""
75149027Scperciva	COMMANDS=""
76148871Scperciva	QUIETREDIR=""
77148871Scperciva	QUIETFLAG=""
78148871Scperciva	STATSREDIR=""
79148879Scperciva	XARGST=""
80148871Scperciva	NDEBUG=""
81148871Scperciva	DDSTATS=""
82148871Scperciva	INDEXONLY=""
83148871Scperciva	SERVERNAME=""
84149824Scperciva	REFUSE=""
85148871Scperciva}
86148871Scperciva
87148871Scperciva# Parse the command line
88148871Scpercivaparse_cmdline() {
89148871Scperciva	while [ $# -gt 0 ]; do
90148871Scperciva		case "$1" in
91148871Scperciva		-d)
92148871Scperciva			if [ $# -eq 1 ]; then usage; fi
93148871Scperciva			if [ ! -z "${WORKDIR}" ]; then usage; fi
94148871Scperciva			shift; WORKDIR="$1"
95148871Scperciva			;;
96148871Scperciva		--debug)
97148871Scperciva			QUIETREDIR="/dev/stderr"
98148871Scperciva			STATSREDIR="/dev/stderr"
99148871Scperciva			QUIETFLAG=" "
100148871Scperciva			NDEBUG=" "
101148879Scperciva			XARGST="-t"
102148871Scperciva			DDSTATS=".."
103148871Scperciva			;;
104148871Scperciva		-f)
105148871Scperciva			if [ $# -eq 1 ]; then usage; fi
106148871Scperciva			if [ ! -z "${CONFFILE}" ]; then usage; fi
107148871Scperciva			shift; CONFFILE="$1"
108148871Scperciva			;;
109148871Scperciva		-h | --help | help)
110148871Scperciva			usage
111148871Scperciva			;;
112148871Scperciva		-I)
113148871Scperciva			INDEXONLY="YES"
114148871Scperciva			;;
115148871Scperciva		-k)
116148871Scperciva			if [ $# -eq 1 ]; then usage; fi
117148871Scperciva			if [ ! -z "${KEYPRINT}" ]; then usage; fi
118148871Scperciva			shift; KEYPRINT="$1"
119148871Scperciva			;;
120148871Scperciva		--no-stats)
121148871Scperciva			if [ -z "${STATSREDIR}" ]; then
122148871Scperciva				STATSREDIR="/dev/null"
123148871Scperciva				DDSTATS=".. "
124148871Scperciva			fi
125148871Scperciva			;;
126148871Scperciva		-p)
127148871Scperciva			if [ $# -eq 1 ]; then usage; fi
128148871Scperciva			if [ ! -z "${PORTSDIR}" ]; then usage; fi
129148871Scperciva			shift; PORTSDIR="$1"
130148871Scperciva			;;
131148871Scperciva		-s)
132148871Scperciva			if [ $# -eq 1 ]; then usage; fi
133148871Scperciva			if [ ! -z "${SERVERNAME}" ]; then usage; fi
134148871Scperciva			shift; SERVERNAME="$1"
135148871Scperciva			;;
136148871Scperciva		cron | extract | fetch | update)
137149027Scperciva			COMMANDS="${COMMANDS} $1"
138148871Scperciva			;;
139148871Scperciva		*)
140148871Scperciva			if [ $# -gt 1 ]; then usage; fi
141149027Scperciva			if echo ${COMMANDS} | grep -vq extract; then
142149027Scperciva				usage
143149027Scperciva			fi
144148871Scperciva			EXTRACTPATH="$1"
145148871Scperciva			;;
146148871Scperciva		esac
147148871Scperciva		shift
148148871Scperciva	done
149148871Scperciva
150149027Scperciva	if [ -z "${COMMANDS}" ]; then
151148871Scperciva		usage
152148871Scperciva	fi
153148871Scperciva}
154148871Scperciva
155148871Scperciva# If CONFFILE was specified at the command-line, make
156148871Scperciva# sure that it exists and is readable.
157148871Scpercivasanity_conffile() {
158148871Scperciva	if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
159148871Scperciva		echo -n "File does not exist "
160148871Scperciva		echo -n "or is not readable: "
161148871Scperciva		echo ${CONFFILE}
162148871Scperciva		exit 1
163148871Scperciva	fi
164148871Scperciva}
165148871Scperciva
166148871Scperciva# If a configuration file hasn't been specified, use
167148871Scperciva# the default value (/etc/portsnap.conf)
168148871Scpercivadefault_conffile() {
169148871Scperciva	if [ -z "${CONFFILE}" ]; then
170148871Scperciva		CONFFILE="/etc/portsnap.conf"
171148871Scperciva	fi
172148871Scperciva}
173148871Scperciva
174148871Scperciva# Read {KEYPRINT, SERVERNAME, WORKDIR, PORTSDIR} from the configuration
175148871Scperciva# file if they haven't already been set.  If the configuration
176148871Scperciva# file doesn't exist, do nothing.
177149824Scperciva# Also read REFUSE (which cannot be set via the command line) if it is
178149824Scperciva# present in the configuration file.
179148871Scpercivaparse_conffile() {
180148871Scperciva	if [ -r "${CONFFILE}" ]; then
181148871Scperciva		for X in KEYPRINT WORKDIR PORTSDIR SERVERNAME; do
182148871Scperciva			eval _=\$${X}
183148871Scperciva			if [ -z "${_}" ]; then
184148871Scperciva				eval ${X}=`grep "^${X}=" "${CONFFILE}" |
185148871Scperciva				    cut -f 2- -d '=' | tail -1`
186148871Scperciva			fi
187148871Scperciva		done
188149824Scperciva
189149824Scperciva		if grep -qE "^REFUSE[[:space:]]" ${CONFFILE}; then
190149824Scperciva			REFUSE="^(`
191149824Scperciva				grep -E "^REFUSE[[:space:]]" "${CONFFILE}" |
192149824Scperciva				    cut -c 7- | xargs echo | tr ' ' '|'
193149824Scperciva				`)"
194149824Scperciva		fi
195148871Scperciva	fi
196148871Scperciva}
197148871Scperciva
198148871Scperciva# If parameters have not been set, use default values
199148871Scpercivadefault_params() {
200148871Scperciva	_QUIETREDIR="/dev/null"
201148871Scperciva	_QUIETFLAG="-q"
202148871Scperciva	_STATSREDIR="/dev/stdout"
203148871Scperciva	_WORKDIR="/var/db/portsnap"
204148871Scperciva	_PORTSDIR="/usr/ports"
205148871Scperciva	_NDEBUG="-n"
206148871Scperciva	for X in QUIETREDIR QUIETFLAG STATSREDIR WORKDIR PORTSDIR NDEBUG; do
207148871Scperciva		eval _=\$${X}
208148871Scperciva		eval __=\$_${X}
209148871Scperciva		if [ -z "${_}" ]; then
210148871Scperciva			eval ${X}=${__}
211148871Scperciva		fi
212148871Scperciva	done
213148871Scperciva}
214148871Scperciva
215148871Scperciva# Perform sanity checks and set some final parameters
216148871Scperciva# in preparation for fetching files.  Also chdir into
217148871Scperciva# the working directory.
218148871Scpercivafetch_check_params() {
219148871Scperciva	export HTTP_USER_AGENT="portsnap (${COMMAND}, `uname -r`)"
220148871Scperciva
221148871Scperciva	_SERVERNAME_z=\
222148871Scperciva"SERVERNAME must be given via command line or configuration file."
223148871Scperciva	_KEYPRINT_z="Key must be given via -k option or configuration file."
224148871Scperciva	_KEYPRINT_bad="Invalid key fingerprint: "
225148871Scperciva	_WORKDIR_bad="Directory does not exist or is not writable: "
226148871Scperciva
227148871Scperciva	if [ -z "${SERVERNAME}" ]; then
228148871Scperciva		echo -n "`basename $0`: "
229148871Scperciva		echo "${_SERVERNAME_z}"
230148871Scperciva		exit 1
231148871Scperciva	fi
232148871Scperciva	if [ -z "${KEYPRINT}" ]; then
233148871Scperciva		echo -n "`basename $0`: "
234148871Scperciva		echo "${_KEYPRINT_z}"
235148871Scperciva		exit 1
236148871Scperciva	fi
237148871Scperciva	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
238148871Scperciva		echo -n "`basename $0`: "
239148871Scperciva		echo -n "${_KEYPRINT_bad}"
240148871Scperciva		echo ${KEYPRINT}
241148871Scperciva		exit 1
242148871Scperciva	fi
243148871Scperciva	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
244148871Scperciva		echo -n "`basename $0`: "
245148871Scperciva		echo -n "${_WORKDIR_bad}"
246148871Scperciva		echo ${WORKDIR}
247148871Scperciva		exit 1
248148871Scperciva	fi
249148871Scperciva	cd ${WORKDIR} || exit 1
250148871Scperciva
251148871Scperciva	BSPATCH=/usr/bin/bspatch
252148871Scperciva	SHA256=/sbin/sha256
253148871Scperciva	PHTTPGET=/usr/libexec/phttpget
254148871Scperciva}
255148871Scperciva
256148871Scperciva# Perform sanity checks and set some final parameters
257148871Scperciva# in preparation for extracting or updating ${PORTSDIR}
258154088Scperciva# Complain if ${PORTSDIR} exists but is not writable,
259154088Scperciva# but don't complain if ${PORTSDIR} doesn't exist.
260148871Scpercivaextract_check_params() {
261148871Scperciva	_WORKDIR_bad="Directory does not exist: "
262154088Scperciva	_PORTSDIR_bad="Directory is not writable: "
263148871Scperciva
264148871Scperciva	if ! [ -d "${WORKDIR}" ]; then
265148871Scperciva		echo -n "`basename $0`: "
266148871Scperciva		echo -n "${_WORKDIR_bad}"
267148871Scperciva		echo ${WORKDIR}
268148871Scperciva		exit 1
269148871Scperciva	fi
270154088Scperciva	if [ -d "${PORTSDIR}" ] && ! [ -w "${PORTSDIR}" ]; then
271148871Scperciva		echo -n "`basename $0`: "
272148871Scperciva		echo -n "${_PORTSDIR_bad}"
273148871Scperciva		echo ${PORTSDIR}
274148871Scperciva		exit 1
275148871Scperciva	fi
276148871Scperciva
277148871Scperciva	if ! [ -d "${WORKDIR}/files" -a -r "${WORKDIR}/tag"	\
278148871Scperciva	    -a -r "${WORKDIR}/INDEX" -a -r "${WORKDIR}/tINDEX" ]; then
279148871Scperciva		echo "No snapshot available.  Try running"
280148871Scperciva		echo "# `basename $0` fetch"
281148871Scperciva		exit 1
282148871Scperciva	fi
283148871Scperciva
284148871Scperciva	MKINDEX=/usr/libexec/make_index
285148871Scperciva}
286148871Scperciva
287148871Scperciva# Perform sanity checks and set some final parameters
288148871Scperciva# in preparation for updating ${PORTSDIR}
289148871Scpercivaupdate_check_params() {
290148871Scperciva	extract_check_params
291148871Scperciva
292148871Scperciva	if ! [ -r ${PORTSDIR}/.portsnap.INDEX ]; then
293148871Scperciva		echo "${PORTSDIR} was not created by portsnap."
294148871Scperciva		echo -n "You must run '`basename $0` extract' before "
295148871Scperciva		echo "running '`basename $0` update'."
296148871Scperciva		exit 1
297148871Scperciva	fi
298148871Scperciva
299148871Scperciva}
300148871Scperciva
301148871Scperciva#### Core functionality -- the actual work gets done here
302148871Scperciva
303148871Scperciva# Use an SRV query to pick a server.  If the SRV query doesn't provide
304148871Scperciva# a useful answer, use the server name specified by the user.
305148871Scperciva# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
306148871Scperciva# from that; or if no servers are returned, use ${SERVERNAME}.
307148871Scperciva# This allows a user to specify "portsnap.freebsd.org" (in which case
308148871Scperciva# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
309148871Scperciva# (in which case portsnap will use that particular server, since there
310148871Scperciva# won't be an SRV entry for that name).
311148871Scperciva#
312148871Scperciva# We don't implement the recommendations from RFC 2782 completely, since
313148871Scperciva# we are only looking to pick a single server -- the recommendations are
314148871Scperciva# targetted at applications which obtain a list of servers and then try
315148871Scperciva# each in turn, but we are instead just going to pick one server and let
316148871Scperciva# the user re-run portsnap if a broken server was selected.
317148871Scperciva#
318148871Scperciva# We also ignore the Port field, since we are always going to use port 80.
319148871Scpercivafetch_pick_server() {
320150159Scperciva# Check that host(1) exists (i.e., that the system wasn't built with the
321156813Sru# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
322150159Scperciva	if ! which -s host; then
323150159Scperciva		return
324150159Scperciva	fi
325150159Scperciva
326148871Scperciva	echo -n "Looking up ${SERVERNAME} mirrors..."
327148871Scperciva
328148871Scperciva# Issue the SRV query and pull out the Priority, Weight, and Target fields.
329158245Scperciva# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
330158245Scperciva# "$name server selection ..."; we allow either format.
331158245Scperciva	MLIST="_http._tcp.${SERVERNAME}"
332158245Scperciva	host -t srv "${MLIST}" |
333158245Scperciva	    sed -nE "s/${MLIST} (has SRV record|server selection) //p" |
334158245Scperciva	    cut -f 1,2,4 -d ' ' |
335158245Scperciva	    sed -e 's/\.$//' > serverlist
336148871Scperciva
337148871Scperciva# If no records, give up -- we'll just use the server name we were given.
338148871Scperciva	if [ `wc -l < serverlist` -eq 0 ]; then
339148871Scperciva		echo " none found."
340148871Scperciva		return
341148871Scperciva	fi
342148871Scperciva
343148871Scperciva# Find the highest priority level (lowest numeric value).
344148871Scperciva	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
345148871Scperciva
346148871Scperciva# Add up the weights of the response lines at that priority level.
347148871Scperciva	SRV_WSUM=0;
348148871Scperciva	while read X; do
349148871Scperciva		case "$X" in
350148871Scperciva		${SRV_PRIORITY}\ *)
351148871Scperciva			SRV_W=`echo $X | cut -f 2 -d ' '`
352148871Scperciva			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
353148871Scperciva			;;
354148871Scperciva		esac
355148871Scperciva	done < serverlist
356148871Scperciva
357148871Scperciva# If all the weights are 0, pretend that they are all 1 instead.
358148871Scperciva	if [ ${SRV_WSUM} -eq 0 ]; then
359148871Scperciva		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
360148871Scperciva		SRV_W_ADD=1
361148871Scperciva	else
362148871Scperciva		SRV_W_ADD=0
363148871Scperciva	fi
364148871Scperciva
365148871Scperciva# Pick a random value between 1 and the sum of the weights
366148871Scperciva	SRV_RND=`jot -r 1 1 ${SRV_WSUM}`
367148871Scperciva
368148871Scperciva# Read through the list of mirrors and set SERVERNAME
369148871Scperciva	while read X; do
370148871Scperciva		case "$X" in
371148871Scperciva		${SRV_PRIORITY}\ *)
372148871Scperciva			SRV_W=`echo $X | cut -f 2 -d ' '`
373148871Scperciva			SRV_W=$(($SRV_W + $SRV_W_ADD))
374148871Scperciva			if [ $SRV_RND -le $SRV_W ]; then
375148871Scperciva				SERVERNAME=`echo $X | cut -f 3 -d ' '`
376148871Scperciva				break
377148871Scperciva			else
378148871Scperciva				SRV_RND=$(($SRV_RND - $SRV_W))
379148871Scperciva			fi
380148871Scperciva			;;
381148871Scperciva		esac
382148871Scperciva	done < serverlist
383148871Scperciva
384148871Scperciva	echo " using ${SERVERNAME}"
385148871Scperciva}
386148871Scperciva
387148871Scperciva# Check that we have a public key with an appropriate hash, or
388148871Scperciva# fetch the key if it doesn't exist.
389148871Scpercivafetch_key() {
390148871Scperciva	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
391148871Scperciva		return
392148871Scperciva	fi
393148871Scperciva
394148871Scperciva	echo -n "Fetching public key... "
395148871Scperciva	rm -f pub.ssl
396148871Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/pub.ssl \
397148871Scperciva	    2>${QUIETREDIR} || true
398148871Scperciva	if ! [ -r pub.ssl ]; then
399148871Scperciva		echo "failed."
400148871Scperciva		return 1
401148871Scperciva	fi
402148871Scperciva	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
403148871Scperciva		echo "key has incorrect hash."
404148871Scperciva		rm -f pub.ssl
405148871Scperciva		return 1
406148871Scperciva	fi
407148871Scperciva	echo "done."
408148871Scperciva}
409148871Scperciva
410148871Scperciva# Fetch a snapshot tag
411148871Scpercivafetch_tag() {
412148871Scperciva	rm -f snapshot.ssl tag.new
413148871Scperciva
414148871Scperciva	echo ${NDEBUG} "Fetching snapshot tag... "
415148871Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/$1.ssl
416148871Scperciva	    2>${QUIETREDIR} || true
417148871Scperciva	if ! [ -r $1.ssl ]; then
418148871Scperciva		echo "failed."
419148871Scperciva		return 1
420148871Scperciva	fi
421148871Scperciva
422148871Scperciva	openssl rsautl -pubin -inkey pub.ssl -verify		\
423148871Scperciva	    < $1.ssl > tag.new 2>${QUIETREDIR} || true
424148871Scperciva	rm $1.ssl
425148871Scperciva
426148871Scperciva	if ! [ `wc -l < tag.new` = 1 ] ||
427148871Scperciva	    ! grep -qE "^portsnap\|[0-9]{10}\|[0-9a-f]{64}" tag.new; then
428148871Scperciva		echo "invalid snapshot tag."
429148871Scperciva		return 1
430148871Scperciva	fi
431148871Scperciva
432148871Scperciva	echo "done."
433148871Scperciva
434148871Scperciva	SNAPSHOTDATE=`cut -f 2 -d '|' < tag.new`
435148871Scperciva	SNAPSHOTHASH=`cut -f 3 -d '|' < tag.new`
436148871Scperciva}
437148871Scperciva
438148871Scperciva# Sanity-check the date on a snapshot tag
439148871Scpercivafetch_snapshot_tagsanity() {
440148871Scperciva	if [ `date "+%s"` -gt `expr ${SNAPSHOTDATE} + 31536000` ]; then
441148871Scperciva		echo "Snapshot appears to be more than a year old!"
442148871Scperciva		echo "(Is the system clock correct?)"
443149868Scperciva		echo "Cowardly refusing to proceed any further."
444148871Scperciva		return 1
445148871Scperciva	fi
446148871Scperciva	if [ `date "+%s"` -lt `expr ${SNAPSHOTDATE} - 86400` ]; then
447148871Scperciva		echo -n "Snapshot appears to have been created more than "
448148871Scperciva		echo "one day into the future!"
449148871Scperciva		echo "(Is the system clock correct?)"
450148871Scperciva		echo "Cowardly refusing to proceed any further."
451148871Scperciva		return 1
452148871Scperciva	fi
453148871Scperciva}
454148871Scperciva
455148871Scperciva# Sanity-check the date on a snapshot update tag
456148871Scpercivafetch_update_tagsanity() {
457148871Scperciva	fetch_snapshot_tagsanity || return 1
458148871Scperciva
459148871Scperciva	if [ ${OLDSNAPSHOTDATE} -gt ${SNAPSHOTDATE} ]; then
460148871Scperciva		echo -n "Latest snapshot on server is "
461148871Scperciva		echo "older than what we already have!"
462148871Scperciva		echo -n "Cowardly refusing to downgrade from "
463148871Scperciva		date -r ${OLDSNAPSHOTDATE}
464148879Scperciva		echo "to `date -r ${SNAPSHOTDATE}`."
465148871Scperciva		return 1
466148871Scperciva	fi
467148871Scperciva}
468148871Scperciva
469148871Scperciva# Compare old and new tags; return 1 if update is unnecessary
470148871Scpercivafetch_update_neededp() {
471148871Scperciva	if [ ${OLDSNAPSHOTDATE} -eq ${SNAPSHOTDATE} ]; then
472148871Scperciva		echo -n "Latest snapshot on server matches "
473148871Scperciva		echo "what we already have."
474148871Scperciva		echo "No updates needed."
475148871Scperciva		rm tag.new
476148871Scperciva		return 1
477148871Scperciva	fi
478148871Scperciva	if [ ${OLDSNAPSHOTHASH} = ${SNAPSHOTHASH} ]; then
479148871Scperciva		echo -n "Ports tree hasn't changed since "
480148871Scperciva		echo "last snapshot."
481148871Scperciva		echo "No updates needed."
482148871Scperciva		rm tag.new
483148871Scperciva		return 1
484148871Scperciva	fi
485148871Scperciva
486148871Scperciva	return 0
487148871Scperciva}
488148871Scperciva
489148871Scperciva# Fetch snapshot metadata file
490148871Scpercivafetch_metadata() {
491148871Scperciva	rm -f ${SNAPSHOTHASH} tINDEX.new
492148871Scperciva
493148871Scperciva	echo ${NDEBUG} "Fetching snapshot metadata... "
494148871Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/t/${SNAPSHOTHASH}
495148871Scperciva	    2>${QUIETREDIR} || return
496148871Scperciva	if [ `${SHA256} -q ${SNAPSHOTHASH}` != ${SNAPSHOTHASH} ]; then
497148871Scperciva		echo "snapshot metadata corrupt."
498148871Scperciva		return 1
499148871Scperciva	fi
500148871Scperciva	mv ${SNAPSHOTHASH} tINDEX.new
501148871Scperciva	echo "done."
502148871Scperciva}
503148871Scperciva
504148871Scperciva# Warn user about bogus metadata
505148871Scpercivafetch_metadata_freakout() {
506148871Scperciva	echo
507148871Scperciva	echo "Portsnap metadata is correctly signed, but contains"
508148871Scperciva	echo "at least one line which appears bogus."
509148871Scperciva	echo "Cowardly refusing to proceed any further."
510148871Scperciva}
511148871Scperciva
512148871Scperciva# Sanity-check a snapshot metadata file
513148871Scpercivafetch_metadata_sanity() {
514148871Scperciva	if grep -qvE "^[0-9A-Z.]+\|[0-9a-f]{64}$" tINDEX.new; then
515148871Scperciva		fetch_metadata_freakout
516148871Scperciva		return 1
517148871Scperciva	fi
518148871Scperciva	if [ `look INDEX tINDEX.new | wc -l` != 1 ]; then
519148871Scperciva		echo
520148871Scperciva		echo "Portsnap metadata appears bogus."
521148871Scperciva		echo "Cowardly refusing to proceed any further."
522148871Scperciva		return 1
523148871Scperciva	fi
524148871Scperciva}
525148871Scperciva
526148871Scperciva# Take a list of ${oldhash}|${newhash} and output a list of needed patches
527148871Scpercivafetch_make_patchlist() {
528148871Scperciva	grep -vE "^([0-9a-f]{64})\|\1$" | 
529148871Scperciva		while read LINE; do
530148871Scperciva			X=`echo ${LINE} | cut -f 1 -d '|'`
531148871Scperciva			Y=`echo ${LINE} | cut -f 2 -d '|'`
532148871Scperciva			if [ -f "files/${Y}.gz" ]; then continue; fi
533148871Scperciva			if [ ! -f "files/${X}.gz" ]; then continue; fi
534148871Scperciva			echo "${LINE}"
535148871Scperciva		done
536148871Scperciva}
537148871Scperciva
538148871Scperciva# Print user-friendly progress statistics
539148871Scpercivafetch_progress() {
540148871Scperciva	LNC=0
541148871Scperciva	while read x; do
542148871Scperciva		LNC=$(($LNC + 1))
543148871Scperciva		if [ $(($LNC % 10)) = 0 ]; then
544149023Scperciva			echo -n $LNC
545148871Scperciva		elif [ $(($LNC % 2)) = 0 ]; then
546148871Scperciva			echo -n .
547148871Scperciva		fi
548148871Scperciva	done
549148871Scperciva	echo -n " "
550148871Scperciva}
551148871Scperciva
552148871Scperciva# Sanity-check an index file
553148871Scpercivafetch_index_sanity() {
554148871Scperciva	if grep -qvE "^[-_+./@0-9A-Za-z]+\|[0-9a-f]{64}$" INDEX.new ||
555148871Scperciva	    fgrep -q "./" INDEX.new; then
556148871Scperciva		fetch_metadata_freakout
557148871Scperciva		return 1
558148871Scperciva	fi
559148871Scperciva}
560148871Scperciva
561148871Scperciva# Verify a list of files
562148871Scpercivafetch_snapshot_verify() {
563148871Scperciva	while read F; do
564148871Scperciva		if [ `gunzip -c snap/${F} | ${SHA256} -q` != ${F} ]; then
565148871Scperciva			echo "snapshot corrupt."
566148871Scperciva			return 1
567148871Scperciva		fi
568148871Scperciva	done
569148871Scperciva	return 0
570148871Scperciva}
571148871Scperciva
572148871Scperciva# Fetch a snapshot tarball, extract, and verify.
573148871Scpercivafetch_snapshot() {
574148871Scperciva	fetch_tag snapshot || return 1
575148871Scperciva	fetch_snapshot_tagsanity || return 1
576148871Scperciva	fetch_metadata || return 1
577148871Scperciva	fetch_metadata_sanity || return 1
578148871Scperciva
579148871Scperciva	rm -rf snap/
580148871Scperciva
581148871Scperciva# Don't ask fetch(1) to be quiet -- downloading a snapshot of ~ 35MB will
582148871Scperciva# probably take a while, so the progrees reports that fetch(1) generates
583148871Scperciva# will be useful for keeping the users' attention from drifting.
584148871Scperciva	echo "Fetching snapshot generated at `date -r ${SNAPSHOTDATE}`:"
585154693Scperciva	fetch -r http://${SERVERNAME}/s/${SNAPSHOTHASH}.tgz || return 1
586148871Scperciva
587148871Scperciva	echo -n "Extracting snapshot... "
588148871Scperciva	tar -xzf ${SNAPSHOTHASH}.tgz snap/ || return 1
589148871Scperciva	rm ${SNAPSHOTHASH}.tgz
590148871Scperciva	echo "done."
591148871Scperciva
592148871Scperciva	echo -n "Verifying snapshot integrity... "
593148871Scperciva# Verify the metadata files
594148871Scperciva	cut -f 2 -d '|' tINDEX.new | fetch_snapshot_verify || return 1
595148871Scperciva# Extract the index
596148871Scperciva	rm -f INDEX.new
597148871Scperciva	gunzip -c snap/`look INDEX tINDEX.new |
598148871Scperciva	    cut -f 2 -d '|'`.gz > INDEX.new
599148871Scperciva	fetch_index_sanity || return 1
600148871Scperciva# Verify the snapshot contents
601148871Scperciva	cut -f 2 -d '|' INDEX.new | fetch_snapshot_verify || return 1
602148871Scperciva	echo "done."
603148871Scperciva
604148871Scperciva# Move files into their proper locations
605148871Scperciva	rm -f tag INDEX tINDEX
606148871Scperciva	rm -rf files
607148871Scperciva	mv tag.new tag
608148871Scperciva	mv tINDEX.new tINDEX
609148871Scperciva	mv INDEX.new INDEX
610148871Scperciva	mv snap/ files/
611148871Scperciva
612148871Scperciva	return 0
613148871Scperciva}
614148871Scperciva
615148871Scperciva# Update a compressed snapshot
616148871Scpercivafetch_update() {
617148871Scperciva	rm -f patchlist diff OLD NEW filelist INDEX.new
618148871Scperciva
619148871Scperciva	OLDSNAPSHOTDATE=`cut -f 2 -d '|' < tag`
620148871Scperciva	OLDSNAPSHOTHASH=`cut -f 3 -d '|' < tag`
621148871Scperciva
622148871Scperciva	fetch_tag latest || return 1
623148871Scperciva	fetch_update_tagsanity || return 1
624148871Scperciva	fetch_update_neededp || return 0
625148871Scperciva	fetch_metadata || return 1
626148871Scperciva	fetch_metadata_sanity || return 1
627148871Scperciva
628148871Scperciva	echo -n "Updating from `date -r ${OLDSNAPSHOTDATE}` "
629148871Scperciva	echo "to `date -r ${SNAPSHOTDATE}`."
630148871Scperciva
631148871Scperciva# Generate a list of wanted metadata patches
632148871Scperciva	join -t '|' -o 1.2,2.2 tINDEX tINDEX.new |
633148871Scperciva	    fetch_make_patchlist > patchlist
634148871Scperciva
635148871Scperciva# Attempt to fetch metadata patches
636148871Scperciva	echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
637148871Scperciva	echo ${NDEBUG} "metadata patches.${DDSTATS}"
638148871Scperciva	tr '|' '-' < patchlist |
639148871Scperciva	    lam -s "tp/" - -s ".gz" |
640148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
641148871Scperciva	    2>${STATSREDIR} | fetch_progress
642148871Scperciva	echo "done."
643148871Scperciva
644148871Scperciva# Attempt to apply metadata patches
645148871Scperciva	echo -n "Applying metadata patches... "
646148871Scperciva	while read LINE; do
647148871Scperciva		X=`echo ${LINE} | cut -f 1 -d '|'`
648148871Scperciva		Y=`echo ${LINE} | cut -f 2 -d '|'`
649148871Scperciva		if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
650148871Scperciva		gunzip -c < ${X}-${Y}.gz > diff
651148871Scperciva		gunzip -c < files/${X}.gz > OLD
652148871Scperciva		cut -c 2- diff | join -t '|' -v 2 - OLD > ptmp
653148871Scperciva		grep '^\+' diff | cut -c 2- |
654148871Scperciva		    sort -k 1,1 -t '|' -m - ptmp > NEW
655148871Scperciva		if [ `${SHA256} -q NEW` = ${Y} ]; then
656148871Scperciva			mv NEW files/${Y}
657148871Scperciva			gzip -n files/${Y}
658148871Scperciva		fi
659148871Scperciva		rm -f diff OLD NEW ${X}-${Y}.gz ptmp
660148871Scperciva	done < patchlist 2>${QUIETREDIR}
661148871Scperciva	echo "done."
662148871Scperciva
663148871Scperciva# Update metadata without patches
664148871Scperciva	join -t '|' -v 2 tINDEX tINDEX.new |
665148871Scperciva	    cut -f 2 -d '|' /dev/stdin patchlist |
666148871Scperciva		while read Y; do
667148871Scperciva			if [ ! -f "files/${Y}.gz" ]; then
668148871Scperciva				echo ${Y};
669148871Scperciva			fi
670148871Scperciva		done > filelist
671148871Scperciva	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
672148871Scperciva	echo ${NDEBUG} "metadata files... "
673148871Scperciva	lam -s "f/" - -s ".gz" < filelist |
674148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
675148871Scperciva	    2>${QUIETREDIR}
676148871Scperciva
677148871Scperciva	while read Y; do
678148871Scperciva		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
679148871Scperciva			mv ${Y}.gz files/${Y}.gz
680148871Scperciva		else
681148871Scperciva			echo "metadata is corrupt."
682148871Scperciva			return 1
683148871Scperciva		fi
684148871Scperciva	done < filelist
685148871Scperciva	echo "done."
686148871Scperciva
687148871Scperciva# Extract the index
688148871Scperciva	gunzip -c files/`look INDEX tINDEX.new |
689148871Scperciva	    cut -f 2 -d '|'`.gz > INDEX.new
690148871Scperciva	fetch_index_sanity || return 1
691148871Scperciva
692149824Scperciva# If we have decided to refuse certain updates, construct a hybrid index which
693149824Scperciva# is equal to the old index for parts of the tree which we don't want to
694149824Scperciva# update, and equal to the new index for parts of the tree which gets updates.
695149824Scperciva# This means that we should always have a "complete snapshot" of the ports
696149824Scperciva# tree -- with the caveat that it isn't actually a snapshot.
697149824Scperciva	if [ ! -z "${REFUSE}" ]; then
698149824Scperciva		echo "Refusing to download updates for ${REFUSE}"	\
699149824Scperciva		    >${QUIETREDIR}
700149824Scperciva
701149824Scperciva		grep -Ev "${REFUSE}" INDEX.new > INDEX.tmp
702149824Scperciva		grep -E "${REFUSE}" INDEX |
703149824Scperciva		    sort -m -k 1,1 -t '|' - INDEX.tmp > INDEX.new
704149824Scperciva		rm -f INDEX.tmp
705149824Scperciva	fi
706149824Scperciva
707148871Scperciva# Generate a list of wanted ports patches
708148871Scperciva	join -t '|' -o 1.2,2.2 INDEX INDEX.new |
709148871Scperciva	    fetch_make_patchlist > patchlist
710148871Scperciva
711148871Scperciva# Attempt to fetch ports patches
712148871Scperciva	echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
713148871Scperciva	echo ${NDEBUG} "patches.${DDSTATS}"
714148871Scperciva	tr '|' '-' < patchlist | lam -s "bp/" - |
715148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
716148871Scperciva	    2>${STATSREDIR} | fetch_progress
717148871Scperciva	echo "done."
718148871Scperciva
719148871Scperciva# Attempt to apply ports patches
720148871Scperciva	echo -n "Applying patches... "
721148871Scperciva	while read LINE; do
722148871Scperciva		X=`echo ${LINE} | cut -f 1 -d '|'`
723148871Scperciva		Y=`echo ${LINE} | cut -f 2 -d '|'`
724148871Scperciva		if [ ! -f "${X}-${Y}" ]; then continue; fi
725148871Scperciva		gunzip -c < files/${X}.gz > OLD
726148871Scperciva		${BSPATCH} OLD NEW ${X}-${Y}
727148871Scperciva		if [ `${SHA256} -q NEW` = ${Y} ]; then
728148871Scperciva			mv NEW files/${Y}
729148871Scperciva			gzip -n files/${Y}
730148871Scperciva		fi
731148871Scperciva		rm -f diff OLD NEW ${X}-${Y}
732148871Scperciva	done < patchlist 2>${QUIETREDIR}
733148871Scperciva	echo "done."
734148871Scperciva
735148871Scperciva# Update ports without patches
736148871Scperciva	join -t '|' -v 2 INDEX INDEX.new |
737148871Scperciva	    cut -f 2 -d '|' /dev/stdin patchlist |
738148871Scperciva		while read Y; do
739148871Scperciva			if [ ! -f "files/${Y}.gz" ]; then
740148871Scperciva				echo ${Y};
741148871Scperciva			fi
742148871Scperciva		done > filelist
743148871Scperciva	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
744148871Scperciva	echo ${NDEBUG} "new ports or files... "
745148871Scperciva	lam -s "f/" - -s ".gz" < filelist |
746148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
747148871Scperciva	    2>${QUIETREDIR}
748148871Scperciva
749148871Scperciva	while read Y; do
750148871Scperciva		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
751148871Scperciva			mv ${Y}.gz files/${Y}.gz
752148871Scperciva		else
753148871Scperciva			echo "snapshot is corrupt."
754148871Scperciva			return 1
755148871Scperciva		fi
756148871Scperciva	done < filelist
757148871Scperciva	echo "done."
758148871Scperciva
759148871Scperciva# Remove files which are no longer needed
760148871Scperciva	cut -f 2 -d '|' tINDEX INDEX | sort > oldfiles
761148871Scperciva	cut -f 2 -d '|' tINDEX.new INDEX.new | sort | comm -13 - oldfiles |
762148871Scperciva	    lam -s "files/" - -s ".gz" | xargs rm -f
763148871Scperciva	rm patchlist filelist oldfiles
764148871Scperciva
765148871Scperciva# We're done!
766148871Scperciva	mv INDEX.new INDEX
767148871Scperciva	mv tINDEX.new tINDEX
768148871Scperciva	mv tag.new tag
769148871Scperciva
770148871Scperciva	return 0
771148871Scperciva}
772148871Scperciva
773148871Scperciva# Do the actual work involved in "fetch" / "cron".
774148871Scpercivafetch_run() {
775148871Scperciva	fetch_pick_server
776148871Scperciva
777148871Scperciva	fetch_key || return 1
778148871Scperciva
779148871Scperciva	if ! [ -d files -a -r tag -a -r INDEX -a -r tINDEX ]; then
780148871Scperciva		fetch_snapshot || return 1
781148871Scperciva	fi
782148871Scperciva	fetch_update || return 1
783148871Scperciva}
784148871Scperciva
785148871Scperciva# Build a ports INDEX file
786148871Scpercivaextract_make_index() {
787148871Scperciva	gunzip -c "${WORKDIR}/files/`look $1 ${WORKDIR}/tINDEX |
788148871Scperciva	    cut -f 2 -d '|'`.gz" | ${MKINDEX} /dev/stdin > ${PORTSDIR}/$2
789148871Scperciva}
790148871Scperciva
791148871Scperciva# Create INDEX, INDEX-5, INDEX-6
792148871Scpercivaextract_indices() {
793148871Scperciva	echo -n "Building new INDEX files... "
794148871Scperciva	extract_make_index DESCRIBE.4 INDEX || return 1
795148871Scperciva	extract_make_index DESCRIBE.5 INDEX-5 || return 1
796148871Scperciva	extract_make_index DESCRIBE.6 INDEX-6 || return 1
797148871Scperciva	echo "done."
798148871Scperciva}
799148871Scperciva
800149824Scperciva# Create .portsnap.INDEX; if we are REFUSEing to touch certain directories,
801149824Scperciva# merge the values from any exiting .portsnap.INDEX file.
802148871Scpercivaextract_metadata() {
803149824Scperciva	if [ -z "${REFUSE}" ]; then
804149984Scperciva		sort ${WORKDIR}/INDEX > ${PORTSDIR}/.portsnap.INDEX
805149824Scperciva	elif [ -f ${PORTSDIR}/.portsnap.INDEX ]; then
806149824Scperciva		grep -E "${REFUSE}" ${PORTSDIR}/.portsnap.INDEX	\
807149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX.tmp
808149824Scperciva		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort |
809149824Scperciva		    sort -m - ${PORTSDIR}/.portsnap.INDEX.tmp	\
810149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX
811149824Scperciva		rm -f ${PORTSDIR}/.portsnap.INDEX.tmp
812149824Scperciva	else
813149824Scperciva		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort \
814149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX
815149824Scperciva	fi
816148871Scperciva}
817148871Scperciva
818148871Scperciva# Do the actual work involved in "extract"
819148871Scpercivaextract_run() {
820154088Scperciva	mkdir -p ${PORTSDIR} || return 1
821154088Scperciva
822149824Scperciva	if !
823149824Scperciva		if ! [ -z "${EXTRACTPATH}" ]; then
824149824Scperciva			grep "^${EXTRACTPATH}" ${WORKDIR}/INDEX
825149824Scperciva		elif ! [ -z "${REFUSE}" ]; then
826149824Scperciva			grep -vE "${REFUSE}" ${WORKDIR}/INDEX
827149824Scperciva		else
828149824Scperciva			cat ${WORKDIR}/INDEX
829149824Scperciva		fi | while read LINE; do
830148871Scperciva		FILE=`echo ${LINE} | cut -f 1 -d '|'`
831148871Scperciva		HASH=`echo ${LINE} | cut -f 2 -d '|'`
832148871Scperciva		echo ${PORTSDIR}/${FILE}
833148871Scperciva		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
834148871Scperciva			echo "files/${HASH}.gz not found -- snapshot corrupt."
835148871Scperciva			return 1
836148871Scperciva		fi
837148871Scperciva		case ${FILE} in
838148871Scperciva		*/)
839148871Scperciva			rm -rf ${PORTSDIR}/${FILE}
840148871Scperciva			mkdir -p ${PORTSDIR}/${FILE}
841148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
842148871Scperciva			    -C ${PORTSDIR}/${FILE}
843148871Scperciva			;;
844148871Scperciva		*)
845148871Scperciva			rm -f ${PORTSDIR}/${FILE}
846148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
847148871Scperciva			    -C ${PORTSDIR} ${FILE}
848148871Scperciva			;;
849148871Scperciva		esac
850149041Scperciva	done; then
851149041Scperciva		return 1
852149041Scperciva	fi
853148871Scperciva	if [ ! -z "${EXTRACTPATH}" ]; then
854148871Scperciva		return 0;
855148871Scperciva	fi
856148871Scperciva
857148871Scperciva	extract_metadata
858148871Scperciva	extract_indices
859148871Scperciva}
860148871Scperciva
861148871Scperciva# Do the actual work involved in "update"
862148871Scpercivaupdate_run() {
863148871Scperciva	if ! [ -z "${INDEXONLY}" ]; then
864148871Scperciva		extract_indices >/dev/null || return 1
865148871Scperciva		return 0
866148871Scperciva	fi
867148871Scperciva
868148981Scperciva	if sort ${WORKDIR}/INDEX |
869149042Scperciva	    cmp -s ${PORTSDIR}/.portsnap.INDEX -; then
870148958Scperciva		echo "Ports tree is already up to date."
871148958Scperciva		return 0
872148958Scperciva	fi
873148958Scperciva
874149824Scperciva# If we are REFUSEing to touch certain directories, don't remove files
875149824Scperciva# from those directories (even if they are out of date)
876148871Scperciva	echo -n "Removing old files and directories... "
877149824Scperciva	if ! [ -z "${REFUSE}" ]; then 
878149824Scperciva		sort ${WORKDIR}/INDEX |
879149824Scperciva		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
880149824Scperciva		    grep -vE "${REFUSE}" |
881149824Scperciva		    lam -s "${PORTSDIR}/" - | xargs rm -rf
882149824Scperciva	else
883149824Scperciva		sort ${WORKDIR}/INDEX |
884149824Scperciva		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
885149824Scperciva		    lam -s "${PORTSDIR}/" - | xargs rm -rf
886149824Scperciva	fi
887148871Scperciva	echo "done."
888148871Scperciva
889148871Scperciva# Install new files
890148871Scperciva	echo "Extracting new files:"
891149824Scperciva	if !
892149824Scperciva		if ! [ -z "${REFUSE}" ]; then
893149824Scperciva			grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort
894149824Scperciva		else
895149824Scperciva			sort ${WORKDIR}/INDEX
896149824Scperciva		fi |
897149041Scperciva	    comm -13 ${PORTSDIR}/.portsnap.INDEX - |
898148871Scperciva	    while read LINE; do
899148871Scperciva		FILE=`echo ${LINE} | cut -f 1 -d '|'`
900148871Scperciva		HASH=`echo ${LINE} | cut -f 2 -d '|'`
901148871Scperciva		echo ${PORTSDIR}/${FILE}
902148871Scperciva		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
903148871Scperciva			echo "files/${HASH}.gz not found -- snapshot corrupt."
904148871Scperciva			return 1
905148871Scperciva		fi
906148871Scperciva		case ${FILE} in
907148871Scperciva		*/)
908148871Scperciva			mkdir -p ${PORTSDIR}/${FILE}
909148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
910148871Scperciva			    -C ${PORTSDIR}/${FILE}
911148871Scperciva			;;
912148871Scperciva		*)
913148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
914148871Scperciva			    -C ${PORTSDIR} ${FILE}
915148871Scperciva			;;
916148871Scperciva		esac
917149041Scperciva	done; then
918149041Scperciva		return 1
919149041Scperciva	fi
920148871Scperciva
921148871Scperciva	extract_metadata
922148871Scperciva	extract_indices
923148871Scperciva}
924148871Scperciva
925148871Scperciva#### Main functions -- call parameter-handling and core functions
926148871Scperciva
927148871Scperciva# Using the command line, configuration file, and defaults,
928148871Scperciva# set all the parameters which are needed later.
929148871Scpercivaget_params() {
930148871Scperciva	init_params
931148871Scperciva	parse_cmdline $@
932148871Scperciva	sanity_conffile
933148871Scperciva	default_conffile
934148871Scperciva	parse_conffile
935148871Scperciva	default_params
936148871Scperciva}
937148871Scperciva
938148871Scperciva# Fetch command.  Make sure that we're being called
939148871Scperciva# interactively, then run fetch_check_params and fetch_run
940148871Scpercivacmd_fetch() {
941148871Scperciva	if [ ! -t 0 ]; then
942148871Scperciva		echo -n "`basename $0` fetch should not "
943148871Scperciva		echo "be run non-interactively."
944148871Scperciva		echo "Run `basename $0` cron instead."
945148871Scperciva		exit 1
946148871Scperciva	fi
947148871Scperciva	fetch_check_params
948148871Scperciva	fetch_run || exit 1
949148871Scperciva}
950148871Scperciva
951148871Scperciva# Cron command.  Make sure the parameters are sensible; wait
952148871Scperciva# rand(3600) seconds; then fetch updates.  While fetching updates,
953148871Scperciva# send output to a temporary file; only print that file if the
954148871Scperciva# fetching failed.
955148871Scpercivacmd_cron() {
956148871Scperciva	fetch_check_params
957148871Scperciva	sleep `jot -r 1 0 3600`
958148871Scperciva
959148871Scperciva	TMPFILE=`mktemp /tmp/portsnap.XXXXXX` || exit 1
960148871Scperciva	if ! fetch_run >> ${TMPFILE}; then
961148871Scperciva		cat ${TMPFILE}
962148871Scperciva		rm ${TMPFILE}
963148871Scperciva		exit 1
964148871Scperciva	fi
965148871Scperciva
966148871Scperciva	rm ${TMPFILE}
967148871Scperciva}
968148871Scperciva
969148871Scperciva# Extract command.  Make sure the parameters are sensible,
970148871Scperciva# then extract the ports tree (or part thereof).
971148871Scpercivacmd_extract() {
972148871Scperciva	extract_check_params
973148871Scperciva	extract_run || exit 1
974148871Scperciva}
975148871Scperciva
976148871Scperciva# Update command.  Make sure the parameters are sensible,
977148871Scperciva# then update the ports tree.
978148871Scpercivacmd_update() {
979148871Scperciva	update_check_params
980148871Scperciva	update_run || exit 1
981148871Scperciva}
982148871Scperciva
983148871Scperciva#### Entry point
984148871Scperciva
985148871Scperciva# Make sure we find utilities from the base system
986148871Scpercivaexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
987148871Scperciva
988148871Scpercivaget_params $@
989149027Scpercivafor COMMAND in ${COMMANDS}; do
990149027Scperciva	cmd_${COMMAND}
991149027Scpercivadone
992