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