Advertisement
Guest User

Untitled

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