freebsd-update.sh revision 181425
1161748Scperciva#!/bin/sh 2161748Scperciva 3161748Scperciva#- 4173441Scperciva# Copyright 2004-2007 Colin Percival 5161748Scperciva# All rights reserved 6161748Scperciva# 7161748Scperciva# Redistribution and use in source and binary forms, with or without 8161748Scperciva# modification, are permitted providing that the following conditions 9161748Scperciva# are met: 10161748Scperciva# 1. Redistributions of source code must retain the above copyright 11161748Scperciva# notice, this list of conditions and the following disclaimer. 12161748Scperciva# 2. Redistributions in binary form must reproduce the above copyright 13161748Scperciva# notice, this list of conditions and the following disclaimer in the 14161748Scperciva# documentation and/or other materials provided with the distribution. 15161748Scperciva# 16161748Scperciva# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17161748Scperciva# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18161748Scperciva# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19161748Scperciva# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20161748Scperciva# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21161748Scperciva# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22161748Scperciva# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23161748Scperciva# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24161748Scperciva# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25161748Scperciva# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26161748Scperciva# POSSIBILITY OF SUCH DAMAGE. 27161748Scperciva 28161748Scperciva# $FreeBSD: head/usr.sbin/freebsd-update/freebsd-update.sh 181425 2008-08-08 04:34:00Z cperciva $ 29161748Scperciva 30161748Scperciva#### Usage function -- called from command-line handling code. 31161748Scperciva 32161748Scperciva# Usage instructions. Options not listed: 33161748Scperciva# --debug -- don't filter output from utilities 34161748Scperciva# --no-stats -- don't show progress statistics while fetching files 35161748Scpercivausage () { 36161748Scperciva cat <<EOF 37161748Scpercivausage: `basename $0` [options] command ... [path] 38161748Scperciva 39161748ScpercivaOptions: 40161748Scperciva -b basedir -- Operate on a system mounted at basedir 41161748Scperciva (default: /) 42161748Scperciva -d workdir -- Store working files in workdir 43161748Scperciva (default: /var/db/freebsd-update/) 44161748Scperciva -f conffile -- Read configuration options from conffile 45161748Scperciva (default: /etc/freebsd-update.conf) 46161748Scperciva -k KEY -- Trust an RSA key with SHA256 hash of KEY 47173564Scperciva -r release -- Target for upgrade (e.g., 6.2-RELEASE) 48161748Scperciva -s server -- Server from which to fetch updates 49161748Scperciva (default: update.FreeBSD.org) 50161748Scperciva -t address -- Mail output of cron command, if any, to address 51161748Scperciva (default: root) 52161748ScpercivaCommands: 53161748Scperciva fetch -- Fetch updates from server 54161748Scperciva cron -- Sleep rand(3600) seconds, fetch updates, and send an 55161748Scperciva email if updates were found 56173564Scperciva upgrade -- Fetch upgrades to FreeBSD version specified via -r option 57173564Scperciva install -- Install downloaded updates or upgrades 58161748Scperciva rollback -- Uninstall most recently installed updates 59181142Scperciva IDS -- Compare the system against an index of "known good" files. 60161748ScpercivaEOF 61161748Scperciva exit 0 62161748Scperciva} 63161748Scperciva 64161748Scperciva#### Configuration processing functions 65161748Scperciva 66161748Scperciva#- 67161748Scperciva# Configuration options are set in the following order of priority: 68161748Scperciva# 1. Command line options 69161748Scperciva# 2. Configuration file options 70161748Scperciva# 3. Default options 71161748Scperciva# In addition, certain options (e.g., IgnorePaths) can be specified multiple 72161748Scperciva# times and (as long as these are all in the same place, e.g., inside the 73161748Scperciva# configuration file) they will accumulate. Finally, because the path to the 74161748Scperciva# configuration file can be specified at the command line, the entire command 75161748Scperciva# line must be processed before we start reading the configuration file. 76161748Scperciva# 77161748Scperciva# Sound like a mess? It is. Here's how we handle this: 78161748Scperciva# 1. Initialize CONFFILE and all the options to "". 79161748Scperciva# 2. Process the command line. Throw an error if a non-accumulating option 80161748Scperciva# is specified twice. 81161748Scperciva# 3. If CONFFILE is "", set CONFFILE to /etc/freebsd-update.conf . 82161748Scperciva# 4. For all the configuration options X, set X_saved to X. 83161748Scperciva# 5. Initialize all the options to "". 84161748Scperciva# 6. Read CONFFILE line by line, parsing options. 85161748Scperciva# 7. For each configuration option X, set X to X_saved iff X_saved is not "". 86161748Scperciva# 8. Repeat steps 4-7, except setting options to their default values at (6). 87161748Scperciva 88161748ScpercivaCONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE 89161748Scperciva KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED 90181142Scperciva BASEDIR VERBOSELEVEL TARGETRELEASE STRICTCOMPONENTS MERGECHANGES 91181142Scperciva IDSIGNOREPATHS" 92161748Scperciva 93161748Scperciva# Set all the configuration options to "". 94161748Scpercivanullconfig () { 95161748Scperciva for X in ${CONFIGOPTIONS}; do 96161748Scperciva eval ${X}="" 97161748Scperciva done 98161748Scperciva} 99161748Scperciva 100161748Scperciva# For each configuration option X, set X_saved to X. 101161748Scpercivasaveconfig () { 102161748Scperciva for X in ${CONFIGOPTIONS}; do 103161748Scperciva eval ${X}_saved=\$${X} 104161748Scperciva done 105161748Scperciva} 106161748Scperciva 107161748Scperciva# For each configuration option X, set X to X_saved if X_saved is not "". 108161748Scpercivamergeconfig () { 109161748Scperciva for X in ${CONFIGOPTIONS}; do 110161748Scperciva eval _=\$${X}_saved 111161748Scperciva if ! [ -z "${_}" ]; then 112161748Scperciva eval ${X}=\$${X}_saved 113161748Scperciva fi 114161748Scperciva done 115161748Scperciva} 116161748Scperciva 117161748Scperciva# Set the trusted keyprint. 118161748Scpercivaconfig_KeyPrint () { 119161748Scperciva if [ -z ${KEYPRINT} ]; then 120161748Scperciva KEYPRINT=$1 121161748Scperciva else 122161748Scperciva return 1 123161748Scperciva fi 124161748Scperciva} 125161748Scperciva 126161748Scperciva# Set the working directory. 127161748Scpercivaconfig_WorkDir () { 128161748Scperciva if [ -z ${WORKDIR} ]; then 129161748Scperciva WORKDIR=$1 130161748Scperciva else 131161748Scperciva return 1 132161748Scperciva fi 133161748Scperciva} 134161748Scperciva 135161748Scperciva# Set the name of the server (pool) from which to fetch updates 136161748Scpercivaconfig_ServerName () { 137161748Scperciva if [ -z ${SERVERNAME} ]; then 138161748Scperciva SERVERNAME=$1 139161748Scperciva else 140161748Scperciva return 1 141161748Scperciva fi 142161748Scperciva} 143161748Scperciva 144161748Scperciva# Set the address to which 'cron' output will be mailed. 145161748Scpercivaconfig_MailTo () { 146161748Scperciva if [ -z ${MAILTO} ]; then 147161748Scperciva MAILTO=$1 148161748Scperciva else 149161748Scperciva return 1 150161748Scperciva fi 151161748Scperciva} 152161748Scperciva 153161748Scperciva# Set whether FreeBSD Update is allowed to add files (or directories, or 154161748Scperciva# symlinks) which did not previously exist. 155161748Scpercivaconfig_AllowAdd () { 156161748Scperciva if [ -z ${ALLOWADD} ]; then 157161748Scperciva case $1 in 158161748Scperciva [Yy][Ee][Ss]) 159161748Scperciva ALLOWADD=yes 160161748Scperciva ;; 161161748Scperciva [Nn][Oo]) 162161748Scperciva ALLOWADD=no 163161748Scperciva ;; 164161748Scperciva *) 165161748Scperciva return 1 166161748Scperciva ;; 167161748Scperciva esac 168161748Scperciva else 169161748Scperciva return 1 170161748Scperciva fi 171161748Scperciva} 172161748Scperciva 173161748Scperciva# Set whether FreeBSD Update is allowed to remove files/directories/symlinks. 174161748Scpercivaconfig_AllowDelete () { 175161748Scperciva if [ -z ${ALLOWDELETE} ]; then 176161748Scperciva case $1 in 177161748Scperciva [Yy][Ee][Ss]) 178161748Scperciva ALLOWDELETE=yes 179161748Scperciva ;; 180161748Scperciva [Nn][Oo]) 181161748Scperciva ALLOWDELETE=no 182161748Scperciva ;; 183161748Scperciva *) 184161748Scperciva return 1 185161748Scperciva ;; 186161748Scperciva esac 187161748Scperciva else 188161748Scperciva return 1 189161748Scperciva fi 190161748Scperciva} 191161748Scperciva 192161748Scperciva# Set whether FreeBSD Update should keep existing inode ownership, 193161748Scperciva# permissions, and flags, in the event that they have been modified locally 194161748Scperciva# after the release. 195161748Scpercivaconfig_KeepModifiedMetadata () { 196161748Scperciva if [ -z ${KEEPMODIFIEDMETADATA} ]; then 197161748Scperciva case $1 in 198161748Scperciva [Yy][Ee][Ss]) 199161748Scperciva KEEPMODIFIEDMETADATA=yes 200161748Scperciva ;; 201161748Scperciva [Nn][Oo]) 202161748Scperciva KEEPMODIFIEDMETADATA=no 203161748Scperciva ;; 204161748Scperciva *) 205161748Scperciva return 1 206161748Scperciva ;; 207161748Scperciva esac 208161748Scperciva else 209161748Scperciva return 1 210161748Scperciva fi 211161748Scperciva} 212161748Scperciva 213161748Scperciva# Add to the list of components which should be kept updated. 214161748Scpercivaconfig_Components () { 215161748Scperciva for C in $@; do 216161748Scperciva COMPONENTS="${COMPONENTS} ${C}" 217161748Scperciva done 218161748Scperciva} 219161748Scperciva 220161748Scperciva# Add to the list of paths under which updates will be ignored. 221161748Scpercivaconfig_IgnorePaths () { 222161748Scperciva for C in $@; do 223161748Scperciva IGNOREPATHS="${IGNOREPATHS} ${C}" 224161748Scperciva done 225161748Scperciva} 226161748Scperciva 227181142Scperciva# Add to the list of paths which IDS should ignore. 228181142Scpercivaconfig_IDSIgnorePaths () { 229181142Scperciva for C in $@; do 230181142Scperciva IDSIGNOREPATHS="${IDSIGNOREPATHS} ${C}" 231181142Scperciva done 232181142Scperciva} 233181142Scperciva 234161748Scperciva# Add to the list of paths within which updates will be performed only if the 235161748Scperciva# file on disk has not been modified locally. 236161748Scpercivaconfig_UpdateIfUnmodified () { 237161748Scperciva for C in $@; do 238161748Scperciva UPDATEIFUNMODIFIED="${UPDATEIFUNMODIFIED} ${C}" 239161748Scperciva done 240161748Scperciva} 241161748Scperciva 242173564Scperciva# Add to the list of paths within which updates to text files will be merged 243173564Scperciva# instead of overwritten. 244173564Scpercivaconfig_MergeChanges () { 245173564Scperciva for C in $@; do 246173564Scperciva MERGECHANGES="${MERGECHANGES} ${C}" 247173564Scperciva done 248173564Scperciva} 249173564Scperciva 250161748Scperciva# Work on a FreeBSD installation mounted under $1 251161748Scpercivaconfig_BaseDir () { 252161748Scperciva if [ -z ${BASEDIR} ]; then 253161748Scperciva BASEDIR=$1 254161748Scperciva else 255161748Scperciva return 1 256161748Scperciva fi 257161748Scperciva} 258161748Scperciva 259173564Scperciva# When fetching upgrades, should we assume the user wants exactly the 260173564Scperciva# components listed in COMPONENTS, rather than trying to guess based on 261173564Scperciva# what's currently installed? 262173564Scpercivaconfig_StrictComponents () { 263173564Scperciva if [ -z ${STRICTCOMPONENTS} ]; then 264173564Scperciva case $1 in 265173564Scperciva [Yy][Ee][Ss]) 266173564Scperciva STRICTCOMPONENTS=yes 267173564Scperciva ;; 268173564Scperciva [Nn][Oo]) 269173564Scperciva STRICTCOMPONENTS=no 270173564Scperciva ;; 271173564Scperciva *) 272173564Scperciva return 1 273173564Scperciva ;; 274173564Scperciva esac 275173564Scperciva else 276173564Scperciva return 1 277173564Scperciva fi 278173564Scperciva} 279173564Scperciva 280173564Scperciva# Upgrade to FreeBSD $1 281173564Scpercivaconfig_TargetRelease () { 282173564Scperciva if [ -z ${TARGETRELEASE} ]; then 283173564Scperciva TARGETRELEASE=$1 284173564Scperciva else 285173564Scperciva return 1 286173564Scperciva fi 287173564Scperciva} 288173564Scperciva 289161748Scperciva# Define what happens to output of utilities 290161748Scpercivaconfig_VerboseLevel () { 291161748Scperciva if [ -z ${VERBOSELEVEL} ]; then 292161748Scperciva case $1 in 293161748Scperciva [Dd][Ee][Bb][Uu][Gg]) 294161748Scperciva VERBOSELEVEL=debug 295161748Scperciva ;; 296161748Scperciva [Nn][Oo][Ss][Tt][Aa][Tt][Ss]) 297161748Scperciva VERBOSELEVEL=nostats 298161748Scperciva ;; 299161748Scperciva [Ss][Tt][Aa][Tt][Ss]) 300161748Scperciva VERBOSELEVEL=stats 301161748Scperciva ;; 302161748Scperciva *) 303161748Scperciva return 1 304161748Scperciva ;; 305161748Scperciva esac 306161748Scperciva else 307161748Scperciva return 1 308161748Scperciva fi 309161748Scperciva} 310161748Scperciva 311161748Scperciva# Handle one line of configuration 312161748Scpercivaconfigline () { 313161748Scperciva if [ $# -eq 0 ]; then 314161748Scperciva return 315161748Scperciva fi 316161748Scperciva 317161748Scperciva OPT=$1 318161748Scperciva shift 319161748Scperciva config_${OPT} $@ 320161748Scperciva} 321161748Scperciva 322161748Scperciva#### Parameter handling functions. 323161748Scperciva 324161748Scperciva# Initialize parameters to null, just in case they're 325161748Scperciva# set in the environment. 326161748Scpercivainit_params () { 327161748Scperciva # Configration settings 328161748Scperciva nullconfig 329161748Scperciva 330161748Scperciva # No configuration file set yet 331161748Scperciva CONFFILE="" 332161748Scperciva 333161748Scperciva # No commands specified yet 334161748Scperciva COMMANDS="" 335161748Scperciva} 336161748Scperciva 337161748Scperciva# Parse the command line 338161748Scpercivaparse_cmdline () { 339161748Scperciva while [ $# -gt 0 ]; do 340161748Scperciva case "$1" in 341161748Scperciva # Location of configuration file 342161748Scperciva -f) 343161748Scperciva if [ $# -eq 1 ]; then usage; fi 344161748Scperciva if [ ! -z "${CONFFILE}" ]; then usage; fi 345161748Scperciva shift; CONFFILE="$1" 346161748Scperciva ;; 347161748Scperciva 348161748Scperciva # Configuration file equivalents 349161748Scperciva -b) 350161748Scperciva if [ $# -eq 1 ]; then usage; fi; shift 351161748Scperciva config_BaseDir $1 || usage 352161748Scperciva ;; 353161748Scperciva -d) 354161748Scperciva if [ $# -eq 1 ]; then usage; fi; shift 355161748Scperciva config_WorkDir $1 || usage 356161748Scperciva ;; 357161748Scperciva -k) 358161748Scperciva if [ $# -eq 1 ]; then usage; fi; shift 359161748Scperciva config_KeyPrint $1 || usage 360161748Scperciva ;; 361161748Scperciva -s) 362161748Scperciva if [ $# -eq 1 ]; then usage; fi; shift 363161748Scperciva config_ServerName $1 || usage 364161748Scperciva ;; 365173564Scperciva -r) 366173564Scperciva if [ $# -eq 1 ]; then usage; fi; shift 367173564Scperciva config_TargetRelease $1 || usage 368173564Scperciva ;; 369161748Scperciva -t) 370161748Scperciva if [ $# -eq 1 ]; then usage; fi; shift 371161748Scperciva config_MailTo $1 || usage 372161748Scperciva ;; 373161748Scperciva -v) 374161748Scperciva if [ $# -eq 1 ]; then usage; fi; shift 375161748Scperciva config_VerboseLevel $1 || usage 376161748Scperciva ;; 377161748Scperciva 378161748Scperciva # Aliases for "-v debug" and "-v nostats" 379161748Scperciva --debug) 380161748Scperciva config_VerboseLevel debug || usage 381161748Scperciva ;; 382161748Scperciva --no-stats) 383161748Scperciva config_VerboseLevel nostats || usage 384161748Scperciva ;; 385161748Scperciva 386161748Scperciva # Commands 387181142Scperciva cron | fetch | upgrade | install | rollback | IDS) 388161748Scperciva COMMANDS="${COMMANDS} $1" 389161748Scperciva ;; 390161748Scperciva 391161748Scperciva # Anything else is an error 392161748Scperciva *) 393161748Scperciva usage 394161748Scperciva ;; 395161748Scperciva esac 396161748Scperciva shift 397161748Scperciva done 398161748Scperciva 399161748Scperciva # Make sure we have at least one command 400161748Scperciva if [ -z "${COMMANDS}" ]; then 401161748Scperciva usage 402161748Scperciva fi 403161748Scperciva} 404161748Scperciva 405161748Scperciva# Parse the configuration file 406161748Scpercivaparse_conffile () { 407161748Scperciva # If a configuration file was specified on the command line, check 408161748Scperciva # that it exists and is readable. 409161748Scperciva if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then 410161748Scperciva echo -n "File does not exist " 411161748Scperciva echo -n "or is not readable: " 412161748Scperciva echo ${CONFFILE} 413161748Scperciva exit 1 414161748Scperciva fi 415161748Scperciva 416161748Scperciva # If a configuration file was not specified on the command line, 417161748Scperciva # use the default configuration file path. If that default does 418161748Scperciva # not exist, give up looking for any configuration. 419161748Scperciva if [ -z "${CONFFILE}" ]; then 420161748Scperciva CONFFILE="/etc/freebsd-update.conf" 421161748Scperciva if [ ! -r "${CONFFILE}" ]; then 422161748Scperciva return 423161748Scperciva fi 424161748Scperciva fi 425161748Scperciva 426161748Scperciva # Save the configuration options specified on the command line, and 427161748Scperciva # clear all the options in preparation for reading the config file. 428161748Scperciva saveconfig 429161748Scperciva nullconfig 430161748Scperciva 431161748Scperciva # Read the configuration file. Anything after the first '#' is 432161748Scperciva # ignored, and any blank lines are ignored. 433161748Scperciva L=0 434161748Scperciva while read LINE; do 435161748Scperciva L=$(($L + 1)) 436161748Scperciva LINEX=`echo "${LINE}" | cut -f 1 -d '#'` 437161748Scperciva if ! configline ${LINEX}; then 438161748Scperciva echo "Error processing configuration file, line $L:" 439161748Scperciva echo "==> ${LINE}" 440161748Scperciva exit 1 441161748Scperciva fi 442161748Scperciva done < ${CONFFILE} 443161748Scperciva 444161748Scperciva # Merge the settings read from the configuration file with those 445161748Scperciva # provided at the command line. 446161748Scperciva mergeconfig 447161748Scperciva} 448161748Scperciva 449161748Scperciva# Provide some default parameters 450161748Scpercivadefault_params () { 451161748Scperciva # Save any parameters already configured, and clear the slate 452161748Scperciva saveconfig 453161748Scperciva nullconfig 454161748Scperciva 455161748Scperciva # Default configurations 456161748Scperciva config_WorkDir /var/db/freebsd-update 457161748Scperciva config_MailTo root 458161748Scperciva config_AllowAdd yes 459161748Scperciva config_AllowDelete yes 460161748Scperciva config_KeepModifiedMetadata yes 461161748Scperciva config_BaseDir / 462161748Scperciva config_VerboseLevel stats 463173564Scperciva config_StrictComponents no 464161748Scperciva 465161748Scperciva # Merge these defaults into the earlier-configured settings 466161748Scperciva mergeconfig 467161748Scperciva} 468161748Scperciva 469161748Scperciva# Set utility output filtering options, based on ${VERBOSELEVEL} 470161748Scpercivafetch_setup_verboselevel () { 471161748Scperciva case ${VERBOSELEVEL} in 472161748Scperciva debug) 473161748Scperciva QUIETREDIR="/dev/stderr" 474161748Scperciva QUIETFLAG=" " 475161748Scperciva STATSREDIR="/dev/stderr" 476161748Scperciva DDSTATS=".." 477161748Scperciva XARGST="-t" 478161748Scperciva NDEBUG=" " 479161748Scperciva ;; 480161748Scperciva nostats) 481161748Scperciva QUIETREDIR="" 482161748Scperciva QUIETFLAG="" 483161748Scperciva STATSREDIR="/dev/null" 484161748Scperciva DDSTATS=".." 485161748Scperciva XARGST="" 486161748Scperciva NDEBUG="" 487161748Scperciva ;; 488161748Scperciva stats) 489161748Scperciva QUIETREDIR="/dev/null" 490161748Scperciva QUIETFLAG="-q" 491161748Scperciva STATSREDIR="/dev/stdout" 492161748Scperciva DDSTATS="" 493161748Scperciva XARGST="" 494161748Scperciva NDEBUG="-n" 495161748Scperciva ;; 496161748Scperciva esac 497161748Scperciva} 498161748Scperciva 499161748Scperciva# Perform sanity checks and set some final parameters 500161748Scperciva# in preparation for fetching files. Figure out which 501161748Scperciva# set of updates should be downloaded: If the user is 502161748Scperciva# running *-p[0-9]+, strip off the last part; if the 503161748Scperciva# user is running -SECURITY, call it -RELEASE. Chdir 504161748Scperciva# into the working directory. 505161748Scpercivafetch_check_params () { 506161748Scperciva export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)" 507161748Scperciva 508161748Scperciva _SERVERNAME_z=\ 509161748Scperciva"SERVERNAME must be given via command line or configuration file." 510161748Scperciva _KEYPRINT_z="Key must be given via -k option or configuration file." 511161748Scperciva _KEYPRINT_bad="Invalid key fingerprint: " 512161748Scperciva _WORKDIR_bad="Directory does not exist or is not writable: " 513161748Scperciva 514161748Scperciva if [ -z "${SERVERNAME}" ]; then 515161748Scperciva echo -n "`basename $0`: " 516161748Scperciva echo "${_SERVERNAME_z}" 517161748Scperciva exit 1 518161748Scperciva fi 519161748Scperciva if [ -z "${KEYPRINT}" ]; then 520161748Scperciva echo -n "`basename $0`: " 521161748Scperciva echo "${_KEYPRINT_z}" 522161748Scperciva exit 1 523161748Scperciva fi 524161748Scperciva if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then 525161748Scperciva echo -n "`basename $0`: " 526161748Scperciva echo -n "${_KEYPRINT_bad}" 527161748Scperciva echo ${KEYPRINT} 528161748Scperciva exit 1 529161748Scperciva fi 530161748Scperciva if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then 531161748Scperciva echo -n "`basename $0`: " 532161748Scperciva echo -n "${_WORKDIR_bad}" 533161748Scperciva echo ${WORKDIR} 534161748Scperciva exit 1 535161748Scperciva fi 536161748Scperciva cd ${WORKDIR} || exit 1 537161748Scperciva 538161748Scperciva # Generate release number. The s/SECURITY/RELEASE/ bit exists 539161748Scperciva # to provide an upgrade path for FreeBSD Update 1.x users, since 540161748Scperciva # the kernels provided by FreeBSD Update 1.x are always labelled 541161748Scperciva # as X.Y-SECURITY. 542161748Scperciva RELNUM=`uname -r | 543161748Scperciva sed -E 's,-p[0-9]+,,' | 544161748Scperciva sed -E 's,-SECURITY,-RELEASE,'` 545161748Scperciva ARCH=`uname -m` 546161748Scperciva FETCHDIR=${RELNUM}/${ARCH} 547173564Scperciva PATCHDIR=${RELNUM}/${ARCH}/bp 548161748Scperciva 549161748Scperciva # Figure out what directory contains the running kernel 550161748Scperciva BOOTFILE=`sysctl -n kern.bootfile` 551161748Scperciva KERNELDIR=${BOOTFILE%/kernel} 552161748Scperciva if ! [ -d ${KERNELDIR} ]; then 553161748Scperciva echo "Cannot identify running kernel" 554161748Scperciva exit 1 555161748Scperciva fi 556161748Scperciva 557167189Scperciva # Figure out what kernel configuration is running. We start with 558167189Scperciva # the output of `uname -i`, and then make the following adjustments: 559167189Scperciva # 1. Replace "SMP-GENERIC" with "SMP". Why the SMP kernel config 560167189Scperciva # file says "ident SMP-GENERIC", I don't know... 561167189Scperciva # 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64" 562167189Scperciva # _and_ `sysctl kern.version` contains a line which ends "/SMP", then 563167189Scperciva # we're running an SMP kernel. This mis-identification is a bug 564167189Scperciva # which was fixed in 6.2-STABLE. 565167189Scperciva KERNCONF=`uname -i` 566167189Scperciva if [ ${KERNCONF} = "SMP-GENERIC" ]; then 567167189Scperciva KERNCONF=SMP 568167189Scperciva fi 569167189Scperciva if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then 570167189Scperciva if sysctl kern.version | grep -qE '/SMP$'; then 571167189Scperciva KERNCONF=SMP 572167189Scperciva fi 573167189Scperciva fi 574167189Scperciva 575161748Scperciva # Define some paths 576161748Scperciva BSPATCH=/usr/bin/bspatch 577161748Scperciva SHA256=/sbin/sha256 578161748Scperciva PHTTPGET=/usr/libexec/phttpget 579161748Scperciva 580161748Scperciva # Set up variables relating to VERBOSELEVEL 581161748Scperciva fetch_setup_verboselevel 582161748Scperciva 583161748Scperciva # Construct a unique name from ${BASEDIR} 584161748Scperciva BDHASH=`echo ${BASEDIR} | sha256 -q` 585161748Scperciva} 586161748Scperciva 587173564Scperciva# Perform sanity checks etc. before fetching upgrades. 588173564Scpercivaupgrade_check_params () { 589173564Scperciva fetch_check_params 590173564Scperciva 591173564Scperciva # Unless set otherwise, we're upgrading to the same kernel config. 592173564Scperciva NKERNCONF=${KERNCONF} 593173564Scperciva 594173564Scperciva # We need TARGETRELEASE set 595173564Scperciva _TARGETRELEASE_z="Release target must be specified via -r option." 596173564Scperciva if [ -z "${TARGETRELEASE}" ]; then 597173564Scperciva echo -n "`basename $0`: " 598173564Scperciva echo "${_TARGETRELEASE_z}" 599173564Scperciva exit 1 600173564Scperciva fi 601173564Scperciva 602173564Scperciva # The target release should be != the current release. 603173564Scperciva if [ "${TARGETRELEASE}" = "${RELNUM}" ]; then 604173564Scperciva echo -n "`basename $0`: " 605173564Scperciva echo "Cannot upgrade from ${RELNUM} to itself" 606173564Scperciva exit 1 607173564Scperciva fi 608173564Scperciva 609173564Scperciva # Turning off AllowAdd or AllowDelete is a bad idea for upgrades. 610173564Scperciva if [ "${ALLOWADD}" = "no" ]; then 611173564Scperciva echo -n "`basename $0`: " 612173564Scperciva echo -n "WARNING: \"AllowAdd no\" is a bad idea " 613173564Scperciva echo "when upgrading between releases." 614173564Scperciva echo 615173564Scperciva fi 616173564Scperciva if [ "${ALLOWDELETE}" = "no" ]; then 617173564Scperciva echo -n "`basename $0`: " 618173564Scperciva echo -n "WARNING: \"AllowDelete no\" is a bad idea " 619173564Scperciva echo "when upgrading between releases." 620173564Scperciva echo 621173564Scperciva fi 622173564Scperciva 623173564Scperciva # Set EDITOR to /usr/bin/vi if it isn't already set 624173564Scperciva : ${EDITOR:='/usr/bin/vi'} 625173564Scperciva} 626173564Scperciva 627161748Scperciva# Perform sanity checks and set some final parameters in 628161748Scperciva# preparation for installing updates. 629161748Scpercivainstall_check_params () { 630161748Scperciva # Check that we are root. All sorts of things won't work otherwise. 631161748Scperciva if [ `id -u` != 0 ]; then 632161748Scperciva echo "You must be root to run this." 633161748Scperciva exit 1 634161748Scperciva fi 635161748Scperciva 636173441Scperciva # Check that securelevel <= 0. Otherwise we can't update schg files. 637173441Scperciva if [ `sysctl -n kern.securelevel` -gt 0 ]; then 638173441Scperciva echo "Updates cannot be installed when the system securelevel" 639173441Scperciva echo "is greater than zero." 640173441Scperciva exit 1 641173441Scperciva fi 642173441Scperciva 643161748Scperciva # Check that we have a working directory 644161748Scperciva _WORKDIR_bad="Directory does not exist or is not writable: " 645161748Scperciva if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then 646161748Scperciva echo -n "`basename $0`: " 647161748Scperciva echo -n "${_WORKDIR_bad}" 648161748Scperciva echo ${WORKDIR} 649161748Scperciva exit 1 650161748Scperciva fi 651161748Scperciva cd ${WORKDIR} || exit 1 652161748Scperciva 653161748Scperciva # Construct a unique name from ${BASEDIR} 654161748Scperciva BDHASH=`echo ${BASEDIR} | sha256 -q` 655161748Scperciva 656161748Scperciva # Check that we have updates ready to install 657161748Scperciva if ! [ -L ${BDHASH}-install ]; then 658161748Scperciva echo "No updates are available to install." 659161748Scperciva echo "Run '$0 fetch' first." 660161748Scperciva exit 1 661161748Scperciva fi 662161748Scperciva if ! [ -f ${BDHASH}-install/INDEX-OLD ] || 663161748Scperciva ! [ -f ${BDHASH}-install/INDEX-NEW ]; then 664161748Scperciva echo "Update manifest is corrupt -- this should never happen." 665161748Scperciva echo "Re-run '$0 fetch'." 666161748Scperciva exit 1 667161748Scperciva fi 668161748Scperciva} 669161748Scperciva 670161748Scperciva# Perform sanity checks and set some final parameters in 671161748Scperciva# preparation for UNinstalling updates. 672161748Scpercivarollback_check_params () { 673161748Scperciva # Check that we are root. All sorts of things won't work otherwise. 674161748Scperciva if [ `id -u` != 0 ]; then 675161748Scperciva echo "You must be root to run this." 676161748Scperciva exit 1 677161748Scperciva fi 678161748Scperciva 679161748Scperciva # Check that we have a working directory 680161748Scperciva _WORKDIR_bad="Directory does not exist or is not writable: " 681161748Scperciva if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then 682161748Scperciva echo -n "`basename $0`: " 683161748Scperciva echo -n "${_WORKDIR_bad}" 684161748Scperciva echo ${WORKDIR} 685161748Scperciva exit 1 686161748Scperciva fi 687161748Scperciva cd ${WORKDIR} || exit 1 688161748Scperciva 689161748Scperciva # Construct a unique name from ${BASEDIR} 690161748Scperciva BDHASH=`echo ${BASEDIR} | sha256 -q` 691161748Scperciva 692161748Scperciva # Check that we have updates ready to rollback 693161748Scperciva if ! [ -L ${BDHASH}-rollback ]; then 694161748Scperciva echo "No rollback directory found." 695161748Scperciva exit 1 696161748Scperciva fi 697161748Scperciva if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] || 698161748Scperciva ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then 699161748Scperciva echo "Update manifest is corrupt -- this should never happen." 700161748Scperciva exit 1 701161748Scperciva fi 702161748Scperciva} 703161748Scperciva 704181142Scperciva# Perform sanity checks and set some final parameters 705181142Scperciva# in preparation for comparing the system against the 706181142Scperciva# published index. Figure out which index we should 707181142Scperciva# compare against: If the user is running *-p[0-9]+, 708181142Scperciva# strip off the last part; if the user is running 709181142Scperciva# -SECURITY, call it -RELEASE. Chdir into the working 710181142Scperciva# directory. 711181142ScpercivaIDS_check_params () { 712181142Scperciva export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)" 713181142Scperciva 714181142Scperciva _SERVERNAME_z=\ 715181142Scperciva"SERVERNAME must be given via command line or configuration file." 716181142Scperciva _KEYPRINT_z="Key must be given via -k option or configuration file." 717181142Scperciva _KEYPRINT_bad="Invalid key fingerprint: " 718181142Scperciva _WORKDIR_bad="Directory does not exist or is not writable: " 719181142Scperciva 720181142Scperciva if [ -z "${SERVERNAME}" ]; then 721181142Scperciva echo -n "`basename $0`: " 722181142Scperciva echo "${_SERVERNAME_z}" 723181142Scperciva exit 1 724181142Scperciva fi 725181142Scperciva if [ -z "${KEYPRINT}" ]; then 726181142Scperciva echo -n "`basename $0`: " 727181142Scperciva echo "${_KEYPRINT_z}" 728181142Scperciva exit 1 729181142Scperciva fi 730181142Scperciva if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then 731181142Scperciva echo -n "`basename $0`: " 732181142Scperciva echo -n "${_KEYPRINT_bad}" 733181142Scperciva echo ${KEYPRINT} 734181142Scperciva exit 1 735181142Scperciva fi 736181142Scperciva if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then 737181142Scperciva echo -n "`basename $0`: " 738181142Scperciva echo -n "${_WORKDIR_bad}" 739181142Scperciva echo ${WORKDIR} 740181142Scperciva exit 1 741181142Scperciva fi 742181142Scperciva cd ${WORKDIR} || exit 1 743181142Scperciva 744181142Scperciva # Generate release number. The s/SECURITY/RELEASE/ bit exists 745181142Scperciva # to provide an upgrade path for FreeBSD Update 1.x users, since 746181142Scperciva # the kernels provided by FreeBSD Update 1.x are always labelled 747181142Scperciva # as X.Y-SECURITY. 748181142Scperciva RELNUM=`uname -r | 749181142Scperciva sed -E 's,-p[0-9]+,,' | 750181142Scperciva sed -E 's,-SECURITY,-RELEASE,'` 751181142Scperciva ARCH=`uname -m` 752181142Scperciva FETCHDIR=${RELNUM}/${ARCH} 753181142Scperciva PATCHDIR=${RELNUM}/${ARCH}/bp 754181142Scperciva 755181142Scperciva # Figure out what directory contains the running kernel 756181142Scperciva BOOTFILE=`sysctl -n kern.bootfile` 757181142Scperciva KERNELDIR=${BOOTFILE%/kernel} 758181142Scperciva if ! [ -d ${KERNELDIR} ]; then 759181142Scperciva echo "Cannot identify running kernel" 760181142Scperciva exit 1 761181142Scperciva fi 762181142Scperciva 763181142Scperciva # Figure out what kernel configuration is running. We start with 764181142Scperciva # the output of `uname -i`, and then make the following adjustments: 765181142Scperciva # 1. Replace "SMP-GENERIC" with "SMP". Why the SMP kernel config 766181142Scperciva # file says "ident SMP-GENERIC", I don't know... 767181142Scperciva # 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64" 768181142Scperciva # _and_ `sysctl kern.version` contains a line which ends "/SMP", then 769181142Scperciva # we're running an SMP kernel. This mis-identification is a bug 770181142Scperciva # which was fixed in 6.2-STABLE. 771181142Scperciva KERNCONF=`uname -i` 772181142Scperciva if [ ${KERNCONF} = "SMP-GENERIC" ]; then 773181142Scperciva KERNCONF=SMP 774181142Scperciva fi 775181142Scperciva if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then 776181142Scperciva if sysctl kern.version | grep -qE '/SMP$'; then 777181142Scperciva KERNCONF=SMP 778181142Scperciva fi 779181142Scperciva fi 780181142Scperciva 781181142Scperciva # Define some paths 782181142Scperciva SHA256=/sbin/sha256 783181142Scperciva PHTTPGET=/usr/libexec/phttpget 784181142Scperciva 785181142Scperciva # Set up variables relating to VERBOSELEVEL 786181142Scperciva fetch_setup_verboselevel 787181142Scperciva} 788181142Scperciva 789161748Scperciva#### Core functionality -- the actual work gets done here 790161748Scperciva 791161748Scperciva# Use an SRV query to pick a server. If the SRV query doesn't provide 792161748Scperciva# a useful answer, use the server name specified by the user. 793161748Scperciva# Put another way... look up _http._tcp.${SERVERNAME} and pick a server 794161748Scperciva# from that; or if no servers are returned, use ${SERVERNAME}. 795161748Scperciva# This allows a user to specify "portsnap.freebsd.org" (in which case 796161748Scperciva# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org" 797161748Scperciva# (in which case portsnap will use that particular server, since there 798161748Scperciva# won't be an SRV entry for that name). 799161748Scperciva# 800161748Scperciva# We ignore the Port field, since we are always going to use port 80. 801161748Scperciva 802161748Scperciva# Fetch the mirror list, but do not pick a mirror yet. Returns 1 if 803161748Scperciva# no mirrors are available for any reason. 804161748Scpercivafetch_pick_server_init () { 805161748Scperciva : > serverlist_tried 806161748Scperciva 807161748Scperciva# Check that host(1) exists (i.e., that the system wasn't built with the 808161748Scperciva# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist. 809161748Scperciva if ! which -s host; then 810161748Scperciva : > serverlist_full 811161748Scperciva return 1 812161748Scperciva fi 813161748Scperciva 814161748Scperciva echo -n "Looking up ${SERVERNAME} mirrors... " 815161748Scperciva 816161748Scperciva# Issue the SRV query and pull out the Priority, Weight, and Target fields. 817161748Scperciva# BIND 9 prints "$name has SRV record ..." while BIND 8 prints 818161748Scperciva# "$name server selection ..."; we allow either format. 819161748Scperciva MLIST="_http._tcp.${SERVERNAME}" 820161748Scperciva host -t srv "${MLIST}" | 821161748Scperciva sed -nE "s/${MLIST} (has SRV record|server selection) //p" | 822161748Scperciva cut -f 1,2,4 -d ' ' | 823161748Scperciva sed -e 's/\.$//' | 824161748Scperciva sort > serverlist_full 825161748Scperciva 826161748Scperciva# If no records, give up -- we'll just use the server name we were given. 827161748Scperciva if [ `wc -l < serverlist_full` -eq 0 ]; then 828161748Scperciva echo "none found." 829161748Scperciva return 1 830161748Scperciva fi 831161748Scperciva 832161748Scperciva# Report how many mirrors we found. 833161748Scperciva echo `wc -l < serverlist_full` "mirrors found." 834161748Scperciva 835161748Scperciva# Generate a random seed for use in picking mirrors. If HTTP_PROXY 836161748Scperciva# is set, this will be used to generate the seed; otherwise, the seed 837161748Scperciva# will be random. 838161748Scperciva if [ -n "${HTTP_PROXY}${http_proxy}" ]; then 839161748Scperciva RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" | 840161748Scperciva tr -d 'a-f' | 841161748Scperciva cut -c 1-9` 842161748Scperciva else 843161748Scperciva RANDVALUE=`jot -r 1 0 999999999` 844161748Scperciva fi 845161748Scperciva} 846161748Scperciva 847161748Scperciva# Pick a mirror. Returns 1 if we have run out of mirrors to try. 848161748Scpercivafetch_pick_server () { 849161748Scperciva# Generate a list of not-yet-tried mirrors 850161748Scperciva sort serverlist_tried | 851161748Scperciva comm -23 serverlist_full - > serverlist 852161748Scperciva 853161748Scperciva# Have we run out of mirrors? 854161748Scperciva if [ `wc -l < serverlist` -eq 0 ]; then 855161748Scperciva echo "No mirrors remaining, giving up." 856161748Scperciva return 1 857161748Scperciva fi 858161748Scperciva 859161748Scperciva# Find the highest priority level (lowest numeric value). 860161748Scperciva SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1` 861161748Scperciva 862161748Scperciva# Add up the weights of the response lines at that priority level. 863161748Scperciva SRV_WSUM=0; 864161748Scperciva while read X; do 865161748Scperciva case "$X" in 866161748Scperciva ${SRV_PRIORITY}\ *) 867161748Scperciva SRV_W=`echo $X | cut -f 2 -d ' '` 868161748Scperciva SRV_WSUM=$(($SRV_WSUM + $SRV_W)) 869161748Scperciva ;; 870161748Scperciva esac 871161748Scperciva done < serverlist 872161748Scperciva 873161748Scperciva# If all the weights are 0, pretend that they are all 1 instead. 874161748Scperciva if [ ${SRV_WSUM} -eq 0 ]; then 875161748Scperciva SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l` 876161748Scperciva SRV_W_ADD=1 877161748Scperciva else 878161748Scperciva SRV_W_ADD=0 879161748Scperciva fi 880161748Scperciva 881161748Scperciva# Pick a value between 0 and the sum of the weights - 1 882161748Scperciva SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}` 883161748Scperciva 884161748Scperciva# Read through the list of mirrors and set SERVERNAME. Write the line 885161748Scperciva# corresponding to the mirror we selected into serverlist_tried so that 886161748Scperciva# we won't try it again. 887161748Scperciva while read X; do 888161748Scperciva case "$X" in 889161748Scperciva ${SRV_PRIORITY}\ *) 890161748Scperciva SRV_W=`echo $X | cut -f 2 -d ' '` 891161748Scperciva SRV_W=$(($SRV_W + $SRV_W_ADD)) 892161748Scperciva if [ $SRV_RND -lt $SRV_W ]; then 893161748Scperciva SERVERNAME=`echo $X | cut -f 3 -d ' '` 894161748Scperciva echo "$X" >> serverlist_tried 895161748Scperciva break 896161748Scperciva else 897161748Scperciva SRV_RND=$(($SRV_RND - $SRV_W)) 898161748Scperciva fi 899161748Scperciva ;; 900161748Scperciva esac 901161748Scperciva done < serverlist 902161748Scperciva} 903161748Scperciva 904161748Scperciva# Take a list of ${oldhash}|${newhash} and output a list of needed patches, 905161748Scperciva# i.e., those for which we have ${oldhash} and don't have ${newhash}. 906161748Scpercivafetch_make_patchlist () { 907161748Scperciva grep -vE "^([0-9a-f]{64})\|\1$" | 908161748Scperciva tr '|' ' ' | 909161748Scperciva while read X Y; do 910161748Scperciva if [ -f "files/${Y}.gz" ] || 911161748Scperciva [ ! -f "files/${X}.gz" ]; then 912161748Scperciva continue 913161748Scperciva fi 914161748Scperciva echo "${X}|${Y}" 915161748Scperciva done | uniq 916161748Scperciva} 917161748Scperciva 918161748Scperciva# Print user-friendly progress statistics 919161748Scpercivafetch_progress () { 920161748Scperciva LNC=0 921161748Scperciva while read x; do 922161748Scperciva LNC=$(($LNC + 1)) 923161748Scperciva if [ $(($LNC % 10)) = 0 ]; then 924161748Scperciva echo -n $LNC 925161748Scperciva elif [ $(($LNC % 2)) = 0 ]; then 926161748Scperciva echo -n . 927161748Scperciva fi 928161748Scperciva done 929161748Scperciva echo -n " " 930161748Scperciva} 931161748Scperciva 932173564Scperciva# Function for asking the user if everything is ok 933173564Scpercivacontinuep () { 934173564Scperciva while read -p "Does this look reasonable (y/n)? " CONTINUE; do 935173564Scperciva case "${CONTINUE}" in 936173564Scperciva y*) 937173564Scperciva return 0 938173564Scperciva ;; 939173564Scperciva n*) 940173564Scperciva return 1 941173564Scperciva ;; 942173564Scperciva esac 943173564Scperciva done 944173564Scperciva} 945173564Scperciva 946161748Scperciva# Initialize the working directory 947161748Scpercivaworkdir_init () { 948161748Scperciva mkdir -p files 949161748Scperciva touch tINDEX.present 950161748Scperciva} 951161748Scperciva 952161748Scperciva# Check that we have a public key with an appropriate hash, or 953161748Scperciva# fetch the key if it doesn't exist. Returns 1 if the key has 954161748Scperciva# not yet been fetched. 955161748Scpercivafetch_key () { 956161748Scperciva if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then 957161748Scperciva return 0 958161748Scperciva fi 959161748Scperciva 960161748Scperciva echo -n "Fetching public key from ${SERVERNAME}... " 961161748Scperciva rm -f pub.ssl 962161748Scperciva fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \ 963161748Scperciva 2>${QUIETREDIR} || true 964161748Scperciva if ! [ -r pub.ssl ]; then 965161748Scperciva echo "failed." 966161748Scperciva return 1 967161748Scperciva fi 968161748Scperciva if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then 969161748Scperciva echo "key has incorrect hash." 970161748Scperciva rm -f pub.ssl 971161748Scperciva return 1 972161748Scperciva fi 973161748Scperciva echo "done." 974161748Scperciva} 975161748Scperciva 976161748Scperciva# Fetch metadata signature, aka "tag". 977161748Scpercivafetch_tag () { 978173564Scperciva echo -n "Fetching metadata signature " 979173564Scperciva echo ${NDEBUG} "for ${RELNUM} from ${SERVERNAME}... " 980161748Scperciva rm -f latest.ssl 981161748Scperciva fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl \ 982161748Scperciva 2>${QUIETREDIR} || true 983161748Scperciva if ! [ -r latest.ssl ]; then 984161748Scperciva echo "failed." 985161748Scperciva return 1 986161748Scperciva fi 987161748Scperciva 988161748Scperciva openssl rsautl -pubin -inkey pub.ssl -verify \ 989161748Scperciva < latest.ssl > tag.new 2>${QUIETREDIR} || true 990161748Scperciva rm latest.ssl 991161748Scperciva 992161748Scperciva if ! [ `wc -l < tag.new` = 1 ] || 993161748Scperciva ! grep -qE \ 994161748Scperciva "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \ 995161748Scperciva tag.new; then 996161748Scperciva echo "invalid signature." 997161748Scperciva return 1 998161748Scperciva fi 999161748Scperciva 1000161748Scperciva echo "done." 1001161748Scperciva 1002161748Scperciva RELPATCHNUM=`cut -f 4 -d '|' < tag.new` 1003161748Scperciva TINDEXHASH=`cut -f 5 -d '|' < tag.new` 1004161748Scperciva EOLTIME=`cut -f 6 -d '|' < tag.new` 1005161748Scperciva} 1006161748Scperciva 1007161748Scperciva# Sanity-check the patch number in a tag, to make sure that we're not 1008161748Scperciva# going to "update" backwards and to prevent replay attacks. 1009161748Scpercivafetch_tagsanity () { 1010161748Scperciva # Check that we're not going to move from -pX to -pY with Y < X. 1011161748Scperciva RELPX=`uname -r | sed -E 's,.*-,,'` 1012161748Scperciva if echo ${RELPX} | grep -qE '^p[0-9]+$'; then 1013161748Scperciva RELPX=`echo ${RELPX} | cut -c 2-` 1014161748Scperciva else 1015161748Scperciva RELPX=0 1016161748Scperciva fi 1017161748Scperciva if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then 1018161748Scperciva echo 1019161748Scperciva echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})" 1020161748Scperciva echo " appear older than what" 1021161748Scperciva echo "we are currently running (`uname -r`)!" 1022161748Scperciva echo "Cowardly refusing to proceed any further." 1023161748Scperciva return 1 1024161748Scperciva fi 1025161748Scperciva 1026161748Scperciva # If "tag" exists and corresponds to ${RELNUM}, make sure that 1027161748Scperciva # it contains a patch number <= RELPATCHNUM, in order to protect 1028161748Scperciva # against rollback (replay) attacks. 1029161748Scperciva if [ -f tag ] && 1030161748Scperciva grep -qE \ 1031161748Scperciva "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \ 1032161748Scperciva tag; then 1033161748Scperciva LASTRELPATCHNUM=`cut -f 4 -d '|' < tag` 1034161748Scperciva 1035161748Scperciva if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then 1036161748Scperciva echo 1037161748Scperciva echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})" 1038161748Scperciva echo " are older than the" 1039161748Scperciva echo -n "most recently seen updates" 1040161748Scperciva echo " (${RELNUM}-p${LASTRELPATCHNUM})." 1041161748Scperciva echo "Cowardly refusing to proceed any further." 1042161748Scperciva return 1 1043161748Scperciva fi 1044161748Scperciva fi 1045161748Scperciva} 1046161748Scperciva 1047161748Scperciva# Fetch metadata index file 1048161748Scpercivafetch_metadata_index () { 1049161748Scperciva echo ${NDEBUG} "Fetching metadata index... " 1050161748Scperciva rm -f ${TINDEXHASH} 1051161748Scperciva fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH} 1052161748Scperciva 2>${QUIETREDIR} 1053161748Scperciva if ! [ -f ${TINDEXHASH} ]; then 1054161748Scperciva echo "failed." 1055161748Scperciva return 1 1056161748Scperciva fi 1057161748Scperciva if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then 1058161748Scperciva echo "update metadata index corrupt." 1059161748Scperciva return 1 1060161748Scperciva fi 1061161748Scperciva echo "done." 1062161748Scperciva} 1063161748Scperciva 1064161748Scperciva# Print an error message about signed metadata being bogus. 1065161748Scpercivafetch_metadata_bogus () { 1066161748Scperciva echo 1067161748Scperciva echo "The update metadata$1 is correctly signed, but" 1068161748Scperciva echo "failed an integrity check." 1069161748Scperciva echo "Cowardly refusing to proceed any further." 1070161748Scperciva return 1 1071161748Scperciva} 1072161748Scperciva 1073161748Scperciva# Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH} 1074161748Scperciva# with the lines not named in $@ from tINDEX.present (if that file exists). 1075161748Scpercivafetch_metadata_index_merge () { 1076161748Scperciva for METAFILE in $@; do 1077161748Scperciva if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l` \ 1078161748Scperciva -ne 1 ]; then 1079161748Scperciva fetch_metadata_bogus " index" 1080161748Scperciva return 1 1081161748Scperciva fi 1082161748Scperciva 1083161748Scperciva grep -E "${METAFILE}\|" ${TINDEXHASH} 1084161748Scperciva done | 1085161748Scperciva sort > tINDEX.wanted 1086161748Scperciva 1087161748Scperciva if [ -f tINDEX.present ]; then 1088161748Scperciva join -t '|' -v 2 tINDEX.wanted tINDEX.present | 1089161748Scperciva sort -m - tINDEX.wanted > tINDEX.new 1090161748Scperciva rm tINDEX.wanted 1091161748Scperciva else 1092161748Scperciva mv tINDEX.wanted tINDEX.new 1093161748Scperciva fi 1094161748Scperciva} 1095161748Scperciva 1096161748Scperciva# Sanity check all the lines of tINDEX.new. Even if more metadata lines 1097161748Scperciva# are added by future versions of the server, this won't cause problems, 1098161748Scperciva# since the only lines which appear in tINDEX.new are the ones which we 1099161748Scperciva# specifically grepped out of ${TINDEXHASH}. 1100161748Scpercivafetch_metadata_index_sanity () { 1101161748Scperciva if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then 1102161748Scperciva fetch_metadata_bogus " index" 1103161748Scperciva return 1 1104161748Scperciva fi 1105161748Scperciva} 1106161748Scperciva 1107161748Scperciva# Sanity check the metadata file $1. 1108161748Scpercivafetch_metadata_sanity () { 1109161748Scperciva # Some aliases to save space later: ${P} is a character which can 1110161748Scperciva # appear in a path; ${M} is the four numeric metadata fields; and 1111161748Scperciva # ${H} is a sha256 hash. 1112161748Scperciva P="[-+./:=_[[:alnum:]]" 1113161748Scperciva M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+" 1114161748Scperciva H="[0-9a-f]{64}" 1115161748Scperciva 1116161748Scperciva # Check that the first four fields make sense. 1117161748Scperciva if gunzip -c < files/$1.gz | 1118161748Scperciva grep -qvE "^[a-z]+\|[0-9a-z]+\|${P}+\|[fdL-]\|"; then 1119161748Scperciva fetch_metadata_bogus "" 1120161748Scperciva return 1 1121161748Scperciva fi 1122161748Scperciva 1123161748Scperciva # Remove the first three fields. 1124161748Scperciva gunzip -c < files/$1.gz | 1125161748Scperciva cut -f 4- -d '|' > sanitycheck.tmp 1126161748Scperciva 1127161748Scperciva # Sanity check entries with type 'f' 1128161748Scperciva if grep -E '^f' sanitycheck.tmp | 1129161748Scperciva grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then 1130161748Scperciva fetch_metadata_bogus "" 1131161748Scperciva return 1 1132161748Scperciva fi 1133161748Scperciva 1134161748Scperciva # Sanity check entries with type 'd' 1135161748Scperciva if grep -E '^d' sanitycheck.tmp | 1136161748Scperciva grep -qvE "^d\|${M}\|\|\$"; then 1137161748Scperciva fetch_metadata_bogus "" 1138161748Scperciva return 1 1139161748Scperciva fi 1140161748Scperciva 1141161748Scperciva # Sanity check entries with type 'L' 1142161748Scperciva if grep -E '^L' sanitycheck.tmp | 1143161748Scperciva grep -qvE "^L\|${M}\|${P}*\|\$"; then 1144161748Scperciva fetch_metadata_bogus "" 1145161748Scperciva return 1 1146161748Scperciva fi 1147161748Scperciva 1148161748Scperciva # Sanity check entries with type '-' 1149161748Scperciva if grep -E '^-' sanitycheck.tmp | 1150161748Scperciva grep -qvE "^-\|\|\|\|\|\|"; then 1151161748Scperciva fetch_metadata_bogus "" 1152161748Scperciva return 1 1153161748Scperciva fi 1154161748Scperciva 1155161748Scperciva # Clean up 1156161748Scperciva rm sanitycheck.tmp 1157161748Scperciva} 1158161748Scperciva 1159161748Scperciva# Fetch the metadata index and metadata files listed in $@, 1160161748Scperciva# taking advantage of metadata patches where possible. 1161161748Scpercivafetch_metadata () { 1162161748Scperciva fetch_metadata_index || return 1 1163161748Scperciva fetch_metadata_index_merge $@ || return 1 1164161748Scperciva fetch_metadata_index_sanity || return 1 1165161748Scperciva 1166161748Scperciva # Generate a list of wanted metadata patches 1167161748Scperciva join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new | 1168161748Scperciva fetch_make_patchlist > patchlist 1169161748Scperciva 1170161748Scperciva if [ -s patchlist ]; then 1171161748Scperciva # Attempt to fetch metadata patches 1172161748Scperciva echo -n "Fetching `wc -l < patchlist | tr -d ' '` " 1173161748Scperciva echo ${NDEBUG} "metadata patches.${DDSTATS}" 1174161748Scperciva tr '|' '-' < patchlist | 1175161748Scperciva lam -s "${FETCHDIR}/tp/" - -s ".gz" | 1176161748Scperciva xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 1177161748Scperciva 2>${STATSREDIR} | fetch_progress 1178161748Scperciva echo "done." 1179161748Scperciva 1180161748Scperciva # Attempt to apply metadata patches 1181161748Scperciva echo -n "Applying metadata patches... " 1182161748Scperciva tr '|' ' ' < patchlist | 1183161748Scperciva while read X Y; do 1184161748Scperciva if [ ! -f "${X}-${Y}.gz" ]; then continue; fi 1185161748Scperciva gunzip -c < ${X}-${Y}.gz > diff 1186161748Scperciva gunzip -c < files/${X}.gz > diff-OLD 1187161748Scperciva 1188161748Scperciva # Figure out which lines are being added and removed 1189161748Scperciva grep -E '^-' diff | 1190161748Scperciva cut -c 2- | 1191161748Scperciva while read PREFIX; do 1192161748Scperciva look "${PREFIX}" diff-OLD 1193161748Scperciva done | 1194161748Scperciva sort > diff-rm 1195161748Scperciva grep -E '^\+' diff | 1196161748Scperciva cut -c 2- > diff-add 1197161748Scperciva 1198161748Scperciva # Generate the new file 1199161748Scperciva comm -23 diff-OLD diff-rm | 1200161748Scperciva sort - diff-add > diff-NEW 1201161748Scperciva 1202161748Scperciva if [ `${SHA256} -q diff-NEW` = ${Y} ]; then 1203161748Scperciva mv diff-NEW files/${Y} 1204161748Scperciva gzip -n files/${Y} 1205161748Scperciva else 1206161748Scperciva mv diff-NEW ${Y}.bad 1207161748Scperciva fi 1208161748Scperciva rm -f ${X}-${Y}.gz diff 1209161748Scperciva rm -f diff-OLD diff-NEW diff-add diff-rm 1210161748Scperciva done 2>${QUIETREDIR} 1211161748Scperciva echo "done." 1212161748Scperciva fi 1213161748Scperciva 1214161748Scperciva # Update metadata without patches 1215161748Scperciva cut -f 2 -d '|' < tINDEX.new | 1216161748Scperciva while read Y; do 1217161748Scperciva if [ ! -f "files/${Y}.gz" ]; then 1218161748Scperciva echo ${Y}; 1219161748Scperciva fi 1220164600Scperciva done | 1221164600Scperciva sort -u > filelist 1222161748Scperciva 1223161748Scperciva if [ -s filelist ]; then 1224161748Scperciva echo -n "Fetching `wc -l < filelist | tr -d ' '` " 1225161748Scperciva echo ${NDEBUG} "metadata files... " 1226161748Scperciva lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist | 1227161748Scperciva xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 1228161748Scperciva 2>${QUIETREDIR} 1229161748Scperciva 1230161748Scperciva while read Y; do 1231161748Scperciva if ! [ -f ${Y}.gz ]; then 1232161748Scperciva echo "failed." 1233161748Scperciva return 1 1234161748Scperciva fi 1235161748Scperciva if [ `gunzip -c < ${Y}.gz | 1236161748Scperciva ${SHA256} -q` = ${Y} ]; then 1237161748Scperciva mv ${Y}.gz files/${Y}.gz 1238161748Scperciva else 1239161748Scperciva echo "metadata is corrupt." 1240161748Scperciva return 1 1241161748Scperciva fi 1242161748Scperciva done < filelist 1243161748Scperciva echo "done." 1244161748Scperciva fi 1245161748Scperciva 1246161748Scperciva# Sanity-check the metadata files. 1247161748Scperciva cut -f 2 -d '|' tINDEX.new > filelist 1248161748Scperciva while read X; do 1249161748Scperciva fetch_metadata_sanity ${X} || return 1 1250161748Scperciva done < filelist 1251161748Scperciva 1252161748Scperciva# Remove files which are no longer needed 1253161748Scperciva cut -f 2 -d '|' tINDEX.present | 1254161748Scperciva sort > oldfiles 1255161748Scperciva cut -f 2 -d '|' tINDEX.new | 1256161748Scperciva sort | 1257161748Scperciva comm -13 - oldfiles | 1258161748Scperciva lam -s "files/" - -s ".gz" | 1259161748Scperciva xargs rm -f 1260161748Scperciva rm patchlist filelist oldfiles 1261161748Scperciva rm ${TINDEXHASH} 1262161748Scperciva 1263161748Scperciva# We're done! 1264161748Scperciva mv tINDEX.new tINDEX.present 1265161748Scperciva mv tag.new tag 1266161748Scperciva 1267161748Scperciva return 0 1268161748Scperciva} 1269161748Scperciva 1270173564Scperciva# Extract a subset of a downloaded metadata file containing only the parts 1271173564Scperciva# which are listed in COMPONENTS. 1272173564Scpercivafetch_filter_metadata_components () { 1273173564Scperciva METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'` 1274173564Scperciva gunzip -c < files/${METAHASH}.gz > $1.all 1275173564Scperciva 1276173564Scperciva # Fish out the lines belonging to components we care about. 1277173564Scperciva for C in ${COMPONENTS}; do 1278173564Scperciva look "`echo ${C} | tr '/' '|'`|" $1.all 1279173564Scperciva done > $1 1280173564Scperciva 1281173564Scperciva # Remove temporary file. 1282173564Scperciva rm $1.all 1283173564Scperciva} 1284173564Scperciva 1285161869Scperciva# Generate a filtered version of the metadata file $1 from the downloaded 1286161748Scperciva# file, by fishing out the lines corresponding to components we're trying 1287161748Scperciva# to keep updated, and then removing lines corresponding to paths we want 1288161748Scperciva# to ignore. 1289161748Scpercivafetch_filter_metadata () { 1290173564Scperciva # Fish out the lines belonging to components we care about. 1291173564Scperciva fetch_filter_metadata_components $1 1292161748Scperciva 1293161748Scperciva # Canonicalize directory names by removing any trailing / in 1294161748Scperciva # order to avoid listing directories multiple times if they 1295161748Scperciva # belong to multiple components. Turning "/" into "" doesn't 1296161748Scperciva # matter, since we add a leading "/" when we use paths later. 1297173564Scperciva cut -f 3- -d '|' $1 | 1298161748Scperciva sed -e 's,/|d|,|d|,' | 1299161748Scperciva sort -u > $1.tmp 1300161748Scperciva 1301161748Scperciva # Figure out which lines to ignore and remove them. 1302161748Scperciva for X in ${IGNOREPATHS}; do 1303161748Scperciva grep -E "^${X}" $1.tmp 1304161748Scperciva done | 1305161748Scperciva sort -u | 1306161748Scperciva comm -13 - $1.tmp > $1 1307161748Scperciva 1308161748Scperciva # Remove temporary files. 1309173564Scperciva rm $1.tmp 1310161748Scperciva} 1311161748Scperciva 1312173564Scperciva# Filter the metadata file $1 by adding lines with "/boot/$2" 1313164600Scperciva# replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the 1314173564Scperciva# trailing "/kernel"); and if "/boot/$2" does not exist, remove 1315164600Scperciva# the original lines which start with that. 1316164600Scperciva# Put another way: Deal with the fact that the FOO kernel is sometimes 1317164600Scperciva# installed in /boot/FOO/ and is sometimes installed elsewhere. 1318161748Scpercivafetch_filter_kernel_names () { 1319173564Scperciva grep ^/boot/$2 $1 | 1320173564Scperciva sed -e "s,/boot/$2,${KERNELDIR},g" | 1321161748Scperciva sort - $1 > $1.tmp 1322161748Scperciva mv $1.tmp $1 1323164600Scperciva 1324173564Scperciva if ! [ -d /boot/$2 ]; then 1325173564Scperciva grep -v ^/boot/$2 $1 > $1.tmp 1326164600Scperciva mv $1.tmp $1 1327164600Scperciva fi 1328161748Scperciva} 1329161748Scperciva 1330161748Scperciva# For all paths appearing in $1 or $3, inspect the system 1331161748Scperciva# and generate $2 describing what is currently installed. 1332161748Scpercivafetch_inspect_system () { 1333161748Scperciva # No errors yet... 1334161748Scperciva rm -f .err 1335161748Scperciva 1336161748Scperciva # Tell the user why his disk is suddenly making lots of noise 1337161748Scperciva echo -n "Inspecting system... " 1338161748Scperciva 1339161748Scperciva # Generate list of files to inspect 1340161748Scperciva cat $1 $3 | 1341161748Scperciva cut -f 1 -d '|' | 1342161748Scperciva sort -u > filelist 1343161748Scperciva 1344161748Scperciva # Examine each file and output lines of the form 1345161748Scperciva # /path/to/file|type|device-inum|user|group|perm|flags|value 1346161748Scperciva # sorted by device and inode number. 1347161748Scperciva while read F; do 1348161748Scperciva # If the symlink/file/directory does not exist, record this. 1349161748Scperciva if ! [ -e ${BASEDIR}/${F} ]; then 1350161748Scperciva echo "${F}|-||||||" 1351161748Scperciva continue 1352161748Scperciva fi 1353161748Scperciva if ! [ -r ${BASEDIR}/${F} ]; then 1354161748Scperciva echo "Cannot read file: ${BASEDIR}/${F}" \ 1355161748Scperciva >/dev/stderr 1356161748Scperciva touch .err 1357161748Scperciva return 1 1358161748Scperciva fi 1359161748Scperciva 1360161748Scperciva # Otherwise, output an index line. 1361161748Scperciva if [ -L ${BASEDIR}/${F} ]; then 1362161748Scperciva echo -n "${F}|L|" 1363161748Scperciva stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; 1364161748Scperciva readlink ${BASEDIR}/${F}; 1365161748Scperciva elif [ -f ${BASEDIR}/${F} ]; then 1366161748Scperciva echo -n "${F}|f|" 1367161748Scperciva stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; 1368161748Scperciva sha256 -q ${BASEDIR}/${F}; 1369161748Scperciva elif [ -d ${BASEDIR}/${F} ]; then 1370161748Scperciva echo -n "${F}|d|" 1371161748Scperciva stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; 1372161748Scperciva else 1373161748Scperciva echo "Unknown file type: ${BASEDIR}/${F}" \ 1374161748Scperciva >/dev/stderr 1375161748Scperciva touch .err 1376161748Scperciva return 1 1377161748Scperciva fi 1378161748Scperciva done < filelist | 1379161748Scperciva sort -k 3,3 -t '|' > $2.tmp 1380161748Scperciva rm filelist 1381161748Scperciva 1382161748Scperciva # Check if an error occured during system inspection 1383161748Scperciva if [ -f .err ]; then 1384161748Scperciva return 1 1385161748Scperciva fi 1386161748Scperciva 1387161748Scperciva # Convert to the form 1388161748Scperciva # /path/to/file|type|user|group|perm|flags|value|hlink 1389161748Scperciva # by resolving identical device and inode numbers into hard links. 1390161748Scperciva cut -f 1,3 -d '|' $2.tmp | 1391161748Scperciva sort -k 1,1 -t '|' | 1392161748Scperciva sort -s -u -k 2,2 -t '|' | 1393161748Scperciva join -1 2 -2 3 -t '|' - $2.tmp | 1394161748Scperciva awk -F \| -v OFS=\| \ 1395161748Scperciva '{ 1396161748Scperciva if (($2 == $3) || ($4 == "-")) 1397161748Scperciva print $3,$4,$5,$6,$7,$8,$9,"" 1398161748Scperciva else 1399161748Scperciva print $3,$4,$5,$6,$7,$8,$9,$2 1400161748Scperciva }' | 1401161748Scperciva sort > $2 1402161748Scperciva rm $2.tmp 1403161748Scperciva 1404161748Scperciva # We're finished looking around 1405161748Scperciva echo "done." 1406161748Scperciva} 1407161748Scperciva 1408173564Scperciva# For any paths matching ${MERGECHANGES}, compare $1 and $2 and find any 1409173564Scperciva# files which differ; generate $3 containing these paths and the old hashes. 1410173564Scpercivafetch_filter_mergechanges () { 1411173564Scperciva # Pull out the paths and hashes of the files matching ${MERGECHANGES}. 1412173564Scperciva for F in $1 $2; do 1413173564Scperciva for X in ${MERGECHANGES}; do 1414173564Scperciva grep -E "^${X}" ${F} 1415173564Scperciva done | 1416173564Scperciva cut -f 1,2,7 -d '|' | 1417173564Scperciva sort > ${F}-values 1418173564Scperciva done 1419173564Scperciva 1420173564Scperciva # Any line in $2-values which doesn't appear in $1-values and is a 1421173564Scperciva # file means that we should list the path in $3. 1422173564Scperciva comm -13 $1-values $2-values | 1423173564Scperciva fgrep '|f|' | 1424173564Scperciva cut -f 1 -d '|' > $2-paths 1425173564Scperciva 1426173564Scperciva # For each path, pull out one (and only one!) entry from $1-values. 1427173564Scperciva # Note that we cannot distinguish which "old" version the user made 1428173564Scperciva # changes to; but hopefully any changes which occur due to security 1429173564Scperciva # updates will exist in both the "new" version and the version which 1430173564Scperciva # the user has installed, so the merging will still work. 1431173564Scperciva while read X; do 1432173564Scperciva look "${X}|" $1-values | 1433173564Scperciva head -1 1434173564Scperciva done < $2-paths > $3 1435173564Scperciva 1436173564Scperciva # Clean up 1437173564Scperciva rm $1-values $2-values $2-paths 1438173564Scperciva} 1439173564Scperciva 1440161748Scperciva# For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123] 1441173564Scperciva# which correspond to lines in $2 with hashes not matching $1 or $3, unless 1442173564Scperciva# the paths are listed in $4. For entries in $2 marked "not present" 1443173564Scperciva# (aka. type -), remove lines from $[123] unless there is a corresponding 1444173564Scperciva# entry in $1. 1445161748Scpercivafetch_filter_unmodified_notpresent () { 1446161748Scperciva # Figure out which lines of $1 and $3 correspond to bits which 1447161748Scperciva # should only be updated if they haven't changed, and fish out 1448161748Scperciva # the (path, type, value) tuples. 1449161748Scperciva # NOTE: We don't consider a file to be "modified" if it matches 1450161748Scperciva # the hash from $3. 1451161748Scperciva for X in ${UPDATEIFUNMODIFIED}; do 1452161748Scperciva grep -E "^${X}" $1 1453161748Scperciva grep -E "^${X}" $3 1454161748Scperciva done | 1455161748Scperciva cut -f 1,2,7 -d '|' | 1456161748Scperciva sort > $1-values 1457161748Scperciva 1458161748Scperciva # Do the same for $2. 1459161748Scperciva for X in ${UPDATEIFUNMODIFIED}; do 1460161748Scperciva grep -E "^${X}" $2 1461161748Scperciva done | 1462161748Scperciva cut -f 1,2,7 -d '|' | 1463161748Scperciva sort > $2-values 1464161748Scperciva 1465161748Scperciva # Any entry in $2-values which is not in $1-values corresponds to 1466173564Scperciva # a path which we need to remove from $1, $2, and $3, unless it 1467173564Scperciva # that path appears in $4. 1468173564Scperciva comm -13 $1-values $2-values | 1469173564Scperciva sort -t '|' -k 1,1 > mlines.tmp 1470173564Scperciva cut -f 1 -d '|' $4 | 1471173564Scperciva sort | 1472173564Scperciva join -v 2 -t '|' - mlines.tmp | 1473173564Scperciva sort > mlines 1474173564Scperciva rm $1-values $2-values mlines.tmp 1475161748Scperciva 1476161748Scperciva # Any lines in $2 which are not in $1 AND are "not present" lines 1477161748Scperciva # also belong in mlines. 1478161748Scperciva comm -13 $1 $2 | 1479161748Scperciva cut -f 1,2,7 -d '|' | 1480161748Scperciva fgrep '|-|' >> mlines 1481161748Scperciva 1482161748Scperciva # Remove lines from $1, $2, and $3 1483161748Scperciva for X in $1 $2 $3; do 1484161748Scperciva sort -t '|' -k 1,1 ${X} > ${X}.tmp 1485161748Scperciva cut -f 1 -d '|' < mlines | 1486161748Scperciva sort | 1487161748Scperciva join -v 2 -t '|' - ${X}.tmp | 1488161748Scperciva sort > ${X} 1489161748Scperciva rm ${X}.tmp 1490161748Scperciva done 1491161748Scperciva 1492161748Scperciva # Store a list of the modified files, for future reference 1493161748Scperciva fgrep -v '|-|' mlines | 1494161748Scperciva cut -f 1 -d '|' > modifiedfiles 1495161748Scperciva rm mlines 1496161748Scperciva} 1497161748Scperciva 1498161748Scperciva# For each entry in $1 of type -, remove any corresponding 1499161748Scperciva# entry from $2 if ${ALLOWADD} != "yes". Remove all entries 1500161748Scperciva# of type - from $1. 1501161748Scpercivafetch_filter_allowadd () { 1502161748Scperciva cut -f 1,2 -d '|' < $1 | 1503161748Scperciva fgrep '|-' | 1504161748Scperciva cut -f 1 -d '|' > filesnotpresent 1505161748Scperciva 1506161748Scperciva if [ ${ALLOWADD} != "yes" ]; then 1507161748Scperciva sort < $2 | 1508161748Scperciva join -v 1 -t '|' - filesnotpresent | 1509161748Scperciva sort > $2.tmp 1510161748Scperciva mv $2.tmp $2 1511161748Scperciva fi 1512161748Scperciva 1513161748Scperciva sort < $1 | 1514161748Scperciva join -v 1 -t '|' - filesnotpresent | 1515161748Scperciva sort > $1.tmp 1516161748Scperciva mv $1.tmp $1 1517161748Scperciva rm filesnotpresent 1518161748Scperciva} 1519161748Scperciva 1520161748Scperciva# If ${ALLOWDELETE} != "yes", then remove any entries from $1 1521161748Scperciva# which don't correspond to entries in $2. 1522161748Scpercivafetch_filter_allowdelete () { 1523161748Scperciva # Produce a lists ${PATH}|${TYPE} 1524161748Scperciva for X in $1 $2; do 1525161748Scperciva cut -f 1-2 -d '|' < ${X} | 1526161748Scperciva sort -u > ${X}.nodes 1527161748Scperciva done 1528161748Scperciva 1529161748Scperciva # Figure out which lines need to be removed from $1. 1530161748Scperciva if [ ${ALLOWDELETE} != "yes" ]; then 1531161748Scperciva comm -23 $1.nodes $2.nodes > $1.badnodes 1532161748Scperciva else 1533161748Scperciva : > $1.badnodes 1534161748Scperciva fi 1535161748Scperciva 1536161748Scperciva # Remove the relevant lines from $1 1537161748Scperciva while read X; do 1538161748Scperciva look "${X}|" $1 1539161748Scperciva done < $1.badnodes | 1540161748Scperciva comm -13 - $1 > $1.tmp 1541161748Scperciva mv $1.tmp $1 1542161748Scperciva 1543161748Scperciva rm $1.badnodes $1.nodes $2.nodes 1544161748Scperciva} 1545161748Scperciva 1546161748Scperciva# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2 1547161748Scperciva# with metadata not matching any entry in $1, replace the corresponding 1548161748Scperciva# line of $3 with one having the same metadata as the entry in $2. 1549161748Scpercivafetch_filter_modified_metadata () { 1550161748Scperciva # Fish out the metadata from $1 and $2 1551161748Scperciva for X in $1 $2; do 1552161748Scperciva cut -f 1-6 -d '|' < ${X} > ${X}.metadata 1553161748Scperciva done 1554161748Scperciva 1555161748Scperciva # Find the metadata we need to keep 1556161748Scperciva if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then 1557161748Scperciva comm -13 $1.metadata $2.metadata > keepmeta 1558161748Scperciva else 1559161748Scperciva : > keepmeta 1560161748Scperciva fi 1561161748Scperciva 1562161748Scperciva # Extract the lines which we need to remove from $3, and 1563161748Scperciva # construct the lines which we need to add to $3. 1564161748Scperciva : > $3.remove 1565161748Scperciva : > $3.add 1566161748Scperciva while read LINE; do 1567161748Scperciva NODE=`echo "${LINE}" | cut -f 1-2 -d '|'` 1568161748Scperciva look "${NODE}|" $3 >> $3.remove 1569161748Scperciva look "${NODE}|" $3 | 1570161748Scperciva cut -f 7- -d '|' | 1571161748Scperciva lam -s "${LINE}|" - >> $3.add 1572161748Scperciva done < keepmeta 1573161748Scperciva 1574161748Scperciva # Remove the specified lines and add the new lines. 1575161748Scperciva sort $3.remove | 1576161748Scperciva comm -13 - $3 | 1577161748Scperciva sort -u - $3.add > $3.tmp 1578161748Scperciva mv $3.tmp $3 1579161748Scperciva 1580161748Scperciva rm keepmeta $1.metadata $2.metadata $3.add $3.remove 1581161748Scperciva} 1582161748Scperciva 1583161748Scperciva# Remove lines from $1 and $2 which are identical; 1584161748Scperciva# no need to update a file if it isn't changing. 1585161748Scpercivafetch_filter_uptodate () { 1586161748Scperciva comm -23 $1 $2 > $1.tmp 1587161748Scperciva comm -13 $1 $2 > $2.tmp 1588161748Scperciva 1589161748Scperciva mv $1.tmp $1 1590161748Scperciva mv $2.tmp $2 1591161748Scperciva} 1592161748Scperciva 1593173564Scperciva# Fetch any "clean" old versions of files we need for merging changes. 1594173564Scpercivafetch_files_premerge () { 1595173564Scperciva # We only need to do anything if $1 is non-empty. 1596173564Scperciva if [ -s $1 ]; then 1597173564Scperciva # Tell the user what we're doing 1598173564Scperciva echo -n "Fetching files from ${OLDRELNUM} for merging... " 1599173564Scperciva 1600173564Scperciva # List of files wanted 1601173564Scperciva fgrep '|f|' < $1 | 1602173564Scperciva cut -f 3 -d '|' | 1603173564Scperciva sort -u > files.wanted 1604173564Scperciva 1605173564Scperciva # Only fetch the files we don't already have 1606173564Scperciva while read Y; do 1607173564Scperciva if [ ! -f "files/${Y}.gz" ]; then 1608173564Scperciva echo ${Y}; 1609173564Scperciva fi 1610173564Scperciva done < files.wanted > filelist 1611173564Scperciva 1612173564Scperciva # Actually fetch them 1613173564Scperciva lam -s "${OLDFETCHDIR}/f/" - -s ".gz" < filelist | 1614173564Scperciva xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 1615173564Scperciva 2>${QUIETREDIR} 1616173564Scperciva 1617173564Scperciva # Make sure we got them all, and move them into /files/ 1618173564Scperciva while read Y; do 1619173564Scperciva if ! [ -f ${Y}.gz ]; then 1620173564Scperciva echo "failed." 1621173564Scperciva return 1 1622173564Scperciva fi 1623173564Scperciva if [ `gunzip -c < ${Y}.gz | 1624173564Scperciva ${SHA256} -q` = ${Y} ]; then 1625173564Scperciva mv ${Y}.gz files/${Y}.gz 1626173564Scperciva else 1627173564Scperciva echo "${Y} has incorrect hash." 1628173564Scperciva return 1 1629173564Scperciva fi 1630173564Scperciva done < filelist 1631173564Scperciva echo "done." 1632173564Scperciva 1633173564Scperciva # Clean up 1634173564Scperciva rm filelist files.wanted 1635173564Scperciva fi 1636173564Scperciva} 1637173564Scperciva 1638161748Scperciva# Prepare to fetch files: Generate a list of the files we need, 1639161748Scperciva# copy the unmodified files we have into /files/, and generate 1640161748Scperciva# a list of patches to download. 1641161748Scpercivafetch_files_prepare () { 1642161748Scperciva # Tell the user why his disk is suddenly making lots of noise 1643161748Scperciva echo -n "Preparing to download files... " 1644161748Scperciva 1645161748Scperciva # Reduce indices to ${PATH}|${HASH} pairs 1646161748Scperciva for X in $1 $2 $3; do 1647161748Scperciva cut -f 1,2,7 -d '|' < ${X} | 1648161748Scperciva fgrep '|f|' | 1649161748Scperciva cut -f 1,3 -d '|' | 1650161748Scperciva sort > ${X}.hashes 1651161748Scperciva done 1652161748Scperciva 1653161748Scperciva # List of files wanted 1654161748Scperciva cut -f 2 -d '|' < $3.hashes | 1655173441Scperciva sort -u | 1656173441Scperciva while read HASH; do 1657173441Scperciva if ! [ -f files/${HASH}.gz ]; then 1658173441Scperciva echo ${HASH} 1659173441Scperciva fi 1660173441Scperciva done > files.wanted 1661161748Scperciva 1662161748Scperciva # Generate a list of unmodified files 1663161748Scperciva comm -12 $1.hashes $2.hashes | 1664161748Scperciva sort -k 1,1 -t '|' > unmodified.files 1665161748Scperciva 1666161748Scperciva # Copy all files into /files/. We only need the unmodified files 1667161748Scperciva # for use in patching; but we'll want all of them if the user asks 1668161748Scperciva # to rollback the updates later. 1669171784Scperciva while read LINE; do 1670171784Scperciva F=`echo "${LINE}" | cut -f 1 -d '|'` 1671171784Scperciva HASH=`echo "${LINE}" | cut -f 2 -d '|'` 1672171784Scperciva 1673171784Scperciva # Skip files we already have. 1674171784Scperciva if [ -f files/${HASH}.gz ]; then 1675171784Scperciva continue 1676171784Scperciva fi 1677171784Scperciva 1678171784Scperciva # Make sure the file hasn't changed. 1679161748Scperciva cp "${BASEDIR}/${F}" tmpfile 1680171784Scperciva if [ `sha256 -q tmpfile` != ${HASH} ]; then 1681171784Scperciva echo 1682171784Scperciva echo "File changed while FreeBSD Update running: ${F}" 1683171784Scperciva return 1 1684171784Scperciva fi 1685171784Scperciva 1686171784Scperciva # Place the file into storage. 1687171784Scperciva gzip -c < tmpfile > files/${HASH}.gz 1688161748Scperciva rm tmpfile 1689171784Scperciva done < $2.hashes 1690161748Scperciva 1691161748Scperciva # Produce a list of patches to download 1692161748Scperciva sort -k 1,1 -t '|' $3.hashes | 1693161748Scperciva join -t '|' -o 2.2,1.2 - unmodified.files | 1694161748Scperciva fetch_make_patchlist > patchlist 1695161748Scperciva 1696161748Scperciva # Garbage collect 1697161748Scperciva rm unmodified.files $1.hashes $2.hashes $3.hashes 1698161748Scperciva 1699161748Scperciva # We don't need the list of possible old files any more. 1700161748Scperciva rm $1 1701161748Scperciva 1702161748Scperciva # We're finished making noise 1703161748Scperciva echo "done." 1704161748Scperciva} 1705161748Scperciva 1706161748Scperciva# Fetch files. 1707161748Scpercivafetch_files () { 1708161748Scperciva # Attempt to fetch patches 1709161748Scperciva if [ -s patchlist ]; then 1710161748Scperciva echo -n "Fetching `wc -l < patchlist | tr -d ' '` " 1711161748Scperciva echo ${NDEBUG} "patches.${DDSTATS}" 1712161748Scperciva tr '|' '-' < patchlist | 1713173564Scperciva lam -s "${PATCHDIR}/" - | 1714161748Scperciva xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 1715161748Scperciva 2>${STATSREDIR} | fetch_progress 1716161748Scperciva echo "done." 1717161748Scperciva 1718161748Scperciva # Attempt to apply patches 1719161748Scperciva echo -n "Applying patches... " 1720161748Scperciva tr '|' ' ' < patchlist | 1721161748Scperciva while read X Y; do 1722161748Scperciva if [ ! -f "${X}-${Y}" ]; then continue; fi 1723161748Scperciva gunzip -c < files/${X}.gz > OLD 1724161748Scperciva 1725161748Scperciva bspatch OLD NEW ${X}-${Y} 1726161748Scperciva 1727161748Scperciva if [ `${SHA256} -q NEW` = ${Y} ]; then 1728161748Scperciva mv NEW files/${Y} 1729161748Scperciva gzip -n files/${Y} 1730161748Scperciva fi 1731161748Scperciva rm -f diff OLD NEW ${X}-${Y} 1732161748Scperciva done 2>${QUIETREDIR} 1733161748Scperciva echo "done." 1734161748Scperciva fi 1735161748Scperciva 1736161748Scperciva # Download files which couldn't be generate via patching 1737161748Scperciva while read Y; do 1738161748Scperciva if [ ! -f "files/${Y}.gz" ]; then 1739161748Scperciva echo ${Y}; 1740161748Scperciva fi 1741161748Scperciva done < files.wanted > filelist 1742161748Scperciva 1743161748Scperciva if [ -s filelist ]; then 1744161748Scperciva echo -n "Fetching `wc -l < filelist | tr -d ' '` " 1745161748Scperciva echo ${NDEBUG} "files... " 1746161748Scperciva lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist | 1747161748Scperciva xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 1748161748Scperciva 2>${QUIETREDIR} 1749161748Scperciva 1750161748Scperciva while read Y; do 1751161748Scperciva if ! [ -f ${Y}.gz ]; then 1752161748Scperciva echo "failed." 1753161748Scperciva return 1 1754161748Scperciva fi 1755161748Scperciva if [ `gunzip -c < ${Y}.gz | 1756161748Scperciva ${SHA256} -q` = ${Y} ]; then 1757161748Scperciva mv ${Y}.gz files/${Y}.gz 1758161748Scperciva else 1759161748Scperciva echo "${Y} has incorrect hash." 1760161748Scperciva return 1 1761161748Scperciva fi 1762161748Scperciva done < filelist 1763161748Scperciva echo "done." 1764161748Scperciva fi 1765161748Scperciva 1766161748Scperciva # Clean up 1767161748Scperciva rm files.wanted filelist patchlist 1768161748Scperciva} 1769161748Scperciva 1770161748Scperciva# Create and populate install manifest directory; and report what updates 1771161748Scperciva# are available. 1772161748Scpercivafetch_create_manifest () { 1773161748Scperciva # If we have an existing install manifest, nuke it. 1774161748Scperciva if [ -L "${BDHASH}-install" ]; then 1775161748Scperciva rm -r ${BDHASH}-install/ 1776161748Scperciva rm ${BDHASH}-install 1777161748Scperciva fi 1778161748Scperciva 1779161748Scperciva # Report to the user if any updates were avoided due to local changes 1780161748Scperciva if [ -s modifiedfiles ]; then 1781161748Scperciva echo 1782161748Scperciva echo -n "The following files are affected by updates, " 1783161748Scperciva echo "but no changes have" 1784161748Scperciva echo -n "been downloaded because the files have been " 1785161748Scperciva echo "modified locally:" 1786161748Scperciva cat modifiedfiles 1787173564Scperciva fi | more 1788161748Scperciva rm modifiedfiles 1789161748Scperciva 1790161748Scperciva # If no files will be updated, tell the user and exit 1791161748Scperciva if ! [ -s INDEX-PRESENT ] && 1792161748Scperciva ! [ -s INDEX-NEW ]; then 1793161748Scperciva rm INDEX-PRESENT INDEX-NEW 1794161748Scperciva echo 1795161748Scperciva echo -n "No updates needed to update system to " 1796161748Scperciva echo "${RELNUM}-p${RELPATCHNUM}." 1797161748Scperciva return 1798161748Scperciva fi 1799161748Scperciva 1800161748Scperciva # Divide files into (a) removed files, (b) added files, and 1801161748Scperciva # (c) updated files. 1802161748Scperciva cut -f 1 -d '|' < INDEX-PRESENT | 1803161748Scperciva sort > INDEX-PRESENT.flist 1804161748Scperciva cut -f 1 -d '|' < INDEX-NEW | 1805161748Scperciva sort > INDEX-NEW.flist 1806161748Scperciva comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed 1807161748Scperciva comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added 1808161748Scperciva comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated 1809161748Scperciva rm INDEX-PRESENT.flist INDEX-NEW.flist 1810161748Scperciva 1811161748Scperciva # Report removed files, if any 1812161748Scperciva if [ -s files.removed ]; then 1813161748Scperciva echo 1814161748Scperciva echo -n "The following files will be removed " 1815161748Scperciva echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:" 1816161748Scperciva cat files.removed 1817173564Scperciva fi | more 1818161748Scperciva rm files.removed 1819161748Scperciva 1820161748Scperciva # Report added files, if any 1821161748Scperciva if [ -s files.added ]; then 1822161748Scperciva echo 1823161748Scperciva echo -n "The following files will be added " 1824161748Scperciva echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:" 1825161748Scperciva cat files.added 1826173564Scperciva fi | more 1827161748Scperciva rm files.added 1828161748Scperciva 1829161748Scperciva # Report updated files, if any 1830161748Scperciva if [ -s files.updated ]; then 1831161748Scperciva echo 1832161748Scperciva echo -n "The following files will be updated " 1833161748Scperciva echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:" 1834161748Scperciva 1835161748Scperciva cat files.updated 1836173564Scperciva fi | more 1837161748Scperciva rm files.updated 1838161748Scperciva 1839161748Scperciva # Create a directory for the install manifest. 1840161748Scperciva MDIR=`mktemp -d install.XXXXXX` || return 1 1841161748Scperciva 1842161748Scperciva # Populate it 1843161748Scperciva mv INDEX-PRESENT ${MDIR}/INDEX-OLD 1844161748Scperciva mv INDEX-NEW ${MDIR}/INDEX-NEW 1845161748Scperciva 1846161748Scperciva # Link it into place 1847161748Scperciva ln -s ${MDIR} ${BDHASH}-install 1848161748Scperciva} 1849161748Scperciva 1850161748Scperciva# Warn about any upcoming EoL 1851161748Scpercivafetch_warn_eol () { 1852161748Scperciva # What's the current time? 1853161748Scperciva NOWTIME=`date "+%s"` 1854161748Scperciva 1855161748Scperciva # When did we last warn about the EoL date? 1856161748Scperciva if [ -f lasteolwarn ]; then 1857161748Scperciva LASTWARN=`cat lasteolwarn` 1858161748Scperciva else 1859161748Scperciva LASTWARN=`expr ${NOWTIME} - 63072000` 1860161748Scperciva fi 1861161748Scperciva 1862161748Scperciva # If the EoL time is past, warn. 1863161748Scperciva if [ ${EOLTIME} -lt ${NOWTIME} ]; then 1864161748Scperciva echo 1865161748Scperciva cat <<-EOF 1866161869Scperciva WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE. 1867161748Scperciva Any security issues discovered after `date -r ${EOLTIME}` 1868161748Scperciva will not have been corrected. 1869161748Scperciva EOF 1870161748Scperciva return 1 1871161748Scperciva fi 1872161748Scperciva 1873161748Scperciva # Figure out how long it has been since we last warned about the 1874161748Scperciva # upcoming EoL, and how much longer we have left. 1875161748Scperciva SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}` 1876161748Scperciva TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}` 1877161748Scperciva 1878171838Scperciva # Don't warn if the EoL is more than 3 months away 1879171838Scperciva if [ ${TIMELEFT} -gt 7884000 ]; then 1880161748Scperciva return 0 1881161748Scperciva fi 1882161748Scperciva 1883161748Scperciva # Don't warn if the time remaining is more than 3 times the time 1884161748Scperciva # since the last warning. 1885161748Scperciva if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then 1886161748Scperciva return 0 1887161748Scperciva fi 1888161748Scperciva 1889161748Scperciva # Figure out what time units to use. 1890161748Scperciva if [ ${TIMELEFT} -lt 604800 ]; then 1891161748Scperciva UNIT="day" 1892161748Scperciva SIZE=86400 1893161748Scperciva elif [ ${TIMELEFT} -lt 2678400 ]; then 1894161748Scperciva UNIT="week" 1895161748Scperciva SIZE=604800 1896161748Scperciva else 1897161748Scperciva UNIT="month" 1898161748Scperciva SIZE=2678400 1899161748Scperciva fi 1900161748Scperciva 1901161748Scperciva # Compute the right number of units 1902161748Scperciva NUM=`expr ${TIMELEFT} / ${SIZE}` 1903161748Scperciva if [ ${NUM} != 1 ]; then 1904161748Scperciva UNIT="${UNIT}s" 1905161748Scperciva fi 1906161748Scperciva 1907161748Scperciva # Print the warning 1908161748Scperciva echo 1909161748Scperciva cat <<-EOF 1910161748Scperciva WARNING: `uname -sr` is approaching its End-of-Life date. 1911161748Scperciva It is strongly recommended that you upgrade to a newer 1912161748Scperciva release within the next ${NUM} ${UNIT}. 1913161748Scperciva EOF 1914161748Scperciva 1915161748Scperciva # Update the stored time of last warning 1916161748Scperciva echo ${NOWTIME} > lasteolwarn 1917161748Scperciva} 1918161748Scperciva 1919161748Scperciva# Do the actual work involved in "fetch" / "cron". 1920161748Scpercivafetch_run () { 1921161748Scperciva workdir_init || return 1 1922161748Scperciva 1923161748Scperciva # Prepare the mirror list. 1924161748Scperciva fetch_pick_server_init && fetch_pick_server 1925161748Scperciva 1926161748Scperciva # Try to fetch the public key until we run out of servers. 1927161748Scperciva while ! fetch_key; do 1928161748Scperciva fetch_pick_server || return 1 1929161748Scperciva done 1930161748Scperciva 1931161748Scperciva # Try to fetch the metadata index signature ("tag") until we run 1932161748Scperciva # out of available servers; and sanity check the downloaded tag. 1933161748Scperciva while ! fetch_tag; do 1934161748Scperciva fetch_pick_server || return 1 1935161748Scperciva done 1936161748Scperciva fetch_tagsanity || return 1 1937161748Scperciva 1938161748Scperciva # Fetch the latest INDEX-NEW and INDEX-OLD files. 1939161748Scperciva fetch_metadata INDEX-NEW INDEX-OLD || return 1 1940161748Scperciva 1941161748Scperciva # Generate filtered INDEX-NEW and INDEX-OLD files containing only 1942161748Scperciva # the lines which (a) belong to components we care about, and (b) 1943161748Scperciva # don't correspond to paths we're explicitly ignoring. 1944161748Scperciva fetch_filter_metadata INDEX-NEW || return 1 1945161748Scperciva fetch_filter_metadata INDEX-OLD || return 1 1946161748Scperciva 1947173564Scperciva # Translate /boot/${KERNCONF} into ${KERNELDIR} 1948173564Scperciva fetch_filter_kernel_names INDEX-NEW ${KERNCONF} 1949173564Scperciva fetch_filter_kernel_names INDEX-OLD ${KERNCONF} 1950161748Scperciva 1951161748Scperciva # For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the 1952161748Scperciva # system and generate an INDEX-PRESENT file. 1953161748Scperciva fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 1954161748Scperciva 1955161748Scperciva # Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which 1956161748Scperciva # correspond to lines in INDEX-PRESENT with hashes not appearing 1957161748Scperciva # in INDEX-OLD or INDEX-NEW. Also remove lines where the entry in 1958161748Scperciva # INDEX-PRESENT has type - and there isn't a corresponding entry in 1959161748Scperciva # INDEX-OLD with type -. 1960173564Scperciva fetch_filter_unmodified_notpresent \ 1961173564Scperciva INDEX-OLD INDEX-PRESENT INDEX-NEW /dev/null 1962161748Scperciva 1963161748Scperciva # For each entry in INDEX-PRESENT of type -, remove any corresponding 1964161748Scperciva # entry from INDEX-NEW if ${ALLOWADD} != "yes". Remove all entries 1965161748Scperciva # of type - from INDEX-PRESENT. 1966161748Scperciva fetch_filter_allowadd INDEX-PRESENT INDEX-NEW 1967161748Scperciva 1968161748Scperciva # If ${ALLOWDELETE} != "yes", then remove any entries from 1969161748Scperciva # INDEX-PRESENT which don't correspond to entries in INDEX-NEW. 1970161748Scperciva fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW 1971161748Scperciva 1972161748Scperciva # If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in 1973161748Scperciva # INDEX-PRESENT with metadata not matching any entry in INDEX-OLD, 1974161748Scperciva # replace the corresponding line of INDEX-NEW with one having the 1975161748Scperciva # same metadata as the entry in INDEX-PRESENT. 1976161748Scperciva fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW 1977161748Scperciva 1978161748Scperciva # Remove lines from INDEX-PRESENT and INDEX-NEW which are identical; 1979161748Scperciva # no need to update a file if it isn't changing. 1980161748Scperciva fetch_filter_uptodate INDEX-PRESENT INDEX-NEW 1981161748Scperciva 1982161748Scperciva # Prepare to fetch files: Generate a list of the files we need, 1983161748Scperciva # copy the unmodified files we have into /files/, and generate 1984161748Scperciva # a list of patches to download. 1985171784Scperciva fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 1986161748Scperciva 1987161748Scperciva # Fetch files. 1988161748Scperciva fetch_files || return 1 1989161748Scperciva 1990161748Scperciva # Create and populate install manifest directory; and report what 1991161748Scperciva # updates are available. 1992161748Scperciva fetch_create_manifest || return 1 1993161748Scperciva 1994161748Scperciva # Warn about any upcoming EoL 1995161748Scperciva fetch_warn_eol || return 1 1996161748Scperciva} 1997161748Scperciva 1998173564Scperciva# If StrictComponents is not "yes", generate a new components list 1999173564Scperciva# with only the components which appear to be installed. 2000173564Scpercivaupgrade_guess_components () { 2001173564Scperciva if [ "${STRICTCOMPONENTS}" = "no" ]; then 2002173564Scperciva # Generate filtered INDEX-ALL with only the components listed 2003173564Scperciva # in COMPONENTS. 2004173564Scperciva fetch_filter_metadata_components $1 || return 1 2005173564Scperciva 2006173564Scperciva # Tell the user why his disk is suddenly making lots of noise 2007173564Scperciva echo -n "Inspecting system... " 2008173564Scperciva 2009173564Scperciva # Look at the files on disk, and assume that a component is 2010173564Scperciva # supposed to be present if it is more than half-present. 2011173564Scperciva cut -f 1-3 -d '|' < INDEX-ALL | 2012173564Scperciva tr '|' ' ' | 2013173564Scperciva while read C S F; do 2014173564Scperciva if [ -e ${BASEDIR}/${F} ]; then 2015173564Scperciva echo "+ ${C}|${S}" 2016173564Scperciva fi 2017173564Scperciva echo "= ${C}|${S}" 2018173564Scperciva done | 2019173564Scperciva sort | 2020173564Scperciva uniq -c | 2021173564Scperciva sed -E 's,^ +,,' > compfreq 2022173564Scperciva grep ' = ' compfreq | 2023173564Scperciva cut -f 1,3 -d ' ' | 2024173564Scperciva sort -k 2,2 -t ' ' > compfreq.total 2025173564Scperciva grep ' + ' compfreq | 2026173564Scperciva cut -f 1,3 -d ' ' | 2027173564Scperciva sort -k 2,2 -t ' ' > compfreq.present 2028173564Scperciva join -t ' ' -1 2 -2 2 compfreq.present compfreq.total | 2029173564Scperciva while read S P T; do 2030173564Scperciva if [ ${P} -gt `expr ${T} / 2` ]; then 2031173564Scperciva echo ${S} 2032173564Scperciva fi 2033173564Scperciva done > comp.present 2034173564Scperciva cut -f 2 -d ' ' < compfreq.total > comp.total 2035173564Scperciva rm INDEX-ALL compfreq compfreq.total compfreq.present 2036173564Scperciva 2037173564Scperciva # We're done making noise. 2038173564Scperciva echo "done." 2039173564Scperciva 2040173564Scperciva # Sometimes the kernel isn't installed where INDEX-ALL 2041173564Scperciva # thinks that it should be: In particular, it is often in 2042173564Scperciva # /boot/kernel instead of /boot/GENERIC or /boot/SMP. To 2043173564Scperciva # deal with this, if "kernel|X" is listed in comp.total 2044173564Scperciva # (i.e., is a component which would be upgraded if it is 2045173564Scperciva # found to be present) we will add it to comp.present. 2046173564Scperciva # If "kernel|<anything>" is in comp.total but "kernel|X" is 2047173564Scperciva # not, we print a warning -- the user is running a kernel 2048173564Scperciva # which isn't part of the release. 2049173564Scperciva KCOMP=`echo ${KERNCONF} | tr 'A-Z' 'a-z'` 2050173564Scperciva grep -E "^kernel\|${KCOMP}\$" comp.total >> comp.present 2051173564Scperciva 2052173564Scperciva if grep -qE "^kernel\|" comp.total && 2053173564Scperciva ! grep -qE "^kernel\|${KCOMP}\$" comp.total; then 2054173564Scperciva cat <<-EOF 2055173564Scperciva 2056173564ScpercivaWARNING: This system is running a "${KCOMP}" kernel, which is not a 2057173564Scpercivakernel configuration distributed as part of FreeBSD ${RELNUM}. 2058173564ScpercivaThis kernel will not be updated: you MUST update the kernel manually 2059173564Scpercivabefore running "$0 install". 2060173564Scperciva EOF 2061173564Scperciva fi 2062173564Scperciva 2063173564Scperciva # Re-sort the list of installed components and generate 2064173564Scperciva # the list of non-installed components. 2065173564Scperciva sort -u < comp.present > comp.present.tmp 2066173564Scperciva mv comp.present.tmp comp.present 2067173564Scperciva comm -13 comp.present comp.total > comp.absent 2068173564Scperciva 2069173564Scperciva # Ask the user to confirm that what we have is correct. To 2070173564Scperciva # reduce user confusion, translate "X|Y" back to "X/Y" (as 2071173564Scperciva # subcomponents must be listed in the configuration file). 2072173564Scperciva echo 2073173564Scperciva echo -n "The following components of FreeBSD " 2074173564Scperciva echo "seem to be installed:" 2075173564Scperciva tr '|' '/' < comp.present | 2076173564Scperciva fmt -72 2077173564Scperciva echo 2078173564Scperciva echo -n "The following components of FreeBSD " 2079173564Scperciva echo "do not seem to be installed:" 2080173564Scperciva tr '|' '/' < comp.absent | 2081173564Scperciva fmt -72 2082173564Scperciva echo 2083173564Scperciva continuep || return 1 2084173564Scperciva echo 2085173564Scperciva 2086173564Scperciva # Suck the generated list of components into ${COMPONENTS}. 2087173564Scperciva # Note that comp.present.tmp is used due to issues with 2088173564Scperciva # pipelines and setting variables. 2089173564Scperciva COMPONENTS="" 2090173564Scperciva tr '|' '/' < comp.present > comp.present.tmp 2091173564Scperciva while read C; do 2092173564Scperciva COMPONENTS="${COMPONENTS} ${C}" 2093173564Scperciva done < comp.present.tmp 2094173564Scperciva 2095173564Scperciva # Delete temporary files 2096173564Scperciva rm comp.present comp.present.tmp comp.absent comp.total 2097173564Scperciva fi 2098173564Scperciva} 2099173564Scperciva 2100173564Scperciva# If StrictComponents is not "yes", COMPONENTS contains an entry 2101173564Scperciva# corresponding to the currently running kernel, and said kernel 2102173564Scperciva# does not exist in the new release, add "kernel/generic" to the 2103173564Scperciva# list of components. 2104173564Scpercivaupgrade_guess_new_kernel () { 2105173564Scperciva if [ "${STRICTCOMPONENTS}" = "no" ]; then 2106173564Scperciva # Grab the unfiltered metadata file. 2107173564Scperciva METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'` 2108173564Scperciva gunzip -c < files/${METAHASH}.gz > $1.all 2109173564Scperciva 2110173564Scperciva # If "kernel/${KCOMP}" is in ${COMPONENTS} and that component 2111173564Scperciva # isn't in $1.all, we need to add kernel/generic. 2112173564Scperciva for C in ${COMPONENTS}; do 2113173564Scperciva if [ ${C} = "kernel/${KCOMP}" ] && 2114173564Scperciva ! grep -qE "^kernel\|${KCOMP}\|" $1.all; then 2115173564Scperciva COMPONENTS="${COMPONENTS} kernel/generic" 2116173564Scperciva NKERNCONF="GENERIC" 2117173564Scperciva cat <<-EOF 2118173564Scperciva 2119173564ScpercivaWARNING: This system is running a "${KCOMP}" kernel, which is not a 2120173564Scpercivakernel configuration distributed as part of FreeBSD ${RELNUM}. 2121173564ScpercivaAs part of upgrading to FreeBSD ${RELNUM}, this kernel will be 2122173564Scpercivareplaced with a "generic" kernel. 2123173564Scperciva EOF 2124173564Scperciva continuep || return 1 2125173564Scperciva fi 2126173564Scperciva done 2127173564Scperciva 2128173564Scperciva # Don't need this any more... 2129173564Scperciva rm $1.all 2130173564Scperciva fi 2131173564Scperciva} 2132173564Scperciva 2133173564Scperciva# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into 2134173564Scperciva# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades). 2135173564Scpercivaupgrade_oldall_to_oldnew () { 2136173564Scperciva # For each ${F}|... which appears in INDEX-ALL but does not appear 2137173564Scperciva # in INDEX-OLD, add ${F}|-|||||| to INDEX-OLD. 2138173564Scperciva cut -f 1 -d '|' < $1 | 2139173564Scperciva sort -u > $1.paths 2140173564Scperciva cut -f 1 -d '|' < $2 | 2141173564Scperciva sort -u | 2142173564Scperciva comm -13 $1.paths - | 2143173564Scperciva lam - -s "|-||||||" | 2144173564Scperciva sort - $1 > $1.tmp 2145173564Scperciva mv $1.tmp $1 2146173564Scperciva 2147173564Scperciva # Remove lines from INDEX-OLD which also appear in INDEX-ALL 2148173564Scperciva comm -23 $1 $2 > $1.tmp 2149173564Scperciva mv $1.tmp $1 2150173564Scperciva 2151173564Scperciva # Remove lines from INDEX-ALL which have a file name not appearing 2152173564Scperciva # anywhere in INDEX-OLD (since these must be files which haven't 2153173564Scperciva # changed -- if they were new, there would be an entry of type "-"). 2154173564Scperciva cut -f 1 -d '|' < $1 | 2155173564Scperciva sort -u > $1.paths 2156173564Scperciva sort -k 1,1 -t '|' < $2 | 2157173564Scperciva join -t '|' - $1.paths | 2158173564Scperciva sort > $2.tmp 2159173564Scperciva rm $1.paths 2160173564Scperciva mv $2.tmp $2 2161173564Scperciva 2162173564Scperciva # Rename INDEX-ALL to INDEX-NEW. 2163173564Scperciva mv $2 $3 2164173564Scperciva} 2165173564Scperciva 2166173564Scperciva# From the list of "old" files in $1, merge changes in $2 with those in $3, 2167173564Scperciva# and update $3 to reflect the hashes of merged files. 2168173564Scpercivaupgrade_merge () { 2169173564Scperciva # We only need to do anything if $1 is non-empty. 2170173564Scperciva if [ -s $1 ]; then 2171173564Scperciva cut -f 1 -d '|' $1 | 2172173564Scperciva sort > $1-paths 2173173564Scperciva 2174173564Scperciva # Create staging area for merging files 2175173564Scperciva rm -rf merge/ 2176173564Scperciva while read F; do 2177173564Scperciva D=`dirname ${F}` 2178173564Scperciva mkdir -p merge/old/${D} 2179173564Scperciva mkdir -p merge/${OLDRELNUM}/${D} 2180173564Scperciva mkdir -p merge/${RELNUM}/${D} 2181173564Scperciva mkdir -p merge/new/${D} 2182173564Scperciva done < $1-paths 2183173564Scperciva 2184173564Scperciva # Copy in files 2185173564Scperciva while read F; do 2186173564Scperciva # Currently installed file 2187173564Scperciva V=`look "${F}|" $2 | cut -f 7 -d '|'` 2188173564Scperciva gunzip < files/${V}.gz > merge/old/${F} 2189173564Scperciva 2190173564Scperciva # Old release 2191173564Scperciva if look "${F}|" $1 | fgrep -q "|f|"; then 2192173564Scperciva V=`look "${F}|" $1 | cut -f 3 -d '|'` 2193173564Scperciva gunzip < files/${V}.gz \ 2194173564Scperciva > merge/${OLDRELNUM}/${F} 2195173564Scperciva fi 2196173564Scperciva 2197173564Scperciva # New release 2198173564Scperciva if look "${F}|" $3 | cut -f 1,2,7 -d '|' | 2199173564Scperciva fgrep -q "|f|"; then 2200173564Scperciva V=`look "${F}|" $3 | cut -f 7 -d '|'` 2201173564Scperciva gunzip < files/${V}.gz \ 2202173564Scperciva > merge/${RELNUM}/${F} 2203173564Scperciva fi 2204173564Scperciva done < $1-paths 2205173564Scperciva 2206173564Scperciva # Attempt to automatically merge changes 2207173564Scperciva echo -n "Attempting to automatically merge " 2208173564Scperciva echo -n "changes in files..." 2209173564Scperciva : > failed.merges 2210173564Scperciva while read F; do 2211173564Scperciva # If the file doesn't exist in the new release, 2212173564Scperciva # the result of "merging changes" is having the file 2213173564Scperciva # not exist. 2214173564Scperciva if ! [ -f merge/${RELNUM}/${F} ]; then 2215173564Scperciva continue 2216173564Scperciva fi 2217173564Scperciva 2218173564Scperciva # If the file didn't exist in the old release, we're 2219173564Scperciva # going to throw away the existing file and hope that 2220173564Scperciva # the version from the new release is what we want. 2221173564Scperciva if ! [ -f merge/${OLDRELNUM}/${F} ]; then 2222173564Scperciva cp merge/${RELNUM}/${F} merge/new/${F} 2223173564Scperciva continue 2224173564Scperciva fi 2225173564Scperciva 2226173564Scperciva # Some files need special treatment. 2227173564Scperciva case ${F} in 2228173564Scperciva /etc/spwd.db | /etc/pwd.db | /etc/login.conf.db) 2229173564Scperciva # Don't merge these -- we're rebuild them 2230173564Scperciva # after updates are installed. 2231173564Scperciva cp merge/old/${F} merge/new/${F} 2232173564Scperciva ;; 2233173564Scperciva *) 2234173564Scperciva if ! merge -p -L "current version" \ 2235173564Scperciva -L "${OLDRELNUM}" -L "${RELNUM}" \ 2236173564Scperciva merge/old/${F} \ 2237173564Scperciva merge/${OLDRELNUM}/${F} \ 2238173564Scperciva merge/${RELNUM}/${F} \ 2239173564Scperciva > merge/new/${F} 2>/dev/null; then 2240173564Scperciva echo ${F} >> failed.merges 2241173564Scperciva fi 2242173564Scperciva ;; 2243173564Scperciva esac 2244173564Scperciva done < $1-paths 2245173564Scperciva echo " done." 2246173564Scperciva 2247173564Scperciva # Ask the user to handle any files which didn't merge. 2248173564Scperciva while read F; do 2249173564Scperciva cat <<-EOF 2250173564Scperciva 2251173564ScpercivaThe following file could not be merged automatically: ${F} 2252173564ScpercivaPress Enter to edit this file in ${EDITOR} and resolve the conflicts 2253173564Scpercivamanually... 2254173564Scperciva EOF 2255173564Scperciva read dummy </dev/tty 2256173564Scperciva ${EDITOR} `pwd`/merge/new/${F} < /dev/tty 2257173564Scperciva done < failed.merges 2258173564Scperciva rm failed.merges 2259173564Scperciva 2260173564Scperciva # Ask the user to confirm that he likes how the result 2261173564Scperciva # of merging files. 2262173564Scperciva while read F; do 2263173564Scperciva # Skip files which haven't changed. 2264173564Scperciva if [ -f merge/new/${F} ] && 2265173564Scperciva cmp -s merge/old/${F} merge/new/${F}; then 2266173564Scperciva continue 2267173564Scperciva fi 2268173564Scperciva 2269173564Scperciva # Warn about files which are ceasing to exist. 2270173564Scperciva if ! [ -f merge/new/${F} ]; then 2271173564Scperciva cat <<-EOF 2272173564Scperciva 2273173564ScpercivaThe following file will be removed, as it no longer exists in 2274173564ScpercivaFreeBSD ${RELNUM}: ${F} 2275173564Scperciva EOF 2276173564Scperciva continuep < /dev/tty || return 1 2277173564Scperciva continue 2278173564Scperciva fi 2279173564Scperciva 2280173564Scperciva # Print changes for the user's approval. 2281173564Scperciva cat <<-EOF 2282173564Scperciva 2283173564ScpercivaThe following changes, which occurred between FreeBSD ${OLDRELNUM} and 2284173564ScpercivaFreeBSD ${RELNUM} have been merged into ${F}: 2285173564ScpercivaEOF 2286173564Scperciva diff -U 5 -L "current version" -L "new version" \ 2287173564Scperciva merge/old/${F} merge/new/${F} || true 2288173564Scperciva continuep < /dev/tty || return 1 2289173564Scperciva done < $1-paths 2290173564Scperciva 2291173564Scperciva # Store merged files. 2292173564Scperciva while read F; do 2293177527Scperciva if [ -f merge/new/${F} ]; then 2294177527Scperciva V=`${SHA256} -q merge/new/${F}` 2295173564Scperciva 2296173564Scperciva gzip -c < merge/new/${F} > files/${V}.gz 2297173564Scperciva echo "${F}|${V}" 2298173564Scperciva fi 2299173564Scperciva done < $1-paths > newhashes 2300173564Scperciva 2301173564Scperciva # Pull lines out from $3 which need to be updated to 2302173564Scperciva # reflect merged files. 2303173564Scperciva while read F; do 2304173564Scperciva look "${F}|" $3 2305173564Scperciva done < $1-paths > $3-oldlines 2306173564Scperciva 2307173564Scperciva # Update lines to reflect merged files 2308173564Scperciva join -t '|' -o 1.1,1.2,1.3,1.4,1.5,1.6,2.2,1.8 \ 2309173564Scperciva $3-oldlines newhashes > $3-newlines 2310173564Scperciva 2311173564Scperciva # Remove old lines from $3 and add new lines. 2312173564Scperciva sort $3-oldlines | 2313173564Scperciva comm -13 - $3 | 2314173564Scperciva sort - $3-newlines > $3.tmp 2315173564Scperciva mv $3.tmp $3 2316173564Scperciva 2317173564Scperciva # Clean up 2318173564Scperciva rm $1-paths newhashes $3-oldlines $3-newlines 2319173564Scperciva rm -rf merge/ 2320173564Scperciva fi 2321173564Scperciva 2322173564Scperciva # We're done with merging files. 2323173564Scperciva rm $1 2324173564Scperciva} 2325173564Scperciva 2326173564Scperciva# Do the work involved in fetching upgrades to a new release 2327173564Scpercivaupgrade_run () { 2328173564Scperciva workdir_init || return 1 2329173564Scperciva 2330173564Scperciva # Prepare the mirror list. 2331173564Scperciva fetch_pick_server_init && fetch_pick_server 2332173564Scperciva 2333173564Scperciva # Try to fetch the public key until we run out of servers. 2334173564Scperciva while ! fetch_key; do 2335173564Scperciva fetch_pick_server || return 1 2336173564Scperciva done 2337173564Scperciva 2338173564Scperciva # Try to fetch the metadata index signature ("tag") until we run 2339173564Scperciva # out of available servers; and sanity check the downloaded tag. 2340173564Scperciva while ! fetch_tag; do 2341173564Scperciva fetch_pick_server || return 1 2342173564Scperciva done 2343173564Scperciva fetch_tagsanity || return 1 2344173564Scperciva 2345173564Scperciva # Fetch the INDEX-OLD and INDEX-ALL. 2346173564Scperciva fetch_metadata INDEX-OLD INDEX-ALL || return 1 2347173564Scperciva 2348173564Scperciva # If StrictComponents is not "yes", generate a new components list 2349173564Scperciva # with only the components which appear to be installed. 2350173564Scperciva upgrade_guess_components INDEX-ALL || return 1 2351173564Scperciva 2352173564Scperciva # Generate filtered INDEX-OLD and INDEX-ALL files containing only 2353173564Scperciva # the components we want and without anything marked as "Ignore". 2354173564Scperciva fetch_filter_metadata INDEX-OLD || return 1 2355173564Scperciva fetch_filter_metadata INDEX-ALL || return 1 2356173564Scperciva 2357173564Scperciva # Merge the INDEX-OLD and INDEX-ALL files into INDEX-OLD. 2358173564Scperciva sort INDEX-OLD INDEX-ALL > INDEX-OLD.tmp 2359173564Scperciva mv INDEX-OLD.tmp INDEX-OLD 2360173564Scperciva rm INDEX-ALL 2361173564Scperciva 2362173564Scperciva # Adjust variables for fetching files from the new release. 2363173564Scperciva OLDRELNUM=${RELNUM} 2364173564Scperciva RELNUM=${TARGETRELEASE} 2365173564Scperciva OLDFETCHDIR=${FETCHDIR} 2366173564Scperciva FETCHDIR=${RELNUM}/${ARCH} 2367173564Scperciva 2368173564Scperciva # Try to fetch the NEW metadata index signature ("tag") until we run 2369173564Scperciva # out of available servers; and sanity check the downloaded tag. 2370173564Scperciva while ! fetch_tag; do 2371173564Scperciva fetch_pick_server || return 1 2372173564Scperciva done 2373173564Scperciva 2374173564Scperciva # Fetch the new INDEX-ALL. 2375173564Scperciva fetch_metadata INDEX-ALL || return 1 2376173564Scperciva 2377173564Scperciva # If StrictComponents is not "yes", COMPONENTS contains an entry 2378173564Scperciva # corresponding to the currently running kernel, and said kernel 2379173564Scperciva # does not exist in the new release, add "kernel/generic" to the 2380173564Scperciva # list of components. 2381173564Scperciva upgrade_guess_new_kernel INDEX-ALL || return 1 2382173564Scperciva 2383173564Scperciva # Filter INDEX-ALL to contain only the components we want and without 2384173564Scperciva # anything marked as "Ignore". 2385173564Scperciva fetch_filter_metadata INDEX-ALL || return 1 2386173564Scperciva 2387173564Scperciva # Convert INDEX-OLD (last release) and INDEX-ALL (new release) into 2388173564Scperciva # INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades). 2389173564Scperciva upgrade_oldall_to_oldnew INDEX-OLD INDEX-ALL INDEX-NEW 2390173564Scperciva 2391173564Scperciva # Translate /boot/${KERNCONF} or /boot/${NKERNCONF} into ${KERNELDIR} 2392173564Scperciva fetch_filter_kernel_names INDEX-NEW ${NKERNCONF} 2393173564Scperciva fetch_filter_kernel_names INDEX-OLD ${KERNCONF} 2394173564Scperciva 2395173564Scperciva # For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the 2396173564Scperciva # system and generate an INDEX-PRESENT file. 2397173564Scperciva fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 2398173564Scperciva 2399173564Scperciva # Based on ${MERGECHANGES}, generate a file tomerge-old with the 2400173564Scperciva # paths and hashes of old versions of files to merge. 2401173564Scperciva fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT tomerge-old 2402173564Scperciva 2403173564Scperciva # Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which 2404173564Scperciva # correspond to lines in INDEX-PRESENT with hashes not appearing 2405173564Scperciva # in INDEX-OLD or INDEX-NEW. Also remove lines where the entry in 2406173564Scperciva # INDEX-PRESENT has type - and there isn't a corresponding entry in 2407173564Scperciva # INDEX-OLD with type -. 2408173564Scperciva fetch_filter_unmodified_notpresent \ 2409173564Scperciva INDEX-OLD INDEX-PRESENT INDEX-NEW tomerge-old 2410173564Scperciva 2411173564Scperciva # For each entry in INDEX-PRESENT of type -, remove any corresponding 2412173564Scperciva # entry from INDEX-NEW if ${ALLOWADD} != "yes". Remove all entries 2413173564Scperciva # of type - from INDEX-PRESENT. 2414173564Scperciva fetch_filter_allowadd INDEX-PRESENT INDEX-NEW 2415173564Scperciva 2416173564Scperciva # If ${ALLOWDELETE} != "yes", then remove any entries from 2417173564Scperciva # INDEX-PRESENT which don't correspond to entries in INDEX-NEW. 2418173564Scperciva fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW 2419173564Scperciva 2420173564Scperciva # If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in 2421173564Scperciva # INDEX-PRESENT with metadata not matching any entry in INDEX-OLD, 2422173564Scperciva # replace the corresponding line of INDEX-NEW with one having the 2423173564Scperciva # same metadata as the entry in INDEX-PRESENT. 2424173564Scperciva fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW 2425173564Scperciva 2426173564Scperciva # Remove lines from INDEX-PRESENT and INDEX-NEW which are identical; 2427173564Scperciva # no need to update a file if it isn't changing. 2428173564Scperciva fetch_filter_uptodate INDEX-PRESENT INDEX-NEW 2429173564Scperciva 2430173564Scperciva # Fetch "clean" files from the old release for merging changes. 2431173564Scperciva fetch_files_premerge tomerge-old 2432173564Scperciva 2433173564Scperciva # Prepare to fetch files: Generate a list of the files we need, 2434173564Scperciva # copy the unmodified files we have into /files/, and generate 2435173564Scperciva # a list of patches to download. 2436173564Scperciva fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 2437173564Scperciva 2438173564Scperciva # Fetch patches from to-${RELNUM}/${ARCH}/bp/ 2439173564Scperciva PATCHDIR=to-${RELNUM}/${ARCH}/bp 2440173564Scperciva fetch_files || return 1 2441173564Scperciva 2442173564Scperciva # Merge configuration file changes. 2443173564Scperciva upgrade_merge tomerge-old INDEX-PRESENT INDEX-NEW || return 1 2444173564Scperciva 2445173564Scperciva # Create and populate install manifest directory; and report what 2446173564Scperciva # updates are available. 2447173564Scperciva fetch_create_manifest || return 1 2448173564Scperciva 2449173564Scperciva # Leave a note behind to tell the "install" command that the kernel 2450173564Scperciva # needs to be installed before the world. 2451173564Scperciva touch ${BDHASH}-install/kernelfirst 2452173564Scperciva} 2453173564Scperciva 2454161748Scperciva# Make sure that all the file hashes mentioned in $@ have corresponding 2455161748Scperciva# gzipped files stored in /files/. 2456161748Scpercivainstall_verify () { 2457161748Scperciva # Generate a list of hashes 2458161748Scperciva cat $@ | 2459161748Scperciva cut -f 2,7 -d '|' | 2460161748Scperciva grep -E '^f' | 2461161748Scperciva cut -f 2 -d '|' | 2462161748Scperciva sort -u > filelist 2463161748Scperciva 2464161748Scperciva # Make sure all the hashes exist 2465161748Scperciva while read HASH; do 2466161748Scperciva if ! [ -f files/${HASH}.gz ]; then 2467161748Scperciva echo -n "Update files missing -- " 2468161748Scperciva echo "this should never happen." 2469161748Scperciva echo "Re-run '$0 fetch'." 2470161748Scperciva return 1 2471161748Scperciva fi 2472161748Scperciva done < filelist 2473161748Scperciva 2474161748Scperciva # Clean up 2475161748Scperciva rm filelist 2476161748Scperciva} 2477161748Scperciva 2478161748Scperciva# Remove the system immutable flag from files 2479161748Scpercivainstall_unschg () { 2480161748Scperciva # Generate file list 2481161748Scperciva cat $@ | 2482161748Scperciva cut -f 1 -d '|' > filelist 2483161748Scperciva 2484161748Scperciva # Remove flags 2485161748Scperciva while read F; do 2486169603Scperciva if ! [ -e ${BASEDIR}/${F} ]; then 2487161748Scperciva continue 2488161748Scperciva fi 2489161748Scperciva 2490169603Scperciva chflags noschg ${BASEDIR}/${F} || return 1 2491161748Scperciva done < filelist 2492161748Scperciva 2493161748Scperciva # Clean up 2494161748Scperciva rm filelist 2495161748Scperciva} 2496161748Scperciva 2497161748Scperciva# Install new files 2498161748Scpercivainstall_from_index () { 2499161748Scperciva # First pass: Do everything apart from setting file flags. We 2500161748Scperciva # can't set flags yet, because schg inhibits hard linking. 2501161748Scperciva sort -k 1,1 -t '|' $1 | 2502161748Scperciva tr '|' ' ' | 2503161748Scperciva while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do 2504161748Scperciva case ${TYPE} in 2505161748Scperciva d) 2506161748Scperciva # Create a directory 2507161748Scperciva install -d -o ${OWNER} -g ${GROUP} \ 2508161748Scperciva -m ${PERM} ${BASEDIR}/${FPATH} 2509161748Scperciva ;; 2510161748Scperciva f) 2511161748Scperciva if [ -z "${LINK}" ]; then 2512161748Scperciva # Create a file, without setting flags. 2513161748Scperciva gunzip < files/${HASH}.gz > ${HASH} 2514161748Scperciva install -S -o ${OWNER} -g ${GROUP} \ 2515161748Scperciva -m ${PERM} ${HASH} ${BASEDIR}/${FPATH} 2516161748Scperciva rm ${HASH} 2517161748Scperciva else 2518161748Scperciva # Create a hard link. 2519169603Scperciva ln -f ${BASEDIR}/${LINK} ${BASEDIR}/${FPATH} 2520161748Scperciva fi 2521161748Scperciva ;; 2522161748Scperciva L) 2523161748Scperciva # Create a symlink 2524161748Scperciva ln -sfh ${HASH} ${BASEDIR}/${FPATH} 2525161748Scperciva ;; 2526161748Scperciva esac 2527161748Scperciva done 2528161748Scperciva 2529161748Scperciva # Perform a second pass, adding file flags. 2530161748Scperciva tr '|' ' ' < $1 | 2531161748Scperciva while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do 2532161748Scperciva if [ ${TYPE} = "f" ] && 2533161748Scperciva ! [ ${FLAGS} = "0" ]; then 2534161748Scperciva chflags ${FLAGS} ${BASEDIR}/${FPATH} 2535161748Scperciva fi 2536161748Scperciva done 2537161748Scperciva} 2538161748Scperciva 2539161748Scperciva# Remove files which we want to delete 2540161748Scpercivainstall_delete () { 2541161748Scperciva # Generate list of new files 2542161748Scperciva cut -f 1 -d '|' < $2 | 2543161748Scperciva sort > newfiles 2544161748Scperciva 2545161748Scperciva # Generate subindex of old files we want to nuke 2546161748Scperciva sort -k 1,1 -t '|' $1 | 2547161748Scperciva join -t '|' -v 1 - newfiles | 2548164600Scperciva sort -r -k 1,1 -t '|' | 2549161748Scperciva cut -f 1,2 -d '|' | 2550161748Scperciva tr '|' ' ' > killfiles 2551161748Scperciva 2552161748Scperciva # Remove the offending bits 2553161748Scperciva while read FPATH TYPE; do 2554161748Scperciva case ${TYPE} in 2555161748Scperciva d) 2556161748Scperciva rmdir ${BASEDIR}/${FPATH} 2557161748Scperciva ;; 2558161748Scperciva f) 2559161748Scperciva rm ${BASEDIR}/${FPATH} 2560161748Scperciva ;; 2561161748Scperciva L) 2562161748Scperciva rm ${BASEDIR}/${FPATH} 2563161748Scperciva ;; 2564161748Scperciva esac 2565161748Scperciva done < killfiles 2566161748Scperciva 2567161748Scperciva # Clean up 2568161748Scperciva rm newfiles killfiles 2569161748Scperciva} 2570161748Scperciva 2571173564Scperciva# Install new files, delete old files, and update linker.hints 2572173564Scpercivainstall_files () { 2573173564Scperciva # If we haven't already dealt with the kernel, deal with it. 2574173564Scperciva if ! [ -f $1/kerneldone ]; then 2575173564Scperciva grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD 2576173564Scperciva grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW 2577173564Scperciva 2578173564Scperciva # Install new files 2579173564Scperciva install_from_index INDEX-NEW || return 1 2580173564Scperciva 2581173564Scperciva # Remove files which need to be deleted 2582173564Scperciva install_delete INDEX-OLD INDEX-NEW || return 1 2583173564Scperciva 2584173564Scperciva # Update linker.hints if necessary 2585173564Scperciva if [ -s INDEX-OLD -o -s INDEX-NEW ]; then 2586173564Scperciva kldxref -R /boot/ 2>/dev/null 2587173564Scperciva fi 2588173564Scperciva 2589173564Scperciva # We've finished updating the kernel. 2590173564Scperciva touch $1/kerneldone 2591173564Scperciva 2592173564Scperciva # Do we need to ask for a reboot now? 2593173564Scperciva if [ -f $1/kernelfirst ] && 2594173564Scperciva [ -s INDEX-OLD -o -s INDEX-NEW ]; then 2595173564Scperciva cat <<-EOF 2596173564Scperciva 2597173564ScpercivaKernel updates have been installed. Please reboot and run 2598173564Scperciva"$0 install" again to finish installing updates. 2599173564Scperciva EOF 2600173564Scperciva exit 0 2601173564Scperciva fi 2602161748Scperciva fi 2603173564Scperciva 2604173564Scperciva # If we haven't already dealt with the world, deal with it. 2605173564Scperciva if ! [ -f $1/worlddone ]; then 2606173564Scperciva # Install new shared libraries next 2607173564Scperciva grep -vE '^/boot/' $1/INDEX-NEW | 2608177601Scperciva grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW 2609173564Scperciva install_from_index INDEX-NEW || return 1 2610173564Scperciva 2611173564Scperciva # Deal with everything else 2612173564Scperciva grep -vE '^/boot/' $1/INDEX-OLD | 2613177601Scperciva grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD 2614173564Scperciva grep -vE '^/boot/' $1/INDEX-NEW | 2615177601Scperciva grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW 2616173564Scperciva install_from_index INDEX-NEW || return 1 2617173564Scperciva install_delete INDEX-OLD INDEX-NEW || return 1 2618173564Scperciva 2619173564Scperciva # Rebuild /etc/spwd.db and /etc/pwd.db if necessary. 2620173564Scperciva if [ /etc/master.passwd -nt /etc/spwd.db ] || 2621173564Scperciva [ /etc/master.passwd -nt /etc/pwd.db ]; then 2622173564Scperciva pwd_mkdb /etc/master.passwd 2623173564Scperciva fi 2624173564Scperciva 2625173564Scperciva # Rebuild /etc/login.conf.db if necessary. 2626173564Scperciva if [ /etc/login.conf -nt /etc/login.conf.db ]; then 2627173564Scperciva cap_mkdb /etc/login.conf 2628173564Scperciva fi 2629173564Scperciva 2630173564Scperciva # We've finished installing the world and deleting old files 2631173564Scperciva # which are not shared libraries. 2632173564Scperciva touch $1/worlddone 2633173564Scperciva 2634173564Scperciva # Do we need to ask the user to portupgrade now? 2635173564Scperciva grep -vE '^/boot/' $1/INDEX-NEW | 2636177601Scperciva grep -E '/lib/.*\.so\.[0-9]+\|' | 2637173564Scperciva cut -f 1 -d '|' | 2638173564Scperciva sort > newfiles 2639173564Scperciva if grep -vE '^/boot/' $1/INDEX-OLD | 2640177601Scperciva grep -E '/lib/.*\.so\.[0-9]+\|' | 2641173564Scperciva cut -f 1 -d '|' | 2642173564Scperciva sort | 2643173564Scperciva join -v 1 - newfiles | 2644173564Scperciva grep -q .; then 2645173564Scperciva cat <<-EOF 2646173564Scperciva 2647173564ScpercivaCompleting this upgrade requires removing old shared object files. 2648173564ScpercivaPlease rebuild all installed 3rd party software (e.g., programs 2649173564Scpercivainstalled from the ports tree) and then run "$0 install" 2650173564Scpercivaagain to finish installing updates. 2651173564Scperciva EOF 2652173564Scperciva rm newfiles 2653173564Scperciva exit 0 2654173564Scperciva fi 2655173564Scperciva rm newfiles 2656173564Scperciva fi 2657173564Scperciva 2658173564Scperciva # Remove old shared libraries 2659173564Scperciva grep -vE '^/boot/' $1/INDEX-NEW | 2660177601Scperciva grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW 2661173564Scperciva grep -vE '^/boot/' $1/INDEX-OLD | 2662177601Scperciva grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD 2663173564Scperciva install_delete INDEX-OLD INDEX-NEW || return 1 2664173564Scperciva 2665173564Scperciva # Remove temporary files 2666173564Scperciva rm INDEX-OLD INDEX-NEW 2667161748Scperciva} 2668161748Scperciva 2669161748Scperciva# Rearrange bits to allow the installed updates to be rolled back 2670161748Scpercivainstall_setup_rollback () { 2671173564Scperciva # Remove the "reboot after installing kernel", "kernel updated", and 2672173564Scperciva # "finished installing the world" flags if present -- they are 2673173564Scperciva # irrelevant when rolling back updates. 2674173564Scperciva if [ -f ${BDHASH}-install/kernelfirst ]; then 2675173564Scperciva rm ${BDHASH}-install/kernelfirst 2676173564Scperciva rm ${BDHASH}-install/kerneldone 2677173564Scperciva fi 2678173564Scperciva if [ -f ${BDHASH}-install/worlddone ]; then 2679173564Scperciva rm ${BDHASH}-install/worlddone 2680173564Scperciva fi 2681173564Scperciva 2682161748Scperciva if [ -L ${BDHASH}-rollback ]; then 2683161748Scperciva mv ${BDHASH}-rollback ${BDHASH}-install/rollback 2684161748Scperciva fi 2685161748Scperciva 2686161748Scperciva mv ${BDHASH}-install ${BDHASH}-rollback 2687161748Scperciva} 2688161748Scperciva 2689161748Scperciva# Actually install updates 2690161748Scpercivainstall_run () { 2691161748Scperciva echo -n "Installing updates..." 2692161748Scperciva 2693161748Scperciva # Make sure we have all the files we should have 2694161748Scperciva install_verify ${BDHASH}-install/INDEX-OLD \ 2695161748Scperciva ${BDHASH}-install/INDEX-NEW || return 1 2696161748Scperciva 2697161748Scperciva # Remove system immutable flag from files 2698161748Scperciva install_unschg ${BDHASH}-install/INDEX-OLD \ 2699161748Scperciva ${BDHASH}-install/INDEX-NEW || return 1 2700161748Scperciva 2701173564Scperciva # Install new files, delete old files, and update linker.hints 2702173564Scperciva install_files ${BDHASH}-install || return 1 2703161748Scperciva 2704161748Scperciva # Rearrange bits to allow the installed updates to be rolled back 2705161748Scperciva install_setup_rollback 2706161748Scperciva 2707161748Scperciva echo " done." 2708161748Scperciva} 2709161748Scperciva 2710161748Scperciva# Rearrange bits to allow the previous set of updates to be rolled back next. 2711161748Scpercivarollback_setup_rollback () { 2712161748Scperciva if [ -L ${BDHASH}-rollback/rollback ]; then 2713161748Scperciva mv ${BDHASH}-rollback/rollback rollback-tmp 2714161748Scperciva rm -r ${BDHASH}-rollback/ 2715161748Scperciva rm ${BDHASH}-rollback 2716161748Scperciva mv rollback-tmp ${BDHASH}-rollback 2717161748Scperciva else 2718161748Scperciva rm -r ${BDHASH}-rollback/ 2719161748Scperciva rm ${BDHASH}-rollback 2720161748Scperciva fi 2721161748Scperciva} 2722161748Scperciva 2723173564Scperciva# Install old files, delete new files, and update linker.hints 2724173564Scpercivarollback_files () { 2725173671Scperciva # Install old shared library files which don't have the same path as 2726173671Scperciva # a new shared library file. 2727173671Scperciva grep -vE '^/boot/' $1/INDEX-NEW | 2728177601Scperciva grep -E '/lib/.*\.so\.[0-9]+\|' | 2729173671Scperciva cut -f 1 -d '|' | 2730173671Scperciva sort > INDEX-NEW.libs.flist 2731173564Scperciva grep -vE '^/boot/' $1/INDEX-OLD | 2732177601Scperciva grep -E '/lib/.*\.so\.[0-9]+\|' | 2733173671Scperciva sort -k 1,1 -t '|' - | 2734173671Scperciva join -t '|' -v 1 - INDEX-NEW.libs.flist > INDEX-OLD 2735173564Scperciva install_from_index INDEX-OLD || return 1 2736173564Scperciva 2737173564Scperciva # Deal with files which are neither kernel nor shared library 2738173564Scperciva grep -vE '^/boot/' $1/INDEX-OLD | 2739177601Scperciva grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD 2740173564Scperciva grep -vE '^/boot/' $1/INDEX-NEW | 2741177601Scperciva grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW 2742173564Scperciva install_from_index INDEX-OLD || return 1 2743173564Scperciva install_delete INDEX-NEW INDEX-OLD || return 1 2744173564Scperciva 2745173671Scperciva # Install any old shared library files which we didn't install above. 2746173671Scperciva grep -vE '^/boot/' $1/INDEX-OLD | 2747177601Scperciva grep -E '/lib/.*\.so\.[0-9]+\|' | 2748173671Scperciva sort -k 1,1 -t '|' - | 2749173671Scperciva join -t '|' - INDEX-NEW.libs.flist > INDEX-OLD 2750173671Scperciva install_from_index INDEX-OLD || return 1 2751173671Scperciva 2752173564Scperciva # Delete unneeded shared library files 2753173564Scperciva grep -vE '^/boot/' $1/INDEX-OLD | 2754177601Scperciva grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD 2755173564Scperciva grep -vE '^/boot/' $1/INDEX-NEW | 2756177601Scperciva grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW 2757173564Scperciva install_delete INDEX-NEW INDEX-OLD || return 1 2758173564Scperciva 2759173564Scperciva # Deal with kernel files 2760173564Scperciva grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD 2761173564Scperciva grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW 2762173564Scperciva install_from_index INDEX-OLD || return 1 2763173564Scperciva install_delete INDEX-NEW INDEX-OLD || return 1 2764173564Scperciva if [ -s INDEX-OLD -o -s INDEX-NEW ]; then 2765173564Scperciva kldxref -R /boot/ 2>/dev/null 2766173564Scperciva fi 2767173564Scperciva 2768173564Scperciva # Remove temporary files 2769173672Scperciva rm INDEX-OLD INDEX-NEW INDEX-NEW.libs.flist 2770173564Scperciva} 2771173564Scperciva 2772161748Scperciva# Actually rollback updates 2773161748Scpercivarollback_run () { 2774161748Scperciva echo -n "Uninstalling updates..." 2775161748Scperciva 2776161748Scperciva # If there are updates waiting to be installed, remove them; we 2777161748Scperciva # want the user to re-run 'fetch' after rolling back updates. 2778161748Scperciva if [ -L ${BDHASH}-install ]; then 2779161748Scperciva rm -r ${BDHASH}-install/ 2780161748Scperciva rm ${BDHASH}-install 2781161748Scperciva fi 2782161748Scperciva 2783161748Scperciva # Make sure we have all the files we should have 2784161748Scperciva install_verify ${BDHASH}-rollback/INDEX-NEW \ 2785161748Scperciva ${BDHASH}-rollback/INDEX-OLD || return 1 2786161748Scperciva 2787161748Scperciva # Remove system immutable flag from files 2788161748Scperciva install_unschg ${BDHASH}-rollback/INDEX-NEW \ 2789161748Scperciva ${BDHASH}-rollback/INDEX-OLD || return 1 2790161748Scperciva 2791173564Scperciva # Install old files, delete new files, and update linker.hints 2792173564Scperciva rollback_files ${BDHASH}-rollback || return 1 2793161748Scperciva 2794161748Scperciva # Remove the rollback directory and the symlink pointing to it; and 2795161748Scperciva # rearrange bits to allow the previous set of updates to be rolled 2796161748Scperciva # back next. 2797161748Scperciva rollback_setup_rollback 2798161748Scperciva 2799161748Scperciva echo " done." 2800161748Scperciva} 2801161748Scperciva 2802181142Scperciva# Compare INDEX-ALL and INDEX-PRESENT and print warnings about differences. 2803181142ScpercivaIDS_compare () { 2804181425Scperciva # Get all the lines which mismatch in something other than file 2805181425Scperciva # flags. We ignore file flags because sysinstall doesn't seem to 2806181425Scperciva # set them when it installs FreeBSD; warning about these adds a 2807181425Scperciva # very large amount of noise. 2808181425Scperciva cut -f 1-5,7-8 -d '|' $1 > $1.noflags 2809181425Scperciva sort -k 1,1 -t '|' $1.noflags > $1.sorted 2810181425Scperciva cut -f 1-5,7-8 -d '|' $2 | 2811181425Scperciva comm -13 $1.noflags - | 2812181425Scperciva fgrep -v '|-|||||' | 2813181142Scperciva sort -k 1,1 -t '|' | 2814181142Scperciva join -t '|' $1.sorted - > INDEX-NOTMATCHING 2815181142Scperciva 2816181142Scperciva # Ignore files which match IDSIGNOREPATHS. 2817181142Scperciva for X in ${IDSIGNOREPATHS}; do 2818181142Scperciva grep -E "^${X}" INDEX-NOTMATCHING 2819181142Scperciva done | 2820181142Scperciva sort -u | 2821181142Scperciva comm -13 - INDEX-NOTMATCHING > INDEX-NOTMATCHING.tmp 2822181142Scperciva mv INDEX-NOTMATCHING.tmp INDEX-NOTMATCHING 2823181142Scperciva 2824181142Scperciva # Go through the lines and print warnings. 2825181142Scperciva while read LINE; do 2826181142Scperciva FPATH=`echo "${LINE}" | cut -f 1 -d '|'` 2827181142Scperciva TYPE=`echo "${LINE}" | cut -f 2 -d '|'` 2828181142Scperciva OWNER=`echo "${LINE}" | cut -f 3 -d '|'` 2829181142Scperciva GROUP=`echo "${LINE}" | cut -f 4 -d '|'` 2830181142Scperciva PERM=`echo "${LINE}" | cut -f 5 -d '|'` 2831181425Scperciva HASH=`echo "${LINE}" | cut -f 6 -d '|'` 2832181425Scperciva LINK=`echo "${LINE}" | cut -f 7 -d '|'` 2833181425Scperciva P_TYPE=`echo "${LINE}" | cut -f 8 -d '|'` 2834181425Scperciva P_OWNER=`echo "${LINE}" | cut -f 9 -d '|'` 2835181425Scperciva P_GROUP=`echo "${LINE}" | cut -f 10 -d '|'` 2836181425Scperciva P_PERM=`echo "${LINE}" | cut -f 11 -d '|'` 2837181425Scperciva P_HASH=`echo "${LINE}" | cut -f 12 -d '|'` 2838181425Scperciva P_LINK=`echo "${LINE}" | cut -f 13 -d '|'` 2839181142Scperciva 2840181142Scperciva # Warn about different object types. 2841181142Scperciva if ! [ "${TYPE}" = "${P_TYPE}" ]; then 2842181142Scperciva echo -n "${FPATH} is a " 2843181142Scperciva case "${P_TYPE}" in 2844181142Scperciva f) echo -n "regular file, " 2845181142Scperciva ;; 2846181142Scperciva d) echo -n "directory, " 2847181142Scperciva ;; 2848181142Scperciva L) echo -n "symlink, " 2849181142Scperciva ;; 2850181142Scperciva esac 2851181142Scperciva echo -n "but should be a " 2852181142Scperciva case "${TYPE}" in 2853181142Scperciva f) echo -n "regular file." 2854181142Scperciva ;; 2855181142Scperciva d) echo -n "directory." 2856181142Scperciva ;; 2857181142Scperciva L) echo -n "symlink." 2858181142Scperciva ;; 2859181142Scperciva esac 2860181142Scperciva echo 2861181142Scperciva 2862181142Scperciva # Skip other tests, since they don't make sense if 2863181142Scperciva # we're comparing different object types. 2864181142Scperciva continue 2865181142Scperciva fi 2866181142Scperciva 2867181142Scperciva # Warn about different owners. 2868181142Scperciva if ! [ "${OWNER}" = "${P_OWNER}" ]; then 2869181142Scperciva echo -n "${FPATH} is owned by user id ${P_OWNER}, " 2870181142Scperciva echo "but should be owned by user id ${OWNER}." 2871181142Scperciva fi 2872181142Scperciva 2873181142Scperciva # Warn about different groups. 2874181142Scperciva if ! [ "${GROUP}" = "${P_GROUP}" ]; then 2875181142Scperciva echo -n "${FPATH} is owned by group id ${P_GROUP}, " 2876181142Scperciva echo "but should be owned by group id ${GROUP}." 2877181142Scperciva fi 2878181142Scperciva 2879181142Scperciva # Warn about different permissions. We do not warn about 2880181142Scperciva # different permissions on symlinks, since some archivers 2881181142Scperciva # don't extract symlink permissions correctly and they are 2882181142Scperciva # ignored anyway. 2883181142Scperciva if ! [ "${PERM}" = "${P_PERM}" ] && 2884181142Scperciva ! [ "${TYPE}" = "L" ]; then 2885181142Scperciva echo -n "${FPATH} has ${P_PERM} permissions, " 2886181142Scperciva echo "but should have ${PERM} permissions." 2887181142Scperciva fi 2888181142Scperciva 2889181142Scperciva # Warn about different file hashes / symlink destinations. 2890181142Scperciva if ! [ "${HASH}" = "${P_HASH}" ]; then 2891181142Scperciva if [ "${TYPE}" = "L" ]; then 2892181142Scperciva echo -n "${FPATH} is a symlink to ${P_HASH}, " 2893181142Scperciva echo "but should be a symlink to ${HASH}." 2894181142Scperciva fi 2895181142Scperciva if [ "${TYPE}" = "f" ]; then 2896181142Scperciva echo -n "${FPATH} has SHA256 hash ${P_HASH}, " 2897181142Scperciva echo "but should have SHA256 hash ${HASH}." 2898181142Scperciva fi 2899181142Scperciva fi 2900181142Scperciva 2901181142Scperciva # We don't warn about different hard links, since some 2902181142Scperciva # some archivers break hard links, and as long as the 2903181142Scperciva # underlying data is correct they really don't matter. 2904181142Scperciva done < INDEX-NOTMATCHING 2905181142Scperciva 2906181142Scperciva # Clean up 2907181425Scperciva rm $1 $1.noflags $1.sorted $2 INDEX-NOTMATCHING 2908181142Scperciva} 2909181142Scperciva 2910181142Scperciva# Do the work involved in comparing the system to a "known good" index 2911181142ScpercivaIDS_run () { 2912181142Scperciva workdir_init || return 1 2913181142Scperciva 2914181142Scperciva # Prepare the mirror list. 2915181142Scperciva fetch_pick_server_init && fetch_pick_server 2916181142Scperciva 2917181142Scperciva # Try to fetch the public key until we run out of servers. 2918181142Scperciva while ! fetch_key; do 2919181142Scperciva fetch_pick_server || return 1 2920181142Scperciva done 2921181142Scperciva 2922181142Scperciva # Try to fetch the metadata index signature ("tag") until we run 2923181142Scperciva # out of available servers; and sanity check the downloaded tag. 2924181142Scperciva while ! fetch_tag; do 2925181142Scperciva fetch_pick_server || return 1 2926181142Scperciva done 2927181142Scperciva fetch_tagsanity || return 1 2928181142Scperciva 2929181142Scperciva # Fetch INDEX-OLD and INDEX-ALL. 2930181142Scperciva fetch_metadata INDEX-OLD INDEX-ALL || return 1 2931181142Scperciva 2932181142Scperciva # Generate filtered INDEX-OLD and INDEX-ALL files containing only 2933181142Scperciva # the components we want and without anything marked as "Ignore". 2934181142Scperciva fetch_filter_metadata INDEX-OLD || return 1 2935181142Scperciva fetch_filter_metadata INDEX-ALL || return 1 2936181142Scperciva 2937181142Scperciva # Merge the INDEX-OLD and INDEX-ALL files into INDEX-ALL. 2938181142Scperciva sort INDEX-OLD INDEX-ALL > INDEX-ALL.tmp 2939181142Scperciva mv INDEX-ALL.tmp INDEX-ALL 2940181142Scperciva rm INDEX-OLD 2941181142Scperciva 2942181142Scperciva # Translate /boot/${KERNCONF} to ${KERNELDIR} 2943181142Scperciva fetch_filter_kernel_names INDEX-ALL ${KERNCONF} 2944181142Scperciva 2945181142Scperciva # Inspect the system and generate an INDEX-PRESENT file. 2946181142Scperciva fetch_inspect_system INDEX-ALL INDEX-PRESENT /dev/null || return 1 2947181142Scperciva 2948181142Scperciva # Compare INDEX-ALL and INDEX-PRESENT and print warnings about any 2949181142Scperciva # differences. 2950181142Scperciva IDS_compare INDEX-ALL INDEX-PRESENT 2951181142Scperciva} 2952181142Scperciva 2953161748Scperciva#### Main functions -- call parameter-handling and core functions 2954161748Scperciva 2955161748Scperciva# Using the command line, configuration file, and defaults, 2956161748Scperciva# set all the parameters which are needed later. 2957161748Scpercivaget_params () { 2958161748Scperciva init_params 2959161748Scperciva parse_cmdline $@ 2960161748Scperciva parse_conffile 2961161748Scperciva default_params 2962161748Scperciva} 2963161748Scperciva 2964161748Scperciva# Fetch command. Make sure that we're being called 2965161748Scperciva# interactively, then run fetch_check_params and fetch_run 2966161748Scpercivacmd_fetch () { 2967161748Scperciva if [ ! -t 0 ]; then 2968161748Scperciva echo -n "`basename $0` fetch should not " 2969161748Scperciva echo "be run non-interactively." 2970161748Scperciva echo "Run `basename $0` cron instead." 2971161748Scperciva exit 1 2972161748Scperciva fi 2973161748Scperciva fetch_check_params 2974161748Scperciva fetch_run || exit 1 2975161748Scperciva} 2976161748Scperciva 2977161748Scperciva# Cron command. Make sure the parameters are sensible; wait 2978161748Scperciva# rand(3600) seconds; then fetch updates. While fetching updates, 2979161748Scperciva# send output to a temporary file; only print that file if the 2980161748Scperciva# fetching failed. 2981161748Scpercivacmd_cron () { 2982161748Scperciva fetch_check_params 2983161748Scperciva sleep `jot -r 1 0 3600` 2984161748Scperciva 2985161748Scperciva TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1 2986161748Scperciva if ! fetch_run >> ${TMPFILE} || 2987161748Scperciva ! grep -q "No updates needed" ${TMPFILE} || 2988161748Scperciva [ ${VERBOSELEVEL} = "debug" ]; then 2989161748Scperciva mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE} 2990161748Scperciva fi 2991161748Scperciva 2992161748Scperciva rm ${TMPFILE} 2993161748Scperciva} 2994161748Scperciva 2995173564Scperciva# Fetch files for upgrading to a new release. 2996173564Scpercivacmd_upgrade () { 2997173564Scperciva upgrade_check_params 2998173564Scperciva upgrade_run || exit 1 2999173564Scperciva} 3000173564Scperciva 3001161748Scperciva# Install downloaded updates. 3002161748Scpercivacmd_install () { 3003161748Scperciva install_check_params 3004161748Scperciva install_run || exit 1 3005161748Scperciva} 3006161748Scperciva 3007161748Scperciva# Rollback most recently installed updates. 3008161748Scpercivacmd_rollback () { 3009161748Scperciva rollback_check_params 3010161748Scperciva rollback_run || exit 1 3011161748Scperciva} 3012161748Scperciva 3013181142Scperciva# Compare system against a "known good" index. 3014181142Scpercivacmd_IDS () { 3015181142Scperciva IDS_check_params 3016181142Scperciva IDS_run || exit 1 3017181142Scperciva} 3018181142Scperciva 3019161748Scperciva#### Entry point 3020161748Scperciva 3021161748Scperciva# Make sure we find utilities from the base system 3022161748Scpercivaexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH} 3023161748Scperciva 3024163564Scperciva# Set LC_ALL in order to avoid problems with character ranges like [A-Z]. 3025163564Scpercivaexport LC_ALL=C 3026163564Scperciva 3027161748Scpercivaget_params $@ 3028161748Scpercivafor COMMAND in ${COMMANDS}; do 3029161748Scperciva cmd_${COMMAND} 3030161748Scpercivadone 3031