portsnap.sh revision 156813
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 156813 2006-03-17 18:54:44Z ru $
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.
329148871Scperciva	host -t srv "_http._tcp.${SERVERNAME}" |
330148871Scperciva	    grep -E "^_http._tcp.${SERVERNAME} has SRV record" |
331156406Sume	    cut -f 5,6,8 -d ' ' | sed -e 's/\.$//' > serverlist
332148871Scperciva
333148871Scperciva# If no records, give up -- we'll just use the server name we were given.
334148871Scperciva	if [ `wc -l < serverlist` -eq 0 ]; then
335148871Scperciva		echo " none found."
336148871Scperciva		return
337148871Scperciva	fi
338148871Scperciva
339148871Scperciva# Find the highest priority level (lowest numeric value).
340148871Scperciva	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
341148871Scperciva
342148871Scperciva# Add up the weights of the response lines at that priority level.
343148871Scperciva	SRV_WSUM=0;
344148871Scperciva	while read X; do
345148871Scperciva		case "$X" in
346148871Scperciva		${SRV_PRIORITY}\ *)
347148871Scperciva			SRV_W=`echo $X | cut -f 2 -d ' '`
348148871Scperciva			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
349148871Scperciva			;;
350148871Scperciva		esac
351148871Scperciva	done < serverlist
352148871Scperciva
353148871Scperciva# If all the weights are 0, pretend that they are all 1 instead.
354148871Scperciva	if [ ${SRV_WSUM} -eq 0 ]; then
355148871Scperciva		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
356148871Scperciva		SRV_W_ADD=1
357148871Scperciva	else
358148871Scperciva		SRV_W_ADD=0
359148871Scperciva	fi
360148871Scperciva
361148871Scperciva# Pick a random value between 1 and the sum of the weights
362148871Scperciva	SRV_RND=`jot -r 1 1 ${SRV_WSUM}`
363148871Scperciva
364148871Scperciva# Read through the list of mirrors and set SERVERNAME
365148871Scperciva	while read X; do
366148871Scperciva		case "$X" in
367148871Scperciva		${SRV_PRIORITY}\ *)
368148871Scperciva			SRV_W=`echo $X | cut -f 2 -d ' '`
369148871Scperciva			SRV_W=$(($SRV_W + $SRV_W_ADD))
370148871Scperciva			if [ $SRV_RND -le $SRV_W ]; then
371148871Scperciva				SERVERNAME=`echo $X | cut -f 3 -d ' '`
372148871Scperciva				break
373148871Scperciva			else
374148871Scperciva				SRV_RND=$(($SRV_RND - $SRV_W))
375148871Scperciva			fi
376148871Scperciva			;;
377148871Scperciva		esac
378148871Scperciva	done < serverlist
379148871Scperciva
380148871Scperciva	echo " using ${SERVERNAME}"
381148871Scperciva}
382148871Scperciva
383148871Scperciva# Check that we have a public key with an appropriate hash, or
384148871Scperciva# fetch the key if it doesn't exist.
385148871Scpercivafetch_key() {
386148871Scperciva	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
387148871Scperciva		return
388148871Scperciva	fi
389148871Scperciva
390148871Scperciva	echo -n "Fetching public key... "
391148871Scperciva	rm -f pub.ssl
392148871Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/pub.ssl \
393148871Scperciva	    2>${QUIETREDIR} || true
394148871Scperciva	if ! [ -r pub.ssl ]; then
395148871Scperciva		echo "failed."
396148871Scperciva		return 1
397148871Scperciva	fi
398148871Scperciva	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
399148871Scperciva		echo "key has incorrect hash."
400148871Scperciva		rm -f pub.ssl
401148871Scperciva		return 1
402148871Scperciva	fi
403148871Scperciva	echo "done."
404148871Scperciva}
405148871Scperciva
406148871Scperciva# Fetch a snapshot tag
407148871Scpercivafetch_tag() {
408148871Scperciva	rm -f snapshot.ssl tag.new
409148871Scperciva
410148871Scperciva	echo ${NDEBUG} "Fetching snapshot tag... "
411148871Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/$1.ssl
412148871Scperciva	    2>${QUIETREDIR} || true
413148871Scperciva	if ! [ -r $1.ssl ]; then
414148871Scperciva		echo "failed."
415148871Scperciva		return 1
416148871Scperciva	fi
417148871Scperciva
418148871Scperciva	openssl rsautl -pubin -inkey pub.ssl -verify		\
419148871Scperciva	    < $1.ssl > tag.new 2>${QUIETREDIR} || true
420148871Scperciva	rm $1.ssl
421148871Scperciva
422148871Scperciva	if ! [ `wc -l < tag.new` = 1 ] ||
423148871Scperciva	    ! grep -qE "^portsnap\|[0-9]{10}\|[0-9a-f]{64}" tag.new; then
424148871Scperciva		echo "invalid snapshot tag."
425148871Scperciva		return 1
426148871Scperciva	fi
427148871Scperciva
428148871Scperciva	echo "done."
429148871Scperciva
430148871Scperciva	SNAPSHOTDATE=`cut -f 2 -d '|' < tag.new`
431148871Scperciva	SNAPSHOTHASH=`cut -f 3 -d '|' < tag.new`
432148871Scperciva}
433148871Scperciva
434148871Scperciva# Sanity-check the date on a snapshot tag
435148871Scpercivafetch_snapshot_tagsanity() {
436148871Scperciva	if [ `date "+%s"` -gt `expr ${SNAPSHOTDATE} + 31536000` ]; then
437148871Scperciva		echo "Snapshot appears to be more than a year old!"
438148871Scperciva		echo "(Is the system clock correct?)"
439149868Scperciva		echo "Cowardly refusing to proceed any further."
440148871Scperciva		return 1
441148871Scperciva	fi
442148871Scperciva	if [ `date "+%s"` -lt `expr ${SNAPSHOTDATE} - 86400` ]; then
443148871Scperciva		echo -n "Snapshot appears to have been created more than "
444148871Scperciva		echo "one day into the future!"
445148871Scperciva		echo "(Is the system clock correct?)"
446148871Scperciva		echo "Cowardly refusing to proceed any further."
447148871Scperciva		return 1
448148871Scperciva	fi
449148871Scperciva}
450148871Scperciva
451148871Scperciva# Sanity-check the date on a snapshot update tag
452148871Scpercivafetch_update_tagsanity() {
453148871Scperciva	fetch_snapshot_tagsanity || return 1
454148871Scperciva
455148871Scperciva	if [ ${OLDSNAPSHOTDATE} -gt ${SNAPSHOTDATE} ]; then
456148871Scperciva		echo -n "Latest snapshot on server is "
457148871Scperciva		echo "older than what we already have!"
458148871Scperciva		echo -n "Cowardly refusing to downgrade from "
459148871Scperciva		date -r ${OLDSNAPSHOTDATE}
460148879Scperciva		echo "to `date -r ${SNAPSHOTDATE}`."
461148871Scperciva		return 1
462148871Scperciva	fi
463148871Scperciva}
464148871Scperciva
465148871Scperciva# Compare old and new tags; return 1 if update is unnecessary
466148871Scpercivafetch_update_neededp() {
467148871Scperciva	if [ ${OLDSNAPSHOTDATE} -eq ${SNAPSHOTDATE} ]; then
468148871Scperciva		echo -n "Latest snapshot on server matches "
469148871Scperciva		echo "what we already have."
470148871Scperciva		echo "No updates needed."
471148871Scperciva		rm tag.new
472148871Scperciva		return 1
473148871Scperciva	fi
474148871Scperciva	if [ ${OLDSNAPSHOTHASH} = ${SNAPSHOTHASH} ]; then
475148871Scperciva		echo -n "Ports tree hasn't changed since "
476148871Scperciva		echo "last snapshot."
477148871Scperciva		echo "No updates needed."
478148871Scperciva		rm tag.new
479148871Scperciva		return 1
480148871Scperciva	fi
481148871Scperciva
482148871Scperciva	return 0
483148871Scperciva}
484148871Scperciva
485148871Scperciva# Fetch snapshot metadata file
486148871Scpercivafetch_metadata() {
487148871Scperciva	rm -f ${SNAPSHOTHASH} tINDEX.new
488148871Scperciva
489148871Scperciva	echo ${NDEBUG} "Fetching snapshot metadata... "
490148871Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/t/${SNAPSHOTHASH}
491148871Scperciva	    2>${QUIETREDIR} || return
492148871Scperciva	if [ `${SHA256} -q ${SNAPSHOTHASH}` != ${SNAPSHOTHASH} ]; then
493148871Scperciva		echo "snapshot metadata corrupt."
494148871Scperciva		return 1
495148871Scperciva	fi
496148871Scperciva	mv ${SNAPSHOTHASH} tINDEX.new
497148871Scperciva	echo "done."
498148871Scperciva}
499148871Scperciva
500148871Scperciva# Warn user about bogus metadata
501148871Scpercivafetch_metadata_freakout() {
502148871Scperciva	echo
503148871Scperciva	echo "Portsnap metadata is correctly signed, but contains"
504148871Scperciva	echo "at least one line which appears bogus."
505148871Scperciva	echo "Cowardly refusing to proceed any further."
506148871Scperciva}
507148871Scperciva
508148871Scperciva# Sanity-check a snapshot metadata file
509148871Scpercivafetch_metadata_sanity() {
510148871Scperciva	if grep -qvE "^[0-9A-Z.]+\|[0-9a-f]{64}$" tINDEX.new; then
511148871Scperciva		fetch_metadata_freakout
512148871Scperciva		return 1
513148871Scperciva	fi
514148871Scperciva	if [ `look INDEX tINDEX.new | wc -l` != 1 ]; then
515148871Scperciva		echo
516148871Scperciva		echo "Portsnap metadata appears bogus."
517148871Scperciva		echo "Cowardly refusing to proceed any further."
518148871Scperciva		return 1
519148871Scperciva	fi
520148871Scperciva}
521148871Scperciva
522148871Scperciva# Take a list of ${oldhash}|${newhash} and output a list of needed patches
523148871Scpercivafetch_make_patchlist() {
524148871Scperciva	grep -vE "^([0-9a-f]{64})\|\1$" | 
525148871Scperciva		while read LINE; do
526148871Scperciva			X=`echo ${LINE} | cut -f 1 -d '|'`
527148871Scperciva			Y=`echo ${LINE} | cut -f 2 -d '|'`
528148871Scperciva			if [ -f "files/${Y}.gz" ]; then continue; fi
529148871Scperciva			if [ ! -f "files/${X}.gz" ]; then continue; fi
530148871Scperciva			echo "${LINE}"
531148871Scperciva		done
532148871Scperciva}
533148871Scperciva
534148871Scperciva# Print user-friendly progress statistics
535148871Scpercivafetch_progress() {
536148871Scperciva	LNC=0
537148871Scperciva	while read x; do
538148871Scperciva		LNC=$(($LNC + 1))
539148871Scperciva		if [ $(($LNC % 10)) = 0 ]; then
540149023Scperciva			echo -n $LNC
541148871Scperciva		elif [ $(($LNC % 2)) = 0 ]; then
542148871Scperciva			echo -n .
543148871Scperciva		fi
544148871Scperciva	done
545148871Scperciva	echo -n " "
546148871Scperciva}
547148871Scperciva
548148871Scperciva# Sanity-check an index file
549148871Scpercivafetch_index_sanity() {
550148871Scperciva	if grep -qvE "^[-_+./@0-9A-Za-z]+\|[0-9a-f]{64}$" INDEX.new ||
551148871Scperciva	    fgrep -q "./" INDEX.new; then
552148871Scperciva		fetch_metadata_freakout
553148871Scperciva		return 1
554148871Scperciva	fi
555148871Scperciva}
556148871Scperciva
557148871Scperciva# Verify a list of files
558148871Scpercivafetch_snapshot_verify() {
559148871Scperciva	while read F; do
560148871Scperciva		if [ `gunzip -c snap/${F} | ${SHA256} -q` != ${F} ]; then
561148871Scperciva			echo "snapshot corrupt."
562148871Scperciva			return 1
563148871Scperciva		fi
564148871Scperciva	done
565148871Scperciva	return 0
566148871Scperciva}
567148871Scperciva
568148871Scperciva# Fetch a snapshot tarball, extract, and verify.
569148871Scpercivafetch_snapshot() {
570148871Scperciva	fetch_tag snapshot || return 1
571148871Scperciva	fetch_snapshot_tagsanity || return 1
572148871Scperciva	fetch_metadata || return 1
573148871Scperciva	fetch_metadata_sanity || return 1
574148871Scperciva
575148871Scperciva	rm -rf snap/
576148871Scperciva
577148871Scperciva# Don't ask fetch(1) to be quiet -- downloading a snapshot of ~ 35MB will
578148871Scperciva# probably take a while, so the progrees reports that fetch(1) generates
579148871Scperciva# will be useful for keeping the users' attention from drifting.
580148871Scperciva	echo "Fetching snapshot generated at `date -r ${SNAPSHOTDATE}`:"
581154693Scperciva	fetch -r http://${SERVERNAME}/s/${SNAPSHOTHASH}.tgz || return 1
582148871Scperciva
583148871Scperciva	echo -n "Extracting snapshot... "
584148871Scperciva	tar -xzf ${SNAPSHOTHASH}.tgz snap/ || return 1
585148871Scperciva	rm ${SNAPSHOTHASH}.tgz
586148871Scperciva	echo "done."
587148871Scperciva
588148871Scperciva	echo -n "Verifying snapshot integrity... "
589148871Scperciva# Verify the metadata files
590148871Scperciva	cut -f 2 -d '|' tINDEX.new | fetch_snapshot_verify || return 1
591148871Scperciva# Extract the index
592148871Scperciva	rm -f INDEX.new
593148871Scperciva	gunzip -c snap/`look INDEX tINDEX.new |
594148871Scperciva	    cut -f 2 -d '|'`.gz > INDEX.new
595148871Scperciva	fetch_index_sanity || return 1
596148871Scperciva# Verify the snapshot contents
597148871Scperciva	cut -f 2 -d '|' INDEX.new | fetch_snapshot_verify || return 1
598148871Scperciva	echo "done."
599148871Scperciva
600148871Scperciva# Move files into their proper locations
601148871Scperciva	rm -f tag INDEX tINDEX
602148871Scperciva	rm -rf files
603148871Scperciva	mv tag.new tag
604148871Scperciva	mv tINDEX.new tINDEX
605148871Scperciva	mv INDEX.new INDEX
606148871Scperciva	mv snap/ files/
607148871Scperciva
608148871Scperciva	return 0
609148871Scperciva}
610148871Scperciva
611148871Scperciva# Update a compressed snapshot
612148871Scpercivafetch_update() {
613148871Scperciva	rm -f patchlist diff OLD NEW filelist INDEX.new
614148871Scperciva
615148871Scperciva	OLDSNAPSHOTDATE=`cut -f 2 -d '|' < tag`
616148871Scperciva	OLDSNAPSHOTHASH=`cut -f 3 -d '|' < tag`
617148871Scperciva
618148871Scperciva	fetch_tag latest || return 1
619148871Scperciva	fetch_update_tagsanity || return 1
620148871Scperciva	fetch_update_neededp || return 0
621148871Scperciva	fetch_metadata || return 1
622148871Scperciva	fetch_metadata_sanity || return 1
623148871Scperciva
624148871Scperciva	echo -n "Updating from `date -r ${OLDSNAPSHOTDATE}` "
625148871Scperciva	echo "to `date -r ${SNAPSHOTDATE}`."
626148871Scperciva
627148871Scperciva# Generate a list of wanted metadata patches
628148871Scperciva	join -t '|' -o 1.2,2.2 tINDEX tINDEX.new |
629148871Scperciva	    fetch_make_patchlist > patchlist
630148871Scperciva
631148871Scperciva# Attempt to fetch metadata patches
632148871Scperciva	echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
633148871Scperciva	echo ${NDEBUG} "metadata patches.${DDSTATS}"
634148871Scperciva	tr '|' '-' < patchlist |
635148871Scperciva	    lam -s "tp/" - -s ".gz" |
636148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
637148871Scperciva	    2>${STATSREDIR} | fetch_progress
638148871Scperciva	echo "done."
639148871Scperciva
640148871Scperciva# Attempt to apply metadata patches
641148871Scperciva	echo -n "Applying metadata patches... "
642148871Scperciva	while read LINE; do
643148871Scperciva		X=`echo ${LINE} | cut -f 1 -d '|'`
644148871Scperciva		Y=`echo ${LINE} | cut -f 2 -d '|'`
645148871Scperciva		if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
646148871Scperciva		gunzip -c < ${X}-${Y}.gz > diff
647148871Scperciva		gunzip -c < files/${X}.gz > OLD
648148871Scperciva		cut -c 2- diff | join -t '|' -v 2 - OLD > ptmp
649148871Scperciva		grep '^\+' diff | cut -c 2- |
650148871Scperciva		    sort -k 1,1 -t '|' -m - ptmp > NEW
651148871Scperciva		if [ `${SHA256} -q NEW` = ${Y} ]; then
652148871Scperciva			mv NEW files/${Y}
653148871Scperciva			gzip -n files/${Y}
654148871Scperciva		fi
655148871Scperciva		rm -f diff OLD NEW ${X}-${Y}.gz ptmp
656148871Scperciva	done < patchlist 2>${QUIETREDIR}
657148871Scperciva	echo "done."
658148871Scperciva
659148871Scperciva# Update metadata without patches
660148871Scperciva	join -t '|' -v 2 tINDEX tINDEX.new |
661148871Scperciva	    cut -f 2 -d '|' /dev/stdin patchlist |
662148871Scperciva		while read Y; do
663148871Scperciva			if [ ! -f "files/${Y}.gz" ]; then
664148871Scperciva				echo ${Y};
665148871Scperciva			fi
666148871Scperciva		done > filelist
667148871Scperciva	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
668148871Scperciva	echo ${NDEBUG} "metadata files... "
669148871Scperciva	lam -s "f/" - -s ".gz" < filelist |
670148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
671148871Scperciva	    2>${QUIETREDIR}
672148871Scperciva
673148871Scperciva	while read Y; do
674148871Scperciva		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
675148871Scperciva			mv ${Y}.gz files/${Y}.gz
676148871Scperciva		else
677148871Scperciva			echo "metadata is corrupt."
678148871Scperciva			return 1
679148871Scperciva		fi
680148871Scperciva	done < filelist
681148871Scperciva	echo "done."
682148871Scperciva
683148871Scperciva# Extract the index
684148871Scperciva	gunzip -c files/`look INDEX tINDEX.new |
685148871Scperciva	    cut -f 2 -d '|'`.gz > INDEX.new
686148871Scperciva	fetch_index_sanity || return 1
687148871Scperciva
688149824Scperciva# If we have decided to refuse certain updates, construct a hybrid index which
689149824Scperciva# is equal to the old index for parts of the tree which we don't want to
690149824Scperciva# update, and equal to the new index for parts of the tree which gets updates.
691149824Scperciva# This means that we should always have a "complete snapshot" of the ports
692149824Scperciva# tree -- with the caveat that it isn't actually a snapshot.
693149824Scperciva	if [ ! -z "${REFUSE}" ]; then
694149824Scperciva		echo "Refusing to download updates for ${REFUSE}"	\
695149824Scperciva		    >${QUIETREDIR}
696149824Scperciva
697149824Scperciva		grep -Ev "${REFUSE}" INDEX.new > INDEX.tmp
698149824Scperciva		grep -E "${REFUSE}" INDEX |
699149824Scperciva		    sort -m -k 1,1 -t '|' - INDEX.tmp > INDEX.new
700149824Scperciva		rm -f INDEX.tmp
701149824Scperciva	fi
702149824Scperciva
703148871Scperciva# Generate a list of wanted ports patches
704148871Scperciva	join -t '|' -o 1.2,2.2 INDEX INDEX.new |
705148871Scperciva	    fetch_make_patchlist > patchlist
706148871Scperciva
707148871Scperciva# Attempt to fetch ports patches
708148871Scperciva	echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
709148871Scperciva	echo ${NDEBUG} "patches.${DDSTATS}"
710148871Scperciva	tr '|' '-' < patchlist | lam -s "bp/" - |
711148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
712148871Scperciva	    2>${STATSREDIR} | fetch_progress
713148871Scperciva	echo "done."
714148871Scperciva
715148871Scperciva# Attempt to apply ports patches
716148871Scperciva	echo -n "Applying patches... "
717148871Scperciva	while read LINE; do
718148871Scperciva		X=`echo ${LINE} | cut -f 1 -d '|'`
719148871Scperciva		Y=`echo ${LINE} | cut -f 2 -d '|'`
720148871Scperciva		if [ ! -f "${X}-${Y}" ]; then continue; fi
721148871Scperciva		gunzip -c < files/${X}.gz > OLD
722148871Scperciva		${BSPATCH} OLD NEW ${X}-${Y}
723148871Scperciva		if [ `${SHA256} -q NEW` = ${Y} ]; then
724148871Scperciva			mv NEW files/${Y}
725148871Scperciva			gzip -n files/${Y}
726148871Scperciva		fi
727148871Scperciva		rm -f diff OLD NEW ${X}-${Y}
728148871Scperciva	done < patchlist 2>${QUIETREDIR}
729148871Scperciva	echo "done."
730148871Scperciva
731148871Scperciva# Update ports without patches
732148871Scperciva	join -t '|' -v 2 INDEX INDEX.new |
733148871Scperciva	    cut -f 2 -d '|' /dev/stdin patchlist |
734148871Scperciva		while read Y; do
735148871Scperciva			if [ ! -f "files/${Y}.gz" ]; then
736148871Scperciva				echo ${Y};
737148871Scperciva			fi
738148871Scperciva		done > filelist
739148871Scperciva	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
740148871Scperciva	echo ${NDEBUG} "new ports or files... "
741148871Scperciva	lam -s "f/" - -s ".gz" < filelist |
742148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
743148871Scperciva	    2>${QUIETREDIR}
744148871Scperciva
745148871Scperciva	while read Y; do
746148871Scperciva		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
747148871Scperciva			mv ${Y}.gz files/${Y}.gz
748148871Scperciva		else
749148871Scperciva			echo "snapshot is corrupt."
750148871Scperciva			return 1
751148871Scperciva		fi
752148871Scperciva	done < filelist
753148871Scperciva	echo "done."
754148871Scperciva
755148871Scperciva# Remove files which are no longer needed
756148871Scperciva	cut -f 2 -d '|' tINDEX INDEX | sort > oldfiles
757148871Scperciva	cut -f 2 -d '|' tINDEX.new INDEX.new | sort | comm -13 - oldfiles |
758148871Scperciva	    lam -s "files/" - -s ".gz" | xargs rm -f
759148871Scperciva	rm patchlist filelist oldfiles
760148871Scperciva
761148871Scperciva# We're done!
762148871Scperciva	mv INDEX.new INDEX
763148871Scperciva	mv tINDEX.new tINDEX
764148871Scperciva	mv tag.new tag
765148871Scperciva
766148871Scperciva	return 0
767148871Scperciva}
768148871Scperciva
769148871Scperciva# Do the actual work involved in "fetch" / "cron".
770148871Scpercivafetch_run() {
771148871Scperciva	fetch_pick_server
772148871Scperciva
773148871Scperciva	fetch_key || return 1
774148871Scperciva
775148871Scperciva	if ! [ -d files -a -r tag -a -r INDEX -a -r tINDEX ]; then
776148871Scperciva		fetch_snapshot || return 1
777148871Scperciva	fi
778148871Scperciva	fetch_update || return 1
779148871Scperciva}
780148871Scperciva
781148871Scperciva# Build a ports INDEX file
782148871Scpercivaextract_make_index() {
783148871Scperciva	gunzip -c "${WORKDIR}/files/`look $1 ${WORKDIR}/tINDEX |
784148871Scperciva	    cut -f 2 -d '|'`.gz" | ${MKINDEX} /dev/stdin > ${PORTSDIR}/$2
785148871Scperciva}
786148871Scperciva
787148871Scperciva# Create INDEX, INDEX-5, INDEX-6
788148871Scpercivaextract_indices() {
789148871Scperciva	echo -n "Building new INDEX files... "
790148871Scperciva	extract_make_index DESCRIBE.4 INDEX || return 1
791148871Scperciva	extract_make_index DESCRIBE.5 INDEX-5 || return 1
792148871Scperciva	extract_make_index DESCRIBE.6 INDEX-6 || return 1
793148871Scperciva	echo "done."
794148871Scperciva}
795148871Scperciva
796149824Scperciva# Create .portsnap.INDEX; if we are REFUSEing to touch certain directories,
797149824Scperciva# merge the values from any exiting .portsnap.INDEX file.
798148871Scpercivaextract_metadata() {
799149824Scperciva	if [ -z "${REFUSE}" ]; then
800149984Scperciva		sort ${WORKDIR}/INDEX > ${PORTSDIR}/.portsnap.INDEX
801149824Scperciva	elif [ -f ${PORTSDIR}/.portsnap.INDEX ]; then
802149824Scperciva		grep -E "${REFUSE}" ${PORTSDIR}/.portsnap.INDEX	\
803149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX.tmp
804149824Scperciva		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort |
805149824Scperciva		    sort -m - ${PORTSDIR}/.portsnap.INDEX.tmp	\
806149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX
807149824Scperciva		rm -f ${PORTSDIR}/.portsnap.INDEX.tmp
808149824Scperciva	else
809149824Scperciva		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort \
810149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX
811149824Scperciva	fi
812148871Scperciva}
813148871Scperciva
814148871Scperciva# Do the actual work involved in "extract"
815148871Scpercivaextract_run() {
816154088Scperciva	mkdir -p ${PORTSDIR} || return 1
817154088Scperciva
818149824Scperciva	if !
819149824Scperciva		if ! [ -z "${EXTRACTPATH}" ]; then
820149824Scperciva			grep "^${EXTRACTPATH}" ${WORKDIR}/INDEX
821149824Scperciva		elif ! [ -z "${REFUSE}" ]; then
822149824Scperciva			grep -vE "${REFUSE}" ${WORKDIR}/INDEX
823149824Scperciva		else
824149824Scperciva			cat ${WORKDIR}/INDEX
825149824Scperciva		fi | while read LINE; do
826148871Scperciva		FILE=`echo ${LINE} | cut -f 1 -d '|'`
827148871Scperciva		HASH=`echo ${LINE} | cut -f 2 -d '|'`
828148871Scperciva		echo ${PORTSDIR}/${FILE}
829148871Scperciva		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
830148871Scperciva			echo "files/${HASH}.gz not found -- snapshot corrupt."
831148871Scperciva			return 1
832148871Scperciva		fi
833148871Scperciva		case ${FILE} in
834148871Scperciva		*/)
835148871Scperciva			rm -rf ${PORTSDIR}/${FILE}
836148871Scperciva			mkdir -p ${PORTSDIR}/${FILE}
837148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
838148871Scperciva			    -C ${PORTSDIR}/${FILE}
839148871Scperciva			;;
840148871Scperciva		*)
841148871Scperciva			rm -f ${PORTSDIR}/${FILE}
842148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
843148871Scperciva			    -C ${PORTSDIR} ${FILE}
844148871Scperciva			;;
845148871Scperciva		esac
846149041Scperciva	done; then
847149041Scperciva		return 1
848149041Scperciva	fi
849148871Scperciva	if [ ! -z "${EXTRACTPATH}" ]; then
850148871Scperciva		return 0;
851148871Scperciva	fi
852148871Scperciva
853148871Scperciva	extract_metadata
854148871Scperciva	extract_indices
855148871Scperciva}
856148871Scperciva
857148871Scperciva# Do the actual work involved in "update"
858148871Scpercivaupdate_run() {
859148871Scperciva	if ! [ -z "${INDEXONLY}" ]; then
860148871Scperciva		extract_indices >/dev/null || return 1
861148871Scperciva		return 0
862148871Scperciva	fi
863148871Scperciva
864148981Scperciva	if sort ${WORKDIR}/INDEX |
865149042Scperciva	    cmp -s ${PORTSDIR}/.portsnap.INDEX -; then
866148958Scperciva		echo "Ports tree is already up to date."
867148958Scperciva		return 0
868148958Scperciva	fi
869148958Scperciva
870149824Scperciva# If we are REFUSEing to touch certain directories, don't remove files
871149824Scperciva# from those directories (even if they are out of date)
872148871Scperciva	echo -n "Removing old files and directories... "
873149824Scperciva	if ! [ -z "${REFUSE}" ]; then 
874149824Scperciva		sort ${WORKDIR}/INDEX |
875149824Scperciva		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
876149824Scperciva		    grep -vE "${REFUSE}" |
877149824Scperciva		    lam -s "${PORTSDIR}/" - | xargs rm -rf
878149824Scperciva	else
879149824Scperciva		sort ${WORKDIR}/INDEX |
880149824Scperciva		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
881149824Scperciva		    lam -s "${PORTSDIR}/" - | xargs rm -rf
882149824Scperciva	fi
883148871Scperciva	echo "done."
884148871Scperciva
885148871Scperciva# Install new files
886148871Scperciva	echo "Extracting new files:"
887149824Scperciva	if !
888149824Scperciva		if ! [ -z "${REFUSE}" ]; then
889149824Scperciva			grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort
890149824Scperciva		else
891149824Scperciva			sort ${WORKDIR}/INDEX
892149824Scperciva		fi |
893149041Scperciva	    comm -13 ${PORTSDIR}/.portsnap.INDEX - |
894148871Scperciva	    while read LINE; do
895148871Scperciva		FILE=`echo ${LINE} | cut -f 1 -d '|'`
896148871Scperciva		HASH=`echo ${LINE} | cut -f 2 -d '|'`
897148871Scperciva		echo ${PORTSDIR}/${FILE}
898148871Scperciva		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
899148871Scperciva			echo "files/${HASH}.gz not found -- snapshot corrupt."
900148871Scperciva			return 1
901148871Scperciva		fi
902148871Scperciva		case ${FILE} in
903148871Scperciva		*/)
904148871Scperciva			mkdir -p ${PORTSDIR}/${FILE}
905148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
906148871Scperciva			    -C ${PORTSDIR}/${FILE}
907148871Scperciva			;;
908148871Scperciva		*)
909148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
910148871Scperciva			    -C ${PORTSDIR} ${FILE}
911148871Scperciva			;;
912148871Scperciva		esac
913149041Scperciva	done; then
914149041Scperciva		return 1
915149041Scperciva	fi
916148871Scperciva
917148871Scperciva	extract_metadata
918148871Scperciva	extract_indices
919148871Scperciva}
920148871Scperciva
921148871Scperciva#### Main functions -- call parameter-handling and core functions
922148871Scperciva
923148871Scperciva# Using the command line, configuration file, and defaults,
924148871Scperciva# set all the parameters which are needed later.
925148871Scpercivaget_params() {
926148871Scperciva	init_params
927148871Scperciva	parse_cmdline $@
928148871Scperciva	sanity_conffile
929148871Scperciva	default_conffile
930148871Scperciva	parse_conffile
931148871Scperciva	default_params
932148871Scperciva}
933148871Scperciva
934148871Scperciva# Fetch command.  Make sure that we're being called
935148871Scperciva# interactively, then run fetch_check_params and fetch_run
936148871Scpercivacmd_fetch() {
937148871Scperciva	if [ ! -t 0 ]; then
938148871Scperciva		echo -n "`basename $0` fetch should not "
939148871Scperciva		echo "be run non-interactively."
940148871Scperciva		echo "Run `basename $0` cron instead."
941148871Scperciva		exit 1
942148871Scperciva	fi
943148871Scperciva	fetch_check_params
944148871Scperciva	fetch_run || exit 1
945148871Scperciva}
946148871Scperciva
947148871Scperciva# Cron command.  Make sure the parameters are sensible; wait
948148871Scperciva# rand(3600) seconds; then fetch updates.  While fetching updates,
949148871Scperciva# send output to a temporary file; only print that file if the
950148871Scperciva# fetching failed.
951148871Scpercivacmd_cron() {
952148871Scperciva	fetch_check_params
953148871Scperciva	sleep `jot -r 1 0 3600`
954148871Scperciva
955148871Scperciva	TMPFILE=`mktemp /tmp/portsnap.XXXXXX` || exit 1
956148871Scperciva	if ! fetch_run >> ${TMPFILE}; then
957148871Scperciva		cat ${TMPFILE}
958148871Scperciva		rm ${TMPFILE}
959148871Scperciva		exit 1
960148871Scperciva	fi
961148871Scperciva
962148871Scperciva	rm ${TMPFILE}
963148871Scperciva}
964148871Scperciva
965148871Scperciva# Extract command.  Make sure the parameters are sensible,
966148871Scperciva# then extract the ports tree (or part thereof).
967148871Scpercivacmd_extract() {
968148871Scperciva	extract_check_params
969148871Scperciva	extract_run || exit 1
970148871Scperciva}
971148871Scperciva
972148871Scperciva# Update command.  Make sure the parameters are sensible,
973148871Scperciva# then update the ports tree.
974148871Scpercivacmd_update() {
975148871Scperciva	update_check_params
976148871Scperciva	update_run || exit 1
977148871Scperciva}
978148871Scperciva
979148871Scperciva#### Entry point
980148871Scperciva
981148871Scperciva# Make sure we find utilities from the base system
982148871Scpercivaexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
983148871Scperciva
984148871Scpercivaget_params $@
985149027Scpercivafor COMMAND in ${COMMANDS}; do
986149027Scperciva	cmd_${COMMAND}
987149027Scpercivadone
988