xref: /freebsd/usr.sbin/freebsd-update/freebsd-update.sh (revision 23d827ef055170d3c43b4db46f9959f10a4217ed)
148ffe56aSColin Percival#!/bin/sh
248ffe56aSColin Percival
348ffe56aSColin Percival#-
42328d598SColin Percival# Copyright 2004-2007 Colin Percival
548ffe56aSColin Percival# All rights reserved
648ffe56aSColin Percival#
748ffe56aSColin Percival# Redistribution and use in source and binary forms, with or without
848ffe56aSColin Percival# modification, are permitted providing that the following conditions
948ffe56aSColin Percival# are met:
1048ffe56aSColin Percival# 1. Redistributions of source code must retain the above copyright
1148ffe56aSColin Percival#    notice, this list of conditions and the following disclaimer.
1248ffe56aSColin Percival# 2. Redistributions in binary form must reproduce the above copyright
1348ffe56aSColin Percival#    notice, this list of conditions and the following disclaimer in the
1448ffe56aSColin Percival#    documentation and/or other materials provided with the distribution.
1548ffe56aSColin Percival#
1648ffe56aSColin Percival# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1748ffe56aSColin Percival# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1848ffe56aSColin Percival# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1948ffe56aSColin Percival# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
2048ffe56aSColin Percival# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2148ffe56aSColin Percival# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2248ffe56aSColin Percival# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2348ffe56aSColin Percival# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2448ffe56aSColin Percival# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
2548ffe56aSColin Percival# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2648ffe56aSColin Percival# POSSIBILITY OF SUCH DAMAGE.
2748ffe56aSColin Percival
2848ffe56aSColin Percival# $FreeBSD$
2948ffe56aSColin Percival
3048ffe56aSColin Percival#### Usage function -- called from command-line handling code.
3148ffe56aSColin Percival
3248ffe56aSColin Percival# Usage instructions.  Options not listed:
3348ffe56aSColin Percival# --debug	-- don't filter output from utilities
3448ffe56aSColin Percival# --no-stats	-- don't show progress statistics while fetching files
3548ffe56aSColin Percivalusage () {
3648ffe56aSColin Percival	cat <<EOF
3748ffe56aSColin Percivalusage: `basename $0` [options] command ... [path]
3848ffe56aSColin Percival
3948ffe56aSColin PercivalOptions:
4048ffe56aSColin Percival  -b basedir   -- Operate on a system mounted at basedir
4148ffe56aSColin Percival                  (default: /)
4248ffe56aSColin Percival  -d workdir   -- Store working files in workdir
4348ffe56aSColin Percival                  (default: /var/db/freebsd-update/)
4448ffe56aSColin Percival  -f conffile  -- Read configuration options from conffile
4548ffe56aSColin Percival                  (default: /etc/freebsd-update.conf)
4648ffe56aSColin Percival  -k KEY       -- Trust an RSA key with SHA256 hash of KEY
47db6b0a61SColin Percival  -r release   -- Target for upgrade (e.g., 6.2-RELEASE)
4848ffe56aSColin Percival  -s server    -- Server from which to fetch updates
4948ffe56aSColin Percival                  (default: update.FreeBSD.org)
5048ffe56aSColin Percival  -t address   -- Mail output of cron command, if any, to address
5148ffe56aSColin Percival                  (default: root)
5248ffe56aSColin PercivalCommands:
5348ffe56aSColin Percival  fetch        -- Fetch updates from server
5448ffe56aSColin Percival  cron         -- Sleep rand(3600) seconds, fetch updates, and send an
5548ffe56aSColin Percival                  email if updates were found
56db6b0a61SColin Percival  upgrade      -- Fetch upgrades to FreeBSD version specified via -r option
57db6b0a61SColin Percival  install      -- Install downloaded updates or upgrades
5848ffe56aSColin Percival  rollback     -- Uninstall most recently installed updates
5908e23beeSColin Percival  IDS          -- Compare the system against an index of "known good" files.
6048ffe56aSColin PercivalEOF
6148ffe56aSColin Percival	exit 0
6248ffe56aSColin Percival}
6348ffe56aSColin Percival
6448ffe56aSColin Percival#### Configuration processing functions
6548ffe56aSColin Percival
6648ffe56aSColin Percival#-
6748ffe56aSColin Percival# Configuration options are set in the following order of priority:
6848ffe56aSColin Percival# 1. Command line options
6948ffe56aSColin Percival# 2. Configuration file options
7048ffe56aSColin Percival# 3. Default options
7148ffe56aSColin Percival# In addition, certain options (e.g., IgnorePaths) can be specified multiple
7248ffe56aSColin Percival# times and (as long as these are all in the same place, e.g., inside the
7348ffe56aSColin Percival# configuration file) they will accumulate.  Finally, because the path to the
7448ffe56aSColin Percival# configuration file can be specified at the command line, the entire command
7548ffe56aSColin Percival# line must be processed before we start reading the configuration file.
7648ffe56aSColin Percival#
7748ffe56aSColin Percival# Sound like a mess?  It is.  Here's how we handle this:
7848ffe56aSColin Percival# 1. Initialize CONFFILE and all the options to "".
7948ffe56aSColin Percival# 2. Process the command line.  Throw an error if a non-accumulating option
8048ffe56aSColin Percival#    is specified twice.
8148ffe56aSColin Percival# 3. If CONFFILE is "", set CONFFILE to /etc/freebsd-update.conf .
8248ffe56aSColin Percival# 4. For all the configuration options X, set X_saved to X.
8348ffe56aSColin Percival# 5. Initialize all the options to "".
8448ffe56aSColin Percival# 6. Read CONFFILE line by line, parsing options.
8548ffe56aSColin Percival# 7. For each configuration option X, set X to X_saved iff X_saved is not "".
8648ffe56aSColin Percival# 8. Repeat steps 4-7, except setting options to their default values at (6).
8748ffe56aSColin Percival
8848ffe56aSColin PercivalCONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE
8948ffe56aSColin Percival    KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED
9008e23beeSColin Percival    BASEDIR VERBOSELEVEL TARGETRELEASE STRICTCOMPONENTS MERGECHANGES
9123d827efSSimon L. B. Nielsen    IDSIGNOREPATHS BACKUPKERNEL BACKUPKERNELDIR BACKUPKERNELSYMBOLFILES"
9248ffe56aSColin Percival
9348ffe56aSColin Percival# Set all the configuration options to "".
9448ffe56aSColin Percivalnullconfig () {
9548ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
9648ffe56aSColin Percival		eval ${X}=""
9748ffe56aSColin Percival	done
9848ffe56aSColin Percival}
9948ffe56aSColin Percival
10048ffe56aSColin Percival# For each configuration option X, set X_saved to X.
10148ffe56aSColin Percivalsaveconfig () {
10248ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
10348ffe56aSColin Percival		eval ${X}_saved=\$${X}
10448ffe56aSColin Percival	done
10548ffe56aSColin Percival}
10648ffe56aSColin Percival
10748ffe56aSColin Percival# For each configuration option X, set X to X_saved if X_saved is not "".
10848ffe56aSColin Percivalmergeconfig () {
10948ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
11048ffe56aSColin Percival		eval _=\$${X}_saved
11148ffe56aSColin Percival		if ! [ -z "${_}" ]; then
11248ffe56aSColin Percival			eval ${X}=\$${X}_saved
11348ffe56aSColin Percival		fi
11448ffe56aSColin Percival	done
11548ffe56aSColin Percival}
11648ffe56aSColin Percival
11748ffe56aSColin Percival# Set the trusted keyprint.
11848ffe56aSColin Percivalconfig_KeyPrint () {
11948ffe56aSColin Percival	if [ -z ${KEYPRINT} ]; then
12048ffe56aSColin Percival		KEYPRINT=$1
12148ffe56aSColin Percival	else
12248ffe56aSColin Percival		return 1
12348ffe56aSColin Percival	fi
12448ffe56aSColin Percival}
12548ffe56aSColin Percival
12648ffe56aSColin Percival# Set the working directory.
12748ffe56aSColin Percivalconfig_WorkDir () {
12848ffe56aSColin Percival	if [ -z ${WORKDIR} ]; then
12948ffe56aSColin Percival		WORKDIR=$1
13048ffe56aSColin Percival	else
13148ffe56aSColin Percival		return 1
13248ffe56aSColin Percival	fi
13348ffe56aSColin Percival}
13448ffe56aSColin Percival
13548ffe56aSColin Percival# Set the name of the server (pool) from which to fetch updates
13648ffe56aSColin Percivalconfig_ServerName () {
13748ffe56aSColin Percival	if [ -z ${SERVERNAME} ]; then
13848ffe56aSColin Percival		SERVERNAME=$1
13948ffe56aSColin Percival	else
14048ffe56aSColin Percival		return 1
14148ffe56aSColin Percival	fi
14248ffe56aSColin Percival}
14348ffe56aSColin Percival
14448ffe56aSColin Percival# Set the address to which 'cron' output will be mailed.
14548ffe56aSColin Percivalconfig_MailTo () {
14648ffe56aSColin Percival	if [ -z ${MAILTO} ]; then
14748ffe56aSColin Percival		MAILTO=$1
14848ffe56aSColin Percival	else
14948ffe56aSColin Percival		return 1
15048ffe56aSColin Percival	fi
15148ffe56aSColin Percival}
15248ffe56aSColin Percival
15348ffe56aSColin Percival# Set whether FreeBSD Update is allowed to add files (or directories, or
15448ffe56aSColin Percival# symlinks) which did not previously exist.
15548ffe56aSColin Percivalconfig_AllowAdd () {
15648ffe56aSColin Percival	if [ -z ${ALLOWADD} ]; then
15748ffe56aSColin Percival		case $1 in
15848ffe56aSColin Percival		[Yy][Ee][Ss])
15948ffe56aSColin Percival			ALLOWADD=yes
16048ffe56aSColin Percival			;;
16148ffe56aSColin Percival		[Nn][Oo])
16248ffe56aSColin Percival			ALLOWADD=no
16348ffe56aSColin Percival			;;
16448ffe56aSColin Percival		*)
16548ffe56aSColin Percival			return 1
16648ffe56aSColin Percival			;;
16748ffe56aSColin Percival		esac
16848ffe56aSColin Percival	else
16948ffe56aSColin Percival		return 1
17048ffe56aSColin Percival	fi
17148ffe56aSColin Percival}
17248ffe56aSColin Percival
17348ffe56aSColin Percival# Set whether FreeBSD Update is allowed to remove files/directories/symlinks.
17448ffe56aSColin Percivalconfig_AllowDelete () {
17548ffe56aSColin Percival	if [ -z ${ALLOWDELETE} ]; then
17648ffe56aSColin Percival		case $1 in
17748ffe56aSColin Percival		[Yy][Ee][Ss])
17848ffe56aSColin Percival			ALLOWDELETE=yes
17948ffe56aSColin Percival			;;
18048ffe56aSColin Percival		[Nn][Oo])
18148ffe56aSColin Percival			ALLOWDELETE=no
18248ffe56aSColin Percival			;;
18348ffe56aSColin Percival		*)
18448ffe56aSColin Percival			return 1
18548ffe56aSColin Percival			;;
18648ffe56aSColin Percival		esac
18748ffe56aSColin Percival	else
18848ffe56aSColin Percival		return 1
18948ffe56aSColin Percival	fi
19048ffe56aSColin Percival}
19148ffe56aSColin Percival
19248ffe56aSColin Percival# Set whether FreeBSD Update should keep existing inode ownership,
19348ffe56aSColin Percival# permissions, and flags, in the event that they have been modified locally
19448ffe56aSColin Percival# after the release.
19548ffe56aSColin Percivalconfig_KeepModifiedMetadata () {
19648ffe56aSColin Percival	if [ -z ${KEEPMODIFIEDMETADATA} ]; then
19748ffe56aSColin Percival		case $1 in
19848ffe56aSColin Percival		[Yy][Ee][Ss])
19948ffe56aSColin Percival			KEEPMODIFIEDMETADATA=yes
20048ffe56aSColin Percival			;;
20148ffe56aSColin Percival		[Nn][Oo])
20248ffe56aSColin Percival			KEEPMODIFIEDMETADATA=no
20348ffe56aSColin Percival			;;
20448ffe56aSColin Percival		*)
20548ffe56aSColin Percival			return 1
20648ffe56aSColin Percival			;;
20748ffe56aSColin Percival		esac
20848ffe56aSColin Percival	else
20948ffe56aSColin Percival		return 1
21048ffe56aSColin Percival	fi
21148ffe56aSColin Percival}
21248ffe56aSColin Percival
21348ffe56aSColin Percival# Add to the list of components which should be kept updated.
21448ffe56aSColin Percivalconfig_Components () {
21548ffe56aSColin Percival	for C in $@; do
21648ffe56aSColin Percival		COMPONENTS="${COMPONENTS} ${C}"
21748ffe56aSColin Percival	done
21848ffe56aSColin Percival}
21948ffe56aSColin Percival
22048ffe56aSColin Percival# Add to the list of paths under which updates will be ignored.
22148ffe56aSColin Percivalconfig_IgnorePaths () {
22248ffe56aSColin Percival	for C in $@; do
22348ffe56aSColin Percival		IGNOREPATHS="${IGNOREPATHS} ${C}"
22448ffe56aSColin Percival	done
22548ffe56aSColin Percival}
22648ffe56aSColin Percival
22708e23beeSColin Percival# Add to the list of paths which IDS should ignore.
22808e23beeSColin Percivalconfig_IDSIgnorePaths () {
22908e23beeSColin Percival	for C in $@; do
23008e23beeSColin Percival		IDSIGNOREPATHS="${IDSIGNOREPATHS} ${C}"
23108e23beeSColin Percival	done
23208e23beeSColin Percival}
23308e23beeSColin Percival
23448ffe56aSColin Percival# Add to the list of paths within which updates will be performed only if the
23548ffe56aSColin Percival# file on disk has not been modified locally.
23648ffe56aSColin Percivalconfig_UpdateIfUnmodified () {
23748ffe56aSColin Percival	for C in $@; do
23848ffe56aSColin Percival		UPDATEIFUNMODIFIED="${UPDATEIFUNMODIFIED} ${C}"
23948ffe56aSColin Percival	done
24048ffe56aSColin Percival}
24148ffe56aSColin Percival
242db6b0a61SColin Percival# Add to the list of paths within which updates to text files will be merged
243db6b0a61SColin Percival# instead of overwritten.
244db6b0a61SColin Percivalconfig_MergeChanges () {
245db6b0a61SColin Percival	for C in $@; do
246db6b0a61SColin Percival		MERGECHANGES="${MERGECHANGES} ${C}"
247db6b0a61SColin Percival	done
248db6b0a61SColin Percival}
249db6b0a61SColin Percival
25048ffe56aSColin Percival# Work on a FreeBSD installation mounted under $1
25148ffe56aSColin Percivalconfig_BaseDir () {
25248ffe56aSColin Percival	if [ -z ${BASEDIR} ]; then
25348ffe56aSColin Percival		BASEDIR=$1
25448ffe56aSColin Percival	else
25548ffe56aSColin Percival		return 1
25648ffe56aSColin Percival	fi
25748ffe56aSColin Percival}
25848ffe56aSColin Percival
259db6b0a61SColin Percival# When fetching upgrades, should we assume the user wants exactly the
260db6b0a61SColin Percival# components listed in COMPONENTS, rather than trying to guess based on
261db6b0a61SColin Percival# what's currently installed?
262db6b0a61SColin Percivalconfig_StrictComponents () {
263db6b0a61SColin Percival	if [ -z ${STRICTCOMPONENTS} ]; then
264db6b0a61SColin Percival		case $1 in
265db6b0a61SColin Percival		[Yy][Ee][Ss])
266db6b0a61SColin Percival			STRICTCOMPONENTS=yes
267db6b0a61SColin Percival			;;
268db6b0a61SColin Percival		[Nn][Oo])
269db6b0a61SColin Percival			STRICTCOMPONENTS=no
270db6b0a61SColin Percival			;;
271db6b0a61SColin Percival		*)
272db6b0a61SColin Percival			return 1
273db6b0a61SColin Percival			;;
274db6b0a61SColin Percival		esac
275db6b0a61SColin Percival	else
276db6b0a61SColin Percival		return 1
277db6b0a61SColin Percival	fi
278db6b0a61SColin Percival}
279db6b0a61SColin Percival
280db6b0a61SColin Percival# Upgrade to FreeBSD $1
281db6b0a61SColin Percivalconfig_TargetRelease () {
282db6b0a61SColin Percival	if [ -z ${TARGETRELEASE} ]; then
283db6b0a61SColin Percival		TARGETRELEASE=$1
284db6b0a61SColin Percival	else
285db6b0a61SColin Percival		return 1
286db6b0a61SColin Percival	fi
287db6b0a61SColin Percival}
288db6b0a61SColin Percival
28948ffe56aSColin Percival# Define what happens to output of utilities
29048ffe56aSColin Percivalconfig_VerboseLevel () {
29148ffe56aSColin Percival	if [ -z ${VERBOSELEVEL} ]; then
29248ffe56aSColin Percival		case $1 in
29348ffe56aSColin Percival		[Dd][Ee][Bb][Uu][Gg])
29448ffe56aSColin Percival			VERBOSELEVEL=debug
29548ffe56aSColin Percival			;;
29648ffe56aSColin Percival		[Nn][Oo][Ss][Tt][Aa][Tt][Ss])
29748ffe56aSColin Percival			VERBOSELEVEL=nostats
29848ffe56aSColin Percival			;;
29948ffe56aSColin Percival		[Ss][Tt][Aa][Tt][Ss])
30048ffe56aSColin Percival			VERBOSELEVEL=stats
30148ffe56aSColin Percival			;;
30248ffe56aSColin Percival		*)
30348ffe56aSColin Percival			return 1
30448ffe56aSColin Percival			;;
30548ffe56aSColin Percival		esac
30648ffe56aSColin Percival	else
30748ffe56aSColin Percival		return 1
30848ffe56aSColin Percival	fi
30948ffe56aSColin Percival}
31048ffe56aSColin Percival
31123d827efSSimon L. B. Nielsenconfig_BackupKernel () {
31223d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNEL} ]; then
31323d827efSSimon L. B. Nielsen		case $1 in
31423d827efSSimon L. B. Nielsen		[Yy][Ee][Ss])
31523d827efSSimon L. B. Nielsen			BACKUPKERNEL=yes
31623d827efSSimon L. B. Nielsen			;;
31723d827efSSimon L. B. Nielsen		[Nn][Oo])
31823d827efSSimon L. B. Nielsen			BACKUPKERNEL=no
31923d827efSSimon L. B. Nielsen			;;
32023d827efSSimon L. B. Nielsen		*)
32123d827efSSimon L. B. Nielsen			return 1
32223d827efSSimon L. B. Nielsen			;;
32323d827efSSimon L. B. Nielsen		esac
32423d827efSSimon L. B. Nielsen	else
32523d827efSSimon L. B. Nielsen		return 1
32623d827efSSimon L. B. Nielsen	fi
32723d827efSSimon L. B. Nielsen}
32823d827efSSimon L. B. Nielsen
32923d827efSSimon L. B. Nielsenconfig_BackupKernelDir () {
33023d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNELDIR} ]; then
33123d827efSSimon L. B. Nielsen		if [ -z "$1" ]; then
33223d827efSSimon L. B. Nielsen			echo "BackupKernelDir set to empty dir"
33323d827efSSimon L. B. Nielsen			return 1
33423d827efSSimon L. B. Nielsen		fi
33523d827efSSimon L. B. Nielsen
33623d827efSSimon L. B. Nielsen		# We check for some paths which would be extremely odd
33723d827efSSimon L. B. Nielsen		# to use, but which could cause a lot of problems if
33823d827efSSimon L. B. Nielsen		# used.
33923d827efSSimon L. B. Nielsen		case $1 in
34023d827efSSimon L. B. Nielsen		/|/bin|/boot|/etc|/lib|/libexec|/sbin|/usr|/var)
34123d827efSSimon L. B. Nielsen			echo "BackupKernelDir set to invalid path $1"
34223d827efSSimon L. B. Nielsen			return 1
34323d827efSSimon L. B. Nielsen			;;
34423d827efSSimon L. B. Nielsen		/*)
34523d827efSSimon L. B. Nielsen			BACKUPKERNELDIR=$1
34623d827efSSimon L. B. Nielsen			;;
34723d827efSSimon L. B. Nielsen		*)
34823d827efSSimon L. B. Nielsen			echo "BackupKernelDir ($1) is not an absolute path"
34923d827efSSimon L. B. Nielsen			return 1
35023d827efSSimon L. B. Nielsen			;;
35123d827efSSimon L. B. Nielsen		esac
35223d827efSSimon L. B. Nielsen	else
35323d827efSSimon L. B. Nielsen		return 1
35423d827efSSimon L. B. Nielsen	fi
35523d827efSSimon L. B. Nielsen}
35623d827efSSimon L. B. Nielsen
35723d827efSSimon L. B. Nielsenconfig_BackupKernelSymbolFiles () {
35823d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNELSYMBOLFILES} ]; then
35923d827efSSimon L. B. Nielsen		case $1 in
36023d827efSSimon L. B. Nielsen		[Yy][Ee][Ss])
36123d827efSSimon L. B. Nielsen			BACKUPKERNELSYMBOLFILES=yes
36223d827efSSimon L. B. Nielsen			;;
36323d827efSSimon L. B. Nielsen		[Nn][Oo])
36423d827efSSimon L. B. Nielsen			BACKUPKERNELSYMBOLFILES=no
36523d827efSSimon L. B. Nielsen			;;
36623d827efSSimon L. B. Nielsen		*)
36723d827efSSimon L. B. Nielsen			return 1
36823d827efSSimon L. B. Nielsen			;;
36923d827efSSimon L. B. Nielsen		esac
37023d827efSSimon L. B. Nielsen	else
37123d827efSSimon L. B. Nielsen		return 1
37223d827efSSimon L. B. Nielsen	fi
37323d827efSSimon L. B. Nielsen}
37423d827efSSimon L. B. Nielsen
37548ffe56aSColin Percival# Handle one line of configuration
37648ffe56aSColin Percivalconfigline () {
37748ffe56aSColin Percival	if [ $# -eq 0 ]; then
37848ffe56aSColin Percival		return
37948ffe56aSColin Percival	fi
38048ffe56aSColin Percival
38148ffe56aSColin Percival	OPT=$1
38248ffe56aSColin Percival	shift
38348ffe56aSColin Percival	config_${OPT} $@
38448ffe56aSColin Percival}
38548ffe56aSColin Percival
38648ffe56aSColin Percival#### Parameter handling functions.
38748ffe56aSColin Percival
38848ffe56aSColin Percival# Initialize parameters to null, just in case they're
38948ffe56aSColin Percival# set in the environment.
39048ffe56aSColin Percivalinit_params () {
39148ffe56aSColin Percival	# Configration settings
39248ffe56aSColin Percival	nullconfig
39348ffe56aSColin Percival
39448ffe56aSColin Percival	# No configuration file set yet
39548ffe56aSColin Percival	CONFFILE=""
39648ffe56aSColin Percival
39748ffe56aSColin Percival	# No commands specified yet
39848ffe56aSColin Percival	COMMANDS=""
39948ffe56aSColin Percival}
40048ffe56aSColin Percival
40148ffe56aSColin Percival# Parse the command line
40248ffe56aSColin Percivalparse_cmdline () {
40348ffe56aSColin Percival	while [ $# -gt 0 ]; do
40448ffe56aSColin Percival		case "$1" in
40548ffe56aSColin Percival		# Location of configuration file
40648ffe56aSColin Percival		-f)
40748ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi
40848ffe56aSColin Percival			if [ ! -z "${CONFFILE}" ]; then usage; fi
40948ffe56aSColin Percival			shift; CONFFILE="$1"
41048ffe56aSColin Percival			;;
41148ffe56aSColin Percival
41248ffe56aSColin Percival		# Configuration file equivalents
41348ffe56aSColin Percival		-b)
41448ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
41548ffe56aSColin Percival			config_BaseDir $1 || usage
41648ffe56aSColin Percival			;;
41748ffe56aSColin Percival		-d)
41848ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
41948ffe56aSColin Percival			config_WorkDir $1 || usage
42048ffe56aSColin Percival			;;
42148ffe56aSColin Percival		-k)
42248ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
42348ffe56aSColin Percival			config_KeyPrint $1 || usage
42448ffe56aSColin Percival			;;
42548ffe56aSColin Percival		-s)
42648ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
42748ffe56aSColin Percival			config_ServerName $1 || usage
42848ffe56aSColin Percival			;;
429db6b0a61SColin Percival		-r)
430db6b0a61SColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
431db6b0a61SColin Percival			config_TargetRelease $1 || usage
432db6b0a61SColin Percival			;;
43348ffe56aSColin Percival		-t)
43448ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
43548ffe56aSColin Percival			config_MailTo $1 || usage
43648ffe56aSColin Percival			;;
43748ffe56aSColin Percival		-v)
43848ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
43948ffe56aSColin Percival			config_VerboseLevel $1 || usage
44048ffe56aSColin Percival			;;
44148ffe56aSColin Percival
44248ffe56aSColin Percival		# Aliases for "-v debug" and "-v nostats"
44348ffe56aSColin Percival		--debug)
44448ffe56aSColin Percival			config_VerboseLevel debug || usage
44548ffe56aSColin Percival			;;
44648ffe56aSColin Percival		--no-stats)
44748ffe56aSColin Percival			config_VerboseLevel nostats || usage
44848ffe56aSColin Percival			;;
44948ffe56aSColin Percival
45048ffe56aSColin Percival		# Commands
45108e23beeSColin Percival		cron | fetch | upgrade | install | rollback | IDS)
45248ffe56aSColin Percival			COMMANDS="${COMMANDS} $1"
45348ffe56aSColin Percival			;;
45448ffe56aSColin Percival
45548ffe56aSColin Percival		# Anything else is an error
45648ffe56aSColin Percival		*)
45748ffe56aSColin Percival			usage
45848ffe56aSColin Percival			;;
45948ffe56aSColin Percival		esac
46048ffe56aSColin Percival		shift
46148ffe56aSColin Percival	done
46248ffe56aSColin Percival
46348ffe56aSColin Percival	# Make sure we have at least one command
46448ffe56aSColin Percival	if [ -z "${COMMANDS}" ]; then
46548ffe56aSColin Percival		usage
46648ffe56aSColin Percival	fi
46748ffe56aSColin Percival}
46848ffe56aSColin Percival
46948ffe56aSColin Percival# Parse the configuration file
47048ffe56aSColin Percivalparse_conffile () {
47148ffe56aSColin Percival	# If a configuration file was specified on the command line, check
47248ffe56aSColin Percival	# that it exists and is readable.
47348ffe56aSColin Percival	if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
47448ffe56aSColin Percival		echo -n "File does not exist "
47548ffe56aSColin Percival		echo -n "or is not readable: "
47648ffe56aSColin Percival		echo ${CONFFILE}
47748ffe56aSColin Percival		exit 1
47848ffe56aSColin Percival	fi
47948ffe56aSColin Percival
48048ffe56aSColin Percival	# If a configuration file was not specified on the command line,
48148ffe56aSColin Percival	# use the default configuration file path.  If that default does
48248ffe56aSColin Percival	# not exist, give up looking for any configuration.
48348ffe56aSColin Percival	if [ -z "${CONFFILE}" ]; then
48448ffe56aSColin Percival		CONFFILE="/etc/freebsd-update.conf"
48548ffe56aSColin Percival		if [ ! -r "${CONFFILE}" ]; then
48648ffe56aSColin Percival			return
48748ffe56aSColin Percival		fi
48848ffe56aSColin Percival	fi
48948ffe56aSColin Percival
49048ffe56aSColin Percival	# Save the configuration options specified on the command line, and
49148ffe56aSColin Percival	# clear all the options in preparation for reading the config file.
49248ffe56aSColin Percival	saveconfig
49348ffe56aSColin Percival	nullconfig
49448ffe56aSColin Percival
49548ffe56aSColin Percival	# Read the configuration file.  Anything after the first '#' is
49648ffe56aSColin Percival	# ignored, and any blank lines are ignored.
49748ffe56aSColin Percival	L=0
49848ffe56aSColin Percival	while read LINE; do
49948ffe56aSColin Percival		L=$(($L + 1))
50048ffe56aSColin Percival		LINEX=`echo "${LINE}" | cut -f 1 -d '#'`
50148ffe56aSColin Percival		if ! configline ${LINEX}; then
50248ffe56aSColin Percival			echo "Error processing configuration file, line $L:"
50348ffe56aSColin Percival			echo "==> ${LINE}"
50448ffe56aSColin Percival			exit 1
50548ffe56aSColin Percival		fi
50648ffe56aSColin Percival	done < ${CONFFILE}
50748ffe56aSColin Percival
50848ffe56aSColin Percival	# Merge the settings read from the configuration file with those
50948ffe56aSColin Percival	# provided at the command line.
51048ffe56aSColin Percival	mergeconfig
51148ffe56aSColin Percival}
51248ffe56aSColin Percival
51348ffe56aSColin Percival# Provide some default parameters
51448ffe56aSColin Percivaldefault_params () {
51548ffe56aSColin Percival	# Save any parameters already configured, and clear the slate
51648ffe56aSColin Percival	saveconfig
51748ffe56aSColin Percival	nullconfig
51848ffe56aSColin Percival
51948ffe56aSColin Percival	# Default configurations
52048ffe56aSColin Percival	config_WorkDir /var/db/freebsd-update
52148ffe56aSColin Percival	config_MailTo root
52248ffe56aSColin Percival	config_AllowAdd yes
52348ffe56aSColin Percival	config_AllowDelete yes
52448ffe56aSColin Percival	config_KeepModifiedMetadata yes
52548ffe56aSColin Percival	config_BaseDir /
52648ffe56aSColin Percival	config_VerboseLevel stats
527db6b0a61SColin Percival	config_StrictComponents no
52823d827efSSimon L. B. Nielsen	config_BackupKernel yes
52923d827efSSimon L. B. Nielsen	config_BackupKernelDir /boot/kernel.old
53023d827efSSimon L. B. Nielsen	config_BackupKernelSymbolFiles no
53148ffe56aSColin Percival
53248ffe56aSColin Percival	# Merge these defaults into the earlier-configured settings
53348ffe56aSColin Percival	mergeconfig
53448ffe56aSColin Percival}
53548ffe56aSColin Percival
53648ffe56aSColin Percival# Set utility output filtering options, based on ${VERBOSELEVEL}
53748ffe56aSColin Percivalfetch_setup_verboselevel () {
53848ffe56aSColin Percival	case ${VERBOSELEVEL} in
53948ffe56aSColin Percival	debug)
54048ffe56aSColin Percival		QUIETREDIR="/dev/stderr"
54148ffe56aSColin Percival		QUIETFLAG=" "
54248ffe56aSColin Percival		STATSREDIR="/dev/stderr"
54348ffe56aSColin Percival		DDSTATS=".."
54448ffe56aSColin Percival		XARGST="-t"
54548ffe56aSColin Percival		NDEBUG=" "
54648ffe56aSColin Percival		;;
54748ffe56aSColin Percival	nostats)
54848ffe56aSColin Percival		QUIETREDIR=""
54948ffe56aSColin Percival		QUIETFLAG=""
55048ffe56aSColin Percival		STATSREDIR="/dev/null"
55148ffe56aSColin Percival		DDSTATS=".."
55248ffe56aSColin Percival		XARGST=""
55348ffe56aSColin Percival		NDEBUG=""
55448ffe56aSColin Percival		;;
55548ffe56aSColin Percival	stats)
55648ffe56aSColin Percival		QUIETREDIR="/dev/null"
55748ffe56aSColin Percival		QUIETFLAG="-q"
55848ffe56aSColin Percival		STATSREDIR="/dev/stdout"
55948ffe56aSColin Percival		DDSTATS=""
56048ffe56aSColin Percival		XARGST=""
56148ffe56aSColin Percival		NDEBUG="-n"
56248ffe56aSColin Percival		;;
56348ffe56aSColin Percival	esac
56448ffe56aSColin Percival}
56548ffe56aSColin Percival
56648ffe56aSColin Percival# Perform sanity checks and set some final parameters
56748ffe56aSColin Percival# in preparation for fetching files.  Figure out which
56848ffe56aSColin Percival# set of updates should be downloaded: If the user is
56948ffe56aSColin Percival# running *-p[0-9]+, strip off the last part; if the
57048ffe56aSColin Percival# user is running -SECURITY, call it -RELEASE.  Chdir
57148ffe56aSColin Percival# into the working directory.
57248ffe56aSColin Percivalfetch_check_params () {
57348ffe56aSColin Percival	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
57448ffe56aSColin Percival
57548ffe56aSColin Percival	_SERVERNAME_z=\
57648ffe56aSColin Percival"SERVERNAME must be given via command line or configuration file."
57748ffe56aSColin Percival	_KEYPRINT_z="Key must be given via -k option or configuration file."
57848ffe56aSColin Percival	_KEYPRINT_bad="Invalid key fingerprint: "
57948ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
58048ffe56aSColin Percival
58148ffe56aSColin Percival	if [ -z "${SERVERNAME}" ]; then
58248ffe56aSColin Percival		echo -n "`basename $0`: "
58348ffe56aSColin Percival		echo "${_SERVERNAME_z}"
58448ffe56aSColin Percival		exit 1
58548ffe56aSColin Percival	fi
58648ffe56aSColin Percival	if [ -z "${KEYPRINT}" ]; then
58748ffe56aSColin Percival		echo -n "`basename $0`: "
58848ffe56aSColin Percival		echo "${_KEYPRINT_z}"
58948ffe56aSColin Percival		exit 1
59048ffe56aSColin Percival	fi
59148ffe56aSColin Percival	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
59248ffe56aSColin Percival		echo -n "`basename $0`: "
59348ffe56aSColin Percival		echo -n "${_KEYPRINT_bad}"
59448ffe56aSColin Percival		echo ${KEYPRINT}
59548ffe56aSColin Percival		exit 1
59648ffe56aSColin Percival	fi
59748ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
59848ffe56aSColin Percival		echo -n "`basename $0`: "
59948ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
60048ffe56aSColin Percival		echo ${WORKDIR}
60148ffe56aSColin Percival		exit 1
60248ffe56aSColin Percival	fi
60348ffe56aSColin Percival	cd ${WORKDIR} || exit 1
60448ffe56aSColin Percival
60548ffe56aSColin Percival	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
60648ffe56aSColin Percival	# to provide an upgrade path for FreeBSD Update 1.x users, since
60748ffe56aSColin Percival	# the kernels provided by FreeBSD Update 1.x are always labelled
60848ffe56aSColin Percival	# as X.Y-SECURITY.
60948ffe56aSColin Percival	RELNUM=`uname -r |
61048ffe56aSColin Percival	    sed -E 's,-p[0-9]+,,' |
61148ffe56aSColin Percival	    sed -E 's,-SECURITY,-RELEASE,'`
61248ffe56aSColin Percival	ARCH=`uname -m`
61348ffe56aSColin Percival	FETCHDIR=${RELNUM}/${ARCH}
614db6b0a61SColin Percival	PATCHDIR=${RELNUM}/${ARCH}/bp
61548ffe56aSColin Percival
61648ffe56aSColin Percival	# Figure out what directory contains the running kernel
61748ffe56aSColin Percival	BOOTFILE=`sysctl -n kern.bootfile`
61848ffe56aSColin Percival	KERNELDIR=${BOOTFILE%/kernel}
61948ffe56aSColin Percival	if ! [ -d ${KERNELDIR} ]; then
62048ffe56aSColin Percival		echo "Cannot identify running kernel"
62148ffe56aSColin Percival		exit 1
62248ffe56aSColin Percival	fi
62348ffe56aSColin Percival
6242c434b2cSColin Percival	# Figure out what kernel configuration is running.  We start with
6252c434b2cSColin Percival	# the output of `uname -i`, and then make the following adjustments:
6262c434b2cSColin Percival	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
6272c434b2cSColin Percival	# file says "ident SMP-GENERIC", I don't know...
6282c434b2cSColin Percival	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
6292c434b2cSColin Percival	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
6302c434b2cSColin Percival	# we're running an SMP kernel.  This mis-identification is a bug
6312c434b2cSColin Percival	# which was fixed in 6.2-STABLE.
6322c434b2cSColin Percival	KERNCONF=`uname -i`
6332c434b2cSColin Percival	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
6342c434b2cSColin Percival		KERNCONF=SMP
6352c434b2cSColin Percival	fi
6362c434b2cSColin Percival	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
6372c434b2cSColin Percival		if sysctl kern.version | grep -qE '/SMP$'; then
6382c434b2cSColin Percival			KERNCONF=SMP
6392c434b2cSColin Percival		fi
6402c434b2cSColin Percival	fi
6412c434b2cSColin Percival
64248ffe56aSColin Percival	# Define some paths
64348ffe56aSColin Percival	BSPATCH=/usr/bin/bspatch
64448ffe56aSColin Percival	SHA256=/sbin/sha256
64548ffe56aSColin Percival	PHTTPGET=/usr/libexec/phttpget
64648ffe56aSColin Percival
64748ffe56aSColin Percival	# Set up variables relating to VERBOSELEVEL
64848ffe56aSColin Percival	fetch_setup_verboselevel
64948ffe56aSColin Percival
65048ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
65148ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
65248ffe56aSColin Percival}
65348ffe56aSColin Percival
654db6b0a61SColin Percival# Perform sanity checks etc. before fetching upgrades.
655db6b0a61SColin Percivalupgrade_check_params () {
656db6b0a61SColin Percival	fetch_check_params
657db6b0a61SColin Percival
658db6b0a61SColin Percival	# Unless set otherwise, we're upgrading to the same kernel config.
659db6b0a61SColin Percival	NKERNCONF=${KERNCONF}
660db6b0a61SColin Percival
661db6b0a61SColin Percival	# We need TARGETRELEASE set
662db6b0a61SColin Percival	_TARGETRELEASE_z="Release target must be specified via -r option."
663db6b0a61SColin Percival	if [ -z "${TARGETRELEASE}" ]; then
664db6b0a61SColin Percival		echo -n "`basename $0`: "
665db6b0a61SColin Percival		echo "${_TARGETRELEASE_z}"
666db6b0a61SColin Percival		exit 1
667db6b0a61SColin Percival	fi
668db6b0a61SColin Percival
669db6b0a61SColin Percival	# The target release should be != the current release.
670db6b0a61SColin Percival	if [ "${TARGETRELEASE}" = "${RELNUM}" ]; then
671db6b0a61SColin Percival		echo -n "`basename $0`: "
672db6b0a61SColin Percival		echo "Cannot upgrade from ${RELNUM} to itself"
673db6b0a61SColin Percival		exit 1
674db6b0a61SColin Percival	fi
675db6b0a61SColin Percival
676db6b0a61SColin Percival	# Turning off AllowAdd or AllowDelete is a bad idea for upgrades.
677db6b0a61SColin Percival	if [ "${ALLOWADD}" = "no" ]; then
678db6b0a61SColin Percival		echo -n "`basename $0`: "
679db6b0a61SColin Percival		echo -n "WARNING: \"AllowAdd no\" is a bad idea "
680db6b0a61SColin Percival		echo "when upgrading between releases."
681db6b0a61SColin Percival		echo
682db6b0a61SColin Percival	fi
683db6b0a61SColin Percival	if [ "${ALLOWDELETE}" = "no" ]; then
684db6b0a61SColin Percival		echo -n "`basename $0`: "
685db6b0a61SColin Percival		echo -n "WARNING: \"AllowDelete no\" is a bad idea "
686db6b0a61SColin Percival		echo "when upgrading between releases."
687db6b0a61SColin Percival		echo
688db6b0a61SColin Percival	fi
689db6b0a61SColin Percival
690db6b0a61SColin Percival	# Set EDITOR to /usr/bin/vi if it isn't already set
691db6b0a61SColin Percival	: ${EDITOR:='/usr/bin/vi'}
692db6b0a61SColin Percival}
693db6b0a61SColin Percival
69448ffe56aSColin Percival# Perform sanity checks and set some final parameters in
69548ffe56aSColin Percival# preparation for installing updates.
69648ffe56aSColin Percivalinstall_check_params () {
69748ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
69848ffe56aSColin Percival	if [ `id -u` != 0 ]; then
69948ffe56aSColin Percival		echo "You must be root to run this."
70048ffe56aSColin Percival		exit 1
70148ffe56aSColin Percival	fi
70248ffe56aSColin Percival
7032328d598SColin Percival	# Check that securelevel <= 0.  Otherwise we can't update schg files.
7042328d598SColin Percival	if [ `sysctl -n kern.securelevel` -gt 0 ]; then
7052328d598SColin Percival		echo "Updates cannot be installed when the system securelevel"
7062328d598SColin Percival		echo "is greater than zero."
7072328d598SColin Percival		exit 1
7082328d598SColin Percival	fi
7092328d598SColin Percival
71048ffe56aSColin Percival	# Check that we have a working directory
71148ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
71248ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
71348ffe56aSColin Percival		echo -n "`basename $0`: "
71448ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
71548ffe56aSColin Percival		echo ${WORKDIR}
71648ffe56aSColin Percival		exit 1
71748ffe56aSColin Percival	fi
71848ffe56aSColin Percival	cd ${WORKDIR} || exit 1
71948ffe56aSColin Percival
72048ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
72148ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
72248ffe56aSColin Percival
72348ffe56aSColin Percival	# Check that we have updates ready to install
72448ffe56aSColin Percival	if ! [ -L ${BDHASH}-install ]; then
72548ffe56aSColin Percival		echo "No updates are available to install."
72648ffe56aSColin Percival		echo "Run '$0 fetch' first."
72748ffe56aSColin Percival		exit 1
72848ffe56aSColin Percival	fi
72948ffe56aSColin Percival	if ! [ -f ${BDHASH}-install/INDEX-OLD ] ||
73048ffe56aSColin Percival	    ! [ -f ${BDHASH}-install/INDEX-NEW ]; then
73148ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
73248ffe56aSColin Percival		echo "Re-run '$0 fetch'."
73348ffe56aSColin Percival		exit 1
73448ffe56aSColin Percival	fi
73523d827efSSimon L. B. Nielsen
73623d827efSSimon L. B. Nielsen	# Figure out what directory contains the running kernel
73723d827efSSimon L. B. Nielsen	BOOTFILE=`sysctl -n kern.bootfile`
73823d827efSSimon L. B. Nielsen	KERNELDIR=${BOOTFILE%/kernel}
73923d827efSSimon L. B. Nielsen	if ! [ -d ${KERNELDIR} ]; then
74023d827efSSimon L. B. Nielsen		echo "Cannot identify running kernel"
74123d827efSSimon L. B. Nielsen		exit 1
74223d827efSSimon L. B. Nielsen	fi
74348ffe56aSColin Percival}
74448ffe56aSColin Percival
74548ffe56aSColin Percival# Perform sanity checks and set some final parameters in
74648ffe56aSColin Percival# preparation for UNinstalling updates.
74748ffe56aSColin Percivalrollback_check_params () {
74848ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
74948ffe56aSColin Percival	if [ `id -u` != 0 ]; then
75048ffe56aSColin Percival		echo "You must be root to run this."
75148ffe56aSColin Percival		exit 1
75248ffe56aSColin Percival	fi
75348ffe56aSColin Percival
75448ffe56aSColin Percival	# Check that we have a working directory
75548ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
75648ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
75748ffe56aSColin Percival		echo -n "`basename $0`: "
75848ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
75948ffe56aSColin Percival		echo ${WORKDIR}
76048ffe56aSColin Percival		exit 1
76148ffe56aSColin Percival	fi
76248ffe56aSColin Percival	cd ${WORKDIR} || exit 1
76348ffe56aSColin Percival
76448ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
76548ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
76648ffe56aSColin Percival
76748ffe56aSColin Percival	# Check that we have updates ready to rollback
76848ffe56aSColin Percival	if ! [ -L ${BDHASH}-rollback ]; then
76948ffe56aSColin Percival		echo "No rollback directory found."
77048ffe56aSColin Percival		exit 1
77148ffe56aSColin Percival	fi
77248ffe56aSColin Percival	if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] ||
77348ffe56aSColin Percival	    ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then
77448ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
77548ffe56aSColin Percival		exit 1
77648ffe56aSColin Percival	fi
77748ffe56aSColin Percival}
77848ffe56aSColin Percival
77908e23beeSColin Percival# Perform sanity checks and set some final parameters
78008e23beeSColin Percival# in preparation for comparing the system against the
78108e23beeSColin Percival# published index.  Figure out which index we should
78208e23beeSColin Percival# compare against: If the user is running *-p[0-9]+,
78308e23beeSColin Percival# strip off the last part; if the user is running
78408e23beeSColin Percival# -SECURITY, call it -RELEASE.  Chdir into the working
78508e23beeSColin Percival# directory.
78608e23beeSColin PercivalIDS_check_params () {
78708e23beeSColin Percival	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
78808e23beeSColin Percival
78908e23beeSColin Percival	_SERVERNAME_z=\
79008e23beeSColin Percival"SERVERNAME must be given via command line or configuration file."
79108e23beeSColin Percival	_KEYPRINT_z="Key must be given via -k option or configuration file."
79208e23beeSColin Percival	_KEYPRINT_bad="Invalid key fingerprint: "
79308e23beeSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
79408e23beeSColin Percival
79508e23beeSColin Percival	if [ -z "${SERVERNAME}" ]; then
79608e23beeSColin Percival		echo -n "`basename $0`: "
79708e23beeSColin Percival		echo "${_SERVERNAME_z}"
79808e23beeSColin Percival		exit 1
79908e23beeSColin Percival	fi
80008e23beeSColin Percival	if [ -z "${KEYPRINT}" ]; then
80108e23beeSColin Percival		echo -n "`basename $0`: "
80208e23beeSColin Percival		echo "${_KEYPRINT_z}"
80308e23beeSColin Percival		exit 1
80408e23beeSColin Percival	fi
80508e23beeSColin Percival	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
80608e23beeSColin Percival		echo -n "`basename $0`: "
80708e23beeSColin Percival		echo -n "${_KEYPRINT_bad}"
80808e23beeSColin Percival		echo ${KEYPRINT}
80908e23beeSColin Percival		exit 1
81008e23beeSColin Percival	fi
81108e23beeSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
81208e23beeSColin Percival		echo -n "`basename $0`: "
81308e23beeSColin Percival		echo -n "${_WORKDIR_bad}"
81408e23beeSColin Percival		echo ${WORKDIR}
81508e23beeSColin Percival		exit 1
81608e23beeSColin Percival	fi
81708e23beeSColin Percival	cd ${WORKDIR} || exit 1
81808e23beeSColin Percival
81908e23beeSColin Percival	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
82008e23beeSColin Percival	# to provide an upgrade path for FreeBSD Update 1.x users, since
82108e23beeSColin Percival	# the kernels provided by FreeBSD Update 1.x are always labelled
82208e23beeSColin Percival	# as X.Y-SECURITY.
82308e23beeSColin Percival	RELNUM=`uname -r |
82408e23beeSColin Percival	    sed -E 's,-p[0-9]+,,' |
82508e23beeSColin Percival	    sed -E 's,-SECURITY,-RELEASE,'`
82608e23beeSColin Percival	ARCH=`uname -m`
82708e23beeSColin Percival	FETCHDIR=${RELNUM}/${ARCH}
82808e23beeSColin Percival	PATCHDIR=${RELNUM}/${ARCH}/bp
82908e23beeSColin Percival
83008e23beeSColin Percival	# Figure out what directory contains the running kernel
83108e23beeSColin Percival	BOOTFILE=`sysctl -n kern.bootfile`
83208e23beeSColin Percival	KERNELDIR=${BOOTFILE%/kernel}
83308e23beeSColin Percival	if ! [ -d ${KERNELDIR} ]; then
83408e23beeSColin Percival		echo "Cannot identify running kernel"
83508e23beeSColin Percival		exit 1
83608e23beeSColin Percival	fi
83708e23beeSColin Percival
83808e23beeSColin Percival	# Figure out what kernel configuration is running.  We start with
83908e23beeSColin Percival	# the output of `uname -i`, and then make the following adjustments:
84008e23beeSColin Percival	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
84108e23beeSColin Percival	# file says "ident SMP-GENERIC", I don't know...
84208e23beeSColin Percival	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
84308e23beeSColin Percival	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
84408e23beeSColin Percival	# we're running an SMP kernel.  This mis-identification is a bug
84508e23beeSColin Percival	# which was fixed in 6.2-STABLE.
84608e23beeSColin Percival	KERNCONF=`uname -i`
84708e23beeSColin Percival	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
84808e23beeSColin Percival		KERNCONF=SMP
84908e23beeSColin Percival	fi
85008e23beeSColin Percival	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
85108e23beeSColin Percival		if sysctl kern.version | grep -qE '/SMP$'; then
85208e23beeSColin Percival			KERNCONF=SMP
85308e23beeSColin Percival		fi
85408e23beeSColin Percival	fi
85508e23beeSColin Percival
85608e23beeSColin Percival	# Define some paths
85708e23beeSColin Percival	SHA256=/sbin/sha256
85808e23beeSColin Percival	PHTTPGET=/usr/libexec/phttpget
85908e23beeSColin Percival
86008e23beeSColin Percival	# Set up variables relating to VERBOSELEVEL
86108e23beeSColin Percival	fetch_setup_verboselevel
86208e23beeSColin Percival}
86308e23beeSColin Percival
86448ffe56aSColin Percival#### Core functionality -- the actual work gets done here
86548ffe56aSColin Percival
86648ffe56aSColin Percival# Use an SRV query to pick a server.  If the SRV query doesn't provide
86748ffe56aSColin Percival# a useful answer, use the server name specified by the user.
86848ffe56aSColin Percival# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
86948ffe56aSColin Percival# from that; or if no servers are returned, use ${SERVERNAME}.
87048ffe56aSColin Percival# This allows a user to specify "portsnap.freebsd.org" (in which case
87148ffe56aSColin Percival# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
87248ffe56aSColin Percival# (in which case portsnap will use that particular server, since there
87348ffe56aSColin Percival# won't be an SRV entry for that name).
87448ffe56aSColin Percival#
87548ffe56aSColin Percival# We ignore the Port field, since we are always going to use port 80.
87648ffe56aSColin Percival
87748ffe56aSColin Percival# Fetch the mirror list, but do not pick a mirror yet.  Returns 1 if
87848ffe56aSColin Percival# no mirrors are available for any reason.
87948ffe56aSColin Percivalfetch_pick_server_init () {
88048ffe56aSColin Percival	: > serverlist_tried
88148ffe56aSColin Percival
88248ffe56aSColin Percival# Check that host(1) exists (i.e., that the system wasn't built with the
88348ffe56aSColin Percival# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
88448ffe56aSColin Percival	if ! which -s host; then
88548ffe56aSColin Percival		: > serverlist_full
88648ffe56aSColin Percival		return 1
88748ffe56aSColin Percival	fi
88848ffe56aSColin Percival
88948ffe56aSColin Percival	echo -n "Looking up ${SERVERNAME} mirrors... "
89048ffe56aSColin Percival
89148ffe56aSColin Percival# Issue the SRV query and pull out the Priority, Weight, and Target fields.
89248ffe56aSColin Percival# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
89348ffe56aSColin Percival# "$name server selection ..."; we allow either format.
89448ffe56aSColin Percival	MLIST="_http._tcp.${SERVERNAME}"
89548ffe56aSColin Percival	host -t srv "${MLIST}" |
89648ffe56aSColin Percival	    sed -nE "s/${MLIST} (has SRV record|server selection) //p" |
89748ffe56aSColin Percival	    cut -f 1,2,4 -d ' ' |
89848ffe56aSColin Percival	    sed -e 's/\.$//' |
89948ffe56aSColin Percival	    sort > serverlist_full
90048ffe56aSColin Percival
90148ffe56aSColin Percival# If no records, give up -- we'll just use the server name we were given.
90248ffe56aSColin Percival	if [ `wc -l < serverlist_full` -eq 0 ]; then
90348ffe56aSColin Percival		echo "none found."
90448ffe56aSColin Percival		return 1
90548ffe56aSColin Percival	fi
90648ffe56aSColin Percival
90748ffe56aSColin Percival# Report how many mirrors we found.
90848ffe56aSColin Percival	echo `wc -l < serverlist_full` "mirrors found."
90948ffe56aSColin Percival
91048ffe56aSColin Percival# Generate a random seed for use in picking mirrors.  If HTTP_PROXY
91148ffe56aSColin Percival# is set, this will be used to generate the seed; otherwise, the seed
91248ffe56aSColin Percival# will be random.
91348ffe56aSColin Percival	if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
91448ffe56aSColin Percival		RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
91548ffe56aSColin Percival		    tr -d 'a-f' |
91648ffe56aSColin Percival		    cut -c 1-9`
91748ffe56aSColin Percival	else
91848ffe56aSColin Percival		RANDVALUE=`jot -r 1 0 999999999`
91948ffe56aSColin Percival	fi
92048ffe56aSColin Percival}
92148ffe56aSColin Percival
92248ffe56aSColin Percival# Pick a mirror.  Returns 1 if we have run out of mirrors to try.
92348ffe56aSColin Percivalfetch_pick_server () {
92448ffe56aSColin Percival# Generate a list of not-yet-tried mirrors
92548ffe56aSColin Percival	sort serverlist_tried |
92648ffe56aSColin Percival	    comm -23 serverlist_full - > serverlist
92748ffe56aSColin Percival
92848ffe56aSColin Percival# Have we run out of mirrors?
92948ffe56aSColin Percival	if [ `wc -l < serverlist` -eq 0 ]; then
93048ffe56aSColin Percival		echo "No mirrors remaining, giving up."
93148ffe56aSColin Percival		return 1
93248ffe56aSColin Percival	fi
93348ffe56aSColin Percival
93448ffe56aSColin Percival# Find the highest priority level (lowest numeric value).
93548ffe56aSColin Percival	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
93648ffe56aSColin Percival
93748ffe56aSColin Percival# Add up the weights of the response lines at that priority level.
93848ffe56aSColin Percival	SRV_WSUM=0;
93948ffe56aSColin Percival	while read X; do
94048ffe56aSColin Percival		case "$X" in
94148ffe56aSColin Percival		${SRV_PRIORITY}\ *)
94248ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
94348ffe56aSColin Percival			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
94448ffe56aSColin Percival			;;
94548ffe56aSColin Percival		esac
94648ffe56aSColin Percival	done < serverlist
94748ffe56aSColin Percival
94848ffe56aSColin Percival# If all the weights are 0, pretend that they are all 1 instead.
94948ffe56aSColin Percival	if [ ${SRV_WSUM} -eq 0 ]; then
95048ffe56aSColin Percival		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
95148ffe56aSColin Percival		SRV_W_ADD=1
95248ffe56aSColin Percival	else
95348ffe56aSColin Percival		SRV_W_ADD=0
95448ffe56aSColin Percival	fi
95548ffe56aSColin Percival
95648ffe56aSColin Percival# Pick a value between 0 and the sum of the weights - 1
95748ffe56aSColin Percival	SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
95848ffe56aSColin Percival
95948ffe56aSColin Percival# Read through the list of mirrors and set SERVERNAME.  Write the line
96048ffe56aSColin Percival# corresponding to the mirror we selected into serverlist_tried so that
96148ffe56aSColin Percival# we won't try it again.
96248ffe56aSColin Percival	while read X; do
96348ffe56aSColin Percival		case "$X" in
96448ffe56aSColin Percival		${SRV_PRIORITY}\ *)
96548ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
96648ffe56aSColin Percival			SRV_W=$(($SRV_W + $SRV_W_ADD))
96748ffe56aSColin Percival			if [ $SRV_RND -lt $SRV_W ]; then
96848ffe56aSColin Percival				SERVERNAME=`echo $X | cut -f 3 -d ' '`
96948ffe56aSColin Percival				echo "$X" >> serverlist_tried
97048ffe56aSColin Percival				break
97148ffe56aSColin Percival			else
97248ffe56aSColin Percival				SRV_RND=$(($SRV_RND - $SRV_W))
97348ffe56aSColin Percival			fi
97448ffe56aSColin Percival			;;
97548ffe56aSColin Percival		esac
97648ffe56aSColin Percival	done < serverlist
97748ffe56aSColin Percival}
97848ffe56aSColin Percival
97948ffe56aSColin Percival# Take a list of ${oldhash}|${newhash} and output a list of needed patches,
98048ffe56aSColin Percival# i.e., those for which we have ${oldhash} and don't have ${newhash}.
98148ffe56aSColin Percivalfetch_make_patchlist () {
98248ffe56aSColin Percival	grep -vE "^([0-9a-f]{64})\|\1$" |
98348ffe56aSColin Percival	    tr '|' ' ' |
98448ffe56aSColin Percival		while read X Y; do
98548ffe56aSColin Percival			if [ -f "files/${Y}.gz" ] ||
98648ffe56aSColin Percival			    [ ! -f "files/${X}.gz" ]; then
98748ffe56aSColin Percival				continue
98848ffe56aSColin Percival			fi
98948ffe56aSColin Percival			echo "${X}|${Y}"
99048ffe56aSColin Percival		done | uniq
99148ffe56aSColin Percival}
99248ffe56aSColin Percival
99348ffe56aSColin Percival# Print user-friendly progress statistics
99448ffe56aSColin Percivalfetch_progress () {
99548ffe56aSColin Percival	LNC=0
99648ffe56aSColin Percival	while read x; do
99748ffe56aSColin Percival		LNC=$(($LNC + 1))
99848ffe56aSColin Percival		if [ $(($LNC % 10)) = 0 ]; then
99948ffe56aSColin Percival			echo -n $LNC
100048ffe56aSColin Percival		elif [ $(($LNC % 2)) = 0 ]; then
100148ffe56aSColin Percival			echo -n .
100248ffe56aSColin Percival		fi
100348ffe56aSColin Percival	done
100448ffe56aSColin Percival	echo -n " "
100548ffe56aSColin Percival}
100648ffe56aSColin Percival
1007db6b0a61SColin Percival# Function for asking the user if everything is ok
1008db6b0a61SColin Percivalcontinuep () {
1009db6b0a61SColin Percival	while read -p "Does this look reasonable (y/n)? " CONTINUE; do
1010db6b0a61SColin Percival		case "${CONTINUE}" in
1011db6b0a61SColin Percival		y*)
1012db6b0a61SColin Percival			return 0
1013db6b0a61SColin Percival			;;
1014db6b0a61SColin Percival		n*)
1015db6b0a61SColin Percival			return 1
1016db6b0a61SColin Percival			;;
1017db6b0a61SColin Percival		esac
1018db6b0a61SColin Percival	done
1019db6b0a61SColin Percival}
1020db6b0a61SColin Percival
102148ffe56aSColin Percival# Initialize the working directory
102248ffe56aSColin Percivalworkdir_init () {
102348ffe56aSColin Percival	mkdir -p files
102448ffe56aSColin Percival	touch tINDEX.present
102548ffe56aSColin Percival}
102648ffe56aSColin Percival
102748ffe56aSColin Percival# Check that we have a public key with an appropriate hash, or
102848ffe56aSColin Percival# fetch the key if it doesn't exist.  Returns 1 if the key has
102948ffe56aSColin Percival# not yet been fetched.
103048ffe56aSColin Percivalfetch_key () {
103148ffe56aSColin Percival	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
103248ffe56aSColin Percival		return 0
103348ffe56aSColin Percival	fi
103448ffe56aSColin Percival
103548ffe56aSColin Percival	echo -n "Fetching public key from ${SERVERNAME}... "
103648ffe56aSColin Percival	rm -f pub.ssl
103748ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \
103848ffe56aSColin Percival	    2>${QUIETREDIR} || true
103948ffe56aSColin Percival	if ! [ -r pub.ssl ]; then
104048ffe56aSColin Percival		echo "failed."
104148ffe56aSColin Percival		return 1
104248ffe56aSColin Percival	fi
104348ffe56aSColin Percival	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
104448ffe56aSColin Percival		echo "key has incorrect hash."
104548ffe56aSColin Percival		rm -f pub.ssl
104648ffe56aSColin Percival		return 1
104748ffe56aSColin Percival	fi
104848ffe56aSColin Percival	echo "done."
104948ffe56aSColin Percival}
105048ffe56aSColin Percival
105148ffe56aSColin Percival# Fetch metadata signature, aka "tag".
105248ffe56aSColin Percivalfetch_tag () {
1053db6b0a61SColin Percival	echo -n "Fetching metadata signature "
1054db6b0a61SColin Percival	echo ${NDEBUG} "for ${RELNUM} from ${SERVERNAME}... "
105548ffe56aSColin Percival	rm -f latest.ssl
105648ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl	\
105748ffe56aSColin Percival	    2>${QUIETREDIR} || true
105848ffe56aSColin Percival	if ! [ -r latest.ssl ]; then
105948ffe56aSColin Percival		echo "failed."
106048ffe56aSColin Percival		return 1
106148ffe56aSColin Percival	fi
106248ffe56aSColin Percival
106348ffe56aSColin Percival	openssl rsautl -pubin -inkey pub.ssl -verify		\
106448ffe56aSColin Percival	    < latest.ssl > tag.new 2>${QUIETREDIR} || true
106548ffe56aSColin Percival	rm latest.ssl
106648ffe56aSColin Percival
106748ffe56aSColin Percival	if ! [ `wc -l < tag.new` = 1 ] ||
106848ffe56aSColin Percival	    ! grep -qE	\
106948ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
107048ffe56aSColin Percival		tag.new; then
107148ffe56aSColin Percival		echo "invalid signature."
107248ffe56aSColin Percival		return 1
107348ffe56aSColin Percival	fi
107448ffe56aSColin Percival
107548ffe56aSColin Percival	echo "done."
107648ffe56aSColin Percival
107748ffe56aSColin Percival	RELPATCHNUM=`cut -f 4 -d '|' < tag.new`
107848ffe56aSColin Percival	TINDEXHASH=`cut -f 5 -d '|' < tag.new`
107948ffe56aSColin Percival	EOLTIME=`cut -f 6 -d '|' < tag.new`
108048ffe56aSColin Percival}
108148ffe56aSColin Percival
108248ffe56aSColin Percival# Sanity-check the patch number in a tag, to make sure that we're not
108348ffe56aSColin Percival# going to "update" backwards and to prevent replay attacks.
108448ffe56aSColin Percivalfetch_tagsanity () {
108548ffe56aSColin Percival	# Check that we're not going to move from -pX to -pY with Y < X.
108648ffe56aSColin Percival	RELPX=`uname -r | sed -E 's,.*-,,'`
108748ffe56aSColin Percival	if echo ${RELPX} | grep -qE '^p[0-9]+$'; then
108848ffe56aSColin Percival		RELPX=`echo ${RELPX} | cut -c 2-`
108948ffe56aSColin Percival	else
109048ffe56aSColin Percival		RELPX=0
109148ffe56aSColin Percival	fi
109248ffe56aSColin Percival	if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then
109348ffe56aSColin Percival		echo
109448ffe56aSColin Percival		echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
109548ffe56aSColin Percival		echo " appear older than what"
109648ffe56aSColin Percival		echo "we are currently running (`uname -r`)!"
109748ffe56aSColin Percival		echo "Cowardly refusing to proceed any further."
109848ffe56aSColin Percival		return 1
109948ffe56aSColin Percival	fi
110048ffe56aSColin Percival
110148ffe56aSColin Percival	# If "tag" exists and corresponds to ${RELNUM}, make sure that
110248ffe56aSColin Percival	# it contains a patch number <= RELPATCHNUM, in order to protect
110348ffe56aSColin Percival	# against rollback (replay) attacks.
110448ffe56aSColin Percival	if [ -f tag ] &&
110548ffe56aSColin Percival	    grep -qE	\
110648ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
110748ffe56aSColin Percival		tag; then
110848ffe56aSColin Percival		LASTRELPATCHNUM=`cut -f 4 -d '|' < tag`
110948ffe56aSColin Percival
111048ffe56aSColin Percival		if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then
111148ffe56aSColin Percival			echo
111248ffe56aSColin Percival			echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
111348ffe56aSColin Percival			echo " are older than the"
111448ffe56aSColin Percival			echo -n "most recently seen updates"
111548ffe56aSColin Percival			echo " (${RELNUM}-p${LASTRELPATCHNUM})."
111648ffe56aSColin Percival			echo "Cowardly refusing to proceed any further."
111748ffe56aSColin Percival			return 1
111848ffe56aSColin Percival		fi
111948ffe56aSColin Percival	fi
112048ffe56aSColin Percival}
112148ffe56aSColin Percival
112248ffe56aSColin Percival# Fetch metadata index file
112348ffe56aSColin Percivalfetch_metadata_index () {
112448ffe56aSColin Percival	echo ${NDEBUG} "Fetching metadata index... "
112548ffe56aSColin Percival	rm -f ${TINDEXHASH}
112648ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH}
112748ffe56aSColin Percival	    2>${QUIETREDIR}
112848ffe56aSColin Percival	if ! [ -f ${TINDEXHASH} ]; then
112948ffe56aSColin Percival		echo "failed."
113048ffe56aSColin Percival		return 1
113148ffe56aSColin Percival	fi
113248ffe56aSColin Percival	if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then
113348ffe56aSColin Percival		echo "update metadata index corrupt."
113448ffe56aSColin Percival		return 1
113548ffe56aSColin Percival	fi
113648ffe56aSColin Percival	echo "done."
113748ffe56aSColin Percival}
113848ffe56aSColin Percival
113948ffe56aSColin Percival# Print an error message about signed metadata being bogus.
114048ffe56aSColin Percivalfetch_metadata_bogus () {
114148ffe56aSColin Percival	echo
114248ffe56aSColin Percival	echo "The update metadata$1 is correctly signed, but"
114348ffe56aSColin Percival	echo "failed an integrity check."
114448ffe56aSColin Percival	echo "Cowardly refusing to proceed any further."
114548ffe56aSColin Percival	return 1
114648ffe56aSColin Percival}
114748ffe56aSColin Percival
114848ffe56aSColin Percival# Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH}
114948ffe56aSColin Percival# with the lines not named in $@ from tINDEX.present (if that file exists).
115048ffe56aSColin Percivalfetch_metadata_index_merge () {
115148ffe56aSColin Percival	for METAFILE in $@; do
115248ffe56aSColin Percival		if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l`	\
115348ffe56aSColin Percival		    -ne 1 ]; then
115448ffe56aSColin Percival			fetch_metadata_bogus " index"
115548ffe56aSColin Percival			return 1
115648ffe56aSColin Percival		fi
115748ffe56aSColin Percival
115848ffe56aSColin Percival		grep -E "${METAFILE}\|" ${TINDEXHASH}
115948ffe56aSColin Percival	done |
116048ffe56aSColin Percival	    sort > tINDEX.wanted
116148ffe56aSColin Percival
116248ffe56aSColin Percival	if [ -f tINDEX.present ]; then
116348ffe56aSColin Percival		join -t '|' -v 2 tINDEX.wanted tINDEX.present |
116448ffe56aSColin Percival		    sort -m - tINDEX.wanted > tINDEX.new
116548ffe56aSColin Percival		rm tINDEX.wanted
116648ffe56aSColin Percival	else
116748ffe56aSColin Percival		mv tINDEX.wanted tINDEX.new
116848ffe56aSColin Percival	fi
116948ffe56aSColin Percival}
117048ffe56aSColin Percival
117148ffe56aSColin Percival# Sanity check all the lines of tINDEX.new.  Even if more metadata lines
117248ffe56aSColin Percival# are added by future versions of the server, this won't cause problems,
117348ffe56aSColin Percival# since the only lines which appear in tINDEX.new are the ones which we
117448ffe56aSColin Percival# specifically grepped out of ${TINDEXHASH}.
117548ffe56aSColin Percivalfetch_metadata_index_sanity () {
117648ffe56aSColin Percival	if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then
117748ffe56aSColin Percival		fetch_metadata_bogus " index"
117848ffe56aSColin Percival		return 1
117948ffe56aSColin Percival	fi
118048ffe56aSColin Percival}
118148ffe56aSColin Percival
118248ffe56aSColin Percival# Sanity check the metadata file $1.
118348ffe56aSColin Percivalfetch_metadata_sanity () {
118448ffe56aSColin Percival	# Some aliases to save space later: ${P} is a character which can
118548ffe56aSColin Percival	# appear in a path; ${M} is the four numeric metadata fields; and
118648ffe56aSColin Percival	# ${H} is a sha256 hash.
118748ffe56aSColin Percival	P="[-+./:=_[[:alnum:]]"
118848ffe56aSColin Percival	M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+"
118948ffe56aSColin Percival	H="[0-9a-f]{64}"
119048ffe56aSColin Percival
119148ffe56aSColin Percival	# Check that the first four fields make sense.
119248ffe56aSColin Percival	if gunzip -c < files/$1.gz |
119348ffe56aSColin Percival	    grep -qvE "^[a-z]+\|[0-9a-z]+\|${P}+\|[fdL-]\|"; then
119448ffe56aSColin Percival		fetch_metadata_bogus ""
119548ffe56aSColin Percival		return 1
119648ffe56aSColin Percival	fi
119748ffe56aSColin Percival
119848ffe56aSColin Percival	# Remove the first three fields.
119948ffe56aSColin Percival	gunzip -c < files/$1.gz |
120048ffe56aSColin Percival	    cut -f 4- -d '|' > sanitycheck.tmp
120148ffe56aSColin Percival
120248ffe56aSColin Percival	# Sanity check entries with type 'f'
120348ffe56aSColin Percival	if grep -E '^f' sanitycheck.tmp |
120448ffe56aSColin Percival	    grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then
120548ffe56aSColin Percival		fetch_metadata_bogus ""
120648ffe56aSColin Percival		return 1
120748ffe56aSColin Percival	fi
120848ffe56aSColin Percival
120948ffe56aSColin Percival	# Sanity check entries with type 'd'
121048ffe56aSColin Percival	if grep -E '^d' sanitycheck.tmp |
121148ffe56aSColin Percival	    grep -qvE "^d\|${M}\|\|\$"; then
121248ffe56aSColin Percival		fetch_metadata_bogus ""
121348ffe56aSColin Percival		return 1
121448ffe56aSColin Percival	fi
121548ffe56aSColin Percival
121648ffe56aSColin Percival	# Sanity check entries with type 'L'
121748ffe56aSColin Percival	if grep -E '^L' sanitycheck.tmp |
121848ffe56aSColin Percival	    grep -qvE "^L\|${M}\|${P}*\|\$"; then
121948ffe56aSColin Percival		fetch_metadata_bogus ""
122048ffe56aSColin Percival		return 1
122148ffe56aSColin Percival	fi
122248ffe56aSColin Percival
122348ffe56aSColin Percival	# Sanity check entries with type '-'
122448ffe56aSColin Percival	if grep -E '^-' sanitycheck.tmp |
122548ffe56aSColin Percival	    grep -qvE "^-\|\|\|\|\|\|"; then
122648ffe56aSColin Percival		fetch_metadata_bogus ""
122748ffe56aSColin Percival		return 1
122848ffe56aSColin Percival	fi
122948ffe56aSColin Percival
123048ffe56aSColin Percival	# Clean up
123148ffe56aSColin Percival	rm sanitycheck.tmp
123248ffe56aSColin Percival}
123348ffe56aSColin Percival
123448ffe56aSColin Percival# Fetch the metadata index and metadata files listed in $@,
123548ffe56aSColin Percival# taking advantage of metadata patches where possible.
123648ffe56aSColin Percivalfetch_metadata () {
123748ffe56aSColin Percival	fetch_metadata_index || return 1
123848ffe56aSColin Percival	fetch_metadata_index_merge $@ || return 1
123948ffe56aSColin Percival	fetch_metadata_index_sanity || return 1
124048ffe56aSColin Percival
124148ffe56aSColin Percival	# Generate a list of wanted metadata patches
124248ffe56aSColin Percival	join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new |
124348ffe56aSColin Percival	    fetch_make_patchlist > patchlist
124448ffe56aSColin Percival
124548ffe56aSColin Percival	if [ -s patchlist ]; then
124648ffe56aSColin Percival		# Attempt to fetch metadata patches
124748ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
124848ffe56aSColin Percival		echo ${NDEBUG} "metadata patches.${DDSTATS}"
124948ffe56aSColin Percival		tr '|' '-' < patchlist |
125048ffe56aSColin Percival		    lam -s "${FETCHDIR}/tp/" - -s ".gz" |
125148ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
125248ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
125348ffe56aSColin Percival		echo "done."
125448ffe56aSColin Percival
125548ffe56aSColin Percival		# Attempt to apply metadata patches
125648ffe56aSColin Percival		echo -n "Applying metadata patches... "
125748ffe56aSColin Percival		tr '|' ' ' < patchlist |
125848ffe56aSColin Percival		    while read X Y; do
125948ffe56aSColin Percival			if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
126048ffe56aSColin Percival			gunzip -c < ${X}-${Y}.gz > diff
126148ffe56aSColin Percival			gunzip -c < files/${X}.gz > diff-OLD
126248ffe56aSColin Percival
126348ffe56aSColin Percival			# Figure out which lines are being added and removed
126448ffe56aSColin Percival			grep -E '^-' diff |
126548ffe56aSColin Percival			    cut -c 2- |
126648ffe56aSColin Percival			    while read PREFIX; do
126748ffe56aSColin Percival				look "${PREFIX}" diff-OLD
126848ffe56aSColin Percival			    done |
126948ffe56aSColin Percival			    sort > diff-rm
127048ffe56aSColin Percival			grep -E '^\+' diff |
127148ffe56aSColin Percival			    cut -c 2- > diff-add
127248ffe56aSColin Percival
127348ffe56aSColin Percival			# Generate the new file
127448ffe56aSColin Percival			comm -23 diff-OLD diff-rm |
127548ffe56aSColin Percival			    sort - diff-add > diff-NEW
127648ffe56aSColin Percival
127748ffe56aSColin Percival			if [ `${SHA256} -q diff-NEW` = ${Y} ]; then
127848ffe56aSColin Percival				mv diff-NEW files/${Y}
127948ffe56aSColin Percival				gzip -n files/${Y}
128048ffe56aSColin Percival			else
128148ffe56aSColin Percival				mv diff-NEW ${Y}.bad
128248ffe56aSColin Percival			fi
128348ffe56aSColin Percival			rm -f ${X}-${Y}.gz diff
128448ffe56aSColin Percival			rm -f diff-OLD diff-NEW diff-add diff-rm
128548ffe56aSColin Percival		done 2>${QUIETREDIR}
128648ffe56aSColin Percival		echo "done."
128748ffe56aSColin Percival	fi
128848ffe56aSColin Percival
128948ffe56aSColin Percival	# Update metadata without patches
129048ffe56aSColin Percival	cut -f 2 -d '|' < tINDEX.new |
129148ffe56aSColin Percival	    while read Y; do
129248ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
129348ffe56aSColin Percival			echo ${Y};
129448ffe56aSColin Percival		fi
1295bce02f98SColin Percival	    done |
1296bce02f98SColin Percival	    sort -u > filelist
129748ffe56aSColin Percival
129848ffe56aSColin Percival	if [ -s filelist ]; then
129948ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
130048ffe56aSColin Percival		echo ${NDEBUG} "metadata files... "
130148ffe56aSColin Percival		lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist |
130248ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
130348ffe56aSColin Percival		    2>${QUIETREDIR}
130448ffe56aSColin Percival
130548ffe56aSColin Percival		while read Y; do
130648ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
130748ffe56aSColin Percival				echo "failed."
130848ffe56aSColin Percival				return 1
130948ffe56aSColin Percival			fi
131048ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
131148ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
131248ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
131348ffe56aSColin Percival			else
131448ffe56aSColin Percival				echo "metadata is corrupt."
131548ffe56aSColin Percival				return 1
131648ffe56aSColin Percival			fi
131748ffe56aSColin Percival		done < filelist
131848ffe56aSColin Percival		echo "done."
131948ffe56aSColin Percival	fi
132048ffe56aSColin Percival
132148ffe56aSColin Percival# Sanity-check the metadata files.
132248ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new > filelist
132348ffe56aSColin Percival	while read X; do
132448ffe56aSColin Percival		fetch_metadata_sanity ${X} || return 1
132548ffe56aSColin Percival	done < filelist
132648ffe56aSColin Percival
132748ffe56aSColin Percival# Remove files which are no longer needed
132848ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.present |
132948ffe56aSColin Percival	    sort > oldfiles
133048ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new |
133148ffe56aSColin Percival	    sort |
133248ffe56aSColin Percival	    comm -13 - oldfiles |
133348ffe56aSColin Percival	    lam -s "files/" - -s ".gz" |
133448ffe56aSColin Percival	    xargs rm -f
133548ffe56aSColin Percival	rm patchlist filelist oldfiles
133648ffe56aSColin Percival	rm ${TINDEXHASH}
133748ffe56aSColin Percival
133848ffe56aSColin Percival# We're done!
133948ffe56aSColin Percival	mv tINDEX.new tINDEX.present
134048ffe56aSColin Percival	mv tag.new tag
134148ffe56aSColin Percival
134248ffe56aSColin Percival	return 0
134348ffe56aSColin Percival}
134448ffe56aSColin Percival
1345db6b0a61SColin Percival# Extract a subset of a downloaded metadata file containing only the parts
1346db6b0a61SColin Percival# which are listed in COMPONENTS.
1347db6b0a61SColin Percivalfetch_filter_metadata_components () {
1348db6b0a61SColin Percival	METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
1349db6b0a61SColin Percival	gunzip -c < files/${METAHASH}.gz > $1.all
1350db6b0a61SColin Percival
1351db6b0a61SColin Percival	# Fish out the lines belonging to components we care about.
1352db6b0a61SColin Percival	for C in ${COMPONENTS}; do
1353db6b0a61SColin Percival		look "`echo ${C} | tr '/' '|'`|" $1.all
1354db6b0a61SColin Percival	done > $1
1355db6b0a61SColin Percival
1356db6b0a61SColin Percival	# Remove temporary file.
1357db6b0a61SColin Percival	rm $1.all
1358db6b0a61SColin Percival}
1359db6b0a61SColin Percival
1360b698a3abSColin Percival# Generate a filtered version of the metadata file $1 from the downloaded
136148ffe56aSColin Percival# file, by fishing out the lines corresponding to components we're trying
136248ffe56aSColin Percival# to keep updated, and then removing lines corresponding to paths we want
136348ffe56aSColin Percival# to ignore.
136448ffe56aSColin Percivalfetch_filter_metadata () {
136548ffe56aSColin Percival	# Fish out the lines belonging to components we care about.
1366db6b0a61SColin Percival	fetch_filter_metadata_components $1
1367db6b0a61SColin Percival
136848ffe56aSColin Percival	# Canonicalize directory names by removing any trailing / in
136948ffe56aSColin Percival	# order to avoid listing directories multiple times if they
137048ffe56aSColin Percival	# belong to multiple components.  Turning "/" into "" doesn't
137148ffe56aSColin Percival	# matter, since we add a leading "/" when we use paths later.
1372db6b0a61SColin Percival	cut -f 3- -d '|' $1 |
137348ffe56aSColin Percival	    sed -e 's,/|d|,|d|,' |
137448ffe56aSColin Percival	    sort -u > $1.tmp
137548ffe56aSColin Percival
137648ffe56aSColin Percival	# Figure out which lines to ignore and remove them.
137748ffe56aSColin Percival	for X in ${IGNOREPATHS}; do
137848ffe56aSColin Percival		grep -E "^${X}" $1.tmp
137948ffe56aSColin Percival	done |
138048ffe56aSColin Percival	    sort -u |
138148ffe56aSColin Percival	    comm -13 - $1.tmp > $1
138248ffe56aSColin Percival
138348ffe56aSColin Percival	# Remove temporary files.
1384db6b0a61SColin Percival	rm $1.tmp
138548ffe56aSColin Percival}
138648ffe56aSColin Percival
1387db6b0a61SColin Percival# Filter the metadata file $1 by adding lines with "/boot/$2"
1388bce02f98SColin Percival# replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the
1389db6b0a61SColin Percival# trailing "/kernel"); and if "/boot/$2" does not exist, remove
1390bce02f98SColin Percival# the original lines which start with that.
1391bce02f98SColin Percival# Put another way: Deal with the fact that the FOO kernel is sometimes
1392bce02f98SColin Percival# installed in /boot/FOO/ and is sometimes installed elsewhere.
139348ffe56aSColin Percivalfetch_filter_kernel_names () {
1394db6b0a61SColin Percival	grep ^/boot/$2 $1 |
1395db6b0a61SColin Percival	    sed -e "s,/boot/$2,${KERNELDIR},g" |
139648ffe56aSColin Percival	    sort - $1 > $1.tmp
139748ffe56aSColin Percival	mv $1.tmp $1
1398bce02f98SColin Percival
1399db6b0a61SColin Percival	if ! [ -d /boot/$2 ]; then
1400db6b0a61SColin Percival		grep -v ^/boot/$2 $1 > $1.tmp
1401bce02f98SColin Percival		mv $1.tmp $1
1402bce02f98SColin Percival	fi
140348ffe56aSColin Percival}
140448ffe56aSColin Percival
140548ffe56aSColin Percival# For all paths appearing in $1 or $3, inspect the system
140648ffe56aSColin Percival# and generate $2 describing what is currently installed.
140748ffe56aSColin Percivalfetch_inspect_system () {
140848ffe56aSColin Percival	# No errors yet...
140948ffe56aSColin Percival	rm -f .err
141048ffe56aSColin Percival
141148ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
141248ffe56aSColin Percival	echo -n "Inspecting system... "
141348ffe56aSColin Percival
141448ffe56aSColin Percival	# Generate list of files to inspect
141548ffe56aSColin Percival	cat $1 $3 |
141648ffe56aSColin Percival	    cut -f 1 -d '|' |
141748ffe56aSColin Percival	    sort -u > filelist
141848ffe56aSColin Percival
141948ffe56aSColin Percival	# Examine each file and output lines of the form
142048ffe56aSColin Percival	# /path/to/file|type|device-inum|user|group|perm|flags|value
142148ffe56aSColin Percival	# sorted by device and inode number.
142248ffe56aSColin Percival	while read F; do
142348ffe56aSColin Percival		# If the symlink/file/directory does not exist, record this.
142448ffe56aSColin Percival		if ! [ -e ${BASEDIR}/${F} ]; then
142548ffe56aSColin Percival			echo "${F}|-||||||"
142648ffe56aSColin Percival			continue
142748ffe56aSColin Percival		fi
142848ffe56aSColin Percival		if ! [ -r ${BASEDIR}/${F} ]; then
142948ffe56aSColin Percival			echo "Cannot read file: ${BASEDIR}/${F}"	\
143048ffe56aSColin Percival			    >/dev/stderr
143148ffe56aSColin Percival			touch .err
143248ffe56aSColin Percival			return 1
143348ffe56aSColin Percival		fi
143448ffe56aSColin Percival
143548ffe56aSColin Percival		# Otherwise, output an index line.
143648ffe56aSColin Percival		if [ -L ${BASEDIR}/${F} ]; then
143748ffe56aSColin Percival			echo -n "${F}|L|"
143848ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
143948ffe56aSColin Percival			readlink ${BASEDIR}/${F};
144048ffe56aSColin Percival		elif [ -f ${BASEDIR}/${F} ]; then
144148ffe56aSColin Percival			echo -n "${F}|f|"
144248ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
144348ffe56aSColin Percival			sha256 -q ${BASEDIR}/${F};
144448ffe56aSColin Percival		elif [ -d ${BASEDIR}/${F} ]; then
144548ffe56aSColin Percival			echo -n "${F}|d|"
144648ffe56aSColin Percival			stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
144748ffe56aSColin Percival		else
144848ffe56aSColin Percival			echo "Unknown file type: ${BASEDIR}/${F}"	\
144948ffe56aSColin Percival			    >/dev/stderr
145048ffe56aSColin Percival			touch .err
145148ffe56aSColin Percival			return 1
145248ffe56aSColin Percival		fi
145348ffe56aSColin Percival	done < filelist |
145448ffe56aSColin Percival	    sort -k 3,3 -t '|' > $2.tmp
145548ffe56aSColin Percival	rm filelist
145648ffe56aSColin Percival
145748ffe56aSColin Percival	# Check if an error occured during system inspection
145848ffe56aSColin Percival	if [ -f .err ]; then
145948ffe56aSColin Percival		return 1
146048ffe56aSColin Percival	fi
146148ffe56aSColin Percival
146248ffe56aSColin Percival	# Convert to the form
146348ffe56aSColin Percival	# /path/to/file|type|user|group|perm|flags|value|hlink
146448ffe56aSColin Percival	# by resolving identical device and inode numbers into hard links.
146548ffe56aSColin Percival	cut -f 1,3 -d '|' $2.tmp |
146648ffe56aSColin Percival	    sort -k 1,1 -t '|' |
146748ffe56aSColin Percival	    sort -s -u -k 2,2 -t '|' |
146848ffe56aSColin Percival	    join -1 2 -2 3 -t '|' - $2.tmp |
146948ffe56aSColin Percival	    awk -F \| -v OFS=\|		\
147048ffe56aSColin Percival		'{
147148ffe56aSColin Percival		    if (($2 == $3) || ($4 == "-"))
147248ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,""
147348ffe56aSColin Percival		    else
147448ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,$2
147548ffe56aSColin Percival		}' |
147648ffe56aSColin Percival	    sort > $2
147748ffe56aSColin Percival	rm $2.tmp
147848ffe56aSColin Percival
147948ffe56aSColin Percival	# We're finished looking around
148048ffe56aSColin Percival	echo "done."
148148ffe56aSColin Percival}
148248ffe56aSColin Percival
1483db6b0a61SColin Percival# For any paths matching ${MERGECHANGES}, compare $1 and $2 and find any
1484db6b0a61SColin Percival# files which differ; generate $3 containing these paths and the old hashes.
1485db6b0a61SColin Percivalfetch_filter_mergechanges () {
1486db6b0a61SColin Percival	# Pull out the paths and hashes of the files matching ${MERGECHANGES}.
1487db6b0a61SColin Percival	for F in $1 $2; do
1488db6b0a61SColin Percival		for X in ${MERGECHANGES}; do
1489db6b0a61SColin Percival			grep -E "^${X}" ${F}
1490db6b0a61SColin Percival		done |
1491db6b0a61SColin Percival		    cut -f 1,2,7 -d '|' |
1492db6b0a61SColin Percival		    sort > ${F}-values
1493db6b0a61SColin Percival	done
1494db6b0a61SColin Percival
1495db6b0a61SColin Percival	# Any line in $2-values which doesn't appear in $1-values and is a
1496db6b0a61SColin Percival	# file means that we should list the path in $3.
1497db6b0a61SColin Percival	comm -13 $1-values $2-values |
1498db6b0a61SColin Percival	    fgrep '|f|' |
1499db6b0a61SColin Percival	    cut -f 1 -d '|' > $2-paths
1500db6b0a61SColin Percival
1501db6b0a61SColin Percival	# For each path, pull out one (and only one!) entry from $1-values.
1502db6b0a61SColin Percival	# Note that we cannot distinguish which "old" version the user made
1503db6b0a61SColin Percival	# changes to; but hopefully any changes which occur due to security
1504db6b0a61SColin Percival	# updates will exist in both the "new" version and the version which
1505db6b0a61SColin Percival	# the user has installed, so the merging will still work.
1506db6b0a61SColin Percival	while read X; do
1507db6b0a61SColin Percival		look "${X}|" $1-values |
1508db6b0a61SColin Percival		    head -1
1509db6b0a61SColin Percival	done < $2-paths > $3
1510db6b0a61SColin Percival
1511db6b0a61SColin Percival	# Clean up
1512db6b0a61SColin Percival	rm $1-values $2-values $2-paths
1513db6b0a61SColin Percival}
1514db6b0a61SColin Percival
151548ffe56aSColin Percival# For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123]
1516db6b0a61SColin Percival# which correspond to lines in $2 with hashes not matching $1 or $3, unless
1517db6b0a61SColin Percival# the paths are listed in $4.  For entries in $2 marked "not present"
1518db6b0a61SColin Percival# (aka. type -), remove lines from $[123] unless there is a corresponding
1519db6b0a61SColin Percival# entry in $1.
152048ffe56aSColin Percivalfetch_filter_unmodified_notpresent () {
152148ffe56aSColin Percival	# Figure out which lines of $1 and $3 correspond to bits which
152248ffe56aSColin Percival	# should only be updated if they haven't changed, and fish out
152348ffe56aSColin Percival	# the (path, type, value) tuples.
152448ffe56aSColin Percival	# NOTE: We don't consider a file to be "modified" if it matches
152548ffe56aSColin Percival	# the hash from $3.
152648ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
152748ffe56aSColin Percival		grep -E "^${X}" $1
152848ffe56aSColin Percival		grep -E "^${X}" $3
152948ffe56aSColin Percival	done |
153048ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
153148ffe56aSColin Percival	    sort > $1-values
153248ffe56aSColin Percival
153348ffe56aSColin Percival	# Do the same for $2.
153448ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
153548ffe56aSColin Percival		grep -E "^${X}" $2
153648ffe56aSColin Percival	done |
153748ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
153848ffe56aSColin Percival	    sort > $2-values
153948ffe56aSColin Percival
154048ffe56aSColin Percival	# Any entry in $2-values which is not in $1-values corresponds to
1541db6b0a61SColin Percival	# a path which we need to remove from $1, $2, and $3, unless it
1542db6b0a61SColin Percival	# that path appears in $4.
1543db6b0a61SColin Percival	comm -13 $1-values $2-values |
1544db6b0a61SColin Percival	    sort -t '|' -k 1,1 > mlines.tmp
1545db6b0a61SColin Percival	cut -f 1 -d '|' $4 |
1546db6b0a61SColin Percival	    sort |
1547db6b0a61SColin Percival	    join -v 2 -t '|' - mlines.tmp |
1548db6b0a61SColin Percival	    sort > mlines
1549db6b0a61SColin Percival	rm $1-values $2-values mlines.tmp
155048ffe56aSColin Percival
155148ffe56aSColin Percival	# Any lines in $2 which are not in $1 AND are "not present" lines
155248ffe56aSColin Percival	# also belong in mlines.
155348ffe56aSColin Percival	comm -13 $1 $2 |
155448ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
155548ffe56aSColin Percival	    fgrep '|-|' >> mlines
155648ffe56aSColin Percival
155748ffe56aSColin Percival	# Remove lines from $1, $2, and $3
155848ffe56aSColin Percival	for X in $1 $2 $3; do
155948ffe56aSColin Percival		sort -t '|' -k 1,1 ${X} > ${X}.tmp
156048ffe56aSColin Percival		cut -f 1 -d '|' < mlines |
156148ffe56aSColin Percival		    sort |
156248ffe56aSColin Percival		    join -v 2 -t '|' - ${X}.tmp |
156348ffe56aSColin Percival		    sort > ${X}
156448ffe56aSColin Percival		rm ${X}.tmp
156548ffe56aSColin Percival	done
156648ffe56aSColin Percival
156748ffe56aSColin Percival	# Store a list of the modified files, for future reference
156848ffe56aSColin Percival	fgrep -v '|-|' mlines |
156948ffe56aSColin Percival	    cut -f 1 -d '|' > modifiedfiles
157048ffe56aSColin Percival	rm mlines
157148ffe56aSColin Percival}
157248ffe56aSColin Percival
157348ffe56aSColin Percival# For each entry in $1 of type -, remove any corresponding
157448ffe56aSColin Percival# entry from $2 if ${ALLOWADD} != "yes".  Remove all entries
157548ffe56aSColin Percival# of type - from $1.
157648ffe56aSColin Percivalfetch_filter_allowadd () {
157748ffe56aSColin Percival	cut -f 1,2 -d '|' < $1 |
157848ffe56aSColin Percival	    fgrep '|-' |
157948ffe56aSColin Percival	    cut -f 1 -d '|' > filesnotpresent
158048ffe56aSColin Percival
158148ffe56aSColin Percival	if [ ${ALLOWADD} != "yes" ]; then
158248ffe56aSColin Percival		sort < $2 |
158348ffe56aSColin Percival		    join -v 1 -t '|' - filesnotpresent |
158448ffe56aSColin Percival		    sort > $2.tmp
158548ffe56aSColin Percival		mv $2.tmp $2
158648ffe56aSColin Percival	fi
158748ffe56aSColin Percival
158848ffe56aSColin Percival	sort < $1 |
158948ffe56aSColin Percival	    join -v 1 -t '|' - filesnotpresent |
159048ffe56aSColin Percival	    sort > $1.tmp
159148ffe56aSColin Percival	mv $1.tmp $1
159248ffe56aSColin Percival	rm filesnotpresent
159348ffe56aSColin Percival}
159448ffe56aSColin Percival
159548ffe56aSColin Percival# If ${ALLOWDELETE} != "yes", then remove any entries from $1
159648ffe56aSColin Percival# which don't correspond to entries in $2.
159748ffe56aSColin Percivalfetch_filter_allowdelete () {
159848ffe56aSColin Percival	# Produce a lists ${PATH}|${TYPE}
159948ffe56aSColin Percival	for X in $1 $2; do
160048ffe56aSColin Percival		cut -f 1-2 -d '|' < ${X} |
160148ffe56aSColin Percival		    sort -u > ${X}.nodes
160248ffe56aSColin Percival	done
160348ffe56aSColin Percival
160448ffe56aSColin Percival	# Figure out which lines need to be removed from $1.
160548ffe56aSColin Percival	if [ ${ALLOWDELETE} != "yes" ]; then
160648ffe56aSColin Percival		comm -23 $1.nodes $2.nodes > $1.badnodes
160748ffe56aSColin Percival	else
160848ffe56aSColin Percival		: > $1.badnodes
160948ffe56aSColin Percival	fi
161048ffe56aSColin Percival
161148ffe56aSColin Percival	# Remove the relevant lines from $1
161248ffe56aSColin Percival	while read X; do
161348ffe56aSColin Percival		look "${X}|" $1
161448ffe56aSColin Percival	done < $1.badnodes |
161548ffe56aSColin Percival	    comm -13 - $1 > $1.tmp
161648ffe56aSColin Percival	mv $1.tmp $1
161748ffe56aSColin Percival
161848ffe56aSColin Percival	rm $1.badnodes $1.nodes $2.nodes
161948ffe56aSColin Percival}
162048ffe56aSColin Percival
162148ffe56aSColin Percival# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2
162248ffe56aSColin Percival# with metadata not matching any entry in $1, replace the corresponding
162348ffe56aSColin Percival# line of $3 with one having the same metadata as the entry in $2.
162448ffe56aSColin Percivalfetch_filter_modified_metadata () {
162548ffe56aSColin Percival	# Fish out the metadata from $1 and $2
162648ffe56aSColin Percival	for X in $1 $2; do
162748ffe56aSColin Percival		cut -f 1-6 -d '|' < ${X} > ${X}.metadata
162848ffe56aSColin Percival	done
162948ffe56aSColin Percival
163048ffe56aSColin Percival	# Find the metadata we need to keep
163148ffe56aSColin Percival	if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then
163248ffe56aSColin Percival		comm -13 $1.metadata $2.metadata > keepmeta
163348ffe56aSColin Percival	else
163448ffe56aSColin Percival		: > keepmeta
163548ffe56aSColin Percival	fi
163648ffe56aSColin Percival
163748ffe56aSColin Percival	# Extract the lines which we need to remove from $3, and
163848ffe56aSColin Percival	# construct the lines which we need to add to $3.
163948ffe56aSColin Percival	: > $3.remove
164048ffe56aSColin Percival	: > $3.add
164148ffe56aSColin Percival	while read LINE; do
164248ffe56aSColin Percival		NODE=`echo "${LINE}" | cut -f 1-2 -d '|'`
164348ffe56aSColin Percival		look "${NODE}|" $3 >> $3.remove
164448ffe56aSColin Percival		look "${NODE}|" $3 |
164548ffe56aSColin Percival		    cut -f 7- -d '|' |
164648ffe56aSColin Percival		    lam -s "${LINE}|" - >> $3.add
164748ffe56aSColin Percival	done < keepmeta
164848ffe56aSColin Percival
164948ffe56aSColin Percival	# Remove the specified lines and add the new lines.
165048ffe56aSColin Percival	sort $3.remove |
165148ffe56aSColin Percival	    comm -13 - $3 |
165248ffe56aSColin Percival	    sort -u - $3.add > $3.tmp
165348ffe56aSColin Percival	mv $3.tmp $3
165448ffe56aSColin Percival
165548ffe56aSColin Percival	rm keepmeta $1.metadata $2.metadata $3.add $3.remove
165648ffe56aSColin Percival}
165748ffe56aSColin Percival
165848ffe56aSColin Percival# Remove lines from $1 and $2 which are identical;
165948ffe56aSColin Percival# no need to update a file if it isn't changing.
166048ffe56aSColin Percivalfetch_filter_uptodate () {
166148ffe56aSColin Percival	comm -23 $1 $2 > $1.tmp
166248ffe56aSColin Percival	comm -13 $1 $2 > $2.tmp
166348ffe56aSColin Percival
166448ffe56aSColin Percival	mv $1.tmp $1
166548ffe56aSColin Percival	mv $2.tmp $2
166648ffe56aSColin Percival}
166748ffe56aSColin Percival
1668db6b0a61SColin Percival# Fetch any "clean" old versions of files we need for merging changes.
1669db6b0a61SColin Percivalfetch_files_premerge () {
1670db6b0a61SColin Percival	# We only need to do anything if $1 is non-empty.
1671db6b0a61SColin Percival	if [ -s $1 ]; then
1672db6b0a61SColin Percival		# Tell the user what we're doing
1673db6b0a61SColin Percival		echo -n "Fetching files from ${OLDRELNUM} for merging... "
1674db6b0a61SColin Percival
1675db6b0a61SColin Percival		# List of files wanted
1676db6b0a61SColin Percival		fgrep '|f|' < $1 |
1677db6b0a61SColin Percival		    cut -f 3 -d '|' |
1678db6b0a61SColin Percival		    sort -u > files.wanted
1679db6b0a61SColin Percival
1680db6b0a61SColin Percival		# Only fetch the files we don't already have
1681db6b0a61SColin Percival		while read Y; do
1682db6b0a61SColin Percival			if [ ! -f "files/${Y}.gz" ]; then
1683db6b0a61SColin Percival				echo ${Y};
1684db6b0a61SColin Percival			fi
1685db6b0a61SColin Percival		done < files.wanted > filelist
1686db6b0a61SColin Percival
1687db6b0a61SColin Percival		# Actually fetch them
1688db6b0a61SColin Percival		lam -s "${OLDFETCHDIR}/f/" - -s ".gz" < filelist |
1689db6b0a61SColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
1690db6b0a61SColin Percival		    2>${QUIETREDIR}
1691db6b0a61SColin Percival
1692db6b0a61SColin Percival		# Make sure we got them all, and move them into /files/
1693db6b0a61SColin Percival		while read Y; do
1694db6b0a61SColin Percival			if ! [ -f ${Y}.gz ]; then
1695db6b0a61SColin Percival				echo "failed."
1696db6b0a61SColin Percival				return 1
1697db6b0a61SColin Percival			fi
1698db6b0a61SColin Percival			if [ `gunzip -c < ${Y}.gz |
1699db6b0a61SColin Percival			    ${SHA256} -q` = ${Y} ]; then
1700db6b0a61SColin Percival				mv ${Y}.gz files/${Y}.gz
1701db6b0a61SColin Percival			else
1702db6b0a61SColin Percival				echo "${Y} has incorrect hash."
1703db6b0a61SColin Percival				return 1
1704db6b0a61SColin Percival			fi
1705db6b0a61SColin Percival		done < filelist
1706db6b0a61SColin Percival		echo "done."
1707db6b0a61SColin Percival
1708db6b0a61SColin Percival		# Clean up
1709db6b0a61SColin Percival		rm filelist files.wanted
1710db6b0a61SColin Percival	fi
1711db6b0a61SColin Percival}
1712db6b0a61SColin Percival
171348ffe56aSColin Percival# Prepare to fetch files: Generate a list of the files we need,
171448ffe56aSColin Percival# copy the unmodified files we have into /files/, and generate
171548ffe56aSColin Percival# a list of patches to download.
171648ffe56aSColin Percivalfetch_files_prepare () {
171748ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
171848ffe56aSColin Percival	echo -n "Preparing to download files... "
171948ffe56aSColin Percival
172048ffe56aSColin Percival	# Reduce indices to ${PATH}|${HASH} pairs
172148ffe56aSColin Percival	for X in $1 $2 $3; do
172248ffe56aSColin Percival		cut -f 1,2,7 -d '|' < ${X} |
172348ffe56aSColin Percival		    fgrep '|f|' |
172448ffe56aSColin Percival		    cut -f 1,3 -d '|' |
172548ffe56aSColin Percival		    sort > ${X}.hashes
172648ffe56aSColin Percival	done
172748ffe56aSColin Percival
172848ffe56aSColin Percival	# List of files wanted
172948ffe56aSColin Percival	cut -f 2 -d '|' < $3.hashes |
17302328d598SColin Percival	    sort -u |
17312328d598SColin Percival	    while read HASH; do
17322328d598SColin Percival		if ! [ -f files/${HASH}.gz ]; then
17332328d598SColin Percival			echo ${HASH}
17342328d598SColin Percival		fi
17352328d598SColin Percival	done > files.wanted
173648ffe56aSColin Percival
173748ffe56aSColin Percival	# Generate a list of unmodified files
173848ffe56aSColin Percival	comm -12 $1.hashes $2.hashes |
173948ffe56aSColin Percival	    sort -k 1,1 -t '|' > unmodified.files
174048ffe56aSColin Percival
174148ffe56aSColin Percival	# Copy all files into /files/.  We only need the unmodified files
174248ffe56aSColin Percival	# for use in patching; but we'll want all of them if the user asks
174348ffe56aSColin Percival	# to rollback the updates later.
1744210b8123SColin Percival	while read LINE; do
1745210b8123SColin Percival		F=`echo "${LINE}" | cut -f 1 -d '|'`
1746210b8123SColin Percival		HASH=`echo "${LINE}" | cut -f 2 -d '|'`
1747210b8123SColin Percival
1748210b8123SColin Percival		# Skip files we already have.
1749210b8123SColin Percival		if [ -f files/${HASH}.gz ]; then
1750210b8123SColin Percival			continue
1751210b8123SColin Percival		fi
1752210b8123SColin Percival
1753210b8123SColin Percival		# Make sure the file hasn't changed.
175448ffe56aSColin Percival		cp "${BASEDIR}/${F}" tmpfile
1755210b8123SColin Percival		if [ `sha256 -q tmpfile` != ${HASH} ]; then
1756210b8123SColin Percival			echo
1757210b8123SColin Percival			echo "File changed while FreeBSD Update running: ${F}"
1758210b8123SColin Percival			return 1
1759210b8123SColin Percival		fi
1760210b8123SColin Percival
1761210b8123SColin Percival		# Place the file into storage.
1762210b8123SColin Percival		gzip -c < tmpfile > files/${HASH}.gz
176348ffe56aSColin Percival		rm tmpfile
1764210b8123SColin Percival	done < $2.hashes
176548ffe56aSColin Percival
176648ffe56aSColin Percival	# Produce a list of patches to download
176748ffe56aSColin Percival	sort -k 1,1 -t '|' $3.hashes |
176848ffe56aSColin Percival	    join -t '|' -o 2.2,1.2 - unmodified.files |
176948ffe56aSColin Percival	    fetch_make_patchlist > patchlist
177048ffe56aSColin Percival
177148ffe56aSColin Percival	# Garbage collect
177248ffe56aSColin Percival	rm unmodified.files $1.hashes $2.hashes $3.hashes
177348ffe56aSColin Percival
177448ffe56aSColin Percival	# We don't need the list of possible old files any more.
177548ffe56aSColin Percival	rm $1
177648ffe56aSColin Percival
177748ffe56aSColin Percival	# We're finished making noise
177848ffe56aSColin Percival	echo "done."
177948ffe56aSColin Percival}
178048ffe56aSColin Percival
178148ffe56aSColin Percival# Fetch files.
178248ffe56aSColin Percivalfetch_files () {
178348ffe56aSColin Percival	# Attempt to fetch patches
178448ffe56aSColin Percival	if [ -s patchlist ]; then
178548ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
178648ffe56aSColin Percival		echo ${NDEBUG} "patches.${DDSTATS}"
178748ffe56aSColin Percival		tr '|' '-' < patchlist |
1788db6b0a61SColin Percival		    lam -s "${PATCHDIR}/" - |
178948ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
179048ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
179148ffe56aSColin Percival		echo "done."
179248ffe56aSColin Percival
179348ffe56aSColin Percival		# Attempt to apply patches
179448ffe56aSColin Percival		echo -n "Applying patches... "
179548ffe56aSColin Percival		tr '|' ' ' < patchlist |
179648ffe56aSColin Percival		    while read X Y; do
179748ffe56aSColin Percival			if [ ! -f "${X}-${Y}" ]; then continue; fi
179848ffe56aSColin Percival			gunzip -c < files/${X}.gz > OLD
179948ffe56aSColin Percival
180048ffe56aSColin Percival			bspatch OLD NEW ${X}-${Y}
180148ffe56aSColin Percival
180248ffe56aSColin Percival			if [ `${SHA256} -q NEW` = ${Y} ]; then
180348ffe56aSColin Percival				mv NEW files/${Y}
180448ffe56aSColin Percival				gzip -n files/${Y}
180548ffe56aSColin Percival			fi
180648ffe56aSColin Percival			rm -f diff OLD NEW ${X}-${Y}
180748ffe56aSColin Percival		done 2>${QUIETREDIR}
180848ffe56aSColin Percival		echo "done."
180948ffe56aSColin Percival	fi
181048ffe56aSColin Percival
181148ffe56aSColin Percival	# Download files which couldn't be generate via patching
181248ffe56aSColin Percival	while read Y; do
181348ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
181448ffe56aSColin Percival			echo ${Y};
181548ffe56aSColin Percival		fi
181648ffe56aSColin Percival	done < files.wanted > filelist
181748ffe56aSColin Percival
181848ffe56aSColin Percival	if [ -s filelist ]; then
181948ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
182048ffe56aSColin Percival		echo ${NDEBUG} "files... "
182148ffe56aSColin Percival		lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist |
182248ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
182348ffe56aSColin Percival		    2>${QUIETREDIR}
182448ffe56aSColin Percival
182548ffe56aSColin Percival		while read Y; do
182648ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
182748ffe56aSColin Percival				echo "failed."
182848ffe56aSColin Percival				return 1
182948ffe56aSColin Percival			fi
183048ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
183148ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
183248ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
183348ffe56aSColin Percival			else
183448ffe56aSColin Percival				echo "${Y} has incorrect hash."
183548ffe56aSColin Percival				return 1
183648ffe56aSColin Percival			fi
183748ffe56aSColin Percival		done < filelist
183848ffe56aSColin Percival		echo "done."
183948ffe56aSColin Percival	fi
184048ffe56aSColin Percival
184148ffe56aSColin Percival	# Clean up
184248ffe56aSColin Percival	rm files.wanted filelist patchlist
184348ffe56aSColin Percival}
184448ffe56aSColin Percival
184548ffe56aSColin Percival# Create and populate install manifest directory; and report what updates
184648ffe56aSColin Percival# are available.
184748ffe56aSColin Percivalfetch_create_manifest () {
184848ffe56aSColin Percival	# If we have an existing install manifest, nuke it.
184948ffe56aSColin Percival	if [ -L "${BDHASH}-install" ]; then
185048ffe56aSColin Percival		rm -r ${BDHASH}-install/
185148ffe56aSColin Percival		rm ${BDHASH}-install
185248ffe56aSColin Percival	fi
185348ffe56aSColin Percival
185448ffe56aSColin Percival	# Report to the user if any updates were avoided due to local changes
185548ffe56aSColin Percival	if [ -s modifiedfiles ]; then
185648ffe56aSColin Percival		echo
185748ffe56aSColin Percival		echo -n "The following files are affected by updates, "
185848ffe56aSColin Percival		echo "but no changes have"
185948ffe56aSColin Percival		echo -n "been downloaded because the files have been "
186048ffe56aSColin Percival		echo "modified locally:"
186148ffe56aSColin Percival		cat modifiedfiles
1862db6b0a61SColin Percival	fi | more
186348ffe56aSColin Percival	rm modifiedfiles
186448ffe56aSColin Percival
186548ffe56aSColin Percival	# If no files will be updated, tell the user and exit
186648ffe56aSColin Percival	if ! [ -s INDEX-PRESENT ] &&
186748ffe56aSColin Percival	    ! [ -s INDEX-NEW ]; then
186848ffe56aSColin Percival		rm INDEX-PRESENT INDEX-NEW
186948ffe56aSColin Percival		echo
187048ffe56aSColin Percival		echo -n "No updates needed to update system to "
187148ffe56aSColin Percival		echo "${RELNUM}-p${RELPATCHNUM}."
187248ffe56aSColin Percival		return
187348ffe56aSColin Percival	fi
187448ffe56aSColin Percival
187548ffe56aSColin Percival	# Divide files into (a) removed files, (b) added files, and
187648ffe56aSColin Percival	# (c) updated files.
187748ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-PRESENT |
187848ffe56aSColin Percival	    sort > INDEX-PRESENT.flist
187948ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-NEW |
188048ffe56aSColin Percival	    sort > INDEX-NEW.flist
188148ffe56aSColin Percival	comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed
188248ffe56aSColin Percival	comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added
188348ffe56aSColin Percival	comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated
188448ffe56aSColin Percival	rm INDEX-PRESENT.flist INDEX-NEW.flist
188548ffe56aSColin Percival
188648ffe56aSColin Percival	# Report removed files, if any
188748ffe56aSColin Percival	if [ -s files.removed ]; then
188848ffe56aSColin Percival		echo
188948ffe56aSColin Percival		echo -n "The following files will be removed "
189048ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
189148ffe56aSColin Percival		cat files.removed
1892db6b0a61SColin Percival	fi | more
189348ffe56aSColin Percival	rm files.removed
189448ffe56aSColin Percival
189548ffe56aSColin Percival	# Report added files, if any
189648ffe56aSColin Percival	if [ -s files.added ]; then
189748ffe56aSColin Percival		echo
189848ffe56aSColin Percival		echo -n "The following files will be added "
189948ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
190048ffe56aSColin Percival		cat files.added
1901db6b0a61SColin Percival	fi | more
190248ffe56aSColin Percival	rm files.added
190348ffe56aSColin Percival
190448ffe56aSColin Percival	# Report updated files, if any
190548ffe56aSColin Percival	if [ -s files.updated ]; then
190648ffe56aSColin Percival		echo
190748ffe56aSColin Percival		echo -n "The following files will be updated "
190848ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
190948ffe56aSColin Percival
191048ffe56aSColin Percival		cat files.updated
1911db6b0a61SColin Percival	fi | more
191248ffe56aSColin Percival	rm files.updated
191348ffe56aSColin Percival
191448ffe56aSColin Percival	# Create a directory for the install manifest.
191548ffe56aSColin Percival	MDIR=`mktemp -d install.XXXXXX` || return 1
191648ffe56aSColin Percival
191748ffe56aSColin Percival	# Populate it
191848ffe56aSColin Percival	mv INDEX-PRESENT ${MDIR}/INDEX-OLD
191948ffe56aSColin Percival	mv INDEX-NEW ${MDIR}/INDEX-NEW
192048ffe56aSColin Percival
192148ffe56aSColin Percival	# Link it into place
192248ffe56aSColin Percival	ln -s ${MDIR} ${BDHASH}-install
192348ffe56aSColin Percival}
192448ffe56aSColin Percival
192548ffe56aSColin Percival# Warn about any upcoming EoL
192648ffe56aSColin Percivalfetch_warn_eol () {
192748ffe56aSColin Percival	# What's the current time?
192848ffe56aSColin Percival	NOWTIME=`date "+%s"`
192948ffe56aSColin Percival
193048ffe56aSColin Percival	# When did we last warn about the EoL date?
193148ffe56aSColin Percival	if [ -f lasteolwarn ]; then
193248ffe56aSColin Percival		LASTWARN=`cat lasteolwarn`
193348ffe56aSColin Percival	else
193448ffe56aSColin Percival		LASTWARN=`expr ${NOWTIME} - 63072000`
193548ffe56aSColin Percival	fi
193648ffe56aSColin Percival
193748ffe56aSColin Percival	# If the EoL time is past, warn.
193848ffe56aSColin Percival	if [ ${EOLTIME} -lt ${NOWTIME} ]; then
193948ffe56aSColin Percival		echo
194048ffe56aSColin Percival		cat <<-EOF
1941b698a3abSColin Percival		WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE.
194248ffe56aSColin Percival		Any security issues discovered after `date -r ${EOLTIME}`
194348ffe56aSColin Percival		will not have been corrected.
194448ffe56aSColin Percival		EOF
194548ffe56aSColin Percival		return 1
194648ffe56aSColin Percival	fi
194748ffe56aSColin Percival
194848ffe56aSColin Percival	# Figure out how long it has been since we last warned about the
194948ffe56aSColin Percival	# upcoming EoL, and how much longer we have left.
195048ffe56aSColin Percival	SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}`
195148ffe56aSColin Percival	TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}`
195248ffe56aSColin Percival
195389b14566SColin Percival	# Don't warn if the EoL is more than 3 months away
195489b14566SColin Percival	if [ ${TIMELEFT} -gt 7884000 ]; then
195548ffe56aSColin Percival		return 0
195648ffe56aSColin Percival	fi
195748ffe56aSColin Percival
195848ffe56aSColin Percival	# Don't warn if the time remaining is more than 3 times the time
195948ffe56aSColin Percival	# since the last warning.
196048ffe56aSColin Percival	if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then
196148ffe56aSColin Percival		return 0
196248ffe56aSColin Percival	fi
196348ffe56aSColin Percival
196448ffe56aSColin Percival	# Figure out what time units to use.
196548ffe56aSColin Percival	if [ ${TIMELEFT} -lt 604800 ]; then
196648ffe56aSColin Percival		UNIT="day"
196748ffe56aSColin Percival		SIZE=86400
196848ffe56aSColin Percival	elif [ ${TIMELEFT} -lt 2678400 ]; then
196948ffe56aSColin Percival		UNIT="week"
197048ffe56aSColin Percival		SIZE=604800
197148ffe56aSColin Percival	else
197248ffe56aSColin Percival		UNIT="month"
197348ffe56aSColin Percival		SIZE=2678400
197448ffe56aSColin Percival	fi
197548ffe56aSColin Percival
197648ffe56aSColin Percival	# Compute the right number of units
197748ffe56aSColin Percival	NUM=`expr ${TIMELEFT} / ${SIZE}`
197848ffe56aSColin Percival	if [ ${NUM} != 1 ]; then
197948ffe56aSColin Percival		UNIT="${UNIT}s"
198048ffe56aSColin Percival	fi
198148ffe56aSColin Percival
198248ffe56aSColin Percival	# Print the warning
198348ffe56aSColin Percival	echo
198448ffe56aSColin Percival	cat <<-EOF
198548ffe56aSColin Percival		WARNING: `uname -sr` is approaching its End-of-Life date.
198648ffe56aSColin Percival		It is strongly recommended that you upgrade to a newer
198748ffe56aSColin Percival		release within the next ${NUM} ${UNIT}.
198848ffe56aSColin Percival	EOF
198948ffe56aSColin Percival
199048ffe56aSColin Percival	# Update the stored time of last warning
199148ffe56aSColin Percival	echo ${NOWTIME} > lasteolwarn
199248ffe56aSColin Percival}
199348ffe56aSColin Percival
199448ffe56aSColin Percival# Do the actual work involved in "fetch" / "cron".
199548ffe56aSColin Percivalfetch_run () {
199648ffe56aSColin Percival	workdir_init || return 1
199748ffe56aSColin Percival
199848ffe56aSColin Percival	# Prepare the mirror list.
199948ffe56aSColin Percival	fetch_pick_server_init && fetch_pick_server
200048ffe56aSColin Percival
200148ffe56aSColin Percival	# Try to fetch the public key until we run out of servers.
200248ffe56aSColin Percival	while ! fetch_key; do
200348ffe56aSColin Percival		fetch_pick_server || return 1
200448ffe56aSColin Percival	done
200548ffe56aSColin Percival
200648ffe56aSColin Percival	# Try to fetch the metadata index signature ("tag") until we run
200748ffe56aSColin Percival	# out of available servers; and sanity check the downloaded tag.
200848ffe56aSColin Percival	while ! fetch_tag; do
200948ffe56aSColin Percival		fetch_pick_server || return 1
201048ffe56aSColin Percival	done
201148ffe56aSColin Percival	fetch_tagsanity || return 1
201248ffe56aSColin Percival
201348ffe56aSColin Percival	# Fetch the latest INDEX-NEW and INDEX-OLD files.
201448ffe56aSColin Percival	fetch_metadata INDEX-NEW INDEX-OLD || return 1
201548ffe56aSColin Percival
201648ffe56aSColin Percival	# Generate filtered INDEX-NEW and INDEX-OLD files containing only
201748ffe56aSColin Percival	# the lines which (a) belong to components we care about, and (b)
201848ffe56aSColin Percival	# don't correspond to paths we're explicitly ignoring.
201948ffe56aSColin Percival	fetch_filter_metadata INDEX-NEW || return 1
202048ffe56aSColin Percival	fetch_filter_metadata INDEX-OLD || return 1
202148ffe56aSColin Percival
2022db6b0a61SColin Percival	# Translate /boot/${KERNCONF} into ${KERNELDIR}
2023db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-NEW ${KERNCONF}
2024db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
202548ffe56aSColin Percival
202648ffe56aSColin Percival	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
202748ffe56aSColin Percival	# system and generate an INDEX-PRESENT file.
202848ffe56aSColin Percival	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
202948ffe56aSColin Percival
203048ffe56aSColin Percival	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
203148ffe56aSColin Percival	# correspond to lines in INDEX-PRESENT with hashes not appearing
203248ffe56aSColin Percival	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
203348ffe56aSColin Percival	# INDEX-PRESENT has type - and there isn't a corresponding entry in
203448ffe56aSColin Percival	# INDEX-OLD with type -.
2035db6b0a61SColin Percival	fetch_filter_unmodified_notpresent	\
2036db6b0a61SColin Percival	    INDEX-OLD INDEX-PRESENT INDEX-NEW /dev/null
203748ffe56aSColin Percival
203848ffe56aSColin Percival	# For each entry in INDEX-PRESENT of type -, remove any corresponding
203948ffe56aSColin Percival	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
204048ffe56aSColin Percival	# of type - from INDEX-PRESENT.
204148ffe56aSColin Percival	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
204248ffe56aSColin Percival
204348ffe56aSColin Percival	# If ${ALLOWDELETE} != "yes", then remove any entries from
204448ffe56aSColin Percival	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
204548ffe56aSColin Percival	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
204648ffe56aSColin Percival
204748ffe56aSColin Percival	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
204848ffe56aSColin Percival	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
204948ffe56aSColin Percival	# replace the corresponding line of INDEX-NEW with one having the
205048ffe56aSColin Percival	# same metadata as the entry in INDEX-PRESENT.
205148ffe56aSColin Percival	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
205248ffe56aSColin Percival
205348ffe56aSColin Percival	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
205448ffe56aSColin Percival	# no need to update a file if it isn't changing.
205548ffe56aSColin Percival	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
205648ffe56aSColin Percival
205748ffe56aSColin Percival	# Prepare to fetch files: Generate a list of the files we need,
205848ffe56aSColin Percival	# copy the unmodified files we have into /files/, and generate
205948ffe56aSColin Percival	# a list of patches to download.
2060210b8123SColin Percival	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
206148ffe56aSColin Percival
206248ffe56aSColin Percival	# Fetch files.
206348ffe56aSColin Percival	fetch_files || return 1
206448ffe56aSColin Percival
206548ffe56aSColin Percival	# Create and populate install manifest directory; and report what
206648ffe56aSColin Percival	# updates are available.
206748ffe56aSColin Percival	fetch_create_manifest || return 1
206848ffe56aSColin Percival
206948ffe56aSColin Percival	# Warn about any upcoming EoL
207048ffe56aSColin Percival	fetch_warn_eol || return 1
207148ffe56aSColin Percival}
207248ffe56aSColin Percival
2073db6b0a61SColin Percival# If StrictComponents is not "yes", generate a new components list
2074db6b0a61SColin Percival# with only the components which appear to be installed.
2075db6b0a61SColin Percivalupgrade_guess_components () {
2076db6b0a61SColin Percival	if [ "${STRICTCOMPONENTS}" = "no" ]; then
2077db6b0a61SColin Percival		# Generate filtered INDEX-ALL with only the components listed
2078db6b0a61SColin Percival		# in COMPONENTS.
2079db6b0a61SColin Percival		fetch_filter_metadata_components $1 || return 1
2080db6b0a61SColin Percival
2081db6b0a61SColin Percival		# Tell the user why his disk is suddenly making lots of noise
2082db6b0a61SColin Percival		echo -n "Inspecting system... "
2083db6b0a61SColin Percival
2084db6b0a61SColin Percival		# Look at the files on disk, and assume that a component is
2085db6b0a61SColin Percival		# supposed to be present if it is more than half-present.
2086db6b0a61SColin Percival		cut -f 1-3 -d '|' < INDEX-ALL |
2087db6b0a61SColin Percival		    tr '|' ' ' |
2088db6b0a61SColin Percival		    while read C S F; do
2089db6b0a61SColin Percival			if [ -e ${BASEDIR}/${F} ]; then
2090db6b0a61SColin Percival				echo "+ ${C}|${S}"
2091db6b0a61SColin Percival			fi
2092db6b0a61SColin Percival			echo "= ${C}|${S}"
2093db6b0a61SColin Percival		    done |
2094db6b0a61SColin Percival		    sort |
2095db6b0a61SColin Percival		    uniq -c |
2096db6b0a61SColin Percival		    sed -E 's,^ +,,' > compfreq
2097db6b0a61SColin Percival		grep ' = ' compfreq |
2098db6b0a61SColin Percival		    cut -f 1,3 -d ' ' |
2099db6b0a61SColin Percival		    sort -k 2,2 -t ' ' > compfreq.total
2100db6b0a61SColin Percival		grep ' + ' compfreq |
2101db6b0a61SColin Percival		    cut -f 1,3 -d ' ' |
2102db6b0a61SColin Percival		    sort -k 2,2 -t ' ' > compfreq.present
2103db6b0a61SColin Percival		join -t ' ' -1 2 -2 2 compfreq.present compfreq.total |
2104db6b0a61SColin Percival		    while read S P T; do
2105db6b0a61SColin Percival			if [ ${P} -gt `expr ${T} / 2` ]; then
2106db6b0a61SColin Percival				echo ${S}
2107db6b0a61SColin Percival			fi
2108db6b0a61SColin Percival		    done > comp.present
2109db6b0a61SColin Percival		cut -f 2 -d ' ' < compfreq.total > comp.total
2110db6b0a61SColin Percival		rm INDEX-ALL compfreq compfreq.total compfreq.present
2111db6b0a61SColin Percival
2112db6b0a61SColin Percival		# We're done making noise.
2113db6b0a61SColin Percival		echo "done."
2114db6b0a61SColin Percival
2115db6b0a61SColin Percival		# Sometimes the kernel isn't installed where INDEX-ALL
2116db6b0a61SColin Percival		# thinks that it should be: In particular, it is often in
2117db6b0a61SColin Percival		# /boot/kernel instead of /boot/GENERIC or /boot/SMP.  To
2118db6b0a61SColin Percival		# deal with this, if "kernel|X" is listed in comp.total
2119db6b0a61SColin Percival		# (i.e., is a component which would be upgraded if it is
2120db6b0a61SColin Percival		# found to be present) we will add it to comp.present.
2121db6b0a61SColin Percival		# If "kernel|<anything>" is in comp.total but "kernel|X" is
2122db6b0a61SColin Percival		# not, we print a warning -- the user is running a kernel
2123db6b0a61SColin Percival		# which isn't part of the release.
2124db6b0a61SColin Percival		KCOMP=`echo ${KERNCONF} | tr 'A-Z' 'a-z'`
2125db6b0a61SColin Percival		grep -E "^kernel\|${KCOMP}\$" comp.total >> comp.present
2126db6b0a61SColin Percival
2127db6b0a61SColin Percival		if grep -qE "^kernel\|" comp.total &&
2128db6b0a61SColin Percival		    ! grep -qE "^kernel\|${KCOMP}\$" comp.total; then
2129db6b0a61SColin Percival			cat <<-EOF
2130db6b0a61SColin Percival
2131db6b0a61SColin PercivalWARNING: This system is running a "${KCOMP}" kernel, which is not a
2132db6b0a61SColin Percivalkernel configuration distributed as part of FreeBSD ${RELNUM}.
2133db6b0a61SColin PercivalThis kernel will not be updated: you MUST update the kernel manually
2134db6b0a61SColin Percivalbefore running "$0 install".
2135db6b0a61SColin Percival			EOF
2136db6b0a61SColin Percival		fi
2137db6b0a61SColin Percival
2138db6b0a61SColin Percival		# Re-sort the list of installed components and generate
2139db6b0a61SColin Percival		# the list of non-installed components.
2140db6b0a61SColin Percival		sort -u < comp.present > comp.present.tmp
2141db6b0a61SColin Percival		mv comp.present.tmp comp.present
2142db6b0a61SColin Percival		comm -13 comp.present comp.total > comp.absent
2143db6b0a61SColin Percival
2144db6b0a61SColin Percival		# Ask the user to confirm that what we have is correct.  To
2145db6b0a61SColin Percival		# reduce user confusion, translate "X|Y" back to "X/Y" (as
2146db6b0a61SColin Percival		# subcomponents must be listed in the configuration file).
2147db6b0a61SColin Percival		echo
2148db6b0a61SColin Percival		echo -n "The following components of FreeBSD "
2149db6b0a61SColin Percival		echo "seem to be installed:"
2150db6b0a61SColin Percival		tr '|' '/' < comp.present |
2151db6b0a61SColin Percival		    fmt -72
2152db6b0a61SColin Percival		echo
2153db6b0a61SColin Percival		echo -n "The following components of FreeBSD "
2154db6b0a61SColin Percival		echo "do not seem to be installed:"
2155db6b0a61SColin Percival		tr '|' '/' < comp.absent |
2156db6b0a61SColin Percival		    fmt -72
2157db6b0a61SColin Percival		echo
2158db6b0a61SColin Percival		continuep || return 1
2159db6b0a61SColin Percival		echo
2160db6b0a61SColin Percival
2161db6b0a61SColin Percival		# Suck the generated list of components into ${COMPONENTS}.
2162db6b0a61SColin Percival		# Note that comp.present.tmp is used due to issues with
2163db6b0a61SColin Percival		# pipelines and setting variables.
2164db6b0a61SColin Percival		COMPONENTS=""
2165db6b0a61SColin Percival		tr '|' '/' < comp.present > comp.present.tmp
2166db6b0a61SColin Percival		while read C; do
2167db6b0a61SColin Percival			COMPONENTS="${COMPONENTS} ${C}"
2168db6b0a61SColin Percival		done < comp.present.tmp
2169db6b0a61SColin Percival
2170db6b0a61SColin Percival		# Delete temporary files
2171db6b0a61SColin Percival		rm comp.present comp.present.tmp comp.absent comp.total
2172db6b0a61SColin Percival	fi
2173db6b0a61SColin Percival}
2174db6b0a61SColin Percival
2175db6b0a61SColin Percival# If StrictComponents is not "yes", COMPONENTS contains an entry
2176db6b0a61SColin Percival# corresponding to the currently running kernel, and said kernel
2177db6b0a61SColin Percival# does not exist in the new release, add "kernel/generic" to the
2178db6b0a61SColin Percival# list of components.
2179db6b0a61SColin Percivalupgrade_guess_new_kernel () {
2180db6b0a61SColin Percival	if [ "${STRICTCOMPONENTS}" = "no" ]; then
2181db6b0a61SColin Percival		# Grab the unfiltered metadata file.
2182db6b0a61SColin Percival		METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
2183db6b0a61SColin Percival		gunzip -c < files/${METAHASH}.gz > $1.all
2184db6b0a61SColin Percival
2185db6b0a61SColin Percival		# If "kernel/${KCOMP}" is in ${COMPONENTS} and that component
2186db6b0a61SColin Percival		# isn't in $1.all, we need to add kernel/generic.
2187db6b0a61SColin Percival		for C in ${COMPONENTS}; do
2188db6b0a61SColin Percival			if [ ${C} = "kernel/${KCOMP}" ] &&
2189db6b0a61SColin Percival			    ! grep -qE "^kernel\|${KCOMP}\|" $1.all; then
2190db6b0a61SColin Percival				COMPONENTS="${COMPONENTS} kernel/generic"
2191db6b0a61SColin Percival				NKERNCONF="GENERIC"
2192db6b0a61SColin Percival				cat <<-EOF
2193db6b0a61SColin Percival
2194db6b0a61SColin PercivalWARNING: This system is running a "${KCOMP}" kernel, which is not a
2195db6b0a61SColin Percivalkernel configuration distributed as part of FreeBSD ${RELNUM}.
2196db6b0a61SColin PercivalAs part of upgrading to FreeBSD ${RELNUM}, this kernel will be
2197db6b0a61SColin Percivalreplaced with a "generic" kernel.
2198db6b0a61SColin Percival				EOF
2199db6b0a61SColin Percival				continuep || return 1
2200db6b0a61SColin Percival			fi
2201db6b0a61SColin Percival		done
2202db6b0a61SColin Percival
2203db6b0a61SColin Percival		# Don't need this any more...
2204db6b0a61SColin Percival		rm $1.all
2205db6b0a61SColin Percival	fi
2206db6b0a61SColin Percival}
2207db6b0a61SColin Percival
2208db6b0a61SColin Percival# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2209db6b0a61SColin Percival# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2210db6b0a61SColin Percivalupgrade_oldall_to_oldnew () {
2211db6b0a61SColin Percival	# For each ${F}|... which appears in INDEX-ALL but does not appear
2212db6b0a61SColin Percival	# in INDEX-OLD, add ${F}|-|||||| to INDEX-OLD.
2213db6b0a61SColin Percival	cut -f 1 -d '|' < $1 |
2214db6b0a61SColin Percival	    sort -u > $1.paths
2215db6b0a61SColin Percival	cut -f 1 -d '|' < $2 |
2216db6b0a61SColin Percival	    sort -u |
2217db6b0a61SColin Percival	    comm -13 $1.paths - |
2218db6b0a61SColin Percival	    lam - -s "|-||||||" |
2219db6b0a61SColin Percival	    sort - $1 > $1.tmp
2220db6b0a61SColin Percival	mv $1.tmp $1
2221db6b0a61SColin Percival
2222db6b0a61SColin Percival	# Remove lines from INDEX-OLD which also appear in INDEX-ALL
2223db6b0a61SColin Percival	comm -23 $1 $2 > $1.tmp
2224db6b0a61SColin Percival	mv $1.tmp $1
2225db6b0a61SColin Percival
2226db6b0a61SColin Percival	# Remove lines from INDEX-ALL which have a file name not appearing
2227db6b0a61SColin Percival	# anywhere in INDEX-OLD (since these must be files which haven't
2228db6b0a61SColin Percival	# changed -- if they were new, there would be an entry of type "-").
2229db6b0a61SColin Percival	cut -f 1 -d '|' < $1 |
2230db6b0a61SColin Percival	    sort -u > $1.paths
2231db6b0a61SColin Percival	sort -k 1,1 -t '|' < $2 |
2232db6b0a61SColin Percival	    join -t '|' - $1.paths |
2233db6b0a61SColin Percival	    sort > $2.tmp
2234db6b0a61SColin Percival	rm $1.paths
2235db6b0a61SColin Percival	mv $2.tmp $2
2236db6b0a61SColin Percival
2237db6b0a61SColin Percival	# Rename INDEX-ALL to INDEX-NEW.
2238db6b0a61SColin Percival	mv $2 $3
2239db6b0a61SColin Percival}
2240db6b0a61SColin Percival
2241db6b0a61SColin Percival# From the list of "old" files in $1, merge changes in $2 with those in $3,
2242db6b0a61SColin Percival# and update $3 to reflect the hashes of merged files.
2243db6b0a61SColin Percivalupgrade_merge () {
2244db6b0a61SColin Percival	# We only need to do anything if $1 is non-empty.
2245db6b0a61SColin Percival	if [ -s $1 ]; then
2246db6b0a61SColin Percival		cut -f 1 -d '|' $1 |
2247db6b0a61SColin Percival		    sort > $1-paths
2248db6b0a61SColin Percival
2249db6b0a61SColin Percival		# Create staging area for merging files
2250db6b0a61SColin Percival		rm -rf merge/
2251db6b0a61SColin Percival		while read F; do
2252db6b0a61SColin Percival			D=`dirname ${F}`
2253db6b0a61SColin Percival			mkdir -p merge/old/${D}
2254db6b0a61SColin Percival			mkdir -p merge/${OLDRELNUM}/${D}
2255db6b0a61SColin Percival			mkdir -p merge/${RELNUM}/${D}
2256db6b0a61SColin Percival			mkdir -p merge/new/${D}
2257db6b0a61SColin Percival		done < $1-paths
2258db6b0a61SColin Percival
2259db6b0a61SColin Percival		# Copy in files
2260db6b0a61SColin Percival		while read F; do
2261db6b0a61SColin Percival			# Currently installed file
2262db6b0a61SColin Percival			V=`look "${F}|" $2 | cut -f 7 -d '|'`
2263db6b0a61SColin Percival			gunzip < files/${V}.gz > merge/old/${F}
2264db6b0a61SColin Percival
2265db6b0a61SColin Percival			# Old release
2266db6b0a61SColin Percival			if look "${F}|" $1 | fgrep -q "|f|"; then
2267db6b0a61SColin Percival				V=`look "${F}|" $1 | cut -f 3 -d '|'`
2268db6b0a61SColin Percival				gunzip < files/${V}.gz		\
2269db6b0a61SColin Percival				    > merge/${OLDRELNUM}/${F}
2270db6b0a61SColin Percival			fi
2271db6b0a61SColin Percival
2272db6b0a61SColin Percival			# New release
2273db6b0a61SColin Percival			if look "${F}|" $3 | cut -f 1,2,7 -d '|' |
2274db6b0a61SColin Percival			    fgrep -q "|f|"; then
2275db6b0a61SColin Percival				V=`look "${F}|" $3 | cut -f 7 -d '|'`
2276db6b0a61SColin Percival				gunzip < files/${V}.gz		\
2277db6b0a61SColin Percival				    > merge/${RELNUM}/${F}
2278db6b0a61SColin Percival			fi
2279db6b0a61SColin Percival		done < $1-paths
2280db6b0a61SColin Percival
2281db6b0a61SColin Percival		# Attempt to automatically merge changes
2282db6b0a61SColin Percival		echo -n "Attempting to automatically merge "
2283db6b0a61SColin Percival		echo -n "changes in files..."
2284db6b0a61SColin Percival		: > failed.merges
2285db6b0a61SColin Percival		while read F; do
2286db6b0a61SColin Percival			# If the file doesn't exist in the new release,
2287db6b0a61SColin Percival			# the result of "merging changes" is having the file
2288db6b0a61SColin Percival			# not exist.
2289db6b0a61SColin Percival			if ! [ -f merge/${RELNUM}/${F} ]; then
2290db6b0a61SColin Percival				continue
2291db6b0a61SColin Percival			fi
2292db6b0a61SColin Percival
2293db6b0a61SColin Percival			# If the file didn't exist in the old release, we're
2294db6b0a61SColin Percival			# going to throw away the existing file and hope that
2295db6b0a61SColin Percival			# the version from the new release is what we want.
2296db6b0a61SColin Percival			if ! [ -f merge/${OLDRELNUM}/${F} ]; then
2297db6b0a61SColin Percival				cp merge/${RELNUM}/${F} merge/new/${F}
2298db6b0a61SColin Percival				continue
2299db6b0a61SColin Percival			fi
2300db6b0a61SColin Percival
2301db6b0a61SColin Percival			# Some files need special treatment.
2302db6b0a61SColin Percival			case ${F} in
2303db6b0a61SColin Percival			/etc/spwd.db | /etc/pwd.db | /etc/login.conf.db)
2304db6b0a61SColin Percival				# Don't merge these -- we're rebuild them
2305db6b0a61SColin Percival				# after updates are installed.
2306db6b0a61SColin Percival				cp merge/old/${F} merge/new/${F}
2307db6b0a61SColin Percival				;;
2308db6b0a61SColin Percival			*)
2309db6b0a61SColin Percival				if ! merge -p -L "current version"	\
2310db6b0a61SColin Percival				    -L "${OLDRELNUM}" -L "${RELNUM}"	\
2311db6b0a61SColin Percival				    merge/old/${F}			\
2312db6b0a61SColin Percival				    merge/${OLDRELNUM}/${F}		\
2313db6b0a61SColin Percival				    merge/${RELNUM}/${F}		\
2314db6b0a61SColin Percival				    > merge/new/${F} 2>/dev/null; then
2315db6b0a61SColin Percival					echo ${F} >> failed.merges
2316db6b0a61SColin Percival				fi
2317db6b0a61SColin Percival				;;
2318db6b0a61SColin Percival			esac
2319db6b0a61SColin Percival		done < $1-paths
2320db6b0a61SColin Percival		echo " done."
2321db6b0a61SColin Percival
2322db6b0a61SColin Percival		# Ask the user to handle any files which didn't merge.
2323db6b0a61SColin Percival		while read F; do
2324db6b0a61SColin Percival			cat <<-EOF
2325db6b0a61SColin Percival
2326db6b0a61SColin PercivalThe following file could not be merged automatically: ${F}
2327db6b0a61SColin PercivalPress Enter to edit this file in ${EDITOR} and resolve the conflicts
2328db6b0a61SColin Percivalmanually...
2329db6b0a61SColin Percival			EOF
2330db6b0a61SColin Percival			read dummy </dev/tty
2331db6b0a61SColin Percival			${EDITOR} `pwd`/merge/new/${F} < /dev/tty
2332db6b0a61SColin Percival		done < failed.merges
2333db6b0a61SColin Percival		rm failed.merges
2334db6b0a61SColin Percival
2335db6b0a61SColin Percival		# Ask the user to confirm that he likes how the result
2336db6b0a61SColin Percival		# of merging files.
2337db6b0a61SColin Percival		while read F; do
2338db6b0a61SColin Percival			# Skip files which haven't changed.
2339db6b0a61SColin Percival			if [ -f merge/new/${F} ] &&
2340db6b0a61SColin Percival			    cmp -s merge/old/${F} merge/new/${F}; then
2341db6b0a61SColin Percival				continue
2342db6b0a61SColin Percival			fi
2343db6b0a61SColin Percival
2344db6b0a61SColin Percival			# Warn about files which are ceasing to exist.
2345db6b0a61SColin Percival			if ! [ -f merge/new/${F} ]; then
2346db6b0a61SColin Percival				cat <<-EOF
2347db6b0a61SColin Percival
2348db6b0a61SColin PercivalThe following file will be removed, as it no longer exists in
2349db6b0a61SColin PercivalFreeBSD ${RELNUM}: ${F}
2350db6b0a61SColin Percival				EOF
2351db6b0a61SColin Percival				continuep < /dev/tty || return 1
2352db6b0a61SColin Percival				continue
2353db6b0a61SColin Percival			fi
2354db6b0a61SColin Percival
2355db6b0a61SColin Percival			# Print changes for the user's approval.
2356db6b0a61SColin Percival			cat <<-EOF
2357db6b0a61SColin Percival
2358db6b0a61SColin PercivalThe following changes, which occurred between FreeBSD ${OLDRELNUM} and
2359db6b0a61SColin PercivalFreeBSD ${RELNUM} have been merged into ${F}:
2360db6b0a61SColin PercivalEOF
2361db6b0a61SColin Percival			diff -U 5 -L "current version" -L "new version"	\
2362db6b0a61SColin Percival			    merge/old/${F} merge/new/${F} || true
2363db6b0a61SColin Percival			continuep < /dev/tty || return 1
2364db6b0a61SColin Percival		done < $1-paths
2365db6b0a61SColin Percival
2366db6b0a61SColin Percival		# Store merged files.
2367db6b0a61SColin Percival		while read F; do
2368c58b62efSColin Percival			if [ -f merge/new/${F} ]; then
2369db6b0a61SColin Percival				V=`${SHA256} -q merge/new/${F}`
2370db6b0a61SColin Percival
2371db6b0a61SColin Percival				gzip -c < merge/new/${F} > files/${V}.gz
2372db6b0a61SColin Percival				echo "${F}|${V}"
2373db6b0a61SColin Percival			fi
2374db6b0a61SColin Percival		done < $1-paths > newhashes
2375db6b0a61SColin Percival
2376db6b0a61SColin Percival		# Pull lines out from $3 which need to be updated to
2377db6b0a61SColin Percival		# reflect merged files.
2378db6b0a61SColin Percival		while read F; do
2379db6b0a61SColin Percival			look "${F}|" $3
2380db6b0a61SColin Percival		done < $1-paths > $3-oldlines
2381db6b0a61SColin Percival
2382db6b0a61SColin Percival		# Update lines to reflect merged files
2383db6b0a61SColin Percival		join -t '|' -o 1.1,1.2,1.3,1.4,1.5,1.6,2.2,1.8		\
2384db6b0a61SColin Percival		    $3-oldlines newhashes > $3-newlines
2385db6b0a61SColin Percival
2386db6b0a61SColin Percival		# Remove old lines from $3 and add new lines.
2387db6b0a61SColin Percival		sort $3-oldlines |
2388db6b0a61SColin Percival		    comm -13 - $3 |
2389db6b0a61SColin Percival		    sort - $3-newlines > $3.tmp
2390db6b0a61SColin Percival		mv $3.tmp $3
2391db6b0a61SColin Percival
2392db6b0a61SColin Percival		# Clean up
2393db6b0a61SColin Percival		rm $1-paths newhashes $3-oldlines $3-newlines
2394db6b0a61SColin Percival		rm -rf merge/
2395db6b0a61SColin Percival	fi
2396db6b0a61SColin Percival
2397db6b0a61SColin Percival	# We're done with merging files.
2398db6b0a61SColin Percival	rm $1
2399db6b0a61SColin Percival}
2400db6b0a61SColin Percival
2401db6b0a61SColin Percival# Do the work involved in fetching upgrades to a new release
2402db6b0a61SColin Percivalupgrade_run () {
2403db6b0a61SColin Percival	workdir_init || return 1
2404db6b0a61SColin Percival
2405db6b0a61SColin Percival	# Prepare the mirror list.
2406db6b0a61SColin Percival	fetch_pick_server_init && fetch_pick_server
2407db6b0a61SColin Percival
2408db6b0a61SColin Percival	# Try to fetch the public key until we run out of servers.
2409db6b0a61SColin Percival	while ! fetch_key; do
2410db6b0a61SColin Percival		fetch_pick_server || return 1
2411db6b0a61SColin Percival	done
2412db6b0a61SColin Percival
2413db6b0a61SColin Percival	# Try to fetch the metadata index signature ("tag") until we run
2414db6b0a61SColin Percival	# out of available servers; and sanity check the downloaded tag.
2415db6b0a61SColin Percival	while ! fetch_tag; do
2416db6b0a61SColin Percival		fetch_pick_server || return 1
2417db6b0a61SColin Percival	done
2418db6b0a61SColin Percival	fetch_tagsanity || return 1
2419db6b0a61SColin Percival
2420db6b0a61SColin Percival	# Fetch the INDEX-OLD and INDEX-ALL.
2421db6b0a61SColin Percival	fetch_metadata INDEX-OLD INDEX-ALL || return 1
2422db6b0a61SColin Percival
2423db6b0a61SColin Percival	# If StrictComponents is not "yes", generate a new components list
2424db6b0a61SColin Percival	# with only the components which appear to be installed.
2425db6b0a61SColin Percival	upgrade_guess_components INDEX-ALL || return 1
2426db6b0a61SColin Percival
2427db6b0a61SColin Percival	# Generate filtered INDEX-OLD and INDEX-ALL files containing only
2428db6b0a61SColin Percival	# the components we want and without anything marked as "Ignore".
2429db6b0a61SColin Percival	fetch_filter_metadata INDEX-OLD || return 1
2430db6b0a61SColin Percival	fetch_filter_metadata INDEX-ALL || return 1
2431db6b0a61SColin Percival
2432db6b0a61SColin Percival	# Merge the INDEX-OLD and INDEX-ALL files into INDEX-OLD.
2433db6b0a61SColin Percival	sort INDEX-OLD INDEX-ALL > INDEX-OLD.tmp
2434db6b0a61SColin Percival	mv INDEX-OLD.tmp INDEX-OLD
2435db6b0a61SColin Percival	rm INDEX-ALL
2436db6b0a61SColin Percival
2437db6b0a61SColin Percival	# Adjust variables for fetching files from the new release.
2438db6b0a61SColin Percival	OLDRELNUM=${RELNUM}
2439db6b0a61SColin Percival	RELNUM=${TARGETRELEASE}
2440db6b0a61SColin Percival	OLDFETCHDIR=${FETCHDIR}
2441db6b0a61SColin Percival	FETCHDIR=${RELNUM}/${ARCH}
2442db6b0a61SColin Percival
2443db6b0a61SColin Percival	# Try to fetch the NEW metadata index signature ("tag") until we run
2444db6b0a61SColin Percival	# out of available servers; and sanity check the downloaded tag.
2445db6b0a61SColin Percival	while ! fetch_tag; do
2446db6b0a61SColin Percival		fetch_pick_server || return 1
2447db6b0a61SColin Percival	done
2448db6b0a61SColin Percival
2449db6b0a61SColin Percival	# Fetch the new INDEX-ALL.
2450db6b0a61SColin Percival	fetch_metadata INDEX-ALL || return 1
2451db6b0a61SColin Percival
2452db6b0a61SColin Percival	# If StrictComponents is not "yes", COMPONENTS contains an entry
2453db6b0a61SColin Percival	# corresponding to the currently running kernel, and said kernel
2454db6b0a61SColin Percival	# does not exist in the new release, add "kernel/generic" to the
2455db6b0a61SColin Percival	# list of components.
2456db6b0a61SColin Percival	upgrade_guess_new_kernel INDEX-ALL || return 1
2457db6b0a61SColin Percival
2458db6b0a61SColin Percival	# Filter INDEX-ALL to contain only the components we want and without
2459db6b0a61SColin Percival	# anything marked as "Ignore".
2460db6b0a61SColin Percival	fetch_filter_metadata INDEX-ALL || return 1
2461db6b0a61SColin Percival
2462db6b0a61SColin Percival	# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2463db6b0a61SColin Percival	# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2464db6b0a61SColin Percival	upgrade_oldall_to_oldnew INDEX-OLD INDEX-ALL INDEX-NEW
2465db6b0a61SColin Percival
2466db6b0a61SColin Percival	# Translate /boot/${KERNCONF} or /boot/${NKERNCONF} into ${KERNELDIR}
2467db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-NEW ${NKERNCONF}
2468db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
2469db6b0a61SColin Percival
2470db6b0a61SColin Percival	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
2471db6b0a61SColin Percival	# system and generate an INDEX-PRESENT file.
2472db6b0a61SColin Percival	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2473db6b0a61SColin Percival
2474db6b0a61SColin Percival	# Based on ${MERGECHANGES}, generate a file tomerge-old with the
2475db6b0a61SColin Percival	# paths and hashes of old versions of files to merge.
2476db6b0a61SColin Percival	fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT tomerge-old
2477db6b0a61SColin Percival
2478db6b0a61SColin Percival	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
2479db6b0a61SColin Percival	# correspond to lines in INDEX-PRESENT with hashes not appearing
2480db6b0a61SColin Percival	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
2481db6b0a61SColin Percival	# INDEX-PRESENT has type - and there isn't a corresponding entry in
2482db6b0a61SColin Percival	# INDEX-OLD with type -.
2483db6b0a61SColin Percival	fetch_filter_unmodified_notpresent	\
2484db6b0a61SColin Percival	    INDEX-OLD INDEX-PRESENT INDEX-NEW tomerge-old
2485db6b0a61SColin Percival
2486db6b0a61SColin Percival	# For each entry in INDEX-PRESENT of type -, remove any corresponding
2487db6b0a61SColin Percival	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
2488db6b0a61SColin Percival	# of type - from INDEX-PRESENT.
2489db6b0a61SColin Percival	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
2490db6b0a61SColin Percival
2491db6b0a61SColin Percival	# If ${ALLOWDELETE} != "yes", then remove any entries from
2492db6b0a61SColin Percival	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
2493db6b0a61SColin Percival	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
2494db6b0a61SColin Percival
2495db6b0a61SColin Percival	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
2496db6b0a61SColin Percival	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
2497db6b0a61SColin Percival	# replace the corresponding line of INDEX-NEW with one having the
2498db6b0a61SColin Percival	# same metadata as the entry in INDEX-PRESENT.
2499db6b0a61SColin Percival	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
2500db6b0a61SColin Percival
2501db6b0a61SColin Percival	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
2502db6b0a61SColin Percival	# no need to update a file if it isn't changing.
2503db6b0a61SColin Percival	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
2504db6b0a61SColin Percival
2505db6b0a61SColin Percival	# Fetch "clean" files from the old release for merging changes.
2506db6b0a61SColin Percival	fetch_files_premerge tomerge-old
2507db6b0a61SColin Percival
2508db6b0a61SColin Percival	# Prepare to fetch files: Generate a list of the files we need,
2509db6b0a61SColin Percival	# copy the unmodified files we have into /files/, and generate
2510db6b0a61SColin Percival	# a list of patches to download.
2511db6b0a61SColin Percival	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2512db6b0a61SColin Percival
2513db6b0a61SColin Percival	# Fetch patches from to-${RELNUM}/${ARCH}/bp/
2514db6b0a61SColin Percival	PATCHDIR=to-${RELNUM}/${ARCH}/bp
2515db6b0a61SColin Percival	fetch_files || return 1
2516db6b0a61SColin Percival
2517db6b0a61SColin Percival	# Merge configuration file changes.
2518db6b0a61SColin Percival	upgrade_merge tomerge-old INDEX-PRESENT INDEX-NEW || return 1
2519db6b0a61SColin Percival
2520db6b0a61SColin Percival	# Create and populate install manifest directory; and report what
2521db6b0a61SColin Percival	# updates are available.
2522db6b0a61SColin Percival	fetch_create_manifest || return 1
2523db6b0a61SColin Percival
2524db6b0a61SColin Percival	# Leave a note behind to tell the "install" command that the kernel
2525db6b0a61SColin Percival	# needs to be installed before the world.
2526db6b0a61SColin Percival	touch ${BDHASH}-install/kernelfirst
2527db6b0a61SColin Percival}
2528db6b0a61SColin Percival
252948ffe56aSColin Percival# Make sure that all the file hashes mentioned in $@ have corresponding
253048ffe56aSColin Percival# gzipped files stored in /files/.
253148ffe56aSColin Percivalinstall_verify () {
253248ffe56aSColin Percival	# Generate a list of hashes
253348ffe56aSColin Percival	cat $@ |
253448ffe56aSColin Percival	    cut -f 2,7 -d '|' |
253548ffe56aSColin Percival	    grep -E '^f' |
253648ffe56aSColin Percival	    cut -f 2 -d '|' |
253748ffe56aSColin Percival	    sort -u > filelist
253848ffe56aSColin Percival
253948ffe56aSColin Percival	# Make sure all the hashes exist
254048ffe56aSColin Percival	while read HASH; do
254148ffe56aSColin Percival		if ! [ -f files/${HASH}.gz ]; then
254248ffe56aSColin Percival			echo -n "Update files missing -- "
254348ffe56aSColin Percival			echo "this should never happen."
254448ffe56aSColin Percival			echo "Re-run '$0 fetch'."
254548ffe56aSColin Percival			return 1
254648ffe56aSColin Percival		fi
254748ffe56aSColin Percival	done < filelist
254848ffe56aSColin Percival
254948ffe56aSColin Percival	# Clean up
255048ffe56aSColin Percival	rm filelist
255148ffe56aSColin Percival}
255248ffe56aSColin Percival
255348ffe56aSColin Percival# Remove the system immutable flag from files
255448ffe56aSColin Percivalinstall_unschg () {
255548ffe56aSColin Percival	# Generate file list
255648ffe56aSColin Percival	cat $@ |
255748ffe56aSColin Percival	    cut -f 1 -d '|' > filelist
255848ffe56aSColin Percival
255948ffe56aSColin Percival	# Remove flags
256048ffe56aSColin Percival	while read F; do
2561e829ed67SColin Percival		if ! [ -e ${BASEDIR}/${F} ]; then
256248ffe56aSColin Percival			continue
256348ffe56aSColin Percival		fi
256448ffe56aSColin Percival
2565e829ed67SColin Percival		chflags noschg ${BASEDIR}/${F} || return 1
256648ffe56aSColin Percival	done < filelist
256748ffe56aSColin Percival
256848ffe56aSColin Percival	# Clean up
256948ffe56aSColin Percival	rm filelist
257048ffe56aSColin Percival}
257148ffe56aSColin Percival
257223d827efSSimon L. B. Nielsen# Decide which directory name to use for kernel backups.
257323d827efSSimon L. B. Nielsenbackup_kernel_finddir () {
257423d827efSSimon L. B. Nielsen	CNT=0
257523d827efSSimon L. B. Nielsen	while true ; do
257623d827efSSimon L. B. Nielsen		# Pathname does not exist, so it is OK use that name
257723d827efSSimon L. B. Nielsen		# for backup directory.
257823d827efSSimon L. B. Nielsen		if [ ! -e $BACKUPKERNELDIR ]; then
257923d827efSSimon L. B. Nielsen			return 0
258023d827efSSimon L. B. Nielsen		fi
258123d827efSSimon L. B. Nielsen
258223d827efSSimon L. B. Nielsen		# If directory do exist, we only use if it has our
258323d827efSSimon L. B. Nielsen		# marker file.
258423d827efSSimon L. B. Nielsen		if [ -d $BACKUPKERNELDIR -a \
258523d827efSSimon L. B. Nielsen			-e $BACKUPKERNELDIR/.freebsd-update ]; then
258623d827efSSimon L. B. Nielsen			return 0
258723d827efSSimon L. B. Nielsen		fi
258823d827efSSimon L. B. Nielsen
258923d827efSSimon L. B. Nielsen		# We could not use current directory name, so add counter to
259023d827efSSimon L. B. Nielsen		# the end and try again.
259123d827efSSimon L. B. Nielsen		CNT=$((CNT + 1))
259223d827efSSimon L. B. Nielsen		if [ $CNT -gt 9 ]; then
259323d827efSSimon L. B. Nielsen			echo "Could not find valid backup dir ($BACKUPKERNELDIR)"
259423d827efSSimon L. B. Nielsen			exit 1
259523d827efSSimon L. B. Nielsen		fi
259623d827efSSimon L. B. Nielsen		BACKUPKERNELDIR="`echo $BACKUPKERNELDIR | sed -Ee 's/[0-9]\$//'`"
259723d827efSSimon L. B. Nielsen		BACKUPKERNELDIR="${BACKUPKERNELDIR}${CNT}"
259823d827efSSimon L. B. Nielsen	done
259923d827efSSimon L. B. Nielsen}
260023d827efSSimon L. B. Nielsen
260123d827efSSimon L. B. Nielsen# Backup the current kernel using hardlinks, if not disabled by user.
260223d827efSSimon L. B. Nielsen# Since we delete all files in the directory used for previous backups
260323d827efSSimon L. B. Nielsen# we create a marker file called ".freebsd-update" in the directory so
260423d827efSSimon L. B. Nielsen# we can determine on the next run that the directory was created by
260523d827efSSimon L. B. Nielsen# freebsd-update and we then do not accidentally remove user files in
260623d827efSSimon L. B. Nielsen# the unlikely case that the user has created a directory with a
260723d827efSSimon L. B. Nielsen# conflicting name.
260823d827efSSimon L. B. Nielsenbackup_kernel () {
260923d827efSSimon L. B. Nielsen	# Only make kernel backup is so configured.
261023d827efSSimon L. B. Nielsen	if [ $BACKUPKERNEL != yes ]; then
261123d827efSSimon L. B. Nielsen		return 0
261223d827efSSimon L. B. Nielsen	fi
261323d827efSSimon L. B. Nielsen
261423d827efSSimon L. B. Nielsen	# Decide which directory name to use for kernel backups.
261523d827efSSimon L. B. Nielsen	backup_kernel_finddir
261623d827efSSimon L. B. Nielsen
261723d827efSSimon L. B. Nielsen	# Remove old kernel backup files.  If $BACKUPKERNELDIR was
261823d827efSSimon L. B. Nielsen	# "not ours", backup_kernel_finddir would have exited, so
261923d827efSSimon L. B. Nielsen	# deleting the directory content is as safe as we can make it.
262023d827efSSimon L. B. Nielsen	if [ -d $BACKUPKERNELDIR ]; then
262123d827efSSimon L. B. Nielsen		rm -f $BACKUPKERNELDIR/*
262223d827efSSimon L. B. Nielsen	fi
262323d827efSSimon L. B. Nielsen
262423d827efSSimon L. B. Nielsen	# Create directory for backup if it doesn't exist.
262523d827efSSimon L. B. Nielsen	mkdir -p $BACKUPKERNELDIR
262623d827efSSimon L. B. Nielsen
262723d827efSSimon L. B. Nielsen	# Mark the directory as having been created by freebsd-update.
262823d827efSSimon L. B. Nielsen	touch $BACKUPKERNELDIR/.freebsd-update
262923d827efSSimon L. B. Nielsen	if [ $? -ne 0 ]; then
263023d827efSSimon L. B. Nielsen		echo "Could not create kernel backup directory"
263123d827efSSimon L. B. Nielsen		exit 1
263223d827efSSimon L. B. Nielsen	fi
263323d827efSSimon L. B. Nielsen
263423d827efSSimon L. B. Nielsen	# Disable pathname expansion to be sure *.symbols is not
263523d827efSSimon L. B. Nielsen	# expanded.
263623d827efSSimon L. B. Nielsen	set -f
263723d827efSSimon L. B. Nielsen
263823d827efSSimon L. B. Nielsen	# Use find to ignore symbol files, unless disabled by user.
263923d827efSSimon L. B. Nielsen	if [ $BACKUPKERNELSYMBOLFILES = yes ]; then
264023d827efSSimon L. B. Nielsen		FINDFILTER=""
264123d827efSSimon L. B. Nielsen	else
264223d827efSSimon L. B. Nielsen		FINDFILTER=-"a ! -name *.symbols"
264323d827efSSimon L. B. Nielsen	fi
264423d827efSSimon L. B. Nielsen
264523d827efSSimon L. B. Nielsen	# Backup all the kernel files using hardlinks.
264623d827efSSimon L. B. Nielsen	find $KERNELDIR -type f $FINDFILTER | \
264723d827efSSimon L. B. Nielsen		sed -Ee "s,($KERNELDIR)/?(.*),\1/\2 ${BACKUPKERNELDIR}/\2," | \
264823d827efSSimon L. B. Nielsen		xargs -n 2 cp -pl
264923d827efSSimon L. B. Nielsen
265023d827efSSimon L. B. Nielsen	# Re-enable patchname expansion.
265123d827efSSimon L. B. Nielsen	set +f
265223d827efSSimon L. B. Nielsen}
265323d827efSSimon L. B. Nielsen
265448ffe56aSColin Percival# Install new files
265548ffe56aSColin Percivalinstall_from_index () {
265648ffe56aSColin Percival	# First pass: Do everything apart from setting file flags.  We
265748ffe56aSColin Percival	# can't set flags yet, because schg inhibits hard linking.
265848ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
265948ffe56aSColin Percival	    tr '|' ' ' |
266048ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
266148ffe56aSColin Percival		case ${TYPE} in
266248ffe56aSColin Percival		d)
266348ffe56aSColin Percival			# Create a directory
266448ffe56aSColin Percival			install -d -o ${OWNER} -g ${GROUP}		\
266548ffe56aSColin Percival			    -m ${PERM} ${BASEDIR}/${FPATH}
266648ffe56aSColin Percival			;;
266748ffe56aSColin Percival		f)
266848ffe56aSColin Percival			if [ -z "${LINK}" ]; then
266948ffe56aSColin Percival				# Create a file, without setting flags.
267048ffe56aSColin Percival				gunzip < files/${HASH}.gz > ${HASH}
267148ffe56aSColin Percival				install -S -o ${OWNER} -g ${GROUP}	\
267248ffe56aSColin Percival				    -m ${PERM} ${HASH} ${BASEDIR}/${FPATH}
267348ffe56aSColin Percival				rm ${HASH}
267448ffe56aSColin Percival			else
267548ffe56aSColin Percival				# Create a hard link.
2676e829ed67SColin Percival				ln -f ${BASEDIR}/${LINK} ${BASEDIR}/${FPATH}
267748ffe56aSColin Percival			fi
267848ffe56aSColin Percival			;;
267948ffe56aSColin Percival		L)
268048ffe56aSColin Percival			# Create a symlink
268148ffe56aSColin Percival			ln -sfh ${HASH} ${BASEDIR}/${FPATH}
268248ffe56aSColin Percival			;;
268348ffe56aSColin Percival		esac
268448ffe56aSColin Percival	    done
268548ffe56aSColin Percival
268648ffe56aSColin Percival	# Perform a second pass, adding file flags.
268748ffe56aSColin Percival	tr '|' ' ' < $1 |
268848ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
268948ffe56aSColin Percival		if [ ${TYPE} = "f" ] &&
269048ffe56aSColin Percival		    ! [ ${FLAGS} = "0" ]; then
269148ffe56aSColin Percival			chflags ${FLAGS} ${BASEDIR}/${FPATH}
269248ffe56aSColin Percival		fi
269348ffe56aSColin Percival	    done
269448ffe56aSColin Percival}
269548ffe56aSColin Percival
269648ffe56aSColin Percival# Remove files which we want to delete
269748ffe56aSColin Percivalinstall_delete () {
269848ffe56aSColin Percival	# Generate list of new files
269948ffe56aSColin Percival	cut -f 1 -d '|' < $2 |
270048ffe56aSColin Percival	    sort > newfiles
270148ffe56aSColin Percival
270248ffe56aSColin Percival	# Generate subindex of old files we want to nuke
270348ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
270448ffe56aSColin Percival	    join -t '|' -v 1 - newfiles |
2705bce02f98SColin Percival	    sort -r -k 1,1 -t '|' |
270648ffe56aSColin Percival	    cut -f 1,2 -d '|' |
270748ffe56aSColin Percival	    tr '|' ' ' > killfiles
270848ffe56aSColin Percival
270948ffe56aSColin Percival	# Remove the offending bits
271048ffe56aSColin Percival	while read FPATH TYPE; do
271148ffe56aSColin Percival		case ${TYPE} in
271248ffe56aSColin Percival		d)
271348ffe56aSColin Percival			rmdir ${BASEDIR}/${FPATH}
271448ffe56aSColin Percival			;;
271548ffe56aSColin Percival		f)
271648ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
271748ffe56aSColin Percival			;;
271848ffe56aSColin Percival		L)
271948ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
272048ffe56aSColin Percival			;;
272148ffe56aSColin Percival		esac
272248ffe56aSColin Percival	done < killfiles
272348ffe56aSColin Percival
272448ffe56aSColin Percival	# Clean up
272548ffe56aSColin Percival	rm newfiles killfiles
272648ffe56aSColin Percival}
272748ffe56aSColin Percival
2728db6b0a61SColin Percival# Install new files, delete old files, and update linker.hints
2729db6b0a61SColin Percivalinstall_files () {
2730db6b0a61SColin Percival	# If we haven't already dealt with the kernel, deal with it.
2731db6b0a61SColin Percival	if ! [ -f $1/kerneldone ]; then
2732db6b0a61SColin Percival		grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
2733db6b0a61SColin Percival		grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
2734db6b0a61SColin Percival
273523d827efSSimon L. B. Nielsen		# Backup current kernel before installing a new one
273623d827efSSimon L. B. Nielsen		backup_kernel || return 1
273723d827efSSimon L. B. Nielsen
2738db6b0a61SColin Percival		# Install new files
2739db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2740db6b0a61SColin Percival
2741db6b0a61SColin Percival		# Remove files which need to be deleted
2742db6b0a61SColin Percival		install_delete INDEX-OLD INDEX-NEW || return 1
2743db6b0a61SColin Percival
2744db6b0a61SColin Percival		# Update linker.hints if necessary
2745db6b0a61SColin Percival		if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2746db6b0a61SColin Percival			kldxref -R /boot/ 2>/dev/null
274748ffe56aSColin Percival		fi
2748db6b0a61SColin Percival
2749db6b0a61SColin Percival		# We've finished updating the kernel.
2750db6b0a61SColin Percival		touch $1/kerneldone
2751db6b0a61SColin Percival
2752db6b0a61SColin Percival		# Do we need to ask for a reboot now?
2753db6b0a61SColin Percival		if [ -f $1/kernelfirst ] &&
2754db6b0a61SColin Percival		    [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2755db6b0a61SColin Percival			cat <<-EOF
2756db6b0a61SColin Percival
2757db6b0a61SColin PercivalKernel updates have been installed.  Please reboot and run
2758db6b0a61SColin Percival"$0 install" again to finish installing updates.
2759db6b0a61SColin Percival			EOF
2760db6b0a61SColin Percival			exit 0
2761db6b0a61SColin Percival		fi
2762db6b0a61SColin Percival	fi
2763db6b0a61SColin Percival
2764db6b0a61SColin Percival	# If we haven't already dealt with the world, deal with it.
2765db6b0a61SColin Percival	if ! [ -f $1/worlddone ]; then
2766db6b0a61SColin Percival		# Install new shared libraries next
2767db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2768fd0963d1SColin Percival		    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
2769db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2770db6b0a61SColin Percival
2771db6b0a61SColin Percival		# Deal with everything else
2772db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-OLD |
2773fd0963d1SColin Percival		    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
2774db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2775fd0963d1SColin Percival		    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
2776db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2777db6b0a61SColin Percival		install_delete INDEX-OLD INDEX-NEW || return 1
2778db6b0a61SColin Percival
2779db6b0a61SColin Percival		# Rebuild /etc/spwd.db and /etc/pwd.db if necessary.
2780db6b0a61SColin Percival		if [ /etc/master.passwd -nt /etc/spwd.db ] ||
2781db6b0a61SColin Percival		    [ /etc/master.passwd -nt /etc/pwd.db ]; then
2782db6b0a61SColin Percival			pwd_mkdb /etc/master.passwd
2783db6b0a61SColin Percival		fi
2784db6b0a61SColin Percival
2785db6b0a61SColin Percival		# Rebuild /etc/login.conf.db if necessary.
2786db6b0a61SColin Percival		if [ /etc/login.conf -nt /etc/login.conf.db ]; then
2787db6b0a61SColin Percival			cap_mkdb /etc/login.conf
2788db6b0a61SColin Percival		fi
2789db6b0a61SColin Percival
2790db6b0a61SColin Percival		# We've finished installing the world and deleting old files
2791db6b0a61SColin Percival		# which are not shared libraries.
2792db6b0a61SColin Percival		touch $1/worlddone
2793db6b0a61SColin Percival
2794db6b0a61SColin Percival		# Do we need to ask the user to portupgrade now?
2795db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2796fd0963d1SColin Percival		    grep -E '/lib/.*\.so\.[0-9]+\|' |
2797db6b0a61SColin Percival		    cut -f 1 -d '|' |
2798db6b0a61SColin Percival		    sort > newfiles
2799db6b0a61SColin Percival		if grep -vE '^/boot/' $1/INDEX-OLD |
2800fd0963d1SColin Percival		    grep -E '/lib/.*\.so\.[0-9]+\|' |
2801db6b0a61SColin Percival		    cut -f 1 -d '|' |
2802db6b0a61SColin Percival		    sort |
2803db6b0a61SColin Percival		    join -v 1 - newfiles |
2804db6b0a61SColin Percival		    grep -q .; then
2805db6b0a61SColin Percival			cat <<-EOF
2806db6b0a61SColin Percival
2807db6b0a61SColin PercivalCompleting this upgrade requires removing old shared object files.
2808db6b0a61SColin PercivalPlease rebuild all installed 3rd party software (e.g., programs
2809db6b0a61SColin Percivalinstalled from the ports tree) and then run "$0 install"
2810db6b0a61SColin Percivalagain to finish installing updates.
2811db6b0a61SColin Percival			EOF
2812db6b0a61SColin Percival			rm newfiles
2813db6b0a61SColin Percival			exit 0
2814db6b0a61SColin Percival		fi
2815db6b0a61SColin Percival		rm newfiles
2816db6b0a61SColin Percival	fi
2817db6b0a61SColin Percival
2818db6b0a61SColin Percival	# Remove old shared libraries
2819db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2820fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
2821db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2822fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
2823db6b0a61SColin Percival	install_delete INDEX-OLD INDEX-NEW || return 1
2824db6b0a61SColin Percival
2825db6b0a61SColin Percival	# Remove temporary files
2826db6b0a61SColin Percival	rm INDEX-OLD INDEX-NEW
282748ffe56aSColin Percival}
282848ffe56aSColin Percival
282948ffe56aSColin Percival# Rearrange bits to allow the installed updates to be rolled back
283048ffe56aSColin Percivalinstall_setup_rollback () {
2831db6b0a61SColin Percival	# Remove the "reboot after installing kernel", "kernel updated", and
2832db6b0a61SColin Percival	# "finished installing the world" flags if present -- they are
2833db6b0a61SColin Percival	# irrelevant when rolling back updates.
2834db6b0a61SColin Percival	if [ -f ${BDHASH}-install/kernelfirst ]; then
2835db6b0a61SColin Percival		rm ${BDHASH}-install/kernelfirst
2836db6b0a61SColin Percival		rm ${BDHASH}-install/kerneldone
2837db6b0a61SColin Percival	fi
2838db6b0a61SColin Percival	if [ -f ${BDHASH}-install/worlddone ]; then
2839db6b0a61SColin Percival		rm ${BDHASH}-install/worlddone
2840db6b0a61SColin Percival	fi
2841db6b0a61SColin Percival
284248ffe56aSColin Percival	if [ -L ${BDHASH}-rollback ]; then
284348ffe56aSColin Percival		mv ${BDHASH}-rollback ${BDHASH}-install/rollback
284448ffe56aSColin Percival	fi
284548ffe56aSColin Percival
284648ffe56aSColin Percival	mv ${BDHASH}-install ${BDHASH}-rollback
284748ffe56aSColin Percival}
284848ffe56aSColin Percival
284948ffe56aSColin Percival# Actually install updates
285048ffe56aSColin Percivalinstall_run () {
285148ffe56aSColin Percival	echo -n "Installing updates..."
285248ffe56aSColin Percival
285348ffe56aSColin Percival	# Make sure we have all the files we should have
285448ffe56aSColin Percival	install_verify ${BDHASH}-install/INDEX-OLD	\
285548ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
285648ffe56aSColin Percival
285748ffe56aSColin Percival	# Remove system immutable flag from files
285848ffe56aSColin Percival	install_unschg ${BDHASH}-install/INDEX-OLD	\
285948ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
286048ffe56aSColin Percival
2861db6b0a61SColin Percival	# Install new files, delete old files, and update linker.hints
2862db6b0a61SColin Percival	install_files ${BDHASH}-install || return 1
286348ffe56aSColin Percival
286448ffe56aSColin Percival	# Rearrange bits to allow the installed updates to be rolled back
286548ffe56aSColin Percival	install_setup_rollback
286648ffe56aSColin Percival
286748ffe56aSColin Percival	echo " done."
286848ffe56aSColin Percival}
286948ffe56aSColin Percival
287048ffe56aSColin Percival# Rearrange bits to allow the previous set of updates to be rolled back next.
287148ffe56aSColin Percivalrollback_setup_rollback () {
287248ffe56aSColin Percival	if [ -L ${BDHASH}-rollback/rollback ]; then
287348ffe56aSColin Percival		mv ${BDHASH}-rollback/rollback rollback-tmp
287448ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
287548ffe56aSColin Percival		rm ${BDHASH}-rollback
287648ffe56aSColin Percival		mv rollback-tmp ${BDHASH}-rollback
287748ffe56aSColin Percival	else
287848ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
287948ffe56aSColin Percival		rm ${BDHASH}-rollback
288048ffe56aSColin Percival	fi
288148ffe56aSColin Percival}
288248ffe56aSColin Percival
2883db6b0a61SColin Percival# Install old files, delete new files, and update linker.hints
2884db6b0a61SColin Percivalrollback_files () {
28851ec4fb3aSColin Percival	# Install old shared library files which don't have the same path as
28861ec4fb3aSColin Percival	# a new shared library file.
28871ec4fb3aSColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2888fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
28891ec4fb3aSColin Percival	    cut -f 1 -d '|' |
28901ec4fb3aSColin Percival	    sort > INDEX-NEW.libs.flist
2891db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2892fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
28931ec4fb3aSColin Percival	    sort -k 1,1 -t '|' - |
28941ec4fb3aSColin Percival	    join -t '|' -v 1 - INDEX-NEW.libs.flist > INDEX-OLD
2895db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
2896db6b0a61SColin Percival
2897db6b0a61SColin Percival	# Deal with files which are neither kernel nor shared library
2898db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2899fd0963d1SColin Percival	    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
2900db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2901fd0963d1SColin Percival	    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
2902db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
2903db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
2904db6b0a61SColin Percival
29051ec4fb3aSColin Percival	# Install any old shared library files which we didn't install above.
29061ec4fb3aSColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2907fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
29081ec4fb3aSColin Percival	    sort -k 1,1 -t '|' - |
29091ec4fb3aSColin Percival	    join -t '|' - INDEX-NEW.libs.flist > INDEX-OLD
29101ec4fb3aSColin Percival	install_from_index INDEX-OLD || return 1
29111ec4fb3aSColin Percival
2912db6b0a61SColin Percival	# Delete unneeded shared library files
2913db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2914fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
2915db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2916fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
2917db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
2918db6b0a61SColin Percival
2919db6b0a61SColin Percival	# Deal with kernel files
2920db6b0a61SColin Percival	grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
2921db6b0a61SColin Percival	grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
2922db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
2923db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
2924db6b0a61SColin Percival	if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2925db6b0a61SColin Percival		kldxref -R /boot/ 2>/dev/null
2926db6b0a61SColin Percival	fi
2927db6b0a61SColin Percival
2928db6b0a61SColin Percival	# Remove temporary files
29290e0d8d5aSColin Percival	rm INDEX-OLD INDEX-NEW INDEX-NEW.libs.flist
2930db6b0a61SColin Percival}
2931db6b0a61SColin Percival
293248ffe56aSColin Percival# Actually rollback updates
293348ffe56aSColin Percivalrollback_run () {
293448ffe56aSColin Percival	echo -n "Uninstalling updates..."
293548ffe56aSColin Percival
293648ffe56aSColin Percival	# If there are updates waiting to be installed, remove them; we
293748ffe56aSColin Percival	# want the user to re-run 'fetch' after rolling back updates.
293848ffe56aSColin Percival	if [ -L ${BDHASH}-install ]; then
293948ffe56aSColin Percival		rm -r ${BDHASH}-install/
294048ffe56aSColin Percival		rm ${BDHASH}-install
294148ffe56aSColin Percival	fi
294248ffe56aSColin Percival
294348ffe56aSColin Percival	# Make sure we have all the files we should have
294448ffe56aSColin Percival	install_verify ${BDHASH}-rollback/INDEX-NEW	\
294548ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
294648ffe56aSColin Percival
294748ffe56aSColin Percival	# Remove system immutable flag from files
294848ffe56aSColin Percival	install_unschg ${BDHASH}-rollback/INDEX-NEW	\
294948ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
295048ffe56aSColin Percival
2951db6b0a61SColin Percival	# Install old files, delete new files, and update linker.hints
2952db6b0a61SColin Percival	rollback_files ${BDHASH}-rollback || return 1
295348ffe56aSColin Percival
295448ffe56aSColin Percival	# Remove the rollback directory and the symlink pointing to it; and
295548ffe56aSColin Percival	# rearrange bits to allow the previous set of updates to be rolled
295648ffe56aSColin Percival	# back next.
295748ffe56aSColin Percival	rollback_setup_rollback
295848ffe56aSColin Percival
295948ffe56aSColin Percival	echo " done."
296048ffe56aSColin Percival}
296148ffe56aSColin Percival
296208e23beeSColin Percival# Compare INDEX-ALL and INDEX-PRESENT and print warnings about differences.
296308e23beeSColin PercivalIDS_compare () {
2964bb10a826SColin Percival	# Get all the lines which mismatch in something other than file
2965bb10a826SColin Percival	# flags.  We ignore file flags because sysinstall doesn't seem to
2966bb10a826SColin Percival	# set them when it installs FreeBSD; warning about these adds a
2967bb10a826SColin Percival	# very large amount of noise.
2968bb10a826SColin Percival	cut -f 1-5,7-8 -d '|' $1 > $1.noflags
2969bb10a826SColin Percival	sort -k 1,1 -t '|' $1.noflags > $1.sorted
2970bb10a826SColin Percival	cut -f 1-5,7-8 -d '|' $2 |
2971bb10a826SColin Percival	    comm -13 $1.noflags - |
2972bb10a826SColin Percival	    fgrep -v '|-|||||' |
297308e23beeSColin Percival	    sort -k 1,1 -t '|' |
297408e23beeSColin Percival	    join -t '|' $1.sorted - > INDEX-NOTMATCHING
297508e23beeSColin Percival
297608e23beeSColin Percival	# Ignore files which match IDSIGNOREPATHS.
297708e23beeSColin Percival	for X in ${IDSIGNOREPATHS}; do
297808e23beeSColin Percival		grep -E "^${X}" INDEX-NOTMATCHING
297908e23beeSColin Percival	done |
298008e23beeSColin Percival	    sort -u |
298108e23beeSColin Percival	    comm -13 - INDEX-NOTMATCHING > INDEX-NOTMATCHING.tmp
298208e23beeSColin Percival	mv INDEX-NOTMATCHING.tmp INDEX-NOTMATCHING
298308e23beeSColin Percival
298408e23beeSColin Percival	# Go through the lines and print warnings.
298508e23beeSColin Percival	while read LINE; do
298608e23beeSColin Percival		FPATH=`echo "${LINE}" | cut -f 1 -d '|'`
298708e23beeSColin Percival		TYPE=`echo "${LINE}" | cut -f 2 -d '|'`
298808e23beeSColin Percival		OWNER=`echo "${LINE}" | cut -f 3 -d '|'`
298908e23beeSColin Percival		GROUP=`echo "${LINE}" | cut -f 4 -d '|'`
299008e23beeSColin Percival		PERM=`echo "${LINE}" | cut -f 5 -d '|'`
2991bb10a826SColin Percival		HASH=`echo "${LINE}" | cut -f 6 -d '|'`
2992bb10a826SColin Percival		LINK=`echo "${LINE}" | cut -f 7 -d '|'`
2993bb10a826SColin Percival		P_TYPE=`echo "${LINE}" | cut -f 8 -d '|'`
2994bb10a826SColin Percival		P_OWNER=`echo "${LINE}" | cut -f 9 -d '|'`
2995bb10a826SColin Percival		P_GROUP=`echo "${LINE}" | cut -f 10 -d '|'`
2996bb10a826SColin Percival		P_PERM=`echo "${LINE}" | cut -f 11 -d '|'`
2997bb10a826SColin Percival		P_HASH=`echo "${LINE}" | cut -f 12 -d '|'`
2998bb10a826SColin Percival		P_LINK=`echo "${LINE}" | cut -f 13 -d '|'`
299908e23beeSColin Percival
300008e23beeSColin Percival		# Warn about different object types.
300108e23beeSColin Percival		if ! [ "${TYPE}" = "${P_TYPE}" ]; then
300208e23beeSColin Percival			echo -n "${FPATH} is a "
300308e23beeSColin Percival			case "${P_TYPE}" in
300408e23beeSColin Percival			f)	echo -n "regular file, "
300508e23beeSColin Percival				;;
300608e23beeSColin Percival			d)	echo -n "directory, "
300708e23beeSColin Percival				;;
300808e23beeSColin Percival			L)	echo -n "symlink, "
300908e23beeSColin Percival				;;
301008e23beeSColin Percival			esac
301108e23beeSColin Percival			echo -n "but should be a "
301208e23beeSColin Percival			case "${TYPE}" in
301308e23beeSColin Percival			f)	echo -n "regular file."
301408e23beeSColin Percival				;;
301508e23beeSColin Percival			d)	echo -n "directory."
301608e23beeSColin Percival				;;
301708e23beeSColin Percival			L)	echo -n "symlink."
301808e23beeSColin Percival				;;
301908e23beeSColin Percival			esac
302008e23beeSColin Percival			echo
302108e23beeSColin Percival
302208e23beeSColin Percival			# Skip other tests, since they don't make sense if
302308e23beeSColin Percival			# we're comparing different object types.
302408e23beeSColin Percival			continue
302508e23beeSColin Percival		fi
302608e23beeSColin Percival
302708e23beeSColin Percival		# Warn about different owners.
302808e23beeSColin Percival		if ! [ "${OWNER}" = "${P_OWNER}" ]; then
302908e23beeSColin Percival			echo -n "${FPATH} is owned by user id ${P_OWNER}, "
303008e23beeSColin Percival			echo "but should be owned by user id ${OWNER}."
303108e23beeSColin Percival		fi
303208e23beeSColin Percival
303308e23beeSColin Percival		# Warn about different groups.
303408e23beeSColin Percival		if ! [ "${GROUP}" = "${P_GROUP}" ]; then
303508e23beeSColin Percival			echo -n "${FPATH} is owned by group id ${P_GROUP}, "
303608e23beeSColin Percival			echo "but should be owned by group id ${GROUP}."
303708e23beeSColin Percival		fi
303808e23beeSColin Percival
303908e23beeSColin Percival		# Warn about different permissions.  We do not warn about
304008e23beeSColin Percival		# different permissions on symlinks, since some archivers
304108e23beeSColin Percival		# don't extract symlink permissions correctly and they are
304208e23beeSColin Percival		# ignored anyway.
304308e23beeSColin Percival		if ! [ "${PERM}" = "${P_PERM}" ] &&
304408e23beeSColin Percival		    ! [ "${TYPE}" = "L" ]; then
304508e23beeSColin Percival			echo -n "${FPATH} has ${P_PERM} permissions, "
304608e23beeSColin Percival			echo "but should have ${PERM} permissions."
304708e23beeSColin Percival		fi
304808e23beeSColin Percival
304908e23beeSColin Percival		# Warn about different file hashes / symlink destinations.
305008e23beeSColin Percival		if ! [ "${HASH}" = "${P_HASH}" ]; then
305108e23beeSColin Percival			if [ "${TYPE}" = "L" ]; then
305208e23beeSColin Percival				echo -n "${FPATH} is a symlink to ${P_HASH}, "
305308e23beeSColin Percival				echo "but should be a symlink to ${HASH}."
305408e23beeSColin Percival			fi
305508e23beeSColin Percival			if [ "${TYPE}" = "f" ]; then
305608e23beeSColin Percival				echo -n "${FPATH} has SHA256 hash ${P_HASH}, "
305708e23beeSColin Percival				echo "but should have SHA256 hash ${HASH}."
305808e23beeSColin Percival			fi
305908e23beeSColin Percival		fi
306008e23beeSColin Percival
306108e23beeSColin Percival		# We don't warn about different hard links, since some
306208e23beeSColin Percival		# some archivers break hard links, and as long as the
306308e23beeSColin Percival		# underlying data is correct they really don't matter.
306408e23beeSColin Percival	done < INDEX-NOTMATCHING
306508e23beeSColin Percival
306608e23beeSColin Percival	# Clean up
3067bb10a826SColin Percival	rm $1 $1.noflags $1.sorted $2 INDEX-NOTMATCHING
306808e23beeSColin Percival}
306908e23beeSColin Percival
307008e23beeSColin Percival# Do the work involved in comparing the system to a "known good" index
307108e23beeSColin PercivalIDS_run () {
307208e23beeSColin Percival	workdir_init || return 1
307308e23beeSColin Percival
307408e23beeSColin Percival	# Prepare the mirror list.
307508e23beeSColin Percival	fetch_pick_server_init && fetch_pick_server
307608e23beeSColin Percival
307708e23beeSColin Percival	# Try to fetch the public key until we run out of servers.
307808e23beeSColin Percival	while ! fetch_key; do
307908e23beeSColin Percival		fetch_pick_server || return 1
308008e23beeSColin Percival	done
308108e23beeSColin Percival
308208e23beeSColin Percival	# Try to fetch the metadata index signature ("tag") until we run
308308e23beeSColin Percival	# out of available servers; and sanity check the downloaded tag.
308408e23beeSColin Percival	while ! fetch_tag; do
308508e23beeSColin Percival		fetch_pick_server || return 1
308608e23beeSColin Percival	done
308708e23beeSColin Percival	fetch_tagsanity || return 1
308808e23beeSColin Percival
308908e23beeSColin Percival	# Fetch INDEX-OLD and INDEX-ALL.
309008e23beeSColin Percival	fetch_metadata INDEX-OLD INDEX-ALL || return 1
309108e23beeSColin Percival
309208e23beeSColin Percival	# Generate filtered INDEX-OLD and INDEX-ALL files containing only
309308e23beeSColin Percival	# the components we want and without anything marked as "Ignore".
309408e23beeSColin Percival	fetch_filter_metadata INDEX-OLD || return 1
309508e23beeSColin Percival	fetch_filter_metadata INDEX-ALL || return 1
309608e23beeSColin Percival
309708e23beeSColin Percival	# Merge the INDEX-OLD and INDEX-ALL files into INDEX-ALL.
309808e23beeSColin Percival	sort INDEX-OLD INDEX-ALL > INDEX-ALL.tmp
309908e23beeSColin Percival	mv INDEX-ALL.tmp INDEX-ALL
310008e23beeSColin Percival	rm INDEX-OLD
310108e23beeSColin Percival
310208e23beeSColin Percival	# Translate /boot/${KERNCONF} to ${KERNELDIR}
310308e23beeSColin Percival	fetch_filter_kernel_names INDEX-ALL ${KERNCONF}
310408e23beeSColin Percival
310508e23beeSColin Percival	# Inspect the system and generate an INDEX-PRESENT file.
310608e23beeSColin Percival	fetch_inspect_system INDEX-ALL INDEX-PRESENT /dev/null || return 1
310708e23beeSColin Percival
310808e23beeSColin Percival	# Compare INDEX-ALL and INDEX-PRESENT and print warnings about any
310908e23beeSColin Percival	# differences.
311008e23beeSColin Percival	IDS_compare INDEX-ALL INDEX-PRESENT
311108e23beeSColin Percival}
311208e23beeSColin Percival
311348ffe56aSColin Percival#### Main functions -- call parameter-handling and core functions
311448ffe56aSColin Percival
311548ffe56aSColin Percival# Using the command line, configuration file, and defaults,
311648ffe56aSColin Percival# set all the parameters which are needed later.
311748ffe56aSColin Percivalget_params () {
311848ffe56aSColin Percival	init_params
311948ffe56aSColin Percival	parse_cmdline $@
312048ffe56aSColin Percival	parse_conffile
312148ffe56aSColin Percival	default_params
312248ffe56aSColin Percival}
312348ffe56aSColin Percival
312448ffe56aSColin Percival# Fetch command.  Make sure that we're being called
312548ffe56aSColin Percival# interactively, then run fetch_check_params and fetch_run
312648ffe56aSColin Percivalcmd_fetch () {
312748ffe56aSColin Percival	if [ ! -t 0 ]; then
312848ffe56aSColin Percival		echo -n "`basename $0` fetch should not "
312948ffe56aSColin Percival		echo "be run non-interactively."
313048ffe56aSColin Percival		echo "Run `basename $0` cron instead."
313148ffe56aSColin Percival		exit 1
313248ffe56aSColin Percival	fi
313348ffe56aSColin Percival	fetch_check_params
313448ffe56aSColin Percival	fetch_run || exit 1
313548ffe56aSColin Percival}
313648ffe56aSColin Percival
313748ffe56aSColin Percival# Cron command.  Make sure the parameters are sensible; wait
313848ffe56aSColin Percival# rand(3600) seconds; then fetch updates.  While fetching updates,
313948ffe56aSColin Percival# send output to a temporary file; only print that file if the
314048ffe56aSColin Percival# fetching failed.
314148ffe56aSColin Percivalcmd_cron () {
314248ffe56aSColin Percival	fetch_check_params
314348ffe56aSColin Percival	sleep `jot -r 1 0 3600`
314448ffe56aSColin Percival
314548ffe56aSColin Percival	TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1
314648ffe56aSColin Percival	if ! fetch_run >> ${TMPFILE} ||
314748ffe56aSColin Percival	    ! grep -q "No updates needed" ${TMPFILE} ||
314848ffe56aSColin Percival	    [ ${VERBOSELEVEL} = "debug" ]; then
314948ffe56aSColin Percival		mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE}
315048ffe56aSColin Percival	fi
315148ffe56aSColin Percival
315248ffe56aSColin Percival	rm ${TMPFILE}
315348ffe56aSColin Percival}
315448ffe56aSColin Percival
3155db6b0a61SColin Percival# Fetch files for upgrading to a new release.
3156db6b0a61SColin Percivalcmd_upgrade () {
3157db6b0a61SColin Percival	upgrade_check_params
3158db6b0a61SColin Percival	upgrade_run || exit 1
3159db6b0a61SColin Percival}
3160db6b0a61SColin Percival
316148ffe56aSColin Percival# Install downloaded updates.
316248ffe56aSColin Percivalcmd_install () {
316348ffe56aSColin Percival	install_check_params
316448ffe56aSColin Percival	install_run || exit 1
316548ffe56aSColin Percival}
316648ffe56aSColin Percival
316748ffe56aSColin Percival# Rollback most recently installed updates.
316848ffe56aSColin Percivalcmd_rollback () {
316948ffe56aSColin Percival	rollback_check_params
317048ffe56aSColin Percival	rollback_run || exit 1
317148ffe56aSColin Percival}
317248ffe56aSColin Percival
317308e23beeSColin Percival# Compare system against a "known good" index.
317408e23beeSColin Percivalcmd_IDS () {
317508e23beeSColin Percival	IDS_check_params
317608e23beeSColin Percival	IDS_run || exit 1
317708e23beeSColin Percival}
317808e23beeSColin Percival
317948ffe56aSColin Percival#### Entry point
318048ffe56aSColin Percival
318148ffe56aSColin Percival# Make sure we find utilities from the base system
318248ffe56aSColin Percivalexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
318348ffe56aSColin Percival
3184f2890dbdSColin Percival# Set LC_ALL in order to avoid problems with character ranges like [A-Z].
3185f2890dbdSColin Percivalexport LC_ALL=C
3186f2890dbdSColin Percival
318748ffe56aSColin Percivalget_params $@
318848ffe56aSColin Percivalfor COMMAND in ${COMMANDS}; do
318948ffe56aSColin Percival	cmd_${COMMAND}
319048ffe56aSColin Percivaldone
3191