xref: /freebsd/usr.sbin/freebsd-update/freebsd-update.sh (revision 2c434b2cc37b0e2861133dc15769dcdec6485a0f)
148ffe56aSColin Percival#!/bin/sh
248ffe56aSColin Percival
348ffe56aSColin Percival#-
448ffe56aSColin Percival# Copyright 2004-2006 Colin Percival
548ffe56aSColin Percival# All rights reserved
648ffe56aSColin Percival#
748ffe56aSColin Percival# Redistribution and use in source and binary forms, with or without
848ffe56aSColin Percival# modification, are permitted providing that the following conditions
948ffe56aSColin Percival# are met:
1048ffe56aSColin Percival# 1. Redistributions of source code must retain the above copyright
1148ffe56aSColin Percival#    notice, this list of conditions and the following disclaimer.
1248ffe56aSColin Percival# 2. Redistributions in binary form must reproduce the above copyright
1348ffe56aSColin Percival#    notice, this list of conditions and the following disclaimer in the
1448ffe56aSColin Percival#    documentation and/or other materials provided with the distribution.
1548ffe56aSColin Percival#
1648ffe56aSColin Percival# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1748ffe56aSColin Percival# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1848ffe56aSColin Percival# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1948ffe56aSColin Percival# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
2048ffe56aSColin Percival# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2148ffe56aSColin Percival# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2248ffe56aSColin Percival# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2348ffe56aSColin Percival# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2448ffe56aSColin Percival# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
2548ffe56aSColin Percival# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2648ffe56aSColin Percival# POSSIBILITY OF SUCH DAMAGE.
2748ffe56aSColin Percival
2848ffe56aSColin Percival# $FreeBSD$
2948ffe56aSColin Percival
3048ffe56aSColin Percival#### Usage function -- called from command-line handling code.
3148ffe56aSColin Percival
3248ffe56aSColin Percival# Usage instructions.  Options not listed:
3348ffe56aSColin Percival# --debug	-- don't filter output from utilities
3448ffe56aSColin Percival# --no-stats	-- don't show progress statistics while fetching files
3548ffe56aSColin Percivalusage () {
3648ffe56aSColin Percival	cat <<EOF
3748ffe56aSColin Percivalusage: `basename $0` [options] command ... [path]
3848ffe56aSColin Percival
3948ffe56aSColin PercivalOptions:
4048ffe56aSColin Percival  -b basedir   -- Operate on a system mounted at basedir
4148ffe56aSColin Percival                  (default: /)
4248ffe56aSColin Percival  -d workdir   -- Store working files in workdir
4348ffe56aSColin Percival                  (default: /var/db/freebsd-update/)
4448ffe56aSColin Percival  -f conffile  -- Read configuration options from conffile
4548ffe56aSColin Percival                  (default: /etc/freebsd-update.conf)
4648ffe56aSColin Percival  -k KEY       -- Trust an RSA key with SHA256 hash of KEY
4748ffe56aSColin Percival  -s server    -- Server from which to fetch updates
4848ffe56aSColin Percival                  (default: update.FreeBSD.org)
4948ffe56aSColin Percival  -t address   -- Mail output of cron command, if any, to address
5048ffe56aSColin Percival                  (default: root)
5148ffe56aSColin PercivalCommands:
5248ffe56aSColin Percival  fetch        -- Fetch updates from server
5348ffe56aSColin Percival  cron         -- Sleep rand(3600) seconds, fetch updates, and send an
5448ffe56aSColin Percival                  email if updates were found
5548ffe56aSColin Percival  install      -- Install downloaded updates
5648ffe56aSColin Percival  rollback     -- Uninstall most recently installed updates
5748ffe56aSColin PercivalEOF
5848ffe56aSColin Percival	exit 0
5948ffe56aSColin Percival}
6048ffe56aSColin Percival
6148ffe56aSColin Percival#### Configuration processing functions
6248ffe56aSColin Percival
6348ffe56aSColin Percival#-
6448ffe56aSColin Percival# Configuration options are set in the following order of priority:
6548ffe56aSColin Percival# 1. Command line options
6648ffe56aSColin Percival# 2. Configuration file options
6748ffe56aSColin Percival# 3. Default options
6848ffe56aSColin Percival# In addition, certain options (e.g., IgnorePaths) can be specified multiple
6948ffe56aSColin Percival# times and (as long as these are all in the same place, e.g., inside the
7048ffe56aSColin Percival# configuration file) they will accumulate.  Finally, because the path to the
7148ffe56aSColin Percival# configuration file can be specified at the command line, the entire command
7248ffe56aSColin Percival# line must be processed before we start reading the configuration file.
7348ffe56aSColin Percival#
7448ffe56aSColin Percival# Sound like a mess?  It is.  Here's how we handle this:
7548ffe56aSColin Percival# 1. Initialize CONFFILE and all the options to "".
7648ffe56aSColin Percival# 2. Process the command line.  Throw an error if a non-accumulating option
7748ffe56aSColin Percival#    is specified twice.
7848ffe56aSColin Percival# 3. If CONFFILE is "", set CONFFILE to /etc/freebsd-update.conf .
7948ffe56aSColin Percival# 4. For all the configuration options X, set X_saved to X.
8048ffe56aSColin Percival# 5. Initialize all the options to "".
8148ffe56aSColin Percival# 6. Read CONFFILE line by line, parsing options.
8248ffe56aSColin Percival# 7. For each configuration option X, set X to X_saved iff X_saved is not "".
8348ffe56aSColin Percival# 8. Repeat steps 4-7, except setting options to their default values at (6).
8448ffe56aSColin Percival
8548ffe56aSColin PercivalCONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE
8648ffe56aSColin Percival    KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED
8748ffe56aSColin Percival    BASEDIR VERBOSELEVEL"
8848ffe56aSColin Percival
8948ffe56aSColin Percival# Set all the configuration options to "".
9048ffe56aSColin Percivalnullconfig () {
9148ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
9248ffe56aSColin Percival		eval ${X}=""
9348ffe56aSColin Percival	done
9448ffe56aSColin Percival}
9548ffe56aSColin Percival
9648ffe56aSColin Percival# For each configuration option X, set X_saved to X.
9748ffe56aSColin Percivalsaveconfig () {
9848ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
9948ffe56aSColin Percival		eval ${X}_saved=\$${X}
10048ffe56aSColin Percival	done
10148ffe56aSColin Percival}
10248ffe56aSColin Percival
10348ffe56aSColin Percival# For each configuration option X, set X to X_saved if X_saved is not "".
10448ffe56aSColin Percivalmergeconfig () {
10548ffe56aSColin Percival	for X in ${CONFIGOPTIONS}; do
10648ffe56aSColin Percival		eval _=\$${X}_saved
10748ffe56aSColin Percival		if ! [ -z "${_}" ]; then
10848ffe56aSColin Percival			eval ${X}=\$${X}_saved
10948ffe56aSColin Percival		fi
11048ffe56aSColin Percival	done
11148ffe56aSColin Percival}
11248ffe56aSColin Percival
11348ffe56aSColin Percival# Set the trusted keyprint.
11448ffe56aSColin Percivalconfig_KeyPrint () {
11548ffe56aSColin Percival	if [ -z ${KEYPRINT} ]; then
11648ffe56aSColin Percival		KEYPRINT=$1
11748ffe56aSColin Percival	else
11848ffe56aSColin Percival		return 1
11948ffe56aSColin Percival	fi
12048ffe56aSColin Percival}
12148ffe56aSColin Percival
12248ffe56aSColin Percival# Set the working directory.
12348ffe56aSColin Percivalconfig_WorkDir () {
12448ffe56aSColin Percival	if [ -z ${WORKDIR} ]; then
12548ffe56aSColin Percival		WORKDIR=$1
12648ffe56aSColin Percival	else
12748ffe56aSColin Percival		return 1
12848ffe56aSColin Percival	fi
12948ffe56aSColin Percival}
13048ffe56aSColin Percival
13148ffe56aSColin Percival# Set the name of the server (pool) from which to fetch updates
13248ffe56aSColin Percivalconfig_ServerName () {
13348ffe56aSColin Percival	if [ -z ${SERVERNAME} ]; then
13448ffe56aSColin Percival		SERVERNAME=$1
13548ffe56aSColin Percival	else
13648ffe56aSColin Percival		return 1
13748ffe56aSColin Percival	fi
13848ffe56aSColin Percival}
13948ffe56aSColin Percival
14048ffe56aSColin Percival# Set the address to which 'cron' output will be mailed.
14148ffe56aSColin Percivalconfig_MailTo () {
14248ffe56aSColin Percival	if [ -z ${MAILTO} ]; then
14348ffe56aSColin Percival		MAILTO=$1
14448ffe56aSColin Percival	else
14548ffe56aSColin Percival		return 1
14648ffe56aSColin Percival	fi
14748ffe56aSColin Percival}
14848ffe56aSColin Percival
14948ffe56aSColin Percival# Set whether FreeBSD Update is allowed to add files (or directories, or
15048ffe56aSColin Percival# symlinks) which did not previously exist.
15148ffe56aSColin Percivalconfig_AllowAdd () {
15248ffe56aSColin Percival	if [ -z ${ALLOWADD} ]; then
15348ffe56aSColin Percival		case $1 in
15448ffe56aSColin Percival		[Yy][Ee][Ss])
15548ffe56aSColin Percival			ALLOWADD=yes
15648ffe56aSColin Percival			;;
15748ffe56aSColin Percival		[Nn][Oo])
15848ffe56aSColin Percival			ALLOWADD=no
15948ffe56aSColin Percival			;;
16048ffe56aSColin Percival		*)
16148ffe56aSColin Percival			return 1
16248ffe56aSColin Percival			;;
16348ffe56aSColin Percival		esac
16448ffe56aSColin Percival	else
16548ffe56aSColin Percival		return 1
16648ffe56aSColin Percival	fi
16748ffe56aSColin Percival}
16848ffe56aSColin Percival
16948ffe56aSColin Percival# Set whether FreeBSD Update is allowed to remove files/directories/symlinks.
17048ffe56aSColin Percivalconfig_AllowDelete () {
17148ffe56aSColin Percival	if [ -z ${ALLOWDELETE} ]; then
17248ffe56aSColin Percival		case $1 in
17348ffe56aSColin Percival		[Yy][Ee][Ss])
17448ffe56aSColin Percival			ALLOWDELETE=yes
17548ffe56aSColin Percival			;;
17648ffe56aSColin Percival		[Nn][Oo])
17748ffe56aSColin Percival			ALLOWDELETE=no
17848ffe56aSColin Percival			;;
17948ffe56aSColin Percival		*)
18048ffe56aSColin Percival			return 1
18148ffe56aSColin Percival			;;
18248ffe56aSColin Percival		esac
18348ffe56aSColin Percival	else
18448ffe56aSColin Percival		return 1
18548ffe56aSColin Percival	fi
18648ffe56aSColin Percival}
18748ffe56aSColin Percival
18848ffe56aSColin Percival# Set whether FreeBSD Update should keep existing inode ownership,
18948ffe56aSColin Percival# permissions, and flags, in the event that they have been modified locally
19048ffe56aSColin Percival# after the release.
19148ffe56aSColin Percivalconfig_KeepModifiedMetadata () {
19248ffe56aSColin Percival	if [ -z ${KEEPMODIFIEDMETADATA} ]; then
19348ffe56aSColin Percival		case $1 in
19448ffe56aSColin Percival		[Yy][Ee][Ss])
19548ffe56aSColin Percival			KEEPMODIFIEDMETADATA=yes
19648ffe56aSColin Percival			;;
19748ffe56aSColin Percival		[Nn][Oo])
19848ffe56aSColin Percival			KEEPMODIFIEDMETADATA=no
19948ffe56aSColin Percival			;;
20048ffe56aSColin Percival		*)
20148ffe56aSColin Percival			return 1
20248ffe56aSColin Percival			;;
20348ffe56aSColin Percival		esac
20448ffe56aSColin Percival	else
20548ffe56aSColin Percival		return 1
20648ffe56aSColin Percival	fi
20748ffe56aSColin Percival}
20848ffe56aSColin Percival
20948ffe56aSColin Percival# Add to the list of components which should be kept updated.
21048ffe56aSColin Percivalconfig_Components () {
21148ffe56aSColin Percival	for C in $@; do
21248ffe56aSColin Percival		COMPONENTS="${COMPONENTS} ${C}"
21348ffe56aSColin Percival	done
21448ffe56aSColin Percival}
21548ffe56aSColin Percival
21648ffe56aSColin Percival# Add to the list of paths under which updates will be ignored.
21748ffe56aSColin Percivalconfig_IgnorePaths () {
21848ffe56aSColin Percival	for C in $@; do
21948ffe56aSColin Percival		IGNOREPATHS="${IGNOREPATHS} ${C}"
22048ffe56aSColin Percival	done
22148ffe56aSColin Percival}
22248ffe56aSColin Percival
22348ffe56aSColin Percival# Add to the list of paths within which updates will be performed only if the
22448ffe56aSColin Percival# file on disk has not been modified locally.
22548ffe56aSColin Percivalconfig_UpdateIfUnmodified () {
22648ffe56aSColin Percival	for C in $@; do
22748ffe56aSColin Percival		UPDATEIFUNMODIFIED="${UPDATEIFUNMODIFIED} ${C}"
22848ffe56aSColin Percival	done
22948ffe56aSColin Percival}
23048ffe56aSColin Percival
23148ffe56aSColin Percival# Work on a FreeBSD installation mounted under $1
23248ffe56aSColin Percivalconfig_BaseDir () {
23348ffe56aSColin Percival	if [ -z ${BASEDIR} ]; then
23448ffe56aSColin Percival		BASEDIR=$1
23548ffe56aSColin Percival	else
23648ffe56aSColin Percival		return 1
23748ffe56aSColin Percival	fi
23848ffe56aSColin Percival}
23948ffe56aSColin Percival
24048ffe56aSColin Percival# Define what happens to output of utilities
24148ffe56aSColin Percivalconfig_VerboseLevel () {
24248ffe56aSColin Percival	if [ -z ${VERBOSELEVEL} ]; then
24348ffe56aSColin Percival		case $1 in
24448ffe56aSColin Percival		[Dd][Ee][Bb][Uu][Gg])
24548ffe56aSColin Percival			VERBOSELEVEL=debug
24648ffe56aSColin Percival			;;
24748ffe56aSColin Percival		[Nn][Oo][Ss][Tt][Aa][Tt][Ss])
24848ffe56aSColin Percival			VERBOSELEVEL=nostats
24948ffe56aSColin Percival			;;
25048ffe56aSColin Percival		[Ss][Tt][Aa][Tt][Ss])
25148ffe56aSColin Percival			VERBOSELEVEL=stats
25248ffe56aSColin Percival			;;
25348ffe56aSColin Percival		*)
25448ffe56aSColin Percival			return 1
25548ffe56aSColin Percival			;;
25648ffe56aSColin Percival		esac
25748ffe56aSColin Percival	else
25848ffe56aSColin Percival		return 1
25948ffe56aSColin Percival	fi
26048ffe56aSColin Percival}
26148ffe56aSColin Percival
26248ffe56aSColin Percival# Handle one line of configuration
26348ffe56aSColin Percivalconfigline () {
26448ffe56aSColin Percival	if [ $# -eq 0 ]; then
26548ffe56aSColin Percival		return
26648ffe56aSColin Percival	fi
26748ffe56aSColin Percival
26848ffe56aSColin Percival	OPT=$1
26948ffe56aSColin Percival	shift
27048ffe56aSColin Percival	config_${OPT} $@
27148ffe56aSColin Percival}
27248ffe56aSColin Percival
27348ffe56aSColin Percival#### Parameter handling functions.
27448ffe56aSColin Percival
27548ffe56aSColin Percival# Initialize parameters to null, just in case they're
27648ffe56aSColin Percival# set in the environment.
27748ffe56aSColin Percivalinit_params () {
27848ffe56aSColin Percival	# Configration settings
27948ffe56aSColin Percival	nullconfig
28048ffe56aSColin Percival
28148ffe56aSColin Percival	# No configuration file set yet
28248ffe56aSColin Percival	CONFFILE=""
28348ffe56aSColin Percival
28448ffe56aSColin Percival	# No commands specified yet
28548ffe56aSColin Percival	COMMANDS=""
28648ffe56aSColin Percival}
28748ffe56aSColin Percival
28848ffe56aSColin Percival# Parse the command line
28948ffe56aSColin Percivalparse_cmdline () {
29048ffe56aSColin Percival	while [ $# -gt 0 ]; do
29148ffe56aSColin Percival		case "$1" in
29248ffe56aSColin Percival		# Location of configuration file
29348ffe56aSColin Percival		-f)
29448ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi
29548ffe56aSColin Percival			if [ ! -z "${CONFFILE}" ]; then usage; fi
29648ffe56aSColin Percival			shift; CONFFILE="$1"
29748ffe56aSColin Percival			;;
29848ffe56aSColin Percival
29948ffe56aSColin Percival		# Configuration file equivalents
30048ffe56aSColin Percival		-b)
30148ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
30248ffe56aSColin Percival			config_BaseDir $1 || usage
30348ffe56aSColin Percival			;;
30448ffe56aSColin Percival		-d)
30548ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
30648ffe56aSColin Percival			config_WorkDir $1 || usage
30748ffe56aSColin Percival			;;
30848ffe56aSColin Percival		-k)
30948ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
31048ffe56aSColin Percival			config_KeyPrint $1 || usage
31148ffe56aSColin Percival			;;
31248ffe56aSColin Percival		-s)
31348ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
31448ffe56aSColin Percival			config_ServerName $1 || usage
31548ffe56aSColin Percival			;;
31648ffe56aSColin Percival		-t)
31748ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
31848ffe56aSColin Percival			config_MailTo $1 || usage
31948ffe56aSColin Percival			;;
32048ffe56aSColin Percival		-v)
32148ffe56aSColin Percival			if [ $# -eq 1 ]; then usage; fi; shift
32248ffe56aSColin Percival			config_VerboseLevel $1 || usage
32348ffe56aSColin Percival			;;
32448ffe56aSColin Percival
32548ffe56aSColin Percival		# Aliases for "-v debug" and "-v nostats"
32648ffe56aSColin Percival		--debug)
32748ffe56aSColin Percival			config_VerboseLevel debug || usage
32848ffe56aSColin Percival			;;
32948ffe56aSColin Percival		--no-stats)
33048ffe56aSColin Percival			config_VerboseLevel nostats || usage
33148ffe56aSColin Percival			;;
33248ffe56aSColin Percival
33348ffe56aSColin Percival		# Commands
33448ffe56aSColin Percival		cron | fetch | install | rollback)
33548ffe56aSColin Percival			COMMANDS="${COMMANDS} $1"
33648ffe56aSColin Percival			;;
33748ffe56aSColin Percival
33848ffe56aSColin Percival		# Anything else is an error
33948ffe56aSColin Percival		*)
34048ffe56aSColin Percival			usage
34148ffe56aSColin Percival			;;
34248ffe56aSColin Percival		esac
34348ffe56aSColin Percival		shift
34448ffe56aSColin Percival	done
34548ffe56aSColin Percival
34648ffe56aSColin Percival	# Make sure we have at least one command
34748ffe56aSColin Percival	if [ -z "${COMMANDS}" ]; then
34848ffe56aSColin Percival		usage
34948ffe56aSColin Percival	fi
35048ffe56aSColin Percival}
35148ffe56aSColin Percival
35248ffe56aSColin Percival# Parse the configuration file
35348ffe56aSColin Percivalparse_conffile () {
35448ffe56aSColin Percival	# If a configuration file was specified on the command line, check
35548ffe56aSColin Percival	# that it exists and is readable.
35648ffe56aSColin Percival	if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
35748ffe56aSColin Percival		echo -n "File does not exist "
35848ffe56aSColin Percival		echo -n "or is not readable: "
35948ffe56aSColin Percival		echo ${CONFFILE}
36048ffe56aSColin Percival		exit 1
36148ffe56aSColin Percival	fi
36248ffe56aSColin Percival
36348ffe56aSColin Percival	# If a configuration file was not specified on the command line,
36448ffe56aSColin Percival	# use the default configuration file path.  If that default does
36548ffe56aSColin Percival	# not exist, give up looking for any configuration.
36648ffe56aSColin Percival	if [ -z "${CONFFILE}" ]; then
36748ffe56aSColin Percival		CONFFILE="/etc/freebsd-update.conf"
36848ffe56aSColin Percival		if [ ! -r "${CONFFILE}" ]; then
36948ffe56aSColin Percival			return
37048ffe56aSColin Percival		fi
37148ffe56aSColin Percival	fi
37248ffe56aSColin Percival
37348ffe56aSColin Percival	# Save the configuration options specified on the command line, and
37448ffe56aSColin Percival	# clear all the options in preparation for reading the config file.
37548ffe56aSColin Percival	saveconfig
37648ffe56aSColin Percival	nullconfig
37748ffe56aSColin Percival
37848ffe56aSColin Percival	# Read the configuration file.  Anything after the first '#' is
37948ffe56aSColin Percival	# ignored, and any blank lines are ignored.
38048ffe56aSColin Percival	L=0
38148ffe56aSColin Percival	while read LINE; do
38248ffe56aSColin Percival		L=$(($L + 1))
38348ffe56aSColin Percival		LINEX=`echo "${LINE}" | cut -f 1 -d '#'`
38448ffe56aSColin Percival		if ! configline ${LINEX}; then
38548ffe56aSColin Percival			echo "Error processing configuration file, line $L:"
38648ffe56aSColin Percival			echo "==> ${LINE}"
38748ffe56aSColin Percival			exit 1
38848ffe56aSColin Percival		fi
38948ffe56aSColin Percival	done < ${CONFFILE}
39048ffe56aSColin Percival
39148ffe56aSColin Percival	# Merge the settings read from the configuration file with those
39248ffe56aSColin Percival	# provided at the command line.
39348ffe56aSColin Percival	mergeconfig
39448ffe56aSColin Percival}
39548ffe56aSColin Percival
39648ffe56aSColin Percival# Provide some default parameters
39748ffe56aSColin Percivaldefault_params () {
39848ffe56aSColin Percival	# Save any parameters already configured, and clear the slate
39948ffe56aSColin Percival	saveconfig
40048ffe56aSColin Percival	nullconfig
40148ffe56aSColin Percival
40248ffe56aSColin Percival	# Default configurations
40348ffe56aSColin Percival	config_WorkDir /var/db/freebsd-update
40448ffe56aSColin Percival	config_MailTo root
40548ffe56aSColin Percival	config_AllowAdd yes
40648ffe56aSColin Percival	config_AllowDelete yes
40748ffe56aSColin Percival	config_KeepModifiedMetadata yes
40848ffe56aSColin Percival	config_BaseDir /
40948ffe56aSColin Percival	config_VerboseLevel stats
41048ffe56aSColin Percival
41148ffe56aSColin Percival	# Merge these defaults into the earlier-configured settings
41248ffe56aSColin Percival	mergeconfig
41348ffe56aSColin Percival}
41448ffe56aSColin Percival
41548ffe56aSColin Percival# Set utility output filtering options, based on ${VERBOSELEVEL}
41648ffe56aSColin Percivalfetch_setup_verboselevel () {
41748ffe56aSColin Percival	case ${VERBOSELEVEL} in
41848ffe56aSColin Percival	debug)
41948ffe56aSColin Percival		QUIETREDIR="/dev/stderr"
42048ffe56aSColin Percival		QUIETFLAG=" "
42148ffe56aSColin Percival		STATSREDIR="/dev/stderr"
42248ffe56aSColin Percival		DDSTATS=".."
42348ffe56aSColin Percival		XARGST="-t"
42448ffe56aSColin Percival		NDEBUG=" "
42548ffe56aSColin Percival		;;
42648ffe56aSColin Percival	nostats)
42748ffe56aSColin Percival		QUIETREDIR=""
42848ffe56aSColin Percival		QUIETFLAG=""
42948ffe56aSColin Percival		STATSREDIR="/dev/null"
43048ffe56aSColin Percival		DDSTATS=".."
43148ffe56aSColin Percival		XARGST=""
43248ffe56aSColin Percival		NDEBUG=""
43348ffe56aSColin Percival		;;
43448ffe56aSColin Percival	stats)
43548ffe56aSColin Percival		QUIETREDIR="/dev/null"
43648ffe56aSColin Percival		QUIETFLAG="-q"
43748ffe56aSColin Percival		STATSREDIR="/dev/stdout"
43848ffe56aSColin Percival		DDSTATS=""
43948ffe56aSColin Percival		XARGST=""
44048ffe56aSColin Percival		NDEBUG="-n"
44148ffe56aSColin Percival		;;
44248ffe56aSColin Percival	esac
44348ffe56aSColin Percival}
44448ffe56aSColin Percival
44548ffe56aSColin Percival# Perform sanity checks and set some final parameters
44648ffe56aSColin Percival# in preparation for fetching files.  Figure out which
44748ffe56aSColin Percival# set of updates should be downloaded: If the user is
44848ffe56aSColin Percival# running *-p[0-9]+, strip off the last part; if the
44948ffe56aSColin Percival# user is running -SECURITY, call it -RELEASE.  Chdir
45048ffe56aSColin Percival# into the working directory.
45148ffe56aSColin Percivalfetch_check_params () {
45248ffe56aSColin Percival	export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
45348ffe56aSColin Percival
45448ffe56aSColin Percival	_SERVERNAME_z=\
45548ffe56aSColin Percival"SERVERNAME must be given via command line or configuration file."
45648ffe56aSColin Percival	_KEYPRINT_z="Key must be given via -k option or configuration file."
45748ffe56aSColin Percival	_KEYPRINT_bad="Invalid key fingerprint: "
45848ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
45948ffe56aSColin Percival
46048ffe56aSColin Percival	if [ -z "${SERVERNAME}" ]; then
46148ffe56aSColin Percival		echo -n "`basename $0`: "
46248ffe56aSColin Percival		echo "${_SERVERNAME_z}"
46348ffe56aSColin Percival		exit 1
46448ffe56aSColin Percival	fi
46548ffe56aSColin Percival	if [ -z "${KEYPRINT}" ]; then
46648ffe56aSColin Percival		echo -n "`basename $0`: "
46748ffe56aSColin Percival		echo "${_KEYPRINT_z}"
46848ffe56aSColin Percival		exit 1
46948ffe56aSColin Percival	fi
47048ffe56aSColin Percival	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
47148ffe56aSColin Percival		echo -n "`basename $0`: "
47248ffe56aSColin Percival		echo -n "${_KEYPRINT_bad}"
47348ffe56aSColin Percival		echo ${KEYPRINT}
47448ffe56aSColin Percival		exit 1
47548ffe56aSColin Percival	fi
47648ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
47748ffe56aSColin Percival		echo -n "`basename $0`: "
47848ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
47948ffe56aSColin Percival		echo ${WORKDIR}
48048ffe56aSColin Percival		exit 1
48148ffe56aSColin Percival	fi
48248ffe56aSColin Percival	cd ${WORKDIR} || exit 1
48348ffe56aSColin Percival
48448ffe56aSColin Percival	# Generate release number.  The s/SECURITY/RELEASE/ bit exists
48548ffe56aSColin Percival	# to provide an upgrade path for FreeBSD Update 1.x users, since
48648ffe56aSColin Percival	# the kernels provided by FreeBSD Update 1.x are always labelled
48748ffe56aSColin Percival	# as X.Y-SECURITY.
48848ffe56aSColin Percival	RELNUM=`uname -r |
48948ffe56aSColin Percival	    sed -E 's,-p[0-9]+,,' |
49048ffe56aSColin Percival	    sed -E 's,-SECURITY,-RELEASE,'`
49148ffe56aSColin Percival	ARCH=`uname -m`
49248ffe56aSColin Percival	FETCHDIR=${RELNUM}/${ARCH}
49348ffe56aSColin Percival
49448ffe56aSColin Percival	# Figure out what directory contains the running kernel
49548ffe56aSColin Percival	BOOTFILE=`sysctl -n kern.bootfile`
49648ffe56aSColin Percival	KERNELDIR=${BOOTFILE%/kernel}
49748ffe56aSColin Percival	if ! [ -d ${KERNELDIR} ]; then
49848ffe56aSColin Percival		echo "Cannot identify running kernel"
49948ffe56aSColin Percival		exit 1
50048ffe56aSColin Percival	fi
50148ffe56aSColin Percival
5022c434b2cSColin Percival	# Figure out what kernel configuration is running.  We start with
5032c434b2cSColin Percival	# the output of `uname -i`, and then make the following adjustments:
5042c434b2cSColin Percival	# 1. Replace "SMP-GENERIC" with "SMP".  Why the SMP kernel config
5052c434b2cSColin Percival	# file says "ident SMP-GENERIC", I don't know...
5062c434b2cSColin Percival	# 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
5072c434b2cSColin Percival	# _and_ `sysctl kern.version` contains a line which ends "/SMP", then
5082c434b2cSColin Percival	# we're running an SMP kernel.  This mis-identification is a bug
5092c434b2cSColin Percival	# which was fixed in 6.2-STABLE.
5102c434b2cSColin Percival	KERNCONF=`uname -i`
5112c434b2cSColin Percival	if [ ${KERNCONF} = "SMP-GENERIC" ]; then
5122c434b2cSColin Percival		KERNCONF=SMP
5132c434b2cSColin Percival	fi
5142c434b2cSColin Percival	if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
5152c434b2cSColin Percival		if sysctl kern.version | grep -qE '/SMP$'; then
5162c434b2cSColin Percival			KERNCONF=SMP
5172c434b2cSColin Percival		fi
5182c434b2cSColin Percival	fi
5192c434b2cSColin Percival
52048ffe56aSColin Percival	# Define some paths
52148ffe56aSColin Percival	BSPATCH=/usr/bin/bspatch
52248ffe56aSColin Percival	SHA256=/sbin/sha256
52348ffe56aSColin Percival	PHTTPGET=/usr/libexec/phttpget
52448ffe56aSColin Percival
52548ffe56aSColin Percival	# Set up variables relating to VERBOSELEVEL
52648ffe56aSColin Percival	fetch_setup_verboselevel
52748ffe56aSColin Percival
52848ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
52948ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
53048ffe56aSColin Percival}
53148ffe56aSColin Percival
53248ffe56aSColin Percival# Perform sanity checks and set some final parameters in
53348ffe56aSColin Percival# preparation for installing updates.
53448ffe56aSColin Percivalinstall_check_params () {
53548ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
53648ffe56aSColin Percival	if [ `id -u` != 0 ]; then
53748ffe56aSColin Percival		echo "You must be root to run this."
53848ffe56aSColin Percival		exit 1
53948ffe56aSColin Percival	fi
54048ffe56aSColin Percival
54148ffe56aSColin Percival	# Check that we have a working directory
54248ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
54348ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
54448ffe56aSColin Percival		echo -n "`basename $0`: "
54548ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
54648ffe56aSColin Percival		echo ${WORKDIR}
54748ffe56aSColin Percival		exit 1
54848ffe56aSColin Percival	fi
54948ffe56aSColin Percival	cd ${WORKDIR} || exit 1
55048ffe56aSColin Percival
55148ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
55248ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
55348ffe56aSColin Percival
55448ffe56aSColin Percival	# Check that we have updates ready to install
55548ffe56aSColin Percival	if ! [ -L ${BDHASH}-install ]; then
55648ffe56aSColin Percival		echo "No updates are available to install."
55748ffe56aSColin Percival		echo "Run '$0 fetch' first."
55848ffe56aSColin Percival		exit 1
55948ffe56aSColin Percival	fi
56048ffe56aSColin Percival	if ! [ -f ${BDHASH}-install/INDEX-OLD ] ||
56148ffe56aSColin Percival	    ! [ -f ${BDHASH}-install/INDEX-NEW ]; then
56248ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
56348ffe56aSColin Percival		echo "Re-run '$0 fetch'."
56448ffe56aSColin Percival		exit 1
56548ffe56aSColin Percival	fi
56648ffe56aSColin Percival}
56748ffe56aSColin Percival
56848ffe56aSColin Percival# Perform sanity checks and set some final parameters in
56948ffe56aSColin Percival# preparation for UNinstalling updates.
57048ffe56aSColin Percivalrollback_check_params () {
57148ffe56aSColin Percival	# Check that we are root.  All sorts of things won't work otherwise.
57248ffe56aSColin Percival	if [ `id -u` != 0 ]; then
57348ffe56aSColin Percival		echo "You must be root to run this."
57448ffe56aSColin Percival		exit 1
57548ffe56aSColin Percival	fi
57648ffe56aSColin Percival
57748ffe56aSColin Percival	# Check that we have a working directory
57848ffe56aSColin Percival	_WORKDIR_bad="Directory does not exist or is not writable: "
57948ffe56aSColin Percival	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
58048ffe56aSColin Percival		echo -n "`basename $0`: "
58148ffe56aSColin Percival		echo -n "${_WORKDIR_bad}"
58248ffe56aSColin Percival		echo ${WORKDIR}
58348ffe56aSColin Percival		exit 1
58448ffe56aSColin Percival	fi
58548ffe56aSColin Percival	cd ${WORKDIR} || exit 1
58648ffe56aSColin Percival
58748ffe56aSColin Percival	# Construct a unique name from ${BASEDIR}
58848ffe56aSColin Percival	BDHASH=`echo ${BASEDIR} | sha256 -q`
58948ffe56aSColin Percival
59048ffe56aSColin Percival	# Check that we have updates ready to rollback
59148ffe56aSColin Percival	if ! [ -L ${BDHASH}-rollback ]; then
59248ffe56aSColin Percival		echo "No rollback directory found."
59348ffe56aSColin Percival		exit 1
59448ffe56aSColin Percival	fi
59548ffe56aSColin Percival	if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] ||
59648ffe56aSColin Percival	    ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then
59748ffe56aSColin Percival		echo "Update manifest is corrupt -- this should never happen."
59848ffe56aSColin Percival		exit 1
59948ffe56aSColin Percival	fi
60048ffe56aSColin Percival}
60148ffe56aSColin Percival
60248ffe56aSColin Percival#### Core functionality -- the actual work gets done here
60348ffe56aSColin Percival
60448ffe56aSColin Percival# Use an SRV query to pick a server.  If the SRV query doesn't provide
60548ffe56aSColin Percival# a useful answer, use the server name specified by the user.
60648ffe56aSColin Percival# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
60748ffe56aSColin Percival# from that; or if no servers are returned, use ${SERVERNAME}.
60848ffe56aSColin Percival# This allows a user to specify "portsnap.freebsd.org" (in which case
60948ffe56aSColin Percival# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
61048ffe56aSColin Percival# (in which case portsnap will use that particular server, since there
61148ffe56aSColin Percival# won't be an SRV entry for that name).
61248ffe56aSColin Percival#
61348ffe56aSColin Percival# We ignore the Port field, since we are always going to use port 80.
61448ffe56aSColin Percival
61548ffe56aSColin Percival# Fetch the mirror list, but do not pick a mirror yet.  Returns 1 if
61648ffe56aSColin Percival# no mirrors are available for any reason.
61748ffe56aSColin Percivalfetch_pick_server_init () {
61848ffe56aSColin Percival	: > serverlist_tried
61948ffe56aSColin Percival
62048ffe56aSColin Percival# Check that host(1) exists (i.e., that the system wasn't built with the
62148ffe56aSColin Percival# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
62248ffe56aSColin Percival	if ! which -s host; then
62348ffe56aSColin Percival		: > serverlist_full
62448ffe56aSColin Percival		return 1
62548ffe56aSColin Percival	fi
62648ffe56aSColin Percival
62748ffe56aSColin Percival	echo -n "Looking up ${SERVERNAME} mirrors... "
62848ffe56aSColin Percival
62948ffe56aSColin Percival# Issue the SRV query and pull out the Priority, Weight, and Target fields.
63048ffe56aSColin Percival# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
63148ffe56aSColin Percival# "$name server selection ..."; we allow either format.
63248ffe56aSColin Percival	MLIST="_http._tcp.${SERVERNAME}"
63348ffe56aSColin Percival	host -t srv "${MLIST}" |
63448ffe56aSColin Percival	    sed -nE "s/${MLIST} (has SRV record|server selection) //p" |
63548ffe56aSColin Percival	    cut -f 1,2,4 -d ' ' |
63648ffe56aSColin Percival	    sed -e 's/\.$//' |
63748ffe56aSColin Percival	    sort > serverlist_full
63848ffe56aSColin Percival
63948ffe56aSColin Percival# If no records, give up -- we'll just use the server name we were given.
64048ffe56aSColin Percival	if [ `wc -l < serverlist_full` -eq 0 ]; then
64148ffe56aSColin Percival		echo "none found."
64248ffe56aSColin Percival		return 1
64348ffe56aSColin Percival	fi
64448ffe56aSColin Percival
64548ffe56aSColin Percival# Report how many mirrors we found.
64648ffe56aSColin Percival	echo `wc -l < serverlist_full` "mirrors found."
64748ffe56aSColin Percival
64848ffe56aSColin Percival# Generate a random seed for use in picking mirrors.  If HTTP_PROXY
64948ffe56aSColin Percival# is set, this will be used to generate the seed; otherwise, the seed
65048ffe56aSColin Percival# will be random.
65148ffe56aSColin Percival	if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
65248ffe56aSColin Percival		RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
65348ffe56aSColin Percival		    tr -d 'a-f' |
65448ffe56aSColin Percival		    cut -c 1-9`
65548ffe56aSColin Percival	else
65648ffe56aSColin Percival		RANDVALUE=`jot -r 1 0 999999999`
65748ffe56aSColin Percival	fi
65848ffe56aSColin Percival}
65948ffe56aSColin Percival
66048ffe56aSColin Percival# Pick a mirror.  Returns 1 if we have run out of mirrors to try.
66148ffe56aSColin Percivalfetch_pick_server () {
66248ffe56aSColin Percival# Generate a list of not-yet-tried mirrors
66348ffe56aSColin Percival	sort serverlist_tried |
66448ffe56aSColin Percival	    comm -23 serverlist_full - > serverlist
66548ffe56aSColin Percival
66648ffe56aSColin Percival# Have we run out of mirrors?
66748ffe56aSColin Percival	if [ `wc -l < serverlist` -eq 0 ]; then
66848ffe56aSColin Percival		echo "No mirrors remaining, giving up."
66948ffe56aSColin Percival		return 1
67048ffe56aSColin Percival	fi
67148ffe56aSColin Percival
67248ffe56aSColin Percival# Find the highest priority level (lowest numeric value).
67348ffe56aSColin Percival	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
67448ffe56aSColin Percival
67548ffe56aSColin Percival# Add up the weights of the response lines at that priority level.
67648ffe56aSColin Percival	SRV_WSUM=0;
67748ffe56aSColin Percival	while read X; do
67848ffe56aSColin Percival		case "$X" in
67948ffe56aSColin Percival		${SRV_PRIORITY}\ *)
68048ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
68148ffe56aSColin Percival			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
68248ffe56aSColin Percival			;;
68348ffe56aSColin Percival		esac
68448ffe56aSColin Percival	done < serverlist
68548ffe56aSColin Percival
68648ffe56aSColin Percival# If all the weights are 0, pretend that they are all 1 instead.
68748ffe56aSColin Percival	if [ ${SRV_WSUM} -eq 0 ]; then
68848ffe56aSColin Percival		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
68948ffe56aSColin Percival		SRV_W_ADD=1
69048ffe56aSColin Percival	else
69148ffe56aSColin Percival		SRV_W_ADD=0
69248ffe56aSColin Percival	fi
69348ffe56aSColin Percival
69448ffe56aSColin Percival# Pick a value between 0 and the sum of the weights - 1
69548ffe56aSColin Percival	SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
69648ffe56aSColin Percival
69748ffe56aSColin Percival# Read through the list of mirrors and set SERVERNAME.  Write the line
69848ffe56aSColin Percival# corresponding to the mirror we selected into serverlist_tried so that
69948ffe56aSColin Percival# we won't try it again.
70048ffe56aSColin Percival	while read X; do
70148ffe56aSColin Percival		case "$X" in
70248ffe56aSColin Percival		${SRV_PRIORITY}\ *)
70348ffe56aSColin Percival			SRV_W=`echo $X | cut -f 2 -d ' '`
70448ffe56aSColin Percival			SRV_W=$(($SRV_W + $SRV_W_ADD))
70548ffe56aSColin Percival			if [ $SRV_RND -lt $SRV_W ]; then
70648ffe56aSColin Percival				SERVERNAME=`echo $X | cut -f 3 -d ' '`
70748ffe56aSColin Percival				echo "$X" >> serverlist_tried
70848ffe56aSColin Percival				break
70948ffe56aSColin Percival			else
71048ffe56aSColin Percival				SRV_RND=$(($SRV_RND - $SRV_W))
71148ffe56aSColin Percival			fi
71248ffe56aSColin Percival			;;
71348ffe56aSColin Percival		esac
71448ffe56aSColin Percival	done < serverlist
71548ffe56aSColin Percival}
71648ffe56aSColin Percival
71748ffe56aSColin Percival# Take a list of ${oldhash}|${newhash} and output a list of needed patches,
71848ffe56aSColin Percival# i.e., those for which we have ${oldhash} and don't have ${newhash}.
71948ffe56aSColin Percivalfetch_make_patchlist () {
72048ffe56aSColin Percival	grep -vE "^([0-9a-f]{64})\|\1$" |
72148ffe56aSColin Percival	    tr '|' ' ' |
72248ffe56aSColin Percival		while read X Y; do
72348ffe56aSColin Percival			if [ -f "files/${Y}.gz" ] ||
72448ffe56aSColin Percival			    [ ! -f "files/${X}.gz" ]; then
72548ffe56aSColin Percival				continue
72648ffe56aSColin Percival			fi
72748ffe56aSColin Percival			echo "${X}|${Y}"
72848ffe56aSColin Percival		done | uniq
72948ffe56aSColin Percival}
73048ffe56aSColin Percival
73148ffe56aSColin Percival# Print user-friendly progress statistics
73248ffe56aSColin Percivalfetch_progress () {
73348ffe56aSColin Percival	LNC=0
73448ffe56aSColin Percival	while read x; do
73548ffe56aSColin Percival		LNC=$(($LNC + 1))
73648ffe56aSColin Percival		if [ $(($LNC % 10)) = 0 ]; then
73748ffe56aSColin Percival			echo -n $LNC
73848ffe56aSColin Percival		elif [ $(($LNC % 2)) = 0 ]; then
73948ffe56aSColin Percival			echo -n .
74048ffe56aSColin Percival		fi
74148ffe56aSColin Percival	done
74248ffe56aSColin Percival	echo -n " "
74348ffe56aSColin Percival}
74448ffe56aSColin Percival
74548ffe56aSColin Percival# Initialize the working directory
74648ffe56aSColin Percivalworkdir_init () {
74748ffe56aSColin Percival	mkdir -p files
74848ffe56aSColin Percival	touch tINDEX.present
74948ffe56aSColin Percival}
75048ffe56aSColin Percival
75148ffe56aSColin Percival# Check that we have a public key with an appropriate hash, or
75248ffe56aSColin Percival# fetch the key if it doesn't exist.  Returns 1 if the key has
75348ffe56aSColin Percival# not yet been fetched.
75448ffe56aSColin Percivalfetch_key () {
75548ffe56aSColin Percival	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
75648ffe56aSColin Percival		return 0
75748ffe56aSColin Percival	fi
75848ffe56aSColin Percival
75948ffe56aSColin Percival	echo -n "Fetching public key from ${SERVERNAME}... "
76048ffe56aSColin Percival	rm -f pub.ssl
76148ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \
76248ffe56aSColin Percival	    2>${QUIETREDIR} || true
76348ffe56aSColin Percival	if ! [ -r pub.ssl ]; then
76448ffe56aSColin Percival		echo "failed."
76548ffe56aSColin Percival		return 1
76648ffe56aSColin Percival	fi
76748ffe56aSColin Percival	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
76848ffe56aSColin Percival		echo "key has incorrect hash."
76948ffe56aSColin Percival		rm -f pub.ssl
77048ffe56aSColin Percival		return 1
77148ffe56aSColin Percival	fi
77248ffe56aSColin Percival	echo "done."
77348ffe56aSColin Percival}
77448ffe56aSColin Percival
77548ffe56aSColin Percival# Fetch metadata signature, aka "tag".
77648ffe56aSColin Percivalfetch_tag () {
77748ffe56aSColin Percival	echo ${NDEBUG} "Fetching metadata signature from ${SERVERNAME}... "
77848ffe56aSColin Percival	rm -f latest.ssl
77948ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl	\
78048ffe56aSColin Percival	    2>${QUIETREDIR} || true
78148ffe56aSColin Percival	if ! [ -r latest.ssl ]; then
78248ffe56aSColin Percival		echo "failed."
78348ffe56aSColin Percival		return 1
78448ffe56aSColin Percival	fi
78548ffe56aSColin Percival
78648ffe56aSColin Percival	openssl rsautl -pubin -inkey pub.ssl -verify		\
78748ffe56aSColin Percival	    < latest.ssl > tag.new 2>${QUIETREDIR} || true
78848ffe56aSColin Percival	rm latest.ssl
78948ffe56aSColin Percival
79048ffe56aSColin Percival	if ! [ `wc -l < tag.new` = 1 ] ||
79148ffe56aSColin Percival	    ! grep -qE	\
79248ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
79348ffe56aSColin Percival		tag.new; then
79448ffe56aSColin Percival		echo "invalid signature."
79548ffe56aSColin Percival		return 1
79648ffe56aSColin Percival	fi
79748ffe56aSColin Percival
79848ffe56aSColin Percival	echo "done."
79948ffe56aSColin Percival
80048ffe56aSColin Percival	RELPATCHNUM=`cut -f 4 -d '|' < tag.new`
80148ffe56aSColin Percival	TINDEXHASH=`cut -f 5 -d '|' < tag.new`
80248ffe56aSColin Percival	EOLTIME=`cut -f 6 -d '|' < tag.new`
80348ffe56aSColin Percival}
80448ffe56aSColin Percival
80548ffe56aSColin Percival# Sanity-check the patch number in a tag, to make sure that we're not
80648ffe56aSColin Percival# going to "update" backwards and to prevent replay attacks.
80748ffe56aSColin Percivalfetch_tagsanity () {
80848ffe56aSColin Percival	# Check that we're not going to move from -pX to -pY with Y < X.
80948ffe56aSColin Percival	RELPX=`uname -r | sed -E 's,.*-,,'`
81048ffe56aSColin Percival	if echo ${RELPX} | grep -qE '^p[0-9]+$'; then
81148ffe56aSColin Percival		RELPX=`echo ${RELPX} | cut -c 2-`
81248ffe56aSColin Percival	else
81348ffe56aSColin Percival		RELPX=0
81448ffe56aSColin Percival	fi
81548ffe56aSColin Percival	if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then
81648ffe56aSColin Percival		echo
81748ffe56aSColin Percival		echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
81848ffe56aSColin Percival		echo " appear older than what"
81948ffe56aSColin Percival		echo "we are currently running (`uname -r`)!"
82048ffe56aSColin Percival		echo "Cowardly refusing to proceed any further."
82148ffe56aSColin Percival		return 1
82248ffe56aSColin Percival	fi
82348ffe56aSColin Percival
82448ffe56aSColin Percival	# If "tag" exists and corresponds to ${RELNUM}, make sure that
82548ffe56aSColin Percival	# it contains a patch number <= RELPATCHNUM, in order to protect
82648ffe56aSColin Percival	# against rollback (replay) attacks.
82748ffe56aSColin Percival	if [ -f tag ] &&
82848ffe56aSColin Percival	    grep -qE	\
82948ffe56aSColin Percival    "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
83048ffe56aSColin Percival		tag; then
83148ffe56aSColin Percival		LASTRELPATCHNUM=`cut -f 4 -d '|' < tag`
83248ffe56aSColin Percival
83348ffe56aSColin Percival		if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then
83448ffe56aSColin Percival			echo
83548ffe56aSColin Percival			echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
83648ffe56aSColin Percival			echo " are older than the"
83748ffe56aSColin Percival			echo -n "most recently seen updates"
83848ffe56aSColin Percival			echo " (${RELNUM}-p${LASTRELPATCHNUM})."
83948ffe56aSColin Percival			echo "Cowardly refusing to proceed any further."
84048ffe56aSColin Percival			return 1
84148ffe56aSColin Percival		fi
84248ffe56aSColin Percival	fi
84348ffe56aSColin Percival}
84448ffe56aSColin Percival
84548ffe56aSColin Percival# Fetch metadata index file
84648ffe56aSColin Percivalfetch_metadata_index () {
84748ffe56aSColin Percival	echo ${NDEBUG} "Fetching metadata index... "
84848ffe56aSColin Percival	rm -f ${TINDEXHASH}
84948ffe56aSColin Percival	fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH}
85048ffe56aSColin Percival	    2>${QUIETREDIR}
85148ffe56aSColin Percival	if ! [ -f ${TINDEXHASH} ]; then
85248ffe56aSColin Percival		echo "failed."
85348ffe56aSColin Percival		return 1
85448ffe56aSColin Percival	fi
85548ffe56aSColin Percival	if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then
85648ffe56aSColin Percival		echo "update metadata index corrupt."
85748ffe56aSColin Percival		return 1
85848ffe56aSColin Percival	fi
85948ffe56aSColin Percival	echo "done."
86048ffe56aSColin Percival}
86148ffe56aSColin Percival
86248ffe56aSColin Percival# Print an error message about signed metadata being bogus.
86348ffe56aSColin Percivalfetch_metadata_bogus () {
86448ffe56aSColin Percival	echo
86548ffe56aSColin Percival	echo "The update metadata$1 is correctly signed, but"
86648ffe56aSColin Percival	echo "failed an integrity check."
86748ffe56aSColin Percival	echo "Cowardly refusing to proceed any further."
86848ffe56aSColin Percival	return 1
86948ffe56aSColin Percival}
87048ffe56aSColin Percival
87148ffe56aSColin Percival# Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH}
87248ffe56aSColin Percival# with the lines not named in $@ from tINDEX.present (if that file exists).
87348ffe56aSColin Percivalfetch_metadata_index_merge () {
87448ffe56aSColin Percival	for METAFILE in $@; do
87548ffe56aSColin Percival		if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l`	\
87648ffe56aSColin Percival		    -ne 1 ]; then
87748ffe56aSColin Percival			fetch_metadata_bogus " index"
87848ffe56aSColin Percival			return 1
87948ffe56aSColin Percival		fi
88048ffe56aSColin Percival
88148ffe56aSColin Percival		grep -E "${METAFILE}\|" ${TINDEXHASH}
88248ffe56aSColin Percival	done |
88348ffe56aSColin Percival	    sort > tINDEX.wanted
88448ffe56aSColin Percival
88548ffe56aSColin Percival	if [ -f tINDEX.present ]; then
88648ffe56aSColin Percival		join -t '|' -v 2 tINDEX.wanted tINDEX.present |
88748ffe56aSColin Percival		    sort -m - tINDEX.wanted > tINDEX.new
88848ffe56aSColin Percival		rm tINDEX.wanted
88948ffe56aSColin Percival	else
89048ffe56aSColin Percival		mv tINDEX.wanted tINDEX.new
89148ffe56aSColin Percival	fi
89248ffe56aSColin Percival}
89348ffe56aSColin Percival
89448ffe56aSColin Percival# Sanity check all the lines of tINDEX.new.  Even if more metadata lines
89548ffe56aSColin Percival# are added by future versions of the server, this won't cause problems,
89648ffe56aSColin Percival# since the only lines which appear in tINDEX.new are the ones which we
89748ffe56aSColin Percival# specifically grepped out of ${TINDEXHASH}.
89848ffe56aSColin Percivalfetch_metadata_index_sanity () {
89948ffe56aSColin Percival	if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then
90048ffe56aSColin Percival		fetch_metadata_bogus " index"
90148ffe56aSColin Percival		return 1
90248ffe56aSColin Percival	fi
90348ffe56aSColin Percival}
90448ffe56aSColin Percival
90548ffe56aSColin Percival# Sanity check the metadata file $1.
90648ffe56aSColin Percivalfetch_metadata_sanity () {
90748ffe56aSColin Percival	# Some aliases to save space later: ${P} is a character which can
90848ffe56aSColin Percival	# appear in a path; ${M} is the four numeric metadata fields; and
90948ffe56aSColin Percival	# ${H} is a sha256 hash.
91048ffe56aSColin Percival	P="[-+./:=_[[:alnum:]]"
91148ffe56aSColin Percival	M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+"
91248ffe56aSColin Percival	H="[0-9a-f]{64}"
91348ffe56aSColin Percival
91448ffe56aSColin Percival	# Check that the first four fields make sense.
91548ffe56aSColin Percival	if gunzip -c < files/$1.gz |
91648ffe56aSColin Percival	    grep -qvE "^[a-z]+\|[0-9a-z]+\|${P}+\|[fdL-]\|"; then
91748ffe56aSColin Percival		fetch_metadata_bogus ""
91848ffe56aSColin Percival		return 1
91948ffe56aSColin Percival	fi
92048ffe56aSColin Percival
92148ffe56aSColin Percival	# Remove the first three fields.
92248ffe56aSColin Percival	gunzip -c < files/$1.gz |
92348ffe56aSColin Percival	    cut -f 4- -d '|' > sanitycheck.tmp
92448ffe56aSColin Percival
92548ffe56aSColin Percival	# Sanity check entries with type 'f'
92648ffe56aSColin Percival	if grep -E '^f' sanitycheck.tmp |
92748ffe56aSColin Percival	    grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then
92848ffe56aSColin Percival		fetch_metadata_bogus ""
92948ffe56aSColin Percival		return 1
93048ffe56aSColin Percival	fi
93148ffe56aSColin Percival
93248ffe56aSColin Percival	# Sanity check entries with type 'd'
93348ffe56aSColin Percival	if grep -E '^d' sanitycheck.tmp |
93448ffe56aSColin Percival	    grep -qvE "^d\|${M}\|\|\$"; then
93548ffe56aSColin Percival		fetch_metadata_bogus ""
93648ffe56aSColin Percival		return 1
93748ffe56aSColin Percival	fi
93848ffe56aSColin Percival
93948ffe56aSColin Percival	# Sanity check entries with type 'L'
94048ffe56aSColin Percival	if grep -E '^L' sanitycheck.tmp |
94148ffe56aSColin Percival	    grep -qvE "^L\|${M}\|${P}*\|\$"; then
94248ffe56aSColin Percival		fetch_metadata_bogus ""
94348ffe56aSColin Percival		return 1
94448ffe56aSColin Percival	fi
94548ffe56aSColin Percival
94648ffe56aSColin Percival	# Sanity check entries with type '-'
94748ffe56aSColin Percival	if grep -E '^-' sanitycheck.tmp |
94848ffe56aSColin Percival	    grep -qvE "^-\|\|\|\|\|\|"; then
94948ffe56aSColin Percival		fetch_metadata_bogus ""
95048ffe56aSColin Percival		return 1
95148ffe56aSColin Percival	fi
95248ffe56aSColin Percival
95348ffe56aSColin Percival	# Clean up
95448ffe56aSColin Percival	rm sanitycheck.tmp
95548ffe56aSColin Percival}
95648ffe56aSColin Percival
95748ffe56aSColin Percival# Fetch the metadata index and metadata files listed in $@,
95848ffe56aSColin Percival# taking advantage of metadata patches where possible.
95948ffe56aSColin Percivalfetch_metadata () {
96048ffe56aSColin Percival	fetch_metadata_index || return 1
96148ffe56aSColin Percival	fetch_metadata_index_merge $@ || return 1
96248ffe56aSColin Percival	fetch_metadata_index_sanity || return 1
96348ffe56aSColin Percival
96448ffe56aSColin Percival	# Generate a list of wanted metadata patches
96548ffe56aSColin Percival	join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new |
96648ffe56aSColin Percival	    fetch_make_patchlist > patchlist
96748ffe56aSColin Percival
96848ffe56aSColin Percival	if [ -s patchlist ]; then
96948ffe56aSColin Percival		# Attempt to fetch metadata patches
97048ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
97148ffe56aSColin Percival		echo ${NDEBUG} "metadata patches.${DDSTATS}"
97248ffe56aSColin Percival		tr '|' '-' < patchlist |
97348ffe56aSColin Percival		    lam -s "${FETCHDIR}/tp/" - -s ".gz" |
97448ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
97548ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
97648ffe56aSColin Percival		echo "done."
97748ffe56aSColin Percival
97848ffe56aSColin Percival		# Attempt to apply metadata patches
97948ffe56aSColin Percival		echo -n "Applying metadata patches... "
98048ffe56aSColin Percival		tr '|' ' ' < patchlist |
98148ffe56aSColin Percival		    while read X Y; do
98248ffe56aSColin Percival			if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
98348ffe56aSColin Percival			gunzip -c < ${X}-${Y}.gz > diff
98448ffe56aSColin Percival			gunzip -c < files/${X}.gz > diff-OLD
98548ffe56aSColin Percival
98648ffe56aSColin Percival			# Figure out which lines are being added and removed
98748ffe56aSColin Percival			grep -E '^-' diff |
98848ffe56aSColin Percival			    cut -c 2- |
98948ffe56aSColin Percival			    while read PREFIX; do
99048ffe56aSColin Percival				look "${PREFIX}" diff-OLD
99148ffe56aSColin Percival			    done |
99248ffe56aSColin Percival			    sort > diff-rm
99348ffe56aSColin Percival			grep -E '^\+' diff |
99448ffe56aSColin Percival			    cut -c 2- > diff-add
99548ffe56aSColin Percival
99648ffe56aSColin Percival			# Generate the new file
99748ffe56aSColin Percival			comm -23 diff-OLD diff-rm |
99848ffe56aSColin Percival			    sort - diff-add > diff-NEW
99948ffe56aSColin Percival
100048ffe56aSColin Percival			if [ `${SHA256} -q diff-NEW` = ${Y} ]; then
100148ffe56aSColin Percival				mv diff-NEW files/${Y}
100248ffe56aSColin Percival				gzip -n files/${Y}
100348ffe56aSColin Percival			else
100448ffe56aSColin Percival				mv diff-NEW ${Y}.bad
100548ffe56aSColin Percival			fi
100648ffe56aSColin Percival			rm -f ${X}-${Y}.gz diff
100748ffe56aSColin Percival			rm -f diff-OLD diff-NEW diff-add diff-rm
100848ffe56aSColin Percival		done 2>${QUIETREDIR}
100948ffe56aSColin Percival		echo "done."
101048ffe56aSColin Percival	fi
101148ffe56aSColin Percival
101248ffe56aSColin Percival	# Update metadata without patches
101348ffe56aSColin Percival	cut -f 2 -d '|' < tINDEX.new |
101448ffe56aSColin Percival	    while read Y; do
101548ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
101648ffe56aSColin Percival			echo ${Y};
101748ffe56aSColin Percival		fi
1018bce02f98SColin Percival	    done |
1019bce02f98SColin Percival	    sort -u > filelist
102048ffe56aSColin Percival
102148ffe56aSColin Percival	if [ -s filelist ]; then
102248ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
102348ffe56aSColin Percival		echo ${NDEBUG} "metadata files... "
102448ffe56aSColin Percival		lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist |
102548ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
102648ffe56aSColin Percival		    2>${QUIETREDIR}
102748ffe56aSColin Percival
102848ffe56aSColin Percival		while read Y; do
102948ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
103048ffe56aSColin Percival				echo "failed."
103148ffe56aSColin Percival				return 1
103248ffe56aSColin Percival			fi
103348ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
103448ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
103548ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
103648ffe56aSColin Percival			else
103748ffe56aSColin Percival				echo "metadata is corrupt."
103848ffe56aSColin Percival				return 1
103948ffe56aSColin Percival			fi
104048ffe56aSColin Percival		done < filelist
104148ffe56aSColin Percival		echo "done."
104248ffe56aSColin Percival	fi
104348ffe56aSColin Percival
104448ffe56aSColin Percival# Sanity-check the metadata files.
104548ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new > filelist
104648ffe56aSColin Percival	while read X; do
104748ffe56aSColin Percival		fetch_metadata_sanity ${X} || return 1
104848ffe56aSColin Percival	done < filelist
104948ffe56aSColin Percival
105048ffe56aSColin Percival# Remove files which are no longer needed
105148ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.present |
105248ffe56aSColin Percival	    sort > oldfiles
105348ffe56aSColin Percival	cut -f 2 -d '|' tINDEX.new |
105448ffe56aSColin Percival	    sort |
105548ffe56aSColin Percival	    comm -13 - oldfiles |
105648ffe56aSColin Percival	    lam -s "files/" - -s ".gz" |
105748ffe56aSColin Percival	    xargs rm -f
105848ffe56aSColin Percival	rm patchlist filelist oldfiles
105948ffe56aSColin Percival	rm ${TINDEXHASH}
106048ffe56aSColin Percival
106148ffe56aSColin Percival# We're done!
106248ffe56aSColin Percival	mv tINDEX.new tINDEX.present
106348ffe56aSColin Percival	mv tag.new tag
106448ffe56aSColin Percival
106548ffe56aSColin Percival	return 0
106648ffe56aSColin Percival}
106748ffe56aSColin Percival
1068b698a3abSColin Percival# Generate a filtered version of the metadata file $1 from the downloaded
106948ffe56aSColin Percival# file, by fishing out the lines corresponding to components we're trying
107048ffe56aSColin Percival# to keep updated, and then removing lines corresponding to paths we want
107148ffe56aSColin Percival# to ignore.
107248ffe56aSColin Percivalfetch_filter_metadata () {
107348ffe56aSColin Percival	METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
107448ffe56aSColin Percival	gunzip -c < files/${METAHASH}.gz > $1.all
107548ffe56aSColin Percival
107648ffe56aSColin Percival	# Fish out the lines belonging to components we care about.
107748ffe56aSColin Percival	# Canonicalize directory names by removing any trailing / in
107848ffe56aSColin Percival	# order to avoid listing directories multiple times if they
107948ffe56aSColin Percival	# belong to multiple components.  Turning "/" into "" doesn't
108048ffe56aSColin Percival	# matter, since we add a leading "/" when we use paths later.
108148ffe56aSColin Percival	for C in ${COMPONENTS}; do
108248ffe56aSColin Percival		look "`echo ${C} | tr '/' '|'`|" $1.all
108348ffe56aSColin Percival	done |
108448ffe56aSColin Percival	    cut -f 3- -d '|' |
108548ffe56aSColin Percival	    sed -e 's,/|d|,|d|,' |
108648ffe56aSColin Percival	    sort -u > $1.tmp
108748ffe56aSColin Percival
108848ffe56aSColin Percival	# Figure out which lines to ignore and remove them.
108948ffe56aSColin Percival	for X in ${IGNOREPATHS}; do
109048ffe56aSColin Percival		grep -E "^${X}" $1.tmp
109148ffe56aSColin Percival	done |
109248ffe56aSColin Percival	    sort -u |
109348ffe56aSColin Percival	    comm -13 - $1.tmp > $1
109448ffe56aSColin Percival
109548ffe56aSColin Percival	# Remove temporary files.
109648ffe56aSColin Percival	rm $1.all $1.tmp
109748ffe56aSColin Percival}
109848ffe56aSColin Percival
10992c434b2cSColin Percival# Filter the metadata file $1 by adding lines with "/boot/${KERNCONF}"
1100bce02f98SColin Percival# replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the
11012c434b2cSColin Percival# trailing "/kernel"); and if "/boot/${KERNCONF}" does not exist, remove
1102bce02f98SColin Percival# the original lines which start with that.
1103bce02f98SColin Percival# Put another way: Deal with the fact that the FOO kernel is sometimes
1104bce02f98SColin Percival# installed in /boot/FOO/ and is sometimes installed elsewhere.
110548ffe56aSColin Percivalfetch_filter_kernel_names () {
1106bce02f98SColin Percival
1107bce02f98SColin Percival	grep ^/boot/${KERNCONF} $1 |
1108bce02f98SColin Percival	    sed -e "s,/boot/${KERNCONF},${KERNELDIR},g" |
110948ffe56aSColin Percival	    sort - $1 > $1.tmp
111048ffe56aSColin Percival	mv $1.tmp $1
1111bce02f98SColin Percival
1112bce02f98SColin Percival	if ! [ -d /boot/${KERNCONF} ]; then
1113bce02f98SColin Percival		grep -v ^/boot/${KERNCONF} $1 > $1.tmp
1114bce02f98SColin Percival		mv $1.tmp $1
1115bce02f98SColin Percival	fi
111648ffe56aSColin Percival}
111748ffe56aSColin Percival
111848ffe56aSColin Percival# For all paths appearing in $1 or $3, inspect the system
111948ffe56aSColin Percival# and generate $2 describing what is currently installed.
112048ffe56aSColin Percivalfetch_inspect_system () {
112148ffe56aSColin Percival	# No errors yet...
112248ffe56aSColin Percival	rm -f .err
112348ffe56aSColin Percival
112448ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
112548ffe56aSColin Percival	echo -n "Inspecting system... "
112648ffe56aSColin Percival
112748ffe56aSColin Percival	# Generate list of files to inspect
112848ffe56aSColin Percival	cat $1 $3 |
112948ffe56aSColin Percival	    cut -f 1 -d '|' |
113048ffe56aSColin Percival	    sort -u > filelist
113148ffe56aSColin Percival
113248ffe56aSColin Percival	# Examine each file and output lines of the form
113348ffe56aSColin Percival	# /path/to/file|type|device-inum|user|group|perm|flags|value
113448ffe56aSColin Percival	# sorted by device and inode number.
113548ffe56aSColin Percival	while read F; do
113648ffe56aSColin Percival		# If the symlink/file/directory does not exist, record this.
113748ffe56aSColin Percival		if ! [ -e ${BASEDIR}/${F} ]; then
113848ffe56aSColin Percival			echo "${F}|-||||||"
113948ffe56aSColin Percival			continue
114048ffe56aSColin Percival		fi
114148ffe56aSColin Percival		if ! [ -r ${BASEDIR}/${F} ]; then
114248ffe56aSColin Percival			echo "Cannot read file: ${BASEDIR}/${F}"	\
114348ffe56aSColin Percival			    >/dev/stderr
114448ffe56aSColin Percival			touch .err
114548ffe56aSColin Percival			return 1
114648ffe56aSColin Percival		fi
114748ffe56aSColin Percival
114848ffe56aSColin Percival		# Otherwise, output an index line.
114948ffe56aSColin Percival		if [ -L ${BASEDIR}/${F} ]; then
115048ffe56aSColin Percival			echo -n "${F}|L|"
115148ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
115248ffe56aSColin Percival			readlink ${BASEDIR}/${F};
115348ffe56aSColin Percival		elif [ -f ${BASEDIR}/${F} ]; then
115448ffe56aSColin Percival			echo -n "${F}|f|"
115548ffe56aSColin Percival			stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
115648ffe56aSColin Percival			sha256 -q ${BASEDIR}/${F};
115748ffe56aSColin Percival		elif [ -d ${BASEDIR}/${F} ]; then
115848ffe56aSColin Percival			echo -n "${F}|d|"
115948ffe56aSColin Percival			stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
116048ffe56aSColin Percival		else
116148ffe56aSColin Percival			echo "Unknown file type: ${BASEDIR}/${F}"	\
116248ffe56aSColin Percival			    >/dev/stderr
116348ffe56aSColin Percival			touch .err
116448ffe56aSColin Percival			return 1
116548ffe56aSColin Percival		fi
116648ffe56aSColin Percival	done < filelist |
116748ffe56aSColin Percival	    sort -k 3,3 -t '|' > $2.tmp
116848ffe56aSColin Percival	rm filelist
116948ffe56aSColin Percival
117048ffe56aSColin Percival	# Check if an error occured during system inspection
117148ffe56aSColin Percival	if [ -f .err ]; then
117248ffe56aSColin Percival		return 1
117348ffe56aSColin Percival	fi
117448ffe56aSColin Percival
117548ffe56aSColin Percival	# Convert to the form
117648ffe56aSColin Percival	# /path/to/file|type|user|group|perm|flags|value|hlink
117748ffe56aSColin Percival	# by resolving identical device and inode numbers into hard links.
117848ffe56aSColin Percival	cut -f 1,3 -d '|' $2.tmp |
117948ffe56aSColin Percival	    sort -k 1,1 -t '|' |
118048ffe56aSColin Percival	    sort -s -u -k 2,2 -t '|' |
118148ffe56aSColin Percival	    join -1 2 -2 3 -t '|' - $2.tmp |
118248ffe56aSColin Percival	    awk -F \| -v OFS=\|		\
118348ffe56aSColin Percival		'{
118448ffe56aSColin Percival		    if (($2 == $3) || ($4 == "-"))
118548ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,""
118648ffe56aSColin Percival		    else
118748ffe56aSColin Percival			print $3,$4,$5,$6,$7,$8,$9,$2
118848ffe56aSColin Percival		}' |
118948ffe56aSColin Percival	    sort > $2
119048ffe56aSColin Percival	rm $2.tmp
119148ffe56aSColin Percival
119248ffe56aSColin Percival	# We're finished looking around
119348ffe56aSColin Percival	echo "done."
119448ffe56aSColin Percival}
119548ffe56aSColin Percival
119648ffe56aSColin Percival# For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123]
119748ffe56aSColin Percival# which correspond to lines in $2 with hashes not matching $1 or $3.  For
119848ffe56aSColin Percival# entries in $2 marked "not present" (aka. type -), remove lines from $[123]
119948ffe56aSColin Percival# unless there is a corresponding entry in $1.
120048ffe56aSColin Percivalfetch_filter_unmodified_notpresent () {
120148ffe56aSColin Percival	# Figure out which lines of $1 and $3 correspond to bits which
120248ffe56aSColin Percival	# should only be updated if they haven't changed, and fish out
120348ffe56aSColin Percival	# the (path, type, value) tuples.
120448ffe56aSColin Percival	# NOTE: We don't consider a file to be "modified" if it matches
120548ffe56aSColin Percival	# the hash from $3.
120648ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
120748ffe56aSColin Percival		grep -E "^${X}" $1
120848ffe56aSColin Percival		grep -E "^${X}" $3
120948ffe56aSColin Percival	done |
121048ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
121148ffe56aSColin Percival	    sort > $1-values
121248ffe56aSColin Percival
121348ffe56aSColin Percival	# Do the same for $2.
121448ffe56aSColin Percival	for X in ${UPDATEIFUNMODIFIED}; do
121548ffe56aSColin Percival		grep -E "^${X}" $2
121648ffe56aSColin Percival	done |
121748ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
121848ffe56aSColin Percival	    sort > $2-values
121948ffe56aSColin Percival
122048ffe56aSColin Percival	# Any entry in $2-values which is not in $1-values corresponds to
122148ffe56aSColin Percival	# a path which we need to remove from $1, $2, and $3.
122248ffe56aSColin Percival	comm -13 $1-values $2-values > mlines
122348ffe56aSColin Percival	rm $1-values $2-values
122448ffe56aSColin Percival
122548ffe56aSColin Percival	# Any lines in $2 which are not in $1 AND are "not present" lines
122648ffe56aSColin Percival	# also belong in mlines.
122748ffe56aSColin Percival	comm -13 $1 $2 |
122848ffe56aSColin Percival	    cut -f 1,2,7 -d '|' |
122948ffe56aSColin Percival	    fgrep '|-|' >> mlines
123048ffe56aSColin Percival
123148ffe56aSColin Percival	# Remove lines from $1, $2, and $3
123248ffe56aSColin Percival	for X in $1 $2 $3; do
123348ffe56aSColin Percival		sort -t '|' -k 1,1 ${X} > ${X}.tmp
123448ffe56aSColin Percival		cut -f 1 -d '|' < mlines |
123548ffe56aSColin Percival		    sort |
123648ffe56aSColin Percival		    join -v 2 -t '|' - ${X}.tmp |
123748ffe56aSColin Percival		    sort > ${X}
123848ffe56aSColin Percival		rm ${X}.tmp
123948ffe56aSColin Percival	done
124048ffe56aSColin Percival
124148ffe56aSColin Percival	# Store a list of the modified files, for future reference
124248ffe56aSColin Percival	fgrep -v '|-|' mlines |
124348ffe56aSColin Percival	    cut -f 1 -d '|' > modifiedfiles
124448ffe56aSColin Percival	rm mlines
124548ffe56aSColin Percival}
124648ffe56aSColin Percival
124748ffe56aSColin Percival# For each entry in $1 of type -, remove any corresponding
124848ffe56aSColin Percival# entry from $2 if ${ALLOWADD} != "yes".  Remove all entries
124948ffe56aSColin Percival# of type - from $1.
125048ffe56aSColin Percivalfetch_filter_allowadd () {
125148ffe56aSColin Percival	cut -f 1,2 -d '|' < $1 |
125248ffe56aSColin Percival	    fgrep '|-' |
125348ffe56aSColin Percival	    cut -f 1 -d '|' > filesnotpresent
125448ffe56aSColin Percival
125548ffe56aSColin Percival	if [ ${ALLOWADD} != "yes" ]; then
125648ffe56aSColin Percival		sort < $2 |
125748ffe56aSColin Percival		    join -v 1 -t '|' - filesnotpresent |
125848ffe56aSColin Percival		    sort > $2.tmp
125948ffe56aSColin Percival		mv $2.tmp $2
126048ffe56aSColin Percival	fi
126148ffe56aSColin Percival
126248ffe56aSColin Percival	sort < $1 |
126348ffe56aSColin Percival	    join -v 1 -t '|' - filesnotpresent |
126448ffe56aSColin Percival	    sort > $1.tmp
126548ffe56aSColin Percival	mv $1.tmp $1
126648ffe56aSColin Percival	rm filesnotpresent
126748ffe56aSColin Percival}
126848ffe56aSColin Percival
126948ffe56aSColin Percival# If ${ALLOWDELETE} != "yes", then remove any entries from $1
127048ffe56aSColin Percival# which don't correspond to entries in $2.
127148ffe56aSColin Percivalfetch_filter_allowdelete () {
127248ffe56aSColin Percival	# Produce a lists ${PATH}|${TYPE}
127348ffe56aSColin Percival	for X in $1 $2; do
127448ffe56aSColin Percival		cut -f 1-2 -d '|' < ${X} |
127548ffe56aSColin Percival		    sort -u > ${X}.nodes
127648ffe56aSColin Percival	done
127748ffe56aSColin Percival
127848ffe56aSColin Percival	# Figure out which lines need to be removed from $1.
127948ffe56aSColin Percival	if [ ${ALLOWDELETE} != "yes" ]; then
128048ffe56aSColin Percival		comm -23 $1.nodes $2.nodes > $1.badnodes
128148ffe56aSColin Percival	else
128248ffe56aSColin Percival		: > $1.badnodes
128348ffe56aSColin Percival	fi
128448ffe56aSColin Percival
128548ffe56aSColin Percival	# Remove the relevant lines from $1
128648ffe56aSColin Percival	while read X; do
128748ffe56aSColin Percival		look "${X}|" $1
128848ffe56aSColin Percival	done < $1.badnodes |
128948ffe56aSColin Percival	    comm -13 - $1 > $1.tmp
129048ffe56aSColin Percival	mv $1.tmp $1
129148ffe56aSColin Percival
129248ffe56aSColin Percival	rm $1.badnodes $1.nodes $2.nodes
129348ffe56aSColin Percival}
129448ffe56aSColin Percival
129548ffe56aSColin Percival# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2
129648ffe56aSColin Percival# with metadata not matching any entry in $1, replace the corresponding
129748ffe56aSColin Percival# line of $3 with one having the same metadata as the entry in $2.
129848ffe56aSColin Percivalfetch_filter_modified_metadata () {
129948ffe56aSColin Percival	# Fish out the metadata from $1 and $2
130048ffe56aSColin Percival	for X in $1 $2; do
130148ffe56aSColin Percival		cut -f 1-6 -d '|' < ${X} > ${X}.metadata
130248ffe56aSColin Percival	done
130348ffe56aSColin Percival
130448ffe56aSColin Percival	# Find the metadata we need to keep
130548ffe56aSColin Percival	if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then
130648ffe56aSColin Percival		comm -13 $1.metadata $2.metadata > keepmeta
130748ffe56aSColin Percival	else
130848ffe56aSColin Percival		: > keepmeta
130948ffe56aSColin Percival	fi
131048ffe56aSColin Percival
131148ffe56aSColin Percival	# Extract the lines which we need to remove from $3, and
131248ffe56aSColin Percival	# construct the lines which we need to add to $3.
131348ffe56aSColin Percival	: > $3.remove
131448ffe56aSColin Percival	: > $3.add
131548ffe56aSColin Percival	while read LINE; do
131648ffe56aSColin Percival		NODE=`echo "${LINE}" | cut -f 1-2 -d '|'`
131748ffe56aSColin Percival		look "${NODE}|" $3 >> $3.remove
131848ffe56aSColin Percival		look "${NODE}|" $3 |
131948ffe56aSColin Percival		    cut -f 7- -d '|' |
132048ffe56aSColin Percival		    lam -s "${LINE}|" - >> $3.add
132148ffe56aSColin Percival	done < keepmeta
132248ffe56aSColin Percival
132348ffe56aSColin Percival	# Remove the specified lines and add the new lines.
132448ffe56aSColin Percival	sort $3.remove |
132548ffe56aSColin Percival	    comm -13 - $3 |
132648ffe56aSColin Percival	    sort -u - $3.add > $3.tmp
132748ffe56aSColin Percival	mv $3.tmp $3
132848ffe56aSColin Percival
132948ffe56aSColin Percival	rm keepmeta $1.metadata $2.metadata $3.add $3.remove
133048ffe56aSColin Percival}
133148ffe56aSColin Percival
133248ffe56aSColin Percival# Remove lines from $1 and $2 which are identical;
133348ffe56aSColin Percival# no need to update a file if it isn't changing.
133448ffe56aSColin Percivalfetch_filter_uptodate () {
133548ffe56aSColin Percival	comm -23 $1 $2 > $1.tmp
133648ffe56aSColin Percival	comm -13 $1 $2 > $2.tmp
133748ffe56aSColin Percival
133848ffe56aSColin Percival	mv $1.tmp $1
133948ffe56aSColin Percival	mv $2.tmp $2
134048ffe56aSColin Percival}
134148ffe56aSColin Percival
134248ffe56aSColin Percival# Prepare to fetch files: Generate a list of the files we need,
134348ffe56aSColin Percival# copy the unmodified files we have into /files/, and generate
134448ffe56aSColin Percival# a list of patches to download.
134548ffe56aSColin Percivalfetch_files_prepare () {
134648ffe56aSColin Percival	# Tell the user why his disk is suddenly making lots of noise
134748ffe56aSColin Percival	echo -n "Preparing to download files... "
134848ffe56aSColin Percival
134948ffe56aSColin Percival	# Reduce indices to ${PATH}|${HASH} pairs
135048ffe56aSColin Percival	for X in $1 $2 $3; do
135148ffe56aSColin Percival		cut -f 1,2,7 -d '|' < ${X} |
135248ffe56aSColin Percival		    fgrep '|f|' |
135348ffe56aSColin Percival		    cut -f 1,3 -d '|' |
135448ffe56aSColin Percival		    sort > ${X}.hashes
135548ffe56aSColin Percival	done
135648ffe56aSColin Percival
135748ffe56aSColin Percival	# List of files wanted
135848ffe56aSColin Percival	cut -f 2 -d '|' < $3.hashes |
135948ffe56aSColin Percival	    sort -u > files.wanted
136048ffe56aSColin Percival
136148ffe56aSColin Percival	# Generate a list of unmodified files
136248ffe56aSColin Percival	comm -12 $1.hashes $2.hashes |
136348ffe56aSColin Percival	    sort -k 1,1 -t '|' > unmodified.files
136448ffe56aSColin Percival
136548ffe56aSColin Percival	# Copy all files into /files/.  We only need the unmodified files
136648ffe56aSColin Percival	# for use in patching; but we'll want all of them if the user asks
136748ffe56aSColin Percival	# to rollback the updates later.
136848ffe56aSColin Percival	cut -f 1 -d '|' < $2.hashes |
136948ffe56aSColin Percival	    while read F; do
137048ffe56aSColin Percival		cp "${BASEDIR}/${F}" tmpfile
137148ffe56aSColin Percival		gzip -c < tmpfile > files/`sha256 -q tmpfile`.gz
137248ffe56aSColin Percival		rm tmpfile
137348ffe56aSColin Percival	    done
137448ffe56aSColin Percival
137548ffe56aSColin Percival	# Produce a list of patches to download
137648ffe56aSColin Percival	sort -k 1,1 -t '|' $3.hashes |
137748ffe56aSColin Percival	    join -t '|' -o 2.2,1.2 - unmodified.files |
137848ffe56aSColin Percival	    fetch_make_patchlist > patchlist
137948ffe56aSColin Percival
138048ffe56aSColin Percival	# Garbage collect
138148ffe56aSColin Percival	rm unmodified.files $1.hashes $2.hashes $3.hashes
138248ffe56aSColin Percival
138348ffe56aSColin Percival	# We don't need the list of possible old files any more.
138448ffe56aSColin Percival	rm $1
138548ffe56aSColin Percival
138648ffe56aSColin Percival	# We're finished making noise
138748ffe56aSColin Percival	echo "done."
138848ffe56aSColin Percival}
138948ffe56aSColin Percival
139048ffe56aSColin Percival# Fetch files.
139148ffe56aSColin Percivalfetch_files () {
139248ffe56aSColin Percival	# Attempt to fetch patches
139348ffe56aSColin Percival	if [ -s patchlist ]; then
139448ffe56aSColin Percival		echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
139548ffe56aSColin Percival		echo ${NDEBUG} "patches.${DDSTATS}"
139648ffe56aSColin Percival		tr '|' '-' < patchlist |
139748ffe56aSColin Percival		    lam -s "${FETCHDIR}/bp/" - |
139848ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
139948ffe56aSColin Percival			2>${STATSREDIR} | fetch_progress
140048ffe56aSColin Percival		echo "done."
140148ffe56aSColin Percival
140248ffe56aSColin Percival		# Attempt to apply patches
140348ffe56aSColin Percival		echo -n "Applying patches... "
140448ffe56aSColin Percival		tr '|' ' ' < patchlist |
140548ffe56aSColin Percival		    while read X Y; do
140648ffe56aSColin Percival			if [ ! -f "${X}-${Y}" ]; then continue; fi
140748ffe56aSColin Percival			gunzip -c < files/${X}.gz > OLD
140848ffe56aSColin Percival
140948ffe56aSColin Percival			bspatch OLD NEW ${X}-${Y}
141048ffe56aSColin Percival
141148ffe56aSColin Percival			if [ `${SHA256} -q NEW` = ${Y} ]; then
141248ffe56aSColin Percival				mv NEW files/${Y}
141348ffe56aSColin Percival				gzip -n files/${Y}
141448ffe56aSColin Percival			fi
141548ffe56aSColin Percival			rm -f diff OLD NEW ${X}-${Y}
141648ffe56aSColin Percival		done 2>${QUIETREDIR}
141748ffe56aSColin Percival		echo "done."
141848ffe56aSColin Percival	fi
141948ffe56aSColin Percival
142048ffe56aSColin Percival	# Download files which couldn't be generate via patching
142148ffe56aSColin Percival	while read Y; do
142248ffe56aSColin Percival		if [ ! -f "files/${Y}.gz" ]; then
142348ffe56aSColin Percival			echo ${Y};
142448ffe56aSColin Percival		fi
142548ffe56aSColin Percival	done < files.wanted > filelist
142648ffe56aSColin Percival
142748ffe56aSColin Percival	if [ -s filelist ]; then
142848ffe56aSColin Percival		echo -n "Fetching `wc -l < filelist | tr -d ' '` "
142948ffe56aSColin Percival		echo ${NDEBUG} "files... "
143048ffe56aSColin Percival		lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist |
143148ffe56aSColin Percival		    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
143248ffe56aSColin Percival		    2>${QUIETREDIR}
143348ffe56aSColin Percival
143448ffe56aSColin Percival		while read Y; do
143548ffe56aSColin Percival			if ! [ -f ${Y}.gz ]; then
143648ffe56aSColin Percival				echo "failed."
143748ffe56aSColin Percival				return 1
143848ffe56aSColin Percival			fi
143948ffe56aSColin Percival			if [ `gunzip -c < ${Y}.gz |
144048ffe56aSColin Percival			    ${SHA256} -q` = ${Y} ]; then
144148ffe56aSColin Percival				mv ${Y}.gz files/${Y}.gz
144248ffe56aSColin Percival			else
144348ffe56aSColin Percival				echo "${Y} has incorrect hash."
144448ffe56aSColin Percival				return 1
144548ffe56aSColin Percival			fi
144648ffe56aSColin Percival		done < filelist
144748ffe56aSColin Percival		echo "done."
144848ffe56aSColin Percival	fi
144948ffe56aSColin Percival
145048ffe56aSColin Percival	# Clean up
145148ffe56aSColin Percival	rm files.wanted filelist patchlist
145248ffe56aSColin Percival}
145348ffe56aSColin Percival
145448ffe56aSColin Percival# Create and populate install manifest directory; and report what updates
145548ffe56aSColin Percival# are available.
145648ffe56aSColin Percivalfetch_create_manifest () {
145748ffe56aSColin Percival	# If we have an existing install manifest, nuke it.
145848ffe56aSColin Percival	if [ -L "${BDHASH}-install" ]; then
145948ffe56aSColin Percival		rm -r ${BDHASH}-install/
146048ffe56aSColin Percival		rm ${BDHASH}-install
146148ffe56aSColin Percival	fi
146248ffe56aSColin Percival
146348ffe56aSColin Percival	# Report to the user if any updates were avoided due to local changes
146448ffe56aSColin Percival	if [ -s modifiedfiles ]; then
146548ffe56aSColin Percival		echo
146648ffe56aSColin Percival		echo -n "The following files are affected by updates, "
146748ffe56aSColin Percival		echo "but no changes have"
146848ffe56aSColin Percival		echo -n "been downloaded because the files have been "
146948ffe56aSColin Percival		echo "modified locally:"
147048ffe56aSColin Percival		cat modifiedfiles
147148ffe56aSColin Percival	fi
147248ffe56aSColin Percival	rm modifiedfiles
147348ffe56aSColin Percival
147448ffe56aSColin Percival	# If no files will be updated, tell the user and exit
147548ffe56aSColin Percival	if ! [ -s INDEX-PRESENT ] &&
147648ffe56aSColin Percival	    ! [ -s INDEX-NEW ]; then
147748ffe56aSColin Percival		rm INDEX-PRESENT INDEX-NEW
147848ffe56aSColin Percival		echo
147948ffe56aSColin Percival		echo -n "No updates needed to update system to "
148048ffe56aSColin Percival		echo "${RELNUM}-p${RELPATCHNUM}."
148148ffe56aSColin Percival		return
148248ffe56aSColin Percival	fi
148348ffe56aSColin Percival
148448ffe56aSColin Percival	# Divide files into (a) removed files, (b) added files, and
148548ffe56aSColin Percival	# (c) updated files.
148648ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-PRESENT |
148748ffe56aSColin Percival	    sort > INDEX-PRESENT.flist
148848ffe56aSColin Percival	cut -f 1 -d '|' < INDEX-NEW |
148948ffe56aSColin Percival	    sort > INDEX-NEW.flist
149048ffe56aSColin Percival	comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed
149148ffe56aSColin Percival	comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added
149248ffe56aSColin Percival	comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated
149348ffe56aSColin Percival	rm INDEX-PRESENT.flist INDEX-NEW.flist
149448ffe56aSColin Percival
149548ffe56aSColin Percival	# Report removed files, if any
149648ffe56aSColin Percival	if [ -s files.removed ]; then
149748ffe56aSColin Percival		echo
149848ffe56aSColin Percival		echo -n "The following files will be removed "
149948ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
150048ffe56aSColin Percival		cat files.removed
150148ffe56aSColin Percival	fi
150248ffe56aSColin Percival	rm files.removed
150348ffe56aSColin Percival
150448ffe56aSColin Percival	# Report added files, if any
150548ffe56aSColin Percival	if [ -s files.added ]; then
150648ffe56aSColin Percival		echo
150748ffe56aSColin Percival		echo -n "The following files will be added "
150848ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
150948ffe56aSColin Percival		cat files.added
151048ffe56aSColin Percival	fi
151148ffe56aSColin Percival	rm files.added
151248ffe56aSColin Percival
151348ffe56aSColin Percival	# Report updated files, if any
151448ffe56aSColin Percival	if [ -s files.updated ]; then
151548ffe56aSColin Percival		echo
151648ffe56aSColin Percival		echo -n "The following files will be updated "
151748ffe56aSColin Percival		echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
151848ffe56aSColin Percival
151948ffe56aSColin Percival		cat files.updated
152048ffe56aSColin Percival	fi
152148ffe56aSColin Percival	rm files.updated
152248ffe56aSColin Percival
152348ffe56aSColin Percival	# Create a directory for the install manifest.
152448ffe56aSColin Percival	MDIR=`mktemp -d install.XXXXXX` || return 1
152548ffe56aSColin Percival
152648ffe56aSColin Percival	# Populate it
152748ffe56aSColin Percival	mv INDEX-PRESENT ${MDIR}/INDEX-OLD
152848ffe56aSColin Percival	mv INDEX-NEW ${MDIR}/INDEX-NEW
152948ffe56aSColin Percival
153048ffe56aSColin Percival	# Link it into place
153148ffe56aSColin Percival	ln -s ${MDIR} ${BDHASH}-install
153248ffe56aSColin Percival}
153348ffe56aSColin Percival
153448ffe56aSColin Percival# Warn about any upcoming EoL
153548ffe56aSColin Percivalfetch_warn_eol () {
153648ffe56aSColin Percival	# What's the current time?
153748ffe56aSColin Percival	NOWTIME=`date "+%s"`
153848ffe56aSColin Percival
153948ffe56aSColin Percival	# When did we last warn about the EoL date?
154048ffe56aSColin Percival	if [ -f lasteolwarn ]; then
154148ffe56aSColin Percival		LASTWARN=`cat lasteolwarn`
154248ffe56aSColin Percival	else
154348ffe56aSColin Percival		LASTWARN=`expr ${NOWTIME} - 63072000`
154448ffe56aSColin Percival	fi
154548ffe56aSColin Percival
154648ffe56aSColin Percival	# If the EoL time is past, warn.
154748ffe56aSColin Percival	if [ ${EOLTIME} -lt ${NOWTIME} ]; then
154848ffe56aSColin Percival		echo
154948ffe56aSColin Percival		cat <<-EOF
1550b698a3abSColin Percival		WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE.
155148ffe56aSColin Percival		Any security issues discovered after `date -r ${EOLTIME}`
155248ffe56aSColin Percival		will not have been corrected.
155348ffe56aSColin Percival		EOF
155448ffe56aSColin Percival		return 1
155548ffe56aSColin Percival	fi
155648ffe56aSColin Percival
155748ffe56aSColin Percival	# Figure out how long it has been since we last warned about the
155848ffe56aSColin Percival	# upcoming EoL, and how much longer we have left.
155948ffe56aSColin Percival	SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}`
156048ffe56aSColin Percival	TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}`
156148ffe56aSColin Percival
156248ffe56aSColin Percival	# Don't warn if the EoL is more than 6 months away
156348ffe56aSColin Percival	if [ ${TIMELEFT} -gt 15768000 ]; then
156448ffe56aSColin Percival		return 0
156548ffe56aSColin Percival	fi
156648ffe56aSColin Percival
156748ffe56aSColin Percival	# Don't warn if the time remaining is more than 3 times the time
156848ffe56aSColin Percival	# since the last warning.
156948ffe56aSColin Percival	if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then
157048ffe56aSColin Percival		return 0
157148ffe56aSColin Percival	fi
157248ffe56aSColin Percival
157348ffe56aSColin Percival	# Figure out what time units to use.
157448ffe56aSColin Percival	if [ ${TIMELEFT} -lt 604800 ]; then
157548ffe56aSColin Percival		UNIT="day"
157648ffe56aSColin Percival		SIZE=86400
157748ffe56aSColin Percival	elif [ ${TIMELEFT} -lt 2678400 ]; then
157848ffe56aSColin Percival		UNIT="week"
157948ffe56aSColin Percival		SIZE=604800
158048ffe56aSColin Percival	else
158148ffe56aSColin Percival		UNIT="month"
158248ffe56aSColin Percival		SIZE=2678400
158348ffe56aSColin Percival	fi
158448ffe56aSColin Percival
158548ffe56aSColin Percival	# Compute the right number of units
158648ffe56aSColin Percival	NUM=`expr ${TIMELEFT} / ${SIZE}`
158748ffe56aSColin Percival	if [ ${NUM} != 1 ]; then
158848ffe56aSColin Percival		UNIT="${UNIT}s"
158948ffe56aSColin Percival	fi
159048ffe56aSColin Percival
159148ffe56aSColin Percival	# Print the warning
159248ffe56aSColin Percival	echo
159348ffe56aSColin Percival	cat <<-EOF
159448ffe56aSColin Percival		WARNING: `uname -sr` is approaching its End-of-Life date.
159548ffe56aSColin Percival		It is strongly recommended that you upgrade to a newer
159648ffe56aSColin Percival		release within the next ${NUM} ${UNIT}.
159748ffe56aSColin Percival	EOF
159848ffe56aSColin Percival
159948ffe56aSColin Percival	# Update the stored time of last warning
160048ffe56aSColin Percival	echo ${NOWTIME} > lasteolwarn
160148ffe56aSColin Percival}
160248ffe56aSColin Percival
160348ffe56aSColin Percival# Do the actual work involved in "fetch" / "cron".
160448ffe56aSColin Percivalfetch_run () {
160548ffe56aSColin Percival	workdir_init || return 1
160648ffe56aSColin Percival
160748ffe56aSColin Percival	# Prepare the mirror list.
160848ffe56aSColin Percival	fetch_pick_server_init && fetch_pick_server
160948ffe56aSColin Percival
161048ffe56aSColin Percival	# Try to fetch the public key until we run out of servers.
161148ffe56aSColin Percival	while ! fetch_key; do
161248ffe56aSColin Percival		fetch_pick_server || return 1
161348ffe56aSColin Percival	done
161448ffe56aSColin Percival
161548ffe56aSColin Percival	# Try to fetch the metadata index signature ("tag") until we run
161648ffe56aSColin Percival	# out of available servers; and sanity check the downloaded tag.
161748ffe56aSColin Percival	while ! fetch_tag; do
161848ffe56aSColin Percival		fetch_pick_server || return 1
161948ffe56aSColin Percival	done
162048ffe56aSColin Percival	fetch_tagsanity || return 1
162148ffe56aSColin Percival
162248ffe56aSColin Percival	# Fetch the latest INDEX-NEW and INDEX-OLD files.
162348ffe56aSColin Percival	fetch_metadata INDEX-NEW INDEX-OLD || return 1
162448ffe56aSColin Percival
162548ffe56aSColin Percival	# Generate filtered INDEX-NEW and INDEX-OLD files containing only
162648ffe56aSColin Percival	# the lines which (a) belong to components we care about, and (b)
162748ffe56aSColin Percival	# don't correspond to paths we're explicitly ignoring.
162848ffe56aSColin Percival	fetch_filter_metadata INDEX-NEW || return 1
162948ffe56aSColin Percival	fetch_filter_metadata INDEX-OLD || return 1
163048ffe56aSColin Percival
163148ffe56aSColin Percival	# Translate /boot/`uname -i` into ${KERNELDIR}
163248ffe56aSColin Percival	fetch_filter_kernel_names INDEX-NEW
163348ffe56aSColin Percival	fetch_filter_kernel_names INDEX-OLD
163448ffe56aSColin Percival
163548ffe56aSColin Percival	# For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
163648ffe56aSColin Percival	# system and generate an INDEX-PRESENT file.
163748ffe56aSColin Percival	fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
163848ffe56aSColin Percival
163948ffe56aSColin Percival	# Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
164048ffe56aSColin Percival	# correspond to lines in INDEX-PRESENT with hashes not appearing
164148ffe56aSColin Percival	# in INDEX-OLD or INDEX-NEW.  Also remove lines where the entry in
164248ffe56aSColin Percival	# INDEX-PRESENT has type - and there isn't a corresponding entry in
164348ffe56aSColin Percival	# INDEX-OLD with type -.
164448ffe56aSColin Percival	fetch_filter_unmodified_notpresent INDEX-OLD INDEX-PRESENT INDEX-NEW
164548ffe56aSColin Percival
164648ffe56aSColin Percival	# For each entry in INDEX-PRESENT of type -, remove any corresponding
164748ffe56aSColin Percival	# entry from INDEX-NEW if ${ALLOWADD} != "yes".  Remove all entries
164848ffe56aSColin Percival	# of type - from INDEX-PRESENT.
164948ffe56aSColin Percival	fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
165048ffe56aSColin Percival
165148ffe56aSColin Percival	# If ${ALLOWDELETE} != "yes", then remove any entries from
165248ffe56aSColin Percival	# INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
165348ffe56aSColin Percival	fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
165448ffe56aSColin Percival
165548ffe56aSColin Percival	# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
165648ffe56aSColin Percival	# INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
165748ffe56aSColin Percival	# replace the corresponding line of INDEX-NEW with one having the
165848ffe56aSColin Percival	# same metadata as the entry in INDEX-PRESENT.
165948ffe56aSColin Percival	fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
166048ffe56aSColin Percival
166148ffe56aSColin Percival	# Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
166248ffe56aSColin Percival	# no need to update a file if it isn't changing.
166348ffe56aSColin Percival	fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
166448ffe56aSColin Percival
166548ffe56aSColin Percival	# Prepare to fetch files: Generate a list of the files we need,
166648ffe56aSColin Percival	# copy the unmodified files we have into /files/, and generate
166748ffe56aSColin Percival	# a list of patches to download.
166848ffe56aSColin Percival	fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW
166948ffe56aSColin Percival
167048ffe56aSColin Percival	# Fetch files.
167148ffe56aSColin Percival	fetch_files || return 1
167248ffe56aSColin Percival
167348ffe56aSColin Percival	# Create and populate install manifest directory; and report what
167448ffe56aSColin Percival	# updates are available.
167548ffe56aSColin Percival	fetch_create_manifest || return 1
167648ffe56aSColin Percival
167748ffe56aSColin Percival	# Warn about any upcoming EoL
167848ffe56aSColin Percival	fetch_warn_eol || return 1
167948ffe56aSColin Percival}
168048ffe56aSColin Percival
168148ffe56aSColin Percival# Make sure that all the file hashes mentioned in $@ have corresponding
168248ffe56aSColin Percival# gzipped files stored in /files/.
168348ffe56aSColin Percivalinstall_verify () {
168448ffe56aSColin Percival	# Generate a list of hashes
168548ffe56aSColin Percival	cat $@ |
168648ffe56aSColin Percival	    cut -f 2,7 -d '|' |
168748ffe56aSColin Percival	    grep -E '^f' |
168848ffe56aSColin Percival	    cut -f 2 -d '|' |
168948ffe56aSColin Percival	    sort -u > filelist
169048ffe56aSColin Percival
169148ffe56aSColin Percival	# Make sure all the hashes exist
169248ffe56aSColin Percival	while read HASH; do
169348ffe56aSColin Percival		if ! [ -f files/${HASH}.gz ]; then
169448ffe56aSColin Percival			echo -n "Update files missing -- "
169548ffe56aSColin Percival			echo "this should never happen."
169648ffe56aSColin Percival			echo "Re-run '$0 fetch'."
169748ffe56aSColin Percival			return 1
169848ffe56aSColin Percival		fi
169948ffe56aSColin Percival	done < filelist
170048ffe56aSColin Percival
170148ffe56aSColin Percival	# Clean up
170248ffe56aSColin Percival	rm filelist
170348ffe56aSColin Percival}
170448ffe56aSColin Percival
170548ffe56aSColin Percival# Remove the system immutable flag from files
170648ffe56aSColin Percivalinstall_unschg () {
170748ffe56aSColin Percival	# Generate file list
170848ffe56aSColin Percival	cat $@ |
170948ffe56aSColin Percival	    cut -f 1 -d '|' > filelist
171048ffe56aSColin Percival
171148ffe56aSColin Percival	# Remove flags
171248ffe56aSColin Percival	while read F; do
171348ffe56aSColin Percival		if ! [ -e ${F} ]; then
171448ffe56aSColin Percival			continue
171548ffe56aSColin Percival		fi
171648ffe56aSColin Percival
171748ffe56aSColin Percival		chflags noschg ${F} || return 1
171848ffe56aSColin Percival	done < filelist
171948ffe56aSColin Percival
172048ffe56aSColin Percival	# Clean up
172148ffe56aSColin Percival	rm filelist
172248ffe56aSColin Percival}
172348ffe56aSColin Percival
172448ffe56aSColin Percival# Install new files
172548ffe56aSColin Percivalinstall_from_index () {
172648ffe56aSColin Percival	# First pass: Do everything apart from setting file flags.  We
172748ffe56aSColin Percival	# can't set flags yet, because schg inhibits hard linking.
172848ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
172948ffe56aSColin Percival	    tr '|' ' ' |
173048ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
173148ffe56aSColin Percival		case ${TYPE} in
173248ffe56aSColin Percival		d)
173348ffe56aSColin Percival			# Create a directory
173448ffe56aSColin Percival			install -d -o ${OWNER} -g ${GROUP}		\
173548ffe56aSColin Percival			    -m ${PERM} ${BASEDIR}/${FPATH}
173648ffe56aSColin Percival			;;
173748ffe56aSColin Percival		f)
173848ffe56aSColin Percival			if [ -z "${LINK}" ]; then
173948ffe56aSColin Percival				# Create a file, without setting flags.
174048ffe56aSColin Percival				gunzip < files/${HASH}.gz > ${HASH}
174148ffe56aSColin Percival				install -S -o ${OWNER} -g ${GROUP}	\
174248ffe56aSColin Percival				    -m ${PERM} ${HASH} ${BASEDIR}/${FPATH}
174348ffe56aSColin Percival				rm ${HASH}
174448ffe56aSColin Percival			else
174548ffe56aSColin Percival				# Create a hard link.
174648ffe56aSColin Percival				ln -f ${LINK} ${BASEDIR}/${FPATH}
174748ffe56aSColin Percival			fi
174848ffe56aSColin Percival			;;
174948ffe56aSColin Percival		L)
175048ffe56aSColin Percival			# Create a symlink
175148ffe56aSColin Percival			ln -sfh ${HASH} ${BASEDIR}/${FPATH}
175248ffe56aSColin Percival			;;
175348ffe56aSColin Percival		esac
175448ffe56aSColin Percival	    done
175548ffe56aSColin Percival
175648ffe56aSColin Percival	# Perform a second pass, adding file flags.
175748ffe56aSColin Percival	tr '|' ' ' < $1 |
175848ffe56aSColin Percival	    while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
175948ffe56aSColin Percival		if [ ${TYPE} = "f" ] &&
176048ffe56aSColin Percival		    ! [ ${FLAGS} = "0" ]; then
176148ffe56aSColin Percival			chflags ${FLAGS} ${BASEDIR}/${FPATH}
176248ffe56aSColin Percival		fi
176348ffe56aSColin Percival	    done
176448ffe56aSColin Percival}
176548ffe56aSColin Percival
176648ffe56aSColin Percival# Remove files which we want to delete
176748ffe56aSColin Percivalinstall_delete () {
176848ffe56aSColin Percival	# Generate list of new files
176948ffe56aSColin Percival	cut -f 1 -d '|' < $2 |
177048ffe56aSColin Percival	    sort > newfiles
177148ffe56aSColin Percival
177248ffe56aSColin Percival	# Generate subindex of old files we want to nuke
177348ffe56aSColin Percival	sort -k 1,1 -t '|' $1 |
177448ffe56aSColin Percival	    join -t '|' -v 1 - newfiles |
1775bce02f98SColin Percival	    sort -r -k 1,1 -t '|' |
177648ffe56aSColin Percival	    cut -f 1,2 -d '|' |
177748ffe56aSColin Percival	    tr '|' ' ' > killfiles
177848ffe56aSColin Percival
177948ffe56aSColin Percival	# Remove the offending bits
178048ffe56aSColin Percival	while read FPATH TYPE; do
178148ffe56aSColin Percival		case ${TYPE} in
178248ffe56aSColin Percival		d)
178348ffe56aSColin Percival			rmdir ${BASEDIR}/${FPATH}
178448ffe56aSColin Percival			;;
178548ffe56aSColin Percival		f)
178648ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
178748ffe56aSColin Percival			;;
178848ffe56aSColin Percival		L)
178948ffe56aSColin Percival			rm ${BASEDIR}/${FPATH}
179048ffe56aSColin Percival			;;
179148ffe56aSColin Percival		esac
179248ffe56aSColin Percival	done < killfiles
179348ffe56aSColin Percival
179448ffe56aSColin Percival	# Clean up
179548ffe56aSColin Percival	rm newfiles killfiles
179648ffe56aSColin Percival}
179748ffe56aSColin Percival
179848ffe56aSColin Percival# Update linker.hints if anything in /boot/ was touched
179948ffe56aSColin Percivalinstall_kldxref () {
180048ffe56aSColin Percival	if cat $@ |
180148ffe56aSColin Percival	    grep -qE '^/boot/'; then
180248ffe56aSColin Percival		kldxref -R /boot/
180348ffe56aSColin Percival	fi
180448ffe56aSColin Percival}
180548ffe56aSColin Percival
180648ffe56aSColin Percival# Rearrange bits to allow the installed updates to be rolled back
180748ffe56aSColin Percivalinstall_setup_rollback () {
180848ffe56aSColin Percival	if [ -L ${BDHASH}-rollback ]; then
180948ffe56aSColin Percival		mv ${BDHASH}-rollback ${BDHASH}-install/rollback
181048ffe56aSColin Percival	fi
181148ffe56aSColin Percival
181248ffe56aSColin Percival	mv ${BDHASH}-install ${BDHASH}-rollback
181348ffe56aSColin Percival}
181448ffe56aSColin Percival
181548ffe56aSColin Percival# Actually install updates
181648ffe56aSColin Percivalinstall_run () {
181748ffe56aSColin Percival	echo -n "Installing updates..."
181848ffe56aSColin Percival
181948ffe56aSColin Percival	# Make sure we have all the files we should have
182048ffe56aSColin Percival	install_verify ${BDHASH}-install/INDEX-OLD	\
182148ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
182248ffe56aSColin Percival
182348ffe56aSColin Percival	# Remove system immutable flag from files
182448ffe56aSColin Percival	install_unschg ${BDHASH}-install/INDEX-OLD	\
182548ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
182648ffe56aSColin Percival
182748ffe56aSColin Percival	# Install new files
182848ffe56aSColin Percival	install_from_index				\
182948ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
183048ffe56aSColin Percival
183148ffe56aSColin Percival	# Remove files which we want to delete
183248ffe56aSColin Percival	install_delete ${BDHASH}-install/INDEX-OLD	\
183348ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW || return 1
183448ffe56aSColin Percival
183548ffe56aSColin Percival	# Update linker.hints if anything in /boot/ was touched
183648ffe56aSColin Percival	install_kldxref ${BDHASH}-install/INDEX-OLD	\
183748ffe56aSColin Percival	    ${BDHASH}-install/INDEX-NEW
183848ffe56aSColin Percival
183948ffe56aSColin Percival	# Rearrange bits to allow the installed updates to be rolled back
184048ffe56aSColin Percival	install_setup_rollback
184148ffe56aSColin Percival
184248ffe56aSColin Percival	echo " done."
184348ffe56aSColin Percival}
184448ffe56aSColin Percival
184548ffe56aSColin Percival# Rearrange bits to allow the previous set of updates to be rolled back next.
184648ffe56aSColin Percivalrollback_setup_rollback () {
184748ffe56aSColin Percival	if [ -L ${BDHASH}-rollback/rollback ]; then
184848ffe56aSColin Percival		mv ${BDHASH}-rollback/rollback rollback-tmp
184948ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
185048ffe56aSColin Percival		rm ${BDHASH}-rollback
185148ffe56aSColin Percival		mv rollback-tmp ${BDHASH}-rollback
185248ffe56aSColin Percival	else
185348ffe56aSColin Percival		rm -r ${BDHASH}-rollback/
185448ffe56aSColin Percival		rm ${BDHASH}-rollback
185548ffe56aSColin Percival	fi
185648ffe56aSColin Percival}
185748ffe56aSColin Percival
185848ffe56aSColin Percival# Actually rollback updates
185948ffe56aSColin Percivalrollback_run () {
186048ffe56aSColin Percival	echo -n "Uninstalling updates..."
186148ffe56aSColin Percival
186248ffe56aSColin Percival	# If there are updates waiting to be installed, remove them; we
186348ffe56aSColin Percival	# want the user to re-run 'fetch' after rolling back updates.
186448ffe56aSColin Percival	if [ -L ${BDHASH}-install ]; then
186548ffe56aSColin Percival		rm -r ${BDHASH}-install/
186648ffe56aSColin Percival		rm ${BDHASH}-install
186748ffe56aSColin Percival	fi
186848ffe56aSColin Percival
186948ffe56aSColin Percival	# Make sure we have all the files we should have
187048ffe56aSColin Percival	install_verify ${BDHASH}-rollback/INDEX-NEW	\
187148ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
187248ffe56aSColin Percival
187348ffe56aSColin Percival	# Remove system immutable flag from files
187448ffe56aSColin Percival	install_unschg ${BDHASH}-rollback/INDEX-NEW	\
187548ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
187648ffe56aSColin Percival
187748ffe56aSColin Percival	# Install new files
187848ffe56aSColin Percival	install_from_index				\
187948ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
188048ffe56aSColin Percival
188148ffe56aSColin Percival	# Remove files which we want to delete
188248ffe56aSColin Percival	install_delete ${BDHASH}-rollback/INDEX-NEW	\
188348ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD || return 1
188448ffe56aSColin Percival
188548ffe56aSColin Percival	# Update linker.hints if anything in /boot/ was touched
188648ffe56aSColin Percival	install_kldxref ${BDHASH}-rollback/INDEX-NEW	\
188748ffe56aSColin Percival	    ${BDHASH}-rollback/INDEX-OLD
188848ffe56aSColin Percival
188948ffe56aSColin Percival	# Remove the rollback directory and the symlink pointing to it; and
189048ffe56aSColin Percival	# rearrange bits to allow the previous set of updates to be rolled
189148ffe56aSColin Percival	# back next.
189248ffe56aSColin Percival	rollback_setup_rollback
189348ffe56aSColin Percival
189448ffe56aSColin Percival	echo " done."
189548ffe56aSColin Percival}
189648ffe56aSColin Percival
189748ffe56aSColin Percival#### Main functions -- call parameter-handling and core functions
189848ffe56aSColin Percival
189948ffe56aSColin Percival# Using the command line, configuration file, and defaults,
190048ffe56aSColin Percival# set all the parameters which are needed later.
190148ffe56aSColin Percivalget_params () {
190248ffe56aSColin Percival	init_params
190348ffe56aSColin Percival	parse_cmdline $@
190448ffe56aSColin Percival	parse_conffile
190548ffe56aSColin Percival	default_params
190648ffe56aSColin Percival}
190748ffe56aSColin Percival
190848ffe56aSColin Percival# Fetch command.  Make sure that we're being called
190948ffe56aSColin Percival# interactively, then run fetch_check_params and fetch_run
191048ffe56aSColin Percivalcmd_fetch () {
191148ffe56aSColin Percival	if [ ! -t 0 ]; then
191248ffe56aSColin Percival		echo -n "`basename $0` fetch should not "
191348ffe56aSColin Percival		echo "be run non-interactively."
191448ffe56aSColin Percival		echo "Run `basename $0` cron instead."
191548ffe56aSColin Percival		exit 1
191648ffe56aSColin Percival	fi
191748ffe56aSColin Percival	fetch_check_params
191848ffe56aSColin Percival	fetch_run || exit 1
191948ffe56aSColin Percival}
192048ffe56aSColin Percival
192148ffe56aSColin Percival# Cron command.  Make sure the parameters are sensible; wait
192248ffe56aSColin Percival# rand(3600) seconds; then fetch updates.  While fetching updates,
192348ffe56aSColin Percival# send output to a temporary file; only print that file if the
192448ffe56aSColin Percival# fetching failed.
192548ffe56aSColin Percivalcmd_cron () {
192648ffe56aSColin Percival	fetch_check_params
192748ffe56aSColin Percival	sleep `jot -r 1 0 3600`
192848ffe56aSColin Percival
192948ffe56aSColin Percival	TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1
193048ffe56aSColin Percival	if ! fetch_run >> ${TMPFILE} ||
193148ffe56aSColin Percival	    ! grep -q "No updates needed" ${TMPFILE} ||
193248ffe56aSColin Percival	    [ ${VERBOSELEVEL} = "debug" ]; then
193348ffe56aSColin Percival		mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE}
193448ffe56aSColin Percival	fi
193548ffe56aSColin Percival
193648ffe56aSColin Percival	rm ${TMPFILE}
193748ffe56aSColin Percival}
193848ffe56aSColin Percival
193948ffe56aSColin Percival# Install downloaded updates.
194048ffe56aSColin Percivalcmd_install () {
194148ffe56aSColin Percival	install_check_params
194248ffe56aSColin Percival	install_run || exit 1
194348ffe56aSColin Percival}
194448ffe56aSColin Percival
194548ffe56aSColin Percival# Rollback most recently installed updates.
194648ffe56aSColin Percivalcmd_rollback () {
194748ffe56aSColin Percival	rollback_check_params
194848ffe56aSColin Percival	rollback_run || exit 1
194948ffe56aSColin Percival}
195048ffe56aSColin Percival
195148ffe56aSColin Percival#### Entry point
195248ffe56aSColin Percival
195348ffe56aSColin Percival# Make sure we find utilities from the base system
195448ffe56aSColin Percivalexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
195548ffe56aSColin Percival
1956f2890dbdSColin Percival# Set LC_ALL in order to avoid problems with character ranges like [A-Z].
1957f2890dbdSColin Percivalexport LC_ALL=C
1958f2890dbdSColin Percival
195948ffe56aSColin Percivalget_params $@
196048ffe56aSColin Percivalfor COMMAND in ${COMMANDS}; do
196148ffe56aSColin Percival	cmd_${COMMAND}
196248ffe56aSColin Percivaldone
1963