postinstall.in revision 1.66 1 #!/bin/sh
2 #
3 # $NetBSD: postinstall.in,v 1.66 2024/10/10 22:14:34 uwe Exp $
4 #
5 # Copyright (c) 2002-2022 The NetBSD Foundation, Inc.
6 # All rights reserved.
7 #
8 # This code is derived from software contributed to The NetBSD Foundation
9 # by Luke Mewburn.
10 #
11 # Redistribution and use in source and binary forms, with or without
12 # modification, are permitted provided that the following conditions
13 # are met:
14 # 1. Redistributions of source code must retain the above copyright
15 # notice, this list of conditions and the following disclaimer.
16 # 2. Redistributions in binary form must reproduce the above copyright
17 # notice, this list of conditions and the following disclaimer in the
18 # documentation and/or other materials provided with the distribution.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31 #
32 # postinstall
33 # Check for or fix configuration changes that occur
34 # over time as NetBSD evolves.
35 #
36
37 #
38 # NOTE: Be sure to use ${DEST_DIR} prefix before all real file operations.
39 #
40
41 #
42 # checks to add:
43 # - sysctl(8) renames (net.inet6.ip6.bindv6only -> net.inet6.ip6.v6only)
44 # - de* -> tlp* migration (/etc/ifconfig.de*, $ifconfig_de*, ...) ?
45 # - support quiet/verbose mode ?
46 # - differentiate between failures caused by missing source
47 # and real failures
48 # - install moduli into usr/share/examples/ssh and use from there?
49 # - differentiate between "needs fix" versus "can't fix" issues
50 #
51
52 # This script is executed as part of a cross build. Allow the build
53 # environment to override the locations of some tools.
54 : ${AWK:=awk}
55 : ${DB:=db}
56 : ${GREP:=grep}
57 : ${HOST_SH:=sh}
58 : ${MAKE:=make}
59 : ${PWD_MKDB:=/usr/sbin/pwd_mkdb}
60 : ${SED:=sed}
61 : ${SORT:=sort}
62 : ${STAT:=stat}
63 : ${RM:=rm}
64
65 #
66 # helper functions
67 #
68
69 err()
70 {
71 local exitval=$1
72 shift
73 echo 1>&2 "${PROGNAME}: $*"
74 if [ -n "${SCRATCHDIR}" ]; then
75 ${RM} -rf "${SCRATCHDIR}"
76 fi
77 exit ${exitval}
78 }
79
80 warn()
81 {
82 echo 1>&2 "${PROGNAME}: $*"
83 }
84
85 msg()
86 {
87 echo " $*"
88 }
89
90 mkdtemp()
91 {
92 # Make sure we don't loop forever if mkdir will always fail.
93 [ -d /tmp ] || err 2 /tmp is not a directory
94 [ -w /tmp ] || err 2 /tmp is not writable
95
96 local base="/tmp/_postinstall.$$"
97 local serial=0
98 local dir
99
100 while true; do
101 dir="${base}.${serial}"
102 mkdir -m 0700 "${dir}" && break
103 serial=$((${serial} + 1))
104 done
105 echo "${dir}"
106 }
107
108 # Quote args to make them safe in the shell.
109 # Usage: quotedlist="$(shell_quote args...)"
110 #
111 # After building up a quoted list, use it by evaling it inside
112 # double quotes, like this:
113 # eval "set -- $quotedlist"
114 # or like this:
115 # eval "\$command $quotedlist \$filename"
116 #
117 shell_quote()
118 {(
119 local result=''
120 local arg qarg
121 LC_COLLATE=C ; export LC_COLLATE # so [a-zA-Z0-9] works in ASCII
122 for arg in "$@" ; do
123 case "${arg}" in
124 '')
125 qarg="''"
126 ;;
127 *[!-./a-zA-Z0-9]*)
128 # Convert each embedded ' to '\'',
129 # then insert ' at the beginning of the first line,
130 # and append ' at the end of the last line.
131 # Finally, elide unnecessary '' pairs at the
132 # beginning and end of the result and as part of
133 # '\'''\'' sequences that result from multiple
134 # adjacent quotes in he input.
135 qarg="$(printf "%s\n" "$arg" | \
136 ${SED} -e "s/'/'\\\\''/g" \
137 -e "1s/^/'/" -e "\$s/\$/'/" \
138 -e "1s/^''//" -e "\$s/''\$//" \
139 -e "s/'''/'/g"
140 )"
141 ;;
142 *)
143 # Arg is not the empty string, and does not contain
144 # any unsafe characters. Leave it unchanged for
145 # readability.
146 qarg="${arg}"
147 ;;
148 esac
149 result="${result}${result:+ }${qarg}"
150 done
151 printf "%s\n" "$result"
152 )}
153
154 # Convert arg $1 to a basic regular expression (as in sed)
155 # that will match the arg. This works by inserting backslashes
156 # before characters that are special in basic regular expressions.
157 # It also inserts backslashes before the extra characters specified
158 # in $2 (which defaults to "/,").
159 # XXX: Does not handle embedded newlines.
160 # Usage: regex="$(bre_quote "${string}")"
161 bre_quote()
162 {
163 local arg="$1"
164 local extra="${2-/,}"
165 printf "%s\n" "${arg}" | ${SED} -e 's/[][^$.*\\'"${extra}"']/\\&/g'
166 }
167
168 # unprefix dir
169 # Remove any dir prefix from a list of paths on stdin,
170 # and write the result to stdout. Useful for converting
171 # from ${DEST_DIR}/path to /path.
172 #
173 unprefix()
174 {
175 [ $# -eq 1 ] || err 3 "USAGE: unprefix dir"
176 local prefix="${1%/}"
177 prefix="$(bre_quote "${prefix}")"
178
179 ${SED} -e "s,^${prefix}/,/,"
180 }
181
182 # additem item description
183 # Add item to list of supported items to check/fix,
184 # which are checked/fixed by default if no item is requested by user.
185 #
186 additem()
187 {
188 [ $# -eq 2 ] || err 3 "USAGE: additem item description"
189 defaultitems="${defaultitems}${defaultitems:+ }$1"
190 eval desc_$1=\"\$2\"
191 }
192
193 # adddisableditem item description
194 # Add item to list of supported items to check/fix,
195 # but execute the item only if the user asks for it explicitly.
196 #
197 adddisableditem()
198 {
199 [ $# -eq 2 ] || err 3 "USAGE: adddisableditem item description"
200 otheritems="${otheritems}${otheritems:+ }$1"
201 eval desc_$1=\"\$2\"
202 }
203
204 # checkdir op dir mode
205 # Ensure dir exists, and if not, create it with the appropriate mode.
206 # Returns 0 if ok, 1 otherwise.
207 #
208 check_dir()
209 {
210 [ $# -eq 3 ] || err 3 "USAGE: check_dir op dir mode"
211 local op="$1"
212 local dir="$2"
213 local mode="$3"
214 [ -d "${dir}" ] && return 0
215 if [ "${op}" = "check" ]; then
216 msg "${dir} is not a directory"
217 return 1
218 elif ! mkdir -m "${mode}" "${dir}" ; then
219 msg "Can't create missing ${dir}"
220 return 1
221 else
222 msg "Missing ${dir} created"
223 fi
224 return 0
225 }
226
227 # check_ids op type file srcfile start id ...
228 # Check if file of type "users" or "groups" contains the relevant IDs.
229 # Use srcfile as a reference for the expected contents.
230 # The specified "id" names should be given in numerical order,
231 # with the first name corresponding to numerical value "start",
232 # and with the special name "SKIP" being used to mark gaps in the
233 # sequence.
234 # Returns 0 if ok, 1 otherwise.
235 #
236 check_ids()
237 {
238 [ $# -ge 6 ] || err 3 "USAGE: checks_ids op type file srcfile start id ..."
239 local op="$1"
240 local type="$2"
241 local file="$3"
242 local srcfile="$4"
243 local start="$5"
244 shift 5
245 #local ids="$@"
246
247 if [ ! -f "${file}" ]; then
248 msg "${file} doesn't exist; can't check for missing ${type}"
249 return 1
250 fi
251 if [ ! -r "${file}" ]; then
252 msg "${file} is not readable; can't check for missing ${type}"
253 return 1
254 fi
255 local notfixed=""
256 if [ "${op}" = "fix" ]; then
257 notfixed="${NOT_FIXED}"
258 fi
259 local missing="$(${AWK} -v start=$start -F: '
260 BEGIN {
261 for (x = 1; x < ARGC; x++) {
262 if (ARGV[x] == "SKIP")
263 continue;
264 idlist[ARGV[x]]++;
265 value[ARGV[x]] = start + x - 1;
266 }
267 ARGC=1
268 }
269 {
270 found[$1]++
271 number[$1] = $3
272 }
273 END {
274 for (id in idlist) {
275 if (!(id in found))
276 printf("%s (missing)\n", id)
277 else if (number[id] != value[id])
278 printf("%s (%d != %d)\n", id,
279 number[id], value[id])
280 start++;
281 }
282 }
283 ' "$@" < "${file}")" || return 1
284 if [ -n "${missing}" ]; then
285 msg "Error ${type}${notfixed}:" $(echo ${missing})
286 msg "Use the following as a template:"
287 set -- ${missing}
288 while [ $# -gt 0 ]
289 do
290 ${GREP} -E "^${1}:" ${srcfile}
291 shift 2
292 done | sort -t: -k3n
293 msg "and adjust if necessary."
294 return 1
295 fi
296 return 0
297 }
298
299 # populate_dir op onlynew src dst mode file ...
300 # Perform op ("check" or "fix") on files in src/ against dst/
301 # If op = "check" display missing or changed files, optionally with diffs.
302 # If op != "check" copies any missing or changed files.
303 # If onlynew evaluates to true, changed files are ignored.
304 # Returns 0 if ok, 1 otherwise.
305 #
306 populate_dir()
307 {
308 [ $# -ge 5 ] || err 3 "USAGE: populate_dir op onlynew src dst mode file ..."
309 local op="$1"
310 local onlynew="$2"
311 local src="$3"
312 local dst="$4"
313 local mode="$5"
314 shift 5
315 #local files="$@"
316
317 if [ ! -d "${src}" ]; then
318 msg "${src} is not a directory; skipping check"
319 return 1
320 fi
321 check_dir "${op}" "${dst}" 755 || return 1
322
323 local cmpdir_rv=0
324 local f fs fd error
325 for f in "$@"; do
326 fs="${src}/${f}"
327 fd="${dst}/${f}"
328 error=""
329 if [ ! -f "${fd}" ]; then
330 error="${fd} does not exist"
331 elif ! cmp -s "${fs}" "${fd}" ; then
332 if $onlynew; then # leave existing ${fd} alone
333 continue;
334 fi
335 error="${fs} != ${fd}"
336 else
337 continue
338 fi
339 if [ "${op}" = "check" ]; then
340 msg "${error}"
341 if [ -n "${DIFF_STYLE}" -a -f "${fd}" ]; then
342 diff -${DIFF_STYLE} ${DIFF_OPT} "${fd}" "${fs}"
343 fi
344 cmpdir_rv=1
345 elif ! ${RM} -f "${fd}" ||
346 ! cp -f "${fs}" "${fd}"; then
347 msg "Can't copy ${fs} to ${fd}"
348 cmpdir_rv=1
349 elif ! chmod "${mode}" "${fd}"; then
350 msg "Can't change mode of ${fd} to ${mode}"
351 cmpdir_rv=1
352 else
353 msg "Copied ${fs} to ${fd}"
354 fi
355 done
356 return ${cmpdir_rv}
357 }
358
359 # compare_dir op src dst mode file ...
360 # Perform op ("check" or "fix") on files in src/ against dst/
361 # If op = "check" display missing or changed files, optionally with diffs.
362 # If op != "check" copies any missing or changed files.
363 # Returns 0 if ok, 1 otherwise.
364 #
365 compare_dir()
366 {
367 [ $# -ge 4 ] || err 3 "USAGE: compare_dir op src dst mode file ..."
368 local op="$1"
369 local src="$2"
370 local dst="$3"
371 local mode="$4"
372 shift 4
373 #local files="$@"
374
375 populate_dir "$op" false "$src" "$dst" "$mode" "$@"
376 }
377
378 # move_file op src dst --
379 # Check (op == "check") or move (op != "check") from src to dst.
380 # Returns 0 if ok, 1 otherwise.
381 #
382 move_file()
383 {
384 [ $# -eq 3 ] || err 3 "USAGE: move_file op src dst"
385 local op="$1"
386 local src="$2"
387 local dst="$3"
388
389 if [ -f "${src}" -a ! -f "${dst}" ]; then
390 if [ "${op}" = "check" ]; then
391 msg "Move ${src} to ${dst}"
392 return 1
393 fi
394 if ! mv "${src}" "${dst}"; then
395 msg "Can't move ${src} to ${dst}"
396 return 1
397 fi
398 msg "Moved ${src} to ${dst}"
399 fi
400 return 0
401 }
402
403 # rcconf_is_set op name var [verbose] --
404 # Load the rcconf for name, and check if obsolete rc.conf(5) variable
405 # var is defined or not.
406 # Returns 0 if defined (even to ""), otherwise 1.
407 # If verbose != "", print an obsolete warning if the var is defined.
408 #
409 rcconf_is_set()
410 {
411 [ $# -ge 3 ] || err 3 "USAGE: rcconf_is_set op name var [verbose]"
412 local op="$1"
413 local name="$2"
414 local var="$3"
415 local verbose="$4"
416 local notfixed=""
417 if [ "${op}" = "fix" ]; then
418 notfixed="${NOT_FIXED}"
419 fi
420 (
421 for f in \
422 "${DEST_DIR}/etc/rc.conf" \
423 "${DEST_DIR}/etc/rc.conf.d/${name}"; do
424 [ -f "${f}" ] && . "${f}"
425 done
426 eval echo -n \"\${${var}}\" 1>&3
427 if eval "[ -n \"\${${var}+SET}\" ]"; then
428 if [ -n "${verbose}" ]; then
429 msg \
430 "Obsolete rc.conf(5) variable '\$${var}' found.${notfixed}"
431 fi
432 exit 0
433 else
434 exit 1
435 fi
436 )
437 }
438
439 # rcvar_is_enabled var
440 # Check if rcvar is enabled
441 #
442 rcvar_is_enabled()
443 {
444 [ $# -eq 1 ] || err 3 "USAGE: rcvar_is_enabled var"
445 local var="$1"
446 (
447 [ -f "${DEST_DIR}/etc/rc.conf" ] && . "${DEST_DIR}/etc/rc.conf"
448 eval val=\"\${${var}}\"
449 case $val in
450 # "yes", "true", "on", or "1"
451 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
452 exit 0
453 ;;
454
455 *)
456 exit 1
457 ;;
458 esac
459 )
460 }
461
462 # find_file_in_dirlist() file message dir1 ... --
463 # Find which directory file is in, and sets ${dir} to match.
464 # Returns 0 if matched, otherwise 1 (and sets ${dir} to "").
465 #
466 # Generally, check the directory for the "checking from source" case,
467 # and then the directory for the "checking from extracted etc.tgz" case.
468 #
469 find_file_in_dirlist()
470 {
471 [ $# -ge 3 ] || err 3 "USAGE: find_file_in_dirlist file msg dir1 ..."
472
473 local file="$1" ; shift
474 local msg="$1" ; shift
475 local dir1st= # first dir in list
476 # returns dir
477 for dir in "$@"; do
478 : ${dir1st:="${dir}"}
479 if [ -f "${dir}/${file}" ]; then
480 if [ "${dir1st}" != "${dir}" ]; then
481 msg \
482 "(Checking for ${msg} from ${dir} instead of ${dir1st})"
483 fi
484 return 0
485 fi
486 done
487 msg "Can't find source directory for ${msg}"
488 return 1
489 }
490
491 # file_exists_exact path
492 # Returns true if a file exists in the ${DEST_DIR} whose name
493 # is exactly ${path}, interpreted in a case-sensitive way
494 # even if the underlying file system is case-insensitive.
495 #
496 # The path must begin with '/' or './', and is interpreted as
497 # being relative to ${DEST_DIR}.
498 #
499 file_exists_exact()
500 {
501 [ -n "$1" ] || err 3 "USAGE: file_exists_exact path"
502 local path="${1#.}"
503 [ -h "${DEST_DIR}${path}" ] || \
504 [ -e "${DEST_DIR}${path}" ] || return 1
505 while [ "${path}" != "/" -a "${path}" != "." ] ; do
506 local dirname="$(dirname "${path}" 2>/dev/null)"
507 local basename="$(basename "${path}" 2>/dev/null)"
508 ls -fa "${DEST_DIR}${dirname}" 2> /dev/null \
509 | ${GREP} -q -F -x "${basename}" \
510 || return 1
511 path="${dirname}"
512 done
513 return 0
514 }
515
516 # obsolete_paths op
517 # Obsolete the list of paths provided on stdin.
518 # Each path should start with '/' or './', and
519 # will be interpreted relative to ${DEST_DIR}.
520 #
521 obsolete_paths()
522 {
523 [ -n "$1" ] || err 3 "USAGE: obsolete_paths fix|check"
524 local op="$1"
525 local failed=0
526 local ofile cmd ftype
527
528 while read ofile; do
529 if ! ${file_exists_exact} "${ofile}"; then
530 continue
531 fi
532 ofile="${DEST_DIR}${ofile#.}"
533 cmd="${RM}"
534 ftype="file"
535 if [ -h "${ofile}" ]; then
536 ftype="link"
537 elif [ -d "${ofile}" ]; then
538 ftype="directory"
539 cmd="rmdir"
540 elif [ ! -e "${ofile}" ]; then
541 continue
542 fi
543 if [ "${op}" = "check" ]; then
544 msg "Remove obsolete ${ftype} ${ofile}"
545 failed=1
546 elif ! eval "${cmd} \"\${ofile}\""; then
547 msg "Can't remove obsolete ${ftype} ${ofile}"
548 failed=1
549 else
550 msg "Removed obsolete ${ftype} ${ofile}"
551 fi
552 done
553 return ${failed}
554 }
555
556 # obsolete_libs dir
557 # Display the minor/teeny shared libraries in dir that are considered
558 # to be obsolete.
559 #
560 # The implementation supports removing obsolete major libraries
561 # if the awk variable AllLibs is set, although there is no way to
562 # enable that in the enclosing shell function as this time.
563 #
564 obsolete_libs()
565 {
566 [ $# -eq 1 ] || err 3 "USAGE: obsolete_libs dir"
567 local dir="$1"
568
569 _obsolete_libs "${dir}"
570 _obsolete_libs "/usr/libdata/debug/${dir}"
571 }
572
573 exclude()
574 {
575 local dollar
576 case "$1" in
577 -t)
578 dollar='$'
579 shift
580 ;;
581 *)
582 dollar=
583 ;;
584 esac
585 if [ -z "$*" ]; then
586 cat
587 else
588 eval ${GREP} -v -E "'(^$(echo $* | \
589 ${SED} -e s/\\./\\\\./g -e 's/ /'${dollar}'|^/'g)${dollar})'"
590 fi
591 }
592
593 #
594 # Find all the symlink targets of shared libraries and exclude them
595 # from consideration for removal.
596 #
597 # This protects "new old" libs after downgrading a system. Say, you
598 # had a more recent system that comes with libfoo.so.1.2 and then
599 # downgraded to an older version that had libfoo.so.1.1. You still
600 # have 1.2 that "wins" the competition for the "libfoo.so.1" stem and
601 # thus 1.1 is considered obsolete by the awk script in _obsolete_libs
602 # below. But your libfoo.so.1 now points to 1.1 so deleting 1.1 will
603 # render your system broken. exclude_libs is fed the output of
604 # potentially obsolete libs and absolves those (libfoo.so.1.1) that
605 # are a target of a symlink (libfoo.so.1 -> libfoo.so.1.1)
606 #
607 exclude_libs()
608 {
609 local targets="$(find lib*.so.* -prune -type l -exec readlink '{}' + 2> /dev/null \
610 | ${SED} -e 's@.*/@@' | ${SORT} -u)"
611 ${GREP} -F -v -x "$targets"
612 }
613
614 _obsolete_libs()
615 {
616 local dir="$1"
617
618 (
619
620 [ -e "${DEST_DIR}/${dir}" ] || return 0
621 cd "${DEST_DIR}/${dir}" || err 2 "can't cd to ${DEST_DIR}/${dir}"
622
623 # TODO: make this selectable with a command line option?
624 local maybe_purge_major
625 #maybe_purge_major='-v PurgeOldMajor=1'
626
627 printf '%s\n' lib*.so.* | ${AWK} ${maybe_purge_major} '
628 #{
629
630 BEGIN {
631 BASE_REGEX = "^lib.*\\.so\\."
632 MAJOR_REGEX = (BASE_REGEX "[0-9]+\\.")
633
634 # in the usual case different major versions of the same
635 # library are considered different stems and do not compete
636 # with each other, we keep one of each, but you may request to
637 # purge old majors, in which case all versions compete for the
638 # single basename stem
639 if (PurgeOldMajor)
640 keepone = BASE_REGEX
641 else
642 keepone = MAJOR_REGEX
643 }
644
645 # major version symlink
646 PurgeOldMajor && /^lib.*\.so\.[0-9]+$/ {
647 checklib(major, $0, BASE_REGEX)
648 }
649
650 # specific minor version of a library
651 /^lib.*\.so\.[0-9]+\.[0-9]+(\.[0-9]+)?(\.debug)?$/ {
652 checklib(minor, $0, keepone)
653 }
654
655 function checklib(latest, libname, stem_regex) {
656 if (! match(libname, stem_regex))
657 return
658 stem = substr(libname, RSTART, RLENGTH)
659 vers = substr(libname, RLENGTH + 1)
660
661 # first time we see this stem? just record the version
662 if (! (stem in latest)) {
663 latest[stem] = vers
664 return
665 }
666
667 # split version suffixes into the list of numeric components
668 oversc = split(latest[stem], overs, ".")
669 nversc = split(vers, nvers, ".")
670 maxc = (oversc > nversc) ? oversc : nversc
671
672 # is the new version "later" than the one we have seen?
673 for (i = 1; i <= maxc; ++i) {
674 cmp = (overs[i]+0) - (nvers[i]+0)
675
676 if (cmp < 0) {
677 # the one we have seen is older, so report it
678 # as obsolete and update the latest seen
679 # version to this new one
680 print stem latest[stem]
681 latest[stem] = vers
682 return
683 }
684 else if (cmp > 0) {
685 # the one we have just read is older than the
686 # one we have seen previously, so report this
687 # "new" one as obsolete
688 print libname
689 return
690 }
691 }
692 }
693
694 # the ouput is further filtered by exclude_libs that protects
695 # libraries that have symlinks pointing to them, which one
696 # encounters when downgrading
697 #}' \
698 | exclude_libs \
699 | ${SED} "s|^|${dir}/|"
700
701 )
702 }
703
704 # obsolete_stand dir
705 # Prints the names of all obsolete files and subdirs below the
706 # provided dir. dir should be something like /stand/${MACHINE}.
707 # The input dir and all output paths are interpreted
708 # relative to ${DEST_DIR}.
709 #
710 # Assumes that the numerically largest subdir is current, and all
711 # others are obsolete.
712 #
713 obsolete_stand()
714 {
715 [ $# -eq 1 ] || err 3 "USAGE: obsolete_stand dir"
716 local dir="$1"
717 local subdir
718
719 if ! [ -d "${DEST_DIR}${dir}" ]; then
720 msg "${DEST_DIR}${dir} doesn't exist; can't check for obsolete files"
721 return 1
722 fi
723
724 ( cd "${DEST_DIR}${dir}" && ls -1d [0-9]*[0-9]/. ) \
725 | ${GREP} -v '[^0-9./]' \
726 | sort -t. -r -n -k1,1 -k2,2 -k3,3 \
727 | tail -n +2 \
728 | while read subdir ; do
729 subdir="${subdir%/.}"
730 find "${DEST_DIR}${dir}/${subdir}" -depth -print
731 done \
732 | unprefix "${DEST_DIR}"
733 }
734
735 # modify_file op srcfile scratchfile awkprog
736 # Apply awkprog to srcfile sending output to scratchfile, and
737 # if appropriate replace srcfile with scratchfile.
738 #
739 modify_file()
740 {
741 [ $# -eq 4 ] || err 3 "USAGE: modify_file op file scratch awkprog"
742
743 local op="$1"
744 local file="$2"
745 local scratch="$3"
746 local prog="$4"
747 local failed=0
748 local line
749
750 ${AWK} "${prog}" < "${file}" > "${scratch}"
751 if ! cmp -s "${file}" "${scratch}"; then
752 diff "${file}" "${scratch}" > "${scratch}.diffs"
753 if [ "${op}" = "check" ]; then
754 msg "${file} needs the following changes:"
755 mffailed=1
756 elif ! ${RM} -f "${file}" ||
757 ! cp -f "${scratch}" "${file}"; then
758 msg "${file} changes not applied:"
759 mffailed=1
760 else
761 msg "${file} changes applied:"
762 fi
763 while read line; do
764 msg " ${line}"
765 done < "${scratch}.diffs"
766 fi
767 return ${failed}
768 }
769
770
771 # contents_owner op directory user group
772 # Make sure directory and contents are owned (and group-owned)
773 # as specified.
774 #
775 contents_owner()
776 {
777 [ $# -eq 4 ] || err 3 "USAGE: contents_owner op dir user group"
778
779 local op="$1"
780 local dir="$2"
781 local user="$3"
782 local grp="$4"
783 local files error
784
785 if [ "${op}" = "check" ]; then
786 files=$(find "${dir}" \( \( ! -user "${user}" \) -o \
787 \( ! -group "${grp}" \) \) )
788 error=$?
789 if [ ! -z "$files" ] || [ $error != 0 ]; then
790 msg "${dir} and contents not all owned by" \
791 "${user}:${grp}"
792 return 1
793 else
794 return 0
795 fi
796 elif [ "${op}" = "fix" ]; then
797 find "${dir}" \( \( ! -user "${user}" \) -o \
798 \( ! -group "${grp}" \) \) \
799 -exec chown "${user}:${grp}" -- {} \;
800 fi
801 }
802
803 # get_makevar var ...
804 # Retrieve the value of a user-settable system make variable
805 get_makevar()
806 {
807 local var value
808 $SOURCEMODE || err 3 "get_makevar must be used in source mode"
809 [ $# -eq 0 ] && err 3 "USAGE: get_makevar var ..."
810
811 for var in "$@"; do
812 value="$(echo '.include <bsd.own.mk>' | \
813 ${MAKE} -f - -V "\${${var}}")"
814
815 eval ${var}=\"\${value}\"
816 done
817 }
818
819 # detect_x11
820 # Detect if X11 components should be analysed and set values of
821 # relevant variables.
822 detect_x11()
823 {
824 if $SOURCEMODE; then
825 get_makevar MKX11 X11ROOTDIR X11SRCDIR
826 else
827 if [ -f "${SRC_DIR}/etc/mtree/set.xetc" ]; then
828 MKX11=yes
829 X11ROOTDIR=/this/value/isnt/used/yet
830 else
831 MKX11=no
832 X11ROOTDIR=
833 fi
834 X11SRCDIR=/nonexistent/xsrc
835 fi
836 }
837
838 #
839 # find out where MAKEDEV lives, set MAKEDEV_DIR appropriately
840 #
841 find_makedev()
842 {
843 if [ -e "${DEST_DIR}/dev/MAKEDEV" ]; then
844 MAKEDEV_DIR="${DEST_DIR}/dev"
845 elif [ -e "${DEST_DIR}/etc/MAKEDEV" ]; then
846 MAKEDEV_DIR="${DEST_DIR}/etc"
847 else
848 MAKEDEV_DIR="${DEST_DIR}/dev"
849 fi
850 }
851
852
853 #
854 # items
855 # -----
856 #
857 # NOTE: Keep these items sorted, except for obsolete* which are listed last.
858 #
859
860 #
861 # atf
862 #
863
864 handle_atf_user()
865 {
866 local op="$1"
867 local conf="$2"
868 local option="unprivileged-user"
869 local old="_atf"
870 local new="_tests"
871 local failed=0
872
873 local c=$(readlink -f "${conf}")
874 if ${GREP} -q "[^#]*${option}[ \t]*=.*${old}" "${c}"
875 then
876 if [ "${op}" = "fix" ]; then
877 ${SED} -e "/[^#]*${option}[\ t]*=/s/${old}/${new}/" \
878 "${c}" >"${c}.new"
879 failed=$(( ${failed} + $? ))
880 mv "${c}.new" "${c}"
881 failed=$(( ${failed} + $? ))
882 msg "Set ${option}=${new} in ${c}"
883 else
884 msg "${option}=${old} in ${c} should be " \
885 "${option}=${new}"
886 failed=1
887 fi
888 fi
889
890 return ${failed}
891 }
892
893 additem atf "install missing atf configuration files and validate them"
894 do_atf()
895 {
896 [ -n "$1" ] || err 3 "USAGE: do_atf fix|check"
897 local conf="${DEST_DIR}/etc/atf/common.conf"
898 local atfdir="${DEST_DIR}/etc/atf"
899 local op="$1"
900 local failed=0
901
902 # Ensure atf configuration files are in place.
903 if find_file_in_dirlist NetBSD.conf "NetBSD.conf" \
904 "${SRC_DIR}/external/bsd/atf/etc/atf" \
905 "${SRC_DIR}/etc/atf"; then
906 # ${dir} is set by find_file_in_dirlist()
907 populate_dir "${op}" true "${dir}" "${atfdir}" 644 \
908 NetBSD.conf common.conf || failed=1
909 else
910 failed=1
911 fi
912 if find_file_in_dirlist atf-run.hooks "atf-run.hooks" \
913 "${SRC_DIR}/external/bsd/atf/dist/tools/sample" \
914 "${SRC_DIR}/etc/atf"; then
915 # ${dir} is set by find_file_in_dirlist()
916 populate_dir "${op}" true "${dir}" "${atfdir}" 644 \
917 atf-run.hooks || failed=1
918 else
919 failed=1
920 fi
921
922 # Validate the _atf to _tests user/group renaming.
923 if [ -f "${conf}" ]; then
924 handle_atf_user "${op}" "${conf}" || failed=1
925 else
926 failed=1
927 fi
928
929 return ${failed}
930 }
931
932
933 #
934 # autofsconfig
935 #
936
937 additem autofsconfig "automounter configuration files"
938 do_autofsconfig()
939 {
940 [ -n "$1" ] || err 3 "USAGE: do_autofsconfig fix|check"
941 local autofs_files="
942 include_ldap
943 include_nis
944 special_hosts
945 special_media
946 special_noauto
947 special_null
948 "
949 local op="$1"
950 local failed=0
951
952 if [ "$op" = "fix" ]; then
953 mkdir -p "${DEST_DIR}/etc/autofs"
954 fi
955 failed=$(( ${failed} + $? ))
956 populate_dir "$op" true "${SRC_DIR}/etc" \
957 "${DEST_DIR}/etc" \
958 644 \
959 auto_master
960 failed=$(( ${failed} + $? ))
961 populate_dir "$op" true "${SRC_DIR}/etc/autofs" \
962 "${DEST_DIR}/etc/autofs" \
963 644 \
964 ${autofs_files}
965 return ${failed}
966 }
967
968
969 #
970 # blocklist
971 #
972
973 fixblock()
974 {
975 local op="$1"
976 local target="${DEST_DIR}$2"
977
978 if [ ! -f "${target}" ]; then
979 continue
980 fi
981
982 if ${GREP} '[bB]lack' "${target}" > /dev/null; then
983 if [ "$1" = "check" ]; then
984 msg "Fix old configuration file(s)."
985 return 1
986 else
987 local p=$(${STAT} -f %Lp "${target}")
988 chmod u+w "${target}" || return 1
989 if [ "$2" = "/etc/npf.conf" ]; then
990 ${SED} -i -e 's/"blacklistd"/"blocklistd"/g' "${target}"
991 else
992 ${SED} -i -e 's/\([bB]\)lacklist/\1locklist/g' "${target}"
993 fi
994 chmod "${p}" "${target}"
995 fi
996 fi
997 }
998
999 additem blocklist "rename old files to blocklist"
1000 do_blocklist()
1001 {
1002 [ -n "$1" ] || err 3 "USAGE: do_blocklist fix|check"
1003 local op="$1"
1004 local i old
1005
1006 # if we are actually using blocklistd
1007 for i in /var/db/blacklist.db /etc/blacklistd.conf; do
1008 old="${DEST_DIR}${i}"
1009 if [ ! -f "${old}" ]; then
1010 continue
1011 elif [ "$1" = "check" ]; then
1012 msg "Rename old file(s)."
1013 return 1
1014 fi
1015 local new=$(echo "${old}" | ${SED} s/black/block/)
1016 mv "${old}" "${new}" || return 1
1017 done
1018
1019 for i in /etc/rc.conf /etc/npf.conf /etc/blocklistd.conf \
1020 /etc/defaults/rc.conf; do
1021 fixblock "${op}" "${i}" || return 1
1022 done
1023 }
1024
1025
1026 #
1027 # bluetooth
1028 #
1029
1030 additem bluetooth "Bluetooth configuration is up to date"
1031 do_bluetooth()
1032 {
1033 [ -n "$1" ] || err 3 "USAGE: do_bluetooth fix|check"
1034 local op="$1"
1035 local failed=0
1036
1037 populate_dir "${op}" true \
1038 "${SRC_DIR}/etc/bluetooth" "${DEST_DIR}/etc/bluetooth" 644 \
1039 hosts protocols btattach.conf btdevctl.conf
1040 failed=$(( ${failed} + $? ))
1041
1042 move_file "${op}" "${DEST_DIR}/var/db/btdev.xml" \
1043 "${DEST_DIR}/var/db/btdevctl.plist"
1044 failed=$(( ${failed} + $? ))
1045
1046 local notfixed=""
1047 if [ "${op}" = "fix" ]; then
1048 notfixed="${NOT_FIXED}"
1049 fi
1050 for _v in btattach btconfig btdevctl; do
1051 if rcvar_is_enabled "${_v}"; then
1052 msg \
1053 "${_v} is obsolete in rc.conf(5)${notfixed}: use bluetooth=YES"
1054 failed=$(( ${failed} + 1 ))
1055 fi
1056 done
1057
1058 return ${failed}
1059 }
1060
1061
1062 #
1063 # catpages
1064 #
1065
1066 obsolete_catpages()
1067 {
1068 local op="$1"
1069 local basedir="$2"
1070 local section="$3"
1071 local mandir="${basedir}/man${section}"
1072 local catdir="${basedir}/cat${section}"
1073 test -d "$mandir" || return 0
1074 test -d "$catdir" || return 0
1075 (cd "$mandir" && find . -type f) | {
1076 local failed=0
1077 while read manpage; do
1078 manpage="${manpage#./}"
1079 case "$manpage" in
1080 *.Z)
1081 catname="$catdir/${manpage%.*.Z}.0"
1082 ;;
1083 *.gz)
1084 catname="$catdir/${manpage%.*.gz}.0"
1085 ;;
1086 *)
1087 catname="$catdir/${manpage%.*}.0"
1088 ;;
1089 esac
1090 test -e "$catname" -a "$catname" -ot "$mandir/$manpage" || continue
1091 if [ "${op}" = "fix" ]; then
1092 ${RM} "$catname"
1093 failed=$(( ${failed} + $? ))
1094 msg "Removed obsolete cat page $catname"
1095 else
1096 msg "Obsolete cat page $catname"
1097 failed=1
1098 fi
1099 done
1100 exit $failed
1101 }
1102 }
1103
1104 additem catpages "remove outdated cat pages"
1105 do_catpages()
1106 {
1107 local op="$1"
1108 local failed=0
1109 local manbase sec
1110 for manbase in /usr/share/man /usr/X11R6/man /usr/X11R7/man; do
1111 for sec in 1 2 3 4 5 6 7 8 9; do
1112 obsolete_catpages "$1" "${DEST_DIR}${manbase}" "${sec}"
1113 failed=$(( ${failed} + $? ))
1114 if [ "${op}" = "fix" ]; then
1115 rmdir "${DEST_DIR}${manbase}/cat${sec}"/* \
1116 2>/dev/null
1117 rmdir "${DEST_DIR}${manbase}/cat${sec}" \
1118 2>/dev/null
1119 fi
1120 done
1121 done
1122 return $failed
1123 }
1124
1125
1126 #
1127 # ddbonpanic
1128 #
1129
1130 additem ddbonpanic "verify ddb.onpanic is configured in sysctl.conf"
1131 do_ddbonpanic()
1132 {
1133 [ -n "$1" ] || err 3 "USAGE: do_ddbonpanic fix|check"
1134
1135 if ${GREP} -E '^#*[[:space:]]*ddb\.onpanic[[:space:]]*\??=[[:space:]]*[[:digit:]]+' \
1136 "${DEST_DIR}/etc/sysctl.conf" >/dev/null 2>&1
1137 then
1138 result=0
1139 else
1140 if [ "$1" = check ]; then
1141 msg \
1142 "The ddb.onpanic behaviour is not explicitly specified in /etc/sysctl.conf"
1143 result=1
1144 else
1145 echo >> "${DEST_DIR}/etc/sysctl.conf"
1146 ${SED} < "${SRC_DIR}/etc/sysctl.conf" \
1147 -e '/^ddb\.onpanic/q' | \
1148 ${SED} -e '1,/^$/d' >> \
1149 "${DEST_DIR}/etc/sysctl.conf"
1150 result=$?
1151 fi
1152 fi
1153 return ${result}
1154 }
1155
1156
1157 #
1158 # defaults
1159 #
1160
1161 additem defaults "/etc/defaults/ being up to date"
1162 do_defaults()
1163 {
1164 [ -n "$1" ] || err 3 "USAGE: do_defaults fix|check"
1165 local op="$1"
1166 local failed=0
1167 local etcsets=$(getetcsets)
1168
1169 local rc_exclude_scripts=""
1170 if $SOURCEMODE; then
1171 # For most architectures rc.conf(5) should be the same as the
1172 # one obtained from a source directory, except for the ones
1173 # that have an append file for it.
1174 local rc_conf_app="${SRC_DIR}/etc/etc.${MACHINE}/rc.conf.append"
1175 if [ -f "${rc_conf_app}" ]; then
1176 rc_exclude_scripts="rc.conf"
1177
1178 # Generate and compare the correct rc.conf(5) file
1179 mkdir "${SCRATCHDIR}/defaults"
1180
1181 cat "${SRC_DIR}/etc/defaults/rc.conf" "${rc_conf_app}" \
1182 > "${SCRATCHDIR}/defaults/rc.conf"
1183
1184 compare_dir "${op}" "${SCRATCHDIR}/defaults" \
1185 "${DEST_DIR}/etc/defaults" \
1186 444 \
1187 "rc.conf"
1188 failed=$(( ${failed} + $? ))
1189 fi
1190 fi
1191
1192 find_file_in_dirlist pf.boot.conf "pf.boot.conf" \
1193 "${SRC_DIR}/usr.sbin/pf/etc/defaults" "${SRC_DIR}/etc/defaults" \
1194 || return 1
1195 # ${dir} is set by find_file_in_dirlist()
1196 compare_dir "$op" "${dir}" "${DEST_DIR}/etc/defaults" 444 pf.boot.conf
1197 failed=$(( ${failed} + $? ))
1198
1199 rc_exclude_scripts="${rc_exclude_scripts} pf.boot.conf"
1200
1201 local rc_default_conf_files="$(select_set_files /etc/defaults/ \
1202 "/etc/defaults/\([^[:space:]]*\.conf\)" ${etcsets} | \
1203 exclude ${rc_exclude_scripts})"
1204 compare_dir "$op" "${SRC_DIR}/etc/defaults" "${DEST_DIR}/etc/defaults" \
1205 444 \
1206 ${rc_default_conf_files}
1207 failed=$(( ${failed} + $? ))
1208
1209
1210 return ${failed}
1211 }
1212
1213
1214 #
1215 # dhcpcd
1216 #
1217
1218 additem dhcpcd "dhcpcd configuration is up to date"
1219 do_dhcpcd()
1220 {
1221 [ -n "$1" ] || err 3 "USAGE: do_dhcpcd fix|check"
1222 local op="$1"
1223 local failed=0
1224
1225 find_file_in_dirlist dhcpcd.conf "dhcpcd.conf" \
1226 "${SRC_DIR}/external/bsd/dhcpcd/dist/src" \
1227 "${SRC_DIR}/etc" || return 1
1228 # ${dir} is set by find_file_in_dirlist()
1229 populate_dir "$op" true "${dir}" "${DEST_DIR}/etc" 644 dhcpcd.conf
1230 failed=$(( ${failed} + $? ))
1231
1232 check_dir "${op}" "${DEST_DIR}/var/db/dhcpcd" 755
1233 failed=$(( ${failed} + $? ))
1234
1235 move_file "${op}" \
1236 "${DEST_DIR}/etc/dhcpcd.duid" \
1237 "${DEST_DIR}/var/db/dhcpcd/duid"
1238 failed=$(( ${failed} + $? ))
1239
1240 move_file "${op}" \
1241 "${DEST_DIR}/etc/dhcpcd.secret" \
1242 "${DEST_DIR}/var/db/dhcpcd/secret"
1243 failed=$(( ${failed} + $? ))
1244
1245 move_file "${op}" \
1246 "${DEST_DIR}/var/db/dhcpcd-rdm.monotonic" \
1247 "${DEST_DIR}/var/db/dhcpcd/rdm_monotonic"
1248 failed=$(( ${failed} + $? ))
1249
1250 for lease in "${DEST_DIR}/var/db/dhcpcd-"*.lease*; do
1251 [ -f "${lease}" ] || continue
1252 new_lease=$(basename "${lease}" | ${SED} -e 's/dhcpcd-//')
1253 new_lease="${DEST_DIR}/var/db/dhcpcd/${new_lease}"
1254 move_file "${op}" "${lease}" "${new_lease}"
1255 failed=$(( ${failed} + $? ))
1256 done
1257
1258 chroot_dir="${DEST_DIR}/var/chroot/dhcpcd"
1259 move_file "${op}" \
1260 "${chroot_dir}/var/db/dhcpcd/duid" \
1261 "${DEST_DIR}/var/db/dhcpcd/duid"
1262 failed=$(( ${failed} + $? ))
1263
1264 move_file "${op}" \
1265 "${chroot_dir}/var/db/dhcpcd/secret" \
1266 "${DEST_DIR}/var/db/dhcpcd/secret"
1267 failed=$(( ${failed} + $? ))
1268
1269 move_file "${op}" \
1270 "${chroot_dir}/var/db/dhcpcd/rdm_monotonic" \
1271 "${DEST_DIR}/var/db/dhcpcd/rdm_monotonic"
1272 failed=$(( ${failed} + $? ))
1273
1274 for lease in "${chroot_dir}/var/db/dhcpcd/"*.lease*; do
1275 [ -f "${lease}" ] || continue
1276 new_lease="${DEST_DIR}/var/db/dhcpcd/$(basename ${lease})"
1277 move_file "${op}" "${lease}" "${new_lease}"
1278 failed=$(( ${failed} + $? ))
1279 done
1280
1281 # Ensure chroot is now empty
1282 for dir in \
1283 $(find ${chroot_dir} ! -type d) \
1284 $(find ${chroot_dir} -type d -mindepth 1 | sort -r)
1285 do
1286 echo "/var/chroot/dhcpcd${dir##${chroot_dir}}"
1287 done | obsolete_paths "${op}"
1288 failed=$(( ${failed} + $? ))
1289
1290 contents_owner "${op}" "${DEST_DIR}/var/db/dhcpcd" root wheel
1291 failed=$(( ${failed} + $? ))
1292
1293 return ${failed}
1294 }
1295
1296
1297 #
1298 # dhcpcdrundir
1299 #
1300
1301 additem dhcpcdrundir "accidentally created /@RUNDIR@ does not exist"
1302 do_dhcpcdrundir()
1303 {
1304 [ -n "$1" ] || err 3 "USAGE: do_dhcpcdrundir fix|check"
1305 local op="$1"
1306 local failed=0
1307
1308 if [ -d "${DEST_DIR}/@RUNDIR@" ]; then
1309 if [ "${op}" = "check" ]; then
1310 msg "Remove erroneously created /@RUNDIR@"
1311 failed=1
1312 elif ! ${RM} -r "${DEST_DIR}/@RUNDIR@"; then
1313 msg "Failed to remove ${DEST_DIR}/@RUNDIR@"
1314 failed=1
1315 else
1316 msg "Removed erroneously created ${DEST_DIR}/@RUNDIR@"
1317 fi
1318 fi
1319 return ${failed}
1320 }
1321
1322
1323 #
1324 # envsys
1325 #
1326
1327 additem envsys "envsys configuration is up to date"
1328 do_envsys()
1329 {
1330 [ -n "$1" ] || err 3 "USAGE: do_envsys fix|check"
1331 local op="$1"
1332 local failed=0
1333 local etcsets=$(getetcsets)
1334
1335 populate_dir "$op" true "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
1336 envsys.conf
1337 failed=$(( ${failed} + $? ))
1338
1339 local powerd_scripts="$(select_set_files /etc/powerd/scripts/ \
1340 "/etc/powerd/scripts/\([^[:space:]/]*\)" ${etcsets})"
1341
1342 populate_dir "$op" true "${SRC_DIR}/etc/powerd/scripts" \
1343 "${DEST_DIR}/etc/powerd/scripts" \
1344 555 \
1345 ${powerd_scripts}
1346 failed=$(( ${failed} + $? ))
1347
1348 return ${failed}
1349 }
1350
1351
1352 #
1353 # fontconfig
1354 #
1355
1356 additem fontconfig "X11 font configuration is up to date"
1357 do_fontconfig()
1358 {
1359 [ -n "$1" ] || err 3 "USAGE: do_fontconfig fix|check"
1360 local op="$1"
1361 local failed=0
1362
1363 # First, check for updates we can handle.
1364 if ! $SOURCEMODE; then
1365 FONTCONFIG_DIR="${SRC_DIR}/etc/fonts/conf.avail"
1366 else
1367 FONTCONFIG_DIR="${XSRC_DIR}/external/mit/fontconfig/dist/conf.d"
1368 fi
1369
1370 if [ ! -d "${FONTCONFIG_DIR}" ]; then
1371 msg "${FONTCONFIG_DIR} is not a directory; skipping check"
1372 return 0
1373 fi
1374 local regular_fonts="
1375 10-autohint.conf
1376 10-scale-bitmap-fonts.conf
1377 10-sub-pixel-bgr.conf
1378 10-sub-pixel-none.conf
1379 10-sub-pixel-rgb.conf
1380 10-sub-pixel-vbgr.conf
1381 10-sub-pixel-vrgb.conf
1382 10-unhinted.conf
1383 11-lcdfilter-default.conf
1384 11-lcdfilter-legacy.conf
1385 11-lcdfilter-light.conf
1386 20-unhint-small-vera.conf
1387 25-unhint-nonlatin.conf
1388 30-metric-aliases.conf
1389 40-nonlatin.conf
1390 45-generic.conf
1391 45-latin.conf
1392 49-sansserif.conf
1393 50-user.conf
1394 51-local.conf
1395 60-generic.conf
1396 60-latin.conf
1397 65-fonts-persian.conf
1398 65-khmer.conf
1399 65-nonlatin.conf
1400 69-unifont.conf
1401 70-no-bitmaps.conf
1402 70-yes-bitmaps.conf
1403 80-delicious.conf
1404 90-synthetic.conf
1405 "
1406 populate_dir "$op" false "${FONTCONFIG_DIR}" \
1407 "${DEST_DIR}/etc/fonts/conf.avail" \
1408 444 \
1409 ${regular_fonts}
1410 failed=$(( ${failed} + $? ))
1411
1412 if ! $SOURCEMODE; then
1413 FONTS_DIR="${SRC_DIR}/etc/fonts"
1414 else
1415 FONTS_DIR="${SRC_DIR}/external/mit/xorg/lib/fontconfig/etc"
1416 fi
1417
1418 populate_dir "$op" false "${FONTS_DIR}" "${DEST_DIR}/etc/fonts" 444 \
1419 fonts.conf
1420 failed=$(( ${failed} + $? ))
1421
1422 # We can't modify conf.d easily; someone might have removed a file.
1423
1424 # Look for old files that need to be deleted.
1425 local obsolete_fonts="
1426 10-autohint.conf
1427 10-no-sub-pixel.conf
1428 10-sub-pixel-bgr.conf
1429 10-sub-pixel-rgb.conf
1430 10-sub-pixel-vbgr.conf
1431 10-sub-pixel-vrgb.conf
1432 10-unhinted.conf
1433 25-unhint-nonlatin.conf
1434 65-khmer.conf
1435 70-no-bitmaps.conf
1436 70-yes-bitmaps.conf
1437 "
1438 local failed_fonts=""
1439 for i in ${obsolete_fonts}; do
1440 if [ -f "${DEST_DIR}/etc/fonts/conf.d/$i" ]; then
1441 conf_d_failed=1
1442 failed_fonts="$failed_fonts $i"
1443 fi
1444 done
1445
1446 if [ -n "$failed_fonts" ]; then
1447 msg \
1448 "Broken fontconfig configuration found; please delete these files:"
1449 msg "[$failed_fonts]"
1450 failed=$(( ${failed} + 1 ))
1451 fi
1452
1453 return ${failed}
1454 }
1455
1456
1457 #
1458 # gid
1459 #
1460
1461 additem gid "required groups in /etc/group"
1462 do_gid()
1463 {
1464 [ -n "$1" ] || err 3 "USAGE: do_gid fix|check"
1465
1466 check_ids "$1" groups "${DEST_DIR}/etc/group" \
1467 "${SRC_DIR}/etc/group" 14 \
1468 named ntpd sshd SKIP _pflogd _rwhod staff _proxy _timedc \
1469 _sdpd _httpd _mdnsd _tests _tcpdump _tss _gpio _rtadvd SKIP \
1470 _unbound _nsd nvmm _dhcpcd
1471 }
1472
1473
1474 #
1475 # gpio
1476 #
1477
1478 additem gpio "gpio configuration is up to date"
1479 do_gpio()
1480 {
1481 [ -n "$1" ] || err 3 "USAGE: do_gpio fix|check"
1482 local op="$1"
1483 local failed=0
1484
1485 populate_dir "$op" true "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
1486 gpio.conf
1487 failed=$(( ${failed} + $? ))
1488
1489 return ${failed}
1490 }
1491
1492
1493 #
1494 # hosts
1495 #
1496
1497 additem hosts "/etc/hosts being up to date"
1498 do_hosts()
1499 {
1500 [ -n "$1" ] || err 3 "USAGE: do_hosts fix|check"
1501
1502 modify_file "$1" "${DEST_DIR}/etc/hosts" "${SCRATCHDIR}/hosts" '
1503 /^(127\.0\.0\.1|::1)[ ]+[^\.]*$/ {
1504 print $0, "localhost."
1505 next
1506 }
1507 { print }
1508 '
1509 return $?
1510 }
1511
1512
1513 #
1514 # iscsi
1515 #
1516
1517 additem iscsi "/etc/iscsi is populated"
1518 do_iscsi()
1519 {
1520 [ -n "$1" ] || err 3 "USAGE: do_iscsi fix|check"
1521
1522 populate_dir "${op}" true \
1523 "${SRC_DIR}/etc/iscsi" "${DEST_DIR}/etc/iscsi" 600 auths
1524 populate_dir "${op}" true \
1525 "${SRC_DIR}/etc/iscsi" "${DEST_DIR}/etc/iscsi" 644 targets
1526 return $?
1527 }
1528
1529
1530 #
1531 # mailerconf
1532 #
1533
1534 adddisableditem mailerconf "update /etc/mailer.conf after sendmail removal"
1535 do_mailerconf()
1536 {
1537 [ -n "$1" ] || err 3 "USAGE: do_mailterconf fix|check"
1538 local op="$1"
1539
1540 local failed=0
1541 mta_path="$(${AWK} '/^sendmail[ \t]/{print$2}' \
1542 "${DEST_DIR}/etc/mailer.conf")"
1543 old_sendmail_path="/usr/libexec/sendmail/sendmail"
1544 if [ "${mta_path}" = "${old_sendmail_path}" ]; then
1545 if [ "$op" = check ]; then
1546 msg "mailer.conf points to obsolete ${old_sendmail_path}"
1547 failed=1;
1548 else
1549 populate_dir "${op}" false \
1550 "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 mailer.conf
1551 failed=$?
1552 fi
1553 fi
1554
1555 return ${failed}
1556 }
1557
1558
1559 #
1560 # makedev
1561 #
1562
1563 additem makedev "/dev/MAKEDEV being up to date"
1564 do_makedev()
1565 {
1566 [ -n "$1" ] || err 3 "USAGE: do_makedev fix|check"
1567 local failed=0
1568
1569 if [ -f "${SRC_DIR}/etc/MAKEDEV.tmpl" ]; then
1570 # generate MAKEDEV from source if source is available
1571 env MACHINE="${MACHINE}" \
1572 MACHINE_ARCH="${MACHINE_ARCH}" \
1573 NETBSDSRCDIR="${SRC_DIR}" \
1574 ${AWK} -f "${SRC_DIR}/etc/MAKEDEV.awk" \
1575 "${SRC_DIR}/etc/MAKEDEV.tmpl" > "${SCRATCHDIR}/MAKEDEV"
1576 fi
1577
1578 find_file_in_dirlist MAKEDEV "MAKEDEV" \
1579 "${SCRATCHDIR}" "${SRC_DIR}/dev" \
1580 || return 1
1581 # ${dir} is set by find_file_in_dirlist()
1582 find_makedev
1583 compare_dir "$1" "${dir}" "${MAKEDEV_DIR}" 555 MAKEDEV
1584 failed=$(( ${failed} + $? ))
1585
1586 find_file_in_dirlist MAKEDEV.local "MAKEDEV.local" \
1587 "${SRC_DIR}/etc" "${SRC_DIR}/dev" \
1588 || return 1
1589 # ${dir} is set by find_file_in_dirlist()
1590 compare_dir "$1" "${dir}" "${DEST_DIR}/dev" 555 MAKEDEV.local
1591 failed=$(( ${failed} + $? ))
1592
1593 return ${failed}
1594 }
1595
1596
1597 #
1598 # man.conf
1599 #
1600
1601 additem manconf "check for a mandoc usage in /etc/man.conf"
1602 do_manconf()
1603 {
1604 [ -n "$1" ] || err 3 "USAGE: do_manconf fix|check"
1605 local op="$1"
1606 local failed=0
1607
1608 [ -f "${DEST_DIR}/etc/man.conf" ] || return 0
1609 if ${GREP} -w "mandoc" "${DEST_DIR}/etc/man.conf" >/dev/null 2>&1;
1610 then
1611 failed=0;
1612 else
1613 failed=1
1614 notfixed=""
1615 if [ "${op}" = "fix" ]; then
1616 notfixed="${NOT_FIXED}"
1617 fi
1618 msg "The file /etc/man.conf has not been adapted to mandoc usage; you"
1619 msg "probably want to copy a new version over. ${notfixed}"
1620 fi
1621
1622 return ${failed}
1623 }
1624
1625
1626 #
1627 # motd
1628 #
1629
1630 additem motd "contents of motd"
1631 do_motd()
1632 {
1633 [ -n "$1" ] || err 3 "USAGE: do_motd fix|check"
1634
1635 if ${GREP} -i 'http://www.NetBSD.org/Misc/send-pr.html' \
1636 "${DEST_DIR}/etc/motd" >/dev/null 2>&1 \
1637 || ${GREP} -i 'https*://www.NetBSD.org/support/send-pr.html' \
1638 "${DEST_DIR}/etc/motd" >/dev/null 2>&1
1639 then
1640 tmp1="$(mktemp /tmp/postinstall.motd.XXXXXXXX)"
1641 tmp2="$(mktemp /tmp/postinstall.motd.XXXXXXXX)"
1642 ${SED} '1,2d' <"${SRC_DIR}/etc/motd" >"${tmp1}"
1643 ${SED} '1,2d' <"${DEST_DIR}/etc/motd" >"${tmp2}"
1644
1645 if [ "$1" = check ]; then
1646 cmp -s "${tmp1}" "${tmp2}"
1647 result=$?
1648 if [ "${result}" -ne 0 ]; then
1649 msg \
1650 "Bug reporting messages do not seem to match the installed release"
1651 fi
1652 else
1653 head -n 2 "${DEST_DIR}/etc/motd" >"${tmp1}"
1654 ${SED} '1,2d' <"${SRC_DIR}/etc/motd" >>"${tmp1}"
1655 cp "${tmp1}" "${DEST_DIR}/etc/motd"
1656 result=0
1657 fi
1658
1659 ${RM} -f "${tmp1}" "${tmp2}"
1660 else
1661 result=0
1662 fi
1663
1664 return ${result}
1665 }
1666
1667
1668 #
1669 # mtree
1670 #
1671
1672 additem mtree "/etc/mtree/ being up to date"
1673 do_mtree()
1674 {
1675 [ -n "$1" ] || err 3 "USAGE: do_mtree fix|check"
1676 local failed=0
1677
1678 compare_dir "$1" "${SRC_DIR}/etc/mtree" "${DEST_DIR}/etc/mtree" 444 special
1679 failed=$(( ${failed} + $? ))
1680
1681 if ! $SOURCEMODE; then
1682 MTREE_DIR="${SRC_DIR}/etc/mtree"
1683 else
1684 ${RM} -rf "${SCRATCHDIR}/obj"
1685 mkdir "${SCRATCHDIR}/obj"
1686 ${MAKE} -s -C "${SRC_DIR}/etc/mtree" TOOL_AWK="${AWK}" \
1687 MAKEOBJDIR="${SCRATCHDIR}/obj" emit_dist_file > \
1688 "${SCRATCHDIR}/NetBSD.dist"
1689 MTREE_DIR="${SCRATCHDIR}"
1690 ${RM} -rf "${SCRATCHDIR}/obj"
1691 fi
1692 compare_dir "$1" "${MTREE_DIR}" "${DEST_DIR}/etc/mtree" 444 NetBSD.dist
1693 failed=$(( ${failed} + $? ))
1694
1695 return ${failed}
1696 }
1697
1698
1699 #
1700 # named
1701 #
1702 handle_named_conf()
1703 {
1704 local op="$1"
1705 local option="dnssec-enable"
1706 local failed=0
1707 local conf
1708
1709 shift
1710
1711 for conf; do
1712 local c=$(readlink -f "${conf}")
1713 if ! ${GREP} -qs "${option}" "${c}"
1714 then
1715 continue
1716 fi
1717
1718 if [ "${op}" = "fix" ]; then
1719 ${SED} -e "/${option}/d" "${c}" > "${c}.new"
1720 failed=$(( ${failed} + $? ))
1721 mv "${c}.new" "${c}"
1722 failed=$(( ${failed} + $? ))
1723 msg "Removed obsolete '${option}' in ${c}"
1724 else
1725 msg "'${option}' option in ${c} should be removed"
1726 failed=$(( ${failed} + 1 ))
1727 fi
1728 done
1729
1730 return ${failed}
1731 }
1732
1733 additem named "named configuration update"
1734 do_named()
1735 {
1736 local oldconf="${DEST_DIR}/etc/namedb/named.conf"
1737 local conf="${DEST_DIR}/etc/named.conf"
1738 [ -n "$1" ] || err 3 "USAGE: do_named fix|check"
1739 local op="$1"
1740
1741 move_file "${op}" "${oldconf}" "${conf}"
1742 handle_named_conf "${op}" "${oldconf}" "${conf}"
1743
1744 compare_dir "${op}" "${SRC_DIR}/etc/namedb" "${DEST_DIR}/etc/namedb" \
1745 644 \
1746 root.cache
1747
1748 local od="${DEST_DIR}/usr/libexec/named"
1749 if [ -d "$od" ]; then
1750 rm -fr "$od"
1751 msg "Removed obsolete '${od}'"
1752 fi
1753 }
1754
1755
1756 #
1757 # opensslcertsconf
1758 #
1759
1760 additem opensslcertsconf "ensure TLS trust anchor configuration exists"
1761 do_opensslcertsconf()
1762 {
1763 local certsdir certsconf defaultconf manualmsg
1764
1765 [ -n "$1" ] || err 3 "USAGE: do_opensslcertsconf fix|check"
1766
1767 certsdir="${DEST_DIR}/etc/openssl/certs"
1768 certsconf="${DEST_DIR}/etc/openssl/certs.conf"
1769 defaultconf="${DEST_DIR}/usr/share/examples/certctl/certs.conf"
1770
1771 case $1 in
1772 check) if [ ! -r "$certsconf" ]; then
1773 msg "/etc/openssl/certs.conf missing; see certctl(8)"
1774 return 1
1775 fi
1776 ;;
1777 fix) # If /etc/openssl/certs.conf is already there, nothing
1778 # to do.
1779 if [ -r "$certsconf" ]; then
1780 return 0
1781 fi
1782
1783 # If /etc/openssl/certs is a symlink, or exists but is
1784 # not a directory, or is a directory but is nonempty,
1785 # then either it's managed by someone else or something
1786 # fishy is afoot. So set it manual in that case.
1787 # Otherwise, install the default config file.
1788 if [ -h "$certsdir" ] ||
1789 [ -e "$certsdir" -a ! -d "$certsdir" ] ||
1790 ([ -d "$certsdir" ] &&
1791 find -f "$certsdir" -- \
1792 -maxdepth 0 -type d -empty -exit 1)
1793 then
1794 msg "/etc/openssl/certs appears manually configured"
1795 manualmsg="[existing /etc/openssl/certs configuration"
1796 manualmsg="$manualmsg detected by postinstall(8)]"
1797 # Change the commented-out `#manual' line to
1798 # uncommented `manual', or print an error
1799 # message if there is no `#manual' line and put
1800 # `manual' at the end.
1801 awk -v defaultconf="$defaultconf" \
1802 -v manualmsg="$manualmsg" '
1803 BEGIN {
1804 manual = 0
1805 }
1806 /^#manual/ && !manual {
1807 manual = 1
1808 sub(/^#/, "")
1809 print
1810 print "#", manualmsg
1811 next
1812 }
1813 {
1814 print
1815 }
1816 END {
1817 if (!manual) {
1818 printf "warning: %s %s?\n", \
1819 "corrupt", defaultconf \
1820 >"/dev/stderr"
1821 print "manual"
1822 print "#", manualmsg
1823 }
1824 }
1825 ' <$defaultconf >${certsconf}.tmp
1826 else
1827 msg "installing default /etc/openssl/certs.conf"
1828 cat <$defaultconf >${certsconf}.tmp
1829 fi && mv -f -- "${certsconf}.tmp" "$certsconf"
1830 ;;
1831 *) err 3 "USAGE: do_opensslcerts fix|check"
1832 ;;
1833 esac
1834 }
1835
1836
1837 #
1838 # opensslcertsrehash
1839 #
1840
1841 additem opensslcertsrehash "make /etc/openssl/certs cache of TLS trust anchors"
1842 do_opensslcertsrehash()
1843 {
1844 local mtreekeys scratchdir
1845
1846 [ -n "$1" ] || err 3 "USAGE: do_opensslcertsrehash fix|check"
1847
1848 if [ ! -r "${DEST_DIR}/etc/openssl/certs.conf" ]; then
1849 msg "/etc/openssl/certs.conf missing; see certctl(8)"
1850 return 1
1851 fi
1852
1853 case $1 in
1854 check) # Create a scratch rehash for comparison.
1855 mtreekeys="type,link"
1856 scratchdir="${SCRATCHDIR}/opensslcerts"
1857 /usr/sbin/certctl -c "$scratchdir" rehash || return $?
1858
1859 # This will create ${scratchdir}/.certctl unless the
1860 # configuration is manual. If the configuration is
1861 # manual, stop here; nothing to do. certctl(8) will
1862 # have already printed a message about that.
1863 #
1864 # XXX Grody to rely on the internal structure used by
1865 # certctl(8), but less bad than having two versions of
1866 # the config parsing logic.
1867 if [ ! -f "${scratchdir}/.certctl" ]; then
1868 return 0
1869 fi
1870
1871 # Do a dry run of rehashing into the real
1872 # /etc/openssl/certs. This curious extra step ensures
1873 # that we report a failure if /etc/openssl/certs
1874 # appears to be managed manually, but `manual' was not
1875 # specified in /etc/openssl/certs.conf.
1876 /usr/sbin/certctl -n rehash || return $?
1877
1878 # Compare the trees with mtree(8). Inconveniently,
1879 # mtree returns status zero even if there are missing
1880 # or extra files. So instead of examining the return
1881 # status, test for any output. Empty output means
1882 # everything matches; otherwise the mismatch, missing,
1883 # or extra files are output.
1884 mtree -p "$scratchdir" -c -k "$mtreekeys" \
1885 | mtree -p "${DEST_DIR}/etc/openssl/certs" 2>&1 \
1886 | {
1887 while read -r line; do
1888 # mismatch, missing, or extra
1889 msg "/etc/openssl/certs needs rehash"
1890 exit 1
1891 done
1892 exit 0
1893 }
1894 ;;
1895 fix) # This runs openssl(1), which is not available as a
1896 # build-time tool. So for now, restrict it to running
1897 # on the installed system.
1898 case $DEST_DIR in
1899 ''|/) ;;
1900 *) msg "opensslcertsrehash limited to DEST_DIR=/"
1901 return 1
1902 ;;
1903 esac
1904 /usr/sbin/certctl rehash
1905 ;;
1906 *) err 3 "USAGE: do_opensslcerts fix|check"
1907 ;;
1908 esac
1909 }
1910
1911
1912 #
1913 # pam
1914 #
1915
1916 additem pam "/etc/pam.d is populated"
1917 do_pam()
1918 {
1919 [ -n "$1" ] || err 3 "USAGE: do_pam fix|check"
1920 local op="$1"
1921 local failed=0
1922
1923 populate_dir "${op}" true "${SRC_DIR}/etc/pam.d" \
1924 "${DEST_DIR}/etc/pam.d" 644 \
1925 README cron display_manager ftpd gdm imap kde login other \
1926 passwd pop3 ppp racoon rexecd rsh sshd su system telnetd \
1927 xdm xserver
1928
1929 failed=$(( ${failed} + $? ))
1930
1931 return ${failed}
1932 }
1933
1934
1935 #
1936 # periodic
1937 #
1938
1939 additem periodic "/etc/{daily,weekly,monthly,security} being up to date"
1940 do_periodic()
1941 {
1942 [ -n "$1" ] || err 3 "USAGE: do_periodic fix|check"
1943
1944 compare_dir "$1" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
1945 daily weekly monthly security
1946 }
1947
1948
1949 #
1950 # pf
1951 #
1952
1953 additem pf "pf configuration being up to date"
1954 do_pf()
1955 {
1956 [ -n "$1" ] || err 3 "USAGE: do_pf fix|check"
1957 local op="$1"
1958 local failed=0
1959
1960 find_file_in_dirlist pf.os "pf.os" \
1961 "${SRC_DIR}/dist/pf/etc" "${SRC_DIR}/etc" \
1962 || return 1
1963 # ${dir} is set by find_file_in_dirlist()
1964 populate_dir "${op}" true \
1965 "${dir}" "${DEST_DIR}/etc" 644 \
1966 pf.conf
1967 failed=$(( ${failed} + $? ))
1968
1969 compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 pf.os
1970 failed=$(( ${failed} + $? ))
1971
1972 return ${failed}
1973 }
1974
1975
1976 #
1977 # ptyfsoldnodes
1978 #
1979
1980 additem ptyfsoldnodes "remove legacy device nodes when using ptyfs"
1981 do_ptyfsoldnodes()
1982 {
1983 [ -n "$1" ] || err 3 "USAGE: do_ptyfsoldnodes fix|check"
1984 local op="$1"
1985
1986 # Check whether ptyfs is in use
1987 local failed=0;
1988 if ! ${GREP} -E "^ptyfs" "${DEST_DIR}/etc/fstab" > /dev/null; then
1989 msg "ptyfs is not in use"
1990 return 0
1991 fi
1992
1993 if [ ! -e "${DEST_DIR}/dev/pts" ]; then
1994 msg "ptyfs is not properly configured: missing /dev/pts"
1995 return 1
1996 fi
1997
1998 # Find the device major numbers for the pty master and slave
1999 # devices, by parsing the output from "MAKEDEV -s pty0".
2000 #
2001 # Output from MAKEDEV looks like this:
2002 # ./ttyp0 type=char device=netbsd,5,0 mode=666 gid=0 uid=0
2003 # ./ptyp0 type=char device=netbsd,6,0 mode=666 gid=0 uid=0
2004 #
2005 # Output from awk, used in the eval statement, looks like this:
2006 # maj_ptym=6; maj_ptys=5;
2007 #
2008 local maj_ptym maj_ptys
2009 find_makedev
2010 eval "$(
2011 ${HOST_SH} "${MAKEDEV_DIR}/MAKEDEV" -s pty0 2>/dev/null \
2012 | ${AWK} '\
2013 BEGIN { before_re = ".*device=[a-zA-Z]*,"; after_re = ",.*"; }
2014 /ptyp0/ { maj_ptym = gensub(before_re, "", 1, $0);
2015 maj_ptym = gensub(after_re, "", 1, maj_ptym); }
2016 /ttyp0/ { maj_ptys = gensub(before_re, "", 1, $0);
2017 maj_ptys = gensub(after_re, "", 1, maj_ptys); }
2018 END { print "maj_ptym=" maj_ptym "; maj_ptys=" maj_ptys ";"; }
2019 '
2020 )"
2021 #msg "Major numbers are maj_ptym=${maj_ptym} maj_ptys=${maj_ptys}"
2022 if [ -z "$maj_ptym" ] || [ -z "$maj_ptys" ]; then
2023 msg "Cannot find device major numbers for pty master and slave"
2024 return 1
2025 fi
2026
2027 # look for /dev/[pt]ty[p-zP-T][0-9a-zA-Z], and check that they
2028 # have the expected device major numbers. ttyv* is typically not a
2029 # pty device, but we check it anyway.
2030 #
2031 # The "for d1" loop is intended to avoid overflowing ARG_MAX;
2032 # otherwise we could have used a single glob pattern.
2033 #
2034 # If there are no files that match a particular pattern,
2035 # then stat prints something like:
2036 # stat: /dev/[pt]tyx?: lstat: No such file or directory
2037 # and we ignore it. XXX: We also ignore other error messages.
2038 #
2039 local d1 major node
2040 local tmp="$(mktemp /tmp/postinstall.ptyfs.XXXXXXXX)"
2041
2042 for d1 in p q r s t u v w x y z P Q R S T; do
2043 ${STAT} -f "%Hr %N" "${DEST_DIR}/dev/"[pt]ty${d1}? 2>&1
2044 done \
2045 | while read -r major node ; do
2046 case "$major" in
2047 ${maj_ptym}|${maj_ptys}) echo "$node" ;;
2048 esac
2049 done > "${tmp}"
2050
2051 local desc="legacy device node"
2052 while read node; do
2053 if [ "${op}" = "check" ]; then
2054 msg "Remove ${desc} ${node}"
2055 failed=1
2056 else # "fix"
2057 if ${RM} "${node}"; then
2058 msg "Removed ${desc} ${node}"
2059 else
2060 warn "Failed to remove ${desc} ${node}"
2061 failed=1
2062 fi
2063 fi
2064 done < "${tmp}"
2065 ${RM} "${tmp}"
2066
2067 return ${failed}
2068 }
2069
2070
2071 #
2072 # pwd_mkdb
2073 #
2074
2075 additem pwd_mkdb "passwd database version"
2076 do_pwd_mkdb()
2077 {
2078 [ -n "$1" ] || err 3 "USAGE: do_pwd_mkdb fix|check"
2079 local op="$1"
2080 local failed=0
2081
2082 # XXX Ideally, we should figure out the endianness of the
2083 # target machine, and add "-E B"/"-E L" to the db(1) flags,
2084 # and "-B"/"-L" to the pwd_mkdb(8) flags if the target is not
2085 # the same as the host machine. It probably doesn't matter,
2086 # because we don't expect "postinstall fix pwd_mkdb" to be
2087 # invoked during a cross build.
2088
2089 set -- $(${DB} -q -Sb -Ub -To -N hash "${DEST_DIR}/etc/pwd.db" \
2090 'VERSION\0')
2091 case "$2" in
2092 '\001\000\000\000') return 0 ;; # version 1, little-endian
2093 '\000\000\000\001') return 0 ;; # version 1, big-endian
2094 esac
2095
2096 if [ "${op}" = "check" ]; then
2097 msg "Update format of passwd database"
2098 failed=1
2099 elif ! ${PWD_MKDB} -V 1 -d "${DEST_DIR:-/}" \
2100 "${DEST_DIR}/etc/master.passwd";
2101 then
2102 msg "Can't update format of passwd database"
2103 failed=1
2104 else
2105 msg "Updated format of passwd database"
2106 fi
2107
2108 return ${failed}
2109 }
2110
2111
2112 #
2113 # rc
2114 #
2115
2116 # There is no info in src/distrib or /etc/mtree which rc* files
2117 # can be overwritten unconditionally on upgrade. See PR/54741.
2118 rc_644_files="
2119 rc
2120 rc.subr
2121 rc.shutdown
2122 "
2123
2124 rc_obsolete_vars="
2125 amd amd_master
2126 btcontrol btcontrol_devices
2127 critical_filesystems critical_filesystems_beforenet
2128 mountcritlocal mountcritremote
2129 network ip6forwarding
2130 network nfsiod_flags
2131 sdpd sdpd_control
2132 sdpd sdpd_groupname
2133 sdpd sdpd_username
2134 sysctl defcorename
2135 "
2136
2137 update_rc()
2138 {
2139 local op=$1
2140 local dir=$2
2141 local name=$3
2142 local bindir=$4
2143 local rcdir=$5
2144
2145 if [ ! -x "${DEST_DIR}/${bindir}/${name}" ]; then
2146 return 0
2147 fi
2148
2149 if ! find_file_in_dirlist "${name}" "${name}" \
2150 "${rcdir}" "${SRC_DIR}/etc/rc.d"; then
2151 return 1
2152 fi
2153 populate_dir "${op}" false "${dir}" "${DEST_DIR}/etc/rc.d" 555 "${name}"
2154 return $?
2155 }
2156
2157 # select non-obsolete files in a sets file
2158 # $1: directory pattern
2159 # $2: file pattern
2160 # $3: filename
2161 select_set_files()
2162 {
2163 local qdir="$(echo $1 | ${SED} -e s@/@\\\\/@g -e s/\\./\\\\./g)"
2164 ${SED} -n -e /obsolete/d \
2165 -e "/^\.${qdir}/s@^.$2[[:space:]].*@\1@p" $3
2166 }
2167
2168 # select obsolete files in a sets file
2169 # $1: directory pattern
2170 # $2: file pattern
2171 # $3: setname
2172 select_obsolete_files()
2173 {
2174 if $SOURCEMODE; then
2175 ${SED} -n -e "/obsolete/s@\.$1$2[[:space:]].*@\1@p" \
2176 "${SRC_DIR}/distrib/sets/lists/$3/mi"
2177 return
2178 fi
2179
2180 # On upgrade builds we don't extract the "etc" set so we
2181 # try to use the source set instead. See PR/54730 for
2182 # ways to better handle this.
2183
2184 local obsolete_dir
2185
2186 if [ $3 = "etc" ] ;then
2187 obsolete_dir="${SRC_DIR}/var/db/obsolete"
2188 else
2189 obsolete_dir="${DEST_DIR}/var/db/obsolete"
2190 fi
2191 ${SED} -n -e "s@\.$1$2\$@\1@p" "${obsolete_dir}/$3"
2192 }
2193
2194 getetcsets()
2195 {
2196 if $SOURCEMODE; then
2197 echo "${SRC_DIR}/distrib/sets/lists/etc/mi"
2198 else
2199 echo "${SRC_DIR}/etc/mtree/set.etc"
2200 fi
2201 }
2202
2203 additem rc "/etc/rc* and /etc/rc.d/ being up to date"
2204 do_rc()
2205 {
2206 [ -n "$1" ] || err 3 "USAGE: do_rc fix|check"
2207 local op="$1"
2208 local failed=0
2209 local generated_scripts=""
2210 local etcsets=$(getetcsets)
2211 if [ "${MKX11}" != "no" ]; then
2212 generated_scripts="${generated_scripts} xdm xfs"
2213 fi
2214
2215 # Directories of external programs that have rc files (in bsd)
2216 local rc_external_files="blocklist nsd unbound"
2217
2218 # rc* files in /etc/
2219 # XXX: at least rc.conf and rc.local shouldn't be updated. PR/54741
2220 #local rc_644_files="$(select_set_files /etc/rc \
2221 # "/etc/\(rc[^[:space:]/]*\)" ${etcsets})"
2222
2223 # no-obsolete rc files in /etc/rc.d
2224 local rc_555_files="$(select_set_files /etc/rc.d/ \
2225 "/etc/rc\.d/\([^[:space:]]*\)" ${etcsets} | \
2226 exclude ${rc_external_files})"
2227
2228 # obsolete rc file in /etc/rc.d
2229 local rc_obsolete_files="$(select_obsolete_files /etc/rc.d/ \
2230 "\([^[:space:]]*\)" etc)"
2231
2232 compare_dir "${op}" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
2233 ${rc_644_files}
2234 failed=$(( ${failed} + $? ))
2235
2236 local extra_scripts
2237 if ! $SOURCEMODE; then
2238 extra_scripts="${generated_scripts}"
2239 else
2240 extra_scripts=""
2241 fi
2242
2243 compare_dir "${op}" "${SRC_DIR}/etc/rc.d" "${DEST_DIR}/etc/rc.d" 555 \
2244 ${rc_555_files} \
2245 ${extra_scripts}
2246 failed=$(( ${failed} + $? ))
2247
2248 local i rc_file
2249 for i in ${rc_external_files}; do
2250 case $i in
2251 *d) rc_file=${i};;
2252 *) rc_file=${i}d;;
2253 esac
2254
2255 update_rc "${op}" "${dir}" ${rc_file} /sbin \
2256 "${SRC_DIR}/external/bsd/$i/etc/rc.d"
2257 failed=$(( ${failed} + $? ))
2258 done
2259
2260 if $SOURCEMODE && [ -n "${generated_scripts}" ]; then
2261 # generate scripts
2262 mkdir "${SCRATCHDIR}/rc"
2263 for f in ${generated_scripts}; do
2264 ${SED} -e "s,@X11ROOTDIR@,${X11ROOTDIR},g" \
2265 < "${SRC_DIR}/etc/rc.d/${f}.in" \
2266 > "${SCRATCHDIR}/rc/${f}"
2267 done
2268 compare_dir "${op}" "${SCRATCHDIR}/rc" \
2269 "${DEST_DIR}/etc/rc.d" 555 \
2270 ${generated_scripts}
2271 failed=$(( ${failed} + $? ))
2272 fi
2273
2274 # check for obsolete rc.d files
2275 for f in ${rc_obsolete_files}; do
2276 local fd="/etc/rc.d/${f}"
2277 [ -e "${DEST_DIR}${fd}" ] && echo "${fd}"
2278 done | obsolete_paths "${op}"
2279 failed=$(( ${failed} + $? ))
2280
2281 # check for obsolete rc.conf(5) variables
2282 set -- ${rc_obsolete_vars}
2283 while [ $# -gt 1 ]; do
2284 if rcconf_is_set "${op}" "$1" "$2" 1; then
2285 failed=1
2286 fi
2287 shift 2
2288 done
2289
2290 return ${failed}
2291 }
2292
2293
2294 #
2295 # sendmail
2296 #
2297
2298 adddisableditem sendmail "remove obsolete sendmail configuration files and scripts"
2299 do_sendmail()
2300 {
2301 [ -n "$1" ] || err 3 "USAGE: do_sendmail fix|check"
2302 op="$1"
2303 failed=0
2304
2305 # Don't complain if the "sendmail" package is installed because the
2306 # files might still be in use.
2307 if /usr/sbin/pkg_info -qe sendmail >/dev/null 2>&1; then
2308 return 0
2309 fi
2310
2311 for f in /etc/mail/helpfile /etc/mail/local-host-names \
2312 /etc/mail/sendmail.cf /etc/mail/submit.cf /etc/rc.d/sendmail \
2313 /etc/rc.d/smmsp /usr/share/misc/sendmail.hf \
2314 $( ( find "${DEST_DIR}/usr/share/sendmail" -type f ; \
2315 find "${DEST_DIR}/usr/share/sendmail" -type d \
2316 ) | unprefix "${DEST_DIR}" ) \
2317 /var/log/sendmail.st \
2318 /var/spool/clientmqueue \
2319 /var/spool/mqueue
2320 do
2321 [ -e "${DEST_DIR}${f}" ] && echo "${f}"
2322 done | obsolete_paths "${op}"
2323 failed=$(( ${failed} + $? ))
2324
2325 return ${failed}
2326 }
2327
2328
2329 #
2330 # ssh
2331 #
2332
2333 additem ssh "ssh configuration update"
2334 do_ssh()
2335 {
2336 [ -n "$1" ] || err 3 "USAGE: do_ssh fix|check"
2337 local op="$1"
2338
2339 local failed=0
2340 local etcssh="${DEST_DIR}/etc/ssh"
2341 if ! check_dir "${op}" "${etcssh}" 755; then
2342 failed=1
2343 fi
2344
2345 if [ ${failed} -eq 0 ]; then
2346 for f in \
2347 ssh_known_hosts ssh_known_hosts2 \
2348 ssh_host_dsa_key ssh_host_dsa_key.pub \
2349 ssh_host_rsa_key ssh_host_rsa_key.pub \
2350 ssh_host_key ssh_host_key.pub \
2351 ; do
2352 if ! move_file "${op}" \
2353 "${DEST_DIR}/etc/${f}" "${etcssh}/${f}" ; then
2354 failed=1
2355 fi
2356 done
2357 for f in sshd.conf ssh.conf ; do
2358 # /etc/ssh/ssh{,d}.conf -> ssh{,d}_config
2359 #
2360 if ! move_file "${op}" \
2361 "${etcssh}/${f}" "${etcssh}/${f%.conf}_config" ;
2362 then
2363 failed=1
2364 fi
2365 # /etc/ssh{,d}.conf -> /etc/ssh/ssh{,d}_config
2366 #
2367 if ! move_file "${op}" \
2368 "${DEST_DIR}/etc/${f}" \
2369 "${etcssh}/${f%.conf}_config" ;
2370 then
2371 failed=1
2372 fi
2373 done
2374 fi
2375
2376 local sshdconf=""
2377 local f
2378 for f in \
2379 "${etcssh}/sshd_config" \
2380 "${etcssh}/sshd.conf" \
2381 "${DEST_DIR}/etc/sshd.conf" ; do
2382 if [ -f "${f}" ]; then
2383 sshdconf="${f}"
2384 break
2385 fi
2386 done
2387 if [ -n "${sshdconf}" ]; then
2388 modify_file "${op}" "${sshdconf}" "${SCRATCHDIR}/sshdconf" '
2389 /^[^#$]/ {
2390 kw = tolower($1)
2391 if (kw == "hostkey" &&
2392 $2 ~ /^\/etc\/+ssh_host(_[dr]sa)?_key$/ ) {
2393 sub(/\/etc\/+/, "/etc/ssh/")
2394 }
2395 if (kw == "rhostsauthentication" ||
2396 kw == "verifyreversemapping" ||
2397 kw == "reversemappingcheck") {
2398 sub(/^/, "# DEPRECATED:\t")
2399 }
2400 }
2401 { print }
2402 '
2403 failed=$(( ${failed} + $? ))
2404 fi
2405
2406 if ! find_file_in_dirlist moduli "moduli" \
2407 "${SRC_DIR}/crypto/external/bsd/openssh/dist" "${SRC_DIR}/etc" ; then
2408 failed=1
2409 # ${dir} is set by find_file_in_dirlist()
2410 elif ! compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 moduli; then
2411 failed=1
2412 fi
2413
2414 if ! check_dir "${op}" "${DEST_DIR}/var/chroot/sshd" 755 ; then
2415 failed=1
2416 fi
2417
2418 if rcconf_is_set "${op}" sshd sshd_conf_dir 1; then
2419 failed=1
2420 fi
2421
2422 return ${failed}
2423 }
2424
2425
2426 #
2427 # tcpdumpchroot
2428 #
2429
2430 additem tcpdumpchroot "remove /var/chroot/tcpdump/etc/protocols"
2431 do_tcpdumpchroot()
2432 {
2433 [ -n "$1" ] || err 3 "USAGE: do_tcpdumpchroot fix|check"
2434 local op="$1"
2435
2436 local failed=0;
2437 if [ -r "${DEST_DIR}/var/chroot/tcpdump/etc/protocols" ]; then
2438 if [ "${op}" = "fix" ]; then
2439 ${RM} "${DEST_DIR}/var/chroot/tcpdump/etc/protocols"
2440 failed=$(( ${failed} + $? ))
2441 rmdir "${DEST_DIR}/var/chroot/tcpdump/etc"
2442 failed=$(( ${failed} + $? ))
2443 else
2444 failed=1
2445 fi
2446 fi
2447 return ${failed}
2448 }
2449
2450
2451 #
2452 # uid
2453 #
2454
2455 additem uid "required users in /etc/master.passwd"
2456 do_uid()
2457 {
2458 [ -n "$1" ] || err 3 "USAGE: do_uid fix|check"
2459
2460 check_ids "$1" users "${DEST_DIR}/etc/master.passwd" \
2461 "${SRC_DIR}/etc/master.passwd" 12 \
2462 postfix SKIP named ntpd sshd SKIP _pflogd _rwhod SKIP _proxy \
2463 _timedc _sdpd _httpd _mdnsd _tests _tcpdump _tss SKIP _rtadvd \
2464 SKIP _unbound _nsd SKIP _dhcpcd
2465 }
2466
2467
2468 #
2469 # varrwho
2470 #
2471
2472 additem varrwho "required ownership of files in /var/rwho"
2473 do_varrwho()
2474 {
2475 [ -n "$1" ] || err 3 "USAGE: do_varrwho fix|check"
2476
2477 contents_owner "$1" "${DEST_DIR}/var/rwho" _rwhod _rwhod
2478 }
2479
2480
2481 #
2482 # varshm
2483 #
2484
2485 additem varshm "check for a tmpfs mounted on /var/shm"
2486 do_varshm()
2487 {
2488 [ -n "$1" ] || err 3 "USAGE: do_varshm fix|check"
2489 op="$1"
2490 failed=0
2491
2492 [ -f "${DEST_DIR}/etc/fstab" ] || return 0
2493 if ${GREP} -E "^var_shm_symlink" "${DEST_DIR}/etc/rc.conf" >/dev/null 2>&1;
2494 then
2495 failed=0;
2496 elif ${GREP} -w "/var/shm" "${DEST_DIR}/etc/fstab" >/dev/null 2>&1;
2497 then
2498 failed=0;
2499 else
2500 if [ "${op}" = "check" ]; then
2501 failed=1
2502 msg "No /var/shm mount found in ${DEST_DIR}/etc/fstab"
2503 elif [ "${op}" = "fix" ]; then
2504 printf '\ntmpfs\t/var/shm\ttmpfs\trw,-m1777,-sram%%25\n' \
2505 >> "${DEST_DIR}/etc/fstab"
2506 msg "Added tmpfs with 25% ram limit as /var/shm"
2507
2508 fi
2509 fi
2510
2511 return ${failed}
2512 }
2513
2514
2515 #
2516 # wscons
2517 #
2518
2519 additem wscons "wscons configuration file update"
2520 do_wscons()
2521 {
2522 [ -n "$1" ] || err 3 "USAGE: do_wscons fix|check"
2523 op="$1"
2524
2525 [ -f "${DEST_DIR}/etc/wscons.conf" ] || return 0
2526
2527 failed=0
2528 notfixed=""
2529 if [ "${op}" = "fix" ]; then
2530 notfixed="${NOT_FIXED}"
2531 fi
2532 while read _type _arg1 _rest; do
2533 if [ "${_type}" = "mux" -a "${_arg1}" = "1" ]; then
2534 msg \
2535 "Obsolete wscons.conf(5) entry \""${_type} ${_arg1}"\" found.${notfixed}"
2536 failed=1
2537 fi
2538 done < "${DEST_DIR}/etc/wscons.conf"
2539
2540 return ${failed}
2541 }
2542
2543
2544 #
2545 # x11
2546 #
2547
2548 additem x11 "x11 configuration update"
2549 do_x11()
2550 {
2551 [ -n "$1" ] || err 3 "USAGE: do_x11 fix|check"
2552 local p="$1"
2553
2554 local failed=0
2555 local etcx11="${DEST_DIR}/etc/X11"
2556 local libx11=""
2557 if [ ! -d "${etcx11}" ]; then
2558 msg "${etcx11} is not a directory; skipping check"
2559 return 0
2560 fi
2561 if [ -d "${DEST_DIR}/usr/X11R6/." ]
2562 then
2563 libx11="${DEST_DIR}/usr/X11R6/lib/X11"
2564 if [ ! -d "${libx11}" ]; then
2565 msg "${libx11} is not a directory; skipping check"
2566 return 0
2567 fi
2568 fi
2569
2570 local notfixed=""
2571 if [ "${op}" = "fix" ]; then
2572 notfixed="${NOT_FIXED}"
2573 fi
2574
2575 local d
2576 # check if /usr/X11R6/lib/X11 needs to migrate to /etc/X11
2577 if [ -n "${libx11}" ]; then
2578 for d in \
2579 fs lbxproxy proxymngr rstart twm xdm xinit xserver xsm \
2580 ; do
2581 sd="${libx11}/${d}"
2582 ld="/etc/X11/${d}"
2583 td="${DEST_DIR}${ld}"
2584 if [ -h "${sd}" ]; then
2585 continue
2586 elif [ -d "${sd}" ]; then
2587 tdfiles="$(find "${td}" \! -type d)"
2588 if [ -n "${tdfiles}" ]; then
2589 msg "${sd} exists yet ${td} already" \
2590 "contains files${notfixed}"
2591 else
2592 msg "Migrate ${sd} to ${td}${notfixed}"
2593 fi
2594 failed=1
2595 elif [ -e "${sd}" ]; then
2596 msg "Unexpected file ${sd}${notfixed}"
2597 continue
2598 else
2599 continue
2600 fi
2601 done
2602 fi
2603
2604 # check if xdm resources have been updated
2605 if [ -r ${etcx11}/xdm/Xresources ] && \
2606 ! ${GREP} -q 'inpColor:' ${etcx11}/xdm/Xresources; then
2607 msg "Update ${etcx11}/xdm/Xresources${notfixed}"
2608 failed=1
2609 fi
2610
2611 return ${failed}
2612 }
2613
2614
2615 #
2616 # xkb
2617 #
2618 # /usr/X11R7/lib/X11/xkb/symbols/pc used to be a directory, but changed
2619 # to a file on 2009-06-12. Fixing this requires removing the directory
2620 # (which we can do) and re-extracting the xbase set (which we can't do),
2621 # or at least adding that one file (which we may be able to do if X11SRCDIR
2622 # is available).
2623 #
2624
2625 additem xkb "clean up for xkbdata to xkeyboard-config upgrade"
2626 do_xkb()
2627 {
2628 [ -n "$1" ] || err 3 "USAGE: do_xkb fix|check"
2629 local op="$1"
2630 local failed=0
2631
2632 local pcpath="/usr/X11R7/lib/X11/xkb/symbols/pc"
2633 local pcsrcdir="${X11SRCDIR}/external/mit/xkeyboard-config/dist/symbols"
2634
2635 local filemsg="\
2636 ${pcpath} was a directory, should be a file.
2637 To fix, extract the xbase set again."
2638
2639 local notfixed=""
2640 if [ "${op}" = "fix" ]; then
2641 notfixed="${NOT_FIXED}"
2642 fi
2643
2644 if [ ! -d "${DEST_DIR}${pcpath}" ]; then
2645 return 0
2646 fi
2647
2648 # Delete obsolete files in the directory, and the directory
2649 # itself. If the directory contains unexpected extra files
2650 # then it will not be deleted.
2651 ( [ -f "${DEST_DIR}"/var/db/obsolete/xbase ] \
2652 && ${SORT} -ru "${DEST_DIR}"/var/db/obsolete/xbase \
2653 | ${GREP} -E "^\\.?${pcpath}/" ;
2654 echo "${pcpath}" ) \
2655 | obsolete_paths "${op}"
2656 failed=$(( ${failed} + $? ))
2657
2658 # If the directory was removed above, then try to replace it with
2659 # a file.
2660 if [ -d "${DEST_DIR}${pcpath}" ]; then
2661 msg "${filemsg}${notfixed}"
2662 failed=$(( ${failed} + 1 ))
2663 else
2664 if ! find_file_in_dirlist pc "${pcpath}" \
2665 "${pcsrcdir}" "${SRC_DIR}${pcpath%/*}"
2666 then
2667 msg "${filemsg}${notfixed}"
2668 failed=$(( ${failed} + 1 ))
2669 else
2670 # ${dir} is set by find_file_in_dirlist()
2671 populate_dir "${op}" true \
2672 "${dir}" "${DEST_DIR}${pcpath%/*}" 444 \
2673 pc
2674 failed=$(( ${failed} + $? ))
2675 fi
2676 fi
2677
2678 return $failed
2679 }
2680
2681
2682 #
2683 # obsolete_stand
2684 # obsolete_stand_debug
2685 #
2686
2687 obsolete_stand_internal()
2688 {
2689 local prefix="$1"
2690 shift
2691 [ -n "$1" ] || err 3 "USAGE: do_obsolete_stand fix|check"
2692 local op="$1"
2693 local failed=0
2694 local dir
2695
2696 for dir in \
2697 ${prefix}/stand/${MACHINE} \
2698 ${prefix}/stand/${MACHINE}-4xx \
2699 ${prefix}/stand/${MACHINE}-booke \
2700 ${prefix}/stand/${MACHINE}-xen \
2701 ${prefix}/stand/${MACHINE}pae-xen
2702 do
2703 [ -d "${DEST_DIR}${dir}" ] && obsolete_stand "${dir}"
2704 done | obsolete_paths "${op}"
2705 failed=$(( ${failed} + $? ))
2706
2707 return ${failed}
2708 }
2709
2710 adddisableditem obsolete_stand "remove obsolete files from /stand"
2711 do_obsolete_stand()
2712 {
2713 obsolete_stand_internal "" "$@"
2714 return $?
2715 }
2716
2717 adddisableditem obsolete_stand_debug "remove obsolete files from /usr/libdata/debug/stand"
2718 do_obsolete_stand_debug()
2719 {
2720 obsolete_stand_internal "/usr/libdata/debug" "$@"
2721 return $?
2722 }
2723
2724
2725 #
2726 # obsolete
2727 #
2728 # NOTE: This item is last to allow other items to move obsolete files.
2729 #
2730
2731 listarchsubdirs()
2732 {
2733 if ! $SOURCEMODE; then
2734 echo "@ARCHSUBDIRS@"
2735 else
2736 ${SED} -n -e '/ARCHDIR_SUBDIR/s/[[:space:]]//gp' \
2737 "${SRC_DIR}/compat/archdirs.mk"
2738 fi
2739 }
2740
2741 getarchsubdirs()
2742 {
2743 local m
2744 local i
2745
2746 case ${MACHINE_ARCH} in
2747 *arm*|*aarch64*) m=arm;;
2748 x86_64) m=amd64;;
2749 *) m=${MACHINE_ARCH};;
2750 esac
2751
2752 for i in $(listarchsubdirs); do
2753 echo $i
2754 done | ${SORT} -u | ${SED} -n -e "/=${m}/s@.*=${m}/\(.*\)@\1@p"
2755 }
2756
2757 getcompatlibdirs()
2758 {
2759 local i
2760
2761 for i in $(getarchsubdirs); do
2762 if [ -d "${DEST_DIR}/usr/lib/$i" ]; then
2763 echo /usr/lib/$i
2764 fi
2765 done
2766 }
2767
2768 additem obsolete "remove obsolete file sets and minor libraries"
2769 do_obsolete()
2770 {
2771 [ -n "$1" ] || err 3 "USAGE: do_obsolete fix|check"
2772 local op="$1"
2773 local failed=0
2774 local i
2775
2776 ${SORT} -ru "${DEST_DIR}"/var/db/obsolete/* | obsolete_paths "${op}"
2777 failed=$(( ${failed} + $? ))
2778
2779 (
2780 obsolete_libs /lib
2781 obsolete_libs /usr/lib
2782 obsolete_libs /usr/lib/i18n
2783 obsolete_libs /usr/X11R6/lib
2784 obsolete_libs /usr/X11R7/lib
2785 for i in $(getcompatlibdirs); do
2786 obsolete_libs $i
2787 done
2788 ) | obsolete_paths "${op}"
2789 failed=$(( ${failed} + $? ))
2790
2791 return ${failed}
2792 }
2793
2794
2795 #
2796 # end of items
2797 # ------------
2798 #
2799
2800
2801 help()
2802 {
2803 cat << _USAGE_
2804 Usage: ${PROGNAME} [-a ARCH] [-d DEST_DIR] [-m MACHINE] [-s SRC_ARG] [-x XSRC_DIR] OPERATION ...
2805 ${PROGNAME} -?
2806
2807 Perform post-installation checks and/or fixes on a system's
2808 configuration files.
2809 If no items are provided, a default set of checks or fixes is applied.
2810
2811 Options:
2812 -? Display this help, and exit.
2813 -a ARCH Set \$MACHINE_ARCH to ARCH. [${MACHINE_ARCH}]
2814 -d DEST_DIR Destination directory to check. [${DEST_DIR:-/}]
2815 -m MACHINE Set \$MACHINE to MACHINE. [${MACHINE}]
2816 -s SRC_ARG Location of the source files. This may be any of
2817 the following:
2818 -s SRC_DIR A directory that contains a NetBSD
2819 source tree.
2820 -s TGZ_DIR A directory in which one or both of
2821 "etc.tgz" and "xetc.tgz" have been
2822 extracted.
2823 -s TGZ_FILE A distribution set file such as
2824 "etc.tgz" or "xetc.tgz".
2825 May be specified multipled times.
2826 [${SRC_DIR:-/usr/src}]
2827 -x XSRC_DIR Location of the X11 source files. This must be
2828 a directory that contains a NetBSD xsrc tree.
2829 [${XSRC_DIR:-/usr/src/../xsrc}]
2830
2831 Supported values for OPERATION:
2832 help Display this help, and exit.
2833 list List available items.
2834 check ITEM ... Perform post-installation checks on ITEMs.
2835 diff [-bcenpuw] ITEM ...
2836 Similar to 'check' but also output difference of files,
2837 using diff with the provided options.
2838 fix ITEM ... Apply fixes that 'check' determines need to be applied.
2839 usage Display this help, and exit.
2840 _USAGE_
2841 }
2842
2843 usage()
2844 {
2845 help 1>&2
2846 exit 2
2847 }
2848
2849
2850 list()
2851 {
2852 local i
2853 echo "Default set of items (to apply if no items are provided by user):"
2854 echo " Item Description"
2855 echo " ---- -----------"
2856 for i in ${defaultitems}; do
2857 eval desc=\"\${desc_${i}}\"
2858 printf " %-20s %s\n" "${i}" "${desc}"
2859 done
2860 echo "Items disabled by default (must be requested explicitly):"
2861 echo " Item Description"
2862 echo " ---- -----------"
2863 for i in ${otheritems}; do
2864 eval desc=\"\${desc_${i}}\"
2865 printf " %-20s %s\n" "${i}" "${desc}"
2866 done
2867 }
2868
2869
2870 main()
2871 {
2872 DIRMODE=false # true if "-s" specified a directory
2873 ITEMS= # items to check|diff|fix. [${defaultitems}]
2874 N_SRC_ARGS=0 # number of "-s" args in SRC_ARGLIST
2875 SOURCEMODE=false # true if "-s" specified a source directory
2876 SRC_ARGLIST= # quoted list of one or more "-s" args
2877 SRC_DIR="${SRC_ARG}" # set default value for early usage()
2878 TGZLIST= # quoted list list of tgz files
2879 TGZMODE=false # true if "-s" specifies a tgz file
2880 XSRC_DIR="${SRC_ARG}/../xsrc"
2881 XSRC_DIR_FIX=
2882
2883 case "$(uname -s)" in
2884 Darwin)
2885 # case sensitive match for case insensitive fs
2886 file_exists_exact=file_exists_exact
2887 ;;
2888 *)
2889 file_exists_exact=:
2890 ;;
2891 esac
2892
2893 # Validate options.
2894 #
2895 while getopts :a:d:m:s:x: ch; do
2896 case "${ch}" in
2897 a)
2898 MACHINE_ARCH="${OPTARG}"
2899 ;;
2900 d)
2901 DEST_DIR="${OPTARG}"
2902 ;;
2903 m)
2904 MACHINE="${OPTARG}"
2905 ;;
2906 s)
2907 qarg="$(shell_quote "${OPTARG}")"
2908 N_SRC_ARGS=$(( $N_SRC_ARGS + 1 ))
2909 SRC_ARGLIST="${SRC_ARGLIST}${SRC_ARGLIST:+ }-s ${qarg}"
2910 if [ -f "${OPTARG}" ]; then
2911 # arg refers to a *.tgz file.
2912 # This may happen twice, for both
2913 # etc.tgz and xetc.tgz, so we build up a
2914 # quoted list in TGZLIST.
2915 TGZMODE=true
2916 TGZLIST="${TGZLIST}${TGZLIST:+ }${qarg}"
2917 # Note that, when TGZMODE is true,
2918 # SRC_ARG is used only for printing
2919 # human-readable messages.
2920 SRC_ARG="${TGZLIST}"
2921 elif [ -d "${OPTARG}" ]; then
2922 # arg refers to a directory.
2923 # It might be a source directory, or a
2924 # directory where the sets have already
2925 # been extracted.
2926 DIRMODE=true
2927 SRC_ARG="${OPTARG}"
2928 if [ -f "${OPTARG}/etc/Makefile" ]; then
2929 SOURCEMODE=true
2930 fi
2931 else
2932 err 2 "Invalid argument for -s option"
2933 fi
2934 ;;
2935 x)
2936 if [ -d "${OPTARG}" ]; then
2937 # arg refers to a directory.
2938 XSRC_DIR="${OPTARG}"
2939 XSRC_DIR_FIX="-x ${OPTARG} "
2940 else
2941 err 2 "Not a directory for -x option"
2942 fi
2943 ;;
2944 "?")
2945 if [ "${OPTARG}" = "?" ]; then
2946 help
2947 return # no further processing or validation
2948 fi
2949 warn "Unknown option -${OPTARG}"
2950 usage
2951 ;;
2952
2953 :)
2954 warn "Missing argument for option -${OPTARG}"
2955 usage
2956 ;;
2957
2958 *)
2959 err 3 "Unimplemented option -${ch}"
2960 ;;
2961 esac
2962 done
2963 shift $((${OPTIND} - 1))
2964 if [ $# -eq 0 ] ; then
2965 warn "Missing operation"
2966 usage
2967 fi
2968 op="$1"
2969 shift
2970
2971 if [ "$N_SRC_ARGS" -gt 1 ] && $DIRMODE; then
2972 err 2 "Multiple -s args are allowed only with tgz files"
2973 fi
2974 if [ "$N_SRC_ARGS" -eq 0 ]; then
2975 # The default SRC_ARG was set elsewhere
2976 DIRMODE=true
2977 SOURCEMODE=true
2978 SRC_ARGLIST="-s $(shell_quote "${SRC_ARG}")"
2979 fi
2980
2981 # Validate 'diff' first, as it becomes 'check'
2982 #
2983 case "${op}" in
2984
2985 diff)
2986 op=check
2987 DIFF_STYLE=n # default style is RCS
2988 OPTIND=1
2989 while getopts :bcenpuw ch; do
2990 case "${ch}" in
2991 c|e|n|u)
2992 if [ "${DIFF_STYLE}" != "n" -a \
2993 "${DIFF_STYLE}" != "${ch}" ]; then
2994 warn "diff: conflicting output style: -${ch}"
2995 usage
2996 fi
2997 DIFF_STYLE="${ch}"
2998 ;;
2999 b|p|w)
3000 DIFF_OPT="${DIFF_OPT} -${ch}"
3001 ;;
3002 "?")
3003 # NOTE: not supporting diff -?
3004 warn "diff: Unknown option -${OPTARG}"
3005 usage
3006 ;;
3007 :)
3008 warn "diff: Missing argument for option -${OPTARG}"
3009 usage
3010 ;;
3011 *)
3012 err 3 "diff: Unimplemented option -${ch}"
3013 ;;
3014 esac
3015 done
3016 shift $((${OPTIND} - 1))
3017 ;;
3018
3019 esac
3020
3021 # Validate operation and items.
3022 #
3023 case "${op}" in
3024
3025 check|fix)
3026 ITEMS="$*"
3027 : ${ITEMS:="${defaultitems}"}
3028
3029 # ensure that all supplied items are valid
3030 #
3031 for i in ${ITEMS}; do
3032 eval desc=\"\${desc_${i}}\"
3033 [ -n "${desc}" ] || err 2 "Unsupported ${op} '"${i}"'"
3034 done
3035 ;;
3036
3037 help|usage)
3038 help
3039 return # no further processing or validation
3040 ;;
3041
3042 list)
3043 # processed below
3044 ;;
3045
3046 *)
3047 warn "Unknown operation '"${op}"'"
3048 usage
3049 ;;
3050
3051 esac
3052
3053 #
3054 # If '-s' arg or args specified tgz files, extract them
3055 # to a scratch directory.
3056 #
3057 if $TGZMODE; then
3058 ETCTGZDIR="${SCRATCHDIR}/etc.tgz"
3059 echo "Note: Creating temporary directory ${ETCTGZDIR}"
3060 if ! mkdir "${ETCTGZDIR}"; then
3061 err 2 "Can't create ${ETCTGZDIR}"
3062 fi
3063 ( # subshell to localise changes to "$@"
3064 eval "set -- ${TGZLIST}"
3065 for tgz in "$@"; do
3066 echo "Note: Extracting files from ${tgz}"
3067 cat "${tgz}" | (
3068 cd "${ETCTGZDIR}" &&
3069 tar -zxf -
3070 ) || err 2 "Can't extract ${tgz}"
3071 done
3072 )
3073 SRC_DIR="${ETCTGZDIR}"
3074 else
3075 SRC_DIR="${SRC_ARG}"
3076 fi
3077
3078 [ -d "${SRC_DIR}" ] || err 2 "${SRC_DIR} is not a directory"
3079 [ -d "${DEST_DIR}" ] || err 2 "${DEST_DIR} is not a directory"
3080 [ -n "${MACHINE}" ] || err 2 "\${MACHINE} is not defined"
3081 [ -n "${MACHINE_ARCH}" ] || err 2 "\${MACHINE_ARCH} is not defined"
3082 if ! $SOURCEMODE && ! [ -f "${SRC_DIR}/etc/mtree/set.etc" ]; then
3083 err 2 "Files from the etc.tgz set are missing"
3084 fi
3085
3086 # If directories are /, clear them, so various messages
3087 # don't have leading "//". However, this requires
3088 # the use of ${foo:-/} to display the variables.
3089 #
3090 [ "${SRC_DIR}" = "/" ] && SRC_DIR=""
3091 [ "${DEST_DIR}" = "/" ] && DEST_DIR=""
3092
3093 detect_x11
3094
3095 # Perform operation.
3096 #
3097 case "${op}" in
3098
3099 check|fix)
3100 [ -n "${ITEMS}" ] || err 2 "${op}: missing items"
3101
3102 echo "Source directory: ${SRC_DIR:-/}"
3103 if $TGZMODE; then
3104 echo " (extracted from: ${SRC_ARG})"
3105 fi
3106 echo "Target directory: ${DEST_DIR:-/}"
3107 items_passed=
3108 items_failed=
3109 for i in ${ITEMS}; do
3110 echo "${i} ${op}:"
3111 ( eval do_${i} ${op} )
3112 if [ $? -eq 0 ]; then
3113 items_passed="${items_passed} ${i}"
3114 else
3115 items_failed="${items_failed} ${i}"
3116 fi
3117 done
3118
3119 if [ "${op}" = "check" ]; then
3120 plural="checks"
3121 else
3122 plural="fixes"
3123 fi
3124
3125 echo "${PROGNAME} ${plural} passed:${items_passed}"
3126 echo "${PROGNAME} ${plural} failed:${items_failed}"
3127 if [ -n "${items_failed}" ]; then
3128 exitstatus=1;
3129 if [ "${op}" = "check" ]; then
3130 [ "$MACHINE" = "$(uname -m)" ] && m= || m=" -m $MACHINE"
3131 cat <<_Fix_me_
3132 To fix, run:
3133 ${HOST_SH} ${0} ${SRC_ARGLIST} ${XSRC_DIR_FIX}-d ${DEST_DIR:-/}$m fix${items_failed}
3134 Note that this may overwrite local changes.
3135 _Fix_me_
3136 fi
3137 fi
3138 ;;
3139
3140 list)
3141 echo "Source directory: ${SRC_DIR:-/}"
3142 echo "Target directory: ${DEST_DIR:-/}"
3143 if $TGZMODE; then
3144 echo " (extracted from: ${SRC_ARG})"
3145 fi
3146 list
3147 ;;
3148
3149 *)
3150 # diff, help, usage handled during operation validation
3151 err 3 "Unimplemented operation '"${op}"'"
3152 ;;
3153
3154 esac
3155 }
3156
3157 if [ -n "$POSTINSTALL_FUNCTION" ]; then
3158 eval "$POSTINSTALL_FUNCTION"
3159 exit 0
3160 fi
3161
3162 # defaults
3163 #
3164 PROGNAME="${0##*/}"
3165 SRC_ARG="/usr/src"
3166 DEST_DIR="/"
3167 : ${MACHINE:="$( uname -m )"} # assume native build if $MACHINE is not set
3168 : ${MACHINE_ARCH:="$( uname -p )"}# assume native build if not set
3169
3170 DIFF_STYLE=
3171 DIFF_OPT=
3172 NOT_FIXED=" (FIX MANUALLY)"
3173 SCRATCHDIR="$( mkdtemp )" || err 2 "Can't create scratch directory"
3174 trap "${RM} -rf \"\${SCRATCHDIR}\" ; exit 0" 1 2 3 15 # HUP INT QUIT TERM
3175
3176 umask 022
3177 exec 3>/dev/null
3178 exec 4>/dev/null
3179 exitstatus=0
3180
3181 main "$@"
3182 ${RM} -rf "${SCRATCHDIR}"
3183 exit $exitstatus
3184