xref: /freebsd/usr.sbin/freebsd-update/freebsd-update.sh (revision f6e2146105c8a4253b008ce7818457faa60b3dda)
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
48dd917c79SGleb Smirnoff  -r release   -- Target for upgrade (e.g., 11.1-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
55b39ce43eSColin Percival  --currently-running release
56b39ce43eSColin Percival               -- Update as if currently running this release
5748ffe56aSColin PercivalCommands:
5848ffe56aSColin Percival  fetch        -- Fetch updates from server
5948ffe56aSColin Percival  cron         -- Sleep rand(3600) seconds, fetch updates, and send an
6048ffe56aSColin Percival                  email if updates were found
61db6b0a61SColin Percival  upgrade      -- Fetch upgrades to FreeBSD version specified via -r option
62db6b0a61SColin Percival  install      -- Install downloaded updates or upgrades
6348ffe56aSColin Percival  rollback     -- Uninstall most recently installed updates
6408e23beeSColin Percival  IDS          -- Compare the system against an index of "known good" files.
6548ffe56aSColin PercivalEOF
6648ffe56aSColin Percival	exit 0
6748ffe56aSColin Percival}
6848ffe56aSColin Percival
6948ffe56aSColin Percival#### Configuration processing functions
7048ffe56aSColin Percival
7148ffe56aSColin Percival#-
7248ffe56aSColin Percival# Configuration options are set in the following order of priority:
7348ffe56aSColin Percival# 1. Command line options
7448ffe56aSColin Percival# 2. Configuration file options
7548ffe56aSColin Percival# 3. Default options
7648ffe56aSColin Percival# In addition, certain options (e.g., IgnorePaths) can be specified multiple
7748ffe56aSColin Percival# times and (as long as these are all in the same place, e.g., inside the
7848ffe56aSColin Percival# configuration file) they will accumulate.  Finally, because the path to the
7948ffe56aSColin Percival# configuration file can be specified at the command line, the entire command
8048ffe56aSColin Percival# line must be processed before we start reading the configuration file.
8148ffe56aSColin Percival#
8248ffe56aSColin Percival# Sound like a mess?  It is.  Here's how we handle this:
8348ffe56aSColin Percival# 1. Initialize CONFFILE and all the options to "".
8448ffe56aSColin Percival# 2. Process the command line.  Throw an error if a non-accumulating option
8548ffe56aSColin Percival#    is specified twice.
8648ffe56aSColin Percival# 3. If CONFFILE is "", set CONFFILE to /etc/freebsd-update.conf .
8748ffe56aSColin Percival# 4. For all the configuration options X, set X_saved to X.
8848ffe56aSColin Percival# 5. Initialize all the options to "".
8948ffe56aSColin Percival# 6. Read CONFFILE line by line, parsing options.
9048ffe56aSColin Percival# 7. For each configuration option X, set X to X_saved iff X_saved is not "".
9148ffe56aSColin Percival# 8. Repeat steps 4-7, except setting options to their default values at (6).
9248ffe56aSColin Percival
9348ffe56aSColin PercivalCONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE
9448ffe56aSColin Percival    KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED
9508e23beeSColin Percival    BASEDIR VERBOSELEVEL TARGETRELEASE STRICTCOMPONENTS MERGECHANGES
9623d827efSSimon L. B. Nielsen    IDSIGNOREPATHS BACKUPKERNEL BACKUPKERNELDIR BACKUPKERNELSYMBOLFILES"
9748ffe56aSColin Percival
9848ffe56aSColin Percival# Set all the configuration options to "".
9948ffe56aSColin Percivalnullconfig () {
10048ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
10148ffe56aSColin Percival		eval ${X}=""
10248ffe56aSColin Percival	done
10348ffe56aSColin Percival}
10448ffe56aSColin Percival
10548ffe56aSColin Percival# For each configuration option X, set X_saved to X.
10648ffe56aSColin Percivalsaveconfig () {
10748ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
10848ffe56aSColin Percival		eval ${X}_saved=\$${X}
10948ffe56aSColin Percival	done
11048ffe56aSColin Percival}
11148ffe56aSColin Percival
11248ffe56aSColin Percival# For each configuration option X, set X to X_saved if X_saved is not "".
11348ffe56aSColin Percivalmergeconfig () {
11448ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
11548ffe56aSColin Percival		eval _=\$${X}_saved
11648ffe56aSColin Percival		if ! [ -z "${_}" ]; then
11748ffe56aSColin Percival			eval ${X}=\$${X}_saved
11848ffe56aSColin Percival		fi
11948ffe56aSColin Percival	done
12048ffe56aSColin Percival}
12148ffe56aSColin Percival
12248ffe56aSColin Percival# Set the trusted keyprint.
12348ffe56aSColin Percivalconfig_KeyPrint () {
12448ffe56aSColin Percival	if [ -z ${KEYPRINT} ]; then
12548ffe56aSColin Percival		KEYPRINT=$1
12648ffe56aSColin Percival	else
12748ffe56aSColin Percival		return 1
12848ffe56aSColin Percival	fi
12948ffe56aSColin Percival}
13048ffe56aSColin Percival
13148ffe56aSColin Percival# Set the working directory.
13248ffe56aSColin Percivalconfig_WorkDir () {
13348ffe56aSColin Percival	if [ -z ${WORKDIR} ]; then
13448ffe56aSColin Percival		WORKDIR=$1
13548ffe56aSColin Percival	else
13648ffe56aSColin Percival		return 1
13748ffe56aSColin Percival	fi
13848ffe56aSColin Percival}
13948ffe56aSColin Percival
14048ffe56aSColin Percival# Set the name of the server (pool) from which to fetch updates
14148ffe56aSColin Percivalconfig_ServerName () {
14248ffe56aSColin Percival	if [ -z ${SERVERNAME} ]; then
14348ffe56aSColin Percival		SERVERNAME=$1
14448ffe56aSColin Percival	else
14548ffe56aSColin Percival		return 1
14648ffe56aSColin Percival	fi
14748ffe56aSColin Percival}
14848ffe56aSColin Percival
14948ffe56aSColin Percival# Set the address to which 'cron' output will be mailed.
15048ffe56aSColin Percivalconfig_MailTo () {
15148ffe56aSColin Percival	if [ -z ${MAILTO} ]; then
15248ffe56aSColin Percival		MAILTO=$1
15348ffe56aSColin Percival	else
15448ffe56aSColin Percival		return 1
15548ffe56aSColin Percival	fi
15648ffe56aSColin Percival}
15748ffe56aSColin Percival
15848ffe56aSColin Percival# Set whether FreeBSD Update is allowed to add files (or directories, or
15948ffe56aSColin Percival# symlinks) which did not previously exist.
16048ffe56aSColin Percivalconfig_AllowAdd () {
16148ffe56aSColin Percival	if [ -z ${ALLOWADD} ]; then
16248ffe56aSColin Percival		case $1 in
16348ffe56aSColin Percival		[Yy][Ee][Ss])
16448ffe56aSColin Percival			ALLOWADD=yes
16548ffe56aSColin Percival			;;
16648ffe56aSColin Percival		[Nn][Oo])
16748ffe56aSColin Percival			ALLOWADD=no
16848ffe56aSColin Percival			;;
16948ffe56aSColin Percival		*)
17048ffe56aSColin Percival			return 1
17148ffe56aSColin Percival			;;
17248ffe56aSColin Percival		esac
17348ffe56aSColin Percival	else
17448ffe56aSColin Percival		return 1
17548ffe56aSColin Percival	fi
17648ffe56aSColin Percival}
17748ffe56aSColin Percival
17848ffe56aSColin Percival# Set whether FreeBSD Update is allowed to remove files/directories/symlinks.
17948ffe56aSColin Percivalconfig_AllowDelete () {
18048ffe56aSColin Percival	if [ -z ${ALLOWDELETE} ]; then
18148ffe56aSColin Percival		case $1 in
18248ffe56aSColin Percival		[Yy][Ee][Ss])
18348ffe56aSColin Percival			ALLOWDELETE=yes
18448ffe56aSColin Percival			;;
18548ffe56aSColin Percival		[Nn][Oo])
18648ffe56aSColin Percival			ALLOWDELETE=no
18748ffe56aSColin Percival			;;
18848ffe56aSColin Percival		*)
18948ffe56aSColin Percival			return 1
19048ffe56aSColin Percival			;;
19148ffe56aSColin Percival		esac
19248ffe56aSColin Percival	else
19348ffe56aSColin Percival		return 1
19448ffe56aSColin Percival	fi
19548ffe56aSColin Percival}
19648ffe56aSColin Percival
19748ffe56aSColin Percival# Set whether FreeBSD Update should keep existing inode ownership,
19848ffe56aSColin Percival# permissions, and flags, in the event that they have been modified locally
19948ffe56aSColin Percival# after the release.
20048ffe56aSColin Percivalconfig_KeepModifiedMetadata () {
20148ffe56aSColin Percival	if [ -z ${KEEPMODIFIEDMETADATA} ]; then
20248ffe56aSColin Percival		case $1 in
20348ffe56aSColin Percival		[Yy][Ee][Ss])
20448ffe56aSColin Percival			KEEPMODIFIEDMETADATA=yes
20548ffe56aSColin Percival			;;
20648ffe56aSColin Percival		[Nn][Oo])
20748ffe56aSColin Percival			KEEPMODIFIEDMETADATA=no
20848ffe56aSColin Percival			;;
20948ffe56aSColin Percival		*)
21048ffe56aSColin Percival			return 1
21148ffe56aSColin Percival			;;
21248ffe56aSColin Percival		esac
21348ffe56aSColin Percival	else
21448ffe56aSColin Percival		return 1
21548ffe56aSColin Percival	fi
21648ffe56aSColin Percival}
21748ffe56aSColin Percival
21848ffe56aSColin Percival# Add to the list of components which should be kept updated.
21948ffe56aSColin Percivalconfig_Components () {
22048ffe56aSColin Percival	for C in $@; do
2215a74378cSXin LI		if [ "$C" = "src" ]; then
2225a74378cSXin LI			if [ -e /usr/src/COPYRIGHT ]; then
22348ffe56aSColin Percival				COMPONENTS="${COMPONENTS} ${C}"
2245a74378cSXin LI			else
2255a74378cSXin LI				echo "src component not installed, skipped"
2265a74378cSXin LI			fi
2275a74378cSXin LI		else
2285a74378cSXin LI			COMPONENTS="${COMPONENTS} ${C}"
2295a74378cSXin LI		fi
23048ffe56aSColin Percival	done
23148ffe56aSColin Percival}
23248ffe56aSColin Percival
23348ffe56aSColin Percival# Add to the list of paths under which updates will be ignored.
23448ffe56aSColin Percivalconfig_IgnorePaths () {
23548ffe56aSColin Percival	for C in $@; do
23648ffe56aSColin Percival		IGNOREPATHS="${IGNOREPATHS} ${C}"
23748ffe56aSColin Percival	done
23848ffe56aSColin Percival}
23948ffe56aSColin Percival
24008e23beeSColin Percival# Add to the list of paths which IDS should ignore.
24108e23beeSColin Percivalconfig_IDSIgnorePaths () {
24208e23beeSColin Percival	for C in $@; do
24308e23beeSColin Percival		IDSIGNOREPATHS="${IDSIGNOREPATHS} ${C}"
24408e23beeSColin Percival	done
24508e23beeSColin Percival}
24608e23beeSColin Percival
24748ffe56aSColin Percival# Add to the list of paths within which updates will be performed only if the
24848ffe56aSColin Percival# file on disk has not been modified locally.
24948ffe56aSColin Percivalconfig_UpdateIfUnmodified () {
25048ffe56aSColin Percival	for C in $@; do
25148ffe56aSColin Percival		UPDATEIFUNMODIFIED="${UPDATEIFUNMODIFIED} ${C}"
25248ffe56aSColin Percival	done
25348ffe56aSColin Percival}
25448ffe56aSColin Percival
255db6b0a61SColin Percival# Add to the list of paths within which updates to text files will be merged
256db6b0a61SColin Percival# instead of overwritten.
257db6b0a61SColin Percivalconfig_MergeChanges () {
258db6b0a61SColin Percival	for C in $@; do
259db6b0a61SColin Percival		MERGECHANGES="${MERGECHANGES} ${C}"
260db6b0a61SColin Percival	done
261db6b0a61SColin Percival}
262db6b0a61SColin Percival
26348ffe56aSColin Percival# Work on a FreeBSD installation mounted under $1
26448ffe56aSColin Percivalconfig_BaseDir () {
26548ffe56aSColin Percival	if [ -z ${BASEDIR} ]; then
26648ffe56aSColin Percival		BASEDIR=$1
26748ffe56aSColin Percival	else
26848ffe56aSColin Percival		return 1
26948ffe56aSColin Percival	fi
27048ffe56aSColin Percival}
27148ffe56aSColin Percival
272db6b0a61SColin Percival# When fetching upgrades, should we assume the user wants exactly the
273db6b0a61SColin Percival# components listed in COMPONENTS, rather than trying to guess based on
274db6b0a61SColin Percival# what's currently installed?
275db6b0a61SColin Percivalconfig_StrictComponents () {
276db6b0a61SColin Percival	if [ -z ${STRICTCOMPONENTS} ]; then
277db6b0a61SColin Percival		case $1 in
278db6b0a61SColin Percival		[Yy][Ee][Ss])
279db6b0a61SColin Percival			STRICTCOMPONENTS=yes
280db6b0a61SColin Percival			;;
281db6b0a61SColin Percival		[Nn][Oo])
282db6b0a61SColin Percival			STRICTCOMPONENTS=no
283db6b0a61SColin Percival			;;
284db6b0a61SColin Percival		*)
285db6b0a61SColin Percival			return 1
286db6b0a61SColin Percival			;;
287db6b0a61SColin Percival		esac
288db6b0a61SColin Percival	else
289db6b0a61SColin Percival		return 1
290db6b0a61SColin Percival	fi
291db6b0a61SColin Percival}
292db6b0a61SColin Percival
293db6b0a61SColin Percival# Upgrade to FreeBSD $1
294db6b0a61SColin Percivalconfig_TargetRelease () {
295db6b0a61SColin Percival	if [ -z ${TARGETRELEASE} ]; then
296db6b0a61SColin Percival		TARGETRELEASE=$1
297db6b0a61SColin Percival	else
298db6b0a61SColin Percival		return 1
299db6b0a61SColin Percival	fi
300d23dc1eeSColin Percival	if echo ${TARGETRELEASE} | grep -qE '^[0-9.]+$'; then
301d23dc1eeSColin Percival		TARGETRELEASE="${TARGETRELEASE}-RELEASE"
302d23dc1eeSColin Percival	fi
303db6b0a61SColin Percival}
304db6b0a61SColin Percival
30548ffe56aSColin Percival# Define what happens to output of utilities
30648ffe56aSColin Percivalconfig_VerboseLevel () {
30748ffe56aSColin Percival	if [ -z ${VERBOSELEVEL} ]; then
30848ffe56aSColin Percival		case $1 in
30948ffe56aSColin Percival		[Dd][Ee][Bb][Uu][Gg])
31048ffe56aSColin Percival			VERBOSELEVEL=debug
31148ffe56aSColin Percival			;;
31248ffe56aSColin Percival		[Nn][Oo][Ss][Tt][Aa][Tt][Ss])
31348ffe56aSColin Percival			VERBOSELEVEL=nostats
31448ffe56aSColin Percival			;;
31548ffe56aSColin Percival		[Ss][Tt][Aa][Tt][Ss])
31648ffe56aSColin Percival			VERBOSELEVEL=stats
31748ffe56aSColin Percival			;;
31848ffe56aSColin Percival		*)
31948ffe56aSColin Percival			return 1
32048ffe56aSColin Percival			;;
32148ffe56aSColin Percival		esac
32248ffe56aSColin Percival	else
32348ffe56aSColin Percival		return 1
32448ffe56aSColin Percival	fi
32548ffe56aSColin Percival}
32648ffe56aSColin Percival
32723d827efSSimon L. B. Nielsenconfig_BackupKernel () {
32823d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNEL} ]; then
32923d827efSSimon L. B. Nielsen		case $1 in
33023d827efSSimon L. B. Nielsen		[Yy][Ee][Ss])
33123d827efSSimon L. B. Nielsen			BACKUPKERNEL=yes
33223d827efSSimon L. B. Nielsen			;;
33323d827efSSimon L. B. Nielsen		[Nn][Oo])
33423d827efSSimon L. B. Nielsen			BACKUPKERNEL=no
33523d827efSSimon L. B. Nielsen			;;
33623d827efSSimon L. B. Nielsen		*)
33723d827efSSimon L. B. Nielsen			return 1
33823d827efSSimon L. B. Nielsen			;;
33923d827efSSimon L. B. Nielsen		esac
34023d827efSSimon L. B. Nielsen	else
34123d827efSSimon L. B. Nielsen		return 1
34223d827efSSimon L. B. Nielsen	fi
34323d827efSSimon L. B. Nielsen}
34423d827efSSimon L. B. Nielsen
34523d827efSSimon L. B. Nielsenconfig_BackupKernelDir () {
34623d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNELDIR} ]; then
34723d827efSSimon L. B. Nielsen		if [ -z "$1" ]; then
34823d827efSSimon L. B. Nielsen			echo "BackupKernelDir set to empty dir"
34923d827efSSimon L. B. Nielsen			return 1
35023d827efSSimon L. B. Nielsen		fi
35123d827efSSimon L. B. Nielsen
35223d827efSSimon L. B. Nielsen		# We check for some paths which would be extremely odd
35323d827efSSimon L. B. Nielsen		# to use, but which could cause a lot of problems if
35423d827efSSimon L. B. Nielsen		# used.
35523d827efSSimon L. B. Nielsen		case $1 in
35623d827efSSimon L. B. Nielsen		/|/bin|/boot|/etc|/lib|/libexec|/sbin|/usr|/var)
35723d827efSSimon L. B. Nielsen			echo "BackupKernelDir set to invalid path $1"
35823d827efSSimon L. B. Nielsen			return 1
35923d827efSSimon L. B. Nielsen			;;
36023d827efSSimon L. B. Nielsen		/*)
36123d827efSSimon L. B. Nielsen			BACKUPKERNELDIR=$1
36223d827efSSimon L. B. Nielsen			;;
36323d827efSSimon L. B. Nielsen		*)
36423d827efSSimon L. B. Nielsen			echo "BackupKernelDir ($1) is not an absolute path"
36523d827efSSimon L. B. Nielsen			return 1
36623d827efSSimon L. B. Nielsen			;;
36723d827efSSimon L. B. Nielsen		esac
36823d827efSSimon L. B. Nielsen	else
36923d827efSSimon L. B. Nielsen		return 1
37023d827efSSimon L. B. Nielsen	fi
37123d827efSSimon L. B. Nielsen}
37223d827efSSimon L. B. Nielsen
37323d827efSSimon L. B. Nielsenconfig_BackupKernelSymbolFiles () {
37423d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNELSYMBOLFILES} ]; then
37523d827efSSimon L. B. Nielsen		case $1 in
37623d827efSSimon L. B. Nielsen		[Yy][Ee][Ss])
37723d827efSSimon L. B. Nielsen			BACKUPKERNELSYMBOLFILES=yes
37823d827efSSimon L. B. Nielsen			;;
37923d827efSSimon L. B. Nielsen		[Nn][Oo])
38023d827efSSimon L. B. Nielsen			BACKUPKERNELSYMBOLFILES=no
38123d827efSSimon L. B. Nielsen			;;
38223d827efSSimon L. B. Nielsen		*)
38323d827efSSimon L. B. Nielsen			return 1
38423d827efSSimon L. B. Nielsen			;;
38523d827efSSimon L. B. Nielsen		esac
38623d827efSSimon L. B. Nielsen	else
38723d827efSSimon L. B. Nielsen		return 1
38823d827efSSimon L. B. Nielsen	fi
38923d827efSSimon L. B. Nielsen}
39023d827efSSimon L. B. Nielsen
39148ffe56aSColin Percival# Handle one line of configuration
39248ffe56aSColin Percivalconfigline () {
39348ffe56aSColin Percival	if [ $# -eq 0 ]; then
39448ffe56aSColin Percival		return
39548ffe56aSColin Percival	fi
39648ffe56aSColin Percival
39748ffe56aSColin Percival	OPT=$1
39848ffe56aSColin Percival	shift
39948ffe56aSColin Percival	config_${OPT} $@
40048ffe56aSColin Percival}
40148ffe56aSColin Percival
40248ffe56aSColin Percival#### Parameter handling functions.
40348ffe56aSColin Percival
40448ffe56aSColin Percival# Initialize parameters to null, just in case they're
40548ffe56aSColin Percival# set in the environment.
40648ffe56aSColin Percivalinit_params () {
40748ffe56aSColin Percival	# Configration settings
40848ffe56aSColin Percival	nullconfig
40948ffe56aSColin Percival
41048ffe56aSColin Percival	# No configuration file set yet
41148ffe56aSColin Percival	CONFFILE=""
41248ffe56aSColin Percival
41348ffe56aSColin Percival	# No commands specified yet
41448ffe56aSColin Percival	COMMANDS=""
4158935f242SAllan Jude
4168935f242SAllan Jude	# Force fetch to proceed
4178935f242SAllan Jude	FORCEFETCH=0
4188935f242SAllan Jude
4198935f242SAllan Jude	# Run without a TTY
4208935f242SAllan Jude	NOTTYOK=0
42133bd05c3SGuangyuan Yang
42233bd05c3SGuangyuan Yang	# Fetched first in a chain of commands
42333bd05c3SGuangyuan Yang	ISFETCHED=0
42448ffe56aSColin Percival}
42548ffe56aSColin Percival
42648ffe56aSColin Percival# Parse the command line
42748ffe56aSColin Percivalparse_cmdline () {
42848ffe56aSColin Percival	while [ $# -gt 0 ]; do
42948ffe56aSColin Percival		case "$1" in
43048ffe56aSColin Percival		# Location of configuration file
43148ffe56aSColin Percival		-f)
43248ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi
43348ffe56aSColin Percival			if [ ! -z "${CONFFILE}" ]; then usage; fi
43448ffe56aSColin Percival			shift; CONFFILE="$1"
43548ffe56aSColin Percival			;;
4368935f242SAllan Jude		-F)
4378935f242SAllan Jude			FORCEFETCH=1
4388935f242SAllan Jude			;;
4398935f242SAllan Jude		--not-running-from-cron)
4408935f242SAllan Jude			NOTTYOK=1
4418935f242SAllan Jude			;;
442b39ce43eSColin Percival		--currently-running)
443b39ce43eSColin Percival			shift; export UNAME_r="$1"
444b39ce43eSColin Percival			;;
44548ffe56aSColin Percival
44648ffe56aSColin Percival		# Configuration file equivalents
44748ffe56aSColin Percival		-b)
44848ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
44948ffe56aSColin Percival			config_BaseDir $1 || usage
45048ffe56aSColin Percival			;;
45148ffe56aSColin Percival		-d)
45248ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
45348ffe56aSColin Percival			config_WorkDir $1 || usage
45448ffe56aSColin Percival			;;
45548ffe56aSColin Percival		-k)
45648ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
45748ffe56aSColin Percival			config_KeyPrint $1 || usage
45848ffe56aSColin Percival			;;
45948ffe56aSColin Percival		-s)
46048ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
46148ffe56aSColin Percival			config_ServerName $1 || usage
46248ffe56aSColin Percival			;;
463db6b0a61SColin Percival		-r)
464db6b0a61SColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
465db6b0a61SColin Percival			config_TargetRelease $1 || usage
466db6b0a61SColin Percival			;;
46748ffe56aSColin Percival		-t)
46848ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
46948ffe56aSColin Percival			config_MailTo $1 || usage
47048ffe56aSColin Percival			;;
47148ffe56aSColin Percival		-v)
47248ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
47348ffe56aSColin Percival			config_VerboseLevel $1 || usage
47448ffe56aSColin Percival			;;
47548ffe56aSColin Percival
47648ffe56aSColin Percival		# Aliases for "-v debug" and "-v nostats"
47748ffe56aSColin Percival		--debug)
47848ffe56aSColin Percival			config_VerboseLevel debug || usage
47948ffe56aSColin Percival			;;
48048ffe56aSColin Percival		--no-stats)
48148ffe56aSColin Percival			config_VerboseLevel nostats || usage
48248ffe56aSColin Percival			;;
48348ffe56aSColin Percival
48448ffe56aSColin Percival		# Commands
48508e23beeSColin Percival		cron | fetch | upgrade | install | rollback | IDS)
48648ffe56aSColin Percival			COMMANDS="${COMMANDS} $1"
48748ffe56aSColin Percival			;;
48848ffe56aSColin Percival
48948ffe56aSColin Percival		# Anything else is an error
49048ffe56aSColin Percival		*)
49148ffe56aSColin Percival			usage
49248ffe56aSColin Percival			;;
49348ffe56aSColin Percival		esac
49448ffe56aSColin Percival		shift
49548ffe56aSColin Percival	done
49648ffe56aSColin Percival
49748ffe56aSColin Percival	# Make sure we have at least one command
49848ffe56aSColin Percival	if [ -z "${COMMANDS}" ]; then
49948ffe56aSColin Percival		usage
50048ffe56aSColin Percival	fi
50148ffe56aSColin Percival}
50248ffe56aSColin Percival
50348ffe56aSColin Percival# Parse the configuration file
50448ffe56aSColin Percivalparse_conffile () {
50548ffe56aSColin Percival	# If a configuration file was specified on the command line, check
50648ffe56aSColin Percival	# that it exists and is readable.
50748ffe56aSColin Percival	if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
50848ffe56aSColin Percival		echo -n "File does not exist "
50948ffe56aSColin Percival		echo -n "or is not readable: "
51048ffe56aSColin Percival		echo ${CONFFILE}
51148ffe56aSColin Percival		exit 1
51248ffe56aSColin Percival	fi
51348ffe56aSColin Percival
51448ffe56aSColin Percival	# If a configuration file was not specified on the command line,
51548ffe56aSColin Percival	# use the default configuration file path.  If that default does
51648ffe56aSColin Percival	# not exist, give up looking for any configuration.
51748ffe56aSColin Percival	if [ -z "${CONFFILE}" ]; then
51848ffe56aSColin Percival		CONFFILE="/etc/freebsd-update.conf"
51948ffe56aSColin Percival		if [ ! -r "${CONFFILE}" ]; then
52048ffe56aSColin Percival			return
52148ffe56aSColin Percival		fi
52248ffe56aSColin Percival	fi
52348ffe56aSColin Percival
52448ffe56aSColin Percival	# Save the configuration options specified on the command line, and
52548ffe56aSColin Percival	# clear all the options in preparation for reading the config file.
52648ffe56aSColin Percival	saveconfig
52748ffe56aSColin Percival	nullconfig
52848ffe56aSColin Percival
52948ffe56aSColin Percival	# Read the configuration file.  Anything after the first '#' is
53048ffe56aSColin Percival	# ignored, and any blank lines are ignored.
53148ffe56aSColin Percival	L=0
53248ffe56aSColin Percival	while read LINE; do
53348ffe56aSColin Percival		L=$(($L + 1))
53448ffe56aSColin Percival		LINEX=`echo "${LINE}" | cut -f 1 -d '#'`
53548ffe56aSColin Percival		if ! configline ${LINEX}; then
53648ffe56aSColin Percival			echo "Error processing configuration file, line $L:"
53748ffe56aSColin Percival			echo "==> ${LINE}"
53848ffe56aSColin Percival			exit 1
53948ffe56aSColin Percival		fi
54048ffe56aSColin Percival	done < ${CONFFILE}
54148ffe56aSColin Percival
54248ffe56aSColin Percival	# Merge the settings read from the configuration file with those
54348ffe56aSColin Percival	# provided at the command line.
54448ffe56aSColin Percival	mergeconfig
54548ffe56aSColin Percival}
54648ffe56aSColin Percival
54748ffe56aSColin Percival# Provide some default parameters
54848ffe56aSColin Percivaldefault_params () {
54948ffe56aSColin Percival	# Save any parameters already configured, and clear the slate
55048ffe56aSColin Percival	saveconfig
55148ffe56aSColin Percival	nullconfig
55248ffe56aSColin Percival
55348ffe56aSColin Percival	# Default configurations
55448ffe56aSColin Percival	config_WorkDir /var/db/freebsd-update
55548ffe56aSColin Percival	config_MailTo root
55648ffe56aSColin Percival	config_AllowAdd yes
55748ffe56aSColin Percival	config_AllowDelete yes
55848ffe56aSColin Percival	config_KeepModifiedMetadata yes
55948ffe56aSColin Percival	config_BaseDir /
56048ffe56aSColin Percival	config_VerboseLevel stats
561db6b0a61SColin Percival	config_StrictComponents no
56223d827efSSimon L. B. Nielsen	config_BackupKernel yes
56323d827efSSimon L. B. Nielsen	config_BackupKernelDir /boot/kernel.old
56423d827efSSimon L. B. Nielsen	config_BackupKernelSymbolFiles no
56548ffe56aSColin Percival
56648ffe56aSColin Percival	# Merge these defaults into the earlier-configured settings
56748ffe56aSColin Percival	mergeconfig
56848ffe56aSColin Percival}
56948ffe56aSColin Percival
57048ffe56aSColin Percival# Set utility output filtering options, based on ${VERBOSELEVEL}
57148ffe56aSColin Percivalfetch_setup_verboselevel () {
57248ffe56aSColin Percival	case ${VERBOSELEVEL} in
57348ffe56aSColin Percival	debug)
57448ffe56aSColin Percival		QUIETREDIR="/dev/stderr"
57548ffe56aSColin Percival		QUIETFLAG=" "
57648ffe56aSColin Percival		STATSREDIR="/dev/stderr"
57748ffe56aSColin Percival		DDSTATS=".."
57848ffe56aSColin Percival		XARGST="-t"
57948ffe56aSColin Percival		NDEBUG=" "
58048ffe56aSColin Percival		;;
58148ffe56aSColin Percival	nostats)
58248ffe56aSColin Percival		QUIETREDIR=""
58348ffe56aSColin Percival		QUIETFLAG=""
58448ffe56aSColin Percival		STATSREDIR="/dev/null"
58548ffe56aSColin Percival		DDSTATS=".."
58648ffe56aSColin Percival		XARGST=""
58748ffe56aSColin Percival		NDEBUG=""
58848ffe56aSColin Percival		;;
58948ffe56aSColin Percival	stats)
59048ffe56aSColin Percival		QUIETREDIR="/dev/null"
59148ffe56aSColin Percival		QUIETFLAG="-q"
59248ffe56aSColin Percival		STATSREDIR="/dev/stdout"
59348ffe56aSColin Percival		DDSTATS=""
59448ffe56aSColin Percival		XARGST=""
59548ffe56aSColin Percival		NDEBUG="-n"
59648ffe56aSColin Percival		;;
59748ffe56aSColin Percival	esac
59848ffe56aSColin Percival}
59948ffe56aSColin Percival
60048ffe56aSColin Percival# Perform sanity checks and set some final parameters
60148ffe56aSColin Percival# in preparation for fetching files.  Figure out which
60248ffe56aSColin Percival# set of updates should be downloaded: If the user is
60348ffe56aSColin Percival# running *-p[0-9]+, strip off the last part; if the
60448ffe56aSColin Percival# user is running -SECURITY, call it -RELEASE.  Chdir
60548ffe56aSColin Percival# into the working directory.
606211f2ba0SColin Percivalfetchupgrade_check_params () {
60748ffe56aSColin Percival	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
60848ffe56aSColin Percival
60948ffe56aSColin Percival	_SERVERNAME_z=\
61048ffe56aSColin Percival"SERVERNAME must be given via command line or configuration file."
61148ffe56aSColin Percival	_KEYPRINT_z="Key must be given via -k option or configuration file."
61248ffe56aSColin Percival	_KEYPRINT_bad="Invalid key fingerprint: "
61348ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
614f88076f0SMark Felder	_WORKDIR_bad2="Directory is not on a persistent filesystem: "
61548ffe56aSColin Percival
61648ffe56aSColin Percival	if [ -z "${SERVERNAME}" ]; then
61748ffe56aSColin Percival		echo -n "`basename $0`: "
61848ffe56aSColin Percival		echo "${_SERVERNAME_z}"
61948ffe56aSColin Percival		exit 1
62048ffe56aSColin Percival	fi
62148ffe56aSColin Percival	if [ -z "${KEYPRINT}" ]; then
62248ffe56aSColin Percival		echo -n "`basename $0`: "
62348ffe56aSColin Percival		echo "${_KEYPRINT_z}"
62448ffe56aSColin Percival		exit 1
62548ffe56aSColin Percival	fi
62648ffe56aSColin Percival	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
62748ffe56aSColin Percival		echo -n "`basename $0`: "
62848ffe56aSColin Percival		echo -n "${_KEYPRINT_bad}"
62948ffe56aSColin Percival		echo ${KEYPRINT}
63048ffe56aSColin Percival		exit 1
63148ffe56aSColin Percival	fi
63248ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
63348ffe56aSColin Percival		echo -n "`basename $0`: "
63448ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
63548ffe56aSColin Percival		echo ${WORKDIR}
63648ffe56aSColin Percival		exit 1
63748ffe56aSColin Percival	fi
638dfe9215bSMark Felder	case `df -T ${WORKDIR}` in */dev/md[0-9]* | *tmpfs*)
639f88076f0SMark Felder		echo -n "`basename $0`: "
640f88076f0SMark Felder		echo -n "${_WORKDIR_bad2}"
641f88076f0SMark Felder		echo ${WORKDIR}
642f88076f0SMark Felder		exit 1
643dfe9215bSMark Felder		;;
644dfe9215bSMark Felder	esac
645a2356430SColin Percival	chmod 700 ${WORKDIR}
64648ffe56aSColin Percival	cd ${WORKDIR} || exit 1
64748ffe56aSColin Percival
64848ffe56aSColin Percival	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
64948ffe56aSColin Percival	# to provide an upgrade path for FreeBSD Update 1.x users, since
65048ffe56aSColin Percival	# the kernels provided by FreeBSD Update 1.x are always labelled
65148ffe56aSColin Percival	# as X.Y-SECURITY.
65248ffe56aSColin Percival	RELNUM=`uname -r |
65348ffe56aSColin Percival	    sed -E 's,-p[0-9]+,,' |
65448ffe56aSColin Percival	    sed -E 's,-SECURITY,-RELEASE,'`
65548ffe56aSColin Percival	ARCH=`uname -m`
65648ffe56aSColin Percival	FETCHDIR=${RELNUM}/${ARCH}
657db6b0a61SColin Percival	PATCHDIR=${RELNUM}/${ARCH}/bp
65848ffe56aSColin Percival
65948ffe56aSColin Percival	# Figure out what directory contains the running kernel
66048ffe56aSColin Percival	BOOTFILE=`sysctl -n kern.bootfile`
66148ffe56aSColin Percival	KERNELDIR=${BOOTFILE%/kernel}
66248ffe56aSColin Percival	if ! [ -d ${KERNELDIR} ]; then
66348ffe56aSColin Percival		echo "Cannot identify running kernel"
66448ffe56aSColin Percival		exit 1
66548ffe56aSColin Percival	fi
66648ffe56aSColin Percival
6672c434b2cSColin Percival	# Figure out what kernel configuration is running.  We start with
6682c434b2cSColin Percival	# the output of `uname -i`, and then make the following adjustments:
6692c434b2cSColin Percival	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
6702c434b2cSColin Percival	# file says "ident SMP-GENERIC", I don't know...
6712c434b2cSColin Percival	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
6722c434b2cSColin Percival	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
6732c434b2cSColin Percival	# we're running an SMP kernel.  This mis-identification is a bug
6742c434b2cSColin Percival	# which was fixed in 6.2-STABLE.
6752c434b2cSColin Percival	KERNCONF=`uname -i`
6762c434b2cSColin Percival	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
6772c434b2cSColin Percival		KERNCONF=SMP
6782c434b2cSColin Percival	fi
6792c434b2cSColin Percival	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
6802c434b2cSColin Percival		if sysctl kern.version | grep -qE '/SMP$'; then
6812c434b2cSColin Percival			KERNCONF=SMP
6822c434b2cSColin Percival		fi
6832c434b2cSColin Percival	fi
6842c434b2cSColin Percival
68548ffe56aSColin Percival	# Define some paths
68648ffe56aSColin Percival	BSPATCH=/usr/bin/bspatch
68748ffe56aSColin Percival	SHA256=/sbin/sha256
68848ffe56aSColin Percival	PHTTPGET=/usr/libexec/phttpget
68948ffe56aSColin Percival
69048ffe56aSColin Percival	# Set up variables relating to VERBOSELEVEL
69148ffe56aSColin Percival	fetch_setup_verboselevel
69248ffe56aSColin Percival
69348ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
69448ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
69548ffe56aSColin Percival}
69648ffe56aSColin Percival
697211f2ba0SColin Percival# Perform sanity checks etc. before fetching updates.
698211f2ba0SColin Percivalfetch_check_params () {
699211f2ba0SColin Percival	fetchupgrade_check_params
700211f2ba0SColin Percival
701211f2ba0SColin Percival	if ! [ -z "${TARGETRELEASE}" ]; then
702211f2ba0SColin Percival		echo -n "`basename $0`: "
703211f2ba0SColin Percival		echo -n "-r option is meaningless with 'fetch' command.  "
704211f2ba0SColin Percival		echo "(Did you mean 'upgrade' instead?)"
705211f2ba0SColin Percival		exit 1
706211f2ba0SColin Percival	fi
7078935f242SAllan Jude
7088935f242SAllan Jude	# Check that we have updates ready to install
7098bf2dcceSAllan Jude	if [ -f ${BDHASH}-install/kerneldone -a $FORCEFETCH -eq 0 ]; then
7108935f242SAllan Jude		echo "You have a partially completed upgrade pending"
7118935f242SAllan Jude		echo "Run '$0 install' first."
7128935f242SAllan Jude		echo "Run '$0 fetch -F' to proceed anyway."
7138935f242SAllan Jude		exit 1
7148935f242SAllan Jude	fi
715211f2ba0SColin Percival}
716211f2ba0SColin Percival
717db6b0a61SColin Percival# Perform sanity checks etc. before fetching upgrades.
718db6b0a61SColin Percivalupgrade_check_params () {
719211f2ba0SColin Percival	fetchupgrade_check_params
720db6b0a61SColin Percival
721db6b0a61SColin Percival	# Unless set otherwise, we're upgrading to the same kernel config.
722db6b0a61SColin Percival	NKERNCONF=${KERNCONF}
723db6b0a61SColin Percival
724db6b0a61SColin Percival	# We need TARGETRELEASE set
725db6b0a61SColin Percival	_TARGETRELEASE_z="Release target must be specified via -r option."
726db6b0a61SColin Percival	if [ -z "${TARGETRELEASE}" ]; then
727db6b0a61SColin Percival		echo -n "`basename $0`: "
728db6b0a61SColin Percival		echo "${_TARGETRELEASE_z}"
729db6b0a61SColin Percival		exit 1
730db6b0a61SColin Percival	fi
731db6b0a61SColin Percival
732db6b0a61SColin Percival	# The target release should be != the current release.
733db6b0a61SColin Percival	if [ "${TARGETRELEASE}" = "${RELNUM}" ]; then
734db6b0a61SColin Percival		echo -n "`basename $0`: "
735db6b0a61SColin Percival		echo "Cannot upgrade from ${RELNUM} to itself"
736db6b0a61SColin Percival		exit 1
737db6b0a61SColin Percival	fi
738db6b0a61SColin Percival
739db6b0a61SColin Percival	# Turning off AllowAdd or AllowDelete is a bad idea for upgrades.
740db6b0a61SColin Percival	if [ "${ALLOWADD}" = "no" ]; then
741db6b0a61SColin Percival		echo -n "`basename $0`: "
742db6b0a61SColin Percival		echo -n "WARNING: \"AllowAdd no\" is a bad idea "
743db6b0a61SColin Percival		echo "when upgrading between releases."
744db6b0a61SColin Percival		echo
745db6b0a61SColin Percival	fi
746db6b0a61SColin Percival	if [ "${ALLOWDELETE}" = "no" ]; then
747db6b0a61SColin Percival		echo -n "`basename $0`: "
748db6b0a61SColin Percival		echo -n "WARNING: \"AllowDelete no\" is a bad idea "
749db6b0a61SColin Percival		echo "when upgrading between releases."
750db6b0a61SColin Percival		echo
751db6b0a61SColin Percival	fi
752db6b0a61SColin Percival
753db6b0a61SColin Percival	# Set EDITOR to /usr/bin/vi if it isn't already set
754db6b0a61SColin Percival	: ${EDITOR:='/usr/bin/vi'}
755db6b0a61SColin Percival}
756db6b0a61SColin Percival
75748ffe56aSColin Percival# Perform sanity checks and set some final parameters in
75848ffe56aSColin Percival# preparation for installing updates.
75948ffe56aSColin Percivalinstall_check_params () {
76048ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
76148ffe56aSColin Percival	if [ `id -u` != 0 ]; then
76248ffe56aSColin Percival		echo "You must be root to run this."
76348ffe56aSColin Percival		exit 1
76448ffe56aSColin Percival	fi
76548ffe56aSColin Percival
7662328d598SColin Percival	# Check that securelevel <= 0.  Otherwise we can't update schg files.
7672328d598SColin Percival	if [ `sysctl -n kern.securelevel` -gt 0 ]; then
7682328d598SColin Percival		echo "Updates cannot be installed when the system securelevel"
7692328d598SColin Percival		echo "is greater than zero."
7702328d598SColin Percival		exit 1
7712328d598SColin Percival	fi
7722328d598SColin Percival
77348ffe56aSColin Percival	# Check that we have a working directory
77448ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
77548ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
77648ffe56aSColin Percival		echo -n "`basename $0`: "
77748ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
77848ffe56aSColin Percival		echo ${WORKDIR}
77948ffe56aSColin Percival		exit 1
78048ffe56aSColin Percival	fi
78148ffe56aSColin Percival	cd ${WORKDIR} || exit 1
78248ffe56aSColin Percival
78348ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
78448ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
78548ffe56aSColin Percival
78648ffe56aSColin Percival	# Check that we have updates ready to install
78748ffe56aSColin Percival	if ! [ -L ${BDHASH}-install ]; then
78848ffe56aSColin Percival		echo "No updates are available to install."
78933bd05c3SGuangyuan Yang		if [ $ISFETCHED -eq 0 ]; then
79048ffe56aSColin Percival			echo "Run '$0 fetch' first."
79133bd05c3SGuangyuan Yang		fi
79233bd05c3SGuangyuan Yang		exit 0
79348ffe56aSColin Percival	fi
79448ffe56aSColin Percival	if ! [ -f ${BDHASH}-install/INDEX-OLD ] ||
79548ffe56aSColin Percival	    ! [ -f ${BDHASH}-install/INDEX-NEW ]; then
79648ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
79748ffe56aSColin Percival		echo "Re-run '$0 fetch'."
79848ffe56aSColin Percival		exit 1
79948ffe56aSColin Percival	fi
80023d827efSSimon L. B. Nielsen
80123d827efSSimon L. B. Nielsen	# Figure out what directory contains the running kernel
80223d827efSSimon L. B. Nielsen	BOOTFILE=`sysctl -n kern.bootfile`
80323d827efSSimon L. B. Nielsen	KERNELDIR=${BOOTFILE%/kernel}
80423d827efSSimon L. B. Nielsen	if ! [ -d ${KERNELDIR} ]; then
80523d827efSSimon L. B. Nielsen		echo "Cannot identify running kernel"
80623d827efSSimon L. B. Nielsen		exit 1
80723d827efSSimon L. B. Nielsen	fi
80848ffe56aSColin Percival}
80948ffe56aSColin Percival
81048ffe56aSColin Percival# Perform sanity checks and set some final parameters in
81148ffe56aSColin Percival# preparation for UNinstalling updates.
81248ffe56aSColin Percivalrollback_check_params () {
81348ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
81448ffe56aSColin Percival	if [ `id -u` != 0 ]; then
81548ffe56aSColin Percival		echo "You must be root to run this."
81648ffe56aSColin Percival		exit 1
81748ffe56aSColin Percival	fi
81848ffe56aSColin Percival
81948ffe56aSColin Percival	# Check that we have a working directory
82048ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
82148ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
82248ffe56aSColin Percival		echo -n "`basename $0`: "
82348ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
82448ffe56aSColin Percival		echo ${WORKDIR}
82548ffe56aSColin Percival		exit 1
82648ffe56aSColin Percival	fi
82748ffe56aSColin Percival	cd ${WORKDIR} || exit 1
82848ffe56aSColin Percival
82948ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
83048ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
83148ffe56aSColin Percival
83248ffe56aSColin Percival	# Check that we have updates ready to rollback
83348ffe56aSColin Percival	if ! [ -L ${BDHASH}-rollback ]; then
83448ffe56aSColin Percival		echo "No rollback directory found."
83548ffe56aSColin Percival		exit 1
83648ffe56aSColin Percival	fi
83748ffe56aSColin Percival	if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] ||
83848ffe56aSColin Percival	    ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then
83948ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
84048ffe56aSColin Percival		exit 1
84148ffe56aSColin Percival	fi
84248ffe56aSColin Percival}
84348ffe56aSColin Percival
84408e23beeSColin Percival# Perform sanity checks and set some final parameters
84508e23beeSColin Percival# in preparation for comparing the system against the
84608e23beeSColin Percival# published index.  Figure out which index we should
84708e23beeSColin Percival# compare against: If the user is running *-p[0-9]+,
84808e23beeSColin Percival# strip off the last part; if the user is running
84908e23beeSColin Percival# -SECURITY, call it -RELEASE.  Chdir into the working
85008e23beeSColin Percival# directory.
85108e23beeSColin PercivalIDS_check_params () {
85208e23beeSColin Percival	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
85308e23beeSColin Percival
85408e23beeSColin Percival	_SERVERNAME_z=\
85508e23beeSColin Percival"SERVERNAME must be given via command line or configuration file."
85608e23beeSColin Percival	_KEYPRINT_z="Key must be given via -k option or configuration file."
85708e23beeSColin Percival	_KEYPRINT_bad="Invalid key fingerprint: "
85808e23beeSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
85908e23beeSColin Percival
86008e23beeSColin Percival	if [ -z "${SERVERNAME}" ]; then
86108e23beeSColin Percival		echo -n "`basename $0`: "
86208e23beeSColin Percival		echo "${_SERVERNAME_z}"
86308e23beeSColin Percival		exit 1
86408e23beeSColin Percival	fi
86508e23beeSColin Percival	if [ -z "${KEYPRINT}" ]; then
86608e23beeSColin Percival		echo -n "`basename $0`: "
86708e23beeSColin Percival		echo "${_KEYPRINT_z}"
86808e23beeSColin Percival		exit 1
86908e23beeSColin Percival	fi
87008e23beeSColin Percival	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
87108e23beeSColin Percival		echo -n "`basename $0`: "
87208e23beeSColin Percival		echo -n "${_KEYPRINT_bad}"
87308e23beeSColin Percival		echo ${KEYPRINT}
87408e23beeSColin Percival		exit 1
87508e23beeSColin Percival	fi
87608e23beeSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
87708e23beeSColin Percival		echo -n "`basename $0`: "
87808e23beeSColin Percival		echo -n "${_WORKDIR_bad}"
87908e23beeSColin Percival		echo ${WORKDIR}
88008e23beeSColin Percival		exit 1
88108e23beeSColin Percival	fi
88208e23beeSColin Percival	cd ${WORKDIR} || exit 1
88308e23beeSColin Percival
88408e23beeSColin Percival	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
88508e23beeSColin Percival	# to provide an upgrade path for FreeBSD Update 1.x users, since
88608e23beeSColin Percival	# the kernels provided by FreeBSD Update 1.x are always labelled
88708e23beeSColin Percival	# as X.Y-SECURITY.
88808e23beeSColin Percival	RELNUM=`uname -r |
88908e23beeSColin Percival	    sed -E 's,-p[0-9]+,,' |
89008e23beeSColin Percival	    sed -E 's,-SECURITY,-RELEASE,'`
89108e23beeSColin Percival	ARCH=`uname -m`
89208e23beeSColin Percival	FETCHDIR=${RELNUM}/${ARCH}
89308e23beeSColin Percival	PATCHDIR=${RELNUM}/${ARCH}/bp
89408e23beeSColin Percival
89508e23beeSColin Percival	# Figure out what directory contains the running kernel
89608e23beeSColin Percival	BOOTFILE=`sysctl -n kern.bootfile`
89708e23beeSColin Percival	KERNELDIR=${BOOTFILE%/kernel}
89808e23beeSColin Percival	if ! [ -d ${KERNELDIR} ]; then
89908e23beeSColin Percival		echo "Cannot identify running kernel"
90008e23beeSColin Percival		exit 1
90108e23beeSColin Percival	fi
90208e23beeSColin Percival
90308e23beeSColin Percival	# Figure out what kernel configuration is running.  We start with
90408e23beeSColin Percival	# the output of `uname -i`, and then make the following adjustments:
90508e23beeSColin Percival	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
90608e23beeSColin Percival	# file says "ident SMP-GENERIC", I don't know...
90708e23beeSColin Percival	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
90808e23beeSColin Percival	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
90908e23beeSColin Percival	# we're running an SMP kernel.  This mis-identification is a bug
91008e23beeSColin Percival	# which was fixed in 6.2-STABLE.
91108e23beeSColin Percival	KERNCONF=`uname -i`
91208e23beeSColin Percival	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
91308e23beeSColin Percival		KERNCONF=SMP
91408e23beeSColin Percival	fi
91508e23beeSColin Percival	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
91608e23beeSColin Percival		if sysctl kern.version | grep -qE '/SMP$'; then
91708e23beeSColin Percival			KERNCONF=SMP
91808e23beeSColin Percival		fi
91908e23beeSColin Percival	fi
92008e23beeSColin Percival
92108e23beeSColin Percival	# Define some paths
92208e23beeSColin Percival	SHA256=/sbin/sha256
92308e23beeSColin Percival	PHTTPGET=/usr/libexec/phttpget
92408e23beeSColin Percival
92508e23beeSColin Percival	# Set up variables relating to VERBOSELEVEL
92608e23beeSColin Percival	fetch_setup_verboselevel
92708e23beeSColin Percival}
92808e23beeSColin Percival
92948ffe56aSColin Percival#### Core functionality -- the actual work gets done here
93048ffe56aSColin Percival
93148ffe56aSColin Percival# Use an SRV query to pick a server.  If the SRV query doesn't provide
93248ffe56aSColin Percival# a useful answer, use the server name specified by the user.
93348ffe56aSColin Percival# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
93448ffe56aSColin Percival# from that; or if no servers are returned, use ${SERVERNAME}.
93548ffe56aSColin Percival# This allows a user to specify "portsnap.freebsd.org" (in which case
93648ffe56aSColin Percival# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
93748ffe56aSColin Percival# (in which case portsnap will use that particular server, since there
93848ffe56aSColin Percival# won't be an SRV entry for that name).
93948ffe56aSColin Percival#
94048ffe56aSColin Percival# We ignore the Port field, since we are always going to use port 80.
94148ffe56aSColin Percival
94248ffe56aSColin Percival# Fetch the mirror list, but do not pick a mirror yet.  Returns 1 if
94348ffe56aSColin Percival# no mirrors are available for any reason.
94448ffe56aSColin Percivalfetch_pick_server_init () {
94548ffe56aSColin Percival	: > serverlist_tried
94648ffe56aSColin Percival
94748ffe56aSColin Percival# Check that host(1) exists (i.e., that the system wasn't built with the
94848ffe56aSColin Percival# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
94948ffe56aSColin Percival	if ! which -s host; then
95048ffe56aSColin Percival		: > serverlist_full
95148ffe56aSColin Percival		return 1
95248ffe56aSColin Percival	fi
95348ffe56aSColin Percival
95448ffe56aSColin Percival	echo -n "Looking up ${SERVERNAME} mirrors... "
95548ffe56aSColin Percival
95648ffe56aSColin Percival# Issue the SRV query and pull out the Priority, Weight, and Target fields.
95748ffe56aSColin Percival# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
95848ffe56aSColin Percival# "$name server selection ..."; we allow either format.
95948ffe56aSColin Percival	MLIST="_http._tcp.${SERVERNAME}"
96048ffe56aSColin Percival	host -t srv "${MLIST}" |
961e7fd266eSColin Percival	    sed -nE "s/${MLIST} (has SRV record|server selection) //Ip" |
96248ffe56aSColin Percival	    cut -f 1,2,4 -d ' ' |
96348ffe56aSColin Percival	    sed -e 's/\.$//' |
96448ffe56aSColin Percival	    sort > serverlist_full
96548ffe56aSColin Percival
96648ffe56aSColin Percival# If no records, give up -- we'll just use the server name we were given.
96748ffe56aSColin Percival	if [ `wc -l < serverlist_full` -eq 0 ]; then
96848ffe56aSColin Percival		echo "none found."
96948ffe56aSColin Percival		return 1
97048ffe56aSColin Percival	fi
97148ffe56aSColin Percival
97248ffe56aSColin Percival# Report how many mirrors we found.
97348ffe56aSColin Percival	echo `wc -l < serverlist_full` "mirrors found."
97448ffe56aSColin Percival
97548ffe56aSColin Percival# Generate a random seed for use in picking mirrors.  If HTTP_PROXY
97648ffe56aSColin Percival# is set, this will be used to generate the seed; otherwise, the seed
97748ffe56aSColin Percival# will be random.
97848ffe56aSColin Percival	if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
97948ffe56aSColin Percival		RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
98048ffe56aSColin Percival		    tr -d 'a-f' |
98148ffe56aSColin Percival		    cut -c 1-9`
98248ffe56aSColin Percival	else
98348ffe56aSColin Percival		RANDVALUE=`jot -r 1 0 999999999`
98448ffe56aSColin Percival	fi
98548ffe56aSColin Percival}
98648ffe56aSColin Percival
98748ffe56aSColin Percival# Pick a mirror.  Returns 1 if we have run out of mirrors to try.
98848ffe56aSColin Percivalfetch_pick_server () {
98948ffe56aSColin Percival# Generate a list of not-yet-tried mirrors
99048ffe56aSColin Percival	sort serverlist_tried |
99148ffe56aSColin Percival	    comm -23 serverlist_full - > serverlist
99248ffe56aSColin Percival
99348ffe56aSColin Percival# Have we run out of mirrors?
99448ffe56aSColin Percival	if [ `wc -l < serverlist` -eq 0 ]; then
99548ffe56aSColin Percival		echo "No mirrors remaining, giving up."
99648ffe56aSColin Percival		return 1
99748ffe56aSColin Percival	fi
99848ffe56aSColin Percival
99948ffe56aSColin Percival# Find the highest priority level (lowest numeric value).
100048ffe56aSColin Percival	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
100148ffe56aSColin Percival
100248ffe56aSColin Percival# Add up the weights of the response lines at that priority level.
100348ffe56aSColin Percival	SRV_WSUM=0;
100448ffe56aSColin Percival	while read X; do
100548ffe56aSColin Percival		case "$X" in
100648ffe56aSColin Percival		${SRV_PRIORITY}\ *)
100748ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
100848ffe56aSColin Percival			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
100948ffe56aSColin Percival			;;
101048ffe56aSColin Percival		esac
101148ffe56aSColin Percival	done < serverlist
101248ffe56aSColin Percival
101348ffe56aSColin Percival# If all the weights are 0, pretend that they are all 1 instead.
101448ffe56aSColin Percival	if [ ${SRV_WSUM} -eq 0 ]; then
101548ffe56aSColin Percival		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
101648ffe56aSColin Percival		SRV_W_ADD=1
101748ffe56aSColin Percival	else
101848ffe56aSColin Percival		SRV_W_ADD=0
101948ffe56aSColin Percival	fi
102048ffe56aSColin Percival
102148ffe56aSColin Percival# Pick a value between 0 and the sum of the weights - 1
102248ffe56aSColin Percival	SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
102348ffe56aSColin Percival
102448ffe56aSColin Percival# Read through the list of mirrors and set SERVERNAME.  Write the line
102548ffe56aSColin Percival# corresponding to the mirror we selected into serverlist_tried so that
102648ffe56aSColin Percival# we won't try it again.
102748ffe56aSColin Percival	while read X; do
102848ffe56aSColin Percival		case "$X" in
102948ffe56aSColin Percival		${SRV_PRIORITY}\ *)
103048ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
103148ffe56aSColin Percival			SRV_W=$(($SRV_W + $SRV_W_ADD))
103248ffe56aSColin Percival			if [ $SRV_RND -lt $SRV_W ]; then
103348ffe56aSColin Percival				SERVERNAME=`echo $X | cut -f 3 -d ' '`
103448ffe56aSColin Percival				echo "$X" >> serverlist_tried
103548ffe56aSColin Percival				break
103648ffe56aSColin Percival			else
103748ffe56aSColin Percival				SRV_RND=$(($SRV_RND - $SRV_W))
103848ffe56aSColin Percival			fi
103948ffe56aSColin Percival			;;
104048ffe56aSColin Percival		esac
104148ffe56aSColin Percival	done < serverlist
104248ffe56aSColin Percival}
104348ffe56aSColin Percival
104448ffe56aSColin Percival# Take a list of ${oldhash}|${newhash} and output a list of needed patches,
104548ffe56aSColin Percival# i.e., those for which we have ${oldhash} and don't have ${newhash}.
104648ffe56aSColin Percivalfetch_make_patchlist () {
104748ffe56aSColin Percival	grep -vE "^([0-9a-f]{64})\|\1$" |
104848ffe56aSColin Percival	    tr '|' ' ' |
104948ffe56aSColin Percival		while read X Y; do
105048ffe56aSColin Percival			if [ -f "files/${Y}.gz" ] ||
105148ffe56aSColin Percival			    [ ! -f "files/${X}.gz" ]; then
105248ffe56aSColin Percival				continue
105348ffe56aSColin Percival			fi
105448ffe56aSColin Percival			echo "${X}|${Y}"
1055*f6e21461SEd Maste		done | sort -u
105648ffe56aSColin Percival}
105748ffe56aSColin Percival
105848ffe56aSColin Percival# Print user-friendly progress statistics
105948ffe56aSColin Percivalfetch_progress () {
106048ffe56aSColin Percival	LNC=0
106148ffe56aSColin Percival	while read x; do
106248ffe56aSColin Percival		LNC=$(($LNC + 1))
106348ffe56aSColin Percival		if [ $(($LNC % 10)) = 0 ]; then
106448ffe56aSColin Percival			echo -n $LNC
106548ffe56aSColin Percival		elif [ $(($LNC % 2)) = 0 ]; then
106648ffe56aSColin Percival			echo -n .
106748ffe56aSColin Percival		fi
106848ffe56aSColin Percival	done
106948ffe56aSColin Percival	echo -n " "
107048ffe56aSColin Percival}
107148ffe56aSColin Percival
1072db6b0a61SColin Percival# Function for asking the user if everything is ok
1073db6b0a61SColin Percivalcontinuep () {
1074db6b0a61SColin Percival	while read -p "Does this look reasonable (y/n)? " CONTINUE; do
1075db6b0a61SColin Percival		case "${CONTINUE}" in
1076db6b0a61SColin Percival		y*)
1077db6b0a61SColin Percival			return 0
1078db6b0a61SColin Percival			;;
1079db6b0a61SColin Percival		n*)
1080db6b0a61SColin Percival			return 1
1081db6b0a61SColin Percival			;;
1082db6b0a61SColin Percival		esac
1083db6b0a61SColin Percival	done
1084db6b0a61SColin Percival}
1085db6b0a61SColin Percival
108648ffe56aSColin Percival# Initialize the working directory
108748ffe56aSColin Percivalworkdir_init () {
108848ffe56aSColin Percival	mkdir -p files
108948ffe56aSColin Percival	touch tINDEX.present
109048ffe56aSColin Percival}
109148ffe56aSColin Percival
109248ffe56aSColin Percival# Check that we have a public key with an appropriate hash, or
109348ffe56aSColin Percival# fetch the key if it doesn't exist.  Returns 1 if the key has
109448ffe56aSColin Percival# not yet been fetched.
109548ffe56aSColin Percivalfetch_key () {
109648ffe56aSColin Percival	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
109748ffe56aSColin Percival		return 0
109848ffe56aSColin Percival	fi
109948ffe56aSColin Percival
110048ffe56aSColin Percival	echo -n "Fetching public key from ${SERVERNAME}... "
110148ffe56aSColin Percival	rm -f pub.ssl
110248ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \
110348ffe56aSColin Percival	    2>${QUIETREDIR} || true
110448ffe56aSColin Percival	if ! [ -r pub.ssl ]; then
110548ffe56aSColin Percival		echo "failed."
110648ffe56aSColin Percival		return 1
110748ffe56aSColin Percival	fi
110848ffe56aSColin Percival	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
110948ffe56aSColin Percival		echo "key has incorrect hash."
111048ffe56aSColin Percival		rm -f pub.ssl
111148ffe56aSColin Percival		return 1
111248ffe56aSColin Percival	fi
111348ffe56aSColin Percival	echo "done."
111448ffe56aSColin Percival}
111548ffe56aSColin Percival
111648ffe56aSColin Percival# Fetch metadata signature, aka "tag".
111748ffe56aSColin Percivalfetch_tag () {
1118db6b0a61SColin Percival	echo -n "Fetching metadata signature "
1119db6b0a61SColin Percival	echo ${NDEBUG} "for ${RELNUM} from ${SERVERNAME}... "
112048ffe56aSColin Percival	rm -f latest.ssl
112148ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl	\
112248ffe56aSColin Percival	    2>${QUIETREDIR} || true
112348ffe56aSColin Percival	if ! [ -r latest.ssl ]; then
112448ffe56aSColin Percival		echo "failed."
112548ffe56aSColin Percival		return 1
112648ffe56aSColin Percival	fi
112748ffe56aSColin Percival
112848ffe56aSColin Percival	openssl rsautl -pubin -inkey pub.ssl -verify		\
112948ffe56aSColin Percival	    < latest.ssl > tag.new 2>${QUIETREDIR} || true
113048ffe56aSColin Percival	rm latest.ssl
113148ffe56aSColin Percival
113248ffe56aSColin Percival	if ! [ `wc -l < tag.new` = 1 ] ||
113348ffe56aSColin Percival	    ! grep -qE	\
113448ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
113548ffe56aSColin Percival		tag.new; then
113648ffe56aSColin Percival		echo "invalid signature."
113748ffe56aSColin Percival		return 1
113848ffe56aSColin Percival	fi
113948ffe56aSColin Percival
114048ffe56aSColin Percival	echo "done."
114148ffe56aSColin Percival
114248ffe56aSColin Percival	RELPATCHNUM=`cut -f 4 -d '|' < tag.new`
114348ffe56aSColin Percival	TINDEXHASH=`cut -f 5 -d '|' < tag.new`
114448ffe56aSColin Percival	EOLTIME=`cut -f 6 -d '|' < tag.new`
114548ffe56aSColin Percival}
114648ffe56aSColin Percival
114748ffe56aSColin Percival# Sanity-check the patch number in a tag, to make sure that we're not
114848ffe56aSColin Percival# going to "update" backwards and to prevent replay attacks.
114948ffe56aSColin Percivalfetch_tagsanity () {
115048ffe56aSColin Percival	# Check that we're not going to move from -pX to -pY with Y < X.
115148ffe56aSColin Percival	RELPX=`uname -r | sed -E 's,.*-,,'`
115248ffe56aSColin Percival	if echo ${RELPX} | grep -qE '^p[0-9]+$'; then
115348ffe56aSColin Percival		RELPX=`echo ${RELPX} | cut -c 2-`
115448ffe56aSColin Percival	else
115548ffe56aSColin Percival		RELPX=0
115648ffe56aSColin Percival	fi
115748ffe56aSColin Percival	if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then
115848ffe56aSColin Percival		echo
115948ffe56aSColin Percival		echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
116048ffe56aSColin Percival		echo " appear older than what"
116148ffe56aSColin Percival		echo "we are currently running (`uname -r`)!"
116248ffe56aSColin Percival		echo "Cowardly refusing to proceed any further."
116348ffe56aSColin Percival		return 1
116448ffe56aSColin Percival	fi
116548ffe56aSColin Percival
116648ffe56aSColin Percival	# If "tag" exists and corresponds to ${RELNUM}, make sure that
116748ffe56aSColin Percival	# it contains a patch number <= RELPATCHNUM, in order to protect
116848ffe56aSColin Percival	# against rollback (replay) attacks.
116948ffe56aSColin Percival	if [ -f tag ] &&
117048ffe56aSColin Percival	    grep -qE	\
117148ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
117248ffe56aSColin Percival		tag; then
117348ffe56aSColin Percival		LASTRELPATCHNUM=`cut -f 4 -d '|' < tag`
117448ffe56aSColin Percival
117548ffe56aSColin Percival		if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then
117648ffe56aSColin Percival			echo
117748ffe56aSColin Percival			echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
117848ffe56aSColin Percival			echo " are older than the"
117948ffe56aSColin Percival			echo -n "most recently seen updates"
118048ffe56aSColin Percival			echo " (${RELNUM}-p${LASTRELPATCHNUM})."
118148ffe56aSColin Percival			echo "Cowardly refusing to proceed any further."
118248ffe56aSColin Percival			return 1
118348ffe56aSColin Percival		fi
118448ffe56aSColin Percival	fi
118548ffe56aSColin Percival}
118648ffe56aSColin Percival
118748ffe56aSColin Percival# Fetch metadata index file
118848ffe56aSColin Percivalfetch_metadata_index () {
118948ffe56aSColin Percival	echo ${NDEBUG} "Fetching metadata index... "
119048ffe56aSColin Percival	rm -f ${TINDEXHASH}
119148ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH}
119248ffe56aSColin Percival	    2>${QUIETREDIR}
119348ffe56aSColin Percival	if ! [ -f ${TINDEXHASH} ]; then
119448ffe56aSColin Percival		echo "failed."
119548ffe56aSColin Percival		return 1
119648ffe56aSColin Percival	fi
119748ffe56aSColin Percival	if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then
119848ffe56aSColin Percival		echo "update metadata index corrupt."
119948ffe56aSColin Percival		return 1
120048ffe56aSColin Percival	fi
120148ffe56aSColin Percival	echo "done."
120248ffe56aSColin Percival}
120348ffe56aSColin Percival
120448ffe56aSColin Percival# Print an error message about signed metadata being bogus.
120548ffe56aSColin Percivalfetch_metadata_bogus () {
120648ffe56aSColin Percival	echo
120748ffe56aSColin Percival	echo "The update metadata$1 is correctly signed, but"
120848ffe56aSColin Percival	echo "failed an integrity check."
120948ffe56aSColin Percival	echo "Cowardly refusing to proceed any further."
121048ffe56aSColin Percival	return 1
121148ffe56aSColin Percival}
121248ffe56aSColin Percival
121348ffe56aSColin Percival# Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH}
121448ffe56aSColin Percival# with the lines not named in $@ from tINDEX.present (if that file exists).
121548ffe56aSColin Percivalfetch_metadata_index_merge () {
121648ffe56aSColin Percival	for METAFILE in $@; do
121748ffe56aSColin Percival		if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l`	\
121848ffe56aSColin Percival		    -ne 1 ]; then
121948ffe56aSColin Percival			fetch_metadata_bogus " index"
122048ffe56aSColin Percival			return 1
122148ffe56aSColin Percival		fi
122248ffe56aSColin Percival
122348ffe56aSColin Percival		grep -E "${METAFILE}\|" ${TINDEXHASH}
122448ffe56aSColin Percival	done |
122548ffe56aSColin Percival	    sort > tINDEX.wanted
122648ffe56aSColin Percival
122748ffe56aSColin Percival	if [ -f tINDEX.present ]; then
122848ffe56aSColin Percival		join -t '|' -v 2 tINDEX.wanted tINDEX.present |
122948ffe56aSColin Percival		    sort -m - tINDEX.wanted > tINDEX.new
123048ffe56aSColin Percival		rm tINDEX.wanted
123148ffe56aSColin Percival	else
123248ffe56aSColin Percival		mv tINDEX.wanted tINDEX.new
123348ffe56aSColin Percival	fi
123448ffe56aSColin Percival}
123548ffe56aSColin Percival
123648ffe56aSColin Percival# Sanity check all the lines of tINDEX.new.  Even if more metadata lines
123748ffe56aSColin Percival# are added by future versions of the server, this won't cause problems,
123848ffe56aSColin Percival# since the only lines which appear in tINDEX.new are the ones which we
123948ffe56aSColin Percival# specifically grepped out of ${TINDEXHASH}.
124048ffe56aSColin Percivalfetch_metadata_index_sanity () {
124148ffe56aSColin Percival	if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then
124248ffe56aSColin Percival		fetch_metadata_bogus " index"
124348ffe56aSColin Percival		return 1
124448ffe56aSColin Percival	fi
124548ffe56aSColin Percival}
124648ffe56aSColin Percival
124748ffe56aSColin Percival# Sanity check the metadata file $1.
124848ffe56aSColin Percivalfetch_metadata_sanity () {
124948ffe56aSColin Percival	# Some aliases to save space later: ${P} is a character which can
125048ffe56aSColin Percival	# appear in a path; ${M} is the four numeric metadata fields; and
125148ffe56aSColin Percival	# ${H} is a sha256 hash.
12527c06c7c5SKris Moore	P="[-+./:=,%@_[~[:alnum:]]"
125348ffe56aSColin Percival	M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+"
125448ffe56aSColin Percival	H="[0-9a-f]{64}"
125548ffe56aSColin Percival
125648ffe56aSColin Percival	# Check that the first four fields make sense.
125748ffe56aSColin Percival	if gunzip -c < files/$1.gz |
1258823c0d5fSXin LI	    grep -qvE "^[a-z]+\|[0-9a-z-]+\|${P}+\|[fdL-]\|"; then
125948ffe56aSColin Percival		fetch_metadata_bogus ""
126048ffe56aSColin Percival		return 1
126148ffe56aSColin Percival	fi
126248ffe56aSColin Percival
126348ffe56aSColin Percival	# Remove the first three fields.
126448ffe56aSColin Percival	gunzip -c < files/$1.gz |
126548ffe56aSColin Percival	    cut -f 4- -d '|' > sanitycheck.tmp
126648ffe56aSColin Percival
126748ffe56aSColin Percival	# Sanity check entries with type 'f'
126848ffe56aSColin Percival	if grep -E '^f' sanitycheck.tmp |
126948ffe56aSColin Percival	    grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then
127048ffe56aSColin Percival		fetch_metadata_bogus ""
127148ffe56aSColin Percival		return 1
127248ffe56aSColin Percival	fi
127348ffe56aSColin Percival
127448ffe56aSColin Percival	# Sanity check entries with type 'd'
127548ffe56aSColin Percival	if grep -E '^d' sanitycheck.tmp |
127648ffe56aSColin Percival	    grep -qvE "^d\|${M}\|\|\$"; then
127748ffe56aSColin Percival		fetch_metadata_bogus ""
127848ffe56aSColin Percival		return 1
127948ffe56aSColin Percival	fi
128048ffe56aSColin Percival
128148ffe56aSColin Percival	# Sanity check entries with type 'L'
128248ffe56aSColin Percival	if grep -E '^L' sanitycheck.tmp |
128348ffe56aSColin Percival	    grep -qvE "^L\|${M}\|${P}*\|\$"; then
128448ffe56aSColin Percival		fetch_metadata_bogus ""
128548ffe56aSColin Percival		return 1
128648ffe56aSColin Percival	fi
128748ffe56aSColin Percival
128848ffe56aSColin Percival	# Sanity check entries with type '-'
128948ffe56aSColin Percival	if grep -E '^-' sanitycheck.tmp |
129048ffe56aSColin Percival	    grep -qvE "^-\|\|\|\|\|\|"; then
129148ffe56aSColin Percival		fetch_metadata_bogus ""
129248ffe56aSColin Percival		return 1
129348ffe56aSColin Percival	fi
129448ffe56aSColin Percival
129548ffe56aSColin Percival	# Clean up
129648ffe56aSColin Percival	rm sanitycheck.tmp
129748ffe56aSColin Percival}
129848ffe56aSColin Percival
129948ffe56aSColin Percival# Fetch the metadata index and metadata files listed in $@,
130048ffe56aSColin Percival# taking advantage of metadata patches where possible.
130148ffe56aSColin Percivalfetch_metadata () {
130248ffe56aSColin Percival	fetch_metadata_index || return 1
130348ffe56aSColin Percival	fetch_metadata_index_merge $@ || return 1
130448ffe56aSColin Percival	fetch_metadata_index_sanity || return 1
130548ffe56aSColin Percival
130648ffe56aSColin Percival	# Generate a list of wanted metadata patches
130748ffe56aSColin Percival	join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new |
130848ffe56aSColin Percival	    fetch_make_patchlist > patchlist
130948ffe56aSColin Percival
131048ffe56aSColin Percival	if [ -s patchlist ]; then
131148ffe56aSColin Percival		# Attempt to fetch metadata patches
131248ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
131348ffe56aSColin Percival		echo ${NDEBUG} "metadata patches.${DDSTATS}"
131448ffe56aSColin Percival		tr '|' '-' < patchlist |
131548ffe56aSColin Percival		    lam -s "${FETCHDIR}/tp/" - -s ".gz" |
131648ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
131748ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
131848ffe56aSColin Percival		echo "done."
131948ffe56aSColin Percival
132048ffe56aSColin Percival		# Attempt to apply metadata patches
132148ffe56aSColin Percival		echo -n "Applying metadata patches... "
132248ffe56aSColin Percival		tr '|' ' ' < patchlist |
132348ffe56aSColin Percival		    while read X Y; do
132448ffe56aSColin Percival			if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
132548ffe56aSColin Percival			gunzip -c < ${X}-${Y}.gz > diff
132648ffe56aSColin Percival			gunzip -c < files/${X}.gz > diff-OLD
132748ffe56aSColin Percival
132848ffe56aSColin Percival			# Figure out which lines are being added and removed
132948ffe56aSColin Percival			grep -E '^-' diff |
133048ffe56aSColin Percival			    cut -c 2- |
133148ffe56aSColin Percival			    while read PREFIX; do
133248ffe56aSColin Percival				look "${PREFIX}" diff-OLD
133348ffe56aSColin Percival			    done |
133448ffe56aSColin Percival			    sort > diff-rm
133548ffe56aSColin Percival			grep -E '^\+' diff |
133648ffe56aSColin Percival			    cut -c 2- > diff-add
133748ffe56aSColin Percival
133848ffe56aSColin Percival			# Generate the new file
133948ffe56aSColin Percival			comm -23 diff-OLD diff-rm |
134048ffe56aSColin Percival			    sort - diff-add > diff-NEW
134148ffe56aSColin Percival
134248ffe56aSColin Percival			if [ `${SHA256} -q diff-NEW` = ${Y} ]; then
134348ffe56aSColin Percival				mv diff-NEW files/${Y}
134448ffe56aSColin Percival				gzip -n files/${Y}
134548ffe56aSColin Percival			else
134648ffe56aSColin Percival				mv diff-NEW ${Y}.bad
134748ffe56aSColin Percival			fi
134848ffe56aSColin Percival			rm -f ${X}-${Y}.gz diff
134948ffe56aSColin Percival			rm -f diff-OLD diff-NEW diff-add diff-rm
135048ffe56aSColin Percival		done 2>${QUIETREDIR}
135148ffe56aSColin Percival		echo "done."
135248ffe56aSColin Percival	fi
135348ffe56aSColin Percival
135448ffe56aSColin Percival	# Update metadata without patches
135548ffe56aSColin Percival	cut -f 2 -d '|' < tINDEX.new |
135648ffe56aSColin Percival	    while read Y; do
135748ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
135848ffe56aSColin Percival			echo ${Y};
135948ffe56aSColin Percival		fi
1360bce02f98SColin Percival	    done |
1361bce02f98SColin Percival	    sort -u > filelist
136248ffe56aSColin Percival
136348ffe56aSColin Percival	if [ -s filelist ]; then
136448ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
136548ffe56aSColin Percival		echo ${NDEBUG} "metadata files... "
136648ffe56aSColin Percival		lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist |
136748ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
136848ffe56aSColin Percival		    2>${QUIETREDIR}
136948ffe56aSColin Percival
137048ffe56aSColin Percival		while read Y; do
137148ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
137248ffe56aSColin Percival				echo "failed."
137348ffe56aSColin Percival				return 1
137448ffe56aSColin Percival			fi
137548ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
137648ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
137748ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
137848ffe56aSColin Percival			else
137948ffe56aSColin Percival				echo "metadata is corrupt."
138048ffe56aSColin Percival				return 1
138148ffe56aSColin Percival			fi
138248ffe56aSColin Percival		done < filelist
138348ffe56aSColin Percival		echo "done."
138448ffe56aSColin Percival	fi
138548ffe56aSColin Percival
138648ffe56aSColin Percival# Sanity-check the metadata files.
138748ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new > filelist
138848ffe56aSColin Percival	while read X; do
138948ffe56aSColin Percival		fetch_metadata_sanity ${X} || return 1
139048ffe56aSColin Percival	done < filelist
139148ffe56aSColin Percival
139248ffe56aSColin Percival# Remove files which are no longer needed
139348ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.present |
139448ffe56aSColin Percival	    sort > oldfiles
139548ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new |
139648ffe56aSColin Percival	    sort |
139748ffe56aSColin Percival	    comm -13 - oldfiles |
139848ffe56aSColin Percival	    lam -s "files/" - -s ".gz" |
139948ffe56aSColin Percival	    xargs rm -f
140048ffe56aSColin Percival	rm patchlist filelist oldfiles
140148ffe56aSColin Percival	rm ${TINDEXHASH}
140248ffe56aSColin Percival
140348ffe56aSColin Percival# We're done!
140448ffe56aSColin Percival	mv tINDEX.new tINDEX.present
140548ffe56aSColin Percival	mv tag.new tag
140648ffe56aSColin Percival
140748ffe56aSColin Percival	return 0
140848ffe56aSColin Percival}
140948ffe56aSColin Percival
1410db6b0a61SColin Percival# Extract a subset of a downloaded metadata file containing only the parts
1411db6b0a61SColin Percival# which are listed in COMPONENTS.
1412db6b0a61SColin Percivalfetch_filter_metadata_components () {
1413db6b0a61SColin Percival	METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
1414db6b0a61SColin Percival	gunzip -c < files/${METAHASH}.gz > $1.all
1415db6b0a61SColin Percival
1416db6b0a61SColin Percival	# Fish out the lines belonging to components we care about.
1417db6b0a61SColin Percival	for C in ${COMPONENTS}; do
1418db6b0a61SColin Percival		look "`echo ${C} | tr '/' '|'`|" $1.all
1419db6b0a61SColin Percival	done > $1
1420db6b0a61SColin Percival
1421db6b0a61SColin Percival	# Remove temporary file.
1422db6b0a61SColin Percival	rm $1.all
1423db6b0a61SColin Percival}
1424db6b0a61SColin Percival
1425b698a3abSColin Percival# Generate a filtered version of the metadata file $1 from the downloaded
142648ffe56aSColin Percival# file, by fishing out the lines corresponding to components we're trying
142748ffe56aSColin Percival# to keep updated, and then removing lines corresponding to paths we want
142848ffe56aSColin Percival# to ignore.
142948ffe56aSColin Percivalfetch_filter_metadata () {
143048ffe56aSColin Percival	# Fish out the lines belonging to components we care about.
1431db6b0a61SColin Percival	fetch_filter_metadata_components $1
1432db6b0a61SColin Percival
143348ffe56aSColin Percival	# Canonicalize directory names by removing any trailing / in
143448ffe56aSColin Percival	# order to avoid listing directories multiple times if they
143548ffe56aSColin Percival	# belong to multiple components.  Turning "/" into "" doesn't
143648ffe56aSColin Percival	# matter, since we add a leading "/" when we use paths later.
1437db6b0a61SColin Percival	cut -f 3- -d '|' $1 |
143848ffe56aSColin Percival	    sed -e 's,/|d|,|d|,' |
14397e654612SColin Percival	    sed -e 's,/|-|,|-|,' |
144048ffe56aSColin Percival	    sort -u > $1.tmp
144148ffe56aSColin Percival
144248ffe56aSColin Percival	# Figure out which lines to ignore and remove them.
144348ffe56aSColin Percival	for X in ${IGNOREPATHS}; do
144448ffe56aSColin Percival		grep -E "^${X}" $1.tmp
144548ffe56aSColin Percival	done |
144648ffe56aSColin Percival	    sort -u |
144748ffe56aSColin Percival	    comm -13 - $1.tmp > $1
144848ffe56aSColin Percival
144948ffe56aSColin Percival	# Remove temporary files.
1450db6b0a61SColin Percival	rm $1.tmp
145148ffe56aSColin Percival}
145248ffe56aSColin Percival
1453db6b0a61SColin Percival# Filter the metadata file $1 by adding lines with "/boot/$2"
1454bce02f98SColin Percival# replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the
1455db6b0a61SColin Percival# trailing "/kernel"); and if "/boot/$2" does not exist, remove
1456bce02f98SColin Percival# the original lines which start with that.
1457bce02f98SColin Percival# Put another way: Deal with the fact that the FOO kernel is sometimes
1458bce02f98SColin Percival# installed in /boot/FOO/ and is sometimes installed elsewhere.
145948ffe56aSColin Percivalfetch_filter_kernel_names () {
1460db6b0a61SColin Percival	grep ^/boot/$2 $1 |
1461db6b0a61SColin Percival	    sed -e "s,/boot/$2,${KERNELDIR},g" |
146248ffe56aSColin Percival	    sort - $1 > $1.tmp
146348ffe56aSColin Percival	mv $1.tmp $1
1464bce02f98SColin Percival
1465db6b0a61SColin Percival	if ! [ -d /boot/$2 ]; then
1466db6b0a61SColin Percival		grep -v ^/boot/$2 $1 > $1.tmp
1467bce02f98SColin Percival		mv $1.tmp $1
1468bce02f98SColin Percival	fi
146948ffe56aSColin Percival}
147048ffe56aSColin Percival
147148ffe56aSColin Percival# For all paths appearing in $1 or $3, inspect the system
147248ffe56aSColin Percival# and generate $2 describing what is currently installed.
147348ffe56aSColin Percivalfetch_inspect_system () {
147448ffe56aSColin Percival	# No errors yet...
147548ffe56aSColin Percival	rm -f .err
147648ffe56aSColin Percival
147748ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
147848ffe56aSColin Percival	echo -n "Inspecting system... "
147948ffe56aSColin Percival
148048ffe56aSColin Percival	# Generate list of files to inspect
148148ffe56aSColin Percival	cat $1 $3 |
148248ffe56aSColin Percival	    cut -f 1 -d '|' |
148348ffe56aSColin Percival	    sort -u > filelist
148448ffe56aSColin Percival
148548ffe56aSColin Percival	# Examine each file and output lines of the form
148648ffe56aSColin Percival	# /path/to/file|type|device-inum|user|group|perm|flags|value
148748ffe56aSColin Percival	# sorted by device and inode number.
148848ffe56aSColin Percival	while read F; do
148948ffe56aSColin Percival		# If the symlink/file/directory does not exist, record this.
149048ffe56aSColin Percival		if ! [ -e ${BASEDIR}/${F} ]; then
149148ffe56aSColin Percival			echo "${F}|-||||||"
149248ffe56aSColin Percival			continue
149348ffe56aSColin Percival		fi
149448ffe56aSColin Percival		if ! [ -r ${BASEDIR}/${F} ]; then
149548ffe56aSColin Percival			echo "Cannot read file: ${BASEDIR}/${F}"	\
149648ffe56aSColin Percival			    >/dev/stderr
149748ffe56aSColin Percival			touch .err
149848ffe56aSColin Percival			return 1
149948ffe56aSColin Percival		fi
150048ffe56aSColin Percival
150148ffe56aSColin Percival		# Otherwise, output an index line.
150248ffe56aSColin Percival		if [ -L ${BASEDIR}/${F} ]; then
150348ffe56aSColin Percival			echo -n "${F}|L|"
150448ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
150548ffe56aSColin Percival			readlink ${BASEDIR}/${F};
150648ffe56aSColin Percival		elif [ -f ${BASEDIR}/${F} ]; then
150748ffe56aSColin Percival			echo -n "${F}|f|"
150848ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
150948ffe56aSColin Percival			sha256 -q ${BASEDIR}/${F};
151048ffe56aSColin Percival		elif [ -d ${BASEDIR}/${F} ]; then
151148ffe56aSColin Percival			echo -n "${F}|d|"
151248ffe56aSColin Percival			stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
151348ffe56aSColin Percival		else
151448ffe56aSColin Percival			echo "Unknown file type: ${BASEDIR}/${F}"	\
151548ffe56aSColin Percival			    >/dev/stderr
151648ffe56aSColin Percival			touch .err
151748ffe56aSColin Percival			return 1
151848ffe56aSColin Percival		fi
151948ffe56aSColin Percival	done < filelist |
152048ffe56aSColin Percival	    sort -k 3,3 -t '|' > $2.tmp
152148ffe56aSColin Percival	rm filelist
152248ffe56aSColin Percival
15236dcc68c8SBenedict Reuschling	# Check if an error occurred during system inspection
152448ffe56aSColin Percival	if [ -f .err ]; then
152548ffe56aSColin Percival		return 1
152648ffe56aSColin Percival	fi
152748ffe56aSColin Percival
152848ffe56aSColin Percival	# Convert to the form
152948ffe56aSColin Percival	# /path/to/file|type|user|group|perm|flags|value|hlink
153048ffe56aSColin Percival	# by resolving identical device and inode numbers into hard links.
153148ffe56aSColin Percival	cut -f 1,3 -d '|' $2.tmp |
153248ffe56aSColin Percival	    sort -k 1,1 -t '|' |
153348ffe56aSColin Percival	    sort -s -u -k 2,2 -t '|' |
153448ffe56aSColin Percival	    join -1 2 -2 3 -t '|' - $2.tmp |
153548ffe56aSColin Percival	    awk -F \| -v OFS=\|		\
153648ffe56aSColin Percival		'{
153748ffe56aSColin Percival		    if (($2 == $3) || ($4 == "-"))
153848ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,""
153948ffe56aSColin Percival		    else
154048ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,$2
154148ffe56aSColin Percival		}' |
154248ffe56aSColin Percival	    sort > $2
154348ffe56aSColin Percival	rm $2.tmp
154448ffe56aSColin Percival
154548ffe56aSColin Percival	# We're finished looking around
154648ffe56aSColin Percival	echo "done."
154748ffe56aSColin Percival}
154848ffe56aSColin Percival
1549db6b0a61SColin Percival# For any paths matching ${MERGECHANGES}, compare $1 and $2 and find any
1550db6b0a61SColin Percival# files which differ; generate $3 containing these paths and the old hashes.
1551db6b0a61SColin Percivalfetch_filter_mergechanges () {
1552db6b0a61SColin Percival	# Pull out the paths and hashes of the files matching ${MERGECHANGES}.
1553db6b0a61SColin Percival	for F in $1 $2; do
1554db6b0a61SColin Percival		for X in ${MERGECHANGES}; do
1555db6b0a61SColin Percival			grep -E "^${X}" ${F}
1556db6b0a61SColin Percival		done |
1557db6b0a61SColin Percival		    cut -f 1,2,7 -d '|' |
1558db6b0a61SColin Percival		    sort > ${F}-values
1559db6b0a61SColin Percival	done
1560db6b0a61SColin Percival
1561db6b0a61SColin Percival	# Any line in $2-values which doesn't appear in $1-values and is a
1562db6b0a61SColin Percival	# file means that we should list the path in $3.
1563db6b0a61SColin Percival	comm -13 $1-values $2-values |
1564db6b0a61SColin Percival	    fgrep '|f|' |
1565db6b0a61SColin Percival	    cut -f 1 -d '|' > $2-paths
1566db6b0a61SColin Percival
1567db6b0a61SColin Percival	# For each path, pull out one (and only one!) entry from $1-values.
1568db6b0a61SColin Percival	# Note that we cannot distinguish which "old" version the user made
1569db6b0a61SColin Percival	# changes to; but hopefully any changes which occur due to security
1570db6b0a61SColin Percival	# updates will exist in both the "new" version and the version which
1571db6b0a61SColin Percival	# the user has installed, so the merging will still work.
1572db6b0a61SColin Percival	while read X; do
1573db6b0a61SColin Percival		look "${X}|" $1-values |
1574db6b0a61SColin Percival		    head -1
1575db6b0a61SColin Percival	done < $2-paths > $3
1576db6b0a61SColin Percival
1577db6b0a61SColin Percival	# Clean up
1578db6b0a61SColin Percival	rm $1-values $2-values $2-paths
1579db6b0a61SColin Percival}
1580db6b0a61SColin Percival
158148ffe56aSColin Percival# For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123]
1582db6b0a61SColin Percival# which correspond to lines in $2 with hashes not matching $1 or $3, unless
1583db6b0a61SColin Percival# the paths are listed in $4.  For entries in $2 marked "not present"
1584db6b0a61SColin Percival# (aka. type -), remove lines from $[123] unless there is a corresponding
1585db6b0a61SColin Percival# entry in $1.
158648ffe56aSColin Percivalfetch_filter_unmodified_notpresent () {
158748ffe56aSColin Percival	# Figure out which lines of $1 and $3 correspond to bits which
158848ffe56aSColin Percival	# should only be updated if they haven't changed, and fish out
158948ffe56aSColin Percival	# the (path, type, value) tuples.
159048ffe56aSColin Percival	# NOTE: We don't consider a file to be "modified" if it matches
159148ffe56aSColin Percival	# the hash from $3.
159248ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
159348ffe56aSColin Percival		grep -E "^${X}" $1
159448ffe56aSColin Percival		grep -E "^${X}" $3
159548ffe56aSColin Percival	done |
159648ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
159748ffe56aSColin Percival	    sort > $1-values
159848ffe56aSColin Percival
159948ffe56aSColin Percival	# Do the same for $2.
160048ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
160148ffe56aSColin Percival		grep -E "^${X}" $2
160248ffe56aSColin Percival	done |
160348ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
160448ffe56aSColin Percival	    sort > $2-values
160548ffe56aSColin Percival
160648ffe56aSColin Percival	# Any entry in $2-values which is not in $1-values corresponds to
1607db6b0a61SColin Percival	# a path which we need to remove from $1, $2, and $3, unless it
1608db6b0a61SColin Percival	# that path appears in $4.
1609db6b0a61SColin Percival	comm -13 $1-values $2-values |
1610db6b0a61SColin Percival	    sort -t '|' -k 1,1 > mlines.tmp
1611db6b0a61SColin Percival	cut -f 1 -d '|' $4 |
1612db6b0a61SColin Percival	    sort |
1613db6b0a61SColin Percival	    join -v 2 -t '|' - mlines.tmp |
1614db6b0a61SColin Percival	    sort > mlines
1615db6b0a61SColin Percival	rm $1-values $2-values mlines.tmp
161648ffe56aSColin Percival
161748ffe56aSColin Percival	# Any lines in $2 which are not in $1 AND are "not present" lines
161848ffe56aSColin Percival	# also belong in mlines.
161948ffe56aSColin Percival	comm -13 $1 $2 |
162048ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
162148ffe56aSColin Percival	    fgrep '|-|' >> mlines
162248ffe56aSColin Percival
162348ffe56aSColin Percival	# Remove lines from $1, $2, and $3
162448ffe56aSColin Percival	for X in $1 $2 $3; do
162548ffe56aSColin Percival		sort -t '|' -k 1,1 ${X} > ${X}.tmp
162648ffe56aSColin Percival		cut -f 1 -d '|' < mlines |
162748ffe56aSColin Percival		    sort |
162848ffe56aSColin Percival		    join -v 2 -t '|' - ${X}.tmp |
162948ffe56aSColin Percival		    sort > ${X}
163048ffe56aSColin Percival		rm ${X}.tmp
163148ffe56aSColin Percival	done
163248ffe56aSColin Percival
163348ffe56aSColin Percival	# Store a list of the modified files, for future reference
163448ffe56aSColin Percival	fgrep -v '|-|' mlines |
163548ffe56aSColin Percival	    cut -f 1 -d '|' > modifiedfiles
163648ffe56aSColin Percival	rm mlines
163748ffe56aSColin Percival}
163848ffe56aSColin Percival
163948ffe56aSColin Percival# For each entry in $1 of type -, remove any corresponding
164048ffe56aSColin Percival# entry from $2 if ${ALLOWADD} != "yes".  Remove all entries
164148ffe56aSColin Percival# of type - from $1.
164248ffe56aSColin Percivalfetch_filter_allowadd () {
164348ffe56aSColin Percival	cut -f 1,2 -d '|' < $1 |
164448ffe56aSColin Percival	    fgrep '|-' |
164548ffe56aSColin Percival	    cut -f 1 -d '|' > filesnotpresent
164648ffe56aSColin Percival
164748ffe56aSColin Percival	if [ ${ALLOWADD} != "yes" ]; then
164848ffe56aSColin Percival		sort < $2 |
164948ffe56aSColin Percival		    join -v 1 -t '|' - filesnotpresent |
165048ffe56aSColin Percival		    sort > $2.tmp
165148ffe56aSColin Percival		mv $2.tmp $2
165248ffe56aSColin Percival	fi
165348ffe56aSColin Percival
165448ffe56aSColin Percival	sort < $1 |
165548ffe56aSColin Percival	    join -v 1 -t '|' - filesnotpresent |
165648ffe56aSColin Percival	    sort > $1.tmp
165748ffe56aSColin Percival	mv $1.tmp $1
165848ffe56aSColin Percival	rm filesnotpresent
165948ffe56aSColin Percival}
166048ffe56aSColin Percival
166148ffe56aSColin Percival# If ${ALLOWDELETE} != "yes", then remove any entries from $1
166248ffe56aSColin Percival# which don't correspond to entries in $2.
166348ffe56aSColin Percivalfetch_filter_allowdelete () {
166448ffe56aSColin Percival	# Produce a lists ${PATH}|${TYPE}
166548ffe56aSColin Percival	for X in $1 $2; do
166648ffe56aSColin Percival		cut -f 1-2 -d '|' < ${X} |
166748ffe56aSColin Percival		    sort -u > ${X}.nodes
166848ffe56aSColin Percival	done
166948ffe56aSColin Percival
167048ffe56aSColin Percival	# Figure out which lines need to be removed from $1.
167148ffe56aSColin Percival	if [ ${ALLOWDELETE} != "yes" ]; then
167248ffe56aSColin Percival		comm -23 $1.nodes $2.nodes > $1.badnodes
167348ffe56aSColin Percival	else
167448ffe56aSColin Percival		: > $1.badnodes
167548ffe56aSColin Percival	fi
167648ffe56aSColin Percival
167748ffe56aSColin Percival	# Remove the relevant lines from $1
167848ffe56aSColin Percival	while read X; do
167948ffe56aSColin Percival		look "${X}|" $1
168048ffe56aSColin Percival	done < $1.badnodes |
168148ffe56aSColin Percival	    comm -13 - $1 > $1.tmp
168248ffe56aSColin Percival	mv $1.tmp $1
168348ffe56aSColin Percival
168448ffe56aSColin Percival	rm $1.badnodes $1.nodes $2.nodes
168548ffe56aSColin Percival}
168648ffe56aSColin Percival
168748ffe56aSColin Percival# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2
168848ffe56aSColin Percival# with metadata not matching any entry in $1, replace the corresponding
168948ffe56aSColin Percival# line of $3 with one having the same metadata as the entry in $2.
169048ffe56aSColin Percivalfetch_filter_modified_metadata () {
169148ffe56aSColin Percival	# Fish out the metadata from $1 and $2
169248ffe56aSColin Percival	for X in $1 $2; do
169348ffe56aSColin Percival		cut -f 1-6 -d '|' < ${X} > ${X}.metadata
169448ffe56aSColin Percival	done
169548ffe56aSColin Percival
169648ffe56aSColin Percival	# Find the metadata we need to keep
169748ffe56aSColin Percival	if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then
169848ffe56aSColin Percival		comm -13 $1.metadata $2.metadata > keepmeta
169948ffe56aSColin Percival	else
170048ffe56aSColin Percival		: > keepmeta
170148ffe56aSColin Percival	fi
170248ffe56aSColin Percival
170348ffe56aSColin Percival	# Extract the lines which we need to remove from $3, and
170448ffe56aSColin Percival	# construct the lines which we need to add to $3.
170548ffe56aSColin Percival	: > $3.remove
170648ffe56aSColin Percival	: > $3.add
170748ffe56aSColin Percival	while read LINE; do
170848ffe56aSColin Percival		NODE=`echo "${LINE}" | cut -f 1-2 -d '|'`
170948ffe56aSColin Percival		look "${NODE}|" $3 >> $3.remove
171048ffe56aSColin Percival		look "${NODE}|" $3 |
171148ffe56aSColin Percival		    cut -f 7- -d '|' |
171248ffe56aSColin Percival		    lam -s "${LINE}|" - >> $3.add
171348ffe56aSColin Percival	done < keepmeta
171448ffe56aSColin Percival
171548ffe56aSColin Percival	# Remove the specified lines and add the new lines.
171648ffe56aSColin Percival	sort $3.remove |
171748ffe56aSColin Percival	    comm -13 - $3 |
171848ffe56aSColin Percival	    sort -u - $3.add > $3.tmp
171948ffe56aSColin Percival	mv $3.tmp $3
172048ffe56aSColin Percival
172148ffe56aSColin Percival	rm keepmeta $1.metadata $2.metadata $3.add $3.remove
172248ffe56aSColin Percival}
172348ffe56aSColin Percival
172448ffe56aSColin Percival# Remove lines from $1 and $2 which are identical;
172548ffe56aSColin Percival# no need to update a file if it isn't changing.
172648ffe56aSColin Percivalfetch_filter_uptodate () {
172748ffe56aSColin Percival	comm -23 $1 $2 > $1.tmp
172848ffe56aSColin Percival	comm -13 $1 $2 > $2.tmp
172948ffe56aSColin Percival
173048ffe56aSColin Percival	mv $1.tmp $1
173148ffe56aSColin Percival	mv $2.tmp $2
173248ffe56aSColin Percival}
173348ffe56aSColin Percival
1734db6b0a61SColin Percival# Fetch any "clean" old versions of files we need for merging changes.
1735db6b0a61SColin Percivalfetch_files_premerge () {
1736db6b0a61SColin Percival	# We only need to do anything if $1 is non-empty.
1737db6b0a61SColin Percival	if [ -s $1 ]; then
1738db6b0a61SColin Percival		# Tell the user what we're doing
1739db6b0a61SColin Percival		echo -n "Fetching files from ${OLDRELNUM} for merging... "
1740db6b0a61SColin Percival
1741db6b0a61SColin Percival		# List of files wanted
1742db6b0a61SColin Percival		fgrep '|f|' < $1 |
1743db6b0a61SColin Percival		    cut -f 3 -d '|' |
1744db6b0a61SColin Percival		    sort -u > files.wanted
1745db6b0a61SColin Percival
1746db6b0a61SColin Percival		# Only fetch the files we don't already have
1747db6b0a61SColin Percival		while read Y; do
1748db6b0a61SColin Percival			if [ ! -f "files/${Y}.gz" ]; then
1749db6b0a61SColin Percival				echo ${Y};
1750db6b0a61SColin Percival			fi
1751db6b0a61SColin Percival		done < files.wanted > filelist
1752db6b0a61SColin Percival
1753db6b0a61SColin Percival		# Actually fetch them
1754db6b0a61SColin Percival		lam -s "${OLDFETCHDIR}/f/" - -s ".gz" < filelist |
1755db6b0a61SColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
1756db6b0a61SColin Percival		    2>${QUIETREDIR}
1757db6b0a61SColin Percival
1758db6b0a61SColin Percival		# Make sure we got them all, and move them into /files/
1759db6b0a61SColin Percival		while read Y; do
1760db6b0a61SColin Percival			if ! [ -f ${Y}.gz ]; then
1761db6b0a61SColin Percival				echo "failed."
1762db6b0a61SColin Percival				return 1
1763db6b0a61SColin Percival			fi
1764db6b0a61SColin Percival			if [ `gunzip -c < ${Y}.gz |
1765db6b0a61SColin Percival			    ${SHA256} -q` = ${Y} ]; then
1766db6b0a61SColin Percival				mv ${Y}.gz files/${Y}.gz
1767db6b0a61SColin Percival			else
1768db6b0a61SColin Percival				echo "${Y} has incorrect hash."
1769db6b0a61SColin Percival				return 1
1770db6b0a61SColin Percival			fi
1771db6b0a61SColin Percival		done < filelist
1772db6b0a61SColin Percival		echo "done."
1773db6b0a61SColin Percival
1774db6b0a61SColin Percival		# Clean up
1775db6b0a61SColin Percival		rm filelist files.wanted
1776db6b0a61SColin Percival	fi
1777db6b0a61SColin Percival}
1778db6b0a61SColin Percival
177948ffe56aSColin Percival# Prepare to fetch files: Generate a list of the files we need,
178048ffe56aSColin Percival# copy the unmodified files we have into /files/, and generate
178148ffe56aSColin Percival# a list of patches to download.
178248ffe56aSColin Percivalfetch_files_prepare () {
178348ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
178448ffe56aSColin Percival	echo -n "Preparing to download files... "
178548ffe56aSColin Percival
178648ffe56aSColin Percival	# Reduce indices to ${PATH}|${HASH} pairs
178748ffe56aSColin Percival	for X in $1 $2 $3; do
178848ffe56aSColin Percival		cut -f 1,2,7 -d '|' < ${X} |
178948ffe56aSColin Percival		    fgrep '|f|' |
179048ffe56aSColin Percival		    cut -f 1,3 -d '|' |
179148ffe56aSColin Percival		    sort > ${X}.hashes
179248ffe56aSColin Percival	done
179348ffe56aSColin Percival
179448ffe56aSColin Percival	# List of files wanted
179548ffe56aSColin Percival	cut -f 2 -d '|' < $3.hashes |
17962328d598SColin Percival	    sort -u |
17972328d598SColin Percival	    while read HASH; do
17982328d598SColin Percival		if ! [ -f files/${HASH}.gz ]; then
17992328d598SColin Percival			echo ${HASH}
18002328d598SColin Percival		fi
18012328d598SColin Percival	done > files.wanted
180248ffe56aSColin Percival
180348ffe56aSColin Percival	# Generate a list of unmodified files
180448ffe56aSColin Percival	comm -12 $1.hashes $2.hashes |
180548ffe56aSColin Percival	    sort -k 1,1 -t '|' > unmodified.files
180648ffe56aSColin Percival
180748ffe56aSColin Percival	# Copy all files into /files/.  We only need the unmodified files
180848ffe56aSColin Percival	# for use in patching; but we'll want all of them if the user asks
180948ffe56aSColin Percival	# to rollback the updates later.
1810210b8123SColin Percival	while read LINE; do
1811210b8123SColin Percival		F=`echo "${LINE}" | cut -f 1 -d '|'`
1812210b8123SColin Percival		HASH=`echo "${LINE}" | cut -f 2 -d '|'`
1813210b8123SColin Percival
1814210b8123SColin Percival		# Skip files we already have.
1815210b8123SColin Percival		if [ -f files/${HASH}.gz ]; then
1816210b8123SColin Percival			continue
1817210b8123SColin Percival		fi
1818210b8123SColin Percival
1819210b8123SColin Percival		# Make sure the file hasn't changed.
182048ffe56aSColin Percival		cp "${BASEDIR}/${F}" tmpfile
1821210b8123SColin Percival		if [ `sha256 -q tmpfile` != ${HASH} ]; then
1822210b8123SColin Percival			echo
1823210b8123SColin Percival			echo "File changed while FreeBSD Update running: ${F}"
1824210b8123SColin Percival			return 1
1825210b8123SColin Percival		fi
1826210b8123SColin Percival
1827210b8123SColin Percival		# Place the file into storage.
1828210b8123SColin Percival		gzip -c < tmpfile > files/${HASH}.gz
182948ffe56aSColin Percival		rm tmpfile
1830210b8123SColin Percival	done < $2.hashes
183148ffe56aSColin Percival
183248ffe56aSColin Percival	# Produce a list of patches to download
183348ffe56aSColin Percival	sort -k 1,1 -t '|' $3.hashes |
183448ffe56aSColin Percival	    join -t '|' -o 2.2,1.2 - unmodified.files |
183548ffe56aSColin Percival	    fetch_make_patchlist > patchlist
183648ffe56aSColin Percival
183748ffe56aSColin Percival	# Garbage collect
183848ffe56aSColin Percival	rm unmodified.files $1.hashes $2.hashes $3.hashes
183948ffe56aSColin Percival
184048ffe56aSColin Percival	# We don't need the list of possible old files any more.
184148ffe56aSColin Percival	rm $1
184248ffe56aSColin Percival
184348ffe56aSColin Percival	# We're finished making noise
184448ffe56aSColin Percival	echo "done."
184548ffe56aSColin Percival}
184648ffe56aSColin Percival
184748ffe56aSColin Percival# Fetch files.
184848ffe56aSColin Percivalfetch_files () {
184948ffe56aSColin Percival	# Attempt to fetch patches
185048ffe56aSColin Percival	if [ -s patchlist ]; then
185148ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
185248ffe56aSColin Percival		echo ${NDEBUG} "patches.${DDSTATS}"
185348ffe56aSColin Percival		tr '|' '-' < patchlist |
1854db6b0a61SColin Percival		    lam -s "${PATCHDIR}/" - |
185548ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
185648ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
185748ffe56aSColin Percival		echo "done."
185848ffe56aSColin Percival
185948ffe56aSColin Percival		# Attempt to apply patches
186048ffe56aSColin Percival		echo -n "Applying patches... "
186148ffe56aSColin Percival		tr '|' ' ' < patchlist |
186248ffe56aSColin Percival		    while read X Y; do
186348ffe56aSColin Percival			if [ ! -f "${X}-${Y}" ]; then continue; fi
186448ffe56aSColin Percival			gunzip -c < files/${X}.gz > OLD
186548ffe56aSColin Percival
186648ffe56aSColin Percival			bspatch OLD NEW ${X}-${Y}
186748ffe56aSColin Percival
186848ffe56aSColin Percival			if [ `${SHA256} -q NEW` = ${Y} ]; then
186948ffe56aSColin Percival				mv NEW files/${Y}
187048ffe56aSColin Percival				gzip -n files/${Y}
187148ffe56aSColin Percival			fi
187248ffe56aSColin Percival			rm -f diff OLD NEW ${X}-${Y}
187348ffe56aSColin Percival		done 2>${QUIETREDIR}
187448ffe56aSColin Percival		echo "done."
187548ffe56aSColin Percival	fi
187648ffe56aSColin Percival
187748ffe56aSColin Percival	# Download files which couldn't be generate via patching
187848ffe56aSColin Percival	while read Y; do
187948ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
188048ffe56aSColin Percival			echo ${Y};
188148ffe56aSColin Percival		fi
188248ffe56aSColin Percival	done < files.wanted > filelist
188348ffe56aSColin Percival
188448ffe56aSColin Percival	if [ -s filelist ]; then
188548ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
188648ffe56aSColin Percival		echo ${NDEBUG} "files... "
188748ffe56aSColin Percival		lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist |
188848ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
188948ffe56aSColin Percival		    2>${QUIETREDIR}
189048ffe56aSColin Percival
189148ffe56aSColin Percival		while read Y; do
189248ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
189348ffe56aSColin Percival				echo "failed."
189448ffe56aSColin Percival				return 1
189548ffe56aSColin Percival			fi
189648ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
189748ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
189848ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
189948ffe56aSColin Percival			else
190048ffe56aSColin Percival				echo "${Y} has incorrect hash."
190148ffe56aSColin Percival				return 1
190248ffe56aSColin Percival			fi
190348ffe56aSColin Percival		done < filelist
190448ffe56aSColin Percival		echo "done."
190548ffe56aSColin Percival	fi
190648ffe56aSColin Percival
190748ffe56aSColin Percival	# Clean up
190848ffe56aSColin Percival	rm files.wanted filelist patchlist
190948ffe56aSColin Percival}
191048ffe56aSColin Percival
191148ffe56aSColin Percival# Create and populate install manifest directory; and report what updates
191248ffe56aSColin Percival# are available.
191348ffe56aSColin Percivalfetch_create_manifest () {
191448ffe56aSColin Percival	# If we have an existing install manifest, nuke it.
191548ffe56aSColin Percival	if [ -L "${BDHASH}-install" ]; then
191648ffe56aSColin Percival		rm -r ${BDHASH}-install/
191748ffe56aSColin Percival		rm ${BDHASH}-install
191848ffe56aSColin Percival	fi
191948ffe56aSColin Percival
192048ffe56aSColin Percival	# Report to the user if any updates were avoided due to local changes
192148ffe56aSColin Percival	if [ -s modifiedfiles ]; then
192248ffe56aSColin Percival		echo
192348ffe56aSColin Percival		echo -n "The following files are affected by updates, "
192448ffe56aSColin Percival		echo "but no changes have"
192548ffe56aSColin Percival		echo -n "been downloaded because the files have been "
192648ffe56aSColin Percival		echo "modified locally:"
192748ffe56aSColin Percival		cat modifiedfiles
19289c990fb2SGordon Tetlow	fi | $PAGER
192948ffe56aSColin Percival	rm modifiedfiles
193048ffe56aSColin Percival
193148ffe56aSColin Percival	# If no files will be updated, tell the user and exit
193248ffe56aSColin Percival	if ! [ -s INDEX-PRESENT ] &&
193348ffe56aSColin Percival	    ! [ -s INDEX-NEW ]; then
193448ffe56aSColin Percival		rm INDEX-PRESENT INDEX-NEW
193548ffe56aSColin Percival		echo
193648ffe56aSColin Percival		echo -n "No updates needed to update system to "
193748ffe56aSColin Percival		echo "${RELNUM}-p${RELPATCHNUM}."
193848ffe56aSColin Percival		return
193948ffe56aSColin Percival	fi
194048ffe56aSColin Percival
194148ffe56aSColin Percival	# Divide files into (a) removed files, (b) added files, and
194248ffe56aSColin Percival	# (c) updated files.
194348ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-PRESENT |
194448ffe56aSColin Percival	    sort > INDEX-PRESENT.flist
194548ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-NEW |
194648ffe56aSColin Percival	    sort > INDEX-NEW.flist
194748ffe56aSColin Percival	comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed
194848ffe56aSColin Percival	comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added
194948ffe56aSColin Percival	comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated
195048ffe56aSColin Percival	rm INDEX-PRESENT.flist INDEX-NEW.flist
195148ffe56aSColin Percival
195248ffe56aSColin Percival	# Report removed files, if any
195348ffe56aSColin Percival	if [ -s files.removed ]; then
195448ffe56aSColin Percival		echo
195548ffe56aSColin Percival		echo -n "The following files will be removed "
195648ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
195748ffe56aSColin Percival		cat files.removed
19589c990fb2SGordon Tetlow	fi | $PAGER
195948ffe56aSColin Percival	rm files.removed
196048ffe56aSColin Percival
196148ffe56aSColin Percival	# Report added files, if any
196248ffe56aSColin Percival	if [ -s files.added ]; then
196348ffe56aSColin Percival		echo
196448ffe56aSColin Percival		echo -n "The following files will be added "
196548ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
196648ffe56aSColin Percival		cat files.added
19679c990fb2SGordon Tetlow	fi | $PAGER
196848ffe56aSColin Percival	rm files.added
196948ffe56aSColin Percival
197048ffe56aSColin Percival	# Report updated files, if any
197148ffe56aSColin Percival	if [ -s files.updated ]; then
197248ffe56aSColin Percival		echo
197348ffe56aSColin Percival		echo -n "The following files will be updated "
197448ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
197548ffe56aSColin Percival
197648ffe56aSColin Percival		cat files.updated
19779c990fb2SGordon Tetlow	fi | $PAGER
197848ffe56aSColin Percival	rm files.updated
197948ffe56aSColin Percival
198048ffe56aSColin Percival	# Create a directory for the install manifest.
198148ffe56aSColin Percival	MDIR=`mktemp -d install.XXXXXX` || return 1
198248ffe56aSColin Percival
198348ffe56aSColin Percival	# Populate it
198448ffe56aSColin Percival	mv INDEX-PRESENT ${MDIR}/INDEX-OLD
198548ffe56aSColin Percival	mv INDEX-NEW ${MDIR}/INDEX-NEW
198648ffe56aSColin Percival
198748ffe56aSColin Percival	# Link it into place
198848ffe56aSColin Percival	ln -s ${MDIR} ${BDHASH}-install
198948ffe56aSColin Percival}
199048ffe56aSColin Percival
199148ffe56aSColin Percival# Warn about any upcoming EoL
199248ffe56aSColin Percivalfetch_warn_eol () {
199348ffe56aSColin Percival	# What's the current time?
199448ffe56aSColin Percival	NOWTIME=`date "+%s"`
199548ffe56aSColin Percival
199648ffe56aSColin Percival	# When did we last warn about the EoL date?
199748ffe56aSColin Percival	if [ -f lasteolwarn ]; then
199848ffe56aSColin Percival		LASTWARN=`cat lasteolwarn`
199948ffe56aSColin Percival	else
200048ffe56aSColin Percival		LASTWARN=`expr ${NOWTIME} - 63072000`
200148ffe56aSColin Percival	fi
200248ffe56aSColin Percival
200348ffe56aSColin Percival	# If the EoL time is past, warn.
200448ffe56aSColin Percival	if [ ${EOLTIME} -lt ${NOWTIME} ]; then
200548ffe56aSColin Percival		echo
200648ffe56aSColin Percival		cat <<-EOF
2007b698a3abSColin Percival		WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE.
200848ffe56aSColin Percival		Any security issues discovered after `date -r ${EOLTIME}`
200948ffe56aSColin Percival		will not have been corrected.
201048ffe56aSColin Percival		EOF
201148ffe56aSColin Percival		return 1
201248ffe56aSColin Percival	fi
201348ffe56aSColin Percival
201448ffe56aSColin Percival	# Figure out how long it has been since we last warned about the
201548ffe56aSColin Percival	# upcoming EoL, and how much longer we have left.
201648ffe56aSColin Percival	SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}`
201748ffe56aSColin Percival	TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}`
201848ffe56aSColin Percival
201989b14566SColin Percival	# Don't warn if the EoL is more than 3 months away
202089b14566SColin Percival	if [ ${TIMELEFT} -gt 7884000 ]; then
202148ffe56aSColin Percival		return 0
202248ffe56aSColin Percival	fi
202348ffe56aSColin Percival
202448ffe56aSColin Percival	# Don't warn if the time remaining is more than 3 times the time
202548ffe56aSColin Percival	# since the last warning.
202648ffe56aSColin Percival	if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then
202748ffe56aSColin Percival		return 0
202848ffe56aSColin Percival	fi
202948ffe56aSColin Percival
203048ffe56aSColin Percival	# Figure out what time units to use.
203148ffe56aSColin Percival	if [ ${TIMELEFT} -lt 604800 ]; then
203248ffe56aSColin Percival		UNIT="day"
203348ffe56aSColin Percival		SIZE=86400
203448ffe56aSColin Percival	elif [ ${TIMELEFT} -lt 2678400 ]; then
203548ffe56aSColin Percival		UNIT="week"
203648ffe56aSColin Percival		SIZE=604800
203748ffe56aSColin Percival	else
203848ffe56aSColin Percival		UNIT="month"
203948ffe56aSColin Percival		SIZE=2678400
204048ffe56aSColin Percival	fi
204148ffe56aSColin Percival
204248ffe56aSColin Percival	# Compute the right number of units
204348ffe56aSColin Percival	NUM=`expr ${TIMELEFT} / ${SIZE}`
204448ffe56aSColin Percival	if [ ${NUM} != 1 ]; then
204548ffe56aSColin Percival		UNIT="${UNIT}s"
204648ffe56aSColin Percival	fi
204748ffe56aSColin Percival
204848ffe56aSColin Percival	# Print the warning
204948ffe56aSColin Percival	echo
205048ffe56aSColin Percival	cat <<-EOF
205148ffe56aSColin Percival		WARNING: `uname -sr` is approaching its End-of-Life date.
205248ffe56aSColin Percival		It is strongly recommended that you upgrade to a newer
205348ffe56aSColin Percival		release within the next ${NUM} ${UNIT}.
205448ffe56aSColin Percival	EOF
205548ffe56aSColin Percival
205648ffe56aSColin Percival	# Update the stored time of last warning
205748ffe56aSColin Percival	echo ${NOWTIME} > lasteolwarn
205848ffe56aSColin Percival}
205948ffe56aSColin Percival
206048ffe56aSColin Percival# Do the actual work involved in "fetch" / "cron".
206148ffe56aSColin Percivalfetch_run () {
206248ffe56aSColin Percival	workdir_init || return 1
206348ffe56aSColin Percival
206448ffe56aSColin Percival	# Prepare the mirror list.
206548ffe56aSColin Percival	fetch_pick_server_init && fetch_pick_server
206648ffe56aSColin Percival
206748ffe56aSColin Percival	# Try to fetch the public key until we run out of servers.
206848ffe56aSColin Percival	while ! fetch_key; do
206948ffe56aSColin Percival		fetch_pick_server || return 1
207048ffe56aSColin Percival	done
207148ffe56aSColin Percival
207248ffe56aSColin Percival	# Try to fetch the metadata index signature ("tag") until we run
207348ffe56aSColin Percival	# out of available servers; and sanity check the downloaded tag.
207448ffe56aSColin Percival	while ! fetch_tag; do
207548ffe56aSColin Percival		fetch_pick_server || return 1
207648ffe56aSColin Percival	done
207748ffe56aSColin Percival	fetch_tagsanity || return 1
207848ffe56aSColin Percival
207948ffe56aSColin Percival	# Fetch the latest INDEX-NEW and INDEX-OLD files.
208048ffe56aSColin Percival	fetch_metadata INDEX-NEW INDEX-OLD || return 1
208148ffe56aSColin Percival
208248ffe56aSColin Percival	# Generate filtered INDEX-NEW and INDEX-OLD files containing only
208348ffe56aSColin Percival	# the lines which (a) belong to components we care about, and (b)
208448ffe56aSColin Percival	# don't correspond to paths we're explicitly ignoring.
208548ffe56aSColin Percival	fetch_filter_metadata INDEX-NEW || return 1
208648ffe56aSColin Percival	fetch_filter_metadata INDEX-OLD || return 1
208748ffe56aSColin Percival
2088db6b0a61SColin Percival	# Translate /boot/${KERNCONF} into ${KERNELDIR}
2089db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-NEW ${KERNCONF}
2090db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
209148ffe56aSColin Percival
209248ffe56aSColin Percival	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
209348ffe56aSColin Percival	# system and generate an INDEX-PRESENT file.
209448ffe56aSColin Percival	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
209548ffe56aSColin Percival
209648ffe56aSColin Percival	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
209748ffe56aSColin Percival	# correspond to lines in INDEX-PRESENT with hashes not appearing
209848ffe56aSColin Percival	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
209948ffe56aSColin Percival	# INDEX-PRESENT has type - and there isn't a corresponding entry in
210048ffe56aSColin Percival	# INDEX-OLD with type -.
2101db6b0a61SColin Percival	fetch_filter_unmodified_notpresent	\
2102db6b0a61SColin Percival	    INDEX-OLD INDEX-PRESENT INDEX-NEW /dev/null
210348ffe56aSColin Percival
210448ffe56aSColin Percival	# For each entry in INDEX-PRESENT of type -, remove any corresponding
210548ffe56aSColin Percival	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
210648ffe56aSColin Percival	# of type - from INDEX-PRESENT.
210748ffe56aSColin Percival	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
210848ffe56aSColin Percival
210948ffe56aSColin Percival	# If ${ALLOWDELETE} != "yes", then remove any entries from
211048ffe56aSColin Percival	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
211148ffe56aSColin Percival	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
211248ffe56aSColin Percival
211348ffe56aSColin Percival	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
211448ffe56aSColin Percival	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
211548ffe56aSColin Percival	# replace the corresponding line of INDEX-NEW with one having the
211648ffe56aSColin Percival	# same metadata as the entry in INDEX-PRESENT.
211748ffe56aSColin Percival	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
211848ffe56aSColin Percival
211948ffe56aSColin Percival	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
212048ffe56aSColin Percival	# no need to update a file if it isn't changing.
212148ffe56aSColin Percival	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
212248ffe56aSColin Percival
212348ffe56aSColin Percival	# Prepare to fetch files: Generate a list of the files we need,
212448ffe56aSColin Percival	# copy the unmodified files we have into /files/, and generate
212548ffe56aSColin Percival	# a list of patches to download.
2126210b8123SColin Percival	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
212748ffe56aSColin Percival
212848ffe56aSColin Percival	# Fetch files.
212948ffe56aSColin Percival	fetch_files || return 1
213048ffe56aSColin Percival
213148ffe56aSColin Percival	# Create and populate install manifest directory; and report what
213248ffe56aSColin Percival	# updates are available.
213348ffe56aSColin Percival	fetch_create_manifest || return 1
213448ffe56aSColin Percival
213548ffe56aSColin Percival	# Warn about any upcoming EoL
213648ffe56aSColin Percival	fetch_warn_eol || return 1
213748ffe56aSColin Percival}
213848ffe56aSColin Percival
2139db6b0a61SColin Percival# If StrictComponents is not "yes", generate a new components list
2140db6b0a61SColin Percival# with only the components which appear to be installed.
2141db6b0a61SColin Percivalupgrade_guess_components () {
2142db6b0a61SColin Percival	if [ "${STRICTCOMPONENTS}" = "no" ]; then
2143db6b0a61SColin Percival		# Generate filtered INDEX-ALL with only the components listed
2144db6b0a61SColin Percival		# in COMPONENTS.
2145db6b0a61SColin Percival		fetch_filter_metadata_components $1 || return 1
2146db6b0a61SColin Percival
2147db6b0a61SColin Percival		# Tell the user why his disk is suddenly making lots of noise
2148db6b0a61SColin Percival		echo -n "Inspecting system... "
2149db6b0a61SColin Percival
2150db6b0a61SColin Percival		# Look at the files on disk, and assume that a component is
2151db6b0a61SColin Percival		# supposed to be present if it is more than half-present.
2152db6b0a61SColin Percival		cut -f 1-3 -d '|' < INDEX-ALL |
2153db6b0a61SColin Percival		    tr '|' ' ' |
2154db6b0a61SColin Percival		    while read C S F; do
2155db6b0a61SColin Percival			if [ -e ${BASEDIR}/${F} ]; then
2156db6b0a61SColin Percival				echo "+ ${C}|${S}"
2157db6b0a61SColin Percival			fi
2158db6b0a61SColin Percival			echo "= ${C}|${S}"
2159db6b0a61SColin Percival		    done |
2160db6b0a61SColin Percival		    sort |
2161db6b0a61SColin Percival		    uniq -c |
2162db6b0a61SColin Percival		    sed -E 's,^ +,,' > compfreq
2163db6b0a61SColin Percival		grep ' = ' compfreq |
2164db6b0a61SColin Percival		    cut -f 1,3 -d ' ' |
2165db6b0a61SColin Percival		    sort -k 2,2 -t ' ' > compfreq.total
2166db6b0a61SColin Percival		grep ' + ' compfreq |
2167db6b0a61SColin Percival		    cut -f 1,3 -d ' ' |
2168db6b0a61SColin Percival		    sort -k 2,2 -t ' ' > compfreq.present
2169db6b0a61SColin Percival		join -t ' ' -1 2 -2 2 compfreq.present compfreq.total |
2170db6b0a61SColin Percival		    while read S P T; do
2171db6b0a61SColin Percival			if [ ${P} -gt `expr ${T} / 2` ]; then
2172db6b0a61SColin Percival				echo ${S}
2173db6b0a61SColin Percival			fi
2174db6b0a61SColin Percival		    done > comp.present
2175db6b0a61SColin Percival		cut -f 2 -d ' ' < compfreq.total > comp.total
2176db6b0a61SColin Percival		rm INDEX-ALL compfreq compfreq.total compfreq.present
2177db6b0a61SColin Percival
2178db6b0a61SColin Percival		# We're done making noise.
2179db6b0a61SColin Percival		echo "done."
2180db6b0a61SColin Percival
2181db6b0a61SColin Percival		# Sometimes the kernel isn't installed where INDEX-ALL
2182db6b0a61SColin Percival		# thinks that it should be: In particular, it is often in
2183db6b0a61SColin Percival		# /boot/kernel instead of /boot/GENERIC or /boot/SMP.  To
2184db6b0a61SColin Percival		# deal with this, if "kernel|X" is listed in comp.total
2185db6b0a61SColin Percival		# (i.e., is a component which would be upgraded if it is
2186db6b0a61SColin Percival		# found to be present) we will add it to comp.present.
2187db6b0a61SColin Percival		# If "kernel|<anything>" is in comp.total but "kernel|X" is
2188db6b0a61SColin Percival		# not, we print a warning -- the user is running a kernel
2189db6b0a61SColin Percival		# which isn't part of the release.
2190db6b0a61SColin Percival		KCOMP=`echo ${KERNCONF} | tr 'A-Z' 'a-z'`
2191db6b0a61SColin Percival		grep -E "^kernel\|${KCOMP}\$" comp.total >> comp.present
2192db6b0a61SColin Percival
2193db6b0a61SColin Percival		if grep -qE "^kernel\|" comp.total &&
2194db6b0a61SColin Percival		    ! grep -qE "^kernel\|${KCOMP}\$" comp.total; then
2195db6b0a61SColin Percival			cat <<-EOF
2196db6b0a61SColin Percival
2197db6b0a61SColin PercivalWARNING: This system is running a "${KCOMP}" kernel, which is not a
2198db6b0a61SColin Percivalkernel configuration distributed as part of FreeBSD ${RELNUM}.
2199db6b0a61SColin PercivalThis kernel will not be updated: you MUST update the kernel manually
2200db6b0a61SColin Percivalbefore running "$0 install".
2201db6b0a61SColin Percival			EOF
2202db6b0a61SColin Percival		fi
2203db6b0a61SColin Percival
2204db6b0a61SColin Percival		# Re-sort the list of installed components and generate
2205db6b0a61SColin Percival		# the list of non-installed components.
2206db6b0a61SColin Percival		sort -u < comp.present > comp.present.tmp
2207db6b0a61SColin Percival		mv comp.present.tmp comp.present
2208db6b0a61SColin Percival		comm -13 comp.present comp.total > comp.absent
2209db6b0a61SColin Percival
2210db6b0a61SColin Percival		# Ask the user to confirm that what we have is correct.  To
2211db6b0a61SColin Percival		# reduce user confusion, translate "X|Y" back to "X/Y" (as
2212db6b0a61SColin Percival		# subcomponents must be listed in the configuration file).
2213db6b0a61SColin Percival		echo
2214db6b0a61SColin Percival		echo -n "The following components of FreeBSD "
2215db6b0a61SColin Percival		echo "seem to be installed:"
2216db6b0a61SColin Percival		tr '|' '/' < comp.present |
2217db6b0a61SColin Percival		    fmt -72
2218db6b0a61SColin Percival		echo
2219db6b0a61SColin Percival		echo -n "The following components of FreeBSD "
2220db6b0a61SColin Percival		echo "do not seem to be installed:"
2221db6b0a61SColin Percival		tr '|' '/' < comp.absent |
2222db6b0a61SColin Percival		    fmt -72
2223db6b0a61SColin Percival		echo
2224db6b0a61SColin Percival		continuep || return 1
2225db6b0a61SColin Percival		echo
2226db6b0a61SColin Percival
2227db6b0a61SColin Percival		# Suck the generated list of components into ${COMPONENTS}.
2228db6b0a61SColin Percival		# Note that comp.present.tmp is used due to issues with
2229db6b0a61SColin Percival		# pipelines and setting variables.
2230db6b0a61SColin Percival		COMPONENTS=""
2231db6b0a61SColin Percival		tr '|' '/' < comp.present > comp.present.tmp
2232db6b0a61SColin Percival		while read C; do
2233db6b0a61SColin Percival			COMPONENTS="${COMPONENTS} ${C}"
2234db6b0a61SColin Percival		done < comp.present.tmp
2235db6b0a61SColin Percival
2236db6b0a61SColin Percival		# Delete temporary files
2237db6b0a61SColin Percival		rm comp.present comp.present.tmp comp.absent comp.total
2238db6b0a61SColin Percival	fi
2239db6b0a61SColin Percival}
2240db6b0a61SColin Percival
2241db6b0a61SColin Percival# If StrictComponents is not "yes", COMPONENTS contains an entry
2242db6b0a61SColin Percival# corresponding to the currently running kernel, and said kernel
2243db6b0a61SColin Percival# does not exist in the new release, add "kernel/generic" to the
2244db6b0a61SColin Percival# list of components.
2245db6b0a61SColin Percivalupgrade_guess_new_kernel () {
2246db6b0a61SColin Percival	if [ "${STRICTCOMPONENTS}" = "no" ]; then
2247db6b0a61SColin Percival		# Grab the unfiltered metadata file.
2248db6b0a61SColin Percival		METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
2249db6b0a61SColin Percival		gunzip -c < files/${METAHASH}.gz > $1.all
2250db6b0a61SColin Percival
2251db6b0a61SColin Percival		# If "kernel/${KCOMP}" is in ${COMPONENTS} and that component
2252db6b0a61SColin Percival		# isn't in $1.all, we need to add kernel/generic.
2253db6b0a61SColin Percival		for C in ${COMPONENTS}; do
2254db6b0a61SColin Percival			if [ ${C} = "kernel/${KCOMP}" ] &&
2255db6b0a61SColin Percival			    ! grep -qE "^kernel\|${KCOMP}\|" $1.all; then
2256db6b0a61SColin Percival				COMPONENTS="${COMPONENTS} kernel/generic"
2257db6b0a61SColin Percival				NKERNCONF="GENERIC"
2258db6b0a61SColin Percival				cat <<-EOF
2259db6b0a61SColin Percival
2260db6b0a61SColin PercivalWARNING: This system is running a "${KCOMP}" kernel, which is not a
2261db6b0a61SColin Percivalkernel configuration distributed as part of FreeBSD ${RELNUM}.
2262db6b0a61SColin PercivalAs part of upgrading to FreeBSD ${RELNUM}, this kernel will be
2263db6b0a61SColin Percivalreplaced with a "generic" kernel.
2264db6b0a61SColin Percival				EOF
2265db6b0a61SColin Percival				continuep || return 1
2266db6b0a61SColin Percival			fi
2267db6b0a61SColin Percival		done
2268db6b0a61SColin Percival
2269db6b0a61SColin Percival		# Don't need this any more...
2270db6b0a61SColin Percival		rm $1.all
2271db6b0a61SColin Percival	fi
2272db6b0a61SColin Percival}
2273db6b0a61SColin Percival
2274db6b0a61SColin Percival# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2275db6b0a61SColin Percival# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2276db6b0a61SColin Percivalupgrade_oldall_to_oldnew () {
2277db6b0a61SColin Percival	# For each ${F}|... which appears in INDEX-ALL but does not appear
2278db6b0a61SColin Percival	# in INDEX-OLD, add ${F}|-|||||| to INDEX-OLD.
2279db6b0a61SColin Percival	cut -f 1 -d '|' < $1 |
2280db6b0a61SColin Percival	    sort -u > $1.paths
2281db6b0a61SColin Percival	cut -f 1 -d '|' < $2 |
2282db6b0a61SColin Percival	    sort -u |
2283db6b0a61SColin Percival	    comm -13 $1.paths - |
2284db6b0a61SColin Percival	    lam - -s "|-||||||" |
2285db6b0a61SColin Percival	    sort - $1 > $1.tmp
2286db6b0a61SColin Percival	mv $1.tmp $1
2287db6b0a61SColin Percival
2288db6b0a61SColin Percival	# Remove lines from INDEX-OLD which also appear in INDEX-ALL
2289db6b0a61SColin Percival	comm -23 $1 $2 > $1.tmp
2290db6b0a61SColin Percival	mv $1.tmp $1
2291db6b0a61SColin Percival
2292db6b0a61SColin Percival	# Remove lines from INDEX-ALL which have a file name not appearing
2293db6b0a61SColin Percival	# anywhere in INDEX-OLD (since these must be files which haven't
2294db6b0a61SColin Percival	# changed -- if they were new, there would be an entry of type "-").
2295db6b0a61SColin Percival	cut -f 1 -d '|' < $1 |
2296db6b0a61SColin Percival	    sort -u > $1.paths
2297db6b0a61SColin Percival	sort -k 1,1 -t '|' < $2 |
2298db6b0a61SColin Percival	    join -t '|' - $1.paths |
2299db6b0a61SColin Percival	    sort > $2.tmp
2300db6b0a61SColin Percival	rm $1.paths
2301db6b0a61SColin Percival	mv $2.tmp $2
2302db6b0a61SColin Percival
2303db6b0a61SColin Percival	# Rename INDEX-ALL to INDEX-NEW.
2304db6b0a61SColin Percival	mv $2 $3
2305db6b0a61SColin Percival}
2306db6b0a61SColin Percival
23077449d2f5SColin Percival# Helper for upgrade_merge: Return zero true iff the two files differ only
23086d514f10SDag-Erling Smørgrav# in the contents of their RCS tags.
23097449d2f5SColin Percivalsamef () {
23107449d2f5SColin Percival	X=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $1 | ${SHA256}`
23117449d2f5SColin Percival	Y=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $2 | ${SHA256}`
23127449d2f5SColin Percival
23137449d2f5SColin Percival	if [ $X = $Y ]; then
23147449d2f5SColin Percival		return 0;
23157449d2f5SColin Percival	else
23167449d2f5SColin Percival		return 1;
23177449d2f5SColin Percival	fi
23187449d2f5SColin Percival}
23197449d2f5SColin Percival
2320db6b0a61SColin Percival# From the list of "old" files in $1, merge changes in $2 with those in $3,
2321db6b0a61SColin Percival# and update $3 to reflect the hashes of merged files.
2322db6b0a61SColin Percivalupgrade_merge () {
2323db6b0a61SColin Percival	# We only need to do anything if $1 is non-empty.
2324db6b0a61SColin Percival	if [ -s $1 ]; then
2325db6b0a61SColin Percival		cut -f 1 -d '|' $1 |
2326db6b0a61SColin Percival		    sort > $1-paths
2327db6b0a61SColin Percival
2328db6b0a61SColin Percival		# Create staging area for merging files
2329db6b0a61SColin Percival		rm -rf merge/
2330db6b0a61SColin Percival		while read F; do
2331db6b0a61SColin Percival			D=`dirname ${F}`
2332db6b0a61SColin Percival			mkdir -p merge/old/${D}
2333db6b0a61SColin Percival			mkdir -p merge/${OLDRELNUM}/${D}
2334db6b0a61SColin Percival			mkdir -p merge/${RELNUM}/${D}
2335db6b0a61SColin Percival			mkdir -p merge/new/${D}
2336db6b0a61SColin Percival		done < $1-paths
2337db6b0a61SColin Percival
2338db6b0a61SColin Percival		# Copy in files
2339db6b0a61SColin Percival		while read F; do
2340db6b0a61SColin Percival			# Currently installed file
2341db6b0a61SColin Percival			V=`look "${F}|" $2 | cut -f 7 -d '|'`
2342db6b0a61SColin Percival			gunzip < files/${V}.gz > merge/old/${F}
2343db6b0a61SColin Percival
2344db6b0a61SColin Percival			# Old release
2345db6b0a61SColin Percival			if look "${F}|" $1 | fgrep -q "|f|"; then
2346db6b0a61SColin Percival				V=`look "${F}|" $1 | cut -f 3 -d '|'`
2347db6b0a61SColin Percival				gunzip < files/${V}.gz		\
2348db6b0a61SColin Percival				    > merge/${OLDRELNUM}/${F}
2349db6b0a61SColin Percival			fi
2350db6b0a61SColin Percival
2351db6b0a61SColin Percival			# New release
2352db6b0a61SColin Percival			if look "${F}|" $3 | cut -f 1,2,7 -d '|' |
2353db6b0a61SColin Percival			    fgrep -q "|f|"; then
2354db6b0a61SColin Percival				V=`look "${F}|" $3 | cut -f 7 -d '|'`
2355db6b0a61SColin Percival				gunzip < files/${V}.gz		\
2356db6b0a61SColin Percival				    > merge/${RELNUM}/${F}
2357db6b0a61SColin Percival			fi
2358db6b0a61SColin Percival		done < $1-paths
2359db6b0a61SColin Percival
2360db6b0a61SColin Percival		# Attempt to automatically merge changes
2361db6b0a61SColin Percival		echo -n "Attempting to automatically merge "
2362db6b0a61SColin Percival		echo -n "changes in files..."
2363db6b0a61SColin Percival		: > failed.merges
2364db6b0a61SColin Percival		while read F; do
2365db6b0a61SColin Percival			# If the file doesn't exist in the new release,
2366db6b0a61SColin Percival			# the result of "merging changes" is having the file
2367db6b0a61SColin Percival			# not exist.
2368db6b0a61SColin Percival			if ! [ -f merge/${RELNUM}/${F} ]; then
2369db6b0a61SColin Percival				continue
2370db6b0a61SColin Percival			fi
2371db6b0a61SColin Percival
2372db6b0a61SColin Percival			# If the file didn't exist in the old release, we're
2373db6b0a61SColin Percival			# going to throw away the existing file and hope that
2374db6b0a61SColin Percival			# the version from the new release is what we want.
2375db6b0a61SColin Percival			if ! [ -f merge/${OLDRELNUM}/${F} ]; then
2376db6b0a61SColin Percival				cp merge/${RELNUM}/${F} merge/new/${F}
2377db6b0a61SColin Percival				continue
2378db6b0a61SColin Percival			fi
2379db6b0a61SColin Percival
2380db6b0a61SColin Percival			# Some files need special treatment.
2381db6b0a61SColin Percival			case ${F} in
2382db6b0a61SColin Percival			/etc/spwd.db | /etc/pwd.db | /etc/login.conf.db)
2383db6b0a61SColin Percival				# Don't merge these -- we're rebuild them
2384db6b0a61SColin Percival				# after updates are installed.
2385db6b0a61SColin Percival				cp merge/old/${F} merge/new/${F}
2386db6b0a61SColin Percival				;;
2387db6b0a61SColin Percival			*)
2388073dd712SBaptiste Daroussin				if ! diff3 -E -m -L "current version"	\
2389db6b0a61SColin Percival				    -L "${OLDRELNUM}" -L "${RELNUM}"	\
2390db6b0a61SColin Percival				    merge/old/${F}			\
2391db6b0a61SColin Percival				    merge/${OLDRELNUM}/${F}		\
2392db6b0a61SColin Percival				    merge/${RELNUM}/${F}		\
2393db6b0a61SColin Percival				    > merge/new/${F} 2>/dev/null; then
2394db6b0a61SColin Percival					echo ${F} >> failed.merges
2395db6b0a61SColin Percival				fi
2396db6b0a61SColin Percival				;;
2397db6b0a61SColin Percival			esac
2398db6b0a61SColin Percival		done < $1-paths
2399db6b0a61SColin Percival		echo " done."
2400db6b0a61SColin Percival
2401db6b0a61SColin Percival		# Ask the user to handle any files which didn't merge.
2402db6b0a61SColin Percival		while read F; do
24037449d2f5SColin Percival			# If the installed file differs from the version in
24046d514f10SDag-Erling Smørgrav			# the old release only due to RCS tag expansion
24057449d2f5SColin Percival			# then just use the version in the new release.
24067449d2f5SColin Percival			if samef merge/old/${F} merge/${OLDRELNUM}/${F}; then
24077449d2f5SColin Percival				cp merge/${RELNUM}/${F} merge/new/${F}
24087449d2f5SColin Percival				continue
24097449d2f5SColin Percival			fi
24107449d2f5SColin Percival
2411db6b0a61SColin Percival			cat <<-EOF
2412db6b0a61SColin Percival
2413db6b0a61SColin PercivalThe following file could not be merged automatically: ${F}
2414db6b0a61SColin PercivalPress Enter to edit this file in ${EDITOR} and resolve the conflicts
2415db6b0a61SColin Percivalmanually...
2416db6b0a61SColin Percival			EOF
2417db6b0a61SColin Percival			read dummy </dev/tty
2418db6b0a61SColin Percival			${EDITOR} `pwd`/merge/new/${F} < /dev/tty
2419db6b0a61SColin Percival		done < failed.merges
2420db6b0a61SColin Percival		rm failed.merges
2421db6b0a61SColin Percival
2422db6b0a61SColin Percival		# Ask the user to confirm that he likes how the result
2423db6b0a61SColin Percival		# of merging files.
2424db6b0a61SColin Percival		while read F; do
24257449d2f5SColin Percival			# Skip files which haven't changed except possibly
24266d514f10SDag-Erling Smørgrav			# in their RCS tags.
24277449d2f5SColin Percival			if [ -f merge/old/${F} ] && [ -f merge/new/${F} ] &&
24287449d2f5SColin Percival			    samef merge/old/${F} merge/new/${F}; then
24297449d2f5SColin Percival				continue
24307449d2f5SColin Percival			fi
24317449d2f5SColin Percival
24327449d2f5SColin Percival			# Skip files where the installed file differs from
24336d514f10SDag-Erling Smørgrav			# the old file only due to RCS tags.
24347449d2f5SColin Percival			if [ -f merge/old/${F} ] &&
24357449d2f5SColin Percival			    [ -f merge/${OLDRELNUM}/${F} ] &&
24367449d2f5SColin Percival			    samef merge/old/${F} merge/${OLDRELNUM}/${F}; then
2437db6b0a61SColin Percival				continue
2438db6b0a61SColin Percival			fi
2439db6b0a61SColin Percival
2440db6b0a61SColin Percival			# Warn about files which are ceasing to exist.
2441db6b0a61SColin Percival			if ! [ -f merge/new/${F} ]; then
2442db6b0a61SColin Percival				cat <<-EOF
2443db6b0a61SColin Percival
2444db6b0a61SColin PercivalThe following file will be removed, as it no longer exists in
2445db6b0a61SColin PercivalFreeBSD ${RELNUM}: ${F}
2446db6b0a61SColin Percival				EOF
2447db6b0a61SColin Percival				continuep < /dev/tty || return 1
2448db6b0a61SColin Percival				continue
2449db6b0a61SColin Percival			fi
2450db6b0a61SColin Percival
2451db6b0a61SColin Percival			# Print changes for the user's approval.
2452db6b0a61SColin Percival			cat <<-EOF
2453db6b0a61SColin Percival
2454db6b0a61SColin PercivalThe following changes, which occurred between FreeBSD ${OLDRELNUM} and
2455db6b0a61SColin PercivalFreeBSD ${RELNUM} have been merged into ${F}:
2456db6b0a61SColin PercivalEOF
2457db6b0a61SColin Percival			diff -U 5 -L "current version" -L "new version"	\
2458db6b0a61SColin Percival			    merge/old/${F} merge/new/${F} || true
2459db6b0a61SColin Percival			continuep < /dev/tty || return 1
2460db6b0a61SColin Percival		done < $1-paths
2461db6b0a61SColin Percival
2462db6b0a61SColin Percival		# Store merged files.
2463db6b0a61SColin Percival		while read F; do
2464c58b62efSColin Percival			if [ -f merge/new/${F} ]; then
2465db6b0a61SColin Percival				V=`${SHA256} -q merge/new/${F}`
2466db6b0a61SColin Percival
2467db6b0a61SColin Percival				gzip -c < merge/new/${F} > files/${V}.gz
2468db6b0a61SColin Percival				echo "${F}|${V}"
2469db6b0a61SColin Percival			fi
2470db6b0a61SColin Percival		done < $1-paths > newhashes
2471db6b0a61SColin Percival
2472db6b0a61SColin Percival		# Pull lines out from $3 which need to be updated to
2473db6b0a61SColin Percival		# reflect merged files.
2474db6b0a61SColin Percival		while read F; do
2475db6b0a61SColin Percival			look "${F}|" $3
2476db6b0a61SColin Percival		done < $1-paths > $3-oldlines
2477db6b0a61SColin Percival
2478db6b0a61SColin Percival		# Update lines to reflect merged files
2479db6b0a61SColin Percival		join -t '|' -o 1.1,1.2,1.3,1.4,1.5,1.6,2.2,1.8		\
2480db6b0a61SColin Percival		    $3-oldlines newhashes > $3-newlines
2481db6b0a61SColin Percival
2482db6b0a61SColin Percival		# Remove old lines from $3 and add new lines.
2483db6b0a61SColin Percival		sort $3-oldlines |
2484db6b0a61SColin Percival		    comm -13 - $3 |
2485db6b0a61SColin Percival		    sort - $3-newlines > $3.tmp
2486db6b0a61SColin Percival		mv $3.tmp $3
2487db6b0a61SColin Percival
2488db6b0a61SColin Percival		# Clean up
2489db6b0a61SColin Percival		rm $1-paths newhashes $3-oldlines $3-newlines
2490db6b0a61SColin Percival		rm -rf merge/
2491db6b0a61SColin Percival	fi
2492db6b0a61SColin Percival
2493db6b0a61SColin Percival	# We're done with merging files.
2494db6b0a61SColin Percival	rm $1
2495db6b0a61SColin Percival}
2496db6b0a61SColin Percival
2497db6b0a61SColin Percival# Do the work involved in fetching upgrades to a new release
2498db6b0a61SColin Percivalupgrade_run () {
2499db6b0a61SColin Percival	workdir_init || return 1
2500db6b0a61SColin Percival
2501db6b0a61SColin Percival	# Prepare the mirror list.
2502db6b0a61SColin Percival	fetch_pick_server_init && fetch_pick_server
2503db6b0a61SColin Percival
2504db6b0a61SColin Percival	# Try to fetch the public key until we run out of servers.
2505db6b0a61SColin Percival	while ! fetch_key; do
2506db6b0a61SColin Percival		fetch_pick_server || return 1
2507db6b0a61SColin Percival	done
2508db6b0a61SColin Percival
2509db6b0a61SColin Percival	# Try to fetch the metadata index signature ("tag") until we run
2510db6b0a61SColin Percival	# out of available servers; and sanity check the downloaded tag.
2511db6b0a61SColin Percival	while ! fetch_tag; do
2512db6b0a61SColin Percival		fetch_pick_server || return 1
2513db6b0a61SColin Percival	done
2514db6b0a61SColin Percival	fetch_tagsanity || return 1
2515db6b0a61SColin Percival
2516db6b0a61SColin Percival	# Fetch the INDEX-OLD and INDEX-ALL.
2517db6b0a61SColin Percival	fetch_metadata INDEX-OLD INDEX-ALL || return 1
2518db6b0a61SColin Percival
2519db6b0a61SColin Percival	# If StrictComponents is not "yes", generate a new components list
2520db6b0a61SColin Percival	# with only the components which appear to be installed.
2521db6b0a61SColin Percival	upgrade_guess_components INDEX-ALL || return 1
2522db6b0a61SColin Percival
2523db6b0a61SColin Percival	# Generate filtered INDEX-OLD and INDEX-ALL files containing only
2524db6b0a61SColin Percival	# the components we want and without anything marked as "Ignore".
2525db6b0a61SColin Percival	fetch_filter_metadata INDEX-OLD || return 1
2526db6b0a61SColin Percival	fetch_filter_metadata INDEX-ALL || return 1
2527db6b0a61SColin Percival
2528db6b0a61SColin Percival	# Merge the INDEX-OLD and INDEX-ALL files into INDEX-OLD.
2529db6b0a61SColin Percival	sort INDEX-OLD INDEX-ALL > INDEX-OLD.tmp
2530db6b0a61SColin Percival	mv INDEX-OLD.tmp INDEX-OLD
2531db6b0a61SColin Percival	rm INDEX-ALL
2532db6b0a61SColin Percival
2533db6b0a61SColin Percival	# Adjust variables for fetching files from the new release.
2534db6b0a61SColin Percival	OLDRELNUM=${RELNUM}
2535db6b0a61SColin Percival	RELNUM=${TARGETRELEASE}
2536db6b0a61SColin Percival	OLDFETCHDIR=${FETCHDIR}
2537db6b0a61SColin Percival	FETCHDIR=${RELNUM}/${ARCH}
2538db6b0a61SColin Percival
2539db6b0a61SColin Percival	# Try to fetch the NEW metadata index signature ("tag") until we run
2540db6b0a61SColin Percival	# out of available servers; and sanity check the downloaded tag.
2541db6b0a61SColin Percival	while ! fetch_tag; do
2542db6b0a61SColin Percival		fetch_pick_server || return 1
2543db6b0a61SColin Percival	done
2544db6b0a61SColin Percival
2545db6b0a61SColin Percival	# Fetch the new INDEX-ALL.
2546db6b0a61SColin Percival	fetch_metadata INDEX-ALL || return 1
2547db6b0a61SColin Percival
2548db6b0a61SColin Percival	# If StrictComponents is not "yes", COMPONENTS contains an entry
2549db6b0a61SColin Percival	# corresponding to the currently running kernel, and said kernel
2550db6b0a61SColin Percival	# does not exist in the new release, add "kernel/generic" to the
2551db6b0a61SColin Percival	# list of components.
2552db6b0a61SColin Percival	upgrade_guess_new_kernel INDEX-ALL || return 1
2553db6b0a61SColin Percival
2554db6b0a61SColin Percival	# Filter INDEX-ALL to contain only the components we want and without
2555db6b0a61SColin Percival	# anything marked as "Ignore".
2556db6b0a61SColin Percival	fetch_filter_metadata INDEX-ALL || return 1
2557db6b0a61SColin Percival
2558db6b0a61SColin Percival	# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2559db6b0a61SColin Percival	# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2560db6b0a61SColin Percival	upgrade_oldall_to_oldnew INDEX-OLD INDEX-ALL INDEX-NEW
2561db6b0a61SColin Percival
2562db6b0a61SColin Percival	# Translate /boot/${KERNCONF} or /boot/${NKERNCONF} into ${KERNELDIR}
2563db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-NEW ${NKERNCONF}
2564db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
2565db6b0a61SColin Percival
2566db6b0a61SColin Percival	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
2567db6b0a61SColin Percival	# system and generate an INDEX-PRESENT file.
2568db6b0a61SColin Percival	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2569db6b0a61SColin Percival
2570db6b0a61SColin Percival	# Based on ${MERGECHANGES}, generate a file tomerge-old with the
2571db6b0a61SColin Percival	# paths and hashes of old versions of files to merge.
2572db6b0a61SColin Percival	fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT tomerge-old
2573db6b0a61SColin Percival
2574db6b0a61SColin Percival	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
2575db6b0a61SColin Percival	# correspond to lines in INDEX-PRESENT with hashes not appearing
2576db6b0a61SColin Percival	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
2577db6b0a61SColin Percival	# INDEX-PRESENT has type - and there isn't a corresponding entry in
2578db6b0a61SColin Percival	# INDEX-OLD with type -.
2579db6b0a61SColin Percival	fetch_filter_unmodified_notpresent	\
2580db6b0a61SColin Percival	    INDEX-OLD INDEX-PRESENT INDEX-NEW tomerge-old
2581db6b0a61SColin Percival
2582db6b0a61SColin Percival	# For each entry in INDEX-PRESENT of type -, remove any corresponding
2583db6b0a61SColin Percival	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
2584db6b0a61SColin Percival	# of type - from INDEX-PRESENT.
2585db6b0a61SColin Percival	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
2586db6b0a61SColin Percival
2587db6b0a61SColin Percival	# If ${ALLOWDELETE} != "yes", then remove any entries from
2588db6b0a61SColin Percival	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
2589db6b0a61SColin Percival	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
2590db6b0a61SColin Percival
2591db6b0a61SColin Percival	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
2592db6b0a61SColin Percival	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
2593db6b0a61SColin Percival	# replace the corresponding line of INDEX-NEW with one having the
2594db6b0a61SColin Percival	# same metadata as the entry in INDEX-PRESENT.
2595db6b0a61SColin Percival	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
2596db6b0a61SColin Percival
2597db6b0a61SColin Percival	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
2598db6b0a61SColin Percival	# no need to update a file if it isn't changing.
2599db6b0a61SColin Percival	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
2600db6b0a61SColin Percival
2601db6b0a61SColin Percival	# Fetch "clean" files from the old release for merging changes.
2602db6b0a61SColin Percival	fetch_files_premerge tomerge-old
2603db6b0a61SColin Percival
2604db6b0a61SColin Percival	# Prepare to fetch files: Generate a list of the files we need,
2605db6b0a61SColin Percival	# copy the unmodified files we have into /files/, and generate
2606db6b0a61SColin Percival	# a list of patches to download.
2607db6b0a61SColin Percival	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2608db6b0a61SColin Percival
2609db6b0a61SColin Percival	# Fetch patches from to-${RELNUM}/${ARCH}/bp/
2610db6b0a61SColin Percival	PATCHDIR=to-${RELNUM}/${ARCH}/bp
2611db6b0a61SColin Percival	fetch_files || return 1
2612db6b0a61SColin Percival
2613db6b0a61SColin Percival	# Merge configuration file changes.
2614db6b0a61SColin Percival	upgrade_merge tomerge-old INDEX-PRESENT INDEX-NEW || return 1
2615db6b0a61SColin Percival
2616db6b0a61SColin Percival	# Create and populate install manifest directory; and report what
2617db6b0a61SColin Percival	# updates are available.
2618db6b0a61SColin Percival	fetch_create_manifest || return 1
2619db6b0a61SColin Percival
2620db6b0a61SColin Percival	# Leave a note behind to tell the "install" command that the kernel
2621db6b0a61SColin Percival	# needs to be installed before the world.
2622db6b0a61SColin Percival	touch ${BDHASH}-install/kernelfirst
262385451f90SColin Percival
262485451f90SColin Percival	# Remind the user that they need to run "freebsd-update install"
262585451f90SColin Percival	# to install the downloaded bits, in case they didn't RTFM.
262685451f90SColin Percival	echo "To install the downloaded upgrades, run \"$0 install\"."
2627db6b0a61SColin Percival}
2628db6b0a61SColin Percival
262948ffe56aSColin Percival# Make sure that all the file hashes mentioned in $@ have corresponding
263048ffe56aSColin Percival# gzipped files stored in /files/.
263148ffe56aSColin Percivalinstall_verify () {
263248ffe56aSColin Percival	# Generate a list of hashes
263348ffe56aSColin Percival	cat $@ |
263448ffe56aSColin Percival	    cut -f 2,7 -d '|' |
263548ffe56aSColin Percival	    grep -E '^f' |
263648ffe56aSColin Percival	    cut -f 2 -d '|' |
263748ffe56aSColin Percival	    sort -u > filelist
263848ffe56aSColin Percival
263948ffe56aSColin Percival	# Make sure all the hashes exist
264048ffe56aSColin Percival	while read HASH; do
264148ffe56aSColin Percival		if ! [ -f files/${HASH}.gz ]; then
264248ffe56aSColin Percival			echo -n "Update files missing -- "
264348ffe56aSColin Percival			echo "this should never happen."
264448ffe56aSColin Percival			echo "Re-run '$0 fetch'."
264548ffe56aSColin Percival			return 1
264648ffe56aSColin Percival		fi
264748ffe56aSColin Percival	done < filelist
264848ffe56aSColin Percival
264948ffe56aSColin Percival	# Clean up
265048ffe56aSColin Percival	rm filelist
265148ffe56aSColin Percival}
265248ffe56aSColin Percival
265348ffe56aSColin Percival# Remove the system immutable flag from files
265448ffe56aSColin Percivalinstall_unschg () {
265548ffe56aSColin Percival	# Generate file list
265648ffe56aSColin Percival	cat $@ |
265748ffe56aSColin Percival	    cut -f 1 -d '|' > filelist
265848ffe56aSColin Percival
265948ffe56aSColin Percival	# Remove flags
266048ffe56aSColin Percival	while read F; do
2661e829ed67SColin Percival		if ! [ -e ${BASEDIR}/${F} ]; then
266248ffe56aSColin Percival			continue
26635a74378cSXin LI		else
26645a74378cSXin LI			echo ${BASEDIR}/${F}
266548ffe56aSColin Percival		fi
26665a74378cSXin LI	done < filelist | xargs chflags noschg || return 1
266748ffe56aSColin Percival
266848ffe56aSColin Percival	# Clean up
266948ffe56aSColin Percival	rm filelist
267048ffe56aSColin Percival}
267148ffe56aSColin Percival
267223d827efSSimon L. B. Nielsen# Decide which directory name to use for kernel backups.
267323d827efSSimon L. B. Nielsenbackup_kernel_finddir () {
267423d827efSSimon L. B. Nielsen	CNT=0
267523d827efSSimon L. B. Nielsen	while true ; do
267623d827efSSimon L. B. Nielsen		# Pathname does not exist, so it is OK use that name
267723d827efSSimon L. B. Nielsen		# for backup directory.
2678c4a0c62cSThomas Quinot		if [ ! -e $BASEDIR/$BACKUPKERNELDIR ]; then
267923d827efSSimon L. B. Nielsen			return 0
268023d827efSSimon L. B. Nielsen		fi
268123d827efSSimon L. B. Nielsen
268223d827efSSimon L. B. Nielsen		# If directory do exist, we only use if it has our
268323d827efSSimon L. B. Nielsen		# marker file.
2684c4a0c62cSThomas Quinot		if [ -d $BASEDIR/$BACKUPKERNELDIR -a \
2685c4a0c62cSThomas Quinot			-e $BASEDIR/$BACKUPKERNELDIR/.freebsd-update ]; then
268623d827efSSimon L. B. Nielsen			return 0
268723d827efSSimon L. B. Nielsen		fi
268823d827efSSimon L. B. Nielsen
268923d827efSSimon L. B. Nielsen		# We could not use current directory name, so add counter to
269023d827efSSimon L. B. Nielsen		# the end and try again.
269123d827efSSimon L. B. Nielsen		CNT=$((CNT + 1))
269223d827efSSimon L. B. Nielsen		if [ $CNT -gt 9 ]; then
2693c4a0c62cSThomas Quinot			echo "Could not find valid backup dir ($BASEDIR/$BACKUPKERNELDIR)"
269423d827efSSimon L. B. Nielsen			exit 1
269523d827efSSimon L. B. Nielsen		fi
269623d827efSSimon L. B. Nielsen		BACKUPKERNELDIR="`echo $BACKUPKERNELDIR | sed -Ee 's/[0-9]\$//'`"
269723d827efSSimon L. B. Nielsen		BACKUPKERNELDIR="${BACKUPKERNELDIR}${CNT}"
269823d827efSSimon L. B. Nielsen	done
269923d827efSSimon L. B. Nielsen}
270023d827efSSimon L. B. Nielsen
270123d827efSSimon L. B. Nielsen# Backup the current kernel using hardlinks, if not disabled by user.
270223d827efSSimon L. B. Nielsen# Since we delete all files in the directory used for previous backups
270323d827efSSimon L. B. Nielsen# we create a marker file called ".freebsd-update" in the directory so
270423d827efSSimon L. B. Nielsen# we can determine on the next run that the directory was created by
270523d827efSSimon L. B. Nielsen# freebsd-update and we then do not accidentally remove user files in
270623d827efSSimon L. B. Nielsen# the unlikely case that the user has created a directory with a
270723d827efSSimon L. B. Nielsen# conflicting name.
270823d827efSSimon L. B. Nielsenbackup_kernel () {
270923d827efSSimon L. B. Nielsen	# Only make kernel backup is so configured.
271023d827efSSimon L. B. Nielsen	if [ $BACKUPKERNEL != yes ]; then
271123d827efSSimon L. B. Nielsen		return 0
271223d827efSSimon L. B. Nielsen	fi
271323d827efSSimon L. B. Nielsen
271423d827efSSimon L. B. Nielsen	# Decide which directory name to use for kernel backups.
271523d827efSSimon L. B. Nielsen	backup_kernel_finddir
271623d827efSSimon L. B. Nielsen
271723d827efSSimon L. B. Nielsen	# Remove old kernel backup files.  If $BACKUPKERNELDIR was
271823d827efSSimon L. B. Nielsen	# "not ours", backup_kernel_finddir would have exited, so
271923d827efSSimon L. B. Nielsen	# deleting the directory content is as safe as we can make it.
2720c4a0c62cSThomas Quinot	if [ -d $BASEDIR/$BACKUPKERNELDIR ]; then
2721c4a0c62cSThomas Quinot		rm -fr $BASEDIR/$BACKUPKERNELDIR
272223d827efSSimon L. B. Nielsen	fi
272323d827efSSimon L. B. Nielsen
2724ab7d0151SJaakko Heinonen	# Create directories for backup.
2725c4a0c62cSThomas Quinot	mkdir -p $BASEDIR/$BACKUPKERNELDIR
2726c4a0c62cSThomas Quinot	mtree -cdn -p "${BASEDIR}/${KERNELDIR}" | \
2727c4a0c62cSThomas Quinot	    mtree -Ue -p "${BASEDIR}/${BACKUPKERNELDIR}" > /dev/null
272823d827efSSimon L. B. Nielsen
272923d827efSSimon L. B. Nielsen	# Mark the directory as having been created by freebsd-update.
2730c4a0c62cSThomas Quinot	touch $BASEDIR/$BACKUPKERNELDIR/.freebsd-update
273123d827efSSimon L. B. Nielsen	if [ $? -ne 0 ]; then
273223d827efSSimon L. B. Nielsen		echo "Could not create kernel backup directory"
273323d827efSSimon L. B. Nielsen		exit 1
273423d827efSSimon L. B. Nielsen	fi
273523d827efSSimon L. B. Nielsen
273623d827efSSimon L. B. Nielsen	# Disable pathname expansion to be sure *.symbols is not
273723d827efSSimon L. B. Nielsen	# expanded.
273823d827efSSimon L. B. Nielsen	set -f
273923d827efSSimon L. B. Nielsen
274023d827efSSimon L. B. Nielsen	# Use find to ignore symbol files, unless disabled by user.
274123d827efSSimon L. B. Nielsen	if [ $BACKUPKERNELSYMBOLFILES = yes ]; then
274223d827efSSimon L. B. Nielsen		FINDFILTER=""
274323d827efSSimon L. B. Nielsen	else
27447e1ed2c7SEd Maste		FINDFILTER="-a ! -name *.debug -a ! -name *.symbols"
274523d827efSSimon L. B. Nielsen	fi
274623d827efSSimon L. B. Nielsen
274723d827efSSimon L. B. Nielsen	# Backup all the kernel files using hardlinks.
2748c4a0c62cSThomas Quinot	(cd ${BASEDIR}/${KERNELDIR} && find . -type f $FINDFILTER -exec \
2749c4a0c62cSThomas Quinot	    cp -pl '{}' ${BASEDIR}/${BACKUPKERNELDIR}/'{}' \;)
275023d827efSSimon L. B. Nielsen
275123d827efSSimon L. B. Nielsen	# Re-enable patchname expansion.
275223d827efSSimon L. B. Nielsen	set +f
275323d827efSSimon L. B. Nielsen}
275423d827efSSimon L. B. Nielsen
275548ffe56aSColin Percival# Install new files
275648ffe56aSColin Percivalinstall_from_index () {
275748ffe56aSColin Percival	# First pass: Do everything apart from setting file flags.  We
275848ffe56aSColin Percival	# can't set flags yet, because schg inhibits hard linking.
275948ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
276048ffe56aSColin Percival	    tr '|' ' ' |
276148ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
276248ffe56aSColin Percival		case ${TYPE} in
276348ffe56aSColin Percival		d)
276448ffe56aSColin Percival			# Create a directory
276548ffe56aSColin Percival			install -d -o ${OWNER} -g ${GROUP}		\
276648ffe56aSColin Percival			    -m ${PERM} ${BASEDIR}/${FPATH}
276748ffe56aSColin Percival			;;
276848ffe56aSColin Percival		f)
276948ffe56aSColin Percival			if [ -z "${LINK}" ]; then
277048ffe56aSColin Percival				# Create a file, without setting flags.
277148ffe56aSColin Percival				gunzip < files/${HASH}.gz > ${HASH}
277248ffe56aSColin Percival				install -S -o ${OWNER} -g ${GROUP}	\
277348ffe56aSColin Percival				    -m ${PERM} ${HASH} ${BASEDIR}/${FPATH}
277448ffe56aSColin Percival				rm ${HASH}
277548ffe56aSColin Percival			else
277648ffe56aSColin Percival				# Create a hard link.
2777e829ed67SColin Percival				ln -f ${BASEDIR}/${LINK} ${BASEDIR}/${FPATH}
277848ffe56aSColin Percival			fi
277948ffe56aSColin Percival			;;
278048ffe56aSColin Percival		L)
278148ffe56aSColin Percival			# Create a symlink
278248ffe56aSColin Percival			ln -sfh ${HASH} ${BASEDIR}/${FPATH}
278348ffe56aSColin Percival			;;
278448ffe56aSColin Percival		esac
278548ffe56aSColin Percival	    done
278648ffe56aSColin Percival
278748ffe56aSColin Percival	# Perform a second pass, adding file flags.
278848ffe56aSColin Percival	tr '|' ' ' < $1 |
278948ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
279048ffe56aSColin Percival		if [ ${TYPE} = "f" ] &&
279148ffe56aSColin Percival		    ! [ ${FLAGS} = "0" ]; then
279248ffe56aSColin Percival			chflags ${FLAGS} ${BASEDIR}/${FPATH}
279348ffe56aSColin Percival		fi
279448ffe56aSColin Percival	    done
279548ffe56aSColin Percival}
279648ffe56aSColin Percival
279748ffe56aSColin Percival# Remove files which we want to delete
279848ffe56aSColin Percivalinstall_delete () {
279948ffe56aSColin Percival	# Generate list of new files
280048ffe56aSColin Percival	cut -f 1 -d '|' < $2 |
280148ffe56aSColin Percival	    sort > newfiles
280248ffe56aSColin Percival
280348ffe56aSColin Percival	# Generate subindex of old files we want to nuke
280448ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
280548ffe56aSColin Percival	    join -t '|' -v 1 - newfiles |
2806bce02f98SColin Percival	    sort -r -k 1,1 -t '|' |
280748ffe56aSColin Percival	    cut -f 1,2 -d '|' |
280848ffe56aSColin Percival	    tr '|' ' ' > killfiles
280948ffe56aSColin Percival
281048ffe56aSColin Percival	# Remove the offending bits
281148ffe56aSColin Percival	while read FPATH TYPE; do
281248ffe56aSColin Percival		case ${TYPE} in
281348ffe56aSColin Percival		d)
281448ffe56aSColin Percival			rmdir ${BASEDIR}/${FPATH}
281548ffe56aSColin Percival			;;
281648ffe56aSColin Percival		f)
281748ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
281848ffe56aSColin Percival			;;
281948ffe56aSColin Percival		L)
282048ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
282148ffe56aSColin Percival			;;
282248ffe56aSColin Percival		esac
282348ffe56aSColin Percival	done < killfiles
282448ffe56aSColin Percival
282548ffe56aSColin Percival	# Clean up
282648ffe56aSColin Percival	rm newfiles killfiles
282748ffe56aSColin Percival}
282848ffe56aSColin Percival
2829db6b0a61SColin Percival# Install new files, delete old files, and update linker.hints
2830db6b0a61SColin Percivalinstall_files () {
2831db6b0a61SColin Percival	# If we haven't already dealt with the kernel, deal with it.
2832db6b0a61SColin Percival	if ! [ -f $1/kerneldone ]; then
2833db6b0a61SColin Percival		grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
2834db6b0a61SColin Percival		grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
2835db6b0a61SColin Percival
283623d827efSSimon L. B. Nielsen		# Backup current kernel before installing a new one
283723d827efSSimon L. B. Nielsen		backup_kernel || return 1
283823d827efSSimon L. B. Nielsen
2839db6b0a61SColin Percival		# Install new files
2840db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2841db6b0a61SColin Percival
2842db6b0a61SColin Percival		# Remove files which need to be deleted
2843db6b0a61SColin Percival		install_delete INDEX-OLD INDEX-NEW || return 1
2844db6b0a61SColin Percival
2845db6b0a61SColin Percival		# Update linker.hints if necessary
2846db6b0a61SColin Percival		if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2847c4a0c62cSThomas Quinot			kldxref -R ${BASEDIR}/boot/ 2>/dev/null
284848ffe56aSColin Percival		fi
2849db6b0a61SColin Percival
2850db6b0a61SColin Percival		# We've finished updating the kernel.
2851db6b0a61SColin Percival		touch $1/kerneldone
2852db6b0a61SColin Percival
2853db6b0a61SColin Percival		# Do we need to ask for a reboot now?
2854db6b0a61SColin Percival		if [ -f $1/kernelfirst ] &&
2855db6b0a61SColin Percival		    [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2856db6b0a61SColin Percival			cat <<-EOF
2857db6b0a61SColin Percival
2858db6b0a61SColin PercivalKernel updates have been installed.  Please reboot and run
2859db6b0a61SColin Percival"$0 install" again to finish installing updates.
2860db6b0a61SColin Percival			EOF
2861db6b0a61SColin Percival			exit 0
2862db6b0a61SColin Percival		fi
2863db6b0a61SColin Percival	fi
2864db6b0a61SColin Percival
2865db6b0a61SColin Percival	# If we haven't already dealt with the world, deal with it.
2866db6b0a61SColin Percival	if ! [ -f $1/worlddone ]; then
2867cd1ab228SColin Percival		# Create any necessary directories first
2868cd1ab228SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2869cd1ab228SColin Percival		    grep -E '^[^|]+\|d\|' > INDEX-NEW
2870cd1ab228SColin Percival		install_from_index INDEX-NEW || return 1
2871cd1ab228SColin Percival
2872722d81b5SBrooks Davis		# Install new runtime linker
2873722d81b5SBrooks Davis		grep -vE '^/boot/' $1/INDEX-NEW |
2874722d81b5SBrooks Davis		    grep -vE '^[^|]+\|d\|' |
2875722d81b5SBrooks Davis		    grep -E '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2876722d81b5SBrooks Davis		install_from_index INDEX-NEW || return 1
2877722d81b5SBrooks Davis
2878db6b0a61SColin Percival		# Install new shared libraries next
2879db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2880cd1ab228SColin Percival		    grep -vE '^[^|]+\|d\|' |
2881722d81b5SBrooks Davis		    grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' |
28829546dbd1SColin Percival		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2883db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2884db6b0a61SColin Percival
2885db6b0a61SColin Percival		# Deal with everything else
2886db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-OLD |
2887cd1ab228SColin Percival		    grep -vE '^[^|]+\|d\|' |
2888722d81b5SBrooks Davis		    grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' |
28899546dbd1SColin Percival		    grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD
2890db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2891cd1ab228SColin Percival		    grep -vE '^[^|]+\|d\|' |
2892722d81b5SBrooks Davis		    grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' |
28939546dbd1SColin Percival		    grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2894db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2895db6b0a61SColin Percival		install_delete INDEX-OLD INDEX-NEW || return 1
2896db6b0a61SColin Percival
2897db6b0a61SColin Percival		# Rebuild /etc/spwd.db and /etc/pwd.db if necessary.
2898c4a0c62cSThomas Quinot		if [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/spwd.db ] ||
2899c4a0c62cSThomas Quinot		    [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/pwd.db ]; then
2900c4a0c62cSThomas Quinot			pwd_mkdb -d ${BASEDIR}/etc ${BASEDIR}/etc/master.passwd
2901db6b0a61SColin Percival		fi
2902db6b0a61SColin Percival
2903db6b0a61SColin Percival		# Rebuild /etc/login.conf.db if necessary.
2904c4a0c62cSThomas Quinot		if [ ${BASEDIR}/etc/login.conf -nt ${BASEDIR}/etc/login.conf.db ]; then
2905c4a0c62cSThomas Quinot			cap_mkdb ${BASEDIR}/etc/login.conf
2906db6b0a61SColin Percival		fi
2907db6b0a61SColin Percival
2908db6b0a61SColin Percival		# We've finished installing the world and deleting old files
2909db6b0a61SColin Percival		# which are not shared libraries.
2910db6b0a61SColin Percival		touch $1/worlddone
2911db6b0a61SColin Percival
2912db6b0a61SColin Percival		# Do we need to ask the user to portupgrade now?
2913db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
29149546dbd1SColin Percival		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' |
2915db6b0a61SColin Percival		    cut -f 1 -d '|' |
2916db6b0a61SColin Percival		    sort > newfiles
2917db6b0a61SColin Percival		if grep -vE '^/boot/' $1/INDEX-OLD |
29189546dbd1SColin Percival		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' |
2919db6b0a61SColin Percival		    cut -f 1 -d '|' |
2920db6b0a61SColin Percival		    sort |
2921db6b0a61SColin Percival		    join -v 1 - newfiles |
2922db6b0a61SColin Percival		    grep -q .; then
2923db6b0a61SColin Percival			cat <<-EOF
2924db6b0a61SColin Percival
2925db6b0a61SColin PercivalCompleting this upgrade requires removing old shared object files.
2926db6b0a61SColin PercivalPlease rebuild all installed 3rd party software (e.g., programs
2927db6b0a61SColin Percivalinstalled from the ports tree) and then run "$0 install"
2928db6b0a61SColin Percivalagain to finish installing updates.
2929db6b0a61SColin Percival			EOF
2930db6b0a61SColin Percival			rm newfiles
2931db6b0a61SColin Percival			exit 0
2932db6b0a61SColin Percival		fi
2933db6b0a61SColin Percival		rm newfiles
2934db6b0a61SColin Percival	fi
2935db6b0a61SColin Percival
2936db6b0a61SColin Percival	# Remove old shared libraries
2937db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2938cd1ab228SColin Percival	    grep -vE '^[^|]+\|d\|' |
29399546dbd1SColin Percival	    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2940db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2941cd1ab228SColin Percival	    grep -vE '^[^|]+\|d\|' |
29429546dbd1SColin Percival	    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD
2943db6b0a61SColin Percival	install_delete INDEX-OLD INDEX-NEW || return 1
2944db6b0a61SColin Percival
2945cd1ab228SColin Percival	# Remove old directories
2946ebc1d19cSColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2947ebc1d19cSColin Percival	    grep -E '^[^|]+\|d\|' > INDEX-NEW
2948cd1ab228SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2949cd1ab228SColin Percival	    grep -E '^[^|]+\|d\|' > INDEX-OLD
2950cd1ab228SColin Percival	install_delete INDEX-OLD INDEX-NEW || return 1
2951cd1ab228SColin Percival
2952db6b0a61SColin Percival	# Remove temporary files
2953db6b0a61SColin Percival	rm INDEX-OLD INDEX-NEW
295448ffe56aSColin Percival}
295548ffe56aSColin Percival
295648ffe56aSColin Percival# Rearrange bits to allow the installed updates to be rolled back
295748ffe56aSColin Percivalinstall_setup_rollback () {
2958db6b0a61SColin Percival	# Remove the "reboot after installing kernel", "kernel updated", and
2959db6b0a61SColin Percival	# "finished installing the world" flags if present -- they are
2960db6b0a61SColin Percival	# irrelevant when rolling back updates.
2961db6b0a61SColin Percival	if [ -f ${BDHASH}-install/kernelfirst ]; then
2962db6b0a61SColin Percival		rm ${BDHASH}-install/kernelfirst
2963db6b0a61SColin Percival		rm ${BDHASH}-install/kerneldone
2964db6b0a61SColin Percival	fi
2965db6b0a61SColin Percival	if [ -f ${BDHASH}-install/worlddone ]; then
2966db6b0a61SColin Percival		rm ${BDHASH}-install/worlddone
2967db6b0a61SColin Percival	fi
2968db6b0a61SColin Percival
296948ffe56aSColin Percival	if [ -L ${BDHASH}-rollback ]; then
297048ffe56aSColin Percival		mv ${BDHASH}-rollback ${BDHASH}-install/rollback
297148ffe56aSColin Percival	fi
297248ffe56aSColin Percival
297348ffe56aSColin Percival	mv ${BDHASH}-install ${BDHASH}-rollback
297448ffe56aSColin Percival}
297548ffe56aSColin Percival
297648ffe56aSColin Percival# Actually install updates
297748ffe56aSColin Percivalinstall_run () {
297848ffe56aSColin Percival	echo -n "Installing updates..."
297948ffe56aSColin Percival
298048ffe56aSColin Percival	# Make sure we have all the files we should have
298148ffe56aSColin Percival	install_verify ${BDHASH}-install/INDEX-OLD	\
298248ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
298348ffe56aSColin Percival
298448ffe56aSColin Percival	# Remove system immutable flag from files
298548ffe56aSColin Percival	install_unschg ${BDHASH}-install/INDEX-OLD	\
298648ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
298748ffe56aSColin Percival
2988db6b0a61SColin Percival	# Install new files, delete old files, and update linker.hints
2989db6b0a61SColin Percival	install_files ${BDHASH}-install || return 1
299048ffe56aSColin Percival
299148ffe56aSColin Percival	# Rearrange bits to allow the installed updates to be rolled back
299248ffe56aSColin Percival	install_setup_rollback
299348ffe56aSColin Percival
299448ffe56aSColin Percival	echo " done."
299548ffe56aSColin Percival}
299648ffe56aSColin Percival
299748ffe56aSColin Percival# Rearrange bits to allow the previous set of updates to be rolled back next.
299848ffe56aSColin Percivalrollback_setup_rollback () {
299948ffe56aSColin Percival	if [ -L ${BDHASH}-rollback/rollback ]; then
300048ffe56aSColin Percival		mv ${BDHASH}-rollback/rollback rollback-tmp
300148ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
300248ffe56aSColin Percival		rm ${BDHASH}-rollback
300348ffe56aSColin Percival		mv rollback-tmp ${BDHASH}-rollback
300448ffe56aSColin Percival	else
300548ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
300648ffe56aSColin Percival		rm ${BDHASH}-rollback
300748ffe56aSColin Percival	fi
300848ffe56aSColin Percival}
300948ffe56aSColin Percival
3010db6b0a61SColin Percival# Install old files, delete new files, and update linker.hints
3011db6b0a61SColin Percivalrollback_files () {
30121ec4fb3aSColin Percival	# Install old shared library files which don't have the same path as
30131ec4fb3aSColin Percival	# a new shared library file.
30141ec4fb3aSColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
3015fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
30161ec4fb3aSColin Percival	    cut -f 1 -d '|' |
30171ec4fb3aSColin Percival	    sort > INDEX-NEW.libs.flist
3018db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
3019fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
30201ec4fb3aSColin Percival	    sort -k 1,1 -t '|' - |
30211ec4fb3aSColin Percival	    join -t '|' -v 1 - INDEX-NEW.libs.flist > INDEX-OLD
3022db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
3023db6b0a61SColin Percival
3024db6b0a61SColin Percival	# Deal with files which are neither kernel nor shared library
3025db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
3026fd0963d1SColin Percival	    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
3027db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
3028fd0963d1SColin Percival	    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
3029db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
3030db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
3031db6b0a61SColin Percival
30321ec4fb3aSColin Percival	# Install any old shared library files which we didn't install above.
30331ec4fb3aSColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
3034fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
30351ec4fb3aSColin Percival	    sort -k 1,1 -t '|' - |
30361ec4fb3aSColin Percival	    join -t '|' - INDEX-NEW.libs.flist > INDEX-OLD
30371ec4fb3aSColin Percival	install_from_index INDEX-OLD || return 1
30381ec4fb3aSColin Percival
3039db6b0a61SColin Percival	# Delete unneeded shared library files
3040db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
3041fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
3042db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
3043fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
3044db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
3045db6b0a61SColin Percival
3046db6b0a61SColin Percival	# Deal with kernel files
3047db6b0a61SColin Percival	grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
3048db6b0a61SColin Percival	grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
3049db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
3050db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
3051db6b0a61SColin Percival	if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
3052db6b0a61SColin Percival		kldxref -R /boot/ 2>/dev/null
3053db6b0a61SColin Percival	fi
3054db6b0a61SColin Percival
3055db6b0a61SColin Percival	# Remove temporary files
30560e0d8d5aSColin Percival	rm INDEX-OLD INDEX-NEW INDEX-NEW.libs.flist
3057db6b0a61SColin Percival}
3058db6b0a61SColin Percival
305948ffe56aSColin Percival# Actually rollback updates
306048ffe56aSColin Percivalrollback_run () {
306148ffe56aSColin Percival	echo -n "Uninstalling updates..."
306248ffe56aSColin Percival
306348ffe56aSColin Percival	# If there are updates waiting to be installed, remove them; we
306448ffe56aSColin Percival	# want the user to re-run 'fetch' after rolling back updates.
306548ffe56aSColin Percival	if [ -L ${BDHASH}-install ]; then
306648ffe56aSColin Percival		rm -r ${BDHASH}-install/
306748ffe56aSColin Percival		rm ${BDHASH}-install
306848ffe56aSColin Percival	fi
306948ffe56aSColin Percival
307048ffe56aSColin Percival	# Make sure we have all the files we should have
307148ffe56aSColin Percival	install_verify ${BDHASH}-rollback/INDEX-NEW	\
307248ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
307348ffe56aSColin Percival
307448ffe56aSColin Percival	# Remove system immutable flag from files
307548ffe56aSColin Percival	install_unschg ${BDHASH}-rollback/INDEX-NEW	\
307648ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
307748ffe56aSColin Percival
3078db6b0a61SColin Percival	# Install old files, delete new files, and update linker.hints
3079db6b0a61SColin Percival	rollback_files ${BDHASH}-rollback || return 1
308048ffe56aSColin Percival
308148ffe56aSColin Percival	# Remove the rollback directory and the symlink pointing to it; and
308248ffe56aSColin Percival	# rearrange bits to allow the previous set of updates to be rolled
308348ffe56aSColin Percival	# back next.
308448ffe56aSColin Percival	rollback_setup_rollback
308548ffe56aSColin Percival
308648ffe56aSColin Percival	echo " done."
308748ffe56aSColin Percival}
308848ffe56aSColin Percival
308908e23beeSColin Percival# Compare INDEX-ALL and INDEX-PRESENT and print warnings about differences.
309008e23beeSColin PercivalIDS_compare () {
3091bb10a826SColin Percival	# Get all the lines which mismatch in something other than file
3092bb10a826SColin Percival	# flags.  We ignore file flags because sysinstall doesn't seem to
3093bb10a826SColin Percival	# set them when it installs FreeBSD; warning about these adds a
3094bb10a826SColin Percival	# very large amount of noise.
3095bb10a826SColin Percival	cut -f 1-5,7-8 -d '|' $1 > $1.noflags
3096bb10a826SColin Percival	sort -k 1,1 -t '|' $1.noflags > $1.sorted
3097bb10a826SColin Percival	cut -f 1-5,7-8 -d '|' $2 |
3098bb10a826SColin Percival	    comm -13 $1.noflags - |
3099bb10a826SColin Percival	    fgrep -v '|-|||||' |
310008e23beeSColin Percival	    sort -k 1,1 -t '|' |
310108e23beeSColin Percival	    join -t '|' $1.sorted - > INDEX-NOTMATCHING
310208e23beeSColin Percival
310308e23beeSColin Percival	# Ignore files which match IDSIGNOREPATHS.
310408e23beeSColin Percival	for X in ${IDSIGNOREPATHS}; do
310508e23beeSColin Percival		grep -E "^${X}" INDEX-NOTMATCHING
310608e23beeSColin Percival	done |
310708e23beeSColin Percival	    sort -u |
310808e23beeSColin Percival	    comm -13 - INDEX-NOTMATCHING > INDEX-NOTMATCHING.tmp
310908e23beeSColin Percival	mv INDEX-NOTMATCHING.tmp INDEX-NOTMATCHING
311008e23beeSColin Percival
311108e23beeSColin Percival	# Go through the lines and print warnings.
3112aa60062eSColin Percival	local IFS='|'
3113aa60062eSColin Percival	while read FPATH TYPE OWNER GROUP PERM HASH LINK P_TYPE P_OWNER P_GROUP P_PERM P_HASH P_LINK; do
311408e23beeSColin Percival		# Warn about different object types.
311508e23beeSColin Percival		if ! [ "${TYPE}" = "${P_TYPE}" ]; then
311608e23beeSColin Percival			echo -n "${FPATH} is a "
311708e23beeSColin Percival			case "${P_TYPE}" in
311808e23beeSColin Percival			f)	echo -n "regular file, "
311908e23beeSColin Percival				;;
312008e23beeSColin Percival			d)	echo -n "directory, "
312108e23beeSColin Percival				;;
312208e23beeSColin Percival			L)	echo -n "symlink, "
312308e23beeSColin Percival				;;
312408e23beeSColin Percival			esac
312508e23beeSColin Percival			echo -n "but should be a "
312608e23beeSColin Percival			case "${TYPE}" in
312708e23beeSColin Percival			f)	echo -n "regular file."
312808e23beeSColin Percival				;;
312908e23beeSColin Percival			d)	echo -n "directory."
313008e23beeSColin Percival				;;
313108e23beeSColin Percival			L)	echo -n "symlink."
313208e23beeSColin Percival				;;
313308e23beeSColin Percival			esac
313408e23beeSColin Percival			echo
313508e23beeSColin Percival
313608e23beeSColin Percival			# Skip other tests, since they don't make sense if
313708e23beeSColin Percival			# we're comparing different object types.
313808e23beeSColin Percival			continue
313908e23beeSColin Percival		fi
314008e23beeSColin Percival
314108e23beeSColin Percival		# Warn about different owners.
314208e23beeSColin Percival		if ! [ "${OWNER}" = "${P_OWNER}" ]; then
314308e23beeSColin Percival			echo -n "${FPATH} is owned by user id ${P_OWNER}, "
314408e23beeSColin Percival			echo "but should be owned by user id ${OWNER}."
314508e23beeSColin Percival		fi
314608e23beeSColin Percival
314708e23beeSColin Percival		# Warn about different groups.
314808e23beeSColin Percival		if ! [ "${GROUP}" = "${P_GROUP}" ]; then
314908e23beeSColin Percival			echo -n "${FPATH} is owned by group id ${P_GROUP}, "
315008e23beeSColin Percival			echo "but should be owned by group id ${GROUP}."
315108e23beeSColin Percival		fi
315208e23beeSColin Percival
315308e23beeSColin Percival		# Warn about different permissions.  We do not warn about
315408e23beeSColin Percival		# different permissions on symlinks, since some archivers
315508e23beeSColin Percival		# don't extract symlink permissions correctly and they are
315608e23beeSColin Percival		# ignored anyway.
315708e23beeSColin Percival		if ! [ "${PERM}" = "${P_PERM}" ] &&
315808e23beeSColin Percival		    ! [ "${TYPE}" = "L" ]; then
315908e23beeSColin Percival			echo -n "${FPATH} has ${P_PERM} permissions, "
316008e23beeSColin Percival			echo "but should have ${PERM} permissions."
316108e23beeSColin Percival		fi
316208e23beeSColin Percival
316308e23beeSColin Percival		# Warn about different file hashes / symlink destinations.
316408e23beeSColin Percival		if ! [ "${HASH}" = "${P_HASH}" ]; then
316508e23beeSColin Percival			if [ "${TYPE}" = "L" ]; then
316608e23beeSColin Percival				echo -n "${FPATH} is a symlink to ${P_HASH}, "
316708e23beeSColin Percival				echo "but should be a symlink to ${HASH}."
316808e23beeSColin Percival			fi
316908e23beeSColin Percival			if [ "${TYPE}" = "f" ]; then
317008e23beeSColin Percival				echo -n "${FPATH} has SHA256 hash ${P_HASH}, "
317108e23beeSColin Percival				echo "but should have SHA256 hash ${HASH}."
317208e23beeSColin Percival			fi
317308e23beeSColin Percival		fi
317408e23beeSColin Percival
317508e23beeSColin Percival		# We don't warn about different hard links, since some
317608e23beeSColin Percival		# some archivers break hard links, and as long as the
317708e23beeSColin Percival		# underlying data is correct they really don't matter.
317808e23beeSColin Percival	done < INDEX-NOTMATCHING
317908e23beeSColin Percival
318008e23beeSColin Percival	# Clean up
3181bb10a826SColin Percival	rm $1 $1.noflags $1.sorted $2 INDEX-NOTMATCHING
318208e23beeSColin Percival}
318308e23beeSColin Percival
318408e23beeSColin Percival# Do the work involved in comparing the system to a "known good" index
318508e23beeSColin PercivalIDS_run () {
318608e23beeSColin Percival	workdir_init || return 1
318708e23beeSColin Percival
318808e23beeSColin Percival	# Prepare the mirror list.
318908e23beeSColin Percival	fetch_pick_server_init && fetch_pick_server
319008e23beeSColin Percival
319108e23beeSColin Percival	# Try to fetch the public key until we run out of servers.
319208e23beeSColin Percival	while ! fetch_key; do
319308e23beeSColin Percival		fetch_pick_server || return 1
319408e23beeSColin Percival	done
319508e23beeSColin Percival
319608e23beeSColin Percival	# Try to fetch the metadata index signature ("tag") until we run
319708e23beeSColin Percival	# out of available servers; and sanity check the downloaded tag.
319808e23beeSColin Percival	while ! fetch_tag; do
319908e23beeSColin Percival		fetch_pick_server || return 1
320008e23beeSColin Percival	done
320108e23beeSColin Percival	fetch_tagsanity || return 1
320208e23beeSColin Percival
320308e23beeSColin Percival	# Fetch INDEX-OLD and INDEX-ALL.
320408e23beeSColin Percival	fetch_metadata INDEX-OLD INDEX-ALL || return 1
320508e23beeSColin Percival
320608e23beeSColin Percival	# Generate filtered INDEX-OLD and INDEX-ALL files containing only
320708e23beeSColin Percival	# the components we want and without anything marked as "Ignore".
320808e23beeSColin Percival	fetch_filter_metadata INDEX-OLD || return 1
320908e23beeSColin Percival	fetch_filter_metadata INDEX-ALL || return 1
321008e23beeSColin Percival
321108e23beeSColin Percival	# Merge the INDEX-OLD and INDEX-ALL files into INDEX-ALL.
321208e23beeSColin Percival	sort INDEX-OLD INDEX-ALL > INDEX-ALL.tmp
321308e23beeSColin Percival	mv INDEX-ALL.tmp INDEX-ALL
321408e23beeSColin Percival	rm INDEX-OLD
321508e23beeSColin Percival
321608e23beeSColin Percival	# Translate /boot/${KERNCONF} to ${KERNELDIR}
321708e23beeSColin Percival	fetch_filter_kernel_names INDEX-ALL ${KERNCONF}
321808e23beeSColin Percival
321908e23beeSColin Percival	# Inspect the system and generate an INDEX-PRESENT file.
322008e23beeSColin Percival	fetch_inspect_system INDEX-ALL INDEX-PRESENT /dev/null || return 1
322108e23beeSColin Percival
322208e23beeSColin Percival	# Compare INDEX-ALL and INDEX-PRESENT and print warnings about any
322308e23beeSColin Percival	# differences.
322408e23beeSColin Percival	IDS_compare INDEX-ALL INDEX-PRESENT
322508e23beeSColin Percival}
322608e23beeSColin Percival
322748ffe56aSColin Percival#### Main functions -- call parameter-handling and core functions
322848ffe56aSColin Percival
322948ffe56aSColin Percival# Using the command line, configuration file, and defaults,
323048ffe56aSColin Percival# set all the parameters which are needed later.
323148ffe56aSColin Percivalget_params () {
323248ffe56aSColin Percival	init_params
323348ffe56aSColin Percival	parse_cmdline $@
323448ffe56aSColin Percival	parse_conffile
323548ffe56aSColin Percival	default_params
323648ffe56aSColin Percival}
323748ffe56aSColin Percival
323848ffe56aSColin Percival# Fetch command.  Make sure that we're being called
323948ffe56aSColin Percival# interactively, then run fetch_check_params and fetch_run
324048ffe56aSColin Percivalcmd_fetch () {
32418bf2dcceSAllan Jude	if [ ! -t 0 -a $NOTTYOK -eq 0 ]; then
324248ffe56aSColin Percival		echo -n "`basename $0` fetch should not "
324348ffe56aSColin Percival		echo "be run non-interactively."
324448ffe56aSColin Percival		echo "Run `basename $0` cron instead."
324548ffe56aSColin Percival		exit 1
324648ffe56aSColin Percival	fi
324748ffe56aSColin Percival	fetch_check_params
324848ffe56aSColin Percival	fetch_run || exit 1
324933bd05c3SGuangyuan Yang	ISFETCHED=1
325048ffe56aSColin Percival}
325148ffe56aSColin Percival
325248ffe56aSColin Percival# Cron command.  Make sure the parameters are sensible; wait
325348ffe56aSColin Percival# rand(3600) seconds; then fetch updates.  While fetching updates,
325448ffe56aSColin Percival# send output to a temporary file; only print that file if the
325548ffe56aSColin Percival# fetching failed.
325648ffe56aSColin Percivalcmd_cron () {
325748ffe56aSColin Percival	fetch_check_params
325848ffe56aSColin Percival	sleep `jot -r 1 0 3600`
325948ffe56aSColin Percival
326048ffe56aSColin Percival	TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1
326148ffe56aSColin Percival	if ! fetch_run >> ${TMPFILE} ||
326248ffe56aSColin Percival	    ! grep -q "No updates needed" ${TMPFILE} ||
326348ffe56aSColin Percival	    [ ${VERBOSELEVEL} = "debug" ]; then
326448ffe56aSColin Percival		mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE}
326548ffe56aSColin Percival	fi
326648ffe56aSColin Percival
326748ffe56aSColin Percival	rm ${TMPFILE}
326848ffe56aSColin Percival}
326948ffe56aSColin Percival
3270db6b0a61SColin Percival# Fetch files for upgrading to a new release.
3271db6b0a61SColin Percivalcmd_upgrade () {
3272db6b0a61SColin Percival	upgrade_check_params
3273db6b0a61SColin Percival	upgrade_run || exit 1
3274db6b0a61SColin Percival}
3275db6b0a61SColin Percival
327648ffe56aSColin Percival# Install downloaded updates.
327748ffe56aSColin Percivalcmd_install () {
327848ffe56aSColin Percival	install_check_params
327948ffe56aSColin Percival	install_run || exit 1
328048ffe56aSColin Percival}
328148ffe56aSColin Percival
328248ffe56aSColin Percival# Rollback most recently installed updates.
328348ffe56aSColin Percivalcmd_rollback () {
328448ffe56aSColin Percival	rollback_check_params
328548ffe56aSColin Percival	rollback_run || exit 1
328648ffe56aSColin Percival}
328748ffe56aSColin Percival
328808e23beeSColin Percival# Compare system against a "known good" index.
328908e23beeSColin Percivalcmd_IDS () {
329008e23beeSColin Percival	IDS_check_params
329108e23beeSColin Percival	IDS_run || exit 1
329208e23beeSColin Percival}
329308e23beeSColin Percival
329448ffe56aSColin Percival#### Entry point
329548ffe56aSColin Percival
329648ffe56aSColin Percival# Make sure we find utilities from the base system
329748ffe56aSColin Percivalexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
329848ffe56aSColin Percival
32999c990fb2SGordon Tetlow# Set a pager if the user doesn't
33009c990fb2SGordon Tetlowif [ -z "$PAGER" ]; then
33019c990fb2SGordon Tetlow	PAGER=/usr/bin/more
33029c990fb2SGordon Tetlowfi
33039c990fb2SGordon Tetlow
3304f2890dbdSColin Percival# Set LC_ALL in order to avoid problems with character ranges like [A-Z].
3305f2890dbdSColin Percivalexport LC_ALL=C
3306f2890dbdSColin Percival
330748ffe56aSColin Percivalget_params $@
330848ffe56aSColin Percivalfor COMMAND in ${COMMANDS}; do
330948ffe56aSColin Percival	cmd_${COMMAND}
331048ffe56aSColin Percivaldone
3311