xref: /freebsd/usr.sbin/freebsd-update/freebsd-update.sh (revision 1ec4fb3aa4d505c5fd81ab96cdbf026aa9fabefc)
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
5948ffe56aSColin PercivalEOF
6048ffe56aSColin Percival	exit 0
6148ffe56aSColin Percival}
6248ffe56aSColin Percival
6348ffe56aSColin Percival#### Configuration processing functions
6448ffe56aSColin Percival
6548ffe56aSColin Percival#-
6648ffe56aSColin Percival# Configuration options are set in the following order of priority:
6748ffe56aSColin Percival# 1. Command line options
6848ffe56aSColin Percival# 2. Configuration file options
6948ffe56aSColin Percival# 3. Default options
7048ffe56aSColin Percival# In addition, certain options (e.g., IgnorePaths) can be specified multiple
7148ffe56aSColin Percival# times and (as long as these are all in the same place, e.g., inside the
7248ffe56aSColin Percival# configuration file) they will accumulate.  Finally, because the path to the
7348ffe56aSColin Percival# configuration file can be specified at the command line, the entire command
7448ffe56aSColin Percival# line must be processed before we start reading the configuration file.
7548ffe56aSColin Percival#
7648ffe56aSColin Percival# Sound like a mess?  It is.  Here's how we handle this:
7748ffe56aSColin Percival# 1. Initialize CONFFILE and all the options to "".
7848ffe56aSColin Percival# 2. Process the command line.  Throw an error if a non-accumulating option
7948ffe56aSColin Percival#    is specified twice.
8048ffe56aSColin Percival# 3. If CONFFILE is "", set CONFFILE to /etc/freebsd-update.conf .
8148ffe56aSColin Percival# 4. For all the configuration options X, set X_saved to X.
8248ffe56aSColin Percival# 5. Initialize all the options to "".
8348ffe56aSColin Percival# 6. Read CONFFILE line by line, parsing options.
8448ffe56aSColin Percival# 7. For each configuration option X, set X to X_saved iff X_saved is not "".
8548ffe56aSColin Percival# 8. Repeat steps 4-7, except setting options to their default values at (6).
8648ffe56aSColin Percival
8748ffe56aSColin PercivalCONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE
8848ffe56aSColin Percival    KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED
89db6b0a61SColin Percival    BASEDIR VERBOSELEVEL TARGETRELEASE STRICTCOMPONENTS MERGECHANGES"
9048ffe56aSColin Percival
9148ffe56aSColin Percival# Set all the configuration options to "".
9248ffe56aSColin Percivalnullconfig () {
9348ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
9448ffe56aSColin Percival		eval ${X}=""
9548ffe56aSColin Percival	done
9648ffe56aSColin Percival}
9748ffe56aSColin Percival
9848ffe56aSColin Percival# For each configuration option X, set X_saved to X.
9948ffe56aSColin Percivalsaveconfig () {
10048ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
10148ffe56aSColin Percival		eval ${X}_saved=\$${X}
10248ffe56aSColin Percival	done
10348ffe56aSColin Percival}
10448ffe56aSColin Percival
10548ffe56aSColin Percival# For each configuration option X, set X to X_saved if X_saved is not "".
10648ffe56aSColin Percivalmergeconfig () {
10748ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
10848ffe56aSColin Percival		eval _=\$${X}_saved
10948ffe56aSColin Percival		if ! [ -z "${_}" ]; then
11048ffe56aSColin Percival			eval ${X}=\$${X}_saved
11148ffe56aSColin Percival		fi
11248ffe56aSColin Percival	done
11348ffe56aSColin Percival}
11448ffe56aSColin Percival
11548ffe56aSColin Percival# Set the trusted keyprint.
11648ffe56aSColin Percivalconfig_KeyPrint () {
11748ffe56aSColin Percival	if [ -z ${KEYPRINT} ]; then
11848ffe56aSColin Percival		KEYPRINT=$1
11948ffe56aSColin Percival	else
12048ffe56aSColin Percival		return 1
12148ffe56aSColin Percival	fi
12248ffe56aSColin Percival}
12348ffe56aSColin Percival
12448ffe56aSColin Percival# Set the working directory.
12548ffe56aSColin Percivalconfig_WorkDir () {
12648ffe56aSColin Percival	if [ -z ${WORKDIR} ]; then
12748ffe56aSColin Percival		WORKDIR=$1
12848ffe56aSColin Percival	else
12948ffe56aSColin Percival		return 1
13048ffe56aSColin Percival	fi
13148ffe56aSColin Percival}
13248ffe56aSColin Percival
13348ffe56aSColin Percival# Set the name of the server (pool) from which to fetch updates
13448ffe56aSColin Percivalconfig_ServerName () {
13548ffe56aSColin Percival	if [ -z ${SERVERNAME} ]; then
13648ffe56aSColin Percival		SERVERNAME=$1
13748ffe56aSColin Percival	else
13848ffe56aSColin Percival		return 1
13948ffe56aSColin Percival	fi
14048ffe56aSColin Percival}
14148ffe56aSColin Percival
14248ffe56aSColin Percival# Set the address to which 'cron' output will be mailed.
14348ffe56aSColin Percivalconfig_MailTo () {
14448ffe56aSColin Percival	if [ -z ${MAILTO} ]; then
14548ffe56aSColin Percival		MAILTO=$1
14648ffe56aSColin Percival	else
14748ffe56aSColin Percival		return 1
14848ffe56aSColin Percival	fi
14948ffe56aSColin Percival}
15048ffe56aSColin Percival
15148ffe56aSColin Percival# Set whether FreeBSD Update is allowed to add files (or directories, or
15248ffe56aSColin Percival# symlinks) which did not previously exist.
15348ffe56aSColin Percivalconfig_AllowAdd () {
15448ffe56aSColin Percival	if [ -z ${ALLOWADD} ]; then
15548ffe56aSColin Percival		case $1 in
15648ffe56aSColin Percival		[Yy][Ee][Ss])
15748ffe56aSColin Percival			ALLOWADD=yes
15848ffe56aSColin Percival			;;
15948ffe56aSColin Percival		[Nn][Oo])
16048ffe56aSColin Percival			ALLOWADD=no
16148ffe56aSColin Percival			;;
16248ffe56aSColin Percival		*)
16348ffe56aSColin Percival			return 1
16448ffe56aSColin Percival			;;
16548ffe56aSColin Percival		esac
16648ffe56aSColin Percival	else
16748ffe56aSColin Percival		return 1
16848ffe56aSColin Percival	fi
16948ffe56aSColin Percival}
17048ffe56aSColin Percival
17148ffe56aSColin Percival# Set whether FreeBSD Update is allowed to remove files/directories/symlinks.
17248ffe56aSColin Percivalconfig_AllowDelete () {
17348ffe56aSColin Percival	if [ -z ${ALLOWDELETE} ]; then
17448ffe56aSColin Percival		case $1 in
17548ffe56aSColin Percival		[Yy][Ee][Ss])
17648ffe56aSColin Percival			ALLOWDELETE=yes
17748ffe56aSColin Percival			;;
17848ffe56aSColin Percival		[Nn][Oo])
17948ffe56aSColin Percival			ALLOWDELETE=no
18048ffe56aSColin Percival			;;
18148ffe56aSColin Percival		*)
18248ffe56aSColin Percival			return 1
18348ffe56aSColin Percival			;;
18448ffe56aSColin Percival		esac
18548ffe56aSColin Percival	else
18648ffe56aSColin Percival		return 1
18748ffe56aSColin Percival	fi
18848ffe56aSColin Percival}
18948ffe56aSColin Percival
19048ffe56aSColin Percival# Set whether FreeBSD Update should keep existing inode ownership,
19148ffe56aSColin Percival# permissions, and flags, in the event that they have been modified locally
19248ffe56aSColin Percival# after the release.
19348ffe56aSColin Percivalconfig_KeepModifiedMetadata () {
19448ffe56aSColin Percival	if [ -z ${KEEPMODIFIEDMETADATA} ]; then
19548ffe56aSColin Percival		case $1 in
19648ffe56aSColin Percival		[Yy][Ee][Ss])
19748ffe56aSColin Percival			KEEPMODIFIEDMETADATA=yes
19848ffe56aSColin Percival			;;
19948ffe56aSColin Percival		[Nn][Oo])
20048ffe56aSColin Percival			KEEPMODIFIEDMETADATA=no
20148ffe56aSColin Percival			;;
20248ffe56aSColin Percival		*)
20348ffe56aSColin Percival			return 1
20448ffe56aSColin Percival			;;
20548ffe56aSColin Percival		esac
20648ffe56aSColin Percival	else
20748ffe56aSColin Percival		return 1
20848ffe56aSColin Percival	fi
20948ffe56aSColin Percival}
21048ffe56aSColin Percival
21148ffe56aSColin Percival# Add to the list of components which should be kept updated.
21248ffe56aSColin Percivalconfig_Components () {
21348ffe56aSColin Percival	for C in $@; do
21448ffe56aSColin Percival		COMPONENTS="${COMPONENTS} ${C}"
21548ffe56aSColin Percival	done
21648ffe56aSColin Percival}
21748ffe56aSColin Percival
21848ffe56aSColin Percival# Add to the list of paths under which updates will be ignored.
21948ffe56aSColin Percivalconfig_IgnorePaths () {
22048ffe56aSColin Percival	for C in $@; do
22148ffe56aSColin Percival		IGNOREPATHS="${IGNOREPATHS} ${C}"
22248ffe56aSColin Percival	done
22348ffe56aSColin Percival}
22448ffe56aSColin Percival
22548ffe56aSColin Percival# Add to the list of paths within which updates will be performed only if the
22648ffe56aSColin Percival# file on disk has not been modified locally.
22748ffe56aSColin Percivalconfig_UpdateIfUnmodified () {
22848ffe56aSColin Percival	for C in $@; do
22948ffe56aSColin Percival		UPDATEIFUNMODIFIED="${UPDATEIFUNMODIFIED} ${C}"
23048ffe56aSColin Percival	done
23148ffe56aSColin Percival}
23248ffe56aSColin Percival
233db6b0a61SColin Percival# Add to the list of paths within which updates to text files will be merged
234db6b0a61SColin Percival# instead of overwritten.
235db6b0a61SColin Percivalconfig_MergeChanges () {
236db6b0a61SColin Percival	for C in $@; do
237db6b0a61SColin Percival		MERGECHANGES="${MERGECHANGES} ${C}"
238db6b0a61SColin Percival	done
239db6b0a61SColin Percival}
240db6b0a61SColin Percival
24148ffe56aSColin Percival# Work on a FreeBSD installation mounted under $1
24248ffe56aSColin Percivalconfig_BaseDir () {
24348ffe56aSColin Percival	if [ -z ${BASEDIR} ]; then
24448ffe56aSColin Percival		BASEDIR=$1
24548ffe56aSColin Percival	else
24648ffe56aSColin Percival		return 1
24748ffe56aSColin Percival	fi
24848ffe56aSColin Percival}
24948ffe56aSColin Percival
250db6b0a61SColin Percival# When fetching upgrades, should we assume the user wants exactly the
251db6b0a61SColin Percival# components listed in COMPONENTS, rather than trying to guess based on
252db6b0a61SColin Percival# what's currently installed?
253db6b0a61SColin Percivalconfig_StrictComponents () {
254db6b0a61SColin Percival	if [ -z ${STRICTCOMPONENTS} ]; then
255db6b0a61SColin Percival		case $1 in
256db6b0a61SColin Percival		[Yy][Ee][Ss])
257db6b0a61SColin Percival			STRICTCOMPONENTS=yes
258db6b0a61SColin Percival			;;
259db6b0a61SColin Percival		[Nn][Oo])
260db6b0a61SColin Percival			STRICTCOMPONENTS=no
261db6b0a61SColin Percival			;;
262db6b0a61SColin Percival		*)
263db6b0a61SColin Percival			return 1
264db6b0a61SColin Percival			;;
265db6b0a61SColin Percival		esac
266db6b0a61SColin Percival	else
267db6b0a61SColin Percival		return 1
268db6b0a61SColin Percival	fi
269db6b0a61SColin Percival}
270db6b0a61SColin Percival
271db6b0a61SColin Percival# Upgrade to FreeBSD $1
272db6b0a61SColin Percivalconfig_TargetRelease () {
273db6b0a61SColin Percival	if [ -z ${TARGETRELEASE} ]; then
274db6b0a61SColin Percival		TARGETRELEASE=$1
275db6b0a61SColin Percival	else
276db6b0a61SColin Percival		return 1
277db6b0a61SColin Percival	fi
278db6b0a61SColin Percival}
279db6b0a61SColin Percival
28048ffe56aSColin Percival# Define what happens to output of utilities
28148ffe56aSColin Percivalconfig_VerboseLevel () {
28248ffe56aSColin Percival	if [ -z ${VERBOSELEVEL} ]; then
28348ffe56aSColin Percival		case $1 in
28448ffe56aSColin Percival		[Dd][Ee][Bb][Uu][Gg])
28548ffe56aSColin Percival			VERBOSELEVEL=debug
28648ffe56aSColin Percival			;;
28748ffe56aSColin Percival		[Nn][Oo][Ss][Tt][Aa][Tt][Ss])
28848ffe56aSColin Percival			VERBOSELEVEL=nostats
28948ffe56aSColin Percival			;;
29048ffe56aSColin Percival		[Ss][Tt][Aa][Tt][Ss])
29148ffe56aSColin Percival			VERBOSELEVEL=stats
29248ffe56aSColin Percival			;;
29348ffe56aSColin Percival		*)
29448ffe56aSColin Percival			return 1
29548ffe56aSColin Percival			;;
29648ffe56aSColin Percival		esac
29748ffe56aSColin Percival	else
29848ffe56aSColin Percival		return 1
29948ffe56aSColin Percival	fi
30048ffe56aSColin Percival}
30148ffe56aSColin Percival
30248ffe56aSColin Percival# Handle one line of configuration
30348ffe56aSColin Percivalconfigline () {
30448ffe56aSColin Percival	if [ $# -eq 0 ]; then
30548ffe56aSColin Percival		return
30648ffe56aSColin Percival	fi
30748ffe56aSColin Percival
30848ffe56aSColin Percival	OPT=$1
30948ffe56aSColin Percival	shift
31048ffe56aSColin Percival	config_${OPT} $@
31148ffe56aSColin Percival}
31248ffe56aSColin Percival
31348ffe56aSColin Percival#### Parameter handling functions.
31448ffe56aSColin Percival
31548ffe56aSColin Percival# Initialize parameters to null, just in case they're
31648ffe56aSColin Percival# set in the environment.
31748ffe56aSColin Percivalinit_params () {
31848ffe56aSColin Percival	# Configration settings
31948ffe56aSColin Percival	nullconfig
32048ffe56aSColin Percival
32148ffe56aSColin Percival	# No configuration file set yet
32248ffe56aSColin Percival	CONFFILE=""
32348ffe56aSColin Percival
32448ffe56aSColin Percival	# No commands specified yet
32548ffe56aSColin Percival	COMMANDS=""
32648ffe56aSColin Percival}
32748ffe56aSColin Percival
32848ffe56aSColin Percival# Parse the command line
32948ffe56aSColin Percivalparse_cmdline () {
33048ffe56aSColin Percival	while [ $# -gt 0 ]; do
33148ffe56aSColin Percival		case "$1" in
33248ffe56aSColin Percival		# Location of configuration file
33348ffe56aSColin Percival		-f)
33448ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi
33548ffe56aSColin Percival			if [ ! -z "${CONFFILE}" ]; then usage; fi
33648ffe56aSColin Percival			shift; CONFFILE="$1"
33748ffe56aSColin Percival			;;
33848ffe56aSColin Percival
33948ffe56aSColin Percival		# Configuration file equivalents
34048ffe56aSColin Percival		-b)
34148ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
34248ffe56aSColin Percival			config_BaseDir $1 || usage
34348ffe56aSColin Percival			;;
34448ffe56aSColin Percival		-d)
34548ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
34648ffe56aSColin Percival			config_WorkDir $1 || usage
34748ffe56aSColin Percival			;;
34848ffe56aSColin Percival		-k)
34948ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
35048ffe56aSColin Percival			config_KeyPrint $1 || usage
35148ffe56aSColin Percival			;;
35248ffe56aSColin Percival		-s)
35348ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
35448ffe56aSColin Percival			config_ServerName $1 || usage
35548ffe56aSColin Percival			;;
356db6b0a61SColin Percival		-r)
357db6b0a61SColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
358db6b0a61SColin Percival			config_TargetRelease $1 || usage
359db6b0a61SColin Percival			;;
36048ffe56aSColin Percival		-t)
36148ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
36248ffe56aSColin Percival			config_MailTo $1 || usage
36348ffe56aSColin Percival			;;
36448ffe56aSColin Percival		-v)
36548ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
36648ffe56aSColin Percival			config_VerboseLevel $1 || usage
36748ffe56aSColin Percival			;;
36848ffe56aSColin Percival
36948ffe56aSColin Percival		# Aliases for "-v debug" and "-v nostats"
37048ffe56aSColin Percival		--debug)
37148ffe56aSColin Percival			config_VerboseLevel debug || usage
37248ffe56aSColin Percival			;;
37348ffe56aSColin Percival		--no-stats)
37448ffe56aSColin Percival			config_VerboseLevel nostats || usage
37548ffe56aSColin Percival			;;
37648ffe56aSColin Percival
37748ffe56aSColin Percival		# Commands
378db6b0a61SColin Percival		cron | fetch | upgrade | install | rollback)
37948ffe56aSColin Percival			COMMANDS="${COMMANDS} $1"
38048ffe56aSColin Percival			;;
38148ffe56aSColin Percival
38248ffe56aSColin Percival		# Anything else is an error
38348ffe56aSColin Percival		*)
38448ffe56aSColin Percival			usage
38548ffe56aSColin Percival			;;
38648ffe56aSColin Percival		esac
38748ffe56aSColin Percival		shift
38848ffe56aSColin Percival	done
38948ffe56aSColin Percival
39048ffe56aSColin Percival	# Make sure we have at least one command
39148ffe56aSColin Percival	if [ -z "${COMMANDS}" ]; then
39248ffe56aSColin Percival		usage
39348ffe56aSColin Percival	fi
39448ffe56aSColin Percival}
39548ffe56aSColin Percival
39648ffe56aSColin Percival# Parse the configuration file
39748ffe56aSColin Percivalparse_conffile () {
39848ffe56aSColin Percival	# If a configuration file was specified on the command line, check
39948ffe56aSColin Percival	# that it exists and is readable.
40048ffe56aSColin Percival	if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
40148ffe56aSColin Percival		echo -n "File does not exist "
40248ffe56aSColin Percival		echo -n "or is not readable: "
40348ffe56aSColin Percival		echo ${CONFFILE}
40448ffe56aSColin Percival		exit 1
40548ffe56aSColin Percival	fi
40648ffe56aSColin Percival
40748ffe56aSColin Percival	# If a configuration file was not specified on the command line,
40848ffe56aSColin Percival	# use the default configuration file path.  If that default does
40948ffe56aSColin Percival	# not exist, give up looking for any configuration.
41048ffe56aSColin Percival	if [ -z "${CONFFILE}" ]; then
41148ffe56aSColin Percival		CONFFILE="/etc/freebsd-update.conf"
41248ffe56aSColin Percival		if [ ! -r "${CONFFILE}" ]; then
41348ffe56aSColin Percival			return
41448ffe56aSColin Percival		fi
41548ffe56aSColin Percival	fi
41648ffe56aSColin Percival
41748ffe56aSColin Percival	# Save the configuration options specified on the command line, and
41848ffe56aSColin Percival	# clear all the options in preparation for reading the config file.
41948ffe56aSColin Percival	saveconfig
42048ffe56aSColin Percival	nullconfig
42148ffe56aSColin Percival
42248ffe56aSColin Percival	# Read the configuration file.  Anything after the first '#' is
42348ffe56aSColin Percival	# ignored, and any blank lines are ignored.
42448ffe56aSColin Percival	L=0
42548ffe56aSColin Percival	while read LINE; do
42648ffe56aSColin Percival		L=$(($L + 1))
42748ffe56aSColin Percival		LINEX=`echo "${LINE}" | cut -f 1 -d '#'`
42848ffe56aSColin Percival		if ! configline ${LINEX}; then
42948ffe56aSColin Percival			echo "Error processing configuration file, line $L:"
43048ffe56aSColin Percival			echo "==> ${LINE}"
43148ffe56aSColin Percival			exit 1
43248ffe56aSColin Percival		fi
43348ffe56aSColin Percival	done < ${CONFFILE}
43448ffe56aSColin Percival
43548ffe56aSColin Percival	# Merge the settings read from the configuration file with those
43648ffe56aSColin Percival	# provided at the command line.
43748ffe56aSColin Percival	mergeconfig
43848ffe56aSColin Percival}
43948ffe56aSColin Percival
44048ffe56aSColin Percival# Provide some default parameters
44148ffe56aSColin Percivaldefault_params () {
44248ffe56aSColin Percival	# Save any parameters already configured, and clear the slate
44348ffe56aSColin Percival	saveconfig
44448ffe56aSColin Percival	nullconfig
44548ffe56aSColin Percival
44648ffe56aSColin Percival	# Default configurations
44748ffe56aSColin Percival	config_WorkDir /var/db/freebsd-update
44848ffe56aSColin Percival	config_MailTo root
44948ffe56aSColin Percival	config_AllowAdd yes
45048ffe56aSColin Percival	config_AllowDelete yes
45148ffe56aSColin Percival	config_KeepModifiedMetadata yes
45248ffe56aSColin Percival	config_BaseDir /
45348ffe56aSColin Percival	config_VerboseLevel stats
454db6b0a61SColin Percival	config_StrictComponents no
45548ffe56aSColin Percival
45648ffe56aSColin Percival	# Merge these defaults into the earlier-configured settings
45748ffe56aSColin Percival	mergeconfig
45848ffe56aSColin Percival}
45948ffe56aSColin Percival
46048ffe56aSColin Percival# Set utility output filtering options, based on ${VERBOSELEVEL}
46148ffe56aSColin Percivalfetch_setup_verboselevel () {
46248ffe56aSColin Percival	case ${VERBOSELEVEL} in
46348ffe56aSColin Percival	debug)
46448ffe56aSColin Percival		QUIETREDIR="/dev/stderr"
46548ffe56aSColin Percival		QUIETFLAG=" "
46648ffe56aSColin Percival		STATSREDIR="/dev/stderr"
46748ffe56aSColin Percival		DDSTATS=".."
46848ffe56aSColin Percival		XARGST="-t"
46948ffe56aSColin Percival		NDEBUG=" "
47048ffe56aSColin Percival		;;
47148ffe56aSColin Percival	nostats)
47248ffe56aSColin Percival		QUIETREDIR=""
47348ffe56aSColin Percival		QUIETFLAG=""
47448ffe56aSColin Percival		STATSREDIR="/dev/null"
47548ffe56aSColin Percival		DDSTATS=".."
47648ffe56aSColin Percival		XARGST=""
47748ffe56aSColin Percival		NDEBUG=""
47848ffe56aSColin Percival		;;
47948ffe56aSColin Percival	stats)
48048ffe56aSColin Percival		QUIETREDIR="/dev/null"
48148ffe56aSColin Percival		QUIETFLAG="-q"
48248ffe56aSColin Percival		STATSREDIR="/dev/stdout"
48348ffe56aSColin Percival		DDSTATS=""
48448ffe56aSColin Percival		XARGST=""
48548ffe56aSColin Percival		NDEBUG="-n"
48648ffe56aSColin Percival		;;
48748ffe56aSColin Percival	esac
48848ffe56aSColin Percival}
48948ffe56aSColin Percival
49048ffe56aSColin Percival# Perform sanity checks and set some final parameters
49148ffe56aSColin Percival# in preparation for fetching files.  Figure out which
49248ffe56aSColin Percival# set of updates should be downloaded: If the user is
49348ffe56aSColin Percival# running *-p[0-9]+, strip off the last part; if the
49448ffe56aSColin Percival# user is running -SECURITY, call it -RELEASE.  Chdir
49548ffe56aSColin Percival# into the working directory.
49648ffe56aSColin Percivalfetch_check_params () {
49748ffe56aSColin Percival	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
49848ffe56aSColin Percival
49948ffe56aSColin Percival	_SERVERNAME_z=\
50048ffe56aSColin Percival"SERVERNAME must be given via command line or configuration file."
50148ffe56aSColin Percival	_KEYPRINT_z="Key must be given via -k option or configuration file."
50248ffe56aSColin Percival	_KEYPRINT_bad="Invalid key fingerprint: "
50348ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
50448ffe56aSColin Percival
50548ffe56aSColin Percival	if [ -z "${SERVERNAME}" ]; then
50648ffe56aSColin Percival		echo -n "`basename $0`: "
50748ffe56aSColin Percival		echo "${_SERVERNAME_z}"
50848ffe56aSColin Percival		exit 1
50948ffe56aSColin Percival	fi
51048ffe56aSColin Percival	if [ -z "${KEYPRINT}" ]; then
51148ffe56aSColin Percival		echo -n "`basename $0`: "
51248ffe56aSColin Percival		echo "${_KEYPRINT_z}"
51348ffe56aSColin Percival		exit 1
51448ffe56aSColin Percival	fi
51548ffe56aSColin Percival	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
51648ffe56aSColin Percival		echo -n "`basename $0`: "
51748ffe56aSColin Percival		echo -n "${_KEYPRINT_bad}"
51848ffe56aSColin Percival		echo ${KEYPRINT}
51948ffe56aSColin Percival		exit 1
52048ffe56aSColin Percival	fi
52148ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
52248ffe56aSColin Percival		echo -n "`basename $0`: "
52348ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
52448ffe56aSColin Percival		echo ${WORKDIR}
52548ffe56aSColin Percival		exit 1
52648ffe56aSColin Percival	fi
52748ffe56aSColin Percival	cd ${WORKDIR} || exit 1
52848ffe56aSColin Percival
52948ffe56aSColin Percival	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
53048ffe56aSColin Percival	# to provide an upgrade path for FreeBSD Update 1.x users, since
53148ffe56aSColin Percival	# the kernels provided by FreeBSD Update 1.x are always labelled
53248ffe56aSColin Percival	# as X.Y-SECURITY.
53348ffe56aSColin Percival	RELNUM=`uname -r |
53448ffe56aSColin Percival	    sed -E 's,-p[0-9]+,,' |
53548ffe56aSColin Percival	    sed -E 's,-SECURITY,-RELEASE,'`
53648ffe56aSColin Percival	ARCH=`uname -m`
53748ffe56aSColin Percival	FETCHDIR=${RELNUM}/${ARCH}
538db6b0a61SColin Percival	PATCHDIR=${RELNUM}/${ARCH}/bp
53948ffe56aSColin Percival
54048ffe56aSColin Percival	# Figure out what directory contains the running kernel
54148ffe56aSColin Percival	BOOTFILE=`sysctl -n kern.bootfile`
54248ffe56aSColin Percival	KERNELDIR=${BOOTFILE%/kernel}
54348ffe56aSColin Percival	if ! [ -d ${KERNELDIR} ]; then
54448ffe56aSColin Percival		echo "Cannot identify running kernel"
54548ffe56aSColin Percival		exit 1
54648ffe56aSColin Percival	fi
54748ffe56aSColin Percival
5482c434b2cSColin Percival	# Figure out what kernel configuration is running.  We start with
5492c434b2cSColin Percival	# the output of `uname -i`, and then make the following adjustments:
5502c434b2cSColin Percival	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
5512c434b2cSColin Percival	# file says "ident SMP-GENERIC", I don't know...
5522c434b2cSColin Percival	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
5532c434b2cSColin Percival	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
5542c434b2cSColin Percival	# we're running an SMP kernel.  This mis-identification is a bug
5552c434b2cSColin Percival	# which was fixed in 6.2-STABLE.
5562c434b2cSColin Percival	KERNCONF=`uname -i`
5572c434b2cSColin Percival	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
5582c434b2cSColin Percival		KERNCONF=SMP
5592c434b2cSColin Percival	fi
5602c434b2cSColin Percival	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
5612c434b2cSColin Percival		if sysctl kern.version | grep -qE '/SMP$'; then
5622c434b2cSColin Percival			KERNCONF=SMP
5632c434b2cSColin Percival		fi
5642c434b2cSColin Percival	fi
5652c434b2cSColin Percival
56648ffe56aSColin Percival	# Define some paths
56748ffe56aSColin Percival	BSPATCH=/usr/bin/bspatch
56848ffe56aSColin Percival	SHA256=/sbin/sha256
56948ffe56aSColin Percival	PHTTPGET=/usr/libexec/phttpget
57048ffe56aSColin Percival
57148ffe56aSColin Percival	# Set up variables relating to VERBOSELEVEL
57248ffe56aSColin Percival	fetch_setup_verboselevel
57348ffe56aSColin Percival
57448ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
57548ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
57648ffe56aSColin Percival}
57748ffe56aSColin Percival
578db6b0a61SColin Percival# Perform sanity checks etc. before fetching upgrades.
579db6b0a61SColin Percivalupgrade_check_params () {
580db6b0a61SColin Percival	fetch_check_params
581db6b0a61SColin Percival
582db6b0a61SColin Percival	# Unless set otherwise, we're upgrading to the same kernel config.
583db6b0a61SColin Percival	NKERNCONF=${KERNCONF}
584db6b0a61SColin Percival
585db6b0a61SColin Percival	# We need TARGETRELEASE set
586db6b0a61SColin Percival	_TARGETRELEASE_z="Release target must be specified via -r option."
587db6b0a61SColin Percival	if [ -z "${TARGETRELEASE}" ]; then
588db6b0a61SColin Percival		echo -n "`basename $0`: "
589db6b0a61SColin Percival		echo "${_TARGETRELEASE_z}"
590db6b0a61SColin Percival		exit 1
591db6b0a61SColin Percival	fi
592db6b0a61SColin Percival
593db6b0a61SColin Percival	# The target release should be != the current release.
594db6b0a61SColin Percival	if [ "${TARGETRELEASE}" = "${RELNUM}" ]; then
595db6b0a61SColin Percival		echo -n "`basename $0`: "
596db6b0a61SColin Percival		echo "Cannot upgrade from ${RELNUM} to itself"
597db6b0a61SColin Percival		exit 1
598db6b0a61SColin Percival	fi
599db6b0a61SColin Percival
600db6b0a61SColin Percival	# Turning off AllowAdd or AllowDelete is a bad idea for upgrades.
601db6b0a61SColin Percival	if [ "${ALLOWADD}" = "no" ]; then
602db6b0a61SColin Percival		echo -n "`basename $0`: "
603db6b0a61SColin Percival		echo -n "WARNING: \"AllowAdd no\" is a bad idea "
604db6b0a61SColin Percival		echo "when upgrading between releases."
605db6b0a61SColin Percival		echo
606db6b0a61SColin Percival	fi
607db6b0a61SColin Percival	if [ "${ALLOWDELETE}" = "no" ]; then
608db6b0a61SColin Percival		echo -n "`basename $0`: "
609db6b0a61SColin Percival		echo -n "WARNING: \"AllowDelete no\" is a bad idea "
610db6b0a61SColin Percival		echo "when upgrading between releases."
611db6b0a61SColin Percival		echo
612db6b0a61SColin Percival	fi
613db6b0a61SColin Percival
614db6b0a61SColin Percival	# Set EDITOR to /usr/bin/vi if it isn't already set
615db6b0a61SColin Percival	: ${EDITOR:='/usr/bin/vi'}
616db6b0a61SColin Percival}
617db6b0a61SColin Percival
61848ffe56aSColin Percival# Perform sanity checks and set some final parameters in
61948ffe56aSColin Percival# preparation for installing updates.
62048ffe56aSColin Percivalinstall_check_params () {
62148ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
62248ffe56aSColin Percival	if [ `id -u` != 0 ]; then
62348ffe56aSColin Percival		echo "You must be root to run this."
62448ffe56aSColin Percival		exit 1
62548ffe56aSColin Percival	fi
62648ffe56aSColin Percival
6272328d598SColin Percival	# Check that securelevel <= 0.  Otherwise we can't update schg files.
6282328d598SColin Percival	if [ `sysctl -n kern.securelevel` -gt 0 ]; then
6292328d598SColin Percival		echo "Updates cannot be installed when the system securelevel"
6302328d598SColin Percival		echo "is greater than zero."
6312328d598SColin Percival		exit 1
6322328d598SColin Percival	fi
6332328d598SColin Percival
63448ffe56aSColin Percival	# Check that we have a working directory
63548ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
63648ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
63748ffe56aSColin Percival		echo -n "`basename $0`: "
63848ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
63948ffe56aSColin Percival		echo ${WORKDIR}
64048ffe56aSColin Percival		exit 1
64148ffe56aSColin Percival	fi
64248ffe56aSColin Percival	cd ${WORKDIR} || exit 1
64348ffe56aSColin Percival
64448ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
64548ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
64648ffe56aSColin Percival
64748ffe56aSColin Percival	# Check that we have updates ready to install
64848ffe56aSColin Percival	if ! [ -L ${BDHASH}-install ]; then
64948ffe56aSColin Percival		echo "No updates are available to install."
65048ffe56aSColin Percival		echo "Run '$0 fetch' first."
65148ffe56aSColin Percival		exit 1
65248ffe56aSColin Percival	fi
65348ffe56aSColin Percival	if ! [ -f ${BDHASH}-install/INDEX-OLD ] ||
65448ffe56aSColin Percival	    ! [ -f ${BDHASH}-install/INDEX-NEW ]; then
65548ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
65648ffe56aSColin Percival		echo "Re-run '$0 fetch'."
65748ffe56aSColin Percival		exit 1
65848ffe56aSColin Percival	fi
65948ffe56aSColin Percival}
66048ffe56aSColin Percival
66148ffe56aSColin Percival# Perform sanity checks and set some final parameters in
66248ffe56aSColin Percival# preparation for UNinstalling updates.
66348ffe56aSColin Percivalrollback_check_params () {
66448ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
66548ffe56aSColin Percival	if [ `id -u` != 0 ]; then
66648ffe56aSColin Percival		echo "You must be root to run this."
66748ffe56aSColin Percival		exit 1
66848ffe56aSColin Percival	fi
66948ffe56aSColin Percival
67048ffe56aSColin Percival	# Check that we have a working directory
67148ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
67248ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
67348ffe56aSColin Percival		echo -n "`basename $0`: "
67448ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
67548ffe56aSColin Percival		echo ${WORKDIR}
67648ffe56aSColin Percival		exit 1
67748ffe56aSColin Percival	fi
67848ffe56aSColin Percival	cd ${WORKDIR} || exit 1
67948ffe56aSColin Percival
68048ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
68148ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
68248ffe56aSColin Percival
68348ffe56aSColin Percival	# Check that we have updates ready to rollback
68448ffe56aSColin Percival	if ! [ -L ${BDHASH}-rollback ]; then
68548ffe56aSColin Percival		echo "No rollback directory found."
68648ffe56aSColin Percival		exit 1
68748ffe56aSColin Percival	fi
68848ffe56aSColin Percival	if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] ||
68948ffe56aSColin Percival	    ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then
69048ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
69148ffe56aSColin Percival		exit 1
69248ffe56aSColin Percival	fi
69348ffe56aSColin Percival}
69448ffe56aSColin Percival
69548ffe56aSColin Percival#### Core functionality -- the actual work gets done here
69648ffe56aSColin Percival
69748ffe56aSColin Percival# Use an SRV query to pick a server.  If the SRV query doesn't provide
69848ffe56aSColin Percival# a useful answer, use the server name specified by the user.
69948ffe56aSColin Percival# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
70048ffe56aSColin Percival# from that; or if no servers are returned, use ${SERVERNAME}.
70148ffe56aSColin Percival# This allows a user to specify "portsnap.freebsd.org" (in which case
70248ffe56aSColin Percival# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
70348ffe56aSColin Percival# (in which case portsnap will use that particular server, since there
70448ffe56aSColin Percival# won't be an SRV entry for that name).
70548ffe56aSColin Percival#
70648ffe56aSColin Percival# We ignore the Port field, since we are always going to use port 80.
70748ffe56aSColin Percival
70848ffe56aSColin Percival# Fetch the mirror list, but do not pick a mirror yet.  Returns 1 if
70948ffe56aSColin Percival# no mirrors are available for any reason.
71048ffe56aSColin Percivalfetch_pick_server_init () {
71148ffe56aSColin Percival	: > serverlist_tried
71248ffe56aSColin Percival
71348ffe56aSColin Percival# Check that host(1) exists (i.e., that the system wasn't built with the
71448ffe56aSColin Percival# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
71548ffe56aSColin Percival	if ! which -s host; then
71648ffe56aSColin Percival		: > serverlist_full
71748ffe56aSColin Percival		return 1
71848ffe56aSColin Percival	fi
71948ffe56aSColin Percival
72048ffe56aSColin Percival	echo -n "Looking up ${SERVERNAME} mirrors... "
72148ffe56aSColin Percival
72248ffe56aSColin Percival# Issue the SRV query and pull out the Priority, Weight, and Target fields.
72348ffe56aSColin Percival# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
72448ffe56aSColin Percival# "$name server selection ..."; we allow either format.
72548ffe56aSColin Percival	MLIST="_http._tcp.${SERVERNAME}"
72648ffe56aSColin Percival	host -t srv "${MLIST}" |
72748ffe56aSColin Percival	    sed -nE "s/${MLIST} (has SRV record|server selection) //p" |
72848ffe56aSColin Percival	    cut -f 1,2,4 -d ' ' |
72948ffe56aSColin Percival	    sed -e 's/\.$//' |
73048ffe56aSColin Percival	    sort > serverlist_full
73148ffe56aSColin Percival
73248ffe56aSColin Percival# If no records, give up -- we'll just use the server name we were given.
73348ffe56aSColin Percival	if [ `wc -l < serverlist_full` -eq 0 ]; then
73448ffe56aSColin Percival		echo "none found."
73548ffe56aSColin Percival		return 1
73648ffe56aSColin Percival	fi
73748ffe56aSColin Percival
73848ffe56aSColin Percival# Report how many mirrors we found.
73948ffe56aSColin Percival	echo `wc -l < serverlist_full` "mirrors found."
74048ffe56aSColin Percival
74148ffe56aSColin Percival# Generate a random seed for use in picking mirrors.  If HTTP_PROXY
74248ffe56aSColin Percival# is set, this will be used to generate the seed; otherwise, the seed
74348ffe56aSColin Percival# will be random.
74448ffe56aSColin Percival	if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
74548ffe56aSColin Percival		RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
74648ffe56aSColin Percival		    tr -d 'a-f' |
74748ffe56aSColin Percival		    cut -c 1-9`
74848ffe56aSColin Percival	else
74948ffe56aSColin Percival		RANDVALUE=`jot -r 1 0 999999999`
75048ffe56aSColin Percival	fi
75148ffe56aSColin Percival}
75248ffe56aSColin Percival
75348ffe56aSColin Percival# Pick a mirror.  Returns 1 if we have run out of mirrors to try.
75448ffe56aSColin Percivalfetch_pick_server () {
75548ffe56aSColin Percival# Generate a list of not-yet-tried mirrors
75648ffe56aSColin Percival	sort serverlist_tried |
75748ffe56aSColin Percival	    comm -23 serverlist_full - > serverlist
75848ffe56aSColin Percival
75948ffe56aSColin Percival# Have we run out of mirrors?
76048ffe56aSColin Percival	if [ `wc -l < serverlist` -eq 0 ]; then
76148ffe56aSColin Percival		echo "No mirrors remaining, giving up."
76248ffe56aSColin Percival		return 1
76348ffe56aSColin Percival	fi
76448ffe56aSColin Percival
76548ffe56aSColin Percival# Find the highest priority level (lowest numeric value).
76648ffe56aSColin Percival	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
76748ffe56aSColin Percival
76848ffe56aSColin Percival# Add up the weights of the response lines at that priority level.
76948ffe56aSColin Percival	SRV_WSUM=0;
77048ffe56aSColin Percival	while read X; do
77148ffe56aSColin Percival		case "$X" in
77248ffe56aSColin Percival		${SRV_PRIORITY}\ *)
77348ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
77448ffe56aSColin Percival			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
77548ffe56aSColin Percival			;;
77648ffe56aSColin Percival		esac
77748ffe56aSColin Percival	done < serverlist
77848ffe56aSColin Percival
77948ffe56aSColin Percival# If all the weights are 0, pretend that they are all 1 instead.
78048ffe56aSColin Percival	if [ ${SRV_WSUM} -eq 0 ]; then
78148ffe56aSColin Percival		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
78248ffe56aSColin Percival		SRV_W_ADD=1
78348ffe56aSColin Percival	else
78448ffe56aSColin Percival		SRV_W_ADD=0
78548ffe56aSColin Percival	fi
78648ffe56aSColin Percival
78748ffe56aSColin Percival# Pick a value between 0 and the sum of the weights - 1
78848ffe56aSColin Percival	SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
78948ffe56aSColin Percival
79048ffe56aSColin Percival# Read through the list of mirrors and set SERVERNAME.  Write the line
79148ffe56aSColin Percival# corresponding to the mirror we selected into serverlist_tried so that
79248ffe56aSColin Percival# we won't try it again.
79348ffe56aSColin Percival	while read X; do
79448ffe56aSColin Percival		case "$X" in
79548ffe56aSColin Percival		${SRV_PRIORITY}\ *)
79648ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
79748ffe56aSColin Percival			SRV_W=$(($SRV_W + $SRV_W_ADD))
79848ffe56aSColin Percival			if [ $SRV_RND -lt $SRV_W ]; then
79948ffe56aSColin Percival				SERVERNAME=`echo $X | cut -f 3 -d ' '`
80048ffe56aSColin Percival				echo "$X" >> serverlist_tried
80148ffe56aSColin Percival				break
80248ffe56aSColin Percival			else
80348ffe56aSColin Percival				SRV_RND=$(($SRV_RND - $SRV_W))
80448ffe56aSColin Percival			fi
80548ffe56aSColin Percival			;;
80648ffe56aSColin Percival		esac
80748ffe56aSColin Percival	done < serverlist
80848ffe56aSColin Percival}
80948ffe56aSColin Percival
81048ffe56aSColin Percival# Take a list of ${oldhash}|${newhash} and output a list of needed patches,
81148ffe56aSColin Percival# i.e., those for which we have ${oldhash} and don't have ${newhash}.
81248ffe56aSColin Percivalfetch_make_patchlist () {
81348ffe56aSColin Percival	grep -vE "^([0-9a-f]{64})\|\1$" |
81448ffe56aSColin Percival	    tr '|' ' ' |
81548ffe56aSColin Percival		while read X Y; do
81648ffe56aSColin Percival			if [ -f "files/${Y}.gz" ] ||
81748ffe56aSColin Percival			    [ ! -f "files/${X}.gz" ]; then
81848ffe56aSColin Percival				continue
81948ffe56aSColin Percival			fi
82048ffe56aSColin Percival			echo "${X}|${Y}"
82148ffe56aSColin Percival		done | uniq
82248ffe56aSColin Percival}
82348ffe56aSColin Percival
82448ffe56aSColin Percival# Print user-friendly progress statistics
82548ffe56aSColin Percivalfetch_progress () {
82648ffe56aSColin Percival	LNC=0
82748ffe56aSColin Percival	while read x; do
82848ffe56aSColin Percival		LNC=$(($LNC + 1))
82948ffe56aSColin Percival		if [ $(($LNC % 10)) = 0 ]; then
83048ffe56aSColin Percival			echo -n $LNC
83148ffe56aSColin Percival		elif [ $(($LNC % 2)) = 0 ]; then
83248ffe56aSColin Percival			echo -n .
83348ffe56aSColin Percival		fi
83448ffe56aSColin Percival	done
83548ffe56aSColin Percival	echo -n " "
83648ffe56aSColin Percival}
83748ffe56aSColin Percival
838db6b0a61SColin Percival# Function for asking the user if everything is ok
839db6b0a61SColin Percivalcontinuep () {
840db6b0a61SColin Percival	while read -p "Does this look reasonable (y/n)? " CONTINUE; do
841db6b0a61SColin Percival		case "${CONTINUE}" in
842db6b0a61SColin Percival		y*)
843db6b0a61SColin Percival			return 0
844db6b0a61SColin Percival			;;
845db6b0a61SColin Percival		n*)
846db6b0a61SColin Percival			return 1
847db6b0a61SColin Percival			;;
848db6b0a61SColin Percival		esac
849db6b0a61SColin Percival	done
850db6b0a61SColin Percival}
851db6b0a61SColin Percival
85248ffe56aSColin Percival# Initialize the working directory
85348ffe56aSColin Percivalworkdir_init () {
85448ffe56aSColin Percival	mkdir -p files
85548ffe56aSColin Percival	touch tINDEX.present
85648ffe56aSColin Percival}
85748ffe56aSColin Percival
85848ffe56aSColin Percival# Check that we have a public key with an appropriate hash, or
85948ffe56aSColin Percival# fetch the key if it doesn't exist.  Returns 1 if the key has
86048ffe56aSColin Percival# not yet been fetched.
86148ffe56aSColin Percivalfetch_key () {
86248ffe56aSColin Percival	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
86348ffe56aSColin Percival		return 0
86448ffe56aSColin Percival	fi
86548ffe56aSColin Percival
86648ffe56aSColin Percival	echo -n "Fetching public key from ${SERVERNAME}... "
86748ffe56aSColin Percival	rm -f pub.ssl
86848ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \
86948ffe56aSColin Percival	    2>${QUIETREDIR} || true
87048ffe56aSColin Percival	if ! [ -r pub.ssl ]; then
87148ffe56aSColin Percival		echo "failed."
87248ffe56aSColin Percival		return 1
87348ffe56aSColin Percival	fi
87448ffe56aSColin Percival	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
87548ffe56aSColin Percival		echo "key has incorrect hash."
87648ffe56aSColin Percival		rm -f pub.ssl
87748ffe56aSColin Percival		return 1
87848ffe56aSColin Percival	fi
87948ffe56aSColin Percival	echo "done."
88048ffe56aSColin Percival}
88148ffe56aSColin Percival
88248ffe56aSColin Percival# Fetch metadata signature, aka "tag".
88348ffe56aSColin Percivalfetch_tag () {
884db6b0a61SColin Percival	echo -n "Fetching metadata signature "
885db6b0a61SColin Percival	echo ${NDEBUG} "for ${RELNUM} from ${SERVERNAME}... "
88648ffe56aSColin Percival	rm -f latest.ssl
88748ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl	\
88848ffe56aSColin Percival	    2>${QUIETREDIR} || true
88948ffe56aSColin Percival	if ! [ -r latest.ssl ]; then
89048ffe56aSColin Percival		echo "failed."
89148ffe56aSColin Percival		return 1
89248ffe56aSColin Percival	fi
89348ffe56aSColin Percival
89448ffe56aSColin Percival	openssl rsautl -pubin -inkey pub.ssl -verify		\
89548ffe56aSColin Percival	    < latest.ssl > tag.new 2>${QUIETREDIR} || true
89648ffe56aSColin Percival	rm latest.ssl
89748ffe56aSColin Percival
89848ffe56aSColin Percival	if ! [ `wc -l < tag.new` = 1 ] ||
89948ffe56aSColin Percival	    ! grep -qE	\
90048ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
90148ffe56aSColin Percival		tag.new; then
90248ffe56aSColin Percival		echo "invalid signature."
90348ffe56aSColin Percival		return 1
90448ffe56aSColin Percival	fi
90548ffe56aSColin Percival
90648ffe56aSColin Percival	echo "done."
90748ffe56aSColin Percival
90848ffe56aSColin Percival	RELPATCHNUM=`cut -f 4 -d '|' < tag.new`
90948ffe56aSColin Percival	TINDEXHASH=`cut -f 5 -d '|' < tag.new`
91048ffe56aSColin Percival	EOLTIME=`cut -f 6 -d '|' < tag.new`
91148ffe56aSColin Percival}
91248ffe56aSColin Percival
91348ffe56aSColin Percival# Sanity-check the patch number in a tag, to make sure that we're not
91448ffe56aSColin Percival# going to "update" backwards and to prevent replay attacks.
91548ffe56aSColin Percivalfetch_tagsanity () {
91648ffe56aSColin Percival	# Check that we're not going to move from -pX to -pY with Y < X.
91748ffe56aSColin Percival	RELPX=`uname -r | sed -E 's,.*-,,'`
91848ffe56aSColin Percival	if echo ${RELPX} | grep -qE '^p[0-9]+$'; then
91948ffe56aSColin Percival		RELPX=`echo ${RELPX} | cut -c 2-`
92048ffe56aSColin Percival	else
92148ffe56aSColin Percival		RELPX=0
92248ffe56aSColin Percival	fi
92348ffe56aSColin Percival	if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then
92448ffe56aSColin Percival		echo
92548ffe56aSColin Percival		echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
92648ffe56aSColin Percival		echo " appear older than what"
92748ffe56aSColin Percival		echo "we are currently running (`uname -r`)!"
92848ffe56aSColin Percival		echo "Cowardly refusing to proceed any further."
92948ffe56aSColin Percival		return 1
93048ffe56aSColin Percival	fi
93148ffe56aSColin Percival
93248ffe56aSColin Percival	# If "tag" exists and corresponds to ${RELNUM}, make sure that
93348ffe56aSColin Percival	# it contains a patch number <= RELPATCHNUM, in order to protect
93448ffe56aSColin Percival	# against rollback (replay) attacks.
93548ffe56aSColin Percival	if [ -f tag ] &&
93648ffe56aSColin Percival	    grep -qE	\
93748ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
93848ffe56aSColin Percival		tag; then
93948ffe56aSColin Percival		LASTRELPATCHNUM=`cut -f 4 -d '|' < tag`
94048ffe56aSColin Percival
94148ffe56aSColin Percival		if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then
94248ffe56aSColin Percival			echo
94348ffe56aSColin Percival			echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
94448ffe56aSColin Percival			echo " are older than the"
94548ffe56aSColin Percival			echo -n "most recently seen updates"
94648ffe56aSColin Percival			echo " (${RELNUM}-p${LASTRELPATCHNUM})."
94748ffe56aSColin Percival			echo "Cowardly refusing to proceed any further."
94848ffe56aSColin Percival			return 1
94948ffe56aSColin Percival		fi
95048ffe56aSColin Percival	fi
95148ffe56aSColin Percival}
95248ffe56aSColin Percival
95348ffe56aSColin Percival# Fetch metadata index file
95448ffe56aSColin Percivalfetch_metadata_index () {
95548ffe56aSColin Percival	echo ${NDEBUG} "Fetching metadata index... "
95648ffe56aSColin Percival	rm -f ${TINDEXHASH}
95748ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH}
95848ffe56aSColin Percival	    2>${QUIETREDIR}
95948ffe56aSColin Percival	if ! [ -f ${TINDEXHASH} ]; then
96048ffe56aSColin Percival		echo "failed."
96148ffe56aSColin Percival		return 1
96248ffe56aSColin Percival	fi
96348ffe56aSColin Percival	if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then
96448ffe56aSColin Percival		echo "update metadata index corrupt."
96548ffe56aSColin Percival		return 1
96648ffe56aSColin Percival	fi
96748ffe56aSColin Percival	echo "done."
96848ffe56aSColin Percival}
96948ffe56aSColin Percival
97048ffe56aSColin Percival# Print an error message about signed metadata being bogus.
97148ffe56aSColin Percivalfetch_metadata_bogus () {
97248ffe56aSColin Percival	echo
97348ffe56aSColin Percival	echo "The update metadata$1 is correctly signed, but"
97448ffe56aSColin Percival	echo "failed an integrity check."
97548ffe56aSColin Percival	echo "Cowardly refusing to proceed any further."
97648ffe56aSColin Percival	return 1
97748ffe56aSColin Percival}
97848ffe56aSColin Percival
97948ffe56aSColin Percival# Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH}
98048ffe56aSColin Percival# with the lines not named in $@ from tINDEX.present (if that file exists).
98148ffe56aSColin Percivalfetch_metadata_index_merge () {
98248ffe56aSColin Percival	for METAFILE in $@; do
98348ffe56aSColin Percival		if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l`	\
98448ffe56aSColin Percival		    -ne 1 ]; then
98548ffe56aSColin Percival			fetch_metadata_bogus " index"
98648ffe56aSColin Percival			return 1
98748ffe56aSColin Percival		fi
98848ffe56aSColin Percival
98948ffe56aSColin Percival		grep -E "${METAFILE}\|" ${TINDEXHASH}
99048ffe56aSColin Percival	done |
99148ffe56aSColin Percival	    sort > tINDEX.wanted
99248ffe56aSColin Percival
99348ffe56aSColin Percival	if [ -f tINDEX.present ]; then
99448ffe56aSColin Percival		join -t '|' -v 2 tINDEX.wanted tINDEX.present |
99548ffe56aSColin Percival		    sort -m - tINDEX.wanted > tINDEX.new
99648ffe56aSColin Percival		rm tINDEX.wanted
99748ffe56aSColin Percival	else
99848ffe56aSColin Percival		mv tINDEX.wanted tINDEX.new
99948ffe56aSColin Percival	fi
100048ffe56aSColin Percival}
100148ffe56aSColin Percival
100248ffe56aSColin Percival# Sanity check all the lines of tINDEX.new.  Even if more metadata lines
100348ffe56aSColin Percival# are added by future versions of the server, this won't cause problems,
100448ffe56aSColin Percival# since the only lines which appear in tINDEX.new are the ones which we
100548ffe56aSColin Percival# specifically grepped out of ${TINDEXHASH}.
100648ffe56aSColin Percivalfetch_metadata_index_sanity () {
100748ffe56aSColin Percival	if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then
100848ffe56aSColin Percival		fetch_metadata_bogus " index"
100948ffe56aSColin Percival		return 1
101048ffe56aSColin Percival	fi
101148ffe56aSColin Percival}
101248ffe56aSColin Percival
101348ffe56aSColin Percival# Sanity check the metadata file $1.
101448ffe56aSColin Percivalfetch_metadata_sanity () {
101548ffe56aSColin Percival	# Some aliases to save space later: ${P} is a character which can
101648ffe56aSColin Percival	# appear in a path; ${M} is the four numeric metadata fields; and
101748ffe56aSColin Percival	# ${H} is a sha256 hash.
101848ffe56aSColin Percival	P="[-+./:=_[[:alnum:]]"
101948ffe56aSColin Percival	M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+"
102048ffe56aSColin Percival	H="[0-9a-f]{64}"
102148ffe56aSColin Percival
102248ffe56aSColin Percival	# Check that the first four fields make sense.
102348ffe56aSColin Percival	if gunzip -c < files/$1.gz |
102448ffe56aSColin Percival	    grep -qvE "^[a-z]+\|[0-9a-z]+\|${P}+\|[fdL-]\|"; then
102548ffe56aSColin Percival		fetch_metadata_bogus ""
102648ffe56aSColin Percival		return 1
102748ffe56aSColin Percival	fi
102848ffe56aSColin Percival
102948ffe56aSColin Percival	# Remove the first three fields.
103048ffe56aSColin Percival	gunzip -c < files/$1.gz |
103148ffe56aSColin Percival	    cut -f 4- -d '|' > sanitycheck.tmp
103248ffe56aSColin Percival
103348ffe56aSColin Percival	# Sanity check entries with type 'f'
103448ffe56aSColin Percival	if grep -E '^f' sanitycheck.tmp |
103548ffe56aSColin Percival	    grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then
103648ffe56aSColin Percival		fetch_metadata_bogus ""
103748ffe56aSColin Percival		return 1
103848ffe56aSColin Percival	fi
103948ffe56aSColin Percival
104048ffe56aSColin Percival	# Sanity check entries with type 'd'
104148ffe56aSColin Percival	if grep -E '^d' sanitycheck.tmp |
104248ffe56aSColin Percival	    grep -qvE "^d\|${M}\|\|\$"; then
104348ffe56aSColin Percival		fetch_metadata_bogus ""
104448ffe56aSColin Percival		return 1
104548ffe56aSColin Percival	fi
104648ffe56aSColin Percival
104748ffe56aSColin Percival	# Sanity check entries with type 'L'
104848ffe56aSColin Percival	if grep -E '^L' sanitycheck.tmp |
104948ffe56aSColin Percival	    grep -qvE "^L\|${M}\|${P}*\|\$"; then
105048ffe56aSColin Percival		fetch_metadata_bogus ""
105148ffe56aSColin Percival		return 1
105248ffe56aSColin Percival	fi
105348ffe56aSColin Percival
105448ffe56aSColin Percival	# Sanity check entries with type '-'
105548ffe56aSColin Percival	if grep -E '^-' sanitycheck.tmp |
105648ffe56aSColin Percival	    grep -qvE "^-\|\|\|\|\|\|"; then
105748ffe56aSColin Percival		fetch_metadata_bogus ""
105848ffe56aSColin Percival		return 1
105948ffe56aSColin Percival	fi
106048ffe56aSColin Percival
106148ffe56aSColin Percival	# Clean up
106248ffe56aSColin Percival	rm sanitycheck.tmp
106348ffe56aSColin Percival}
106448ffe56aSColin Percival
106548ffe56aSColin Percival# Fetch the metadata index and metadata files listed in $@,
106648ffe56aSColin Percival# taking advantage of metadata patches where possible.
106748ffe56aSColin Percivalfetch_metadata () {
106848ffe56aSColin Percival	fetch_metadata_index || return 1
106948ffe56aSColin Percival	fetch_metadata_index_merge $@ || return 1
107048ffe56aSColin Percival	fetch_metadata_index_sanity || return 1
107148ffe56aSColin Percival
107248ffe56aSColin Percival	# Generate a list of wanted metadata patches
107348ffe56aSColin Percival	join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new |
107448ffe56aSColin Percival	    fetch_make_patchlist > patchlist
107548ffe56aSColin Percival
107648ffe56aSColin Percival	if [ -s patchlist ]; then
107748ffe56aSColin Percival		# Attempt to fetch metadata patches
107848ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
107948ffe56aSColin Percival		echo ${NDEBUG} "metadata patches.${DDSTATS}"
108048ffe56aSColin Percival		tr '|' '-' < patchlist |
108148ffe56aSColin Percival		    lam -s "${FETCHDIR}/tp/" - -s ".gz" |
108248ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
108348ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
108448ffe56aSColin Percival		echo "done."
108548ffe56aSColin Percival
108648ffe56aSColin Percival		# Attempt to apply metadata patches
108748ffe56aSColin Percival		echo -n "Applying metadata patches... "
108848ffe56aSColin Percival		tr '|' ' ' < patchlist |
108948ffe56aSColin Percival		    while read X Y; do
109048ffe56aSColin Percival			if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
109148ffe56aSColin Percival			gunzip -c < ${X}-${Y}.gz > diff
109248ffe56aSColin Percival			gunzip -c < files/${X}.gz > diff-OLD
109348ffe56aSColin Percival
109448ffe56aSColin Percival			# Figure out which lines are being added and removed
109548ffe56aSColin Percival			grep -E '^-' diff |
109648ffe56aSColin Percival			    cut -c 2- |
109748ffe56aSColin Percival			    while read PREFIX; do
109848ffe56aSColin Percival				look "${PREFIX}" diff-OLD
109948ffe56aSColin Percival			    done |
110048ffe56aSColin Percival			    sort > diff-rm
110148ffe56aSColin Percival			grep -E '^\+' diff |
110248ffe56aSColin Percival			    cut -c 2- > diff-add
110348ffe56aSColin Percival
110448ffe56aSColin Percival			# Generate the new file
110548ffe56aSColin Percival			comm -23 diff-OLD diff-rm |
110648ffe56aSColin Percival			    sort - diff-add > diff-NEW
110748ffe56aSColin Percival
110848ffe56aSColin Percival			if [ `${SHA256} -q diff-NEW` = ${Y} ]; then
110948ffe56aSColin Percival				mv diff-NEW files/${Y}
111048ffe56aSColin Percival				gzip -n files/${Y}
111148ffe56aSColin Percival			else
111248ffe56aSColin Percival				mv diff-NEW ${Y}.bad
111348ffe56aSColin Percival			fi
111448ffe56aSColin Percival			rm -f ${X}-${Y}.gz diff
111548ffe56aSColin Percival			rm -f diff-OLD diff-NEW diff-add diff-rm
111648ffe56aSColin Percival		done 2>${QUIETREDIR}
111748ffe56aSColin Percival		echo "done."
111848ffe56aSColin Percival	fi
111948ffe56aSColin Percival
112048ffe56aSColin Percival	# Update metadata without patches
112148ffe56aSColin Percival	cut -f 2 -d '|' < tINDEX.new |
112248ffe56aSColin Percival	    while read Y; do
112348ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
112448ffe56aSColin Percival			echo ${Y};
112548ffe56aSColin Percival		fi
1126bce02f98SColin Percival	    done |
1127bce02f98SColin Percival	    sort -u > filelist
112848ffe56aSColin Percival
112948ffe56aSColin Percival	if [ -s filelist ]; then
113048ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
113148ffe56aSColin Percival		echo ${NDEBUG} "metadata files... "
113248ffe56aSColin Percival		lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist |
113348ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
113448ffe56aSColin Percival		    2>${QUIETREDIR}
113548ffe56aSColin Percival
113648ffe56aSColin Percival		while read Y; do
113748ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
113848ffe56aSColin Percival				echo "failed."
113948ffe56aSColin Percival				return 1
114048ffe56aSColin Percival			fi
114148ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
114248ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
114348ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
114448ffe56aSColin Percival			else
114548ffe56aSColin Percival				echo "metadata is corrupt."
114648ffe56aSColin Percival				return 1
114748ffe56aSColin Percival			fi
114848ffe56aSColin Percival		done < filelist
114948ffe56aSColin Percival		echo "done."
115048ffe56aSColin Percival	fi
115148ffe56aSColin Percival
115248ffe56aSColin Percival# Sanity-check the metadata files.
115348ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new > filelist
115448ffe56aSColin Percival	while read X; do
115548ffe56aSColin Percival		fetch_metadata_sanity ${X} || return 1
115648ffe56aSColin Percival	done < filelist
115748ffe56aSColin Percival
115848ffe56aSColin Percival# Remove files which are no longer needed
115948ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.present |
116048ffe56aSColin Percival	    sort > oldfiles
116148ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new |
116248ffe56aSColin Percival	    sort |
116348ffe56aSColin Percival	    comm -13 - oldfiles |
116448ffe56aSColin Percival	    lam -s "files/" - -s ".gz" |
116548ffe56aSColin Percival	    xargs rm -f
116648ffe56aSColin Percival	rm patchlist filelist oldfiles
116748ffe56aSColin Percival	rm ${TINDEXHASH}
116848ffe56aSColin Percival
116948ffe56aSColin Percival# We're done!
117048ffe56aSColin Percival	mv tINDEX.new tINDEX.present
117148ffe56aSColin Percival	mv tag.new tag
117248ffe56aSColin Percival
117348ffe56aSColin Percival	return 0
117448ffe56aSColin Percival}
117548ffe56aSColin Percival
1176db6b0a61SColin Percival# Extract a subset of a downloaded metadata file containing only the parts
1177db6b0a61SColin Percival# which are listed in COMPONENTS.
1178db6b0a61SColin Percivalfetch_filter_metadata_components () {
1179db6b0a61SColin Percival	METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
1180db6b0a61SColin Percival	gunzip -c < files/${METAHASH}.gz > $1.all
1181db6b0a61SColin Percival
1182db6b0a61SColin Percival	# Fish out the lines belonging to components we care about.
1183db6b0a61SColin Percival	for C in ${COMPONENTS}; do
1184db6b0a61SColin Percival		look "`echo ${C} | tr '/' '|'`|" $1.all
1185db6b0a61SColin Percival	done > $1
1186db6b0a61SColin Percival
1187db6b0a61SColin Percival	# Remove temporary file.
1188db6b0a61SColin Percival	rm $1.all
1189db6b0a61SColin Percival}
1190db6b0a61SColin Percival
1191b698a3abSColin Percival# Generate a filtered version of the metadata file $1 from the downloaded
119248ffe56aSColin Percival# file, by fishing out the lines corresponding to components we're trying
119348ffe56aSColin Percival# to keep updated, and then removing lines corresponding to paths we want
119448ffe56aSColin Percival# to ignore.
119548ffe56aSColin Percivalfetch_filter_metadata () {
119648ffe56aSColin Percival	# Fish out the lines belonging to components we care about.
1197db6b0a61SColin Percival	fetch_filter_metadata_components $1
1198db6b0a61SColin Percival
119948ffe56aSColin Percival	# Canonicalize directory names by removing any trailing / in
120048ffe56aSColin Percival	# order to avoid listing directories multiple times if they
120148ffe56aSColin Percival	# belong to multiple components.  Turning "/" into "" doesn't
120248ffe56aSColin Percival	# matter, since we add a leading "/" when we use paths later.
1203db6b0a61SColin Percival	cut -f 3- -d '|' $1 |
120448ffe56aSColin Percival	    sed -e 's,/|d|,|d|,' |
120548ffe56aSColin Percival	    sort -u > $1.tmp
120648ffe56aSColin Percival
120748ffe56aSColin Percival	# Figure out which lines to ignore and remove them.
120848ffe56aSColin Percival	for X in ${IGNOREPATHS}; do
120948ffe56aSColin Percival		grep -E "^${X}" $1.tmp
121048ffe56aSColin Percival	done |
121148ffe56aSColin Percival	    sort -u |
121248ffe56aSColin Percival	    comm -13 - $1.tmp > $1
121348ffe56aSColin Percival
121448ffe56aSColin Percival	# Remove temporary files.
1215db6b0a61SColin Percival	rm $1.tmp
121648ffe56aSColin Percival}
121748ffe56aSColin Percival
1218db6b0a61SColin Percival# Filter the metadata file $1 by adding lines with "/boot/$2"
1219bce02f98SColin Percival# replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the
1220db6b0a61SColin Percival# trailing "/kernel"); and if "/boot/$2" does not exist, remove
1221bce02f98SColin Percival# the original lines which start with that.
1222bce02f98SColin Percival# Put another way: Deal with the fact that the FOO kernel is sometimes
1223bce02f98SColin Percival# installed in /boot/FOO/ and is sometimes installed elsewhere.
122448ffe56aSColin Percivalfetch_filter_kernel_names () {
1225db6b0a61SColin Percival	grep ^/boot/$2 $1 |
1226db6b0a61SColin Percival	    sed -e "s,/boot/$2,${KERNELDIR},g" |
122748ffe56aSColin Percival	    sort - $1 > $1.tmp
122848ffe56aSColin Percival	mv $1.tmp $1
1229bce02f98SColin Percival
1230db6b0a61SColin Percival	if ! [ -d /boot/$2 ]; then
1231db6b0a61SColin Percival		grep -v ^/boot/$2 $1 > $1.tmp
1232bce02f98SColin Percival		mv $1.tmp $1
1233bce02f98SColin Percival	fi
123448ffe56aSColin Percival}
123548ffe56aSColin Percival
123648ffe56aSColin Percival# For all paths appearing in $1 or $3, inspect the system
123748ffe56aSColin Percival# and generate $2 describing what is currently installed.
123848ffe56aSColin Percivalfetch_inspect_system () {
123948ffe56aSColin Percival	# No errors yet...
124048ffe56aSColin Percival	rm -f .err
124148ffe56aSColin Percival
124248ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
124348ffe56aSColin Percival	echo -n "Inspecting system... "
124448ffe56aSColin Percival
124548ffe56aSColin Percival	# Generate list of files to inspect
124648ffe56aSColin Percival	cat $1 $3 |
124748ffe56aSColin Percival	    cut -f 1 -d '|' |
124848ffe56aSColin Percival	    sort -u > filelist
124948ffe56aSColin Percival
125048ffe56aSColin Percival	# Examine each file and output lines of the form
125148ffe56aSColin Percival	# /path/to/file|type|device-inum|user|group|perm|flags|value
125248ffe56aSColin Percival	# sorted by device and inode number.
125348ffe56aSColin Percival	while read F; do
125448ffe56aSColin Percival		# If the symlink/file/directory does not exist, record this.
125548ffe56aSColin Percival		if ! [ -e ${BASEDIR}/${F} ]; then
125648ffe56aSColin Percival			echo "${F}|-||||||"
125748ffe56aSColin Percival			continue
125848ffe56aSColin Percival		fi
125948ffe56aSColin Percival		if ! [ -r ${BASEDIR}/${F} ]; then
126048ffe56aSColin Percival			echo "Cannot read file: ${BASEDIR}/${F}"	\
126148ffe56aSColin Percival			    >/dev/stderr
126248ffe56aSColin Percival			touch .err
126348ffe56aSColin Percival			return 1
126448ffe56aSColin Percival		fi
126548ffe56aSColin Percival
126648ffe56aSColin Percival		# Otherwise, output an index line.
126748ffe56aSColin Percival		if [ -L ${BASEDIR}/${F} ]; then
126848ffe56aSColin Percival			echo -n "${F}|L|"
126948ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
127048ffe56aSColin Percival			readlink ${BASEDIR}/${F};
127148ffe56aSColin Percival		elif [ -f ${BASEDIR}/${F} ]; then
127248ffe56aSColin Percival			echo -n "${F}|f|"
127348ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
127448ffe56aSColin Percival			sha256 -q ${BASEDIR}/${F};
127548ffe56aSColin Percival		elif [ -d ${BASEDIR}/${F} ]; then
127648ffe56aSColin Percival			echo -n "${F}|d|"
127748ffe56aSColin Percival			stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
127848ffe56aSColin Percival		else
127948ffe56aSColin Percival			echo "Unknown file type: ${BASEDIR}/${F}"	\
128048ffe56aSColin Percival			    >/dev/stderr
128148ffe56aSColin Percival			touch .err
128248ffe56aSColin Percival			return 1
128348ffe56aSColin Percival		fi
128448ffe56aSColin Percival	done < filelist |
128548ffe56aSColin Percival	    sort -k 3,3 -t '|' > $2.tmp
128648ffe56aSColin Percival	rm filelist
128748ffe56aSColin Percival
128848ffe56aSColin Percival	# Check if an error occured during system inspection
128948ffe56aSColin Percival	if [ -f .err ]; then
129048ffe56aSColin Percival		return 1
129148ffe56aSColin Percival	fi
129248ffe56aSColin Percival
129348ffe56aSColin Percival	# Convert to the form
129448ffe56aSColin Percival	# /path/to/file|type|user|group|perm|flags|value|hlink
129548ffe56aSColin Percival	# by resolving identical device and inode numbers into hard links.
129648ffe56aSColin Percival	cut -f 1,3 -d '|' $2.tmp |
129748ffe56aSColin Percival	    sort -k 1,1 -t '|' |
129848ffe56aSColin Percival	    sort -s -u -k 2,2 -t '|' |
129948ffe56aSColin Percival	    join -1 2 -2 3 -t '|' - $2.tmp |
130048ffe56aSColin Percival	    awk -F \| -v OFS=\|		\
130148ffe56aSColin Percival		'{
130248ffe56aSColin Percival		    if (($2 == $3) || ($4 == "-"))
130348ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,""
130448ffe56aSColin Percival		    else
130548ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,$2
130648ffe56aSColin Percival		}' |
130748ffe56aSColin Percival	    sort > $2
130848ffe56aSColin Percival	rm $2.tmp
130948ffe56aSColin Percival
131048ffe56aSColin Percival	# We're finished looking around
131148ffe56aSColin Percival	echo "done."
131248ffe56aSColin Percival}
131348ffe56aSColin Percival
1314db6b0a61SColin Percival# For any paths matching ${MERGECHANGES}, compare $1 and $2 and find any
1315db6b0a61SColin Percival# files which differ; generate $3 containing these paths and the old hashes.
1316db6b0a61SColin Percivalfetch_filter_mergechanges () {
1317db6b0a61SColin Percival	# Pull out the paths and hashes of the files matching ${MERGECHANGES}.
1318db6b0a61SColin Percival	for F in $1 $2; do
1319db6b0a61SColin Percival		for X in ${MERGECHANGES}; do
1320db6b0a61SColin Percival			grep -E "^${X}" ${F}
1321db6b0a61SColin Percival		done |
1322db6b0a61SColin Percival		    cut -f 1,2,7 -d '|' |
1323db6b0a61SColin Percival		    sort > ${F}-values
1324db6b0a61SColin Percival	done
1325db6b0a61SColin Percival
1326db6b0a61SColin Percival	# Any line in $2-values which doesn't appear in $1-values and is a
1327db6b0a61SColin Percival	# file means that we should list the path in $3.
1328db6b0a61SColin Percival	comm -13 $1-values $2-values |
1329db6b0a61SColin Percival	    fgrep '|f|' |
1330db6b0a61SColin Percival	    cut -f 1 -d '|' > $2-paths
1331db6b0a61SColin Percival
1332db6b0a61SColin Percival	# For each path, pull out one (and only one!) entry from $1-values.
1333db6b0a61SColin Percival	# Note that we cannot distinguish which "old" version the user made
1334db6b0a61SColin Percival	# changes to; but hopefully any changes which occur due to security
1335db6b0a61SColin Percival	# updates will exist in both the "new" version and the version which
1336db6b0a61SColin Percival	# the user has installed, so the merging will still work.
1337db6b0a61SColin Percival	while read X; do
1338db6b0a61SColin Percival		look "${X}|" $1-values |
1339db6b0a61SColin Percival		    head -1
1340db6b0a61SColin Percival	done < $2-paths > $3
1341db6b0a61SColin Percival
1342db6b0a61SColin Percival	# Clean up
1343db6b0a61SColin Percival	rm $1-values $2-values $2-paths
1344db6b0a61SColin Percival}
1345db6b0a61SColin Percival
134648ffe56aSColin Percival# For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123]
1347db6b0a61SColin Percival# which correspond to lines in $2 with hashes not matching $1 or $3, unless
1348db6b0a61SColin Percival# the paths are listed in $4.  For entries in $2 marked "not present"
1349db6b0a61SColin Percival# (aka. type -), remove lines from $[123] unless there is a corresponding
1350db6b0a61SColin Percival# entry in $1.
135148ffe56aSColin Percivalfetch_filter_unmodified_notpresent () {
135248ffe56aSColin Percival	# Figure out which lines of $1 and $3 correspond to bits which
135348ffe56aSColin Percival	# should only be updated if they haven't changed, and fish out
135448ffe56aSColin Percival	# the (path, type, value) tuples.
135548ffe56aSColin Percival	# NOTE: We don't consider a file to be "modified" if it matches
135648ffe56aSColin Percival	# the hash from $3.
135748ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
135848ffe56aSColin Percival		grep -E "^${X}" $1
135948ffe56aSColin Percival		grep -E "^${X}" $3
136048ffe56aSColin Percival	done |
136148ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
136248ffe56aSColin Percival	    sort > $1-values
136348ffe56aSColin Percival
136448ffe56aSColin Percival	# Do the same for $2.
136548ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
136648ffe56aSColin Percival		grep -E "^${X}" $2
136748ffe56aSColin Percival	done |
136848ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
136948ffe56aSColin Percival	    sort > $2-values
137048ffe56aSColin Percival
137148ffe56aSColin Percival	# Any entry in $2-values which is not in $1-values corresponds to
1372db6b0a61SColin Percival	# a path which we need to remove from $1, $2, and $3, unless it
1373db6b0a61SColin Percival	# that path appears in $4.
1374db6b0a61SColin Percival	comm -13 $1-values $2-values |
1375db6b0a61SColin Percival	    sort -t '|' -k 1,1 > mlines.tmp
1376db6b0a61SColin Percival	cut -f 1 -d '|' $4 |
1377db6b0a61SColin Percival	    sort |
1378db6b0a61SColin Percival	    join -v 2 -t '|' - mlines.tmp |
1379db6b0a61SColin Percival	    sort > mlines
1380db6b0a61SColin Percival	rm $1-values $2-values mlines.tmp
138148ffe56aSColin Percival
138248ffe56aSColin Percival	# Any lines in $2 which are not in $1 AND are "not present" lines
138348ffe56aSColin Percival	# also belong in mlines.
138448ffe56aSColin Percival	comm -13 $1 $2 |
138548ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
138648ffe56aSColin Percival	    fgrep '|-|' >> mlines
138748ffe56aSColin Percival
138848ffe56aSColin Percival	# Remove lines from $1, $2, and $3
138948ffe56aSColin Percival	for X in $1 $2 $3; do
139048ffe56aSColin Percival		sort -t '|' -k 1,1 ${X} > ${X}.tmp
139148ffe56aSColin Percival		cut -f 1 -d '|' < mlines |
139248ffe56aSColin Percival		    sort |
139348ffe56aSColin Percival		    join -v 2 -t '|' - ${X}.tmp |
139448ffe56aSColin Percival		    sort > ${X}
139548ffe56aSColin Percival		rm ${X}.tmp
139648ffe56aSColin Percival	done
139748ffe56aSColin Percival
139848ffe56aSColin Percival	# Store a list of the modified files, for future reference
139948ffe56aSColin Percival	fgrep -v '|-|' mlines |
140048ffe56aSColin Percival	    cut -f 1 -d '|' > modifiedfiles
140148ffe56aSColin Percival	rm mlines
140248ffe56aSColin Percival}
140348ffe56aSColin Percival
140448ffe56aSColin Percival# For each entry in $1 of type -, remove any corresponding
140548ffe56aSColin Percival# entry from $2 if ${ALLOWADD} != "yes".  Remove all entries
140648ffe56aSColin Percival# of type - from $1.
140748ffe56aSColin Percivalfetch_filter_allowadd () {
140848ffe56aSColin Percival	cut -f 1,2 -d '|' < $1 |
140948ffe56aSColin Percival	    fgrep '|-' |
141048ffe56aSColin Percival	    cut -f 1 -d '|' > filesnotpresent
141148ffe56aSColin Percival
141248ffe56aSColin Percival	if [ ${ALLOWADD} != "yes" ]; then
141348ffe56aSColin Percival		sort < $2 |
141448ffe56aSColin Percival		    join -v 1 -t '|' - filesnotpresent |
141548ffe56aSColin Percival		    sort > $2.tmp
141648ffe56aSColin Percival		mv $2.tmp $2
141748ffe56aSColin Percival	fi
141848ffe56aSColin Percival
141948ffe56aSColin Percival	sort < $1 |
142048ffe56aSColin Percival	    join -v 1 -t '|' - filesnotpresent |
142148ffe56aSColin Percival	    sort > $1.tmp
142248ffe56aSColin Percival	mv $1.tmp $1
142348ffe56aSColin Percival	rm filesnotpresent
142448ffe56aSColin Percival}
142548ffe56aSColin Percival
142648ffe56aSColin Percival# If ${ALLOWDELETE} != "yes", then remove any entries from $1
142748ffe56aSColin Percival# which don't correspond to entries in $2.
142848ffe56aSColin Percivalfetch_filter_allowdelete () {
142948ffe56aSColin Percival	# Produce a lists ${PATH}|${TYPE}
143048ffe56aSColin Percival	for X in $1 $2; do
143148ffe56aSColin Percival		cut -f 1-2 -d '|' < ${X} |
143248ffe56aSColin Percival		    sort -u > ${X}.nodes
143348ffe56aSColin Percival	done
143448ffe56aSColin Percival
143548ffe56aSColin Percival	# Figure out which lines need to be removed from $1.
143648ffe56aSColin Percival	if [ ${ALLOWDELETE} != "yes" ]; then
143748ffe56aSColin Percival		comm -23 $1.nodes $2.nodes > $1.badnodes
143848ffe56aSColin Percival	else
143948ffe56aSColin Percival		: > $1.badnodes
144048ffe56aSColin Percival	fi
144148ffe56aSColin Percival
144248ffe56aSColin Percival	# Remove the relevant lines from $1
144348ffe56aSColin Percival	while read X; do
144448ffe56aSColin Percival		look "${X}|" $1
144548ffe56aSColin Percival	done < $1.badnodes |
144648ffe56aSColin Percival	    comm -13 - $1 > $1.tmp
144748ffe56aSColin Percival	mv $1.tmp $1
144848ffe56aSColin Percival
144948ffe56aSColin Percival	rm $1.badnodes $1.nodes $2.nodes
145048ffe56aSColin Percival}
145148ffe56aSColin Percival
145248ffe56aSColin Percival# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2
145348ffe56aSColin Percival# with metadata not matching any entry in $1, replace the corresponding
145448ffe56aSColin Percival# line of $3 with one having the same metadata as the entry in $2.
145548ffe56aSColin Percivalfetch_filter_modified_metadata () {
145648ffe56aSColin Percival	# Fish out the metadata from $1 and $2
145748ffe56aSColin Percival	for X in $1 $2; do
145848ffe56aSColin Percival		cut -f 1-6 -d '|' < ${X} > ${X}.metadata
145948ffe56aSColin Percival	done
146048ffe56aSColin Percival
146148ffe56aSColin Percival	# Find the metadata we need to keep
146248ffe56aSColin Percival	if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then
146348ffe56aSColin Percival		comm -13 $1.metadata $2.metadata > keepmeta
146448ffe56aSColin Percival	else
146548ffe56aSColin Percival		: > keepmeta
146648ffe56aSColin Percival	fi
146748ffe56aSColin Percival
146848ffe56aSColin Percival	# Extract the lines which we need to remove from $3, and
146948ffe56aSColin Percival	# construct the lines which we need to add to $3.
147048ffe56aSColin Percival	: > $3.remove
147148ffe56aSColin Percival	: > $3.add
147248ffe56aSColin Percival	while read LINE; do
147348ffe56aSColin Percival		NODE=`echo "${LINE}" | cut -f 1-2 -d '|'`
147448ffe56aSColin Percival		look "${NODE}|" $3 >> $3.remove
147548ffe56aSColin Percival		look "${NODE}|" $3 |
147648ffe56aSColin Percival		    cut -f 7- -d '|' |
147748ffe56aSColin Percival		    lam -s "${LINE}|" - >> $3.add
147848ffe56aSColin Percival	done < keepmeta
147948ffe56aSColin Percival
148048ffe56aSColin Percival	# Remove the specified lines and add the new lines.
148148ffe56aSColin Percival	sort $3.remove |
148248ffe56aSColin Percival	    comm -13 - $3 |
148348ffe56aSColin Percival	    sort -u - $3.add > $3.tmp
148448ffe56aSColin Percival	mv $3.tmp $3
148548ffe56aSColin Percival
148648ffe56aSColin Percival	rm keepmeta $1.metadata $2.metadata $3.add $3.remove
148748ffe56aSColin Percival}
148848ffe56aSColin Percival
148948ffe56aSColin Percival# Remove lines from $1 and $2 which are identical;
149048ffe56aSColin Percival# no need to update a file if it isn't changing.
149148ffe56aSColin Percivalfetch_filter_uptodate () {
149248ffe56aSColin Percival	comm -23 $1 $2 > $1.tmp
149348ffe56aSColin Percival	comm -13 $1 $2 > $2.tmp
149448ffe56aSColin Percival
149548ffe56aSColin Percival	mv $1.tmp $1
149648ffe56aSColin Percival	mv $2.tmp $2
149748ffe56aSColin Percival}
149848ffe56aSColin Percival
1499db6b0a61SColin Percival# Fetch any "clean" old versions of files we need for merging changes.
1500db6b0a61SColin Percivalfetch_files_premerge () {
1501db6b0a61SColin Percival	# We only need to do anything if $1 is non-empty.
1502db6b0a61SColin Percival	if [ -s $1 ]; then
1503db6b0a61SColin Percival		# Tell the user what we're doing
1504db6b0a61SColin Percival		echo -n "Fetching files from ${OLDRELNUM} for merging... "
1505db6b0a61SColin Percival
1506db6b0a61SColin Percival		# List of files wanted
1507db6b0a61SColin Percival		fgrep '|f|' < $1 |
1508db6b0a61SColin Percival		    cut -f 3 -d '|' |
1509db6b0a61SColin Percival		    sort -u > files.wanted
1510db6b0a61SColin Percival
1511db6b0a61SColin Percival		# Only fetch the files we don't already have
1512db6b0a61SColin Percival		while read Y; do
1513db6b0a61SColin Percival			if [ ! -f "files/${Y}.gz" ]; then
1514db6b0a61SColin Percival				echo ${Y};
1515db6b0a61SColin Percival			fi
1516db6b0a61SColin Percival		done < files.wanted > filelist
1517db6b0a61SColin Percival
1518db6b0a61SColin Percival		# Actually fetch them
1519db6b0a61SColin Percival		lam -s "${OLDFETCHDIR}/f/" - -s ".gz" < filelist |
1520db6b0a61SColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
1521db6b0a61SColin Percival		    2>${QUIETREDIR}
1522db6b0a61SColin Percival
1523db6b0a61SColin Percival		# Make sure we got them all, and move them into /files/
1524db6b0a61SColin Percival		while read Y; do
1525db6b0a61SColin Percival			if ! [ -f ${Y}.gz ]; then
1526db6b0a61SColin Percival				echo "failed."
1527db6b0a61SColin Percival				return 1
1528db6b0a61SColin Percival			fi
1529db6b0a61SColin Percival			if [ `gunzip -c < ${Y}.gz |
1530db6b0a61SColin Percival			    ${SHA256} -q` = ${Y} ]; then
1531db6b0a61SColin Percival				mv ${Y}.gz files/${Y}.gz
1532db6b0a61SColin Percival			else
1533db6b0a61SColin Percival				echo "${Y} has incorrect hash."
1534db6b0a61SColin Percival				return 1
1535db6b0a61SColin Percival			fi
1536db6b0a61SColin Percival		done < filelist
1537db6b0a61SColin Percival		echo "done."
1538db6b0a61SColin Percival
1539db6b0a61SColin Percival		# Clean up
1540db6b0a61SColin Percival		rm filelist files.wanted
1541db6b0a61SColin Percival	fi
1542db6b0a61SColin Percival}
1543db6b0a61SColin Percival
154448ffe56aSColin Percival# Prepare to fetch files: Generate a list of the files we need,
154548ffe56aSColin Percival# copy the unmodified files we have into /files/, and generate
154648ffe56aSColin Percival# a list of patches to download.
154748ffe56aSColin Percivalfetch_files_prepare () {
154848ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
154948ffe56aSColin Percival	echo -n "Preparing to download files... "
155048ffe56aSColin Percival
155148ffe56aSColin Percival	# Reduce indices to ${PATH}|${HASH} pairs
155248ffe56aSColin Percival	for X in $1 $2 $3; do
155348ffe56aSColin Percival		cut -f 1,2,7 -d '|' < ${X} |
155448ffe56aSColin Percival		    fgrep '|f|' |
155548ffe56aSColin Percival		    cut -f 1,3 -d '|' |
155648ffe56aSColin Percival		    sort > ${X}.hashes
155748ffe56aSColin Percival	done
155848ffe56aSColin Percival
155948ffe56aSColin Percival	# List of files wanted
156048ffe56aSColin Percival	cut -f 2 -d '|' < $3.hashes |
15612328d598SColin Percival	    sort -u |
15622328d598SColin Percival	    while read HASH; do
15632328d598SColin Percival		if ! [ -f files/${HASH}.gz ]; then
15642328d598SColin Percival			echo ${HASH}
15652328d598SColin Percival		fi
15662328d598SColin Percival	done > files.wanted
156748ffe56aSColin Percival
156848ffe56aSColin Percival	# Generate a list of unmodified files
156948ffe56aSColin Percival	comm -12 $1.hashes $2.hashes |
157048ffe56aSColin Percival	    sort -k 1,1 -t '|' > unmodified.files
157148ffe56aSColin Percival
157248ffe56aSColin Percival	# Copy all files into /files/.  We only need the unmodified files
157348ffe56aSColin Percival	# for use in patching; but we'll want all of them if the user asks
157448ffe56aSColin Percival	# to rollback the updates later.
1575210b8123SColin Percival	while read LINE; do
1576210b8123SColin Percival		F=`echo "${LINE}" | cut -f 1 -d '|'`
1577210b8123SColin Percival		HASH=`echo "${LINE}" | cut -f 2 -d '|'`
1578210b8123SColin Percival
1579210b8123SColin Percival		# Skip files we already have.
1580210b8123SColin Percival		if [ -f files/${HASH}.gz ]; then
1581210b8123SColin Percival			continue
1582210b8123SColin Percival		fi
1583210b8123SColin Percival
1584210b8123SColin Percival		# Make sure the file hasn't changed.
158548ffe56aSColin Percival		cp "${BASEDIR}/${F}" tmpfile
1586210b8123SColin Percival		if [ `sha256 -q tmpfile` != ${HASH} ]; then
1587210b8123SColin Percival			echo
1588210b8123SColin Percival			echo "File changed while FreeBSD Update running: ${F}"
1589210b8123SColin Percival			return 1
1590210b8123SColin Percival		fi
1591210b8123SColin Percival
1592210b8123SColin Percival		# Place the file into storage.
1593210b8123SColin Percival		gzip -c < tmpfile > files/${HASH}.gz
159448ffe56aSColin Percival		rm tmpfile
1595210b8123SColin Percival	done < $2.hashes
159648ffe56aSColin Percival
159748ffe56aSColin Percival	# Produce a list of patches to download
159848ffe56aSColin Percival	sort -k 1,1 -t '|' $3.hashes |
159948ffe56aSColin Percival	    join -t '|' -o 2.2,1.2 - unmodified.files |
160048ffe56aSColin Percival	    fetch_make_patchlist > patchlist
160148ffe56aSColin Percival
160248ffe56aSColin Percival	# Garbage collect
160348ffe56aSColin Percival	rm unmodified.files $1.hashes $2.hashes $3.hashes
160448ffe56aSColin Percival
160548ffe56aSColin Percival	# We don't need the list of possible old files any more.
160648ffe56aSColin Percival	rm $1
160748ffe56aSColin Percival
160848ffe56aSColin Percival	# We're finished making noise
160948ffe56aSColin Percival	echo "done."
161048ffe56aSColin Percival}
161148ffe56aSColin Percival
161248ffe56aSColin Percival# Fetch files.
161348ffe56aSColin Percivalfetch_files () {
161448ffe56aSColin Percival	# Attempt to fetch patches
161548ffe56aSColin Percival	if [ -s patchlist ]; then
161648ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
161748ffe56aSColin Percival		echo ${NDEBUG} "patches.${DDSTATS}"
161848ffe56aSColin Percival		tr '|' '-' < patchlist |
1619db6b0a61SColin Percival		    lam -s "${PATCHDIR}/" - |
162048ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
162148ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
162248ffe56aSColin Percival		echo "done."
162348ffe56aSColin Percival
162448ffe56aSColin Percival		# Attempt to apply patches
162548ffe56aSColin Percival		echo -n "Applying patches... "
162648ffe56aSColin Percival		tr '|' ' ' < patchlist |
162748ffe56aSColin Percival		    while read X Y; do
162848ffe56aSColin Percival			if [ ! -f "${X}-${Y}" ]; then continue; fi
162948ffe56aSColin Percival			gunzip -c < files/${X}.gz > OLD
163048ffe56aSColin Percival
163148ffe56aSColin Percival			bspatch OLD NEW ${X}-${Y}
163248ffe56aSColin Percival
163348ffe56aSColin Percival			if [ `${SHA256} -q NEW` = ${Y} ]; then
163448ffe56aSColin Percival				mv NEW files/${Y}
163548ffe56aSColin Percival				gzip -n files/${Y}
163648ffe56aSColin Percival			fi
163748ffe56aSColin Percival			rm -f diff OLD NEW ${X}-${Y}
163848ffe56aSColin Percival		done 2>${QUIETREDIR}
163948ffe56aSColin Percival		echo "done."
164048ffe56aSColin Percival	fi
164148ffe56aSColin Percival
164248ffe56aSColin Percival	# Download files which couldn't be generate via patching
164348ffe56aSColin Percival	while read Y; do
164448ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
164548ffe56aSColin Percival			echo ${Y};
164648ffe56aSColin Percival		fi
164748ffe56aSColin Percival	done < files.wanted > filelist
164848ffe56aSColin Percival
164948ffe56aSColin Percival	if [ -s filelist ]; then
165048ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
165148ffe56aSColin Percival		echo ${NDEBUG} "files... "
165248ffe56aSColin Percival		lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist |
165348ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
165448ffe56aSColin Percival		    2>${QUIETREDIR}
165548ffe56aSColin Percival
165648ffe56aSColin Percival		while read Y; do
165748ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
165848ffe56aSColin Percival				echo "failed."
165948ffe56aSColin Percival				return 1
166048ffe56aSColin Percival			fi
166148ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
166248ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
166348ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
166448ffe56aSColin Percival			else
166548ffe56aSColin Percival				echo "${Y} has incorrect hash."
166648ffe56aSColin Percival				return 1
166748ffe56aSColin Percival			fi
166848ffe56aSColin Percival		done < filelist
166948ffe56aSColin Percival		echo "done."
167048ffe56aSColin Percival	fi
167148ffe56aSColin Percival
167248ffe56aSColin Percival	# Clean up
167348ffe56aSColin Percival	rm files.wanted filelist patchlist
167448ffe56aSColin Percival}
167548ffe56aSColin Percival
167648ffe56aSColin Percival# Create and populate install manifest directory; and report what updates
167748ffe56aSColin Percival# are available.
167848ffe56aSColin Percivalfetch_create_manifest () {
167948ffe56aSColin Percival	# If we have an existing install manifest, nuke it.
168048ffe56aSColin Percival	if [ -L "${BDHASH}-install" ]; then
168148ffe56aSColin Percival		rm -r ${BDHASH}-install/
168248ffe56aSColin Percival		rm ${BDHASH}-install
168348ffe56aSColin Percival	fi
168448ffe56aSColin Percival
168548ffe56aSColin Percival	# Report to the user if any updates were avoided due to local changes
168648ffe56aSColin Percival	if [ -s modifiedfiles ]; then
168748ffe56aSColin Percival		echo
168848ffe56aSColin Percival		echo -n "The following files are affected by updates, "
168948ffe56aSColin Percival		echo "but no changes have"
169048ffe56aSColin Percival		echo -n "been downloaded because the files have been "
169148ffe56aSColin Percival		echo "modified locally:"
169248ffe56aSColin Percival		cat modifiedfiles
1693db6b0a61SColin Percival	fi | more
169448ffe56aSColin Percival	rm modifiedfiles
169548ffe56aSColin Percival
169648ffe56aSColin Percival	# If no files will be updated, tell the user and exit
169748ffe56aSColin Percival	if ! [ -s INDEX-PRESENT ] &&
169848ffe56aSColin Percival	    ! [ -s INDEX-NEW ]; then
169948ffe56aSColin Percival		rm INDEX-PRESENT INDEX-NEW
170048ffe56aSColin Percival		echo
170148ffe56aSColin Percival		echo -n "No updates needed to update system to "
170248ffe56aSColin Percival		echo "${RELNUM}-p${RELPATCHNUM}."
170348ffe56aSColin Percival		return
170448ffe56aSColin Percival	fi
170548ffe56aSColin Percival
170648ffe56aSColin Percival	# Divide files into (a) removed files, (b) added files, and
170748ffe56aSColin Percival	# (c) updated files.
170848ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-PRESENT |
170948ffe56aSColin Percival	    sort > INDEX-PRESENT.flist
171048ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-NEW |
171148ffe56aSColin Percival	    sort > INDEX-NEW.flist
171248ffe56aSColin Percival	comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed
171348ffe56aSColin Percival	comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added
171448ffe56aSColin Percival	comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated
171548ffe56aSColin Percival	rm INDEX-PRESENT.flist INDEX-NEW.flist
171648ffe56aSColin Percival
171748ffe56aSColin Percival	# Report removed files, if any
171848ffe56aSColin Percival	if [ -s files.removed ]; then
171948ffe56aSColin Percival		echo
172048ffe56aSColin Percival		echo -n "The following files will be removed "
172148ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
172248ffe56aSColin Percival		cat files.removed
1723db6b0a61SColin Percival	fi | more
172448ffe56aSColin Percival	rm files.removed
172548ffe56aSColin Percival
172648ffe56aSColin Percival	# Report added files, if any
172748ffe56aSColin Percival	if [ -s files.added ]; then
172848ffe56aSColin Percival		echo
172948ffe56aSColin Percival		echo -n "The following files will be added "
173048ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
173148ffe56aSColin Percival		cat files.added
1732db6b0a61SColin Percival	fi | more
173348ffe56aSColin Percival	rm files.added
173448ffe56aSColin Percival
173548ffe56aSColin Percival	# Report updated files, if any
173648ffe56aSColin Percival	if [ -s files.updated ]; then
173748ffe56aSColin Percival		echo
173848ffe56aSColin Percival		echo -n "The following files will be updated "
173948ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
174048ffe56aSColin Percival
174148ffe56aSColin Percival		cat files.updated
1742db6b0a61SColin Percival	fi | more
174348ffe56aSColin Percival	rm files.updated
174448ffe56aSColin Percival
174548ffe56aSColin Percival	# Create a directory for the install manifest.
174648ffe56aSColin Percival	MDIR=`mktemp -d install.XXXXXX` || return 1
174748ffe56aSColin Percival
174848ffe56aSColin Percival	# Populate it
174948ffe56aSColin Percival	mv INDEX-PRESENT ${MDIR}/INDEX-OLD
175048ffe56aSColin Percival	mv INDEX-NEW ${MDIR}/INDEX-NEW
175148ffe56aSColin Percival
175248ffe56aSColin Percival	# Link it into place
175348ffe56aSColin Percival	ln -s ${MDIR} ${BDHASH}-install
175448ffe56aSColin Percival}
175548ffe56aSColin Percival
175648ffe56aSColin Percival# Warn about any upcoming EoL
175748ffe56aSColin Percivalfetch_warn_eol () {
175848ffe56aSColin Percival	# What's the current time?
175948ffe56aSColin Percival	NOWTIME=`date "+%s"`
176048ffe56aSColin Percival
176148ffe56aSColin Percival	# When did we last warn about the EoL date?
176248ffe56aSColin Percival	if [ -f lasteolwarn ]; then
176348ffe56aSColin Percival		LASTWARN=`cat lasteolwarn`
176448ffe56aSColin Percival	else
176548ffe56aSColin Percival		LASTWARN=`expr ${NOWTIME} - 63072000`
176648ffe56aSColin Percival	fi
176748ffe56aSColin Percival
176848ffe56aSColin Percival	# If the EoL time is past, warn.
176948ffe56aSColin Percival	if [ ${EOLTIME} -lt ${NOWTIME} ]; then
177048ffe56aSColin Percival		echo
177148ffe56aSColin Percival		cat <<-EOF
1772b698a3abSColin Percival		WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE.
177348ffe56aSColin Percival		Any security issues discovered after `date -r ${EOLTIME}`
177448ffe56aSColin Percival		will not have been corrected.
177548ffe56aSColin Percival		EOF
177648ffe56aSColin Percival		return 1
177748ffe56aSColin Percival	fi
177848ffe56aSColin Percival
177948ffe56aSColin Percival	# Figure out how long it has been since we last warned about the
178048ffe56aSColin Percival	# upcoming EoL, and how much longer we have left.
178148ffe56aSColin Percival	SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}`
178248ffe56aSColin Percival	TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}`
178348ffe56aSColin Percival
178489b14566SColin Percival	# Don't warn if the EoL is more than 3 months away
178589b14566SColin Percival	if [ ${TIMELEFT} -gt 7884000 ]; then
178648ffe56aSColin Percival		return 0
178748ffe56aSColin Percival	fi
178848ffe56aSColin Percival
178948ffe56aSColin Percival	# Don't warn if the time remaining is more than 3 times the time
179048ffe56aSColin Percival	# since the last warning.
179148ffe56aSColin Percival	if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then
179248ffe56aSColin Percival		return 0
179348ffe56aSColin Percival	fi
179448ffe56aSColin Percival
179548ffe56aSColin Percival	# Figure out what time units to use.
179648ffe56aSColin Percival	if [ ${TIMELEFT} -lt 604800 ]; then
179748ffe56aSColin Percival		UNIT="day"
179848ffe56aSColin Percival		SIZE=86400
179948ffe56aSColin Percival	elif [ ${TIMELEFT} -lt 2678400 ]; then
180048ffe56aSColin Percival		UNIT="week"
180148ffe56aSColin Percival		SIZE=604800
180248ffe56aSColin Percival	else
180348ffe56aSColin Percival		UNIT="month"
180448ffe56aSColin Percival		SIZE=2678400
180548ffe56aSColin Percival	fi
180648ffe56aSColin Percival
180748ffe56aSColin Percival	# Compute the right number of units
180848ffe56aSColin Percival	NUM=`expr ${TIMELEFT} / ${SIZE}`
180948ffe56aSColin Percival	if [ ${NUM} != 1 ]; then
181048ffe56aSColin Percival		UNIT="${UNIT}s"
181148ffe56aSColin Percival	fi
181248ffe56aSColin Percival
181348ffe56aSColin Percival	# Print the warning
181448ffe56aSColin Percival	echo
181548ffe56aSColin Percival	cat <<-EOF
181648ffe56aSColin Percival		WARNING: `uname -sr` is approaching its End-of-Life date.
181748ffe56aSColin Percival		It is strongly recommended that you upgrade to a newer
181848ffe56aSColin Percival		release within the next ${NUM} ${UNIT}.
181948ffe56aSColin Percival	EOF
182048ffe56aSColin Percival
182148ffe56aSColin Percival	# Update the stored time of last warning
182248ffe56aSColin Percival	echo ${NOWTIME} > lasteolwarn
182348ffe56aSColin Percival}
182448ffe56aSColin Percival
182548ffe56aSColin Percival# Do the actual work involved in "fetch" / "cron".
182648ffe56aSColin Percivalfetch_run () {
182748ffe56aSColin Percival	workdir_init || return 1
182848ffe56aSColin Percival
182948ffe56aSColin Percival	# Prepare the mirror list.
183048ffe56aSColin Percival	fetch_pick_server_init && fetch_pick_server
183148ffe56aSColin Percival
183248ffe56aSColin Percival	# Try to fetch the public key until we run out of servers.
183348ffe56aSColin Percival	while ! fetch_key; do
183448ffe56aSColin Percival		fetch_pick_server || return 1
183548ffe56aSColin Percival	done
183648ffe56aSColin Percival
183748ffe56aSColin Percival	# Try to fetch the metadata index signature ("tag") until we run
183848ffe56aSColin Percival	# out of available servers; and sanity check the downloaded tag.
183948ffe56aSColin Percival	while ! fetch_tag; do
184048ffe56aSColin Percival		fetch_pick_server || return 1
184148ffe56aSColin Percival	done
184248ffe56aSColin Percival	fetch_tagsanity || return 1
184348ffe56aSColin Percival
184448ffe56aSColin Percival	# Fetch the latest INDEX-NEW and INDEX-OLD files.
184548ffe56aSColin Percival	fetch_metadata INDEX-NEW INDEX-OLD || return 1
184648ffe56aSColin Percival
184748ffe56aSColin Percival	# Generate filtered INDEX-NEW and INDEX-OLD files containing only
184848ffe56aSColin Percival	# the lines which (a) belong to components we care about, and (b)
184948ffe56aSColin Percival	# don't correspond to paths we're explicitly ignoring.
185048ffe56aSColin Percival	fetch_filter_metadata INDEX-NEW || return 1
185148ffe56aSColin Percival	fetch_filter_metadata INDEX-OLD || return 1
185248ffe56aSColin Percival
1853db6b0a61SColin Percival	# Translate /boot/${KERNCONF} into ${KERNELDIR}
1854db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-NEW ${KERNCONF}
1855db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
185648ffe56aSColin Percival
185748ffe56aSColin Percival	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
185848ffe56aSColin Percival	# system and generate an INDEX-PRESENT file.
185948ffe56aSColin Percival	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
186048ffe56aSColin Percival
186148ffe56aSColin Percival	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
186248ffe56aSColin Percival	# correspond to lines in INDEX-PRESENT with hashes not appearing
186348ffe56aSColin Percival	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
186448ffe56aSColin Percival	# INDEX-PRESENT has type - and there isn't a corresponding entry in
186548ffe56aSColin Percival	# INDEX-OLD with type -.
1866db6b0a61SColin Percival	fetch_filter_unmodified_notpresent	\
1867db6b0a61SColin Percival	    INDEX-OLD INDEX-PRESENT INDEX-NEW /dev/null
186848ffe56aSColin Percival
186948ffe56aSColin Percival	# For each entry in INDEX-PRESENT of type -, remove any corresponding
187048ffe56aSColin Percival	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
187148ffe56aSColin Percival	# of type - from INDEX-PRESENT.
187248ffe56aSColin Percival	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
187348ffe56aSColin Percival
187448ffe56aSColin Percival	# If ${ALLOWDELETE} != "yes", then remove any entries from
187548ffe56aSColin Percival	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
187648ffe56aSColin Percival	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
187748ffe56aSColin Percival
187848ffe56aSColin Percival	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
187948ffe56aSColin Percival	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
188048ffe56aSColin Percival	# replace the corresponding line of INDEX-NEW with one having the
188148ffe56aSColin Percival	# same metadata as the entry in INDEX-PRESENT.
188248ffe56aSColin Percival	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
188348ffe56aSColin Percival
188448ffe56aSColin Percival	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
188548ffe56aSColin Percival	# no need to update a file if it isn't changing.
188648ffe56aSColin Percival	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
188748ffe56aSColin Percival
188848ffe56aSColin Percival	# Prepare to fetch files: Generate a list of the files we need,
188948ffe56aSColin Percival	# copy the unmodified files we have into /files/, and generate
189048ffe56aSColin Percival	# a list of patches to download.
1891210b8123SColin Percival	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
189248ffe56aSColin Percival
189348ffe56aSColin Percival	# Fetch files.
189448ffe56aSColin Percival	fetch_files || return 1
189548ffe56aSColin Percival
189648ffe56aSColin Percival	# Create and populate install manifest directory; and report what
189748ffe56aSColin Percival	# updates are available.
189848ffe56aSColin Percival	fetch_create_manifest || return 1
189948ffe56aSColin Percival
190048ffe56aSColin Percival	# Warn about any upcoming EoL
190148ffe56aSColin Percival	fetch_warn_eol || return 1
190248ffe56aSColin Percival}
190348ffe56aSColin Percival
1904db6b0a61SColin Percival# If StrictComponents is not "yes", generate a new components list
1905db6b0a61SColin Percival# with only the components which appear to be installed.
1906db6b0a61SColin Percivalupgrade_guess_components () {
1907db6b0a61SColin Percival	if [ "${STRICTCOMPONENTS}" = "no" ]; then
1908db6b0a61SColin Percival		# Generate filtered INDEX-ALL with only the components listed
1909db6b0a61SColin Percival		# in COMPONENTS.
1910db6b0a61SColin Percival		fetch_filter_metadata_components $1 || return 1
1911db6b0a61SColin Percival
1912db6b0a61SColin Percival		# Tell the user why his disk is suddenly making lots of noise
1913db6b0a61SColin Percival		echo -n "Inspecting system... "
1914db6b0a61SColin Percival
1915db6b0a61SColin Percival		# Look at the files on disk, and assume that a component is
1916db6b0a61SColin Percival		# supposed to be present if it is more than half-present.
1917db6b0a61SColin Percival		cut -f 1-3 -d '|' < INDEX-ALL |
1918db6b0a61SColin Percival		    tr '|' ' ' |
1919db6b0a61SColin Percival		    while read C S F; do
1920db6b0a61SColin Percival			if [ -e ${BASEDIR}/${F} ]; then
1921db6b0a61SColin Percival				echo "+ ${C}|${S}"
1922db6b0a61SColin Percival			fi
1923db6b0a61SColin Percival			echo "= ${C}|${S}"
1924db6b0a61SColin Percival		    done |
1925db6b0a61SColin Percival		    sort |
1926db6b0a61SColin Percival		    uniq -c |
1927db6b0a61SColin Percival		    sed -E 's,^ +,,' > compfreq
1928db6b0a61SColin Percival		grep ' = ' compfreq |
1929db6b0a61SColin Percival		    cut -f 1,3 -d ' ' |
1930db6b0a61SColin Percival		    sort -k 2,2 -t ' ' > compfreq.total
1931db6b0a61SColin Percival		grep ' + ' compfreq |
1932db6b0a61SColin Percival		    cut -f 1,3 -d ' ' |
1933db6b0a61SColin Percival		    sort -k 2,2 -t ' ' > compfreq.present
1934db6b0a61SColin Percival		join -t ' ' -1 2 -2 2 compfreq.present compfreq.total |
1935db6b0a61SColin Percival		    while read S P T; do
1936db6b0a61SColin Percival			if [ ${P} -gt `expr ${T} / 2` ]; then
1937db6b0a61SColin Percival				echo ${S}
1938db6b0a61SColin Percival			fi
1939db6b0a61SColin Percival		    done > comp.present
1940db6b0a61SColin Percival		cut -f 2 -d ' ' < compfreq.total > comp.total
1941db6b0a61SColin Percival		rm INDEX-ALL compfreq compfreq.total compfreq.present
1942db6b0a61SColin Percival
1943db6b0a61SColin Percival		# We're done making noise.
1944db6b0a61SColin Percival		echo "done."
1945db6b0a61SColin Percival
1946db6b0a61SColin Percival		# Sometimes the kernel isn't installed where INDEX-ALL
1947db6b0a61SColin Percival		# thinks that it should be: In particular, it is often in
1948db6b0a61SColin Percival		# /boot/kernel instead of /boot/GENERIC or /boot/SMP.  To
1949db6b0a61SColin Percival		# deal with this, if "kernel|X" is listed in comp.total
1950db6b0a61SColin Percival		# (i.e., is a component which would be upgraded if it is
1951db6b0a61SColin Percival		# found to be present) we will add it to comp.present.
1952db6b0a61SColin Percival		# If "kernel|<anything>" is in comp.total but "kernel|X" is
1953db6b0a61SColin Percival		# not, we print a warning -- the user is running a kernel
1954db6b0a61SColin Percival		# which isn't part of the release.
1955db6b0a61SColin Percival		KCOMP=`echo ${KERNCONF} | tr 'A-Z' 'a-z'`
1956db6b0a61SColin Percival		grep -E "^kernel\|${KCOMP}\$" comp.total >> comp.present
1957db6b0a61SColin Percival
1958db6b0a61SColin Percival		if grep -qE "^kernel\|" comp.total &&
1959db6b0a61SColin Percival		    ! grep -qE "^kernel\|${KCOMP}\$" comp.total; then
1960db6b0a61SColin Percival			cat <<-EOF
1961db6b0a61SColin Percival
1962db6b0a61SColin PercivalWARNING: This system is running a "${KCOMP}" kernel, which is not a
1963db6b0a61SColin Percivalkernel configuration distributed as part of FreeBSD ${RELNUM}.
1964db6b0a61SColin PercivalThis kernel will not be updated: you MUST update the kernel manually
1965db6b0a61SColin Percivalbefore running "$0 install".
1966db6b0a61SColin Percival			EOF
1967db6b0a61SColin Percival		fi
1968db6b0a61SColin Percival
1969db6b0a61SColin Percival		# Re-sort the list of installed components and generate
1970db6b0a61SColin Percival		# the list of non-installed components.
1971db6b0a61SColin Percival		sort -u < comp.present > comp.present.tmp
1972db6b0a61SColin Percival		mv comp.present.tmp comp.present
1973db6b0a61SColin Percival		comm -13 comp.present comp.total > comp.absent
1974db6b0a61SColin Percival
1975db6b0a61SColin Percival		# Ask the user to confirm that what we have is correct.  To
1976db6b0a61SColin Percival		# reduce user confusion, translate "X|Y" back to "X/Y" (as
1977db6b0a61SColin Percival		# subcomponents must be listed in the configuration file).
1978db6b0a61SColin Percival		echo
1979db6b0a61SColin Percival		echo -n "The following components of FreeBSD "
1980db6b0a61SColin Percival		echo "seem to be installed:"
1981db6b0a61SColin Percival		tr '|' '/' < comp.present |
1982db6b0a61SColin Percival		    fmt -72
1983db6b0a61SColin Percival		echo
1984db6b0a61SColin Percival		echo -n "The following components of FreeBSD "
1985db6b0a61SColin Percival		echo "do not seem to be installed:"
1986db6b0a61SColin Percival		tr '|' '/' < comp.absent |
1987db6b0a61SColin Percival		    fmt -72
1988db6b0a61SColin Percival		echo
1989db6b0a61SColin Percival		continuep || return 1
1990db6b0a61SColin Percival		echo
1991db6b0a61SColin Percival
1992db6b0a61SColin Percival		# Suck the generated list of components into ${COMPONENTS}.
1993db6b0a61SColin Percival		# Note that comp.present.tmp is used due to issues with
1994db6b0a61SColin Percival		# pipelines and setting variables.
1995db6b0a61SColin Percival		COMPONENTS=""
1996db6b0a61SColin Percival		tr '|' '/' < comp.present > comp.present.tmp
1997db6b0a61SColin Percival		while read C; do
1998db6b0a61SColin Percival			COMPONENTS="${COMPONENTS} ${C}"
1999db6b0a61SColin Percival		done < comp.present.tmp
2000db6b0a61SColin Percival
2001db6b0a61SColin Percival		# Delete temporary files
2002db6b0a61SColin Percival		rm comp.present comp.present.tmp comp.absent comp.total
2003db6b0a61SColin Percival	fi
2004db6b0a61SColin Percival}
2005db6b0a61SColin Percival
2006db6b0a61SColin Percival# If StrictComponents is not "yes", COMPONENTS contains an entry
2007db6b0a61SColin Percival# corresponding to the currently running kernel, and said kernel
2008db6b0a61SColin Percival# does not exist in the new release, add "kernel/generic" to the
2009db6b0a61SColin Percival# list of components.
2010db6b0a61SColin Percivalupgrade_guess_new_kernel () {
2011db6b0a61SColin Percival	if [ "${STRICTCOMPONENTS}" = "no" ]; then
2012db6b0a61SColin Percival		# Grab the unfiltered metadata file.
2013db6b0a61SColin Percival		METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
2014db6b0a61SColin Percival		gunzip -c < files/${METAHASH}.gz > $1.all
2015db6b0a61SColin Percival
2016db6b0a61SColin Percival		# If "kernel/${KCOMP}" is in ${COMPONENTS} and that component
2017db6b0a61SColin Percival		# isn't in $1.all, we need to add kernel/generic.
2018db6b0a61SColin Percival		for C in ${COMPONENTS}; do
2019db6b0a61SColin Percival			if [ ${C} = "kernel/${KCOMP}" ] &&
2020db6b0a61SColin Percival			    ! grep -qE "^kernel\|${KCOMP}\|" $1.all; then
2021db6b0a61SColin Percival				COMPONENTS="${COMPONENTS} kernel/generic"
2022db6b0a61SColin Percival				NKERNCONF="GENERIC"
2023db6b0a61SColin Percival				cat <<-EOF
2024db6b0a61SColin Percival
2025db6b0a61SColin PercivalWARNING: This system is running a "${KCOMP}" kernel, which is not a
2026db6b0a61SColin Percivalkernel configuration distributed as part of FreeBSD ${RELNUM}.
2027db6b0a61SColin PercivalAs part of upgrading to FreeBSD ${RELNUM}, this kernel will be
2028db6b0a61SColin Percivalreplaced with a "generic" kernel.
2029db6b0a61SColin Percival				EOF
2030db6b0a61SColin Percival				continuep || return 1
2031db6b0a61SColin Percival			fi
2032db6b0a61SColin Percival		done
2033db6b0a61SColin Percival
2034db6b0a61SColin Percival		# Don't need this any more...
2035db6b0a61SColin Percival		rm $1.all
2036db6b0a61SColin Percival	fi
2037db6b0a61SColin Percival}
2038db6b0a61SColin Percival
2039db6b0a61SColin Percival# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2040db6b0a61SColin Percival# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2041db6b0a61SColin Percivalupgrade_oldall_to_oldnew () {
2042db6b0a61SColin Percival	# For each ${F}|... which appears in INDEX-ALL but does not appear
2043db6b0a61SColin Percival	# in INDEX-OLD, add ${F}|-|||||| to INDEX-OLD.
2044db6b0a61SColin Percival	cut -f 1 -d '|' < $1 |
2045db6b0a61SColin Percival	    sort -u > $1.paths
2046db6b0a61SColin Percival	cut -f 1 -d '|' < $2 |
2047db6b0a61SColin Percival	    sort -u |
2048db6b0a61SColin Percival	    comm -13 $1.paths - |
2049db6b0a61SColin Percival	    lam - -s "|-||||||" |
2050db6b0a61SColin Percival	    sort - $1 > $1.tmp
2051db6b0a61SColin Percival	mv $1.tmp $1
2052db6b0a61SColin Percival
2053db6b0a61SColin Percival	# Remove lines from INDEX-OLD which also appear in INDEX-ALL
2054db6b0a61SColin Percival	comm -23 $1 $2 > $1.tmp
2055db6b0a61SColin Percival	mv $1.tmp $1
2056db6b0a61SColin Percival
2057db6b0a61SColin Percival	# Remove lines from INDEX-ALL which have a file name not appearing
2058db6b0a61SColin Percival	# anywhere in INDEX-OLD (since these must be files which haven't
2059db6b0a61SColin Percival	# changed -- if they were new, there would be an entry of type "-").
2060db6b0a61SColin Percival	cut -f 1 -d '|' < $1 |
2061db6b0a61SColin Percival	    sort -u > $1.paths
2062db6b0a61SColin Percival	sort -k 1,1 -t '|' < $2 |
2063db6b0a61SColin Percival	    join -t '|' - $1.paths |
2064db6b0a61SColin Percival	    sort > $2.tmp
2065db6b0a61SColin Percival	rm $1.paths
2066db6b0a61SColin Percival	mv $2.tmp $2
2067db6b0a61SColin Percival
2068db6b0a61SColin Percival	# Rename INDEX-ALL to INDEX-NEW.
2069db6b0a61SColin Percival	mv $2 $3
2070db6b0a61SColin Percival}
2071db6b0a61SColin Percival
2072db6b0a61SColin Percival# From the list of "old" files in $1, merge changes in $2 with those in $3,
2073db6b0a61SColin Percival# and update $3 to reflect the hashes of merged files.
2074db6b0a61SColin Percivalupgrade_merge () {
2075db6b0a61SColin Percival	# We only need to do anything if $1 is non-empty.
2076db6b0a61SColin Percival	if [ -s $1 ]; then
2077db6b0a61SColin Percival		cut -f 1 -d '|' $1 |
2078db6b0a61SColin Percival		    sort > $1-paths
2079db6b0a61SColin Percival
2080db6b0a61SColin Percival		# Create staging area for merging files
2081db6b0a61SColin Percival		rm -rf merge/
2082db6b0a61SColin Percival		while read F; do
2083db6b0a61SColin Percival			D=`dirname ${F}`
2084db6b0a61SColin Percival			mkdir -p merge/old/${D}
2085db6b0a61SColin Percival			mkdir -p merge/${OLDRELNUM}/${D}
2086db6b0a61SColin Percival			mkdir -p merge/${RELNUM}/${D}
2087db6b0a61SColin Percival			mkdir -p merge/new/${D}
2088db6b0a61SColin Percival		done < $1-paths
2089db6b0a61SColin Percival
2090db6b0a61SColin Percival		# Copy in files
2091db6b0a61SColin Percival		while read F; do
2092db6b0a61SColin Percival			# Currently installed file
2093db6b0a61SColin Percival			V=`look "${F}|" $2 | cut -f 7 -d '|'`
2094db6b0a61SColin Percival			gunzip < files/${V}.gz > merge/old/${F}
2095db6b0a61SColin Percival
2096db6b0a61SColin Percival			# Old release
2097db6b0a61SColin Percival			if look "${F}|" $1 | fgrep -q "|f|"; then
2098db6b0a61SColin Percival				V=`look "${F}|" $1 | cut -f 3 -d '|'`
2099db6b0a61SColin Percival				gunzip < files/${V}.gz		\
2100db6b0a61SColin Percival				    > merge/${OLDRELNUM}/${F}
2101db6b0a61SColin Percival			fi
2102db6b0a61SColin Percival
2103db6b0a61SColin Percival			# New release
2104db6b0a61SColin Percival			if look "${F}|" $3 | cut -f 1,2,7 -d '|' |
2105db6b0a61SColin Percival			    fgrep -q "|f|"; then
2106db6b0a61SColin Percival				V=`look "${F}|" $3 | cut -f 7 -d '|'`
2107db6b0a61SColin Percival				gunzip < files/${V}.gz		\
2108db6b0a61SColin Percival				    > merge/${RELNUM}/${F}
2109db6b0a61SColin Percival			fi
2110db6b0a61SColin Percival		done < $1-paths
2111db6b0a61SColin Percival
2112db6b0a61SColin Percival		# Attempt to automatically merge changes
2113db6b0a61SColin Percival		echo -n "Attempting to automatically merge "
2114db6b0a61SColin Percival		echo -n "changes in files..."
2115db6b0a61SColin Percival		: > failed.merges
2116db6b0a61SColin Percival		while read F; do
2117db6b0a61SColin Percival			# If the file doesn't exist in the new release,
2118db6b0a61SColin Percival			# the result of "merging changes" is having the file
2119db6b0a61SColin Percival			# not exist.
2120db6b0a61SColin Percival			if ! [ -f merge/${RELNUM}/${F} ]; then
2121db6b0a61SColin Percival				continue
2122db6b0a61SColin Percival			fi
2123db6b0a61SColin Percival
2124db6b0a61SColin Percival			# If the file didn't exist in the old release, we're
2125db6b0a61SColin Percival			# going to throw away the existing file and hope that
2126db6b0a61SColin Percival			# the version from the new release is what we want.
2127db6b0a61SColin Percival			if ! [ -f merge/${OLDRELNUM}/${F} ]; then
2128db6b0a61SColin Percival				cp merge/${RELNUM}/${F} merge/new/${F}
2129db6b0a61SColin Percival				continue
2130db6b0a61SColin Percival			fi
2131db6b0a61SColin Percival
2132db6b0a61SColin Percival			# Some files need special treatment.
2133db6b0a61SColin Percival			case ${F} in
2134db6b0a61SColin Percival			/etc/spwd.db | /etc/pwd.db | /etc/login.conf.db)
2135db6b0a61SColin Percival				# Don't merge these -- we're rebuild them
2136db6b0a61SColin Percival				# after updates are installed.
2137db6b0a61SColin Percival				cp merge/old/${F} merge/new/${F}
2138db6b0a61SColin Percival				;;
2139db6b0a61SColin Percival			*)
2140db6b0a61SColin Percival				if ! merge -p -L "current version"	\
2141db6b0a61SColin Percival				    -L "${OLDRELNUM}" -L "${RELNUM}"	\
2142db6b0a61SColin Percival				    merge/old/${F}			\
2143db6b0a61SColin Percival				    merge/${OLDRELNUM}/${F}		\
2144db6b0a61SColin Percival				    merge/${RELNUM}/${F}		\
2145db6b0a61SColin Percival				    > merge/new/${F} 2>/dev/null; then
2146db6b0a61SColin Percival					echo ${F} >> failed.merges
2147db6b0a61SColin Percival				fi
2148db6b0a61SColin Percival				;;
2149db6b0a61SColin Percival			esac
2150db6b0a61SColin Percival		done < $1-paths
2151db6b0a61SColin Percival		echo " done."
2152db6b0a61SColin Percival
2153db6b0a61SColin Percival		# Ask the user to handle any files which didn't merge.
2154db6b0a61SColin Percival		while read F; do
2155db6b0a61SColin Percival			cat <<-EOF
2156db6b0a61SColin Percival
2157db6b0a61SColin PercivalThe following file could not be merged automatically: ${F}
2158db6b0a61SColin PercivalPress Enter to edit this file in ${EDITOR} and resolve the conflicts
2159db6b0a61SColin Percivalmanually...
2160db6b0a61SColin Percival			EOF
2161db6b0a61SColin Percival			read dummy </dev/tty
2162db6b0a61SColin Percival			${EDITOR} `pwd`/merge/new/${F} < /dev/tty
2163db6b0a61SColin Percival		done < failed.merges
2164db6b0a61SColin Percival		rm failed.merges
2165db6b0a61SColin Percival
2166db6b0a61SColin Percival		# Ask the user to confirm that he likes how the result
2167db6b0a61SColin Percival		# of merging files.
2168db6b0a61SColin Percival		while read F; do
2169db6b0a61SColin Percival			# Skip files which haven't changed.
2170db6b0a61SColin Percival			if [ -f merge/new/${F} ] &&
2171db6b0a61SColin Percival			    cmp -s merge/old/${F} merge/new/${F}; then
2172db6b0a61SColin Percival				continue
2173db6b0a61SColin Percival			fi
2174db6b0a61SColin Percival
2175db6b0a61SColin Percival			# Warn about files which are ceasing to exist.
2176db6b0a61SColin Percival			if ! [ -f merge/new/${F} ]; then
2177db6b0a61SColin Percival				cat <<-EOF
2178db6b0a61SColin Percival
2179db6b0a61SColin PercivalThe following file will be removed, as it no longer exists in
2180db6b0a61SColin PercivalFreeBSD ${RELNUM}: ${F}
2181db6b0a61SColin Percival				EOF
2182db6b0a61SColin Percival				continuep < /dev/tty || return 1
2183db6b0a61SColin Percival				continue
2184db6b0a61SColin Percival			fi
2185db6b0a61SColin Percival
2186db6b0a61SColin Percival			# Print changes for the user's approval.
2187db6b0a61SColin Percival			cat <<-EOF
2188db6b0a61SColin Percival
2189db6b0a61SColin PercivalThe following changes, which occurred between FreeBSD ${OLDRELNUM} and
2190db6b0a61SColin PercivalFreeBSD ${RELNUM} have been merged into ${F}:
2191db6b0a61SColin PercivalEOF
2192db6b0a61SColin Percival			diff -U 5 -L "current version" -L "new version"	\
2193db6b0a61SColin Percival			    merge/old/${F} merge/new/${F} || true
2194db6b0a61SColin Percival			continuep < /dev/tty || return 1
2195db6b0a61SColin Percival		done < $1-paths
2196db6b0a61SColin Percival
2197db6b0a61SColin Percival		# Store merged files.
2198db6b0a61SColin Percival		while read F; do
2199db6b0a61SColin Percival			V=`${SHA256} -q merge/new/${F}`
2200db6b0a61SColin Percival
2201db6b0a61SColin Percival			if [ -f merge/new/${F} ]; then
2202db6b0a61SColin Percival				gzip -c < merge/new/${F} > files/${V}.gz
2203db6b0a61SColin Percival				echo "${F}|${V}"
2204db6b0a61SColin Percival			fi
2205db6b0a61SColin Percival		done < $1-paths > newhashes
2206db6b0a61SColin Percival
2207db6b0a61SColin Percival		# Pull lines out from $3 which need to be updated to
2208db6b0a61SColin Percival		# reflect merged files.
2209db6b0a61SColin Percival		while read F; do
2210db6b0a61SColin Percival			look "${F}|" $3
2211db6b0a61SColin Percival		done < $1-paths > $3-oldlines
2212db6b0a61SColin Percival
2213db6b0a61SColin Percival		# Update lines to reflect merged files
2214db6b0a61SColin Percival		join -t '|' -o 1.1,1.2,1.3,1.4,1.5,1.6,2.2,1.8		\
2215db6b0a61SColin Percival		    $3-oldlines newhashes > $3-newlines
2216db6b0a61SColin Percival
2217db6b0a61SColin Percival		# Remove old lines from $3 and add new lines.
2218db6b0a61SColin Percival		sort $3-oldlines |
2219db6b0a61SColin Percival		    comm -13 - $3 |
2220db6b0a61SColin Percival		    sort - $3-newlines > $3.tmp
2221db6b0a61SColin Percival		mv $3.tmp $3
2222db6b0a61SColin Percival
2223db6b0a61SColin Percival		# Clean up
2224db6b0a61SColin Percival		rm $1-paths newhashes $3-oldlines $3-newlines
2225db6b0a61SColin Percival		rm -rf merge/
2226db6b0a61SColin Percival	fi
2227db6b0a61SColin Percival
2228db6b0a61SColin Percival	# We're done with merging files.
2229db6b0a61SColin Percival	rm $1
2230db6b0a61SColin Percival}
2231db6b0a61SColin Percival
2232db6b0a61SColin Percival# Do the work involved in fetching upgrades to a new release
2233db6b0a61SColin Percivalupgrade_run () {
2234db6b0a61SColin Percival	workdir_init || return 1
2235db6b0a61SColin Percival
2236db6b0a61SColin Percival	# Prepare the mirror list.
2237db6b0a61SColin Percival	fetch_pick_server_init && fetch_pick_server
2238db6b0a61SColin Percival
2239db6b0a61SColin Percival	# Try to fetch the public key until we run out of servers.
2240db6b0a61SColin Percival	while ! fetch_key; do
2241db6b0a61SColin Percival		fetch_pick_server || return 1
2242db6b0a61SColin Percival	done
2243db6b0a61SColin Percival
2244db6b0a61SColin Percival	# Try to fetch the metadata index signature ("tag") until we run
2245db6b0a61SColin Percival	# out of available servers; and sanity check the downloaded tag.
2246db6b0a61SColin Percival	while ! fetch_tag; do
2247db6b0a61SColin Percival		fetch_pick_server || return 1
2248db6b0a61SColin Percival	done
2249db6b0a61SColin Percival	fetch_tagsanity || return 1
2250db6b0a61SColin Percival
2251db6b0a61SColin Percival	# Fetch the INDEX-OLD and INDEX-ALL.
2252db6b0a61SColin Percival	fetch_metadata INDEX-OLD INDEX-ALL || return 1
2253db6b0a61SColin Percival
2254db6b0a61SColin Percival	# If StrictComponents is not "yes", generate a new components list
2255db6b0a61SColin Percival	# with only the components which appear to be installed.
2256db6b0a61SColin Percival	upgrade_guess_components INDEX-ALL || return 1
2257db6b0a61SColin Percival
2258db6b0a61SColin Percival	# Generate filtered INDEX-OLD and INDEX-ALL files containing only
2259db6b0a61SColin Percival	# the components we want and without anything marked as "Ignore".
2260db6b0a61SColin Percival	fetch_filter_metadata INDEX-OLD || return 1
2261db6b0a61SColin Percival	fetch_filter_metadata INDEX-ALL || return 1
2262db6b0a61SColin Percival
2263db6b0a61SColin Percival	# Merge the INDEX-OLD and INDEX-ALL files into INDEX-OLD.
2264db6b0a61SColin Percival	sort INDEX-OLD INDEX-ALL > INDEX-OLD.tmp
2265db6b0a61SColin Percival	mv INDEX-OLD.tmp INDEX-OLD
2266db6b0a61SColin Percival	rm INDEX-ALL
2267db6b0a61SColin Percival
2268db6b0a61SColin Percival	# Adjust variables for fetching files from the new release.
2269db6b0a61SColin Percival	OLDRELNUM=${RELNUM}
2270db6b0a61SColin Percival	RELNUM=${TARGETRELEASE}
2271db6b0a61SColin Percival	OLDFETCHDIR=${FETCHDIR}
2272db6b0a61SColin Percival	FETCHDIR=${RELNUM}/${ARCH}
2273db6b0a61SColin Percival
2274db6b0a61SColin Percival	# Try to fetch the NEW metadata index signature ("tag") until we run
2275db6b0a61SColin Percival	# out of available servers; and sanity check the downloaded tag.
2276db6b0a61SColin Percival	while ! fetch_tag; do
2277db6b0a61SColin Percival		fetch_pick_server || return 1
2278db6b0a61SColin Percival	done
2279db6b0a61SColin Percival
2280db6b0a61SColin Percival	# Fetch the new INDEX-ALL.
2281db6b0a61SColin Percival	fetch_metadata INDEX-ALL || return 1
2282db6b0a61SColin Percival
2283db6b0a61SColin Percival	# If StrictComponents is not "yes", COMPONENTS contains an entry
2284db6b0a61SColin Percival	# corresponding to the currently running kernel, and said kernel
2285db6b0a61SColin Percival	# does not exist in the new release, add "kernel/generic" to the
2286db6b0a61SColin Percival	# list of components.
2287db6b0a61SColin Percival	upgrade_guess_new_kernel INDEX-ALL || return 1
2288db6b0a61SColin Percival
2289db6b0a61SColin Percival	# Filter INDEX-ALL to contain only the components we want and without
2290db6b0a61SColin Percival	# anything marked as "Ignore".
2291db6b0a61SColin Percival	fetch_filter_metadata INDEX-ALL || return 1
2292db6b0a61SColin Percival
2293db6b0a61SColin Percival	# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2294db6b0a61SColin Percival	# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2295db6b0a61SColin Percival	upgrade_oldall_to_oldnew INDEX-OLD INDEX-ALL INDEX-NEW
2296db6b0a61SColin Percival
2297db6b0a61SColin Percival	# Translate /boot/${KERNCONF} or /boot/${NKERNCONF} into ${KERNELDIR}
2298db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-NEW ${NKERNCONF}
2299db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
2300db6b0a61SColin Percival
2301db6b0a61SColin Percival	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
2302db6b0a61SColin Percival	# system and generate an INDEX-PRESENT file.
2303db6b0a61SColin Percival	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2304db6b0a61SColin Percival
2305db6b0a61SColin Percival	# Based on ${MERGECHANGES}, generate a file tomerge-old with the
2306db6b0a61SColin Percival	# paths and hashes of old versions of files to merge.
2307db6b0a61SColin Percival	fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT tomerge-old
2308db6b0a61SColin Percival
2309db6b0a61SColin Percival	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
2310db6b0a61SColin Percival	# correspond to lines in INDEX-PRESENT with hashes not appearing
2311db6b0a61SColin Percival	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
2312db6b0a61SColin Percival	# INDEX-PRESENT has type - and there isn't a corresponding entry in
2313db6b0a61SColin Percival	# INDEX-OLD with type -.
2314db6b0a61SColin Percival	fetch_filter_unmodified_notpresent	\
2315db6b0a61SColin Percival	    INDEX-OLD INDEX-PRESENT INDEX-NEW tomerge-old
2316db6b0a61SColin Percival
2317db6b0a61SColin Percival	# For each entry in INDEX-PRESENT of type -, remove any corresponding
2318db6b0a61SColin Percival	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
2319db6b0a61SColin Percival	# of type - from INDEX-PRESENT.
2320db6b0a61SColin Percival	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
2321db6b0a61SColin Percival
2322db6b0a61SColin Percival	# If ${ALLOWDELETE} != "yes", then remove any entries from
2323db6b0a61SColin Percival	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
2324db6b0a61SColin Percival	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
2325db6b0a61SColin Percival
2326db6b0a61SColin Percival	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
2327db6b0a61SColin Percival	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
2328db6b0a61SColin Percival	# replace the corresponding line of INDEX-NEW with one having the
2329db6b0a61SColin Percival	# same metadata as the entry in INDEX-PRESENT.
2330db6b0a61SColin Percival	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
2331db6b0a61SColin Percival
2332db6b0a61SColin Percival	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
2333db6b0a61SColin Percival	# no need to update a file if it isn't changing.
2334db6b0a61SColin Percival	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
2335db6b0a61SColin Percival
2336db6b0a61SColin Percival	# Fetch "clean" files from the old release for merging changes.
2337db6b0a61SColin Percival	fetch_files_premerge tomerge-old
2338db6b0a61SColin Percival
2339db6b0a61SColin Percival	# Prepare to fetch files: Generate a list of the files we need,
2340db6b0a61SColin Percival	# copy the unmodified files we have into /files/, and generate
2341db6b0a61SColin Percival	# a list of patches to download.
2342db6b0a61SColin Percival	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2343db6b0a61SColin Percival
2344db6b0a61SColin Percival	# Fetch patches from to-${RELNUM}/${ARCH}/bp/
2345db6b0a61SColin Percival	PATCHDIR=to-${RELNUM}/${ARCH}/bp
2346db6b0a61SColin Percival	fetch_files || return 1
2347db6b0a61SColin Percival
2348db6b0a61SColin Percival	# Merge configuration file changes.
2349db6b0a61SColin Percival	upgrade_merge tomerge-old INDEX-PRESENT INDEX-NEW || return 1
2350db6b0a61SColin Percival
2351db6b0a61SColin Percival	# Create and populate install manifest directory; and report what
2352db6b0a61SColin Percival	# updates are available.
2353db6b0a61SColin Percival	fetch_create_manifest || return 1
2354db6b0a61SColin Percival
2355db6b0a61SColin Percival	# Leave a note behind to tell the "install" command that the kernel
2356db6b0a61SColin Percival	# needs to be installed before the world.
2357db6b0a61SColin Percival	touch ${BDHASH}-install/kernelfirst
2358db6b0a61SColin Percival}
2359db6b0a61SColin Percival
236048ffe56aSColin Percival# Make sure that all the file hashes mentioned in $@ have corresponding
236148ffe56aSColin Percival# gzipped files stored in /files/.
236248ffe56aSColin Percivalinstall_verify () {
236348ffe56aSColin Percival	# Generate a list of hashes
236448ffe56aSColin Percival	cat $@ |
236548ffe56aSColin Percival	    cut -f 2,7 -d '|' |
236648ffe56aSColin Percival	    grep -E '^f' |
236748ffe56aSColin Percival	    cut -f 2 -d '|' |
236848ffe56aSColin Percival	    sort -u > filelist
236948ffe56aSColin Percival
237048ffe56aSColin Percival	# Make sure all the hashes exist
237148ffe56aSColin Percival	while read HASH; do
237248ffe56aSColin Percival		if ! [ -f files/${HASH}.gz ]; then
237348ffe56aSColin Percival			echo -n "Update files missing -- "
237448ffe56aSColin Percival			echo "this should never happen."
237548ffe56aSColin Percival			echo "Re-run '$0 fetch'."
237648ffe56aSColin Percival			return 1
237748ffe56aSColin Percival		fi
237848ffe56aSColin Percival	done < filelist
237948ffe56aSColin Percival
238048ffe56aSColin Percival	# Clean up
238148ffe56aSColin Percival	rm filelist
238248ffe56aSColin Percival}
238348ffe56aSColin Percival
238448ffe56aSColin Percival# Remove the system immutable flag from files
238548ffe56aSColin Percivalinstall_unschg () {
238648ffe56aSColin Percival	# Generate file list
238748ffe56aSColin Percival	cat $@ |
238848ffe56aSColin Percival	    cut -f 1 -d '|' > filelist
238948ffe56aSColin Percival
239048ffe56aSColin Percival	# Remove flags
239148ffe56aSColin Percival	while read F; do
2392e829ed67SColin Percival		if ! [ -e ${BASEDIR}/${F} ]; then
239348ffe56aSColin Percival			continue
239448ffe56aSColin Percival		fi
239548ffe56aSColin Percival
2396e829ed67SColin Percival		chflags noschg ${BASEDIR}/${F} || return 1
239748ffe56aSColin Percival	done < filelist
239848ffe56aSColin Percival
239948ffe56aSColin Percival	# Clean up
240048ffe56aSColin Percival	rm filelist
240148ffe56aSColin Percival}
240248ffe56aSColin Percival
240348ffe56aSColin Percival# Install new files
240448ffe56aSColin Percivalinstall_from_index () {
240548ffe56aSColin Percival	# First pass: Do everything apart from setting file flags.  We
240648ffe56aSColin Percival	# can't set flags yet, because schg inhibits hard linking.
240748ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
240848ffe56aSColin Percival	    tr '|' ' ' |
240948ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
241048ffe56aSColin Percival		case ${TYPE} in
241148ffe56aSColin Percival		d)
241248ffe56aSColin Percival			# Create a directory
241348ffe56aSColin Percival			install -d -o ${OWNER} -g ${GROUP}		\
241448ffe56aSColin Percival			    -m ${PERM} ${BASEDIR}/${FPATH}
241548ffe56aSColin Percival			;;
241648ffe56aSColin Percival		f)
241748ffe56aSColin Percival			if [ -z "${LINK}" ]; then
241848ffe56aSColin Percival				# Create a file, without setting flags.
241948ffe56aSColin Percival				gunzip < files/${HASH}.gz > ${HASH}
242048ffe56aSColin Percival				install -S -o ${OWNER} -g ${GROUP}	\
242148ffe56aSColin Percival				    -m ${PERM} ${HASH} ${BASEDIR}/${FPATH}
242248ffe56aSColin Percival				rm ${HASH}
242348ffe56aSColin Percival			else
242448ffe56aSColin Percival				# Create a hard link.
2425e829ed67SColin Percival				ln -f ${BASEDIR}/${LINK} ${BASEDIR}/${FPATH}
242648ffe56aSColin Percival			fi
242748ffe56aSColin Percival			;;
242848ffe56aSColin Percival		L)
242948ffe56aSColin Percival			# Create a symlink
243048ffe56aSColin Percival			ln -sfh ${HASH} ${BASEDIR}/${FPATH}
243148ffe56aSColin Percival			;;
243248ffe56aSColin Percival		esac
243348ffe56aSColin Percival	    done
243448ffe56aSColin Percival
243548ffe56aSColin Percival	# Perform a second pass, adding file flags.
243648ffe56aSColin Percival	tr '|' ' ' < $1 |
243748ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
243848ffe56aSColin Percival		if [ ${TYPE} = "f" ] &&
243948ffe56aSColin Percival		    ! [ ${FLAGS} = "0" ]; then
244048ffe56aSColin Percival			chflags ${FLAGS} ${BASEDIR}/${FPATH}
244148ffe56aSColin Percival		fi
244248ffe56aSColin Percival	    done
244348ffe56aSColin Percival}
244448ffe56aSColin Percival
244548ffe56aSColin Percival# Remove files which we want to delete
244648ffe56aSColin Percivalinstall_delete () {
244748ffe56aSColin Percival	# Generate list of new files
244848ffe56aSColin Percival	cut -f 1 -d '|' < $2 |
244948ffe56aSColin Percival	    sort > newfiles
245048ffe56aSColin Percival
245148ffe56aSColin Percival	# Generate subindex of old files we want to nuke
245248ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
245348ffe56aSColin Percival	    join -t '|' -v 1 - newfiles |
2454bce02f98SColin Percival	    sort -r -k 1,1 -t '|' |
245548ffe56aSColin Percival	    cut -f 1,2 -d '|' |
245648ffe56aSColin Percival	    tr '|' ' ' > killfiles
245748ffe56aSColin Percival
245848ffe56aSColin Percival	# Remove the offending bits
245948ffe56aSColin Percival	while read FPATH TYPE; do
246048ffe56aSColin Percival		case ${TYPE} in
246148ffe56aSColin Percival		d)
246248ffe56aSColin Percival			rmdir ${BASEDIR}/${FPATH}
246348ffe56aSColin Percival			;;
246448ffe56aSColin Percival		f)
246548ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
246648ffe56aSColin Percival			;;
246748ffe56aSColin Percival		L)
246848ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
246948ffe56aSColin Percival			;;
247048ffe56aSColin Percival		esac
247148ffe56aSColin Percival	done < killfiles
247248ffe56aSColin Percival
247348ffe56aSColin Percival	# Clean up
247448ffe56aSColin Percival	rm newfiles killfiles
247548ffe56aSColin Percival}
247648ffe56aSColin Percival
2477db6b0a61SColin Percival# Install new files, delete old files, and update linker.hints
2478db6b0a61SColin Percivalinstall_files () {
2479db6b0a61SColin Percival	# If we haven't already dealt with the kernel, deal with it.
2480db6b0a61SColin Percival	if ! [ -f $1/kerneldone ]; then
2481db6b0a61SColin Percival		grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
2482db6b0a61SColin Percival		grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
2483db6b0a61SColin Percival
2484db6b0a61SColin Percival		# Install new files
2485db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2486db6b0a61SColin Percival
2487db6b0a61SColin Percival		# Remove files which need to be deleted
2488db6b0a61SColin Percival		install_delete INDEX-OLD INDEX-NEW || return 1
2489db6b0a61SColin Percival
2490db6b0a61SColin Percival		# Update linker.hints if necessary
2491db6b0a61SColin Percival		if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2492db6b0a61SColin Percival			kldxref -R /boot/ 2>/dev/null
249348ffe56aSColin Percival		fi
2494db6b0a61SColin Percival
2495db6b0a61SColin Percival		# We've finished updating the kernel.
2496db6b0a61SColin Percival		touch $1/kerneldone
2497db6b0a61SColin Percival
2498db6b0a61SColin Percival		# Do we need to ask for a reboot now?
2499db6b0a61SColin Percival		if [ -f $1/kernelfirst ] &&
2500db6b0a61SColin Percival		    [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2501db6b0a61SColin Percival			cat <<-EOF
2502db6b0a61SColin Percival
2503db6b0a61SColin PercivalKernel updates have been installed.  Please reboot and run
2504db6b0a61SColin Percival"$0 install" again to finish installing updates.
2505db6b0a61SColin Percival			EOF
2506db6b0a61SColin Percival			exit 0
2507db6b0a61SColin Percival		fi
2508db6b0a61SColin Percival	fi
2509db6b0a61SColin Percival
2510db6b0a61SColin Percival	# If we haven't already dealt with the world, deal with it.
2511db6b0a61SColin Percival	if ! [ -f $1/worlddone ]; then
2512db6b0a61SColin Percival		# Install new shared libraries next
2513db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2514db6b0a61SColin Percival		    grep -E '/lib/.*\.so\.[0-9]+' > INDEX-NEW
2515db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2516db6b0a61SColin Percival
2517db6b0a61SColin Percival		# Deal with everything else
2518db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-OLD |
2519db6b0a61SColin Percival		    grep -vE '/lib/.*\.so\.[0-9]+' > INDEX-OLD
2520db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2521db6b0a61SColin Percival		    grep -vE '/lib/.*\.so\.[0-9]+' > INDEX-NEW
2522db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2523db6b0a61SColin Percival		install_delete INDEX-OLD INDEX-NEW || return 1
2524db6b0a61SColin Percival
2525db6b0a61SColin Percival		# Rebuild /etc/spwd.db and /etc/pwd.db if necessary.
2526db6b0a61SColin Percival		if [ /etc/master.passwd -nt /etc/spwd.db ] ||
2527db6b0a61SColin Percival		    [ /etc/master.passwd -nt /etc/pwd.db ]; then
2528db6b0a61SColin Percival			pwd_mkdb /etc/master.passwd
2529db6b0a61SColin Percival		fi
2530db6b0a61SColin Percival
2531db6b0a61SColin Percival		# Rebuild /etc/login.conf.db if necessary.
2532db6b0a61SColin Percival		if [ /etc/login.conf -nt /etc/login.conf.db ]; then
2533db6b0a61SColin Percival			cap_mkdb /etc/login.conf
2534db6b0a61SColin Percival		fi
2535db6b0a61SColin Percival
2536db6b0a61SColin Percival		# We've finished installing the world and deleting old files
2537db6b0a61SColin Percival		# which are not shared libraries.
2538db6b0a61SColin Percival		touch $1/worlddone
2539db6b0a61SColin Percival
2540db6b0a61SColin Percival		# Do we need to ask the user to portupgrade now?
2541db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2542db6b0a61SColin Percival		    grep -E '/lib/.*\.so\.[0-9]+' |
2543db6b0a61SColin Percival		    cut -f 1 -d '|' |
2544db6b0a61SColin Percival		    sort > newfiles
2545db6b0a61SColin Percival		if grep -vE '^/boot/' $1/INDEX-OLD |
2546db6b0a61SColin Percival		    grep -E '/lib/.*\.so\.[0-9]+' |
2547db6b0a61SColin Percival		    cut -f 1 -d '|' |
2548db6b0a61SColin Percival		    sort |
2549db6b0a61SColin Percival		    join -v 1 - newfiles |
2550db6b0a61SColin Percival		    grep -q .; then
2551db6b0a61SColin Percival			cat <<-EOF
2552db6b0a61SColin Percival
2553db6b0a61SColin PercivalCompleting this upgrade requires removing old shared object files.
2554db6b0a61SColin PercivalPlease rebuild all installed 3rd party software (e.g., programs
2555db6b0a61SColin Percivalinstalled from the ports tree) and then run "$0 install"
2556db6b0a61SColin Percivalagain to finish installing updates.
2557db6b0a61SColin Percival			EOF
2558db6b0a61SColin Percival			rm newfiles
2559db6b0a61SColin Percival			exit 0
2560db6b0a61SColin Percival		fi
2561db6b0a61SColin Percival		rm newfiles
2562db6b0a61SColin Percival	fi
2563db6b0a61SColin Percival
2564db6b0a61SColin Percival	# Remove old shared libraries
2565db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2566db6b0a61SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+' > INDEX-NEW
2567db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2568db6b0a61SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+' > INDEX-OLD
2569db6b0a61SColin Percival	install_delete INDEX-OLD INDEX-NEW || return 1
2570db6b0a61SColin Percival
2571db6b0a61SColin Percival	# Remove temporary files
2572db6b0a61SColin Percival	rm INDEX-OLD INDEX-NEW
257348ffe56aSColin Percival}
257448ffe56aSColin Percival
257548ffe56aSColin Percival# Rearrange bits to allow the installed updates to be rolled back
257648ffe56aSColin Percivalinstall_setup_rollback () {
2577db6b0a61SColin Percival	# Remove the "reboot after installing kernel", "kernel updated", and
2578db6b0a61SColin Percival	# "finished installing the world" flags if present -- they are
2579db6b0a61SColin Percival	# irrelevant when rolling back updates.
2580db6b0a61SColin Percival	if [ -f ${BDHASH}-install/kernelfirst ]; then
2581db6b0a61SColin Percival		rm ${BDHASH}-install/kernelfirst
2582db6b0a61SColin Percival		rm ${BDHASH}-install/kerneldone
2583db6b0a61SColin Percival	fi
2584db6b0a61SColin Percival	if [ -f ${BDHASH}-install/worlddone ]; then
2585db6b0a61SColin Percival		rm ${BDHASH}-install/worlddone
2586db6b0a61SColin Percival	fi
2587db6b0a61SColin Percival
258848ffe56aSColin Percival	if [ -L ${BDHASH}-rollback ]; then
258948ffe56aSColin Percival		mv ${BDHASH}-rollback ${BDHASH}-install/rollback
259048ffe56aSColin Percival	fi
259148ffe56aSColin Percival
259248ffe56aSColin Percival	mv ${BDHASH}-install ${BDHASH}-rollback
259348ffe56aSColin Percival}
259448ffe56aSColin Percival
259548ffe56aSColin Percival# Actually install updates
259648ffe56aSColin Percivalinstall_run () {
259748ffe56aSColin Percival	echo -n "Installing updates..."
259848ffe56aSColin Percival
259948ffe56aSColin Percival	# Make sure we have all the files we should have
260048ffe56aSColin Percival	install_verify ${BDHASH}-install/INDEX-OLD	\
260148ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
260248ffe56aSColin Percival
260348ffe56aSColin Percival	# Remove system immutable flag from files
260448ffe56aSColin Percival	install_unschg ${BDHASH}-install/INDEX-OLD	\
260548ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
260648ffe56aSColin Percival
2607db6b0a61SColin Percival	# Install new files, delete old files, and update linker.hints
2608db6b0a61SColin Percival	install_files ${BDHASH}-install || return 1
260948ffe56aSColin Percival
261048ffe56aSColin Percival	# Rearrange bits to allow the installed updates to be rolled back
261148ffe56aSColin Percival	install_setup_rollback
261248ffe56aSColin Percival
261348ffe56aSColin Percival	echo " done."
261448ffe56aSColin Percival}
261548ffe56aSColin Percival
261648ffe56aSColin Percival# Rearrange bits to allow the previous set of updates to be rolled back next.
261748ffe56aSColin Percivalrollback_setup_rollback () {
261848ffe56aSColin Percival	if [ -L ${BDHASH}-rollback/rollback ]; then
261948ffe56aSColin Percival		mv ${BDHASH}-rollback/rollback rollback-tmp
262048ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
262148ffe56aSColin Percival		rm ${BDHASH}-rollback
262248ffe56aSColin Percival		mv rollback-tmp ${BDHASH}-rollback
262348ffe56aSColin Percival	else
262448ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
262548ffe56aSColin Percival		rm ${BDHASH}-rollback
262648ffe56aSColin Percival	fi
262748ffe56aSColin Percival}
262848ffe56aSColin Percival
2629db6b0a61SColin Percival# Install old files, delete new files, and update linker.hints
2630db6b0a61SColin Percivalrollback_files () {
26311ec4fb3aSColin Percival	# Install old shared library files which don't have the same path as
26321ec4fb3aSColin Percival	# a new shared library file.
26331ec4fb3aSColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
26341ec4fb3aSColin Percival	    grep -E '/lib/.*\.so\.[0-9]+' |
26351ec4fb3aSColin Percival	    cut -f 1 -d '|' |
26361ec4fb3aSColin Percival	    sort > INDEX-NEW.libs.flist
2637db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
26381ec4fb3aSColin Percival	    grep -E '/lib/.*\.so\.[0-9]+' |
26391ec4fb3aSColin Percival	    sort -k 1,1 -t '|' - |
26401ec4fb3aSColin Percival	    join -t '|' -v 1 - INDEX-NEW.libs.flist > INDEX-OLD
2641db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
2642db6b0a61SColin Percival
2643db6b0a61SColin Percival	# Deal with files which are neither kernel nor shared library
2644db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2645db6b0a61SColin Percival	    grep -vE '/lib/.*\.so\.[0-9]+' > INDEX-OLD
2646db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2647db6b0a61SColin Percival	    grep -vE '/lib/.*\.so\.[0-9]+' > INDEX-NEW
2648db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
2649db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
2650db6b0a61SColin Percival
26511ec4fb3aSColin Percival	# Install any old shared library files which we didn't install above.
26521ec4fb3aSColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
26531ec4fb3aSColin Percival	    grep -E '/lib/.*\.so\.[0-9]+' |
26541ec4fb3aSColin Percival	    sort -k 1,1 -t '|' - |
26551ec4fb3aSColin Percival	    join -t '|' - INDEX-NEW.libs.flist > INDEX-OLD
26561ec4fb3aSColin Percival	install_from_index INDEX-OLD || return 1
26571ec4fb3aSColin Percival
2658db6b0a61SColin Percival	# Delete unneeded shared library files
2659db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2660db6b0a61SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+' > INDEX-OLD
2661db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2662db6b0a61SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+' > INDEX-NEW
2663db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
2664db6b0a61SColin Percival
2665db6b0a61SColin Percival	# Deal with kernel files
2666db6b0a61SColin Percival	grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
2667db6b0a61SColin Percival	grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
2668db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
2669db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
2670db6b0a61SColin Percival	if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2671db6b0a61SColin Percival		kldxref -R /boot/ 2>/dev/null
2672db6b0a61SColin Percival	fi
2673db6b0a61SColin Percival
2674db6b0a61SColin Percival	# Remove temporary files
2675db6b0a61SColin Percival	rm INDEX-OLD INDEX-NEW
2676db6b0a61SColin Percival}
2677db6b0a61SColin Percival
267848ffe56aSColin Percival# Actually rollback updates
267948ffe56aSColin Percivalrollback_run () {
268048ffe56aSColin Percival	echo -n "Uninstalling updates..."
268148ffe56aSColin Percival
268248ffe56aSColin Percival	# If there are updates waiting to be installed, remove them; we
268348ffe56aSColin Percival	# want the user to re-run 'fetch' after rolling back updates.
268448ffe56aSColin Percival	if [ -L ${BDHASH}-install ]; then
268548ffe56aSColin Percival		rm -r ${BDHASH}-install/
268648ffe56aSColin Percival		rm ${BDHASH}-install
268748ffe56aSColin Percival	fi
268848ffe56aSColin Percival
268948ffe56aSColin Percival	# Make sure we have all the files we should have
269048ffe56aSColin Percival	install_verify ${BDHASH}-rollback/INDEX-NEW	\
269148ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
269248ffe56aSColin Percival
269348ffe56aSColin Percival	# Remove system immutable flag from files
269448ffe56aSColin Percival	install_unschg ${BDHASH}-rollback/INDEX-NEW	\
269548ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
269648ffe56aSColin Percival
2697db6b0a61SColin Percival	# Install old files, delete new files, and update linker.hints
2698db6b0a61SColin Percival	rollback_files ${BDHASH}-rollback || return 1
269948ffe56aSColin Percival
270048ffe56aSColin Percival	# Remove the rollback directory and the symlink pointing to it; and
270148ffe56aSColin Percival	# rearrange bits to allow the previous set of updates to be rolled
270248ffe56aSColin Percival	# back next.
270348ffe56aSColin Percival	rollback_setup_rollback
270448ffe56aSColin Percival
270548ffe56aSColin Percival	echo " done."
270648ffe56aSColin Percival}
270748ffe56aSColin Percival
270848ffe56aSColin Percival#### Main functions -- call parameter-handling and core functions
270948ffe56aSColin Percival
271048ffe56aSColin Percival# Using the command line, configuration file, and defaults,
271148ffe56aSColin Percival# set all the parameters which are needed later.
271248ffe56aSColin Percivalget_params () {
271348ffe56aSColin Percival	init_params
271448ffe56aSColin Percival	parse_cmdline $@
271548ffe56aSColin Percival	parse_conffile
271648ffe56aSColin Percival	default_params
271748ffe56aSColin Percival}
271848ffe56aSColin Percival
271948ffe56aSColin Percival# Fetch command.  Make sure that we're being called
272048ffe56aSColin Percival# interactively, then run fetch_check_params and fetch_run
272148ffe56aSColin Percivalcmd_fetch () {
272248ffe56aSColin Percival	if [ ! -t 0 ]; then
272348ffe56aSColin Percival		echo -n "`basename $0` fetch should not "
272448ffe56aSColin Percival		echo "be run non-interactively."
272548ffe56aSColin Percival		echo "Run `basename $0` cron instead."
272648ffe56aSColin Percival		exit 1
272748ffe56aSColin Percival	fi
272848ffe56aSColin Percival	fetch_check_params
272948ffe56aSColin Percival	fetch_run || exit 1
273048ffe56aSColin Percival}
273148ffe56aSColin Percival
273248ffe56aSColin Percival# Cron command.  Make sure the parameters are sensible; wait
273348ffe56aSColin Percival# rand(3600) seconds; then fetch updates.  While fetching updates,
273448ffe56aSColin Percival# send output to a temporary file; only print that file if the
273548ffe56aSColin Percival# fetching failed.
273648ffe56aSColin Percivalcmd_cron () {
273748ffe56aSColin Percival	fetch_check_params
273848ffe56aSColin Percival	sleep `jot -r 1 0 3600`
273948ffe56aSColin Percival
274048ffe56aSColin Percival	TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1
274148ffe56aSColin Percival	if ! fetch_run >> ${TMPFILE} ||
274248ffe56aSColin Percival	    ! grep -q "No updates needed" ${TMPFILE} ||
274348ffe56aSColin Percival	    [ ${VERBOSELEVEL} = "debug" ]; then
274448ffe56aSColin Percival		mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE}
274548ffe56aSColin Percival	fi
274648ffe56aSColin Percival
274748ffe56aSColin Percival	rm ${TMPFILE}
274848ffe56aSColin Percival}
274948ffe56aSColin Percival
2750db6b0a61SColin Percival# Fetch files for upgrading to a new release.
2751db6b0a61SColin Percivalcmd_upgrade () {
2752db6b0a61SColin Percival	upgrade_check_params
2753db6b0a61SColin Percival	upgrade_run || exit 1
2754db6b0a61SColin Percival}
2755db6b0a61SColin Percival
275648ffe56aSColin Percival# Install downloaded updates.
275748ffe56aSColin Percivalcmd_install () {
275848ffe56aSColin Percival	install_check_params
275948ffe56aSColin Percival	install_run || exit 1
276048ffe56aSColin Percival}
276148ffe56aSColin Percival
276248ffe56aSColin Percival# Rollback most recently installed updates.
276348ffe56aSColin Percivalcmd_rollback () {
276448ffe56aSColin Percival	rollback_check_params
276548ffe56aSColin Percival	rollback_run || exit 1
276648ffe56aSColin Percival}
276748ffe56aSColin Percival
276848ffe56aSColin Percival#### Entry point
276948ffe56aSColin Percival
277048ffe56aSColin Percival# Make sure we find utilities from the base system
277148ffe56aSColin Percivalexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
277248ffe56aSColin Percival
2773f2890dbdSColin Percival# Set LC_ALL in order to avoid problems with character ranges like [A-Z].
2774f2890dbdSColin Percivalexport LC_ALL=C
2775f2890dbdSColin Percival
277648ffe56aSColin Percivalget_params $@
277748ffe56aSColin Percivalfor COMMAND in ${COMMANDS}; do
277848ffe56aSColin Percival	cmd_${COMMAND}
277948ffe56aSColin Percivaldone
2780