1#!/bin/sh 2 3#- 4# SPDX-License-Identifier: BSD-2-Clause-FreeBSD 5# 6# Copyright 2004-2007 Colin Percival 7# All rights reserved 8# 9# Redistribution and use in source and binary forms, with or without 10# modification, are permitted providing that the following conditions 11# are met: 12# 1. Redistributions of source code must retain the above copyright 13# notice, this list of conditions and the following disclaimer. 14# 2. Redistributions in binary form must reproduce the above copyright 15# notice, this list of conditions and the following disclaimer in the 16# documentation and/or other materials provided with the distribution. 17# 18# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 22# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 27# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28# POSSIBILITY OF SUCH DAMAGE. 29 30# $FreeBSD$ 31 32#### Usage function -- called from command-line handling code. 33 34# Usage instructions. Options not listed: 35# --debug -- don't filter output from utilities 36# --no-stats -- don't show progress statistics while fetching files 37usage () { 38 cat <<EOF 39usage: `basename $0` [options] command ... [path] 40 41Options: 42 -b basedir -- Operate on a system mounted at basedir 43 (default: /) 44 -d workdir -- Store working files in workdir 45 (default: /var/db/freebsd-update/) 46 -f conffile -- Read configuration options from conffile 47 (default: /etc/freebsd-update.conf) 48 -F -- Force a fetch operation to proceed in the 49 case of an unfinished upgrade 50 -k KEY -- Trust an RSA key with SHA256 hash of KEY 51 -r release -- Target for upgrade (e.g., 11.1-RELEASE) 52 -s server -- Server from which to fetch updates 53 (default: update.FreeBSD.org) 54 -t address -- Mail output of cron command, if any, to address 55 (default: root) 56 --not-running-from-cron 57 -- Run without a tty, for use by automated tools 58 --currently-running release 59 -- Update as if currently running this release 60Commands: 61 fetch -- Fetch updates from server 62 cron -- Sleep rand(3600) seconds, fetch updates, and send an 63 email if updates were found 64 upgrade -- Fetch upgrades to FreeBSD version specified via -r option 65 updatesready -- Check if there are fetched updates ready to install 66 install -- Install downloaded updates or upgrades 67 rollback -- Uninstall most recently installed updates 68 IDS -- Compare the system against an index of "known good" files 69 showconfig -- Show configuration 70EOF 71 exit 0 72} 73 74#### Configuration processing functions 75 76#- 77# Configuration options are set in the following order of priority: 78# 1. Command line options 79# 2. Configuration file options 80# 3. Default options 81# In addition, certain options (e.g., IgnorePaths) can be specified multiple 82# times and (as long as these are all in the same place, e.g., inside the 83# configuration file) they will accumulate. Finally, because the path to the 84# configuration file can be specified at the command line, the entire command 85# line must be processed before we start reading the configuration file. 86# 87# Sound like a mess? It is. Here's how we handle this: 88# 1. Initialize CONFFILE and all the options to "". 89# 2. Process the command line. Throw an error if a non-accumulating option 90# is specified twice. 91# 3. If CONFFILE is "", set CONFFILE to /etc/freebsd-update.conf . 92# 4. For all the configuration options X, set X_saved to X. 93# 5. Initialize all the options to "". 94# 6. Read CONFFILE line by line, parsing options. 95# 7. For each configuration option X, set X to X_saved iff X_saved is not "". 96# 8. Repeat steps 4-7, except setting options to their default values at (6). 97 98CONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE 99 KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED 100 BASEDIR VERBOSELEVEL TARGETRELEASE STRICTCOMPONENTS MERGECHANGES 101 IDSIGNOREPATHS BACKUPKERNEL BACKUPKERNELDIR BACKUPKERNELSYMBOLFILES" 102 103# Set all the configuration options to "". 104nullconfig () { 105 for X in ${CONFIGOPTIONS}; do 106 eval ${X}="" 107 done 108} 109 110# For each configuration option X, set X_saved to X. 111saveconfig () { 112 for X in ${CONFIGOPTIONS}; do 113 eval ${X}_saved=\$${X} 114 done 115} 116 117# For each configuration option X, set X to X_saved if X_saved is not "". 118mergeconfig () { 119 for X in ${CONFIGOPTIONS}; do 120 eval _=\$${X}_saved 121 if ! [ -z "${_}" ]; then 122 eval ${X}=\$${X}_saved 123 fi 124 done 125} 126 127# Set the trusted keyprint. 128config_KeyPrint () { 129 if [ -z ${KEYPRINT} ]; then 130 KEYPRINT=$1 131 else 132 return 1 133 fi 134} 135 136# Set the working directory. 137config_WorkDir () { 138 if [ -z ${WORKDIR} ]; then 139 WORKDIR=$1 140 else 141 return 1 142 fi 143} 144 145# Set the name of the server (pool) from which to fetch updates 146config_ServerName () { 147 if [ -z ${SERVERNAME} ]; then 148 SERVERNAME=$1 149 else 150 return 1 151 fi 152} 153 154# Set the address to which 'cron' output will be mailed. 155config_MailTo () { 156 if [ -z ${MAILTO} ]; then 157 MAILTO=$1 158 else 159 return 1 160 fi 161} 162 163# Set whether FreeBSD Update is allowed to add files (or directories, or 164# symlinks) which did not previously exist. 165config_AllowAdd () { 166 if [ -z ${ALLOWADD} ]; then 167 case $1 in 168 [Yy][Ee][Ss]) 169 ALLOWADD=yes 170 ;; 171 [Nn][Oo]) 172 ALLOWADD=no 173 ;; 174 *) 175 return 1 176 ;; 177 esac 178 else 179 return 1 180 fi 181} 182 183# Set whether FreeBSD Update is allowed to remove files/directories/symlinks. 184config_AllowDelete () { 185 if [ -z ${ALLOWDELETE} ]; then 186 case $1 in 187 [Yy][Ee][Ss]) 188 ALLOWDELETE=yes 189 ;; 190 [Nn][Oo]) 191 ALLOWDELETE=no 192 ;; 193 *) 194 return 1 195 ;; 196 esac 197 else 198 return 1 199 fi 200} 201 202# Set whether FreeBSD Update should keep existing inode ownership, 203# permissions, and flags, in the event that they have been modified locally 204# after the release. 205config_KeepModifiedMetadata () { 206 if [ -z ${KEEPMODIFIEDMETADATA} ]; then 207 case $1 in 208 [Yy][Ee][Ss]) 209 KEEPMODIFIEDMETADATA=yes 210 ;; 211 [Nn][Oo]) 212 KEEPMODIFIEDMETADATA=no 213 ;; 214 *) 215 return 1 216 ;; 217 esac 218 else 219 return 1 220 fi 221} 222 223# Add to the list of components which should be kept updated. 224config_Components () { 225 for C in $@; do 226 COMPONENTS="${COMPONENTS} ${C}" 227 done 228} 229 230# Remove src component from list if it isn't installed 231finalize_components_config () { 232 COMPONENTS="" 233 for C in $@; do 234 if [ "$C" = "src" ]; then 235 if [ -e "${BASEDIR}/usr/src/COPYRIGHT" ]; then 236 COMPONENTS="${COMPONENTS} ${C}" 237 else 238 echo "src component not installed, skipped" 239 fi 240 else 241 COMPONENTS="${COMPONENTS} ${C}" 242 fi 243 done 244} 245 246# Add to the list of paths under which updates will be ignored. 247config_IgnorePaths () { 248 for C in $@; do 249 IGNOREPATHS="${IGNOREPATHS} ${C}" 250 done 251} 252 253# Add to the list of paths which IDS should ignore. 254config_IDSIgnorePaths () { 255 for C in $@; do 256 IDSIGNOREPATHS="${IDSIGNOREPATHS} ${C}" 257 done 258} 259 260# Add to the list of paths within which updates will be performed only if the 261# file on disk has not been modified locally. 262config_UpdateIfUnmodified () { 263 for C in $@; do 264 UPDATEIFUNMODIFIED="${UPDATEIFUNMODIFIED} ${C}" 265 done 266} 267 268# Add to the list of paths within which updates to text files will be merged 269# instead of overwritten. 270config_MergeChanges () { 271 for C in $@; do 272 MERGECHANGES="${MERGECHANGES} ${C}" 273 done 274} 275 276# Work on a FreeBSD installation mounted under $1 277config_BaseDir () { 278 if [ -z ${BASEDIR} ]; then 279 BASEDIR=$1 280 else 281 return 1 282 fi 283} 284 285# When fetching upgrades, should we assume the user wants exactly the 286# components listed in COMPONENTS, rather than trying to guess based on 287# what's currently installed? 288config_StrictComponents () { 289 if [ -z ${STRICTCOMPONENTS} ]; then 290 case $1 in 291 [Yy][Ee][Ss]) 292 STRICTCOMPONENTS=yes 293 ;; 294 [Nn][Oo]) 295 STRICTCOMPONENTS=no 296 ;; 297 *) 298 return 1 299 ;; 300 esac 301 else 302 return 1 303 fi 304} 305 306# Upgrade to FreeBSD $1 307config_TargetRelease () { 308 if [ -z ${TARGETRELEASE} ]; then 309 TARGETRELEASE=$1 310 else 311 return 1 312 fi 313 if echo ${TARGETRELEASE} | grep -qE '^[0-9.]+$'; then 314 TARGETRELEASE="${TARGETRELEASE}-RELEASE" 315 fi 316} 317 318# Pretend current release is FreeBSD $1 319config_SourceRelease () { 320 UNAME_r=$1 321 if echo ${UNAME_r} | grep -qE '^[0-9.]+$'; then 322 UNAME_r="${UNAME_r}-RELEASE" 323 fi 324 export UNAME_r 325} 326 327# Define what happens to output of utilities 328config_VerboseLevel () { 329 if [ -z ${VERBOSELEVEL} ]; then 330 case $1 in 331 [Dd][Ee][Bb][Uu][Gg]) 332 VERBOSELEVEL=debug 333 ;; 334 [Nn][Oo][Ss][Tt][Aa][Tt][Ss]) 335 VERBOSELEVEL=nostats 336 ;; 337 [Ss][Tt][Aa][Tt][Ss]) 338 VERBOSELEVEL=stats 339 ;; 340 *) 341 return 1 342 ;; 343 esac 344 else 345 return 1 346 fi 347} 348 349config_BackupKernel () { 350 if [ -z ${BACKUPKERNEL} ]; then 351 case $1 in 352 [Yy][Ee][Ss]) 353 BACKUPKERNEL=yes 354 ;; 355 [Nn][Oo]) 356 BACKUPKERNEL=no 357 ;; 358 *) 359 return 1 360 ;; 361 esac 362 else 363 return 1 364 fi 365} 366 367config_BackupKernelDir () { 368 if [ -z ${BACKUPKERNELDIR} ]; then 369 if [ -z "$1" ]; then 370 echo "BackupKernelDir set to empty dir" 371 return 1 372 fi 373 374 # We check for some paths which would be extremely odd 375 # to use, but which could cause a lot of problems if 376 # used. 377 case $1 in 378 /|/bin|/boot|/etc|/lib|/libexec|/sbin|/usr|/var) 379 echo "BackupKernelDir set to invalid path $1" 380 return 1 381 ;; 382 /*) 383 BACKUPKERNELDIR=$1 384 ;; 385 *) 386 echo "BackupKernelDir ($1) is not an absolute path" 387 return 1 388 ;; 389 esac 390 else 391 return 1 392 fi 393} 394 395config_BackupKernelSymbolFiles () { 396 if [ -z ${BACKUPKERNELSYMBOLFILES} ]; then 397 case $1 in 398 [Yy][Ee][Ss]) 399 BACKUPKERNELSYMBOLFILES=yes 400 ;; 401 [Nn][Oo]) 402 BACKUPKERNELSYMBOLFILES=no 403 ;; 404 *) 405 return 1 406 ;; 407 esac 408 else 409 return 1 410 fi 411} 412 413# Handle one line of configuration 414configline () { 415 if [ $# -eq 0 ]; then 416 return 417 fi 418 419 OPT=$1 420 shift 421 config_${OPT} $@ 422} 423 424#### Parameter handling functions. 425 426# Initialize parameters to null, just in case they're 427# set in the environment. 428init_params () { 429 # Configration settings 430 nullconfig 431 432 # No configuration file set yet 433 CONFFILE="" 434 435 # No commands specified yet 436 COMMANDS="" 437 438 # Force fetch to proceed 439 FORCEFETCH=0 440 441 # Run without a TTY 442 NOTTYOK=0 443 444 # Fetched first in a chain of commands 445 ISFETCHED=0 446} 447 448# Parse the command line 449parse_cmdline () { 450 while [ $# -gt 0 ]; do 451 case "$1" in 452 # Location of configuration file 453 -f) 454 if [ $# -eq 1 ]; then usage; fi 455 if [ ! -z "${CONFFILE}" ]; then usage; fi 456 shift; CONFFILE="$1" 457 ;; 458 -F) 459 FORCEFETCH=1 460 ;; 461 --not-running-from-cron) 462 NOTTYOK=1 463 ;; 464 --currently-running) 465 shift 466 config_SourceRelease $1 || usage 467 ;; 468 469 # Configuration file equivalents 470 -b) 471 if [ $# -eq 1 ]; then usage; fi; shift 472 config_BaseDir $1 || usage 473 ;; 474 -d) 475 if [ $# -eq 1 ]; then usage; fi; shift 476 config_WorkDir $1 || usage 477 ;; 478 -k) 479 if [ $# -eq 1 ]; then usage; fi; shift 480 config_KeyPrint $1 || usage 481 ;; 482 -s) 483 if [ $# -eq 1 ]; then usage; fi; shift 484 config_ServerName $1 || usage 485 ;; 486 -r) 487 if [ $# -eq 1 ]; then usage; fi; shift 488 config_TargetRelease $1 || usage 489 ;; 490 -t) 491 if [ $# -eq 1 ]; then usage; fi; shift 492 config_MailTo $1 || usage 493 ;; 494 -v) 495 if [ $# -eq 1 ]; then usage; fi; shift 496 config_VerboseLevel $1 || usage 497 ;; 498 499 # Aliases for "-v debug" and "-v nostats" 500 --debug) 501 config_VerboseLevel debug || usage 502 ;; 503 --no-stats) 504 config_VerboseLevel nostats || usage 505 ;; 506 507 # Commands 508 cron | fetch | upgrade | updatesready | install | rollback |\ 509 IDS | showconfig) 510 COMMANDS="${COMMANDS} $1" 511 ;; 512 513 # Anything else is an error 514 *) 515 usage 516 ;; 517 esac 518 shift 519 done 520 521 # Make sure we have at least one command 522 if [ -z "${COMMANDS}" ]; then 523 usage 524 fi 525} 526 527# Parse the configuration file 528parse_conffile () { 529 # If a configuration file was specified on the command line, check 530 # that it exists and is readable. 531 if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then 532 echo -n "File does not exist " 533 echo -n "or is not readable: " 534 echo ${CONFFILE} 535 exit 1 536 fi 537 538 # If a configuration file was not specified on the command line, 539 # use the default configuration file path. If that default does 540 # not exist, give up looking for any configuration. 541 if [ -z "${CONFFILE}" ]; then 542 CONFFILE="/etc/freebsd-update.conf" 543 if [ ! -r "${CONFFILE}" ]; then 544 return 545 fi 546 fi 547 548 # Save the configuration options specified on the command line, and 549 # clear all the options in preparation for reading the config file. 550 saveconfig 551 nullconfig 552 553 # Read the configuration file. Anything after the first '#' is 554 # ignored, and any blank lines are ignored. 555 L=0 556 while read LINE; do 557 L=$(($L + 1)) 558 LINEX=`echo "${LINE}" | cut -f 1 -d '#'` 559 if ! configline ${LINEX}; then 560 echo "Error processing configuration file, line $L:" 561 echo "==> ${LINE}" 562 exit 1 563 fi 564 done < ${CONFFILE} 565 566 # Merge the settings read from the configuration file with those 567 # provided at the command line. 568 mergeconfig 569} 570 571# Provide some default parameters 572default_params () { 573 # Save any parameters already configured, and clear the slate 574 saveconfig 575 nullconfig 576 577 # Default configurations 578 config_WorkDir /var/db/freebsd-update 579 config_MailTo root 580 config_AllowAdd yes 581 config_AllowDelete yes 582 config_KeepModifiedMetadata yes 583 config_BaseDir / 584 config_VerboseLevel stats 585 config_StrictComponents no 586 config_BackupKernel yes 587 config_BackupKernelDir /boot/kernel.old 588 config_BackupKernelSymbolFiles no 589 590 # Merge these defaults into the earlier-configured settings 591 mergeconfig 592} 593 594# Set utility output filtering options, based on ${VERBOSELEVEL} 595fetch_setup_verboselevel () { 596 case ${VERBOSELEVEL} in 597 debug) 598 QUIETREDIR="/dev/stderr" 599 QUIETFLAG=" " 600 STATSREDIR="/dev/stderr" 601 DDSTATS=".." 602 XARGST="-t" 603 NDEBUG=" " 604 ;; 605 nostats) 606 QUIETREDIR="" 607 QUIETFLAG="" 608 STATSREDIR="/dev/null" 609 DDSTATS=".." 610 XARGST="" 611 NDEBUG="" 612 ;; 613 stats) 614 QUIETREDIR="/dev/null" 615 QUIETFLAG="-q" 616 STATSREDIR="/dev/stdout" 617 DDSTATS="" 618 XARGST="" 619 NDEBUG="-n" 620 ;; 621 esac 622} 623 624# Perform sanity checks and set some final parameters 625# in preparation for fetching files. Figure out which 626# set of updates should be downloaded: If the user is 627# running *-p[0-9]+, strip off the last part; if the 628# user is running -SECURITY, call it -RELEASE. Chdir 629# into the working directory. 630fetchupgrade_check_params () { 631 export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)" 632 633 _SERVERNAME_z=\ 634"SERVERNAME must be given via command line or configuration file." 635 _KEYPRINT_z="Key must be given via -k option or configuration file." 636 _KEYPRINT_bad="Invalid key fingerprint: " 637 _WORKDIR_bad="Directory does not exist or is not writable: " 638 _WORKDIR_bad2="Directory is not on a persistent filesystem: " 639 640 if [ -z "${SERVERNAME}" ]; then 641 echo -n "`basename $0`: " 642 echo "${_SERVERNAME_z}" 643 exit 1 644 fi 645 if [ -z "${KEYPRINT}" ]; then 646 echo -n "`basename $0`: " 647 echo "${_KEYPRINT_z}" 648 exit 1 649 fi 650 if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then 651 echo -n "`basename $0`: " 652 echo -n "${_KEYPRINT_bad}" 653 echo ${KEYPRINT} 654 exit 1 655 fi 656 if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then 657 echo -n "`basename $0`: " 658 echo -n "${_WORKDIR_bad}" 659 echo ${WORKDIR} 660 exit 1 661 fi 662 case `df -T ${WORKDIR}` in */dev/md[0-9]* | *tmpfs*) 663 echo -n "`basename $0`: " 664 echo -n "${_WORKDIR_bad2}" 665 echo ${WORKDIR} 666 exit 1 667 ;; 668 esac 669 chmod 700 ${WORKDIR} 670 cd ${WORKDIR} || exit 1 671 672 # Generate release number. The s/SECURITY/RELEASE/ bit exists 673 # to provide an upgrade path for FreeBSD Update 1.x users, since 674 # the kernels provided by FreeBSD Update 1.x are always labelled 675 # as X.Y-SECURITY. 676 RELNUM=`uname -r | 677 sed -E 's,-p[0-9]+,,' | 678 sed -E 's,-SECURITY,-RELEASE,'` 679 ARCH=`uname -m` 680 FETCHDIR=${RELNUM}/${ARCH} 681 PATCHDIR=${RELNUM}/${ARCH}/bp 682 683 # Disallow upgrade from a version that is not a release 684 case ${RELNUM} in 685 *-RELEASE | *-ALPHA* | *-BETA* | *-RC*) 686 ;; 687 *) 688 echo -n "`basename $0`: " 689 cat <<- EOF 690 Cannot upgrade from a version that is not a release 691 (including alpha, beta and release candidates) 692 using `basename $0`. Instead, FreeBSD can be directly 693 upgraded by source or upgraded to a RELEASE/RELENG version 694 prior to running `basename $0`. 695 Currently running: ${RELNUM} 696 EOF 697 exit 1 698 ;; 699 esac 700 701 # Figure out what directory contains the running kernel 702 BOOTFILE=`sysctl -n kern.bootfile` 703 KERNELDIR=${BOOTFILE%/kernel} 704 if ! [ -d ${KERNELDIR} ]; then 705 echo "Cannot identify running kernel" 706 exit 1 707 fi 708 709 # Figure out what kernel configuration is running. We start with 710 # the output of `uname -i`, and then make the following adjustments: 711 # 1. Replace "SMP-GENERIC" with "SMP". Why the SMP kernel config 712 # file says "ident SMP-GENERIC", I don't know... 713 # 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64" 714 # _and_ `sysctl kern.version` contains a line which ends "/SMP", then 715 # we're running an SMP kernel. This mis-identification is a bug 716 # which was fixed in 6.2-STABLE. 717 KERNCONF=`uname -i` 718 if [ ${KERNCONF} = "SMP-GENERIC" ]; then 719 KERNCONF=SMP 720 fi 721 if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then 722 if sysctl kern.version | grep -qE '/SMP$'; then 723 KERNCONF=SMP 724 fi 725 fi 726 727 # Define some paths 728 BSPATCH=/usr/bin/bspatch 729 SHA256=/sbin/sha256 730 PHTTPGET=/usr/libexec/phttpget 731 732 # Set up variables relating to VERBOSELEVEL 733 fetch_setup_verboselevel 734 735 # Construct a unique name from ${BASEDIR} 736 BDHASH=`echo ${BASEDIR} | sha256 -q` 737} 738 739# Perform sanity checks etc. before fetching updates. 740fetch_check_params () { 741 fetchupgrade_check_params 742 743 if ! [ -z "${TARGETRELEASE}" ]; then 744 echo -n "`basename $0`: " 745 echo -n "-r option is meaningless with 'fetch' command. " 746 echo "(Did you mean 'upgrade' instead?)" 747 exit 1 748 fi 749 750 # Check that we have updates ready to install 751 if [ -f ${BDHASH}-install/kerneldone -a $FORCEFETCH -eq 0 ]; then 752 echo "You have a partially completed upgrade pending" 753 echo "Run '$0 install' first." 754 echo "Run '$0 fetch -F' to proceed anyway." 755 exit 1 756 fi 757} 758 759# Perform sanity checks etc. before fetching upgrades. 760upgrade_check_params () { 761 fetchupgrade_check_params 762 763 # Unless set otherwise, we're upgrading to the same kernel config. 764 NKERNCONF=${KERNCONF} 765 766 # We need TARGETRELEASE set 767 _TARGETRELEASE_z="Release target must be specified via -r option." 768 if [ -z "${TARGETRELEASE}" ]; then 769 echo -n "`basename $0`: " 770 echo "${_TARGETRELEASE_z}" 771 exit 1 772 fi 773 774 # The target release should be != the current release. 775 if [ "${TARGETRELEASE}" = "${RELNUM}" ]; then 776 echo -n "`basename $0`: " 777 echo "Cannot upgrade from ${RELNUM} to itself" 778 exit 1 779 fi 780 781 # Turning off AllowAdd or AllowDelete is a bad idea for upgrades. 782 if [ "${ALLOWADD}" = "no" ]; then 783 echo -n "`basename $0`: " 784 echo -n "WARNING: \"AllowAdd no\" is a bad idea " 785 echo "when upgrading between releases." 786 echo 787 fi 788 if [ "${ALLOWDELETE}" = "no" ]; then 789 echo -n "`basename $0`: " 790 echo -n "WARNING: \"AllowDelete no\" is a bad idea " 791 echo "when upgrading between releases." 792 echo 793 fi 794 795 # Set EDITOR to /usr/bin/vi if it isn't already set 796 : ${EDITOR:='/usr/bin/vi'} 797} 798 799# Perform sanity checks and set some final parameters in 800# preparation for installing updates. 801install_check_params () { 802 # Check that we are root. All sorts of things won't work otherwise. 803 if [ `id -u` != 0 ]; then 804 echo "You must be root to run this." 805 exit 1 806 fi 807 808 # Check that securelevel <= 0. Otherwise we can't update schg files. 809 if [ `sysctl -n kern.securelevel` -gt 0 ]; then 810 echo "Updates cannot be installed when the system securelevel" 811 echo "is greater than zero." 812 exit 1 813 fi 814 815 # Check that we have a working directory 816 _WORKDIR_bad="Directory does not exist or is not writable: " 817 if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then 818 echo -n "`basename $0`: " 819 echo -n "${_WORKDIR_bad}" 820 echo ${WORKDIR} 821 exit 1 822 fi 823 cd ${WORKDIR} || exit 1 824 825 # Construct a unique name from ${BASEDIR} 826 BDHASH=`echo ${BASEDIR} | sha256 -q` 827 828 # Check that we have updates ready to install 829 if ! [ -L ${BDHASH}-install ]; then 830 echo "No updates are available to install." 831 if [ $ISFETCHED -eq 0 ]; then 832 echo "Run '$0 fetch' first." 833 exit 2 834 fi 835 exit 0 836 fi 837 if ! [ -f ${BDHASH}-install/INDEX-OLD ] || 838 ! [ -f ${BDHASH}-install/INDEX-NEW ]; then 839 echo "Update manifest is corrupt -- this should never happen." 840 echo "Re-run '$0 fetch'." 841 exit 1 842 fi 843 844 # Figure out what directory contains the running kernel 845 BOOTFILE=`sysctl -n kern.bootfile` 846 KERNELDIR=${BOOTFILE%/kernel} 847 if ! [ -d ${KERNELDIR} ]; then 848 echo "Cannot identify running kernel" 849 exit 1 850 fi 851} 852 853# Perform sanity checks and set some final parameters in 854# preparation for UNinstalling updates. 855rollback_check_params () { 856 # Check that we are root. All sorts of things won't work otherwise. 857 if [ `id -u` != 0 ]; then 858 echo "You must be root to run this." 859 exit 1 860 fi 861 862 # Check that we have a working directory 863 _WORKDIR_bad="Directory does not exist or is not writable: " 864 if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then 865 echo -n "`basename $0`: " 866 echo -n "${_WORKDIR_bad}" 867 echo ${WORKDIR} 868 exit 1 869 fi 870 cd ${WORKDIR} || exit 1 871 872 # Construct a unique name from ${BASEDIR} 873 BDHASH=`echo ${BASEDIR} | sha256 -q` 874 875 # Check that we have updates ready to rollback 876 if ! [ -L ${BDHASH}-rollback ]; then 877 echo "No rollback directory found." 878 exit 1 879 fi 880 if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] || 881 ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then 882 echo "Update manifest is corrupt -- this should never happen." 883 exit 1 884 fi 885} 886 887# Perform sanity checks and set some final parameters 888# in preparation for comparing the system against the 889# published index. Figure out which index we should 890# compare against: If the user is running *-p[0-9]+, 891# strip off the last part; if the user is running 892# -SECURITY, call it -RELEASE. Chdir into the working 893# directory. 894IDS_check_params () { 895 export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)" 896 897 _SERVERNAME_z=\ 898"SERVERNAME must be given via command line or configuration file." 899 _KEYPRINT_z="Key must be given via -k option or configuration file." 900 _KEYPRINT_bad="Invalid key fingerprint: " 901 _WORKDIR_bad="Directory does not exist or is not writable: " 902 903 if [ -z "${SERVERNAME}" ]; then 904 echo -n "`basename $0`: " 905 echo "${_SERVERNAME_z}" 906 exit 1 907 fi 908 if [ -z "${KEYPRINT}" ]; then 909 echo -n "`basename $0`: " 910 echo "${_KEYPRINT_z}" 911 exit 1 912 fi 913 if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then 914 echo -n "`basename $0`: " 915 echo -n "${_KEYPRINT_bad}" 916 echo ${KEYPRINT} 917 exit 1 918 fi 919 if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then 920 echo -n "`basename $0`: " 921 echo -n "${_WORKDIR_bad}" 922 echo ${WORKDIR} 923 exit 1 924 fi 925 cd ${WORKDIR} || exit 1 926 927 # Generate release number. The s/SECURITY/RELEASE/ bit exists 928 # to provide an upgrade path for FreeBSD Update 1.x users, since 929 # the kernels provided by FreeBSD Update 1.x are always labelled 930 # as X.Y-SECURITY. 931 RELNUM=`uname -r | 932 sed -E 's,-p[0-9]+,,' | 933 sed -E 's,-SECURITY,-RELEASE,'` 934 ARCH=`uname -m` 935 FETCHDIR=${RELNUM}/${ARCH} 936 PATCHDIR=${RELNUM}/${ARCH}/bp 937 938 # Figure out what directory contains the running kernel 939 BOOTFILE=`sysctl -n kern.bootfile` 940 KERNELDIR=${BOOTFILE%/kernel} 941 if ! [ -d ${KERNELDIR} ]; then 942 echo "Cannot identify running kernel" 943 exit 1 944 fi 945 946 # Figure out what kernel configuration is running. We start with 947 # the output of `uname -i`, and then make the following adjustments: 948 # 1. Replace "SMP-GENERIC" with "SMP". Why the SMP kernel config 949 # file says "ident SMP-GENERIC", I don't know... 950 # 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64" 951 # _and_ `sysctl kern.version` contains a line which ends "/SMP", then 952 # we're running an SMP kernel. This mis-identification is a bug 953 # which was fixed in 6.2-STABLE. 954 KERNCONF=`uname -i` 955 if [ ${KERNCONF} = "SMP-GENERIC" ]; then 956 KERNCONF=SMP 957 fi 958 if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then 959 if sysctl kern.version | grep -qE '/SMP$'; then 960 KERNCONF=SMP 961 fi 962 fi 963 964 # Define some paths 965 SHA256=/sbin/sha256 966 PHTTPGET=/usr/libexec/phttpget 967 968 # Set up variables relating to VERBOSELEVEL 969 fetch_setup_verboselevel 970} 971 972#### Core functionality -- the actual work gets done here 973 974# Use an SRV query to pick a server. If the SRV query doesn't provide 975# a useful answer, use the server name specified by the user. 976# Put another way... look up _http._tcp.${SERVERNAME} and pick a server 977# from that; or if no servers are returned, use ${SERVERNAME}. 978# This allows a user to specify "portsnap.freebsd.org" (in which case 979# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org" 980# (in which case portsnap will use that particular server, since there 981# won't be an SRV entry for that name). 982# 983# We ignore the Port field, since we are always going to use port 80. 984 985# Fetch the mirror list, but do not pick a mirror yet. Returns 1 if 986# no mirrors are available for any reason. 987fetch_pick_server_init () { 988 : > serverlist_tried 989 990# Check that host(1) exists (i.e., that the system wasn't built with the 991# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist. 992 if ! which -s host; then 993 : > serverlist_full 994 return 1 995 fi 996 997 echo -n "Looking up ${SERVERNAME} mirrors... " 998 999# Issue the SRV query and pull out the Priority, Weight, and Target fields. 1000# BIND 9 prints "$name has SRV record ..." while BIND 8 prints 1001# "$name server selection ..."; we allow either format. 1002 MLIST="_http._tcp.${SERVERNAME}" 1003 host -t srv "${MLIST}" | 1004 sed -nE "s/${MLIST} (has SRV record|server selection) //Ip" | 1005 cut -f 1,2,4 -d ' ' | 1006 sed -e 's/\.$//' | 1007 sort > serverlist_full 1008 1009# If no records, give up -- we'll just use the server name we were given. 1010 if [ `wc -l < serverlist_full` -eq 0 ]; then 1011 echo "none found." 1012 return 1 1013 fi 1014 1015# Report how many mirrors we found. 1016 echo `wc -l < serverlist_full` "mirrors found." 1017 1018# Generate a random seed for use in picking mirrors. If HTTP_PROXY 1019# is set, this will be used to generate the seed; otherwise, the seed 1020# will be random. 1021 if [ -n "${HTTP_PROXY}${http_proxy}" ]; then 1022 RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" | 1023 tr -d 'a-f' | 1024 cut -c 1-9` 1025 else 1026 RANDVALUE=`jot -r 1 0 999999999` 1027 fi 1028} 1029 1030# Pick a mirror. Returns 1 if we have run out of mirrors to try. 1031fetch_pick_server () { 1032# Generate a list of not-yet-tried mirrors 1033 sort serverlist_tried | 1034 comm -23 serverlist_full - > serverlist 1035 1036# Have we run out of mirrors? 1037 if [ `wc -l < serverlist` -eq 0 ]; then 1038 cat <<- EOF 1039 No mirrors remaining, giving up. 1040 1041 This may be because upgrading from this platform (${ARCH}) 1042 or release (${RELNUM}) is unsupported by `basename $0`. Only 1043 platforms with Tier 1 support can be upgraded by `basename $0`. 1044 See https://www.freebsd.org/platforms/index.html for more info. 1045 1046 If unsupported, FreeBSD must be upgraded by source. 1047 EOF 1048 return 1 1049 fi 1050 1051# Find the highest priority level (lowest numeric value). 1052 SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1` 1053 1054# Add up the weights of the response lines at that priority level. 1055 SRV_WSUM=0; 1056 while read X; do 1057 case "$X" in 1058 ${SRV_PRIORITY}\ *) 1059 SRV_W=`echo $X | cut -f 2 -d ' '` 1060 SRV_WSUM=$(($SRV_WSUM + $SRV_W)) 1061 ;; 1062 esac 1063 done < serverlist 1064 1065# If all the weights are 0, pretend that they are all 1 instead. 1066 if [ ${SRV_WSUM} -eq 0 ]; then 1067 SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l` 1068 SRV_W_ADD=1 1069 else 1070 SRV_W_ADD=0 1071 fi 1072 1073# Pick a value between 0 and the sum of the weights - 1 1074 SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}` 1075 1076# Read through the list of mirrors and set SERVERNAME. Write the line 1077# corresponding to the mirror we selected into serverlist_tried so that 1078# we won't try it again. 1079 while read X; do 1080 case "$X" in 1081 ${SRV_PRIORITY}\ *) 1082 SRV_W=`echo $X | cut -f 2 -d ' '` 1083 SRV_W=$(($SRV_W + $SRV_W_ADD)) 1084 if [ $SRV_RND -lt $SRV_W ]; then 1085 SERVERNAME=`echo $X | cut -f 3 -d ' '` 1086 echo "$X" >> serverlist_tried 1087 break 1088 else 1089 SRV_RND=$(($SRV_RND - $SRV_W)) 1090 fi 1091 ;; 1092 esac 1093 done < serverlist 1094} 1095 1096# Take a list of ${oldhash}|${newhash} and output a list of needed patches, 1097# i.e., those for which we have ${oldhash} and don't have ${newhash}. 1098fetch_make_patchlist () { 1099 grep -vE "^([0-9a-f]{64})\|\1$" | 1100 tr '|' ' ' | 1101 while read X Y; do 1102 if [ -f "files/${Y}.gz" ] || 1103 [ ! -f "files/${X}.gz" ]; then 1104 continue 1105 fi 1106 echo "${X}|${Y}" 1107 done | sort -u 1108} 1109 1110# Print user-friendly progress statistics 1111fetch_progress () { 1112 LNC=0 1113 while read x; do 1114 LNC=$(($LNC + 1)) 1115 if [ $(($LNC % 10)) = 0 ]; then 1116 echo -n $LNC 1117 elif [ $(($LNC % 2)) = 0 ]; then 1118 echo -n . 1119 fi 1120 done 1121 echo -n " " 1122} 1123 1124# Function for asking the user if everything is ok 1125continuep () { 1126 while read -p "Does this look reasonable (y/n)? " CONTINUE; do 1127 case "${CONTINUE}" in 1128 y*) 1129 return 0 1130 ;; 1131 n*) 1132 return 1 1133 ;; 1134 esac 1135 done 1136} 1137 1138# Initialize the working directory 1139workdir_init () { 1140 mkdir -p files 1141 touch tINDEX.present 1142} 1143 1144# Check that we have a public key with an appropriate hash, or 1145# fetch the key if it doesn't exist. Returns 1 if the key has 1146# not yet been fetched. 1147fetch_key () { 1148 if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then 1149 return 0 1150 fi 1151 1152 echo -n "Fetching public key from ${SERVERNAME}... " 1153 rm -f pub.ssl 1154 fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \ 1155 2>${QUIETREDIR} || true 1156 if ! [ -r pub.ssl ]; then 1157 echo "failed." 1158 return 1 1159 fi 1160 if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then 1161 echo "key has incorrect hash." 1162 rm -f pub.ssl 1163 return 1 1164 fi 1165 echo "done." 1166} 1167 1168# Fetch metadata signature, aka "tag". 1169fetch_tag () { 1170 echo -n "Fetching metadata signature " 1171 echo ${NDEBUG} "for ${RELNUM} from ${SERVERNAME}... " 1172 rm -f latest.ssl 1173 fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl \ 1174 2>${QUIETREDIR} || true 1175 if ! [ -r latest.ssl ]; then 1176 echo "failed." 1177 return 1 1178 fi 1179 1180 openssl rsautl -pubin -inkey pub.ssl -verify \ 1181 < latest.ssl > tag.new 2>${QUIETREDIR} || true 1182 rm latest.ssl 1183 1184 if ! [ `wc -l < tag.new` = 1 ] || 1185 ! grep -qE \ 1186 "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \ 1187 tag.new; then 1188 echo "invalid signature." 1189 return 1 1190 fi 1191 1192 echo "done." 1193 1194 RELPATCHNUM=`cut -f 4 -d '|' < tag.new` 1195 TINDEXHASH=`cut -f 5 -d '|' < tag.new` 1196 EOLTIME=`cut -f 6 -d '|' < tag.new` 1197} 1198 1199# Sanity-check the patch number in a tag, to make sure that we're not 1200# going to "update" backwards and to prevent replay attacks. 1201fetch_tagsanity () { 1202 # Check that we're not going to move from -pX to -pY with Y < X. 1203 RELPX=`uname -r | sed -E 's,.*-,,'` 1204 if echo ${RELPX} | grep -qE '^p[0-9]+$'; then 1205 RELPX=`echo ${RELPX} | cut -c 2-` 1206 else 1207 RELPX=0 1208 fi 1209 if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then 1210 echo 1211 echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})" 1212 echo " appear older than what" 1213 echo "we are currently running (`uname -r`)!" 1214 echo "Cowardly refusing to proceed any further." 1215 return 1 1216 fi 1217 1218 # If "tag" exists and corresponds to ${RELNUM}, make sure that 1219 # it contains a patch number <= RELPATCHNUM, in order to protect 1220 # against rollback (replay) attacks. 1221 if [ -f tag ] && 1222 grep -qE \ 1223 "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \ 1224 tag; then 1225 LASTRELPATCHNUM=`cut -f 4 -d '|' < tag` 1226 1227 if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then 1228 echo 1229 echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})" 1230 echo " are older than the" 1231 echo -n "most recently seen updates" 1232 echo " (${RELNUM}-p${LASTRELPATCHNUM})." 1233 echo "Cowardly refusing to proceed any further." 1234 return 1 1235 fi 1236 fi 1237} 1238 1239# Fetch metadata index file 1240fetch_metadata_index () { 1241 echo ${NDEBUG} "Fetching metadata index... " 1242 rm -f ${TINDEXHASH} 1243 fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH} 1244 2>${QUIETREDIR} 1245 if ! [ -f ${TINDEXHASH} ]; then 1246 echo "failed." 1247 return 1 1248 fi 1249 if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then 1250 echo "update metadata index corrupt." 1251 return 1 1252 fi 1253 echo "done." 1254} 1255 1256# Print an error message about signed metadata being bogus. 1257fetch_metadata_bogus () { 1258 echo 1259 echo "The update metadata$1 is correctly signed, but" 1260 echo "failed an integrity check." 1261 echo "Cowardly refusing to proceed any further." 1262 return 1 1263} 1264 1265# Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH} 1266# with the lines not named in $@ from tINDEX.present (if that file exists). 1267fetch_metadata_index_merge () { 1268 for METAFILE in $@; do 1269 if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l` \ 1270 -ne 1 ]; then 1271 fetch_metadata_bogus " index" 1272 return 1 1273 fi 1274 1275 grep -E "${METAFILE}\|" ${TINDEXHASH} 1276 done | 1277 sort > tINDEX.wanted 1278 1279 if [ -f tINDEX.present ]; then 1280 join -t '|' -v 2 tINDEX.wanted tINDEX.present | 1281 sort -m - tINDEX.wanted > tINDEX.new 1282 rm tINDEX.wanted 1283 else 1284 mv tINDEX.wanted tINDEX.new 1285 fi 1286} 1287 1288# Sanity check all the lines of tINDEX.new. Even if more metadata lines 1289# are added by future versions of the server, this won't cause problems, 1290# since the only lines which appear in tINDEX.new are the ones which we 1291# specifically grepped out of ${TINDEXHASH}. 1292fetch_metadata_index_sanity () { 1293 if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then 1294 fetch_metadata_bogus " index" 1295 return 1 1296 fi 1297} 1298 1299# Sanity check the metadata file $1. 1300fetch_metadata_sanity () { 1301 # Some aliases to save space later: ${P} is a character which can 1302 # appear in a path; ${M} is the four numeric metadata fields; and 1303 # ${H} is a sha256 hash. 1304 P="[-+./:=,%@_[~[:alnum:]]" 1305 M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+" 1306 H="[0-9a-f]{64}" 1307 1308 # Check that the first four fields make sense. 1309 if gunzip -c < files/$1.gz | 1310 grep -qvE "^[a-z]+\|[0-9a-z-]+\|${P}+\|[fdL-]\|"; then 1311 fetch_metadata_bogus "" 1312 return 1 1313 fi 1314 1315 # Remove the first three fields. 1316 gunzip -c < files/$1.gz | 1317 cut -f 4- -d '|' > sanitycheck.tmp 1318 1319 # Sanity check entries with type 'f' 1320 if grep -E '^f' sanitycheck.tmp | 1321 grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then 1322 fetch_metadata_bogus "" 1323 return 1 1324 fi 1325 1326 # Sanity check entries with type 'd' 1327 if grep -E '^d' sanitycheck.tmp | 1328 grep -qvE "^d\|${M}\|\|\$"; then 1329 fetch_metadata_bogus "" 1330 return 1 1331 fi 1332 1333 # Sanity check entries with type 'L' 1334 if grep -E '^L' sanitycheck.tmp | 1335 grep -qvE "^L\|${M}\|${P}*\|\$"; then 1336 fetch_metadata_bogus "" 1337 return 1 1338 fi 1339 1340 # Sanity check entries with type '-' 1341 if grep -E '^-' sanitycheck.tmp | 1342 grep -qvE "^-\|\|\|\|\|\|"; then 1343 fetch_metadata_bogus "" 1344 return 1 1345 fi 1346 1347 # Clean up 1348 rm sanitycheck.tmp 1349} 1350 1351# Fetch the metadata index and metadata files listed in $@, 1352# taking advantage of metadata patches where possible. 1353fetch_metadata () { 1354 fetch_metadata_index || return 1 1355 fetch_metadata_index_merge $@ || return 1 1356 fetch_metadata_index_sanity || return 1 1357 1358 # Generate a list of wanted metadata patches 1359 join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new | 1360 fetch_make_patchlist > patchlist 1361 1362 if [ -s patchlist ]; then 1363 # Attempt to fetch metadata patches 1364 echo -n "Fetching `wc -l < patchlist | tr -d ' '` " 1365 echo ${NDEBUG} "metadata patches.${DDSTATS}" 1366 tr '|' '-' < patchlist | 1367 lam -s "${FETCHDIR}/tp/" - -s ".gz" | 1368 xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 1369 2>${STATSREDIR} | fetch_progress 1370 echo "done." 1371 1372 # Attempt to apply metadata patches 1373 echo -n "Applying metadata patches... " 1374 tr '|' ' ' < patchlist | 1375 while read X Y; do 1376 if [ ! -f "${X}-${Y}.gz" ]; then continue; fi 1377 gunzip -c < ${X}-${Y}.gz > diff 1378 gunzip -c < files/${X}.gz > diff-OLD 1379 1380 # Figure out which lines are being added and removed 1381 grep -E '^-' diff | 1382 cut -c 2- | 1383 while read PREFIX; do 1384 look "${PREFIX}" diff-OLD 1385 done | 1386 sort > diff-rm 1387 grep -E '^\+' diff | 1388 cut -c 2- > diff-add 1389 1390 # Generate the new file 1391 comm -23 diff-OLD diff-rm | 1392 sort - diff-add > diff-NEW 1393 1394 if [ `${SHA256} -q diff-NEW` = ${Y} ]; then 1395 mv diff-NEW files/${Y} 1396 gzip -n files/${Y} 1397 else 1398 mv diff-NEW ${Y}.bad 1399 fi 1400 rm -f ${X}-${Y}.gz diff 1401 rm -f diff-OLD diff-NEW diff-add diff-rm 1402 done 2>${QUIETREDIR} 1403 echo "done." 1404 fi 1405 1406 # Update metadata without patches 1407 cut -f 2 -d '|' < tINDEX.new | 1408 while read Y; do 1409 if [ ! -f "files/${Y}.gz" ]; then 1410 echo ${Y}; 1411 fi 1412 done | 1413 sort -u > filelist 1414 1415 if [ -s filelist ]; then 1416 echo -n "Fetching `wc -l < filelist | tr -d ' '` " 1417 echo ${NDEBUG} "metadata files... " 1418 lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist | 1419 xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 1420 2>${QUIETREDIR} 1421 1422 while read Y; do 1423 if ! [ -f ${Y}.gz ]; then 1424 echo "failed." 1425 return 1 1426 fi 1427 if [ `gunzip -c < ${Y}.gz | 1428 ${SHA256} -q` = ${Y} ]; then 1429 mv ${Y}.gz files/${Y}.gz 1430 else 1431 echo "metadata is corrupt." 1432 return 1 1433 fi 1434 done < filelist 1435 echo "done." 1436 fi 1437 1438# Sanity-check the metadata files. 1439 cut -f 2 -d '|' tINDEX.new > filelist 1440 while read X; do 1441 fetch_metadata_sanity ${X} || return 1 1442 done < filelist 1443 1444# Remove files which are no longer needed 1445 cut -f 2 -d '|' tINDEX.present | 1446 sort > oldfiles 1447 cut -f 2 -d '|' tINDEX.new | 1448 sort | 1449 comm -13 - oldfiles | 1450 lam -s "files/" - -s ".gz" | 1451 xargs rm -f 1452 rm patchlist filelist oldfiles 1453 rm ${TINDEXHASH} 1454 1455# We're done! 1456 mv tINDEX.new tINDEX.present 1457 mv tag.new tag 1458 1459 return 0 1460} 1461 1462# Extract a subset of a downloaded metadata file containing only the parts 1463# which are listed in COMPONENTS. 1464fetch_filter_metadata_components () { 1465 METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'` 1466 gunzip -c < files/${METAHASH}.gz > $1.all 1467 1468 # Fish out the lines belonging to components we care about. 1469 for C in ${COMPONENTS}; do 1470 look "`echo ${C} | tr '/' '|'`|" $1.all 1471 done > $1 1472 1473 # Remove temporary file. 1474 rm $1.all 1475} 1476 1477# Generate a filtered version of the metadata file $1 from the downloaded 1478# file, by fishing out the lines corresponding to components we're trying 1479# to keep updated, and then removing lines corresponding to paths we want 1480# to ignore. 1481fetch_filter_metadata () { 1482 # Fish out the lines belonging to components we care about. 1483 fetch_filter_metadata_components $1 1484 1485 # Canonicalize directory names by removing any trailing / in 1486 # order to avoid listing directories multiple times if they 1487 # belong to multiple components. Turning "/" into "" doesn't 1488 # matter, since we add a leading "/" when we use paths later. 1489 cut -f 3- -d '|' $1 | 1490 sed -e 's,/|d|,|d|,' | 1491 sed -e 's,/|-|,|-|,' | 1492 sort -u > $1.tmp 1493 1494 # Figure out which lines to ignore and remove them. 1495 for X in ${IGNOREPATHS}; do 1496 grep -E "^${X}" $1.tmp 1497 done | 1498 sort -u | 1499 comm -13 - $1.tmp > $1 1500 1501 # Remove temporary files. 1502 rm $1.tmp 1503} 1504 1505# Filter the metadata file $1 by adding lines with "/boot/$2" 1506# replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the 1507# trailing "/kernel"); and if "/boot/$2" does not exist, remove 1508# the original lines which start with that. 1509# Put another way: Deal with the fact that the FOO kernel is sometimes 1510# installed in /boot/FOO/ and is sometimes installed elsewhere. 1511fetch_filter_kernel_names () { 1512 grep ^/boot/$2 $1 | 1513 sed -e "s,/boot/$2,${KERNELDIR},g" | 1514 sort - $1 > $1.tmp 1515 mv $1.tmp $1 1516 1517 if ! [ -d /boot/$2 ]; then 1518 grep -v ^/boot/$2 $1 > $1.tmp 1519 mv $1.tmp $1 1520 fi 1521} 1522 1523# For all paths appearing in $1 or $3, inspect the system 1524# and generate $2 describing what is currently installed. 1525fetch_inspect_system () { 1526 # No errors yet... 1527 rm -f .err 1528 1529 # Tell the user why his disk is suddenly making lots of noise 1530 echo -n "Inspecting system... " 1531 1532 # Generate list of files to inspect 1533 cat $1 $3 | 1534 cut -f 1 -d '|' | 1535 sort -u > filelist 1536 1537 # Examine each file and output lines of the form 1538 # /path/to/file|type|device-inum|user|group|perm|flags|value 1539 # sorted by device and inode number. 1540 while read F; do 1541 # If the symlink/file/directory does not exist, record this. 1542 if ! [ -e ${BASEDIR}/${F} ]; then 1543 echo "${F}|-||||||" 1544 continue 1545 fi 1546 if ! [ -r ${BASEDIR}/${F} ]; then 1547 echo "Cannot read file: ${BASEDIR}/${F}" \ 1548 >/dev/stderr 1549 touch .err 1550 return 1 1551 fi 1552 1553 # Otherwise, output an index line. 1554 if [ -L ${BASEDIR}/${F} ]; then 1555 echo -n "${F}|L|" 1556 stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; 1557 readlink ${BASEDIR}/${F}; 1558 elif [ -f ${BASEDIR}/${F} ]; then 1559 echo -n "${F}|f|" 1560 stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; 1561 sha256 -q ${BASEDIR}/${F}; 1562 elif [ -d ${BASEDIR}/${F} ]; then 1563 echo -n "${F}|d|" 1564 stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; 1565 else 1566 echo "Unknown file type: ${BASEDIR}/${F}" \ 1567 >/dev/stderr 1568 touch .err 1569 return 1 1570 fi 1571 done < filelist | 1572 sort -k 3,3 -t '|' > $2.tmp 1573 rm filelist 1574 1575 # Check if an error occurred during system inspection 1576 if [ -f .err ]; then 1577 return 1 1578 fi 1579 1580 # Convert to the form 1581 # /path/to/file|type|user|group|perm|flags|value|hlink 1582 # by resolving identical device and inode numbers into hard links. 1583 cut -f 1,3 -d '|' $2.tmp | 1584 sort -k 1,1 -t '|' | 1585 sort -s -u -k 2,2 -t '|' | 1586 join -1 2 -2 3 -t '|' - $2.tmp | 1587 awk -F \| -v OFS=\| \ 1588 '{ 1589 if (($2 == $3) || ($4 == "-")) 1590 print $3,$4,$5,$6,$7,$8,$9,"" 1591 else 1592 print $3,$4,$5,$6,$7,$8,$9,$2 1593 }' | 1594 sort > $2 1595 rm $2.tmp 1596 1597 # We're finished looking around 1598 echo "done." 1599} 1600 1601# For any paths matching ${MERGECHANGES}, compare $1 and $2 and find any 1602# files which differ; generate $3 containing these paths and the old hashes. 1603fetch_filter_mergechanges () { 1604 # Pull out the paths and hashes of the files matching ${MERGECHANGES}. 1605 for F in $1 $2; do 1606 for X in ${MERGECHANGES}; do 1607 grep -E "^${X}" ${F} 1608 done | 1609 cut -f 1,2,7 -d '|' | 1610 sort > ${F}-values 1611 done 1612 1613 # Any line in $2-values which doesn't appear in $1-values and is a 1614 # file means that we should list the path in $3. 1615 comm -13 $1-values $2-values | 1616 fgrep '|f|' | 1617 cut -f 1 -d '|' > $2-paths 1618 1619 # For each path, pull out one (and only one!) entry from $1-values. 1620 # Note that we cannot distinguish which "old" version the user made 1621 # changes to; but hopefully any changes which occur due to security 1622 # updates will exist in both the "new" version and the version which 1623 # the user has installed, so the merging will still work. 1624 while read X; do 1625 look "${X}|" $1-values | 1626 head -1 1627 done < $2-paths > $3 1628 1629 # Clean up 1630 rm $1-values $2-values $2-paths 1631} 1632 1633# For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123] 1634# which correspond to lines in $2 with hashes not matching $1 or $3, unless 1635# the paths are listed in $4. For entries in $2 marked "not present" 1636# (aka. type -), remove lines from $[123] unless there is a corresponding 1637# entry in $1. 1638fetch_filter_unmodified_notpresent () { 1639 # Figure out which lines of $1 and $3 correspond to bits which 1640 # should only be updated if they haven't changed, and fish out 1641 # the (path, type, value) tuples. 1642 # NOTE: We don't consider a file to be "modified" if it matches 1643 # the hash from $3. 1644 for X in ${UPDATEIFUNMODIFIED}; do 1645 grep -E "^${X}" $1 1646 grep -E "^${X}" $3 1647 done | 1648 cut -f 1,2,7 -d '|' | 1649 sort > $1-values 1650 1651 # Do the same for $2. 1652 for X in ${UPDATEIFUNMODIFIED}; do 1653 grep -E "^${X}" $2 1654 done | 1655 cut -f 1,2,7 -d '|' | 1656 sort > $2-values 1657 1658 # Any entry in $2-values which is not in $1-values corresponds to 1659 # a path which we need to remove from $1, $2, and $3, unless it 1660 # that path appears in $4. 1661 comm -13 $1-values $2-values | 1662 sort -t '|' -k 1,1 > mlines.tmp 1663 cut -f 1 -d '|' $4 | 1664 sort | 1665 join -v 2 -t '|' - mlines.tmp | 1666 sort > mlines 1667 rm $1-values $2-values mlines.tmp 1668 1669 # Any lines in $2 which are not in $1 AND are "not present" lines 1670 # also belong in mlines. 1671 comm -13 $1 $2 | 1672 cut -f 1,2,7 -d '|' | 1673 fgrep '|-|' >> mlines 1674 1675 # Remove lines from $1, $2, and $3 1676 for X in $1 $2 $3; do 1677 sort -t '|' -k 1,1 ${X} > ${X}.tmp 1678 cut -f 1 -d '|' < mlines | 1679 sort | 1680 join -v 2 -t '|' - ${X}.tmp | 1681 sort > ${X} 1682 rm ${X}.tmp 1683 done 1684 1685 # Store a list of the modified files, for future reference 1686 fgrep -v '|-|' mlines | 1687 cut -f 1 -d '|' > modifiedfiles 1688 rm mlines 1689} 1690 1691# For each entry in $1 of type -, remove any corresponding 1692# entry from $2 if ${ALLOWADD} != "yes". Remove all entries 1693# of type - from $1. 1694fetch_filter_allowadd () { 1695 cut -f 1,2 -d '|' < $1 | 1696 fgrep '|-' | 1697 cut -f 1 -d '|' > filesnotpresent 1698 1699 if [ ${ALLOWADD} != "yes" ]; then 1700 sort < $2 | 1701 join -v 1 -t '|' - filesnotpresent | 1702 sort > $2.tmp 1703 mv $2.tmp $2 1704 fi 1705 1706 sort < $1 | 1707 join -v 1 -t '|' - filesnotpresent | 1708 sort > $1.tmp 1709 mv $1.tmp $1 1710 rm filesnotpresent 1711} 1712 1713# If ${ALLOWDELETE} != "yes", then remove any entries from $1 1714# which don't correspond to entries in $2. 1715fetch_filter_allowdelete () { 1716 # Produce a lists ${PATH}|${TYPE} 1717 for X in $1 $2; do 1718 cut -f 1-2 -d '|' < ${X} | 1719 sort -u > ${X}.nodes 1720 done 1721 1722 # Figure out which lines need to be removed from $1. 1723 if [ ${ALLOWDELETE} != "yes" ]; then 1724 comm -23 $1.nodes $2.nodes > $1.badnodes 1725 else 1726 : > $1.badnodes 1727 fi 1728 1729 # Remove the relevant lines from $1 1730 while read X; do 1731 look "${X}|" $1 1732 done < $1.badnodes | 1733 comm -13 - $1 > $1.tmp 1734 mv $1.tmp $1 1735 1736 rm $1.badnodes $1.nodes $2.nodes 1737} 1738 1739# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2 1740# with metadata not matching any entry in $1, replace the corresponding 1741# line of $3 with one having the same metadata as the entry in $2. 1742fetch_filter_modified_metadata () { 1743 # Fish out the metadata from $1 and $2 1744 for X in $1 $2; do 1745 cut -f 1-6 -d '|' < ${X} > ${X}.metadata 1746 done 1747 1748 # Find the metadata we need to keep 1749 if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then 1750 comm -13 $1.metadata $2.metadata > keepmeta 1751 else 1752 : > keepmeta 1753 fi 1754 1755 # Extract the lines which we need to remove from $3, and 1756 # construct the lines which we need to add to $3. 1757 : > $3.remove 1758 : > $3.add 1759 while read LINE; do 1760 NODE=`echo "${LINE}" | cut -f 1-2 -d '|'` 1761 look "${NODE}|" $3 >> $3.remove 1762 look "${NODE}|" $3 | 1763 cut -f 7- -d '|' | 1764 lam -s "${LINE}|" - >> $3.add 1765 done < keepmeta 1766 1767 # Remove the specified lines and add the new lines. 1768 sort $3.remove | 1769 comm -13 - $3 | 1770 sort -u - $3.add > $3.tmp 1771 mv $3.tmp $3 1772 1773 rm keepmeta $1.metadata $2.metadata $3.add $3.remove 1774} 1775 1776# Remove lines from $1 and $2 which are identical; 1777# no need to update a file if it isn't changing. 1778fetch_filter_uptodate () { 1779 comm -23 $1 $2 > $1.tmp 1780 comm -13 $1 $2 > $2.tmp 1781 1782 mv $1.tmp $1 1783 mv $2.tmp $2 1784} 1785 1786# Fetch any "clean" old versions of files we need for merging changes. 1787fetch_files_premerge () { 1788 # We only need to do anything if $1 is non-empty. 1789 if [ -s $1 ]; then 1790 # Tell the user what we're doing 1791 echo -n "Fetching files from ${OLDRELNUM} for merging... " 1792 1793 # List of files wanted 1794 fgrep '|f|' < $1 | 1795 cut -f 3 -d '|' | 1796 sort -u > files.wanted 1797 1798 # Only fetch the files we don't already have 1799 while read Y; do 1800 if [ ! -f "files/${Y}.gz" ]; then 1801 echo ${Y}; 1802 fi 1803 done < files.wanted > filelist 1804 1805 # Actually fetch them 1806 lam -s "${OLDFETCHDIR}/f/" - -s ".gz" < filelist | 1807 xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 1808 2>${QUIETREDIR} 1809 1810 # Make sure we got them all, and move them into /files/ 1811 while read Y; do 1812 if ! [ -f ${Y}.gz ]; then 1813 echo "failed." 1814 return 1 1815 fi 1816 if [ `gunzip -c < ${Y}.gz | 1817 ${SHA256} -q` = ${Y} ]; then 1818 mv ${Y}.gz files/${Y}.gz 1819 else 1820 echo "${Y} has incorrect hash." 1821 return 1 1822 fi 1823 done < filelist 1824 echo "done." 1825 1826 # Clean up 1827 rm filelist files.wanted 1828 fi 1829} 1830 1831# Prepare to fetch files: Generate a list of the files we need, 1832# copy the unmodified files we have into /files/, and generate 1833# a list of patches to download. 1834fetch_files_prepare () { 1835 # Tell the user why his disk is suddenly making lots of noise 1836 echo -n "Preparing to download files... " 1837 1838 # Reduce indices to ${PATH}|${HASH} pairs 1839 for X in $1 $2 $3; do 1840 cut -f 1,2,7 -d '|' < ${X} | 1841 fgrep '|f|' | 1842 cut -f 1,3 -d '|' | 1843 sort > ${X}.hashes 1844 done 1845 1846 # List of files wanted 1847 cut -f 2 -d '|' < $3.hashes | 1848 sort -u | 1849 while read HASH; do 1850 if ! [ -f files/${HASH}.gz ]; then 1851 echo ${HASH} 1852 fi 1853 done > files.wanted 1854 1855 # Generate a list of unmodified files 1856 comm -12 $1.hashes $2.hashes | 1857 sort -k 1,1 -t '|' > unmodified.files 1858 1859 # Copy all files into /files/. We only need the unmodified files 1860 # for use in patching; but we'll want all of them if the user asks 1861 # to rollback the updates later. 1862 while read LINE; do 1863 F=`echo "${LINE}" | cut -f 1 -d '|'` 1864 HASH=`echo "${LINE}" | cut -f 2 -d '|'` 1865 1866 # Skip files we already have. 1867 if [ -f files/${HASH}.gz ]; then 1868 continue 1869 fi 1870 1871 # Make sure the file hasn't changed. 1872 cp "${BASEDIR}/${F}" tmpfile 1873 if [ `sha256 -q tmpfile` != ${HASH} ]; then 1874 echo 1875 echo "File changed while FreeBSD Update running: ${F}" 1876 return 1 1877 fi 1878 1879 # Place the file into storage. 1880 gzip -c < tmpfile > files/${HASH}.gz 1881 rm tmpfile 1882 done < $2.hashes 1883 1884 # Produce a list of patches to download 1885 sort -k 1,1 -t '|' $3.hashes | 1886 join -t '|' -o 2.2,1.2 - unmodified.files | 1887 fetch_make_patchlist > patchlist 1888 1889 # Garbage collect 1890 rm unmodified.files $1.hashes $2.hashes $3.hashes 1891 1892 # We don't need the list of possible old files any more. 1893 rm $1 1894 1895 # We're finished making noise 1896 echo "done." 1897} 1898 1899# Fetch files. 1900fetch_files () { 1901 # Attempt to fetch patches 1902 if [ -s patchlist ]; then 1903 echo -n "Fetching `wc -l < patchlist | tr -d ' '` " 1904 echo ${NDEBUG} "patches.${DDSTATS}" 1905 tr '|' '-' < patchlist | 1906 lam -s "${PATCHDIR}/" - | 1907 xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 1908 2>${STATSREDIR} | fetch_progress 1909 echo "done." 1910 1911 # Attempt to apply patches 1912 echo -n "Applying patches... " 1913 tr '|' ' ' < patchlist | 1914 while read X Y; do 1915 if [ ! -f "${X}-${Y}" ]; then continue; fi 1916 gunzip -c < files/${X}.gz > OLD 1917 1918 bspatch OLD NEW ${X}-${Y} 1919 1920 if [ `${SHA256} -q NEW` = ${Y} ]; then 1921 mv NEW files/${Y} 1922 gzip -n files/${Y} 1923 fi 1924 rm -f diff OLD NEW ${X}-${Y} 1925 done 2>${QUIETREDIR} 1926 echo "done." 1927 fi 1928 1929 # Download files which couldn't be generate via patching 1930 while read Y; do 1931 if [ ! -f "files/${Y}.gz" ]; then 1932 echo ${Y}; 1933 fi 1934 done < files.wanted > filelist 1935 1936 if [ -s filelist ]; then 1937 echo -n "Fetching `wc -l < filelist | tr -d ' '` " 1938 echo ${NDEBUG} "files... " 1939 lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist | 1940 xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 1941 2>${STATSREDIR} | fetch_progress 1942 1943 while read Y; do 1944 if ! [ -f ${Y}.gz ]; then 1945 echo "failed." 1946 return 1 1947 fi 1948 if [ `gunzip -c < ${Y}.gz | 1949 ${SHA256} -q` = ${Y} ]; then 1950 mv ${Y}.gz files/${Y}.gz 1951 else 1952 echo "${Y} has incorrect hash." 1953 return 1 1954 fi 1955 done < filelist 1956 echo "done." 1957 fi 1958 1959 # Clean up 1960 rm files.wanted filelist patchlist 1961} 1962 1963# Create and populate install manifest directory; and report what updates 1964# are available. 1965fetch_create_manifest () { 1966 # If we have an existing install manifest, nuke it. 1967 if [ -L "${BDHASH}-install" ]; then 1968 rm -r ${BDHASH}-install/ 1969 rm ${BDHASH}-install 1970 fi 1971 1972 # Report to the user if any updates were avoided due to local changes 1973 if [ -s modifiedfiles ]; then 1974 cat - modifiedfiles <<- EOF | ${PAGER} 1975 The following files are affected by updates. No changes have 1976 been downloaded, however, because the files have been modified 1977 locally: 1978 EOF 1979 fi 1980 rm modifiedfiles 1981 1982 # If no files will be updated, tell the user and exit 1983 if ! [ -s INDEX-PRESENT ] && 1984 ! [ -s INDEX-NEW ]; then 1985 rm INDEX-PRESENT INDEX-NEW 1986 echo 1987 echo -n "No updates needed to update system to " 1988 echo "${RELNUM}-p${RELPATCHNUM}." 1989 return 1990 fi 1991 1992 # Divide files into (a) removed files, (b) added files, and 1993 # (c) updated files. 1994 cut -f 1 -d '|' < INDEX-PRESENT | 1995 sort > INDEX-PRESENT.flist 1996 cut -f 1 -d '|' < INDEX-NEW | 1997 sort > INDEX-NEW.flist 1998 comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed 1999 comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added 2000 comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated 2001 rm INDEX-PRESENT.flist INDEX-NEW.flist 2002 2003 # Report removed files, if any 2004 if [ -s files.removed ]; then 2005 cat - files.removed <<- EOF | ${PAGER} 2006 The following files will be removed as part of updating to 2007 ${RELNUM}-p${RELPATCHNUM}: 2008 EOF 2009 fi 2010 rm files.removed 2011 2012 # Report added files, if any 2013 if [ -s files.added ]; then 2014 cat - files.added <<- EOF | ${PAGER} 2015 The following files will be added as part of updating to 2016 ${RELNUM}-p${RELPATCHNUM}: 2017 EOF 2018 fi 2019 rm files.added 2020 2021 # Report updated files, if any 2022 if [ -s files.updated ]; then 2023 cat - files.updated <<- EOF | ${PAGER} 2024 The following files will be updated as part of updating to 2025 ${RELNUM}-p${RELPATCHNUM}: 2026 EOF 2027 fi 2028 rm files.updated 2029 2030 # Create a directory for the install manifest. 2031 MDIR=`mktemp -d install.XXXXXX` || return 1 2032 2033 # Populate it 2034 mv INDEX-PRESENT ${MDIR}/INDEX-OLD 2035 mv INDEX-NEW ${MDIR}/INDEX-NEW 2036 2037 # Link it into place 2038 ln -s ${MDIR} ${BDHASH}-install 2039} 2040 2041# Warn about any upcoming EoL 2042fetch_warn_eol () { 2043 # What's the current time? 2044 NOWTIME=`date "+%s"` 2045 2046 # When did we last warn about the EoL date? 2047 if [ -f lasteolwarn ]; then 2048 LASTWARN=`cat lasteolwarn` 2049 else 2050 LASTWARN=`expr ${NOWTIME} - 63072000` 2051 fi 2052 2053 # If the EoL time is past, warn. 2054 if [ ${EOLTIME} -lt ${NOWTIME} ]; then 2055 echo 2056 cat <<-EOF 2057 WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE. 2058 Any security issues discovered after `date -r ${EOLTIME}` 2059 will not have been corrected. 2060 EOF 2061 return 1 2062 fi 2063 2064 # Figure out how long it has been since we last warned about the 2065 # upcoming EoL, and how much longer we have left. 2066 SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}` 2067 TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}` 2068 2069 # Don't warn if the EoL is more than 3 months away 2070 if [ ${TIMELEFT} -gt 7884000 ]; then 2071 return 0 2072 fi 2073 2074 # Don't warn if the time remaining is more than 3 times the time 2075 # since the last warning. 2076 if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then 2077 return 0 2078 fi 2079 2080 # Figure out what time units to use. 2081 if [ ${TIMELEFT} -lt 604800 ]; then 2082 UNIT="day" 2083 SIZE=86400 2084 elif [ ${TIMELEFT} -lt 2678400 ]; then 2085 UNIT="week" 2086 SIZE=604800 2087 else 2088 UNIT="month" 2089 SIZE=2678400 2090 fi 2091 2092 # Compute the right number of units 2093 NUM=`expr ${TIMELEFT} / ${SIZE}` 2094 if [ ${NUM} != 1 ]; then 2095 UNIT="${UNIT}s" 2096 fi 2097 2098 # Print the warning 2099 echo 2100 cat <<-EOF 2101 WARNING: `uname -sr` is approaching its End-of-Life date. 2102 It is strongly recommended that you upgrade to a newer 2103 release within the next ${NUM} ${UNIT}. 2104 EOF 2105 2106 # Update the stored time of last warning 2107 echo ${NOWTIME} > lasteolwarn 2108} 2109 2110# Do the actual work involved in "fetch" / "cron". 2111fetch_run () { 2112 workdir_init || return 1 2113 2114 # Prepare the mirror list. 2115 fetch_pick_server_init && fetch_pick_server 2116 2117 # Try to fetch the public key until we run out of servers. 2118 while ! fetch_key; do 2119 fetch_pick_server || return 1 2120 done 2121 2122 # Try to fetch the metadata index signature ("tag") until we run 2123 # out of available servers; and sanity check the downloaded tag. 2124 while ! fetch_tag; do 2125 fetch_pick_server || return 1 2126 done 2127 fetch_tagsanity || return 1 2128 2129 # Fetch the latest INDEX-NEW and INDEX-OLD files. 2130 fetch_metadata INDEX-NEW INDEX-OLD || return 1 2131 2132 # Generate filtered INDEX-NEW and INDEX-OLD files containing only 2133 # the lines which (a) belong to components we care about, and (b) 2134 # don't correspond to paths we're explicitly ignoring. 2135 fetch_filter_metadata INDEX-NEW || return 1 2136 fetch_filter_metadata INDEX-OLD || return 1 2137 2138 # Translate /boot/${KERNCONF} into ${KERNELDIR} 2139 fetch_filter_kernel_names INDEX-NEW ${KERNCONF} 2140 fetch_filter_kernel_names INDEX-OLD ${KERNCONF} 2141 2142 # For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the 2143 # system and generate an INDEX-PRESENT file. 2144 fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 2145 2146 # Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which 2147 # correspond to lines in INDEX-PRESENT with hashes not appearing 2148 # in INDEX-OLD or INDEX-NEW. Also remove lines where the entry in 2149 # INDEX-PRESENT has type - and there isn't a corresponding entry in 2150 # INDEX-OLD with type -. 2151 fetch_filter_unmodified_notpresent \ 2152 INDEX-OLD INDEX-PRESENT INDEX-NEW /dev/null 2153 2154 # For each entry in INDEX-PRESENT of type -, remove any corresponding 2155 # entry from INDEX-NEW if ${ALLOWADD} != "yes". Remove all entries 2156 # of type - from INDEX-PRESENT. 2157 fetch_filter_allowadd INDEX-PRESENT INDEX-NEW 2158 2159 # If ${ALLOWDELETE} != "yes", then remove any entries from 2160 # INDEX-PRESENT which don't correspond to entries in INDEX-NEW. 2161 fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW 2162 2163 # If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in 2164 # INDEX-PRESENT with metadata not matching any entry in INDEX-OLD, 2165 # replace the corresponding line of INDEX-NEW with one having the 2166 # same metadata as the entry in INDEX-PRESENT. 2167 fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW 2168 2169 # Remove lines from INDEX-PRESENT and INDEX-NEW which are identical; 2170 # no need to update a file if it isn't changing. 2171 fetch_filter_uptodate INDEX-PRESENT INDEX-NEW 2172 2173 # Prepare to fetch files: Generate a list of the files we need, 2174 # copy the unmodified files we have into /files/, and generate 2175 # a list of patches to download. 2176 fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 2177 2178 # Fetch files. 2179 fetch_files || return 1 2180 2181 # Create and populate install manifest directory; and report what 2182 # updates are available. 2183 fetch_create_manifest || return 1 2184 2185 # Warn about any upcoming EoL 2186 fetch_warn_eol || return 1 2187} 2188 2189# If StrictComponents is not "yes", generate a new components list 2190# with only the components which appear to be installed. 2191upgrade_guess_components () { 2192 if [ "${STRICTCOMPONENTS}" = "no" ]; then 2193 # Generate filtered INDEX-ALL with only the components listed 2194 # in COMPONENTS. 2195 fetch_filter_metadata_components $1 || return 1 2196 2197 # Tell the user why his disk is suddenly making lots of noise 2198 echo -n "Inspecting system... " 2199 2200 # Look at the files on disk, and assume that a component is 2201 # supposed to be present if it is more than half-present. 2202 cut -f 1-3 -d '|' < INDEX-ALL | 2203 tr '|' ' ' | 2204 while read C S F; do 2205 if [ -e ${BASEDIR}/${F} ]; then 2206 echo "+ ${C}|${S}" 2207 fi 2208 echo "= ${C}|${S}" 2209 done | 2210 sort | 2211 uniq -c | 2212 sed -E 's,^ +,,' > compfreq 2213 grep ' = ' compfreq | 2214 cut -f 1,3 -d ' ' | 2215 sort -k 2,2 -t ' ' > compfreq.total 2216 grep ' + ' compfreq | 2217 cut -f 1,3 -d ' ' | 2218 sort -k 2,2 -t ' ' > compfreq.present 2219 join -t ' ' -1 2 -2 2 compfreq.present compfreq.total | 2220 while read S P T; do 2221 if [ ${T} -ne 0 -a ${P} -gt `expr ${T} / 2` ]; then 2222 echo ${S} 2223 fi 2224 done > comp.present 2225 cut -f 2 -d ' ' < compfreq.total > comp.total 2226 rm INDEX-ALL compfreq compfreq.total compfreq.present 2227 2228 # We're done making noise. 2229 echo "done." 2230 2231 # Sometimes the kernel isn't installed where INDEX-ALL 2232 # thinks that it should be: In particular, it is often in 2233 # /boot/kernel instead of /boot/GENERIC or /boot/SMP. To 2234 # deal with this, if "kernel|X" is listed in comp.total 2235 # (i.e., is a component which would be upgraded if it is 2236 # found to be present) we will add it to comp.present. 2237 # If "kernel|<anything>" is in comp.total but "kernel|X" is 2238 # not, we print a warning -- the user is running a kernel 2239 # which isn't part of the release. 2240 KCOMP=`echo ${KERNCONF} | tr 'A-Z' 'a-z'` 2241 grep -E "^kernel\|${KCOMP}\$" comp.total >> comp.present 2242 2243 if grep -qE "^kernel\|" comp.total && 2244 ! grep -qE "^kernel\|${KCOMP}\$" comp.total; then 2245 cat <<-EOF 2246 2247WARNING: This system is running a "${KCOMP}" kernel, which is not a 2248kernel configuration distributed as part of FreeBSD ${RELNUM}. 2249This kernel will not be updated: you MUST update the kernel manually 2250before running "$0 install". 2251 EOF 2252 fi 2253 2254 # Re-sort the list of installed components and generate 2255 # the list of non-installed components. 2256 sort -u < comp.present > comp.present.tmp 2257 mv comp.present.tmp comp.present 2258 comm -13 comp.present comp.total > comp.absent 2259 2260 # Ask the user to confirm that what we have is correct. To 2261 # reduce user confusion, translate "X|Y" back to "X/Y" (as 2262 # subcomponents must be listed in the configuration file). 2263 echo 2264 echo -n "The following components of FreeBSD " 2265 echo "seem to be installed:" 2266 tr '|' '/' < comp.present | 2267 fmt -72 2268 echo 2269 echo -n "The following components of FreeBSD " 2270 echo "do not seem to be installed:" 2271 tr '|' '/' < comp.absent | 2272 fmt -72 2273 echo 2274 continuep || return 1 2275 echo 2276 2277 # Suck the generated list of components into ${COMPONENTS}. 2278 # Note that comp.present.tmp is used due to issues with 2279 # pipelines and setting variables. 2280 COMPONENTS="" 2281 tr '|' '/' < comp.present > comp.present.tmp 2282 while read C; do 2283 COMPONENTS="${COMPONENTS} ${C}" 2284 done < comp.present.tmp 2285 2286 # Delete temporary files 2287 rm comp.present comp.present.tmp comp.absent comp.total 2288 fi 2289} 2290 2291# If StrictComponents is not "yes", COMPONENTS contains an entry 2292# corresponding to the currently running kernel, and said kernel 2293# does not exist in the new release, add "kernel/generic" to the 2294# list of components. 2295upgrade_guess_new_kernel () { 2296 if [ "${STRICTCOMPONENTS}" = "no" ]; then 2297 # Grab the unfiltered metadata file. 2298 METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'` 2299 gunzip -c < files/${METAHASH}.gz > $1.all 2300 2301 # If "kernel/${KCOMP}" is in ${COMPONENTS} and that component 2302 # isn't in $1.all, we need to add kernel/generic. 2303 for C in ${COMPONENTS}; do 2304 if [ ${C} = "kernel/${KCOMP}" ] && 2305 ! grep -qE "^kernel\|${KCOMP}\|" $1.all; then 2306 COMPONENTS="${COMPONENTS} kernel/generic" 2307 NKERNCONF="GENERIC" 2308 cat <<-EOF 2309 2310WARNING: This system is running a "${KCOMP}" kernel, which is not a 2311kernel configuration distributed as part of FreeBSD ${RELNUM}. 2312As part of upgrading to FreeBSD ${RELNUM}, this kernel will be 2313replaced with a "generic" kernel. 2314 EOF 2315 continuep || return 1 2316 fi 2317 done 2318 2319 # Don't need this any more... 2320 rm $1.all 2321 fi 2322} 2323 2324# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into 2325# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades). 2326upgrade_oldall_to_oldnew () { 2327 # For each ${F}|... which appears in INDEX-ALL but does not appear 2328 # in INDEX-OLD, add ${F}|-|||||| to INDEX-OLD. 2329 cut -f 1 -d '|' < $1 | 2330 sort -u > $1.paths 2331 cut -f 1 -d '|' < $2 | 2332 sort -u | 2333 comm -13 $1.paths - | 2334 lam - -s "|-||||||" | 2335 sort - $1 > $1.tmp 2336 mv $1.tmp $1 2337 2338 # Remove lines from INDEX-OLD which also appear in INDEX-ALL 2339 comm -23 $1 $2 > $1.tmp 2340 mv $1.tmp $1 2341 2342 # Remove lines from INDEX-ALL which have a file name not appearing 2343 # anywhere in INDEX-OLD (since these must be files which haven't 2344 # changed -- if they were new, there would be an entry of type "-"). 2345 cut -f 1 -d '|' < $1 | 2346 sort -u > $1.paths 2347 sort -k 1,1 -t '|' < $2 | 2348 join -t '|' - $1.paths | 2349 sort > $2.tmp 2350 rm $1.paths 2351 mv $2.tmp $2 2352 2353 # Rename INDEX-ALL to INDEX-NEW. 2354 mv $2 $3 2355} 2356 2357# Helper for upgrade_merge: Return zero true iff the two files differ only 2358# in the contents of their RCS tags. 2359samef () { 2360 X=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $1 | ${SHA256}` 2361 Y=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $2 | ${SHA256}` 2362 2363 if [ $X = $Y ]; then 2364 return 0; 2365 else 2366 return 1; 2367 fi 2368} 2369 2370# From the list of "old" files in $1, merge changes in $2 with those in $3, 2371# and update $3 to reflect the hashes of merged files. 2372upgrade_merge () { 2373 # We only need to do anything if $1 is non-empty. 2374 if [ -s $1 ]; then 2375 cut -f 1 -d '|' $1 | 2376 sort > $1-paths 2377 2378 # Create staging area for merging files 2379 rm -rf merge/ 2380 while read F; do 2381 D=`dirname ${F}` 2382 mkdir -p merge/old/${D} 2383 mkdir -p merge/${OLDRELNUM}/${D} 2384 mkdir -p merge/${RELNUM}/${D} 2385 mkdir -p merge/new/${D} 2386 done < $1-paths 2387 2388 # Copy in files 2389 while read F; do 2390 # Currently installed file 2391 V=`look "${F}|" $2 | cut -f 7 -d '|'` 2392 gunzip < files/${V}.gz > merge/old/${F} 2393 2394 # Old release 2395 if look "${F}|" $1 | fgrep -q "|f|"; then 2396 V=`look "${F}|" $1 | cut -f 3 -d '|'` 2397 gunzip < files/${V}.gz \ 2398 > merge/${OLDRELNUM}/${F} 2399 fi 2400 2401 # New release 2402 if look "${F}|" $3 | cut -f 1,2,7 -d '|' | 2403 fgrep -q "|f|"; then 2404 V=`look "${F}|" $3 | cut -f 7 -d '|'` 2405 gunzip < files/${V}.gz \ 2406 > merge/${RELNUM}/${F} 2407 fi 2408 done < $1-paths 2409 2410 # Attempt to automatically merge changes 2411 echo -n "Attempting to automatically merge " 2412 echo -n "changes in files..." 2413 : > failed.merges 2414 while read F; do 2415 # If the file doesn't exist in the new release, 2416 # the result of "merging changes" is having the file 2417 # not exist. 2418 if ! [ -f merge/${RELNUM}/${F} ]; then 2419 continue 2420 fi 2421 2422 # If the file didn't exist in the old release, we're 2423 # going to throw away the existing file and hope that 2424 # the version from the new release is what we want. 2425 if ! [ -f merge/${OLDRELNUM}/${F} ]; then 2426 cp merge/${RELNUM}/${F} merge/new/${F} 2427 continue 2428 fi 2429 2430 # Some files need special treatment. 2431 case ${F} in 2432 /etc/spwd.db | /etc/pwd.db | /etc/login.conf.db) 2433 # Don't merge these -- we're rebuild them 2434 # after updates are installed. 2435 cp merge/old/${F} merge/new/${F} 2436 ;; 2437 *) 2438 if ! diff3 -E -m -L "current version" \ 2439 -L "${OLDRELNUM}" -L "${RELNUM}" \ 2440 merge/old/${F} \ 2441 merge/${OLDRELNUM}/${F} \ 2442 merge/${RELNUM}/${F} \ 2443 > merge/new/${F} 2>/dev/null; then 2444 echo ${F} >> failed.merges 2445 fi 2446 ;; 2447 esac 2448 done < $1-paths 2449 echo " done." 2450 2451 # Ask the user to handle any files which didn't merge. 2452 while read F; do 2453 # If the installed file differs from the version in 2454 # the old release only due to RCS tag expansion 2455 # then just use the version in the new release. 2456 if samef merge/old/${F} merge/${OLDRELNUM}/${F}; then 2457 cp merge/${RELNUM}/${F} merge/new/${F} 2458 continue 2459 fi 2460 2461 cat <<-EOF 2462 2463The following file could not be merged automatically: ${F} 2464Press Enter to edit this file in ${EDITOR} and resolve the conflicts 2465manually... 2466 EOF 2467 read dummy </dev/tty 2468 ${EDITOR} `pwd`/merge/new/${F} < /dev/tty 2469 done < failed.merges 2470 rm failed.merges 2471 2472 # Ask the user to confirm that he likes how the result 2473 # of merging files. 2474 while read F; do 2475 # Skip files which haven't changed except possibly 2476 # in their RCS tags. 2477 if [ -f merge/old/${F} ] && [ -f merge/new/${F} ] && 2478 samef merge/old/${F} merge/new/${F}; then 2479 continue 2480 fi 2481 2482 # Skip files where the installed file differs from 2483 # the old file only due to RCS tags. 2484 if [ -f merge/old/${F} ] && 2485 [ -f merge/${OLDRELNUM}/${F} ] && 2486 samef merge/old/${F} merge/${OLDRELNUM}/${F}; then 2487 continue 2488 fi 2489 2490 # Warn about files which are ceasing to exist. 2491 if ! [ -f merge/new/${F} ]; then 2492 cat <<-EOF 2493 2494The following file will be removed, as it no longer exists in 2495FreeBSD ${RELNUM}: ${F} 2496 EOF 2497 continuep < /dev/tty || return 1 2498 continue 2499 fi 2500 2501 # Print changes for the user's approval. 2502 cat <<-EOF 2503 2504The following changes, which occurred between FreeBSD ${OLDRELNUM} and 2505FreeBSD ${RELNUM} have been merged into ${F}: 2506EOF 2507 diff -U 5 -L "current version" -L "new version" \ 2508 merge/old/${F} merge/new/${F} || true 2509 continuep < /dev/tty || return 1 2510 done < $1-paths 2511 2512 # Store merged files. 2513 while read F; do 2514 if [ -f merge/new/${F} ]; then 2515 V=`${SHA256} -q merge/new/${F}` 2516 2517 gzip -c < merge/new/${F} > files/${V}.gz 2518 echo "${F}|${V}" 2519 fi 2520 done < $1-paths > newhashes 2521 2522 # Pull lines out from $3 which need to be updated to 2523 # reflect merged files. 2524 while read F; do 2525 look "${F}|" $3 2526 done < $1-paths > $3-oldlines 2527 2528 # Update lines to reflect merged files 2529 join -t '|' -o 1.1,1.2,1.3,1.4,1.5,1.6,2.2,1.8 \ 2530 $3-oldlines newhashes > $3-newlines 2531 2532 # Remove old lines from $3 and add new lines. 2533 sort $3-oldlines | 2534 comm -13 - $3 | 2535 sort - $3-newlines > $3.tmp 2536 mv $3.tmp $3 2537 2538 # Clean up 2539 rm $1-paths newhashes $3-oldlines $3-newlines 2540 rm -rf merge/ 2541 fi 2542 2543 # We're done with merging files. 2544 rm $1 2545} 2546 2547# Do the work involved in fetching upgrades to a new release 2548upgrade_run () { 2549 workdir_init || return 1 2550 2551 # Prepare the mirror list. 2552 fetch_pick_server_init && fetch_pick_server 2553 2554 # Try to fetch the public key until we run out of servers. 2555 while ! fetch_key; do 2556 fetch_pick_server || return 1 2557 done 2558 2559 # Try to fetch the metadata index signature ("tag") until we run 2560 # out of available servers; and sanity check the downloaded tag. 2561 while ! fetch_tag; do 2562 fetch_pick_server || return 1 2563 done 2564 fetch_tagsanity || return 1 2565 2566 # Fetch the INDEX-OLD and INDEX-ALL. 2567 fetch_metadata INDEX-OLD INDEX-ALL || return 1 2568 2569 # If StrictComponents is not "yes", generate a new components list 2570 # with only the components which appear to be installed. 2571 upgrade_guess_components INDEX-ALL || return 1 2572 2573 # Generate filtered INDEX-OLD and INDEX-ALL files containing only 2574 # the components we want and without anything marked as "Ignore". 2575 fetch_filter_metadata INDEX-OLD || return 1 2576 fetch_filter_metadata INDEX-ALL || return 1 2577 2578 # Merge the INDEX-OLD and INDEX-ALL files into INDEX-OLD. 2579 sort INDEX-OLD INDEX-ALL > INDEX-OLD.tmp 2580 mv INDEX-OLD.tmp INDEX-OLD 2581 rm INDEX-ALL 2582 2583 # Adjust variables for fetching files from the new release. 2584 OLDRELNUM=${RELNUM} 2585 RELNUM=${TARGETRELEASE} 2586 OLDFETCHDIR=${FETCHDIR} 2587 FETCHDIR=${RELNUM}/${ARCH} 2588 2589 # Try to fetch the NEW metadata index signature ("tag") until we run 2590 # out of available servers; and sanity check the downloaded tag. 2591 while ! fetch_tag; do 2592 fetch_pick_server || return 1 2593 done 2594 2595 # Fetch the new INDEX-ALL. 2596 fetch_metadata INDEX-ALL || return 1 2597 2598 # If StrictComponents is not "yes", COMPONENTS contains an entry 2599 # corresponding to the currently running kernel, and said kernel 2600 # does not exist in the new release, add "kernel/generic" to the 2601 # list of components. 2602 upgrade_guess_new_kernel INDEX-ALL || return 1 2603 2604 # Filter INDEX-ALL to contain only the components we want and without 2605 # anything marked as "Ignore". 2606 fetch_filter_metadata INDEX-ALL || return 1 2607 2608 # Convert INDEX-OLD (last release) and INDEX-ALL (new release) into 2609 # INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades). 2610 upgrade_oldall_to_oldnew INDEX-OLD INDEX-ALL INDEX-NEW 2611 2612 # Translate /boot/${KERNCONF} or /boot/${NKERNCONF} into ${KERNELDIR} 2613 fetch_filter_kernel_names INDEX-NEW ${NKERNCONF} 2614 fetch_filter_kernel_names INDEX-OLD ${KERNCONF} 2615 2616 # For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the 2617 # system and generate an INDEX-PRESENT file. 2618 fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 2619 2620 # Based on ${MERGECHANGES}, generate a file tomerge-old with the 2621 # paths and hashes of old versions of files to merge. 2622 fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT tomerge-old 2623 2624 # Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which 2625 # correspond to lines in INDEX-PRESENT with hashes not appearing 2626 # in INDEX-OLD or INDEX-NEW. Also remove lines where the entry in 2627 # INDEX-PRESENT has type - and there isn't a corresponding entry in 2628 # INDEX-OLD with type -. 2629 fetch_filter_unmodified_notpresent \ 2630 INDEX-OLD INDEX-PRESENT INDEX-NEW tomerge-old 2631 2632 # For each entry in INDEX-PRESENT of type -, remove any corresponding 2633 # entry from INDEX-NEW if ${ALLOWADD} != "yes". Remove all entries 2634 # of type - from INDEX-PRESENT. 2635 fetch_filter_allowadd INDEX-PRESENT INDEX-NEW 2636 2637 # If ${ALLOWDELETE} != "yes", then remove any entries from 2638 # INDEX-PRESENT which don't correspond to entries in INDEX-NEW. 2639 fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW 2640 2641 # If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in 2642 # INDEX-PRESENT with metadata not matching any entry in INDEX-OLD, 2643 # replace the corresponding line of INDEX-NEW with one having the 2644 # same metadata as the entry in INDEX-PRESENT. 2645 fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW 2646 2647 # Remove lines from INDEX-PRESENT and INDEX-NEW which are identical; 2648 # no need to update a file if it isn't changing. 2649 fetch_filter_uptodate INDEX-PRESENT INDEX-NEW 2650 2651 # Fetch "clean" files from the old release for merging changes. 2652 fetch_files_premerge tomerge-old 2653 2654 # Prepare to fetch files: Generate a list of the files we need, 2655 # copy the unmodified files we have into /files/, and generate 2656 # a list of patches to download. 2657 fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 2658 2659 # Fetch patches from to-${RELNUM}/${ARCH}/bp/ 2660 PATCHDIR=to-${RELNUM}/${ARCH}/bp 2661 fetch_files || return 1 2662 2663 # Merge configuration file changes. 2664 upgrade_merge tomerge-old INDEX-PRESENT INDEX-NEW || return 1 2665 2666 # Create and populate install manifest directory; and report what 2667 # updates are available. 2668 fetch_create_manifest || return 1 2669 2670 # Leave a note behind to tell the "install" command that the kernel 2671 # needs to be installed before the world. 2672 touch ${BDHASH}-install/kernelfirst 2673 2674 # Remind the user that they need to run "freebsd-update install" 2675 # to install the downloaded bits, in case they didn't RTFM. 2676 echo "To install the downloaded upgrades, run \"$0 install\"." 2677} 2678 2679# Make sure that all the file hashes mentioned in $@ have corresponding 2680# gzipped files stored in /files/. 2681install_verify () { 2682 # Generate a list of hashes 2683 cat $@ | 2684 cut -f 2,7 -d '|' | 2685 grep -E '^f' | 2686 cut -f 2 -d '|' | 2687 sort -u > filelist 2688 2689 # Make sure all the hashes exist 2690 while read HASH; do 2691 if ! [ -f files/${HASH}.gz ]; then 2692 echo -n "Update files missing -- " 2693 echo "this should never happen." 2694 echo "Re-run '$0 fetch'." 2695 return 1 2696 fi 2697 done < filelist 2698 2699 # Clean up 2700 rm filelist 2701} 2702 2703# Remove the system immutable flag from files 2704install_unschg () { 2705 # Generate file list 2706 cat $@ | 2707 cut -f 1 -d '|' > filelist 2708 2709 # Remove flags 2710 while read F; do 2711 if ! [ -e ${BASEDIR}/${F} ]; then 2712 continue 2713 else 2714 echo ${BASEDIR}/${F} 2715 fi 2716 done < filelist | xargs chflags noschg || return 1 2717 2718 # Clean up 2719 rm filelist 2720} 2721 2722# Decide which directory name to use for kernel backups. 2723backup_kernel_finddir () { 2724 CNT=0 2725 while true ; do 2726 # Pathname does not exist, so it is OK use that name 2727 # for backup directory. 2728 if [ ! -e $BASEDIR/$BACKUPKERNELDIR ]; then 2729 return 0 2730 fi 2731 2732 # If directory do exist, we only use if it has our 2733 # marker file. 2734 if [ -d $BASEDIR/$BACKUPKERNELDIR -a \ 2735 -e $BASEDIR/$BACKUPKERNELDIR/.freebsd-update ]; then 2736 return 0 2737 fi 2738 2739 # We could not use current directory name, so add counter to 2740 # the end and try again. 2741 CNT=$((CNT + 1)) 2742 if [ $CNT -gt 9 ]; then 2743 echo "Could not find valid backup dir ($BASEDIR/$BACKUPKERNELDIR)" 2744 exit 1 2745 fi 2746 BACKUPKERNELDIR="`echo $BACKUPKERNELDIR | sed -Ee 's/[0-9]\$//'`" 2747 BACKUPKERNELDIR="${BACKUPKERNELDIR}${CNT}" 2748 done 2749} 2750 2751# Backup the current kernel using hardlinks, if not disabled by user. 2752# Since we delete all files in the directory used for previous backups 2753# we create a marker file called ".freebsd-update" in the directory so 2754# we can determine on the next run that the directory was created by 2755# freebsd-update and we then do not accidentally remove user files in 2756# the unlikely case that the user has created a directory with a 2757# conflicting name. 2758backup_kernel () { 2759 # Only make kernel backup is so configured. 2760 if [ $BACKUPKERNEL != yes ]; then 2761 return 0 2762 fi 2763 2764 # Decide which directory name to use for kernel backups. 2765 backup_kernel_finddir 2766 2767 # Remove old kernel backup files. If $BACKUPKERNELDIR was 2768 # "not ours", backup_kernel_finddir would have exited, so 2769 # deleting the directory content is as safe as we can make it. 2770 if [ -d $BASEDIR/$BACKUPKERNELDIR ]; then 2771 rm -fr $BASEDIR/$BACKUPKERNELDIR 2772 fi 2773 2774 # Create directories for backup. 2775 mkdir -p $BASEDIR/$BACKUPKERNELDIR 2776 mtree -cdn -p "${BASEDIR}/${KERNELDIR}" | \ 2777 mtree -Ue -p "${BASEDIR}/${BACKUPKERNELDIR}" > /dev/null 2778 2779 # Mark the directory as having been created by freebsd-update. 2780 touch $BASEDIR/$BACKUPKERNELDIR/.freebsd-update 2781 if [ $? -ne 0 ]; then 2782 echo "Could not create kernel backup directory" 2783 exit 1 2784 fi 2785 2786 # Disable pathname expansion to be sure *.symbols is not 2787 # expanded. 2788 set -f 2789 2790 # Use find to ignore symbol files, unless disabled by user. 2791 if [ $BACKUPKERNELSYMBOLFILES = yes ]; then 2792 FINDFILTER="" 2793 else 2794 FINDFILTER="-a ! -name *.debug -a ! -name *.symbols" 2795 fi 2796 2797 # Backup all the kernel files using hardlinks. 2798 (cd ${BASEDIR}/${KERNELDIR} && find . -type f $FINDFILTER -exec \ 2799 cp -pl '{}' ${BASEDIR}/${BACKUPKERNELDIR}/'{}' \;) 2800 2801 # Re-enable patchname expansion. 2802 set +f 2803} 2804 2805# Install new files 2806install_from_index () { 2807 # First pass: Do everything apart from setting file flags. We 2808 # can't set flags yet, because schg inhibits hard linking. 2809 sort -k 1,1 -t '|' $1 | 2810 tr '|' ' ' | 2811 while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do 2812 case ${TYPE} in 2813 d) 2814 # Create a directory 2815 install -d -o ${OWNER} -g ${GROUP} \ 2816 -m ${PERM} ${BASEDIR}/${FPATH} 2817 ;; 2818 f) 2819 if [ -z "${LINK}" ]; then 2820 # Create a file, without setting flags. 2821 gunzip < files/${HASH}.gz > ${HASH} 2822 install -S -o ${OWNER} -g ${GROUP} \ 2823 -m ${PERM} ${HASH} ${BASEDIR}/${FPATH} 2824 rm ${HASH} 2825 else 2826 # Create a hard link. 2827 ln -f ${BASEDIR}/${LINK} ${BASEDIR}/${FPATH} 2828 fi 2829 ;; 2830 L) 2831 # Create a symlink 2832 ln -sfh ${HASH} ${BASEDIR}/${FPATH} 2833 ;; 2834 esac 2835 done 2836 2837 # Perform a second pass, adding file flags. 2838 tr '|' ' ' < $1 | 2839 while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do 2840 if [ ${TYPE} = "f" ] && 2841 ! [ ${FLAGS} = "0" ]; then 2842 chflags ${FLAGS} ${BASEDIR}/${FPATH} 2843 fi 2844 done 2845} 2846 2847# Remove files which we want to delete 2848install_delete () { 2849 # Generate list of new files 2850 cut -f 1 -d '|' < $2 | 2851 sort > newfiles 2852 2853 # Generate subindex of old files we want to nuke 2854 sort -k 1,1 -t '|' $1 | 2855 join -t '|' -v 1 - newfiles | 2856 sort -r -k 1,1 -t '|' | 2857 cut -f 1,2 -d '|' | 2858 tr '|' ' ' > killfiles 2859 2860 # Remove the offending bits 2861 while read FPATH TYPE; do 2862 case ${TYPE} in 2863 d) 2864 rmdir ${BASEDIR}/${FPATH} 2865 ;; 2866 f) 2867 rm ${BASEDIR}/${FPATH} 2868 ;; 2869 L) 2870 rm ${BASEDIR}/${FPATH} 2871 ;; 2872 esac 2873 done < killfiles 2874 2875 # Clean up 2876 rm newfiles killfiles 2877} 2878 2879# Install new files, delete old files, and update generated files 2880install_files () { 2881 # If we haven't already dealt with the kernel, deal with it. 2882 if ! [ -f $1/kerneldone ]; then 2883 grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD 2884 grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW 2885 2886 # Backup current kernel before installing a new one 2887 backup_kernel || return 1 2888 2889 # Install new files 2890 install_from_index INDEX-NEW || return 1 2891 2892 # Remove files which need to be deleted 2893 install_delete INDEX-OLD INDEX-NEW || return 1 2894 2895 # Update linker.hints if necessary 2896 if [ -s INDEX-OLD -o -s INDEX-NEW ]; then 2897 kldxref -R ${BASEDIR}/boot/ 2>/dev/null 2898 fi 2899 2900 # We've finished updating the kernel. 2901 touch $1/kerneldone 2902 2903 # Do we need to ask for a reboot now? 2904 if [ -f $1/kernelfirst ] && 2905 [ -s INDEX-OLD -o -s INDEX-NEW ]; then 2906 cat <<-EOF 2907 2908Kernel updates have been installed. Please reboot and run 2909"$0 install" again to finish installing updates. 2910 EOF 2911 exit 0 2912 fi 2913 fi 2914 2915 # If we haven't already dealt with the world, deal with it. 2916 if ! [ -f $1/worlddone ]; then 2917 # Create any necessary directories first 2918 grep -vE '^/boot/' $1/INDEX-NEW | 2919 grep -E '^[^|]+\|d\|' > INDEX-NEW 2920 install_from_index INDEX-NEW || return 1 2921 2922 # Install new runtime linker 2923 grep -vE '^/boot/' $1/INDEX-NEW | 2924 grep -vE '^[^|]+\|d\|' | 2925 grep -E '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' > INDEX-NEW 2926 install_from_index INDEX-NEW || return 1 2927 2928 # Install new shared libraries next 2929 grep -vE '^/boot/' $1/INDEX-NEW | 2930 grep -vE '^[^|]+\|d\|' | 2931 grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' | 2932 grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW 2933 install_from_index INDEX-NEW || return 1 2934 2935 # Deal with everything else 2936 grep -vE '^/boot/' $1/INDEX-OLD | 2937 grep -vE '^[^|]+\|d\|' | 2938 grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' | 2939 grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD 2940 grep -vE '^/boot/' $1/INDEX-NEW | 2941 grep -vE '^[^|]+\|d\|' | 2942 grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' | 2943 grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW 2944 install_from_index INDEX-NEW || return 1 2945 install_delete INDEX-OLD INDEX-NEW || return 1 2946 2947 # Rehash certs if we actually have certctl installed. 2948 if which certctl>/dev/null; then 2949 env DESTDIR=${BASEDIR} certctl rehash 2950 fi 2951 2952 # Rebuild generated pwd files. 2953 if [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/spwd.db ] || 2954 [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/pwd.db ] || 2955 [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/passwd ]; then 2956 pwd_mkdb -d ${BASEDIR}/etc -p ${BASEDIR}/etc/master.passwd 2957 fi 2958 2959 # Rebuild /etc/login.conf.db if necessary. 2960 if [ ${BASEDIR}/etc/login.conf -nt ${BASEDIR}/etc/login.conf.db ]; then 2961 cap_mkdb ${BASEDIR}/etc/login.conf 2962 fi 2963 2964 # Rebuild man page databases, if necessary. 2965 for D in /usr/share/man /usr/share/openssl/man; do 2966 if [ ! -d ${BASEDIR}/$D ]; then 2967 continue 2968 fi 2969 if [ -z "$(find ${BASEDIR}/$D -type f -newer ${BASEDIR}/$D/mandoc.db)" ]; then 2970 continue; 2971 fi 2972 makewhatis ${BASEDIR}/$D 2973 done 2974 2975 # We've finished installing the world and deleting old files 2976 # which are not shared libraries. 2977 touch $1/worlddone 2978 2979 # Do we need to ask the user to portupgrade now? 2980 grep -vE '^/boot/' $1/INDEX-NEW | 2981 grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' | 2982 cut -f 1 -d '|' | 2983 sort > newfiles 2984 if grep -vE '^/boot/' $1/INDEX-OLD | 2985 grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' | 2986 cut -f 1 -d '|' | 2987 sort | 2988 join -v 1 - newfiles | 2989 grep -q .; then 2990 cat <<-EOF 2991 2992Completing this upgrade requires removing old shared object files. 2993Please rebuild all installed 3rd party software (e.g., programs 2994installed from the ports tree) and then run "$0 install" 2995again to finish installing updates. 2996 EOF 2997 rm newfiles 2998 exit 0 2999 fi 3000 rm newfiles 3001 fi 3002 3003 # Remove old shared libraries 3004 grep -vE '^/boot/' $1/INDEX-NEW | 3005 grep -vE '^[^|]+\|d\|' | 3006 grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW 3007 grep -vE '^/boot/' $1/INDEX-OLD | 3008 grep -vE '^[^|]+\|d\|' | 3009 grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD 3010 install_delete INDEX-OLD INDEX-NEW || return 1 3011 3012 # Remove old directories 3013 grep -vE '^/boot/' $1/INDEX-NEW | 3014 grep -E '^[^|]+\|d\|' > INDEX-NEW 3015 grep -vE '^/boot/' $1/INDEX-OLD | 3016 grep -E '^[^|]+\|d\|' > INDEX-OLD 3017 install_delete INDEX-OLD INDEX-NEW || return 1 3018 3019 # Remove temporary files 3020 rm INDEX-OLD INDEX-NEW 3021} 3022 3023# Rearrange bits to allow the installed updates to be rolled back 3024install_setup_rollback () { 3025 # Remove the "reboot after installing kernel", "kernel updated", and 3026 # "finished installing the world" flags if present -- they are 3027 # irrelevant when rolling back updates. 3028 if [ -f ${BDHASH}-install/kernelfirst ]; then 3029 rm ${BDHASH}-install/kernelfirst 3030 rm ${BDHASH}-install/kerneldone 3031 fi 3032 if [ -f ${BDHASH}-install/worlddone ]; then 3033 rm ${BDHASH}-install/worlddone 3034 fi 3035 3036 if [ -L ${BDHASH}-rollback ]; then 3037 mv ${BDHASH}-rollback ${BDHASH}-install/rollback 3038 fi 3039 3040 mv ${BDHASH}-install ${BDHASH}-rollback 3041} 3042 3043# Actually install updates 3044install_run () { 3045 echo -n "Installing updates..." 3046 3047 # Make sure we have all the files we should have 3048 install_verify ${BDHASH}-install/INDEX-OLD \ 3049 ${BDHASH}-install/INDEX-NEW || return 1 3050 3051 # Remove system immutable flag from files 3052 install_unschg ${BDHASH}-install/INDEX-OLD \ 3053 ${BDHASH}-install/INDEX-NEW || return 1 3054 3055 # Install new files, delete old files, and update linker.hints 3056 install_files ${BDHASH}-install || return 1 3057 3058 # Rearrange bits to allow the installed updates to be rolled back 3059 install_setup_rollback 3060 3061 echo " done." 3062} 3063 3064# Rearrange bits to allow the previous set of updates to be rolled back next. 3065rollback_setup_rollback () { 3066 if [ -L ${BDHASH}-rollback/rollback ]; then 3067 mv ${BDHASH}-rollback/rollback rollback-tmp 3068 rm -r ${BDHASH}-rollback/ 3069 rm ${BDHASH}-rollback 3070 mv rollback-tmp ${BDHASH}-rollback 3071 else 3072 rm -r ${BDHASH}-rollback/ 3073 rm ${BDHASH}-rollback 3074 fi 3075} 3076 3077# Install old files, delete new files, and update linker.hints 3078rollback_files () { 3079 # Install old shared library files which don't have the same path as 3080 # a new shared library file. 3081 grep -vE '^/boot/' $1/INDEX-NEW | 3082 grep -E '/lib/.*\.so\.[0-9]+\|' | 3083 cut -f 1 -d '|' | 3084 sort > INDEX-NEW.libs.flist 3085 grep -vE '^/boot/' $1/INDEX-OLD | 3086 grep -E '/lib/.*\.so\.[0-9]+\|' | 3087 sort -k 1,1 -t '|' - | 3088 join -t '|' -v 1 - INDEX-NEW.libs.flist > INDEX-OLD 3089 install_from_index INDEX-OLD || return 1 3090 3091 # Deal with files which are neither kernel nor shared library 3092 grep -vE '^/boot/' $1/INDEX-OLD | 3093 grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD 3094 grep -vE '^/boot/' $1/INDEX-NEW | 3095 grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW 3096 install_from_index INDEX-OLD || return 1 3097 install_delete INDEX-NEW INDEX-OLD || return 1 3098 3099 # Install any old shared library files which we didn't install above. 3100 grep -vE '^/boot/' $1/INDEX-OLD | 3101 grep -E '/lib/.*\.so\.[0-9]+\|' | 3102 sort -k 1,1 -t '|' - | 3103 join -t '|' - INDEX-NEW.libs.flist > INDEX-OLD 3104 install_from_index INDEX-OLD || return 1 3105 3106 # Delete unneeded shared library files 3107 grep -vE '^/boot/' $1/INDEX-OLD | 3108 grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD 3109 grep -vE '^/boot/' $1/INDEX-NEW | 3110 grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW 3111 install_delete INDEX-NEW INDEX-OLD || return 1 3112 3113 # Deal with kernel files 3114 grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD 3115 grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW 3116 install_from_index INDEX-OLD || return 1 3117 install_delete INDEX-NEW INDEX-OLD || return 1 3118 if [ -s INDEX-OLD -o -s INDEX-NEW ]; then 3119 kldxref -R /boot/ 2>/dev/null 3120 fi 3121 3122 # Remove temporary files 3123 rm INDEX-OLD INDEX-NEW INDEX-NEW.libs.flist 3124} 3125 3126# Actually rollback updates 3127rollback_run () { 3128 echo -n "Uninstalling updates..." 3129 3130 # If there are updates waiting to be installed, remove them; we 3131 # want the user to re-run 'fetch' after rolling back updates. 3132 if [ -L ${BDHASH}-install ]; then 3133 rm -r ${BDHASH}-install/ 3134 rm ${BDHASH}-install 3135 fi 3136 3137 # Make sure we have all the files we should have 3138 install_verify ${BDHASH}-rollback/INDEX-NEW \ 3139 ${BDHASH}-rollback/INDEX-OLD || return 1 3140 3141 # Remove system immutable flag from files 3142 install_unschg ${BDHASH}-rollback/INDEX-NEW \ 3143 ${BDHASH}-rollback/INDEX-OLD || return 1 3144 3145 # Install old files, delete new files, and update linker.hints 3146 rollback_files ${BDHASH}-rollback || return 1 3147 3148 # Remove the rollback directory and the symlink pointing to it; and 3149 # rearrange bits to allow the previous set of updates to be rolled 3150 # back next. 3151 rollback_setup_rollback 3152 3153 echo " done." 3154} 3155 3156# Compare INDEX-ALL and INDEX-PRESENT and print warnings about differences. 3157IDS_compare () { 3158 # Get all the lines which mismatch in something other than file 3159 # flags. We ignore file flags because sysinstall doesn't seem to 3160 # set them when it installs FreeBSD; warning about these adds a 3161 # very large amount of noise. 3162 cut -f 1-5,7-8 -d '|' $1 > $1.noflags 3163 sort -k 1,1 -t '|' $1.noflags > $1.sorted 3164 cut -f 1-5,7-8 -d '|' $2 | 3165 comm -13 $1.noflags - | 3166 fgrep -v '|-|||||' | 3167 sort -k 1,1 -t '|' | 3168 join -t '|' $1.sorted - > INDEX-NOTMATCHING 3169 3170 # Ignore files which match IDSIGNOREPATHS. 3171 for X in ${IDSIGNOREPATHS}; do 3172 grep -E "^${X}" INDEX-NOTMATCHING 3173 done | 3174 sort -u | 3175 comm -13 - INDEX-NOTMATCHING > INDEX-NOTMATCHING.tmp 3176 mv INDEX-NOTMATCHING.tmp INDEX-NOTMATCHING 3177 3178 # Go through the lines and print warnings. 3179 local IFS='|' 3180 while read FPATH TYPE OWNER GROUP PERM HASH LINK P_TYPE P_OWNER P_GROUP P_PERM P_HASH P_LINK; do 3181 # Warn about different object types. 3182 if ! [ "${TYPE}" = "${P_TYPE}" ]; then 3183 echo -n "${FPATH} is a " 3184 case "${P_TYPE}" in 3185 f) echo -n "regular file, " 3186 ;; 3187 d) echo -n "directory, " 3188 ;; 3189 L) echo -n "symlink, " 3190 ;; 3191 esac 3192 echo -n "but should be a " 3193 case "${TYPE}" in 3194 f) echo -n "regular file." 3195 ;; 3196 d) echo -n "directory." 3197 ;; 3198 L) echo -n "symlink." 3199 ;; 3200 esac 3201 echo 3202 3203 # Skip other tests, since they don't make sense if 3204 # we're comparing different object types. 3205 continue 3206 fi 3207 3208 # Warn about different owners. 3209 if ! [ "${OWNER}" = "${P_OWNER}" ]; then 3210 echo -n "${FPATH} is owned by user id ${P_OWNER}, " 3211 echo "but should be owned by user id ${OWNER}." 3212 fi 3213 3214 # Warn about different groups. 3215 if ! [ "${GROUP}" = "${P_GROUP}" ]; then 3216 echo -n "${FPATH} is owned by group id ${P_GROUP}, " 3217 echo "but should be owned by group id ${GROUP}." 3218 fi 3219 3220 # Warn about different permissions. We do not warn about 3221 # different permissions on symlinks, since some archivers 3222 # don't extract symlink permissions correctly and they are 3223 # ignored anyway. 3224 if ! [ "${PERM}" = "${P_PERM}" ] && 3225 ! [ "${TYPE}" = "L" ]; then 3226 echo -n "${FPATH} has ${P_PERM} permissions, " 3227 echo "but should have ${PERM} permissions." 3228 fi 3229 3230 # Warn about different file hashes / symlink destinations. 3231 if ! [ "${HASH}" = "${P_HASH}" ]; then 3232 if [ "${TYPE}" = "L" ]; then 3233 echo -n "${FPATH} is a symlink to ${P_HASH}, " 3234 echo "but should be a symlink to ${HASH}." 3235 fi 3236 if [ "${TYPE}" = "f" ]; then 3237 echo -n "${FPATH} has SHA256 hash ${P_HASH}, " 3238 echo "but should have SHA256 hash ${HASH}." 3239 fi 3240 fi 3241 3242 # We don't warn about different hard links, since some 3243 # some archivers break hard links, and as long as the 3244 # underlying data is correct they really don't matter. 3245 done < INDEX-NOTMATCHING 3246 3247 # Clean up 3248 rm $1 $1.noflags $1.sorted $2 INDEX-NOTMATCHING 3249} 3250 3251# Do the work involved in comparing the system to a "known good" index 3252IDS_run () { 3253 workdir_init || return 1 3254 3255 # Prepare the mirror list. 3256 fetch_pick_server_init && fetch_pick_server 3257 3258 # Try to fetch the public key until we run out of servers. 3259 while ! fetch_key; do 3260 fetch_pick_server || return 1 3261 done 3262 3263 # Try to fetch the metadata index signature ("tag") until we run 3264 # out of available servers; and sanity check the downloaded tag. 3265 while ! fetch_tag; do 3266 fetch_pick_server || return 1 3267 done 3268 fetch_tagsanity || return 1 3269 3270 # Fetch INDEX-OLD and INDEX-ALL. 3271 fetch_metadata INDEX-OLD INDEX-ALL || return 1 3272 3273 # Generate filtered INDEX-OLD and INDEX-ALL files containing only 3274 # the components we want and without anything marked as "Ignore". 3275 fetch_filter_metadata INDEX-OLD || return 1 3276 fetch_filter_metadata INDEX-ALL || return 1 3277 3278 # Merge the INDEX-OLD and INDEX-ALL files into INDEX-ALL. 3279 sort INDEX-OLD INDEX-ALL > INDEX-ALL.tmp 3280 mv INDEX-ALL.tmp INDEX-ALL 3281 rm INDEX-OLD 3282 3283 # Translate /boot/${KERNCONF} to ${KERNELDIR} 3284 fetch_filter_kernel_names INDEX-ALL ${KERNCONF} 3285 3286 # Inspect the system and generate an INDEX-PRESENT file. 3287 fetch_inspect_system INDEX-ALL INDEX-PRESENT /dev/null || return 1 3288 3289 # Compare INDEX-ALL and INDEX-PRESENT and print warnings about any 3290 # differences. 3291 IDS_compare INDEX-ALL INDEX-PRESENT 3292} 3293 3294#### Main functions -- call parameter-handling and core functions 3295 3296# Using the command line, configuration file, and defaults, 3297# set all the parameters which are needed later. 3298get_params () { 3299 init_params 3300 parse_cmdline $@ 3301 parse_conffile 3302 default_params 3303 finalize_components_config ${COMPONENTS} 3304} 3305 3306# Fetch command. Make sure that we're being called 3307# interactively, then run fetch_check_params and fetch_run 3308cmd_fetch () { 3309 if [ ! -t 0 -a $NOTTYOK -eq 0 ]; then 3310 echo -n "`basename $0` fetch should not " 3311 echo "be run non-interactively." 3312 echo "Run `basename $0` cron instead." 3313 exit 1 3314 fi 3315 fetch_check_params 3316 fetch_run || exit 1 3317 ISFETCHED=1 3318} 3319 3320# Cron command. Make sure the parameters are sensible; wait 3321# rand(3600) seconds; then fetch updates. While fetching updates, 3322# send output to a temporary file; only print that file if the 3323# fetching failed. 3324cmd_cron () { 3325 fetch_check_params 3326 sleep `jot -r 1 0 3600` 3327 3328 TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1 3329 if ! fetch_run >> ${TMPFILE} || 3330 ! grep -q "No updates needed" ${TMPFILE} || 3331 [ ${VERBOSELEVEL} = "debug" ]; then 3332 mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE} 3333 fi 3334 3335 rm ${TMPFILE} 3336} 3337 3338# Fetch files for upgrading to a new release. 3339cmd_upgrade () { 3340 upgrade_check_params 3341 upgrade_run || exit 1 3342} 3343 3344# Check if there are fetched updates ready to install 3345cmd_updatesready () { 3346 # Construct a unique name from ${BASEDIR} 3347 BDHASH=`echo ${BASEDIR} | sha256 -q` 3348 3349 # Check that we have updates ready to install 3350 if ! [ -L ${BDHASH}-install ]; then 3351 echo "No updates are available to install." 3352 exit 2 3353 fi 3354 3355 echo "There are updates available to install." 3356 echo "Run '$0 install' to proceed." 3357} 3358 3359# Install downloaded updates. 3360cmd_install () { 3361 install_check_params 3362 install_run || exit 1 3363} 3364 3365# Rollback most recently installed updates. 3366cmd_rollback () { 3367 rollback_check_params 3368 rollback_run || exit 1 3369} 3370 3371# Compare system against a "known good" index. 3372cmd_IDS () { 3373 IDS_check_params 3374 IDS_run || exit 1 3375} 3376 3377# Output configuration. 3378cmd_showconfig () { 3379 for X in ${CONFIGOPTIONS}; do 3380 echo $X=$(eval echo \$${X}) 3381 done 3382} 3383 3384#### Entry point 3385 3386# Make sure we find utilities from the base system 3387export PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH} 3388 3389# Set a pager if the user doesn't 3390if [ -z "$PAGER" ]; then 3391 PAGER=/usr/bin/less 3392fi 3393 3394# Set LC_ALL in order to avoid problems with character ranges like [A-Z]. 3395export LC_ALL=C 3396 3397get_params $@ 3398for COMMAND in ${COMMANDS}; do 3399 cmd_${COMMAND} 3400done 3401