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