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