xref: /freebsd/usr.sbin/freebsd-update/freebsd-update.sh (revision ebc1d19c3c9740f23c8aec04c738e03bc46cc2cd)
148ffe56aSColin Percival#!/bin/sh
248ffe56aSColin Percival
348ffe56aSColin Percival#-
42328d598SColin Percival# Copyright 2004-2007 Colin Percival
548ffe56aSColin Percival# All rights reserved
648ffe56aSColin Percival#
748ffe56aSColin Percival# Redistribution and use in source and binary forms, with or without
848ffe56aSColin Percival# modification, are permitted providing that the following conditions
948ffe56aSColin Percival# are met:
1048ffe56aSColin Percival# 1. Redistributions of source code must retain the above copyright
1148ffe56aSColin Percival#    notice, this list of conditions and the following disclaimer.
1248ffe56aSColin Percival# 2. Redistributions in binary form must reproduce the above copyright
1348ffe56aSColin Percival#    notice, this list of conditions and the following disclaimer in the
1448ffe56aSColin Percival#    documentation and/or other materials provided with the distribution.
1548ffe56aSColin Percival#
1648ffe56aSColin Percival# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1748ffe56aSColin Percival# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1848ffe56aSColin Percival# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1948ffe56aSColin Percival# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
2048ffe56aSColin Percival# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2148ffe56aSColin Percival# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2248ffe56aSColin Percival# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2348ffe56aSColin Percival# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2448ffe56aSColin Percival# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
2548ffe56aSColin Percival# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2648ffe56aSColin Percival# POSSIBILITY OF SUCH DAMAGE.
2748ffe56aSColin Percival
2848ffe56aSColin Percival# $FreeBSD$
2948ffe56aSColin Percival
3048ffe56aSColin Percival#### Usage function -- called from command-line handling code.
3148ffe56aSColin Percival
3248ffe56aSColin Percival# Usage instructions.  Options not listed:
3348ffe56aSColin Percival# --debug	-- don't filter output from utilities
3448ffe56aSColin Percival# --no-stats	-- don't show progress statistics while fetching files
3548ffe56aSColin Percivalusage () {
3648ffe56aSColin Percival	cat <<EOF
3748ffe56aSColin Percivalusage: `basename $0` [options] command ... [path]
3848ffe56aSColin Percival
3948ffe56aSColin PercivalOptions:
4048ffe56aSColin Percival  -b basedir   -- Operate on a system mounted at basedir
4148ffe56aSColin Percival                  (default: /)
4248ffe56aSColin Percival  -d workdir   -- Store working files in workdir
4348ffe56aSColin Percival                  (default: /var/db/freebsd-update/)
4448ffe56aSColin Percival  -f conffile  -- Read configuration options from conffile
4548ffe56aSColin Percival                  (default: /etc/freebsd-update.conf)
4648ffe56aSColin Percival  -k KEY       -- Trust an RSA key with SHA256 hash of KEY
47db6b0a61SColin Percival  -r release   -- Target for upgrade (e.g., 6.2-RELEASE)
4848ffe56aSColin Percival  -s server    -- Server from which to fetch updates
4948ffe56aSColin Percival                  (default: update.FreeBSD.org)
5048ffe56aSColin Percival  -t address   -- Mail output of cron command, if any, to address
5148ffe56aSColin Percival                  (default: root)
5248ffe56aSColin PercivalCommands:
5348ffe56aSColin Percival  fetch        -- Fetch updates from server
5448ffe56aSColin Percival  cron         -- Sleep rand(3600) seconds, fetch updates, and send an
5548ffe56aSColin Percival                  email if updates were found
56db6b0a61SColin Percival  upgrade      -- Fetch upgrades to FreeBSD version specified via -r option
57db6b0a61SColin Percival  install      -- Install downloaded updates or upgrades
5848ffe56aSColin Percival  rollback     -- Uninstall most recently installed updates
5908e23beeSColin Percival  IDS          -- Compare the system against an index of "known good" files.
6048ffe56aSColin PercivalEOF
6148ffe56aSColin Percival	exit 0
6248ffe56aSColin Percival}
6348ffe56aSColin Percival
6448ffe56aSColin Percival#### Configuration processing functions
6548ffe56aSColin Percival
6648ffe56aSColin Percival#-
6748ffe56aSColin Percival# Configuration options are set in the following order of priority:
6848ffe56aSColin Percival# 1. Command line options
6948ffe56aSColin Percival# 2. Configuration file options
7048ffe56aSColin Percival# 3. Default options
7148ffe56aSColin Percival# In addition, certain options (e.g., IgnorePaths) can be specified multiple
7248ffe56aSColin Percival# times and (as long as these are all in the same place, e.g., inside the
7348ffe56aSColin Percival# configuration file) they will accumulate.  Finally, because the path to the
7448ffe56aSColin Percival# configuration file can be specified at the command line, the entire command
7548ffe56aSColin Percival# line must be processed before we start reading the configuration file.
7648ffe56aSColin Percival#
7748ffe56aSColin Percival# Sound like a mess?  It is.  Here's how we handle this:
7848ffe56aSColin Percival# 1. Initialize CONFFILE and all the options to "".
7948ffe56aSColin Percival# 2. Process the command line.  Throw an error if a non-accumulating option
8048ffe56aSColin Percival#    is specified twice.
8148ffe56aSColin Percival# 3. If CONFFILE is "", set CONFFILE to /etc/freebsd-update.conf .
8248ffe56aSColin Percival# 4. For all the configuration options X, set X_saved to X.
8348ffe56aSColin Percival# 5. Initialize all the options to "".
8448ffe56aSColin Percival# 6. Read CONFFILE line by line, parsing options.
8548ffe56aSColin Percival# 7. For each configuration option X, set X to X_saved iff X_saved is not "".
8648ffe56aSColin Percival# 8. Repeat steps 4-7, except setting options to their default values at (6).
8748ffe56aSColin Percival
8848ffe56aSColin PercivalCONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE
8948ffe56aSColin Percival    KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED
9008e23beeSColin Percival    BASEDIR VERBOSELEVEL TARGETRELEASE STRICTCOMPONENTS MERGECHANGES
9123d827efSSimon L. B. Nielsen    IDSIGNOREPATHS BACKUPKERNEL BACKUPKERNELDIR BACKUPKERNELSYMBOLFILES"
9248ffe56aSColin Percival
9348ffe56aSColin Percival# Set all the configuration options to "".
9448ffe56aSColin Percivalnullconfig () {
9548ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
9648ffe56aSColin Percival		eval ${X}=""
9748ffe56aSColin Percival	done
9848ffe56aSColin Percival}
9948ffe56aSColin Percival
10048ffe56aSColin Percival# For each configuration option X, set X_saved to X.
10148ffe56aSColin Percivalsaveconfig () {
10248ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
10348ffe56aSColin Percival		eval ${X}_saved=\$${X}
10448ffe56aSColin Percival	done
10548ffe56aSColin Percival}
10648ffe56aSColin Percival
10748ffe56aSColin Percival# For each configuration option X, set X to X_saved if X_saved is not "".
10848ffe56aSColin Percivalmergeconfig () {
10948ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
11048ffe56aSColin Percival		eval _=\$${X}_saved
11148ffe56aSColin Percival		if ! [ -z "${_}" ]; then
11248ffe56aSColin Percival			eval ${X}=\$${X}_saved
11348ffe56aSColin Percival		fi
11448ffe56aSColin Percival	done
11548ffe56aSColin Percival}
11648ffe56aSColin Percival
11748ffe56aSColin Percival# Set the trusted keyprint.
11848ffe56aSColin Percivalconfig_KeyPrint () {
11948ffe56aSColin Percival	if [ -z ${KEYPRINT} ]; then
12048ffe56aSColin Percival		KEYPRINT=$1
12148ffe56aSColin Percival	else
12248ffe56aSColin Percival		return 1
12348ffe56aSColin Percival	fi
12448ffe56aSColin Percival}
12548ffe56aSColin Percival
12648ffe56aSColin Percival# Set the working directory.
12748ffe56aSColin Percivalconfig_WorkDir () {
12848ffe56aSColin Percival	if [ -z ${WORKDIR} ]; then
12948ffe56aSColin Percival		WORKDIR=$1
13048ffe56aSColin Percival	else
13148ffe56aSColin Percival		return 1
13248ffe56aSColin Percival	fi
13348ffe56aSColin Percival}
13448ffe56aSColin Percival
13548ffe56aSColin Percival# Set the name of the server (pool) from which to fetch updates
13648ffe56aSColin Percivalconfig_ServerName () {
13748ffe56aSColin Percival	if [ -z ${SERVERNAME} ]; then
13848ffe56aSColin Percival		SERVERNAME=$1
13948ffe56aSColin Percival	else
14048ffe56aSColin Percival		return 1
14148ffe56aSColin Percival	fi
14248ffe56aSColin Percival}
14348ffe56aSColin Percival
14448ffe56aSColin Percival# Set the address to which 'cron' output will be mailed.
14548ffe56aSColin Percivalconfig_MailTo () {
14648ffe56aSColin Percival	if [ -z ${MAILTO} ]; then
14748ffe56aSColin Percival		MAILTO=$1
14848ffe56aSColin Percival	else
14948ffe56aSColin Percival		return 1
15048ffe56aSColin Percival	fi
15148ffe56aSColin Percival}
15248ffe56aSColin Percival
15348ffe56aSColin Percival# Set whether FreeBSD Update is allowed to add files (or directories, or
15448ffe56aSColin Percival# symlinks) which did not previously exist.
15548ffe56aSColin Percivalconfig_AllowAdd () {
15648ffe56aSColin Percival	if [ -z ${ALLOWADD} ]; then
15748ffe56aSColin Percival		case $1 in
15848ffe56aSColin Percival		[Yy][Ee][Ss])
15948ffe56aSColin Percival			ALLOWADD=yes
16048ffe56aSColin Percival			;;
16148ffe56aSColin Percival		[Nn][Oo])
16248ffe56aSColin Percival			ALLOWADD=no
16348ffe56aSColin Percival			;;
16448ffe56aSColin Percival		*)
16548ffe56aSColin Percival			return 1
16648ffe56aSColin Percival			;;
16748ffe56aSColin Percival		esac
16848ffe56aSColin Percival	else
16948ffe56aSColin Percival		return 1
17048ffe56aSColin Percival	fi
17148ffe56aSColin Percival}
17248ffe56aSColin Percival
17348ffe56aSColin Percival# Set whether FreeBSD Update is allowed to remove files/directories/symlinks.
17448ffe56aSColin Percivalconfig_AllowDelete () {
17548ffe56aSColin Percival	if [ -z ${ALLOWDELETE} ]; then
17648ffe56aSColin Percival		case $1 in
17748ffe56aSColin Percival		[Yy][Ee][Ss])
17848ffe56aSColin Percival			ALLOWDELETE=yes
17948ffe56aSColin Percival			;;
18048ffe56aSColin Percival		[Nn][Oo])
18148ffe56aSColin Percival			ALLOWDELETE=no
18248ffe56aSColin Percival			;;
18348ffe56aSColin Percival		*)
18448ffe56aSColin Percival			return 1
18548ffe56aSColin Percival			;;
18648ffe56aSColin Percival		esac
18748ffe56aSColin Percival	else
18848ffe56aSColin Percival		return 1
18948ffe56aSColin Percival	fi
19048ffe56aSColin Percival}
19148ffe56aSColin Percival
19248ffe56aSColin Percival# Set whether FreeBSD Update should keep existing inode ownership,
19348ffe56aSColin Percival# permissions, and flags, in the event that they have been modified locally
19448ffe56aSColin Percival# after the release.
19548ffe56aSColin Percivalconfig_KeepModifiedMetadata () {
19648ffe56aSColin Percival	if [ -z ${KEEPMODIFIEDMETADATA} ]; then
19748ffe56aSColin Percival		case $1 in
19848ffe56aSColin Percival		[Yy][Ee][Ss])
19948ffe56aSColin Percival			KEEPMODIFIEDMETADATA=yes
20048ffe56aSColin Percival			;;
20148ffe56aSColin Percival		[Nn][Oo])
20248ffe56aSColin Percival			KEEPMODIFIEDMETADATA=no
20348ffe56aSColin Percival			;;
20448ffe56aSColin Percival		*)
20548ffe56aSColin Percival			return 1
20648ffe56aSColin Percival			;;
20748ffe56aSColin Percival		esac
20848ffe56aSColin Percival	else
20948ffe56aSColin Percival		return 1
21048ffe56aSColin Percival	fi
21148ffe56aSColin Percival}
21248ffe56aSColin Percival
21348ffe56aSColin Percival# Add to the list of components which should be kept updated.
21448ffe56aSColin Percivalconfig_Components () {
21548ffe56aSColin Percival	for C in $@; do
21648ffe56aSColin Percival		COMPONENTS="${COMPONENTS} ${C}"
21748ffe56aSColin Percival	done
21848ffe56aSColin Percival}
21948ffe56aSColin Percival
22048ffe56aSColin Percival# Add to the list of paths under which updates will be ignored.
22148ffe56aSColin Percivalconfig_IgnorePaths () {
22248ffe56aSColin Percival	for C in $@; do
22348ffe56aSColin Percival		IGNOREPATHS="${IGNOREPATHS} ${C}"
22448ffe56aSColin Percival	done
22548ffe56aSColin Percival}
22648ffe56aSColin Percival
22708e23beeSColin Percival# Add to the list of paths which IDS should ignore.
22808e23beeSColin Percivalconfig_IDSIgnorePaths () {
22908e23beeSColin Percival	for C in $@; do
23008e23beeSColin Percival		IDSIGNOREPATHS="${IDSIGNOREPATHS} ${C}"
23108e23beeSColin Percival	done
23208e23beeSColin Percival}
23308e23beeSColin Percival
23448ffe56aSColin Percival# Add to the list of paths within which updates will be performed only if the
23548ffe56aSColin Percival# file on disk has not been modified locally.
23648ffe56aSColin Percivalconfig_UpdateIfUnmodified () {
23748ffe56aSColin Percival	for C in $@; do
23848ffe56aSColin Percival		UPDATEIFUNMODIFIED="${UPDATEIFUNMODIFIED} ${C}"
23948ffe56aSColin Percival	done
24048ffe56aSColin Percival}
24148ffe56aSColin Percival
242db6b0a61SColin Percival# Add to the list of paths within which updates to text files will be merged
243db6b0a61SColin Percival# instead of overwritten.
244db6b0a61SColin Percivalconfig_MergeChanges () {
245db6b0a61SColin Percival	for C in $@; do
246db6b0a61SColin Percival		MERGECHANGES="${MERGECHANGES} ${C}"
247db6b0a61SColin Percival	done
248db6b0a61SColin Percival}
249db6b0a61SColin Percival
25048ffe56aSColin Percival# Work on a FreeBSD installation mounted under $1
25148ffe56aSColin Percivalconfig_BaseDir () {
25248ffe56aSColin Percival	if [ -z ${BASEDIR} ]; then
25348ffe56aSColin Percival		BASEDIR=$1
25448ffe56aSColin Percival	else
25548ffe56aSColin Percival		return 1
25648ffe56aSColin Percival	fi
25748ffe56aSColin Percival}
25848ffe56aSColin Percival
259db6b0a61SColin Percival# When fetching upgrades, should we assume the user wants exactly the
260db6b0a61SColin Percival# components listed in COMPONENTS, rather than trying to guess based on
261db6b0a61SColin Percival# what's currently installed?
262db6b0a61SColin Percivalconfig_StrictComponents () {
263db6b0a61SColin Percival	if [ -z ${STRICTCOMPONENTS} ]; then
264db6b0a61SColin Percival		case $1 in
265db6b0a61SColin Percival		[Yy][Ee][Ss])
266db6b0a61SColin Percival			STRICTCOMPONENTS=yes
267db6b0a61SColin Percival			;;
268db6b0a61SColin Percival		[Nn][Oo])
269db6b0a61SColin Percival			STRICTCOMPONENTS=no
270db6b0a61SColin Percival			;;
271db6b0a61SColin Percival		*)
272db6b0a61SColin Percival			return 1
273db6b0a61SColin Percival			;;
274db6b0a61SColin Percival		esac
275db6b0a61SColin Percival	else
276db6b0a61SColin Percival		return 1
277db6b0a61SColin Percival	fi
278db6b0a61SColin Percival}
279db6b0a61SColin Percival
280db6b0a61SColin Percival# Upgrade to FreeBSD $1
281db6b0a61SColin Percivalconfig_TargetRelease () {
282db6b0a61SColin Percival	if [ -z ${TARGETRELEASE} ]; then
283db6b0a61SColin Percival		TARGETRELEASE=$1
284db6b0a61SColin Percival	else
285db6b0a61SColin Percival		return 1
286db6b0a61SColin Percival	fi
287d23dc1eeSColin Percival	if echo ${TARGETRELEASE} | grep -qE '^[0-9.]+$'; then
288d23dc1eeSColin Percival		TARGETRELEASE="${TARGETRELEASE}-RELEASE"
289d23dc1eeSColin Percival	fi
290db6b0a61SColin Percival}
291db6b0a61SColin Percival
29248ffe56aSColin Percival# Define what happens to output of utilities
29348ffe56aSColin Percivalconfig_VerboseLevel () {
29448ffe56aSColin Percival	if [ -z ${VERBOSELEVEL} ]; then
29548ffe56aSColin Percival		case $1 in
29648ffe56aSColin Percival		[Dd][Ee][Bb][Uu][Gg])
29748ffe56aSColin Percival			VERBOSELEVEL=debug
29848ffe56aSColin Percival			;;
29948ffe56aSColin Percival		[Nn][Oo][Ss][Tt][Aa][Tt][Ss])
30048ffe56aSColin Percival			VERBOSELEVEL=nostats
30148ffe56aSColin Percival			;;
30248ffe56aSColin Percival		[Ss][Tt][Aa][Tt][Ss])
30348ffe56aSColin Percival			VERBOSELEVEL=stats
30448ffe56aSColin Percival			;;
30548ffe56aSColin Percival		*)
30648ffe56aSColin Percival			return 1
30748ffe56aSColin Percival			;;
30848ffe56aSColin Percival		esac
30948ffe56aSColin Percival	else
31048ffe56aSColin Percival		return 1
31148ffe56aSColin Percival	fi
31248ffe56aSColin Percival}
31348ffe56aSColin Percival
31423d827efSSimon L. B. Nielsenconfig_BackupKernel () {
31523d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNEL} ]; then
31623d827efSSimon L. B. Nielsen		case $1 in
31723d827efSSimon L. B. Nielsen		[Yy][Ee][Ss])
31823d827efSSimon L. B. Nielsen			BACKUPKERNEL=yes
31923d827efSSimon L. B. Nielsen			;;
32023d827efSSimon L. B. Nielsen		[Nn][Oo])
32123d827efSSimon L. B. Nielsen			BACKUPKERNEL=no
32223d827efSSimon L. B. Nielsen			;;
32323d827efSSimon L. B. Nielsen		*)
32423d827efSSimon L. B. Nielsen			return 1
32523d827efSSimon L. B. Nielsen			;;
32623d827efSSimon L. B. Nielsen		esac
32723d827efSSimon L. B. Nielsen	else
32823d827efSSimon L. B. Nielsen		return 1
32923d827efSSimon L. B. Nielsen	fi
33023d827efSSimon L. B. Nielsen}
33123d827efSSimon L. B. Nielsen
33223d827efSSimon L. B. Nielsenconfig_BackupKernelDir () {
33323d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNELDIR} ]; then
33423d827efSSimon L. B. Nielsen		if [ -z "$1" ]; then
33523d827efSSimon L. B. Nielsen			echo "BackupKernelDir set to empty dir"
33623d827efSSimon L. B. Nielsen			return 1
33723d827efSSimon L. B. Nielsen		fi
33823d827efSSimon L. B. Nielsen
33923d827efSSimon L. B. Nielsen		# We check for some paths which would be extremely odd
34023d827efSSimon L. B. Nielsen		# to use, but which could cause a lot of problems if
34123d827efSSimon L. B. Nielsen		# used.
34223d827efSSimon L. B. Nielsen		case $1 in
34323d827efSSimon L. B. Nielsen		/|/bin|/boot|/etc|/lib|/libexec|/sbin|/usr|/var)
34423d827efSSimon L. B. Nielsen			echo "BackupKernelDir set to invalid path $1"
34523d827efSSimon L. B. Nielsen			return 1
34623d827efSSimon L. B. Nielsen			;;
34723d827efSSimon L. B. Nielsen		/*)
34823d827efSSimon L. B. Nielsen			BACKUPKERNELDIR=$1
34923d827efSSimon L. B. Nielsen			;;
35023d827efSSimon L. B. Nielsen		*)
35123d827efSSimon L. B. Nielsen			echo "BackupKernelDir ($1) is not an absolute path"
35223d827efSSimon L. B. Nielsen			return 1
35323d827efSSimon L. B. Nielsen			;;
35423d827efSSimon L. B. Nielsen		esac
35523d827efSSimon L. B. Nielsen	else
35623d827efSSimon L. B. Nielsen		return 1
35723d827efSSimon L. B. Nielsen	fi
35823d827efSSimon L. B. Nielsen}
35923d827efSSimon L. B. Nielsen
36023d827efSSimon L. B. Nielsenconfig_BackupKernelSymbolFiles () {
36123d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNELSYMBOLFILES} ]; then
36223d827efSSimon L. B. Nielsen		case $1 in
36323d827efSSimon L. B. Nielsen		[Yy][Ee][Ss])
36423d827efSSimon L. B. Nielsen			BACKUPKERNELSYMBOLFILES=yes
36523d827efSSimon L. B. Nielsen			;;
36623d827efSSimon L. B. Nielsen		[Nn][Oo])
36723d827efSSimon L. B. Nielsen			BACKUPKERNELSYMBOLFILES=no
36823d827efSSimon L. B. Nielsen			;;
36923d827efSSimon L. B. Nielsen		*)
37023d827efSSimon L. B. Nielsen			return 1
37123d827efSSimon L. B. Nielsen			;;
37223d827efSSimon L. B. Nielsen		esac
37323d827efSSimon L. B. Nielsen	else
37423d827efSSimon L. B. Nielsen		return 1
37523d827efSSimon L. B. Nielsen	fi
37623d827efSSimon L. B. Nielsen}
37723d827efSSimon L. B. Nielsen
37848ffe56aSColin Percival# Handle one line of configuration
37948ffe56aSColin Percivalconfigline () {
38048ffe56aSColin Percival	if [ $# -eq 0 ]; then
38148ffe56aSColin Percival		return
38248ffe56aSColin Percival	fi
38348ffe56aSColin Percival
38448ffe56aSColin Percival	OPT=$1
38548ffe56aSColin Percival	shift
38648ffe56aSColin Percival	config_${OPT} $@
38748ffe56aSColin Percival}
38848ffe56aSColin Percival
38948ffe56aSColin Percival#### Parameter handling functions.
39048ffe56aSColin Percival
39148ffe56aSColin Percival# Initialize parameters to null, just in case they're
39248ffe56aSColin Percival# set in the environment.
39348ffe56aSColin Percivalinit_params () {
39448ffe56aSColin Percival	# Configration settings
39548ffe56aSColin Percival	nullconfig
39648ffe56aSColin Percival
39748ffe56aSColin Percival	# No configuration file set yet
39848ffe56aSColin Percival	CONFFILE=""
39948ffe56aSColin Percival
40048ffe56aSColin Percival	# No commands specified yet
40148ffe56aSColin Percival	COMMANDS=""
40248ffe56aSColin Percival}
40348ffe56aSColin Percival
40448ffe56aSColin Percival# Parse the command line
40548ffe56aSColin Percivalparse_cmdline () {
40648ffe56aSColin Percival	while [ $# -gt 0 ]; do
40748ffe56aSColin Percival		case "$1" in
40848ffe56aSColin Percival		# Location of configuration file
40948ffe56aSColin Percival		-f)
41048ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi
41148ffe56aSColin Percival			if [ ! -z "${CONFFILE}" ]; then usage; fi
41248ffe56aSColin Percival			shift; CONFFILE="$1"
41348ffe56aSColin Percival			;;
41448ffe56aSColin Percival
41548ffe56aSColin Percival		# Configuration file equivalents
41648ffe56aSColin Percival		-b)
41748ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
41848ffe56aSColin Percival			config_BaseDir $1 || usage
41948ffe56aSColin Percival			;;
42048ffe56aSColin Percival		-d)
42148ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
42248ffe56aSColin Percival			config_WorkDir $1 || usage
42348ffe56aSColin Percival			;;
42448ffe56aSColin Percival		-k)
42548ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
42648ffe56aSColin Percival			config_KeyPrint $1 || usage
42748ffe56aSColin Percival			;;
42848ffe56aSColin Percival		-s)
42948ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
43048ffe56aSColin Percival			config_ServerName $1 || usage
43148ffe56aSColin Percival			;;
432db6b0a61SColin Percival		-r)
433db6b0a61SColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
434db6b0a61SColin Percival			config_TargetRelease $1 || usage
435db6b0a61SColin Percival			;;
43648ffe56aSColin Percival		-t)
43748ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
43848ffe56aSColin Percival			config_MailTo $1 || usage
43948ffe56aSColin Percival			;;
44048ffe56aSColin Percival		-v)
44148ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
44248ffe56aSColin Percival			config_VerboseLevel $1 || usage
44348ffe56aSColin Percival			;;
44448ffe56aSColin Percival
44548ffe56aSColin Percival		# Aliases for "-v debug" and "-v nostats"
44648ffe56aSColin Percival		--debug)
44748ffe56aSColin Percival			config_VerboseLevel debug || usage
44848ffe56aSColin Percival			;;
44948ffe56aSColin Percival		--no-stats)
45048ffe56aSColin Percival			config_VerboseLevel nostats || usage
45148ffe56aSColin Percival			;;
45248ffe56aSColin Percival
45348ffe56aSColin Percival		# Commands
45408e23beeSColin Percival		cron | fetch | upgrade | install | rollback | IDS)
45548ffe56aSColin Percival			COMMANDS="${COMMANDS} $1"
45648ffe56aSColin Percival			;;
45748ffe56aSColin Percival
45848ffe56aSColin Percival		# Anything else is an error
45948ffe56aSColin Percival		*)
46048ffe56aSColin Percival			usage
46148ffe56aSColin Percival			;;
46248ffe56aSColin Percival		esac
46348ffe56aSColin Percival		shift
46448ffe56aSColin Percival	done
46548ffe56aSColin Percival
46648ffe56aSColin Percival	# Make sure we have at least one command
46748ffe56aSColin Percival	if [ -z "${COMMANDS}" ]; then
46848ffe56aSColin Percival		usage
46948ffe56aSColin Percival	fi
47048ffe56aSColin Percival}
47148ffe56aSColin Percival
47248ffe56aSColin Percival# Parse the configuration file
47348ffe56aSColin Percivalparse_conffile () {
47448ffe56aSColin Percival	# If a configuration file was specified on the command line, check
47548ffe56aSColin Percival	# that it exists and is readable.
47648ffe56aSColin Percival	if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
47748ffe56aSColin Percival		echo -n "File does not exist "
47848ffe56aSColin Percival		echo -n "or is not readable: "
47948ffe56aSColin Percival		echo ${CONFFILE}
48048ffe56aSColin Percival		exit 1
48148ffe56aSColin Percival	fi
48248ffe56aSColin Percival
48348ffe56aSColin Percival	# If a configuration file was not specified on the command line,
48448ffe56aSColin Percival	# use the default configuration file path.  If that default does
48548ffe56aSColin Percival	# not exist, give up looking for any configuration.
48648ffe56aSColin Percival	if [ -z "${CONFFILE}" ]; then
48748ffe56aSColin Percival		CONFFILE="/etc/freebsd-update.conf"
48848ffe56aSColin Percival		if [ ! -r "${CONFFILE}" ]; then
48948ffe56aSColin Percival			return
49048ffe56aSColin Percival		fi
49148ffe56aSColin Percival	fi
49248ffe56aSColin Percival
49348ffe56aSColin Percival	# Save the configuration options specified on the command line, and
49448ffe56aSColin Percival	# clear all the options in preparation for reading the config file.
49548ffe56aSColin Percival	saveconfig
49648ffe56aSColin Percival	nullconfig
49748ffe56aSColin Percival
49848ffe56aSColin Percival	# Read the configuration file.  Anything after the first '#' is
49948ffe56aSColin Percival	# ignored, and any blank lines are ignored.
50048ffe56aSColin Percival	L=0
50148ffe56aSColin Percival	while read LINE; do
50248ffe56aSColin Percival		L=$(($L + 1))
50348ffe56aSColin Percival		LINEX=`echo "${LINE}" | cut -f 1 -d '#'`
50448ffe56aSColin Percival		if ! configline ${LINEX}; then
50548ffe56aSColin Percival			echo "Error processing configuration file, line $L:"
50648ffe56aSColin Percival			echo "==> ${LINE}"
50748ffe56aSColin Percival			exit 1
50848ffe56aSColin Percival		fi
50948ffe56aSColin Percival	done < ${CONFFILE}
51048ffe56aSColin Percival
51148ffe56aSColin Percival	# Merge the settings read from the configuration file with those
51248ffe56aSColin Percival	# provided at the command line.
51348ffe56aSColin Percival	mergeconfig
51448ffe56aSColin Percival}
51548ffe56aSColin Percival
51648ffe56aSColin Percival# Provide some default parameters
51748ffe56aSColin Percivaldefault_params () {
51848ffe56aSColin Percival	# Save any parameters already configured, and clear the slate
51948ffe56aSColin Percival	saveconfig
52048ffe56aSColin Percival	nullconfig
52148ffe56aSColin Percival
52248ffe56aSColin Percival	# Default configurations
52348ffe56aSColin Percival	config_WorkDir /var/db/freebsd-update
52448ffe56aSColin Percival	config_MailTo root
52548ffe56aSColin Percival	config_AllowAdd yes
52648ffe56aSColin Percival	config_AllowDelete yes
52748ffe56aSColin Percival	config_KeepModifiedMetadata yes
52848ffe56aSColin Percival	config_BaseDir /
52948ffe56aSColin Percival	config_VerboseLevel stats
530db6b0a61SColin Percival	config_StrictComponents no
53123d827efSSimon L. B. Nielsen	config_BackupKernel yes
53223d827efSSimon L. B. Nielsen	config_BackupKernelDir /boot/kernel.old
53323d827efSSimon L. B. Nielsen	config_BackupKernelSymbolFiles no
53448ffe56aSColin Percival
53548ffe56aSColin Percival	# Merge these defaults into the earlier-configured settings
53648ffe56aSColin Percival	mergeconfig
53748ffe56aSColin Percival}
53848ffe56aSColin Percival
53948ffe56aSColin Percival# Set utility output filtering options, based on ${VERBOSELEVEL}
54048ffe56aSColin Percivalfetch_setup_verboselevel () {
54148ffe56aSColin Percival	case ${VERBOSELEVEL} in
54248ffe56aSColin Percival	debug)
54348ffe56aSColin Percival		QUIETREDIR="/dev/stderr"
54448ffe56aSColin Percival		QUIETFLAG=" "
54548ffe56aSColin Percival		STATSREDIR="/dev/stderr"
54648ffe56aSColin Percival		DDSTATS=".."
54748ffe56aSColin Percival		XARGST="-t"
54848ffe56aSColin Percival		NDEBUG=" "
54948ffe56aSColin Percival		;;
55048ffe56aSColin Percival	nostats)
55148ffe56aSColin Percival		QUIETREDIR=""
55248ffe56aSColin Percival		QUIETFLAG=""
55348ffe56aSColin Percival		STATSREDIR="/dev/null"
55448ffe56aSColin Percival		DDSTATS=".."
55548ffe56aSColin Percival		XARGST=""
55648ffe56aSColin Percival		NDEBUG=""
55748ffe56aSColin Percival		;;
55848ffe56aSColin Percival	stats)
55948ffe56aSColin Percival		QUIETREDIR="/dev/null"
56048ffe56aSColin Percival		QUIETFLAG="-q"
56148ffe56aSColin Percival		STATSREDIR="/dev/stdout"
56248ffe56aSColin Percival		DDSTATS=""
56348ffe56aSColin Percival		XARGST=""
56448ffe56aSColin Percival		NDEBUG="-n"
56548ffe56aSColin Percival		;;
56648ffe56aSColin Percival	esac
56748ffe56aSColin Percival}
56848ffe56aSColin Percival
56948ffe56aSColin Percival# Perform sanity checks and set some final parameters
57048ffe56aSColin Percival# in preparation for fetching files.  Figure out which
57148ffe56aSColin Percival# set of updates should be downloaded: If the user is
57248ffe56aSColin Percival# running *-p[0-9]+, strip off the last part; if the
57348ffe56aSColin Percival# user is running -SECURITY, call it -RELEASE.  Chdir
57448ffe56aSColin Percival# into the working directory.
575211f2ba0SColin Percivalfetchupgrade_check_params () {
57648ffe56aSColin Percival	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
57748ffe56aSColin Percival
57848ffe56aSColin Percival	_SERVERNAME_z=\
57948ffe56aSColin Percival"SERVERNAME must be given via command line or configuration file."
58048ffe56aSColin Percival	_KEYPRINT_z="Key must be given via -k option or configuration file."
58148ffe56aSColin Percival	_KEYPRINT_bad="Invalid key fingerprint: "
58248ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
58348ffe56aSColin Percival
58448ffe56aSColin Percival	if [ -z "${SERVERNAME}" ]; then
58548ffe56aSColin Percival		echo -n "`basename $0`: "
58648ffe56aSColin Percival		echo "${_SERVERNAME_z}"
58748ffe56aSColin Percival		exit 1
58848ffe56aSColin Percival	fi
58948ffe56aSColin Percival	if [ -z "${KEYPRINT}" ]; then
59048ffe56aSColin Percival		echo -n "`basename $0`: "
59148ffe56aSColin Percival		echo "${_KEYPRINT_z}"
59248ffe56aSColin Percival		exit 1
59348ffe56aSColin Percival	fi
59448ffe56aSColin Percival	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
59548ffe56aSColin Percival		echo -n "`basename $0`: "
59648ffe56aSColin Percival		echo -n "${_KEYPRINT_bad}"
59748ffe56aSColin Percival		echo ${KEYPRINT}
59848ffe56aSColin Percival		exit 1
59948ffe56aSColin Percival	fi
60048ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
60148ffe56aSColin Percival		echo -n "`basename $0`: "
60248ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
60348ffe56aSColin Percival		echo ${WORKDIR}
60448ffe56aSColin Percival		exit 1
60548ffe56aSColin Percival	fi
606a2356430SColin Percival	chmod 700 ${WORKDIR}
60748ffe56aSColin Percival	cd ${WORKDIR} || exit 1
60848ffe56aSColin Percival
60948ffe56aSColin Percival	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
61048ffe56aSColin Percival	# to provide an upgrade path for FreeBSD Update 1.x users, since
61148ffe56aSColin Percival	# the kernels provided by FreeBSD Update 1.x are always labelled
61248ffe56aSColin Percival	# as X.Y-SECURITY.
61348ffe56aSColin Percival	RELNUM=`uname -r |
61448ffe56aSColin Percival	    sed -E 's,-p[0-9]+,,' |
61548ffe56aSColin Percival	    sed -E 's,-SECURITY,-RELEASE,'`
61648ffe56aSColin Percival	ARCH=`uname -m`
61748ffe56aSColin Percival	FETCHDIR=${RELNUM}/${ARCH}
618db6b0a61SColin Percival	PATCHDIR=${RELNUM}/${ARCH}/bp
61948ffe56aSColin Percival
62048ffe56aSColin Percival	# Figure out what directory contains the running kernel
62148ffe56aSColin Percival	BOOTFILE=`sysctl -n kern.bootfile`
62248ffe56aSColin Percival	KERNELDIR=${BOOTFILE%/kernel}
62348ffe56aSColin Percival	if ! [ -d ${KERNELDIR} ]; then
62448ffe56aSColin Percival		echo "Cannot identify running kernel"
62548ffe56aSColin Percival		exit 1
62648ffe56aSColin Percival	fi
62748ffe56aSColin Percival
6282c434b2cSColin Percival	# Figure out what kernel configuration is running.  We start with
6292c434b2cSColin Percival	# the output of `uname -i`, and then make the following adjustments:
6302c434b2cSColin Percival	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
6312c434b2cSColin Percival	# file says "ident SMP-GENERIC", I don't know...
6322c434b2cSColin Percival	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
6332c434b2cSColin Percival	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
6342c434b2cSColin Percival	# we're running an SMP kernel.  This mis-identification is a bug
6352c434b2cSColin Percival	# which was fixed in 6.2-STABLE.
6362c434b2cSColin Percival	KERNCONF=`uname -i`
6372c434b2cSColin Percival	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
6382c434b2cSColin Percival		KERNCONF=SMP
6392c434b2cSColin Percival	fi
6402c434b2cSColin Percival	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
6412c434b2cSColin Percival		if sysctl kern.version | grep -qE '/SMP$'; then
6422c434b2cSColin Percival			KERNCONF=SMP
6432c434b2cSColin Percival		fi
6442c434b2cSColin Percival	fi
6452c434b2cSColin Percival
64648ffe56aSColin Percival	# Define some paths
64748ffe56aSColin Percival	BSPATCH=/usr/bin/bspatch
64848ffe56aSColin Percival	SHA256=/sbin/sha256
64948ffe56aSColin Percival	PHTTPGET=/usr/libexec/phttpget
65048ffe56aSColin Percival
65148ffe56aSColin Percival	# Set up variables relating to VERBOSELEVEL
65248ffe56aSColin Percival	fetch_setup_verboselevel
65348ffe56aSColin Percival
65448ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
65548ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
65648ffe56aSColin Percival}
65748ffe56aSColin Percival
658211f2ba0SColin Percival# Perform sanity checks etc. before fetching updates.
659211f2ba0SColin Percivalfetch_check_params () {
660211f2ba0SColin Percival	fetchupgrade_check_params
661211f2ba0SColin Percival
662211f2ba0SColin Percival	if ! [ -z "${TARGETRELEASE}" ]; then
663211f2ba0SColin Percival		echo -n "`basename $0`: "
664211f2ba0SColin Percival		echo -n "-r option is meaningless with 'fetch' command.  "
665211f2ba0SColin Percival		echo "(Did you mean 'upgrade' instead?)"
666211f2ba0SColin Percival		exit 1
667211f2ba0SColin Percival	fi
668211f2ba0SColin Percival}
669211f2ba0SColin Percival
670db6b0a61SColin Percival# Perform sanity checks etc. before fetching upgrades.
671db6b0a61SColin Percivalupgrade_check_params () {
672211f2ba0SColin Percival	fetchupgrade_check_params
673db6b0a61SColin Percival
674db6b0a61SColin Percival	# Unless set otherwise, we're upgrading to the same kernel config.
675db6b0a61SColin Percival	NKERNCONF=${KERNCONF}
676db6b0a61SColin Percival
677db6b0a61SColin Percival	# We need TARGETRELEASE set
678db6b0a61SColin Percival	_TARGETRELEASE_z="Release target must be specified via -r option."
679db6b0a61SColin Percival	if [ -z "${TARGETRELEASE}" ]; then
680db6b0a61SColin Percival		echo -n "`basename $0`: "
681db6b0a61SColin Percival		echo "${_TARGETRELEASE_z}"
682db6b0a61SColin Percival		exit 1
683db6b0a61SColin Percival	fi
684db6b0a61SColin Percival
685db6b0a61SColin Percival	# The target release should be != the current release.
686db6b0a61SColin Percival	if [ "${TARGETRELEASE}" = "${RELNUM}" ]; then
687db6b0a61SColin Percival		echo -n "`basename $0`: "
688db6b0a61SColin Percival		echo "Cannot upgrade from ${RELNUM} to itself"
689db6b0a61SColin Percival		exit 1
690db6b0a61SColin Percival	fi
691db6b0a61SColin Percival
692db6b0a61SColin Percival	# Turning off AllowAdd or AllowDelete is a bad idea for upgrades.
693db6b0a61SColin Percival	if [ "${ALLOWADD}" = "no" ]; then
694db6b0a61SColin Percival		echo -n "`basename $0`: "
695db6b0a61SColin Percival		echo -n "WARNING: \"AllowAdd no\" is a bad idea "
696db6b0a61SColin Percival		echo "when upgrading between releases."
697db6b0a61SColin Percival		echo
698db6b0a61SColin Percival	fi
699db6b0a61SColin Percival	if [ "${ALLOWDELETE}" = "no" ]; then
700db6b0a61SColin Percival		echo -n "`basename $0`: "
701db6b0a61SColin Percival		echo -n "WARNING: \"AllowDelete no\" is a bad idea "
702db6b0a61SColin Percival		echo "when upgrading between releases."
703db6b0a61SColin Percival		echo
704db6b0a61SColin Percival	fi
705db6b0a61SColin Percival
706db6b0a61SColin Percival	# Set EDITOR to /usr/bin/vi if it isn't already set
707db6b0a61SColin Percival	: ${EDITOR:='/usr/bin/vi'}
708db6b0a61SColin Percival}
709db6b0a61SColin Percival
71048ffe56aSColin Percival# Perform sanity checks and set some final parameters in
71148ffe56aSColin Percival# preparation for installing updates.
71248ffe56aSColin Percivalinstall_check_params () {
71348ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
71448ffe56aSColin Percival	if [ `id -u` != 0 ]; then
71548ffe56aSColin Percival		echo "You must be root to run this."
71648ffe56aSColin Percival		exit 1
71748ffe56aSColin Percival	fi
71848ffe56aSColin Percival
7192328d598SColin Percival	# Check that securelevel <= 0.  Otherwise we can't update schg files.
7202328d598SColin Percival	if [ `sysctl -n kern.securelevel` -gt 0 ]; then
7212328d598SColin Percival		echo "Updates cannot be installed when the system securelevel"
7222328d598SColin Percival		echo "is greater than zero."
7232328d598SColin Percival		exit 1
7242328d598SColin Percival	fi
7252328d598SColin Percival
72648ffe56aSColin Percival	# Check that we have a working directory
72748ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
72848ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
72948ffe56aSColin Percival		echo -n "`basename $0`: "
73048ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
73148ffe56aSColin Percival		echo ${WORKDIR}
73248ffe56aSColin Percival		exit 1
73348ffe56aSColin Percival	fi
73448ffe56aSColin Percival	cd ${WORKDIR} || exit 1
73548ffe56aSColin Percival
73648ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
73748ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
73848ffe56aSColin Percival
73948ffe56aSColin Percival	# Check that we have updates ready to install
74048ffe56aSColin Percival	if ! [ -L ${BDHASH}-install ]; then
74148ffe56aSColin Percival		echo "No updates are available to install."
74248ffe56aSColin Percival		echo "Run '$0 fetch' first."
74348ffe56aSColin Percival		exit 1
74448ffe56aSColin Percival	fi
74548ffe56aSColin Percival	if ! [ -f ${BDHASH}-install/INDEX-OLD ] ||
74648ffe56aSColin Percival	    ! [ -f ${BDHASH}-install/INDEX-NEW ]; then
74748ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
74848ffe56aSColin Percival		echo "Re-run '$0 fetch'."
74948ffe56aSColin Percival		exit 1
75048ffe56aSColin Percival	fi
75123d827efSSimon L. B. Nielsen
75223d827efSSimon L. B. Nielsen	# Figure out what directory contains the running kernel
75323d827efSSimon L. B. Nielsen	BOOTFILE=`sysctl -n kern.bootfile`
75423d827efSSimon L. B. Nielsen	KERNELDIR=${BOOTFILE%/kernel}
75523d827efSSimon L. B. Nielsen	if ! [ -d ${KERNELDIR} ]; then
75623d827efSSimon L. B. Nielsen		echo "Cannot identify running kernel"
75723d827efSSimon L. B. Nielsen		exit 1
75823d827efSSimon L. B. Nielsen	fi
75948ffe56aSColin Percival}
76048ffe56aSColin Percival
76148ffe56aSColin Percival# Perform sanity checks and set some final parameters in
76248ffe56aSColin Percival# preparation for UNinstalling updates.
76348ffe56aSColin Percivalrollback_check_params () {
76448ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
76548ffe56aSColin Percival	if [ `id -u` != 0 ]; then
76648ffe56aSColin Percival		echo "You must be root to run this."
76748ffe56aSColin Percival		exit 1
76848ffe56aSColin Percival	fi
76948ffe56aSColin Percival
77048ffe56aSColin Percival	# Check that we have a working directory
77148ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
77248ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
77348ffe56aSColin Percival		echo -n "`basename $0`: "
77448ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
77548ffe56aSColin Percival		echo ${WORKDIR}
77648ffe56aSColin Percival		exit 1
77748ffe56aSColin Percival	fi
77848ffe56aSColin Percival	cd ${WORKDIR} || exit 1
77948ffe56aSColin Percival
78048ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
78148ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
78248ffe56aSColin Percival
78348ffe56aSColin Percival	# Check that we have updates ready to rollback
78448ffe56aSColin Percival	if ! [ -L ${BDHASH}-rollback ]; then
78548ffe56aSColin Percival		echo "No rollback directory found."
78648ffe56aSColin Percival		exit 1
78748ffe56aSColin Percival	fi
78848ffe56aSColin Percival	if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] ||
78948ffe56aSColin Percival	    ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then
79048ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
79148ffe56aSColin Percival		exit 1
79248ffe56aSColin Percival	fi
79348ffe56aSColin Percival}
79448ffe56aSColin Percival
79508e23beeSColin Percival# Perform sanity checks and set some final parameters
79608e23beeSColin Percival# in preparation for comparing the system against the
79708e23beeSColin Percival# published index.  Figure out which index we should
79808e23beeSColin Percival# compare against: If the user is running *-p[0-9]+,
79908e23beeSColin Percival# strip off the last part; if the user is running
80008e23beeSColin Percival# -SECURITY, call it -RELEASE.  Chdir into the working
80108e23beeSColin Percival# directory.
80208e23beeSColin PercivalIDS_check_params () {
80308e23beeSColin Percival	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
80408e23beeSColin Percival
80508e23beeSColin Percival	_SERVERNAME_z=\
80608e23beeSColin Percival"SERVERNAME must be given via command line or configuration file."
80708e23beeSColin Percival	_KEYPRINT_z="Key must be given via -k option or configuration file."
80808e23beeSColin Percival	_KEYPRINT_bad="Invalid key fingerprint: "
80908e23beeSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
81008e23beeSColin Percival
81108e23beeSColin Percival	if [ -z "${SERVERNAME}" ]; then
81208e23beeSColin Percival		echo -n "`basename $0`: "
81308e23beeSColin Percival		echo "${_SERVERNAME_z}"
81408e23beeSColin Percival		exit 1
81508e23beeSColin Percival	fi
81608e23beeSColin Percival	if [ -z "${KEYPRINT}" ]; then
81708e23beeSColin Percival		echo -n "`basename $0`: "
81808e23beeSColin Percival		echo "${_KEYPRINT_z}"
81908e23beeSColin Percival		exit 1
82008e23beeSColin Percival	fi
82108e23beeSColin Percival	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
82208e23beeSColin Percival		echo -n "`basename $0`: "
82308e23beeSColin Percival		echo -n "${_KEYPRINT_bad}"
82408e23beeSColin Percival		echo ${KEYPRINT}
82508e23beeSColin Percival		exit 1
82608e23beeSColin Percival	fi
82708e23beeSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
82808e23beeSColin Percival		echo -n "`basename $0`: "
82908e23beeSColin Percival		echo -n "${_WORKDIR_bad}"
83008e23beeSColin Percival		echo ${WORKDIR}
83108e23beeSColin Percival		exit 1
83208e23beeSColin Percival	fi
83308e23beeSColin Percival	cd ${WORKDIR} || exit 1
83408e23beeSColin Percival
83508e23beeSColin Percival	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
83608e23beeSColin Percival	# to provide an upgrade path for FreeBSD Update 1.x users, since
83708e23beeSColin Percival	# the kernels provided by FreeBSD Update 1.x are always labelled
83808e23beeSColin Percival	# as X.Y-SECURITY.
83908e23beeSColin Percival	RELNUM=`uname -r |
84008e23beeSColin Percival	    sed -E 's,-p[0-9]+,,' |
84108e23beeSColin Percival	    sed -E 's,-SECURITY,-RELEASE,'`
84208e23beeSColin Percival	ARCH=`uname -m`
84308e23beeSColin Percival	FETCHDIR=${RELNUM}/${ARCH}
84408e23beeSColin Percival	PATCHDIR=${RELNUM}/${ARCH}/bp
84508e23beeSColin Percival
84608e23beeSColin Percival	# Figure out what directory contains the running kernel
84708e23beeSColin Percival	BOOTFILE=`sysctl -n kern.bootfile`
84808e23beeSColin Percival	KERNELDIR=${BOOTFILE%/kernel}
84908e23beeSColin Percival	if ! [ -d ${KERNELDIR} ]; then
85008e23beeSColin Percival		echo "Cannot identify running kernel"
85108e23beeSColin Percival		exit 1
85208e23beeSColin Percival	fi
85308e23beeSColin Percival
85408e23beeSColin Percival	# Figure out what kernel configuration is running.  We start with
85508e23beeSColin Percival	# the output of `uname -i`, and then make the following adjustments:
85608e23beeSColin Percival	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
85708e23beeSColin Percival	# file says "ident SMP-GENERIC", I don't know...
85808e23beeSColin Percival	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
85908e23beeSColin Percival	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
86008e23beeSColin Percival	# we're running an SMP kernel.  This mis-identification is a bug
86108e23beeSColin Percival	# which was fixed in 6.2-STABLE.
86208e23beeSColin Percival	KERNCONF=`uname -i`
86308e23beeSColin Percival	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
86408e23beeSColin Percival		KERNCONF=SMP
86508e23beeSColin Percival	fi
86608e23beeSColin Percival	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
86708e23beeSColin Percival		if sysctl kern.version | grep -qE '/SMP$'; then
86808e23beeSColin Percival			KERNCONF=SMP
86908e23beeSColin Percival		fi
87008e23beeSColin Percival	fi
87108e23beeSColin Percival
87208e23beeSColin Percival	# Define some paths
87308e23beeSColin Percival	SHA256=/sbin/sha256
87408e23beeSColin Percival	PHTTPGET=/usr/libexec/phttpget
87508e23beeSColin Percival
87608e23beeSColin Percival	# Set up variables relating to VERBOSELEVEL
87708e23beeSColin Percival	fetch_setup_verboselevel
87808e23beeSColin Percival}
87908e23beeSColin Percival
88048ffe56aSColin Percival#### Core functionality -- the actual work gets done here
88148ffe56aSColin Percival
88248ffe56aSColin Percival# Use an SRV query to pick a server.  If the SRV query doesn't provide
88348ffe56aSColin Percival# a useful answer, use the server name specified by the user.
88448ffe56aSColin Percival# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
88548ffe56aSColin Percival# from that; or if no servers are returned, use ${SERVERNAME}.
88648ffe56aSColin Percival# This allows a user to specify "portsnap.freebsd.org" (in which case
88748ffe56aSColin Percival# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
88848ffe56aSColin Percival# (in which case portsnap will use that particular server, since there
88948ffe56aSColin Percival# won't be an SRV entry for that name).
89048ffe56aSColin Percival#
89148ffe56aSColin Percival# We ignore the Port field, since we are always going to use port 80.
89248ffe56aSColin Percival
89348ffe56aSColin Percival# Fetch the mirror list, but do not pick a mirror yet.  Returns 1 if
89448ffe56aSColin Percival# no mirrors are available for any reason.
89548ffe56aSColin Percivalfetch_pick_server_init () {
89648ffe56aSColin Percival	: > serverlist_tried
89748ffe56aSColin Percival
89848ffe56aSColin Percival# Check that host(1) exists (i.e., that the system wasn't built with the
89948ffe56aSColin Percival# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
90048ffe56aSColin Percival	if ! which -s host; then
90148ffe56aSColin Percival		: > serverlist_full
90248ffe56aSColin Percival		return 1
90348ffe56aSColin Percival	fi
90448ffe56aSColin Percival
90548ffe56aSColin Percival	echo -n "Looking up ${SERVERNAME} mirrors... "
90648ffe56aSColin Percival
90748ffe56aSColin Percival# Issue the SRV query and pull out the Priority, Weight, and Target fields.
90848ffe56aSColin Percival# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
90948ffe56aSColin Percival# "$name server selection ..."; we allow either format.
91048ffe56aSColin Percival	MLIST="_http._tcp.${SERVERNAME}"
91148ffe56aSColin Percival	host -t srv "${MLIST}" |
91248ffe56aSColin Percival	    sed -nE "s/${MLIST} (has SRV record|server selection) //p" |
91348ffe56aSColin Percival	    cut -f 1,2,4 -d ' ' |
91448ffe56aSColin Percival	    sed -e 's/\.$//' |
91548ffe56aSColin Percival	    sort > serverlist_full
91648ffe56aSColin Percival
91748ffe56aSColin Percival# If no records, give up -- we'll just use the server name we were given.
91848ffe56aSColin Percival	if [ `wc -l < serverlist_full` -eq 0 ]; then
91948ffe56aSColin Percival		echo "none found."
92048ffe56aSColin Percival		return 1
92148ffe56aSColin Percival	fi
92248ffe56aSColin Percival
92348ffe56aSColin Percival# Report how many mirrors we found.
92448ffe56aSColin Percival	echo `wc -l < serverlist_full` "mirrors found."
92548ffe56aSColin Percival
92648ffe56aSColin Percival# Generate a random seed for use in picking mirrors.  If HTTP_PROXY
92748ffe56aSColin Percival# is set, this will be used to generate the seed; otherwise, the seed
92848ffe56aSColin Percival# will be random.
92948ffe56aSColin Percival	if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
93048ffe56aSColin Percival		RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
93148ffe56aSColin Percival		    tr -d 'a-f' |
93248ffe56aSColin Percival		    cut -c 1-9`
93348ffe56aSColin Percival	else
93448ffe56aSColin Percival		RANDVALUE=`jot -r 1 0 999999999`
93548ffe56aSColin Percival	fi
93648ffe56aSColin Percival}
93748ffe56aSColin Percival
93848ffe56aSColin Percival# Pick a mirror.  Returns 1 if we have run out of mirrors to try.
93948ffe56aSColin Percivalfetch_pick_server () {
94048ffe56aSColin Percival# Generate a list of not-yet-tried mirrors
94148ffe56aSColin Percival	sort serverlist_tried |
94248ffe56aSColin Percival	    comm -23 serverlist_full - > serverlist
94348ffe56aSColin Percival
94448ffe56aSColin Percival# Have we run out of mirrors?
94548ffe56aSColin Percival	if [ `wc -l < serverlist` -eq 0 ]; then
94648ffe56aSColin Percival		echo "No mirrors remaining, giving up."
94748ffe56aSColin Percival		return 1
94848ffe56aSColin Percival	fi
94948ffe56aSColin Percival
95048ffe56aSColin Percival# Find the highest priority level (lowest numeric value).
95148ffe56aSColin Percival	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
95248ffe56aSColin Percival
95348ffe56aSColin Percival# Add up the weights of the response lines at that priority level.
95448ffe56aSColin Percival	SRV_WSUM=0;
95548ffe56aSColin Percival	while read X; do
95648ffe56aSColin Percival		case "$X" in
95748ffe56aSColin Percival		${SRV_PRIORITY}\ *)
95848ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
95948ffe56aSColin Percival			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
96048ffe56aSColin Percival			;;
96148ffe56aSColin Percival		esac
96248ffe56aSColin Percival	done < serverlist
96348ffe56aSColin Percival
96448ffe56aSColin Percival# If all the weights are 0, pretend that they are all 1 instead.
96548ffe56aSColin Percival	if [ ${SRV_WSUM} -eq 0 ]; then
96648ffe56aSColin Percival		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
96748ffe56aSColin Percival		SRV_W_ADD=1
96848ffe56aSColin Percival	else
96948ffe56aSColin Percival		SRV_W_ADD=0
97048ffe56aSColin Percival	fi
97148ffe56aSColin Percival
97248ffe56aSColin Percival# Pick a value between 0 and the sum of the weights - 1
97348ffe56aSColin Percival	SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
97448ffe56aSColin Percival
97548ffe56aSColin Percival# Read through the list of mirrors and set SERVERNAME.  Write the line
97648ffe56aSColin Percival# corresponding to the mirror we selected into serverlist_tried so that
97748ffe56aSColin Percival# we won't try it again.
97848ffe56aSColin Percival	while read X; do
97948ffe56aSColin Percival		case "$X" in
98048ffe56aSColin Percival		${SRV_PRIORITY}\ *)
98148ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
98248ffe56aSColin Percival			SRV_W=$(($SRV_W + $SRV_W_ADD))
98348ffe56aSColin Percival			if [ $SRV_RND -lt $SRV_W ]; then
98448ffe56aSColin Percival				SERVERNAME=`echo $X | cut -f 3 -d ' '`
98548ffe56aSColin Percival				echo "$X" >> serverlist_tried
98648ffe56aSColin Percival				break
98748ffe56aSColin Percival			else
98848ffe56aSColin Percival				SRV_RND=$(($SRV_RND - $SRV_W))
98948ffe56aSColin Percival			fi
99048ffe56aSColin Percival			;;
99148ffe56aSColin Percival		esac
99248ffe56aSColin Percival	done < serverlist
99348ffe56aSColin Percival}
99448ffe56aSColin Percival
99548ffe56aSColin Percival# Take a list of ${oldhash}|${newhash} and output a list of needed patches,
99648ffe56aSColin Percival# i.e., those for which we have ${oldhash} and don't have ${newhash}.
99748ffe56aSColin Percivalfetch_make_patchlist () {
99848ffe56aSColin Percival	grep -vE "^([0-9a-f]{64})\|\1$" |
99948ffe56aSColin Percival	    tr '|' ' ' |
100048ffe56aSColin Percival		while read X Y; do
100148ffe56aSColin Percival			if [ -f "files/${Y}.gz" ] ||
100248ffe56aSColin Percival			    [ ! -f "files/${X}.gz" ]; then
100348ffe56aSColin Percival				continue
100448ffe56aSColin Percival			fi
100548ffe56aSColin Percival			echo "${X}|${Y}"
100648ffe56aSColin Percival		done | uniq
100748ffe56aSColin Percival}
100848ffe56aSColin Percival
100948ffe56aSColin Percival# Print user-friendly progress statistics
101048ffe56aSColin Percivalfetch_progress () {
101148ffe56aSColin Percival	LNC=0
101248ffe56aSColin Percival	while read x; do
101348ffe56aSColin Percival		LNC=$(($LNC + 1))
101448ffe56aSColin Percival		if [ $(($LNC % 10)) = 0 ]; then
101548ffe56aSColin Percival			echo -n $LNC
101648ffe56aSColin Percival		elif [ $(($LNC % 2)) = 0 ]; then
101748ffe56aSColin Percival			echo -n .
101848ffe56aSColin Percival		fi
101948ffe56aSColin Percival	done
102048ffe56aSColin Percival	echo -n " "
102148ffe56aSColin Percival}
102248ffe56aSColin Percival
1023db6b0a61SColin Percival# Function for asking the user if everything is ok
1024db6b0a61SColin Percivalcontinuep () {
1025db6b0a61SColin Percival	while read -p "Does this look reasonable (y/n)? " CONTINUE; do
1026db6b0a61SColin Percival		case "${CONTINUE}" in
1027db6b0a61SColin Percival		y*)
1028db6b0a61SColin Percival			return 0
1029db6b0a61SColin Percival			;;
1030db6b0a61SColin Percival		n*)
1031db6b0a61SColin Percival			return 1
1032db6b0a61SColin Percival			;;
1033db6b0a61SColin Percival		esac
1034db6b0a61SColin Percival	done
1035db6b0a61SColin Percival}
1036db6b0a61SColin Percival
103748ffe56aSColin Percival# Initialize the working directory
103848ffe56aSColin Percivalworkdir_init () {
103948ffe56aSColin Percival	mkdir -p files
104048ffe56aSColin Percival	touch tINDEX.present
104148ffe56aSColin Percival}
104248ffe56aSColin Percival
104348ffe56aSColin Percival# Check that we have a public key with an appropriate hash, or
104448ffe56aSColin Percival# fetch the key if it doesn't exist.  Returns 1 if the key has
104548ffe56aSColin Percival# not yet been fetched.
104648ffe56aSColin Percivalfetch_key () {
104748ffe56aSColin Percival	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
104848ffe56aSColin Percival		return 0
104948ffe56aSColin Percival	fi
105048ffe56aSColin Percival
105148ffe56aSColin Percival	echo -n "Fetching public key from ${SERVERNAME}... "
105248ffe56aSColin Percival	rm -f pub.ssl
105348ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \
105448ffe56aSColin Percival	    2>${QUIETREDIR} || true
105548ffe56aSColin Percival	if ! [ -r pub.ssl ]; then
105648ffe56aSColin Percival		echo "failed."
105748ffe56aSColin Percival		return 1
105848ffe56aSColin Percival	fi
105948ffe56aSColin Percival	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
106048ffe56aSColin Percival		echo "key has incorrect hash."
106148ffe56aSColin Percival		rm -f pub.ssl
106248ffe56aSColin Percival		return 1
106348ffe56aSColin Percival	fi
106448ffe56aSColin Percival	echo "done."
106548ffe56aSColin Percival}
106648ffe56aSColin Percival
106748ffe56aSColin Percival# Fetch metadata signature, aka "tag".
106848ffe56aSColin Percivalfetch_tag () {
1069db6b0a61SColin Percival	echo -n "Fetching metadata signature "
1070db6b0a61SColin Percival	echo ${NDEBUG} "for ${RELNUM} from ${SERVERNAME}... "
107148ffe56aSColin Percival	rm -f latest.ssl
107248ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl	\
107348ffe56aSColin Percival	    2>${QUIETREDIR} || true
107448ffe56aSColin Percival	if ! [ -r latest.ssl ]; then
107548ffe56aSColin Percival		echo "failed."
107648ffe56aSColin Percival		return 1
107748ffe56aSColin Percival	fi
107848ffe56aSColin Percival
107948ffe56aSColin Percival	openssl rsautl -pubin -inkey pub.ssl -verify		\
108048ffe56aSColin Percival	    < latest.ssl > tag.new 2>${QUIETREDIR} || true
108148ffe56aSColin Percival	rm latest.ssl
108248ffe56aSColin Percival
108348ffe56aSColin Percival	if ! [ `wc -l < tag.new` = 1 ] ||
108448ffe56aSColin Percival	    ! grep -qE	\
108548ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
108648ffe56aSColin Percival		tag.new; then
108748ffe56aSColin Percival		echo "invalid signature."
108848ffe56aSColin Percival		return 1
108948ffe56aSColin Percival	fi
109048ffe56aSColin Percival
109148ffe56aSColin Percival	echo "done."
109248ffe56aSColin Percival
109348ffe56aSColin Percival	RELPATCHNUM=`cut -f 4 -d '|' < tag.new`
109448ffe56aSColin Percival	TINDEXHASH=`cut -f 5 -d '|' < tag.new`
109548ffe56aSColin Percival	EOLTIME=`cut -f 6 -d '|' < tag.new`
109648ffe56aSColin Percival}
109748ffe56aSColin Percival
109848ffe56aSColin Percival# Sanity-check the patch number in a tag, to make sure that we're not
109948ffe56aSColin Percival# going to "update" backwards and to prevent replay attacks.
110048ffe56aSColin Percivalfetch_tagsanity () {
110148ffe56aSColin Percival	# Check that we're not going to move from -pX to -pY with Y < X.
110248ffe56aSColin Percival	RELPX=`uname -r | sed -E 's,.*-,,'`
110348ffe56aSColin Percival	if echo ${RELPX} | grep -qE '^p[0-9]+$'; then
110448ffe56aSColin Percival		RELPX=`echo ${RELPX} | cut -c 2-`
110548ffe56aSColin Percival	else
110648ffe56aSColin Percival		RELPX=0
110748ffe56aSColin Percival	fi
110848ffe56aSColin Percival	if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then
110948ffe56aSColin Percival		echo
111048ffe56aSColin Percival		echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
111148ffe56aSColin Percival		echo " appear older than what"
111248ffe56aSColin Percival		echo "we are currently running (`uname -r`)!"
111348ffe56aSColin Percival		echo "Cowardly refusing to proceed any further."
111448ffe56aSColin Percival		return 1
111548ffe56aSColin Percival	fi
111648ffe56aSColin Percival
111748ffe56aSColin Percival	# If "tag" exists and corresponds to ${RELNUM}, make sure that
111848ffe56aSColin Percival	# it contains a patch number <= RELPATCHNUM, in order to protect
111948ffe56aSColin Percival	# against rollback (replay) attacks.
112048ffe56aSColin Percival	if [ -f tag ] &&
112148ffe56aSColin Percival	    grep -qE	\
112248ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
112348ffe56aSColin Percival		tag; then
112448ffe56aSColin Percival		LASTRELPATCHNUM=`cut -f 4 -d '|' < tag`
112548ffe56aSColin Percival
112648ffe56aSColin Percival		if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then
112748ffe56aSColin Percival			echo
112848ffe56aSColin Percival			echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
112948ffe56aSColin Percival			echo " are older than the"
113048ffe56aSColin Percival			echo -n "most recently seen updates"
113148ffe56aSColin Percival			echo " (${RELNUM}-p${LASTRELPATCHNUM})."
113248ffe56aSColin Percival			echo "Cowardly refusing to proceed any further."
113348ffe56aSColin Percival			return 1
113448ffe56aSColin Percival		fi
113548ffe56aSColin Percival	fi
113648ffe56aSColin Percival}
113748ffe56aSColin Percival
113848ffe56aSColin Percival# Fetch metadata index file
113948ffe56aSColin Percivalfetch_metadata_index () {
114048ffe56aSColin Percival	echo ${NDEBUG} "Fetching metadata index... "
114148ffe56aSColin Percival	rm -f ${TINDEXHASH}
114248ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH}
114348ffe56aSColin Percival	    2>${QUIETREDIR}
114448ffe56aSColin Percival	if ! [ -f ${TINDEXHASH} ]; then
114548ffe56aSColin Percival		echo "failed."
114648ffe56aSColin Percival		return 1
114748ffe56aSColin Percival	fi
114848ffe56aSColin Percival	if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then
114948ffe56aSColin Percival		echo "update metadata index corrupt."
115048ffe56aSColin Percival		return 1
115148ffe56aSColin Percival	fi
115248ffe56aSColin Percival	echo "done."
115348ffe56aSColin Percival}
115448ffe56aSColin Percival
115548ffe56aSColin Percival# Print an error message about signed metadata being bogus.
115648ffe56aSColin Percivalfetch_metadata_bogus () {
115748ffe56aSColin Percival	echo
115848ffe56aSColin Percival	echo "The update metadata$1 is correctly signed, but"
115948ffe56aSColin Percival	echo "failed an integrity check."
116048ffe56aSColin Percival	echo "Cowardly refusing to proceed any further."
116148ffe56aSColin Percival	return 1
116248ffe56aSColin Percival}
116348ffe56aSColin Percival
116448ffe56aSColin Percival# Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH}
116548ffe56aSColin Percival# with the lines not named in $@ from tINDEX.present (if that file exists).
116648ffe56aSColin Percivalfetch_metadata_index_merge () {
116748ffe56aSColin Percival	for METAFILE in $@; do
116848ffe56aSColin Percival		if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l`	\
116948ffe56aSColin Percival		    -ne 1 ]; then
117048ffe56aSColin Percival			fetch_metadata_bogus " index"
117148ffe56aSColin Percival			return 1
117248ffe56aSColin Percival		fi
117348ffe56aSColin Percival
117448ffe56aSColin Percival		grep -E "${METAFILE}\|" ${TINDEXHASH}
117548ffe56aSColin Percival	done |
117648ffe56aSColin Percival	    sort > tINDEX.wanted
117748ffe56aSColin Percival
117848ffe56aSColin Percival	if [ -f tINDEX.present ]; then
117948ffe56aSColin Percival		join -t '|' -v 2 tINDEX.wanted tINDEX.present |
118048ffe56aSColin Percival		    sort -m - tINDEX.wanted > tINDEX.new
118148ffe56aSColin Percival		rm tINDEX.wanted
118248ffe56aSColin Percival	else
118348ffe56aSColin Percival		mv tINDEX.wanted tINDEX.new
118448ffe56aSColin Percival	fi
118548ffe56aSColin Percival}
118648ffe56aSColin Percival
118748ffe56aSColin Percival# Sanity check all the lines of tINDEX.new.  Even if more metadata lines
118848ffe56aSColin Percival# are added by future versions of the server, this won't cause problems,
118948ffe56aSColin Percival# since the only lines which appear in tINDEX.new are the ones which we
119048ffe56aSColin Percival# specifically grepped out of ${TINDEXHASH}.
119148ffe56aSColin Percivalfetch_metadata_index_sanity () {
119248ffe56aSColin Percival	if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then
119348ffe56aSColin Percival		fetch_metadata_bogus " index"
119448ffe56aSColin Percival		return 1
119548ffe56aSColin Percival	fi
119648ffe56aSColin Percival}
119748ffe56aSColin Percival
119848ffe56aSColin Percival# Sanity check the metadata file $1.
119948ffe56aSColin Percivalfetch_metadata_sanity () {
120048ffe56aSColin Percival	# Some aliases to save space later: ${P} is a character which can
120148ffe56aSColin Percival	# appear in a path; ${M} is the four numeric metadata fields; and
120248ffe56aSColin Percival	# ${H} is a sha256 hash.
12030016a849SDag-Erling Smørgrav	P="[-+./:=%@_[~[:alnum:]]"
120448ffe56aSColin Percival	M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+"
120548ffe56aSColin Percival	H="[0-9a-f]{64}"
120648ffe56aSColin Percival
120748ffe56aSColin Percival	# Check that the first four fields make sense.
120848ffe56aSColin Percival	if gunzip -c < files/$1.gz |
120948ffe56aSColin Percival	    grep -qvE "^[a-z]+\|[0-9a-z]+\|${P}+\|[fdL-]\|"; then
121048ffe56aSColin Percival		fetch_metadata_bogus ""
121148ffe56aSColin Percival		return 1
121248ffe56aSColin Percival	fi
121348ffe56aSColin Percival
121448ffe56aSColin Percival	# Remove the first three fields.
121548ffe56aSColin Percival	gunzip -c < files/$1.gz |
121648ffe56aSColin Percival	    cut -f 4- -d '|' > sanitycheck.tmp
121748ffe56aSColin Percival
121848ffe56aSColin Percival	# Sanity check entries with type 'f'
121948ffe56aSColin Percival	if grep -E '^f' sanitycheck.tmp |
122048ffe56aSColin Percival	    grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then
122148ffe56aSColin Percival		fetch_metadata_bogus ""
122248ffe56aSColin Percival		return 1
122348ffe56aSColin Percival	fi
122448ffe56aSColin Percival
122548ffe56aSColin Percival	# Sanity check entries with type 'd'
122648ffe56aSColin Percival	if grep -E '^d' sanitycheck.tmp |
122748ffe56aSColin Percival	    grep -qvE "^d\|${M}\|\|\$"; then
122848ffe56aSColin Percival		fetch_metadata_bogus ""
122948ffe56aSColin Percival		return 1
123048ffe56aSColin Percival	fi
123148ffe56aSColin Percival
123248ffe56aSColin Percival	# Sanity check entries with type 'L'
123348ffe56aSColin Percival	if grep -E '^L' sanitycheck.tmp |
123448ffe56aSColin Percival	    grep -qvE "^L\|${M}\|${P}*\|\$"; then
123548ffe56aSColin Percival		fetch_metadata_bogus ""
123648ffe56aSColin Percival		return 1
123748ffe56aSColin Percival	fi
123848ffe56aSColin Percival
123948ffe56aSColin Percival	# Sanity check entries with type '-'
124048ffe56aSColin Percival	if grep -E '^-' sanitycheck.tmp |
124148ffe56aSColin Percival	    grep -qvE "^-\|\|\|\|\|\|"; then
124248ffe56aSColin Percival		fetch_metadata_bogus ""
124348ffe56aSColin Percival		return 1
124448ffe56aSColin Percival	fi
124548ffe56aSColin Percival
124648ffe56aSColin Percival	# Clean up
124748ffe56aSColin Percival	rm sanitycheck.tmp
124848ffe56aSColin Percival}
124948ffe56aSColin Percival
125048ffe56aSColin Percival# Fetch the metadata index and metadata files listed in $@,
125148ffe56aSColin Percival# taking advantage of metadata patches where possible.
125248ffe56aSColin Percivalfetch_metadata () {
125348ffe56aSColin Percival	fetch_metadata_index || return 1
125448ffe56aSColin Percival	fetch_metadata_index_merge $@ || return 1
125548ffe56aSColin Percival	fetch_metadata_index_sanity || return 1
125648ffe56aSColin Percival
125748ffe56aSColin Percival	# Generate a list of wanted metadata patches
125848ffe56aSColin Percival	join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new |
125948ffe56aSColin Percival	    fetch_make_patchlist > patchlist
126048ffe56aSColin Percival
126148ffe56aSColin Percival	if [ -s patchlist ]; then
126248ffe56aSColin Percival		# Attempt to fetch metadata patches
126348ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
126448ffe56aSColin Percival		echo ${NDEBUG} "metadata patches.${DDSTATS}"
126548ffe56aSColin Percival		tr '|' '-' < patchlist |
126648ffe56aSColin Percival		    lam -s "${FETCHDIR}/tp/" - -s ".gz" |
126748ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
126848ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
126948ffe56aSColin Percival		echo "done."
127048ffe56aSColin Percival
127148ffe56aSColin Percival		# Attempt to apply metadata patches
127248ffe56aSColin Percival		echo -n "Applying metadata patches... "
127348ffe56aSColin Percival		tr '|' ' ' < patchlist |
127448ffe56aSColin Percival		    while read X Y; do
127548ffe56aSColin Percival			if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
127648ffe56aSColin Percival			gunzip -c < ${X}-${Y}.gz > diff
127748ffe56aSColin Percival			gunzip -c < files/${X}.gz > diff-OLD
127848ffe56aSColin Percival
127948ffe56aSColin Percival			# Figure out which lines are being added and removed
128048ffe56aSColin Percival			grep -E '^-' diff |
128148ffe56aSColin Percival			    cut -c 2- |
128248ffe56aSColin Percival			    while read PREFIX; do
128348ffe56aSColin Percival				look "${PREFIX}" diff-OLD
128448ffe56aSColin Percival			    done |
128548ffe56aSColin Percival			    sort > diff-rm
128648ffe56aSColin Percival			grep -E '^\+' diff |
128748ffe56aSColin Percival			    cut -c 2- > diff-add
128848ffe56aSColin Percival
128948ffe56aSColin Percival			# Generate the new file
129048ffe56aSColin Percival			comm -23 diff-OLD diff-rm |
129148ffe56aSColin Percival			    sort - diff-add > diff-NEW
129248ffe56aSColin Percival
129348ffe56aSColin Percival			if [ `${SHA256} -q diff-NEW` = ${Y} ]; then
129448ffe56aSColin Percival				mv diff-NEW files/${Y}
129548ffe56aSColin Percival				gzip -n files/${Y}
129648ffe56aSColin Percival			else
129748ffe56aSColin Percival				mv diff-NEW ${Y}.bad
129848ffe56aSColin Percival			fi
129948ffe56aSColin Percival			rm -f ${X}-${Y}.gz diff
130048ffe56aSColin Percival			rm -f diff-OLD diff-NEW diff-add diff-rm
130148ffe56aSColin Percival		done 2>${QUIETREDIR}
130248ffe56aSColin Percival		echo "done."
130348ffe56aSColin Percival	fi
130448ffe56aSColin Percival
130548ffe56aSColin Percival	# Update metadata without patches
130648ffe56aSColin Percival	cut -f 2 -d '|' < tINDEX.new |
130748ffe56aSColin Percival	    while read Y; do
130848ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
130948ffe56aSColin Percival			echo ${Y};
131048ffe56aSColin Percival		fi
1311bce02f98SColin Percival	    done |
1312bce02f98SColin Percival	    sort -u > filelist
131348ffe56aSColin Percival
131448ffe56aSColin Percival	if [ -s filelist ]; then
131548ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
131648ffe56aSColin Percival		echo ${NDEBUG} "metadata files... "
131748ffe56aSColin Percival		lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist |
131848ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
131948ffe56aSColin Percival		    2>${QUIETREDIR}
132048ffe56aSColin Percival
132148ffe56aSColin Percival		while read Y; do
132248ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
132348ffe56aSColin Percival				echo "failed."
132448ffe56aSColin Percival				return 1
132548ffe56aSColin Percival			fi
132648ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
132748ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
132848ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
132948ffe56aSColin Percival			else
133048ffe56aSColin Percival				echo "metadata is corrupt."
133148ffe56aSColin Percival				return 1
133248ffe56aSColin Percival			fi
133348ffe56aSColin Percival		done < filelist
133448ffe56aSColin Percival		echo "done."
133548ffe56aSColin Percival	fi
133648ffe56aSColin Percival
133748ffe56aSColin Percival# Sanity-check the metadata files.
133848ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new > filelist
133948ffe56aSColin Percival	while read X; do
134048ffe56aSColin Percival		fetch_metadata_sanity ${X} || return 1
134148ffe56aSColin Percival	done < filelist
134248ffe56aSColin Percival
134348ffe56aSColin Percival# Remove files which are no longer needed
134448ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.present |
134548ffe56aSColin Percival	    sort > oldfiles
134648ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new |
134748ffe56aSColin Percival	    sort |
134848ffe56aSColin Percival	    comm -13 - oldfiles |
134948ffe56aSColin Percival	    lam -s "files/" - -s ".gz" |
135048ffe56aSColin Percival	    xargs rm -f
135148ffe56aSColin Percival	rm patchlist filelist oldfiles
135248ffe56aSColin Percival	rm ${TINDEXHASH}
135348ffe56aSColin Percival
135448ffe56aSColin Percival# We're done!
135548ffe56aSColin Percival	mv tINDEX.new tINDEX.present
135648ffe56aSColin Percival	mv tag.new tag
135748ffe56aSColin Percival
135848ffe56aSColin Percival	return 0
135948ffe56aSColin Percival}
136048ffe56aSColin Percival
1361db6b0a61SColin Percival# Extract a subset of a downloaded metadata file containing only the parts
1362db6b0a61SColin Percival# which are listed in COMPONENTS.
1363db6b0a61SColin Percivalfetch_filter_metadata_components () {
1364db6b0a61SColin Percival	METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
1365db6b0a61SColin Percival	gunzip -c < files/${METAHASH}.gz > $1.all
1366db6b0a61SColin Percival
1367db6b0a61SColin Percival	# Fish out the lines belonging to components we care about.
1368db6b0a61SColin Percival	for C in ${COMPONENTS}; do
1369db6b0a61SColin Percival		look "`echo ${C} | tr '/' '|'`|" $1.all
1370db6b0a61SColin Percival	done > $1
1371db6b0a61SColin Percival
1372db6b0a61SColin Percival	# Remove temporary file.
1373db6b0a61SColin Percival	rm $1.all
1374db6b0a61SColin Percival}
1375db6b0a61SColin Percival
1376b698a3abSColin Percival# Generate a filtered version of the metadata file $1 from the downloaded
137748ffe56aSColin Percival# file, by fishing out the lines corresponding to components we're trying
137848ffe56aSColin Percival# to keep updated, and then removing lines corresponding to paths we want
137948ffe56aSColin Percival# to ignore.
138048ffe56aSColin Percivalfetch_filter_metadata () {
138148ffe56aSColin Percival	# Fish out the lines belonging to components we care about.
1382db6b0a61SColin Percival	fetch_filter_metadata_components $1
1383db6b0a61SColin Percival
138448ffe56aSColin Percival	# Canonicalize directory names by removing any trailing / in
138548ffe56aSColin Percival	# order to avoid listing directories multiple times if they
138648ffe56aSColin Percival	# belong to multiple components.  Turning "/" into "" doesn't
138748ffe56aSColin Percival	# matter, since we add a leading "/" when we use paths later.
1388db6b0a61SColin Percival	cut -f 3- -d '|' $1 |
138948ffe56aSColin Percival	    sed -e 's,/|d|,|d|,' |
139048ffe56aSColin Percival	    sort -u > $1.tmp
139148ffe56aSColin Percival
139248ffe56aSColin Percival	# Figure out which lines to ignore and remove them.
139348ffe56aSColin Percival	for X in ${IGNOREPATHS}; do
139448ffe56aSColin Percival		grep -E "^${X}" $1.tmp
139548ffe56aSColin Percival	done |
139648ffe56aSColin Percival	    sort -u |
139748ffe56aSColin Percival	    comm -13 - $1.tmp > $1
139848ffe56aSColin Percival
139948ffe56aSColin Percival	# Remove temporary files.
1400db6b0a61SColin Percival	rm $1.tmp
140148ffe56aSColin Percival}
140248ffe56aSColin Percival
1403db6b0a61SColin Percival# Filter the metadata file $1 by adding lines with "/boot/$2"
1404bce02f98SColin Percival# replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the
1405db6b0a61SColin Percival# trailing "/kernel"); and if "/boot/$2" does not exist, remove
1406bce02f98SColin Percival# the original lines which start with that.
1407bce02f98SColin Percival# Put another way: Deal with the fact that the FOO kernel is sometimes
1408bce02f98SColin Percival# installed in /boot/FOO/ and is sometimes installed elsewhere.
140948ffe56aSColin Percivalfetch_filter_kernel_names () {
1410db6b0a61SColin Percival	grep ^/boot/$2 $1 |
1411db6b0a61SColin Percival	    sed -e "s,/boot/$2,${KERNELDIR},g" |
141248ffe56aSColin Percival	    sort - $1 > $1.tmp
141348ffe56aSColin Percival	mv $1.tmp $1
1414bce02f98SColin Percival
1415db6b0a61SColin Percival	if ! [ -d /boot/$2 ]; then
1416db6b0a61SColin Percival		grep -v ^/boot/$2 $1 > $1.tmp
1417bce02f98SColin Percival		mv $1.tmp $1
1418bce02f98SColin Percival	fi
141948ffe56aSColin Percival}
142048ffe56aSColin Percival
142148ffe56aSColin Percival# For all paths appearing in $1 or $3, inspect the system
142248ffe56aSColin Percival# and generate $2 describing what is currently installed.
142348ffe56aSColin Percivalfetch_inspect_system () {
142448ffe56aSColin Percival	# No errors yet...
142548ffe56aSColin Percival	rm -f .err
142648ffe56aSColin Percival
142748ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
142848ffe56aSColin Percival	echo -n "Inspecting system... "
142948ffe56aSColin Percival
143048ffe56aSColin Percival	# Generate list of files to inspect
143148ffe56aSColin Percival	cat $1 $3 |
143248ffe56aSColin Percival	    cut -f 1 -d '|' |
143348ffe56aSColin Percival	    sort -u > filelist
143448ffe56aSColin Percival
143548ffe56aSColin Percival	# Examine each file and output lines of the form
143648ffe56aSColin Percival	# /path/to/file|type|device-inum|user|group|perm|flags|value
143748ffe56aSColin Percival	# sorted by device and inode number.
143848ffe56aSColin Percival	while read F; do
143948ffe56aSColin Percival		# If the symlink/file/directory does not exist, record this.
144048ffe56aSColin Percival		if ! [ -e ${BASEDIR}/${F} ]; then
144148ffe56aSColin Percival			echo "${F}|-||||||"
144248ffe56aSColin Percival			continue
144348ffe56aSColin Percival		fi
144448ffe56aSColin Percival		if ! [ -r ${BASEDIR}/${F} ]; then
144548ffe56aSColin Percival			echo "Cannot read file: ${BASEDIR}/${F}"	\
144648ffe56aSColin Percival			    >/dev/stderr
144748ffe56aSColin Percival			touch .err
144848ffe56aSColin Percival			return 1
144948ffe56aSColin Percival		fi
145048ffe56aSColin Percival
145148ffe56aSColin Percival		# Otherwise, output an index line.
145248ffe56aSColin Percival		if [ -L ${BASEDIR}/${F} ]; then
145348ffe56aSColin Percival			echo -n "${F}|L|"
145448ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
145548ffe56aSColin Percival			readlink ${BASEDIR}/${F};
145648ffe56aSColin Percival		elif [ -f ${BASEDIR}/${F} ]; then
145748ffe56aSColin Percival			echo -n "${F}|f|"
145848ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
145948ffe56aSColin Percival			sha256 -q ${BASEDIR}/${F};
146048ffe56aSColin Percival		elif [ -d ${BASEDIR}/${F} ]; then
146148ffe56aSColin Percival			echo -n "${F}|d|"
146248ffe56aSColin Percival			stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
146348ffe56aSColin Percival		else
146448ffe56aSColin Percival			echo "Unknown file type: ${BASEDIR}/${F}"	\
146548ffe56aSColin Percival			    >/dev/stderr
146648ffe56aSColin Percival			touch .err
146748ffe56aSColin Percival			return 1
146848ffe56aSColin Percival		fi
146948ffe56aSColin Percival	done < filelist |
147048ffe56aSColin Percival	    sort -k 3,3 -t '|' > $2.tmp
147148ffe56aSColin Percival	rm filelist
147248ffe56aSColin Percival
14736dcc68c8SBenedict Reuschling	# Check if an error occurred during system inspection
147448ffe56aSColin Percival	if [ -f .err ]; then
147548ffe56aSColin Percival		return 1
147648ffe56aSColin Percival	fi
147748ffe56aSColin Percival
147848ffe56aSColin Percival	# Convert to the form
147948ffe56aSColin Percival	# /path/to/file|type|user|group|perm|flags|value|hlink
148048ffe56aSColin Percival	# by resolving identical device and inode numbers into hard links.
148148ffe56aSColin Percival	cut -f 1,3 -d '|' $2.tmp |
148248ffe56aSColin Percival	    sort -k 1,1 -t '|' |
148348ffe56aSColin Percival	    sort -s -u -k 2,2 -t '|' |
148448ffe56aSColin Percival	    join -1 2 -2 3 -t '|' - $2.tmp |
148548ffe56aSColin Percival	    awk -F \| -v OFS=\|		\
148648ffe56aSColin Percival		'{
148748ffe56aSColin Percival		    if (($2 == $3) || ($4 == "-"))
148848ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,""
148948ffe56aSColin Percival		    else
149048ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,$2
149148ffe56aSColin Percival		}' |
149248ffe56aSColin Percival	    sort > $2
149348ffe56aSColin Percival	rm $2.tmp
149448ffe56aSColin Percival
149548ffe56aSColin Percival	# We're finished looking around
149648ffe56aSColin Percival	echo "done."
149748ffe56aSColin Percival}
149848ffe56aSColin Percival
1499db6b0a61SColin Percival# For any paths matching ${MERGECHANGES}, compare $1 and $2 and find any
1500db6b0a61SColin Percival# files which differ; generate $3 containing these paths and the old hashes.
1501db6b0a61SColin Percivalfetch_filter_mergechanges () {
1502db6b0a61SColin Percival	# Pull out the paths and hashes of the files matching ${MERGECHANGES}.
1503db6b0a61SColin Percival	for F in $1 $2; do
1504db6b0a61SColin Percival		for X in ${MERGECHANGES}; do
1505db6b0a61SColin Percival			grep -E "^${X}" ${F}
1506db6b0a61SColin Percival		done |
1507db6b0a61SColin Percival		    cut -f 1,2,7 -d '|' |
1508db6b0a61SColin Percival		    sort > ${F}-values
1509db6b0a61SColin Percival	done
1510db6b0a61SColin Percival
1511db6b0a61SColin Percival	# Any line in $2-values which doesn't appear in $1-values and is a
1512db6b0a61SColin Percival	# file means that we should list the path in $3.
1513db6b0a61SColin Percival	comm -13 $1-values $2-values |
1514db6b0a61SColin Percival	    fgrep '|f|' |
1515db6b0a61SColin Percival	    cut -f 1 -d '|' > $2-paths
1516db6b0a61SColin Percival
1517db6b0a61SColin Percival	# For each path, pull out one (and only one!) entry from $1-values.
1518db6b0a61SColin Percival	# Note that we cannot distinguish which "old" version the user made
1519db6b0a61SColin Percival	# changes to; but hopefully any changes which occur due to security
1520db6b0a61SColin Percival	# updates will exist in both the "new" version and the version which
1521db6b0a61SColin Percival	# the user has installed, so the merging will still work.
1522db6b0a61SColin Percival	while read X; do
1523db6b0a61SColin Percival		look "${X}|" $1-values |
1524db6b0a61SColin Percival		    head -1
1525db6b0a61SColin Percival	done < $2-paths > $3
1526db6b0a61SColin Percival
1527db6b0a61SColin Percival	# Clean up
1528db6b0a61SColin Percival	rm $1-values $2-values $2-paths
1529db6b0a61SColin Percival}
1530db6b0a61SColin Percival
153148ffe56aSColin Percival# For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123]
1532db6b0a61SColin Percival# which correspond to lines in $2 with hashes not matching $1 or $3, unless
1533db6b0a61SColin Percival# the paths are listed in $4.  For entries in $2 marked "not present"
1534db6b0a61SColin Percival# (aka. type -), remove lines from $[123] unless there is a corresponding
1535db6b0a61SColin Percival# entry in $1.
153648ffe56aSColin Percivalfetch_filter_unmodified_notpresent () {
153748ffe56aSColin Percival	# Figure out which lines of $1 and $3 correspond to bits which
153848ffe56aSColin Percival	# should only be updated if they haven't changed, and fish out
153948ffe56aSColin Percival	# the (path, type, value) tuples.
154048ffe56aSColin Percival	# NOTE: We don't consider a file to be "modified" if it matches
154148ffe56aSColin Percival	# the hash from $3.
154248ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
154348ffe56aSColin Percival		grep -E "^${X}" $1
154448ffe56aSColin Percival		grep -E "^${X}" $3
154548ffe56aSColin Percival	done |
154648ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
154748ffe56aSColin Percival	    sort > $1-values
154848ffe56aSColin Percival
154948ffe56aSColin Percival	# Do the same for $2.
155048ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
155148ffe56aSColin Percival		grep -E "^${X}" $2
155248ffe56aSColin Percival	done |
155348ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
155448ffe56aSColin Percival	    sort > $2-values
155548ffe56aSColin Percival
155648ffe56aSColin Percival	# Any entry in $2-values which is not in $1-values corresponds to
1557db6b0a61SColin Percival	# a path which we need to remove from $1, $2, and $3, unless it
1558db6b0a61SColin Percival	# that path appears in $4.
1559db6b0a61SColin Percival	comm -13 $1-values $2-values |
1560db6b0a61SColin Percival	    sort -t '|' -k 1,1 > mlines.tmp
1561db6b0a61SColin Percival	cut -f 1 -d '|' $4 |
1562db6b0a61SColin Percival	    sort |
1563db6b0a61SColin Percival	    join -v 2 -t '|' - mlines.tmp |
1564db6b0a61SColin Percival	    sort > mlines
1565db6b0a61SColin Percival	rm $1-values $2-values mlines.tmp
156648ffe56aSColin Percival
156748ffe56aSColin Percival	# Any lines in $2 which are not in $1 AND are "not present" lines
156848ffe56aSColin Percival	# also belong in mlines.
156948ffe56aSColin Percival	comm -13 $1 $2 |
157048ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
157148ffe56aSColin Percival	    fgrep '|-|' >> mlines
157248ffe56aSColin Percival
157348ffe56aSColin Percival	# Remove lines from $1, $2, and $3
157448ffe56aSColin Percival	for X in $1 $2 $3; do
157548ffe56aSColin Percival		sort -t '|' -k 1,1 ${X} > ${X}.tmp
157648ffe56aSColin Percival		cut -f 1 -d '|' < mlines |
157748ffe56aSColin Percival		    sort |
157848ffe56aSColin Percival		    join -v 2 -t '|' - ${X}.tmp |
157948ffe56aSColin Percival		    sort > ${X}
158048ffe56aSColin Percival		rm ${X}.tmp
158148ffe56aSColin Percival	done
158248ffe56aSColin Percival
158348ffe56aSColin Percival	# Store a list of the modified files, for future reference
158448ffe56aSColin Percival	fgrep -v '|-|' mlines |
158548ffe56aSColin Percival	    cut -f 1 -d '|' > modifiedfiles
158648ffe56aSColin Percival	rm mlines
158748ffe56aSColin Percival}
158848ffe56aSColin Percival
158948ffe56aSColin Percival# For each entry in $1 of type -, remove any corresponding
159048ffe56aSColin Percival# entry from $2 if ${ALLOWADD} != "yes".  Remove all entries
159148ffe56aSColin Percival# of type - from $1.
159248ffe56aSColin Percivalfetch_filter_allowadd () {
159348ffe56aSColin Percival	cut -f 1,2 -d '|' < $1 |
159448ffe56aSColin Percival	    fgrep '|-' |
159548ffe56aSColin Percival	    cut -f 1 -d '|' > filesnotpresent
159648ffe56aSColin Percival
159748ffe56aSColin Percival	if [ ${ALLOWADD} != "yes" ]; then
159848ffe56aSColin Percival		sort < $2 |
159948ffe56aSColin Percival		    join -v 1 -t '|' - filesnotpresent |
160048ffe56aSColin Percival		    sort > $2.tmp
160148ffe56aSColin Percival		mv $2.tmp $2
160248ffe56aSColin Percival	fi
160348ffe56aSColin Percival
160448ffe56aSColin Percival	sort < $1 |
160548ffe56aSColin Percival	    join -v 1 -t '|' - filesnotpresent |
160648ffe56aSColin Percival	    sort > $1.tmp
160748ffe56aSColin Percival	mv $1.tmp $1
160848ffe56aSColin Percival	rm filesnotpresent
160948ffe56aSColin Percival}
161048ffe56aSColin Percival
161148ffe56aSColin Percival# If ${ALLOWDELETE} != "yes", then remove any entries from $1
161248ffe56aSColin Percival# which don't correspond to entries in $2.
161348ffe56aSColin Percivalfetch_filter_allowdelete () {
161448ffe56aSColin Percival	# Produce a lists ${PATH}|${TYPE}
161548ffe56aSColin Percival	for X in $1 $2; do
161648ffe56aSColin Percival		cut -f 1-2 -d '|' < ${X} |
161748ffe56aSColin Percival		    sort -u > ${X}.nodes
161848ffe56aSColin Percival	done
161948ffe56aSColin Percival
162048ffe56aSColin Percival	# Figure out which lines need to be removed from $1.
162148ffe56aSColin Percival	if [ ${ALLOWDELETE} != "yes" ]; then
162248ffe56aSColin Percival		comm -23 $1.nodes $2.nodes > $1.badnodes
162348ffe56aSColin Percival	else
162448ffe56aSColin Percival		: > $1.badnodes
162548ffe56aSColin Percival	fi
162648ffe56aSColin Percival
162748ffe56aSColin Percival	# Remove the relevant lines from $1
162848ffe56aSColin Percival	while read X; do
162948ffe56aSColin Percival		look "${X}|" $1
163048ffe56aSColin Percival	done < $1.badnodes |
163148ffe56aSColin Percival	    comm -13 - $1 > $1.tmp
163248ffe56aSColin Percival	mv $1.tmp $1
163348ffe56aSColin Percival
163448ffe56aSColin Percival	rm $1.badnodes $1.nodes $2.nodes
163548ffe56aSColin Percival}
163648ffe56aSColin Percival
163748ffe56aSColin Percival# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2
163848ffe56aSColin Percival# with metadata not matching any entry in $1, replace the corresponding
163948ffe56aSColin Percival# line of $3 with one having the same metadata as the entry in $2.
164048ffe56aSColin Percivalfetch_filter_modified_metadata () {
164148ffe56aSColin Percival	# Fish out the metadata from $1 and $2
164248ffe56aSColin Percival	for X in $1 $2; do
164348ffe56aSColin Percival		cut -f 1-6 -d '|' < ${X} > ${X}.metadata
164448ffe56aSColin Percival	done
164548ffe56aSColin Percival
164648ffe56aSColin Percival	# Find the metadata we need to keep
164748ffe56aSColin Percival	if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then
164848ffe56aSColin Percival		comm -13 $1.metadata $2.metadata > keepmeta
164948ffe56aSColin Percival	else
165048ffe56aSColin Percival		: > keepmeta
165148ffe56aSColin Percival	fi
165248ffe56aSColin Percival
165348ffe56aSColin Percival	# Extract the lines which we need to remove from $3, and
165448ffe56aSColin Percival	# construct the lines which we need to add to $3.
165548ffe56aSColin Percival	: > $3.remove
165648ffe56aSColin Percival	: > $3.add
165748ffe56aSColin Percival	while read LINE; do
165848ffe56aSColin Percival		NODE=`echo "${LINE}" | cut -f 1-2 -d '|'`
165948ffe56aSColin Percival		look "${NODE}|" $3 >> $3.remove
166048ffe56aSColin Percival		look "${NODE}|" $3 |
166148ffe56aSColin Percival		    cut -f 7- -d '|' |
166248ffe56aSColin Percival		    lam -s "${LINE}|" - >> $3.add
166348ffe56aSColin Percival	done < keepmeta
166448ffe56aSColin Percival
166548ffe56aSColin Percival	# Remove the specified lines and add the new lines.
166648ffe56aSColin Percival	sort $3.remove |
166748ffe56aSColin Percival	    comm -13 - $3 |
166848ffe56aSColin Percival	    sort -u - $3.add > $3.tmp
166948ffe56aSColin Percival	mv $3.tmp $3
167048ffe56aSColin Percival
167148ffe56aSColin Percival	rm keepmeta $1.metadata $2.metadata $3.add $3.remove
167248ffe56aSColin Percival}
167348ffe56aSColin Percival
167448ffe56aSColin Percival# Remove lines from $1 and $2 which are identical;
167548ffe56aSColin Percival# no need to update a file if it isn't changing.
167648ffe56aSColin Percivalfetch_filter_uptodate () {
167748ffe56aSColin Percival	comm -23 $1 $2 > $1.tmp
167848ffe56aSColin Percival	comm -13 $1 $2 > $2.tmp
167948ffe56aSColin Percival
168048ffe56aSColin Percival	mv $1.tmp $1
168148ffe56aSColin Percival	mv $2.tmp $2
168248ffe56aSColin Percival}
168348ffe56aSColin Percival
1684db6b0a61SColin Percival# Fetch any "clean" old versions of files we need for merging changes.
1685db6b0a61SColin Percivalfetch_files_premerge () {
1686db6b0a61SColin Percival	# We only need to do anything if $1 is non-empty.
1687db6b0a61SColin Percival	if [ -s $1 ]; then
1688db6b0a61SColin Percival		# Tell the user what we're doing
1689db6b0a61SColin Percival		echo -n "Fetching files from ${OLDRELNUM} for merging... "
1690db6b0a61SColin Percival
1691db6b0a61SColin Percival		# List of files wanted
1692db6b0a61SColin Percival		fgrep '|f|' < $1 |
1693db6b0a61SColin Percival		    cut -f 3 -d '|' |
1694db6b0a61SColin Percival		    sort -u > files.wanted
1695db6b0a61SColin Percival
1696db6b0a61SColin Percival		# Only fetch the files we don't already have
1697db6b0a61SColin Percival		while read Y; do
1698db6b0a61SColin Percival			if [ ! -f "files/${Y}.gz" ]; then
1699db6b0a61SColin Percival				echo ${Y};
1700db6b0a61SColin Percival			fi
1701db6b0a61SColin Percival		done < files.wanted > filelist
1702db6b0a61SColin Percival
1703db6b0a61SColin Percival		# Actually fetch them
1704db6b0a61SColin Percival		lam -s "${OLDFETCHDIR}/f/" - -s ".gz" < filelist |
1705db6b0a61SColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
1706db6b0a61SColin Percival		    2>${QUIETREDIR}
1707db6b0a61SColin Percival
1708db6b0a61SColin Percival		# Make sure we got them all, and move them into /files/
1709db6b0a61SColin Percival		while read Y; do
1710db6b0a61SColin Percival			if ! [ -f ${Y}.gz ]; then
1711db6b0a61SColin Percival				echo "failed."
1712db6b0a61SColin Percival				return 1
1713db6b0a61SColin Percival			fi
1714db6b0a61SColin Percival			if [ `gunzip -c < ${Y}.gz |
1715db6b0a61SColin Percival			    ${SHA256} -q` = ${Y} ]; then
1716db6b0a61SColin Percival				mv ${Y}.gz files/${Y}.gz
1717db6b0a61SColin Percival			else
1718db6b0a61SColin Percival				echo "${Y} has incorrect hash."
1719db6b0a61SColin Percival				return 1
1720db6b0a61SColin Percival			fi
1721db6b0a61SColin Percival		done < filelist
1722db6b0a61SColin Percival		echo "done."
1723db6b0a61SColin Percival
1724db6b0a61SColin Percival		# Clean up
1725db6b0a61SColin Percival		rm filelist files.wanted
1726db6b0a61SColin Percival	fi
1727db6b0a61SColin Percival}
1728db6b0a61SColin Percival
172948ffe56aSColin Percival# Prepare to fetch files: Generate a list of the files we need,
173048ffe56aSColin Percival# copy the unmodified files we have into /files/, and generate
173148ffe56aSColin Percival# a list of patches to download.
173248ffe56aSColin Percivalfetch_files_prepare () {
173348ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
173448ffe56aSColin Percival	echo -n "Preparing to download files... "
173548ffe56aSColin Percival
173648ffe56aSColin Percival	# Reduce indices to ${PATH}|${HASH} pairs
173748ffe56aSColin Percival	for X in $1 $2 $3; do
173848ffe56aSColin Percival		cut -f 1,2,7 -d '|' < ${X} |
173948ffe56aSColin Percival		    fgrep '|f|' |
174048ffe56aSColin Percival		    cut -f 1,3 -d '|' |
174148ffe56aSColin Percival		    sort > ${X}.hashes
174248ffe56aSColin Percival	done
174348ffe56aSColin Percival
174448ffe56aSColin Percival	# List of files wanted
174548ffe56aSColin Percival	cut -f 2 -d '|' < $3.hashes |
17462328d598SColin Percival	    sort -u |
17472328d598SColin Percival	    while read HASH; do
17482328d598SColin Percival		if ! [ -f files/${HASH}.gz ]; then
17492328d598SColin Percival			echo ${HASH}
17502328d598SColin Percival		fi
17512328d598SColin Percival	done > files.wanted
175248ffe56aSColin Percival
175348ffe56aSColin Percival	# Generate a list of unmodified files
175448ffe56aSColin Percival	comm -12 $1.hashes $2.hashes |
175548ffe56aSColin Percival	    sort -k 1,1 -t '|' > unmodified.files
175648ffe56aSColin Percival
175748ffe56aSColin Percival	# Copy all files into /files/.  We only need the unmodified files
175848ffe56aSColin Percival	# for use in patching; but we'll want all of them if the user asks
175948ffe56aSColin Percival	# to rollback the updates later.
1760210b8123SColin Percival	while read LINE; do
1761210b8123SColin Percival		F=`echo "${LINE}" | cut -f 1 -d '|'`
1762210b8123SColin Percival		HASH=`echo "${LINE}" | cut -f 2 -d '|'`
1763210b8123SColin Percival
1764210b8123SColin Percival		# Skip files we already have.
1765210b8123SColin Percival		if [ -f files/${HASH}.gz ]; then
1766210b8123SColin Percival			continue
1767210b8123SColin Percival		fi
1768210b8123SColin Percival
1769210b8123SColin Percival		# Make sure the file hasn't changed.
177048ffe56aSColin Percival		cp "${BASEDIR}/${F}" tmpfile
1771210b8123SColin Percival		if [ `sha256 -q tmpfile` != ${HASH} ]; then
1772210b8123SColin Percival			echo
1773210b8123SColin Percival			echo "File changed while FreeBSD Update running: ${F}"
1774210b8123SColin Percival			return 1
1775210b8123SColin Percival		fi
1776210b8123SColin Percival
1777210b8123SColin Percival		# Place the file into storage.
1778210b8123SColin Percival		gzip -c < tmpfile > files/${HASH}.gz
177948ffe56aSColin Percival		rm tmpfile
1780210b8123SColin Percival	done < $2.hashes
178148ffe56aSColin Percival
178248ffe56aSColin Percival	# Produce a list of patches to download
178348ffe56aSColin Percival	sort -k 1,1 -t '|' $3.hashes |
178448ffe56aSColin Percival	    join -t '|' -o 2.2,1.2 - unmodified.files |
178548ffe56aSColin Percival	    fetch_make_patchlist > patchlist
178648ffe56aSColin Percival
178748ffe56aSColin Percival	# Garbage collect
178848ffe56aSColin Percival	rm unmodified.files $1.hashes $2.hashes $3.hashes
178948ffe56aSColin Percival
179048ffe56aSColin Percival	# We don't need the list of possible old files any more.
179148ffe56aSColin Percival	rm $1
179248ffe56aSColin Percival
179348ffe56aSColin Percival	# We're finished making noise
179448ffe56aSColin Percival	echo "done."
179548ffe56aSColin Percival}
179648ffe56aSColin Percival
179748ffe56aSColin Percival# Fetch files.
179848ffe56aSColin Percivalfetch_files () {
179948ffe56aSColin Percival	# Attempt to fetch patches
180048ffe56aSColin Percival	if [ -s patchlist ]; then
180148ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
180248ffe56aSColin Percival		echo ${NDEBUG} "patches.${DDSTATS}"
180348ffe56aSColin Percival		tr '|' '-' < patchlist |
1804db6b0a61SColin Percival		    lam -s "${PATCHDIR}/" - |
180548ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
180648ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
180748ffe56aSColin Percival		echo "done."
180848ffe56aSColin Percival
180948ffe56aSColin Percival		# Attempt to apply patches
181048ffe56aSColin Percival		echo -n "Applying patches... "
181148ffe56aSColin Percival		tr '|' ' ' < patchlist |
181248ffe56aSColin Percival		    while read X Y; do
181348ffe56aSColin Percival			if [ ! -f "${X}-${Y}" ]; then continue; fi
181448ffe56aSColin Percival			gunzip -c < files/${X}.gz > OLD
181548ffe56aSColin Percival
181648ffe56aSColin Percival			bspatch OLD NEW ${X}-${Y}
181748ffe56aSColin Percival
181848ffe56aSColin Percival			if [ `${SHA256} -q NEW` = ${Y} ]; then
181948ffe56aSColin Percival				mv NEW files/${Y}
182048ffe56aSColin Percival				gzip -n files/${Y}
182148ffe56aSColin Percival			fi
182248ffe56aSColin Percival			rm -f diff OLD NEW ${X}-${Y}
182348ffe56aSColin Percival		done 2>${QUIETREDIR}
182448ffe56aSColin Percival		echo "done."
182548ffe56aSColin Percival	fi
182648ffe56aSColin Percival
182748ffe56aSColin Percival	# Download files which couldn't be generate via patching
182848ffe56aSColin Percival	while read Y; do
182948ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
183048ffe56aSColin Percival			echo ${Y};
183148ffe56aSColin Percival		fi
183248ffe56aSColin Percival	done < files.wanted > filelist
183348ffe56aSColin Percival
183448ffe56aSColin Percival	if [ -s filelist ]; then
183548ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
183648ffe56aSColin Percival		echo ${NDEBUG} "files... "
183748ffe56aSColin Percival		lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist |
183848ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
183948ffe56aSColin Percival		    2>${QUIETREDIR}
184048ffe56aSColin Percival
184148ffe56aSColin Percival		while read Y; do
184248ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
184348ffe56aSColin Percival				echo "failed."
184448ffe56aSColin Percival				return 1
184548ffe56aSColin Percival			fi
184648ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
184748ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
184848ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
184948ffe56aSColin Percival			else
185048ffe56aSColin Percival				echo "${Y} has incorrect hash."
185148ffe56aSColin Percival				return 1
185248ffe56aSColin Percival			fi
185348ffe56aSColin Percival		done < filelist
185448ffe56aSColin Percival		echo "done."
185548ffe56aSColin Percival	fi
185648ffe56aSColin Percival
185748ffe56aSColin Percival	# Clean up
185848ffe56aSColin Percival	rm files.wanted filelist patchlist
185948ffe56aSColin Percival}
186048ffe56aSColin Percival
186148ffe56aSColin Percival# Create and populate install manifest directory; and report what updates
186248ffe56aSColin Percival# are available.
186348ffe56aSColin Percivalfetch_create_manifest () {
186448ffe56aSColin Percival	# If we have an existing install manifest, nuke it.
186548ffe56aSColin Percival	if [ -L "${BDHASH}-install" ]; then
186648ffe56aSColin Percival		rm -r ${BDHASH}-install/
186748ffe56aSColin Percival		rm ${BDHASH}-install
186848ffe56aSColin Percival	fi
186948ffe56aSColin Percival
187048ffe56aSColin Percival	# Report to the user if any updates were avoided due to local changes
187148ffe56aSColin Percival	if [ -s modifiedfiles ]; then
187248ffe56aSColin Percival		echo
187348ffe56aSColin Percival		echo -n "The following files are affected by updates, "
187448ffe56aSColin Percival		echo "but no changes have"
187548ffe56aSColin Percival		echo -n "been downloaded because the files have been "
187648ffe56aSColin Percival		echo "modified locally:"
187748ffe56aSColin Percival		cat modifiedfiles
18789c990fb2SGordon Tetlow	fi | $PAGER
187948ffe56aSColin Percival	rm modifiedfiles
188048ffe56aSColin Percival
188148ffe56aSColin Percival	# If no files will be updated, tell the user and exit
188248ffe56aSColin Percival	if ! [ -s INDEX-PRESENT ] &&
188348ffe56aSColin Percival	    ! [ -s INDEX-NEW ]; then
188448ffe56aSColin Percival		rm INDEX-PRESENT INDEX-NEW
188548ffe56aSColin Percival		echo
188648ffe56aSColin Percival		echo -n "No updates needed to update system to "
188748ffe56aSColin Percival		echo "${RELNUM}-p${RELPATCHNUM}."
188848ffe56aSColin Percival		return
188948ffe56aSColin Percival	fi
189048ffe56aSColin Percival
189148ffe56aSColin Percival	# Divide files into (a) removed files, (b) added files, and
189248ffe56aSColin Percival	# (c) updated files.
189348ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-PRESENT |
189448ffe56aSColin Percival	    sort > INDEX-PRESENT.flist
189548ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-NEW |
189648ffe56aSColin Percival	    sort > INDEX-NEW.flist
189748ffe56aSColin Percival	comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed
189848ffe56aSColin Percival	comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added
189948ffe56aSColin Percival	comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated
190048ffe56aSColin Percival	rm INDEX-PRESENT.flist INDEX-NEW.flist
190148ffe56aSColin Percival
190248ffe56aSColin Percival	# Report removed files, if any
190348ffe56aSColin Percival	if [ -s files.removed ]; then
190448ffe56aSColin Percival		echo
190548ffe56aSColin Percival		echo -n "The following files will be removed "
190648ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
190748ffe56aSColin Percival		cat files.removed
19089c990fb2SGordon Tetlow	fi | $PAGER
190948ffe56aSColin Percival	rm files.removed
191048ffe56aSColin Percival
191148ffe56aSColin Percival	# Report added files, if any
191248ffe56aSColin Percival	if [ -s files.added ]; then
191348ffe56aSColin Percival		echo
191448ffe56aSColin Percival		echo -n "The following files will be added "
191548ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
191648ffe56aSColin Percival		cat files.added
19179c990fb2SGordon Tetlow	fi | $PAGER
191848ffe56aSColin Percival	rm files.added
191948ffe56aSColin Percival
192048ffe56aSColin Percival	# Report updated files, if any
192148ffe56aSColin Percival	if [ -s files.updated ]; then
192248ffe56aSColin Percival		echo
192348ffe56aSColin Percival		echo -n "The following files will be updated "
192448ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
192548ffe56aSColin Percival
192648ffe56aSColin Percival		cat files.updated
19279c990fb2SGordon Tetlow	fi | $PAGER
192848ffe56aSColin Percival	rm files.updated
192948ffe56aSColin Percival
193048ffe56aSColin Percival	# Create a directory for the install manifest.
193148ffe56aSColin Percival	MDIR=`mktemp -d install.XXXXXX` || return 1
193248ffe56aSColin Percival
193348ffe56aSColin Percival	# Populate it
193448ffe56aSColin Percival	mv INDEX-PRESENT ${MDIR}/INDEX-OLD
193548ffe56aSColin Percival	mv INDEX-NEW ${MDIR}/INDEX-NEW
193648ffe56aSColin Percival
193748ffe56aSColin Percival	# Link it into place
193848ffe56aSColin Percival	ln -s ${MDIR} ${BDHASH}-install
193948ffe56aSColin Percival}
194048ffe56aSColin Percival
194148ffe56aSColin Percival# Warn about any upcoming EoL
194248ffe56aSColin Percivalfetch_warn_eol () {
194348ffe56aSColin Percival	# What's the current time?
194448ffe56aSColin Percival	NOWTIME=`date "+%s"`
194548ffe56aSColin Percival
194648ffe56aSColin Percival	# When did we last warn about the EoL date?
194748ffe56aSColin Percival	if [ -f lasteolwarn ]; then
194848ffe56aSColin Percival		LASTWARN=`cat lasteolwarn`
194948ffe56aSColin Percival	else
195048ffe56aSColin Percival		LASTWARN=`expr ${NOWTIME} - 63072000`
195148ffe56aSColin Percival	fi
195248ffe56aSColin Percival
195348ffe56aSColin Percival	# If the EoL time is past, warn.
195448ffe56aSColin Percival	if [ ${EOLTIME} -lt ${NOWTIME} ]; then
195548ffe56aSColin Percival		echo
195648ffe56aSColin Percival		cat <<-EOF
1957b698a3abSColin Percival		WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE.
195848ffe56aSColin Percival		Any security issues discovered after `date -r ${EOLTIME}`
195948ffe56aSColin Percival		will not have been corrected.
196048ffe56aSColin Percival		EOF
196148ffe56aSColin Percival		return 1
196248ffe56aSColin Percival	fi
196348ffe56aSColin Percival
196448ffe56aSColin Percival	# Figure out how long it has been since we last warned about the
196548ffe56aSColin Percival	# upcoming EoL, and how much longer we have left.
196648ffe56aSColin Percival	SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}`
196748ffe56aSColin Percival	TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}`
196848ffe56aSColin Percival
196989b14566SColin Percival	# Don't warn if the EoL is more than 3 months away
197089b14566SColin Percival	if [ ${TIMELEFT} -gt 7884000 ]; then
197148ffe56aSColin Percival		return 0
197248ffe56aSColin Percival	fi
197348ffe56aSColin Percival
197448ffe56aSColin Percival	# Don't warn if the time remaining is more than 3 times the time
197548ffe56aSColin Percival	# since the last warning.
197648ffe56aSColin Percival	if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then
197748ffe56aSColin Percival		return 0
197848ffe56aSColin Percival	fi
197948ffe56aSColin Percival
198048ffe56aSColin Percival	# Figure out what time units to use.
198148ffe56aSColin Percival	if [ ${TIMELEFT} -lt 604800 ]; then
198248ffe56aSColin Percival		UNIT="day"
198348ffe56aSColin Percival		SIZE=86400
198448ffe56aSColin Percival	elif [ ${TIMELEFT} -lt 2678400 ]; then
198548ffe56aSColin Percival		UNIT="week"
198648ffe56aSColin Percival		SIZE=604800
198748ffe56aSColin Percival	else
198848ffe56aSColin Percival		UNIT="month"
198948ffe56aSColin Percival		SIZE=2678400
199048ffe56aSColin Percival	fi
199148ffe56aSColin Percival
199248ffe56aSColin Percival	# Compute the right number of units
199348ffe56aSColin Percival	NUM=`expr ${TIMELEFT} / ${SIZE}`
199448ffe56aSColin Percival	if [ ${NUM} != 1 ]; then
199548ffe56aSColin Percival		UNIT="${UNIT}s"
199648ffe56aSColin Percival	fi
199748ffe56aSColin Percival
199848ffe56aSColin Percival	# Print the warning
199948ffe56aSColin Percival	echo
200048ffe56aSColin Percival	cat <<-EOF
200148ffe56aSColin Percival		WARNING: `uname -sr` is approaching its End-of-Life date.
200248ffe56aSColin Percival		It is strongly recommended that you upgrade to a newer
200348ffe56aSColin Percival		release within the next ${NUM} ${UNIT}.
200448ffe56aSColin Percival	EOF
200548ffe56aSColin Percival
200648ffe56aSColin Percival	# Update the stored time of last warning
200748ffe56aSColin Percival	echo ${NOWTIME} > lasteolwarn
200848ffe56aSColin Percival}
200948ffe56aSColin Percival
201048ffe56aSColin Percival# Do the actual work involved in "fetch" / "cron".
201148ffe56aSColin Percivalfetch_run () {
201248ffe56aSColin Percival	workdir_init || return 1
201348ffe56aSColin Percival
201448ffe56aSColin Percival	# Prepare the mirror list.
201548ffe56aSColin Percival	fetch_pick_server_init && fetch_pick_server
201648ffe56aSColin Percival
201748ffe56aSColin Percival	# Try to fetch the public key until we run out of servers.
201848ffe56aSColin Percival	while ! fetch_key; do
201948ffe56aSColin Percival		fetch_pick_server || return 1
202048ffe56aSColin Percival	done
202148ffe56aSColin Percival
202248ffe56aSColin Percival	# Try to fetch the metadata index signature ("tag") until we run
202348ffe56aSColin Percival	# out of available servers; and sanity check the downloaded tag.
202448ffe56aSColin Percival	while ! fetch_tag; do
202548ffe56aSColin Percival		fetch_pick_server || return 1
202648ffe56aSColin Percival	done
202748ffe56aSColin Percival	fetch_tagsanity || return 1
202848ffe56aSColin Percival
202948ffe56aSColin Percival	# Fetch the latest INDEX-NEW and INDEX-OLD files.
203048ffe56aSColin Percival	fetch_metadata INDEX-NEW INDEX-OLD || return 1
203148ffe56aSColin Percival
203248ffe56aSColin Percival	# Generate filtered INDEX-NEW and INDEX-OLD files containing only
203348ffe56aSColin Percival	# the lines which (a) belong to components we care about, and (b)
203448ffe56aSColin Percival	# don't correspond to paths we're explicitly ignoring.
203548ffe56aSColin Percival	fetch_filter_metadata INDEX-NEW || return 1
203648ffe56aSColin Percival	fetch_filter_metadata INDEX-OLD || return 1
203748ffe56aSColin Percival
2038db6b0a61SColin Percival	# Translate /boot/${KERNCONF} into ${KERNELDIR}
2039db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-NEW ${KERNCONF}
2040db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
204148ffe56aSColin Percival
204248ffe56aSColin Percival	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
204348ffe56aSColin Percival	# system and generate an INDEX-PRESENT file.
204448ffe56aSColin Percival	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
204548ffe56aSColin Percival
204648ffe56aSColin Percival	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
204748ffe56aSColin Percival	# correspond to lines in INDEX-PRESENT with hashes not appearing
204848ffe56aSColin Percival	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
204948ffe56aSColin Percival	# INDEX-PRESENT has type - and there isn't a corresponding entry in
205048ffe56aSColin Percival	# INDEX-OLD with type -.
2051db6b0a61SColin Percival	fetch_filter_unmodified_notpresent	\
2052db6b0a61SColin Percival	    INDEX-OLD INDEX-PRESENT INDEX-NEW /dev/null
205348ffe56aSColin Percival
205448ffe56aSColin Percival	# For each entry in INDEX-PRESENT of type -, remove any corresponding
205548ffe56aSColin Percival	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
205648ffe56aSColin Percival	# of type - from INDEX-PRESENT.
205748ffe56aSColin Percival	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
205848ffe56aSColin Percival
205948ffe56aSColin Percival	# If ${ALLOWDELETE} != "yes", then remove any entries from
206048ffe56aSColin Percival	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
206148ffe56aSColin Percival	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
206248ffe56aSColin Percival
206348ffe56aSColin Percival	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
206448ffe56aSColin Percival	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
206548ffe56aSColin Percival	# replace the corresponding line of INDEX-NEW with one having the
206648ffe56aSColin Percival	# same metadata as the entry in INDEX-PRESENT.
206748ffe56aSColin Percival	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
206848ffe56aSColin Percival
206948ffe56aSColin Percival	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
207048ffe56aSColin Percival	# no need to update a file if it isn't changing.
207148ffe56aSColin Percival	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
207248ffe56aSColin Percival
207348ffe56aSColin Percival	# Prepare to fetch files: Generate a list of the files we need,
207448ffe56aSColin Percival	# copy the unmodified files we have into /files/, and generate
207548ffe56aSColin Percival	# a list of patches to download.
2076210b8123SColin Percival	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
207748ffe56aSColin Percival
207848ffe56aSColin Percival	# Fetch files.
207948ffe56aSColin Percival	fetch_files || return 1
208048ffe56aSColin Percival
208148ffe56aSColin Percival	# Create and populate install manifest directory; and report what
208248ffe56aSColin Percival	# updates are available.
208348ffe56aSColin Percival	fetch_create_manifest || return 1
208448ffe56aSColin Percival
208548ffe56aSColin Percival	# Warn about any upcoming EoL
208648ffe56aSColin Percival	fetch_warn_eol || return 1
208748ffe56aSColin Percival}
208848ffe56aSColin Percival
2089db6b0a61SColin Percival# If StrictComponents is not "yes", generate a new components list
2090db6b0a61SColin Percival# with only the components which appear to be installed.
2091db6b0a61SColin Percivalupgrade_guess_components () {
2092db6b0a61SColin Percival	if [ "${STRICTCOMPONENTS}" = "no" ]; then
2093db6b0a61SColin Percival		# Generate filtered INDEX-ALL with only the components listed
2094db6b0a61SColin Percival		# in COMPONENTS.
2095db6b0a61SColin Percival		fetch_filter_metadata_components $1 || return 1
2096db6b0a61SColin Percival
2097db6b0a61SColin Percival		# Tell the user why his disk is suddenly making lots of noise
2098db6b0a61SColin Percival		echo -n "Inspecting system... "
2099db6b0a61SColin Percival
2100db6b0a61SColin Percival		# Look at the files on disk, and assume that a component is
2101db6b0a61SColin Percival		# supposed to be present if it is more than half-present.
2102db6b0a61SColin Percival		cut -f 1-3 -d '|' < INDEX-ALL |
2103db6b0a61SColin Percival		    tr '|' ' ' |
2104db6b0a61SColin Percival		    while read C S F; do
2105db6b0a61SColin Percival			if [ -e ${BASEDIR}/${F} ]; then
2106db6b0a61SColin Percival				echo "+ ${C}|${S}"
2107db6b0a61SColin Percival			fi
2108db6b0a61SColin Percival			echo "= ${C}|${S}"
2109db6b0a61SColin Percival		    done |
2110db6b0a61SColin Percival		    sort |
2111db6b0a61SColin Percival		    uniq -c |
2112db6b0a61SColin Percival		    sed -E 's,^ +,,' > compfreq
2113db6b0a61SColin Percival		grep ' = ' compfreq |
2114db6b0a61SColin Percival		    cut -f 1,3 -d ' ' |
2115db6b0a61SColin Percival		    sort -k 2,2 -t ' ' > compfreq.total
2116db6b0a61SColin Percival		grep ' + ' compfreq |
2117db6b0a61SColin Percival		    cut -f 1,3 -d ' ' |
2118db6b0a61SColin Percival		    sort -k 2,2 -t ' ' > compfreq.present
2119db6b0a61SColin Percival		join -t ' ' -1 2 -2 2 compfreq.present compfreq.total |
2120db6b0a61SColin Percival		    while read S P T; do
2121db6b0a61SColin Percival			if [ ${P} -gt `expr ${T} / 2` ]; then
2122db6b0a61SColin Percival				echo ${S}
2123db6b0a61SColin Percival			fi
2124db6b0a61SColin Percival		    done > comp.present
2125db6b0a61SColin Percival		cut -f 2 -d ' ' < compfreq.total > comp.total
2126db6b0a61SColin Percival		rm INDEX-ALL compfreq compfreq.total compfreq.present
2127db6b0a61SColin Percival
2128db6b0a61SColin Percival		# We're done making noise.
2129db6b0a61SColin Percival		echo "done."
2130db6b0a61SColin Percival
2131db6b0a61SColin Percival		# Sometimes the kernel isn't installed where INDEX-ALL
2132db6b0a61SColin Percival		# thinks that it should be: In particular, it is often in
2133db6b0a61SColin Percival		# /boot/kernel instead of /boot/GENERIC or /boot/SMP.  To
2134db6b0a61SColin Percival		# deal with this, if "kernel|X" is listed in comp.total
2135db6b0a61SColin Percival		# (i.e., is a component which would be upgraded if it is
2136db6b0a61SColin Percival		# found to be present) we will add it to comp.present.
2137db6b0a61SColin Percival		# If "kernel|<anything>" is in comp.total but "kernel|X" is
2138db6b0a61SColin Percival		# not, we print a warning -- the user is running a kernel
2139db6b0a61SColin Percival		# which isn't part of the release.
2140db6b0a61SColin Percival		KCOMP=`echo ${KERNCONF} | tr 'A-Z' 'a-z'`
2141db6b0a61SColin Percival		grep -E "^kernel\|${KCOMP}\$" comp.total >> comp.present
2142db6b0a61SColin Percival
2143db6b0a61SColin Percival		if grep -qE "^kernel\|" comp.total &&
2144db6b0a61SColin Percival		    ! grep -qE "^kernel\|${KCOMP}\$" comp.total; then
2145db6b0a61SColin Percival			cat <<-EOF
2146db6b0a61SColin Percival
2147db6b0a61SColin PercivalWARNING: This system is running a "${KCOMP}" kernel, which is not a
2148db6b0a61SColin Percivalkernel configuration distributed as part of FreeBSD ${RELNUM}.
2149db6b0a61SColin PercivalThis kernel will not be updated: you MUST update the kernel manually
2150db6b0a61SColin Percivalbefore running "$0 install".
2151db6b0a61SColin Percival			EOF
2152db6b0a61SColin Percival		fi
2153db6b0a61SColin Percival
2154db6b0a61SColin Percival		# Re-sort the list of installed components and generate
2155db6b0a61SColin Percival		# the list of non-installed components.
2156db6b0a61SColin Percival		sort -u < comp.present > comp.present.tmp
2157db6b0a61SColin Percival		mv comp.present.tmp comp.present
2158db6b0a61SColin Percival		comm -13 comp.present comp.total > comp.absent
2159db6b0a61SColin Percival
2160db6b0a61SColin Percival		# Ask the user to confirm that what we have is correct.  To
2161db6b0a61SColin Percival		# reduce user confusion, translate "X|Y" back to "X/Y" (as
2162db6b0a61SColin Percival		# subcomponents must be listed in the configuration file).
2163db6b0a61SColin Percival		echo
2164db6b0a61SColin Percival		echo -n "The following components of FreeBSD "
2165db6b0a61SColin Percival		echo "seem to be installed:"
2166db6b0a61SColin Percival		tr '|' '/' < comp.present |
2167db6b0a61SColin Percival		    fmt -72
2168db6b0a61SColin Percival		echo
2169db6b0a61SColin Percival		echo -n "The following components of FreeBSD "
2170db6b0a61SColin Percival		echo "do not seem to be installed:"
2171db6b0a61SColin Percival		tr '|' '/' < comp.absent |
2172db6b0a61SColin Percival		    fmt -72
2173db6b0a61SColin Percival		echo
2174db6b0a61SColin Percival		continuep || return 1
2175db6b0a61SColin Percival		echo
2176db6b0a61SColin Percival
2177db6b0a61SColin Percival		# Suck the generated list of components into ${COMPONENTS}.
2178db6b0a61SColin Percival		# Note that comp.present.tmp is used due to issues with
2179db6b0a61SColin Percival		# pipelines and setting variables.
2180db6b0a61SColin Percival		COMPONENTS=""
2181db6b0a61SColin Percival		tr '|' '/' < comp.present > comp.present.tmp
2182db6b0a61SColin Percival		while read C; do
2183db6b0a61SColin Percival			COMPONENTS="${COMPONENTS} ${C}"
2184db6b0a61SColin Percival		done < comp.present.tmp
2185db6b0a61SColin Percival
2186db6b0a61SColin Percival		# Delete temporary files
2187db6b0a61SColin Percival		rm comp.present comp.present.tmp comp.absent comp.total
2188db6b0a61SColin Percival	fi
2189db6b0a61SColin Percival}
2190db6b0a61SColin Percival
2191db6b0a61SColin Percival# If StrictComponents is not "yes", COMPONENTS contains an entry
2192db6b0a61SColin Percival# corresponding to the currently running kernel, and said kernel
2193db6b0a61SColin Percival# does not exist in the new release, add "kernel/generic" to the
2194db6b0a61SColin Percival# list of components.
2195db6b0a61SColin Percivalupgrade_guess_new_kernel () {
2196db6b0a61SColin Percival	if [ "${STRICTCOMPONENTS}" = "no" ]; then
2197db6b0a61SColin Percival		# Grab the unfiltered metadata file.
2198db6b0a61SColin Percival		METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
2199db6b0a61SColin Percival		gunzip -c < files/${METAHASH}.gz > $1.all
2200db6b0a61SColin Percival
2201db6b0a61SColin Percival		# If "kernel/${KCOMP}" is in ${COMPONENTS} and that component
2202db6b0a61SColin Percival		# isn't in $1.all, we need to add kernel/generic.
2203db6b0a61SColin Percival		for C in ${COMPONENTS}; do
2204db6b0a61SColin Percival			if [ ${C} = "kernel/${KCOMP}" ] &&
2205db6b0a61SColin Percival			    ! grep -qE "^kernel\|${KCOMP}\|" $1.all; then
2206db6b0a61SColin Percival				COMPONENTS="${COMPONENTS} kernel/generic"
2207db6b0a61SColin Percival				NKERNCONF="GENERIC"
2208db6b0a61SColin Percival				cat <<-EOF
2209db6b0a61SColin Percival
2210db6b0a61SColin PercivalWARNING: This system is running a "${KCOMP}" kernel, which is not a
2211db6b0a61SColin Percivalkernel configuration distributed as part of FreeBSD ${RELNUM}.
2212db6b0a61SColin PercivalAs part of upgrading to FreeBSD ${RELNUM}, this kernel will be
2213db6b0a61SColin Percivalreplaced with a "generic" kernel.
2214db6b0a61SColin Percival				EOF
2215db6b0a61SColin Percival				continuep || return 1
2216db6b0a61SColin Percival			fi
2217db6b0a61SColin Percival		done
2218db6b0a61SColin Percival
2219db6b0a61SColin Percival		# Don't need this any more...
2220db6b0a61SColin Percival		rm $1.all
2221db6b0a61SColin Percival	fi
2222db6b0a61SColin Percival}
2223db6b0a61SColin Percival
2224db6b0a61SColin Percival# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2225db6b0a61SColin Percival# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2226db6b0a61SColin Percivalupgrade_oldall_to_oldnew () {
2227db6b0a61SColin Percival	# For each ${F}|... which appears in INDEX-ALL but does not appear
2228db6b0a61SColin Percival	# in INDEX-OLD, add ${F}|-|||||| to INDEX-OLD.
2229db6b0a61SColin Percival	cut -f 1 -d '|' < $1 |
2230db6b0a61SColin Percival	    sort -u > $1.paths
2231db6b0a61SColin Percival	cut -f 1 -d '|' < $2 |
2232db6b0a61SColin Percival	    sort -u |
2233db6b0a61SColin Percival	    comm -13 $1.paths - |
2234db6b0a61SColin Percival	    lam - -s "|-||||||" |
2235db6b0a61SColin Percival	    sort - $1 > $1.tmp
2236db6b0a61SColin Percival	mv $1.tmp $1
2237db6b0a61SColin Percival
2238db6b0a61SColin Percival	# Remove lines from INDEX-OLD which also appear in INDEX-ALL
2239db6b0a61SColin Percival	comm -23 $1 $2 > $1.tmp
2240db6b0a61SColin Percival	mv $1.tmp $1
2241db6b0a61SColin Percival
2242db6b0a61SColin Percival	# Remove lines from INDEX-ALL which have a file name not appearing
2243db6b0a61SColin Percival	# anywhere in INDEX-OLD (since these must be files which haven't
2244db6b0a61SColin Percival	# changed -- if they were new, there would be an entry of type "-").
2245db6b0a61SColin Percival	cut -f 1 -d '|' < $1 |
2246db6b0a61SColin Percival	    sort -u > $1.paths
2247db6b0a61SColin Percival	sort -k 1,1 -t '|' < $2 |
2248db6b0a61SColin Percival	    join -t '|' - $1.paths |
2249db6b0a61SColin Percival	    sort > $2.tmp
2250db6b0a61SColin Percival	rm $1.paths
2251db6b0a61SColin Percival	mv $2.tmp $2
2252db6b0a61SColin Percival
2253db6b0a61SColin Percival	# Rename INDEX-ALL to INDEX-NEW.
2254db6b0a61SColin Percival	mv $2 $3
2255db6b0a61SColin Percival}
2256db6b0a61SColin Percival
22577449d2f5SColin Percival# Helper for upgrade_merge: Return zero true iff the two files differ only
22587449d2f5SColin Percival# in the contents of their $FreeBSD$ tags.
22597449d2f5SColin Percivalsamef () {
22607449d2f5SColin Percival	X=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $1 | ${SHA256}`
22617449d2f5SColin Percival	Y=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $2 | ${SHA256}`
22627449d2f5SColin Percival
22637449d2f5SColin Percival	if [ $X = $Y ]; then
22647449d2f5SColin Percival		return 0;
22657449d2f5SColin Percival	else
22667449d2f5SColin Percival		return 1;
22677449d2f5SColin Percival	fi
22687449d2f5SColin Percival}
22697449d2f5SColin Percival
2270db6b0a61SColin Percival# From the list of "old" files in $1, merge changes in $2 with those in $3,
2271db6b0a61SColin Percival# and update $3 to reflect the hashes of merged files.
2272db6b0a61SColin Percivalupgrade_merge () {
2273db6b0a61SColin Percival	# We only need to do anything if $1 is non-empty.
2274db6b0a61SColin Percival	if [ -s $1 ]; then
2275db6b0a61SColin Percival		cut -f 1 -d '|' $1 |
2276db6b0a61SColin Percival		    sort > $1-paths
2277db6b0a61SColin Percival
2278db6b0a61SColin Percival		# Create staging area for merging files
2279db6b0a61SColin Percival		rm -rf merge/
2280db6b0a61SColin Percival		while read F; do
2281db6b0a61SColin Percival			D=`dirname ${F}`
2282db6b0a61SColin Percival			mkdir -p merge/old/${D}
2283db6b0a61SColin Percival			mkdir -p merge/${OLDRELNUM}/${D}
2284db6b0a61SColin Percival			mkdir -p merge/${RELNUM}/${D}
2285db6b0a61SColin Percival			mkdir -p merge/new/${D}
2286db6b0a61SColin Percival		done < $1-paths
2287db6b0a61SColin Percival
2288db6b0a61SColin Percival		# Copy in files
2289db6b0a61SColin Percival		while read F; do
2290db6b0a61SColin Percival			# Currently installed file
2291db6b0a61SColin Percival			V=`look "${F}|" $2 | cut -f 7 -d '|'`
2292db6b0a61SColin Percival			gunzip < files/${V}.gz > merge/old/${F}
2293db6b0a61SColin Percival
2294db6b0a61SColin Percival			# Old release
2295db6b0a61SColin Percival			if look "${F}|" $1 | fgrep -q "|f|"; then
2296db6b0a61SColin Percival				V=`look "${F}|" $1 | cut -f 3 -d '|'`
2297db6b0a61SColin Percival				gunzip < files/${V}.gz		\
2298db6b0a61SColin Percival				    > merge/${OLDRELNUM}/${F}
2299db6b0a61SColin Percival			fi
2300db6b0a61SColin Percival
2301db6b0a61SColin Percival			# New release
2302db6b0a61SColin Percival			if look "${F}|" $3 | cut -f 1,2,7 -d '|' |
2303db6b0a61SColin Percival			    fgrep -q "|f|"; then
2304db6b0a61SColin Percival				V=`look "${F}|" $3 | cut -f 7 -d '|'`
2305db6b0a61SColin Percival				gunzip < files/${V}.gz		\
2306db6b0a61SColin Percival				    > merge/${RELNUM}/${F}
2307db6b0a61SColin Percival			fi
2308db6b0a61SColin Percival		done < $1-paths
2309db6b0a61SColin Percival
2310db6b0a61SColin Percival		# Attempt to automatically merge changes
2311db6b0a61SColin Percival		echo -n "Attempting to automatically merge "
2312db6b0a61SColin Percival		echo -n "changes in files..."
2313db6b0a61SColin Percival		: > failed.merges
2314db6b0a61SColin Percival		while read F; do
2315db6b0a61SColin Percival			# If the file doesn't exist in the new release,
2316db6b0a61SColin Percival			# the result of "merging changes" is having the file
2317db6b0a61SColin Percival			# not exist.
2318db6b0a61SColin Percival			if ! [ -f merge/${RELNUM}/${F} ]; then
2319db6b0a61SColin Percival				continue
2320db6b0a61SColin Percival			fi
2321db6b0a61SColin Percival
2322db6b0a61SColin Percival			# If the file didn't exist in the old release, we're
2323db6b0a61SColin Percival			# going to throw away the existing file and hope that
2324db6b0a61SColin Percival			# the version from the new release is what we want.
2325db6b0a61SColin Percival			if ! [ -f merge/${OLDRELNUM}/${F} ]; then
2326db6b0a61SColin Percival				cp merge/${RELNUM}/${F} merge/new/${F}
2327db6b0a61SColin Percival				continue
2328db6b0a61SColin Percival			fi
2329db6b0a61SColin Percival
2330db6b0a61SColin Percival			# Some files need special treatment.
2331db6b0a61SColin Percival			case ${F} in
2332db6b0a61SColin Percival			/etc/spwd.db | /etc/pwd.db | /etc/login.conf.db)
2333db6b0a61SColin Percival				# Don't merge these -- we're rebuild them
2334db6b0a61SColin Percival				# after updates are installed.
2335db6b0a61SColin Percival				cp merge/old/${F} merge/new/${F}
2336db6b0a61SColin Percival				;;
2337db6b0a61SColin Percival			*)
2338db6b0a61SColin Percival				if ! merge -p -L "current version"	\
2339db6b0a61SColin Percival				    -L "${OLDRELNUM}" -L "${RELNUM}"	\
2340db6b0a61SColin Percival				    merge/old/${F}			\
2341db6b0a61SColin Percival				    merge/${OLDRELNUM}/${F}		\
2342db6b0a61SColin Percival				    merge/${RELNUM}/${F}		\
2343db6b0a61SColin Percival				    > merge/new/${F} 2>/dev/null; then
2344db6b0a61SColin Percival					echo ${F} >> failed.merges
2345db6b0a61SColin Percival				fi
2346db6b0a61SColin Percival				;;
2347db6b0a61SColin Percival			esac
2348db6b0a61SColin Percival		done < $1-paths
2349db6b0a61SColin Percival		echo " done."
2350db6b0a61SColin Percival
2351db6b0a61SColin Percival		# Ask the user to handle any files which didn't merge.
2352db6b0a61SColin Percival		while read F; do
23537449d2f5SColin Percival			# If the installed file differs from the version in
23547449d2f5SColin Percival			# the old release only due to $FreeBSD$ tag expansion
23557449d2f5SColin Percival			# then just use the version in the new release.
23567449d2f5SColin Percival			if samef merge/old/${F} merge/${OLDRELNUM}/${F}; then
23577449d2f5SColin Percival				cp merge/${RELNUM}/${F} merge/new/${F}
23587449d2f5SColin Percival				continue
23597449d2f5SColin Percival			fi
23607449d2f5SColin Percival
2361db6b0a61SColin Percival			cat <<-EOF
2362db6b0a61SColin Percival
2363db6b0a61SColin PercivalThe following file could not be merged automatically: ${F}
2364db6b0a61SColin PercivalPress Enter to edit this file in ${EDITOR} and resolve the conflicts
2365db6b0a61SColin Percivalmanually...
2366db6b0a61SColin Percival			EOF
2367db6b0a61SColin Percival			read dummy </dev/tty
2368db6b0a61SColin Percival			${EDITOR} `pwd`/merge/new/${F} < /dev/tty
2369db6b0a61SColin Percival		done < failed.merges
2370db6b0a61SColin Percival		rm failed.merges
2371db6b0a61SColin Percival
2372db6b0a61SColin Percival		# Ask the user to confirm that he likes how the result
2373db6b0a61SColin Percival		# of merging files.
2374db6b0a61SColin Percival		while read F; do
23757449d2f5SColin Percival			# Skip files which haven't changed except possibly
23767449d2f5SColin Percival			# in their $FreeBSD$ tags.
23777449d2f5SColin Percival			if [ -f merge/old/${F} ] && [ -f merge/new/${F} ] &&
23787449d2f5SColin Percival			    samef merge/old/${F} merge/new/${F}; then
23797449d2f5SColin Percival				continue
23807449d2f5SColin Percival			fi
23817449d2f5SColin Percival
23827449d2f5SColin Percival			# Skip files where the installed file differs from
23837449d2f5SColin Percival			# the old file only due to $FreeBSD$ tags.
23847449d2f5SColin Percival			if [ -f merge/old/${F} ] &&
23857449d2f5SColin Percival			    [ -f merge/${OLDRELNUM}/${F} ] &&
23867449d2f5SColin Percival			    samef merge/old/${F} merge/${OLDRELNUM}/${F}; then
2387db6b0a61SColin Percival				continue
2388db6b0a61SColin Percival			fi
2389db6b0a61SColin Percival
2390db6b0a61SColin Percival			# Warn about files which are ceasing to exist.
2391db6b0a61SColin Percival			if ! [ -f merge/new/${F} ]; then
2392db6b0a61SColin Percival				cat <<-EOF
2393db6b0a61SColin Percival
2394db6b0a61SColin PercivalThe following file will be removed, as it no longer exists in
2395db6b0a61SColin PercivalFreeBSD ${RELNUM}: ${F}
2396db6b0a61SColin Percival				EOF
2397db6b0a61SColin Percival				continuep < /dev/tty || return 1
2398db6b0a61SColin Percival				continue
2399db6b0a61SColin Percival			fi
2400db6b0a61SColin Percival
2401db6b0a61SColin Percival			# Print changes for the user's approval.
2402db6b0a61SColin Percival			cat <<-EOF
2403db6b0a61SColin Percival
2404db6b0a61SColin PercivalThe following changes, which occurred between FreeBSD ${OLDRELNUM} and
2405db6b0a61SColin PercivalFreeBSD ${RELNUM} have been merged into ${F}:
2406db6b0a61SColin PercivalEOF
2407db6b0a61SColin Percival			diff -U 5 -L "current version" -L "new version"	\
2408db6b0a61SColin Percival			    merge/old/${F} merge/new/${F} || true
2409db6b0a61SColin Percival			continuep < /dev/tty || return 1
2410db6b0a61SColin Percival		done < $1-paths
2411db6b0a61SColin Percival
2412db6b0a61SColin Percival		# Store merged files.
2413db6b0a61SColin Percival		while read F; do
2414c58b62efSColin Percival			if [ -f merge/new/${F} ]; then
2415db6b0a61SColin Percival				V=`${SHA256} -q merge/new/${F}`
2416db6b0a61SColin Percival
2417db6b0a61SColin Percival				gzip -c < merge/new/${F} > files/${V}.gz
2418db6b0a61SColin Percival				echo "${F}|${V}"
2419db6b0a61SColin Percival			fi
2420db6b0a61SColin Percival		done < $1-paths > newhashes
2421db6b0a61SColin Percival
2422db6b0a61SColin Percival		# Pull lines out from $3 which need to be updated to
2423db6b0a61SColin Percival		# reflect merged files.
2424db6b0a61SColin Percival		while read F; do
2425db6b0a61SColin Percival			look "${F}|" $3
2426db6b0a61SColin Percival		done < $1-paths > $3-oldlines
2427db6b0a61SColin Percival
2428db6b0a61SColin Percival		# Update lines to reflect merged files
2429db6b0a61SColin Percival		join -t '|' -o 1.1,1.2,1.3,1.4,1.5,1.6,2.2,1.8		\
2430db6b0a61SColin Percival		    $3-oldlines newhashes > $3-newlines
2431db6b0a61SColin Percival
2432db6b0a61SColin Percival		# Remove old lines from $3 and add new lines.
2433db6b0a61SColin Percival		sort $3-oldlines |
2434db6b0a61SColin Percival		    comm -13 - $3 |
2435db6b0a61SColin Percival		    sort - $3-newlines > $3.tmp
2436db6b0a61SColin Percival		mv $3.tmp $3
2437db6b0a61SColin Percival
2438db6b0a61SColin Percival		# Clean up
2439db6b0a61SColin Percival		rm $1-paths newhashes $3-oldlines $3-newlines
2440db6b0a61SColin Percival		rm -rf merge/
2441db6b0a61SColin Percival	fi
2442db6b0a61SColin Percival
2443db6b0a61SColin Percival	# We're done with merging files.
2444db6b0a61SColin Percival	rm $1
2445db6b0a61SColin Percival}
2446db6b0a61SColin Percival
2447db6b0a61SColin Percival# Do the work involved in fetching upgrades to a new release
2448db6b0a61SColin Percivalupgrade_run () {
2449db6b0a61SColin Percival	workdir_init || return 1
2450db6b0a61SColin Percival
2451db6b0a61SColin Percival	# Prepare the mirror list.
2452db6b0a61SColin Percival	fetch_pick_server_init && fetch_pick_server
2453db6b0a61SColin Percival
2454db6b0a61SColin Percival	# Try to fetch the public key until we run out of servers.
2455db6b0a61SColin Percival	while ! fetch_key; do
2456db6b0a61SColin Percival		fetch_pick_server || return 1
2457db6b0a61SColin Percival	done
2458db6b0a61SColin Percival
2459db6b0a61SColin Percival	# Try to fetch the metadata index signature ("tag") until we run
2460db6b0a61SColin Percival	# out of available servers; and sanity check the downloaded tag.
2461db6b0a61SColin Percival	while ! fetch_tag; do
2462db6b0a61SColin Percival		fetch_pick_server || return 1
2463db6b0a61SColin Percival	done
2464db6b0a61SColin Percival	fetch_tagsanity || return 1
2465db6b0a61SColin Percival
2466db6b0a61SColin Percival	# Fetch the INDEX-OLD and INDEX-ALL.
2467db6b0a61SColin Percival	fetch_metadata INDEX-OLD INDEX-ALL || return 1
2468db6b0a61SColin Percival
2469db6b0a61SColin Percival	# If StrictComponents is not "yes", generate a new components list
2470db6b0a61SColin Percival	# with only the components which appear to be installed.
2471db6b0a61SColin Percival	upgrade_guess_components INDEX-ALL || return 1
2472db6b0a61SColin Percival
2473db6b0a61SColin Percival	# Generate filtered INDEX-OLD and INDEX-ALL files containing only
2474db6b0a61SColin Percival	# the components we want and without anything marked as "Ignore".
2475db6b0a61SColin Percival	fetch_filter_metadata INDEX-OLD || return 1
2476db6b0a61SColin Percival	fetch_filter_metadata INDEX-ALL || return 1
2477db6b0a61SColin Percival
2478db6b0a61SColin Percival	# Merge the INDEX-OLD and INDEX-ALL files into INDEX-OLD.
2479db6b0a61SColin Percival	sort INDEX-OLD INDEX-ALL > INDEX-OLD.tmp
2480db6b0a61SColin Percival	mv INDEX-OLD.tmp INDEX-OLD
2481db6b0a61SColin Percival	rm INDEX-ALL
2482db6b0a61SColin Percival
2483db6b0a61SColin Percival	# Adjust variables for fetching files from the new release.
2484db6b0a61SColin Percival	OLDRELNUM=${RELNUM}
2485db6b0a61SColin Percival	RELNUM=${TARGETRELEASE}
2486db6b0a61SColin Percival	OLDFETCHDIR=${FETCHDIR}
2487db6b0a61SColin Percival	FETCHDIR=${RELNUM}/${ARCH}
2488db6b0a61SColin Percival
2489db6b0a61SColin Percival	# Try to fetch the NEW metadata index signature ("tag") until we run
2490db6b0a61SColin Percival	# out of available servers; and sanity check the downloaded tag.
2491db6b0a61SColin Percival	while ! fetch_tag; do
2492db6b0a61SColin Percival		fetch_pick_server || return 1
2493db6b0a61SColin Percival	done
2494db6b0a61SColin Percival
2495db6b0a61SColin Percival	# Fetch the new INDEX-ALL.
2496db6b0a61SColin Percival	fetch_metadata INDEX-ALL || return 1
2497db6b0a61SColin Percival
2498db6b0a61SColin Percival	# If StrictComponents is not "yes", COMPONENTS contains an entry
2499db6b0a61SColin Percival	# corresponding to the currently running kernel, and said kernel
2500db6b0a61SColin Percival	# does not exist in the new release, add "kernel/generic" to the
2501db6b0a61SColin Percival	# list of components.
2502db6b0a61SColin Percival	upgrade_guess_new_kernel INDEX-ALL || return 1
2503db6b0a61SColin Percival
2504db6b0a61SColin Percival	# Filter INDEX-ALL to contain only the components we want and without
2505db6b0a61SColin Percival	# anything marked as "Ignore".
2506db6b0a61SColin Percival	fetch_filter_metadata INDEX-ALL || return 1
2507db6b0a61SColin Percival
2508db6b0a61SColin Percival	# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2509db6b0a61SColin Percival	# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2510db6b0a61SColin Percival	upgrade_oldall_to_oldnew INDEX-OLD INDEX-ALL INDEX-NEW
2511db6b0a61SColin Percival
2512db6b0a61SColin Percival	# Translate /boot/${KERNCONF} or /boot/${NKERNCONF} into ${KERNELDIR}
2513db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-NEW ${NKERNCONF}
2514db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
2515db6b0a61SColin Percival
2516db6b0a61SColin Percival	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
2517db6b0a61SColin Percival	# system and generate an INDEX-PRESENT file.
2518db6b0a61SColin Percival	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2519db6b0a61SColin Percival
2520db6b0a61SColin Percival	# Based on ${MERGECHANGES}, generate a file tomerge-old with the
2521db6b0a61SColin Percival	# paths and hashes of old versions of files to merge.
2522db6b0a61SColin Percival	fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT tomerge-old
2523db6b0a61SColin Percival
2524db6b0a61SColin Percival	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
2525db6b0a61SColin Percival	# correspond to lines in INDEX-PRESENT with hashes not appearing
2526db6b0a61SColin Percival	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
2527db6b0a61SColin Percival	# INDEX-PRESENT has type - and there isn't a corresponding entry in
2528db6b0a61SColin Percival	# INDEX-OLD with type -.
2529db6b0a61SColin Percival	fetch_filter_unmodified_notpresent	\
2530db6b0a61SColin Percival	    INDEX-OLD INDEX-PRESENT INDEX-NEW tomerge-old
2531db6b0a61SColin Percival
2532db6b0a61SColin Percival	# For each entry in INDEX-PRESENT of type -, remove any corresponding
2533db6b0a61SColin Percival	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
2534db6b0a61SColin Percival	# of type - from INDEX-PRESENT.
2535db6b0a61SColin Percival	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
2536db6b0a61SColin Percival
2537db6b0a61SColin Percival	# If ${ALLOWDELETE} != "yes", then remove any entries from
2538db6b0a61SColin Percival	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
2539db6b0a61SColin Percival	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
2540db6b0a61SColin Percival
2541db6b0a61SColin Percival	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
2542db6b0a61SColin Percival	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
2543db6b0a61SColin Percival	# replace the corresponding line of INDEX-NEW with one having the
2544db6b0a61SColin Percival	# same metadata as the entry in INDEX-PRESENT.
2545db6b0a61SColin Percival	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
2546db6b0a61SColin Percival
2547db6b0a61SColin Percival	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
2548db6b0a61SColin Percival	# no need to update a file if it isn't changing.
2549db6b0a61SColin Percival	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
2550db6b0a61SColin Percival
2551db6b0a61SColin Percival	# Fetch "clean" files from the old release for merging changes.
2552db6b0a61SColin Percival	fetch_files_premerge tomerge-old
2553db6b0a61SColin Percival
2554db6b0a61SColin Percival	# Prepare to fetch files: Generate a list of the files we need,
2555db6b0a61SColin Percival	# copy the unmodified files we have into /files/, and generate
2556db6b0a61SColin Percival	# a list of patches to download.
2557db6b0a61SColin Percival	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2558db6b0a61SColin Percival
2559db6b0a61SColin Percival	# Fetch patches from to-${RELNUM}/${ARCH}/bp/
2560db6b0a61SColin Percival	PATCHDIR=to-${RELNUM}/${ARCH}/bp
2561db6b0a61SColin Percival	fetch_files || return 1
2562db6b0a61SColin Percival
2563db6b0a61SColin Percival	# Merge configuration file changes.
2564db6b0a61SColin Percival	upgrade_merge tomerge-old INDEX-PRESENT INDEX-NEW || return 1
2565db6b0a61SColin Percival
2566db6b0a61SColin Percival	# Create and populate install manifest directory; and report what
2567db6b0a61SColin Percival	# updates are available.
2568db6b0a61SColin Percival	fetch_create_manifest || return 1
2569db6b0a61SColin Percival
2570db6b0a61SColin Percival	# Leave a note behind to tell the "install" command that the kernel
2571db6b0a61SColin Percival	# needs to be installed before the world.
2572db6b0a61SColin Percival	touch ${BDHASH}-install/kernelfirst
257385451f90SColin Percival
257485451f90SColin Percival	# Remind the user that they need to run "freebsd-update install"
257585451f90SColin Percival	# to install the downloaded bits, in case they didn't RTFM.
257685451f90SColin Percival	echo "To install the downloaded upgrades, run \"$0 install\"."
2577db6b0a61SColin Percival}
2578db6b0a61SColin Percival
257948ffe56aSColin Percival# Make sure that all the file hashes mentioned in $@ have corresponding
258048ffe56aSColin Percival# gzipped files stored in /files/.
258148ffe56aSColin Percivalinstall_verify () {
258248ffe56aSColin Percival	# Generate a list of hashes
258348ffe56aSColin Percival	cat $@ |
258448ffe56aSColin Percival	    cut -f 2,7 -d '|' |
258548ffe56aSColin Percival	    grep -E '^f' |
258648ffe56aSColin Percival	    cut -f 2 -d '|' |
258748ffe56aSColin Percival	    sort -u > filelist
258848ffe56aSColin Percival
258948ffe56aSColin Percival	# Make sure all the hashes exist
259048ffe56aSColin Percival	while read HASH; do
259148ffe56aSColin Percival		if ! [ -f files/${HASH}.gz ]; then
259248ffe56aSColin Percival			echo -n "Update files missing -- "
259348ffe56aSColin Percival			echo "this should never happen."
259448ffe56aSColin Percival			echo "Re-run '$0 fetch'."
259548ffe56aSColin Percival			return 1
259648ffe56aSColin Percival		fi
259748ffe56aSColin Percival	done < filelist
259848ffe56aSColin Percival
259948ffe56aSColin Percival	# Clean up
260048ffe56aSColin Percival	rm filelist
260148ffe56aSColin Percival}
260248ffe56aSColin Percival
260348ffe56aSColin Percival# Remove the system immutable flag from files
260448ffe56aSColin Percivalinstall_unschg () {
260548ffe56aSColin Percival	# Generate file list
260648ffe56aSColin Percival	cat $@ |
260748ffe56aSColin Percival	    cut -f 1 -d '|' > filelist
260848ffe56aSColin Percival
260948ffe56aSColin Percival	# Remove flags
261048ffe56aSColin Percival	while read F; do
2611e829ed67SColin Percival		if ! [ -e ${BASEDIR}/${F} ]; then
261248ffe56aSColin Percival			continue
261348ffe56aSColin Percival		fi
261448ffe56aSColin Percival
2615e829ed67SColin Percival		chflags noschg ${BASEDIR}/${F} || return 1
261648ffe56aSColin Percival	done < filelist
261748ffe56aSColin Percival
261848ffe56aSColin Percival	# Clean up
261948ffe56aSColin Percival	rm filelist
262048ffe56aSColin Percival}
262148ffe56aSColin Percival
262223d827efSSimon L. B. Nielsen# Decide which directory name to use for kernel backups.
262323d827efSSimon L. B. Nielsenbackup_kernel_finddir () {
262423d827efSSimon L. B. Nielsen	CNT=0
262523d827efSSimon L. B. Nielsen	while true ; do
262623d827efSSimon L. B. Nielsen		# Pathname does not exist, so it is OK use that name
262723d827efSSimon L. B. Nielsen		# for backup directory.
262823d827efSSimon L. B. Nielsen		if [ ! -e $BACKUPKERNELDIR ]; then
262923d827efSSimon L. B. Nielsen			return 0
263023d827efSSimon L. B. Nielsen		fi
263123d827efSSimon L. B. Nielsen
263223d827efSSimon L. B. Nielsen		# If directory do exist, we only use if it has our
263323d827efSSimon L. B. Nielsen		# marker file.
263423d827efSSimon L. B. Nielsen		if [ -d $BACKUPKERNELDIR -a \
263523d827efSSimon L. B. Nielsen			-e $BACKUPKERNELDIR/.freebsd-update ]; then
263623d827efSSimon L. B. Nielsen			return 0
263723d827efSSimon L. B. Nielsen		fi
263823d827efSSimon L. B. Nielsen
263923d827efSSimon L. B. Nielsen		# We could not use current directory name, so add counter to
264023d827efSSimon L. B. Nielsen		# the end and try again.
264123d827efSSimon L. B. Nielsen		CNT=$((CNT + 1))
264223d827efSSimon L. B. Nielsen		if [ $CNT -gt 9 ]; then
264323d827efSSimon L. B. Nielsen			echo "Could not find valid backup dir ($BACKUPKERNELDIR)"
264423d827efSSimon L. B. Nielsen			exit 1
264523d827efSSimon L. B. Nielsen		fi
264623d827efSSimon L. B. Nielsen		BACKUPKERNELDIR="`echo $BACKUPKERNELDIR | sed -Ee 's/[0-9]\$//'`"
264723d827efSSimon L. B. Nielsen		BACKUPKERNELDIR="${BACKUPKERNELDIR}${CNT}"
264823d827efSSimon L. B. Nielsen	done
264923d827efSSimon L. B. Nielsen}
265023d827efSSimon L. B. Nielsen
265123d827efSSimon L. B. Nielsen# Backup the current kernel using hardlinks, if not disabled by user.
265223d827efSSimon L. B. Nielsen# Since we delete all files in the directory used for previous backups
265323d827efSSimon L. B. Nielsen# we create a marker file called ".freebsd-update" in the directory so
265423d827efSSimon L. B. Nielsen# we can determine on the next run that the directory was created by
265523d827efSSimon L. B. Nielsen# freebsd-update and we then do not accidentally remove user files in
265623d827efSSimon L. B. Nielsen# the unlikely case that the user has created a directory with a
265723d827efSSimon L. B. Nielsen# conflicting name.
265823d827efSSimon L. B. Nielsenbackup_kernel () {
265923d827efSSimon L. B. Nielsen	# Only make kernel backup is so configured.
266023d827efSSimon L. B. Nielsen	if [ $BACKUPKERNEL != yes ]; then
266123d827efSSimon L. B. Nielsen		return 0
266223d827efSSimon L. B. Nielsen	fi
266323d827efSSimon L. B. Nielsen
266423d827efSSimon L. B. Nielsen	# Decide which directory name to use for kernel backups.
266523d827efSSimon L. B. Nielsen	backup_kernel_finddir
266623d827efSSimon L. B. Nielsen
266723d827efSSimon L. B. Nielsen	# Remove old kernel backup files.  If $BACKUPKERNELDIR was
266823d827efSSimon L. B. Nielsen	# "not ours", backup_kernel_finddir would have exited, so
266923d827efSSimon L. B. Nielsen	# deleting the directory content is as safe as we can make it.
267023d827efSSimon L. B. Nielsen	if [ -d $BACKUPKERNELDIR ]; then
2671ab7d0151SJaakko Heinonen		rm -fr $BACKUPKERNELDIR
267223d827efSSimon L. B. Nielsen	fi
267323d827efSSimon L. B. Nielsen
2674ab7d0151SJaakko Heinonen	# Create directories for backup.
267523d827efSSimon L. B. Nielsen	mkdir -p $BACKUPKERNELDIR
2676ab7d0151SJaakko Heinonen	mtree -cdn -p "${KERNELDIR}" | \
2677ab7d0151SJaakko Heinonen	    mtree -Ue -p "${BACKUPKERNELDIR}" > /dev/null
267823d827efSSimon L. B. Nielsen
267923d827efSSimon L. B. Nielsen	# Mark the directory as having been created by freebsd-update.
268023d827efSSimon L. B. Nielsen	touch $BACKUPKERNELDIR/.freebsd-update
268123d827efSSimon L. B. Nielsen	if [ $? -ne 0 ]; then
268223d827efSSimon L. B. Nielsen		echo "Could not create kernel backup directory"
268323d827efSSimon L. B. Nielsen		exit 1
268423d827efSSimon L. B. Nielsen	fi
268523d827efSSimon L. B. Nielsen
268623d827efSSimon L. B. Nielsen	# Disable pathname expansion to be sure *.symbols is not
268723d827efSSimon L. B. Nielsen	# expanded.
268823d827efSSimon L. B. Nielsen	set -f
268923d827efSSimon L. B. Nielsen
269023d827efSSimon L. B. Nielsen	# Use find to ignore symbol files, unless disabled by user.
269123d827efSSimon L. B. Nielsen	if [ $BACKUPKERNELSYMBOLFILES = yes ]; then
269223d827efSSimon L. B. Nielsen		FINDFILTER=""
269323d827efSSimon L. B. Nielsen	else
269423d827efSSimon L. B. Nielsen		FINDFILTER=-"a ! -name *.symbols"
269523d827efSSimon L. B. Nielsen	fi
269623d827efSSimon L. B. Nielsen
269723d827efSSimon L. B. Nielsen	# Backup all the kernel files using hardlinks.
2698ab7d0151SJaakko Heinonen	(cd $KERNELDIR && find . -type f $FINDFILTER -exec \
2699ab7d0151SJaakko Heinonen	    cp -pl '{}' ${BACKUPKERNELDIR}/'{}' \;)
270023d827efSSimon L. B. Nielsen
270123d827efSSimon L. B. Nielsen	# Re-enable patchname expansion.
270223d827efSSimon L. B. Nielsen	set +f
270323d827efSSimon L. B. Nielsen}
270423d827efSSimon L. B. Nielsen
270548ffe56aSColin Percival# Install new files
270648ffe56aSColin Percivalinstall_from_index () {
270748ffe56aSColin Percival	# First pass: Do everything apart from setting file flags.  We
270848ffe56aSColin Percival	# can't set flags yet, because schg inhibits hard linking.
270948ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
271048ffe56aSColin Percival	    tr '|' ' ' |
271148ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
271248ffe56aSColin Percival		case ${TYPE} in
271348ffe56aSColin Percival		d)
271448ffe56aSColin Percival			# Create a directory
271548ffe56aSColin Percival			install -d -o ${OWNER} -g ${GROUP}		\
271648ffe56aSColin Percival			    -m ${PERM} ${BASEDIR}/${FPATH}
271748ffe56aSColin Percival			;;
271848ffe56aSColin Percival		f)
271948ffe56aSColin Percival			if [ -z "${LINK}" ]; then
272048ffe56aSColin Percival				# Create a file, without setting flags.
272148ffe56aSColin Percival				gunzip < files/${HASH}.gz > ${HASH}
272248ffe56aSColin Percival				install -S -o ${OWNER} -g ${GROUP}	\
272348ffe56aSColin Percival				    -m ${PERM} ${HASH} ${BASEDIR}/${FPATH}
272448ffe56aSColin Percival				rm ${HASH}
272548ffe56aSColin Percival			else
272648ffe56aSColin Percival				# Create a hard link.
2727e829ed67SColin Percival				ln -f ${BASEDIR}/${LINK} ${BASEDIR}/${FPATH}
272848ffe56aSColin Percival			fi
272948ffe56aSColin Percival			;;
273048ffe56aSColin Percival		L)
273148ffe56aSColin Percival			# Create a symlink
273248ffe56aSColin Percival			ln -sfh ${HASH} ${BASEDIR}/${FPATH}
273348ffe56aSColin Percival			;;
273448ffe56aSColin Percival		esac
273548ffe56aSColin Percival	    done
273648ffe56aSColin Percival
273748ffe56aSColin Percival	# Perform a second pass, adding file flags.
273848ffe56aSColin Percival	tr '|' ' ' < $1 |
273948ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
274048ffe56aSColin Percival		if [ ${TYPE} = "f" ] &&
274148ffe56aSColin Percival		    ! [ ${FLAGS} = "0" ]; then
274248ffe56aSColin Percival			chflags ${FLAGS} ${BASEDIR}/${FPATH}
274348ffe56aSColin Percival		fi
274448ffe56aSColin Percival	    done
274548ffe56aSColin Percival}
274648ffe56aSColin Percival
274748ffe56aSColin Percival# Remove files which we want to delete
274848ffe56aSColin Percivalinstall_delete () {
274948ffe56aSColin Percival	# Generate list of new files
275048ffe56aSColin Percival	cut -f 1 -d '|' < $2 |
275148ffe56aSColin Percival	    sort > newfiles
275248ffe56aSColin Percival
275348ffe56aSColin Percival	# Generate subindex of old files we want to nuke
275448ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
275548ffe56aSColin Percival	    join -t '|' -v 1 - newfiles |
2756bce02f98SColin Percival	    sort -r -k 1,1 -t '|' |
275748ffe56aSColin Percival	    cut -f 1,2 -d '|' |
275848ffe56aSColin Percival	    tr '|' ' ' > killfiles
275948ffe56aSColin Percival
276048ffe56aSColin Percival	# Remove the offending bits
276148ffe56aSColin Percival	while read FPATH TYPE; do
276248ffe56aSColin Percival		case ${TYPE} in
276348ffe56aSColin Percival		d)
276448ffe56aSColin Percival			rmdir ${BASEDIR}/${FPATH}
276548ffe56aSColin Percival			;;
276648ffe56aSColin Percival		f)
276748ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
276848ffe56aSColin Percival			;;
276948ffe56aSColin Percival		L)
277048ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
277148ffe56aSColin Percival			;;
277248ffe56aSColin Percival		esac
277348ffe56aSColin Percival	done < killfiles
277448ffe56aSColin Percival
277548ffe56aSColin Percival	# Clean up
277648ffe56aSColin Percival	rm newfiles killfiles
277748ffe56aSColin Percival}
277848ffe56aSColin Percival
2779db6b0a61SColin Percival# Install new files, delete old files, and update linker.hints
2780db6b0a61SColin Percivalinstall_files () {
2781db6b0a61SColin Percival	# If we haven't already dealt with the kernel, deal with it.
2782db6b0a61SColin Percival	if ! [ -f $1/kerneldone ]; then
2783db6b0a61SColin Percival		grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
2784db6b0a61SColin Percival		grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
2785db6b0a61SColin Percival
278623d827efSSimon L. B. Nielsen		# Backup current kernel before installing a new one
278723d827efSSimon L. B. Nielsen		backup_kernel || return 1
278823d827efSSimon L. B. Nielsen
2789db6b0a61SColin Percival		# Install new files
2790db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2791db6b0a61SColin Percival
2792db6b0a61SColin Percival		# Remove files which need to be deleted
2793db6b0a61SColin Percival		install_delete INDEX-OLD INDEX-NEW || return 1
2794db6b0a61SColin Percival
2795db6b0a61SColin Percival		# Update linker.hints if necessary
2796db6b0a61SColin Percival		if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2797db6b0a61SColin Percival			kldxref -R /boot/ 2>/dev/null
279848ffe56aSColin Percival		fi
2799db6b0a61SColin Percival
2800db6b0a61SColin Percival		# We've finished updating the kernel.
2801db6b0a61SColin Percival		touch $1/kerneldone
2802db6b0a61SColin Percival
2803db6b0a61SColin Percival		# Do we need to ask for a reboot now?
2804db6b0a61SColin Percival		if [ -f $1/kernelfirst ] &&
2805db6b0a61SColin Percival		    [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2806db6b0a61SColin Percival			cat <<-EOF
2807db6b0a61SColin Percival
2808db6b0a61SColin PercivalKernel updates have been installed.  Please reboot and run
2809db6b0a61SColin Percival"$0 install" again to finish installing updates.
2810db6b0a61SColin Percival			EOF
2811db6b0a61SColin Percival			exit 0
2812db6b0a61SColin Percival		fi
2813db6b0a61SColin Percival	fi
2814db6b0a61SColin Percival
2815db6b0a61SColin Percival	# If we haven't already dealt with the world, deal with it.
2816db6b0a61SColin Percival	if ! [ -f $1/worlddone ]; then
2817cd1ab228SColin Percival		# Create any necessary directories first
2818cd1ab228SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2819cd1ab228SColin Percival		    grep -E '^[^|]+\|d\|' > INDEX-NEW
2820cd1ab228SColin Percival		install_from_index INDEX-NEW || return 1
2821cd1ab228SColin Percival
2822db6b0a61SColin Percival		# Install new shared libraries next
2823db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2824cd1ab228SColin Percival		    grep -vE '^[^|]+\|d\|' |
28259546dbd1SColin Percival		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2826db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2827db6b0a61SColin Percival
2828db6b0a61SColin Percival		# Deal with everything else
2829db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-OLD |
2830cd1ab228SColin Percival		    grep -vE '^[^|]+\|d\|' |
28319546dbd1SColin Percival		    grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD
2832db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2833cd1ab228SColin Percival		    grep -vE '^[^|]+\|d\|' |
28349546dbd1SColin Percival		    grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2835db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2836db6b0a61SColin Percival		install_delete INDEX-OLD INDEX-NEW || return 1
2837db6b0a61SColin Percival
2838db6b0a61SColin Percival		# Rebuild /etc/spwd.db and /etc/pwd.db if necessary.
2839db6b0a61SColin Percival		if [ /etc/master.passwd -nt /etc/spwd.db ] ||
2840db6b0a61SColin Percival		    [ /etc/master.passwd -nt /etc/pwd.db ]; then
2841db6b0a61SColin Percival			pwd_mkdb /etc/master.passwd
2842db6b0a61SColin Percival		fi
2843db6b0a61SColin Percival
2844db6b0a61SColin Percival		# Rebuild /etc/login.conf.db if necessary.
2845db6b0a61SColin Percival		if [ /etc/login.conf -nt /etc/login.conf.db ]; then
2846db6b0a61SColin Percival			cap_mkdb /etc/login.conf
2847db6b0a61SColin Percival		fi
2848db6b0a61SColin Percival
2849db6b0a61SColin Percival		# We've finished installing the world and deleting old files
2850db6b0a61SColin Percival		# which are not shared libraries.
2851db6b0a61SColin Percival		touch $1/worlddone
2852db6b0a61SColin Percival
2853db6b0a61SColin Percival		# Do we need to ask the user to portupgrade now?
2854db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
28559546dbd1SColin Percival		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' |
2856db6b0a61SColin Percival		    cut -f 1 -d '|' |
2857db6b0a61SColin Percival		    sort > newfiles
2858db6b0a61SColin Percival		if grep -vE '^/boot/' $1/INDEX-OLD |
28599546dbd1SColin Percival		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' |
2860db6b0a61SColin Percival		    cut -f 1 -d '|' |
2861db6b0a61SColin Percival		    sort |
2862db6b0a61SColin Percival		    join -v 1 - newfiles |
2863db6b0a61SColin Percival		    grep -q .; then
2864db6b0a61SColin Percival			cat <<-EOF
2865db6b0a61SColin Percival
2866db6b0a61SColin PercivalCompleting this upgrade requires removing old shared object files.
2867db6b0a61SColin PercivalPlease rebuild all installed 3rd party software (e.g., programs
2868db6b0a61SColin Percivalinstalled from the ports tree) and then run "$0 install"
2869db6b0a61SColin Percivalagain to finish installing updates.
2870db6b0a61SColin Percival			EOF
2871db6b0a61SColin Percival			rm newfiles
2872db6b0a61SColin Percival			exit 0
2873db6b0a61SColin Percival		fi
2874db6b0a61SColin Percival		rm newfiles
2875db6b0a61SColin Percival	fi
2876db6b0a61SColin Percival
2877db6b0a61SColin Percival	# Remove old shared libraries
2878db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2879cd1ab228SColin Percival	    grep -vE '^[^|]+\|d\|' |
28809546dbd1SColin Percival	    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2881db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2882cd1ab228SColin Percival	    grep -vE '^[^|]+\|d\|' |
28839546dbd1SColin Percival	    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD
2884db6b0a61SColin Percival	install_delete INDEX-OLD INDEX-NEW || return 1
2885db6b0a61SColin Percival
2886cd1ab228SColin Percival	# Remove old directories
2887*ebc1d19cSColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2888*ebc1d19cSColin Percival	    grep -E '^[^|]+\|d\|' > INDEX-NEW
2889cd1ab228SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2890cd1ab228SColin Percival	    grep -E '^[^|]+\|d\|' > INDEX-OLD
2891cd1ab228SColin Percival	install_delete INDEX-OLD INDEX-NEW || return 1
2892cd1ab228SColin Percival
2893db6b0a61SColin Percival	# Remove temporary files
2894db6b0a61SColin Percival	rm INDEX-OLD INDEX-NEW
289548ffe56aSColin Percival}
289648ffe56aSColin Percival
289748ffe56aSColin Percival# Rearrange bits to allow the installed updates to be rolled back
289848ffe56aSColin Percivalinstall_setup_rollback () {
2899db6b0a61SColin Percival	# Remove the "reboot after installing kernel", "kernel updated", and
2900db6b0a61SColin Percival	# "finished installing the world" flags if present -- they are
2901db6b0a61SColin Percival	# irrelevant when rolling back updates.
2902db6b0a61SColin Percival	if [ -f ${BDHASH}-install/kernelfirst ]; then
2903db6b0a61SColin Percival		rm ${BDHASH}-install/kernelfirst
2904db6b0a61SColin Percival		rm ${BDHASH}-install/kerneldone
2905db6b0a61SColin Percival	fi
2906db6b0a61SColin Percival	if [ -f ${BDHASH}-install/worlddone ]; then
2907db6b0a61SColin Percival		rm ${BDHASH}-install/worlddone
2908db6b0a61SColin Percival	fi
2909db6b0a61SColin Percival
291048ffe56aSColin Percival	if [ -L ${BDHASH}-rollback ]; then
291148ffe56aSColin Percival		mv ${BDHASH}-rollback ${BDHASH}-install/rollback
291248ffe56aSColin Percival	fi
291348ffe56aSColin Percival
291448ffe56aSColin Percival	mv ${BDHASH}-install ${BDHASH}-rollback
291548ffe56aSColin Percival}
291648ffe56aSColin Percival
291748ffe56aSColin Percival# Actually install updates
291848ffe56aSColin Percivalinstall_run () {
291948ffe56aSColin Percival	echo -n "Installing updates..."
292048ffe56aSColin Percival
292148ffe56aSColin Percival	# Make sure we have all the files we should have
292248ffe56aSColin Percival	install_verify ${BDHASH}-install/INDEX-OLD	\
292348ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
292448ffe56aSColin Percival
292548ffe56aSColin Percival	# Remove system immutable flag from files
292648ffe56aSColin Percival	install_unschg ${BDHASH}-install/INDEX-OLD	\
292748ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
292848ffe56aSColin Percival
2929db6b0a61SColin Percival	# Install new files, delete old files, and update linker.hints
2930db6b0a61SColin Percival	install_files ${BDHASH}-install || return 1
293148ffe56aSColin Percival
293248ffe56aSColin Percival	# Rearrange bits to allow the installed updates to be rolled back
293348ffe56aSColin Percival	install_setup_rollback
293448ffe56aSColin Percival
293548ffe56aSColin Percival	echo " done."
293648ffe56aSColin Percival}
293748ffe56aSColin Percival
293848ffe56aSColin Percival# Rearrange bits to allow the previous set of updates to be rolled back next.
293948ffe56aSColin Percivalrollback_setup_rollback () {
294048ffe56aSColin Percival	if [ -L ${BDHASH}-rollback/rollback ]; then
294148ffe56aSColin Percival		mv ${BDHASH}-rollback/rollback rollback-tmp
294248ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
294348ffe56aSColin Percival		rm ${BDHASH}-rollback
294448ffe56aSColin Percival		mv rollback-tmp ${BDHASH}-rollback
294548ffe56aSColin Percival	else
294648ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
294748ffe56aSColin Percival		rm ${BDHASH}-rollback
294848ffe56aSColin Percival	fi
294948ffe56aSColin Percival}
295048ffe56aSColin Percival
2951db6b0a61SColin Percival# Install old files, delete new files, and update linker.hints
2952db6b0a61SColin Percivalrollback_files () {
29531ec4fb3aSColin Percival	# Install old shared library files which don't have the same path as
29541ec4fb3aSColin Percival	# a new shared library file.
29551ec4fb3aSColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2956fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
29571ec4fb3aSColin Percival	    cut -f 1 -d '|' |
29581ec4fb3aSColin Percival	    sort > INDEX-NEW.libs.flist
2959db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2960fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
29611ec4fb3aSColin Percival	    sort -k 1,1 -t '|' - |
29621ec4fb3aSColin Percival	    join -t '|' -v 1 - INDEX-NEW.libs.flist > INDEX-OLD
2963db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
2964db6b0a61SColin Percival
2965db6b0a61SColin Percival	# Deal with files which are neither kernel nor shared library
2966db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2967fd0963d1SColin Percival	    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
2968db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2969fd0963d1SColin Percival	    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
2970db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
2971db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
2972db6b0a61SColin Percival
29731ec4fb3aSColin Percival	# Install any old shared library files which we didn't install above.
29741ec4fb3aSColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2975fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
29761ec4fb3aSColin Percival	    sort -k 1,1 -t '|' - |
29771ec4fb3aSColin Percival	    join -t '|' - INDEX-NEW.libs.flist > INDEX-OLD
29781ec4fb3aSColin Percival	install_from_index INDEX-OLD || return 1
29791ec4fb3aSColin Percival
2980db6b0a61SColin Percival	# Delete unneeded shared library files
2981db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2982fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
2983db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2984fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
2985db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
2986db6b0a61SColin Percival
2987db6b0a61SColin Percival	# Deal with kernel files
2988db6b0a61SColin Percival	grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
2989db6b0a61SColin Percival	grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
2990db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
2991db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
2992db6b0a61SColin Percival	if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2993db6b0a61SColin Percival		kldxref -R /boot/ 2>/dev/null
2994db6b0a61SColin Percival	fi
2995db6b0a61SColin Percival
2996db6b0a61SColin Percival	# Remove temporary files
29970e0d8d5aSColin Percival	rm INDEX-OLD INDEX-NEW INDEX-NEW.libs.flist
2998db6b0a61SColin Percival}
2999db6b0a61SColin Percival
300048ffe56aSColin Percival# Actually rollback updates
300148ffe56aSColin Percivalrollback_run () {
300248ffe56aSColin Percival	echo -n "Uninstalling updates..."
300348ffe56aSColin Percival
300448ffe56aSColin Percival	# If there are updates waiting to be installed, remove them; we
300548ffe56aSColin Percival	# want the user to re-run 'fetch' after rolling back updates.
300648ffe56aSColin Percival	if [ -L ${BDHASH}-install ]; then
300748ffe56aSColin Percival		rm -r ${BDHASH}-install/
300848ffe56aSColin Percival		rm ${BDHASH}-install
300948ffe56aSColin Percival	fi
301048ffe56aSColin Percival
301148ffe56aSColin Percival	# Make sure we have all the files we should have
301248ffe56aSColin Percival	install_verify ${BDHASH}-rollback/INDEX-NEW	\
301348ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
301448ffe56aSColin Percival
301548ffe56aSColin Percival	# Remove system immutable flag from files
301648ffe56aSColin Percival	install_unschg ${BDHASH}-rollback/INDEX-NEW	\
301748ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
301848ffe56aSColin Percival
3019db6b0a61SColin Percival	# Install old files, delete new files, and update linker.hints
3020db6b0a61SColin Percival	rollback_files ${BDHASH}-rollback || return 1
302148ffe56aSColin Percival
302248ffe56aSColin Percival	# Remove the rollback directory and the symlink pointing to it; and
302348ffe56aSColin Percival	# rearrange bits to allow the previous set of updates to be rolled
302448ffe56aSColin Percival	# back next.
302548ffe56aSColin Percival	rollback_setup_rollback
302648ffe56aSColin Percival
302748ffe56aSColin Percival	echo " done."
302848ffe56aSColin Percival}
302948ffe56aSColin Percival
303008e23beeSColin Percival# Compare INDEX-ALL and INDEX-PRESENT and print warnings about differences.
303108e23beeSColin PercivalIDS_compare () {
3032bb10a826SColin Percival	# Get all the lines which mismatch in something other than file
3033bb10a826SColin Percival	# flags.  We ignore file flags because sysinstall doesn't seem to
3034bb10a826SColin Percival	# set them when it installs FreeBSD; warning about these adds a
3035bb10a826SColin Percival	# very large amount of noise.
3036bb10a826SColin Percival	cut -f 1-5,7-8 -d '|' $1 > $1.noflags
3037bb10a826SColin Percival	sort -k 1,1 -t '|' $1.noflags > $1.sorted
3038bb10a826SColin Percival	cut -f 1-5,7-8 -d '|' $2 |
3039bb10a826SColin Percival	    comm -13 $1.noflags - |
3040bb10a826SColin Percival	    fgrep -v '|-|||||' |
304108e23beeSColin Percival	    sort -k 1,1 -t '|' |
304208e23beeSColin Percival	    join -t '|' $1.sorted - > INDEX-NOTMATCHING
304308e23beeSColin Percival
304408e23beeSColin Percival	# Ignore files which match IDSIGNOREPATHS.
304508e23beeSColin Percival	for X in ${IDSIGNOREPATHS}; do
304608e23beeSColin Percival		grep -E "^${X}" INDEX-NOTMATCHING
304708e23beeSColin Percival	done |
304808e23beeSColin Percival	    sort -u |
304908e23beeSColin Percival	    comm -13 - INDEX-NOTMATCHING > INDEX-NOTMATCHING.tmp
305008e23beeSColin Percival	mv INDEX-NOTMATCHING.tmp INDEX-NOTMATCHING
305108e23beeSColin Percival
305208e23beeSColin Percival	# Go through the lines and print warnings.
3053aa60062eSColin Percival	local IFS='|'
3054aa60062eSColin Percival	while read FPATH TYPE OWNER GROUP PERM HASH LINK P_TYPE P_OWNER P_GROUP P_PERM P_HASH P_LINK; do
305508e23beeSColin Percival		# Warn about different object types.
305608e23beeSColin Percival		if ! [ "${TYPE}" = "${P_TYPE}" ]; then
305708e23beeSColin Percival			echo -n "${FPATH} is a "
305808e23beeSColin Percival			case "${P_TYPE}" in
305908e23beeSColin Percival			f)	echo -n "regular file, "
306008e23beeSColin Percival				;;
306108e23beeSColin Percival			d)	echo -n "directory, "
306208e23beeSColin Percival				;;
306308e23beeSColin Percival			L)	echo -n "symlink, "
306408e23beeSColin Percival				;;
306508e23beeSColin Percival			esac
306608e23beeSColin Percival			echo -n "but should be a "
306708e23beeSColin Percival			case "${TYPE}" in
306808e23beeSColin Percival			f)	echo -n "regular file."
306908e23beeSColin Percival				;;
307008e23beeSColin Percival			d)	echo -n "directory."
307108e23beeSColin Percival				;;
307208e23beeSColin Percival			L)	echo -n "symlink."
307308e23beeSColin Percival				;;
307408e23beeSColin Percival			esac
307508e23beeSColin Percival			echo
307608e23beeSColin Percival
307708e23beeSColin Percival			# Skip other tests, since they don't make sense if
307808e23beeSColin Percival			# we're comparing different object types.
307908e23beeSColin Percival			continue
308008e23beeSColin Percival		fi
308108e23beeSColin Percival
308208e23beeSColin Percival		# Warn about different owners.
308308e23beeSColin Percival		if ! [ "${OWNER}" = "${P_OWNER}" ]; then
308408e23beeSColin Percival			echo -n "${FPATH} is owned by user id ${P_OWNER}, "
308508e23beeSColin Percival			echo "but should be owned by user id ${OWNER}."
308608e23beeSColin Percival		fi
308708e23beeSColin Percival
308808e23beeSColin Percival		# Warn about different groups.
308908e23beeSColin Percival		if ! [ "${GROUP}" = "${P_GROUP}" ]; then
309008e23beeSColin Percival			echo -n "${FPATH} is owned by group id ${P_GROUP}, "
309108e23beeSColin Percival			echo "but should be owned by group id ${GROUP}."
309208e23beeSColin Percival		fi
309308e23beeSColin Percival
309408e23beeSColin Percival		# Warn about different permissions.  We do not warn about
309508e23beeSColin Percival		# different permissions on symlinks, since some archivers
309608e23beeSColin Percival		# don't extract symlink permissions correctly and they are
309708e23beeSColin Percival		# ignored anyway.
309808e23beeSColin Percival		if ! [ "${PERM}" = "${P_PERM}" ] &&
309908e23beeSColin Percival		    ! [ "${TYPE}" = "L" ]; then
310008e23beeSColin Percival			echo -n "${FPATH} has ${P_PERM} permissions, "
310108e23beeSColin Percival			echo "but should have ${PERM} permissions."
310208e23beeSColin Percival		fi
310308e23beeSColin Percival
310408e23beeSColin Percival		# Warn about different file hashes / symlink destinations.
310508e23beeSColin Percival		if ! [ "${HASH}" = "${P_HASH}" ]; then
310608e23beeSColin Percival			if [ "${TYPE}" = "L" ]; then
310708e23beeSColin Percival				echo -n "${FPATH} is a symlink to ${P_HASH}, "
310808e23beeSColin Percival				echo "but should be a symlink to ${HASH}."
310908e23beeSColin Percival			fi
311008e23beeSColin Percival			if [ "${TYPE}" = "f" ]; then
311108e23beeSColin Percival				echo -n "${FPATH} has SHA256 hash ${P_HASH}, "
311208e23beeSColin Percival				echo "but should have SHA256 hash ${HASH}."
311308e23beeSColin Percival			fi
311408e23beeSColin Percival		fi
311508e23beeSColin Percival
311608e23beeSColin Percival		# We don't warn about different hard links, since some
311708e23beeSColin Percival		# some archivers break hard links, and as long as the
311808e23beeSColin Percival		# underlying data is correct they really don't matter.
311908e23beeSColin Percival	done < INDEX-NOTMATCHING
312008e23beeSColin Percival
312108e23beeSColin Percival	# Clean up
3122bb10a826SColin Percival	rm $1 $1.noflags $1.sorted $2 INDEX-NOTMATCHING
312308e23beeSColin Percival}
312408e23beeSColin Percival
312508e23beeSColin Percival# Do the work involved in comparing the system to a "known good" index
312608e23beeSColin PercivalIDS_run () {
312708e23beeSColin Percival	workdir_init || return 1
312808e23beeSColin Percival
312908e23beeSColin Percival	# Prepare the mirror list.
313008e23beeSColin Percival	fetch_pick_server_init && fetch_pick_server
313108e23beeSColin Percival
313208e23beeSColin Percival	# Try to fetch the public key until we run out of servers.
313308e23beeSColin Percival	while ! fetch_key; do
313408e23beeSColin Percival		fetch_pick_server || return 1
313508e23beeSColin Percival	done
313608e23beeSColin Percival
313708e23beeSColin Percival	# Try to fetch the metadata index signature ("tag") until we run
313808e23beeSColin Percival	# out of available servers; and sanity check the downloaded tag.
313908e23beeSColin Percival	while ! fetch_tag; do
314008e23beeSColin Percival		fetch_pick_server || return 1
314108e23beeSColin Percival	done
314208e23beeSColin Percival	fetch_tagsanity || return 1
314308e23beeSColin Percival
314408e23beeSColin Percival	# Fetch INDEX-OLD and INDEX-ALL.
314508e23beeSColin Percival	fetch_metadata INDEX-OLD INDEX-ALL || return 1
314608e23beeSColin Percival
314708e23beeSColin Percival	# Generate filtered INDEX-OLD and INDEX-ALL files containing only
314808e23beeSColin Percival	# the components we want and without anything marked as "Ignore".
314908e23beeSColin Percival	fetch_filter_metadata INDEX-OLD || return 1
315008e23beeSColin Percival	fetch_filter_metadata INDEX-ALL || return 1
315108e23beeSColin Percival
315208e23beeSColin Percival	# Merge the INDEX-OLD and INDEX-ALL files into INDEX-ALL.
315308e23beeSColin Percival	sort INDEX-OLD INDEX-ALL > INDEX-ALL.tmp
315408e23beeSColin Percival	mv INDEX-ALL.tmp INDEX-ALL
315508e23beeSColin Percival	rm INDEX-OLD
315608e23beeSColin Percival
315708e23beeSColin Percival	# Translate /boot/${KERNCONF} to ${KERNELDIR}
315808e23beeSColin Percival	fetch_filter_kernel_names INDEX-ALL ${KERNCONF}
315908e23beeSColin Percival
316008e23beeSColin Percival	# Inspect the system and generate an INDEX-PRESENT file.
316108e23beeSColin Percival	fetch_inspect_system INDEX-ALL INDEX-PRESENT /dev/null || return 1
316208e23beeSColin Percival
316308e23beeSColin Percival	# Compare INDEX-ALL and INDEX-PRESENT and print warnings about any
316408e23beeSColin Percival	# differences.
316508e23beeSColin Percival	IDS_compare INDEX-ALL INDEX-PRESENT
316608e23beeSColin Percival}
316708e23beeSColin Percival
316848ffe56aSColin Percival#### Main functions -- call parameter-handling and core functions
316948ffe56aSColin Percival
317048ffe56aSColin Percival# Using the command line, configuration file, and defaults,
317148ffe56aSColin Percival# set all the parameters which are needed later.
317248ffe56aSColin Percivalget_params () {
317348ffe56aSColin Percival	init_params
317448ffe56aSColin Percival	parse_cmdline $@
317548ffe56aSColin Percival	parse_conffile
317648ffe56aSColin Percival	default_params
317748ffe56aSColin Percival}
317848ffe56aSColin Percival
317948ffe56aSColin Percival# Fetch command.  Make sure that we're being called
318048ffe56aSColin Percival# interactively, then run fetch_check_params and fetch_run
318148ffe56aSColin Percivalcmd_fetch () {
318248ffe56aSColin Percival	if [ ! -t 0 ]; then
318348ffe56aSColin Percival		echo -n "`basename $0` fetch should not "
318448ffe56aSColin Percival		echo "be run non-interactively."
318548ffe56aSColin Percival		echo "Run `basename $0` cron instead."
318648ffe56aSColin Percival		exit 1
318748ffe56aSColin Percival	fi
318848ffe56aSColin Percival	fetch_check_params
318948ffe56aSColin Percival	fetch_run || exit 1
319048ffe56aSColin Percival}
319148ffe56aSColin Percival
319248ffe56aSColin Percival# Cron command.  Make sure the parameters are sensible; wait
319348ffe56aSColin Percival# rand(3600) seconds; then fetch updates.  While fetching updates,
319448ffe56aSColin Percival# send output to a temporary file; only print that file if the
319548ffe56aSColin Percival# fetching failed.
319648ffe56aSColin Percivalcmd_cron () {
319748ffe56aSColin Percival	fetch_check_params
319848ffe56aSColin Percival	sleep `jot -r 1 0 3600`
319948ffe56aSColin Percival
320048ffe56aSColin Percival	TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1
320148ffe56aSColin Percival	if ! fetch_run >> ${TMPFILE} ||
320248ffe56aSColin Percival	    ! grep -q "No updates needed" ${TMPFILE} ||
320348ffe56aSColin Percival	    [ ${VERBOSELEVEL} = "debug" ]; then
320448ffe56aSColin Percival		mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE}
320548ffe56aSColin Percival	fi
320648ffe56aSColin Percival
320748ffe56aSColin Percival	rm ${TMPFILE}
320848ffe56aSColin Percival}
320948ffe56aSColin Percival
3210db6b0a61SColin Percival# Fetch files for upgrading to a new release.
3211db6b0a61SColin Percivalcmd_upgrade () {
3212db6b0a61SColin Percival	upgrade_check_params
3213db6b0a61SColin Percival	upgrade_run || exit 1
3214db6b0a61SColin Percival}
3215db6b0a61SColin Percival
321648ffe56aSColin Percival# Install downloaded updates.
321748ffe56aSColin Percivalcmd_install () {
321848ffe56aSColin Percival	install_check_params
321948ffe56aSColin Percival	install_run || exit 1
322048ffe56aSColin Percival}
322148ffe56aSColin Percival
322248ffe56aSColin Percival# Rollback most recently installed updates.
322348ffe56aSColin Percivalcmd_rollback () {
322448ffe56aSColin Percival	rollback_check_params
322548ffe56aSColin Percival	rollback_run || exit 1
322648ffe56aSColin Percival}
322748ffe56aSColin Percival
322808e23beeSColin Percival# Compare system against a "known good" index.
322908e23beeSColin Percivalcmd_IDS () {
323008e23beeSColin Percival	IDS_check_params
323108e23beeSColin Percival	IDS_run || exit 1
323208e23beeSColin Percival}
323308e23beeSColin Percival
323448ffe56aSColin Percival#### Entry point
323548ffe56aSColin Percival
323648ffe56aSColin Percival# Make sure we find utilities from the base system
323748ffe56aSColin Percivalexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
323848ffe56aSColin Percival
32399c990fb2SGordon Tetlow# Set a pager if the user doesn't
32409c990fb2SGordon Tetlowif [ -z "$PAGER" ]; then
32419c990fb2SGordon Tetlow	PAGER=/usr/bin/more
32429c990fb2SGordon Tetlowfi
32439c990fb2SGordon Tetlow
3244f2890dbdSColin Percival# Set LC_ALL in order to avoid problems with character ranges like [A-Z].
3245f2890dbdSColin Percivalexport LC_ALL=C
3246f2890dbdSColin Percival
324748ffe56aSColin Percivalget_params $@
324848ffe56aSColin Percivalfor COMMAND in ${COMMANDS}; do
324948ffe56aSColin Percival	cmd_${COMMAND}
325048ffe56aSColin Percivaldone
3251