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