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