1161748Scperciva#!/bin/sh
2161748Scperciva
3161748Scperciva#-
4173441Scperciva# Copyright 2004-2007 Colin Percival
5161748Scperciva# All rights reserved
6161748Scperciva#
7161748Scperciva# Redistribution and use in source and binary forms, with or without
8161748Scperciva# modification, are permitted providing that the following conditions 
9161748Scperciva# are met:
10161748Scperciva# 1. Redistributions of source code must retain the above copyright
11161748Scperciva#    notice, this list of conditions and the following disclaimer.
12161748Scperciva# 2. Redistributions in binary form must reproduce the above copyright
13161748Scperciva#    notice, this list of conditions and the following disclaimer in the
14161748Scperciva#    documentation and/or other materials provided with the distribution.
15161748Scperciva#
16161748Scperciva# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17161748Scperciva# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18161748Scperciva# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19161748Scperciva# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20161748Scperciva# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21161748Scperciva# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22161748Scperciva# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23161748Scperciva# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24161748Scperciva# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25161748Scperciva# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26161748Scperciva# POSSIBILITY OF SUCH DAMAGE.
27161748Scperciva
28161748Scperciva# $FreeBSD: releng/10.2/usr.sbin/freebsd-update/freebsd-update.sh 303304 2016-07-25 15:04:17Z delphij $
29161748Scperciva
30161748Scperciva#### Usage function -- called from command-line handling code.
31161748Scperciva
32161748Scperciva# Usage instructions.  Options not listed:
33161748Scperciva# --debug	-- don't filter output from utilities
34161748Scperciva# --no-stats	-- don't show progress statistics while fetching files
35161748Scpercivausage () {
36161748Scperciva	cat <<EOF
37161748Scpercivausage: `basename $0` [options] command ... [path]
38161748Scperciva
39161748ScpercivaOptions:
40161748Scperciva  -b basedir   -- Operate on a system mounted at basedir
41161748Scperciva                  (default: /)
42161748Scperciva  -d workdir   -- Store working files in workdir
43161748Scperciva                  (default: /var/db/freebsd-update/)
44161748Scperciva  -f conffile  -- Read configuration options from conffile
45161748Scperciva                  (default: /etc/freebsd-update.conf)
46282870Sdelphij  -F           -- Force a fetch operation to proceed
47161748Scperciva  -k KEY       -- Trust an RSA key with SHA256 hash of KEY
48173564Scperciva  -r release   -- Target for upgrade (e.g., 6.2-RELEASE)
49161748Scperciva  -s server    -- Server from which to fetch updates
50161748Scperciva                  (default: update.FreeBSD.org)
51161748Scperciva  -t address   -- Mail output of cron command, if any, to address
52161748Scperciva                  (default: root)
53282870Sdelphij  --not-running-from-cron
54282870Sdelphij               -- Run without a tty, for use by automated tools
55161748ScpercivaCommands:
56161748Scperciva  fetch        -- Fetch updates from server
57161748Scperciva  cron         -- Sleep rand(3600) seconds, fetch updates, and send an
58161748Scperciva                  email if updates were found
59173564Scperciva  upgrade      -- Fetch upgrades to FreeBSD version specified via -r option
60173564Scperciva  install      -- Install downloaded updates or upgrades
61161748Scperciva  rollback     -- Uninstall most recently installed updates
62181142Scperciva  IDS          -- Compare the system against an index of "known good" files.
63161748ScpercivaEOF
64161748Scperciva	exit 0
65161748Scperciva}
66161748Scperciva
67161748Scperciva#### Configuration processing functions
68161748Scperciva
69161748Scperciva#-
70161748Scperciva# Configuration options are set in the following order of priority:
71161748Scperciva# 1. Command line options
72161748Scperciva# 2. Configuration file options
73161748Scperciva# 3. Default options
74161748Scperciva# In addition, certain options (e.g., IgnorePaths) can be specified multiple
75161748Scperciva# times and (as long as these are all in the same place, e.g., inside the
76161748Scperciva# configuration file) they will accumulate.  Finally, because the path to the
77161748Scperciva# configuration file can be specified at the command line, the entire command
78161748Scperciva# line must be processed before we start reading the configuration file.
79161748Scperciva#
80161748Scperciva# Sound like a mess?  It is.  Here's how we handle this:
81161748Scperciva# 1. Initialize CONFFILE and all the options to "".
82161748Scperciva# 2. Process the command line.  Throw an error if a non-accumulating option
83161748Scperciva#    is specified twice.
84161748Scperciva# 3. If CONFFILE is "", set CONFFILE to /etc/freebsd-update.conf .
85161748Scperciva# 4. For all the configuration options X, set X_saved to X.
86161748Scperciva# 5. Initialize all the options to "".
87161748Scperciva# 6. Read CONFFILE line by line, parsing options.
88161748Scperciva# 7. For each configuration option X, set X to X_saved iff X_saved is not "".
89161748Scperciva# 8. Repeat steps 4-7, except setting options to their default values at (6).
90161748Scperciva
91161748ScpercivaCONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE
92161748Scperciva    KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED
93181142Scperciva    BASEDIR VERBOSELEVEL TARGETRELEASE STRICTCOMPONENTS MERGECHANGES
94196392Ssimon    IDSIGNOREPATHS BACKUPKERNEL BACKUPKERNELDIR BACKUPKERNELSYMBOLFILES"
95161748Scperciva
96161748Scperciva# Set all the configuration options to "".
97161748Scpercivanullconfig () {
98161748Scperciva	for X in ${CONFIGOPTIONS}; do
99161748Scperciva		eval ${X}=""
100161748Scperciva	done
101161748Scperciva}
102161748Scperciva
103161748Scperciva# For each configuration option X, set X_saved to X.
104161748Scpercivasaveconfig () {
105161748Scperciva	for X in ${CONFIGOPTIONS}; do
106161748Scperciva		eval ${X}_saved=\$${X}
107161748Scperciva	done
108161748Scperciva}
109161748Scperciva
110161748Scperciva# For each configuration option X, set X to X_saved if X_saved is not "".
111161748Scpercivamergeconfig () {
112161748Scperciva	for X in ${CONFIGOPTIONS}; do
113161748Scperciva		eval _=\$${X}_saved
114161748Scperciva		if ! [ -z "${_}" ]; then
115161748Scperciva			eval ${X}=\$${X}_saved
116161748Scperciva		fi
117161748Scperciva	done
118161748Scperciva}
119161748Scperciva
120161748Scperciva# Set the trusted keyprint.
121161748Scpercivaconfig_KeyPrint () {
122161748Scperciva	if [ -z ${KEYPRINT} ]; then
123161748Scperciva		KEYPRINT=$1
124161748Scperciva	else
125161748Scperciva		return 1
126161748Scperciva	fi
127161748Scperciva}
128161748Scperciva
129161748Scperciva# Set the working directory.
130161748Scpercivaconfig_WorkDir () {
131161748Scperciva	if [ -z ${WORKDIR} ]; then
132161748Scperciva		WORKDIR=$1
133161748Scperciva	else
134161748Scperciva		return 1
135161748Scperciva	fi
136161748Scperciva}
137161748Scperciva
138161748Scperciva# Set the name of the server (pool) from which to fetch updates
139161748Scpercivaconfig_ServerName () {
140161748Scperciva	if [ -z ${SERVERNAME} ]; then
141161748Scperciva		SERVERNAME=$1
142161748Scperciva	else
143161748Scperciva		return 1
144161748Scperciva	fi
145161748Scperciva}
146161748Scperciva
147161748Scperciva# Set the address to which 'cron' output will be mailed.
148161748Scpercivaconfig_MailTo () {
149161748Scperciva	if [ -z ${MAILTO} ]; then
150161748Scperciva		MAILTO=$1
151161748Scperciva	else
152161748Scperciva		return 1
153161748Scperciva	fi
154161748Scperciva}
155161748Scperciva
156161748Scperciva# Set whether FreeBSD Update is allowed to add files (or directories, or
157161748Scperciva# symlinks) which did not previously exist.
158161748Scpercivaconfig_AllowAdd () {
159161748Scperciva	if [ -z ${ALLOWADD} ]; then
160161748Scperciva		case $1 in
161161748Scperciva		[Yy][Ee][Ss])
162161748Scperciva			ALLOWADD=yes
163161748Scperciva			;;
164161748Scperciva		[Nn][Oo])
165161748Scperciva			ALLOWADD=no
166161748Scperciva			;;
167161748Scperciva		*)
168161748Scperciva			return 1
169161748Scperciva			;;
170161748Scperciva		esac
171161748Scperciva	else
172161748Scperciva		return 1
173161748Scperciva	fi
174161748Scperciva}
175161748Scperciva
176161748Scperciva# Set whether FreeBSD Update is allowed to remove files/directories/symlinks.
177161748Scpercivaconfig_AllowDelete () {
178161748Scperciva	if [ -z ${ALLOWDELETE} ]; then
179161748Scperciva		case $1 in
180161748Scperciva		[Yy][Ee][Ss])
181161748Scperciva			ALLOWDELETE=yes
182161748Scperciva			;;
183161748Scperciva		[Nn][Oo])
184161748Scperciva			ALLOWDELETE=no
185161748Scperciva			;;
186161748Scperciva		*)
187161748Scperciva			return 1
188161748Scperciva			;;
189161748Scperciva		esac
190161748Scperciva	else
191161748Scperciva		return 1
192161748Scperciva	fi
193161748Scperciva}
194161748Scperciva
195161748Scperciva# Set whether FreeBSD Update should keep existing inode ownership,
196161748Scperciva# permissions, and flags, in the event that they have been modified locally
197161748Scperciva# after the release.
198161748Scpercivaconfig_KeepModifiedMetadata () {
199161748Scperciva	if [ -z ${KEEPMODIFIEDMETADATA} ]; then
200161748Scperciva		case $1 in
201161748Scperciva		[Yy][Ee][Ss])
202161748Scperciva			KEEPMODIFIEDMETADATA=yes
203161748Scperciva			;;
204161748Scperciva		[Nn][Oo])
205161748Scperciva			KEEPMODIFIEDMETADATA=no
206161748Scperciva			;;
207161748Scperciva		*)
208161748Scperciva			return 1
209161748Scperciva			;;
210161748Scperciva		esac
211161748Scperciva	else
212161748Scperciva		return 1
213161748Scperciva	fi
214161748Scperciva}
215161748Scperciva
216161748Scperciva# Add to the list of components which should be kept updated.
217161748Scpercivaconfig_Components () {
218161748Scperciva	for C in $@; do
219284936Sdelphij		if [ "$C" = "src" ]; then
220284936Sdelphij			if [ -e /usr/src/COPYRIGHT ]; then
221284936Sdelphij				COMPONENTS="${COMPONENTS} ${C}"
222284936Sdelphij			else
223284936Sdelphij				echo "src component not installed, skipped"
224284936Sdelphij			fi
225284936Sdelphij		else
226284936Sdelphij			COMPONENTS="${COMPONENTS} ${C}"
227284936Sdelphij		fi
228161748Scperciva	done
229161748Scperciva}
230161748Scperciva
231161748Scperciva# Add to the list of paths under which updates will be ignored.
232161748Scpercivaconfig_IgnorePaths () {
233161748Scperciva	for C in $@; do
234161748Scperciva		IGNOREPATHS="${IGNOREPATHS} ${C}"
235161748Scperciva	done
236161748Scperciva}
237161748Scperciva
238181142Scperciva# Add to the list of paths which IDS should ignore.
239181142Scpercivaconfig_IDSIgnorePaths () {
240181142Scperciva	for C in $@; do
241181142Scperciva		IDSIGNOREPATHS="${IDSIGNOREPATHS} ${C}"
242181142Scperciva	done
243181142Scperciva}
244181142Scperciva
245161748Scperciva# Add to the list of paths within which updates will be performed only if the
246161748Scperciva# file on disk has not been modified locally.
247161748Scpercivaconfig_UpdateIfUnmodified () {
248161748Scperciva	for C in $@; do
249161748Scperciva		UPDATEIFUNMODIFIED="${UPDATEIFUNMODIFIED} ${C}"
250161748Scperciva	done
251161748Scperciva}
252161748Scperciva
253173564Scperciva# Add to the list of paths within which updates to text files will be merged
254173564Scperciva# instead of overwritten.
255173564Scpercivaconfig_MergeChanges () {
256173564Scperciva	for C in $@; do
257173564Scperciva		MERGECHANGES="${MERGECHANGES} ${C}"
258173564Scperciva	done
259173564Scperciva}
260173564Scperciva
261161748Scperciva# Work on a FreeBSD installation mounted under $1
262161748Scpercivaconfig_BaseDir () {
263161748Scperciva	if [ -z ${BASEDIR} ]; then
264161748Scperciva		BASEDIR=$1
265161748Scperciva	else
266161748Scperciva		return 1
267161748Scperciva	fi
268161748Scperciva}
269161748Scperciva
270173564Scperciva# When fetching upgrades, should we assume the user wants exactly the
271173564Scperciva# components listed in COMPONENTS, rather than trying to guess based on
272173564Scperciva# what's currently installed?
273173564Scpercivaconfig_StrictComponents () {
274173564Scperciva	if [ -z ${STRICTCOMPONENTS} ]; then
275173564Scperciva		case $1 in
276173564Scperciva		[Yy][Ee][Ss])
277173564Scperciva			STRICTCOMPONENTS=yes
278173564Scperciva			;;
279173564Scperciva		[Nn][Oo])
280173564Scperciva			STRICTCOMPONENTS=no
281173564Scperciva			;;
282173564Scperciva		*)
283173564Scperciva			return 1
284173564Scperciva			;;
285173564Scperciva		esac
286173564Scperciva	else
287173564Scperciva		return 1
288173564Scperciva	fi
289173564Scperciva}
290173564Scperciva
291173564Scperciva# Upgrade to FreeBSD $1
292173564Scpercivaconfig_TargetRelease () {
293173564Scperciva	if [ -z ${TARGETRELEASE} ]; then
294173564Scperciva		TARGETRELEASE=$1
295173564Scperciva	else
296173564Scperciva		return 1
297173564Scperciva	fi
298197618Scperciva	if echo ${TARGETRELEASE} | grep -qE '^[0-9.]+$'; then
299197618Scperciva		TARGETRELEASE="${TARGETRELEASE}-RELEASE"
300197618Scperciva	fi
301173564Scperciva}
302173564Scperciva
303161748Scperciva# Define what happens to output of utilities
304161748Scpercivaconfig_VerboseLevel () {
305161748Scperciva	if [ -z ${VERBOSELEVEL} ]; then
306161748Scperciva		case $1 in
307161748Scperciva		[Dd][Ee][Bb][Uu][Gg])
308161748Scperciva			VERBOSELEVEL=debug
309161748Scperciva			;;
310161748Scperciva		[Nn][Oo][Ss][Tt][Aa][Tt][Ss])
311161748Scperciva			VERBOSELEVEL=nostats
312161748Scperciva			;;
313161748Scperciva		[Ss][Tt][Aa][Tt][Ss])
314161748Scperciva			VERBOSELEVEL=stats
315161748Scperciva			;;
316161748Scperciva		*)
317161748Scperciva			return 1
318161748Scperciva			;;
319161748Scperciva		esac
320161748Scperciva	else
321161748Scperciva		return 1
322161748Scperciva	fi
323161748Scperciva}
324161748Scperciva
325196392Ssimonconfig_BackupKernel () {
326196392Ssimon	if [ -z ${BACKUPKERNEL} ]; then
327196392Ssimon		case $1 in
328196392Ssimon		[Yy][Ee][Ss])
329196392Ssimon			BACKUPKERNEL=yes
330196392Ssimon			;;
331196392Ssimon		[Nn][Oo])
332196392Ssimon			BACKUPKERNEL=no
333196392Ssimon			;;
334196392Ssimon		*)
335196392Ssimon			return 1
336196392Ssimon			;;
337196392Ssimon		esac
338196392Ssimon	else
339196392Ssimon		return 1
340196392Ssimon	fi
341196392Ssimon}
342196392Ssimon
343196392Ssimonconfig_BackupKernelDir () {
344196392Ssimon	if [ -z ${BACKUPKERNELDIR} ]; then
345196392Ssimon		if [ -z "$1" ]; then
346196392Ssimon			echo "BackupKernelDir set to empty dir"
347196392Ssimon			return 1
348196392Ssimon		fi
349196392Ssimon
350196392Ssimon		# We check for some paths which would be extremely odd
351196392Ssimon		# to use, but which could cause a lot of problems if
352196392Ssimon		# used.
353196392Ssimon		case $1 in
354196392Ssimon		/|/bin|/boot|/etc|/lib|/libexec|/sbin|/usr|/var)
355196392Ssimon			echo "BackupKernelDir set to invalid path $1"
356196392Ssimon			return 1
357196392Ssimon			;;
358196392Ssimon		/*)
359196392Ssimon			BACKUPKERNELDIR=$1
360196392Ssimon			;;
361196392Ssimon		*)
362196392Ssimon			echo "BackupKernelDir ($1) is not an absolute path"
363196392Ssimon			return 1
364196392Ssimon			;;
365196392Ssimon		esac
366196392Ssimon	else
367196392Ssimon		return 1
368196392Ssimon	fi
369196392Ssimon}
370196392Ssimon
371196392Ssimonconfig_BackupKernelSymbolFiles () {
372196392Ssimon	if [ -z ${BACKUPKERNELSYMBOLFILES} ]; then
373196392Ssimon		case $1 in
374196392Ssimon		[Yy][Ee][Ss])
375196392Ssimon			BACKUPKERNELSYMBOLFILES=yes
376196392Ssimon			;;
377196392Ssimon		[Nn][Oo])
378196392Ssimon			BACKUPKERNELSYMBOLFILES=no
379196392Ssimon			;;
380196392Ssimon		*)
381196392Ssimon			return 1
382196392Ssimon			;;
383196392Ssimon		esac
384196392Ssimon	else
385196392Ssimon		return 1
386196392Ssimon	fi
387196392Ssimon}
388196392Ssimon
389161748Scperciva# Handle one line of configuration
390161748Scpercivaconfigline () {
391161748Scperciva	if [ $# -eq 0 ]; then
392161748Scperciva		return
393161748Scperciva	fi
394161748Scperciva
395161748Scperciva	OPT=$1
396161748Scperciva	shift
397161748Scperciva	config_${OPT} $@
398161748Scperciva}
399161748Scperciva
400161748Scperciva#### Parameter handling functions.
401161748Scperciva
402161748Scperciva# Initialize parameters to null, just in case they're
403161748Scperciva# set in the environment.
404161748Scpercivainit_params () {
405161748Scperciva	# Configration settings
406161748Scperciva	nullconfig
407161748Scperciva
408161748Scperciva	# No configuration file set yet
409161748Scperciva	CONFFILE=""
410161748Scperciva
411161748Scperciva	# No commands specified yet
412161748Scperciva	COMMANDS=""
413282870Sdelphij
414282870Sdelphij	# Force fetch to proceed
415282870Sdelphij	FORCEFETCH=0
416282870Sdelphij
417282870Sdelphij	# Run without a TTY
418282870Sdelphij	NOTTYOK=0
419161748Scperciva}
420161748Scperciva
421161748Scperciva# Parse the command line
422161748Scpercivaparse_cmdline () {
423161748Scperciva	while [ $# -gt 0 ]; do
424161748Scperciva		case "$1" in
425161748Scperciva		# Location of configuration file
426161748Scperciva		-f)
427161748Scperciva			if [ $# -eq 1 ]; then usage; fi
428161748Scperciva			if [ ! -z "${CONFFILE}" ]; then usage; fi
429161748Scperciva			shift; CONFFILE="$1"
430161748Scperciva			;;
431282870Sdelphij		-F)
432282870Sdelphij			FORCEFETCH=1
433282870Sdelphij			;;
434282870Sdelphij		--not-running-from-cron)
435282870Sdelphij			NOTTYOK=1
436282870Sdelphij			;;
437161748Scperciva
438161748Scperciva		# Configuration file equivalents
439161748Scperciva		-b)
440161748Scperciva			if [ $# -eq 1 ]; then usage; fi; shift
441161748Scperciva			config_BaseDir $1 || usage
442161748Scperciva			;;
443161748Scperciva		-d)
444161748Scperciva			if [ $# -eq 1 ]; then usage; fi; shift
445161748Scperciva			config_WorkDir $1 || usage
446161748Scperciva			;;
447161748Scperciva		-k)
448161748Scperciva			if [ $# -eq 1 ]; then usage; fi; shift
449161748Scperciva			config_KeyPrint $1 || usage
450161748Scperciva			;;
451161748Scperciva		-s)
452161748Scperciva			if [ $# -eq 1 ]; then usage; fi; shift
453161748Scperciva			config_ServerName $1 || usage
454161748Scperciva			;;
455173564Scperciva		-r)
456173564Scperciva			if [ $# -eq 1 ]; then usage; fi; shift
457173564Scperciva			config_TargetRelease $1 || usage
458173564Scperciva			;;
459161748Scperciva		-t)
460161748Scperciva			if [ $# -eq 1 ]; then usage; fi; shift
461161748Scperciva			config_MailTo $1 || usage
462161748Scperciva			;;
463161748Scperciva		-v)
464161748Scperciva			if [ $# -eq 1 ]; then usage; fi; shift
465161748Scperciva			config_VerboseLevel $1 || usage
466161748Scperciva			;;
467161748Scperciva
468161748Scperciva		# Aliases for "-v debug" and "-v nostats"
469161748Scperciva		--debug)
470161748Scperciva			config_VerboseLevel debug || usage
471161748Scperciva			;;
472161748Scperciva		--no-stats)
473161748Scperciva			config_VerboseLevel nostats || usage
474161748Scperciva			;;
475161748Scperciva
476161748Scperciva		# Commands
477181142Scperciva		cron | fetch | upgrade | install | rollback | IDS)
478161748Scperciva			COMMANDS="${COMMANDS} $1"
479161748Scperciva			;;
480161748Scperciva
481161748Scperciva		# Anything else is an error
482161748Scperciva		*)
483161748Scperciva			usage
484161748Scperciva			;;
485161748Scperciva		esac
486161748Scperciva		shift
487161748Scperciva	done
488161748Scperciva
489161748Scperciva	# Make sure we have at least one command
490161748Scperciva	if [ -z "${COMMANDS}" ]; then
491161748Scperciva		usage
492161748Scperciva	fi
493161748Scperciva}
494161748Scperciva
495161748Scperciva# Parse the configuration file
496161748Scpercivaparse_conffile () {
497161748Scperciva	# If a configuration file was specified on the command line, check
498161748Scperciva	# that it exists and is readable.
499161748Scperciva	if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
500161748Scperciva		echo -n "File does not exist "
501161748Scperciva		echo -n "or is not readable: "
502161748Scperciva		echo ${CONFFILE}
503161748Scperciva		exit 1
504161748Scperciva	fi
505161748Scperciva
506161748Scperciva	# If a configuration file was not specified on the command line,
507161748Scperciva	# use the default configuration file path.  If that default does
508161748Scperciva	# not exist, give up looking for any configuration.
509161748Scperciva	if [ -z "${CONFFILE}" ]; then
510161748Scperciva		CONFFILE="/etc/freebsd-update.conf"
511161748Scperciva		if [ ! -r "${CONFFILE}" ]; then
512161748Scperciva			return
513161748Scperciva		fi
514161748Scperciva	fi
515161748Scperciva
516161748Scperciva	# Save the configuration options specified on the command line, and
517161748Scperciva	# clear all the options in preparation for reading the config file.
518161748Scperciva	saveconfig
519161748Scperciva	nullconfig
520161748Scperciva
521161748Scperciva	# Read the configuration file.  Anything after the first '#' is
522161748Scperciva	# ignored, and any blank lines are ignored.
523161748Scperciva	L=0
524161748Scperciva	while read LINE; do
525161748Scperciva		L=$(($L + 1))
526161748Scperciva		LINEX=`echo "${LINE}" | cut -f 1 -d '#'`
527161748Scperciva		if ! configline ${LINEX}; then
528161748Scperciva			echo "Error processing configuration file, line $L:"
529161748Scperciva			echo "==> ${LINE}"
530161748Scperciva			exit 1
531161748Scperciva		fi
532161748Scperciva	done < ${CONFFILE}
533161748Scperciva
534161748Scperciva	# Merge the settings read from the configuration file with those
535161748Scperciva	# provided at the command line.
536161748Scperciva	mergeconfig
537161748Scperciva}
538161748Scperciva
539161748Scperciva# Provide some default parameters
540161748Scpercivadefault_params () {
541161748Scperciva	# Save any parameters already configured, and clear the slate
542161748Scperciva	saveconfig
543161748Scperciva	nullconfig
544161748Scperciva
545161748Scperciva	# Default configurations
546161748Scperciva	config_WorkDir /var/db/freebsd-update
547161748Scperciva	config_MailTo root
548161748Scperciva	config_AllowAdd yes
549161748Scperciva	config_AllowDelete yes
550161748Scperciva	config_KeepModifiedMetadata yes
551161748Scperciva	config_BaseDir /
552161748Scperciva	config_VerboseLevel stats
553173564Scperciva	config_StrictComponents no
554196392Ssimon	config_BackupKernel yes
555196392Ssimon	config_BackupKernelDir /boot/kernel.old
556196392Ssimon	config_BackupKernelSymbolFiles no
557161748Scperciva
558161748Scperciva	# Merge these defaults into the earlier-configured settings
559161748Scperciva	mergeconfig
560161748Scperciva}
561161748Scperciva
562161748Scperciva# Set utility output filtering options, based on ${VERBOSELEVEL}
563161748Scpercivafetch_setup_verboselevel () {
564161748Scperciva	case ${VERBOSELEVEL} in
565161748Scperciva	debug)
566161748Scperciva		QUIETREDIR="/dev/stderr"
567161748Scperciva		QUIETFLAG=" "
568161748Scperciva		STATSREDIR="/dev/stderr"
569161748Scperciva		DDSTATS=".."
570161748Scperciva		XARGST="-t"
571161748Scperciva		NDEBUG=" "
572161748Scperciva		;;
573161748Scperciva	nostats)
574161748Scperciva		QUIETREDIR=""
575161748Scperciva		QUIETFLAG=""
576161748Scperciva		STATSREDIR="/dev/null"
577161748Scperciva		DDSTATS=".."
578161748Scperciva		XARGST=""
579161748Scperciva		NDEBUG=""
580161748Scperciva		;;
581161748Scperciva	stats)
582161748Scperciva		QUIETREDIR="/dev/null"
583161748Scperciva		QUIETFLAG="-q"
584161748Scperciva		STATSREDIR="/dev/stdout"
585161748Scperciva		DDSTATS=""
586161748Scperciva		XARGST=""
587161748Scperciva		NDEBUG="-n"
588161748Scperciva		;;
589161748Scperciva	esac
590161748Scperciva}
591161748Scperciva
592161748Scperciva# Perform sanity checks and set some final parameters
593161748Scperciva# in preparation for fetching files.  Figure out which
594161748Scperciva# set of updates should be downloaded: If the user is
595161748Scperciva# running *-p[0-9]+, strip off the last part; if the
596161748Scperciva# user is running -SECURITY, call it -RELEASE.  Chdir
597161748Scperciva# into the working directory.
598212434Scpercivafetchupgrade_check_params () {
599161748Scperciva	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
600161748Scperciva
601161748Scperciva	_SERVERNAME_z=\
602161748Scperciva"SERVERNAME must be given via command line or configuration file."
603161748Scperciva	_KEYPRINT_z="Key must be given via -k option or configuration file."
604161748Scperciva	_KEYPRINT_bad="Invalid key fingerprint: "
605161748Scperciva	_WORKDIR_bad="Directory does not exist or is not writable: "
606284938Sdelphij	_WORKDIR_bad2="Directory is not on a persistent filesystem: "
607161748Scperciva
608161748Scperciva	if [ -z "${SERVERNAME}" ]; then
609161748Scperciva		echo -n "`basename $0`: "
610161748Scperciva		echo "${_SERVERNAME_z}"
611161748Scperciva		exit 1
612161748Scperciva	fi
613161748Scperciva	if [ -z "${KEYPRINT}" ]; then
614161748Scperciva		echo -n "`basename $0`: "
615161748Scperciva		echo "${_KEYPRINT_z}"
616161748Scperciva		exit 1
617161748Scperciva	fi
618161748Scperciva	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
619161748Scperciva		echo -n "`basename $0`: "
620161748Scperciva		echo -n "${_KEYPRINT_bad}"
621161748Scperciva		echo ${KEYPRINT}
622161748Scperciva		exit 1
623161748Scperciva	fi
624161748Scperciva	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
625161748Scperciva		echo -n "`basename $0`: "
626161748Scperciva		echo -n "${_WORKDIR_bad}"
627161748Scperciva		echo ${WORKDIR}
628161748Scperciva		exit 1
629161748Scperciva	fi
630284938Sdelphij	case `df -T ${WORKDIR}` in */dev/md[0-9]* | *tmpfs*)
631284938Sdelphij		echo -n "`basename $0`: "
632284938Sdelphij		echo -n "${_WORKDIR_bad2}"
633284938Sdelphij		echo ${WORKDIR}
634284938Sdelphij		exit 1
635284938Sdelphij		;;
636284938Sdelphij	esac
637200054Scperciva	chmod 700 ${WORKDIR}
638161748Scperciva	cd ${WORKDIR} || exit 1
639161748Scperciva
640161748Scperciva	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
641161748Scperciva	# to provide an upgrade path for FreeBSD Update 1.x users, since
642161748Scperciva	# the kernels provided by FreeBSD Update 1.x are always labelled
643161748Scperciva	# as X.Y-SECURITY.
644161748Scperciva	RELNUM=`uname -r |
645161748Scperciva	    sed -E 's,-p[0-9]+,,' |
646161748Scperciva	    sed -E 's,-SECURITY,-RELEASE,'`
647161748Scperciva	ARCH=`uname -m`
648161748Scperciva	FETCHDIR=${RELNUM}/${ARCH}
649173564Scperciva	PATCHDIR=${RELNUM}/${ARCH}/bp
650161748Scperciva
651161748Scperciva	# Figure out what directory contains the running kernel
652161748Scperciva	BOOTFILE=`sysctl -n kern.bootfile`
653161748Scperciva	KERNELDIR=${BOOTFILE%/kernel}
654161748Scperciva	if ! [ -d ${KERNELDIR} ]; then
655161748Scperciva		echo "Cannot identify running kernel"
656161748Scperciva		exit 1
657161748Scperciva	fi
658161748Scperciva
659167189Scperciva	# Figure out what kernel configuration is running.  We start with
660167189Scperciva	# the output of `uname -i`, and then make the following adjustments:
661167189Scperciva	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
662167189Scperciva	# file says "ident SMP-GENERIC", I don't know...
663167189Scperciva	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
664167189Scperciva	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
665167189Scperciva	# we're running an SMP kernel.  This mis-identification is a bug
666167189Scperciva	# which was fixed in 6.2-STABLE.
667167189Scperciva	KERNCONF=`uname -i`
668167189Scperciva	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
669167189Scperciva		KERNCONF=SMP
670167189Scperciva	fi
671167189Scperciva	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
672167189Scperciva		if sysctl kern.version | grep -qE '/SMP$'; then
673167189Scperciva			KERNCONF=SMP
674167189Scperciva		fi
675167189Scperciva	fi
676167189Scperciva
677161748Scperciva	# Define some paths
678161748Scperciva	BSPATCH=/usr/bin/bspatch
679161748Scperciva	SHA256=/sbin/sha256
680161748Scperciva	PHTTPGET=/usr/libexec/phttpget
681161748Scperciva
682161748Scperciva	# Set up variables relating to VERBOSELEVEL
683161748Scperciva	fetch_setup_verboselevel
684161748Scperciva
685161748Scperciva	# Construct a unique name from ${BASEDIR}
686161748Scperciva	BDHASH=`echo ${BASEDIR} | sha256 -q`
687161748Scperciva}
688161748Scperciva
689212434Scperciva# Perform sanity checks etc. before fetching updates.
690212434Scpercivafetch_check_params () {
691212434Scperciva	fetchupgrade_check_params
692212434Scperciva
693212434Scperciva	if ! [ -z "${TARGETRELEASE}" ]; then
694212434Scperciva		echo -n "`basename $0`: "
695212434Scperciva		echo -n "-r option is meaningless with 'fetch' command.  "
696212434Scperciva		echo "(Did you mean 'upgrade' instead?)"
697212434Scperciva		exit 1
698212434Scperciva	fi
699282870Sdelphij
700282870Sdelphij	# Check that we have updates ready to install
701282870Sdelphij	if [ -f ${BDHASH}-install/kerneldone -a $FORCEFETCH -eq 0 ]; then
702282870Sdelphij		echo "You have a partially completed upgrade pending"
703282870Sdelphij		echo "Run '$0 install' first."
704282870Sdelphij		echo "Run '$0 fetch -F' to proceed anyway."
705282870Sdelphij		exit 1
706282870Sdelphij	fi
707212434Scperciva}
708212434Scperciva
709173564Scperciva# Perform sanity checks etc. before fetching upgrades.
710173564Scpercivaupgrade_check_params () {
711212434Scperciva	fetchupgrade_check_params
712173564Scperciva
713173564Scperciva	# Unless set otherwise, we're upgrading to the same kernel config.
714173564Scperciva	NKERNCONF=${KERNCONF}
715173564Scperciva
716173564Scperciva	# We need TARGETRELEASE set
717173564Scperciva	_TARGETRELEASE_z="Release target must be specified via -r option."
718173564Scperciva	if [ -z "${TARGETRELEASE}" ]; then
719173564Scperciva		echo -n "`basename $0`: "
720173564Scperciva		echo "${_TARGETRELEASE_z}"
721173564Scperciva		exit 1
722173564Scperciva	fi
723173564Scperciva
724173564Scperciva	# The target release should be != the current release.
725173564Scperciva	if [ "${TARGETRELEASE}" = "${RELNUM}" ]; then
726173564Scperciva		echo -n "`basename $0`: "
727173564Scperciva		echo "Cannot upgrade from ${RELNUM} to itself"
728173564Scperciva		exit 1
729173564Scperciva	fi
730173564Scperciva
731173564Scperciva	# Turning off AllowAdd or AllowDelete is a bad idea for upgrades.
732173564Scperciva	if [ "${ALLOWADD}" = "no" ]; then
733173564Scperciva		echo -n "`basename $0`: "
734173564Scperciva		echo -n "WARNING: \"AllowAdd no\" is a bad idea "
735173564Scperciva		echo "when upgrading between releases."
736173564Scperciva		echo
737173564Scperciva	fi
738173564Scperciva	if [ "${ALLOWDELETE}" = "no" ]; then
739173564Scperciva		echo -n "`basename $0`: "
740173564Scperciva		echo -n "WARNING: \"AllowDelete no\" is a bad idea "
741173564Scperciva		echo "when upgrading between releases."
742173564Scperciva		echo
743173564Scperciva	fi
744173564Scperciva
745173564Scperciva	# Set EDITOR to /usr/bin/vi if it isn't already set
746173564Scperciva	: ${EDITOR:='/usr/bin/vi'}
747173564Scperciva}
748173564Scperciva
749161748Scperciva# Perform sanity checks and set some final parameters in
750161748Scperciva# preparation for installing updates.
751161748Scpercivainstall_check_params () {
752161748Scperciva	# Check that we are root.  All sorts of things won't work otherwise.
753161748Scperciva	if [ `id -u` != 0 ]; then
754161748Scperciva		echo "You must be root to run this."
755161748Scperciva		exit 1
756161748Scperciva	fi
757161748Scperciva
758173441Scperciva	# Check that securelevel <= 0.  Otherwise we can't update schg files.
759173441Scperciva	if [ `sysctl -n kern.securelevel` -gt 0 ]; then
760173441Scperciva		echo "Updates cannot be installed when the system securelevel"
761173441Scperciva		echo "is greater than zero."
762173441Scperciva		exit 1
763173441Scperciva	fi
764173441Scperciva
765161748Scperciva	# Check that we have a working directory
766161748Scperciva	_WORKDIR_bad="Directory does not exist or is not writable: "
767161748Scperciva	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
768161748Scperciva		echo -n "`basename $0`: "
769161748Scperciva		echo -n "${_WORKDIR_bad}"
770161748Scperciva		echo ${WORKDIR}
771161748Scperciva		exit 1
772161748Scperciva	fi
773161748Scperciva	cd ${WORKDIR} || exit 1
774161748Scperciva
775161748Scperciva	# Construct a unique name from ${BASEDIR}
776161748Scperciva	BDHASH=`echo ${BASEDIR} | sha256 -q`
777161748Scperciva
778161748Scperciva	# Check that we have updates ready to install
779161748Scperciva	if ! [ -L ${BDHASH}-install ]; then
780161748Scperciva		echo "No updates are available to install."
781161748Scperciva		echo "Run '$0 fetch' first."
782161748Scperciva		exit 1
783161748Scperciva	fi
784161748Scperciva	if ! [ -f ${BDHASH}-install/INDEX-OLD ] ||
785161748Scperciva	    ! [ -f ${BDHASH}-install/INDEX-NEW ]; then
786161748Scperciva		echo "Update manifest is corrupt -- this should never happen."
787161748Scperciva		echo "Re-run '$0 fetch'."
788161748Scperciva		exit 1
789161748Scperciva	fi
790196392Ssimon
791196392Ssimon	# Figure out what directory contains the running kernel
792196392Ssimon	BOOTFILE=`sysctl -n kern.bootfile`
793196392Ssimon	KERNELDIR=${BOOTFILE%/kernel}
794196392Ssimon	if ! [ -d ${KERNELDIR} ]; then
795196392Ssimon		echo "Cannot identify running kernel"
796196392Ssimon		exit 1
797196392Ssimon	fi
798161748Scperciva}
799161748Scperciva
800161748Scperciva# Perform sanity checks and set some final parameters in
801161748Scperciva# preparation for UNinstalling updates.
802161748Scpercivarollback_check_params () {
803161748Scperciva	# Check that we are root.  All sorts of things won't work otherwise.
804161748Scperciva	if [ `id -u` != 0 ]; then
805161748Scperciva		echo "You must be root to run this."
806161748Scperciva		exit 1
807161748Scperciva	fi
808161748Scperciva
809161748Scperciva	# Check that we have a working directory
810161748Scperciva	_WORKDIR_bad="Directory does not exist or is not writable: "
811161748Scperciva	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
812161748Scperciva		echo -n "`basename $0`: "
813161748Scperciva		echo -n "${_WORKDIR_bad}"
814161748Scperciva		echo ${WORKDIR}
815161748Scperciva		exit 1
816161748Scperciva	fi
817161748Scperciva	cd ${WORKDIR} || exit 1
818161748Scperciva
819161748Scperciva	# Construct a unique name from ${BASEDIR}
820161748Scperciva	BDHASH=`echo ${BASEDIR} | sha256 -q`
821161748Scperciva
822161748Scperciva	# Check that we have updates ready to rollback
823161748Scperciva	if ! [ -L ${BDHASH}-rollback ]; then
824161748Scperciva		echo "No rollback directory found."
825161748Scperciva		exit 1
826161748Scperciva	fi
827161748Scperciva	if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] ||
828161748Scperciva	    ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then
829161748Scperciva		echo "Update manifest is corrupt -- this should never happen."
830161748Scperciva		exit 1
831161748Scperciva	fi
832161748Scperciva}
833161748Scperciva
834181142Scperciva# Perform sanity checks and set some final parameters
835181142Scperciva# in preparation for comparing the system against the
836181142Scperciva# published index.  Figure out which index we should
837181142Scperciva# compare against: If the user is running *-p[0-9]+,
838181142Scperciva# strip off the last part; if the user is running
839181142Scperciva# -SECURITY, call it -RELEASE.  Chdir into the working
840181142Scperciva# directory.
841181142ScpercivaIDS_check_params () {
842181142Scperciva	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
843181142Scperciva
844181142Scperciva	_SERVERNAME_z=\
845181142Scperciva"SERVERNAME must be given via command line or configuration file."
846181142Scperciva	_KEYPRINT_z="Key must be given via -k option or configuration file."
847181142Scperciva	_KEYPRINT_bad="Invalid key fingerprint: "
848181142Scperciva	_WORKDIR_bad="Directory does not exist or is not writable: "
849181142Scperciva
850181142Scperciva	if [ -z "${SERVERNAME}" ]; then
851181142Scperciva		echo -n "`basename $0`: "
852181142Scperciva		echo "${_SERVERNAME_z}"
853181142Scperciva		exit 1
854181142Scperciva	fi
855181142Scperciva	if [ -z "${KEYPRINT}" ]; then
856181142Scperciva		echo -n "`basename $0`: "
857181142Scperciva		echo "${_KEYPRINT_z}"
858181142Scperciva		exit 1
859181142Scperciva	fi
860181142Scperciva	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
861181142Scperciva		echo -n "`basename $0`: "
862181142Scperciva		echo -n "${_KEYPRINT_bad}"
863181142Scperciva		echo ${KEYPRINT}
864181142Scperciva		exit 1
865181142Scperciva	fi
866181142Scperciva	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
867181142Scperciva		echo -n "`basename $0`: "
868181142Scperciva		echo -n "${_WORKDIR_bad}"
869181142Scperciva		echo ${WORKDIR}
870181142Scperciva		exit 1
871181142Scperciva	fi
872181142Scperciva	cd ${WORKDIR} || exit 1
873181142Scperciva
874181142Scperciva	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
875181142Scperciva	# to provide an upgrade path for FreeBSD Update 1.x users, since
876181142Scperciva	# the kernels provided by FreeBSD Update 1.x are always labelled
877181142Scperciva	# as X.Y-SECURITY.
878181142Scperciva	RELNUM=`uname -r |
879181142Scperciva	    sed -E 's,-p[0-9]+,,' |
880181142Scperciva	    sed -E 's,-SECURITY,-RELEASE,'`
881181142Scperciva	ARCH=`uname -m`
882181142Scperciva	FETCHDIR=${RELNUM}/${ARCH}
883181142Scperciva	PATCHDIR=${RELNUM}/${ARCH}/bp
884181142Scperciva
885181142Scperciva	# Figure out what directory contains the running kernel
886181142Scperciva	BOOTFILE=`sysctl -n kern.bootfile`
887181142Scperciva	KERNELDIR=${BOOTFILE%/kernel}
888181142Scperciva	if ! [ -d ${KERNELDIR} ]; then
889181142Scperciva		echo "Cannot identify running kernel"
890181142Scperciva		exit 1
891181142Scperciva	fi
892181142Scperciva
893181142Scperciva	# Figure out what kernel configuration is running.  We start with
894181142Scperciva	# the output of `uname -i`, and then make the following adjustments:
895181142Scperciva	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
896181142Scperciva	# file says "ident SMP-GENERIC", I don't know...
897181142Scperciva	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
898181142Scperciva	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
899181142Scperciva	# we're running an SMP kernel.  This mis-identification is a bug
900181142Scperciva	# which was fixed in 6.2-STABLE.
901181142Scperciva	KERNCONF=`uname -i`
902181142Scperciva	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
903181142Scperciva		KERNCONF=SMP
904181142Scperciva	fi
905181142Scperciva	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
906181142Scperciva		if sysctl kern.version | grep -qE '/SMP$'; then
907181142Scperciva			KERNCONF=SMP
908181142Scperciva		fi
909181142Scperciva	fi
910181142Scperciva
911181142Scperciva	# Define some paths
912181142Scperciva	SHA256=/sbin/sha256
913181142Scperciva	PHTTPGET=/usr/libexec/phttpget
914181142Scperciva
915181142Scperciva	# Set up variables relating to VERBOSELEVEL
916181142Scperciva	fetch_setup_verboselevel
917181142Scperciva}
918181142Scperciva
919161748Scperciva#### Core functionality -- the actual work gets done here
920161748Scperciva
921161748Scperciva# Use an SRV query to pick a server.  If the SRV query doesn't provide
922161748Scperciva# a useful answer, use the server name specified by the user.
923161748Scperciva# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
924161748Scperciva# from that; or if no servers are returned, use ${SERVERNAME}.
925161748Scperciva# This allows a user to specify "portsnap.freebsd.org" (in which case
926161748Scperciva# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
927161748Scperciva# (in which case portsnap will use that particular server, since there
928161748Scperciva# won't be an SRV entry for that name).
929161748Scperciva#
930161748Scperciva# We ignore the Port field, since we are always going to use port 80.
931161748Scperciva
932161748Scperciva# Fetch the mirror list, but do not pick a mirror yet.  Returns 1 if
933161748Scperciva# no mirrors are available for any reason.
934161748Scpercivafetch_pick_server_init () {
935161748Scperciva	: > serverlist_tried
936161748Scperciva
937161748Scperciva# Check that host(1) exists (i.e., that the system wasn't built with the
938161748Scperciva# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
939161748Scperciva	if ! which -s host; then
940161748Scperciva		: > serverlist_full
941161748Scperciva		return 1
942161748Scperciva	fi
943161748Scperciva
944161748Scperciva	echo -n "Looking up ${SERVERNAME} mirrors... "
945161748Scperciva
946161748Scperciva# Issue the SRV query and pull out the Priority, Weight, and Target fields.
947161748Scperciva# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
948161748Scperciva# "$name server selection ..."; we allow either format.
949161748Scperciva	MLIST="_http._tcp.${SERVERNAME}"
950161748Scperciva	host -t srv "${MLIST}" |
951161748Scperciva	    sed -nE "s/${MLIST} (has SRV record|server selection) //p" |
952161748Scperciva	    cut -f 1,2,4 -d ' ' |
953161748Scperciva	    sed -e 's/\.$//' |
954161748Scperciva	    sort > serverlist_full
955161748Scperciva
956161748Scperciva# If no records, give up -- we'll just use the server name we were given.
957161748Scperciva	if [ `wc -l < serverlist_full` -eq 0 ]; then
958161748Scperciva		echo "none found."
959161748Scperciva		return 1
960161748Scperciva	fi
961161748Scperciva
962161748Scperciva# Report how many mirrors we found.
963161748Scperciva	echo `wc -l < serverlist_full` "mirrors found."
964161748Scperciva
965161748Scperciva# Generate a random seed for use in picking mirrors.  If HTTP_PROXY
966161748Scperciva# is set, this will be used to generate the seed; otherwise, the seed
967161748Scperciva# will be random.
968161748Scperciva	if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
969161748Scperciva		RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
970161748Scperciva		    tr -d 'a-f' |
971161748Scperciva		    cut -c 1-9`
972161748Scperciva	else
973161748Scperciva		RANDVALUE=`jot -r 1 0 999999999`
974161748Scperciva	fi
975161748Scperciva}
976161748Scperciva
977161748Scperciva# Pick a mirror.  Returns 1 if we have run out of mirrors to try.
978161748Scpercivafetch_pick_server () {
979161748Scperciva# Generate a list of not-yet-tried mirrors
980161748Scperciva	sort serverlist_tried |
981161748Scperciva	    comm -23 serverlist_full - > serverlist
982161748Scperciva
983161748Scperciva# Have we run out of mirrors?
984161748Scperciva	if [ `wc -l < serverlist` -eq 0 ]; then
985161748Scperciva		echo "No mirrors remaining, giving up."
986161748Scperciva		return 1
987161748Scperciva	fi
988161748Scperciva
989161748Scperciva# Find the highest priority level (lowest numeric value).
990161748Scperciva	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
991161748Scperciva
992161748Scperciva# Add up the weights of the response lines at that priority level.
993161748Scperciva	SRV_WSUM=0;
994161748Scperciva	while read X; do
995161748Scperciva		case "$X" in
996161748Scperciva		${SRV_PRIORITY}\ *)
997161748Scperciva			SRV_W=`echo $X | cut -f 2 -d ' '`
998161748Scperciva			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
999161748Scperciva			;;
1000161748Scperciva		esac
1001161748Scperciva	done < serverlist
1002161748Scperciva
1003161748Scperciva# If all the weights are 0, pretend that they are all 1 instead.
1004161748Scperciva	if [ ${SRV_WSUM} -eq 0 ]; then
1005161748Scperciva		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
1006161748Scperciva		SRV_W_ADD=1
1007161748Scperciva	else
1008161748Scperciva		SRV_W_ADD=0
1009161748Scperciva	fi
1010161748Scperciva
1011161748Scperciva# Pick a value between 0 and the sum of the weights - 1
1012161748Scperciva	SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
1013161748Scperciva
1014161748Scperciva# Read through the list of mirrors and set SERVERNAME.  Write the line
1015161748Scperciva# corresponding to the mirror we selected into serverlist_tried so that
1016161748Scperciva# we won't try it again.
1017161748Scperciva	while read X; do
1018161748Scperciva		case "$X" in
1019161748Scperciva		${SRV_PRIORITY}\ *)
1020161748Scperciva			SRV_W=`echo $X | cut -f 2 -d ' '`
1021161748Scperciva			SRV_W=$(($SRV_W + $SRV_W_ADD))
1022161748Scperciva			if [ $SRV_RND -lt $SRV_W ]; then
1023161748Scperciva				SERVERNAME=`echo $X | cut -f 3 -d ' '`
1024161748Scperciva				echo "$X" >> serverlist_tried
1025161748Scperciva				break
1026161748Scperciva			else
1027161748Scperciva				SRV_RND=$(($SRV_RND - $SRV_W))
1028161748Scperciva			fi
1029161748Scperciva			;;
1030161748Scperciva		esac
1031161748Scperciva	done < serverlist
1032161748Scperciva}
1033161748Scperciva
1034161748Scperciva# Take a list of ${oldhash}|${newhash} and output a list of needed patches,
1035161748Scperciva# i.e., those for which we have ${oldhash} and don't have ${newhash}.
1036161748Scpercivafetch_make_patchlist () {
1037161748Scperciva	grep -vE "^([0-9a-f]{64})\|\1$" |
1038161748Scperciva	    tr '|' ' ' |
1039161748Scperciva		while read X Y; do
1040161748Scperciva			if [ -f "files/${Y}.gz" ] ||
1041161748Scperciva			    [ ! -f "files/${X}.gz" ]; then
1042161748Scperciva				continue
1043161748Scperciva			fi
1044161748Scperciva			echo "${X}|${Y}"
1045161748Scperciva		done | uniq
1046161748Scperciva}
1047161748Scperciva
1048161748Scperciva# Print user-friendly progress statistics
1049161748Scpercivafetch_progress () {
1050161748Scperciva	LNC=0
1051161748Scperciva	while read x; do
1052161748Scperciva		LNC=$(($LNC + 1))
1053161748Scperciva		if [ $(($LNC % 10)) = 0 ]; then
1054161748Scperciva			echo -n $LNC
1055161748Scperciva		elif [ $(($LNC % 2)) = 0 ]; then
1056161748Scperciva			echo -n .
1057161748Scperciva		fi
1058161748Scperciva	done
1059161748Scperciva	echo -n " "
1060161748Scperciva}
1061161748Scperciva
1062173564Scperciva# Function for asking the user if everything is ok
1063173564Scpercivacontinuep () {
1064173564Scperciva	while read -p "Does this look reasonable (y/n)? " CONTINUE; do
1065173564Scperciva		case "${CONTINUE}" in
1066173564Scperciva		y*)
1067173564Scperciva			return 0
1068173564Scperciva			;;
1069173564Scperciva		n*)
1070173564Scperciva			return 1
1071173564Scperciva			;;
1072173564Scperciva		esac
1073173564Scperciva	done
1074173564Scperciva}
1075173564Scperciva
1076161748Scperciva# Initialize the working directory
1077161748Scpercivaworkdir_init () {
1078161748Scperciva	mkdir -p files
1079161748Scperciva	touch tINDEX.present
1080161748Scperciva}
1081161748Scperciva
1082161748Scperciva# Check that we have a public key with an appropriate hash, or
1083161748Scperciva# fetch the key if it doesn't exist.  Returns 1 if the key has
1084161748Scperciva# not yet been fetched.
1085161748Scpercivafetch_key () {
1086161748Scperciva	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
1087161748Scperciva		return 0
1088161748Scperciva	fi
1089161748Scperciva
1090161748Scperciva	echo -n "Fetching public key from ${SERVERNAME}... "
1091161748Scperciva	rm -f pub.ssl
1092161748Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \
1093161748Scperciva	    2>${QUIETREDIR} || true
1094161748Scperciva	if ! [ -r pub.ssl ]; then
1095161748Scperciva		echo "failed."
1096161748Scperciva		return 1
1097161748Scperciva	fi
1098161748Scperciva	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
1099161748Scperciva		echo "key has incorrect hash."
1100161748Scperciva		rm -f pub.ssl
1101161748Scperciva		return 1
1102161748Scperciva	fi
1103161748Scperciva	echo "done."
1104161748Scperciva}
1105161748Scperciva
1106161748Scperciva# Fetch metadata signature, aka "tag".
1107161748Scpercivafetch_tag () {
1108173564Scperciva	echo -n "Fetching metadata signature "
1109173564Scperciva	echo ${NDEBUG} "for ${RELNUM} from ${SERVERNAME}... "
1110161748Scperciva	rm -f latest.ssl
1111161748Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl	\
1112161748Scperciva	    2>${QUIETREDIR} || true
1113161748Scperciva	if ! [ -r latest.ssl ]; then
1114161748Scperciva		echo "failed."
1115161748Scperciva		return 1
1116161748Scperciva	fi
1117161748Scperciva
1118161748Scperciva	openssl rsautl -pubin -inkey pub.ssl -verify		\
1119161748Scperciva	    < latest.ssl > tag.new 2>${QUIETREDIR} || true
1120161748Scperciva	rm latest.ssl
1121161748Scperciva
1122161748Scperciva	if ! [ `wc -l < tag.new` = 1 ] ||
1123161748Scperciva	    ! grep -qE	\
1124161748Scperciva    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
1125161748Scperciva		tag.new; then
1126161748Scperciva		echo "invalid signature."
1127161748Scperciva		return 1
1128161748Scperciva	fi
1129161748Scperciva
1130161748Scperciva	echo "done."
1131161748Scperciva
1132161748Scperciva	RELPATCHNUM=`cut -f 4 -d '|' < tag.new`
1133161748Scperciva	TINDEXHASH=`cut -f 5 -d '|' < tag.new`
1134161748Scperciva	EOLTIME=`cut -f 6 -d '|' < tag.new`
1135161748Scperciva}
1136161748Scperciva
1137161748Scperciva# Sanity-check the patch number in a tag, to make sure that we're not
1138161748Scperciva# going to "update" backwards and to prevent replay attacks.
1139161748Scpercivafetch_tagsanity () {
1140161748Scperciva	# Check that we're not going to move from -pX to -pY with Y < X.
1141161748Scperciva	RELPX=`uname -r | sed -E 's,.*-,,'`
1142161748Scperciva	if echo ${RELPX} | grep -qE '^p[0-9]+$'; then
1143161748Scperciva		RELPX=`echo ${RELPX} | cut -c 2-`
1144161748Scperciva	else
1145161748Scperciva		RELPX=0
1146161748Scperciva	fi
1147161748Scperciva	if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then
1148161748Scperciva		echo
1149161748Scperciva		echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
1150161748Scperciva		echo " appear older than what"
1151161748Scperciva		echo "we are currently running (`uname -r`)!"
1152161748Scperciva		echo "Cowardly refusing to proceed any further."
1153161748Scperciva		return 1
1154161748Scperciva	fi
1155161748Scperciva
1156161748Scperciva	# If "tag" exists and corresponds to ${RELNUM}, make sure that
1157161748Scperciva	# it contains a patch number <= RELPATCHNUM, in order to protect
1158161748Scperciva	# against rollback (replay) attacks.
1159161748Scperciva	if [ -f tag ] &&
1160161748Scperciva	    grep -qE	\
1161161748Scperciva    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
1162161748Scperciva		tag; then
1163161748Scperciva		LASTRELPATCHNUM=`cut -f 4 -d '|' < tag`
1164161748Scperciva
1165161748Scperciva		if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then
1166161748Scperciva			echo
1167161748Scperciva			echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
1168161748Scperciva			echo " are older than the"
1169161748Scperciva			echo -n "most recently seen updates"
1170161748Scperciva			echo " (${RELNUM}-p${LASTRELPATCHNUM})."
1171161748Scperciva			echo "Cowardly refusing to proceed any further."
1172161748Scperciva			return 1
1173161748Scperciva		fi
1174161748Scperciva	fi
1175161748Scperciva}
1176161748Scperciva
1177161748Scperciva# Fetch metadata index file
1178161748Scpercivafetch_metadata_index () {
1179161748Scperciva	echo ${NDEBUG} "Fetching metadata index... "
1180161748Scperciva	rm -f ${TINDEXHASH}
1181161748Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH}
1182161748Scperciva	    2>${QUIETREDIR}
1183161748Scperciva	if ! [ -f ${TINDEXHASH} ]; then
1184161748Scperciva		echo "failed."
1185161748Scperciva		return 1
1186161748Scperciva	fi
1187161748Scperciva	if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then
1188161748Scperciva		echo "update metadata index corrupt."
1189161748Scperciva		return 1
1190161748Scperciva	fi
1191161748Scperciva	echo "done."
1192161748Scperciva}
1193161748Scperciva
1194161748Scperciva# Print an error message about signed metadata being bogus.
1195161748Scpercivafetch_metadata_bogus () {
1196161748Scperciva	echo
1197161748Scperciva	echo "The update metadata$1 is correctly signed, but"
1198161748Scperciva	echo "failed an integrity check."
1199161748Scperciva	echo "Cowardly refusing to proceed any further."
1200161748Scperciva	return 1
1201161748Scperciva}
1202161748Scperciva
1203161748Scperciva# Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH}
1204161748Scperciva# with the lines not named in $@ from tINDEX.present (if that file exists).
1205161748Scpercivafetch_metadata_index_merge () {
1206161748Scperciva	for METAFILE in $@; do
1207161748Scperciva		if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l`	\
1208161748Scperciva		    -ne 1 ]; then
1209161748Scperciva			fetch_metadata_bogus " index"
1210161748Scperciva			return 1
1211161748Scperciva		fi
1212161748Scperciva
1213161748Scperciva		grep -E "${METAFILE}\|" ${TINDEXHASH}
1214161748Scperciva	done |
1215161748Scperciva	    sort > tINDEX.wanted
1216161748Scperciva
1217161748Scperciva	if [ -f tINDEX.present ]; then
1218161748Scperciva		join -t '|' -v 2 tINDEX.wanted tINDEX.present |
1219161748Scperciva		    sort -m - tINDEX.wanted > tINDEX.new
1220161748Scperciva		rm tINDEX.wanted
1221161748Scperciva	else
1222161748Scperciva		mv tINDEX.wanted tINDEX.new
1223161748Scperciva	fi
1224161748Scperciva}
1225161748Scperciva
1226161748Scperciva# Sanity check all the lines of tINDEX.new.  Even if more metadata lines
1227161748Scperciva# are added by future versions of the server, this won't cause problems,
1228161748Scperciva# since the only lines which appear in tINDEX.new are the ones which we
1229161748Scperciva# specifically grepped out of ${TINDEXHASH}.
1230161748Scpercivafetch_metadata_index_sanity () {
1231161748Scperciva	if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then
1232161748Scperciva		fetch_metadata_bogus " index"
1233161748Scperciva		return 1
1234161748Scperciva	fi
1235161748Scperciva}
1236161748Scperciva
1237161748Scperciva# Sanity check the metadata file $1.
1238161748Scpercivafetch_metadata_sanity () {
1239161748Scperciva	# Some aliases to save space later: ${P} is a character which can
1240161748Scperciva	# appear in a path; ${M} is the four numeric metadata fields; and
1241161748Scperciva	# ${H} is a sha256 hash.
1242284940Sdelphij	P="[-+./:=,%@_[~[:alnum:]]"
1243161748Scperciva	M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+"
1244161748Scperciva	H="[0-9a-f]{64}"
1245161748Scperciva
1246161748Scperciva	# Check that the first four fields make sense.
1247161748Scperciva	if gunzip -c < files/$1.gz |
1248303304Sdelphij	    grep -qvE "^[a-z]+\|[0-9a-z-]+\|${P}+\|[fdL-]\|"; then
1249161748Scperciva		fetch_metadata_bogus ""
1250161748Scperciva		return 1
1251161748Scperciva	fi
1252161748Scperciva
1253161748Scperciva	# Remove the first three fields.
1254161748Scperciva	gunzip -c < files/$1.gz |
1255161748Scperciva	    cut -f 4- -d '|' > sanitycheck.tmp
1256161748Scperciva
1257161748Scperciva	# Sanity check entries with type 'f'
1258161748Scperciva	if grep -E '^f' sanitycheck.tmp |
1259161748Scperciva	    grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then
1260161748Scperciva		fetch_metadata_bogus ""
1261161748Scperciva		return 1
1262161748Scperciva	fi
1263161748Scperciva
1264161748Scperciva	# Sanity check entries with type 'd'
1265161748Scperciva	if grep -E '^d' sanitycheck.tmp |
1266161748Scperciva	    grep -qvE "^d\|${M}\|\|\$"; then
1267161748Scperciva		fetch_metadata_bogus ""
1268161748Scperciva		return 1
1269161748Scperciva	fi
1270161748Scperciva
1271161748Scperciva	# Sanity check entries with type 'L'
1272161748Scperciva	if grep -E '^L' sanitycheck.tmp |
1273161748Scperciva	    grep -qvE "^L\|${M}\|${P}*\|\$"; then
1274161748Scperciva		fetch_metadata_bogus ""
1275161748Scperciva		return 1
1276161748Scperciva	fi
1277161748Scperciva
1278161748Scperciva	# Sanity check entries with type '-'
1279161748Scperciva	if grep -E '^-' sanitycheck.tmp |
1280161748Scperciva	    grep -qvE "^-\|\|\|\|\|\|"; then
1281161748Scperciva		fetch_metadata_bogus ""
1282161748Scperciva		return 1
1283161748Scperciva	fi
1284161748Scperciva
1285161748Scperciva	# Clean up
1286161748Scperciva	rm sanitycheck.tmp
1287161748Scperciva}
1288161748Scperciva
1289161748Scperciva# Fetch the metadata index and metadata files listed in $@,
1290161748Scperciva# taking advantage of metadata patches where possible.
1291161748Scpercivafetch_metadata () {
1292161748Scperciva	fetch_metadata_index || return 1
1293161748Scperciva	fetch_metadata_index_merge $@ || return 1
1294161748Scperciva	fetch_metadata_index_sanity || return 1
1295161748Scperciva
1296161748Scperciva	# Generate a list of wanted metadata patches
1297161748Scperciva	join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new |
1298161748Scperciva	    fetch_make_patchlist > patchlist
1299161748Scperciva
1300161748Scperciva	if [ -s patchlist ]; then
1301161748Scperciva		# Attempt to fetch metadata patches
1302161748Scperciva		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
1303161748Scperciva		echo ${NDEBUG} "metadata patches.${DDSTATS}"
1304161748Scperciva		tr '|' '-' < patchlist |
1305161748Scperciva		    lam -s "${FETCHDIR}/tp/" - -s ".gz" |
1306161748Scperciva		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
1307161748Scperciva			2>${STATSREDIR} | fetch_progress
1308161748Scperciva		echo "done."
1309161748Scperciva
1310161748Scperciva		# Attempt to apply metadata patches
1311161748Scperciva		echo -n "Applying metadata patches... "
1312161748Scperciva		tr '|' ' ' < patchlist |
1313161748Scperciva		    while read X Y; do
1314161748Scperciva			if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
1315161748Scperciva			gunzip -c < ${X}-${Y}.gz > diff
1316161748Scperciva			gunzip -c < files/${X}.gz > diff-OLD
1317161748Scperciva
1318161748Scperciva			# Figure out which lines are being added and removed
1319161748Scperciva			grep -E '^-' diff |
1320161748Scperciva			    cut -c 2- |
1321161748Scperciva			    while read PREFIX; do
1322161748Scperciva				look "${PREFIX}" diff-OLD
1323161748Scperciva			    done |
1324161748Scperciva			    sort > diff-rm
1325161748Scperciva			grep -E '^\+' diff |
1326161748Scperciva			    cut -c 2- > diff-add
1327161748Scperciva
1328161748Scperciva			# Generate the new file
1329161748Scperciva			comm -23 diff-OLD diff-rm |
1330161748Scperciva			    sort - diff-add > diff-NEW
1331161748Scperciva
1332161748Scperciva			if [ `${SHA256} -q diff-NEW` = ${Y} ]; then
1333161748Scperciva				mv diff-NEW files/${Y}
1334161748Scperciva				gzip -n files/${Y}
1335161748Scperciva			else
1336161748Scperciva				mv diff-NEW ${Y}.bad
1337161748Scperciva			fi
1338161748Scperciva			rm -f ${X}-${Y}.gz diff
1339161748Scperciva			rm -f diff-OLD diff-NEW diff-add diff-rm
1340161748Scperciva		done 2>${QUIETREDIR}
1341161748Scperciva		echo "done."
1342161748Scperciva	fi
1343161748Scperciva
1344161748Scperciva	# Update metadata without patches
1345161748Scperciva	cut -f 2 -d '|' < tINDEX.new |
1346161748Scperciva	    while read Y; do
1347161748Scperciva		if [ ! -f "files/${Y}.gz" ]; then
1348161748Scperciva			echo ${Y};
1349161748Scperciva		fi
1350164600Scperciva	    done |
1351164600Scperciva	    sort -u > filelist
1352161748Scperciva
1353161748Scperciva	if [ -s filelist ]; then
1354161748Scperciva		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
1355161748Scperciva		echo ${NDEBUG} "metadata files... "
1356161748Scperciva		lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist |
1357161748Scperciva		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
1358161748Scperciva		    2>${QUIETREDIR}
1359161748Scperciva
1360161748Scperciva		while read Y; do
1361161748Scperciva			if ! [ -f ${Y}.gz ]; then
1362161748Scperciva				echo "failed."
1363161748Scperciva				return 1
1364161748Scperciva			fi
1365161748Scperciva			if [ `gunzip -c < ${Y}.gz |
1366161748Scperciva			    ${SHA256} -q` = ${Y} ]; then
1367161748Scperciva				mv ${Y}.gz files/${Y}.gz
1368161748Scperciva			else
1369161748Scperciva				echo "metadata is corrupt."
1370161748Scperciva				return 1
1371161748Scperciva			fi
1372161748Scperciva		done < filelist
1373161748Scperciva		echo "done."
1374161748Scperciva	fi
1375161748Scperciva
1376161748Scperciva# Sanity-check the metadata files.
1377161748Scperciva	cut -f 2 -d '|' tINDEX.new > filelist
1378161748Scperciva	while read X; do
1379161748Scperciva		fetch_metadata_sanity ${X} || return 1
1380161748Scperciva	done < filelist
1381161748Scperciva
1382161748Scperciva# Remove files which are no longer needed
1383161748Scperciva	cut -f 2 -d '|' tINDEX.present |
1384161748Scperciva	    sort > oldfiles
1385161748Scperciva	cut -f 2 -d '|' tINDEX.new |
1386161748Scperciva	    sort |
1387161748Scperciva	    comm -13 - oldfiles |
1388161748Scperciva	    lam -s "files/" - -s ".gz" |
1389161748Scperciva	    xargs rm -f
1390161748Scperciva	rm patchlist filelist oldfiles
1391161748Scperciva	rm ${TINDEXHASH}
1392161748Scperciva
1393161748Scperciva# We're done!
1394161748Scperciva	mv tINDEX.new tINDEX.present
1395161748Scperciva	mv tag.new tag
1396161748Scperciva
1397161748Scperciva	return 0
1398161748Scperciva}
1399161748Scperciva
1400173564Scperciva# Extract a subset of a downloaded metadata file containing only the parts
1401173564Scperciva# which are listed in COMPONENTS.
1402173564Scpercivafetch_filter_metadata_components () {
1403173564Scperciva	METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
1404173564Scperciva	gunzip -c < files/${METAHASH}.gz > $1.all
1405173564Scperciva
1406173564Scperciva	# Fish out the lines belonging to components we care about.
1407173564Scperciva	for C in ${COMPONENTS}; do
1408173564Scperciva		look "`echo ${C} | tr '/' '|'`|" $1.all
1409173564Scperciva	done > $1
1410173564Scperciva
1411173564Scperciva	# Remove temporary file.
1412173564Scperciva	rm $1.all
1413173564Scperciva}
1414173564Scperciva
1415161869Scperciva# Generate a filtered version of the metadata file $1 from the downloaded
1416161748Scperciva# file, by fishing out the lines corresponding to components we're trying
1417161748Scperciva# to keep updated, and then removing lines corresponding to paths we want
1418161748Scperciva# to ignore.
1419161748Scpercivafetch_filter_metadata () {
1420173564Scperciva	# Fish out the lines belonging to components we care about.
1421173564Scperciva	fetch_filter_metadata_components $1
1422161748Scperciva
1423161748Scperciva	# Canonicalize directory names by removing any trailing / in
1424161748Scperciva	# order to avoid listing directories multiple times if they
1425161748Scperciva	# belong to multiple components.  Turning "/" into "" doesn't
1426161748Scperciva	# matter, since we add a leading "/" when we use paths later.
1427173564Scperciva	cut -f 3- -d '|' $1 |
1428161748Scperciva	    sed -e 's,/|d|,|d|,' |
1429276088Sdes	    sed -e 's,/|-|,|-|,' |
1430161748Scperciva	    sort -u > $1.tmp
1431161748Scperciva
1432161748Scperciva	# Figure out which lines to ignore and remove them.
1433161748Scperciva	for X in ${IGNOREPATHS}; do
1434161748Scperciva		grep -E "^${X}" $1.tmp
1435161748Scperciva	done |
1436161748Scperciva	    sort -u |
1437161748Scperciva	    comm -13 - $1.tmp > $1
1438161748Scperciva
1439161748Scperciva	# Remove temporary files.
1440173564Scperciva	rm $1.tmp
1441161748Scperciva}
1442161748Scperciva
1443173564Scperciva# Filter the metadata file $1 by adding lines with "/boot/$2"
1444164600Scperciva# replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the
1445173564Scperciva# trailing "/kernel"); and if "/boot/$2" does not exist, remove
1446164600Scperciva# the original lines which start with that.
1447164600Scperciva# Put another way: Deal with the fact that the FOO kernel is sometimes
1448164600Scperciva# installed in /boot/FOO/ and is sometimes installed elsewhere.
1449161748Scpercivafetch_filter_kernel_names () {
1450173564Scperciva	grep ^/boot/$2 $1 |
1451173564Scperciva	    sed -e "s,/boot/$2,${KERNELDIR},g" |
1452161748Scperciva	    sort - $1 > $1.tmp
1453161748Scperciva	mv $1.tmp $1
1454164600Scperciva
1455173564Scperciva	if ! [ -d /boot/$2 ]; then
1456173564Scperciva		grep -v ^/boot/$2 $1 > $1.tmp
1457164600Scperciva		mv $1.tmp $1
1458164600Scperciva	fi
1459161748Scperciva}
1460161748Scperciva
1461161748Scperciva# For all paths appearing in $1 or $3, inspect the system
1462161748Scperciva# and generate $2 describing what is currently installed.
1463161748Scpercivafetch_inspect_system () {
1464161748Scperciva	# No errors yet...
1465161748Scperciva	rm -f .err
1466161748Scperciva
1467161748Scperciva	# Tell the user why his disk is suddenly making lots of noise
1468161748Scperciva	echo -n "Inspecting system... "
1469161748Scperciva
1470161748Scperciva	# Generate list of files to inspect
1471161748Scperciva	cat $1 $3 |
1472161748Scperciva	    cut -f 1 -d '|' |
1473161748Scperciva	    sort -u > filelist
1474161748Scperciva
1475161748Scperciva	# Examine each file and output lines of the form
1476161748Scperciva	# /path/to/file|type|device-inum|user|group|perm|flags|value
1477161748Scperciva	# sorted by device and inode number.
1478161748Scperciva	while read F; do
1479161748Scperciva		# If the symlink/file/directory does not exist, record this.
1480161748Scperciva		if ! [ -e ${BASEDIR}/${F} ]; then
1481161748Scperciva			echo "${F}|-||||||"
1482161748Scperciva			continue
1483161748Scperciva		fi
1484161748Scperciva		if ! [ -r ${BASEDIR}/${F} ]; then
1485161748Scperciva			echo "Cannot read file: ${BASEDIR}/${F}"	\
1486161748Scperciva			    >/dev/stderr
1487161748Scperciva			touch .err
1488161748Scperciva			return 1
1489161748Scperciva		fi
1490161748Scperciva
1491161748Scperciva		# Otherwise, output an index line.
1492161748Scperciva		if [ -L ${BASEDIR}/${F} ]; then
1493161748Scperciva			echo -n "${F}|L|"
1494161748Scperciva			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
1495161748Scperciva			readlink ${BASEDIR}/${F};
1496161748Scperciva		elif [ -f ${BASEDIR}/${F} ]; then
1497161748Scperciva			echo -n "${F}|f|"
1498161748Scperciva			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
1499161748Scperciva			sha256 -q ${BASEDIR}/${F};
1500161748Scperciva		elif [ -d ${BASEDIR}/${F} ]; then
1501161748Scperciva			echo -n "${F}|d|"
1502161748Scperciva			stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
1503161748Scperciva		else
1504161748Scperciva			echo "Unknown file type: ${BASEDIR}/${F}"	\
1505161748Scperciva			    >/dev/stderr
1506161748Scperciva			touch .err
1507161748Scperciva			return 1
1508161748Scperciva		fi
1509161748Scperciva	done < filelist |
1510161748Scperciva	    sort -k 3,3 -t '|' > $2.tmp
1511161748Scperciva	rm filelist
1512161748Scperciva
1513215087Sbcr	# Check if an error occurred during system inspection
1514161748Scperciva	if [ -f .err ]; then
1515161748Scperciva		return 1
1516161748Scperciva	fi
1517161748Scperciva
1518161748Scperciva	# Convert to the form
1519161748Scperciva	# /path/to/file|type|user|group|perm|flags|value|hlink
1520161748Scperciva	# by resolving identical device and inode numbers into hard links.
1521161748Scperciva	cut -f 1,3 -d '|' $2.tmp |
1522161748Scperciva	    sort -k 1,1 -t '|' |
1523161748Scperciva	    sort -s -u -k 2,2 -t '|' |
1524161748Scperciva	    join -1 2 -2 3 -t '|' - $2.tmp |
1525161748Scperciva	    awk -F \| -v OFS=\|		\
1526161748Scperciva		'{
1527161748Scperciva		    if (($2 == $3) || ($4 == "-"))
1528161748Scperciva			print $3,$4,$5,$6,$7,$8,$9,""
1529161748Scperciva		    else
1530161748Scperciva			print $3,$4,$5,$6,$7,$8,$9,$2
1531161748Scperciva		}' |
1532161748Scperciva	    sort > $2
1533161748Scperciva	rm $2.tmp
1534161748Scperciva
1535161748Scperciva	# We're finished looking around
1536161748Scperciva	echo "done."
1537161748Scperciva}
1538161748Scperciva
1539173564Scperciva# For any paths matching ${MERGECHANGES}, compare $1 and $2 and find any
1540173564Scperciva# files which differ; generate $3 containing these paths and the old hashes.
1541173564Scpercivafetch_filter_mergechanges () {
1542173564Scperciva	# Pull out the paths and hashes of the files matching ${MERGECHANGES}.
1543173564Scperciva	for F in $1 $2; do
1544173564Scperciva		for X in ${MERGECHANGES}; do
1545173564Scperciva			grep -E "^${X}" ${F}
1546173564Scperciva		done |
1547173564Scperciva		    cut -f 1,2,7 -d '|' |
1548173564Scperciva		    sort > ${F}-values
1549173564Scperciva	done
1550173564Scperciva
1551173564Scperciva	# Any line in $2-values which doesn't appear in $1-values and is a
1552173564Scperciva	# file means that we should list the path in $3.
1553173564Scperciva	comm -13 $1-values $2-values |
1554173564Scperciva	    fgrep '|f|' |
1555173564Scperciva	    cut -f 1 -d '|' > $2-paths
1556173564Scperciva
1557173564Scperciva	# For each path, pull out one (and only one!) entry from $1-values.
1558173564Scperciva	# Note that we cannot distinguish which "old" version the user made
1559173564Scperciva	# changes to; but hopefully any changes which occur due to security
1560173564Scperciva	# updates will exist in both the "new" version and the version which
1561173564Scperciva	# the user has installed, so the merging will still work.
1562173564Scperciva	while read X; do
1563173564Scperciva		look "${X}|" $1-values |
1564173564Scperciva		    head -1
1565173564Scperciva	done < $2-paths > $3
1566173564Scperciva
1567173564Scperciva	# Clean up
1568173564Scperciva	rm $1-values $2-values $2-paths
1569173564Scperciva}
1570173564Scperciva
1571161748Scperciva# For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123]
1572173564Scperciva# which correspond to lines in $2 with hashes not matching $1 or $3, unless
1573173564Scperciva# the paths are listed in $4.  For entries in $2 marked "not present"
1574173564Scperciva# (aka. type -), remove lines from $[123] unless there is a corresponding
1575173564Scperciva# entry in $1.
1576161748Scpercivafetch_filter_unmodified_notpresent () {
1577161748Scperciva	# Figure out which lines of $1 and $3 correspond to bits which
1578161748Scperciva	# should only be updated if they haven't changed, and fish out
1579161748Scperciva	# the (path, type, value) tuples.
1580161748Scperciva	# NOTE: We don't consider a file to be "modified" if it matches
1581161748Scperciva	# the hash from $3.
1582161748Scperciva	for X in ${UPDATEIFUNMODIFIED}; do
1583161748Scperciva		grep -E "^${X}" $1
1584161748Scperciva		grep -E "^${X}" $3
1585161748Scperciva	done |
1586161748Scperciva	    cut -f 1,2,7 -d '|' |
1587161748Scperciva	    sort > $1-values
1588161748Scperciva
1589161748Scperciva	# Do the same for $2.
1590161748Scperciva	for X in ${UPDATEIFUNMODIFIED}; do
1591161748Scperciva		grep -E "^${X}" $2
1592161748Scperciva	done |
1593161748Scperciva	    cut -f 1,2,7 -d '|' |
1594161748Scperciva	    sort > $2-values
1595161748Scperciva
1596161748Scperciva	# Any entry in $2-values which is not in $1-values corresponds to
1597173564Scperciva	# a path which we need to remove from $1, $2, and $3, unless it
1598173564Scperciva	# that path appears in $4.
1599173564Scperciva	comm -13 $1-values $2-values |
1600173564Scperciva	    sort -t '|' -k 1,1 > mlines.tmp
1601173564Scperciva	cut -f 1 -d '|' $4 |
1602173564Scperciva	    sort |
1603173564Scperciva	    join -v 2 -t '|' - mlines.tmp |
1604173564Scperciva	    sort > mlines
1605173564Scperciva	rm $1-values $2-values mlines.tmp
1606161748Scperciva
1607161748Scperciva	# Any lines in $2 which are not in $1 AND are "not present" lines
1608161748Scperciva	# also belong in mlines.
1609161748Scperciva	comm -13 $1 $2 |
1610161748Scperciva	    cut -f 1,2,7 -d '|' |
1611161748Scperciva	    fgrep '|-|' >> mlines
1612161748Scperciva
1613161748Scperciva	# Remove lines from $1, $2, and $3
1614161748Scperciva	for X in $1 $2 $3; do
1615161748Scperciva		sort -t '|' -k 1,1 ${X} > ${X}.tmp
1616161748Scperciva		cut -f 1 -d '|' < mlines |
1617161748Scperciva		    sort |
1618161748Scperciva		    join -v 2 -t '|' - ${X}.tmp |
1619161748Scperciva		    sort > ${X}
1620161748Scperciva		rm ${X}.tmp
1621161748Scperciva	done
1622161748Scperciva
1623161748Scperciva	# Store a list of the modified files, for future reference
1624161748Scperciva	fgrep -v '|-|' mlines |
1625161748Scperciva	    cut -f 1 -d '|' > modifiedfiles
1626161748Scperciva	rm mlines
1627161748Scperciva}
1628161748Scperciva
1629161748Scperciva# For each entry in $1 of type -, remove any corresponding
1630161748Scperciva# entry from $2 if ${ALLOWADD} != "yes".  Remove all entries
1631161748Scperciva# of type - from $1.
1632161748Scpercivafetch_filter_allowadd () {
1633161748Scperciva	cut -f 1,2 -d '|' < $1 |
1634161748Scperciva	    fgrep '|-' |
1635161748Scperciva	    cut -f 1 -d '|' > filesnotpresent
1636161748Scperciva
1637161748Scperciva	if [ ${ALLOWADD} != "yes" ]; then
1638161748Scperciva		sort < $2 |
1639161748Scperciva		    join -v 1 -t '|' - filesnotpresent |
1640161748Scperciva		    sort > $2.tmp
1641161748Scperciva		mv $2.tmp $2
1642161748Scperciva	fi
1643161748Scperciva
1644161748Scperciva	sort < $1 |
1645161748Scperciva	    join -v 1 -t '|' - filesnotpresent |
1646161748Scperciva	    sort > $1.tmp
1647161748Scperciva	mv $1.tmp $1
1648161748Scperciva	rm filesnotpresent
1649161748Scperciva}
1650161748Scperciva
1651161748Scperciva# If ${ALLOWDELETE} != "yes", then remove any entries from $1
1652161748Scperciva# which don't correspond to entries in $2.
1653161748Scpercivafetch_filter_allowdelete () {
1654161748Scperciva	# Produce a lists ${PATH}|${TYPE}
1655161748Scperciva	for X in $1 $2; do
1656161748Scperciva		cut -f 1-2 -d '|' < ${X} |
1657161748Scperciva		    sort -u > ${X}.nodes
1658161748Scperciva	done
1659161748Scperciva
1660161748Scperciva	# Figure out which lines need to be removed from $1.
1661161748Scperciva	if [ ${ALLOWDELETE} != "yes" ]; then
1662161748Scperciva		comm -23 $1.nodes $2.nodes > $1.badnodes
1663161748Scperciva	else
1664161748Scperciva		: > $1.badnodes
1665161748Scperciva	fi
1666161748Scperciva
1667161748Scperciva	# Remove the relevant lines from $1
1668161748Scperciva	while read X; do
1669161748Scperciva		look "${X}|" $1
1670161748Scperciva	done < $1.badnodes |
1671161748Scperciva	    comm -13 - $1 > $1.tmp
1672161748Scperciva	mv $1.tmp $1
1673161748Scperciva
1674161748Scperciva	rm $1.badnodes $1.nodes $2.nodes
1675161748Scperciva}
1676161748Scperciva
1677161748Scperciva# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2
1678161748Scperciva# with metadata not matching any entry in $1, replace the corresponding
1679161748Scperciva# line of $3 with one having the same metadata as the entry in $2.
1680161748Scpercivafetch_filter_modified_metadata () {
1681161748Scperciva	# Fish out the metadata from $1 and $2
1682161748Scperciva	for X in $1 $2; do
1683161748Scperciva		cut -f 1-6 -d '|' < ${X} > ${X}.metadata
1684161748Scperciva	done
1685161748Scperciva
1686161748Scperciva	# Find the metadata we need to keep
1687161748Scperciva	if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then
1688161748Scperciva		comm -13 $1.metadata $2.metadata > keepmeta
1689161748Scperciva	else
1690161748Scperciva		: > keepmeta
1691161748Scperciva	fi
1692161748Scperciva
1693161748Scperciva	# Extract the lines which we need to remove from $3, and
1694161748Scperciva	# construct the lines which we need to add to $3.
1695161748Scperciva	: > $3.remove
1696161748Scperciva	: > $3.add
1697161748Scperciva	while read LINE; do
1698161748Scperciva		NODE=`echo "${LINE}" | cut -f 1-2 -d '|'`
1699161748Scperciva		look "${NODE}|" $3 >> $3.remove
1700161748Scperciva		look "${NODE}|" $3 |
1701161748Scperciva		    cut -f 7- -d '|' |
1702161748Scperciva		    lam -s "${LINE}|" - >> $3.add
1703161748Scperciva	done < keepmeta
1704161748Scperciva
1705161748Scperciva	# Remove the specified lines and add the new lines.
1706161748Scperciva	sort $3.remove |
1707161748Scperciva	    comm -13 - $3 |
1708161748Scperciva	    sort -u - $3.add > $3.tmp
1709161748Scperciva	mv $3.tmp $3
1710161748Scperciva
1711161748Scperciva	rm keepmeta $1.metadata $2.metadata $3.add $3.remove
1712161748Scperciva}
1713161748Scperciva
1714161748Scperciva# Remove lines from $1 and $2 which are identical;
1715161748Scperciva# no need to update a file if it isn't changing.
1716161748Scpercivafetch_filter_uptodate () {
1717161748Scperciva	comm -23 $1 $2 > $1.tmp
1718161748Scperciva	comm -13 $1 $2 > $2.tmp
1719161748Scperciva
1720161748Scperciva	mv $1.tmp $1
1721161748Scperciva	mv $2.tmp $2
1722161748Scperciva}
1723161748Scperciva
1724173564Scperciva# Fetch any "clean" old versions of files we need for merging changes.
1725173564Scpercivafetch_files_premerge () {
1726173564Scperciva	# We only need to do anything if $1 is non-empty.
1727173564Scperciva	if [ -s $1 ]; then
1728173564Scperciva		# Tell the user what we're doing
1729173564Scperciva		echo -n "Fetching files from ${OLDRELNUM} for merging... "
1730173564Scperciva
1731173564Scperciva		# List of files wanted
1732173564Scperciva		fgrep '|f|' < $1 |
1733173564Scperciva		    cut -f 3 -d '|' |
1734173564Scperciva		    sort -u > files.wanted
1735173564Scperciva
1736173564Scperciva		# Only fetch the files we don't already have
1737173564Scperciva		while read Y; do
1738173564Scperciva			if [ ! -f "files/${Y}.gz" ]; then
1739173564Scperciva				echo ${Y};
1740173564Scperciva			fi
1741173564Scperciva		done < files.wanted > filelist
1742173564Scperciva
1743173564Scperciva		# Actually fetch them
1744173564Scperciva		lam -s "${OLDFETCHDIR}/f/" - -s ".gz" < filelist |
1745173564Scperciva		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
1746173564Scperciva		    2>${QUIETREDIR}
1747173564Scperciva
1748173564Scperciva		# Make sure we got them all, and move them into /files/
1749173564Scperciva		while read Y; do
1750173564Scperciva			if ! [ -f ${Y}.gz ]; then
1751173564Scperciva				echo "failed."
1752173564Scperciva				return 1
1753173564Scperciva			fi
1754173564Scperciva			if [ `gunzip -c < ${Y}.gz |
1755173564Scperciva			    ${SHA256} -q` = ${Y} ]; then
1756173564Scperciva				mv ${Y}.gz files/${Y}.gz
1757173564Scperciva			else
1758173564Scperciva				echo "${Y} has incorrect hash."
1759173564Scperciva				return 1
1760173564Scperciva			fi
1761173564Scperciva		done < filelist
1762173564Scperciva		echo "done."
1763173564Scperciva
1764173564Scperciva		# Clean up
1765173564Scperciva		rm filelist files.wanted
1766173564Scperciva	fi
1767173564Scperciva}
1768173564Scperciva
1769161748Scperciva# Prepare to fetch files: Generate a list of the files we need,
1770161748Scperciva# copy the unmodified files we have into /files/, and generate
1771161748Scperciva# a list of patches to download.
1772161748Scpercivafetch_files_prepare () {
1773161748Scperciva	# Tell the user why his disk is suddenly making lots of noise
1774161748Scperciva	echo -n "Preparing to download files... "
1775161748Scperciva
1776161748Scperciva	# Reduce indices to ${PATH}|${HASH} pairs
1777161748Scperciva	for X in $1 $2 $3; do
1778161748Scperciva		cut -f 1,2,7 -d '|' < ${X} |
1779161748Scperciva		    fgrep '|f|' |
1780161748Scperciva		    cut -f 1,3 -d '|' |
1781161748Scperciva		    sort > ${X}.hashes
1782161748Scperciva	done
1783161748Scperciva
1784161748Scperciva	# List of files wanted
1785161748Scperciva	cut -f 2 -d '|' < $3.hashes |
1786173441Scperciva	    sort -u |
1787173441Scperciva	    while read HASH; do
1788173441Scperciva		if ! [ -f files/${HASH}.gz ]; then
1789173441Scperciva			echo ${HASH}
1790173441Scperciva		fi
1791173441Scperciva	done > files.wanted
1792161748Scperciva
1793161748Scperciva	# Generate a list of unmodified files
1794161748Scperciva	comm -12 $1.hashes $2.hashes |
1795161748Scperciva	    sort -k 1,1 -t '|' > unmodified.files
1796161748Scperciva
1797161748Scperciva	# Copy all files into /files/.  We only need the unmodified files
1798161748Scperciva	# for use in patching; but we'll want all of them if the user asks
1799161748Scperciva	# to rollback the updates later.
1800171784Scperciva	while read LINE; do
1801171784Scperciva		F=`echo "${LINE}" | cut -f 1 -d '|'`
1802171784Scperciva		HASH=`echo "${LINE}" | cut -f 2 -d '|'`
1803171784Scperciva
1804171784Scperciva		# Skip files we already have.
1805171784Scperciva		if [ -f files/${HASH}.gz ]; then
1806171784Scperciva			continue
1807171784Scperciva		fi
1808171784Scperciva
1809171784Scperciva		# Make sure the file hasn't changed.
1810161748Scperciva		cp "${BASEDIR}/${F}" tmpfile
1811171784Scperciva		if [ `sha256 -q tmpfile` != ${HASH} ]; then
1812171784Scperciva			echo
1813171784Scperciva			echo "File changed while FreeBSD Update running: ${F}"
1814171784Scperciva			return 1
1815171784Scperciva		fi
1816171784Scperciva
1817171784Scperciva		# Place the file into storage.
1818171784Scperciva		gzip -c < tmpfile > files/${HASH}.gz
1819161748Scperciva		rm tmpfile
1820171784Scperciva	done < $2.hashes
1821161748Scperciva
1822161748Scperciva	# Produce a list of patches to download
1823161748Scperciva	sort -k 1,1 -t '|' $3.hashes |
1824161748Scperciva	    join -t '|' -o 2.2,1.2 - unmodified.files |
1825161748Scperciva	    fetch_make_patchlist > patchlist
1826161748Scperciva
1827161748Scperciva	# Garbage collect
1828161748Scperciva	rm unmodified.files $1.hashes $2.hashes $3.hashes
1829161748Scperciva
1830161748Scperciva	# We don't need the list of possible old files any more.
1831161748Scperciva	rm $1
1832161748Scperciva
1833161748Scperciva	# We're finished making noise
1834161748Scperciva	echo "done."
1835161748Scperciva}
1836161748Scperciva
1837161748Scperciva# Fetch files.
1838161748Scpercivafetch_files () {
1839161748Scperciva	# Attempt to fetch patches
1840161748Scperciva	if [ -s patchlist ]; then
1841161748Scperciva		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
1842161748Scperciva		echo ${NDEBUG} "patches.${DDSTATS}"
1843161748Scperciva		tr '|' '-' < patchlist |
1844173564Scperciva		    lam -s "${PATCHDIR}/" - |
1845161748Scperciva		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
1846161748Scperciva			2>${STATSREDIR} | fetch_progress
1847161748Scperciva		echo "done."
1848161748Scperciva
1849161748Scperciva		# Attempt to apply patches
1850161748Scperciva		echo -n "Applying patches... "
1851161748Scperciva		tr '|' ' ' < patchlist |
1852161748Scperciva		    while read X Y; do
1853161748Scperciva			if [ ! -f "${X}-${Y}" ]; then continue; fi
1854161748Scperciva			gunzip -c < files/${X}.gz > OLD
1855161748Scperciva
1856161748Scperciva			bspatch OLD NEW ${X}-${Y}
1857161748Scperciva
1858161748Scperciva			if [ `${SHA256} -q NEW` = ${Y} ]; then
1859161748Scperciva				mv NEW files/${Y}
1860161748Scperciva				gzip -n files/${Y}
1861161748Scperciva			fi
1862161748Scperciva			rm -f diff OLD NEW ${X}-${Y}
1863161748Scperciva		done 2>${QUIETREDIR}
1864161748Scperciva		echo "done."
1865161748Scperciva	fi
1866161748Scperciva
1867161748Scperciva	# Download files which couldn't be generate via patching
1868161748Scperciva	while read Y; do
1869161748Scperciva		if [ ! -f "files/${Y}.gz" ]; then
1870161748Scperciva			echo ${Y};
1871161748Scperciva		fi
1872161748Scperciva	done < files.wanted > filelist
1873161748Scperciva
1874161748Scperciva	if [ -s filelist ]; then
1875161748Scperciva		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
1876161748Scperciva		echo ${NDEBUG} "files... "
1877161748Scperciva		lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist |
1878161748Scperciva		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
1879161748Scperciva		    2>${QUIETREDIR}
1880161748Scperciva
1881161748Scperciva		while read Y; do
1882161748Scperciva			if ! [ -f ${Y}.gz ]; then
1883161748Scperciva				echo "failed."
1884161748Scperciva				return 1
1885161748Scperciva			fi
1886161748Scperciva			if [ `gunzip -c < ${Y}.gz |
1887161748Scperciva			    ${SHA256} -q` = ${Y} ]; then
1888161748Scperciva				mv ${Y}.gz files/${Y}.gz
1889161748Scperciva			else
1890161748Scperciva				echo "${Y} has incorrect hash."
1891161748Scperciva				return 1
1892161748Scperciva			fi
1893161748Scperciva		done < filelist
1894161748Scperciva		echo "done."
1895161748Scperciva	fi
1896161748Scperciva
1897161748Scperciva	# Clean up
1898161748Scperciva	rm files.wanted filelist patchlist
1899161748Scperciva}
1900161748Scperciva
1901161748Scperciva# Create and populate install manifest directory; and report what updates
1902161748Scperciva# are available.
1903161748Scpercivafetch_create_manifest () {
1904161748Scperciva	# If we have an existing install manifest, nuke it.
1905161748Scperciva	if [ -L "${BDHASH}-install" ]; then
1906161748Scperciva		rm -r ${BDHASH}-install/
1907161748Scperciva		rm ${BDHASH}-install
1908161748Scperciva	fi
1909161748Scperciva
1910161748Scperciva	# Report to the user if any updates were avoided due to local changes
1911161748Scperciva	if [ -s modifiedfiles ]; then
1912161748Scperciva		echo
1913161748Scperciva		echo -n "The following files are affected by updates, "
1914161748Scperciva		echo "but no changes have"
1915161748Scperciva		echo -n "been downloaded because the files have been "
1916161748Scperciva		echo "modified locally:"
1917161748Scperciva		cat modifiedfiles
1918217767Sgordon	fi | $PAGER
1919161748Scperciva	rm modifiedfiles
1920161748Scperciva
1921161748Scperciva	# If no files will be updated, tell the user and exit
1922161748Scperciva	if ! [ -s INDEX-PRESENT ] &&
1923161748Scperciva	    ! [ -s INDEX-NEW ]; then
1924161748Scperciva		rm INDEX-PRESENT INDEX-NEW
1925161748Scperciva		echo
1926161748Scperciva		echo -n "No updates needed to update system to "
1927161748Scperciva		echo "${RELNUM}-p${RELPATCHNUM}."
1928161748Scperciva		return
1929161748Scperciva	fi
1930161748Scperciva
1931161748Scperciva	# Divide files into (a) removed files, (b) added files, and
1932161748Scperciva	# (c) updated files.
1933161748Scperciva	cut -f 1 -d '|' < INDEX-PRESENT |
1934161748Scperciva	    sort > INDEX-PRESENT.flist
1935161748Scperciva	cut -f 1 -d '|' < INDEX-NEW |
1936161748Scperciva	    sort > INDEX-NEW.flist
1937161748Scperciva	comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed
1938161748Scperciva	comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added
1939161748Scperciva	comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated
1940161748Scperciva	rm INDEX-PRESENT.flist INDEX-NEW.flist
1941161748Scperciva
1942161748Scperciva	# Report removed files, if any
1943161748Scperciva	if [ -s files.removed ]; then
1944161748Scperciva		echo
1945161748Scperciva		echo -n "The following files will be removed "
1946161748Scperciva		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
1947161748Scperciva		cat files.removed
1948217767Sgordon	fi | $PAGER
1949161748Scperciva	rm files.removed
1950161748Scperciva
1951161748Scperciva	# Report added files, if any
1952161748Scperciva	if [ -s files.added ]; then
1953161748Scperciva		echo
1954161748Scperciva		echo -n "The following files will be added "
1955161748Scperciva		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
1956161748Scperciva		cat files.added
1957217767Sgordon	fi | $PAGER
1958161748Scperciva	rm files.added
1959161748Scperciva
1960161748Scperciva	# Report updated files, if any
1961161748Scperciva	if [ -s files.updated ]; then
1962161748Scperciva		echo
1963161748Scperciva		echo -n "The following files will be updated "
1964161748Scperciva		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
1965161748Scperciva
1966161748Scperciva		cat files.updated
1967217767Sgordon	fi | $PAGER
1968161748Scperciva	rm files.updated
1969161748Scperciva
1970161748Scperciva	# Create a directory for the install manifest.
1971161748Scperciva	MDIR=`mktemp -d install.XXXXXX` || return 1
1972161748Scperciva
1973161748Scperciva	# Populate it
1974161748Scperciva	mv INDEX-PRESENT ${MDIR}/INDEX-OLD
1975161748Scperciva	mv INDEX-NEW ${MDIR}/INDEX-NEW
1976161748Scperciva
1977161748Scperciva	# Link it into place
1978161748Scperciva	ln -s ${MDIR} ${BDHASH}-install
1979161748Scperciva}
1980161748Scperciva
1981161748Scperciva# Warn about any upcoming EoL
1982161748Scpercivafetch_warn_eol () {
1983161748Scperciva	# What's the current time?
1984161748Scperciva	NOWTIME=`date "+%s"`
1985161748Scperciva
1986161748Scperciva	# When did we last warn about the EoL date?
1987161748Scperciva	if [ -f lasteolwarn ]; then
1988161748Scperciva		LASTWARN=`cat lasteolwarn`
1989161748Scperciva	else
1990161748Scperciva		LASTWARN=`expr ${NOWTIME} - 63072000`
1991161748Scperciva	fi
1992161748Scperciva
1993161748Scperciva	# If the EoL time is past, warn.
1994161748Scperciva	if [ ${EOLTIME} -lt ${NOWTIME} ]; then
1995161748Scperciva		echo
1996161748Scperciva		cat <<-EOF
1997161869Scperciva		WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE.
1998161748Scperciva		Any security issues discovered after `date -r ${EOLTIME}`
1999161748Scperciva		will not have been corrected.
2000161748Scperciva		EOF
2001161748Scperciva		return 1
2002161748Scperciva	fi
2003161748Scperciva
2004161748Scperciva	# Figure out how long it has been since we last warned about the
2005161748Scperciva	# upcoming EoL, and how much longer we have left.
2006161748Scperciva	SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}`
2007161748Scperciva	TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}`
2008161748Scperciva
2009171838Scperciva	# Don't warn if the EoL is more than 3 months away
2010171838Scperciva	if [ ${TIMELEFT} -gt 7884000 ]; then
2011161748Scperciva		return 0
2012161748Scperciva	fi
2013161748Scperciva
2014161748Scperciva	# Don't warn if the time remaining is more than 3 times the time
2015161748Scperciva	# since the last warning.
2016161748Scperciva	if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then
2017161748Scperciva		return 0
2018161748Scperciva	fi
2019161748Scperciva
2020161748Scperciva	# Figure out what time units to use.
2021161748Scperciva	if [ ${TIMELEFT} -lt 604800 ]; then
2022161748Scperciva		UNIT="day"
2023161748Scperciva		SIZE=86400
2024161748Scperciva	elif [ ${TIMELEFT} -lt 2678400 ]; then
2025161748Scperciva		UNIT="week"
2026161748Scperciva		SIZE=604800
2027161748Scperciva	else
2028161748Scperciva		UNIT="month"
2029161748Scperciva		SIZE=2678400
2030161748Scperciva	fi
2031161748Scperciva
2032161748Scperciva	# Compute the right number of units
2033161748Scperciva	NUM=`expr ${TIMELEFT} / ${SIZE}`
2034161748Scperciva	if [ ${NUM} != 1 ]; then
2035161748Scperciva		UNIT="${UNIT}s"
2036161748Scperciva	fi
2037161748Scperciva
2038161748Scperciva	# Print the warning
2039161748Scperciva	echo
2040161748Scperciva	cat <<-EOF
2041161748Scperciva		WARNING: `uname -sr` is approaching its End-of-Life date.
2042161748Scperciva		It is strongly recommended that you upgrade to a newer
2043161748Scperciva		release within the next ${NUM} ${UNIT}.
2044161748Scperciva	EOF
2045161748Scperciva
2046161748Scperciva	# Update the stored time of last warning
2047161748Scperciva	echo ${NOWTIME} > lasteolwarn
2048161748Scperciva}
2049161748Scperciva
2050161748Scperciva# Do the actual work involved in "fetch" / "cron".
2051161748Scpercivafetch_run () {
2052161748Scperciva	workdir_init || return 1
2053161748Scperciva
2054161748Scperciva	# Prepare the mirror list.
2055161748Scperciva	fetch_pick_server_init && fetch_pick_server
2056161748Scperciva
2057161748Scperciva	# Try to fetch the public key until we run out of servers.
2058161748Scperciva	while ! fetch_key; do
2059161748Scperciva		fetch_pick_server || return 1
2060161748Scperciva	done
2061161748Scperciva
2062161748Scperciva	# Try to fetch the metadata index signature ("tag") until we run
2063161748Scperciva	# out of available servers; and sanity check the downloaded tag.
2064161748Scperciva	while ! fetch_tag; do
2065161748Scperciva		fetch_pick_server || return 1
2066161748Scperciva	done
2067161748Scperciva	fetch_tagsanity || return 1
2068161748Scperciva
2069161748Scperciva	# Fetch the latest INDEX-NEW and INDEX-OLD files.
2070161748Scperciva	fetch_metadata INDEX-NEW INDEX-OLD || return 1
2071161748Scperciva
2072161748Scperciva	# Generate filtered INDEX-NEW and INDEX-OLD files containing only
2073161748Scperciva	# the lines which (a) belong to components we care about, and (b)
2074161748Scperciva	# don't correspond to paths we're explicitly ignoring.
2075161748Scperciva	fetch_filter_metadata INDEX-NEW || return 1
2076161748Scperciva	fetch_filter_metadata INDEX-OLD || return 1
2077161748Scperciva
2078173564Scperciva	# Translate /boot/${KERNCONF} into ${KERNELDIR}
2079173564Scperciva	fetch_filter_kernel_names INDEX-NEW ${KERNCONF}
2080173564Scperciva	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
2081161748Scperciva
2082161748Scperciva	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
2083161748Scperciva	# system and generate an INDEX-PRESENT file.
2084161748Scperciva	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2085161748Scperciva
2086161748Scperciva	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
2087161748Scperciva	# correspond to lines in INDEX-PRESENT with hashes not appearing
2088161748Scperciva	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
2089161748Scperciva	# INDEX-PRESENT has type - and there isn't a corresponding entry in
2090161748Scperciva	# INDEX-OLD with type -.
2091173564Scperciva	fetch_filter_unmodified_notpresent	\
2092173564Scperciva	    INDEX-OLD INDEX-PRESENT INDEX-NEW /dev/null
2093161748Scperciva
2094161748Scperciva	# For each entry in INDEX-PRESENT of type -, remove any corresponding
2095161748Scperciva	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
2096161748Scperciva	# of type - from INDEX-PRESENT.
2097161748Scperciva	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
2098161748Scperciva
2099161748Scperciva	# If ${ALLOWDELETE} != "yes", then remove any entries from
2100161748Scperciva	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
2101161748Scperciva	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
2102161748Scperciva
2103161748Scperciva	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
2104161748Scperciva	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
2105161748Scperciva	# replace the corresponding line of INDEX-NEW with one having the
2106161748Scperciva	# same metadata as the entry in INDEX-PRESENT.
2107161748Scperciva	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
2108161748Scperciva
2109161748Scperciva	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
2110161748Scperciva	# no need to update a file if it isn't changing.
2111161748Scperciva	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
2112161748Scperciva
2113161748Scperciva	# Prepare to fetch files: Generate a list of the files we need,
2114161748Scperciva	# copy the unmodified files we have into /files/, and generate
2115161748Scperciva	# a list of patches to download.
2116171784Scperciva	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2117161748Scperciva
2118161748Scperciva	# Fetch files.
2119161748Scperciva	fetch_files || return 1
2120161748Scperciva
2121161748Scperciva	# Create and populate install manifest directory; and report what
2122161748Scperciva	# updates are available.
2123161748Scperciva	fetch_create_manifest || return 1
2124161748Scperciva
2125161748Scperciva	# Warn about any upcoming EoL
2126161748Scperciva	fetch_warn_eol || return 1
2127161748Scperciva}
2128161748Scperciva
2129173564Scperciva# If StrictComponents is not "yes", generate a new components list
2130173564Scperciva# with only the components which appear to be installed.
2131173564Scpercivaupgrade_guess_components () {
2132173564Scperciva	if [ "${STRICTCOMPONENTS}" = "no" ]; then
2133173564Scperciva		# Generate filtered INDEX-ALL with only the components listed
2134173564Scperciva		# in COMPONENTS.
2135173564Scperciva		fetch_filter_metadata_components $1 || return 1
2136173564Scperciva
2137173564Scperciva		# Tell the user why his disk is suddenly making lots of noise
2138173564Scperciva		echo -n "Inspecting system... "
2139173564Scperciva
2140173564Scperciva		# Look at the files on disk, and assume that a component is
2141173564Scperciva		# supposed to be present if it is more than half-present.
2142173564Scperciva		cut -f 1-3 -d '|' < INDEX-ALL |
2143173564Scperciva		    tr '|' ' ' |
2144173564Scperciva		    while read C S F; do
2145173564Scperciva			if [ -e ${BASEDIR}/${F} ]; then
2146173564Scperciva				echo "+ ${C}|${S}"
2147173564Scperciva			fi
2148173564Scperciva			echo "= ${C}|${S}"
2149173564Scperciva		    done |
2150173564Scperciva		    sort |
2151173564Scperciva		    uniq -c |
2152173564Scperciva		    sed -E 's,^ +,,' > compfreq
2153173564Scperciva		grep ' = ' compfreq |
2154173564Scperciva		    cut -f 1,3 -d ' ' |
2155173564Scperciva		    sort -k 2,2 -t ' ' > compfreq.total
2156173564Scperciva		grep ' + ' compfreq |
2157173564Scperciva		    cut -f 1,3 -d ' ' |
2158173564Scperciva		    sort -k 2,2 -t ' ' > compfreq.present
2159173564Scperciva		join -t ' ' -1 2 -2 2 compfreq.present compfreq.total |
2160173564Scperciva		    while read S P T; do
2161173564Scperciva			if [ ${P} -gt `expr ${T} / 2` ]; then
2162173564Scperciva				echo ${S}
2163173564Scperciva			fi
2164173564Scperciva		    done > comp.present
2165173564Scperciva		cut -f 2 -d ' ' < compfreq.total > comp.total
2166173564Scperciva		rm INDEX-ALL compfreq compfreq.total compfreq.present
2167173564Scperciva
2168173564Scperciva		# We're done making noise.
2169173564Scperciva		echo "done."
2170173564Scperciva
2171173564Scperciva		# Sometimes the kernel isn't installed where INDEX-ALL
2172173564Scperciva		# thinks that it should be: In particular, it is often in
2173173564Scperciva		# /boot/kernel instead of /boot/GENERIC or /boot/SMP.  To
2174173564Scperciva		# deal with this, if "kernel|X" is listed in comp.total
2175173564Scperciva		# (i.e., is a component which would be upgraded if it is
2176173564Scperciva		# found to be present) we will add it to comp.present.
2177173564Scperciva		# If "kernel|<anything>" is in comp.total but "kernel|X" is
2178173564Scperciva		# not, we print a warning -- the user is running a kernel
2179173564Scperciva		# which isn't part of the release.
2180173564Scperciva		KCOMP=`echo ${KERNCONF} | tr 'A-Z' 'a-z'`
2181173564Scperciva		grep -E "^kernel\|${KCOMP}\$" comp.total >> comp.present
2182173564Scperciva
2183173564Scperciva		if grep -qE "^kernel\|" comp.total &&
2184173564Scperciva		    ! grep -qE "^kernel\|${KCOMP}\$" comp.total; then
2185173564Scperciva			cat <<-EOF
2186173564Scperciva
2187173564ScpercivaWARNING: This system is running a "${KCOMP}" kernel, which is not a
2188173564Scpercivakernel configuration distributed as part of FreeBSD ${RELNUM}.
2189173564ScpercivaThis kernel will not be updated: you MUST update the kernel manually
2190173564Scpercivabefore running "$0 install".
2191173564Scperciva			EOF
2192173564Scperciva		fi
2193173564Scperciva
2194173564Scperciva		# Re-sort the list of installed components and generate
2195173564Scperciva		# the list of non-installed components.
2196173564Scperciva		sort -u < comp.present > comp.present.tmp
2197173564Scperciva		mv comp.present.tmp comp.present
2198173564Scperciva		comm -13 comp.present comp.total > comp.absent
2199173564Scperciva
2200173564Scperciva		# Ask the user to confirm that what we have is correct.  To
2201173564Scperciva		# reduce user confusion, translate "X|Y" back to "X/Y" (as
2202173564Scperciva		# subcomponents must be listed in the configuration file).
2203173564Scperciva		echo
2204173564Scperciva		echo -n "The following components of FreeBSD "
2205173564Scperciva		echo "seem to be installed:"
2206173564Scperciva		tr '|' '/' < comp.present |
2207173564Scperciva		    fmt -72
2208173564Scperciva		echo
2209173564Scperciva		echo -n "The following components of FreeBSD "
2210173564Scperciva		echo "do not seem to be installed:"
2211173564Scperciva		tr '|' '/' < comp.absent |
2212173564Scperciva		    fmt -72
2213173564Scperciva		echo
2214173564Scperciva		continuep || return 1
2215173564Scperciva		echo
2216173564Scperciva
2217173564Scperciva		# Suck the generated list of components into ${COMPONENTS}.
2218173564Scperciva		# Note that comp.present.tmp is used due to issues with
2219173564Scperciva		# pipelines and setting variables.
2220173564Scperciva		COMPONENTS=""
2221173564Scperciva		tr '|' '/' < comp.present > comp.present.tmp
2222173564Scperciva		while read C; do
2223173564Scperciva			COMPONENTS="${COMPONENTS} ${C}"
2224173564Scperciva		done < comp.present.tmp
2225173564Scperciva
2226173564Scperciva		# Delete temporary files
2227173564Scperciva		rm comp.present comp.present.tmp comp.absent comp.total
2228173564Scperciva	fi
2229173564Scperciva}
2230173564Scperciva
2231173564Scperciva# If StrictComponents is not "yes", COMPONENTS contains an entry
2232173564Scperciva# corresponding to the currently running kernel, and said kernel
2233173564Scperciva# does not exist in the new release, add "kernel/generic" to the
2234173564Scperciva# list of components.
2235173564Scpercivaupgrade_guess_new_kernel () {
2236173564Scperciva	if [ "${STRICTCOMPONENTS}" = "no" ]; then
2237173564Scperciva		# Grab the unfiltered metadata file.
2238173564Scperciva		METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
2239173564Scperciva		gunzip -c < files/${METAHASH}.gz > $1.all
2240173564Scperciva
2241173564Scperciva		# If "kernel/${KCOMP}" is in ${COMPONENTS} and that component
2242173564Scperciva		# isn't in $1.all, we need to add kernel/generic.
2243173564Scperciva		for C in ${COMPONENTS}; do
2244173564Scperciva			if [ ${C} = "kernel/${KCOMP}" ] &&
2245173564Scperciva			    ! grep -qE "^kernel\|${KCOMP}\|" $1.all; then
2246173564Scperciva				COMPONENTS="${COMPONENTS} kernel/generic"
2247173564Scperciva				NKERNCONF="GENERIC"
2248173564Scperciva				cat <<-EOF
2249173564Scperciva
2250173564ScpercivaWARNING: This system is running a "${KCOMP}" kernel, which is not a
2251173564Scpercivakernel configuration distributed as part of FreeBSD ${RELNUM}.
2252173564ScpercivaAs part of upgrading to FreeBSD ${RELNUM}, this kernel will be
2253173564Scpercivareplaced with a "generic" kernel.
2254173564Scperciva				EOF
2255173564Scperciva				continuep || return 1
2256173564Scperciva			fi
2257173564Scperciva		done
2258173564Scperciva
2259173564Scperciva		# Don't need this any more...
2260173564Scperciva		rm $1.all
2261173564Scperciva	fi
2262173564Scperciva}
2263173564Scperciva
2264173564Scperciva# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2265173564Scperciva# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2266173564Scpercivaupgrade_oldall_to_oldnew () {
2267173564Scperciva	# For each ${F}|... which appears in INDEX-ALL but does not appear
2268173564Scperciva	# in INDEX-OLD, add ${F}|-|||||| to INDEX-OLD.
2269173564Scperciva	cut -f 1 -d '|' < $1 |
2270173564Scperciva	    sort -u > $1.paths
2271173564Scperciva	cut -f 1 -d '|' < $2 |
2272173564Scperciva	    sort -u |
2273173564Scperciva	    comm -13 $1.paths - |
2274173564Scperciva	    lam - -s "|-||||||" |
2275173564Scperciva	    sort - $1 > $1.tmp
2276173564Scperciva	mv $1.tmp $1
2277173564Scperciva
2278173564Scperciva	# Remove lines from INDEX-OLD which also appear in INDEX-ALL
2279173564Scperciva	comm -23 $1 $2 > $1.tmp
2280173564Scperciva	mv $1.tmp $1
2281173564Scperciva
2282173564Scperciva	# Remove lines from INDEX-ALL which have a file name not appearing
2283173564Scperciva	# anywhere in INDEX-OLD (since these must be files which haven't
2284173564Scperciva	# changed -- if they were new, there would be an entry of type "-").
2285173564Scperciva	cut -f 1 -d '|' < $1 |
2286173564Scperciva	    sort -u > $1.paths
2287173564Scperciva	sort -k 1,1 -t '|' < $2 |
2288173564Scperciva	    join -t '|' - $1.paths |
2289173564Scperciva	    sort > $2.tmp
2290173564Scperciva	rm $1.paths
2291173564Scperciva	mv $2.tmp $2
2292173564Scperciva
2293173564Scperciva	# Rename INDEX-ALL to INDEX-NEW.
2294173564Scperciva	mv $2 $3
2295173564Scperciva}
2296173564Scperciva
2297221780Scperciva# Helper for upgrade_merge: Return zero true iff the two files differ only
2298284937Sdelphij# in the contents of their RCS tags.
2299221780Scpercivasamef () {
2300221780Scperciva	X=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $1 | ${SHA256}`
2301221780Scperciva	Y=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $2 | ${SHA256}`
2302221780Scperciva
2303221780Scperciva	if [ $X = $Y ]; then
2304221780Scperciva		return 0;
2305221780Scperciva	else
2306221780Scperciva		return 1;
2307221780Scperciva	fi
2308221780Scperciva}
2309221780Scperciva
2310173564Scperciva# From the list of "old" files in $1, merge changes in $2 with those in $3,
2311173564Scperciva# and update $3 to reflect the hashes of merged files.
2312173564Scpercivaupgrade_merge () {
2313173564Scperciva	# We only need to do anything if $1 is non-empty.
2314173564Scperciva	if [ -s $1 ]; then
2315173564Scperciva		cut -f 1 -d '|' $1 |
2316173564Scperciva		    sort > $1-paths
2317173564Scperciva
2318173564Scperciva		# Create staging area for merging files
2319173564Scperciva		rm -rf merge/
2320173564Scperciva		while read F; do
2321173564Scperciva			D=`dirname ${F}`
2322173564Scperciva			mkdir -p merge/old/${D}
2323173564Scperciva			mkdir -p merge/${OLDRELNUM}/${D}
2324173564Scperciva			mkdir -p merge/${RELNUM}/${D}
2325173564Scperciva			mkdir -p merge/new/${D}
2326173564Scperciva		done < $1-paths
2327173564Scperciva
2328173564Scperciva		# Copy in files
2329173564Scperciva		while read F; do
2330173564Scperciva			# Currently installed file
2331173564Scperciva			V=`look "${F}|" $2 | cut -f 7 -d '|'`
2332173564Scperciva			gunzip < files/${V}.gz > merge/old/${F}
2333173564Scperciva
2334173564Scperciva			# Old release
2335173564Scperciva			if look "${F}|" $1 | fgrep -q "|f|"; then
2336173564Scperciva				V=`look "${F}|" $1 | cut -f 3 -d '|'`
2337173564Scperciva				gunzip < files/${V}.gz		\
2338173564Scperciva				    > merge/${OLDRELNUM}/${F}
2339173564Scperciva			fi
2340173564Scperciva
2341173564Scperciva			# New release
2342173564Scperciva			if look "${F}|" $3 | cut -f 1,2,7 -d '|' |
2343173564Scperciva			    fgrep -q "|f|"; then
2344173564Scperciva				V=`look "${F}|" $3 | cut -f 7 -d '|'`
2345173564Scperciva				gunzip < files/${V}.gz		\
2346173564Scperciva				    > merge/${RELNUM}/${F}
2347173564Scperciva			fi
2348173564Scperciva		done < $1-paths
2349173564Scperciva
2350173564Scperciva		# Attempt to automatically merge changes
2351173564Scperciva		echo -n "Attempting to automatically merge "
2352173564Scperciva		echo -n "changes in files..."
2353173564Scperciva		: > failed.merges
2354173564Scperciva		while read F; do
2355173564Scperciva			# If the file doesn't exist in the new release,
2356173564Scperciva			# the result of "merging changes" is having the file
2357173564Scperciva			# not exist.
2358173564Scperciva			if ! [ -f merge/${RELNUM}/${F} ]; then
2359173564Scperciva				continue
2360173564Scperciva			fi
2361173564Scperciva
2362173564Scperciva			# If the file didn't exist in the old release, we're
2363173564Scperciva			# going to throw away the existing file and hope that
2364173564Scperciva			# the version from the new release is what we want.
2365173564Scperciva			if ! [ -f merge/${OLDRELNUM}/${F} ]; then
2366173564Scperciva				cp merge/${RELNUM}/${F} merge/new/${F}
2367173564Scperciva				continue
2368173564Scperciva			fi
2369173564Scperciva
2370173564Scperciva			# Some files need special treatment.
2371173564Scperciva			case ${F} in
2372173564Scperciva			/etc/spwd.db | /etc/pwd.db | /etc/login.conf.db)
2373173564Scperciva				# Don't merge these -- we're rebuild them
2374173564Scperciva				# after updates are installed.
2375173564Scperciva				cp merge/old/${F} merge/new/${F}
2376173564Scperciva				;;
2377173564Scperciva			*)
2378173564Scperciva				if ! merge -p -L "current version"	\
2379173564Scperciva				    -L "${OLDRELNUM}" -L "${RELNUM}"	\
2380173564Scperciva				    merge/old/${F}			\
2381173564Scperciva				    merge/${OLDRELNUM}/${F}		\
2382173564Scperciva				    merge/${RELNUM}/${F}		\
2383173564Scperciva				    > merge/new/${F} 2>/dev/null; then
2384173564Scperciva					echo ${F} >> failed.merges
2385173564Scperciva				fi
2386173564Scperciva				;;
2387173564Scperciva			esac
2388173564Scperciva		done < $1-paths
2389173564Scperciva		echo " done."
2390173564Scperciva
2391173564Scperciva		# Ask the user to handle any files which didn't merge.
2392173564Scperciva		while read F; do
2393221780Scperciva			# If the installed file differs from the version in
2394284937Sdelphij			# the old release only due to RCS tag expansion
2395221780Scperciva			# then just use the version in the new release.
2396221780Scperciva			if samef merge/old/${F} merge/${OLDRELNUM}/${F}; then
2397221780Scperciva				cp merge/${RELNUM}/${F} merge/new/${F}
2398221780Scperciva				continue
2399221780Scperciva			fi
2400221780Scperciva
2401173564Scperciva			cat <<-EOF
2402173564Scperciva
2403173564ScpercivaThe following file could not be merged automatically: ${F}
2404173564ScpercivaPress Enter to edit this file in ${EDITOR} and resolve the conflicts
2405173564Scpercivamanually...
2406173564Scperciva			EOF
2407173564Scperciva			read dummy </dev/tty
2408173564Scperciva			${EDITOR} `pwd`/merge/new/${F} < /dev/tty
2409173564Scperciva		done < failed.merges
2410173564Scperciva		rm failed.merges
2411173564Scperciva
2412173564Scperciva		# Ask the user to confirm that he likes how the result
2413173564Scperciva		# of merging files.
2414173564Scperciva		while read F; do
2415221780Scperciva			# Skip files which haven't changed except possibly
2416284937Sdelphij			# in their RCS tags.
2417221780Scperciva			if [ -f merge/old/${F} ] && [ -f merge/new/${F} ] &&
2418221780Scperciva			    samef merge/old/${F} merge/new/${F}; then
2419173564Scperciva				continue
2420173564Scperciva			fi
2421173564Scperciva
2422221780Scperciva			# Skip files where the installed file differs from
2423284937Sdelphij			# the old file only due to RCS tags.
2424221780Scperciva			if [ -f merge/old/${F} ] &&
2425221780Scperciva			    [ -f merge/${OLDRELNUM}/${F} ] &&
2426221780Scperciva			    samef merge/old/${F} merge/${OLDRELNUM}/${F}; then
2427221780Scperciva				continue
2428221780Scperciva			fi
2429221780Scperciva
2430173564Scperciva			# Warn about files which are ceasing to exist.
2431173564Scperciva			if ! [ -f merge/new/${F} ]; then
2432173564Scperciva				cat <<-EOF
2433173564Scperciva
2434173564ScpercivaThe following file will be removed, as it no longer exists in
2435173564ScpercivaFreeBSD ${RELNUM}: ${F}
2436173564Scperciva				EOF
2437173564Scperciva				continuep < /dev/tty || return 1
2438173564Scperciva				continue
2439173564Scperciva			fi
2440173564Scperciva
2441173564Scperciva			# Print changes for the user's approval.
2442173564Scperciva			cat <<-EOF
2443173564Scperciva
2444173564ScpercivaThe following changes, which occurred between FreeBSD ${OLDRELNUM} and
2445173564ScpercivaFreeBSD ${RELNUM} have been merged into ${F}:
2446173564ScpercivaEOF
2447173564Scperciva			diff -U 5 -L "current version" -L "new version"	\
2448173564Scperciva			    merge/old/${F} merge/new/${F} || true
2449173564Scperciva			continuep < /dev/tty || return 1
2450173564Scperciva		done < $1-paths
2451173564Scperciva
2452173564Scperciva		# Store merged files.
2453173564Scperciva		while read F; do
2454177527Scperciva			if [ -f merge/new/${F} ]; then
2455177527Scperciva				V=`${SHA256} -q merge/new/${F}`
2456173564Scperciva
2457173564Scperciva				gzip -c < merge/new/${F} > files/${V}.gz
2458173564Scperciva				echo "${F}|${V}"
2459173564Scperciva			fi
2460173564Scperciva		done < $1-paths > newhashes
2461173564Scperciva
2462173564Scperciva		# Pull lines out from $3 which need to be updated to
2463173564Scperciva		# reflect merged files.
2464173564Scperciva		while read F; do
2465173564Scperciva			look "${F}|" $3
2466173564Scperciva		done < $1-paths > $3-oldlines
2467173564Scperciva
2468173564Scperciva		# Update lines to reflect merged files
2469173564Scperciva		join -t '|' -o 1.1,1.2,1.3,1.4,1.5,1.6,2.2,1.8		\
2470173564Scperciva		    $3-oldlines newhashes > $3-newlines
2471173564Scperciva
2472173564Scperciva		# Remove old lines from $3 and add new lines.
2473173564Scperciva		sort $3-oldlines |
2474173564Scperciva		    comm -13 - $3 |
2475173564Scperciva		    sort - $3-newlines > $3.tmp
2476173564Scperciva		mv $3.tmp $3
2477173564Scperciva
2478173564Scperciva		# Clean up
2479173564Scperciva		rm $1-paths newhashes $3-oldlines $3-newlines
2480173564Scperciva		rm -rf merge/
2481173564Scperciva	fi
2482173564Scperciva
2483173564Scperciva	# We're done with merging files.
2484173564Scperciva	rm $1
2485173564Scperciva}
2486173564Scperciva
2487173564Scperciva# Do the work involved in fetching upgrades to a new release
2488173564Scpercivaupgrade_run () {
2489173564Scperciva	workdir_init || return 1
2490173564Scperciva
2491173564Scperciva	# Prepare the mirror list.
2492173564Scperciva	fetch_pick_server_init && fetch_pick_server
2493173564Scperciva
2494173564Scperciva	# Try to fetch the public key until we run out of servers.
2495173564Scperciva	while ! fetch_key; do
2496173564Scperciva		fetch_pick_server || return 1
2497173564Scperciva	done
2498173564Scperciva 
2499173564Scperciva	# Try to fetch the metadata index signature ("tag") until we run
2500173564Scperciva	# out of available servers; and sanity check the downloaded tag.
2501173564Scperciva	while ! fetch_tag; do
2502173564Scperciva		fetch_pick_server || return 1
2503173564Scperciva	done
2504173564Scperciva	fetch_tagsanity || return 1
2505173564Scperciva
2506173564Scperciva	# Fetch the INDEX-OLD and INDEX-ALL.
2507173564Scperciva	fetch_metadata INDEX-OLD INDEX-ALL || return 1
2508173564Scperciva
2509173564Scperciva	# If StrictComponents is not "yes", generate a new components list
2510173564Scperciva	# with only the components which appear to be installed.
2511173564Scperciva	upgrade_guess_components INDEX-ALL || return 1
2512173564Scperciva
2513173564Scperciva	# Generate filtered INDEX-OLD and INDEX-ALL files containing only
2514173564Scperciva	# the components we want and without anything marked as "Ignore".
2515173564Scperciva	fetch_filter_metadata INDEX-OLD || return 1
2516173564Scperciva	fetch_filter_metadata INDEX-ALL || return 1
2517173564Scperciva
2518173564Scperciva	# Merge the INDEX-OLD and INDEX-ALL files into INDEX-OLD.
2519173564Scperciva	sort INDEX-OLD INDEX-ALL > INDEX-OLD.tmp
2520173564Scperciva	mv INDEX-OLD.tmp INDEX-OLD
2521173564Scperciva	rm INDEX-ALL
2522173564Scperciva
2523173564Scperciva	# Adjust variables for fetching files from the new release.
2524173564Scperciva	OLDRELNUM=${RELNUM}
2525173564Scperciva	RELNUM=${TARGETRELEASE}
2526173564Scperciva	OLDFETCHDIR=${FETCHDIR}
2527173564Scperciva	FETCHDIR=${RELNUM}/${ARCH}
2528173564Scperciva
2529173564Scperciva	# Try to fetch the NEW metadata index signature ("tag") until we run
2530173564Scperciva	# out of available servers; and sanity check the downloaded tag.
2531173564Scperciva	while ! fetch_tag; do
2532173564Scperciva		fetch_pick_server || return 1
2533173564Scperciva	done
2534173564Scperciva
2535173564Scperciva	# Fetch the new INDEX-ALL.
2536173564Scperciva	fetch_metadata INDEX-ALL || return 1
2537173564Scperciva
2538173564Scperciva	# If StrictComponents is not "yes", COMPONENTS contains an entry
2539173564Scperciva	# corresponding to the currently running kernel, and said kernel
2540173564Scperciva	# does not exist in the new release, add "kernel/generic" to the
2541173564Scperciva	# list of components.
2542173564Scperciva	upgrade_guess_new_kernel INDEX-ALL || return 1
2543173564Scperciva
2544173564Scperciva	# Filter INDEX-ALL to contain only the components we want and without
2545173564Scperciva	# anything marked as "Ignore".
2546173564Scperciva	fetch_filter_metadata INDEX-ALL || return 1
2547173564Scperciva
2548173564Scperciva	# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2549173564Scperciva	# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2550173564Scperciva	upgrade_oldall_to_oldnew INDEX-OLD INDEX-ALL INDEX-NEW
2551173564Scperciva
2552173564Scperciva	# Translate /boot/${KERNCONF} or /boot/${NKERNCONF} into ${KERNELDIR}
2553173564Scperciva	fetch_filter_kernel_names INDEX-NEW ${NKERNCONF}
2554173564Scperciva	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
2555173564Scperciva
2556173564Scperciva	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
2557173564Scperciva	# system and generate an INDEX-PRESENT file.
2558173564Scperciva	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2559173564Scperciva
2560173564Scperciva	# Based on ${MERGECHANGES}, generate a file tomerge-old with the
2561173564Scperciva	# paths and hashes of old versions of files to merge.
2562173564Scperciva	fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT tomerge-old
2563173564Scperciva
2564173564Scperciva	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
2565173564Scperciva	# correspond to lines in INDEX-PRESENT with hashes not appearing
2566173564Scperciva	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
2567173564Scperciva	# INDEX-PRESENT has type - and there isn't a corresponding entry in
2568173564Scperciva	# INDEX-OLD with type -.
2569173564Scperciva	fetch_filter_unmodified_notpresent	\
2570173564Scperciva	    INDEX-OLD INDEX-PRESENT INDEX-NEW tomerge-old
2571173564Scperciva
2572173564Scperciva	# For each entry in INDEX-PRESENT of type -, remove any corresponding
2573173564Scperciva	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
2574173564Scperciva	# of type - from INDEX-PRESENT.
2575173564Scperciva	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
2576173564Scperciva
2577173564Scperciva	# If ${ALLOWDELETE} != "yes", then remove any entries from
2578173564Scperciva	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
2579173564Scperciva	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
2580173564Scperciva
2581173564Scperciva	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
2582173564Scperciva	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
2583173564Scperciva	# replace the corresponding line of INDEX-NEW with one having the
2584173564Scperciva	# same metadata as the entry in INDEX-PRESENT.
2585173564Scperciva	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
2586173564Scperciva
2587173564Scperciva	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
2588173564Scperciva	# no need to update a file if it isn't changing.
2589173564Scperciva	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
2590173564Scperciva
2591173564Scperciva	# Fetch "clean" files from the old release for merging changes.
2592173564Scperciva	fetch_files_premerge tomerge-old
2593173564Scperciva
2594173564Scperciva	# Prepare to fetch files: Generate a list of the files we need,
2595173564Scperciva	# copy the unmodified files we have into /files/, and generate
2596173564Scperciva	# a list of patches to download.
2597173564Scperciva	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2598173564Scperciva
2599173564Scperciva	# Fetch patches from to-${RELNUM}/${ARCH}/bp/
2600173564Scperciva	PATCHDIR=to-${RELNUM}/${ARCH}/bp
2601173564Scperciva	fetch_files || return 1
2602173564Scperciva
2603173564Scperciva	# Merge configuration file changes.
2604173564Scperciva	upgrade_merge tomerge-old INDEX-PRESENT INDEX-NEW || return 1
2605173564Scperciva
2606173564Scperciva	# Create and populate install manifest directory; and report what
2607173564Scperciva	# updates are available.
2608173564Scperciva	fetch_create_manifest || return 1
2609173564Scperciva
2610173564Scperciva	# Leave a note behind to tell the "install" command that the kernel
2611173564Scperciva	# needs to be installed before the world.
2612173564Scperciva	touch ${BDHASH}-install/kernelfirst
2613212431Scperciva
2614212431Scperciva	# Remind the user that they need to run "freebsd-update install"
2615212431Scperciva	# to install the downloaded bits, in case they didn't RTFM.
2616212431Scperciva	echo "To install the downloaded upgrades, run \"$0 install\"."
2617173564Scperciva}
2618173564Scperciva
2619161748Scperciva# Make sure that all the file hashes mentioned in $@ have corresponding
2620161748Scperciva# gzipped files stored in /files/.
2621161748Scpercivainstall_verify () {
2622161748Scperciva	# Generate a list of hashes
2623161748Scperciva	cat $@ |
2624161748Scperciva	    cut -f 2,7 -d '|' |
2625161748Scperciva	    grep -E '^f' |
2626161748Scperciva	    cut -f 2 -d '|' |
2627161748Scperciva	    sort -u > filelist
2628161748Scperciva
2629161748Scperciva	# Make sure all the hashes exist
2630161748Scperciva	while read HASH; do
2631161748Scperciva		if ! [ -f files/${HASH}.gz ]; then
2632161748Scperciva			echo -n "Update files missing -- "
2633161748Scperciva			echo "this should never happen."
2634161748Scperciva			echo "Re-run '$0 fetch'."
2635161748Scperciva			return 1
2636161748Scperciva		fi
2637161748Scperciva	done < filelist
2638161748Scperciva
2639161748Scperciva	# Clean up
2640161748Scperciva	rm filelist
2641161748Scperciva}
2642161748Scperciva
2643161748Scperciva# Remove the system immutable flag from files
2644161748Scpercivainstall_unschg () {
2645161748Scperciva	# Generate file list
2646161748Scperciva	cat $@ |
2647161748Scperciva	    cut -f 1 -d '|' > filelist
2648161748Scperciva
2649161748Scperciva	# Remove flags
2650161748Scperciva	while read F; do
2651169603Scperciva		if ! [ -e ${BASEDIR}/${F} ]; then
2652161748Scperciva			continue
2653284936Sdelphij		else
2654284936Sdelphij			echo ${BASEDIR}/${F}
2655161748Scperciva		fi
2656284936Sdelphij	done < filelist | xargs chflags noschg || return 1
2657161748Scperciva
2658161748Scperciva	# Clean up
2659161748Scperciva	rm filelist
2660161748Scperciva}
2661161748Scperciva
2662196392Ssimon# Decide which directory name to use for kernel backups.
2663196392Ssimonbackup_kernel_finddir () {
2664196392Ssimon	CNT=0
2665196392Ssimon	while true ; do
2666196392Ssimon		# Pathname does not exist, so it is OK use that name
2667196392Ssimon		# for backup directory.
2668279556Sthomas		if [ ! -e $BASEDIR/$BACKUPKERNELDIR ]; then
2669196392Ssimon			return 0
2670196392Ssimon		fi
2671196392Ssimon
2672196392Ssimon		# If directory do exist, we only use if it has our
2673196392Ssimon		# marker file.
2674279556Sthomas		if [ -d $BASEDIR/$BACKUPKERNELDIR -a \
2675279556Sthomas			-e $BASEDIR/$BACKUPKERNELDIR/.freebsd-update ]; then
2676196392Ssimon			return 0
2677196392Ssimon		fi
2678196392Ssimon
2679196392Ssimon		# We could not use current directory name, so add counter to
2680196392Ssimon		# the end and try again.
2681196392Ssimon		CNT=$((CNT + 1))
2682196392Ssimon		if [ $CNT -gt 9 ]; then
2683279556Sthomas			echo "Could not find valid backup dir ($BASEDIR/$BACKUPKERNELDIR)"
2684196392Ssimon			exit 1
2685196392Ssimon		fi
2686196392Ssimon		BACKUPKERNELDIR="`echo $BACKUPKERNELDIR | sed -Ee 's/[0-9]\$//'`"
2687196392Ssimon		BACKUPKERNELDIR="${BACKUPKERNELDIR}${CNT}"
2688196392Ssimon	done
2689196392Ssimon}
2690196392Ssimon
2691196392Ssimon# Backup the current kernel using hardlinks, if not disabled by user.
2692196392Ssimon# Since we delete all files in the directory used for previous backups
2693196392Ssimon# we create a marker file called ".freebsd-update" in the directory so
2694196392Ssimon# we can determine on the next run that the directory was created by
2695196392Ssimon# freebsd-update and we then do not accidentally remove user files in
2696196392Ssimon# the unlikely case that the user has created a directory with a
2697196392Ssimon# conflicting name.
2698196392Ssimonbackup_kernel () {
2699196392Ssimon	# Only make kernel backup is so configured.
2700196392Ssimon	if [ $BACKUPKERNEL != yes ]; then
2701196392Ssimon		return 0
2702196392Ssimon	fi
2703196392Ssimon
2704196392Ssimon	# Decide which directory name to use for kernel backups.
2705196392Ssimon	backup_kernel_finddir
2706196392Ssimon
2707196392Ssimon	# Remove old kernel backup files.  If $BACKUPKERNELDIR was
2708196392Ssimon	# "not ours", backup_kernel_finddir would have exited, so
2709196392Ssimon	# deleting the directory content is as safe as we can make it.
2710279556Sthomas	if [ -d $BASEDIR/$BACKUPKERNELDIR ]; then
2711279556Sthomas		rm -fr $BASEDIR/$BACKUPKERNELDIR
2712196392Ssimon	fi
2713196392Ssimon
2714212505Sjh	# Create directories for backup.
2715279556Sthomas	mkdir -p $BASEDIR/$BACKUPKERNELDIR
2716279556Sthomas	mtree -cdn -p "${BASEDIR}/${KERNELDIR}" | \
2717279556Sthomas	    mtree -Ue -p "${BASEDIR}/${BACKUPKERNELDIR}" > /dev/null
2718196392Ssimon
2719196392Ssimon	# Mark the directory as having been created by freebsd-update.
2720279556Sthomas	touch $BASEDIR/$BACKUPKERNELDIR/.freebsd-update
2721196392Ssimon	if [ $? -ne 0 ]; then
2722196392Ssimon		echo "Could not create kernel backup directory"
2723196392Ssimon		exit 1
2724196392Ssimon	fi
2725196392Ssimon
2726196392Ssimon	# Disable pathname expansion to be sure *.symbols is not
2727196392Ssimon	# expanded.
2728196392Ssimon	set -f
2729196392Ssimon
2730196392Ssimon	# Use find to ignore symbol files, unless disabled by user.
2731196392Ssimon	if [ $BACKUPKERNELSYMBOLFILES = yes ]; then
2732196392Ssimon		FINDFILTER=""
2733196392Ssimon	else
2734196392Ssimon		FINDFILTER=-"a ! -name *.symbols"
2735196392Ssimon	fi
2736196392Ssimon
2737196392Ssimon	# Backup all the kernel files using hardlinks.
2738279556Sthomas	(cd ${BASEDIR}/${KERNELDIR} && find . -type f $FINDFILTER -exec \
2739279556Sthomas	    cp -pl '{}' ${BASEDIR}/${BACKUPKERNELDIR}/'{}' \;)
2740196392Ssimon
2741196392Ssimon	# Re-enable patchname expansion.
2742196392Ssimon	set +f
2743196392Ssimon}
2744196392Ssimon
2745161748Scperciva# Install new files
2746161748Scpercivainstall_from_index () {
2747161748Scperciva	# First pass: Do everything apart from setting file flags.  We
2748161748Scperciva	# can't set flags yet, because schg inhibits hard linking.
2749161748Scperciva	sort -k 1,1 -t '|' $1 |
2750161748Scperciva	    tr '|' ' ' |
2751161748Scperciva	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
2752161748Scperciva		case ${TYPE} in
2753161748Scperciva		d)
2754161748Scperciva			# Create a directory
2755161748Scperciva			install -d -o ${OWNER} -g ${GROUP}		\
2756161748Scperciva			    -m ${PERM} ${BASEDIR}/${FPATH}
2757161748Scperciva			;;
2758161748Scperciva		f)
2759161748Scperciva			if [ -z "${LINK}" ]; then
2760161748Scperciva				# Create a file, without setting flags.
2761161748Scperciva				gunzip < files/${HASH}.gz > ${HASH}
2762161748Scperciva				install -S -o ${OWNER} -g ${GROUP}	\
2763161748Scperciva				    -m ${PERM} ${HASH} ${BASEDIR}/${FPATH}
2764161748Scperciva				rm ${HASH}
2765161748Scperciva			else
2766161748Scperciva				# Create a hard link.
2767169603Scperciva				ln -f ${BASEDIR}/${LINK} ${BASEDIR}/${FPATH}
2768161748Scperciva			fi
2769161748Scperciva			;;
2770161748Scperciva		L)
2771161748Scperciva			# Create a symlink
2772161748Scperciva			ln -sfh ${HASH} ${BASEDIR}/${FPATH}
2773161748Scperciva			;;
2774161748Scperciva		esac
2775161748Scperciva	    done
2776161748Scperciva
2777161748Scperciva	# Perform a second pass, adding file flags.
2778161748Scperciva	tr '|' ' ' < $1 |
2779161748Scperciva	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
2780161748Scperciva		if [ ${TYPE} = "f" ] &&
2781161748Scperciva		    ! [ ${FLAGS} = "0" ]; then
2782161748Scperciva			chflags ${FLAGS} ${BASEDIR}/${FPATH}
2783161748Scperciva		fi
2784161748Scperciva	    done
2785161748Scperciva}
2786161748Scperciva
2787161748Scperciva# Remove files which we want to delete
2788161748Scpercivainstall_delete () {
2789161748Scperciva	# Generate list of new files
2790161748Scperciva	cut -f 1 -d '|' < $2 |
2791161748Scperciva	    sort > newfiles
2792161748Scperciva
2793161748Scperciva	# Generate subindex of old files we want to nuke
2794161748Scperciva	sort -k 1,1 -t '|' $1 |
2795161748Scperciva	    join -t '|' -v 1 - newfiles |
2796164600Scperciva	    sort -r -k 1,1 -t '|' |
2797161748Scperciva	    cut -f 1,2 -d '|' |
2798161748Scperciva	    tr '|' ' ' > killfiles
2799161748Scperciva
2800161748Scperciva	# Remove the offending bits
2801161748Scperciva	while read FPATH TYPE; do
2802161748Scperciva		case ${TYPE} in
2803161748Scperciva		d)
2804161748Scperciva			rmdir ${BASEDIR}/${FPATH}
2805161748Scperciva			;;
2806161748Scperciva		f)
2807161748Scperciva			rm ${BASEDIR}/${FPATH}
2808161748Scperciva			;;
2809161748Scperciva		L)
2810161748Scperciva			rm ${BASEDIR}/${FPATH}
2811161748Scperciva			;;
2812161748Scperciva		esac
2813161748Scperciva	done < killfiles
2814161748Scperciva
2815161748Scperciva	# Clean up
2816161748Scperciva	rm newfiles killfiles
2817161748Scperciva}
2818161748Scperciva
2819173564Scperciva# Install new files, delete old files, and update linker.hints
2820173564Scpercivainstall_files () {
2821173564Scperciva	# If we haven't already dealt with the kernel, deal with it.
2822173564Scperciva	if ! [ -f $1/kerneldone ]; then
2823173564Scperciva		grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
2824173564Scperciva		grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
2825173564Scperciva
2826196392Ssimon		# Backup current kernel before installing a new one
2827196392Ssimon		backup_kernel || return 1
2828196392Ssimon
2829173564Scperciva		# Install new files
2830173564Scperciva		install_from_index INDEX-NEW || return 1
2831173564Scperciva
2832173564Scperciva		# Remove files which need to be deleted
2833173564Scperciva		install_delete INDEX-OLD INDEX-NEW || return 1
2834173564Scperciva
2835173564Scperciva		# Update linker.hints if necessary
2836173564Scperciva		if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2837279556Sthomas			kldxref -R ${BASEDIR}/boot/ 2>/dev/null
2838173564Scperciva		fi
2839173564Scperciva
2840173564Scperciva		# We've finished updating the kernel.
2841173564Scperciva		touch $1/kerneldone
2842173564Scperciva
2843173564Scperciva		# Do we need to ask for a reboot now?
2844173564Scperciva		if [ -f $1/kernelfirst ] &&
2845173564Scperciva		    [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2846173564Scperciva			cat <<-EOF
2847173564Scperciva
2848173564ScpercivaKernel updates have been installed.  Please reboot and run
2849173564Scperciva"$0 install" again to finish installing updates.
2850173564Scperciva			EOF
2851173564Scperciva			exit 0
2852173564Scperciva		fi
2853161748Scperciva	fi
2854173564Scperciva
2855173564Scperciva	# If we haven't already dealt with the world, deal with it.
2856173564Scperciva	if ! [ -f $1/worlddone ]; then
2857257153Scperciva		# Create any necessary directories first
2858257153Scperciva		grep -vE '^/boot/' $1/INDEX-NEW |
2859257153Scperciva		    grep -E '^[^|]+\|d\|' > INDEX-NEW
2860257153Scperciva		install_from_index INDEX-NEW || return 1
2861257153Scperciva
2862278443Sbrooks		# Install new runtime linker
2863278443Sbrooks		grep -vE '^/boot/' $1/INDEX-NEW |
2864278443Sbrooks		    grep -vE '^[^|]+\|d\|' |
2865278443Sbrooks		    grep -E '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2866278443Sbrooks		install_from_index INDEX-NEW || return 1
2867278443Sbrooks
2868173564Scperciva		# Install new shared libraries next
2869173564Scperciva		grep -vE '^/boot/' $1/INDEX-NEW |
2870257153Scperciva		    grep -vE '^[^|]+\|d\|' |
2871278443Sbrooks		    grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' |
2872257153Scperciva		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2873173564Scperciva		install_from_index INDEX-NEW || return 1
2874173564Scperciva
2875173564Scperciva		# Deal with everything else
2876173564Scperciva		grep -vE '^/boot/' $1/INDEX-OLD |
2877257153Scperciva		    grep -vE '^[^|]+\|d\|' |
2878278443Sbrooks		    grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' |
2879257153Scperciva		    grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD
2880173564Scperciva		grep -vE '^/boot/' $1/INDEX-NEW |
2881257153Scperciva		    grep -vE '^[^|]+\|d\|' |
2882278443Sbrooks		    grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' |
2883257153Scperciva		    grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2884173564Scperciva		install_from_index INDEX-NEW || return 1
2885173564Scperciva		install_delete INDEX-OLD INDEX-NEW || return 1
2886173564Scperciva
2887173564Scperciva		# Rebuild /etc/spwd.db and /etc/pwd.db if necessary.
2888279556Sthomas		if [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/spwd.db ] ||
2889279556Sthomas		    [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/pwd.db ]; then
2890279556Sthomas			pwd_mkdb -d ${BASEDIR}/etc ${BASEDIR}/etc/master.passwd
2891173564Scperciva		fi
2892173564Scperciva
2893173564Scperciva		# Rebuild /etc/login.conf.db if necessary.
2894279556Sthomas		if [ ${BASEDIR}/etc/login.conf -nt ${BASEDIR}/etc/login.conf.db ]; then
2895279556Sthomas			cap_mkdb ${BASEDIR}/etc/login.conf
2896173564Scperciva		fi
2897173564Scperciva
2898173564Scperciva		# We've finished installing the world and deleting old files
2899173564Scperciva		# which are not shared libraries.
2900173564Scperciva		touch $1/worlddone
2901173564Scperciva
2902173564Scperciva		# Do we need to ask the user to portupgrade now?
2903173564Scperciva		grep -vE '^/boot/' $1/INDEX-NEW |
2904257153Scperciva		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' |
2905173564Scperciva		    cut -f 1 -d '|' |
2906173564Scperciva		    sort > newfiles
2907173564Scperciva		if grep -vE '^/boot/' $1/INDEX-OLD |
2908257153Scperciva		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' |
2909173564Scperciva		    cut -f 1 -d '|' |
2910173564Scperciva		    sort |
2911173564Scperciva		    join -v 1 - newfiles |
2912173564Scperciva		    grep -q .; then
2913173564Scperciva			cat <<-EOF
2914173564Scperciva
2915173564ScpercivaCompleting this upgrade requires removing old shared object files.
2916173564ScpercivaPlease rebuild all installed 3rd party software (e.g., programs
2917173564Scpercivainstalled from the ports tree) and then run "$0 install"
2918173564Scpercivaagain to finish installing updates.
2919173564Scperciva			EOF
2920173564Scperciva			rm newfiles
2921173564Scperciva			exit 0
2922173564Scperciva		fi
2923173564Scperciva		rm newfiles
2924173564Scperciva	fi
2925173564Scperciva
2926173564Scperciva	# Remove old shared libraries
2927173564Scperciva	grep -vE '^/boot/' $1/INDEX-NEW |
2928257153Scperciva	    grep -vE '^[^|]+\|d\|' |
2929257153Scperciva	    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2930173564Scperciva	grep -vE '^/boot/' $1/INDEX-OLD |
2931257153Scperciva	    grep -vE '^[^|]+\|d\|' |
2932257153Scperciva	    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD
2933173564Scperciva	install_delete INDEX-OLD INDEX-NEW || return 1
2934173564Scperciva
2935257153Scperciva	# Remove old directories
2936258723Sdelphij	grep -vE '^/boot/' $1/INDEX-NEW |
2937258723Sdelphij	    grep -E '^[^|]+\|d\|' > INDEX-NEW
2938257153Scperciva	grep -vE '^/boot/' $1/INDEX-OLD |
2939257153Scperciva	    grep -E '^[^|]+\|d\|' > INDEX-OLD
2940257153Scperciva	install_delete INDEX-OLD INDEX-NEW || return 1
2941257153Scperciva
2942173564Scperciva	# Remove temporary files
2943173564Scperciva	rm INDEX-OLD INDEX-NEW
2944161748Scperciva}
2945161748Scperciva
2946161748Scperciva# Rearrange bits to allow the installed updates to be rolled back
2947161748Scpercivainstall_setup_rollback () {
2948173564Scperciva	# Remove the "reboot after installing kernel", "kernel updated", and
2949173564Scperciva	# "finished installing the world" flags if present -- they are
2950173564Scperciva	# irrelevant when rolling back updates.
2951173564Scperciva	if [ -f ${BDHASH}-install/kernelfirst ]; then
2952173564Scperciva		rm ${BDHASH}-install/kernelfirst
2953173564Scperciva		rm ${BDHASH}-install/kerneldone
2954173564Scperciva	fi
2955173564Scperciva	if [ -f ${BDHASH}-install/worlddone ]; then
2956173564Scperciva		rm ${BDHASH}-install/worlddone
2957173564Scperciva	fi
2958173564Scperciva
2959161748Scperciva	if [ -L ${BDHASH}-rollback ]; then
2960161748Scperciva		mv ${BDHASH}-rollback ${BDHASH}-install/rollback
2961161748Scperciva	fi
2962161748Scperciva
2963161748Scperciva	mv ${BDHASH}-install ${BDHASH}-rollback
2964161748Scperciva}
2965161748Scperciva
2966161748Scperciva# Actually install updates
2967161748Scpercivainstall_run () {
2968161748Scperciva	echo -n "Installing updates..."
2969161748Scperciva
2970161748Scperciva	# Make sure we have all the files we should have
2971161748Scperciva	install_verify ${BDHASH}-install/INDEX-OLD	\
2972161748Scperciva	    ${BDHASH}-install/INDEX-NEW || return 1
2973161748Scperciva
2974161748Scperciva	# Remove system immutable flag from files
2975161748Scperciva	install_unschg ${BDHASH}-install/INDEX-OLD	\
2976161748Scperciva	    ${BDHASH}-install/INDEX-NEW || return 1
2977161748Scperciva
2978173564Scperciva	# Install new files, delete old files, and update linker.hints
2979173564Scperciva	install_files ${BDHASH}-install || return 1
2980161748Scperciva
2981161748Scperciva	# Rearrange bits to allow the installed updates to be rolled back
2982161748Scperciva	install_setup_rollback
2983161748Scperciva
2984161748Scperciva	echo " done."
2985161748Scperciva}
2986161748Scperciva
2987161748Scperciva# Rearrange bits to allow the previous set of updates to be rolled back next.
2988161748Scpercivarollback_setup_rollback () {
2989161748Scperciva	if [ -L ${BDHASH}-rollback/rollback ]; then
2990161748Scperciva		mv ${BDHASH}-rollback/rollback rollback-tmp
2991161748Scperciva		rm -r ${BDHASH}-rollback/
2992161748Scperciva		rm ${BDHASH}-rollback
2993161748Scperciva		mv rollback-tmp ${BDHASH}-rollback
2994161748Scperciva	else
2995161748Scperciva		rm -r ${BDHASH}-rollback/
2996161748Scperciva		rm ${BDHASH}-rollback
2997161748Scperciva	fi
2998161748Scperciva}
2999161748Scperciva
3000173564Scperciva# Install old files, delete new files, and update linker.hints
3001173564Scpercivarollback_files () {
3002173671Scperciva	# Install old shared library files which don't have the same path as
3003173671Scperciva	# a new shared library file.
3004173671Scperciva	grep -vE '^/boot/' $1/INDEX-NEW |
3005177601Scperciva	    grep -E '/lib/.*\.so\.[0-9]+\|' |
3006173671Scperciva	    cut -f 1 -d '|' |
3007173671Scperciva	    sort > INDEX-NEW.libs.flist
3008173564Scperciva	grep -vE '^/boot/' $1/INDEX-OLD |
3009177601Scperciva	    grep -E '/lib/.*\.so\.[0-9]+\|' |
3010173671Scperciva	    sort -k 1,1 -t '|' - |
3011173671Scperciva	    join -t '|' -v 1 - INDEX-NEW.libs.flist > INDEX-OLD
3012173564Scperciva	install_from_index INDEX-OLD || return 1
3013173564Scperciva
3014173564Scperciva	# Deal with files which are neither kernel nor shared library
3015173564Scperciva	grep -vE '^/boot/' $1/INDEX-OLD |
3016177601Scperciva	    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
3017173564Scperciva	grep -vE '^/boot/' $1/INDEX-NEW |
3018177601Scperciva	    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
3019173564Scperciva	install_from_index INDEX-OLD || return 1
3020173564Scperciva	install_delete INDEX-NEW INDEX-OLD || return 1
3021173564Scperciva
3022173671Scperciva	# Install any old shared library files which we didn't install above.
3023173671Scperciva	grep -vE '^/boot/' $1/INDEX-OLD |
3024177601Scperciva	    grep -E '/lib/.*\.so\.[0-9]+\|' |
3025173671Scperciva	    sort -k 1,1 -t '|' - |
3026173671Scperciva	    join -t '|' - INDEX-NEW.libs.flist > INDEX-OLD
3027173671Scperciva	install_from_index INDEX-OLD || return 1
3028173671Scperciva
3029173564Scperciva	# Delete unneeded shared library files
3030173564Scperciva	grep -vE '^/boot/' $1/INDEX-OLD |
3031177601Scperciva	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
3032173564Scperciva	grep -vE '^/boot/' $1/INDEX-NEW |
3033177601Scperciva	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
3034173564Scperciva	install_delete INDEX-NEW INDEX-OLD || return 1
3035173564Scperciva
3036173564Scperciva	# Deal with kernel files
3037173564Scperciva	grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
3038173564Scperciva	grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
3039173564Scperciva	install_from_index INDEX-OLD || return 1
3040173564Scperciva	install_delete INDEX-NEW INDEX-OLD || return 1
3041173564Scperciva	if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
3042173564Scperciva		kldxref -R /boot/ 2>/dev/null
3043173564Scperciva	fi
3044173564Scperciva
3045173564Scperciva	# Remove temporary files
3046173672Scperciva	rm INDEX-OLD INDEX-NEW INDEX-NEW.libs.flist
3047173564Scperciva}
3048173564Scperciva
3049161748Scperciva# Actually rollback updates
3050161748Scpercivarollback_run () {
3051161748Scperciva	echo -n "Uninstalling updates..."
3052161748Scperciva
3053161748Scperciva	# If there are updates waiting to be installed, remove them; we
3054161748Scperciva	# want the user to re-run 'fetch' after rolling back updates.
3055161748Scperciva	if [ -L ${BDHASH}-install ]; then
3056161748Scperciva		rm -r ${BDHASH}-install/
3057161748Scperciva		rm ${BDHASH}-install
3058161748Scperciva	fi
3059161748Scperciva
3060161748Scperciva	# Make sure we have all the files we should have
3061161748Scperciva	install_verify ${BDHASH}-rollback/INDEX-NEW	\
3062161748Scperciva	    ${BDHASH}-rollback/INDEX-OLD || return 1
3063161748Scperciva
3064161748Scperciva	# Remove system immutable flag from files
3065161748Scperciva	install_unschg ${BDHASH}-rollback/INDEX-NEW	\
3066161748Scperciva	    ${BDHASH}-rollback/INDEX-OLD || return 1
3067161748Scperciva
3068173564Scperciva	# Install old files, delete new files, and update linker.hints
3069173564Scperciva	rollback_files ${BDHASH}-rollback || return 1
3070161748Scperciva
3071161748Scperciva	# Remove the rollback directory and the symlink pointing to it; and
3072161748Scperciva	# rearrange bits to allow the previous set of updates to be rolled
3073161748Scperciva	# back next.
3074161748Scperciva	rollback_setup_rollback
3075161748Scperciva
3076161748Scperciva	echo " done."
3077161748Scperciva}
3078161748Scperciva
3079181142Scperciva# Compare INDEX-ALL and INDEX-PRESENT and print warnings about differences.
3080181142ScpercivaIDS_compare () {
3081181425Scperciva	# Get all the lines which mismatch in something other than file
3082181425Scperciva	# flags.  We ignore file flags because sysinstall doesn't seem to
3083181425Scperciva	# set them when it installs FreeBSD; warning about these adds a
3084181425Scperciva	# very large amount of noise.
3085181425Scperciva	cut -f 1-5,7-8 -d '|' $1 > $1.noflags
3086181425Scperciva	sort -k 1,1 -t '|' $1.noflags > $1.sorted
3087181425Scperciva	cut -f 1-5,7-8 -d '|' $2 |
3088181425Scperciva	    comm -13 $1.noflags - |
3089181425Scperciva	    fgrep -v '|-|||||' |
3090181142Scperciva	    sort -k 1,1 -t '|' |
3091181142Scperciva	    join -t '|' $1.sorted - > INDEX-NOTMATCHING
3092181142Scperciva
3093181142Scperciva	# Ignore files which match IDSIGNOREPATHS.
3094181142Scperciva	for X in ${IDSIGNOREPATHS}; do
3095181142Scperciva		grep -E "^${X}" INDEX-NOTMATCHING
3096181142Scperciva	done |
3097181142Scperciva	    sort -u |
3098181142Scperciva	    comm -13 - INDEX-NOTMATCHING > INDEX-NOTMATCHING.tmp
3099181142Scperciva	mv INDEX-NOTMATCHING.tmp INDEX-NOTMATCHING
3100181142Scperciva
3101181142Scperciva	# Go through the lines and print warnings.
3102284939Sdelphij	local IFS='|'
3103284939Sdelphij	while read FPATH TYPE OWNER GROUP PERM HASH LINK P_TYPE P_OWNER P_GROUP P_PERM P_HASH P_LINK; do
3104181142Scperciva		# Warn about different object types.
3105181142Scperciva		if ! [ "${TYPE}" = "${P_TYPE}" ]; then
3106181142Scperciva			echo -n "${FPATH} is a "
3107181142Scperciva			case "${P_TYPE}" in
3108181142Scperciva			f)	echo -n "regular file, "
3109181142Scperciva				;;
3110181142Scperciva			d)	echo -n "directory, "
3111181142Scperciva				;;
3112181142Scperciva			L)	echo -n "symlink, "
3113181142Scperciva				;;
3114181142Scperciva			esac
3115181142Scperciva			echo -n "but should be a "
3116181142Scperciva			case "${TYPE}" in
3117181142Scperciva			f)	echo -n "regular file."
3118181142Scperciva				;;
3119181142Scperciva			d)	echo -n "directory."
3120181142Scperciva				;;
3121181142Scperciva			L)	echo -n "symlink."
3122181142Scperciva				;;
3123181142Scperciva			esac
3124181142Scperciva			echo
3125181142Scperciva
3126181142Scperciva			# Skip other tests, since they don't make sense if
3127181142Scperciva			# we're comparing different object types.
3128181142Scperciva			continue
3129181142Scperciva		fi
3130181142Scperciva
3131181142Scperciva		# Warn about different owners.
3132181142Scperciva		if ! [ "${OWNER}" = "${P_OWNER}" ]; then
3133181142Scperciva			echo -n "${FPATH} is owned by user id ${P_OWNER}, "
3134181142Scperciva			echo "but should be owned by user id ${OWNER}."
3135181142Scperciva		fi
3136181142Scperciva
3137181142Scperciva		# Warn about different groups.
3138181142Scperciva		if ! [ "${GROUP}" = "${P_GROUP}" ]; then
3139181142Scperciva			echo -n "${FPATH} is owned by group id ${P_GROUP}, "
3140181142Scperciva			echo "but should be owned by group id ${GROUP}."
3141181142Scperciva		fi
3142181142Scperciva
3143181142Scperciva		# Warn about different permissions.  We do not warn about
3144181142Scperciva		# different permissions on symlinks, since some archivers
3145181142Scperciva		# don't extract symlink permissions correctly and they are
3146181142Scperciva		# ignored anyway.
3147181142Scperciva		if ! [ "${PERM}" = "${P_PERM}" ] &&
3148181142Scperciva		    ! [ "${TYPE}" = "L" ]; then
3149181142Scperciva			echo -n "${FPATH} has ${P_PERM} permissions, "
3150181142Scperciva			echo "but should have ${PERM} permissions."
3151181142Scperciva		fi
3152181142Scperciva
3153181142Scperciva		# Warn about different file hashes / symlink destinations.
3154181142Scperciva		if ! [ "${HASH}" = "${P_HASH}" ]; then
3155181142Scperciva			if [ "${TYPE}" = "L" ]; then
3156181142Scperciva				echo -n "${FPATH} is a symlink to ${P_HASH}, "
3157181142Scperciva				echo "but should be a symlink to ${HASH}."
3158181142Scperciva			fi
3159181142Scperciva			if [ "${TYPE}" = "f" ]; then
3160181142Scperciva				echo -n "${FPATH} has SHA256 hash ${P_HASH}, "
3161181142Scperciva				echo "but should have SHA256 hash ${HASH}."
3162181142Scperciva			fi
3163181142Scperciva		fi
3164181142Scperciva
3165181142Scperciva		# We don't warn about different hard links, since some
3166181142Scperciva		# some archivers break hard links, and as long as the
3167181142Scperciva		# underlying data is correct they really don't matter.
3168181142Scperciva	done < INDEX-NOTMATCHING
3169181142Scperciva
3170181142Scperciva	# Clean up
3171181425Scperciva	rm $1 $1.noflags $1.sorted $2 INDEX-NOTMATCHING
3172181142Scperciva}
3173181142Scperciva
3174181142Scperciva# Do the work involved in comparing the system to a "known good" index
3175181142ScpercivaIDS_run () {
3176181142Scperciva	workdir_init || return 1
3177181142Scperciva
3178181142Scperciva	# Prepare the mirror list.
3179181142Scperciva	fetch_pick_server_init && fetch_pick_server
3180181142Scperciva
3181181142Scperciva	# Try to fetch the public key until we run out of servers.
3182181142Scperciva	while ! fetch_key; do
3183181142Scperciva		fetch_pick_server || return 1
3184181142Scperciva	done
3185181142Scperciva 
3186181142Scperciva	# Try to fetch the metadata index signature ("tag") until we run
3187181142Scperciva	# out of available servers; and sanity check the downloaded tag.
3188181142Scperciva	while ! fetch_tag; do
3189181142Scperciva		fetch_pick_server || return 1
3190181142Scperciva	done
3191181142Scperciva	fetch_tagsanity || return 1
3192181142Scperciva
3193181142Scperciva	# Fetch INDEX-OLD and INDEX-ALL.
3194181142Scperciva	fetch_metadata INDEX-OLD INDEX-ALL || return 1
3195181142Scperciva
3196181142Scperciva	# Generate filtered INDEX-OLD and INDEX-ALL files containing only
3197181142Scperciva	# the components we want and without anything marked as "Ignore".
3198181142Scperciva	fetch_filter_metadata INDEX-OLD || return 1
3199181142Scperciva	fetch_filter_metadata INDEX-ALL || return 1
3200181142Scperciva
3201181142Scperciva	# Merge the INDEX-OLD and INDEX-ALL files into INDEX-ALL.
3202181142Scperciva	sort INDEX-OLD INDEX-ALL > INDEX-ALL.tmp
3203181142Scperciva	mv INDEX-ALL.tmp INDEX-ALL
3204181142Scperciva	rm INDEX-OLD
3205181142Scperciva
3206181142Scperciva	# Translate /boot/${KERNCONF} to ${KERNELDIR}
3207181142Scperciva	fetch_filter_kernel_names INDEX-ALL ${KERNCONF}
3208181142Scperciva
3209181142Scperciva	# Inspect the system and generate an INDEX-PRESENT file.
3210181142Scperciva	fetch_inspect_system INDEX-ALL INDEX-PRESENT /dev/null || return 1
3211181142Scperciva
3212181142Scperciva	# Compare INDEX-ALL and INDEX-PRESENT and print warnings about any
3213181142Scperciva	# differences.
3214181142Scperciva	IDS_compare INDEX-ALL INDEX-PRESENT
3215181142Scperciva}
3216181142Scperciva
3217161748Scperciva#### Main functions -- call parameter-handling and core functions
3218161748Scperciva
3219161748Scperciva# Using the command line, configuration file, and defaults,
3220161748Scperciva# set all the parameters which are needed later.
3221161748Scpercivaget_params () {
3222161748Scperciva	init_params
3223161748Scperciva	parse_cmdline $@
3224161748Scperciva	parse_conffile
3225161748Scperciva	default_params
3226161748Scperciva}
3227161748Scperciva
3228161748Scperciva# Fetch command.  Make sure that we're being called
3229161748Scperciva# interactively, then run fetch_check_params and fetch_run
3230161748Scpercivacmd_fetch () {
3231282870Sdelphij	if [ ! -t 0 -a $NOTTYOK -eq 0 ]; then
3232161748Scperciva		echo -n "`basename $0` fetch should not "
3233161748Scperciva		echo "be run non-interactively."
3234161748Scperciva		echo "Run `basename $0` cron instead."
3235161748Scperciva		exit 1
3236161748Scperciva	fi
3237161748Scperciva	fetch_check_params
3238161748Scperciva	fetch_run || exit 1
3239161748Scperciva}
3240161748Scperciva
3241161748Scperciva# Cron command.  Make sure the parameters are sensible; wait
3242161748Scperciva# rand(3600) seconds; then fetch updates.  While fetching updates,
3243161748Scperciva# send output to a temporary file; only print that file if the
3244161748Scperciva# fetching failed.
3245161748Scpercivacmd_cron () {
3246161748Scperciva	fetch_check_params
3247161748Scperciva	sleep `jot -r 1 0 3600`
3248161748Scperciva
3249161748Scperciva	TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1
3250161748Scperciva	if ! fetch_run >> ${TMPFILE} ||
3251161748Scperciva	    ! grep -q "No updates needed" ${TMPFILE} ||
3252161748Scperciva	    [ ${VERBOSELEVEL} = "debug" ]; then
3253161748Scperciva		mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE}
3254161748Scperciva	fi
3255161748Scperciva
3256161748Scperciva	rm ${TMPFILE}
3257161748Scperciva}
3258161748Scperciva
3259173564Scperciva# Fetch files for upgrading to a new release.
3260173564Scpercivacmd_upgrade () {
3261173564Scperciva	upgrade_check_params
3262173564Scperciva	upgrade_run || exit 1
3263173564Scperciva}
3264173564Scperciva
3265161748Scperciva# Install downloaded updates.
3266161748Scpercivacmd_install () {
3267161748Scperciva	install_check_params
3268161748Scperciva	install_run || exit 1
3269161748Scperciva}
3270161748Scperciva
3271161748Scperciva# Rollback most recently installed updates.
3272161748Scpercivacmd_rollback () {
3273161748Scperciva	rollback_check_params
3274161748Scperciva	rollback_run || exit 1
3275161748Scperciva}
3276161748Scperciva
3277181142Scperciva# Compare system against a "known good" index.
3278181142Scpercivacmd_IDS () {
3279181142Scperciva	IDS_check_params
3280181142Scperciva	IDS_run || exit 1
3281181142Scperciva}
3282181142Scperciva
3283161748Scperciva#### Entry point
3284161748Scperciva
3285161748Scperciva# Make sure we find utilities from the base system
3286161748Scpercivaexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
3287161748Scperciva
3288217767Sgordon# Set a pager if the user doesn't
3289217767Sgordonif [ -z "$PAGER" ]; then
3290217767Sgordon	PAGER=/usr/bin/more
3291217767Sgordonfi
3292217767Sgordon
3293163564Scperciva# Set LC_ALL in order to avoid problems with character ranges like [A-Z].
3294163564Scpercivaexport LC_ALL=C
3295163564Scperciva
3296161748Scpercivaget_params $@
3297161748Scpercivafor COMMAND in ${COMMANDS}; do
3298161748Scperciva	cmd_${COMMAND}
3299161748Scpercivadone
3300