postinstall.in revision 1.67 1 #!/bin/sh
2 #
3 # $NetBSD: postinstall.in,v 1.67 2024/10/10 22:30:01 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 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]lack' "${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/blacklist.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/black/block/)
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-rgb.conf
1419 10-sub-pixel-vbgr.conf
1420 10-sub-pixel-vrgb.conf
1421 10-unhinted.conf
1422 25-unhint-nonlatin.conf
1423 65-khmer.conf
1424 70-no-bitmaps.conf
1425 70-yes-bitmaps.conf
1426 "
1427 local failed_fonts=""
1428 for i in ${obsolete_fonts}; do
1429 if [ -f "${DEST_DIR}/etc/fonts/conf.d/$i" ]; then
1430 conf_d_failed=1
1431 failed_fonts="$failed_fonts $i"
1432 fi
1433 done
1434
1435 if [ -n "$failed_fonts" ]; then
1436 msg \
1437 "Broken fontconfig configuration found; please delete these files:"
1438 msg "[$failed_fonts]"
1439 failed=$(( ${failed} + 1 ))
1440 fi
1441
1442 return ${failed}
1443 }
1444
1445
1446 #
1447 # gid
1448 #
1449
1450 additem gid "required groups in /etc/group"
1451 do_gid()
1452 {
1453 [ -n "$1" ] || err 3 "USAGE: do_gid fix|check"
1454
1455 check_ids "$1" groups "${DEST_DIR}/etc/group" \
1456 "${SRC_DIR}/etc/group" 14 \
1457 named ntpd sshd SKIP _pflogd _rwhod staff _proxy _timedc \
1458 _sdpd _httpd _mdnsd _tests _tcpdump _tss _gpio _rtadvd SKIP \
1459 _unbound _nsd nvmm _dhcpcd
1460 }
1461
1462
1463 #
1464 # gpio
1465 #
1466
1467 additem gpio "gpio configuration is up to date"
1468 do_gpio()
1469 {
1470 [ -n "$1" ] || err 3 "USAGE: do_gpio fix|check"
1471 local op="$1"
1472 local failed=0
1473
1474 populate_dir "$op" true "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
1475 gpio.conf
1476 failed=$(( ${failed} + $? ))
1477
1478 return ${failed}
1479 }
1480
1481
1482 #
1483 # hosts
1484 #
1485
1486 additem hosts "/etc/hosts being up to date"
1487 do_hosts()
1488 {
1489 [ -n "$1" ] || err 3 "USAGE: do_hosts fix|check"
1490
1491 modify_file "$1" "${DEST_DIR}/etc/hosts" "${SCRATCHDIR}/hosts" '
1492 /^(127\.0\.0\.1|::1)[ ]+[^\.]*$/ {
1493 print $0, "localhost."
1494 next
1495 }
1496 { print }
1497 '
1498 return $?
1499 }
1500
1501
1502 #
1503 # iscsi
1504 #
1505
1506 additem iscsi "/etc/iscsi is populated"
1507 do_iscsi()
1508 {
1509 [ -n "$1" ] || err 3 "USAGE: do_iscsi fix|check"
1510
1511 populate_dir "${op}" true \
1512 "${SRC_DIR}/etc/iscsi" "${DEST_DIR}/etc/iscsi" 600 auths
1513 populate_dir "${op}" true \
1514 "${SRC_DIR}/etc/iscsi" "${DEST_DIR}/etc/iscsi" 644 targets
1515 return $?
1516 }
1517
1518
1519 #
1520 # mailerconf
1521 #
1522
1523 adddisableditem mailerconf "update /etc/mailer.conf after sendmail removal"
1524 do_mailerconf()
1525 {
1526 [ -n "$1" ] || err 3 "USAGE: do_mailterconf fix|check"
1527 local op="$1"
1528
1529 local failed=0
1530 mta_path="$(${AWK} '/^sendmail[ \t]/{print$2}' \
1531 "${DEST_DIR}/etc/mailer.conf")"
1532 old_sendmail_path="/usr/libexec/sendmail/sendmail"
1533 if [ "${mta_path}" = "${old_sendmail_path}" ]; then
1534 if [ "$op" = check ]; then
1535 msg "mailer.conf points to obsolete ${old_sendmail_path}"
1536 failed=1;
1537 else
1538 populate_dir "${op}" false \
1539 "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 mailer.conf
1540 failed=$?
1541 fi
1542 fi
1543
1544 return ${failed}
1545 }
1546
1547
1548 #
1549 # makedev
1550 #
1551
1552 additem makedev "/dev/MAKEDEV being up to date"
1553 do_makedev()
1554 {
1555 [ -n "$1" ] || err 3 "USAGE: do_makedev fix|check"
1556 local failed=0
1557
1558 if [ -f "${SRC_DIR}/etc/MAKEDEV.tmpl" ]; then
1559 # generate MAKEDEV from source if source is available
1560 env MACHINE="${MACHINE}" \
1561 MACHINE_ARCH="${MACHINE_ARCH}" \
1562 NETBSDSRCDIR="${SRC_DIR}" \
1563 ${AWK} -f "${SRC_DIR}/etc/MAKEDEV.awk" \
1564 "${SRC_DIR}/etc/MAKEDEV.tmpl" > "${SCRATCHDIR}/MAKEDEV"
1565 fi
1566
1567 find_file_in_dirlist MAKEDEV "MAKEDEV" \
1568 "${SCRATCHDIR}" "${SRC_DIR}/dev" \
1569 || return 1
1570 # ${dir} is set by find_file_in_dirlist()
1571 find_makedev
1572 compare_dir "$1" "${dir}" "${MAKEDEV_DIR}" 555 MAKEDEV
1573 failed=$(( ${failed} + $? ))
1574
1575 find_file_in_dirlist MAKEDEV.local "MAKEDEV.local" \
1576 "${SRC_DIR}/etc" "${SRC_DIR}/dev" \
1577 || return 1
1578 # ${dir} is set by find_file_in_dirlist()
1579 compare_dir "$1" "${dir}" "${DEST_DIR}/dev" 555 MAKEDEV.local
1580 failed=$(( ${failed} + $? ))
1581
1582 return ${failed}
1583 }
1584
1585
1586 #
1587 # man.conf
1588 #
1589
1590 additem manconf "check for a mandoc usage in /etc/man.conf"
1591 do_manconf()
1592 {
1593 [ -n "$1" ] || err 3 "USAGE: do_manconf fix|check"
1594 local op="$1"
1595 local failed=0
1596
1597 [ -f "${DEST_DIR}/etc/man.conf" ] || return 0
1598 if ${GREP} -w "mandoc" "${DEST_DIR}/etc/man.conf" >/dev/null 2>&1;
1599 then
1600 failed=0;
1601 else
1602 failed=1
1603 notfixed=""
1604 if [ "${op}" = "fix" ]; then
1605 notfixed="${NOT_FIXED}"
1606 fi
1607 msg "The file /etc/man.conf has not been adapted to mandoc usage; you"
1608 msg "probably want to copy a new version over. ${notfixed}"
1609 fi
1610
1611 return ${failed}
1612 }
1613
1614
1615 #
1616 # motd
1617 #
1618
1619 additem motd "contents of motd"
1620 do_motd()
1621 {
1622 [ -n "$1" ] || err 3 "USAGE: do_motd fix|check"
1623
1624 if ${GREP} -i 'http://www.NetBSD.org/Misc/send-pr.html' \
1625 "${DEST_DIR}/etc/motd" >/dev/null 2>&1 \
1626 || ${GREP} -i 'https*://www.NetBSD.org/support/send-pr.html' \
1627 "${DEST_DIR}/etc/motd" >/dev/null 2>&1
1628 then
1629 tmp1="$(mktemp /tmp/postinstall.motd.XXXXXXXX)"
1630 tmp2="$(mktemp /tmp/postinstall.motd.XXXXXXXX)"
1631 ${SED} '1,2d' <"${SRC_DIR}/etc/motd" >"${tmp1}"
1632 ${SED} '1,2d' <"${DEST_DIR}/etc/motd" >"${tmp2}"
1633
1634 if [ "$1" = check ]; then
1635 cmp -s "${tmp1}" "${tmp2}"
1636 result=$?
1637 if [ "${result}" -ne 0 ]; then
1638 msg \
1639 "Bug reporting messages do not seem to match the installed release"
1640 fi
1641 else
1642 head -n 2 "${DEST_DIR}/etc/motd" >"${tmp1}"
1643 ${SED} '1,2d' <"${SRC_DIR}/etc/motd" >>"${tmp1}"
1644 cp "${tmp1}" "${DEST_DIR}/etc/motd"
1645 result=0
1646 fi
1647
1648 ${RM} -f "${tmp1}" "${tmp2}"
1649 else
1650 result=0
1651 fi
1652
1653 return ${result}
1654 }
1655
1656
1657 #
1658 # mtree
1659 #
1660
1661 additem mtree "/etc/mtree/ being up to date"
1662 do_mtree()
1663 {
1664 [ -n "$1" ] || err 3 "USAGE: do_mtree fix|check"
1665 local failed=0
1666
1667 compare_dir "$1" "${SRC_DIR}/etc/mtree" "${DEST_DIR}/etc/mtree" 444 special
1668 failed=$(( ${failed} + $? ))
1669
1670 if ! $SOURCEMODE; then
1671 MTREE_DIR="${SRC_DIR}/etc/mtree"
1672 else
1673 ${RM} -rf "${SCRATCHDIR}/obj"
1674 mkdir "${SCRATCHDIR}/obj"
1675 ${MAKE} -s -C "${SRC_DIR}/etc/mtree" TOOL_AWK="${AWK}" \
1676 MAKEOBJDIR="${SCRATCHDIR}/obj" emit_dist_file > \
1677 "${SCRATCHDIR}/NetBSD.dist"
1678 MTREE_DIR="${SCRATCHDIR}"
1679 ${RM} -rf "${SCRATCHDIR}/obj"
1680 fi
1681 compare_dir "$1" "${MTREE_DIR}" "${DEST_DIR}/etc/mtree" 444 NetBSD.dist
1682 failed=$(( ${failed} + $? ))
1683
1684 return ${failed}
1685 }
1686
1687
1688 #
1689 # named
1690 #
1691 handle_named_conf()
1692 {
1693 local op="$1"
1694 local option="dnssec-enable"
1695 local failed=0
1696 local conf
1697
1698 shift
1699
1700 for conf; do
1701 local c=$(readlink -f "${conf}")
1702 if ! ${GREP} -qs "${option}" "${c}"
1703 then
1704 continue
1705 fi
1706
1707 if [ "${op}" = "fix" ]; then
1708 ${SED} -e "/${option}/d" "${c}" > "${c}.new"
1709 failed=$(( ${failed} + $? ))
1710 mv "${c}.new" "${c}"
1711 failed=$(( ${failed} + $? ))
1712 msg "Removed obsolete '${option}' in ${c}"
1713 else
1714 msg "'${option}' option in ${c} should be removed"
1715 failed=$(( ${failed} + 1 ))
1716 fi
1717 done
1718
1719 return ${failed}
1720 }
1721
1722 additem named "named configuration update"
1723 do_named()
1724 {
1725 local oldconf="${DEST_DIR}/etc/namedb/named.conf"
1726 local conf="${DEST_DIR}/etc/named.conf"
1727 [ -n "$1" ] || err 3 "USAGE: do_named fix|check"
1728 local op="$1"
1729
1730 move_file "${op}" "${oldconf}" "${conf}"
1731 handle_named_conf "${op}" "${oldconf}" "${conf}"
1732
1733 compare_dir "${op}" "${SRC_DIR}/etc/namedb" "${DEST_DIR}/etc/namedb" \
1734 644 \
1735 root.cache
1736
1737 local od="${DEST_DIR}/usr/libexec/named"
1738 if [ -d "$od" ]; then
1739 rm -fr "$od"
1740 msg "Removed obsolete '${od}'"
1741 fi
1742 }
1743
1744
1745 #
1746 # opensslcertsconf
1747 #
1748
1749 additem opensslcertsconf "ensure TLS trust anchor configuration exists"
1750 do_opensslcertsconf()
1751 {
1752 local certsdir certsconf defaultconf manualmsg
1753
1754 [ -n "$1" ] || err 3 "USAGE: do_opensslcertsconf fix|check"
1755
1756 certsdir="${DEST_DIR}/etc/openssl/certs"
1757 certsconf="${DEST_DIR}/etc/openssl/certs.conf"
1758 defaultconf="${DEST_DIR}/usr/share/examples/certctl/certs.conf"
1759
1760 case $1 in
1761 check) if [ ! -r "$certsconf" ]; then
1762 msg "/etc/openssl/certs.conf missing; see certctl(8)"
1763 return 1
1764 fi
1765 ;;
1766 fix) # If /etc/openssl/certs.conf is already there, nothing
1767 # to do.
1768 if [ -r "$certsconf" ]; then
1769 return 0
1770 fi
1771
1772 # If /etc/openssl/certs is a symlink, or exists but is
1773 # not a directory, or is a directory but is nonempty,
1774 # then either it's managed by someone else or something
1775 # fishy is afoot. So set it manual in that case.
1776 # Otherwise, install the default config file.
1777 if [ -h "$certsdir" ] ||
1778 [ -e "$certsdir" -a ! -d "$certsdir" ] ||
1779 ([ -d "$certsdir" ] &&
1780 find -f "$certsdir" -- \
1781 -maxdepth 0 -type d -empty -exit 1)
1782 then
1783 msg "/etc/openssl/certs appears manually configured"
1784 manualmsg="[existing /etc/openssl/certs configuration"
1785 manualmsg="$manualmsg detected by postinstall(8)]"
1786 # Change the commented-out `#manual' line to
1787 # uncommented `manual', or print an error
1788 # message if there is no `#manual' line and put
1789 # `manual' at the end.
1790 awk -v defaultconf="$defaultconf" \
1791 -v manualmsg="$manualmsg" '
1792 BEGIN {
1793 manual = 0
1794 }
1795 /^#manual/ && !manual {
1796 manual = 1
1797 sub(/^#/, "")
1798 print
1799 print "#", manualmsg
1800 next
1801 }
1802 {
1803 print
1804 }
1805 END {
1806 if (!manual) {
1807 printf "warning: %s %s?\n", \
1808 "corrupt", defaultconf \
1809 >"/dev/stderr"
1810 print "manual"
1811 print "#", manualmsg
1812 }
1813 }
1814 ' <$defaultconf >${certsconf}.tmp
1815 else
1816 msg "installing default /etc/openssl/certs.conf"
1817 cat <$defaultconf >${certsconf}.tmp
1818 fi && mv -f -- "${certsconf}.tmp" "$certsconf"
1819 ;;
1820 *) err 3 "USAGE: do_opensslcerts fix|check"
1821 ;;
1822 esac
1823 }
1824
1825
1826 #
1827 # opensslcertsrehash
1828 #
1829
1830 additem opensslcertsrehash "make /etc/openssl/certs cache of TLS trust anchors"
1831 do_opensslcertsrehash()
1832 {
1833 local mtreekeys scratchdir
1834
1835 [ -n "$1" ] || err 3 "USAGE: do_opensslcertsrehash fix|check"
1836
1837 if [ ! -r "${DEST_DIR}/etc/openssl/certs.conf" ]; then
1838 msg "/etc/openssl/certs.conf missing; see certctl(8)"
1839 return 1
1840 fi
1841
1842 case $1 in
1843 check) # Create a scratch rehash for comparison.
1844 mtreekeys="type,link"
1845 scratchdir="${SCRATCHDIR}/opensslcerts"
1846 /usr/sbin/certctl -c "$scratchdir" rehash || return $?
1847
1848 # This will create ${scratchdir}/.certctl unless the
1849 # configuration is manual. If the configuration is
1850 # manual, stop here; nothing to do. certctl(8) will
1851 # have already printed a message about that.
1852 #
1853 # XXX Grody to rely on the internal structure used by
1854 # certctl(8), but less bad than having two versions of
1855 # the config parsing logic.
1856 if [ ! -f "${scratchdir}/.certctl" ]; then
1857 return 0
1858 fi
1859
1860 # Do a dry run of rehashing into the real
1861 # /etc/openssl/certs. This curious extra step ensures
1862 # that we report a failure if /etc/openssl/certs
1863 # appears to be managed manually, but `manual' was not
1864 # specified in /etc/openssl/certs.conf.
1865 /usr/sbin/certctl -n rehash || return $?
1866
1867 # Compare the trees with mtree(8). Inconveniently,
1868 # mtree returns status zero even if there are missing
1869 # or extra files. So instead of examining the return
1870 # status, test for any output. Empty output means
1871 # everything matches; otherwise the mismatch, missing,
1872 # or extra files are output.
1873 mtree -p "$scratchdir" -c -k "$mtreekeys" \
1874 | mtree -p "${DEST_DIR}/etc/openssl/certs" 2>&1 \
1875 | {
1876 while read -r line; do
1877 # mismatch, missing, or extra
1878 msg "/etc/openssl/certs needs rehash"
1879 exit 1
1880 done
1881 exit 0
1882 }
1883 ;;
1884 fix) # This runs openssl(1), which is not available as a
1885 # build-time tool. So for now, restrict it to running
1886 # on the installed system.
1887 case $DEST_DIR in
1888 ''|/) ;;
1889 *) msg "opensslcertsrehash limited to DEST_DIR=/"
1890 return 1
1891 ;;
1892 esac
1893 /usr/sbin/certctl rehash
1894 ;;
1895 *) err 3 "USAGE: do_opensslcerts fix|check"
1896 ;;
1897 esac
1898 }
1899
1900
1901 #
1902 # pam
1903 #
1904
1905 additem pam "/etc/pam.d is populated"
1906 do_pam()
1907 {
1908 [ -n "$1" ] || err 3 "USAGE: do_pam fix|check"
1909 local op="$1"
1910 local failed=0
1911
1912 populate_dir "${op}" true "${SRC_DIR}/etc/pam.d" \
1913 "${DEST_DIR}/etc/pam.d" 644 \
1914 README cron display_manager ftpd gdm imap kde login other \
1915 passwd pop3 ppp racoon rexecd rsh sshd su system telnetd \
1916 xdm xserver
1917
1918 failed=$(( ${failed} + $? ))
1919
1920 return ${failed}
1921 }
1922
1923
1924 #
1925 # periodic
1926 #
1927
1928 additem periodic "/etc/{daily,weekly,monthly,security} being up to date"
1929 do_periodic()
1930 {
1931 [ -n "$1" ] || err 3 "USAGE: do_periodic fix|check"
1932
1933 compare_dir "$1" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
1934 daily weekly monthly security
1935 }
1936
1937
1938 #
1939 # pf
1940 #
1941
1942 additem pf "pf configuration being up to date"
1943 do_pf()
1944 {
1945 [ -n "$1" ] || err 3 "USAGE: do_pf fix|check"
1946 local op="$1"
1947 local failed=0
1948
1949 find_file_in_dirlist pf.os "pf.os" \
1950 "${SRC_DIR}/dist/pf/etc" "${SRC_DIR}/etc" \
1951 || return 1
1952 # ${dir} is set by find_file_in_dirlist()
1953 populate_dir "${op}" true \
1954 "${dir}" "${DEST_DIR}/etc" 644 \
1955 pf.conf
1956 failed=$(( ${failed} + $? ))
1957
1958 compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 pf.os
1959 failed=$(( ${failed} + $? ))
1960
1961 return ${failed}
1962 }
1963
1964
1965 #
1966 # ptyfsoldnodes
1967 #
1968
1969 additem ptyfsoldnodes "remove legacy device nodes when using ptyfs"
1970 do_ptyfsoldnodes()
1971 {
1972 [ -n "$1" ] || err 3 "USAGE: do_ptyfsoldnodes fix|check"
1973 local op="$1"
1974
1975 # Check whether ptyfs is in use
1976 local failed=0;
1977 if ! ${GREP} -E "^ptyfs" "${DEST_DIR}/etc/fstab" > /dev/null; then
1978 msg "ptyfs is not in use"
1979 return 0
1980 fi
1981
1982 if [ ! -e "${DEST_DIR}/dev/pts" ]; then
1983 msg "ptyfs is not properly configured: missing /dev/pts"
1984 return 1
1985 fi
1986
1987 # Find the device major numbers for the pty master and slave
1988 # devices, by parsing the output from "MAKEDEV -s pty0".
1989 #
1990 # Output from MAKEDEV looks like this:
1991 # ./ttyp0 type=char device=netbsd,5,0 mode=666 gid=0 uid=0
1992 # ./ptyp0 type=char device=netbsd,6,0 mode=666 gid=0 uid=0
1993 #
1994 # Output from awk, used in the eval statement, looks like this:
1995 # maj_ptym=6; maj_ptys=5;
1996 #
1997 local maj_ptym maj_ptys
1998 find_makedev
1999 eval "$(
2000 ${HOST_SH} "${MAKEDEV_DIR}/MAKEDEV" -s pty0 2>/dev/null \
2001 | ${AWK} '\
2002 BEGIN { before_re = ".*device=[a-zA-Z]*,"; after_re = ",.*"; }
2003 /ptyp0/ { maj_ptym = gensub(before_re, "", 1, $0);
2004 maj_ptym = gensub(after_re, "", 1, maj_ptym); }
2005 /ttyp0/ { maj_ptys = gensub(before_re, "", 1, $0);
2006 maj_ptys = gensub(after_re, "", 1, maj_ptys); }
2007 END { print "maj_ptym=" maj_ptym "; maj_ptys=" maj_ptys ";"; }
2008 '
2009 )"
2010 #msg "Major numbers are maj_ptym=${maj_ptym} maj_ptys=${maj_ptys}"
2011 if [ -z "$maj_ptym" ] || [ -z "$maj_ptys" ]; then
2012 msg "Cannot find device major numbers for pty master and slave"
2013 return 1
2014 fi
2015
2016 # look for /dev/[pt]ty[p-zP-T][0-9a-zA-Z], and check that they
2017 # have the expected device major numbers. ttyv* is typically not a
2018 # pty device, but we check it anyway.
2019 #
2020 # The "for d1" loop is intended to avoid overflowing ARG_MAX;
2021 # otherwise we could have used a single glob pattern.
2022 #
2023 # If there are no files that match a particular pattern,
2024 # then stat prints something like:
2025 # stat: /dev/[pt]tyx?: lstat: No such file or directory
2026 # and we ignore it. XXX: We also ignore other error messages.
2027 #
2028 local d1 major node
2029 local tmp="$(mktemp /tmp/postinstall.ptyfs.XXXXXXXX)"
2030
2031 for d1 in p q r s t u v w x y z P Q R S T; do
2032 ${STAT} -f "%Hr %N" "${DEST_DIR}/dev/"[pt]ty${d1}? 2>&1
2033 done \
2034 | while read -r major node ; do
2035 case "$major" in
2036 ${maj_ptym}|${maj_ptys}) echo "$node" ;;
2037 esac
2038 done > "${tmp}"
2039
2040 local desc="legacy device node"
2041 while read node; do
2042 if [ "${op}" = "check" ]; then
2043 msg "Remove ${desc} ${node}"
2044 failed=1
2045 else # "fix"
2046 if ${RM} "${node}"; then
2047 msg "Removed ${desc} ${node}"
2048 else
2049 warn "Failed to remove ${desc} ${node}"
2050 failed=1
2051 fi
2052 fi
2053 done < "${tmp}"
2054 ${RM} "${tmp}"
2055
2056 return ${failed}
2057 }
2058
2059
2060 #
2061 # pwd_mkdb
2062 #
2063
2064 additem pwd_mkdb "passwd database version"
2065 do_pwd_mkdb()
2066 {
2067 [ -n "$1" ] || err 3 "USAGE: do_pwd_mkdb fix|check"
2068 local op="$1"
2069 local failed=0
2070
2071 # XXX Ideally, we should figure out the endianness of the
2072 # target machine, and add "-E B"/"-E L" to the db(1) flags,
2073 # and "-B"/"-L" to the pwd_mkdb(8) flags if the target is not
2074 # the same as the host machine. It probably doesn't matter,
2075 # because we don't expect "postinstall fix pwd_mkdb" to be
2076 # invoked during a cross build.
2077
2078 set -- $(${DB} -q -Sb -Ub -To -N hash "${DEST_DIR}/etc/pwd.db" \
2079 'VERSION\0')
2080 case "$2" in
2081 '\001\000\000\000') return 0 ;; # version 1, little-endian
2082 '\000\000\000\001') return 0 ;; # version 1, big-endian
2083 esac
2084
2085 if [ "${op}" = "check" ]; then
2086 msg "Update format of passwd database"
2087 failed=1
2088 elif ! ${PWD_MKDB} -V 1 -d "${DEST_DIR:-/}" \
2089 "${DEST_DIR}/etc/master.passwd";
2090 then
2091 msg "Can't update format of passwd database"
2092 failed=1
2093 else
2094 msg "Updated format of passwd database"
2095 fi
2096
2097 return ${failed}
2098 }
2099
2100
2101 #
2102 # rc
2103 #
2104
2105 # There is no info in src/distrib or /etc/mtree which rc* files
2106 # can be overwritten unconditionally on upgrade. See PR/54741.
2107 rc_644_files="
2108 rc
2109 rc.subr
2110 rc.shutdown
2111 "
2112
2113 rc_obsolete_vars="
2114 amd amd_master
2115 btcontrol btcontrol_devices
2116 critical_filesystems critical_filesystems_beforenet
2117 mountcritlocal mountcritremote
2118 network ip6forwarding
2119 network nfsiod_flags
2120 sdpd sdpd_control
2121 sdpd sdpd_groupname
2122 sdpd sdpd_username
2123 sysctl defcorename
2124 "
2125
2126 update_rc()
2127 {
2128 local op=$1
2129 local dir=$2
2130 local name=$3
2131 local bindir=$4
2132 local rcdir=$5
2133
2134 if [ ! -x "${DEST_DIR}/${bindir}/${name}" ]; then
2135 return 0
2136 fi
2137
2138 if ! find_file_in_dirlist "${name}" "${name}" \
2139 "${rcdir}" "${SRC_DIR}/etc/rc.d"; then
2140 return 1
2141 fi
2142 populate_dir "${op}" false "${dir}" "${DEST_DIR}/etc/rc.d" 555 "${name}"
2143 return $?
2144 }
2145
2146 # select non-obsolete files in a sets file
2147 # $1: directory pattern
2148 # $2: file pattern
2149 # $3: filename
2150 select_set_files()
2151 {
2152 local qdir="$(echo $1 | ${SED} -e s@/@\\\\/@g -e s/\\./\\\\./g)"
2153 ${SED} -n -e /obsolete/d \
2154 -e "/^\.${qdir}/s@^.$2[[:space:]].*@\1@p" $3
2155 }
2156
2157 # select obsolete files in a sets file
2158 # $1: directory pattern
2159 # $2: file pattern
2160 # $3: setname
2161 select_obsolete_files()
2162 {
2163 if $SOURCEMODE; then
2164 ${SED} -n -e "/obsolete/s@\.$1$2[[:space:]].*@\1@p" \
2165 "${SRC_DIR}/distrib/sets/lists/$3/mi"
2166 return
2167 fi
2168
2169 # On upgrade builds we don't extract the "etc" set so we
2170 # try to use the source set instead. See PR/54730 for
2171 # ways to better handle this.
2172
2173 local obsolete_dir
2174
2175 if [ $3 = "etc" ] ;then
2176 obsolete_dir="${SRC_DIR}/var/db/obsolete"
2177 else
2178 obsolete_dir="${DEST_DIR}/var/db/obsolete"
2179 fi
2180 ${SED} -n -e "s@\.$1$2\$@\1@p" "${obsolete_dir}/$3"
2181 }
2182
2183 getetcsets()
2184 {
2185 if $SOURCEMODE; then
2186 echo "${SRC_DIR}/distrib/sets/lists/etc/mi"
2187 else
2188 echo "${SRC_DIR}/etc/mtree/set.etc"
2189 fi
2190 }
2191
2192 additem rc "/etc/rc* and /etc/rc.d/ being up to date"
2193 do_rc()
2194 {
2195 [ -n "$1" ] || err 3 "USAGE: do_rc fix|check"
2196 local op="$1"
2197 local failed=0
2198 local generated_scripts=""
2199 local etcsets=$(getetcsets)
2200 if [ "${MKX11}" != "no" ]; then
2201 generated_scripts="${generated_scripts} xdm xfs"
2202 fi
2203
2204 # Directories of external programs that have rc files (in bsd)
2205 local rc_external_files="blocklist nsd unbound"
2206
2207 # rc* files in /etc/
2208 # XXX: at least rc.conf and rc.local shouldn't be updated. PR/54741
2209 #local rc_644_files="$(select_set_files /etc/rc \
2210 # "/etc/\(rc[^[:space:]/]*\)" ${etcsets})"
2211
2212 # no-obsolete rc files in /etc/rc.d
2213 local rc_555_files="$(select_set_files /etc/rc.d/ \
2214 "/etc/rc\.d/\([^[:space:]]*\)" ${etcsets} | \
2215 exclude ${rc_external_files})"
2216
2217 # obsolete rc file in /etc/rc.d
2218 local rc_obsolete_files="$(select_obsolete_files /etc/rc.d/ \
2219 "\([^[:space:]]*\)" etc)"
2220
2221 compare_dir "${op}" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
2222 ${rc_644_files}
2223 failed=$(( ${failed} + $? ))
2224
2225 local extra_scripts
2226 if ! $SOURCEMODE; then
2227 extra_scripts="${generated_scripts}"
2228 else
2229 extra_scripts=""
2230 fi
2231
2232 compare_dir "${op}" "${SRC_DIR}/etc/rc.d" "${DEST_DIR}/etc/rc.d" 555 \
2233 ${rc_555_files} \
2234 ${extra_scripts}
2235 failed=$(( ${failed} + $? ))
2236
2237 local i rc_file
2238 for i in ${rc_external_files}; do
2239 case $i in
2240 *d) rc_file=${i};;
2241 *) rc_file=${i}d;;
2242 esac
2243
2244 update_rc "${op}" "${dir}" ${rc_file} /sbin \
2245 "${SRC_DIR}/external/bsd/$i/etc/rc.d"
2246 failed=$(( ${failed} + $? ))
2247 done
2248
2249 if $SOURCEMODE && [ -n "${generated_scripts}" ]; then
2250 # generate scripts
2251 mkdir "${SCRATCHDIR}/rc"
2252 for f in ${generated_scripts}; do
2253 ${SED} -e "s,@X11ROOTDIR@,${X11ROOTDIR},g" \
2254 < "${SRC_DIR}/etc/rc.d/${f}.in" \
2255 > "${SCRATCHDIR}/rc/${f}"
2256 done
2257 compare_dir "${op}" "${SCRATCHDIR}/rc" \
2258 "${DEST_DIR}/etc/rc.d" 555 \
2259 ${generated_scripts}
2260 failed=$(( ${failed} + $? ))
2261 fi
2262
2263 # check for obsolete rc.d files
2264 for f in ${rc_obsolete_files}; do
2265 local fd="/etc/rc.d/${f}"
2266 [ -e "${DEST_DIR}${fd}" ] && echo "${fd}"
2267 done | obsolete_paths "${op}"
2268 failed=$(( ${failed} + $? ))
2269
2270 # check for obsolete rc.conf(5) variables
2271 set -- ${rc_obsolete_vars}
2272 while [ $# -gt 1 ]; do
2273 if rcconf_is_set "${op}" "$1" "$2" 1; then
2274 failed=1
2275 fi
2276 shift 2
2277 done
2278
2279 return ${failed}
2280 }
2281
2282
2283 #
2284 # sendmail
2285 #
2286
2287 adddisableditem sendmail "remove obsolete sendmail configuration files and scripts"
2288 do_sendmail()
2289 {
2290 [ -n "$1" ] || err 3 "USAGE: do_sendmail fix|check"
2291 op="$1"
2292 failed=0
2293
2294 # Don't complain if the "sendmail" package is installed because the
2295 # files might still be in use.
2296 if /usr/sbin/pkg_info -qe sendmail >/dev/null 2>&1; then
2297 return 0
2298 fi
2299
2300 for f in /etc/mail/helpfile /etc/mail/local-host-names \
2301 /etc/mail/sendmail.cf /etc/mail/submit.cf /etc/rc.d/sendmail \
2302 /etc/rc.d/smmsp /usr/share/misc/sendmail.hf \
2303 $( ( find "${DEST_DIR}/usr/share/sendmail" -type f ; \
2304 find "${DEST_DIR}/usr/share/sendmail" -type d \
2305 ) | unprefix "${DEST_DIR}" ) \
2306 /var/log/sendmail.st \
2307 /var/spool/clientmqueue \
2308 /var/spool/mqueue
2309 do
2310 [ -e "${DEST_DIR}${f}" ] && echo "${f}"
2311 done | obsolete_paths "${op}"
2312 failed=$(( ${failed} + $? ))
2313
2314 return ${failed}
2315 }
2316
2317
2318 #
2319 # ssh
2320 #
2321
2322 additem ssh "ssh configuration update"
2323 do_ssh()
2324 {
2325 [ -n "$1" ] || err 3 "USAGE: do_ssh fix|check"
2326 local op="$1"
2327
2328 local failed=0
2329 local etcssh="${DEST_DIR}/etc/ssh"
2330 if ! check_dir "${op}" "${etcssh}" 755; then
2331 failed=1
2332 fi
2333
2334 if [ ${failed} -eq 0 ]; then
2335 for f in \
2336 ssh_known_hosts ssh_known_hosts2 \
2337 ssh_host_dsa_key ssh_host_dsa_key.pub \
2338 ssh_host_rsa_key ssh_host_rsa_key.pub \
2339 ssh_host_key ssh_host_key.pub \
2340 ; do
2341 if ! move_file "${op}" \
2342 "${DEST_DIR}/etc/${f}" "${etcssh}/${f}" ; then
2343 failed=1
2344 fi
2345 done
2346 for f in sshd.conf ssh.conf ; do
2347 # /etc/ssh/ssh{,d}.conf -> ssh{,d}_config
2348 #
2349 if ! move_file "${op}" \
2350 "${etcssh}/${f}" "${etcssh}/${f%.conf}_config" ;
2351 then
2352 failed=1
2353 fi
2354 # /etc/ssh{,d}.conf -> /etc/ssh/ssh{,d}_config
2355 #
2356 if ! move_file "${op}" \
2357 "${DEST_DIR}/etc/${f}" \
2358 "${etcssh}/${f%.conf}_config" ;
2359 then
2360 failed=1
2361 fi
2362 done
2363 fi
2364
2365 local sshdconf=""
2366 local f
2367 for f in \
2368 "${etcssh}/sshd_config" \
2369 "${etcssh}/sshd.conf" \
2370 "${DEST_DIR}/etc/sshd.conf" ; do
2371 if [ -f "${f}" ]; then
2372 sshdconf="${f}"
2373 break
2374 fi
2375 done
2376 if [ -n "${sshdconf}" ]; then
2377 modify_file "${op}" "${sshdconf}" "${SCRATCHDIR}/sshdconf" '
2378 /^[^#$]/ {
2379 kw = tolower($1)
2380 if (kw == "hostkey" &&
2381 $2 ~ /^\/etc\/+ssh_host(_[dr]sa)?_key$/ ) {
2382 sub(/\/etc\/+/, "/etc/ssh/")
2383 }
2384 if (kw == "rhostsauthentication" ||
2385 kw == "verifyreversemapping" ||
2386 kw == "reversemappingcheck") {
2387 sub(/^/, "# DEPRECATED:\t")
2388 }
2389 }
2390 { print }
2391 '
2392 failed=$(( ${failed} + $? ))
2393 fi
2394
2395 if ! find_file_in_dirlist moduli "moduli" \
2396 "${SRC_DIR}/crypto/external/bsd/openssh/dist" "${SRC_DIR}/etc" ; then
2397 failed=1
2398 # ${dir} is set by find_file_in_dirlist()
2399 elif ! compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 moduli; then
2400 failed=1
2401 fi
2402
2403 if ! check_dir "${op}" "${DEST_DIR}/var/chroot/sshd" 755 ; then
2404 failed=1
2405 fi
2406
2407 if rcconf_is_set "${op}" sshd sshd_conf_dir 1; then
2408 failed=1
2409 fi
2410
2411 return ${failed}
2412 }
2413
2414
2415 #
2416 # tcpdumpchroot
2417 #
2418
2419 additem tcpdumpchroot "remove /var/chroot/tcpdump/etc/protocols"
2420 do_tcpdumpchroot()
2421 {
2422 [ -n "$1" ] || err 3 "USAGE: do_tcpdumpchroot fix|check"
2423 local op="$1"
2424
2425 local failed=0;
2426 if [ -r "${DEST_DIR}/var/chroot/tcpdump/etc/protocols" ]; then
2427 if [ "${op}" = "fix" ]; then
2428 ${RM} "${DEST_DIR}/var/chroot/tcpdump/etc/protocols"
2429 failed=$(( ${failed} + $? ))
2430 rmdir "${DEST_DIR}/var/chroot/tcpdump/etc"
2431 failed=$(( ${failed} + $? ))
2432 else
2433 failed=1
2434 fi
2435 fi
2436 return ${failed}
2437 }
2438
2439
2440 #
2441 # uid
2442 #
2443
2444 additem uid "required users in /etc/master.passwd"
2445 do_uid()
2446 {
2447 [ -n "$1" ] || err 3 "USAGE: do_uid fix|check"
2448
2449 check_ids "$1" users "${DEST_DIR}/etc/master.passwd" \
2450 "${SRC_DIR}/etc/master.passwd" 12 \
2451 postfix SKIP named ntpd sshd SKIP _pflogd _rwhod SKIP _proxy \
2452 _timedc _sdpd _httpd _mdnsd _tests _tcpdump _tss SKIP _rtadvd \
2453 SKIP _unbound _nsd SKIP _dhcpcd
2454 }
2455
2456
2457 #
2458 # varrwho
2459 #
2460
2461 additem varrwho "required ownership of files in /var/rwho"
2462 do_varrwho()
2463 {
2464 [ -n "$1" ] || err 3 "USAGE: do_varrwho fix|check"
2465
2466 contents_owner "$1" "${DEST_DIR}/var/rwho" _rwhod _rwhod
2467 }
2468
2469
2470 #
2471 # varshm
2472 #
2473
2474 additem varshm "check for a tmpfs mounted on /var/shm"
2475 do_varshm()
2476 {
2477 [ -n "$1" ] || err 3 "USAGE: do_varshm fix|check"
2478 op="$1"
2479 failed=0
2480
2481 [ -f "${DEST_DIR}/etc/fstab" ] || return 0
2482 if ${GREP} -E "^var_shm_symlink" "${DEST_DIR}/etc/rc.conf" >/dev/null 2>&1;
2483 then
2484 failed=0;
2485 elif ${GREP} -w "/var/shm" "${DEST_DIR}/etc/fstab" >/dev/null 2>&1;
2486 then
2487 failed=0;
2488 else
2489 if [ "${op}" = "check" ]; then
2490 failed=1
2491 msg "No /var/shm mount found in ${DEST_DIR}/etc/fstab"
2492 elif [ "${op}" = "fix" ]; then
2493 printf '\ntmpfs\t/var/shm\ttmpfs\trw,-m1777,-sram%%25\n' \
2494 >> "${DEST_DIR}/etc/fstab"
2495 msg "Added tmpfs with 25% ram limit as /var/shm"
2496
2497 fi
2498 fi
2499
2500 return ${failed}
2501 }
2502
2503
2504 #
2505 # wscons
2506 #
2507
2508 additem wscons "wscons configuration file update"
2509 do_wscons()
2510 {
2511 [ -n "$1" ] || err 3 "USAGE: do_wscons fix|check"
2512 op="$1"
2513
2514 [ -f "${DEST_DIR}/etc/wscons.conf" ] || return 0
2515
2516 failed=0
2517 notfixed=""
2518 if [ "${op}" = "fix" ]; then
2519 notfixed="${NOT_FIXED}"
2520 fi
2521 while read _type _arg1 _rest; do
2522 if [ "${_type}" = "mux" -a "${_arg1}" = "1" ]; then
2523 msg \
2524 "Obsolete wscons.conf(5) entry \""${_type} ${_arg1}"\" found.${notfixed}"
2525 failed=1
2526 fi
2527 done < "${DEST_DIR}/etc/wscons.conf"
2528
2529 return ${failed}
2530 }
2531
2532
2533 #
2534 # x11
2535 #
2536
2537 additem x11 "x11 configuration update"
2538 do_x11()
2539 {
2540 [ -n "$1" ] || err 3 "USAGE: do_x11 fix|check"
2541 local p="$1"
2542
2543 local failed=0
2544 local etcx11="${DEST_DIR}/etc/X11"
2545 local libx11=""
2546 if [ ! -d "${etcx11}" ]; then
2547 msg "${etcx11} is not a directory; skipping check"
2548 return 0
2549 fi
2550 if [ -d "${DEST_DIR}/usr/X11R6/." ]
2551 then
2552 libx11="${DEST_DIR}/usr/X11R6/lib/X11"
2553 if [ ! -d "${libx11}" ]; then
2554 msg "${libx11} is not a directory; skipping check"
2555 return 0
2556 fi
2557 fi
2558
2559 local notfixed=""
2560 if [ "${op}" = "fix" ]; then
2561 notfixed="${NOT_FIXED}"
2562 fi
2563
2564 local d
2565 # check if /usr/X11R6/lib/X11 needs to migrate to /etc/X11
2566 if [ -n "${libx11}" ]; then
2567 for d in \
2568 fs lbxproxy proxymngr rstart twm xdm xinit xserver xsm \
2569 ; do
2570 sd="${libx11}/${d}"
2571 ld="/etc/X11/${d}"
2572 td="${DEST_DIR}${ld}"
2573 if [ -h "${sd}" ]; then
2574 continue
2575 elif [ -d "${sd}" ]; then
2576 tdfiles="$(find "${td}" \! -type d)"
2577 if [ -n "${tdfiles}" ]; then
2578 msg "${sd} exists yet ${td} already" \
2579 "contains files${notfixed}"
2580 else
2581 msg "Migrate ${sd} to ${td}${notfixed}"
2582 fi
2583 failed=1
2584 elif [ -e "${sd}" ]; then
2585 msg "Unexpected file ${sd}${notfixed}"
2586 continue
2587 else
2588 continue
2589 fi
2590 done
2591 fi
2592
2593 # check if xdm resources have been updated
2594 if [ -r ${etcx11}/xdm/Xresources ] && \
2595 ! ${GREP} -q 'inpColor:' ${etcx11}/xdm/Xresources; then
2596 msg "Update ${etcx11}/xdm/Xresources${notfixed}"
2597 failed=1
2598 fi
2599
2600 return ${failed}
2601 }
2602
2603
2604 #
2605 # xkb
2606 #
2607 # /usr/X11R7/lib/X11/xkb/symbols/pc used to be a directory, but changed
2608 # to a file on 2009-06-12. Fixing this requires removing the directory
2609 # (which we can do) and re-extracting the xbase set (which we can't do),
2610 # or at least adding that one file (which we may be able to do if X11SRCDIR
2611 # is available).
2612 #
2613
2614 additem xkb "clean up for xkbdata to xkeyboard-config upgrade"
2615 do_xkb()
2616 {
2617 [ -n "$1" ] || err 3 "USAGE: do_xkb fix|check"
2618 local op="$1"
2619 local failed=0
2620
2621 local pcpath="/usr/X11R7/lib/X11/xkb/symbols/pc"
2622 local pcsrcdir="${X11SRCDIR}/external/mit/xkeyboard-config/dist/symbols"
2623
2624 local filemsg="\
2625 ${pcpath} was a directory, should be a file.
2626 To fix, extract the xbase set again."
2627
2628 local notfixed=""
2629 if [ "${op}" = "fix" ]; then
2630 notfixed="${NOT_FIXED}"
2631 fi
2632
2633 if [ ! -d "${DEST_DIR}${pcpath}" ]; then
2634 return 0
2635 fi
2636
2637 # Delete obsolete files in the directory, and the directory
2638 # itself. If the directory contains unexpected extra files
2639 # then it will not be deleted.
2640 ( [ -f "${DEST_DIR}"/var/db/obsolete/xbase ] \
2641 && ${SORT} -ru "${DEST_DIR}"/var/db/obsolete/xbase \
2642 | ${GREP} -E "^\\.?${pcpath}/" ;
2643 echo "${pcpath}" ) \
2644 | obsolete_paths "${op}"
2645 failed=$(( ${failed} + $? ))
2646
2647 # If the directory was removed above, then try to replace it with
2648 # a file.
2649 if [ -d "${DEST_DIR}${pcpath}" ]; then
2650 msg "${filemsg}${notfixed}"
2651 failed=$(( ${failed} + 1 ))
2652 else
2653 if ! find_file_in_dirlist pc "${pcpath}" \
2654 "${pcsrcdir}" "${SRC_DIR}${pcpath%/*}"
2655 then
2656 msg "${filemsg}${notfixed}"
2657 failed=$(( ${failed} + 1 ))
2658 else
2659 # ${dir} is set by find_file_in_dirlist()
2660 populate_dir "${op}" true \
2661 "${dir}" "${DEST_DIR}${pcpath%/*}" 444 \
2662 pc
2663 failed=$(( ${failed} + $? ))
2664 fi
2665 fi
2666
2667 return $failed
2668 }
2669
2670
2671 #
2672 # obsolete_stand
2673 # obsolete_stand_debug
2674 #
2675
2676 obsolete_stand_internal()
2677 {
2678 local prefix="$1"
2679 shift
2680 [ -n "$1" ] || err 3 "USAGE: do_obsolete_stand fix|check"
2681 local op="$1"
2682 local failed=0
2683 local dir
2684
2685 for dir in \
2686 ${prefix}/stand/${MACHINE} \
2687 ${prefix}/stand/${MACHINE}-4xx \
2688 ${prefix}/stand/${MACHINE}-booke \
2689 ${prefix}/stand/${MACHINE}-xen \
2690 ${prefix}/stand/${MACHINE}pae-xen
2691 do
2692 [ -d "${DEST_DIR}${dir}" ] && obsolete_stand "${dir}"
2693 done | obsolete_paths "${op}"
2694 failed=$(( ${failed} + $? ))
2695
2696 return ${failed}
2697 }
2698
2699 adddisableditem obsolete_stand "remove obsolete files from /stand"
2700 do_obsolete_stand()
2701 {
2702 obsolete_stand_internal "" "$@"
2703 return $?
2704 }
2705
2706 adddisableditem obsolete_stand_debug "remove obsolete files from /usr/libdata/debug/stand"
2707 do_obsolete_stand_debug()
2708 {
2709 obsolete_stand_internal "/usr/libdata/debug" "$@"
2710 return $?
2711 }
2712
2713
2714 #
2715 # obsolete
2716 #
2717 # NOTE: This item is last to allow other items to move obsolete files.
2718 #
2719
2720 listarchsubdirs()
2721 {
2722 if ! $SOURCEMODE; then
2723 echo "@ARCHSUBDIRS@"
2724 else
2725 ${SED} -n -e '/ARCHDIR_SUBDIR/s/[[:space:]]//gp' \
2726 "${SRC_DIR}/compat/archdirs.mk"
2727 fi
2728 }
2729
2730 getarchsubdirs()
2731 {
2732 local m
2733 local i
2734
2735 case ${MACHINE_ARCH} in
2736 *arm*|*aarch64*) m=arm;;
2737 x86_64) m=amd64;;
2738 *) m=${MACHINE_ARCH};;
2739 esac
2740
2741 for i in $(listarchsubdirs); do
2742 echo $i
2743 done | ${SORT} -u | ${SED} -n -e "/=${m}/s@.*=${m}/\(.*\)@\1@p"
2744 }
2745
2746 getcompatlibdirs()
2747 {
2748 local i
2749
2750 for i in $(getarchsubdirs); do
2751 if [ -d "${DEST_DIR}/usr/lib/$i" ]; then
2752 echo /usr/lib/$i
2753 fi
2754 done
2755 }
2756
2757 additem obsolete "remove obsolete file sets and minor libraries"
2758 do_obsolete()
2759 {
2760 [ -n "$1" ] || err 3 "USAGE: do_obsolete fix|check"
2761 local op="$1"
2762 local failed=0
2763 local i
2764
2765 ${SORT} -ru "${DEST_DIR}"/var/db/obsolete/* | obsolete_paths "${op}"
2766 failed=$(( ${failed} + $? ))
2767
2768 (
2769 obsolete_libs /lib
2770 obsolete_libs /usr/lib
2771 obsolete_libs /usr/lib/i18n
2772 obsolete_libs /usr/X11R6/lib
2773 obsolete_libs /usr/X11R7/lib
2774 for i in $(getcompatlibdirs); do
2775 obsolete_libs $i
2776 done
2777 ) | obsolete_paths "${op}"
2778 failed=$(( ${failed} + $? ))
2779
2780 return ${failed}
2781 }
2782
2783
2784 #
2785 # end of items
2786 # ------------
2787 #
2788
2789
2790 help()
2791 {
2792 cat << _USAGE_
2793 Usage: ${PROGNAME} [-a ARCH] [-d DEST_DIR] [-m MACHINE] [-s SRC_ARG] [-x XSRC_DIR] OPERATION ...
2794 ${PROGNAME} -?
2795
2796 Perform post-installation checks and/or fixes on a system's
2797 configuration files.
2798 If no items are provided, a default set of checks or fixes is applied.
2799
2800 Options:
2801 -? Display this help, and exit.
2802 -a ARCH Set \$MACHINE_ARCH to ARCH. [${MACHINE_ARCH}]
2803 -d DEST_DIR Destination directory to check. [${DEST_DIR:-/}]
2804 -m MACHINE Set \$MACHINE to MACHINE. [${MACHINE}]
2805 -s SRC_ARG Location of the source files. This may be any of
2806 the following:
2807 -s SRC_DIR A directory that contains a NetBSD
2808 source tree.
2809 -s TGZ_DIR A directory in which one or both of
2810 "etc.tgz" and "xetc.tgz" have been
2811 extracted.
2812 -s TGZ_FILE A distribution set file such as
2813 "etc.tgz" or "xetc.tgz".
2814 May be specified multipled times.
2815 [${SRC_DIR:-/usr/src}]
2816 -x XSRC_DIR Location of the X11 source files. This must be
2817 a directory that contains a NetBSD xsrc tree.
2818 [${XSRC_DIR:-/usr/src/../xsrc}]
2819
2820 Supported values for OPERATION:
2821 help Display this help, and exit.
2822 list List available items.
2823 check ITEM ... Perform post-installation checks on ITEMs.
2824 diff [-bcenpuw] ITEM ...
2825 Similar to 'check' but also output difference of files,
2826 using diff with the provided options.
2827 fix ITEM ... Apply fixes that 'check' determines need to be applied.
2828 usage Display this help, and exit.
2829 _USAGE_
2830 }
2831
2832 usage()
2833 {
2834 help 1>&2
2835 exit 2
2836 }
2837
2838
2839 list()
2840 {
2841 local i
2842 echo "Default set of items (to apply if no items are provided by user):"
2843 echo " Item Description"
2844 echo " ---- -----------"
2845 for i in ${defaultitems}; do
2846 eval desc=\"\${desc_${i}}\"
2847 printf " %-20s %s\n" "${i}" "${desc}"
2848 done
2849 echo "Items disabled by default (must be requested explicitly):"
2850 echo " Item Description"
2851 echo " ---- -----------"
2852 for i in ${otheritems}; do
2853 eval desc=\"\${desc_${i}}\"
2854 printf " %-20s %s\n" "${i}" "${desc}"
2855 done
2856 }
2857
2858
2859 main()
2860 {
2861 DIRMODE=false # true if "-s" specified a directory
2862 ITEMS= # items to check|diff|fix. [${defaultitems}]
2863 N_SRC_ARGS=0 # number of "-s" args in SRC_ARGLIST
2864 SOURCEMODE=false # true if "-s" specified a source directory
2865 SRC_ARGLIST= # quoted list of one or more "-s" args
2866 SRC_DIR="${SRC_ARG}" # set default value for early usage()
2867 TGZLIST= # quoted list list of tgz files
2868 TGZMODE=false # true if "-s" specifies a tgz file
2869 XSRC_DIR="${SRC_ARG}/../xsrc"
2870 XSRC_DIR_FIX=
2871
2872 case "$(uname -s)" in
2873 Darwin)
2874 # case sensitive match for case insensitive fs
2875 file_exists_exact=file_exists_exact
2876 ;;
2877 *)
2878 file_exists_exact=:
2879 ;;
2880 esac
2881
2882 # Validate options.
2883 #
2884 while getopts :a:d:m:s:x: ch; do
2885 case "${ch}" in
2886 a)
2887 MACHINE_ARCH="${OPTARG}"
2888 ;;
2889 d)
2890 DEST_DIR="${OPTARG}"
2891 ;;
2892 m)
2893 MACHINE="${OPTARG}"
2894 ;;
2895 s)
2896 qarg="$(shell_quote "${OPTARG}")"
2897 N_SRC_ARGS=$(( $N_SRC_ARGS + 1 ))
2898 SRC_ARGLIST="${SRC_ARGLIST}${SRC_ARGLIST:+ }-s ${qarg}"
2899 if [ -f "${OPTARG}" ]; then
2900 # arg refers to a *.tgz file.
2901 # This may happen twice, for both
2902 # etc.tgz and xetc.tgz, so we build up a
2903 # quoted list in TGZLIST.
2904 TGZMODE=true
2905 TGZLIST="${TGZLIST}${TGZLIST:+ }${qarg}"
2906 # Note that, when TGZMODE is true,
2907 # SRC_ARG is used only for printing
2908 # human-readable messages.
2909 SRC_ARG="${TGZLIST}"
2910 elif [ -d "${OPTARG}" ]; then
2911 # arg refers to a directory.
2912 # It might be a source directory, or a
2913 # directory where the sets have already
2914 # been extracted.
2915 DIRMODE=true
2916 SRC_ARG="${OPTARG}"
2917 if [ -f "${OPTARG}/etc/Makefile" ]; then
2918 SOURCEMODE=true
2919 fi
2920 else
2921 err 2 "Invalid argument for -s option"
2922 fi
2923 ;;
2924 x)
2925 if [ -d "${OPTARG}" ]; then
2926 # arg refers to a directory.
2927 XSRC_DIR="${OPTARG}"
2928 XSRC_DIR_FIX="-x ${OPTARG} "
2929 else
2930 err 2 "Not a directory for -x option"
2931 fi
2932 ;;
2933 "?")
2934 if [ "${OPTARG}" = "?" ]; then
2935 help
2936 return # no further processing or validation
2937 fi
2938 warn "Unknown option -${OPTARG}"
2939 usage
2940 ;;
2941
2942 :)
2943 warn "Missing argument for option -${OPTARG}"
2944 usage
2945 ;;
2946
2947 *)
2948 err 3 "Unimplemented option -${ch}"
2949 ;;
2950 esac
2951 done
2952 shift $((${OPTIND} - 1))
2953 if [ $# -eq 0 ] ; then
2954 warn "Missing operation"
2955 usage
2956 fi
2957 op="$1"
2958 shift
2959
2960 if [ "$N_SRC_ARGS" -gt 1 ] && $DIRMODE; then
2961 err 2 "Multiple -s args are allowed only with tgz files"
2962 fi
2963 if [ "$N_SRC_ARGS" -eq 0 ]; then
2964 # The default SRC_ARG was set elsewhere
2965 DIRMODE=true
2966 SOURCEMODE=true
2967 SRC_ARGLIST="-s $(shell_quote "${SRC_ARG}")"
2968 fi
2969
2970 # Validate 'diff' first, as it becomes 'check'
2971 #
2972 case "${op}" in
2973
2974 diff)
2975 op=check
2976 DIFF_STYLE=n # default style is RCS
2977 OPTIND=1
2978 while getopts :bcenpuw ch; do
2979 case "${ch}" in
2980 c|e|n|u)
2981 if [ "${DIFF_STYLE}" != "n" -a \
2982 "${DIFF_STYLE}" != "${ch}" ]; then
2983 warn "diff: conflicting output style: -${ch}"
2984 usage
2985 fi
2986 DIFF_STYLE="${ch}"
2987 ;;
2988 b|p|w)
2989 DIFF_OPT="${DIFF_OPT} -${ch}"
2990 ;;
2991 "?")
2992 # NOTE: not supporting diff -?
2993 warn "diff: Unknown option -${OPTARG}"
2994 usage
2995 ;;
2996 :)
2997 warn "diff: Missing argument for option -${OPTARG}"
2998 usage
2999 ;;
3000 *)
3001 err 3 "diff: Unimplemented option -${ch}"
3002 ;;
3003 esac
3004 done
3005 shift $((${OPTIND} - 1))
3006 ;;
3007
3008 esac
3009
3010 # Validate operation and items.
3011 #
3012 case "${op}" in
3013
3014 check|fix)
3015 ITEMS="$*"
3016 : ${ITEMS:="${defaultitems}"}
3017
3018 # ensure that all supplied items are valid
3019 #
3020 for i in ${ITEMS}; do
3021 eval desc=\"\${desc_${i}}\"
3022 [ -n "${desc}" ] || err 2 "Unsupported ${op} '"${i}"'"
3023 done
3024 ;;
3025
3026 help|usage)
3027 help
3028 return # no further processing or validation
3029 ;;
3030
3031 list)
3032 # processed below
3033 ;;
3034
3035 *)
3036 warn "Unknown operation '"${op}"'"
3037 usage
3038 ;;
3039
3040 esac
3041
3042 #
3043 # If '-s' arg or args specified tgz files, extract them
3044 # to a scratch directory.
3045 #
3046 if $TGZMODE; then
3047 ETCTGZDIR="${SCRATCHDIR}/etc.tgz"
3048 echo "Note: Creating temporary directory ${ETCTGZDIR}"
3049 if ! mkdir "${ETCTGZDIR}"; then
3050 err 2 "Can't create ${ETCTGZDIR}"
3051 fi
3052 ( # subshell to localise changes to "$@"
3053 eval "set -- ${TGZLIST}"
3054 for tgz in "$@"; do
3055 echo "Note: Extracting files from ${tgz}"
3056 cat "${tgz}" | (
3057 cd "${ETCTGZDIR}" &&
3058 tar -zxf -
3059 ) || err 2 "Can't extract ${tgz}"
3060 done
3061 )
3062 SRC_DIR="${ETCTGZDIR}"
3063 else
3064 SRC_DIR="${SRC_ARG}"
3065 fi
3066
3067 [ -d "${SRC_DIR}" ] || err 2 "${SRC_DIR} is not a directory"
3068 [ -d "${DEST_DIR}" ] || err 2 "${DEST_DIR} is not a directory"
3069 [ -n "${MACHINE}" ] || err 2 "\${MACHINE} is not defined"
3070 [ -n "${MACHINE_ARCH}" ] || err 2 "\${MACHINE_ARCH} is not defined"
3071 if ! $SOURCEMODE && ! [ -f "${SRC_DIR}/etc/mtree/set.etc" ]; then
3072 err 2 "Files from the etc.tgz set are missing"
3073 fi
3074
3075 # If directories are /, clear them, so various messages
3076 # don't have leading "//". However, this requires
3077 # the use of ${foo:-/} to display the variables.
3078 #
3079 [ "${SRC_DIR}" = "/" ] && SRC_DIR=""
3080 [ "${DEST_DIR}" = "/" ] && DEST_DIR=""
3081
3082 detect_x11
3083
3084 # Perform operation.
3085 #
3086 case "${op}" in
3087
3088 check|fix)
3089 [ -n "${ITEMS}" ] || err 2 "${op}: missing items"
3090
3091 echo "Source directory: ${SRC_DIR:-/}"
3092 if $TGZMODE; then
3093 echo " (extracted from: ${SRC_ARG})"
3094 fi
3095 echo "Target directory: ${DEST_DIR:-/}"
3096 items_passed=
3097 items_failed=
3098 for i in ${ITEMS}; do
3099 echo "${i} ${op}:"
3100 ( eval do_${i} ${op} )
3101 if [ $? -eq 0 ]; then
3102 items_passed="${items_passed} ${i}"
3103 else
3104 items_failed="${items_failed} ${i}"
3105 fi
3106 done
3107
3108 if [ "${op}" = "check" ]; then
3109 plural="checks"
3110 else
3111 plural="fixes"
3112 fi
3113
3114 echo "${PROGNAME} ${plural} passed:${items_passed}"
3115 echo "${PROGNAME} ${plural} failed:${items_failed}"
3116 if [ -n "${items_failed}" ]; then
3117 exitstatus=1;
3118 if [ "${op}" = "check" ]; then
3119 [ "$MACHINE" = "$(uname -m)" ] && m= || m=" -m $MACHINE"
3120 cat <<_Fix_me_
3121 To fix, run:
3122 ${HOST_SH} ${0} ${SRC_ARGLIST} ${XSRC_DIR_FIX}-d ${DEST_DIR:-/}$m fix${items_failed}
3123 Note that this may overwrite local changes.
3124 _Fix_me_
3125 fi
3126 fi
3127 ;;
3128
3129 list)
3130 echo "Source directory: ${SRC_DIR:-/}"
3131 echo "Target directory: ${DEST_DIR:-/}"
3132 if $TGZMODE; then
3133 echo " (extracted from: ${SRC_ARG})"
3134 fi
3135 list
3136 ;;
3137
3138 *)
3139 # diff, help, usage handled during operation validation
3140 err 3 "Unimplemented operation '"${op}"'"
3141 ;;
3142
3143 esac
3144 }
3145
3146 if [ -n "$POSTINSTALL_FUNCTION" ]; then
3147 eval "$POSTINSTALL_FUNCTION"
3148 exit 0
3149 fi
3150
3151 # defaults
3152 #
3153 PROGNAME="${0##*/}"
3154 SRC_ARG="/usr/src"
3155 DEST_DIR="/"
3156 : ${MACHINE:="$( uname -m )"} # assume native build if $MACHINE is not set
3157 : ${MACHINE_ARCH:="$( uname -p )"}# assume native build if not set
3158
3159 DIFF_STYLE=
3160 DIFF_OPT=
3161 NOT_FIXED=" (FIX MANUALLY)"
3162 SCRATCHDIR="$( mkdtemp )" || err 2 "Can't create scratch directory"
3163 trap "${RM} -rf \"\${SCRATCHDIR}\" ; exit 0" 1 2 3 15 # HUP INT QUIT TERM
3164
3165 umask 022
3166 exec 3>/dev/null
3167 exec 4>/dev/null
3168 exitstatus=0
3169
3170 main "$@"
3171 ${RM} -rf "${SCRATCHDIR}"
3172 exit $exitstatus
3173