xref: /freebsd/usr.sbin/freebsd-update/freebsd-update.sh (revision fc24ba59eef56147823b2af4c3af5a71946d007d)
148ffe56aSColin Percival#!/bin/sh
248ffe56aSColin Percival
348ffe56aSColin Percival#-
41de7b4b8SPedro F. Giffuni# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
51de7b4b8SPedro F. Giffuni#
62328d598SColin Percival# Copyright 2004-2007 Colin Percival
748ffe56aSColin Percival# All rights reserved
848ffe56aSColin Percival#
948ffe56aSColin Percival# Redistribution and use in source and binary forms, with or without
1048ffe56aSColin Percival# modification, are permitted providing that the following conditions
1148ffe56aSColin Percival# are met:
1248ffe56aSColin Percival# 1. Redistributions of source code must retain the above copyright
1348ffe56aSColin Percival#    notice, this list of conditions and the following disclaimer.
1448ffe56aSColin Percival# 2. Redistributions in binary form must reproduce the above copyright
1548ffe56aSColin Percival#    notice, this list of conditions and the following disclaimer in the
1648ffe56aSColin Percival#    documentation and/or other materials provided with the distribution.
1748ffe56aSColin Percival#
1848ffe56aSColin Percival# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1948ffe56aSColin Percival# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2048ffe56aSColin Percival# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2148ffe56aSColin Percival# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
2248ffe56aSColin Percival# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2348ffe56aSColin Percival# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2448ffe56aSColin Percival# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2548ffe56aSColin Percival# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2648ffe56aSColin Percival# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
2748ffe56aSColin Percival# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2848ffe56aSColin Percival# POSSIBILITY OF SUCH DAMAGE.
2948ffe56aSColin Percival
3048ffe56aSColin Percival# $FreeBSD$
3148ffe56aSColin Percival
3248ffe56aSColin Percival#### Usage function -- called from command-line handling code.
3348ffe56aSColin Percival
3448ffe56aSColin Percival# Usage instructions.  Options not listed:
3548ffe56aSColin Percival# --debug	-- don't filter output from utilities
3648ffe56aSColin Percival# --no-stats	-- don't show progress statistics while fetching files
3748ffe56aSColin Percivalusage () {
3848ffe56aSColin Percival	cat <<EOF
3948ffe56aSColin Percivalusage: `basename $0` [options] command ... [path]
4048ffe56aSColin Percival
4148ffe56aSColin PercivalOptions:
4248ffe56aSColin Percival  -b basedir   -- Operate on a system mounted at basedir
4348ffe56aSColin Percival                  (default: /)
4448ffe56aSColin Percival  -d workdir   -- Store working files in workdir
4548ffe56aSColin Percival                  (default: /var/db/freebsd-update/)
4648ffe56aSColin Percival  -f conffile  -- Read configuration options from conffile
4748ffe56aSColin Percival                  (default: /etc/freebsd-update.conf)
488935f242SAllan Jude  -F           -- Force a fetch operation to proceed
4948ffe56aSColin Percival  -k KEY       -- Trust an RSA key with SHA256 hash of KEY
50dd917c79SGleb Smirnoff  -r release   -- Target for upgrade (e.g., 11.1-RELEASE)
5148ffe56aSColin Percival  -s server    -- Server from which to fetch updates
5248ffe56aSColin Percival                  (default: update.FreeBSD.org)
5348ffe56aSColin Percival  -t address   -- Mail output of cron command, if any, to address
5448ffe56aSColin Percival                  (default: root)
558935f242SAllan Jude  --not-running-from-cron
568935f242SAllan Jude               -- Run without a tty, for use by automated tools
57b39ce43eSColin Percival  --currently-running release
58b39ce43eSColin Percival               -- Update as if currently running this release
5948ffe56aSColin PercivalCommands:
6048ffe56aSColin Percival  fetch        -- Fetch updates from server
6148ffe56aSColin Percival  cron         -- Sleep rand(3600) seconds, fetch updates, and send an
6248ffe56aSColin Percival                  email if updates were found
63db6b0a61SColin Percival  upgrade      -- Fetch upgrades to FreeBSD version specified via -r option
64db6b0a61SColin Percival  install      -- Install downloaded updates or upgrades
6548ffe56aSColin Percival  rollback     -- Uninstall most recently installed updates
6608e23beeSColin Percival  IDS          -- Compare the system against an index of "known good" files.
6748ffe56aSColin PercivalEOF
6848ffe56aSColin Percival	exit 0
6948ffe56aSColin Percival}
7048ffe56aSColin Percival
7148ffe56aSColin Percival#### Configuration processing functions
7248ffe56aSColin Percival
7348ffe56aSColin Percival#-
7448ffe56aSColin Percival# Configuration options are set in the following order of priority:
7548ffe56aSColin Percival# 1. Command line options
7648ffe56aSColin Percival# 2. Configuration file options
7748ffe56aSColin Percival# 3. Default options
7848ffe56aSColin Percival# In addition, certain options (e.g., IgnorePaths) can be specified multiple
7948ffe56aSColin Percival# times and (as long as these are all in the same place, e.g., inside the
8048ffe56aSColin Percival# configuration file) they will accumulate.  Finally, because the path to the
8148ffe56aSColin Percival# configuration file can be specified at the command line, the entire command
8248ffe56aSColin Percival# line must be processed before we start reading the configuration file.
8348ffe56aSColin Percival#
8448ffe56aSColin Percival# Sound like a mess?  It is.  Here's how we handle this:
8548ffe56aSColin Percival# 1. Initialize CONFFILE and all the options to "".
8648ffe56aSColin Percival# 2. Process the command line.  Throw an error if a non-accumulating option
8748ffe56aSColin Percival#    is specified twice.
8848ffe56aSColin Percival# 3. If CONFFILE is "", set CONFFILE to /etc/freebsd-update.conf .
8948ffe56aSColin Percival# 4. For all the configuration options X, set X_saved to X.
9048ffe56aSColin Percival# 5. Initialize all the options to "".
9148ffe56aSColin Percival# 6. Read CONFFILE line by line, parsing options.
9248ffe56aSColin Percival# 7. For each configuration option X, set X to X_saved iff X_saved is not "".
9348ffe56aSColin Percival# 8. Repeat steps 4-7, except setting options to their default values at (6).
9448ffe56aSColin Percival
9548ffe56aSColin PercivalCONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE
9648ffe56aSColin Percival    KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED
9708e23beeSColin Percival    BASEDIR VERBOSELEVEL TARGETRELEASE STRICTCOMPONENTS MERGECHANGES
9823d827efSSimon L. B. Nielsen    IDSIGNOREPATHS BACKUPKERNEL BACKUPKERNELDIR BACKUPKERNELSYMBOLFILES"
9948ffe56aSColin Percival
10048ffe56aSColin Percival# Set all the configuration options to "".
10148ffe56aSColin Percivalnullconfig () {
10248ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
10348ffe56aSColin Percival		eval ${X}=""
10448ffe56aSColin Percival	done
10548ffe56aSColin Percival}
10648ffe56aSColin Percival
10748ffe56aSColin Percival# For each configuration option X, set X_saved to X.
10848ffe56aSColin Percivalsaveconfig () {
10948ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
11048ffe56aSColin Percival		eval ${X}_saved=\$${X}
11148ffe56aSColin Percival	done
11248ffe56aSColin Percival}
11348ffe56aSColin Percival
11448ffe56aSColin Percival# For each configuration option X, set X to X_saved if X_saved is not "".
11548ffe56aSColin Percivalmergeconfig () {
11648ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
11748ffe56aSColin Percival		eval _=\$${X}_saved
11848ffe56aSColin Percival		if ! [ -z "${_}" ]; then
11948ffe56aSColin Percival			eval ${X}=\$${X}_saved
12048ffe56aSColin Percival		fi
12148ffe56aSColin Percival	done
12248ffe56aSColin Percival}
12348ffe56aSColin Percival
12448ffe56aSColin Percival# Set the trusted keyprint.
12548ffe56aSColin Percivalconfig_KeyPrint () {
12648ffe56aSColin Percival	if [ -z ${KEYPRINT} ]; then
12748ffe56aSColin Percival		KEYPRINT=$1
12848ffe56aSColin Percival	else
12948ffe56aSColin Percival		return 1
13048ffe56aSColin Percival	fi
13148ffe56aSColin Percival}
13248ffe56aSColin Percival
13348ffe56aSColin Percival# Set the working directory.
13448ffe56aSColin Percivalconfig_WorkDir () {
13548ffe56aSColin Percival	if [ -z ${WORKDIR} ]; then
13648ffe56aSColin Percival		WORKDIR=$1
13748ffe56aSColin Percival	else
13848ffe56aSColin Percival		return 1
13948ffe56aSColin Percival	fi
14048ffe56aSColin Percival}
14148ffe56aSColin Percival
14248ffe56aSColin Percival# Set the name of the server (pool) from which to fetch updates
14348ffe56aSColin Percivalconfig_ServerName () {
14448ffe56aSColin Percival	if [ -z ${SERVERNAME} ]; then
14548ffe56aSColin Percival		SERVERNAME=$1
14648ffe56aSColin Percival	else
14748ffe56aSColin Percival		return 1
14848ffe56aSColin Percival	fi
14948ffe56aSColin Percival}
15048ffe56aSColin Percival
15148ffe56aSColin Percival# Set the address to which 'cron' output will be mailed.
15248ffe56aSColin Percivalconfig_MailTo () {
15348ffe56aSColin Percival	if [ -z ${MAILTO} ]; then
15448ffe56aSColin Percival		MAILTO=$1
15548ffe56aSColin Percival	else
15648ffe56aSColin Percival		return 1
15748ffe56aSColin Percival	fi
15848ffe56aSColin Percival}
15948ffe56aSColin Percival
16048ffe56aSColin Percival# Set whether FreeBSD Update is allowed to add files (or directories, or
16148ffe56aSColin Percival# symlinks) which did not previously exist.
16248ffe56aSColin Percivalconfig_AllowAdd () {
16348ffe56aSColin Percival	if [ -z ${ALLOWADD} ]; then
16448ffe56aSColin Percival		case $1 in
16548ffe56aSColin Percival		[Yy][Ee][Ss])
16648ffe56aSColin Percival			ALLOWADD=yes
16748ffe56aSColin Percival			;;
16848ffe56aSColin Percival		[Nn][Oo])
16948ffe56aSColin Percival			ALLOWADD=no
17048ffe56aSColin Percival			;;
17148ffe56aSColin Percival		*)
17248ffe56aSColin Percival			return 1
17348ffe56aSColin Percival			;;
17448ffe56aSColin Percival		esac
17548ffe56aSColin Percival	else
17648ffe56aSColin Percival		return 1
17748ffe56aSColin Percival	fi
17848ffe56aSColin Percival}
17948ffe56aSColin Percival
18048ffe56aSColin Percival# Set whether FreeBSD Update is allowed to remove files/directories/symlinks.
18148ffe56aSColin Percivalconfig_AllowDelete () {
18248ffe56aSColin Percival	if [ -z ${ALLOWDELETE} ]; then
18348ffe56aSColin Percival		case $1 in
18448ffe56aSColin Percival		[Yy][Ee][Ss])
18548ffe56aSColin Percival			ALLOWDELETE=yes
18648ffe56aSColin Percival			;;
18748ffe56aSColin Percival		[Nn][Oo])
18848ffe56aSColin Percival			ALLOWDELETE=no
18948ffe56aSColin Percival			;;
19048ffe56aSColin Percival		*)
19148ffe56aSColin Percival			return 1
19248ffe56aSColin Percival			;;
19348ffe56aSColin Percival		esac
19448ffe56aSColin Percival	else
19548ffe56aSColin Percival		return 1
19648ffe56aSColin Percival	fi
19748ffe56aSColin Percival}
19848ffe56aSColin Percival
19948ffe56aSColin Percival# Set whether FreeBSD Update should keep existing inode ownership,
20048ffe56aSColin Percival# permissions, and flags, in the event that they have been modified locally
20148ffe56aSColin Percival# after the release.
20248ffe56aSColin Percivalconfig_KeepModifiedMetadata () {
20348ffe56aSColin Percival	if [ -z ${KEEPMODIFIEDMETADATA} ]; then
20448ffe56aSColin Percival		case $1 in
20548ffe56aSColin Percival		[Yy][Ee][Ss])
20648ffe56aSColin Percival			KEEPMODIFIEDMETADATA=yes
20748ffe56aSColin Percival			;;
20848ffe56aSColin Percival		[Nn][Oo])
20948ffe56aSColin Percival			KEEPMODIFIEDMETADATA=no
21048ffe56aSColin Percival			;;
21148ffe56aSColin Percival		*)
21248ffe56aSColin Percival			return 1
21348ffe56aSColin Percival			;;
21448ffe56aSColin Percival		esac
21548ffe56aSColin Percival	else
21648ffe56aSColin Percival		return 1
21748ffe56aSColin Percival	fi
21848ffe56aSColin Percival}
21948ffe56aSColin Percival
22048ffe56aSColin Percival# Add to the list of components which should be kept updated.
22148ffe56aSColin Percivalconfig_Components () {
22248ffe56aSColin Percival	for C in $@; do
2235a74378cSXin LI		if [ "$C" = "src" ]; then
224cfd9be9cSEd Maste			if [ -e "${BASEDIR}/usr/src/COPYRIGHT" ]; then
22548ffe56aSColin Percival				COMPONENTS="${COMPONENTS} ${C}"
2265a74378cSXin LI			else
2275a74378cSXin LI				echo "src component not installed, skipped"
2285a74378cSXin LI			fi
2295a74378cSXin LI		else
2305a74378cSXin LI			COMPONENTS="${COMPONENTS} ${C}"
2315a74378cSXin LI		fi
23248ffe56aSColin Percival	done
23348ffe56aSColin Percival}
23448ffe56aSColin Percival
23548ffe56aSColin Percival# Add to the list of paths under which updates will be ignored.
23648ffe56aSColin Percivalconfig_IgnorePaths () {
23748ffe56aSColin Percival	for C in $@; do
23848ffe56aSColin Percival		IGNOREPATHS="${IGNOREPATHS} ${C}"
23948ffe56aSColin Percival	done
24048ffe56aSColin Percival}
24148ffe56aSColin Percival
24208e23beeSColin Percival# Add to the list of paths which IDS should ignore.
24308e23beeSColin Percivalconfig_IDSIgnorePaths () {
24408e23beeSColin Percival	for C in $@; do
24508e23beeSColin Percival		IDSIGNOREPATHS="${IDSIGNOREPATHS} ${C}"
24608e23beeSColin Percival	done
24708e23beeSColin Percival}
24808e23beeSColin Percival
24948ffe56aSColin Percival# Add to the list of paths within which updates will be performed only if the
25048ffe56aSColin Percival# file on disk has not been modified locally.
25148ffe56aSColin Percivalconfig_UpdateIfUnmodified () {
25248ffe56aSColin Percival	for C in $@; do
25348ffe56aSColin Percival		UPDATEIFUNMODIFIED="${UPDATEIFUNMODIFIED} ${C}"
25448ffe56aSColin Percival	done
25548ffe56aSColin Percival}
25648ffe56aSColin Percival
257db6b0a61SColin Percival# Add to the list of paths within which updates to text files will be merged
258db6b0a61SColin Percival# instead of overwritten.
259db6b0a61SColin Percivalconfig_MergeChanges () {
260db6b0a61SColin Percival	for C in $@; do
261db6b0a61SColin Percival		MERGECHANGES="${MERGECHANGES} ${C}"
262db6b0a61SColin Percival	done
263db6b0a61SColin Percival}
264db6b0a61SColin Percival
26548ffe56aSColin Percival# Work on a FreeBSD installation mounted under $1
26648ffe56aSColin Percivalconfig_BaseDir () {
26748ffe56aSColin Percival	if [ -z ${BASEDIR} ]; then
26848ffe56aSColin Percival		BASEDIR=$1
26948ffe56aSColin Percival	else
27048ffe56aSColin Percival		return 1
27148ffe56aSColin Percival	fi
27248ffe56aSColin Percival}
27348ffe56aSColin Percival
274db6b0a61SColin Percival# When fetching upgrades, should we assume the user wants exactly the
275db6b0a61SColin Percival# components listed in COMPONENTS, rather than trying to guess based on
276db6b0a61SColin Percival# what's currently installed?
277db6b0a61SColin Percivalconfig_StrictComponents () {
278db6b0a61SColin Percival	if [ -z ${STRICTCOMPONENTS} ]; then
279db6b0a61SColin Percival		case $1 in
280db6b0a61SColin Percival		[Yy][Ee][Ss])
281db6b0a61SColin Percival			STRICTCOMPONENTS=yes
282db6b0a61SColin Percival			;;
283db6b0a61SColin Percival		[Nn][Oo])
284db6b0a61SColin Percival			STRICTCOMPONENTS=no
285db6b0a61SColin Percival			;;
286db6b0a61SColin Percival		*)
287db6b0a61SColin Percival			return 1
288db6b0a61SColin Percival			;;
289db6b0a61SColin Percival		esac
290db6b0a61SColin Percival	else
291db6b0a61SColin Percival		return 1
292db6b0a61SColin Percival	fi
293db6b0a61SColin Percival}
294db6b0a61SColin Percival
295db6b0a61SColin Percival# Upgrade to FreeBSD $1
296db6b0a61SColin Percivalconfig_TargetRelease () {
297db6b0a61SColin Percival	if [ -z ${TARGETRELEASE} ]; then
298db6b0a61SColin Percival		TARGETRELEASE=$1
299db6b0a61SColin Percival	else
300db6b0a61SColin Percival		return 1
301db6b0a61SColin Percival	fi
302d23dc1eeSColin Percival	if echo ${TARGETRELEASE} | grep -qE '^[0-9.]+$'; then
303d23dc1eeSColin Percival		TARGETRELEASE="${TARGETRELEASE}-RELEASE"
304d23dc1eeSColin Percival	fi
305db6b0a61SColin Percival}
306db6b0a61SColin Percival
3070d5c5243SEd Maste# Pretend current release is FreeBSD $1
3080d5c5243SEd Masteconfig_SourceRelease () {
3090d5c5243SEd Maste	UNAME_r=$1
3100d5c5243SEd Maste	if echo ${UNAME_r} | grep -qE '^[0-9.]+$'; then
3110d5c5243SEd Maste		UNAME_r="${UNAME_r}-RELEASE"
3120d5c5243SEd Maste	fi
313b958d4b2SEd Maste	export UNAME_r
3140d5c5243SEd Maste}
3150d5c5243SEd Maste
31648ffe56aSColin Percival# Define what happens to output of utilities
31748ffe56aSColin Percivalconfig_VerboseLevel () {
31848ffe56aSColin Percival	if [ -z ${VERBOSELEVEL} ]; then
31948ffe56aSColin Percival		case $1 in
32048ffe56aSColin Percival		[Dd][Ee][Bb][Uu][Gg])
32148ffe56aSColin Percival			VERBOSELEVEL=debug
32248ffe56aSColin Percival			;;
32348ffe56aSColin Percival		[Nn][Oo][Ss][Tt][Aa][Tt][Ss])
32448ffe56aSColin Percival			VERBOSELEVEL=nostats
32548ffe56aSColin Percival			;;
32648ffe56aSColin Percival		[Ss][Tt][Aa][Tt][Ss])
32748ffe56aSColin Percival			VERBOSELEVEL=stats
32848ffe56aSColin Percival			;;
32948ffe56aSColin Percival		*)
33048ffe56aSColin Percival			return 1
33148ffe56aSColin Percival			;;
33248ffe56aSColin Percival		esac
33348ffe56aSColin Percival	else
33448ffe56aSColin Percival		return 1
33548ffe56aSColin Percival	fi
33648ffe56aSColin Percival}
33748ffe56aSColin Percival
33823d827efSSimon L. B. Nielsenconfig_BackupKernel () {
33923d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNEL} ]; then
34023d827efSSimon L. B. Nielsen		case $1 in
34123d827efSSimon L. B. Nielsen		[Yy][Ee][Ss])
34223d827efSSimon L. B. Nielsen			BACKUPKERNEL=yes
34323d827efSSimon L. B. Nielsen			;;
34423d827efSSimon L. B. Nielsen		[Nn][Oo])
34523d827efSSimon L. B. Nielsen			BACKUPKERNEL=no
34623d827efSSimon L. B. Nielsen			;;
34723d827efSSimon L. B. Nielsen		*)
34823d827efSSimon L. B. Nielsen			return 1
34923d827efSSimon L. B. Nielsen			;;
35023d827efSSimon L. B. Nielsen		esac
35123d827efSSimon L. B. Nielsen	else
35223d827efSSimon L. B. Nielsen		return 1
35323d827efSSimon L. B. Nielsen	fi
35423d827efSSimon L. B. Nielsen}
35523d827efSSimon L. B. Nielsen
35623d827efSSimon L. B. Nielsenconfig_BackupKernelDir () {
35723d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNELDIR} ]; then
35823d827efSSimon L. B. Nielsen		if [ -z "$1" ]; then
35923d827efSSimon L. B. Nielsen			echo "BackupKernelDir set to empty dir"
36023d827efSSimon L. B. Nielsen			return 1
36123d827efSSimon L. B. Nielsen		fi
36223d827efSSimon L. B. Nielsen
36323d827efSSimon L. B. Nielsen		# We check for some paths which would be extremely odd
36423d827efSSimon L. B. Nielsen		# to use, but which could cause a lot of problems if
36523d827efSSimon L. B. Nielsen		# used.
36623d827efSSimon L. B. Nielsen		case $1 in
36723d827efSSimon L. B. Nielsen		/|/bin|/boot|/etc|/lib|/libexec|/sbin|/usr|/var)
36823d827efSSimon L. B. Nielsen			echo "BackupKernelDir set to invalid path $1"
36923d827efSSimon L. B. Nielsen			return 1
37023d827efSSimon L. B. Nielsen			;;
37123d827efSSimon L. B. Nielsen		/*)
37223d827efSSimon L. B. Nielsen			BACKUPKERNELDIR=$1
37323d827efSSimon L. B. Nielsen			;;
37423d827efSSimon L. B. Nielsen		*)
37523d827efSSimon L. B. Nielsen			echo "BackupKernelDir ($1) is not an absolute path"
37623d827efSSimon L. B. Nielsen			return 1
37723d827efSSimon L. B. Nielsen			;;
37823d827efSSimon L. B. Nielsen		esac
37923d827efSSimon L. B. Nielsen	else
38023d827efSSimon L. B. Nielsen		return 1
38123d827efSSimon L. B. Nielsen	fi
38223d827efSSimon L. B. Nielsen}
38323d827efSSimon L. B. Nielsen
38423d827efSSimon L. B. Nielsenconfig_BackupKernelSymbolFiles () {
38523d827efSSimon L. B. Nielsen	if [ -z ${BACKUPKERNELSYMBOLFILES} ]; then
38623d827efSSimon L. B. Nielsen		case $1 in
38723d827efSSimon L. B. Nielsen		[Yy][Ee][Ss])
38823d827efSSimon L. B. Nielsen			BACKUPKERNELSYMBOLFILES=yes
38923d827efSSimon L. B. Nielsen			;;
39023d827efSSimon L. B. Nielsen		[Nn][Oo])
39123d827efSSimon L. B. Nielsen			BACKUPKERNELSYMBOLFILES=no
39223d827efSSimon L. B. Nielsen			;;
39323d827efSSimon L. B. Nielsen		*)
39423d827efSSimon L. B. Nielsen			return 1
39523d827efSSimon L. B. Nielsen			;;
39623d827efSSimon L. B. Nielsen		esac
39723d827efSSimon L. B. Nielsen	else
39823d827efSSimon L. B. Nielsen		return 1
39923d827efSSimon L. B. Nielsen	fi
40023d827efSSimon L. B. Nielsen}
40123d827efSSimon L. B. Nielsen
40248ffe56aSColin Percival# Handle one line of configuration
40348ffe56aSColin Percivalconfigline () {
40448ffe56aSColin Percival	if [ $# -eq 0 ]; then
40548ffe56aSColin Percival		return
40648ffe56aSColin Percival	fi
40748ffe56aSColin Percival
40848ffe56aSColin Percival	OPT=$1
40948ffe56aSColin Percival	shift
41048ffe56aSColin Percival	config_${OPT} $@
41148ffe56aSColin Percival}
41248ffe56aSColin Percival
41348ffe56aSColin Percival#### Parameter handling functions.
41448ffe56aSColin Percival
41548ffe56aSColin Percival# Initialize parameters to null, just in case they're
41648ffe56aSColin Percival# set in the environment.
41748ffe56aSColin Percivalinit_params () {
41848ffe56aSColin Percival	# Configration settings
41948ffe56aSColin Percival	nullconfig
42048ffe56aSColin Percival
42148ffe56aSColin Percival	# No configuration file set yet
42248ffe56aSColin Percival	CONFFILE=""
42348ffe56aSColin Percival
42448ffe56aSColin Percival	# No commands specified yet
42548ffe56aSColin Percival	COMMANDS=""
4268935f242SAllan Jude
4278935f242SAllan Jude	# Force fetch to proceed
4288935f242SAllan Jude	FORCEFETCH=0
4298935f242SAllan Jude
4308935f242SAllan Jude	# Run without a TTY
4318935f242SAllan Jude	NOTTYOK=0
43233bd05c3SGuangyuan Yang
43333bd05c3SGuangyuan Yang	# Fetched first in a chain of commands
43433bd05c3SGuangyuan Yang	ISFETCHED=0
43548ffe56aSColin Percival}
43648ffe56aSColin Percival
43748ffe56aSColin Percival# Parse the command line
43848ffe56aSColin Percivalparse_cmdline () {
43948ffe56aSColin Percival	while [ $# -gt 0 ]; do
44048ffe56aSColin Percival		case "$1" in
44148ffe56aSColin Percival		# Location of configuration file
44248ffe56aSColin Percival		-f)
44348ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi
44448ffe56aSColin Percival			if [ ! -z "${CONFFILE}" ]; then usage; fi
44548ffe56aSColin Percival			shift; CONFFILE="$1"
44648ffe56aSColin Percival			;;
4478935f242SAllan Jude		-F)
4488935f242SAllan Jude			FORCEFETCH=1
4498935f242SAllan Jude			;;
4508935f242SAllan Jude		--not-running-from-cron)
4518935f242SAllan Jude			NOTTYOK=1
4528935f242SAllan Jude			;;
453b39ce43eSColin Percival		--currently-running)
4540d5c5243SEd Maste			shift
4550d5c5243SEd Maste			config_SourceRelease $1 || usage
456b39ce43eSColin Percival			;;
45748ffe56aSColin Percival
45848ffe56aSColin Percival		# Configuration file equivalents
45948ffe56aSColin Percival		-b)
46048ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
46148ffe56aSColin Percival			config_BaseDir $1 || usage
46248ffe56aSColin Percival			;;
46348ffe56aSColin Percival		-d)
46448ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
46548ffe56aSColin Percival			config_WorkDir $1 || usage
46648ffe56aSColin Percival			;;
46748ffe56aSColin Percival		-k)
46848ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
46948ffe56aSColin Percival			config_KeyPrint $1 || usage
47048ffe56aSColin Percival			;;
47148ffe56aSColin Percival		-s)
47248ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
47348ffe56aSColin Percival			config_ServerName $1 || usage
47448ffe56aSColin Percival			;;
475db6b0a61SColin Percival		-r)
476db6b0a61SColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
477db6b0a61SColin Percival			config_TargetRelease $1 || usage
478db6b0a61SColin Percival			;;
47948ffe56aSColin Percival		-t)
48048ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
48148ffe56aSColin Percival			config_MailTo $1 || usage
48248ffe56aSColin Percival			;;
48348ffe56aSColin Percival		-v)
48448ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
48548ffe56aSColin Percival			config_VerboseLevel $1 || usage
48648ffe56aSColin Percival			;;
48748ffe56aSColin Percival
48848ffe56aSColin Percival		# Aliases for "-v debug" and "-v nostats"
48948ffe56aSColin Percival		--debug)
49048ffe56aSColin Percival			config_VerboseLevel debug || usage
49148ffe56aSColin Percival			;;
49248ffe56aSColin Percival		--no-stats)
49348ffe56aSColin Percival			config_VerboseLevel nostats || usage
49448ffe56aSColin Percival			;;
49548ffe56aSColin Percival
49648ffe56aSColin Percival		# Commands
49708e23beeSColin Percival		cron | fetch | upgrade | install | rollback | IDS)
49848ffe56aSColin Percival			COMMANDS="${COMMANDS} $1"
49948ffe56aSColin Percival			;;
50048ffe56aSColin Percival
50148ffe56aSColin Percival		# Anything else is an error
50248ffe56aSColin Percival		*)
50348ffe56aSColin Percival			usage
50448ffe56aSColin Percival			;;
50548ffe56aSColin Percival		esac
50648ffe56aSColin Percival		shift
50748ffe56aSColin Percival	done
50848ffe56aSColin Percival
50948ffe56aSColin Percival	# Make sure we have at least one command
51048ffe56aSColin Percival	if [ -z "${COMMANDS}" ]; then
51148ffe56aSColin Percival		usage
51248ffe56aSColin Percival	fi
51348ffe56aSColin Percival}
51448ffe56aSColin Percival
51548ffe56aSColin Percival# Parse the configuration file
51648ffe56aSColin Percivalparse_conffile () {
51748ffe56aSColin Percival	# If a configuration file was specified on the command line, check
51848ffe56aSColin Percival	# that it exists and is readable.
51948ffe56aSColin Percival	if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
52048ffe56aSColin Percival		echo -n "File does not exist "
52148ffe56aSColin Percival		echo -n "or is not readable: "
52248ffe56aSColin Percival		echo ${CONFFILE}
52348ffe56aSColin Percival		exit 1
52448ffe56aSColin Percival	fi
52548ffe56aSColin Percival
52648ffe56aSColin Percival	# If a configuration file was not specified on the command line,
52748ffe56aSColin Percival	# use the default configuration file path.  If that default does
52848ffe56aSColin Percival	# not exist, give up looking for any configuration.
52948ffe56aSColin Percival	if [ -z "${CONFFILE}" ]; then
53048ffe56aSColin Percival		CONFFILE="/etc/freebsd-update.conf"
53148ffe56aSColin Percival		if [ ! -r "${CONFFILE}" ]; then
53248ffe56aSColin Percival			return
53348ffe56aSColin Percival		fi
53448ffe56aSColin Percival	fi
53548ffe56aSColin Percival
53648ffe56aSColin Percival	# Save the configuration options specified on the command line, and
53748ffe56aSColin Percival	# clear all the options in preparation for reading the config file.
53848ffe56aSColin Percival	saveconfig
53948ffe56aSColin Percival	nullconfig
54048ffe56aSColin Percival
54148ffe56aSColin Percival	# Read the configuration file.  Anything after the first '#' is
54248ffe56aSColin Percival	# ignored, and any blank lines are ignored.
54348ffe56aSColin Percival	L=0
54448ffe56aSColin Percival	while read LINE; do
54548ffe56aSColin Percival		L=$(($L + 1))
54648ffe56aSColin Percival		LINEX=`echo "${LINE}" | cut -f 1 -d '#'`
54748ffe56aSColin Percival		if ! configline ${LINEX}; then
54848ffe56aSColin Percival			echo "Error processing configuration file, line $L:"
54948ffe56aSColin Percival			echo "==> ${LINE}"
55048ffe56aSColin Percival			exit 1
55148ffe56aSColin Percival		fi
55248ffe56aSColin Percival	done < ${CONFFILE}
55348ffe56aSColin Percival
55448ffe56aSColin Percival	# Merge the settings read from the configuration file with those
55548ffe56aSColin Percival	# provided at the command line.
55648ffe56aSColin Percival	mergeconfig
55748ffe56aSColin Percival}
55848ffe56aSColin Percival
55948ffe56aSColin Percival# Provide some default parameters
56048ffe56aSColin Percivaldefault_params () {
56148ffe56aSColin Percival	# Save any parameters already configured, and clear the slate
56248ffe56aSColin Percival	saveconfig
56348ffe56aSColin Percival	nullconfig
56448ffe56aSColin Percival
56548ffe56aSColin Percival	# Default configurations
56648ffe56aSColin Percival	config_WorkDir /var/db/freebsd-update
56748ffe56aSColin Percival	config_MailTo root
56848ffe56aSColin Percival	config_AllowAdd yes
56948ffe56aSColin Percival	config_AllowDelete yes
57048ffe56aSColin Percival	config_KeepModifiedMetadata yes
57148ffe56aSColin Percival	config_BaseDir /
57248ffe56aSColin Percival	config_VerboseLevel stats
573db6b0a61SColin Percival	config_StrictComponents no
57423d827efSSimon L. B. Nielsen	config_BackupKernel yes
57523d827efSSimon L. B. Nielsen	config_BackupKernelDir /boot/kernel.old
57623d827efSSimon L. B. Nielsen	config_BackupKernelSymbolFiles no
57748ffe56aSColin Percival
57848ffe56aSColin Percival	# Merge these defaults into the earlier-configured settings
57948ffe56aSColin Percival	mergeconfig
58048ffe56aSColin Percival}
58148ffe56aSColin Percival
58248ffe56aSColin Percival# Set utility output filtering options, based on ${VERBOSELEVEL}
58348ffe56aSColin Percivalfetch_setup_verboselevel () {
58448ffe56aSColin Percival	case ${VERBOSELEVEL} in
58548ffe56aSColin Percival	debug)
58648ffe56aSColin Percival		QUIETREDIR="/dev/stderr"
58748ffe56aSColin Percival		QUIETFLAG=" "
58848ffe56aSColin Percival		STATSREDIR="/dev/stderr"
58948ffe56aSColin Percival		DDSTATS=".."
59048ffe56aSColin Percival		XARGST="-t"
59148ffe56aSColin Percival		NDEBUG=" "
59248ffe56aSColin Percival		;;
59348ffe56aSColin Percival	nostats)
59448ffe56aSColin Percival		QUIETREDIR=""
59548ffe56aSColin Percival		QUIETFLAG=""
59648ffe56aSColin Percival		STATSREDIR="/dev/null"
59748ffe56aSColin Percival		DDSTATS=".."
59848ffe56aSColin Percival		XARGST=""
59948ffe56aSColin Percival		NDEBUG=""
60048ffe56aSColin Percival		;;
60148ffe56aSColin Percival	stats)
60248ffe56aSColin Percival		QUIETREDIR="/dev/null"
60348ffe56aSColin Percival		QUIETFLAG="-q"
60448ffe56aSColin Percival		STATSREDIR="/dev/stdout"
60548ffe56aSColin Percival		DDSTATS=""
60648ffe56aSColin Percival		XARGST=""
60748ffe56aSColin Percival		NDEBUG="-n"
60848ffe56aSColin Percival		;;
60948ffe56aSColin Percival	esac
61048ffe56aSColin Percival}
61148ffe56aSColin Percival
61248ffe56aSColin Percival# Perform sanity checks and set some final parameters
61348ffe56aSColin Percival# in preparation for fetching files.  Figure out which
61448ffe56aSColin Percival# set of updates should be downloaded: If the user is
61548ffe56aSColin Percival# running *-p[0-9]+, strip off the last part; if the
61648ffe56aSColin Percival# user is running -SECURITY, call it -RELEASE.  Chdir
61748ffe56aSColin Percival# into the working directory.
618211f2ba0SColin Percivalfetchupgrade_check_params () {
61948ffe56aSColin Percival	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
62048ffe56aSColin Percival
62148ffe56aSColin Percival	_SERVERNAME_z=\
62248ffe56aSColin Percival"SERVERNAME must be given via command line or configuration file."
62348ffe56aSColin Percival	_KEYPRINT_z="Key must be given via -k option or configuration file."
62448ffe56aSColin Percival	_KEYPRINT_bad="Invalid key fingerprint: "
62548ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
626f88076f0SMark Felder	_WORKDIR_bad2="Directory is not on a persistent filesystem: "
62748ffe56aSColin Percival
62848ffe56aSColin Percival	if [ -z "${SERVERNAME}" ]; then
62948ffe56aSColin Percival		echo -n "`basename $0`: "
63048ffe56aSColin Percival		echo "${_SERVERNAME_z}"
63148ffe56aSColin Percival		exit 1
63248ffe56aSColin Percival	fi
63348ffe56aSColin Percival	if [ -z "${KEYPRINT}" ]; then
63448ffe56aSColin Percival		echo -n "`basename $0`: "
63548ffe56aSColin Percival		echo "${_KEYPRINT_z}"
63648ffe56aSColin Percival		exit 1
63748ffe56aSColin Percival	fi
63848ffe56aSColin Percival	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
63948ffe56aSColin Percival		echo -n "`basename $0`: "
64048ffe56aSColin Percival		echo -n "${_KEYPRINT_bad}"
64148ffe56aSColin Percival		echo ${KEYPRINT}
64248ffe56aSColin Percival		exit 1
64348ffe56aSColin Percival	fi
64448ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
64548ffe56aSColin Percival		echo -n "`basename $0`: "
64648ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
64748ffe56aSColin Percival		echo ${WORKDIR}
64848ffe56aSColin Percival		exit 1
64948ffe56aSColin Percival	fi
650dfe9215bSMark Felder	case `df -T ${WORKDIR}` in */dev/md[0-9]* | *tmpfs*)
651f88076f0SMark Felder		echo -n "`basename $0`: "
652f88076f0SMark Felder		echo -n "${_WORKDIR_bad2}"
653f88076f0SMark Felder		echo ${WORKDIR}
654f88076f0SMark Felder		exit 1
655dfe9215bSMark Felder		;;
656dfe9215bSMark Felder	esac
657a2356430SColin Percival	chmod 700 ${WORKDIR}
65848ffe56aSColin Percival	cd ${WORKDIR} || exit 1
65948ffe56aSColin Percival
66048ffe56aSColin Percival	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
66148ffe56aSColin Percival	# to provide an upgrade path for FreeBSD Update 1.x users, since
66248ffe56aSColin Percival	# the kernels provided by FreeBSD Update 1.x are always labelled
66348ffe56aSColin Percival	# as X.Y-SECURITY.
66448ffe56aSColin Percival	RELNUM=`uname -r |
66548ffe56aSColin Percival	    sed -E 's,-p[0-9]+,,' |
66648ffe56aSColin Percival	    sed -E 's,-SECURITY,-RELEASE,'`
66748ffe56aSColin Percival	ARCH=`uname -m`
66848ffe56aSColin Percival	FETCHDIR=${RELNUM}/${ARCH}
669db6b0a61SColin Percival	PATCHDIR=${RELNUM}/${ARCH}/bp
67048ffe56aSColin Percival
671d308a8bfSEd Maste	# Disallow upgrade from a version that is not a release
672d308a8bfSEd Maste	case ${RELNUM} in
673d308a8bfSEd Maste		*-RELEASE | *-ALPHA*  | *-BETA* | *-RC*)
674d308a8bfSEd Maste			;;
675d308a8bfSEd Maste		*)
6760d5c5243SEd Maste			echo -n "`basename $0`: "
6770d5c5243SEd Maste			cat <<- EOF
678d308a8bfSEd Maste				Cannot upgrade from a version that is not a release
679d308a8bfSEd Maste				(including alpha, beta and release candidates)
680d308a8bfSEd Maste				using `basename $0`. Instead, FreeBSD can be directly
681d308a8bfSEd Maste				upgraded by source or upgraded to a RELEASE/RELENG version
682d308a8bfSEd Maste				prior to running `basename $0`.
683d308a8bfSEd Maste				Currently running: ${RELNUM}
6840d5c5243SEd Maste			EOF
6850d5c5243SEd Maste			exit 1
686d308a8bfSEd Maste			;;
687d308a8bfSEd Maste	esac
6880d5c5243SEd Maste
68948ffe56aSColin Percival	# Figure out what directory contains the running kernel
69048ffe56aSColin Percival	BOOTFILE=`sysctl -n kern.bootfile`
69148ffe56aSColin Percival	KERNELDIR=${BOOTFILE%/kernel}
69248ffe56aSColin Percival	if ! [ -d ${KERNELDIR} ]; then
69348ffe56aSColin Percival		echo "Cannot identify running kernel"
69448ffe56aSColin Percival		exit 1
69548ffe56aSColin Percival	fi
69648ffe56aSColin Percival
6972c434b2cSColin Percival	# Figure out what kernel configuration is running.  We start with
6982c434b2cSColin Percival	# the output of `uname -i`, and then make the following adjustments:
6992c434b2cSColin Percival	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
7002c434b2cSColin Percival	# file says "ident SMP-GENERIC", I don't know...
7012c434b2cSColin Percival	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
7022c434b2cSColin Percival	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
7032c434b2cSColin Percival	# we're running an SMP kernel.  This mis-identification is a bug
7042c434b2cSColin Percival	# which was fixed in 6.2-STABLE.
7052c434b2cSColin Percival	KERNCONF=`uname -i`
7062c434b2cSColin Percival	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
7072c434b2cSColin Percival		KERNCONF=SMP
7082c434b2cSColin Percival	fi
7092c434b2cSColin Percival	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
7102c434b2cSColin Percival		if sysctl kern.version | grep -qE '/SMP$'; then
7112c434b2cSColin Percival			KERNCONF=SMP
7122c434b2cSColin Percival		fi
7132c434b2cSColin Percival	fi
7142c434b2cSColin Percival
71548ffe56aSColin Percival	# Define some paths
71648ffe56aSColin Percival	BSPATCH=/usr/bin/bspatch
71748ffe56aSColin Percival	SHA256=/sbin/sha256
71848ffe56aSColin Percival	PHTTPGET=/usr/libexec/phttpget
71948ffe56aSColin Percival
72048ffe56aSColin Percival	# Set up variables relating to VERBOSELEVEL
72148ffe56aSColin Percival	fetch_setup_verboselevel
72248ffe56aSColin Percival
72348ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
72448ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
72548ffe56aSColin Percival}
72648ffe56aSColin Percival
727211f2ba0SColin Percival# Perform sanity checks etc. before fetching updates.
728211f2ba0SColin Percivalfetch_check_params () {
729211f2ba0SColin Percival	fetchupgrade_check_params
730211f2ba0SColin Percival
731211f2ba0SColin Percival	if ! [ -z "${TARGETRELEASE}" ]; then
732211f2ba0SColin Percival		echo -n "`basename $0`: "
733211f2ba0SColin Percival		echo -n "-r option is meaningless with 'fetch' command.  "
734211f2ba0SColin Percival		echo "(Did you mean 'upgrade' instead?)"
735211f2ba0SColin Percival		exit 1
736211f2ba0SColin Percival	fi
7378935f242SAllan Jude
7388935f242SAllan Jude	# Check that we have updates ready to install
7398bf2dcceSAllan Jude	if [ -f ${BDHASH}-install/kerneldone -a $FORCEFETCH -eq 0 ]; then
7408935f242SAllan Jude		echo "You have a partially completed upgrade pending"
7418935f242SAllan Jude		echo "Run '$0 install' first."
7428935f242SAllan Jude		echo "Run '$0 fetch -F' to proceed anyway."
7438935f242SAllan Jude		exit 1
7448935f242SAllan Jude	fi
745211f2ba0SColin Percival}
746211f2ba0SColin Percival
747db6b0a61SColin Percival# Perform sanity checks etc. before fetching upgrades.
748db6b0a61SColin Percivalupgrade_check_params () {
749211f2ba0SColin Percival	fetchupgrade_check_params
750db6b0a61SColin Percival
751db6b0a61SColin Percival	# Unless set otherwise, we're upgrading to the same kernel config.
752db6b0a61SColin Percival	NKERNCONF=${KERNCONF}
753db6b0a61SColin Percival
754db6b0a61SColin Percival	# We need TARGETRELEASE set
755db6b0a61SColin Percival	_TARGETRELEASE_z="Release target must be specified via -r option."
756db6b0a61SColin Percival	if [ -z "${TARGETRELEASE}" ]; then
757db6b0a61SColin Percival		echo -n "`basename $0`: "
758db6b0a61SColin Percival		echo "${_TARGETRELEASE_z}"
759db6b0a61SColin Percival		exit 1
760db6b0a61SColin Percival	fi
761db6b0a61SColin Percival
762db6b0a61SColin Percival	# The target release should be != the current release.
763db6b0a61SColin Percival	if [ "${TARGETRELEASE}" = "${RELNUM}" ]; then
764db6b0a61SColin Percival		echo -n "`basename $0`: "
765db6b0a61SColin Percival		echo "Cannot upgrade from ${RELNUM} to itself"
766db6b0a61SColin Percival		exit 1
767db6b0a61SColin Percival	fi
768db6b0a61SColin Percival
769db6b0a61SColin Percival	# Turning off AllowAdd or AllowDelete is a bad idea for upgrades.
770db6b0a61SColin Percival	if [ "${ALLOWADD}" = "no" ]; then
771db6b0a61SColin Percival		echo -n "`basename $0`: "
772db6b0a61SColin Percival		echo -n "WARNING: \"AllowAdd no\" is a bad idea "
773db6b0a61SColin Percival		echo "when upgrading between releases."
774db6b0a61SColin Percival		echo
775db6b0a61SColin Percival	fi
776db6b0a61SColin Percival	if [ "${ALLOWDELETE}" = "no" ]; then
777db6b0a61SColin Percival		echo -n "`basename $0`: "
778db6b0a61SColin Percival		echo -n "WARNING: \"AllowDelete no\" is a bad idea "
779db6b0a61SColin Percival		echo "when upgrading between releases."
780db6b0a61SColin Percival		echo
781db6b0a61SColin Percival	fi
782db6b0a61SColin Percival
783db6b0a61SColin Percival	# Set EDITOR to /usr/bin/vi if it isn't already set
784db6b0a61SColin Percival	: ${EDITOR:='/usr/bin/vi'}
785db6b0a61SColin Percival}
786db6b0a61SColin Percival
78748ffe56aSColin Percival# Perform sanity checks and set some final parameters in
78848ffe56aSColin Percival# preparation for installing updates.
78948ffe56aSColin Percivalinstall_check_params () {
79048ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
79148ffe56aSColin Percival	if [ `id -u` != 0 ]; then
79248ffe56aSColin Percival		echo "You must be root to run this."
79348ffe56aSColin Percival		exit 1
79448ffe56aSColin Percival	fi
79548ffe56aSColin Percival
7962328d598SColin Percival	# Check that securelevel <= 0.  Otherwise we can't update schg files.
7972328d598SColin Percival	if [ `sysctl -n kern.securelevel` -gt 0 ]; then
7982328d598SColin Percival		echo "Updates cannot be installed when the system securelevel"
7992328d598SColin Percival		echo "is greater than zero."
8002328d598SColin Percival		exit 1
8012328d598SColin Percival	fi
8022328d598SColin Percival
80348ffe56aSColin Percival	# Check that we have a working directory
80448ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
80548ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
80648ffe56aSColin Percival		echo -n "`basename $0`: "
80748ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
80848ffe56aSColin Percival		echo ${WORKDIR}
80948ffe56aSColin Percival		exit 1
81048ffe56aSColin Percival	fi
81148ffe56aSColin Percival	cd ${WORKDIR} || exit 1
81248ffe56aSColin Percival
81348ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
81448ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
81548ffe56aSColin Percival
81648ffe56aSColin Percival	# Check that we have updates ready to install
81748ffe56aSColin Percival	if ! [ -L ${BDHASH}-install ]; then
81848ffe56aSColin Percival		echo "No updates are available to install."
81933bd05c3SGuangyuan Yang		if [ $ISFETCHED -eq 0 ]; then
82048ffe56aSColin Percival			echo "Run '$0 fetch' first."
82133bd05c3SGuangyuan Yang		fi
82233bd05c3SGuangyuan Yang		exit 0
82348ffe56aSColin Percival	fi
82448ffe56aSColin Percival	if ! [ -f ${BDHASH}-install/INDEX-OLD ] ||
82548ffe56aSColin Percival	    ! [ -f ${BDHASH}-install/INDEX-NEW ]; then
82648ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
82748ffe56aSColin Percival		echo "Re-run '$0 fetch'."
82848ffe56aSColin Percival		exit 1
82948ffe56aSColin Percival	fi
83023d827efSSimon L. B. Nielsen
83123d827efSSimon L. B. Nielsen	# Figure out what directory contains the running kernel
83223d827efSSimon L. B. Nielsen	BOOTFILE=`sysctl -n kern.bootfile`
83323d827efSSimon L. B. Nielsen	KERNELDIR=${BOOTFILE%/kernel}
83423d827efSSimon L. B. Nielsen	if ! [ -d ${KERNELDIR} ]; then
83523d827efSSimon L. B. Nielsen		echo "Cannot identify running kernel"
83623d827efSSimon L. B. Nielsen		exit 1
83723d827efSSimon L. B. Nielsen	fi
83848ffe56aSColin Percival}
83948ffe56aSColin Percival
84048ffe56aSColin Percival# Perform sanity checks and set some final parameters in
84148ffe56aSColin Percival# preparation for UNinstalling updates.
84248ffe56aSColin Percivalrollback_check_params () {
84348ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
84448ffe56aSColin Percival	if [ `id -u` != 0 ]; then
84548ffe56aSColin Percival		echo "You must be root to run this."
84648ffe56aSColin Percival		exit 1
84748ffe56aSColin Percival	fi
84848ffe56aSColin Percival
84948ffe56aSColin Percival	# Check that we have a working directory
85048ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
85148ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
85248ffe56aSColin Percival		echo -n "`basename $0`: "
85348ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
85448ffe56aSColin Percival		echo ${WORKDIR}
85548ffe56aSColin Percival		exit 1
85648ffe56aSColin Percival	fi
85748ffe56aSColin Percival	cd ${WORKDIR} || exit 1
85848ffe56aSColin Percival
85948ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
86048ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
86148ffe56aSColin Percival
86248ffe56aSColin Percival	# Check that we have updates ready to rollback
86348ffe56aSColin Percival	if ! [ -L ${BDHASH}-rollback ]; then
86448ffe56aSColin Percival		echo "No rollback directory found."
86548ffe56aSColin Percival		exit 1
86648ffe56aSColin Percival	fi
86748ffe56aSColin Percival	if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] ||
86848ffe56aSColin Percival	    ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then
86948ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
87048ffe56aSColin Percival		exit 1
87148ffe56aSColin Percival	fi
87248ffe56aSColin Percival}
87348ffe56aSColin Percival
87408e23beeSColin Percival# Perform sanity checks and set some final parameters
87508e23beeSColin Percival# in preparation for comparing the system against the
87608e23beeSColin Percival# published index.  Figure out which index we should
87708e23beeSColin Percival# compare against: If the user is running *-p[0-9]+,
87808e23beeSColin Percival# strip off the last part; if the user is running
87908e23beeSColin Percival# -SECURITY, call it -RELEASE.  Chdir into the working
88008e23beeSColin Percival# directory.
88108e23beeSColin PercivalIDS_check_params () {
88208e23beeSColin Percival	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
88308e23beeSColin Percival
88408e23beeSColin Percival	_SERVERNAME_z=\
88508e23beeSColin Percival"SERVERNAME must be given via command line or configuration file."
88608e23beeSColin Percival	_KEYPRINT_z="Key must be given via -k option or configuration file."
88708e23beeSColin Percival	_KEYPRINT_bad="Invalid key fingerprint: "
88808e23beeSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
88908e23beeSColin Percival
89008e23beeSColin Percival	if [ -z "${SERVERNAME}" ]; then
89108e23beeSColin Percival		echo -n "`basename $0`: "
89208e23beeSColin Percival		echo "${_SERVERNAME_z}"
89308e23beeSColin Percival		exit 1
89408e23beeSColin Percival	fi
89508e23beeSColin Percival	if [ -z "${KEYPRINT}" ]; then
89608e23beeSColin Percival		echo -n "`basename $0`: "
89708e23beeSColin Percival		echo "${_KEYPRINT_z}"
89808e23beeSColin Percival		exit 1
89908e23beeSColin Percival	fi
90008e23beeSColin Percival	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
90108e23beeSColin Percival		echo -n "`basename $0`: "
90208e23beeSColin Percival		echo -n "${_KEYPRINT_bad}"
90308e23beeSColin Percival		echo ${KEYPRINT}
90408e23beeSColin Percival		exit 1
90508e23beeSColin Percival	fi
90608e23beeSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
90708e23beeSColin Percival		echo -n "`basename $0`: "
90808e23beeSColin Percival		echo -n "${_WORKDIR_bad}"
90908e23beeSColin Percival		echo ${WORKDIR}
91008e23beeSColin Percival		exit 1
91108e23beeSColin Percival	fi
91208e23beeSColin Percival	cd ${WORKDIR} || exit 1
91308e23beeSColin Percival
91408e23beeSColin Percival	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
91508e23beeSColin Percival	# to provide an upgrade path for FreeBSD Update 1.x users, since
91608e23beeSColin Percival	# the kernels provided by FreeBSD Update 1.x are always labelled
91708e23beeSColin Percival	# as X.Y-SECURITY.
91808e23beeSColin Percival	RELNUM=`uname -r |
91908e23beeSColin Percival	    sed -E 's,-p[0-9]+,,' |
92008e23beeSColin Percival	    sed -E 's,-SECURITY,-RELEASE,'`
92108e23beeSColin Percival	ARCH=`uname -m`
92208e23beeSColin Percival	FETCHDIR=${RELNUM}/${ARCH}
92308e23beeSColin Percival	PATCHDIR=${RELNUM}/${ARCH}/bp
92408e23beeSColin Percival
92508e23beeSColin Percival	# Figure out what directory contains the running kernel
92608e23beeSColin Percival	BOOTFILE=`sysctl -n kern.bootfile`
92708e23beeSColin Percival	KERNELDIR=${BOOTFILE%/kernel}
92808e23beeSColin Percival	if ! [ -d ${KERNELDIR} ]; then
92908e23beeSColin Percival		echo "Cannot identify running kernel"
93008e23beeSColin Percival		exit 1
93108e23beeSColin Percival	fi
93208e23beeSColin Percival
93308e23beeSColin Percival	# Figure out what kernel configuration is running.  We start with
93408e23beeSColin Percival	# the output of `uname -i`, and then make the following adjustments:
93508e23beeSColin Percival	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
93608e23beeSColin Percival	# file says "ident SMP-GENERIC", I don't know...
93708e23beeSColin Percival	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
93808e23beeSColin Percival	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
93908e23beeSColin Percival	# we're running an SMP kernel.  This mis-identification is a bug
94008e23beeSColin Percival	# which was fixed in 6.2-STABLE.
94108e23beeSColin Percival	KERNCONF=`uname -i`
94208e23beeSColin Percival	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
94308e23beeSColin Percival		KERNCONF=SMP
94408e23beeSColin Percival	fi
94508e23beeSColin Percival	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
94608e23beeSColin Percival		if sysctl kern.version | grep -qE '/SMP$'; then
94708e23beeSColin Percival			KERNCONF=SMP
94808e23beeSColin Percival		fi
94908e23beeSColin Percival	fi
95008e23beeSColin Percival
95108e23beeSColin Percival	# Define some paths
95208e23beeSColin Percival	SHA256=/sbin/sha256
95308e23beeSColin Percival	PHTTPGET=/usr/libexec/phttpget
95408e23beeSColin Percival
95508e23beeSColin Percival	# Set up variables relating to VERBOSELEVEL
95608e23beeSColin Percival	fetch_setup_verboselevel
95708e23beeSColin Percival}
95808e23beeSColin Percival
95948ffe56aSColin Percival#### Core functionality -- the actual work gets done here
96048ffe56aSColin Percival
96148ffe56aSColin Percival# Use an SRV query to pick a server.  If the SRV query doesn't provide
96248ffe56aSColin Percival# a useful answer, use the server name specified by the user.
96348ffe56aSColin Percival# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
96448ffe56aSColin Percival# from that; or if no servers are returned, use ${SERVERNAME}.
96548ffe56aSColin Percival# This allows a user to specify "portsnap.freebsd.org" (in which case
96648ffe56aSColin Percival# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
96748ffe56aSColin Percival# (in which case portsnap will use that particular server, since there
96848ffe56aSColin Percival# won't be an SRV entry for that name).
96948ffe56aSColin Percival#
97048ffe56aSColin Percival# We ignore the Port field, since we are always going to use port 80.
97148ffe56aSColin Percival
97248ffe56aSColin Percival# Fetch the mirror list, but do not pick a mirror yet.  Returns 1 if
97348ffe56aSColin Percival# no mirrors are available for any reason.
97448ffe56aSColin Percivalfetch_pick_server_init () {
97548ffe56aSColin Percival	: > serverlist_tried
97648ffe56aSColin Percival
97748ffe56aSColin Percival# Check that host(1) exists (i.e., that the system wasn't built with the
97848ffe56aSColin Percival# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
97948ffe56aSColin Percival	if ! which -s host; then
98048ffe56aSColin Percival		: > serverlist_full
98148ffe56aSColin Percival		return 1
98248ffe56aSColin Percival	fi
98348ffe56aSColin Percival
98448ffe56aSColin Percival	echo -n "Looking up ${SERVERNAME} mirrors... "
98548ffe56aSColin Percival
98648ffe56aSColin Percival# Issue the SRV query and pull out the Priority, Weight, and Target fields.
98748ffe56aSColin Percival# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
98848ffe56aSColin Percival# "$name server selection ..."; we allow either format.
98948ffe56aSColin Percival	MLIST="_http._tcp.${SERVERNAME}"
99048ffe56aSColin Percival	host -t srv "${MLIST}" |
991e7fd266eSColin Percival	    sed -nE "s/${MLIST} (has SRV record|server selection) //Ip" |
99248ffe56aSColin Percival	    cut -f 1,2,4 -d ' ' |
99348ffe56aSColin Percival	    sed -e 's/\.$//' |
99448ffe56aSColin Percival	    sort > serverlist_full
99548ffe56aSColin Percival
99648ffe56aSColin Percival# If no records, give up -- we'll just use the server name we were given.
99748ffe56aSColin Percival	if [ `wc -l < serverlist_full` -eq 0 ]; then
99848ffe56aSColin Percival		echo "none found."
99948ffe56aSColin Percival		return 1
100048ffe56aSColin Percival	fi
100148ffe56aSColin Percival
100248ffe56aSColin Percival# Report how many mirrors we found.
100348ffe56aSColin Percival	echo `wc -l < serverlist_full` "mirrors found."
100448ffe56aSColin Percival
100548ffe56aSColin Percival# Generate a random seed for use in picking mirrors.  If HTTP_PROXY
100648ffe56aSColin Percival# is set, this will be used to generate the seed; otherwise, the seed
100748ffe56aSColin Percival# will be random.
100848ffe56aSColin Percival	if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
100948ffe56aSColin Percival		RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
101048ffe56aSColin Percival		    tr -d 'a-f' |
101148ffe56aSColin Percival		    cut -c 1-9`
101248ffe56aSColin Percival	else
101348ffe56aSColin Percival		RANDVALUE=`jot -r 1 0 999999999`
101448ffe56aSColin Percival	fi
101548ffe56aSColin Percival}
101648ffe56aSColin Percival
101748ffe56aSColin Percival# Pick a mirror.  Returns 1 if we have run out of mirrors to try.
101848ffe56aSColin Percivalfetch_pick_server () {
101948ffe56aSColin Percival# Generate a list of not-yet-tried mirrors
102048ffe56aSColin Percival	sort serverlist_tried |
102148ffe56aSColin Percival	    comm -23 serverlist_full - > serverlist
102248ffe56aSColin Percival
102348ffe56aSColin Percival# Have we run out of mirrors?
102448ffe56aSColin Percival	if [ `wc -l < serverlist` -eq 0 ]; then
102548ffe56aSColin Percival		echo "No mirrors remaining, giving up."
102648ffe56aSColin Percival		return 1
102748ffe56aSColin Percival	fi
102848ffe56aSColin Percival
102948ffe56aSColin Percival# Find the highest priority level (lowest numeric value).
103048ffe56aSColin Percival	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
103148ffe56aSColin Percival
103248ffe56aSColin Percival# Add up the weights of the response lines at that priority level.
103348ffe56aSColin Percival	SRV_WSUM=0;
103448ffe56aSColin Percival	while read X; do
103548ffe56aSColin Percival		case "$X" in
103648ffe56aSColin Percival		${SRV_PRIORITY}\ *)
103748ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
103848ffe56aSColin Percival			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
103948ffe56aSColin Percival			;;
104048ffe56aSColin Percival		esac
104148ffe56aSColin Percival	done < serverlist
104248ffe56aSColin Percival
104348ffe56aSColin Percival# If all the weights are 0, pretend that they are all 1 instead.
104448ffe56aSColin Percival	if [ ${SRV_WSUM} -eq 0 ]; then
104548ffe56aSColin Percival		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
104648ffe56aSColin Percival		SRV_W_ADD=1
104748ffe56aSColin Percival	else
104848ffe56aSColin Percival		SRV_W_ADD=0
104948ffe56aSColin Percival	fi
105048ffe56aSColin Percival
105148ffe56aSColin Percival# Pick a value between 0 and the sum of the weights - 1
105248ffe56aSColin Percival	SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
105348ffe56aSColin Percival
105448ffe56aSColin Percival# Read through the list of mirrors and set SERVERNAME.  Write the line
105548ffe56aSColin Percival# corresponding to the mirror we selected into serverlist_tried so that
105648ffe56aSColin Percival# we won't try it again.
105748ffe56aSColin Percival	while read X; do
105848ffe56aSColin Percival		case "$X" in
105948ffe56aSColin Percival		${SRV_PRIORITY}\ *)
106048ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
106148ffe56aSColin Percival			SRV_W=$(($SRV_W + $SRV_W_ADD))
106248ffe56aSColin Percival			if [ $SRV_RND -lt $SRV_W ]; then
106348ffe56aSColin Percival				SERVERNAME=`echo $X | cut -f 3 -d ' '`
106448ffe56aSColin Percival				echo "$X" >> serverlist_tried
106548ffe56aSColin Percival				break
106648ffe56aSColin Percival			else
106748ffe56aSColin Percival				SRV_RND=$(($SRV_RND - $SRV_W))
106848ffe56aSColin Percival			fi
106948ffe56aSColin Percival			;;
107048ffe56aSColin Percival		esac
107148ffe56aSColin Percival	done < serverlist
107248ffe56aSColin Percival}
107348ffe56aSColin Percival
107448ffe56aSColin Percival# Take a list of ${oldhash}|${newhash} and output a list of needed patches,
107548ffe56aSColin Percival# i.e., those for which we have ${oldhash} and don't have ${newhash}.
107648ffe56aSColin Percivalfetch_make_patchlist () {
107748ffe56aSColin Percival	grep -vE "^([0-9a-f]{64})\|\1$" |
107848ffe56aSColin Percival	    tr '|' ' ' |
107948ffe56aSColin Percival		while read X Y; do
108048ffe56aSColin Percival			if [ -f "files/${Y}.gz" ] ||
108148ffe56aSColin Percival			    [ ! -f "files/${X}.gz" ]; then
108248ffe56aSColin Percival				continue
108348ffe56aSColin Percival			fi
108448ffe56aSColin Percival			echo "${X}|${Y}"
1085f6e21461SEd Maste		done | sort -u
108648ffe56aSColin Percival}
108748ffe56aSColin Percival
108848ffe56aSColin Percival# Print user-friendly progress statistics
108948ffe56aSColin Percivalfetch_progress () {
109048ffe56aSColin Percival	LNC=0
109148ffe56aSColin Percival	while read x; do
109248ffe56aSColin Percival		LNC=$(($LNC + 1))
109348ffe56aSColin Percival		if [ $(($LNC % 10)) = 0 ]; then
109448ffe56aSColin Percival			echo -n $LNC
109548ffe56aSColin Percival		elif [ $(($LNC % 2)) = 0 ]; then
109648ffe56aSColin Percival			echo -n .
109748ffe56aSColin Percival		fi
109848ffe56aSColin Percival	done
109948ffe56aSColin Percival	echo -n " "
110048ffe56aSColin Percival}
110148ffe56aSColin Percival
1102db6b0a61SColin Percival# Function for asking the user if everything is ok
1103db6b0a61SColin Percivalcontinuep () {
1104db6b0a61SColin Percival	while read -p "Does this look reasonable (y/n)? " CONTINUE; do
1105db6b0a61SColin Percival		case "${CONTINUE}" in
1106db6b0a61SColin Percival		y*)
1107db6b0a61SColin Percival			return 0
1108db6b0a61SColin Percival			;;
1109db6b0a61SColin Percival		n*)
1110db6b0a61SColin Percival			return 1
1111db6b0a61SColin Percival			;;
1112db6b0a61SColin Percival		esac
1113db6b0a61SColin Percival	done
1114db6b0a61SColin Percival}
1115db6b0a61SColin Percival
111648ffe56aSColin Percival# Initialize the working directory
111748ffe56aSColin Percivalworkdir_init () {
111848ffe56aSColin Percival	mkdir -p files
111948ffe56aSColin Percival	touch tINDEX.present
112048ffe56aSColin Percival}
112148ffe56aSColin Percival
112248ffe56aSColin Percival# Check that we have a public key with an appropriate hash, or
112348ffe56aSColin Percival# fetch the key if it doesn't exist.  Returns 1 if the key has
112448ffe56aSColin Percival# not yet been fetched.
112548ffe56aSColin Percivalfetch_key () {
112648ffe56aSColin Percival	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
112748ffe56aSColin Percival		return 0
112848ffe56aSColin Percival	fi
112948ffe56aSColin Percival
113048ffe56aSColin Percival	echo -n "Fetching public key from ${SERVERNAME}... "
113148ffe56aSColin Percival	rm -f pub.ssl
113248ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \
113348ffe56aSColin Percival	    2>${QUIETREDIR} || true
113448ffe56aSColin Percival	if ! [ -r pub.ssl ]; then
113548ffe56aSColin Percival		echo "failed."
113648ffe56aSColin Percival		return 1
113748ffe56aSColin Percival	fi
113848ffe56aSColin Percival	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
113948ffe56aSColin Percival		echo "key has incorrect hash."
114048ffe56aSColin Percival		rm -f pub.ssl
114148ffe56aSColin Percival		return 1
114248ffe56aSColin Percival	fi
114348ffe56aSColin Percival	echo "done."
114448ffe56aSColin Percival}
114548ffe56aSColin Percival
114648ffe56aSColin Percival# Fetch metadata signature, aka "tag".
114748ffe56aSColin Percivalfetch_tag () {
1148db6b0a61SColin Percival	echo -n "Fetching metadata signature "
1149db6b0a61SColin Percival	echo ${NDEBUG} "for ${RELNUM} from ${SERVERNAME}... "
115048ffe56aSColin Percival	rm -f latest.ssl
115148ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl	\
115248ffe56aSColin Percival	    2>${QUIETREDIR} || true
115348ffe56aSColin Percival	if ! [ -r latest.ssl ]; then
115448ffe56aSColin Percival		echo "failed."
115548ffe56aSColin Percival		return 1
115648ffe56aSColin Percival	fi
115748ffe56aSColin Percival
115848ffe56aSColin Percival	openssl rsautl -pubin -inkey pub.ssl -verify		\
115948ffe56aSColin Percival	    < latest.ssl > tag.new 2>${QUIETREDIR} || true
116048ffe56aSColin Percival	rm latest.ssl
116148ffe56aSColin Percival
116248ffe56aSColin Percival	if ! [ `wc -l < tag.new` = 1 ] ||
116348ffe56aSColin Percival	    ! grep -qE	\
116448ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
116548ffe56aSColin Percival		tag.new; then
116648ffe56aSColin Percival		echo "invalid signature."
116748ffe56aSColin Percival		return 1
116848ffe56aSColin Percival	fi
116948ffe56aSColin Percival
117048ffe56aSColin Percival	echo "done."
117148ffe56aSColin Percival
117248ffe56aSColin Percival	RELPATCHNUM=`cut -f 4 -d '|' < tag.new`
117348ffe56aSColin Percival	TINDEXHASH=`cut -f 5 -d '|' < tag.new`
117448ffe56aSColin Percival	EOLTIME=`cut -f 6 -d '|' < tag.new`
117548ffe56aSColin Percival}
117648ffe56aSColin Percival
117748ffe56aSColin Percival# Sanity-check the patch number in a tag, to make sure that we're not
117848ffe56aSColin Percival# going to "update" backwards and to prevent replay attacks.
117948ffe56aSColin Percivalfetch_tagsanity () {
118048ffe56aSColin Percival	# Check that we're not going to move from -pX to -pY with Y < X.
118148ffe56aSColin Percival	RELPX=`uname -r | sed -E 's,.*-,,'`
118248ffe56aSColin Percival	if echo ${RELPX} | grep -qE '^p[0-9]+$'; then
118348ffe56aSColin Percival		RELPX=`echo ${RELPX} | cut -c 2-`
118448ffe56aSColin Percival	else
118548ffe56aSColin Percival		RELPX=0
118648ffe56aSColin Percival	fi
118748ffe56aSColin Percival	if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then
118848ffe56aSColin Percival		echo
118948ffe56aSColin Percival		echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
119048ffe56aSColin Percival		echo " appear older than what"
119148ffe56aSColin Percival		echo "we are currently running (`uname -r`)!"
119248ffe56aSColin Percival		echo "Cowardly refusing to proceed any further."
119348ffe56aSColin Percival		return 1
119448ffe56aSColin Percival	fi
119548ffe56aSColin Percival
119648ffe56aSColin Percival	# If "tag" exists and corresponds to ${RELNUM}, make sure that
119748ffe56aSColin Percival	# it contains a patch number <= RELPATCHNUM, in order to protect
119848ffe56aSColin Percival	# against rollback (replay) attacks.
119948ffe56aSColin Percival	if [ -f tag ] &&
120048ffe56aSColin Percival	    grep -qE	\
120148ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
120248ffe56aSColin Percival		tag; then
120348ffe56aSColin Percival		LASTRELPATCHNUM=`cut -f 4 -d '|' < tag`
120448ffe56aSColin Percival
120548ffe56aSColin Percival		if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then
120648ffe56aSColin Percival			echo
120748ffe56aSColin Percival			echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
120848ffe56aSColin Percival			echo " are older than the"
120948ffe56aSColin Percival			echo -n "most recently seen updates"
121048ffe56aSColin Percival			echo " (${RELNUM}-p${LASTRELPATCHNUM})."
121148ffe56aSColin Percival			echo "Cowardly refusing to proceed any further."
121248ffe56aSColin Percival			return 1
121348ffe56aSColin Percival		fi
121448ffe56aSColin Percival	fi
121548ffe56aSColin Percival}
121648ffe56aSColin Percival
121748ffe56aSColin Percival# Fetch metadata index file
121848ffe56aSColin Percivalfetch_metadata_index () {
121948ffe56aSColin Percival	echo ${NDEBUG} "Fetching metadata index... "
122048ffe56aSColin Percival	rm -f ${TINDEXHASH}
122148ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH}
122248ffe56aSColin Percival	    2>${QUIETREDIR}
122348ffe56aSColin Percival	if ! [ -f ${TINDEXHASH} ]; then
122448ffe56aSColin Percival		echo "failed."
122548ffe56aSColin Percival		return 1
122648ffe56aSColin Percival	fi
122748ffe56aSColin Percival	if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then
122848ffe56aSColin Percival		echo "update metadata index corrupt."
122948ffe56aSColin Percival		return 1
123048ffe56aSColin Percival	fi
123148ffe56aSColin Percival	echo "done."
123248ffe56aSColin Percival}
123348ffe56aSColin Percival
123448ffe56aSColin Percival# Print an error message about signed metadata being bogus.
123548ffe56aSColin Percivalfetch_metadata_bogus () {
123648ffe56aSColin Percival	echo
123748ffe56aSColin Percival	echo "The update metadata$1 is correctly signed, but"
123848ffe56aSColin Percival	echo "failed an integrity check."
123948ffe56aSColin Percival	echo "Cowardly refusing to proceed any further."
124048ffe56aSColin Percival	return 1
124148ffe56aSColin Percival}
124248ffe56aSColin Percival
124348ffe56aSColin Percival# Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH}
124448ffe56aSColin Percival# with the lines not named in $@ from tINDEX.present (if that file exists).
124548ffe56aSColin Percivalfetch_metadata_index_merge () {
124648ffe56aSColin Percival	for METAFILE in $@; do
124748ffe56aSColin Percival		if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l`	\
124848ffe56aSColin Percival		    -ne 1 ]; then
124948ffe56aSColin Percival			fetch_metadata_bogus " index"
125048ffe56aSColin Percival			return 1
125148ffe56aSColin Percival		fi
125248ffe56aSColin Percival
125348ffe56aSColin Percival		grep -E "${METAFILE}\|" ${TINDEXHASH}
125448ffe56aSColin Percival	done |
125548ffe56aSColin Percival	    sort > tINDEX.wanted
125648ffe56aSColin Percival
125748ffe56aSColin Percival	if [ -f tINDEX.present ]; then
125848ffe56aSColin Percival		join -t '|' -v 2 tINDEX.wanted tINDEX.present |
125948ffe56aSColin Percival		    sort -m - tINDEX.wanted > tINDEX.new
126048ffe56aSColin Percival		rm tINDEX.wanted
126148ffe56aSColin Percival	else
126248ffe56aSColin Percival		mv tINDEX.wanted tINDEX.new
126348ffe56aSColin Percival	fi
126448ffe56aSColin Percival}
126548ffe56aSColin Percival
126648ffe56aSColin Percival# Sanity check all the lines of tINDEX.new.  Even if more metadata lines
126748ffe56aSColin Percival# are added by future versions of the server, this won't cause problems,
126848ffe56aSColin Percival# since the only lines which appear in tINDEX.new are the ones which we
126948ffe56aSColin Percival# specifically grepped out of ${TINDEXHASH}.
127048ffe56aSColin Percivalfetch_metadata_index_sanity () {
127148ffe56aSColin Percival	if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then
127248ffe56aSColin Percival		fetch_metadata_bogus " index"
127348ffe56aSColin Percival		return 1
127448ffe56aSColin Percival	fi
127548ffe56aSColin Percival}
127648ffe56aSColin Percival
127748ffe56aSColin Percival# Sanity check the metadata file $1.
127848ffe56aSColin Percivalfetch_metadata_sanity () {
127948ffe56aSColin Percival	# Some aliases to save space later: ${P} is a character which can
128048ffe56aSColin Percival	# appear in a path; ${M} is the four numeric metadata fields; and
128148ffe56aSColin Percival	# ${H} is a sha256 hash.
12827c06c7c5SKris Moore	P="[-+./:=,%@_[~[:alnum:]]"
128348ffe56aSColin Percival	M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+"
128448ffe56aSColin Percival	H="[0-9a-f]{64}"
128548ffe56aSColin Percival
128648ffe56aSColin Percival	# Check that the first four fields make sense.
128748ffe56aSColin Percival	if gunzip -c < files/$1.gz |
1288823c0d5fSXin LI	    grep -qvE "^[a-z]+\|[0-9a-z-]+\|${P}+\|[fdL-]\|"; then
128948ffe56aSColin Percival		fetch_metadata_bogus ""
129048ffe56aSColin Percival		return 1
129148ffe56aSColin Percival	fi
129248ffe56aSColin Percival
129348ffe56aSColin Percival	# Remove the first three fields.
129448ffe56aSColin Percival	gunzip -c < files/$1.gz |
129548ffe56aSColin Percival	    cut -f 4- -d '|' > sanitycheck.tmp
129648ffe56aSColin Percival
129748ffe56aSColin Percival	# Sanity check entries with type 'f'
129848ffe56aSColin Percival	if grep -E '^f' sanitycheck.tmp |
129948ffe56aSColin Percival	    grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then
130048ffe56aSColin Percival		fetch_metadata_bogus ""
130148ffe56aSColin Percival		return 1
130248ffe56aSColin Percival	fi
130348ffe56aSColin Percival
130448ffe56aSColin Percival	# Sanity check entries with type 'd'
130548ffe56aSColin Percival	if grep -E '^d' sanitycheck.tmp |
130648ffe56aSColin Percival	    grep -qvE "^d\|${M}\|\|\$"; then
130748ffe56aSColin Percival		fetch_metadata_bogus ""
130848ffe56aSColin Percival		return 1
130948ffe56aSColin Percival	fi
131048ffe56aSColin Percival
131148ffe56aSColin Percival	# Sanity check entries with type 'L'
131248ffe56aSColin Percival	if grep -E '^L' sanitycheck.tmp |
131348ffe56aSColin Percival	    grep -qvE "^L\|${M}\|${P}*\|\$"; then
131448ffe56aSColin Percival		fetch_metadata_bogus ""
131548ffe56aSColin Percival		return 1
131648ffe56aSColin Percival	fi
131748ffe56aSColin Percival
131848ffe56aSColin Percival	# Sanity check entries with type '-'
131948ffe56aSColin Percival	if grep -E '^-' sanitycheck.tmp |
132048ffe56aSColin Percival	    grep -qvE "^-\|\|\|\|\|\|"; then
132148ffe56aSColin Percival		fetch_metadata_bogus ""
132248ffe56aSColin Percival		return 1
132348ffe56aSColin Percival	fi
132448ffe56aSColin Percival
132548ffe56aSColin Percival	# Clean up
132648ffe56aSColin Percival	rm sanitycheck.tmp
132748ffe56aSColin Percival}
132848ffe56aSColin Percival
132948ffe56aSColin Percival# Fetch the metadata index and metadata files listed in $@,
133048ffe56aSColin Percival# taking advantage of metadata patches where possible.
133148ffe56aSColin Percivalfetch_metadata () {
133248ffe56aSColin Percival	fetch_metadata_index || return 1
133348ffe56aSColin Percival	fetch_metadata_index_merge $@ || return 1
133448ffe56aSColin Percival	fetch_metadata_index_sanity || return 1
133548ffe56aSColin Percival
133648ffe56aSColin Percival	# Generate a list of wanted metadata patches
133748ffe56aSColin Percival	join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new |
133848ffe56aSColin Percival	    fetch_make_patchlist > patchlist
133948ffe56aSColin Percival
134048ffe56aSColin Percival	if [ -s patchlist ]; then
134148ffe56aSColin Percival		# Attempt to fetch metadata patches
134248ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
134348ffe56aSColin Percival		echo ${NDEBUG} "metadata patches.${DDSTATS}"
134448ffe56aSColin Percival		tr '|' '-' < patchlist |
134548ffe56aSColin Percival		    lam -s "${FETCHDIR}/tp/" - -s ".gz" |
134648ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
134748ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
134848ffe56aSColin Percival		echo "done."
134948ffe56aSColin Percival
135048ffe56aSColin Percival		# Attempt to apply metadata patches
135148ffe56aSColin Percival		echo -n "Applying metadata patches... "
135248ffe56aSColin Percival		tr '|' ' ' < patchlist |
135348ffe56aSColin Percival		    while read X Y; do
135448ffe56aSColin Percival			if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
135548ffe56aSColin Percival			gunzip -c < ${X}-${Y}.gz > diff
135648ffe56aSColin Percival			gunzip -c < files/${X}.gz > diff-OLD
135748ffe56aSColin Percival
135848ffe56aSColin Percival			# Figure out which lines are being added and removed
135948ffe56aSColin Percival			grep -E '^-' diff |
136048ffe56aSColin Percival			    cut -c 2- |
136148ffe56aSColin Percival			    while read PREFIX; do
136248ffe56aSColin Percival				look "${PREFIX}" diff-OLD
136348ffe56aSColin Percival			    done |
136448ffe56aSColin Percival			    sort > diff-rm
136548ffe56aSColin Percival			grep -E '^\+' diff |
136648ffe56aSColin Percival			    cut -c 2- > diff-add
136748ffe56aSColin Percival
136848ffe56aSColin Percival			# Generate the new file
136948ffe56aSColin Percival			comm -23 diff-OLD diff-rm |
137048ffe56aSColin Percival			    sort - diff-add > diff-NEW
137148ffe56aSColin Percival
137248ffe56aSColin Percival			if [ `${SHA256} -q diff-NEW` = ${Y} ]; then
137348ffe56aSColin Percival				mv diff-NEW files/${Y}
137448ffe56aSColin Percival				gzip -n files/${Y}
137548ffe56aSColin Percival			else
137648ffe56aSColin Percival				mv diff-NEW ${Y}.bad
137748ffe56aSColin Percival			fi
137848ffe56aSColin Percival			rm -f ${X}-${Y}.gz diff
137948ffe56aSColin Percival			rm -f diff-OLD diff-NEW diff-add diff-rm
138048ffe56aSColin Percival		done 2>${QUIETREDIR}
138148ffe56aSColin Percival		echo "done."
138248ffe56aSColin Percival	fi
138348ffe56aSColin Percival
138448ffe56aSColin Percival	# Update metadata without patches
138548ffe56aSColin Percival	cut -f 2 -d '|' < tINDEX.new |
138648ffe56aSColin Percival	    while read Y; do
138748ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
138848ffe56aSColin Percival			echo ${Y};
138948ffe56aSColin Percival		fi
1390bce02f98SColin Percival	    done |
1391bce02f98SColin Percival	    sort -u > filelist
139248ffe56aSColin Percival
139348ffe56aSColin Percival	if [ -s filelist ]; then
139448ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
139548ffe56aSColin Percival		echo ${NDEBUG} "metadata files... "
139648ffe56aSColin Percival		lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist |
139748ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
139848ffe56aSColin Percival		    2>${QUIETREDIR}
139948ffe56aSColin Percival
140048ffe56aSColin Percival		while read Y; do
140148ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
140248ffe56aSColin Percival				echo "failed."
140348ffe56aSColin Percival				return 1
140448ffe56aSColin Percival			fi
140548ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
140648ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
140748ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
140848ffe56aSColin Percival			else
140948ffe56aSColin Percival				echo "metadata is corrupt."
141048ffe56aSColin Percival				return 1
141148ffe56aSColin Percival			fi
141248ffe56aSColin Percival		done < filelist
141348ffe56aSColin Percival		echo "done."
141448ffe56aSColin Percival	fi
141548ffe56aSColin Percival
141648ffe56aSColin Percival# Sanity-check the metadata files.
141748ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new > filelist
141848ffe56aSColin Percival	while read X; do
141948ffe56aSColin Percival		fetch_metadata_sanity ${X} || return 1
142048ffe56aSColin Percival	done < filelist
142148ffe56aSColin Percival
142248ffe56aSColin Percival# Remove files which are no longer needed
142348ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.present |
142448ffe56aSColin Percival	    sort > oldfiles
142548ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new |
142648ffe56aSColin Percival	    sort |
142748ffe56aSColin Percival	    comm -13 - oldfiles |
142848ffe56aSColin Percival	    lam -s "files/" - -s ".gz" |
142948ffe56aSColin Percival	    xargs rm -f
143048ffe56aSColin Percival	rm patchlist filelist oldfiles
143148ffe56aSColin Percival	rm ${TINDEXHASH}
143248ffe56aSColin Percival
143348ffe56aSColin Percival# We're done!
143448ffe56aSColin Percival	mv tINDEX.new tINDEX.present
143548ffe56aSColin Percival	mv tag.new tag
143648ffe56aSColin Percival
143748ffe56aSColin Percival	return 0
143848ffe56aSColin Percival}
143948ffe56aSColin Percival
1440db6b0a61SColin Percival# Extract a subset of a downloaded metadata file containing only the parts
1441db6b0a61SColin Percival# which are listed in COMPONENTS.
1442db6b0a61SColin Percivalfetch_filter_metadata_components () {
1443db6b0a61SColin Percival	METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
1444db6b0a61SColin Percival	gunzip -c < files/${METAHASH}.gz > $1.all
1445db6b0a61SColin Percival
1446db6b0a61SColin Percival	# Fish out the lines belonging to components we care about.
1447db6b0a61SColin Percival	for C in ${COMPONENTS}; do
1448db6b0a61SColin Percival		look "`echo ${C} | tr '/' '|'`|" $1.all
1449db6b0a61SColin Percival	done > $1
1450db6b0a61SColin Percival
1451db6b0a61SColin Percival	# Remove temporary file.
1452db6b0a61SColin Percival	rm $1.all
1453db6b0a61SColin Percival}
1454db6b0a61SColin Percival
1455b698a3abSColin Percival# Generate a filtered version of the metadata file $1 from the downloaded
145648ffe56aSColin Percival# file, by fishing out the lines corresponding to components we're trying
145748ffe56aSColin Percival# to keep updated, and then removing lines corresponding to paths we want
145848ffe56aSColin Percival# to ignore.
145948ffe56aSColin Percivalfetch_filter_metadata () {
146048ffe56aSColin Percival	# Fish out the lines belonging to components we care about.
1461db6b0a61SColin Percival	fetch_filter_metadata_components $1
1462db6b0a61SColin Percival
146348ffe56aSColin Percival	# Canonicalize directory names by removing any trailing / in
146448ffe56aSColin Percival	# order to avoid listing directories multiple times if they
146548ffe56aSColin Percival	# belong to multiple components.  Turning "/" into "" doesn't
146648ffe56aSColin Percival	# matter, since we add a leading "/" when we use paths later.
1467db6b0a61SColin Percival	cut -f 3- -d '|' $1 |
146848ffe56aSColin Percival	    sed -e 's,/|d|,|d|,' |
14697e654612SColin Percival	    sed -e 's,/|-|,|-|,' |
147048ffe56aSColin Percival	    sort -u > $1.tmp
147148ffe56aSColin Percival
147248ffe56aSColin Percival	# Figure out which lines to ignore and remove them.
147348ffe56aSColin Percival	for X in ${IGNOREPATHS}; do
147448ffe56aSColin Percival		grep -E "^${X}" $1.tmp
147548ffe56aSColin Percival	done |
147648ffe56aSColin Percival	    sort -u |
147748ffe56aSColin Percival	    comm -13 - $1.tmp > $1
147848ffe56aSColin Percival
147948ffe56aSColin Percival	# Remove temporary files.
1480db6b0a61SColin Percival	rm $1.tmp
148148ffe56aSColin Percival}
148248ffe56aSColin Percival
1483db6b0a61SColin Percival# Filter the metadata file $1 by adding lines with "/boot/$2"
1484bce02f98SColin Percival# replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the
1485db6b0a61SColin Percival# trailing "/kernel"); and if "/boot/$2" does not exist, remove
1486bce02f98SColin Percival# the original lines which start with that.
1487bce02f98SColin Percival# Put another way: Deal with the fact that the FOO kernel is sometimes
1488bce02f98SColin Percival# installed in /boot/FOO/ and is sometimes installed elsewhere.
148948ffe56aSColin Percivalfetch_filter_kernel_names () {
1490db6b0a61SColin Percival	grep ^/boot/$2 $1 |
1491db6b0a61SColin Percival	    sed -e "s,/boot/$2,${KERNELDIR},g" |
149248ffe56aSColin Percival	    sort - $1 > $1.tmp
149348ffe56aSColin Percival	mv $1.tmp $1
1494bce02f98SColin Percival
1495db6b0a61SColin Percival	if ! [ -d /boot/$2 ]; then
1496db6b0a61SColin Percival		grep -v ^/boot/$2 $1 > $1.tmp
1497bce02f98SColin Percival		mv $1.tmp $1
1498bce02f98SColin Percival	fi
149948ffe56aSColin Percival}
150048ffe56aSColin Percival
150148ffe56aSColin Percival# For all paths appearing in $1 or $3, inspect the system
150248ffe56aSColin Percival# and generate $2 describing what is currently installed.
150348ffe56aSColin Percivalfetch_inspect_system () {
150448ffe56aSColin Percival	# No errors yet...
150548ffe56aSColin Percival	rm -f .err
150648ffe56aSColin Percival
150748ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
150848ffe56aSColin Percival	echo -n "Inspecting system... "
150948ffe56aSColin Percival
151048ffe56aSColin Percival	# Generate list of files to inspect
151148ffe56aSColin Percival	cat $1 $3 |
151248ffe56aSColin Percival	    cut -f 1 -d '|' |
151348ffe56aSColin Percival	    sort -u > filelist
151448ffe56aSColin Percival
151548ffe56aSColin Percival	# Examine each file and output lines of the form
151648ffe56aSColin Percival	# /path/to/file|type|device-inum|user|group|perm|flags|value
151748ffe56aSColin Percival	# sorted by device and inode number.
151848ffe56aSColin Percival	while read F; do
151948ffe56aSColin Percival		# If the symlink/file/directory does not exist, record this.
152048ffe56aSColin Percival		if ! [ -e ${BASEDIR}/${F} ]; then
152148ffe56aSColin Percival			echo "${F}|-||||||"
152248ffe56aSColin Percival			continue
152348ffe56aSColin Percival		fi
152448ffe56aSColin Percival		if ! [ -r ${BASEDIR}/${F} ]; then
152548ffe56aSColin Percival			echo "Cannot read file: ${BASEDIR}/${F}"	\
152648ffe56aSColin Percival			    >/dev/stderr
152748ffe56aSColin Percival			touch .err
152848ffe56aSColin Percival			return 1
152948ffe56aSColin Percival		fi
153048ffe56aSColin Percival
153148ffe56aSColin Percival		# Otherwise, output an index line.
153248ffe56aSColin Percival		if [ -L ${BASEDIR}/${F} ]; then
153348ffe56aSColin Percival			echo -n "${F}|L|"
153448ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
153548ffe56aSColin Percival			readlink ${BASEDIR}/${F};
153648ffe56aSColin Percival		elif [ -f ${BASEDIR}/${F} ]; then
153748ffe56aSColin Percival			echo -n "${F}|f|"
153848ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
153948ffe56aSColin Percival			sha256 -q ${BASEDIR}/${F};
154048ffe56aSColin Percival		elif [ -d ${BASEDIR}/${F} ]; then
154148ffe56aSColin Percival			echo -n "${F}|d|"
154248ffe56aSColin Percival			stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
154348ffe56aSColin Percival		else
154448ffe56aSColin Percival			echo "Unknown file type: ${BASEDIR}/${F}"	\
154548ffe56aSColin Percival			    >/dev/stderr
154648ffe56aSColin Percival			touch .err
154748ffe56aSColin Percival			return 1
154848ffe56aSColin Percival		fi
154948ffe56aSColin Percival	done < filelist |
155048ffe56aSColin Percival	    sort -k 3,3 -t '|' > $2.tmp
155148ffe56aSColin Percival	rm filelist
155248ffe56aSColin Percival
15536dcc68c8SBenedict Reuschling	# Check if an error occurred during system inspection
155448ffe56aSColin Percival	if [ -f .err ]; then
155548ffe56aSColin Percival		return 1
155648ffe56aSColin Percival	fi
155748ffe56aSColin Percival
155848ffe56aSColin Percival	# Convert to the form
155948ffe56aSColin Percival	# /path/to/file|type|user|group|perm|flags|value|hlink
156048ffe56aSColin Percival	# by resolving identical device and inode numbers into hard links.
156148ffe56aSColin Percival	cut -f 1,3 -d '|' $2.tmp |
156248ffe56aSColin Percival	    sort -k 1,1 -t '|' |
156348ffe56aSColin Percival	    sort -s -u -k 2,2 -t '|' |
156448ffe56aSColin Percival	    join -1 2 -2 3 -t '|' - $2.tmp |
156548ffe56aSColin Percival	    awk -F \| -v OFS=\|		\
156648ffe56aSColin Percival		'{
156748ffe56aSColin Percival		    if (($2 == $3) || ($4 == "-"))
156848ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,""
156948ffe56aSColin Percival		    else
157048ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,$2
157148ffe56aSColin Percival		}' |
157248ffe56aSColin Percival	    sort > $2
157348ffe56aSColin Percival	rm $2.tmp
157448ffe56aSColin Percival
157548ffe56aSColin Percival	# We're finished looking around
157648ffe56aSColin Percival	echo "done."
157748ffe56aSColin Percival}
157848ffe56aSColin Percival
1579db6b0a61SColin Percival# For any paths matching ${MERGECHANGES}, compare $1 and $2 and find any
1580db6b0a61SColin Percival# files which differ; generate $3 containing these paths and the old hashes.
1581db6b0a61SColin Percivalfetch_filter_mergechanges () {
1582db6b0a61SColin Percival	# Pull out the paths and hashes of the files matching ${MERGECHANGES}.
1583db6b0a61SColin Percival	for F in $1 $2; do
1584db6b0a61SColin Percival		for X in ${MERGECHANGES}; do
1585db6b0a61SColin Percival			grep -E "^${X}" ${F}
1586db6b0a61SColin Percival		done |
1587db6b0a61SColin Percival		    cut -f 1,2,7 -d '|' |
1588db6b0a61SColin Percival		    sort > ${F}-values
1589db6b0a61SColin Percival	done
1590db6b0a61SColin Percival
1591db6b0a61SColin Percival	# Any line in $2-values which doesn't appear in $1-values and is a
1592db6b0a61SColin Percival	# file means that we should list the path in $3.
1593db6b0a61SColin Percival	comm -13 $1-values $2-values |
1594db6b0a61SColin Percival	    fgrep '|f|' |
1595db6b0a61SColin Percival	    cut -f 1 -d '|' > $2-paths
1596db6b0a61SColin Percival
1597db6b0a61SColin Percival	# For each path, pull out one (and only one!) entry from $1-values.
1598db6b0a61SColin Percival	# Note that we cannot distinguish which "old" version the user made
1599db6b0a61SColin Percival	# changes to; but hopefully any changes which occur due to security
1600db6b0a61SColin Percival	# updates will exist in both the "new" version and the version which
1601db6b0a61SColin Percival	# the user has installed, so the merging will still work.
1602db6b0a61SColin Percival	while read X; do
1603db6b0a61SColin Percival		look "${X}|" $1-values |
1604db6b0a61SColin Percival		    head -1
1605db6b0a61SColin Percival	done < $2-paths > $3
1606db6b0a61SColin Percival
1607db6b0a61SColin Percival	# Clean up
1608db6b0a61SColin Percival	rm $1-values $2-values $2-paths
1609db6b0a61SColin Percival}
1610db6b0a61SColin Percival
161148ffe56aSColin Percival# For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123]
1612db6b0a61SColin Percival# which correspond to lines in $2 with hashes not matching $1 or $3, unless
1613db6b0a61SColin Percival# the paths are listed in $4.  For entries in $2 marked "not present"
1614db6b0a61SColin Percival# (aka. type -), remove lines from $[123] unless there is a corresponding
1615db6b0a61SColin Percival# entry in $1.
161648ffe56aSColin Percivalfetch_filter_unmodified_notpresent () {
161748ffe56aSColin Percival	# Figure out which lines of $1 and $3 correspond to bits which
161848ffe56aSColin Percival	# should only be updated if they haven't changed, and fish out
161948ffe56aSColin Percival	# the (path, type, value) tuples.
162048ffe56aSColin Percival	# NOTE: We don't consider a file to be "modified" if it matches
162148ffe56aSColin Percival	# the hash from $3.
162248ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
162348ffe56aSColin Percival		grep -E "^${X}" $1
162448ffe56aSColin Percival		grep -E "^${X}" $3
162548ffe56aSColin Percival	done |
162648ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
162748ffe56aSColin Percival	    sort > $1-values
162848ffe56aSColin Percival
162948ffe56aSColin Percival	# Do the same for $2.
163048ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
163148ffe56aSColin Percival		grep -E "^${X}" $2
163248ffe56aSColin Percival	done |
163348ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
163448ffe56aSColin Percival	    sort > $2-values
163548ffe56aSColin Percival
163648ffe56aSColin Percival	# Any entry in $2-values which is not in $1-values corresponds to
1637db6b0a61SColin Percival	# a path which we need to remove from $1, $2, and $3, unless it
1638db6b0a61SColin Percival	# that path appears in $4.
1639db6b0a61SColin Percival	comm -13 $1-values $2-values |
1640db6b0a61SColin Percival	    sort -t '|' -k 1,1 > mlines.tmp
1641db6b0a61SColin Percival	cut -f 1 -d '|' $4 |
1642db6b0a61SColin Percival	    sort |
1643db6b0a61SColin Percival	    join -v 2 -t '|' - mlines.tmp |
1644db6b0a61SColin Percival	    sort > mlines
1645db6b0a61SColin Percival	rm $1-values $2-values mlines.tmp
164648ffe56aSColin Percival
164748ffe56aSColin Percival	# Any lines in $2 which are not in $1 AND are "not present" lines
164848ffe56aSColin Percival	# also belong in mlines.
164948ffe56aSColin Percival	comm -13 $1 $2 |
165048ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
165148ffe56aSColin Percival	    fgrep '|-|' >> mlines
165248ffe56aSColin Percival
165348ffe56aSColin Percival	# Remove lines from $1, $2, and $3
165448ffe56aSColin Percival	for X in $1 $2 $3; do
165548ffe56aSColin Percival		sort -t '|' -k 1,1 ${X} > ${X}.tmp
165648ffe56aSColin Percival		cut -f 1 -d '|' < mlines |
165748ffe56aSColin Percival		    sort |
165848ffe56aSColin Percival		    join -v 2 -t '|' - ${X}.tmp |
165948ffe56aSColin Percival		    sort > ${X}
166048ffe56aSColin Percival		rm ${X}.tmp
166148ffe56aSColin Percival	done
166248ffe56aSColin Percival
166348ffe56aSColin Percival	# Store a list of the modified files, for future reference
166448ffe56aSColin Percival	fgrep -v '|-|' mlines |
166548ffe56aSColin Percival	    cut -f 1 -d '|' > modifiedfiles
166648ffe56aSColin Percival	rm mlines
166748ffe56aSColin Percival}
166848ffe56aSColin Percival
166948ffe56aSColin Percival# For each entry in $1 of type -, remove any corresponding
167048ffe56aSColin Percival# entry from $2 if ${ALLOWADD} != "yes".  Remove all entries
167148ffe56aSColin Percival# of type - from $1.
167248ffe56aSColin Percivalfetch_filter_allowadd () {
167348ffe56aSColin Percival	cut -f 1,2 -d '|' < $1 |
167448ffe56aSColin Percival	    fgrep '|-' |
167548ffe56aSColin Percival	    cut -f 1 -d '|' > filesnotpresent
167648ffe56aSColin Percival
167748ffe56aSColin Percival	if [ ${ALLOWADD} != "yes" ]; then
167848ffe56aSColin Percival		sort < $2 |
167948ffe56aSColin Percival		    join -v 1 -t '|' - filesnotpresent |
168048ffe56aSColin Percival		    sort > $2.tmp
168148ffe56aSColin Percival		mv $2.tmp $2
168248ffe56aSColin Percival	fi
168348ffe56aSColin Percival
168448ffe56aSColin Percival	sort < $1 |
168548ffe56aSColin Percival	    join -v 1 -t '|' - filesnotpresent |
168648ffe56aSColin Percival	    sort > $1.tmp
168748ffe56aSColin Percival	mv $1.tmp $1
168848ffe56aSColin Percival	rm filesnotpresent
168948ffe56aSColin Percival}
169048ffe56aSColin Percival
169148ffe56aSColin Percival# If ${ALLOWDELETE} != "yes", then remove any entries from $1
169248ffe56aSColin Percival# which don't correspond to entries in $2.
169348ffe56aSColin Percivalfetch_filter_allowdelete () {
169448ffe56aSColin Percival	# Produce a lists ${PATH}|${TYPE}
169548ffe56aSColin Percival	for X in $1 $2; do
169648ffe56aSColin Percival		cut -f 1-2 -d '|' < ${X} |
169748ffe56aSColin Percival		    sort -u > ${X}.nodes
169848ffe56aSColin Percival	done
169948ffe56aSColin Percival
170048ffe56aSColin Percival	# Figure out which lines need to be removed from $1.
170148ffe56aSColin Percival	if [ ${ALLOWDELETE} != "yes" ]; then
170248ffe56aSColin Percival		comm -23 $1.nodes $2.nodes > $1.badnodes
170348ffe56aSColin Percival	else
170448ffe56aSColin Percival		: > $1.badnodes
170548ffe56aSColin Percival	fi
170648ffe56aSColin Percival
170748ffe56aSColin Percival	# Remove the relevant lines from $1
170848ffe56aSColin Percival	while read X; do
170948ffe56aSColin Percival		look "${X}|" $1
171048ffe56aSColin Percival	done < $1.badnodes |
171148ffe56aSColin Percival	    comm -13 - $1 > $1.tmp
171248ffe56aSColin Percival	mv $1.tmp $1
171348ffe56aSColin Percival
171448ffe56aSColin Percival	rm $1.badnodes $1.nodes $2.nodes
171548ffe56aSColin Percival}
171648ffe56aSColin Percival
171748ffe56aSColin Percival# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2
171848ffe56aSColin Percival# with metadata not matching any entry in $1, replace the corresponding
171948ffe56aSColin Percival# line of $3 with one having the same metadata as the entry in $2.
172048ffe56aSColin Percivalfetch_filter_modified_metadata () {
172148ffe56aSColin Percival	# Fish out the metadata from $1 and $2
172248ffe56aSColin Percival	for X in $1 $2; do
172348ffe56aSColin Percival		cut -f 1-6 -d '|' < ${X} > ${X}.metadata
172448ffe56aSColin Percival	done
172548ffe56aSColin Percival
172648ffe56aSColin Percival	# Find the metadata we need to keep
172748ffe56aSColin Percival	if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then
172848ffe56aSColin Percival		comm -13 $1.metadata $2.metadata > keepmeta
172948ffe56aSColin Percival	else
173048ffe56aSColin Percival		: > keepmeta
173148ffe56aSColin Percival	fi
173248ffe56aSColin Percival
173348ffe56aSColin Percival	# Extract the lines which we need to remove from $3, and
173448ffe56aSColin Percival	# construct the lines which we need to add to $3.
173548ffe56aSColin Percival	: > $3.remove
173648ffe56aSColin Percival	: > $3.add
173748ffe56aSColin Percival	while read LINE; do
173848ffe56aSColin Percival		NODE=`echo "${LINE}" | cut -f 1-2 -d '|'`
173948ffe56aSColin Percival		look "${NODE}|" $3 >> $3.remove
174048ffe56aSColin Percival		look "${NODE}|" $3 |
174148ffe56aSColin Percival		    cut -f 7- -d '|' |
174248ffe56aSColin Percival		    lam -s "${LINE}|" - >> $3.add
174348ffe56aSColin Percival	done < keepmeta
174448ffe56aSColin Percival
174548ffe56aSColin Percival	# Remove the specified lines and add the new lines.
174648ffe56aSColin Percival	sort $3.remove |
174748ffe56aSColin Percival	    comm -13 - $3 |
174848ffe56aSColin Percival	    sort -u - $3.add > $3.tmp
174948ffe56aSColin Percival	mv $3.tmp $3
175048ffe56aSColin Percival
175148ffe56aSColin Percival	rm keepmeta $1.metadata $2.metadata $3.add $3.remove
175248ffe56aSColin Percival}
175348ffe56aSColin Percival
175448ffe56aSColin Percival# Remove lines from $1 and $2 which are identical;
175548ffe56aSColin Percival# no need to update a file if it isn't changing.
175648ffe56aSColin Percivalfetch_filter_uptodate () {
175748ffe56aSColin Percival	comm -23 $1 $2 > $1.tmp
175848ffe56aSColin Percival	comm -13 $1 $2 > $2.tmp
175948ffe56aSColin Percival
176048ffe56aSColin Percival	mv $1.tmp $1
176148ffe56aSColin Percival	mv $2.tmp $2
176248ffe56aSColin Percival}
176348ffe56aSColin Percival
1764db6b0a61SColin Percival# Fetch any "clean" old versions of files we need for merging changes.
1765db6b0a61SColin Percivalfetch_files_premerge () {
1766db6b0a61SColin Percival	# We only need to do anything if $1 is non-empty.
1767db6b0a61SColin Percival	if [ -s $1 ]; then
1768db6b0a61SColin Percival		# Tell the user what we're doing
1769db6b0a61SColin Percival		echo -n "Fetching files from ${OLDRELNUM} for merging... "
1770db6b0a61SColin Percival
1771db6b0a61SColin Percival		# List of files wanted
1772db6b0a61SColin Percival		fgrep '|f|' < $1 |
1773db6b0a61SColin Percival		    cut -f 3 -d '|' |
1774db6b0a61SColin Percival		    sort -u > files.wanted
1775db6b0a61SColin Percival
1776db6b0a61SColin Percival		# Only fetch the files we don't already have
1777db6b0a61SColin Percival		while read Y; do
1778db6b0a61SColin Percival			if [ ! -f "files/${Y}.gz" ]; then
1779db6b0a61SColin Percival				echo ${Y};
1780db6b0a61SColin Percival			fi
1781db6b0a61SColin Percival		done < files.wanted > filelist
1782db6b0a61SColin Percival
1783db6b0a61SColin Percival		# Actually fetch them
1784db6b0a61SColin Percival		lam -s "${OLDFETCHDIR}/f/" - -s ".gz" < filelist |
1785db6b0a61SColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
1786db6b0a61SColin Percival		    2>${QUIETREDIR}
1787db6b0a61SColin Percival
1788db6b0a61SColin Percival		# Make sure we got them all, and move them into /files/
1789db6b0a61SColin Percival		while read Y; do
1790db6b0a61SColin Percival			if ! [ -f ${Y}.gz ]; then
1791db6b0a61SColin Percival				echo "failed."
1792db6b0a61SColin Percival				return 1
1793db6b0a61SColin Percival			fi
1794db6b0a61SColin Percival			if [ `gunzip -c < ${Y}.gz |
1795db6b0a61SColin Percival			    ${SHA256} -q` = ${Y} ]; then
1796db6b0a61SColin Percival				mv ${Y}.gz files/${Y}.gz
1797db6b0a61SColin Percival			else
1798db6b0a61SColin Percival				echo "${Y} has incorrect hash."
1799db6b0a61SColin Percival				return 1
1800db6b0a61SColin Percival			fi
1801db6b0a61SColin Percival		done < filelist
1802db6b0a61SColin Percival		echo "done."
1803db6b0a61SColin Percival
1804db6b0a61SColin Percival		# Clean up
1805db6b0a61SColin Percival		rm filelist files.wanted
1806db6b0a61SColin Percival	fi
1807db6b0a61SColin Percival}
1808db6b0a61SColin Percival
180948ffe56aSColin Percival# Prepare to fetch files: Generate a list of the files we need,
181048ffe56aSColin Percival# copy the unmodified files we have into /files/, and generate
181148ffe56aSColin Percival# a list of patches to download.
181248ffe56aSColin Percivalfetch_files_prepare () {
181348ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
181448ffe56aSColin Percival	echo -n "Preparing to download files... "
181548ffe56aSColin Percival
181648ffe56aSColin Percival	# Reduce indices to ${PATH}|${HASH} pairs
181748ffe56aSColin Percival	for X in $1 $2 $3; do
181848ffe56aSColin Percival		cut -f 1,2,7 -d '|' < ${X} |
181948ffe56aSColin Percival		    fgrep '|f|' |
182048ffe56aSColin Percival		    cut -f 1,3 -d '|' |
182148ffe56aSColin Percival		    sort > ${X}.hashes
182248ffe56aSColin Percival	done
182348ffe56aSColin Percival
182448ffe56aSColin Percival	# List of files wanted
182548ffe56aSColin Percival	cut -f 2 -d '|' < $3.hashes |
18262328d598SColin Percival	    sort -u |
18272328d598SColin Percival	    while read HASH; do
18282328d598SColin Percival		if ! [ -f files/${HASH}.gz ]; then
18292328d598SColin Percival			echo ${HASH}
18302328d598SColin Percival		fi
18312328d598SColin Percival	done > files.wanted
183248ffe56aSColin Percival
183348ffe56aSColin Percival	# Generate a list of unmodified files
183448ffe56aSColin Percival	comm -12 $1.hashes $2.hashes |
183548ffe56aSColin Percival	    sort -k 1,1 -t '|' > unmodified.files
183648ffe56aSColin Percival
183748ffe56aSColin Percival	# Copy all files into /files/.  We only need the unmodified files
183848ffe56aSColin Percival	# for use in patching; but we'll want all of them if the user asks
183948ffe56aSColin Percival	# to rollback the updates later.
1840210b8123SColin Percival	while read LINE; do
1841210b8123SColin Percival		F=`echo "${LINE}" | cut -f 1 -d '|'`
1842210b8123SColin Percival		HASH=`echo "${LINE}" | cut -f 2 -d '|'`
1843210b8123SColin Percival
1844210b8123SColin Percival		# Skip files we already have.
1845210b8123SColin Percival		if [ -f files/${HASH}.gz ]; then
1846210b8123SColin Percival			continue
1847210b8123SColin Percival		fi
1848210b8123SColin Percival
1849210b8123SColin Percival		# Make sure the file hasn't changed.
185048ffe56aSColin Percival		cp "${BASEDIR}/${F}" tmpfile
1851210b8123SColin Percival		if [ `sha256 -q tmpfile` != ${HASH} ]; then
1852210b8123SColin Percival			echo
1853210b8123SColin Percival			echo "File changed while FreeBSD Update running: ${F}"
1854210b8123SColin Percival			return 1
1855210b8123SColin Percival		fi
1856210b8123SColin Percival
1857210b8123SColin Percival		# Place the file into storage.
1858210b8123SColin Percival		gzip -c < tmpfile > files/${HASH}.gz
185948ffe56aSColin Percival		rm tmpfile
1860210b8123SColin Percival	done < $2.hashes
186148ffe56aSColin Percival
186248ffe56aSColin Percival	# Produce a list of patches to download
186348ffe56aSColin Percival	sort -k 1,1 -t '|' $3.hashes |
186448ffe56aSColin Percival	    join -t '|' -o 2.2,1.2 - unmodified.files |
186548ffe56aSColin Percival	    fetch_make_patchlist > patchlist
186648ffe56aSColin Percival
186748ffe56aSColin Percival	# Garbage collect
186848ffe56aSColin Percival	rm unmodified.files $1.hashes $2.hashes $3.hashes
186948ffe56aSColin Percival
187048ffe56aSColin Percival	# We don't need the list of possible old files any more.
187148ffe56aSColin Percival	rm $1
187248ffe56aSColin Percival
187348ffe56aSColin Percival	# We're finished making noise
187448ffe56aSColin Percival	echo "done."
187548ffe56aSColin Percival}
187648ffe56aSColin Percival
187748ffe56aSColin Percival# Fetch files.
187848ffe56aSColin Percivalfetch_files () {
187948ffe56aSColin Percival	# Attempt to fetch patches
188048ffe56aSColin Percival	if [ -s patchlist ]; then
188148ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
188248ffe56aSColin Percival		echo ${NDEBUG} "patches.${DDSTATS}"
188348ffe56aSColin Percival		tr '|' '-' < patchlist |
1884db6b0a61SColin Percival		    lam -s "${PATCHDIR}/" - |
188548ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
188648ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
188748ffe56aSColin Percival		echo "done."
188848ffe56aSColin Percival
188948ffe56aSColin Percival		# Attempt to apply patches
189048ffe56aSColin Percival		echo -n "Applying patches... "
189148ffe56aSColin Percival		tr '|' ' ' < patchlist |
189248ffe56aSColin Percival		    while read X Y; do
189348ffe56aSColin Percival			if [ ! -f "${X}-${Y}" ]; then continue; fi
189448ffe56aSColin Percival			gunzip -c < files/${X}.gz > OLD
189548ffe56aSColin Percival
189648ffe56aSColin Percival			bspatch OLD NEW ${X}-${Y}
189748ffe56aSColin Percival
189848ffe56aSColin Percival			if [ `${SHA256} -q NEW` = ${Y} ]; then
189948ffe56aSColin Percival				mv NEW files/${Y}
190048ffe56aSColin Percival				gzip -n files/${Y}
190148ffe56aSColin Percival			fi
190248ffe56aSColin Percival			rm -f diff OLD NEW ${X}-${Y}
190348ffe56aSColin Percival		done 2>${QUIETREDIR}
190448ffe56aSColin Percival		echo "done."
190548ffe56aSColin Percival	fi
190648ffe56aSColin Percival
190748ffe56aSColin Percival	# Download files which couldn't be generate via patching
190848ffe56aSColin Percival	while read Y; do
190948ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
191048ffe56aSColin Percival			echo ${Y};
191148ffe56aSColin Percival		fi
191248ffe56aSColin Percival	done < files.wanted > filelist
191348ffe56aSColin Percival
191448ffe56aSColin Percival	if [ -s filelist ]; then
191548ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
191648ffe56aSColin Percival		echo ${NDEBUG} "files... "
191748ffe56aSColin Percival		lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist |
191848ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
1919d6e1e31aSConrad Meyer			2>${STATSREDIR} | fetch_progress
192048ffe56aSColin Percival
192148ffe56aSColin Percival		while read Y; do
192248ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
192348ffe56aSColin Percival				echo "failed."
192448ffe56aSColin Percival				return 1
192548ffe56aSColin Percival			fi
192648ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
192748ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
192848ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
192948ffe56aSColin Percival			else
193048ffe56aSColin Percival				echo "${Y} has incorrect hash."
193148ffe56aSColin Percival				return 1
193248ffe56aSColin Percival			fi
193348ffe56aSColin Percival		done < filelist
193448ffe56aSColin Percival		echo "done."
193548ffe56aSColin Percival	fi
193648ffe56aSColin Percival
193748ffe56aSColin Percival	# Clean up
193848ffe56aSColin Percival	rm files.wanted filelist patchlist
193948ffe56aSColin Percival}
194048ffe56aSColin Percival
194148ffe56aSColin Percival# Create and populate install manifest directory; and report what updates
194248ffe56aSColin Percival# are available.
194348ffe56aSColin Percivalfetch_create_manifest () {
194448ffe56aSColin Percival	# If we have an existing install manifest, nuke it.
194548ffe56aSColin Percival	if [ -L "${BDHASH}-install" ]; then
194648ffe56aSColin Percival		rm -r ${BDHASH}-install/
194748ffe56aSColin Percival		rm ${BDHASH}-install
194848ffe56aSColin Percival	fi
194948ffe56aSColin Percival
195048ffe56aSColin Percival	# Report to the user if any updates were avoided due to local changes
195148ffe56aSColin Percival	if [ -s modifiedfiles ]; then
1952*fc24ba59SEd Maste		cat - modifiedfiles <<- EOF | ${PAGER}
1953*fc24ba59SEd Maste			The folling files are affected by updates but no changes have
1954*fc24ba59SEd Maste			been downloaded because the files have been modified locally:
1955*fc24ba59SEd Maste		EOF
1956*fc24ba59SEd Maste	fi
195748ffe56aSColin Percival	rm modifiedfiles
195848ffe56aSColin Percival
195948ffe56aSColin Percival	# If no files will be updated, tell the user and exit
196048ffe56aSColin Percival	if ! [ -s INDEX-PRESENT ] &&
196148ffe56aSColin Percival	    ! [ -s INDEX-NEW ]; then
196248ffe56aSColin Percival		rm INDEX-PRESENT INDEX-NEW
196348ffe56aSColin Percival		echo
196448ffe56aSColin Percival		echo -n "No updates needed to update system to "
196548ffe56aSColin Percival		echo "${RELNUM}-p${RELPATCHNUM}."
196648ffe56aSColin Percival		return
196748ffe56aSColin Percival	fi
196848ffe56aSColin Percival
196948ffe56aSColin Percival	# Divide files into (a) removed files, (b) added files, and
197048ffe56aSColin Percival	# (c) updated files.
197148ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-PRESENT |
197248ffe56aSColin Percival	    sort > INDEX-PRESENT.flist
197348ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-NEW |
197448ffe56aSColin Percival	    sort > INDEX-NEW.flist
197548ffe56aSColin Percival	comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed
197648ffe56aSColin Percival	comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added
197748ffe56aSColin Percival	comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated
197848ffe56aSColin Percival	rm INDEX-PRESENT.flist INDEX-NEW.flist
197948ffe56aSColin Percival
198048ffe56aSColin Percival	# Report removed files, if any
198148ffe56aSColin Percival	if [ -s files.removed ]; then
1982*fc24ba59SEd Maste		cat - files.removed <<- EOF | ${PAGER}
1983*fc24ba59SEd Maste			The following files will be removed as part of updating to
1984*fc24ba59SEd Maste			${RELNUM}-p${RELPATCHNUM}:
1985*fc24ba59SEd Maste		EOF
1986*fc24ba59SEd Maste	fi
198748ffe56aSColin Percival	rm files.removed
198848ffe56aSColin Percival
198948ffe56aSColin Percival	# Report added files, if any
199048ffe56aSColin Percival	if [ -s files.added ]; then
1991*fc24ba59SEd Maste		cat - files.added <<- EOF | ${PAGER}
1992*fc24ba59SEd Maste			The following files will be added as part of updating to
1993*fc24ba59SEd Maste			${RELNUM}-p${RELPATCHNUM}:
1994*fc24ba59SEd Maste		EOF
1995*fc24ba59SEd Maste	fi
199648ffe56aSColin Percival	rm files.added
199748ffe56aSColin Percival
199848ffe56aSColin Percival	# Report updated files, if any
199948ffe56aSColin Percival	if [ -s files.updated ]; then
2000*fc24ba59SEd Maste		cat - files.updated <<- EOF | ${PAGER}
2001*fc24ba59SEd Maste			The following files will be updated as part of updating to
2002*fc24ba59SEd Maste			${RELNUM}-p${RELPATCHNUM}:
2003*fc24ba59SEd Maste		EOF
2004*fc24ba59SEd Maste	fi
200548ffe56aSColin Percival	rm files.updated
200648ffe56aSColin Percival
200748ffe56aSColin Percival	# Create a directory for the install manifest.
200848ffe56aSColin Percival	MDIR=`mktemp -d install.XXXXXX` || return 1
200948ffe56aSColin Percival
201048ffe56aSColin Percival	# Populate it
201148ffe56aSColin Percival	mv INDEX-PRESENT ${MDIR}/INDEX-OLD
201248ffe56aSColin Percival	mv INDEX-NEW ${MDIR}/INDEX-NEW
201348ffe56aSColin Percival
201448ffe56aSColin Percival	# Link it into place
201548ffe56aSColin Percival	ln -s ${MDIR} ${BDHASH}-install
201648ffe56aSColin Percival}
201748ffe56aSColin Percival
201848ffe56aSColin Percival# Warn about any upcoming EoL
201948ffe56aSColin Percivalfetch_warn_eol () {
202048ffe56aSColin Percival	# What's the current time?
202148ffe56aSColin Percival	NOWTIME=`date "+%s"`
202248ffe56aSColin Percival
202348ffe56aSColin Percival	# When did we last warn about the EoL date?
202448ffe56aSColin Percival	if [ -f lasteolwarn ]; then
202548ffe56aSColin Percival		LASTWARN=`cat lasteolwarn`
202648ffe56aSColin Percival	else
202748ffe56aSColin Percival		LASTWARN=`expr ${NOWTIME} - 63072000`
202848ffe56aSColin Percival	fi
202948ffe56aSColin Percival
203048ffe56aSColin Percival	# If the EoL time is past, warn.
203148ffe56aSColin Percival	if [ ${EOLTIME} -lt ${NOWTIME} ]; then
203248ffe56aSColin Percival		echo
203348ffe56aSColin Percival		cat <<-EOF
2034b698a3abSColin Percival		WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE.
203548ffe56aSColin Percival		Any security issues discovered after `date -r ${EOLTIME}`
203648ffe56aSColin Percival		will not have been corrected.
203748ffe56aSColin Percival		EOF
203848ffe56aSColin Percival		return 1
203948ffe56aSColin Percival	fi
204048ffe56aSColin Percival
204148ffe56aSColin Percival	# Figure out how long it has been since we last warned about the
204248ffe56aSColin Percival	# upcoming EoL, and how much longer we have left.
204348ffe56aSColin Percival	SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}`
204448ffe56aSColin Percival	TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}`
204548ffe56aSColin Percival
204689b14566SColin Percival	# Don't warn if the EoL is more than 3 months away
204789b14566SColin Percival	if [ ${TIMELEFT} -gt 7884000 ]; then
204848ffe56aSColin Percival		return 0
204948ffe56aSColin Percival	fi
205048ffe56aSColin Percival
205148ffe56aSColin Percival	# Don't warn if the time remaining is more than 3 times the time
205248ffe56aSColin Percival	# since the last warning.
205348ffe56aSColin Percival	if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then
205448ffe56aSColin Percival		return 0
205548ffe56aSColin Percival	fi
205648ffe56aSColin Percival
205748ffe56aSColin Percival	# Figure out what time units to use.
205848ffe56aSColin Percival	if [ ${TIMELEFT} -lt 604800 ]; then
205948ffe56aSColin Percival		UNIT="day"
206048ffe56aSColin Percival		SIZE=86400
206148ffe56aSColin Percival	elif [ ${TIMELEFT} -lt 2678400 ]; then
206248ffe56aSColin Percival		UNIT="week"
206348ffe56aSColin Percival		SIZE=604800
206448ffe56aSColin Percival	else
206548ffe56aSColin Percival		UNIT="month"
206648ffe56aSColin Percival		SIZE=2678400
206748ffe56aSColin Percival	fi
206848ffe56aSColin Percival
206948ffe56aSColin Percival	# Compute the right number of units
207048ffe56aSColin Percival	NUM=`expr ${TIMELEFT} / ${SIZE}`
207148ffe56aSColin Percival	if [ ${NUM} != 1 ]; then
207248ffe56aSColin Percival		UNIT="${UNIT}s"
207348ffe56aSColin Percival	fi
207448ffe56aSColin Percival
207548ffe56aSColin Percival	# Print the warning
207648ffe56aSColin Percival	echo
207748ffe56aSColin Percival	cat <<-EOF
207848ffe56aSColin Percival		WARNING: `uname -sr` is approaching its End-of-Life date.
207948ffe56aSColin Percival		It is strongly recommended that you upgrade to a newer
208048ffe56aSColin Percival		release within the next ${NUM} ${UNIT}.
208148ffe56aSColin Percival	EOF
208248ffe56aSColin Percival
208348ffe56aSColin Percival	# Update the stored time of last warning
208448ffe56aSColin Percival	echo ${NOWTIME} > lasteolwarn
208548ffe56aSColin Percival}
208648ffe56aSColin Percival
208748ffe56aSColin Percival# Do the actual work involved in "fetch" / "cron".
208848ffe56aSColin Percivalfetch_run () {
208948ffe56aSColin Percival	workdir_init || return 1
209048ffe56aSColin Percival
209148ffe56aSColin Percival	# Prepare the mirror list.
209248ffe56aSColin Percival	fetch_pick_server_init && fetch_pick_server
209348ffe56aSColin Percival
209448ffe56aSColin Percival	# Try to fetch the public key until we run out of servers.
209548ffe56aSColin Percival	while ! fetch_key; do
209648ffe56aSColin Percival		fetch_pick_server || return 1
209748ffe56aSColin Percival	done
209848ffe56aSColin Percival
209948ffe56aSColin Percival	# Try to fetch the metadata index signature ("tag") until we run
210048ffe56aSColin Percival	# out of available servers; and sanity check the downloaded tag.
210148ffe56aSColin Percival	while ! fetch_tag; do
210248ffe56aSColin Percival		fetch_pick_server || return 1
210348ffe56aSColin Percival	done
210448ffe56aSColin Percival	fetch_tagsanity || return 1
210548ffe56aSColin Percival
210648ffe56aSColin Percival	# Fetch the latest INDEX-NEW and INDEX-OLD files.
210748ffe56aSColin Percival	fetch_metadata INDEX-NEW INDEX-OLD || return 1
210848ffe56aSColin Percival
210948ffe56aSColin Percival	# Generate filtered INDEX-NEW and INDEX-OLD files containing only
211048ffe56aSColin Percival	# the lines which (a) belong to components we care about, and (b)
211148ffe56aSColin Percival	# don't correspond to paths we're explicitly ignoring.
211248ffe56aSColin Percival	fetch_filter_metadata INDEX-NEW || return 1
211348ffe56aSColin Percival	fetch_filter_metadata INDEX-OLD || return 1
211448ffe56aSColin Percival
2115db6b0a61SColin Percival	# Translate /boot/${KERNCONF} into ${KERNELDIR}
2116db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-NEW ${KERNCONF}
2117db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
211848ffe56aSColin Percival
211948ffe56aSColin Percival	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
212048ffe56aSColin Percival	# system and generate an INDEX-PRESENT file.
212148ffe56aSColin Percival	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
212248ffe56aSColin Percival
212348ffe56aSColin Percival	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
212448ffe56aSColin Percival	# correspond to lines in INDEX-PRESENT with hashes not appearing
212548ffe56aSColin Percival	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
212648ffe56aSColin Percival	# INDEX-PRESENT has type - and there isn't a corresponding entry in
212748ffe56aSColin Percival	# INDEX-OLD with type -.
2128db6b0a61SColin Percival	fetch_filter_unmodified_notpresent	\
2129db6b0a61SColin Percival	    INDEX-OLD INDEX-PRESENT INDEX-NEW /dev/null
213048ffe56aSColin Percival
213148ffe56aSColin Percival	# For each entry in INDEX-PRESENT of type -, remove any corresponding
213248ffe56aSColin Percival	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
213348ffe56aSColin Percival	# of type - from INDEX-PRESENT.
213448ffe56aSColin Percival	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
213548ffe56aSColin Percival
213648ffe56aSColin Percival	# If ${ALLOWDELETE} != "yes", then remove any entries from
213748ffe56aSColin Percival	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
213848ffe56aSColin Percival	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
213948ffe56aSColin Percival
214048ffe56aSColin Percival	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
214148ffe56aSColin Percival	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
214248ffe56aSColin Percival	# replace the corresponding line of INDEX-NEW with one having the
214348ffe56aSColin Percival	# same metadata as the entry in INDEX-PRESENT.
214448ffe56aSColin Percival	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
214548ffe56aSColin Percival
214648ffe56aSColin Percival	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
214748ffe56aSColin Percival	# no need to update a file if it isn't changing.
214848ffe56aSColin Percival	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
214948ffe56aSColin Percival
215048ffe56aSColin Percival	# Prepare to fetch files: Generate a list of the files we need,
215148ffe56aSColin Percival	# copy the unmodified files we have into /files/, and generate
215248ffe56aSColin Percival	# a list of patches to download.
2153210b8123SColin Percival	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
215448ffe56aSColin Percival
215548ffe56aSColin Percival	# Fetch files.
215648ffe56aSColin Percival	fetch_files || return 1
215748ffe56aSColin Percival
215848ffe56aSColin Percival	# Create and populate install manifest directory; and report what
215948ffe56aSColin Percival	# updates are available.
216048ffe56aSColin Percival	fetch_create_manifest || return 1
216148ffe56aSColin Percival
216248ffe56aSColin Percival	# Warn about any upcoming EoL
216348ffe56aSColin Percival	fetch_warn_eol || return 1
216448ffe56aSColin Percival}
216548ffe56aSColin Percival
2166db6b0a61SColin Percival# If StrictComponents is not "yes", generate a new components list
2167db6b0a61SColin Percival# with only the components which appear to be installed.
2168db6b0a61SColin Percivalupgrade_guess_components () {
2169db6b0a61SColin Percival	if [ "${STRICTCOMPONENTS}" = "no" ]; then
2170db6b0a61SColin Percival		# Generate filtered INDEX-ALL with only the components listed
2171db6b0a61SColin Percival		# in COMPONENTS.
2172db6b0a61SColin Percival		fetch_filter_metadata_components $1 || return 1
2173db6b0a61SColin Percival
2174db6b0a61SColin Percival		# Tell the user why his disk is suddenly making lots of noise
2175db6b0a61SColin Percival		echo -n "Inspecting system... "
2176db6b0a61SColin Percival
2177db6b0a61SColin Percival		# Look at the files on disk, and assume that a component is
2178db6b0a61SColin Percival		# supposed to be present if it is more than half-present.
2179db6b0a61SColin Percival		cut -f 1-3 -d '|' < INDEX-ALL |
2180db6b0a61SColin Percival		    tr '|' ' ' |
2181db6b0a61SColin Percival		    while read C S F; do
2182db6b0a61SColin Percival			if [ -e ${BASEDIR}/${F} ]; then
2183db6b0a61SColin Percival				echo "+ ${C}|${S}"
2184db6b0a61SColin Percival			fi
2185db6b0a61SColin Percival			echo "= ${C}|${S}"
2186db6b0a61SColin Percival		    done |
2187db6b0a61SColin Percival		    sort |
2188db6b0a61SColin Percival		    uniq -c |
2189db6b0a61SColin Percival		    sed -E 's,^ +,,' > compfreq
2190db6b0a61SColin Percival		grep ' = ' compfreq |
2191db6b0a61SColin Percival		    cut -f 1,3 -d ' ' |
2192db6b0a61SColin Percival		    sort -k 2,2 -t ' ' > compfreq.total
2193db6b0a61SColin Percival		grep ' + ' compfreq |
2194db6b0a61SColin Percival		    cut -f 1,3 -d ' ' |
2195db6b0a61SColin Percival		    sort -k 2,2 -t ' ' > compfreq.present
2196db6b0a61SColin Percival		join -t ' ' -1 2 -2 2 compfreq.present compfreq.total |
2197db6b0a61SColin Percival		    while read S P T; do
2198db6b0a61SColin Percival			if [ ${P} -gt `expr ${T} / 2` ]; then
2199db6b0a61SColin Percival				echo ${S}
2200db6b0a61SColin Percival			fi
2201db6b0a61SColin Percival		    done > comp.present
2202db6b0a61SColin Percival		cut -f 2 -d ' ' < compfreq.total > comp.total
2203db6b0a61SColin Percival		rm INDEX-ALL compfreq compfreq.total compfreq.present
2204db6b0a61SColin Percival
2205db6b0a61SColin Percival		# We're done making noise.
2206db6b0a61SColin Percival		echo "done."
2207db6b0a61SColin Percival
2208db6b0a61SColin Percival		# Sometimes the kernel isn't installed where INDEX-ALL
2209db6b0a61SColin Percival		# thinks that it should be: In particular, it is often in
2210db6b0a61SColin Percival		# /boot/kernel instead of /boot/GENERIC or /boot/SMP.  To
2211db6b0a61SColin Percival		# deal with this, if "kernel|X" is listed in comp.total
2212db6b0a61SColin Percival		# (i.e., is a component which would be upgraded if it is
2213db6b0a61SColin Percival		# found to be present) we will add it to comp.present.
2214db6b0a61SColin Percival		# If "kernel|<anything>" is in comp.total but "kernel|X" is
2215db6b0a61SColin Percival		# not, we print a warning -- the user is running a kernel
2216db6b0a61SColin Percival		# which isn't part of the release.
2217db6b0a61SColin Percival		KCOMP=`echo ${KERNCONF} | tr 'A-Z' 'a-z'`
2218db6b0a61SColin Percival		grep -E "^kernel\|${KCOMP}\$" comp.total >> comp.present
2219db6b0a61SColin Percival
2220db6b0a61SColin Percival		if grep -qE "^kernel\|" comp.total &&
2221db6b0a61SColin Percival		    ! grep -qE "^kernel\|${KCOMP}\$" comp.total; then
2222db6b0a61SColin Percival			cat <<-EOF
2223db6b0a61SColin Percival
2224db6b0a61SColin PercivalWARNING: This system is running a "${KCOMP}" kernel, which is not a
2225db6b0a61SColin Percivalkernel configuration distributed as part of FreeBSD ${RELNUM}.
2226db6b0a61SColin PercivalThis kernel will not be updated: you MUST update the kernel manually
2227db6b0a61SColin Percivalbefore running "$0 install".
2228db6b0a61SColin Percival			EOF
2229db6b0a61SColin Percival		fi
2230db6b0a61SColin Percival
2231db6b0a61SColin Percival		# Re-sort the list of installed components and generate
2232db6b0a61SColin Percival		# the list of non-installed components.
2233db6b0a61SColin Percival		sort -u < comp.present > comp.present.tmp
2234db6b0a61SColin Percival		mv comp.present.tmp comp.present
2235db6b0a61SColin Percival		comm -13 comp.present comp.total > comp.absent
2236db6b0a61SColin Percival
2237db6b0a61SColin Percival		# Ask the user to confirm that what we have is correct.  To
2238db6b0a61SColin Percival		# reduce user confusion, translate "X|Y" back to "X/Y" (as
2239db6b0a61SColin Percival		# subcomponents must be listed in the configuration file).
2240db6b0a61SColin Percival		echo
2241db6b0a61SColin Percival		echo -n "The following components of FreeBSD "
2242db6b0a61SColin Percival		echo "seem to be installed:"
2243db6b0a61SColin Percival		tr '|' '/' < comp.present |
2244db6b0a61SColin Percival		    fmt -72
2245db6b0a61SColin Percival		echo
2246db6b0a61SColin Percival		echo -n "The following components of FreeBSD "
2247db6b0a61SColin Percival		echo "do not seem to be installed:"
2248db6b0a61SColin Percival		tr '|' '/' < comp.absent |
2249db6b0a61SColin Percival		    fmt -72
2250db6b0a61SColin Percival		echo
2251db6b0a61SColin Percival		continuep || return 1
2252db6b0a61SColin Percival		echo
2253db6b0a61SColin Percival
2254db6b0a61SColin Percival		# Suck the generated list of components into ${COMPONENTS}.
2255db6b0a61SColin Percival		# Note that comp.present.tmp is used due to issues with
2256db6b0a61SColin Percival		# pipelines and setting variables.
2257db6b0a61SColin Percival		COMPONENTS=""
2258db6b0a61SColin Percival		tr '|' '/' < comp.present > comp.present.tmp
2259db6b0a61SColin Percival		while read C; do
2260db6b0a61SColin Percival			COMPONENTS="${COMPONENTS} ${C}"
2261db6b0a61SColin Percival		done < comp.present.tmp
2262db6b0a61SColin Percival
2263db6b0a61SColin Percival		# Delete temporary files
2264db6b0a61SColin Percival		rm comp.present comp.present.tmp comp.absent comp.total
2265db6b0a61SColin Percival	fi
2266db6b0a61SColin Percival}
2267db6b0a61SColin Percival
2268db6b0a61SColin Percival# If StrictComponents is not "yes", COMPONENTS contains an entry
2269db6b0a61SColin Percival# corresponding to the currently running kernel, and said kernel
2270db6b0a61SColin Percival# does not exist in the new release, add "kernel/generic" to the
2271db6b0a61SColin Percival# list of components.
2272db6b0a61SColin Percivalupgrade_guess_new_kernel () {
2273db6b0a61SColin Percival	if [ "${STRICTCOMPONENTS}" = "no" ]; then
2274db6b0a61SColin Percival		# Grab the unfiltered metadata file.
2275db6b0a61SColin Percival		METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
2276db6b0a61SColin Percival		gunzip -c < files/${METAHASH}.gz > $1.all
2277db6b0a61SColin Percival
2278db6b0a61SColin Percival		# If "kernel/${KCOMP}" is in ${COMPONENTS} and that component
2279db6b0a61SColin Percival		# isn't in $1.all, we need to add kernel/generic.
2280db6b0a61SColin Percival		for C in ${COMPONENTS}; do
2281db6b0a61SColin Percival			if [ ${C} = "kernel/${KCOMP}" ] &&
2282db6b0a61SColin Percival			    ! grep -qE "^kernel\|${KCOMP}\|" $1.all; then
2283db6b0a61SColin Percival				COMPONENTS="${COMPONENTS} kernel/generic"
2284db6b0a61SColin Percival				NKERNCONF="GENERIC"
2285db6b0a61SColin Percival				cat <<-EOF
2286db6b0a61SColin Percival
2287db6b0a61SColin PercivalWARNING: This system is running a "${KCOMP}" kernel, which is not a
2288db6b0a61SColin Percivalkernel configuration distributed as part of FreeBSD ${RELNUM}.
2289db6b0a61SColin PercivalAs part of upgrading to FreeBSD ${RELNUM}, this kernel will be
2290db6b0a61SColin Percivalreplaced with a "generic" kernel.
2291db6b0a61SColin Percival				EOF
2292db6b0a61SColin Percival				continuep || return 1
2293db6b0a61SColin Percival			fi
2294db6b0a61SColin Percival		done
2295db6b0a61SColin Percival
2296db6b0a61SColin Percival		# Don't need this any more...
2297db6b0a61SColin Percival		rm $1.all
2298db6b0a61SColin Percival	fi
2299db6b0a61SColin Percival}
2300db6b0a61SColin Percival
2301db6b0a61SColin Percival# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2302db6b0a61SColin Percival# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2303db6b0a61SColin Percivalupgrade_oldall_to_oldnew () {
2304db6b0a61SColin Percival	# For each ${F}|... which appears in INDEX-ALL but does not appear
2305db6b0a61SColin Percival	# in INDEX-OLD, add ${F}|-|||||| to INDEX-OLD.
2306db6b0a61SColin Percival	cut -f 1 -d '|' < $1 |
2307db6b0a61SColin Percival	    sort -u > $1.paths
2308db6b0a61SColin Percival	cut -f 1 -d '|' < $2 |
2309db6b0a61SColin Percival	    sort -u |
2310db6b0a61SColin Percival	    comm -13 $1.paths - |
2311db6b0a61SColin Percival	    lam - -s "|-||||||" |
2312db6b0a61SColin Percival	    sort - $1 > $1.tmp
2313db6b0a61SColin Percival	mv $1.tmp $1
2314db6b0a61SColin Percival
2315db6b0a61SColin Percival	# Remove lines from INDEX-OLD which also appear in INDEX-ALL
2316db6b0a61SColin Percival	comm -23 $1 $2 > $1.tmp
2317db6b0a61SColin Percival	mv $1.tmp $1
2318db6b0a61SColin Percival
2319db6b0a61SColin Percival	# Remove lines from INDEX-ALL which have a file name not appearing
2320db6b0a61SColin Percival	# anywhere in INDEX-OLD (since these must be files which haven't
2321db6b0a61SColin Percival	# changed -- if they were new, there would be an entry of type "-").
2322db6b0a61SColin Percival	cut -f 1 -d '|' < $1 |
2323db6b0a61SColin Percival	    sort -u > $1.paths
2324db6b0a61SColin Percival	sort -k 1,1 -t '|' < $2 |
2325db6b0a61SColin Percival	    join -t '|' - $1.paths |
2326db6b0a61SColin Percival	    sort > $2.tmp
2327db6b0a61SColin Percival	rm $1.paths
2328db6b0a61SColin Percival	mv $2.tmp $2
2329db6b0a61SColin Percival
2330db6b0a61SColin Percival	# Rename INDEX-ALL to INDEX-NEW.
2331db6b0a61SColin Percival	mv $2 $3
2332db6b0a61SColin Percival}
2333db6b0a61SColin Percival
23347449d2f5SColin Percival# Helper for upgrade_merge: Return zero true iff the two files differ only
23356d514f10SDag-Erling Smørgrav# in the contents of their RCS tags.
23367449d2f5SColin Percivalsamef () {
23377449d2f5SColin Percival	X=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $1 | ${SHA256}`
23387449d2f5SColin Percival	Y=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $2 | ${SHA256}`
23397449d2f5SColin Percival
23407449d2f5SColin Percival	if [ $X = $Y ]; then
23417449d2f5SColin Percival		return 0;
23427449d2f5SColin Percival	else
23437449d2f5SColin Percival		return 1;
23447449d2f5SColin Percival	fi
23457449d2f5SColin Percival}
23467449d2f5SColin Percival
2347db6b0a61SColin Percival# From the list of "old" files in $1, merge changes in $2 with those in $3,
2348db6b0a61SColin Percival# and update $3 to reflect the hashes of merged files.
2349db6b0a61SColin Percivalupgrade_merge () {
2350db6b0a61SColin Percival	# We only need to do anything if $1 is non-empty.
2351db6b0a61SColin Percival	if [ -s $1 ]; then
2352db6b0a61SColin Percival		cut -f 1 -d '|' $1 |
2353db6b0a61SColin Percival		    sort > $1-paths
2354db6b0a61SColin Percival
2355db6b0a61SColin Percival		# Create staging area for merging files
2356db6b0a61SColin Percival		rm -rf merge/
2357db6b0a61SColin Percival		while read F; do
2358db6b0a61SColin Percival			D=`dirname ${F}`
2359db6b0a61SColin Percival			mkdir -p merge/old/${D}
2360db6b0a61SColin Percival			mkdir -p merge/${OLDRELNUM}/${D}
2361db6b0a61SColin Percival			mkdir -p merge/${RELNUM}/${D}
2362db6b0a61SColin Percival			mkdir -p merge/new/${D}
2363db6b0a61SColin Percival		done < $1-paths
2364db6b0a61SColin Percival
2365db6b0a61SColin Percival		# Copy in files
2366db6b0a61SColin Percival		while read F; do
2367db6b0a61SColin Percival			# Currently installed file
2368db6b0a61SColin Percival			V=`look "${F}|" $2 | cut -f 7 -d '|'`
2369db6b0a61SColin Percival			gunzip < files/${V}.gz > merge/old/${F}
2370db6b0a61SColin Percival
2371db6b0a61SColin Percival			# Old release
2372db6b0a61SColin Percival			if look "${F}|" $1 | fgrep -q "|f|"; then
2373db6b0a61SColin Percival				V=`look "${F}|" $1 | cut -f 3 -d '|'`
2374db6b0a61SColin Percival				gunzip < files/${V}.gz		\
2375db6b0a61SColin Percival				    > merge/${OLDRELNUM}/${F}
2376db6b0a61SColin Percival			fi
2377db6b0a61SColin Percival
2378db6b0a61SColin Percival			# New release
2379db6b0a61SColin Percival			if look "${F}|" $3 | cut -f 1,2,7 -d '|' |
2380db6b0a61SColin Percival			    fgrep -q "|f|"; then
2381db6b0a61SColin Percival				V=`look "${F}|" $3 | cut -f 7 -d '|'`
2382db6b0a61SColin Percival				gunzip < files/${V}.gz		\
2383db6b0a61SColin Percival				    > merge/${RELNUM}/${F}
2384db6b0a61SColin Percival			fi
2385db6b0a61SColin Percival		done < $1-paths
2386db6b0a61SColin Percival
2387db6b0a61SColin Percival		# Attempt to automatically merge changes
2388db6b0a61SColin Percival		echo -n "Attempting to automatically merge "
2389db6b0a61SColin Percival		echo -n "changes in files..."
2390db6b0a61SColin Percival		: > failed.merges
2391db6b0a61SColin Percival		while read F; do
2392db6b0a61SColin Percival			# If the file doesn't exist in the new release,
2393db6b0a61SColin Percival			# the result of "merging changes" is having the file
2394db6b0a61SColin Percival			# not exist.
2395db6b0a61SColin Percival			if ! [ -f merge/${RELNUM}/${F} ]; then
2396db6b0a61SColin Percival				continue
2397db6b0a61SColin Percival			fi
2398db6b0a61SColin Percival
2399db6b0a61SColin Percival			# If the file didn't exist in the old release, we're
2400db6b0a61SColin Percival			# going to throw away the existing file and hope that
2401db6b0a61SColin Percival			# the version from the new release is what we want.
2402db6b0a61SColin Percival			if ! [ -f merge/${OLDRELNUM}/${F} ]; then
2403db6b0a61SColin Percival				cp merge/${RELNUM}/${F} merge/new/${F}
2404db6b0a61SColin Percival				continue
2405db6b0a61SColin Percival			fi
2406db6b0a61SColin Percival
2407db6b0a61SColin Percival			# Some files need special treatment.
2408db6b0a61SColin Percival			case ${F} in
2409db6b0a61SColin Percival			/etc/spwd.db | /etc/pwd.db | /etc/login.conf.db)
2410db6b0a61SColin Percival				# Don't merge these -- we're rebuild them
2411db6b0a61SColin Percival				# after updates are installed.
2412db6b0a61SColin Percival				cp merge/old/${F} merge/new/${F}
2413db6b0a61SColin Percival				;;
2414db6b0a61SColin Percival			*)
2415073dd712SBaptiste Daroussin				if ! diff3 -E -m -L "current version"	\
2416db6b0a61SColin Percival				    -L "${OLDRELNUM}" -L "${RELNUM}"	\
2417db6b0a61SColin Percival				    merge/old/${F}			\
2418db6b0a61SColin Percival				    merge/${OLDRELNUM}/${F}		\
2419db6b0a61SColin Percival				    merge/${RELNUM}/${F}		\
2420db6b0a61SColin Percival				    > merge/new/${F} 2>/dev/null; then
2421db6b0a61SColin Percival					echo ${F} >> failed.merges
2422db6b0a61SColin Percival				fi
2423db6b0a61SColin Percival				;;
2424db6b0a61SColin Percival			esac
2425db6b0a61SColin Percival		done < $1-paths
2426db6b0a61SColin Percival		echo " done."
2427db6b0a61SColin Percival
2428db6b0a61SColin Percival		# Ask the user to handle any files which didn't merge.
2429db6b0a61SColin Percival		while read F; do
24307449d2f5SColin Percival			# If the installed file differs from the version in
24316d514f10SDag-Erling Smørgrav			# the old release only due to RCS tag expansion
24327449d2f5SColin Percival			# then just use the version in the new release.
24337449d2f5SColin Percival			if samef merge/old/${F} merge/${OLDRELNUM}/${F}; then
24347449d2f5SColin Percival				cp merge/${RELNUM}/${F} merge/new/${F}
24357449d2f5SColin Percival				continue
24367449d2f5SColin Percival			fi
24377449d2f5SColin Percival
2438db6b0a61SColin Percival			cat <<-EOF
2439db6b0a61SColin Percival
2440db6b0a61SColin PercivalThe following file could not be merged automatically: ${F}
2441db6b0a61SColin PercivalPress Enter to edit this file in ${EDITOR} and resolve the conflicts
2442db6b0a61SColin Percivalmanually...
2443db6b0a61SColin Percival			EOF
2444db6b0a61SColin Percival			read dummy </dev/tty
2445db6b0a61SColin Percival			${EDITOR} `pwd`/merge/new/${F} < /dev/tty
2446db6b0a61SColin Percival		done < failed.merges
2447db6b0a61SColin Percival		rm failed.merges
2448db6b0a61SColin Percival
2449db6b0a61SColin Percival		# Ask the user to confirm that he likes how the result
2450db6b0a61SColin Percival		# of merging files.
2451db6b0a61SColin Percival		while read F; do
24527449d2f5SColin Percival			# Skip files which haven't changed except possibly
24536d514f10SDag-Erling Smørgrav			# in their RCS tags.
24547449d2f5SColin Percival			if [ -f merge/old/${F} ] && [ -f merge/new/${F} ] &&
24557449d2f5SColin Percival			    samef merge/old/${F} merge/new/${F}; then
24567449d2f5SColin Percival				continue
24577449d2f5SColin Percival			fi
24587449d2f5SColin Percival
24597449d2f5SColin Percival			# Skip files where the installed file differs from
24606d514f10SDag-Erling Smørgrav			# the old file only due to RCS tags.
24617449d2f5SColin Percival			if [ -f merge/old/${F} ] &&
24627449d2f5SColin Percival			    [ -f merge/${OLDRELNUM}/${F} ] &&
24637449d2f5SColin Percival			    samef merge/old/${F} merge/${OLDRELNUM}/${F}; then
2464db6b0a61SColin Percival				continue
2465db6b0a61SColin Percival			fi
2466db6b0a61SColin Percival
2467db6b0a61SColin Percival			# Warn about files which are ceasing to exist.
2468db6b0a61SColin Percival			if ! [ -f merge/new/${F} ]; then
2469db6b0a61SColin Percival				cat <<-EOF
2470db6b0a61SColin Percival
2471db6b0a61SColin PercivalThe following file will be removed, as it no longer exists in
2472db6b0a61SColin PercivalFreeBSD ${RELNUM}: ${F}
2473db6b0a61SColin Percival				EOF
2474db6b0a61SColin Percival				continuep < /dev/tty || return 1
2475db6b0a61SColin Percival				continue
2476db6b0a61SColin Percival			fi
2477db6b0a61SColin Percival
2478db6b0a61SColin Percival			# Print changes for the user's approval.
2479db6b0a61SColin Percival			cat <<-EOF
2480db6b0a61SColin Percival
2481db6b0a61SColin PercivalThe following changes, which occurred between FreeBSD ${OLDRELNUM} and
2482db6b0a61SColin PercivalFreeBSD ${RELNUM} have been merged into ${F}:
2483db6b0a61SColin PercivalEOF
2484db6b0a61SColin Percival			diff -U 5 -L "current version" -L "new version"	\
2485db6b0a61SColin Percival			    merge/old/${F} merge/new/${F} || true
2486db6b0a61SColin Percival			continuep < /dev/tty || return 1
2487db6b0a61SColin Percival		done < $1-paths
2488db6b0a61SColin Percival
2489db6b0a61SColin Percival		# Store merged files.
2490db6b0a61SColin Percival		while read F; do
2491c58b62efSColin Percival			if [ -f merge/new/${F} ]; then
2492db6b0a61SColin Percival				V=`${SHA256} -q merge/new/${F}`
2493db6b0a61SColin Percival
2494db6b0a61SColin Percival				gzip -c < merge/new/${F} > files/${V}.gz
2495db6b0a61SColin Percival				echo "${F}|${V}"
2496db6b0a61SColin Percival			fi
2497db6b0a61SColin Percival		done < $1-paths > newhashes
2498db6b0a61SColin Percival
2499db6b0a61SColin Percival		# Pull lines out from $3 which need to be updated to
2500db6b0a61SColin Percival		# reflect merged files.
2501db6b0a61SColin Percival		while read F; do
2502db6b0a61SColin Percival			look "${F}|" $3
2503db6b0a61SColin Percival		done < $1-paths > $3-oldlines
2504db6b0a61SColin Percival
2505db6b0a61SColin Percival		# Update lines to reflect merged files
2506db6b0a61SColin Percival		join -t '|' -o 1.1,1.2,1.3,1.4,1.5,1.6,2.2,1.8		\
2507db6b0a61SColin Percival		    $3-oldlines newhashes > $3-newlines
2508db6b0a61SColin Percival
2509db6b0a61SColin Percival		# Remove old lines from $3 and add new lines.
2510db6b0a61SColin Percival		sort $3-oldlines |
2511db6b0a61SColin Percival		    comm -13 - $3 |
2512db6b0a61SColin Percival		    sort - $3-newlines > $3.tmp
2513db6b0a61SColin Percival		mv $3.tmp $3
2514db6b0a61SColin Percival
2515db6b0a61SColin Percival		# Clean up
2516db6b0a61SColin Percival		rm $1-paths newhashes $3-oldlines $3-newlines
2517db6b0a61SColin Percival		rm -rf merge/
2518db6b0a61SColin Percival	fi
2519db6b0a61SColin Percival
2520db6b0a61SColin Percival	# We're done with merging files.
2521db6b0a61SColin Percival	rm $1
2522db6b0a61SColin Percival}
2523db6b0a61SColin Percival
2524db6b0a61SColin Percival# Do the work involved in fetching upgrades to a new release
2525db6b0a61SColin Percivalupgrade_run () {
2526db6b0a61SColin Percival	workdir_init || return 1
2527db6b0a61SColin Percival
2528db6b0a61SColin Percival	# Prepare the mirror list.
2529db6b0a61SColin Percival	fetch_pick_server_init && fetch_pick_server
2530db6b0a61SColin Percival
2531db6b0a61SColin Percival	# Try to fetch the public key until we run out of servers.
2532db6b0a61SColin Percival	while ! fetch_key; do
2533db6b0a61SColin Percival		fetch_pick_server || return 1
2534db6b0a61SColin Percival	done
2535db6b0a61SColin Percival
2536db6b0a61SColin Percival	# Try to fetch the metadata index signature ("tag") until we run
2537db6b0a61SColin Percival	# out of available servers; and sanity check the downloaded tag.
2538db6b0a61SColin Percival	while ! fetch_tag; do
2539db6b0a61SColin Percival		fetch_pick_server || return 1
2540db6b0a61SColin Percival	done
2541db6b0a61SColin Percival	fetch_tagsanity || return 1
2542db6b0a61SColin Percival
2543db6b0a61SColin Percival	# Fetch the INDEX-OLD and INDEX-ALL.
2544db6b0a61SColin Percival	fetch_metadata INDEX-OLD INDEX-ALL || return 1
2545db6b0a61SColin Percival
2546db6b0a61SColin Percival	# If StrictComponents is not "yes", generate a new components list
2547db6b0a61SColin Percival	# with only the components which appear to be installed.
2548db6b0a61SColin Percival	upgrade_guess_components INDEX-ALL || return 1
2549db6b0a61SColin Percival
2550db6b0a61SColin Percival	# Generate filtered INDEX-OLD and INDEX-ALL files containing only
2551db6b0a61SColin Percival	# the components we want and without anything marked as "Ignore".
2552db6b0a61SColin Percival	fetch_filter_metadata INDEX-OLD || return 1
2553db6b0a61SColin Percival	fetch_filter_metadata INDEX-ALL || return 1
2554db6b0a61SColin Percival
2555db6b0a61SColin Percival	# Merge the INDEX-OLD and INDEX-ALL files into INDEX-OLD.
2556db6b0a61SColin Percival	sort INDEX-OLD INDEX-ALL > INDEX-OLD.tmp
2557db6b0a61SColin Percival	mv INDEX-OLD.tmp INDEX-OLD
2558db6b0a61SColin Percival	rm INDEX-ALL
2559db6b0a61SColin Percival
2560db6b0a61SColin Percival	# Adjust variables for fetching files from the new release.
2561db6b0a61SColin Percival	OLDRELNUM=${RELNUM}
2562db6b0a61SColin Percival	RELNUM=${TARGETRELEASE}
2563db6b0a61SColin Percival	OLDFETCHDIR=${FETCHDIR}
2564db6b0a61SColin Percival	FETCHDIR=${RELNUM}/${ARCH}
2565db6b0a61SColin Percival
2566db6b0a61SColin Percival	# Try to fetch the NEW metadata index signature ("tag") until we run
2567db6b0a61SColin Percival	# out of available servers; and sanity check the downloaded tag.
2568db6b0a61SColin Percival	while ! fetch_tag; do
2569db6b0a61SColin Percival		fetch_pick_server || return 1
2570db6b0a61SColin Percival	done
2571db6b0a61SColin Percival
2572db6b0a61SColin Percival	# Fetch the new INDEX-ALL.
2573db6b0a61SColin Percival	fetch_metadata INDEX-ALL || return 1
2574db6b0a61SColin Percival
2575db6b0a61SColin Percival	# If StrictComponents is not "yes", COMPONENTS contains an entry
2576db6b0a61SColin Percival	# corresponding to the currently running kernel, and said kernel
2577db6b0a61SColin Percival	# does not exist in the new release, add "kernel/generic" to the
2578db6b0a61SColin Percival	# list of components.
2579db6b0a61SColin Percival	upgrade_guess_new_kernel INDEX-ALL || return 1
2580db6b0a61SColin Percival
2581db6b0a61SColin Percival	# Filter INDEX-ALL to contain only the components we want and without
2582db6b0a61SColin Percival	# anything marked as "Ignore".
2583db6b0a61SColin Percival	fetch_filter_metadata INDEX-ALL || return 1
2584db6b0a61SColin Percival
2585db6b0a61SColin Percival	# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
2586db6b0a61SColin Percival	# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
2587db6b0a61SColin Percival	upgrade_oldall_to_oldnew INDEX-OLD INDEX-ALL INDEX-NEW
2588db6b0a61SColin Percival
2589db6b0a61SColin Percival	# Translate /boot/${KERNCONF} or /boot/${NKERNCONF} into ${KERNELDIR}
2590db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-NEW ${NKERNCONF}
2591db6b0a61SColin Percival	fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
2592db6b0a61SColin Percival
2593db6b0a61SColin Percival	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
2594db6b0a61SColin Percival	# system and generate an INDEX-PRESENT file.
2595db6b0a61SColin Percival	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2596db6b0a61SColin Percival
2597db6b0a61SColin Percival	# Based on ${MERGECHANGES}, generate a file tomerge-old with the
2598db6b0a61SColin Percival	# paths and hashes of old versions of files to merge.
2599db6b0a61SColin Percival	fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT tomerge-old
2600db6b0a61SColin Percival
2601db6b0a61SColin Percival	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
2602db6b0a61SColin Percival	# correspond to lines in INDEX-PRESENT with hashes not appearing
2603db6b0a61SColin Percival	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
2604db6b0a61SColin Percival	# INDEX-PRESENT has type - and there isn't a corresponding entry in
2605db6b0a61SColin Percival	# INDEX-OLD with type -.
2606db6b0a61SColin Percival	fetch_filter_unmodified_notpresent	\
2607db6b0a61SColin Percival	    INDEX-OLD INDEX-PRESENT INDEX-NEW tomerge-old
2608db6b0a61SColin Percival
2609db6b0a61SColin Percival	# For each entry in INDEX-PRESENT of type -, remove any corresponding
2610db6b0a61SColin Percival	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
2611db6b0a61SColin Percival	# of type - from INDEX-PRESENT.
2612db6b0a61SColin Percival	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
2613db6b0a61SColin Percival
2614db6b0a61SColin Percival	# If ${ALLOWDELETE} != "yes", then remove any entries from
2615db6b0a61SColin Percival	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
2616db6b0a61SColin Percival	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
2617db6b0a61SColin Percival
2618db6b0a61SColin Percival	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
2619db6b0a61SColin Percival	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
2620db6b0a61SColin Percival	# replace the corresponding line of INDEX-NEW with one having the
2621db6b0a61SColin Percival	# same metadata as the entry in INDEX-PRESENT.
2622db6b0a61SColin Percival	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
2623db6b0a61SColin Percival
2624db6b0a61SColin Percival	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
2625db6b0a61SColin Percival	# no need to update a file if it isn't changing.
2626db6b0a61SColin Percival	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
2627db6b0a61SColin Percival
2628db6b0a61SColin Percival	# Fetch "clean" files from the old release for merging changes.
2629db6b0a61SColin Percival	fetch_files_premerge tomerge-old
2630db6b0a61SColin Percival
2631db6b0a61SColin Percival	# Prepare to fetch files: Generate a list of the files we need,
2632db6b0a61SColin Percival	# copy the unmodified files we have into /files/, and generate
2633db6b0a61SColin Percival	# a list of patches to download.
2634db6b0a61SColin Percival	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
2635db6b0a61SColin Percival
2636db6b0a61SColin Percival	# Fetch patches from to-${RELNUM}/${ARCH}/bp/
2637db6b0a61SColin Percival	PATCHDIR=to-${RELNUM}/${ARCH}/bp
2638db6b0a61SColin Percival	fetch_files || return 1
2639db6b0a61SColin Percival
2640db6b0a61SColin Percival	# Merge configuration file changes.
2641db6b0a61SColin Percival	upgrade_merge tomerge-old INDEX-PRESENT INDEX-NEW || return 1
2642db6b0a61SColin Percival
2643db6b0a61SColin Percival	# Create and populate install manifest directory; and report what
2644db6b0a61SColin Percival	# updates are available.
2645db6b0a61SColin Percival	fetch_create_manifest || return 1
2646db6b0a61SColin Percival
2647db6b0a61SColin Percival	# Leave a note behind to tell the "install" command that the kernel
2648db6b0a61SColin Percival	# needs to be installed before the world.
2649db6b0a61SColin Percival	touch ${BDHASH}-install/kernelfirst
265085451f90SColin Percival
265185451f90SColin Percival	# Remind the user that they need to run "freebsd-update install"
265285451f90SColin Percival	# to install the downloaded bits, in case they didn't RTFM.
265385451f90SColin Percival	echo "To install the downloaded upgrades, run \"$0 install\"."
2654db6b0a61SColin Percival}
2655db6b0a61SColin Percival
265648ffe56aSColin Percival# Make sure that all the file hashes mentioned in $@ have corresponding
265748ffe56aSColin Percival# gzipped files stored in /files/.
265848ffe56aSColin Percivalinstall_verify () {
265948ffe56aSColin Percival	# Generate a list of hashes
266048ffe56aSColin Percival	cat $@ |
266148ffe56aSColin Percival	    cut -f 2,7 -d '|' |
266248ffe56aSColin Percival	    grep -E '^f' |
266348ffe56aSColin Percival	    cut -f 2 -d '|' |
266448ffe56aSColin Percival	    sort -u > filelist
266548ffe56aSColin Percival
266648ffe56aSColin Percival	# Make sure all the hashes exist
266748ffe56aSColin Percival	while read HASH; do
266848ffe56aSColin Percival		if ! [ -f files/${HASH}.gz ]; then
266948ffe56aSColin Percival			echo -n "Update files missing -- "
267048ffe56aSColin Percival			echo "this should never happen."
267148ffe56aSColin Percival			echo "Re-run '$0 fetch'."
267248ffe56aSColin Percival			return 1
267348ffe56aSColin Percival		fi
267448ffe56aSColin Percival	done < filelist
267548ffe56aSColin Percival
267648ffe56aSColin Percival	# Clean up
267748ffe56aSColin Percival	rm filelist
267848ffe56aSColin Percival}
267948ffe56aSColin Percival
268048ffe56aSColin Percival# Remove the system immutable flag from files
268148ffe56aSColin Percivalinstall_unschg () {
268248ffe56aSColin Percival	# Generate file list
268348ffe56aSColin Percival	cat $@ |
268448ffe56aSColin Percival	    cut -f 1 -d '|' > filelist
268548ffe56aSColin Percival
268648ffe56aSColin Percival	# Remove flags
268748ffe56aSColin Percival	while read F; do
2688e829ed67SColin Percival		if ! [ -e ${BASEDIR}/${F} ]; then
268948ffe56aSColin Percival			continue
26905a74378cSXin LI		else
26915a74378cSXin LI			echo ${BASEDIR}/${F}
269248ffe56aSColin Percival		fi
26935a74378cSXin LI	done < filelist | xargs chflags noschg || return 1
269448ffe56aSColin Percival
269548ffe56aSColin Percival	# Clean up
269648ffe56aSColin Percival	rm filelist
269748ffe56aSColin Percival}
269848ffe56aSColin Percival
269923d827efSSimon L. B. Nielsen# Decide which directory name to use for kernel backups.
270023d827efSSimon L. B. Nielsenbackup_kernel_finddir () {
270123d827efSSimon L. B. Nielsen	CNT=0
270223d827efSSimon L. B. Nielsen	while true ; do
270323d827efSSimon L. B. Nielsen		# Pathname does not exist, so it is OK use that name
270423d827efSSimon L. B. Nielsen		# for backup directory.
2705c4a0c62cSThomas Quinot		if [ ! -e $BASEDIR/$BACKUPKERNELDIR ]; then
270623d827efSSimon L. B. Nielsen			return 0
270723d827efSSimon L. B. Nielsen		fi
270823d827efSSimon L. B. Nielsen
270923d827efSSimon L. B. Nielsen		# If directory do exist, we only use if it has our
271023d827efSSimon L. B. Nielsen		# marker file.
2711c4a0c62cSThomas Quinot		if [ -d $BASEDIR/$BACKUPKERNELDIR -a \
2712c4a0c62cSThomas Quinot			-e $BASEDIR/$BACKUPKERNELDIR/.freebsd-update ]; then
271323d827efSSimon L. B. Nielsen			return 0
271423d827efSSimon L. B. Nielsen		fi
271523d827efSSimon L. B. Nielsen
271623d827efSSimon L. B. Nielsen		# We could not use current directory name, so add counter to
271723d827efSSimon L. B. Nielsen		# the end and try again.
271823d827efSSimon L. B. Nielsen		CNT=$((CNT + 1))
271923d827efSSimon L. B. Nielsen		if [ $CNT -gt 9 ]; then
2720c4a0c62cSThomas Quinot			echo "Could not find valid backup dir ($BASEDIR/$BACKUPKERNELDIR)"
272123d827efSSimon L. B. Nielsen			exit 1
272223d827efSSimon L. B. Nielsen		fi
272323d827efSSimon L. B. Nielsen		BACKUPKERNELDIR="`echo $BACKUPKERNELDIR | sed -Ee 's/[0-9]\$//'`"
272423d827efSSimon L. B. Nielsen		BACKUPKERNELDIR="${BACKUPKERNELDIR}${CNT}"
272523d827efSSimon L. B. Nielsen	done
272623d827efSSimon L. B. Nielsen}
272723d827efSSimon L. B. Nielsen
272823d827efSSimon L. B. Nielsen# Backup the current kernel using hardlinks, if not disabled by user.
272923d827efSSimon L. B. Nielsen# Since we delete all files in the directory used for previous backups
273023d827efSSimon L. B. Nielsen# we create a marker file called ".freebsd-update" in the directory so
273123d827efSSimon L. B. Nielsen# we can determine on the next run that the directory was created by
273223d827efSSimon L. B. Nielsen# freebsd-update and we then do not accidentally remove user files in
273323d827efSSimon L. B. Nielsen# the unlikely case that the user has created a directory with a
273423d827efSSimon L. B. Nielsen# conflicting name.
273523d827efSSimon L. B. Nielsenbackup_kernel () {
273623d827efSSimon L. B. Nielsen	# Only make kernel backup is so configured.
273723d827efSSimon L. B. Nielsen	if [ $BACKUPKERNEL != yes ]; then
273823d827efSSimon L. B. Nielsen		return 0
273923d827efSSimon L. B. Nielsen	fi
274023d827efSSimon L. B. Nielsen
274123d827efSSimon L. B. Nielsen	# Decide which directory name to use for kernel backups.
274223d827efSSimon L. B. Nielsen	backup_kernel_finddir
274323d827efSSimon L. B. Nielsen
274423d827efSSimon L. B. Nielsen	# Remove old kernel backup files.  If $BACKUPKERNELDIR was
274523d827efSSimon L. B. Nielsen	# "not ours", backup_kernel_finddir would have exited, so
274623d827efSSimon L. B. Nielsen	# deleting the directory content is as safe as we can make it.
2747c4a0c62cSThomas Quinot	if [ -d $BASEDIR/$BACKUPKERNELDIR ]; then
2748c4a0c62cSThomas Quinot		rm -fr $BASEDIR/$BACKUPKERNELDIR
274923d827efSSimon L. B. Nielsen	fi
275023d827efSSimon L. B. Nielsen
2751ab7d0151SJaakko Heinonen	# Create directories for backup.
2752c4a0c62cSThomas Quinot	mkdir -p $BASEDIR/$BACKUPKERNELDIR
2753c4a0c62cSThomas Quinot	mtree -cdn -p "${BASEDIR}/${KERNELDIR}" | \
2754c4a0c62cSThomas Quinot	    mtree -Ue -p "${BASEDIR}/${BACKUPKERNELDIR}" > /dev/null
275523d827efSSimon L. B. Nielsen
275623d827efSSimon L. B. Nielsen	# Mark the directory as having been created by freebsd-update.
2757c4a0c62cSThomas Quinot	touch $BASEDIR/$BACKUPKERNELDIR/.freebsd-update
275823d827efSSimon L. B. Nielsen	if [ $? -ne 0 ]; then
275923d827efSSimon L. B. Nielsen		echo "Could not create kernel backup directory"
276023d827efSSimon L. B. Nielsen		exit 1
276123d827efSSimon L. B. Nielsen	fi
276223d827efSSimon L. B. Nielsen
276323d827efSSimon L. B. Nielsen	# Disable pathname expansion to be sure *.symbols is not
276423d827efSSimon L. B. Nielsen	# expanded.
276523d827efSSimon L. B. Nielsen	set -f
276623d827efSSimon L. B. Nielsen
276723d827efSSimon L. B. Nielsen	# Use find to ignore symbol files, unless disabled by user.
276823d827efSSimon L. B. Nielsen	if [ $BACKUPKERNELSYMBOLFILES = yes ]; then
276923d827efSSimon L. B. Nielsen		FINDFILTER=""
277023d827efSSimon L. B. Nielsen	else
27717e1ed2c7SEd Maste		FINDFILTER="-a ! -name *.debug -a ! -name *.symbols"
277223d827efSSimon L. B. Nielsen	fi
277323d827efSSimon L. B. Nielsen
277423d827efSSimon L. B. Nielsen	# Backup all the kernel files using hardlinks.
2775c4a0c62cSThomas Quinot	(cd ${BASEDIR}/${KERNELDIR} && find . -type f $FINDFILTER -exec \
2776c4a0c62cSThomas Quinot	    cp -pl '{}' ${BASEDIR}/${BACKUPKERNELDIR}/'{}' \;)
277723d827efSSimon L. B. Nielsen
277823d827efSSimon L. B. Nielsen	# Re-enable patchname expansion.
277923d827efSSimon L. B. Nielsen	set +f
278023d827efSSimon L. B. Nielsen}
278123d827efSSimon L. B. Nielsen
278248ffe56aSColin Percival# Install new files
278348ffe56aSColin Percivalinstall_from_index () {
278448ffe56aSColin Percival	# First pass: Do everything apart from setting file flags.  We
278548ffe56aSColin Percival	# can't set flags yet, because schg inhibits hard linking.
278648ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
278748ffe56aSColin Percival	    tr '|' ' ' |
278848ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
278948ffe56aSColin Percival		case ${TYPE} in
279048ffe56aSColin Percival		d)
279148ffe56aSColin Percival			# Create a directory
279248ffe56aSColin Percival			install -d -o ${OWNER} -g ${GROUP}		\
279348ffe56aSColin Percival			    -m ${PERM} ${BASEDIR}/${FPATH}
279448ffe56aSColin Percival			;;
279548ffe56aSColin Percival		f)
279648ffe56aSColin Percival			if [ -z "${LINK}" ]; then
279748ffe56aSColin Percival				# Create a file, without setting flags.
279848ffe56aSColin Percival				gunzip < files/${HASH}.gz > ${HASH}
279948ffe56aSColin Percival				install -S -o ${OWNER} -g ${GROUP}	\
280048ffe56aSColin Percival				    -m ${PERM} ${HASH} ${BASEDIR}/${FPATH}
280148ffe56aSColin Percival				rm ${HASH}
280248ffe56aSColin Percival			else
280348ffe56aSColin Percival				# Create a hard link.
2804e829ed67SColin Percival				ln -f ${BASEDIR}/${LINK} ${BASEDIR}/${FPATH}
280548ffe56aSColin Percival			fi
280648ffe56aSColin Percival			;;
280748ffe56aSColin Percival		L)
280848ffe56aSColin Percival			# Create a symlink
280948ffe56aSColin Percival			ln -sfh ${HASH} ${BASEDIR}/${FPATH}
281048ffe56aSColin Percival			;;
281148ffe56aSColin Percival		esac
281248ffe56aSColin Percival	    done
281348ffe56aSColin Percival
281448ffe56aSColin Percival	# Perform a second pass, adding file flags.
281548ffe56aSColin Percival	tr '|' ' ' < $1 |
281648ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
281748ffe56aSColin Percival		if [ ${TYPE} = "f" ] &&
281848ffe56aSColin Percival		    ! [ ${FLAGS} = "0" ]; then
281948ffe56aSColin Percival			chflags ${FLAGS} ${BASEDIR}/${FPATH}
282048ffe56aSColin Percival		fi
282148ffe56aSColin Percival	    done
282248ffe56aSColin Percival}
282348ffe56aSColin Percival
282448ffe56aSColin Percival# Remove files which we want to delete
282548ffe56aSColin Percivalinstall_delete () {
282648ffe56aSColin Percival	# Generate list of new files
282748ffe56aSColin Percival	cut -f 1 -d '|' < $2 |
282848ffe56aSColin Percival	    sort > newfiles
282948ffe56aSColin Percival
283048ffe56aSColin Percival	# Generate subindex of old files we want to nuke
283148ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
283248ffe56aSColin Percival	    join -t '|' -v 1 - newfiles |
2833bce02f98SColin Percival	    sort -r -k 1,1 -t '|' |
283448ffe56aSColin Percival	    cut -f 1,2 -d '|' |
283548ffe56aSColin Percival	    tr '|' ' ' > killfiles
283648ffe56aSColin Percival
283748ffe56aSColin Percival	# Remove the offending bits
283848ffe56aSColin Percival	while read FPATH TYPE; do
283948ffe56aSColin Percival		case ${TYPE} in
284048ffe56aSColin Percival		d)
284148ffe56aSColin Percival			rmdir ${BASEDIR}/${FPATH}
284248ffe56aSColin Percival			;;
284348ffe56aSColin Percival		f)
284448ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
284548ffe56aSColin Percival			;;
284648ffe56aSColin Percival		L)
284748ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
284848ffe56aSColin Percival			;;
284948ffe56aSColin Percival		esac
285048ffe56aSColin Percival	done < killfiles
285148ffe56aSColin Percival
285248ffe56aSColin Percival	# Clean up
285348ffe56aSColin Percival	rm newfiles killfiles
285448ffe56aSColin Percival}
285548ffe56aSColin Percival
2856db6b0a61SColin Percival# Install new files, delete old files, and update linker.hints
2857db6b0a61SColin Percivalinstall_files () {
2858db6b0a61SColin Percival	# If we haven't already dealt with the kernel, deal with it.
2859db6b0a61SColin Percival	if ! [ -f $1/kerneldone ]; then
2860db6b0a61SColin Percival		grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
2861db6b0a61SColin Percival		grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
2862db6b0a61SColin Percival
286323d827efSSimon L. B. Nielsen		# Backup current kernel before installing a new one
286423d827efSSimon L. B. Nielsen		backup_kernel || return 1
286523d827efSSimon L. B. Nielsen
2866db6b0a61SColin Percival		# Install new files
2867db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2868db6b0a61SColin Percival
2869db6b0a61SColin Percival		# Remove files which need to be deleted
2870db6b0a61SColin Percival		install_delete INDEX-OLD INDEX-NEW || return 1
2871db6b0a61SColin Percival
2872db6b0a61SColin Percival		# Update linker.hints if necessary
2873db6b0a61SColin Percival		if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2874c4a0c62cSThomas Quinot			kldxref -R ${BASEDIR}/boot/ 2>/dev/null
287548ffe56aSColin Percival		fi
2876db6b0a61SColin Percival
2877db6b0a61SColin Percival		# We've finished updating the kernel.
2878db6b0a61SColin Percival		touch $1/kerneldone
2879db6b0a61SColin Percival
2880db6b0a61SColin Percival		# Do we need to ask for a reboot now?
2881db6b0a61SColin Percival		if [ -f $1/kernelfirst ] &&
2882db6b0a61SColin Percival		    [ -s INDEX-OLD -o -s INDEX-NEW ]; then
2883db6b0a61SColin Percival			cat <<-EOF
2884db6b0a61SColin Percival
2885db6b0a61SColin PercivalKernel updates have been installed.  Please reboot and run
2886db6b0a61SColin Percival"$0 install" again to finish installing updates.
2887db6b0a61SColin Percival			EOF
2888db6b0a61SColin Percival			exit 0
2889db6b0a61SColin Percival		fi
2890db6b0a61SColin Percival	fi
2891db6b0a61SColin Percival
2892db6b0a61SColin Percival	# If we haven't already dealt with the world, deal with it.
2893db6b0a61SColin Percival	if ! [ -f $1/worlddone ]; then
2894cd1ab228SColin Percival		# Create any necessary directories first
2895cd1ab228SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2896cd1ab228SColin Percival		    grep -E '^[^|]+\|d\|' > INDEX-NEW
2897cd1ab228SColin Percival		install_from_index INDEX-NEW || return 1
2898cd1ab228SColin Percival
2899722d81b5SBrooks Davis		# Install new runtime linker
2900722d81b5SBrooks Davis		grep -vE '^/boot/' $1/INDEX-NEW |
2901722d81b5SBrooks Davis		    grep -vE '^[^|]+\|d\|' |
2902722d81b5SBrooks Davis		    grep -E '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2903722d81b5SBrooks Davis		install_from_index INDEX-NEW || return 1
2904722d81b5SBrooks Davis
2905db6b0a61SColin Percival		# Install new shared libraries next
2906db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2907cd1ab228SColin Percival		    grep -vE '^[^|]+\|d\|' |
2908722d81b5SBrooks Davis		    grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' |
29099546dbd1SColin Percival		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2910db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2911db6b0a61SColin Percival
2912db6b0a61SColin Percival		# Deal with everything else
2913db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-OLD |
2914cd1ab228SColin Percival		    grep -vE '^[^|]+\|d\|' |
2915722d81b5SBrooks Davis		    grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' |
29169546dbd1SColin Percival		    grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD
2917db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
2918cd1ab228SColin Percival		    grep -vE '^[^|]+\|d\|' |
2919722d81b5SBrooks Davis		    grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' |
29209546dbd1SColin Percival		    grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2921db6b0a61SColin Percival		install_from_index INDEX-NEW || return 1
2922db6b0a61SColin Percival		install_delete INDEX-OLD INDEX-NEW || return 1
2923db6b0a61SColin Percival
29249b659110SEd Maste		# Rebuild generated pwd files.
2925c4a0c62cSThomas Quinot		if [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/spwd.db ] ||
29269b659110SEd Maste		    [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/pwd.db ] ||
29279b659110SEd Maste		    [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/passwd ]; then
29289b659110SEd Maste			pwd_mkdb -d ${BASEDIR}/etc -p ${BASEDIR}/etc/master.passwd
2929db6b0a61SColin Percival		fi
2930db6b0a61SColin Percival
2931db6b0a61SColin Percival		# Rebuild /etc/login.conf.db if necessary.
2932c4a0c62cSThomas Quinot		if [ ${BASEDIR}/etc/login.conf -nt ${BASEDIR}/etc/login.conf.db ]; then
2933c4a0c62cSThomas Quinot			cap_mkdb ${BASEDIR}/etc/login.conf
2934db6b0a61SColin Percival		fi
2935db6b0a61SColin Percival
2936db6b0a61SColin Percival		# We've finished installing the world and deleting old files
2937db6b0a61SColin Percival		# which are not shared libraries.
2938db6b0a61SColin Percival		touch $1/worlddone
2939db6b0a61SColin Percival
2940db6b0a61SColin Percival		# Do we need to ask the user to portupgrade now?
2941db6b0a61SColin Percival		grep -vE '^/boot/' $1/INDEX-NEW |
29429546dbd1SColin Percival		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' |
2943db6b0a61SColin Percival		    cut -f 1 -d '|' |
2944db6b0a61SColin Percival		    sort > newfiles
2945db6b0a61SColin Percival		if grep -vE '^/boot/' $1/INDEX-OLD |
29469546dbd1SColin Percival		    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' |
2947db6b0a61SColin Percival		    cut -f 1 -d '|' |
2948db6b0a61SColin Percival		    sort |
2949db6b0a61SColin Percival		    join -v 1 - newfiles |
2950db6b0a61SColin Percival		    grep -q .; then
2951db6b0a61SColin Percival			cat <<-EOF
2952db6b0a61SColin Percival
2953db6b0a61SColin PercivalCompleting this upgrade requires removing old shared object files.
2954db6b0a61SColin PercivalPlease rebuild all installed 3rd party software (e.g., programs
2955db6b0a61SColin Percivalinstalled from the ports tree) and then run "$0 install"
2956db6b0a61SColin Percivalagain to finish installing updates.
2957db6b0a61SColin Percival			EOF
2958db6b0a61SColin Percival			rm newfiles
2959db6b0a61SColin Percival			exit 0
2960db6b0a61SColin Percival		fi
2961db6b0a61SColin Percival		rm newfiles
2962db6b0a61SColin Percival	fi
2963db6b0a61SColin Percival
2964db6b0a61SColin Percival	# Remove old shared libraries
2965db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2966cd1ab228SColin Percival	    grep -vE '^[^|]+\|d\|' |
29679546dbd1SColin Percival	    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW
2968db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2969cd1ab228SColin Percival	    grep -vE '^[^|]+\|d\|' |
29709546dbd1SColin Percival	    grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD
2971db6b0a61SColin Percival	install_delete INDEX-OLD INDEX-NEW || return 1
2972db6b0a61SColin Percival
2973cd1ab228SColin Percival	# Remove old directories
2974ebc1d19cSColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
2975ebc1d19cSColin Percival	    grep -E '^[^|]+\|d\|' > INDEX-NEW
2976cd1ab228SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
2977cd1ab228SColin Percival	    grep -E '^[^|]+\|d\|' > INDEX-OLD
2978cd1ab228SColin Percival	install_delete INDEX-OLD INDEX-NEW || return 1
2979cd1ab228SColin Percival
2980db6b0a61SColin Percival	# Remove temporary files
2981db6b0a61SColin Percival	rm INDEX-OLD INDEX-NEW
298248ffe56aSColin Percival}
298348ffe56aSColin Percival
298448ffe56aSColin Percival# Rearrange bits to allow the installed updates to be rolled back
298548ffe56aSColin Percivalinstall_setup_rollback () {
2986db6b0a61SColin Percival	# Remove the "reboot after installing kernel", "kernel updated", and
2987db6b0a61SColin Percival	# "finished installing the world" flags if present -- they are
2988db6b0a61SColin Percival	# irrelevant when rolling back updates.
2989db6b0a61SColin Percival	if [ -f ${BDHASH}-install/kernelfirst ]; then
2990db6b0a61SColin Percival		rm ${BDHASH}-install/kernelfirst
2991db6b0a61SColin Percival		rm ${BDHASH}-install/kerneldone
2992db6b0a61SColin Percival	fi
2993db6b0a61SColin Percival	if [ -f ${BDHASH}-install/worlddone ]; then
2994db6b0a61SColin Percival		rm ${BDHASH}-install/worlddone
2995db6b0a61SColin Percival	fi
2996db6b0a61SColin Percival
299748ffe56aSColin Percival	if [ -L ${BDHASH}-rollback ]; then
299848ffe56aSColin Percival		mv ${BDHASH}-rollback ${BDHASH}-install/rollback
299948ffe56aSColin Percival	fi
300048ffe56aSColin Percival
300148ffe56aSColin Percival	mv ${BDHASH}-install ${BDHASH}-rollback
300248ffe56aSColin Percival}
300348ffe56aSColin Percival
300448ffe56aSColin Percival# Actually install updates
300548ffe56aSColin Percivalinstall_run () {
300648ffe56aSColin Percival	echo -n "Installing updates..."
300748ffe56aSColin Percival
300848ffe56aSColin Percival	# Make sure we have all the files we should have
300948ffe56aSColin Percival	install_verify ${BDHASH}-install/INDEX-OLD	\
301048ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
301148ffe56aSColin Percival
301248ffe56aSColin Percival	# Remove system immutable flag from files
301348ffe56aSColin Percival	install_unschg ${BDHASH}-install/INDEX-OLD	\
301448ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
301548ffe56aSColin Percival
3016db6b0a61SColin Percival	# Install new files, delete old files, and update linker.hints
3017db6b0a61SColin Percival	install_files ${BDHASH}-install || return 1
301848ffe56aSColin Percival
301948ffe56aSColin Percival	# Rearrange bits to allow the installed updates to be rolled back
302048ffe56aSColin Percival	install_setup_rollback
302148ffe56aSColin Percival
302248ffe56aSColin Percival	echo " done."
302348ffe56aSColin Percival}
302448ffe56aSColin Percival
302548ffe56aSColin Percival# Rearrange bits to allow the previous set of updates to be rolled back next.
302648ffe56aSColin Percivalrollback_setup_rollback () {
302748ffe56aSColin Percival	if [ -L ${BDHASH}-rollback/rollback ]; then
302848ffe56aSColin Percival		mv ${BDHASH}-rollback/rollback rollback-tmp
302948ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
303048ffe56aSColin Percival		rm ${BDHASH}-rollback
303148ffe56aSColin Percival		mv rollback-tmp ${BDHASH}-rollback
303248ffe56aSColin Percival	else
303348ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
303448ffe56aSColin Percival		rm ${BDHASH}-rollback
303548ffe56aSColin Percival	fi
303648ffe56aSColin Percival}
303748ffe56aSColin Percival
3038db6b0a61SColin Percival# Install old files, delete new files, and update linker.hints
3039db6b0a61SColin Percivalrollback_files () {
30401ec4fb3aSColin Percival	# Install old shared library files which don't have the same path as
30411ec4fb3aSColin Percival	# a new shared library file.
30421ec4fb3aSColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
3043fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
30441ec4fb3aSColin Percival	    cut -f 1 -d '|' |
30451ec4fb3aSColin Percival	    sort > INDEX-NEW.libs.flist
3046db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
3047fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
30481ec4fb3aSColin Percival	    sort -k 1,1 -t '|' - |
30491ec4fb3aSColin Percival	    join -t '|' -v 1 - INDEX-NEW.libs.flist > INDEX-OLD
3050db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
3051db6b0a61SColin Percival
3052db6b0a61SColin Percival	# Deal with files which are neither kernel nor shared library
3053db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
3054fd0963d1SColin Percival	    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
3055db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
3056fd0963d1SColin Percival	    grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
3057db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
3058db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
3059db6b0a61SColin Percival
30601ec4fb3aSColin Percival	# Install any old shared library files which we didn't install above.
30611ec4fb3aSColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
3062fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' |
30631ec4fb3aSColin Percival	    sort -k 1,1 -t '|' - |
30641ec4fb3aSColin Percival	    join -t '|' - INDEX-NEW.libs.flist > INDEX-OLD
30651ec4fb3aSColin Percival	install_from_index INDEX-OLD || return 1
30661ec4fb3aSColin Percival
3067db6b0a61SColin Percival	# Delete unneeded shared library files
3068db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-OLD |
3069fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
3070db6b0a61SColin Percival	grep -vE '^/boot/' $1/INDEX-NEW |
3071fd0963d1SColin Percival	    grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
3072db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
3073db6b0a61SColin Percival
3074db6b0a61SColin Percival	# Deal with kernel files
3075db6b0a61SColin Percival	grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
3076db6b0a61SColin Percival	grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
3077db6b0a61SColin Percival	install_from_index INDEX-OLD || return 1
3078db6b0a61SColin Percival	install_delete INDEX-NEW INDEX-OLD || return 1
3079db6b0a61SColin Percival	if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
3080db6b0a61SColin Percival		kldxref -R /boot/ 2>/dev/null
3081db6b0a61SColin Percival	fi
3082db6b0a61SColin Percival
3083db6b0a61SColin Percival	# Remove temporary files
30840e0d8d5aSColin Percival	rm INDEX-OLD INDEX-NEW INDEX-NEW.libs.flist
3085db6b0a61SColin Percival}
3086db6b0a61SColin Percival
308748ffe56aSColin Percival# Actually rollback updates
308848ffe56aSColin Percivalrollback_run () {
308948ffe56aSColin Percival	echo -n "Uninstalling updates..."
309048ffe56aSColin Percival
309148ffe56aSColin Percival	# If there are updates waiting to be installed, remove them; we
309248ffe56aSColin Percival	# want the user to re-run 'fetch' after rolling back updates.
309348ffe56aSColin Percival	if [ -L ${BDHASH}-install ]; then
309448ffe56aSColin Percival		rm -r ${BDHASH}-install/
309548ffe56aSColin Percival		rm ${BDHASH}-install
309648ffe56aSColin Percival	fi
309748ffe56aSColin Percival
309848ffe56aSColin Percival	# Make sure we have all the files we should have
309948ffe56aSColin Percival	install_verify ${BDHASH}-rollback/INDEX-NEW	\
310048ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
310148ffe56aSColin Percival
310248ffe56aSColin Percival	# Remove system immutable flag from files
310348ffe56aSColin Percival	install_unschg ${BDHASH}-rollback/INDEX-NEW	\
310448ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
310548ffe56aSColin Percival
3106db6b0a61SColin Percival	# Install old files, delete new files, and update linker.hints
3107db6b0a61SColin Percival	rollback_files ${BDHASH}-rollback || return 1
310848ffe56aSColin Percival
310948ffe56aSColin Percival	# Remove the rollback directory and the symlink pointing to it; and
311048ffe56aSColin Percival	# rearrange bits to allow the previous set of updates to be rolled
311148ffe56aSColin Percival	# back next.
311248ffe56aSColin Percival	rollback_setup_rollback
311348ffe56aSColin Percival
311448ffe56aSColin Percival	echo " done."
311548ffe56aSColin Percival}
311648ffe56aSColin Percival
311708e23beeSColin Percival# Compare INDEX-ALL and INDEX-PRESENT and print warnings about differences.
311808e23beeSColin PercivalIDS_compare () {
3119bb10a826SColin Percival	# Get all the lines which mismatch in something other than file
3120bb10a826SColin Percival	# flags.  We ignore file flags because sysinstall doesn't seem to
3121bb10a826SColin Percival	# set them when it installs FreeBSD; warning about these adds a
3122bb10a826SColin Percival	# very large amount of noise.
3123bb10a826SColin Percival	cut -f 1-5,7-8 -d '|' $1 > $1.noflags
3124bb10a826SColin Percival	sort -k 1,1 -t '|' $1.noflags > $1.sorted
3125bb10a826SColin Percival	cut -f 1-5,7-8 -d '|' $2 |
3126bb10a826SColin Percival	    comm -13 $1.noflags - |
3127bb10a826SColin Percival	    fgrep -v '|-|||||' |
312808e23beeSColin Percival	    sort -k 1,1 -t '|' |
312908e23beeSColin Percival	    join -t '|' $1.sorted - > INDEX-NOTMATCHING
313008e23beeSColin Percival
313108e23beeSColin Percival	# Ignore files which match IDSIGNOREPATHS.
313208e23beeSColin Percival	for X in ${IDSIGNOREPATHS}; do
313308e23beeSColin Percival		grep -E "^${X}" INDEX-NOTMATCHING
313408e23beeSColin Percival	done |
313508e23beeSColin Percival	    sort -u |
313608e23beeSColin Percival	    comm -13 - INDEX-NOTMATCHING > INDEX-NOTMATCHING.tmp
313708e23beeSColin Percival	mv INDEX-NOTMATCHING.tmp INDEX-NOTMATCHING
313808e23beeSColin Percival
313908e23beeSColin Percival	# Go through the lines and print warnings.
3140aa60062eSColin Percival	local IFS='|'
3141aa60062eSColin Percival	while read FPATH TYPE OWNER GROUP PERM HASH LINK P_TYPE P_OWNER P_GROUP P_PERM P_HASH P_LINK; do
314208e23beeSColin Percival		# Warn about different object types.
314308e23beeSColin Percival		if ! [ "${TYPE}" = "${P_TYPE}" ]; then
314408e23beeSColin Percival			echo -n "${FPATH} is a "
314508e23beeSColin Percival			case "${P_TYPE}" in
314608e23beeSColin Percival			f)	echo -n "regular file, "
314708e23beeSColin Percival				;;
314808e23beeSColin Percival			d)	echo -n "directory, "
314908e23beeSColin Percival				;;
315008e23beeSColin Percival			L)	echo -n "symlink, "
315108e23beeSColin Percival				;;
315208e23beeSColin Percival			esac
315308e23beeSColin Percival			echo -n "but should be a "
315408e23beeSColin Percival			case "${TYPE}" in
315508e23beeSColin Percival			f)	echo -n "regular file."
315608e23beeSColin Percival				;;
315708e23beeSColin Percival			d)	echo -n "directory."
315808e23beeSColin Percival				;;
315908e23beeSColin Percival			L)	echo -n "symlink."
316008e23beeSColin Percival				;;
316108e23beeSColin Percival			esac
316208e23beeSColin Percival			echo
316308e23beeSColin Percival
316408e23beeSColin Percival			# Skip other tests, since they don't make sense if
316508e23beeSColin Percival			# we're comparing different object types.
316608e23beeSColin Percival			continue
316708e23beeSColin Percival		fi
316808e23beeSColin Percival
316908e23beeSColin Percival		# Warn about different owners.
317008e23beeSColin Percival		if ! [ "${OWNER}" = "${P_OWNER}" ]; then
317108e23beeSColin Percival			echo -n "${FPATH} is owned by user id ${P_OWNER}, "
317208e23beeSColin Percival			echo "but should be owned by user id ${OWNER}."
317308e23beeSColin Percival		fi
317408e23beeSColin Percival
317508e23beeSColin Percival		# Warn about different groups.
317608e23beeSColin Percival		if ! [ "${GROUP}" = "${P_GROUP}" ]; then
317708e23beeSColin Percival			echo -n "${FPATH} is owned by group id ${P_GROUP}, "
317808e23beeSColin Percival			echo "but should be owned by group id ${GROUP}."
317908e23beeSColin Percival		fi
318008e23beeSColin Percival
318108e23beeSColin Percival		# Warn about different permissions.  We do not warn about
318208e23beeSColin Percival		# different permissions on symlinks, since some archivers
318308e23beeSColin Percival		# don't extract symlink permissions correctly and they are
318408e23beeSColin Percival		# ignored anyway.
318508e23beeSColin Percival		if ! [ "${PERM}" = "${P_PERM}" ] &&
318608e23beeSColin Percival		    ! [ "${TYPE}" = "L" ]; then
318708e23beeSColin Percival			echo -n "${FPATH} has ${P_PERM} permissions, "
318808e23beeSColin Percival			echo "but should have ${PERM} permissions."
318908e23beeSColin Percival		fi
319008e23beeSColin Percival
319108e23beeSColin Percival		# Warn about different file hashes / symlink destinations.
319208e23beeSColin Percival		if ! [ "${HASH}" = "${P_HASH}" ]; then
319308e23beeSColin Percival			if [ "${TYPE}" = "L" ]; then
319408e23beeSColin Percival				echo -n "${FPATH} is a symlink to ${P_HASH}, "
319508e23beeSColin Percival				echo "but should be a symlink to ${HASH}."
319608e23beeSColin Percival			fi
319708e23beeSColin Percival			if [ "${TYPE}" = "f" ]; then
319808e23beeSColin Percival				echo -n "${FPATH} has SHA256 hash ${P_HASH}, "
319908e23beeSColin Percival				echo "but should have SHA256 hash ${HASH}."
320008e23beeSColin Percival			fi
320108e23beeSColin Percival		fi
320208e23beeSColin Percival
320308e23beeSColin Percival		# We don't warn about different hard links, since some
320408e23beeSColin Percival		# some archivers break hard links, and as long as the
320508e23beeSColin Percival		# underlying data is correct they really don't matter.
320608e23beeSColin Percival	done < INDEX-NOTMATCHING
320708e23beeSColin Percival
320808e23beeSColin Percival	# Clean up
3209bb10a826SColin Percival	rm $1 $1.noflags $1.sorted $2 INDEX-NOTMATCHING
321008e23beeSColin Percival}
321108e23beeSColin Percival
321208e23beeSColin Percival# Do the work involved in comparing the system to a "known good" index
321308e23beeSColin PercivalIDS_run () {
321408e23beeSColin Percival	workdir_init || return 1
321508e23beeSColin Percival
321608e23beeSColin Percival	# Prepare the mirror list.
321708e23beeSColin Percival	fetch_pick_server_init && fetch_pick_server
321808e23beeSColin Percival
321908e23beeSColin Percival	# Try to fetch the public key until we run out of servers.
322008e23beeSColin Percival	while ! fetch_key; do
322108e23beeSColin Percival		fetch_pick_server || return 1
322208e23beeSColin Percival	done
322308e23beeSColin Percival
322408e23beeSColin Percival	# Try to fetch the metadata index signature ("tag") until we run
322508e23beeSColin Percival	# out of available servers; and sanity check the downloaded tag.
322608e23beeSColin Percival	while ! fetch_tag; do
322708e23beeSColin Percival		fetch_pick_server || return 1
322808e23beeSColin Percival	done
322908e23beeSColin Percival	fetch_tagsanity || return 1
323008e23beeSColin Percival
323108e23beeSColin Percival	# Fetch INDEX-OLD and INDEX-ALL.
323208e23beeSColin Percival	fetch_metadata INDEX-OLD INDEX-ALL || return 1
323308e23beeSColin Percival
323408e23beeSColin Percival	# Generate filtered INDEX-OLD and INDEX-ALL files containing only
323508e23beeSColin Percival	# the components we want and without anything marked as "Ignore".
323608e23beeSColin Percival	fetch_filter_metadata INDEX-OLD || return 1
323708e23beeSColin Percival	fetch_filter_metadata INDEX-ALL || return 1
323808e23beeSColin Percival
323908e23beeSColin Percival	# Merge the INDEX-OLD and INDEX-ALL files into INDEX-ALL.
324008e23beeSColin Percival	sort INDEX-OLD INDEX-ALL > INDEX-ALL.tmp
324108e23beeSColin Percival	mv INDEX-ALL.tmp INDEX-ALL
324208e23beeSColin Percival	rm INDEX-OLD
324308e23beeSColin Percival
324408e23beeSColin Percival	# Translate /boot/${KERNCONF} to ${KERNELDIR}
324508e23beeSColin Percival	fetch_filter_kernel_names INDEX-ALL ${KERNCONF}
324608e23beeSColin Percival
324708e23beeSColin Percival	# Inspect the system and generate an INDEX-PRESENT file.
324808e23beeSColin Percival	fetch_inspect_system INDEX-ALL INDEX-PRESENT /dev/null || return 1
324908e23beeSColin Percival
325008e23beeSColin Percival	# Compare INDEX-ALL and INDEX-PRESENT and print warnings about any
325108e23beeSColin Percival	# differences.
325208e23beeSColin Percival	IDS_compare INDEX-ALL INDEX-PRESENT
325308e23beeSColin Percival}
325408e23beeSColin Percival
325548ffe56aSColin Percival#### Main functions -- call parameter-handling and core functions
325648ffe56aSColin Percival
325748ffe56aSColin Percival# Using the command line, configuration file, and defaults,
325848ffe56aSColin Percival# set all the parameters which are needed later.
325948ffe56aSColin Percivalget_params () {
326048ffe56aSColin Percival	init_params
326148ffe56aSColin Percival	parse_cmdline $@
326248ffe56aSColin Percival	parse_conffile
326348ffe56aSColin Percival	default_params
326448ffe56aSColin Percival}
326548ffe56aSColin Percival
326648ffe56aSColin Percival# Fetch command.  Make sure that we're being called
326748ffe56aSColin Percival# interactively, then run fetch_check_params and fetch_run
326848ffe56aSColin Percivalcmd_fetch () {
32698bf2dcceSAllan Jude	if [ ! -t 0 -a $NOTTYOK -eq 0 ]; then
327048ffe56aSColin Percival		echo -n "`basename $0` fetch should not "
327148ffe56aSColin Percival		echo "be run non-interactively."
327248ffe56aSColin Percival		echo "Run `basename $0` cron instead."
327348ffe56aSColin Percival		exit 1
327448ffe56aSColin Percival	fi
327548ffe56aSColin Percival	fetch_check_params
327648ffe56aSColin Percival	fetch_run || exit 1
327733bd05c3SGuangyuan Yang	ISFETCHED=1
327848ffe56aSColin Percival}
327948ffe56aSColin Percival
328048ffe56aSColin Percival# Cron command.  Make sure the parameters are sensible; wait
328148ffe56aSColin Percival# rand(3600) seconds; then fetch updates.  While fetching updates,
328248ffe56aSColin Percival# send output to a temporary file; only print that file if the
328348ffe56aSColin Percival# fetching failed.
328448ffe56aSColin Percivalcmd_cron () {
328548ffe56aSColin Percival	fetch_check_params
328648ffe56aSColin Percival	sleep `jot -r 1 0 3600`
328748ffe56aSColin Percival
328848ffe56aSColin Percival	TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1
328948ffe56aSColin Percival	if ! fetch_run >> ${TMPFILE} ||
329048ffe56aSColin Percival	    ! grep -q "No updates needed" ${TMPFILE} ||
329148ffe56aSColin Percival	    [ ${VERBOSELEVEL} = "debug" ]; then
329248ffe56aSColin Percival		mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE}
329348ffe56aSColin Percival	fi
329448ffe56aSColin Percival
329548ffe56aSColin Percival	rm ${TMPFILE}
329648ffe56aSColin Percival}
329748ffe56aSColin Percival
3298db6b0a61SColin Percival# Fetch files for upgrading to a new release.
3299db6b0a61SColin Percivalcmd_upgrade () {
3300db6b0a61SColin Percival	upgrade_check_params
3301db6b0a61SColin Percival	upgrade_run || exit 1
3302db6b0a61SColin Percival}
3303db6b0a61SColin Percival
330448ffe56aSColin Percival# Install downloaded updates.
330548ffe56aSColin Percivalcmd_install () {
330648ffe56aSColin Percival	install_check_params
330748ffe56aSColin Percival	install_run || exit 1
330848ffe56aSColin Percival}
330948ffe56aSColin Percival
331048ffe56aSColin Percival# Rollback most recently installed updates.
331148ffe56aSColin Percivalcmd_rollback () {
331248ffe56aSColin Percival	rollback_check_params
331348ffe56aSColin Percival	rollback_run || exit 1
331448ffe56aSColin Percival}
331548ffe56aSColin Percival
331608e23beeSColin Percival# Compare system against a "known good" index.
331708e23beeSColin Percivalcmd_IDS () {
331808e23beeSColin Percival	IDS_check_params
331908e23beeSColin Percival	IDS_run || exit 1
332008e23beeSColin Percival}
332108e23beeSColin Percival
332248ffe56aSColin Percival#### Entry point
332348ffe56aSColin Percival
332448ffe56aSColin Percival# Make sure we find utilities from the base system
332548ffe56aSColin Percivalexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
332648ffe56aSColin Percival
33279c990fb2SGordon Tetlow# Set a pager if the user doesn't
33289c990fb2SGordon Tetlowif [ -z "$PAGER" ]; then
332947cc9ee1SAlan Somers	PAGER=/usr/bin/less
33309c990fb2SGordon Tetlowfi
33319c990fb2SGordon Tetlow
3332f2890dbdSColin Percival# Set LC_ALL in order to avoid problems with character ranges like [A-Z].
3333f2890dbdSColin Percivalexport LC_ALL=C
3334f2890dbdSColin Percival
333548ffe56aSColin Percivalget_params $@
333648ffe56aSColin Percivalfor COMMAND in ${COMMANDS}; do
333748ffe56aSColin Percival	cmd_${COMMAND}
333848ffe56aSColin Percivaldone
3339