portsnap.sh revision 154088
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 154088 2006-01-06 20:39:11Z 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
321150159Scperciva# NO_BIND flag 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" |
331148871Scperciva	    cut -f 5,6,8 -d ' ' > 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 -f ${SNAPSHOTHASH}.tgz
576148871Scperciva	rm -rf snap/
577148871Scperciva
578148871Scperciva# Don't ask fetch(1) to be quiet -- downloading a snapshot of ~ 35MB will
579148871Scperciva# probably take a while, so the progrees reports that fetch(1) generates
580148871Scperciva# will be useful for keeping the users' attention from drifting.
581148871Scperciva	echo "Fetching snapshot generated at `date -r ${SNAPSHOTDATE}`:"
582148871Scperciva	fetch http://${SERVERNAME}/s/${SNAPSHOTHASH}.tgz || return 1
583148871Scperciva
584148871Scperciva	echo -n "Extracting snapshot... "
585148871Scperciva	tar -xzf ${SNAPSHOTHASH}.tgz snap/ || return 1
586148871Scperciva	rm ${SNAPSHOTHASH}.tgz
587148871Scperciva	echo "done."
588148871Scperciva
589148871Scperciva	echo -n "Verifying snapshot integrity... "
590148871Scperciva# Verify the metadata files
591148871Scperciva	cut -f 2 -d '|' tINDEX.new | fetch_snapshot_verify || return 1
592148871Scperciva# Extract the index
593148871Scperciva	rm -f INDEX.new
594148871Scperciva	gunzip -c snap/`look INDEX tINDEX.new |
595148871Scperciva	    cut -f 2 -d '|'`.gz > INDEX.new
596148871Scperciva	fetch_index_sanity || return 1
597148871Scperciva# Verify the snapshot contents
598148871Scperciva	cut -f 2 -d '|' INDEX.new | fetch_snapshot_verify || return 1
599148871Scperciva	echo "done."
600148871Scperciva
601148871Scperciva# Move files into their proper locations
602148871Scperciva	rm -f tag INDEX tINDEX
603148871Scperciva	rm -rf files
604148871Scperciva	mv tag.new tag
605148871Scperciva	mv tINDEX.new tINDEX
606148871Scperciva	mv INDEX.new INDEX
607148871Scperciva	mv snap/ files/
608148871Scperciva
609148871Scperciva	return 0
610148871Scperciva}
611148871Scperciva
612148871Scperciva# Update a compressed snapshot
613148871Scpercivafetch_update() {
614148871Scperciva	rm -f patchlist diff OLD NEW filelist INDEX.new
615148871Scperciva
616148871Scperciva	OLDSNAPSHOTDATE=`cut -f 2 -d '|' < tag`
617148871Scperciva	OLDSNAPSHOTHASH=`cut -f 3 -d '|' < tag`
618148871Scperciva
619148871Scperciva	fetch_tag latest || return 1
620148871Scperciva	fetch_update_tagsanity || return 1
621148871Scperciva	fetch_update_neededp || return 0
622148871Scperciva	fetch_metadata || return 1
623148871Scperciva	fetch_metadata_sanity || return 1
624148871Scperciva
625148871Scperciva	echo -n "Updating from `date -r ${OLDSNAPSHOTDATE}` "
626148871Scperciva	echo "to `date -r ${SNAPSHOTDATE}`."
627148871Scperciva
628148871Scperciva# Generate a list of wanted metadata patches
629148871Scperciva	join -t '|' -o 1.2,2.2 tINDEX tINDEX.new |
630148871Scperciva	    fetch_make_patchlist > patchlist
631148871Scperciva
632148871Scperciva# Attempt to fetch metadata patches
633148871Scperciva	echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
634148871Scperciva	echo ${NDEBUG} "metadata patches.${DDSTATS}"
635148871Scperciva	tr '|' '-' < patchlist |
636148871Scperciva	    lam -s "tp/" - -s ".gz" |
637148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
638148871Scperciva	    2>${STATSREDIR} | fetch_progress
639148871Scperciva	echo "done."
640148871Scperciva
641148871Scperciva# Attempt to apply metadata patches
642148871Scperciva	echo -n "Applying metadata patches... "
643148871Scperciva	while read LINE; do
644148871Scperciva		X=`echo ${LINE} | cut -f 1 -d '|'`
645148871Scperciva		Y=`echo ${LINE} | cut -f 2 -d '|'`
646148871Scperciva		if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
647148871Scperciva		gunzip -c < ${X}-${Y}.gz > diff
648148871Scperciva		gunzip -c < files/${X}.gz > OLD
649148871Scperciva		cut -c 2- diff | join -t '|' -v 2 - OLD > ptmp
650148871Scperciva		grep '^\+' diff | cut -c 2- |
651148871Scperciva		    sort -k 1,1 -t '|' -m - ptmp > NEW
652148871Scperciva		if [ `${SHA256} -q NEW` = ${Y} ]; then
653148871Scperciva			mv NEW files/${Y}
654148871Scperciva			gzip -n files/${Y}
655148871Scperciva		fi
656148871Scperciva		rm -f diff OLD NEW ${X}-${Y}.gz ptmp
657148871Scperciva	done < patchlist 2>${QUIETREDIR}
658148871Scperciva	echo "done."
659148871Scperciva
660148871Scperciva# Update metadata without patches
661148871Scperciva	join -t '|' -v 2 tINDEX tINDEX.new |
662148871Scperciva	    cut -f 2 -d '|' /dev/stdin patchlist |
663148871Scperciva		while read Y; do
664148871Scperciva			if [ ! -f "files/${Y}.gz" ]; then
665148871Scperciva				echo ${Y};
666148871Scperciva			fi
667148871Scperciva		done > filelist
668148871Scperciva	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
669148871Scperciva	echo ${NDEBUG} "metadata files... "
670148871Scperciva	lam -s "f/" - -s ".gz" < filelist |
671148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
672148871Scperciva	    2>${QUIETREDIR}
673148871Scperciva
674148871Scperciva	while read Y; do
675148871Scperciva		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
676148871Scperciva			mv ${Y}.gz files/${Y}.gz
677148871Scperciva		else
678148871Scperciva			echo "metadata is corrupt."
679148871Scperciva			return 1
680148871Scperciva		fi
681148871Scperciva	done < filelist
682148871Scperciva	echo "done."
683148871Scperciva
684148871Scperciva# Extract the index
685148871Scperciva	gunzip -c files/`look INDEX tINDEX.new |
686148871Scperciva	    cut -f 2 -d '|'`.gz > INDEX.new
687148871Scperciva	fetch_index_sanity || return 1
688148871Scperciva
689149824Scperciva# If we have decided to refuse certain updates, construct a hybrid index which
690149824Scperciva# is equal to the old index for parts of the tree which we don't want to
691149824Scperciva# update, and equal to the new index for parts of the tree which gets updates.
692149824Scperciva# This means that we should always have a "complete snapshot" of the ports
693149824Scperciva# tree -- with the caveat that it isn't actually a snapshot.
694149824Scperciva	if [ ! -z "${REFUSE}" ]; then
695149824Scperciva		echo "Refusing to download updates for ${REFUSE}"	\
696149824Scperciva		    >${QUIETREDIR}
697149824Scperciva
698149824Scperciva		grep -Ev "${REFUSE}" INDEX.new > INDEX.tmp
699149824Scperciva		grep -E "${REFUSE}" INDEX |
700149824Scperciva		    sort -m -k 1,1 -t '|' - INDEX.tmp > INDEX.new
701149824Scperciva		rm -f INDEX.tmp
702149824Scperciva	fi
703149824Scperciva
704148871Scperciva# Generate a list of wanted ports patches
705148871Scperciva	join -t '|' -o 1.2,2.2 INDEX INDEX.new |
706148871Scperciva	    fetch_make_patchlist > patchlist
707148871Scperciva
708148871Scperciva# Attempt to fetch ports patches
709148871Scperciva	echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
710148871Scperciva	echo ${NDEBUG} "patches.${DDSTATS}"
711148871Scperciva	tr '|' '-' < patchlist | lam -s "bp/" - |
712148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
713148871Scperciva	    2>${STATSREDIR} | fetch_progress
714148871Scperciva	echo "done."
715148871Scperciva
716148871Scperciva# Attempt to apply ports patches
717148871Scperciva	echo -n "Applying patches... "
718148871Scperciva	while read LINE; do
719148871Scperciva		X=`echo ${LINE} | cut -f 1 -d '|'`
720148871Scperciva		Y=`echo ${LINE} | cut -f 2 -d '|'`
721148871Scperciva		if [ ! -f "${X}-${Y}" ]; then continue; fi
722148871Scperciva		gunzip -c < files/${X}.gz > OLD
723148871Scperciva		${BSPATCH} OLD NEW ${X}-${Y}
724148871Scperciva		if [ `${SHA256} -q NEW` = ${Y} ]; then
725148871Scperciva			mv NEW files/${Y}
726148871Scperciva			gzip -n files/${Y}
727148871Scperciva		fi
728148871Scperciva		rm -f diff OLD NEW ${X}-${Y}
729148871Scperciva	done < patchlist 2>${QUIETREDIR}
730148871Scperciva	echo "done."
731148871Scperciva
732148871Scperciva# Update ports without patches
733148871Scperciva	join -t '|' -v 2 INDEX INDEX.new |
734148871Scperciva	    cut -f 2 -d '|' /dev/stdin patchlist |
735148871Scperciva		while read Y; do
736148871Scperciva			if [ ! -f "files/${Y}.gz" ]; then
737148871Scperciva				echo ${Y};
738148871Scperciva			fi
739148871Scperciva		done > filelist
740148871Scperciva	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
741148871Scperciva	echo ${NDEBUG} "new ports or files... "
742148871Scperciva	lam -s "f/" - -s ".gz" < filelist |
743148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
744148871Scperciva	    2>${QUIETREDIR}
745148871Scperciva
746148871Scperciva	while read Y; do
747148871Scperciva		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
748148871Scperciva			mv ${Y}.gz files/${Y}.gz
749148871Scperciva		else
750148871Scperciva			echo "snapshot is corrupt."
751148871Scperciva			return 1
752148871Scperciva		fi
753148871Scperciva	done < filelist
754148871Scperciva	echo "done."
755148871Scperciva
756148871Scperciva# Remove files which are no longer needed
757148871Scperciva	cut -f 2 -d '|' tINDEX INDEX | sort > oldfiles
758148871Scperciva	cut -f 2 -d '|' tINDEX.new INDEX.new | sort | comm -13 - oldfiles |
759148871Scperciva	    lam -s "files/" - -s ".gz" | xargs rm -f
760148871Scperciva	rm patchlist filelist oldfiles
761148871Scperciva
762148871Scperciva# We're done!
763148871Scperciva	mv INDEX.new INDEX
764148871Scperciva	mv tINDEX.new tINDEX
765148871Scperciva	mv tag.new tag
766148871Scperciva
767148871Scperciva	return 0
768148871Scperciva}
769148871Scperciva
770148871Scperciva# Do the actual work involved in "fetch" / "cron".
771148871Scpercivafetch_run() {
772148871Scperciva	fetch_pick_server
773148871Scperciva
774148871Scperciva	fetch_key || return 1
775148871Scperciva
776148871Scperciva	if ! [ -d files -a -r tag -a -r INDEX -a -r tINDEX ]; then
777148871Scperciva		fetch_snapshot || return 1
778148871Scperciva	fi
779148871Scperciva	fetch_update || return 1
780148871Scperciva}
781148871Scperciva
782148871Scperciva# Build a ports INDEX file
783148871Scpercivaextract_make_index() {
784148871Scperciva	gunzip -c "${WORKDIR}/files/`look $1 ${WORKDIR}/tINDEX |
785148871Scperciva	    cut -f 2 -d '|'`.gz" | ${MKINDEX} /dev/stdin > ${PORTSDIR}/$2
786148871Scperciva}
787148871Scperciva
788148871Scperciva# Create INDEX, INDEX-5, INDEX-6
789148871Scpercivaextract_indices() {
790148871Scperciva	echo -n "Building new INDEX files... "
791148871Scperciva	extract_make_index DESCRIBE.4 INDEX || return 1
792148871Scperciva	extract_make_index DESCRIBE.5 INDEX-5 || return 1
793148871Scperciva	extract_make_index DESCRIBE.6 INDEX-6 || return 1
794148871Scperciva	echo "done."
795148871Scperciva}
796148871Scperciva
797149824Scperciva# Create .portsnap.INDEX; if we are REFUSEing to touch certain directories,
798149824Scperciva# merge the values from any exiting .portsnap.INDEX file.
799148871Scpercivaextract_metadata() {
800149824Scperciva	if [ -z "${REFUSE}" ]; then
801149984Scperciva		sort ${WORKDIR}/INDEX > ${PORTSDIR}/.portsnap.INDEX
802149824Scperciva	elif [ -f ${PORTSDIR}/.portsnap.INDEX ]; then
803149824Scperciva		grep -E "${REFUSE}" ${PORTSDIR}/.portsnap.INDEX	\
804149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX.tmp
805149824Scperciva		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort |
806149824Scperciva		    sort -m - ${PORTSDIR}/.portsnap.INDEX.tmp	\
807149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX
808149824Scperciva		rm -f ${PORTSDIR}/.portsnap.INDEX.tmp
809149824Scperciva	else
810149824Scperciva		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort \
811149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX
812149824Scperciva	fi
813148871Scperciva}
814148871Scperciva
815148871Scperciva# Do the actual work involved in "extract"
816148871Scpercivaextract_run() {
817154088Scperciva	mkdir -p ${PORTSDIR} || return 1
818154088Scperciva
819149824Scperciva	if !
820149824Scperciva		if ! [ -z "${EXTRACTPATH}" ]; then
821149824Scperciva			grep "^${EXTRACTPATH}" ${WORKDIR}/INDEX
822149824Scperciva		elif ! [ -z "${REFUSE}" ]; then
823149824Scperciva			grep -vE "${REFUSE}" ${WORKDIR}/INDEX
824149824Scperciva		else
825149824Scperciva			cat ${WORKDIR}/INDEX
826149824Scperciva		fi | while read LINE; do
827148871Scperciva		FILE=`echo ${LINE} | cut -f 1 -d '|'`
828148871Scperciva		HASH=`echo ${LINE} | cut -f 2 -d '|'`
829148871Scperciva		echo ${PORTSDIR}/${FILE}
830148871Scperciva		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
831148871Scperciva			echo "files/${HASH}.gz not found -- snapshot corrupt."
832148871Scperciva			return 1
833148871Scperciva		fi
834148871Scperciva		case ${FILE} in
835148871Scperciva		*/)
836148871Scperciva			rm -rf ${PORTSDIR}/${FILE}
837148871Scperciva			mkdir -p ${PORTSDIR}/${FILE}
838148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
839148871Scperciva			    -C ${PORTSDIR}/${FILE}
840148871Scperciva			;;
841148871Scperciva		*)
842148871Scperciva			rm -f ${PORTSDIR}/${FILE}
843148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
844148871Scperciva			    -C ${PORTSDIR} ${FILE}
845148871Scperciva			;;
846148871Scperciva		esac
847149041Scperciva	done; then
848149041Scperciva		return 1
849149041Scperciva	fi
850148871Scperciva	if [ ! -z "${EXTRACTPATH}" ]; then
851148871Scperciva		return 0;
852148871Scperciva	fi
853148871Scperciva
854148871Scperciva	extract_metadata
855148871Scperciva	extract_indices
856148871Scperciva}
857148871Scperciva
858148871Scperciva# Do the actual work involved in "update"
859148871Scpercivaupdate_run() {
860148871Scperciva	if ! [ -z "${INDEXONLY}" ]; then
861148871Scperciva		extract_indices >/dev/null || return 1
862148871Scperciva		return 0
863148871Scperciva	fi
864148871Scperciva
865148981Scperciva	if sort ${WORKDIR}/INDEX |
866149042Scperciva	    cmp -s ${PORTSDIR}/.portsnap.INDEX -; then
867148958Scperciva		echo "Ports tree is already up to date."
868148958Scperciva		return 0
869148958Scperciva	fi
870148958Scperciva
871149824Scperciva# If we are REFUSEing to touch certain directories, don't remove files
872149824Scperciva# from those directories (even if they are out of date)
873148871Scperciva	echo -n "Removing old files and directories... "
874149824Scperciva	if ! [ -z "${REFUSE}" ]; then 
875149824Scperciva		sort ${WORKDIR}/INDEX |
876149824Scperciva		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
877149824Scperciva		    grep -vE "${REFUSE}" |
878149824Scperciva		    lam -s "${PORTSDIR}/" - | xargs rm -rf
879149824Scperciva	else
880149824Scperciva		sort ${WORKDIR}/INDEX |
881149824Scperciva		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
882149824Scperciva		    lam -s "${PORTSDIR}/" - | xargs rm -rf
883149824Scperciva	fi
884148871Scperciva	echo "done."
885148871Scperciva
886148871Scperciva# Install new files
887148871Scperciva	echo "Extracting new files:"
888149824Scperciva	if !
889149824Scperciva		if ! [ -z "${REFUSE}" ]; then
890149824Scperciva			grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort
891149824Scperciva		else
892149824Scperciva			sort ${WORKDIR}/INDEX
893149824Scperciva		fi |
894149041Scperciva	    comm -13 ${PORTSDIR}/.portsnap.INDEX - |
895148871Scperciva	    while read LINE; do
896148871Scperciva		FILE=`echo ${LINE} | cut -f 1 -d '|'`
897148871Scperciva		HASH=`echo ${LINE} | cut -f 2 -d '|'`
898148871Scperciva		echo ${PORTSDIR}/${FILE}
899148871Scperciva		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
900148871Scperciva			echo "files/${HASH}.gz not found -- snapshot corrupt."
901148871Scperciva			return 1
902148871Scperciva		fi
903148871Scperciva		case ${FILE} in
904148871Scperciva		*/)
905148871Scperciva			mkdir -p ${PORTSDIR}/${FILE}
906148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
907148871Scperciva			    -C ${PORTSDIR}/${FILE}
908148871Scperciva			;;
909148871Scperciva		*)
910148871Scperciva			tar -xzf ${WORKDIR}/files/${HASH}.gz	\
911148871Scperciva			    -C ${PORTSDIR} ${FILE}
912148871Scperciva			;;
913148871Scperciva		esac
914149041Scperciva	done; then
915149041Scperciva		return 1
916149041Scperciva	fi
917148871Scperciva
918148871Scperciva	extract_metadata
919148871Scperciva	extract_indices
920148871Scperciva}
921148871Scperciva
922148871Scperciva#### Main functions -- call parameter-handling and core functions
923148871Scperciva
924148871Scperciva# Using the command line, configuration file, and defaults,
925148871Scperciva# set all the parameters which are needed later.
926148871Scpercivaget_params() {
927148871Scperciva	init_params
928148871Scperciva	parse_cmdline $@
929148871Scperciva	sanity_conffile
930148871Scperciva	default_conffile
931148871Scperciva	parse_conffile
932148871Scperciva	default_params
933148871Scperciva}
934148871Scperciva
935148871Scperciva# Fetch command.  Make sure that we're being called
936148871Scperciva# interactively, then run fetch_check_params and fetch_run
937148871Scpercivacmd_fetch() {
938148871Scperciva	if [ ! -t 0 ]; then
939148871Scperciva		echo -n "`basename $0` fetch should not "
940148871Scperciva		echo "be run non-interactively."
941148871Scperciva		echo "Run `basename $0` cron instead."
942148871Scperciva		exit 1
943148871Scperciva	fi
944148871Scperciva	fetch_check_params
945148871Scperciva	fetch_run || exit 1
946148871Scperciva}
947148871Scperciva
948148871Scperciva# Cron command.  Make sure the parameters are sensible; wait
949148871Scperciva# rand(3600) seconds; then fetch updates.  While fetching updates,
950148871Scperciva# send output to a temporary file; only print that file if the
951148871Scperciva# fetching failed.
952148871Scpercivacmd_cron() {
953148871Scperciva	fetch_check_params
954148871Scperciva	sleep `jot -r 1 0 3600`
955148871Scperciva
956148871Scperciva	TMPFILE=`mktemp /tmp/portsnap.XXXXXX` || exit 1
957148871Scperciva	if ! fetch_run >> ${TMPFILE}; then
958148871Scperciva		cat ${TMPFILE}
959148871Scperciva		rm ${TMPFILE}
960148871Scperciva		exit 1
961148871Scperciva	fi
962148871Scperciva
963148871Scperciva	rm ${TMPFILE}
964148871Scperciva}
965148871Scperciva
966148871Scperciva# Extract command.  Make sure the parameters are sensible,
967148871Scperciva# then extract the ports tree (or part thereof).
968148871Scpercivacmd_extract() {
969148871Scperciva	extract_check_params
970148871Scperciva	extract_run || exit 1
971148871Scperciva}
972148871Scperciva
973148871Scperciva# Update command.  Make sure the parameters are sensible,
974148871Scperciva# then update the ports tree.
975148871Scpercivacmd_update() {
976148871Scperciva	update_check_params
977148871Scperciva	update_run || exit 1
978148871Scperciva}
979148871Scperciva
980148871Scperciva#### Entry point
981148871Scperciva
982148871Scperciva# Make sure we find utilities from the base system
983148871Scpercivaexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
984148871Scperciva
985148871Scpercivaget_params $@
986149027Scpercivafor COMMAND in ${COMMANDS}; do
987149027Scperciva	cmd_${COMMAND}
988149027Scpercivadone
989