freebsd-update.sh revision 171838
1161748Scperciva#!/bin/sh 2161748Scperciva 3161748Scperciva#- 4161748Scperciva# Copyright 2004-2006 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 171838 2007-08-14 14:48:46Z 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 47161748Scperciva -s server -- Server from which to fetch updates 48161748Scperciva (default: update.FreeBSD.org) 49161748Scperciva -t address -- Mail output of cron command, if any, to address 50161748Scperciva (default: root) 51161748ScpercivaCommands: 52161748Scperciva fetch -- Fetch updates from server 53161748Scperciva cron -- Sleep rand(3600) seconds, fetch updates, and send an 54161748Scperciva email if updates were found 55161748Scperciva install -- Install downloaded updates 56161748Scperciva rollback -- Uninstall most recently installed updates 57161748ScpercivaEOF 58161748Scperciva exit 0 59161748Scperciva} 60161748Scperciva 61161748Scperciva#### Configuration processing functions 62161748Scperciva 63161748Scperciva#- 64161748Scperciva# Configuration options are set in the following order of priority: 65161748Scperciva# 1. Command line options 66161748Scperciva# 2. Configuration file options 67161748Scperciva# 3. Default options 68161748Scperciva# In addition, certain options (e.g., IgnorePaths) can be specified multiple 69161748Scperciva# times and (as long as these are all in the same place, e.g., inside the 70161748Scperciva# configuration file) they will accumulate. Finally, because the path to the 71161748Scperciva# configuration file can be specified at the command line, the entire command 72161748Scperciva# line must be processed before we start reading the configuration file. 73161748Scperciva# 74161748Scperciva# Sound like a mess? It is. Here's how we handle this: 75161748Scperciva# 1. Initialize CONFFILE and all the options to "". 76161748Scperciva# 2. Process the command line. Throw an error if a non-accumulating option 77161748Scperciva# is specified twice. 78161748Scperciva# 3. If CONFFILE is "", set CONFFILE to /etc/freebsd-update.conf . 79161748Scperciva# 4. For all the configuration options X, set X_saved to X. 80161748Scperciva# 5. Initialize all the options to "". 81161748Scperciva# 6. Read CONFFILE line by line, parsing options. 82161748Scperciva# 7. For each configuration option X, set X to X_saved iff X_saved is not "". 83161748Scperciva# 8. Repeat steps 4-7, except setting options to their default values at (6). 84161748Scperciva 85161748ScpercivaCONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE 86161748Scperciva KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED 87161748Scperciva BASEDIR VERBOSELEVEL" 88161748Scperciva 89161748Scperciva# Set all the configuration options to "". 90161748Scpercivanullconfig () { 91161748Scperciva for X in ${CONFIGOPTIONS}; do 92161748Scperciva eval ${X}="" 93161748Scperciva done 94161748Scperciva} 95161748Scperciva 96161748Scperciva# For each configuration option X, set X_saved to X. 97161748Scpercivasaveconfig () { 98161748Scperciva for X in ${CONFIGOPTIONS}; do 99161748Scperciva eval ${X}_saved=\$${X} 100161748Scperciva done 101161748Scperciva} 102161748Scperciva 103161748Scperciva# For each configuration option X, set X to X_saved if X_saved is not "". 104161748Scpercivamergeconfig () { 105161748Scperciva for X in ${CONFIGOPTIONS}; do 106161748Scperciva eval _=\$${X}_saved 107161748Scperciva if ! [ -z "${_}" ]; then 108161748Scperciva eval ${X}=\$${X}_saved 109161748Scperciva fi 110161748Scperciva done 111161748Scperciva} 112161748Scperciva 113161748Scperciva# Set the trusted keyprint. 114161748Scpercivaconfig_KeyPrint () { 115161748Scperciva if [ -z ${KEYPRINT} ]; then 116161748Scperciva KEYPRINT=$1 117161748Scperciva else 118161748Scperciva return 1 119161748Scperciva fi 120161748Scperciva} 121161748Scperciva 122161748Scperciva# Set the working directory. 123161748Scpercivaconfig_WorkDir () { 124161748Scperciva if [ -z ${WORKDIR} ]; then 125161748Scperciva WORKDIR=$1 126161748Scperciva else 127161748Scperciva return 1 128161748Scperciva fi 129161748Scperciva} 130161748Scperciva 131161748Scperciva# Set the name of the server (pool) from which to fetch updates 132161748Scpercivaconfig_ServerName () { 133161748Scperciva if [ -z ${SERVERNAME} ]; then 134161748Scperciva SERVERNAME=$1 135161748Scperciva else 136161748Scperciva return 1 137161748Scperciva fi 138161748Scperciva} 139161748Scperciva 140161748Scperciva# Set the address to which 'cron' output will be mailed. 141161748Scpercivaconfig_MailTo () { 142161748Scperciva if [ -z ${MAILTO} ]; then 143161748Scperciva MAILTO=$1 144161748Scperciva else 145161748Scperciva return 1 146161748Scperciva fi 147161748Scperciva} 148161748Scperciva 149161748Scperciva# Set whether FreeBSD Update is allowed to add files (or directories, or 150161748Scperciva# symlinks) which did not previously exist. 151161748Scpercivaconfig_AllowAdd () { 152161748Scperciva if [ -z ${ALLOWADD} ]; then 153161748Scperciva case $1 in 154161748Scperciva [Yy][Ee][Ss]) 155161748Scperciva ALLOWADD=yes 156161748Scperciva ;; 157161748Scperciva [Nn][Oo]) 158161748Scperciva ALLOWADD=no 159161748Scperciva ;; 160161748Scperciva *) 161161748Scperciva return 1 162161748Scperciva ;; 163161748Scperciva esac 164161748Scperciva else 165161748Scperciva return 1 166161748Scperciva fi 167161748Scperciva} 168161748Scperciva 169161748Scperciva# Set whether FreeBSD Update is allowed to remove files/directories/symlinks. 170161748Scpercivaconfig_AllowDelete () { 171161748Scperciva if [ -z ${ALLOWDELETE} ]; then 172161748Scperciva case $1 in 173161748Scperciva [Yy][Ee][Ss]) 174161748Scperciva ALLOWDELETE=yes 175161748Scperciva ;; 176161748Scperciva [Nn][Oo]) 177161748Scperciva ALLOWDELETE=no 178161748Scperciva ;; 179161748Scperciva *) 180161748Scperciva return 1 181161748Scperciva ;; 182161748Scperciva esac 183161748Scperciva else 184161748Scperciva return 1 185161748Scperciva fi 186161748Scperciva} 187161748Scperciva 188161748Scperciva# Set whether FreeBSD Update should keep existing inode ownership, 189161748Scperciva# permissions, and flags, in the event that they have been modified locally 190161748Scperciva# after the release. 191161748Scpercivaconfig_KeepModifiedMetadata () { 192161748Scperciva if [ -z ${KEEPMODIFIEDMETADATA} ]; then 193161748Scperciva case $1 in 194161748Scperciva [Yy][Ee][Ss]) 195161748Scperciva KEEPMODIFIEDMETADATA=yes 196161748Scperciva ;; 197161748Scperciva [Nn][Oo]) 198161748Scperciva KEEPMODIFIEDMETADATA=no 199161748Scperciva ;; 200161748Scperciva *) 201161748Scperciva return 1 202161748Scperciva ;; 203161748Scperciva esac 204161748Scperciva else 205161748Scperciva return 1 206161748Scperciva fi 207161748Scperciva} 208161748Scperciva 209161748Scperciva# Add to the list of components which should be kept updated. 210161748Scpercivaconfig_Components () { 211161748Scperciva for C in $@; do 212161748Scperciva COMPONENTS="${COMPONENTS} ${C}" 213161748Scperciva done 214161748Scperciva} 215161748Scperciva 216161748Scperciva# Add to the list of paths under which updates will be ignored. 217161748Scpercivaconfig_IgnorePaths () { 218161748Scperciva for C in $@; do 219161748Scperciva IGNOREPATHS="${IGNOREPATHS} ${C}" 220161748Scperciva done 221161748Scperciva} 222161748Scperciva 223161748Scperciva# Add to the list of paths within which updates will be performed only if the 224161748Scperciva# file on disk has not been modified locally. 225161748Scpercivaconfig_UpdateIfUnmodified () { 226161748Scperciva for C in $@; do 227161748Scperciva UPDATEIFUNMODIFIED="${UPDATEIFUNMODIFIED} ${C}" 228161748Scperciva done 229161748Scperciva} 230161748Scperciva 231161748Scperciva# Work on a FreeBSD installation mounted under $1 232161748Scpercivaconfig_BaseDir () { 233161748Scperciva if [ -z ${BASEDIR} ]; then 234161748Scperciva BASEDIR=$1 235161748Scperciva else 236161748Scperciva return 1 237161748Scperciva fi 238161748Scperciva} 239161748Scperciva 240161748Scperciva# Define what happens to output of utilities 241161748Scpercivaconfig_VerboseLevel () { 242161748Scperciva if [ -z ${VERBOSELEVEL} ]; then 243161748Scperciva case $1 in 244161748Scperciva [Dd][Ee][Bb][Uu][Gg]) 245161748Scperciva VERBOSELEVEL=debug 246161748Scperciva ;; 247161748Scperciva [Nn][Oo][Ss][Tt][Aa][Tt][Ss]) 248161748Scperciva VERBOSELEVEL=nostats 249161748Scperciva ;; 250161748Scperciva [Ss][Tt][Aa][Tt][Ss]) 251161748Scperciva VERBOSELEVEL=stats 252161748Scperciva ;; 253161748Scperciva *) 254161748Scperciva return 1 255161748Scperciva ;; 256161748Scperciva esac 257161748Scperciva else 258161748Scperciva return 1 259161748Scperciva fi 260161748Scperciva} 261161748Scperciva 262161748Scperciva# Handle one line of configuration 263161748Scpercivaconfigline () { 264161748Scperciva if [ $# -eq 0 ]; then 265161748Scperciva return 266161748Scperciva fi 267161748Scperciva 268161748Scperciva OPT=$1 269161748Scperciva shift 270161748Scperciva config_${OPT} $@ 271161748Scperciva} 272161748Scperciva 273161748Scperciva#### Parameter handling functions. 274161748Scperciva 275161748Scperciva# Initialize parameters to null, just in case they're 276161748Scperciva# set in the environment. 277161748Scpercivainit_params () { 278161748Scperciva # Configration settings 279161748Scperciva nullconfig 280161748Scperciva 281161748Scperciva # No configuration file set yet 282161748Scperciva CONFFILE="" 283161748Scperciva 284161748Scperciva # No commands specified yet 285161748Scperciva COMMANDS="" 286161748Scperciva} 287161748Scperciva 288161748Scperciva# Parse the command line 289161748Scpercivaparse_cmdline () { 290161748Scperciva while [ $# -gt 0 ]; do 291161748Scperciva case "$1" in 292161748Scperciva # Location of configuration file 293161748Scperciva -f) 294161748Scperciva if [ $# -eq 1 ]; then usage; fi 295161748Scperciva if [ ! -z "${CONFFILE}" ]; then usage; fi 296161748Scperciva shift; CONFFILE="$1" 297161748Scperciva ;; 298161748Scperciva 299161748Scperciva # Configuration file equivalents 300161748Scperciva -b) 301161748Scperciva if [ $# -eq 1 ]; then usage; fi; shift 302161748Scperciva config_BaseDir $1 || usage 303161748Scperciva ;; 304161748Scperciva -d) 305161748Scperciva if [ $# -eq 1 ]; then usage; fi; shift 306161748Scperciva config_WorkDir $1 || usage 307161748Scperciva ;; 308161748Scperciva -k) 309161748Scperciva if [ $# -eq 1 ]; then usage; fi; shift 310161748Scperciva config_KeyPrint $1 || usage 311161748Scperciva ;; 312161748Scperciva -s) 313161748Scperciva if [ $# -eq 1 ]; then usage; fi; shift 314161748Scperciva config_ServerName $1 || usage 315161748Scperciva ;; 316161748Scperciva -t) 317161748Scperciva if [ $# -eq 1 ]; then usage; fi; shift 318161748Scperciva config_MailTo $1 || usage 319161748Scperciva ;; 320161748Scperciva -v) 321161748Scperciva if [ $# -eq 1 ]; then usage; fi; shift 322161748Scperciva config_VerboseLevel $1 || usage 323161748Scperciva ;; 324161748Scperciva 325161748Scperciva # Aliases for "-v debug" and "-v nostats" 326161748Scperciva --debug) 327161748Scperciva config_VerboseLevel debug || usage 328161748Scperciva ;; 329161748Scperciva --no-stats) 330161748Scperciva config_VerboseLevel nostats || usage 331161748Scperciva ;; 332161748Scperciva 333161748Scperciva # Commands 334161748Scperciva cron | fetch | install | rollback) 335161748Scperciva COMMANDS="${COMMANDS} $1" 336161748Scperciva ;; 337161748Scperciva 338161748Scperciva # Anything else is an error 339161748Scperciva *) 340161748Scperciva usage 341161748Scperciva ;; 342161748Scperciva esac 343161748Scperciva shift 344161748Scperciva done 345161748Scperciva 346161748Scperciva # Make sure we have at least one command 347161748Scperciva if [ -z "${COMMANDS}" ]; then 348161748Scperciva usage 349161748Scperciva fi 350161748Scperciva} 351161748Scperciva 352161748Scperciva# Parse the configuration file 353161748Scpercivaparse_conffile () { 354161748Scperciva # If a configuration file was specified on the command line, check 355161748Scperciva # that it exists and is readable. 356161748Scperciva if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then 357161748Scperciva echo -n "File does not exist " 358161748Scperciva echo -n "or is not readable: " 359161748Scperciva echo ${CONFFILE} 360161748Scperciva exit 1 361161748Scperciva fi 362161748Scperciva 363161748Scperciva # If a configuration file was not specified on the command line, 364161748Scperciva # use the default configuration file path. If that default does 365161748Scperciva # not exist, give up looking for any configuration. 366161748Scperciva if [ -z "${CONFFILE}" ]; then 367161748Scperciva CONFFILE="/etc/freebsd-update.conf" 368161748Scperciva if [ ! -r "${CONFFILE}" ]; then 369161748Scperciva return 370161748Scperciva fi 371161748Scperciva fi 372161748Scperciva 373161748Scperciva # Save the configuration options specified on the command line, and 374161748Scperciva # clear all the options in preparation for reading the config file. 375161748Scperciva saveconfig 376161748Scperciva nullconfig 377161748Scperciva 378161748Scperciva # Read the configuration file. Anything after the first '#' is 379161748Scperciva # ignored, and any blank lines are ignored. 380161748Scperciva L=0 381161748Scperciva while read LINE; do 382161748Scperciva L=$(($L + 1)) 383161748Scperciva LINEX=`echo "${LINE}" | cut -f 1 -d '#'` 384161748Scperciva if ! configline ${LINEX}; then 385161748Scperciva echo "Error processing configuration file, line $L:" 386161748Scperciva echo "==> ${LINE}" 387161748Scperciva exit 1 388161748Scperciva fi 389161748Scperciva done < ${CONFFILE} 390161748Scperciva 391161748Scperciva # Merge the settings read from the configuration file with those 392161748Scperciva # provided at the command line. 393161748Scperciva mergeconfig 394161748Scperciva} 395161748Scperciva 396161748Scperciva# Provide some default parameters 397161748Scpercivadefault_params () { 398161748Scperciva # Save any parameters already configured, and clear the slate 399161748Scperciva saveconfig 400161748Scperciva nullconfig 401161748Scperciva 402161748Scperciva # Default configurations 403161748Scperciva config_WorkDir /var/db/freebsd-update 404161748Scperciva config_MailTo root 405161748Scperciva config_AllowAdd yes 406161748Scperciva config_AllowDelete yes 407161748Scperciva config_KeepModifiedMetadata yes 408161748Scperciva config_BaseDir / 409161748Scperciva config_VerboseLevel stats 410161748Scperciva 411161748Scperciva # Merge these defaults into the earlier-configured settings 412161748Scperciva mergeconfig 413161748Scperciva} 414161748Scperciva 415161748Scperciva# Set utility output filtering options, based on ${VERBOSELEVEL} 416161748Scpercivafetch_setup_verboselevel () { 417161748Scperciva case ${VERBOSELEVEL} in 418161748Scperciva debug) 419161748Scperciva QUIETREDIR="/dev/stderr" 420161748Scperciva QUIETFLAG=" " 421161748Scperciva STATSREDIR="/dev/stderr" 422161748Scperciva DDSTATS=".." 423161748Scperciva XARGST="-t" 424161748Scperciva NDEBUG=" " 425161748Scperciva ;; 426161748Scperciva nostats) 427161748Scperciva QUIETREDIR="" 428161748Scperciva QUIETFLAG="" 429161748Scperciva STATSREDIR="/dev/null" 430161748Scperciva DDSTATS=".." 431161748Scperciva XARGST="" 432161748Scperciva NDEBUG="" 433161748Scperciva ;; 434161748Scperciva stats) 435161748Scperciva QUIETREDIR="/dev/null" 436161748Scperciva QUIETFLAG="-q" 437161748Scperciva STATSREDIR="/dev/stdout" 438161748Scperciva DDSTATS="" 439161748Scperciva XARGST="" 440161748Scperciva NDEBUG="-n" 441161748Scperciva ;; 442161748Scperciva esac 443161748Scperciva} 444161748Scperciva 445161748Scperciva# Perform sanity checks and set some final parameters 446161748Scperciva# in preparation for fetching files. Figure out which 447161748Scperciva# set of updates should be downloaded: If the user is 448161748Scperciva# running *-p[0-9]+, strip off the last part; if the 449161748Scperciva# user is running -SECURITY, call it -RELEASE. Chdir 450161748Scperciva# into the working directory. 451161748Scpercivafetch_check_params () { 452161748Scperciva export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)" 453161748Scperciva 454161748Scperciva _SERVERNAME_z=\ 455161748Scperciva"SERVERNAME must be given via command line or configuration file." 456161748Scperciva _KEYPRINT_z="Key must be given via -k option or configuration file." 457161748Scperciva _KEYPRINT_bad="Invalid key fingerprint: " 458161748Scperciva _WORKDIR_bad="Directory does not exist or is not writable: " 459161748Scperciva 460161748Scperciva if [ -z "${SERVERNAME}" ]; then 461161748Scperciva echo -n "`basename $0`: " 462161748Scperciva echo "${_SERVERNAME_z}" 463161748Scperciva exit 1 464161748Scperciva fi 465161748Scperciva if [ -z "${KEYPRINT}" ]; then 466161748Scperciva echo -n "`basename $0`: " 467161748Scperciva echo "${_KEYPRINT_z}" 468161748Scperciva exit 1 469161748Scperciva fi 470161748Scperciva if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then 471161748Scperciva echo -n "`basename $0`: " 472161748Scperciva echo -n "${_KEYPRINT_bad}" 473161748Scperciva echo ${KEYPRINT} 474161748Scperciva exit 1 475161748Scperciva fi 476161748Scperciva if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then 477161748Scperciva echo -n "`basename $0`: " 478161748Scperciva echo -n "${_WORKDIR_bad}" 479161748Scperciva echo ${WORKDIR} 480161748Scperciva exit 1 481161748Scperciva fi 482161748Scperciva cd ${WORKDIR} || exit 1 483161748Scperciva 484161748Scperciva # Generate release number. The s/SECURITY/RELEASE/ bit exists 485161748Scperciva # to provide an upgrade path for FreeBSD Update 1.x users, since 486161748Scperciva # the kernels provided by FreeBSD Update 1.x are always labelled 487161748Scperciva # as X.Y-SECURITY. 488161748Scperciva RELNUM=`uname -r | 489161748Scperciva sed -E 's,-p[0-9]+,,' | 490161748Scperciva sed -E 's,-SECURITY,-RELEASE,'` 491161748Scperciva ARCH=`uname -m` 492161748Scperciva FETCHDIR=${RELNUM}/${ARCH} 493161748Scperciva 494161748Scperciva # Figure out what directory contains the running kernel 495161748Scperciva BOOTFILE=`sysctl -n kern.bootfile` 496161748Scperciva KERNELDIR=${BOOTFILE%/kernel} 497161748Scperciva if ! [ -d ${KERNELDIR} ]; then 498161748Scperciva echo "Cannot identify running kernel" 499161748Scperciva exit 1 500161748Scperciva fi 501161748Scperciva 502167189Scperciva # Figure out what kernel configuration is running. We start with 503167189Scperciva # the output of `uname -i`, and then make the following adjustments: 504167189Scperciva # 1. Replace "SMP-GENERIC" with "SMP". Why the SMP kernel config 505167189Scperciva # file says "ident SMP-GENERIC", I don't know... 506167189Scperciva # 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64" 507167189Scperciva # _and_ `sysctl kern.version` contains a line which ends "/SMP", then 508167189Scperciva # we're running an SMP kernel. This mis-identification is a bug 509167189Scperciva # which was fixed in 6.2-STABLE. 510167189Scperciva KERNCONF=`uname -i` 511167189Scperciva if [ ${KERNCONF} = "SMP-GENERIC" ]; then 512167189Scperciva KERNCONF=SMP 513167189Scperciva fi 514167189Scperciva if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then 515167189Scperciva if sysctl kern.version | grep -qE '/SMP$'; then 516167189Scperciva KERNCONF=SMP 517167189Scperciva fi 518167189Scperciva fi 519167189Scperciva 520161748Scperciva # Define some paths 521161748Scperciva BSPATCH=/usr/bin/bspatch 522161748Scperciva SHA256=/sbin/sha256 523161748Scperciva PHTTPGET=/usr/libexec/phttpget 524161748Scperciva 525161748Scperciva # Set up variables relating to VERBOSELEVEL 526161748Scperciva fetch_setup_verboselevel 527161748Scperciva 528161748Scperciva # Construct a unique name from ${BASEDIR} 529161748Scperciva BDHASH=`echo ${BASEDIR} | sha256 -q` 530161748Scperciva} 531161748Scperciva 532161748Scperciva# Perform sanity checks and set some final parameters in 533161748Scperciva# preparation for installing updates. 534161748Scpercivainstall_check_params () { 535161748Scperciva # Check that we are root. All sorts of things won't work otherwise. 536161748Scperciva if [ `id -u` != 0 ]; then 537161748Scperciva echo "You must be root to run this." 538161748Scperciva exit 1 539161748Scperciva fi 540161748Scperciva 541161748Scperciva # Check that we have a working directory 542161748Scperciva _WORKDIR_bad="Directory does not exist or is not writable: " 543161748Scperciva if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then 544161748Scperciva echo -n "`basename $0`: " 545161748Scperciva echo -n "${_WORKDIR_bad}" 546161748Scperciva echo ${WORKDIR} 547161748Scperciva exit 1 548161748Scperciva fi 549161748Scperciva cd ${WORKDIR} || exit 1 550161748Scperciva 551161748Scperciva # Construct a unique name from ${BASEDIR} 552161748Scperciva BDHASH=`echo ${BASEDIR} | sha256 -q` 553161748Scperciva 554161748Scperciva # Check that we have updates ready to install 555161748Scperciva if ! [ -L ${BDHASH}-install ]; then 556161748Scperciva echo "No updates are available to install." 557161748Scperciva echo "Run '$0 fetch' first." 558161748Scperciva exit 1 559161748Scperciva fi 560161748Scperciva if ! [ -f ${BDHASH}-install/INDEX-OLD ] || 561161748Scperciva ! [ -f ${BDHASH}-install/INDEX-NEW ]; then 562161748Scperciva echo "Update manifest is corrupt -- this should never happen." 563161748Scperciva echo "Re-run '$0 fetch'." 564161748Scperciva exit 1 565161748Scperciva fi 566161748Scperciva} 567161748Scperciva 568161748Scperciva# Perform sanity checks and set some final parameters in 569161748Scperciva# preparation for UNinstalling updates. 570161748Scpercivarollback_check_params () { 571161748Scperciva # Check that we are root. All sorts of things won't work otherwise. 572161748Scperciva if [ `id -u` != 0 ]; then 573161748Scperciva echo "You must be root to run this." 574161748Scperciva exit 1 575161748Scperciva fi 576161748Scperciva 577161748Scperciva # Check that we have a working directory 578161748Scperciva _WORKDIR_bad="Directory does not exist or is not writable: " 579161748Scperciva if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then 580161748Scperciva echo -n "`basename $0`: " 581161748Scperciva echo -n "${_WORKDIR_bad}" 582161748Scperciva echo ${WORKDIR} 583161748Scperciva exit 1 584161748Scperciva fi 585161748Scperciva cd ${WORKDIR} || exit 1 586161748Scperciva 587161748Scperciva # Construct a unique name from ${BASEDIR} 588161748Scperciva BDHASH=`echo ${BASEDIR} | sha256 -q` 589161748Scperciva 590161748Scperciva # Check that we have updates ready to rollback 591161748Scperciva if ! [ -L ${BDHASH}-rollback ]; then 592161748Scperciva echo "No rollback directory found." 593161748Scperciva exit 1 594161748Scperciva fi 595161748Scperciva if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] || 596161748Scperciva ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then 597161748Scperciva echo "Update manifest is corrupt -- this should never happen." 598161748Scperciva exit 1 599161748Scperciva fi 600161748Scperciva} 601161748Scperciva 602161748Scperciva#### Core functionality -- the actual work gets done here 603161748Scperciva 604161748Scperciva# Use an SRV query to pick a server. If the SRV query doesn't provide 605161748Scperciva# a useful answer, use the server name specified by the user. 606161748Scperciva# Put another way... look up _http._tcp.${SERVERNAME} and pick a server 607161748Scperciva# from that; or if no servers are returned, use ${SERVERNAME}. 608161748Scperciva# This allows a user to specify "portsnap.freebsd.org" (in which case 609161748Scperciva# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org" 610161748Scperciva# (in which case portsnap will use that particular server, since there 611161748Scperciva# won't be an SRV entry for that name). 612161748Scperciva# 613161748Scperciva# We ignore the Port field, since we are always going to use port 80. 614161748Scperciva 615161748Scperciva# Fetch the mirror list, but do not pick a mirror yet. Returns 1 if 616161748Scperciva# no mirrors are available for any reason. 617161748Scpercivafetch_pick_server_init () { 618161748Scperciva : > serverlist_tried 619161748Scperciva 620161748Scperciva# Check that host(1) exists (i.e., that the system wasn't built with the 621161748Scperciva# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist. 622161748Scperciva if ! which -s host; then 623161748Scperciva : > serverlist_full 624161748Scperciva return 1 625161748Scperciva fi 626161748Scperciva 627161748Scperciva echo -n "Looking up ${SERVERNAME} mirrors... " 628161748Scperciva 629161748Scperciva# Issue the SRV query and pull out the Priority, Weight, and Target fields. 630161748Scperciva# BIND 9 prints "$name has SRV record ..." while BIND 8 prints 631161748Scperciva# "$name server selection ..."; we allow either format. 632161748Scperciva MLIST="_http._tcp.${SERVERNAME}" 633161748Scperciva host -t srv "${MLIST}" | 634161748Scperciva sed -nE "s/${MLIST} (has SRV record|server selection) //p" | 635161748Scperciva cut -f 1,2,4 -d ' ' | 636161748Scperciva sed -e 's/\.$//' | 637161748Scperciva sort > serverlist_full 638161748Scperciva 639161748Scperciva# If no records, give up -- we'll just use the server name we were given. 640161748Scperciva if [ `wc -l < serverlist_full` -eq 0 ]; then 641161748Scperciva echo "none found." 642161748Scperciva return 1 643161748Scperciva fi 644161748Scperciva 645161748Scperciva# Report how many mirrors we found. 646161748Scperciva echo `wc -l < serverlist_full` "mirrors found." 647161748Scperciva 648161748Scperciva# Generate a random seed for use in picking mirrors. If HTTP_PROXY 649161748Scperciva# is set, this will be used to generate the seed; otherwise, the seed 650161748Scperciva# will be random. 651161748Scperciva if [ -n "${HTTP_PROXY}${http_proxy}" ]; then 652161748Scperciva RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" | 653161748Scperciva tr -d 'a-f' | 654161748Scperciva cut -c 1-9` 655161748Scperciva else 656161748Scperciva RANDVALUE=`jot -r 1 0 999999999` 657161748Scperciva fi 658161748Scperciva} 659161748Scperciva 660161748Scperciva# Pick a mirror. Returns 1 if we have run out of mirrors to try. 661161748Scpercivafetch_pick_server () { 662161748Scperciva# Generate a list of not-yet-tried mirrors 663161748Scperciva sort serverlist_tried | 664161748Scperciva comm -23 serverlist_full - > serverlist 665161748Scperciva 666161748Scperciva# Have we run out of mirrors? 667161748Scperciva if [ `wc -l < serverlist` -eq 0 ]; then 668161748Scperciva echo "No mirrors remaining, giving up." 669161748Scperciva return 1 670161748Scperciva fi 671161748Scperciva 672161748Scperciva# Find the highest priority level (lowest numeric value). 673161748Scperciva SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1` 674161748Scperciva 675161748Scperciva# Add up the weights of the response lines at that priority level. 676161748Scperciva SRV_WSUM=0; 677161748Scperciva while read X; do 678161748Scperciva case "$X" in 679161748Scperciva ${SRV_PRIORITY}\ *) 680161748Scperciva SRV_W=`echo $X | cut -f 2 -d ' '` 681161748Scperciva SRV_WSUM=$(($SRV_WSUM + $SRV_W)) 682161748Scperciva ;; 683161748Scperciva esac 684161748Scperciva done < serverlist 685161748Scperciva 686161748Scperciva# If all the weights are 0, pretend that they are all 1 instead. 687161748Scperciva if [ ${SRV_WSUM} -eq 0 ]; then 688161748Scperciva SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l` 689161748Scperciva SRV_W_ADD=1 690161748Scperciva else 691161748Scperciva SRV_W_ADD=0 692161748Scperciva fi 693161748Scperciva 694161748Scperciva# Pick a value between 0 and the sum of the weights - 1 695161748Scperciva SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}` 696161748Scperciva 697161748Scperciva# Read through the list of mirrors and set SERVERNAME. Write the line 698161748Scperciva# corresponding to the mirror we selected into serverlist_tried so that 699161748Scperciva# we won't try it again. 700161748Scperciva while read X; do 701161748Scperciva case "$X" in 702161748Scperciva ${SRV_PRIORITY}\ *) 703161748Scperciva SRV_W=`echo $X | cut -f 2 -d ' '` 704161748Scperciva SRV_W=$(($SRV_W + $SRV_W_ADD)) 705161748Scperciva if [ $SRV_RND -lt $SRV_W ]; then 706161748Scperciva SERVERNAME=`echo $X | cut -f 3 -d ' '` 707161748Scperciva echo "$X" >> serverlist_tried 708161748Scperciva break 709161748Scperciva else 710161748Scperciva SRV_RND=$(($SRV_RND - $SRV_W)) 711161748Scperciva fi 712161748Scperciva ;; 713161748Scperciva esac 714161748Scperciva done < serverlist 715161748Scperciva} 716161748Scperciva 717161748Scperciva# Take a list of ${oldhash}|${newhash} and output a list of needed patches, 718161748Scperciva# i.e., those for which we have ${oldhash} and don't have ${newhash}. 719161748Scpercivafetch_make_patchlist () { 720161748Scperciva grep -vE "^([0-9a-f]{64})\|\1$" | 721161748Scperciva tr '|' ' ' | 722161748Scperciva while read X Y; do 723161748Scperciva if [ -f "files/${Y}.gz" ] || 724161748Scperciva [ ! -f "files/${X}.gz" ]; then 725161748Scperciva continue 726161748Scperciva fi 727161748Scperciva echo "${X}|${Y}" 728161748Scperciva done | uniq 729161748Scperciva} 730161748Scperciva 731161748Scperciva# Print user-friendly progress statistics 732161748Scpercivafetch_progress () { 733161748Scperciva LNC=0 734161748Scperciva while read x; do 735161748Scperciva LNC=$(($LNC + 1)) 736161748Scperciva if [ $(($LNC % 10)) = 0 ]; then 737161748Scperciva echo -n $LNC 738161748Scperciva elif [ $(($LNC % 2)) = 0 ]; then 739161748Scperciva echo -n . 740161748Scperciva fi 741161748Scperciva done 742161748Scperciva echo -n " " 743161748Scperciva} 744161748Scperciva 745161748Scperciva# Initialize the working directory 746161748Scpercivaworkdir_init () { 747161748Scperciva mkdir -p files 748161748Scperciva touch tINDEX.present 749161748Scperciva} 750161748Scperciva 751161748Scperciva# Check that we have a public key with an appropriate hash, or 752161748Scperciva# fetch the key if it doesn't exist. Returns 1 if the key has 753161748Scperciva# not yet been fetched. 754161748Scpercivafetch_key () { 755161748Scperciva if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then 756161748Scperciva return 0 757161748Scperciva fi 758161748Scperciva 759161748Scperciva echo -n "Fetching public key from ${SERVERNAME}... " 760161748Scperciva rm -f pub.ssl 761161748Scperciva fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \ 762161748Scperciva 2>${QUIETREDIR} || true 763161748Scperciva if ! [ -r pub.ssl ]; then 764161748Scperciva echo "failed." 765161748Scperciva return 1 766161748Scperciva fi 767161748Scperciva if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then 768161748Scperciva echo "key has incorrect hash." 769161748Scperciva rm -f pub.ssl 770161748Scperciva return 1 771161748Scperciva fi 772161748Scperciva echo "done." 773161748Scperciva} 774161748Scperciva 775161748Scperciva# Fetch metadata signature, aka "tag". 776161748Scpercivafetch_tag () { 777161748Scperciva echo ${NDEBUG} "Fetching metadata signature from ${SERVERNAME}... " 778161748Scperciva rm -f latest.ssl 779161748Scperciva fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl \ 780161748Scperciva 2>${QUIETREDIR} || true 781161748Scperciva if ! [ -r latest.ssl ]; then 782161748Scperciva echo "failed." 783161748Scperciva return 1 784161748Scperciva fi 785161748Scperciva 786161748Scperciva openssl rsautl -pubin -inkey pub.ssl -verify \ 787161748Scperciva < latest.ssl > tag.new 2>${QUIETREDIR} || true 788161748Scperciva rm latest.ssl 789161748Scperciva 790161748Scperciva if ! [ `wc -l < tag.new` = 1 ] || 791161748Scperciva ! grep -qE \ 792161748Scperciva "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \ 793161748Scperciva tag.new; then 794161748Scperciva echo "invalid signature." 795161748Scperciva return 1 796161748Scperciva fi 797161748Scperciva 798161748Scperciva echo "done." 799161748Scperciva 800161748Scperciva RELPATCHNUM=`cut -f 4 -d '|' < tag.new` 801161748Scperciva TINDEXHASH=`cut -f 5 -d '|' < tag.new` 802161748Scperciva EOLTIME=`cut -f 6 -d '|' < tag.new` 803161748Scperciva} 804161748Scperciva 805161748Scperciva# Sanity-check the patch number in a tag, to make sure that we're not 806161748Scperciva# going to "update" backwards and to prevent replay attacks. 807161748Scpercivafetch_tagsanity () { 808161748Scperciva # Check that we're not going to move from -pX to -pY with Y < X. 809161748Scperciva RELPX=`uname -r | sed -E 's,.*-,,'` 810161748Scperciva if echo ${RELPX} | grep -qE '^p[0-9]+$'; then 811161748Scperciva RELPX=`echo ${RELPX} | cut -c 2-` 812161748Scperciva else 813161748Scperciva RELPX=0 814161748Scperciva fi 815161748Scperciva if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then 816161748Scperciva echo 817161748Scperciva echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})" 818161748Scperciva echo " appear older than what" 819161748Scperciva echo "we are currently running (`uname -r`)!" 820161748Scperciva echo "Cowardly refusing to proceed any further." 821161748Scperciva return 1 822161748Scperciva fi 823161748Scperciva 824161748Scperciva # If "tag" exists and corresponds to ${RELNUM}, make sure that 825161748Scperciva # it contains a patch number <= RELPATCHNUM, in order to protect 826161748Scperciva # against rollback (replay) attacks. 827161748Scperciva if [ -f tag ] && 828161748Scperciva grep -qE \ 829161748Scperciva "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \ 830161748Scperciva tag; then 831161748Scperciva LASTRELPATCHNUM=`cut -f 4 -d '|' < tag` 832161748Scperciva 833161748Scperciva if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then 834161748Scperciva echo 835161748Scperciva echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})" 836161748Scperciva echo " are older than the" 837161748Scperciva echo -n "most recently seen updates" 838161748Scperciva echo " (${RELNUM}-p${LASTRELPATCHNUM})." 839161748Scperciva echo "Cowardly refusing to proceed any further." 840161748Scperciva return 1 841161748Scperciva fi 842161748Scperciva fi 843161748Scperciva} 844161748Scperciva 845161748Scperciva# Fetch metadata index file 846161748Scpercivafetch_metadata_index () { 847161748Scperciva echo ${NDEBUG} "Fetching metadata index... " 848161748Scperciva rm -f ${TINDEXHASH} 849161748Scperciva fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH} 850161748Scperciva 2>${QUIETREDIR} 851161748Scperciva if ! [ -f ${TINDEXHASH} ]; then 852161748Scperciva echo "failed." 853161748Scperciva return 1 854161748Scperciva fi 855161748Scperciva if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then 856161748Scperciva echo "update metadata index corrupt." 857161748Scperciva return 1 858161748Scperciva fi 859161748Scperciva echo "done." 860161748Scperciva} 861161748Scperciva 862161748Scperciva# Print an error message about signed metadata being bogus. 863161748Scpercivafetch_metadata_bogus () { 864161748Scperciva echo 865161748Scperciva echo "The update metadata$1 is correctly signed, but" 866161748Scperciva echo "failed an integrity check." 867161748Scperciva echo "Cowardly refusing to proceed any further." 868161748Scperciva return 1 869161748Scperciva} 870161748Scperciva 871161748Scperciva# Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH} 872161748Scperciva# with the lines not named in $@ from tINDEX.present (if that file exists). 873161748Scpercivafetch_metadata_index_merge () { 874161748Scperciva for METAFILE in $@; do 875161748Scperciva if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l` \ 876161748Scperciva -ne 1 ]; then 877161748Scperciva fetch_metadata_bogus " index" 878161748Scperciva return 1 879161748Scperciva fi 880161748Scperciva 881161748Scperciva grep -E "${METAFILE}\|" ${TINDEXHASH} 882161748Scperciva done | 883161748Scperciva sort > tINDEX.wanted 884161748Scperciva 885161748Scperciva if [ -f tINDEX.present ]; then 886161748Scperciva join -t '|' -v 2 tINDEX.wanted tINDEX.present | 887161748Scperciva sort -m - tINDEX.wanted > tINDEX.new 888161748Scperciva rm tINDEX.wanted 889161748Scperciva else 890161748Scperciva mv tINDEX.wanted tINDEX.new 891161748Scperciva fi 892161748Scperciva} 893161748Scperciva 894161748Scperciva# Sanity check all the lines of tINDEX.new. Even if more metadata lines 895161748Scperciva# are added by future versions of the server, this won't cause problems, 896161748Scperciva# since the only lines which appear in tINDEX.new are the ones which we 897161748Scperciva# specifically grepped out of ${TINDEXHASH}. 898161748Scpercivafetch_metadata_index_sanity () { 899161748Scperciva if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then 900161748Scperciva fetch_metadata_bogus " index" 901161748Scperciva return 1 902161748Scperciva fi 903161748Scperciva} 904161748Scperciva 905161748Scperciva# Sanity check the metadata file $1. 906161748Scpercivafetch_metadata_sanity () { 907161748Scperciva # Some aliases to save space later: ${P} is a character which can 908161748Scperciva # appear in a path; ${M} is the four numeric metadata fields; and 909161748Scperciva # ${H} is a sha256 hash. 910161748Scperciva P="[-+./:=_[[:alnum:]]" 911161748Scperciva M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+" 912161748Scperciva H="[0-9a-f]{64}" 913161748Scperciva 914161748Scperciva # Check that the first four fields make sense. 915161748Scperciva if gunzip -c < files/$1.gz | 916161748Scperciva grep -qvE "^[a-z]+\|[0-9a-z]+\|${P}+\|[fdL-]\|"; then 917161748Scperciva fetch_metadata_bogus "" 918161748Scperciva return 1 919161748Scperciva fi 920161748Scperciva 921161748Scperciva # Remove the first three fields. 922161748Scperciva gunzip -c < files/$1.gz | 923161748Scperciva cut -f 4- -d '|' > sanitycheck.tmp 924161748Scperciva 925161748Scperciva # Sanity check entries with type 'f' 926161748Scperciva if grep -E '^f' sanitycheck.tmp | 927161748Scperciva grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then 928161748Scperciva fetch_metadata_bogus "" 929161748Scperciva return 1 930161748Scperciva fi 931161748Scperciva 932161748Scperciva # Sanity check entries with type 'd' 933161748Scperciva if grep -E '^d' sanitycheck.tmp | 934161748Scperciva grep -qvE "^d\|${M}\|\|\$"; then 935161748Scperciva fetch_metadata_bogus "" 936161748Scperciva return 1 937161748Scperciva fi 938161748Scperciva 939161748Scperciva # Sanity check entries with type 'L' 940161748Scperciva if grep -E '^L' sanitycheck.tmp | 941161748Scperciva grep -qvE "^L\|${M}\|${P}*\|\$"; then 942161748Scperciva fetch_metadata_bogus "" 943161748Scperciva return 1 944161748Scperciva fi 945161748Scperciva 946161748Scperciva # Sanity check entries with type '-' 947161748Scperciva if grep -E '^-' sanitycheck.tmp | 948161748Scperciva grep -qvE "^-\|\|\|\|\|\|"; then 949161748Scperciva fetch_metadata_bogus "" 950161748Scperciva return 1 951161748Scperciva fi 952161748Scperciva 953161748Scperciva # Clean up 954161748Scperciva rm sanitycheck.tmp 955161748Scperciva} 956161748Scperciva 957161748Scperciva# Fetch the metadata index and metadata files listed in $@, 958161748Scperciva# taking advantage of metadata patches where possible. 959161748Scpercivafetch_metadata () { 960161748Scperciva fetch_metadata_index || return 1 961161748Scperciva fetch_metadata_index_merge $@ || return 1 962161748Scperciva fetch_metadata_index_sanity || return 1 963161748Scperciva 964161748Scperciva # Generate a list of wanted metadata patches 965161748Scperciva join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new | 966161748Scperciva fetch_make_patchlist > patchlist 967161748Scperciva 968161748Scperciva if [ -s patchlist ]; then 969161748Scperciva # Attempt to fetch metadata patches 970161748Scperciva echo -n "Fetching `wc -l < patchlist | tr -d ' '` " 971161748Scperciva echo ${NDEBUG} "metadata patches.${DDSTATS}" 972161748Scperciva tr '|' '-' < patchlist | 973161748Scperciva lam -s "${FETCHDIR}/tp/" - -s ".gz" | 974161748Scperciva xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 975161748Scperciva 2>${STATSREDIR} | fetch_progress 976161748Scperciva echo "done." 977161748Scperciva 978161748Scperciva # Attempt to apply metadata patches 979161748Scperciva echo -n "Applying metadata patches... " 980161748Scperciva tr '|' ' ' < patchlist | 981161748Scperciva while read X Y; do 982161748Scperciva if [ ! -f "${X}-${Y}.gz" ]; then continue; fi 983161748Scperciva gunzip -c < ${X}-${Y}.gz > diff 984161748Scperciva gunzip -c < files/${X}.gz > diff-OLD 985161748Scperciva 986161748Scperciva # Figure out which lines are being added and removed 987161748Scperciva grep -E '^-' diff | 988161748Scperciva cut -c 2- | 989161748Scperciva while read PREFIX; do 990161748Scperciva look "${PREFIX}" diff-OLD 991161748Scperciva done | 992161748Scperciva sort > diff-rm 993161748Scperciva grep -E '^\+' diff | 994161748Scperciva cut -c 2- > diff-add 995161748Scperciva 996161748Scperciva # Generate the new file 997161748Scperciva comm -23 diff-OLD diff-rm | 998161748Scperciva sort - diff-add > diff-NEW 999161748Scperciva 1000161748Scperciva if [ `${SHA256} -q diff-NEW` = ${Y} ]; then 1001161748Scperciva mv diff-NEW files/${Y} 1002161748Scperciva gzip -n files/${Y} 1003161748Scperciva else 1004161748Scperciva mv diff-NEW ${Y}.bad 1005161748Scperciva fi 1006161748Scperciva rm -f ${X}-${Y}.gz diff 1007161748Scperciva rm -f diff-OLD diff-NEW diff-add diff-rm 1008161748Scperciva done 2>${QUIETREDIR} 1009161748Scperciva echo "done." 1010161748Scperciva fi 1011161748Scperciva 1012161748Scperciva # Update metadata without patches 1013161748Scperciva cut -f 2 -d '|' < tINDEX.new | 1014161748Scperciva while read Y; do 1015161748Scperciva if [ ! -f "files/${Y}.gz" ]; then 1016161748Scperciva echo ${Y}; 1017161748Scperciva fi 1018164600Scperciva done | 1019164600Scperciva sort -u > filelist 1020161748Scperciva 1021161748Scperciva if [ -s filelist ]; then 1022161748Scperciva echo -n "Fetching `wc -l < filelist | tr -d ' '` " 1023161748Scperciva echo ${NDEBUG} "metadata files... " 1024161748Scperciva lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist | 1025161748Scperciva xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 1026161748Scperciva 2>${QUIETREDIR} 1027161748Scperciva 1028161748Scperciva while read Y; do 1029161748Scperciva if ! [ -f ${Y}.gz ]; then 1030161748Scperciva echo "failed." 1031161748Scperciva return 1 1032161748Scperciva fi 1033161748Scperciva if [ `gunzip -c < ${Y}.gz | 1034161748Scperciva ${SHA256} -q` = ${Y} ]; then 1035161748Scperciva mv ${Y}.gz files/${Y}.gz 1036161748Scperciva else 1037161748Scperciva echo "metadata is corrupt." 1038161748Scperciva return 1 1039161748Scperciva fi 1040161748Scperciva done < filelist 1041161748Scperciva echo "done." 1042161748Scperciva fi 1043161748Scperciva 1044161748Scperciva# Sanity-check the metadata files. 1045161748Scperciva cut -f 2 -d '|' tINDEX.new > filelist 1046161748Scperciva while read X; do 1047161748Scperciva fetch_metadata_sanity ${X} || return 1 1048161748Scperciva done < filelist 1049161748Scperciva 1050161748Scperciva# Remove files which are no longer needed 1051161748Scperciva cut -f 2 -d '|' tINDEX.present | 1052161748Scperciva sort > oldfiles 1053161748Scperciva cut -f 2 -d '|' tINDEX.new | 1054161748Scperciva sort | 1055161748Scperciva comm -13 - oldfiles | 1056161748Scperciva lam -s "files/" - -s ".gz" | 1057161748Scperciva xargs rm -f 1058161748Scperciva rm patchlist filelist oldfiles 1059161748Scperciva rm ${TINDEXHASH} 1060161748Scperciva 1061161748Scperciva# We're done! 1062161748Scperciva mv tINDEX.new tINDEX.present 1063161748Scperciva mv tag.new tag 1064161748Scperciva 1065161748Scperciva return 0 1066161748Scperciva} 1067161748Scperciva 1068161869Scperciva# Generate a filtered version of the metadata file $1 from the downloaded 1069161748Scperciva# file, by fishing out the lines corresponding to components we're trying 1070161748Scperciva# to keep updated, and then removing lines corresponding to paths we want 1071161748Scperciva# to ignore. 1072161748Scpercivafetch_filter_metadata () { 1073161748Scperciva METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'` 1074161748Scperciva gunzip -c < files/${METAHASH}.gz > $1.all 1075161748Scperciva 1076161748Scperciva # Fish out the lines belonging to components we care about. 1077161748Scperciva # Canonicalize directory names by removing any trailing / in 1078161748Scperciva # order to avoid listing directories multiple times if they 1079161748Scperciva # belong to multiple components. Turning "/" into "" doesn't 1080161748Scperciva # matter, since we add a leading "/" when we use paths later. 1081161748Scperciva for C in ${COMPONENTS}; do 1082161748Scperciva look "`echo ${C} | tr '/' '|'`|" $1.all 1083161748Scperciva done | 1084161748Scperciva cut -f 3- -d '|' | 1085161748Scperciva sed -e 's,/|d|,|d|,' | 1086161748Scperciva sort -u > $1.tmp 1087161748Scperciva 1088161748Scperciva # Figure out which lines to ignore and remove them. 1089161748Scperciva for X in ${IGNOREPATHS}; do 1090161748Scperciva grep -E "^${X}" $1.tmp 1091161748Scperciva done | 1092161748Scperciva sort -u | 1093161748Scperciva comm -13 - $1.tmp > $1 1094161748Scperciva 1095161748Scperciva # Remove temporary files. 1096161748Scperciva rm $1.all $1.tmp 1097161748Scperciva} 1098161748Scperciva 1099167189Scperciva# Filter the metadata file $1 by adding lines with "/boot/${KERNCONF}" 1100164600Scperciva# replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the 1101167189Scperciva# trailing "/kernel"); and if "/boot/${KERNCONF}" does not exist, remove 1102164600Scperciva# the original lines which start with that. 1103164600Scperciva# Put another way: Deal with the fact that the FOO kernel is sometimes 1104164600Scperciva# installed in /boot/FOO/ and is sometimes installed elsewhere. 1105161748Scpercivafetch_filter_kernel_names () { 1106164600Scperciva 1107164600Scperciva grep ^/boot/${KERNCONF} $1 | 1108164600Scperciva sed -e "s,/boot/${KERNCONF},${KERNELDIR},g" | 1109161748Scperciva sort - $1 > $1.tmp 1110161748Scperciva mv $1.tmp $1 1111164600Scperciva 1112164600Scperciva if ! [ -d /boot/${KERNCONF} ]; then 1113164600Scperciva grep -v ^/boot/${KERNCONF} $1 > $1.tmp 1114164600Scperciva mv $1.tmp $1 1115164600Scperciva fi 1116161748Scperciva} 1117161748Scperciva 1118161748Scperciva# For all paths appearing in $1 or $3, inspect the system 1119161748Scperciva# and generate $2 describing what is currently installed. 1120161748Scpercivafetch_inspect_system () { 1121161748Scperciva # No errors yet... 1122161748Scperciva rm -f .err 1123161748Scperciva 1124161748Scperciva # Tell the user why his disk is suddenly making lots of noise 1125161748Scperciva echo -n "Inspecting system... " 1126161748Scperciva 1127161748Scperciva # Generate list of files to inspect 1128161748Scperciva cat $1 $3 | 1129161748Scperciva cut -f 1 -d '|' | 1130161748Scperciva sort -u > filelist 1131161748Scperciva 1132161748Scperciva # Examine each file and output lines of the form 1133161748Scperciva # /path/to/file|type|device-inum|user|group|perm|flags|value 1134161748Scperciva # sorted by device and inode number. 1135161748Scperciva while read F; do 1136161748Scperciva # If the symlink/file/directory does not exist, record this. 1137161748Scperciva if ! [ -e ${BASEDIR}/${F} ]; then 1138161748Scperciva echo "${F}|-||||||" 1139161748Scperciva continue 1140161748Scperciva fi 1141161748Scperciva if ! [ -r ${BASEDIR}/${F} ]; then 1142161748Scperciva echo "Cannot read file: ${BASEDIR}/${F}" \ 1143161748Scperciva >/dev/stderr 1144161748Scperciva touch .err 1145161748Scperciva return 1 1146161748Scperciva fi 1147161748Scperciva 1148161748Scperciva # Otherwise, output an index line. 1149161748Scperciva if [ -L ${BASEDIR}/${F} ]; then 1150161748Scperciva echo -n "${F}|L|" 1151161748Scperciva stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; 1152161748Scperciva readlink ${BASEDIR}/${F}; 1153161748Scperciva elif [ -f ${BASEDIR}/${F} ]; then 1154161748Scperciva echo -n "${F}|f|" 1155161748Scperciva stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; 1156161748Scperciva sha256 -q ${BASEDIR}/${F}; 1157161748Scperciva elif [ -d ${BASEDIR}/${F} ]; then 1158161748Scperciva echo -n "${F}|d|" 1159161748Scperciva stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; 1160161748Scperciva else 1161161748Scperciva echo "Unknown file type: ${BASEDIR}/${F}" \ 1162161748Scperciva >/dev/stderr 1163161748Scperciva touch .err 1164161748Scperciva return 1 1165161748Scperciva fi 1166161748Scperciva done < filelist | 1167161748Scperciva sort -k 3,3 -t '|' > $2.tmp 1168161748Scperciva rm filelist 1169161748Scperciva 1170161748Scperciva # Check if an error occured during system inspection 1171161748Scperciva if [ -f .err ]; then 1172161748Scperciva return 1 1173161748Scperciva fi 1174161748Scperciva 1175161748Scperciva # Convert to the form 1176161748Scperciva # /path/to/file|type|user|group|perm|flags|value|hlink 1177161748Scperciva # by resolving identical device and inode numbers into hard links. 1178161748Scperciva cut -f 1,3 -d '|' $2.tmp | 1179161748Scperciva sort -k 1,1 -t '|' | 1180161748Scperciva sort -s -u -k 2,2 -t '|' | 1181161748Scperciva join -1 2 -2 3 -t '|' - $2.tmp | 1182161748Scperciva awk -F \| -v OFS=\| \ 1183161748Scperciva '{ 1184161748Scperciva if (($2 == $3) || ($4 == "-")) 1185161748Scperciva print $3,$4,$5,$6,$7,$8,$9,"" 1186161748Scperciva else 1187161748Scperciva print $3,$4,$5,$6,$7,$8,$9,$2 1188161748Scperciva }' | 1189161748Scperciva sort > $2 1190161748Scperciva rm $2.tmp 1191161748Scperciva 1192161748Scperciva # We're finished looking around 1193161748Scperciva echo "done." 1194161748Scperciva} 1195161748Scperciva 1196161748Scperciva# For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123] 1197161748Scperciva# which correspond to lines in $2 with hashes not matching $1 or $3. For 1198161748Scperciva# entries in $2 marked "not present" (aka. type -), remove lines from $[123] 1199161748Scperciva# unless there is a corresponding entry in $1. 1200161748Scpercivafetch_filter_unmodified_notpresent () { 1201161748Scperciva # Figure out which lines of $1 and $3 correspond to bits which 1202161748Scperciva # should only be updated if they haven't changed, and fish out 1203161748Scperciva # the (path, type, value) tuples. 1204161748Scperciva # NOTE: We don't consider a file to be "modified" if it matches 1205161748Scperciva # the hash from $3. 1206161748Scperciva for X in ${UPDATEIFUNMODIFIED}; do 1207161748Scperciva grep -E "^${X}" $1 1208161748Scperciva grep -E "^${X}" $3 1209161748Scperciva done | 1210161748Scperciva cut -f 1,2,7 -d '|' | 1211161748Scperciva sort > $1-values 1212161748Scperciva 1213161748Scperciva # Do the same for $2. 1214161748Scperciva for X in ${UPDATEIFUNMODIFIED}; do 1215161748Scperciva grep -E "^${X}" $2 1216161748Scperciva done | 1217161748Scperciva cut -f 1,2,7 -d '|' | 1218161748Scperciva sort > $2-values 1219161748Scperciva 1220161748Scperciva # Any entry in $2-values which is not in $1-values corresponds to 1221161748Scperciva # a path which we need to remove from $1, $2, and $3. 1222161748Scperciva comm -13 $1-values $2-values > mlines 1223161748Scperciva rm $1-values $2-values 1224161748Scperciva 1225161748Scperciva # Any lines in $2 which are not in $1 AND are "not present" lines 1226161748Scperciva # also belong in mlines. 1227161748Scperciva comm -13 $1 $2 | 1228161748Scperciva cut -f 1,2,7 -d '|' | 1229161748Scperciva fgrep '|-|' >> mlines 1230161748Scperciva 1231161748Scperciva # Remove lines from $1, $2, and $3 1232161748Scperciva for X in $1 $2 $3; do 1233161748Scperciva sort -t '|' -k 1,1 ${X} > ${X}.tmp 1234161748Scperciva cut -f 1 -d '|' < mlines | 1235161748Scperciva sort | 1236161748Scperciva join -v 2 -t '|' - ${X}.tmp | 1237161748Scperciva sort > ${X} 1238161748Scperciva rm ${X}.tmp 1239161748Scperciva done 1240161748Scperciva 1241161748Scperciva # Store a list of the modified files, for future reference 1242161748Scperciva fgrep -v '|-|' mlines | 1243161748Scperciva cut -f 1 -d '|' > modifiedfiles 1244161748Scperciva rm mlines 1245161748Scperciva} 1246161748Scperciva 1247161748Scperciva# For each entry in $1 of type -, remove any corresponding 1248161748Scperciva# entry from $2 if ${ALLOWADD} != "yes". Remove all entries 1249161748Scperciva# of type - from $1. 1250161748Scpercivafetch_filter_allowadd () { 1251161748Scperciva cut -f 1,2 -d '|' < $1 | 1252161748Scperciva fgrep '|-' | 1253161748Scperciva cut -f 1 -d '|' > filesnotpresent 1254161748Scperciva 1255161748Scperciva if [ ${ALLOWADD} != "yes" ]; then 1256161748Scperciva sort < $2 | 1257161748Scperciva join -v 1 -t '|' - filesnotpresent | 1258161748Scperciva sort > $2.tmp 1259161748Scperciva mv $2.tmp $2 1260161748Scperciva fi 1261161748Scperciva 1262161748Scperciva sort < $1 | 1263161748Scperciva join -v 1 -t '|' - filesnotpresent | 1264161748Scperciva sort > $1.tmp 1265161748Scperciva mv $1.tmp $1 1266161748Scperciva rm filesnotpresent 1267161748Scperciva} 1268161748Scperciva 1269161748Scperciva# If ${ALLOWDELETE} != "yes", then remove any entries from $1 1270161748Scperciva# which don't correspond to entries in $2. 1271161748Scpercivafetch_filter_allowdelete () { 1272161748Scperciva # Produce a lists ${PATH}|${TYPE} 1273161748Scperciva for X in $1 $2; do 1274161748Scperciva cut -f 1-2 -d '|' < ${X} | 1275161748Scperciva sort -u > ${X}.nodes 1276161748Scperciva done 1277161748Scperciva 1278161748Scperciva # Figure out which lines need to be removed from $1. 1279161748Scperciva if [ ${ALLOWDELETE} != "yes" ]; then 1280161748Scperciva comm -23 $1.nodes $2.nodes > $1.badnodes 1281161748Scperciva else 1282161748Scperciva : > $1.badnodes 1283161748Scperciva fi 1284161748Scperciva 1285161748Scperciva # Remove the relevant lines from $1 1286161748Scperciva while read X; do 1287161748Scperciva look "${X}|" $1 1288161748Scperciva done < $1.badnodes | 1289161748Scperciva comm -13 - $1 > $1.tmp 1290161748Scperciva mv $1.tmp $1 1291161748Scperciva 1292161748Scperciva rm $1.badnodes $1.nodes $2.nodes 1293161748Scperciva} 1294161748Scperciva 1295161748Scperciva# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2 1296161748Scperciva# with metadata not matching any entry in $1, replace the corresponding 1297161748Scperciva# line of $3 with one having the same metadata as the entry in $2. 1298161748Scpercivafetch_filter_modified_metadata () { 1299161748Scperciva # Fish out the metadata from $1 and $2 1300161748Scperciva for X in $1 $2; do 1301161748Scperciva cut -f 1-6 -d '|' < ${X} > ${X}.metadata 1302161748Scperciva done 1303161748Scperciva 1304161748Scperciva # Find the metadata we need to keep 1305161748Scperciva if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then 1306161748Scperciva comm -13 $1.metadata $2.metadata > keepmeta 1307161748Scperciva else 1308161748Scperciva : > keepmeta 1309161748Scperciva fi 1310161748Scperciva 1311161748Scperciva # Extract the lines which we need to remove from $3, and 1312161748Scperciva # construct the lines which we need to add to $3. 1313161748Scperciva : > $3.remove 1314161748Scperciva : > $3.add 1315161748Scperciva while read LINE; do 1316161748Scperciva NODE=`echo "${LINE}" | cut -f 1-2 -d '|'` 1317161748Scperciva look "${NODE}|" $3 >> $3.remove 1318161748Scperciva look "${NODE}|" $3 | 1319161748Scperciva cut -f 7- -d '|' | 1320161748Scperciva lam -s "${LINE}|" - >> $3.add 1321161748Scperciva done < keepmeta 1322161748Scperciva 1323161748Scperciva # Remove the specified lines and add the new lines. 1324161748Scperciva sort $3.remove | 1325161748Scperciva comm -13 - $3 | 1326161748Scperciva sort -u - $3.add > $3.tmp 1327161748Scperciva mv $3.tmp $3 1328161748Scperciva 1329161748Scperciva rm keepmeta $1.metadata $2.metadata $3.add $3.remove 1330161748Scperciva} 1331161748Scperciva 1332161748Scperciva# Remove lines from $1 and $2 which are identical; 1333161748Scperciva# no need to update a file if it isn't changing. 1334161748Scpercivafetch_filter_uptodate () { 1335161748Scperciva comm -23 $1 $2 > $1.tmp 1336161748Scperciva comm -13 $1 $2 > $2.tmp 1337161748Scperciva 1338161748Scperciva mv $1.tmp $1 1339161748Scperciva mv $2.tmp $2 1340161748Scperciva} 1341161748Scperciva 1342161748Scperciva# Prepare to fetch files: Generate a list of the files we need, 1343161748Scperciva# copy the unmodified files we have into /files/, and generate 1344161748Scperciva# a list of patches to download. 1345161748Scpercivafetch_files_prepare () { 1346161748Scperciva # Tell the user why his disk is suddenly making lots of noise 1347161748Scperciva echo -n "Preparing to download files... " 1348161748Scperciva 1349161748Scperciva # Reduce indices to ${PATH}|${HASH} pairs 1350161748Scperciva for X in $1 $2 $3; do 1351161748Scperciva cut -f 1,2,7 -d '|' < ${X} | 1352161748Scperciva fgrep '|f|' | 1353161748Scperciva cut -f 1,3 -d '|' | 1354161748Scperciva sort > ${X}.hashes 1355161748Scperciva done 1356161748Scperciva 1357161748Scperciva # List of files wanted 1358161748Scperciva cut -f 2 -d '|' < $3.hashes | 1359161748Scperciva sort -u > files.wanted 1360161748Scperciva 1361161748Scperciva # Generate a list of unmodified files 1362161748Scperciva comm -12 $1.hashes $2.hashes | 1363161748Scperciva sort -k 1,1 -t '|' > unmodified.files 1364161748Scperciva 1365161748Scperciva # Copy all files into /files/. We only need the unmodified files 1366161748Scperciva # for use in patching; but we'll want all of them if the user asks 1367161748Scperciva # to rollback the updates later. 1368171784Scperciva while read LINE; do 1369171784Scperciva F=`echo "${LINE}" | cut -f 1 -d '|'` 1370171784Scperciva HASH=`echo "${LINE}" | cut -f 2 -d '|'` 1371171784Scperciva 1372171784Scperciva # Skip files we already have. 1373171784Scperciva if [ -f files/${HASH}.gz ]; then 1374171784Scperciva continue 1375171784Scperciva fi 1376171784Scperciva 1377171784Scperciva # Make sure the file hasn't changed. 1378161748Scperciva cp "${BASEDIR}/${F}" tmpfile 1379171784Scperciva if [ `sha256 -q tmpfile` != ${HASH} ]; then 1380171784Scperciva echo 1381171784Scperciva echo "File changed while FreeBSD Update running: ${F}" 1382171784Scperciva return 1 1383171784Scperciva fi 1384171784Scperciva 1385171784Scperciva # Place the file into storage. 1386171784Scperciva gzip -c < tmpfile > files/${HASH}.gz 1387161748Scperciva rm tmpfile 1388171784Scperciva done < $2.hashes 1389161748Scperciva 1390161748Scperciva # Produce a list of patches to download 1391161748Scperciva sort -k 1,1 -t '|' $3.hashes | 1392161748Scperciva join -t '|' -o 2.2,1.2 - unmodified.files | 1393161748Scperciva fetch_make_patchlist > patchlist 1394161748Scperciva 1395161748Scperciva # Garbage collect 1396161748Scperciva rm unmodified.files $1.hashes $2.hashes $3.hashes 1397161748Scperciva 1398161748Scperciva # We don't need the list of possible old files any more. 1399161748Scperciva rm $1 1400161748Scperciva 1401161748Scperciva # We're finished making noise 1402161748Scperciva echo "done." 1403161748Scperciva} 1404161748Scperciva 1405161748Scperciva# Fetch files. 1406161748Scpercivafetch_files () { 1407161748Scperciva # Attempt to fetch patches 1408161748Scperciva if [ -s patchlist ]; then 1409161748Scperciva echo -n "Fetching `wc -l < patchlist | tr -d ' '` " 1410161748Scperciva echo ${NDEBUG} "patches.${DDSTATS}" 1411161748Scperciva tr '|' '-' < patchlist | 1412161748Scperciva lam -s "${FETCHDIR}/bp/" - | 1413161748Scperciva xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 1414161748Scperciva 2>${STATSREDIR} | fetch_progress 1415161748Scperciva echo "done." 1416161748Scperciva 1417161748Scperciva # Attempt to apply patches 1418161748Scperciva echo -n "Applying patches... " 1419161748Scperciva tr '|' ' ' < patchlist | 1420161748Scperciva while read X Y; do 1421161748Scperciva if [ ! -f "${X}-${Y}" ]; then continue; fi 1422161748Scperciva gunzip -c < files/${X}.gz > OLD 1423161748Scperciva 1424161748Scperciva bspatch OLD NEW ${X}-${Y} 1425161748Scperciva 1426161748Scperciva if [ `${SHA256} -q NEW` = ${Y} ]; then 1427161748Scperciva mv NEW files/${Y} 1428161748Scperciva gzip -n files/${Y} 1429161748Scperciva fi 1430161748Scperciva rm -f diff OLD NEW ${X}-${Y} 1431161748Scperciva done 2>${QUIETREDIR} 1432161748Scperciva echo "done." 1433161748Scperciva fi 1434161748Scperciva 1435161748Scperciva # Download files which couldn't be generate via patching 1436161748Scperciva while read Y; do 1437161748Scperciva if [ ! -f "files/${Y}.gz" ]; then 1438161748Scperciva echo ${Y}; 1439161748Scperciva fi 1440161748Scperciva done < files.wanted > filelist 1441161748Scperciva 1442161748Scperciva if [ -s filelist ]; then 1443161748Scperciva echo -n "Fetching `wc -l < filelist | tr -d ' '` " 1444161748Scperciva echo ${NDEBUG} "files... " 1445161748Scperciva lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist | 1446161748Scperciva xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 1447161748Scperciva 2>${QUIETREDIR} 1448161748Scperciva 1449161748Scperciva while read Y; do 1450161748Scperciva if ! [ -f ${Y}.gz ]; then 1451161748Scperciva echo "failed." 1452161748Scperciva return 1 1453161748Scperciva fi 1454161748Scperciva if [ `gunzip -c < ${Y}.gz | 1455161748Scperciva ${SHA256} -q` = ${Y} ]; then 1456161748Scperciva mv ${Y}.gz files/${Y}.gz 1457161748Scperciva else 1458161748Scperciva echo "${Y} has incorrect hash." 1459161748Scperciva return 1 1460161748Scperciva fi 1461161748Scperciva done < filelist 1462161748Scperciva echo "done." 1463161748Scperciva fi 1464161748Scperciva 1465161748Scperciva # Clean up 1466161748Scperciva rm files.wanted filelist patchlist 1467161748Scperciva} 1468161748Scperciva 1469161748Scperciva# Create and populate install manifest directory; and report what updates 1470161748Scperciva# are available. 1471161748Scpercivafetch_create_manifest () { 1472161748Scperciva # If we have an existing install manifest, nuke it. 1473161748Scperciva if [ -L "${BDHASH}-install" ]; then 1474161748Scperciva rm -r ${BDHASH}-install/ 1475161748Scperciva rm ${BDHASH}-install 1476161748Scperciva fi 1477161748Scperciva 1478161748Scperciva # Report to the user if any updates were avoided due to local changes 1479161748Scperciva if [ -s modifiedfiles ]; then 1480161748Scperciva echo 1481161748Scperciva echo -n "The following files are affected by updates, " 1482161748Scperciva echo "but no changes have" 1483161748Scperciva echo -n "been downloaded because the files have been " 1484161748Scperciva echo "modified locally:" 1485161748Scperciva cat modifiedfiles 1486161748Scperciva fi 1487161748Scperciva rm modifiedfiles 1488161748Scperciva 1489161748Scperciva # If no files will be updated, tell the user and exit 1490161748Scperciva if ! [ -s INDEX-PRESENT ] && 1491161748Scperciva ! [ -s INDEX-NEW ]; then 1492161748Scperciva rm INDEX-PRESENT INDEX-NEW 1493161748Scperciva echo 1494161748Scperciva echo -n "No updates needed to update system to " 1495161748Scperciva echo "${RELNUM}-p${RELPATCHNUM}." 1496161748Scperciva return 1497161748Scperciva fi 1498161748Scperciva 1499161748Scperciva # Divide files into (a) removed files, (b) added files, and 1500161748Scperciva # (c) updated files. 1501161748Scperciva cut -f 1 -d '|' < INDEX-PRESENT | 1502161748Scperciva sort > INDEX-PRESENT.flist 1503161748Scperciva cut -f 1 -d '|' < INDEX-NEW | 1504161748Scperciva sort > INDEX-NEW.flist 1505161748Scperciva comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed 1506161748Scperciva comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added 1507161748Scperciva comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated 1508161748Scperciva rm INDEX-PRESENT.flist INDEX-NEW.flist 1509161748Scperciva 1510161748Scperciva # Report removed files, if any 1511161748Scperciva if [ -s files.removed ]; then 1512161748Scperciva echo 1513161748Scperciva echo -n "The following files will be removed " 1514161748Scperciva echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:" 1515161748Scperciva cat files.removed 1516161748Scperciva fi 1517161748Scperciva rm files.removed 1518161748Scperciva 1519161748Scperciva # Report added files, if any 1520161748Scperciva if [ -s files.added ]; then 1521161748Scperciva echo 1522161748Scperciva echo -n "The following files will be added " 1523161748Scperciva echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:" 1524161748Scperciva cat files.added 1525161748Scperciva fi 1526161748Scperciva rm files.added 1527161748Scperciva 1528161748Scperciva # Report updated files, if any 1529161748Scperciva if [ -s files.updated ]; then 1530161748Scperciva echo 1531161748Scperciva echo -n "The following files will be updated " 1532161748Scperciva echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:" 1533161748Scperciva 1534161748Scperciva cat files.updated 1535161748Scperciva fi 1536161748Scperciva rm files.updated 1537161748Scperciva 1538161748Scperciva # Create a directory for the install manifest. 1539161748Scperciva MDIR=`mktemp -d install.XXXXXX` || return 1 1540161748Scperciva 1541161748Scperciva # Populate it 1542161748Scperciva mv INDEX-PRESENT ${MDIR}/INDEX-OLD 1543161748Scperciva mv INDEX-NEW ${MDIR}/INDEX-NEW 1544161748Scperciva 1545161748Scperciva # Link it into place 1546161748Scperciva ln -s ${MDIR} ${BDHASH}-install 1547161748Scperciva} 1548161748Scperciva 1549161748Scperciva# Warn about any upcoming EoL 1550161748Scpercivafetch_warn_eol () { 1551161748Scperciva # What's the current time? 1552161748Scperciva NOWTIME=`date "+%s"` 1553161748Scperciva 1554161748Scperciva # When did we last warn about the EoL date? 1555161748Scperciva if [ -f lasteolwarn ]; then 1556161748Scperciva LASTWARN=`cat lasteolwarn` 1557161748Scperciva else 1558161748Scperciva LASTWARN=`expr ${NOWTIME} - 63072000` 1559161748Scperciva fi 1560161748Scperciva 1561161748Scperciva # If the EoL time is past, warn. 1562161748Scperciva if [ ${EOLTIME} -lt ${NOWTIME} ]; then 1563161748Scperciva echo 1564161748Scperciva cat <<-EOF 1565161869Scperciva WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE. 1566161748Scperciva Any security issues discovered after `date -r ${EOLTIME}` 1567161748Scperciva will not have been corrected. 1568161748Scperciva EOF 1569161748Scperciva return 1 1570161748Scperciva fi 1571161748Scperciva 1572161748Scperciva # Figure out how long it has been since we last warned about the 1573161748Scperciva # upcoming EoL, and how much longer we have left. 1574161748Scperciva SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}` 1575161748Scperciva TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}` 1576161748Scperciva 1577171838Scperciva # Don't warn if the EoL is more than 3 months away 1578171838Scperciva if [ ${TIMELEFT} -gt 7884000 ]; then 1579161748Scperciva return 0 1580161748Scperciva fi 1581161748Scperciva 1582161748Scperciva # Don't warn if the time remaining is more than 3 times the time 1583161748Scperciva # since the last warning. 1584161748Scperciva if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then 1585161748Scperciva return 0 1586161748Scperciva fi 1587161748Scperciva 1588161748Scperciva # Figure out what time units to use. 1589161748Scperciva if [ ${TIMELEFT} -lt 604800 ]; then 1590161748Scperciva UNIT="day" 1591161748Scperciva SIZE=86400 1592161748Scperciva elif [ ${TIMELEFT} -lt 2678400 ]; then 1593161748Scperciva UNIT="week" 1594161748Scperciva SIZE=604800 1595161748Scperciva else 1596161748Scperciva UNIT="month" 1597161748Scperciva SIZE=2678400 1598161748Scperciva fi 1599161748Scperciva 1600161748Scperciva # Compute the right number of units 1601161748Scperciva NUM=`expr ${TIMELEFT} / ${SIZE}` 1602161748Scperciva if [ ${NUM} != 1 ]; then 1603161748Scperciva UNIT="${UNIT}s" 1604161748Scperciva fi 1605161748Scperciva 1606161748Scperciva # Print the warning 1607161748Scperciva echo 1608161748Scperciva cat <<-EOF 1609161748Scperciva WARNING: `uname -sr` is approaching its End-of-Life date. 1610161748Scperciva It is strongly recommended that you upgrade to a newer 1611161748Scperciva release within the next ${NUM} ${UNIT}. 1612161748Scperciva EOF 1613161748Scperciva 1614161748Scperciva # Update the stored time of last warning 1615161748Scperciva echo ${NOWTIME} > lasteolwarn 1616161748Scperciva} 1617161748Scperciva 1618161748Scperciva# Do the actual work involved in "fetch" / "cron". 1619161748Scpercivafetch_run () { 1620161748Scperciva workdir_init || return 1 1621161748Scperciva 1622161748Scperciva # Prepare the mirror list. 1623161748Scperciva fetch_pick_server_init && fetch_pick_server 1624161748Scperciva 1625161748Scperciva # Try to fetch the public key until we run out of servers. 1626161748Scperciva while ! fetch_key; do 1627161748Scperciva fetch_pick_server || return 1 1628161748Scperciva done 1629161748Scperciva 1630161748Scperciva # Try to fetch the metadata index signature ("tag") until we run 1631161748Scperciva # out of available servers; and sanity check the downloaded tag. 1632161748Scperciva while ! fetch_tag; do 1633161748Scperciva fetch_pick_server || return 1 1634161748Scperciva done 1635161748Scperciva fetch_tagsanity || return 1 1636161748Scperciva 1637161748Scperciva # Fetch the latest INDEX-NEW and INDEX-OLD files. 1638161748Scperciva fetch_metadata INDEX-NEW INDEX-OLD || return 1 1639161748Scperciva 1640161748Scperciva # Generate filtered INDEX-NEW and INDEX-OLD files containing only 1641161748Scperciva # the lines which (a) belong to components we care about, and (b) 1642161748Scperciva # don't correspond to paths we're explicitly ignoring. 1643161748Scperciva fetch_filter_metadata INDEX-NEW || return 1 1644161748Scperciva fetch_filter_metadata INDEX-OLD || return 1 1645161748Scperciva 1646161748Scperciva # Translate /boot/`uname -i` into ${KERNELDIR} 1647161748Scperciva fetch_filter_kernel_names INDEX-NEW 1648161748Scperciva fetch_filter_kernel_names INDEX-OLD 1649161748Scperciva 1650161748Scperciva # For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the 1651161748Scperciva # system and generate an INDEX-PRESENT file. 1652161748Scperciva fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 1653161748Scperciva 1654161748Scperciva # Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which 1655161748Scperciva # correspond to lines in INDEX-PRESENT with hashes not appearing 1656161748Scperciva # in INDEX-OLD or INDEX-NEW. Also remove lines where the entry in 1657161748Scperciva # INDEX-PRESENT has type - and there isn't a corresponding entry in 1658161748Scperciva # INDEX-OLD with type -. 1659161748Scperciva fetch_filter_unmodified_notpresent INDEX-OLD INDEX-PRESENT INDEX-NEW 1660161748Scperciva 1661161748Scperciva # For each entry in INDEX-PRESENT of type -, remove any corresponding 1662161748Scperciva # entry from INDEX-NEW if ${ALLOWADD} != "yes". Remove all entries 1663161748Scperciva # of type - from INDEX-PRESENT. 1664161748Scperciva fetch_filter_allowadd INDEX-PRESENT INDEX-NEW 1665161748Scperciva 1666161748Scperciva # If ${ALLOWDELETE} != "yes", then remove any entries from 1667161748Scperciva # INDEX-PRESENT which don't correspond to entries in INDEX-NEW. 1668161748Scperciva fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW 1669161748Scperciva 1670161748Scperciva # If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in 1671161748Scperciva # INDEX-PRESENT with metadata not matching any entry in INDEX-OLD, 1672161748Scperciva # replace the corresponding line of INDEX-NEW with one having the 1673161748Scperciva # same metadata as the entry in INDEX-PRESENT. 1674161748Scperciva fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW 1675161748Scperciva 1676161748Scperciva # Remove lines from INDEX-PRESENT and INDEX-NEW which are identical; 1677161748Scperciva # no need to update a file if it isn't changing. 1678161748Scperciva fetch_filter_uptodate INDEX-PRESENT INDEX-NEW 1679161748Scperciva 1680161748Scperciva # Prepare to fetch files: Generate a list of the files we need, 1681161748Scperciva # copy the unmodified files we have into /files/, and generate 1682161748Scperciva # a list of patches to download. 1683171784Scperciva fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 1684161748Scperciva 1685161748Scperciva # Fetch files. 1686161748Scperciva fetch_files || return 1 1687161748Scperciva 1688161748Scperciva # Create and populate install manifest directory; and report what 1689161748Scperciva # updates are available. 1690161748Scperciva fetch_create_manifest || return 1 1691161748Scperciva 1692161748Scperciva # Warn about any upcoming EoL 1693161748Scperciva fetch_warn_eol || return 1 1694161748Scperciva} 1695161748Scperciva 1696161748Scperciva# Make sure that all the file hashes mentioned in $@ have corresponding 1697161748Scperciva# gzipped files stored in /files/. 1698161748Scpercivainstall_verify () { 1699161748Scperciva # Generate a list of hashes 1700161748Scperciva cat $@ | 1701161748Scperciva cut -f 2,7 -d '|' | 1702161748Scperciva grep -E '^f' | 1703161748Scperciva cut -f 2 -d '|' | 1704161748Scperciva sort -u > filelist 1705161748Scperciva 1706161748Scperciva # Make sure all the hashes exist 1707161748Scperciva while read HASH; do 1708161748Scperciva if ! [ -f files/${HASH}.gz ]; then 1709161748Scperciva echo -n "Update files missing -- " 1710161748Scperciva echo "this should never happen." 1711161748Scperciva echo "Re-run '$0 fetch'." 1712161748Scperciva return 1 1713161748Scperciva fi 1714161748Scperciva done < filelist 1715161748Scperciva 1716161748Scperciva # Clean up 1717161748Scperciva rm filelist 1718161748Scperciva} 1719161748Scperciva 1720161748Scperciva# Remove the system immutable flag from files 1721161748Scpercivainstall_unschg () { 1722161748Scperciva # Generate file list 1723161748Scperciva cat $@ | 1724161748Scperciva cut -f 1 -d '|' > filelist 1725161748Scperciva 1726161748Scperciva # Remove flags 1727161748Scperciva while read F; do 1728169603Scperciva if ! [ -e ${BASEDIR}/${F} ]; then 1729161748Scperciva continue 1730161748Scperciva fi 1731161748Scperciva 1732169603Scperciva chflags noschg ${BASEDIR}/${F} || return 1 1733161748Scperciva done < filelist 1734161748Scperciva 1735161748Scperciva # Clean up 1736161748Scperciva rm filelist 1737161748Scperciva} 1738161748Scperciva 1739161748Scperciva# Install new files 1740161748Scpercivainstall_from_index () { 1741161748Scperciva # First pass: Do everything apart from setting file flags. We 1742161748Scperciva # can't set flags yet, because schg inhibits hard linking. 1743161748Scperciva sort -k 1,1 -t '|' $1 | 1744161748Scperciva tr '|' ' ' | 1745161748Scperciva while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do 1746161748Scperciva case ${TYPE} in 1747161748Scperciva d) 1748161748Scperciva # Create a directory 1749161748Scperciva install -d -o ${OWNER} -g ${GROUP} \ 1750161748Scperciva -m ${PERM} ${BASEDIR}/${FPATH} 1751161748Scperciva ;; 1752161748Scperciva f) 1753161748Scperciva if [ -z "${LINK}" ]; then 1754161748Scperciva # Create a file, without setting flags. 1755161748Scperciva gunzip < files/${HASH}.gz > ${HASH} 1756161748Scperciva install -S -o ${OWNER} -g ${GROUP} \ 1757161748Scperciva -m ${PERM} ${HASH} ${BASEDIR}/${FPATH} 1758161748Scperciva rm ${HASH} 1759161748Scperciva else 1760161748Scperciva # Create a hard link. 1761169603Scperciva ln -f ${BASEDIR}/${LINK} ${BASEDIR}/${FPATH} 1762161748Scperciva fi 1763161748Scperciva ;; 1764161748Scperciva L) 1765161748Scperciva # Create a symlink 1766161748Scperciva ln -sfh ${HASH} ${BASEDIR}/${FPATH} 1767161748Scperciva ;; 1768161748Scperciva esac 1769161748Scperciva done 1770161748Scperciva 1771161748Scperciva # Perform a second pass, adding file flags. 1772161748Scperciva tr '|' ' ' < $1 | 1773161748Scperciva while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do 1774161748Scperciva if [ ${TYPE} = "f" ] && 1775161748Scperciva ! [ ${FLAGS} = "0" ]; then 1776161748Scperciva chflags ${FLAGS} ${BASEDIR}/${FPATH} 1777161748Scperciva fi 1778161748Scperciva done 1779161748Scperciva} 1780161748Scperciva 1781161748Scperciva# Remove files which we want to delete 1782161748Scpercivainstall_delete () { 1783161748Scperciva # Generate list of new files 1784161748Scperciva cut -f 1 -d '|' < $2 | 1785161748Scperciva sort > newfiles 1786161748Scperciva 1787161748Scperciva # Generate subindex of old files we want to nuke 1788161748Scperciva sort -k 1,1 -t '|' $1 | 1789161748Scperciva join -t '|' -v 1 - newfiles | 1790164600Scperciva sort -r -k 1,1 -t '|' | 1791161748Scperciva cut -f 1,2 -d '|' | 1792161748Scperciva tr '|' ' ' > killfiles 1793161748Scperciva 1794161748Scperciva # Remove the offending bits 1795161748Scperciva while read FPATH TYPE; do 1796161748Scperciva case ${TYPE} in 1797161748Scperciva d) 1798161748Scperciva rmdir ${BASEDIR}/${FPATH} 1799161748Scperciva ;; 1800161748Scperciva f) 1801161748Scperciva rm ${BASEDIR}/${FPATH} 1802161748Scperciva ;; 1803161748Scperciva L) 1804161748Scperciva rm ${BASEDIR}/${FPATH} 1805161748Scperciva ;; 1806161748Scperciva esac 1807161748Scperciva done < killfiles 1808161748Scperciva 1809161748Scperciva # Clean up 1810161748Scperciva rm newfiles killfiles 1811161748Scperciva} 1812161748Scperciva 1813161748Scperciva# Update linker.hints if anything in /boot/ was touched 1814161748Scpercivainstall_kldxref () { 1815161748Scperciva if cat $@ | 1816161748Scperciva grep -qE '^/boot/'; then 1817161748Scperciva kldxref -R /boot/ 1818161748Scperciva fi 1819161748Scperciva} 1820161748Scperciva 1821161748Scperciva# Rearrange bits to allow the installed updates to be rolled back 1822161748Scpercivainstall_setup_rollback () { 1823161748Scperciva if [ -L ${BDHASH}-rollback ]; then 1824161748Scperciva mv ${BDHASH}-rollback ${BDHASH}-install/rollback 1825161748Scperciva fi 1826161748Scperciva 1827161748Scperciva mv ${BDHASH}-install ${BDHASH}-rollback 1828161748Scperciva} 1829161748Scperciva 1830161748Scperciva# Actually install updates 1831161748Scpercivainstall_run () { 1832161748Scperciva echo -n "Installing updates..." 1833161748Scperciva 1834161748Scperciva # Make sure we have all the files we should have 1835161748Scperciva install_verify ${BDHASH}-install/INDEX-OLD \ 1836161748Scperciva ${BDHASH}-install/INDEX-NEW || return 1 1837161748Scperciva 1838161748Scperciva # Remove system immutable flag from files 1839161748Scperciva install_unschg ${BDHASH}-install/INDEX-OLD \ 1840161748Scperciva ${BDHASH}-install/INDEX-NEW || return 1 1841161748Scperciva 1842161748Scperciva # Install new files 1843161748Scperciva install_from_index \ 1844161748Scperciva ${BDHASH}-install/INDEX-NEW || return 1 1845161748Scperciva 1846161748Scperciva # Remove files which we want to delete 1847161748Scperciva install_delete ${BDHASH}-install/INDEX-OLD \ 1848161748Scperciva ${BDHASH}-install/INDEX-NEW || return 1 1849161748Scperciva 1850161748Scperciva # Update linker.hints if anything in /boot/ was touched 1851161748Scperciva install_kldxref ${BDHASH}-install/INDEX-OLD \ 1852161748Scperciva ${BDHASH}-install/INDEX-NEW 1853161748Scperciva 1854161748Scperciva # Rearrange bits to allow the installed updates to be rolled back 1855161748Scperciva install_setup_rollback 1856161748Scperciva 1857161748Scperciva echo " done." 1858161748Scperciva} 1859161748Scperciva 1860161748Scperciva# Rearrange bits to allow the previous set of updates to be rolled back next. 1861161748Scpercivarollback_setup_rollback () { 1862161748Scperciva if [ -L ${BDHASH}-rollback/rollback ]; then 1863161748Scperciva mv ${BDHASH}-rollback/rollback rollback-tmp 1864161748Scperciva rm -r ${BDHASH}-rollback/ 1865161748Scperciva rm ${BDHASH}-rollback 1866161748Scperciva mv rollback-tmp ${BDHASH}-rollback 1867161748Scperciva else 1868161748Scperciva rm -r ${BDHASH}-rollback/ 1869161748Scperciva rm ${BDHASH}-rollback 1870161748Scperciva fi 1871161748Scperciva} 1872161748Scperciva 1873161748Scperciva# Actually rollback updates 1874161748Scpercivarollback_run () { 1875161748Scperciva echo -n "Uninstalling updates..." 1876161748Scperciva 1877161748Scperciva # If there are updates waiting to be installed, remove them; we 1878161748Scperciva # want the user to re-run 'fetch' after rolling back updates. 1879161748Scperciva if [ -L ${BDHASH}-install ]; then 1880161748Scperciva rm -r ${BDHASH}-install/ 1881161748Scperciva rm ${BDHASH}-install 1882161748Scperciva fi 1883161748Scperciva 1884161748Scperciva # Make sure we have all the files we should have 1885161748Scperciva install_verify ${BDHASH}-rollback/INDEX-NEW \ 1886161748Scperciva ${BDHASH}-rollback/INDEX-OLD || return 1 1887161748Scperciva 1888161748Scperciva # Remove system immutable flag from files 1889161748Scperciva install_unschg ${BDHASH}-rollback/INDEX-NEW \ 1890161748Scperciva ${BDHASH}-rollback/INDEX-OLD || return 1 1891161748Scperciva 1892161748Scperciva # Install new files 1893161748Scperciva install_from_index \ 1894161748Scperciva ${BDHASH}-rollback/INDEX-OLD || return 1 1895161748Scperciva 1896161748Scperciva # Remove files which we want to delete 1897161748Scperciva install_delete ${BDHASH}-rollback/INDEX-NEW \ 1898161748Scperciva ${BDHASH}-rollback/INDEX-OLD || return 1 1899161748Scperciva 1900161748Scperciva # Update linker.hints if anything in /boot/ was touched 1901161748Scperciva install_kldxref ${BDHASH}-rollback/INDEX-NEW \ 1902161748Scperciva ${BDHASH}-rollback/INDEX-OLD 1903161748Scperciva 1904161748Scperciva # Remove the rollback directory and the symlink pointing to it; and 1905161748Scperciva # rearrange bits to allow the previous set of updates to be rolled 1906161748Scperciva # back next. 1907161748Scperciva rollback_setup_rollback 1908161748Scperciva 1909161748Scperciva echo " done." 1910161748Scperciva} 1911161748Scperciva 1912161748Scperciva#### Main functions -- call parameter-handling and core functions 1913161748Scperciva 1914161748Scperciva# Using the command line, configuration file, and defaults, 1915161748Scperciva# set all the parameters which are needed later. 1916161748Scpercivaget_params () { 1917161748Scperciva init_params 1918161748Scperciva parse_cmdline $@ 1919161748Scperciva parse_conffile 1920161748Scperciva default_params 1921161748Scperciva} 1922161748Scperciva 1923161748Scperciva# Fetch command. Make sure that we're being called 1924161748Scperciva# interactively, then run fetch_check_params and fetch_run 1925161748Scpercivacmd_fetch () { 1926161748Scperciva if [ ! -t 0 ]; then 1927161748Scperciva echo -n "`basename $0` fetch should not " 1928161748Scperciva echo "be run non-interactively." 1929161748Scperciva echo "Run `basename $0` cron instead." 1930161748Scperciva exit 1 1931161748Scperciva fi 1932161748Scperciva fetch_check_params 1933161748Scperciva fetch_run || exit 1 1934161748Scperciva} 1935161748Scperciva 1936161748Scperciva# Cron command. Make sure the parameters are sensible; wait 1937161748Scperciva# rand(3600) seconds; then fetch updates. While fetching updates, 1938161748Scperciva# send output to a temporary file; only print that file if the 1939161748Scperciva# fetching failed. 1940161748Scpercivacmd_cron () { 1941161748Scperciva fetch_check_params 1942161748Scperciva sleep `jot -r 1 0 3600` 1943161748Scperciva 1944161748Scperciva TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1 1945161748Scperciva if ! fetch_run >> ${TMPFILE} || 1946161748Scperciva ! grep -q "No updates needed" ${TMPFILE} || 1947161748Scperciva [ ${VERBOSELEVEL} = "debug" ]; then 1948161748Scperciva mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE} 1949161748Scperciva fi 1950161748Scperciva 1951161748Scperciva rm ${TMPFILE} 1952161748Scperciva} 1953161748Scperciva 1954161748Scperciva# Install downloaded updates. 1955161748Scpercivacmd_install () { 1956161748Scperciva install_check_params 1957161748Scperciva install_run || exit 1 1958161748Scperciva} 1959161748Scperciva 1960161748Scperciva# Rollback most recently installed updates. 1961161748Scpercivacmd_rollback () { 1962161748Scperciva rollback_check_params 1963161748Scperciva rollback_run || exit 1 1964161748Scperciva} 1965161748Scperciva 1966161748Scperciva#### Entry point 1967161748Scperciva 1968161748Scperciva# Make sure we find utilities from the base system 1969161748Scpercivaexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH} 1970161748Scperciva 1971163564Scperciva# Set LC_ALL in order to avoid problems with character ranges like [A-Z]. 1972163564Scpercivaexport LC_ALL=C 1973163564Scperciva 1974161748Scpercivaget_params $@ 1975161748Scpercivafor COMMAND in ${COMMANDS}; do 1976161748Scperciva cmd_${COMMAND} 1977161748Scpercivadone 1978