/[base]/stable/11/usr.sbin/freebsd-update/freebsd-update.sh
ViewVC logotype

Contents of /stable/11/usr.sbin/freebsd-update/freebsd-update.sh

Parent Directory Parent Directory | Revision Log Revision Log


Revision 368824 - (show annotations) (download) (as text)
Wed Dec 30 01:11:02 2020 UTC (3 years, 6 months ago) by kevans
File MIME type: application/x-sh
File size: 93469 byte(s)
MFC freebsd-update: unconditionally regenerate passwd/login.conf files

The existing logic is nice in theory, but in practice freebsd-update will
not preserve the timestamps on these files. When doing a major upgrade, e.g.
from 12.1-RELEASE -> 12.2-RELEASE, pwd.mkdb et al. appear in the INDEX and
we clobber the timestamp several times in the process of packaging up the
existing system into /var/db/freebsd-update/files and extracting for
comparisons. This leads to these files not getting regenerated when they're
most likely to be needed.

Measures could be taken to preserve timestamps, but it's unclear whether
the complexity and overhead of doing so is really outweighed by the marginal
benefit.

I observed this issue when pkg subsequently failed to install a package that
wanted to add a user, claiming that the user was removed in the process.
bapt@ pointed to this pre-existing bug with freebsd-update as the cause.

PR:		234014, 232921

(cherry picked from commit ebebc41e4cfe44b8e8fd881badf2fa2c4be65aa4)

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

Properties

Name Value
svn:keywords FreeBSD=%H

  ViewVC Help
Powered by ViewVC 1.1.27