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