xref: /freebsd/usr.sbin/freebsd-update/freebsd-update.sh (revision 7c06c7c509a9c4b54c557a563686b097d24ce293)
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)
468935f242SAllan Jude  -F           -- Force a fetch operation to proceed
4748ffe56aSColin Percival  -k KEY       -- Trust an RSA key with SHA256 hash of KEY
48db6b0a61SColin Percival  -r release   -- Target for upgrade (e.g., 6.2-RELEASE)
4948ffe56aSColin Percival  -s server    -- Server from which to fetch updates
5048ffe56aSColin Percival                  (default: update.FreeBSD.org)
5148ffe56aSColin Percival  -t address   -- Mail output of cron command, if any, to address
5248ffe56aSColin Percival                  (default: root)
538935f242SAllan Jude  --not-running-from-cron
548935f242SAllan Jude               -- Run without a tty, for use by automated tools
5548ffe56aSColin PercivalCommands:
5648ffe56aSColin Percival  fetch        -- Fetch updates from server
5748ffe56aSColin Percival  cron         -- Sleep rand(3600) seconds, fetch updates, and send an
5848ffe56aSColin Percival                  email if updates were found
59db6b0a61SColin Percival  upgrade      -- Fetch upgrades to FreeBSD version specified via -r option
60db6b0a61SColin Percival  install      -- Install downloaded updates or upgrades
6148ffe56aSColin Percival  rollback     -- Uninstall most recently installed updates
6208e23beeSColin Percival  IDS          -- Compare the system against an index of "known good" files.
6348ffe56aSColin PercivalEOF
6448ffe56aSColin Percival	exit 0
6548ffe56aSColin Percival}
6648ffe56aSColin Percival
6748ffe56aSColin Percival#### Configuration processing functions
6848ffe56aSColin Percival
6948ffe56aSColin Percival#-
7048ffe56aSColin Percival# Configuration options are set in the following order of priority:
7148ffe56aSColin Percival# 1. Command line options
7248ffe56aSColin Percival# 2. Configuration file options
7348ffe56aSColin Percival# 3. Default options
7448ffe56aSColin Percival# In addition, certain options (e.g., IgnorePaths) can be specified multiple
7548ffe56aSColin Percival# times and (as long as these are all in the same place, e.g., inside the
7648ffe56aSColin Percival# configuration file) they will accumulate.  Finally, because the path to the
7748ffe56aSColin Percival# configuration file can be specified at the command line, the entire command
7848ffe56aSColin Percival# line must be processed before we start reading the configuration file.
7948ffe56aSColin Percival#
8048ffe56aSColin Percival# Sound like a mess?  It is.  Here's how we handle this:
8148ffe56aSColin Percival# 1. Initialize CONFFILE and all the options to "".
8248ffe56aSColin Percival# 2. Process the command line.  Throw an error if a non-accumulating option
8348ffe56aSColin Percival#    is specified twice.
8448ffe56aSColin Percival# 3. If CONFFILE is "", set CONFFILE to /etc/freebsd-update.conf .
8548ffe56aSColin Percival# 4. For all the configuration options X, set X_saved to X.
8648ffe56aSColin Percival# 5. Initialize all the options to "".
8748ffe56aSColin Percival# 6. Read CONFFILE line by line, parsing options.
8848ffe56aSColin Percival# 7. For each configuration option X, set X to X_saved iff X_saved is not "".
8948ffe56aSColin Percival# 8. Repeat steps 4-7, except setting options to their default values at (6).
9048ffe56aSColin Percival
9148ffe56aSColin PercivalCONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE
9248ffe56aSColin Percival    KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED
9308e23beeSColin Percival    BASEDIR VERBOSELEVEL TARGETRELEASE STRICTCOMPONENTS MERGECHANGES
9423d827efSSimon L. B. Nielsen    IDSIGNOREPATHS BACKUPKERNEL BACKUPKERNELDIR BACKUPKERNELSYMBOLFILES"
9548ffe56aSColin Percival
9648ffe56aSColin Percival# Set all the configuration options to "".
9748ffe56aSColin Percivalnullconfig () {
9848ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
9948ffe56aSColin Percival		eval ${X}=""
10048ffe56aSColin Percival	done
10148ffe56aSColin Percival}
10248ffe56aSColin Percival
10348ffe56aSColin Percival# For each configuration option X, set X_saved to X.
10448ffe56aSColin Percivalsaveconfig () {
10548ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
10648ffe56aSColin Percival		eval ${X}_saved=\$${X}
10748ffe56aSColin Percival	done
10848ffe56aSColin Percival}
10948ffe56aSColin Percival
11048ffe56aSColin Percival# For each configuration option X, set X to X_saved if X_saved is not "".
11148ffe56aSColin Percivalmergeconfig () {
11248ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
11348ffe56aSColin Percival		eval _=\$${X}_saved
11448ffe56aSColin Percival		if ! [ -z "${_}" ]; then
11548ffe56aSColin Percival			eval ${X}=\$${X}_saved
11648ffe56aSColin Percival		fi
11748ffe56aSColin Percival	done
11848ffe56aSColin Percival}
11948ffe56aSColin Percival
12048ffe56aSColin Percival# Set the trusted keyprint.
12148ffe56aSColin Percivalconfig_KeyPrint () {
12248ffe56aSColin Percival	if [ -z ${KEYPRINT} ]; then
12348ffe56aSColin Percival		KEYPRINT=$1
12448ffe56aSColin Percival	else
12548ffe56aSColin Percival		return 1
12648ffe56aSColin Percival	fi
12748ffe56aSColin Percival}
12848ffe56aSColin Percival
12948ffe56aSColin Percival# Set the working directory.
13048ffe56aSColin Percivalconfig_WorkDir () {
13148ffe56aSColin Percival	if [ -z ${WORKDIR} ]; then
13248ffe56aSColin Percival		WORKDIR=$1
13348ffe56aSColin Percival	else
13448ffe56aSColin Percival		return 1
13548ffe56aSColin Percival	fi
13648ffe56aSColin Percival}
13748ffe56aSColin Percival
13848ffe56aSColin Percival# Set the name of the server (pool) from which to fetch updates
13948ffe56aSColin Percivalconfig_ServerName () {
14048ffe56aSColin Percival	if [ -z ${SERVERNAME} ]; then
14148ffe56aSColin Percival		SERVERNAME=$1
14248ffe56aSColin Percival	else
14348ffe56aSColin Percival		return 1
14448ffe56aSColin Percival	fi
14548ffe56aSColin Percival}
14648ffe56aSColin Percival
14748ffe56aSColin Percival# Set the address to which 'cron' output will be mailed.
14848ffe56aSColin Percivalconfig_MailTo () {
14948ffe56aSColin Percival	if [ -z ${MAILTO} ]; then
15048ffe56aSColin Percival		MAILTO=$1
15148ffe56aSColin Percival	else
15248ffe56aSColin Percival		return 1
15348ffe56aSColin Percival	fi
15448ffe56aSColin Percival}
15548ffe56aSColin Percival
15648ffe56aSColin Percival# Set whether FreeBSD Update is allowed to add files (or directories, or
15748ffe56aSColin Percival# symlinks) which did not previously exist.
15848ffe56aSColin Percivalconfig_AllowAdd () {
15948ffe56aSColin Percival	if [ -z ${ALLOWADD} ]; then
16048ffe56aSColin Percival		case $1 in
16148ffe56aSColin Percival		[Yy][Ee][Ss])
16248ffe56aSColin Percival			ALLOWADD=yes
16348ffe56aSColin Percival			;;
16448ffe56aSColin Percival		[Nn][Oo])
16548ffe56aSColin Percival			ALLOWADD=no
16648ffe56aSColin Percival			;;
16748ffe56aSColin Percival		*)
16848ffe56aSColin Percival			return 1
16948ffe56aSColin Percival			;;
17048ffe56aSColin Percival		esac
17148ffe56aSColin Percival	else
17248ffe56aSColin Percival		return 1
17348ffe56aSColin Percival	fi
17448ffe56aSColin Percival}
17548ffe56aSColin Percival
17648ffe56aSColin Percival# Set whether FreeBSD Update is allowed to remove files/directories/symlinks.
17748ffe56aSColin Percivalconfig_AllowDelete () {
17848ffe56aSColin Percival	if [ -z ${ALLOWDELETE} ]; then
17948ffe56aSColin Percival		case $1 in
18048ffe56aSColin Percival		[Yy][Ee][Ss])
18148ffe56aSColin Percival			ALLOWDELETE=yes
18248ffe56aSColin Percival			;;
18348ffe56aSColin Percival		[Nn][Oo])
18448ffe56aSColin Percival			ALLOWDELETE=no
18548ffe56aSColin Percival			;;
18648ffe56aSColin Percival		*)
18748ffe56aSColin Percival			return 1
18848ffe56aSColin Percival			;;
18948ffe56aSColin Percival		esac
19048ffe56aSColin Percival	else
19148ffe56aSColin Percival		return 1
19248ffe56aSColin Percival	fi
19348ffe56aSColin Percival}
19448ffe56aSColin Percival
19548ffe56aSColin Percival# Set whether FreeBSD Update should keep existing inode ownership,
19648ffe56aSColin Percival# permissions, and flags, in the event that they have been modified locally
19748ffe56aSColin Percival# after the release.
19848ffe56aSColin Percivalconfig_KeepModifiedMetadata () {
19948ffe56aSColin Percival	if [ -z ${KEEPMODIFIEDMETADATA} ]; then
20048ffe56aSColin Percival		case $1 in
20148ffe56aSColin Percival		[Yy][Ee][Ss])
20248ffe56aSColin Percival			KEEPMODIFIEDMETADATA=yes
20348ffe56aSColin Percival			;;
20448ffe56aSColin Percival		[Nn][Oo])
20548ffe56aSColin Percival			KEEPMODIFIEDMETADATA=no
20648ffe56aSColin Percival			;;
20748ffe56aSColin Percival		*)
20848ffe56aSColin Percival			return 1
20948ffe56aSColin Percival			;;
21048ffe56aSColin Percival		esac
21148ffe56aSColin Percival	else
21248ffe56aSColin Percival		return 1
21348ffe56aSColin Percival	fi
21448ffe56aSColin Percival}
21548ffe56aSColin Percival
21648ffe56aSColin Percival# Add to the list of components which should be kept updated.
21748ffe56aSColin Percivalconfig_Components () {
21848ffe56aSColin Percival	for C in $@; do
21948ffe56aSColin Percival		COMPONENTS="${COMPONENTS} ${C}"
22048ffe56aSColin Percival	done
22148ffe56aSColin Percival}
22248ffe56aSColin Percival
22348ffe56aSColin Percival# Add to the list of paths under which updates will be ignored.
22448ffe56aSColin Percivalconfig_IgnorePaths () {
22548ffe56aSColin Percival	for C in $@; do
22648ffe56aSColin Percival		IGNOREPATHS="${IGNOREPATHS} ${C}"
22748ffe56aSColin Percival	done
22848ffe56aSColin Percival}
22948ffe56aSColin Percival
23008e23beeSColin Percival# Add to the list of paths which IDS should ignore.
23108e23beeSColin Percivalconfig_IDSIgnorePaths () {
23208e23beeSColin Percival	for C in $@; do
23308e23beeSColin Percival		IDSIGNOREPATHS="${IDSIGNOREPATHS} ${C}"
23408e23beeSColin Percival	done
23508e23beeSColin Percival}
23608e23beeSColin Percival
23748ffe56aSColin Percival# Add to the list of paths within which updates will be performed only if the
23848ffe56aSColin Percival# file on disk has not been modified locally.
23948ffe56aSColin Percivalconfig_UpdateIfUnmodified () {
24048ffe56aSColin Percival	for C in $@; do
24148ffe56aSColin Percival		UPDATEIFUNMODIFIED="${UPDATEIFUNMODIFIED} ${C}"
24248ffe56aSColin Percival	done
24348ffe56aSColin Percival}
24448ffe56aSColin Percival
245db6b0a61SColin Percival# Add to the list of paths within which updates to text files will be merged
246db6b0a61SColin Percival# instead of overwritten.
247db6b0a61SColin Percivalconfig_MergeChanges () {
248db6b0a61SColin Percival	for C in $@; do
249db6b0a61SColin Percival		MERGECHANGES="${MERGECHANGES} ${C}"
250db6b0a61SColin Percival	done
251db6b0a61SColin Percival}
252db6b0a61SColin Percival
25348ffe56aSColin Percival# Work on a FreeBSD installation mounted under $1
25448ffe56aSColin Percivalconfig_BaseDir () {
25548ffe56aSColin Percival	if [ -z ${BASEDIR} ]; then
25648ffe56aSColin Percival		BASEDIR=$1
25748ffe56aSColin Percival	else
25848ffe56aSColin Percival		return 1
25948ffe56aSColin Percival	fi
26048ffe56aSColin Percival}
26148ffe56aSColin Percival
262db6b0a61SColin Percival# When fetching upgrades, should we assume the user wants exactly the
263db6b0a61SColin Percival# components listed in COMPONENTS, rather than trying to guess based on
264db6b0a61SColin Percival# what's currently installed?
265db6b0a61SColin Percivalconfig_StrictComponents () {
266db6b0a61SColin Percival	if [ -z ${STRICTCOMPONENTS} ]; then
267db6b0a61SColin Percival		case $1 in
268db6b0a61SColin Percival		[Yy][Ee][Ss])
269db6b0a61SColin Percival			STRICTCOMPONENTS=yes
270db6b0a61SColin Percival			;;
271db6b0a61SColin Percival		[Nn][Oo])
272db6b0a61SColin Percival			STRICTCOMPONENTS=no
273db6b0a61SColin Percival			;;
274db6b0a61SColin Percival		*)
275db6b0a61SColin Percival			return 1
276db6b0a61SColin Percival			;;
277db6b0a61SColin Percival		esac
278db6b0a61SColin Percival	else
279db6b0a61SColin Percival		return 1
280db6b0a61SColin Percival	fi
281db6b0a61SColin Percival}
282db6b0a61SColin Percival
283db6b0a61SColin Percival# Upgrade to FreeBSD $1
284db6b0a61SColin Percivalconfig_TargetRelease () {
285db6b0a61SColin Percival	if [ -z ${TARGETRELEASE} ]; then
286db6b0a61SColin Percival		TARGETRELEASE=$1
287db6b0a61SColin Percival	else
288db6b0a61SColin Percival		return 1
289db6b0a61SColin Percival	fi
290d23dc1eeSColin Percival	if echo ${TARGETRELEASE} | grep -qE '^[0-9.]+$'; then
291d23dc1eeSColin Percival		TARGETRELEASE="${TARGETRELEASE}-RELEASE"
292d23dc1eeSColin Percival	fi
293db6b0a61SColin Percival}
294db6b0a61SColin Percival
29548ffe56aSColin Percival# Define what happens to output of utilities
29648ffe56aSColin Percivalconfig_VerboseLevel () {
29748ffe56aSColin Percival	if [ -z ${VERBOSELEVEL} ]; then
29848ffe56aSColin Percival		case $1 in
29948ffe56aSColin Percival		[Dd][Ee][Bb][Uu][Gg])
30048ffe56aSColin Percival			VERBOSELEVEL=debug
30148ffe56aSColin Percival			;;
30248ffe56aSColin Percival		[Nn][Oo][Ss][Tt][Aa][Tt][Ss])
30348ffe56aSColin Percival			VERBOSELEVEL=nostats
30448ffe56aSColin Percival			;;
30548ffe56aSColin Percival		[Ss][Tt][Aa][Tt][Ss])
30648ffe56aSColin Percival			VERBOSELEVEL=stats
30748ffe56aSColin Percival			;;
30848ffe56aSColin Percival		*)
30948ffe56aSColin Percival			return 1
31048ffe56aSColin Percival			;;
31148ffe56aSColin Percival		esac
31248ffe56aSColin Percival	else
31348ffe56aSColin Percival		return 1
31448ffe56aSColin Percival	fi
31548ffe56aSColin Percival}
31648ffe56aSColin Percival
31723d827efSSimon L. B. Nielsenconfig_BackupKernel () {
31823d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNEL} ]; then
31923d827efSSimon L. B. Nielsen		case $1 in
32023d827efSSimon L. B. Nielsen		[Yy][Ee][Ss])
32123d827efSSimon L. B. Nielsen			BACKUPKERNEL=yes
32223d827efSSimon L. B. Nielsen			;;
32323d827efSSimon L. B. Nielsen		[Nn][Oo])
32423d827efSSimon L. B. Nielsen			BACKUPKERNEL=no
32523d827efSSimon L. B. Nielsen			;;
32623d827efSSimon L. B. Nielsen		*)
32723d827efSSimon L. B. Nielsen			return 1
32823d827efSSimon L. B. Nielsen			;;
32923d827efSSimon L. B. Nielsen		esac
33023d827efSSimon L. B. Nielsen	else
33123d827efSSimon L. B. Nielsen		return 1
33223d827efSSimon L. B. Nielsen	fi
33323d827efSSimon L. B. Nielsen}
33423d827efSSimon L. B. Nielsen
33523d827efSSimon L. B. Nielsenconfig_BackupKernelDir () {
33623d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNELDIR} ]; then
33723d827efSSimon L. B. Nielsen		if [ -z "$1" ]; then
33823d827efSSimon L. B. Nielsen			echo "BackupKernelDir set to empty dir"
33923d827efSSimon L. B. Nielsen			return 1
34023d827efSSimon L. B. Nielsen		fi
34123d827efSSimon L. B. Nielsen
34223d827efSSimon L. B. Nielsen		# We check for some paths which would be extremely odd
34323d827efSSimon L. B. Nielsen		# to use, but which could cause a lot of problems if
34423d827efSSimon L. B. Nielsen		# used.
34523d827efSSimon L. B. Nielsen		case $1 in
34623d827efSSimon L. B. Nielsen		/|/bin|/boot|/etc|/lib|/libexec|/sbin|/usr|/var)
34723d827efSSimon L. B. Nielsen			echo "BackupKernelDir set to invalid path $1"
34823d827efSSimon L. B. Nielsen			return 1
34923d827efSSimon L. B. Nielsen			;;
35023d827efSSimon L. B. Nielsen		/*)
35123d827efSSimon L. B. Nielsen			BACKUPKERNELDIR=$1
35223d827efSSimon L. B. Nielsen			;;
35323d827efSSimon L. B. Nielsen		*)
35423d827efSSimon L. B. Nielsen			echo "BackupKernelDir ($1) is not an absolute path"
35523d827efSSimon L. B. Nielsen			return 1
35623d827efSSimon L. B. Nielsen			;;
35723d827efSSimon L. B. Nielsen		esac
35823d827efSSimon L. B. Nielsen	else
35923d827efSSimon L. B. Nielsen		return 1
36023d827efSSimon L. B. Nielsen	fi
36123d827efSSimon L. B. Nielsen}
36223d827efSSimon L. B. Nielsen
36323d827efSSimon L. B. Nielsenconfig_BackupKernelSymbolFiles () {
36423d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNELSYMBOLFILES} ]; then
36523d827efSSimon L. B. Nielsen		case $1 in
36623d827efSSimon L. B. Nielsen		[Yy][Ee][Ss])
36723d827efSSimon L. B. Nielsen			BACKUPKERNELSYMBOLFILES=yes
36823d827efSSimon L. B. Nielsen			;;
36923d827efSSimon L. B. Nielsen		[Nn][Oo])
37023d827efSSimon L. B. Nielsen			BACKUPKERNELSYMBOLFILES=no
37123d827efSSimon L. B. Nielsen			;;
37223d827efSSimon L. B. Nielsen		*)
37323d827efSSimon L. B. Nielsen			return 1
37423d827efSSimon L. B. Nielsen			;;
37523d827efSSimon L. B. Nielsen		esac
37623d827efSSimon L. B. Nielsen	else
37723d827efSSimon L. B. Nielsen		return 1
37823d827efSSimon L. B. Nielsen	fi
37923d827efSSimon L. B. Nielsen}
38023d827efSSimon L. B. Nielsen
38148ffe56aSColin Percival# Handle one line of configuration
38248ffe56aSColin Percivalconfigline () {
38348ffe56aSColin Percival	if [ $# -eq 0 ]; then
38448ffe56aSColin Percival		return
38548ffe56aSColin Percival	fi
38648ffe56aSColin Percival
38748ffe56aSColin Percival	OPT=$1
38848ffe56aSColin Percival	shift
38948ffe56aSColin Percival	config_${OPT} $@
39048ffe56aSColin Percival}
39148ffe56aSColin Percival
39248ffe56aSColin Percival#### Parameter handling functions.
39348ffe56aSColin Percival
39448ffe56aSColin Percival# Initialize parameters to null, just in case they're
39548ffe56aSColin Percival# set in the environment.
39648ffe56aSColin Percivalinit_params () {
39748ffe56aSColin Percival	# Configration settings
39848ffe56aSColin Percival	nullconfig
39948ffe56aSColin Percival
40048ffe56aSColin Percival	# No configuration file set yet
40148ffe56aSColin Percival	CONFFILE=""
40248ffe56aSColin Percival
40348ffe56aSColin Percival	# No commands specified yet
40448ffe56aSColin Percival	COMMANDS=""
4058935f242SAllan Jude
4068935f242SAllan Jude	# Force fetch to proceed
4078935f242SAllan Jude	FORCEFETCH=0
4088935f242SAllan Jude
4098935f242SAllan Jude	# Run without a TTY
4108935f242SAllan Jude	NOTTYOK=0
41148ffe56aSColin Percival}
41248ffe56aSColin Percival
41348ffe56aSColin Percival# Parse the command line
41448ffe56aSColin Percivalparse_cmdline () {
41548ffe56aSColin Percival	while [ $# -gt 0 ]; do
41648ffe56aSColin Percival		case "$1" in
41748ffe56aSColin Percival		# Location of configuration file
41848ffe56aSColin Percival		-f)
41948ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi
42048ffe56aSColin Percival			if [ ! -z "${CONFFILE}" ]; then usage; fi
42148ffe56aSColin Percival			shift; CONFFILE="$1"
42248ffe56aSColin Percival			;;
4238935f242SAllan Jude		-F)
4248935f242SAllan Jude			FORCEFETCH=1
4258935f242SAllan Jude			;;
4268935f242SAllan Jude		--not-running-from-cron)
4278935f242SAllan Jude			NOTTYOK=1
4288935f242SAllan Jude			;;
42948ffe56aSColin Percival
43048ffe56aSColin Percival		# Configuration file equivalents
43148ffe56aSColin Percival		-b)
43248ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
43348ffe56aSColin Percival			config_BaseDir $1 || usage
43448ffe56aSColin Percival			;;
43548ffe56aSColin Percival		-d)
43648ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
43748ffe56aSColin Percival			config_WorkDir $1 || usage
43848ffe56aSColin Percival			;;
43948ffe56aSColin Percival		-k)
44048ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
44148ffe56aSColin Percival			config_KeyPrint $1 || usage
44248ffe56aSColin Percival			;;
44348ffe56aSColin Percival		-s)
44448ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
44548ffe56aSColin Percival			config_ServerName $1 || usage
44648ffe56aSColin Percival			;;
447db6b0a61SColin Percival		-r)
448db6b0a61SColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
449db6b0a61SColin Percival			config_TargetRelease $1 || usage
450db6b0a61SColin Percival			;;
45148ffe56aSColin Percival		-t)
45248ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
45348ffe56aSColin Percival			config_MailTo $1 || usage
45448ffe56aSColin Percival			;;
45548ffe56aSColin Percival		-v)
45648ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
45748ffe56aSColin Percival			config_VerboseLevel $1 || usage
45848ffe56aSColin Percival			;;
45948ffe56aSColin Percival
46048ffe56aSColin Percival		# Aliases for "-v debug" and "-v nostats"
46148ffe56aSColin Percival		--debug)
46248ffe56aSColin Percival			config_VerboseLevel debug || usage
46348ffe56aSColin Percival			;;
46448ffe56aSColin Percival		--no-stats)
46548ffe56aSColin Percival			config_VerboseLevel nostats || usage
46648ffe56aSColin Percival			;;
46748ffe56aSColin Percival
46848ffe56aSColin Percival		# Commands
46908e23beeSColin Percival		cron | fetch | upgrade | install | rollback | IDS)
47048ffe56aSColin Percival			COMMANDS="${COMMANDS} $1"
47148ffe56aSColin Percival			;;
47248ffe56aSColin Percival
47348ffe56aSColin Percival		# Anything else is an error
47448ffe56aSColin Percival		*)
47548ffe56aSColin Percival			usage
47648ffe56aSColin Percival			;;
47748ffe56aSColin Percival		esac
47848ffe56aSColin Percival		shift
47948ffe56aSColin Percival	done
48048ffe56aSColin Percival
48148ffe56aSColin Percival	# Make sure we have at least one command
48248ffe56aSColin Percival	if [ -z "${COMMANDS}" ]; then
48348ffe56aSColin Percival		usage
48448ffe56aSColin Percival	fi
48548ffe56aSColin Percival}
48648ffe56aSColin Percival
48748ffe56aSColin Percival# Parse the configuration file
48848ffe56aSColin Percivalparse_conffile () {
48948ffe56aSColin Percival	# If a configuration file was specified on the command line, check
49048ffe56aSColin Percival	# that it exists and is readable.
49148ffe56aSColin Percival	if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
49248ffe56aSColin Percival		echo -n "File does not exist "
49348ffe56aSColin Percival		echo -n "or is not readable: "
49448ffe56aSColin Percival		echo ${CONFFILE}
49548ffe56aSColin Percival		exit 1
49648ffe56aSColin Percival	fi
49748ffe56aSColin Percival
49848ffe56aSColin Percival	# If a configuration file was not specified on the command line,
49948ffe56aSColin Percival	# use the default configuration file path.  If that default does
50048ffe56aSColin Percival	# not exist, give up looking for any configuration.
50148ffe56aSColin Percival	if [ -z "${CONFFILE}" ]; then
50248ffe56aSColin Percival		CONFFILE="/etc/freebsd-update.conf"
50348ffe56aSColin Percival		if [ ! -r "${CONFFILE}" ]; then
50448ffe56aSColin Percival			return
50548ffe56aSColin Percival		fi
50648ffe56aSColin Percival	fi
50748ffe56aSColin Percival
50848ffe56aSColin Percival	# Save the configuration options specified on the command line, and
50948ffe56aSColin Percival	# clear all the options in preparation for reading the config file.
51048ffe56aSColin Percival	saveconfig
51148ffe56aSColin Percival	nullconfig
51248ffe56aSColin Percival
51348ffe56aSColin Percival	# Read the configuration file.  Anything after the first '#' is
51448ffe56aSColin Percival	# ignored, and any blank lines are ignored.
51548ffe56aSColin Percival	L=0
51648ffe56aSColin Percival	while read LINE; do
51748ffe56aSColin Percival		L=$(($L + 1))
51848ffe56aSColin Percival		LINEX=`echo "${LINE}" | cut -f 1 -d '#'`
51948ffe56aSColin Percival		if ! configline ${LINEX}; then
52048ffe56aSColin Percival			echo "Error processing configuration file, line $L:"
52148ffe56aSColin Percival			echo "==> ${LINE}"
52248ffe56aSColin Percival			exit 1
52348ffe56aSColin Percival		fi
52448ffe56aSColin Percival	done < ${CONFFILE}
52548ffe56aSColin Percival
52648ffe56aSColin Percival	# Merge the settings read from the configuration file with those
52748ffe56aSColin Percival	# provided at the command line.
52848ffe56aSColin Percival	mergeconfig
52948ffe56aSColin Percival}
53048ffe56aSColin Percival
53148ffe56aSColin Percival# Provide some default parameters
53248ffe56aSColin Percivaldefault_params () {
53348ffe56aSColin Percival	# Save any parameters already configured, and clear the slate
53448ffe56aSColin Percival	saveconfig
53548ffe56aSColin Percival	nullconfig
53648ffe56aSColin Percival
53748ffe56aSColin Percival	# Default configurations
53848ffe56aSColin Percival	config_WorkDir /var/db/freebsd-update
53948ffe56aSColin Percival	config_MailTo root
54048ffe56aSColin Percival	config_AllowAdd yes
54148ffe56aSColin Percival	config_AllowDelete yes
54248ffe56aSColin Percival	config_KeepModifiedMetadata yes
54348ffe56aSColin Percival	config_BaseDir /
54448ffe56aSColin Percival	config_VerboseLevel stats
545db6b0a61SColin Percival	config_StrictComponents no
54623d827efSSimon L. B. Nielsen	config_BackupKernel yes
54723d827efSSimon L. B. Nielsen	config_BackupKernelDir /boot/kernel.old
54823d827efSSimon L. B. Nielsen	config_BackupKernelSymbolFiles no
54948ffe56aSColin Percival
55048ffe56aSColin Percival	# Merge these defaults into the earlier-configured settings
55148ffe56aSColin Percival	mergeconfig
55248ffe56aSColin Percival}
55348ffe56aSColin Percival
55448ffe56aSColin Percival# Set utility output filtering options, based on ${VERBOSELEVEL}
55548ffe56aSColin Percivalfetch_setup_verboselevel () {
55648ffe56aSColin Percival	case ${VERBOSELEVEL} in
55748ffe56aSColin Percival	debug)
55848ffe56aSColin Percival		QUIETREDIR="/dev/stderr"
55948ffe56aSColin Percival		QUIETFLAG=" "
56048ffe56aSColin Percival		STATSREDIR="/dev/stderr"
56148ffe56aSColin Percival		DDSTATS=".."
56248ffe56aSColin Percival		XARGST="-t"
56348ffe56aSColin Percival		NDEBUG=" "
56448ffe56aSColin Percival		;;
56548ffe56aSColin Percival	nostats)
56648ffe56aSColin Percival		QUIETREDIR=""
56748ffe56aSColin Percival		QUIETFLAG=""
56848ffe56aSColin Percival		STATSREDIR="/dev/null"
56948ffe56aSColin Percival		DDSTATS=".."
57048ffe56aSColin Percival		XARGST=""
57148ffe56aSColin Percival		NDEBUG=""
57248ffe56aSColin Percival		;;
57348ffe56aSColin Percival	stats)
57448ffe56aSColin Percival		QUIETREDIR="/dev/null"
57548ffe56aSColin Percival		QUIETFLAG="-q"
57648ffe56aSColin Percival		STATSREDIR="/dev/stdout"
57748ffe56aSColin Percival		DDSTATS=""
57848ffe56aSColin Percival		XARGST=""
57948ffe56aSColin Percival		NDEBUG="-n"
58048ffe56aSColin Percival		;;
58148ffe56aSColin Percival	esac
58248ffe56aSColin Percival}
58348ffe56aSColin Percival
58448ffe56aSColin Percival# Perform sanity checks and set some final parameters
58548ffe56aSColin Percival# in preparation for fetching files.  Figure out which
58648ffe56aSColin Percival# set of updates should be downloaded: If the user is
58748ffe56aSColin Percival# running *-p[0-9]+, strip off the last part; if the
58848ffe56aSColin Percival# user is running -SECURITY, call it -RELEASE.  Chdir
58948ffe56aSColin Percival# into the working directory.
590211f2ba0SColin Percivalfetchupgrade_check_params () {
59148ffe56aSColin Percival	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
59248ffe56aSColin Percival
59348ffe56aSColin Percival	_SERVERNAME_z=\
59448ffe56aSColin Percival"SERVERNAME must be given via command line or configuration file."
59548ffe56aSColin Percival	_KEYPRINT_z="Key must be given via -k option or configuration file."
59648ffe56aSColin Percival	_KEYPRINT_bad="Invalid key fingerprint: "
59748ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
598f88076f0SMark Felder	_WORKDIR_bad2="Directory is not on a persistent filesystem: "
59948ffe56aSColin Percival
60048ffe56aSColin Percival	if [ -z "${SERVERNAME}" ]; then
60148ffe56aSColin Percival		echo -n "`basename $0`: "
60248ffe56aSColin Percival		echo "${_SERVERNAME_z}"
60348ffe56aSColin Percival		exit 1
60448ffe56aSColin Percival	fi
60548ffe56aSColin Percival	if [ -z "${KEYPRINT}" ]; then
60648ffe56aSColin Percival		echo -n "`basename $0`: "
60748ffe56aSColin Percival		echo "${_KEYPRINT_z}"
60848ffe56aSColin Percival		exit 1
60948ffe56aSColin Percival	fi
61048ffe56aSColin Percival	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
61148ffe56aSColin Percival		echo -n "`basename $0`: "
61248ffe56aSColin Percival		echo -n "${_KEYPRINT_bad}"
61348ffe56aSColin Percival		echo ${KEYPRINT}
61448ffe56aSColin Percival		exit 1
61548ffe56aSColin Percival	fi
61648ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
61748ffe56aSColin Percival		echo -n "`basename $0`: "
61848ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
61948ffe56aSColin Percival		echo ${WORKDIR}
62048ffe56aSColin Percival		exit 1
62148ffe56aSColin Percival	fi
622dfe9215bSMark Felder	case `df -T ${WORKDIR}` in */dev/md[0-9]* | *tmpfs*)
623f88076f0SMark Felder		echo -n "`basename $0`: "
624f88076f0SMark Felder		echo -n "${_WORKDIR_bad2}"
625f88076f0SMark Felder		echo ${WORKDIR}
626f88076f0SMark Felder		exit 1
627dfe9215bSMark Felder		;;
628dfe9215bSMark Felder	esac
629a2356430SColin Percival	chmod 700 ${WORKDIR}
63048ffe56aSColin Percival	cd ${WORKDIR} || exit 1
63148ffe56aSColin Percival
63248ffe56aSColin Percival	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
63348ffe56aSColin Percival	# to provide an upgrade path for FreeBSD Update 1.x users, since
63448ffe56aSColin Percival	# the kernels provided by FreeBSD Update 1.x are always labelled
63548ffe56aSColin Percival	# as X.Y-SECURITY.
63648ffe56aSColin Percival	RELNUM=`uname -r |
63748ffe56aSColin Percival	    sed -E 's,-p[0-9]+,,' |
63848ffe56aSColin Percival	    sed -E 's,-SECURITY,-RELEASE,'`
63948ffe56aSColin Percival	ARCH=`uname -m`
64048ffe56aSColin Percival	FETCHDIR=${RELNUM}/${ARCH}
641db6b0a61SColin Percival	PATCHDIR=${RELNUM}/${ARCH}/bp
64248ffe56aSColin Percival
64348ffe56aSColin Percival	# Figure out what directory contains the running kernel
64448ffe56aSColin Percival	BOOTFILE=`sysctl -n kern.bootfile`
64548ffe56aSColin Percival	KERNELDIR=${BOOTFILE%/kernel}
64648ffe56aSColin Percival	if ! [ -d ${KERNELDIR} ]; then
64748ffe56aSColin Percival		echo "Cannot identify running kernel"
64848ffe56aSColin Percival		exit 1
64948ffe56aSColin Percival	fi
65048ffe56aSColin Percival
6512c434b2cSColin Percival	# Figure out what kernel configuration is running.  We start with
6522c434b2cSColin Percival	# the output of `uname -i`, and then make the following adjustments:
6532c434b2cSColin Percival	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
6542c434b2cSColin Percival	# file says "ident SMP-GENERIC", I don't know...
6552c434b2cSColin Percival	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
6562c434b2cSColin Percival	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
6572c434b2cSColin Percival	# we're running an SMP kernel.  This mis-identification is a bug
6582c434b2cSColin Percival	# which was fixed in 6.2-STABLE.
6592c434b2cSColin Percival	KERNCONF=`uname -i`
6602c434b2cSColin Percival	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
6612c434b2cSColin Percival		KERNCONF=SMP
6622c434b2cSColin Percival	fi
6632c434b2cSColin Percival	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
6642c434b2cSColin Percival		if sysctl kern.version | grep -qE '/SMP$'; then
6652c434b2cSColin Percival			KERNCONF=SMP
6662c434b2cSColin Percival		fi
6672c434b2cSColin Percival	fi
6682c434b2cSColin Percival
66948ffe56aSColin Percival	# Define some paths
67048ffe56aSColin Percival	BSPATCH=/usr/bin/bspatch
67148ffe56aSColin Percival	SHA256=/sbin/sha256
67248ffe56aSColin Percival	PHTTPGET=/usr/libexec/phttpget
67348ffe56aSColin Percival
67448ffe56aSColin Percival	# Set up variables relating to VERBOSELEVEL
67548ffe56aSColin Percival	fetch_setup_verboselevel
67648ffe56aSColin Percival
67748ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
67848ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
67948ffe56aSColin Percival}
68048ffe56aSColin Percival
681211f2ba0SColin Percival# Perform sanity checks etc. before fetching updates.
682211f2ba0SColin Percivalfetch_check_params () {
683211f2ba0SColin Percival	fetchupgrade_check_params
684211f2ba0SColin Percival
685211f2ba0SColin Percival	if ! [ -z "${TARGETRELEASE}" ]; then
686211f2ba0SColin Percival		echo -n "`basename $0`: "
687211f2ba0SColin Percival		echo -n "-r option is meaningless with 'fetch' command.  "
688211f2ba0SColin Percival		echo "(Did you mean 'upgrade' instead?)"
689211f2ba0SColin Percival		exit 1
690211f2ba0SColin Percival	fi
6918935f242SAllan Jude
6928935f242SAllan Jude	# Check that we have updates ready to install
6938935f242SAllan Jude	if [ -f ${BDHASH}-install/kerneldone && $FORCEFETCH -eq 0 ]; then
6948935f242SAllan Jude		echo "You have a partially completed upgrade pending"
6958935f242SAllan Jude		echo "Run '$0 install' first."
6968935f242SAllan Jude		echo "Run '$0 fetch -F' to proceed anyway."
6978935f242SAllan Jude		exit 1
6988935f242SAllan Jude	fi
699211f2ba0SColin Percival}
700211f2ba0SColin Percival
701db6b0a61SColin Percival# Perform sanity checks etc. before fetching upgrades.
702db6b0a61SColin Percivalupgrade_check_params () {
703211f2ba0SColin Percival	fetchupgrade_check_params
704db6b0a61SColin Percival
705db6b0a61SColin Percival	# Unless set otherwise, we're upgrading to the same kernel config.
706db6b0a61SColin Percival	NKERNCONF=${KERNCONF}
707db6b0a61SColin Percival
708db6b0a61SColin Percival	# We need TARGETRELEASE set
709db6b0a61SColin Percival	_TARGETRELEASE_z="Release target must be specified via -r option."
710db6b0a61SColin Percival	if [ -z "${TARGETRELEASE}" ]; then
711db6b0a61SColin Percival		echo -n "`basename $0`: "
712db6b0a61SColin Percival		echo "${_TARGETRELEASE_z}"
713db6b0a61SColin Percival		exit 1
714db6b0a61SColin Percival	fi
715db6b0a61SColin Percival
716db6b0a61SColin Percival	# The target release should be != the current release.
717db6b0a61SColin Percival	if [ "${TARGETRELEASE}" = "${RELNUM}" ]; then
718db6b0a61SColin Percival		echo -n "`basename $0`: "
719db6b0a61SColin Percival		echo "Cannot upgrade from ${RELNUM} to itself"
720db6b0a61SColin Percival		exit 1
721db6b0a61SColin Percival	fi
722db6b0a61SColin Percival
723db6b0a61SColin Percival	# Turning off AllowAdd or AllowDelete is a bad idea for upgrades.
724db6b0a61SColin Percival	if [ "${ALLOWADD}" = "no" ]; then
725db6b0a61SColin Percival		echo -n "`basename $0`: "
726db6b0a61SColin Percival		echo -n "WARNING: \"AllowAdd no\" is a bad idea "
727db6b0a61SColin Percival		echo "when upgrading between releases."
728db6b0a61SColin Percival		echo
729db6b0a61SColin Percival	fi
730db6b0a61SColin Percival	if [ "${ALLOWDELETE}" = "no" ]; then
731db6b0a61SColin Percival		echo -n "`basename $0`: "
732db6b0a61SColin Percival		echo -n "WARNING: \"AllowDelete no\" is a bad idea "
733db6b0a61SColin Percival		echo "when upgrading between releases."
734db6b0a61SColin Percival		echo
735db6b0a61SColin Percival	fi
736db6b0a61SColin Percival
737db6b0a61SColin Percival	# Set EDITOR to /usr/bin/vi if it isn't already set
738db6b0a61SColin Percival	: ${EDITOR:='/usr/bin/vi'}
739db6b0a61SColin Percival}
740db6b0a61SColin Percival
74148ffe56aSColin Percival# Perform sanity checks and set some final parameters in
74248ffe56aSColin Percival# preparation for installing updates.
74348ffe56aSColin Percivalinstall_check_params () {
74448ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
74548ffe56aSColin Percival	if [ `id -u` != 0 ]; then
74648ffe56aSColin Percival		echo "You must be root to run this."
74748ffe56aSColin Percival		exit 1
74848ffe56aSColin Percival	fi
74948ffe56aSColin Percival
7502328d598SColin Percival	# Check that securelevel <= 0.  Otherwise we can't update schg files.
7512328d598SColin Percival	if [ `sysctl -n kern.securelevel` -gt 0 ]; then
7522328d598SColin Percival		echo "Updates cannot be installed when the system securelevel"
7532328d598SColin Percival		echo "is greater than zero."
7542328d598SColin Percival		exit 1
7552328d598SColin Percival	fi
7562328d598SColin Percival
75748ffe56aSColin Percival	# Check that we have a working directory
75848ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
75948ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
76048ffe56aSColin Percival		echo -n "`basename $0`: "
76148ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
76248ffe56aSColin Percival		echo ${WORKDIR}
76348ffe56aSColin Percival		exit 1
76448ffe56aSColin Percival	fi
76548ffe56aSColin Percival	cd ${WORKDIR} || exit 1
76648ffe56aSColin Percival
76748ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
76848ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
76948ffe56aSColin Percival
77048ffe56aSColin Percival	# Check that we have updates ready to install
77148ffe56aSColin Percival	if ! [ -L ${BDHASH}-install ]; then
77248ffe56aSColin Percival		echo "No updates are available to install."
77348ffe56aSColin Percival		echo "Run '$0 fetch' first."
77448ffe56aSColin Percival		exit 1
77548ffe56aSColin Percival	fi
77648ffe56aSColin Percival	if ! [ -f ${BDHASH}-install/INDEX-OLD ] ||
77748ffe56aSColin Percival	    ! [ -f ${BDHASH}-install/INDEX-NEW ]; then
77848ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
77948ffe56aSColin Percival		echo "Re-run '$0 fetch'."
78048ffe56aSColin Percival		exit 1
78148ffe56aSColin Percival	fi
78223d827efSSimon L. B. Nielsen
78323d827efSSimon L. B. Nielsen	# Figure out what directory contains the running kernel
78423d827efSSimon L. B. Nielsen	BOOTFILE=`sysctl -n kern.bootfile`
78523d827efSSimon L. B. Nielsen	KERNELDIR=${BOOTFILE%/kernel}
78623d827efSSimon L. B. Nielsen	if ! [ -d ${KERNELDIR} ]; then
78723d827efSSimon L. B. Nielsen		echo "Cannot identify running kernel"
78823d827efSSimon L. B. Nielsen		exit 1
78923d827efSSimon L. B. Nielsen	fi
79048ffe56aSColin Percival}
79148ffe56aSColin Percival
79248ffe56aSColin Percival# Perform sanity checks and set some final parameters in
79348ffe56aSColin Percival# preparation for UNinstalling updates.
79448ffe56aSColin Percivalrollback_check_params () {
79548ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
79648ffe56aSColin Percival	if [ `id -u` != 0 ]; then
79748ffe56aSColin Percival		echo "You must be root to run this."
79848ffe56aSColin Percival		exit 1
79948ffe56aSColin Percival	fi
80048ffe56aSColin Percival
80148ffe56aSColin Percival	# Check that we have a working directory
80248ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
80348ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
80448ffe56aSColin Percival		echo -n "`basename $0`: "
80548ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
80648ffe56aSColin Percival		echo ${WORKDIR}
80748ffe56aSColin Percival		exit 1
80848ffe56aSColin Percival	fi
80948ffe56aSColin Percival	cd ${WORKDIR} || exit 1
81048ffe56aSColin Percival
81148ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
81248ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
81348ffe56aSColin Percival
81448ffe56aSColin Percival	# Check that we have updates ready to rollback
81548ffe56aSColin Percival	if ! [ -L ${BDHASH}-rollback ]; then
81648ffe56aSColin Percival		echo "No rollback directory found."
81748ffe56aSColin Percival		exit 1
81848ffe56aSColin Percival	fi
81948ffe56aSColin Percival	if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] ||
82048ffe56aSColin Percival	    ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then
82148ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
82248ffe56aSColin Percival		exit 1
82348ffe56aSColin Percival	fi
82448ffe56aSColin Percival}
82548ffe56aSColin Percival
82608e23beeSColin Percival# Perform sanity checks and set some final parameters
82708e23beeSColin Percival# in preparation for comparing the system against the
82808e23beeSColin Percival# published index.  Figure out which index we should
82908e23beeSColin Percival# compare against: If the user is running *-p[0-9]+,
83008e23beeSColin Percival# strip off the last part; if the user is running
83108e23beeSColin Percival# -SECURITY, call it -RELEASE.  Chdir into the working
83208e23beeSColin Percival# directory.
83308e23beeSColin PercivalIDS_check_params () {
83408e23beeSColin Percival	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
83508e23beeSColin Percival
83608e23beeSColin Percival	_SERVERNAME_z=\
83708e23beeSColin Percival"SERVERNAME must be given via command line or configuration file."
83808e23beeSColin Percival	_KEYPRINT_z="Key must be given via -k option or configuration file."
83908e23beeSColin Percival	_KEYPRINT_bad="Invalid key fingerprint: "
84008e23beeSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
84108e23beeSColin Percival
84208e23beeSColin Percival	if [ -z "${SERVERNAME}" ]; then
84308e23beeSColin Percival		echo -n "`basename $0`: "
84408e23beeSColin Percival		echo "${_SERVERNAME_z}"
84508e23beeSColin Percival		exit 1
84608e23beeSColin Percival	fi
84708e23beeSColin Percival	if [ -z "${KEYPRINT}" ]; then
84808e23beeSColin Percival		echo -n "`basename $0`: "
84908e23beeSColin Percival		echo "${_KEYPRINT_z}"
85008e23beeSColin Percival		exit 1
85108e23beeSColin Percival	fi
85208e23beeSColin Percival	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
85308e23beeSColin Percival		echo -n "`basename $0`: "
85408e23beeSColin Percival		echo -n "${_KEYPRINT_bad}"
85508e23beeSColin Percival		echo ${KEYPRINT}
85608e23beeSColin Percival		exit 1
85708e23beeSColin Percival	fi
85808e23beeSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
85908e23beeSColin Percival		echo -n "`basename $0`: "
86008e23beeSColin Percival		echo -n "${_WORKDIR_bad}"
86108e23beeSColin Percival		echo ${WORKDIR}
86208e23beeSColin Percival		exit 1
86308e23beeSColin Percival	fi
86408e23beeSColin Percival	cd ${WORKDIR} || exit 1
86508e23beeSColin Percival
86608e23beeSColin Percival	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
86708e23beeSColin Percival	# to provide an upgrade path for FreeBSD Update 1.x users, since
86808e23beeSColin Percival	# the kernels provided by FreeBSD Update 1.x are always labelled
86908e23beeSColin Percival	# as X.Y-SECURITY.
87008e23beeSColin Percival	RELNUM=`uname -r |
87108e23beeSColin Percival	    sed -E 's,-p[0-9]+,,' |
87208e23beeSColin Percival	    sed -E 's,-SECURITY,-RELEASE,'`
87308e23beeSColin Percival	ARCH=`uname -m`
87408e23beeSColin Percival	FETCHDIR=${RELNUM}/${ARCH}
87508e23beeSColin Percival	PATCHDIR=${RELNUM}/${ARCH}/bp
87608e23beeSColin Percival
87708e23beeSColin Percival	# Figure out what directory contains the running kernel
87808e23beeSColin Percival	BOOTFILE=`sysctl -n kern.bootfile`
87908e23beeSColin Percival	KERNELDIR=${BOOTFILE%/kernel}
88008e23beeSColin Percival	if ! [ -d ${KERNELDIR} ]; then
88108e23beeSColin Percival		echo "Cannot identify running kernel"
88208e23beeSColin Percival		exit 1
88308e23beeSColin Percival	fi
88408e23beeSColin Percival
88508e23beeSColin Percival	# Figure out what kernel configuration is running.  We start with
88608e23beeSColin Percival	# the output of `uname -i`, and then make the following adjustments:
88708e23beeSColin Percival	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
88808e23beeSColin Percival	# file says "ident SMP-GENERIC", I don't know...
88908e23beeSColin Percival	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
89008e23beeSColin Percival	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
89108e23beeSColin Percival	# we're running an SMP kernel.  This mis-identification is a bug
89208e23beeSColin Percival	# which was fixed in 6.2-STABLE.
89308e23beeSColin Percival	KERNCONF=`uname -i`
89408e23beeSColin Percival	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
89508e23beeSColin Percival		KERNCONF=SMP
89608e23beeSColin Percival	fi
89708e23beeSColin Percival	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
89808e23beeSColin Percival		if sysctl kern.version | grep -qE '/SMP$'; then
89908e23beeSColin Percival			KERNCONF=SMP
90008e23beeSColin Percival		fi
90108e23beeSColin Percival	fi
90208e23beeSColin Percival
90308e23beeSColin Percival	# Define some paths
90408e23beeSColin Percival	SHA256=/sbin/sha256
90508e23beeSColin Percival	PHTTPGET=/usr/libexec/phttpget
90608e23beeSColin Percival
90708e23beeSColin Percival	# Set up variables relating to VERBOSELEVEL
90808e23beeSColin Percival	fetch_setup_verboselevel
90908e23beeSColin Percival}
91008e23beeSColin Percival
91148ffe56aSColin Percival#### Core functionality -- the actual work gets done here
91248ffe56aSColin Percival
91348ffe56aSColin Percival# Use an SRV query to pick a server.  If the SRV query doesn't provide
91448ffe56aSColin Percival# a useful answer, use the server name specified by the user.
91548ffe56aSColin Percival# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
91648ffe56aSColin Percival# from that; or if no servers are returned, use ${SERVERNAME}.
91748ffe56aSColin Percival# This allows a user to specify "portsnap.freebsd.org" (in which case
91848ffe56aSColin Percival# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
91948ffe56aSColin Percival# (in which case portsnap will use that particular server, since there
92048ffe56aSColin Percival# won't be an SRV entry for that name).
92148ffe56aSColin Percival#
92248ffe56aSColin Percival# We ignore the Port field, since we are always going to use port 80.
92348ffe56aSColin Percival
92448ffe56aSColin Percival# Fetch the mirror list, but do not pick a mirror yet.  Returns 1 if
92548ffe56aSColin Percival# no mirrors are available for any reason.
92648ffe56aSColin Percivalfetch_pick_server_init () {
92748ffe56aSColin Percival	: > serverlist_tried
92848ffe56aSColin Percival
92948ffe56aSColin Percival# Check that host(1) exists (i.e., that the system wasn't built with the
93048ffe56aSColin Percival# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
93148ffe56aSColin Percival	if ! which -s host; then
93248ffe56aSColin Percival		: > serverlist_full
93348ffe56aSColin Percival		return 1
93448ffe56aSColin Percival	fi
93548ffe56aSColin Percival
93648ffe56aSColin Percival	echo -n "Looking up ${SERVERNAME} mirrors... "
93748ffe56aSColin Percival
93848ffe56aSColin Percival# Issue the SRV query and pull out the Priority, Weight, and Target fields.
93948ffe56aSColin Percival# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
94048ffe56aSColin Percival# "$name server selection ..."; we allow either format.
94148ffe56aSColin Percival	MLIST="_http._tcp.${SERVERNAME}"
94248ffe56aSColin Percival	host -t srv "${MLIST}" |
94348ffe56aSColin Percival	    sed -nE "s/${MLIST} (has SRV record|server selection) //p" |
94448ffe56aSColin Percival	    cut -f 1,2,4 -d ' ' |
94548ffe56aSColin Percival	    sed -e 's/\.$//' |
94648ffe56aSColin Percival	    sort > serverlist_full
94748ffe56aSColin Percival
94848ffe56aSColin Percival# If no records, give up -- we'll just use the server name we were given.
94948ffe56aSColin Percival	if [ `wc -l < serverlist_full` -eq 0 ]; then
95048ffe56aSColin Percival		echo "none found."
95148ffe56aSColin Percival		return 1
95248ffe56aSColin Percival	fi
95348ffe56aSColin Percival
95448ffe56aSColin Percival# Report how many mirrors we found.
95548ffe56aSColin Percival	echo `wc -l < serverlist_full` "mirrors found."
95648ffe56aSColin Percival
95748ffe56aSColin Percival# Generate a random seed for use in picking mirrors.  If HTTP_PROXY
95848ffe56aSColin Percival# is set, this will be used to generate the seed; otherwise, the seed
95948ffe56aSColin Percival# will be random.
96048ffe56aSColin Percival	if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
96148ffe56aSColin Percival		RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
96248ffe56aSColin Percival		    tr -d 'a-f' |
96348ffe56aSColin Percival		    cut -c 1-9`
96448ffe56aSColin Percival	else
96548ffe56aSColin Percival		RANDVALUE=`jot -r 1 0 999999999`
96648ffe56aSColin Percival	fi
96748ffe56aSColin Percival}
96848ffe56aSColin Percival
96948ffe56aSColin Percival# Pick a mirror.  Returns 1 if we have run out of mirrors to try.
97048ffe56aSColin Percivalfetch_pick_server () {
97148ffe56aSColin Percival# Generate a list of not-yet-tried mirrors
97248ffe56aSColin Percival	sort serverlist_tried |
97348ffe56aSColin Percival	    comm -23 serverlist_full - > serverlist
97448ffe56aSColin Percival
97548ffe56aSColin Percival# Have we run out of mirrors?
97648ffe56aSColin Percival	if [ `wc -l < serverlist` -eq 0 ]; then
97748ffe56aSColin Percival		echo "No mirrors remaining, giving up."
97848ffe56aSColin Percival		return 1
97948ffe56aSColin Percival	fi
98048ffe56aSColin Percival
98148ffe56aSColin Percival# Find the highest priority level (lowest numeric value).
98248ffe56aSColin Percival	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
98348ffe56aSColin Percival
98448ffe56aSColin Percival# Add up the weights of the response lines at that priority level.
98548ffe56aSColin Percival	SRV_WSUM=0;
98648ffe56aSColin Percival	while read X; do
98748ffe56aSColin Percival		case "$X" in
98848ffe56aSColin Percival		${SRV_PRIORITY}\ *)
98948ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
99048ffe56aSColin Percival			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
99148ffe56aSColin Percival			;;
99248ffe56aSColin Percival		esac
99348ffe56aSColin Percival	done < serverlist
99448ffe56aSColin Percival
99548ffe56aSColin Percival# If all the weights are 0, pretend that they are all 1 instead.
99648ffe56aSColin Percival	if [ ${SRV_WSUM} -eq 0 ]; then
99748ffe56aSColin Percival		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
99848ffe56aSColin Percival		SRV_W_ADD=1
99948ffe56aSColin Percival	else
100048ffe56aSColin Percival		SRV_W_ADD=0
100148ffe56aSColin Percival	fi
100248ffe56aSColin Percival
100348ffe56aSColin Percival# Pick a value between 0 and the sum of the weights - 1
100448ffe56aSColin Percival	SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
100548ffe56aSColin Percival
100648ffe56aSColin Percival# Read through the list of mirrors and set SERVERNAME.  Write the line
100748ffe56aSColin Percival# corresponding to the mirror we selected into serverlist_tried so that
100848ffe56aSColin Percival# we won't try it again.
100948ffe56aSColin Percival	while read X; do
101048ffe56aSColin Percival		case "$X" in
101148ffe56aSColin Percival		${SRV_PRIORITY}\ *)
101248ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
101348ffe56aSColin Percival			SRV_W=$(($SRV_W + $SRV_W_ADD))
101448ffe56aSColin Percival			if [ $SRV_RND -lt $SRV_W ]; then
101548ffe56aSColin Percival				SERVERNAME=`echo $X | cut -f 3 -d ' '`
101648ffe56aSColin Percival				echo "$X" >> serverlist_tried
101748ffe56aSColin Percival				break
101848ffe56aSColin Percival			else
101948ffe56aSColin Percival				SRV_RND=$(($SRV_RND - $SRV_W))
102048ffe56aSColin Percival			fi
102148ffe56aSColin Percival			;;
102248ffe56aSColin Percival		esac
102348ffe56aSColin Percival	done < serverlist
102448ffe56aSColin Percival}
102548ffe56aSColin Percival
102648ffe56aSColin Percival# Take a list of ${oldhash}|${newhash} and output a list of needed patches,
102748ffe56aSColin Percival# i.e., those for which we have ${oldhash} and don't have ${newhash}.
102848ffe56aSColin Percivalfetch_make_patchlist () {
102948ffe56aSColin Percival	grep -vE "^([0-9a-f]{64})\|\1$" |
103048ffe56aSColin Percival	    tr '|' ' ' |
103148ffe56aSColin Percival		while read X Y; do
103248ffe56aSColin Percival			if [ -f "files/${Y}.gz" ] ||
103348ffe56aSColin Percival			    [ ! -f "files/${X}.gz" ]; then
103448ffe56aSColin Percival				continue
103548ffe56aSColin Percival			fi
103648ffe56aSColin Percival			echo "${X}|${Y}"
103748ffe56aSColin Percival		done | uniq
103848ffe56aSColin Percival}
103948ffe56aSColin Percival
104048ffe56aSColin Percival# Print user-friendly progress statistics
104148ffe56aSColin Percivalfetch_progress () {
104248ffe56aSColin Percival	LNC=0
104348ffe56aSColin Percival	while read x; do
104448ffe56aSColin Percival		LNC=$(($LNC + 1))
104548ffe56aSColin Percival		if [ $(($LNC % 10)) = 0 ]; then
104648ffe56aSColin Percival			echo -n $LNC
104748ffe56aSColin Percival		elif [ $(($LNC % 2)) = 0 ]; then
104848ffe56aSColin Percival			echo -n .
104948ffe56aSColin Percival		fi
105048ffe56aSColin Percival	done
105148ffe56aSColin Percival	echo -n " "
105248ffe56aSColin Percival}
105348ffe56aSColin Percival
1054db6b0a61SColin Percival# Function for asking the user if everything is ok
1055db6b0a61SColin Percivalcontinuep () {
1056db6b0a61SColin Percival	while read -p "Does this look reasonable (y/n)? " CONTINUE; do
1057db6b0a61SColin Percival		case "${CONTINUE}" in
1058db6b0a61SColin Percival		y*)
1059db6b0a61SColin Percival			return 0
1060db6b0a61SColin Percival			;;
1061db6b0a61SColin Percival		n*)
1062db6b0a61SColin Percival			return 1
1063db6b0a61SColin Percival			;;
1064db6b0a61SColin Percival		esac
1065db6b0a61SColin Percival	done
1066db6b0a61SColin Percival}
1067db6b0a61SColin Percival
106848ffe56aSColin Percival# Initialize the working directory
106948ffe56aSColin Percivalworkdir_init () {
107048ffe56aSColin Percival	mkdir -p files
107148ffe56aSColin Percival	touch tINDEX.present
107248ffe56aSColin Percival}
107348ffe56aSColin Percival
107448ffe56aSColin Percival# Check that we have a public key with an appropriate hash, or
107548ffe56aSColin Percival# fetch the key if it doesn't exist.  Returns 1 if the key has
107648ffe56aSColin Percival# not yet been fetched.
107748ffe56aSColin Percivalfetch_key () {
107848ffe56aSColin Percival	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
107948ffe56aSColin Percival		return 0
108048ffe56aSColin Percival	fi
108148ffe56aSColin Percival
108248ffe56aSColin Percival	echo -n "Fetching public key from ${SERVERNAME}... "
108348ffe56aSColin Percival	rm -f pub.ssl
108448ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \
108548ffe56aSColin Percival	    2>${QUIETREDIR} || true
108648ffe56aSColin Percival	if ! [ -r pub.ssl ]; then
108748ffe56aSColin Percival		echo "failed."
108848ffe56aSColin Percival		return 1
108948ffe56aSColin Percival	fi
109048ffe56aSColin Percival	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
109148ffe56aSColin Percival		echo "key has incorrect hash."
109248ffe56aSColin Percival		rm -f pub.ssl
109348ffe56aSColin Percival		return 1
109448ffe56aSColin Percival	fi
109548ffe56aSColin Percival	echo "done."
109648ffe56aSColin Percival}
109748ffe56aSColin Percival
109848ffe56aSColin Percival# Fetch metadata signature, aka "tag".
109948ffe56aSColin Percivalfetch_tag () {
1100db6b0a61SColin Percival	echo -n "Fetching metadata signature "
1101db6b0a61SColin Percival	echo ${NDEBUG} "for ${RELNUM} from ${SERVERNAME}... "
110248ffe56aSColin Percival	rm -f latest.ssl
110348ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl	\
110448ffe56aSColin Percival	    2>${QUIETREDIR} || true
110548ffe56aSColin Percival	if ! [ -r latest.ssl ]; then
110648ffe56aSColin Percival		echo "failed."
110748ffe56aSColin Percival		return 1
110848ffe56aSColin Percival	fi
110948ffe56aSColin Percival
111048ffe56aSColin Percival	openssl rsautl -pubin -inkey pub.ssl -verify		\
111148ffe56aSColin Percival	    < latest.ssl > tag.new 2>${QUIETREDIR} || true
111248ffe56aSColin Percival	rm latest.ssl
111348ffe56aSColin Percival
111448ffe56aSColin Percival	if ! [ `wc -l < tag.new` = 1 ] ||
111548ffe56aSColin Percival	    ! grep -qE	\
111648ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
111748ffe56aSColin Percival		tag.new; then
111848ffe56aSColin Percival		echo "invalid signature."
111948ffe56aSColin Percival		return 1
112048ffe56aSColin Percival	fi
112148ffe56aSColin Percival
112248ffe56aSColin Percival	echo "done."
112348ffe56aSColin Percival
112448ffe56aSColin Percival	RELPATCHNUM=`cut -f 4 -d '|' < tag.new`
112548ffe56aSColin Percival	TINDEXHASH=`cut -f 5 -d '|' < tag.new`
112648ffe56aSColin Percival	EOLTIME=`cut -f 6 -d '|' < tag.new`
112748ffe56aSColin Percival}
112848ffe56aSColin Percival
112948ffe56aSColin Percival# Sanity-check the patch number in a tag, to make sure that we're not
113048ffe56aSColin Percival# going to "update" backwards and to prevent replay attacks.
113148ffe56aSColin Percivalfetch_tagsanity () {
113248ffe56aSColin Percival	# Check that we're not going to move from -pX to -pY with Y < X.
113348ffe56aSColin Percival	RELPX=`uname -r | sed -E 's,.*-,,'`
113448ffe56aSColin Percival	if echo ${RELPX} | grep -qE '^p[0-9]+$'; then
113548ffe56aSColin Percival		RELPX=`echo ${RELPX} | cut -c 2-`
113648ffe56aSColin Percival	else
113748ffe56aSColin Percival		RELPX=0
113848ffe56aSColin Percival	fi
113948ffe56aSColin Percival	if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then
114048ffe56aSColin Percival		echo
114148ffe56aSColin Percival		echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
114248ffe56aSColin Percival		echo " appear older than what"
114348ffe56aSColin Percival		echo "we are currently running (`uname -r`)!"
114448ffe56aSColin Percival		echo "Cowardly refusing to proceed any further."
114548ffe56aSColin Percival		return 1
114648ffe56aSColin Percival	fi
114748ffe56aSColin Percival
114848ffe56aSColin Percival	# If "tag" exists and corresponds to ${RELNUM}, make sure that
114948ffe56aSColin Percival	# it contains a patch number <= RELPATCHNUM, in order to protect
115048ffe56aSColin Percival	# against rollback (replay) attacks.
115148ffe56aSColin Percival	if [ -f tag ] &&
115248ffe56aSColin Percival	    grep -qE	\
115348ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
115448ffe56aSColin Percival		tag; then
115548ffe56aSColin Percival		LASTRELPATCHNUM=`cut -f 4 -d '|' < tag`
115648ffe56aSColin Percival
115748ffe56aSColin Percival		if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then
115848ffe56aSColin Percival			echo
115948ffe56aSColin Percival			echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
116048ffe56aSColin Percival			echo " are older than the"
116148ffe56aSColin Percival			echo -n "most recently seen updates"
116248ffe56aSColin Percival			echo " (${RELNUM}-p${LASTRELPATCHNUM})."
116348ffe56aSColin Percival			echo "Cowardly refusing to proceed any further."
116448ffe56aSColin Percival			return 1
116548ffe56aSColin Percival		fi
116648ffe56aSColin Percival	fi
116748ffe56aSColin Percival}
116848ffe56aSColin Percival
116948ffe56aSColin Percival# Fetch metadata index file
117048ffe56aSColin Percivalfetch_metadata_index () {
117148ffe56aSColin Percival	echo ${NDEBUG} "Fetching metadata index... "
117248ffe56aSColin Percival	rm -f ${TINDEXHASH}
117348ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH}
117448ffe56aSColin Percival	    2>${QUIETREDIR}
117548ffe56aSColin Percival	if ! [ -f ${TINDEXHASH} ]; then
117648ffe56aSColin Percival		echo "failed."
117748ffe56aSColin Percival		return 1
117848ffe56aSColin Percival	fi
117948ffe56aSColin Percival	if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then
118048ffe56aSColin Percival		echo "update metadata index corrupt."
118148ffe56aSColin Percival		return 1
118248ffe56aSColin Percival	fi
118348ffe56aSColin Percival	echo "done."
118448ffe56aSColin Percival}
118548ffe56aSColin Percival
118648ffe56aSColin Percival# Print an error message about signed metadata being bogus.
118748ffe56aSColin Percivalfetch_metadata_bogus () {
118848ffe56aSColin Percival	echo
118948ffe56aSColin Percival	echo "The update metadata$1 is correctly signed, but"
119048ffe56aSColin Percival	echo "failed an integrity check."
119148ffe56aSColin Percival	echo "Cowardly refusing to proceed any further."
119248ffe56aSColin Percival	return 1
119348ffe56aSColin Percival}
119448ffe56aSColin Percival
119548ffe56aSColin Percival# Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH}
119648ffe56aSColin Percival# with the lines not named in $@ from tINDEX.present (if that file exists).
119748ffe56aSColin Percivalfetch_metadata_index_merge () {
119848ffe56aSColin Percival	for METAFILE in $@; do
119948ffe56aSColin Percival		if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l`	\
120048ffe56aSColin Percival		    -ne 1 ]; then
120148ffe56aSColin Percival			fetch_metadata_bogus " index"
120248ffe56aSColin Percival			return 1
120348ffe56aSColin Percival		fi
120448ffe56aSColin Percival
120548ffe56aSColin Percival		grep -E "${METAFILE}\|" ${TINDEXHASH}
120648ffe56aSColin Percival	done |
120748ffe56aSColin Percival	    sort > tINDEX.wanted
120848ffe56aSColin Percival
120948ffe56aSColin Percival	if [ -f tINDEX.present ]; then
121048ffe56aSColin Percival		join -t '|' -v 2 tINDEX.wanted tINDEX.present |
121148ffe56aSColin Percival		    sort -m - tINDEX.wanted > tINDEX.new
121248ffe56aSColin Percival		rm tINDEX.wanted
121348ffe56aSColin Percival	else
121448ffe56aSColin Percival		mv tINDEX.wanted tINDEX.new
121548ffe56aSColin Percival	fi
121648ffe56aSColin Percival}
121748ffe56aSColin Percival
121848ffe56aSColin Percival# Sanity check all the lines of tINDEX.new.  Even if more metadata lines
121948ffe56aSColin Percival# are added by future versions of the server, this won't cause problems,
122048ffe56aSColin Percival# since the only lines which appear in tINDEX.new are the ones which we
122148ffe56aSColin Percival# specifically grepped out of ${TINDEXHASH}.
122248ffe56aSColin Percivalfetch_metadata_index_sanity () {
122348ffe56aSColin Percival	if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then
122448ffe56aSColin Percival		fetch_metadata_bogus " index"
122548ffe56aSColin Percival		return 1
122648ffe56aSColin Percival	fi
122748ffe56aSColin Percival}
122848ffe56aSColin Percival
122948ffe56aSColin Percival# Sanity check the metadata file $1.
123048ffe56aSColin Percivalfetch_metadata_sanity () {
123148ffe56aSColin Percival	# Some aliases to save space later: ${P} is a character which can
123248ffe56aSColin Percival	# appear in a path; ${M} is the four numeric metadata fields; and
123348ffe56aSColin Percival	# ${H} is a sha256 hash.
1234*7c06c7c5SKris Moore	P="[-+./:=,%@_[~[:alnum:]]"
123548ffe56aSColin Percival	M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+"
123648ffe56aSColin Percival	H="[0-9a-f]{64}"
123748ffe56aSColin Percival
123848ffe56aSColin Percival	# Check that the first four fields make sense.
123948ffe56aSColin Percival	if gunzip -c < files/$1.gz |
124048ffe56aSColin Percival	    grep -qvE "^[a-z]+\|[0-9a-z]+\|${P}+\|[fdL-]\|"; then
124148ffe56aSColin Percival		fetch_metadata_bogus ""
124248ffe56aSColin Percival		return 1
124348ffe56aSColin Percival	fi
124448ffe56aSColin Percival
124548ffe56aSColin Percival	# Remove the first three fields.
124648ffe56aSColin Percival	gunzip -c < files/$1.gz |
124748ffe56aSColin Percival	    cut -f 4- -d '|' > sanitycheck.tmp
124848ffe56aSColin Percival
124948ffe56aSColin Percival	# Sanity check entries with type 'f'
125048ffe56aSColin Percival	if grep -E '^f' sanitycheck.tmp |
125148ffe56aSColin Percival	    grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then
125248ffe56aSColin Percival		fetch_metadata_bogus ""
125348ffe56aSColin Percival		return 1
125448ffe56aSColin Percival	fi
125548ffe56aSColin Percival
125648ffe56aSColin Percival	# Sanity check entries with type 'd'
125748ffe56aSColin Percival	if grep -E '^d' sanitycheck.tmp |
125848ffe56aSColin Percival	    grep -qvE "^d\|${M}\|\|\$"; then
125948ffe56aSColin Percival		fetch_metadata_bogus ""
126048ffe56aSColin Percival		return 1
126148ffe56aSColin Percival	fi
126248ffe56aSColin Percival
126348ffe56aSColin Percival	# Sanity check entries with type 'L'
126448ffe56aSColin Percival	if grep -E '^L' sanitycheck.tmp |
126548ffe56aSColin Percival	    grep -qvE "^L\|${M}\|${P}*\|\$"; then
126648ffe56aSColin Percival		fetch_metadata_bogus ""
126748ffe56aSColin Percival		return 1
126848ffe56aSColin Percival	fi
126948ffe56aSColin Percival
127048ffe56aSColin Percival	# Sanity check entries with type '-'
127148ffe56aSColin Percival	if grep -E '^-' sanitycheck.tmp |
127248ffe56aSColin Percival	    grep -qvE "^-\|\|\|\|\|\|"; then
127348ffe56aSColin Percival		fetch_metadata_bogus ""
127448ffe56aSColin Percival		return 1
127548ffe56aSColin Percival	fi
127648ffe56aSColin Percival
127748ffe56aSColin Percival	# Clean up
127848ffe56aSColin Percival	rm sanitycheck.tmp
127948ffe56aSColin Percival}
128048ffe56aSColin Percival
128148ffe56aSColin Percival# Fetch the metadata index and metadata files listed in $@,
128248ffe56aSColin Percival# taking advantage of metadata patches where possible.
128348ffe56aSColin Percivalfetch_metadata () {
128448ffe56aSColin Percival	fetch_metadata_index || return 1
128548ffe56aSColin Percival	fetch_metadata_index_merge $@ || return 1
128648ffe56aSColin Percival	fetch_metadata_index_sanity || return 1
128748ffe56aSColin Percival
128848ffe56aSColin Percival	# Generate a list of wanted metadata patches
128948ffe56aSColin Percival	join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new |
129048ffe56aSColin Percival	    fetch_make_patchlist > patchlist
129148ffe56aSColin Percival
129248ffe56aSColin Percival	if [ -s patchlist ]; then
129348ffe56aSColin Percival		# Attempt to fetch metadata patches
129448ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
129548ffe56aSColin Percival		echo ${NDEBUG} "metadata patches.${DDSTATS}"
129648ffe56aSColin Percival		tr '|' '-' < patchlist |
129748ffe56aSColin Percival		    lam -s "${FETCHDIR}/tp/" - -s ".gz" |
129848ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
129948ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
130048ffe56aSColin Percival		echo "done."
130148ffe56aSColin Percival
130248ffe56aSColin Percival		# Attempt to apply metadata patches
130348ffe56aSColin Percival		echo -n "Applying metadata patches... "
130448ffe56aSColin Percival		tr '|' ' ' < patchlist |
130548ffe56aSColin Percival		    while read X Y; do
130648ffe56aSColin Percival			if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
130748ffe56aSColin Percival			gunzip -c < ${X}-${Y}.gz > diff
130848ffe56aSColin Percival			gunzip -c < files/${X}.gz > diff-OLD
130948ffe56aSColin Percival
131048ffe56aSColin Percival			# Figure out which lines are being added and removed
131148ffe56aSColin Percival			grep -E '^-' diff |
131248ffe56aSColin Percival			    cut -c 2- |
131348ffe56aSColin Percival			    while read PREFIX; do
131448ffe56aSColin Percival				look "${PREFIX}" diff-OLD
131548ffe56aSColin Percival			    done |
131648ffe56aSColin Percival			    sort > diff-rm
131748ffe56aSColin Percival			grep -E '^\+' diff |
131848ffe56aSColin Percival			    cut -c 2- > diff-add
131948ffe56aSColin Percival
132048ffe56aSColin Percival			# Generate the new file
132148ffe56aSColin Percival			comm -23 diff-OLD diff-rm |
132248ffe56aSColin Percival			    sort - diff-add > diff-NEW
132348ffe56aSColin Percival
132448ffe56aSColin Percival			if [ `${SHA256} -q diff-NEW` = ${Y} ]; then
132548ffe56aSColin Percival				mv diff-NEW files/${Y}
132648ffe56aSColin Percival				gzip -n files/${Y}
132748ffe56aSColin Percival			else
132848ffe56aSColin Percival				mv diff-NEW ${Y}.bad
132948ffe56aSColin Percival			fi
133048ffe56aSColin Percival			rm -f ${X}-${Y}.gz diff
133148ffe56aSColin Percival			rm -f diff-OLD diff-NEW diff-add diff-rm
133248ffe56aSColin Percival		done 2>${QUIETREDIR}
133348ffe56aSColin Percival		echo "done."
133448ffe56aSColin Percival	fi
133548ffe56aSColin Percival
133648ffe56aSColin Percival	# Update metadata without patches
133748ffe56aSColin Percival	cut -f 2 -d '|' < tINDEX.new |
133848ffe56aSColin Percival	    while read Y; do
133948ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
134048ffe56aSColin Percival			echo ${Y};
134148ffe56aSColin Percival		fi
1342bce02f98SColin Percival	    done |
1343bce02f98SColin Percival	    sort -u > filelist
134448ffe56aSColin Percival
134548ffe56aSColin Percival	if [ -s filelist ]; then
134648ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
134748ffe56aSColin Percival		echo ${NDEBUG} "metadata files... "
134848ffe56aSColin Percival		lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist |
134948ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
135048ffe56aSColin Percival		    2>${QUIETREDIR}
135148ffe56aSColin Percival
135248ffe56aSColin Percival		while read Y; do
135348ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
135448ffe56aSColin Percival				echo "failed."
135548ffe56aSColin Percival				return 1
135648ffe56aSColin Percival			fi
135748ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
135848ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
135948ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
136048ffe56aSColin Percival			else
136148ffe56aSColin Percival				echo "metadata is corrupt."
136248ffe56aSColin Percival				return 1
136348ffe56aSColin Percival			fi
136448ffe56aSColin Percival		done < filelist
136548ffe56aSColin Percival		echo "done."
136648ffe56aSColin Percival	fi
136748ffe56aSColin Percival
136848ffe56aSColin Percival# Sanity-check the metadata files.
136948ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new > filelist
137048ffe56aSColin Percival	while read X; do
137148ffe56aSColin Percival		fetch_metadata_sanity ${X} || return 1
137248ffe56aSColin Percival	done < filelist
137348ffe56aSColin Percival
137448ffe56aSColin Percival# Remove files which are no longer needed
137548ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.present |
137648ffe56aSColin Percival	    sort > oldfiles
137748ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new |
137848ffe56aSColin Percival	    sort |
137948ffe56aSColin Percival	    comm -13 - oldfiles |
138048ffe56aSColin Percival	    lam -s "files/" - -s ".gz" |
138148ffe56aSColin Percival	    xargs rm -f
138248ffe56aSColin Percival	rm patchlist filelist oldfiles
138348ffe56aSColin Percival	rm ${TINDEXHASH}
138448ffe56aSColin Percival
138548ffe56aSColin Percival# We're done!
138648ffe56aSColin Percival	mv tINDEX.new tINDEX.present
138748ffe56aSColin Percival	mv tag.new tag
138848ffe56aSColin Percival
138948ffe56aSColin Percival	return 0
139048ffe56aSColin Percival}
139148ffe56aSColin Percival
1392db6b0a61SColin Percival# Extract a subset of a downloaded metadata file containing only the parts
1393db6b0a61SColin Percival# which are listed in COMPONENTS.
1394db6b0a61SColin Percivalfetch_filter_metadata_components () {
1395db6b0a61SColin Percival	METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
1396db6b0a61SColin Percival	gunzip -c < files/${METAHASH}.gz > $1.all
1397db6b0a61SColin Percival
1398db6b0a61SColin Percival	# Fish out the lines belonging to components we care about.
1399db6b0a61SColin Percival	for C in ${COMPONENTS}; do
1400db6b0a61SColin Percival		look "`echo ${C} | tr '/' '|'`|" $1.all
1401db6b0a61SColin Percival	done > $1
1402db6b0a61SColin Percival
1403db6b0a61SColin Percival	# Remove temporary file.
1404db6b0a61SColin Percival	rm $1.all
1405db6b0a61SColin Percival}
1406db6b0a61SColin Percival
1407b698a3abSColin Percival# Generate a filtered version of the metadata file $1 from the downloaded
140848ffe56aSColin Percival# file, by fishing out the lines corresponding to components we're trying
140948ffe56aSColin Percival# to keep updated, and then removing lines corresponding to paths we want
141048ffe56aSColin Percival# to ignore.
141148ffe56aSColin Percivalfetch_filter_metadata () {
141248ffe56aSColin Percival	# Fish out the lines belonging to components we care about.
1413db6b0a61SColin Percival	fetch_filter_metadata_components $1
1414db6b0a61SColin Percival
141548ffe56aSColin Percival	# Canonicalize directory names by removing any trailing / in
141648ffe56aSColin Percival	# order to avoid listing directories multiple times if they
141748ffe56aSColin Percival	# belong to multiple components.  Turning "/" into "" doesn't
141848ffe56aSColin Percival	# matter, since we add a leading "/" when we use paths later.
1419db6b0a61SColin Percival	cut -f 3- -d '|' $1 |
142048ffe56aSColin Percival	    sed -e 's,/|d|,|d|,' |
14217e654612SColin Percival	    sed -e 's,/|-|,|-|,' |
142248ffe56aSColin Percival	    sort -u > $1.tmp
142348ffe56aSColin Percival
142448ffe56aSColin Percival	# Figure out which lines to ignore and remove them.
142548ffe56aSColin Percival	for X in ${IGNOREPATHS}; do
142648ffe56aSColin Percival		grep -E "^${X}" $1.tmp
142748ffe56aSColin Percival	done |
142848ffe56aSColin Percival	    sort -u |
142948ffe56aSColin Percival	    comm -13 - $1.tmp > $1
143048ffe56aSColin Percival
143148ffe56aSColin Percival	# Remove temporary files.
1432db6b0a61SColin Percival	rm $1.tmp
143348ffe56aSColin Percival}
143448ffe56aSColin Percival
1435db6b0a61SColin Percival# Filter the metadata file $1 by adding lines with "/boot/$2"
1436bce02f98SColin Percival# replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the
1437db6b0a61SColin Percival# trailing "/kernel"); and if "/boot/$2" does not exist, remove
1438bce02f98SColin Percival# the original lines which start with that.
1439bce02f98SColin Percival# Put another way: Deal with the fact that the FOO kernel is sometimes
1440bce02f98SColin Percival# installed in /boot/FOO/ and is sometimes installed elsewhere.
144148ffe56aSColin Percivalfetch_filter_kernel_names () {
1442db6b0a61SColin Percival	grep ^/boot/$2 $1 |
1443db6b0a61SColin Percival	    sed -e "s,/boot/$2,${KERNELDIR},g" |
144448ffe56aSColin Percival	    sort - $1 > $1.tmp
144548ffe56aSColin Percival	mv $1.tmp $1
1446bce02f98SColin Percival
1447db6b0a61SColin Percival	if ! [ -d /boot/$2 ]; then
1448db6b0a61SColin Percival		grep -v ^/boot/$2 $1 > $1.tmp
1449bce02f98SColin Percival		mv $1.tmp $1
1450bce02f98SColin Percival	fi
145148ffe56aSColin Percival}
145248ffe56aSColin Percival
145348ffe56aSColin Percival# For all paths appearing in $1 or $3, inspect the system
145448ffe56aSColin Percival# and generate $2 describing what is currently installed.
145548ffe56aSColin Percivalfetch_inspect_system () {
145648ffe56aSColin Percival	# No errors yet...
145748ffe56aSColin Percival	rm -f .err
145848ffe56aSColin Percival
145948ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
146048ffe56aSColin Percival	echo -n "Inspecting system... "
146148ffe56aSColin Percival
146248ffe56aSColin Percival	# Generate list of files to inspect
146348ffe56aSColin Percival	cat $1 $3 |
146448ffe56aSColin Percival	    cut -f 1 -d '|' |
146548ffe56aSColin Percival	    sort -u > filelist
146648ffe56aSColin Percival
146748ffe56aSColin Percival	# Examine each file and output lines of the form
146848ffe56aSColin Percival	# /path/to/file|type|device-inum|user|group|perm|flags|value
146948ffe56aSColin Percival	# sorted by device and inode number.
147048ffe56aSColin Percival	while read F; do
147148ffe56aSColin Percival		# If the symlink/file/directory does not exist, record this.
147248ffe56aSColin Percival		if ! [ -e ${BASEDIR}/${F} ]; then
147348ffe56aSColin Percival			echo "${F}|-||||||"
147448ffe56aSColin Percival			continue
147548ffe56aSColin Percival		fi
147648ffe56aSColin Percival		if ! [ -r ${BASEDIR}/${F} ]; then
147748ffe56aSColin Percival			echo "Cannot read file: ${BASEDIR}/${F}"	\
147848ffe56aSColin Percival			    >/dev/stderr
147948ffe56aSColin Percival			touch .err
148048ffe56aSColin Percival			return 1
148148ffe56aSColin Percival		fi
148248ffe56aSColin Percival
148348ffe56aSColin Percival		# Otherwise, output an index line.
148448ffe56aSColin Percival		if [ -L ${BASEDIR}/${F} ]; then
148548ffe56aSColin Percival			echo -n "${F}|L|"
148648ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
148748ffe56aSColin Percival			readlink ${BASEDIR}/${F};
148848ffe56aSColin Percival		elif [ -f ${BASEDIR}/${F} ]; then
148948ffe56aSColin Percival			echo -n "${F}|f|"
149048ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
149148ffe56aSColin Percival			sha256 -q ${BASEDIR}/${F};
149248ffe56aSColin Percival		elif [ -d ${BASEDIR}/${F} ]; then
149348ffe56aSColin Percival			echo -n "${F}|d|"
149448ffe56aSColin Percival			stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
149548ffe56aSColin Percival		else
149648ffe56aSColin Percival			echo "Unknown file type: ${BASEDIR}/${F}"	\
149748ffe56aSColin Percival			    >/dev/stderr
149848ffe56aSColin Percival			touch .err
149948ffe56aSColin Percival			return 1
150048ffe56aSColin Percival		fi
150148ffe56aSColin Percival	done < filelist |
150248ffe56aSColin Percival	    sort -k 3,3 -t '|' > $2.tmp
150348ffe56aSColin Percival	rm filelist
150448ffe56aSColin Percival
15056dcc68c8SBenedict Reuschling	# Check if an error occurred during system inspection
150648ffe56aSColin Percival	if [ -f .err ]; then
150748ffe56aSColin Percival		return 1
150848ffe56aSColin Percival	fi
150948ffe56aSColin Percival
151048ffe56aSColin Percival	# Convert to the form
151148ffe56aSColin Percival	# /path/to/file|type|user|group|perm|flags|value|hlink
151248ffe56aSColin Percival	# by resolving identical device and inode numbers into hard links.
151348ffe56aSColin Percival	cut -f 1,3 -d '|' $2.tmp |
151448ffe56aSColin Percival	    sort -k 1,1 -t '|' |
151548ffe56aSColin Percival	    sort -s -u -k 2,2 -t '|' |
151648ffe56aSColin Percival	    join -1 2 -2 3 -t '|' - $2.tmp |
151748ffe56aSColin Percival	    awk -F \| -v OFS=\|		\
151848ffe56aSColin Percival		'{
151948ffe56aSColin Percival		    if (($2 == $3) || ($4 == "-"))
152048ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,""
152148ffe56aSColin Percival		    else
152248ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,$2
152348ffe56aSColin Percival		}' |
152448ffe56aSColin Percival	    sort > $2
152548ffe56aSColin Percival	rm $2.tmp
152648ffe56aSColin Percival
152748ffe56aSColin Percival	# We're finished looking around
152848ffe56aSColin Percival	echo "done."
152948ffe56aSColin Percival}
153048ffe56aSColin Percival
1531db6b0a61SColin Percival# For any paths matching ${MERGECHANGES}, compare $1 and $2 and find any
1532db6b0a61SColin Percival# files which differ; generate $3 containing these paths and the old hashes.
1533db6b0a61SColin Percivalfetch_filter_mergechanges () {
1534db6b0a61SColin Percival	# Pull out the paths and hashes of the files matching ${MERGECHANGES}.
1535db6b0a61SColin Percival	for F in $1 $2; do
1536db6b0a61SColin Percival		for X in ${MERGECHANGES}; do
1537db6b0a61SColin Percival			grep -E "^${X}" ${F}
1538db6b0a61SColin Percival		done |
1539db6b0a61SColin Percival		    cut -f 1,2,7 -d '|' |
1540db6b0a61SColin Percival		    sort > ${F}-values
1541db6b0a61SColin Percival	done
1542db6b0a61SColin Percival
1543db6b0a61SColin Percival	# Any line in $2-values which doesn't appear in $1-values and is a
1544db6b0a61SColin Percival	# file means that we should list the path in $3.
1545db6b0a61SColin Percival	comm -13 $1-values $2-values |
1546db6b0a61SColin Percival	    fgrep '|f|' |
1547db6b0a61SColin Percival	    cut -f 1 -d '|' > $2-paths
1548db6b0a61SColin Percival
1549db6b0a61SColin Percival	# For each path, pull out one (and only one!) entry from $1-values.
1550db6b0a61SColin Percival	# Note that we cannot distinguish which "old" version the user made
1551db6b0a61SColin Percival	# changes to; but hopefully any changes which occur due to security
1552db6b0a61SColin Percival	# updates will exist in both the "new" version and the version which
1553db6b0a61SColin Percival	# the user has installed, so the merging will still work.
1554db6b0a61SColin Percival	while read X; do
1555db6b0a61SColin Percival		look "${X}|" $1-values |
1556db6b0a61SColin Percival		    head -1
1557db6b0a61SColin Percival	done < $2-paths > $3
1558db6b0a61SColin Percival
1559db6b0a61SColin Percival	# Clean up
1560db6b0a61SColin Percival	rm $1-values $2-values $2-paths
1561db6b0a61SColin Percival}
1562db6b0a61SColin Percival
156348ffe56aSColin Percival# For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123]
1564db6b0a61SColin Percival# which correspond to lines in $2 with hashes not matching $1 or $3, unless
1565db6b0a61SColin Percival# the paths are listed in $4.  For entries in $2 marked "not present"
1566db6b0a61SColin Percival# (aka. type -), remove lines from $[123] unless there is a corresponding
1567db6b0a61SColin Percival# entry in $1.
156848ffe56aSColin Percivalfetch_filter_unmodified_notpresent () {
156948ffe56aSColin Percival	# Figure out which lines of $1 and $3 correspond to bits which
157048ffe56aSColin Percival	# should only be updated if they haven't changed, and fish out
157148ffe56aSColin Percival	# the (path, type, value) tuples.
157248ffe56aSColin Percival	# NOTE: We don't consider a file to be "modified" if it matches
157348ffe56aSColin Percival	# the hash from $3.
157448ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
157548ffe56aSColin Percival		grep -E "^${X}" $1
157648ffe56aSColin Percival		grep -E "^${X}" $3
157748ffe56aSColin Percival	done |
157848ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
157948ffe56aSColin Percival	    sort > $1-values
158048ffe56aSColin Percival
158148ffe56aSColin Percival	# Do the same for $2.
158248ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
158348ffe56aSColin Percival		grep -E "^${X}" $2
158448ffe56aSColin Percival	done |
158548ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
158648ffe56aSColin Percival	    sort > $2-values
158748ffe56aSColin Percival
158848ffe56aSColin Percival	# Any entry in $2-values which is not in $1-values corresponds to
1589db6b0a61SColin Percival	# a path which we need to remove from $1, $2, and $3, unless it
1590db6b0a61SColin Percival	# that path appears in $4.
1591db6b0a61SColin Percival	comm -13 $1-values $2-values |
1592db6b0a61SColin Percival	    sort -t '|' -k 1,1 > mlines.tmp
1593db6b0a61SColin Percival	cut -f 1 -d '|' $4 |
1594db6b0a61SColin Percival	    sort |
1595db6b0a61SColin Percival	    join -v 2 -t '|' - mlines.tmp |
1596db6b0a61SColin Percival	    sort > mlines
1597db6b0a61SColin Percival	rm $1-values $2-values mlines.tmp
159848ffe56aSColin Percival
159948ffe56aSColin Percival	# Any lines in $2 which are not in $1 AND are "not present" lines
160048ffe56aSColin Percival	# also belong in mlines.
160148ffe56aSColin Percival	comm -13 $1 $2 |
160248ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
160348ffe56aSColin Percival	    fgrep '|-|' >> mlines
160448ffe56aSColin Percival
160548ffe56aSColin Percival	# Remove lines from $1, $2, and $3
160648ffe56aSColin Percival	for X in $1 $2 $3; do
160748ffe56aSColin Percival		sort -t '|' -k 1,1 ${X} > ${X}.tmp
160848ffe56aSColin Percival		cut -f 1 -d '|' < mlines |
160948ffe56aSColin Percival		    sort |
161048ffe56aSColin Percival		    join -v 2 -t '|' - ${X}.tmp |
161148ffe56aSColin Percival		    sort > ${X}
161248ffe56aSColin Percival		rm ${X}.tmp
161348ffe56aSColin Percival	done
161448ffe56aSColin Percival
161548ffe56aSColin Percival	# Store a list of the modified files, for future reference
161648ffe56aSColin Percival	fgrep -v '|-|' mlines |
161748ffe56aSColin Percival	    cut -f 1 -d '|' > modifiedfiles
161848ffe56aSColin Percival	rm mlines
161948ffe56aSColin Percival}
162048ffe56aSColin Percival
162148ffe56aSColin Percival# For each entry in $1 of type -, remove any corresponding
162248ffe56aSColin Percival# entry from $2 if ${ALLOWADD} != "yes".  Remove all entries
162348ffe56aSColin Percival# of type - from $1.
162448ffe56aSColin Percivalfetch_filter_allowadd () {
162548ffe56aSColin Percival	cut -f 1,2 -d '|' < $1 |
162648ffe56aSColin Percival	    fgrep '|-' |
162748ffe56aSColin Percival	    cut -f 1 -d '|' > filesnotpresent
162848ffe56aSColin Percival
162948ffe56aSColin Percival	if [ ${ALLOWADD} != "yes" ]; then
163048ffe56aSColin Percival		sort < $2 |
163148ffe56aSColin Percival		    join -v 1 -t '|' - filesnotpresent |
163248ffe56aSColin Percival		    sort > $2.tmp
163348ffe56aSColin Percival		mv $2.tmp $2
163448ffe56aSColin Percival	fi
163548ffe56aSColin Percival
163648ffe56aSColin Percival	sort < $1 |
163748ffe56aSColin Percival	    join -v 1 -t '|' - filesnotpresent |
163848ffe56aSColin Percival	    sort > $1.tmp
163948ffe56aSColin Percival	mv $1.tmp $1
164048ffe56aSColin Percival	rm filesnotpresent
164148ffe56aSColin Percival}
164248ffe56aSColin Percival
164348ffe56aSColin Percival# If ${ALLOWDELETE} != "yes", then remove any entries from $1
164448ffe56aSColin Percival# which don't correspond to entries in $2.
164548ffe56aSColin Percivalfetch_filter_allowdelete () {
164648ffe56aSColin Percival	# Produce a lists ${PATH}|${TYPE}
164748ffe56aSColin Percival	for X in $1 $2; do
164848ffe56aSColin Percival		cut -f 1-2 -d '|' < ${X} |
164948ffe56aSColin Percival		    sort -u > ${X}.nodes
165048ffe56aSColin Percival	done
165148ffe56aSColin Percival
165248ffe56aSColin Percival	# Figure out which lines need to be removed from $1.
165348ffe56aSColin Percival	if [ ${ALLOWDELETE} != "yes" ]; then
165448ffe56aSColin Percival		comm -23 $1.nodes $2.nodes > $1.badnodes
165548ffe56aSColin Percival	else
165648ffe56aSColin Percival		: > $1.badnodes
165748ffe56aSColin Percival	fi
165848ffe56aSColin Percival
165948ffe56aSColin Percival	# Remove the relevant lines from $1
166048ffe56aSColin Percival	while read X; do
166148ffe56aSColin Percival		look "${X}|" $1
166248ffe56aSColin Percival	done < $1.badnodes |
166348ffe56aSColin Percival	    comm -13 - $1 > $1.tmp
166448ffe56aSColin Percival	mv $1.tmp $1
166548ffe56aSColin Percival
166648ffe56aSColin Percival	rm $1.badnodes $1.nodes $2.nodes
166748ffe56aSColin Percival}
166848ffe56aSColin Percival
166948ffe56aSColin Percival# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2
167048ffe56aSColin Percival# with metadata not matching any entry in $1, replace the corresponding
167148ffe56aSColin Percival# line of $3 with one having the same metadata as the entry in $2.
167248ffe56aSColin Percivalfetch_filter_modified_metadata () {
167348ffe56aSColin Percival	# Fish out the metadata from $1 and $2
167448ffe56aSColin Percival	for X in $1 $2; do
167548ffe56aSColin Percival		cut -f 1-6 -d '|' < ${X} > ${X}.metadata
167648ffe56aSColin Percival	done
167748ffe56aSColin Percival
167848ffe56aSColin Percival	# Find the metadata we need to keep
167948ffe56aSColin Percival	if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then
168048ffe56aSColin Percival		comm -13 $1.metadata $2.metadata > keepmeta
168148ffe56aSColin Percival	else
168248ffe56aSColin Percival		: > keepmeta
168348ffe56aSColin Percival	fi
168448ffe56aSColin Percival
168548ffe56aSColin Percival	# Extract the lines which we need to remove from $3, and
168648ffe56aSColin Percival	# construct the lines which we need to add to $3.
168748ffe56aSColin Percival	: > $3.remove
168848ffe56aSColin Percival	: > $3.add
168948ffe56aSColin Percival	while read LINE; do
169048ffe56aSColin Percival		NODE=`echo "${LINE}" | cut -f 1-2 -d '|'`
169148ffe56aSColin Percival		look "${NODE}|" $3 >> $3.remove
169248ffe56aSColin Percival		look "${NODE}|" $3 |
169348ffe56aSColin Percival		    cut -f 7- -d '|' |
169448ffe56aSColin Percival		    lam -s "${LINE}|" - >> $3.add
169548ffe56aSColin Percival	done < keepmeta
169648ffe56aSColin Percival
169748ffe56aSColin Percival	# Remove the specified lines and add the new lines.
169848ffe56aSColin Percival	sort $3.remove |
169948ffe56aSColin Percival	    comm -13 - $3 |
170048ffe56aSColin Percival	    sort -u - $3.add > $3.tmp
170148ffe56aSColin Percival	mv $3.tmp $3
170248ffe56aSColin Percival
170348ffe56aSColin Percival	rm keepmeta $1.metadata $2.metadata $3.add $3.remove
170448ffe56aSColin Percival}
170548ffe56aSColin Percival
170648ffe56aSColin Percival# Remove lines from $1 and $2 which are identical;
170748ffe56aSColin Percival# no need to update a file if it isn't changing.
170848ffe56aSColin Percivalfetch_filter_uptodate () {
170948ffe56aSColin Percival	comm -23 $1 $2 > $1.tmp
171048ffe56aSColin Percival	comm -13 $1 $2 > $2.tmp
171148ffe56aSColin Percival
171248ffe56aSColin Percival	mv $1.tmp $1
171348ffe56aSColin Percival	mv $2.tmp $2
171448ffe56aSColin Percival}
171548ffe56aSColin Percival
1716db6b0a61SColin Percival# Fetch any "clean" old versions of files we need for merging changes.
1717db6b0a61SColin Percivalfetch_files_premerge () {
1718db6b0a61SColin Percival	# We only need to do anything if $1 is non-empty.
1719db6b0a61SColin Percival	if [ -s $1 ]; then
1720db6b0a61SColin Percival		# Tell the user what we're doing
1721db6b0a61SColin Percival		echo -n "Fetching files from ${OLDRELNUM} for merging... "
1722db6b0a61SColin Percival
1723db6b0a61SColin Percival		# List of files wanted
1724db6b0a61SColin Percival		fgrep '|f|' < $1 |
1725db6b0a61SColin Percival		    cut -f 3 -d '|' |
1726db6b0a61SColin Percival		    sort -u > files.wanted
1727db6b0a61SColin Percival
1728db6b0a61SColin Percival		# Only fetch the files we don't already have
1729db6b0a61SColin Percival		while read Y; do
1730db6b0a61SColin Percival			if [ ! -f "files/${Y}.gz" ]; then
1731db6b0a61SColin Percival				echo ${Y};
1732db6b0a61SColin Percival			fi
1733db6b0a61SColin Percival		done < files.wanted > filelist
1734db6b0a61SColin Percival
1735db6b0a61SColin Percival		# Actually fetch them
1736db6b0a61SColin Percival		lam -s "${OLDFETCHDIR}/f/" - -s ".gz" < filelist |
1737db6b0a61SColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
1738db6b0a61SColin Percival		    2>${QUIETREDIR}
1739db6b0a61SColin Percival
1740db6b0a61SColin Percival		# Make sure we got them all, and move them into /files/
1741db6b0a61SColin Percival		while read Y; do
1742db6b0a61SColin Percival			if ! [ -f ${Y}.gz ]; then
1743db6b0a61SColin Percival				echo "failed."
1744db6b0a61SColin Percival				return 1
1745db6b0a61SColin Percival			fi
1746db6b0a61SColin Percival			if [ `gunzip -c < ${Y}.gz |
1747db6b0a61SColin Percival			    ${SHA256} -q` = ${Y} ]; then
1748db6b0a61SColin Percival				mv ${Y}.gz files/${Y}.gz
1749db6b0a61SColin Percival			else
1750db6b0a61SColin Percival				echo "${Y} has incorrect hash."
1751db6b0a61SColin Percival				return 1
1752db6b0a61SColin Percival			fi
1753db6b0a61SColin Percival		done < filelist
1754db6b0a61SColin Percival		echo "done."
1755db6b0a61SColin Percival
1756db6b0a61SColin Percival		# Clean up
1757db6b0a61SColin Percival		rm filelist files.wanted
1758db6b0a61SColin Percival	fi
1759db6b0a61SColin Percival}
1760db6b0a61SColin Percival
176148ffe56aSColin Percival# Prepare to fetch files: Generate a list of the files we need,
176248ffe56aSColin Percival# copy the unmodified files we have into /files/, and generate
176348ffe56aSColin Percival# a list of patches to download.
176448ffe56aSColin Percivalfetch_files_prepare () {
176548ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
176648ffe56aSColin Percival	echo -n "Preparing to download files... "
176748ffe56aSColin Percival
176848ffe56aSColin Percival	# Reduce indices to ${PATH}|${HASH} pairs
176948ffe56aSColin Percival	for X in $1 $2 $3; do
177048ffe56aSColin Percival		cut -f 1,2,7 -d '|' < ${X} |
177148ffe56aSColin Percival		    fgrep '|f|' |
177248ffe56aSColin Percival		    cut -f 1,3 -d '|' |
177348ffe56aSColin Percival		    sort > ${X}.hashes
177448ffe56aSColin Percival	done
177548ffe56aSColin Percival
177648ffe56aSColin Percival	# List of files wanted
177748ffe56aSColin Percival	cut -f 2 -d '|' < $3.hashes |
17782328d598SColin Percival	    sort -u |
17792328d598SColin Percival	    while read HASH; do
17802328d598SColin Percival		if ! [ -f files/${HASH}.gz ]; then
17812328d598SColin Percival			echo ${HASH}
17822328d598SColin Percival		fi
17832328d598SColin Percival	done > files.wanted
178448ffe56aSColin Percival
178548ffe56aSColin Percival	# Generate a list of unmodified files
178648ffe56aSColin Percival	comm -12 $1.hashes $2.hashes |
178748ffe56aSColin Percival	    sort -k 1,1 -t '|' > unmodified.files
178848ffe56aSColin Percival
178948ffe56aSColin Percival	# Copy all files into /files/.  We only need the unmodified files
179048ffe56aSColin Percival	# for use in patching; but we'll want all of them if the user asks
179148ffe56aSColin Percival	# to rollback the updates later.
1792210b8123SColin Percival	while read LINE; do
1793210b8123SColin Percival		F=`echo "${LINE}" | cut -f 1 -d '|'`
1794210b8123SColin Percival		HASH=`echo "${LINE}" | cut -f 2 -d '|'`
1795210b8123SColin Percival
1796210b8123SColin Percival		# Skip files we already have.
1797210b8123SColin Percival		if [ -f files/${HASH}.gz ]; then
1798210b8123SColin Percival			continue
1799210b8123SColin Percival		fi
1800210b8123SColin Percival
1801210b8123SColin Percival		# Make sure the file hasn't changed.
180248ffe56aSColin Percival		cp "${BASEDIR}/${F}" tmpfile
1803210b8123SColin Percival		if [ `sha256 -q tmpfile` != ${HASH} ]; then
1804210b8123SColin Percival			echo
1805210b8123SColin Percival			echo "File changed while FreeBSD Update running: ${F}"
1806210b8123SColin Percival			return 1
1807210b8123SColin Percival		fi
1808210b8123SColin Percival
1809210b8123SColin Percival		# Place the file into storage.
1810210b8123SColin Percival		gzip -c < tmpfile > files/${HASH}.gz
181148ffe56aSColin Percival		rm tmpfile
1812210b8123SColin Percival	done < $2.hashes
181348ffe56aSColin Percival
181448ffe56aSColin Percival	# Produce a list of patches to download
181548ffe56aSColin Percival	sort -k 1,1 -t '|' $3.hashes |
181648ffe56aSColin Percival	    join -t '|' -o 2.2,1.2 - unmodified.files |
181748ffe56aSColin Percival	    fetch_make_patchlist > patchlist
181848ffe56aSColin Percival
181948ffe56aSColin Percival	# Garbage collect
182048ffe56aSColin Percival	rm unmodified.files $1.hashes $2.hashes $3.hashes
182148ffe56aSColin Percival
182248ffe56aSColin Percival	# We don't need the list of possible old files any more.
182348ffe56aSColin Percival	rm $1
182448ffe56aSColin Percival
182548ffe56aSColin Percival	# We're finished making noise
182648ffe56aSColin Percival	echo "done."
182748ffe56aSColin Percival}
182848ffe56aSColin Percival
182948ffe56aSColin Percival# Fetch files.
183048ffe56aSColin Percivalfetch_files () {
183148ffe56aSColin Percival	# Attempt to fetch patches
183248ffe56aSColin Percival	if [ -s patchlist ]; then
183348ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
183448ffe56aSColin Percival		echo ${NDEBUG} "patches.${DDSTATS}"
183548ffe56aSColin Percival		tr '|' '-' < patchlist |
1836db6b0a61SColin Percival		    lam -s "${PATCHDIR}/" - |
183748ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
183848ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
183948ffe56aSColin Percival		echo "done."
184048ffe56aSColin Percival
184148ffe56aSColin Percival		# Attempt to apply patches
184248ffe56aSColin Percival		echo -n "Applying patches... "
184348ffe56aSColin Percival		tr '|' ' ' < patchlist |
184448ffe56aSColin Percival		    while read X Y; do
184548ffe56aSColin Percival			if [ ! -f "${X}-${Y}" ]; then continue; fi
184648ffe56aSColin Percival			gunzip -c < files/${X}.gz > OLD
184748ffe56aSColin Percival
184848ffe56aSColin Percival			bspatch OLD NEW ${X}-${Y}
184948ffe56aSColin Percival
185048ffe56aSColin Percival			if [ `${SHA256} -q NEW` = ${Y} ]; then
185148ffe56aSColin Percival				mv NEW files/${Y}
185248ffe56aSColin Percival				gzip -n files/${Y}
185348ffe56aSColin Percival			fi
185448ffe56aSColin Percival			rm -f diff OLD NEW ${X}-${Y}
185548ffe56aSColin Percival		done 2>${QUIETREDIR}
185648ffe56aSColin Percival		echo "done."
185748ffe56aSColin Percival	fi
185848ffe56aSColin Percival
185948ffe56aSColin Percival	# Download files which couldn't be generate via patching
186048ffe56aSColin Percival	while read Y; do
186148ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
186248ffe56aSColin Percival			echo ${Y};
186348ffe56aSColin Percival		fi
186448ffe56aSColin Percival	done < files.wanted > filelist
186548ffe56aSColin Percival
186648ffe56aSColin Percival	if [ -s filelist ]; then
186748ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
186848ffe56aSColin Percival		echo ${NDEBUG} "files... "
186948ffe56aSColin Percival		lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist |
187048ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
187148ffe56aSColin Percival		    2>${QUIETREDIR}
187248ffe56aSColin Percival
187348ffe56aSColin Percival		while read Y; do
187448ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
187548ffe56aSColin Percival				echo "failed."
187648ffe56aSColin Percival				return 1
187748ffe56aSColin Percival			fi
187848ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
187948ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
188048ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
188148ffe56aSColin Percival			else
188248ffe56aSColin Percival				echo "${Y} has incorrect hash."
188348ffe56aSColin Percival				return 1
188448ffe56aSColin Percival			fi
188548ffe56aSColin Percival		done < filelist
188648ffe56aSColin Percival		echo "done."
188748ffe56aSColin Percival	fi
188848ffe56aSColin Percival
188948ffe56aSColin Percival	# Clean up
189048ffe56aSColin Percival	rm files.wanted filelist patchlist
189148ffe56aSColin Percival}
189248ffe56aSColin Percival
189348ffe56aSColin Percival# Create and populate install manifest directory; and report what updates
189448ffe56aSColin Percival# are available.
189548ffe56aSColin Percivalfetch_create_manifest () {
189648ffe56aSColin Percival	# If we have an existing install manifest, nuke it.
189748ffe56aSColin Percival	if [ -L "${BDHASH}-install" ]; then
189848ffe56aSColin Percival		rm -r ${BDHASH}-install/
189948ffe56aSColin Percival		rm ${BDHASH}-install
190048ffe56aSColin Percival	fi
190148ffe56aSColin Percival
190248ffe56aSColin Percival	# Report to the user if any updates were avoided due to local changes
190348ffe56aSColin Percival	if [ -s modifiedfiles ]; then
190448ffe56aSColin Percival		echo
190548ffe56aSColin Percival		echo -n "The following files are affected by updates, "
190648ffe56aSColin Percival		echo "but no changes have"
190748ffe56aSColin Percival		echo -n "been downloaded because the files have been "
190848ffe56aSColin Percival		echo "modified locally:"
190948ffe56aSColin Percival		cat modifiedfiles
19109c990fb2SGordon Tetlow	fi | $PAGER
191148ffe56aSColin Percival	rm modifiedfiles
191248ffe56aSColin Percival
191348ffe56aSColin Percival	# If no files will be updated, tell the user and exit
191448ffe56aSColin Percival	if ! [ -s INDEX-PRESENT ] &&
191548ffe56aSColin Percival	    ! [ -s INDEX-NEW ]; then
191648ffe56aSColin Percival		rm INDEX-PRESENT INDEX-NEW
191748ffe56aSColin Percival		echo
191848ffe56aSColin Percival		echo -n "No updates needed to update system to "
191948ffe56aSColin Percival		echo "${RELNUM}-p${RELPATCHNUM}."
192048ffe56aSColin Percival		return
192148ffe56aSColin Percival	fi
192248ffe56aSColin Percival
192348ffe56aSColin Percival	# Divide files into (a) removed files, (b) added files, and
192448ffe56aSColin Percival	# (c) updated files.
192548ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-PRESENT |
192648ffe56aSColin Percival	    sort > INDEX-PRESENT.flist
192748ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-NEW |
192848ffe56aSColin Percival	    sort > INDEX-NEW.flist
192948ffe56aSColin Percival	comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed
193048ffe56aSColin Percival	comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added
193148ffe56aSColin Percival	comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated
193248ffe56aSColin Percival	rm INDEX-PRESENT.flist INDEX-NEW.flist
193348ffe56aSColin Percival
193448ffe56aSColin Percival	# Report removed files, if any
193548ffe56aSColin Percival	if [ -s files.removed ]; then
193648ffe56aSColin Percival		echo
193748ffe56aSColin Percival		echo -n "The following files will be removed "
193848ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
193948ffe56aSColin Percival		cat files.removed
19409c990fb2SGordon Tetlow	fi | $PAGER
194148ffe56aSColin Percival	rm files.removed
194248ffe56aSColin Percival
194348ffe56aSColin Percival	# Report added files, if any
194448ffe56aSColin Percival	if [ -s files.added ]; then
194548ffe56aSColin Percival		echo
194648ffe56aSColin Percival		echo -n "The following files will be added "
194748ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
194848ffe56aSColin Percival		cat files.added
19499c990fb2SGordon Tetlow	fi | $PAGER
195048ffe56aSColin Percival	rm files.added
195148ffe56aSColin Percival
195248ffe56aSColin Percival	# Report updated files, if any
195348ffe56aSColin Percival	if [ -s files.updated ]; then
195448ffe56aSColin Percival		echo
195548ffe56aSColin Percival		echo -n "The following files will be updated "
195648ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
195748ffe56aSColin Percival
195848ffe56aSColin Percival		cat files.updated
19599c990fb2SGordon Tetlow	fi | $PAGER
196048ffe56aSColin Percival	rm files.updated
196148ffe56aSColin Percival
196248ffe56aSColin Percival	# Create a directory for the install manifest.
196348ffe56aSColin Percival	MDIR=`mktemp -d install.XXXXXX` || return 1
196448ffe56aSColin Percival
196548ffe56aSColin Percival	# Populate it
196648ffe56aSColin Percival	mv INDEX-PRESENT ${MDIR}/INDEX-OLD
196748ffe56aSColin Percival	mv INDEX-NEW ${MDIR}/INDEX-NEW
196848ffe56aSColin Percival
196948ffe56aSColin Percival	# Link it into place
197048ffe56aSColin Percival	ln -s ${MDIR} ${BDHASH}-install
197148ffe56aSColin Percival}
197248ffe56aSColin Percival
197348ffe56aSColin Percival# Warn about any upcoming EoL
197448ffe56aSColin Percivalfetch_warn_eol () {
197548ffe56aSColin Percival	# What's the current time?
197648ffe56aSColin Percival	NOWTIME=`date "+%s"`
197748ffe56aSColin Percival
197848ffe56aSColin Percival	# When did we last warn about the EoL date?
197948ffe56aSColin Percival	if [ -f lasteolwarn ]; then
198048ffe56aSColin Percival		LASTWARN=`cat lasteolwarn`
198148ffe56aSColin Percival	else
198248ffe56aSColin Percival		LASTWARN=`expr ${NOWTIME} - 63072000`
198348ffe56aSColin Percival	fi
198448ffe56aSColin Percival
198548ffe56aSColin Percival	# If the EoL time is past, warn.
198648ffe56aSColin Percival	if [ ${EOLTIME} -lt ${NOWTIME} ]; then
198748ffe56aSColin Percival		echo
198848ffe56aSColin Percival		cat <<-EOF
1989b698a3abSColin Percival		WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE.
199048ffe56aSColin Percival		Any security issues discovered after `date -r ${EOLTIME}`
199148ffe56aSColin Percival		will not have been corrected.
199248ffe56aSColin Percival		EOF
199348ffe56aSColin Percival		return 1
199448ffe56aSColin Percival	fi
199548ffe56aSColin Percival
199648ffe56aSColin Percival	# Figure out how long it has been since we last warned about the
199748ffe56aSColin Percival	# upcoming EoL, and how much longer we have left.
199848ffe56aSColin Percival	SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}`
199948ffe56aSColin Percival	TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}`
200048ffe56aSColin Percival
200189b14566SColin Percival	# Don't warn if the EoL is more than 3 months away
200289b14566SColin Percival	if [ ${TIMELEFT} -gt 7884000 ]; then
200348ffe56aSColin Percival		return 0
200448ffe56aSColin Percival	fi
200548ffe56aSColin Percival
200648ffe56aSColin Percival	# Don't warn if the time remaining is more than 3 times the time
200748ffe56aSColin Percival	# since the last warning.
200848ffe56aSColin Percival	if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then
200948ffe56aSColin Percival		return 0
201048ffe56aSColin Percival	fi
201148ffe56aSColin Percival
201248ffe56aSColin Percival	# Figure out what time units to use.
201348ffe56aSColin Percival	if [ ${TIMELEFT} -lt 604800 ]; then
201448ffe56aSColin Percival		UNIT="day"
201548ffe56aSColin Percival		SIZE=86400
201648ffe56aSColin Percival	elif [ ${TIMELEFT} -lt 2678400 ]; then
201748ffe56aSColin Percival		UNIT="week"
201848ffe56aSColin Percival		SIZE=604800
201948ffe56aSColin Percival	else
202048ffe56aSColin Percival		UNIT="month"
202148ffe56aSColin Percival		SIZE=2678400
202248ffe56aSColin Percival	fi
202348ffe56aSColin Percival
202448ffe56aSColin Percival	# Compute the right number of units
202548ffe56aSColin Percival	NUM=`expr ${TIMELEFT} / ${SIZE}`
202648ffe56aSColin Percival	if [ ${NUM} != 1 ]; then
202748ffe56aSColin Percival		UNIT="${UNIT}s"
202848ffe56aSColin Percival	fi
202948ffe56aSColin Percival
203048ffe56aSColin Percival	# Print the warning
203148ffe56aSColin Percival	echo
203248ffe56aSColin Percival	cat <<-EOF
203348ffe56aSColin Percival		WARNING: `uname -sr` is approaching its End-of-Life date.
203448ffe56aSColin Percival		It is strongly recommended that you upgrade to a newer
203548ffe56aSColin Percival		release within the next ${NUM} ${UNIT}.
203648ffe56aSColin Percival	EOF
203748ffe56aSColin Percival
203848ffe56aSColin Percival	# Update the stored time of last warning
203948ffe56aSColin Percival	echo ${NOWTIME} > lasteolwarn
204048ffe56aSColin Percival}
204148ffe56aSColin Percival
204248ffe56aSColin Percival# Do the actual work involved in "fetch" / "cron".
204348ffe56aSColin Percivalfetch_run () {
204448ffe56aSColin Percival	workdir_init || return 1
204548ffe56aSColin Percival
204648ffe56aSColin Percival	# Prepare the mirror list.
204748ffe56aSColin Percival	fetch_pick_server_init && fetch_pick_server
204848ffe56aSColin Percival
204948ffe56aSColin Percival	# Try to fetch the public key until we run out of servers.
205048ffe56aSColin Percival	while ! fetch_key; do
205148ffe56aSColin Percival		fetch_pick_server || return 1
205248ffe56aSColin Percival	done
205348ffe56aSColin Percival
205448ffe56aSColin Percival	# Try to fetch the metadata index signature ("tag") until we run
205548ffe56aSColin Percival	# out of available servers; and sanity check the downloaded tag.
205648ffe56aSColin Percival	while ! fetch_tag; do
205748ffe56aSColin Percival		fetch_pick_server || return 1
205848ffe56aSColin Percival	done
205948ffe56aSColin Percival	fetch_tagsanity || return 1
206048ffe56aSColin Percival
206148ffe56aSColin Percival	# Fetch the latest INDEX-NEW and INDEX-OLD files.
206248ffe56aSColin Percival	fetch_metadata INDEX-NEW INDEX-OLD || return 1
206348ffe56aSColin Percival
206448ffe56aSColin Percival	# Generate filtered INDEX-NEW and INDEX-OLD files containing only
206548ffe56aSColin Percival	# the lines which (a) belong to components we care about, and (b)
206648ffe56aSColin Percival	# don't correspond to paths we're explicitly ignoring.
206748ffe56aSColin Percival	fetch_filter_metadata INDEX-NEW || return 1
206848ffe56aSColin Percival	fetch_filter_metadata INDEX-OLD || return 1
206948ffe56aSColin Percival
2070db6b0a61SColin Percival	# Translate /boot/${KERNCONF} into ${KERNELDIR}
2071db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-NEW ${KERNCONF}
2072db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
207348ffe56aSColin Percival
207448ffe56aSColin Percival	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
207548ffe56aSColin Percival	# system and generate an INDEX-PRESENT file.
207648ffe56aSColin Percival	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
207748ffe56aSColin Percival
207848ffe56aSColin Percival	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
207948ffe56aSColin Percival	# correspond to lines in INDEX-PRESENT with hashes not appearing
208048ffe56aSColin Percival	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
208148ffe56aSColin Percival	# INDEX-PRESENT has type - and there isn't a corresponding entry in
208248ffe56aSColin Percival	# INDEX-OLD with type -.
2083db6b0a61SColin Percival	fetch_filter_unmodified_notpresent	\
2084db6b0a61SColin Percival	    INDEX-OLD INDEX-PRESENT INDEX-NEW /dev/null
208548ffe56aSColin Percival
208648ffe56aSColin Percival	# For each entry in INDEX-PRESENT of type -, remove any corresponding
208748ffe56aSColin Percival	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
208848ffe56aSColin Percival	# of type - from INDEX-PRESENT.
208948ffe56aSColin Percival	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
209048ffe56aSColin Percival
209148ffe56aSColin Percival	# If ${ALLOWDELETE} != "yes", then remove any entries from
209248ffe56aSColin Percival	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
209348ffe56aSColin Percival	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
209448ffe56aSColin Percival
209548ffe56aSColin Percival	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
209648ffe56aSColin Percival	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
209748ffe56aSColin Percival	# replace the corresponding line of INDEX-NEW with one having the
209848ffe56aSColin Percival	# same metadata as the entry in INDEX-PRESENT.
209948ffe56aSColin Percival	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
210048ffe56aSColin Percival
210148ffe56aSColin Percival	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
210248ffe56aSColin Percival	# no need to update a file if it isn't changing.
210348ffe56aSColin Percival	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
210448ffe56aSColin Percival
210548ffe56aSColin Percival	# Prepare to fetch files: Generate a list of the files we need,
210648ffe56aSColin Percival	# copy the unmodified files we have into /files/, and generate
210748ffe56aSColin Percival	# a list of patches to download.
2108210b8123SColin Percival	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
210948ffe56aSColin Percival
211048ffe56aSColin Percival	# Fetch files.
211148ffe56aSColin Percival	fetch_files || return 1
211248ffe56aSColin Percival
211348ffe56aSColin Percival	# Create and populate install manifest directory; and report what
211448ffe56aSColin Percival	# updates are available.
211548ffe56aSColin Percival	fetch_create_manifest || return 1
211648ffe56aSColin Percival
211748ffe56aSColin Percival	# Warn about any upcoming EoL
211848ffe56aSColin Percival	fetch_warn_eol || return 1
211948ffe56aSColin Percival}
212048ffe56aSColin Percival
2121db6b0a61SColin Percival# If StrictComponents is not "yes", generate a new components list
2122db6b0a61SColin Percival# with only the components which appear to be installed.
2123db6b0a61SColin Percivalupgrade_guess_components () {
2124db6b0a61SColin Percival	if [ "${STRICTCOMPONENTS}" = "no" ]; then
2125db6b0a61SColin Percival		# Generate filtered INDEX-ALL with only the components listed
2126db6b0a61SColin Percival		# in COMPONENTS.
2127db6b0a61SColin Percival		fetch_filter_metadata_components $1 || return 1
2128db6b0a61SColin Percival
2129db6b0a61SColin Percival		# Tell the user why his disk is suddenly making lots of noise
2130db6b0a61SColin Percival		echo -n "Inspecting system... "
2131db6b0a61SColin Percival
2132db6b0a61SColin Percival		# Look at the files on disk, and assume that a component is
2133db6b0a61SColin Percival		# supposed to be present if it is more than half-present.
2134db6b0a61SColin Percival		cut -f 1-3 -d '|' < INDEX-ALL |
2135db6b0a61SColin Percival		    tr '|' ' ' |
2136db6b0a61SColin Percival		    while read C S F; do
2137db6b0a61SColin Percival			if [ -e ${BASEDIR}/${F} ]; then
2138db6b0a61SColin Percival				echo "+ ${C}|${S}"
2139db6b0a61SColin Percival			fi
2140db6b0a61SColin Percival			echo "= ${C}|${S}"
2141db6b0a61SColin Percival		    done |
2142db6b0a61SColin Percival		    sort |
2143db6b0a61SColin Percival		    uniq -c |
2144db6b0a61SColin Percival		    sed -E 's,^ +,,' > compfreq
2145db6b0a61SColin Percival		grep ' = ' compfreq |
2146db6b0a61SColin Percival		    cut -f 1,3 -d ' ' |
2147db6b0a61SColin Percival		    sort -k 2,2 -t ' ' > compfreq.total
2148db6b0a61SColin Percival		grep ' + ' compfreq |
2149db6b0a61SColin Percival		    cut -f 1,3 -d ' ' |
2150db6b0a61SColin Percival		    sort -k 2,2 -t ' ' > compfreq.present
2151db6b0a61SColin Percival		join -t ' ' -1 2 -2 2 compfreq.present compfreq.total |
2152db6b0a61SColin Percival		    while read S P T; do
2153db6b0a61SColin Percival			if [ ${P} -gt `expr ${T} / 2` ]; then
2154db6b0a61SColin Percival				echo ${S}
2155db6b0a61SColin Percival			fi
2156db6b0a61SColin Percival		    done > comp.present
2157db6b0a61SColin Percival		cut -f 2 -d ' ' < compfreq.total > comp.total
2158db6b0a61SColin Percival		rm INDEX-ALL compfreq compfreq.total compfreq.present
2159db6b0a61SColin Percival
2160db6b0a61SColin Percival		# We're done making noise.
2161db6b0a61SColin Percival		echo "done."
2162db6b0a61SColin Percival
2163db6b0a61SColin Percival		# Sometimes the kernel isn't installed where INDEX-ALL
2164db6b0a61SColin Percival		# thinks that it should be: In particular, it is often in
2165db6b0a61SColin Percival		# /boot/kernel instead of /boot/GENERIC or /boot/SMP.  To
2166db6b0a61SColin Percival		# deal with this, if "kernel|X" is listed in comp.total
2167db6b0a61SColin Percival		# (i.e., is a component which would be upgraded if it is
2168db6b0a61SColin Percival		# found to be present) we will add it to comp.present.
2169db6b0a61SColin Percival		# If "kernel|<anything>" is in comp.total but "kernel|X" is
2170db6b0a61SColin Percival		# not, we print a warning -- the user is running a kernel
2171db6b0a61SColin Percival		# which isn't part of the release.
2172db6b0a61SColin Percival		KCOMP=`echo ${KERNCONF} | tr 'A-Z' 'a-z'`
2173db6b0a61SColin Percival		grep -E "^kernel\|${KCOMP}\$" comp.total >> comp.present
2174db6b0a61SColin Percival
2175db6b0a61SColin Percival		if grep -qE "^kernel\|" comp.total &&
2176db6b0a61SColin Percival		    ! grep -qE "^kernel\|${KCOMP}\$" comp.total; then
2177db6b0a61SColin Percival			cat <<-EOF
2178db6b0a61SColin Percival
2179db6b0a61SColin PercivalWARNING: This system is running a "${KCOMP}" kernel, which is not a
2180db6b0a61SColin Percivalkernel configuration distributed as part of FreeBSD ${RELNUM}.
2181db6b0a61SColin PercivalThis kernel will not be updated: you MUST update the kernel manually
2182db6b0a61SColin Percivalbefore running "$0 install".
2183db6b0a61SColin Percival			EOF
2184db6b0a61SColin Percival		fi
2185db6b0a61SColin Percival
2186db6b0a61SColin Percival		# Re-sort the list of installed components and generate
2187db6b0a61SColin Percival		# the list of non-installed components.
2188db6b0a61SColin Percival		sort -u < comp.present > comp.present.tmp
2189db6b0a61SColin Percival		mv comp.present.tmp comp.present
2190db6b0a61SColin Percival		comm -13 comp.present comp.total > comp.absent
2191db6b0a61SColin Percival
2192db6b0a61SColin Percival		# Ask the user to confirm that what we have is correct.  To
2193db6b0a61SColin Percival		# reduce user confusion, translate "X|Y" back to "X/Y" (as
2194db6b0a61SColin Percival		# subcomponents must be listed in the configuration file).
2195db6b0a61SColin Percival		echo
2196db6b0a61SColin Percival		echo -n "The following components of FreeBSD "
2197db6b0a61SColin Percival		echo "seem to be installed:"
2198db6b0a61SColin Percival		tr '|' '/' < comp.present |
2199db6b0a61SColin Percival		    fmt -72
2200db6b0a61SColin Percival		echo
2201db6b0a61SColin Percival		echo -n "The following components of FreeBSD "
2202db6b0a61SColin Percival		echo "do not seem to be installed:"
2203db6b0a61SColin Percival		tr '|' '/' < comp.absent |
2204db6b0a61SColin Percival		    fmt -72
2205db6b0a61SColin Percival		echo
2206db6b0a61SColin Percival		continuep || return 1
2207db6b0a61SColin Percival		echo
2208db6b0a61SColin Percival
2209db6b0a61SColin Percival		# Suck the generated list of components into ${COMPONENTS}.
2210db6b0a61SColin Percival		# Note that comp.present.tmp is used due to issues with
2211db6b0a61SColin Percival		# pipelines and setting variables.
2212db6b0a61SColin Percival		COMPONENTS=""
2213db6b0a61SColin Percival		tr '|' '/' < comp.present > comp.present.tmp
2214db6b0a61SColin Percival		while read C; do
2215db6b0a61SColin Percival			COMPONENTS="${COMPONENTS} ${C}"
2216db6b0a61SColin Percival		done < comp.present.tmp
2217db6b0a61SColin Percival
2218db6b0a61SColin Percival		# Delete temporary files
2219db6b0a61SColin Percival		rm comp.present comp.present.tmp comp.absent comp.total
2220db6b0a61SColin Percival	fi
2221db6b0a61SColin Percival}
2222db6b0a61SColin Percival
2223db6b0a61SColin Percival# If StrictComponents is not "yes", COMPONENTS contains an entry
2224db6b0a61SColin Percival# corresponding to the currently running kernel, and said kernel
2225db6b0a61SColin Percival# does not exist in the new release, add "kernel/generic" to the
2226db6b0a61SColin Percival# list of components.
2227db6b0a61SColin Percivalupgrade_guess_new_kernel () {
2228db6b0a61SColin Percival	if [ "${STRICTCOMPONENTS}" = "no" ]; then
2229db6b0a61SColin Percival		# Grab the unfiltered metadata file.
2230db6b0a61SColin Percival		METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
2231db6b0a61SColin Percival		gunzip -c < files/${METAHASH}.gz > $1.all
2232db6b0a61SColin Percival
2233db6b0a61SColin Percival		# If "kernel/${KCOMP}" is in ${COMPONENTS} and that component
2234db6b0a61SColin Percival		# isn't in $1.all, we need to add kernel/generic.
2235db6b0a61SColin Percival		for C in ${COMPONENTS}; do
2236db6b0a61SColin Percival			if [ ${C} = "kernel/${KCOMP}" ] &&
2237db6b0a61SColin Percival			    ! grep -qE "^kernel\|${KCOMP}\|" $1.all; then
2238db6b0a61SColin Percival				COMPONENTS="${COMPONENTS} kernel/generic"
2239db6b0a61SColin Percival				NKERNCONF="GENERIC"
2240db6b0a61SColin Percival				cat <<-EOF
2241db6b0a61SColin Percival
2242db6b0a61SColin PercivalWARNING: This system is running a "${KCOMP}" kernel, which is not a
2243db6b0a61SColin Percivalkernel configuration distributed as part of FreeBSD ${RELNUM}.
2244db6b0a61SColin PercivalAs part of upgrading to FreeBSD ${RELNUM}, this kernel will be
2245db6b0a61SColin Percivalreplaced with a "generic" kernel.
2246db6b0a61SColin Percival				EOF
2247db6b0a61SColin Percival				continuep || return 1
2248db6b0a61SColin Percival			fi
2249db6b0a61SColin Percival		done
2250db6b0a61SColin Percival
2251db6b0a61SColin Percival		# Don't need this any more...
2252db6b0a61SColin Percival		rm $1.all
2253db6b0a61SColin Percival	fi
2254db6b0a61SColin Percival}
2255db6b0a61SColin Percival
2256db6b0a61SColin Percival# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2257db6b0a61SColin Percival# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2258db6b0a61SColin Percivalupgrade_oldall_to_oldnew () {
2259db6b0a61SColin Percival	# For each ${F}|... which appears in INDEX-ALL but does not appear
2260db6b0a61SColin Percival	# in INDEX-OLD, add ${F}|-|||||| to INDEX-OLD.
2261db6b0a61SColin Percival	cut -f 1 -d '|' < $1 |
2262db6b0a61SColin Percival	    sort -u > $1.paths
2263db6b0a61SColin Percival	cut -f 1 -d '|' < $2 |
2264db6b0a61SColin Percival	    sort -u |
2265db6b0a61SColin Percival	    comm -13 $1.paths - |
2266db6b0a61SColin Percival	    lam - -s "|-||||||" |
2267db6b0a61SColin Percival	    sort - $1 > $1.tmp
2268db6b0a61SColin Percival	mv $1.tmp $1
2269db6b0a61SColin Percival
2270db6b0a61SColin Percival	# Remove lines from INDEX-OLD which also appear in INDEX-ALL
2271db6b0a61SColin Percival	comm -23 $1 $2 > $1.tmp
2272db6b0a61SColin Percival	mv $1.tmp $1
2273db6b0a61SColin Percival
2274db6b0a61SColin Percival	# Remove lines from INDEX-ALL which have a file name not appearing
2275db6b0a61SColin Percival	# anywhere in INDEX-OLD (since these must be files which haven't
2276db6b0a61SColin Percival	# changed -- if they were new, there would be an entry of type "-").
2277db6b0a61SColin Percival	cut -f 1 -d '|' < $1 |
2278db6b0a61SColin Percival	    sort -u > $1.paths
2279db6b0a61SColin Percival	sort -k 1,1 -t '|' < $2 |
2280db6b0a61SColin Percival	    join -t '|' - $1.paths |
2281db6b0a61SColin Percival	    sort > $2.tmp
2282db6b0a61SColin Percival	rm $1.paths
2283db6b0a61SColin Percival	mv $2.tmp $2
2284db6b0a61SColin Percival
2285db6b0a61SColin Percival	# Rename INDEX-ALL to INDEX-NEW.
2286db6b0a61SColin Percival	mv $2 $3
2287db6b0a61SColin Percival}
2288db6b0a61SColin Percival
22897449d2f5SColin Percival# Helper for upgrade_merge: Return zero true iff the two files differ only
22906d514f10SDag-Erling Smørgrav# in the contents of their RCS tags.
22917449d2f5SColin Percivalsamef () {
22927449d2f5SColin Percival	X=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $1 | ${SHA256}`
22937449d2f5SColin Percival	Y=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $2 | ${SHA256}`
22947449d2f5SColin Percival
22957449d2f5SColin Percival	if [ $X = $Y ]; then
22967449d2f5SColin Percival		return 0;
22977449d2f5SColin Percival	else
22987449d2f5SColin Percival		return 1;
22997449d2f5SColin Percival	fi
23007449d2f5SColin Percival}
23017449d2f5SColin Percival
2302db6b0a61SColin Percival# From the list of "old" files in $1, merge changes in $2 with those in $3,
2303db6b0a61SColin Percival# and update $3 to reflect the hashes of merged files.
2304db6b0a61SColin Percivalupgrade_merge () {
2305db6b0a61SColin Percival	# We only need to do anything if $1 is non-empty.
2306db6b0a61SColin Percival	if [ -s $1 ]; then
2307db6b0a61SColin Percival		cut -f 1 -d '|' $1 |
2308db6b0a61SColin Percival		    sort > $1-paths
2309db6b0a61SColin Percival
2310db6b0a61SColin Percival		# Create staging area for merging files
2311db6b0a61SColin Percival		rm -rf merge/
2312db6b0a61SColin Percival		while read F; do
2313db6b0a61SColin Percival			D=`dirname ${F}`
2314db6b0a61SColin Percival			mkdir -p merge/old/${D}
2315db6b0a61SColin Percival			mkdir -p merge/${OLDRELNUM}/${D}
2316db6b0a61SColin Percival			mkdir -p merge/${RELNUM}/${D}
2317db6b0a61SColin Percival			mkdir -p merge/new/${D}
2318db6b0a61SColin Percival		done < $1-paths
2319db6b0a61SColin Percival
2320db6b0a61SColin Percival		# Copy in files
2321db6b0a61SColin Percival		while read F; do
2322db6b0a61SColin Percival			# Currently installed file
2323db6b0a61SColin Percival			V=`look "${F}|" $2 | cut -f 7 -d '|'`
2324db6b0a61SColin Percival			gunzip < files/${V}.gz > merge/old/${F}
2325db6b0a61SColin Percival
2326db6b0a61SColin Percival			# Old release
2327db6b0a61SColin Percival			if look "${F}|" $1 | fgrep -q "|f|"; then
2328db6b0a61SColin Percival				V=`look "${F}|" $1 | cut -f 3 -d '|'`
2329db6b0a61SColin Percival				gunzip < files/${V}.gz		\
2330db6b0a61SColin Percival				    > merge/${OLDRELNUM}/${F}
2331db6b0a61SColin Percival			fi
2332db6b0a61SColin Percival
2333db6b0a61SColin Percival			# New release
2334db6b0a61SColin Percival			if look "${F}|" $3 | cut -f 1,2,7 -d '|' |
2335db6b0a61SColin Percival			    fgrep -q "|f|"; then
2336db6b0a61SColin Percival				V=`look "${F}|" $3 | cut -f 7 -d '|'`
2337db6b0a61SColin Percival				gunzip < files/${V}.gz		\
2338db6b0a61SColin Percival				    > merge/${RELNUM}/${F}
2339db6b0a61SColin Percival			fi
2340db6b0a61SColin Percival		done < $1-paths
2341db6b0a61SColin Percival
2342db6b0a61SColin Percival		# Attempt to automatically merge changes
2343db6b0a61SColin Percival		echo -n "Attempting to automatically merge "
2344db6b0a61SColin Percival		echo -n "changes in files..."
2345db6b0a61SColin Percival		: > failed.merges
2346db6b0a61SColin Percival		while read F; do
2347db6b0a61SColin Percival			# If the file doesn't exist in the new release,
2348db6b0a61SColin Percival			# the result of "merging changes" is having the file
2349db6b0a61SColin Percival			# not exist.
2350db6b0a61SColin Percival			if ! [ -f merge/${RELNUM}/${F} ]; then
2351db6b0a61SColin Percival				continue
2352db6b0a61SColin Percival			fi
2353db6b0a61SColin Percival
2354db6b0a61SColin Percival			# If the file didn't exist in the old release, we're
2355db6b0a61SColin Percival			# going to throw away the existing file and hope that
2356db6b0a61SColin Percival			# the version from the new release is what we want.
2357db6b0a61SColin Percival			if ! [ -f merge/${OLDRELNUM}/${F} ]; then
2358db6b0a61SColin Percival				cp merge/${RELNUM}/${F} merge/new/${F}
2359db6b0a61SColin Percival				continue
2360db6b0a61SColin Percival			fi
2361db6b0a61SColin Percival
2362db6b0a61SColin Percival			# Some files need special treatment.
2363db6b0a61SColin Percival			case ${F} in
2364db6b0a61SColin Percival			/etc/spwd.db | /etc/pwd.db | /etc/login.conf.db)
2365db6b0a61SColin Percival				# Don't merge these -- we're rebuild them
2366db6b0a61SColin Percival				# after updates are installed.
2367db6b0a61SColin Percival				cp merge/old/${F} merge/new/${F}
2368db6b0a61SColin Percival				;;
2369db6b0a61SColin Percival			*)
2370db6b0a61SColin Percival				if ! merge -p -L "current version"	\
2371db6b0a61SColin Percival				    -L "${OLDRELNUM}" -L "${RELNUM}"	\
2372db6b0a61SColin Percival				    merge/old/${F}			\
2373db6b0a61SColin Percival				    merge/${OLDRELNUM}/${F}		\
2374db6b0a61SColin Percival				    merge/${RELNUM}/${F}		\
2375db6b0a61SColin Percival				    > merge/new/${F} 2>/dev/null; then
2376db6b0a61SColin Percival					echo ${F} >> failed.merges
2377db6b0a61SColin Percival				fi
2378db6b0a61SColin Percival				;;
2379db6b0a61SColin Percival			esac
2380db6b0a61SColin Percival		done < $1-paths
2381db6b0a61SColin Percival		echo " done."
2382db6b0a61SColin Percival
2383db6b0a61SColin Percival		# Ask the user to handle any files which didn't merge.
2384db6b0a61SColin Percival		while read F; do
23857449d2f5SColin Percival			# If the installed file differs from the version in
23866d514f10SDag-Erling Smørgrav			# the old release only due to RCS tag expansion
23877449d2f5SColin Percival			# then just use the version in the new release.
23887449d2f5SColin Percival			if samef merge/old/${F} merge/${OLDRELNUM}/${F}; then
23897449d2f5SColin Percival				cp merge/${RELNUM}/${F} merge/new/${F}
23907449d2f5SColin Percival				continue
23917449d2f5SColin Percival			fi
23927449d2f5SColin Percival
2393db6b0a61SColin Percival			cat <<-EOF
2394db6b0a61SColin Percival
2395db6b0a61SColin PercivalThe following file could not be merged automatically: ${F}
2396db6b0a61SColin PercivalPress Enter to edit this file in ${EDITOR} and resolve the conflicts
2397db6b0a61SColin Percivalmanually...
2398db6b0a61SColin Percival			EOF
2399db6b0a61SColin Percival			read dummy </dev/tty
2400db6b0a61SColin Percival			${EDITOR} `pwd`/merge/new/${F} < /dev/tty
2401db6b0a61SColin Percival		done < failed.merges
2402db6b0a61SColin Percival		rm failed.merges
2403db6b0a61SColin Percival
2404db6b0a61SColin Percival		# Ask the user to confirm that he likes how the result
2405db6b0a61SColin Percival		# of merging files.
2406db6b0a61SColin Percival		while read F; do
24077449d2f5SColin Percival			# Skip files which haven't changed except possibly
24086d514f10SDag-Erling Smørgrav			# in their RCS tags.
24097449d2f5SColin Percival			if [ -f merge/old/${F} ] && [ -f merge/new/${F} ] &&
24107449d2f5SColin Percival			    samef merge/old/${F} merge/new/${F}; then
24117449d2f5SColin Percival				continue
24127449d2f5SColin Percival			fi
24137449d2f5SColin Percival
24147449d2f5SColin Percival			# Skip files where the installed file differs from
24156d514f10SDag-Erling Smørgrav			# the old file only due to RCS tags.
24167449d2f5SColin Percival			if [ -f merge/old/${F} ] &&
24177449d2f5SColin Percival			    [ -f merge/${OLDRELNUM}/${F} ] &&
24187449d2f5SColin Percival			    samef merge/old/${F} merge/${OLDRELNUM}/${F}; then
2419db6b0a61SColin Percival				continue
2420db6b0a61SColin Percival			fi
2421db6b0a61SColin Percival
2422db6b0a61SColin Percival			# Warn about files which are ceasing to exist.
2423db6b0a61SColin Percival			if ! [ -f merge/new/${F} ]; then
2424db6b0a61SColin Percival				cat <<-EOF
2425db6b0a61SColin Percival
2426db6b0a61SColin PercivalThe following file will be removed, as it no longer exists in
2427db6b0a61SColin PercivalFreeBSD ${RELNUM}: ${F}
2428db6b0a61SColin Percival				EOF
2429db6b0a61SColin Percival				continuep < /dev/tty || return 1
2430db6b0a61SColin Percival				continue
2431db6b0a61SColin Percival			fi
2432db6b0a61SColin Percival
2433db6b0a61SColin Percival			# Print changes for the user's approval.
2434db6b0a61SColin Percival			cat <<-EOF
2435db6b0a61SColin Percival
2436db6b0a61SColin PercivalThe following changes, which occurred between FreeBSD ${OLDRELNUM} and
2437db6b0a61SColin PercivalFreeBSD ${RELNUM} have been merged into ${F}:
2438db6b0a61SColin PercivalEOF
2439db6b0a61SColin Percival			diff -U 5 -L "current version" -L "new version"	\
2440db6b0a61SColin Percival			    merge/old/${F} merge/new/${F} || true
2441db6b0a61SColin Percival			continuep < /dev/tty || return 1
2442db6b0a61SColin Percival		done < $1-paths
2443db6b0a61SColin Percival
2444db6b0a61SColin Percival		# Store merged files.
2445db6b0a61SColin Percival		while read F; do
2446c58b62efSColin Percival			if [ -f merge/new/${F} ]; then
2447db6b0a61SColin Percival				V=`${SHA256} -q merge/new/${F}`
2448db6b0a61SColin Percival
2449db6b0a61SColin Percival				gzip -c < merge/new/${F} > files/${V}.gz
2450db6b0a61SColin Percival				echo "${F}|${V}"
2451db6b0a61SColin Percival			fi
2452db6b0a61SColin Percival		done < $1-paths > newhashes
2453db6b0a61SColin Percival
2454db6b0a61SColin Percival		# Pull lines out from $3 which need to be updated to
2455db6b0a61SColin Percival		# reflect merged files.
2456db6b0a61SColin Percival		while read F; do
2457db6b0a61SColin Percival			look "${F}|" $3
2458db6b0a61SColin Percival		done < $1-paths > $3-oldlines
2459db6b0a61SColin Percival
2460db6b0a61SColin Percival		# Update lines to reflect merged files
2461db6b0a61SColin Percival		join -t '|' -o 1.1,1.2,1.3,1.4,1.5,1.6,2.2,1.8		\
2462db6b0a61SColin Percival		    $3-oldlines newhashes > $3-newlines
2463db6b0a61SColin Percival
2464db6b0a61SColin Percival		# Remove old lines from $3 and add new lines.
2465db6b0a61SColin Percival		sort $3-oldlines |
2466db6b0a61SColin Percival		    comm -13 - $3 |
2467db6b0a61SColin Percival		    sort - $3-newlines > $3.tmp
2468db6b0a61SColin Percival		mv $3.tmp $3
2469db6b0a61SColin Percival
2470db6b0a61SColin Percival		# Clean up
2471db6b0a61SColin Percival		rm $1-paths newhashes $3-oldlines $3-newlines
2472db6b0a61SColin Percival		rm -rf merge/
2473db6b0a61SColin Percival	fi
2474db6b0a61SColin Percival
2475db6b0a61SColin Percival	# We're done with merging files.
2476db6b0a61SColin Percival	rm $1
2477db6b0a61SColin Percival}
2478db6b0a61SColin Percival
2479db6b0a61SColin Percival# Do the work involved in fetching upgrades to a new release
2480db6b0a61SColin Percivalupgrade_run () {
2481db6b0a61SColin Percival	workdir_init || return 1
2482db6b0a61SColin Percival
2483db6b0a61SColin Percival	# Prepare the mirror list.
2484db6b0a61SColin Percival	fetch_pick_server_init && fetch_pick_server
2485db6b0a61SColin Percival
2486db6b0a61SColin Percival	# Try to fetch the public key until we run out of servers.
2487db6b0a61SColin Percival	while ! fetch_key; do
2488db6b0a61SColin Percival		fetch_pick_server || return 1
2489db6b0a61SColin Percival	done
2490db6b0a61SColin Percival
2491db6b0a61SColin Percival	# Try to fetch the metadata index signature ("tag") until we run
2492db6b0a61SColin Percival	# out of available servers; and sanity check the downloaded tag.
2493db6b0a61SColin Percival	while ! fetch_tag; do
2494db6b0a61SColin Percival		fetch_pick_server || return 1
2495db6b0a61SColin Percival	done
2496db6b0a61SColin Percival	fetch_tagsanity || return 1
2497db6b0a61SColin Percival
2498db6b0a61SColin Percival	# Fetch the INDEX-OLD and INDEX-ALL.
2499db6b0a61SColin Percival	fetch_metadata INDEX-OLD INDEX-ALL || return 1
2500db6b0a61SColin Percival
2501db6b0a61SColin Percival	# If StrictComponents is not "yes", generate a new components list
2502db6b0a61SColin Percival	# with only the components which appear to be installed.
2503db6b0a61SColin Percival	upgrade_guess_components INDEX-ALL || return 1
2504db6b0a61SColin Percival
2505db6b0a61SColin Percival	# Generate filtered INDEX-OLD and INDEX-ALL files containing only
2506db6b0a61SColin Percival	# the components we want and without anything marked as "Ignore".
2507db6b0a61SColin Percival	fetch_filter_metadata INDEX-OLD || return 1
2508db6b0a61SColin Percival	fetch_filter_metadata INDEX-ALL || return 1
2509db6b0a61SColin Percival
2510db6b0a61SColin Percival	# Merge the INDEX-OLD and INDEX-ALL files into INDEX-OLD.
2511db6b0a61SColin Percival	sort INDEX-OLD INDEX-ALL > INDEX-OLD.tmp
2512db6b0a61SColin Percival	mv INDEX-OLD.tmp INDEX-OLD
2513db6b0a61SColin Percival	rm INDEX-ALL
2514db6b0a61SColin Percival
2515db6b0a61SColin Percival	# Adjust variables for fetching files from the new release.
2516db6b0a61SColin Percival	OLDRELNUM=${RELNUM}
2517db6b0a61SColin Percival	RELNUM=${TARGETRELEASE}
2518db6b0a61SColin Percival	OLDFETCHDIR=${FETCHDIR}
2519db6b0a61SColin Percival	FETCHDIR=${RELNUM}/${ARCH}
2520db6b0a61SColin Percival
2521db6b0a61SColin Percival	# Try to fetch the NEW metadata index signature ("tag") until we run
2522db6b0a61SColin Percival	# out of available servers; and sanity check the downloaded tag.
2523db6b0a61SColin Percival	while ! fetch_tag; do
2524db6b0a61SColin Percival		fetch_pick_server || return 1
2525db6b0a61SColin Percival	done
2526db6b0a61SColin Percival
2527db6b0a61SColin Percival	# Fetch the new INDEX-ALL.
2528db6b0a61SColin Percival	fetch_metadata INDEX-ALL || return 1
2529db6b0a61SColin Percival
2530db6b0a61SColin Percival	# If StrictComponents is not "yes", COMPONENTS contains an entry
2531db6b0a61SColin Percival	# corresponding to the currently running kernel, and said kernel
2532db6b0a61SColin Percival	# does not exist in the new release, add "kernel/generic" to the
2533db6b0a61SColin Percival	# list of components.
2534db6b0a61SColin Percival	upgrade_guess_new_kernel INDEX-ALL || return 1
2535db6b0a61SColin Percival
2536db6b0a61SColin Percival	# Filter INDEX-ALL to contain only the components we want and without
2537db6b0a61SColin Percival	# anything marked as "Ignore".
2538db6b0a61SColin Percival	fetch_filter_metadata INDEX-ALL || return 1
2539db6b0a61SColin Percival
2540db6b0a61SColin Percival	# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2541db6b0a61SColin Percival	# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2542db6b0a61SColin Percival	upgrade_oldall_to_oldnew INDEX-OLD INDEX-ALL INDEX-NEW
2543db6b0a61SColin Percival
2544db6b0a61SColin Percival	# Translate /boot/${KERNCONF} or /boot/${NKERNCONF} into ${KERNELDIR}
2545db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-NEW ${NKERNCONF}
2546db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
2547db6b0a61SColin Percival
2548db6b0a61SColin Percival	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
2549db6b0a61SColin Percival	# system and generate an INDEX-PRESENT file.
2550db6b0a61SColin Percival	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2551db6b0a61SColin Percival
2552db6b0a61SColin Percival	# Based on ${MERGECHANGES}, generate a file tomerge-old with the
2553db6b0a61SColin Percival	# paths and hashes of old versions of files to merge.
2554db6b0a61SColin Percival	fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT tomerge-old
2555db6b0a61SColin Percival
2556db6b0a61SColin Percival	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
2557db6b0a61SColin Percival	# correspond to lines in INDEX-PRESENT with hashes not appearing
2558db6b0a61SColin Percival	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
2559db6b0a61SColin Percival	# INDEX-PRESENT has type - and there isn't a corresponding entry in
2560db6b0a61SColin Percival	# INDEX-OLD with type -.
2561db6b0a61SColin Percival	fetch_filter_unmodified_notpresent	\
2562db6b0a61SColin Percival	    INDEX-OLD INDEX-PRESENT INDEX-NEW tomerge-old
2563db6b0a61SColin Percival
2564db6b0a61SColin Percival	# For each entry in INDEX-PRESENT of type -, remove any corresponding
2565db6b0a61SColin Percival	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
2566db6b0a61SColin Percival	# of type - from INDEX-PRESENT.
2567db6b0a61SColin Percival	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
2568db6b0a61SColin Percival
2569db6b0a61SColin Percival	# If ${ALLOWDELETE} != "yes", then remove any entries from
2570db6b0a61SColin Percival	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
2571db6b0a61SColin Percival	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
2572db6b0a61SColin Percival
2573db6b0a61SColin Percival	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
2574db6b0a61SColin Percival	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
2575db6b0a61SColin Percival	# replace the corresponding line of INDEX-NEW with one having the
2576db6b0a61SColin Percival	# same metadata as the entry in INDEX-PRESENT.
2577db6b0a61SColin Percival	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
2578db6b0a61SColin Percival
2579db6b0a61SColin Percival	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
2580db6b0a61SColin Percival	# no need to update a file if it isn't changing.
2581db6b0a61SColin Percival	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
2582db6b0a61SColin Percival
2583db6b0a61SColin Percival	# Fetch "clean" files from the old release for merging changes.
2584db6b0a61SColin Percival	fetch_files_premerge tomerge-old
2585db6b0a61SColin Percival
2586db6b0a61SColin Percival	# Prepare to fetch files: Generate a list of the files we need,
2587db6b0a61SColin Percival	# copy the unmodified files we have into /files/, and generate
2588db6b0a61SColin Percival	# a list of patches to download.
2589db6b0a61SColin Percival	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2590db6b0a61SColin Percival
2591db6b0a61SColin Percival	# Fetch patches from to-${RELNUM}/${ARCH}/bp/
2592db6b0a61SColin Percival	PATCHDIR=to-${RELNUM}/${ARCH}/bp
2593db6b0a61SColin Percival	fetch_files || return 1
2594db6b0a61SColin Percival
2595db6b0a61SColin Percival	# Merge configuration file changes.
2596db6b0a61SColin Percival	upgrade_merge tomerge-old INDEX-PRESENT INDEX-NEW || return 1
2597db6b0a61SColin Percival
2598db6b0a61SColin Percival	# Create and populate install manifest directory; and report what
2599db6b0a61SColin Percival	# updates are available.
2600db6b0a61SColin Percival	fetch_create_manifest || return 1
2601db6b0a61SColin Percival
2602db6b0a61SColin Percival	# Leave a note behind to tell the "install" command that the kernel
2603db6b0a61SColin Percival	# needs to be installed before the world.
2604db6b0a61SColin Percival	touch ${BDHASH}-install/kernelfirst
260585451f90SColin Percival
260685451f90SColin Percival	# Remind the user that they need to run "freebsd-update install"
260785451f90SColin Percival	# to install the downloaded bits, in case they didn't RTFM.
260885451f90SColin Percival	echo "To install the downloaded upgrades, run \"$0 install\"."
2609db6b0a61SColin Percival}
2610db6b0a61SColin Percival
261148ffe56aSColin Percival# Make sure that all the file hashes mentioned in $@ have corresponding
261248ffe56aSColin Percival# gzipped files stored in /files/.
261348ffe56aSColin Percivalinstall_verify () {
261448ffe56aSColin Percival	# Generate a list of hashes
261548ffe56aSColin Percival	cat $@ |
261648ffe56aSColin Percival	    cut -f 2,7 -d '|' |
261748ffe56aSColin Percival	    grep -E '^f' |
261848ffe56aSColin Percival	    cut -f 2 -d '|' |
261948ffe56aSColin Percival	    sort -u > filelist
262048ffe56aSColin Percival
262148ffe56aSColin Percival	# Make sure all the hashes exist
262248ffe56aSColin Percival	while read HASH; do
262348ffe56aSColin Percival		if ! [ -f files/${HASH}.gz ]; then
262448ffe56aSColin Percival			echo -n "Update files missing -- "
262548ffe56aSColin Percival			echo "this should never happen."
262648ffe56aSColin Percival			echo "Re-run '$0 fetch'."
262748ffe56aSColin Percival			return 1
262848ffe56aSColin Percival		fi
262948ffe56aSColin Percival	done < filelist
263048ffe56aSColin Percival
263148ffe56aSColin Percival	# Clean up
263248ffe56aSColin Percival	rm filelist
263348ffe56aSColin Percival}
263448ffe56aSColin Percival
263548ffe56aSColin Percival# Remove the system immutable flag from files
263648ffe56aSColin Percivalinstall_unschg () {
263748ffe56aSColin Percival	# Generate file list
263848ffe56aSColin Percival	cat $@ |
263948ffe56aSColin Percival	    cut -f 1 -d '|' > filelist
264048ffe56aSColin Percival
264148ffe56aSColin Percival	# Remove flags
264248ffe56aSColin Percival	while read F; do
2643e829ed67SColin Percival		if ! [ -e ${BASEDIR}/${F} ]; then
264448ffe56aSColin Percival			continue
264548ffe56aSColin Percival		fi
264648ffe56aSColin Percival
2647e829ed67SColin Percival		chflags noschg ${BASEDIR}/${F} || return 1
264848ffe56aSColin Percival	done < filelist
264948ffe56aSColin Percival
265048ffe56aSColin Percival	# Clean up
265148ffe56aSColin Percival	rm filelist
265248ffe56aSColin Percival}
265348ffe56aSColin Percival
265423d827efSSimon L. B. Nielsen# Decide which directory name to use for kernel backups.
265523d827efSSimon L. B. Nielsenbackup_kernel_finddir () {
265623d827efSSimon L. B. Nielsen	CNT=0
265723d827efSSimon L. B. Nielsen	while true ; do
265823d827efSSimon L. B. Nielsen		# Pathname does not exist, so it is OK use that name
265923d827efSSimon L. B. Nielsen		# for backup directory.
2660c4a0c62cSThomas Quinot		if [ ! -e $BASEDIR/$BACKUPKERNELDIR ]; then
266123d827efSSimon L. B. Nielsen			return 0
266223d827efSSimon L. B. Nielsen		fi
266323d827efSSimon L. B. Nielsen
266423d827efSSimon L. B. Nielsen		# If directory do exist, we only use if it has our
266523d827efSSimon L. B. Nielsen		# marker file.
2666c4a0c62cSThomas Quinot		if [ -d $BASEDIR/$BACKUPKERNELDIR -a \
2667c4a0c62cSThomas Quinot			-e $BASEDIR/$BACKUPKERNELDIR/.freebsd-update ]; then
266823d827efSSimon L. B. Nielsen			return 0
266923d827efSSimon L. B. Nielsen		fi
267023d827efSSimon L. B. Nielsen
267123d827efSSimon L. B. Nielsen		# We could not use current directory name, so add counter to
267223d827efSSimon L. B. Nielsen		# the end and try again.
267323d827efSSimon L. B. Nielsen		CNT=$((CNT + 1))
267423d827efSSimon L. B. Nielsen		if [ $CNT -gt 9 ]; then
2675c4a0c62cSThomas Quinot			echo "Could not find valid backup dir ($BASEDIR/$BACKUPKERNELDIR)"
267623d827efSSimon L. B. Nielsen			exit 1
267723d827efSSimon L. B. Nielsen		fi
267823d827efSSimon L. B. Nielsen		BACKUPKERNELDIR="`echo $BACKUPKERNELDIR | sed -Ee 's/[0-9]\$//'`"
267923d827efSSimon L. B. Nielsen		BACKUPKERNELDIR="${BACKUPKERNELDIR}${CNT}"
268023d827efSSimon L. B. Nielsen	done
268123d827efSSimon L. B. Nielsen}
268223d827efSSimon L. B. Nielsen
268323d827efSSimon L. B. Nielsen# Backup the current kernel using hardlinks, if not disabled by user.
268423d827efSSimon L. B. Nielsen# Since we delete all files in the directory used for previous backups
268523d827efSSimon L. B. Nielsen# we create a marker file called ".freebsd-update" in the directory so
268623d827efSSimon L. B. Nielsen# we can determine on the next run that the directory was created by
268723d827efSSimon L. B. Nielsen# freebsd-update and we then do not accidentally remove user files in
268823d827efSSimon L. B. Nielsen# the unlikely case that the user has created a directory with a
268923d827efSSimon L. B. Nielsen# conflicting name.
269023d827efSSimon L. B. Nielsenbackup_kernel () {
269123d827efSSimon L. B. Nielsen	# Only make kernel backup is so configured.
269223d827efSSimon L. B. Nielsen	if [ $BACKUPKERNEL != yes ]; then
269323d827efSSimon L. B. Nielsen		return 0
269423d827efSSimon L. B. Nielsen	fi
269523d827efSSimon L. B. Nielsen
269623d827efSSimon L. B. Nielsen	# Decide which directory name to use for kernel backups.
269723d827efSSimon L. B. Nielsen	backup_kernel_finddir
269823d827efSSimon L. B. Nielsen
269923d827efSSimon L. B. Nielsen	# Remove old kernel backup files.  If $BACKUPKERNELDIR was
270023d827efSSimon L. B. Nielsen	# "not ours", backup_kernel_finddir would have exited, so
270123d827efSSimon L. B. Nielsen	# deleting the directory content is as safe as we can make it.
2702c4a0c62cSThomas Quinot	if [ -d $BASEDIR/$BACKUPKERNELDIR ]; then
2703c4a0c62cSThomas Quinot		rm -fr $BASEDIR/$BACKUPKERNELDIR
270423d827efSSimon L. B. Nielsen	fi
270523d827efSSimon L. B. Nielsen
2706ab7d0151SJaakko Heinonen	# Create directories for backup.
2707c4a0c62cSThomas Quinot	mkdir -p $BASEDIR/$BACKUPKERNELDIR
2708c4a0c62cSThomas Quinot	mtree -cdn -p "${BASEDIR}/${KERNELDIR}" | \
2709c4a0c62cSThomas Quinot	    mtree -Ue -p "${BASEDIR}/${BACKUPKERNELDIR}" > /dev/null
271023d827efSSimon L. B. Nielsen
271123d827efSSimon L. B. Nielsen	# Mark the directory as having been created by freebsd-update.
2712c4a0c62cSThomas Quinot	touch $BASEDIR/$BACKUPKERNELDIR/.freebsd-update
271323d827efSSimon L. B. Nielsen	if [ $? -ne 0 ]; then
271423d827efSSimon L. B. Nielsen		echo "Could not create kernel backup directory"
271523d827efSSimon L. B. Nielsen		exit 1
271623d827efSSimon L. B. Nielsen	fi
271723d827efSSimon L. B. Nielsen
271823d827efSSimon L. B. Nielsen	# Disable pathname expansion to be sure *.symbols is not
271923d827efSSimon L. B. Nielsen	# expanded.
272023d827efSSimon L. B. Nielsen	set -f
272123d827efSSimon L. B. Nielsen
272223d827efSSimon L. B. Nielsen	# Use find to ignore symbol files, unless disabled by user.
272323d827efSSimon L. B. Nielsen	if [ $BACKUPKERNELSYMBOLFILES = yes ]; then
272423d827efSSimon L. B. Nielsen		FINDFILTER=""
272523d827efSSimon L. B. Nielsen	else
272623d827efSSimon L. B. Nielsen		FINDFILTER=-"a ! -name *.symbols"
272723d827efSSimon L. B. Nielsen	fi
272823d827efSSimon L. B. Nielsen
272923d827efSSimon L. B. Nielsen	# Backup all the kernel files using hardlinks.
2730c4a0c62cSThomas Quinot	(cd ${BASEDIR}/${KERNELDIR} && find . -type f $FINDFILTER -exec \
2731c4a0c62cSThomas Quinot	    cp -pl '{}' ${BASEDIR}/${BACKUPKERNELDIR}/'{}' \;)
273223d827efSSimon L. B. Nielsen
273323d827efSSimon L. B. Nielsen	# Re-enable patchname expansion.
273423d827efSSimon L. B. Nielsen	set +f
273523d827efSSimon L. B. Nielsen}
273623d827efSSimon L. B. Nielsen
273748ffe56aSColin Percival# Install new files
273848ffe56aSColin Percivalinstall_from_index () {
273948ffe56aSColin Percival	# First pass: Do everything apart from setting file flags.  We
274048ffe56aSColin Percival	# can't set flags yet, because schg inhibits hard linking.
274148ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
274248ffe56aSColin Percival	    tr '|' ' ' |
274348ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
274448ffe56aSColin Percival		case ${TYPE} in
274548ffe56aSColin Percival		d)
274648ffe56aSColin Percival			# Create a directory
274748ffe56aSColin Percival			install -d -o ${OWNER} -g ${GROUP}		\
274848ffe56aSColin Percival			    -m ${PERM} ${BASEDIR}/${FPATH}
274948ffe56aSColin Percival			;;
275048ffe56aSColin Percival		f)
275148ffe56aSColin Percival			if [ -z "${LINK}" ]; then
275248ffe56aSColin Percival				# Create a file, without setting flags.
275348ffe56aSColin Percival				gunzip < files/${HASH}.gz > ${HASH}
275448ffe56aSColin Percival				install -S -o ${OWNER} -g ${GROUP}	\
275548ffe56aSColin Percival				    -m ${PERM} ${HASH} ${BASEDIR}/${FPATH}
275648ffe56aSColin Percival				rm ${HASH}
275748ffe56aSColin Percival			else
275848ffe56aSColin Percival				# Create a hard link.
2759e829ed67SColin Percival				ln -f ${BASEDIR}/${LINK} ${BASEDIR}/${FPATH}
276048ffe56aSColin Percival			fi
276148ffe56aSColin Percival			;;
276248ffe56aSColin Percival		L)
276348ffe56aSColin Percival			# Create a symlink
276448ffe56aSColin Percival			ln -sfh ${HASH} ${BASEDIR}/${FPATH}
276548ffe56aSColin Percival			;;
276648ffe56aSColin Percival		esac
276748ffe56aSColin Percival	    done
276848ffe56aSColin Percival
276948ffe56aSColin Percival	# Perform a second pass, adding file flags.
277048ffe56aSColin Percival	tr '|' ' ' < $1 |
277148ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
277248ffe56aSColin Percival		if [ ${TYPE} = "f" ] &&
277348ffe56aSColin Percival		    ! [ ${FLAGS} = "0" ]; then
277448ffe56aSColin Percival			chflags ${FLAGS} ${BASEDIR}/${FPATH}
277548ffe56aSColin Percival		fi
277648ffe56aSColin Percival	    done
277748ffe56aSColin Percival}
277848ffe56aSColin Percival
277948ffe56aSColin Percival# Remove files which we want to delete
278048ffe56aSColin Percivalinstall_delete () {
278148ffe56aSColin Percival	# Generate list of new files
278248ffe56aSColin Percival	cut -f 1 -d '|' < $2 |
278348ffe56aSColin Percival	    sort > newfiles
278448ffe56aSColin Percival
278548ffe56aSColin Percival	# Generate subindex of old files we want to nuke
278648ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
278748ffe56aSColin Percival	    join -t '|' -v 1 - newfiles |
2788bce02f98SColin Percival	    sort -r -k 1,1 -t '|' |
278948ffe56aSColin Percival	    cut -f 1,2 -d '|' |
279048ffe56aSColin Percival	    tr '|' ' ' > killfiles
279148ffe56aSColin Percival
279248ffe56aSColin Percival	# Remove the offending bits
279348ffe56aSColin Percival	while read FPATH TYPE; do
279448ffe56aSColin Percival		case ${TYPE} in
279548ffe56aSColin Percival		d)
279648ffe56aSColin Percival			rmdir ${BASEDIR}/${FPATH}
279748ffe56aSColin Percival			;;
279848ffe56aSColin Percival		f)
279948ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
280048ffe56aSColin Percival			;;
280148ffe56aSColin Percival		L)
280248ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
280348ffe56aSColin Percival			;;
280448ffe56aSColin Percival		esac
280548ffe56aSColin Percival	done < killfiles
280648ffe56aSColin Percival
280748ffe56aSColin Percival	# Clean up
280848ffe56aSColin Percival	rm newfiles killfiles
280948ffe56aSColin Percival}
281048ffe56aSColin Percival
2811db6b0a61SColin Percival# Install new files, delete old files, and update linker.hints
2812db6b0a61SColin Percivalinstall_files () {
2813db6b0a61SColin Percival	# If we haven't already dealt with the kernel, deal with it.
2814db6b0a61SColin Percival	if ! [ -f $1/kerneldone ]; then
2815db6b0a61SColin Percival		grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
2816db6b0a61SColin Percival		grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
2817db6b0a61SColin Percival
281823d827efSSimon L. B. Nielsen		# Backup current kernel before installing a new one
281923d827efSSimon L. B. Nielsen		backup_kernel || return 1
282023d827efSSimon L. B. Nielsen
2821db6b0a61SColin Percival		# Install new files
2822db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2823db6b0a61SColin Percival
2824db6b0a61SColin Percival		# Remove files which need to be deleted
2825db6b0a61SColin Percival		install_delete INDEX-OLD INDEX-NEW || return 1
2826db6b0a61SColin Percival
2827db6b0a61SColin Percival		# Update linker.hints if necessary
2828db6b0a61SColin Percival		if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2829c4a0c62cSThomas Quinot			kldxref -R ${BASEDIR}/boot/ 2>/dev/null
283048ffe56aSColin Percival		fi
2831db6b0a61SColin Percival
2832db6b0a61SColin Percival		# We've finished updating the kernel.
2833db6b0a61SColin Percival		touch $1/kerneldone
2834db6b0a61SColin Percival
2835db6b0a61SColin Percival		# Do we need to ask for a reboot now?
2836db6b0a61SColin Percival		if [ -f $1/kernelfirst ] &&
2837db6b0a61SColin Percival		    [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2838db6b0a61SColin Percival			cat <<-EOF
2839db6b0a61SColin Percival
2840db6b0a61SColin PercivalKernel updates have been installed.  Please reboot and run
2841db6b0a61SColin Percival"$0 install" again to finish installing updates.
2842db6b0a61SColin Percival			EOF
2843db6b0a61SColin Percival			exit 0
2844db6b0a61SColin Percival		fi
2845db6b0a61SColin Percival	fi
2846db6b0a61SColin Percival
2847db6b0a61SColin Percival	# If we haven't already dealt with the world, deal with it.
2848db6b0a61SColin Percival	if ! [ -f $1/worlddone ]; then
2849cd1ab228SColin Percival		# Create any necessary directories first
2850cd1ab228SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2851cd1ab228SColin Percival		    grep -E '^[^|]+\|d\|' > INDEX-NEW
2852cd1ab228SColin Percival		install_from_index INDEX-NEW || return 1
2853cd1ab228SColin Percival
2854722d81b5SBrooks Davis		# Install new runtime linker
2855722d81b5SBrooks Davis		grep -vE '^/boot/' $1/INDEX-NEW |
2856722d81b5SBrooks Davis		    grep -vE '^[^|]+\|d\|' |
2857722d81b5SBrooks Davis		    grep -E '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2858722d81b5SBrooks Davis		install_from_index INDEX-NEW || return 1
2859722d81b5SBrooks Davis
2860db6b0a61SColin Percival		# Install new shared libraries next
2861db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2862cd1ab228SColin Percival		    grep -vE '^[^|]+\|d\|' |
2863722d81b5SBrooks Davis		    grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' |
28649546dbd1SColin Percival		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2865db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2866db6b0a61SColin Percival
2867db6b0a61SColin Percival		# Deal with everything else
2868db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-OLD |
2869cd1ab228SColin Percival		    grep -vE '^[^|]+\|d\|' |
2870722d81b5SBrooks Davis		    grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' |
28719546dbd1SColin Percival		    grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD
2872db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2873cd1ab228SColin Percival		    grep -vE '^[^|]+\|d\|' |
2874722d81b5SBrooks Davis		    grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' |
28759546dbd1SColin Percival		    grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2876db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2877db6b0a61SColin Percival		install_delete INDEX-OLD INDEX-NEW || return 1
2878db6b0a61SColin Percival
2879db6b0a61SColin Percival		# Rebuild /etc/spwd.db and /etc/pwd.db if necessary.
2880c4a0c62cSThomas Quinot		if [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/spwd.db ] ||
2881c4a0c62cSThomas Quinot		    [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/pwd.db ]; then
2882c4a0c62cSThomas Quinot			pwd_mkdb -d ${BASEDIR}/etc ${BASEDIR}/etc/master.passwd
2883db6b0a61SColin Percival		fi
2884db6b0a61SColin Percival
2885db6b0a61SColin Percival		# Rebuild /etc/login.conf.db if necessary.
2886c4a0c62cSThomas Quinot		if [ ${BASEDIR}/etc/login.conf -nt ${BASEDIR}/etc/login.conf.db ]; then
2887c4a0c62cSThomas Quinot			cap_mkdb ${BASEDIR}/etc/login.conf
2888db6b0a61SColin Percival		fi
2889db6b0a61SColin Percival
2890db6b0a61SColin Percival		# We've finished installing the world and deleting old files
2891db6b0a61SColin Percival		# which are not shared libraries.
2892db6b0a61SColin Percival		touch $1/worlddone
2893db6b0a61SColin Percival
2894db6b0a61SColin Percival		# Do we need to ask the user to portupgrade now?
2895db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
28969546dbd1SColin Percival		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' |
2897db6b0a61SColin Percival		    cut -f 1 -d '|' |
2898db6b0a61SColin Percival		    sort > newfiles
2899db6b0a61SColin Percival		if grep -vE '^/boot/' $1/INDEX-OLD |
29009546dbd1SColin Percival		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' |
2901db6b0a61SColin Percival		    cut -f 1 -d '|' |
2902db6b0a61SColin Percival		    sort |
2903db6b0a61SColin Percival		    join -v 1 - newfiles |
2904db6b0a61SColin Percival		    grep -q .; then
2905db6b0a61SColin Percival			cat <<-EOF
2906db6b0a61SColin Percival
2907db6b0a61SColin PercivalCompleting this upgrade requires removing old shared object files.
2908db6b0a61SColin PercivalPlease rebuild all installed 3rd party software (e.g., programs
2909db6b0a61SColin Percivalinstalled from the ports tree) and then run "$0 install"
2910db6b0a61SColin Percivalagain to finish installing updates.
2911db6b0a61SColin Percival			EOF
2912db6b0a61SColin Percival			rm newfiles
2913db6b0a61SColin Percival			exit 0
2914db6b0a61SColin Percival		fi
2915db6b0a61SColin Percival		rm newfiles
2916db6b0a61SColin Percival	fi
2917db6b0a61SColin Percival
2918db6b0a61SColin Percival	# Remove old shared libraries
2919db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2920cd1ab228SColin Percival	    grep -vE '^[^|]+\|d\|' |
29219546dbd1SColin Percival	    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2922db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2923cd1ab228SColin Percival	    grep -vE '^[^|]+\|d\|' |
29249546dbd1SColin Percival	    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD
2925db6b0a61SColin Percival	install_delete INDEX-OLD INDEX-NEW || return 1
2926db6b0a61SColin Percival
2927cd1ab228SColin Percival	# Remove old directories
2928ebc1d19cSColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2929ebc1d19cSColin Percival	    grep -E '^[^|]+\|d\|' > INDEX-NEW
2930cd1ab228SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2931cd1ab228SColin Percival	    grep -E '^[^|]+\|d\|' > INDEX-OLD
2932cd1ab228SColin Percival	install_delete INDEX-OLD INDEX-NEW || return 1
2933cd1ab228SColin Percival
2934db6b0a61SColin Percival	# Remove temporary files
2935db6b0a61SColin Percival	rm INDEX-OLD INDEX-NEW
293648ffe56aSColin Percival}
293748ffe56aSColin Percival
293848ffe56aSColin Percival# Rearrange bits to allow the installed updates to be rolled back
293948ffe56aSColin Percivalinstall_setup_rollback () {
2940db6b0a61SColin Percival	# Remove the "reboot after installing kernel", "kernel updated", and
2941db6b0a61SColin Percival	# "finished installing the world" flags if present -- they are
2942db6b0a61SColin Percival	# irrelevant when rolling back updates.
2943db6b0a61SColin Percival	if [ -f ${BDHASH}-install/kernelfirst ]; then
2944db6b0a61SColin Percival		rm ${BDHASH}-install/kernelfirst
2945db6b0a61SColin Percival		rm ${BDHASH}-install/kerneldone
2946db6b0a61SColin Percival	fi
2947db6b0a61SColin Percival	if [ -f ${BDHASH}-install/worlddone ]; then
2948db6b0a61SColin Percival		rm ${BDHASH}-install/worlddone
2949db6b0a61SColin Percival	fi
2950db6b0a61SColin Percival
295148ffe56aSColin Percival	if [ -L ${BDHASH}-rollback ]; then
295248ffe56aSColin Percival		mv ${BDHASH}-rollback ${BDHASH}-install/rollback
295348ffe56aSColin Percival	fi
295448ffe56aSColin Percival
295548ffe56aSColin Percival	mv ${BDHASH}-install ${BDHASH}-rollback
295648ffe56aSColin Percival}
295748ffe56aSColin Percival
295848ffe56aSColin Percival# Actually install updates
295948ffe56aSColin Percivalinstall_run () {
296048ffe56aSColin Percival	echo -n "Installing updates..."
296148ffe56aSColin Percival
296248ffe56aSColin Percival	# Make sure we have all the files we should have
296348ffe56aSColin Percival	install_verify ${BDHASH}-install/INDEX-OLD	\
296448ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
296548ffe56aSColin Percival
296648ffe56aSColin Percival	# Remove system immutable flag from files
296748ffe56aSColin Percival	install_unschg ${BDHASH}-install/INDEX-OLD	\
296848ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
296948ffe56aSColin Percival
2970db6b0a61SColin Percival	# Install new files, delete old files, and update linker.hints
2971db6b0a61SColin Percival	install_files ${BDHASH}-install || return 1
297248ffe56aSColin Percival
297348ffe56aSColin Percival	# Rearrange bits to allow the installed updates to be rolled back
297448ffe56aSColin Percival	install_setup_rollback
297548ffe56aSColin Percival
297648ffe56aSColin Percival	echo " done."
297748ffe56aSColin Percival}
297848ffe56aSColin Percival
297948ffe56aSColin Percival# Rearrange bits to allow the previous set of updates to be rolled back next.
298048ffe56aSColin Percivalrollback_setup_rollback () {
298148ffe56aSColin Percival	if [ -L ${BDHASH}-rollback/rollback ]; then
298248ffe56aSColin Percival		mv ${BDHASH}-rollback/rollback rollback-tmp
298348ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
298448ffe56aSColin Percival		rm ${BDHASH}-rollback
298548ffe56aSColin Percival		mv rollback-tmp ${BDHASH}-rollback
298648ffe56aSColin Percival	else
298748ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
298848ffe56aSColin Percival		rm ${BDHASH}-rollback
298948ffe56aSColin Percival	fi
299048ffe56aSColin Percival}
299148ffe56aSColin Percival
2992db6b0a61SColin Percival# Install old files, delete new files, and update linker.hints
2993db6b0a61SColin Percivalrollback_files () {
29941ec4fb3aSColin Percival	# Install old shared library files which don't have the same path as
29951ec4fb3aSColin Percival	# a new shared library file.
29961ec4fb3aSColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2997fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
29981ec4fb3aSColin Percival	    cut -f 1 -d '|' |
29991ec4fb3aSColin Percival	    sort > INDEX-NEW.libs.flist
3000db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
3001fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
30021ec4fb3aSColin Percival	    sort -k 1,1 -t '|' - |
30031ec4fb3aSColin Percival	    join -t '|' -v 1 - INDEX-NEW.libs.flist > INDEX-OLD
3004db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
3005db6b0a61SColin Percival
3006db6b0a61SColin Percival	# Deal with files which are neither kernel nor shared library
3007db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
3008fd0963d1SColin Percival	    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
3009db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
3010fd0963d1SColin Percival	    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
3011db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
3012db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
3013db6b0a61SColin Percival
30141ec4fb3aSColin Percival	# Install any old shared library files which we didn't install above.
30151ec4fb3aSColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
3016fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
30171ec4fb3aSColin Percival	    sort -k 1,1 -t '|' - |
30181ec4fb3aSColin Percival	    join -t '|' - INDEX-NEW.libs.flist > INDEX-OLD
30191ec4fb3aSColin Percival	install_from_index INDEX-OLD || return 1
30201ec4fb3aSColin Percival
3021db6b0a61SColin Percival	# Delete unneeded shared library files
3022db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
3023fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
3024db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
3025fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
3026db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
3027db6b0a61SColin Percival
3028db6b0a61SColin Percival	# Deal with kernel files
3029db6b0a61SColin Percival	grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
3030db6b0a61SColin Percival	grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
3031db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
3032db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
3033db6b0a61SColin Percival	if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
3034db6b0a61SColin Percival		kldxref -R /boot/ 2>/dev/null
3035db6b0a61SColin Percival	fi
3036db6b0a61SColin Percival
3037db6b0a61SColin Percival	# Remove temporary files
30380e0d8d5aSColin Percival	rm INDEX-OLD INDEX-NEW INDEX-NEW.libs.flist
3039db6b0a61SColin Percival}
3040db6b0a61SColin Percival
304148ffe56aSColin Percival# Actually rollback updates
304248ffe56aSColin Percivalrollback_run () {
304348ffe56aSColin Percival	echo -n "Uninstalling updates..."
304448ffe56aSColin Percival
304548ffe56aSColin Percival	# If there are updates waiting to be installed, remove them; we
304648ffe56aSColin Percival	# want the user to re-run 'fetch' after rolling back updates.
304748ffe56aSColin Percival	if [ -L ${BDHASH}-install ]; then
304848ffe56aSColin Percival		rm -r ${BDHASH}-install/
304948ffe56aSColin Percival		rm ${BDHASH}-install
305048ffe56aSColin Percival	fi
305148ffe56aSColin Percival
305248ffe56aSColin Percival	# Make sure we have all the files we should have
305348ffe56aSColin Percival	install_verify ${BDHASH}-rollback/INDEX-NEW	\
305448ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
305548ffe56aSColin Percival
305648ffe56aSColin Percival	# Remove system immutable flag from files
305748ffe56aSColin Percival	install_unschg ${BDHASH}-rollback/INDEX-NEW	\
305848ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
305948ffe56aSColin Percival
3060db6b0a61SColin Percival	# Install old files, delete new files, and update linker.hints
3061db6b0a61SColin Percival	rollback_files ${BDHASH}-rollback || return 1
306248ffe56aSColin Percival
306348ffe56aSColin Percival	# Remove the rollback directory and the symlink pointing to it; and
306448ffe56aSColin Percival	# rearrange bits to allow the previous set of updates to be rolled
306548ffe56aSColin Percival	# back next.
306648ffe56aSColin Percival	rollback_setup_rollback
306748ffe56aSColin Percival
306848ffe56aSColin Percival	echo " done."
306948ffe56aSColin Percival}
307048ffe56aSColin Percival
307108e23beeSColin Percival# Compare INDEX-ALL and INDEX-PRESENT and print warnings about differences.
307208e23beeSColin PercivalIDS_compare () {
3073bb10a826SColin Percival	# Get all the lines which mismatch in something other than file
3074bb10a826SColin Percival	# flags.  We ignore file flags because sysinstall doesn't seem to
3075bb10a826SColin Percival	# set them when it installs FreeBSD; warning about these adds a
3076bb10a826SColin Percival	# very large amount of noise.
3077bb10a826SColin Percival	cut -f 1-5,7-8 -d '|' $1 > $1.noflags
3078bb10a826SColin Percival	sort -k 1,1 -t '|' $1.noflags > $1.sorted
3079bb10a826SColin Percival	cut -f 1-5,7-8 -d '|' $2 |
3080bb10a826SColin Percival	    comm -13 $1.noflags - |
3081bb10a826SColin Percival	    fgrep -v '|-|||||' |
308208e23beeSColin Percival	    sort -k 1,1 -t '|' |
308308e23beeSColin Percival	    join -t '|' $1.sorted - > INDEX-NOTMATCHING
308408e23beeSColin Percival
308508e23beeSColin Percival	# Ignore files which match IDSIGNOREPATHS.
308608e23beeSColin Percival	for X in ${IDSIGNOREPATHS}; do
308708e23beeSColin Percival		grep -E "^${X}" INDEX-NOTMATCHING
308808e23beeSColin Percival	done |
308908e23beeSColin Percival	    sort -u |
309008e23beeSColin Percival	    comm -13 - INDEX-NOTMATCHING > INDEX-NOTMATCHING.tmp
309108e23beeSColin Percival	mv INDEX-NOTMATCHING.tmp INDEX-NOTMATCHING
309208e23beeSColin Percival
309308e23beeSColin Percival	# Go through the lines and print warnings.
3094aa60062eSColin Percival	local IFS='|'
3095aa60062eSColin Percival	while read FPATH TYPE OWNER GROUP PERM HASH LINK P_TYPE P_OWNER P_GROUP P_PERM P_HASH P_LINK; do
309608e23beeSColin Percival		# Warn about different object types.
309708e23beeSColin Percival		if ! [ "${TYPE}" = "${P_TYPE}" ]; then
309808e23beeSColin Percival			echo -n "${FPATH} is a "
309908e23beeSColin Percival			case "${P_TYPE}" in
310008e23beeSColin Percival			f)	echo -n "regular file, "
310108e23beeSColin Percival				;;
310208e23beeSColin Percival			d)	echo -n "directory, "
310308e23beeSColin Percival				;;
310408e23beeSColin Percival			L)	echo -n "symlink, "
310508e23beeSColin Percival				;;
310608e23beeSColin Percival			esac
310708e23beeSColin Percival			echo -n "but should be a "
310808e23beeSColin Percival			case "${TYPE}" in
310908e23beeSColin Percival			f)	echo -n "regular file."
311008e23beeSColin Percival				;;
311108e23beeSColin Percival			d)	echo -n "directory."
311208e23beeSColin Percival				;;
311308e23beeSColin Percival			L)	echo -n "symlink."
311408e23beeSColin Percival				;;
311508e23beeSColin Percival			esac
311608e23beeSColin Percival			echo
311708e23beeSColin Percival
311808e23beeSColin Percival			# Skip other tests, since they don't make sense if
311908e23beeSColin Percival			# we're comparing different object types.
312008e23beeSColin Percival			continue
312108e23beeSColin Percival		fi
312208e23beeSColin Percival
312308e23beeSColin Percival		# Warn about different owners.
312408e23beeSColin Percival		if ! [ "${OWNER}" = "${P_OWNER}" ]; then
312508e23beeSColin Percival			echo -n "${FPATH} is owned by user id ${P_OWNER}, "
312608e23beeSColin Percival			echo "but should be owned by user id ${OWNER}."
312708e23beeSColin Percival		fi
312808e23beeSColin Percival
312908e23beeSColin Percival		# Warn about different groups.
313008e23beeSColin Percival		if ! [ "${GROUP}" = "${P_GROUP}" ]; then
313108e23beeSColin Percival			echo -n "${FPATH} is owned by group id ${P_GROUP}, "
313208e23beeSColin Percival			echo "but should be owned by group id ${GROUP}."
313308e23beeSColin Percival		fi
313408e23beeSColin Percival
313508e23beeSColin Percival		# Warn about different permissions.  We do not warn about
313608e23beeSColin Percival		# different permissions on symlinks, since some archivers
313708e23beeSColin Percival		# don't extract symlink permissions correctly and they are
313808e23beeSColin Percival		# ignored anyway.
313908e23beeSColin Percival		if ! [ "${PERM}" = "${P_PERM}" ] &&
314008e23beeSColin Percival		    ! [ "${TYPE}" = "L" ]; then
314108e23beeSColin Percival			echo -n "${FPATH} has ${P_PERM} permissions, "
314208e23beeSColin Percival			echo "but should have ${PERM} permissions."
314308e23beeSColin Percival		fi
314408e23beeSColin Percival
314508e23beeSColin Percival		# Warn about different file hashes / symlink destinations.
314608e23beeSColin Percival		if ! [ "${HASH}" = "${P_HASH}" ]; then
314708e23beeSColin Percival			if [ "${TYPE}" = "L" ]; then
314808e23beeSColin Percival				echo -n "${FPATH} is a symlink to ${P_HASH}, "
314908e23beeSColin Percival				echo "but should be a symlink to ${HASH}."
315008e23beeSColin Percival			fi
315108e23beeSColin Percival			if [ "${TYPE}" = "f" ]; then
315208e23beeSColin Percival				echo -n "${FPATH} has SHA256 hash ${P_HASH}, "
315308e23beeSColin Percival				echo "but should have SHA256 hash ${HASH}."
315408e23beeSColin Percival			fi
315508e23beeSColin Percival		fi
315608e23beeSColin Percival
315708e23beeSColin Percival		# We don't warn about different hard links, since some
315808e23beeSColin Percival		# some archivers break hard links, and as long as the
315908e23beeSColin Percival		# underlying data is correct they really don't matter.
316008e23beeSColin Percival	done < INDEX-NOTMATCHING
316108e23beeSColin Percival
316208e23beeSColin Percival	# Clean up
3163bb10a826SColin Percival	rm $1 $1.noflags $1.sorted $2 INDEX-NOTMATCHING
316408e23beeSColin Percival}
316508e23beeSColin Percival
316608e23beeSColin Percival# Do the work involved in comparing the system to a "known good" index
316708e23beeSColin PercivalIDS_run () {
316808e23beeSColin Percival	workdir_init || return 1
316908e23beeSColin Percival
317008e23beeSColin Percival	# Prepare the mirror list.
317108e23beeSColin Percival	fetch_pick_server_init && fetch_pick_server
317208e23beeSColin Percival
317308e23beeSColin Percival	# Try to fetch the public key until we run out of servers.
317408e23beeSColin Percival	while ! fetch_key; do
317508e23beeSColin Percival		fetch_pick_server || return 1
317608e23beeSColin Percival	done
317708e23beeSColin Percival
317808e23beeSColin Percival	# Try to fetch the metadata index signature ("tag") until we run
317908e23beeSColin Percival	# out of available servers; and sanity check the downloaded tag.
318008e23beeSColin Percival	while ! fetch_tag; do
318108e23beeSColin Percival		fetch_pick_server || return 1
318208e23beeSColin Percival	done
318308e23beeSColin Percival	fetch_tagsanity || return 1
318408e23beeSColin Percival
318508e23beeSColin Percival	# Fetch INDEX-OLD and INDEX-ALL.
318608e23beeSColin Percival	fetch_metadata INDEX-OLD INDEX-ALL || return 1
318708e23beeSColin Percival
318808e23beeSColin Percival	# Generate filtered INDEX-OLD and INDEX-ALL files containing only
318908e23beeSColin Percival	# the components we want and without anything marked as "Ignore".
319008e23beeSColin Percival	fetch_filter_metadata INDEX-OLD || return 1
319108e23beeSColin Percival	fetch_filter_metadata INDEX-ALL || return 1
319208e23beeSColin Percival
319308e23beeSColin Percival	# Merge the INDEX-OLD and INDEX-ALL files into INDEX-ALL.
319408e23beeSColin Percival	sort INDEX-OLD INDEX-ALL > INDEX-ALL.tmp
319508e23beeSColin Percival	mv INDEX-ALL.tmp INDEX-ALL
319608e23beeSColin Percival	rm INDEX-OLD
319708e23beeSColin Percival
319808e23beeSColin Percival	# Translate /boot/${KERNCONF} to ${KERNELDIR}
319908e23beeSColin Percival	fetch_filter_kernel_names INDEX-ALL ${KERNCONF}
320008e23beeSColin Percival
320108e23beeSColin Percival	# Inspect the system and generate an INDEX-PRESENT file.
320208e23beeSColin Percival	fetch_inspect_system INDEX-ALL INDEX-PRESENT /dev/null || return 1
320308e23beeSColin Percival
320408e23beeSColin Percival	# Compare INDEX-ALL and INDEX-PRESENT and print warnings about any
320508e23beeSColin Percival	# differences.
320608e23beeSColin Percival	IDS_compare INDEX-ALL INDEX-PRESENT
320708e23beeSColin Percival}
320808e23beeSColin Percival
320948ffe56aSColin Percival#### Main functions -- call parameter-handling and core functions
321048ffe56aSColin Percival
321148ffe56aSColin Percival# Using the command line, configuration file, and defaults,
321248ffe56aSColin Percival# set all the parameters which are needed later.
321348ffe56aSColin Percivalget_params () {
321448ffe56aSColin Percival	init_params
321548ffe56aSColin Percival	parse_cmdline $@
321648ffe56aSColin Percival	parse_conffile
321748ffe56aSColin Percival	default_params
321848ffe56aSColin Percival}
321948ffe56aSColin Percival
322048ffe56aSColin Percival# Fetch command.  Make sure that we're being called
322148ffe56aSColin Percival# interactively, then run fetch_check_params and fetch_run
322248ffe56aSColin Percivalcmd_fetch () {
32238935f242SAllan Jude	if [ ! -t 0 && $NOTTYOK -eq 0 ]; then
322448ffe56aSColin Percival		echo -n "`basename $0` fetch should not "
322548ffe56aSColin Percival		echo "be run non-interactively."
322648ffe56aSColin Percival		echo "Run `basename $0` cron instead."
322748ffe56aSColin Percival		exit 1
322848ffe56aSColin Percival	fi
322948ffe56aSColin Percival	fetch_check_params
323048ffe56aSColin Percival	fetch_run || exit 1
323148ffe56aSColin Percival}
323248ffe56aSColin Percival
323348ffe56aSColin Percival# Cron command.  Make sure the parameters are sensible; wait
323448ffe56aSColin Percival# rand(3600) seconds; then fetch updates.  While fetching updates,
323548ffe56aSColin Percival# send output to a temporary file; only print that file if the
323648ffe56aSColin Percival# fetching failed.
323748ffe56aSColin Percivalcmd_cron () {
323848ffe56aSColin Percival	fetch_check_params
323948ffe56aSColin Percival	sleep `jot -r 1 0 3600`
324048ffe56aSColin Percival
324148ffe56aSColin Percival	TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1
324248ffe56aSColin Percival	if ! fetch_run >> ${TMPFILE} ||
324348ffe56aSColin Percival	    ! grep -q "No updates needed" ${TMPFILE} ||
324448ffe56aSColin Percival	    [ ${VERBOSELEVEL} = "debug" ]; then
324548ffe56aSColin Percival		mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE}
324648ffe56aSColin Percival	fi
324748ffe56aSColin Percival
324848ffe56aSColin Percival	rm ${TMPFILE}
324948ffe56aSColin Percival}
325048ffe56aSColin Percival
3251db6b0a61SColin Percival# Fetch files for upgrading to a new release.
3252db6b0a61SColin Percivalcmd_upgrade () {
3253db6b0a61SColin Percival	upgrade_check_params
3254db6b0a61SColin Percival	upgrade_run || exit 1
3255db6b0a61SColin Percival}
3256db6b0a61SColin Percival
325748ffe56aSColin Percival# Install downloaded updates.
325848ffe56aSColin Percivalcmd_install () {
325948ffe56aSColin Percival	install_check_params
326048ffe56aSColin Percival	install_run || exit 1
326148ffe56aSColin Percival}
326248ffe56aSColin Percival
326348ffe56aSColin Percival# Rollback most recently installed updates.
326448ffe56aSColin Percivalcmd_rollback () {
326548ffe56aSColin Percival	rollback_check_params
326648ffe56aSColin Percival	rollback_run || exit 1
326748ffe56aSColin Percival}
326848ffe56aSColin Percival
326908e23beeSColin Percival# Compare system against a "known good" index.
327008e23beeSColin Percivalcmd_IDS () {
327108e23beeSColin Percival	IDS_check_params
327208e23beeSColin Percival	IDS_run || exit 1
327308e23beeSColin Percival}
327408e23beeSColin Percival
327548ffe56aSColin Percival#### Entry point
327648ffe56aSColin Percival
327748ffe56aSColin Percival# Make sure we find utilities from the base system
327848ffe56aSColin Percivalexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
327948ffe56aSColin Percival
32809c990fb2SGordon Tetlow# Set a pager if the user doesn't
32819c990fb2SGordon Tetlowif [ -z "$PAGER" ]; then
32829c990fb2SGordon Tetlow	PAGER=/usr/bin/more
32839c990fb2SGordon Tetlowfi
32849c990fb2SGordon Tetlow
3285f2890dbdSColin Percival# Set LC_ALL in order to avoid problems with character ranges like [A-Z].
3286f2890dbdSColin Percivalexport LC_ALL=C
3287f2890dbdSColin Percival
328848ffe56aSColin Percivalget_params $@
328948ffe56aSColin Percivalfor COMMAND in ${COMMANDS}; do
329048ffe56aSColin Percival	cmd_${COMMAND}
329148ffe56aSColin Percivaldone
3292