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